├── .ci └── conda.yml ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ └── build.yml ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE-MIT.txt ├── LICENSE.txt ├── README.md ├── cmake └── CCCoreLibConfig.cmake.in ├── doc ├── .gitignore ├── CCCoreLib_doxygen_file └── cc_icon_64.png ├── include ├── AutoSegmentationTools.h ├── BoundingBox.h ├── CCConst.h ├── CCCoreLib.h ├── CCGeom.h ├── CCMath.h ├── CCMiscTools.h ├── CCPlatform.h ├── CCShareable.h ├── CCToolbox.h ├── CCTypes.h ├── CMakeLists.txt ├── ChamferDistanceTransform.h ├── CloudSamplingTools.h ├── ConjugateGradient.h ├── Delaunay2dMesh.h ├── DgmOctree.h ├── DgmOctreeReferenceCloud.h ├── DistanceComputationTools.h ├── ErrorFunction.h ├── FastMarching.h ├── FastMarchingForPropagation.h ├── Garbage.h ├── GenericCloud.h ├── GenericDistribution.h ├── GenericIndexedCloud.h ├── GenericIndexedCloudPersist.h ├── GenericIndexedMesh.h ├── GenericMesh.h ├── GenericOctree.h ├── GenericProgressCallback.h ├── GenericTriangle.h ├── GeometricalAnalysisTools.h ├── Grid3D.h ├── GridAndMeshIntersection.h ├── Jacobi.h ├── KdTree.h ├── Kriging.h ├── LocalModel.h ├── ManualSegmentationTools.h ├── MathTools.h ├── MeshSamplingTools.h ├── Neighbourhood.h ├── NormalDistribution.h ├── ParallelSort.h ├── PointCloud.h ├── PointCloudTpl.h ├── PointProjectionTools.h ├── Polyline.h ├── RayAndBox.h ├── ReferenceCloud.h ├── RegistrationTools.h ├── SaitoSquaredDistanceTransform.h ├── ScalarField.h ├── ScalarFieldTools.h ├── SimpleMesh.h ├── SimpleTriangle.h ├── SquareMatrix.h ├── StatisticalTestingTools.h ├── TrueKdTree.h └── WeibullDistribution.h └── src ├── AutoSegmentationTools.cpp ├── CCMiscTools.cpp ├── CCShareable.cpp ├── CMakeLists.txt ├── ChamferDistanceTransform.cpp ├── Chi2Helper.h ├── CloudSamplingTools.cpp ├── Delaunay2dMesh.cpp ├── DgmOctree.cpp ├── DgmOctreeReferenceCloud.cpp ├── DistanceComputationTools.cpp ├── ErrorFunction.cpp ├── FastMarching.cpp ├── FastMarchingForPropagation.cpp ├── GeometricalAnalysisTools.cpp ├── GridAndMeshIntersection.cpp ├── KdTree.cpp ├── Kriging.cpp ├── LocalModel.cpp ├── ManualSegmentationTools.cpp ├── MeshSamplingTools.cpp ├── Neighbourhood.cpp ├── NormalDistribution.cpp ├── NormalizedProgress.cpp ├── PointProjectionTools.cpp ├── Polyline.cpp ├── ReferenceCloud.cpp ├── RegistrationTools.cpp ├── SaitoSquaredDistanceTransform.cpp ├── ScalarField.cpp ├── ScalarFieldTools.cpp ├── SimpleMesh.cpp ├── StatisticalTestingTools.cpp ├── TrueKdTree.cpp └── WeibullDistribution.cpp /.ci/conda.yml: -------------------------------------------------------------------------------- 1 | name: CCCoreLib 2 | channels: 3 | - conda-forge 4 | dependencies: 5 | - ninja 6 | - qt=5.15.* 7 | - cgal=5.* 8 | - tbb-devel=2021.* 9 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | custom: ['https://donorbox.org/support-cloudcompare'] 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve CCCoreLib 4 | title: '' 5 | labels: not_yet_confirmed 6 | assignees: '' 7 | --- 8 | 9 | 10 | 11 | **Describe the bug** 12 | 13 | 14 | **How to reproduce** 15 | 22 | 23 | **Expected behaviour** 24 | 25 | 26 | **Additional context** 27 | 28 | 29 | **Your environment** 30 | - CCCoreLib Version (or git hash/tag): 31 | - OS & Version: 32 | - Qt Version (if compiling w/Qt): 33 | - TBB Version (if compiling w/TBB): 34 | 35 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for the CCCoreLib project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | --- 8 | 9 | **Describe the feature you would like** 10 | 11 | 12 | **Describe alternatives you've considered** 13 | 14 | 15 | **Is your feature request related to a problem? Please describe.** 16 | 17 | 18 | **Additional context** 19 | 20 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: [ push, pull_request ] 4 | 5 | jobs: 6 | build: 7 | name: ${{ matrix.config.name }} 8 | runs-on: ${{ matrix.config.os }} 9 | if: "!contains(github.event.head_commit.message, '[skip ci]')" 10 | 11 | strategy: 12 | fail-fast: false 13 | matrix: 14 | config: 15 | - { 16 | name: "Windows MSVC", 17 | os: windows-latest, 18 | generator: "Ninja", 19 | conda_library_dir: "Library" 20 | } 21 | - { 22 | name: "macOS Clang", 23 | os: macos-latest, 24 | generator: "Ninja", 25 | conda_library_dir: "." 26 | } 27 | 28 | steps: 29 | - name: Checkout 30 | uses: actions/checkout@v4 31 | with: 32 | submodules: recursive 33 | 34 | - name: Install Dependencies 35 | uses: conda-incubator/setup-miniconda@v3 36 | with: 37 | architecture: ${{ matrix.config.os == 'macos-latest' && 'arm64' || 'x64' }} 38 | activate-environment: CCCoreLib 39 | auto-activate-base: false 40 | environment-file: .ci/conda.yml 41 | 42 | - name: Configure MSVC console (Windows) 43 | if: matrix.config.os == 'windows-latest' 44 | uses: ilammy/msvc-dev-cmd@v1 45 | 46 | - name: Set environment for MSVC (Windows) 47 | if: matrix.config.os == 'windows-latest' 48 | run: | 49 | # Set these env vars so cmake picks the correct compiler 50 | # https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-commands-for-github-actions#environment-files 51 | echo "CXX=cl.exe" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append 52 | echo "CC=cl.exe" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append 53 | 54 | - name: Configure (macOS) 55 | if: matrix.config.os == 'macos-latest' 56 | shell: bash -l {0} 57 | run: | 58 | mkdir cccorelib-build 59 | 60 | export CONDA_LIB_DIR="$CONDA_PREFIX/${{ matrix.config.conda_library_dir }}" 61 | 62 | cmake \ 63 | -B cccorelib-build \ 64 | -G "${{ matrix.config.generator }}" \ 65 | -DCMAKE_BUILD_TYPE=Release \ 66 | -DCMAKE_PREFIX_PATH="$CONDA_LIB_DIR" \ 67 | -DCCCORELIB_USE_CGAL=FALSE \ 68 | -DCCCORELIB_USES_TBB=TRUE \ 69 | . 70 | 71 | - name: Configure (Windows) 72 | if: matrix.config.os == 'windows-latest' 73 | shell: bash -l {0} 74 | run: | 75 | mkdir build 76 | 77 | # DGM: without caching, using conda takes too long. 78 | # Therefore we can't set -DCCCORELIB_USE_CGAL=TRUE 79 | cmake \ 80 | -B cccorelib-build \ 81 | -G "${{ matrix.config.generator }}" \ 82 | -DCMAKE_BUILD_TYPE=Release \ 83 | -DCMAKE_PREFIX_PATH="$CONDA_LIB_DIR" \ 84 | -DCCCORELIB_USE_CGAL=FALSE \ 85 | -DCCCORELIB_USES_TBB=TRUE \ 86 | . 87 | 88 | - name: Build 89 | run: cmake --build cccorelib-build --parallel 90 | 91 | ubuntu-build: 92 | name: Ubuntu ${{ matrix.compiler }} 93 | runs-on: ubuntu-22.04 94 | if: "!contains(github.event.head_commit.message, '[skip ci]')" 95 | 96 | strategy: 97 | fail-fast: false 98 | matrix: 99 | compiler: ["GCC", "Clang"] 100 | 101 | steps: 102 | - name: Checkout 103 | uses: actions/checkout@v4 104 | with: 105 | submodules: recursive 106 | 107 | - name: Install Dependencies 108 | run: > 109 | sudo apt-get update -qq 110 | 111 | sudo apt-get install -qy cmake ninja-build 112 | qtbase5-dev qttools5-dev qttools5-dev-tools 113 | libtbb-dev 114 | libcgal-dev libcgal-qt5-dev 115 | 116 | - name: Setup GCC 117 | if: matrix.compiler == 'GCC' 118 | run: | 119 | echo "CC=gcc" >> $GITHUB_ENV 120 | echo "CXX=g++" >> $GITHUB_ENV 121 | 122 | - name: Setup Clang 123 | if: matrix.compiler == 'Clang' 124 | run: | 125 | echo "CC=clang" >> $GITHUB_ENV 126 | echo "CXX=clang++" >> $GITHUB_ENV 127 | 128 | - name: Configure cmake 129 | run: > 130 | mkdir cccorelib-build 131 | 132 | cmake 133 | -B cccorelib-build 134 | -S . 135 | -G Ninja 136 | -DCMAKE_BUILD_TYPE=Release 137 | -DCCCORELIB_USE_CGAL=TRUE 138 | -DCCCORELIB_USES_TBB=TRUE 139 | . 140 | 141 | - name: Build 142 | run: cmake --build cccorelib-build --parallel 143 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.user 2 | #MSVC Ignores 3 | .vs 4 | .vscode 5 | CMakeSettings.json 6 | out 7 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "extern/nanoflann"] 2 | path = extern/nanoflann 3 | url = https://github.com/jlblancoc/nanoflann.git 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | # Copyright © Andy Maloney 3 | 4 | cmake_minimum_required( VERSION 3.10 ) 5 | 6 | project( CCCoreLib 7 | DESCRIPTION 8 | "Core CloudCompare data structures & algorithms for handling point clouds" 9 | LANGUAGES 10 | CXX 11 | VERSION 12 | 1.0 13 | ) 14 | 15 | # Options 16 | option( CCCORELIB_USE_CGAL 17 | "Compile CCCoreLib with CGAL (to enable Delaunay 2.5D triangulation with a GPL-compliant licence)" 18 | OFF 19 | ) 20 | option( CCCORELIB_USE_TBB 21 | "Compile CCCoreLib with Intel Threading Building Blocks lib (enables some parallel processing )" 22 | OFF 23 | ) 24 | option( CCCORELIB_USE_QT_CONCURRENT 25 | "Compile CCCoreLib with QtConcurrent (to enable parallel processing)" 26 | ON 27 | ) 28 | option( CCCORELIB_SHARED 29 | "Compile CCCoreLib as a shared library" 30 | ON 31 | ) 32 | 33 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) 34 | 35 | # Add the library (shared or static) 36 | if ( CCCORELIB_SHARED ) 37 | add_library( CCCoreLib SHARED ) 38 | 39 | set_target_properties( CCCoreLib 40 | PROPERTIES 41 | CXX_VISIBILITY_PRESET hidden 42 | VISIBILITY_INLINES_HIDDEN 1 43 | ) 44 | else() 45 | add_library( CCCoreLib STATIC ) 46 | 47 | target_compile_definitions( CCCoreLib 48 | PRIVATE 49 | CC_CORE_LIB_STATIC_DEFINE 50 | ) 51 | endif() 52 | 53 | set_target_properties( CCCoreLib 54 | PROPERTIES 55 | DEBUG_POSTFIX d 56 | ) 57 | 58 | add_library( CCCoreLib::CCCoreLib ALIAS CCCoreLib ) 59 | 60 | # Generate the export header file 61 | include( GenerateExportHeader ) 62 | 63 | generate_export_header( CCCoreLib 64 | EXPORT_FILE_NAME ${CMAKE_CURRENT_BINARY_DIR}/exports/CCCoreLibExport.h 65 | EXPORT_MACRO_NAME CC_CORE_LIB_API 66 | ) 67 | 68 | target_sources( CCCoreLib 69 | PRIVATE 70 | ${CMAKE_CURRENT_BINARY_DIR}/exports/CCCoreLibExport.h 71 | ) 72 | 73 | install( 74 | FILES 75 | ${CMAKE_CURRENT_BINARY_DIR}/exports/CCCoreLibExport.h 76 | DESTINATION 77 | include/CCCoreLib 78 | ) 79 | 80 | target_include_directories( CCCoreLib 81 | PUBLIC 82 | $ 83 | $ 84 | ) 85 | 86 | # ccache 87 | # https://crascit.com/2016/04/09/using-ccache-with-cmake/ 88 | find_program( CCACHE_PROGRAM ccache ) 89 | 90 | if ( CCACHE_PROGRAM ) 91 | set_target_properties( CCCoreLib 92 | PROPERTIES 93 | CXX_COMPILER_LAUNCHER "${CCACHE_PROGRAM}" 94 | C_COMPILER_LAUNCHER "${CCACHE_PROGRAM}" 95 | ) 96 | endif() 97 | 98 | # Main sources and includes 99 | add_subdirectory( include ) 100 | add_subdirectory( src ) 101 | 102 | # Compiler & definitions 103 | target_compile_features( CCCoreLib 104 | PRIVATE 105 | cxx_std_14 106 | ) 107 | 108 | set_target_properties( CCCoreLib 109 | PROPERTIES 110 | CXX_EXTENSIONS OFF 111 | ) 112 | 113 | target_compile_definitions( CCCoreLib 114 | PRIVATE 115 | "$<$:CC_DEBUG>" 116 | ) 117 | 118 | # Nanoflann 119 | option(NANOFLANN_BUILD_BENCHMARKS "" OFF) 120 | option(NANOFLANN_BUILD_EXAMPLES "" OFF) 121 | option(NANOFLANN_BUILD_TESTS "" OFF) 122 | add_subdirectory( extern/nanoflann EXCLUDE_FROM_ALL ) 123 | target_link_libraries( CCCoreLib 124 | PUBLIC 125 | nanoflann::nanoflann 126 | ) 127 | 128 | # Windows-specific flags 129 | if ( WIN32 ) 130 | # VLD for mem leak checking 131 | option( CCCORELIB_USE_VISUAL_LEAK_DETECTOR 132 | "Check to activate compilation (in debug) with Visual Leak Detector" 133 | OFF 134 | ) 135 | 136 | if ( CCCORELIB_USE_VISUAL_LEAK_DETECTOR ) 137 | target_compile_definitions( CCCoreLib 138 | PRIVATE 139 | CC_CORE_LIB_USES_VLD 140 | ) 141 | endif() 142 | 143 | # Disable SECURE_SCL 144 | # See https://channel9.msdn.com/shows/Going+Deep/STL-Iterator-Debugging-and-Secure-SCL/ 145 | target_compile_definitions( CCCoreLib 146 | PRIVATE 147 | "$<$:_SECURE_SCL=0>" 148 | ) 149 | 150 | target_compile_definitions( CCCoreLib 151 | PRIVATE 152 | _CRT_SECURE_NO_WARNINGS 153 | __STDC_LIMIT_MACROS 154 | NOMINMAX 155 | ) 156 | endif() 157 | 158 | # TBB (optional) 159 | # Must come before CGAL so it can use TBB properly 160 | if ( CCCORELIB_USE_TBB ) 161 | find_package( TBB COMPONENTS tbb CONFIG ) 162 | 163 | if ( TBB_FOUND ) 164 | if ( ${TBB_VERSION} VERSION_GREATER 2021.0.0 ) 165 | target_link_libraries( CCCoreLib 166 | PUBLIC 167 | TBB::tbb 168 | ) 169 | else() 170 | target_link_libraries( CCCoreLib 171 | PUBLIC 172 | ${TBB_IMPORTED_TARGETS} 173 | ) 174 | endif() 175 | target_compile_definitions( CCCoreLib 176 | PUBLIC 177 | CC_CORE_LIB_USES_TBB 178 | TBB_VERSION="${TBB_VERSION}" 179 | ) 180 | endif() 181 | endif() 182 | 183 | # CGAL (optional) 184 | if ( CCCORELIB_USE_CGAL ) 185 | find_package( CGAL 5.1 REQUIRED ) 186 | 187 | if( WIN32 ) 188 | # Need to force the visibility of these variables so that we can use them later 189 | set (GMP_LIBRARIES ${GMP_LIBRARIES} PARENT_SCOPE) 190 | set (MPFR_LIBRARIES ${MPFR_LIBRARIES} PARENT_SCOPE) 191 | endif() 192 | 193 | if ( CCCORELIB_USE_TBB ) 194 | if ( TBB_FOUND ) 195 | # Once Linux libcgal-dev >= 5.0, target_compile_definitions replaced by: 196 | # CGAL_target_use_TBB( CCCoreLib ) 197 | 198 | target_compile_definitions( CCCoreLib 199 | PRIVATE 200 | CGAL_LINKED_WITH_TBB 201 | NOMINMAX 202 | ) 203 | else() 204 | message( WARNING "CGAL cannot compile with TBB (TBB not found)" ) 205 | endif() 206 | endif() 207 | 208 | target_link_libraries( CCCoreLib 209 | PUBLIC 210 | CGAL::CGAL 211 | ) 212 | 213 | target_compile_definitions( CCCoreLib 214 | PUBLIC 215 | CC_CORE_LIB_USES_CGAL_LIB 216 | ) 217 | endif() 218 | 219 | # QT (optional) 220 | if ( CCCORELIB_USE_QT_CONCURRENT ) 221 | find_package( Qt5 222 | COMPONENTS 223 | Concurrent 224 | REQUIRED 225 | ) 226 | 227 | set_target_properties( CCCoreLib PROPERTIES 228 | AUTOMOC OFF 229 | AUTORCC OFF 230 | AUTOUIC OFF 231 | ) 232 | 233 | target_link_libraries( CCCoreLib 234 | PUBLIC 235 | Qt5::Concurrent 236 | ) 237 | 238 | target_compile_definitions( CCCoreLib 239 | PUBLIC 240 | CC_CORE_LIB_USES_QT_CONCURRENT 241 | ) 242 | endif() 243 | 244 | # Install 245 | # See: https://cliutils.gitlab.io/modern-cmake/chapters/install/installing.html 246 | install( 247 | TARGETS 248 | CCCoreLib 249 | EXPORT 250 | CCCoreLib-targets 251 | LIBRARY DESTINATION lib 252 | ARCHIVE DESTINATION lib 253 | INCLUDES DESTINATION include 254 | ) 255 | 256 | install( 257 | EXPORT 258 | CCCoreLib-targets 259 | FILE 260 | CCCoreLibTargets.cmake 261 | NAMESPACE 262 | CCCoreLib:: 263 | DESTINATION 264 | lib/cmake/CCCoreLib 265 | ) 266 | 267 | # CMake Package Files 268 | include( CMakePackageConfigHelpers ) 269 | 270 | configure_package_config_file(${CMAKE_CURRENT_LIST_DIR}/cmake/${PROJECT_NAME}Config.cmake.in 271 | ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake 272 | INSTALL_DESTINATION lib/cmake/${PROJECT_NAME} 273 | NO_CHECK_REQUIRED_COMPONENTS_MACRO 274 | ) 275 | 276 | write_basic_package_version_file( CCCoreLibConfigVersion.cmake 277 | VERSION 278 | ${PACKAGE_VERSION} 279 | COMPATIBILITY 280 | AnyNewerVersion 281 | ) 282 | 283 | install( 284 | FILES 285 | "${CMAKE_CURRENT_BINARY_DIR}/CCCoreLibConfigVersion.cmake" 286 | "${CMAKE_CURRENT_BINARY_DIR}/CCCoreLibConfig.cmake" 287 | DESTINATION 288 | lib/cmake/CCCoreLib 289 | ) 290 | 291 | # Export 292 | export( 293 | TARGETS 294 | CCCoreLib 295 | NAMESPACE 296 | CCCoreLib:: 297 | FILE 298 | CCCoreLibTargets.cmake 299 | ) 300 | -------------------------------------------------------------------------------- /LICENSE-MIT.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CCCoreLib 2 | 3 | [![Actions Status](https://github.com/CloudCompare/CCCoreLib/workflows/Build/badge.svg)](https://github.com/CloudCompare/CCCoreLib/actions) 4 | 5 | This library provides data structures and algorithms for working with 3D point cloud data. 6 | 7 | It was originally part of the [CloudCompare repository](https://github.com/CloudCompare/CloudCompare) (as "CCLib"). 8 | 9 | We have separated it into its own repository because it is useful on its own without having to download the entire CloudCompare repository. This makes it easier to work with and gives this part of the CloudCompare project some visibility it would not otherwise have. 10 | 11 | It uses CMake, requires C++14, and compiles & runs on Linux, macOS, and Windows. 12 | 13 | ## Main CMake Options 14 | 15 | | Option | Description | Default | 16 | | ------------- |-------------| ---------| 17 | | CCCORELIB_USE_CGAL | Use [CGAL](https://github.com/CGAL/cgal) to enable Delaunay 2.5D triangulation with a GPL compliant licence | OFF | 18 | | CCCORELIB_USE_TBB | Use [Intel Threading Building Blocks](https://github.com/oneapi-src/oneTBB) lib to enable some parallel processing | OFF | 19 | | CCCORELIB_USE_QT_CONCURRENT | Use Qt to enable parallel processing using [QtConcurrent](https://doc.qt.io/qt-5/qtconcurrent-index.html) | ON | 20 | | CCCORELIB_SHARED | Compile as a shared library | ON | 21 | | CCCORELIB_SCALAR_DOUBLE | Define _ScalarType_ as double (instead of float) | OFF | 22 | 23 | ### Qt Option (Qt5_DIR) 24 | 25 | If `CCCORELIB_USE_QT_CONCURRENT` is on (it is by default), then you may need to tell CMake where to find the Qt cmake files. The [official docs from Qt](https://doc.qt.io/qt-5/cmake-get-started.html) show two ways to do so: 26 | - setting `CMAKE_PREFIX_PATH` to point to your Qt installation (where 'bin', 'doc', 'include', lib', etc. live) 27 | - setting `Qt5_DIR` to point at the cmake directory within your Qt installation (this would be something like `/cmake/Qt5`) 28 | 29 | From this, cmake will determine `Qt5Concurrent_DIR`, `Qt5Core_DIR`, and `Qt5Widgets_DIR`, so there's no need to set those explicitly. 30 | 31 | ## Things We Have Yet To Do 32 | 33 | - contribution guidelines (including coding style) 34 | - documentation 35 | 36 | ## How You Can Help 37 | 38 | - [report issues](https://github.com/CloudCompare/CCCoreLib/issues) 39 | - help with documentation 40 | - [contribute improvements](https://github.com/CloudCompare/CCCoreLib/pulls) 41 | 42 | ## License 43 | This project as a whole is licensed under the **LGPL 2.0+** license - see the [LICENSE](LICENSE.txt) file for details. 44 | 45 | Individual source files contain the following tag instead of the full license text: 46 | 47 | SPDX-License-Identifier: LGPL-2.0-or-later 48 | 49 | The CMake files are licensed under the **MIT** license - see the [LICENSE-MIT](LICENSE-MIT.txt) file for details. 50 | 51 | These files contain the following tag instead of the full license text: 52 | 53 | SPDX-License-Identifier: MIT 54 | 55 | Using SPDX enables machine processing of license information based on the [SPDX License Identifiers](https://spdx.org/ids) and makes it easier for developers to see at a glance which license they are dealing with. 56 | 57 | ### License Special Note 58 | Two files (BoundingBox.cpp and RayAndBox.h) were _previously_ licensed under the **GPL 2.0+** license by mistake. 59 | 60 | These files contained the following tag: 61 | 62 | SPDX-License-Identifier: GPL-2.0-or-later 63 | 64 | The necessary permissions were secured to relicense these under LGPL: 65 | 66 | SPDX-License-Identifier: LGPL-2.0-or-later 67 | -------------------------------------------------------------------------------- /cmake/CCCoreLibConfig.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | set(@PROJECT_NAME@_FOUND ON) 4 | set_and_check(@PROJECT_NAME@_INCLUDE_DIRS "${PACKAGE_PREFIX_DIR}/include") 5 | set_and_check(@PROJECT_NAME@_LIBRARY_DIRS "${PACKAGE_PREFIX_DIR}/lib") 6 | set(@PROJECT_NAME@_LIBRARIES "@PACKAGE_LIBRARIES@") 7 | 8 | include(CMakeFindDependencyMacro) 9 | 10 | if( @CCCORELIB_USE_CGAL@ ) 11 | find_dependency( CGAL ) 12 | endif() 13 | if( @CCCORELIB_USE_TBB@ ) 14 | if( ${CMAKE_VERSION} VERSION_LESS "3.10.0" ) 15 | find_package (TBB COMPONENTS tbb CONFIG ) 16 | else() 17 | find_dependency( TBB COMPONENTS tbb CONFIG ) 18 | endif() 19 | endif() 20 | if( @CCCORELIB_USE_QT_CONCURRENT@ ) 21 | if( ${CMAKE_VERSION} VERSION_LESS "3.10.0" ) 22 | find_package( Qt5 COMPONENTS Concurrent ) 23 | else() 24 | find_dependency( Qt5 COMPONENTS Concurrent ) 25 | endif() 26 | endif() 27 | 28 | include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") 29 | -------------------------------------------------------------------------------- /doc/.gitignore: -------------------------------------------------------------------------------- 1 | /html/ 2 | -------------------------------------------------------------------------------- /doc/cc_icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CloudCompare/CCCoreLib/08137fc62ec246f9bcfb32c337a550d086e0be21/doc/cc_icon_64.png -------------------------------------------------------------------------------- /include/AutoSegmentationTools.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | //Local 9 | #include "CCToolbox.h" 10 | #include "CCTypes.h" 11 | 12 | namespace CCCoreLib 13 | { 14 | class GenericIndexedCloudPersist; 15 | class GenericProgressCallback; 16 | class DgmOctree; 17 | class ReferenceCloud; 18 | 19 | //! A standard container to store several subsets of points 20 | /** Several algorithms of the AutoSegmentationTools toolbox return a collection of subsets of points 21 | corresponding to each segmented part. Such a collection is generally stored in this type of container. 22 | **/ 23 | using ReferenceCloudContainer = std::vector; 24 | 25 | //! Several point cloud auto-segmentation algorithms (Connected Components, Front propagation, etc.) 26 | class CC_CORE_LIB_API AutoSegmentationTools : public CCToolbox 27 | { 28 | public: 29 | 30 | //! Labels connected components from a point cloud 31 | /** The algorithm is described in Daniel Girardeau-Montaut's PhD manuscript 32 | (Chapter 3, section 3.2.4). The main parameter is the maximal distance 33 | between two points in order to consider them as belonging to the same 34 | connected component. Actually, this parameter is not expressed as is, but 35 | as the length of a cell of the octree for a given level of subdivision (this 36 | leeds to a great enhancement of the process speed). If the level is "n", 37 | then the cell size will be equal to the maximum length of the bounding box 38 | of the point cloud divided by 2^n. The greater the level "n" is, the smaller 39 | the cell size will be, and therefore the process will produce more 40 | connected components.To label the CCs, this implementation of the algorithm 41 | use the distance field (via Generic3dPoint::setDist). So be sure to 42 | store the original distance field (or to deviate the setDist process) if 43 | you don't want it to be replaced. 44 | \param theCloud the point cloud to label 45 | \param level the level of subdivision of the octree (between 1 and MAX_OCTREE_LEVEL) 46 | \param sixConnexity indicates if the CC's 3D connexity should be 6 (26 otherwise) 47 | \param progressCb the client application can get some notification of the process progress through this callback mechanism (see GenericProgressCallback) 48 | \param inputOctree the cloud octree if it has already been computed 49 | \return the number of components (>= 0) or an error code (< 0 - see DgmOctree::extractCCs) 50 | **/ 51 | static int labelConnectedComponents(GenericIndexedCloudPersist* theCloud, 52 | unsigned char level, 53 | bool sixConnexity = false, 54 | GenericProgressCallback* progressCb = nullptr, 55 | DgmOctree* inputOctree = nullptr); 56 | 57 | //! Extracts connected components from a point cloud 58 | /** This method shloud only be called after the connected components have been 59 | labeled (see AutoSegmentationTools::labelConnectedComponents). This 60 | implementation of the algorithm assumes that the CCs labels are stored for 61 | each point in the associated scalar field. 62 | Warning: be sure to set the labels S.F. as OUTPUT (reading) 63 | \param theCloud the point cloud to segment 64 | \param ccc the extracted connected compenents (as a list of subsets of points) 65 | \return success 66 | **/ 67 | static bool extractConnectedComponents(GenericIndexedCloudPersist* theCloud, ReferenceCloudContainer& ccc); 68 | 69 | //! Segment a point cloud by propagating fronts constrained by values of the point cloud associated scalar field 70 | /** The algorithm is described in Daniel Girardeau-Montaut's PhD manuscript 71 | (Chapter 3, section 3.3). It consists mainly in propagating a front on 72 | the surface implicitly represented by the point cloud and making this 73 | propagation dependent on the scalar values associated to each point (such 74 | as the distance information computed between the point cloud and another 75 | entity). The propgation is realized with the Fast Marching Algorithm applied 76 | on a gridded structure (the octree in this case). 77 | Warning: be sure to activate an OUTPUT scalar field on the input cloud 78 | \param theCloud the point cloud to segment 79 | \param minSeedDist the minimum value associated to the point where to start the propagation from ("security" value) 80 | \param radius spherical neighborhood size (or 0 for automatic size) 81 | \param octreeLevel level of subdivision where to apply the gridding (the greater it is, the smaller and numerous the segmented parts will be) 82 | \param theSegmentedLists the segmented parts (as a list of subsets of points) 83 | \param applyGaussianFilter to specify if a gaussian filter should be applied after computing the scalar field gradient (to smooth the results) 84 | \param progressCb the client application can get some notification of the process progress through this callback mechanism (see GenericProgressCallback) 85 | \param inputOctree the cloud octree if it has already be computed 86 | \param alpha the gaussian filter kernel size (needed only if a gaussian filtering pass is required) 87 | \return success 88 | **/ 89 | static bool frontPropagationBasedSegmentation( GenericIndexedCloudPersist* theCloud, 90 | PointCoordinateType radius, 91 | ScalarType minSeedDist, 92 | unsigned char octreeLevel, 93 | ReferenceCloudContainer& theSegmentedLists, 94 | GenericProgressCallback* progressCb = nullptr, 95 | DgmOctree* inputOctree = nullptr, 96 | bool applyGaussianFilter = false, 97 | float alpha = 2.0f); 98 | }; 99 | } 100 | -------------------------------------------------------------------------------- /include/BoundingBox.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // SPDX-License-Identifier: LGPL-2.0-or-later 4 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 5 | 6 | //Local 7 | #include "SquareMatrix.h" 8 | 9 | //System 10 | #include 11 | #include 12 | 13 | namespace CCCoreLib 14 | { 15 | //! Bounding box structure 16 | template class BoundingBoxTpl 17 | { 18 | public: 19 | 20 | //! Default constructor 21 | BoundingBoxTpl() 22 | : m_bbMin(0, 0, 0) 23 | , m_bbMax(0, 0, 0) 24 | , m_valid(false) 25 | {} 26 | 27 | //! Constructor from two vectors (lower min. and upper max. corners) 28 | BoundingBoxTpl(const Vector3Tpl& minCorner, const Vector3Tpl& maxCorner, bool valid) 29 | : m_bbMin(minCorner) 30 | , m_bbMax(maxCorner) 31 | , m_valid(valid) 32 | {} 33 | 34 | //! Returns the 'sum' of this bounding-box and another one 35 | BoundingBoxTpl operator + (const BoundingBoxTpl& bbox) const 36 | { 37 | if (!m_valid) 38 | return bbox; 39 | if (!bbox.isValid()) 40 | return *this; 41 | 42 | BoundingBoxTpl tempBox; 43 | { 44 | tempBox.m_bbMin.x = std::min(m_bbMin.x, bbox.m_bbMin.x); 45 | tempBox.m_bbMin.y = std::min(m_bbMin.y, bbox.m_bbMin.y); 46 | tempBox.m_bbMin.z = std::min(m_bbMin.z, bbox.m_bbMin.z); 47 | tempBox.m_bbMax.x = std::max(m_bbMax.x, bbox.m_bbMax.x); 48 | tempBox.m_bbMax.y = std::max(m_bbMax.y, bbox.m_bbMax.y); 49 | tempBox.m_bbMax.z = std::max(m_bbMax.z, bbox.m_bbMax.z); 50 | tempBox.setValidity(true); 51 | } 52 | 53 | return tempBox; 54 | } 55 | 56 | //! In place 'sum' of this bounding-box with another one 57 | const BoundingBoxTpl& operator += (const BoundingBoxTpl& bbox) 58 | { 59 | if (bbox.isValid()) 60 | { 61 | add(bbox.minCorner()); 62 | add(bbox.maxCorner()); 63 | } 64 | 65 | return *this; 66 | } 67 | 68 | //! Shifts the bounding box with a vector 69 | const BoundingBoxTpl& operator += (const Vector3Tpl& V) 70 | { 71 | if (m_valid) 72 | { 73 | m_bbMin += V; 74 | m_bbMax += V; 75 | } 76 | 77 | return *this; 78 | } 79 | 80 | //! Shifts the bounding box with a vector 81 | const BoundingBoxTpl& operator -= (const Vector3Tpl& V) 82 | { 83 | if (m_valid) 84 | { 85 | m_bbMin -= V; 86 | m_bbMax -= V; 87 | } 88 | 89 | return *this; 90 | } 91 | 92 | //! Scales the bounding box 93 | const BoundingBoxTpl& operator *= (T scaleFactor) 94 | { 95 | if (m_valid) 96 | { 97 | m_bbMin *= scaleFactor; 98 | m_bbMax *= scaleFactor; 99 | } 100 | 101 | return *this; 102 | } 103 | 104 | //! Rotates the bounding box 105 | const BoundingBoxTpl& operator *= (const SquareMatrixTpl& mat) 106 | { 107 | if (m_valid) 108 | { 109 | Vector3Tpl boxCorners[8]; 110 | 111 | boxCorners[0] = m_bbMin; 112 | boxCorners[1] = Vector3Tpl(m_bbMin.x, m_bbMin.y, m_bbMax.z); 113 | boxCorners[2] = Vector3Tpl(m_bbMin.x, m_bbMax.y, m_bbMin.z); 114 | boxCorners[3] = Vector3Tpl(m_bbMax.x, m_bbMin.y, m_bbMin.z); 115 | boxCorners[4] = m_bbMax; 116 | boxCorners[5] = Vector3Tpl(m_bbMin.x, m_bbMax.y, m_bbMax.z); 117 | boxCorners[6] = Vector3Tpl(m_bbMax.x, m_bbMax.y, m_bbMin.z); 118 | boxCorners[7] = Vector3Tpl(m_bbMax.x, m_bbMin.y, m_bbMax.z); 119 | 120 | clear(); 121 | 122 | for (int i = 0; i < 8; ++i) 123 | { 124 | add(mat*boxCorners[i]); 125 | } 126 | } 127 | 128 | return *this; 129 | } 130 | 131 | //! Resets the bounding box 132 | /** (0,0,0) --> (0,0,0) 133 | **/ 134 | void clear() 135 | { 136 | m_bbMin = m_bbMax = Vector3Tpl(0, 0, 0); 137 | m_valid = false; 138 | } 139 | 140 | //! 'Enlarges' the bounding box with a point 141 | void add(const Vector3Tpl& P) 142 | { 143 | if (m_valid) 144 | { 145 | if (P.x < m_bbMin.x) 146 | m_bbMin.x = P.x; 147 | else if (P.x > m_bbMax.x) 148 | m_bbMax.x = P.x; 149 | 150 | if (P.y < m_bbMin.y) 151 | m_bbMin.y = P.y; 152 | else if (P.y > m_bbMax.y) 153 | m_bbMax.y = P.y; 154 | 155 | if (P.z < m_bbMin.z) 156 | m_bbMin.z = P.z; 157 | else if (P.z > m_bbMax.z) 158 | m_bbMax.z = P.z; 159 | } 160 | else 161 | { 162 | m_bbMax = m_bbMin = P; 163 | m_valid = true; 164 | } 165 | } 166 | 167 | //! Returns min corner (const) 168 | inline const Vector3Tpl& minCorner() const { return m_bbMin; } 169 | //! Returns max corner (const) 170 | inline const Vector3Tpl& maxCorner() const { return m_bbMax; } 171 | 172 | //! Returns min corner 173 | inline Vector3Tpl& minCorner() { return m_bbMin; } 174 | //! Returns max corner 175 | inline Vector3Tpl& maxCorner() { return m_bbMax; } 176 | 177 | //! Returns center 178 | Vector3Tpl getCenter() const 179 | { 180 | return (m_bbMax + m_bbMin) * static_cast(0.5); 181 | } 182 | 183 | //! Returns diagonal vector 184 | Vector3Tpl getDiagVec() const 185 | { 186 | return (m_bbMax - m_bbMin); 187 | } 188 | 189 | //! Returns diagonal length 190 | inline T getDiagNorm() const 191 | { 192 | return getDiagVec().norm(); 193 | } 194 | 195 | //! Returns diagonal length (double precision) 196 | double getDiagNormd() const 197 | { 198 | return getDiagVec().normd(); 199 | } 200 | 201 | //! Returns minimal box dimension 202 | T getMinBoxDim() const 203 | { 204 | Vector3Tpl V = getDiagVec(); 205 | 206 | return std::min(V.x, std::min(V.y, V.z)); 207 | } 208 | 209 | //! Returns maximal box dimension 210 | T getMaxBoxDim() const 211 | { 212 | Vector3Tpl V = getDiagVec(); 213 | 214 | return std::max(V.x, std::max(V.y, V.z)); 215 | } 216 | 217 | //! Returns the bounding-box volume 218 | double computeVolume() const 219 | { 220 | Vector3Tpl V = getDiagVec(); 221 | 222 | return static_cast(V.x) * static_cast(V.y) * static_cast(V.z); 223 | } 224 | 225 | //! Sets bonding box validity 226 | inline void setValidity(bool state) 227 | { 228 | m_valid = state; 229 | } 230 | 231 | //! Returns whether bounding box is valid or not 232 | inline bool isValid() const 233 | { 234 | return m_valid; 235 | } 236 | 237 | //! Computes min gap (absolute distance) between this bounding-box and another one 238 | /** \return min gap (>=0) or -1 if at least one of the box is not valid 239 | **/ 240 | T minDistTo(const BoundingBoxTpl& bbox) const 241 | { 242 | if (m_valid && bbox.isValid()) 243 | { 244 | Vector3Tpl d(0, 0, 0); 245 | 246 | for (uint8_t dim = 0; dim < 3; ++dim) 247 | { 248 | //if the boxes overlap in one dimension, the distance is zero (in this dimension) 249 | if (bbox.m_bbMin.u[dim] > m_bbMax.u[dim]) 250 | d.u[dim] = bbox.m_bbMin.u[dim] - m_bbMax.u[dim]; 251 | else if (bbox.m_bbMax.u[dim] < m_bbMin.u[dim]) 252 | d.u[dim] = m_bbMin.u[dim] - bbox.m_bbMax.u[dim]; 253 | } 254 | 255 | return d.norm(); 256 | } 257 | else 258 | { 259 | return std::numeric_limits::quiet_NaN(); 260 | } 261 | } 262 | 263 | //! Returns whether a points is inside the box or not 264 | /** Warning: box should be valid! 265 | **/ 266 | inline bool contains(const Vector3Tpl& P) const 267 | { 268 | return (P.x >= m_bbMin.x && P.x <= m_bbMax.x && 269 | P.y >= m_bbMin.y && P.y <= m_bbMax.y && 270 | P.z >= m_bbMin.z && P.z <= m_bbMax.z); 271 | } 272 | 273 | protected: 274 | 275 | //! Lower min. corner 276 | Vector3Tpl m_bbMin; 277 | //! Upper max. corner 278 | Vector3Tpl m_bbMax; 279 | //! Validity 280 | bool m_valid; 281 | }; 282 | 283 | //! Default bounding-box type 284 | using BoundingBox = BoundingBoxTpl; 285 | } 286 | -------------------------------------------------------------------------------- /include/CCConst.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #pragma once 5 | 6 | #include "CCTypes.h" 7 | 8 | //system 9 | #include 10 | #include 11 | 12 | //! Pi - outside namespace because it's mostly-standard 13 | #ifndef M_PI 14 | constexpr double M_PI = 3.14159265358979323846; 15 | #endif 16 | 17 | //! Pi/2 - outside namespace because it's mostly-standard 18 | #ifndef M_PI_2 19 | constexpr double M_PI_2 = (M_PI / 2); 20 | #endif 21 | 22 | namespace CCCoreLib 23 | { 24 | //! Square root of 3 25 | constexpr double SQRT_3 = 1.7320508075688772935274463415059; 26 | 27 | //! ZERO_TOLERANCE_F is used to set or compare a float variable to "close to zero". 28 | constexpr float ZERO_TOLERANCE_F = std::numeric_limits::epsilon(); 29 | 30 | //! ZERO_TOLERANCE_D is used to set or compare a double variable to "close to zero". 31 | //! It is defined as std::numeric_limits::epsilon() because using 32 | //! std::numeric_limits::epsilon() results in numbers that are too small for our purposes. 33 | constexpr double ZERO_TOLERANCE_D = static_cast(ZERO_TOLERANCE_F); 34 | 35 | //! ZERO_SQUARED_TOLERANCE_D is used to set or compare a (square) value to "close to zero". 36 | constexpr double ZERO_SQUARED_TOLERANCE_D = ZERO_TOLERANCE_D * ZERO_TOLERANCE_D; 37 | 38 | //! ZERO_TOLERANCE_SCALAR is used to set or compare a ScalarType variable to "close to zero". 39 | constexpr ScalarType ZERO_TOLERANCE_SCALAR = std::numeric_limits::epsilon(); 40 | 41 | //! ZERO_TOLERANCE_POINT_COORDINATE is used to set or compare a PointCoordinateType variable to "close to zero". 42 | constexpr ScalarType ZERO_TOLERANCE_POINT_COORDINATE = std::numeric_limits::epsilon(); 43 | 44 | //! '1' as a PointCoordinateType value 45 | /** To avoid compiler warnings about 'possible loss of data' **/ 46 | constexpr PointCoordinateType PC_ONE = static_cast(1.0); 47 | 48 | //! 'NaN' as a PointCoordinateType value 49 | /** \warning: handle with care! **/ 50 | constexpr PointCoordinateType PC_NAN = std::numeric_limits::quiet_NaN(); 51 | 52 | //! NaN as a ScalarType value 53 | /** \warning: handle with care! **/ 54 | constexpr ScalarType NAN_VALUE = std::numeric_limits::quiet_NaN(); 55 | 56 | // Point visibility states 57 | // By default visibility is expressed relative to the sensor point of view. 58 | // Warning: 'visible' value must always be the lowest! 59 | constexpr unsigned char POINT_VISIBLE = 0; //!< Point visibility state: visible 60 | constexpr unsigned char POINT_HIDDEN = 1; //!< Point visibility state: hidden (e.g. behind other points) 61 | constexpr unsigned char POINT_OUT_OF_RANGE = 2; //!< Point visibility state: out of range 62 | constexpr unsigned char POINT_OUT_OF_FOV = 4; //!< Point visibility state: out of field of view 63 | 64 | //! Chamfer distances types 65 | enum CHAMFER_DISTANCE_TYPE { 66 | CHAMFER_111 = 0, //!< Chamfer distance <1-1-1> 67 | CHAMFER_345 = 1 //!< Chamfer distance <3-4-5> 68 | }; 69 | 70 | //! Types of local models (no model, least square best fitting plan, Delaunay 2D1/2 triangulation, height function) 71 | enum LOCAL_MODEL_TYPES { 72 | NO_MODEL = 0, //!< No local model 73 | LS = 1, //!< Least Square best fitting plane 74 | TRI = 2, //!< 2.5D Delaunay triangulation 75 | QUADRIC = 3 //!< 2.5D quadric function 76 | }; 77 | 78 | //! Min number of points to compute local models (see CC_LOCAL_MODEL_TYPES) 79 | constexpr unsigned CC_LOCAL_MODEL_MIN_SIZE[] { 80 | 1, //!< for single point model (i.e. no model ;) 81 | 3, //!< for least Square best fitting plane 82 | 3, //!< for Delaunay triangulation (2.5D) 83 | 6, //!< for Quadratic 'height' function 84 | }; 85 | } 86 | -------------------------------------------------------------------------------- /include/CCCoreLib.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #pragma once 5 | 6 | #ifdef _MSC_VER 7 | //To get rid of the really annoying warnings about template class exportation 8 | #pragma warning( disable: 4251 ) 9 | #pragma warning( disable: 4530 ) 10 | //To get rid of the new warnings in Qt 5.15 11 | #pragma warning( disable: 4996 ) 12 | #endif 13 | 14 | // this file is generated by cmake 15 | #include "CCCoreLibExport.h" 16 | -------------------------------------------------------------------------------- /include/CCMath.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © CloudCompare Project 3 | 4 | #pragma once 5 | 6 | //! \file CCMath.h 7 | //! Methods for conversion and comparison. Note that these are intenionally not 8 | //! templates. They are short methods and templates are overkill for these cases. 9 | 10 | #include "CCConst.h" 11 | 12 | 13 | namespace CCCoreLib 14 | { 15 | //! Test a floating point number against our epsilon (a very small number). 16 | /*! 17 | \param x The number to test 18 | \return True if the number is less than epsilon. 19 | */ 20 | inline bool LessThanEpsilon( float x ) 21 | { 22 | return x < ZERO_TOLERANCE_F; 23 | } 24 | 25 | //! Test a floating point number against our epsilon (a very small number). 26 | /*! 27 | \param x The number to test 28 | \return True if the number is less than epsilon. 29 | */ 30 | inline bool LessThanEpsilon( double x ) 31 | { 32 | return x < ZERO_TOLERANCE_D; 33 | } 34 | 35 | //! Test a (squared) floating point number against our epsilon (a very small number). 36 | /*! 37 | \param x The (squared) number to test 38 | \return True if the number is less than epsilon. 39 | */ 40 | inline bool LessThanSquareEpsilon(double x) 41 | { 42 | return x < ZERO_SQUARED_TOLERANCE_D; 43 | } 44 | 45 | //! Test a floating point number against our epsilon (a very small number). 46 | /*! 47 | \param x The number to test 48 | \return True if the number is greater than epsilon. 49 | */ 50 | inline bool GreaterThanEpsilon( float x ) 51 | { 52 | return x > ZERO_TOLERANCE_F; 53 | } 54 | 55 | //! Test a floating point number against our epsilon (a very small number). 56 | /*! 57 | \param x The number to test 58 | \return True if the number is greater than epsilon. 59 | */ 60 | inline bool GreaterThanEpsilon( double x ) 61 | { 62 | return x > ZERO_TOLERANCE_D; 63 | } 64 | 65 | //! Test a (squared) floating point number against our epsilon (a very small number). 66 | /*! 67 | \param x The number to test 68 | \return True if the number is greater than epsilon. 69 | */ 70 | inline bool GreaterThanSquareEpsilon(double x) 71 | { 72 | return x > ZERO_SQUARED_TOLERANCE_D; 73 | } 74 | 75 | //! Convert radians to degrees. 76 | /*! 77 | \param radians Radians to convert 78 | \return \a radians represented as degrees. 79 | */ 80 | inline float RadiansToDegrees( float radians ) 81 | { 82 | return radians * (180.0f / static_cast(M_PI)); 83 | } 84 | 85 | //! Convert radians to degrees. 86 | /*! 87 | \param radians Radians to convert 88 | \return \a radians represented as degrees. 89 | */ 90 | inline double RadiansToDegrees( double radians ) 91 | { 92 | return radians * (180.0 / M_PI); 93 | } 94 | 95 | //! Convert degrees to radians. 96 | /*! 97 | \param degrees Degrees to convert 98 | \return \a degrees represented as radians. 99 | */ 100 | inline float DegreesToRadians( float degrees ) 101 | { 102 | return degrees * (static_cast(M_PI) / 180.0f); 103 | } 104 | 105 | //! Convert degrees to radians. 106 | /*! 107 | \param degrees Degrees to convert 108 | \return \a degrees represented as radians. 109 | */ 110 | inline double DegreesToRadians( double degrees ) 111 | { 112 | return degrees * (M_PI / 180.0); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /include/CCMiscTools.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #pragma once 5 | 6 | //Local 7 | #include "CCGeom.h" 8 | #include "CCToolbox.h" 9 | 10 | namespace CCCoreLib 11 | { 12 | //! Miscellaneous useful functions (geometrical elements handling) 13 | class CC_CORE_LIB_API CCMiscTools : public CCToolbox 14 | { 15 | public: 16 | 17 | //! Proportionally enlarges a 3D box 18 | /** \param dimMin the upper-left corner of the box 19 | \param dimMax the lower-right corner of the box 20 | \param coef the enlargement coefficient (1.1 <-> +10%) 21 | **/ 22 | static void EnlargeBox( CCVector3& dimMin, 23 | CCVector3& dimMax, 24 | double coef); 25 | 26 | //! Transforms a 3D box into a 3D cube 27 | /** The cube dimensions will be equal to the largest box dimension. 28 | \param dimMin the upper-left corner of the rectangle 29 | \param dimMax the lower-right corner of the rectangle 30 | \param enlargeFactor the resulting box can be automatically enlarged if this parameter is greater than 0 31 | **/ 32 | static void MakeMinAndMaxCubical( CCVector3& dimMin, 33 | CCVector3& dimMax, 34 | double enlargeFactor); 35 | 36 | //! Computes base vectors for a given 3D plane 37 | /** Determines at least two orthogonal vectors perpendicular to a third one 38 | \param[in] N a non null vector 39 | \param[out] X the first vector (a 3 coordinates array to be updated by the algorithm) 40 | \param[out] Y the second vector (a 3 coordinates array to be updated by the algorithm) 41 | **/ 42 | static void ComputeBaseVectors( const CCVector3 &N, 43 | CCVector3& X, 44 | CCVector3& Y); 45 | //! Computes base vectors for a given 3D plane - double version 46 | /** Determines at least two orthogonal vectors perpendicular to a third one 47 | \param[in] N a non null vector 48 | \param[out] X the first vector (a 3 coordinates array to be updated by the algorithm) 49 | \param[out] Y the second vector (a 3 coordinates array to be updated by the algorithm) 50 | **/ 51 | static void ComputeBaseVectors( const CCVector3d &N, 52 | CCVector3d& X, 53 | CCVector3d& Y); 54 | 55 | //! Ovelap test between a 3D box and a triangle 56 | /** \param boxcenter the box center 57 | \param boxhalfsize the box half dimensions 58 | \param triverts the 3 triangle vertices 59 | \return true if the input box and triangle overlap, false otherwise 60 | **/ 61 | static bool TriBoxOverlap(const CCVector3& boxcenter, 62 | const CCVector3& boxhalfsize, 63 | const CCVector3* triverts[3]); 64 | 65 | //! Ovelap test between a 3D box and a triangle (double version) 66 | /** \param boxcenter the box center 67 | \param boxhalfsize the box half dimensions 68 | \param triverts the 3 triangle vertices 69 | \return true if the input box and triangle overlap, false otherwise 70 | **/ 71 | static bool TriBoxOverlapd(const CCVector3d& boxcenter, 72 | const CCVector3d& boxhalfsize, 73 | const CCVector3d triverts[3]); 74 | }; 75 | } 76 | -------------------------------------------------------------------------------- /include/CCPlatform.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #pragma once 5 | 6 | //Defines the following macros (depending on the compilation platform/settings) 7 | // - CC_WINDOWS / CC_MAC_OS / CC_LINUX 8 | // - CC_ENV32 / CC_ENV64 9 | #if defined(_WIN32) || defined(_WIN64) || defined(WIN32) 10 | #define CC_WINDOWS 11 | #if defined(_WIN64) 12 | #define CC_ENV_64 13 | #else 14 | #define CC_ENV_32 15 | #endif 16 | #else 17 | #if defined(__APPLE__) 18 | #define CC_MAC_OS 19 | #else 20 | #define CC_LINUX 21 | #endif 22 | #if defined(__x86_64__) || defined(__ppc64__) || defined(__arm64__) || defined(__aarch64__) 23 | #define CC_ENV_64 24 | #else 25 | #define CC_ENV_32 26 | #endif 27 | #endif 28 | -------------------------------------------------------------------------------- /include/CCShareable.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #pragma once 5 | 6 | //Local 7 | #include "CCCoreLib.h" 8 | 9 | //system 10 | #include 11 | 12 | //Activate shared objects tracking (for debug purposes) 13 | #ifdef CC_DEBUG 14 | //#define CC_TRACK_ALIVE_SHARED_OBJECTS 15 | #endif 16 | 17 | //CCShareable object (with counter) 18 | class CC_CORE_LIB_API CCShareable 19 | { 20 | public: 21 | 22 | //! Default constructor 23 | CCShareable(); 24 | 25 | //! Increase counter 26 | /** Should be called when this object is 'attached' to another one. 27 | **/ 28 | virtual void link(); 29 | 30 | //! Decrease counter and deletes object when 0 31 | /** Should be called when this object is 'detached'. 32 | **/ 33 | virtual void release(); 34 | 35 | //! Returns the current link count 36 | /** \return current link count 37 | **/ 38 | inline virtual unsigned getLinkCount() const { return m_linkCount; } 39 | 40 | #ifdef CC_TRACK_ALIVE_SHARED_OBJECTS 41 | //! Get alive shared objects count 42 | static unsigned GetAliveCount(); 43 | #endif 44 | 45 | protected: 46 | 47 | //! Destructor 48 | /** private to avoid deletion with 'delete' operator 49 | **/ 50 | virtual ~CCShareable(); 51 | 52 | //! Links counter 53 | unsigned m_linkCount; 54 | }; 55 | -------------------------------------------------------------------------------- /include/CCToolbox.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #pragma once 5 | 6 | //Local 7 | #include "CCCoreLib.h" 8 | 9 | namespace CCCoreLib 10 | { 11 | //! Empty class - for classification purpose only 12 | class CC_CORE_LIB_API CCToolbox {}; 13 | 14 | } 15 | -------------------------------------------------------------------------------- /include/CCTypes.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #pragma once 5 | 6 | #include "CCCoreLib.h" 7 | 8 | //! Type of the coordinates of a (N-D) point 9 | using PointCoordinateType = float; 10 | 11 | //! Type of a single scalar field value 12 | using ScalarType = double; 13 | -------------------------------------------------------------------------------- /include/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | # Copyright © Andy Maloney 3 | 4 | target_sources( ${PROJECT_NAME} 5 | PRIVATE 6 | ${CMAKE_CURRENT_LIST_DIR}/AutoSegmentationTools.h 7 | ${CMAKE_CURRENT_LIST_DIR}/BoundingBox.h 8 | ${CMAKE_CURRENT_LIST_DIR}/CCConst.h 9 | ${CMAKE_CURRENT_LIST_DIR}/CCCoreLib.h 10 | ${CMAKE_CURRENT_LIST_DIR}/CCGeom.h 11 | ${CMAKE_CURRENT_LIST_DIR}/CCMath.h 12 | ${CMAKE_CURRENT_LIST_DIR}/CCMiscTools.h 13 | ${CMAKE_CURRENT_LIST_DIR}/CCPlatform.h 14 | ${CMAKE_CURRENT_LIST_DIR}/CCShareable.h 15 | ${CMAKE_CURRENT_LIST_DIR}/CCToolbox.h 16 | ${CMAKE_CURRENT_LIST_DIR}/CCTypes.h 17 | ${CMAKE_CURRENT_LIST_DIR}/ChamferDistanceTransform.h 18 | ${CMAKE_CURRENT_LIST_DIR}/CloudSamplingTools.h 19 | ${CMAKE_CURRENT_LIST_DIR}/ConjugateGradient.h 20 | ${CMAKE_CURRENT_LIST_DIR}/Delaunay2dMesh.h 21 | ${CMAKE_CURRENT_LIST_DIR}/DgmOctree.h 22 | ${CMAKE_CURRENT_LIST_DIR}/DgmOctreeReferenceCloud.h 23 | ${CMAKE_CURRENT_LIST_DIR}/DistanceComputationTools.h 24 | ${CMAKE_CURRENT_LIST_DIR}/ErrorFunction.h 25 | ${CMAKE_CURRENT_LIST_DIR}/FastMarching.h 26 | ${CMAKE_CURRENT_LIST_DIR}/FastMarchingForPropagation.h 27 | ${CMAKE_CURRENT_LIST_DIR}/Garbage.h 28 | ${CMAKE_CURRENT_LIST_DIR}/GenericCloud.h 29 | ${CMAKE_CURRENT_LIST_DIR}/GenericDistribution.h 30 | ${CMAKE_CURRENT_LIST_DIR}/GenericIndexedCloud.h 31 | ${CMAKE_CURRENT_LIST_DIR}/GenericIndexedCloudPersist.h 32 | ${CMAKE_CURRENT_LIST_DIR}/GenericIndexedMesh.h 33 | ${CMAKE_CURRENT_LIST_DIR}/GenericMesh.h 34 | ${CMAKE_CURRENT_LIST_DIR}/GenericOctree.h 35 | ${CMAKE_CURRENT_LIST_DIR}/GenericProgressCallback.h 36 | ${CMAKE_CURRENT_LIST_DIR}/GenericTriangle.h 37 | ${CMAKE_CURRENT_LIST_DIR}/GeometricalAnalysisTools.h 38 | ${CMAKE_CURRENT_LIST_DIR}/Grid3D.h 39 | ${CMAKE_CURRENT_LIST_DIR}/GridAndMeshIntersection.h 40 | ${CMAKE_CURRENT_LIST_DIR}/Jacobi.h 41 | ${CMAKE_CURRENT_LIST_DIR}/KdTree.h 42 | ${CMAKE_CURRENT_LIST_DIR}/Kriging.h 43 | ${CMAKE_CURRENT_LIST_DIR}/LocalModel.h 44 | ${CMAKE_CURRENT_LIST_DIR}/ManualSegmentationTools.h 45 | ${CMAKE_CURRENT_LIST_DIR}/MathTools.h 46 | ${CMAKE_CURRENT_LIST_DIR}/MeshSamplingTools.h 47 | ${CMAKE_CURRENT_LIST_DIR}/Neighbourhood.h 48 | ${CMAKE_CURRENT_LIST_DIR}/NormalDistribution.h 49 | ${CMAKE_CURRENT_LIST_DIR}/ParallelSort.h 50 | ${CMAKE_CURRENT_LIST_DIR}/PointCloud.h 51 | ${CMAKE_CURRENT_LIST_DIR}/PointCloudTpl.h 52 | ${CMAKE_CURRENT_LIST_DIR}/PointProjectionTools.h 53 | ${CMAKE_CURRENT_LIST_DIR}/Polyline.h 54 | ${CMAKE_CURRENT_LIST_DIR}/RayAndBox.h 55 | ${CMAKE_CURRENT_LIST_DIR}/ReferenceCloud.h 56 | ${CMAKE_CURRENT_LIST_DIR}/RegistrationTools.h 57 | ${CMAKE_CURRENT_LIST_DIR}/SaitoSquaredDistanceTransform.h 58 | ${CMAKE_CURRENT_LIST_DIR}/ScalarField.h 59 | ${CMAKE_CURRENT_LIST_DIR}/ScalarFieldTools.h 60 | ${CMAKE_CURRENT_LIST_DIR}/SimpleMesh.h 61 | ${CMAKE_CURRENT_LIST_DIR}/SimpleTriangle.h 62 | ${CMAKE_CURRENT_LIST_DIR}/SquareMatrix.h 63 | ${CMAKE_CURRENT_LIST_DIR}/StatisticalTestingTools.h 64 | ${CMAKE_CURRENT_LIST_DIR}/TrueKdTree.h 65 | ${CMAKE_CURRENT_LIST_DIR}/WeibullDistribution.h 66 | ) 67 | 68 | install( 69 | FILES 70 | AutoSegmentationTools.h 71 | BoundingBox.h 72 | CCConst.h 73 | CCCoreLib.h 74 | CCGeom.h 75 | CCMath.h 76 | CCMiscTools.h 77 | CCPlatform.h 78 | CCShareable.h 79 | CCToolbox.h 80 | CCTypes.h 81 | ChamferDistanceTransform.h 82 | CloudSamplingTools.h 83 | ConjugateGradient.h 84 | Delaunay2dMesh.h 85 | DgmOctree.h 86 | DgmOctreeReferenceCloud.h 87 | DistanceComputationTools.h 88 | ErrorFunction.h 89 | FastMarching.h 90 | FastMarchingForPropagation.h 91 | Garbage.h 92 | GenericCloud.h 93 | GenericDistribution.h 94 | GenericIndexedCloud.h 95 | GenericIndexedCloudPersist.h 96 | GenericIndexedMesh.h 97 | GenericMesh.h 98 | GenericOctree.h 99 | GenericProgressCallback.h 100 | GenericTriangle.h 101 | GeometricalAnalysisTools.h 102 | GridAndMeshIntersection.h 103 | Grid3D.h 104 | Jacobi.h 105 | KdTree.h 106 | Kriging.h 107 | LocalModel.h 108 | ManualSegmentationTools.h 109 | MathTools.h 110 | MeshSamplingTools.h 111 | Neighbourhood.h 112 | NormalDistribution.h 113 | ParallelSort.h 114 | PointCloud.h 115 | PointCloudTpl.h 116 | PointProjectionTools.h 117 | Polyline.h 118 | RayAndBox.h 119 | ReferenceCloud.h 120 | RegistrationTools.h 121 | SaitoSquaredDistanceTransform.h 122 | ScalarField.h 123 | ScalarFieldTools.h 124 | SimpleMesh.h 125 | SimpleTriangle.h 126 | SquareMatrix.h 127 | StatisticalTestingTools.h 128 | TrueKdTree.h 129 | WeibullDistribution.h 130 | DESTINATION 131 | include/CCCoreLib 132 | ) 133 | 134 | target_include_directories( ${PROJECT_NAME} 135 | PUBLIC 136 | $ 137 | $ 138 | ) 139 | 140 | -------------------------------------------------------------------------------- /include/ChamferDistanceTransform.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #pragma once 5 | 6 | //Local 7 | #include "Grid3D.h" 8 | #include "MathTools.h" 9 | 10 | namespace CCCoreLib 11 | { 12 | 13 | class GenericProgressCallback; 14 | class NormalizedProgress; 15 | 16 | //! Class to compute a Chamfer distance field on a 3D grid 17 | /** Internally we use 'unsigned short' value to limit memory consumption. 18 | For computational reasons, the max computable 'distance' is 0xFAFA = 64250. 19 | **/ 20 | class CC_CORE_LIB_API ChamferDistanceTransform : public Grid3D, public MathTools 21 | { 22 | public: 23 | 24 | //! Max possible 'distance' 25 | /** \warning Never pass a 'constant initializer' by reference 26 | **/ 27 | static const unsigned short MAX_DIST = 0xFAFA; 28 | 29 | //! Initializes the grid 30 | /** 'Zero' cells must be initialized with setValue(0). 31 | The grid must be explicitelty initialized prior to any action. 32 | \return true if the initialization succeeded 33 | **/ 34 | inline bool init(const Tuple3ui& gridSize) 35 | { 36 | return Grid3D::init(gridSize.x, gridSize.y, gridSize.z, 1, MAX_DIST); 37 | } 38 | 39 | //! Computes the Chamfer distance on the whole grid 40 | /** Propagates the distances on the whole grid. The 'zeros' should 41 | have already been initialized before calling this method (see 42 | ChamferDistanceTransform::setZero). 43 | \param type the Chamfer distance type 44 | \param progressCb the client application can get some notification of the process 45 | progress through this callback mechanism (see GenericProgressCallback) 46 | \return max distance (or -1 if an error occurred) 47 | **/ 48 | int propagateDistance(CHAMFER_DISTANCE_TYPE type, GenericProgressCallback* progressCb = nullptr); 49 | }; 50 | 51 | } 52 | -------------------------------------------------------------------------------- /include/ConjugateGradient.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #pragma once 5 | 6 | //Local 7 | #include "MathTools.h" 8 | #include "SquareMatrix.h" 9 | 10 | 11 | namespace CCCoreLib 12 | { 13 | //! A class to perform a conjugate gradient optimization 14 | /** Inspired from the "Numerical Recipes". 15 | Template parameter 'N' is the dimension of the linear system. 16 | Lets "A*Xn=b" be the system to optimize (at iteration n). 17 | First the user must init the A matrix (N*N) and b vector (N*1). 18 | Then the solver is initialized with X0 (see initConjugateGradient). 19 | And the conjugate gradient is iterated with iterConjugateGradient. 20 | **/ 21 | template class ConjugateGradient : MathTools 22 | { 23 | public: 24 | 25 | //! Default constructor 26 | ConjugateGradient() 27 | : cg_A(N) 28 | { 29 | memset(cg_Gn, 0, sizeof(Scalar)*N); 30 | memset(cg_Hn, 0, sizeof(Scalar)*N); 31 | memset(cg_u, 0, sizeof(Scalar)*N); 32 | memset(cg_b, 0, sizeof(Scalar)*N); 33 | } 34 | 35 | //! Default destructor 36 | virtual ~ConjugateGradient() = default; 37 | 38 | //! Returns A matrix 39 | inline SquareMatrixTpl& A() { return cg_A; } 40 | 41 | //! Returns b vector 42 | inline Scalar* b() { return cg_b; } 43 | 44 | //! Initializes the conjugate gradient 45 | /** \param X0 the initial state (size N) 46 | **/ 47 | void initConjugateGradient(const Scalar* X0) 48 | { 49 | //we init the Gn (residuals) and Hn vectors 50 | //H0 = G0 = A.X0-b 51 | cg_A.apply(X0,cg_Gn); 52 | for (unsigned k=0; k cg_A; 122 | }; 123 | } 124 | -------------------------------------------------------------------------------- /include/Delaunay2dMesh.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #pragma once 5 | 6 | //Local 7 | #include "GenericIndexedMesh.h" 8 | #include "SimpleTriangle.h" 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | namespace CCCoreLib 15 | { 16 | class GenericIndexedCloud; 17 | class GenericIndexedCloudPersist; 18 | class Polyline; 19 | 20 | //! A class to compute and handle a Delaunay 2D mesh on a subset of points 21 | class CC_CORE_LIB_API Delaunay2dMesh : public GenericIndexedMesh 22 | { 23 | public: 24 | static constexpr int USE_ALL_POINTS = 0; 25 | 26 | //! Delaunay2dMesh constructor 27 | Delaunay2dMesh(); 28 | 29 | //! Delaunay2dMesh destructor 30 | ~Delaunay2dMesh() override; 31 | 32 | //! Returns whether 2D Delaunay triangulation is supported or not 33 | /** 2D Delaunay triangulation requires the CGAL library. 34 | **/ 35 | static bool Available(); 36 | 37 | //! Associate this mesh to a point cloud 38 | /** This particular mesh structure deals with point indexes instead of points. 39 | Therefore, it is possible to change the associated point cloud (if the 40 | new cloud has the same size). For example, it can be useful to compute 41 | the mesh on 2D points corresponding to 3D points that have been projected 42 | on a plane and then to link this structure with the 3D original points. 43 | \param aCloud a point cloud 44 | \param passOwnership if true the Delaunay2dMesh destructor will delete the cloud as well 45 | **/ 46 | virtual void linkMeshWith(GenericIndexedCloud* aCloud, bool passOwnership = false); 47 | 48 | //! Build the Delaunay mesh on top a set of 2D points 49 | /** \param points2D a set of 2D points 50 | \param pointCountToUse number of points to use from the input set (USE_ALL_POINTS = all) 51 | \param outputErrorStr error string as output by the CGAL lib. (if any) 52 | \return success 53 | **/ 54 | virtual bool buildMesh( const std::vector& points2D, 55 | std::size_t pointCountToUse, 56 | std::string& outputErrorStr ); 57 | 58 | //! Build the Delaunay mesh from a set of 2D polylines 59 | /** \param points2D a set of 2D points 60 | \param segments2D constraining segments (as 2 indexes per segment) 61 | \param outputErrorStr error string as output by the CGAL lib. (if any) 62 | \return success 63 | **/ 64 | virtual bool buildMesh( const std::vector& points2D, 65 | const std::vector& segments2D, 66 | std::string& outputErrorStr ); 67 | 68 | //! Removes the triangles falling outside of a given (2D) polygon 69 | /** \param vertices2D vertices of the mesh as 2D points (typically the one used to triangulate the mesh!) 70 | \param polygon2D vertices of the 2D boundary polygon (ordered) 71 | \param removeOutside whether to remove triangles outside (default) or inside 72 | \return success 73 | **/ 74 | virtual bool removeOuterTriangles( const std::vector& vertices2D, 75 | const std::vector& polygon2D, 76 | bool removeOutside = true); 77 | 78 | 79 | 80 | //inherited methods (see GenericMesh) 81 | unsigned size() const override { return m_numberOfTriangles; } 82 | void forEach(genericTriangleAction action) override; 83 | void getBoundingBox(CCVector3& bbMin, CCVector3& bbMax) override; 84 | void placeIteratorAtBeginning() override; 85 | GenericTriangle* _getNextTriangle() override; 86 | GenericTriangle* _getTriangle(unsigned triangleIndex) override; 87 | VerticesIndexes* getNextTriangleVertIndexes() override; 88 | VerticesIndexes* getTriangleVertIndexes(unsigned triangleIndex) override; 89 | void getTriangleVertices(unsigned triangleIndex, CCVector3& A, CCVector3& B, CCVector3& C) const override; 90 | 91 | //! Returns triangles indexes array (pointer to) 92 | /** Handle with care! 93 | **/ 94 | inline int* getTriangleVertIndexesArray() { return m_triIndexes.data(); } 95 | 96 | //! Filters out the triangles based on their edge length 97 | /** Warning: may remove ALL triangles! 98 | Check the resulting size afterwards. 99 | **/ 100 | bool removeTrianglesWithEdgesLongerThan(PointCoordinateType maxEdgeLength); 101 | 102 | //! Returns associated cloud 103 | inline GenericIndexedCloud* getAssociatedCloud() { return m_associatedCloud; } 104 | 105 | //! Tesselates a 2D polyline (shortcut to buildMesh and removeOuterTriangles) 106 | static Delaunay2dMesh* TesselateContour(const std::vector& contourPoints); 107 | 108 | //! Tesselates a 2D polyline (not necessarily axis-aligned) 109 | static Delaunay2dMesh* TesselateContour(GenericIndexedCloudPersist* contourPoints, int flatDimension = -1); 110 | 111 | 112 | protected: 113 | 114 | //! Associated point cloud 115 | GenericIndexedCloud* m_associatedCloud; 116 | 117 | //! Triangle vertex indexes 118 | std::vector m_triIndexes; 119 | 120 | //! Iterator on the list of triangle vertex indexes 121 | int* m_globalIterator; 122 | 123 | //! End position of global iterator 124 | int* m_globalIteratorEnd; 125 | 126 | //! The number of triangles 127 | unsigned m_numberOfTriangles; 128 | 129 | //! Specifies if the associated cloud should be deleted when the mesh is deleted 130 | bool m_cloudIsOwnedByMesh; 131 | 132 | //! Dump triangle structure to transmit temporary data 133 | SimpleTriangle m_dumpTriangle; 134 | 135 | //! Dump triangle index structure to transmit temporary data 136 | VerticesIndexes m_dumpTriangleIndexes; 137 | }; 138 | } 139 | -------------------------------------------------------------------------------- /include/DgmOctreeReferenceCloud.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #pragma once 5 | 6 | //Local 7 | #include "DgmOctree.h" 8 | #include "GenericIndexedCloudPersist.h" 9 | 10 | 11 | namespace CCCoreLib 12 | { 13 | //! A kind of ReferenceCloud based on the DgmOctree::NeighboursSet structure 14 | class CC_CORE_LIB_API DgmOctreeReferenceCloud : public GenericIndexedCloudPersist 15 | { 16 | public: 17 | 18 | //! Default constructor. 19 | /** \param associatedSet associated NeighboursSet 20 | \param count number of values to use (0 = all) 21 | **/ 22 | DgmOctreeReferenceCloud(DgmOctree::NeighboursSet* associatedSet, unsigned count = 0); 23 | 24 | //**** inherited form GenericCloud ****// 25 | inline unsigned size() const override { return m_size; } 26 | void forEach(genericPointAction action) override; 27 | void getBoundingBox(CCVector3& bbMin, CCVector3& bbMax) override; 28 | //virtual unsigned char testVisibility(const CCVector3& P) const; //not supported 29 | inline void placeIteratorAtBeginning() override { m_globalIterator = 0; } 30 | inline const CCVector3* getNextPoint() override { return (m_globalIterator < size() ? m_set->at(m_globalIterator++).point : nullptr); } 31 | inline bool enableScalarField() override { return true; } //use DgmOctree::PointDescriptor::squareDistd by default 32 | inline bool isScalarFieldEnabled() const override { return true; } //use DgmOctree::PointDescriptor::squareDistd by default 33 | inline void setPointScalarValue(unsigned pointIndex, ScalarType value) override { assert(pointIndex < size()); m_set->at(pointIndex).squareDistd = static_cast(value); } 34 | inline ScalarType getPointScalarValue(unsigned pointIndex) const override { assert(pointIndex < size()); return static_cast(m_set->at(pointIndex).squareDistd); } 35 | //**** inherited form GenericIndexedCloud ****// 36 | inline const CCVector3* getPoint(unsigned index) const override { assert(index < size()); return m_set->at(index).point; } 37 | inline void getPoint(unsigned index, CCVector3& P) const override { assert(index < size()); P = *m_set->at(index).point; } 38 | //**** inherited form GenericIndexedCloudPersist ****// 39 | inline const CCVector3* getPointPersistentPtr(unsigned index) const override { assert(index < size()); return m_set->at(index).point; } 40 | 41 | //! Forwards global iterator 42 | inline void forwardIterator() { ++m_globalIterator; } 43 | 44 | //! Returns a given point descriptor 45 | inline const DgmOctree::PointDescriptor& getPointDescriptor(unsigned pointIndex) const { assert(pointIndex < size()); return m_set->at(pointIndex); } 46 | 47 | protected: 48 | 49 | //! Computes the cloud bounding-box (internal) 50 | virtual void computeBB(); 51 | 52 | //! Iterator on the point references container 53 | unsigned m_globalIterator; 54 | 55 | //! Bounding-box min corner 56 | CCVector3 m_bbMin; 57 | //! Bounding-box max corner 58 | CCVector3 m_bbMax; 59 | //! Bounding-box validity 60 | bool m_validBB; 61 | 62 | //! Associated PointDescriptor set 63 | DgmOctree::NeighboursSet* m_set; 64 | 65 | //! Number of points 66 | unsigned m_size; 67 | }; 68 | } 69 | -------------------------------------------------------------------------------- /include/ErrorFunction.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #pragma once 5 | 6 | //Local 7 | #include "CCCoreLib.h" 8 | #include "MathTools.h" 9 | 10 | namespace CCCoreLib 11 | { 12 | //! Relative error for Error Function computation 13 | /** You can adjust it to trade off between accuracy and speed 14 | but don't ask for more than 15 figures (assuming usual 52 bit mantissa 15 | in a double). Example: 1E-12 <--> calculate 12 significant figures 16 | **/ 17 | static const double c_erfRelativeError = 1e-12; 18 | 19 | //! A class to compute the Error function (erf) 20 | /** See for example http://mathworld.wolfram.com/Erf.html. 21 | Most of the code comes from "erf.cpp" by Steve Strand 22 | (29-Jan-04). 23 | **/ 24 | class CC_CORE_LIB_API ErrorFunction : MathTools 25 | { 26 | public: 27 | 28 | //! Computes erfc(x) 29 | /** erfc(x) = 2/sqrt(pi)*integral(exp(-t^2),t,x,inf) 30 | = exp(-x^2)/sqrt(pi) * [1/x+ (1/2)/x+ (2/2)/x+ (3/2)/x+ (4/2)/x+ ...] 31 | = 1-erf(x) 32 | (expression inside [] is a continued fraction so '+' means add to denominator 33 | only). 34 | \param x a real variable 35 | \return erfc(x) 36 | **/ 37 | static double erfc(double x); 38 | 39 | //! Computes erf(x) 40 | /** erf(x) = 2/sqrt(pi)*integral(exp(-t^2),t,0,x) 41 | = 2/sqrt(pi)*[x - x^3/3 + x^5/5*2! - x^7/7*3! + ...] 42 | = 1-erfc(x) 43 | \param x a real variable 44 | \return erf(x) 45 | **/ 46 | static double erf(double x); 47 | }; 48 | } 49 | -------------------------------------------------------------------------------- /include/FastMarchingForPropagation.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #pragma once 5 | 6 | //local 7 | #include "DgmOctree.h" 8 | #include "FastMarching.h" 9 | 10 | namespace CCCoreLib 11 | { 12 | class ReferenceCloud; 13 | class GenericCloud; 14 | 15 | //! Fast Marching algorithm for surface front propagation 16 | /** Extends the FastMarching class. 17 | **/ 18 | class FastMarchingForPropagation : public FastMarching 19 | { 20 | public: 21 | 22 | //! Default constructor 23 | FastMarchingForPropagation(); 24 | 25 | //! Initializes the grid with a point cloud (and ist corresponding octree) 26 | /** The points should be associated to scalar values. 27 | Warning: be sure to activate an OUTPUT scalar field on the input cloud 28 | The Fast Marching grid will have the same characteristics as 29 | the octree considered at a given level of subdivision. The local 30 | front acceleration in each cell is deduced from the scalar values 31 | associated to the points lying in the octree cell (mean value). 32 | \param theCloud the point cloud 33 | \param theOctree the associated octree 34 | \param gridLevel the level of subdivision 35 | \param constantAcceleration specifies if the acceleration is constant or shoul be computed from the cell points scalar values 36 | \return a negative value if something went wrong 37 | **/ 38 | int init(GenericCloud* theCloud, 39 | DgmOctree* theOctree, 40 | unsigned char gridLevel, 41 | bool constantAcceleration = false); 42 | 43 | //! Returns a list of the points (references to) reached by the propagation process 44 | /** Returns a cloud of points (references to) corresponding to the points that are 45 | lying in cells that have been visited by the last propagation process. 46 | \param[out] Zk (reference) point cloud 47 | **/ 48 | bool extractPropagatedPoints(ReferenceCloud* Zk); 49 | 50 | //! Sets the propagation timings as distances for each point 51 | /** \return true if ok, false otherwise 52 | **/ 53 | bool setPropagationTimingsAsDistances(); 54 | 55 | //! Sets the threshold for propagation stop 56 | /** This threshold corresponds to the maximum front arrival time 57 | increase allowed. If the delta between the fornt arrival time at 58 | two consecutive cells is higher, the propagation process is stoped. 59 | \param value the threshold 60 | **/ 61 | void setDetectionThreshold(float value) { m_detectionThreshold = value; } 62 | 63 | //! Sets the accceleration exageration factor 64 | /** In order to detect the front arrival time jumps (see 65 | FastMarchingForPropagation::setDetectionThreshold), it 66 | is possible to exaggerate the result of the acceleration 67 | computation with this factor. 68 | \param value the acceleration exageration factor 69 | **/ 70 | void setJumpCoef(float value) { m_jumpCoef = value; } 71 | 72 | //! Find peaks of local acceleration values 73 | /** This method is useful when using this Fast Marching 74 | algorithm in a Watershed process. Peak cells are 75 | automatically set as "seeds". 76 | **/ 77 | void findPeaks(); 78 | 79 | //inherited methods (see FastMarching) 80 | int propagate() override; 81 | 82 | protected: 83 | 84 | //! A Fast Marching grid cell for surfacical propagation 85 | class PropagationCell : public Cell 86 | { 87 | public: 88 | //! Default constructor 89 | PropagationCell() 90 | : Cell() 91 | , f(0) 92 | , cellCode(0) 93 | {} 94 | 95 | //! Destructor 96 | ~PropagationCell() override = default; 97 | 98 | //! Local front acceleration 99 | float f; 100 | //! Equivalent cell code in the octree 101 | DgmOctree::CellCode cellCode; 102 | }; 103 | 104 | //inherited methods (see FastMarching) 105 | float computeTCoefApprox(Cell* currentCell, Cell* neighbourCell) const override; 106 | int step() override; 107 | bool instantiateGrid(unsigned size) override { return instantiateGridTpl(size); } 108 | 109 | //! Accceleration exageration factor 110 | float m_jumpCoef; 111 | //! Threshold for propagation stop 112 | float m_detectionThreshold; 113 | }; 114 | } 115 | -------------------------------------------------------------------------------- /include/Garbage.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #pragma once 5 | 6 | //local 7 | #include "ScalarField.h" 8 | 9 | //STL 10 | #include 11 | 12 | namespace CCCoreLib 13 | { 14 | //! Garbage container (automatically deletes pointers when destroyed) 15 | template class Garbage 16 | { 17 | public: 18 | //! Puts an item in the trash 19 | inline void add(C* item) 20 | { 21 | try 22 | { 23 | m_items.insert(item); 24 | } 25 | catch (const std::bad_alloc&) 26 | { 27 | //what can we do?! 28 | } 29 | } 30 | 31 | //! Removes an item from the trash 32 | /** \warning The item won't be destroyed! **/ 33 | inline void remove(C* item) 34 | { 35 | m_items.erase(item); 36 | } 37 | 38 | //! To manually delete an item already in the trash 39 | inline void destroy(C* item) 40 | { 41 | m_items.erase(item); 42 | delete item; 43 | } 44 | 45 | //! Destructor 46 | /** Automatically deletes all items **/ 47 | ~Garbage() 48 | { 49 | //dispose of left over 50 | for (auto it = m_items.begin(); it != m_items.end(); ++it) 51 | delete *it; 52 | m_items.clear(); 53 | } 54 | 55 | //! Items to delete 56 | std::unordered_set m_items; 57 | }; 58 | 59 | //! Specialization for ScalarFields 60 | template <> class Garbage 61 | { 62 | public: 63 | //! Puts an item in the trash 64 | inline void add(ScalarField* item) 65 | { 66 | try 67 | { 68 | m_items.insert(item); 69 | } 70 | catch (const std::bad_alloc&) 71 | { 72 | //what can we do?! 73 | } 74 | } 75 | 76 | //! Removes an item from the trash 77 | /** \warning The item won't be destroyed! **/ 78 | inline void remove(ScalarField* item) 79 | { 80 | m_items.erase(item); 81 | } 82 | 83 | //! Manually deltes an item already in the trash 84 | inline void destroy(ScalarField* item) 85 | { 86 | m_items.erase(item); 87 | item->release(); 88 | } 89 | 90 | //! Destructor 91 | /** Automatically deletes all items **/ 92 | ~Garbage() 93 | { 94 | //dispose of left over 95 | for (auto item : m_items) 96 | { 97 | item->release(); 98 | } 99 | m_items.clear(); 100 | } 101 | 102 | //! Items to delete 103 | std::unordered_set m_items; 104 | }; 105 | } 106 | -------------------------------------------------------------------------------- /include/GenericCloud.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | //Local 9 | #include "CCConst.h" 10 | #include "CCGeom.h" 11 | 12 | namespace CCCoreLib 13 | { 14 | //! A generic 3D point cloud interface for data communication between library and client applications 15 | class CC_CORE_LIB_API GenericCloud 16 | { 17 | 18 | public: 19 | 20 | //! Default constructor 21 | GenericCloud() = default; 22 | 23 | //! Default destructor 24 | virtual ~GenericCloud() = default; 25 | 26 | //! Generic function applied to a point (used by foreach) 27 | using genericPointAction = std::function; 28 | 29 | //! Returns the number of points 30 | /** Virtual method to request the cloud size 31 | \return the cloud size 32 | **/ 33 | virtual unsigned size() const = 0; 34 | 35 | //! Fast iteration mechanism 36 | /** Virtual method to apply a function to the whole cloud 37 | \param action the function to apply (see GenericCloud::genericPointAction) 38 | **/ 39 | virtual void forEach(genericPointAction action) = 0; 40 | 41 | //! Returns the cloud bounding box 42 | /** Virtual method to request the cloud bounding box limits 43 | \param bbMin lower bounding-box limits (Xmin,Ymin,Zmin) 44 | \param bbMax higher bounding-box limits (Xmax,Ymax,Zmax) 45 | **/ 46 | virtual void getBoundingBox(CCVector3& bbMin, CCVector3& bbMax) = 0; 47 | 48 | //! Returns a given point visibility state (relatively to a sensor for instance) 49 | /** Generic method to request a point visibility (should be overloaded if this functionality is required). 50 | The point visibility is such as defined in Daniel Girardeau-Montaut's PhD manuscript (see Chapter 2, 51 | section 2-3-3). In this case, a ground based laser sensor model should be used to determine it. 52 | This method is called before performing any point-to-cloud comparison. If the result is not 53 | POINT_VISIBLE, then the comparison won't be performed and the scalar field value associated 54 | to this point will be this visibility value. 55 | \param P the 3D point to test 56 | \return visibility (default: POINT_VISIBLE) 57 | **/ 58 | virtual inline unsigned char testVisibility(const CCVector3& P) const { (void)P; return POINT_VISIBLE; } 59 | 60 | //! Sets the cloud iterator at the beginning 61 | /** Virtual method to handle the cloud global iterator 62 | **/ 63 | virtual void placeIteratorAtBeginning() = 0; 64 | 65 | //! Returns the next point (relatively to the global iterator position) 66 | /** Virtual method to handle the cloud global iterator. 67 | Global iterator position should be increased by one each time 68 | this method is called. 69 | Warning: 70 | - the returned object may not be persistent! 71 | - THIS METHOD MAY NOT BE COMPATIBLE WITH PARALLEL STRATEGIES 72 | (see the DgmOctree::executeFunctionForAllCellsAtLevel_MT and 73 | DgmOctree::executeFunctionForAllCellsAtStartingLevel_MT methods). 74 | \return pointer on next point (or 0 if no more) 75 | **/ 76 | virtual const CCVector3* getNextPoint() = 0; 77 | 78 | //! Enables the scalar field associated to the cloud 79 | /** If the scalar field structure is not yet initialized/allocated, 80 | this method gives the signal for its creation. Otherwise, if possible 81 | the structure size should be pre-reserved with the same number of 82 | elements as the point cloud. 83 | \warning If the cloud is empty, the scalar field will be empty as well. 84 | The scalar field will be reserved with the same capacity as the cloud. 85 | **/ 86 | virtual bool enableScalarField() = 0; 87 | 88 | //! Returns true if the scalar field is enabled, false otherwise 89 | virtual bool isScalarFieldEnabled() const = 0; 90 | 91 | //! Sets the ith point associated scalar value 92 | virtual void setPointScalarValue(unsigned pointIndex, ScalarType value) = 0; 93 | 94 | //! Returns the ith point associated scalar value 95 | virtual ScalarType getPointScalarValue(unsigned pointIndex) const = 0; 96 | }; 97 | 98 | } 99 | -------------------------------------------------------------------------------- /include/GenericDistribution.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #pragma once 5 | 6 | //Local 7 | #include "ScalarField.h" 8 | 9 | namespace CCCoreLib 10 | { 11 | class GenericCloud; 12 | 13 | //! A generic class to handle a probability distribution 14 | /** Custom parametric distributions can be implemented through this 15 | interface and used for filtering data (see StatisticalTestingTools). 16 | **/ 17 | class CC_CORE_LIB_API GenericDistribution 18 | { 19 | public: 20 | 21 | //! Default constructor 22 | GenericDistribution() : m_isValid(false) {} 23 | 24 | //! Default destructor 25 | virtual ~GenericDistribution() = default; 26 | 27 | //! Returns distribution name 28 | virtual const char* getName() const = 0; 29 | 30 | //! Indicates if the distribution parameters are valid 31 | /** This function is related to 'computeParameters()'. If the parameters 32 | computation failed, then the parameters will be marked as 'invalid'. In this 33 | case, the dsitribution should'nt be used (most of the methods won't work anyway). 34 | \return true (if the distribution parameters are valid) or false (if not) 35 | **/ 36 | virtual bool isValid() const { return m_isValid; } 37 | 38 | //! Scalar values container interface 39 | struct ScalarContainer 40 | { 41 | virtual size_t size() const = 0; 42 | virtual ScalarType getValue(size_t index) const = 0; 43 | }; 44 | 45 | //! Wrapper of a CCCoreLib's scalar field as a Scalar values container 46 | struct SFAsScalarContainer : ScalarContainer 47 | { 48 | SFAsScalarContainer(const ScalarField& sf) 49 | : ScalarContainer() 50 | , m_sf(sf) 51 | {} 52 | 53 | inline size_t size() const override { return m_sf.size(); } 54 | inline ScalarType getValue(size_t index) const override { return m_sf.getValue(index); } 55 | 56 | const CCCoreLib::ScalarField& m_sf; 57 | }; 58 | 59 | //! Wrapper of a std::vector as a Scalar values container 60 | struct VectorAsScalarContainer : ScalarContainer 61 | { 62 | VectorAsScalarContainer(const std::vector& vector) 63 | : ScalarContainer() 64 | , m_vector(vector) 65 | {} 66 | 67 | inline size_t size() const override { return m_vector.size(); } 68 | inline ScalarType getValue(size_t index) const override { return m_vector[index]; } 69 | 70 | const std::vector& m_vector; 71 | }; 72 | 73 | //! Computes the distribution parameters from a set of values 74 | /** \param values a set of scalar values 75 | \return true (if the computation succeeded) or false (if not) 76 | **/ 77 | virtual bool computeParameters(const ScalarContainer& values) = 0; 78 | 79 | //! Computes the probability of x 80 | /** \param x the variable 81 | \return the probabilty 82 | **/ 83 | virtual double computeP(ScalarType x) const = 0; 84 | 85 | //! Computes the cumulative probability between 0 and x 86 | /** \param x the upper boundary 87 | \return the cumulative probabilty 88 | **/ 89 | virtual double computePfromZero(ScalarType x) const = 0; 90 | 91 | //! Computes the cumulative probability between x1 and x2 92 | /** x1 should be lower than x2 93 | \param x1 the lower boundary 94 | \param x2 the upper boundary 95 | \return the cumulative probabilty 96 | **/ 97 | virtual double computeP(ScalarType x1, ScalarType x2) const = 0; 98 | 99 | //! Computes the Chi2 distance (related to the Chi2 Test) 100 | /** Computes the Chi2 distance from a group of point, accordingly to 101 | a certain number of classes (see Chi2 test theory for more information). 102 | The result of projecting each point (or more precisely each scalar value 103 | associated to each point) in the different classes can be stored in an array 104 | (of the same size as the number of classes). To do so, such an array (already 105 | allocated in memory) should be passed as an argument. 106 | Warning: be sure to activate an OUTPUT scalar field on the input cloud 107 | \param Yk a group of points 108 | \param numberOfClasses the number of classes for the Chi2 Test 109 | \param histo an array to store the values projection result (optional) 110 | \return the Chi2 distance (or -1.0 if an error occurred) 111 | **/ 112 | virtual double computeChi2Dist(const GenericCloud* Yk, unsigned numberOfClasses, int* histo = nullptr) = 0; 113 | 114 | protected: 115 | 116 | //! Sets distribution current validity 117 | void setValid(bool state) { m_isValid = state; } 118 | 119 | //! Whether the distribution is in a valid state or not 120 | bool m_isValid; 121 | }; 122 | 123 | } 124 | -------------------------------------------------------------------------------- /include/GenericIndexedCloud.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #pragma once 5 | 6 | //Local 7 | #include "GenericCloud.h" 8 | 9 | namespace CCCoreLib 10 | { 11 | //! A generic 3D point cloud with index-based point access 12 | /** Implements the GenericCloud interface. 13 | **/ 14 | class CC_CORE_LIB_API GenericIndexedCloud : virtual public GenericCloud 15 | { 16 | 17 | public: 18 | //! Default constructor 19 | GenericIndexedCloud() = default; 20 | 21 | //! Default destructor 22 | ~GenericIndexedCloud() override = default; 23 | 24 | //! Returns the ith point 25 | /** Virtual method to request a point with a specific index. 26 | WARNINGS: 27 | - the returned object may not be persistent! 28 | - THIS METHOD MAY NOT BE COMPATIBLE WITH PARALLEL STRATEGIES 29 | (see the DgmOctree::executeFunctionForAllCellsAtLevel_MT and 30 | DgmOctree::executeFunctionForAllCellsAtStartingLevel_MT methods). 31 | Consider the other version of getPoint instead or the 32 | GenericIndexedCloudPersist class. 33 | \param index of the requested point (between 0 and the cloud size minus 1) 34 | \return the requested point (undefined behavior if index is invalid) 35 | **/ 36 | virtual const CCVector3* getPoint(unsigned index) const = 0; 37 | 38 | //! Returns the ith point 39 | /** Virtual method to request a point with a specific index. 40 | Index must be valid (undefined behavior if index is invalid) 41 | \param index of the requested point (between 0 and the cloud size minus 1) 42 | \param P output point 43 | **/ 44 | virtual void getPoint(unsigned index, CCVector3& P) const = 0; 45 | 46 | //! Returns whether normals are available 47 | virtual bool normalsAvailable() const { return false; } 48 | 49 | //! If per-point normals are available, returns the one at a specific index 50 | /** \warning If overriden, this method should return a valid normal for all points 51 | **/ 52 | virtual const CCVector3* getNormal(unsigned index) const { (void)index; return nullptr; } 53 | }; 54 | } 55 | -------------------------------------------------------------------------------- /include/GenericIndexedCloudPersist.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #pragma once 5 | 6 | //Local 7 | #include "GenericIndexedCloud.h" 8 | 9 | namespace CCCoreLib 10 | { 11 | //! A generic 3D point cloud with index-based and presistent access to points 12 | /** Implements the GenericIndexedCloud interface. 13 | **/ 14 | class CC_CORE_LIB_API GenericIndexedCloudPersist : virtual public GenericIndexedCloud 15 | { 16 | public: 17 | //! Default constructor 18 | GenericIndexedCloudPersist() = default; 19 | 20 | //! Mock constructor for compatibility with the PointCloudTpl interface 21 | /** \warning Parameters are simply ignored 22 | \param name ignored 23 | \param ID ignored 24 | **/ 25 | GenericIndexedCloudPersist(const char* name, unsigned ID) { (void)name; (void)ID; /* input parameters are ignored */ } 26 | 27 | 28 | //! Default destructor 29 | ~GenericIndexedCloudPersist() override = default; 30 | 31 | //! Returns the ith point as a persistent pointer 32 | /** Virtual method to request a point with a specific index. 33 | WARNING: the returned object MUST be persistent in order 34 | to be compatible with parallel strategies! 35 | \param index of the requested point (between 0 and the cloud size minus 1) 36 | \return the requested point (or 0 if index is invalid) 37 | **/ 38 | virtual const CCVector3* getPointPersistentPtr(unsigned index) const = 0; 39 | }; 40 | } 41 | -------------------------------------------------------------------------------- /include/GenericIndexedMesh.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #pragma once 5 | 6 | //Local 7 | #include "GenericMesh.h" 8 | 9 | #ifdef _MSC_VER 10 | //To get rid of the warning about unnamed struct/union 11 | #pragma warning( disable: 4201 ) 12 | #endif 13 | 14 | namespace CCCoreLib 15 | { 16 | //! Triangle described by the indexes of its 3 vertices 17 | struct VerticesIndexes 18 | { 19 | union 20 | { 21 | struct 22 | { 23 | unsigned i1, i2, i3; 24 | }; 25 | unsigned i[3]; 26 | }; 27 | 28 | //! Constructor with specified indexes 29 | VerticesIndexes(unsigned _i1, unsigned _i2, unsigned _i3) 30 | : i1(_i1) 31 | , i2(_i2) 32 | , i3(_i3) 33 | {} 34 | 35 | //! Default constructor 36 | VerticesIndexes() 37 | : i1(0) 38 | , i2(0) 39 | , i3(0) 40 | {} 41 | }; 42 | 43 | //! A generic mesh with index-based vertex access 44 | /** Implements the GenericMesh interface. 45 | **/ 46 | class CC_CORE_LIB_API GenericIndexedMesh : public GenericMesh 47 | { 48 | public: 49 | 50 | //! Default destructor 51 | ~GenericIndexedMesh() override = default; 52 | 53 | //! Returns the ith triangle 54 | /** Virtual method to request a triangle with a specific index. 55 | The returned object can be temporary. 56 | \param triangleIndex of the requested triangle (between 0 and the mesh size-1) 57 | \return the requested triangle, or 0 if index value is not valid 58 | **/ 59 | virtual GenericTriangle* _getTriangle(unsigned triangleIndex) = 0; 60 | 61 | //! Returns the indexes of the vertices of a given triangle 62 | /** \param triangleIndex index of the triangle (between 0 and size(mesh)-1) 63 | \return the triangle indexes (or 0 if index value is not valid) 64 | **/ 65 | virtual VerticesIndexes* getTriangleVertIndexes(unsigned triangleIndex) = 0; 66 | 67 | //! Returns the vertices of a given triangle 68 | /** \param[in] triangleIndex index of the triangle (between 0 and the size(mesh)-1) 69 | \param[out] A first vertex 70 | \param[out] B second vertex 71 | \param[out] C third vertex 72 | **/ 73 | virtual void getTriangleVertices(unsigned triangleIndex, CCVector3& A, CCVector3& B, CCVector3& C) const = 0; 74 | 75 | //! Returns the indexes of the vertices of the next triangle (relatively to the global iterator position) 76 | /** \return the triangle indexes (or 0 if the global iterator is out of bounds) 77 | **/ 78 | virtual VerticesIndexes* getNextTriangleVertIndexes() = 0; 79 | 80 | //! Returns whether normals are available 81 | virtual bool normalsAvailable() const { return false; } 82 | 83 | //! Interpolates normal(s) inside a given triangle 84 | /** This method should be ideally overriden by the child class if normals are supported 85 | \param[in] triIndex triangle index 86 | \param[in] P point where to interpolate (should be inside the triangle!) 87 | \param[out] N interpolated normal 88 | \return success 89 | **/ 90 | virtual bool interpolateNormals(unsigned triIndex, const CCVector3& P, CCVector3& N) { (void)triIndex; (void)P; (void)N; return false; } 91 | }; 92 | } 93 | 94 | #ifdef _MSC_VER 95 | //Restore the default warning behavior 96 | #pragma warning( default: 4201 ) 97 | #endif 98 | -------------------------------------------------------------------------------- /include/GenericMesh.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | //Local 9 | #include "CCGeom.h" 10 | 11 | namespace CCCoreLib 12 | { 13 | class GenericTriangle; 14 | 15 | //! A generic mesh interface for data communication between library and client applications 16 | class CC_CORE_LIB_API GenericMesh 17 | { 18 | public: 19 | 20 | //! Default destructor 21 | virtual ~GenericMesh() = default; 22 | 23 | //! Generic function to apply to a triangle (used by foreach) 24 | using genericTriangleAction = std::function; 25 | 26 | //! Returns the number of triangles 27 | /** Virtual method to request the mesh size 28 | \return the mesh size 29 | **/ 30 | virtual unsigned size() const = 0; 31 | 32 | //! Fast iteration mechanism 33 | /** Virtual method to apply a function to the whole mesh 34 | \param action function to apply (see GenericMesh::genericTriangleAction) 35 | **/ 36 | virtual void forEach(genericTriangleAction action) = 0; 37 | 38 | //! Returns the mesh bounding-box 39 | /** Virtual method to request the mesh bounding-box limits. It is equivalent to 40 | the bounding-box of the cloud composed of the mesh vertexes. 41 | \param bbMin lower bounding-box limits (Xmin,Ymin,Zmin) 42 | \param bbMax higher bounding-box limits (Xmax,Ymax,Zmax) 43 | **/ 44 | virtual void getBoundingBox(CCVector3& bbMin, CCVector3& bbMax) = 0; 45 | 46 | //! Places the mesh iterator at the beginning 47 | /** Virtual method to handle the mesh global iterator 48 | **/ 49 | virtual void placeIteratorAtBeginning() = 0; 50 | 51 | //! Returns the next triangle (relatively to the global iterator position) 52 | /** Virtual method to handle the mesh global iterator. 53 | Global iterator position should be increased each time 54 | this method is called. The returned object can be temporary. 55 | \return a triangle 56 | **/ 57 | virtual GenericTriangle* _getNextTriangle() = 0; //temporary 58 | }; 59 | } 60 | -------------------------------------------------------------------------------- /include/GenericOctree.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #pragma once 5 | 6 | //Local 7 | #include "CCGeom.h" 8 | 9 | namespace CCCoreLib 10 | { 11 | //! A generic octree interface for data communication between library and client applications 12 | class CC_CORE_LIB_API GenericOctree 13 | { 14 | public: 15 | 16 | //! Default destructor 17 | virtual ~GenericOctree() = default; 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /include/GenericProgressCallback.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #pragma once 5 | 6 | //Local 7 | #include "CCConst.h" 8 | #include "CCCoreLib.h" 9 | 10 | namespace CCCoreLib 11 | { 12 | //! A generic progress indicator interface to notify algorithms progress to the client application 13 | class CC_CORE_LIB_API GenericProgressCallback 14 | { 15 | public: 16 | 17 | //! Default destructor 18 | virtual ~GenericProgressCallback() = default; 19 | 20 | //! Notifies the algorithm progress 21 | /** The notification is sent by the running algorithm (on the library side). 22 | This virtual method shouldn't be called too often, as the real process 23 | behind it is unspecified and may be time consuming. Ideally it shouldn't 24 | be called more than a few hundreds time. 25 | \param percent current progress, between 0.0 and 100.0 26 | **/ 27 | virtual void update(float percent) = 0; 28 | 29 | //! Notifies the algorithm title 30 | /** The notification is sent by the ongoing algorithm (on the library side). 31 | \param methodTitle the algorithm title 32 | **/ 33 | virtual void setMethodTitle(const char* methodTitle) = 0; 34 | 35 | //! Notifies some information about the ongoing process 36 | /** The notification is sent by the ongoing algorithm (on the library side). 37 | \param infoStr some textual information about the ongoing process 38 | **/ 39 | virtual void setInfo(const char* infoStr) = 0; 40 | 41 | //! Notifies the fact that every information has been sent and that the process begins 42 | /** Once start() is called, the progress bar and other informations could be displayed (for example). 43 | **/ 44 | virtual void start() = 0; 45 | 46 | //! Notifies the fact that the process has ended 47 | /** Once end() is called, the progress bar and other informations could be hidden (for example). 48 | **/ 49 | virtual void stop() = 0; 50 | 51 | //! Checks if the process should be canceled 52 | /** This method is called by some process from time to time to know if it 53 | should halt before its normal ending. This is a way for the client application 54 | to cancel an ongoing process (but it won't work with all algorithms). 55 | Process results may be incomplete/void. The cancel requirement mechanism must 56 | be implemented (typically a simple "cancel()" method that will be called by the 57 | client application). 58 | **/ 59 | virtual bool isCancelRequested() = 0; 60 | 61 | //! Returns whether the dialog title and info can be updated or not 62 | virtual bool textCanBeEdited() const { return true; } 63 | 64 | }; 65 | 66 | class StdMutex; 67 | 68 | //! Efficient management of progress based on a total number of steps different than 100 69 | /** DGM: can now be associated to a null 'callback' pointer to simplify the client code. 70 | **/ 71 | class CC_CORE_LIB_API NormalizedProgress 72 | { 73 | public: 74 | //! Default constructor 75 | /** \param callback associated GenericProgressCallback instance (can be null) 76 | \param totalSteps total number of steps (> 0) 77 | \param totalPercentage equivalent percentage (> 0) 78 | **/ 79 | NormalizedProgress(GenericProgressCallback* callback, unsigned totalSteps, unsigned totalPercentage = 100); 80 | 81 | //! Destructor 82 | virtual ~NormalizedProgress(); 83 | 84 | //! Scales inner parameters so that 'totalSteps' calls of the 'oneStep' method correspond to 'totalPercentage' percents 85 | void scale(unsigned totalSteps, unsigned totalPercentage = 100, bool updateCurrentProgress = false); 86 | 87 | //! Resets progress state 88 | void reset(); 89 | 90 | //! Increments total progress value of a single unit 91 | bool oneStep(); 92 | 93 | //! Increments total progress value of more than a single unit 94 | bool steps(unsigned n); 95 | 96 | protected: 97 | 98 | //! Total progress value (in percent) 99 | float m_percent; 100 | 101 | //! Number of necessary calls to 'oneStep' to actually call progress callback 102 | unsigned m_step; 103 | 104 | //! Percentage added to total progress value at each step 105 | float m_percentAdd; 106 | 107 | //! Current number of calls to 'oneStep' 108 | unsigned m_counter; 109 | 110 | //! Mutex to manage concurrent calls to oneStep() 111 | StdMutex* m_mutex; 112 | 113 | //! associated GenericProgressCallback 114 | GenericProgressCallback* progressCallback; 115 | }; 116 | } 117 | -------------------------------------------------------------------------------- /include/GenericTriangle.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #pragma once 5 | 6 | //Local 7 | #include "CCGeom.h" 8 | 9 | namespace CCCoreLib 10 | { 11 | //! A generic triangle interface 12 | /** Returns (temporary) references to each vertex. 13 | **/ 14 | class CC_CORE_LIB_API GenericTriangle 15 | { 16 | public: 17 | 18 | //! Default destructor 19 | virtual ~GenericTriangle() = default; 20 | 21 | //! Returns the first vertex (A) 22 | virtual const CCVector3* _getA() const = 0; 23 | 24 | //! Returns the second vertex (B) 25 | virtual const CCVector3* _getB() const = 0; 26 | 27 | //! Returns the third vertex (C) 28 | virtual const CCVector3* _getC() const = 0; 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /include/GridAndMeshIntersection.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #pragma once 5 | 6 | //Local 7 | #include "CCConst.h" 8 | #include "Grid3D.h" 9 | 10 | namespace CCCoreLib 11 | { 12 | class GenericIndexedMesh; 13 | class GenericProgressCallback; 14 | class SaitoSquaredDistanceTransform; 15 | struct TriangleList; 16 | 17 | //! Structure to compute the intersection between a mesh and a grid (to compute fast distances) 18 | class CC_CORE_LIB_API GridAndMeshIntersection 19 | { 20 | public: 21 | 22 | //! Default constructor 23 | GridAndMeshIntersection(); 24 | 25 | //! Destructor 26 | virtual ~GridAndMeshIntersection(); 27 | 28 | //! Clears the structure 29 | void clear(); 30 | 31 | //! Returns whether the structure is initialized or not 32 | inline bool isInitialized() const { return m_initialized; } 33 | 34 | //! Initializes the structure with a mesh 35 | bool computeMeshIntersection( GenericIndexedMesh* mesh, 36 | const CCVector3& minGridBB, 37 | const CCVector3& maxGridBB, 38 | PointCoordinateType cellSize, 39 | GenericProgressCallback* progressCb = nullptr); 40 | 41 | //! Initializes the structure with a mesh 42 | bool initDistanceTransformWithMesh( GenericIndexedMesh* mesh, 43 | const CCVector3& minGridBB, 44 | const CCVector3& maxGridBB, 45 | const CCVector3& minFilledBB, 46 | const CCVector3& maxFilledBB, 47 | PointCoordinateType cellSize, 48 | GenericProgressCallback* progressCb = nullptr); 49 | 50 | //! Returns the grid size 51 | const Tuple3ui& internalGridSize() const; 52 | 53 | //! Computes the (grid) cell position that contains a given point 54 | inline Tuple3i computeCellPos(const CCVector3& P) const 55 | { 56 | //DGM: if we admit that cellSize > 0, then the 'floor' operator is useless (int cast = truncation) 57 | assert(m_cellSize > 0); 58 | 59 | Tuple3i cellPos(static_cast(/*floor*/(P.x - m_minGridBB.x) / m_cellSize), 60 | static_cast(/*floor*/(P.y - m_minGridBB.y) / m_cellSize), 61 | static_cast(/*floor*/(P.z - m_minGridBB.z) / m_cellSize)); 62 | 63 | return cellPos; 64 | } 65 | 66 | //! Computes the center of a given (grid) cell 67 | /** \param cellPos the (grid) cell position 68 | \param center the computed center 69 | **/ 70 | inline void computeCellCenter(const Tuple3i& cellPos, CCVector3& center) const 71 | { 72 | center.x = m_minGridBB.x + (m_cellSize * (cellPos.x + static_cast(0.5))); 73 | center.y = m_minGridBB.y + (m_cellSize * (cellPos.y + static_cast(0.5))); 74 | center.z = m_minGridBB.z + (m_cellSize * (cellPos.z + static_cast(0.5))); 75 | } 76 | 77 | //! Returns the associated mesh (if any) 78 | inline const GenericIndexedMesh* mesh() const { return m_mesh; } 79 | 80 | //! Returns the distance transform (if any) 81 | inline const SaitoSquaredDistanceTransform* distanceTransform() const { return m_distanceTransform; } 82 | 83 | //! Returns the distance transform value (if any) 84 | unsigned distanceTransformValue(const Tuple3i& cellPos, bool isLocalCellPos) const; 85 | 86 | //! Returns the virtual grid cell size 87 | inline PointCoordinateType cellSize() const { return m_cellSize; } 88 | 89 | //! Converts a global cell position to a local one 90 | inline Tuple3i toLocal(const Tuple3i& cellPos) const { return cellPos - m_minFillIndexes; } 91 | 92 | //! Returns the list of triangles intersecting a given cell 93 | const TriangleList* trianglesInCell(const Tuple3i& cellPos, bool isLocalCellPos) const; 94 | 95 | //! Computes the distances between a given cell and the inner grid boundaries 96 | void computeSignedDistToBoundaries(const Tuple3i& cellPos, Tuple3i& distToLowerBorder, Tuple3i& distToUpperBorder) const; 97 | 98 | //! Returns whether a valid distance transform has been computed 99 | bool hasDistanceTransform() const; 100 | 101 | //! Returns whether a valid grid-mesh intersection has been computed 102 | bool hasGridMeshIntersection() const; 103 | 104 | protected: 105 | 106 | //! Mesh 107 | GenericIndexedMesh* m_mesh; 108 | //! Distance transform 109 | SaitoSquaredDistanceTransform* m_distanceTransform; 110 | 111 | //! Virtual grid bounding-box (min corner) 112 | CCVector3 m_minGridBB; 113 | //! Virtual grid bounding-box (max corner) 114 | CCVector3 m_maxGridBB; 115 | //! Virtual grid occupancy of the mesh (minimum indexes for each dimension) 116 | Tuple3i m_minFillIndexes; 117 | //! Virtual grid occupancy of the mesh (maximum indexes for each dimension) 118 | Tuple3i m_maxFillIndexes; 119 | //! Virtual grid cell size 120 | PointCoordinateType m_cellSize; 121 | 122 | //! Grid of TriangleList items 123 | /** \warning: may be smaller than the 'virtual grid' (see m_minFillIndexes and m_maxFillIndexes) **/ 124 | Grid3D m_perCellTriangleList; 125 | 126 | //! Whether the structure is properly initialized 127 | bool m_initialized; 128 | }; 129 | } 130 | -------------------------------------------------------------------------------- /include/Kriging.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #pragma once 5 | 6 | //CCCoreLib 7 | #include 8 | 9 | //STL 10 | #include 11 | 12 | //! 2D point with an associated scalar value 13 | struct DataPoint : CCVector2d 14 | { 15 | DataPoint() 16 | : CCVector2d() 17 | , value{ 0.0 } 18 | {} 19 | 20 | DataPoint(double x, double y, double _value) 21 | : CCVector2d(x, y) 22 | , value{ _value } 23 | { 24 | } 25 | 26 | double value; 27 | }; 28 | 29 | //! Raster descriptor 30 | class RasterParameters 31 | { 32 | public: 33 | 34 | RasterParameters() 35 | : minCorner(0.0, 0.0) 36 | , step(0.0) 37 | , width(0) 38 | , height(0) 39 | { 40 | } 41 | 42 | RasterParameters( const CCVector2d& _minCorner, 43 | double _step, 44 | unsigned _width, 45 | unsigned _height ) 46 | : minCorner(_minCorner) 47 | , step(_step) 48 | , width(_width) 49 | , height(_height) 50 | { 51 | } 52 | 53 | inline CCVector2d toPoint(unsigned x, unsigned y) const 54 | { 55 | return minCorner + CCVector2d(x * step, y * step); 56 | } 57 | 58 | CCVector2d minCorner; 59 | double step; 60 | unsigned width; 61 | unsigned height; 62 | }; 63 | 64 | class CholeskyDecomposition; 65 | struct OrdinaryKrigeContext; 66 | struct SimpleKrigeContext; 67 | 68 | //! Simple and Ordinary Kriging 69 | /** Inspired by https://github.com/ByteShark/Kriging/ 70 | **/ 71 | class CC_CORE_LIB_API Kriging 72 | { 73 | public: 74 | 75 | // Interpolation model 76 | enum Model 77 | { 78 | Spherical = 0, 79 | Exponential, 80 | Gaussian, 81 | Invalid 82 | }; 83 | 84 | //! Vector type 85 | using Vector = std::vector; 86 | //! Matrix type 87 | using Matrix = std::vector; 88 | 89 | //! Constructor 90 | Kriging(const std::vector& dataPoints, 91 | const RasterParameters& rasterParams); 92 | 93 | //! Parameters 94 | struct KrigeParams 95 | { 96 | KrigeParams(Model _model = Invalid, 97 | double _nugget = 0.0, 98 | double _sill = 0.0, 99 | double _range = 1.0) 100 | : model(_model) 101 | , nugget(_nugget) 102 | , sill(_sill) 103 | , range(_range) 104 | { 105 | } 106 | 107 | Model model; 108 | double nugget; 109 | double sill; 110 | double range; 111 | }; 112 | 113 | //! Computes default parameters 114 | KrigeParams computeDefaultParameters() const; 115 | 116 | // Ordinary Kriging (all cells at once, returns a grid) 117 | bool ordinaryKrige( const KrigeParams& params, 118 | unsigned knn, 119 | std::vector& output); 120 | 121 | // Ordinary Kriging (cell by cell, with a 'context' object) 122 | double ordinaryKrigeSingleCell( const KrigeParams& params, 123 | unsigned row, 124 | unsigned col, 125 | OrdinaryKrigeContext* context, 126 | bool alreadyHaveCandidates = false); 127 | 128 | // context management 129 | OrdinaryKrigeContext* createOrdinaryKrigeContext(int knn); 130 | void releaseOrdinaryKrigeContext(OrdinaryKrigeContext*& context); 131 | 132 | protected: 133 | 134 | //! Fills a matrix of covariograms over all point distances 135 | Matrix calculateCovariogramMatrix(const std::vector& dataPointCandidates, const KrigeParams& params, bool lagrangeMultiplier) const; 136 | 137 | //! Fills a vector of covariograms over all distances from a given point 138 | Vector calculateCovariogramVector(const std::vector& dataPointCandidates, const CCVector2d& point, const KrigeParams& params, bool lagrangeMultiplier) const; 139 | 140 | //! Association of a point index and a (squared) distance 141 | struct SquareDistanceAndIndex 142 | { 143 | double sqDistance; 144 | size_t index; 145 | }; 146 | 147 | //! Estimates the lag parameters 148 | void calculateDefaultLagParameters(double& lag, double& lagTolerance) const; 149 | 150 | //! Estimates the kriging parameters 151 | void calculateEstimatedParameters(); 152 | 153 | //! Calculates the covariogram 154 | double calculateCovariogram(const KrigeParams& params, double distance) const; 155 | 156 | //! Simple linear regression 157 | /** \return slope and intercept 158 | **/ 159 | std::pair linearRegression(const Vector& X, const Vector& Y) const; 160 | 161 | //! Ordinary Kringing for an individual point 162 | double ordinaryKrigeForPoint(const CCVector2d& point, const KrigeParams& params, 163 | const std::vector& dataPointCandidates); 164 | 165 | protected: // members 166 | 167 | const std::vector& m_dataPoints; 168 | 169 | RasterParameters m_rasterParams; 170 | }; 171 | -------------------------------------------------------------------------------- /include/LocalModel.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #pragma once 5 | 6 | #include "Neighbourhood.h" 7 | 8 | namespace CCCoreLib 9 | { 10 | //! Local modelization (generic interface) 11 | /** Local surface approximation of a point cloud 12 | **/ 13 | class LocalModel 14 | { 15 | public: 16 | 17 | //! Factory 18 | /** \param type the model type 19 | \param subset (small) set of points from which to compute the local model 20 | \param center model "center" 21 | \param squaredRadius model max radius (squared) 22 | **/ 23 | static LocalModel* New( LOCAL_MODEL_TYPES type, 24 | Neighbourhood& subset, 25 | const CCVector3 ¢er, 26 | PointCoordinateType squaredRadius); 27 | 28 | //! Destructor 29 | virtual ~LocalModel() = default; 30 | 31 | //! Returns the model type 32 | virtual LOCAL_MODEL_TYPES getType() const = 0; 33 | 34 | //! Returns the model center 35 | inline const CCVector3& getCenter() const { return m_modelCenter; } 36 | 37 | //! Returns the model max radius (squared) 38 | inline PointCoordinateType getSquareSize() const { return m_squaredRadius; } 39 | 40 | //! Compute the (unsigned) distance between a 3D point and this model 41 | /** \param[in] P the query point 42 | \param[out] nearestPoint returns the nearest point (optional) 43 | \return the (unsigned) distance (or CCCoreLib::NAN_VALUE if the computation failed) 44 | **/ 45 | virtual ScalarType computeDistanceFromModelToPoint(const CCVector3* P, CCVector3* nearestPoint = nullptr) const = 0; 46 | 47 | protected: 48 | 49 | //! Constructor 50 | /** \param center model "center" 51 | \param squaredRadius model max "radius" (squared) 52 | **/ 53 | LocalModel(const CCVector3 ¢er, PointCoordinateType squaredRadius); 54 | 55 | //! Center 56 | CCVector3 m_modelCenter; 57 | 58 | //! Max radius (squared) 59 | PointCoordinateType m_squaredRadius; 60 | }; 61 | } 62 | -------------------------------------------------------------------------------- /include/ManualSegmentationTools.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #pragma once 5 | 6 | //Local 7 | #include "Neighbourhood.h" 8 | 9 | namespace CCCoreLib 10 | { 11 | class GenericIndexedCloud; 12 | class GenericIndexedCloudPersist; 13 | class GenericIndexedMesh; 14 | class GenericProgressCallback; 15 | class ReferenceCloud; 16 | class SimpleMesh; 17 | class Polyline; 18 | 19 | //! Manual segmentation algorithms (inside/outside a polyline, etc.) 20 | class CC_CORE_LIB_API ManualSegmentationTools : public CCToolbox 21 | { 22 | public: 23 | 24 | //! Extracts the points that fall inside/outside of a 2D polyline once projected on the screen 25 | /** The camera parameters of the screen must be transmitted to this method, 26 | as well as the polyline (generally drawn on the screen by a user) 27 | expressed in the screen coordinates. 28 | \param aCloud the cloud to segment 29 | \param poly the polyline 30 | \param keepInside if true (resp. false), the points falling inside (resp. outside) the polyline will be extracted 31 | \param viewMat the optional 4x4 visualization matrix (OpenGL style) 32 | \return a cloud structure containing references to the extracted points (references to - no duplication) 33 | **/ 34 | static ReferenceCloud* segment(GenericIndexedCloudPersist* aCloud, const Polyline* poly, bool keepInside, const float* viewMat = nullptr); 35 | 36 | //! Selects the points which associated scalar value fall inside or outside a specified interval 37 | /** \warning: be sure to activate an OUTPUT scalar field on the input cloud 38 | \param cloud the cloud to segment (note that if the cloud is a ReferenceCloud, the output ReferenceCloud will refer to its source cloud instead of the input cloud) 39 | \param minDist the lower boundary 40 | \param maxDist the upper boundary 41 | \param outside whether to select the points inside or outside 42 | \return a new cloud structure containing the extracted points (references to - no duplication) 43 | **/ 44 | static ReferenceCloud* segment(GenericIndexedCloudPersist* cloud, ScalarType minDist, ScalarType maxDist, bool outside = false); 45 | 46 | //! Tests if a point is inside a polygon (2D) 47 | /** \param P a 2D point 48 | \param polyVertices polygon vertices (considered as ordered 2D poyline vertices) 49 | \return true if P is inside poly 50 | **/ 51 | static bool isPointInsidePoly(const CCVector2& P, const GenericIndexedCloud* polyVertices); 52 | 53 | //! Tests if a point is inside a polygon (2D) 54 | /** \param P a 2D point 55 | \param polyVertices polygon vertices (considered as ordered 2D poyline vertices) 56 | \return true if P is inside poly 57 | **/ 58 | static bool isPointInsidePoly(const CCVector2& P, const std::vector& polyVertices); 59 | 60 | //! Segments a mesh knowing which vertices should be kept or not 61 | /** This method takes as input a set of vertex indexes and creates a new mesh 62 | composed either of: 63 | - the triangles that have exactly those points as vertices (useSelectedVertices = true) 64 | - or all the triangles for which no vertex is part of this subset (useSelectedVertices = false). 65 | 66 | \warning No re-triangulation on the border will occur. 67 | 68 | \param mesh a mesh 69 | \param selectedVertexIndexes the indexes of selected vertices 70 | \param useSelectedVertices specifies if the points corresponding to the input indexes should be the new mesh vertices, or the opposite 71 | \param progressCb the client application can get some notification of the process progress through this callback mechanism (see GenericProgressCallback) 72 | \param destCloud optionally, a cloud object can be specified to be associated to the new created mesh object, instead of the cloud associated to the ReferenceCloud "pointsIndexes" 73 | \param indexShift optionally, a shift can be added to all vertex indexes of the new mesh 74 | \param triangleIndexMap optionally, a vector can be filled to record the new indexes of the triangles in the destination mesh (or -1 if they are discarded) 75 | \return a new mesh structure, or 0 if something went wrong 76 | **/ 77 | static GenericIndexedMesh* segmentMesh( GenericIndexedMesh* mesh, 78 | ReferenceCloud* selectedVertexIndexes, 79 | bool useSelectedVertices, 80 | GenericProgressCallback* progressCb = nullptr, 81 | GenericIndexedCloud* destCloud = nullptr, 82 | unsigned indexShift = 0, 83 | std::vector* triangleIndexMap = nullptr); 84 | 85 | //! Input/output parameters for the segmentMeshWitAAPlane method 86 | struct MeshCutterParams 87 | { 88 | SimpleMesh* insideMesh; 89 | SimpleMesh* outsideMesh; 90 | bool generateOutsideMesh; 91 | double epsilon; 92 | //for infinite plane intersection 93 | unsigned char planeOrthoDim; 94 | double planeCoord; 95 | //for box intersection 96 | CCVector3d bbMin, bbMax; 97 | //for the reprojection of triangle features 98 | bool trackOrigIndexes; 99 | std::vector origTriIndexesMapInside; 100 | std::vector origTriIndexesMapOutside; 101 | 102 | MeshCutterParams() 103 | : insideMesh(nullptr) 104 | , outsideMesh(nullptr) 105 | , generateOutsideMesh(false) 106 | , epsilon(ZERO_TOLERANCE_D) 107 | , planeOrthoDim(0) 108 | , planeCoord(0) 109 | , bbMin(0, 0, 0) 110 | , bbMax(0, 0, 0) 111 | , trackOrigIndexes(false) 112 | {} 113 | }; 114 | 115 | static bool segmentMeshWithAAPlane(GenericIndexedMesh* mesh, 116 | GenericIndexedCloudPersist* vertices, 117 | MeshCutterParams& ioParams, 118 | GenericProgressCallback* progressCb = nullptr); 119 | 120 | static bool segmentMeshWithAABox(GenericIndexedMesh* mesh, 121 | GenericIndexedCloudPersist* vertices, 122 | MeshCutterParams& ioParams, 123 | GenericProgressCallback* progressCb = nullptr); 124 | }; 125 | } 126 | -------------------------------------------------------------------------------- /include/MathTools.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #pragma once 5 | 6 | //Local 7 | #include "CCTypes.h" 8 | 9 | namespace CCCoreLib 10 | { 11 | //! Empty class - for classification purpose only 12 | class MathTools {}; 13 | } 14 | -------------------------------------------------------------------------------- /include/MeshSamplingTools.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #pragma once 5 | 6 | //Local 7 | #include "CCToolbox.h" 8 | 9 | //system 10 | #include 11 | #include 12 | 13 | namespace CCCoreLib 14 | { 15 | class GenericProgressCallback; 16 | class GenericMesh; 17 | class GenericIndexedMesh; 18 | class PointCloud; 19 | class ScalarField; 20 | 21 | //! Mesh sampling algorithms 22 | class CC_CORE_LIB_API MeshSamplingTools : public CCToolbox 23 | { 24 | public: 25 | 26 | //! Computes the mesh area 27 | /** \param mesh triangular mesh 28 | \return mesh area 29 | **/ 30 | static double computeMeshArea(GenericMesh* mesh); 31 | 32 | //! Computes the mesh volume 33 | /** \warning Make sure the input mesh is closed! 34 | See MeshSamplingTools::computeMeshEdgesConnectivity. 35 | \param mesh triangular mesh (closed!) 36 | \return mesh volume 37 | **/ 38 | static double computeMeshVolume(GenericMesh* mesh); 39 | 40 | //! Statistics on the edges connectivty of a mesh 41 | struct EdgeConnectivityStats 42 | { 43 | EdgeConnectivityStats() 44 | : edgesCount(0) 45 | , edgesNotShared(0) 46 | , edgesSharedByTwo(0) 47 | , edgesSharedByMore(0) 48 | {} 49 | 50 | //! Total number of edges 51 | unsigned edgesCount; 52 | //! Edges not shared (i.e. used by only one triangle) 53 | unsigned edgesNotShared; 54 | //! Edges shared by exactly two triangles 55 | unsigned edgesSharedByTwo; 56 | //! Edges shared by more than two triangles 57 | unsigned edgesSharedByMore; 58 | }; 59 | 60 | //! Computes some statistics on the edges connectivty of a mesh 61 | /** This methods counts the number of edges shared by 1, 2 or more faces. 62 | One or more edges used only by 1 face each indicates the presence of 63 | at least one hole. Edges used by more than two faces are non-manifold. 64 | \param[in] mesh triangular mesh 65 | \param[out] stats output statistics 66 | \return false if an error occurred (invalid input or not enough memory) 67 | **/ 68 | static bool computeMeshEdgesConnectivity(GenericIndexedMesh* mesh, EdgeConnectivityStats& stats); 69 | 70 | //! Flags used by the MeshSamplingTools::flagMeshVerticesByType method. 71 | enum VertexFlags 72 | { 73 | VERTEX_NORMAL = 0, /**< Normal vertex **/ 74 | VERTEX_BORDER = 1, /**< Vertex on a border/hole **/ 75 | VERTEX_NON_MANIFOLD = 2 /**< Vertex on a non-manifold edge **/ 76 | }; 77 | 78 | //! Flags the vertices of a mesh depending on their type 79 | /** See MeshSamplingTools::VertexFlags. 80 | \param[in] mesh triangular mesh 81 | \param[in] flags already allocated scalar field to store the per-vertex flags 82 | \param[out] stats output statistics (optional) 83 | \return false if an error occurred (invalid input or not enough memory) 84 | **/ 85 | static bool flagMeshVerticesByType(GenericIndexedMesh* mesh, ScalarField* flags, EdgeConnectivityStats* stats = nullptr); 86 | 87 | //! Samples points on a mesh 88 | /** The points are sampled on each triangle randomly, by generating 89 | two numbers between 0 and 1 (a and b). If a+b > 1, then a = 1-a and 90 | b = 1-b. Let ABC be the triangle, then the new point P will be as 91 | AP = a.AB+b.AC (AP,AB and AC are vectors here). The number of 92 | points sampled on each triangle depends on the triangle's area. 93 | Let s be this area, and µ the sampling density, then N = s*µ is 94 | the theoretic (floating) number of points to sample. The floating 95 | part of N (let's call it Nf, and let Ni be the integer part) is 96 | handled by generating another random number between 0 and 1. 97 | If this number is less than Nf, then Ni = Ni+1. The number of points 98 | sampled on the triangle will simply be Ni. 99 | \param mesh the mesh to be sampled 100 | \param samplingDensity the sampling surface density 101 | \param progressCb the client application can get some notification of the process progress through this callback mechanism (see GenericProgressCallback) 102 | \param[out] triIndices triangle index for each samples point (output only - optional) 103 | \return the sampled points 104 | **/ 105 | static PointCloud* samplePointsOnMesh( GenericMesh* mesh, 106 | double samplingDensity, 107 | GenericProgressCallback* progressCb = nullptr, 108 | std::vector* triIndices = nullptr); 109 | 110 | //! Samples points on a mesh 111 | /** See the other version of this method. Instead of specifying a 112 | density, it is possible here to specify the total number of 113 | points to sample (approximative). 114 | \param mesh the mesh to be sampled 115 | \param numberOfPoints the desired number of points on the whole mesh 116 | \param progressCb the client application can get some notification of the process progress through this callback mechanism (see GenericProgressCallback) 117 | \param[out] triIndices triangle index for each samples point (output only - optional) 118 | \return the sampled points 119 | **/ 120 | static PointCloud* samplePointsOnMesh( GenericMesh* mesh, 121 | unsigned numberOfPoints, 122 | GenericProgressCallback* progressCb = nullptr, 123 | std::vector* triIndices = nullptr); 124 | 125 | protected: 126 | 127 | //! Samples points on a mesh - internal method 128 | /** See public methods descriptions 129 | \param mesh the mesh to be sampled 130 | \param samplingDensity the sampling surfacical density 131 | \param theoreticNumberOfPoints the approximated number of points that will be sampled 132 | \param progressCb the client application can get some notification of the process progress through this callback mechanism (see GenericProgressCallback) 133 | \param[out] triIndices triangle index for each samples point (output only - optional) 134 | \return the sampled points 135 | **/ 136 | static PointCloud* samplePointsOnMesh( GenericMesh* mesh, 137 | double samplingDensity, 138 | unsigned theoreticNumberOfPoints, 139 | GenericProgressCallback* progressCb = nullptr, 140 | std::vector* triIndices = nullptr); 141 | 142 | //! Map used to count the number of triangles using each edge 143 | /** Edges are represented by two 32 bits indexes merged as a 64 integer 144 | **/ 145 | using EdgeUsageMap = std::map; 146 | 147 | //! Computes the unique key corresponding to an edge 148 | static unsigned long long ComputeEdgeKey(unsigned i1, unsigned i2); 149 | //! Computes the edge vertex indexes from its unique key 150 | static void DecodeEdgeKey(unsigned long long key, unsigned& i1, unsigned& i2); 151 | 152 | //! Creates a map to count the number of triangles using each edge 153 | static bool buildMeshEdgeUsageMap(GenericIndexedMesh* mesh, EdgeUsageMap& edgeMap); 154 | }; 155 | } 156 | -------------------------------------------------------------------------------- /include/NormalDistribution.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #pragma once 5 | 6 | //Local 7 | #include "GenericDistribution.h" 8 | 9 | namespace CCCoreLib 10 | { 11 | //! The Normal/Gaussian statistical distribution 12 | /** Implements the GenericDistribution interface. 13 | **/ 14 | class CC_CORE_LIB_API NormalDistribution : public GenericDistribution 15 | { 16 | public: 17 | 18 | //! NormalDistribution constructor 19 | NormalDistribution(); 20 | 21 | //! NormalDistribution constructor 22 | /** Distrubtion parameters can be directly set during object 23 | construction. 24 | \param _mu the normal distribution mean 25 | \param _sigma2 the normal distribution variance 26 | **/ 27 | NormalDistribution(ScalarType _mu, ScalarType _sigma2); 28 | 29 | //inherited methods (see GenericDistribution) 30 | bool computeParameters(const ScalarContainer& values) override; 31 | double computeP(ScalarType x) const override; 32 | double computePfromZero(ScalarType x) const override; 33 | double computeP(ScalarType x1, ScalarType x2) const override; 34 | double computeChi2Dist(const GenericCloud* Yk, unsigned numberOfClasses, int* histo = nullptr) override; 35 | const char* getName() const override { return "Gauss"; } 36 | 37 | //! Returns the distribution parameters 38 | /** \param _mu a field to transmit the distribution mean 39 | \param _sigma2 a field to transmit the distribution variance 40 | return the parameters validity 41 | **/ 42 | bool getParameters(ScalarType &_mu, ScalarType &_sigma2) const; 43 | 44 | //! Sets the distribution parameters 45 | /** \param _mu the distribution mean 46 | \param _sigma2 the distribution variance 47 | return the parameters validity 48 | **/ 49 | bool setParameters(ScalarType _mu, ScalarType _sigma2); 50 | 51 | //! Returns the distribution mean 52 | inline ScalarType getMu() const { return m_mu; } 53 | 54 | //! Returns the distribution variance 55 | inline ScalarType getSigma2() const { return m_sigma2; } 56 | 57 | //! Computes the distribution parameters from a point cloud (with scalar values) 58 | bool computeParameters(const GenericCloud* cloud); 59 | 60 | //! Computes robust parameters for the distribution from an array of scalar values 61 | /** Specific method to compute the parameters directly from an array 62 | (vector) of scalar values, without associated points. After a first pass, 63 | only the values close enough to the mean (in terms of nSigma times the initial 64 | variance) are kept to make a second and more robust evaluation of the parameters. 65 | \param values the scalar values 66 | \param nSigma the values filtering interval size ([mu -nSigma * stddev : mu + nSigma * stddev]) 67 | \return the validity of the computed parameters 68 | **/ 69 | bool computeRobustParameters(const ScalarContainer& values, double nSigma); 70 | 71 | protected: 72 | 73 | //! Compute each Chi2 class limits 74 | /** This method is used (internally) to accelerate the Chi2 distance computation. 75 | \param numberOfClasses the number of classes that will be used for Chi2 distance computation 76 | \return success 77 | **/ 78 | virtual bool setChi2ClassesPositions(unsigned numberOfClasses); 79 | 80 | //! Mean 81 | ScalarType m_mu; 82 | //! Variance 83 | ScalarType m_sigma2; 84 | //! Exponential quotient 85 | double m_qFactor; 86 | //! Normalization factor 87 | double m_normFactor; 88 | 89 | //! Chi2 classes limits 90 | /** Used internally. Stores both limits for each class in a vector 91 | (min_class_1, max_class_1, min_class_2, max_class_2, etc.). 92 | **/ 93 | std::vector m_chi2ClassesPositions; 94 | 95 | //! Structure used during the Chi2 distance computation 96 | std::vector m_Pi; 97 | }; 98 | } 99 | -------------------------------------------------------------------------------- /include/ParallelSort.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © CloudCompare Project 3 | 4 | #pragma once 5 | 6 | #ifdef ParallelSort 7 | #undef ParallelSort 8 | #pragma message "Replacing preprocessor symbol 'ParallelSort' with the one defined in Parallel.h" 9 | #endif 10 | 11 | #if defined(_MSC_VER) && (_MSC_VER >= 1800) 12 | 13 | //Parallel Patterns Library (for parallel sort) 14 | #include 15 | 16 | #define ParallelSort Concurrency::parallel_sort 17 | 18 | #elif CC_CORE_LIB_USES_TBB 19 | 20 | #ifndef Q_MOC_RUN 21 | #if defined(emit) 22 | #undef emit 23 | #include 24 | #define emit // restore the macro definition of "emit", as it was defined in gtmetamacros.h 25 | #else 26 | #include 27 | #endif // defined(emit) 28 | #endif // Q_MOC_RUN 29 | 30 | #define ParallelSort tbb::parallel_sort 31 | 32 | #else 33 | 34 | #include 35 | 36 | #define ParallelSort std::sort 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /include/PointCloud.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #pragma once 5 | 6 | //Local 7 | #include "GenericIndexedCloudPersist.h" 8 | #include "PointCloudTpl.h" 9 | 10 | namespace CCCoreLib 11 | { 12 | //! A storage-efficient point cloud structure that can also handle an unlimited number of scalar fields 13 | class CC_CORE_LIB_API PointCloud : public PointCloudTpl 14 | { 15 | public: 16 | //! Default constructor 17 | PointCloud() = default; 18 | 19 | //! Default destructor 20 | ~PointCloud() override = default; 21 | 22 | //! Reserves memory to store the normals 23 | bool reserveNormals(unsigned newCount) 24 | { 25 | if (m_normals.capacity() < newCount) 26 | { 27 | try 28 | { 29 | m_normals.reserve(newCount); 30 | } 31 | catch (const std::bad_alloc&) 32 | { 33 | return false; 34 | } 35 | } 36 | 37 | return true; 38 | } 39 | 40 | //inherited from PointCloudTpl 41 | bool resize(unsigned newNumberOfPoints) override 42 | { 43 | if (!PointCloudTpl::resize(newNumberOfPoints)) 44 | { 45 | return false; 46 | } 47 | 48 | // resize the normals as well 49 | if (m_normals.capacity() != 0) 50 | { 51 | try 52 | { 53 | m_normals.resize(newNumberOfPoints); 54 | } 55 | catch (const std::bad_alloc&) 56 | { 57 | return false; 58 | } 59 | } 60 | 61 | return true; 62 | } 63 | 64 | //! Adds a normal 65 | /** \param N a 3D normal 66 | **/ 67 | inline void addNormal(const CCVector3 &N) 68 | { 69 | assert(m_normals.size() < m_normals.capacity()); 70 | m_normals.push_back(N); 71 | } 72 | 73 | //! Returns the set of normals 74 | std::vector& normals() { return m_normals; } 75 | 76 | //! Returns the set of normals (const version) 77 | const std::vector& normals() const { return m_normals; } 78 | 79 | //inherited from CCCoreLib::GenericIndexedCloud 80 | bool normalsAvailable() const override { return !m_normals.empty() && m_normals.size() >= size(); } 81 | const CCVector3* getNormal(unsigned pointIndex) const override { return &m_normals[pointIndex]; } 82 | 83 | protected: 84 | 85 | //! Point normals (if any) 86 | std::vector m_normals; 87 | }; 88 | } 89 | -------------------------------------------------------------------------------- /include/Polyline.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #pragma once 5 | 6 | //Local 7 | #include "ReferenceCloud.h" 8 | 9 | namespace CCCoreLib 10 | { 11 | //! A simple polyline class 12 | /** The polyline is considered as a cloud of points 13 | (in a specific order) with a open/closed state 14 | information. 15 | **/ 16 | class CC_CORE_LIB_API Polyline : public ReferenceCloud 17 | { 18 | public: 19 | 20 | //! Polyline constructor 21 | explicit Polyline(GenericIndexedCloudPersist* associatedCloud); 22 | 23 | //! Returns whether the polyline is closed or not 24 | inline bool isClosed() const { return m_isClosed; } 25 | 26 | //! Sets whether the polyline is closed or not 27 | inline virtual void setClosed(bool state) { m_isClosed = state; } 28 | 29 | //inherited from ReferenceCloud 30 | void clear(bool unusedParam = true) override; 31 | 32 | protected: 33 | 34 | //! Closing state 35 | bool m_isClosed; 36 | }; 37 | } 38 | -------------------------------------------------------------------------------- /include/RayAndBox.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © CloudCompare Project 3 | 4 | #pragma once 5 | 6 | //Local 7 | #include "CCGeom.h" 8 | 9 | //System 10 | #include 11 | 12 | namespace CCCoreLib 13 | { 14 | //! Simple Ray structure 15 | template struct Ray 16 | { 17 | Ray(const Vector3Tpl& rayAxis, const Vector3Tpl& rayOrigin) 18 | : dir(rayAxis) 19 | , origin(rayOrigin) 20 | , invDir(0,0,0) 21 | , sign(0,0,0) 22 | { 23 | dir.normalize(); 24 | invDir = Vector3Tpl(1/rayAxis.x, 1/rayAxis.y, 1/rayAxis.z); // +/-infinity is acceptable here because we are mainly interested in the sign 25 | sign = Tuple3i(invDir.x < 0, invDir.y < 0, invDir.z < 0); 26 | } 27 | 28 | double radialSquareDistance(const Vector3Tpl& P) const 29 | { 30 | Vector3Tpl OP = P - origin; 31 | return OP.cross(dir).norm2d(); 32 | } 33 | 34 | double squareDistanceToOrigin(const Vector3Tpl& P) const 35 | { 36 | Vector3Tpl OP = P - origin; 37 | return OP.norm2d(); 38 | } 39 | 40 | void squareDistances(const Vector3Tpl& P, double& radial, double& toOrigin) const 41 | { 42 | Vector3Tpl OP = P - origin; 43 | radial = OP.cross(dir).norm2d(); 44 | toOrigin = OP.norm2d(); 45 | } 46 | 47 | Vector3Tpl dir, origin; 48 | Vector3Tpl invDir; 49 | Tuple3i sign; 50 | }; 51 | 52 | //! Simple axis aligned box structure 53 | template struct AABB 54 | { 55 | AABB(const Vector3Tpl& minCorner, const Vector3Tpl& maxCorner) 56 | { 57 | assert(minCorner.x <= maxCorner.x); 58 | assert(minCorner.y <= maxCorner.y); 59 | assert(minCorner.z <= maxCorner.z); 60 | 61 | corners[0] = minCorner; 62 | corners[1] = maxCorner; 63 | } 64 | 65 | /* 66 | * Ray-box intersection using IEEE numerical properties to ensure that the 67 | * test is both robust and efficient, as described in: 68 | * 69 | * Amy Williams, Steve Barrus, R. Keith Morley, and Peter Shirley 70 | * "An Efficient and Robust Ray-Box Intersection Algorithm" 71 | * Journal of graphics tools, 10(1):49-54, 2005 72 | */ 73 | bool intersects(const Ray &r, T* t0 = 0, T* t1 = 0) const 74 | { 75 | T tmin = (corners[ r.sign.x].x - r.origin.x) * r.invDir.x; 76 | T tmax = (corners[1-r.sign.x].x - r.origin.x) * r.invDir.x; 77 | T tymin = (corners[ r.sign.y].y - r.origin.y) * r.invDir.y; 78 | T tymax = (corners[1-r.sign.y].y - r.origin.y) * r.invDir.y; 79 | 80 | if (tmin > tymax || tymin > tmax) 81 | return false; 82 | if (tymin > tmin) 83 | tmin = tymin; 84 | if (tymax < tmax) 85 | tmax = tymax; 86 | 87 | T tzmin = (corners[ r.sign.z].z - r.origin.z) * r.invDir.z; 88 | T tzmax = (corners[1-r.sign.z].z - r.origin.z) * r.invDir.z; 89 | 90 | if (tmin > tzmax || tzmin > tmax) 91 | return false; 92 | if (tzmin > tmin) 93 | tmin = tzmin; 94 | if (tzmax < tmax) 95 | tmax = tzmax; 96 | 97 | if (t0) 98 | *t0 = tmin; 99 | if (t1) 100 | *t1 = tmax; 101 | 102 | return true; 103 | } 104 | 105 | Vector3Tpl corners[2]; 106 | }; 107 | } 108 | -------------------------------------------------------------------------------- /include/SaitoSquaredDistanceTransform.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © CloudCompare Project 3 | 4 | #pragma once 5 | 6 | // Inspired from bil_edt.cxx (VXL) by Ricardo Fabbri (rfabbri), Brown University (rfabbri@lems.brown.edu) 7 | 8 | //Local 9 | #include "Grid3D.h" 10 | #include "MathTools.h" 11 | 12 | namespace CCCoreLib 13 | { 14 | class GenericProgressCallback; 15 | class NormalizedProgress; 16 | class GenericIndexedMesh; 17 | class GenericCloud; 18 | 19 | //! Class to compute a Squared Distance Field with the Saito algorithm on a 3D grid 20 | class CC_CORE_LIB_API SaitoSquaredDistanceTransform : public Grid3D, public MathTools 21 | { 22 | public: 23 | 24 | //! Default constructor 25 | SaitoSquaredDistanceTransform() = default; 26 | 27 | //! Initializes the grid 28 | /** The memory for the grid must be explicitelty reserved prior to any action. 29 | \return true if the initialization succeeded 30 | **/ 31 | inline bool initGrid(const Tuple3ui& gridSize) 32 | { 33 | return Grid3D::init(gridSize.x, gridSize.y, gridSize.z, 0, 0); //margin = 0 (we need continuous memory) 34 | } 35 | 36 | //! Initializes the distance transform with a mesh 37 | inline bool initDT( GenericIndexedMesh* mesh, 38 | PointCoordinateType cellLength, 39 | const CCVector3& gridMinCorner, 40 | GenericProgressCallback* progressCb = nullptr) 41 | { 42 | return intersectWith(mesh, cellLength, gridMinCorner, 1, progressCb); 43 | } 44 | 45 | //! Initializes the distance transform with a cloud 46 | inline bool initDT( GenericCloud* cloud, 47 | PointCoordinateType cellLength, 48 | const CCVector3& gridMinCorner, 49 | GenericProgressCallback* progressCb = nullptr) 50 | { 51 | return intersectWith(cloud, cellLength, gridMinCorner, 1, progressCb); 52 | } 53 | 54 | //! Computes the exact Squared Distance Transform on the whole grid 55 | /** Propagates the distances on the whole grid. 56 | 57 | Base on the implementation by R. Fabbri (which is itslef based on 58 | two independent implementations by O. Cuisenaire and J. C. Torelli). 59 | 60 | PAPER 61 | T. Saito and J.I. Toriwaki, "New algorithms for Euclidean distance 62 | transformations of an n-dimensional digitised picture with applications", 63 | Pattern Recognition, 27(11), pp. 1551-1565, 1994 64 | 65 | \warning Output distances are squared 66 | 67 | \param progressCb progress callback (optional) 68 | \return success 69 | **/ 70 | inline bool propagateDistance(GenericProgressCallback* progressCb = nullptr) { return SDT_3D(*this, progressCb); } 71 | 72 | protected: 73 | 74 | //! 1D Euclidean Distance Transform 75 | static bool EDT_1D(GridElement* slice, std::size_t r, std::size_t c); 76 | //! 2D Exact Squared Distance Transform 77 | static bool SDT_2D(Grid3D& image, std::size_t sliceIndex, const std::vector& sq); 78 | //! 3D Exact Squared Distance Transform 79 | static bool SDT_3D(Grid3D& image, GenericProgressCallback* progressCb = nullptr); 80 | }; 81 | } 82 | -------------------------------------------------------------------------------- /include/SimpleMesh.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #pragma once 5 | 6 | //Local 7 | #include "BoundingBox.h" 8 | #include "GenericIndexedMesh.h" 9 | #include "SimpleTriangle.h" 10 | 11 | //System 12 | #include 13 | 14 | namespace CCCoreLib 15 | { 16 | class GenericIndexedCloud; 17 | 18 | //! A simple mesh structure, with index-based vertex access 19 | /** Implements the GenericIndexedMesh interface. This mesh is always associated 20 | to a (index based) point cloud that stores the mesh vertexes. 21 | **/ 22 | class CC_CORE_LIB_API SimpleMesh : public GenericIndexedMesh 23 | { 24 | public: //constructors 25 | 26 | //! SimpleMesh Constructor 27 | /** \param _theVertices the point cloud containing the vertices 28 | \param linkVerticesWithMesh specifies if the vertex cloud should be deleted when the SimpleMesh object is destructed 29 | **/ 30 | SimpleMesh(GenericIndexedCloud* _theVertices, bool linkVerticesWithMesh = false); 31 | 32 | //! SimpleMesh destructor 33 | ~SimpleMesh() override; 34 | 35 | public: //inherited methods 36 | 37 | void forEach(genericTriangleAction action) override; 38 | void placeIteratorAtBeginning() override; 39 | GenericTriangle* _getNextTriangle() override; //temporary 40 | GenericTriangle* _getTriangle(unsigned triangleIndex) override; //temporary 41 | VerticesIndexes* getNextTriangleVertIndexes() override; 42 | VerticesIndexes* getTriangleVertIndexes(unsigned triangleIndex) override; 43 | unsigned size() const override { return static_cast(triIndexes.size()); } 44 | void getBoundingBox(CCVector3& bbMin, CCVector3& bbMax) override; 45 | void getTriangleVertices(unsigned triangleIndex, CCVector3& A, CCVector3& B, CCVector3& C) const override; 46 | 47 | public: //specific methods 48 | 49 | //! Returns the mesh capacity 50 | inline unsigned capacity() const { return static_cast(triIndexes.capacity()); } 51 | 52 | //! Returns the vertices 53 | inline const GenericIndexedCloud* vertices() const { return theVertices; } 54 | 55 | //! Clears the mesh 56 | inline void clear() { triIndexes.resize(0); } 57 | 58 | //! Adds a triangle to the mesh 59 | /** Vertex indexes are expresesed relatively to the vertex cloud. 60 | \param i1 first vertex index 61 | \param i2 second vertex index 62 | \param i3 third vertex index 63 | **/ 64 | virtual void addTriangle(unsigned i1, unsigned i2, unsigned i3); 65 | 66 | //! Reserves the memory to store the triangles (as 3 indexes each) 67 | /** \param n the number of triangles to reserve 68 | \return true if the method succeeds, false otherwise 69 | **/ 70 | virtual bool reserve(unsigned n); 71 | 72 | //! Resizes the mesh database 73 | /** If the new number of elements is smaller than the actual size, 74 | the overflooding elements will be deleted. 75 | \param n the new number of triangles 76 | \return true if the method succeeds, false otherwise 77 | **/ 78 | virtual bool resize(unsigned n); 79 | 80 | //inherited from GenericIndexedMesh 81 | bool normalsAvailable() const override; 82 | bool interpolateNormals(unsigned triIndex, const CCVector3& P, CCVector3& N) override; 83 | 84 | protected: 85 | 86 | //! A triangle vertices indexes container 87 | using TriangleIndexesContainer = std::vector; 88 | //! The triangles indexes 89 | TriangleIndexesContainer triIndexes; 90 | 91 | //! Iterator on the list of triangles 92 | unsigned globalIterator; 93 | //! Dump triangle structure to transmit temporary data 94 | SimpleTriangle dummyTriangle; 95 | 96 | //! The associated point cloud (vertices) 97 | GenericIndexedCloud* theVertices; 98 | //! Specifies if the associated cloud should be deleted when the mesh is deleted 99 | bool verticesLinked; 100 | 101 | //! Bounding-box 102 | BoundingBox m_bbox; 103 | }; 104 | 105 | } 106 | -------------------------------------------------------------------------------- /include/SimpleTriangle.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #pragma once 5 | 6 | //Local 7 | #include "GenericTriangle.h" 8 | 9 | namespace CCCoreLib 10 | { 11 | //! A simple triangle class 12 | /** Implements the GenericTriangle class with references to 3D points. 13 | WARNING: make sure that references don't point to temporary objects! 14 | WARNING: not compatible with parallelization. 15 | **/ 16 | class CC_CORE_LIB_API SimpleRefTriangle : public GenericTriangle 17 | { 18 | public: 19 | 20 | //! Default constructor 21 | SimpleRefTriangle() 22 | : A(nullptr) 23 | , B(nullptr) 24 | , C(nullptr) 25 | {} 26 | 27 | //! Constructor from 3 vertices (references to) 28 | /** \param _A first vertex 29 | \param _B second vertex 30 | \param _C third vertex 31 | **/ 32 | SimpleRefTriangle(const CCVector3* _A, const CCVector3* _B, const CCVector3* _C) 33 | : A(_A) 34 | , B(_B) 35 | , C(_C) 36 | {} 37 | 38 | //inherited methods (see GenericDistribution) 39 | inline const CCVector3* _getA() const override { return A; } 40 | inline const CCVector3* _getB() const override { return B; } 41 | inline const CCVector3* _getC() const override { return C; } 42 | 43 | //! A vertex (ref) 44 | const CCVector3 *A; 45 | //! B vertex (ref) 46 | const CCVector3 *B; 47 | //! C vertex (ref) 48 | const CCVector3 *C; 49 | }; 50 | 51 | //! A simple triangle class 52 | /** Implements the GenericTriangle class with a triplet of 3D points. 53 | Relies on direct storage for speed enhancement and parallelization! 54 | **/ 55 | class CC_CORE_LIB_API SimpleTriangle : public GenericTriangle 56 | { 57 | public: 58 | 59 | //! Default constructor 60 | SimpleTriangle() 61 | : A(0,0,0) 62 | , B(0,0,0) 63 | , C(0,0,0) 64 | {} 65 | 66 | //! Constructor from 3 vertices 67 | /** \param _A first vertex 68 | \param _B second vertex 69 | \param _C third vertex 70 | **/ 71 | SimpleTriangle(const CCVector3& _A, const CCVector3& _B, const CCVector3& _C) 72 | : A(_A) 73 | , B(_B) 74 | , C(_C) 75 | {} 76 | 77 | //inherited methods (see GenericDistribution) 78 | inline const CCVector3* _getA() const override { return &A; } 79 | inline const CCVector3* _getB() const override { return &B; } 80 | inline const CCVector3* _getC() const override { return &C; } 81 | 82 | //! A vertex 83 | CCVector3 A; 84 | //! B vertex 85 | CCVector3 B; 86 | //! C vertex 87 | CCVector3 C; 88 | }; 89 | } 90 | -------------------------------------------------------------------------------- /include/StatisticalTestingTools.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #pragma once 5 | 6 | //Local 7 | #include "CCToolbox.h" 8 | #include "DgmOctree.h" 9 | 10 | namespace CCCoreLib 11 | { 12 | class GenericCloud; 13 | class GenericDistribution; 14 | class GenericIndexedCloud; 15 | class GenericIndexedCloudPersist; 16 | class GenericProgressCallback; 17 | 18 | //! Statistical testing algorithms (Chi2 distance computation, statistic filtering, etc.) 19 | class CC_CORE_LIB_API StatisticalTestingTools : public CCToolbox 20 | { 21 | public: 22 | 23 | //! Computes the Chi2 distance on a sample of scalar values 24 | /** The Chi2 distance is computed between an empiric distribution generated from a 25 | set of scalar values (with a specific number of classes), and a theoretical distribution. 26 | It assures that each class of the empirical distribution is such that it respects n.pi>=5 27 | (where n is the total number of points, and pi is the cumulative probability of the class). 28 | Therefore the number of classes can be changed by the method. 29 | If the 'noClassCompression' parameter is set to true, the above condition is not 30 | checked and distance can diverge (which should not be possible according to the Chi2 Test theory, 31 | but it can be useful for classification purposes). 32 | \param distrib a theoretical distribution 33 | \param cloud a subset of points (associated to scalar values) 34 | \param numberOfClasses initial number of classes for the empirical distribution (0 for automatic determination, >1 otherwise) 35 | \param finalNumberOfClasses final number of classes of the empirical distribution 36 | \param noClassCompression prevent the algorithm from performing classes compression (faster but less accurate) 37 | \param histoMin [optional] minimum histogram value 38 | \param histoMax [optional] maximum histogram value 39 | \param[out] histoValues [optional] histogram array (its size should be equal to the initial number of classes) 40 | \param[out] npis [optional] array containing the theoretical probabilities for each class (its size should be equal to the initial number of classes) 41 | \return the Chi2 distance (or -1.0 if an error occurred) 42 | **/ 43 | static double computeAdaptativeChi2Dist(const GenericDistribution* distrib, 44 | const GenericCloud* cloud, 45 | unsigned numberOfClasses, 46 | unsigned &finalNumberOfClasses, 47 | bool noClassCompression = false, 48 | const ScalarType* histoMin = nullptr, 49 | const ScalarType* histoMax = nullptr, 50 | unsigned* histoValues = nullptr, 51 | double* npis = nullptr); 52 | 53 | //! Computes the Chi2 fractile 54 | /** Returns the max Chi2 Distance for a given "confidence" probability and a given number of 55 | "degrees of liberty" (equivalent to the number of classes-1). 56 | \param p the result "confidence" 57 | \param d the number of d.o.l. 58 | \return the Chi2 fractile 59 | **/ 60 | static double computeChi2Fractile(double p, int d); 61 | 62 | //! Computes the Chi2 confidence probability 63 | /** Returns the Chi2 confidence probability for a given Chi2 distance value and a given 64 | number of "degress of liberty" (equivalent to the number of classes-1). 65 | \param chi2result the Chi2 distance 66 | \param d the number of d.o.l. 67 | \return the Chi2 confidence probability 68 | **/ 69 | static double computeChi2Probability(double chi2result, int d); 70 | 71 | //! Classfies the point cloud in two category by locally applying a statistical (Chi2) test 72 | /** This algorithm is described in Daniel Girardeau-Montaut's PhD manuscript (Chapter 3. 73 | section 3.2.3). It mainly consists in determining if a point associated scalar value 74 | is part of the measurements noise (in which case the point will be considered as 75 | "unchanged") or if not (in which case the point will be considered as a trully "changed"). 76 | This determination is based on a statistical analysis (Chi2 Test) of the scalar values 77 | distribution around each point (by considering a small neighbourhood of points around 78 | each point). The classification result will depend on the Chi2 Test parameters (e.g. 79 | the number of classes - which in this case is equal to the square root of the neighbourhood 80 | size - and the confidence probability - the greater it is, the more severe the result is). 81 | WARNING : the scalar field behind the GenericIndexedCloud interface should be splet in 2, 82 | one for reading the scalar values to test (OUT - getScalarValue) and the other to save the 83 | resulting Chi2 distance (IN - setScalarValue). 84 | \param distrib a theoretical noise distribution 85 | \param theCloud the point cloud to classify 86 | \param numberOfNeighbours the neighbourhood size for the local analysis 87 | \param pTrust the Chi2 Test confidence probability 88 | \param progressCb the client application can get some notification of the process progress through this callback mechanism (see GenericProgressCallback) 89 | \param inputOctree the cloud octree if it has already be computed 90 | \return the distance threshold for filtering (or -1 if someting went wrong during the process) 91 | **/ 92 | static double testCloudWithStatisticalModel(const GenericDistribution* distrib, 93 | GenericIndexedCloudPersist* theCloud, 94 | unsigned numberOfNeighbours, 95 | double pTrust, 96 | GenericProgressCallback* progressCb = nullptr, 97 | DgmOctree* inputOctree = nullptr); 98 | 99 | protected: 100 | 101 | //! Computes (locally) the Chi2 distance inside an octree cell 102 | /** Additional parameters are: 103 | - (GenericDistribution*) the theoretical noise distribution 104 | - (int) the size of a neighbourhood for local analysis 105 | - (int) the number of classes for the Chi2 distance computation 106 | - (unsigned*) a pre-allocated array (of a size equal to the number of classes)for computation acceleration 107 | - (bool) specifies whether negative values should be included in computation 108 | \param cell structure describing the cell on which processing is applied 109 | \param additionalParameters see method description 110 | \param nProgress optional (normalized) progress notification (per-point) 111 | **/ 112 | static bool computeLocalChi2DistAtLevel(const DgmOctree::octreeCell& cell, 113 | void** additionalParameters, 114 | NormalizedProgress* nProgress = nullptr); 115 | }; 116 | } 117 | -------------------------------------------------------------------------------- /include/TrueKdTree.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #pragma once 5 | 6 | //Local 7 | #include "DistanceComputationTools.h" 8 | #include "ReferenceCloud.h" 9 | 10 | //system 11 | #include 12 | 13 | namespace CCCoreLib 14 | { 15 | class GenericIndexedCloudPersist; 16 | class GenericProgressCallback; 17 | 18 | //! KD-tree implementation to subdivide a 3D cloud based on a planarity criterion 19 | /** The resulting subsets of points falling inside each partition will be as flat as required 20 | (unless a minimum number of points is reached). 21 | **/ 22 | class CC_CORE_LIB_API TrueKdTree 23 | { 24 | public: 25 | 26 | //Warning: never pass a 'constant initializer' by reference 27 | static const uint8_t X_DIM = 0; 28 | static const uint8_t Y_DIM = 1; 29 | static const uint8_t Z_DIM = 2; 30 | static const uint8_t NODE_TYPE = 0; 31 | static const uint8_t LEAF_TYPE = 1; 32 | 33 | //! Tree base node 34 | class BaseNode 35 | { 36 | public: 37 | explicit BaseNode(uint8_t nodeType) : parent(nullptr), type(nodeType) {} 38 | virtual ~BaseNode() = default; 39 | 40 | bool isNode() const { return type == NODE_TYPE; } 41 | bool isLeaf() const { return type == LEAF_TYPE; } 42 | 43 | public: 44 | 45 | //Warning: put the non aligned members (< 4 bytes) at the end to avoid too much alignment padding! 46 | BaseNode* parent; //8 bytes 47 | 48 | protected: 49 | const uint8_t type; //1 byte (+ 3 for alignment) 50 | 51 | //Total //12 bytes 52 | }; 53 | 54 | //! Tree node 55 | class Node : public BaseNode 56 | { 57 | public: 58 | 59 | //Warning: put the non aligned members (< 4 bytes) at the end to avoid too much alignment padding! 60 | PointCoordinateType splitValue; //4 bytes 61 | BaseNode* leftChild; //8 bytes 62 | BaseNode* rightChild; //8 bytes 63 | uint8_t splitDim; //1 byte (+ 3 bytes for alignment) 64 | 65 | //Total //24 bytes (+ 12 for father) 66 | 67 | Node() 68 | : BaseNode(NODE_TYPE) 69 | , splitValue(0) 70 | , leftChild(nullptr) 71 | , rightChild(nullptr) 72 | , splitDim(X_DIM) 73 | {} 74 | 75 | ~Node() override 76 | { 77 | delete leftChild; 78 | delete rightChild; 79 | } 80 | }; 81 | 82 | //! Tree leaf 83 | class Leaf : public BaseNode 84 | { 85 | public: 86 | 87 | //Warning: put the non aligned members (< 4 bytes) at the end to avoid too much alignment padding! 88 | ReferenceCloud* points; // 8 bytes 89 | PointCoordinateType planeEq[4]; //16 bytes 90 | ScalarType error; // 4 bytes 91 | int userData; // 4 bytes 92 | 93 | //Total //32 bytes (+ 12 for father) 94 | 95 | //! Constructor 96 | /** The Leaf class takes ownership of its associated subset 97 | **/ 98 | Leaf(ReferenceCloud* set, const PointCoordinateType planeEquation[], ScalarType _error) 99 | : BaseNode(LEAF_TYPE) 100 | , points(set) 101 | , error(_error) 102 | , userData(0) 103 | { 104 | memcpy(planeEq, planeEquation, sizeof(PointCoordinateType) * 4); 105 | } 106 | 107 | ~Leaf() override 108 | { 109 | delete points; 110 | } 111 | }; 112 | 113 | //! A vector of leaves 114 | using LeafVector = std::vector; 115 | 116 | //! Default constructor 117 | explicit TrueKdTree(GenericIndexedCloudPersist* cloud); 118 | 119 | //! Destructor 120 | ~TrueKdTree(); 121 | 122 | //! Returns the associated cloud 123 | inline GenericIndexedCloudPersist* associatedCloud() const { return m_associatedCloud; } 124 | 125 | //! Builds KD-tree 126 | /** \param maxError maximum error per cell (relatively to the best LS plane fit) 127 | \param errorMeasure error measurement 128 | \param minPointCountPerCell minimum number of points per cell (can't be smaller than 3) 129 | \param maxPointCountPerCell maximum number of points per cell (speed-up - ignored if < 6) 130 | \param progressCb the client application can get some notification of the process progress through this callback mechanism (see GenericProgressCallback) 131 | **/ 132 | bool build( double maxError, 133 | DistanceComputationTools::ERROR_MEASURES errorMeasure = DistanceComputationTools::RMS, 134 | unsigned minPointCountPerCell = 3, 135 | unsigned maxPointCountPerCell = 0, 136 | GenericProgressCallback* progressCb = nullptr); 137 | 138 | //! Clears structure 139 | void clear(); 140 | 141 | //! Returns max error threshold used for planarity-based split strategy 142 | inline double getMaxError() const { return m_maxError; } 143 | 144 | //! Returns max error estimator used for planarity-based split strategy 145 | inline DistanceComputationTools::ERROR_MEASURES getMaxErrorType() const { return m_errorMeasure; } 146 | 147 | //! Returns all leaf nodes 148 | bool getLeaves(LeafVector& leaves) const; 149 | 150 | protected: 151 | 152 | //! Recursive split process 153 | BaseNode* split(ReferenceCloud* subset); 154 | 155 | //! Root node 156 | BaseNode* m_root; 157 | 158 | //! Associated cloud 159 | GenericIndexedCloudPersist* m_associatedCloud; 160 | 161 | //! Max error for planarity-based split strategy (see m_errorMeasure) 162 | double m_maxError; 163 | 164 | //! Error measurement 165 | DistanceComputationTools::ERROR_MEASURES m_errorMeasure; 166 | 167 | //! Min number of points per cell (speed-up) 168 | /** Can't be < 3 169 | **/ 170 | unsigned m_minPointCountPerCell; 171 | 172 | //! Max number of points per cell (speed-up) 173 | /** Ignored if < 6 174 | **/ 175 | unsigned m_maxPointCountPerCell; 176 | }; 177 | } 178 | -------------------------------------------------------------------------------- /include/WeibullDistribution.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #pragma once 5 | 6 | //Local 7 | #include "GenericDistribution.h" 8 | 9 | namespace CCCoreLib 10 | { 11 | //! The Weibull statistical parametric distribution 12 | /** Implements the GenericDistribution interface. 13 | **/ 14 | class CC_CORE_LIB_API WeibullDistribution : public GenericDistribution 15 | { 16 | public: 17 | 18 | //! WeibullDistribution constructor 19 | WeibullDistribution(); 20 | 21 | //! WeibullDistribution constructor 22 | /** Distrubtion parameters can be directly set during object 23 | construction. 24 | \param a the Weibull a parameter (also known as 'k') 25 | \param b the Weibull b parameter (also known as 'lambda') 26 | \param valueShift a value shift ('zero') 27 | **/ 28 | WeibullDistribution(ScalarType a, ScalarType b, ScalarType valueShift = 0); 29 | 30 | //! Returns the distribution parameters 31 | /** \param a the Weibull a parameter (also known as 'k') 32 | \param b the Weibull b parameter (also known as 'lambda') 33 | \return the parameters validity 34 | **/ 35 | bool getParameters(ScalarType &a, ScalarType &b) const; 36 | 37 | //! Returns the normal distribution equivalent parameters 38 | /** \param mu a field to transmit the equivalent mean 39 | \param sigma2 a field to transmit the equivalent variance 40 | \return the parameters validity 41 | **/ 42 | bool getOtherParameters(ScalarType &mu, ScalarType &sigma2) const; 43 | 44 | //! Returns the distribution 'mode' 45 | double computeMode() const; 46 | //! Returns the distribution 'skewness' 47 | double computeSkewness() const; 48 | 49 | //! Sets the distribution parameters 50 | /** \param a the Weibull a parameter (also known as 'k') 51 | \param b the Weibull b parameter (also known as 'lambda') 52 | \param valueShift a value shift ('zero') 53 | \return the parameters validity 54 | **/ 55 | bool setParameters(ScalarType a, ScalarType b, ScalarType valueShift = 0); 56 | 57 | //! Sets the distribution value shift 58 | /** \param vs value shift 59 | **/ 60 | void setValueShift(ScalarType vs); 61 | 62 | //! Returns the distribution value shift 63 | inline ScalarType getValueShift() const { return m_valueShift; } 64 | 65 | //inherited methods (see GenericDistribution) 66 | bool computeParameters(const ScalarContainer& values) override; 67 | double computeP(ScalarType x) const override; 68 | double computePfromZero(ScalarType x) const override; 69 | double computeP(ScalarType x1, ScalarType x2) const override; 70 | double computeChi2Dist(const GenericCloud* cloud, unsigned numberOfClasses, int* histo = nullptr) override; 71 | const char* getName() const override { return "Weibull"; } 72 | 73 | protected: 74 | 75 | //! Compute each Chi2 class limits 76 | /** This method is used (internally) to accelerate the Chi2 distance computation. 77 | \param numberOfClasses the number of classes that will be used for Chi2 distance computation 78 | \return success 79 | **/ 80 | virtual bool setChi2ClassesPositions(unsigned numberOfClasses); 81 | 82 | //! Chi2 classes limits 83 | /** Used internally. Stores both limits for each class in a vector 84 | (min_class_1, max_class_1, min_class_2, max_class_2, etc.). 85 | **/ 86 | std::vector chi2ClassesPositions; 87 | 88 | //! Parameters validity 89 | bool parametersDefined; 90 | //! Weibull distribution parameter a (k) 91 | ScalarType m_a; 92 | //! Weibull distribution parameter b (lambda) 93 | ScalarType m_b; 94 | //! Weibull distribution parameter 'value shift' 95 | ScalarType m_valueShift; 96 | 97 | //! Normal distribution equivalent parameter: mean 98 | ScalarType m_mu; 99 | //! Normal distribution equivalent parameter: variance 100 | ScalarType m_sigma2; 101 | 102 | //! internal function for parameters evaluation from sample points 103 | /** inverseVmax can be optionally specified for overflow-safe version 104 | **/ 105 | static double ComputeG(const ScalarContainer& values, double a, ScalarType valueShift, double valueRange); 106 | //! internal function for parameters evaluation from sample points 107 | static double FindGRoot(const ScalarContainer& values, ScalarType valueShift, double valueRange); 108 | }; 109 | } 110 | -------------------------------------------------------------------------------- /src/CCShareable.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #include "CCShareable.h" 5 | 6 | #ifdef CC_TRACK_ALIVE_SHARED_OBJECTS 7 | 8 | #ifdef _MSC_VER 9 | //To get rid of the really annoying warnings about template class exportation 10 | #pragma warning( disable: 4530 ) 11 | #endif 12 | 13 | //system 14 | #include 15 | 16 | //set of all currently 'alived' shared objects 17 | static std::vector s_aliveSharedObjects; 18 | 19 | unsigned CCShareable::GetAliveCount() 20 | { 21 | return s_aliveSharedObjects.size(); 22 | } 23 | 24 | #endif 25 | 26 | CCShareable::CCShareable() : m_linkCount(0) 27 | { 28 | #ifdef CC_TRACK_ALIVE_SHARED_OBJECTS 29 | s_aliveSharedObjects.push_back(this); 30 | #endif 31 | } 32 | 33 | void CCShareable::link() 34 | { 35 | ++m_linkCount; 36 | } 37 | 38 | void CCShareable::release() 39 | { 40 | if (m_linkCount > 1) 41 | --m_linkCount; 42 | else 43 | delete this; 44 | } 45 | 46 | CCShareable::~CCShareable() 47 | { 48 | #ifdef CC_TRACK_ALIVE_SHARED_OBJECTS 49 | std::vector::iterator it; 50 | for (it=s_aliveSharedObjects.begin(); it!=s_aliveSharedObjects.end(); ++it) 51 | { 52 | if (*it == this) 53 | { 54 | std::swap(*it,s_aliveSharedObjects.back()); 55 | s_aliveSharedObjects.pop_back(); 56 | return; 57 | } 58 | } 59 | #endif 60 | } 61 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | # Copyright © Andy Maloney 3 | 4 | target_sources( ${PROJECT_NAME} 5 | PRIVATE 6 | ${CMAKE_CURRENT_LIST_DIR}/AutoSegmentationTools.cpp 7 | ${CMAKE_CURRENT_LIST_DIR}/CCMiscTools.cpp 8 | ${CMAKE_CURRENT_LIST_DIR}/CCShareable.cpp 9 | ${CMAKE_CURRENT_LIST_DIR}/ChamferDistanceTransform.cpp 10 | ${CMAKE_CURRENT_LIST_DIR}/Chi2Helper.h 11 | ${CMAKE_CURRENT_LIST_DIR}/CloudSamplingTools.cpp 12 | ${CMAKE_CURRENT_LIST_DIR}/Delaunay2dMesh.cpp 13 | ${CMAKE_CURRENT_LIST_DIR}/DgmOctree.cpp 14 | ${CMAKE_CURRENT_LIST_DIR}/DgmOctreeReferenceCloud.cpp 15 | ${CMAKE_CURRENT_LIST_DIR}/DistanceComputationTools.cpp 16 | ${CMAKE_CURRENT_LIST_DIR}/ErrorFunction.cpp 17 | ${CMAKE_CURRENT_LIST_DIR}/FastMarching.cpp 18 | ${CMAKE_CURRENT_LIST_DIR}/FastMarchingForPropagation.cpp 19 | ${CMAKE_CURRENT_LIST_DIR}/GeometricalAnalysisTools.cpp 20 | ${CMAKE_CURRENT_LIST_DIR}/GridAndMeshIntersection.cpp 21 | ${CMAKE_CURRENT_LIST_DIR}/KdTree.cpp 22 | ${CMAKE_CURRENT_LIST_DIR}/Kriging.cpp 23 | ${CMAKE_CURRENT_LIST_DIR}/LocalModel.cpp 24 | ${CMAKE_CURRENT_LIST_DIR}/ManualSegmentationTools.cpp 25 | ${CMAKE_CURRENT_LIST_DIR}/MeshSamplingTools.cpp 26 | ${CMAKE_CURRENT_LIST_DIR}/Neighbourhood.cpp 27 | ${CMAKE_CURRENT_LIST_DIR}/NormalDistribution.cpp 28 | ${CMAKE_CURRENT_LIST_DIR}/NormalizedProgress.cpp 29 | ${CMAKE_CURRENT_LIST_DIR}/PointProjectionTools.cpp 30 | ${CMAKE_CURRENT_LIST_DIR}/Polyline.cpp 31 | ${CMAKE_CURRENT_LIST_DIR}/ReferenceCloud.cpp 32 | ${CMAKE_CURRENT_LIST_DIR}/RegistrationTools.cpp 33 | ${CMAKE_CURRENT_LIST_DIR}/SaitoSquaredDistanceTransform.cpp 34 | ${CMAKE_CURRENT_LIST_DIR}/ScalarField.cpp 35 | ${CMAKE_CURRENT_LIST_DIR}/ScalarFieldTools.cpp 36 | ${CMAKE_CURRENT_LIST_DIR}/SimpleMesh.cpp 37 | ${CMAKE_CURRENT_LIST_DIR}/StatisticalTestingTools.cpp 38 | ${CMAKE_CURRENT_LIST_DIR}/TrueKdTree.cpp 39 | ${CMAKE_CURRENT_LIST_DIR}/WeibullDistribution.cpp 40 | ) 41 | 42 | target_include_directories( ${PROJECT_NAME} 43 | PRIVATE 44 | ${CMAKE_CURRENT_SOURCE_DIR}/src 45 | ) 46 | -------------------------------------------------------------------------------- /src/ChamferDistanceTransform.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #include "ChamferDistanceTransform.h" 5 | 6 | //system 7 | #include 8 | #include 9 | 10 | using namespace CCCoreLib; 11 | 12 | //! Forward mask shifts and weights (Chamfer 3-4-5) 13 | const signed char ForwardNeighbours345[14*4] = { 14 | -1,-1,-1, 5, 15 | 0,-1,-1, 4, 16 | 1,-1,-1, 5, 17 | -1, 0,-1, 4, 18 | 0, 0,-1, 3, 19 | 1, 0,-1, 4, 20 | -1, 1,-1, 5, 21 | 0, 1,-1, 4, 22 | 1, 1,-1, 5, 23 | -1,-1, 0, 4, 24 | 0,-1, 0, 3, 25 | 1,-1, 0, 4, 26 | -1, 0, 0, 3, 27 | 0, 0, 0, 0 28 | }; 29 | 30 | //! Backward mask shifts and weights (Chamfer 3-4-5) 31 | const signed char BackwardNeighbours345[14*4] = { 32 | 0, 0, 0, 0, 33 | 1, 0, 0, 3, 34 | -1, 1, 0, 4, 35 | 0, 1, 0, 3, 36 | 1, 1, 0, 4, 37 | -1,-1, 1, 5, 38 | 0,-1, 1, 4, 39 | 1,-1, 1, 5, 40 | -1, 0, 1, 4, 41 | 0, 0, 1, 3, 42 | 1, 0, 1, 4, 43 | -1, 1, 1, 5, 44 | 0, 1, 1, 4, 45 | 1, 1, 1, 5 46 | }; 47 | 48 | //! Forward mask shifts and weights (Chamfer 1-1-1) 49 | const signed char ForwardNeighbours111[14*4] = { 50 | -1,-1,-1, 1, 51 | 0,-1,-1, 1, 52 | 1,-1,-1, 1, 53 | -1, 0,-1, 1, 54 | 0, 0,-1, 1, 55 | 1, 0,-1, 1, 56 | -1, 1,-1, 1, 57 | 0, 1,-1, 1, 58 | 1, 1,-1, 1, 59 | -1,-1, 0, 1, 60 | 0,-1, 0, 1, 61 | 1,-1, 0, 1, 62 | -1, 0, 0, 1, 63 | 0, 0, 0, 0 64 | }; 65 | 66 | //! Backward masks shifts and weights (Chamfer 1-1-1) 67 | const signed char BackwardNeighbours111[14*4] = { 68 | 0, 0, 0, 0, 69 | 1, 0, 0, 1, 70 | -1, 1, 0, 1, 71 | 0, 1, 0, 1, 72 | 1, 1, 0, 1, 73 | -1,-1, 1, 1, 74 | 0,-1, 1, 1, 75 | 1,-1, 1, 1, 76 | -1, 0, 1, 1, 77 | 0, 0, 1, 1, 78 | 1, 0, 1, 1, 79 | -1, 1, 1, 1, 80 | 0, 1, 1, 1, 81 | 1, 1, 1, 1 82 | }; 83 | 84 | //ChamferDistanceTransform::GridElement ChamferDistanceTransform::propagateDistance( unsigned iStart, 85 | // unsigned jStart, 86 | // unsigned kStart, 87 | // bool forward, 88 | // const signed char neighbours[14][4], 89 | // NormalizedProgress* normProgress/*=nullptr*/) 90 | //{ 91 | // assert(!m_grid.empty()); 92 | // 93 | // GridElement* _grid = &(m_grid[pos2index(iStart, jStart, kStart)]); 94 | // 95 | // //accelerating structure 96 | // int neighborShift[14]; 97 | // { 98 | // for (unsigned char v=0; v<14; ++v) 99 | // { 100 | // neighborShift[v] = static_cast(neighbours[v][0]) + 101 | // static_cast(neighbours[v][1]) * static_cast(m_rowSize) + 102 | // static_cast(neighbours[v][2]) * static_cast(m_sliceSize); 103 | // } 104 | // } 105 | // 106 | // GridElement maxDist = 0; 107 | // 108 | // int order = forward ? 1 : -1; 109 | // 110 | // for (unsigned k=0; k(neighbours[0][3]); 117 | // 118 | // for (unsigned char v=1; v<14; ++v) 119 | // { 120 | // GridElement neighborVal = _grid[neighborShift[v]] + static_cast(neighbours[v][3]); 121 | // minVal = std::min(minVal, neighborVal); 122 | // } 123 | // 124 | // *_grid = minVal; 125 | // 126 | // //we track the max distance 127 | // if (minVal > maxDist) 128 | // { 129 | // maxDist = minVal; 130 | // } 131 | // 132 | // _grid += order; 133 | // } 134 | // _grid += order*2; //next line 135 | // 136 | // if (normProgress && !normProgress->oneStep()) 137 | // { 138 | // break; 139 | // } 140 | // } 141 | // 142 | // _grid += (order*2) * static_cast(m_rowSize); //next slice 143 | // } 144 | // 145 | // return maxDist; 146 | //} 147 | 148 | int ChamferDistanceTransform::propagateDistance(CHAMFER_DISTANCE_TYPE type, GenericProgressCallback* progressCb) 149 | { 150 | if (m_grid.empty()) 151 | { 152 | assert(false); 153 | return -1; 154 | } 155 | 156 | const signed char* fwNeighbours = nullptr; 157 | const signed char* bwNeighbours = nullptr; 158 | switch (type) 159 | { 160 | case CHAMFER_111: 161 | { 162 | fwNeighbours = ForwardNeighbours111; 163 | bwNeighbours = BackwardNeighbours111; 164 | } 165 | break; 166 | 167 | case CHAMFER_345: 168 | { 169 | fwNeighbours = ForwardNeighbours345; 170 | bwNeighbours = BackwardNeighbours345; 171 | } 172 | break; 173 | 174 | default: 175 | //unhandled type?! 176 | assert(false); 177 | return -1; 178 | } 179 | 180 | NormalizedProgress normProgress(progressCb, m_innerSize.y*m_innerSize.z * 2); 181 | if (progressCb) 182 | { 183 | if (progressCb->textCanBeEdited()) 184 | { 185 | progressCb->setMethodTitle("Chamfer distance"); 186 | char buffer[64]; 187 | snprintf(buffer, 64, "Box: [%u x %u x %u]", m_innerSize.x, m_innerSize.y, m_innerSize.z); 188 | progressCb->setInfo(buffer); 189 | } 190 | progressCb->update(0); 191 | progressCb->start(); 192 | } 193 | 194 | //1st pass: forward scan 195 | { 196 | GridElement* _grid = &(m_grid[pos2index(0, 0, 0)]); 197 | 198 | //accelerating structure 199 | int neighborShift[14]; 200 | { 201 | for (unsigned char v=0; v<14; ++v) 202 | { 203 | const signed char* fwNeighbour = fwNeighbours + 4*v; 204 | neighborShift[v] = static_cast(fwNeighbour[0]) + 205 | static_cast(fwNeighbour[1]) * static_cast(m_rowSize) + 206 | static_cast(fwNeighbour[2]) * static_cast(m_sliceSize); 207 | } 208 | } 209 | 210 | for (unsigned k=0; k(fwNeighbours[3]); 217 | 218 | for (unsigned char v=1; v<14; ++v) 219 | { 220 | const signed char* fwNeighbour = fwNeighbours + 4*v; 221 | GridElement neighborVal = _grid[neighborShift[v]] + static_cast(fwNeighbour[3]); 222 | minVal = std::min(minVal, neighborVal); 223 | } 224 | 225 | *_grid++ = minVal; 226 | } 227 | _grid += 2; //next line 228 | 229 | if (progressCb && !normProgress.oneStep()) 230 | { 231 | break; 232 | } 233 | } 234 | 235 | _grid += 2*m_rowSize; //next slice 236 | } 237 | } 238 | 239 | //2nd pass: backward scan 240 | GridElement maxDist = 0; 241 | { 242 | //accelerating structure 243 | int neighborShift[14]; 244 | { 245 | for (unsigned char v=0; v<14; ++v) 246 | { 247 | const signed char* bwNeighbour = bwNeighbours + 4*v; 248 | neighborShift[v] = static_cast(bwNeighbour[0]) + 249 | static_cast(bwNeighbour[1]) * static_cast(m_rowSize) + 250 | static_cast(bwNeighbour[2]) * static_cast(m_sliceSize); 251 | } 252 | } 253 | 254 | GridElement* _grid = &(m_grid[pos2index(static_cast(m_innerSize.x) - 1, static_cast(m_innerSize.y) - 1, static_cast(m_innerSize.z) - 1)]); 255 | 256 | for (unsigned k = 0; k < m_innerSize.z; ++k) 257 | { 258 | for (unsigned j = 0; j < m_innerSize.y; ++j) 259 | { 260 | for (unsigned i = 0; i < m_innerSize.x; ++i) 261 | { 262 | GridElement minVal = _grid[neighborShift[0]] + static_cast(bwNeighbours[3]); 263 | 264 | for (unsigned char v = 1; v < 14; ++v) 265 | { 266 | const signed char* bwNeighbour = bwNeighbours + 4 * v; 267 | GridElement neighborVal = _grid[neighborShift[v]] + static_cast(bwNeighbour[3]); 268 | minVal = std::min(minVal, neighborVal); 269 | } 270 | 271 | *_grid-- = minVal; 272 | 273 | //we track the max distance 274 | if (minVal > maxDist) 275 | { 276 | maxDist = minVal; 277 | } 278 | } 279 | _grid -= 2; //next line 280 | 281 | if (progressCb && !normProgress.oneStep()) 282 | { 283 | break; 284 | } 285 | } 286 | 287 | _grid -= 2 * m_rowSize; //next slice 288 | } 289 | } 290 | 291 | return static_cast(maxDist); 292 | } 293 | -------------------------------------------------------------------------------- /src/Chi2Helper.h: -------------------------------------------------------------------------------- 1 | //########################################################################## 2 | //# # 3 | //# CCLIB # 4 | //# # 5 | //# This program is free software; you can redistribute it and/or modify # 6 | //# it under the terms of the GNU Library General Public License as # 7 | //# published by the Free Software Foundation; version 2 or later of the # 8 | //# License. # 9 | //# # 10 | //# This program is distributed in the hope that it will be useful, # 11 | //# but WITHOUT ANY WARRANTY; without even the implied warranty of # 12 | //# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # 13 | //# GNU General Public License for more details. # 14 | //# # 15 | //# COPYRIGHT: EDF R&D / TELECOM ParisTech (ENST-TSI) # 16 | //# # 17 | //########################################################################## 18 | 19 | #ifndef CHI2_HELPER_HEADER 20 | #define CHI2_HELPER_HEADER 21 | 22 | //system 23 | #include 24 | 25 | #ifndef LOG_SQRT_PI 26 | #define LOG_SQRT_PI 0.5723649429247000870717135 /* log(sqrt(pi)) */ 27 | #endif 28 | #ifndef I_SQRT_PI 29 | #define I_SQRT_PI 0.5641895835477562869480795 /* 1 / sqrt(pi) */ 30 | #endif 31 | 32 | //! Package of methods to compute Chi2 related stuff 33 | /** The following JavaScript functions for calculating normal and 34 | chi-square probabilities and critical values were adapted by 35 | John Walker from C implementations 36 | written by Gary Perlman of Wang Institute, Tyngsboro, MA 37 | 01879. Both the original C code and this JavaScript edition 38 | are in the public domain. 39 | **/ 40 | class Chi2Helper 41 | { 42 | public: 43 | //! Probability of normal z value 44 | /** Adapted from a polynomial approximation in: 45 | Ibbetson D, Algorithm 209 46 | Collected Algorithms of the CACM 1963 p. 616 47 | Note: 48 | This routine has six digit accuracy, so it is only useful for absolute 49 | z values < 6. For z values >= to 6.0, poz() returns 0.0. 50 | **/ 51 | static double poz(double z) 52 | { 53 | double x; 54 | 55 | if (z == 0.0) 56 | { 57 | x = 0.0; 58 | } 59 | else 60 | { 61 | double y = 0.5 * std::abs(z); 62 | if (y >= 3.0) /* Maximum meaningful z value (6) divided by 2 */ 63 | { 64 | x = 1.0; 65 | } 66 | else if (y < 1.0) 67 | { 68 | double w = y * y; 69 | x = ((((((((0.000124818987 * w 70 | - 0.001075204047) * w + 0.005198775019) * w 71 | - 0.019198292004) * w + 0.059054035642) * w 72 | - 0.151968751364) * w + 0.319152932694) * w 73 | - 0.531923007300) * w + 0.797884560593) * y * 2.0; 74 | } 75 | else 76 | { 77 | y -= 2.0; 78 | x = (((((((((((((-0.000045255659 * y 79 | + 0.000152529290) * y - 0.000019538132) * y 80 | - 0.000676904986) * y + 0.001390604284) * y 81 | - 0.000794620820) * y - 0.002034254874) * y 82 | + 0.006549791214) * y - 0.010557625006) * y 83 | + 0.011630447319) * y - 0.009279453341) * y 84 | + 0.005353579108) * y - 0.002141268741) * y 85 | + 0.000535310849) * y + 0.999936657524; 86 | } 87 | } 88 | return z > 0.0 ? ((x + 1.0) * 0.5) : ((1.0 - x) * 0.5); 89 | } 90 | 91 | //! Value above which exp(EXP_MAX_A_VALUE) diverges 92 | static inline double EXP_MAX_A_VALUE() { return 50.0; } 93 | 94 | //! Probability of chi-square value 95 | /** Adapted from: 96 | Hill, I. D. and Pike, M. C. Algorithm 299 97 | Collected Algorithms for the CACM 1967 p. 243 98 | Updated for rounding errors based on remark in 99 | ACM TOMS June 1985, page 185 100 | **/ 101 | static double pochisq(double x, int df) 102 | { 103 | if (x <= 0.0 || df < 1) 104 | return 1.0; 105 | 106 | double a = 0.5 * x; 107 | bool even = !(df & 1); /* True if df is an even number */ 108 | double y = 0; 109 | if (df > 1) { 110 | y = std::exp(-a); 111 | } 112 | double s = (even ? y : (2.0 * poz(-std::sqrt(x)))); 113 | if (df > 2) { 114 | x = 0.5 * (df - 1.0); 115 | double z = (even ? 1.0 : 0.5); 116 | if (a > EXP_MAX_A_VALUE()) 117 | { 118 | double e = (even ? 0.0 : LOG_SQRT_PI); 119 | double c = std::log(a); 120 | while (z <= x) 121 | { 122 | e = std::log(z)+e; 123 | s += std::exp(c*z-a-e); 124 | z += 1.0; 125 | } 126 | return s; 127 | } 128 | else 129 | { 130 | double e = (even ? 1.0 : (I_SQRT_PI / std::sqrt(a))); 131 | double c = 0.0; 132 | while (z <= x) 133 | { 134 | e = e*(a/z); 135 | c = c+e; 136 | z += 1.0; 137 | } 138 | return c*y+s; 139 | } 140 | } 141 | else return s; 142 | } 143 | 144 | //! Compute critical chi-square value toproduce given p. 145 | /** We just do a bisection search for a value within CHI_EPSILON, 146 | relying on the monotonicity of pochisq(). 147 | **/ 148 | static double critchi(double p, int df) 149 | { 150 | double CHI_EPSILON = 0.000001; /* Accuracy of critchi approximation */ 151 | double CHI_MAX = 99999.0; /* Maximum chi-square value */ 152 | double minchisq = 0.0; 153 | double maxchisq = CHI_MAX; 154 | double chisqval; 155 | 156 | if (p <= 0.0) 157 | return maxchisq; 158 | else if (p >= 1.0) 159 | return 0.0; 160 | 161 | chisqval = df / std::sqrt(p); /* fair first value */ 162 | while ((maxchisq - minchisq) > CHI_EPSILON) 163 | { 164 | if (pochisq(chisqval, df) < p) 165 | maxchisq = chisqval; 166 | else 167 | minchisq = chisqval; 168 | 169 | chisqval = (maxchisq + minchisq) * 0.5; 170 | } 171 | 172 | return chisqval; 173 | } 174 | 175 | }; 176 | 177 | #endif //CHI2_HELPER_HEADER 178 | -------------------------------------------------------------------------------- /src/DgmOctreeReferenceCloud.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #include "DgmOctreeReferenceCloud.h" 5 | 6 | using namespace CCCoreLib; 7 | 8 | DgmOctreeReferenceCloud::DgmOctreeReferenceCloud(DgmOctree::NeighboursSet* associatedSet, unsigned size/*=0*/) 9 | : m_globalIterator(0) 10 | , m_validBB(false) 11 | , m_set(associatedSet) 12 | , m_size(size == 0 && associatedSet ? static_cast(m_set->size()) : size) 13 | { 14 | assert(associatedSet); 15 | } 16 | 17 | void DgmOctreeReferenceCloud::computeBB() 18 | { 19 | unsigned count = size(); 20 | if (count == 0) 21 | { 22 | //empty cloud?! 23 | m_bbMin = m_bbMax = CCVector3(0, 0, 0); 24 | return; 25 | } 26 | 27 | //initialize BBox with first point 28 | m_bbMin = m_bbMax = *m_set->at(0).point; 29 | 30 | for (unsigned i = 1; i < count; ++i) 31 | { 32 | const CCVector3& P = *m_set->at(i).point; 33 | //X boundaries 34 | if (m_bbMin.x > P.x) 35 | m_bbMin.x = P.x; 36 | else if (m_bbMax.x < P.x) 37 | m_bbMax.x = P.x; 38 | 39 | //Y boundaries 40 | if (m_bbMin.y > P.y) 41 | m_bbMin.y = P.y; 42 | else if (m_bbMax.y < P.y) 43 | m_bbMax.y = P.y; 44 | 45 | //Z boundaries 46 | if (m_bbMin.z > P.z) 47 | m_bbMin.z = P.z; 48 | else if (m_bbMax.z < P.z) 49 | m_bbMax.z = P.z; 50 | } 51 | 52 | m_validBB = true; 53 | } 54 | 55 | void DgmOctreeReferenceCloud::getBoundingBox(CCVector3& bbMin, CCVector3& bbMax) 56 | { 57 | if (!m_validBB) 58 | { 59 | computeBB(); 60 | } 61 | 62 | bbMin = m_bbMin; 63 | bbMax = m_bbMax; 64 | } 65 | 66 | void DgmOctreeReferenceCloud::forEach(genericPointAction action) 67 | { 68 | unsigned count = size(); 69 | for (unsigned i = 0; i < count; ++i) 70 | { 71 | //we must change from double container to 'ScalarType' one! 72 | ScalarType sqDist = static_cast(m_set->at(i).squareDistd); 73 | action(*m_set->at(i).point, sqDist); 74 | m_set->at(i).squareDistd = static_cast(sqDist); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/ErrorFunction.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #include "ErrorFunction.h" 5 | 6 | //local 7 | #include "CCConst.h" 8 | 9 | 10 | using namespace CCCoreLib; 11 | 12 | /************************** 13 | * erf.cpp 14 | * author: Steve Strand 15 | * written: 29-Jan-04 16 | * modified: Jan-06 (DGM) 17 | ***************************/ 18 | 19 | double ErrorFunction::erf(double x) 20 | //erf(x) = 2/sqrt(pi)*integral(exp(-t^2),t,0,x) 21 | // = 2/sqrt(pi)*[x - x^3/3 + x^5/5*2! - x^7/7*3! + ...] 22 | // = 1-erfc(x) 23 | { 24 | static const double two_sqrtpi = 1.128379167095512574; // 2/sqrt(pi) 25 | 26 | if (std::abs(x) > 2.2) 27 | return 1.0 - erfc(x); //use continued fraction when std::abs(x) > 2.2 28 | 29 | double sum = x; 30 | double term = x; 31 | double xsqr = x * x; 32 | int j = 1; 33 | 34 | do 35 | { 36 | term *= xsqr / j; 37 | sum -= term / (2 * j + 1); 38 | ++j; 39 | term *= xsqr / j; 40 | sum += term / (2 * j + 1); 41 | ++j; 42 | } 43 | while (std::abs(term/sum) > c_erfRelativeError); 44 | 45 | return two_sqrtpi * sum; 46 | } 47 | 48 | static const double one_sqrtpi = 1.0 / sqrt(M_PI); 49 | 50 | double ErrorFunction::erfc(double x) 51 | //erfc(x) = 2/sqrt(pi)*integral(exp(-t^2),t,x,inf) 52 | // = exp(-x^2)/sqrt(pi) * [1/x+ (1/2)/x+ (2/2)/x+ (3/2)/x+ (4/2)/x+ ...] 53 | // = 1-erf(x) 54 | //expression inside [] is a continued fraction so '+' means add to denominator only 55 | { 56 | if (std::abs(x) < 2.2) 57 | return erfc(x); //use series when std::abs(x) < 2.2 58 | 59 | if (x < 1.0e-12) //continued fraction only valid for x>0 60 | return 2.0 - erfc(-x); 61 | 62 | //last two convergent numerators 63 | double a = 1; 64 | double b = x; 65 | 66 | //last two convergent denominators 67 | double c = x; 68 | double d = x * x + 0.5; 69 | 70 | //last two convergents (a/c and b/d) 71 | double q1; 72 | double q2 = b / d; 73 | 74 | double n = 1.0; 75 | 76 | do 77 | { 78 | double t = a * n + b * x; 79 | a = b; 80 | b = t; 81 | t = c * n + d * x; 82 | c = d; 83 | d = t; 84 | n += 0.5; 85 | q1 = q2; 86 | q2 = b / d; 87 | } 88 | while (std::abs(q1 - q2) / q2 > c_erfRelativeError); 89 | 90 | return one_sqrtpi * exp(-x * x)*q2; 91 | } 92 | -------------------------------------------------------------------------------- /src/FastMarchingForPropagation.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #include "FastMarchingForPropagation.h" 5 | 6 | //local 7 | #include "DgmOctree.h" 8 | #include "ReferenceCloud.h" 9 | #include "ScalarFieldTools.h" 10 | 11 | 12 | using namespace CCCoreLib; 13 | 14 | FastMarchingForPropagation::FastMarchingForPropagation() 15 | : FastMarching() 16 | , m_jumpCoef(0) //resistance a l'avancement du front, en fonction de Cell->f (ici, pas de resistance) 17 | , m_detectionThreshold(Cell::T_INF()) //saut relatif de la valeur d'arrivee qui arrete la propagation (ici, "desactive") 18 | { 19 | } 20 | 21 | int FastMarchingForPropagation::init( GenericCloud* theCloud, 22 | DgmOctree* theOctree, 23 | unsigned char level, 24 | bool constantAcceleration/*=false*/) 25 | { 26 | assert(theCloud && theOctree); 27 | 28 | int result = initGridWithOctree(theOctree,level); 29 | if (result < 0) 30 | return result; 31 | 32 | //on remplit la grille 33 | DgmOctree::cellCodesContainer cellCodes; 34 | theOctree->getCellCodes(level,cellCodes,true); 35 | 36 | ReferenceCloud Yk(theOctree->associatedCloud()); 37 | 38 | while (!cellCodes.empty()) 39 | { 40 | if (!theOctree->getPointsInCell(cellCodes.back(),level,&Yk,true)) 41 | { 42 | //not enough memory? 43 | return -1; 44 | } 45 | 46 | //on transforme le code de cellule en position 47 | Tuple3i cellPos; 48 | theOctree->getCellPos(cellCodes.back(),level,cellPos,true); 49 | 50 | //on renseigne la grille 51 | unsigned gridPos = pos2index(cellPos); 52 | 53 | PropagationCell* aCell = new PropagationCell; 54 | aCell->cellCode = cellCodes.back(); 55 | aCell->f = (constantAcceleration ? 1.0f : static_cast(ScalarFieldTools::computeMeanScalarValue(&Yk))); 56 | 57 | m_theGrid[gridPos] = aCell; 58 | 59 | cellCodes.pop_back(); 60 | } 61 | 62 | m_initialized = true; 63 | 64 | return 0; 65 | } 66 | 67 | int FastMarchingForPropagation::step() 68 | { 69 | if (!m_initialized) 70 | return -1; 71 | 72 | unsigned minTCellIndex = getNearestTrialCell(); 73 | if (minTCellIndex == 0) 74 | { 75 | //fl_alert("No more trial cells !"); 76 | return 0; 77 | } 78 | 79 | Cell* minTCell = m_theGrid[minTCellIndex]; 80 | assert(minTCell != nullptr); 81 | 82 | //last arrival time 83 | float lastT = (m_activeCells.empty() ? 0 : m_theGrid[m_activeCells.back()]->T); 84 | 85 | if (minTCell->T-lastT > m_detectionThreshold * m_cellSize) 86 | { 87 | //reset(); 88 | return 0; 89 | } 90 | 91 | assert(minTCell->state != Cell::ACTIVE_CELL); 92 | 93 | if (minTCell->T < Cell::T_INF()) 94 | { 95 | //we add this cell to the "ACTIVE" set 96 | addActiveCell(minTCellIndex); 97 | 98 | assert(minTCell->T >= lastT); 99 | 100 | //add its neighbors to the TRIAL set 101 | Cell* nCell; 102 | for (unsigned i=0;istate == Cell::FAR_CELL) 111 | { 112 | nCell->T = computeT(nIndex); 113 | addTrialCell(nIndex); 114 | } 115 | else if (nCell->state == Cell::TRIAL_CELL) 116 | //otherwise we must update it's arrival time 117 | { 118 | float t_old = nCell->T; 119 | float t_new = computeT(nIndex); 120 | 121 | if (t_new < t_old) 122 | nCell->T = t_new; 123 | } 124 | } 125 | } 126 | } 127 | else 128 | { 129 | addIgnoredCell(minTCellIndex); 130 | } 131 | 132 | return 1; 133 | } 134 | 135 | int FastMarchingForPropagation::propagate() 136 | { 137 | initTrialCells(); 138 | 139 | int result = 1; 140 | while (result > 0) 141 | { 142 | result = step(); 143 | } 144 | 145 | return result; 146 | } 147 | 148 | bool FastMarchingForPropagation::extractPropagatedPoints(ReferenceCloud* points) 149 | { 150 | if (!m_initialized || !m_octree || m_gridLevel > DgmOctree::MAX_OCTREE_LEVEL || !points) 151 | return false; 152 | 153 | points->clear(); 154 | 155 | for (unsigned int activeCellIndex : m_activeCells) 156 | { 157 | PropagationCell* aCell = static_cast(m_theGrid[activeCellIndex]); 158 | 159 | if (!m_octree->getPointsInCell(aCell->cellCode, m_gridLevel, points, true, false)) 160 | return false; 161 | } 162 | 163 | //raz de la norme du gradient du point, pour qu'il ne soit plus pris en compte par la suite ! 164 | points->placeIteratorAtBeginning(); 165 | for (unsigned k = 0; k < points->size(); ++k) 166 | { 167 | points->setCurrentPointScalarValue(NAN_VALUE); 168 | points->forwardIterator(); 169 | } 170 | 171 | return true; 172 | } 173 | 174 | bool FastMarchingForPropagation::setPropagationTimingsAsDistances() 175 | { 176 | if (!m_initialized || !m_octree || m_gridLevel > DgmOctree::MAX_OCTREE_LEVEL) 177 | return false; 178 | 179 | ReferenceCloud Yk(m_octree->associatedCloud()); 180 | 181 | for (unsigned int activeCellIndex : m_activeCells) 182 | { 183 | PropagationCell* aCell = static_cast(m_theGrid[activeCellIndex]); 184 | 185 | if (!m_octree->getPointsInCell(aCell->cellCode,m_gridLevel,&Yk,true)) 186 | { 187 | //not enough memory? 188 | return false; 189 | } 190 | 191 | Yk.placeIteratorAtBeginning(); 192 | for (unsigned k=0; kT); 195 | Yk.forwardIterator(); 196 | } 197 | //Yk.clear(); //useless 198 | } 199 | 200 | return true; 201 | } 202 | 203 | #ifdef _MSC_VER 204 | //Visual 2012 (and previous versions) don't know expm1 205 | #if _MSC_VER <= 1700 206 | 207 | // Compute exp(x) - 1 without loss of precision for small values of x. 208 | template T expm1(T x) 209 | { 210 | if (std::abs(x) < 1e-5) 211 | return x + (x*x)/2; 212 | else 213 | return exp(x) - 1; 214 | } 215 | 216 | #endif 217 | #endif 218 | 219 | float FastMarchingForPropagation::computeTCoefApprox(Cell* currentCell, Cell* neighbourCell) const 220 | { 221 | PropagationCell* cCell = static_cast(currentCell); 222 | PropagationCell* nCell = static_cast(neighbourCell); 223 | return static_cast(expm1(m_jumpCoef * static_cast(cCell->f - nCell->f))); 224 | } 225 | 226 | void FastMarchingForPropagation::findPeaks() 227 | { 228 | if (!m_initialized) 229 | return; 230 | 231 | //on fait bien attention a ne pas initialiser les cellules sur les bords 232 | for (unsigned k=0; k(k) }; 235 | 236 | for (unsigned j=0; j(j); 239 | 240 | for (unsigned i=0; i(i); 243 | 244 | unsigned index = static_cast(pos[0]+1) 245 | + static_cast(pos[1]+1) * m_rowSize 246 | + static_cast(pos[2]+1) * m_sliceSize; 247 | 248 | PropagationCell* theCell = reinterpret_cast(m_theGrid[index]); 249 | 250 | if (theCell) 251 | { 252 | bool isMin = true; 253 | bool isMax = true; 254 | 255 | //theCell->state = ACTIVE_CELL; 256 | 257 | for (int n : m_neighboursIndexShift) 258 | { 259 | const PropagationCell* nCell = reinterpret_cast(m_theGrid[index+n]); 260 | if (nCell) 261 | { 262 | if (nCell->f > theCell->f) 263 | isMax = false; 264 | else if (nCell->f < theCell->f) 265 | isMin = false; 266 | } 267 | } 268 | 269 | if (isMin != isMax) 270 | { 271 | //if (isMin) 272 | // theCell->T = 1.0; 273 | //else 274 | // theCell->T = 2.0; 275 | 276 | if (isMax) 277 | { 278 | theCell->T = 0; 279 | addActiveCell(index); 280 | } 281 | } 282 | //else theCell->T = 0; 283 | } 284 | } 285 | } 286 | } 287 | } 288 | -------------------------------------------------------------------------------- /src/LocalModel.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #include "LocalModel.h" 5 | 6 | //local 7 | #include "DistanceComputationTools.h" 8 | #include "GenericMesh.h" 9 | #include "GenericTriangle.h" 10 | 11 | 12 | using namespace CCCoreLib; 13 | 14 | //! Least Squares Best Fitting Plane "local modelization" 15 | class LSLocalModel : public LocalModel 16 | { 17 | public: 18 | 19 | //! Constructor 20 | LSLocalModel(const PointCoordinateType eq[4], const CCVector3 ¢er, PointCoordinateType squaredRadius) 21 | : LocalModel(center, squaredRadius) 22 | { 23 | memcpy(m_eq, eq, sizeof(PointCoordinateType) * 4); 24 | } 25 | 26 | //inherited from LocalModel 27 | LOCAL_MODEL_TYPES getType() const override { return LS; } 28 | 29 | //inherited from LocalModel 30 | ScalarType computeDistanceFromModelToPoint(const CCVector3* P, CCVector3* nearestPoint = nullptr) const override 31 | { 32 | ScalarType dist = DistanceComputationTools::computePoint2PlaneDistance(P, m_eq); 33 | 34 | if (nearestPoint) 35 | { 36 | *nearestPoint = *P - static_cast(dist) * CCVector3(m_eq); 37 | } 38 | 39 | return std::abs(dist); 40 | } 41 | 42 | protected: 43 | 44 | //! Plane equation 45 | PointCoordinateType m_eq[4]; 46 | }; 47 | 48 | //! Delaunay 2D1/2 "local modelization" 49 | class DelaunayLocalModel : public LocalModel 50 | { 51 | public: 52 | 53 | //! Constructor 54 | DelaunayLocalModel(GenericMesh* tri, const CCVector3 ¢er, PointCoordinateType squaredRadius) 55 | : LocalModel(center, squaredRadius) 56 | , m_tri(tri) 57 | { 58 | assert(tri); 59 | } 60 | 61 | //! Destructor 62 | ~DelaunayLocalModel() override { delete m_tri; } 63 | 64 | //inherited from LocalModel 65 | LOCAL_MODEL_TYPES getType() const override { return TRI; } 66 | 67 | //inherited from LocalModel 68 | ScalarType computeDistanceFromModelToPoint(const CCVector3* P, CCVector3* nearestPoint = nullptr) const override 69 | { 70 | ScalarType minDist2 = NAN_VALUE; 71 | if (m_tri) 72 | { 73 | m_tri->placeIteratorAtBeginning(); 74 | unsigned numberOfTriangles = m_tri->size(); 75 | CCVector3 triNearestPoint; 76 | for (unsigned i = 0; i < numberOfTriangles; ++i) 77 | { 78 | GenericTriangle* tri = m_tri->_getNextTriangle(); 79 | ScalarType dist2 = DistanceComputationTools::computePoint2TriangleDistance(P, tri, false, nearestPoint ? &triNearestPoint : nullptr); 80 | if (dist2 < minDist2 || i == 0) 81 | { 82 | //keep track of the smallest distance 83 | minDist2 = dist2; 84 | if (nearestPoint) 85 | *nearestPoint = triNearestPoint; 86 | } 87 | } 88 | } 89 | 90 | //there should be at least one triangle! 91 | assert(minDist2 == minDist2); 92 | 93 | return sqrt(minDist2); 94 | } 95 | 96 | protected: 97 | 98 | //! Associated triangulation 99 | GenericMesh* m_tri; 100 | }; 101 | 102 | //! Quadric "local modelization" 103 | /** Former 'Height Function' model. 104 | **/ 105 | class QuadricLocalModel : public LocalModel 106 | { 107 | public: 108 | 109 | //! Constructor 110 | QuadricLocalModel( const PointCoordinateType eq[6], 111 | const SquareMatrix& localOrientation, 112 | const CCVector3& gravityCenter, 113 | const CCVector3 ¢er, 114 | PointCoordinateType squaredRadius ) 115 | : LocalModel(center, squaredRadius) 116 | , m_localOrientation(localOrientation) 117 | , m_gravityCenter(gravityCenter) 118 | { 119 | m_localOrientationInv = m_localOrientation.inv(); 120 | memcpy(m_eq, eq, sizeof(PointCoordinateType) * 6); 121 | } 122 | 123 | //inherited from LocalModel 124 | LOCAL_MODEL_TYPES getType() const override { return QUADRIC; } 125 | 126 | //inherited from LocalModel 127 | ScalarType computeDistanceFromModelToPoint(const CCVector3* _P, CCVector3* nearestPoint = nullptr) const override 128 | { 129 | CCVector3 Plocal = m_localOrientation * (*_P - m_gravityCenter); 130 | 131 | PointCoordinateType z = m_eq[0] 132 | + m_eq[1] * Plocal.x 133 | + m_eq[2] * Plocal.y 134 | + m_eq[3] * Plocal.x * Plocal.x 135 | + m_eq[4] * Plocal.x * Plocal.y 136 | + m_eq[5] * Plocal.y * Plocal.y; 137 | 138 | if (nearestPoint) 139 | { 140 | *nearestPoint = m_localOrientationInv * CCVector3(Plocal.x, Plocal.y, z); 141 | } 142 | 143 | return static_cast(std::abs(Plocal.z - z)); 144 | } 145 | 146 | protected: 147 | 148 | //! Quadric equation 149 | PointCoordinateType m_eq[6]; 150 | //! Quadric local 'orientation' system 151 | SquareMatrix m_localOrientation; 152 | //! Quadric local 'orientation' system (inverse) 153 | SquareMatrix m_localOrientationInv; 154 | //! Model gravity center 155 | CCVector3 m_gravityCenter; 156 | 157 | }; 158 | 159 | LocalModel::LocalModel(const CCVector3 ¢er, PointCoordinateType squaredRadius) 160 | : m_modelCenter(center) 161 | , m_squaredRadius(squaredRadius) 162 | {} 163 | 164 | LocalModel* LocalModel::New(LOCAL_MODEL_TYPES type, 165 | Neighbourhood& subset, 166 | const CCVector3 ¢er, 167 | PointCoordinateType squaredRadius) 168 | { 169 | switch (type) 170 | { 171 | case NO_MODEL: 172 | assert(false); 173 | break; 174 | 175 | case LS: 176 | { 177 | const PointCoordinateType* lsPlane = subset.getLSPlane(); 178 | if (lsPlane) 179 | { 180 | return new LSLocalModel(lsPlane, center, squaredRadius); 181 | } 182 | } 183 | break; 184 | 185 | case TRI: 186 | { 187 | std::string errorStr; 188 | 189 | GenericMesh* tri = subset.triangulateOnPlane( Neighbourhood::DUPLICATE_VERTICES, 190 | Neighbourhood::IGNORE_MAX_EDGE_LENGTH, 191 | errorStr ); //'subset' is potentially associated to a volatile ReferenceCloud, so we must duplicate vertices! 192 | if (tri) 193 | { 194 | return new DelaunayLocalModel(tri, center, squaredRadius); 195 | } 196 | } 197 | break; 198 | 199 | case QUADRIC: 200 | { 201 | SquareMatrix toLocalCS; 202 | const PointCoordinateType* eq = subset.getQuadric(&toLocalCS); 203 | if (eq) 204 | { 205 | return new QuadricLocalModel( eq, 206 | toLocalCS, 207 | *subset.getGravityCenter(), //should be ok as the quadric computation succeeded! 208 | center, 209 | squaredRadius ); 210 | } 211 | } 212 | break; 213 | } 214 | 215 | //invalid input type or computation failed! 216 | return nullptr; 217 | } 218 | -------------------------------------------------------------------------------- /src/NormalDistribution.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #ifdef _MSC_VER 5 | //To get rid of the really annoying warnings about unsafe methods 6 | #pragma warning( disable: 4996 ) 7 | #endif 8 | 9 | #include 10 | 11 | //local 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | 19 | using namespace CCCoreLib; 20 | 21 | NormalDistribution::NormalDistribution() 22 | : GenericDistribution() 23 | , m_mu(0) 24 | , m_sigma2(0) 25 | , m_qFactor(0) 26 | , m_normFactor(0) 27 | { 28 | } 29 | 30 | NormalDistribution::NormalDistribution(ScalarType mu, ScalarType sigma2) 31 | { 32 | setParameters(mu, sigma2); 33 | } 34 | 35 | bool NormalDistribution::getParameters(ScalarType &mu, ScalarType &sigma2) const 36 | { 37 | mu = m_mu; 38 | sigma2 = m_sigma2; 39 | 40 | return isValid(); 41 | } 42 | 43 | bool NormalDistribution::setParameters(ScalarType mu, ScalarType sigma2) 44 | { 45 | m_mu = mu; 46 | m_sigma2 = sigma2; 47 | 48 | //update Chi2 data 49 | m_chi2ClassesPositions.resize(0); 50 | m_Pi.resize(0); 51 | 52 | if (m_sigma2 >= 0) 53 | { 54 | setValid(true); 55 | m_qFactor = 1.0 / (2.0 * m_sigma2); 56 | m_normFactor = 1.0 / sqrt(2.0 * M_PI * m_sigma2); 57 | } 58 | else 59 | { 60 | setValid(false); 61 | m_qFactor = 1.0; 62 | m_normFactor = 1.0; 63 | } 64 | 65 | return isValid(); 66 | } 67 | 68 | double NormalDistribution::computeP(ScalarType x) const 69 | { 70 | double p = static_cast(x - m_mu); 71 | return exp(-p*p*m_qFactor)*m_normFactor; 72 | } 73 | 74 | double NormalDistribution::computeP(ScalarType x1, ScalarType x2) const 75 | { 76 | return 0.5 * (ErrorFunction::erf(static_cast(x2 - m_mu) / sqrt(2 * m_sigma2)) 77 | - ErrorFunction::erf(static_cast(x1 - m_mu) / sqrt(2 * m_sigma2))); 78 | } 79 | 80 | double NormalDistribution::computePfromZero(ScalarType x) const 81 | { 82 | return 0.5 * (ErrorFunction::erf(static_cast(x - m_mu) / sqrt(2 * m_sigma2)) + 1.0); 83 | } 84 | 85 | bool NormalDistribution::computeParameters(const GenericCloud* cloud) 86 | { 87 | setValid(false); 88 | 89 | double mean = 0.0; 90 | double stddev2 = 0.0; 91 | unsigned counter = 0; 92 | 93 | unsigned n = cloud->size(); 94 | for (unsigned i = 0; i < n; ++i) 95 | { 96 | ScalarType v = cloud->getPointScalarValue(i); 97 | if (ScalarField::ValidValue(v)) 98 | { 99 | mean += v; 100 | stddev2 += static_cast(v) * v; 101 | ++counter; 102 | } 103 | } 104 | 105 | if (counter == 0) 106 | { 107 | return false; 108 | } 109 | 110 | mean /= counter; 111 | stddev2 = std::abs(stddev2 / counter - mean*mean); 112 | 113 | return setParameters(static_cast(mean), static_cast(stddev2)); 114 | } 115 | 116 | bool NormalDistribution::computeParameters(const ScalarContainer& values) 117 | { 118 | setValid(false); 119 | 120 | //compute mean and std. dev. 121 | double mean = 0.0; 122 | double stddev2 = 0.0; 123 | unsigned counter = 0; 124 | 125 | for (size_t i = 0; i< values.size(); ++i) 126 | { 127 | ScalarType v = values.getValue(i); 128 | if (ScalarField::ValidValue(v)) 129 | { 130 | mean += v; 131 | stddev2 += static_cast(v) * v; 132 | ++counter; 133 | } 134 | } 135 | 136 | if (counter == 0) 137 | { 138 | return false; 139 | } 140 | 141 | mean /= counter; 142 | stddev2 = std::abs(stddev2 / counter - mean*mean); 143 | 144 | return setParameters(static_cast(mean), static_cast(stddev2)); 145 | } 146 | 147 | bool NormalDistribution::computeRobustParameters(const ScalarContainer& values, double nSigma) 148 | { 149 | if (!computeParameters(values)) 150 | return false; 151 | 152 | //max std. deviation 153 | const double maxStddev = sqrt(static_cast(m_sigma2))*nSigma; 154 | 155 | unsigned counter = 0; 156 | double mean = 0.0; 157 | double stddev2 = 0.0; 158 | 159 | for (size_t i = 0; i < values.size(); ++i) 160 | { 161 | ScalarType v = values.getValue(i); 162 | if (static_cast(std::abs(v - m_mu)) < maxStddev) 163 | { 164 | mean += v; 165 | stddev2 += static_cast(v) * v; 166 | ++counter; 167 | } 168 | } 169 | 170 | if (counter == 0) 171 | { 172 | return false; 173 | } 174 | 175 | mean /= counter; 176 | stddev2 = std::abs(stddev2 / counter - mean*mean); 177 | 178 | return setParameters(static_cast(mean), static_cast(stddev2)); 179 | } 180 | 181 | double NormalDistribution::computeChi2Dist(const GenericCloud* cloud, unsigned numberOfClasses, int* histo) 182 | { 183 | assert(cloud); 184 | 185 | unsigned n = cloud->size(); 186 | 187 | //we must refine the real number of elements 188 | unsigned numberOfElements = ScalarFieldTools::countScalarFieldValidValues(cloud); 189 | 190 | if (numberOfElements == 0) 191 | return -1.0; 192 | 193 | if (numberOfClasses < 1 || numberOfClasses*numberOfClasses > numberOfElements) 194 | return -1.0; 195 | else if (numberOfClasses == 1) 196 | return 0.0; 197 | 198 | if (!setChi2ClassesPositions(numberOfClasses)) 199 | return -1.0; 200 | 201 | assert(m_Pi.size() == numberOfClasses); 202 | 203 | int* _histo = histo; 204 | if (!_histo) 205 | _histo = new int[numberOfClasses]; 206 | if (!_histo) 207 | return -1.0; 208 | 209 | memset(_histo, 0, numberOfClasses*sizeof(int)); 210 | 211 | //histogram computation 212 | for (unsigned i = 0; i < n; ++i) 213 | { 214 | ScalarType V = cloud->getPointScalarValue(i); 215 | if (ScalarField::ValidValue(V)) 216 | { 217 | unsigned j = 0; 218 | for (; j < numberOfClasses - 1; ++j) 219 | if (V < m_chi2ClassesPositions[j]) 220 | break; 221 | 222 | ++_histo[j]; 223 | } 224 | } 225 | 226 | //calcul de la distance du Chi2 227 | double dk = 0.0; 228 | { 229 | for (unsigned i = 0; i < numberOfClasses; ++i) 230 | { 231 | double nPi = static_cast(m_Pi[i]) * numberOfElements; 232 | double tempValue = static_cast(_histo[i]) - nPi; 233 | dk += tempValue*tempValue / nPi; 234 | } 235 | } 236 | 237 | if (_histo && !histo) 238 | delete[] _histo; 239 | _histo = nullptr; 240 | 241 | return dk; 242 | } 243 | 244 | bool NormalDistribution::setChi2ClassesPositions(unsigned numberOfClasses) 245 | { 246 | m_chi2ClassesPositions.resize(0); 247 | m_Pi.resize(0); 248 | 249 | if (!isValid() || numberOfClasses < 2) 250 | return false; 251 | 252 | try 253 | { 254 | m_Pi.reserve(numberOfClasses); 255 | m_chi2ClassesPositions.reserve(numberOfClasses - 1); 256 | } 257 | catch (const std::bad_alloc&) 258 | { 259 | //not engouh memory 260 | return false; 261 | } 262 | 263 | //simplest case 264 | if (numberOfClasses == 2) 265 | { 266 | m_Pi.push_back(0.5); 267 | m_chi2ClassesPositions.push_back(m_mu); 268 | m_Pi.push_back(0.5); 269 | } 270 | else //general case: numberOfClasses>2 271 | { 272 | ScalarType sigma = sqrt(m_sigma2); 273 | //1st class between -inf and mu-2.sigma 274 | ScalarType x = m_mu - 2 * sigma; 275 | ScalarType y = static_cast(computePfromZero(x)); 276 | m_Pi.push_back(y); 277 | m_chi2ClassesPositions.push_back(x); 278 | 279 | //numberOfClasses-2 classes between mu-2.sigma and mu+2.sigma 280 | ScalarType pas = 4 * sigma / (numberOfClasses - 2); 281 | for (unsigned i = 0; i < numberOfClasses - 2; ++i) 282 | { 283 | x = x + pas; 284 | ScalarType oldy = y; 285 | y = static_cast(computePfromZero(x)); 286 | m_Pi.push_back(y - oldy); 287 | m_chi2ClassesPositions.push_back(x); 288 | } 289 | 290 | //last class between mu+2.sigma and +inf 291 | //x = m_mu + 2 * sigma; 292 | y = 1 - y; 293 | m_Pi.push_back(y); 294 | } 295 | 296 | return true; 297 | } 298 | -------------------------------------------------------------------------------- /src/NormalizedProgress.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #include "GenericProgressCallback.h" 5 | 6 | //system 7 | #include 8 | #include 9 | 10 | using namespace CCCoreLib; 11 | 12 | // Use a class "wrapper" to avoid having to include in header 13 | class CCCoreLib::StdMutex : public std::mutex {}; 14 | 15 | NormalizedProgress::NormalizedProgress( GenericProgressCallback* callback, 16 | unsigned totalSteps, 17 | unsigned totalPercentage/*=100*/) 18 | : m_percent(0) 19 | , m_step(1) 20 | , m_percentAdd(1.0f) 21 | , m_counter(0) 22 | , m_mutex(new StdMutex) 23 | , progressCallback(callback) 24 | { 25 | scale(totalSteps, totalPercentage); 26 | } 27 | 28 | NormalizedProgress::~NormalizedProgress() 29 | { 30 | delete m_mutex; 31 | m_mutex = nullptr; 32 | } 33 | 34 | void NormalizedProgress::scale( unsigned totalSteps, 35 | unsigned totalPercentage/*=100*/, 36 | bool updateCurrentProgress/*=false*/) 37 | { 38 | if (progressCallback) 39 | { 40 | if (totalSteps == 0 || totalPercentage == 0) 41 | { 42 | m_step = 1; 43 | m_percentAdd = 0; 44 | return; 45 | } 46 | 47 | if (totalSteps >= 2 * totalPercentage) 48 | { 49 | m_step = static_cast(ceil(static_cast(totalSteps) / totalPercentage)); 50 | assert(m_step != 0 && m_step < totalSteps); 51 | m_percentAdd = static_cast(totalPercentage) / (totalSteps / m_step); 52 | } 53 | else 54 | { 55 | m_step = 1; 56 | m_percentAdd = static_cast(totalPercentage) / totalSteps; 57 | } 58 | 59 | m_mutex->lock(); 60 | if (updateCurrentProgress) 61 | { 62 | m_percent = (static_cast(totalPercentage) / totalSteps) * m_counter; 63 | } 64 | else 65 | { 66 | m_counter = 0; 67 | } 68 | m_mutex->unlock(); 69 | } 70 | } 71 | 72 | void NormalizedProgress::reset() 73 | { 74 | m_mutex->lock(); 75 | m_percent = 0; 76 | m_counter = 0; 77 | if (progressCallback) 78 | { 79 | progressCallback->update(0); 80 | } 81 | m_mutex->unlock(); 82 | } 83 | 84 | bool NormalizedProgress::oneStep() 85 | { 86 | if (!progressCallback) 87 | { 88 | return true; 89 | } 90 | 91 | m_mutex->lock(); 92 | if ((++m_counter % m_step) == 0) 93 | { 94 | m_percent += m_percentAdd; 95 | progressCallback->update(m_percent); 96 | } 97 | m_mutex->unlock(); 98 | 99 | return !progressCallback->isCancelRequested(); 100 | } 101 | 102 | bool NormalizedProgress::steps(unsigned n) 103 | { 104 | if (!progressCallback) 105 | { 106 | return true; 107 | } 108 | 109 | m_mutex->lock(); 110 | m_counter += n; 111 | unsigned d1 = m_counter / m_step; 112 | unsigned d2 = (m_counter + n) / m_step; 113 | if (d2 != d1) 114 | { 115 | m_percent += static_cast(d2 - d1) * m_percentAdd; 116 | progressCallback->update(m_percent); 117 | } 118 | m_mutex->unlock(); 119 | 120 | return !progressCallback->isCancelRequested(); 121 | } 122 | -------------------------------------------------------------------------------- /src/Polyline.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #include "Polyline.h" 5 | 6 | using namespace CCCoreLib; 7 | 8 | Polyline::Polyline(GenericIndexedCloudPersist* associatedCloud) 9 | : ReferenceCloud(associatedCloud) 10 | , m_isClosed(false) 11 | { 12 | } 13 | 14 | void Polyline::clear(bool /*unusedParam*/) 15 | { 16 | ReferenceCloud::clear(); 17 | m_isClosed = false; 18 | } 19 | -------------------------------------------------------------------------------- /src/ReferenceCloud.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #include "ReferenceCloud.h" 5 | 6 | using namespace CCCoreLib; 7 | 8 | ReferenceCloud::ReferenceCloud(GenericIndexedCloudPersist* associatedCloud) 9 | : m_globalIterator(0) 10 | , m_theAssociatedCloud(associatedCloud) 11 | { 12 | } 13 | 14 | ReferenceCloud::ReferenceCloud(const ReferenceCloud& refCloud) 15 | : m_theIndexes(refCloud.m_theIndexes) //we don't catch any exception so that the caller of the constructor can do it! 16 | , m_globalIterator(0) 17 | , m_theAssociatedCloud(refCloud.m_theAssociatedCloud) 18 | { 19 | } 20 | 21 | void ReferenceCloud::clear(bool releaseMemory/*=false*/) 22 | { 23 | m_mutex.lock(); 24 | if (releaseMemory) 25 | m_theIndexes.resize(0); 26 | else 27 | m_theIndexes.clear(); 28 | 29 | invalidateBoundingBoxInternal(false); 30 | m_mutex.unlock(); 31 | } 32 | 33 | void ReferenceCloud::getBoundingBox(CCVector3& bbMin, CCVector3& bbMax) 34 | { 35 | m_mutex.lock(); 36 | if (!m_bbox.isValid()) 37 | { 38 | m_bbox.clear(); 39 | for (unsigned index : m_theIndexes) 40 | { 41 | m_bbox.add(*m_theAssociatedCloud->getPoint(index)); 42 | } 43 | } 44 | 45 | bbMin = m_bbox.minCorner(); 46 | bbMax = m_bbox.maxCorner(); 47 | m_mutex.unlock(); 48 | } 49 | 50 | bool ReferenceCloud::reserve(unsigned n) 51 | { 52 | m_mutex.lock(); 53 | try 54 | { 55 | m_theIndexes.reserve(n); 56 | } 57 | catch (const std::bad_alloc&) 58 | { 59 | m_mutex.unlock(); 60 | return false; 61 | } 62 | 63 | m_mutex.unlock(); 64 | return true; 65 | } 66 | 67 | bool ReferenceCloud::resize(unsigned n) 68 | { 69 | m_mutex.lock(); 70 | try 71 | { 72 | m_theIndexes.resize(n); 73 | } 74 | catch (const std::bad_alloc&) 75 | { 76 | m_mutex.unlock(); 77 | return false; 78 | } 79 | 80 | m_mutex.unlock(); 81 | return true; 82 | } 83 | 84 | const CCVector3* ReferenceCloud::getCurrentPointCoordinates() const 85 | { 86 | assert(m_theAssociatedCloud && m_globalIterator < size()); 87 | assert(m_theIndexes[m_globalIterator] < m_theAssociatedCloud->size()); 88 | return m_theAssociatedCloud->getPointPersistentPtr(m_theIndexes[m_globalIterator]); 89 | } 90 | 91 | bool ReferenceCloud::addPointIndex(unsigned globalIndex) 92 | { 93 | m_mutex.lock(); 94 | try 95 | { 96 | m_theIndexes.push_back(globalIndex); 97 | } 98 | catch (const std::bad_alloc&) 99 | { 100 | m_mutex.unlock(); 101 | return false; 102 | } 103 | invalidateBoundingBoxInternal(false); 104 | 105 | m_mutex.unlock(); 106 | return true; 107 | } 108 | 109 | bool ReferenceCloud::addPointIndex(unsigned firstIndex, unsigned lastIndex) 110 | { 111 | if (firstIndex >= lastIndex) 112 | { 113 | assert(false); 114 | return false; 115 | } 116 | 117 | unsigned range = lastIndex - firstIndex; //lastIndex is excluded 118 | 119 | m_mutex.lock(); 120 | unsigned pos = size(); 121 | if (size() < pos + range) 122 | { 123 | try 124 | { 125 | m_theIndexes.resize(pos + range); 126 | } 127 | catch (const std::bad_alloc&) 128 | { 129 | m_mutex.unlock(); 130 | return false; 131 | } 132 | } 133 | 134 | for (unsigned i = 0; i < range; ++i, ++firstIndex) 135 | { 136 | m_theIndexes[pos++] = firstIndex; 137 | } 138 | 139 | invalidateBoundingBoxInternal(false); 140 | m_mutex.unlock(); 141 | 142 | return true; 143 | } 144 | 145 | void ReferenceCloud::setPointIndex(unsigned localIndex, unsigned globalIndex) 146 | { 147 | assert(localIndex < size()); 148 | m_theIndexes[localIndex] = globalIndex; 149 | invalidateBoundingBox(); 150 | } 151 | 152 | void ReferenceCloud::forEach(genericPointAction action) 153 | { 154 | assert(m_theAssociatedCloud); 155 | 156 | unsigned count = size(); 157 | for (unsigned i = 0; i < count; ++i) 158 | { 159 | unsigned index = m_theIndexes[i]; 160 | ScalarType d = m_theAssociatedCloud->getPointScalarValue(index); 161 | action(*m_theAssociatedCloud->getPointPersistentPtr(index), d); 162 | m_theAssociatedCloud->setPointScalarValue(index, d); 163 | } 164 | } 165 | 166 | void ReferenceCloud::removePointGlobalIndex(unsigned localIndex) 167 | { 168 | m_mutex.lock(); 169 | 170 | if (localIndex < size()) 171 | { 172 | //swap the value to be removed with the last one 173 | m_theIndexes[localIndex] = m_theIndexes.back(); 174 | m_theIndexes.pop_back(); 175 | } 176 | else 177 | { 178 | assert(false); 179 | } 180 | 181 | m_mutex.unlock(); 182 | } 183 | 184 | void ReferenceCloud::setAssociatedCloud(GenericIndexedCloudPersist* cloud) 185 | { 186 | m_theAssociatedCloud = cloud; 187 | invalidateBoundingBox(); 188 | } 189 | 190 | bool ReferenceCloud::add(const ReferenceCloud& cloud) 191 | { 192 | if (!cloud.m_theAssociatedCloud || m_theAssociatedCloud != cloud.m_theAssociatedCloud) 193 | { 194 | return false; 195 | } 196 | 197 | std::size_t newCount = cloud.m_theIndexes.size(); 198 | if (newCount == 0) 199 | return true; 200 | 201 | m_mutex.lock(); 202 | 203 | //reserve memory 204 | std::size_t currentSize = size(); 205 | try 206 | { 207 | m_theIndexes.resize(currentSize + newCount); 208 | } 209 | catch (const std::bad_alloc&) 210 | { 211 | m_mutex.unlock(); 212 | return false; 213 | } 214 | 215 | //copy new indexes (warning: no duplicate check!) 216 | for (unsigned i = 0; i < newCount; ++i) 217 | { 218 | m_theIndexes[currentSize + i] = cloud.m_theIndexes[i]; 219 | } 220 | 221 | invalidateBoundingBoxInternal(false); 222 | 223 | m_mutex.unlock(); 224 | return true; 225 | } 226 | 227 | void ReferenceCloud::invalidateBoundingBoxInternal(bool threadSafe) 228 | { 229 | if (threadSafe) 230 | { 231 | m_mutex.lock(); 232 | } 233 | 234 | m_bbox.setValidity(false); 235 | 236 | if (threadSafe) 237 | { 238 | m_mutex.unlock(); 239 | } 240 | } 241 | -------------------------------------------------------------------------------- /src/ScalarField.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #include 5 | 6 | //System 7 | #include 8 | 9 | using namespace CCCoreLib; 10 | 11 | ScalarField::ScalarField(const std::string& name/*=std::string()*/) 12 | : m_name{ } 13 | , m_offset{ 0.0 } 14 | , m_offsetHasBeenSet{ false } 15 | , m_localMinVal{ 0.0f } 16 | , m_localMaxVal{ 0.0f } 17 | { 18 | setName(name); 19 | } 20 | 21 | ScalarField::ScalarField(const ScalarField& sf) 22 | : std::vector(sf) 23 | , m_name{ sf.m_name } 24 | , m_offset{ sf.m_offset } 25 | , m_offsetHasBeenSet{ sf.m_offsetHasBeenSet } 26 | , m_localMinVal{ sf.m_localMinVal } 27 | , m_localMaxVal{ sf.m_localMaxVal } 28 | { 29 | } 30 | 31 | void ScalarField::setName(const std::string& name) 32 | { 33 | if (name.empty()) 34 | { 35 | m_name = "Undefined"; 36 | } 37 | else 38 | { 39 | m_name = name; 40 | } 41 | } 42 | 43 | std::size_t ScalarField::countValidValues() const 44 | { 45 | if (false == std::isfinite(m_offset)) 46 | { 47 | // special case: if the offset is invalid, all values become invalid! 48 | return size(); 49 | } 50 | 51 | std::size_t count = 0; 52 | 53 | for (std::size_t i = 0; i < size(); ++i) 54 | { 55 | const ScalarType& val = at(i); 56 | if (ValidValue(val)) 57 | { 58 | ++count; 59 | } 60 | } 61 | 62 | return count; 63 | } 64 | 65 | void ScalarField::computeMeanAndVariance(ScalarType& mean, ScalarType* variance) const 66 | { 67 | double _mean = 0.0; 68 | double _std2 = 0.0; 69 | std::size_t count = 0; 70 | 71 | for (std::size_t i = 0; i < size(); ++i) 72 | { 73 | float val = at(i); 74 | if (std::isfinite(val)) 75 | { 76 | _mean += val; 77 | _std2 += static_cast(val) * val; 78 | ++count; 79 | } 80 | } 81 | 82 | if (count) 83 | { 84 | _mean /= count; 85 | mean = _mean; 86 | 87 | if (variance) 88 | { 89 | _std2 = std::abs(_std2 / count - _mean*_mean); 90 | *variance = static_cast(_std2); 91 | } 92 | 93 | mean += m_offset; // only after the standard deviation has been calculated! 94 | 95 | } 96 | else 97 | { 98 | mean = 0; 99 | if (variance) 100 | { 101 | *variance = 0; 102 | } 103 | } 104 | } 105 | 106 | bool ScalarField::reserveSafe(std::size_t count) 107 | { 108 | try 109 | { 110 | reserve(count); 111 | } 112 | catch (const std::bad_alloc&) 113 | { 114 | //not enough memory 115 | return false; 116 | } 117 | return true; 118 | } 119 | 120 | bool ScalarField::resizeSafe(std::size_t count, bool initNewElements/*=false*/, ScalarType valueForNewElements/*=0*/) 121 | { 122 | try 123 | { 124 | if (initNewElements && count > size()) 125 | { 126 | float fillValueF = 0.0f; 127 | if (std::isfinite(valueForNewElements)) 128 | { 129 | if (m_offsetHasBeenSet) 130 | { 131 | // use the already set offset 132 | fillValueF = static_cast(valueForNewElements - m_offset); 133 | } 134 | else // if the offset has not been set yet... 135 | { 136 | // we use the first finite value as offset by default 137 | setOffset(valueForNewElements); 138 | } 139 | } 140 | else 141 | { 142 | // special case: filling with NaN or +/-inf values 143 | fillValueF = static_cast(valueForNewElements); // NaN/-inf/+inf should be maintained 144 | } 145 | 146 | resize(count, fillValueF); 147 | } 148 | else 149 | { 150 | resize(count); 151 | } 152 | } 153 | catch (const std::bad_alloc&) 154 | { 155 | //not enough memory 156 | return false; 157 | } 158 | return true; 159 | } 160 | -------------------------------------------------------------------------------- /src/SimpleMesh.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: LGPL-2.0-or-later 2 | // Copyright © EDF R&D / TELECOM ParisTech (ENST-TSI) 3 | 4 | #include "SimpleMesh.h" 5 | 6 | //local 7 | #include "GenericIndexedCloud.h" 8 | 9 | //System 10 | #include 11 | 12 | using namespace CCCoreLib; 13 | 14 | SimpleMesh::SimpleMesh(GenericIndexedCloud* _theVertices, bool linkVerticesWithMesh) 15 | : GenericIndexedMesh() 16 | , globalIterator(0) 17 | , theVertices(_theVertices) 18 | , verticesLinked(linkVerticesWithMesh) 19 | { 20 | } 21 | 22 | SimpleMesh::~SimpleMesh() 23 | { 24 | if (theVertices && verticesLinked) 25 | { 26 | delete theVertices; 27 | theVertices = nullptr; 28 | } 29 | } 30 | 31 | void SimpleMesh::forEach(genericTriangleAction action) 32 | { 33 | SimpleTriangle tri; 34 | 35 | for (VerticesIndexes& ti : triIndexes) 36 | { 37 | theVertices->getPoint(ti.i1, tri.A); 38 | theVertices->getPoint(ti.i2, tri.B); 39 | theVertices->getPoint(ti.i3, tri.C); 40 | action(tri); 41 | } 42 | } 43 | 44 | void SimpleMesh::placeIteratorAtBeginning() 45 | { 46 | globalIterator = 0; 47 | } 48 | 49 | GenericTriangle* SimpleMesh::_getNextTriangle() 50 | { 51 | return _getTriangle(globalIterator++); 52 | } 53 | 54 | GenericTriangle* SimpleMesh::_getTriangle(unsigned triangleIndex) 55 | { 56 | assert(triangleIndex < triIndexes.size()); 57 | 58 | const VerticesIndexes& ti = triIndexes[triangleIndex]; 59 | theVertices->getPoint(ti.i1, dummyTriangle.A); 60 | theVertices->getPoint(ti.i2, dummyTriangle.B); 61 | theVertices->getPoint(ti.i3, dummyTriangle.C); 62 | 63 | return &dummyTriangle; //temporary! 64 | } 65 | 66 | void SimpleMesh::getTriangleVertices(unsigned triangleIndex, CCVector3& A, CCVector3& B, CCVector3& C) const 67 | { 68 | assert(triangleIndex < triIndexes.size()); 69 | 70 | const VerticesIndexes& ti = triIndexes[triangleIndex]; 71 | theVertices->getPoint(ti.i1, A); 72 | theVertices->getPoint(ti.i2, B); 73 | theVertices->getPoint(ti.i3, C); 74 | } 75 | 76 | void SimpleMesh::getBoundingBox(CCVector3& bbMin, CCVector3& bbMax) 77 | { 78 | ////TODO: how can we know if the vertices cloud changes?! 79 | //if (!m_bbox.isValid()) 80 | //{ 81 | // m_bbox.clear(); 82 | // for (const VerticesIndexes& ti : triIndexes) 83 | // { 84 | // m_bbox.add(*theVertices->getPoint(ti.i1)); 85 | // m_bbox.add(*theVertices->getPoint(ti.i2)); 86 | // m_bbox.add(*theVertices->getPoint(ti.i3)); 87 | // } 88 | //} 89 | 90 | //bbMin = m_bbox.minCorner(); 91 | //bbMax = m_bbox.maxCorner(); 92 | 93 | return theVertices->getBoundingBox(bbMin, bbMax); 94 | } 95 | 96 | void SimpleMesh::addTriangle(unsigned i1, unsigned i2, unsigned i3) 97 | { 98 | triIndexes.push_back(VerticesIndexes(i1, i2, i3)); 99 | 100 | m_bbox.setValidity(false); 101 | } 102 | 103 | bool SimpleMesh::reserve(unsigned n) 104 | { 105 | try 106 | { 107 | triIndexes.reserve(n); 108 | } 109 | catch (const std::bad_alloc&) 110 | { 111 | return false; 112 | } 113 | return true; 114 | } 115 | 116 | bool SimpleMesh::resize(unsigned n) 117 | { 118 | try 119 | { 120 | triIndexes.resize(n); 121 | } 122 | catch (const std::bad_alloc&) 123 | { 124 | return false; 125 | } 126 | return true; 127 | } 128 | 129 | bool SimpleMesh::normalsAvailable() const 130 | { 131 | return theVertices && theVertices->normalsAvailable(); 132 | } 133 | 134 | bool SimpleMesh::interpolateNormals(unsigned triIndex, const CCVector3& P, CCVector3& N) 135 | { 136 | if (static_cast(triIndex) >= triIndexes.size()) 137 | { 138 | // index out of range 139 | assert(false); 140 | return false; 141 | } 142 | 143 | const VerticesIndexes& tri = triIndexes[triIndex]; 144 | 145 | // intepolation weights 146 | CCVector3d weights; 147 | { 148 | CCVector3 A, B, C; 149 | theVertices->getPoint(tri.i1, A); 150 | theVertices->getPoint(tri.i2, B); 151 | theVertices->getPoint(tri.i3, C); 152 | 153 | // barcyentric intepolation weights 154 | weights.x = sqrt(((P - B).cross(C - B)).norm2d())/*/2*/; 155 | weights.y = sqrt(((P - C).cross(A - C)).norm2d())/*/2*/; 156 | weights.z = sqrt(((P - A).cross(B - A)).norm2d())/*/2*/; 157 | 158 | // normalize weights 159 | double sum = weights.x + weights.y + weights.z; 160 | weights /= sum; 161 | } 162 | 163 | // interpolated normal 164 | CCVector3d Nd(0, 0, 0); 165 | { 166 | const CCVector3* N1 = theVertices->getNormal(tri.i1); 167 | Nd += N1->toDouble() * weights.u[0]; 168 | 169 | const CCVector3* N2 = theVertices->getNormal(tri.i2); 170 | Nd += N2->toDouble() * weights.u[1]; 171 | 172 | const CCVector3* N3 = theVertices->getNormal(tri.i3); 173 | Nd += N3->toDouble() * weights.u[2]; 174 | 175 | Nd.normalize(); 176 | } 177 | 178 | N = Nd.toPC(); 179 | 180 | return true; 181 | } 182 | 183 | VerticesIndexes* SimpleMesh::getTriangleVertIndexes(unsigned triangleIndex) 184 | { 185 | return &(triIndexes[triangleIndex]); 186 | } 187 | 188 | VerticesIndexes* SimpleMesh::getNextTriangleVertIndexes() 189 | { 190 | return getTriangleVertIndexes(globalIterator++); 191 | } 192 | --------------------------------------------------------------------------------