├── .github └── workflows │ └── continuous.yml ├── .gitignore ├── .travis.yml ├── CMakeLists.txt ├── LICENSE ├── README.md ├── app ├── benchmark │ ├── AABBWrapper.cpp │ ├── AABBWrapper.h │ ├── CMakeLists.txt │ ├── EnvelopeTest.cpp │ ├── EnvelopeTest.h │ ├── MeshIO.cpp │ ├── MeshIO.hpp │ ├── csv_reader.cpp │ ├── csv_reader.h │ ├── getRSS.hpp │ ├── mesh_AABB.cpp │ ├── mesh_AABB.h │ ├── sampling.cpp │ └── sampling.h ├── main.cpp ├── test.cpp └── test_data │ ├── 63465.off │ ├── 63465.stl │ ├── 63465.stl_envelope_log.csv │ ├── 63465_result.csv │ └── 63465_result.csv.json ├── bunny.jpg ├── cmake ├── CXXFeatures.cmake ├── DownloadProject.CMakeLists.cmake.in ├── DownloadProject.cmake ├── FastEnvelopeDependencies.cmake ├── FastEnvelopeDownloadExternal.cmake ├── FastEnvelopeUtils.cmake ├── FindGMP.cmake ├── FindLIBIGL.cmake ├── PrependCurrentPath.cmake ├── UseColors.cmake ├── Warnings.cmake ├── geogram.cmake └── recipes │ └── ipred.cmake ├── geogram_predicates ├── Predicates_psm.cpp ├── Predicates_psm.h └── README.txt ├── indirectPredicates ├── ip_filtered.cpp ├── ip_filtered.h ├── ip_filtered_ex.cpp └── ip_filtered_ex.h └── src ├── AABB.cpp ├── AABB.h ├── CMakeLists.txt ├── FastEnvelope.cpp ├── FastEnvelope.h ├── Logger.cpp ├── Logger.hpp ├── Morton.cpp ├── Morton.h ├── Rational.hpp ├── Types.hpp ├── common_algorithms.cpp ├── common_algorithms.h ├── external ├── CMakeLists.txt ├── Predicates.cpp ├── Predicates.hpp └── triangle_triangle_intersection.cpp ├── obb.cpp └── obb.h /.github/workflows/continuous.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | env: 12 | CTEST_OUTPUT_ON_FAILURE: ON 13 | CTEST_PARALLEL_LEVEL: 2 14 | 15 | jobs: 16 | #################### 17 | # Linux / macOS 18 | #################### 19 | 20 | Linux: 21 | name: ${{ matrix.name }} (${{ matrix.config }}), ${{ matrix.opts }} 22 | runs-on: ${{ matrix.os }} 23 | strategy: 24 | fail-fast: false 25 | matrix: 26 | os: [ubuntu-latest] 27 | config: [Debug, Release] 28 | opts: [Geogram_Predicates, Geogram_Psm_Predicates, None] 29 | include: 30 | - os: ubuntu-latest 31 | name: Linux 32 | steps: 33 | - name: Checkout repository 34 | uses: actions/checkout@v1 35 | with: 36 | fetch-depth: 10 37 | 38 | - name: Dependencies (Linux) 39 | run: | 40 | sudo apt-get update 41 | sudo apt-get -o Acquire::Retries=3 install \ 42 | libblas-dev \ 43 | libboost-filesystem-dev \ 44 | libboost-system-dev \ 45 | libboost-thread-dev \ 46 | libglu1-mesa-dev \ 47 | libsuitesparse-dev \ 48 | xorg-dev \ 49 | ccache 50 | echo 'CACHE_PATH=~/.ccache' >> "$GITHUB_ENV" 51 | 52 | - name: Cache Build 53 | id: cache-build 54 | uses: actions/cache@v2 55 | with: 56 | path: ${{ env.CACHE_PATH }} 57 | key: ${{ runner.os }}-${{ matrix.config }}-${{ matrix.opts }}-cache-${{ github.sha }} 58 | restore-keys: ${{ runner.os }}-${{ matrix.config }}-${{ matrix.opts }}-cache 59 | 60 | - name: Prepare ccache 61 | run: | 62 | ccache --max-size=1.0G 63 | ccache -V && ccache --show-stats && ccache --zero-stats 64 | 65 | - name: Configure with Geogram_Predicates 66 | if: matrix.opts == 'Geogram_Predicates' 67 | run: | 68 | mkdir -p build 69 | cd build 70 | cmake .. \ 71 | -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ 72 | -DFAST_ENVELOPE_WITH_GEOGRAM_PREDICATES=ON \ 73 | -DFAST_ENVELOPE_WITH_GEOGRAM_PSM_PREDICATES=OFF \ 74 | -DCMAKE_BUILD_TYPE=${{ matrix.config }} 75 | 76 | - name: Configure with Geogram_Psm_Predicates 77 | if: matrix.opts == 'Geogram_Psm_Predicates' 78 | run: | 79 | mkdir -p build 80 | cd build 81 | cmake .. \ 82 | -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ 83 | -DFAST_ENVELOPE_WITH_GEOGRAM_PREDICATES=OFF \ 84 | -DFAST_ENVELOPE_WITH_GEOGRAM_PSM_PREDICATES=ON \ 85 | -DCMAKE_BUILD_TYPE=${{ matrix.config }} 86 | 87 | - name: Configure without Geogram 88 | if: matrix.opts == 'None' 89 | run: | 90 | mkdir -p build 91 | cd build 92 | cmake .. \ 93 | -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ 94 | -DFAST_ENVELOPE_WITH_GEOGRAM_PREDICATES=OFF \ 95 | -DFAST_ENVELOPE_WITH_GEOGRAM_PSM_PREDICATES=OFF \ 96 | -DCMAKE_BUILD_TYPE=${{ matrix.config }} 97 | 98 | - name: Build 99 | run: cd build; make -j2; ccache --show-stats 100 | 101 | - name: Tests 102 | run: cd build; make -j2; ./FastEnvelope_test #ctest --verbose --output-on-failure 103 | 104 | #################### 105 | # macOS 106 | #################### 107 | 108 | MacOS: 109 | name: ${{ matrix.name }}-${{ matrix.arch }} (${{ matrix.config }}), ${{ matrix.opts }} 110 | runs-on: ${{ matrix.os }} 111 | strategy: 112 | fail-fast: false 113 | matrix: 114 | os: [macos-latest] 115 | config: [Debug, Release] 116 | opts: [Geogram_Predicates, Geogram_Psm_Predicates, None] 117 | arch: [x86_64, arm64] 118 | include: 119 | - os: macos-latest 120 | name: macOS 121 | steps: 122 | - name: Checkout repository 123 | uses: actions/checkout@v1 124 | with: 125 | fetch-depth: 10 126 | 127 | - name: Dependencies 128 | run: | 129 | brew install suite-sparse ccache 130 | echo 'CACHE_PATH=~/Library/Caches/ccache' >> "$GITHUB_ENV" 131 | 132 | - name: Cache Build 133 | id: cache-build 134 | uses: actions/cache@v2 135 | with: 136 | path: ${{ env.CACHE_PATH }} 137 | key: ${{ runner.os }}-${{ matrix.config }}-${{ matrix.arch }}-${{ matrix.opts }}-cache-${{ github.sha }} 138 | restore-keys: ${{ runner.os }}-${{ matrix.config }}-${{ matrix.arch }}-${{ matrix.opts }}-cache 139 | 140 | - name: Prepare ccache 141 | run: | 142 | ccache --max-size=1.0G 143 | ccache -V && ccache --show-stats && ccache --zero-stats 144 | 145 | - name: Configure with Geogram_Predicates 146 | if: matrix.opts == 'Geogram_Predicates' 147 | run: | 148 | mkdir -p build 149 | cd build 150 | cmake .. \ 151 | -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ 152 | -DFAST_ENVELOPE_WITH_GEOGRAM_PREDICATES=ON \ 153 | -DFAST_ENVELOPE_WITH_GEOGRAM_PSM_PREDICATES=OFF \ 154 | -DCMAKE_OSX_ARCHITECTURES=${{ matrix.arch }} \ 155 | -DCMAKE_BUILD_TYPE=${{ matrix.config }} 156 | 157 | - name: Configure with Geogram_Psm_Predicates 158 | if: matrix.opts == 'Geogram_Psm_Predicates' 159 | run: | 160 | mkdir -p build 161 | cd build 162 | cmake .. \ 163 | -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ 164 | -DFAST_ENVELOPE_WITH_GEOGRAM_PREDICATES=OFF \ 165 | -DFAST_ENVELOPE_WITH_GEOGRAM_PSM_PREDICATES=ON \ 166 | -DCMAKE_OSX_ARCHITECTURES=${{ matrix.arch }} \ 167 | -DCMAKE_BUILD_TYPE=${{ matrix.config }} 168 | 169 | - name: Configure without Geogram 170 | if: matrix.opts == 'None' 171 | run: | 172 | mkdir -p build 173 | cd build 174 | cmake .. \ 175 | -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ 176 | -DFAST_ENVELOPE_WITH_GEOGRAM_PREDICATES=OFF \ 177 | -DFAST_ENVELOPE_WITH_GEOGRAM_PSM_PREDICATES=OFF \ 178 | -DCMAKE_OSX_ARCHITECTURES=${{ matrix.arch }} \ 179 | -DCMAKE_BUILD_TYPE=${{ matrix.config }} 180 | 181 | - name: Build 182 | run: cd build; make -j2; ccache --show-stats 183 | 184 | - name: Tests 185 | if: matrix.arch == 'x86_64' # no ARM vm on actions yet 186 | run: cd build; make -j2; ./FastEnvelope_test #ctest --verbose --output-on-failure 187 | 188 | #################### 189 | # Windows 190 | #################### 191 | 192 | Windows: 193 | name: Windows (${{ matrix.config }}), ${{ matrix.opts }} 194 | runs-on: windows-2019 195 | env: 196 | CC: cl.exe 197 | CXX: cl.exe 198 | SCCACHE_IDLE_TIMEOUT: "12000" 199 | strategy: 200 | fail-fast: false 201 | matrix: 202 | config: [Debug, Release] 203 | opts: [Geogram_Predicates, Geogram_Psm_Predicates, None] 204 | steps: 205 | - name: Checkout repository 206 | uses: actions/checkout@v1 207 | with: 208 | fetch-depth: 10 209 | - uses: seanmiddleditch/gha-setup-ninja@master 210 | 211 | - name: Set env 212 | run: | 213 | echo "appdata=$env:LOCALAPPDATA" >> ${env:GITHUB_ENV} 214 | 215 | - name: Cache build 216 | id: cache-build 217 | uses: actions/cache@v2 218 | with: 219 | path: ${{ env.appdata }}\Mozilla\sccache 220 | key: ${{ runner.os }}-${{ matrix.config }}-${{ matrix.opts }}-cache-${{ github.sha }} 221 | restore-keys: ${{ runner.os }}-${{ matrix.config }}-${{ matrix.opts }}-cache 222 | 223 | - name: Prepare sccache 224 | run: | 225 | Invoke-Expression (New-Object System.Net.WebClient).DownloadString('https://get.scoop.sh') 226 | scoop install sccache --global --RunAsAdmin 227 | # Scoop modifies the PATH so we make it available for the next steps of the job 228 | echo "${env:PATH}" >> ${env:GITHUB_PATH} 229 | 230 | - name: Configure and run with Geogram_Predicates 231 | if: matrix.opts == 'Geogram_Predicates' 232 | shell: cmd 233 | run: | 234 | call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\Tools\VsDevCmd.bat" -arch=x64 235 | cmake -G Ninja ^ 236 | -DCMAKE_CXX_COMPILER_LAUNCHER=sccache ^ 237 | -DFAST_ENVELOPE_WITH_GEOGRAM_PREDICATES=ON ^ 238 | -DFAST_ENVELOPE_WITH_GEOGRAM_PSM_PREDICATES=OFF ^ 239 | -DCMAKE_BUILD_TYPE=${{ matrix.config }} ^ 240 | -B build ^ 241 | -S . 242 | cd build 243 | ninja -j1 244 | 245 | - name: Configure and run with Geogram_Psm_Predicates 246 | if: matrix.opts == 'Geogram_Psm_Predicates' 247 | shell: cmd 248 | run: | 249 | call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\Tools\VsDevCmd.bat" -arch=x64 250 | cmake -G Ninja ^ 251 | -DCMAKE_CXX_COMPILER_LAUNCHER=sccache ^ 252 | -DFAST_ENVELOPE_WITH_GEOGRAM_PREDICATES=OFF ^ 253 | -DFAST_ENVELOPE_WITH_GEOGRAM_PSM_PREDICATES=ON ^ 254 | -DCMAKE_BUILD_TYPE=${{ matrix.config }} ^ 255 | -B build ^ 256 | -S . 257 | cd build 258 | ninja -j1 259 | 260 | - name: Configure and run without Geogram 261 | if: matrix.opts == 'None' 262 | shell: cmd 263 | run: | 264 | call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\Tools\VsDevCmd.bat" -arch=x64 265 | cmake -G Ninja ^ 266 | -DCMAKE_CXX_COMPILER_LAUNCHER=sccache ^ 267 | -DFAST_ENVELOPE_WITH_GEOGRAM_PREDICATES=OFF ^ 268 | -DFAST_ENVELOPE_WITH_GEOGRAM_PSM_PREDICATES=OFF ^ 269 | -DCMAKE_BUILD_TYPE=${{ matrix.config }} ^ 270 | -B build ^ 271 | -S . 272 | cd build 273 | ninja -j1 274 | 275 | - name: Tests 276 | run: | 277 | cd build 278 | ./FastEnvelope_test 279 | #ctest --verbose --output-on-failure 280 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 3rdparty* 2 | *.sublime-* 3 | bin* 4 | *.code-workspace 5 | *build* 6 | .idea* 7 | *debug* 8 | .vscode -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | dist: trusty 3 | language: cpp 4 | cache: ccache 5 | addons: 6 | apt: 7 | sources: 8 | - ubuntu-toolchain-r-test 9 | packages: 10 | - g++-7 11 | - gcc-7 12 | - libglu1-mesa-dev 13 | - xorg-dev 14 | homebrew: 15 | packages: 16 | - suite-sparse 17 | - ccache 18 | matrix: 19 | include: 20 | - os: linux 21 | compiler: gcc-7 22 | env: 23 | - MATRIX_EVAL="export CC=gcc-7 && CXX=g++-7 && CONFIG=Debug" 24 | 25 | - os: linux 26 | compiler: gcc-7 27 | env: 28 | - MATRIX_EVAL="export CC=gcc-7 && CXX=g++-7 && CONFIG=Release" 29 | 30 | - os: osx 31 | osx_image: xcode10.2 32 | compiler: clang 33 | env: 34 | - MATRIX_EVAL="export CONFIG=Debug" 35 | 36 | - os: osx 37 | osx_image: xcode10.2 38 | compiler: clang 39 | env: 40 | - MATRIX_EVAL="export CONFIG=Release" 41 | 42 | install: 43 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export PATH="/usr/local/opt/ccache/libexec:$PATH"; fi 44 | - eval "${MATRIX_EVAL}" 45 | # - ccache --max-size=5.0G 46 | # - ccache -V && ccache --show-stats && ccache --zero-stats 47 | 48 | script: 49 | - mkdir build 50 | - cd build 51 | - cmake -DCMAKE_BUILD_TYPE=$CONFIG ${EXTRA_FLAGS} .. 52 | - make -j2 53 | - if [[ "$CONFIG" == "Release" ]]; then make test; fi 54 | # - ccache --show-stats 55 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | cmake_minimum_required(VERSION 3.15) 3 | project(FastEnvelope) 4 | ################################################################################ 5 | 6 | # cmake_policy(SET CMP0063 NEW) 7 | # set(CMAKE_CXX_VISIBILITY_PRESET hidden) 8 | 9 | # Detects whether this is a top-level project 10 | if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) 11 | set(FAST_ENVELOPE_TOPLEVEL_PROJECT ON) 12 | else() 13 | set(FAST_ENVELOPE_TOPLEVEL_PROJECT OFF) 14 | endif() 15 | 16 | if(INPUT_THIRD_PARTY_DIR) 17 | set(THIRD_PARTY_DIR ${CMAKE_CURRENT_SOURCE_DIR}/${INPUT_THIRD_PARTY_DIR}/) 18 | else() 19 | set(THIRD_PARTY_DIR ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/) 20 | endif() 21 | 22 | list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) 23 | list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/recipes) 24 | list(APPEND CMAKE_MODULE_PATH ${THIRD_PARTY_DIR}/Catch2/contrib) 25 | 26 | # Color output 27 | include(UseColors) 28 | 29 | # Prepend function 30 | include(PrependCurrentPath) 31 | 32 | # FastEnvelope utils 33 | include(FastEnvelopeUtils) 34 | 35 | # Extra warnings 36 | include(Warnings) 37 | 38 | # Use C++11/14 39 | include(CXXFeatures) 40 | 41 | 42 | # Sort projects inside the solution 43 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 44 | 45 | # Generate position independent code by default 46 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) 47 | 48 | ################################################################################ 49 | 50 | 51 | ################################################################################ 52 | 53 | # FastEnvelope options 54 | option(FAST_ENVELOPE_WITH_UNIT_TESTS "unit test project" ON) 55 | option(FAST_ENVELOPE_ENABLE_TBB "Enable TBB" ON) 56 | # option(FAST_ENVELOPE_USE_FLOAT "Use floats instead of double" OFF) 57 | 58 | option(FAST_ENVELOPE_WITH_GEOGRAM_PREDICATES "Use whole Geogram for predicates" ON) 59 | option(FAST_ENVELOPE_WITH_GEOGRAM_PSM_PREDICATES "Use Geogram predicates only" OFF) 60 | option(FAST_ENVELOPE_WITH_GMP "Use gmp to have rational predicates" OFF) 61 | option(FAST_ENVELOPE_WITH_TIMER "use timer in the library for debug" OFF) 62 | 63 | # Sanitizer options 64 | option(SANITIZE_ADDRESS "Sanitize Address" OFF) 65 | option(SANITIZE_MEMORY "Sanitize Memory" OFF) 66 | option(SANITIZE_THREAD "Sanitize Thread" OFF) 67 | option(SANITIZE_UNDEFINED "Sanitize Undefined" OFF) 68 | 69 | # Options for libigl modules 70 | option(LIBIGL_USE_STATIC_LIBRARY "Use libigl as static library" OFF) 71 | option(LIBIGL_WITH_ANTTWEAKBAR "Use AntTweakBar" OFF) 72 | option(LIBIGL_WITH_CGAL "Use CGAL" OFF) 73 | option(LIBIGL_WITH_COMISO "Use CoMiso" OFF) 74 | option(LIBIGL_WITH_CORK "Use Cork" OFF) 75 | option(LIBIGL_WITH_EMBREE "Use Embree" OFF) 76 | option(LIBIGL_WITH_LIM "Use LIM" OFF) 77 | option(LIBIGL_WITH_MATLAB "Use Matlab" OFF) 78 | option(LIBIGL_WITH_MOSEK "Use MOSEK" OFF) 79 | option(LIBIGL_WITH_OPENGL "Use OpenGL" OFF) 80 | option(LIBIGL_WITH_OPENGL_GLFW "Use GLFW" OFF) 81 | option(LIBIGL_WITH_OPENGL_GLFW_IMGUI "Use ImGui" OFF) 82 | option(LIBIGL_WITH_PNG "Use PNG" OFF) 83 | option(LIBIGL_WITH_PYTHON "Use Python" OFF) 84 | option(LIBIGL_WITH_TETGEN "Use Tetgen" OFF) 85 | option(LIBIGL_WITH_TRIANGLE "Use Triangle" OFF) 86 | option(LIBIGL_WITH_VIEWER "Use OpenGL viewer" OFF) 87 | option(LIBIGL_WITH_XML "Use XML" OFF) 88 | 89 | if(NOT ${FAST_ENVELOPE_WITH_GEOGRAM_PREDICATES} AND NOT ${FAST_ENVELOPE_WITH_GEOGRAM_PSM_PREDICATES}) 90 | set(LIBIGL_WITH_PREDICATES ON) 91 | else() 92 | set(LIBIGL_WITH_PREDICATES OFF) 93 | endif() 94 | 95 | 96 | 97 | # Sanitizers 98 | if(FAST_ENVELOPE_WITH_SANITIZERS) 99 | list(APPEND CMAKE_MODULE_PATH ${THIRD_PARTY_DIR}/sanitizers-cmake/cmake) 100 | endif() 101 | 102 | # Setup dependencies 103 | include(FastEnvelopeDependencies) 104 | 105 | ################################################################################ 106 | # FastEnvelope library 107 | ################################################################################ 108 | 109 | # add_library() can only be called without any source since CMake 3.11 ... 110 | add_library(${PROJECT_NAME} src/common_algorithms.cpp) 111 | target_compile_features(${PROJECT_NAME} PRIVATE ${CXX11_FEATURES}) 112 | 113 | # Public include directory for FastEnvelope 114 | target_include_directories(${PROJECT_NAME} PUBLIC ${PROJECT_BINARY_DIR}/include) 115 | # Extra warnings 116 | target_link_libraries(${PROJECT_NAME} PRIVATE warnings::all) 117 | 118 | # Use C++14 119 | target_compile_features(${PROJECT_NAME} PUBLIC ${CXX14_FEATURES}) 120 | 121 | 122 | # libigl 123 | if(FAST_ENVELOPE_TOPLEVEL_PROJECT) 124 | if(NOT TARGET igl::core) 125 | fast_envelope_download_libigl() 126 | find_package(LIBIGL REQUIRED) 127 | endif() 128 | target_link_libraries(${PROJECT_NAME} PUBLIC igl::core) 129 | if(LIBIGL_WITH_PREDICATES) 130 | target_link_libraries(${PROJECT_NAME} PUBLIC igl::predicates) 131 | endif() 132 | else() 133 | if(LIBIGL_WITH_PREDICATES) 134 | if(NOT TARGET igl::core) 135 | fast_envelope_download_libigl() 136 | find_package(LIBIGL REQUIRED) 137 | endif() 138 | target_link_libraries(${PROJECT_NAME} PUBLIC igl::core) 139 | target_link_libraries(${PROJECT_NAME} PUBLIC igl::predicates) 140 | else() 141 | # Assume eigen exists in the other lib 142 | target_link_libraries(${PROJECT_NAME} PUBLIC Eigen3::Eigen) 143 | endif() 144 | endif() 145 | 146 | if(FAST_ENVELOPE_WITH_SANITIZERS) 147 | add_sanitizers(${PROJECT_NAME}) 148 | endif() 149 | 150 | 151 | ################################################################################ 152 | # Required libraries 153 | ################################################################################ 154 | 155 | 156 | # TBB 157 | if(FAST_ENVELOPE_ENABLE_TBB AND NOT TARGET tbb::tbb) 158 | fast_envelope_download_tbb() 159 | set(TBB_BUILD_STATIC ON CACHE BOOL " " FORCE) 160 | set(TBB_BUILD_SHARED OFF CACHE BOOL " " FORCE) 161 | set(TBB_BUILD_TBBMALLOC OFF CACHE BOOL " " FORCE) 162 | set(TBB_BUILD_TBBMALLOC_PROXY OFF CACHE BOOL " " FORCE) 163 | set(TBB_BUILD_TESTS OFF CACHE BOOL " " FORCE) 164 | set(TBB_NO_DATE ON CACHE BOOL " " FORCE) 165 | 166 | add_subdirectory(${THIRD_PARTY_DIR}/tbb tbb) 167 | set_target_properties(tbb_static PROPERTIES 168 | INTERFACE_INCLUDE_DIRECTORIES "${THIRD_PARTY_DIR}/tbb/include" 169 | ) 170 | if(NOT MSVC) 171 | set_target_properties(tbb_static PROPERTIES 172 | COMPILE_FLAGS "-Wno-implicit-fallthrough -Wno-missing-field-initializers -Wno-unused-parameter -Wno-keyword-macro" 173 | ) 174 | set_target_properties(tbb_static PROPERTIES POSITION_INDEPENDENT_CODE ON) 175 | endif() 176 | target_compile_definitions(tbb_static PUBLIC -DUSE_TBB) 177 | add_library(tbb::tbb ALIAS tbb_static) 178 | 179 | set_property(TARGET tbb_static tbb_def_files PROPERTY FOLDER "dependencies") 180 | endif() 181 | 182 | 183 | # if(${FAST_ENVELOPE_USE_FLOAT}) 184 | # target_compile_definitions(${PROJECT_NAME} PUBLIC -DFAST_ENVELOPE_USE_FLOAT) 185 | # endif() 186 | 187 | 188 | #spdlog 189 | target_link_libraries(${PROJECT_NAME} PUBLIC spdlog::spdlog) 190 | target_link_libraries(${PROJECT_NAME} PUBLIC CLI11::CLI11) 191 | 192 | if(FAST_ENVELOPE_WITH_GEOGRAM_PSM_PREDICATES) 193 | # fast_envelope_download_geogram_predicates() 194 | 195 | add_library(GeoPredicates geogram_predicates/Predicates_psm.cpp) 196 | target_include_directories(GeoPredicates PUBLIC geogram_predicates/) 197 | target_compile_features(GeoPredicates PRIVATE ${CXX11_FEATURES}) 198 | target_compile_definitions(GeoPredicates PRIVATE GEO_STATIC_LIBS) 199 | 200 | target_link_libraries(${PROJECT_NAME} PUBLIC GeoPredicates) 201 | target_compile_definitions(${PROJECT_NAME} PUBLIC ENVELOPE_WITH_GEO_PSM ENVELOPE_WITH_GEO) 202 | 203 | elseif(FAST_ENVELOPE_WITH_GEOGRAM_PREDICATES) 204 | if(NOT TARGET geogram AND NOT TARGET geogram::geogram) 205 | fast_envelope_download_geogram() 206 | include(geogram) 207 | endif() 208 | if(TARGET geogram) 209 | target_link_libraries(${PROJECT_NAME} PUBLIC geogram) 210 | elseif(TARGET geogram::geogram) 211 | target_link_libraries(${PROJECT_NAME} PUBLIC geogram::geogram) 212 | else() 213 | MESSAGE(FATAL_ERROR "Unable to get geogram or geogram::geogram targets") 214 | endif() 215 | target_compile_definitions(${PROJECT_NAME} PUBLIC ENVELOPE_WITH_GEO) 216 | endif() 217 | 218 | 219 | 220 | # Predicates 221 | include("ipred") 222 | target_link_libraries(${PROJECT_NAME} PUBLIC indirectPredicates) 223 | if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") 224 | message(WARNING "On CLang, there is no compiler flag which is required for ensuring the correctness of the algorithm.") 225 | endif() 226 | set(INDIRECTPREDICATES_HEADERS 227 | indirectPredicates/ip_filtered.h 228 | indirectPredicates/ip_filtered_ex.h 229 | ) 230 | indirect_predicates_copy_headers(${INDIRECTPREDICATES_HEADERS}) 231 | 232 | if(FAST_ENVELOPE_WITH_TIMER) 233 | target_compile_definitions(${PROJECT_NAME} PRIVATE FAST_ENVELOPE_USE_TIMER) 234 | endif() 235 | 236 | 237 | 238 | 239 | 240 | ################################################################################ 241 | # Subdirectories 242 | ################################################################################ 243 | 244 | # Sources 245 | add_subdirectory(src) 246 | target_sources(${PROJECT_NAME} PRIVATE indirectPredicates/ip_filtered.cpp) 247 | 248 | if(FAST_ENVELOPE_WITH_GMP) 249 | find_package(GMP) 250 | 251 | IF(NOT ${GMP_FOUND}) 252 | MESSAGE(FATAL_ERROR "Cannot find GMP") 253 | ENDIF() 254 | target_include_directories(${PROJECT_NAME} PUBLIC ${GMP_INCLUDE_DIRS}) 255 | target_link_libraries(${PROJECT_NAME} PUBLIC ${GMP_LIBRARIES}) 256 | target_compile_definitions(${PROJECT_NAME} PUBLIC ENVELOPE_WITH_GMP) 257 | 258 | endif() 259 | 260 | 261 | ################################################################################ 262 | # FastEnvelope binary 263 | ################################################################################ 264 | 265 | # Main executable 266 | if(FAST_ENVELOPE_TOPLEVEL_PROJECT) 267 | # add an application library 268 | #add_subdirectory(app/benchmark) 269 | set(APP_SOURCES 270 | app/benchmark/MeshIO.hpp 271 | app/benchmark/MeshIO.cpp 272 | app/benchmark/mesh_AABB.h 273 | app/benchmark/mesh_AABB.cpp 274 | app/benchmark/AABBWrapper.cpp 275 | app/benchmark/AABBWrapper.h 276 | app/benchmark/csv_reader.h 277 | app/benchmark/csv_reader.cpp 278 | app/benchmark/getRSS.hpp 279 | app/benchmark/sampling.h 280 | app/benchmark/sampling.cpp 281 | ) 282 | fast_envelope_copy_headers(${APP_SOURCES}) 283 | add_library(${PROJECT_NAME}_app ${APP_SOURCES}) 284 | target_include_directories(${PROJECT_NAME}_app PUBLIC ${PROJECT_BINARY_DIR}/include) 285 | target_include_directories(${PROJECT_NAME}_app PUBLIC app) 286 | 287 | # link libraries to the app library 288 | if(NOT FAST_ENVELOPE_WITH_GEOGRAM_PREDICATES) 289 | # Geogram 290 | fast_envelope_download_geogram() 291 | include(geogram) 292 | target_link_libraries(${PROJECT_NAME}_app PUBLIC geogram) 293 | endif() 294 | target_link_libraries(${PROJECT_NAME}_app 295 | PUBLIC 296 | ${PROJECT_NAME} 297 | 298 | ) 299 | 300 | # executable 301 | add_executable(${PROJECT_NAME}_bin app/main.cpp) 302 | target_link_libraries(${PROJECT_NAME}_bin PUBLIC ${PROJECT_NAME}_app) 303 | 304 | target_compile_features(${PROJECT_NAME}_bin PUBLIC ${CXX14_FEATURES}) 305 | if(FAST_ENVELOPE_WITH_SANITIZERS) 306 | add_sanitizers(${PROJECT_NAME}_bin) 307 | endif() 308 | 309 | 310 | if(FAST_ENVELOPE_ENABLE_TBB) 311 | target_link_libraries(${PROJECT_NAME}_bin PUBLIC tbb::tbb) 312 | endif() 313 | if(NOT (${CMAKE_VERSION} VERSION_LESS "3.6.0")) 314 | set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ${PROJECT_NAME}_bin) 315 | endif() 316 | 317 | # unit tests 318 | if(FAST_ENVELOPE_WITH_UNIT_TESTS) 319 | 320 | add_executable(${PROJECT_NAME}_test app/test.cpp) 321 | target_compile_definitions(${PROJECT_NAME}_test PUBLIC 322 | ENVELOPE_TEST_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/app/test_data/") 323 | target_link_libraries(${PROJECT_NAME}_test PUBLIC ${PROJECT_NAME}_app) 324 | 325 | target_compile_features(${PROJECT_NAME}_test PUBLIC ${CXX14_FEATURES}) 326 | if(FAST_ENVELOPE_WITH_SANITIZERS) 327 | add_sanitizers(${PROJECT_NAME}_test) 328 | endif() 329 | 330 | 331 | if(FAST_ENVELOPE_ENABLE_TBB) 332 | target_link_libraries(${PROJECT_NAME}_test PUBLIC tbb::tbb) 333 | endif() 334 | if(NOT (${CMAKE_VERSION} VERSION_LESS "3.6.0")) 335 | set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ${PROJECT_NAME}_test) 336 | endif() 337 | endif() 338 | ################################################################### 339 | #add_executable(${PROJECT_NAME}_bin app/main.cpp) 340 | #target_sources(FastEnvelope_bin PRIVATE ${SOURCES}) 341 | # 342 | #target_compile_features(${PROJECT_NAME}_bin PUBLIC ${CXX14_FEATURES}) 343 | # 344 | # 345 | #if(NOT FAST_ENVELOPE_WITH_GEOGRAM_PREDICATES) 346 | # # Geogram 347 | # fast_envelope_download_geogram() 348 | # include(geogram) 349 | # 350 | #endif() 351 | #target_link_libraries(${PROJECT_NAME}_bin PUBLIC geogram) 352 | #target_link_libraries(${PROJECT_NAME}_bin 353 | # PUBLIC 354 | # ${PROJECT_NAME} 355 | # warnings::all 356 | #) 357 | # 358 | #if(FAST_ENVELOPE_WITH_SANITIZERS) 359 | # add_sanitizers(${PROJECT_NAME}_bin) 360 | #endif() 361 | # 362 | # 363 | #if(FAST_ENVELOPE_ENABLE_TBB) 364 | # target_link_libraries(${PROJECT_NAME}_bin PUBLIC tbb::tbb) 365 | #endif() 366 | # 367 | #if(NOT (${CMAKE_VERSION} VERSION_LESS "3.6.0")) 368 | # set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ${PROJECT_NAME}_bin) 369 | #endif() 370 | 371 | ########################################################################################################## 372 | 373 | 374 | 375 | endif() 376 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Bolun Wang, Teseo Schneider, Yixin Hu, Marco Attene, and Daniele Panozzo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Exact and Efficient Polyhedral Envelope Containment Check 2 | 3 | ![Build](https://github.com/wangbolun300/fast-envelope/workflows/Build/badge.svg) 4 | 5 | ![](bunny.jpg) 6 | 7 | If you use our code, please cite our paper 8 | ```bibtex 9 | @article{Wang:2020:FE, 10 | title={Exact and Efficient Polyhedral Envelope Containment Check}, 11 | author={Bolun Wang and Teseo Schneider and Yixin Hu and Marco Attene and Daniele Panozzo}, 12 | journal = {ACM Trans. Graph.}, 13 | volume = {39}, 14 | number = {4}, 15 | month = jul, 16 | year = {2020}, 17 | publisher = {ACM} 18 | } 19 | ``` 20 | Please click [HERE](https://cims.nyu.edu/gcl/papers/2020-Fast-Envelope.pdf) to download the paper. 21 | This is the link of our talk on SIGGRAPH 2020 [https://www.youtube.com/watch?v=_Vm61nlxyBI](https://www.youtube.com/watch?v=_Vm61nlxyBI). 22 | 23 | **CGAL** 24 | 25 | A partial reimplementation of the algorithm in this repository which does not use the indirect predicates is available in CGAL 5.3 (https://doc.cgal.org/latest/Polygon_mesh_processing/index.html#title36). 26 | 27 | ## Important Note 28 | There is a compiler flag which is required for ensuring the correctness of the algorithm. 29 | The flag is not available on clang. The code has been tested on GCC and Windows compiler. 30 | 31 | 32 | # Installation via CMake 33 | - clone our repository in your dependency folder (or add it as submodule) 34 | - add this in your main `CMakeLists.txt` file `add_subdirectory` pointing to the directory where you cloned this repository 35 | - link your target with our library `target_link_libraries( PUBLIC FastEnvelope)` 36 | 37 | ## Note 38 | Our library requires standard predicates to work, by default we use the fast predicates inside [Geogram](http://alice.loria.fr/software/geogram/doc/html/index.html). If you want to avoid having Geogram as dependency, you can disable it by setting `FAST_ENVELOPE_WITH_GEOGRAM_PSM_PREDICATES` to `ON`. The code will be slower. 39 | 40 | # Usage 41 | - Include `#include ` 42 | - Initialize the envelope checker `FastEnvelope(const std::vector& m_ver, const std::vector& m_faces, const Scalar eps);` with vertices, connectivity, and envelope size. 43 | - Call one of the `is_outside` function with a triangle, point, or segment. 44 | 45 | 46 | # Testing 47 | We also provide an executable target `FastEnvelope_bin` that can be used for benchmarking 48 | 49 | You can run it by: 50 | ```bash 51 | ./FastEnvelope_bin ./queries/_envelope_log.csv ./ftetwild_queries/ 1e-3 1 ours. 52 | ``` 53 | 54 | # Data 55 | All data used in our paper can be downloaded from [https://archive.nyu.edu/handle/2451/61221](https://archive.nyu.edu/handle/2451/61221). 56 | -------------------------------------------------------------------------------- /app/benchmark/AABBWrapper.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace fastEnvelope; 6 | template 7 | void vector_unique(std::vector& v){ 8 | std::sort(v.begin(), v.end()); 9 | v.erase(std::unique(v.begin(), v.end()), v.end()); 10 | } 11 | 12 | void fastEnvelope::AABBWrapper::init_b_mesh(const std::vector& input_vertices, const std::vector& input_faces) { 13 | b_mesh.clear(false,false); 14 | // std::vector> edges; 15 | // for(int i=0;i> conn_tris(input_vertices.size()); 24 | std::vector> all_edges; 25 | all_edges.reserve(input_faces.size() * 3); 26 | for (int i = 0; i < input_faces.size(); i++) { 27 | for (int j = 0; j < 3; j++) { 28 | conn_tris[input_faces[i][j]].push_back(i); 29 | if (input_faces[i][j] < input_faces[i][(j + 1) % 3]) 30 | all_edges.push_back({{input_faces[i][j], input_faces[i][(j + 1) % 3]}}); 31 | else 32 | all_edges.push_back({{input_faces[i][(j + 1) % 3], input_faces[i][j]}}); 33 | } 34 | } 35 | vector_unique(all_edges); 36 | 37 | std::vector> b_edges; 38 | for (auto &e:all_edges) { 39 | std::vector tmp; 40 | std::set_intersection(conn_tris[e[0]].begin(), conn_tris[e[0]].end(), 41 | conn_tris[e[1]].begin(), conn_tris[e[1]].end(), std::back_inserter(tmp)); 42 | if (tmp.size() == 1) { 43 | b_edges.push_back(e); 44 | } 45 | } 46 | 47 | if (b_edges.empty()) { 48 | b_mesh.vertices.clear(); 49 | b_mesh.vertices.create_vertices(1); 50 | b_mesh.vertices.point(0) = GEO::vec3(0, 0, 0); 51 | b_mesh.facets.clear(); 52 | b_mesh.facets.create_triangles(1); 53 | b_mesh.facets.set_vertex(0, 0, 0); 54 | b_mesh.facets.set_vertex(0, 1, 0); 55 | b_mesh.facets.set_vertex(0, 2, 0); 56 | } else { 57 | b_mesh.vertices.clear(); 58 | b_mesh.vertices.create_vertices((int) b_edges.size() * 2); 59 | int cnt = 0; 60 | for (auto &e:b_edges) { 61 | for (int j = 0; j < 2; j++) { 62 | GEO::vec3 &p = b_mesh.vertices.point(cnt++); 63 | p[0] = input_vertices[e[j]][0]; 64 | p[1] = input_vertices[e[j]][1]; 65 | p[2] = input_vertices[e[j]][2]; 66 | } 67 | } 68 | b_mesh.facets.clear(); 69 | b_mesh.facets.create_triangles((int) b_edges.size()); 70 | for (int i = 0; i < b_edges.size(); i++) { 71 | b_mesh.facets.set_vertex(i, 0, i * 2); 72 | b_mesh.facets.set_vertex(i, 1, i * 2); 73 | b_mesh.facets.set_vertex(i, 2, i * 2 + 1); 74 | } 75 | } 76 | 77 | mesh_reorder(b_mesh, GEO::MESH_ORDER_MORTON); 78 | } 79 | 80 | //void fastEnvelope::AABBWrapper::init_tmp_b_mesh_and_tree(const Mesh& mesh, const std::vector>& b_edges){ 81 | // if (b_edges.empty()) { 82 | // tmp_b_mesh.vertices.clear(); 83 | // tmp_b_mesh.vertices.create_vertices(1); 84 | // tmp_b_mesh.vertices.point(0) = GEO::vec3(0, 0, 0); 85 | // tmp_b_mesh.facets.clear(); 86 | // tmp_b_mesh.facets.create_triangles(1); 87 | // tmp_b_mesh.facets.set_vertex(0, 0, 0); 88 | // tmp_b_mesh.facets.set_vertex(0, 1, 0); 89 | // tmp_b_mesh.facets.set_vertex(0, 2, 0); 90 | // } else { 91 | // tmp_b_mesh.vertices.clear(); 92 | // tmp_b_mesh.vertices.create_vertices((int) b_edges.size() * 2); 93 | // int cnt = 0; 94 | // for (auto &e:b_edges) { 95 | // for (int j = 0; j < 2; j++) { 96 | // GEO::vec3 &p = tmp_b_mesh.vertices.point(cnt++); 97 | // p[0] = mesh.tet_vertices[e[j]].pos[0]; 98 | // p[1] = mesh.tet_vertices[e[j]].pos[1]; 99 | // p[2] = mesh.tet_vertices[e[j]].pos[2]; 100 | // } 101 | // } 102 | // tmp_b_mesh.facets.clear(); 103 | // tmp_b_mesh.facets.create_triangles((int) b_edges.size()); 104 | // for (int i = 0; i < b_edges.size(); i++) { 105 | // tmp_b_mesh.facets.set_vertex(i, 0, i * 2); 106 | // tmp_b_mesh.facets.set_vertex(i, 1, i * 2); 107 | // tmp_b_mesh.facets.set_vertex(i, 2, i * 2 + 1); 108 | // } 109 | // } 110 | // mesh_reorder(tmp_b_mesh, GEO::MESH_ORDER_MORTON); 111 | // tmp_b_tree = std::make_shared(tmp_b_mesh); 112 | //} 113 | 114 | void fastEnvelope::AABBWrapper::init_tmp_b_mesh_and_tree(const std::vector& input_vertices, const std::vector& input_faces, 115 | const std::vector>& b_edges){ 116 | if (b_edges.empty()) { 117 | tmp_b_mesh.vertices.clear(); 118 | tmp_b_mesh.vertices.create_vertices(1); 119 | tmp_b_mesh.vertices.point(0) = GEO::vec3(0, 0, 0); 120 | tmp_b_mesh.facets.clear(); 121 | tmp_b_mesh.facets.create_triangles(1); 122 | tmp_b_mesh.facets.set_vertex(0, 0, 0); 123 | tmp_b_mesh.facets.set_vertex(0, 1, 0); 124 | tmp_b_mesh.facets.set_vertex(0, 2, 0); 125 | } else { 126 | tmp_b_mesh.vertices.clear(); 127 | tmp_b_mesh.vertices.create_vertices((int) b_edges.size() * 2); 128 | int cnt = 0; 129 | for (auto &e:b_edges) { 130 | for (int j = 0; j < 2; j++) { 131 | GEO::vec3 &p = tmp_b_mesh.vertices.point(cnt++); 132 | p[0] = input_vertices[e[j]][0]; 133 | p[1] = input_vertices[e[j]][1]; 134 | p[2] = input_vertices[e[j]][2]; 135 | } 136 | } 137 | tmp_b_mesh.facets.clear(); 138 | tmp_b_mesh.facets.create_triangles((int) b_edges.size()); 139 | for (int i = 0; i < b_edges.size(); i++) { 140 | tmp_b_mesh.facets.set_vertex(i, 0, i * 2); 141 | tmp_b_mesh.facets.set_vertex(i, 1, i * 2); 142 | tmp_b_mesh.facets.set_vertex(i, 2, i * 2 + 1); 143 | } 144 | } 145 | mesh_reorder(tmp_b_mesh, GEO::MESH_ORDER_MORTON); 146 | tmp_b_tree = std::make_shared(tmp_b_mesh); 147 | } -------------------------------------------------------------------------------- /app/benchmark/AABBWrapper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | namespace fastEnvelope { 11 | 12 | using namespace floatTetWild; 13 | 14 | 15 | class AABBWrapper { 16 | private: 17 | GEO::Mesh b_mesh; 18 | GEO::Mesh tmp_b_mesh; 19 | const GEO::Mesh &sf_mesh; 20 | 21 | std::shared_ptr b_tree; 22 | std::shared_ptr tmp_b_tree; 23 | floatTetWild::MeshFacetsAABBWithEps sf_tree; 24 | 25 | void init_b_mesh(const std::vector& input_vertices, const std::vector& input_faces); 26 | 27 | public: 28 | void sample_triangle(const std::array& vs, std::vector& ps, Scalar sampling_dist) { 29 | Scalar sqrt3_2 = std::sqrt(3) / 2; 30 | 31 | std::array ls; 32 | for (int i = 0; i < 3; i++) { 33 | ls[i] = (vs[i] - vs[(i + 1) % 3]).squaredNorm(); 34 | } 35 | auto min_max = std::minmax_element(ls.begin(), ls.end()); 36 | int min_i = min_max.first - ls.begin(); 37 | int max_i = min_max.second - ls.begin(); 38 | Scalar N = sqrt(ls[max_i]) / sampling_dist; 39 | if (N <= 1) { 40 | for (int i = 0; i < 3; i++) 41 | ps.push_back(GEO::vec3(vs[i][0], vs[i][1], vs[i][2])); 42 | return; 43 | } 44 | if (N == int(N)) 45 | N -= 1; 46 | 47 | GEO::vec3 v0(vs[max_i][0], vs[max_i][1], vs[max_i][2]); 48 | GEO::vec3 v1(vs[(max_i + 1) % 3][0], vs[(max_i + 1) % 3][1], vs[(max_i + 1) % 3][2]); 49 | GEO::vec3 v2(vs[(max_i + 2) % 3][0], vs[(max_i + 2) % 3][1], vs[(max_i + 2) % 3][2]); 50 | 51 | GEO::vec3 n_v0v1 = GEO::normalize(v1 - v0); 52 | for (int n = 0; n <= N; n++) { 53 | ps.push_back(v0 + n_v0v1 * sampling_dist * n); 54 | } 55 | ps.push_back(v1); 56 | 57 | Scalar h = GEO::distance(GEO::dot((v2 - v0), (v1 - v0)) * (v1 - v0) / ls[max_i] + v0, v2); 58 | int M = h / (sqrt3_2 * sampling_dist); 59 | if (M < 1) { 60 | ps.push_back(v2); 61 | return; 62 | } 63 | 64 | GEO::vec3 n_v0v2 = GEO::normalize(v2 - v0); 65 | GEO::vec3 n_v1v2 = GEO::normalize(v2 - v1); 66 | Scalar tan_v0, tan_v1, sin_v0, sin_v1; 67 | sin_v0 = GEO::length(GEO::cross((v2 - v0), (v1 - v0))) / (GEO::distance(v0, v2) * GEO::distance(v0, v1)); 68 | tan_v0 = GEO::length(GEO::cross((v2 - v0), (v1 - v0))) / GEO::dot((v2 - v0), (v1 - v0)); 69 | tan_v1 = GEO::length(GEO::cross((v2 - v1), (v0 - v1))) / GEO::dot((v2 - v1), (v0 - v1)); 70 | sin_v1 = GEO::length(GEO::cross((v2 - v1), (v0 - v1))) / (GEO::distance(v1, v2) * GEO::distance(v0, v1)); 71 | 72 | for (int m = 1; m <= M; m++) { 73 | int n = sqrt3_2 / tan_v0 * m + 0.5; 74 | int n1 = sqrt3_2 / tan_v0 * m; 75 | if (m % 2 == 0 && n == n1) { 76 | n += 1; 77 | } 78 | GEO::vec3 v0_m = v0 + m * sqrt3_2 * sampling_dist / sin_v0 * n_v0v2; 79 | GEO::vec3 v1_m = v1 + m * sqrt3_2 * sampling_dist / sin_v1 * n_v1v2; 80 | if (GEO::distance(v0_m, v1_m) <= sampling_dist) 81 | break; 82 | 83 | Scalar delta_d = ((n + (m % 2) / 2.0) - m * sqrt3_2 / tan_v0) * sampling_dist; 84 | GEO::vec3 v = v0_m + delta_d * n_v0v1; 85 | int N1 = GEO::distance(v, v1_m) / sampling_dist; 86 | // ps.push_back(v0_m); 87 | for (int i = 0; i <= N1; i++) { 88 | ps.push_back(v + i * n_v0v1 * sampling_dist); 89 | } 90 | // ps.push_back(v1_m); 91 | } 92 | ps.push_back(v2); 93 | 94 | //sample edges 95 | N = sqrt(ls[(max_i + 1) % 3]) / sampling_dist; 96 | if (N > 1) { 97 | if (N == int(N)) 98 | N -= 1; 99 | GEO::vec3 n_v1v2 = GEO::normalize(v2 - v1); 100 | for (int n = 1; n <= N; n++) { 101 | ps.push_back(v1 + n_v1v2 * sampling_dist * n); 102 | } 103 | } 104 | 105 | N = sqrt(ls[(max_i + 2) % 3]) / sampling_dist; 106 | if (N > 1) { 107 | if (N == int(N)) 108 | N -= 1; 109 | GEO::vec3 n_v2v0 = GEO::normalize(v0 - v2); 110 | for (int n = 1; n <= N; n++) { 111 | ps.push_back(v2 + n_v2v0 * sampling_dist * n); 112 | } 113 | } 114 | } 115 | 116 | 117 | 118 | AABBWrapper(const GEO::Mesh &sf_mesh) : sf_mesh(sf_mesh), sf_tree(sf_mesh) {} 119 | 120 | inline Scalar get_sf_diag() const { return GEO::bbox_diagonal(sf_mesh); } 121 | 122 | void init_b_mesh_and_tree(const std::vector &input_vertices, const std::vector &input_faces) { 123 | init_b_mesh(input_vertices, input_faces); 124 | b_tree = std::make_shared(b_mesh); 125 | } 126 | 127 | 128 | void init_tmp_b_mesh_and_tree(const std::vector& input_vertices, const std::vector& input_faces, 129 | const std::vector>& b_edges); 130 | 131 | inline Scalar project_to_sf(Vector3 &p) const { 132 | GEO::vec3 geo_p(p[0], p[1], p[2]); 133 | GEO::vec3 nearest_p; 134 | double sq_dist = std::numeric_limits::max(); //?? 135 | sf_tree.nearest_facet(geo_p, nearest_p, sq_dist); 136 | p[0] = nearest_p[0]; 137 | p[1] = nearest_p[1]; 138 | p[2] = nearest_p[2]; 139 | 140 | return sq_dist; 141 | } 142 | 143 | inline Scalar project_to_sf(const GEO::vec3 geo_p) const { 144 | GEO::vec3 nearest_p; 145 | double sq_dist = std::numeric_limits::max(); //?? 146 | sf_tree.nearest_facet(geo_p, nearest_p, sq_dist); 147 | return sq_dist; 148 | } 149 | 150 | inline Scalar project_to_b(Vector3 &p) const { 151 | GEO::vec3 geo_p(p[0], p[1], p[2]); 152 | GEO::vec3 nearest_p; 153 | double sq_dist = std::numeric_limits::max(); //? 154 | b_tree->nearest_facet(geo_p, nearest_p, sq_dist); 155 | p[0] = nearest_p[0]; 156 | p[1] = nearest_p[1]; 157 | p[2] = nearest_p[2]; 158 | 159 | return sq_dist; 160 | } 161 | 162 | inline Scalar project_to_tmp_b(Vector3 &p) const { 163 | GEO::vec3 geo_p(p[0], p[1], p[2]); 164 | GEO::vec3 nearest_p; 165 | double sq_dist = std::numeric_limits::max(); //? 166 | tmp_b_tree->nearest_facet(geo_p, nearest_p, sq_dist); 167 | p[0] = nearest_p[0]; 168 | p[1] = nearest_p[1]; 169 | p[2] = nearest_p[2]; 170 | 171 | return sq_dist; 172 | } 173 | 174 | inline Scalar project_to_b(const GEO::vec3 geo_p) const { 175 | GEO::vec3 nearest_p; 176 | double sq_dist = std::numeric_limits::max(); //?? 177 | b_tree->nearest_facet(geo_p, nearest_p, sq_dist); 178 | return sq_dist; 179 | } 180 | 181 | inline bool is_out_sf_envelope(const std::vector &ps, const Scalar eps_2, 182 | GEO::index_t prev_facet = GEO::NO_FACET) const { 183 | GEO::vec3 nearest_point; 184 | double sq_dist = std::numeric_limits::max(); 185 | 186 | for (const GEO::vec3 ¤t_point : ps) { 187 | if (prev_facet != GEO::NO_FACET) { 188 | get_point_facet_nearest_point(sf_mesh, current_point, prev_facet, nearest_point, sq_dist); 189 | } 190 | if (Scalar(sq_dist) > eps_2) { 191 | sf_tree.facet_in_envelope_with_hint(current_point, eps_2, prev_facet, nearest_point, sq_dist); 192 | } 193 | if (Scalar(sq_dist) > eps_2) { 194 | return true; 195 | } 196 | } 197 | 198 | return false; 199 | } 200 | 201 | inline Scalar dist_sf_envelope(const std::vector &ps, const Scalar eps_2, 202 | GEO::index_t prev_facet = GEO::NO_FACET) const { 203 | GEO::vec3 nearest_point; 204 | double sq_dist = std::numeric_limits::max(); 205 | 206 | for (const GEO::vec3 ¤t_point : ps) { 207 | if (prev_facet != GEO::NO_FACET) { 208 | get_point_facet_nearest_point(sf_mesh, current_point, prev_facet, nearest_point, sq_dist); 209 | } 210 | if (Scalar(sq_dist) > eps_2) { 211 | sf_tree.facet_in_envelope_with_hint(current_point, eps_2, prev_facet, nearest_point, sq_dist); 212 | } 213 | if (Scalar(sq_dist) > eps_2) { 214 | return sq_dist; 215 | } 216 | } 217 | 218 | return 0; 219 | } 220 | 221 | inline bool is_out_b_envelope(const std::vector &ps, const Scalar eps_2, 222 | GEO::index_t prev_facet = GEO::NO_FACET) const { 223 | GEO::vec3 nearest_point; 224 | double sq_dist = std::numeric_limits::max(); 225 | 226 | for (const GEO::vec3 ¤t_point : ps) { 227 | if (prev_facet != GEO::NO_FACET) { 228 | get_point_facet_nearest_point(b_mesh, current_point, prev_facet, nearest_point, sq_dist); 229 | } 230 | if (Scalar(sq_dist) > eps_2) { 231 | b_tree->facet_in_envelope_with_hint(current_point, eps_2, prev_facet, nearest_point, sq_dist); 232 | } 233 | if (Scalar(sq_dist) > eps_2) { 234 | return true; 235 | } 236 | } 237 | 238 | return false; 239 | } 240 | 241 | inline bool is_out_tmp_b_envelope(const std::vector &ps, const Scalar eps_2, 242 | GEO::index_t prev_facet = GEO::NO_FACET) const { 243 | GEO::vec3 nearest_point; 244 | double sq_dist = std::numeric_limits::max(); 245 | 246 | for (const GEO::vec3 ¤t_point : ps) { 247 | if (prev_facet != GEO::NO_FACET) { 248 | get_point_facet_nearest_point(tmp_b_mesh, current_point, prev_facet, nearest_point, sq_dist); 249 | } 250 | if (Scalar(sq_dist) > eps_2) { 251 | tmp_b_tree->facet_in_envelope_with_hint(current_point, eps_2, prev_facet, nearest_point, sq_dist); 252 | } 253 | if (Scalar(sq_dist) > eps_2) { 254 | return true; 255 | } 256 | } 257 | 258 | return false; 259 | } 260 | 261 | inline bool is_out_sf_envelope(const Vector3& p, const Scalar eps_2, GEO::index_t& prev_facet) const { 262 | GEO::vec3 nearest_p; 263 | double sq_dist; 264 | GEO::vec3 geo_p(p[0], p[1], p[2]); 265 | prev_facet = sf_tree.facet_in_envelope(geo_p, eps_2, nearest_p, sq_dist); 266 | 267 | if (Scalar(sq_dist) > eps_2) 268 | return true; 269 | return false; 270 | } 271 | 272 | inline bool is_out_b_envelope(const Vector3& p, const Scalar eps_2, GEO::index_t& prev_facet) const { 273 | GEO::vec3 nearest_p; 274 | double sq_dist; 275 | GEO::vec3 geo_p(p[0], p[1], p[2]); 276 | prev_facet = b_tree->facet_in_envelope(geo_p, eps_2, nearest_p, sq_dist); 277 | 278 | if (Scalar(sq_dist) > eps_2) 279 | return true; 280 | return false; 281 | } 282 | 283 | inline bool is_out_tmp_b_envelope(const Vector3& p, const Scalar eps_2, GEO::index_t& prev_facet) const { 284 | GEO::vec3 nearest_p; 285 | double sq_dist; 286 | GEO::vec3 geo_p(p[0], p[1], p[2]); 287 | prev_facet = tmp_b_tree->facet_in_envelope(geo_p, eps_2, nearest_p, sq_dist); 288 | 289 | if (Scalar(sq_dist) > eps_2) 290 | return true; 291 | return false; 292 | } 293 | 294 | // 295 | inline bool is_out_sf_envelope(const Vector3& p, const Scalar eps_2, 296 | GEO::index_t& prev_facet, double& sq_dist, GEO::vec3& nearest_p) const { 297 | GEO::vec3 geo_p(p[0], p[1], p[2]); 298 | return is_out_sf_envelope(geo_p, eps_2, prev_facet, sq_dist, nearest_p); 299 | } 300 | 301 | inline bool is_out_sf_envelope(const GEO::vec3& geo_p, const Scalar eps_2, 302 | GEO::index_t& prev_facet, double& sq_dist, GEO::vec3& nearest_p) const { 303 | //// prev_facet = sf_tree.facet_in_envelope(geo_p, eps_2, nearest_p, sq_dist); 304 | //// double sq_dist = std::numeric_limits::max(); //?? 305 | // sf_tree.nearest_facet(geo_p, nearest_p, sq_dist); 306 | // 307 | // if (Scalar(sq_dist) > eps_2) 308 | // return true; 309 | // return false; 310 | // ///////// 311 | 312 | 313 | // prev_facet = sf_tree.facet_in_envelope(geo_p, eps_2, nearest_p, sq_dist); 314 | if (prev_facet != GEO::NO_FACET) { 315 | get_point_facet_nearest_point(sf_mesh, geo_p, prev_facet, nearest_p, sq_dist); 316 | } 317 | if (Scalar(sq_dist) > eps_2) { 318 | sf_tree.facet_in_envelope_with_hint(geo_p, eps_2, prev_facet, nearest_p, sq_dist); 319 | } 320 | 321 | if (Scalar(sq_dist) > eps_2) 322 | return true; 323 | return false; 324 | } 325 | }; 326 | } 327 | -------------------------------------------------------------------------------- /app/benchmark/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # set(SOURCES 2 | # MeshIO.hpp 3 | # MeshIO.cpp 4 | 5 | # #EnvelopeTest.cpp 6 | # #EnvelopeTest.h 7 | 8 | # mesh_AABB.h 9 | # mesh_AABB.cpp 10 | 11 | # AABBWrapper.cpp 12 | # AABBWrapper.h 13 | # csv_reader.h 14 | # csv_reader.cpp 15 | 16 | # getRSS.hpp 17 | # sampling.h 18 | # sampling.cpp 19 | # ) 20 | 21 | # prepend_current_path(SOURCES) 22 | # fast_envelope_copy_headers(${SOURCES}) 23 | # fast_envelope_set_source_group(${SOURCES}) 24 | # target_sources(FastEnvelope_bin PRIVATE ${SOURCES}) 25 | 26 | -------------------------------------------------------------------------------- /app/benchmark/EnvelopeTest.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | namespace fastEnvelope { 7 | 8 | class Envelop 9 | { 10 | public: 11 | // generate octree, recording the cells position, input vertices list, cell list, the depth of iteration, 12 | // out put an orctree 13 | //static void Octree(const std::vector& vlist, const std::vector& cell, const int& depth, 14 | // std::vector>& tree); 15 | 16 | // to judge if two faces have intersection, in order to 17 | //static void FaceIntersection(const std::vector& vlist, const std::vector& cell, const int& depth, 18 | // std::vector>& tree); 19 | 20 | //to judge if a triangle is in the envelope. input 3 vertices of the triangle, the potential intersection prisms, 21 | //out put a bool value to tell if the envelope is out of the envelope 22 | static bool EnvelopeTest(const std::array& face, const std::vector>& interprism); 23 | static bool EnvelopeTest1(const std::array& face, const std::vector>& interprism, const int& mark); 24 | static bool EnvelopeTest2(const std::array& face, const std::vector>& interprism); 25 | //to cut a triangle with a prism. 26 | //input a single prism, a triangle 27 | //output a outer triangle list 28 | static void OnePrismCutOneTriangle(const std::array& triangle, const std::array& envprism 29 | , std::vector< std::array>& olist); 30 | // to cut a triangle with a prism triangle face. 31 | //input triangle vertices, prism triangle face, output if the triangle is intersected, and a inner triangles list and a outer triangle list 32 | static void PrismTriangleCutTriangle(const std::array& triangle, const std::array& p_face, bool& intersection, 33 | std::vector>& innerlist, std::vector>& outerlist); 34 | // to generate envelope prisms accroding to the model faces. 35 | //input model vertices, model faces indexs,and bounding box diagonal. 36 | //out put prism vertices 37 | static void PrismGeneration(const std::vector& m_ver, const std::vector& m_faces, std::vector>& envprism, const Scalar& bbd); 38 | 39 | 40 | //surface and line intersection points, input surface vertices and line vertices,out put if 41 | //the line is cutted and the cut point,and the bool if the line go through the surface 42 | //THIS VERSION DO NOT DO THE COLLASPING 43 | static void SLIntersection(const Parameters ¶ms, const std::array& cutface, const Vector3& linepoints0, const Vector3& linepoints1, 44 | int & cutOrnot, Vector3& interp, Scalar &t, int& ion); 45 | 46 | // this is for back up of the SLIntersection, it do the collasping while calculation of intersection points 47 | static void SLIntersectionBackup(const Parameters & params, const std::array& cutface, const Vector3 & linepoints0, 48 | const Vector3 & linepoints1, int & cutOrnot, Vector3 & interp, Scalar & t, int & ion); 49 | 50 | //Minkowski sweeping. not considering face orientation 51 | static void BoxGeneration(const std::vector& m_ver, const std::vector& m_faces, std::vector>& envprism, const Scalar& bbd); 52 | }; 53 | 54 | 55 | 56 | } -------------------------------------------------------------------------------- /app/benchmark/MeshIO.cpp: -------------------------------------------------------------------------------- 1 | #include "MeshIO.hpp" 2 | 3 | //#include 4 | 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | 20 | #include 21 | 22 | 23 | namespace fastEnvelope 24 | { 25 | bool MeshIO::load_mesh(const std::string &path, std::vector &points, std::vector &faces, GEO::Mesh& input) 26 | { 27 | //logger().debug("Loading mesh at {}...", path); 28 | igl::Timer timer; timer.start(); 29 | 30 | input.clear(false,false); 31 | std::vector flags; 32 | 33 | const bool ok = GEO::mesh_load(path, input); 34 | 35 | if(!ok) 36 | return false; 37 | 38 | bool is_valid = (flags.size() == input.facets.nb()); 39 | if(is_valid) 40 | { 41 | assert(flags.size() == input.facets.nb()); 42 | GEO::Attribute bflags(input.facets.attributes(), "bbflags"); 43 | for (int index = 0; index < (int) input.facets.nb(); ++index) { 44 | bflags[index] = flags[index]; 45 | } 46 | } 47 | 48 | if(!input.facets.are_simplices()) { 49 | mesh_repair( 50 | input, 51 | GEO::MeshRepairMode(GEO::MESH_REPAIR_TRIANGULATE | GEO::MESH_REPAIR_QUIET) 52 | ); 53 | } 54 | 55 | // #ifdef FLOAT_TETWILD_USE_FLOAT 56 | // input.vertices.set_single_precision(); 57 | // #else 58 | // input.vertices.set_double_precision(); 59 | // #endif 60 | 61 | GEO::mesh_reorder(input, GEO::MESH_ORDER_MORTON); 62 | 63 | if(is_valid) 64 | { 65 | flags.clear(); 66 | flags.resize(input.facets.nb()); 67 | GEO::Attribute bflags(input.facets.attributes(), "bbflags"); 68 | for (int index = 0; index < (int) input.facets.nb(); ++index) { 69 | flags[index] = bflags[index]; 70 | } 71 | } 72 | 73 | points.resize(input.vertices.nb()); 74 | for(size_t i=0; i 4 | 5 | #include 6 | 7 | #include 8 | 9 | namespace fastEnvelope 10 | { 11 | class MeshIO 12 | { 13 | public: 14 | static bool load_mesh(const std::string &path, std::vector &points, std::vector &faces, GEO::Mesh& input); 15 | }; 16 | } -------------------------------------------------------------------------------- /app/benchmark/csv_reader.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | using namespace std; 6 | namespace fastEnvelope{ 7 | std::vector> read_CSV_triangle(const string inputFileName, vector& inenvelope) { 8 | 9 | 10 | std::vector> triangle; 11 | 12 | ifstream infile; 13 | infile.open(inputFileName); 14 | if (!infile.is_open()) 15 | { 16 | cout << "Path Wrong!!!!" << endl; 17 | return triangle; 18 | } 19 | 20 | int l = 0; 21 | while (infile) // there is input overload classfile 22 | { 23 | l++; 24 | string s; 25 | if (!getline(infile, s)) break; 26 | if (s[0] != '#') { 27 | istringstream ss(s); 28 | array record; 29 | int c = 0; 30 | while (ss) { 31 | string line; 32 | if (!getline(ss, line, ',')) 33 | break; 34 | try { 35 | record[c] = stod(line); 36 | c++; 37 | } 38 | catch (const std::invalid_argument e) { 39 | cout << "NaN found in file " << inputFileName << " line " << l 40 | << endl; 41 | e.what(); 42 | } 43 | } 44 | 45 | triangle.push_back({ {Vector3(record[0],record[1],record[2]),Vector3(record[3],record[4],record[5]), 46 | Vector3(record[6],record[7],record[8])} }); 47 | inenvelope.push_back(record[9]); 48 | } 49 | } 50 | if (!infile.eof()) { 51 | cerr << "Could not read file " << inputFileName << "\n"; 52 | } 53 | cout << "triangle size " << triangle.size() << endl; 54 | return triangle; 55 | } 56 | 57 | } -------------------------------------------------------------------------------- /app/benchmark/csv_reader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | using namespace std; 6 | namespace fastEnvelope { 7 | std::vector> read_CSV_triangle(const string inputFileName, vector& inenvelope); 8 | } -------------------------------------------------------------------------------- /app/benchmark/getRSS.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: David Robert Nadeau 3 | * Site: http://NadeauSoftware.com/ 4 | * License: Creative Commons Attribution 3.0 Unported License 5 | * http://creativecommons.org/licenses/by/3.0/deed.en_US 6 | */ 7 | 8 | #if defined(_WIN32) 9 | #include 10 | #include 11 | 12 | #elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__)) 13 | #include 14 | #include 15 | 16 | #if defined(__APPLE__) && defined(__MACH__) 17 | #include 18 | 19 | #elif (defined(_AIX) || defined(__TOS__AIX__)) || (defined(__sun__) || defined(__sun) || defined(sun) && (defined(__SVR4) || defined(__svr4__))) 20 | #include 21 | #include 22 | 23 | #elif defined(__linux__) || defined(__linux) || defined(linux) || defined(__gnu_linux__) 24 | #include 25 | 26 | #endif 27 | 28 | #else 29 | #error "Cannot define getPeakRSS( ) or getCurrentRSS( ) for an unknown OS." 30 | #endif 31 | 32 | 33 | 34 | 35 | 36 | /** 37 | * Returns the peak (maximum so far) resident set size (physical 38 | * memory use) measured in bytes, or zero if the value cannot be 39 | * determined on this OS. 40 | */ 41 | size_t getPeakRSS() 42 | { 43 | #if defined(_WIN32) 44 | /* Windows -------------------------------------------------- */ 45 | PROCESS_MEMORY_COUNTERS info; 46 | GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info)); 47 | return (size_t)info.PeakWorkingSetSize; 48 | 49 | #elif (defined(_AIX) || defined(__TOS__AIX__)) || (defined(__sun__) || defined(__sun) || defined(sun) && (defined(__SVR4) || defined(__svr4__))) 50 | /* AIX and Solaris ------------------------------------------ */ 51 | struct psinfo psinfo; 52 | int fd = -1; 53 | if ((fd = open("/proc/self/psinfo", O_RDONLY)) == -1) 54 | return (size_t)0L; /* Can't open? */ 55 | if (read(fd, &psinfo, sizeof(psinfo)) != sizeof(psinfo)) 56 | { 57 | close(fd); 58 | return (size_t)0L; /* Can't read? */ 59 | } 60 | close(fd); 61 | return (size_t)(psinfo.pr_rssize * 1024L); 62 | 63 | #elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__)) 64 | /* BSD, Linux, and OSX -------------------------------------- */ 65 | struct rusage rusage; 66 | getrusage(RUSAGE_SELF, &rusage); 67 | #if defined(__APPLE__) && defined(__MACH__) 68 | return (size_t)rusage.ru_maxrss; 69 | #else 70 | return (size_t)(rusage.ru_maxrss * 1024L); 71 | #endif 72 | 73 | #else 74 | /* Unknown OS ----------------------------------------------- */ 75 | return (size_t)0L; /* Unsupported. */ 76 | #endif 77 | } 78 | 79 | 80 | 81 | 82 | 83 | /** 84 | * Returns the current resident set size (physical memory use) measured 85 | * in bytes, or zero if the value cannot be determined on this OS. 86 | */ 87 | size_t getCurrentRSS() 88 | { 89 | #if defined(_WIN32) 90 | /* Windows -------------------------------------------------- */ 91 | PROCESS_MEMORY_COUNTERS info; 92 | GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info)); 93 | return (size_t)info.WorkingSetSize; 94 | 95 | #elif defined(__APPLE__) && defined(__MACH__) 96 | /* OSX ------------------------------------------------------ */ 97 | struct mach_task_basic_info info; 98 | mach_msg_type_number_t infoCount = MACH_TASK_BASIC_INFO_COUNT; 99 | if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO, 100 | (task_info_t)&info, &infoCount) != KERN_SUCCESS) 101 | return (size_t)0L; /* Can't access? */ 102 | return (size_t)info.resident_size; 103 | 104 | #elif defined(__linux__) || defined(__linux) || defined(linux) || defined(__gnu_linux__) 105 | /* Linux ---------------------------------------------------- */ 106 | long rss = 0L; 107 | FILE* fp = NULL; 108 | if ((fp = fopen("/proc/self/statm", "r")) == NULL) 109 | return (size_t)0L; /* Can't open? */ 110 | if (fscanf(fp, "%*s%ld", &rss) != 1) 111 | { 112 | fclose(fp); 113 | return (size_t)0L; /* Can't read? */ 114 | } 115 | fclose(fp); 116 | return (size_t)rss * (size_t)sysconf(_SC_PAGESIZE); 117 | 118 | #else 119 | /* AIX, BSD, Solaris, and Unknown OS ------------------------ */ 120 | return (size_t)0L; /* Unsupported. */ 121 | #endif 122 | } -------------------------------------------------------------------------------- /app/benchmark/mesh_AABB.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2014, Bruno Levy 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright notice, 11 | * this list of conditions and the following disclaimer in the documentation 12 | * and/or other materials provided with the distribution. 13 | * * Neither the name of the ALICE Project-Team nor the names of its 14 | * contributors may be used to endorse or promote products derived from this 15 | * software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | * 29 | * If you modify this software, you should include a notice giving the 30 | * name of the person performing the modification, the date of modification, 31 | * and the reason for such modification. 32 | * 33 | * Contact: Bruno Levy 34 | * 35 | * Bruno.Levy@inria.fr 36 | * http://www.loria.fr/~levy 37 | * 38 | * ALICE Project 39 | * LORIA, INRIA Lorraine, 40 | * Campus Scientifique, BP 239 41 | * 54506 VANDOEUVRE LES NANCY CEDEX 42 | * FRANCE 43 | * 44 | */ 45 | 46 | #pragma once 47 | /** 48 | * \file mesh_AABB.h 49 | * \brief Axis Aligned Bounding Box trees for accelerating 50 | * geometric queries that operate on a Mesh. 51 | */ 52 | 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | 59 | namespace floatTetWild { 60 | 61 | /** 62 | * \brief Axis Aligned Bounding Box tree of mesh facets. 63 | * \details Used to quickly compute facet intersection and 64 | * to locate the nearest facet from 3d query points. 65 | */ 66 | class GEOGRAM_API MeshFacetsAABBWithEps { 67 | public: 68 | /** 69 | * \brief Creates the Axis Aligned Bounding Boxes tree. 70 | * \param[in] M the input mesh. It can be modified, 71 | * and will be triangulated (if 72 | * not already a triangular mesh). The facets are 73 | * re-ordered (using Morton's order, see mesh_reorder()). 74 | * \param[in] reorder if not set, Morton re-ordering is 75 | * skipped (but it means that mesh_reorder() was previously 76 | * called else the algorithm will be pretty unefficient). 77 | * \pre M.facets.are_simplices() 78 | */ 79 | MeshFacetsAABBWithEps(const GEO::Mesh& M); 80 | 81 | /** 82 | * \brief Computes all the pairs of intersecting facets. 83 | * \param[in] action ACTION::operator(index_t,index_t) is 84 | * invoked of all pairs of facets that have overlapping 85 | * bounding boxes. triangles_intersection() needs to be 86 | * called to detect the actual intersections. 87 | * \tparam ACTION user action class, that needs to define 88 | * operator(index_t,index_t), where the two indices are 89 | * the indices each pair of triangles that have intersecting 90 | * bounding boxes. 91 | */ 92 | template 93 | void compute_facet_bbox_intersections( 94 | ACTION& action 95 | ) const { 96 | intersect_recursive( 97 | action, 98 | 1, 0, mesh_.facets.nb(), 99 | 1, 0, mesh_.facets.nb() 100 | ); 101 | } 102 | 103 | 104 | /** 105 | * \brief Computes all the intersections between a given 106 | * box and the bounding boxes of all the facets. 107 | * \param[in] action ACTION::operator(index_t) is 108 | * invoked for all facets that have a bounding 109 | * box that intersects \p box_in. 110 | * \tparam ACTION user action class, that needs to define 111 | * operator(index_t), where the parameter is the index 112 | * of the triangle that has its bounding box intersecting 113 | * \p box_in. 114 | */ 115 | template< class ACTION > 116 | void compute_bbox_facet_bbox_intersections( 117 | const GEO::Box& box_in, 118 | ACTION& action 119 | ) const { 120 | bbox_intersect_recursive( 121 | action, box_in, 1, 0, mesh_.facets.nb() 122 | ); 123 | } 124 | 125 | /** 126 | * \brief Finds the nearest facet from an arbitrary 3d query point. 127 | * \param[in] p query point 128 | * \param[out] nearest_point nearest point on the surface 129 | * \param[out] sq_dist squared distance between p and the surface. 130 | * \return the index of the facet nearest to point p. 131 | */ 132 | GEO::index_t nearest_facet( 133 | const GEO::vec3& p, GEO::vec3& nearest_point, double& sq_dist 134 | ) const { 135 | GEO::index_t nearest_facet; 136 | get_nearest_facet_hint(p, nearest_facet, nearest_point, sq_dist); 137 | nearest_facet_recursive( 138 | p, 139 | nearest_facet, nearest_point, sq_dist, 140 | 1, 0, mesh_.facets.nb() 141 | ); 142 | return nearest_facet; 143 | } 144 | 145 | /** 146 | * \brief Computes the nearest point and nearest facet from 147 | * a query point, using user-specified hint. 148 | * 149 | * \details The hint is specified as reasonable initial values of 150 | * (nearest_facet, nearest_point, sq_dist). If multiple queries 151 | * are done on a set of points that has spatial locality, 152 | * the hint can be the result of the previous call. 153 | * 154 | * \param[in] p query point 155 | * \param[in,out] nearest_facet the nearest facet so far, 156 | * or NO_FACET if not known yet 157 | * \param[in,out] nearest_point a point in nearest_facet 158 | * \param[in,out] sq_dist squared distance between p and 159 | * nearest_point 160 | * \note On entry, \p sq_dist needs to be equal to the squared 161 | * distance between \p p and \p nearest_point (it is easy to 162 | * forget to update it when calling it within a loop). 163 | */ 164 | void nearest_facet_with_hint( 165 | const GEO::vec3& p, 166 | GEO::index_t& nearest_facet, GEO::vec3& nearest_point, double& sq_dist 167 | ) const { 168 | if(nearest_facet == GEO::NO_FACET) { 169 | get_nearest_facet_hint( 170 | p, nearest_facet, nearest_point, sq_dist 171 | ); 172 | } 173 | nearest_facet_recursive( 174 | p, 175 | nearest_facet, nearest_point, sq_dist, 176 | 1, 0, mesh_.facets.nb() 177 | ); 178 | } 179 | 180 | /* 181 | * Finds the nearest facet on the surface, but stops early if a 182 | * point within a given distance is found. 183 | */ 184 | GEO::index_t facet_in_envelope( 185 | const GEO::vec3& p, double sq_epsilon, GEO::vec3& nearest_point, double& sq_dist 186 | ) const { 187 | GEO::index_t nearest_facet; 188 | get_nearest_facet_hint(p, nearest_facet, nearest_point, sq_dist); 189 | facet_in_envelope_recursive( 190 | p, sq_epsilon, 191 | nearest_facet, nearest_point, sq_dist, 192 | 1, 0, mesh_.facets.nb() 193 | ); 194 | return nearest_facet; 195 | } 196 | 197 | /* 198 | * Same as before, but stops as soon as a point on the surface in 199 | * within a given distance bound from the triangle mesh. 200 | */ 201 | void facet_in_envelope_with_hint( 202 | const GEO::vec3& p, double sq_epsilon, 203 | GEO::index_t& nearest_facet, GEO::vec3& nearest_point, double& sq_dist 204 | ) const { 205 | if(nearest_facet == GEO::NO_FACET) { 206 | get_nearest_facet_hint( 207 | p, nearest_facet, nearest_point, sq_dist 208 | ); 209 | } 210 | facet_in_envelope_recursive( 211 | p, sq_epsilon, 212 | nearest_facet, nearest_point, sq_dist, 213 | 1, 0, mesh_.facets.nb() 214 | ); 215 | } 216 | 217 | /** 218 | * \brief Computes the distance between an arbitrary 3d query 219 | * point and the surface. 220 | * \param[in] p query point 221 | * \return the squared distance between \p p and the surface. 222 | */ 223 | double squared_distance(const GEO::vec3& p) const { 224 | GEO::vec3 nearest_point; 225 | double result; 226 | nearest_facet(p, nearest_point, result); 227 | return result; 228 | } 229 | 230 | /** 231 | * \brief Tests whether this surface mesh has an intersection 232 | * with a segment. 233 | * \param[in] q1 , q2 the two extremities of the segment. 234 | * \retval true if there exists an intersection between [q1 , q2] 235 | * and a facet of the mesh. 236 | * \retval false otherwise. 237 | */ 238 | bool segment_intersection(const GEO::vec3& q1, const GEO::vec3& q2) const; 239 | 240 | protected: 241 | 242 | 243 | /** 244 | * \brief Computes all the facets that have a bbox that 245 | * intersects a given bbox in a sub-tree of the AABB tree. 246 | * 247 | * Note that the tree structure is completely implicit, 248 | * therefore the bounds of the (continuous) facet indices 249 | * sequences that correspond to the facets contained 250 | * in the two nodes are sent as well as the node indices. 251 | * 252 | * \param[in] action ACTION::operator(index_t) is 253 | * invoked for all facet that has a bounding box that 254 | * overlaps \p box. 255 | * \param[in] node index of the first node of the AABB tree 256 | * \param[in] b index of the first facet in \p node 257 | * \param[in] e one position past the index of the last 258 | * facet in \p node 259 | */ 260 | template 261 | void bbox_intersect_recursive( 262 | ACTION& action, 263 | const GEO::Box& box, 264 | GEO::index_t node, GEO::index_t b, GEO::index_t e 265 | ) const { 266 | geo_debug_assert(e != b); 267 | 268 | // Prune sub-tree that does not have intersection 269 | if(!bboxes_overlap(box, bboxes_[node])) { 270 | return; 271 | } 272 | 273 | // Leaf case 274 | if(e == b+1) { 275 | action(b); 276 | return; 277 | } 278 | 279 | // Recursion 280 | GEO::index_t m = b + (e - b) / 2; 281 | GEO::index_t node_l = 2 * node; 282 | GEO::index_t node_r = 2 * node + 1; 283 | 284 | bbox_intersect_recursive(action, box, node_l, b, m); 285 | bbox_intersect_recursive(action, box, node_r, m, e); 286 | } 287 | 288 | /** 289 | * \brief Computes all the pairs of intersecting facets 290 | * for two sub-trees of the AABB tree. 291 | * 292 | * Note that the tree structure is completely implicit, 293 | * therefore the bounds of the (continuous) facet indices 294 | * sequences that correspond to the facets contained 295 | * in the two nodes are sent as well as the node indices. 296 | * 297 | * \param[in] action ACTION::operator(index_t,index_t) is 298 | * invoked of all pairs of facets that have overlapping 299 | * bounding boxes. 300 | * \param[in] node1 index of the first node of the AABB tree 301 | * \param[in] b1 index of the first facet in \p node1 302 | * \param[in] e1 one position past the index of the last 303 | * facet in \p node1 304 | * \param[in] node2 index of the second node of the AABB tree 305 | * \param[in] b2 index of the first facet in \p node2 306 | * \param[in] e2 one position past the index of the second 307 | * facet in \p node2 308 | */ 309 | template 310 | void intersect_recursive( 311 | ACTION& action, 312 | GEO::index_t node1, GEO::index_t b1, GEO::index_t e1, 313 | GEO::index_t node2, GEO::index_t b2, GEO::index_t e2 314 | ) const { 315 | geo_debug_assert(e1 != b1); 316 | geo_debug_assert(e2 != b2); 317 | 318 | // Since we are intersecting the AABBTree with *itself*, 319 | // we can prune half of the cases by skipping the test 320 | // whenever node2's facet index interval is greated than 321 | // node1's facet index interval. 322 | if(e2 <= b1) { 323 | return; 324 | } 325 | 326 | // The acceleration is here: 327 | if(!bboxes_overlap(bboxes_[node1], bboxes_[node2])) { 328 | return; 329 | } 330 | 331 | // Simple case: leaf - leaf intersection. 332 | if(b1 + 1 == e1 && b2 + 1 == e2) { 333 | action(b1, b2); 334 | return; 335 | } 336 | 337 | // If node2 has more facets than node1, then 338 | // intersect node2's two children with node1 339 | // else 340 | // intersect node1's two children with node2 341 | if(e2 - b2 > e1 - b1) { 342 | GEO::index_t m2 = b2 + (e2 - b2) / 2; 343 | GEO::index_t node2_l = 2 * node2; 344 | GEO::index_t node2_r = 2 * node2 + 1; 345 | intersect_recursive(action, node1, b1, e1, node2_l, b2, m2); 346 | intersect_recursive(action, node1, b1, e1, node2_r, m2, e2); 347 | } else { 348 | GEO::index_t m1 = b1 + (e1 - b1) / 2; 349 | GEO::index_t node1_l = 2 * node1; 350 | GEO::index_t node1_r = 2 * node1 + 1; 351 | intersect_recursive(action, node1_l, b1, m1, node2, b2, e2); 352 | intersect_recursive(action, node1_r, m1, e1, node2, b2, e2); 353 | } 354 | } 355 | 356 | /** 357 | * \brief Computes a reasonable initialization for 358 | * nearest facet search. 359 | * 360 | * \details A good initialization makes the algorithm faster, 361 | * by allowing early pruning of subtrees that provably 362 | * do not contain the nearest neighbor. 363 | * 364 | * \param[in] p query point 365 | * \param[out] nearest_facet a facet reasonably near p 366 | * \param[out] nearest_point a point in nearest_facet 367 | * \param[out] sq_dist squared distance between p and nearest_point 368 | */ 369 | void get_nearest_facet_hint( 370 | const GEO::vec3& p, 371 | GEO::index_t& nearest_facet, GEO::vec3& nearest_point, double& sq_dist 372 | ) const; 373 | 374 | /** 375 | * \brief The recursive function used by the implementation 376 | * of nearest_facet(). 377 | * 378 | * \details The first call may use get_nearest_facet_hint() 379 | * to initialize nearest_facet, nearest_point and sq_dist, 380 | * as done in nearest_facet(). 381 | * 382 | * \param[in] p query point 383 | * \param[in,out] nearest_facet the nearest facet so far, 384 | * \param[in,out] nearest_point a point in nearest_facet 385 | * \param[in,out] sq_dist squared distance between p and nearest_point 386 | * \param[in] n index of the current node in the AABB tree 387 | * \param[in] b index of the first facet in the subtree under node \p n 388 | * \param[in] e one position past the index of the last facet in the 389 | * subtree under node \p n 390 | */ 391 | void nearest_facet_recursive( 392 | const GEO::vec3& p, 393 | GEO::index_t& nearest_facet, GEO::vec3& nearest_point, double& sq_dist, 394 | GEO::index_t n, GEO::index_t b, GEO::index_t e 395 | ) const; 396 | 397 | /* 398 | * Same as before, but stops early if a point within a given distance 399 | * is found. 400 | */ 401 | void facet_in_envelope_recursive( 402 | const GEO::vec3& p, double sq_epsilon, 403 | GEO::index_t& nearest_facet, GEO::vec3& nearest_point, double& sq_dist, 404 | GEO::index_t n, GEO::index_t b, GEO::index_t e 405 | ) const; 406 | 407 | /** 408 | * \brief The recursive function used by the implementation 409 | * of segment_intersection() 410 | * \param[in] q1 , q2 the segment 411 | * \param[in] n index of the current node in the AABB tree 412 | * \param[in] b index of the first facet in the subtree under node \p n 413 | * \param[in] e one position past the index of the last facet in the 414 | * subtree under node \p n 415 | */ 416 | bool segment_intersection_recursive( 417 | const GEO::vec3& q1, const GEO::vec3& q2, GEO::index_t n, GEO::index_t b, GEO::index_t e 418 | ) const; 419 | 420 | protected: 421 | GEO::vector bboxes_; 422 | const GEO::Mesh& mesh_; 423 | }; 424 | 425 | } 426 | 427 | namespace floatTetWild { 428 | 429 | inline void get_point_facet_nearest_point( 430 | const GEO::Mesh& M, 431 | const GEO::vec3& p, 432 | GEO::index_t f, 433 | GEO::vec3& nearest_p, 434 | double& squared_dist 435 | ) { 436 | using namespace GEO; 437 | geo_debug_assert(M.facets.nb_vertices(f) == 3); 438 | index_t c = M.facets.corners_begin(f); 439 | const vec3& p1 = Geom::mesh_vertex(M, M.facet_corners.vertex(c)); 440 | ++c; 441 | const vec3& p2 = Geom::mesh_vertex(M, M.facet_corners.vertex(c)); 442 | ++c; 443 | const vec3& p3 = Geom::mesh_vertex(M, M.facet_corners.vertex(c)); 444 | double lambda1, lambda2, lambda3; // barycentric coords, not used. 445 | squared_dist = Geom::point_triangle_squared_distance( 446 | p, p1, p2, p3, nearest_p, lambda1, lambda2, lambda3 447 | ); 448 | } 449 | } 450 | -------------------------------------------------------------------------------- /app/benchmark/sampling.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | //#include 4 | //#include 5 | namespace fastEnvelope { 6 | sampling::sampling(const std::vector& m_ver, const std::vector& m_faces, const Scalar eps) { 7 | Vector3 min, max; 8 | min = m_ver.front(); 9 | max = m_ver.front(); 10 | 11 | for (size_t j = 0; j < m_ver.size(); j++) 12 | { 13 | for (int i = 0; i < 3; i++) 14 | { 15 | min(i) = std::min(min(i), m_ver[j](i)); 16 | max(i) = std::max(max(i), m_ver[j](i)); 17 | } 18 | } 19 | 20 | 21 | 22 | const Scalar bbd = (max - min).norm(); 23 | const Scalar epsilon = bbd * eps; //eps*bounding box diagnal 24 | 25 | 26 | std::vector faces_new; 27 | 28 | 29 | algorithms::resorting(m_ver, m_faces, faces_new);//resort the facets order 30 | //---- 31 | 32 | 33 | 34 | 35 | algorithms::halfspace_generation(m_ver, faces_new, halfspace, cornerlist, epsilon); 36 | 37 | 38 | 39 | tree.init(cornerlist); 40 | 41 | 42 | //initializing types 43 | } 44 | 45 | 46 | bool sampling::sample_triangle_outside(const std::array &triangle, const int &pieces) const 47 | { 48 | const auto triangle_sample_segment=[](const std::array &triangle, Vector3 &ps, const int &pieces, const int &nbr) 49 | { 50 | int t = pieces - 1; 51 | if (triangle[1] - triangle[0] == Vector3(0, 0, 0)) 52 | { 53 | 54 | ps = (triangle[0] + (triangle[2] - triangle[0]) * nbr / t); 55 | 56 | return; 57 | } 58 | if (triangle[2] - triangle[0] == Vector3(0, 0, 0)) 59 | { 60 | 61 | ps = (triangle[0] + (triangle[1] - triangle[0]) * nbr / t); 62 | 63 | return; 64 | } 65 | if (triangle[2] - triangle[1] == Vector3(0, 0, 0)) 66 | { 67 | 68 | ps = (triangle[0] + (triangle[1] - triangle[0]) * nbr / t); 69 | 70 | return; 71 | } 72 | 73 | Scalar d1 = (triangle[1] - triangle[0]).norm(), d2 = (triangle[2] - triangle[0]).norm(), d3 = (triangle[1] - triangle[2]).norm(); 74 | if (d1 >= d2 && d1 >= d3) 75 | { 76 | ps = (triangle[0] + (triangle[1] - triangle[0]) * nbr / t); 77 | 78 | return; 79 | } 80 | if (d2 >= d1 && d2 >= d3) 81 | { 82 | ps = (triangle[0] + (triangle[2] - triangle[0]) * nbr / t); 83 | 84 | return; 85 | } 86 | if (d3 >= d1 && d3 >= d2) 87 | { 88 | ps = (triangle[1] + (triangle[2] - triangle[1]) * nbr / t); 89 | 90 | return; 91 | } 92 | }; 93 | 94 | const auto triangle_sample_normal = [](const std::array &triangle, Vector3 &ps, const int &pieces, const int &nbr1, const int &nbr2) 95 | { 96 | int l1s = pieces - 1; // 97 | Vector3 p1 = triangle[0] + (triangle[1] - triangle[0]) * nbr1 / l1s, d = (triangle[2] - triangle[1]) / l1s; 98 | ps = p1 + d * nbr2; 99 | }; 100 | 101 | //const auto triangle_sample_normal_rational = [](const std::array &triangle, Rational &ps0, Rational &ps1, Rational &ps2, const int &pieces, const int &nbr1, const int &nbr2) 102 | //{ 103 | // int l1s = pieces - 1; // 104 | // Rational t00(triangle[0][0]), t01(triangle[0][1]), t02(triangle[0][2]), t10(triangle[1][0]), t11(triangle[1][1]), 105 | // t12(triangle[1][2]), t20(triangle[2][0]), t21(triangle[2][1]), t22(triangle[2][2]), nbr1r(nbr1), nbr2r(nbr2), l1sr(l1s); 106 | 107 | // Rational p0 = t00 + (t10 - t00) * nbr1r / l1sr, d0 = (t20 - t10) / l1sr; 108 | // Rational p1 = t01 + (t11 - t01) * nbr1r / l1sr, d1 = (t21 - t11) / l1sr; 109 | // Rational p2 = t02 + (t12 - t02) * nbr1r / l1sr, d2 = (t22 - t12) / l1sr; 110 | // ps0 = p0 + d0 * nbr2; 111 | // ps1 = p1 + d1 * nbr2; 112 | // ps2 = p2 + d2 * nbr2; 113 | //}; 114 | 115 | bool out; 116 | Vector3 point; 117 | std::vector querylist; 118 | tree.triangle_find_bbox(triangle[0], triangle[1], triangle[2], querylist); 119 | int jump = -1; 120 | if (querylist.size() == 0) 121 | return 1; 122 | 123 | int deg = algorithms::is_triangle_degenerated(triangle[0], triangle[1], triangle[2]); 124 | 125 | if (deg == DEGENERATED_POINT) 126 | { 127 | out = point_out_prism(triangle[0], querylist, jump); 128 | if (out == true) 129 | { 130 | 131 | return 1; 132 | } 133 | return 0; 134 | } 135 | if (deg == DEGENERATED_SEGMENT) 136 | { 137 | for (int i = 0; i < pieces; i++) 138 | { 139 | triangle_sample_segment(triangle, point, pieces, i); 140 | out = point_out_prism(point, querylist, jump); 141 | if (out == true) 142 | { 143 | 144 | return 1; 145 | } 146 | } 147 | return 0; 148 | } 149 | 150 | for (int i = 0; i < pieces; i++) 151 | { 152 | for (int j = 0; j <= i; j++) 153 | { 154 | triangle_sample_normal(triangle, point, pieces, i, j); 155 | out = point_out_prism(point, querylist, jump); 156 | if (out == true) 157 | { 158 | 159 | return 1; 160 | } 161 | } 162 | } 163 | 164 | return 0; 165 | } 166 | bool sampling::point_out_prism(const Vector3 &point, const std::vector &prismindex, const int &jump) const 167 | { 168 | 169 | int ori; 170 | 171 | for (int i = 0; i < prismindex.size(); i++) 172 | { 173 | if (prismindex[i] == jump) 174 | continue; 175 | 176 | for (int j = 0; j < halfspace[prismindex[i]].size(); j++) 177 | { 178 | 179 | ori = Predicates::orient_3d(halfspace[prismindex[i]][j][0], halfspace[prismindex[i]][j][1], halfspace[prismindex[i]][j][2], point); 180 | if (ori == -1 || ori == 0) 181 | { 182 | break; 183 | } 184 | if (j == halfspace[prismindex[i]].size() - 1) 185 | { 186 | 187 | return false; 188 | } 189 | } 190 | 191 | } 192 | 193 | return true; 194 | } 195 | } 196 | 197 | -------------------------------------------------------------------------------- /app/benchmark/sampling.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | namespace fastEnvelope { 8 | class sampling { 9 | public: 10 | sampling(const std::vector& m_ver, const std::vector& m_faces, const Scalar eps); 11 | bool sample_triangle_outside(const std::array &triangle, const int &pieces) const; 12 | private: 13 | std::vector> cornerlist; 14 | std::vector>> halfspace; 15 | AABB tree; 16 | 17 | 18 | 19 | bool point_out_prism(const Vector3 &point, const std::vector &prismindex, const int &jump) const; 20 | 21 | }; 22 | 23 | 24 | } -------------------------------------------------------------------------------- /app/test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | #include 6 | #include 7 | 8 | 9 | #include 10 | 11 | 12 | #include 13 | 14 | #include 15 | 16 | #ifdef USE_TBB 17 | #include 18 | #endif 19 | 20 | #include 21 | 22 | #include 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | using namespace fastEnvelope; 39 | void pure_our_method(string queryfile, string model, string resultfile, Scalar envsize, bool csv_model) { 40 | std::cout << "running our method" << std::endl; 41 | vector outenvelope; 42 | std::vector env_vertices, v; 43 | std::vector env_faces, f; 44 | GEO::Mesh envmesh, mesh; 45 | 46 | std::vector> triangles; 47 | if (csv_model) { 48 | triangles = read_CSV_triangle(queryfile, outenvelope); 49 | } 50 | else { 51 | bool ok1 = MeshIO::load_mesh(queryfile, v, f, mesh); 52 | if (!ok1) { 53 | std::cout << ("Unable to load query mesh") << std::endl; 54 | return; 55 | } 56 | triangles.resize(f.size()); 57 | for (int i = 0; i < f.size(); i++) { 58 | for (int j = 0; j < 3; j++) { 59 | triangles[i][j][0] = v[f[i][j]][0]; 60 | triangles[i][j][1] = v[f[i][j]][1]; 61 | triangles[i][j][2] = v[f[i][j]][2]; 62 | } 63 | } 64 | } 65 | 66 | bool ok = MeshIO::load_mesh(model, env_vertices, env_faces, envmesh); 67 | if (!ok) { 68 | std::cout << ("Unable to load mesh") << std::endl; 69 | return; 70 | } 71 | 72 | Vector3 min, max; 73 | Scalar eps = envsize; 74 | 75 | Scalar dd; 76 | algorithms::get_bb_corners(env_vertices, min, max); 77 | dd = ((max - min).norm()) *eps; 78 | igl::Timer timer; 79 | timer.start(); 80 | const FastEnvelope fast_envelope(env_vertices, env_faces, dd); 81 | 82 | timer.stop(); 83 | const auto init_time = timer.getElapsedTimeInSec(); 84 | std::cout << "ours initialization time " << timer.getElapsedTimeInSec() << std::endl; 85 | //int fn = triangles.size() > 100000 ? 100000 : triangles.size(); 86 | int fn = triangles.size() > 100000 ? 100000 : triangles.size(); 87 | std::cout << "total query size, " << fn << std::endl; 88 | std::vector results; 89 | results.resize(fn); 90 | timer.start(); 91 | int inbr = 0; 92 | for (int i = 0; i < fn; i++) { 93 | 94 | results[i] = fast_envelope.is_outside(triangles[i]); 95 | if (results[i] == 0) inbr++; 96 | } 97 | timer.stop(); 98 | cout << "ours method time " << timer.getElapsedTimeInSec() << endl; 99 | cout << "memory use, " << getPeakRSS() << std::endl; 100 | cout << "inside percentage, " << float(inbr) / float(fn) << std::endl; 101 | std::cout << "inside number, " << inbr << std::endl; 102 | if (inbr == 97688) { 103 | 104 | std::cout << "\n\n\nthe results are correct\n\n\n" << std::endl; 105 | } 106 | else { 107 | std::cout << "\n\n\nTHIS TEST IS WRONG! the number of queries that are inside envelope should be 97688\n\n\n" << std::endl; 108 | throw "\n\n\nWRONG ANSWERS\n\n\n"; 109 | } 110 | std::ofstream fout; 111 | fout.open(resultfile + ".json"); 112 | fout << "{\n"; 113 | 114 | fout << "\"method\": " << "\"ours\"" << ",\n"; 115 | fout << "\"init_time\": " << init_time << ",\n"; 116 | fout << "\"query_time\": " << timer.getElapsedTimeInSec() << ",\n"; 117 | fout << "\"memory\": " << getPeakRSS() << ",\n"; 118 | fout << "\"inside\": " << double(inbr) / double(fn) << ",\n"; 119 | fout << "\"queries\": " << fn << ",\n"; 120 | fout << "\"vertices\": " << env_vertices.size() << ",\n"; 121 | fout << "\"facets\": " << env_faces.size() << "\n"; 122 | fout << "}"; 123 | fout.close(); 124 | fast_envelope.printnumber(); 125 | 126 | 127 | 128 | 129 | fout.open(resultfile); 130 | fout << "results" << endl; 131 | for (int i = 0; i < fn; i++) { 132 | 133 | fout << results[i] << endl; 134 | 135 | } 136 | fout.close(); 137 | std::cout << model << " done! " << std::endl; 138 | } 139 | 140 | void test_initialization(const std::string& filename ) { 141 | std::vector env_vertices; 142 | std::vector env_faces; 143 | GEO::Mesh envmesh; 144 | bool ok = MeshIO::load_mesh(filename, env_vertices, env_faces, envmesh); 145 | if (!ok) { 146 | std::cout << ("Unable to load mesh") << std::endl; 147 | return; 148 | } 149 | 150 | Vector3 min, max; 151 | Scalar eps = 1e-3; 152 | 153 | Scalar dd; 154 | algorithms::get_bb_corners(env_vertices, min, max); 155 | dd = ((max - min).norm()) *eps; 156 | std::cout << "bounding box \nmin " << min.transpose() << "\nmax" << max.transpose() << std::endl; 157 | const FastEnvelope fast_envelope(env_vertices, env_faces, dd); 158 | 159 | } 160 | int main(int argc, char const *argv[]) { 161 | #ifndef WIN32 162 | setenv("GEO_NO_SIGNAL_HANDLER", "1", 1); 163 | #endif 164 | GEO::initialize(0); 165 | GEO::CmdLine::import_arg_group("standard"); 166 | GEO::CmdLine::import_arg_group("pre"); 167 | GEO::CmdLine::import_arg_group("algo"); 168 | #ifdef ENVELOPE_WITH_GMP 169 | std::cout << "using RATIONAL calculation in GMP" << std::endl; 170 | #endif 171 | 172 | /*const string filename = "D:\\vs/envelope_x64\\Debug\\M-L-Femur.stl"; 173 | test_initialization(filename);*/ 174 | std::string datapath = ENVELOPE_TEST_DATA_DIR; 175 | std::cout << "Running the test dataset.\ndata path," << datapath << std::endl; 176 | string query = datapath + "63465.stl_envelope_log.csv"; 177 | string model = datapath + "63465.stl"; 178 | string resultfile= datapath + "63465_result.csv"; 179 | Scalar scale_ratio = 1e-3; 180 | bool csv_model = true; 181 | pure_our_method(query, model, resultfile, scale_ratio, csv_model); 182 | 183 | 184 | return 0; 185 | } -------------------------------------------------------------------------------- /app/test_data/63465.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangbolun300/fast-envelope/8b246ba44963631aa33483cc96bc63816aaf0cf0/app/test_data/63465.stl -------------------------------------------------------------------------------- /app/test_data/63465_result.csv.json: -------------------------------------------------------------------------------- 1 | { 2 | "method": "ours", 3 | "init_time": 0.0010127, 4 | "query_time": 5.07593, 5 | "memory": 20344832, 6 | "inside": 0.97688, 7 | "queries": 100000, 8 | "vertices": 3546, 9 | "facets": 1182 10 | } -------------------------------------------------------------------------------- /bunny.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangbolun300/fast-envelope/8b246ba44963631aa33483cc96bc63816aaf0cf0/bunny.jpg -------------------------------------------------------------------------------- /cmake/CXXFeatures.cmake: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | 3 | if(NOT (${CMAKE_VERSION} VERSION_LESS "3.8.0")) 4 | # For CMake 3.8 and above, we can use meta features directly provided by CMake itself 5 | set(CXX11_FEATURES cxx_std_11) 6 | set(CXX14_FEATURES cxx_std_14) 7 | set(CXX17_FEATURES cxx_std_17) 8 | return() 9 | endif() 10 | 11 | ################################################################################ 12 | 13 | set(CXX11_FEATURES 14 | cxx_auto_type 15 | cxx_constexpr 16 | ) 17 | 18 | set(CXX14_FEATURES 19 | cxx_generic_lambdas 20 | ) 21 | 22 | set(CXX17_FEATURES 23 | cxx_generic_lambdas 24 | ) 25 | 26 | ################################################################################ 27 | 28 | # https://cmake.org/cmake/help/v3.1/prop_gbl/CMAKE_CXX_KNOWN_FEATURES.html 29 | # cxx_aggregate_default_initializers Aggregate default initializers, as defined in N3605. 30 | # cxx_alias_templates Template aliases, as defined in N2258. 31 | # cxx_alignas Alignment control alignas, as defined in N2341. 32 | # cxx_alignof Alignment control alignof, as defined in N2341. 33 | # cxx_attributes Generic attributes, as defined in N2761. 34 | # cxx_attribute_deprecated deprecated]] attribute, as defined in N3760. 35 | # cxx_auto_type Automatic type deduction, as defined in N1984. 36 | # cxx_binary_literals Binary literals, as defined in N3472. 37 | # cxx_constexpr Constant expressions, as defined in N2235. 38 | # cxx_contextual_conversions Contextual conversions, as defined in N3323. 39 | # cxx_decltype_incomplete_return_types Decltype on incomplete return types, as defined in N3276. 40 | # cxx_decltype Decltype, as defined in N2343. 41 | # cxx_decltype_auto decltype(auto) semantics, as defined in N3638. 42 | # cxx_default_function_template_args Default template arguments for function templates, as defined in DR226 43 | # cxx_defaulted_functions Defaulted functions, as defined in N2346. 44 | # cxx_defaulted_move_initializers Defaulted move initializers, as defined in N3053. 45 | # cxx_delegating_constructors Delegating constructors, as defined in N1986. 46 | # cxx_deleted_functions Deleted functions, as defined in N2346. 47 | # cxx_digit_separators Digit separators, as defined in N3781. 48 | # cxx_enum_forward_declarations Enum forward declarations, as defined in N2764. 49 | # cxx_explicit_conversions Explicit conversion operators, as defined in N2437. 50 | # cxx_extended_friend_declarations Extended friend declarations, as defined in N1791. 51 | # cxx_extern_templates Extern templates, as defined in N1987. 52 | # cxx_final Override control final keyword, as defined in N2928, N3206 and N3272. 53 | # cxx_func_identifier Predefined __func__ identifier, as defined in N2340. 54 | # cxx_generalized_initializers Initializer lists, as defined in N2672. 55 | # cxx_generic_lambdas Generic lambdas, as defined in N3649. 56 | # cxx_inheriting_constructors Inheriting constructors, as defined in N2540. 57 | # cxx_inline_namespaces Inline namespaces, as defined in N2535. 58 | # cxx_lambdas Lambda functions, as defined in N2927. 59 | # cxx_lambda_init_captures Initialized lambda captures, as defined in N3648. 60 | # cxx_local_type_template_args Local and unnamed types as template arguments, as defined in N2657. 61 | # cxx_long_long_type long long type, as defined in N1811. 62 | # cxx_noexcept Exception specifications, as defined in N3050. 63 | # cxx_nonstatic_member_init Non-static data member initialization, as defined in N2756. 64 | # cxx_nullptr Null pointer, as defined in N2431. 65 | # cxx_override Override control override keyword, as defined in N2928, N3206 and N3272. 66 | # cxx_range_for Range-based for, as defined in N2930. 67 | # cxx_raw_string_literals Raw string literals, as defined in N2442. 68 | # cxx_reference_qualified_functions Reference qualified functions, as defined in N2439. 69 | # cxx_relaxed_constexpr Relaxed constexpr, as defined in N3652. 70 | # cxx_return_type_deduction Return type deduction on normal functions, as defined in N3386. 71 | # cxx_right_angle_brackets Right angle bracket parsing, as defined in N1757. 72 | # cxx_rvalue_references R-value references, as defined in N2118. 73 | # cxx_sizeof_member Size of non-static data members, as defined in N2253. 74 | # cxx_static_assert Static assert, as defined in N1720. 75 | # cxx_strong_enums Strongly typed enums, as defined in N2347. 76 | # cxx_thread_local Thread-local variables, as defined in N2659. 77 | # cxx_trailing_return_types Automatic function return type, as defined in N2541. 78 | # cxx_unicode_literals Unicode string literals, as defined in N2442. 79 | # cxx_uniform_initialization Uniform intialization, as defined in N2640. 80 | # cxx_unrestricted_unions Unrestricted unions, as defined in N2544. 81 | # cxx_user_literals User-defined literals, as defined in N2765. 82 | # cxx_variable_templates Variable templates, as defined in N3651. 83 | # cxx_variadic_macros Variadic macros, as defined in N1653. 84 | # cxx_variadic_templates Variadic templates, as defined in N2242. 85 | # cxx_template_template_parameters Template template parameters, as defined in ISO/IEC 14882:1998. 86 | -------------------------------------------------------------------------------- /cmake/DownloadProject.CMakeLists.cmake.in: -------------------------------------------------------------------------------- 1 | # Distributed under the OSI-approved MIT License. See accompanying 2 | # file LICENSE or https://github.com/Crascit/DownloadProject for details. 3 | 4 | cmake_minimum_required(VERSION 2.8.2) 5 | 6 | project(${DL_ARGS_PROJ}-download NONE) 7 | 8 | include(ExternalProject) 9 | ExternalProject_Add(${DL_ARGS_PROJ}-download 10 | ${DL_ARGS_UNPARSED_ARGUMENTS} 11 | SOURCE_DIR "${DL_ARGS_SOURCE_DIR}" 12 | BINARY_DIR "${DL_ARGS_BINARY_DIR}" 13 | CONFIGURE_COMMAND "" 14 | BUILD_COMMAND "" 15 | INSTALL_COMMAND "" 16 | TEST_COMMAND "" 17 | ) 18 | -------------------------------------------------------------------------------- /cmake/DownloadProject.cmake: -------------------------------------------------------------------------------- 1 | # Distributed under the OSI-approved MIT License. See accompanying 2 | # file LICENSE or https://github.com/Crascit/DownloadProject for details. 3 | # 4 | # MODULE: DownloadProject 5 | # 6 | # PROVIDES: 7 | # download_project( PROJ projectName 8 | # [PREFIX prefixDir] 9 | # [DOWNLOAD_DIR downloadDir] 10 | # [SOURCE_DIR srcDir] 11 | # [BINARY_DIR binDir] 12 | # [QUIET] 13 | # ... 14 | # ) 15 | # 16 | # Provides the ability to download and unpack a tarball, zip file, git repository, 17 | # etc. at configure time (i.e. when the cmake command is run). How the downloaded 18 | # and unpacked contents are used is up to the caller, but the motivating case is 19 | # to download source code which can then be included directly in the build with 20 | # add_subdirectory() after the call to download_project(). Source and build 21 | # directories are set up with this in mind. 22 | # 23 | # The PROJ argument is required. The projectName value will be used to construct 24 | # the following variables upon exit (obviously replace projectName with its actual 25 | # value): 26 | # 27 | # projectName_SOURCE_DIR 28 | # projectName_BINARY_DIR 29 | # 30 | # The SOURCE_DIR and BINARY_DIR arguments are optional and would not typically 31 | # need to be provided. They can be specified if you want the downloaded source 32 | # and build directories to be located in a specific place. The contents of 33 | # projectName_SOURCE_DIR and projectName_BINARY_DIR will be populated with the 34 | # locations used whether you provide SOURCE_DIR/BINARY_DIR or not. 35 | # 36 | # The DOWNLOAD_DIR argument does not normally need to be set. It controls the 37 | # location of the temporary CMake build used to perform the download. 38 | # 39 | # The PREFIX argument can be provided to change the base location of the default 40 | # values of DOWNLOAD_DIR, SOURCE_DIR and BINARY_DIR. If all of those three arguments 41 | # are provided, then PREFIX will have no effect. The default value for PREFIX is 42 | # CMAKE_BINARY_DIR. 43 | # 44 | # The QUIET option can be given if you do not want to show the output associated 45 | # with downloading the specified project. 46 | # 47 | # In addition to the above, any other options are passed through unmodified to 48 | # ExternalProject_Add() to perform the actual download, patch and update steps. 49 | # The following ExternalProject_Add() options are explicitly prohibited (they 50 | # are reserved for use by the download_project() command): 51 | # 52 | # CONFIGURE_COMMAND 53 | # BUILD_COMMAND 54 | # INSTALL_COMMAND 55 | # TEST_COMMAND 56 | # 57 | # Only those ExternalProject_Add() arguments which relate to downloading, patching 58 | # and updating of the project sources are intended to be used. Also note that at 59 | # least one set of download-related arguments are required. 60 | # 61 | # If using CMake 3.2 or later, the UPDATE_DISCONNECTED option can be used to 62 | # prevent a check at the remote end for changes every time CMake is run 63 | # after the first successful download. See the documentation of the ExternalProject 64 | # module for more information. It is likely you will want to use this option if it 65 | # is available to you. Note, however, that the ExternalProject implementation contains 66 | # bugs which result in incorrect handling of the UPDATE_DISCONNECTED option when 67 | # using the URL download method or when specifying a SOURCE_DIR with no download 68 | # method. Fixes for these have been created, the last of which is scheduled for 69 | # inclusion in CMake 3.8.0. Details can be found here: 70 | # 71 | # https://gitlab.kitware.com/cmake/cmake/commit/bdca68388bd57f8302d3c1d83d691034b7ffa70c 72 | # https://gitlab.kitware.com/cmake/cmake/issues/16428 73 | # 74 | # If you experience build errors related to the update step, consider avoiding 75 | # the use of UPDATE_DISCONNECTED. 76 | # 77 | # EXAMPLE USAGE: 78 | # 79 | # include(DownloadProject) 80 | # download_project(PROJ googletest 81 | # GIT_REPOSITORY https://github.com/google/googletest.git 82 | # GIT_TAG master 83 | # UPDATE_DISCONNECTED 1 84 | # QUIET 85 | # ) 86 | # 87 | # add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR}) 88 | # 89 | #======================================================================================== 90 | 91 | 92 | set(_DownloadProjectDir "${CMAKE_CURRENT_LIST_DIR}") 93 | 94 | include(CMakeParseArguments) 95 | 96 | function(download_project) 97 | 98 | set(options QUIET) 99 | set(oneValueArgs 100 | PROJ 101 | PREFIX 102 | DOWNLOAD_DIR 103 | SOURCE_DIR 104 | BINARY_DIR 105 | # Prevent the following from being passed through 106 | CONFIGURE_COMMAND 107 | BUILD_COMMAND 108 | INSTALL_COMMAND 109 | TEST_COMMAND 110 | ) 111 | set(multiValueArgs "") 112 | 113 | cmake_parse_arguments(DL_ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) 114 | 115 | # Hide output if requested 116 | if (DL_ARGS_QUIET) 117 | set(OUTPUT_QUIET "OUTPUT_QUIET") 118 | else() 119 | unset(OUTPUT_QUIET) 120 | message(STATUS "Downloading/updating ${DL_ARGS_PROJ}") 121 | endif() 122 | 123 | # Set up where we will put our temporary CMakeLists.txt file and also 124 | # the base point below which the default source and binary dirs will be. 125 | # The prefix must always be an absolute path. 126 | if (NOT DL_ARGS_PREFIX) 127 | set(DL_ARGS_PREFIX "${CMAKE_BINARY_DIR}") 128 | else() 129 | get_filename_component(DL_ARGS_PREFIX "${DL_ARGS_PREFIX}" ABSOLUTE 130 | BASE_DIR "${CMAKE_CURRENT_BINARY_DIR}") 131 | endif() 132 | if (NOT DL_ARGS_DOWNLOAD_DIR) 133 | set(DL_ARGS_DOWNLOAD_DIR "${DL_ARGS_PREFIX}/${DL_ARGS_PROJ}-download") 134 | endif() 135 | 136 | # Ensure the caller can know where to find the source and build directories 137 | if (NOT DL_ARGS_SOURCE_DIR) 138 | set(DL_ARGS_SOURCE_DIR "${DL_ARGS_PREFIX}/${DL_ARGS_PROJ}-src") 139 | endif() 140 | if (NOT DL_ARGS_BINARY_DIR) 141 | set(DL_ARGS_BINARY_DIR "${DL_ARGS_PREFIX}/${DL_ARGS_PROJ}-build") 142 | endif() 143 | set(${DL_ARGS_PROJ}_SOURCE_DIR "${DL_ARGS_SOURCE_DIR}" PARENT_SCOPE) 144 | set(${DL_ARGS_PROJ}_BINARY_DIR "${DL_ARGS_BINARY_DIR}" PARENT_SCOPE) 145 | 146 | # The way that CLion manages multiple configurations, it causes a copy of 147 | # the CMakeCache.txt to be copied across due to it not expecting there to 148 | # be a project within a project. This causes the hard-coded paths in the 149 | # cache to be copied and builds to fail. To mitigate this, we simply 150 | # remove the cache if it exists before we configure the new project. It 151 | # is safe to do so because it will be re-generated. Since this is only 152 | # executed at the configure step, it should not cause additional builds or 153 | # downloads. 154 | file(REMOVE "${DL_ARGS_DOWNLOAD_DIR}/CMakeCache.txt") 155 | 156 | # Create and build a separate CMake project to carry out the download. 157 | # If we've already previously done these steps, they will not cause 158 | # anything to be updated, so extra rebuilds of the project won't occur. 159 | # Make sure to pass through CMAKE_MAKE_PROGRAM in case the main project 160 | # has this set to something not findable on the PATH. 161 | configure_file("${_DownloadProjectDir}/DownloadProject.CMakeLists.cmake.in" 162 | "${DL_ARGS_DOWNLOAD_DIR}/CMakeLists.txt") 163 | execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" 164 | -D "CMAKE_MAKE_PROGRAM:FILE=${CMAKE_MAKE_PROGRAM}" 165 | . 166 | RESULT_VARIABLE result 167 | ${OUTPUT_QUIET} 168 | WORKING_DIRECTORY "${DL_ARGS_DOWNLOAD_DIR}" 169 | ) 170 | if(result) 171 | message(FATAL_ERROR "CMake step for ${DL_ARGS_PROJ} failed: ${result}") 172 | endif() 173 | execute_process(COMMAND ${CMAKE_COMMAND} --build . 174 | RESULT_VARIABLE result 175 | ${OUTPUT_QUIET} 176 | WORKING_DIRECTORY "${DL_ARGS_DOWNLOAD_DIR}" 177 | ) 178 | if(result) 179 | message(FATAL_ERROR "Build step for ${DL_ARGS_PROJ} failed: ${result}") 180 | endif() 181 | 182 | endfunction() 183 | -------------------------------------------------------------------------------- /cmake/FastEnvelopeDependencies.cmake: -------------------------------------------------------------------------------- 1 | # Prepare dependencies 2 | # 3 | # For each third-party library, if the appropriate target doesn't exist yet, 4 | # download it via external project, and add_subdirectory to build it alongside 5 | # this project. 6 | 7 | ### Configuration 8 | set(FAST_ENVELOPE_ROOT "${CMAKE_CURRENT_LIST_DIR}/..") 9 | set(FAST_ENVELOPE_EXTERNAL ${THIRD_PARTY_DIR}) 10 | 11 | # Download and update 3rdparty libraries 12 | list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}) 13 | list(REMOVE_DUPLICATES CMAKE_MODULE_PATH) 14 | include(FastEnvelopeDownloadExternal) 15 | 16 | ################################################################################ 17 | # Required libraries 18 | ################################################################################ 19 | 20 | # Sanitizers 21 | if(FAST_ENVELOPE_WITH_SANITIZERS) 22 | fast_envelope_download_sanitizers() 23 | find_package(Sanitizers) 24 | endif() 25 | 26 | # CL11 27 | if(NOT TARGET CLI11::CLI11) 28 | fast_envelope_download_cli11() 29 | add_subdirectory(${FAST_ENVELOPE_EXTERNAL}/cli11) 30 | endif() 31 | 32 | # spdlog 33 | if(NOT TARGET spdlog::spdlog) 34 | fast_envelope_download_spdlog() 35 | add_subdirectory(${FAST_ENVELOPE_EXTERNAL}/spdlog) 36 | endif() 37 | 38 | 39 | -------------------------------------------------------------------------------- /cmake/FastEnvelopeDownloadExternal.cmake: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | include(DownloadProject) 3 | 4 | # With CMake 3.8 and above, we can hide warnings about git being in a 5 | # detached head by passing an extra GIT_CONFIG option 6 | if(NOT (${CMAKE_VERSION} VERSION_LESS "3.8.0")) 7 | set(FAST_ENVELOPE_EXTRA_OPTIONS "GIT_CONFIG advice.detachedHead=false") 8 | else() 9 | set(FAST_ENVELOPE_EXTRA_OPTIONS "") 10 | endif() 11 | 12 | # Shortcut function 13 | function(fast_envelope_download_project name) 14 | download_project( 15 | PROJ ${name} 16 | SOURCE_DIR ${FAST_ENVELOPE_EXTERNAL}/${name} 17 | DOWNLOAD_DIR ${FAST_ENVELOPE_EXTERNAL}/.cache/${name} 18 | QUIET 19 | ${FAST_ENVELOPE_EXTRA_OPTIONS} 20 | ${ARGN} 21 | ) 22 | endfunction() 23 | 24 | ################################################################################ 25 | 26 | ## libigl 27 | function(fast_envelope_download_libigl) 28 | fast_envelope_download_project(libigl 29 | GIT_REPOSITORY https://github.com/libigl/libigl.git 30 | GIT_TAG fc7f4b99415e0b0e3c4109d8e55b7ff6a5fc8b72 31 | ) 32 | endfunction() 33 | 34 | 35 | ## CLI11 36 | function(fast_envelope_download_cli11) 37 | fast_envelope_download_project(cli11 38 | URL https://github.com/CLIUtils/CLI11/archive/v1.6.1.tar.gz 39 | URL_MD5 48ef97262adb0b47a2f0a7edbda6e2aa 40 | ) 41 | endfunction() 42 | 43 | ## tbb 44 | function(fast_envelope_download_tbb) 45 | fast_envelope_download_project(tbb 46 | GIT_REPOSITORY https://github.com/wjakob/tbb.git 47 | GIT_TAG 08b4341a1893a72656467e96137f1f99d0112547 48 | ) 49 | endfunction() 50 | 51 | 52 | ## Sanitizers 53 | function(fast_envelope_download_sanitizers) 54 | fast_envelope_download_project(sanitizers-cmake 55 | GIT_REPOSITORY https://github.com/arsenm/sanitizers-cmake.git 56 | GIT_TAG 6947cff3a9c9305eb9c16135dd81da3feb4bf87f 57 | ) 58 | endfunction() 59 | 60 | ## spdlog 61 | function(fast_envelope_download_spdlog) 62 | fast_envelope_download_project(spdlog 63 | GIT_REPOSITORY https://github.com/gabime/spdlog.git 64 | GIT_TAG 188cff7d6567b80c6b99bc15899fef9637a8fe52 65 | ) 66 | endfunction() 67 | 68 | ## Geogram LGPL 69 | function(fast_envelope_download_geogram) 70 | fast_envelope_download_project(geogram 71 | GIT_REPOSITORY https://github.com/polyfem/geogram.git 72 | GIT_TAG 516c151c244d9019a9076a1a468d52a0f6dd195d 73 | ) 74 | endfunction() 75 | 76 | 77 | # ## Geogram predicates LGPL 78 | # function(fast_envelope_download_geogram_predicates) 79 | # fast_envelope_download_project(geogram_predicates 80 | # URL https://gforge.inria.fr/frs/download.php/file/38199/Predicates_psm_1.7.2.tar.gz 81 | # URL_MD5 4635a4e19538514ee2d6183af791e280 82 | # ) 83 | # endfunction() 84 | -------------------------------------------------------------------------------- /cmake/FastEnvelopeUtils.cmake: -------------------------------------------------------------------------------- 1 | # Copy fast envelope header files into the build directory 2 | function(fast_envelope_copy_headers) 3 | foreach(filepath IN ITEMS ${ARGN}) 4 | get_filename_component(filename "${filepath}" NAME) 5 | if(${filename} MATCHES ".*\.(hpp|h|ipp)$") 6 | configure_file(${filepath} ${PROJECT_BINARY_DIR}/include/fastenvelope/${filename}) 7 | endif() 8 | endforeach() 9 | endfunction() 10 | 11 | 12 | function(indirect_predicates_copy_headers) 13 | foreach(filepath IN ITEMS ${ARGN}) 14 | get_filename_component(filename "${filepath}" NAME) 15 | if(${filename} MATCHES ".*\.(hpp|h|ipp)$") 16 | configure_file(${filepath} ${PROJECT_BINARY_DIR}/include/indirectpredicates/${filename}) 17 | endif() 18 | endforeach() 19 | endfunction() 20 | 21 | # Set source group for IDE like Visual Studio or XCode 22 | function(fast_envelope_set_source_group) 23 | foreach(filepath IN ITEMS ${ARGN}) 24 | get_filename_component(folderpath "${filepath}" DIRECTORY) 25 | get_filename_component(foldername "${folderpath}" NAME) 26 | source_group(foldername FILES "${filepath}") 27 | endforeach() 28 | endfunction() 29 | 30 | 31 | # Add an application from one source 32 | function(fast_envelope_add_application APP_SOURCE) 33 | # Add executable 34 | get_filename_component(APP_NAME ${APP_SOURCE} NAME_WE) 35 | add_executable(${APP_NAME} ${APP_SOURCE}) 36 | message(STATUS "Compiling single-source application: ${APP_NAME}") 37 | 38 | # Dependencies 39 | target_link_libraries(${APP_NAME} PRIVATE ${ARGN}) 40 | 41 | # Output directory for binaries 42 | set_target_properties(${APP_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}") 43 | 44 | if(FAST_ENVELOPE_WITH_SANITIZERS) 45 | add_sanitizers(${APP_NAME}) 46 | endif() 47 | endfunction() -------------------------------------------------------------------------------- /cmake/FindGMP.cmake: -------------------------------------------------------------------------------- 1 | # Try to find the GMP librairies 2 | # GMP_FOUND - system has GMP lib 3 | # GMP_INCLUDE_DIRS - the GMP include directory 4 | # GMP_LIBRARIES - Libraries needed to use GMP 5 | 6 | if (GMP_INCLUDE_DIRS AND GMP_LIBRARIES) 7 | # Already in cache, be silent 8 | set(GMP_FIND_QUIETLY TRUE) 9 | endif (GMP_INCLUDE_DIRS AND GMP_LIBRARIES) 10 | 11 | 12 | 13 | #if(WIN32) 14 | # if(CYGWIN) 15 | # triwild_download_gmp_cygwin() 16 | #elseif(MINGW) 17 | # triwild_download_gmp_mingw() 18 | #else() 19 | # triwild_download_gmp_vc() 20 | #endif() 21 | 22 | #SET(GMP_WINDOWS_PATH ${THIRD_PARTY_DIR}/gmp) 23 | #endif() 24 | 25 | 26 | find_path(GMP_INCLUDE_DIRS NAMES gmp.h PATHS $ENV{GMP_INC} ${GMP_WINDOWS_PATH}) 27 | find_library(GMP_LIBRARIES NAMES gmp libgmp PATHS $ENV{GMP_LIB} ${GMP_WINDOWS_PATH}) 28 | find_library(GMPXX_LIBRARIES NAMES gmpxx libgmpxx PATHS $ENV{GMP_LIB} ${GMP_WINDOWS_PATH}) 29 | #MESSAGE(STATUS "GMP libs: " ${GMP_LIBRARIES} " " ${GMPXX_LIBRARIES} ) 30 | 31 | include(FindPackageHandleStandardArgs) 32 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(GMP DEFAULT_MSG GMP_INCLUDE_DIRS GMP_LIBRARIES) 33 | 34 | mark_as_advanced(GMP_INCLUDE_DIRS GMP_LIBRARIES) 35 | MESSAGE(STATUS "GMP libs: " ${GMP_LIBRARIES} " " ${GMP_INCLUDE_DIRS} ) -------------------------------------------------------------------------------- /cmake/FindLIBIGL.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find the LIBIGL library 2 | # Once done this will define 3 | # 4 | # LIBIGL_FOUND - system has LIBIGL 5 | # LIBIGL_INCLUDE_DIR - **the** LIBIGL include directory 6 | if(LIBIGL_FOUND) 7 | return() 8 | endif() 9 | 10 | find_path(LIBIGL_INCLUDE_DIR igl/readOBJ.h 11 | HINTS 12 | ${THIRD_PARTY_DIR}/libigl 13 | PATHS 14 | ENV LIBIGL 15 | ENV LIBIGLROOT 16 | ENV LIBIGL_ROOT 17 | ENV LIBIGL_DIR 18 | ${CMAKE_SOURCE_DIR}/../.. 19 | ${CMAKE_SOURCE_DIR}/.. 20 | ${CMAKE_SOURCE_DIR} 21 | ${CMAKE_SOURCE_DIR}/libigl 22 | ${CMAKE_SOURCE_DIR}/../libigl 23 | ${CMAKE_SOURCE_DIR}/../../libigl 24 | /usr 25 | /usr/local 26 | /usr/local/igl/libigl 27 | PATH_SUFFIXES include 28 | ) 29 | 30 | include(FindPackageHandleStandardArgs) 31 | find_package_handle_standard_args(LIBIGL 32 | "\nlibigl not found --- You can download it using:\n\tgit clone --recursive https://github.com/libigl/libigl.git ${CMAKE_SOURCE_DIR}/../libigl" 33 | LIBIGL_INCLUDE_DIR) 34 | mark_as_advanced(LIBIGL_INCLUDE_DIR) 35 | 36 | list(APPEND CMAKE_MODULE_PATH "${LIBIGL_INCLUDE_DIR}/../cmake") 37 | include(libigl) 38 | -------------------------------------------------------------------------------- /cmake/PrependCurrentPath.cmake: -------------------------------------------------------------------------------- 1 | function(prepend_current_path SOURCE_FILES) 2 | # Use recursive substitution to expand SOURCE_FILES 3 | unset(MODIFIED) 4 | foreach(SOURCE_FILE IN ITEMS ${${SOURCE_FILES}}) 5 | list(APPEND MODIFIED "${CMAKE_CURRENT_SOURCE_DIR}/${SOURCE_FILE}") 6 | endforeach() 7 | set(${SOURCE_FILES} ${MODIFIED} PARENT_SCOPE) 8 | endfunction() 9 | -------------------------------------------------------------------------------- /cmake/UseColors.cmake: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # When using Clang, there is nothing to do: colors are enabled by default 3 | # When using GCC >= 4.9, colored diagnostics can be enabled natively 4 | # When using an older version, one can use gccfilter (a perl script) 5 | # 6 | # I do not recommend using gccfilter as of now (May 2014), because it seems to 7 | # be bugged. But if you still want to try, here is how to install it on Ubuntu: 8 | # 9 | # 10 | # 1) Download the perl script and add it to you $PATH 11 | # mkdir -p ~/.local/bin 12 | # wget -P ~/.local/bin http://www.mixtion.org/gccfilter/gccfilter 13 | # chmod +x ~/local/bin/gccfilter 14 | # echo 'PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc 15 | # 16 | # 2) Install the dependencies 17 | # * Term::ANSIColor 18 | # sudo cpan 19 | # cpan> install Term::ANSIColor 20 | # * The module "Getopt::Long" is included in "perl-base" 21 | # * For Getopt::ArgvFile and Regexp::Common ... 22 | # sudo apt-get install libgetopt-argvfile-perl libregexp-common-perl 23 | # 24 | ################################################################################ 25 | 26 | if(CMAKE_COMPILER_IS_GNUCXX) 27 | # If GCC >= 4.9, just activate the right option 28 | # We enable colorized diagnostics always instead of using "auto" so that 29 | # they're still colored when using Ninja. 30 | if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9) 31 | message(STATUS "GCC >= 4.9 detected, enabling colored diagnostics") 32 | add_definitions(-fdiagnostics-color=always) 33 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdiagnostics-color=always") 34 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fdiagnostics-color=always") 35 | return() 36 | endif() 37 | # If GCC < 4.9, maybe we can use gccfilter 38 | find_program(GCC_FILTER gccfilter) 39 | if(GCC_FILTER) 40 | option(COLOR_GCC "Use GCCFilter to color compiler output messages" OFF) 41 | set(COLOR_GCC_OPTIONS "-c -r -w" CACHE STRING "Arguments that are passed to gccfilter when output coloring is switchend on. Defaults to -c -r -w.") 42 | if(COLOR_GCC) 43 | set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${GCC_FILTER} ${COLOR_GCC_OPTIONS}") 44 | message(STATUS "Using gccfilter for colored diagnostics") 45 | endif() 46 | endif() 47 | endif() 48 | -------------------------------------------------------------------------------- /cmake/Warnings.cmake: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | cmake_minimum_required(VERSION 3.11) 3 | ################################################################################ 4 | # See comments and discussions here: 5 | # http://stackoverflow.com/questions/5088460/flags-to-enable-thorough-and-verbose-g-warnings 6 | ################################################################################ 7 | 8 | if(TARGET warnings::all) 9 | return() 10 | endif() 11 | 12 | set(MY_FLAGS 13 | -Wall 14 | -Wextra 15 | -pedantic 16 | 17 | # -Wconversion 18 | #-Wunsafe-loop-optimizations # broken with C++11 loops 19 | -Wunused 20 | 21 | -Wno-long-long 22 | -Wpointer-arith 23 | -Wformat=2 24 | -Wuninitialized 25 | -Wcast-qual 26 | -Wmissing-noreturn 27 | -Wmissing-format-attribute 28 | -Wredundant-decls 29 | 30 | -Werror=implicit 31 | -Werror=nonnull 32 | -Werror=init-self 33 | -Werror=main 34 | -Werror=missing-braces 35 | -Werror=sequence-point 36 | -Werror=return-type 37 | -Werror=trigraphs 38 | -Werror=array-bounds 39 | -Werror=write-strings 40 | -Werror=address 41 | -Werror=int-to-pointer-cast 42 | -Werror=pointer-to-int-cast 43 | 44 | -Wno-unused-variable 45 | -Wunused-but-set-variable 46 | -Wno-unused-parameter 47 | 48 | #-Weffc++ 49 | -Wno-old-style-cast 50 | # -Wno-sign-conversion 51 | #-Wsign-conversion 52 | 53 | -Wshadow 54 | 55 | -Wstrict-null-sentinel 56 | -Woverloaded-virtual 57 | -Wsign-promo 58 | -Wstack-protector 59 | -Wstrict-aliasing 60 | -Wstrict-aliasing=2 61 | -Wswitch-default 62 | -Wswitch-enum 63 | -Wswitch-unreachable 64 | 65 | -Wcast-align 66 | -Wdisabled-optimization 67 | #-Winline # produces warning on default implicit destructor 68 | -Winvalid-pch 69 | # -Wmissing-include-dirs 70 | -Wpacked 71 | -Wno-padded 72 | -Wstrict-overflow 73 | -Wstrict-overflow=2 74 | 75 | -Wctor-dtor-privacy 76 | -Wlogical-op 77 | -Wnoexcept 78 | -Woverloaded-virtual 79 | # -Wundef 80 | 81 | -Wnon-virtual-dtor 82 | -Wdelete-non-virtual-dtor 83 | -Werror=non-virtual-dtor 84 | -Werror=delete-non-virtual-dtor 85 | 86 | -Wno-sign-compare 87 | 88 | ########### 89 | # GCC 6.1 # 90 | ########### 91 | 92 | -Wnull-dereference 93 | -fdelete-null-pointer-checks 94 | -Wduplicated-cond 95 | -Wmisleading-indentation 96 | 97 | #-Weverything 98 | 99 | ########################### 100 | # Enabled by -Weverything # 101 | ########################### 102 | 103 | #-Wdocumentation 104 | #-Wdocumentation-unknown-command 105 | #-Wfloat-equal 106 | #-Wcovered-switch-default 107 | 108 | #-Wglobal-constructors 109 | #-Wexit-time-destructors 110 | #-Wmissing-variable-declarations 111 | #-Wextra-semi 112 | #-Wweak-vtables 113 | #-Wno-source-uses-openmp 114 | #-Wdeprecated 115 | #-Wnewline-eof 116 | #-Wmissing-prototypes 117 | 118 | #-Wno-c++98-compat 119 | #-Wno-c++98-compat-pedantic 120 | 121 | ########################### 122 | # Need to check if those are still valid today 123 | ########################### 124 | 125 | #-Wimplicit-atomic-properties 126 | #-Wmissing-declarations 127 | #-Wmissing-prototypes 128 | #-Wstrict-selector-match 129 | #-Wundeclared-selector 130 | #-Wunreachable-code 131 | 132 | # Not a warning, but enable link-time-optimization 133 | # TODO: Check out modern CMake version of setting this flag 134 | # https://cmake.org/cmake/help/latest/module/CheckIPOSupported.html 135 | #-flto 136 | 137 | # Gives meaningful stack traces 138 | -fno-omit-frame-pointer 139 | -fno-optimize-sibling-calls 140 | ) 141 | 142 | # Flags above don't make sense for MSVC 143 | if(MSVC) 144 | set(MY_FLAGS) 145 | endif() 146 | 147 | include(CheckCXXCompilerFlag) 148 | 149 | add_library(warnings_all INTERFACE) 150 | add_library(warnings::all ALIAS warnings_all) 151 | 152 | foreach(FLAG IN ITEMS ${MY_FLAGS}) 153 | string(REPLACE "=" "-" FLAG_VAR "${FLAG}") 154 | if(NOT DEFINED IS_SUPPORTED_${FLAG_VAR}) 155 | check_cxx_compiler_flag("${FLAG}" IS_SUPPORTED_${FLAG_VAR}) 156 | endif() 157 | if(IS_SUPPORTED_${FLAG_VAR}) 158 | target_compile_options(warnings_all INTERFACE ${FLAG}) 159 | endif() 160 | endforeach() 161 | -------------------------------------------------------------------------------- /cmake/geogram.cmake: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Find Geogram and build it as part of the current build 3 | ################################################################################ 4 | 5 | if(TARGET geogram) 6 | return() 7 | endif() 8 | 9 | ################################################################################ 10 | 11 | if(THIRD_PARTY_DIR) 12 | set(GEOGRAM_SEARCH_PATHS ${THIRD_PARTY_DIR}) 13 | else() 14 | # set(GEOGRAM_SEARCH_PATHS 15 | # ${GEOGRAM_INSTALL_PREFIX} 16 | # "$ENV{GEOGRAM_INSTALL_PREFIX}" 17 | # "/usr/local/" 18 | # "$ENV{PROGRAMFILES}/Geogram" 19 | # "$ENV{PROGRAMW6432}/Geogram" 20 | # "$ENV{HOME}/.local/") 21 | endif() 22 | 23 | find_path(GEOGRAM_SOURCE_INCLUDE_DIR 24 | geogram/basic/common.h 25 | PATHS ${GEOGRAM_SEARCH_PATHS} 26 | PATH_SUFFIXES geogram/src/lib 27 | NO_DEFAULT_PATH 28 | ) 29 | 30 | set(GEOGRAM_ROOT ${GEOGRAM_SOURCE_INCLUDE_DIR}/../..) 31 | 32 | message("Found Geogram here: ${GEOGRAM_ROOT}") 33 | ################################################################################ 34 | 35 | if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") 36 | set(VORPALINE_ARCH_64 TRUE CACHE BOOL "" FORCE) 37 | set(VORPALINE_PLATFORM Win-vs-generic CACHE STRING "" FORCE) 38 | set(VORPALINE_BUILD_DYNAMIC false CACHE STRING "" FORCE) 39 | elseif(${CMAKE_SYSTEM_NAME} MATCHES "Linux") 40 | set(VORPALINE_PLATFORM Linux64-gcc CACHE STRING "" FORCE) 41 | set(VORPALINE_BUILD_DYNAMIC false CACHE STRING "" FORCE) 42 | elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") 43 | set(VORPALINE_PLATFORM Darwin-clang CACHE STRING "" FORCE) 44 | set(VORPALINE_BUILD_DYNAMIC false CACHE STRING "" FORCE) 45 | endif() 46 | 47 | option(GEOGRAM_WITH_GRAPHICS "Viewers and geogram_gfx library" OFF) 48 | option(GEOGRAM_WITH_LEGACY_NUMERICS "Legacy numerical libraries" OFF) 49 | option(GEOGRAM_WITH_HLBFGS "Non-linear solver (Yang Liu's HLBFGS)" OFF) 50 | option(GEOGRAM_WITH_TETGEN "Tetrahedral mesher (Hang Si's TetGen)" OFF) 51 | option(GEOGRAM_WITH_TRIANGLE "Triangle mesher (Jonathan Shewchuk's triangle)" OFF) 52 | option(GEOGRAM_WITH_EXPLORAGRAM "Experimental code (hexahedral meshing vpipeline and optimal transport)" OFF) 53 | option(GEOGRAM_WITH_LUA "Built-in LUA interpreter" OFF) 54 | option(GEOGRAM_LIB_ONLY "Libraries only (no example programs/no viewer)" ON) 55 | option(GEOGRAM_WITH_FPG "Predicate generator (Sylvain Pion's FPG)" OFF) 56 | option(GEOGRAM_USE_SYSTEM_GLFW3 "Use the version of GLFW3 installed in the system if found" OFF) 57 | 58 | # Workaround CMake limitation with geogram 1.5.4 59 | if(TARGET glfw AND NOT TARGET glfw3) 60 | add_library(glfw3 ALIAS glfw) 61 | endif() 62 | 63 | ################################################################################ 64 | 65 | add_subdirectory(${GEOGRAM_ROOT} geogram) 66 | target_include_directories(geogram SYSTEM PUBLIC ${GEOGRAM_SOURCE_INCLUDE_DIR}) 67 | 68 | if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") 69 | SET_TARGET_PROPERTIES(geogram PROPERTIES COMPILE_FLAGS -fopenmp LINK_FLAGS -fopenmp) 70 | target_compile_options(geogram PUBLIC -fopenmp) 71 | SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fopenmp") 72 | 73 | find_package(OpenMP REQUIRED) 74 | if(NOT TARGET OpenMP::OpenMP_CXX) 75 | add_library(OpenMP_TARGET INTERFACE) 76 | add_library(OpenMP::OpenMP_CXX ALIAS OpenMP_TARGET) 77 | target_compile_options(OpenMP_TARGET INTERFACE ${OpenMP_CXX_FLAGS}) 78 | find_package(Threads REQUIRED) 79 | target_link_libraries(OpenMP_TARGET INTERFACE Threads::Threads) 80 | target_link_libraries(OpenMP_TARGET INTERFACE ${OpenMP_CXX_FLAGS}) 81 | endif() 82 | 83 | target_link_libraries(FastEnvelope PUBLIC OpenMP::OpenMP_CXX) 84 | endif() 85 | 86 | ################################################################################ 87 | 88 | 89 | if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") 90 | # remove warning for multiply defined symbols (caused by multiple 91 | # instantiations of STL templates) 92 | #target_compile_options(geogram INTERFACE /wd4251) 93 | 94 | # remove all unused stuff from windows.h 95 | # target_compile_definitions(geogram INTERFACE -DWIN32_LEAN_AND_MEAN) 96 | # target_compile_definitions(geogram INTERFACE -DVC_EXTRALEAN) 97 | 98 | # do not define a min() and a max() macro, breaks 99 | # std::min() and std::max() !! 100 | target_compile_definitions(geogram INTERFACE -DNOMINMAX) 101 | 102 | # we want M_PI etc... 103 | target_compile_definitions(geogram INTERFACE -D_USE_MATH_DEFINES) 104 | endif() 105 | -------------------------------------------------------------------------------- /cmake/recipes/ipred.cmake: -------------------------------------------------------------------------------- 1 | if(TARGET ipred) 2 | return() 3 | endif() 4 | 5 | message(STATUS "Third-party: creating target 'ipred'") 6 | 7 | include(FetchContent) 8 | FetchContent_Declare( 9 | ipred 10 | GIT_REPOSITORY https://github.com/teseoch/Indirect_Predicates.git 11 | GIT_TAG a0bf2cd73383202b491a3d8d89f65a8efff232b1 12 | GIT_SHALLOW TRUE 13 | ) 14 | FetchContent_MakeAvailable(ipred) 15 | 16 | -------------------------------------------------------------------------------- /geogram_predicates/README.txt: -------------------------------------------------------------------------------- 1 | This is Predicates Pluggable Software Module, extracted 2 | from GEOGRAM source tree. It contains a standalone .cpp/.h 3 | pair that can be used in any program and that does not have 4 | any dependency. 5 | 6 | It may also contain an example program that can be compiled by using: 7 | g++ --std=c++11 Predicates_psm.cpp Predicates_example.cpp -o Predicates_example 8 | (or gcc if it is plain C, as in OpenNL) 9 | 10 | Some examples may require additional compilation flags (see comments at the beginning 11 | of the example source, e.g. Delaunay_example.cpp). 12 | -------------------------------------------------------------------------------- /indirectPredicates/ip_filtered_ex.cpp: -------------------------------------------------------------------------------- 1 | int get_projection_plane(double ov1x, double ov1y, double ov1z, double ov2x, double ov2y, double ov2z, double ov3x, double ov3y, double ov3z) 2 | { 3 | int r = triangle_normal_filtered(ov1x, ov1y, ov1z, ov2x, ov2y, ov2z, ov3x, ov3y, ov3z); 4 | if (r >= 0) 5 | return r; 6 | 7 | return triangle_normal_exact(ov1x, ov1y, ov1z, ov2x, ov2y, ov2z, ov3x, ov3y, ov3z); 8 | } 9 | 10 | void triangle_normal_exact(double ov1x, double ov1y, double ov1z, double ov2x, double ov2y, double ov2z, double ov3x, double ov3y, double ov3z, double &nvxc, double &nvyc, double &nvzc) 11 | { 12 | expansionObject o; 13 | double v3x[2]; 14 | o.two_Diff(ov3x, ov2x, v3x); 15 | double v3y[2]; 16 | o.two_Diff(ov3y, ov2y, v3y); 17 | double v3z[2]; 18 | o.two_Diff(ov3z, ov2z, v3z); 19 | double v2x[2]; 20 | o.two_Diff(ov2x, ov1x, v2x); 21 | double v2y[2]; 22 | o.two_Diff(ov2y, ov1y, v2y); 23 | double v2z[2]; 24 | o.two_Diff(ov2z, ov1z, v2z); 25 | double nvx1[8]; 26 | o.Two_Two_Prod(v2y, v3z, nvx1); 27 | double nvx2[8]; 28 | o.Two_Two_Prod(v2z, v3y, nvx2); 29 | double nvx[16]; 30 | int nvx_len = o.Gen_Diff(8, nvx1, 8, nvx2, nvx); 31 | double nvy1[8]; 32 | o.Two_Two_Prod(v3x, v2z, nvy1); 33 | double nvy2[8]; 34 | o.Two_Two_Prod(v3z, v2x, nvy2); 35 | double nvy[16]; 36 | int nvy_len = o.Gen_Diff(8, nvy1, 8, nvy2, nvy); 37 | double nvz1[8]; 38 | o.Two_Two_Prod(v2x, v3y, nvz1); 39 | double nvz2[8]; 40 | o.Two_Two_Prod(v2y, v3x, nvz2); 41 | double nvz[16]; 42 | int nvz_len = o.Gen_Diff(8, nvz1, 8, nvz2, nvz); 43 | 44 | nvxc = nvx[nvx_len - 1]; 45 | nvyc = nvy[nvy_len - 1]; 46 | nvzc = nvz[nvz_len - 1]; 47 | 48 | double l = sqrt(nvxc * nvxc + nvyc * nvyc + nvzc * nvzc); 49 | 50 | nvxc /= l; 51 | nvyc /= l; 52 | nvzc /= l; 53 | } 54 | 55 | void cross_product_normalized_exact( 56 | double ov1x, double ov1y, double ov1z, 57 | double ov2x, double ov2y, double ov2z, 58 | double pv1x, double pv1y, double pv1z, 59 | double pv2x, double pv2y, double pv2z, 60 | double &nvxc, double &nvyc, double &nvzc) { 61 | expansionObject o; 62 | double v3x[2]; 63 | o.two_Diff(pv2x, pv1x, v3x); 64 | double v3y[2]; 65 | o.two_Diff(pv2y, pv1y, v3y); 66 | double v3z[2]; 67 | o.two_Diff(pv2z, pv1z, v3z); 68 | double v2x[2]; 69 | o.two_Diff(ov2x, ov1x, v2x); 70 | double v2y[2]; 71 | o.two_Diff(ov2y, ov1y, v2y); 72 | double v2z[2]; 73 | o.two_Diff(ov2z, ov1z, v2z); 74 | double nvx1[8]; 75 | o.Two_Two_Prod(v2y, v3z, nvx1); 76 | double nvx2[8]; 77 | o.Two_Two_Prod(v2z, v3y, nvx2); 78 | double nvx[16]; 79 | int nvx_len = o.Gen_Diff(8, nvx1, 8, nvx2, nvx); 80 | double nvy1[8]; 81 | o.Two_Two_Prod(v3x, v2z, nvy1); 82 | double nvy2[8]; 83 | o.Two_Two_Prod(v3z, v2x, nvy2); 84 | double nvy[16]; 85 | int nvy_len = o.Gen_Diff(8, nvy1, 8, nvy2, nvy); 86 | double nvz1[8]; 87 | o.Two_Two_Prod(v2x, v3y, nvz1); 88 | double nvz2[8]; 89 | o.Two_Two_Prod(v2y, v3x, nvz2); 90 | double nvz[16]; 91 | int nvz_len = o.Gen_Diff(8, nvz1, 8, nvz2, nvz); 92 | 93 | nvxc = nvx[nvx_len - 1]; 94 | nvyc = nvy[nvy_len - 1]; 95 | nvzc = nvz[nvz_len - 1]; 96 | 97 | double l = sqrt(nvxc * nvxc + nvyc * nvyc + nvzc * nvzc); 98 | 99 | nvxc /= l; 100 | nvyc /= l; 101 | nvzc /= l; 102 | } 103 | 104 | 105 | int dot_product_sign(double vx, double vy, double vz, 106 | double ux, double uy, double uz) { 107 | expansionObject o; 108 | double x[2]; 109 | o.Two_Prod(vx, ux, x); 110 | double y[2]; 111 | o.Two_Prod(vy, uy, y); 112 | double z[2]; 113 | o.Two_Prod(vz, uz, z); 114 | double xsumy[4]; 115 | o.Two_Two_Sum(x[1], x[0], y[1], y[0], xsumy[3], xsumy[2], xsumy[1], xsumy[0]); 116 | double sum[6]; 117 | int sl = o.Gen_Sum(4, xsumy, 2, z, sum); 118 | double s = sum[sl - 1]; 119 | 120 | if (s > 0) return 1; 121 | if (s < 0) return -1; 122 | return 0; 123 | } 124 | -------------------------------------------------------------------------------- /indirectPredicates/ip_filtered_ex.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | int get_projection_plane( 4 | double ov1x, double ov1y, double ov1z, 5 | double ov2x, double ov2y, double ov2z, 6 | double ov3x, double ov3y, double ov3z); 7 | 8 | void triangle_normal_exact( 9 | double ov1x, double ov1y, double ov1z, 10 | double ov2x, double ov2y, double ov2z, 11 | double ov3x, double ov3y, double ov3z, 12 | double &nvxc, double &nvyc, double &nvzc); 13 | void cross_product_normalized_exact( 14 | double ov1x, double ov1y, double ov1z, 15 | double ov2x, double ov2y, double ov2z, 16 | double pv1x, double pv1y, double pv1z, 17 | double pv2x, double pv2y, double pv2z, 18 | double &nvxc, double &nvyc, double &nvzc); 19 | 20 | 21 | int dot_product_sign(double vx, double vy, double vz, 22 | double ux, double uy, double uz); 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/AABB.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | namespace fastEnvelope { 6 | 7 | void AABB::init_envelope_boxes_recursive( 8 | const std::vector> &cornerlist, int node_index, 9 | int b, int e) { 10 | assert(b != e); 11 | assert(node_index < boxlist.size()); 12 | 13 | if (b + 1 == e) { 14 | boxlist[node_index] = cornerlist[b]; 15 | return; 16 | } 17 | int m = b + (e - b) / 2; 18 | int childl = 2 * node_index; 19 | int childr = 2 * node_index + 1; 20 | 21 | assert(childl < boxlist.size()); 22 | assert(childr < boxlist.size()); 23 | 24 | init_envelope_boxes_recursive(cornerlist, childl, b, m); 25 | init_envelope_boxes_recursive(cornerlist, childr, m, e); 26 | 27 | assert(childl < boxlist.size()); 28 | assert(childr < boxlist.size()); 29 | for (int c = 0; c < 3; ++c) { 30 | boxlist[node_index][0][c] = 31 | std::min(boxlist[childl][0][c], boxlist[childr][0][c]); 32 | boxlist[node_index][1][c] = 33 | std::max(boxlist[childl][1][c], boxlist[childr][1][c]); 34 | } 35 | } 36 | 37 | void AABB::triangle_search_bbd_recursive(const Vector3 &triangle0, 38 | const Vector3 &triangle1, 39 | const Vector3 &triangle2, 40 | std::vector &list, int n, 41 | int b, int e) const { 42 | assert(e != b); 43 | 44 | assert(n < boxlist.size()); 45 | bool cut = is_triangle_cut_bounding_box(triangle0, triangle1, triangle2, n); 46 | 47 | if (cut == false) 48 | return; 49 | 50 | // Leaf case 51 | if (e == b + 1) { 52 | list.emplace_back(b); 53 | return; 54 | } 55 | 56 | int m = b + (e - b) / 2; 57 | int childl = 2 * n; 58 | int childr = 2 * n + 1; 59 | 60 | // assert(childl < boxlist.size()); 61 | // assert(childr < boxlist.size()); 62 | 63 | // Traverse the "nearest" child first, so that it has more chances 64 | // to prune the traversal of the other child. 65 | triangle_search_bbd_recursive(triangle0, triangle1, triangle2, list, childl, 66 | b, m); 67 | triangle_search_bbd_recursive(triangle0, triangle1, triangle2, list, childr, 68 | m, e); 69 | } 70 | 71 | void AABB::point_search_bbd_recursive(const Vector3 &point, 72 | std::vector &list, int n, 73 | int b, int e) const { 74 | assert(e != b); 75 | 76 | assert(n < boxlist.size()); 77 | bool cut = is_point_cut_bounding_box(point, n); 78 | 79 | if (cut == false) 80 | return; 81 | 82 | // Leaf case 83 | if (e == b + 1) { 84 | list.emplace_back(b); 85 | return; 86 | } 87 | 88 | int m = b + (e - b) / 2; 89 | int childl = 2 * n; 90 | int childr = 2 * n + 1; 91 | 92 | // assert(childl < boxlist.size()); 93 | // assert(childr < boxlist.size()); 94 | 95 | // Traverse the "nearest" child first, so that it has more chances 96 | // to prune the traversal of the other child. 97 | point_search_bbd_recursive(point, list, childl, b, m); 98 | point_search_bbd_recursive(point, list, childr, m, e); 99 | } 100 | 101 | void AABB::segment_search_bbd_recursive(const Vector3 &seg0, 102 | const Vector3 &seg1, 103 | std::vector &list, int n, 104 | int b, int e) const { 105 | assert(e != b); 106 | 107 | assert(n < boxlist.size()); 108 | bool cut = is_segment_cut_bounding_box(seg0, seg1, n); 109 | 110 | if (cut == false) 111 | return; 112 | 113 | // Leaf case 114 | if (e == b + 1) { 115 | list.emplace_back(b); 116 | return; 117 | } 118 | 119 | int m = b + (e - b) / 2; 120 | int childl = 2 * n; 121 | int childr = 2 * n + 1; 122 | 123 | // assert(childl < boxlist.size()); 124 | // assert(childr < boxlist.size()); 125 | 126 | // Traverse the "nearest" child first, so that it has more chances 127 | // to prune the traversal of the other child. 128 | segment_search_bbd_recursive(seg0, seg1, list, childl, b, m); 129 | segment_search_bbd_recursive(seg0, seg1, list, childr, m, e); 130 | } 131 | 132 | void AABB::bbd_searching_recursive(const Vector3 &bbd0, const Vector3 &bbd1, 133 | std::vector &list, int n, 134 | int b, int e) const { 135 | assert(e != b); 136 | 137 | assert(n < boxlist.size()); 138 | bool cut = is_bbd_cut_bounding_box(bbd0, bbd1, n); 139 | 140 | if (cut == false) 141 | return; 142 | 143 | // Leaf case 144 | if (e == b + 1) { 145 | list.emplace_back(b); 146 | return; 147 | } 148 | 149 | int m = b + (e - b) / 2; 150 | int childl = 2 * n; 151 | int childr = 2 * n + 1; 152 | 153 | // assert(childl < boxlist.size()); 154 | // assert(childr < boxlist.size()); 155 | 156 | // Traverse the "nearest" child first, so that it has more chances 157 | // to prune the traversal of the other child. 158 | bbd_searching_recursive(bbd0, bbd1, list, childl, b, m); 159 | bbd_searching_recursive(bbd0, bbd1, list, childr, m, e); 160 | } 161 | int AABB::envelope_max_node_index(int node_index, int b, int e) { 162 | assert(e > b); 163 | if (b + 1 == e) { 164 | return node_index; 165 | } 166 | int m = b + (e - b) / 2; 167 | int childl = 2 * node_index; 168 | int childr = 2 * node_index + 1; 169 | return std::max(envelope_max_node_index(childl, b, m), 170 | envelope_max_node_index(childr, m, e)); 171 | } 172 | 173 | void AABB::init(const std::vector> &cornerlist) { 174 | n_corners = cornerlist.size(); 175 | 176 | boxlist.resize(envelope_max_node_index(1, 0, n_corners) + 177 | 1 // <-- this is because size == max_index + 1 !!! 178 | ); 179 | 180 | init_envelope_boxes_recursive(cornerlist, 1, 0, n_corners); 181 | } 182 | 183 | bool AABB::is_triangle_cut_bounding_box(const Vector3 &tri0, 184 | const Vector3 &tri1, 185 | const Vector3 &tri2, int index) const { 186 | const auto &bmin = boxlist[index][0]; 187 | const auto &bmax = boxlist[index][1]; 188 | Vector3 tmin, tmax; 189 | 190 | algorithms::get_tri_corners(tri0, tri1, tri2, tmin, tmax); 191 | bool cut = algorithms::box_box_intersection(tmin, tmax, bmin, bmax); 192 | if (cut == false) 193 | return false; 194 | 195 | if (cut) { 196 | 197 | std::array tri; 198 | std::array mp; 199 | int o0, o1, o2, o3, ori; 200 | for (int i = 0; i < 3; i++) { 201 | tri[0] = algorithms::to_2d(tri0, i); 202 | tri[1] = algorithms::to_2d(tri1, i); 203 | tri[2] = algorithms::to_2d(tri2, i); 204 | 205 | mp[0] = algorithms::to_2d(bmin, i); 206 | mp[1] = algorithms::to_2d(bmax, i); 207 | mp[2][0] = mp[0][0]; 208 | mp[2][1] = mp[1][1]; 209 | mp[3][0] = mp[1][0]; 210 | mp[3][1] = mp[0][1]; 211 | 212 | for (int j = 0; j < 3; j++) { 213 | o0 = fastEnvelope::Predicates::orient_2d(mp[0], tri[j % 3], 214 | tri[(j + 1) % 3]); 215 | o1 = fastEnvelope::Predicates::orient_2d(mp[1], tri[j % 3], 216 | tri[(j + 1) % 3]); 217 | o2 = fastEnvelope::Predicates::orient_2d(mp[2], tri[j % 3], 218 | tri[(j + 1) % 3]); 219 | o3 = fastEnvelope::Predicates::orient_2d(mp[3], tri[j % 3], 220 | tri[(j + 1) % 3]); 221 | ori = fastEnvelope::Predicates::orient_2d(tri[(j + 2) % 3], tri[j % 3], 222 | tri[(j + 1) % 3]); 223 | if (ori == 0) 224 | continue; 225 | if (ori * o0 <= 0 && ori * o1 <= 0 && ori * o2 <= 0 && ori * o3 <= 0) 226 | return false; 227 | } 228 | } 229 | } 230 | 231 | return cut; 232 | } 233 | bool AABB::is_point_cut_bounding_box(const Vector3 &p, int index) const { 234 | const auto &bmin = boxlist[index][0]; 235 | const auto &bmax = boxlist[index][1]; 236 | if (p[0] < bmin[0] || p[1] < bmin[1] || p[2] < bmin[2]) 237 | return false; 238 | if (p[0] > bmax[0] || p[1] > bmax[1] || p[2] > bmax[2]) 239 | return false; 240 | return true; 241 | } 242 | 243 | bool AABB::is_segment_cut_bounding_box(const Vector3 &seg0, const Vector3 &seg1, 244 | int index) const { 245 | const auto &bmin = boxlist[index][0]; 246 | const auto &bmax = boxlist[index][1]; 247 | Scalar min[3], max[3]; 248 | min[0] = std::min(seg0[0], seg1[0]); 249 | min[1] = std::min(seg0[1], seg1[1]); 250 | min[2] = std::min(seg0[2], seg1[2]); 251 | max[0] = std::max(seg0[0], seg1[0]); 252 | max[1] = std::max(seg0[1], seg1[1]); 253 | max[2] = std::max(seg0[2], seg1[2]); 254 | if (max[0] < bmin[0] || max[1] < bmin[1] || max[2] < bmin[2]) 255 | return false; 256 | if (min[0] > bmax[0] || min[1] > bmax[1] || min[2] > bmax[2]) 257 | return false; 258 | return true; 259 | } 260 | bool AABB::is_bbd_cut_bounding_box(const Vector3 &bbd0, const Vector3 &bbd1, 261 | int index) const { 262 | const auto &bmin = boxlist[index][0]; 263 | const auto &bmax = boxlist[index][1]; 264 | 265 | return algorithms::box_box_intersection(bbd0, bbd1, bmin, bmax); 266 | } 267 | } // namespace fastEnvelope 268 | -------------------------------------------------------------------------------- /src/AABB.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace fastEnvelope { 10 | class AABB { 11 | private: 12 | 13 | 14 | std::vector> boxlist; 15 | size_t n_corners = -1; 16 | 17 | void init_envelope_boxes_recursive( 18 | const std::vector> &cornerlist, 19 | int node_index, 20 | int b, int e); 21 | 22 | void triangle_search_bbd_recursive( 23 | const Vector3 &triangle0, const Vector3 &triangle1, const Vector3 &triangle2, 24 | std::vector &list, 25 | int n, int b, int e) const; 26 | void point_search_bbd_recursive( 27 | const Vector3 &point, 28 | std::vector &list, 29 | int n, int b, int e) const; 30 | void segment_search_bbd_recursive( 31 | const Vector3 &seg0, const Vector3 &seg1, 32 | std::vector &list, 33 | int n, int b, int e) const; 34 | 35 | 36 | void bbd_searching_recursive( 37 | const Vector3 &bbd0, const Vector3 &bbd1, 38 | std::vector &list, 39 | int n, int b, int e) const; 40 | 41 | static int envelope_max_node_index(int node_index, int b, int e); 42 | 43 | bool is_triangle_cut_bounding_box(const Vector3 &tri0, const Vector3 &tri1, const Vector3 &tri2, int index) const; 44 | bool is_point_cut_bounding_box(const Vector3 &p, int index) const; 45 | bool is_segment_cut_bounding_box(const Vector3 &seg0, const Vector3 &seg1, int index) const; 46 | bool is_bbd_cut_bounding_box(const Vector3 &bbd0, const Vector3 &bbd1, int index) const; 47 | public: 48 | void init(const std::vector> &cornerlist); 49 | bool is_initialized = false; 50 | inline void triangle_find_bbox( 51 | const Vector3 &triangle0, const Vector3 &triangle1, const Vector3 &triangle2, 52 | std::vector &list) const 53 | { 54 | assert(n_corners >= 0); 55 | assert(boxlist.size() > 0); 56 | int de = algorithms::is_triangle_degenerated(triangle0, triangle1, triangle2); 57 | if (de == DEGENERATED_SEGMENT) { 58 | Vector3 tmin, tmax; 59 | algorithms::get_tri_corners(triangle0, triangle1, triangle2, tmin, tmax); 60 | segment_find_bbox(tmin, tmax, list); 61 | return; 62 | } 63 | else if (de == DEGENERATED_POINT) { 64 | point_find_bbox(triangle0, list); 65 | return; 66 | } 67 | 68 | triangle_search_bbd_recursive(triangle0, triangle1, triangle2, list, 1, 0, n_corners); 69 | } 70 | 71 | inline void point_find_bbox( 72 | const Vector3 &p, 73 | std::vector &list) const 74 | { 75 | assert(boxlist.size() > 0); 76 | point_search_bbd_recursive( 77 | p, list, 1, 0, n_corners); 78 | } 79 | inline void segment_find_bbox( 80 | const Vector3 &seg0, const Vector3 &seg1, 81 | std::vector &list) const 82 | { 83 | assert(boxlist.size() > 0); 84 | segment_search_bbd_recursive( 85 | seg0, seg1, list, 1, 0, n_corners); 86 | } 87 | inline void bbox_find_bbox( 88 | const Vector3 &bbd0, const Vector3 &bbd1, 89 | std::vector &list) const 90 | { 91 | list.clear(); 92 | assert(n_corners >= 0); 93 | bbd_searching_recursive(bbd0,bbd1, list, 1, 0, n_corners); 94 | } 95 | }; 96 | } -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SOURCES 2 | AABB.h 3 | AABB.cpp 4 | FastEnvelope.cpp 5 | FastEnvelope.h 6 | #Logger.hpp 7 | Morton.h 8 | Morton.cpp 9 | Types.hpp 10 | #common_algorithms.cpp 11 | common_algorithms.h 12 | ) 13 | 14 | if(FAST_ENVELOPE_WITH_GMP) 15 | SET(SOURCES ${SOURCES} Rational.hpp) 16 | endif() 17 | 18 | prepend_current_path(SOURCES) 19 | fast_envelope_copy_headers(${SOURCES}) 20 | fast_envelope_set_source_group(${SOURCES}) 21 | target_sources(FastEnvelope PRIVATE ${SOURCES}) 22 | 23 | ################################################################################ 24 | # Subfolders 25 | ################################################################################ 26 | 27 | add_subdirectory(external) 28 | -------------------------------------------------------------------------------- /src/FastEnvelope.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | 13 | namespace fastEnvelope { 14 | 15 | class FastEnvelope 16 | { 17 | 18 | public: 19 | FastEnvelope(const std::vector& m_ver, const std::vector& m_faces, const Scalar eps); 20 | FastEnvelope(){} 21 | 22 | void init(const std::vector& m_ver, const std::vector& m_faces, const Scalar eps); 23 | void init(const std::vector& m_ver, const std::vector& m_faces, const std::vector eps); 24 | void init(const std::vector>> halfspace_input, 25 | const std::vector> cornerlist_input, const Scalar eps); 26 | static void printnumber(); 27 | 28 | //check if tri or point is outside 29 | bool is_outside(const std::array &triangle) const; 30 | bool is_outside(const Vector3 &point) const; 31 | bool is_outside(const Vector3 &point0, const Vector3 &point1) const; 32 | bool is_outside_no_optimazation(const std::array &triangle)const ; 33 | private: 34 | 35 | struct INDEX 36 | { 37 | int Pi; 38 | std::vector FACES; 39 | }; 40 | 41 | private: 42 | AABB tree; 43 | std::vector m_ver_p; 44 | std::vector m_faces_p; 45 | std::vector> cornerlist; 46 | std::vector>> halfspace; 47 | bool USE_ADJACENT_INFORMATION = true; 48 | 49 | bool debugcode(const std::array &triangle, const std::vector &prismindex)const; 50 | bool triangle_out_simple(const std::array &triangle, const std::vector& prismindex) const; 51 | 52 | bool triangle_out_of_envelope(const std::array &triangle, const std::vector& prismindex) const; 53 | bool segment_out_of_envelope(const Vector3& seg0, const Vector3 &seg1, const std::vector& prismindex) const; 54 | bool is_two_facets_neighbouring(const int & pid, const int &i, const int &j)const; 55 | 56 | 57 | int Implicit_Seg_Facet_interpoint_Out_Prism_return_local_id( 58 | const Vector3 &segpoint0, const Vector3 &segpoint1, const Vector3 &triangle0, 59 | const Vector3 &triangle1, const Vector3 &triangle2, const std::vector &prismindex, const int &jump, int &id) const; 60 | #ifdef ENVELOPE_WITH_GMP 61 | int Implicit_Seg_Facet_interpoint_Out_Prism_return_local_id_Rational( 62 | const Vector3 &segpoint0, const Vector3 &segpoint1, const Vector3 &triangle0, 63 | const Vector3 &triangle1, const Vector3 &triangle2, const std::vector &prismindex, const int &jump, int &id) const; 64 | #endif 65 | int Implicit_Seg_Facet_interpoint_Out_Prism_return_local_id_with_face_order( 66 | const Vector3 &segpoint0, const Vector3 &segpoint1, const Vector3 &triangle0, 67 | const Vector3 &triangle1, const Vector3 &triangle2, const std::vector &prismindex, 68 | const std::vector>& faceorder, const int &jump, int &id) const; 69 | #ifdef ENVELOPE_WITH_GMP 70 | int Implicit_Seg_Facet_interpoint_Out_Prism_return_local_id_with_face_order_Rational( 71 | const Vector3 &segpoint0, const Vector3 &segpoint1, const Vector3 &triangle0, 72 | const Vector3 &triangle1, const Vector3 &triangle2, const std::vector &prismindex, 73 | const std::vector>& intersect_face, const int &jump, int &id) const; 74 | #endif 75 | 76 | // this function check the adjacent polyhedrons and jump over the polyhedrons that already in the cover list 77 | int Implicit_Seg_Facet_interpoint_Out_Prism_return_local_id_with_face_order_jump_over( 78 | const Vector3 &segpoint0, const Vector3 &segpoint1, const Vector3 &triangle0, 79 | const Vector3 &triangle1, const Vector3 &triangle2, const std::vector &prismindex, 80 | const std::vector>& intersect_face, const std::vector& coverlist, const int &jump, int &id) const; 81 | 82 | #ifdef ENVELOPE_WITH_GMP 83 | int Implicit_Seg_Facet_interpoint_Out_Prism_return_local_id_with_face_order_jump_over_Rational( 84 | const Vector3 &segpoint0, const Vector3 &segpoint1, const Vector3 &triangle0, 85 | const Vector3 &triangle1, const Vector3 &triangle2, const std::vector &prismindex, 86 | const std::vector>& intersect_face, const std::vector& coverlist, const int &jump, int &id) const; 87 | int Implicit_Seg_Facet_interpoint_Out_Prism_return_local_id_with_face_order_jump_over_Multiprecision( 88 | const Vector3 &segpoint0, const Vector3 &segpoint1, const Vector3 &triangle0, 89 | const Vector3 &triangle1, const Vector3 &triangle2, const std::vector &prismindex, 90 | const std::vector>& intersect_face, const std::vector& coverlist, const int &jump, int &id) const; 91 | int Implicit_Tri_Facet_Facet_interpoint_Out_Prism_Rational( 92 | const std::array &triangle, 93 | const Vector3 &facet10, const Vector3 &facet11, const Vector3 &facet12, const Vector3 &facet20, const Vector3 &facet21, const Vector3 &facet22, 94 | const std::vector &prismindex, const int &jump1, const int &jump2) const; 95 | 96 | #endif 97 | int Implicit_Tri_Facet_Facet_interpoint_Out_Prism( 98 | const std::array &triangle, 99 | const Vector3 &facet10, const Vector3 &facet11, const Vector3 &facet12, const Vector3 &facet20, const Vector3 &facet21, const Vector3 &facet22, 100 | const std::vector &prismindex, const int &jump1, const int &jump2) const; 101 | // this function check the adjacent polyhedrons and jump over the polyhedrons that already in the cover list 102 | int Implicit_Tri_Facet_Facet_interpoint_Out_Prism_return_local_id_with_face_order_jump_over( 103 | const std::array &triangle, 104 | const Vector3 &facet10, const Vector3 &facet11, const Vector3 &facet12, const Vector3 &facet20, const Vector3 &facet21, const Vector3 &facet22, 105 | const std::vector &prismindex, const std::vector>&intersect_face, const std::vector& coverlist, const int &jump1, const int &jump2, 106 | int &id) const; 107 | 108 | int Implicit_Tri_Facet_Facet_interpoint_Out_Prism_return_local_id_with_face_order( 109 | const std::array &triangle, 110 | const Vector3 &facet10, const Vector3 &facet11, const Vector3 &facet12, const Vector3 &facet20, const Vector3 &facet21, const Vector3 &facet22, 111 | const std::vector &prismindex, const std::vector>&intersect_face, const int &jump1, const int &jump2, 112 | int &id) const; 113 | 114 | #ifdef ENVELOPE_WITH_GMP 115 | int Implicit_Tri_Facet_Facet_interpoint_Out_Prism_return_local_id_with_face_order_Rational( 116 | const std::array &triangle, 117 | const Vector3 &facet10, const Vector3 &facet11, const Vector3 &facet12, const Vector3 &facet20, const Vector3 &facet21, const Vector3 &facet22, 118 | const std::vector &prismindex, const std::vector>&intersect_face, const int &jump1, const int &jump2, 119 | int &id) const; 120 | #endif 121 | static bool is_3_triangle_cut_pure_multiprecision(const std::array &triangle, TPI_exact_suppvars &s); 122 | 123 | 124 | static bool is_3_triangle_cut( 125 | const std::array &triangle, 126 | const Vector3 &facet10, const Vector3 &facet11, const Vector3 &facet12, 127 | const Vector3 &facet20, const Vector3 &facet21, const Vector3 &facet22); 128 | bool is_tpp_on_polyhedra( 129 | const std::array &triangle, 130 | const Vector3 &facet10, const Vector3 &facet11, const Vector3 &facet12, const Vector3 &facet20, const Vector3 &facet21, const Vector3 &facet22, 131 | const int &prismid, const int &faceid)const; 132 | 133 | bool point_out_prism(const Vector3 &point, const std::vector &prismindex, const int &jump) const; 134 | bool point_out_prism_return_local_id(const Vector3 &point, const std::vector &prismindex, const int &jump, int &id)const; 135 | 136 | //heuristics to refine the box box guess from the tree 137 | static int is_3_triangle_cut_float_fast( 138 | const Vector3& tri0, const Vector3& tri1, const Vector3& tri2, 139 | const Vector3& facet10, const Vector3& facet11, const Vector3& facet12, 140 | const Vector3& facet20, const Vector3& facet21, const Vector3& facet22); 141 | #ifdef ENVELOPE_WITH_GMP 142 | static bool is_3_triangle_cut_Rational(const std::array& triangle, 143 | const Vector3& facet10, const Vector3& facet11, const Vector3& facet12, const Vector3& facet20, const Vector3& facet21, const Vector3& facet22); 144 | #endif 145 | int is_triangle_cut_envelope_polyhedra(const int &cindex, 146 | const Vector3 &tri0, const Vector3 &tri1, const Vector3 &tri2, std::vector &cid) const; 147 | bool is_seg_cut_polyhedra(const int &cindex, 148 | const Vector3 &seg0, const Vector3 &seg1, std::vector &cid) const; 149 | #ifdef ENVELOPE_WITH_GMP 150 | bool is_tpp_on_polyhedra_Rational( 151 | const std::array &triangle, 152 | const Vector3 &facet10, const Vector3 &facet11, const Vector3 &facet12, const Vector3 &facet20, const Vector3 &facet21, const Vector3 &facet22, 153 | const int &prismid, const int &faceid)const; 154 | #endif 155 | template 156 | static bool orient3D_LPI_prefilter_multiprecision( 157 | const T& px, const T& py, const T& pz, const T& qx, const T& qy, const T& qz, 158 | const T& rx, const T& ry, const T& rz, const T& sx, const T& sy, const T& sz, const T& tx, const T& ty, const T& tz, 159 | T& a11, T& a12, T& a13, T& d, const std::function &checker) { 160 | 161 | a11 = (px - qx); 162 | a12 = (py - qy); 163 | a13 = (pz - qz); 164 | T a21(sx - rx); 165 | T a22(sy - ry); 166 | T a23(sz - rz); 167 | T a31(tx - rx); 168 | T a32(ty - ry); 169 | T a33(tz - rz); 170 | T a2233((a22 * a33) - (a23 * a32)); 171 | T a2133((a21 * a33) - (a23 * a31)); 172 | T a2132((a21 * a32) - (a22 * a31)); 173 | d = (((a11 * a2233) - (a12 * a2133)) + (a13 * a2132));//TODO maybe not safe 174 | int flag1 = checker(d); 175 | if (flag1 == -2 || flag1 == 0) { 176 | return false;// not enough precision 177 | } 178 | T px_rx(px - rx); 179 | T py_ry(py - ry); 180 | T pz_rz(pz - rz); 181 | 182 | T n((((py_ry)* a2133) - ((px_rx)* a2233)) - ((pz_rz)* a2132)); 183 | 184 | a11 = a11 * n; 185 | a12 = a12 * n; 186 | a13 = a13 * n; 187 | return true; 188 | } 189 | 190 | template 191 | static bool orient3D_TPI_prefilter_multiprecision( 192 | const T& ov1x, const T& ov1y, const T& ov1z, const T& ov2x, const T& ov2y, const T& ov2z, const T& ov3x, const T& ov3y, const T& ov3z, 193 | const T& ow1x, const T& ow1y, const T& ow1z, const T& ow2x, const T& ow2y, const T& ow2z, const T& ow3x, const T& ow3y, const T& ow3z, 194 | const T& ou1x, const T& ou1y, const T& ou1z, const T& ou2x, const T& ou2y, const T& ou2z, const T& ou3x, const T& ou3y, const T& ou3z, 195 | T& d, T& n1, T& n2, T& n3, const std::function &checker 196 | ) 197 | { 198 | ::feclearexcept(FE_UNDERFLOW | FE_OVERFLOW | FE_INVALID); 199 | 200 | T v3x(ov3x - ov2x); 201 | T v3y(ov3y - ov2y); 202 | T v3z(ov3z - ov2z); 203 | T v2x(ov2x - ov1x); 204 | T v2y(ov2y - ov1y); 205 | T v2z(ov2z - ov1z); 206 | T w3x(ow3x - ow2x); 207 | T w3y(ow3y - ow2y); 208 | T w3z(ow3z - ow2z); 209 | T w2x(ow2x - ow1x); 210 | T w2y(ow2y - ow1y); 211 | T w2z(ow2z - ow1z); 212 | T u3x(ou3x - ou2x); 213 | T u3y(ou3y - ou2y); 214 | T u3z(ou3z - ou2z); 215 | T u2x(ou2x - ou1x); 216 | T u2y(ou2y - ou1y); 217 | T u2z(ou2z - ou1z); 218 | 219 | T nvx(v2y * v3z - v2z * v3y); 220 | T nvy(v3x * v2z - v3z * v2x); 221 | T nvz(v2x * v3y - v2y * v3x); 222 | 223 | T nwx(w2y * w3z - w2z * w3y); 224 | T nwy(w3x * w2z - w3z * w2x); 225 | T nwz(w2x * w3y - w2y * w3x); 226 | 227 | T nux(u2y * u3z - u2z * u3y); 228 | T nuy(u3x * u2z - u3z * u2x); 229 | T nuz(u2x * u3y - u2y * u3x); 230 | 231 | T nwyuz(nwy * nuz - nwz * nuy); 232 | T nwxuz(nwx * nuz - nwz * nux); 233 | T nwxuy(nwx * nuy - nwy * nux); 234 | 235 | T nvyuz(nvy * nuz - nvz * nuy); 236 | T nvxuz(nvx * nuz - nvz * nux); 237 | T nvxuy(nvx * nuy - nvy * nux); 238 | 239 | T nvywz(nvy * nwz - nvz * nwy); 240 | T nvxwz(nvx * nwz - nvz * nwx); 241 | T nvxwy(nvx * nwy - nvy * nwx); 242 | 243 | d = (nvx * nwyuz - nvy * nwxuz + nvz * nwxuy); 244 | 245 | 246 | 247 | int flag1 = checker(d); 248 | if (flag1 == -2 || flag1 == 0) { 249 | return false;// not enough precision 250 | } 251 | 252 | T p1(nvx * ov1x + nvy * ov1y + nvz * ov1z); 253 | T p2(nwx * ow1x + nwy * ow1y + nwz * ow1z); 254 | T p3(nux * ou1x + nuy * ou1y + nuz * ou1z); 255 | 256 | n1 = p1 * nwyuz - p2 * nvyuz + p3 * nvywz; 257 | n2 = p2 * nvxuz - p3 * nvxwz - p1 * nwxuz; 258 | n3 = p3 * nvxwy - p2 * nvxuy + p1 * nwxuy; 259 | return true; 260 | } 261 | 262 | template 263 | static int orient3D_LPI_postfilter_multiprecision( 264 | const T& a11, const T& a12, const T& a13, const T& d, 265 | const T& px, const T& py, const T& pz, 266 | const T& ax, const T& ay, const T& az, 267 | const T& bx, const T& by, const T& bz, 268 | const T& cx, const T& cy, const T& cz, const std::function &checker) { 269 | 270 | T px_cx(px - cx); 271 | T py_cy(py - cy); 272 | T pz_cz(pz - cz); 273 | 274 | T d11((d * px_cx) + (a11)); 275 | T d21(ax - cx); 276 | T d31(bx - cx); 277 | T d12((d * py_cy) + (a12)); 278 | T d22(ay - cy); 279 | T d32(by - cy); 280 | T d13((d * pz_cz) + (a13)); 281 | T d23(az - cz); 282 | T d33(bz - cz); 283 | 284 | T d2233(d22 * d33); 285 | T d2332(d23 * d32); 286 | T d2133(d21 * d33); 287 | T d2331(d23 * d31); 288 | T d2132(d21 * d32); 289 | T d2231(d22 * d31); 290 | 291 | T det(d11 * (d2233 - d2332) - d12 * (d2133 - d2331) + d13 * (d2132 - d2231)); 292 | 293 | int flag2 = checker(det); 294 | if (flag2 == -2) { 295 | return 100;// not enough precision, only happens when using floating points 296 | } 297 | if (flag2 == 1) { 298 | if (d > 0) { 299 | return 1; 300 | } 301 | if (d < 0) { 302 | return -1; 303 | } 304 | } 305 | if (flag2 == -1) { 306 | if (d > 0) { 307 | return -1; 308 | } 309 | if (d < 0) { 310 | return 1; 311 | } 312 | } 313 | return 0; 314 | } 315 | 316 | template 317 | static int orient3D_TPI_postfilter_multiprecision( 318 | const T& d, const T& n1, const T& n2, const T& n3, 319 | const T& q1x, const T& q1y, const T& q1z, const T& q2x, const T& q2y, const T& q2z, const T& q3x, const T& q3y, const T& q3z, const std::function &checker 320 | ) 321 | { 322 | ::feclearexcept(FE_UNDERFLOW | FE_OVERFLOW | FE_INVALID); 323 | 324 | T dq3x(d * q3x); 325 | T dq3y(d * q3y); 326 | T dq3z(d * q3z); 327 | 328 | T a11(n1 - dq3x); 329 | T a12(n2 - dq3y); 330 | T a13(n3 - dq3z); 331 | T a21(q1x - q3x); 332 | T a22(q1y - q3y); 333 | T a23(q1z - q3z); 334 | T a31(q2x - q3x); 335 | T a32(q2y - q3y); 336 | T a33(q2z - q3z); 337 | 338 | T det(a11 * (a22*a33 - a23 * a32) - a12 * (a21*a33 - a23 * a31) + a13 * (a21*a32 - a22 * a31)); 339 | 340 | int flag2 = checker(det); 341 | if (flag2 == -2) { 342 | return 100;// not enough precision 343 | } 344 | if (flag2 == 1) { 345 | if (d > 0) { 346 | return 1; 347 | } 348 | if (d < 0) { 349 | return -1; 350 | } 351 | } 352 | if (flag2 == -1) { 353 | if (d > 0) { 354 | return -1; 355 | } 356 | if (d < 0) { 357 | return 1; 358 | } 359 | } 360 | return 0; 361 | } 362 | }; 363 | 364 | } -------------------------------------------------------------------------------- /src/Logger.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | namespace fastEnvelope 14 | { 15 | std::shared_ptr Logger::logger_; 16 | 17 | // See https://github.com/gabime/spdlog#asynchronous-logger-with-multi-sinks 18 | void Logger::init(std::vector &sinks) 19 | { 20 | auto l = spdlog::get("fastEnvelope"); 21 | bool fastEnvelope = l != nullptr; 22 | if (fastEnvelope) 23 | spdlog::drop("fastEnvelope"); 24 | 25 | spdlog::init_thread_pool(8192, 1); 26 | Logger::logger_ = 27 | std::make_shared( 28 | "fastEnvelope", 29 | sinks.begin(), sinks.end(), 30 | spdlog::thread_pool(), spdlog::async_overflow_policy::block); 31 | spdlog::register_logger(logger_); 32 | 33 | if (fastEnvelope) 34 | logger().warn("Removed another fastEnvelope logger"); 35 | } 36 | 37 | void Logger::init(bool use_cout, const std::string &filename, bool truncate) 38 | { 39 | std::vector sinks; 40 | if (use_cout) 41 | { 42 | sinks.emplace_back(std::make_shared()); 43 | } 44 | if (!filename.empty()) 45 | { 46 | sinks.emplace_back(std::make_shared(filename, truncate)); 47 | } 48 | 49 | init(sinks); 50 | } 51 | 52 | void Logger::init(std::ostream &os) 53 | { 54 | std::vector sinks; 55 | sinks.emplace_back(std::make_shared(os, false)); 56 | 57 | init(sinks); 58 | } 59 | 60 | } // namespace fastEnvelope 61 | -------------------------------------------------------------------------------- /src/Logger.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | #include 9 | 10 | namespace fastEnvelope 11 | { 12 | 13 | struct Logger 14 | { 15 | static std::shared_ptr logger_; 16 | 17 | // By default, write to stdout, but don't write to any file 18 | static void init(bool use_cout = true, const std::string &filename = "", bool truncate = true); 19 | static void init(std::ostream &os); 20 | static void init(std::vector &sinks); 21 | }; 22 | 23 | // Retrieve current logger, or create one if not available 24 | inline spdlog::async_logger &logger() 25 | { 26 | if (!Logger::logger_) 27 | { 28 | Logger::init(); 29 | } 30 | return *Logger::logger_; 31 | } 32 | 33 | } // namespace fastEnvelope 34 | -------------------------------------------------------------------------------- /src/Morton.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of NSEssentials. 3 | 4 | Use of this source code is granted via a BSD-style license, which can be found 5 | in License.txt in the repository root. 6 | 7 | @author Nico Schertler 8 | */ 9 | 10 | #include 11 | #include 12 | 13 | using namespace Resorting; 14 | 15 | const MortonCode64 MortonCode64::Zero(0u, 0u, 0u); 16 | const MortonCode64 MortonCode64::UnitX(1u, 0u, 0u); 17 | const MortonCode64 MortonCode64::UnitY(0u, 1u, 0u); 18 | const MortonCode64 MortonCode64::UnitZ(0u, 0u, 1u); 19 | 20 | 21 | uint64_t SplitBy3Bits21(int32_t x) 22 | { 23 | //int64_t r = r & 0x1fffff; 24 | uint64_t r = x; 25 | 26 | r = (r | r << 32) & 0x1f00000000ffff; 27 | r = (r | r << 16) & 0x1f0000ff0000ff; 28 | r = (r | r << 8) & 0x100f00f00f00f00f; 29 | r = (r | r << 4) & 0x10c30c30c30c30c3; 30 | r = (r | r << 2) & 0x1249249249249249; 31 | 32 | return r; 33 | } 34 | 35 | int32_t CompactBy3Bits21(uint64_t x) 36 | { 37 | uint64_t d = x & 0x1249249249249249; 38 | d = (d | d >> 2) & 0x10c30c30c30c30c3; 39 | d = (d | d >> 4) & 0x100f00f00f00f00f; 40 | d = (d | d >> 8) & 0x1f0000ff0000ff; 41 | d = (d | d >> 16) & 0x1f00000000ffff; 42 | d = (d | d >> 32); 43 | 44 | //sign extension 45 | d = (d & 0x100000 ? d | 0xffe00000 : d); 46 | 47 | return (int32_t)d; 48 | } 49 | 50 | MortonCode64::MortonCode64() 51 | { } 52 | 53 | MortonCode64::MortonCode64(int32_t x, int32_t y, int32_t z) 54 | { 55 | assert(-(1 << 20) <= x && x < (1 << 20)); 56 | assert(-(1 << 20) <= y && y < (1 << 20)); 57 | assert(-(1 << 20) <= z && z < (1 << 20)); 58 | //move sign bit to bit 20 59 | x = (x & 0x80000000) >> 11 | (x & 0x0fffff); 60 | y = (y & 0x80000000) >> 11 | (y & 0x0fffff); 61 | z = (z & 0x80000000) >> 11 | (z & 0x0fffff); 62 | 63 | data = SplitBy3Bits21(x) | SplitBy3Bits21(y) << 1 | SplitBy3Bits21(z) << 2; 64 | //invert sign bits 65 | data = data ^ 0x7000000000000000; 66 | 67 | /*int32_t dx, dy, dz; 68 | decode(dx, dy, dz); 69 | assert(x == dx); 70 | assert(y == dy); 71 | assert(z == dz);*/ 72 | } 73 | 74 | MortonCode64::MortonCode64(uint32_t x, uint32_t y, uint32_t z) 75 | { 76 | data = (SplitBy3Bits21(x) | SplitBy3Bits21(y) << 1 | SplitBy3Bits21(z) << 2) | 0x7000000000000000; 77 | } 78 | 79 | MortonCode64::MortonCode64(uint64_t d) 80 | : data(d) 81 | { } 82 | 83 | void MortonCode64::decode(int32_t & x, int32_t & y, int32_t & z) const 84 | { 85 | //invert sign bits 86 | uint64_t d = data ^ 0x7000000000000000; 87 | 88 | x = CompactBy3Bits21(d); 89 | y = CompactBy3Bits21(d >> 1); 90 | z = CompactBy3Bits21(d >> 2); 91 | } 92 | 93 | int64_t xMask = 0x1249249249249249; // ...001001001001001001001 94 | 95 | template 96 | MortonCode64 MortonCode64::InvertDimension() const 97 | { 98 | static_assert(0 <= DIM && DIM < 3, "The dimension must be between 0 and 2."); 99 | uint64_t mask = xMask << DIM; 100 | 101 | //(2 - complement) 102 | //invert bits 103 | uint64_t c = data ^ mask; 104 | //add 1 105 | uint64_t sum = (c | ~mask) + 1; //fill the bits that are not in the mask with 1 to make sure carry gets carried on 106 | return MortonCode64((sum & mask) | (c & ~mask)); 107 | } 108 | 109 | template MortonCode64 MortonCode64::InvertDimension<0>() const; 110 | template MortonCode64 MortonCode64::InvertDimension<1>() const; 111 | template MortonCode64 MortonCode64::InvertDimension<2>() const; 112 | 113 | MortonCode64 MortonCode64::DivideDimensionBy2(int dim) const 114 | { 115 | assert(data >> 60 == 7); //all signs must be positive 116 | assert(0 <= dim && dim < 3); 117 | uint64_t mask = (xMask << dim) & (0x0fffffffffffffff); //exclude signs from mask 118 | 119 | return MortonCode64((data & mask) >> 3 | (data & ~mask)); 120 | } 121 | 122 | MortonCode64 MortonCode64::Negate() const 123 | { 124 | uint64_t yMask = xMask << 1; 125 | uint64_t zMask = xMask << 2; 126 | 127 | //invert 128 | uint64_t d = ~data; 129 | uint64_t xSum = (d | ~xMask) + 1; 130 | uint64_t ySum = (d | ~yMask) + 1; 131 | uint64_t zSum = (d | ~zMask) + 1; 132 | 133 | return MortonCode64((xSum & xMask) | (ySum & yMask) | (zSum & zMask)); 134 | } 135 | 136 | MortonCode64 MortonCode64::operator+(const MortonCode64 rhs) const 137 | { 138 | //invert sign bits 139 | uint64_t code1 = data ^ 0x7000000000000000; 140 | uint64_t code2 = rhs.data ^ 0x7000000000000000; 141 | 142 | uint64_t yMask = xMask << 1; 143 | uint64_t zMask = xMask << 2; 144 | 145 | uint64_t xSum = (code1 | ~xMask) + (code2 & xMask); 146 | uint64_t ySum = (code1 | ~yMask) + (code2 & yMask); 147 | uint64_t zSum = (code1 | ~zMask) + (code2 & zMask); 148 | 149 | uint64_t result = (xSum & xMask) | (ySum & yMask) | (zSum & zMask); 150 | return MortonCode64(result ^ 0x7000000000000000); 151 | } 152 | 153 | MortonCode64 MortonCode64::operator+(const int64_t rhs) const 154 | { 155 | return MortonCode64(data + rhs); 156 | } 157 | 158 | MortonCode64 & MortonCode64::operator+=(const MortonCode64 rhs) 159 | { 160 | *this = *this + rhs; 161 | return *this; 162 | } 163 | 164 | MortonCode64 MortonCode64::operator-(const MortonCode64 rhs) const 165 | { 166 | return *this + rhs.Negate(); 167 | } 168 | 169 | MortonCode64 MortonCode64::operator>>(int shift) const 170 | { 171 | assert(data >> 60 == 7); //all signs must be positive 172 | return MortonCode64((data & 0x0fffffffffffffff) >> (3 * shift) | 0x7000000000000000); 173 | } 174 | 175 | MortonCode64 MortonCode64::operator<<(int shift) const 176 | { 177 | return MortonCode64(((data << (3 * shift)) & 0x0fffffffffffffff) | (data & 0x7000000000000000)); 178 | } 179 | 180 | -------------------------------------------------------------------------------- /src/Morton.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of NSEssentials. 3 | 4 | Use of this source code is granted via a BSD-style license, which can be found 5 | in License.txt in the repository root. 6 | 7 | @author Nico Schertler 8 | */ 9 | 10 | #pragma once 11 | #ifdef NSE_BUILD_SHARED 12 | #ifndef NSE_EXPORT 13 | #ifdef _WIN32 14 | #ifdef nsessentials_EXPORTS 15 | /* We are building this library */ 16 | #define NSE_EXPORT __declspec(dllexport) 17 | #else 18 | /* We are using this library */ 19 | #define NSE_EXPORT __declspec(dllimport) 20 | #endif 21 | #else 22 | #define NSE_EXPORT 23 | #endif 24 | #endif 25 | #else 26 | #define NSE_EXPORT 27 | #endif 28 | #include 29 | #include 30 | 31 | namespace Resorting 32 | { 33 | 34 | // Represents a three-dimensional 64-bit Morton Code. 35 | class NSE_EXPORT MortonCode64 36 | { 37 | public: 38 | MortonCode64(); 39 | 40 | MortonCode64(int32_t x, int32_t y, int32_t z); 41 | 42 | MortonCode64(uint32_t x, uint32_t y, uint32_t z); 43 | 44 | MortonCode64(uint64_t); 45 | 46 | // Decodes the code into its three coordinates. 47 | void decode(int32_t &x, int32_t &y, int32_t &z) const; 48 | 49 | // Negates the specified coordinate. 50 | template 51 | MortonCode64 InvertDimension() const; 52 | 53 | // Under the assumption that all entries are positive 54 | MortonCode64 DivideDimensionBy2(int dim) const; 55 | MortonCode64 Negate() const; 56 | 57 | MortonCode64 operator+(const MortonCode64 rhs) const; 58 | MortonCode64 operator+(const int64_t rhs) const; 59 | MortonCode64 &operator+=(const MortonCode64 rhs); 60 | MortonCode64 operator-(const MortonCode64 rhs) const; 61 | 62 | // Applies the bitshift to every dimension, assumes all entries to be positive 63 | MortonCode64 operator>>(int shift) const; 64 | MortonCode64 operator<<(int shift) const; 65 | 66 | bool operator<(const MortonCode64 rhs) const { return data < rhs.data; } 67 | bool operator>(const MortonCode64 rhs) const { return data > rhs.data; } 68 | bool operator<=(const MortonCode64 rhs) const { return data <= rhs.data; } 69 | bool operator>=(const MortonCode64 rhs) const { return data >= rhs.data; } 70 | bool operator==(const MortonCode64 rhs) const { return data == rhs.data; } 71 | bool operator!=(const MortonCode64 rhs) const { return data != rhs.data; } 72 | 73 | explicit operator uint64_t() const { return data; } 74 | 75 | static const MortonCode64 Zero; 76 | static const MortonCode64 UnitX; 77 | static const MortonCode64 UnitY; 78 | static const MortonCode64 UnitZ; 79 | 80 | private: 81 | uint64_t data; 82 | }; 83 | 84 | } -------------------------------------------------------------------------------- /src/Rational.hpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | namespace fastEnvelope { 6 | 7 | class Rational 8 | { 9 | public: 10 | mpq_t value; 11 | void canonicalize() 12 | { 13 | mpq_canonicalize(value); 14 | } 15 | int get_sign() 16 | { 17 | return mpq_sgn(value); 18 | } 19 | 20 | Rational() 21 | { 22 | mpq_init(value); 23 | mpq_set_d(value, 0); 24 | } 25 | 26 | Rational(double d) 27 | { 28 | mpq_init(value); 29 | mpq_set_d(value, d); 30 | // canonicalize(); 31 | } 32 | 33 | Rational(const mpq_t &v_) 34 | { 35 | mpq_init(value); 36 | mpq_set(value, v_); 37 | // canonicalize(); 38 | } 39 | 40 | Rational(const Rational &other) 41 | { 42 | mpq_init(value); 43 | mpq_set(value, other.value); 44 | } 45 | 46 | ~Rational() 47 | { 48 | mpq_clear(value); 49 | } 50 | 51 | // //+, - another point 52 | // Rational operator+(const Rational &r) const { 53 | // Rational r_out; 54 | // mpq_add(r_out.value, value, r.value); 55 | // return r_out; 56 | // } 57 | // 58 | // Rational operator-(const Rational &r) const { 59 | // Rational r_out; 60 | // mpq_sub(r_out.value, value, r.value); 61 | // return r_out; 62 | // } 63 | // 64 | // //*, / double/rational 65 | // Rational operator*(const Rational &r) const { 66 | // Rational r_out; 67 | // mpq_mul(r_out.value, value, r.value); 68 | // return r_out; 69 | // } 70 | // 71 | // Rational operator/(const Rational &r) const { 72 | // Rational r_out; 73 | // mpq_div(r_out.value, value, r.value); 74 | // return r_out; 75 | // } 76 | // 77 | // //= 78 | // void operator=(const Rational &r) { 79 | // mpq_set(value, r.value); 80 | // } 81 | 82 | friend Rational operator+(const Rational &x, const Rational &y) 83 | { 84 | Rational r_out; 85 | mpq_add(r_out.value, x.value, y.value); 86 | return r_out; 87 | } 88 | 89 | friend Rational operator-(const Rational &x, const Rational &y) 90 | { 91 | Rational r_out; 92 | mpq_sub(r_out.value, x.value, y.value); 93 | return r_out; 94 | } 95 | 96 | friend Rational operator*(const Rational &x, const Rational &y) 97 | { 98 | Rational r_out; 99 | mpq_mul(r_out.value, x.value, y.value); 100 | return r_out; 101 | } 102 | 103 | friend Rational operator/(const Rational &x, const Rational &y) 104 | { 105 | Rational r_out; 106 | mpq_div(r_out.value, x.value, y.value); 107 | return r_out; 108 | } 109 | 110 | Rational &operator=(const Rational &x) 111 | { 112 | if (this == &x) 113 | return *this; 114 | mpq_set(value, x.value); 115 | return *this; 116 | } 117 | 118 | Rational &operator=(const double x) 119 | { 120 | mpq_set_d(value, x); 121 | // canonicalize(); 122 | return *this; 123 | } 124 | 125 | //> < == 126 | friend bool operator<(const Rational &r, const Rational &r1) 127 | { 128 | return mpq_cmp(r.value, r1.value) < 0; 129 | } 130 | 131 | friend bool operator>(const Rational &r, const Rational &r1) 132 | { 133 | return mpq_cmp(r.value, r1.value) > 0; 134 | } 135 | 136 | friend bool operator<=(const Rational &r, const Rational &r1) 137 | { 138 | return mpq_cmp(r.value, r1.value) <= 0; 139 | } 140 | 141 | friend bool operator>=(const Rational &r, const Rational &r1) 142 | { 143 | return mpq_cmp(r.value, r1.value) >= 0; 144 | } 145 | 146 | friend bool operator==(const Rational &r, const Rational &r1) 147 | { 148 | return mpq_equal(r.value, r1.value); 149 | } 150 | 151 | friend bool operator!=(const Rational &r, const Rational &r1) 152 | { 153 | return !mpq_equal(r.value, r1.value); 154 | } 155 | 156 | //to double 157 | double to_double() 158 | { 159 | return mpq_get_d(value); 160 | } 161 | 162 | //<< 163 | friend std::ostream &operator<<(std::ostream &os, const Rational &r) 164 | { 165 | os << mpq_get_d(r.value); 166 | return os; 167 | } 168 | }; 169 | } 170 | 171 | #endif -------------------------------------------------------------------------------- /src/Types.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | #include 5 | 6 | namespace fastEnvelope { 7 | #ifdef FAST_ENVELOPE_USE_FLOAT 8 | typedef float Scalar; 9 | #define SCALAR_ZERO 1e-6 10 | #define SCALAR_ZERO_2 1e-12 11 | #define SCALAR_ZERO_3 1e-18 12 | #else 13 | typedef double Scalar; 14 | #define SCALAR_ZERO 1e-8 15 | #define SCALAR_ZERO_2 1e-16 16 | #define SCALAR_ZERO_3 1e-24 17 | 18 | #endif 19 | 20 | 21 | static const bool OUT_PRISM = 1; 22 | static const bool IN_PRISM = 0; 23 | 24 | static const int FE_CUT_COPLANAR = 4; 25 | static const int FE_CUT_EMPTY = -1; 26 | static const int FE_CUT_FACE = 3; 27 | 28 | static const int NOT_DEGENERATED = 0; 29 | static const int NERLY_DEGENERATED = 1; 30 | static const int DEGENERATED_SEGMENT = 2; 31 | static const int DEGENERATED_POINT = 3; 32 | 33 | typedef Eigen::Matrix Matrix3; 34 | 35 | typedef Eigen::Matrix Vector3; 36 | typedef Eigen::Matrix Vector2; 37 | 38 | 39 | typedef Eigen::Matrix Vector4i; 40 | typedef Eigen::Matrix Vector3i; 41 | typedef Eigen::Matrix Vector2i; 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/common_algorithms.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | namespace fastEnvelope { 7 | namespace algorithms { 8 | int seg_cut_plane(const Vector3 &seg0, const Vector3 &seg1, const Vector3 &t0, const Vector3 &t1, const Vector3 &t2); 9 | 10 | int is_triangle_degenerated(const Vector3& triangle0, const Vector3& triangle1, const Vector3& triangle2); 11 | Vector3 accurate_normal_vector(const Vector3 &p0, const Vector3 &p1, const Vector3 &p2); 12 | void get_tri_corners(const Vector3 &triangle0, const Vector3 &triangle1, const Vector3 &triangle2, Vector3 &mint, Vector3 &maxt); 13 | bool box_box_intersection(const Vector3 &min1, const Vector3 &max1, const Vector3 &min2, const Vector3 &max2);//TDOO; 14 | Vector2 to_2d(const Vector3 &p, int t); 15 | //resort the facets using Morton's code 16 | void resorting(const std::vector &V, const std::vector &F, std::vector &fnew); 17 | void resorting(const std::vector &V, const std::vector &F, std::vector &fnew, std::vector& new2old); 18 | void seg_cube(const Vector3 &p1, const Vector3 &p2, const Scalar &width, std::array &envbox); 19 | 20 | void halfspace_generation(const std::vector &m_ver, const std::vector &m_faces, std::vector>>& halfspace, 21 | std::vector>& cornerlist, const Scalar &epsilon); 22 | void halfspace_generation(const std::vector &m_ver, const std::vector &m_faces, std::vector>>& halfspace, 23 | std::vector>& cornerlist, const std::vector& epsilons); 24 | void get_bb_corners(const std::vector &vertices, Vector3 &min, Vector3 &max); 25 | } 26 | } -------------------------------------------------------------------------------- /src/external/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SOURCES 2 | Predicates.cpp 3 | Predicates.hpp 4 | 5 | triangle_triangle_intersection.cpp 6 | ) 7 | 8 | prepend_current_path(SOURCES) 9 | fast_envelope_copy_headers(${SOURCES}) 10 | fast_envelope_set_source_group(${SOURCES}) 11 | target_sources(FastEnvelope PRIVATE ${SOURCES}) 12 | 13 | -------------------------------------------------------------------------------- /src/external/Predicates.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #ifdef ENVELOPE_WITH_GEO 4 | #ifdef ENVELOPE_WITH_GEO_PSM 5 | #include 6 | #else 7 | #include 8 | #endif 9 | #else 10 | #include 11 | #endif 12 | 13 | namespace fastEnvelope 14 | { 15 | namespace 16 | { 17 | void init() 18 | { 19 | #ifndef ENVELOPE_WITH_GEO 20 | igl::predicates::exactinit(); 21 | #endif 22 | } 23 | } 24 | 25 | const int Predicates::ORI_POSITIVE; 26 | const int Predicates::ORI_ZERO; 27 | const int Predicates::ORI_NEGATIVE; 28 | const int Predicates::ORI_UNKNOWN; 29 | 30 | int Predicates::orient_3d(const Vector3 &p1, const Vector3 &p2, const Vector3 &p3, const Vector3 &p4) 31 | { 32 | init(); 33 | #ifdef ENVELOPE_WITH_GEO 34 | const int result = -GEO::PCK::orient_3d(p1.data(), p2.data(), p3.data(), p4.data()); 35 | #else 36 | const int result = (int)igl::predicates::orient3d(p1, p2, p3, p4); 37 | #endif 38 | 39 | // if (result > SCALAR_ZERO) 40 | // return ORI_POSITIVE; 41 | // else if (result < -SCALAR_ZERO) 42 | // return ORI_NEGATIVE; 43 | // else 44 | // return ORI_ZERO; 45 | 46 | if (result > 0) 47 | return ORI_POSITIVE; 48 | else if (result < 0) 49 | return ORI_NEGATIVE; 50 | else 51 | return ORI_ZERO; 52 | } 53 | 54 | // int Predicates::orient_3d_tolerance(const Vector3& p1, const Vector3& p2, const Vector3& p3, const Vector3& p) { 55 | // const Scalar result = orient3d(p1.data(), p2.data(), p3.data(), p.data()); 56 | // if (result == 0) 57 | // return ORI_ZERO; 58 | 59 | // Vector3 n = ((p2 - p3).cross(p1 - p3)).normalized(); 60 | // Scalar d = std::abs(n.dot(p - p1)); 61 | // if (d <= SCALAR_ZERO) 62 | // return Predicates::ORI_ZERO; 63 | 64 | // if (result > 0) 65 | // return ORI_POSITIVE; 66 | // else 67 | // return ORI_NEGATIVE; 68 | // } 69 | 70 | Scalar Predicates::orient_3d_volume(const Vector3 &p1, const Vector3 &p2, const Vector3 &p3, const Vector3 &p4) 71 | { 72 | init(); 73 | #ifdef ENVELOPE_WITH_GEO 74 | const int ori = -GEO::PCK::orient_3d(p1.data(), p2.data(), p3.data(), p4.data()); 75 | #else 76 | const int ori = (int)igl::predicates::orient3d(p1, p2, p3, p4); 77 | #endif 78 | if (ori <= 0) 79 | return ori; 80 | else 81 | return (p1 - p4).dot((p2 - p4).cross(p3 - p4)) / 6; 82 | } 83 | 84 | int Predicates::orient_2d(const Vector2 &p1, const Vector2 &p2, const Vector2 &p3) 85 | { 86 | init(); 87 | #ifdef ENVELOPE_WITH_GEO 88 | const int result = -GEO::PCK::orient_2d(p1.data(), p2.data(), p3.data()); 89 | #else 90 | const int result = (int)igl::predicates::orient2d(p1, p2, p3); 91 | #endif 92 | if (result > 0) 93 | return ORI_POSITIVE; 94 | else if (result < 0) 95 | return ORI_NEGATIVE; 96 | else 97 | return ORI_ZERO; 98 | } 99 | 100 | } // namespace fastEnvelope 101 | -------------------------------------------------------------------------------- /src/external/Predicates.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace fastEnvelope { 6 | 7 | class Predicates 8 | { 9 | public: 10 | static const int ORI_POSITIVE = 1; 11 | static const int ORI_ZERO = 0; 12 | static const int ORI_NEGATIVE = -1; 13 | static const int ORI_UNKNOWN = INT_MAX; 14 | 15 | static int orient_3d(const Vector3& p1, const Vector3& p2, const Vector3& p3, const Vector3& p4); 16 | // static int orient_3d_tolerance(const Vector3& p1, const Vector3& p2, const Vector3& p3, const Vector3& p4); 17 | static Scalar orient_3d_volume(const Vector3& p1, const Vector3& p2, const Vector3& p3, const Vector3& p4); 18 | 19 | static int orient_2d(const Vector2& p1, const Vector2& p2, const Vector2& p3); 20 | }; 21 | 22 | } // namespace floattetwild 23 | -------------------------------------------------------------------------------- /src/external/triangle_triangle_intersection.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Triangle-Triangle Overlap Test Routines 3 | * July, 2002 4 | * Updated December 2003 5 | * 6 | * This file contains C implementation of algorithms for 7 | * performing two and three-dimensional triangle-triangle intersection test 8 | * The algorithms and underlying theory are described in 9 | * 10 | * "Fast and Robust Triangle-Triangle Overlap Test 11 | * Using Orientation Predicates" P. Guigue - O. Devillers 12 | * 13 | * Journal of Graphics Tools, 8(1), 2003 14 | * 15 | * Several geometric predicates are defined. Their parameters are all 16 | * points. Each point is an array of two or three real precision 17 | * floating point numbers. The geometric predicates implemented in 18 | * this file are: 19 | * 20 | * 21 | * int tri_tri_intersection_test_3d(p1,q1,r1,p2,q2,r2, 22 | * coplanar,source,target) 23 | * 24 | * is a version that computes the segment of intersection when 25 | * the triangles overlap (and are not coplanar) 26 | * 27 | * each function returns 1 if the triangles (including their 28 | * boundary) intersect, otherwise 0 29 | * 30 | * 31 | * Other information are available from the Web page 32 | * http://www.acm.org/jgt/papers/GuigueDevillers03/ 33 | * 34 | */ 35 | 36 | // modified by Aaron to better detect coplanarity 37 | 38 | 39 | #ifdef FAST_ENVELOPE_USE_FLOAT 40 | typedef float real; // float 41 | #else 42 | typedef double Scalar; // double 43 | #endif 44 | 45 | /* function prototype */ 46 | 47 | 48 | 49 | int tri_tri_intersection_test_3d(Scalar p1[3], Scalar q1[3], Scalar r1[3], 50 | Scalar p2[3], Scalar q2[3], Scalar r2[3], 51 | int * coplanar, 52 | Scalar source[3],Scalar target[3]); 53 | 54 | int sub_sub_cross_sub_dot(Scalar a[3], Scalar b[3], Scalar c[3], Scalar d[3]); 55 | 56 | 57 | 58 | /* coplanar returns whether the triangles are coplanar 59 | * source and target are the endpoints of the segment of 60 | * intersection if it exists) 61 | */ 62 | 63 | 64 | /* some 3D macros */ 65 | 66 | #define CROSS(dest,v1,v2) \ 67 | dest[0]=v1[1]*v2[2]-v1[2]*v2[1]; \ 68 | dest[1]=v1[2]*v2[0]-v1[0]*v2[2]; \ 69 | dest[2]=v1[0]*v2[1]-v1[1]*v2[0]; 70 | 71 | 72 | #define DOT(v1,v2) (v1[0]*v2[0]+v1[1]*v2[1]+v1[2]*v2[2]) 73 | 74 | 75 | 76 | #define SUB(dest,v1,v2) dest[0]=v1[0]-v2[0]; \ 77 | dest[1]=v1[1]-v2[1]; \ 78 | dest[2]=v1[2]-v2[2]; 79 | 80 | 81 | #define SCALAR(dest,alpha,v) dest[0] = alpha * v[0]; \ 82 | dest[1] = alpha * v[1]; \ 83 | dest[2] = alpha * v[2]; 84 | 85 | 86 | /* 87 | * 88 | * Three-dimensional Triangle-Triangle Intersection 89 | * 90 | */ 91 | 92 | /* 93 | This macro is called when the triangles surely intersect 94 | It constructs the segment of intersection of the two triangles 95 | if they are not coplanar. 96 | */ 97 | 98 | #define CONSTRUCT_INTERSECTION(p1,q1,r1,p2,q2,r2) { \ 99 | if (sub_sub_cross_sub_dot(q1, r2, p1, p2) > 0) {\ 100 | if (sub_sub_cross_sub_dot(r1, r2, p1, p2) <= 0) {\ 101 | if (sub_sub_cross_sub_dot(r1, q2, p1, p2) > 0) {\ 102 | SUB(v1,p1,p2) \ 103 | SUB(v2,p1,r1) \ 104 | alpha = DOT(v1,N2) / DOT(v2,N2); \ 105 | SCALAR(v1,alpha,v2) \ 106 | SUB(source,p1,v1) \ 107 | SUB(v1,p2,p1) \ 108 | SUB(v2,p2,r2) \ 109 | alpha = DOT(v1,N1) / DOT(v2,N1); \ 110 | SCALAR(v1,alpha,v2) \ 111 | SUB(target,p2,v1) \ 112 | return 1; \ 113 | }\ 114 | else { \ 115 | SUB(v1,p2,p1) \ 116 | SUB(v2,p2,q2) \ 117 | alpha = DOT(v1,N1) / DOT(v2,N1); \ 118 | SCALAR(v1,alpha,v2) \ 119 | SUB(source,p2,v1) \ 120 | SUB(v1,p2,p1) \ 121 | SUB(v2,p2,r2) \ 122 | alpha = DOT(v1,N1) / DOT(v2,N1); \ 123 | SCALAR(v1,alpha,v2) \ 124 | SUB(target,p2,v1) \ 125 | return 1; \ 126 | } \ 127 | } \ 128 | else { \ 129 | return 0; \ 130 | } \ 131 | } \ 132 | else { \ 133 | if (sub_sub_cross_sub_dot(q1, q2, p1, p2) < 0) {\ 134 | return 0; \ 135 | } \ 136 | else { \ 137 | if (sub_sub_cross_sub_dot(r1, q2, p1, p2) >= 0) {\ 138 | SUB(v1,p1,p2) \ 139 | SUB(v2,p1,r1) \ 140 | alpha = DOT(v1,N2) / DOT(v2,N2); \ 141 | SCALAR(v1,alpha,v2) \ 142 | SUB(source,p1,v1) \ 143 | SUB(v1,p1,p2) \ 144 | SUB(v2,p1,q1) \ 145 | alpha = DOT(v1,N2) / DOT(v2,N2); \ 146 | SCALAR(v1,alpha,v2) \ 147 | SUB(target,p1,v1) \ 148 | return 1; \ 149 | } \ 150 | else { \ 151 | SUB(v1,p2,p1) \ 152 | SUB(v2,p2,q2) \ 153 | alpha = DOT(v1,N1) / DOT(v2,N1); \ 154 | SCALAR(v1,alpha,v2) \ 155 | SUB(source,p2,v1) \ 156 | SUB(v1,p1,p2) \ 157 | SUB(v2,p1,q1) \ 158 | alpha = DOT(v1,N2) / DOT(v2,N2); \ 159 | SCALAR(v1,alpha,v2) \ 160 | SUB(target,p1,v1) \ 161 | return 1; \ 162 | } \ 163 | } \ 164 | } \ 165 | } 166 | 167 | 168 | 169 | #define TRI_TRI_INTER_3D(p1,q1,r1,p2,q2,r2,dp2,dq2,dr2) { \ 170 | if (dp2 > 0) { \ 171 | if (dq2 > 0) CONSTRUCT_INTERSECTION(p1,r1,q1,r2,p2,q2) \ 172 | else if (dr2 > 0) CONSTRUCT_INTERSECTION(p1,r1,q1,q2,r2,p2)\ 173 | else CONSTRUCT_INTERSECTION(p1,q1,r1,p2,q2,r2) }\ 174 | else if (dp2 < 0) { \ 175 | if (dq2 < 0) CONSTRUCT_INTERSECTION(p1,q1,r1,r2,p2,q2)\ 176 | else if (dr2 < 0) CONSTRUCT_INTERSECTION(p1,q1,r1,q2,r2,p2)\ 177 | else CONSTRUCT_INTERSECTION(p1,r1,q1,p2,q2,r2)\ 178 | } else { \ 179 | if (dq2 < 0) { \ 180 | if (dr2 >= 0) CONSTRUCT_INTERSECTION(p1,r1,q1,q2,r2,p2)\ 181 | else CONSTRUCT_INTERSECTION(p1,q1,r1,p2,q2,r2)\ 182 | } \ 183 | else if (dq2 > 0) { \ 184 | if (dr2 > 0) CONSTRUCT_INTERSECTION(p1,r1,q1,p2,q2,r2)\ 185 | else CONSTRUCT_INTERSECTION(p1,q1,r1,q2,r2,p2)\ 186 | } \ 187 | else { \ 188 | if (dr2 > 0) CONSTRUCT_INTERSECTION(p1,q1,r1,r2,p2,q2)\ 189 | else if (dr2 < 0) CONSTRUCT_INTERSECTION(p1,r1,q1,r2,p2,q2)\ 190 | else { \ 191 | *coplanar = 1; \ 192 | return -1;\ 193 | } \ 194 | }} } 195 | 196 | 197 | /* 198 | The following version computes the segment of intersection of the 199 | two triangles if it exists. 200 | coplanar returns whether the triangles are coplanar 201 | source and target are the endpoints of the line segment of intersection 202 | */ 203 | 204 | //extern "C" real orient3d(const real *pa, const real *pb, const real *pc, const real *pd); 205 | #include 206 | 207 | inline int sub_sub_cross_sub_dot(Scalar pa[3], Scalar pb[3], Scalar pc[3], Scalar pd[3]) 208 | { 209 | const Scalar res = fastEnvelope::Predicates::orient_3d(fastEnvelope::Vector3(pa[0], pa[1], pa[2]), fastEnvelope::Vector3(pb[0], pb[1], pb[2]), 210 | fastEnvelope::Vector3(pc[0], pc[1], pc[2]), fastEnvelope::Vector3(pd[0], pd[1], pd[2])); 211 | 212 | if(res > 0) 213 | return 1; 214 | if(res < 0) 215 | return -1; 216 | 217 | return 0; 218 | } 219 | 220 | int tri_tri_intersection_test_3d(Scalar p1[3], Scalar q1[3], Scalar r1[3], 221 | Scalar p2[3], Scalar q2[3], Scalar r2[3], 222 | int* coplanar, 223 | Scalar source[3], Scalar target[3] ) 224 | 225 | { 226 | int dp1, dq1, dr1, dp2, dq2, dr2; 227 | Scalar v1[3], v2[3], v[3]; 228 | Scalar N1[3], N2[3], N[3]; 229 | Scalar alpha; 230 | 231 | SUB(v1,q1,p1) 232 | SUB(v2,r1,p1) 233 | CROSS(N1,v1,v2) 234 | 235 | SUB(v1,p2,r2) 236 | SUB(v2,q2,r2) 237 | CROSS(N2,v1,v2) 238 | 239 | 240 | *coplanar = 0; 241 | 242 | // Compute distance signs of p1, q1 and r1 243 | // to the plane of triangle(p2,q2,r2) 244 | 245 | 246 | dp1 = sub_sub_cross_sub_dot(p2, q2, r2, p1); 247 | dq1 = sub_sub_cross_sub_dot(p2, q2, r2, q1); 248 | dr1 = sub_sub_cross_sub_dot(p2, q2, r2, r1); 249 | 250 | if (((dp1 * dq1) > 0) && ((dp1 * dr1) > 0)) return 0; 251 | 252 | // Compute distance signs of p2, q2 and r2 253 | // to the plane of triangle(p1,q1,r1) 254 | 255 | dp2 = sub_sub_cross_sub_dot(p1, q1, r1, p2); 256 | dq2 = sub_sub_cross_sub_dot(p1, q1, r1, q2); 257 | dr2 = sub_sub_cross_sub_dot(p1, q1, r1, r2); 258 | 259 | if (((dp2 * dq2) > 0) && ((dp2 * dr2) > 0)) return 0; 260 | 261 | // Permutation in a canonical form of T1's vertices 262 | 263 | if (dp1 > 0) { 264 | if (dq1 > 0) TRI_TRI_INTER_3D(r1,p1,q1,p2,r2,q2,dp2,dr2,dq2) 265 | else if (dr1 > 0) TRI_TRI_INTER_3D(q1,r1,p1,p2,r2,q2,dp2,dr2,dq2) 266 | 267 | else TRI_TRI_INTER_3D(p1,q1,r1,p2,q2,r2,dp2,dq2,dr2) 268 | } else if (dp1 < 0) { 269 | if (dq1 < 0) TRI_TRI_INTER_3D(r1,p1,q1,p2,q2,r2,dp2,dq2,dr2) 270 | else if (dr1 < 0) TRI_TRI_INTER_3D(q1,r1,p1,p2,q2,r2,dp2,dq2,dr2) 271 | else TRI_TRI_INTER_3D(p1,q1,r1,p2,r2,q2,dp2,dr2,dq2) 272 | } else { 273 | if (dq1 < 0) { 274 | if (dr1 >= 0) TRI_TRI_INTER_3D(q1,r1,p1,p2,r2,q2,dp2,dr2,dq2) 275 | else TRI_TRI_INTER_3D(p1,q1,r1,p2,q2,r2,dp2,dq2,dr2) 276 | } 277 | else if (dq1 > 0) { 278 | if (dr1 > 0) TRI_TRI_INTER_3D(p1,q1,r1,p2,r2,q2,dp2,dr2,dq2) 279 | else TRI_TRI_INTER_3D(q1,r1,p1,p2,q2,r2,dp2,dq2,dr2) 280 | } 281 | else { 282 | if (dr1 > 0) TRI_TRI_INTER_3D(r1,p1,q1,p2,q2,r2,dp2,dq2,dr2) 283 | else if (dr1 < 0) TRI_TRI_INTER_3D(r1,p1,q1,p2,r2,q2,dp2,dr2,dq2) 284 | else { 285 | // triangles are co-planar 286 | 287 | *coplanar = 1; 288 | 289 | return -1; 290 | } 291 | } 292 | } 293 | }; 294 | -------------------------------------------------------------------------------- /src/obb.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include// for is_triangle_degenerated() 6 | namespace fastEnvelope { 7 | 8 | std::vector obb::build_obb_matrixs(const std::vector& face_vertices, const int p_face[8][3], const int c_face[6][3], 9 | const std::array, 8>& p_facepoint, const std::array, 6>& c_facepoint) { 10 | std::vector points; 11 | 12 | int dege; 13 | Vector3 normal; 14 | std::vector M; 15 | M.reserve(8); 16 | 17 | if (face_vertices.size() == polyhedron_point_number1) { 18 | //M.resize(polyhedron_face_number1); 19 | 20 | for (int j = 0; j < polyhedron_face_number1; j++) { 21 | //triangle to get normal: {envelope_vertices[i][p_face[j][0]],envelope_vertices[i][p_face[j][1]],envelope_vertices[i][p_face[j][2]]} 22 | #ifdef DO_NOT_HAVE_DEGENERATED_FACES 23 | if (((face_vertices[p_face[j][0]] - face_vertices[p_face[j][1]]).cross( 24 | face_vertices[p_face[j][0]] - face_vertices[p_face[j][2]])).norm() < SCALAR_ZERO) 25 | normal = FastEnvelope::accurate_normal_vector(face_vertices[p_face[j][0]], face_vertices[p_face[j][1]], 26 | face_vertices[p_face[j][0]], face_vertices[p_face[j][2]]); 27 | else { 28 | normal = ((face_vertices[p_face[j][0]] - face_vertices[p_face[j][1]]).cross(face_vertices[p_face[j][0]] - face_vertices[p_face[j][2]])).normalized(); 29 | } 30 | #else 31 | //triangle to get normal: {envelope_vertices[i][p_face[j][0]],envelope_vertices[i][p_face[j][1]],envelope_vertices[i][p_face[j][2]]} 32 | 33 | dege = FastEnvelope::is_triangle_degenerated(face_vertices[p_face[j][0]], face_vertices[p_face[j][1]], face_vertices[p_face[j][2]]); 34 | if (dege == FastEnvelope::DEGENERATED_SEGMENT || dege == FastEnvelope::DEGENERATED_POINT) { 35 | std::cout << "need to fix here, face degeneration" << std::endl; 36 | exit(0); 37 | } 38 | 39 | if (dege == FastEnvelope::NERLY_DEGENERATED) { 40 | normal = FastEnvelope::accurate_normal_vector(face_vertices[p_face[j][0]], face_vertices[p_face[j][1]], 41 | face_vertices[p_face[j][0]], face_vertices[p_face[j][2]]); 42 | } 43 | if (dege == FastEnvelope::NOT_DEGENERATED) { 44 | normal = ((face_vertices[p_face[j][0]] - face_vertices[p_face[j][1]]).cross(face_vertices[p_face[j][0]] - face_vertices[p_face[j][2]])).normalized(); 45 | } 46 | #endif 47 | points.clear(); 48 | points.resize(p_facepoint[j].size()); 49 | for (int k = 0; k < p_facepoint[j].size(); k++) { 50 | points[k] = face_vertices[p_facepoint[j][k]]; 51 | } 52 | 53 | M.emplace_back(); 54 | build_obb(points, normal, OBB_OFFSET, M.back().Trans, M.back().invTrans); 55 | } 56 | 57 | } 58 | 59 | if (face_vertices.size() == polyhedron_point_number2) { 60 | M.resize(polyhedron_face_number2); 61 | 62 | for (int j = 0; j < polyhedron_face_number2; j++) { 63 | #ifdef DO_NOT_HAVE_DEGENERATED_FACES 64 | if (((face_vertices[c_face[j][0]] - face_vertices[c_face[j][1]]).cross( 65 | face_vertices[c_face[j][0]] - face_vertices[c_face[j][2]])).norm() < SCALAR_ZERO) 66 | normal = FastEnvelope::accurate_normal_vector(face_vertices[c_face[j][0]], face_vertices[c_face[j][1]], 67 | face_vertices[c_face[j][0]], face_vertices[c_face[j][2]]); 68 | else { 69 | normal = (face_vertices[c_face[j][0]] - face_vertices[c_face[j][1]]).cross(face_vertices[c_face[j][0]] - face_vertices[c_face[j][2]]).normalized(); 70 | } 71 | #else 72 | 73 | 74 | 75 | //triangle to get normal: {envelope_vertices[i][p_face[j][0]],envelope_vertices[i][p_face[j][1]],envelope_vertices[i][p_face[j][2]]} 76 | 77 | dege = FastEnvelope::is_triangle_degenerated(face_vertices[c_face[j][0]], face_vertices[c_face[j][1]], face_vertices[c_face[j][2]]); 78 | if (dege == FastEnvelope::DEGENERATED_SEGMENT || dege == FastEnvelope::DEGENERATED_POINT) { 79 | std::cout << "need to fix here, face degeneration" << std::endl; 80 | exit(0); 81 | } 82 | 83 | if (dege == FastEnvelope::NERLY_DEGENERATED) { 84 | normal = FastEnvelope::accurate_normal_vector(face_vertices[c_face[j][0]], face_vertices[c_face[j][1]], 85 | face_vertices[c_face[j][0]], face_vertices[c_face[j][2]]); 86 | } 87 | if (dege == FastEnvelope::NOT_DEGENERATED) { 88 | normal = (face_vertices[c_face[j][0]] - face_vertices[c_face[j][1]]).cross(face_vertices[c_face[j][0]] - face_vertices[c_face[j][2]]).normalized(); 89 | } 90 | 91 | #endif 92 | points.clear(); 93 | points.resize(c_facepoint[j].size()); 94 | for (int k = 0; k < c_facepoint[j].size(); k++) { 95 | points[k] = face_vertices[c_facepoint[j][k]]; 96 | } 97 | 98 | M.emplace_back(); 99 | build_obb(points, normal, OBB_OFFSET, M.back().Trans, M.back().invTrans); 100 | } 101 | } 102 | return M; 103 | 104 | } 105 | 106 | bool obb::intersects(const obb& M2) const { 107 | return obb_intersection(Trans, invTrans, M2.Trans, M2.invTrans); 108 | } 109 | 110 | bool obb::intersects(const obb& M2, const obb& M3) const { 111 | 112 | //bool flag1=obb_intersection(Trans, invTrans, M2.Trans, M2.invTrans); 113 | bool flag1 = intersects(M2); 114 | if (flag1 == false) return false; 115 | 116 | //bool flag2 = obb_intersection(Trans, invTrans, M3.Trans, M3.invTrans); 117 | bool flag2 = intersects(M3); 118 | if (flag2 == false) return false; 119 | 120 | //bool flag3 = obb_intersection(M2.Trans, M2.invTrans, M3.Trans, M3.invTrans); 121 | bool flag3 = M2.intersects(M3); 122 | if (flag3 == false) return false; 123 | 124 | return true; 125 | } 126 | 127 | obb obb::build_triangle_obb_matrixs(const Vector3&t0, const Vector3&t1, const Vector3&t2) { 128 | Vector3 normal; 129 | obb res; 130 | if (((t0 - t1).cross(t0 - t2)).norm() < SCALAR_ZERO) 131 | normal = FastEnvelope::accurate_normal_vector(t0, t1, 132 | t0, t2); 133 | else { 134 | normal = ((t0 - t1).cross(t0 - t2)).normalized(); 135 | } 136 | std::vector points(3); 137 | points[0] = t0; points[1] = t1; points[2] = t2; 138 | build_obb(points, normal, OBB_OFFSET, res.Trans, res.invTrans); 139 | return res; 140 | } 141 | 142 | void obb::build_obb(const std::vector& points, const Vector3& normal, const Scalar offset, 143 | Eigen::Matrix4d &M, Eigen::Matrix4d &invM) 144 | { 145 | Eigen::MatrixXd PS(3, points.size()), PS1(3, points.size()); 146 | Vector3 cent; 147 | cent << 148 | 0, 0, 0; 149 | 150 | for (int i = 0; i < points.size(); i++) { 151 | PS(0, i) = points[i][0]; 152 | PS(1, i) = points[i][1]; 153 | PS(2, i) = points[i][2]; 154 | 155 | } 156 | cent[0] = PS.row(0).sum() / points.size(); 157 | cent[1] = PS.row(1).sum() / points.size(); 158 | cent[2] = PS.row(2).sum() / points.size(); 159 | PS1.row(0) = PS.row(0) - Eigen::MatrixXd::Ones(1, points.size())*cent[0]; 160 | PS1.row(1) = PS.row(1) - Eigen::MatrixXd::Ones(1, points.size())*cent[1]; 161 | PS1.row(2) = PS.row(2) - Eigen::MatrixXd::Ones(1, points.size())*cent[2]; 162 | Eigen::MatrixXd PCA = PS1 * PS1.transpose(); 163 | Eigen::EigenSolver eg(PCA); 164 | Eigen::VectorXcd ev = eg.eigenvalues(); 165 | 166 | int maxid = 0; 167 | 168 | for (int i = 1; i < 3; i++) { 169 | 170 | if (ev[maxid].real() <= ev(i).real()) { 171 | maxid = i; 172 | } 173 | } 174 | assert(ev(maxid).real() > 0); 175 | Eigen::VectorXcd v = eg.eigenvectors().col(maxid); 176 | Vector3 y; 177 | y[0] = v[0].real(); y[1] = v[1].real(); y[2] = v[2].real(); 178 | y = y.normalized(); 179 | assert(normal.dot(y) < SCALAR_ZERO); 180 | 181 | Vector3 z = normal.cross(y); 182 | Matrix3 R; 183 | R.col(0) = normal; 184 | R.col(1) = y; 185 | R.col(2) = z; 186 | 187 | Matrix3 invR = R.transpose();//inv(R)=trans(R) 188 | Vector3 T0 = -1 * R*cent, T1;//R*cent+T0=[0,0,0] 189 | Eigen::Matrix4d Trans0, invTrans0, Trans1, invTrans1, Scalling, invScalling; 190 | 191 | Trans0 << R, T0, 192 | 0, 0, 0, 1; 193 | invTrans0 << invR, cent, 194 | 0, 0, 0, 1; 195 | Eigen::MatrixXd PSG(4, points.size()), prog(4, points.size()); 196 | PSG << PS, 197 | Eigen::MatrixXd::Ones(1, points.size()); 198 | prog = Trans0 * PSG;//contains the projection of the points in new axis 199 | Vector3 min, max, mid, corner; 200 | for (int i = 0; i < 3; i++) { 201 | min[i] = prog.row(i).minCoeff(); 202 | max[i] = prog.row(i).maxCoeff(); 203 | } 204 | mid = (min + max) / 2; 205 | corner = max - mid + Vector3(offset, offset, offset); 206 | Eigen::MatrixXd procg(4, 1), centng(4, 1), centn(3, 1); 207 | procg << mid, 208 | 1; 209 | centng = invTrans0 * procg; 210 | centn(0) = centng(0); centn(1) = centng(1); centn(2) = centng(2); 211 | T1 = -1 * R*centn; 212 | Trans1 << R, T1, 213 | 0, 0, 0, 1; 214 | invTrans1 << invR, centn, 215 | 0, 0, 0, 1; 216 | Scalling = Eigen::Matrix4d::Zero(4, 4); 217 | Scalling(0, 0) = corner(0); 218 | Scalling(1, 1) = corner(1); 219 | Scalling(2, 2) = corner(2); 220 | Scalling(3, 3) = 1; 221 | invScalling = Scalling.inverse(); 222 | M = invScalling * Trans1; 223 | invM = invTrans1 * Scalling; 224 | } 225 | 226 | bool obb::obb_intersection(const Eigen::Matrix4d &M1, const Eigen::Matrix4d &invM1, const Eigen::Matrix4d &M2, const Eigen::Matrix4d &invM2) { 227 | 228 | Eigen::MatrixXd ub(8, 4); 229 | 230 | ub << -1, -1, -1, 1, 231 | 1, -1, -1, 1, 232 | 1, 1, -1, 1, 233 | -1, 1, -1, 1, 234 | -1, 1, 1, 1, 235 | -1, -1, 1, 1, 236 | 1, -1, 1, 1, 237 | 1, 1, 1, 1; 238 | Eigen::MatrixXd b21(4, 8), b12(4, 8); 239 | 240 | Scalar minx, miny, minz, maxx, maxy, maxz; 241 | 242 | b12 = M2 * invM1*ub.transpose(); 243 | 244 | minx = b12.row(0).minCoeff(); 245 | if (minx >= 1) return false; 246 | miny = b12.row(1).minCoeff(); 247 | if (miny >= 1) return false; 248 | minz = b12.row(2).minCoeff(); 249 | if (minz >= 1) return false; 250 | 251 | maxx = b12.row(0).maxCoeff(); 252 | if (maxx <= -1) return false; 253 | maxy = b12.row(1).maxCoeff(); 254 | if (maxy <= -1) return false; 255 | maxz = b12.row(2).maxCoeff(); 256 | if (maxz <= -1) return false; 257 | 258 | b21 = M1 * invM2*ub.transpose(); 259 | 260 | minx = b21.row(0).minCoeff(); 261 | if (minx >= 1) return false; 262 | miny = b21.row(1).minCoeff(); 263 | if (miny >= 1) return false; 264 | minz = b21.row(2).minCoeff(); 265 | if (minz >= 1) return false; 266 | 267 | maxx = b21.row(0).maxCoeff(); 268 | if (maxx <= -1) return false; 269 | maxy = b21.row(1).maxCoeff(); 270 | if (maxy <= -1) return false; 271 | maxz = b21.row(2).maxCoeff(); 272 | if (maxz <= -1) return false; 273 | 274 | 275 | return true; 276 | 277 | } 278 | #include 279 | void obb::test() { 280 | std::vector p, p1; 281 | Vector3 dis; 282 | srand(int(time(0))); 283 | dis = Vector3(rand() % 100, rand() % 100, rand() % 100) / 50; 284 | p.resize(20); 285 | for (int i = 0; i < p.size(); i++) { 286 | p[i] = Vector3(rand() % 100, rand() % 100, rand() % 100) / 100 + dis; 287 | } 288 | Eigen::MatrixXd PS(3, p.size()); 289 | 290 | 291 | for (int i = 0; i < p.size(); i++) { 292 | PS(0, i) = p[i][0]; 293 | PS(1, i) = p[i][1]; 294 | PS(2, i) = p[i][2]; 295 | 296 | } 297 | 298 | Eigen::MatrixXd PCA = PS * PS.transpose(); 299 | Eigen::EigenSolver eg(PCA); 300 | Eigen::VectorXcd ev = eg.eigenvalues(); 301 | 302 | int minid = 0; 303 | 304 | for (int i = 1; i < 3; i++) { 305 | 306 | if (ev[minid].real() >= ev(i).real()) {//find the minimal eigenvalue 307 | minid = i; 308 | } 309 | } 310 | 311 | Eigen::VectorXcd v = eg.eigenvectors().col(minid); 312 | Vector3 normal; 313 | normal[0] = v[0].real(); normal[1] = v[1].real(); normal[2] = v[2].real(); 314 | Scalar offset = 0.1; 315 | Eigen::Matrix4d M, invM; 316 | build_obb(p, normal, offset, M, invM); 317 | std::ofstream fout; 318 | fout.open("D:\\vs\\fast_envelope\\obb\\points.txt"); 319 | 320 | for (int i = 0; i < p.size(); i++) { 321 | 322 | fout << std::setprecision(17) << p[i][0] << " " << p[i][1] << " " << p[i][2] << std::endl; 323 | 324 | } 325 | fout.close(); 326 | fout.open("D:\\vs\\fast_envelope\\obb\\matrixes.txt"); 327 | 328 | for (int i = 0; i < 4; i++) { 329 | 330 | fout << std::setprecision(17) << M(i, 0) << " " << M(i, 1) << " " << M(i, 2) << " " << M(i, 3) << std::endl; 331 | 332 | } 333 | for (int i = 0; i < 4; i++) { 334 | 335 | fout << std::setprecision(17) << invM(i, 0) << " " << invM(i, 1) << " " << invM(i, 2) << " " << invM(i, 3) << std::endl; 336 | 337 | } 338 | 339 | fout.close(); 340 | 341 | 342 | 343 | 344 | ////////////////////////// 345 | 346 | //srand(int(time(0))); 347 | dis = Vector3(rand() % 100, rand() % 100, rand() % 100) / 100 * 3; 348 | p.clear(); 349 | p.resize(20); 350 | for (int i = 0; i < p.size(); i++) { 351 | p[i] = Vector3(rand() % 100, rand() % 100, rand() % 100) / 100 + dis; 352 | } 353 | Eigen::MatrixXd PS1(3, p.size()); 354 | 355 | 356 | for (int i = 0; i < p.size(); i++) { 357 | PS1(0, i) = p[i][0]; 358 | PS1(1, i) = p[i][1]; 359 | PS1(2, i) = p[i][2]; 360 | 361 | } 362 | 363 | Eigen::MatrixXd PCA1 = PS1 * PS1.transpose(); 364 | Eigen::EigenSolver eg1(PCA1); 365 | Eigen::VectorXcd ev1 = eg1.eigenvalues(); 366 | 367 | minid = 0; 368 | 369 | for (int i = 1; i < 3; i++) { 370 | 371 | if (ev1[minid].real() >= ev1(i).real()) {//find the minimal eigenvalue 372 | minid = i; 373 | } 374 | } 375 | 376 | Eigen::VectorXcd v1 = eg1.eigenvectors().col(minid); 377 | 378 | normal[0] = v1[0].real(); normal[1] = v1[1].real(); normal[2] = v1[2].real(); 379 | 380 | Eigen::Matrix4d M1, invM1; 381 | build_obb(p, normal, offset, M1, invM1); 382 | 383 | fout.open("D:\\vs\\fast_envelope\\obb\\points1.txt"); 384 | 385 | for (int i = 0; i < p.size(); i++) { 386 | 387 | fout << std::setprecision(17) << p[i][0] << " " << p[i][1] << " " << p[i][2] << std::endl; 388 | 389 | } 390 | fout.close(); 391 | fout.open("D:\\vs\\fast_envelope\\obb\\matrixes1.txt"); 392 | 393 | for (int i = 0; i < 4; i++) { 394 | 395 | fout << std::setprecision(17) << M1(i, 0) << " " << M1(i, 1) << " " << M1(i, 2) << " " << M1(i, 3) << std::endl; 396 | 397 | } 398 | for (int i = 0; i < 4; i++) { 399 | 400 | fout << std::setprecision(17) << invM1(i, 0) << " " << invM1(i, 1) << " " << invM1(i, 2) << " " << invM1(i, 3) << std::endl; 401 | 402 | } 403 | 404 | fout.close(); 405 | 406 | std::cout << "is intersected? " << obb_intersection(M, invM, M1, invM1) << std::endl; 407 | } 408 | } -------------------------------------------------------------------------------- /src/obb.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | // this flag DO_NOT_HAVE_DEGENERATED_FACES is to make code faster when it is sure we dont have any three points that 10 | //define one halfspace is degenerated to a segment, or a point 11 | #define DO_NOT_HAVE_DEGENERATED_FACES 12 | namespace fastEnvelope { 13 | class obb 14 | { 15 | private: 16 | 17 | Eigen::Matrix4d Trans; 18 | Eigen::Matrix4d invTrans; 19 | 20 | public: 21 | 22 | static std::vector build_obb_matrixs(const std::vector& prism_vertices, const int p_face[8][3], const int c_face[6][3], 23 | const std::array, 8>& p_facepoint, const std::array, 6>& c_facepoint); 24 | 25 | static obb build_triangle_obb_matrixs(const Vector3&t0, const Vector3&t1, const Vector3&t2);// can not take degenerated triangle 26 | bool intersects(const obb& M2)const; 27 | bool intersects(const obb& M2, const obb& M3)const; 28 | static void test(); 29 | 30 | private: 31 | static const int polyhedron_point_number1 = 12; 32 | static const int polyhedron_point_number2 = 8; 33 | static const int polyhedron_face_number1 = 8; 34 | static const int polyhedron_face_number2 = 6; 35 | static constexpr double OBB_OFFSET = 1e-4; 36 | 37 | 38 | 39 | //build obb out of a set of points, transformation matrix Trans maps the box into a box which corners are 40 | //[1,1,1] and [-1,-1,-1] 41 | //input: points, normal vector of these points(respect to the minimal eigenvalue of PCA matrix) 42 | //output: transformation matrix and it's inversion 43 | static void build_obb(const std::vector& points, const Vector3& normal, const Scalar offset, 44 | Eigen::Matrix4d &M, Eigen::Matrix4d &invM); 45 | 46 | static bool obb_intersection(const Eigen::Matrix4d &M1, const Eigen::Matrix4d &invM1, const Eigen::Matrix4d &M2, const Eigen::Matrix4d &invM2); 47 | 48 | }; 49 | } --------------------------------------------------------------------------------