From 89694e6d903635dc24a69be3d230f5c9c5691a30 Mon Sep 17 00:00:00 2001 From: Christopher Lee Date: Tue, 17 Aug 2021 15:33:41 -0700 Subject: [PATCH 1/3] Preparing for version 2.0.7 (#50) * Fix bug in version update Signed-off-by: Chris Lee * Update links Signed-off-by: Chris Lee * Update file formatting to use clang-format Signed-off-by: Chris Lee * Refactore cmake modules dir Signed-off-by: Chris Lee * Simplifying travis config Signed-off-by: Chris Lee * Bump cmake min version and update python finding Signed-off-by: Chris Lee * Download libs on travis Signed-off-by: Chris Lee * Print cmake version Signed-off-by: Chris Lee * Install python using choco Signed-off-by: Chris Lee * Shallow clone Signed-off-by: Chris Lee * Avoid using ninja on travis; Windows python3 Signed-off-by: Chris Lee * Setup conda for macos python v control Signed-off-by: Chris Lee * Fix typos Signed-off-by: Chris Lee * Add blender version injection Signed-off-by: Chris Lee * Attempt to fix wildcard Signed-off-by: Chris Lee * Fix typos Signed-off-by: Chris Lee * Add blendgamer deployments for other OS Signed-off-by: Chris Lee * Remove appveyor Signed-off-by: Chris Lee * Fix indentation Signed-off-by: Chris Lee * Add pypi release Signed-off-by: Chris Lee * Faster travis make; Restrict deploy to tags Signed-off-by: Chris Lee * Bump version to test deployment Signed-off-by: Chris Lee * Refactor for find Python3 Signed-off-by: Chris Lee * Bump Blender support version Signed-off-by: Chris Lee * Supporting output of surface mesh stacks to comsol (#48) Major: * Migrated to GitHub Actions for CI and deployment * Added preliminary support for exporting meshes to Comsol Minor: * Update BlendGAMer python formatting * Add macOS deployment target Sierra for BlendGAMer releases * Bind surface area function in PyGAMer * Update tetrahedralization code logic * Update BlendGAMer version check * Fixed building of documentation on readthedocs.io * Bump Eigen to 3.3.9 Signed-off-by: Chris Lee * Squash merge broke 2.0.7-alpha1... oops Signed-off-by: Chris Lee * Bump Tetgen to 1.6 Signed-off-by: Chris Lee * Update googletest configuration Signed-off-by: Chris Lee * Add support for Blender 2.93 Signed-off-by: Chris Lee * Add runtime error macro Signed-off-by: Chris Lee * Resolve runtime error for zero area faces Signed-off-by: Chris Lee * Pretty function workaround on windows Signed-off-by: Chris Lee * Skip normal smooth on boundaries Signed-off-by: Chris Lee * Add tetrahedralize test Signed-off-by: Chris Lee * Add check for tet hole Signed-off-by: Chris Lee * chore: Bump pybind11 to 2.7.1 Signed-off-by: Christopher T. Lee * refactor(blendgamer): Mark comsol output as beta Signed-off-by: Christopher T. Lee Co-authored-by: Justin Laughlin --- .appveyor.yml | 39 - .clang-format | 1 + .github/scripts/blender_pyversion_lookup.py | 31 + .github/workflows/ci.yaml | 151 + .gitignore | 3 + .readthedocs.yml | 3 +- .travis.yml | 338 +- CMakeLists.txt | 180 +- MANIFEST.in | 22 +- README.md | 14 +- VERSION | 2 +- .../FetchContent/CMakeLists.cmake.in | 23 - cmake-modules/FetchContentLocal.cmake | 1061 - cmake-modules/GetGitRevisionDescription.cmake | 168 - cmake-modules/__init__.py.in | 7 - {cmake-modules => cmake}/FindBlender.cmake | 4 +- {cmake-modules => cmake}/FindBreathe.cmake | 10 +- {cmake-modules => cmake}/FindExhale.cmake | 10 +- {cmake-modules => cmake}/FindSphinx.cmake | 0 {cmake-modules => cmake}/Findpytest.cmake | 10 +- cmake/GetGitRevisionDescription.cmake | 279 + .../GetGitRevisionDescription.cmake.in | 0 cmake/Macros.cmake | 112 + {cmake-modules => cmake}/VERSION.in | 0 cmake/__init__.py.in | 17 + {cmake-modules => cmake}/blenderexec.py.in | 0 .../c_flag_overrides.cmake | 0 .../cxx_flag_overrides.cmake | 0 {cmake-modules => cmake}/version.cpp.in | 0 docs/conf.py.in | 8 +- docs/rtd-requirements.txt | 23 +- docs/src/conf.py | 155 - docs/src/install.rst | 7 +- include/CMakeLists.txt | 37 + include/gamer/EigenDiagonalization.h | 168 +- include/gamer/MarchingCube.h | 42 +- include/gamer/OsculatingJets.h | 1578 +- include/gamer/PDBReader.h | 975 +- include/gamer/SurfaceMesh.h | 928 +- include/gamer/TetMesh.h | 483 +- include/gamer/Vertex.h | 490 +- include/gamer/gamer | 54 +- include/gamer/gamer.h | 87 +- include/gamer/stringutil.h | 119 +- include/gamer/tensor.h | 1520 +- include/gamer/version.h | 44 +- libraries/CMakeLists.txt | 42 +- libraries/tetgen/CMakeLists.txt | 5 +- libraries/tetgen/README | 3 +- libraries/tetgen/predicates.cxx | 38 +- libraries/tetgen/tetgen.cxx | 20717 ++++++++++------ libraries/tetgen/tetgen.h | 2163 +- pygamer/CMakeLists.txt | 57 +- pygamer/src/SMEdge.cpp | 43 +- pygamer/src/SMFace.cpp | 43 +- pygamer/src/SMFunctions.cpp | 43 +- pygamer/src/SMGlobal.cpp | 43 +- pygamer/src/SMSimplexID.cpp | 43 +- pygamer/src/SMVertex.cpp | 45 +- pygamer/src/SurfaceMesh.cpp | 51 +- pygamer/src/TMCell.cpp | 43 +- pygamer/src/TMEdge.cpp | 43 +- pygamer/src/TMFace.cpp | 43 +- pygamer/src/TMGlobal.cpp | 43 +- pygamer/src/TMSimplexID.cpp | 43 +- pygamer/src/TMVertex.cpp | 43 +- pygamer/src/TetMesh.cpp | 43 +- pygamer/src/Vector.cpp | 20 +- pygamer/src/pygamer.cpp | 65 +- requirements.txt | 15 +- setup.cfg | 2 +- setup.py | 159 +- src/CMakeLists.txt | 34 + src/CurvatureCalcs.cpp | 343 +- src/OBJ_SurfaceMesh.cpp | 275 +- src/OFF_SurfaceMesh.cpp | 508 +- src/PDBReader.cpp | 484 +- src/SurfaceMesh.cpp | 1898 +- src/SurfaceMeshDetail.cpp | 2051 +- src/TetMesh.cpp | 1609 +- src/Vertex.cpp | 129 +- src/comsol_io.cpp | 277 + src/pdb2mesh.cpp | 3904 ++- tests/CMakeLists.txt | 32 +- tests/SurfaceMeshTest.cpp | 21 +- tests/VertexTest.cpp | 20 + tests/scratchtest.cpp | 6 +- tests/tensorTest.cpp | 21 +- tests/tetrahedralizationTest.cpp | 107 + tools/CMakeLists.txt | 30 +- tools/blendgamer/__init__.py.in | 28 +- tools/blendgamer/src/blendgamer.py | 86 +- tools/blendgamer/src/colormap.py | 222 +- tools/blendgamer/src/colormap_enums.py | 22 +- tools/blendgamer/src/curvatures.py | 261 +- tools/blendgamer/src/markers.py | 149 +- tools/blendgamer/src/meshstats.py | 363 +- tools/blendgamer/src/report.py | 33 +- tools/blendgamer/src/surfacemesh_ops.py | 284 +- tools/blendgamer/src/tetrahedralization.py | 386 +- tools/blendgamer/src/ui.py | 331 +- tools/blendgamer/src/util.py | 248 +- tools/blendgamer/src/versions.py | 234 +- uncrustify.cfg | 2807 --- 104 files changed, 26195 insertions(+), 24109 deletions(-) delete mode 100644 .appveyor.yml create mode 100644 .clang-format create mode 100644 .github/scripts/blender_pyversion_lookup.py create mode 100644 .github/workflows/ci.yaml delete mode 100644 cmake-modules/FetchContent/CMakeLists.cmake.in delete mode 100644 cmake-modules/FetchContentLocal.cmake delete mode 100644 cmake-modules/GetGitRevisionDescription.cmake delete mode 100644 cmake-modules/__init__.py.in rename {cmake-modules => cmake}/FindBlender.cmake (98%) rename {cmake-modules => cmake}/FindBreathe.cmake (64%) rename {cmake-modules => cmake}/FindExhale.cmake (64%) rename {cmake-modules => cmake}/FindSphinx.cmake (100%) rename {cmake-modules => cmake}/Findpytest.cmake (64%) create mode 100644 cmake/GetGitRevisionDescription.cmake rename {cmake-modules => cmake}/GetGitRevisionDescription.cmake.in (100%) create mode 100644 cmake/Macros.cmake rename {cmake-modules => cmake}/VERSION.in (100%) create mode 100644 cmake/__init__.py.in rename {cmake-modules => cmake}/blenderexec.py.in (100%) rename {cmake-modules => cmake}/c_flag_overrides.cmake (100%) rename {cmake-modules => cmake}/cxx_flag_overrides.cmake (100%) rename {cmake-modules => cmake}/version.cpp.in (100%) delete mode 100644 docs/src/conf.py create mode 100644 include/CMakeLists.txt create mode 100644 src/CMakeLists.txt create mode 100644 src/comsol_io.cpp create mode 100644 tests/tetrahedralizationTest.cpp delete mode 100644 uncrustify.cfg diff --git a/.appveyor.yml b/.appveyor.yml deleted file mode 100644 index 161aec57..00000000 --- a/.appveyor.yml +++ /dev/null @@ -1,39 +0,0 @@ -notifications: -- provider: Slack - on_build_status_changed: true - incoming_webhook: - secure: bmfSFbqVVCm0/XYaDqAVMGeBP6RNW1f/yzolevVJ1GXCZZHejnpU1aPG8LIYDskbaudyf1Obaf5ua5WTKRqQJm79EGCYiH6NGPq6qhTyMV0= - -version: 2.0.2.{build} - -branches: - only: - - master - - development - -image: -- Visual Studio 2017 -platform: -- x64 - -environment: - matrix: - - PYTHON: "C:\\Python37-x64" - -matrix: - fast_finish: true - -install: -- SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH% -- python --version -- python -m pip install --upgrade pip wheel -- python -m pip install pytest numpy --no-warn-script-location -- mkdir build_64 -- cd build_64 -- cmake -DGETEIGEN=on -DGAMER_TESTS=on -DBUILD_PYGAMER=on -A x64 .. - -build_script: -- cmake --build . --config Release - -test_script: -- ctest -C Release -V diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..9b3aa8b7 --- /dev/null +++ b/.clang-format @@ -0,0 +1 @@ +BasedOnStyle: LLVM diff --git a/.github/scripts/blender_pyversion_lookup.py b/.github/scripts/blender_pyversion_lookup.py new file mode 100644 index 00000000..2c1a08dd --- /dev/null +++ b/.github/scripts/blender_pyversion_lookup.py @@ -0,0 +1,31 @@ +# *************************************************************************** +# This file is part of the GAMer software. +# Copyright (C) 2016-2021 +# by Christopher T. Lee and contributors +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, see +# or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, +# Boston, MA 02111-1307 USA +# *************************************************************************** + +import sys + +bver_to_pyver = { + "2.79": '3.5', + "2.83": '3.7', + "2.93": '3.9' +} + +if __name__ == "__main__": + print(bver_to_pyver[sys.argv[1]]) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 00000000..9879751c --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,151 @@ +name: Testing and release + +on: + push: + branches: + - master + - development + tags: + # The regex: /^v?((\d+)(\.\d+)*)(-?(a|b|c|rc|alpha|beta)([0-9]+)?)?$ + - 'v[0-9].[0-9]+.[0-9]+*' + pull_request: + branches: + - master + - development + +jobs: + build_test: + name: Setup, build, and test + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + blender: [2.79, 2.83, 2.93] + runs-on: ${{ matrix.os }} + defaults: + run: + shell: bash + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Get target py version + id: python + run: | + pwd + pythonver=$(python ./.github/scripts/blender_pyversion_lookup.py ${{ matrix.blender }}) + echo ::set-output name=version::$pythonver + + - name: Configure python version + uses: actions/setup-python@v2 + with: + python-version: ${{ steps.python.outputs.version }} + + - name: Setup python libs + id: pyexe + run: | + python --version + python -m pip install pytest + py_exe_path=$(which python) + echo ::set-output name=path::$py_exe_path + + - name: Build and test + run: | + mkdir -p build && cd build; + cmake -DCMAKE_BUILD_TYPE=RELEASE -DGETEIGEN=ON -DGETPYBIND11=ON -DGAMER_TESTS=on -DBUILD_BLENDGAMER=ON -DBLENDER_VERSION_OVERRIDE=${{ matrix.blender }} -DBUILD_PYGAMER=on -DPython_EXECUTABLE:FILEPATH=${{ steps.pyexe.outputs.path }} -DCMAKE_OSX_DEPLOYMENT_TARGET=10.12 .. + cmake --build . --config Release -j 4 + ctest -C Release -V -j 4 + + - name: Get blendgamer filename + id: pkg + run: | + pwd + cd build + zipfile=$(ls *.zip) + echo ::set-output name=filename::$zipfile + + - name: Stash blendgamer + # Generate artifacts for all tagged versions + # if: startsWith(github.ref, 'refs/tags/') + uses: actions/upload-artifact@v2 + with: + name: ${{ steps.pkg.outputs.filename }} + path: build/*.zip + + stage_releases: + name: Stage GitHub Release + runs-on: ubuntu-latest + needs: [build_test] + # Stage release for tagged versions + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') + steps: + - name: Sync artifacts + uses: actions/download-artifact@v2 + with: + path: artifacts + + - name: Display structure of files + run: ls -R + + - name: Collect zipfiles + run: | + mv artifacts/blendgamer*.zip/blendgamer*.zip . + + - name: Display structure of downloaded files + run: ls -R + + - name: Generate SHA256 checksums + run: sha256sum *.zip > SHA256SUMS.txt + + - name: Verify checksums + run: sha256sum -c SHA256SUMS.txt + + - name: Create Release + id: create_release + uses: softprops/action-gh-release@v1 + with: + tag_name: ${{ github.ref }} + body: If you can read this, we have forgotten to fill in the changelog. Sorry! + draft: true + files: | + ./*.zip + ./SHA256SUMS.txt + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + deploy_to_pypi: + name: Deploy to PyPI + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') + runs-on: ubuntu-latest + needs: [build_test] + defaults: + run: + shell: bash + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Update python libs + run: python -m pip install scikit-build pytest + + - name: Configures source dist + run: python setup.py sdist + + - name: Display structure of files + run: ls -R + + - name: Publish package + uses: pypa/gh-action-pypi-publish@release/v1 + with: + user: __token__ + password: ${{ secrets.PYPI_API_TOKEN }} + + # - name: Publish package to TestPyPI + # uses: pypa/gh-action-pypi-publish@release/v1 + # with: + # user: __token__ + # password: ${{ secrets.TEST_PYPI_API_TOKEN }} + # repository_url: https://test.pypi.org/legacy/ diff --git a/.gitignore b/.gitignore index 1b456d43..f8299887 100644 --- a/.gitignore +++ b/.gitignore @@ -70,3 +70,6 @@ docs/src/_cppapi/ docs/src/_pythonapi/ docs/src/tutorials/ docs/src/conf.py + +.vscode +.pytest_cache diff --git a/.readthedocs.yml b/.readthedocs.yml index faf90e2e..1771183f 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -15,9 +15,8 @@ sphinx: # Optionally set the version of Python and requirements required to build your docs python: - version: 3.7 + version: 3 install: - requirements: docs/rtd-requirements.txt - method: setuptools path: . - diff --git a/.travis.yml b/.travis.yml index 1652c44c..2f2065f0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,134 +1,274 @@ +arch: amd64 +os: linux +dist: focal + notifications: email: false - slack: - secure: ilztqDylzgDwnh+AAHdrmHjgD4rjutJa5rh783H8HfW7KKY+RtHaAa+Z9V6uAbU3/fPk29fkCXRxT/scAI4Fn0TQ/Hi0alCs7ytNJg+2fbVcI5xWHbmJznOb0pQl0Gn6MGwouSyP9WvRtivtT5u+wlNR6yKycTmUI9qP3Fe7nA/k/WdRt5SHGjjprtYZYbBOg+9UStzubMuQj5hTBeDs78UyUbejSsZ0Cp9+OkTcugoEegwn0o9QdkvFGHF/hQ5bVGGNVAFUVOg+B4DFiw59MbsgIDZ663CXCTxUJ1I4uS3rI7LpnY2G8c5dKp9E2KDkM8sPKWzg4qeyj1POMPNDUbNYWIHBBK4NxrQ+PqW1nquxNTVsqYi2IXVmV2BWx9+SUS06YwDsjrCcQyAtnCzxn/tL3aX/8jyeN3yPwytJgDuj9IF8nIJZuuMgDOs1ZVa9fbL8ANBMcZMKIPCSwjO8670ULl5xKAWKGTY7HwzGLU3syQBwOX+eukf9zx2Y9qrsP1kJJT8YTOD45icJRAMPQQEiN20wxKh/V7Kz0ewc6LG8Bv1FyM6f+7ehNZvkwrStzJJ85JwDkPeTKKiBa3kuBQzE0CBm3/cTYCXgQEo3jggH1xPF8OkbYhuX8TrZjp3PBVCABto6V6OGILFA7Hhv6gK9eHxCSVOICPfWCfYXakY= + # slack: + # secure: ilztqDylzgDwnh+AAHdrmHjgD4rjutJa5rh783H8HfW7KKY+RtHaAa+Z9V6uAbU3/fPk29fkCXRxT/scAI4Fn0TQ/Hi0alCs7ytNJg+2fbVcI5xWHbmJznOb0pQl0Gn6MGwouSyP9WvRtivtT5u+wlNR6yKycTmUI9qP3Fe7nA/k/WdRt5SHGjjprtYZYbBOg+9UStzubMuQj5hTBeDs78UyUbejSsZ0Cp9+OkTcugoEegwn0o9QdkvFGHF/hQ5bVGGNVAFUVOg+B4DFiw59MbsgIDZ663CXCTxUJ1I4uS3rI7LpnY2G8c5dKp9E2KDkM8sPKWzg4qeyj1POMPNDUbNYWIHBBK4NxrQ+PqW1nquxNTVsqYi2IXVmV2BWx9+SUS06YwDsjrCcQyAtnCzxn/tL3aX/8jyeN3yPwytJgDuj9IF8nIJZuuMgDOs1ZVa9fbL8ANBMcZMKIPCSwjO8670ULl5xKAWKGTY7HwzGLU3syQBwOX+eukf9zx2Y9qrsP1kJJT8YTOD45icJRAMPQQEiN20wxKh/V7Kz0ewc6LG8Bv1FyM6f+7ehNZvkwrStzJJ85JwDkPeTKKiBa3kuBQzE0CBm3/cTYCXgQEo3jggH1xPF8OkbYhuX8TrZjp3PBVCABto6V6OGILFA7Hhv6gK9eHxCSVOICPfWCfYXakY= language: cpp -dist: xenial -sudo: false - # Don't shallow clone as this may break the versioning git: depth: false - branches: only: - master - development + - /^v?((\d+)(\.\d+)*)(-?(a|b|c|rc|alpha|beta)([0-9]+)?)?$/ # BUILD MATRIX -matrix: +jobs: fast_finish: true - allow_failures: - # These xcode versions have a llvm defect - - os: osx - osx_image: xcode9 - - os: osx - osx_image: xcode9.1 - - os: osx - osx_image: xcode9.2 - include: - # OSX - - os: osx - osx_image: xcode8.3 - - - os: osx - osx_image: xcode9 - - - os: osx - osx_image: xcode9.1 + - stage: test + os: windows + before_install: + - choco install python3 --version 3.7.4 --params "/InstallDir:C:\\Python" + - export PATH="/c/Python:/c/Python/Scripts:$PATH" + - python -m pip install --upgrade pip - - os: osx - osx_image: xcode9.2 + - stage: test + os: osx + osx_image: xcode12.2 + before_install: + - wget https://repo.anaconda.com/miniconda/Miniconda3-latest-MacOSX-x86_64.sh -O miniconda.sh + - chmod +x miniconda.sh + - ./miniconda.sh -b + - export PATH=/Users/travis/miniconda3/bin:$PATH + - conda update --yes conda + - conda create --yes -n env_name python=3.7.4 pip numpy + - source activate env_name - - os: osx - osx_image: xcode9.3 - - - os: osx - osx_image: xcode9.4 - - - os: osx - osx_image: xcode10 + - stage: test + os: linux + dist: focal + compiler: gcc - - os: osx - osx_image: xcode10.1 + ################# + # DEPLOYMENT + ################# + # windows blender2.79 + - stage: deploy + if: branch =~ ^v?((\d+)(\.\d+)*)(-?(a|b|c|rc|alpha|beta)([0-9]+)?)?$ + os: windows + before_install: + - choco install python3 --version 3.5.4 --params "/InstallDir:C:\\Python" + - export PATH="/c/Python:/c/Python/Scripts:$PATH" + - python -m pip install --upgrade pip + # - python -m pip install numpy + install: skip + script: + - mkdir -p build && cd build; + - cmake -DCMAKE_BUILD_TYPE=RELEASE -DGETEIGEN=ON -DGETPYBIND11=ON -DGAMER_TESTS=on -DBUILD_PYGAMER=on -DBUILD_BLENDGAMER=ON -DBLENDER_VERSION_OVERRIDE=2.79 -G "Visual Studio 15 2017 Win64" .. + - cmake --build . --config Release + deploy: + provider: releases + token: $GITHUB_TOKEN + draft: true + file_glob: true + file: "*.zip" + skip_cleanup: true + overwrite: true + on: + tags: true + repo: ctlee/gamer - - os: osx - osx_image: xcode10.2 + # windows blender2.83 + - stage: deploy + if: branch =~ ^v?((\d+)(\.\d+)*)(-?(a|b|c|rc|alpha|beta)([0-9]+)?)?$ + os: windows + before_install: + - choco install python3 --version 3.7.4 --params "/InstallDir:C:\\Python" + - export PATH="/c/Python:/c/Python/Scripts:$PATH" + - python -m pip install --upgrade pip + # - python -m pip install numpy + install: skip + script: + - mkdir -p build && cd build; + - cmake -DCMAKE_BUILD_TYPE=RELEASE -DGETEIGEN=ON -DGETPYBIND11=ON -DGAMER_TESTS=on -DBUILD_PYGAMER=on -DBUILD_BLENDGAMER=ON -DBLENDER_VERSION_OVERRIDE=2.83 -G "Visual Studio 15 2017 Win64" .. + - cmake --build . --config Release + deploy: + provider: releases + token: $GITHUB_TOKEN + draft: true + file_glob: true + file: "*.zip" + skip_cleanup: true + overwrite: true + on: + tags: true + repo: ctlee/gamer - # LINUX GCC - - os: linux + # linux blender2.79 + - stage: deploy + if: branch =~ ^v?((\d+)(\.\d+)*)(-?(a|b|c|rc|alpha|beta)([0-9]+)?)?$ + os: linux + dist: focal compiler: gcc - env: MATRIX_EVAL="CC=gcc-5 && CXX=g++-5" - addons: - apt: - sources: ['ubuntu-toolchain-r-test'] - packages: ['g++-5', 'ninja-build'] + before_install: + - wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh + - chmod +x miniconda.sh + - ./miniconda.sh -b + - export PATH=/home/travis/miniconda3/bin:$PATH + - conda update --yes conda + - conda create --yes -n env_name python=3.5.4 pip numpy + - source activate env_name + # The next couple lines fix a crash with multiprocessing on Travis and are not specific to using Miniconda + # - sudo rm -rf /dev/shm + # - sudo ln -s /run/shm /dev/shm + install: skip + script: + - mkdir -p build && cd build; + - cmake -DCMAKE_BUILD_TYPE=RELEASE -DGETEIGEN=ON -DGETPYBIND11=ON -DBUILD_PYGAMER=on -DBUILD_BLENDGAMER=ON -DBLENDER_VERSION_OVERRIDE=2.79 .. + - cmake --build . --config Release -j 2 + deploy: + provider: releases + token: $GITHUB_TOKEN + draft: true + file_glob: true + file: "*.zip" + skip_cleanup: true + overwrite: true + on: + tags: true + repo: ctlee/gamer - - os: linux + # linux blender2.83 + - stage: deploy + if: branch =~ ^v?((\d+)(\.\d+)*)(-?(a|b|c|rc|alpha|beta)([0-9]+)?)?$ + os: linux + dist: focal compiler: gcc - env: MATRIX_EVAL="CC=gcc-6 && CXX=g++-6" - addons: - apt: - sources: ['ubuntu-toolchain-r-test'] - packages: ['g++-6', 'ninja-build'] + before_install: + - wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh + - chmod +x miniconda.sh + - ./miniconda.sh -b + - export PATH=/home/travis/miniconda3/bin:$PATH + - conda update --yes conda + - conda create --yes -n env_name python=3.7.4 pip numpy + - source activate env_name + # The next couple lines fix a crash with multiprocessing on Travis and are not specific to using Miniconda + # - sudo rm -rf /dev/shm + # - sudo ln -s /run/shm /dev/shm + install: skip + script: + - mkdir -p build && cd build; + - cmake -DCMAKE_BUILD_TYPE=RELEASE -DGETEIGEN=ON -DGETPYBIND11=ON -DBUILD_PYGAMER=on -DBUILD_BLENDGAMER=ON -DBLENDER_VERSION_OVERRIDE=2.83 .. + - cmake --build . --config Release -j 2 + deploy: + provider: releases + token: $GITHUB_TOKEN + draft: true + file_glob: true + file: "*.zip" + skip_cleanup: true + overwrite: true + on: + tags: true + repo: ctlee/gamer - - os: linux - compiler: gcc - env: MATRIX_EVAL="CC=gcc-7 && CXX=g++-7" - addons: - apt: - sources: ['ubuntu-toolchain-r-test'] - packages: ['g++-7', 'ninja-build'] + # osx blender2.79 + - stage: deploy + if: branch =~ ^v?((\d+)(\.\d+)*)(-?(a|b|c|rc|alpha|beta)([0-9]+)?)?$ + os: osx + osx_image: xcode12.2 + before_install: + - wget https://repo.anaconda.com/miniconda/Miniconda3-latest-MacOSX-x86_64.sh -O miniconda.sh + - chmod +x miniconda.sh + - ./miniconda.sh -b + - export PATH=/Users/travis/miniconda3/bin:$PATH + - conda update --yes conda + - conda create --yes -n env_name python=3.5.4 pip numpy + - source activate env_name + install: skip + script: + - mkdir -p build && cd build; + - cmake -DCMAKE_BUILD_TYPE=RELEASE -DGETEIGEN=ON -DGETPYBIND11=ON -DBUILD_PYGAMER=on -DBUILD_BLENDGAMER=ON -DCMAKE_OSX_DEPLOYMENT_TARGET=10.12 -DBLENDER_VERSION_OVERRIDE=2.79 .. + - cmake --build . --config Release -j 2 + deploy: + provider: releases + token: $GITHUB_TOKEN + draft: true + file_glob: true + file: "*.zip" + skip_cleanup: true + overwrite: true + on: + tags: true + repo: ctlee/gamer + + # osx blender2.83 + - stage: deploy + if: branch =~ ^v?((\d+)(\.\d+)*)(-?(a|b|c|rc|alpha|beta)([0-9]+)?)?$ + os: osx + osx_image: xcode12.2 + before_install: + - wget https://repo.anaconda.com/miniconda/Miniconda3-latest-MacOSX-x86_64.sh -O miniconda.sh + - chmod +x miniconda.sh + - ./miniconda.sh -b + - export PATH=/Users/travis/miniconda3/bin:$PATH + - conda update --yes conda + - conda create --yes -n env_name python=3.7.4 pip numpy + - source activate env_name + install: skip + script: + - mkdir -p build && cd build; + - cmake -DCMAKE_BUILD_TYPE=RELEASE -DGETEIGEN=ON -DGETPYBIND11=ON -DBUILD_PYGAMER=on -DBUILD_BLENDGAMER=ON -DCMAKE_OSX_DEPLOYMENT_TARGET=10.12 -DBLENDER_VERSION_OVERRIDE=2.83 .. + - cmake --build . --config Release -j 2 + deploy: + provider: releases + token: $GITHUB_TOKEN + draft: true + file_glob: true + file: "*.zip" + skip_cleanup: true + overwrite: true + on: + tags: true + repo: ctlee/gamer - - os: linux + # Deploy PyPi + - stage: deploy + if: branch =~ ^v?((\d+)(\.\d+)*)(-?(a|b|c|rc|alpha|beta)([0-9]+)?)?$ + os: linux + dist: focal compiler: gcc - env: MATRIX_EVAL="CC=gcc-8 && CXX=g++-8" - addons: - apt: - sources: ['ubuntu-toolchain-r-test'] - packages: ['g++-8', 'ninja-build'] - - # LINUX CLANG - - os: linux - compiler: clang - env: MATRIX_EVAL="CC=clang-7 && CXX=clang++-7" - addons: - apt: - sources: ['llvm-toolchain-xenial-7'] - packages: ['clang-7', 'ninja-build'] - - - os: linux - compiler: clang - env: MATRIX_EVAL="CC=clang-8 && CXX=clang++-8" - addons: - apt: - sources: ['llvm-toolchain-xenial-8'] - packages: ['clang-8', 'ninja-build'] + cache: pip + before_install: skip + install: skip + script: + - python -m pip install scikit-build pytest + - python setup.py sdist; + deploy: + provider: pypi + username: "__token__" + password: + secure: "QDT685JvVcZygQZY7DPc9Yqncn8haVwXddLBYC4jcLgz18pOiJHKXGnF2w+A5UgjjRnF8phNyHmxDd3zYt4/QfZLCGLbxkLEagcUa0ZWaBudOf8kQ7alQI2IPJl4vrU9xpH9U9sUtVMhcPyvfMtvxRnKVrPSX4Sl5JXKFLQJ+hdbu7qVXmpV7zBqBPkC4+bQP1T8RJcm6kKTfd7uE1KAYdb6xD+K5dxXVW4jbOQxsLSqUINLbcTWheq//xB6NmKei49rvaV9/q7MgCvbfFGWRlHwaRn3a6v8GhANfcPKTCCKKDU9BEvn96scGkrCzVaHNqzCXLEPx0qcaZIxEPhDzKk8VQYNYnXp4REpOpJ71EAkaZDSxUKUeYGo1XmNimf9PoLZqUoZ429DJG0eJs3vkKWN4G+X7MZh7eGst1nN8eK0l0QPO5znNPe3nbvTW7V3E5JvrdiGD8sQzEEW7810Elaybznhi2hGHeB/ow7QceUC05a23ZsKlw2i/YeSGkdKvNo7YFqX4pMHuB7KnkFEU5SS1Fu8NIrVfkK9GDcEYpCsDvyz6+RnN6vXiKcUsb7vHnYIQLJll5fYpkl09H7jccf74oNsP4s6rj2FhNl0XLnE8VBRYq5ufEF4hNcPqfdOuv18KljjARba2JI3UzfYscciJR8lcyOKhcQMfPBbaK0=" + skip_cleanup: true + skip_existing: true + distributions: "sdist" + on: + tags: true + repo: ctlee/gamer before_install: -- | - if [[ (-x $(which brew)) ]]; then - brew update - brew install cmake ninja - brew upgrade cmake - cmake --version - fi -- if [[ "${MATRIX_EVAL}" != "" ]]; then eval ${MATRIX_EVAL}; fi -- if [ "$TRAVIS_OS_NAME" = "linux" ]; then pyenv global 3.7.1; fi - -# Show OS/compiler version -- uname -a -- $CXX --version + - uname -a + - $CXX --version + - cmake --version install: -- mkdir build; cd build; -- cmake -DGETEIGEN=on -DGAMER_TESTS=on -DBUILD_PYGAMER=on -GNinja .. -- cmake --build . --config Release + - mkdir -p build && cd build; + - | + if [ "$TRAVIS_OS_NAME" = "windows" ]; then + cmake -DCMAKE_BUILD_TYPE=RELEASE -DGETEIGEN=ON -DGETPYBIND11=ON -DGAMER_TESTS=on -DBUILD_PYGAMER=on -G "Visual Studio 15 2017 Win64" .. + else + cmake -DCMAKE_BUILD_TYPE=RELEASE -DGETEIGEN=ON -DGETPYBIND11=ON -DGAMER_TESTS=on -DBUILD_PYGAMER=on .. + fi + - cmake --build . --config Release -j 2 script: -- ctest -C Release -V -j \ No newline at end of file + - ctest -C Release -V -j diff --git a/CMakeLists.txt b/CMakeLists.txt index e5d7ed54..b548c81d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,7 @@ # *************************************************************************** # This file is part of the GAMer software. -# Copyright (C) 2016-2018 -# by Christopher Lee, John Moody, Rommie Amaro, J. Andrew McCammon, -# and Michael Holst +# Copyright (C) 2016-2021 +# by Christopher T. Lee and contributors # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -15,81 +14,37 @@ # Lesser General Public License for more details. # You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# License along with this library; if not, see +# or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, +# Boston, MA 02111-1307 USA # *************************************************************************** # 3.12 required for FetchContent and objectlib INTERFACE include inheritance -cmake_minimum_required(VERSION 3.10) +cmake_minimum_required(VERSION 3.12...3.18) -# Set default buildtype -set(CMAKE_BUILD_TYPE_INIT Release) -# Disable in source builds -set(CMAKE_DISABLE_IN_SOURCE_BUILD ON) # Add path to custom modules -list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake-modules") +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") if(SKBUILD) message(STATUS "The project is being built using scikit-build") + # Currently, Scikit-build does not support FindPython, so we convert the + # provided hints ourselves. + set(Python_EXECUTABLE "${PYTHON_EXECUTABLE}") + set(Python_INCLUDE_DIR "${PYTHON_INCLUDE_DIR}") + set(Python_LIBRARY "${PYTHON_LIBRARY}") + set(Python_VERSION "${PYTHON_VERSION_STRING}") endif() -# Look for version from GIT -include(GetGitRevisionDescription) -git_describe(VERSION --tags --dirty=.dirty --always) - -#parse the version information into pieces. -string(REGEX MATCH "^v([0-9]+)\\.([0-9]+)\\.([0-9]+)-*(alpha|beta|dev|)-*([A-Za-z0-9_-]*)\\.*(dirty|)$" MATCH_RESULT "${VERSION}") - -if(MATCH_RESULT) - # message(STATUS "Results: ${CMAKE_MATCH_0} ${CMAKE_MATCH_1} ${CMAKE_MATCH_2} ${CMAKE_MATCH_3} ${CMAKE_MATCH_4} ${CMAKE_MATCH_5} ${CMAKE_MATCH_6}") - set(VERSION_MAJOR ${CMAKE_MATCH_1}) - set(VERSION_MINOR ${CMAKE_MATCH_2}) - set(VERSION_PATCH ${CMAKE_MATCH_3}) - set(VERSION_INFO ${CMAKE_MATCH_4}) - set(VERSION_SHA1 ${CMAKE_MATCH_5}) - set(VERSION_DIRTY ${CMAKE_MATCH_6}) - - set(VERSION_DUMP "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}") - if(VERSION_INFO) - string(CONCAT VERSION_DUMP ${VERSION_DUMP} "-${VERSION_INFO}") - endif() - configure_file( - ${CMAKE_CURRENT_SOURCE_DIR}/cmake-modules/VERSION.in - ${CMAKE_CURRENT_SOURCE_DIR}/VERSION) -else() - message(STATUS "No GIT VCS found pulling version from file") - file(READ ${CMAKE_CURRENT_SOURCE_DIR}/VERSION VERSION) - string(STRIP "${VERSION}" VERSION) - string(REGEX MATCH "^([0-9]+)\\.([0-9]+)\\.([0-9]+)-*(alpha|beta|dev|)$" MATCH_RESULT "${VERSION}") - if(MATCH_RESULT) - set(VERSION_MAJOR ${CMAKE_MATCH_1}) - set(VERSION_MINOR ${CMAKE_MATCH_2}) - set(VERSION_PATCH ${CMAKE_MATCH_3}) - set(VERSION_INFO ${CMAKE_MATCH_4}) - else() - # default values - set(VERSION_MAJOR "0") - set(VERSION_MINOR "0") - set(VERSION_PATCH "0") - endif() -endif() - -set(VERSION_SHORT "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}") +include(Macros) +set_default_build_type() +get_version_from_git() ##################################################################### # Project GAMer ##################################################################### project(GAMer VERSION ${VERSION_SHORT}) - message(STATUS "GAMer version: ${VERSION}") -# Configure version.cpp to give access to version in code -configure_file( - ${CMAKE_CURRENT_SOURCE_DIR}/cmake-modules/version.cpp.in - ${CMAKE_CURRENT_BINARY_DIR}/version.cpp) -set(version_file "${CMAKE_CURRENT_BINARY_DIR}/version.cpp") -list(APPEND GAMER_SOURCES "${version_file}") - ##################################################################### # Options ##################################################################### @@ -108,7 +63,9 @@ option(GAMER_DOCS "Download and configure documentation?" OFF) option(VECTORIZE "Enable vectorization?" OFF) -option(GAMER_CMAKE_VERBOSE "Print out information for debugging CMake configuration?") +option(BLENDER_VERSION_OVERRIDE "Override the version number" "") +mark_as_advanced(BLENDER_VERSION_OVERRIDE) +option(GAMER_CMAKE_VERBOSE "Print out information for debugging CMake configuration?" OFF) mark_as_advanced(GAMER_CMAKE_VERBOSE) option(FORCE_COLORED_OUTPUT "Always produce ANSI-colored output (GNU/Clang only)." OFF) @@ -133,8 +90,8 @@ set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib) set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) # Override rules for MSVC static compiler flags -set(CMAKE_USER_MAKE_RULES_OVERRIDE ${CMAKE_CURRENT_LIST_DIR}/cmake-modules/c_flag_overrides.cmake) -set(CMAKE_USER_MAKE_RULES_OVERRIDE_CXX ${CMAKE_CURRENT_LIST_DIR}/cmake-modules/cxx_flag_overrides.cmake) +set(CMAKE_USER_MAKE_RULES_OVERRIDE ${CMAKE_CURRENT_LIST_DIR}/cmake/c_flag_overrides.cmake) +set(CMAKE_USER_MAKE_RULES_OVERRIDE_CXX ${CMAKE_CURRENT_LIST_DIR}/cmake/cxx_flag_overrides.cmake) if(VECTORIZE) message(WARNING "Vectorization in GAMer is experimental, use at your own risk. Enabling vectorization may make your libraries less portable across machines.") @@ -147,6 +104,11 @@ endif() # set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pipe") # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pipe") +set(CMAKE_POLICY_DEFAULT_CMP0063 NEW) +cmake_policy(SET CMP0063 NEW) +set(CMAKE_CXX_VISIBILITY_PRESET hidden) +set(CMAKE_VISIBILITY_INLINES_HIDDEN ON) + # # Add -fPIC to all targets set(CMAKE_POSITION_INDEPENDENT_CODE ON) @@ -162,76 +124,48 @@ endif() ## Print compile flags if(GAMER_CMAKE_VERBOSE) - message(DEBUG "CMAKE_C_FLAGS is: ${CMAKE_C_FLAGS}") - message(DEBUG "CMAKE_C_FLAGS_DEBUG is: ${CMAKE_C_FLAGS_DEBUG}") - message(DEBUG "CMAKE_C_FLAGS_RELEASE is: ${CMAKE_C_FLAGS_RELEASE}") - message(DEBUG "CMAKE_C_FLAGS_RELWITHDEBINFO is: ${CMAKE_C_FLAGS_RELWITHDEBINFO}") - message(DEBUG "CMAKE_C_FLAGS_MINSIZEREL is: ${CMAKE_C_FLAGS_MINSIZEREL}") - message(DEBUG "CMAKE_CXX_FLAGS is: ${CMAKE_CXX_FLAGS}") - message(DEBUG "CMAKE_CXX_FLAGS_DEBUG is: ${CMAKE_CXX_FLAGS_DEBUG}") - message(DEBUG "CMAKE_CXX_FLAGS_RELEASE is: ${CMAKE_CXX_FLAGS_RELEASE}") - message(DEBUG "CMAKE_CXX_FLAGS_RELWITHDEBINFO is: ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") - message(DEBUG "CMAKE_CXX_FLAGS_MINSIZEREL is: ${CMAKE_CXX_FLAGS_MINSIZEREL}") - message(DEBUG "Build type: ${CMAKE_BUILD_TYPE}") - message(DEBUG "CMAKE_VERBOSE_MAKEFILE: " ${CMAKE_VERBOSE_MAKEFILE}) + print_debug_messages() endif(GAMER_CMAKE_VERBOSE) +if(BUILD_PYGAMER) + find_package(Python COMPONENTS Interpreter Development REQUIRED) +endif() + # Add and configure library dependencies add_subdirectory(libraries EXCLUDE_FROM_ALL) - -list(APPEND GAMER_SOURCES - "src/OFF_SurfaceMesh.cpp" - "src/OBJ_SurfaceMesh.cpp" - "src/SurfaceMesh.cpp" - "src/SurfaceMeshDetail.cpp" - "src/CurvatureCalcs.cpp" - "src/Vertex.cpp" - "src/TetMesh.cpp" - "src/PDBReader.cpp" - "src/pdb2mesh.cpp" -) +add_subdirectory(include) +# Src must be traversed prior to appending to GAMER_SOURCES +add_subdirectory(src) +# Configure version.cpp to give access to version in code +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/cmake/version.cpp.in + ${CMAKE_CURRENT_BINARY_DIR}/version.cpp) +list(APPEND GAMER_SOURCES "${CMAKE_CURRENT_BINARY_DIR}/version.cpp") ##################################################################### # LIBRARIES ##################################################################### -if(${CMAKE_VERSION} VERSION_GREATER_EQUAL 3.12) - # OBJECT LIBRARY: compiles the sources only once - add_library(gamer_objlib OBJECT ${GAMER_SOURCES}) - target_include_directories(gamer_objlib PUBLIC - $ - $ - ) - target_link_libraries(gamer_objlib PUBLIC casc tetstatic eigen) - - # SHARED LIBRARY - add_library(gamershared SHARED $) - target_link_libraries(gamershared PUBLIC gamer_objlib) - # set_target_properties(gamershared PROPERTIES OUTPUT_NAME gamer) - - # STATIC LIBRARY - add_library(gamerstatic STATIC $) - target_link_libraries(gamerstatic PUBLIC gamer_objlib) - # if(NOT WIN32) - # # Shared and static libs will clobber each other on Windows - # set_target_properties(gamerstatic PROPERTIES OUTPUT_NAME gamer) - # endif() -else() # CMAKE_VERSION < 3.12 - # SHARED LIBRARY - add_library(gamershared SHARED ${GAMER_SOURCES}) - target_include_directories(gamershared PUBLIC - $ - $ - ) - target_link_libraries(gamershared PUBLIC casc tetstatic eigen) - - # STATIC LIBRARY - add_library(gamerstatic STATIC ${GAMER_SOURCES}) - target_include_directories(gamerstatic PUBLIC +# OBJECT LIBRARY: compiles the sources only once +add_library(gamer_objlib OBJECT ${GAMER_INCLUDES} ${GAMER_SOURCES}) +target_include_directories(gamer_objlib PUBLIC $ $ ) - target_link_libraries(gamerstatic PUBLIC casc tetstatic eigen) -endif() +target_link_libraries(gamer_objlib PUBLIC casc tetstatic Eigen3::Eigen) + +# SHARED LIBRARY +add_library(gamershared SHARED $) +target_link_libraries(gamershared PUBLIC gamer_objlib) +target_compile_definitions(gamershared PRIVATE GAMER_DLL) +# set_target_properties(gamershared PROPERTIES OUTPUT_NAME gamer) + +# STATIC LIBRARY +add_library(gamerstatic STATIC $) +target_link_libraries(gamerstatic PUBLIC gamer_objlib) +# if(NOT WIN32) +# # Shared and static libs will clobber each other on Windows +# set_target_properties(gamerstatic PROPERTIES OUTPUT_NAME gamer) +# endif() # Alias library names set_target_properties(gamershared PROPERTIES OUTPUT_NAME gamer) diff --git a/MANIFEST.in b/MANIFEST.in index ee0dc490..9fc3b455 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,29 +1,33 @@ include VERSION +include COPYING.md include README.md include pyproject.toml # CMake Helpers -include cmake-modules/* +graft cmake include CMakeLists.txt # C++ sources -include include/gamer/* -include src/* -recursive-include libraries * +graft include +graft src +graft libraries # PyGAMer sources -recursive-include pygamer * +graft pygamer # Unit Tests -include tests/* +graft tests # Blender addon -recursive-include tools * +graft tools # Documentation -recursive-include docs * +graft docs prune docs/src/_cppapi prune docs/src/_doxyoutput prune docs/src/_pythonapi prune docs/src/tutorials -exclude docs/src/conf.py \ No newline at end of file +exclude docs/src/conf.py + +# Exclusions +global-exclude *.py[co] diff --git a/README.md b/README.md index 166c9695..83a054c0 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,7 @@ # Geometry-preserving Adaptive MeshER [![DOI](https://zenodo.org/badge/122682242.svg)](https://zenodo.org/badge/latestdoi/122682242) [![PyPI](https://img.shields.io/pypi/v/pygamer)](https://pypi.org/project/pygamer/) -[![Master Build Status](https://travis-ci.org/ctlee/gamer.svg?branch=master)](https://travis-ci.org/ctlee/gamer) -[![Build status](https://ci.appveyor.com/api/projects/status/urffu7062fnohidl/branch/master?svg=true)](https://ci.appveyor.com/project/ctlee/gamer) +[![Master Build Status](https://github.com/ctlee/gamer/actions/workflows/ci.yaml/badge.svg?branch=master)](https://github.com/ctlee/gamer/actions/workflows/ci.yaml) [![Documentation Status](https://readthedocs.org/projects/gamer/badge/?version=latest)](https://gamer.readthedocs.io/en/latest/?badge=latest) GAMer is a surface mesh improvement library developed to condition surface meshes derived from noisy biological imaging data. @@ -25,8 +24,12 @@ GAMer has the following main features: ## Acknowledging your use of GAMer Thanks for using GAMer! The developers would love to hear how you are using the tool. Please send us an email or post on GitHub letting us know. -Please cite the above Zenodo DOI to acknowledge the software version and cite the following paper:
-[Lee, C. T.; Laughlin, J. G.; Angliviel de La Beaumelle, N.; Amaro, R.; McCammon, J. A.; Ramamoorthi, R.; Holst, M. J.; Rangamani, P. GAMer 2: A System for 3D Mesh Processing of Cellular Electron Micrographs. bioRxiv 2019, 534479.](https://www.biorxiv.org/content/10.1101/534479v1) +Please cite the above Zenodo DOI to acknowledge the software version and cite the following papers:
+[Lee, C. T.; Laughlin, J. G.; Moody, J. B.; Amaro, R. E.; McCammon, J. A.; Holst, M.; Rangamani, P. An Open-Source Mesh Generation Platform for Biophysical Modeling Using Realistic Cellular Geometries. Biophysical Journal 2020, 118 (5), 1003–1008.](https://doi.org/10.1016/j.bpj.2019.11.3400) + +[Lee, C. T.; Laughlin, J. G.; Beaumelle, N. A. de L.; Amaro, R. E.; McCammon, J. A.; Ramamoorthi, R.; Holst, M.; Rangamani, P. 3D Mesh Processing Using GAMer 2 to Enable Reaction-Diffusion Simulations in Realistic Cellular Geometries. PLOS Computational Biology 2020, 16 (4), e1007756.](https://doi.org/10.1371/journal.pcbi.1007756) + + ## Installation The following instructions are to build the base GAMer library. @@ -77,6 +80,5 @@ complex data structure. ## Development Build Status -[![Development Build Status](https://travis-ci.org/ctlee/gamer.svg?branch=development)](https://travis-ci.org/ctlee/gamer) -[![Build status](https://ci.appveyor.com/api/projects/status/urffu7062fnohidl/branch/development?svg=true)](https://ci.appveyor.com/project/ctlee/gamer/branch/development) +[![Development Build Status](https://github.com/ctlee/gamer/actions/workflows/ci.yaml/badge.svg?branch=development)](https://github.com/ctlee/gamer/actions/workflows/ci.yaml) [![Documentation Status](https://readthedocs.org/projects/gamer/badge/?version=development)](https://gamer.readthedocs.io/en/development?badge=development) diff --git a/VERSION b/VERSION index 157e54f3..47af7abd 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.0.6 +2.0.7-alpha diff --git a/cmake-modules/FetchContent/CMakeLists.cmake.in b/cmake-modules/FetchContent/CMakeLists.cmake.in deleted file mode 100644 index 3f7e22f0..00000000 --- a/cmake-modules/FetchContent/CMakeLists.cmake.in +++ /dev/null @@ -1,23 +0,0 @@ -# Distributed under the OSI-approved BSD 3-Clause License. See accompanying -# file Copyright.txt or https://cmake.org/licensing for details. - -cmake_minimum_required(VERSION ${CMAKE_VERSION}) - -# We name the project and the target for the ExternalProject_Add() call -# to something that will highlight to the user what we are working on if -# something goes wrong and an error message is produced. - -project(${contentName}-populate NONE) - -include(ExternalProject) -ExternalProject_Add(${contentName}-populate - ${ARG_EXTRA} - SOURCE_DIR "${ARG_SOURCE_DIR}" - BINARY_DIR "${ARG_BINARY_DIR}" - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" - TEST_COMMAND "" - USES_TERMINAL_DOWNLOAD YES - USES_TERMINAL_UPDATE YES -) \ No newline at end of file diff --git a/cmake-modules/FetchContentLocal.cmake b/cmake-modules/FetchContentLocal.cmake deleted file mode 100644 index 9c7eaf88..00000000 --- a/cmake-modules/FetchContentLocal.cmake +++ /dev/null @@ -1,1061 +0,0 @@ -# Distributed under the OSI-approved BSD 3-Clause License. See accompanying -# file Copyright.txt or https://cmake.org/licensing for details. - -#[=======================================================================[.rst: -FetchContent ------------------- - -.. only:: html - - .. contents:: - -Overview -^^^^^^^^ - -This module enables populating content at configure time via any method -supported by the :module:`ExternalProject` module. Whereas -:command:`ExternalProject_Add` downloads at build time, the -``FetchContent`` module makes content available immediately, allowing the -configure step to use the content in commands like :command:`add_subdirectory`, -:command:`include` or :command:`file` operations. - -Content population details would normally be defined separately from the -command that performs the actual population. This separation ensures that -all of the dependency details are defined before anything may try to use those -details to populate content. This is particularly important in more complex -project hierarchies where dependencies may be shared between multiple projects. - -The following shows a typical example of declaring content details: - -.. code-block:: cmake - - FetchContent_Declare( - googletest - GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG release-1.8.0 - ) - -For most typical cases, populating the content can then be done with a single -command like so: - -.. code-block:: cmake - - FetchContent_MakeAvailable(googletest) - -The above command not only populates the content, it also adds it to the main -build (if possible) so that the main build can use the populated project's -targets, etc. In some cases, the main project may need to have more precise -control over the population or may be required to explicitly define the -population steps (e.g. if CMake versions earlier than 3.14 need to be -supported). The typical pattern of such custom steps looks like this: - -.. code-block:: cmake - - FetchContent_GetProperties(googletest) - if(NOT googletest_POPULATED) - FetchContent_Populate(googletest) - add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR}) - endif() - -Regardless of which population method is used, when using the -declare-populate pattern with a hierarchical project arrangement, projects at -higher levels in the hierarchy are able to override the population details of -content specified anywhere lower in the project hierarchy. The ability to -detect whether content has already been populated ensures that even if -multiple child projects want certain content to be available, the first one -to populate it wins. The other child project can simply make use of the -already available content instead of repeating the population for itself. -See the :ref:`Examples ` section which demonstrates -this scenario. - -The ``FetchContent`` module also supports defining and populating -content in a single call, with no check for whether the content has been -populated elsewhere in the project already. This is a more low level -operation and would not normally be the way the module is used, but it is -sometimes useful as part of implementing some higher level feature or to -populate some content in CMake's script mode. - - -Declaring Content Details -^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. command:: FetchContent_Declare - - .. code-block:: cmake - - FetchContent_Declare( ...) - - The ``FetchContent_Declare()`` function records the options that describe - how to populate the specified content, but if such details have already - been recorded earlier in this project (regardless of where in the project - hierarchy), this and all later calls for the same content ```` are - ignored. This "first to record, wins" approach is what allows hierarchical - projects to have parent projects override content details of child projects. - - The content ```` can be any string without spaces, but good practice - would be to use only letters, numbers and underscores. The name will be - treated case-insensitively and it should be obvious for the content it - represents, often being the name of the child project or the value given - to its top level :command:`project` command (if it is a CMake project). - For well-known public projects, the name should generally be the official - name of the project. Choosing an unusual name makes it unlikely that other - projects needing that same content will use the same name, leading to - the content being populated multiple times. - - The ```` can be any of the download or update/patch options - that the :command:`ExternalProject_Add` command understands. The configure, - build, install and test steps are explicitly disabled and therefore options - related to them will be ignored. In most cases, ```` will - just be a couple of options defining the download method and method-specific - details like a commit tag or archive hash. For example: - - .. code-block:: cmake - - FetchContent_Declare( - googletest - GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG release-1.8.0 - ) - - FetchContent_Declare( - myCompanyIcons - URL https://intranet.mycompany.com/assets/iconset_1.12.tar.gz - URL_HASH 5588a7b18261c20068beabfb4f530b87 - ) - - FetchContent_Declare( - myCompanyCertificates - SVN_REPOSITORY svn+ssh://svn.mycompany.com/srv/svn/trunk/certs - SVN_REVISION -r12345 - ) - -Populating The Content -^^^^^^^^^^^^^^^^^^^^^^ - -For most common scenarios, population means making content available to the -main build according to previously declared details for that dependency. -There are two main patterns for populating content, one based on calling -:command:`FetchContent_GetProperties` and -:command:`FetchContent_Populate` for more precise control and the other on -calling :command:`FetchContent_MakeAvailable` for a simpler, more automated -approach. The former generally follows this canonical pattern: - -.. _`fetch-content-canonical-pattern`: - -.. code-block:: cmake - - # Check if population has already been performed - FetchContent_GetProperties() - string(TOLOWER "" lcName) - if(NOT ${lcName}_POPULATED) - # Fetch the content using previously declared details - FetchContent_Populate() - - # Set custom variables, policies, etc. - # ... - - # Bring the populated content into the build - add_subdirectory(${${lcName}_SOURCE_DIR} ${${lcName}_BINARY_DIR}) - endif() - -The above is such a common pattern that, where no custom steps are needed -between the calls to :command:`FetchContent_Populate` and -:command:`add_subdirectory`, equivalent logic can be obtained by calling -:command:`FetchContent_MakeAvailable` instead (and should be preferred where -it meets the needs of the project). - -.. command:: FetchContent_Populate - - .. code-block:: cmake - - FetchContent_Populate( ) - - In most cases, the only argument given to ``FetchContent_Populate()`` is the - ````. When used this way, the command assumes the content details have - been recorded by an earlier call to :command:`FetchContent_Declare`. The - details are stored in a global property, so they are unaffected by things - like variable or directory scope. Therefore, it doesn't matter where in the - project the details were previously declared, as long as they have been - declared before the call to ``FetchContent_Populate()``. Those saved details - are then used to construct a call to :command:`ExternalProject_Add` in a - private sub-build to perform the content population immediately. The - implementation of ``ExternalProject_Add()`` ensures that if the content has - already been populated in a previous CMake run, that content will be reused - rather than repopulating them again. For the common case where population - involves downloading content, the cost of the download is only paid once. - - An internal global property records when a particular content population - request has been processed. If ``FetchContent_Populate()`` is called more - than once for the same content name within a configure run, the second call - will halt with an error. Projects can and should check whether content - population has already been processed with the - :command:`FetchContent_GetProperties` command before calling - ``FetchContent_Populate()``. - - ``FetchContent_Populate()`` will set three variables in the scope of the - caller; ``_POPULATED``, ``_SOURCE_DIR`` and - ``_BINARY_DIR``, where ```` is the lowercased ````. - ``_POPULATED`` will always be set to ``True`` by the call. - ``_SOURCE_DIR`` is the location where the - content can be found upon return (it will have already been populated), while - ``_BINARY_DIR`` is a directory intended for use as a corresponding - build directory. The main use case for the two directory variables is to - call :command:`add_subdirectory` immediately after population, i.e.: - - .. code-block:: cmake - - FetchContent_Populate(FooBar ...) - add_subdirectory(${foobar_SOURCE_DIR} ${foobar_BINARY_DIR}) - - The values of the three variables can also be retrieved from anywhere in the - project hierarchy using the :command:`FetchContent_GetProperties` command. - - A number of cache variables influence the behavior of all content population - performed using details saved from a :command:`FetchContent_Declare` call: - - ``FETCHCONTENT_BASE_DIR`` - In most cases, the saved details do not specify any options relating to the - directories to use for the internal sub-build, final source and build areas. - It is generally best to leave these decisions up to the ``FetchContent`` - module to handle on the project's behalf. The ``FETCHCONTENT_BASE_DIR`` - cache variable controls the point under which all content population - directories are collected, but in most cases developers would not need to - change this. The default location is ``${CMAKE_BINARY_DIR}/_deps``, but if - developers change this value, they should aim to keep the path short and - just below the top level of the build tree to avoid running into path - length problems on Windows. - - ``FETCHCONTENT_QUIET`` - The logging output during population can be quite verbose, making the - configure stage quite noisy. This cache option (``ON`` by default) hides - all population output unless an error is encountered. If experiencing - problems with hung downloads, temporarily switching this option off may - help diagnose which content population is causing the issue. - - ``FETCHCONTENT_FULLY_DISCONNECTED`` - When this option is enabled, no attempt is made to download or update - any content. It is assumed that all content has already been populated in - a previous run or the source directories have been pointed at existing - contents the developer has provided manually (using options described - further below). When the developer knows that no changes have been made to - any content details, turning this option ``ON`` can significantly speed up - the configure stage. It is ``OFF`` by default. - - ``FETCHCONTENT_UPDATES_DISCONNECTED`` - This is a less severe download/update control compared to - ``FETCHCONTENT_FULLY_DISCONNECTED``. Instead of bypassing all download and - update logic, the ``FETCHCONTENT_UPDATES_DISCONNECTED`` only disables the - update stage. Therefore, if content has not been downloaded previously, - it will still be downloaded when this option is enabled. This can speed up - the configure stage, but not as much as - ``FETCHCONTENT_FULLY_DISCONNECTED``. It is ``OFF`` by default. - - In addition to the above cache variables, the following cache variables are - also defined for each content name (```` is the uppercased value of - ````): - - ``FETCHCONTENT_SOURCE_DIR_`` - If this is set, no download or update steps are performed for the specified - content and the ``_SOURCE_DIR`` variable returned to the caller is - pointed at this location. This gives developers a way to have a separate - checkout of the content that they can modify freely without interference - from the build. The build simply uses that existing source, but it still - defines ``_BINARY_DIR`` to point inside its own build area. - Developers are strongly encouraged to use this mechanism rather than - editing the sources populated in the default location, as changes to - sources in the default location can be lost when content population details - are changed by the project. - - ``FETCHCONTENT_UPDATES_DISCONNECTED_`` - This is the per-content equivalent of - ``FETCHCONTENT_UPDATES_DISCONNECTED``. If the global option or this option - is ``ON``, then updates will be disabled for the named content. - Disabling updates for individual content can be useful for content whose - details rarely change, while still leaving other frequently changing - content with updates enabled. - - - The ``FetchContent_Populate()`` command also supports a syntax allowing the - content details to be specified directly rather than using any saved - details. This is more low-level and use of this form is generally to be - avoided in favour of using saved content details as outlined above. - Nevertheless, in certain situations it can be useful to invoke the content - population as an isolated operation (typically as part of implementing some - other higher level feature or when using CMake in script mode): - - .. code-block:: cmake - - FetchContent_Populate( - [QUIET] - [SUBBUILD_DIR ] - [SOURCE_DIR ] - [BINARY_DIR ] - ... - ) - - This form has a number of key differences to that where only ```` is - provided: - - - All required population details are assumed to have been provided directly - in the call to ``FetchContent_Populate()``. Any saved details for - ```` are ignored. - - No check is made for whether content for ```` has already been - populated. - - No global property is set to record that the population has occurred. - - No global properties record the source or binary directories used for the - populated content. - - The ``FETCHCONTENT_FULLY_DISCONNECTED`` and - ``FETCHCONTENT_UPDATES_DISCONNECTED`` cache variables are ignored. - - The ``_SOURCE_DIR`` and ``_BINARY_DIR`` variables are still - returned to the caller, but since these locations are not stored as global - properties when this form is used, they are only available to the calling - scope and below rather than the entire project hierarchy. No - ``_POPULATED`` variable is set in the caller's scope with this form. - - The supported options for ``FetchContent_Populate()`` are the same as those - for :command:`FetchContent_Declare()`. Those few options shown just - above are either specific to ``FetchContent_Populate()`` or their behavior is - slightly modified from how :command:`ExternalProject_Add` treats them. - - ``QUIET`` - The ``QUIET`` option can be given to hide the output associated with - populating the specified content. If the population fails, the output will - be shown regardless of whether this option was given or not so that the - cause of the failure can be diagnosed. The global ``FETCHCONTENT_QUIET`` - cache variable has no effect on ``FetchContent_Populate()`` calls where the - content details are provided directly. - - ``SUBBUILD_DIR`` - The ``SUBBUILD_DIR`` argument can be provided to change the location of the - sub-build created to perform the population. The default value is - ``${CMAKE_CURRENT_BINARY_DIR}/-subbuild`` and it would be unusual - to need to override this default. If a relative path is specified, it will - be interpreted as relative to :variable:`CMAKE_CURRENT_BINARY_DIR`. - - ``SOURCE_DIR``, ``BINARY_DIR`` - The ``SOURCE_DIR`` and ``BINARY_DIR`` arguments are supported by - :command:`ExternalProject_Add`, but different default values are used by - ``FetchContent_Populate()``. ``SOURCE_DIR`` defaults to - ``${CMAKE_CURRENT_BINARY_DIR}/-src`` and ``BINARY_DIR`` defaults to - ``${CMAKE_CURRENT_BINARY_DIR}/-build``. If a relative path is - specified, it will be interpreted as relative to - :variable:`CMAKE_CURRENT_BINARY_DIR`. - - In addition to the above explicit options, any other unrecognized options are - passed through unmodified to :command:`ExternalProject_Add` to perform the - download, patch and update steps. The following options are explicitly - prohibited (they are disabled by the ``FetchContent_Populate()`` command): - - - ``CONFIGURE_COMMAND`` - - ``BUILD_COMMAND`` - - ``INSTALL_COMMAND`` - - ``TEST_COMMAND`` - - If using ``FetchContent_Populate()`` within CMake's script mode, be aware - that the implementation sets up a sub-build which therefore requires a CMake - generator and build tool to be available. If these cannot be found by - default, then the :variable:`CMAKE_GENERATOR` and/or - :variable:`CMAKE_MAKE_PROGRAM` variables will need to be set appropriately - on the command line invoking the script. - - -.. command:: FetchContent_GetProperties - - When using saved content details, a call to :command:`FetchContent_Populate` - records information in global properties which can be queried at any time. - This information includes the source and binary directories associated with - the content and also whether or not the content population has been processed - during the current configure run. - - .. code-block:: cmake - - FetchContent_GetProperties( - [SOURCE_DIR ] - [BINARY_DIR ] - [POPULATED ] - ) - - The ``SOURCE_DIR``, ``BINARY_DIR`` and ``POPULATED`` options can be used to - specify which properties should be retrieved. Each option accepts a value - which is the name of the variable in which to store that property. Most of - the time though, only ```` is given, in which case the call will then - set the same variables as a call to - :command:`FetchContent_Populate(name) `. This allows - the following canonical pattern to be used, which ensures that the relevant - variables will always be defined regardless of whether or not the population - has been performed elsewhere in the project already: - - .. code-block:: cmake - - FetchContent_GetProperties(foobar) - if(NOT foobar_POPULATED) - FetchContent_Populate(foobar) - ... - endif() - - The above pattern allows other parts of the overall project hierarchy to - re-use the same content and ensure that it is only populated once. - - -.. command:: FetchContent_MakeAvailable - - .. code-block:: cmake - - FetchContent_MakeAvailable( [...] ) - - This command implements the common pattern typically needed for most - dependencies. It iterates over each of the named dependencies in turn - and for each one it loosely follows the same - :ref:`canonical pattern ` as - presented at the beginning of this section. One small difference to - that pattern is that it will only call :command:`add_subdirectory` on the - populated content if there is a ``CMakeLists.txt`` file in its top level - source directory. This allows the command to be used for dependencies - that make downloaded content available at a known location but which do - not need or support being added directly to the build. - - -.. _`fetch-content-examples`: - -Examples -^^^^^^^^ - -This first fairly straightforward example ensures that some popular testing -frameworks are available to the main build: - -.. code-block:: cmake - - include(FetchContent) - FetchContent_Declare( - googletest - GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG release-1.8.0 - ) - FetchContent_Declare( - Catch2 - GIT_REPOSITORY https://github.com/catchorg/Catch2.git - GIT_TAG v2.5.0 - ) - - # After the following call, the CMake targets defined by googletest and - # Catch2 will be defined and available to the rest of the build - FetchContent_MakeAvailable(googletest Catch2) - - -In more complex project hierarchies, the dependency relationships can be more -complicated. Consider a hierarchy where ``projA`` is the top level project and -it depends directly on projects ``projB`` and ``projC``. Both ``projB`` and -``projC`` can be built standalone and they also both depend on another project -``projD``. ``projB`` additionally depends on ``projE``. This example assumes -that all five projects are available on a company git server. The -``CMakeLists.txt`` of each project might have sections like the following: - -*projA*: - -.. code-block:: cmake - - include(FetchContent) - FetchContent_Declare( - projB - GIT_REPOSITORY git@mycompany.com:git/projB.git - GIT_TAG 4a89dc7e24ff212a7b5167bef7ab079d - ) - FetchContent_Declare( - projC - GIT_REPOSITORY git@mycompany.com:git/projC.git - GIT_TAG 4ad4016bd1d8d5412d135cf8ceea1bb9 - ) - FetchContent_Declare( - projD - GIT_REPOSITORY git@mycompany.com:git/projD.git - GIT_TAG origin/integrationBranch - ) - FetchContent_Declare( - projE - GIT_REPOSITORY git@mycompany.com:git/projE.git - GIT_TAG origin/release/2.3-rc1 - ) - - # Order is important, see notes in the discussion further below - FetchContent_MakeAvailable(projD projB projC) - -*projB*: - -.. code-block:: cmake - - include(FetchContent) - FetchContent_Declare( - projD - GIT_REPOSITORY git@mycompany.com:git/projD.git - GIT_TAG 20b415f9034bbd2a2e8216e9a5c9e632 - ) - FetchContent_Declare( - projE - GIT_REPOSITORY git@mycompany.com:git/projE.git - GIT_TAG 68e20f674a48be38d60e129f600faf7d - ) - - FetchContent_MakeAvailable(projD projE) - -*projC*: - -.. code-block:: cmake - - include(FetchContent) - FetchContent_Declare( - projD - GIT_REPOSITORY git@mycompany.com:git/projD.git - GIT_TAG 7d9a17ad2c962aa13e2fbb8043fb6b8a - ) - - # This particular version of projD requires workarounds - FetchContent_GetProperties(projD) - if(NOT projd_POPULATED) - FetchContent_Populate(projD) - - # Copy an additional/replacement file into the populated source - file(COPY someFile.c DESTINATION ${projd_SOURCE_DIR}/src) - - add_subdirectory(${projd_SOURCE_DIR} ${projd_BINARY_DIR}) - endif() - -A few key points should be noted in the above: - -- ``projB`` and ``projC`` define different content details for ``projD``, - but ``projA`` also defines a set of content details for ``projD``. - Because ``projA`` will define them first, the details from ``projB`` and - ``projC`` will not be used. The override details defined by ``projA`` - are not required to match either of those from ``projB`` or ``projC``, but - it is up to the higher level project to ensure that the details it does - define still make sense for the child projects. -- In the ``projA`` call to :command:`FetchContent_MakeAvailable`, ``projD`` - is listed ahead of ``projB`` and ``projC`` to ensure that ``projA`` is in - control of how ``projD`` is populated. -- While ``projA`` defines content details for ``projE``, it does not need - to explicitly call ``FetchContent_MakeAvailable(projE)`` or - ``FetchContent_Populate(projD)`` itself. Instead, it leaves that to the - child ``projB``. For higher level projects, it is often enough to just - define the override content details and leave the actual population to the - child projects. This saves repeating the same thing at each level of the - project hierarchy unnecessarily. - - -Projects don't always need to add the populated content to the build. -Sometimes the project just wants to make the downloaded content available at -a predictable location. The next example ensures that a set of standard -company toolchain files (and potentially even the toolchain binaries -themselves) is available early enough to be used for that same build. - -.. code-block:: cmake - - cmake_minimum_required(VERSION 3.14) - - include(FetchContent) - FetchContent_Declare( - mycom_toolchains - URL https://intranet.mycompany.com//toolchains_1.3.2.tar.gz - ) - FetchContent_MakeAvailable(mycom_toolchains) - - project(CrossCompileExample) - -The project could be configured to use one of the downloaded toolchains like -so: - -.. code-block:: shell - - cmake -DCMAKE_TOOLCHAIN_FILE=_deps/mycom_toolchains-src/toolchain_arm.cmake /path/to/src - -When CMake processes the ``CMakeLists.txt`` file, it will download and unpack -the tarball into ``_deps/mycompany_toolchains-src`` relative to the build -directory. The :variable:`CMAKE_TOOLCHAIN_FILE` variable is not used until -the :command:`project` command is reached, at which point CMake looks for the -named toolchain file relative to the build directory. Because the tarball has -already been downloaded and unpacked by then, the toolchain file will be in -place, even the very first time that ``cmake`` is run in the build directory. - -Lastly, the following example demonstrates how one might download and unpack a -firmware tarball using CMake's :manual:`script mode `. The call to -:command:`FetchContent_Populate` specifies all the content details and the -unpacked firmware will be placed in a ``firmware`` directory below the -current working directory. - -*getFirmware.cmake*: - -.. code-block:: cmake - - # NOTE: Intended to be run in script mode with cmake -P - include(FetchContent) - FetchContent_Populate( - firmware - URL https://mycompany.com/assets/firmware-1.23-arm.tar.gz - URL_HASH MD5=68247684da89b608d466253762b0ff11 - SOURCE_DIR firmware - ) - -#]=======================================================================] - - -set(__FetchContent_privateDir "${CMAKE_CURRENT_LIST_DIR}/FetchContent") - -#======================================================================= -# Recording and retrieving content details for later population -#======================================================================= - -# Internal use, projects must not call this directly. It is -# intended for use by FetchContent_Declare() only. -# -# Sets a content-specific global property (not meant for use -# outside of functions defined here in this file) which can later -# be retrieved using __FetchContent_getSavedDetails() with just the -# same content name. If there is already a value stored in the -# property, it is left unchanged and this call has no effect. -# This allows parent projects to define the content details, -# overriding anything a child project may try to set (properties -# are not cached between runs, so the first thing to set it in a -# build will be in control). -function(__FetchContent_declareDetails contentName) - - string(TOLOWER ${contentName} contentNameLower) - set(propertyName "_FetchContent_${contentNameLower}_savedDetails") - get_property(alreadyDefined GLOBAL PROPERTY ${propertyName} DEFINED) - if(NOT alreadyDefined) - define_property(GLOBAL PROPERTY ${propertyName} - BRIEF_DOCS "Internal implementation detail of FetchContent_Populate()" - FULL_DOCS "Details used by FetchContent_Populate() for ${contentName}" - ) - set_property(GLOBAL PROPERTY ${propertyName} ${ARGN}) - endif() - -endfunction() - - -# Internal use, projects must not call this directly. It is -# intended for use by the FetchContent_Declare() function. -# -# Retrieves details saved for the specified content in an -# earlier call to __FetchContent_declareDetails(). -function(__FetchContent_getSavedDetails contentName outVar) - - string(TOLOWER ${contentName} contentNameLower) - set(propertyName "_FetchContent_${contentNameLower}_savedDetails") - get_property(alreadyDefined GLOBAL PROPERTY ${propertyName} DEFINED) - if(NOT alreadyDefined) - message(FATAL_ERROR "No content details recorded for ${contentName}") - endif() - get_property(propertyValue GLOBAL PROPERTY ${propertyName}) - set(${outVar} "${propertyValue}" PARENT_SCOPE) - -endfunction() - - -# Saves population details of the content, sets defaults for the -# SOURCE_DIR and BUILD_DIR. -function(FetchContent_Declare contentName) - - set(options "") - set(oneValueArgs SVN_REPOSITORY) - set(multiValueArgs "") - - cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - unset(srcDirSuffix) - unset(svnRepoArgs) - if(ARG_SVN_REPOSITORY) - # Add a hash of the svn repository URL to the source dir. This works - # around the problem where if the URL changes, the download would - # fail because it tries to checkout/update rather than switch the - # old URL to the new one. We limit the hash to the first 7 characters - # so that the source path doesn't get overly long (which can be a - # problem on windows due to path length limits). - string(SHA1 urlSHA ${ARG_SVN_REPOSITORY}) - string(SUBSTRING ${urlSHA} 0 7 urlSHA) - set(srcDirSuffix "-${urlSHA}") - set(svnRepoArgs SVN_REPOSITORY ${ARG_SVN_REPOSITORY}) - endif() - - string(TOLOWER ${contentName} contentNameLower) - __FetchContent_declareDetails( - ${contentNameLower} - SOURCE_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-src${srcDirSuffix}" - BINARY_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-build" - ${svnRepoArgs} - # List these last so they can override things we set above - ${ARG_UNPARSED_ARGUMENTS} - ) - -endfunction() - - -#======================================================================= -# Set/get whether the specified content has been populated yet. -# The setter also records the source and binary dirs used. -#======================================================================= - -# Internal use, projects must not call this directly. It is -# intended for use by the FetchContent_Populate() function to -# record when FetchContent_Populate() is called for a particular -# content name. -function(__FetchContent_setPopulated contentName sourceDir binaryDir) - - string(TOLOWER ${contentName} contentNameLower) - set(prefix "_FetchContent_${contentNameLower}") - - set(propertyName "${prefix}_sourceDir") - define_property(GLOBAL PROPERTY ${propertyName} - BRIEF_DOCS "Internal implementation detail of FetchContent_Populate()" - FULL_DOCS "Details used by FetchContent_Populate() for ${contentName}" - ) - set_property(GLOBAL PROPERTY ${propertyName} ${sourceDir}) - - set(propertyName "${prefix}_binaryDir") - define_property(GLOBAL PROPERTY ${propertyName} - BRIEF_DOCS "Internal implementation detail of FetchContent_Populate()" - FULL_DOCS "Details used by FetchContent_Populate() for ${contentName}" - ) - set_property(GLOBAL PROPERTY ${propertyName} ${binaryDir}) - - set(propertyName "${prefix}_populated") - define_property(GLOBAL PROPERTY ${propertyName} - BRIEF_DOCS "Internal implementation detail of FetchContent_Populate()" - FULL_DOCS "Details used by FetchContent_Populate() for ${contentName}" - ) - set_property(GLOBAL PROPERTY ${propertyName} True) - -endfunction() - - -# Set variables in the calling scope for any of the retrievable -# properties. If no specific properties are requested, variables -# will be set for all retrievable properties. -# -# This function is intended to also be used by projects as the canonical -# way to detect whether they should call FetchContent_Populate() -# and pull the populated source into the build with add_subdirectory(), -# if they are using the populated content in that way. -function(FetchContent_GetProperties contentName) - - string(TOLOWER ${contentName} contentNameLower) - - set(options "") - set(oneValueArgs SOURCE_DIR BINARY_DIR POPULATED) - set(multiValueArgs "") - - cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - if(NOT ARG_SOURCE_DIR AND - NOT ARG_BINARY_DIR AND - NOT ARG_POPULATED) - # No specific properties requested, provide them all - set(ARG_SOURCE_DIR ${contentNameLower}_SOURCE_DIR) - set(ARG_BINARY_DIR ${contentNameLower}_BINARY_DIR) - set(ARG_POPULATED ${contentNameLower}_POPULATED) - endif() - - set(prefix "_FetchContent_${contentNameLower}") - - if(ARG_SOURCE_DIR) - set(propertyName "${prefix}_sourceDir") - get_property(value GLOBAL PROPERTY ${propertyName}) - if(value) - set(${ARG_SOURCE_DIR} ${value} PARENT_SCOPE) - endif() - endif() - - if(ARG_BINARY_DIR) - set(propertyName "${prefix}_binaryDir") - get_property(value GLOBAL PROPERTY ${propertyName}) - if(value) - set(${ARG_BINARY_DIR} ${value} PARENT_SCOPE) - endif() - endif() - - if(ARG_POPULATED) - set(propertyName "${prefix}_populated") - get_property(value GLOBAL PROPERTY ${propertyName} DEFINED) - set(${ARG_POPULATED} ${value} PARENT_SCOPE) - endif() - -endfunction() - - -#======================================================================= -# Performing the population -#======================================================================= - -# The value of contentName will always have been lowercased by the caller. -# All other arguments are assumed to be options that are understood by -# ExternalProject_Add(), except for QUIET and SUBBUILD_DIR. -function(__FetchContent_directPopulate contentName) - - set(options - QUIET - ) - set(oneValueArgs - SUBBUILD_DIR - SOURCE_DIR - BINARY_DIR - # Prevent the following from being passed through - CONFIGURE_COMMAND - BUILD_COMMAND - INSTALL_COMMAND - TEST_COMMAND - # We force both of these to be ON since we are always executing serially - # and we want all steps to have access to the terminal in case they - # need input from the command line (e.g. ask for a private key password) - # or they want to provide timely progress. We silently absorb and - # discard these if they are set by the caller. - USES_TERMINAL_DOWNLOAD - USES_TERMINAL_UPDATE - ) - set(multiValueArgs "") - - cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - if(NOT ARG_SUBBUILD_DIR) - message(FATAL_ERROR "Internal error: SUBBUILD_DIR not set") - elseif(NOT IS_ABSOLUTE "${ARG_SUBBUILD_DIR}") - set(ARG_SUBBUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/${ARG_SUBBUILD_DIR}") - endif() - - if(NOT ARG_SOURCE_DIR) - message(FATAL_ERROR "Internal error: SOURCE_DIR not set") - elseif(NOT IS_ABSOLUTE "${ARG_SOURCE_DIR}") - set(ARG_SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/${ARG_SOURCE_DIR}") - endif() - - if(NOT ARG_BINARY_DIR) - message(FATAL_ERROR "Internal error: BINARY_DIR not set") - elseif(NOT IS_ABSOLUTE "${ARG_BINARY_DIR}") - set(ARG_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/${ARG_BINARY_DIR}") - endif() - - # Ensure the caller can know where to find the source and build directories - # with some convenient variables. Doing this here ensures the caller sees - # the correct result in the case where the default values are overridden by - # the content details set by the project. - set(${contentName}_SOURCE_DIR "${ARG_SOURCE_DIR}" PARENT_SCOPE) - set(${contentName}_BINARY_DIR "${ARG_BINARY_DIR}" PARENT_SCOPE) - - # The unparsed arguments may contain spaces, so build up ARG_EXTRA - # in such a way that it correctly substitutes into the generated - # CMakeLists.txt file with each argument quoted. - unset(ARG_EXTRA) - foreach(arg IN LISTS ARG_UNPARSED_ARGUMENTS) - set(ARG_EXTRA "${ARG_EXTRA} \"${arg}\"") - endforeach() - - # Hide output if requested, but save it to a variable in case there's an - # error so we can show the output upon failure. When not quiet, don't - # capture the output to a variable because the user may want to see the - # output as it happens (e.g. progress during long downloads). Combine both - # stdout and stderr in the one capture variable so the output stays in order. - if (ARG_QUIET) - set(outputOptions - OUTPUT_VARIABLE capturedOutput - ERROR_VARIABLE capturedOutput - ) - else() - set(capturedOutput) - set(outputOptions) - message(STATUS "Populating ${contentName}") - endif() - - if(CMAKE_GENERATOR) - set(generatorOpts "-G${CMAKE_GENERATOR}") - if(CMAKE_GENERATOR_PLATFORM) - list(APPEND generatorOpts "-A${CMAKE_GENERATOR_PLATFORM}") - endif() - if(CMAKE_GENERATOR_TOOLSET) - list(APPEND generatorOpts "-T${CMAKE_GENERATOR_TOOLSET}") - endif() - - if(CMAKE_MAKE_PROGRAM) - list(APPEND generatorOpts "-DCMAKE_MAKE_PROGRAM:FILEPATH=${CMAKE_MAKE_PROGRAM}") - endif() - - else() - # Likely we've been invoked via CMake's script mode where no - # generator is set (and hence CMAKE_MAKE_PROGRAM could not be - # trusted even if provided). We will have to rely on being - # able to find the default generator and build tool. - unset(generatorOpts) - endif() - - # Create and build a separate CMake project to carry out the population. - # If we've already previously done these steps, they will not cause - # anything to be updated, so extra rebuilds of the project won't occur. - # Make sure to pass through CMAKE_MAKE_PROGRAM in case the main project - # has this set to something not findable on the PATH. - configure_file("${__FetchContent_privateDir}/CMakeLists.cmake.in" - "${ARG_SUBBUILD_DIR}/CMakeLists.txt") - execute_process( - COMMAND ${CMAKE_COMMAND} ${generatorOpts} . - RESULT_VARIABLE result - ${outputOptions} - WORKING_DIRECTORY "${ARG_SUBBUILD_DIR}" - ) - if(result) - if(capturedOutput) - message("${capturedOutput}") - endif() - message(FATAL_ERROR "CMake step for ${contentName} failed: ${result}") - endif() - execute_process( - COMMAND ${CMAKE_COMMAND} --build . - RESULT_VARIABLE result - ${outputOptions} - WORKING_DIRECTORY "${ARG_SUBBUILD_DIR}" - ) - if(result) - if(capturedOutput) - message("${capturedOutput}") - endif() - message(FATAL_ERROR "Build step for ${contentName} failed: ${result}") - endif() - -endfunction() - - -option(FETCHCONTENT_FULLY_DISCONNECTED "Disables all attempts to download or update content and assumes source dirs already exist") -option(FETCHCONTENT_UPDATES_DISCONNECTED "Enables UPDATE_DISCONNECTED behavior for all content population") -option(FETCHCONTENT_QUIET "Enables QUIET option for all content population" ON) -set(FETCHCONTENT_BASE_DIR "${CMAKE_BINARY_DIR}/_deps" CACHE PATH "Directory under which to collect all populated content") - -# Populate the specified content using details stored from -# an earlier call to FetchContent_Declare(). -function(FetchContent_Populate contentName) - - if(NOT contentName) - message(FATAL_ERROR "Empty contentName not allowed for FetchContent_Populate()") - endif() - - string(TOLOWER ${contentName} contentNameLower) - - if(ARGN) - # This is the direct population form with details fully specified - # as part of the call, so we already have everything we need - __FetchContent_directPopulate( - ${contentNameLower} - SUBBUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/${contentNameLower}-subbuild" - SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/${contentNameLower}-src" - BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/${contentNameLower}-build" - ${ARGN} # Could override any of the above ..._DIR variables - ) - - # Pass source and binary dir variables back to the caller - set(${contentNameLower}_SOURCE_DIR "${${contentNameLower}_SOURCE_DIR}" PARENT_SCOPE) - set(${contentNameLower}_BINARY_DIR "${${contentNameLower}_BINARY_DIR}" PARENT_SCOPE) - - # Don't set global properties, or record that we did this population, since - # this was a direct call outside of the normal declared details form. - # We only want to save values in the global properties for content that - # honours the hierarchical details mechanism so that projects are not - # robbed of the ability to override details set in nested projects. - return() - endif() - - # No details provided, so assume they were saved from an earlier call - # to FetchContent_Declare(). Do a check that we haven't already - # populated this content before in case the caller forgot to check. - FetchContent_GetProperties(${contentName}) - if(${contentNameLower}_POPULATED) - message(FATAL_ERROR "Content ${contentName} already populated in ${${contentNameLower}_SOURCE_DIR}") - endif() - - string(TOUPPER ${contentName} contentNameUpper) - set(FETCHCONTENT_SOURCE_DIR_${contentNameUpper} - "${FETCHCONTENT_SOURCE_DIR_${contentNameUpper}}" - CACHE PATH "When not empty, overrides where to find pre-populated content for ${contentName}") - - if(FETCHCONTENT_SOURCE_DIR_${contentNameUpper}) - # The source directory has been explicitly provided in the cache, - # so no population is required - set(${contentNameLower}_SOURCE_DIR "${FETCHCONTENT_SOURCE_DIR_${contentNameUpper}}") - set(${contentNameLower}_BINARY_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-build") - - elseif(FETCHCONTENT_FULLY_DISCONNECTED) - # Bypass population and assume source is already there from a previous run - set(${contentNameLower}_SOURCE_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-src") - set(${contentNameLower}_BINARY_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-build") - - else() - # Support both a global "disconnect all updates" and a per-content - # update test (either one being set disables updates for this content). - option(FETCHCONTENT_UPDATES_DISCONNECTED_${contentNameUpper} - "Enables UPDATE_DISCONNECTED behavior just for population of ${contentName}") - if(FETCHCONTENT_UPDATES_DISCONNECTED OR - FETCHCONTENT_UPDATES_DISCONNECTED_${contentNameUpper}) - set(disconnectUpdates True) - else() - set(disconnectUpdates False) - endif() - - if(FETCHCONTENT_QUIET) - set(quietFlag QUIET) - else() - unset(quietFlag) - endif() - - __FetchContent_getSavedDetails(${contentName} contentDetails) - if("${contentDetails}" STREQUAL "") - message(FATAL_ERROR "No details have been set for content: ${contentName}") - endif() - - __FetchContent_directPopulate( - ${contentNameLower} - ${quietFlag} - UPDATE_DISCONNECTED ${disconnectUpdates} - SUBBUILD_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-subbuild" - SOURCE_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-src" - BINARY_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-build" - # Put the saved details last so they can override any of the - # the options we set above (this can include SOURCE_DIR or - # BUILD_DIR) - ${contentDetails} - ) - endif() - - __FetchContent_setPopulated( - ${contentName} - ${${contentNameLower}_SOURCE_DIR} - ${${contentNameLower}_BINARY_DIR} - ) - - # Pass variables back to the caller. The variables passed back here - # must match what FetchContent_GetProperties() sets when it is called - # with just the content name. - set(${contentNameLower}_SOURCE_DIR "${${contentNameLower}_SOURCE_DIR}" PARENT_SCOPE) - set(${contentNameLower}_BINARY_DIR "${${contentNameLower}_BINARY_DIR}" PARENT_SCOPE) - set(${contentNameLower}_POPULATED True PARENT_SCOPE) - -endfunction() - -# Arguments are assumed to be the names of dependencies that have been -# declared previously and should be populated. It is not an error if -# any of them have already been populated (they will just be skipped in -# that case). The command is implemented as a macro so that the variables -# defined by the FetchContent_GetProperties() and FetchContent_Populate() -# calls will be available to the caller. -macro(FetchContent_MakeAvailable) - - foreach(contentName IN ITEMS ${ARGV}) - string(TOLOWER ${contentName} contentNameLower) - FetchContent_GetProperties(${contentName}) - if(NOT ${contentNameLower}_POPULATED) - FetchContent_Populate(${contentName}) - - # Only try to call add_subdirectory() if the populated content - # can be treated that way. Protecting the call with the check - # allows this function to be used for projects that just want - # to ensure the content exists, such as to provide content at - # a known location. - if(EXISTS ${${contentNameLower}_SOURCE_DIR}/CMakeLists.txt) - add_subdirectory(${${contentNameLower}_SOURCE_DIR} - ${${contentNameLower}_BINARY_DIR}) - endif() - endif() - endforeach() - -endmacro() \ No newline at end of file diff --git a/cmake-modules/GetGitRevisionDescription.cmake b/cmake-modules/GetGitRevisionDescription.cmake deleted file mode 100644 index ddca0f18..00000000 --- a/cmake-modules/GetGitRevisionDescription.cmake +++ /dev/null @@ -1,168 +0,0 @@ -# - Returns a version string from Git -# -# These functions force a re-configure on each git commit so that you can -# trust the values of the variables in your build system. -# -# get_git_head_revision( [ ...]) -# -# Returns the refspec and sha hash of the current head revision -# -# git_describe( [ ...]) -# -# Returns the results of git describe on the source tree, and adjusting -# the output so that it tests false if an error occurs. -# -# git_get_exact_tag( [ ...]) -# -# Returns the results of git describe --exact-match on the source tree, -# and adjusting the output so that it tests false if there was no exact -# matching tag. -# -# git_local_changes() -# -# Returns either "CLEAN" or "DIRTY" with respect to uncommitted changes. -# Uses the return code of "git diff-index --quiet HEAD --". -# Does not regard untracked files. -# -# Requires CMake 2.6 or newer (uses the 'function' command) -# -# Original Author: -# 2009-2010 Ryan Pavlik -# http://academic.cleardefinition.com -# Iowa State University HCI Graduate Program/VRAC -# -# Copyright Iowa State University 2009-2010. -# Distributed under the Boost Software License, Version 1.0. -# (See accompanying file LICENSE_1_0.txt or copy at -# http://www.boost.org/LICENSE_1_0.txt) - -if(__get_git_revision_description) - return() -endif() -set(__get_git_revision_description YES) - -# We must run the following at "include" time, not at function call time, -# to find the path to this module rather than the path to a calling list file -get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH) - -function(get_git_head_revision _refspecvar _hashvar) - set(GIT_PARENT_DIR "${CMAKE_CURRENT_SOURCE_DIR}") - set(GIT_DIR "${GIT_PARENT_DIR}/.git") - while(NOT EXISTS "${GIT_DIR}") # .git dir not found, search parent directories - set(GIT_PREVIOUS_PARENT "${GIT_PARENT_DIR}") - get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH) - if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT) - # We have reached the root directory, we are not in git - set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE) - set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE) - return() - endif() - set(GIT_DIR "${GIT_PARENT_DIR}/.git") - endwhile() - # check if this is a submodule - if(NOT IS_DIRECTORY ${GIT_DIR}) - file(READ ${GIT_DIR} submodule) - string(REGEX REPLACE "gitdir: (.*)\n$" "\\1" GIT_DIR_RELATIVE ${submodule}) - get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH) - get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE) - endif() - set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data") - if(NOT EXISTS "${GIT_DATA}") - file(MAKE_DIRECTORY "${GIT_DATA}") - endif() - - if(NOT EXISTS "${GIT_DIR}/HEAD") - return() - endif() - set(HEAD_FILE "${GIT_DATA}/HEAD") - configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY) - - configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in" - "${GIT_DATA}/grabRef.cmake" - @ONLY) - include("${GIT_DATA}/grabRef.cmake") - - set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE) - set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE) - # message(STATUS ${HEAD_REF}) - # message(STATUS ${HEAD_HASH}) -endfunction() - -function(git_describe _var) - if(NOT GIT_FOUND) - find_package(Git QUIET) - endif() - get_git_head_revision(refspec hash) - if(NOT GIT_FOUND) - set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) - return() - endif() - if(NOT hash) - set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE) - return() - endif() - - # TODO sanitize - #if((${ARGN}" MATCHES "&&") OR - # (ARGN MATCHES "||") OR - # (ARGN MATCHES "\\;")) - # message("Please report the following error to the project!") - # message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}") - #endif() - - #message(STATUS "Arguments to execute_process: ${ARGN}") - execute_process(COMMAND - "${GIT_EXECUTABLE}" - describe - ${ARGN} - WORKING_DIRECTORY - "${CMAKE_CURRENT_SOURCE_DIR}" - RESULT_VARIABLE - res - OUTPUT_VARIABLE - out - ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) - if(NOT res EQUAL 0) - set(out "${out}-${res}-NOTFOUND") - endif() - - set(${_var} "${out}" PARENT_SCOPE) -endfunction() - -function(git_get_exact_tag _var) - git_describe(out --exact-match ${ARGN}) - set(${_var} "${out}" PARENT_SCOPE) -endfunction() - -function(git_local_changes _var) - if(NOT GIT_FOUND) - find_package(Git QUIET) - endif() - get_git_head_revision(refspec hash) - if(NOT GIT_FOUND) - set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) - return() - endif() - if(NOT hash) - set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE) - return() - endif() - - execute_process(COMMAND - "${GIT_EXECUTABLE}" - diff-index --quiet HEAD -- - WORKING_DIRECTORY - "${CMAKE_CURRENT_SOURCE_DIR}" - RESULT_VARIABLE - res - OUTPUT_VARIABLE - out - ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) - if(res EQUAL 0) - set(${_var} "CLEAN" PARENT_SCOPE) - else() - set(${_var} "DIRTY" PARENT_SCOPE) - endif() -endfunction() diff --git a/cmake-modules/__init__.py.in b/cmake-modules/__init__.py.in deleted file mode 100644 index 6919d6e0..00000000 --- a/cmake-modules/__init__.py.in +++ /dev/null @@ -1,7 +0,0 @@ -def __bootstrap__(): - global __bootstrap__, __loader__, __file__ - import sys, pkg_resources, imp - __file__ = pkg_resources.resource_filename(__name__,"$") - __loader__ = None; del __bootstrap__, __loader__ - imp.load_dynamic(__name__,__file__) -__bootstrap__() \ No newline at end of file diff --git a/cmake-modules/FindBlender.cmake b/cmake/FindBlender.cmake similarity index 98% rename from cmake-modules/FindBlender.cmake rename to cmake/FindBlender.cmake index bbdc4111..b3a7803f 100644 --- a/cmake-modules/FindBlender.cmake +++ b/cmake/FindBlender.cmake @@ -36,7 +36,7 @@ find_program(BLENDER_EXECUTABLE NAMES blender Blender) if(BLENDER_EXECUTABLE) configure_file( - ${CMAKE_SOURCE_DIR}/cmake-modules/blenderexec.py.in + ${CMAKE_SOURCE_DIR}/cmake/blenderexec.py.in ${CMAKE_CURRENT_BINARY_DIR}/blenderexec.py) # Get the python path and version from blender @@ -84,4 +84,4 @@ find_package_handle_standard_args(Blender BLENDER_PYTHON_SIZEOF_VOID_P VERSION_VAR BLENDER_VERSION HANDLE_COMPONENTS - ) \ No newline at end of file + ) diff --git a/cmake-modules/FindBreathe.cmake b/cmake/FindBreathe.cmake similarity index 64% rename from cmake-modules/FindBreathe.cmake rename to cmake/FindBreathe.cmake index 8351f35b..1155f433 100644 --- a/cmake-modules/FindBreathe.cmake +++ b/cmake/FindBreathe.cmake @@ -1,14 +1,14 @@ -find_package(PythonInterp) -if(PYTHONINTERP_FOUND) - execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "from importlib import util;print(util.find_spec('breathe') is not None)" +find_package(Python QUIET COMPONENTS Interpreter ) +if(Python_Interpreter_FOUND) + execute_process(COMMAND ${Python_EXECUTABLE} -c "from importlib import util;print(util.find_spec('breathe') is not None)" OUTPUT_VARIABLE BREATHE_IMPORT_STATUS ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) -endif(PYTHONINTERP_FOUND) +endif(Python_Interpreter_FOUND) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Breathe DEFAULT_MSG BREATHE_IMPORT_STATUS -) \ No newline at end of file +) diff --git a/cmake-modules/FindExhale.cmake b/cmake/FindExhale.cmake similarity index 64% rename from cmake-modules/FindExhale.cmake rename to cmake/FindExhale.cmake index eb998c27..726aece8 100644 --- a/cmake-modules/FindExhale.cmake +++ b/cmake/FindExhale.cmake @@ -1,14 +1,14 @@ -find_package(PythonInterp) -if(PYTHONINTERP_FOUND) - execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "from importlib import util;print(util.find_spec('exhale') is not None)" +find_package(Python QUIET COMPONENTS Interpreter ) +if(Python_Interpreter_FOUND) + execute_process(COMMAND ${Python_EXECUTABLE} -c "from importlib import util;print(util.find_spec('exhale') is not None)" OUTPUT_VARIABLE EXHALE_IMPORT_STATUS ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) -endif(PYTHONINTERP_FOUND) +endif(Python_Interpreter_FOUND) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Exhale DEFAULT_MSG EXHALE_IMPORT_STATUS -) \ No newline at end of file +) diff --git a/cmake-modules/FindSphinx.cmake b/cmake/FindSphinx.cmake similarity index 100% rename from cmake-modules/FindSphinx.cmake rename to cmake/FindSphinx.cmake diff --git a/cmake-modules/Findpytest.cmake b/cmake/Findpytest.cmake similarity index 64% rename from cmake-modules/Findpytest.cmake rename to cmake/Findpytest.cmake index 0f9678af..592369c1 100644 --- a/cmake-modules/Findpytest.cmake +++ b/cmake/Findpytest.cmake @@ -1,14 +1,14 @@ -find_package(PythonInterp) -if(PYTHONINTERP_FOUND) - execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "from importlib import util;print(util.find_spec('pytest') is not None)" +find_package(Python QUIET COMPONENTS Interpreter ) +if(Python_Interpreter_FOUND) + execute_process(COMMAND ${Python_EXECUTABLE} -c "from importlib import util;print(util.find_spec('pytest') is not None)" OUTPUT_VARIABLE PYTEST_IMPORT_STATUS ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) -endif(PYTHONINTERP_FOUND) +endif(Python_Interpreter_FOUND) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(pytest DEFAULT_MSG PYTEST_IMPORT_STATUS -) \ No newline at end of file +) diff --git a/cmake/GetGitRevisionDescription.cmake b/cmake/GetGitRevisionDescription.cmake new file mode 100644 index 00000000..db291a9b --- /dev/null +++ b/cmake/GetGitRevisionDescription.cmake @@ -0,0 +1,279 @@ +# - Returns a version string from Git +# +# These functions force a re-configure on each git commit so that you can +# trust the values of the variables in your build system. +# +# get_git_head_revision( [ ...]) +# +# Returns the refspec and sha hash of the current head revision +# +# git_describe( [ ...]) +# +# Returns the results of git describe on the source tree, and adjusting +# the output so that it tests false if an error occurs. +# +# git_describe_working_tree( [ ...]) +# +# Returns the results of git describe on the working tree (--dirty option), +# and adjusting the output so that it tests false if an error occurs. +# +# git_get_exact_tag( [ ...]) +# +# Returns the results of git describe --exact-match on the source tree, +# and adjusting the output so that it tests false if there was no exact +# matching tag. +# +# git_local_changes() +# +# Returns either "CLEAN" or "DIRTY" with respect to uncommitted changes. +# Uses the return code of "git diff-index --quiet HEAD --". +# Does not regard untracked files. +# +# Requires CMake 2.6 or newer (uses the 'function' command) +# +# Original Author: +# 2009-2020 Ryan Pavlik +# http://academic.cleardefinition.com +# +# Copyright 2009-2013, Iowa State University. +# Copyright 2013-2020, Ryan Pavlik +# Copyright 2013-2020, Contributors +# SPDX-License-Identifier: BSL-1.0 +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +if(__get_git_revision_description) + return() +endif() +set(__get_git_revision_description YES) + +# We must run the following at "include" time, not at function call time, +# to find the path to this module rather than the path to a calling list file +get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH) + +# Function _git_find_closest_git_dir finds the next closest .git directory +# that is part of any directory in the path defined by _start_dir. +# The result is returned in the parent scope variable whose name is passed +# as variable _git_dir_var. If no .git directory can be found, the +# function returns an empty string via _git_dir_var. +# +# Example: Given a path C:/bla/foo/bar and assuming C:/bla/.git exists and +# neither foo nor bar contain a file/directory .git. This wil return +# C:/bla/.git +# +function(_git_find_closest_git_dir _start_dir _git_dir_var) + set(cur_dir "${_start_dir}") + set(git_dir "${_start_dir}/.git") + while(NOT EXISTS "${git_dir}") + # .git dir not found, search parent directories + set(git_previous_parent "${cur_dir}") + get_filename_component(cur_dir ${cur_dir} DIRECTORY) + if(cur_dir STREQUAL git_previous_parent) + # We have reached the root directory, we are not in git + set(${_git_dir_var} + "" + PARENT_SCOPE) + return() + endif() + set(git_dir "${cur_dir}/.git") + endwhile() + set(${_git_dir_var} + "${git_dir}" + PARENT_SCOPE) +endfunction() + +function(get_git_head_revision _refspecvar _hashvar) + _git_find_closest_git_dir("${CMAKE_CURRENT_SOURCE_DIR}" GIT_DIR) + + if(NOT "${GIT_DIR}" STREQUAL "") + file(RELATIVE_PATH _relative_to_source_dir "${CMAKE_SOURCE_DIR}" + "${GIT_DIR}") + if("${_relative_to_source_dir}" MATCHES "[.][.]") + # We've gone above the CMake root dir. + set(GIT_DIR "") + endif() + endif() + if("${GIT_DIR}" STREQUAL "") + set(${_refspecvar} + "GITDIR-NOTFOUND" + PARENT_SCOPE) + set(${_hashvar} + "GITDIR-NOTFOUND" + PARENT_SCOPE) + return() + endif() + + # Check if the current source dir is a git submodule or a worktree. + # In both cases .git is a file instead of a directory. + # + if(NOT IS_DIRECTORY ${GIT_DIR}) + # The following git command will return a non empty string that + # points to the super project working tree if the current + # source dir is inside a git submodule. + # Otherwise the command will return an empty string. + # + execute_process( + COMMAND "${GIT_EXECUTABLE}" rev-parse + --show-superproject-working-tree + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + OUTPUT_VARIABLE out + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + if(NOT "${out}" STREQUAL "") + # If out is empty, GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a submodule + file(READ ${GIT_DIR} submodule) + string(REGEX REPLACE "gitdir: (.*)$" "\\1" GIT_DIR_RELATIVE + ${submodule}) + string(STRIP ${GIT_DIR_RELATIVE} GIT_DIR_RELATIVE) + get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH) + get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} + ABSOLUTE) + set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD") + else() + # GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a worktree + file(READ ${GIT_DIR} worktree_ref) + # The .git directory contains a path to the worktree information directory + # inside the parent git repo of the worktree. + # + string(REGEX REPLACE "gitdir: (.*)$" "\\1" git_worktree_dir + ${worktree_ref}) + string(STRIP ${git_worktree_dir} git_worktree_dir) + _git_find_closest_git_dir("${git_worktree_dir}" GIT_DIR) + set(HEAD_SOURCE_FILE "${git_worktree_dir}/HEAD") + endif() + else() + set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD") + endif() + set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data") + if(NOT EXISTS "${GIT_DATA}") + file(MAKE_DIRECTORY "${GIT_DATA}") + endif() + + if(NOT EXISTS "${HEAD_SOURCE_FILE}") + return() + endif() + set(HEAD_FILE "${GIT_DATA}/HEAD") + configure_file("${HEAD_SOURCE_FILE}" "${HEAD_FILE}" COPYONLY) + + configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in" + "${GIT_DATA}/grabRef.cmake" @ONLY) + include("${GIT_DATA}/grabRef.cmake") + + set(${_refspecvar} + "${HEAD_REF}" + PARENT_SCOPE) + set(${_hashvar} + "${HEAD_HASH}" + PARENT_SCOPE) +endfunction() + +function(git_describe _var) + if(NOT GIT_FOUND) + find_package(Git QUIET) + endif() + get_git_head_revision(refspec hash) + if(NOT GIT_FOUND) + set(${_var} + "GIT-NOTFOUND" + PARENT_SCOPE) + return() + endif() + if(NOT hash) + set(${_var} + "HEAD-HASH-NOTFOUND" + PARENT_SCOPE) + return() + endif() + + # TODO sanitize + #if((${ARGN}" MATCHES "&&") OR + # (ARGN MATCHES "||") OR + # (ARGN MATCHES "\\;")) + # message("Please report the following error to the project!") + # message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}") + #endif() + + #message(STATUS "Arguments to execute_process: ${ARGN}") + + execute_process( + COMMAND "${GIT_EXECUTABLE}" describe --tags --always ${hash} ${ARGN} + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + RESULT_VARIABLE res + OUTPUT_VARIABLE out + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + if(NOT res EQUAL 0) + set(out "${out}-${res}-NOTFOUND") + endif() + + set(${_var} + "${out}" + PARENT_SCOPE) +endfunction() + +function(git_describe_working_tree _var) + if(NOT GIT_FOUND) + find_package(Git QUIET) + endif() + if(NOT GIT_FOUND) + set(${_var} + "GIT-NOTFOUND" + PARENT_SCOPE) + return() + endif() + + execute_process( + COMMAND "${GIT_EXECUTABLE}" describe --dirty ${ARGN} + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + RESULT_VARIABLE res + OUTPUT_VARIABLE out + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + if(NOT res EQUAL 0) + set(out "${out}-${res}-NOTFOUND") + endif() + + set(${_var} + "${out}" + PARENT_SCOPE) +endfunction() + +function(git_get_exact_tag _var) + git_describe(out --exact-match ${ARGN}) + set(${_var} + "${out}" + PARENT_SCOPE) +endfunction() + +function(git_local_changes _var) + if(NOT GIT_FOUND) + find_package(Git QUIET) + endif() + get_git_head_revision(refspec hash) + if(NOT GIT_FOUND) + set(${_var} + "GIT-NOTFOUND" + PARENT_SCOPE) + return() + endif() + if(NOT hash) + set(${_var} + "HEAD-HASH-NOTFOUND" + PARENT_SCOPE) + return() + endif() + + execute_process( + COMMAND "${GIT_EXECUTABLE}" diff-index --quiet HEAD -- + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + RESULT_VARIABLE res + OUTPUT_VARIABLE out + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + if(res EQUAL 0) + set(${_var} + "CLEAN" + PARENT_SCOPE) + else() + set(${_var} + "DIRTY" + PARENT_SCOPE) + endif() +endfunction() diff --git a/cmake-modules/GetGitRevisionDescription.cmake.in b/cmake/GetGitRevisionDescription.cmake.in similarity index 100% rename from cmake-modules/GetGitRevisionDescription.cmake.in rename to cmake/GetGitRevisionDescription.cmake.in diff --git a/cmake/Macros.cmake b/cmake/Macros.cmake new file mode 100644 index 00000000..4d423275 --- /dev/null +++ b/cmake/Macros.cmake @@ -0,0 +1,112 @@ +# *************************************************************************** +# This file is part of the GAMer software. +# Copyright (C) 2016-2018 +# by Christopher Lee, John Moody, Rommie Amaro, J. Andrew McCammon, +# and Michael Holst + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# *************************************************************************** + +#[[ +Macro sets the following variables + * `VERSION_SHORT` + * `VERSION_INFO` + * `VERSION_SHA1` + * `VERSION_DIRTY` +#]] +macro(get_version_from_git) + # Look for version from GIT + include(GetGitRevisionDescription) + git_describe_working_tree(VERSION --tags --always) + + # parse the version information into pieces. + string(REGEX MATCH + "^v?(([0-9]+)(\\.?[0-9]+)*)-?(alpha[0-9]*|beta[0-9]*|dev[0-9]*|a[0-9]*|b[0-9]*|c[0-9]*)?-?([0-9]+)?-?([a-z0-9]+)?-?(dirty)?$" + MATCH_RESULT + "${VERSION}" + ) + if(MATCH_RESULT) + # foreach(_TMP RANGE 10) + # message(STATUS "MATCH ${_TMP}: ${CMAKE_MATCH_${_TMP}}") + # endforeach() + set(VERSION_SHORT ${CMAKE_MATCH_1}) + set(VERSION_INFO ${CMAKE_MATCH_4}) + set(VERSION_SHA1 ${CMAKE_MATCH_5}) + set(VERSION_DIRTY ${CMAKE_MATCH_6}) + + set(VERSION_DUMP "${VERSION_SHORT}") + if(VERSION_INFO) + string(CONCAT VERSION_DUMP ${VERSION_DUMP} "-${VERSION_INFO}") + endif() + configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/cmake/VERSION.in + ${CMAKE_CURRENT_SOURCE_DIR}/VERSION + ) + else() + message(STATUS "No GIT VCS found pulling version from file") + file(READ ${CMAKE_CURRENT_SOURCE_DIR}/VERSION VERSION) + string(STRIP "${VERSION}" VERSION) + string(REGEX MATCH + "^v?(([0-9]+)(\\.?[0-9]+)*)-?(alpha[0-9]*|beta[0-9]*|dev[0-9]*|a[0-9]*|b[0-9]*|c[0-9]*)?$" + MATCH_RESULT + "${VERSION}" + ) + # foreach(_TMP RANGE 9) + # message(STATUS "MATCH ${_TMP}: ${CMAKE_MATCH_${_TMP}}") + # endforeach() + if(MATCH_RESULT) + set(VERSION_SHORT ${CMAKE_MATCH_1}) + if(CMAKE_MATCH_4) + set(VERSION_INFO ${CMAKE_MATCH_4}) + endif() + endif() + endif() + +endmacro(get_version_from_git) + + +macro(print_debug_messages) + message(DEBUG "CMAKE_C_FLAGS is: ${CMAKE_C_FLAGS}") + message(DEBUG "CMAKE_C_FLAGS_DEBUG is: ${CMAKE_C_FLAGS_DEBUG}") + message(DEBUG "CMAKE_C_FLAGS_RELEASE is: ${CMAKE_C_FLAGS_RELEASE}") + message(DEBUG "CMAKE_C_FLAGS_RELWITHDEBINFO is: ${CMAKE_C_FLAGS_RELWITHDEBINFO}") + message(DEBUG "CMAKE_C_FLAGS_MINSIZEREL is: ${CMAKE_C_FLAGS_MINSIZEREL}") + message(DEBUG "CMAKE_CXX_FLAGS is: ${CMAKE_CXX_FLAGS}") + message(DEBUG "CMAKE_CXX_FLAGS_DEBUG is: ${CMAKE_CXX_FLAGS_DEBUG}") + message(DEBUG "CMAKE_CXX_FLAGS_RELEASE is: ${CMAKE_CXX_FLAGS_RELEASE}") + message(DEBUG "CMAKE_CXX_FLAGS_RELWITHDEBINFO is: ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") + message(DEBUG "CMAKE_CXX_FLAGS_MINSIZEREL is: ${CMAKE_CXX_FLAGS_MINSIZEREL}") + message(DEBUG "Build type: ${CMAKE_BUILD_TYPE}") + message(DEBUG "CMAKE_VERBOSE_MAKEFILE: " ${CMAKE_VERBOSE_MAKEFILE}) + message(DEBUG "BUILD_SHARED_LIBS: " ${BUILD_SHARED_LIBS}) +endmacro(print_debug_messages) + + +macro(set_default_build_type) + # Set default build type + set(CMAKE_BUILD_TYPE_INIT Release) + + # Set a default build type if none was specified + set(default_build_type "Release") + + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + message(STATUS "Setting build type to '${default_build_type}' as none was specified.") + set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE + STRING "Choose the type of build." FORCE) + # Set the possible values of build type for cmake-gui + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" + "MinSizeRel" "RelWithDebInfo") + endif() +endmacro(set_default_build_type) diff --git a/cmake-modules/VERSION.in b/cmake/VERSION.in similarity index 100% rename from cmake-modules/VERSION.in rename to cmake/VERSION.in diff --git a/cmake/__init__.py.in b/cmake/__init__.py.in new file mode 100644 index 00000000..0ca72af7 --- /dev/null +++ b/cmake/__init__.py.in @@ -0,0 +1,17 @@ +def __bootstrap__(): + global __bootstrap__, __loader__, __file__ + import sys, importlib.util + try: + import importlib.resources as pkg_resources + except ImportError: + # Try backported to PY<37 `importlib_resources`. + import importlib_resources as pkg_resources + # delete junk introduced + __loader__ = None; del __bootstrap__, __loader__ + # get path to shared extension library + with pkg_resources.path(__name__, "$") as __file__: + # load the module + spec = importlib.util.spec_from_file_location(__name__, __file__) + mod = importlib.util.module_from_spec(spec) + spec.loader.exec_module(mod) +__bootstrap__() diff --git a/cmake-modules/blenderexec.py.in b/cmake/blenderexec.py.in similarity index 100% rename from cmake-modules/blenderexec.py.in rename to cmake/blenderexec.py.in diff --git a/cmake-modules/c_flag_overrides.cmake b/cmake/c_flag_overrides.cmake similarity index 100% rename from cmake-modules/c_flag_overrides.cmake rename to cmake/c_flag_overrides.cmake diff --git a/cmake-modules/cxx_flag_overrides.cmake b/cmake/cxx_flag_overrides.cmake similarity index 100% rename from cmake-modules/cxx_flag_overrides.cmake rename to cmake/cxx_flag_overrides.cmake diff --git a/cmake-modules/version.cpp.in b/cmake/version.cpp.in similarity index 100% rename from cmake-modules/version.cpp.in rename to cmake/version.cpp.in diff --git a/docs/conf.py.in b/docs/conf.py.in index d1a06e92..72f11179 100644 --- a/docs/conf.py.in +++ b/docs/conf.py.in @@ -22,8 +22,8 @@ _on_rtd = os.environ.get('READTHEDOCS', None) == 'True' # -- Project information ----------------------------------------------------- project = '@CMAKE_PROJECT_NAME@' -copyright = '2019, Christopher T. Lee' -author = 'Christopher T. Lee' +copyright = '2019-2021, Christopher T. Lee and Contributors' +author = 'Christopher T. Lee and Contributors' version = '@VERSION_SHORT@' release = '@VERSION@' @@ -40,7 +40,7 @@ extensions = [ 'sphinx.ext.intersphinx', 'sphinx_issues', 'nbsphinx', - 'jupyter_sphinx.execute', + 'jupyter_sphinx', ] # Github repo @@ -152,4 +152,4 @@ html_context = { # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = {'python': ('https://docs.python.org/3', None), - 'numpy': ('http://docs.scipy.org/doc/numpy', None)} + 'numpy': ('https://numpy.org/doc/stable/', None)} diff --git a/docs/rtd-requirements.txt b/docs/rtd-requirements.txt index 3cdf04d0..8774a563 100644 --- a/docs/rtd-requirements.txt +++ b/docs/rtd-requirements.txt @@ -1,21 +1,20 @@ # Required for building -cmake >= 3.12 -scikit-build >= 0.10.0 -setuptools -wheel +cmake +scikit-build numpy # Recommended -pytest +pytest==6.2.3 # For building documentation -sphinx>=2.0 -breathe>=4.13.0 -exhale -pandoc -sphinx-issues -nbsphinx -jupyter_sphinx +sphinx==4.0.2 +docutils==0.16 +breathe==4.30.0 +exhale==0.2.3 +# pandoc +sphinx-issues==1.2.0 +nbsphinx==0.8.5 +jupyter_sphinx==0.3.1 # Required to run FEniCS notebooks # dolfin diff --git a/docs/src/conf.py b/docs/src/conf.py deleted file mode 100644 index c4804f0f..00000000 --- a/docs/src/conf.py +++ /dev/null @@ -1,155 +0,0 @@ -# Configuration file for the Sphinx documentation builder. -# -# This file only contains a selection of the most common options. For a full -# list see the documentation: -# http://www.sphinx-doc.org/en/master/config - -# -- Path setup -------------------------------------------------------------- - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. - -# Add the local lib to path if desired -# import sys -# sys.path.insert(0, os.path.abspath('/Users/ctlee/gamer/gamer/build2.82/lib/')) - -import os -import pygamer - -_on_rtd = os.environ.get('READTHEDOCS', None) == 'True' - -# -- Project information ----------------------------------------------------- - -project = 'GAMer' -copyright = '2019, Christopher T. Lee' -author = 'Christopher T. Lee' - -version = '2.0.6' -release = 'v2.0.6' - -# -- General configuration --------------------------------------------------- - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.autosummary', - 'sphinx.ext.napoleon', - 'sphinx.ext.intersphinx', - 'sphinx_issues', - 'nbsphinx', - 'jupyter_sphinx.execute', -] - -# Github repo -issues_github_path = 'ctlee/gamer' - -if(True): - extensions.extend(['breathe', 'exhale']) - -############################## -# Breathe Settings -############################## -breathe_projects = { "gamer_project": "/Users/ctlee/gamer/gamer/docs/src/_doxyoutput/xml" } -breathe_default_project = "gamer_project" - -############################## -# Exhale Settings -############################## - -doxystdin = \ -""" -INPUT = /Users/ctlee/gamer/gamer/include -OPTIMIZE_OUTPUT_FOR_C = YES -EXTRACT_ALL = YES -""" -# ENABLED_SECTIONS = detail - -exhale_args = { - "containmentFolder": "/Users/ctlee/gamer/gamer/docs/src/_cppapi", - "rootFileName": "root.rst", - "rootFileTitle": "C++ API Reference", - "doxygenStripFromPath": "/Users/ctlee/gamer/gamer", - # "pageLevelConfigMeta": ":github_url: https://github.com/ctlee/gamer", - "createTreeView": True, - "exhaleExecutesDoxygen": True, - "exhaleDoxygenStdin": doxystdin, -} - -############################## -# Number figures -############################## - -numfig = True -numfig_secnum_depth = (2) - -############################## -# Autosummary Settings -############################## - -autosummary_generate = True -# autodoc_default_flags = ['members', 'inherited-members'] - -############################## -# Napoleon Settings -############################## -napoleon_google_docstring = True -napoleon_numpy_docstring = True -napoleon_include_init_with_doc = True -napoleon_include_private_with_doc = False -napoleon_include_special_with_doc = True -napoleon_use_admonition_for_examples = False -napoleon_use_admonition_for_notes = False -napoleon_use_admonition_for_references = False -napoleon_use_ivar = True -napoleon_use_param = True -napoleon_use_rtype = True - -# If true, '()' will be appended to :func: etc. cross-reference text. -add_function_parentheses = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -show_authors = True - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['/Users/ctlee/gamer/gamer/docs/src/_templates'] - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This pattern also affects html_static_path and html_extra_path. -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', '_templates', '**.ipynb_checkpoints'] - - -############################## -# HTML Output Settings -############################## - -# Try to load sphinx_rtd_theme otherwise fallback on default -try: - import sphinx_rtd_theme - html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] - html_theme = 'sphinx_rtd_theme' -except ImportError: - html_theme = 'default' - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['/Users/ctlee/gamer/gamer/docs/src/_static'] - -html_context = { - 'css_files': [ - '_static/theme_overrides.css', # override wide tables in RTD theme - ], - } - -############################## -# Intersphinx Settings -############################## - -# Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {'python': ('https://docs.python.org/3', None), - 'numpy': ('http://docs.scipy.org/doc/numpy', None)} diff --git a/docs/src/install.rst b/docs/src/install.rst index d77ba13d..e480895a 100644 --- a/docs/src/install.rst +++ b/docs/src/install.rst @@ -147,7 +147,7 @@ These can be used in addition to the standard `CMake flags`_. * - Build the ``PyGAMer`` extension. - ``-DBUILD_PYGAMER=on`` * - Specify the Python executable path. - - ``-DPYTHON_EXECUTABLE:FILEPATH=/path/to/python3`` + - ``-DPython_EXECUTABLE:FILEPATH=/path/to/python3`` * - Package the Blender addon. This flag automatically builds the Python extension. - ``-DBUILD_BLENDGAMER=on`` * - Use single precision floating point numbers. @@ -318,7 +318,8 @@ Note that the prebuilt Blender binaries from the Blender Foundation do not conta Blender Version Python Version =============== ============== 2.79b 3.5 - 2.8X 3.7 + 2.83LTS 3.7.4 + 2.93 3.9.1 =============== ============== Create a new environment corresponding to the Python version. @@ -425,4 +426,4 @@ It is also possible to compile the documentation using CMake by building target .. note:: If you are getting a module import error, this is indicative that Python cannot find an installed copy of ``PyGAMer`` to retrieve docstrings from. You can either manually append the location of the ``PyGAMer`` extension module to the PYTHONPATH in ``docs/conf.py.in``. - Alternatively you can install ``PyGAMer`` in a more conventional location. \ No newline at end of file + Alternatively you can install ``PyGAMer`` in a more conventional location. diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt new file mode 100644 index 00000000..cfd0337e --- /dev/null +++ b/include/CMakeLists.txt @@ -0,0 +1,37 @@ +# *************************************************************************** +# This file is part of the GAMer software. +# Copyright (C) 2016-2021 +# by Christopher T. Lee and contributors + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, see +# or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, +# Boston, MA 02111-1307 USA +# *************************************************************************** + + +set(GAMER_HEADERS + "${CMAKE_CURRENT_SOURCE_DIR}/gamer/gamer" + "${CMAKE_CURRENT_SOURCE_DIR}/gamer/SurfaceMesh.h" + "${CMAKE_CURRENT_SOURCE_DIR}/gamer/TetMesh.h" + "${CMAKE_CURRENT_SOURCE_DIR}/gamer/EigenDiagonalization.h" + "${CMAKE_CURRENT_SOURCE_DIR}/gamer/MarchingCube.h" + "${CMAKE_CURRENT_SOURCE_DIR}/gamer/OsculatingJets.h" + "${CMAKE_CURRENT_SOURCE_DIR}/gamer/PDBReader.h" + "${CMAKE_CURRENT_SOURCE_DIR}/gamer/Vertex.h" + "${CMAKE_CURRENT_SOURCE_DIR}/gamer/gamer.h" + "${CMAKE_CURRENT_SOURCE_DIR}/gamer/stringutil.h" + "${CMAKE_CURRENT_SOURCE_DIR}/gamer/tensor.h" + "${CMAKE_CURRENT_SOURCE_DIR}/gamer/version.h" + "${CMAKE_CURRENT_SOURCE_DIR}/gamer/visibility_utils.h" +PARENT_SCOPE) diff --git a/include/gamer/EigenDiagonalization.h b/include/gamer/EigenDiagonalization.h index 2846588f..34d7176e 100644 --- a/include/gamer/EigenDiagonalization.h +++ b/include/gamer/EigenDiagonalization.h @@ -1,27 +1,21 @@ - -/* - * *************************************************************************** - * This file is part of the GAMer software. - * Copyright (C) 2016-2018 - * by Christopher Lee, John Moody, Rommie Amaro, J. Andrew McCammon, - * and Michael Holst - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * *************************************************************************** - */ +// This file is part of the GAMer software. +// Copyright (C) 2016-2021 +// by Christopher T. Lee and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, see +// or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA #pragma once @@ -30,93 +24,83 @@ #include "gamer.h" +namespace gamer { -namespace gamer -{ - -template -class EigenDiagonalizeTraits -{ +template class EigenDiagonalizeTraits { public: -using CovarianceMatrix = std::array; + using CovarianceMatrix = std::array; private: -/// Eigen 3x3 Matrix of type REAL -using _EigenMatrix = Eigen::Matrix; -/// Eigen 3 Vector of type REAL -using _EigenVector = Eigen::Matrix; - -/** - * @brief Construct a covariance matrix - */ -static _EigenMatrix construct_covariance_matrix(const CovarianceMatrix &cov) -{ + /// Eigen 3x3 Matrix of type REAL + using _EigenMatrix = Eigen::Matrix; + /// Eigen 3 Vector of type REAL + using _EigenVector = Eigen::Matrix; + + /** + * @brief Construct a covariance matrix + */ + static _EigenMatrix construct_covariance_matrix(const CovarianceMatrix &cov) { _EigenMatrix m; - for (std::size_t i = 0; i < dim; ++i) - { - for (std::size_t j = i; j < dim; ++j) - { - m(i, j) = static_cast(cov[(dim * i) + j - ( (i * (i + 1) ) / 2)]); + for (std::size_t i = 0; i < dim; ++i) { + for (std::size_t j = i; j < dim; ++j) { + m(i, j) = static_cast(cov[(dim * i) + j - ((i * (i + 1)) / 2)]); - if (i != j) - m(j, i) = m(i, j); - } + if (i != j) + m(j, i) = m(i, j); + } } return m; -} + } public: -/** - * @brief Diagonalize a Self Adjoint Matrix - * - * @param[in] mat Self adjoint matrix to diagonalize - * @param[out] eigenvalues Resulting eigenvalues - * @param[out] eigenvectors Resulting eigenvectors - * - * @return True on success - */ -static bool diagonalizeSelfAdjointMatrix(const _EigenMatrix &mat, - _EigenVector &eigenvalues, - _EigenMatrix &eigenvectors ) -{ + /** + * @brief Diagonalize a Self Adjoint Matrix + * + * @param[in] mat Self adjoint matrix to diagonalize + * @param[out] eigenvalues Resulting eigenvalues + * @param[out] eigenvectors Resulting eigenvectors + * + * @return True on success + */ + static bool diagonalizeSelfAdjointMatrix(const _EigenMatrix &mat, + _EigenVector &eigenvalues, + _EigenMatrix &eigenvectors) { Eigen::SelfAdjointEigenSolver<_EigenMatrix> eigensolver; if (dim == 2 || dim == 3) - eigensolver.computeDirect(mat); + eigensolver.computeDirect(mat); else - eigensolver.compute(mat); // More accurate but slower - - if (eigensolver.info() != Eigen::Success) - { - return false; - // std::stringstream ss; - // ss << "getEigenvalues has encountered Eigen error " << - // eigensolver.info(); - // throw std::runtime_error(ss.str()); + eigensolver.compute(mat); // More accurate but slower + + if (eigensolver.info() != Eigen::Success) { + return false; + // std::stringstream ss; + // ss << "getEigenvalues has encountered Eigen error " << + // eigensolver.info(); + // gamer_runtime_error(ss.str()); } - eigenvalues = eigensolver.eigenvalues(); + eigenvalues = eigensolver.eigenvalues(); eigenvectors = eigensolver.eigenvectors(); return true; -} - -/** - * @brief Diagonalize an upper triangular covariance matrix, - * - * @param[in] cov Upper triangular covariance matrix - * @param[out] eigenvalues Resulting eigenvalues - * @param[out] eigenvectors Resulting eigenvectors - * - * @return True on success - */ -static bool diagonalizeSelfAdjointCovMatrix(const CovarianceMatrix &cov, - _EigenVector &eigenvalues, - _EigenMatrix &eigenvectors) -{ - _EigenMatrix m = construct_covariance_matrix(cov); + } + + /** + * @brief Diagonalize an upper triangular covariance matrix, + * + * @param[in] cov Upper triangular covariance matrix + * @param[out] eigenvalues Resulting eigenvalues + * @param[out] eigenvectors Resulting eigenvectors + * + * @return True on success + */ + static bool diagonalizeSelfAdjointCovMatrix(const CovarianceMatrix &cov, + _EigenVector &eigenvalues, + _EigenMatrix &eigenvectors) { + _EigenMatrix m = construct_covariance_matrix(cov); bool res = diagonalizeSelfAdjointMatrix(m, eigenvalues, eigenvectors); return res; -} + } }; // end class EigenDiagonalizeTraits -} // end namespace gamer +} // end namespace gamer diff --git a/include/gamer/MarchingCube.h b/include/gamer/MarchingCube.h index 73918140..61b20dab 100644 --- a/include/gamer/MarchingCube.h +++ b/include/gamer/MarchingCube.h @@ -1,26 +1,22 @@ -/* - * *************************************************************************** - * This file is part of the GAMer software. - * Copyright (C) 2016-2018 - * by Christopher Lee, John Moody, Rommie Amaro, J. Andrew McCammon, - * and Michael Holst - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * *************************************************************************** - */ +// This file is part of the GAMer software. +// Copyright (C) 2016-2021 +// by Christopher T. Lee and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, see +// or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA + /** * @file MarchingCube.h diff --git a/include/gamer/OsculatingJets.h b/include/gamer/OsculatingJets.h index e977b07e..372c1bff 100644 --- a/include/gamer/OsculatingJets.h +++ b/include/gamer/OsculatingJets.h @@ -1,39 +1,35 @@ -/***************************************************************************** - * This file is part of the GAMer software. - * Copyright (C) 2019 - * by Christopher Lee, John Moody, Rommie Amaro, J. Andrew McCammon, - * and Michael Holst - * - * Copyright (c) 2007 INRIA Sophia-Antipolis (France), INRIA Lorraine LORIA. - * all rights reserved. - * - * You can redistribute it and/or modify it under the terms of the GNU - * General Public License as published by the Free SoREALware Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Licensees holding a valid commercial license may use this file in - * accordance with the commercial license agreement provided with the - * software. - * - * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE - * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. - * - * Author(s) : Marc Pouget and Frédéric Cazals - * Christopher T. Lee : Adapted from CGAL for GAMER - * *************************************************************************** - */ + +// This file is part of the GAMer software. +// Copyright (C) 2016-2021 +// by Christopher T. Lee and contributors +// +// Copyright (c) 2007 INRIA Sophia-Antipolis (France), INRIA Lorraine LORIA. +// all rights reserved. +// +// You can redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// Licensees holding a valid commercial license may use this file in +// accordance with the commercial license agreement provided with the +// software. +// +// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Author(s) : Marc Pouget and Frédéric Cazals +// Christopher T. Lee : Adapted from CGAL for GAMER #pragma once #include #include -#include "gamer/gamer.h" #include "gamer/EigenDiagonalization.h" +#include "gamer/gamer.h" -namespace gamer -{ +namespace gamer { /** * @brief Iterative computation of factorial @@ -42,852 +38,792 @@ namespace gamer * * @return Value of the factorial */ -inline unsigned int fact(unsigned int n) -{ - unsigned int i, p = 1; - for (i = 2; i <= n; i++) - p *= i; - return p; +inline unsigned int fact(unsigned int n) { + unsigned int i, p = 1; + for (i = 2; i <= n; i++) + p *= i; + return p; } /** * @brief This class describes a monge via jet fitting. */ -class Monge_via_jet_fitting -{ +class Monge_via_jet_fitting { +public: + /** + * @brief Representation of Monge parameterization + */ + class MongeForm { + protected: + Vector m_origin_pt; /// Origin of parameterization + Vector m_d1; /// Maximal principal direction + Vector m_d2; /// Minimal principal direction + Vector m_n; /// Normal directions + /// Vector of differential values + std::vector m_coefficients; + // coeff = (k1, k2, //ppal curv + // b0, b1, b2, b3, //third order + // c0, c1, c2, c3, c4) //fourth order public: /** - * @brief Representation of Monge parameterization + * @brief Default constructor */ - class MongeForm - { - protected: - Vector m_origin_pt; /// Origin of parameterization - Vector m_d1; /// Maximal principal direction - Vector m_d2; /// Minimal principal direction - Vector m_n; /// Normal directions - /// Vector of differential values - std::vector m_coefficients; - // coeff = (k1, k2, //ppal curv - // b0, b1, b2, b3, //third order - // c0, c1, c2, c3, c4) //fourth order - public: - /** - * @brief Default constructor - */ - MongeForm(std::size_t degree) - { - m_origin_pt = Vector({0., 0., 0.}); - m_d1 = Vector({0., 0., 0.}); - m_d2 = Vector({0., 0., 0.}); - m_n = Vector({0., 0., 0.}); - m_coefficients = std::vector(); - - //if d>=2, number of coeffs = (d+1)(d+2)/2-4. - //we remove cst, linear and the xy coeff which vanish - if (degree >= 2) - std::fill_n(back_inserter(m_coefficients), - (degree + 1) * (degree + 2) / 2 - 4, 0.); - } - - /** - * @brief Destroys the object. - */ - ~MongeForm() { - } - - /** - * @brief Get the origin - * - * @return Vector origin - */ - const Vector origin() const { - return m_origin_pt; - } - - /** - * @brief Get the origin - * - * @return Vector origin - */ - Vector &origin() { - return m_origin_pt; - } - - /** - * @brief Get the max principal direction - * - * @return Vector max principal direction - */ - const Vector maximal_principal_direction() const { - return m_d1; - } - - /** - * @brief Get the max principal direction - * - * @return Vector max principal direction - */ - Vector &maximal_principal_direction() { - return m_d1; - } - - /** - * @brief Get the min principal direction - * - * @return Vector min principal direction - */ - const Vector minimal_principal_direction() const { - return m_d2; - } - - /** - * @brief Get the min principal direction - * - * @return Vector min principal direction - */ - Vector &minimal_principal_direction() { - return m_d2; - } - - /** - * @brief Get the normal direction - * - * @return Vector normal direction - */ - const Vector normal_direction() const { - return m_n; - } - - /** - * @brief Get the normal direction - * - * @return Vector normal direction - */ - Vector &normal_direction() { - return m_n; - } + MongeForm(std::size_t degree) { + m_origin_pt = Vector({0., 0., 0.}); + m_d1 = Vector({0., 0., 0.}); + m_d2 = Vector({0., 0., 0.}); + m_n = Vector({0., 0., 0.}); + m_coefficients = std::vector(); + + // if d>=2, number of coeffs = (d+1)(d+2)/2-4. + // we remove cst, linear and the xy coeff which vanish + if (degree >= 2) + std::fill_n(back_inserter(m_coefficients), + (degree + 1) * (degree + 2) / 2 - 4, 0.); + } - /** - * @brief Access coefficients - * - * @return Coefficients - */ - const std::vector coefficients() const { - return m_coefficients; - } + /** + * @brief Destroys the object. + */ + ~MongeForm() {} - /** - * @brief Access coefficients - * - * @return Coefficients - */ - std::vector &coefficients() { - return m_coefficients; - } + /** + * @brief Get the origin + * + * @return Vector origin + */ + const Vector origin() const { return m_origin_pt; } - /** - * @brief Access principal curvature values - * - * @param[in] i Index - * - * @return Value of the curvature - */ - const REAL principal_curvatures(size_t i) const - { - if ((i == 0 || i == 1) && coefficients().size() >= 2) - { - return coefficients()[i]; - } - else - { - throw std::runtime_error("Index out of bounds for principal curvatures."); - } - } + /** + * @brief Get the origin + * + * @return Vector origin + */ + Vector &origin() { return m_origin_pt; } - /** - * @brief Access third order differential properties - * - * @param[in] i Index - * - * @return Values - */ - const REAL third_order_coefficients(size_t i) const - { - if (i <= 3 && coefficients().size() >= 6) - { - return coefficients()[i+2]; - } - else - { - throw std::runtime_error("Index out of bounds for third order coefficients."); - } - } + /** + * @brief Get the max principal direction + * + * @return Vector max principal direction + */ + const Vector maximal_principal_direction() const { return m_d1; } - /** - * @brief Access fourth order differential properties - * - * @param[in] i Index - * - * @return Values - */ - const REAL fourth_order_coefficients(size_t i) const - { - if (i <= 4 && coefficients().size() >= 11) - { - return coefficients()[i+6]; - } - else - { - throw std::runtime_error("Index out of bounds for fourth order coefficients."); - } - } + /** + * @brief Get the max principal direction + * + * @return Vector max principal direction + */ + Vector &maximal_principal_direction() { return m_d1; } - //switch min-max ppal curv/dir wrt a given normal orientation. - // if given_normal.monge_normal < 0 then change the orientation - // if z=g(x,y) in the basis (d1,d2,n) then in the basis - // (d2,d1,-n) - // z=h(x,y)=-g(y,x) - void comply_wrt_given_normal(const Vector &given_normal){ - if (dot(given_normal, normal_direction()) < 0) - { - //normal_direction() = -normal_direction(); - m_n = -m_n; - // std::swap(maximal_principal_direction(), - // minimal_principal_direction()); - std::swap(m_d1, m_d2); - if (m_coefficients.size() >= 2) - std::swap(m_coefficients[0], m_coefficients[1]); - if (m_coefficients.size() >= 6) - { - std::swap(m_coefficients[2], m_coefficients[5]); - std::swap(m_coefficients[3], m_coefficients[4]); - } - if (m_coefficients.size() >= 11) - { - std::swap(m_coefficients[6], m_coefficients[10]); - std::swap(m_coefficients[7], m_coefficients[9]); - } - typename std::vector::iterator itb = m_coefficients.begin(), - ite = m_coefficients.end(); - for (; itb != ite; itb++) - { - *itb = -(*itb); - } - } - } + /** + * @brief Get the min principal direction + * + * @return Vector min principal direction + */ + const Vector minimal_principal_direction() const { return m_d2; } - void dump_verbose(std::ostream &out_stream) const { - out_stream << "origin : " << origin() << std::endl - << "n : " << normal_direction() << std::endl; - if (coefficients().size() >= 2) - out_stream << "d1 : " << maximal_principal_direction() << std::endl - << "d2 : " << minimal_principal_direction() << std::endl - << "k1 : " << coefficients()[0] << std::endl - << "k2 : " << coefficients()[1] << std::endl; - if (coefficients().size() >= 6) - out_stream << "b0 : " << coefficients()[2] << std::endl - << "b1 : " << coefficients()[3] << std::endl - << "b2 : " << coefficients()[4] << std::endl - << "b3 : " << coefficients()[5] << std::endl; - if (coefficients().size() >= 11) - out_stream << "c0 : " << coefficients()[6] << std::endl - << "c1 : " << coefficients()[7] << std::endl - << "c2 : " << coefficients()[8] << std::endl - << "c3 : " << coefficients()[9] << std::endl - << "c4 : " << coefficients()[10] << std::endl - << std::endl; - } - void dump_4ogl(std::ostream &out_stream, const REAL scale){ - if (coefficients().size() < 2) - throw std::runtime_error("Insufficient coefficients"); - out_stream << origin() << " " - << maximal_principal_direction() * scale << " " - << minimal_principal_direction() * scale << " " - << coefficients()[0] << " " - << coefficients()[1] << " " - << std::endl; - } - }; // end nested class MongeForm + /** + * @brief Get the min principal direction + * + * @return Vector min principal direction + */ + Vector &minimal_principal_direction() { return m_d2; } - /// Default constructor - Monge_via_jet_fitting(){ - m_pca_basis = std::vector< std::pair >(3); - } + /** + * @brief Get the normal direction + * + * @return Vector normal direction + */ + const Vector normal_direction() const { return m_n; } /** - * @brief Function call + * @brief Get the normal direction * - * @param[in] begin Iterator to first vertex and origin of fitting - * @param[in] end Iterator just past the end - * @param[in] dJet Order of jet fitting - * @param dPrime Order of differentials to compute + * @return Vector normal direction + */ + Vector &normal_direction() { return m_n; } + + /** + * @brief Access coefficients * - * @tparam InputIterator Typename of iterator + * @return Coefficients + */ + const std::vector coefficients() const { return m_coefficients; } + + /** + * @brief Access coefficients * - * @return Monge form at vertex + * @return Coefficients */ - template - MongeForm operator()(InputIterator begin, InputIterator end, - std::size_t dJet, std::size_t dPrime){ - // Precondition verifying that the differential - if (!((dJet >= 1) && (dPrime >= 1) && (dPrime <= 4) && (dPrime <= dJet))) { - std::stringstream ss; - ss << "Cannot compute " << dPrime - << "-order differential property using " - << dJet << "-jet."; - throw std::runtime_error(ss.str()); - } - // Degree of jet fitting - deg = static_cast(dJet); - // Degree of differential to compute - deg_monge = static_cast(dPrime); - // Number of points required to fit d-jet - nb_d_jet_coeff = static_cast((dJet+1)*(dJet+2)/2); - // Number of input points - nb_input_pts = static_cast(end - begin); - // Check that enough points are given to fit d-jet - if (nb_input_pts < nb_d_jet_coeff) - throw std::runtime_error("Insufficient points provided to perform jet fitting."); - - // Initialize MongeForm - MongeForm monge_form(dPrime); - - // Assemble the linear system - EigenMatrixN M(nb_input_pts, nb_d_jet_coeff); - EigenVectorN Z(nb_input_pts); - - // Compute - compute_PCA(begin, end); - fill_matrix(begin, end, dJet, M, Z); //with precond - - // std::cout << "M:" << std::endl << M << std::endl; - // std::cout << "Z:" << std::endl << Z << std::endl; - // Solve MA=Z in the ls sense. The solution A is stored in Z. - Eigen::JacobiSVD> jacobiSvd(M, Eigen::ComputeThinU | Eigen::ComputeThinV); - Z = jacobiSvd.solve(EigenVectorN(Z)); - condition_nb = jacobiSvd.singularValues().array().abs().maxCoeff() / - jacobiSvd.singularValues().array().abs().minCoeff(); - - for (int k = 0; k <= deg; k++) - for (int i = 0; i <= k; i++) - Z(k*(k+1)/2+i) /= std::pow(preconditionning, k); - - compute_Monge_basis(Z.data(), monge_form); - if (dPrime >= 3) - compute_Monge_coefficients(Z.data(), dPrime, monge_form); - return monge_form; - } + std::vector &coefficients() { return m_coefficients; } /** - * @brief Access to condition number + * @brief Access principal curvature values * - * @return Condition number + * @param[in] i Index + * + * @return Value of the curvature */ - const REAL condition_number() const { - return condition_nb; + const REAL principal_curvatures(size_t i) const { + if ((i == 0 || i == 1) && coefficients().size() >= 2) { + return coefficients()[i]; + } else { + gamer_runtime_error( + "Index out of bounds for principal curvatures."); + } } /** - * @brief Get the PCA basis + * @brief Access third order differential properties * * @param[in] i Index * - * @return PCA basis vector + * @return Values */ - const std::pair pca_basis(std::size_t i) const - { - if (i >= 3) throw std::runtime_error("Out of bounds for PCA basis..."); - return m_pca_basis[i]; + const REAL third_order_coefficients(size_t i) const { + if (i <= 3 && coefficients().size() >= 6) { + return coefficients()[i + 2]; + } else { + gamer_runtime_error( + "Index out of bounds for third order coefficients."); + } } - protected: - int deg; - int deg_monge; - int nb_d_jet_coeff; - int nb_input_pts; - REAL preconditionning; - REAL condition_nb; - - std::vector< std::pair > m_pca_basis; - - /// Translate the points such that p0 (the first point) is at the origin - Eigen::Affine3d p02origin; - /// Rotate local orientation to PCA - Eigen::Affine3d world2pca; - /// Transform from PCA fitting to Monge - Eigen::Affine3d pca2monge; - /** - * @brief Compute PCA of points + * @brief Access fourth order differential properties * - * @param[in] begin Iterator to origin point - * @param[in] end Iterator to just past the end. + * @param[in] i Index * - * @tparam InputIterator Typename of iterator + * @return Values */ - template - void compute_PCA(InputIterator begin, InputIterator end){ - int n = nb_input_pts; - REAL x, y, z, - sumX = 0., sumY = 0., sumZ = 0., - sumX2 = 0., sumY2 = 0., sumZ2 = 0., - sumXY = 0., sumXZ = 0., sumYZ = 0., - xx, yy, zz, xy, xz, yz; - - for (; begin != end; begin++) - { - Vector lp = (**begin).position; - x = lp[0]; - y = lp[1]; - z = lp[2]; - sumX += x / n; - sumY += y / n; - sumZ += z / n; - sumX2 += x * x / n; - sumY2 += y * y / n; - sumZ2 += z * z / n; - sumXY += x * y / n; - sumXZ += x * z / n; - sumYZ += y * z / n; - } - xx = sumX2 - sumX * sumX; - yy = sumY2 - sumY * sumY; - zz = sumZ2 - sumZ * sumZ; - xy = sumXY - sumX * sumY; - xz = sumXZ - sumX * sumZ; - yz = sumYZ - sumY * sumZ; - - // assemble covariance matrix as a - // semi-definite matrix. - // Matrix numbering: - // 0 1 2 - // 3 4 - // 5 - std::array covariance = {{ xx, xy, xz, yy, yz, zz }}; - EigenVector eigenvalues; - EigenMatrix eigenvectors; - - // solve for eigenvalues and eigenvectors. - // eigen values are sorted in ascending order, - // eigen vectors are sorted in accordance. - EigenDiagonalizeTraits::diagonalizeSelfAdjointCovMatrix - (covariance, eigenvalues, eigenvectors); - // std::cout << "PCA Eigenvalues: " << std::endl - // << eigenvalues << std::endl; - // std::cout << "PCA Eigenvectors: " << std::endl - // << eigenvectors << std::endl; - - // Store eigenvalues in m_pca_basis - for (int i = 0; i < 3; i++) - { - m_pca_basis[i].first = eigenvalues[2-i]; - } - - Vector v1({eigenvectors(6), eigenvectors(7), eigenvectors(8)}); - m_pca_basis[0].second = v1; - Vector v2({eigenvectors(3), eigenvectors(4), eigenvectors(5)}); - m_pca_basis[1].second = v2; - Vector v3({eigenvectors(0), eigenvectors(1), eigenvectors(2)}); - m_pca_basis[2].second = v3; - - switch_to_direct_orientation(m_pca_basis[0].second, - m_pca_basis[1].second, - m_pca_basis[2].second); - - EigenMatrix tmp; - tmp << m_pca_basis[0].second[0], m_pca_basis[0].second[1], m_pca_basis[0].second[2], - m_pca_basis[1].second[0], m_pca_basis[1].second[1], m_pca_basis[1].second[2], - m_pca_basis[2].second[0], m_pca_basis[2].second[1], m_pca_basis[2].second[2]; - - //Store the change of basis W->F - Eigen::Affine3d change_basis(tmp); - world2pca = change_basis; + const REAL fourth_order_coefficients(size_t i) const { + if (i <= 4 && coefficients().size() >= 11) { + return coefficients()[i + 6]; + } else { + gamer_runtime_error( + "Index out of bounds for fourth order coefficients."); + } } - //Coordinates of input points are computed in the fitting basis with - // p0 as origin. - //Preconditionning is computed, M and Z are filled - template - void fill_matrix(InputIterator begin, InputIterator end, - std::size_t d, EigenMatrixN &M, EigenVectorN &Z) - { - //origin of fitting coord system = first input data point - Vector point0 = (**begin).position; - // std::cout << "point0: " << point0 << std::endl; - //transform coordinates of sample points with a - //translation ($-p$) and multiplication by $ P_{W\rightarrow F}$. - Vector orig({0., 0., 0.}); - Vector v_point0_orig(orig - point0); - // std::cout << "v_point0_orig: " << v_point0_orig << std::endl; - - p02origin = Eigen::Translation3d(v_point0_orig); - Eigen::Affine3d transf_points = world2pca * - p02origin; - - //compute and store transformed points - std::vector pts_in_fitting_basis; - pts_in_fitting_basis.reserve(nb_input_pts); - - for(auto it = begin; it != end; ++it) { - Vector cur_pt = (**it).position; - cur_pt = transf_points*EigenMap(cur_pt); - pts_in_fitting_basis.push_back(cur_pt); + // switch min-max ppal curv/dir wrt a given normal orientation. + // if given_normal.monge_normal < 0 then change the orientation + // if z=g(x,y) in the basis (d1,d2,n) then in the basis + // (d2,d1,-n) + // z=h(x,y)=-g(y,x) + void comply_wrt_given_normal(const Vector &given_normal) { + if (dot(given_normal, normal_direction()) < 0) { + // normal_direction() = -normal_direction(); + m_n = -m_n; + // std::swap(maximal_principal_direction(), + // minimal_principal_direction()); + std::swap(m_d1, m_d2); + if (m_coefficients.size() >= 2) + std::swap(m_coefficients[0], m_coefficients[1]); + if (m_coefficients.size() >= 6) { + std::swap(m_coefficients[2], m_coefficients[5]); + std::swap(m_coefficients[3], m_coefficients[4]); } - - //Compute preconditionning - REAL precond = 0.; - for(auto it = pts_in_fitting_basis.begin(); it != pts_in_fitting_basis.end(); ++it) { - precond += std::abs((*it)[0]) + std::abs((*it)[1]); + if (m_coefficients.size() >= 11) { + std::swap(m_coefficients[6], m_coefficients[10]); + std::swap(m_coefficients[7], m_coefficients[9]); } - precond /= 2 * nb_input_pts; - preconditionning = precond; - - //fill matrices M and Z - int line_count = 0; - REAL x, y; - for(auto it = pts_in_fitting_basis.begin(); it != pts_in_fitting_basis.end(); ++it) { - // CGAL_For_all(itb, ite) { - x = (*it)[0]; // x - y = (*it)[1]; // y - Z.coeffRef(line_count) = (*it)[2]; //itb->z()); - for (std::size_t k = 0; k <= d; k++) - { - for (std::size_t i = 0; i <= k; i++) - { - M.coeffRef(line_count, k * (k + 1) / 2 + i) = - std::pow( x, static_cast(k - i) ) - * std::pow( y, static_cast(i) ) - / (fact( static_cast(i) ) * - fact( static_cast(k - i) ) - * std::pow( preconditionning, static_cast(k) ) ); - } - } - line_count++; + typename std::vector::iterator itb = m_coefficients.begin(), + ite = m_coefficients.end(); + for (; itb != ite; itb++) { + *itb = -(*itb); } + } } - /** - * @brief Compute the Monge basis - * - * @param[in] A Coefficients of the d-jet - * @param monge_form The monge form - */ - void compute_Monge_basis(const REAL* A, MongeForm &monge_form){ - //bi-index to uni-index conversion : A(i,j)=A[(i+j)(i+j+1)/2+j] - Vector orig_monge({0., 0., A[0]}); - Vector normal({-A[1], -A[2], 1.}); - REAL norm2 = normal | normal; - normal /= std::sqrt(norm2); - - monge_form.origin() = p02origin.inverse() * world2pca.inverse() * EigenMap(orig_monge); - monge_form.normal_direction() = world2pca.inverse() * EigenMap(normal); - - if (deg_monge >= 2) - { - // normal = cross(Xu, Xv) - Vector Xu({1., 0., A[1]}); - Vector Xv({0., 1., A[2]}); - - //Surface in fitting_basis : X(u,v)=(u,v,J_A(u,v)) - //in the basis Xu=(1,0,A[1]), Xv=(0,1,A[2]), Weingarten=-I^{-1}II - //first fond form I=(e,f,f,g) - // =(Xu.Xu, Xu.Xv, Xu.Xv, Xv.Xv) - //second fond form II=(l,m,m,n)/norm2^(1/2) - // =(n.Xuu, n.Xuv, n.Xuv, n.Xvv) - //ppal curv are the opposite of the eigenvalues of Weingarten or the - // eigenvalues of weingarten = -Weingarten = I^{-1}II - - REAL e = 1 + A[1] * A[1], f = A[1] * A[2], g = 1 + A[2] * A[2], - l = A[3], m = A[4], n = A[5]; - Eigen::Matrix weingarten; - weingarten(0, 0) = (g * l - f * m) / (std::sqrt(norm2) * norm2); - weingarten(0, 1) = (g * m - f * n) / (std::sqrt(norm2) * norm2); - weingarten(1, 0) = (e * m - f * l) / (std::sqrt(norm2) * norm2); - weingarten(1, 1) = (e * n - f * m) / (std::sqrt(norm2) * norm2); - // Y, Z are normalized GramSchmidt of Xu, Xv - // Xu->Y=Xu/||Xu||; - // Xv->Z=Xv-(Xu.Xv)Xu/||Xu||^2; - // Z-> Z/||Z|| - Vector Y, Z; - REAL normXu = std::sqrt( Xu | Xu ); - Y = Xu / normXu; - REAL XudotXv = Xu | Xv; - Z = Xv - XudotXv * Xu / (normXu * normXu); - REAL normZ = std::sqrt( Z | Z ); - Z /= normZ; - Eigen::Matrix change_XuXv2YZ; - change_XuXv2YZ(0, 0) = 1 / normXu; - change_XuXv2YZ(0, 1) = -XudotXv / (normXu * normXu * normZ); - change_XuXv2YZ(1, 0) = 0; - change_XuXv2YZ(1, 1) = 1 / normZ; - - //in the new orthonormal basis (Y,Z) of the tangent plane : - weingarten = change_XuXv2YZ.inverse() * weingarten * change_XuXv2YZ; - - // diagonalization of weingarten - std::array W = {{weingarten(0, 0), weingarten(1, 0), weingarten(1, 1)}}; - Eigen::Matrix eval; - Eigen::Matrix evec; - EigenDiagonalizeTraits::diagonalizeSelfAdjointCovMatrix(W, eval, evec); - - // Principal directions - Vector d_max = evec(2) * Y + evec(3) * Z, - d_min = evec(0) * Y + evec(1) * Z; - switch_to_direct_orientation(d_max, d_min, normal); - - // Construct transformation to Monge basis - EigenMatrix tmp; - tmp << d_max[0], d_max[1], d_max[2], - d_min[0], d_min[1], d_min[2], - normal[0], normal[1], normal[2]; - Eigen::Affine3d change_basis (tmp); - pca2monge = change_basis; - - // Store the monge basis origin and vectors with their world coord - monge_form.maximal_principal_direction() = world2pca.inverse() * EigenMap(d_max); - monge_form.minimal_principal_direction() = world2pca.inverse() * EigenMap(d_min); - - // Store principal curvatures - monge_form.coefficients()[0] = eval[1]; - monge_form.coefficients()[1] = eval[0]; - } + void dump_verbose(std::ostream &out_stream) const { + out_stream << "origin : " << origin() << std::endl + << "n : " << normal_direction() << std::endl; + if (coefficients().size() >= 2) + out_stream << "d1 : " << maximal_principal_direction() << std::endl + << "d2 : " << minimal_principal_direction() << std::endl + << "k1 : " << coefficients()[0] << std::endl + << "k2 : " << coefficients()[1] << std::endl; + if (coefficients().size() >= 6) + out_stream << "b0 : " << coefficients()[2] << std::endl + << "b1 : " << coefficients()[3] << std::endl + << "b2 : " << coefficients()[4] << std::endl + << "b3 : " << coefficients()[5] << std::endl; + if (coefficients().size() >= 11) + out_stream << "c0 : " << coefficients()[6] << std::endl + << "c1 : " << coefficients()[7] << std::endl + << "c2 : " << coefficients()[8] << std::endl + << "c3 : " << coefficients()[9] << std::endl + << "c4 : " << coefficients()[10] << std::endl + << std::endl; + } + void dump_4ogl(std::ostream &out_stream, const REAL scale) { + if (coefficients().size() < 2) + gamer_runtime_error("Insufficient coefficients"); + out_stream << origin() << " " << maximal_principal_direction() * scale + << " " << minimal_principal_direction() * scale << " " + << coefficients()[0] << " " << coefficients()[1] << " " + << std::endl; + } + }; // end nested class MongeForm + + /// Default constructor + Monge_via_jet_fitting() { + m_pca_basis = std::vector>(3); + } + + /** + * @brief Function call + * + * @param[in] begin Iterator to first vertex and origin of fitting + * @param[in] end Iterator just past the end + * @param[in] dJet Order of jet fitting + * @param dPrime Order of differentials to compute + * + * @tparam InputIterator Typename of iterator + * + * @return Monge form at vertex + */ + template + MongeForm operator()(InputIterator begin, InputIterator end, std::size_t dJet, + std::size_t dPrime) { + // Precondition verifying that the differential + if (!((dJet >= 1) && (dPrime >= 1) && (dPrime <= 4) && (dPrime <= dJet))) { + std::stringstream ss; + ss << "Cannot compute " << dPrime << "-order differential property using " + << dJet << "-jet."; + gamer_runtime_error(ss.str()); + } + // Degree of jet fitting + deg = static_cast(dJet); + // Degree of differential to compute + deg_monge = static_cast(dPrime); + // Number of points required to fit d-jet + nb_d_jet_coeff = static_cast((dJet + 1) * (dJet + 2) / 2); + // Number of input points + nb_input_pts = static_cast(end - begin); + // Check that enough points are given to fit d-jet + if (nb_input_pts < nb_d_jet_coeff) + gamer_runtime_error( + "Insufficient points provided to perform jet fitting."); + + // Initialize MongeForm + MongeForm monge_form(dPrime); + + // Assemble the linear system + EigenMatrixN M(nb_input_pts, nb_d_jet_coeff); + EigenVectorN Z(nb_input_pts); + + // Compute + compute_PCA(begin, end); + fill_matrix(begin, end, dJet, M, Z); // with precond + + // std::cout << "M:" << std::endl << M << std::endl; + // std::cout << "Z:" << std::endl << Z << std::endl; + // Solve MA=Z in the ls sense. The solution A is stored in Z. + Eigen::JacobiSVD< + Eigen::Matrix> + jacobiSvd(M, Eigen::ComputeThinU | Eigen::ComputeThinV); + Z = jacobiSvd.solve(EigenVectorN(Z)); + condition_nb = jacobiSvd.singularValues().array().abs().maxCoeff() / + jacobiSvd.singularValues().array().abs().minCoeff(); + + for (int k = 0; k <= deg; k++) + for (int i = 0; i <= k; i++) + Z(k * (k + 1) / 2 + i) /= std::pow(preconditionning, k); + + compute_Monge_basis(Z.data(), monge_form); + if (dPrime >= 3) + compute_Monge_coefficients(Z.data(), dPrime, monge_form); + return monge_form; + } + + /** + * @brief Access to condition number + * + * @return Condition number + */ + const REAL condition_number() const { return condition_nb; } + + /** + * @brief Get the PCA basis + * + * @param[in] i Index + * + * @return PCA basis vector + */ + const std::pair pca_basis(std::size_t i) const { + if (i >= 3) + gamer_runtime_error("Out of bounds for PCA basis..."); + return m_pca_basis[i]; + } + +protected: + int deg; + int deg_monge; + int nb_d_jet_coeff; + int nb_input_pts; + REAL preconditionning; + REAL condition_nb; + + std::vector> m_pca_basis; + + /// Translate the points such that p0 (the first point) is at the origin + Eigen::Affine3d p02origin; + /// Rotate local orientation to PCA + Eigen::Affine3d world2pca; + /// Transform from PCA fitting to Monge + Eigen::Affine3d pca2monge; + + /** + * @brief Compute PCA of points + * + * @param[in] begin Iterator to origin point + * @param[in] end Iterator to just past the end. + * + * @tparam InputIterator Typename of iterator + */ + template + void compute_PCA(InputIterator begin, InputIterator end) { + int n = nb_input_pts; + REAL x, y, z, sumX = 0., sumY = 0., sumZ = 0., sumX2 = 0., sumY2 = 0., + sumZ2 = 0., sumXY = 0., sumXZ = 0., sumYZ = 0., xx, yy, zz, + xy, xz, yz; + + for (; begin != end; begin++) { + Vector lp = (**begin).position; + x = lp[0]; + y = lp[1]; + z = lp[2]; + sumX += x / n; + sumY += y / n; + sumZ += z / n; + sumX2 += x * x / n; + sumY2 += y * y / n; + sumZ2 += z * z / n; + sumXY += x * y / n; + sumXZ += x * z / n; + sumYZ += y * z / n; + } + xx = sumX2 - sumX * sumX; + yy = sumY2 - sumY * sumY; + zz = sumZ2 - sumZ * sumZ; + xy = sumXY - sumX * sumY; + xz = sumXZ - sumX * sumZ; + yz = sumYZ - sumY * sumZ; + + // assemble covariance matrix as a + // semi-definite matrix. + // Matrix numbering: + // 0 1 2 + // 3 4 + // 5 + std::array covariance = {{xx, xy, xz, yy, yz, zz}}; + EigenVector eigenvalues; + EigenMatrix eigenvectors; + + // solve for eigenvalues and eigenvectors. + // eigen values are sorted in ascending order, + // eigen vectors are sorted in accordance. + EigenDiagonalizeTraits::diagonalizeSelfAdjointCovMatrix( + covariance, eigenvalues, eigenvectors); + // std::cout << "PCA Eigenvalues: " << std::endl + // << eigenvalues << std::endl; + // std::cout << "PCA Eigenvectors: " << std::endl + // << eigenvectors << std::endl; + + // Store eigenvalues in m_pca_basis + for (int i = 0; i < 3; i++) { + m_pca_basis[i].first = eigenvalues[2 - i]; } - /** - * @brief Calculates the 3rd and 4th order differential values - * - * @param A Vector of coefficients - * @param[in] dprime Degree differential to compute - * @param monge_form MongeForm object - */ - void compute_Monge_coefficients(REAL* A, std::size_t dprime, - MongeForm &monge_form){ - //One has the equation w=J_A(u,v) of the fitted surface S - // in the fitting_basis - //Substituing (u,v,w)=pca2monge^{-1}(x,y,z) - //One has the equation f(x,y,z)=0 on this surface S in the monge - // basis - //The monge form of the surface at the origin is the bivariate fct - // g(x,y) s.t. f(x,y,g(x,y))=0 - //voir les calculs Maple dans monge.mws - //Notations are f123= d^3f/dxdydz - // g(x,y)=sum (gij x^i y^j/ i!j!) with - // g00=g10=g01=g11=0, g20=kmax, g02=kmin - // - //g(x,y)= 1/2*(k1x^2 +k2y^2) - // +1/6*(b0x^3 +3b1x^2y +3b2xy^2 +b3y^3) - // +1/24*(c0x^4 +4c1x^3y +6c2x^2y^2 +4c3xy^3 +c4y^4) - // +... - // p stores pca2monge^{-1}=pca2monge^{T} - REAL p[3][3]; - p[0][0] = pca2monge(0, 0); - p[1][0] = pca2monge(0, 1); - p[2][0] = pca2monge(0, 2); - p[0][1] = pca2monge(1, 0); - p[1][1] = pca2monge(1, 1); - p[2][1] = pca2monge(1, 2); - p[0][2] = pca2monge(2, 0); - p[1][2] = pca2monge(2, 1); - p[2][2] = pca2monge(2, 2); - - // formula are designed for w=sum( Aij ui vj), but we have J_A = sum( - // Aij/i!j! ui vj) - for (int k = 0; k <= deg; k++) - for (int i = 0; i <= k; i++) - A[k * (k + 1) / 2 + i] /= fact(k - i) * fact(i); - //this is A(k-i;i) - - /* //debug */ - /* std::cout << "coeff of A" << std::endl */ - /* << A[0] << " "<< A[1] << " "<< A[2] << std::endl */ - /* << A[3] << " "<< A[4] << " "<< A[5] << std::endl */ - /* << A[6] << " "<< A[7] << " "<< A[8] << " "<< A[9]<< std::endl */ - /* << A[10] << " "<< A[11] << " "<< A[12] << " "<< A[13]<< " " << - A[14] << - std::endl; */ - - // note f1 = f2 = f12 = 0 - // REAL f1 = A[1] * p[0][0] + A[2] * p[1][0] - p[2][0]; - // REAL f2 = A[2] * p[1][1] + A[1] * p[0][1] - p[2][1]; - // REAL f12 = - // 2 * A[3] * p[0][0] * p[0][1] - // + 2 * A[5] * p[1][0] * p[1][1] - // + A[4] * p[0][1] * p[1][0] - // + A[4] * p[0][0] * p[1][1]; - // -f11 / f3 = kmax - // -f22 / f3 = kmin - - REAL f3 = A[1] * p[0][2] + A[2] * p[1][2] - p[2][2]; - REAL f11 = - 2 * A[4] * p[0][0] * p[1][0] - + 2 * A[5] * p[1][0] * p[1][0] - + 2 * A[3] * p[0][0] * p[0][0]; - REAL f13 = - A[4] * p[0][0] * p[1][2] - + A[4] * p[0][2] * p[1][0] - + 2 * A[5] * p[1][0] * p[1][2] - + 2 * A[3] * p[0][0] * p[0][2]; - REAL f22 = - 2 * A[4] * p[0][1] * p[1][1] - + 2 * A[5] * p[1][1] * p[1][1] - + 2 * A[3] * p[0][1] * p[0][1]; - REAL f23 = - A[4] * p[0][1] * p[1][2] - + 2 * A[5] * p[1][1] * p[1][2] - + A[4] * p[0][2] * p[1][1] - + 2 * A[3] * p[0][1] * p[0][2]; - REAL f33 = - 2 * A[5] * p[1][2] * p[1][2] - + 2 * A[3] * p[0][2] * p[0][2] - + 2 * A[4] * p[0][2] * p[1][2]; - REAL f111 = - 6 * A[8] * p[0][0] * p[1][0] * p[1][0] - + 6 * A[7] * p[0][0] * p[0][0] * p[1][0] - + 6 * A[6] * p[0][0] * p[0][0] * p[0][0] - + 6 * A[9] * p[1][0] * p[1][0] * p[1][0]; - REAL f222 = - 6 * A[7] * p[0][1] * p[0][1] * p[1][1] - + 6 * A[8] * p[0][1] * p[1][1] * p[1][1] - + 6 * A[9] * p[1][1] * p[1][1] * p[1][1] - + 6 * A[6] * p[0][1] * p[0][1] * p[0][1]; - REAL f112 = - 2 * A[7] * p[0][0] * p[0][0] * p[1][1] - + 6 * A[6] * p[0][0] * p[0][0] * p[0][1] - + 2 * A[8] * p[0][1] * p[1][0] * p[1][0] - + 4 * A[8] * p[0][0] * p[1][0] * p[1][1] - + 6 * A[9] * p[1][0] * p[1][0] * p[1][1] - + 4 * A[7] * p[0][0] * p[0][1] * p[1][0]; - REAL f122 = - 4 * A[8] * p[0][1] * p[1][0] * p[1][1] - + 2 * A[8] * p[0][0] * p[1][1] * p[1][1] - + 6 * A[6] * p[0][0] * p[0][1] * p[0][1] - + 2 * A[7] * p[0][1] * p[0][1] * p[1][0] - + 4 * A[7] * p[0][0] * p[0][1] * p[1][1] - + 6 * A[9] * p[1][0] * p[1][1] * p[1][1]; - REAL f113 = - 6 * A[6] * p[0][0] * p[0][0] * p[0][2] - + 6 * A[9] * p[1][0] * p[1][0] * p[1][2] - + 2 * A[7] * p[0][0] * p[0][0] * p[1][2] - + 2 * A[8] * p[0][2] * p[1][0] * p[1][0] - + 4 * A[7] * p[0][0] * p[0][2] * p[1][0] - + 4 * A[8] * p[0][0] * p[1][0] * p[1][2]; - REAL f223 = - 2 * A[8] * p[0][2] * p[1][1] * p[1][1] - + 6 * A[6] * p[0][1] * p[0][1] * p[0][2] - + 6 * A[9] * p[1][1] * p[1][1] * p[1][2] - + 2 * A[7] * p[0][1] * p[0][1] * p[1][2] - + 4 * A[7] * p[0][1] * p[0][2] * p[1][1] - + 4 * A[8] * p[0][1] * p[1][1] * p[1][2]; - REAL f123 = - 2 * A[8] * p[0][2] * p[1][0] * p[1][1] - + 2 * A[7] * p[0][0] * p[0][1] * p[1][2] - + 2 * A[7] * p[0][0] * p[0][2] * p[1][1] - + 6 * A[9] * p[1][0] * p[1][1] * p[1][2] - + 2 * A[7] * p[0][1] * p[0][2] * p[1][0] - + 6 * A[6] * p[0][0] * p[0][1] * p[0][2] - + 2 * A[8] * p[0][0] * p[1][1] * p[1][2] - + 2 * A[8] * p[0][1] * p[1][0] * p[1][2]; - - REAL b0 = 1 / (f3 * f3) * (-f111 * f3 + 3 * f13 * f11); - REAL b1 = 1 / (f3 * f3) * (-f112 * f3 + f23 * f11); - REAL b2 = 1 / (f3 * f3) * (-f122 * f3 + f13 * f22); - REAL b3 = -1 / (f3 * f3) * (f222 * f3 - 3 * f23 * f22); - - monge_form.coefficients()[2] = b0; - monge_form.coefficients()[3] = b1; - monge_form.coefficients()[4] = b2; - monge_form.coefficients()[5] = b3; - - if (dprime == 4) - { - REAL f1111 = - 24 * A[13] * p[0][0] * p[1][0] * p[1][0] * p[1][0] - + 24 * A[12] * p[0][0] * p[0][0] * p[1][0] * p[1][0] - + 24 * A[11] * p[0][0] * p[0][0] * p[0][0] * p[1][0] - + 24 * A[14] * p[1][0] * p[1][0] * p[1][0] * p[1][0] - + 24 * A[10] * p[0][0] * p[0][0] * p[0][0] * p[0][0]; - REAL f1112 = - 6 * A[13] * p[0][1] * p[1][0] * p[1][0] * p[1][0] - + 18 * A[13] * p[0][0] * p[1][0] * p[1][0] * p[1][1] - + 24 * A[10] * p[0][0] * p[0][0] * p[0][0] * p[0][1] - + 12 * A[12] * p[0][0] * p[0][1] * p[1][0] * p[1][0] - + 18 * A[11] * p[0][0] * p[0][0] * p[0][1] * p[1][0] - + 24 * A[14] * p[1][0] * p[1][0] * p[1][0] * p[1][1] - + 6 * A[11] * p[0][0] * p[0][0] * p[0][0] * p[1][1] - + 12 * A[12] * p[0][0] * p[0][0] * p[1][0] * p[1][1]; - REAL f1122 = - 12 * A[11] * p[0][0] * p[0][0] * p[0][1] * p[1][1] - + 12 * A[13] * p[0][0] * p[1][0] * p[1][1] * p[1][1] - + 12 * A[13] * p[0][1] * p[1][0] * p[1][0] * p[1][1] - + 16 * A[12] * p[0][0] * p[0][1] * p[1][0] * p[1][1] - + 12 * A[11] * p[0][0] * p[0][1] * p[0][1] * p[1][0] - + 24 * A[10] * p[0][0] * p[0][0] * p[0][1] * p[0][1] - + 4 * A[12] * p[0][1] * p[0][1] * p[1][0] * p[1][0] - + 4 * A[12] * p[0][0] * p[0][0] * p[1][1] * p[1][1] - + 24 * A[14] * p[1][0] * p[1][0] * p[1][1] * p[1][1]; - REAL f1222 = - 6 * A[13] * p[0][0] * p[1][1] * p[1][1] * p[1][1] - + 24 * A[10] * p[0][0] * p[0][1] * p[0][1] * p[0][1] - + 24 * A[14] * p[1][0] * p[1][1] * p[1][1] * p[1][1] - + 6 * A[11] * p[0][1] * p[0][1] * p[0][1] * p[1][0] - + 18 * A[11] * p[0][0] * p[0][1] * p[0][1] * p[1][1] - + 12 * A[12] * p[0][0] * p[0][1] * p[1][1] * p[1][1] - + 12 * A[12] * p[0][1] * p[0][1] * p[1][0] * p[1][1] - + 18 * A[13] * p[0][1] * p[1][0] * p[1][1] * p[1][1]; - REAL f2222 = - 24 * A[13] * p[0][1] * p[1][1] * p[1][1] * p[1][1] - + 24 * A[11] * p[0][1] * p[0][1] * p[0][1] * p[1][1] - + 24 * A[12] * p[0][1] * p[0][1] * p[1][1] * p[1][1] - + 24 * A[10] * p[0][1] * p[0][1] * p[0][1] * p[0][1] - + 24 * A[14] * p[1][1] * p[1][1] * p[1][1] * p[1][1]; - - REAL c0 = - -1 / (f3 * f3 * f3) * (f1111 * (f3 * f3) - 4 * f13 * f3 * f111 + 12 * f13 * f13 * f11 - 6 * f113 * f3 * f11 + 3 * f33 * f11 * f11); - REAL c1 = - 1 / (f3 * f3 * f3) * (f23 * f3 * f111 + 3 * f3 * f123 * f11 + 3 * f13 * f3 * f112 - f1112 * (f3 * f3) - 6 * f13 * f23 * f11); - REAL c2 = - 1 / (f3 * f3 * f3) * ( -f33 * f22 * f11 + f113 * f3 * f22 + 2 * f13 * f3 * f122 - 2 * f13 * f13 * f22 + f223 * f3 * f11 + 2 * f23 * f3 * f112 - 2 * f23 * f23 * f11 - f1122 * (f3 * f3) ); - REAL c3 = - 1 / (f3 * f3 * f3) * (-f1222 * (f3 * f3) - 6 * f13 * f23 * f22 + 3 * f123 * f3 * f22 + f13 * f3 * f222 + 3 * f23 * f3 * f122); - REAL c4 = - -1 / (f3 * f3 * f3) * (f2222 * (f3 * f3) + 3 * f33 * f22 * f22 - 6 * f223 * f3 * f22 - 4 * f23 * f3 * f222 + 12 * f23 * f23 * f22); - - monge_form.coefficients()[6] = c0; - monge_form.coefficients()[7] = c1; - monge_form.coefficients()[8] = c2; - monge_form.coefficients()[9] = c3; - monge_form.coefficients()[10] = c4; - } + Vector v1({eigenvectors(6), eigenvectors(7), eigenvectors(8)}); + m_pca_basis[0].second = v1; + Vector v2({eigenvectors(3), eigenvectors(4), eigenvectors(5)}); + m_pca_basis[1].second = v2; + Vector v3({eigenvectors(0), eigenvectors(1), eigenvectors(2)}); + m_pca_basis[2].second = v3; + + switch_to_direct_orientation(m_pca_basis[0].second, m_pca_basis[1].second, + m_pca_basis[2].second); + + EigenMatrix tmp; + tmp << m_pca_basis[0].second[0], m_pca_basis[0].second[1], + m_pca_basis[0].second[2], m_pca_basis[1].second[0], + m_pca_basis[1].second[1], m_pca_basis[1].second[2], + m_pca_basis[2].second[0], m_pca_basis[2].second[1], + m_pca_basis[2].second[2]; + + // Store the change of basis W->F + Eigen::Affine3d change_basis(tmp); + world2pca = change_basis; + } + + // Coordinates of input points are computed in the fitting basis with + // p0 as origin. + // Preconditionning is computed, M and Z are filled + template + void fill_matrix(InputIterator begin, InputIterator end, std::size_t d, + EigenMatrixN &M, EigenVectorN &Z) { + // origin of fitting coord system = first input data point + Vector point0 = (**begin).position; + // std::cout << "point0: " << point0 << std::endl; + // transform coordinates of sample points with a + // translation ($-p$) and multiplication by $ P_{W\rightarrow F}$. + Vector orig({0., 0., 0.}); + Vector v_point0_orig(orig - point0); + // std::cout << "v_point0_orig: " << v_point0_orig << std::endl; + + p02origin = Eigen::Translation3d(v_point0_orig); + Eigen::Affine3d transf_points = world2pca * p02origin; + + // compute and store transformed points + std::vector pts_in_fitting_basis; + pts_in_fitting_basis.reserve(nb_input_pts); + + for (auto it = begin; it != end; ++it) { + Vector cur_pt = (**it).position; + cur_pt = transf_points * EigenMap(cur_pt); + pts_in_fitting_basis.push_back(cur_pt); } - /** - * @brief Flip the orientation if det(v1,v2,v3) < 0 - * - * @param v1 Vector 1 - * @param[in] v2 Vector 2 - * @param[in] v3 Vector 3 - */ - void switch_to_direct_orientation(Vector &v1, const Vector &v2, - const Vector &v3){ - if (dot(v1, cross(v2,v3)) < 0.) { - v1 = -v1; + // Compute preconditionning + REAL precond = 0.; + for (auto it = pts_in_fitting_basis.begin(); + it != pts_in_fitting_basis.end(); ++it) { + precond += std::abs((*it)[0]) + std::abs((*it)[1]); + } + precond /= 2 * nb_input_pts; + preconditionning = precond; + + // fill matrices M and Z + int line_count = 0; + REAL x, y; + for (auto it = pts_in_fitting_basis.begin(); + it != pts_in_fitting_basis.end(); ++it) { + // CGAL_For_all(itb, ite) { + x = (*it)[0]; // x + y = (*it)[1]; // y + Z.coeffRef(line_count) = (*it)[2]; // itb->z()); + for (std::size_t k = 0; k <= d; k++) { + for (std::size_t i = 0; i <= k; i++) { + M.coeffRef(line_count, k * (k + 1) / 2 + i) = + std::pow(x, static_cast(k - i)) * + std::pow(y, static_cast(i)) / + (fact(static_cast(i)) * + fact(static_cast(k - i)) * + std::pow(preconditionning, static_cast(k))); } + } + line_count++; } - - /** - * @brief Print operator overload - * - * @param out_stream The stream to write data to - * @param[in] monge MongeForm object to print - * - * @return The stream - */ - friend std::ostream& operator<<(std::ostream &out_stream, - const typename Monge_via_jet_fitting::MongeForm &monge) - { - monge.dump_verbose(out_stream); - return out_stream; + } + + /** + * @brief Compute the Monge basis + * + * @param[in] A Coefficients of the d-jet + * @param monge_form The monge form + */ + void compute_Monge_basis(const REAL *A, MongeForm &monge_form) { + // bi-index to uni-index conversion : A(i,j)=A[(i+j)(i+j+1)/2+j] + Vector orig_monge({0., 0., A[0]}); + Vector normal({-A[1], -A[2], 1.}); + REAL norm2 = normal | normal; + normal /= std::sqrt(norm2); + + monge_form.origin() = + p02origin.inverse() * world2pca.inverse() * EigenMap(orig_monge); + monge_form.normal_direction() = world2pca.inverse() * EigenMap(normal); + + if (deg_monge >= 2) { + // normal = cross(Xu, Xv) + Vector Xu({1., 0., A[1]}); + Vector Xv({0., 1., A[2]}); + + // Surface in fitting_basis : X(u,v)=(u,v,J_A(u,v)) + // in the basis Xu=(1,0,A[1]), Xv=(0,1,A[2]), Weingarten=-I^{-1}II + // first fond form I=(e,f,f,g) + // =(Xu.Xu, Xu.Xv, Xu.Xv, Xv.Xv) + // second fond form II=(l,m,m,n)/norm2^(1/2) + // =(n.Xuu, n.Xuv, n.Xuv, n.Xvv) + // ppal curv are the opposite of the eigenvalues of Weingarten or the + // eigenvalues of weingarten = -Weingarten = I^{-1}II + + REAL e = 1 + A[1] * A[1], f = A[1] * A[2], g = 1 + A[2] * A[2], l = A[3], + m = A[4], n = A[5]; + Eigen::Matrix weingarten; + weingarten(0, 0) = (g * l - f * m) / (std::sqrt(norm2) * norm2); + weingarten(0, 1) = (g * m - f * n) / (std::sqrt(norm2) * norm2); + weingarten(1, 0) = (e * m - f * l) / (std::sqrt(norm2) * norm2); + weingarten(1, 1) = (e * n - f * m) / (std::sqrt(norm2) * norm2); + // Y, Z are normalized GramSchmidt of Xu, Xv + // Xu->Y=Xu/||Xu||; + // Xv->Z=Xv-(Xu.Xv)Xu/||Xu||^2; + // Z-> Z/||Z|| + Vector Y, Z; + REAL normXu = std::sqrt(Xu | Xu); + Y = Xu / normXu; + REAL XudotXv = Xu | Xv; + Z = Xv - XudotXv * Xu / (normXu * normXu); + REAL normZ = std::sqrt(Z | Z); + Z /= normZ; + Eigen::Matrix change_XuXv2YZ; + change_XuXv2YZ(0, 0) = 1 / normXu; + change_XuXv2YZ(0, 1) = -XudotXv / (normXu * normXu * normZ); + change_XuXv2YZ(1, 0) = 0; + change_XuXv2YZ(1, 1) = 1 / normZ; + + // in the new orthonormal basis (Y,Z) of the tangent plane : + weingarten = change_XuXv2YZ.inverse() * weingarten * change_XuXv2YZ; + + // diagonalization of weingarten + std::array W = { + {weingarten(0, 0), weingarten(1, 0), weingarten(1, 1)}}; + Eigen::Matrix eval; + Eigen::Matrix evec; + EigenDiagonalizeTraits::diagonalizeSelfAdjointCovMatrix(W, eval, + evec); + + // Principal directions + Vector d_max = evec(2) * Y + evec(3) * Z, + d_min = evec(0) * Y + evec(1) * Z; + switch_to_direct_orientation(d_max, d_min, normal); + + // Construct transformation to Monge basis + EigenMatrix tmp; + tmp << d_max[0], d_max[1], d_max[2], d_min[0], d_min[1], d_min[2], + normal[0], normal[1], normal[2]; + Eigen::Affine3d change_basis(tmp); + pca2monge = change_basis; + + // Store the monge basis origin and vectors with their world coord + monge_form.maximal_principal_direction() = + world2pca.inverse() * EigenMap(d_max); + monge_form.minimal_principal_direction() = + world2pca.inverse() * EigenMap(d_min); + + // Store principal curvatures + monge_form.coefficients()[0] = eval[1]; + monge_form.coefficients()[1] = eval[0]; + } + } + + /** + * @brief Calculates the 3rd and 4th order differential values + * + * @param A Vector of coefficients + * @param[in] dprime Degree differential to compute + * @param monge_form MongeForm object + */ + void compute_Monge_coefficients(REAL *A, std::size_t dprime, + MongeForm &monge_form) { + // One has the equation w=J_A(u,v) of the fitted surface S + // in the fitting_basis + // Substituing (u,v,w)=pca2monge^{-1}(x,y,z) + // One has the equation f(x,y,z)=0 on this surface S in the monge + // basis + // The monge form of the surface at the origin is the bivariate fct + // g(x,y) s.t. f(x,y,g(x,y))=0 + // voir les calculs Maple dans monge.mws + // Notations are f123= d^3f/dxdydz + // g(x,y)=sum (gij x^i y^j/ i!j!) with + // g00=g10=g01=g11=0, g20=kmax, g02=kmin + // + // g(x,y)= 1/2*(k1x^2 +k2y^2) + // +1/6*(b0x^3 +3b1x^2y +3b2xy^2 +b3y^3) + // +1/24*(c0x^4 +4c1x^3y +6c2x^2y^2 +4c3xy^3 +c4y^4) + // +... + // p stores pca2monge^{-1}=pca2monge^{T} + REAL p[3][3]; + p[0][0] = pca2monge(0, 0); + p[1][0] = pca2monge(0, 1); + p[2][0] = pca2monge(0, 2); + p[0][1] = pca2monge(1, 0); + p[1][1] = pca2monge(1, 1); + p[2][1] = pca2monge(1, 2); + p[0][2] = pca2monge(2, 0); + p[1][2] = pca2monge(2, 1); + p[2][2] = pca2monge(2, 2); + + // formula are designed for w=sum( Aij ui vj), but we have J_A = sum( + // Aij/i!j! ui vj) + for (int k = 0; k <= deg; k++) + for (int i = 0; i <= k; i++) + A[k * (k + 1) / 2 + i] /= fact(k - i) * fact(i); + // this is A(k-i;i) + + /* //debug */ + /* std::cout << "coeff of A" << std::endl */ + /* << A[0] << " "<< A[1] << " "<< A[2] << std::endl */ + /* << A[3] << " "<< A[4] << " "<< A[5] << std::endl */ + /* << A[6] << " "<< A[7] << " "<< A[8] << " "<< A[9]<< std::endl */ + /* << A[10] << " "<< A[11] << " "<< A[12] << " "<< A[13]<< " " << + A[14] << + std::endl; */ + + // note f1 = f2 = f12 = 0 + // REAL f1 = A[1] * p[0][0] + A[2] * p[1][0] - p[2][0]; + // REAL f2 = A[2] * p[1][1] + A[1] * p[0][1] - p[2][1]; + // REAL f12 = + // 2 * A[3] * p[0][0] * p[0][1] + // + 2 * A[5] * p[1][0] * p[1][1] + // + A[4] * p[0][1] * p[1][0] + // + A[4] * p[0][0] * p[1][1]; + // -f11 / f3 = kmax + // -f22 / f3 = kmin + + REAL f3 = A[1] * p[0][2] + A[2] * p[1][2] - p[2][2]; + REAL f11 = 2 * A[4] * p[0][0] * p[1][0] + 2 * A[5] * p[1][0] * p[1][0] + + 2 * A[3] * p[0][0] * p[0][0]; + REAL f13 = A[4] * p[0][0] * p[1][2] + A[4] * p[0][2] * p[1][0] + + 2 * A[5] * p[1][0] * p[1][2] + 2 * A[3] * p[0][0] * p[0][2]; + REAL f22 = 2 * A[4] * p[0][1] * p[1][1] + 2 * A[5] * p[1][1] * p[1][1] + + 2 * A[3] * p[0][1] * p[0][1]; + REAL f23 = A[4] * p[0][1] * p[1][2] + 2 * A[5] * p[1][1] * p[1][2] + + A[4] * p[0][2] * p[1][1] + 2 * A[3] * p[0][1] * p[0][2]; + REAL f33 = 2 * A[5] * p[1][2] * p[1][2] + 2 * A[3] * p[0][2] * p[0][2] + + 2 * A[4] * p[0][2] * p[1][2]; + REAL f111 = 6 * A[8] * p[0][0] * p[1][0] * p[1][0] + + 6 * A[7] * p[0][0] * p[0][0] * p[1][0] + + 6 * A[6] * p[0][0] * p[0][0] * p[0][0] + + 6 * A[9] * p[1][0] * p[1][0] * p[1][0]; + REAL f222 = 6 * A[7] * p[0][1] * p[0][1] * p[1][1] + + 6 * A[8] * p[0][1] * p[1][1] * p[1][1] + + 6 * A[9] * p[1][1] * p[1][1] * p[1][1] + + 6 * A[6] * p[0][1] * p[0][1] * p[0][1]; + REAL f112 = 2 * A[7] * p[0][0] * p[0][0] * p[1][1] + + 6 * A[6] * p[0][0] * p[0][0] * p[0][1] + + 2 * A[8] * p[0][1] * p[1][0] * p[1][0] + + 4 * A[8] * p[0][0] * p[1][0] * p[1][1] + + 6 * A[9] * p[1][0] * p[1][0] * p[1][1] + + 4 * A[7] * p[0][0] * p[0][1] * p[1][0]; + REAL f122 = 4 * A[8] * p[0][1] * p[1][0] * p[1][1] + + 2 * A[8] * p[0][0] * p[1][1] * p[1][1] + + 6 * A[6] * p[0][0] * p[0][1] * p[0][1] + + 2 * A[7] * p[0][1] * p[0][1] * p[1][0] + + 4 * A[7] * p[0][0] * p[0][1] * p[1][1] + + 6 * A[9] * p[1][0] * p[1][1] * p[1][1]; + REAL f113 = 6 * A[6] * p[0][0] * p[0][0] * p[0][2] + + 6 * A[9] * p[1][0] * p[1][0] * p[1][2] + + 2 * A[7] * p[0][0] * p[0][0] * p[1][2] + + 2 * A[8] * p[0][2] * p[1][0] * p[1][0] + + 4 * A[7] * p[0][0] * p[0][2] * p[1][0] + + 4 * A[8] * p[0][0] * p[1][0] * p[1][2]; + REAL f223 = 2 * A[8] * p[0][2] * p[1][1] * p[1][1] + + 6 * A[6] * p[0][1] * p[0][1] * p[0][2] + + 6 * A[9] * p[1][1] * p[1][1] * p[1][2] + + 2 * A[7] * p[0][1] * p[0][1] * p[1][2] + + 4 * A[7] * p[0][1] * p[0][2] * p[1][1] + + 4 * A[8] * p[0][1] * p[1][1] * p[1][2]; + REAL f123 = 2 * A[8] * p[0][2] * p[1][0] * p[1][1] + + 2 * A[7] * p[0][0] * p[0][1] * p[1][2] + + 2 * A[7] * p[0][0] * p[0][2] * p[1][1] + + 6 * A[9] * p[1][0] * p[1][1] * p[1][2] + + 2 * A[7] * p[0][1] * p[0][2] * p[1][0] + + 6 * A[6] * p[0][0] * p[0][1] * p[0][2] + + 2 * A[8] * p[0][0] * p[1][1] * p[1][2] + + 2 * A[8] * p[0][1] * p[1][0] * p[1][2]; + + REAL b0 = 1 / (f3 * f3) * (-f111 * f3 + 3 * f13 * f11); + REAL b1 = 1 / (f3 * f3) * (-f112 * f3 + f23 * f11); + REAL b2 = 1 / (f3 * f3) * (-f122 * f3 + f13 * f22); + REAL b3 = -1 / (f3 * f3) * (f222 * f3 - 3 * f23 * f22); + + monge_form.coefficients()[2] = b0; + monge_form.coefficients()[3] = b1; + monge_form.coefficients()[4] = b2; + monge_form.coefficients()[5] = b3; + + if (dprime == 4) { + REAL f1111 = 24 * A[13] * p[0][0] * p[1][0] * p[1][0] * p[1][0] + + 24 * A[12] * p[0][0] * p[0][0] * p[1][0] * p[1][0] + + 24 * A[11] * p[0][0] * p[0][0] * p[0][0] * p[1][0] + + 24 * A[14] * p[1][0] * p[1][0] * p[1][0] * p[1][0] + + 24 * A[10] * p[0][0] * p[0][0] * p[0][0] * p[0][0]; + REAL f1112 = 6 * A[13] * p[0][1] * p[1][0] * p[1][0] * p[1][0] + + 18 * A[13] * p[0][0] * p[1][0] * p[1][0] * p[1][1] + + 24 * A[10] * p[0][0] * p[0][0] * p[0][0] * p[0][1] + + 12 * A[12] * p[0][0] * p[0][1] * p[1][0] * p[1][0] + + 18 * A[11] * p[0][0] * p[0][0] * p[0][1] * p[1][0] + + 24 * A[14] * p[1][0] * p[1][0] * p[1][0] * p[1][1] + + 6 * A[11] * p[0][0] * p[0][0] * p[0][0] * p[1][1] + + 12 * A[12] * p[0][0] * p[0][0] * p[1][0] * p[1][1]; + REAL f1122 = 12 * A[11] * p[0][0] * p[0][0] * p[0][1] * p[1][1] + + 12 * A[13] * p[0][0] * p[1][0] * p[1][1] * p[1][1] + + 12 * A[13] * p[0][1] * p[1][0] * p[1][0] * p[1][1] + + 16 * A[12] * p[0][0] * p[0][1] * p[1][0] * p[1][1] + + 12 * A[11] * p[0][0] * p[0][1] * p[0][1] * p[1][0] + + 24 * A[10] * p[0][0] * p[0][0] * p[0][1] * p[0][1] + + 4 * A[12] * p[0][1] * p[0][1] * p[1][0] * p[1][0] + + 4 * A[12] * p[0][0] * p[0][0] * p[1][1] * p[1][1] + + 24 * A[14] * p[1][0] * p[1][0] * p[1][1] * p[1][1]; + REAL f1222 = 6 * A[13] * p[0][0] * p[1][1] * p[1][1] * p[1][1] + + 24 * A[10] * p[0][0] * p[0][1] * p[0][1] * p[0][1] + + 24 * A[14] * p[1][0] * p[1][1] * p[1][1] * p[1][1] + + 6 * A[11] * p[0][1] * p[0][1] * p[0][1] * p[1][0] + + 18 * A[11] * p[0][0] * p[0][1] * p[0][1] * p[1][1] + + 12 * A[12] * p[0][0] * p[0][1] * p[1][1] * p[1][1] + + 12 * A[12] * p[0][1] * p[0][1] * p[1][0] * p[1][1] + + 18 * A[13] * p[0][1] * p[1][0] * p[1][1] * p[1][1]; + REAL f2222 = 24 * A[13] * p[0][1] * p[1][1] * p[1][1] * p[1][1] + + 24 * A[11] * p[0][1] * p[0][1] * p[0][1] * p[1][1] + + 24 * A[12] * p[0][1] * p[0][1] * p[1][1] * p[1][1] + + 24 * A[10] * p[0][1] * p[0][1] * p[0][1] * p[0][1] + + 24 * A[14] * p[1][1] * p[1][1] * p[1][1] * p[1][1]; + + REAL c0 = + -1 / (f3 * f3 * f3) * + (f1111 * (f3 * f3) - 4 * f13 * f3 * f111 + 12 * f13 * f13 * f11 - + 6 * f113 * f3 * f11 + 3 * f33 * f11 * f11); + REAL c1 = 1 / (f3 * f3 * f3) * + (f23 * f3 * f111 + 3 * f3 * f123 * f11 + 3 * f13 * f3 * f112 - + f1112 * (f3 * f3) - 6 * f13 * f23 * f11); + REAL c2 = 1 / (f3 * f3 * f3) * + (-f33 * f22 * f11 + f113 * f3 * f22 + 2 * f13 * f3 * f122 - + 2 * f13 * f13 * f22 + f223 * f3 * f11 + 2 * f23 * f3 * f112 - + 2 * f23 * f23 * f11 - f1122 * (f3 * f3)); + REAL c3 = 1 / (f3 * f3 * f3) * + (-f1222 * (f3 * f3) - 6 * f13 * f23 * f22 + + 3 * f123 * f3 * f22 + f13 * f3 * f222 + 3 * f23 * f3 * f122); + REAL c4 = -1 / (f3 * f3 * f3) * + (f2222 * (f3 * f3) + 3 * f33 * f22 * f22 - 6 * f223 * f3 * f22 - + 4 * f23 * f3 * f222 + 12 * f23 * f23 * f22); + + monge_form.coefficients()[6] = c0; + monge_form.coefficients()[7] = c1; + monge_form.coefficients()[8] = c2; + monge_form.coefficients()[9] = c3; + monge_form.coefficients()[10] = c4; + } + } + + /** + * @brief Flip the orientation if det(v1,v2,v3) < 0 + * + * @param v1 Vector 1 + * @param[in] v2 Vector 2 + * @param[in] v3 Vector 3 + */ + void switch_to_direct_orientation(Vector &v1, const Vector &v2, + const Vector &v3) { + if (dot(v1, cross(v2, v3)) < 0.) { + v1 = -v1; } + } + + /** + * @brief Print operator overload + * + * @param out_stream The stream to write data to + * @param[in] monge MongeForm object to print + * + * @return The stream + */ + friend std::ostream & + operator<<(std::ostream &out_stream, + const typename Monge_via_jet_fitting::MongeForm &monge) { + monge.dump_verbose(out_stream); + return out_stream; + } }; // end class Monge_via_jet_fitting } // end namespace gamer diff --git a/include/gamer/PDBReader.h b/include/gamer/PDBReader.h index 6aa5a7eb..4e2d138d 100644 --- a/include/gamer/PDBReader.h +++ b/include/gamer/PDBReader.h @@ -1,26 +1,21 @@ -/* - * *************************************************************************** - * This file is part of the GAMer software. - * Copyright (C) 2016-2018 - * by Christopher Lee, John Moody, Rommie Amaro, J. Andrew McCammon, - * and Michael Holst - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more pdbreader_details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * *************************************************************************** - */ +// This file is part of the GAMer software. +// Copyright (C) 2016-2021 +// by Christopher T. Lee and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, see +// or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA /* * Parts of this are adapted from the PDBParser developed in Chandrajit @@ -30,254 +25,251 @@ #pragma once #include -#include +#include +#include #include +#include #include -#include -#include -#include "gamer/gamer.h" #include "gamer/Vertex.h" - +#include "gamer/gamer.h" +#include "gamer/SurfaceMesh.h" /// Namespace for all things gamer -namespace gamer -{ +namespace gamer { /// @cond detail -namespace pdbreader_detail -{ -const double EPSILON = 1e-3; +namespace pdbreader_detail { +const double EPSILON = 1e-3; /// Regular expression for parsing PDB file extension static const std::regex PDB(".*.pdb", std::regex::icase | std::regex::optimize); /// Regular expression for parsing PQR file extension static const std::regex PQR(".*.pqr", std::regex::icase | std::regex::optimize); /// Regular expression for parsing XYZR file extension -static const std::regex XYZR(".*.xyzr", std::regex::icase | std::regex::optimize); +static const std::regex XYZR(".*.xyzr", + std::regex::icase | std::regex::optimize); /// Regular expression to parse for lines starting with ATOM static const std::regex atom("ATOM.*\n*", std::regex::optimize); /** * @brief PDB element information */ -struct PDBelementInformation -{ - const char * atomName; - const char * residueName; - float radius; - float red; - float green; - float blue; - int hydrophobicity; - unsigned char residueIndex; +struct PDBelementInformation { + const char *atomName; + const char *residueName; + float radius; + float red; + float green; + float blue; + int hydrophobicity; + unsigned char residueIndex; }; /// Total number of elements in the PDBelementTable -static const std::size_t MAX_BIOCHEM_ELEMENTS = 167; +static const std::size_t MAX_BIOCHEM_ELEMENTS = 167; /// Basic protein atomic ookup table -static PDBelementInformation PDBelementTable[MAX_BIOCHEM_ELEMENTS] = -{ - {" N ", "GLY", 1.625f, 0.0f, 0.0f, 1.0f, 1, 10 }, - {" CA ", "GLY", 1.750f, 0.3f, 0.3f, 0.3f, -1, 10 }, - {" C ", "GLY", 1.875f, 0.3f, 0.3f, 0.3f, 1, 10 }, - {" O ", "GLY", 1.480f, 1.0f, 0.0f, 0.0f, 1, 10 }, - {" N ", "ALA", 1.625f, 0.0f, 0.0f, 1.0f, 1, 1 }, - {" CA ", "ALA", 1.750f, 0.3f, 0.3f, 0.3f, -1, 1 }, - {" C ", "ALA", 1.875f, 0.3f, 0.3f, 0.3f, 1, 1 }, - {" O ", "ALA", 1.480f, 1.0f, 0.0f, 0.0f, 1, 1 }, - {" CB ", "ALA", 1.750f, 0.3f, 0.3f, 0.3f, -1, 1 }, - {" N ", "VAL", 1.625f, 0.0f, 0.0f, 1.0f, 1, 23 }, - {" CA ", "VAL", 1.750f, 0.3f, 0.3f, 0.3f, -1, 23 }, - {" C ", "VAL", 1.875f, 0.3f, 0.3f, 0.3f, 1, 23 }, - {" O ", "VAL", 1.480f, 1.0f, 0.0f, 0.0f, 1, 23 }, - {" CB ", "VAL", 1.750f, 0.3f, 0.3f, 0.3f, -1, 23 }, - {" CG1", "VAL", 1.750f, 0.3f, 0.3f, 0.3f, -1, 23 }, - {" CG2", "VAL", 1.750f, 0.3f, 0.3f, 0.3f, -1, 23 }, - {" N ", "LEU", 1.625f, 0.0f, 0.0f, 1.0f, 1, 13 }, - {" CA ", "LEU", 1.750f, 0.3f, 0.3f, 0.3f, -1, 13 }, - {" C ", "LEU", 1.875f, 0.3f, 0.3f, 0.3f, 1, 13 }, - {" O ", "LEU", 1.480f, 1.0f, 0.0f, 0.0f, 1, 13 }, - {" CB ", "LEU", 1.750f, 0.3f, 0.3f, 0.3f, -1, 13 }, - {" CG ", "LEU", 1.750f, 0.3f, 0.3f, 0.3f, -1, 13 }, - {" CD1", "LEU", 1.750f, 0.3f, 0.3f, 0.3f, -1, 13 }, - {" CD2", "LEU", 1.750f, 0.3f, 0.3f, 0.3f, -1, 13 }, - {" N ", "ILE", 1.625f, 0.0f, 0.0f, 1.0f, 1, 12 }, - {" CA ", "ILE", 1.750f, 0.3f, 0.3f, 0.3f, -1, 12 }, - {" C ", "ILE", 1.875f, 0.3f, 0.3f, 0.3f, 1, 12 }, - {" O ", "ILE", 1.480f, 1.0f, 0.0f, 0.0f, 1, 12 }, - {" CB ", "ILE", 1.750f, 0.3f, 0.3f, 0.3f, -1, 12 }, - {" CG1", "ILE", 1.750f, 0.3f, 0.3f, 0.3f, -1, 12 }, - {" CG2", "ILE", 1.750f, 0.3f, 0.3f, 0.3f, -1, 12 }, - {" CD1", "ILE", 1.750f, 0.3f, 0.3f, 0.3f, -1, 12 }, - {" N ", "MET", 1.625f, 0.0f, 0.0f, 1.0f, 1, 15 }, - {" CA ", "MET", 1.750f, 0.3f, 0.3f, 0.3f, -1, 15 }, - {" C ", "MET", 1.875f, 0.3f, 0.3f, 0.3f, 1, 15 }, - {" O ", "MET", 1.480f, 1.0f, 0.0f, 0.0f, 1, 15 }, - {" CB ", "MET", 1.750f, 0.3f, 0.3f, 0.3f, -1, 15 }, - {" CG ", "MET", 1.750f, 0.3f, 0.3f, 0.3f, -1, 15 }, - {" SD ", "MET", 1.775f, 1.0f, 1.0f, 0.0f, 1, 15 }, - {" CE ", "MET", 1.750f, 0.3f, 0.3f, 0.3f, -1, 15 }, - {" N ", "PRO", 1.625f, 0.0f, 0.0f, 1.0f, -1, 17 }, - {" CA ", "PRO", 1.750f, 0.3f, 0.3f, 0.3f, -1, 17 }, - {" C ", "PRO", 1.875f, 0.3f, 0.3f, 0.3f, 1, 17 }, - {" O ", "PRO", 1.480f, 1.0f, 0.0f, 0.0f, 1, 17 }, - {" CB ", "PRO", 1.750f, 0.3f, 0.3f, 0.3f, -1, 17 }, - {" CG ", "PRO", 1.750f, 0.3f, 0.3f, 0.3f, -1, 17 }, - {" CD ", "PRO", 1.750f, 0.3f, 0.3f, 0.3f, -1, 17 }, - {" N ", "PHE", 1.625f, 0.0f, 0.0f, 1.0f, 1, 16 }, - {" CA ", "PHE", 1.750f, 0.3f, 0.3f, 0.3f, -1, 16 }, - {" C ", "PHE", 1.875f, 0.3f, 0.3f, 0.3f, 1, 16 }, - {" O ", "PHE", 1.480f, 1.0f, 0.0f, 0.0f, 1, 16 }, - {" CB ", "PHE", 1.750f, 0.3f, 0.3f, 0.3f, -1, 16 }, - {" CG ", "PHE", 1.775f, 0.3f, 0.3f, 0.3f, -1, 16 }, - {" CD1", "PHE", 1.775f, 0.3f, 0.3f, 0.3f, -1, 16 }, - {" CD2", "PHE", 1.775f, 0.3f, 0.3f, 0.3f, -1, 16 }, - {" CE1", "PHE", 1.775f, 0.3f, 0.3f, 0.3f, -1, 16 }, - {" CE2", "PHE", 1.775f, 0.3f, 0.3f, 0.3f, -1, 16 }, - {" CZ ", "PHE", 1.775f, 0.3f, 0.3f, 0.3f, -1, 16 }, - {" N ", "TRP", 1.625f, 0.0f, 0.0f, 1.0f, 1, 20 }, - {" CA ", "TRP", 1.750f, 0.3f, 0.3f, 0.3f, -1, 20 }, - {" C ", "TRP", 1.875f, 0.3f, 0.3f, 0.3f, 1, 20 }, - {" O ", "TRP", 1.480f, 1.0f, 0.0f, 0.0f, 1, 20 }, - {" CB ", "TRP", 1.750f, 0.3f, 0.3f, 0.3f, -1, 20 }, - {" CG ", "TRP", 1.775f, 0.3f, 0.3f, 0.3f, -1, 20 }, - {" CD1", "TRP", 1.775f, 0.3f, 0.3f, 0.3f, -1, 20 }, - {" CD2", "TRP", 1.775f, 0.3f, 0.3f, 0.3f, -1, 20 }, - {" NE1", "TRP", 1.625f, 0.2f, 0.2f, 1.0f, 1, 20 }, - {" CE2", "TRP", 1.775f, 0.3f, 0.3f, 0.3f, -1, 20 }, - {" CE3", "TRP", 1.775f, 0.3f, 0.3f, 0.3f, -1, 20 }, - {" CZ2", "TRP", 1.775f, 0.3f, 0.3f, 0.3f, -1, 20 }, - {" CZ3", "TRP", 1.775f, 0.3f, 0.3f, 0.3f, -1, 20 }, - {" CH2", "TRP", 1.775f, 0.3f, 0.3f, 0.3f, -1, 20 }, - {" N ", "SER", 1.625f, 0.0f, 0.0f, 1.0f, 1, 18 }, - {" CA ", "SER", 1.750f, 0.3f, 0.3f, 0.3f, -1, 18 }, - {" C ", "SER", 1.875f, 0.3f, 0.3f, 0.3f, 1, 18 }, - {" O ", "SER", 1.480f, 1.0f, 0.0f, 0.0f, 1, 18 }, - {" CB ", "SER", 1.750f, 0.3f, 0.3f, 0.3f, -1, 18 }, - {" OG ", "SER", 1.560f, 1.0f, 0.0f, 0.0f, 1, 18 }, - {" N ", "THR", 1.625f, 0.0f, 0.0f, 1.0f, 1, 19 }, - {" CA ", "THR", 1.750f, 0.3f, 0.3f, 0.3f, -1, 19 }, - {" C ", "THR", 1.875f, 0.3f, 0.3f, 0.3f, 1, 19 }, - {" O ", "THR", 1.480f, 1.0f, 0.0f, 0.0f, 1, 19 }, - {" CB ", "THR", 1.750f, 0.3f, 0.3f, 0.3f, -1, 19 }, - {" OG1", "THR", 1.560f, 1.0f, 0.0f, 0.0f, 1, 19 }, - {" CG2", "THR", 1.750f, 0.3f, 0.3f, 0.3f, -1, 19 }, - {" N ", "ASN", 1.625f, 0.0f, 0.0f, 1.0f, 1, 3 }, - {" CA ", "ASN", 1.750f, 0.3f, 0.3f, 0.3f, -1, 3 }, - {" C ", "ASN", 1.875f, 0.3f, 0.3f, 0.3f, 1, 3 }, - {" O ", "ASN", 1.480f, 1.0f, 0.0f, 0.0f, 1, 3 }, - {" CB ", "ASN", 1.750f, 0.3f, 0.3f, 0.3f, -1, 3 }, - {" CG ", "ASN", 1.875f, 0.3f, 0.3f, 0.3f, 1, 3 }, - {" OD1", "ASN", 1.480f, 1.0f, 0.0f, 0.0f, 1, 3 }, - {" ND2", "ASN", 1.625f, 0.2f, 0.2f, 1.0f, 1, 3 }, - {" N ", "GLN", 1.625f, 0.0f, 0.0f, 1.0f, 1, 7 }, - {" CA ", "GLN", 1.750f, 0.3f, 0.3f, 0.3f, -1, 7 }, - {" C ", "GLN", 1.875f, 0.3f, 0.3f, 0.3f, 1, 7 }, - {" O ", "GLN", 1.480f, 1.0f, 0.0f, 0.0f, 1, 7 }, - {" CB ", "GLN", 1.750f, 0.3f, 0.3f, 0.3f, -1, 7 }, - {" CG ", "GLN", 1.750f, 0.3f, 0.3f, 0.3f, -1, 7 }, - {" CD ", "GLN", 1.875f, 0.3f, 0.3f, 0.3f, 1, 7 }, - {" OE1", "GLN", 1.480f, 1.0f, 0.0f, 0.0f, 1, 7 }, - {" NE2", "GLN", 1.625f, 0.2f, 0.2f, 1.0f, 1, 7 }, - {" N ", "TYR", 1.625f, 0.0f, 0.0f, 1.0f, 1, 21 }, - {" CA ", "TYR", 1.750f, 0.3f, 0.3f, 0.3f, -1, 21 }, - {" C ", "TYR", 1.875f, 0.3f, 0.3f, 0.3f, 1, 21 }, - {" O ", "TYR", 1.480f, 1.0f, 0.0f, 0.0f, 1, 21 }, - {" CB ", "TYR", 1.750f, 0.3f, 0.3f, 0.3f, -1, 21 }, - {" CG ", "TYR", 1.775f, 0.3f, 0.3f, 0.3f, -1, 21 }, - {" CD1", "TYR", 1.775f, 0.3f, 0.3f, 0.3f, -1, 21 }, - {" CD2", "TYR", 1.775f, 0.3f, 0.3f, 0.3f, -1, 21 }, - {" CE1", "TYR", 1.775f, 0.3f, 0.3f, 0.3f, -1, 21 }, - {" CE2", "TYR", 1.775f, 0.3f, 0.3f, 0.3f, -1, 21 }, - {" CZ ", "TYR", 1.775f, 0.3f, 0.3f, 0.3f, -1, 21 }, - {" OH ", "TYR", 1.535f, 1.0f, 0.0f, 0.0f, 1, 21 }, - {" N ", "CYS", 1.625f, 0.0f, 0.0f, 1.0f, 1, 6 }, - {" CA ", "CYS", 1.750f, 0.3f, 0.3f, 0.3f, -1, 6 }, - {" C ", "CYS", 1.875f, 0.3f, 0.3f, 0.3f, 1, 6 }, - {" O ", "CYS", 1.480f, 1.0f, 0.0f, 0.0f, 1, 6 }, - {" CB ", "CYS", 1.750f, 0.3f, 0.3f, 0.3f, -1, 6 }, - {" SG ", "CYS", 1.775f, 1.0f, 1.0f, 0.0f, 1, 6 }, - {" N ", "LYS", 1.625f, 0.0f, 0.0f, 1.0f, 1, 14 }, - {" CA ", "LYS", 1.750f, 0.3f, 0.3f, 0.3f, -1, 14 }, - {" C ", "LYS", 1.875f, 0.3f, 0.3f, 0.3f, 1, 14 }, - {" O ", "LYS", 1.480f, 1.0f, 0.0f, 0.0f, 1, 14 }, - {" CB ", "LYS", 1.750f, 0.3f, 0.3f, 0.3f, -1, 14 }, - {" CG ", "LYS", 1.750f, 0.3f, 0.3f, 0.3f, -1, 14 }, - {" CD ", "LYS", 1.750f, 0.3f, 0.3f, 0.3f, -1, 14 }, - {" CE ", "LYS", 1.750f, 0.3f, 0.3f, 0.3f, -1, 14 }, - {" NZ ", "LYS", 1.625f, 0.2f, 0.2f, 1.0f, 1, 14 }, - {" N ", "ARG", 1.625f, 0.0f, 0.0f, 1.0f, 1, 2 }, - {" CA ", "ARG", 1.750f, 0.3f, 0.3f, 0.3f, -1, 2 }, - {" C ", "ARG", 1.875f, 0.3f, 0.3f, 0.3f, 1, 2 }, - {" O ", "ARG", 1.480f, 1.0f, 0.0f, 0.0f, 1, 2 }, - {" CB ", "ARG", 1.750f, 0.3f, 0.3f, 0.3f, -1, 2 }, - {" CG ", "ARG", 1.750f, 0.3f, 0.3f, 0.3f, -1, 2 }, - {" CD ", "ARG", 1.750f, 0.3f, 0.3f, 0.3f, -1, 2 }, - {" NE ", "ARG", 1.625f, 0.2f, 0.2f, 1.0f, 1, 2 }, - {" CZ ", "ARG", 1.125f, 0.3f, 0.3f, 0.3f, 1, 2 }, - {" NH1", "ARG", 1.625f, 0.2f, 0.2f, 1.0f, 1, 2 }, - {" NH2", "ARG", 1.625f, 0.2f, 0.2f, 1.0f, 1, 2 }, - {" N ", "HIS", 1.625f, 0.0f, 0.0f, 1.0f, 1, 11 }, - {" CA ", "HIS", 1.750f, 0.3f, 0.3f, 0.3f, -1, 11 }, - {" C ", "HIS", 1.875f, 0.3f, 0.3f, 0.3f, 1, 11 }, - {" O ", "HIS", 1.480f, 1.0f, 0.0f, 0.0f, 1, 11 }, - {" CB ", "HIS", 1.750f, 0.3f, 0.3f, 0.3f, -1, 11 }, - {" CG ", "HIS", 1.775f, 0.3f, 0.3f, 0.3f, -1, 11 }, - {" ND1", "HIS", 1.625f, 0.2f, 0.2f, 1.0f, 1, 11 }, - {" CD2", "HIS", 1.775f, 0.3f, 0.3f, 0.3f, -1, 11 }, - {" CE1", "HIS", 1.775f, 0.3f, 0.3f, 0.3f, 1, 11 }, - {" NE2", "HIS", 1.625f, 0.2f, 0.2f, 1.0f, 1, 11 }, - {" N ", "ASP", 1.625f, 0.0f, 0.0f, 1.0f, 1, 4 }, - {" CA ", "ASP", 1.750f, 0.3f, 0.3f, 0.3f, -1, 4 }, - {" C ", "ASP", 1.875f, 0.3f, 0.3f, 0.3f, 1, 4 }, - {" O ", "ASP", 1.480f, 1.0f, 0.0f, 0.0f, 1, 4 }, - {" CB ", "ASP", 1.750f, 0.3f, 0.3f, 0.3f, -1, 4 }, - {" CG ", "ASP", 1.875f, 0.3f, 0.3f, 0.3f, 1, 4 }, - {" OD1", "ASP", 1.480f, 1.0f, 1.0f, 1.0f, 1, 4 }, - {" OD2", "ASP", 1.480f, 1.0f, 0.0f, 0.0f, 1, 4 }, - {" N ", "GLU", 1.625f, 0.0f, 0.0f, 1.0f, 1, 8 }, - {" CA ", "GLU", 1.750f, 0.3f, 0.3f, 0.3f, -1, 8 }, - {" C ", "GLU", 1.875f, 0.3f, 0.3f, 0.3f, 1, 8 }, - {" O ", "GLU", 1.480f, 1.0f, 0.0f, 0.0f, 1, 8 }, - {" CB ", "GLU", 1.750f, 0.3f, 0.3f, 0.3f, -1, 8 }, - {" CG ", "GLU", 1.750f, 0.3f, 0.3f, 0.3f, -1, 8 }, - {" CD ", "GLU", 1.875f, 0.3f, 0.3f, 0.3f, 1, 8 }, - {" OE1", "GLU", 1.480f, 1.0f, 0.0f, 0.0f, 1, 8 }, - {" OE2", "GLU", 1.480f, 1.0f, 0.0f, 0.0f, 1, 8 } +static PDBelementInformation PDBelementTable[MAX_BIOCHEM_ELEMENTS] = { + {" N ", "GLY", 1.625f, 0.0f, 0.0f, 1.0f, 1, 10}, + {" CA ", "GLY", 1.750f, 0.3f, 0.3f, 0.3f, -1, 10}, + {" C ", "GLY", 1.875f, 0.3f, 0.3f, 0.3f, 1, 10}, + {" O ", "GLY", 1.480f, 1.0f, 0.0f, 0.0f, 1, 10}, + {" N ", "ALA", 1.625f, 0.0f, 0.0f, 1.0f, 1, 1}, + {" CA ", "ALA", 1.750f, 0.3f, 0.3f, 0.3f, -1, 1}, + {" C ", "ALA", 1.875f, 0.3f, 0.3f, 0.3f, 1, 1}, + {" O ", "ALA", 1.480f, 1.0f, 0.0f, 0.0f, 1, 1}, + {" CB ", "ALA", 1.750f, 0.3f, 0.3f, 0.3f, -1, 1}, + {" N ", "VAL", 1.625f, 0.0f, 0.0f, 1.0f, 1, 23}, + {" CA ", "VAL", 1.750f, 0.3f, 0.3f, 0.3f, -1, 23}, + {" C ", "VAL", 1.875f, 0.3f, 0.3f, 0.3f, 1, 23}, + {" O ", "VAL", 1.480f, 1.0f, 0.0f, 0.0f, 1, 23}, + {" CB ", "VAL", 1.750f, 0.3f, 0.3f, 0.3f, -1, 23}, + {" CG1", "VAL", 1.750f, 0.3f, 0.3f, 0.3f, -1, 23}, + {" CG2", "VAL", 1.750f, 0.3f, 0.3f, 0.3f, -1, 23}, + {" N ", "LEU", 1.625f, 0.0f, 0.0f, 1.0f, 1, 13}, + {" CA ", "LEU", 1.750f, 0.3f, 0.3f, 0.3f, -1, 13}, + {" C ", "LEU", 1.875f, 0.3f, 0.3f, 0.3f, 1, 13}, + {" O ", "LEU", 1.480f, 1.0f, 0.0f, 0.0f, 1, 13}, + {" CB ", "LEU", 1.750f, 0.3f, 0.3f, 0.3f, -1, 13}, + {" CG ", "LEU", 1.750f, 0.3f, 0.3f, 0.3f, -1, 13}, + {" CD1", "LEU", 1.750f, 0.3f, 0.3f, 0.3f, -1, 13}, + {" CD2", "LEU", 1.750f, 0.3f, 0.3f, 0.3f, -1, 13}, + {" N ", "ILE", 1.625f, 0.0f, 0.0f, 1.0f, 1, 12}, + {" CA ", "ILE", 1.750f, 0.3f, 0.3f, 0.3f, -1, 12}, + {" C ", "ILE", 1.875f, 0.3f, 0.3f, 0.3f, 1, 12}, + {" O ", "ILE", 1.480f, 1.0f, 0.0f, 0.0f, 1, 12}, + {" CB ", "ILE", 1.750f, 0.3f, 0.3f, 0.3f, -1, 12}, + {" CG1", "ILE", 1.750f, 0.3f, 0.3f, 0.3f, -1, 12}, + {" CG2", "ILE", 1.750f, 0.3f, 0.3f, 0.3f, -1, 12}, + {" CD1", "ILE", 1.750f, 0.3f, 0.3f, 0.3f, -1, 12}, + {" N ", "MET", 1.625f, 0.0f, 0.0f, 1.0f, 1, 15}, + {" CA ", "MET", 1.750f, 0.3f, 0.3f, 0.3f, -1, 15}, + {" C ", "MET", 1.875f, 0.3f, 0.3f, 0.3f, 1, 15}, + {" O ", "MET", 1.480f, 1.0f, 0.0f, 0.0f, 1, 15}, + {" CB ", "MET", 1.750f, 0.3f, 0.3f, 0.3f, -1, 15}, + {" CG ", "MET", 1.750f, 0.3f, 0.3f, 0.3f, -1, 15}, + {" SD ", "MET", 1.775f, 1.0f, 1.0f, 0.0f, 1, 15}, + {" CE ", "MET", 1.750f, 0.3f, 0.3f, 0.3f, -1, 15}, + {" N ", "PRO", 1.625f, 0.0f, 0.0f, 1.0f, -1, 17}, + {" CA ", "PRO", 1.750f, 0.3f, 0.3f, 0.3f, -1, 17}, + {" C ", "PRO", 1.875f, 0.3f, 0.3f, 0.3f, 1, 17}, + {" O ", "PRO", 1.480f, 1.0f, 0.0f, 0.0f, 1, 17}, + {" CB ", "PRO", 1.750f, 0.3f, 0.3f, 0.3f, -1, 17}, + {" CG ", "PRO", 1.750f, 0.3f, 0.3f, 0.3f, -1, 17}, + {" CD ", "PRO", 1.750f, 0.3f, 0.3f, 0.3f, -1, 17}, + {" N ", "PHE", 1.625f, 0.0f, 0.0f, 1.0f, 1, 16}, + {" CA ", "PHE", 1.750f, 0.3f, 0.3f, 0.3f, -1, 16}, + {" C ", "PHE", 1.875f, 0.3f, 0.3f, 0.3f, 1, 16}, + {" O ", "PHE", 1.480f, 1.0f, 0.0f, 0.0f, 1, 16}, + {" CB ", "PHE", 1.750f, 0.3f, 0.3f, 0.3f, -1, 16}, + {" CG ", "PHE", 1.775f, 0.3f, 0.3f, 0.3f, -1, 16}, + {" CD1", "PHE", 1.775f, 0.3f, 0.3f, 0.3f, -1, 16}, + {" CD2", "PHE", 1.775f, 0.3f, 0.3f, 0.3f, -1, 16}, + {" CE1", "PHE", 1.775f, 0.3f, 0.3f, 0.3f, -1, 16}, + {" CE2", "PHE", 1.775f, 0.3f, 0.3f, 0.3f, -1, 16}, + {" CZ ", "PHE", 1.775f, 0.3f, 0.3f, 0.3f, -1, 16}, + {" N ", "TRP", 1.625f, 0.0f, 0.0f, 1.0f, 1, 20}, + {" CA ", "TRP", 1.750f, 0.3f, 0.3f, 0.3f, -1, 20}, + {" C ", "TRP", 1.875f, 0.3f, 0.3f, 0.3f, 1, 20}, + {" O ", "TRP", 1.480f, 1.0f, 0.0f, 0.0f, 1, 20}, + {" CB ", "TRP", 1.750f, 0.3f, 0.3f, 0.3f, -1, 20}, + {" CG ", "TRP", 1.775f, 0.3f, 0.3f, 0.3f, -1, 20}, + {" CD1", "TRP", 1.775f, 0.3f, 0.3f, 0.3f, -1, 20}, + {" CD2", "TRP", 1.775f, 0.3f, 0.3f, 0.3f, -1, 20}, + {" NE1", "TRP", 1.625f, 0.2f, 0.2f, 1.0f, 1, 20}, + {" CE2", "TRP", 1.775f, 0.3f, 0.3f, 0.3f, -1, 20}, + {" CE3", "TRP", 1.775f, 0.3f, 0.3f, 0.3f, -1, 20}, + {" CZ2", "TRP", 1.775f, 0.3f, 0.3f, 0.3f, -1, 20}, + {" CZ3", "TRP", 1.775f, 0.3f, 0.3f, 0.3f, -1, 20}, + {" CH2", "TRP", 1.775f, 0.3f, 0.3f, 0.3f, -1, 20}, + {" N ", "SER", 1.625f, 0.0f, 0.0f, 1.0f, 1, 18}, + {" CA ", "SER", 1.750f, 0.3f, 0.3f, 0.3f, -1, 18}, + {" C ", "SER", 1.875f, 0.3f, 0.3f, 0.3f, 1, 18}, + {" O ", "SER", 1.480f, 1.0f, 0.0f, 0.0f, 1, 18}, + {" CB ", "SER", 1.750f, 0.3f, 0.3f, 0.3f, -1, 18}, + {" OG ", "SER", 1.560f, 1.0f, 0.0f, 0.0f, 1, 18}, + {" N ", "THR", 1.625f, 0.0f, 0.0f, 1.0f, 1, 19}, + {" CA ", "THR", 1.750f, 0.3f, 0.3f, 0.3f, -1, 19}, + {" C ", "THR", 1.875f, 0.3f, 0.3f, 0.3f, 1, 19}, + {" O ", "THR", 1.480f, 1.0f, 0.0f, 0.0f, 1, 19}, + {" CB ", "THR", 1.750f, 0.3f, 0.3f, 0.3f, -1, 19}, + {" OG1", "THR", 1.560f, 1.0f, 0.0f, 0.0f, 1, 19}, + {" CG2", "THR", 1.750f, 0.3f, 0.3f, 0.3f, -1, 19}, + {" N ", "ASN", 1.625f, 0.0f, 0.0f, 1.0f, 1, 3}, + {" CA ", "ASN", 1.750f, 0.3f, 0.3f, 0.3f, -1, 3}, + {" C ", "ASN", 1.875f, 0.3f, 0.3f, 0.3f, 1, 3}, + {" O ", "ASN", 1.480f, 1.0f, 0.0f, 0.0f, 1, 3}, + {" CB ", "ASN", 1.750f, 0.3f, 0.3f, 0.3f, -1, 3}, + {" CG ", "ASN", 1.875f, 0.3f, 0.3f, 0.3f, 1, 3}, + {" OD1", "ASN", 1.480f, 1.0f, 0.0f, 0.0f, 1, 3}, + {" ND2", "ASN", 1.625f, 0.2f, 0.2f, 1.0f, 1, 3}, + {" N ", "GLN", 1.625f, 0.0f, 0.0f, 1.0f, 1, 7}, + {" CA ", "GLN", 1.750f, 0.3f, 0.3f, 0.3f, -1, 7}, + {" C ", "GLN", 1.875f, 0.3f, 0.3f, 0.3f, 1, 7}, + {" O ", "GLN", 1.480f, 1.0f, 0.0f, 0.0f, 1, 7}, + {" CB ", "GLN", 1.750f, 0.3f, 0.3f, 0.3f, -1, 7}, + {" CG ", "GLN", 1.750f, 0.3f, 0.3f, 0.3f, -1, 7}, + {" CD ", "GLN", 1.875f, 0.3f, 0.3f, 0.3f, 1, 7}, + {" OE1", "GLN", 1.480f, 1.0f, 0.0f, 0.0f, 1, 7}, + {" NE2", "GLN", 1.625f, 0.2f, 0.2f, 1.0f, 1, 7}, + {" N ", "TYR", 1.625f, 0.0f, 0.0f, 1.0f, 1, 21}, + {" CA ", "TYR", 1.750f, 0.3f, 0.3f, 0.3f, -1, 21}, + {" C ", "TYR", 1.875f, 0.3f, 0.3f, 0.3f, 1, 21}, + {" O ", "TYR", 1.480f, 1.0f, 0.0f, 0.0f, 1, 21}, + {" CB ", "TYR", 1.750f, 0.3f, 0.3f, 0.3f, -1, 21}, + {" CG ", "TYR", 1.775f, 0.3f, 0.3f, 0.3f, -1, 21}, + {" CD1", "TYR", 1.775f, 0.3f, 0.3f, 0.3f, -1, 21}, + {" CD2", "TYR", 1.775f, 0.3f, 0.3f, 0.3f, -1, 21}, + {" CE1", "TYR", 1.775f, 0.3f, 0.3f, 0.3f, -1, 21}, + {" CE2", "TYR", 1.775f, 0.3f, 0.3f, 0.3f, -1, 21}, + {" CZ ", "TYR", 1.775f, 0.3f, 0.3f, 0.3f, -1, 21}, + {" OH ", "TYR", 1.535f, 1.0f, 0.0f, 0.0f, 1, 21}, + {" N ", "CYS", 1.625f, 0.0f, 0.0f, 1.0f, 1, 6}, + {" CA ", "CYS", 1.750f, 0.3f, 0.3f, 0.3f, -1, 6}, + {" C ", "CYS", 1.875f, 0.3f, 0.3f, 0.3f, 1, 6}, + {" O ", "CYS", 1.480f, 1.0f, 0.0f, 0.0f, 1, 6}, + {" CB ", "CYS", 1.750f, 0.3f, 0.3f, 0.3f, -1, 6}, + {" SG ", "CYS", 1.775f, 1.0f, 1.0f, 0.0f, 1, 6}, + {" N ", "LYS", 1.625f, 0.0f, 0.0f, 1.0f, 1, 14}, + {" CA ", "LYS", 1.750f, 0.3f, 0.3f, 0.3f, -1, 14}, + {" C ", "LYS", 1.875f, 0.3f, 0.3f, 0.3f, 1, 14}, + {" O ", "LYS", 1.480f, 1.0f, 0.0f, 0.0f, 1, 14}, + {" CB ", "LYS", 1.750f, 0.3f, 0.3f, 0.3f, -1, 14}, + {" CG ", "LYS", 1.750f, 0.3f, 0.3f, 0.3f, -1, 14}, + {" CD ", "LYS", 1.750f, 0.3f, 0.3f, 0.3f, -1, 14}, + {" CE ", "LYS", 1.750f, 0.3f, 0.3f, 0.3f, -1, 14}, + {" NZ ", "LYS", 1.625f, 0.2f, 0.2f, 1.0f, 1, 14}, + {" N ", "ARG", 1.625f, 0.0f, 0.0f, 1.0f, 1, 2}, + {" CA ", "ARG", 1.750f, 0.3f, 0.3f, 0.3f, -1, 2}, + {" C ", "ARG", 1.875f, 0.3f, 0.3f, 0.3f, 1, 2}, + {" O ", "ARG", 1.480f, 1.0f, 0.0f, 0.0f, 1, 2}, + {" CB ", "ARG", 1.750f, 0.3f, 0.3f, 0.3f, -1, 2}, + {" CG ", "ARG", 1.750f, 0.3f, 0.3f, 0.3f, -1, 2}, + {" CD ", "ARG", 1.750f, 0.3f, 0.3f, 0.3f, -1, 2}, + {" NE ", "ARG", 1.625f, 0.2f, 0.2f, 1.0f, 1, 2}, + {" CZ ", "ARG", 1.125f, 0.3f, 0.3f, 0.3f, 1, 2}, + {" NH1", "ARG", 1.625f, 0.2f, 0.2f, 1.0f, 1, 2}, + {" NH2", "ARG", 1.625f, 0.2f, 0.2f, 1.0f, 1, 2}, + {" N ", "HIS", 1.625f, 0.0f, 0.0f, 1.0f, 1, 11}, + {" CA ", "HIS", 1.750f, 0.3f, 0.3f, 0.3f, -1, 11}, + {" C ", "HIS", 1.875f, 0.3f, 0.3f, 0.3f, 1, 11}, + {" O ", "HIS", 1.480f, 1.0f, 0.0f, 0.0f, 1, 11}, + {" CB ", "HIS", 1.750f, 0.3f, 0.3f, 0.3f, -1, 11}, + {" CG ", "HIS", 1.775f, 0.3f, 0.3f, 0.3f, -1, 11}, + {" ND1", "HIS", 1.625f, 0.2f, 0.2f, 1.0f, 1, 11}, + {" CD2", "HIS", 1.775f, 0.3f, 0.3f, 0.3f, -1, 11}, + {" CE1", "HIS", 1.775f, 0.3f, 0.3f, 0.3f, 1, 11}, + {" NE2", "HIS", 1.625f, 0.2f, 0.2f, 1.0f, 1, 11}, + {" N ", "ASP", 1.625f, 0.0f, 0.0f, 1.0f, 1, 4}, + {" CA ", "ASP", 1.750f, 0.3f, 0.3f, 0.3f, -1, 4}, + {" C ", "ASP", 1.875f, 0.3f, 0.3f, 0.3f, 1, 4}, + {" O ", "ASP", 1.480f, 1.0f, 0.0f, 0.0f, 1, 4}, + {" CB ", "ASP", 1.750f, 0.3f, 0.3f, 0.3f, -1, 4}, + {" CG ", "ASP", 1.875f, 0.3f, 0.3f, 0.3f, 1, 4}, + {" OD1", "ASP", 1.480f, 1.0f, 1.0f, 1.0f, 1, 4}, + {" OD2", "ASP", 1.480f, 1.0f, 0.0f, 0.0f, 1, 4}, + {" N ", "GLU", 1.625f, 0.0f, 0.0f, 1.0f, 1, 8}, + {" CA ", "GLU", 1.750f, 0.3f, 0.3f, 0.3f, -1, 8}, + {" C ", "GLU", 1.875f, 0.3f, 0.3f, 0.3f, 1, 8}, + {" O ", "GLU", 1.480f, 1.0f, 0.0f, 0.0f, 1, 8}, + {" CB ", "GLU", 1.750f, 0.3f, 0.3f, 0.3f, -1, 8}, + {" CG ", "GLU", 1.750f, 0.3f, 0.3f, 0.3f, -1, 8}, + {" CD ", "GLU", 1.875f, 0.3f, 0.3f, 0.3f, 1, 8}, + {" OE1", "GLU", 1.480f, 1.0f, 0.0f, 0.0f, 1, 8}, + {" OE2", "GLU", 1.480f, 1.0f, 0.0f, 0.0f, 1, 8} // {"SI ", "UNL", 1.875f, 1.0f, 1.0f, 1.0f, 1, 27 }, // {" O ", "UNL", 1.480f, 1.0f, 0.0f, 0.0f, 1, 27 } }; /// Map of residueName, atomName to PDBelementInformation -static std::map > PDBelementMap; +static std::map> + PDBelementMap; } // End namespace pdbreader_detail /// @endcond - /** * @brief Basic atom containing position and radius */ struct Atom { - Vector3f pos; /**< @brief position */ - double radius; /**< @brief radius */ + Vector3f pos; /**< @brief position */ + double radius; /**< @brief radius */ }; /** * @brief Initialize the element map */ -static void initElementMap() -{ - // If the element map is empty build it... - if (pdbreader_detail::PDBelementMap.size() == 0) - { - for (int i = 0; i < pdbreader_detail::MAX_BIOCHEM_ELEMENTS; ++i) - { - const std::string atomName = pdbreader_detail::PDBelementTable[i].atomName; - const std::string residueName = pdbreader_detail::PDBelementTable[i].residueName; - pdbreader_detail::PDBelementMap[residueName][atomName] = pdbreader_detail::PDBelementTable[i]; - } +static void initElementMap() { + // If the element map is empty build it... + if (pdbreader_detail::PDBelementMap.size() == 0) { + for (int i = 0; i < pdbreader_detail::MAX_BIOCHEM_ELEMENTS; ++i) { + const std::string atomName = + pdbreader_detail::PDBelementTable[i].atomName; + const std::string residueName = + pdbreader_detail::PDBelementTable[i].residueName; + pdbreader_detail::PDBelementMap[residueName][atomName] = + pdbreader_detail::PDBelementTable[i]; } + } } /** @@ -291,65 +283,52 @@ static void initElementMap() * @return True on success */ template -bool readPDB(const std::string &filename, Inserter inserter) -{ - std::ifstream infile(filename); - std::string line; - - initElementMap(); // Init the element map if it - - if (infile.is_open()) - { - while (std::getline(infile, line)) - { - std::smatch match; - if (std::regex_match(line, match, pdbreader_detail::atom)) - { - Atom atom; - // See PDB file formatting guidelines - float x = std::atof(line.substr(30, 8).c_str()); - float y = std::atof(line.substr(38, 8).c_str()); - float z = std::atof(line.substr(46, 8).c_str()); - atom.pos = Vector({x, y, z}); - - atom.radius = 1.0f; // default radius - std::string atomName = line.substr(12, 4); - std::string residueName = line.substr(17, 3); - - auto innerMapIT = pdbreader_detail::PDBelementMap.find(residueName); - - if (innerMapIT != pdbreader_detail::PDBelementMap.end()) - { - auto innerMap = innerMapIT->second; - auto typeIT = innerMap.find(atomName); - if (typeIT != innerMap.end()) - { - atom.radius = typeIT->second.radius; - } - else - { - std::cout << "Could not find atomtype of '" - << atomName << "' in residue '" - << residueName << "'. " - << "Using default radius." << std::endl; - } - } - else - { - std::cout << "Could not find ResidueName '" - << residueName << "' in table. " - << "Using default radius." << std::endl; - } - *inserter++ = atom; - } +bool readPDB(const std::string &filename, Inserter inserter) { + std::ifstream infile(filename); + std::string line; + + initElementMap(); // Init the element map if it + + if (infile.is_open()) { + while (std::getline(infile, line)) { + std::smatch match; + if (std::regex_match(line, match, pdbreader_detail::atom)) { + Atom atom; + // See PDB file formatting guidelines + float x = std::atof(line.substr(30, 8).c_str()); + float y = std::atof(line.substr(38, 8).c_str()); + float z = std::atof(line.substr(46, 8).c_str()); + atom.pos = Vector({x, y, z}); + + atom.radius = 1.0f; // default radius + std::string atomName = line.substr(12, 4); + std::string residueName = line.substr(17, 3); + + auto innerMapIT = pdbreader_detail::PDBelementMap.find(residueName); + + if (innerMapIT != pdbreader_detail::PDBelementMap.end()) { + auto innerMap = innerMapIT->second; + auto typeIT = innerMap.find(atomName); + if (typeIT != innerMap.end()) { + atom.radius = typeIT->second.radius; + } else { + std::cout << "Could not find atomtype of '" << atomName + << "' in residue '" << residueName << "'. " + << "Using default radius." << std::endl; + } + } else { + std::cout << "Could not find ResidueName '" << residueName + << "' in table. " + << "Using default radius." << std::endl; } - return true; - } - else - { - std::cerr << "Unable to open \"" << filename << "\"" << std::endl; - return false; + *inserter++ = atom; + } } + return true; + } else { + std::cerr << "Unable to open \"" << filename << "\"" << std::endl; + return false; + } } /** @@ -363,77 +342,70 @@ bool readPDB(const std::string &filename, Inserter inserter) * @return True on success */ template -bool readPQR(const std::string &filename, Inserter inserter) -{ - std::ifstream infile(filename); - std::string line; - - initElementMap(); // Init the element map if it - - if (infile.is_open()) - { - while (std::getline(infile, line)) - { - std::smatch match; - if (std::regex_match(line, match, pdbreader_detail::atom)) - { - Atom atom; - // See PDB file formatting guidelines - float x = std::atof(line.substr(30, 8).c_str()); - float y = std::atof(line.substr(38, 8).c_str()); - float z = std::atof(line.substr(46, 8).c_str()); - atom.pos = Vector({x, y, z}); - atom.radius = std::atof(line.substr(62, 7).c_str()); - *inserter++ = atom; - } - } - return true; - } - else - { - std::cerr << "Unable to open \"" << filename << "\"" << std::endl; - return false; +bool readPQR(const std::string &filename, Inserter inserter) { + std::ifstream infile(filename); + std::string line; + + initElementMap(); // Init the element map if it + + if (infile.is_open()) { + while (std::getline(infile, line)) { + std::smatch match; + if (std::regex_match(line, match, pdbreader_detail::atom)) { + Atom atom; + // See PDB file formatting guidelines + float x = std::atof(line.substr(30, 8).c_str()); + float y = std::atof(line.substr(38, 8).c_str()); + float z = std::atof(line.substr(46, 8).c_str()); + atom.pos = Vector({x, y, z}); + atom.radius = std::atof(line.substr(62, 7).c_str()); + *inserter++ = atom; + } } + return true; + } else { + std::cerr << "Unable to open \"" << filename << "\"" << std::endl; + return false; + } } template -void getMinMax(Iterator begin, Iterator end, Vector3f &min, Vector3f &max, BlurFunc &&f) -{ - float maxRad = 0.0; - float tmpRad; - - min[0] = min[1] = min[2] = std::numeric_limits::infinity(); - max[0] = max[1] = max[2] = -std::numeric_limits::infinity(); - - for (auto curr = begin; curr != end; ++curr) - { - float x = curr->pos[0]; - float y = curr->pos[1]; - float z = curr->pos[2]; - - if (min[0] > x) - min[0] = x; - if (max[0] < x) - max[0] = x; - - if (min[1] > y) - min[1] = y; - if (max[1] < y) - max[1] = y; - - if (min[2] > z) - min[2] = z; - if (max[2] < z) - max[2] = z; - - tmpRad = f(curr->radius); // * sqrt(1.0 + log(pdbreader_detail::EPSILON) - // / blobbyness); - if (maxRad < tmpRad) - maxRad = tmpRad; - } - - min -= Vector3f({maxRad, maxRad, maxRad}); - max += Vector3f({maxRad, maxRad, maxRad}); +void getMinMax(Iterator begin, Iterator end, Vector3f &min, Vector3f &max, + BlurFunc &&f) { + float maxRad = 0.0; + float tmpRad; + + min[0] = min[1] = min[2] = std::numeric_limits::infinity(); + max[0] = max[1] = max[2] = -std::numeric_limits::infinity(); + + for (auto curr = begin; curr != end; ++curr) { + float x = curr->pos[0]; + float y = curr->pos[1]; + float z = curr->pos[2]; + + if (min[0] > x) + min[0] = x; + if (max[0] < x) + max[0] = x; + + if (min[1] > y) + min[1] = y; + if (max[1] < y) + max[1] = y; + + if (min[2] > z) + min[2] = z; + if (max[2] < z) + max[2] = z; + + tmpRad = f(curr->radius); // * sqrt(1.0 + log(pdbreader_detail::EPSILON) + // / blobbyness); + if (maxRad < tmpRad) + maxRad = tmpRad; + } + + min -= Vector3f({maxRad, maxRad, maxRad}); + max += Vector3f({maxRad, maxRad, maxRad}); } /** @@ -450,82 +422,74 @@ void getMinMax(Iterator begin, Iterator end, Vector3f &min, Vector3f &max, BlurF * @tparam Iterator Typename of the iterator */ template -void blurAtoms(Iterator begin, Iterator end, - float* dataset, - const Vector3f &min, - const Vector3f &maxMin, - const Vector3i &dim, - float blobbyness) -{ - - // Functor to calculate gaussian blur - auto evalDensity = [blobbyness](const Atom &atom, Vector3f &pnt, float maxRadius) - -> float { - double expval; - - Vector3f tmp = atom.pos - pnt; - double r = tmp|tmp; - double r0 = atom.radius*atom.radius; - - // expval = BLOBBYNESS*(r/r0 - 1.0); - expval = blobbyness*(r-r0); - - // Truncate gaussian - if (sqrt(r) > maxRadius) - { - return 0.0; - } - return (float) exp(expval); - }; - - Vector3f span; - span = (maxMin).ElementwiseDivision(static_cast((dim - Vector3i({1, 1, 1})))); - - float radFactor = sqrt(1.0 + log(pdbreader_detail::EPSILON)/(2.0 * blobbyness)); - - for (auto curr = begin; curr != end; ++curr) - { - float maxRad = curr->radius * radFactor; - // compute the dataset coordinates of the atom's center - Vector3f tmpVec = (curr->pos-min).ElementwiseDivision(span); - Vector3i c; - std::transform(tmpVec.begin(), tmpVec.end(), c.begin(), [](float v) -> int { - return round(v); - }); - - // std::cout << "Max Radius: " << maxRad << std::endl; - - // compute the bounding box of the atom (maxRad^3) - Vector3i amin; - Vector3i amax; - for (int j = 0; j < 3; ++j) - { - int tmp; - float tmpRad = maxRad/span[j]; - - tmp = (int)(c[j] - tmpRad - 1); - amin[j] = (tmp < 0) ? 0 : tmp; // check if tmp is < 0 - tmp = (int)(c[j] + tmpRad + 1); - amax[j] = (tmp > (dim[j] - 1)) ? (dim[j] - 1) : tmp; - } +void blurAtoms(Iterator begin, Iterator end, float *dataset, + const Vector3f &min, const Vector3f &maxMin, const Vector3i &dim, + float blobbyness) { + + // Functor to calculate gaussian blur + auto evalDensity = [blobbyness](const Atom &atom, Vector3f &pnt, + float maxRadius) -> float { + double expval; + + Vector3f tmp = atom.pos - pnt; + double r = tmp | tmp; + double r0 = atom.radius * atom.radius; + + // expval = BLOBBYNESS*(r/r0 - 1.0); + expval = blobbyness * (r - r0); - // std::cout << amin << " " << amax << std::endl; - - // Blur kernel in bounding box - for (int k = amin[2]; k <= amax[2]; k++) - { - for (int j = amin[1]; j <= amax[1]; j++) - { - for (int i = amin[0]; i <= amax[0]; i++) - { - Vector3f pnt = min + Vector3f({static_cast(i), - static_cast(j), - static_cast(k)}).ElementwiseProduct(span); - dataset[Vect2Index(i, j, k, dim)] += evalDensity(*curr, pnt, maxRad); - } - } + // Truncate gaussian + if (sqrt(r) > maxRadius) { + return 0.0; + } + return (float)exp(expval); + }; + + Vector3f span; + span = (maxMin).ElementwiseDivision( + static_cast((dim - Vector3i({1, 1, 1})))); + + float radFactor = + sqrt(1.0 + log(pdbreader_detail::EPSILON) / (2.0 * blobbyness)); + + for (auto curr = begin; curr != end; ++curr) { + float maxRad = curr->radius * radFactor; + // compute the dataset coordinates of the atom's center + Vector3f tmpVec = (curr->pos - min).ElementwiseDivision(span); + Vector3i c; + std::transform(tmpVec.begin(), tmpVec.end(), c.begin(), + [](float v) -> int { return round(v); }); + + // std::cout << "Max Radius: " << maxRad << std::endl; + + // compute the bounding box of the atom (maxRad^3) + Vector3i amin; + Vector3i amax; + for (int j = 0; j < 3; ++j) { + int tmp; + float tmpRad = maxRad / span[j]; + + tmp = (int)(c[j] - tmpRad - 1); + amin[j] = (tmp < 0) ? 0 : tmp; // check if tmp is < 0 + tmp = (int)(c[j] + tmpRad + 1); + amax[j] = (tmp > (dim[j] - 1)) ? (dim[j] - 1) : tmp; + } + + // std::cout << amin << " " << amax << std::endl; + + // Blur kernel in bounding box + for (int k = amin[2]; k <= amax[2]; k++) { + for (int j = amin[1]; j <= amax[1]; j++) { + for (int i = amin[0]; i <= amax[0]; i++) { + Vector3f pnt = + min + Vector3f({static_cast(i), static_cast(j), + static_cast(k)}) + .ElementwiseProduct(span); + dataset[Vect2Index(i, j, k, dim)] += evalDensity(*curr, pnt, maxRad); } + } } + } } /** @@ -541,56 +505,50 @@ void blurAtoms(Iterator begin, Iterator end, * @tparam Iterator Typename of the iterator */ template -void gridSAS(const Iterator begin, const Iterator end, const Vector3i &dim, float* dataset) -{ - // For atom in atoms : - for (auto curr = begin; curr != end; ++curr) - { - float radius = curr->radius; - Vector3f pos = curr->pos; - // compute the dataset coordinates of the atom's center - Vector3i c; - std::transform(pos.begin(), pos.end(), c.begin(), [](float v) -> int { - return round(v); - }); - - // compute bounding box for atom - Vector3i amin; - Vector3i amax; - for (int j = 0; j < 3; ++j) - { - int tmp; - tmp = (int)(c[j] - radius - 1); - amin[j] = (tmp < 0) ? 0 : tmp; // check if tmp is < 0 - tmp = (int)(c[j] + radius + 1); - amax[j] = (tmp > (dim[j] - 1)) ? (dim[j] - 1) : tmp; - } +void gridSAS(const Iterator begin, const Iterator end, const Vector3i &dim, + float *dataset) { + // For atom in atoms : + for (auto curr = begin; curr != end; ++curr) { + float radius = curr->radius; + Vector3f pos = curr->pos; + // compute the dataset coordinates of the atom's center + Vector3i c; + std::transform(pos.begin(), pos.end(), c.begin(), + [](float v) -> int { return round(v); }); + + // compute bounding box for atom + Vector3i amin; + Vector3i amax; + for (int j = 0; j < 3; ++j) { + int tmp; + tmp = (int)(c[j] - radius - 1); + amin[j] = (tmp < 0) ? 0 : tmp; // check if tmp is < 0 + tmp = (int)(c[j] + radius + 1); + amax[j] = (tmp > (dim[j] - 1)) ? (dim[j] - 1) : tmp; + } - // Blur kernel in bounding box - for (int k = amin[2]; k <= amax[2]; k++) - { - for (int j = amin[1]; j <= amax[1]; j++) - { - for (int i = amin[0]; i <= amax[0]; i++) - { - Vector3f coord = Vector3f({static_cast(i), static_cast(j), static_cast(k)}); - coord -= curr->pos; - float dist = -(std::sqrt(coord|coord)-radius); // inside - // is - // positive - int idx = Vect2Index(i, j, k, dim); - - if (dist > dataset[idx]) - { - dataset[idx] = dist; - } - } - } + // Blur kernel in bounding box + for (int k = amin[2]; k <= amax[2]; k++) { + for (int j = amin[1]; j <= amax[1]; j++) { + for (int i = amin[0]; i <= amax[0]; i++) { + Vector3f coord = + Vector3f({static_cast(i), static_cast(j), + static_cast(k)}); + coord -= curr->pos; + float dist = -(std::sqrt(coord | coord) - radius); // inside + // is + // positive + int idx = Vect2Index(i, j, k, dim); + + if (dist > dataset[idx]) { + dataset[idx] = dist; + } } + } } + } } - /** * @brief Compute the grid based Solvent Excluded Surface. * @@ -605,46 +563,42 @@ void gridSAS(const Iterator begin, const Iterator end, const Vector3i &dim, floa */ template void gridSES(const Iterator begin, const Iterator end, const Vector3i &dim, - float* dataset, const float radius) -{ - for (auto curr = begin; curr != end; ++curr) - { - Vector3f pos = (*curr).position; - - // compute bounding box for atom - Vector3i amin; - Vector3i amax; - for (int i = 0; i < 3; ++i) - { - int tmp; - tmp = (int)(pos[i] - radius - 1); - amin[i] = (tmp < 0) ? 0 : tmp; // check if tmp is < 0 - tmp = (int)(pos[i] + radius + 1); - amax[i] = (tmp > (dim[i] - 1)) ? (dim[i] - 1) : tmp; - } + float *dataset, const float radius) { + for (auto curr = begin; curr != end; ++curr) { + Vector3f pos = (*curr).position; + + // compute bounding box for atom + Vector3i amin; + Vector3i amax; + for (int i = 0; i < 3; ++i) { + int tmp; + tmp = (int)(pos[i] - radius - 1); + amin[i] = (tmp < 0) ? 0 : tmp; // check if tmp is < 0 + tmp = (int)(pos[i] + radius + 1); + amax[i] = (tmp > (dim[i] - 1)) ? (dim[i] - 1) : tmp; + } - // Blur kernel in bounding box - for (int k = amin[2]; k <= amax[2]; k++) - { - for (int j = amin[1]; j <= amax[1]; j++) - { - for (int i = amin[0]; i <= amax[0]; i++) - { - Vector3f coord = Vector3f({static_cast(i), static_cast(j), static_cast(k)}); - coord -= pos; - float dist = -(std::sqrt(coord|coord)-radius); - int idx = Vect2Index(i, j, k, dim); - if (dist > dataset[idx]) - { - dataset[idx] = dist; - } - } - } + // Blur kernel in bounding box + for (int k = amin[2]; k <= amax[2]; k++) { + for (int j = amin[1]; j <= amax[1]; j++) { + for (int i = amin[0]; i <= amax[0]; i++) { + Vector3f coord = + Vector3f({static_cast(i), static_cast(j), + static_cast(k)}); + coord -= pos; + float dist = -(std::sqrt(coord | coord) - radius); + int idx = Vect2Index(i, j, k, dim); + if (dist > dataset[idx]) { + dataset[idx] = dist; + } } + } } + } } -// TODO: (1) these functions should all take arrays of x,y,z,r instead of filenames +// TODO: (1) these functions should all take arrays of x,y,z,r instead of +// filenames /** * @brief Generate a mesh from PDB @@ -664,7 +618,8 @@ std::unique_ptr readPDB_molsurf(const std::string &filename); * * @return Meshed object */ -std::unique_ptr readPDB_gauss(const std::string &filename, float blobbyness, float isovalue); +std::unique_ptr readPDB_gauss(const std::string &filename, + float blobbyness, float isovalue); /** * @brief [WIP] Compute the Connolly surface using a distance grid based @@ -675,7 +630,8 @@ std::unique_ptr readPDB_gauss(const std::string &filename, float bl * * @return Meshed object */ -std::unique_ptr readPDB_distgrid(const std::string &filename, const float radius); +std::unique_ptr readPDB_distgrid(const std::string &filename, + const float radius); /** * @brief Generate a mesh from PQR @@ -695,6 +651,7 @@ std::unique_ptr readPQR_molsurf(const std::string &filename); * * @return Meshed object */ -std::unique_ptr readPQR_gauss(const std::string &filename, float blobbyness, float isovalue); +std::unique_ptr readPQR_gauss(const std::string &filename, + float blobbyness, float isovalue); } // end namespace gamer diff --git a/include/gamer/SurfaceMesh.h b/include/gamer/SurfaceMesh.h index 54c662ac..ef8abe39 100644 --- a/include/gamer/SurfaceMesh.h +++ b/include/gamer/SurfaceMesh.h @@ -1,26 +1,21 @@ -/* - * *************************************************************************** - * This file is part of the GAMer software. - * Copyright (C) 2016-2018 - * by Christopher Lee, John Moody, Rommie Amaro, J. Andrew McCammon, - * and Michael Holst - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * *************************************************************************** - */ +// This file is part of the GAMer software. +// Copyright (C) 2016-2021 +// by Christopher T. Lee and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, see +// or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA /** * @file SurfaceMesh.h @@ -30,183 +25,167 @@ #pragma once #include +#include #include #include -#include #include #include -#include #include #include +#include #include "gamer/Vertex.h" - /// Namespace for all things gamer -namespace gamer -{ +namespace gamer { /** * @brief Type for containing root metadata */ -struct SMGlobal -{ - /// Domain marker to be used when tetrahedralizing. - int marker; - /// Volume constraint of the tetrahedralized domain. - float volumeConstraint; - /// flag that determines if the volume constraint is used. - bool useVolumeConstraint; - /// Flag that determines if the mesh represents a hole or not - bool ishole; - - /** - * @brief Default constructor - * - * @param[in] marker Global marker value - * @param[in] volumeConstraint Value of volume constraint to use - * @param[in] useVolumeConstraint Whether or not a volume constraint - * should be applied when tetrahedralizing - * @param[in] ishole Is this domain a hole? - */ - SMGlobal(int marker = -1, float volumeConstraint = -1, bool useVolumeConstraint = false, bool ishole = false) : - marker(marker), volumeConstraint(volumeConstraint), useVolumeConstraint(useVolumeConstraint), ishole(ishole) { - } +struct SMGlobal { + /// Domain marker to be used when tetrahedralizing. + int marker; + /// Volume constraint of the tetrahedralized domain. + float volumeConstraint; + /// flag that determines if the volume constraint is used. + bool useVolumeConstraint; + /// Flag that determines if the mesh represents a hole or not + bool ishole; + + /** + * @brief Default constructor + * + * @param[in] marker Global marker value + * @param[in] volumeConstraint Value of volume constraint to use + * @param[in] useVolumeConstraint Whether or not a volume constraint + * should be applied when tetrahedralizing + * @param[in] ishole Is this domain a hole? + */ + SMGlobal(int marker = -1, float volumeConstraint = -1, + bool useVolumeConstraint = false, bool ishole = false) + : marker(marker), volumeConstraint(volumeConstraint), + useVolumeConstraint(useVolumeConstraint), ishole(ishole) {} }; struct SMVertex : Vertex { - /// Cached normal vector - Vector normal; - using Vertex::Vertex; + /// Cached normal vector + Vector normal; + using Vertex::Vertex; }; /** * @brief Edge data */ struct SMEdge { - /// Selection status of the edge - bool selected; - - /// Default constructor constructs unselected edge - SMEdge() : SMEdge(0) { - } - - /** - * @brief Overload constructor constructs edge with - * - * @param[in] select Selection status - */ - SMEdge(bool select) : selected(select) { - } + /// Selection status of the edge + bool selected; + + /// Default constructor constructs unselected edge + SMEdge() : SMEdge(0) {} + + /** + * @brief Overload constructor constructs edge with + * + * @param[in] select Selection status + */ + SMEdge(bool select) : selected(select) {} }; /** * @brief Properties that Faces should have */ -struct SMFaceProperties -{ - int marker; /**< @brief Marker */ - bool selected; /**< @brief Selection flag */ - Vector normal; /**< @brief cached normal of face */ - - /** - * @brief Face properties constructor - * - * @param[in] marker The marker - * @param[in] selected The selected - */ - SMFaceProperties(int marker, bool selected) : - marker(marker), selected(selected) { - } +struct SMFaceProperties { + int marker; /**< @brief Marker */ + bool selected; /**< @brief Selection flag */ + Vector normal; /**< @brief cached normal of face */ + + /** + * @brief Face properties constructor + * + * @param[in] marker The marker + * @param[in] selected The selected + */ + SMFaceProperties(int marker, bool selected) + : marker(marker), selected(selected) {} }; /** * @brief SMFace object */ -struct SMFace : casc::Orientable, SMFaceProperties -{ - /// Default constructor - SMFace() : SMFace(Orientable{0}, SMFaceProperties{-1, false}) { - } - - /** - * @brief Constructor - * - * @param[in] marker Marker value - * @param[in] selected Selection status - */ - SMFace(int marker, bool selected) : SMFace(Orientable{0}, SMFaceProperties{marker, selected}) { - } - - /** - * @brief Constructor - * - * @param[in] orient Orientation of the simplex - * @param[in] marker Marker value - * @param[in] selected Selection status - */ - SMFace(int orient, int marker, bool selected) : SMFace(Orientable{orient}, SMFaceProperties{marker, selected}) { - } - - /** - * @brief Constructor - * - * @param[in] orient Orientable object - * @param[in] prop Properties of a face - */ - SMFace(Orientable orient, SMFaceProperties prop) - : Orientable(orient), SMFaceProperties(prop) - { - } - - /** - * @brief Print operator overload - * - * @param output The output - * @param[in] f Face to print - * - * @return Output stream - */ - friend std::ostream& operator<<(std::ostream& output, const SMFace& f) - { - output << "SMFace(" - << "m:" << f.marker - << ";sel:" << std::boolalpha << f.selected - << ";o:" << f.orientation << ")"; - return output; - } - - /** - * @brief Returns a string representation of the object. - * - * @return String representation of the object. - */ - std::string to_string() const - { - std::ostringstream output; - output << *this; - return output.str(); - } - +struct SMFace : casc::Orientable, SMFaceProperties { + /// Default constructor + SMFace() : SMFace(Orientable{0}, SMFaceProperties{-1, false}) {} + + /** + * @brief Constructor + * + * @param[in] marker Marker value + * @param[in] selected Selection status + */ + SMFace(int marker, bool selected) + : SMFace(Orientable{0}, SMFaceProperties{marker, selected}) {} + + /** + * @brief Constructor + * + * @param[in] orient Orientation of the simplex + * @param[in] marker Marker value + * @param[in] selected Selection status + */ + SMFace(int orient, int marker, bool selected) + : SMFace(Orientable{orient}, SMFaceProperties{marker, selected}) {} + + /** + * @brief Constructor + * + * @param[in] orient Orientable object + * @param[in] prop Properties of a face + */ + SMFace(Orientable orient, SMFaceProperties prop) + : Orientable(orient), SMFaceProperties(prop) {} + + /** + * @brief Print operator overload + * + * @param output The output + * @param[in] f Face to print + * + * @return Output stream + */ + friend std::ostream &operator<<(std::ostream &output, const SMFace &f) { + output << "SMFace(" + << "m:" << f.marker << ";sel:" << std::boolalpha << f.selected + << ";o:" << f.orientation << ")"; + return output; + } + + /** + * @brief Returns a string representation of the object. + * + * @return String representation of the object. + */ + std::string to_string() const { + std::ostringstream output; + output << *this; + return output.str(); + } }; - /// @cond detail -namespace surfmesh_detail -{ +namespace surfmesh_detail { /** * @brief A helper struct containing the traits/types in the simplicial * complex */ -struct surfmesh_traits -{ - /// The index type - using KeyType = int; - /// The types of each node - using NodeTypes = util::type_holder; - /// The types of each edge - using EdgeTypes = util::type_holder; +struct surfmesh_traits { + /// The index type + using KeyType = int; + /// The types of each node + using NodeTypes = util::type_holder; + /// The types of each edge + using EdgeTypes = + util::type_holder; }; } // end namespace surfmesh_detail /// @endcond @@ -230,7 +209,8 @@ using SurfaceMesh = casc::simplicial_complex; * * @return Returns a 2-tensor representing the tangent plane. */ -tensor getTangent(const SurfaceMesh& mesh, SurfaceMesh::SimplexID<1> vertexID); +tensor getTangent(const SurfaceMesh &mesh, + SurfaceMesh::SimplexID<1> vertexID); /** * @brief Compute the tangent of a face. @@ -243,7 +223,8 @@ tensor getTangent(const SurfaceMesh& mesh, SurfaceMesh::SimplexID< * * @return Returns a 2-tensor representing the tangent plane. */ -tensor getTangent(const SurfaceMesh& mesh, SurfaceMesh::SimplexID<3> faceID); +tensor getTangent(const SurfaceMesh &mesh, + SurfaceMesh::SimplexID<3> faceID); /** * @brief Gets the normal vector from the tangent. @@ -268,7 +249,7 @@ Vector getNormalFromTangent(const tensor tangent); * * @return Returns the Vector normal to the vertex. */ -Vector getNormal(const SurfaceMesh& mesh, SurfaceMesh::SimplexID<1> vertexID); +Vector getNormal(const SurfaceMesh &mesh, SurfaceMesh::SimplexID<1> vertexID); /** * @brief Compute the normal of a face. @@ -281,12 +262,11 @@ Vector getNormal(const SurfaceMesh& mesh, SurfaceMesh::SimplexID<1> vertexID); * * @return Returns a Vector normal to the face. */ -Vector getNormal(const SurfaceMesh& mesh, SurfaceMesh::SimplexID<3> faceID); +Vector getNormal(const SurfaceMesh &mesh, SurfaceMesh::SimplexID<3> faceID); /// @cond detail /// Namespace for surface mesh detail functions -namespace surfacemesh_detail -{ +namespace surfacemesh_detail { /** * @brief Remove a vertex from mesh and triangulate the resulting hole. * @@ -294,7 +274,8 @@ namespace surfacemesh_detail * @param[in] vertexID The vertex id * @param[in] rings Number of neighborhood rings to consider */ -void decimateVertex(SurfaceMesh& mesh, SurfaceMesh::SimplexID<1> vertexID, std::size_t rings = 2); +void decimateVertex(SurfaceMesh &mesh, SurfaceMesh::SimplexID<1> vertexID, + std::size_t rings = 2); /** * @brief Computes the local structure tensor @@ -305,11 +286,10 @@ void decimateVertex(SurfaceMesh& mesh, SurfaceMesh::SimplexID<1> vertexID, std:: * * @return The local structure tensor. */ -tensor computeLocalStructureTensor( - const SurfaceMesh& mesh, - const SurfaceMesh::SimplexID<1> vertexID, - const int rings); - +tensor +computeLocalStructureTensor(const SurfaceMesh &mesh, + const SurfaceMesh::SimplexID<1> vertexID, + const int rings); /** * @brief Computes the local structure tensor from cached normals @@ -320,11 +300,9 @@ tensor computeLocalStructureTensor( * * @return The local structure tensor. */ -tensor computeLSTFromCache( - const SurfaceMesh& mesh, - const SurfaceMesh::SimplexID<1> vertexID, - const int rings); - +tensor +computeLSTFromCache(const SurfaceMesh &mesh, + const SurfaceMesh::SimplexID<1> vertexID, const int rings); /** * @brief Terminal case @@ -338,11 +316,10 @@ tensor computeLSTFromCache( * @return The tangent h. */ template -auto getTangentH(const SurfaceMesh& mesh, - const tensor& origin, - SurfaceMesh::SimplexID curr) -{ - return (*curr).orientation; +auto getTangentH(const SurfaceMesh &mesh, + const tensor &origin, + SurfaceMesh::SimplexID curr) { + return (*curr).orientation; } /** @@ -359,20 +336,18 @@ auto getTangentH(const SurfaceMesh& mesh, * @return The tangent h. */ template -auto getTangentH(const SurfaceMesh& mesh, - const tensor& origin, - SurfaceMesh::SimplexID curr) -{ - tensor rval; - auto cover = mesh.get_cover(curr); - for (auto alpha : cover) - { - auto edge = *mesh.get_edge_up(curr, alpha); - const auto& v = (*mesh.get_simplex_up({alpha})).position; - auto next = mesh.get_simplex_up(curr, alpha); - rval += edge.orientation * (v-origin) * getTangentH(mesh, origin, next); - } - return rval/cover.size(); +auto getTangentH(const SurfaceMesh &mesh, + const tensor &origin, + SurfaceMesh::SimplexID curr) { + tensor rval; + auto cover = mesh.get_cover(curr); + for (auto alpha : cover) { + auto edge = *mesh.get_edge_up(curr, alpha); + const auto &v = (*mesh.get_simplex_up({alpha})).position; + auto next = mesh.get_simplex_up(curr, alpha); + rval += edge.orientation * (v - origin) * getTangentH(mesh, origin, next); + } + return rval / cover.size(); } /** @@ -391,12 +366,11 @@ auto getTangentH(const SurfaceMesh& mesh, * @return Returns a 2-tensor corresponding to the tangent plane. */ template -auto getTangentF(const SurfaceMesh& mesh, - const tensor& origin, +auto getTangentF(const SurfaceMesh &mesh, + const tensor &origin, SurfaceMesh::SimplexID curr, - std::set& cover) -{ - return (*curr).orientation; + std::set &cover) { + return (*curr).orientation; } /** @@ -413,22 +387,21 @@ auto getTangentF(const SurfaceMesh& mesh, * @return Returns a 2-tensor corresponding to the tangent plane. */ template -auto getTangentF(const SurfaceMesh& mesh, - const tensor& origin, +auto getTangentF(const SurfaceMesh &mesh, + const tensor &origin, SurfaceMesh::SimplexID curr, - std::set& cover) -{ - tensor rval; - for (auto alpha : cover) - { - auto edge = *mesh.get_edge_up(curr, alpha); - const auto& v = (*mesh.get_simplex_up({alpha})).position; - auto next = mesh.get_simplex_up(curr, alpha); - auto coverup = cover; - coverup.erase(alpha); - rval += edge.orientation * (v-origin) * getTangentF(mesh, origin, next, coverup); - } - return rval/cover.size(); + std::set &cover) { + tensor rval; + for (auto alpha : cover) { + auto edge = *mesh.get_edge_up(curr, alpha); + const auto &v = (*mesh.get_simplex_up({alpha})).position; + auto next = mesh.get_simplex_up(curr, alpha); + auto coverup = cover; + coverup.erase(alpha); + rval += edge.orientation * (v - origin) * + getTangentF(mesh, origin, next, coverup); + } + return rval / cover.size(); } /** @@ -437,8 +410,7 @@ auto getTangentF(const SurfaceMesh& mesh, * * @tparam K Class template intended for storing level */ -template -struct initLocalOrientation {}; +template struct initLocalOrientation {}; /** * @brief Specialization to initialize the local orientation of a mesh @@ -446,67 +418,57 @@ struct initLocalOrientation {}; * @tparam k The current level traversed */ template -struct initLocalOrientation > -{ - /** - * @brief Set the orientation of a filled hole - * - * @param mesh Simplicial complex - * @param[in] names Names of participant vertices - * @param[in] begin Begin iterator of simplices to traverse - * @param[in] end Past the end iterator of simplices to traverse - * - * @tparam Iterator Typename of the iterator - */ - template - static void apply(SurfaceMesh& mesh, - const std::set&& names, - Iterator begin, - Iterator end) - { - std::vector > next; - for (auto curr = begin; curr != end; ++curr) - { - auto currSimplexID = *curr; - for (auto a : mesh.get_cover(currSimplexID)) - { - // Look for key a in names - auto find = names.find(a); - if (find != names.end()) - { - next.push_back(mesh.get_simplex_up(currSimplexID, a)); - - int orient = 1; - for (auto b : mesh.get_name(currSimplexID)) - { - if (a > b) - { - if (a > b) - { - orient *= -1; - } - else - { - break; - } - } - } - (*mesh.get_edge_up(currSimplexID, a)).orientation = orient; - } +struct initLocalOrientation> { + /** + * @brief Set the orientation of a filled hole + * + * @param mesh Simplicial complex + * @param[in] names Names of participant vertices + * @param[in] begin Begin iterator of simplices to traverse + * @param[in] end Past the end iterator of simplices to traverse + * + * @tparam Iterator Typename of the iterator + */ + template + static void apply(SurfaceMesh &mesh, const std::set &&names, + Iterator begin, Iterator end) { + std::vector> next; + for (auto curr = begin; curr != end; ++curr) { + auto currSimplexID = *curr; + for (auto a : mesh.get_cover(currSimplexID)) { + // Look for key a in names + auto find = names.find(a); + if (find != names.end()) { + next.push_back(mesh.get_simplex_up(currSimplexID, a)); + + int orient = 1; + for (auto b : mesh.get_name(currSimplexID)) { + if (a > b) { + if (a > b) { + orient *= -1; + } else { + break; + } } + } + (*mesh.get_edge_up(currSimplexID, a)).orientation = orient; } - initLocalOrientation >::apply(mesh, std::move(names), next.begin(), next.end()); + } } + initLocalOrientation>::apply( + mesh, std::move(names), next.begin(), next.end()); + } }; /** * @brief Terminal case. The top level does not need to be initialized. */ template <> -struct initLocalOrientation > { - template - static void apply(SurfaceMesh& mesh, const std::set&& names, Iterator begin, Iterator end){ - } +struct initLocalOrientation< + std::integral_constant> { + template + static void apply(SurfaceMesh &mesh, const std::set &&names, + Iterator begin, Iterator end) {} }; /** @@ -519,7 +481,8 @@ struct initLocalOrientation >& edgeList); +bool computeLocalOrientation( + SurfaceMesh &mesh, const std::vector> &edgeList); /** * @brief Smooth the vertex according to Section 2.2.2 of GAMer paper. @@ -528,9 +491,10 @@ bool computeLocalOrientation(SurfaceMesh& mesh, const std::vector vertexID, int rings); +void weightedVertexSmooth(SurfaceMesh &mesh, SurfaceMesh::SimplexID<1> vertexID, + int rings); -Vector weightedVertexSmoothCache(SurfaceMesh& mesh, +Vector weightedVertexSmoothCache(SurfaceMesh &mesh, SurfaceMesh::SimplexID<1> vertexID, std::size_t rings); @@ -540,7 +504,8 @@ Vector weightedVertexSmoothCache(SurfaceMesh& mesh, * @param mesh SurfaceMesh to manipulate. * @param[in] vertexID SimplexID of the vertex to move. */ -void barycenterVertexSmooth(SurfaceMesh& mesh, SurfaceMesh::SimplexID<1> vertexID); +void barycenterVertexSmooth(SurfaceMesh &mesh, + SurfaceMesh::SimplexID<1> vertexID); /** * @brief Perform an edge flip operation @@ -548,9 +513,9 @@ void barycenterVertexSmooth(SurfaceMesh& mesh, SurfaceMesh::SimplexID<1> vertexI * @param mesh SurfaceMesh of interest. * @param[in] edgeID SimplexID of the edge to flip. */ -void edgeFlip(SurfaceMesh& mesh, SurfaceMesh::SimplexID<2> edgeID); +void edgeFlip(SurfaceMesh &mesh, SurfaceMesh::SimplexID<2> edgeID); -void edgeFlipCache(SurfaceMesh& mesh, SurfaceMesh::SimplexID<2> edgeID); +void edgeFlipCache(SurfaceMesh &mesh, SurfaceMesh::SimplexID<2> edgeID); /** * @brief Select edges which are good candidates for flipping @@ -563,75 +528,67 @@ void edgeFlipCache(SurfaceMesh& mesh, SurfaceMesh::SimplexID<2> edgeID); * @tparam Inserter Typename of the inserter. */ template -void selectFlipEdges(const SurfaceMesh& mesh, - bool preserveRidges, - std::function &)>&& checkFlip, - Inserter iter) -{ - casc::NodeSet > ignoredEdges; - - for (auto edgeID : mesh.get_level_id<2>()) - { - if ((*edgeID).selected == true) +void selectFlipEdges( + const SurfaceMesh &mesh, bool preserveRidges, + std::function &)> + &&checkFlip, + Inserter iter) { + casc::NodeSet> ignoredEdges; + + for (auto edgeID : mesh.get_level_id<2>()) { + if ((*edgeID).selected == true) { + if (!ignoredEdges.count(edgeID)) { + auto up = mesh.get_cover(edgeID); + // The mesh is not a surface mesh... + if (up.size() > 2) { + // std::cerr << "This edge participates in more than 2 + // faces. " + // << "Returning..." << std::endl; + gamer_runtime_error("SurfaceMesh is not pseudomanifold. Found " + "an edge connected to more than 2 faces."); + } else if (up.size() < 2) // Edge is a boundary { - if (!ignoredEdges.count(edgeID)) - { - auto up = mesh.get_cover(edgeID); - // The mesh is not a surface mesh... - if (up.size() > 2) - { - // std::cerr << "This edge participates in more than 2 - // faces. " - // << "Returning..." << std::endl; - throw std::runtime_error("SurfaceMesh is not pseudomanifold. Found an edge connected to more than 2 faces."); - } - else if (up.size() < 2) // Edge is a boundary - { - // std::cerr << "This edge participates in fewer than 2 - // faces. " - // << "Returning..." << std::endl; - continue; - } - - // Check if the edge is a part of a tetrahedron. - if (mesh.exists<2>({up[0], up[1]})) - { - // std::cerr << "Found a tetrahedron cannot edge flip." - // << std::endl; - continue; - } - - // Check if we're on a ridge. This prevents folding also. - if (preserveRidges) - { - auto a = getNormal(mesh, mesh.get_simplex_up(edgeID, up[0])); - auto b = getNormal(mesh, mesh.get_simplex_up(edgeID, up[1])); - auto val = angle(a, b); - if (val > 60) - { - continue; - } - } - - // Check the flip using user function - if (checkFlip(mesh, edgeID)) - { - *iter++ = edgeID; // Insert into edges to flip - - // The local topology will be changed by edge flip. - // Don't flip edges which share a common face. - std::set > tmpIgnored; - kneighbors(mesh, edgeID, 3, tmpIgnored); - ignoredEdges.insert(tmpIgnored.begin(), tmpIgnored.end()); - - // Local neighborhood append. Larger neighborhood selected - // above appears to work better... - // neighbors(mesh, edgeID, std::inserter(ignoredEdges, - // ignoredEdges.end())); - } - } + // std::cerr << "This edge participates in fewer than 2 + // faces. " + // << "Returning..." << std::endl; + continue; + } + + // Check if the edge is a part of a tetrahedron. + if (mesh.exists<2>({up[0], up[1]})) { + // std::cerr << "Found a tetrahedron cannot edge flip." + // << std::endl; + continue; } + + // Check if we're on a ridge. This prevents folding also. + if (preserveRidges) { + auto a = getNormal(mesh, mesh.get_simplex_up(edgeID, up[0])); + auto b = getNormal(mesh, mesh.get_simplex_up(edgeID, up[1])); + auto val = angle(a, b); + if (val > 60) { + continue; + } + } + + // Check the flip using user function + if (checkFlip(mesh, edgeID)) { + *iter++ = edgeID; // Insert into edges to flip + + // The local topology will be changed by edge flip. + // Don't flip edges which share a common face. + std::set> tmpIgnored; + kneighbors(mesh, edgeID, 3, tmpIgnored); + ignoredEdges.insert(tmpIgnored.begin(), tmpIgnored.end()); + + // Local neighborhood append. Larger neighborhood selected + // above appears to work better... + // neighbors(mesh, edgeID, std::inserter(ignoredEdges, + // ignoredEdges.end())); + } + } } + } } /** @@ -644,11 +601,11 @@ void selectFlipEdges(const SurfaceMesh& mesh, * * @return True if edge should be flipped */ -bool checkEdgeFlip(const SurfaceMesh& mesh, - bool preserveRidges, - SurfaceMesh::SimplexID<2> edgeID, - std::function &)>&& checkFlip - ); +bool checkEdgeFlip( + const SurfaceMesh &mesh, bool preserveRidges, + SurfaceMesh::SimplexID<2> edgeID, + std::function &)> + &&checkFlip); /** * @brief Check if we should flip an edge to improve the angles. * @@ -658,7 +615,8 @@ bool checkEdgeFlip(const SurfaceMesh& mesh, * @return Returns true if flippiing the edge will improve angles, false * otherwise. */ -bool checkFlipAngle(const SurfaceMesh& mesh, const SurfaceMesh::SimplexID<2>& edgeID); +bool checkFlipAngle(const SurfaceMesh &mesh, + const SurfaceMesh::SimplexID<2> &edgeID); /** * @brief Check if we should flip the edge to improve the valence. @@ -669,7 +627,8 @@ bool checkFlipAngle(const SurfaceMesh& mesh, const SurfaceMesh::SimplexID<2>& ed * @return Returns true if flipping the edge will improve the valence, false * otherwise. */ -int checkFlipValenceExcess(const SurfaceMesh& mesh, const SurfaceMesh::SimplexID<2>& edgeID); +int checkFlipValenceExcess(const SurfaceMesh &mesh, + const SurfaceMesh::SimplexID<2> &edgeID); /** * @brief Apply normal smoothing to a region around a vertex @@ -678,7 +637,8 @@ int checkFlipValenceExcess(const SurfaceMesh& mesh, const SurfaceMesh::SimplexID * @param[in] vertexID Vertex of interest * @param[in] k Anisotropic smoothing factor */ -void normalSmoothH(SurfaceMesh& mesh, const SurfaceMesh::SimplexID<1> vertexID, const double k); +void normalSmoothH(SurfaceMesh &mesh, const SurfaceMesh::SimplexID<1> vertexID, + const double k); /** * @brief Traverse mesh and find holes @@ -686,8 +646,8 @@ void normalSmoothH(SurfaceMesh& mesh, const SurfaceMesh::SimplexID<1> vertexID, * @param[in] mesh SurfaceMesh * @param holeList List of list of edge rings to store holes in */ -void findHoles(const SurfaceMesh& mesh, - std::vector > >& holeList); +void findHoles(const SurfaceMesh &mesh, + std::vector>> &holeList); /** * @brief Sort a list of boundary edges into ring order. @@ -707,19 +667,20 @@ void findHoles(const SurfaceMesh& mesh, * * @return True if hole ring found. False otherwise. */ -bool orderBoundaryEdgeRing(const SurfaceMesh& mesh, - std::set >& unvisitedBdryEdges, - std::vector >& visitedVerts, - std::vector >& bdryRing); +bool orderBoundaryEdgeRing( + const SurfaceMesh &mesh, + std::set> &unvisitedBdryEdges, + std::vector> &visitedVerts, + std::vector> &bdryRing); -void edgeRingToVertices(const SurfaceMesh& mesh, - std::vector >& edgeRing, - std::back_insert_iterator > > iter); +void edgeRingToVertices( + const SurfaceMesh &mesh, std::vector> &edgeRing, + std::back_insert_iterator>> iter); -void triangulateHoleHelper(SurfaceMesh& mesh, - std::vector >& boundary, - const SMFace& fdata, - std::back_insert_iterator > > iter); +void triangulateHoleHelper( + SurfaceMesh &mesh, std::vector> &boundary, + const SMFace &fdata, + std::back_insert_iterator>> iter); /** * @brief Recursively triangulate hole by connecting lowest valence @@ -730,89 +691,75 @@ void triangulateHoleHelper(SurfaceMesh& mesh, * @param[in] fdata Data to store on each face * @param[in] edgeList Back inserter to store new edges and boundary edges */ -void triangulateHole(SurfaceMesh& mesh, - std::vector >& sortedVerts, - const SMFace& fdata, - std::vector >& edgeList); - -template -struct CopyHelper -{ - using SimplexSet = typename casc::SimplexSet; - using KeyType = typename Complex::KeyType; - - template - static void apply(Complex& before, - Complex& after, - const SimplexSet& S) - { - for (auto sID : casc::get(S)) - { - auto name = before.get_name(sID); - auto data = *sID; - after.insert(name, data); - } +void triangulateHole(SurfaceMesh &mesh, + std::vector> &sortedVerts, + const SMFace &fdata, + std::vector> &edgeList); + +template struct CopyHelper { + using SimplexSet = typename casc::SimplexSet; + using KeyType = typename Complex::KeyType; + + template + static void apply(Complex &before, Complex &after, const SimplexSet &S) { + for (auto sID : casc::get(S)) { + auto name = before.get_name(sID); + auto data = *sID; + after.insert(name, data); } + } }; template -void vertexGrabber(const SurfaceMesh& F, - int need, - std::vector>& nbors, - Iterator begin, - Iterator end) -{ - if (need <= 0) return; - std::set> next; - for (; begin != end; ++begin) - { - for (auto a : F.get_cover(*begin)) - { - auto id = F.get_simplex_up(*begin, a); - for (auto b : F.get_name(id)) - { - if (b != a){ - auto nbor = F.get_simplex_down(id, b); - if (std::find(nbors.begin(), nbors.end(), nbor) == nbors.end()) - { - // Haven't visited so push into next ring and - nbors.push_back(nbor); - next.insert(nbor); - if (--need == 0) return; - } - } - } +void vertexGrabber(const SurfaceMesh &F, int need, + std::vector> &nbors, + Iterator begin, Iterator end) { + if (need <= 0) + return; + std::set> next; + for (; begin != end; ++begin) { + for (auto a : F.get_cover(*begin)) { + auto id = F.get_simplex_up(*begin, a); + for (auto b : F.get_name(id)) { + if (b != a) { + auto nbor = F.get_simplex_down(id, b); + if (std::find(nbors.begin(), nbors.end(), nbor) == nbors.end()) { + // Haven't visited so push into next ring and + nbors.push_back(nbor); + next.insert(nbor); + if (--need == 0) + return; + } } + } } - vertexGrabber(F, need, nbors, next.begin(), next.end()); + } + vertexGrabber(F, need, nbors, next.begin(), next.end()); } template -void vertexGrabber(const Complex& F, - int need, - std::vector>& nbors, - typename Complex::template SimplexID<1> vid) -{ - if (need <= 0) return; - std::set> next; - for (auto a : F.get_cover(vid)) - { - auto id = F.get_simplex_up(vid, a); - for (auto b : F.get_name(id)) - { - if (b != a){ - auto nbor = F.get_simplex_down(id, b); - if (std::find(nbors.begin(), nbors.end(), nbor) == nbors.end()) - { - // Haven't visited so push into next ring and - nbors.push_back(nbor); - next.insert(nbor); - if (--need == 0) return; - } - } +void vertexGrabber(const Complex &F, int need, + std::vector> &nbors, + typename Complex::template SimplexID<1> vid) { + if (need <= 0) + return; + std::set> next; + for (auto a : F.get_cover(vid)) { + auto id = F.get_simplex_up(vid, a); + for (auto b : F.get_name(id)) { + if (b != a) { + auto nbor = F.get_simplex_down(id, b); + if (std::find(nbors.begin(), nbors.end(), nbor) == nbors.end()) { + // Haven't visited so push into next ring and + nbors.push_back(nbor); + next.insert(nbor); + if (--need == 0) + return; } + } } - vertexGrabber(F, need, nbors, next.begin(), next.end()); + } + vertexGrabber(F, need, nbors, next.begin(), next.end()); } } // end namespace surfacemesh_detail /// @endcond @@ -824,8 +771,7 @@ void vertexGrabber(const Complex& F, * * @return Returns a unique_ptr to the SurfaceMesh */ -std::unique_ptr readOFF(const std::string& filename); - +std::unique_ptr readOFF(const std::string &filename); /** * @brief Write the SurfaceMesh to file in OFF format. @@ -833,7 +779,7 @@ std::unique_ptr readOFF(const std::string& filename); * @param[in] filename The filename to write to. * @param[in] mesh SurfaceMesh of interest. */ -void writeOFF(const std::string& filename, const SurfaceMesh& mesh); +void writeOFF(const std::string &filename, const SurfaceMesh &mesh); /** * @brief Reads an obj file. @@ -842,8 +788,7 @@ void writeOFF(const std::string& filename, const SurfaceMesh& mesh); * * @return Unique pointer to SurfaceMesh */ -std::unique_ptr readOBJ(const std::string& filename); - +std::unique_ptr readOBJ(const std::string &filename); /** * @brief Writes a mesh to obj file format. @@ -851,14 +796,31 @@ std::unique_ptr readOBJ(const std::string& filename); * @param[in] filename The filename to write out to * @param[in] mesh Surface mesh to output */ -void writeOBJ(const std::string& filename, const SurfaceMesh& mesh); +void writeOBJ(const std::string &filename, const SurfaceMesh &mesh); + +/** + * @brief Writes a surface mesh to COMSOL mph format + * + * @param filename The filename + * @param mesh Surface mesh to output + */ +void writeComsol(const std::string &filename, const SurfaceMesh &mesh); + +/** + * @brief Writes a collection of surface meshes to comsol + * + * @param filename The filename + * @param mesh Collection of surface meshes to output + */ +void writeComsol(const std::string &filename, + const std::vector &meshes); /** * @brief Pretty print the mesh. * * @param[in] mesh The mesh to print */ -void print(const SurfaceMesh& mesh); +void print(const SurfaceMesh &mesh); /** * @brief { function_description } @@ -866,14 +828,14 @@ void print(const SurfaceMesh& mesh); * @param[in] filename The filename * @param[in] mesh The mesh */ -void printQualityInfo(const std::string& filename, const SurfaceMesh& mesh); +void printQualityInfo(const std::string &filename, const SurfaceMesh &mesh); /** * @brief { function_description } * * @param[in] mesh The mesh */ -void generateHistogram(const SurfaceMesh& mesh); +void generateHistogram(const SurfaceMesh &mesh); /** * @brief Gets the minimum maximum angles. @@ -884,8 +846,9 @@ void generateHistogram(const SurfaceMesh& mesh); * * @return The minimum maximum angles. */ -std::tuple getMinMaxAngles(const SurfaceMesh& mesh, - double maxMinAngle, double minMaxAngle); +std::tuple getMinMaxAngles(const SurfaceMesh &mesh, + double maxMinAngle, + double minMaxAngle); /** * @brief Gets the area. @@ -894,7 +857,7 @@ std::tuple getMinMaxAngles(const SurfaceMesh& mesh, * * @return The area. */ -double getArea(const SurfaceMesh& mesh); +double getArea(const SurfaceMesh &mesh); /** * @brief Gets the area. @@ -904,7 +867,7 @@ double getArea(const SurfaceMesh& mesh); * * @return The area. */ -double getArea(const SurfaceMesh& mesh, SurfaceMesh::SimplexID<3> faceID); +double getArea(const SurfaceMesh &mesh, SurfaceMesh::SimplexID<3> faceID); /** * @brief Gets the area of a triangle defined by three vertices @@ -917,7 +880,7 @@ double getArea(const SurfaceMesh& mesh, SurfaceMesh::SimplexID<3> faceID); */ double getArea(Vertex a, Vertex b, Vertex c); -REAL getArea(std::array t); +double getArea(std::array t); /** * @brief Gets the volume. @@ -926,7 +889,7 @@ REAL getArea(std::array t); * * @return The volume. */ -double getVolume(const SurfaceMesh& mesh); +double getVolume(const SurfaceMesh &mesh); /** * @brief Determines if a surface mesh contains holes. @@ -935,7 +898,7 @@ double getVolume(const SurfaceMesh& mesh); * * @return True if has hole, False otherwise. */ -bool hasHole(const SurfaceMesh& mesh); +bool hasHole(const SurfaceMesh &mesh); /** * @brief Gets the number of edges connected to a vertex. @@ -945,8 +908,9 @@ bool hasHole(const SurfaceMesh& mesh); * * @return The valence. */ -inline std::size_t getValence(const SurfaceMesh& mesh, const SurfaceMesh::SimplexID<1> vertexID){ - return mesh.get_cover(vertexID).size(); +inline std::size_t getValence(const SurfaceMesh &mesh, + const SurfaceMesh::SimplexID<1> vertexID) { + return mesh.get_cover(vertexID).size(); } /** @@ -955,8 +919,10 @@ inline std::size_t getValence(const SurfaceMesh& mesh, const SurfaceMesh::Simple * * @param[in] mesh The mesh */ -std::tuple > -curvatureViaMDSB(const SurfaceMesh& mesh); +std::tuple< + REAL *, REAL *, REAL *, REAL *, + std::map> +curvatureViaMDSB(const SurfaceMesh &mesh); /** * @brief Compute the curvature using the Cazals-Pouget algorithm. @@ -965,8 +931,11 @@ curvatureViaMDSB(const SurfaceMesh& mesh); * @param[in] dPrime Maximal order differential to compute * @param[in] mesh The mesh */ -std::tuple > -curvatureViaJets(const SurfaceMesh& mesh, std::size_t dJet = 2, std::size_t dPrime = 2); +std::tuple< + REAL *, REAL *, REAL *, REAL *, + std::map> +curvatureViaJets(const SurfaceMesh &mesh, std::size_t dJet = 2, + std::size_t dPrime = 2); // void osculatingJets(const SurfaceMesh&mesh, std::size_t dJet = 2, std::size_t // dPrime = 2); @@ -975,7 +944,7 @@ curvatureViaJets(const SurfaceMesh& mesh, std::size_t dJet = 2, std::size_t dPri // @param mesh The mesh // @param[in] v Displacement vector // -void translate(SurfaceMesh& mesh, Vector v); +void translate(SurfaceMesh &mesh, Vector v); /** * @brief Translate the mesh * @@ -984,7 +953,7 @@ void translate(SurfaceMesh& mesh, Vector v); * @param[in] dy Distance to move in y direction * @param[in] dz Distance to move in z direction */ -void translate(SurfaceMesh& mesh, double dx, double dy, double dz); +void translate(SurfaceMesh &mesh, double dx, double dy, double dz); /** * @brief Scale mesh anisotropically * @@ -992,7 +961,7 @@ void translate(SurfaceMesh& mesh, double dx, double dy, double dz); * @param[in] v Vector with components representing the anisotropic scaling * factors. */ -void scale(SurfaceMesh& mesh, Vector v); +void scale(SurfaceMesh &mesh, Vector v); /** * @brief Scale a mesh anisotropically * @@ -1001,14 +970,14 @@ void scale(SurfaceMesh& mesh, Vector v); * @param[in] sy Scale factor for y axis * @param[in] sz Scale factor for z axis */ -void scale(SurfaceMesh& mesh, double sx, double sy, double sz); +void scale(SurfaceMesh &mesh, double sx, double sy, double sz); /** * @brief Scale a mesh isotropically * * @param mesh The mesh * @param[in] s Scale factor */ -void scale(SurfaceMesh& mesh, double s); +void scale(SurfaceMesh &mesh, double s); /** * @brief Compute the center and radius of the mesh @@ -1017,13 +986,13 @@ void scale(SurfaceMesh& mesh, double s); * * @return The center and radius. */ -std::pair getCenterRadius(SurfaceMesh& mesh); +std::pair getCenterRadius(SurfaceMesh &mesh); /** * @brief Center the mesh on its center of mass * * @param mesh The mesh */ -void centeralize(SurfaceMesh& mesh); +void centeralize(SurfaceMesh &mesh); /** * @brief Smooth the surface mesh @@ -1034,7 +1003,8 @@ void centeralize(SurfaceMesh& mesh); * @param[in] rings Number of neighborhood rings to consider for LST * @param[in] verbose Print additional information */ -void smoothMesh(SurfaceMesh& mesh, int maxIter, bool preserveRidges, std::size_t rings = 2, bool verbose = false); +void smoothMesh(SurfaceMesh &mesh, int maxIter, bool preserveRidges, + std::size_t rings = 2, bool verbose = false); /** * @brief Coarsens the mesh @@ -1046,7 +1016,8 @@ void smoothMesh(SurfaceMesh& mesh, int maxIter, bool preserveRidges, std::size_t * @param[in] rings Number of neighborhood rings to consider for LST * @param[in] verbose Print additional info */ -void coarse(SurfaceMesh& mesh, double coarseRate, double flatRate, double denseWeight, std::size_t rings = 2, bool verbose = false); +void coarse(SurfaceMesh &mesh, double coarseRate, double flatRate, + double denseWeight, std::size_t rings = 2, bool verbose = false); /** * @brief Coarsens the mesh by selecting vertices first. @@ -1057,7 +1028,8 @@ void coarse(SurfaceMesh& mesh, double coarseRate, double flatRate, double denseW * @param[in] rings Number of neighborhood rings to consider for LST * @param[in] verbose Print additional info */ -void coarse_dense(SurfaceMesh& mesh, REAL threshold, REAL weight, std::size_t rings = 2, bool verbose = false); +void coarse_dense(SurfaceMesh &mesh, REAL threshold, REAL weight, + std::size_t rings = 2, bool verbose = false); /** * @brief Coarsens flat regions by LST analysis @@ -1068,35 +1040,36 @@ void coarse_dense(SurfaceMesh& mesh, REAL threshold, REAL weight, std::size_t ri * @param[in] rings Number of neighborhood rings to consider for LST * @param[in] verbose Print additional info */ -void coarse_flat(SurfaceMesh& mesh, REAL threshold, REAL weight, std::size_t rings = 2, bool verbose = false); +void coarse_flat(SurfaceMesh &mesh, REAL threshold, REAL weight, + std::size_t rings = 2, bool verbose = false); /** * @brief Perform smoothing of the mesh normals * * @param mesh The mesh */ -void normalSmooth(SurfaceMesh& mesh, double k = 1.0); +void normalSmooth(SurfaceMesh &mesh, double k = 1.0); /** * @brief Fill holes in the mesh * * @param mesh The mesh */ -void fillHoles(SurfaceMesh& mesh); +void fillHoles(SurfaceMesh &mesh); /** * @brief Flip the normals of the mesh * * @param mesh The mesh */ -void flipNormals(SurfaceMesh& mesh); +void flipNormals(SurfaceMesh &mesh); /** * @brief Refine the mesh by quadrisection of faces * * @param mesh The mesh */ -std::unique_ptr refineMesh(const SurfaceMesh& mesh); +std::unique_ptr refineMesh(const SurfaceMesh &mesh); /** * @brief Create a triangulated octahedron @@ -1125,9 +1098,16 @@ std::unique_ptr cube(int order); * * @return Vector of surface meshes */ -std::vector > splitSurfaces(SurfaceMesh& mesh); +std::vector> splitSurfaces(SurfaceMesh &mesh); -void cacheNormals(SurfaceMesh& mesh); +/** + * @brief Cache face and vertex normals + * + * A zero vector normal will be stored instead of raising an error + * + * @param mesh Surface mesh of interest + */ +void cacheNormals(SurfaceMesh &mesh); -std::tuple getBettiNumbers(SurfaceMesh& mesh); +std::tuple getBettiNumbers(SurfaceMesh &mesh); } // end namespace gamer diff --git a/include/gamer/TetMesh.h b/include/gamer/TetMesh.h index 48adf057..45179171 100644 --- a/include/gamer/TetMesh.h +++ b/include/gamer/TetMesh.h @@ -1,37 +1,31 @@ -/* - * *************************************************************************** - * This file is part of the GAMer software. - * Copyright (C) 2016-2018 - * by Christopher Lee, John Moody, Rommie Amaro, J. Andrew McCammon, - * and Michael Holst - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * *************************************************************************** - */ +// This file is part of the GAMer software. +// Copyright (C) 2016-2021 +// by Christopher T. Lee and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, see +// or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA /** * @file TetMesh.h * @brief Tetrahedral mesh definition and associated functions */ - #pragma once -#include #include +#include #include #include #include @@ -43,8 +37,8 @@ #include -#include "gamer/Vertex.h" #include "gamer/SurfaceMesh.h" +#include "gamer/Vertex.h" /// @cond detail /// Forward class declaration @@ -52,272 +46,245 @@ class tetgenio; /// @endcond /// Namespace for all things gamer -namespace gamer -{ +namespace gamer { /** * @brief Type for containing root metadata */ -struct TMGlobal -{ - bool higher_order; /// Is this a higher_order mesh? +struct TMGlobal { + bool higher_order; /// Is this a higher_order mesh? }; /** * @brief Properties of a vertex in a tetmesh */ -struct TMVertexProperties -{ - double error; /// Error - - /** - * @brief Default constructor - */ - TMVertexProperties() : error(-1) {} - - /** - * @brief Constructor - * - * @param[in] error The error - */ - TMVertexProperties(double error) : error(error) {} +struct TMVertexProperties { + double error; /// Error + + /** + * @brief Default constructor + */ + TMVertexProperties() : error(-1) {} + + /** + * @brief Constructor + * + * @param[in] error The error + */ + TMVertexProperties(double error) : error(error) {} }; /** * @brief Vertex data in a tetmesh */ -struct TMVertex : Vertex, TMVertexProperties -{ - TMVertex() : TMVertex(Vertex(), TMVertexProperties()) {} - template - TMVertex(Args && ... args) : TMVertex(Vertex(std::forward(args)...)) {} - TMVertex(Vertex v) : TMVertex(v, TMVertexProperties(-1)) {} - TMVertex(Vertex v, TMVertexProperties p) : Vertex(v), TMVertexProperties(p) {} - - /** - * @brief Operator<< overload - * - * @param output stream to print to - * @param[in] v Vertex to print - * - * @return the stream - */ - friend std::ostream &operator<<(std::ostream &output, const TMVertex &v) - { - output << "TMVertex(x:" << v[0] - << ",y:" << v[1] - << ",z:" << v[2] - << ";m:" << v.marker - << ";sel:" << v.selected - << ";err:" << v.error - << ")"; - return output; - } - - /** - * @brief Returns a string representation of the object. - * - * @return String representation of the object. - */ - std::string to_string() const - { - std::ostringstream output; - output << *this; - return output.str(); - } +struct TMVertex : Vertex, TMVertexProperties { + TMVertex() : TMVertex(Vertex(), TMVertexProperties()) {} + template + TMVertex(Args &&... args) : TMVertex(Vertex(std::forward(args)...)) {} + TMVertex(Vertex v) : TMVertex(v, TMVertexProperties(-1)) {} + TMVertex(Vertex v, TMVertexProperties p) : Vertex(v), TMVertexProperties(p) {} + + /** + * @brief Operator<< overload + * + * @param output stream to print to + * @param[in] v Vertex to print + * + * @return the stream + */ + friend std::ostream &operator<<(std::ostream &output, const TMVertex &v) { + output << "TMVertex(x:" << v[0] << ",y:" << v[1] << ",z:" << v[2] + << ";m:" << v.marker << ";sel:" << v.selected << ";err:" << v.error + << ")"; + return output; + } + + /** + * @brief Returns a string representation of the object. + * + * @return String representation of the object. + */ + std::string to_string() const { + std::ostringstream output; + output << *this; + return output.str(); + } }; - /** * @brief Edge data in a tetmesh */ -struct TMEdge : Vertex -{ - using Vertex::Vertex; - - /** - * @brief Operator<< overload - * - * @param output stream to print to - * @param[in] v Vertex to print - * - * @return the stream - */ - friend std::ostream &operator<<(std::ostream &output, const TMEdge &v) - { - output << "TMEdge(x:" << v[0] - << ",y:" << v[1] - << ",z:" << v[2] - << ";m:" << v.marker - << ";sel:" << v.selected - << ")"; - return output; - } - - /** - * @brief Returns a string representation of the object. - * - * @return String representation of the object. - */ - std::string to_string() const - { - std::ostringstream output; - output << *this; - return output.str(); - } +struct TMEdge : Vertex { + using Vertex::Vertex; + + /** + * @brief Operator<< overload + * + * @param output stream to print to + * @param[in] v Vertex to print + * + * @return the stream + */ + friend std::ostream &operator<<(std::ostream &output, const TMEdge &v) { + output << "TMEdge(x:" << v[0] << ",y:" << v[1] << ",z:" << v[2] + << ";m:" << v.marker << ";sel:" << v.selected << ")"; + return output; + } + + /** + * @brief Returns a string representation of the object. + * + * @return String representation of the object. + */ + std::string to_string() const { + std::ostringstream output; + output << *this; + return output.str(); + } }; /** * @brief Properties that Faces should have */ -struct TMFaceProperties -{ - int marker; /// Boundary marker value - bool selected; /// Selected property +struct TMFaceProperties { + int marker; /// Boundary marker value + bool selected; /// Selected property }; /** * @brief Face object */ -struct TMFace : TMFaceProperties -{ - /// Default constructor - TMFace() : TMFace(TMFaceProperties{-1, false}) {} - - /** - * @brief Constructor - * - * @param[in] marker Marker value - * @param[in] selected Selection status - */ - TMFace(int marker, bool selected) : TMFace(TMFaceProperties{marker, selected}) {} - - /** - * @brief Constructor - * - * @param[in] prop Properties of a face - */ - TMFace(TMFaceProperties prop) : TMFaceProperties(prop) {} - - /** - * @brief Print operator overload - * - * @param output The output - * @param[in] f Face data of interest - * - * @return Output - */ - friend std::ostream &operator<<(std::ostream &output, const TMFace &f) - { - output << "TMFace(" - << "m:" << f.marker - << ";sel:" << f.selected << ")"; - return output; - } - - /** - * @brief Returns a string representation of the object. - * - * @return String representation of the object. - */ - std::string to_string() const - { - std::ostringstream output; - output << *this; - return output.str(); - } +struct TMFace : TMFaceProperties { + /// Default constructor + TMFace() : TMFace(TMFaceProperties{-1, false}) {} + + /** + * @brief Constructor + * + * @param[in] marker Marker value + * @param[in] selected Selection status + */ + TMFace(int marker, bool selected) + : TMFace(TMFaceProperties{marker, selected}) {} + + /** + * @brief Constructor + * + * @param[in] prop Properties of a face + */ + TMFace(TMFaceProperties prop) : TMFaceProperties(prop) {} + + /** + * @brief Print operator overload + * + * @param output The output + * @param[in] f Face data of interest + * + * @return Output + */ + friend std::ostream &operator<<(std::ostream &output, const TMFace &f) { + output << "TMFace(" + << "m:" << f.marker << ";sel:" << f.selected << ")"; + return output; + } + + /** + * @brief Returns a string representation of the object. + * + * @return String representation of the object. + */ + std::string to_string() const { + std::ostringstream output; + output << *this; + return output.str(); + } }; /** * @brief Cell data properties */ -struct TMCellProperties -{ - int marker; /// Marker value - bool selected; /// Selected property +struct TMCellProperties { + int marker; /// Marker value + bool selected; /// Selected property }; /** * @brief Cell data */ -struct TMCell : casc::Orientable, TMCellProperties -{ - /// Default constructor - TMCell() : TMCell(-1, false) {} - - - /** - * @brief Constructor overload initializes marker and selection. - * - * @param[in] marker Marker value - * @param[in] selected Selection status - */ - TMCell(int marker, bool selected) : TMCell(0, marker, selected) {} - - - /** - * @brief Constructor overload initializes orientation, marker, and selectino. - * - * @param[in] orient Orientation of the cell - * @param[in] marker Marker value - * @param[in] selected Selection status - */ - TMCell(int orient, int marker, bool selected) : TMCell(Orientable{orient}, TMCellProperties{marker, selected}) {} - - /** - * @brief Operator overload - * - * @param[in] orient Orientation - * @param[in] prop Cell properties - */ - TMCell(Orientable orient, TMCellProperties prop) - : Orientable(orient), TMCellProperties(prop) - {} - - /** - * @brief Print operator overload - * - * @param output The output - * @param[in] c Cell of interest - * - * @return Output - */ - friend std::ostream &operator<<(std::ostream &output, const TMCell &c) - { - output << "tetmesh::Cell(" - << "m:" << c.marker - << ";sel:" << std::boolalpha << c.selected - << ";o:" << c.orientation << ")"; - return output; - } - - /** - * @brief Returns a string representation of the object. - * - * @return String representation of the object. - */ - std::string to_string() const - { - std::ostringstream output; - output << *this; - return output.str(); - } +struct TMCell : casc::Orientable, TMCellProperties { + /// Default constructor + TMCell() : TMCell(-1, false) {} + + /** + * @brief Constructor overload initializes marker and selection. + * + * @param[in] marker Marker value + * @param[in] selected Selection status + */ + TMCell(int marker, bool selected) : TMCell(0, marker, selected) {} + + /** + * @brief Constructor overload initializes orientation, marker, and + * selectino. + * + * @param[in] orient Orientation of the cell + * @param[in] marker Marker value + * @param[in] selected Selection status + */ + TMCell(int orient, int marker, bool selected) + : TMCell(Orientable{orient}, TMCellProperties{marker, selected}) {} + + /** + * @brief Operator overload + * + * @param[in] orient Orientation + * @param[in] prop Cell properties + */ + TMCell(Orientable orient, TMCellProperties prop) + : Orientable(orient), TMCellProperties(prop) {} + + /** + * @brief Print operator overload + * + * @param output The output + * @param[in] c Cell of interest + * + * @return Output + */ + friend std::ostream &operator<<(std::ostream &output, const TMCell &c) { + output << "tetmesh::Cell(" + << "m:" << c.marker << ";sel:" << std::boolalpha << c.selected + << ";o:" << c.orientation << ")"; + return output; + } + + /** + * @brief Returns a string representation of the object. + * + * @return String representation of the object. + */ + std::string to_string() const { + std::ostringstream output; + output << *this; + return output.str(); + } }; /// @cond detail /// Namespace for tetmesh details -namespace tetmesh_detail{ +namespace tetmesh_detail { /** * @brief A helper struct containing the traits/types in the simplicial * complex */ -struct tetmesh_traits -{ - /// The index type - using KeyType = int; - /// The types of each node - using NodeTypes = util::type_holder; - /// The types of each edge - using EdgeTypes = util::type_holder; +struct tetmesh_traits { + /// The index type + using KeyType = int; + /// The types of each node + using NodeTypes = + util::type_holder; + /// The types of each edge + using EdgeTypes = util::type_holder; }; } // end namespace tetmesh_detail /// @endcond @@ -335,16 +302,17 @@ using TetMesh = casc::simplicial_complex; std::unique_ptr tetgenioToTetMesh(tetgenio &tetio); /** - * @brief Call TetGen to make a tetrahedral mesh from a stack of surface meshes. + * @brief Call TetGen to make a tetrahedral mesh from a stack of surface + * meshes. * * @param[in] surfmeshes List of surface meshes * @param[in] tetgen_params TetGen parameters * * @return Tetrahedral mesh */ -std::unique_ptr makeTetMesh( - const std::vector &surfmeshes, - std::string tetgen_params); +std::unique_ptr +makeTetMesh(const std::vector &surfmeshes, + std::string tetgen_params); /** * @brief Extracts the boundary surface of a tetrahedral mesh @@ -355,7 +323,6 @@ std::unique_ptr makeTetMesh( */ std::unique_ptr extractSurface(const TetMesh &mesh); - /** * @brief Laplacian smoothing of tetrahedral mesh * @@ -387,6 +354,14 @@ void writeOFF(const std::string &filename, const TetMesh &mesh); */ void writeDolfin(const std::string &filename, const TetMesh &mesh); +/** + * @brief Writes the mesh out in Comsol mphtxt format. + * + * @param[in] filename The filename + * @param[in] mesh The mesh + */ +void writeComsol(const std::string &filename, const TetMesh &mesh); + /** * @brief Writes the mesh out in triangle format. * @@ -395,9 +370,9 @@ void writeDolfin(const std::string &filename, const TetMesh &mesh); */ void writeTriangle(const std::string &filename, const TetMesh &mesh); -//void writeMCSF(const std::string &filename, const TetMesh &mesh); -//void writeDiffPack -//void writeCARP +// void writeMCSF(const std::string &filename, const TetMesh &mesh); +// void writeDiffPack +// void writeCARP /** * @brief Reads in a mesh in dolfin XML format diff --git a/include/gamer/Vertex.h b/include/gamer/Vertex.h index 32533fc3..3f8d1831 100644 --- a/include/gamer/Vertex.h +++ b/include/gamer/Vertex.h @@ -1,26 +1,21 @@ -/* - * *************************************************************************** - * This file is part of the GAMer software. - * Copyright (C) 2016-2019 - * by Christopher Lee, John Moody, Rommie Amaro, J. Andrew McCammon, - * and Michael Holst - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * *************************************************************************** - */ +// This file is part of the GAMer software. +// Copyright (C) 2016-2021 +// by Christopher T. Lee and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, see +// or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA /** * @file Vertex.h @@ -37,234 +32,214 @@ #include "gamer/tensor.h" /// Namespace for all things gamer -namespace gamer -{ +namespace gamer { /** * @brief Vertex struct represents a general vertex */ -struct Vertex -{ - Vector position; /**< @brief a 3 tensor for x, y, z */ - int marker = 0; /**< @brief Boundary marking ID */ - bool selected = false; /**< @brief Selection flag */ - - /** - * @brief Default constructor with x,y,z = 0 - */ - Vertex() : Vertex(0, 0, 0) {} - - /** - * @brief Constructor with initialized position - * - * @param[in] x x-position of the vertex - * @param[in] y y-position of the vertex - * @param[in] z z-position of the vertex - */ - Vertex(REAL x, REAL y, REAL z) : Vertex(x, y, z, -1, false){} - - /** - * @brief Constructor with initialized position, marker, and selection - * - * @param[in] x x-position of the vertex - * @param[in] y y-position of the vertex - * @param[in] z z-position of the vertex - * @param[in] m marker ID - * @param[in] sel selection flag - */ - Vertex(REAL x, REAL y, REAL z, int m, bool sel) - { - position[0] = x; - position[1] = y; - position[2] = z; - marker = m; - selected = sel; - } - - /** - * @brief Constructor seeded from a Vector - * - * @param[in] v Vector position - */ - Vertex(Vector &v) : position(v), marker(-1), selected(false){} - - /** - * @brief Move construct a vertex from vector - * - * @param v Vector position - */ - Vertex(Vector &&v) : position(std::move(v)), marker(-1), selected(false) {} - - /** - * @brief Copy Constructor - * - * @param[in] x Vertex to copy - */ - Vertex(const Vertex &x) : position(x.position), marker(x.marker), selected(x.selected){} - - /** - * @brief Move Constructor - * - * @param[in] x Vertex to move - */ - Vertex(const Vertex &&x) : position(std::move(x.position)), marker(std::move(x.marker)), selected(std::move(x.selected)) {} - - /** - * @brief Implicit cast of Vertex to Vector type - */ - operator Vector() const { - return position; - } - - /** - * @brief Print operator overload - * - * @param output stream to print to - * @param[in] v Vertex to print - * - * @return the stream - */ - friend std::ostream &operator<<(std::ostream &output, const Vertex &v) - { - output << "Vertex(x:" << v[0] - << ",y:" << v[1] - << ",z:" << v[2] - << ";m:" << v.marker - << ";sel:" << v.selected - << ")"; - return output; - } - - /** - * @brief Returns a string representation of the object. - * - * @return String representation of the object. - */ - std::string to_string() const - { - std::ostringstream output; - output << *this; - return output.str(); - } - - /** - * @brief Const operator[] overload allows easy access to x, y, z using - * intuitive syntax - * - * @param[in] index Index to access - * - * @return Reference to the value at the index - */ - const REAL &operator[](std::size_t index) const - { - return position[index]; - } - - /** - * @brief Operator[] overload allows easy access to x, y, z using - *intuitive syntax - * - * @param[in] index Index to access - * - * @return Reference to the value at the index - */ - REAL &operator[](std::size_t index) - { - return position[index]; - } - - /** - * @brief Assignment operator overload - * - * @param[in] v vertex to assign - */ - void operator=(const Vertex &v) - { - position = v.position; - marker = v.marker; - selected = v.selected; - } - - /** - * @brief Equivalence operator - * - * @param[in] rhs The right hand side - * - * @return True if all values are equal - */ - bool operator==(const Vertex &rhs) const - { - Vertex temp(rhs); - if (position != temp.position) return false; - if (marker != temp.marker) return false; - if (selected != temp.selected) return false; - return true; - } - - /** - * @brief Inequivalence operator - * - * @param[in] rhs The right hand side - * - * @return True if not equal - */ - bool operator!=(const Vertex &rhs) const - { - return !(*this == rhs); - } - - /** - * @brief Add a vector to the vertex - * - * @param[in] rhs The right hand side - * - * @return Vertex with sum of positions - */ - Vertex &operator+=(const Vector &rhs) - { - // retains the marker of the lhs - position += rhs; - return *this; - } - - /** - * @brief Subtracts a vector from a vertex - * - * @param[in] rhs The right hand side - * - * @return Vertex with difference of positions - */ - Vertex &operator-=(const Vector &rhs) - { - // retains the marker of the lhs - position -= rhs; - return *this; - } - - /** - * @brief Multiply the Vertex by a scalar - * - * @param[in] x The scalar to multiply by - * - * @return Post multiplied vertex - */ - Vertex &operator*=(const REAL x) - { - position *= x; - return *this; - } - - /** - * @brief Divide the Vertex by a scalar - * - * @param[in] x Scalar to divide by - * - * @return Post divided vertex - */ - Vertex &operator/=(const REAL x) - { - position /= x; - return *this; - } +struct Vertex { + Vector position; /**< @brief a 3 tensor for x, y, z */ + int marker = 0; /**< @brief Boundary marking ID */ + bool selected = false; /**< @brief Selection flag */ + + /** + * @brief Default constructor with x,y,z = 0 + */ + Vertex() : Vertex(0, 0, 0) {} + + /** + * @brief Constructor with initialized position + * + * @param[in] x x-position of the vertex + * @param[in] y y-position of the vertex + * @param[in] z z-position of the vertex + */ + Vertex(REAL x, REAL y, REAL z) : Vertex(x, y, z, -1, false) {} + + /** + * @brief Constructor with initialized position, marker, and selection + * + * @param[in] x x-position of the vertex + * @param[in] y y-position of the vertex + * @param[in] z z-position of the vertex + * @param[in] m marker ID + * @param[in] sel selection flag + */ + Vertex(REAL x, REAL y, REAL z, int m, bool sel) { + position[0] = x; + position[1] = y; + position[2] = z; + marker = m; + selected = sel; + } + + /** + * @brief Constructor seeded from a Vector + * + * @param[in] v Vector position + */ + Vertex(Vector &v) : position(v), marker(-1), selected(false) {} + + /** + * @brief Move construct a vertex from vector + * + * @param v Vector position + */ + Vertex(Vector &&v) : position(std::move(v)), marker(-1), selected(false) {} + + /** + * @brief Copy Constructor + * + * @param[in] x Vertex to copy + */ + Vertex(const Vertex &x) + : position(x.position), marker(x.marker), selected(x.selected) {} + + /** + * @brief Move Constructor + * + * @param[in] x Vertex to move + */ + Vertex(const Vertex &&x) + : position(std::move(x.position)), marker(std::move(x.marker)), + selected(std::move(x.selected)) {} + + /** + * @brief Implicit cast of Vertex to Vector type + */ + operator Vector() const { return position; } + + /** + * @brief Print operator overload + * + * @param output stream to print to + * @param[in] v Vertex to print + * + * @return the stream + */ + friend std::ostream &operator<<(std::ostream &output, const Vertex &v) { + output << "Vertex(x:" << v[0] << ",y:" << v[1] << ",z:" << v[2] + << ";m:" << v.marker << ";sel:" << v.selected << ")"; + return output; + } + + /** + * @brief Returns a string representation of the object. + * + * @return String representation of the object. + */ + std::string to_string() const { + std::ostringstream output; + output << *this; + return output.str(); + } + + /** + * @brief Const operator[] overload allows easy access to x, y, z using + * intuitive syntax + * + * @param[in] index Index to access + * + * @return Reference to the value at the index + */ + const REAL &operator[](std::size_t index) const { return position[index]; } + + /** + * @brief Operator[] overload allows easy access to x, y, z using + *intuitive syntax + * + * @param[in] index Index to access + * + * @return Reference to the value at the index + */ + REAL &operator[](std::size_t index) { return position[index]; } + + /** + * @brief Assignment operator overload + * + * @param[in] v vertex to assign + */ + void operator=(const Vertex &v) { + position = v.position; + marker = v.marker; + selected = v.selected; + } + + /** + * @brief Equivalence operator + * + * @param[in] rhs The right hand side + * + * @return True if all values are equal + */ + bool operator==(const Vertex &rhs) const { + Vertex temp(rhs); + if (position != temp.position) + return false; + if (marker != temp.marker) + return false; + if (selected != temp.selected) + return false; + return true; + } + + /** + * @brief Inequivalence operator + * + * @param[in] rhs The right hand side + * + * @return True if not equal + */ + bool operator!=(const Vertex &rhs) const { return !(*this == rhs); } + + /** + * @brief Add a vector to the vertex + * + * @param[in] rhs The right hand side + * + * @return Vertex with sum of positions + */ + Vertex &operator+=(const Vector &rhs) { + // retains the marker of the lhs + position += rhs; + return *this; + } + + /** + * @brief Subtracts a vector from a vertex + * + * @param[in] rhs The right hand side + * + * @return Vertex with difference of positions + */ + Vertex &operator-=(const Vector &rhs) { + // retains the marker of the lhs + position -= rhs; + return *this; + } + + /** + * @brief Multiply the Vertex by a scalar + * + * @param[in] x The scalar to multiply by + * + * @return Post multiplied vertex + */ + Vertex &operator*=(const REAL x) { + position *= x; + return *this; + } + + /** + * @brief Divide the Vertex by a scalar + * + * @param[in] x Scalar to divide by + * + * @return Post divided vertex + */ + Vertex &operator/=(const REAL x) { + position /= x; + return *this; + } }; /** @@ -382,7 +357,6 @@ REAL angleDeg(const Vertex &A, const Vertex &B, const Vertex &C); */ REAL angleDeg(const Vector &AB, const Vector &CB); - /** * @brief Get the length of a vector * @@ -390,22 +364,18 @@ REAL angleDeg(const Vector &AB, const Vector &CB); * * @return length of the vector */ -inline REAL length(const Vector &A) -{ - return std::sqrt(A|A); -} +inline REAL length(const Vector &A) { return std::sqrt(A | A); } /** * @brief Normalize a vector * * @param A Vector of interest */ -inline void normalize(Vector &A) -{ - REAL mag = length(A); - if (mag == 0) - throw std::runtime_error("Cannot normalize a vector with length of 0."); - A /= mag; +inline void normalize(Vector &A) { + REAL mag = length(A); + if (mag == 0) + gamer_runtime_error("Cannot normalize a vector with length of 0."); + A /= mag; } -} //end namespace gamer +} // end namespace gamer diff --git a/include/gamer/gamer b/include/gamer/gamer index 08d8de12..f5c9a6ed 100644 --- a/include/gamer/gamer +++ b/include/gamer/gamer @@ -1,35 +1,31 @@ -/* - * *************************************************************************** - * This file is part of the GAMer software. - * Copyright (C) 2016-2018 - * by Christopher Lee, John Moody, Rommie Amaro, J. Andrew McCammon, - * and Michael Holst - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * *************************************************************************** - */ - +// This file is part of the GAMer software. +// Copyright (C) 2016-2021 +// by Christopher T. Lee and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, see +// or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA #include "gamer/gamer.h" #include "gamer/tensor.h" + +#include "gamer/Vertex.h" + +#include "gamer/SurfaceMesh.h" +#include "gamer/TetMesh.h" + +#include "gamer/EigenDiagonalization.h" #include "gamer/MarchingCube.h" #include "gamer/PDBReader.h" #include "gamer/stringutil.h" -#include "gamer/SurfaceMesh.h" -#include "gamer/tensor.h" -#include "gamer/TetMesh.h" -#include "gamer/Vertex.h" -#include "gamer/EigenDiagonalization.h" \ No newline at end of file diff --git a/include/gamer/gamer.h b/include/gamer/gamer.h index f75b6813..32233788 100644 --- a/include/gamer/gamer.h +++ b/include/gamer/gamer.h @@ -1,27 +1,21 @@ -/* - * *************************************************************************** - * This file is part of the GAMer software. - * Copyright (C) 2016-2018 - * by Christopher Lee, John Moody, Rommie Amaro, J. Andrew McCammon, - * and Michael Holst - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * *************************************************************************** - */ - +// This file is part of the GAMer software. +// Copyright (C) 2016-2021 +// by Christopher T. Lee and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, see +// or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA /** * @file gamer.h @@ -31,27 +25,50 @@ #pragma once -#include #include "gamer/tensor.h" +#include +#include + +namespace gamer { +namespace detail { +template +void throw_runtime_error(const char *function, const char *file, const int line, + T &&...ts) { + std::stringstream ss; + ss << "Error: "; + int dummy[] = {0, ((ss << std::forward(ts)), 0)...}; + static_cast(dummy); // Avoid warning for unused variable + ss << " in function " << function << " at " << file << ":" << line; + throw std::runtime_error(ss.str()); +} +} // namespace detail +} // namespace gamer + +#ifdef _MSC_VER + #define __PRETTY_FUNCTION__ __FUNCSIG__ +#endif + +#define gamer_runtime_error(...) \ + gamer::detail::throw_runtime_error(__PRETTY_FUNCTION__, __FILE__, __LINE__, \ + __VA_ARGS__); /// Namespace for all things gamer -namespace gamer -{ +namespace gamer { /// Blurring blobbyness to use in conversion from PDB/PQR to 3D volumes -#define BLOBBYNESS -0.2f +#define BLOBBYNESS -0.2f /// Discretization rate of 3D volumes -#define DIM_SCALE 1.99 +#define DIM_SCALE 1.99 /// The minimal volume (in voxels) of islands to be automatically removed -#define MIN_VOLUME 333333 +#define MIN_VOLUME 333333 #ifdef SINGLE /// Defines REAL to be float - #define REAL float +#define REAL float #else /// Defines REAL to be double - #define REAL double +#define REAL double #endif /// Floating point vector with precision defined by user at compile time @@ -97,8 +114,8 @@ using EigenVectorN = Eigen::Matrix; * @return Index of flat array corresponding to indices in * 3D array. */ -inline std::size_t Vect2Index(const std::size_t i, const std::size_t j, const std::size_t k, const Vector3szt &dim) -{ - return k*dim[0]*dim[1] + j*dim[0] + i; +inline std::size_t Vect2Index(const std::size_t i, const std::size_t j, + const std::size_t k, const Vector3szt &dim) { + return k * dim[0] * dim[1] + j * dim[0] + i; } } // end namespace gamer diff --git a/include/gamer/stringutil.h b/include/gamer/stringutil.h index f1619e0b..09a0a76b 100644 --- a/include/gamer/stringutil.h +++ b/include/gamer/stringutil.h @@ -1,26 +1,21 @@ -/* - * *************************************************************************** - * This file is part of the GAMer software. - * Copyright (C) 2016-2018 - * by Christopher Lee, John Moody, Rommie Amaro, J. Andrew McCammon, - * and Michael Holst - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * *************************************************************************** - */ +// This file is part of the GAMer software. +// Copyright (C) 2016-2021 +// by Christopher T. Lee and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, see +// or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA /** * @file stringutil.h @@ -35,14 +30,11 @@ #include #include - /// Namespace for all things gamer -namespace gamer -{ +namespace gamer { /// Namespace for string utilities -namespace stringutil -{ +namespace stringutil { /** * @brief Split a string into a vector of substrings * @@ -51,38 +43,34 @@ namespace stringutil * * @return Vector of substrings split at delimiters */ -inline std::vector split(const std::string &cstr, std::vector delim = {' ', '\t'}) -{ - std::string s = cstr; - // convert all delims into delim[0] - for (int i = 1; i < delim.size(); ++i) - { - std::replace(s.begin(), s.end(), delim[i], delim[0]); - } - std::vector result; - auto begin = s.begin(); - do - { - auto end = begin; - while (*end != delim[0] && end != s.end()) - end++; - if (end != begin) - result.push_back(std::string(begin, end)); - begin = end; - } - while (begin++ != s.end()); - return result; +inline std::vector split(const std::string &cstr, + std::vector delim = {' ', '\t'}) { + std::string s = cstr; + // convert all delims into delim[0] + for (int i = 1; i < delim.size(); ++i) { + std::replace(s.begin(), s.end(), delim[i], delim[0]); + } + std::vector result; + auto begin = s.begin(); + do { + auto end = begin; + while (*end != delim[0] && end != s.end()) + end++; + if (end != begin) + result.push_back(std::string(begin, end)); + begin = end; + } while (begin++ != s.end()); + return result; } /// @cond detail /// Namespace for internal string utility functions -namespace stringutil_detail -{ +namespace stringutil_detail { /// Functor for negated isspace -std::function isntspace = [](int c) -> int{ - return !std::isspace(c); - }; -} +std::function isntspace = [](int c) -> int { + return !std::isspace(c); +}; +} // namespace stringutil_detail /// @endcond /** @@ -90,20 +78,20 @@ std::function isntspace = [](int c) -> int{ * * @param s String to trim */ -inline void ltrim(std::string &s) -{ - s.erase(s.begin(), std::find_if(s.begin(), s.end(), stringutil_detail::isntspace)); +inline void ltrim(std::string &s) { + s.erase(s.begin(), + std::find_if(s.begin(), s.end(), stringutil_detail::isntspace)); } - /** * @brief Inplace removal of white space from the right side of a string * * @param s String to trim */ -inline void rtrim(std::string &s) -{ - s.erase(std::find_if(s.rbegin(), s.rend(), stringutil_detail::isntspace).base(), s.end()); +inline void rtrim(std::string &s) { + s.erase( + std::find_if(s.rbegin(), s.rend(), stringutil_detail::isntspace).base(), + s.end()); } /** @@ -111,10 +99,9 @@ inline void rtrim(std::string &s) * * @param s String to trim */ -inline void trim(std::string &s) -{ - ltrim(s); - rtrim(s); +inline void trim(std::string &s) { + ltrim(s); + rtrim(s); } } // end namespace stringutil } // end namespace gamer diff --git a/include/gamer/tensor.h b/include/gamer/tensor.h index dc6dd609..13e1920f 100644 --- a/include/gamer/tensor.h +++ b/include/gamer/tensor.h @@ -1,26 +1,21 @@ -/* - * *************************************************************************** - * This file is part of the GAMer software. - * Copyright (C) 2016-2018 - * by Christopher Lee, John Moody, Rommie Amaro, J. Andrew McCammon, - * and Michael Holst - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * *************************************************************************** - */ +// This file is part of the GAMer software. +// Copyright (C) 2016-2021 +// by Christopher T. Lee and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, see +// or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA /** * @file tensor.h @@ -29,40 +24,37 @@ #pragma once +#include #include #include #include #include -#include +#include #include +#include #include #include -#include -#include /// Namespace for all things gamer -namespace gamer -{ +namespace gamer { /// Namespace for tensor array management utilities -namespace array_util -{ +namespace array_util { /// @cond detail /// Namespace for tensor array management details -namespace detail -{ +namespace detail { template -void fill_arrayH(std::array &arr, S arg) -{ - static_assert(depth + 1 == N, "Size of array must match number of input arguments"); - arr[depth] = arg; +void fill_arrayH(std::array &arr, S arg) { + static_assert(depth + 1 == N, + "Size of array must match number of input arguments"); + arr[depth] = arg; } -template -void fill_arrayH(std::array &arr, S head, Ts... tail) -{ - arr[depth] = head; - fill_arrayH(arr, tail ...); +template +void fill_arrayH(std::array &arr, S head, Ts... tail) { + arr[depth] = head; + fill_arrayH(arr, tail...); } } // end namespace detail /// @endcond @@ -77,24 +69,22 @@ void fill_arrayH(std::array &arr, S head, Ts... tail) * @tparam N Number of array elements * @tparam Ts Typename of values */ -template -void fill_array(std::array &arr, Ts... args) -{ - static_assert(sizeof ... (args) == N, "Size of array must match number of input arguments"); - detail::fill_arrayH(arr, args ...); +template +void fill_array(std::array &arr, Ts... args) { + static_assert(sizeof...(args) == N, + "Size of array must match number of input arguments"); + detail::fill_arrayH(arr, args...); } /// @cond detail -namespace detail -{ +namespace detail { /** * @brief Base case * * @tparam Fn Functor * @tparam Ts Arguments list */ -template -struct flattenH {}; +template struct flattenH {}; /** * @brief Pass each element of sequence into functor(N, head) @@ -103,14 +93,12 @@ struct flattenH {}; * @tparam T Typename of head which is passed to the functor * @tparam Ts Arguments list */ -template +template struct flattenH { - template - static void apply(Fn f, T head, Ts... tail) - { - f(N, head); - flattenH::template apply(f, tail ...); - } + template static void apply(Fn f, T head, Ts... tail) { + f(N, head); + flattenH::template apply(f, tail...); + } }; /** @@ -118,27 +106,22 @@ struct flattenH { * * @tparam Fn Functor */ -template -struct flattenH { - template - static void apply(Fn f) { - } +template struct flattenH { + template static void apply(Fn f) {} }; /** * @brief Overload for processing array plus Ts... */ -template +template struct flattenH, Ts...> { - template - static void apply(Fn f, const std::array &head, Ts... tail) - { - for (std::size_t k = 0; k < K; ++k) - { - f(N+k, head[k]); - } - flattenH::template apply(f, tail ...); + template + static void apply(Fn f, const std::array &head, Ts... tail) { + for (std::size_t k = 0; k < K; ++k) { + f(N + k, head[k]); } + flattenH::template apply(f, tail...); + } }; } // end namespace detail /// @endcond @@ -152,36 +135,27 @@ struct flattenH, Ts...> { * @tparam Fn Typename of the functor * @tparam Ts Typenames of arguments */ -template -void flatten(Fn f, Ts... args) -{ - detail::flattenH::template apply<0>(f, args ...); +template void flatten(Fn f, Ts... args) { + detail::flattenH::template apply<0>(f, args...); } } // end namespace array_util /// @cond detail -namespace detail -{ -template -struct factorial { - constexpr static std::size_t value = k*factorial::value; +namespace detail { +template struct factorial { + constexpr static std::size_t value = k * factorial::value; }; -template <> -struct factorial<0> { - constexpr static std::size_t value = 1; -}; +template <> struct factorial<0> { constexpr static std::size_t value = 1; }; -template -struct pow { - constexpr static std::size_t value = x * pow::value; +template struct pow { + constexpr static std::size_t value = x * pow::value; }; -template -struct pow { - constexpr static std::size_t value = 1; +template struct pow { + constexpr static std::size_t value = 1; }; -} +} // namespace detail /// @endcond /** @@ -194,90 +168,76 @@ struct pow { * @tparam _vector_dimension Dimension of the vector space * @tparam _tensor_rank Rank of the tensor */ -template -class tensor -{ +template +class tensor { public: -/// Tensor rank of the tensor -constexpr static std::size_t tensor_rank = _tensor_rank; -/// Dimension of the vector space -constexpr static std::size_t vector_dimension = _vector_dimension; -/// Total number of tensor components -constexpr static std::size_t total_dimension = detail::pow::value; -/// Alias for typename of tensor elements -using ElemType = _ElemType; -/// Typename of an index for the tensor -using IndexType = std::array; -/// Typename of the underlying flat data representation -using DataType = std::array; - -/** - * @brief Iterator over tensor indices - */ -struct index_iterator : public std::iterator -{ + /// Tensor rank of the tensor + constexpr static std::size_t tensor_rank = _tensor_rank; + /// Dimension of the vector space + constexpr static std::size_t vector_dimension = _vector_dimension; + /// Total number of tensor components + constexpr static std::size_t total_dimension = + detail::pow::value; + /// Alias for typename of tensor elements + using ElemType = _ElemType; + /// Typename of an index for the tensor + using IndexType = std::array; + /// Typename of the underlying flat data representation + using DataType = std::array; + + /** + * @brief Iterator over tensor indices + */ + struct index_iterator + : public std::iterator { using super = std::iterator; /** * @brief Copy constructor * * @param[in] iter The iterator to copy from */ - index_iterator(const index_iterator &iter) - : i(iter.i) - { - } + index_iterator(const index_iterator &iter) : i(iter.i) {} /** * @brief Move constructor * * @param[in] iter The iterator to move from */ - index_iterator(const index_iterator &&iter) - : i(std::move(iter.i)) - { - } + index_iterator(const index_iterator &&iter) : i(std::move(iter.i)) {} /** * @brief Constructor initialized at end of indices */ - index_iterator() - { - i.fill(0); - i[0] = vector_dimension; + index_iterator() { + i.fill(0); + i[0] = vector_dimension; } /** * @brief Constructor initialized at start of indices */ - index_iterator(int) - { - i.fill(0); - } + index_iterator(int) { i.fill(0); } /** * @brief Prefix incrementation of the index * * @return Iterator to the next index */ - index_iterator &operator++() - { - std::size_t k = tensor_rank - 1; - ++(i[k]); - while (k > 0) - { - // incrementing overflows, advance the next index - if (i[k] == vector_dimension) - { - i[k] = 0; - --k; - ++(i[k]); - } - else - { - break; - } + index_iterator &operator++() { + std::size_t k = tensor_rank - 1; + ++(i[k]); + while (k > 0) { + // incrementing overflows, advance the next index + if (i[k] == vector_dimension) { + i[k] = 0; + --k; + ++(i[k]); + } else { + break; } - return *this; + } + return *this; } /** @@ -286,7 +246,9 @@ struct index_iterator : public std::iterator 0) - { + index_iterator &operator--() { + std::size_t k = tensor_rank - 1; + + if (i[k] > 0) { + --(i[k]); + } else { + std::size_t p = 1; + while (k > 0) { + --k; + ++p; + if (i[k] > 0) { --(i[k]); - } - else - { - std::size_t p = 1; - while (k > 0) - { - --k; - ++p; - if (i[k] > 0) - { - --(i[k]); - for (std::size_t j = 1; j < p; ++j) - { - i[k+j] = vector_dimension - 1; - } - break; - } + for (std::size_t j = 1; j < p; ++j) { + i[k + j] = vector_dimension - 1; } + break; + } } + } - return *this; + return *this; } /** @@ -330,7 +285,9 @@ struct index_iterator : public std::iterator() { - return i; - } + typename super::pointer operator->() { return i; } -protected: + protected: /// Array of indices IndexType i; -}; - -/** - * @brief Default constructor initializes to zero - */ -tensor() { - _data.fill(0); -} - -/** - * @brief Constructor fill with same value - * - * @param[in] s Value to fill - */ -tensor(const ElemType &s) { - _data.fill(s); -} - -/** - * @brief Constructor from flat array - * - * @param[in] s Row major array to copy from - */ -tensor(const ElemType (&s)[total_dimension]) -{ + }; + + /** + * @brief Default constructor initializes to zero + */ + tensor() { _data.fill(0); } + + /** + * @brief Constructor fill with same value + * + * @param[in] s Value to fill + */ + tensor(const ElemType &s) { _data.fill(s); } + + /** + * @brief Constructor from flat array + * + * @param[in] s Row major array to copy from + */ + tensor(const ElemType (&s)[total_dimension]) { std::copy(std::begin(s), std::end(s), std::begin(_data)); -} - -/** - * @brief Copy constructor - * - * @param[in] x Other tensor to copy - */ -tensor(const tensor &x) : _data(x._data) { -} - -/** - * @brief Move constructor - * - * @param[in] x Other tensor to copy from - */ -tensor(const tensor &&x) : _data(std::move(x._data)) { -} - - -/** - * @brief Type casting for numerical typed tensors - * - * @tparam NumType Typename of new tensor data - * @tparam Check that resulting type is numeric - * @tparam Check that current type is numeric - */ -template ::value>, - typename = std::enable_if_t::value> > -operator tensor() const -{ + } + + /** + * @brief Copy constructor + * + * @param[in] x Other tensor to copy + */ + tensor(const tensor &x) : _data(x._data) {} + + /** + * @brief Move constructor + * + * @param[in] x Other tensor to copy from + */ + tensor(const tensor &&x) : _data(std::move(x._data)) {} + + /** + * @brief Type casting for numerical typed tensors + * + * @tparam NumType Typename of new tensor data + * @tparam Check that resulting type is numeric + * @tparam Check that current type is numeric + */ + template ::value>, + typename = std::enable_if_t::value>> + operator tensor() const { tensor t; std::copy(_data.cbegin(), _data.cend(), t.begin()); return t; -} - -/** - * @brief Print operator overload - * - * @param output The output stream - * @param[in] t Tensor to print - * - * @return Output stream - */ -friend std::ostream &operator<<(std::ostream &output, const tensor &t) -{ + } + + /** + * @brief Print operator overload + * + * @param output The output stream + * @param[in] t Tensor to print + * + * @return Output stream + */ + friend std::ostream &operator<<(std::ostream &output, const tensor &t) { output << "Tensor("; bool first = true; - for (auto curr = t.index_begin(); curr != t.index_end(); ++curr) - { - if (first) {output << "{"; first = false;} - else output << "; {"; - - bool first2 = true; - for (auto x : (*curr)) - { - if (first2) {output << x; first2 = false;} - else output << "," << x; - } - output << "}:" << t[*curr]; + for (auto curr = t.index_begin(); curr != t.index_end(); ++curr) { + if (first) { + output << "{"; + first = false; + } else + output << "; {"; + + bool first2 = true; + for (auto x : (*curr)) { + if (first2) { + output << x; + first2 = false; + } else + output << "," << x; + } + output << "}:" << t[*curr]; } output << ")"; return output; -} - -/** - * @brief Returns a string representation of the object. - * - * @return String representation of the object. - */ -std::string to_string() const -{ + } + + /** + * @brief Returns a string representation of the object. + * + * @return String representation of the object. + */ + std::string to_string() const { std::ostringstream output; output << *this; return output.str(); -} - -/** - * @brief Get element by index - * - * @param index Sequence of indices - * - * @tparam Ts Typenames of indices - * - * @return Reference to value stored at the index - */ -template -const _ElemType &get(Ts && ... index) const -{ + } + + /** + * @brief Get element by index + * + * @param index Sequence of indices + * + * @tparam Ts Typenames of indices + * + * @return Reference to value stored at the index + */ + template const _ElemType &get(Ts &&... index) const { return _data[get_index(std::forward(index)...)]; -} - -/** - * @brief Get element by index - * - * @param index Sequence of indices - * - * @tparam Ts Typenames of indices - * - * @return Reference to value stored at the index - */ -template -_ElemType &get(Ts && ... index) -{ + } + + /** + * @brief Get element by index + * + * @param index Sequence of indices + * + * @tparam Ts Typenames of indices + * + * @return Reference to value stored at the index + */ + template _ElemType &get(Ts &&... index) { return _data[get_index(std::forward(index)...)]; -} - -/** - * @brief Get element by index - * - * @param index Sequence of indices - * - * @return Reference to value stored at the index - */ -const _ElemType &operator[](const IndexType &index) const -{ + } + + /** + * @brief Get element by index + * + * @param index Sequence of indices + * + * @return Reference to value stored at the index + */ + const _ElemType &operator[](const IndexType &index) const { return _data[get_index(index)]; -} - -/** - * @brief Get element by index - * - * @param index Sequence of indices - * - * @return Reference to value stored at the index - */ -_ElemType &operator[](const IndexType &index) -{ + } + + /** + * @brief Get element by index + * + * @param index Sequence of indices + * + * @return Reference to value stored at the index + */ + _ElemType &operator[](const IndexType &index) { return _data[get_index(index)]; -} - -/** - * @brief Special overload for data accession of 1-tensors - * - * @param index Sequence of indices - * - * @return Reference to value stored at the index - */ -const _ElemType &operator[](std::size_t index) const -{ - static_assert(_tensor_rank == 1, "operator[] with integer index only allowed on 1-tensors"); + } + + /** + * @brief Special overload for data accession of 1-tensors + * + * @param index Sequence of indices + * + * @return Reference to value stored at the index + */ + const _ElemType &operator[](std::size_t index) const { + static_assert(_tensor_rank == 1, + "operator[] with integer index only allowed on 1-tensors"); return _data[index]; -} - -/** - * @brief Special overload for data accession of 1-tensors - * - * @param index Sequence of indices - * - * @return Reference to value stored at the index - */ -_ElemType &operator[](std::size_t index) -{ - static_assert(_tensor_rank == 1, "operator[] with integer index only allowed on 1-tensors"); + } + + /** + * @brief Special overload for data accession of 1-tensors + * + * @param index Sequence of indices + * + * @return Reference to value stored at the index + */ + _ElemType &operator[](std::size_t index) { + static_assert(_tensor_rank == 1, + "operator[] with integer index only allowed on 1-tensors"); return _data[index]; -} - -/** - * @brief Equality comparison of tensors - * - * @param[in] rhs The right hand side - * - * @return True if all elements equal or False otherwise - */ -bool operator==(const tensor &rhs) const -{ + } + + /** + * @brief Equality comparison of tensors + * + * @param[in] rhs The right hand side + * + * @return True if all elements equal or False otherwise + */ + bool operator==(const tensor &rhs) const { auto rhs_curr = rhs.begin(); - for (auto tcurr = _data.begin(); tcurr != _data.end(); ++tcurr, ++rhs_curr) - { - if (*tcurr != *rhs_curr) return false; + for (auto tcurr = _data.begin(); tcurr != _data.end(); + ++tcurr, ++rhs_curr) { + if (*tcurr != *rhs_curr) + return false; } return true; -} - -/** - * @brief Inquality comparison of tensors - * - * @param[in] rhs The right hand side - * - * @return False if all elements equal or True otherwise - */ -bool operator!=(const tensor &rhs) const -{ - return !(*this == rhs); -} - -/** - * @brief Assignment operator - * - * @param[in] rhs The right hand side - */ -void operator=(const tensor &rhs) -{ + } + + /** + * @brief Inquality comparison of tensors + * + * @param[in] rhs The right hand side + * + * @return False if all elements equal or True otherwise + */ + bool operator!=(const tensor &rhs) const { return !(*this == rhs); } + + /** + * @brief Assignment operator + * + * @param[in] rhs The right hand side + */ + void operator=(const tensor &rhs) { auto rhs_curr = rhs.begin(); - for (auto tcurr = _data.begin(); tcurr != _data.end(); ++tcurr, ++rhs_curr) - { - *tcurr = *rhs_curr; + for (auto tcurr = _data.begin(); tcurr != _data.end(); + ++tcurr, ++rhs_curr) { + *tcurr = *rhs_curr; } -} - -/** - * @brief Assignment operator from Eigen object type - * - * @param[in] rhs Eigen object - * - * @tparam D Dependent template for tensor rank - * @tparam Enabled only for 1-tensors - */ -template ::type* = nullptr> -void operator=(const Eigen::Matrix<_ElemType, _vector_dimension, 1>& rhs) -{ + } + + /** + * @brief Assignment operator from Eigen object type + * + * @param[in] rhs Eigen object + * + * @tparam D Dependent template for tensor rank + * @tparam Enabled only for 1-tensors + */ + template ::type * = nullptr> + void operator=(const Eigen::Matrix<_ElemType, _vector_dimension, 1> &rhs) { EigenMap(*this) = rhs; -} - -/** - * @brief Assignment operator from Eigen object type - * - * @param[in] rhs Eigen object - * - * @tparam D Dependent template for tensor rank - * @tparam Enabled only for 2-tensors - */ -template ::type* = nullptr> -void operator=(const Eigen::Matrix<_ElemType, _vector_dimension, _vector_dimension>& rhs) -{ + } + + /** + * @brief Assignment operator from Eigen object type + * + * @param[in] rhs Eigen object + * + * @tparam D Dependent template for tensor rank + * @tparam Enabled only for 2-tensors + */ + template ::type * = nullptr> + void operator=(const Eigen::Matrix<_ElemType, _vector_dimension, + _vector_dimension> &rhs) { EigenMap(*this) = rhs; -} - -/** - * @brief Elementwise tensor sum operator - * - * @param[in] rhs The right hand side - * - * @return Reference to this - */ -tensor &operator+=(const tensor &rhs) -{ + } + + /** + * @brief Elementwise tensor sum operator + * + * @param[in] rhs The right hand side + * + * @return Reference to this + */ + tensor &operator+=(const tensor &rhs) { typename DataType::const_iterator rhs_curr = rhs.begin(); - for (typename DataType::iterator tcurr = _data.begin(); tcurr != _data.end(); ++tcurr, ++rhs_curr) - { - *tcurr += *rhs_curr; + for (typename DataType::iterator tcurr = _data.begin(); + tcurr != _data.end(); ++tcurr, ++rhs_curr) { + *tcurr += *rhs_curr; } return *this; -} - -/** - * @brief Elementwise tensor difference operator - * - * @param[in] rhs The right hand side - * - * @return Reference to this - */ -tensor &operator-=(const tensor &rhs) -{ + } + + /** + * @brief Elementwise tensor difference operator + * + * @param[in] rhs The right hand side + * + * @return Reference to this + */ + tensor &operator-=(const tensor &rhs) { typename DataType::const_iterator rhs_curr = rhs.begin(); - for (typename DataType::iterator tcurr = _data.begin(); tcurr != _data.end(); ++tcurr, ++rhs_curr) - { - *tcurr -= *rhs_curr; + for (typename DataType::iterator tcurr = _data.begin(); + tcurr != _data.end(); ++tcurr, ++rhs_curr) { + *tcurr -= *rhs_curr; } return *this; -} - -/** - * @brief Scalar multiplication - * - * @param[in] x Scalar to multiply by - * - * @return Reference to this - */ -tensor &operator*=(ElemType x) -{ - for (auto &a : *this) - { - a *= x; + } + + /** + * @brief Scalar multiplication + * + * @param[in] x Scalar to multiply by + * + * @return Reference to this + */ + tensor &operator*=(ElemType x) { + for (auto &a : *this) { + a *= x; } return *this; -} - -/** - * @brief Scalar division - * - * @param[in] x Scalar to divide by - * - * @return Reference to this - */ -tensor &operator/=(ElemType x) -{ - for (auto &a : *this) - { - a /= x; + } + + /** + * @brief Scalar division + * + * @param[in] x Scalar to divide by + * + * @return Reference to this + */ + tensor &operator/=(ElemType x) { + for (auto &a : *this) { + a /= x; } return *this; -} - -/** - * @brief Unary negation operator - * - * @return Tensor with negated values - */ -tensor operator-() const -{ + } + + /** + * @brief Unary negation operator + * + * @return Tensor with negated values + */ + tensor operator-() const { tensor rhs; typename DataType::iterator rhs_curr = rhs.begin(); - for (typename DataType::const_iterator tcurr = _data.begin(); tcurr != _data.end(); ++tcurr, ++rhs_curr) - { - *rhs_curr = -(*tcurr); + for (typename DataType::const_iterator tcurr = _data.begin(); + tcurr != _data.end(); ++tcurr, ++rhs_curr) { + *rhs_curr = -(*tcurr); } return rhs; -} - -/** - * @brief Elementwise product with another tensor - * - * @param[in] rhs The right hand side - * - * @return Tensor with elementwise product - */ -tensor ElementwiseProduct(const tensor &rhs) const -{ + } + + /** + * @brief Elementwise product with another tensor + * + * @param[in] rhs The right hand side + * + * @return Tensor with elementwise product + */ + tensor ElementwiseProduct(const tensor &rhs) const { tensor ret; typename DataType::iterator ret_curr = ret.begin(); typename DataType::const_iterator rhs_curr = rhs.begin(); - for (typename DataType::const_iterator tcurr = _data.begin(); tcurr != _data.end(); ++tcurr, ++rhs_curr, ++ret_curr) - { - *ret_curr = *tcurr * (*rhs_curr); + for (typename DataType::const_iterator tcurr = _data.begin(); + tcurr != _data.end(); ++tcurr, ++rhs_curr, ++ret_curr) { + *ret_curr = *tcurr * (*rhs_curr); } return ret; -} - -/** - * @brief Elementwise division with another tensor - * - * @param[in] rhs The right hand side - * - * @return Tensor with elementwise division - */ -tensor ElementwiseDivision(const tensor &rhs) const -{ + } + + /** + * @brief Elementwise division with another tensor + * + * @param[in] rhs The right hand side + * + * @return Tensor with elementwise division + */ + tensor ElementwiseDivision(const tensor &rhs) const { tensor ret; typename DataType::iterator ret_curr = ret.begin(); typename DataType::const_iterator rhs_curr = rhs.begin(); - for (typename DataType::const_iterator tcurr = _data.begin(); tcurr != _data.end(); ++tcurr, ++rhs_curr, ++ret_curr) - { - *ret_curr = *tcurr / (*rhs_curr); + for (typename DataType::const_iterator tcurr = _data.begin(); + tcurr != _data.end(); ++tcurr, ++rhs_curr, ++ret_curr) { + *ret_curr = *tcurr / (*rhs_curr); } return ret; -} - -/** - * @brief Direct access to the underlying array data - * - * @return Direct access to the underlying array - */ -auto data() -{ - return _data.data(); -} - - -/** - * @brief Returns a eigen representation of the object. - * - * @return Eigen representation of the object. - */ -template ::type* = nullptr> -Eigen::Map > toEigen() -{ - return Eigen::Map >(_data.data()); -} - -/** - * @brief Implicit conversion of 1-tensors to Eigen Vector type - * - * @return Eigen representation of the object. - */ -template ::type* = nullptr> -operator Eigen::Matrix<_ElemType, _vector_dimension, 1>() -{ - return Eigen::Map >(_data.data()); -} - -/** - * @brief Returns a eigen representation of the object. - * - * @tparam D Tensor rank - * @tparam Enabled only for 2-tensors - * - * @return Eigen representation of the object. - */ -template ::type* = nullptr> -Eigen::Map > toEigen() -{ - return Eigen::Map >(_data.data()); -} - -/** - * @brief Implicit conversion of 2-tensor to Eigen Matrix type - * - * @tparam D Tensor rank - * @tparam Enabled only for 2-tensors - */ -template ::type* = nullptr> -operator Eigen::Matrix<_ElemType, _vector_dimension, _vector_dimension> () -{ - return Eigen::Map >(_data.data()); -} - - -/** - * @brief Iterator to first index - * - * @return Iterator to first index - */ -index_iterator index_begin() { - return index_iterator(0); -} - -/** - * @brief Iterator to past the end - * - * @return Iterator to past the end - */ -index_iterator index_end() { - return index_iterator(); -} - -/** - * @brief Iterator to first index - * - * @return Iterator to first index - */ -index_iterator index_begin() const { - return index_iterator(0); -} - -/** - * @brief Iterator to past the end - * - * @return Iterator to past the end - */ -index_iterator index_end() const { - return index_iterator(); -} - -/** - * @brief Direct iterator to beginning of data - * - * @return Direct iterator to beginning of data - */ -typename DataType::iterator begin() { - return _data.begin(); -} - -/** - * @brief Direct iterator to end of data - * - * @return Direct iterator to end of data - */ -typename DataType::iterator end() { - return _data.end(); -} - -/** - * @brief Direct iterator to beginning of data - * - * @return Direct iterator to beginning of data - */ -typename DataType::const_iterator begin() const { - return _data.begin(); -} - -/** - * @brief Direct iterator to end of data - * - * @return Direct iterator to end of data - */ -typename DataType::const_iterator end() const { - return _data.end(); -} + } + + /** + * @brief Direct access to the underlying array data + * + * @return Direct access to the underlying array + */ + auto data() { return _data.data(); } + + /** + * @brief Returns a eigen representation of the object. + * + * @return Eigen representation of the object. + */ + template ::type * = nullptr> + Eigen::Map> toEigen() { + return Eigen::Map>( + _data.data()); + } + + /** + * @brief Implicit conversion of 1-tensors to Eigen Vector type + * + * @return Eigen representation of the object. + */ + template ::type * = nullptr> + operator Eigen::Matrix<_ElemType, _vector_dimension, 1>() { + return Eigen::Map>( + _data.data()); + } + + /** + * @brief Returns a eigen representation of the object. + * + * @tparam D Tensor rank + * @tparam Enabled only for 2-tensors + * + * @return Eigen representation of the object. + */ + template ::type * = nullptr> + Eigen::Map> + toEigen() { + return Eigen::Map< + Eigen::Matrix<_ElemType, _vector_dimension, _vector_dimension>>( + _data.data()); + } + + /** + * @brief Implicit conversion of 2-tensor to Eigen Matrix type + * + * @tparam D Tensor rank + * @tparam Enabled only for 2-tensors + */ + template ::type * = nullptr> + operator Eigen::Matrix<_ElemType, _vector_dimension, _vector_dimension>() { + return Eigen::Map< + Eigen::Matrix<_ElemType, _vector_dimension, _vector_dimension>>( + _data.data()); + } + + /** + * @brief Iterator to first index + * + * @return Iterator to first index + */ + index_iterator index_begin() { return index_iterator(0); } + + /** + * @brief Iterator to past the end + * + * @return Iterator to past the end + */ + index_iterator index_end() { return index_iterator(); } + + /** + * @brief Iterator to first index + * + * @return Iterator to first index + */ + index_iterator index_begin() const { return index_iterator(0); } + + /** + * @brief Iterator to past the end + * + * @return Iterator to past the end + */ + index_iterator index_end() const { return index_iterator(); } + + /** + * @brief Direct iterator to beginning of data + * + * @return Direct iterator to beginning of data + */ + typename DataType::iterator begin() { return _data.begin(); } + + /** + * @brief Direct iterator to end of data + * + * @return Direct iterator to end of data + */ + typename DataType::iterator end() { return _data.end(); } + + /** + * @brief Direct iterator to beginning of data + * + * @return Direct iterator to beginning of data + */ + typename DataType::const_iterator begin() const { return _data.begin(); } + + /** + * @brief Direct iterator to end of data + * + * @return Direct iterator to end of data + */ + typename DataType::const_iterator end() const { return _data.end(); } private: -/** - * @brief Gets the index. - * - * @param[in] args List of indices - * - * @tparam Ts Typename of indices - * - * @return The flat index to check - */ -template -std::size_t get_index(Ts... args) const -{ + /** + * @brief Gets the index. + * + * @param[in] args List of indices + * + * @tparam Ts Typename of indices + * + * @return The flat index to check + */ + template std::size_t get_index(Ts... args) const { std::size_t rval = 0; - array_util::flatten([&rval](std::size_t ignored, std::size_t i){ - rval *= vector_dimension; - rval += i; - }, args ...); + array_util::flatten( + [&rval](std::size_t ignored, std::size_t i) { + rval *= vector_dimension; + rval += i; + }, + args...); return rval; -} + } -DataType _data; + DataType _data; }; /** @@ -932,32 +835,26 @@ DataType _data; * @return Symmetric part of the tensor */ template -tensor Sym(const tensor &A) -{ - tensor rval; - - std::array sigma; - for (std::size_t i = 0; i < N; ++i) - { - sigma[i] = i; - } - - do - { - for (auto curr = rval.index_begin(); curr != rval.index_end(); ++curr) - { - std::array index = *curr; - for (std::size_t i = 0; i < N; ++i) - { - index[i] = (*curr)[sigma[i]]; - } - rval[*curr] += A[index]; - } +tensor Sym(const tensor &A) { + tensor rval; + + std::array sigma; + for (std::size_t i = 0; i < N; ++i) { + sigma[i] = i; + } + + do { + for (auto curr = rval.index_begin(); curr != rval.index_end(); ++curr) { + std::array index = *curr; + for (std::size_t i = 0; i < N; ++i) { + index[i] = (*curr)[sigma[i]]; + } + rval[*curr] += A[index]; } - while (std::next_permutation(sigma.begin(), sigma.end())); + } while (std::next_permutation(sigma.begin(), sigma.end())); - rval *= 1.0/detail::factorial::value; - return rval; + rval *= 1.0 / detail::factorial::value; + return rval; } /** @@ -969,31 +866,25 @@ tensor Sym(const tensor &A) * * @return 1 if even or -1 if odd number of inversions */ -template -int sgn(const std::array &arr) -{ - int rval = 1; - std::array visited; - visited.fill(false); - - for (std::size_t i = 1; i < N; ++i) - { - if (!visited[i]) - { - std::size_t len = 0; - std::size_t next = i; - do - { - ++len; - visited[next] = true; - next = arr[next]; - } - while (!visited[next]); - - rval *= 2*(len % 2) - 1; - } +template int sgn(const std::array &arr) { + int rval = 1; + std::array visited; + visited.fill(false); + + for (std::size_t i = 1; i < N; ++i) { + if (!visited[i]) { + std::size_t len = 0; + std::size_t next = i; + do { + ++len; + visited[next] = true; + next = arr[next]; + } while (!visited[next]); + + rval *= 2 * (len % 2) - 1; } - return rval; + } + return rval; } /** @@ -1008,32 +899,26 @@ int sgn(const std::array &arr) * @return Alternating tensor */ template -tensor Alt(const tensor &A) -{ - tensor rval; - - std::array sigma; - for (std::size_t i = 0; i < N; ++i) - { - sigma[i] = i; - } - - do - { - for (auto curr = rval.index_begin(); curr != rval.index_end(); ++curr) - { - std::array index = *curr; - for (std::size_t i = 0; i < N; ++i) - { - index[i] = (*curr)[sigma[i]]; - } - rval[*curr] += sgn(sigma)*(A[index]); - } +tensor Alt(const tensor &A) { + tensor rval; + + std::array sigma; + for (std::size_t i = 0; i < N; ++i) { + sigma[i] = i; + } + + do { + for (auto curr = rval.index_begin(); curr != rval.index_end(); ++curr) { + std::array index = *curr; + for (std::size_t i = 0; i < N; ++i) { + index[i] = (*curr)[sigma[i]]; + } + rval[*curr] += sgn(sigma) * (A[index]); } - while (std::next_permutation(sigma.begin(), sigma.end())); + } while (std::next_permutation(sigma.begin(), sigma.end())); - rval *= 1.0/detail::factorial::value; - return rval; + rval *= 1.0 / detail::factorial::value; + return rval; } /** @@ -1050,19 +935,17 @@ tensor Alt(const tensor &A) * @return Tensor with dimension D and rank N+M */ template -tensor operator*(const tensor &A, const tensor &B) -{ - tensor rval; - - for (auto pA = A.index_begin(); pA != A.index_end(); ++pA) - { - for (auto pB = B.index_begin(); pB != B.index_end(); ++pB) - { - rval.get(*pA, *pB) = A[*pA] * B[*pB]; - } +tensor operator*(const tensor &A, + const tensor &B) { + tensor rval; + + for (auto pA = A.index_begin(); pA != A.index_end(); ++pA) { + for (auto pB = B.index_begin(); pB != B.index_end(); ++pB) { + rval.get(*pA, *pB) = A[*pA] * B[*pB]; } + } - return rval; + return rval; } /** @@ -1078,11 +961,11 @@ tensor operator*(const tensor &A, const tensor * @return Elementwise sum of tensors */ template -tensor operator+(const tensor &A, const tensor &B) -{ - tensor rval(A); - rval += B; - return rval; +tensor operator+(const tensor &A, + const tensor &B) { + tensor rval(A); + rval += B; + return rval; } /** @@ -1098,11 +981,11 @@ tensor operator+(const tensor &A, const tensor -tensor operator-(const tensor &A, const tensor &B) -{ - tensor rval(A); - rval -= B; - return rval; +tensor operator-(const tensor &A, + const tensor &B) { + tensor rval(A); + rval -= B; + return rval; } /** @@ -1119,11 +1002,11 @@ tensor operator-(const tensor &A, const tensor -tensor operator*(const tensor &A, ScalarType x) -{ - auto rval(A); - rval *= x; - return rval; +tensor operator*(const tensor &A, + ScalarType x) { + auto rval(A); + rval *= x; + return rval; } /** @@ -1140,11 +1023,11 @@ tensor operator*(const tensor &A, ScalarType x) * @return Scalar tensor product */ template -tensor operator*(ScalarType x, const tensor &A) -{ - auto rval(A); - rval *= x; - return rval; +tensor operator*(ScalarType x, + const tensor &A) { + auto rval(A); + rval *= x; + return rval; } /** @@ -1161,11 +1044,11 @@ tensor operator*(ScalarType x, const tensor &A) * @return Divided tensor */ template -tensor operator/(const tensor &A, ScalarType x) -{ - auto rval(A); - rval /= x; - return rval; +tensor operator/(const tensor &A, + ScalarType x) { + auto rval(A); + rval /= x; + return rval; } /** @@ -1182,13 +1065,13 @@ tensor operator/(const tensor &A, ScalarType x) * @return Tensor with dimension D and rank N+M */ template -tensor operator^(const tensor &A, const tensor &B) -{ - auto rval = Alt(A*B); - ElemType num = detail::factorial::value; - ElemType den = detail::factorial::value * detail::factorial::value; - rval *= num / den; - return rval; +tensor operator^(const tensor &A, + const tensor &B) { + auto rval = Alt(A * B); + ElemType num = detail::factorial::value; + ElemType den = detail::factorial::value * detail::factorial::value; + rval *= num / den; + return rval; } /** @@ -1204,9 +1087,8 @@ tensor operator^(const tensor &A, const tensor * @return Tensor inner product */ template -ElemType dot(const tensor &A, const tensor &B) -{ - return A|B; +ElemType dot(const tensor &A, const tensor &B) { + return A | B; } /** @@ -1222,17 +1104,16 @@ ElemType dot(const tensor &A, const tensor &B) * @return Tensor inner product */ template -ElemType operator|(const tensor &A, const tensor &B) -{ - ElemType rval = 0; - auto Acurr = A.begin(); - auto Bcurr = B.begin(); - for (; Acurr != A.end(); ++Acurr, ++Bcurr) - { - rval += (*Acurr)*(*Bcurr); - } - double scale = detail::factorial::value; - return rval / scale; +ElemType operator|(const tensor &A, + const tensor &B) { + ElemType rval = 0; + auto Acurr = A.begin(); + auto Bcurr = B.begin(); + for (; Acurr != A.end(); ++Acurr, ++Bcurr) { + rval += (*Acurr) * (*Bcurr); + } + double scale = detail::factorial::value; + return rval / scale; } /** @@ -1246,13 +1127,13 @@ ElemType operator|(const tensor &A, const tensor * @return Cross product */ template -tensor cross(const tensor &x, const tensor &y) -{ - tensor rval; - rval[0] = x[1]*y[2] - y[1]*x[2]; - rval[1] = x[2]*y[0] - y[2]*x[0]; - rval[2] = x[0]*y[1] - y[0]*x[1]; - return rval; +tensor cross(const tensor &x, + const tensor &y) { + tensor rval; + rval[0] = x[1] * y[2] - y[1] * x[2]; + rval[1] = x[2] * y[0] - y[2] * x[0]; + rval[2] = x[0] * y[1] - y[0] * x[1]; + return rval; } /** @@ -1267,12 +1148,12 @@ tensor cross(const tensor &x, const tensor::type* = nullptr> -Eigen::Map > -EigenMap(tensor<_ElemType, _vector_dimension, _tensor_rank>& t) -{ - return Eigen::Map >(t.data()); +template ::type * = nullptr> +Eigen::Map> +EigenMap(tensor<_ElemType, _vector_dimension, _tensor_rank> &t) { + return Eigen::Map>(t.data()); } /** @@ -1287,11 +1168,12 @@ EigenMap(tensor<_ElemType, _vector_dimension, _tensor_rank>& t) * * @return Eigen map object */ -template ::type* = nullptr> -Eigen::Map > -EigenMap(tensor<_ElemType, _vector_dimension, _tensor_rank>& t) -{ - return Eigen::Map >(t.data()); +template ::type * = nullptr> +Eigen::Map> +EigenMap(tensor<_ElemType, _vector_dimension, _tensor_rank> &t) { + return Eigen::Map< + Eigen::Matrix<_ElemType, _vector_dimension, _vector_dimension>>(t.data()); } } // end namespace gamer diff --git a/include/gamer/version.h b/include/gamer/version.h index 09f2952d..6a72be13 100644 --- a/include/gamer/version.h +++ b/include/gamer/version.h @@ -1,26 +1,21 @@ -/* - * *************************************************************************** - * This file is part of the GAMer software. - * Copyright (C) 2016-2018 - * by Christopher Lee, John Moody, Rommie Amaro, J. Andrew McCammon, - * and Michael Holst - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * *************************************************************************** - */ +// This file is part of the GAMer software. +// Copyright (C) 2016-2021 +// by Christopher T. Lee and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, see +// or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA /** * @file version.h @@ -32,8 +27,7 @@ #include /// Namespace for all things gamer -namespace gamer -{ +namespace gamer { /// Version of the library extern const std::string gVERSION; /// Short version string diff --git a/libraries/CMakeLists.txt b/libraries/CMakeLists.txt index cceaec9a..8d390997 100644 --- a/libraries/CMakeLists.txt +++ b/libraries/CMakeLists.txt @@ -25,11 +25,7 @@ if(NOT Git_FOUND) message(FATAL_ERROR "Git could not be found. The install process requires Git to download dependencies.") endif() -if(${CMAKE_VERSION} VERSION_LESS 3.11) - include(FetchContentLocal) -else() - include(FetchContent) -endif() +include(FetchContent) ########################### # GET CASC @@ -38,6 +34,7 @@ FetchContent_Declare( casclib GIT_REPOSITORY https://github.com/ctlee/casc.git GIT_TAG v1.0.5 + GIT_SHALLOW TRUE SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/casc-src" BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/casc-build" ) @@ -48,31 +45,7 @@ if(NOT casclib_POPULATED) endif() ########################### -# GET PYBIND11 2.2.4 -########################### -if(NOT GETPYBIND11) - find_package(pybind11) - if(NOT pybind11_FOUND) - message(FATAL_ERROR "Could not find required library pybind11." - "Please append -DGETPYBIND11=ON to your cmake call and I will download pybind11 for you.") - endif() -else() - FetchContent_Declare( - pybind11 - GIT_REPOSITORY https://github.com/pybind/pybind11.git - GIT_TAG v2.2.4 - SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/pybind11-src" - BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/pybind11-build" - ) - FetchContent_GetProperties(pybind11) - if(NOT pybind11_POPULATED) - FetchContent_Populate(pybind11) - add_subdirectory(${pybind11_SOURCE_DIR} ${pybind11_BINARY_DIR}) - endif() -endif() - -########################### -# FIND/GET EIGEN 3.3.7 +# FIND/GET EIGEN ########################### if(NOT GETEIGEN) find_package (Eigen3 3.3 NO_MODULE) @@ -80,13 +53,13 @@ if(NOT GETEIGEN) message(FATAL_ERROR "Could not find required library Eigen." "Please append -DGETEIGEN=on to your cmake call and I will download Eigen for you.") endif() - add_library(eigen ALIAS Eigen3::Eigen) - target_compile_options(eigen PRIVATE -w) # Suppress warnings + target_compile_options(Eigen3::Eigen PRIVATE -w) else() FetchContent_Declare( eigen - GIT_REPOSITORY https://github.com/eigenteam/eigen-git-mirror.git - GIT_TAG 3.3.7 + GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git + GIT_TAG 3.3.9 + GIT_SHALLOW TRUE SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/eigen-src" BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/eigen-build" ) @@ -94,6 +67,7 @@ else() if(NOT eigen_POPULATED) FetchContent_Populate(eigen) add_library (eigen INTERFACE) + add_library (Eigen3::Eigen ALIAS eigen) target_include_directories(eigen INTERFACE $) target_compile_options(eigen INTERFACE -w) # Suppress warnings endif() diff --git a/libraries/tetgen/CMakeLists.txt b/libraries/tetgen/CMakeLists.txt index 159a59cf..b23cf46e 100644 --- a/libraries/tetgen/CMakeLists.txt +++ b/libraries/tetgen/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.0) -project(Tetgen VERSION 1.5.0) +project(Tetgen VERSION 1.6.0) set(TETGEN_MASTER_PROJECT OFF) if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) @@ -26,7 +26,6 @@ if(CMAKE_SYSTEM_NAME STREQUAL Linux AND CMAKE_COMPILER_IS_GNUCC) set_source_files_properties(predicates.cxx PROPERTIES COMPILE_FLAGS -O0) elseif(WIN32) list(APPEND COMPILE_DEFS CPU86) - list(APPEND COMPILE_DEFS _MSC_VER) if(CMAKE_SIZEOF_VOID_P EQUAL 8) list(APPEND COMPILE_DEFS _WIN64) endif() @@ -87,4 +86,4 @@ if(BUILD_TETGEN_BIN) add_executable(tetgen tetgen.cxx predicates.cxx) target_compile_definitions(tetgen PRIVATE ${COMPILE_DEFS}) install(TARGETS tetgen DESTINATION bin) -endif() \ No newline at end of file +endif() diff --git a/libraries/tetgen/README b/libraries/tetgen/README index bc5cfa04..4738a074 100644 --- a/libraries/tetgen/README +++ b/libraries/tetgen/README @@ -1,4 +1,4 @@ -This is TetGen version 1.5 (released on November 4, 2013) +This is TetGen version 1.6.0 (released on August 31, 2020) Please see the documentation of TetGen for compiling and using TetGen. It is available at the following link: @@ -13,7 +13,6 @@ For more information on this product, contact : Mohrenstr. 39 10117 Berlin, Germany - Phone: +49 (0) 30-20372-446 Fax: +49 (0) 30-2044975 EMail: Web Site: http://www.wias-berlin.de/~si diff --git a/libraries/tetgen/predicates.cxx b/libraries/tetgen/predicates.cxx index 33817d79..bb713704 100644 --- a/libraries/tetgen/predicates.cxx +++ b/libraries/tetgen/predicates.cxx @@ -399,7 +399,7 @@ static REAL ispstaticfilter; // http://www.math.utah.edu/~beebe/software/ieee/ // The original program was "fpinfo2.c". -double fppow2(int n) +static double fppow2(int n) { double x, power; x = (n < 0) ? ((double)1.0/(double)2.0) : (double)2.0; @@ -412,12 +412,12 @@ double fppow2(int n) #ifdef SINGLE -float fstore(float x) +static float fstore(float x) { return (x); } -int test_float(int verbose) +static int test_float(int verbose) { float x; int pass = 1; @@ -467,12 +467,12 @@ int test_float(int verbose) # else -double dstore(double x) +static double dstore(double x) { return (x); } -int test_double(int verbose) +static int test_double(int verbose) { double x; int pass = 1; @@ -629,10 +629,6 @@ void exactinit(int verbose, int noexact, int nofilter, REAL maxx, REAL maxy, // Added by H. Si, 2012-08-23. // Sort maxx < maxy < maxz. Re-use 'half' for swapping. - assert(maxx > 0); - assert(maxy > 0); - assert(maxz > 0); - if (maxx > maxz) { half = maxx; maxx = maxz; maxz = half; } @@ -885,7 +881,7 @@ int expansion_sum_zeroelim2(int elen, REAL *e, int flen, REAL *f, REAL *h) /* */ /*****************************************************************************/ -int fast_expansion_sum(int elen, REAL *e, int flen, REAL *f, REAL *h) +static int fast_expansion_sum(int elen, REAL *e, int flen, REAL *f, REAL *h) /* h cannot be e or f. */ { REAL Q; @@ -957,7 +953,7 @@ int fast_expansion_sum(int elen, REAL *e, int flen, REAL *f, REAL *h) /* properties. */ /* */ /*****************************************************************************/ - +static int fast_expansion_sum_zeroelim(int elen, REAL *e, int flen, REAL *f, REAL *h) /* h cannot be e or f. */ { @@ -1169,7 +1165,7 @@ int linear_expansion_sum_zeroelim(int elen, REAL *e, int flen, REAL *f, /* will h.) */ /* */ /*****************************************************************************/ - +static int scale_expansion(int elen, REAL *e, REAL b, REAL *h) /* e and h cannot be the same. */ { @@ -1215,7 +1211,7 @@ int scale_expansion(int elen, REAL *e, REAL b, REAL *h) /* will h.) */ /* */ /*****************************************************************************/ - +static int scale_expansion_zeroelim(int elen, REAL *e, REAL b, REAL *h) /* e and h cannot be the same. */ { @@ -1267,7 +1263,7 @@ int scale_expansion_zeroelim(int elen, REAL *e, REAL b, REAL *h) /* nonadjacent expansion. */ /* */ /*****************************************************************************/ - +static int compress(int elen, REAL *e, REAL *h) /* e and h may be the same. */ { @@ -1310,7 +1306,7 @@ int compress(int elen, REAL *e, REAL *h) /* See either version of my paper for details. */ /* */ /*****************************************************************************/ - +static REAL estimate(int elen, REAL *e) { REAL Q; @@ -1348,7 +1344,7 @@ REAL estimate(int elen, REAL *e) /* nearly so. */ /* */ /*****************************************************************************/ - +static REAL orient2dfast(REAL *pa, REAL *pb, REAL *pc) { REAL acx, bcx, acy, bcy; @@ -1360,6 +1356,7 @@ REAL orient2dfast(REAL *pa, REAL *pb, REAL *pc) return acx * bcy - acy * bcx; } +//static REAL orient2dexact(REAL *pa, REAL *pb, REAL *pc) { INEXACT REAL axby1, axcy1, bxcy1, bxay1, cxay1, cxby1; @@ -4185,7 +4182,7 @@ REAL insphere(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe) /* See my Robust Predicates paper for details. */ /* */ /*****************************************************************************/ - +//static REAL orient4dexact(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe, REAL aheight, REAL bheight, REAL cheight, REAL dheight, REAL eheight) @@ -4401,6 +4398,7 @@ REAL orient4dexact(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe, return deter[deterlen - 1]; } +static REAL orient4dadapt(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe, REAL aheight, REAL bheight, REAL cheight, REAL dheight, REAL eheight, REAL permanent) @@ -4704,3 +4702,9 @@ REAL orient4d(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe, + + + +//============================================================================== + + diff --git a/libraries/tetgen/tetgen.cxx b/libraries/tetgen/tetgen.cxx index f37ac825..899a47b6 100644 --- a/libraries/tetgen/tetgen.cxx +++ b/libraries/tetgen/tetgen.cxx @@ -1,38 +1,70 @@ -/////////////////////////////////////////////////////////////////////////////// -// // -// TetGen // -// // -// A Quality Tetrahedral Mesh Generator and A 3D Delaunay Triangulator // -// // -// Version 1.5 // -// November 4, 2013 // -// // -// TetGen is freely available through the website: http://www.tetgen.org. // -// It may be copied, modified, and redistributed for non-commercial use. // -// Please consult the file LICENSE for the detailed copyright notices. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// TetGen // +// // +// A Quality Tetrahedral Mesh Generator and A 3D Delaunay Triangulator // +// // +// Version 1.6.0 // +// August 31, 2020 // +// // +// Copyright (C) 2002--2020 // +// // +// Hang Si // +// Research Group: Numerical Mathematics and Scientific Computing // +// Weierstrass Institute for Applied Analysis and Stochastics (WIAS) // +// Mohrenstr. 39, 10117 Berlin, Germany // +// si@wias-berlin.de // +// // +// TetGen is a tetrahedral mesh generator. It creates 3d triangulations of // +// polyhedral domains. It generates meshes with well-shaped elements whose // +// sizes are adapted to the geometric features or user-provided sizing // +// functions. It has applications in various applications in scientific // +// computing, such as computer graphics (CG), computer-aided design (CAD), // +// geometry processing (parametrizations and computer animation), and // +// physical simulations (finite element analysis). // +// // +// TetGen computes (weighted) Delaunay triangulations for three-dimensional // +// (weighted) point sets, and constrained Delaunay triangulations for // +// three-dimensional polyhedral domains. In the latter case, input edges // +// and triangles can be completely preserved in the output meshes. TetGen // +// can refine or coarsen an existing mesh to result in good quality and // +// size-adapted mesh according to the geometric features and user-defined // +// mesh sizing functions. // +// // +// TetGen implements theoretically proven algorithms for computing the // +// Delaunay and constrained Delaunay tetrahedralizations. TetGen achieves // +// robustness and efficiency by using advanced techniques in computational // +// geometry. A technical paper describes the algorithms and methods // +// implemented in TetGen is available in ACM-TOMS, Hang Si ``TetGen, a // +// Delaunay-Based Quality Tetrahedral Mesh Generator", ACM Transactions on // +// Mathematical Software, February 2015, https://doi.org/10.1145/2629697. // +// // +// TetGen is freely available through the website: http://www.tetgen.org. // +// It may be copied, modified, and redistributed for non-commercial use. // +// Please consult the file LICENSE for the detailed copyright notices. // +// // +//============================================================================// #include "tetgen.h" -//// io_cxx /////////////////////////////////////////////////////////////////// -//// //// -//// //// - -/////////////////////////////////////////////////////////////////////////////// -// // -// load_node_call() Read a list of points from a file. // -// // -// 'infile' is the file handle contains the node list. It may point to a // -// .node, or .poly or .smesh file. 'markers' indicates each node contains an // -// additional marker (integer) or not. 'uvflag' indicates each node contains // -// u,v coordinates or not. It is reuqired by a PSC. 'infilename' is the name // -// of the file being read, it is only used in error messages. // -// // -// The 'firstnumber' (0 or 1) is automatically determined by the number of // -// the first index of the first point. // -// // -/////////////////////////////////////////////////////////////////////////////// +//== io_cxx ==================================================================// +// // +// // + +//============================================================================// +// // +// load_node_call() Read a list of points from a file. // +// // +// 'infile' is the file handle contains the node list. It may point to a // +// .node, or .poly or .smesh file. 'markers' indicates each node contains an // +// additional marker (integer) or not. 'uvflag' indicates each node contains // +// u,v coordinates or not. It is reuqired by a PSC. 'infilename' is the name // +// of the file being read, it is only used in error messages. // +// // +// The 'firstnumber' (0 or 1) is automatically determined by the number of // +// the first index of the first point. // +// // +//============================================================================// bool tetgenio::load_node_call(FILE* infile, int markers, int uvflag, char* infilename) @@ -126,37 +158,6 @@ bool tetgenio::load_node_call(FILE* infile, int markers, int uvflag, } pointmarkerlist[i] = currentmarker; } - if (uvflag) { - // Read point paramteters. - stringptr = findnextnumber(stringptr); - if (*stringptr == '\0') { - printf("Error: Point %d has no uv[0].\n", firstnumber + i); - break; - } - pointparamlist[i].uv[0] = (REAL) strtod(stringptr, &stringptr); - stringptr = findnextnumber(stringptr); - if (*stringptr == '\0') { - printf("Error: Point %d has no uv[1].\n", firstnumber + i); - break; - } - pointparamlist[i].uv[1] = (REAL) strtod(stringptr, &stringptr); - stringptr = findnextnumber(stringptr); - if (*stringptr == '\0') { - printf("Error: Point %d has no tag.\n", firstnumber + i); - break; - } - pointparamlist[i].tag = (int) strtol (stringptr, &stringptr, 0); - stringptr = findnextnumber(stringptr); - if (*stringptr == '\0') { - printf("Error: Point %d has no type.\n", firstnumber + i); - break; - } - pointparamlist[i].type = (int) strtol (stringptr, &stringptr, 0); - if ((pointparamlist[i].type < 0) || (pointparamlist[i].type > 2)) { - printf("Error: Point %d has an invalid type.\n", firstnumber + i); - break; - } - } } if (i < numberofpoints) { // Failed to read points due to some error. @@ -180,13 +181,13 @@ bool tetgenio::load_node_call(FILE* infile, int markers, int uvflag, return true; } -/////////////////////////////////////////////////////////////////////////////// -// // -// load_node() Load a list of points from a .node file. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// load_node() Load a list of points from a .node file. // +// // +//============================================================================// -bool tetgenio::load_node(const char* filebasename) +bool tetgenio::load_node(char* filebasename) { FILE *infile; char innodefilename[FILENAMESIZE]; @@ -258,13 +259,13 @@ bool tetgenio::load_node(const char* filebasename) return okflag; } -/////////////////////////////////////////////////////////////////////////////// -// // -// load_edge() Load a list of edges from a .edge file. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// load_edge() Load a list of edges from a .edge file. // +// // +//============================================================================// -bool tetgenio::load_edge(const char* filebasename) +bool tetgenio::load_edge(char* filebasename) { FILE *infile; char inedgefilename[FILENAMESIZE]; @@ -339,13 +340,13 @@ bool tetgenio::load_edge(const char* filebasename) return true; } -/////////////////////////////////////////////////////////////////////////////// -// // -// load_face() Load a list of faces (triangles) from a .face file. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// load_face() Load a list of faces (triangles) from a .face file. // +// // +//============================================================================// -bool tetgenio::load_face(const char* filebasename) +bool tetgenio::load_face(char* filebasename) { FILE *infile; char infilename[FILENAMESIZE]; @@ -435,13 +436,13 @@ bool tetgenio::load_face(const char* filebasename) return true; } -/////////////////////////////////////////////////////////////////////////////// -// // -// load_tet() Load a list of tetrahedra from a .ele file. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// load_tet() Load a list of tetrahedra from a .ele file. // +// // +//============================================================================// -bool tetgenio::load_tet(const char* filebasename) +bool tetgenio::load_tet(char* filebasename) { FILE *infile; char infilename[FILENAMESIZE]; @@ -542,13 +543,13 @@ bool tetgenio::load_tet(const char* filebasename) return true; } -/////////////////////////////////////////////////////////////////////////////// -// // -// load_vol() Load a list of volume constraints from a .vol file. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// load_vol() Load a list of volume constraints from a .vol file. // +// // +//============================================================================// -bool tetgenio::load_vol(const char* filebasename) +bool tetgenio::load_vol(char* filebasename) { FILE *infile; char inelefilename[FILENAMESIZE]; @@ -603,14 +604,14 @@ bool tetgenio::load_vol(const char* filebasename) return true; } -/////////////////////////////////////////////////////////////////////////////// -// // -// load_var() Load constraints applied on facets, segments, and nodes // -// from a .var file. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// load_var() Load constraints applied on facets, segments, and nodes // +// from a .var file. // +// // +//============================================================================// -bool tetgenio::load_var(const char* filebasename) +bool tetgenio::load_var(char* filebasename) { FILE *infile; char varfilename[FILENAMESIZE]; @@ -631,6 +632,11 @@ bool tetgenio::load_var(const char* filebasename) // Read the facet constraint section. stringptr = readnumberline(inputline, infile, varfilename); + if (stringptr == NULL) { + // No region list, return. + fclose(infile); + return true; + } if (*stringptr != '\0') { numberoffacetconstraints = (int) strtol (stringptr, &stringptr, 0); } else { @@ -668,6 +674,11 @@ bool tetgenio::load_var(const char* filebasename) // Read the segment constraint section. stringptr = readnumberline(inputline, infile, varfilename); + if (stringptr == NULL) { + // No segment list, return. + fclose(infile); + return true; + } if (*stringptr != '\0') { numberofsegmentconstraints = (int) strtol (stringptr, &stringptr, 0); } else { @@ -715,13 +726,13 @@ bool tetgenio::load_var(const char* filebasename) return true; } -/////////////////////////////////////////////////////////////////////////////// -// // -// load_mtr() Load a size specification map from a .mtr file. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// load_mtr() Load a size specification map from a .mtr file. // +// // +//============================================================================// -bool tetgenio::load_mtr(const char* filebasename) +bool tetgenio::load_mtr(char* filebasename) { FILE *infile; char mtrfilename[FILENAMESIZE]; @@ -754,9 +765,13 @@ bool tetgenio::load_mtr(const char* filebasename) if (*stringptr != '\0') { numberofpointmtrs = (int) strtol (stringptr, &stringptr, 0); } - if (numberofpointmtrs == 0) { - // Column number doesn't match. Set a default number (1). - numberofpointmtrs = 1; + if ((numberofpointmtrs != 1) && (numberofpointmtrs != 3) && + (numberofpointmtrs != 6)) { + // Column number doesn't match. + numberofpointmtrs = 0; + printf(" !! Metric size does not match (1, 3, or 6). Ignored.\n"); + fclose(infile); + return false; } // Allocate space for pointmtrlist. @@ -784,13 +799,60 @@ bool tetgenio::load_mtr(const char* filebasename) return true; } -/////////////////////////////////////////////////////////////////////////////// -// // -// load_poly() Load a PL complex from a .poly or a .smesh file. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// load_elem() Load a list of refine elements from an .elem file. // +// // +//============================================================================// + +bool tetgenio::load_elem(char* filebasename) +{ + FILE *infile; + char inelemfilename[FILENAMESIZE]; + char line[1024]; + + strcpy(inelemfilename, filebasename); + strcat(inelemfilename, ".elem"); + + // Try to open a .elem file. + infile = fopen(inelemfilename, "r"); + if (infile != (FILE *) NULL) { + printf("Opening %s.\n", inelemfilename); + } else { + return false; + } + + int elenum = 0; + float growth_ratio = 0.; + fgets(line, 1023, infile); + sscanf(line, "%d %f", &elenum, &growth_ratio); + + if (elenum == 0) { + fclose(infile); + return false; + } + + refine_elem_list = new int[elenum * 4]; + numberofrefineelems = elenum; + + int *idx, i; + for (i = 0; i < elenum; i++) { + fgets(line, 1023, infile); + idx = &(refine_elem_list[i*4]); + sscanf(line, "%d %d %d %d", &(idx[0]), &(idx[1]), &(idx[2]), &(idx[3])); + } + + fclose(infile); + return true; +} + +//============================================================================// +// // +// load_poly() Load a PL complex from a .poly or a .smesh file. // +// // +//============================================================================// -bool tetgenio::load_poly(const char* filebasename) +bool tetgenio::load_poly(char* filebasename) { FILE *infile; char inpolyfilename[FILENAMESIZE]; @@ -1177,56 +1239,6 @@ bool tetgenio::load_poly(const char* filebasename) } } - } else { - - // Read a PSLG from Triangle's poly file. - assert(mesh_dim == 2); - // A PSLG is a facet of a PLC. - numberoffacets = 1; - // Initialize the 'facetlist'. - facetlist = new facet[numberoffacets]; - facetmarkerlist = (int *) NULL; // No facet markers. - f = &(facetlist[0]); - init(f); - // Read number of segments. - stringptr = readnumberline(inputline, infile, infilename); - // Segments are degenerate polygons. - f->numberofpolygons = (int) strtol (stringptr, &stringptr, 0); - if (f->numberofpolygons > 0) { - f->polygonlist = new polygon[f->numberofpolygons]; - } - // Go through all segments, read in their vertices. - for (j = 0; j < f->numberofpolygons; j++) { - p = &(f->polygonlist[j]); - init(p); - // Read in a segment. - stringptr = readnumberline(inputline, infile, infilename); - stringptr = findnextnumber(stringptr); // Skip its index. - p->numberofvertices = 2; // A segment always has two vertices. - p->vertexlist = new int[p->numberofvertices]; - p->vertexlist[0] = (int) strtol (stringptr, &stringptr, 0); - stringptr = findnextnumber(stringptr); - p->vertexlist[1] = (int) strtol (stringptr, &stringptr, 0); - } - // Read number of holes. - stringptr = readnumberline(inputline, infile, infilename); - f->numberofholes = (int) strtol (stringptr, &stringptr, 0); - if (f->numberofholes > 0) { - // Initialize 'f->holelist'. - f->holelist = new REAL[f->numberofholes * 3]; - // Read the holes' coordinates. - for (j = 0; j < f->numberofholes; j++) { - // Read a 2D hole point. - stringptr = readnumberline(inputline, infile, infilename); - stringptr = findnextnumber(stringptr); // Skip its index. - f->holelist[j * 3] = (REAL) strtod (stringptr, &stringptr); - stringptr = findnextnumber(stringptr); - f->holelist[j * 3 + 1] = (REAL) strtod (stringptr, &stringptr); - f->holelist[j * 3 + 2] = 0.0; // The z-coord. - } - } - // The regions are skipped. - } // End of reading poly/smesh file. @@ -1234,17 +1246,17 @@ bool tetgenio::load_poly(const char* filebasename) return true; } -/////////////////////////////////////////////////////////////////////////////// -// // -// load_off() Load a polyhedron from a .off file. // -// // -// The .off format is one of file formats of the Geomview, an interactive // -// program for viewing and manipulating geometric objects. More information // -// is available form: http://www.geomview.org. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenio::load_off(const char* filebasename) +//============================================================================// +// // +// load_off() Load a polyhedron from a .off file. // +// // +// The .off format is one of file formats of the Geomview, an interactive // +// program for viewing and manipulating geometric objects. More information // +// is available form: http://www.geomview.org. // +// // +//============================================================================// + +bool tetgenio::load_off(char* filebasename) { FILE *fp; tetgenio::facet *f; @@ -1364,7 +1376,6 @@ bool tetgenio::load_off(const char* filebasename) } } - // Close file fclose(fp); // Decide the firstnumber of the index. @@ -1392,21 +1403,21 @@ bool tetgenio::load_off(const char* filebasename) return true; } -/////////////////////////////////////////////////////////////////////////////// -// // -// load_ply() Load a polyhedron from a .ply file. // -// // -// This is a simplified version of reading .ply files, which only reads the // -// set of vertices and the set of faces. Other informations (such as color, // -// material, texture, etc) in .ply file are ignored. Complete routines for // -// reading and writing ,ply files are available from: http://www.cc.gatech. // -// edu/projects/large_models/ply.html. Except the header section, ply file // -// format has exactly the same format for listing vertices and polygons as // -// off file format. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenio::load_ply(const char* filebasename) +//============================================================================// +// // +// load_ply() Load a polyhedron from a .ply file. // +// // +// This is a simplified version of reading .ply files, which only reads the // +// set of vertices and the set of faces. Other informations (such as color, // +// material, texture, etc) in .ply file are ignored. Complete routines for // +// reading and writing ,ply files are available from: http://www.cc.gatech. // +// edu/projects/large_models/ply.html. Except the header section, ply file // +// format has exactly the same format for listing vertices and polygons as // +// off file format. // +// // +//============================================================================// + +bool tetgenio::load_ply(char* filebasename) { FILE *fp; tetgenio::facet *f; @@ -1583,7 +1594,6 @@ bool tetgenio::load_ply(const char* filebasename) } } - // Close file fclose(fp); // Decide the firstnumber of the index. @@ -1611,21 +1621,33 @@ bool tetgenio::load_ply(const char* filebasename) return true; } -/////////////////////////////////////////////////////////////////////////////// -// // -// load_stl() Load a surface mesh from a .stl file. // -// // -// The .stl or stereolithography format is an ASCII or binary file used in // -// manufacturing. It is a list of the triangular surfaces that describe a // -// computer generated solid model. This is the standard input for most rapid // -// prototyping machines. // -// // -// Comment: A .stl file many contain many duplicated points. They will be // -// unified during the Delaunay tetrahedralization process. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// load_stl() Load a surface mesh from a .stl file. // +// // +// The .stl or stereolithography format is an ASCII or binary file used in // +// manufacturing. It is a list of the triangular surfaces that describe a // +// computer generated solid model. This is the standard input for most rapid // +// prototyping machines. // +// // +// Comment: A .stl file many contain many duplicated points. They will be // +// unified during the Delaunay tetrahedralization process. // +// // +//============================================================================// + +static void SwapBytes(char *array, int size, int n) +{ + char *x = new char[size]; + for(int i = 0; i < n; i++) { + char *a = &array[i * size]; + memcpy(x, a, size); + for(int c = 0; c < size; c++) + a[size - 1 - c] = x[c]; + } + delete [] x; +} -bool tetgenio::load_stl(const char* filebasename) +bool tetgenio::load_stl(char* filebasename) { FILE *fp; tetgenmesh::arraypool *plist; @@ -1650,52 +1672,97 @@ bool tetgenio::load_stl(const char* filebasename) strcat(infilename, ".stl"); } - if (!(fp = fopen(infilename, "r"))) { + if (!(fp = fopen(infilename, "rb"))) { printf("Error: Unable to open file %s\n", infilename); return false; } printf("Opening %s.\n", infilename); + // "solid", or binary data header + if(!fgets(buffer, sizeof(buffer), fp)){ fclose(fp); return 0; } + bool binary = strncmp(buffer, "solid", 5) && strncmp(buffer, "SOLID", 5); + // STL file has no number of points available. Use a list to read points. plist = new tetgenmesh::arraypool(sizeof(double) * 3, 10); - while ((bufferp = readline(buffer, fp, &line_count)) != NULL) { - // The ASCII .stl file must start with the lower case keyword solid and - // end with endsolid. - if (solid == 0) { - // Read header - bufferp = strstr(bufferp, "solid"); - if (bufferp != NULL) { - solid = 1; - } - } else { - // We're inside the block of the solid. - str = bufferp; - // Is this the end of the solid. - bufferp = strstr(bufferp, "endsolid"); - if (bufferp != NULL) { - solid = 0; + if(!binary){ + solid = 1; + while ((bufferp = readline(buffer, fp, &line_count)) != NULL) { + // The ASCII .stl file must start with the lower case keyword solid and + // end with endsolid. + if (solid == 0) { + // Read header + bufferp = strstr(bufferp, "solid"); + if (bufferp != NULL) { + solid = 1; + } } else { - // Read the XYZ coordinates if it is a vertex. - bufferp = str; - bufferp = strstr(bufferp, "vertex"); + // We're inside the block of the solid. + str = bufferp; + // Is this the end of the solid. + bufferp = strstr(bufferp, "endsolid"); if (bufferp != NULL) { - plist->newindex((void **) &coord); - for (i = 0; i < 3; i++) { - bufferp = findnextnumber(bufferp); - if (*bufferp == '\0') { - printf("Syntax error reading vertex coords on line %d\n", - line_count); - delete plist; - fclose(fp); - return false; + solid = 0; + } else { + // Read the XYZ coordinates if it is a vertex. + bufferp = str; + bufferp = strstr(bufferp, "vertex"); + if (bufferp != NULL) { + plist->newindex((void **) &coord); + for (i = 0; i < 3; i++) { + bufferp = findnextnumber(bufferp); + if (*bufferp == '\0') { + printf("Syntax error reading vertex coords on line %d\n", + line_count); + delete plist; + fclose(fp); + return false; + } + coord[i] = (REAL) strtod(bufferp, &bufferp); } - coord[i] = (REAL) strtod(bufferp, &bufferp); } } } } - } + } // if(!binary) + + else { + rewind(fp); + while(!feof(fp)) { + char header[80]; + if(!fread(header, sizeof(char), 80, fp)) break; + unsigned int nfacets = 0; + size_t ret = fread(&nfacets, sizeof(unsigned int), 1, fp); + bool swap = false; + if(nfacets > 100000000){ + //Msg::Info("Swapping bytes from binary file"); + swap = true; + SwapBytes((char*)&nfacets, sizeof(unsigned int), 1); + } + if(ret && nfacets){ + //points.resize(points.size() + 1); + char *data = new char[nfacets * 50 * sizeof(char)]; + ret = fread(data, sizeof(char), nfacets * 50, fp); + if(ret == nfacets * 50){ + for(unsigned int i = 0; i < nfacets; i++) { + float *xyz = (float *)&data[i * 50 * sizeof(char)]; + if(swap) SwapBytes((char*)xyz, sizeof(float), 12); + for(int j = 0; j < 3; j++){ + //SPoint3 p(xyz[3 + 3 * j], xyz[3 + 3 * j + 1], xyz[3 + 3 * j + 2]); + //points.back().push_back(p); + //bbox += p; + plist->newindex((void **) &coord); + coord[0] = xyz[3 + 3 * j]; + coord[1] = xyz[3 + 3 * j + 1]; + coord[2] = xyz[3 + 3 * j + 2]; + } + } + } + delete [] data; + } + } // while (!feof(fp)) + } // binary + fclose(fp); nverts = (int) plist->objects; @@ -1743,16 +1810,16 @@ bool tetgenio::load_stl(const char* filebasename) return true; } -/////////////////////////////////////////////////////////////////////////////// -// // -// load_medit() Load a surface mesh from a .mesh file. // -// // -// The .mesh format is the file format of Medit, a user-friendly interactive // -// mesh viewer program. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// load_medit() Load a surface mesh from a .mesh file. // +// // +// The .mesh format is the file format of Medit, a user-friendly interactive // +// mesh viewer program. // +// // +//============================================================================// -bool tetgenio::load_medit(const char* filebasename, int istetmesh) +bool tetgenio::load_medit(char* filebasename, int istetmesh) { FILE *fp; tetgenio::facet *tmpflist, *f; @@ -1776,7 +1843,7 @@ bool tetgenio::load_medit(const char* filebasename, int istetmesh) strncpy(infilename, filebasename, FILENAMESIZE - 1); infilename[FILENAMESIZE - 1] = '\0'; if (infilename[0] == '\0') { - printf("Error: No filename.\n"); + //printf("Error: No filename.\n"); return false; } if (strcmp(&infilename[strlen(infilename) - 5], ".mesh") != 0) { @@ -1784,7 +1851,7 @@ bool tetgenio::load_medit(const char* filebasename, int istetmesh) } if (!(fp = fopen(infilename, "r"))) { - printf("Error: Unable to open file %s\n", infilename); + //printf("Error: Unable to open file %s\n", infilename); return false; } printf("Opening %s.\n", infilename); @@ -1854,7 +1921,6 @@ bool tetgenio::load_medit(const char* filebasename, int istetmesh) if ((j < 2) || (dimension == 3)) { coord[j] = (REAL) strtod(bufferp, &bufferp); } else { - assert((j == 2) && (dimension == 2)); coord[j] = 0.0; } bufferp = findnextnumber(bufferp); @@ -1863,7 +1929,7 @@ bool tetgenio::load_medit(const char* filebasename, int istetmesh) continue; } } - if (ntets == 0) { + if ((ntets == 0) && istetmesh) { // Only read tetrahedra if 'istetmesh = 1'. // Find if it is the keyword "Tetrahedra" corners = 0; str = strstr(bufferp, "Tetrahedra"); @@ -2062,7 +2128,6 @@ bool tetgenio::load_medit(const char* filebasename, int istetmesh) } } - // Close file fclose(fp); // Decide the firstnumber of the index. @@ -2079,14 +2144,14 @@ bool tetgenio::load_medit(const char* filebasename, int istetmesh) return true; } -/////////////////////////////////////////////////////////////////////////////// -// // -// load_vtk() Load VTK surface mesh from file (.vtk ascii or binary). // -// // -// This function is contributed by: Bryn Lloyd, Computer Vision Laboratory, // -// ETH, Zuerich. May 7, 2007. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// load_vtk() Load VTK surface mesh from file (.vtk ascii or binary). // +// // +// This function is contributed by: Bryn Lloyd, Computer Vision Laboratory, // +// ETH, Zuerich. May 7, 2007. // +// // +//============================================================================// // Two inline functions used in read/write VTK files. @@ -2111,8 +2176,7 @@ bool testIsBigEndian() return false; } - -bool tetgenio::load_vtk(const char* filebasename) +bool tetgenio::load_vtk(char* filebasename) { FILE *fp; tetgenio::facet *f; @@ -2351,13 +2415,13 @@ bool tetgenio::load_vtk(const char* filebasename) return true; } -/////////////////////////////////////////////////////////////////////////////// -// // -// load_plc() Load a piecewise linear complex from file(s). // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// load_plc() Load a piecewise linear complex from file(s). // +// // +//============================================================================// -bool tetgenio::load_plc(const char* filebasename, int object) +bool tetgenio::load_plc(char* filebasename, int object) { bool success; @@ -2389,18 +2453,20 @@ bool tetgenio::load_plc(const char* filebasename, int object) return success; } -/////////////////////////////////////////////////////////////////////////////// -// // -// load_mesh() Load a tetrahedral mesh from file(s). // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// load_mesh() Load a tetrahedral mesh from file(s). // +// // +//============================================================================// -bool tetgenio::load_tetmesh(const char* filebasename, int object) +bool tetgenio::load_tetmesh(char* filebasename, int object) { - bool success; + bool success = false; if (object == (int) tetgenbehavior::MEDIT) { success = load_medit(filebasename, 1); + } else if (object == (int) tetgenbehavior::NEU_MESH) { + //success = load_neumesh(filebasename, 1); } else { success = load_node(filebasename); if (success) { @@ -2418,18 +2484,19 @@ bool tetgenio::load_tetmesh(const char* filebasename, int object) // Try to load the following files (.var, .mtr). load_var(filebasename); load_mtr(filebasename); + load_elem(filebasename); } return success; } -/////////////////////////////////////////////////////////////////////////////// -// // -// save_nodes() Save points to a .node file. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// save_nodes() Save points to a .node file. // +// // +//============================================================================// -void tetgenio::save_nodes(const char* filebasename) +void tetgenio::save_nodes(const char *filebasename) { FILE *fout; char outnodefilename[FILENAMESIZE]; @@ -2476,11 +2543,11 @@ void tetgenio::save_nodes(const char* filebasename) } } -/////////////////////////////////////////////////////////////////////////////// -// // -// save_elements() Save elements to a .ele file. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// save_elements() Save elements to a .ele file. // +// // +//============================================================================// void tetgenio::save_elements(const char* filebasename) { @@ -2523,11 +2590,11 @@ void tetgenio::save_elements(const char* filebasename) fclose(fout); } -/////////////////////////////////////////////////////////////////////////////// -// // -// save_faces() Save faces to a .face file. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// save_faces() Save faces to a .face file. // +// // +//============================================================================// void tetgenio::save_faces(const char* filebasename) { @@ -2552,13 +2619,13 @@ void tetgenio::save_faces(const char* filebasename) fclose(fout); } -/////////////////////////////////////////////////////////////////////////////// -// // -// save_edges() Save egdes to a .edge file. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// save_edges() Save egdes to a .edge file. // +// // +//============================================================================// -void tetgenio::save_edges(const char* filebasename) +void tetgenio::save_edges(char* filebasename) { FILE *fout; char outedgefilename[FILENAMESIZE]; @@ -2580,13 +2647,13 @@ void tetgenio::save_edges(const char* filebasename) fclose(fout); } -/////////////////////////////////////////////////////////////////////////////// -// // -// save_neighbors() Save egdes to a .neigh file. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// save_neighbors() Save egdes to a .neigh file. // +// // +//============================================================================// -void tetgenio::save_neighbors(const char* filebasename) +void tetgenio::save_neighbors(char* filebasename) { FILE *fout; char outneighborfilename[FILENAMESIZE]; @@ -2611,15 +2678,15 @@ void tetgenio::save_neighbors(const char* filebasename) fclose(fout); } -/////////////////////////////////////////////////////////////////////////////// -// // -// save_poly() Save segments or facets to a .poly file. // -// // -// It only save the facets, holes and regions. No .node file is saved. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// save_poly() Save segments or facets to a .poly file. // +// // +// It only save the facets, holes and regions. No .node file is saved. // +// // +//============================================================================// -void tetgenio::save_poly(const char* filebasename) +void tetgenio::save_poly(const char *filebasename) { FILE *fout; facet *f; @@ -2711,15 +2778,15 @@ void tetgenio::save_poly(const char* filebasename) fclose(fout); } -/////////////////////////////////////////////////////////////////////////////// -// // -// save_faces2smesh() Save triangular faces to a .smesh file. // -// // -// It only save the facets. No holes and regions. No .node file. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// save_faces2smesh() Save triangular faces to a .smesh file. // +// // +// It only save the facets. No holes and regions. No .node file. // +// // +//============================================================================// -void tetgenio::save_faces2smesh(const char* filebasename) +void tetgenio::save_faces2smesh(char* filebasename) { FILE *fout; char outsmeshfilename[FILENAMESIZE]; @@ -2757,17 +2824,17 @@ void tetgenio::save_faces2smesh(const char* filebasename) fclose(fout); } -/////////////////////////////////////////////////////////////////////////////// -// // -// readline() Read a nonempty line from a file. // -// // -// A line is considered "nonempty" if it contains something more than white // -// spaces. If a line is considered empty, it will be dropped and the next // -// line will be read, this process ends until reaching the end-of-file or a // -// non-empty line. Return NULL if it is the end-of-file, otherwise, return // -// a pointer to the first non-whitespace character of the line. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// readline() Read a nonempty line from a file. // +// // +// A line is considered "nonempty" if it contains something more than white // +// spaces. If a line is considered empty, it will be dropped and the next // +// line will be read, this process ends until reaching the end-of-file or a // +// non-empty line. Return NULL if it is the end-of-file, otherwise, return // +// a pointer to the first non-whitespace character of the line. // +// // +//============================================================================// char* tetgenio::readline(char *string, FILE *infile, int *linenumber) { @@ -2787,14 +2854,14 @@ char* tetgenio::readline(char *string, FILE *infile, int *linenumber) return result; } -/////////////////////////////////////////////////////////////////////////////// -// // -// findnextfield() Find the next field of a string. // -// // -// Jumps past the current field by searching for whitespace or a comma, then // -// jumps past the whitespace or the comma to find the next field. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// findnextfield() Find the next field of a string. // +// // +// Jumps past the current field by searching for whitespace or a comma, then // +// jumps past the whitespace or the comma to find the next field. // +// // +//============================================================================// char* tetgenio::findnextfield(char *string) { @@ -2815,14 +2882,14 @@ char* tetgenio::findnextfield(char *string) return result; } -/////////////////////////////////////////////////////////////////////////////// -// // -// readnumberline() Read a nonempty number line from a file. // -// // -// A line is considered "nonempty" if it contains something that looks like // -// a number. Comments (prefaced by `#') are ignored. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// readnumberline() Read a nonempty number line from a file. // +// // +// A line is considered "nonempty" if it contains something that looks like // +// a number. Comments (prefaced by `#') are ignored. // +// // +//============================================================================// char* tetgenio::readnumberline(char *string, FILE *infile, char *infilename) { @@ -2846,15 +2913,15 @@ char* tetgenio::readnumberline(char *string, FILE *infile, char *infilename) return result; } -/////////////////////////////////////////////////////////////////////////////// -// // -// findnextnumber() Find the next field of a number string. // -// // -// Jumps past the current field by searching for whitespace or a comma, then // -// jumps past the whitespace or the comma to find the next field that looks // -// like a number. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// findnextnumber() Find the next field of a number string. // +// // +// Jumps past the current field by searching for whitespace or a comma, then // +// jumps past the whitespace or the comma to find the next field that looks // +// like a number. // +// // +//============================================================================// char* tetgenio::findnextnumber(char *string) { @@ -2880,19 +2947,20 @@ char* tetgenio::findnextnumber(char *string) return result; } -//// //// -//// //// -//// io_cxx /////////////////////////////////////////////////////////////////// +// // +// // +//== io_cxx ==================================================================// -//// behavior_cxx ///////////////////////////////////////////////////////////// -//// //// -//// //// -/////////////////////////////////////////////////////////////////////////////// -// // -// syntax() Print list of command line switches. // -// // -/////////////////////////////////////////////////////////////////////////////// +//== behavior_cxx ============================================================// +// // +// // + +//============================================================================// +// // +// syntax() Print list of command line switches. // +// // +//============================================================================// void tetgenbehavior::syntax() { @@ -2918,7 +2986,6 @@ void tetgenbehavior::syntax() printf(" -f Outputs all faces to .face file.\n"); printf(" -e Outputs all edges to .edge file.\n"); printf(" -n Outputs tetrahedra neighbors to .neigh file.\n"); - printf(" -v Outputs Voronoi diagram to files.\n"); printf(" -g Outputs mesh to .mesh file for viewing by Medit.\n"); printf(" -k Outputs mesh to .vtk file for viewing by Paraview.\n"); printf(" -J No jettison of unused vertices from output .node file.\n"); @@ -2933,19 +3000,21 @@ void tetgenbehavior::syntax() printf(" -h Help: A brief instruction for using TetGen.\n"); } -/////////////////////////////////////////////////////////////////////////////// -// // -// usage() Print a brief instruction for using TetGen. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// usage() Print a brief instruction for using TetGen. // +// // +//============================================================================// void tetgenbehavior::usage() { printf("TetGen\n"); printf("A Quality Tetrahedral Mesh Generator and 3D Delaunay "); printf("Triangulator\n"); - printf("Version 1.5\n"); - printf("November 4, 2013\n"); + printf("Version 1.6\n"); + printf("August, 2020\n"); + printf("\n"); + printf("Copyright (C) 2002 - 2020\n"); printf("\n"); printf("What Can TetGen Do?\n"); printf("\n"); @@ -2960,10 +3029,10 @@ void tetgenbehavior::usage() printf(" follow certain switches. Do not leave any space between a "); printf("switch\n"); printf(" and its numeric parameter. \'input_file\' contains input data\n"); - printf(" depending on the switches you supplied which may be a "); + printf(" depending on the switches you supplied, which may be a "); printf(" piecewise\n"); printf(" linear complex or a list of nodes. File formats and detailed\n"); - printf(" description of command line switches are found in user's "); + printf(" description of command line switches are found in the user's "); printf("manual.\n"); printf("\n"); syntax(); @@ -2991,18 +3060,18 @@ void tetgenbehavior::usage() terminatetetgen(NULL, 0); } -/////////////////////////////////////////////////////////////////////////////// -// // -// parse_commandline() Read the command line, identify switches, and set // -// up options and file names. // -// // -// 'argc' and 'argv' are the same parameters passed to the function main() // -// of a C/C++ program. They together represent the command line user invoked // -// from an environment in which TetGen is running. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenbehavior::parse_commandline(int argc, const char **argv) +//============================================================================// +// // +// parse_commandline() Read the command line, identify switches, and set // +// up options and file names. // +// // +// 'argc' and 'argv' are the same parameters passed to the function main() // +// of a C/C++ program. They together represent the command line user invoked // +// from an environment in which TetGen is running. // +// // +//============================================================================// + +bool tetgenbehavior::parse_commandline(int argc, char **argv) { int startindex; int increment; @@ -3047,17 +3116,68 @@ bool tetgenbehavior::parse_commandline(int argc, const char **argv) k++; } workstring[k] = '\0'; - facet_ang_tol = (REAL) strtod(workstring, (char **) NULL); - } - } else if (argv[i][j] == 's') { - psc = 1; - } else if (argv[i][j] == 'Y') { - nobisect = 1; - if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) { - nobisect_param = (argv[i][j + 1] - '0'); - j++; + facet_separate_ang_tol = (REAL) strtod(workstring, (char **) NULL); } - if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { + if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { + j++; + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.') || (argv[i][j + 1] == 'e') || + (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + //facet_overlap_ang_tol = (REAL) strtod(workstring, (char **) NULL); + } + } + if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { + j++; + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + facet_small_ang_tol = (REAL) strtod(workstring, (char **) NULL); + } + } + if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { + j++; + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + collinear_ang_tol = (REAL) strtod(workstring, (char **) NULL); + } + } + } else if (argv[i][j] == 'Y') { + nobisect++; + if (cdt > 0) { + printf("Warning: switch -D is omitted.\n"); + cdt = 0; + } + if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { + j++; + if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) { + supsteiner_level = (argv[i][j + 1] - '0'); + j++; + } + } + if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { j++; if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) { addsteiner_algo = (argv[i][j + 1] - '0'); @@ -3066,8 +3186,6 @@ bool tetgenbehavior::parse_commandline(int argc, const char **argv) } } else if (argv[i][j] == 'r') { refine = 1; - } else if (argv[i][j] == 'q') { - quality = 1; if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || (argv[i][j + 1] == '.')) { k = 0; @@ -3078,7 +3196,7 @@ bool tetgenbehavior::parse_commandline(int argc, const char **argv) k++; } workstring[k] = '\0'; - minratio = (REAL) strtod(workstring, (char **) NULL); + elem_growth_ratio = (REAL) strtod(workstring, (char **) NULL); } if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { j++; @@ -3092,8 +3210,22 @@ bool tetgenbehavior::parse_commandline(int argc, const char **argv) k++; } workstring[k] = '\0'; - mindihedral = (REAL) strtod(workstring, (char **) NULL); + refine_progress_ratio = (REAL) strtod(workstring, (char **) NULL); + } + } + } else if (argv[i][j] == 'q') { + quality = 1; + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + j++; + workstring[k] = argv[i][j]; + k++; } + workstring[k] = '\0'; + minratio = (REAL) strtod(workstring, (char **) NULL); } if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { j++; @@ -3107,7 +3239,7 @@ bool tetgenbehavior::parse_commandline(int argc, const char **argv) k++; } workstring[k] = '\0'; - optmaxdihedral = (REAL) strtod(workstring, (char **) NULL); + mindihedral = (REAL) strtod(workstring, (char **) NULL); } } } else if (argv[i][j] == 'R') { @@ -3204,12 +3336,23 @@ bool tetgenbehavior::parse_commandline(int argc, const char **argv) no_sort = 1; brio_hilbert = 0; // Turn off BRIO-Hilbert sorting. } - } else if (argv[i][j] == 'l') { - incrflip = 1; } else if (argv[i][j] == 'L') { flipinsert = 1; } else if (argv[i][j] == 'm') { metric = 1; + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.') || (argv[i][j + 1] == 'e') || + (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + metric_scale = (REAL) strtod(workstring, (char **) NULL); + } } else if (argv[i][j] == 'a') { if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || (argv[i][j + 1] == '.')) { @@ -3224,16 +3367,19 @@ bool tetgenbehavior::parse_commandline(int argc, const char **argv) } workstring[k] = '\0'; maxvolume = (REAL) strtod(workstring, (char **) NULL); + maxvolume_length = pow(maxvolume, 1./3.) / 3.; } else { varvolume = 1; } } else if (argv[i][j] == 'A') { regionattrib = 1; } else if (argv[i][j] == 'D') { - conforming = 1; - if ((argv[i][j + 1] >= '1') && (argv[i][j + 1] <= '3')) { - reflevel = (argv[i][j + 1] - '1') + 1; + if ((argv[i][j + 1] >= '1') && (argv[i][j + 1] <= '7')) { + // -D# (with a number following it.) + cdtrefine = (argv[i][j + 1] - '1') + 1; j++; + } else { + cdt = 1; // -D without a number following it. } } else if (argv[i][j] == 'i') { insertaddpoints = 1; @@ -3263,19 +3409,28 @@ bool tetgenbehavior::parse_commandline(int argc, const char **argv) noexact = 1; } } else if (argv[i][j] == 'z') { - zeroindex = 1; + if (argv[i][j + 1] == '1') { // -z1 + reversetetori = 1; + j++; + } else { + zeroindex = 1; // -z + } } else if (argv[i][j] == 'f') { facesout++; } else if (argv[i][j] == 'e') { edgesout++; } else if (argv[i][j] == 'n') { neighout++; - } else if (argv[i][j] == 'v') { - voroout = 1; } else if (argv[i][j] == 'g') { meditview = 1; } else if (argv[i][j] == 'k') { - vtkview = 1; + if (argv[i][j + 1] == '2') { // -k2 + vtksurfview = 1; + j++; + } + else { + vtkview = 1; + } } else if (argv[i][j] == 'J') { nojettison = 1; } else if (argv[i][j] == 'B') { @@ -3307,7 +3462,7 @@ bool tetgenbehavior::parse_commandline(int argc, const char **argv) order = 2; j++; } - if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { + if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { // -o/# j++; if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || (argv[i][j + 1] == '.')) { @@ -3322,18 +3477,102 @@ bool tetgenbehavior::parse_commandline(int argc, const char **argv) optmaxdihedral = (REAL) strtod(workstring, (char **) NULL); } } + if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { // -o//# + j++; + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.') || (argv[i][j + 1] == 'e') || + (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + opt_max_asp_ratio = (REAL) strtod(workstring, (char **) NULL); + } + } + if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { // -o///# + j++; + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.') || (argv[i][j + 1] == 'e') || + (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + opt_max_edge_ratio = (REAL) strtod(workstring, (char **) NULL); + } + } } else if (argv[i][j] == 'O') { - if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) { - optlevel = (argv[i][j + 1] - '0'); + if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) { // -O# + opt_max_flip_level = (argv[i][j + 1] - '0'); j++; } - if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { + if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { // -O/# j++; if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '7')) { - optscheme = (argv[i][j + 1] - '0'); + opt_scheme = (argv[i][j + 1] - '0'); + j++; + } + } + if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { // -O//# + j++; + if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.') || (argv[i][j + 1] == 'e') || + (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + opt_iterations = (int) strtol(workstring, (char **) NULL, 0); j++; } } + } else if (argv[i][j] == 's') { + if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) { // -s# + smooth_cirterion = (argv[i][j + 1] - '0'); + j++; + } + if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { // -s#/# + j++; + if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.') || (argv[i][j + 1] == 'e') || + (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + smooth_maxiter = (int) strtol(workstring, (char **) NULL, 0); + } + } + if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { // -s#/#/# + j++; + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.') || (argv[i][j + 1] == 'e') || + (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + smooth_alpha = (REAL) strtod(workstring, (char **) NULL); + } + } } else if (argv[i][j] == 'T') { if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || (argv[i][j + 1] == '.')) { @@ -3348,14 +3587,16 @@ bool tetgenbehavior::parse_commandline(int argc, const char **argv) workstring[k] = '\0'; epsilon = (REAL) strtod(workstring, (char **) NULL); } - } else if (argv[i][j] == 'R') { - reversetetori = 1; } else if (argv[i][j] == 'C') { docheck++; } else if (argv[i][j] == 'Q') { quiet = 1; + } else if (argv[i][j] == 'W') { + nowarning = 1; } else if (argv[i][j] == 'V') { verbose++; + } else if (argv[i][j] == 'l') { + //refine_list = 1; //incrflip = 1; } else if (argv[i][j] == 'x') { if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || (argv[i][j + 1] == '.')) { @@ -3376,7 +3617,16 @@ bool tetgenbehavior::parse_commandline(int argc, const char **argv) tetrahedraperblock = 8188; } } - } else if ((argv[i][j] == 'h') || (argv[i][j] == 'H') || + } else if (argv[i][j] == 'H') { + if (argv[i+1][0] != '-') { + hole_mesh = 1; + // It is a filename following by -H + strncpy(hole_mesh_filename, argv[i+1], 1024 - 1); + hole_mesh_filename[1024 - 1] = '\0'; + i++; // Skip the next string. + break; // j + } + } else if ((argv[i][j] == 'h') || // (argv[i][j] == 'H') (argv[i][j] == '?')) { usage(); } else { @@ -3430,6 +3680,10 @@ bool tetgenbehavior::parse_commandline(int argc, const char **argv) infilename[strlen(infilename) - 4] = '\0'; object = MESH; refine = 1; + } else if (!strcmp(&infilename[strlen(infilename) - 4], ".neu")) { + infilename[strlen(infilename) - 4] = '\0'; + object = NEU_MESH; + refine = 1; } } @@ -3444,13 +3698,14 @@ bool tetgenbehavior::parse_commandline(int argc, const char **argv) } if (refine && !quality) { // -r only // Reconstruct a mesh, no mesh optimization. - optlevel = 0; + opt_max_flip_level = 0; + opt_iterations = 0; } - if (insertaddpoints && (optlevel == 0)) { // with -i option - optlevel = 2; + if (insertaddpoints && (opt_max_flip_level == 0)) { // with -i option + opt_max_flip_level = 2; } - if (coarsen && (optlevel == 0)) { // with -R option - optlevel = 2; + if (coarsen && (opt_max_flip_level == 0)) { // with -R option + opt_max_flip_level = 2; } // Detect improper combinations of switches. @@ -3486,23 +3741,17 @@ bool tetgenbehavior::parse_commandline(int argc, const char **argv) } } } - // No user-specified dihedral angle bound. Use default ones. if (!quality) { - if (optmaxdihedral < 179.0) { - if (nobisect) { // with -Y option - optmaxdihedral = 179.0; - } else { // -p only - optmaxdihedral = 179.999; - } - } - if (optminsmtdihed < 179.999) { - optminsmtdihed = 179.999; - } - if (optminslidihed < 179.999) { - optminslidihed = 179.999; + // If no user-specified dihedral angle bound. Use default ones. + if (optmaxdihedral == 177.0) { // set by -o/# + optmaxdihedral = 179.9; } } + if (quiet > 0) { + verbose = 0; // No printf output during the execution. + } + increment = 0; strcpy(workstring, infilename); j = 1; @@ -3545,13 +3794,13 @@ bool tetgenbehavior::parse_commandline(int argc, const char **argv) return true; } -//// //// -//// //// -//// behavior_cxx ///////////////////////////////////////////////////////////// +// // +// // +//== behavior_cxx ============================================================// -//// mempool_cxx ////////////////////////////////////////////////////////////// -//// //// -//// //// +//== mempool_cxx =============================================================// +// // +// // // Initialize fast lookup tables for mesh maniplulation primitives. @@ -3608,14 +3857,15 @@ int tetgenmesh::sorgpivot [6] = {3, 4, 4, 5, 5, 3}; int tetgenmesh::sdestpivot[6] = {4, 3, 5, 4, 3, 5}; int tetgenmesh::sapexpivot[6] = {5, 5, 3, 3, 4, 4}; -/////////////////////////////////////////////////////////////////////////////// -// // -// inittable() Initialize the look-up tables. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// inittable() Initialize the look-up tables. // +// // +//============================================================================// void tetgenmesh::inittables() { + int soffset, toffset; int i, j; @@ -3660,7 +3910,6 @@ void tetgenmesh::inittables() edestoppotbl[i] = enexttbl[esymtbl[eprevtbl[i]]]; } - int soffset, toffset; // i = t.ver, j = s.shver for (i = 0; i < 12; i++) { @@ -3694,29 +3943,30 @@ void tetgenmesh::inittables() } } -/////////////////////////////////////////////////////////////////////////////// -// // -// restart() Deallocate all objects in this pool. // -// // -// The pool returns to a fresh state, like after it was initialized, except // -// that no memory is freed to the operating system. Rather, the previously // -// allocated blocks are ready to be used. // -// // -/////////////////////////////////////////////////////////////////////////////// + +//============================================================================// +// // +// restart() Deallocate all objects in this pool. // +// // +// The pool returns to a fresh state, like after it was initialized, except // +// that no memory is freed to the operating system. Rather, the previously // +// allocated blocks are ready to be used. // +// // +//============================================================================// void tetgenmesh::arraypool::restart() { objects = 0l; } -/////////////////////////////////////////////////////////////////////////////// -// // -// poolinit() Initialize an arraypool for allocation of objects. // -// // -// Before the pool may be used, it must be initialized by this procedure. // -// After initialization, memory can be allocated and freed in this pool. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// poolinit() Initialize an arraypool for allocation of objects. // +// // +// Before the pool may be used, it must be initialized by this procedure. // +// After initialization, memory can be allocated and freed in this pool. // +// // +//============================================================================// void tetgenmesh::arraypool::poolinit(int sizeofobject, int log2objperblk) { @@ -3738,11 +3988,11 @@ void tetgenmesh::arraypool::poolinit(int sizeofobject, int log2objperblk) restart(); } -/////////////////////////////////////////////////////////////////////////////// -// // -// arraypool() The constructor and destructor. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// arraypool() The constructor and destructor. // +// // +//============================================================================// tetgenmesh::arraypool::arraypool(int sizeofobject, int log2objperblk) { @@ -3774,17 +4024,17 @@ tetgenmesh::arraypool::~arraypool() totalmemory = 0; } -/////////////////////////////////////////////////////////////////////////////// -// // -// getblock() Return (and perhaps create) the block containing the object // -// with a given index. // -// // -// This function takes care of allocating or resizing the top array if nece- // -// ssary, and of allocating the block if it hasn't yet been allocated. // -// // -// Return a pointer to the beginning of the block (NOT the object). // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// getblock() Return (and perhaps create) the block containing the object // +// with a given index. // +// // +// This function takes care of allocating or resizing the top array if nece- // +// ssary, and of allocating the block if it hasn't yet been allocated. // +// // +// Return a pointer to the beginning of the block (NOT the object). // +// // +//============================================================================// char* tetgenmesh::arraypool::getblock(int objectindex) { @@ -3844,12 +4094,12 @@ char* tetgenmesh::arraypool::getblock(int objectindex) return block; } -/////////////////////////////////////////////////////////////////////////////// -// // -// lookup() Return the pointer to the object with a given index, or NULL // -// if the object's block doesn't exist yet. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// lookup() Return the pointer to the object with a given index, or NULL // +// if the object's block doesn't exist yet. // +// // +//============================================================================// void* tetgenmesh::arraypool::lookup(int objectindex) { @@ -3880,13 +4130,13 @@ void* tetgenmesh::arraypool::lookup(int objectindex) return (void *)(block + (objectindex & (objectsperblock - 1)) * objectbytes); } -/////////////////////////////////////////////////////////////////////////////// -// // -// newindex() Allocate space for a fresh object from the pool. // -// // -// 'newptr' returns a pointer to the new object (it must not be a NULL). // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// newindex() Allocate space for a fresh object from the pool. // +// // +// 'newptr' returns a pointer to the new object (it must not be a NULL). // +// // +//============================================================================// int tetgenmesh::arraypool::newindex(void **newptr) { @@ -3927,11 +4177,11 @@ tetgenmesh::memorypool::memorypool(int bytecount, int itemcount, int wsize, poolinit(bytecount, itemcount, wsize, alignment); } -/////////////////////////////////////////////////////////////////////////////// -// // -// ~memorypool() Free to the operating system all memory taken by a pool. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// ~memorypool() Free to the operating system all memory taken by a pool. // +// // +//============================================================================// tetgenmesh::memorypool::~memorypool() { @@ -3942,21 +4192,21 @@ tetgenmesh::memorypool::~memorypool() } } -/////////////////////////////////////////////////////////////////////////////// -// // -// poolinit() Initialize a pool of memory for allocation of items. // -// // -// A `pool' is created whose records have size at least `bytecount'. Items // -// will be allocated in `itemcount'-item blocks. Each item is assumed to be // -// a collection of words, and either pointers or floating-point values are // -// assumed to be the "primary" word type. (The "primary" word type is used // -// to determine alignment of items.) If `alignment' isn't zero, all items // -// will be `alignment'-byte aligned in memory. `alignment' must be either a // -// multiple or a factor of the primary word size; powers of two are safe. // -// `alignment' is normally used to create a few unused bits at the bottom of // -// each item's pointer, in which information may be stored. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// poolinit() Initialize a pool of memory for allocation of items. // +// // +// A `pool' is created whose records have size at least `bytecount'. Items // +// will be allocated in `itemcount'-item blocks. Each item is assumed to be // +// a collection of words, and either pointers or floating-point values are // +// assumed to be the "primary" word type. (The "primary" word type is used // +// to determine alignment of items.) If `alignment' isn't zero, all items // +// will be `alignment'-byte aligned in memory. `alignment' must be either a // +// multiple or a factor of the primary word size; powers of two are safe. // +// `alignment' is normally used to create a few unused bits at the bottom of // +// each item's pointer, in which information may be stored. // +// // +//============================================================================// void tetgenmesh::memorypool::poolinit(int bytecount,int itemcount,int wordsize, int alignment) @@ -3992,15 +4242,15 @@ void tetgenmesh::memorypool::poolinit(int bytecount,int itemcount,int wordsize, restart(); } -/////////////////////////////////////////////////////////////////////////////// -// // -// restart() Deallocate all items in this pool. // -// // -// The pool is returned to its starting state, except that no memory is // -// freed to the operating system. Rather, the previously allocated blocks // -// are ready to be reused. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// restart() Deallocate all items in this pool. // +// // +// The pool is returned to its starting state, except that no memory is // +// freed to the operating system. Rather, the previously allocated blocks // +// are ready to be reused. // +// // +//============================================================================// void tetgenmesh::memorypool::restart() { @@ -4023,11 +4273,11 @@ void tetgenmesh::memorypool::restart() deaditemstack = (void *) NULL; } -/////////////////////////////////////////////////////////////////////////////// -// // -// alloc() Allocate space for an item. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// alloc() Allocate space for an item. // +// // +//============================================================================// void* tetgenmesh::memorypool::alloc() { @@ -4078,13 +4328,13 @@ void* tetgenmesh::memorypool::alloc() return newitem; } -/////////////////////////////////////////////////////////////////////////////// -// // -// dealloc() Deallocate space for an item. // -// // -// The deallocated space is stored in a queue for later reuse. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// dealloc() Deallocate space for an item. // +// // +// The deallocated space is stored in a queue for later reuse. // +// // +//============================================================================// void tetgenmesh::memorypool::dealloc(void *dyingitem) { @@ -4094,13 +4344,13 @@ void tetgenmesh::memorypool::dealloc(void *dyingitem) items--; } -/////////////////////////////////////////////////////////////////////////////// -// // -// traversalinit() Prepare to traverse the entire list of items. // -// // -// This routine is used in conjunction with traverse(). // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// traversalinit() Prepare to traverse the entire list of items. // +// // +// This routine is used in conjunction with traverse(). // +// // +//============================================================================// void tetgenmesh::memorypool::traversalinit() { @@ -4118,17 +4368,17 @@ void tetgenmesh::memorypool::traversalinit() pathitemsleft = itemsperblock; } -/////////////////////////////////////////////////////////////////////////////// -// // -// traverse() Find the next item in the list. // -// // -// This routine is used in conjunction with traversalinit(). Be forewarned // -// that this routine successively returns all items in the list, including // -// deallocated ones on the deaditemqueue. It's up to you to figure out which // -// ones are actually dead. It can usually be done more space-efficiently by // -// a routine that knows something about the structure of the item. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// traverse() Find the next item in the list. // +// // +// This routine is used in conjunction with traversalinit(). Be forewarned // +// that this routine successively returns all items in the list, including // +// deallocated ones on the deaditemqueue. It's up to you to figure out which // +// ones are actually dead. It can usually be done more space-efficiently by // +// a routine that knows something about the structure of the item. // +// // +//============================================================================// void* tetgenmesh::memorypool::traverse() { @@ -4159,15 +4409,15 @@ void* tetgenmesh::memorypool::traverse() return newitem; } -/////////////////////////////////////////////////////////////////////////////// -// // -// makeindex2pointmap() Create a map from index to vertices. // -// // -// 'idx2verlist' returns the created map. Traverse all vertices, a pointer // -// to each vertex is set into the array. The pointer to the first vertex is // -// saved in 'idx2verlist[in->firstnumber]'. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// makeindex2pointmap() Create a map from index to vertices. // +// // +// 'idx2verlist' returns the created map. Traverse all vertices, a pointer // +// to each vertex is set into the array. The pointer to the first vertex is // +// saved in 'idx2verlist[in->firstnumber]'. // +// // +//============================================================================// void tetgenmesh::makeindex2pointmap(point*& idx2verlist) { @@ -4189,19 +4439,19 @@ void tetgenmesh::makeindex2pointmap(point*& idx2verlist) } } -/////////////////////////////////////////////////////////////////////////////// -// // -// makesubfacemap() Create a map from vertex to subfaces incident at it. // -// // -// The map is returned in two arrays 'idx2faclist' and 'facperverlist'. All // -// subfaces incident at i-th vertex (i is counted from 0) are found in the // -// array facperverlist[j], where idx2faclist[i] <= j < idx2faclist[i + 1]. // -// Each entry in facperverlist[j] is a subface whose origin is the vertex. // -// // -// NOTE: These two arrays will be created inside this routine, don't forget // -// to free them after using. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// makesubfacemap() Create a map from vertex to subfaces incident at it. // +// // +// The map is returned in two arrays 'idx2faclist' and 'facperverlist'. All // +// subfaces incident at i-th vertex (i is counted from 0) are found in the // +// array facperverlist[j], where idx2faclist[i] <= j < idx2faclist[i + 1]. // +// Each entry in facperverlist[j] is a subface whose origin is the vertex. // +// // +// NOTE: These two arrays will be created inside this routine, don't forget // +// to free them after using. // +// // +//============================================================================// void tetgenmesh::makepoint2submap(memorypool* pool, int*& idx2faclist, face*& facperverlist) @@ -4280,11 +4530,11 @@ void tetgenmesh::makepoint2submap(memorypool* pool, int*& idx2faclist, idx2faclist[0] = 0; } -/////////////////////////////////////////////////////////////////////////////// -// // -// tetrahedrondealloc() Deallocate space for a tet., marking it dead. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// tetrahedrondealloc() Deallocate space for a tet., marking it dead. // +// // +//============================================================================// void tetgenmesh::tetrahedrondealloc(tetrahedron *dyingtetrahedron) { @@ -4303,11 +4553,11 @@ void tetgenmesh::tetrahedrondealloc(tetrahedron *dyingtetrahedron) tetrahedrons->dealloc((void *) dyingtetrahedron); } -/////////////////////////////////////////////////////////////////////////////// -// // -// tetrahedrontraverse() Traverse the tetrahedra, skipping dead ones. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// tetrahedrontraverse() Traverse the tetrahedra, skipping dead ones. // +// // +//============================================================================// tetgenmesh::tetrahedron* tetgenmesh::tetrahedrontraverse() { @@ -4336,12 +4586,12 @@ tetgenmesh::tetrahedron* tetgenmesh::alltetrahedrontraverse() return newtetrahedron; } -/////////////////////////////////////////////////////////////////////////////// -// // -// shellfacedealloc() Deallocate space for a shellface, marking it dead. // -// Used both for dealloc a subface and subsegment. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// shellfacedealloc() Deallocate space for a shellface, marking it dead. // +// Used both for dealloc a subface and subsegment. // +// // +//============================================================================// void tetgenmesh::shellfacedealloc(memorypool *pool, shellface *dyingsh) { @@ -4351,12 +4601,12 @@ void tetgenmesh::shellfacedealloc(memorypool *pool, shellface *dyingsh) pool->dealloc((void *) dyingsh); } -/////////////////////////////////////////////////////////////////////////////// -// // -// shellfacetraverse() Traverse the subfaces, skipping dead ones. Used // -// for both subfaces and subsegments pool traverse. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// shellfacetraverse() Traverse the subfaces, skipping dead ones. Used // +// for both subfaces and subsegments pool traverse. // +// // +//============================================================================// tetgenmesh::shellface* tetgenmesh::shellfacetraverse(memorypool *pool) { @@ -4372,11 +4622,11 @@ tetgenmesh::shellface* tetgenmesh::shellfacetraverse(memorypool *pool) } -/////////////////////////////////////////////////////////////////////////////// -// // -// pointdealloc() Deallocate space for a point, marking it dead. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// pointdealloc() Deallocate space for a point, marking it dead. // +// // +//============================================================================// void tetgenmesh::pointdealloc(point dyingpoint) { @@ -4386,11 +4636,11 @@ void tetgenmesh::pointdealloc(point dyingpoint) points->dealloc((void *) dyingpoint); } -/////////////////////////////////////////////////////////////////////////////// -// // -// pointtraverse() Traverse the points, skipping dead ones. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// pointtraverse() Traverse the points, skipping dead ones. // +// // +//============================================================================// tetgenmesh::point tetgenmesh::pointtraverse() { @@ -4405,11 +4655,11 @@ tetgenmesh::point tetgenmesh::pointtraverse() return newpoint; } -/////////////////////////////////////////////////////////////////////////////// -// // -// maketetrahedron() Create a new tetrahedron. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// maketetrahedron() Create a new tetrahedron. // +// // +//============================================================================// void tetgenmesh::maketetrahedron(triface *newtet) { @@ -4427,7 +4677,13 @@ void tetgenmesh::maketetrahedron(triface *newtet) newtet->tet[7] = NULL; // No attached segments and subfaces yet. newtet->tet[8] = NULL; - newtet->tet[9] = NULL; + newtet->tet[9] = NULL; + + newtet->tet[10] = NULL; // used by mesh improvement + + // Init the volume to be zero. + //REAL *polar = get_polar(newtet->tet); + //polar[4] = 0.0; // Initialize the marker (clear all flags). setelemmarker(newtet->tet, 0); for (int i = 0; i < numelemattrib; i++) { @@ -4441,12 +4697,47 @@ void tetgenmesh::maketetrahedron(triface *newtet) newtet->ver = 11; } -/////////////////////////////////////////////////////////////////////////////// -// // -// makeshellface() Create a new shellface with version zero. Used for // -// both subfaces and subsegments. // -// // -/////////////////////////////////////////////////////////////////////////////// +void tetgenmesh::maketetrahedron2(triface* newtet, point pa, point pb, + point pc, point pd) +{ + newtet->tet = (tetrahedron *) tetrahedrons->alloc(); + + // Initialize the four adjoining tetrahedra to be "outer space". + newtet->tet[0] = NULL; + newtet->tet[1] = NULL; + newtet->tet[2] = NULL; + newtet->tet[3] = NULL; + // Set four vertices. + newtet->tet[4] = (tetrahedron) pa; + newtet->tet[5] = (tetrahedron) pb; + newtet->tet[6] = (tetrahedron) pc; + newtet->tet[7] = (tetrahedron) pd; // may be dummypoint + // No attached segments and subfaces yet. + newtet->tet[8] = NULL; + newtet->tet[9] = NULL; + + newtet->tet[10] = NULL; // used by mesh improvement + + + // Initialize the marker (clear all flags). + setelemmarker(newtet->tet, 0); + for (int i = 0; i < numelemattrib; i++) { + setelemattribute(newtet->tet, i, 0.0); + } + if (b->varvolume) { + setvolumebound(newtet->tet, -1.0); + } + + // Initialize the version to be Zero. + newtet->ver = 11; +} + +//============================================================================// +// // +// makeshellface() Create a new shellface with version zero. Used for // +// both subfaces and subsegments. // +// // +//============================================================================// void tetgenmesh::makeshellface(memorypool *pool, face *newface) { @@ -4471,22 +4762,22 @@ void tetgenmesh::makeshellface(memorypool *pool, face *newface) // Initialize the maximum area bound. setareabound(*newface, 0.0); } + // Set the boundary marker to zero. + setshellmark(*newface, 0); // Clear the infection and marktest bits. ((int *) (newface->sh))[shmarkindex + 1] = 0; if (useinsertradius) { setfacetindex(*newface, 0); } - // Set the boundary marker to zero. - setshellmark(*newface, 0); newface->shver = 0; } -/////////////////////////////////////////////////////////////////////////////// -// // -// makepoint() Create a new point. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// makepoint() Create a new point. // +// // +//============================================================================// void tetgenmesh::makepoint(point* pnewpoint, enum verttype vtype) { @@ -4519,16 +4810,16 @@ void tetgenmesh::makepoint(point* pnewpoint, enum verttype vtype) setpointtype(*pnewpoint, vtype); } -/////////////////////////////////////////////////////////////////////////////// -// // -// initializepools() Calculate the sizes of the point, tetrahedron, and // -// subface. Initialize their memory pools. // -// // -// This routine also computes the indices 'pointmarkindex', 'point2simindex',// -// 'point2pbcptindex', 'elemattribindex', and 'volumeboundindex'. They are // -// used to find values within each point and tetrahedron, respectively. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// initializepools() Calculate the sizes of the point, tetrahedron, and // +// subface. Initialize their memory pools. // +// // +// This routine also computes the indices 'pointmarkindex', 'point2simindex', // +// 'point2pbcptindex', 'elemattribindex', and 'volumeboundindex'. They are // +// used to find values within each point and tetrahedron, respectively. // +// // +//============================================================================// void tetgenmesh::initializepools() { @@ -4568,12 +4859,12 @@ void tetgenmesh::initializepools() if (in->segmentconstraintlist || in->facetconstraintlist) { checkconstraints = 1; } - if (b->plc || b->refine) { + if (b->plc || b->refine || b->quality) { // Save the insertion radius for Steiner points if boundaries // are allowed be split. - if (!b->nobisect || checkconstraints) { + //if (!b->nobisect || checkconstraints) { useinsertradius = 1; - } + //} } // The index within each point at which its metric tensor is found. @@ -4609,12 +4900,13 @@ void tetgenmesh::initializepools() // saved directly after the metric. sizeoftensor++; } + pointinsradiusindex = pointmtrindex + sizeoftensor - 1; // The index within each point at which an element pointer is found, where // the index is measured in pointers. Ensure the index is aligned to a // sizeof(tetrahedron)-byte address. point2simindex = ((pointmtrindex + sizeoftensor) * sizeof(REAL) + sizeof(tetrahedron) - 1) / sizeof(tetrahedron); - if (b->plc || b->refine || b->voroout) { + if (b->plc || b->refine /*|| b->voroout*/) { // Increase the point size by three pointers, which are: // - a pointer to a tet, read by point2tet(); // - a pointer to a parent point, read by point2ppt()). @@ -4637,8 +4929,8 @@ void tetgenmesh::initializepools() // Now point size is the ints (indicated by pointmarkindex) plus: // - an integer for boundary marker; // - an integer for vertex type; - // - an integer for geometry tag (optional, -s option). - pointsize = (pointmarkindex + 2 + (b->psc ? 1 : 0)) * sizeof(tetrahedron); + // - an integer for local index (for vertex insertion) + pointsize = (pointmarkindex + 3) * sizeof(tetrahedron); // Initialize the pool of vertices. points = new memorypool(pointsize, b->vertexperblock, sizeof(REAL), 0); @@ -4696,25 +4988,43 @@ void tetgenmesh::initializepools() // The index to find the element markers. An integer containing varies // flags and element counter. - assert(sizeof(int) <= sizeof(tetrahedron)); - assert((sizeof(tetrahedron) % sizeof(int)) == 0); + if (!(sizeof(int) <= sizeof(tetrahedron)) || + ((sizeof(tetrahedron) % sizeof(int)))) { + terminatetetgen(this, 2); + } elemmarkerindex = (elesize - sizeof(tetrahedron)) / sizeof(int); + // Let (cx, cy, cz) be the circumcenter of this element, r be the radius + // of the circumsphere, and V be the (positive) volume of this element. + // We save the following five values: + // 2*cx, 2*cy, 2*cz, cx^2 + cy^2 + cz^2 - r^2 (height), 6*v. + // where the first four values define the polar plane of this element, + // the fifth value should be postive. It is used to guard the correctness + // of the polar plane. Otherwise, use exact arithmetics to calculate. + + // The index within each element which its polar parameters are found, + // this index is measured in REALs. + polarindex = (elesize + sizeof(REAL) - 1) / sizeof(REAL); + // The index within each element at which its attributes are found, where + // the index is measured in REALs. + //elemattribindex = (elesize + sizeof(REAL) - 1) / sizeof(REAL); + elemattribindex = polarindex; // polarindex + 5; // The actual number of element attributes. Note that if the // `b->regionattrib' flag is set, an additional attribute will be added. numelemattrib = in->numberoftetrahedronattributes + (b->regionattrib > 0); - - // The index within each element at which its attributes are found, where - // the index is measured in REALs. - elemattribindex = (elesize + sizeof(REAL) - 1) / sizeof(REAL); // The index within each element at which the maximum volume bound is // found, where the index is measured in REALs. volumeboundindex = elemattribindex + numelemattrib; // If element attributes or an constraint are needed, increase the number // of bytes occupied by an element. + if (!b->varvolume) { + if (b->refine && (in->refine_elem_list != NULL)) { + b->varvolume = 1; // refine a given element list. + } + } if (b->varvolume) { elesize = (volumeboundindex + 1) * sizeof(REAL); - } else if (numelemattrib > 0) { + } else { elesize = volumeboundindex * sizeof(REAL); } @@ -4747,13 +5057,9 @@ void tetgenmesh::initializepools() // the marker is aligned to a sizeof(int)-byte address. shmarkindex = (shsize + sizeof(int) - 1) / sizeof(int); // Increase the number of bytes by two or three integers, one for facet - // marker, one for shellface type, and optionally one for pbc group. - shsize = (shmarkindex + 2) * sizeof(shellface); - if (useinsertradius) { - // Increase the number of byte by one integer for storing facet index. - // set/read by setfacetindex() and getfacetindex. - shsize = (shmarkindex + 3) * sizeof(shellface); - } + // marker, one for shellface type and flags, and optionally one + // for storing facet index (for mesh refinement). + shsize = (shmarkindex + 2 + useinsertradius) * sizeof(shellface); // Initialize the pool of subfaces. Each subface record is eight-byte // aligned so it has room to store an edge version (from 0 to 5) in @@ -4794,6 +5100,7 @@ void tetgenmesh::initializepools() // Initialize the pools for flips. flippool = new memorypool(sizeof(badface), 1024, sizeof(void *), 0); + later_unflip_queue = new arraypool(sizeof(badface), 10); unflipqueue = new arraypool(sizeof(badface), 10); // Initialize the arraypools for point insertion. @@ -4801,34 +5108,35 @@ void tetgenmesh::initializepools() cavebdrylist = new arraypool(sizeof(triface), 10); caveoldtetlist = new arraypool(sizeof(triface), 10); cavetetvertlist = new arraypool(sizeof(point), 10); + cave_oldtet_list = new arraypool(sizeof(tetrahedron*), 10); } -//// //// -//// //// -//// mempool_cxx ////////////////////////////////////////////////////////////// +// // +// // +//== mempool_cxx =============================================================// -//// geom_cxx ///////////////////////////////////////////////////////////////// -//// //// -//// //// +//== geom_cxx ================================================================// +// // +// // // PI is the ratio of a circle's circumference to its diameter. REAL tetgenmesh::PI = 3.14159265358979323846264338327950288419716939937510582; -/////////////////////////////////////////////////////////////////////////////// -// // -// insphere_s() Insphere test with symbolic perturbation. // -// // -// Given four points pa, pb, pc, and pd, test if the point pe lies inside or // -// outside the circumscribed sphere of the four points. // -// // -// Here we assume that the 3d orientation of the point sequence {pa, pb, pc, // -// pd} is positive (NOT zero), i.e., pd lies above the plane passing through // -// points pa, pb, and pc. Otherwise, the returned sign is flipped. // -// // -// Return a positive value (> 0) if pe lies inside, a negative value (< 0) // -// if pe lies outside the sphere, the returned value will not be zero. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// insphere_s() Insphere test with symbolic perturbation. // +// // +// Given four points pa, pb, pc, and pd, test if the point pe lies inside or // +// outside the circumscribed sphere of the four points. // +// // +// Here we assume that the 3d orientation of the point sequence {pa, pb, pc, // +// pd} is positive (NOT zero), i.e., pd lies above the plane passing through // +// points pa, pb, and pc. Otherwise, the returned sign is flipped. // +// // +// Return a positive value (> 0) if pe lies inside, a negative value (< 0) // +// if pe lies outside the sphere, the returned value will not be zero. // +// // +//============================================================================// REAL tetgenmesh::insphere_s(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe) { @@ -4876,28 +5184,30 @@ REAL tetgenmesh::insphere_s(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe) } oriB = -orient3d(pt[0], pt[2], pt[3], pt[4]); - assert(oriB != 0.0); // SELF_CHECK + if (oriB == 0.0) { + terminatetetgen(this, 2); + } // Flip the sign if there are odd number of swaps. if ((swaps % 2) != 0) oriB = -oriB; return oriB; } -/////////////////////////////////////////////////////////////////////////////// -// // -// orient4d_s() 4d orientation test with symbolic perturbation. // -// // -// Given four lifted points pa', pb', pc', and pd' in R^4,test if the lifted // -// point pe' in R^4 lies below or above the hyperplane passing through the // -// four points pa', pb', pc', and pd'. // -// // -// Here we assume that the 3d orientation of the point sequence {pa, pb, pc, // -// pd} is positive (NOT zero), i.e., pd lies above the plane passing through // -// the points pa, pb, and pc. Otherwise, the returned sign is flipped. // -// // -// Return a positive value (> 0) if pe' lies below, a negative value (< 0) // -// if pe' lies above the hyperplane, the returned value should not be zero. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// orient4d_s() 4d orientation test with symbolic perturbation. // +// // +// Given four lifted points pa', pb', pc', and pd' in R^4,test if the lifted // +// point pe' in R^4 lies below or above the hyperplane passing through the // +// four points pa', pb', pc', and pd'. // +// // +// Here we assume that the 3d orientation of the point sequence {pa, pb, pc, // +// pd} is positive (NOT zero), i.e., pd lies above the plane passing through // +// the points pa, pb, and pc. Otherwise, the returned sign is flipped. // +// // +// Return a positive value (> 0) if pe' lies below, a negative value (< 0) // +// if pe' lies above the hyperplane, the returned value should not be zero. // +// // +//============================================================================// REAL tetgenmesh::orient4d_s(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe, REAL aheight, REAL bheight, REAL cheight, @@ -4948,33 +5258,35 @@ REAL tetgenmesh::orient4d_s(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe, } oriB = -orient3d(pt[0], pt[2], pt[3], pt[4]); - assert(oriB != 0.0); // SELF_CHECK + if (oriB == 0.0) { + terminatetetgen(this, 2); + } // Flip the sign if there are odd number of swaps. if ((swaps % 2) != 0) oriB = -oriB; return oriB; } -/////////////////////////////////////////////////////////////////////////////// -// // -// tri_edge_test() Triangle-edge intersection test. // -// // -// This routine takes a triangle T (with vertices A, B, C) and an edge E (P, // -// Q) in 3D, and tests if they intersect each other. // -// // -// If the point 'R' is not NULL, it lies strictly above the plane defined by // -// A, B, C. It is used in test when T and E are coplanar. // -// // -// If T and E intersect each other, they may intersect in different ways. If // -// 'level' > 0, their intersection type will be reported 'types' and 'pos'. // -// // -// The return value indicates one of the following cases: // -// - 0, T and E are disjoint. // -// - 1, T and E intersect each other. // -// - 2, T and E are not coplanar. They intersect at a single point. // -// - 4, T and E are coplanar. They intersect at a single point or a line // -// segment (if types[1] != DISJOINT). // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// tri_edge_test() Triangle-edge intersection test. // +// // +// This routine takes a triangle T (with vertices A, B, C) and an edge E (P, // +// Q) in 3D, and tests if they intersect each other. // +// // +// If the point 'R' is not NULL, it lies strictly above the plane defined by // +// A, B, C. It is used in test when T and E are coplanar. // +// // +// If T and E intersect each other, they may intersect in different ways. If // +// 'level' > 0, their intersection type will be reported 'types' and 'pos'. // +// // +// The return value indicates one of the following cases: // +// - 0, T and E are disjoint. // +// - 1, T and E intersect each other. // +// - 2, T and E are not coplanar. They intersect at a single point. // +// - 4, T and E are coplanar. They intersect at a single point or a line // +// segment (if types[1] != DISJOINT). // +// // +//============================================================================// #define SETVECTOR3(V, a0, a1, a2) (V)[0] = (a0); (V)[1] = (a1); (V)[2] = (a2) @@ -5012,7 +5324,6 @@ int tetgenmesh::tri_edge_2d(point A, point B, point C, point P, point Q, } else { // The triangle [A,B,C] is (nearly) degenerate, i.e., it is (close) // to a line. We need a line-line intersection test. - //assert(0); // !!! A non-save return value.!!! return 0; // DISJOINT } @@ -5248,7 +5559,6 @@ int tetgenmesh::tri_edge_2d(point A, point B, point C, point P, point Q, return 1; // They are intersected. } - assert(z1 != 4); // SELF_CHECK if (z1 == 1) { if (s1 == 0) { // (0###) @@ -5281,7 +5591,6 @@ int tetgenmesh::tri_edge_2d(point A, point B, point C, point P, point Q, if (z1 == 0) { // (tritri-03) if (s1 < 0) { if (s3 > 0) { - assert(s2 > 0); // SELF_CHECK if (s4 > 0) { // [P, Q] overlaps [k, l] (-+++). types[0] = (int) ACROSSEDGE; @@ -5311,7 +5620,6 @@ int tetgenmesh::tri_edge_2d(point A, point B, point C, point P, point Q, } } else { if (s3 == 0) { - assert(s2 > 0); // SELF_CHECK if (s4 > 0) { // P = k, [P, Q] in [k, l] (-+0+). types[0] = (int) TOUCHEDGE; @@ -5387,7 +5695,6 @@ int tetgenmesh::tri_edge_2d(point A, point B, point C, point P, point Q, } else if (z1 == 2) { // (tritri-23) if (s1 < 0) { if (s3 > 0) { - assert(s2 > 0); // SELF_CHECK if (s4 > 0) { // [P, Q] overlaps [A, l] (-+++). types[0] = (int) ACROSSVERT; @@ -5417,7 +5724,6 @@ int tetgenmesh::tri_edge_2d(point A, point B, point C, point P, point Q, } } else { if (s3 == 0) { - assert(s2 > 0); // SELF_CHECK if (s4 > 0) { // P = A, [P, Q] in [A, l] (-+0+). types[0] = (int) SHAREVERT; @@ -5493,7 +5799,6 @@ int tetgenmesh::tri_edge_2d(point A, point B, point C, point P, point Q, } else if (z1 == 3) { // (tritri-33) if (s1 < 0) { if (s3 > 0) { - assert(s2 > 0); // SELF_CHECK if (s4 > 0) { // [P, Q] overlaps [A, B] (-+++). types[0] = (int) ACROSSVERT; @@ -5523,7 +5828,6 @@ int tetgenmesh::tri_edge_2d(point A, point B, point C, point P, point Q, } } else { if (s3 == 0) { - assert(s2 > 0); // SELF_CHECK if (s4 > 0) { // P = A, [P, Q] in [A, B] (-+0+). types[0] = (int) SHAREVERT; @@ -5739,9 +6043,6 @@ int tetgenmesh::tri_edge_tail(point A,point B,point C,point P,point Q,point R, types[0] = (int) ACROSSVERT; pos[0] = pu[1]; // B pos[1] = 0; // [P, Q] - } else { // s3 == 0 (000) - // Impossible. - assert(0); } } } @@ -5791,9 +6092,6 @@ int tetgenmesh::tri_edge_tail(point A,point B,point C,point P,point Q,point R, types[0] = (int) SHAREVERT; pos[0] = pu[1]; // B pos[1] = pv[1]; // Q - } else { // s3 == 0 (000) - // Impossible. - assert(0); } } } @@ -5815,15 +6113,15 @@ int tetgenmesh::tri_edge_test(point A, point B, point C, point P, point Q, return tri_edge_tail(A, B, C, P, Q, R, sP, sQ, level, types, pos); } -/////////////////////////////////////////////////////////////////////////////// -// // -// tri_tri_inter() Test whether two triangle (abc) and (opq) are // -// intersecting or not. // -// // -// Return 0 if they are disjoint. Otherwise, return 1. 'type' returns one of // -// the four cases: SHAREVERTEX, SHAREEDGE, SHAREFACE, and INTERSECT. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// tri_tri_inter() Test whether two triangle (abc) and (opq) are // +// intersecting or not. // +// // +// Return 0 if they are disjoint. Otherwise, return 1. 'type' returns one of // +// the four cases: SHAREVERTEX, SHAREEDGE, SHAREFACE, and INTERSECT. // +// // +//============================================================================// int tetgenmesh::tri_edge_inter_tail(REAL* A, REAL* B, REAL* C, REAL* P, REAL* Q, REAL s_p, REAL s_q) @@ -5847,7 +6145,6 @@ int tetgenmesh::tri_edge_inter_tail(REAL* A, REAL* B, REAL* C, REAL* P, if (types[1] == (int) DISJOINT) { return (int) SHAREVERT; } else { - assert(types[1] != (int) SHAREVERT); return (int) INTERSECT; } } else { @@ -5857,8 +6154,6 @@ int tetgenmesh::tri_edge_inter_tail(REAL* A, REAL* B, REAL* C, REAL* P, return (int) INTERSECT; } } - } else { - assert(0); } } @@ -5912,9 +6207,6 @@ int tetgenmesh::tri_tri_inter(REAL* A,REAL* B,REAL* C,REAL* O,REAL* P,REAL* Q) return (int) SHAREFACE; } - // It is only possible either no share edge or one. - assert(shareedge == 0 || shareedge == 1); - // Continue to detect whether opq and abc are intersecting or not. int opqab, opqbc, opqca; @@ -5934,36 +6226,24 @@ int tetgenmesh::tri_tri_inter(REAL* A,REAL* B,REAL* C,REAL* O,REAL* P,REAL* Q) // At this point, two triangles are not intersecting and not coincident. // They may be share an edge, or share a vertex, or disjoint. if (abcop == (int) SHAREEDGE) { - assert((abcpq == (int) SHAREVERT) && (abcqo == (int) SHAREVERT)); // op is coincident with an edge of abc. return (int) SHAREEDGE; } if (abcpq == (int) SHAREEDGE) { - assert((abcop == (int) SHAREVERT) && (abcqo == (int) SHAREVERT)); // pq is coincident with an edge of abc. return (int) SHAREEDGE; } if (abcqo == (int) SHAREEDGE) { - assert((abcop == (int) SHAREVERT) && (abcpq == (int) SHAREVERT)); // qo is coincident with an edge of abc. return (int) SHAREEDGE; } // They may share a vertex or disjoint. if (abcop == (int) SHAREVERT) { - // o or p is coincident with a vertex of abc. - if (abcpq == (int) SHAREVERT) { - // p is the coincident vertex. - assert(abcqo != (int) SHAREVERT); - } else { - // o is the coincident vertex. - assert(abcqo == (int) SHAREVERT); - } return (int) SHAREVERT; } if (abcpq == (int) SHAREVERT) { // q is the coincident vertex. - assert(abcqo == (int) SHAREVERT); return (int) SHAREVERT; } @@ -5971,29 +6251,29 @@ int tetgenmesh::tri_tri_inter(REAL* A,REAL* B,REAL* C,REAL* O,REAL* P,REAL* Q) return (int) DISJOINT; } -/////////////////////////////////////////////////////////////////////////////// -// // -// lu_decmp() Compute the LU decomposition of a matrix. // -// // -// Compute the LU decomposition of a (non-singular) square matrix A using // -// partial pivoting and implicit row exchanges. The result is: // -// A = P * L * U, // -// where P is a permutation matrix, L is unit lower triangular, and U is // -// upper triangular. The factored form of A is used in combination with // -// 'lu_solve()' to solve linear equations: Ax = b, or invert a matrix. // -// // -// The inputs are a square matrix 'lu[N..n+N-1][N..n+N-1]', it's size is 'n'.// -// On output, 'lu' is replaced by the LU decomposition of a rowwise permuta- // -// tion of itself, 'ps[N..n+N-1]' is an output vector that records the row // -// permutation effected by the partial pivoting, effectively, 'ps' array // -// tells the user what the permutation matrix P is; 'd' is output as +1/-1 // -// depending on whether the number of row interchanges was even or odd, // -// respectively. // -// // -// Return true if the LU decomposition is successfully computed, otherwise, // -// return false in case that A is a singular matrix. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// lu_decmp() Compute the LU decomposition of a matrix. // +// // +// Compute the LU decomposition of a (non-singular) square matrix A using // +// partial pivoting and implicit row exchanges. The result is: // +// A = P * L * U, // +// where P is a permutation matrix, L is unit lower triangular, and U is // +// upper triangular. The factored form of A is used in combination with // +// 'lu_solve()' to solve linear equations: Ax = b, or invert a matrix. // +// // +// The inputs are a square matrix 'lu[N..n+N-1][N..n+N-1]', it's size is 'n'. // +// On output, 'lu' is replaced by the LU decomposition of a rowwise permuta- // +// tion of itself, 'ps[N..n+N-1]' is an output vector that records the row // +// permutation effected by the partial pivoting, effectively, 'ps' array // +// tells the user what the permutation matrix P is; 'd' is output as +1/-1 // +// depending on whether the number of row interchanges was even or odd, // +// respectively. // +// // +// Return true if the LU decomposition is successfully computed, otherwise, // +// return false in case that A is a singular matrix. // +// // +//============================================================================// bool tetgenmesh::lu_decmp(REAL lu[4][4], int n, int* ps, REAL* d, int N) { @@ -6053,20 +6333,20 @@ bool tetgenmesh::lu_decmp(REAL lu[4][4], int n, int* ps, REAL* d, int N) return lu[ps[n + N - 1]][n + N - 1] != 0.0; } -/////////////////////////////////////////////////////////////////////////////// -// // -// lu_solve() Solves the linear equation: Ax = b, after the matrix A // -// has been decomposed into the lower and upper triangular // -// matrices L and U, where A = LU. // -// // -// 'lu[N..n+N-1][N..n+N-1]' is input, not as the matrix 'A' but rather as // -// its LU decomposition, computed by the routine 'lu_decmp'; 'ps[N..n+N-1]' // -// is input as the permutation vector returned by 'lu_decmp'; 'b[N..n+N-1]' // -// is input as the right-hand side vector, and returns with the solution // -// vector. 'lu', 'n', and 'ps' are not modified by this routine and can be // -// left in place for successive calls with different right-hand sides 'b'. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// lu_solve() Solves the linear equation: Ax = b, after the matrix A // +// has been decomposed into the lower and upper triangular // +// matrices L and U, where A = LU. // +// // +// 'lu[N..n+N-1][N..n+N-1]' is input, not as the matrix 'A' but rather as // +// its LU decomposition, computed by the routine 'lu_decmp'; 'ps[N..n+N-1]' // +// is input as the permutation vector returned by 'lu_decmp'; 'b[N..n+N-1]' // +// is input as the right-hand side vector, and returns with the solution // +// vector. 'lu', 'n', and 'ps' are not modified by this routine and can be // +// left in place for successive calls with different right-hand sides 'b'. // +// // +//============================================================================// void tetgenmesh::lu_solve(REAL lu[4][4], int n, int* ps, REAL* b, int N) { @@ -6094,17 +6374,17 @@ void tetgenmesh::lu_solve(REAL lu[4][4], int n, int* ps, REAL* b, int N) for (i = N; i < n + N; i++) b[i] = X[i]; } -/////////////////////////////////////////////////////////////////////////////// -// // -// incircle3d() 3D in-circle test. // -// // -// Return a negative value if pd is inside the circumcircle of the triangle // -// pa, pb, and pc. // -// // -// IMPORTANT: It assumes that [a,b] is the common edge, i.e., the two input // -// triangles are [a,b,c] and [b,a,d]. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// incircle3d() 3D in-circle test. // +// // +// Return a negative value if pd is inside the circumcircle of the triangle // +// pa, pb, and pc. // +// // +// IMPORTANT: It assumes that [a,b] is the common edge, i.e., the two input // +// triangles are [a,b,c] and [b,a,d]. // +// // +//============================================================================// REAL tetgenmesh::incircle3d(point pa, point pb, point pc, point pd) { @@ -6140,22 +6420,22 @@ REAL tetgenmesh::incircle3d(point pa, point pb, point pc, point pd) return sign; } -/////////////////////////////////////////////////////////////////////////////// -// // -// facenormal() Calculate the normal of the face. // -// // -// The normal of the face abc can be calculated by the cross product of 2 of // -// its 3 edge vectors. A better choice of two edge vectors will reduce the // -// numerical error during the calculation. Burdakov proved that the optimal // -// basis problem is equivalent to the minimum spanning tree problem with the // -// edge length be the functional, see Burdakov, "A greedy algorithm for the // -// optimal basis problem", BIT 37:3 (1997), 591-599. If 'pivot' > 0, the two // -// short edges in abc are chosen for the calculation. // -// // -// If 'lav' is not NULL and if 'pivot' is set, the average edge length of // -// the edges of the face [a,b,c] is returned. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// facenormal() Calculate the normal of the face. // +// // +// The normal of the face abc can be calculated by the cross product of 2 of // +// its 3 edge vectors. A better choice of two edge vectors will reduce the // +// numerical error during the calculation. Burdakov proved that the optimal // +// basis problem is equivalent to the minimum spanning tree problem with the // +// edge length be the functional, see Burdakov, "A greedy algorithm for the // +// optimal basis problem", BIT 37:3 (1997), 591-599. If 'pivot' > 0, the two // +// short edges in abc are chosen for the calculation. // +// // +// If 'lav' is not NULL and if 'pivot' is set, the average edge length of // +// the edges of the face [a,b,c] is returned. // +// // +//============================================================================// void tetgenmesh::facenormal(point pa, point pb, point pc, REAL *n, int pivot, REAL* lav) @@ -6209,48 +6489,49 @@ void tetgenmesh::facenormal(point pa, point pb, point pc, REAL *n, int pivot, n[2] = -n[2]; } -/////////////////////////////////////////////////////////////////////////////// -// // -// shortdistance() Returns the shortest distance from point p to a line // -// defined by two points e1 and e2. // -// // -// First compute the projection length l_p of the vector v1 = p - e1 along // -// the vector v2 = e2 - e1. Then Pythagoras' Theorem is used to compute the // -// shortest distance. // -// // -// This routine allows that p is collinear with the line. In this case, the // -// return value is zero. The two points e1 and e2 should not be identical. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// facedihedral() Return the dihedral angle (in radian) between two // +// adjoining faces. // +// // +// 'pa', 'pb' are the shared edge of these two faces, 'pc1', and 'pc2' are // +// apexes of these two faces. Return the angle (between 0 to 2*pi) between // +// the normal of face (pa, pb, pc1) and normal of face (pa, pb, pc2). // +// // +//============================================================================// -REAL tetgenmesh::shortdistance(REAL* p, REAL* e1, REAL* e2) +REAL tetgenmesh::facedihedral(REAL* pa, REAL* pb, REAL* pc1, REAL* pc2) { - REAL v1[3], v2[3]; - REAL len, l_p; - - v1[0] = e2[0] - e1[0]; - v1[1] = e2[1] - e1[1]; - v1[2] = e2[2] - e1[2]; - v2[0] = p[0] - e1[0]; - v2[1] = p[1] - e1[1]; - v2[2] = p[2] - e1[2]; - - len = sqrt(dot(v1, v1)); - assert(len != 0.0); + REAL n1[3], n2[3]; + REAL n1len, n2len; + REAL costheta, ori; + REAL theta; - v1[0] /= len; - v1[1] /= len; - v1[2] /= len; - l_p = dot(v1, v2); + facenormal(pa, pb, pc1, n1, 1, NULL); + facenormal(pa, pb, pc2, n2, 1, NULL); + n1len = sqrt(dot(n1, n1)); + n2len = sqrt(dot(n2, n2)); + costheta = dot(n1, n2) / (n1len * n2len); + // Be careful rounding error! + if (costheta > 1.0) { + costheta = 1.0; + } else if (costheta < -1.0) { + costheta = -1.0; + } + theta = acos(costheta); + ori = orient3d(pa, pb, pc1, pc2); + if (ori > 0.0) { + theta = 2 * PI - theta; + } - return sqrt(dot(v2, v2) - l_p * l_p); + return theta; } -/////////////////////////////////////////////////////////////////////////////// -// // -// triarea() Return the area of a triangle. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// triarea() Return the area of a triangle. // +// // +//============================================================================// REAL tetgenmesh::triarea(REAL* pa, REAL* pb, REAL* pc) { @@ -6290,18 +6571,18 @@ REAL tetgenmesh::orient3dfast(REAL *pa, REAL *pb, REAL *pc, REAL *pd) + cdx * (ady * bdz - adz * bdy); } -/////////////////////////////////////////////////////////////////////////////// -// // -// interiorangle() Return the interior angle (0 - 2 * PI) between vectors // -// o->p1 and o->p2. // -// // -// 'n' is the normal of the plane containing face (o, p1, p2). The interior // -// angle is the total angle rotating from o->p1 around n to o->p2. Exchange // -// the position of p1 and p2 will get the complement angle of the other one. // -// i.e., interiorangle(o, p1, p2) = 2 * PI - interiorangle(o, p2, p1). Set // -// 'n' be NULL if you only want the interior angle between 0 - PI. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// interiorangle() Return the interior angle (0 - 2 * PI) between vectors // +// o->p1 and o->p2. // +// // +// 'n' is the normal of the plane containing face (o, p1, p2). The interior // +// angle is the total angle rotating from o->p1 around n to o->p2. Exchange // +// the position of p1 and p2 will get the complement angle of the other one. // +// i.e., interiorangle(o, p1, p2) = 2 * PI - interiorangle(o, p2, p1). Set // +// 'n' be NULL if you only want the interior angle between 0 - PI. // +// // +//============================================================================// REAL tetgenmesh::interiorangle(REAL* o, REAL* p1, REAL* p2, REAL* n) { @@ -6319,7 +6600,6 @@ REAL tetgenmesh::interiorangle(REAL* o, REAL* p1, REAL* p2, REAL* n) len1 = sqrt(dot(v1, v1)); len2 = sqrt(dot(v2, v2)); lenlen = len1 * len2; - assert(lenlen != 0.0); costheta = dot(v1, v2) / lenlen; if (costheta > 1.0) { @@ -6343,11 +6623,39 @@ REAL tetgenmesh::interiorangle(REAL* o, REAL* p1, REAL* p2, REAL* n) return theta; } -/////////////////////////////////////////////////////////////////////////////// -// // -// projpt2edge() Return the projection point from a point to an edge. // -// // -/////////////////////////////////////////////////////////////////////////////// +REAL tetgenmesh::cos_interiorangle(REAL* o, REAL* p1, REAL* p2) +{ + REAL v1[3], v2[3], np[3]; + REAL theta, costheta, lenlen; + REAL ori, len1, len2; + + // Get the interior angle (0 - PI) between o->p1, and o->p2. + v1[0] = p1[0] - o[0]; + v1[1] = p1[1] - o[1]; + v1[2] = p1[2] - o[2]; + v2[0] = p2[0] - o[0]; + v2[1] = p2[1] - o[1]; + v2[2] = p2[2] - o[2]; + len1 = sqrt(dot(v1, v1)); + len2 = sqrt(dot(v2, v2)); + lenlen = len1 * len2; + + costheta = dot(v1, v2) / lenlen; + + if (costheta > 1.0) { + costheta = 1.0; // Roundoff. + } else if (costheta < -1.0) { + costheta = -1.0; // Roundoff. + } + + return costheta; +} + +//============================================================================// +// // +// projpt2edge() Return the projection point from a point to an edge. // +// // +//============================================================================// void tetgenmesh::projpt2edge(REAL* p, REAL* e1, REAL* e2, REAL* prj) { @@ -6362,7 +6670,6 @@ void tetgenmesh::projpt2edge(REAL* p, REAL* e1, REAL* e2, REAL* prj) v2[2] = p[2] - e1[2]; len = sqrt(dot(v1, v1)); - assert(len != 0.0); v1[0] /= len; v1[1] /= len; v1[2] /= len; @@ -6373,11 +6680,11 @@ void tetgenmesh::projpt2edge(REAL* p, REAL* e1, REAL* e2, REAL* prj) prj[2] = e1[2] + l_p * v1[2]; } -/////////////////////////////////////////////////////////////////////////////// -// // -// projpt2face() Return the projection point from a point to a face. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// projpt2face() Return the projection point from a point to a face. // +// // +//============================================================================// void tetgenmesh::projpt2face(REAL* p, REAL* f1, REAL* f2, REAL* f3, REAL* prj) { @@ -6404,287 +6711,40 @@ void tetgenmesh::projpt2face(REAL* p, REAL* f1, REAL* f2, REAL* f3, REAL* prj) prj[2] = p[2] - dist * fnormal[2]; } -/////////////////////////////////////////////////////////////////////////////// -// // -// facedihedral() Return the dihedral angle (in radian) between two // -// adjoining faces. // -// // -// 'pa', 'pb' are the shared edge of these two faces, 'pc1', and 'pc2' are // -// apexes of these two faces. Return the angle (between 0 to 2*pi) between // -// the normal of face (pa, pb, pc1) and normal of face (pa, pb, pc2). // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// circumsphere() Calculate the smallest circumsphere (center and radius) // +// of the given three or four points. // +// // +// The circumsphere of four points (a tetrahedron) is unique if they are not // +// degenerate. If 'pd = NULL', the smallest circumsphere of three points is // +// the diametral sphere of the triangle if they are not degenerate. // +// // +// Return TRUE if the input points are not degenerate and the circumcenter // +// and circumradius are returned in 'cent' and 'radius' respectively if they // +// are not NULLs. Otherwise, return FALSE, the four points are co-planar. // +// // +//============================================================================// -REAL tetgenmesh::facedihedral(REAL* pa, REAL* pb, REAL* pc1, REAL* pc2) +bool tetgenmesh::circumsphere(REAL* pa, REAL* pb, REAL* pc, REAL* pd, + REAL* cent, REAL* radius) { - REAL n1[3], n2[3]; - REAL n1len, n2len; - REAL costheta, ori; - REAL theta; + REAL A[4][4], rhs[4], D; + int indx[4]; - facenormal(pa, pb, pc1, n1, 1, NULL); - facenormal(pa, pb, pc2, n2, 1, NULL); - n1len = sqrt(dot(n1, n1)); - n2len = sqrt(dot(n2, n2)); - costheta = dot(n1, n2) / (n1len * n2len); - // Be careful rounding error! - if (costheta > 1.0) { - costheta = 1.0; - } else if (costheta < -1.0) { - costheta = -1.0; - } - theta = acos(costheta); - ori = orient3d(pa, pb, pc1, pc2); - if (ori > 0.0) { - theta = 2 * PI - theta; - } - - return theta; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// tetalldihedral() Get all (six) dihedral angles of a tet. // -// // -// If 'cosdd' is not NULL, it returns the cosines of the 6 dihedral angles, // -// the edge indices are given in the global array 'edge2ver'. If 'cosmaxd' // -// (or 'cosmind') is not NULL, it returns the cosine of the maximal (or // -// minimal) dihedral angle. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::tetalldihedral(point pa, point pb, point pc, point pd, - REAL* cosdd, REAL* cosmaxd, REAL* cosmind) -{ - REAL N[4][3], vol, cosd, len; - int f1 = 0, f2 = 0, i, j; - - vol = 0; // Check if the tet is valid or not. - - // Get four normals of faces of the tet. - tetallnormal(pa, pb, pc, pd, N, &vol); - - if (vol > 0) { - // Normalize the normals. - for (i = 0; i < 4; i++) { - len = sqrt(dot(N[i], N[i])); - if (len != 0.0) { - for (j = 0; j < 3; j++) N[i][j] /= len; - } else { - // There are degeneracies, such as duplicated vertices. - vol = 0; //assert(0); - } - } - } - - if (vol <= 0) { // if (vol == 0.0) { - // A degenerated tet or an inverted tet. - facenormal(pc, pb, pd, N[0], 1, NULL); - facenormal(pa, pc, pd, N[1], 1, NULL); - facenormal(pb, pa, pd, N[2], 1, NULL); - facenormal(pa, pb, pc, N[3], 1, NULL); - // Normalize the normals. - for (i = 0; i < 4; i++) { - len = sqrt(dot(N[i], N[i])); - if (len != 0.0) { - for (j = 0; j < 3; j++) N[i][j] /= len; - } else { - // There are degeneracies, such as duplicated vertices. - break; // Not a valid normal. - } - } - if (i < 4) { - // Do not calculate dihedral angles. - // Set all angles be 0 degree. There will be no quality optimization for - // this tet! Use volume optimization to correct it. - if (cosdd != NULL) { - for (i = 0; i < 6; i++) { - cosdd[i] = -1.0; // 180 degree. - } - } - // This tet has zero volume. - if (cosmaxd != NULL) { - *cosmaxd = -1.0; // 180 degree. - } - if (cosmind != NULL) { - *cosmind = -1.0; // 180 degree. - } - return false; - } - } - - // Calculate the cosine of the dihedral angles of the edges. - for (i = 0; i < 6; i++) { - switch (i) { - case 0: f1 = 0; f2 = 1; break; // [c,d]. - case 1: f1 = 1; f2 = 2; break; // [a,d]. - case 2: f1 = 2; f2 = 3; break; // [a,b]. - case 3: f1 = 0; f2 = 3; break; // [b,c]. - case 4: f1 = 2; f2 = 0; break; // [b,d]. - case 5: f1 = 1; f2 = 3; break; // [a,c]. - } - cosd = -dot(N[f1], N[f2]); - if (cosd < -1.0) cosd = -1.0; // Rounding. - if (cosd > 1.0) cosd = 1.0; // Rounding. - if (cosdd) cosdd[i] = cosd; - if (cosmaxd || cosmind) { - if (i == 0) { - if (cosmaxd) *cosmaxd = cosd; - if (cosmind) *cosmind = cosd; - } else { - if (cosmaxd) *cosmaxd = cosd < *cosmaxd ? cosd : *cosmaxd; - if (cosmind) *cosmind = cosd > *cosmind ? cosd : *cosmind; - } - } - } - - return true; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// tetallnormal() Get the in-normals of the four faces of a given tet. // -// // -// Let tet be abcd. N[4][3] returns the four normals, which are: N[0] cbd, // -// N[1] acd, N[2] bad, N[3] abc (exactly corresponding to the face indices // -// of the mesh data structure). These normals are unnormalized. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::tetallnormal(point pa, point pb, point pc, point pd, - REAL N[4][3], REAL* volume) -{ - REAL A[4][4], rhs[4], D; - int indx[4]; - int i, j; - - // get the entries of A[3][3]. - for (i = 0; i < 3; i++) A[0][i] = pa[i] - pd[i]; // d->a vec - for (i = 0; i < 3; i++) A[1][i] = pb[i] - pd[i]; // d->b vec - for (i = 0; i < 3; i++) A[2][i] = pc[i] - pd[i]; // d->c vec - - // Compute the inverse of matrix A, to get 3 normals of the 4 faces. - if (lu_decmp(A, 3, indx, &D, 0)) { // Decompose the matrix just once. - if (volume != NULL) { - // Get the volume of the tet. - *volume = fabs((A[indx[0]][0] * A[indx[1]][1] * A[indx[2]][2])) / 6.0; - } - for (j = 0; j < 3; j++) { - for (i = 0; i < 3; i++) rhs[i] = 0.0; - rhs[j] = 1.0; // Positive means the inside direction - lu_solve(A, 3, indx, rhs, 0); - for (i = 0; i < 3; i++) N[j][i] = rhs[i]; - } - // Get the fourth normal by summing up the first three. - for (i = 0; i < 3; i++) N[3][i] = - N[0][i] - N[1][i] - N[2][i]; - } else { - // The tet is degenerated. - if (volume != NULL) { - *volume = 0; - } - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// tetaspectratio() Calculate the aspect ratio of the tetrahedron. // -// // -// The aspect ratio of a tet is R/h, where R is the circumradius and h is // -// the shortest height of the tet. // -// // -/////////////////////////////////////////////////////////////////////////////// - -REAL tetgenmesh::tetaspectratio(point pa, point pb, point pc, point pd) -{ - REAL vda[3], vdb[3], vdc[3]; - REAL N[4][3], A[4][4], rhs[4], D; - REAL H[4], volume, radius2, minheightinv; - int indx[4]; - int i, j; - - // Set the matrix A = [vda, vdb, vdc]^T. - for (i = 0; i < 3; i++) A[0][i] = vda[i] = pa[i] - pd[i]; - for (i = 0; i < 3; i++) A[1][i] = vdb[i] = pb[i] - pd[i]; - for (i = 0; i < 3; i++) A[2][i] = vdc[i] = pc[i] - pd[i]; - // Lu-decompose the matrix A. - lu_decmp(A, 3, indx, &D, 0); - // Get the volume of abcd. - volume = (A[indx[0]][0] * A[indx[1]][1] * A[indx[2]][2]) / 6.0; - // Check if it is zero. - if (volume == 0.0) return 1.0e+200; // A degenerate tet. - // if (volume < 0.0) volume = -volume; - // Check the radiu-edge ratio of the tet. - rhs[0] = 0.5 * dot(vda, vda); - rhs[1] = 0.5 * dot(vdb, vdb); - rhs[2] = 0.5 * dot(vdc, vdc); - lu_solve(A, 3, indx, rhs, 0); - // Get the circumcenter. - // for (i = 0; i < 3; i++) circumcent[i] = pd[i] + rhs[i]; - // Get the square of the circumradius. - radius2 = dot(rhs, rhs); - - // Compute the 4 face normals (N[0], ..., N[3]). - for (j = 0; j < 3; j++) { - for (i = 0; i < 3; i++) rhs[i] = 0.0; - rhs[j] = 1.0; // Positive means the inside direction - lu_solve(A, 3, indx, rhs, 0); - for (i = 0; i < 3; i++) N[j][i] = rhs[i]; - } - // Get the fourth normal by summing up the first three. - for (i = 0; i < 3; i++) N[3][i] = - N[0][i] - N[1][i] - N[2][i]; - // Normalized the normals. - for (i = 0; i < 4; i++) { - // H[i] is the inverse of the height of its corresponding face. - H[i] = sqrt(dot(N[i], N[i])); - // if (H[i] > 0.0) { - // for (j = 0; j < 3; j++) N[i][j] /= H[i]; - // } - } - // Get the radius of the inscribed sphere. - // insradius = 1.0 / (H[0] + H[1] + H[2] + H[3]); - // Get the biggest H[i] (corresponding to the smallest height). - minheightinv = H[0]; - for (i = 1; i < 3; i++) { - if (H[i] > minheightinv) minheightinv = H[i]; - } - - return sqrt(radius2) * minheightinv; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// circumsphere() Calculate the smallest circumsphere (center and radius) // -// of the given three or four points. // -// // -// The circumsphere of four points (a tetrahedron) is unique if they are not // -// degenerate. If 'pd = NULL', the smallest circumsphere of three points is // -// the diametral sphere of the triangle if they are not degenerate. // -// // -// Return TRUE if the input points are not degenerate and the circumcenter // -// and circumradius are returned in 'cent' and 'radius' respectively if they // -// are not NULLs. Otherwise, return FALSE, the four points are co-planar. // -// // -/////////////////////////////////////////////////////////////////////////////// - -bool tetgenmesh::circumsphere(REAL* pa, REAL* pb, REAL* pc, REAL* pd, - REAL* cent, REAL* radius) -{ - REAL A[4][4], rhs[4], D; - int indx[4]; - - // Compute the coefficient matrix A (3x3). - A[0][0] = pb[0] - pa[0]; - A[0][1] = pb[1] - pa[1]; - A[0][2] = pb[2] - pa[2]; - A[1][0] = pc[0] - pa[0]; - A[1][1] = pc[1] - pa[1]; - A[1][2] = pc[2] - pa[2]; - if (pd != NULL) { - A[2][0] = pd[0] - pa[0]; - A[2][1] = pd[1] - pa[1]; - A[2][2] = pd[2] - pa[2]; - } else { - cross(A[0], A[1], A[2]); + // Compute the coefficient matrix A (3x3). + A[0][0] = pb[0] - pa[0]; + A[0][1] = pb[1] - pa[1]; + A[0][2] = pb[2] - pa[2]; + A[1][0] = pc[0] - pa[0]; + A[1][1] = pc[1] - pa[1]; + A[1][2] = pc[2] - pa[2]; + if (pd != NULL) { + A[2][0] = pd[0] - pa[0]; + A[2][1] = pd[1] - pa[1]; + A[2][2] = pd[2] - pa[2]; + } else { + cross(A[0], A[1], A[2]); } // Compute the right hand side vector b (3x1). @@ -6714,15 +6774,15 @@ bool tetgenmesh::circumsphere(REAL* pa, REAL* pb, REAL* pc, REAL* pd, return true; } -/////////////////////////////////////////////////////////////////////////////// -// // -// orthosphere() Calulcate the orthosphere of four weighted points. // -// // -// A weighted point (p, P^2) can be interpreted as a sphere centered at the // -// point 'p' with a radius 'P'. The 'height' of 'p' is pheight = p[0]^2 + // -// p[1]^2 + p[2]^2 - P^2. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// orthosphere() Calulcate the orthosphere of four weighted points. // +// // +// A weighted point (p, P^2) can be interpreted as a sphere centered at the // +// point 'p' with a radius 'P'. The 'height' of 'p' is pheight = p[0]^2 + // +// p[1]^2 + p[2]^2 - P^2. // +// // +//============================================================================// bool tetgenmesh::orthosphere(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL aheight, REAL bheight, REAL cheight, @@ -6768,63 +6828,77 @@ bool tetgenmesh::orthosphere(REAL* pa, REAL* pb, REAL* pc, REAL* pd, return true; } -/////////////////////////////////////////////////////////////////////////////// -// // -// planelineint() Calculate the intersection of a line and a plane. // -// // -// The equation of a plane (points P are on the plane with normal N and P3 // -// on the plane) can be written as: N dot (P - P3) = 0. The equation of the // -// line (points P on the line passing through P1 and P2) can be written as: // -// P = P1 + u (P2 - P1). The intersection of these two occurs when: // -// N dot (P1 + u (P2 - P1)) = N dot P3. // -// Solving for u gives: // -// N dot (P3 - P1) // -// u = ------------------. // -// N dot (P2 - P1) // -// If the denominator is 0 then N (the normal to the plane) is perpendicular // -// to the line. Thus the line is either parallel to the plane and there are // -// no solutions or the line is on the plane in which case there are an infi- // -// nite number of solutions. // -// // -// The plane is given by three points pa, pb, and pc, e1 and e2 defines the // -// line. If u is non-zero, The intersection point (if exists) returns in ip. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// planelineint() Calculate the intersection of a line and a plane. // +// // +// The equation of a plane (points P are on the plane with normal N and P3 // +// on the plane) can be written as: N dot (P - P3) = 0. The equation of the // +// line (points P on the line passing through P1 and P2) can be written as: // +// P = P1 + u (P2 - P1). The intersection of these two occurs when: // +// N dot (P1 + u (P2 - P1)) = N dot P3. // +// Solving for u gives: // +// N dot (P3 - P1) // +// u = ------------------. // +// N dot (P2 - P1) // +// If the denominator is 0 then N (the normal to the plane) is perpendicular // +// to the line. Thus the line is either parallel to the plane and there are // +// no solutions or the line is on the plane in which case there are an infi- // +// nite number of solutions. // +// // +// The plane is given by three points pa, pb, and pc, e1 and e2 defines the // +// line. If u is non-zero, The intersection point (if exists) returns in ip. // +// // +//============================================================================// void tetgenmesh::planelineint(REAL* pa, REAL* pb, REAL* pc, REAL* e1, REAL* e2, REAL* ip, REAL* u) { - REAL n[3], det, det1; + REAL *U = e1, *V = e2; + REAL Vuv[3]; // vector U->V + + Vuv[0] = V[0] - U[0]; + Vuv[1] = V[1] - U[1]; + Vuv[2] = V[2] - U[2]; + + REAL A[4], B[4], C[4], D[4], O[4]; + + A[0] = pa[0]; A[1] = pb[0]; A[2] = pc[0]; A[3] = -Vuv[0]; + B[0] = pa[1]; B[1] = pb[1]; B[2] = pc[1]; B[3] = -Vuv[1]; + C[0] = pa[2]; C[1] = pb[2]; C[2] = pc[2]; C[3] = -Vuv[2]; + D[0] = 1.; D[1] = 1.; D[2] = 1.; D[3] = 0.; + O[0] = 0.; O[1] = 0.; O[2] = 0.; O[3] = 0.; + + REAL det, det1; + + det = orient4dexact(A, B, C, D, O, A[3], B[3], C[3], D[3], O[3]); - // Calculate N. - facenormal(pa, pb, pc, n, 1, NULL); - // Calculate N dot (e2 - e1). - det = n[0] * (e2[0] - e1[0]) + n[1] * (e2[1] - e1[1]) - + n[2] * (e2[2] - e1[2]); if (det != 0.0) { - // Calculate N dot (pa - e1) - det1 = n[0] * (pa[0] - e1[0]) + n[1] * (pa[1] - e1[1]) - + n[2] * (pa[2] - e1[2]); + det1 = orient3dexact(pa, pb, pc, U); + *u = det1 / det; - ip[0] = e1[0] + *u * (e2[0] - e1[0]); - ip[1] = e1[1] + *u * (e2[1] - e1[1]); - ip[2] = e1[2] + *u * (e2[2] - e1[2]); + + ip[0] = U[0] + *u * Vuv[0]; // (V[0] - U[0]); + ip[1] = U[1] + *u * Vuv[1]; // (V[1] - U[1]); + ip[2] = U[2] + *u * Vuv[2]; // (V[2] - U[2]); } else { *u = 0.0; + ip[0] = ip[1] = ip[2] = 0.; } + } -/////////////////////////////////////////////////////////////////////////////// -// // -// linelineint() Calculate the intersection(s) of two line segments. // -// // -// Calculate the line segment [P, Q] that is the shortest route between two // -// lines from A to B and C to D. Calculate also the values of tp and tq // -// where: P = A + tp (B - A), and Q = C + tq (D - C). // -// // -// Return 1 if the line segment exists. Otherwise, return 0. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// linelineint() Calculate the intersection(s) of two line segments. // +// // +// Calculate the line segment [P, Q] that is the shortest route between two // +// lines from A to B and C to D. Calculate also the values of tp and tq // +// where: P = A + tp (B - A), and Q = C + tq (D - C). // +// // +// Return 1 if the line segment exists. Otherwise, return 0. // +// // +//============================================================================// int tetgenmesh::linelineint(REAL* A, REAL* B, REAL* C, REAL* D, REAL* P, REAL* Q, REAL* tp, REAL* tq) @@ -6864,26 +6938,26 @@ int tetgenmesh::linelineint(REAL* A, REAL* B, REAL* C, REAL* D, REAL* P, return 1; } -/////////////////////////////////////////////////////////////////////////////// -// // -// tetprismvol() Calculate the volume of a tetrahedral prism in 4D. // -// // -// A tetrahedral prism is a convex uniform polychoron (four dimensional poly-// -// tope). It has 6 polyhedral cells: 2 tetrahedra connected by 4 triangular // -// prisms. It has 14 faces: 8 triangular and 6 square. It has 16 edges and 8 // -// vertices. (Wikipedia). // -// // -// Let 'p0', ..., 'p3' be four affinely independent points in R^3. They form // -// the lower tetrahedral facet of the prism. The top tetrahedral facet is // -// formed by four vertices, 'p4', ..., 'p7' in R^4, which is obtained by // -// lifting each vertex of the lower facet into R^4 by a weight (height). A // -// canonical choice of the weights is the square of Euclidean norm of of the // -// points (vectors). // -// // -// // -// The return value is (4!) 24 times of the volume of the tetrahedral prism. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// tetprismvol() Calculate the volume of a tetrahedral prism in 4D. // +// // +// A tetrahedral prism is a convex uniform polychoron (four dimensional poly- // +// tope). It has 6 polyhedral cells: 2 tetrahedra connected by 4 triangular // +// prisms. It has 14 faces: 8 triangular and 6 square. It has 16 edges and 8 // +// vertices. (Wikipedia). // +// // +// Let 'p0', ..., 'p3' be four affinely independent points in R^3. They form // +// the lower tetrahedral facet of the prism. The top tetrahedral facet is // +// formed by four vertices, 'p4', ..., 'p7' in R^4, which is obtained by // +// lifting each vertex of the lower facet into R^4 by a weight (height). A // +// canonical choice of the weights is the square of Euclidean norm of of the // +// points (vectors). // +// // +// // +// The return value is (4!) 24 times of the volume of the tetrahedral prism. // +// // +//============================================================================// REAL tetgenmesh::tetprismvol(REAL* p0, REAL* p1, REAL* p2, REAL* p3) { @@ -6911,11 +6985,11 @@ REAL tetgenmesh::tetprismvol(REAL* p0, REAL* p1, REAL* p2, REAL* p3) return fabs(vol[0]) + fabs(vol[1]) + fabs(vol[2]) + fabs(vol[3]); } -/////////////////////////////////////////////////////////////////////////////// -// // -// calculateabovepoint() Calculate a point above a facet in 'dummypoint'. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// calculateabovepoint() Calculate a point above a facet in 'dummypoint'. // +// // +//============================================================================// bool tetgenmesh::calculateabovepoint(arraypool *facpoints, point *ppa, point *ppb, point *ppc) @@ -6999,13 +7073,13 @@ bool tetgenmesh::calculateabovepoint(arraypool *facpoints, point *ppa, return true; } -/////////////////////////////////////////////////////////////////////////////// -// // -// Calculate an above point. It lies above the plane containing the subface // -// [a,b,c], and save it in dummypoint. Moreover, the vector pa->dummypoint // -// is the normal of the plane. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// Calculate an above point. It lies above the plane containing the subface // +// [a,b,c], and save it in dummypoint. Moreover, the vector pa->dummypoint // +// is the normal of the plane. // +// // +//============================================================================// void tetgenmesh::calculateabovepoint4(point pa, point pb, point pc, point pd) { @@ -7024,7 +7098,6 @@ void tetgenmesh::calculateabovepoint4(point pa, point pb, point pc, point pd) norm = n2; len = len2; } - assert(len > 0); norm[0] /= len; norm[1] /= len; norm[2] /= len; @@ -7034,38 +7107,61 @@ void tetgenmesh::calculateabovepoint4(point pa, point pb, point pc, point pd) dummypoint[2] = pa[2] + len * norm[2]; } -//// //// -//// //// -//// geom_cxx ///////////////////////////////////////////////////////////////// -//// flip_cxx ///////////////////////////////////////////////////////////////// -//// //// -//// //// +// // +// // +//== geom_cxx ================================================================// -/////////////////////////////////////////////////////////////////////////////// -// // -// flip23() Perform a 2-to-3 flip (face-to-edge flip). // -// // +//== flip_cxx ================================================================// +// // +// // + +//============================================================================// +// // +// flippush() Push a face (possibly will be flipped) into flipstack. // +// // +// The face is marked. The flag is used to check the validity of the face on // +// its popup. Some other flips may change it already. // +// // +//============================================================================// + +void tetgenmesh::flippush(badface*& fstack, triface* flipface) +{ + if (!facemarked(*flipface)) { + badface *newflipface = (badface *) flippool->alloc(); + newflipface->tt = *flipface; + markface(newflipface->tt); + // Push this face into stack. + newflipface->nextitem = fstack; + fstack = newflipface; + } +} + +//============================================================================// +// // +// flip23() Perform a 2-to-3 flip (face-to-edge flip). // +// // // 'fliptets' is an array of three tets (handles), where the [0] and [1] are // -// [a,b,c,d] and [b,a,c,e]. The three new tets: [e,d,a,b], [e,d,b,c], and // -// [e,d,c,a] are returned in [0], [1], and [2] of 'fliptets'. As a result, // -// The face [a,b,c] is removed, and the edge [d,e] is created. // -// // -// If 'hullflag' > 0, hull tets may be involved in this flip, i.e., one of // -// the five vertices may be 'dummypoint'. There are two canonical cases: // -// (1) d is 'dummypoint', then all three new tets are hull tets. If e is // -// 'dummypoint', we reconfigure e to d, i.e., turn it up-side down. // -// (2) c is 'dummypoint', then two new tets: [e,d,b,c] and [e,d,c,a], are // -// hull tets. If a or b is 'dummypoint', we reconfigure it to c, i.e., // -// rotate the three input tets counterclockwisely (right-hand rule) // -// until a or b is in c's position. // -// // -// If 'fc->enqflag' is set, convex hull faces will be queued for flipping. // -// In particular, if 'fc->enqflag' is 1, it is called by incrementalflip() // -// after the insertion of a new point. It is assumed that 'd' is the new // -// point. IN this case, only link faces of 'd' are queued. // -// // -/////////////////////////////////////////////////////////////////////////////// +// [a,b,c,d] and [b,a,c,e]. The three new tets: [e,d,a,b], [e,d,b,c], and // +// [e,d,c,a] are returned in [0], [1], and [2] of 'fliptets'. As a result, // +// the face [a,b,c] is removed, and the edge [d,e] is created. // +// // +// If 'hullflag' > 0, hull tets may be involved in this flip, i.e., one of // +// the five vertices may be 'dummypoint'. There are two canonical cases: // +// (1) d is 'dummypoint', then all three new tets are hull tets. If e is // +// 'dummypoint', we reconfigure e to d, i.e., to turn it up-side down. // +// (2) c is 'dummypoint', then two new tets: [e,d,b,c] and [e,d,c,a], are // +// hull tets. If a (or b) is 'dummypoint', we reconfigure it to c, // +// i.e., to rotate the three tets counterclockwisely (right-hand rule) // +// until a (or b) is in c's position. // +// // +// If 'fc->enqflag > 0', faces on the convex hull of {a,b,c,d,e} will be // +// queued for flipping. // +// In particular, if 'fc->enqflag = 1', it is called by incrementalflip() // +// after the insertion of a new point. It is assumed that 'd' is the new // +// point. In this case, only link faces of 'd' are queued. // +// // +//============================================================================// void tetgenmesh::flip23(triface* fliptets, int hullflag, flipconstraints *fc) { @@ -7374,37 +7470,37 @@ void tetgenmesh::flip23(triface* fliptets, int hullflag, flipconstraints *fc) recenttet = fliptets[0]; } -/////////////////////////////////////////////////////////////////////////////// -// // -// flip32() Perform a 3-to-2 flip (edge-to-face flip). // -// // -// 'fliptets' is an array of three tets (handles), which are [e,d,a,b], // -// [e,d,b,c], and [e,d,c,a]. The two new tets: [a,b,c,d] and [b,a,c,e] are // -// returned in [0] and [1] of 'fliptets'. As a result, the edge [e,d] is // -// replaced by the face [a,b,c]. // -// // -// If 'hullflag' > 0, hull tets may be involved in this flip, i.e., one of // -// the five vertices may be 'dummypoint'. There are two canonical cases: // -// (1) d is 'dummypoint', then [a,b,c,d] is hull tet. If e is 'dummypoint',// -// we reconfigure e to d, i.e., turnover it. // -// (2) c is 'dummypoint' then both [a,b,c,d] and [b,a,c,e] are hull tets. // -// If a or b is 'dummypoint', we reconfigure it to c, i.e., rotate the // -// three old tets counterclockwisely (right-hand rule) until a or b // -// is in c's position. // -// // -// If 'fc->enqflag' is set, convex hull faces will be queued for flipping. // -// In particular, if 'fc->enqflag' is 1, it is called by incrementalflip() // -// after the insertion of a new point. It is assumed that 'a' is the new // -// point. In this case, only link faces of 'a' are queued. // -// // -// If 'checksubfaceflag' is on (global variable), and assume [e,d] is not a // -// segment. There may be two (interior) subfaces sharing at [e,d], which are // -// [e,d,p] and [e,d,q], where the pair (p,q) may be either (a,b), or (b,c), // -// or (c,a) In such case, a 2-to-2 flip is performed on these two subfaces // -// and two new subfaces [p,q,e] and [p,q,d] are created. They are inserted // -// back into the tetrahedralization. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// flip32() Perform a 3-to-2 flip (edge-to-face flip). // +// // +// 'fliptets' is an array of three tets (handles), which are [e,d,a,b], // +// [e,d,b,c], and [e,d,c,a]. The two new tets: [a,b,c,d] and [b,a,c,e] are // +// returned in [0] and [1] of 'fliptets'. As a result, the edge [e,d] is // +// replaced by the face [a,b,c]. // +// // +// If 'hullflag' > 0, hull tets may be involved in this flip, i.e., one of // +// the five vertices may be 'dummypoint'. There are two canonical cases: // +// (1) d is 'dummypoint', then [a,b,c,d] is hull tet. If e is 'dummypoint', // +// we reconfigure e to d, i.e., turnover it. // +// (2) c is 'dummypoint' then both [a,b,c,d] and [b,a,c,e] are hull tets. // +// If a or b is 'dummypoint', we reconfigure it to c, i.e., rotate the // +// three old tets counterclockwisely (right-hand rule) until a or b // +// is in c's position. // +// // +// If 'fc->enqflag' is set, convex hull faces will be queued for flipping. // +// In particular, if 'fc->enqflag' is 1, it is called by incrementalflip() // +// after the insertion of a new point. It is assumed that 'a' is the new // +// point. In this case, only link faces of 'a' are queued. // +// // +// If 'checksubfaceflag' is on (global variable), and assume [e,d] is not a // +// segment. There may be two (interior) subfaces sharing at [e,d], which are // +// [e,d,p] and [e,d,q], where the pair (p,q) may be either (a,b), or (b,c), // +// or (c,a) In such case, a 2-to-2 flip is performed on these two subfaces // +// and two new subfaces [p,q,e] and [p,q,d] are created. They are inserted // +// back into the tetrahedralization. // +// // +//============================================================================// void tetgenmesh::flip32(triface* fliptets, int hullflag, flipconstraints *fc) { @@ -7786,27 +7882,27 @@ void tetgenmesh::flip32(triface* fliptets, int hullflag, flipconstraints *fc) recenttet = fliptets[0]; } -/////////////////////////////////////////////////////////////////////////////// -// // -// flip41() Perform a 4-to-1 flip (Remove a vertex). // -// // -// 'fliptets' is an array of four tetrahedra in the star of the removing // -// vertex 'p'. Let the four vertices in the star of p be a, b, c, and d. The // -// four tets in 'fliptets' are: [p,d,a,b], [p,d,b,c], [p,d,c,a], and [a,b,c, // -// p]. On return, 'fliptets[0]' is the new tet [a,b,c,d]. // -// // -// If 'hullflag' is set (> 0), one of the five vertices may be 'dummypoint'. // -// The 'hullsize' may be changed. Note that p may be dummypoint. In this // -// case, four hull tets are replaced by one real tet. // -// // -// If 'checksubface' flag is set (>0), it is possible that there are three // -// interior subfaces connecting at p. If so, a 3-to-1 flip is performed to // -// to remove p from the surface triangulation. // -// // -// If it is called by the routine incrementalflip(), we assume that d is the // -// newly inserted vertex. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// flip41() Perform a 4-to-1 flip (Remove a vertex). // +// // +// 'fliptets' is an array of four tetrahedra in the star of the removing // +// vertex 'p'. Let the four vertices in the star of p be a, b, c, and d. The // +// four tets in 'fliptets' are: [p,d,a,b], [p,d,b,c], [p,d,c,a], and [a,b,c, // +// p]. On return, 'fliptets[0]' is the new tet [a,b,c,d]. // +// // +// If 'hullflag' is set (> 0), one of the five vertices may be 'dummypoint'. // +// The 'hullsize' may be changed. Note that p may be dummypoint. In this // +// case, four hull tets are replaced by one real tet. // +// // +// If 'checksubface' flag is set (>0), it is possible that there are three // +// interior subfaces connecting at p. If so, a 3-to-1 flip is performed to // +// to remove p from the surface triangulation. // +// // +// If it is called by the routine incrementalflip(), we assume that d is the // +// newly inserted vertex. // +// // +//============================================================================// void tetgenmesh::flip41(triface* fliptets, int hullflag, flipconstraints *fc) { @@ -7851,14 +7947,12 @@ void tetgenmesh::flip41(triface* fliptets, int hullflag, flipconstraints *fc) // There are three subfaces connecting at p. if (scount < 3) { // The new subface is one of {[a,b,d], [b,c,d], [c,a,d]}. - assert(scount == 1); // spivot >= 0 // Go to the tet containing the three subfaces. fsym(topcastets[spivot], neightet); // Get the three subfaces connecting at p. for (i = 0; i < 3; i++) { esym(neightet, newface); tspivot(newface, flipshs[i]); - assert(flipshs[i].sh != NULL); eprevself(neightet); } } else { @@ -8100,34 +8194,34 @@ void tetgenmesh::flip41(triface* fliptets, int hullflag, flipconstraints *fc) recenttet = fliptets[0]; } -/////////////////////////////////////////////////////////////////////////////// -// // -// flipnm() Flip an edge through a sequence of elementary flips. // -// // -// 'abtets' is an array of 'n' tets in the star of edge [a,b].These tets are // -// ordered in a counterclockwise cycle with respect to the vector a->b, i.e.,// -// use the right-hand rule. // -// // -// 'level' (>= 0) indicates the current link level. If 'level > 0', we are // -// flipping a link edge of an edge [a',b'], and 'abedgepivot' indicates // -// which link edge, i.e., [c',b'] or [a',c'], is [a,b] These two parameters // -// allow us to determine the new tets after a 3-to-2 flip, i.e., tets that // -// do not inside the reduced star of edge [a',b']. // -// // -// If the flag 'fc->unflip' is set, this routine un-does the flips performed // -// in flipnm([a,b]) so that the mesh is returned to its original state // -// before doing the flipnm([a,b]) operation. // -// // -// The return value is an integer nn, where nn <= n. If nn is 2, then the // -// edge is flipped. The first and the second tets in 'abtets' are new tets. // -// Otherwise, nn > 2, the edge is not flipped, and nn is the number of tets // -// in the current star of [a,b]. // -// // -// ASSUMPTIONS: // -// - Neither a nor b is 'dummypoint'. // -// - [a,b] must not be a segment. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// flipnm() Flip an edge through a sequence of elementary flips. // +// // +// 'abtets' is an array of 'n' tets in the star of edge [a,b].These tets are // +// ordered in a counterclockwise cycle with respect to the vector a->b, i.e., // +// use the right-hand rule. // +// // +// 'level' (>= 0) indicates the current link level. If 'level > 0', we are // +// flipping a link edge of an edge [a',b'], and 'abedgepivot' indicates // +// which link edge, i.e., [c',b'] or [a',c'], is [a,b] These two parameters // +// allow us to determine the new tets after a 3-to-2 flip, i.e., tets that // +// do not inside the reduced star of edge [a',b']. // +// // +// If the flag 'fc->unflip' is set, this routine un-does the flips performed // +// in flipnm([a,b]) so that the mesh is returned to its original state // +// before doing the flipnm([a,b]) operation. // +// // +// The return value is an integer nn, where nn <= n. If nn is 2, then the // +// edge is flipped. The first and the second tets in 'abtets' are new tets. // +// Otherwise, nn > 2, the edge is not flipped, and nn is the number of tets // +// in the current star of [a,b]. // +// // +// ASSUMPTIONS: // +// - Neither a nor b is 'dummypoint'. // +// - [a,b] must not be a segment. // +// // +//============================================================================// int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, flipconstraints* fc) @@ -8212,7 +8306,6 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, // locally non-convex (at hull faces [a,b,e] and [b,a,d]). // In this case, an edge flip [a,b] to [e,d] is still possible. pf = apex(abtets[(i + 2) % n]); - assert(pf != dummypoint); ori = orient3d(pd, pe, pf, pa); if (ori < 0) { ori = orient3d(pe, pd, pf, pb); @@ -8236,6 +8329,15 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, } } + + + if (reducflag) { + triface checktet = abtets[i]; + if (!valid_constrained_f23(checktet, pd, pe)) { + reducflag = 0; + } + } + if (reducflag) { // [a,b,c] could be removed by a 2-to-3 flip. rejflag = 0; @@ -8276,9 +8378,8 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, // The last entry 'abtets[n-1]' is empty. It is used in two ways: // (i) it remembers the vertex 'c' (in 'abtets[n-1].tet'), and // (ii) it remembers the position [i] where this flip took place. - // These informations let us to either undo this flip or recover + // These information let us to either undo this flip or recover // the original edge link (for collecting new created tets). - //abtets[n - 1] = fliptets[1]; // [e,d,b,c] is remembered. abtets[n - 1].tet = (tetrahedron *) pc; abtets[n - 1].ver = 0; // Clear it. // 'abtets[n - 1].ver' is in range [0,11] -- only uses 4 bits. @@ -8315,7 +8416,6 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, edestoppoself(fliptets[0]); // [e,d,a,b] fnext(fliptets[0], fliptets[1]); // [1] is [e,d,b,c] fnext(fliptets[1], fliptets[2]); // [2] is [e,d,c,a] - assert(apex(fliptets[0]) == oppo(fliptets[2])); // SELF_CHECK // Restore the two original tets in Star(ab). flip32(fliptets, hullflag, fc); // Marktest the two restored tets in Star(ab). @@ -8415,22 +8515,32 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, // Try to flip the selected edge ([c,b] or [a,c]). esymself(flipedge); // Count the number of tets at the edge. + int subface_count = 0; n1 = 0; j = 0; // Sum of the star counters. spintet = flipedge; while (1) { + if (issubface(spintet)) subface_count++; n1++; j += (elemcounter(spintet)); fnextself(spintet); if (spintet.tet == flipedge.tet) break; } - assert(n1 >= 3); + if (n1 < 3) { + // This is only possible when the mesh contains inverted + // elements. Reprot a bug. + terminatetetgen(this, 2); + } if (j > 2) { // The Star(flipedge) overlaps other Stars. continue; // Do not flip this edge. } - // Only two tets can be marktested. - assert(j == 2); + + if (fc->noflip_in_surface) { + if (subface_count > 0) { + continue; + } + } if ((b->flipstarsize > 0) && (n1 > b->flipstarsize)) { // The star size exceeds the given limit. @@ -8440,15 +8550,12 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, // Allocate spaces for Star(flipedge). tmpabtets = new triface[n1]; // Form the Star(flipedge). - j = 0; spintet = flipedge; - while (1) { + for (j = 0; j < n1; j++) { tmpabtets[j] = spintet; // Increase the star counter of this tet. - increaseelemcounter(tmpabtets[j]); - j++; + increaseelemcounter(tmpabtets[j]); fnextself(spintet); - if (spintet.tet == flipedge.tet) break; } // Try to flip the selected edge away. @@ -8470,7 +8577,6 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, esymself(spintet); eprevself(spintet); // [a,b,e,d] } // edgepivot == 2 - assert(elemcounter(spintet) == 0); // It's a new tet. increaseelemcounter(spintet); // It is in Star(ab). // Put the new tet at [i-1]-th entry. abtets[(i - 1 + n) % n] = spintet; @@ -8505,7 +8611,6 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, // The edge is not flipped. if (fc->unflip) { // Recover the flipped edge ([c,b] or [a,c]). - assert(nn == (n - 1)); // The sequence of flips are saved in 'tmpabtets'. // abtets[(i-1) % (n-1)] is [a,b,e,d], i.e., the tet created by // the flipping of edge [c,b] or [a,c].It must still exist in @@ -8575,16 +8680,12 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, // Continue the search for flips. } else { // The selected edge is not flipped. - if (fc->unflip) { - // The memory should already be freed. - assert(nn == n1); - } else { + if (!fc->unflip) { // Release the memory used in this attempted flip. flipnm_post(tmpabtets, n1, nn, edgepivot, fc); } // Decrease the star counters of tets in Star(flipedge). for (j = 0; j < nn; j++) { - assert(elemcounter(tmpabtets[j]) > 0); // SELF_CHECK decreaseelemcounter(tmpabtets[j]); } // Release the allocated spaces. @@ -8661,7 +8762,6 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, if (chkpt == pb) break; if ((chkpt != dummypoint) && (apex(spintet) != dummypoint)) { ori = -orient3d(pd, pc, apex(spintet), chkpt); - assert(ori > 0); if (ori > bigvol) { bigvol = ori; searchpt = chkpt; @@ -8704,7 +8804,6 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, edgepivot = j; } } - assert(nn < 3); if (nn == 1) { // Found only 1 subface containing this edge. This can happen in // the boundary recovery phase. The neighbor subface is not yet @@ -8722,8 +8821,18 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, rejflag = 1; // Conflict to a 2-to-2 flip. } } + } else if (nn == 3) { + // Report a bug. + terminatetetgen(this, 2); + } + } + + if (!rejflag) { + if (!valid_constrained_f32(abtets, pa, pb)) { + rejflag = 1; } } + if (!rejflag && fc->checkflipeligibility) { // Here we must exchange 'a' and 'b'. Since in the check... function, // we assume the following point sequence, 'a,b,c,d,e', where @@ -8765,8 +8874,7 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, cavetetlist->newindex((void **) &parytet); if (abedgepivot == 1) { // [c,b] *parytet = abtets[1]; - } else { - assert(abedgepivot == 2); // [a,c] + } else { *parytet = abtets[0]; } } @@ -8780,35 +8888,35 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, return n; } -/////////////////////////////////////////////////////////////////////////////// -// // -// flipnm_post() Post process a n-to-m flip. // -// // -// IMPORTANT: This routine only works when there is no other flip operation // -// is done after flipnm([a,b]) which attempts to remove an edge [a,b]. // -// // -// 'abtets' is an array of 'n' (>= 3) tets which are in the original star of // -// [a,b] before flipnm([a,b]). 'nn' (< n) is the value returned by flipnm. // -// If 'nn == 2', the edge [a,b] has been flipped. 'abtets[0]' and 'abtets[1]'// -// are [c,d,e,b] and [d,c,e,a], i.e., a 2-to-3 flip can recover the edge [a, // -// b] and its initial Star([a,b]). If 'nn >= 3' edge [a,b] still exists in // -// current mesh and 'nn' is the current number of tets in Star([a,b]). // -// // -// Each 'abtets[i]', where nn <= i < n, saves either a 2-to-3 flip or a // -// flipnm([p1,p2]) operation ([p1,p2] != [a,b]) which created the tet // -// 'abtets[t-1]', where '0 <= t <= i'. These information can be used to // -// undo the flips performed in flipnm([a,b]) or to collect new tets created // -// by the flipnm([a,b]) operation. // -// // -// Default, this routine only walks through the flips and frees the spaces // -// allocated during the flipnm([a,b]) operation. // -// // -// If the flag 'fc->unflip' is set, this routine un-does the flips performed // -// in flipnm([a,b]) so that the mesh is returned to its original state // -// before doing the flipnm([a,b]) operation. // -// // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// flipnm_post() Post process a n-to-m flip. // +// // +// IMPORTANT: This routine only works when there is no other flip operation // +// is done after flipnm([a,b]) which attempts to remove an edge [a,b]. // +// // +// 'abtets' is an array of 'n' (>= 3) tets which are in the original star of // +// [a,b] before flipnm([a,b]). 'nn' (< n) is the value returned by flipnm. // +// If 'nn == 2', the edge [a,b] has been flipped. 'abtets[0]' and 'abtets[1]' // +// are [c,d,e,b] and [d,c,e,a], i.e., a 2-to-3 flip can recover the edge [a, // +// b] and its initial Star([a,b]). If 'nn >= 3' edge [a,b] still exists in // +// current mesh and 'nn' is the current number of tets in Star([a,b]). // +// // +// Each 'abtets[i]', where nn <= i < n, saves either a 2-to-3 flip or a // +// flipnm([p1,p2]) operation ([p1,p2] != [a,b]) which created the tet // +// 'abtets[t-1]', where '0 <= t <= i'. These information can be used to // +// undo the flips performed in flipnm([a,b]) or to collect new tets created // +// by the flipnm([a,b]) operation. // +// // +// Default, this routine only walks through the flips and frees the spaces // +// allocated during the flipnm([a,b]) operation. // +// // +// If the flag 'fc->unflip' is set, this routine un-does the flips performed // +// in flipnm([a,b]) so that the mesh is returned to its original state // +// before doing the flipnm([a,b]) operation. // +// // +// // +//============================================================================// int tetgenmesh::flipnm_post(triface* abtets, int n, int nn, int abedgepivot, flipconstraints* fc) @@ -8852,9 +8960,8 @@ int tetgenmesh::flipnm_post(triface* abtets, int n, int nn, int abedgepivot, if (fliptype == 1) { // It was a 2-to-3 flip: [a,b,c]->[e,d]. t = (abtets[i].ver >> 6); - assert(t <= i); if (fc->unflip) { - if (b->verbose > 2) { + if (b->verbose > 3) { printf(" Recover a 2-to-3 flip at f[%d].\n", t); } // 'abtets[(t-1)%i]' is the tet [a,b,e,d] in current Star(ab), i.e., @@ -8888,9 +8995,8 @@ int tetgenmesh::flipnm_post(triface* abtets, int n, int nn, int abedgepivot, n1 = ((abtets[i].ver >> 19) & 8191); // \sum_{i=0^12}{2^i} = 8191 edgepivot = (abtets[i].ver & 3); t = ((abtets[i].ver >> 6) & 8191); - assert(t <= i); if (fc->unflip) { - if (b->verbose > 2) { + if (b->verbose > 3) { printf(" Recover a %d-to-m flip at e[%d] of f[%d].\n", n1, edgepivot, t); } @@ -8950,7 +9056,7 @@ int tetgenmesh::flipnm_post(triface* abtets, int n, int nn, int abedgepivot, // Only free the spaces. flipnm_post(tmpabtets, n1, 2, edgepivot, fc); } // if (!unflip) - if (b->verbose > 2) { + if (b->verbose > 3) { printf(" Release %d spaces at f[%d].\n", n1, i); } delete [] tmpabtets; @@ -8960,20 +9066,20 @@ int tetgenmesh::flipnm_post(triface* abtets, int n, int nn, int abedgepivot, return 1; } -/////////////////////////////////////////////////////////////////////////////// -// // -// insertpoint() Insert a point into current tetrahedralization. // -// // -// The Bowyer-Watson (B-W) algorithm is used to add a new point p into the // -// tetrahedralization T. It first finds a "cavity", denoted as C, in T, C // -// consists of tetrahedra in T that "conflict" with p. If T is a Delaunay // -// tetrahedralization, then all boundary faces (triangles) of C are visible // -// by p, i.e.,C is star-shaped. We can insert p into T by first deleting all // -// tetrahedra in C, then creating new tetrahedra formed by boundary faces of // -// C and p. If T is not a DT, then C may be not star-shaped. It must be // -// modified so that it becomes star-shaped. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// insertpoint() Insert a point into current tetrahedralization. // +// // +// The Bowyer-Watson (B-W) algorithm is used to add a new point p into the // +// tetrahedralization T. It first finds a "cavity", denoted as C, in T, C // +// consists of tetrahedra in T that "conflict" with p. If T is a Delaunay // +// tetrahedralization, then all boundary faces (triangles) of C are visible // +// by p, i.e.,C is star-shaped. We can insert p into T by first deleting all // +// tetrahedra in C, then creating new tetrahedra formed by boundary faces of // +// C and p. If T is not a DT, then C may be not star-shaped. It must be // +// modified so that it becomes star-shaped. // +// // +//============================================================================// int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, face *splitseg, insertvertexflags *ivf) @@ -9019,14 +9125,11 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, if (loc != OUTSIDE) { // Check if this vertex is regular. pts = (point *) searchtet->tet; - assert(pts[7] != dummypoint); sign = orient4d_s(pts[4], pts[5], pts[6], pts[7], insertpt, pts[4][3], pts[5][3], pts[6][3], pts[7][3], insertpt[3]); if (sign > 0) { - // This new vertex does not lie below the lower hull. Skip it. - setpointtype(insertpt, NREGULARVERTEX); - nonregularcount++; + // This new vertex lies above the lower hull. Do not insert it. ivf->iloc = (int) NONREGULAR; return 0; } @@ -9154,25 +9257,150 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, } else if (loc == INSTAR) { // We assume that all tets in the star are given in 'caveoldtetlist', // and they are all infected. - assert(caveoldtetlist->objects > 0); - // Collect the boundary faces of the star. - for (i = 0; i < caveoldtetlist->objects; i++) { - cavetet = (triface *) fastlookup(caveoldtetlist, i); - // Check its 4 neighbor tets. - for (j = 0; j < 4; j++) { - decode(cavetet->tet[j], neightet); - if (!infected(neightet)) { - // It's a boundary face. - neightet.ver = epivot[neightet.ver]; - cavebdrylist->newindex((void **) &parytet); - *parytet = neightet; + if (cavebdrylist->objects == 0) { + // Collect the boundary faces of the star. + for (i = 0; i < caveoldtetlist->objects; i++) { + cavetet = (triface *) fastlookup(caveoldtetlist, i); + // Check its 4 neighbor tets. + for (j = 0; j < 4; j++) { + decode(cavetet->tet[j], neightet); + if (!infected(neightet)) { + // It's a boundary face. + neightet.ver = epivot[neightet.ver]; + cavebdrylist->newindex((void **) &parytet); + *parytet = neightet; + } } } - } + } // if (cavebdrylist->objects == 0) } else if (loc == ONVERTEX) { // The point already exist. Do nothing and return. return 0; - } + } else if (loc == ENCSUBFACE) { + ivf->iloc = (int) ENCSUBFACE; + return 0; + } else { + // Unknown case + terminatetetgen(this, 2); + } + + if (ivf->collect_inial_cavity_flag) { + tetrahedron **ptptr; + for (i = 0; i < caveoldtetlist->objects; i++) { + cavetet = (triface *) fastlookup(caveoldtetlist, i); + cave_oldtet_list->newindex((void **) &ptptr); + *ptptr = cavetet->tet; + } + // Do not insert this point. + insertpoint_abort(splitseg, ivf); + // ivf->iloc = NULLCAVITY; + return 0; + } // if (ivf->collect_inial_cavity_flag) + + if ((b->plc || b->quality) && (loc != INSTAR)) { + // Reject the new point if it lies too close to an existing point (b->plc), + // or it lies inside a protecting ball of near vertex (ivf->rejflag & 4). + // Collect the list of vertices of the initial cavity. + if (loc == OUTSIDE) { + pts = (point *) &(searchtet->tet[4]); + for (i = 0; i < 3; i++) { + cavetetvertlist->newindex((void **) &parypt); + *parypt = pts[i]; + } + } else if (loc == INTETRAHEDRON) { + pts = (point *) &(searchtet->tet[4]); + for (i = 0; i < 4; i++) { + cavetetvertlist->newindex((void **) &parypt); + *parypt = pts[i]; + } + } else if (loc == ONFACE) { + pts = (point *) &(searchtet->tet[4]); + for (i = 0; i < 3; i++) { + cavetetvertlist->newindex((void **) &parypt); + *parypt = pts[i]; + } + if (pts[3] != dummypoint) { + cavetetvertlist->newindex((void **) &parypt); + *parypt = pts[3]; + } + fsym(*searchtet, spintet); + if (oppo(spintet) != dummypoint) { + cavetetvertlist->newindex((void **) &parypt); + *parypt = oppo(spintet); + } + } else if (loc == ONEDGE) { + spintet = *searchtet; + cavetetvertlist->newindex((void **) &parypt); + *parypt = org(spintet); + cavetetvertlist->newindex((void **) &parypt); + *parypt = dest(spintet); + while (1) { + if (apex(spintet) != dummypoint) { + cavetetvertlist->newindex((void **) &parypt); + *parypt = apex(spintet); + } + fnextself(spintet); + if (spintet.tet == searchtet->tet) break; + } + } + + int rejptflag = (ivf->rejflag & 4); + REAL rd, ins_radius; + pts = NULL; + + for (i = 0; i < cavetetvertlist->objects; i++) { + parypt = (point *) fastlookup(cavetetvertlist, i); + rd = distance(*parypt, insertpt); + // Is the point very close to an existing point? + if (rd < minedgelength) { + if ((!create_a_shorter_edge(insertpt, *parypt)) && + (!ivf->ignore_near_vertex)) { + pts = parypt; + loc = NEARVERTEX; + break; + } + } + if (ivf->check_insert_radius) { //if (useinsertradius) { + ins_radius = getpointinsradius(*parypt); + if (ins_radius > 0.0) { + if (rd < ins_radius) { + if (!create_a_shorter_edge(insertpt, *parypt)) { + // Reject the isnertion of this vertex. + pts = parypt; + loc = ENCVERTEX; + break; + } + } + } + } + if (rejptflag) { + // Is the point encroaches upon an existing point? + if (rd < (0.5 * (*parypt)[pointmtrindex])) { + pts = parypt; + loc = ENCVERTEX; + break; + } + } + } + cavetetvertlist->restart(); // Clear the work list. + + if (pts != NULL) { + // The point is either too close to an existing vertex (NEARVERTEX) + // or encroaches upon (inside the protecting ball) of that vertex. + if (loc == NEARVERTEX) { + point2tetorg(*pts, *searchtet); + insertpoint_abort(splitseg, ivf); + ivf->iloc = (int) loc; + return 0; + } else { // loc == ENCVERTEX + // The point lies inside the protection ball. + point2tetorg(*pts, *searchtet); + insertpoint_abort(splitseg, ivf); + ivf->iloc = (int) loc; + return 0; + } + } + } // if ((b->plc || b->quality) && (loc != INSTAR)) if (ivf->assignmeshsize) { @@ -9180,7 +9408,7 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, if (bgm != NULL) { // Interpolate the mesh size from the background mesh. bgm->decode(point2bgmtet(org(*searchtet)), neightet); - int bgmloc = (int) bgm->scoutpoint(insertpt, &neightet, 0); + int bgmloc = (int) bgm->scout_point(insertpt, &neightet, 0); if (bgmloc != (int) OUTSIDE) { insertpt[pointmtrindex] = bgm->getpointmeshsize(insertpt, &neightet, bgmloc); @@ -9224,10 +9452,8 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, ori = orient3d(pts[4], pts[5], pts[6], insertpt); if (ori < 0) { // A visible hull face. - //if (!nonconvex) { // Include it in the cavity. The convex hull will be enlarged. - enqflag = true; // (ori < 0.0); - //} + enqflag = true; } else if (ori == 0.0) { // A coplanar hull face. We need to test if this hull face is // Delaunay or not. We test if the adjacent tet (not faked) @@ -9237,7 +9463,6 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, if (!marktested(neineitet)) { // Do Delaunay test on this tet. pts = (point *) neineitet.tet; - assert(pts[7] != dummypoint); if (b->weighted) { sign = orient4d_s(pts[4],pts[5],pts[6],pts[7], insertpt, pts[4][3], pts[5][3], pts[6][3], @@ -9264,7 +9489,6 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, if (!marktested(neineitet)) { // Do Delaunay test on this tet. pts = (point *) neineitet.tet; - assert(pts[7] != dummypoint); if (b->weighted) { sign = orient4d_s(pts[4],pts[5],pts[6],pts[7], insertpt, pts[4][3], pts[5][3], pts[6][3], @@ -9307,6 +9531,18 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, cavetetlist->restart(); // Clear the working list. } // if (ivf->bowywat) + if (ivf->refineflag > 0) { + // The new point is inserted by Delaunay refinement, i.e., it is the + // circumcenter of a tetrahedron, or a subface, or a segment. + // Do not insert this point if the tetrahedron, or subface, or segment + // is not inside the final cavity. + if (((ivf->refineflag == 1) && !infected(ivf->refinetet))) { + insertpoint_abort(splitseg, ivf); + ivf->iloc = (int) BADELEMENT; + return 0; + } + } // if (ivf->refineflag) + if (checksubsegflag) { // Collect all segments of C(p). shellface *ssptr; @@ -9336,13 +9572,17 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, face *paryseg1; for (i = 0; i < cavetetseglist->objects; i++) { paryseg1 = (face *) fastlookup(cavetetseglist, i); - if (checkseg4encroach((point) paryseg1->sh[3], (point) paryseg1->sh[4], - insertpt)) { - encseglist->newindex((void **) &paryseg); - *paryseg = *paryseg1; + point *ppt = (point *) &(paryseg1->sh[3]); + if (check_encroachment(ppt[0], ppt[1], insertpt)) { + badface *bf = NULL; + encseglist->newindex((void **) &bf); + bf->init(); + bf->ss = *paryseg1; + bf->forg = sorg(bf->ss); + bf->fdest = sdest(bf->ss); } } // i - if (encseglist->objects > 0) { + if ((ivf->rejflag & 1) && (encseglist->objects > 0)) { insertpoint_abort(splitseg, ivf); ivf->iloc = (int) ENCSEGMENT; return 0; @@ -9375,18 +9615,24 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, } if (ivf->rejflag & 2) { - REAL rd, cent[3]; + REAL ccent[3], radius; badface *bface; // Reject this point if it encroaches upon any subface. for (i = 0; i < cavetetshlist->objects; i++) { parysh = (face *) fastlookup(cavetetshlist, i); - if (checkfac4encroach((point) parysh->sh[3], (point) parysh->sh[4], - (point) parysh->sh[5], insertpt, cent, &rd)) { - encshlist->newindex((void **) &bface); - bface->ss = *parysh; - bface->forg = (point) parysh->sh[3]; // Not a dad one. - for (j = 0; j < 3; j++) bface->cent[j] = cent[j]; - bface->key = rd; + if (get_subface_ccent(parysh, ccent)) { + point encpt = insertpt; + if (check_enc_subface(parysh, &encpt, ccent, &radius)) { + encshlist->newindex((void **) &bface); + bface->ss = *parysh; + bface->forg = sorg(*parysh); + bface->fdest = sdest(*parysh); + bface->fapex = sapex(*parysh); + bface->noppo = NULL; // no existing encroaching vertex. + for (j = 0; j < 3; j++) bface->cent[j] = ccent[j]; + for (j = 3; j < 6; j++) bface->cent[j] = 0.; + bface->key = radius; + } } } if (encshlist->objects > 0) { @@ -9411,12 +9657,10 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, // subfaces which are included in C(p). Do not across a segment. for (i = 0; i < caveshlist->objects; i++) { parysh = (face *) fastlookup(caveshlist, i); - assert(smarktested(*parysh)); checksh = *parysh; for (j = 0; j < 3; j++) { if (!isshsubseg(checksh)) { spivot(checksh, neighsh); - assert(neighsh.sh != NULL); if (!smarktested(neighsh)) { stpivot(neighsh, neightet); if (infected(neightet)) { @@ -9544,10 +9788,10 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, } if (j == 0) { // Not found such a face. - assert(0); // debug this case. + terminatetetgen(this, 2); } neightet = spintet; - if (b->verbose > 3) { + if (b->verbose > 4) { printf(" Cut tet (%d, %d, %d, %d)\n", pointmark(org(neightet)), pointmark(dest(neightet)), pointmark(apex(neightet)), pointmark(oppo(neightet))); @@ -9581,8 +9825,23 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, if (apex(*cavetet) != dummypoint) { // It is a cavity boundary face. Check its visibility. if (oppo(neightet) != dummypoint) { - ori = orient3d(org(*cavetet), dest(*cavetet), apex(*cavetet), - insertpt); + // Check if this face is visible by the new point. + if (issubface(neightet)) { + // Re-use 'volume' and 'attrib'. + pa = org(*cavetet); + pb = dest(*cavetet); + pc = apex(*cavetet); + volume = orient3dfast(pa, pb, pc, insertpt); + attrib = distance(pa, pb) * distance(pb, pc) * distance(pc, pa); + if ((fabs(volume) / attrib) < b->epsilon) { + ori = 0.0; + } else { + ori = orient3d(pa, pb, pc, insertpt); + } + } else { + ori = orient3d(org(*cavetet), dest(*cavetet), apex(*cavetet), + insertpt); + } enqflag = (ori > 0); // Comment: if ori == 0 (coplanar case), we also cut the tet. } else { @@ -9654,7 +9913,7 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, // The cavity should contain at least one tet. if (caveoldtetlist->objects == 0l) { insertpoint_abort(splitseg, ivf); - ivf->iloc = (int) BADELEMENT; + ivf->iloc = (int) NULLCAVITY; // BADELEMENT; return 0; } @@ -9721,7 +9980,7 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, if (i > 0) { // The updated sC(p) is invalid. Do not insert this vertex. insertpoint_abort(splitseg, ivf); - ivf->iloc = (int) BADELEMENT; + ivf->iloc = (int) NULLCAVITY; // BADELEMENT; return 0; } } // if (cutshcount > 0) @@ -9740,115 +9999,65 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, insertpoint_abort(splitseg, ivf); ivf->iloc = (int) BADELEMENT; return 0; - } - } // if (ivf->refineflag) - - if (b->plc && (loc != INSTAR)) { - // Reject the new point if it lies too close to an existing point (b->plc), - // or it lies inside a protecting ball of near vertex (ivf->rejflag & 4). - // Collect the list of vertices of the initial cavity. - if (loc == OUTSIDE) { - pts = (point *) &(searchtet->tet[4]); - for (i = 0; i < 3; i++) { - cavetetvertlist->newindex((void **) &parypt); - *parypt = pts[i]; - } - } else if (loc == INTETRAHEDRON) { - pts = (point *) &(searchtet->tet[4]); - for (i = 0; i < 4; i++) { - cavetetvertlist->newindex((void **) &parypt); - *parypt = pts[i]; - } - } else if (loc == ONFACE) { - pts = (point *) &(searchtet->tet[4]); - for (i = 0; i < 3; i++) { - cavetetvertlist->newindex((void **) &parypt); - *parypt = pts[i]; - } - if (pts[3] != dummypoint) { - cavetetvertlist->newindex((void **) &parypt); - *parypt = pts[3]; - } - fsym(*searchtet, spintet); - if (oppo(spintet) != dummypoint) { - cavetetvertlist->newindex((void **) &parypt); - *parypt = oppo(spintet); - } - } else if (loc == ONEDGE) { - spintet = *searchtet; - cavetetvertlist->newindex((void **) &parypt); - *parypt = org(spintet); - cavetetvertlist->newindex((void **) &parypt); - *parypt = dest(spintet); - while (1) { - if (apex(spintet) != dummypoint) { - cavetetvertlist->newindex((void **) &parypt); - *parypt = apex(spintet); + } else { + // The following options are used in boundary recovery when we try to + // remove a crossing face (ivf->refineflag == 4) or a crossing edge + // (ivf->refineflag == 8). Reject this point if the face(or edge) + // survives after inserting this vertex. + bool bflag = false; + if (ivf->refineflag == 4) { + // Check if the face (ivf.refinetet) is removed. + // Both tets at this face should be in the cavity. + triface adjtet; + fsym(ivf->refinetet, adjtet); + if (!infected(ivf->refinetet) || !infected(adjtet)) { + bflag = true; + } + } else if (ivf->refineflag == 8) { + // Check if the edge (ivf.refinetet) is removed. + // All tets at this edge should be in the cavity. + triface spintet = ivf->refinetet; + while (true) { + if (!infected(spintet)) { + bflag = true; break; + } + fnextself(spintet); + if (spintet.tet == ivf->refinetet.tet) break; } - fnextself(spintet); - if (spintet.tet == searchtet->tet) break; - } - } - - int rejptflag = (ivf->rejflag & 4); - REAL rd; - pts = NULL; - - for (i = 0; i < cavetetvertlist->objects; i++) { - parypt = (point *) fastlookup(cavetetvertlist, i); - rd = distance(*parypt, insertpt); - // Is the point very close to an existing point? - if (rd < b->minedgelength) { - pts = parypt; - loc = NEARVERTEX; - break; } - if (rejptflag) { - // Is the point encroaches upon an existing point? - if (rd < (0.5 * (*parypt)[pointmtrindex])) { - pts = parypt; - loc = ENCVERTEX; - break; - } + if (bflag) { + // Reject this new point. + insertpoint_abort(splitseg, ivf); + ivf->iloc = (int) BADELEMENT; + return 0; } } - cavetetvertlist->restart(); // Clear the work list. + } // if (ivf->refineflag) - if (pts != NULL) { - // The point is either too close to an existing vertex (NEARVERTEX) - // or encroaches upon (inside the protecting ball) of that vertex. - if (loc == NEARVERTEX) { - if (b->nomergevertex) { // -M0/1 option. - // In this case, we still insert this vertex. Although it is very - // close to an existing vertex. Give a warning, anyway. - if (!b->quiet) { - printf("Warning: Two points, %d and %d, are very close.\n", - pointmark(insertpt), pointmark(*pts)); - printf(" Creating a very short edge (len = %g) (< %g).\n", - rd, b->minedgelength); - printf(" You may try a smaller tolerance (-T) (current is %g)\n", - b->epsilon); - printf(" to avoid this warning.\n"); - } - } else { - insertpt[3] = rd; // Only for reporting. - setpoint2ppt(insertpt, *pts); - insertpoint_abort(splitseg, ivf); - ivf->iloc = (int) loc; - return 0; + if ((splitseg != NULL) && (splitseg->sh != NULL)) { + // A segment will be split. It muts lie inside of the cavity. + sstpivot1(*splitseg, neightet); + if (neightet.tet != NULL) { + // This is an existing segment. + bool bflag = false; + spintet = neightet; + while (true) { + if (!infected(spintet)) { + bflag = true; break; } - } else { // loc == ENCVERTEX - // The point lies inside the protection ball. - setpoint2ppt(insertpt, *pts); + fnextself(spintet); + if (spintet.tet == neightet.tet) break; + } + if (bflag) { + // Reject this new point. insertpoint_abort(splitseg, ivf); - ivf->iloc = (int) loc; + ivf->iloc = (int) BADELEMENT; return 0; } - } - } // if (b->plc && (loc != INSTAR)) + } // if (neightet.tet != NULL) + } - if (b->weighted || ivf->cdtflag || ivf->smlenflag - ) { + if (b->weighted || ivf->cdtflag || ivf->smlenflag || ivf->validflag) { // There may be other vertices inside C(p). We need to find them. // Collect all vertices of C(p). for (i = 0; i < caveoldtetlist->objects; i++) { @@ -9906,6 +10115,7 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, if (checksubfaceflag) { cavetetshlist->restart(); } + ivf->iloc = (int) INSTAR; return 1; } @@ -9955,7 +10165,8 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, caveencseglist->newindex((void **) &paryseg); *paryseg = checkseg; } else { - assert(0); // Not possible. + //assert(0); // Not possible. + terminatetetgen(this, 2); } } } else { @@ -10000,7 +10211,8 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, caveencshlist->newindex((void **) &parysh); *parysh = checksh; } else { - assert(0); // Not possible. + //assert(0); // Not possible. + terminatetetgen(this, 2); } } } else { @@ -10081,7 +10293,6 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, } fsym(spintet, newneitet); esymself(newneitet); - assert(newneitet.tet[newneitet.ver & 3] == NULL); bond(neightet, newneitet); if (ivf->lawson > 1) { cavetetlist->newindex((void **) &parytet); @@ -10145,30 +10356,27 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, // Note that the old subface still connects to adjacent old tets // of C(p), which still connect to the tets outside C(p). stpivot(*parysh, neightet); - assert(infected(neightet)); // Find the adjacent tet containing the edge [a,b] outside C(p). spintet = neightet; while (1) { fnextself(spintet); if (!infected(spintet)) break; - assert(spintet.tet != neightet.tet); + if (spintet.tet == neightet.tet) { + terminatetetgen(this, 2); + } } // The adjacent tet connects to a new tet in C(p). fsym(spintet, neightet); - assert(!infected(neightet)); // Find the tet containing the face [a, b, p]. spintet = neightet; while (1) { fnextself(spintet); if (apex(spintet) == insertpt) break; - assert(spintet.tet != neightet.tet); } // Adjust the edge direction in spintet and checksh. if (sorg(checksh) != org(spintet)) { sesymself(checksh); - assert(sorg(checksh) == org(spintet)); } - assert(sdest(checksh) == dest(spintet)); // Connect the subface to two adjacent tets. tsbond(spintet, checksh); fsymself(spintet); @@ -10176,8 +10384,6 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, tsbond(spintet, checksh); } // if (checksh.sh[3] != NULL) } - // There should be no missing interior subfaces in C(p). - assert(caveencshlist->objects == 0l); } else { // The Boundary recovery phase. // Put all new subfaces into stack for recovery. @@ -10194,7 +10400,6 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, // Put all interior subfaces into stack for recovery. for (i = 0; i < caveencshlist->objects; i++) { parysh = (face *) fastlookup(caveencshlist, i); - assert(sinfected(*parysh)); // Some subfaces inside C(p) might be split in sinsertvertex(). // Only queue those faces which are not split. if (!smarktested(*parysh)) { @@ -10226,9 +10431,10 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, // It's a dangling segment. point2tetorg(sorg(checkseg), neightet); finddirection(&neightet, sdest(checkseg)); - assert(dest(neightet) == sdest(checkseg)); } - assert(!infected(neightet)); + if (isdeadtet(neightet)) { + terminatetetgen(this, 2); + } sstbond1(checkseg, neightet); spintet = neightet; while (1) { @@ -10238,8 +10444,6 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, } } } // if (splitseg != NULL) - // There should be no interior segment in C(p). - assert(caveencseglist->objects == 0l); } else { // The Boundary Recovery Phase. // Queue missing segments in C(p) for recovery. @@ -10249,16 +10453,12 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, paryseg = (face *) fastlookup(cavesegshlist, i); checkseg = *paryseg; //sstdissolve1(checkseg); // It has not been connected yet. - s = randomnation(subsegstack->objects + 1); subsegstack->newindex((void **) &paryseg); - *paryseg = * (face *) fastlookup(subsegstack, s); - paryseg = (face *) fastlookup(subsegstack, s); *paryseg = checkseg; } } // if (splitseg != NULL) for (i = 0; i < caveencseglist->objects; i++) { paryseg = (face *) fastlookup(caveencseglist, i); - assert(sinfected(*paryseg)); if (!smarktested(*paryseg)) { // It may be split. checkseg = *paryseg; suninfect(checkseg); @@ -10273,24 +10473,45 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, } } // if (checksubsegflag) - if (b->weighted - ) { + if (b->weighted || ivf->validflag) { // Some vertices may be completed inside the cavity. They must be // detected and added to recovering list. - // Since every "live" vertex must contain a pointer to a non-dead - // tetrahedron, we can check for each vertex this pointer. for (i = 0; i < cavetetvertlist->objects; i++) { pts = (point *) fastlookup(cavetetvertlist, i); decode(point2tet(*pts), *searchtet); - assert(searchtet->tet != NULL); // No tet has been deleted yet. if (infected(*searchtet)) { if (b->weighted) { - if (b->verbose > 1) { + if (b->verbose > 4) { printf(" Point #%d is non-regular after the insertion of #%d.\n", pointmark(*pts), pointmark(insertpt)); } setpointtype(*pts, NREGULARVERTEX); nonregularcount++; + } else { + if (b->verbose > 4) { + printf(" Deleting an interior vertex %d.\n", pointmark(*pts)); + } + // The cavity is updated such that no constrained segments and + // subfaces are in its interior. Interior vertices must be + // inside volume or on a boundary facet. + // The point has been removed. + point steinerpt = *pts; + enum verttype vt = pointtype(steinerpt); + if (vt != UNUSEDVERTEX) { + setpointtype(steinerpt, UNUSEDVERTEX); + unuverts++; + } + if (vt != VOLVERTEX) { + // Update the correspinding counters. + if (vt == FREESEGVERTEX) { + st_segref_count--; + } else if (vt == FREEFACETVERTEX) { + st_facref_count--; + } else if (vt == FREEVOLVERTEX) { + st_volref_count--; + } + if (steinerleft > 0) steinerleft++; + } } } } @@ -10367,10 +10588,8 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, if (neightet.tet != NULL) { if (neightet.tet[4] != NULL) { // Found an adjacent tet. It must be not in C(p). - assert(!infected(neightet)); tsdissolve(neightet); fsymself(neightet); - assert(!infected(neightet)); tsdissolve(neightet); } } @@ -10413,7 +10632,7 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, caveencshlist->restart(); } - if (b->weighted || ivf->validflag) { + if (b->weighted || ivf->smlenflag || ivf->validflag) { cavetetvertlist->restart(); } @@ -10427,13 +10646,13 @@ int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, return 1; // Point is inserted. } -/////////////////////////////////////////////////////////////////////////////// -// // -// insertpoint_abort() Abort the insertion of a new vertex. // -// // -// The cavity will be restored. All working lists are cleared. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// insertpoint_abort() Abort the insertion of a new vertex. // +// // +// The cavity will be restored. All working lists are cleared. // +// // +//============================================================================// void tetgenmesh::insertpoint_abort(face *splitseg, insertvertexflags *ivf) { @@ -10461,7 +10680,6 @@ void tetgenmesh::insertpoint_abort(face *splitseg, insertvertexflags *ivf) } for (i = 0; i < caveshlist->objects; i++) { parysh = (face *) fastlookup(caveshlist, i); - assert(smarktested(*parysh)); sunmarktest(*parysh); } caveshlist->restart(); @@ -10469,38 +10687,34 @@ void tetgenmesh::insertpoint_abort(face *splitseg, insertvertexflags *ivf) } } -//// //// -//// //// -//// flip_cxx ///////////////////////////////////////////////////////////////// - -//// delaunay_cxx ///////////////////////////////////////////////////////////// -//// //// -//// //// - -/////////////////////////////////////////////////////////////////////////////// -// // -// transfernodes() Read the vertices from the input (tetgenio). // -// // -// Transferring all points from input ('in->pointlist') to TetGen's 'points'.// -// All points are indexed (the first point index is 'in->firstnumber'). Each // -// point's type is initialized as UNUSEDVERTEX. The bounding box (xmax, xmin,// -// ...) and the diameter (longest) of the point set are calculated. // -// // -/////////////////////////////////////////////////////////////////////////////// +// // +// // +//== flip_cxx ================================================================// + +//== delaunay_cxx ============================================================// +// // +// // + +//============================================================================// +// // +// transfernodes() Read the vertices from the input (tetgenio). // +// // +// Transferring all points from input ('in->pointlist') to TetGen's 'points'. // +// All points are indexed (the first point index is 'in->firstnumber'). Each // +// point's type is initialized as UNUSEDVERTEX. The bounding box (xmax, xmin, // +// ...) and the diameter (longest) of the point set are calculated. // +// // +//============================================================================// void tetgenmesh::transfernodes() { point pointloop; - REAL x, y, z, w; + REAL x, y, z, w, mtr; int coordindex; int attribindex; int mtrindex; int i, j; - if (b->psc) { - assert(in->pointparamlist != NULL); - } - // Read the points. coordindex = 0; attribindex = 0; @@ -10517,7 +10731,8 @@ void tetgenmesh::transfernodes() } // Read the point metric tensor. for (j = 0; j < in->numberofpointmtrs; j++) { - pointloop[pointmtrindex + j] = in->pointmtrlist[mtrindex++]; + mtr = in->pointmtrlist[mtrindex++] * b->metric_scale; + pointloop[pointmtrindex + j] = mtr; // in->pointmtrlist[mtrindex++]; } if (b->weighted) { // -w option if (in->numberofpointattributes > 0) { @@ -10550,54 +10765,58 @@ void tetgenmesh::transfernodes() zmin = (z < zmin) ? z : zmin; zmax = (z > zmax) ? z : zmax; } - if (b->psc) { - // Read the geometry parameters. - setpointgeomuv(pointloop, 0, in->pointparamlist[i].uv[0]); - setpointgeomuv(pointloop, 1, in->pointparamlist[i].uv[1]); - setpointgeomtag(pointloop, in->pointparamlist[i].tag); - if (in->pointparamlist[i].type == 0) { - setpointtype(pointloop, RIDGEVERTEX); - } else if (in->pointparamlist[i].type == 1) { - setpointtype(pointloop, FREESEGVERTEX); - } else if (in->pointparamlist[i].type == 2) { - setpointtype(pointloop, FREEFACETVERTEX); - } else if (in->pointparamlist[i].type == 3) { - setpointtype(pointloop, FREEVOLVERTEX); - } - } } - // 'longest' is the largest possible edge length formed by input vertices. x = xmax - xmin; y = ymax - ymin; z = zmax - zmin; + + exactinit(b->verbose, b->noexact, b->nostaticfilter, x, y, z); + + // Use the number of points as the random seed. + srand(in->numberofpoints); + + // 'longest' is the largest possible edge length formed by input vertices. longest = sqrt(x * x + y * y + z * z); if (longest == 0.0) { printf("Error: The point set is trivial.\n"); - terminatetetgen(this, 3); + terminatetetgen(this, 10); } + // Two identical points are distinguished by 'minedgelength'. + minedgelength = longest * b->epsilon; - // Two identical points are distinguished by 'lengthlimit'. - if (b->minedgelength == 0.0) { - b->minedgelength = longest * b->epsilon; +#ifndef TETLIBRARY + /* + // Release the memory from the input data strutcure + delete [] in->pointlist; + in->pointlist = NULL; + if (in->pointattributelist != NULL) { + delete [] in->pointattributelist; + in->pointattributelist = NULL; + } + if (in->pointmtrlist != NULL) { + delete [] in->pointmtrlist; + in->pointmtrlist = NULL; } + */ +#endif } -/////////////////////////////////////////////////////////////////////////////// -// // -// hilbert_init() Initialize the Gray code permutation table. // -// // -// The table 'transgc' has 8 x 3 x 8 entries. It contains all possible Gray // -// code sequences traveled by the 1st order Hilbert curve in 3 dimensions. // -// The first column is the Gray code of the entry point of the curve, and // -// the second column is the direction (0, 1, or 2, 0 means the x-axis) where // -// the exit point of curve lies. // -// // -// The table 'tsb1mod3' contains the numbers of trailing set '1' bits of the // -// indices from 0 to 7, modulo by '3'. The code for generating this table is // -// from: http://graphics.stanford.edu/~seander/bithacks.html. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// hilbert_init() Initialize the Gray code permutation table. // +// // +// The table 'transgc' has 8 x 3 x 8 entries. It contains all possible Gray // +// code sequences traveled by the 1st order Hilbert curve in 3 dimensions. // +// The first column is the Gray code of the entry point of the curve, and // +// the second column is the direction (0, 1, or 2, 0 means the x-axis) where // +// the exit point of curve lies. // +// // +// The table 'tsb1mod3' contains the numbers of trailing set '1' bits of the // +// indices from 0 to 7, modulo by '3'. The code for generating this table is // +// from: http://graphics.stanford.edu/~seander/bithacks.html. // +// // +//============================================================================// void tetgenmesh::hilbert_init(int n) { @@ -10627,8 +10846,6 @@ void tetgenmesh::hilbert_init(int n) // Calculate the permuted Gray code by xor with the start point (e). transgc[e][d][i] = (g ^ e); } - assert(transgc[e][d][0] == e); - assert(transgc[e][d][N - 1] == f); } // d } // e @@ -10644,11 +10861,11 @@ void tetgenmesh::hilbert_init(int n) } } -/////////////////////////////////////////////////////////////////////////////// -// // -// hilbert_sort3() Sort points using the 3d Hilbert curve. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// hilbert_sort3() Sort points using the 3d Hilbert curve. // +// // +//============================================================================// int tetgenmesh::hilbert_split(point* vertexarray,int arraysize,int gc0,int gc1, REAL bxmin, REAL bxmax, REAL bymin, REAL bymax, @@ -10815,11 +11032,11 @@ void tetgenmesh::hilbert_sort3(point* vertexarray, int arraysize, int e, int d, } // w } -/////////////////////////////////////////////////////////////////////////////// -// // -// brio_multiscale_sort() Sort the points using BRIO and Hilbert curve. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// brio_multiscale_sort() Sort the points using BRIO and Hilbert curve. // +// // +//============================================================================// void tetgenmesh::brio_multiscale_sort(point* vertexarray, int arraysize, int threshold, REAL ratio, int *depth) @@ -10837,11 +11054,11 @@ void tetgenmesh::brio_multiscale_sort(point* vertexarray, int arraysize, xmin, xmax, ymin, ymax, zmin, zmax, 0); // depth. } -/////////////////////////////////////////////////////////////////////////////// -// // -// randomnation() Generate a random number between 0 and 'choices' - 1. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// randomnation() Generate a random number between 0 and 'choices' - 1. // +// // +//============================================================================// unsigned long tetgenmesh::randomnation(unsigned int choices) { @@ -10862,16 +11079,16 @@ unsigned long tetgenmesh::randomnation(unsigned int choices) } } -/////////////////////////////////////////////////////////////////////////////// -// // -// randomsample() Randomly sample the tetrahedra for point loation. // -// // -// Searching begins from one of handles: the input 'searchtet', a recently // -// encountered tetrahedron 'recenttet', or from one chosen from a random // -// sample. The choice is made by determining which one's origin is closest // -// to the point we are searching for. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// randomsample() Randomly sample the tetrahedra for point loation. // +// // +// Searching begins from one of handles: the input 'searchtet', a recently // +// encountered tetrahedron 'recenttet', or from one chosen from a random // +// sample. The choice is made by determining which one's origin is closest // +// to the point we are searching for. // +// // +//============================================================================// void tetgenmesh::randomsample(point searchpt,triface *searchtet) { @@ -10892,8 +11109,6 @@ void tetgenmesh::randomsample(point searchpt,triface *searchtet) if (searchtet->tet == NULL) { // A null tet. Choose the recenttet as the starting tet. *searchtet = recenttet; - // Recenttet should not be dead. - assert(recenttet.tet[4] != NULL); } // 'searchtet' should be a valid tetrahedron. Choose the base face @@ -10920,7 +11135,6 @@ void tetgenmesh::randomsample(point searchpt,triface *searchtet) } } else { // The mesh is non-convex. Do not use 'recenttet'. - assert(samples >= 1l); // Make sure at least 1 sample. searchdist = longest; } @@ -10936,6 +11150,9 @@ void tetgenmesh::randomsample(point searchpt,triface *searchtet) // Find the average samples per block. Each block at least have 1 sample. samplesperblock = 1 + (samples / tetblocks); sampleblocks = samples / samplesperblock; + if (sampleblocks == 0) { + sampleblocks = 1; // at least one sample block is needed. + } sampleblock = tetrahedrons->firstblock; for (i = 0; i < sampleblocks; i++) { alignptr = (uintptr_t) (sampleblock + 1); @@ -10971,38 +11188,162 @@ void tetgenmesh::randomsample(point searchpt,triface *searchtet) } } -/////////////////////////////////////////////////////////////////////////////// -// // -// locate() Find a tetrahedron containing a given point. // -// // -// Begins its search from 'searchtet', assume there is a line segment L from // -// a vertex of 'searchtet' to the query point 'searchpt', and simply walk // -// towards 'searchpt' by traversing all faces intersected by L. // -// // -// On completion, 'searchtet' is a tetrahedron that contains 'searchpt'. The // -// returned value indicates one of the following cases: // -// - ONVERTEX, the search point lies on the origin of 'searchtet'. // -// - ONEDGE, the search point lies on an edge of 'searchtet'. // -// - ONFACE, the search point lies on a face of 'searchtet'. // -// - INTET, the search point lies in the interior of 'searchtet'. // -// - OUTSIDE, the search point lies outside the mesh. 'searchtet' is a // -// hull face which is visible by the search point. // -// // -// WARNING: This routine is designed for convex triangulations, and will not // -// generally work after the holes and concavities have been carved. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// locate() Find a tetrahedron containing a given point. // +// // +// Begins its search from 'searchtet', assume there is a line segment L from // +// a vertex of 'searchtet' to the query point 'searchpt', and simply walk // +// towards 'searchpt' by traversing all faces intersected by L. // +// // +// On completion, 'searchtet' is a tetrahedron that contains 'searchpt'. The // +// returned value indicates one of the following cases: // +// - ONVERTEX, the search point lies on the origin of 'searchtet'. // +// - ONEDGE, the search point lies on an edge of 'searchtet'. // +// - ONFACE, the search point lies on a face of 'searchtet'. // +// - INTET, the search point lies in the interior of 'searchtet'. // +// - OUTSIDE, the search point lies outside the mesh. 'searchtet' is a // +// hull face which is visible by the search point. // +// // +// WARNING: This routine is designed for convex triangulations, and will not // +// generally work after the holes and concavities have been carved. // +// // +//============================================================================// + +enum tetgenmesh::locateresult + tetgenmesh::locate_dt(point searchpt, triface* searchtet) +{ + //enum {ORGMOVE, DESTMOVE, APEXMOVE} nextmove; + REAL ori, oriorg, oridest, oriapex; + enum locateresult loc = OUTSIDE; + point toppo; + int s, i; + + if (searchtet->tet == NULL) { + searchtet->tet = recenttet.tet; + } + + if (ishulltet(*searchtet)) { + // Get its adjacent tet (inside the hull). + searchtet->tet = decode_tet_only(searchtet->tet[3]); + } + + // Let searchtet be the face such that 'searchpt' lies above to it. + for (searchtet->ver = 0; searchtet->ver < 4; searchtet->ver++) { + ori = orient3d(org(*searchtet), dest(*searchtet), apex(*searchtet), searchpt); + if (ori < 0.0) break; + } + + if (searchtet->ver == 4) { + terminatetetgen(this, 2); + } + + // Walk through tetrahedra to locate the point. + do { + + toppo = oppo(*searchtet); + + // Check if the vertex is we seek. + if (toppo == searchpt) { + // Adjust the origin of searchtet to be searchpt. + esymself(*searchtet); + eprevself(*searchtet); + loc = ONVERTEX; // return ONVERTEX; + break; + } + + // We enter from one of serarchtet's faces, which face do we exit? + // Randomly choose one of three faces (containig toppo) of this tet. + s = rand() % 3; // s \in \{0,1,2\} + for (i = 0; i < s; i++) enextself(*searchtet); + + oriorg = orient3d(dest(*searchtet), apex(*searchtet), toppo, searchpt); + if (oriorg < 0) { + //nextmove = ORGMOVE; + enextesymself(*searchtet); + } else { + oridest = orient3d(apex(*searchtet), org(*searchtet), toppo, searchpt); + if (oridest < 0) { + //nextmove = DESTMOVE; + eprevesymself(*searchtet); + } else { + oriapex = orient3d(org(*searchtet), dest(*searchtet), toppo, searchpt); + if (oriapex < 0) { + //nextmove = APEXMOVE; + esymself(*searchtet); + } else { + // oriorg >= 0, oridest >= 0, oriapex >= 0 ==> found the point. + // The point we seek must be on the boundary of or inside this + // tetrahedron. Check for boundary cases first. + if (oriorg == 0) { + // Go to the face opposite to origin. + enextesymself(*searchtet); + if (oridest == 0) { + eprevself(*searchtet); // edge oppo->apex + if (oriapex == 0) { + // oppo is duplicated with p. + loc = ONVERTEX; // return ONVERTEX; + break; + } + loc = ONEDGE; // return ONEDGE; + break; + } + if (oriapex == 0) { + enextself(*searchtet); // edge dest->oppo + loc = ONEDGE; // return ONEDGE; + break; + } + loc = ONFACE; // return ONFACE; + break; + } + if (oridest == 0) { + // Go to the face opposite to destination. + eprevesymself(*searchtet); + if (oriapex == 0) { + eprevself(*searchtet); // edge oppo->org + loc = ONEDGE; // return ONEDGE; + break; + } + loc = ONFACE; // return ONFACE; + break; + } + if (oriapex == 0) { + // Go to the face opposite to apex + esymself(*searchtet); + loc = ONFACE; // return ONFACE; + break; + } + loc = INTETRAHEDRON; + break; + } + } + } // if (locateflag) + + // Move to the next tet adjacent to the selected face. + decode(searchtet->tet[searchtet->ver & 3], *searchtet); // fsymself + + if (ishulltet(*searchtet)) { + loc = OUTSIDE; // return OUTSIDE; + break; + } + + } while (true); + + return loc; +} -enum tetgenmesh::locateresult tetgenmesh::locate(point searchpt, - triface* searchtet) +enum tetgenmesh::locateresult + tetgenmesh::locate(point searchpt, triface* searchtet, int chkencflag) { point torg, tdest, tapex, toppo; enum {ORGMOVE, DESTMOVE, APEXMOVE} nextmove; REAL ori, oriorg, oridest, oriapex; enum locateresult loc = OUTSIDE; - int t1ver; + //int t1ver; int s; + torg = tdest = tapex = toppo = NULL; + if (searchtet->tet == NULL) { // A null tet. Choose the recenttet as the starting tet. searchtet->tet = recenttet.tet; @@ -11011,8 +11352,7 @@ enum tetgenmesh::locateresult tetgenmesh::locate(point searchpt, // Check if we are in the outside of the convex hull. if (ishulltet(*searchtet)) { // Get its adjacent tet (inside the hull). - searchtet->ver = 3; - fsymself(*searchtet); + searchtet->tet = decode_tet_only(searchtet->tet[3]); } // Let searchtet be the face such that 'searchpt' lies above to it. @@ -11023,11 +11363,12 @@ enum tetgenmesh::locateresult tetgenmesh::locate(point searchpt, ori = orient3d(torg, tdest, tapex, searchpt); if (ori < 0.0) break; } - assert(searchtet->ver != 4); + if (searchtet->ver == 4) { + terminatetetgen(this, 2); + } // Walk through tetrahedra to locate the point. while (true) { - toppo = oppo(*searchtet); // Check if the vertex is we seek. @@ -11154,9 +11495,16 @@ enum tetgenmesh::locateresult tetgenmesh::locate(point searchpt, } else { esymself(*searchtet); } + if (chkencflag) { + // Check if we are walking across a subface. + if (issubface(*searchtet)) { + loc = ENCSUBFACE; + break; + } + } // Move to the adjacent tetrahedron (maybe a hull tetrahedron). - fsymself(*searchtet); - if (oppo(*searchtet) == dummypoint) { + decode(searchtet->tet[searchtet->ver & 3], *searchtet); // fsymself + if (ishulltet(*searchtet)) { loc = OUTSIDE; // return OUTSIDE; break; } @@ -11171,283 +11519,377 @@ enum tetgenmesh::locateresult tetgenmesh::locate(point searchpt, return loc; } -/////////////////////////////////////////////////////////////////////////////// -// // -// flippush() Push a face (possibly will be flipped) into flipstack. // -// // -// The face is marked. The flag is used to check the validity of the face on // -// its popup. Some other flips may change it already. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::flippush(badface*& fstack, triface* flipface) -{ - if (!facemarked(*flipface)) { - badface *newflipface = (badface *) flippool->alloc(); - newflipface->tt = *flipface; - markface(newflipface->tt); - // Push this face into stack. - newflipface->nextitem = fstack; - fstack = newflipface; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// incrementalflip() Incrementally flipping to construct DT. // -// // -// Faces need to be checked for flipping are already queued in 'flipstack'. // -// Return the total number of performed flips. // -// // -// Comment: This routine should be only used in the incremental Delaunay // -// construction. In other cases, lawsonflip3d() should be used. // -// // -// If the new point lies outside of the convex hull ('hullflag' is set). The // -// incremental flip algorithm still works as usual. However, we must ensure // -// that every flip (2-to-3 or 3-to-2) does not create a duplicated (existing)// -// edge or face. Otherwise, the underlying space of the triangulation becomes// -// non-manifold and it is not possible to flip further. // -// Thanks to Joerg Rambau and Frank Lutz for helping in this issue. // -// // -/////////////////////////////////////////////////////////////////////////////// - -int tetgenmesh::incrementalflip(point newpt, int hullflag, flipconstraints *fc) +//============================================================================// +// // +// insert_vertex_bw() Insert a vertex using the Bowyer-Watson algorithm. // +// // +// This function is only used for initial Delaunay triangulation construction.// +// It improves the speed of incremental algorithm. // +// // +//============================================================================// + +int tetgenmesh::insert_vertex_bw(point insertpt, triface *searchtet, + insertvertexflags *ivf) { - badface *popface; - triface fliptets[5], *parytet; - point *pts, *parypt, pe; + tetrahedron **ptptr, *tptr; + triface cavetet, spintet, neightet, neineitet, *parytet; + triface oldtet, newtet; //, newneitet; + point *pts; //, pa, pb, pc, *parypt; + enum locateresult loc = OUTSIDE; REAL sign, ori; - int flipcount = 0; + //REAL attrib, volume; + bool enqflag; int t1ver; - int i; + int i, j, k; //, s; if (b->verbose > 2) { - printf(" Lawson flip (%ld faces).\n", flippool->items); + printf(" Insert point %d\n", pointmark(insertpt)); } - if (hullflag) { - // 'newpt' lies in the outside of the convex hull. - // Mark all hull vertices which are connecting to it. - popface = flipstack; - while (popface != NULL) { - pts = (point *) popface->tt.tet; - for (i = 4; i < 8; i++) { - if ((pts[i] != newpt) && (pts[i] != dummypoint)) { - if (!pinfected(pts[i])) { - pinfect(pts[i]); - cavetetvertlist->newindex((void **) &parypt); - *parypt = pts[i]; - } - } + // Locate the point. + if (searchtet->tet != NULL) { + loc = (enum locateresult) ivf->iloc; + } + + if (loc == OUTSIDE) { + if (searchtet->tet == NULL) { + if (!b->weighted) { + randomsample(insertpt, searchtet); + } else { + // Weighted DT. There may exist dangling vertex. + *searchtet = recenttet; } - popface = popface->nextitem; } + loc = locate_dt(insertpt, searchtet); } - // Loop until the queue is empty. - while (flipstack != NULL) { + ivf->iloc = (int) loc; // The return value. - // Pop a face from the stack. - popface = flipstack; - fliptets[0] = popface->tt; - flipstack = flipstack->nextitem; // The next top item in stack. - flippool->dealloc((void *) popface); + if (b->weighted) { + if (loc != OUTSIDE) { + // Check if this vertex is regular. + pts = (point *) searchtet->tet; + sign = orient4d_s(pts[4], pts[5], pts[6], pts[7], insertpt, + pts[4][3], pts[5][3], pts[6][3], pts[7][3], + insertpt[3]); + if (sign > 0) { + // This new vertex lies above the lower hull. Do not insert it. + ivf->iloc = (int) NONREGULAR; + return 0; + } + } + } + + // Create the initial cavity C(p) which contains all tetrahedra that + // intersect p. It may include 1, 2, or n tetrahedra. + + if (loc == OUTSIDE) { + infect(*searchtet); + cave_oldtet_list->newindex((void **) &ptptr); + *ptptr = searchtet->tet; + } else if (loc == INTETRAHEDRON) { + infect(*searchtet); + cave_oldtet_list->newindex((void **) &ptptr); + *ptptr = searchtet->tet; + } else if (loc == ONFACE) { + infect(*searchtet); + cave_oldtet_list->newindex((void **) &ptptr); + *ptptr = searchtet->tet; + neightet.tet = decode_tet_only(searchtet->tet[searchtet->ver & 3]); + infect(neightet); + cave_oldtet_list->newindex((void **) &ptptr); + *ptptr = neightet.tet; + } else if (loc == ONEDGE) { - // Skip it if it is a dead tet (destroyed by previous flips). - if (isdeadtet(fliptets[0])) continue; - // Skip it if it is not the same tet as we saved. - if (!facemarked(fliptets[0])) continue; + // Add all adjacent boundary tets into list. + spintet = *searchtet; + while (1) { + infect(spintet); + cave_oldtet_list->newindex((void **) &ptptr); + *ptptr = spintet.tet; + fnextself(spintet); + if (spintet.tet == searchtet->tet) break; + } // while (1) + } else if (loc == ONVERTEX) { + // The point already exist. Do nothing and return. + return 0; + } - unmarkface(fliptets[0]); + // Create the cavity C(p). - if ((point) fliptets[0].tet[7] == dummypoint) { - // It must be a hull edge. - fliptets[0].ver = epivot[fliptets[0].ver]; - // A hull edge. The current convex hull may be enlarged. - fsym(fliptets[0], fliptets[1]); - pts = (point *) fliptets[1].tet; - ori = orient3d(pts[4], pts[5], pts[6], newpt); - if (ori < 0) { - // Visible. The convex hull will be enlarged. - // Decide which flip (2-to-3, 3-to-2, or 4-to-1) to use. - // Check if the tet [a,c,e,d] or [c,b,e,d] exists. - enext(fliptets[1], fliptets[2]); - eprev(fliptets[1], fliptets[3]); - fnextself(fliptets[2]); // [a,c,e,*] - fnextself(fliptets[3]); // [c,b,e,*] - if (oppo(fliptets[2]) == newpt) { - if (oppo(fliptets[3]) == newpt) { - // Both tets exist! A 4-to-1 flip is found. - terminatetetgen(this, 2); // Report a bug. - } else { - esym(fliptets[2], fliptets[0]); - fnext(fliptets[0], fliptets[1]); - fnext(fliptets[1], fliptets[2]); - // Perform a 3-to-2 flip. Replace edge [c,a] by face [d,e,b]. - // This corresponds to my standard labels, where edge [e,d] is - // repalced by face [a,b,c], and a is the new vertex. - // [0] [c,a,d,e] (d = newpt) - // [1] [c,a,e,b] (c = dummypoint) - // [2] [c,a,b,d] - flip32(fliptets, 1, fc); - } - } else { - if (oppo(fliptets[3]) == newpt) { - fnext(fliptets[3], fliptets[0]); - fnext(fliptets[0], fliptets[1]); - fnext(fliptets[1], fliptets[2]); - // Perform a 3-to-2 flip. Replace edge [c,b] by face [d,a,e]. - // [0] [c,b,d,a] (d = newpt) - // [1] [c,b,a,e] (c = dummypoint) - // [2] [c,b,e,d] - flip32(fliptets, 1, fc); + for (i = 0; i < cave_oldtet_list->objects; i++) { + ptptr = (tetrahedron **) fastlookup(cave_oldtet_list, i); + cavetet.tet = *ptptr; + for (cavetet.ver = 0; cavetet.ver < 4; cavetet.ver++) { + neightet.tet = decode_tet_only(cavetet.tet[cavetet.ver]); + if (!infected(neightet)) { + // neightet.tet is current outside the cavity. + enqflag = false; + if (!marktested(neightet)) { + if (!ishulltet(neightet)) { + pts = (point *) neightet.tet; + sign = insphere_s(pts[4], pts[5], pts[6], pts[7], insertpt); + enqflag = (sign < 0.0); } else { - if (hullflag) { - // Reject this flip if pe is already marked. - pe = oppo(fliptets[1]); - if (!pinfected(pe)) { - pinfect(pe); - cavetetvertlist->newindex((void **) &parypt); - *parypt = pe; - // Perform a 2-to-3 flip. - flip23(fliptets, 1, fc); - } else { - // Reject this flip. - flipcount--; - } - } else { - // Perform a 2-to-3 flip. Replace face [a,b,c] by edge [e,d]. - // [0] [a,b,c,d], d = newpt. - // [1] [b,a,c,e], c = dummypoint. - flip23(fliptets, 1, fc); + pts = (point *) neightet.tet; + ori = orient3d(pts[4], pts[5], pts[6], insertpt); + if (ori < 0) { + // A visible hull face. + enqflag = true; + } else if (ori == 0.) { + // A coplanar hull face. We need to test if this hull face is + // Delaunay or not. We test if the adjacent tet (not faked) + // of this hull face is Delaunay or not. + triface neineitet; + neineitet.tet = decode_tet_only(neightet.tet[3]); + pts = (point *) neineitet.tet; + sign = insphere_s(pts[4],pts[5],pts[6],pts[7], insertpt); + enqflag = (sign < 0.0); } } + marktest(neightet); } - flipcount++; - } - continue; - } // if (dummypoint) - - fsym(fliptets[0], fliptets[1]); - if ((point) fliptets[1].tet[7] == dummypoint) { - // A hull face is locally Delaunay. - continue; - } - // Check if the adjacent tet has already been tested. - if (marktested(fliptets[1])) { - // It has been tested and it is Delaunay. - continue; + if (enqflag) { + infect(neightet); + cave_oldtet_list->newindex((void **) &ptptr); + *ptptr = neightet.tet; + } else { + // A boundary face. + cavebdrylist->newindex((void **) &parytet); + *parytet = cavetet; + } + } // if (!infected(neightet)) } + } // i - // Test whether the face is locally Delaunay or not. - pts = (point *) fliptets[1].tet; - if (b->weighted) { - sign = orient4d_s(pts[4], pts[5], pts[6], pts[7], newpt, - pts[4][3], pts[5][3], pts[6][3], pts[7][3], - newpt[3]); - } else { - sign = insphere_s(pts[4], pts[5], pts[6], pts[7], newpt); - } + // Create new tetrahedra to fill the cavity. + int f_out = cavebdrylist->objects; + int v_out = (f_out + 4) / 2; + + + triface *pcavetet; + point V[3]; + int local_vcount = 0; // local index of vertex + int sidx[3]; + + static int row_v08_tbl[12] = {8,9,10,11,0,1,2,3,4,5,6,7}; + static int row_v11_tbl[12] = {8,9,10,11,0,1,2,3,4,5,6,7}; + static int col_v01_tbl[12] = {1,1,1,1,5,5,5,5,9,9,9,9}; + static int col_v02_tbl[12] = {2,2,2,2,6,6,6,6,10,10,10,10}; + static int col_v08_tbl[12] = {8,8,8,8,0,0,0,0,4,4,4,4}; + static int col_v11_tbl[12] = {11,11,11,11,3,3,3,3,7,7,7,7}; + + triface *tmp_bw_faces = NULL; + int shiftbits = 0; + + if (v_out < 64) { + shiftbits = 6; + tmp_bw_faces = _bw_faces; + } else if (v_out < 1024) { + // Dynamically allocate an array to store the adjacencies. + int arysize = 1; + int tmp = v_out; + shiftbits = 1; + while ((tmp >>= 1)) shiftbits++; + arysize <<= shiftbits; + tmp_bw_faces = new triface[arysize * arysize]; + } + if (v_out < 1024) { + for (i = 0; i < f_out; i++) { + pcavetet = (triface *) fastlookup(cavebdrylist, i); + oldtet = *pcavetet; - if (sign < 0) { - point pd = newpt; - point pe = oppo(fliptets[1]); - // Check the convexity of its three edges. Stop checking either a - // locally non-convex edge (ori < 0) or a flat edge (ori = 0) is - // encountered, and 'fliptet' represents that edge. - for (i = 0; i < 3; i++) { - ori = orient3d(org(fliptets[0]), dest(fliptets[0]), pd, pe); - if (ori <= 0) break; - enextself(fliptets[0]); - } - if (ori > 0) { - // A 2-to-3 flip is found. - // [0] [a,b,c,d], - // [1] [b,a,c,e]. no dummypoint. - flip23(fliptets, 0, fc); - flipcount++; - } else { // ori <= 0 - // The edge ('fliptets[0]' = [a',b',c',d]) is non-convex or flat, - // where the edge [a',b'] is one of [a,b], [b,c], and [c,a]. - // Check if there are three or four tets sharing at this edge. - esymself(fliptets[0]); // [b,a,d,c] - for (i = 0; i < 3; i++) { - fnext(fliptets[i], fliptets[i+1]); + // Get the tet outside the cavity. + decode(oldtet.tet[oldtet.ver], neightet); + unmarktest(neightet); + + if (ishulltet(oldtet)) { + // neightet.tet may be also a hull tet (=> oldtet is a hull edge). + neightet.ver = epivot[neightet.ver]; + if ((apex(neightet) == dummypoint)) { + hullsize++; // Create a new hull tet. } - if (fliptets[3].tet == fliptets[0].tet) { - // A 3-to-2 flip is found. (No hull tet.) - flip32(fliptets, 0, fc); - flipcount++; - } else { - // There are more than 3 tets at this edge. - fnext(fliptets[3], fliptets[4]); - if (fliptets[4].tet == fliptets[0].tet) { - if (ori == 0) { - // A 4-to-4 flip is found. (Two hull tets may be involved.) - // Current tets in 'fliptets': - // [0] [b,a,d,c] (d may be newpt) - // [1] [b,a,c,e] - // [2] [b,a,e,f] (f may be dummypoint) - // [3] [b,a,f,d] - esymself(fliptets[0]); // [a,b,c,d] - // A 2-to-3 flip replaces face [a,b,c] by edge [e,d]. - // This creates a degenerate tet [e,d,a,b] (tmpfliptets[0]). - // It will be removed by the followed 3-to-2 flip. - flip23(fliptets, 0, fc); // No hull tet. - fnext(fliptets[3], fliptets[1]); - fnext(fliptets[1], fliptets[2]); - // Current tets in 'fliptets': - // [0] [...] - // [1] [b,a,d,e] (degenerated, d may be new point). - // [2] [b,a,e,f] (f may be dummypoint) - // [3] [b,a,f,d] - // A 3-to-2 flip replaces edge [b,a] by face [d,e,f]. - // Hull tets may be involved (f may be dummypoint). - flip32(&(fliptets[1]), (apex(fliptets[3]) == dummypoint), fc); - flipcount++; - } - } - } - } // ori - } else { - // The adjacent tet is Delaunay. Mark it to avoid testing it again. - marktest(fliptets[1]); - // Save it for unmarking it later. - cavebdrylist->newindex((void **) &parytet); - *parytet = fliptets[1]; + } + + // Create a new tet in the cavity. + V[0] = dest(neightet); + V[1] = org(neightet); + V[2] = apex(neightet); + maketetrahedron2(&newtet, V[1], V[0], insertpt, V[2]); + //bond(newtet, neightet); + newtet.tet[2] = encode2(neightet.tet, neightet.ver); + neightet.tet[neightet.ver & 3] = encode2(newtet.tet, col_v02_tbl[neightet.ver]); + + // Fill the adjacency matrix, and count v_out. + for (j = 0; j < 3; j++) { + tptr = (tetrahedron *) point2tet(V[j]); + if (((point *) tptr)[6] != insertpt) { + // Found a unique vertex of the cavity. + setpointgeomtag(V[j], local_vcount++); + //local_vcount++; + setpoint2tet(V[j], (tetrahedron) (newtet.tet)); + } + sidx[j] = pointgeomtag(V[j]); + } // j + + neightet.tet = newtet.tet; + // Avoid using lookup tables. + neightet.ver = 11; + tmp_bw_faces[(sidx[1] << shiftbits) | sidx[0]] = neightet; + neightet.ver = 1; + tmp_bw_faces[(sidx[2] << shiftbits) | sidx[1]] = neightet; + neightet.ver = 8; + tmp_bw_faces[(sidx[0] << shiftbits) | sidx[2]] = neightet; + + *pcavetet = newtet; + } // i // f_out + + // Set a handle for speeding point location. + // Randomly pick a new tet. + i = rand() % f_out; + recenttet = * (triface *) fastlookup(cavebdrylist, i); + setpoint2tet(insertpt, (tetrahedron) (recenttet.tet)); + + for (i = 0; i < f_out; i++) { + neightet = * (triface *) fastlookup(cavebdrylist, i); + if (neightet.tet[3] == NULL) { + neightet.ver = 11; + j = pointgeomtag(org(neightet)); + k = pointgeomtag(dest(neightet)); + neineitet = tmp_bw_faces[(k << shiftbits) | j]; + // bondtbl[i][j] = (j & 3) + (((i & 12) + (j & 12)) % 12); + neightet.tet[3] = encode2(neineitet.tet, row_v11_tbl[neineitet.ver]); + neineitet.tet[neineitet.ver & 3] = encode2(neightet.tet, col_v11_tbl[neineitet.ver]); + } + if (neightet.tet[1] == NULL) { + neightet.ver = 1; + j = pointgeomtag(org(neightet)); + k = pointgeomtag(dest(neightet)); + neineitet = tmp_bw_faces[(k << shiftbits) | j]; + neightet.tet[1] = encode2(neineitet.tet, neineitet.ver); // row_v01_tbl + neineitet.tet[neineitet.ver & 3] = encode2(neightet.tet, col_v01_tbl[neineitet.ver]); + } + if (neightet.tet[0] == NULL) { + neightet.ver = 8; + j = pointgeomtag(org(neightet)); + k = pointgeomtag(dest(neightet)); + neineitet = tmp_bw_faces[(k << shiftbits) | j]; + // bondtbl[i][j] = (j & 3) + (((i & 12) + (j & 12)) % 12); + neightet.tet[0] = encode2(neineitet.tet, row_v08_tbl[neineitet.ver]); + neineitet.tet[neineitet.ver & 3] = encode2(neightet.tet, col_v08_tbl[neineitet.ver]); + } + } // i + + if (v_out >= 64) { + delete [] tmp_bw_faces; } + } // v_out < 1024 + else { + // Fill a very large cavity with original neighboring searching method. + for (i = 0; i < f_out; i++) { + pcavetet = (triface *) fastlookup(cavebdrylist, i); + oldtet = *pcavetet; - } // while (flipstack) + // Get the tet outside the cavity. + decode(oldtet.tet[oldtet.ver], neightet); + unmarktest(neightet); - // Unmark saved tetrahedra. - for (i = 0; i < cavebdrylist->objects; i++) { - parytet = (triface *) fastlookup(cavebdrylist, i); - unmarktest(*parytet); - } - cavebdrylist->restart(); + if (ishulltet(oldtet)) { + // neightet.tet may be also a hull tet (=> oldtet is a hull edge). + neightet.ver = epivot[neightet.ver]; + if ((apex(neightet) == dummypoint)) { + hullsize++; // Create a new hull tet. + } + } + + // Create a new tet in the cavity. + V[0] = dest(neightet); + V[1] = org(neightet); + V[2] = apex(neightet); + maketetrahedron2(&newtet, V[1], V[0], insertpt, V[2]); + //newtet.ver = 2; // esymself(newtet); + //assert(oppo(newtet) == insertpt); + + //bond(newtet, neightet); + newtet.tet[2] = encode2(neightet.tet, neightet.ver); + neightet.tet[neightet.ver & 3] = encode2(newtet.tet, col_v02_tbl[neightet.ver]); - if (hullflag) { - // Unmark infected vertices. - for (i = 0; i < cavetetvertlist->objects; i++) { - parypt = (point *) fastlookup(cavetetvertlist, i); - puninfect(*parypt); + // Fill the adjacency matrix, and count v_out. + for (j = 0; j < 3; j++) { + tptr = (tetrahedron *) point2tet(V[j]); + if (((point *) tptr)[6] != insertpt) { + // Found a unique vertex of the cavity. + //setpointgeomtag(V[j], local_vcount); + local_vcount++; + setpoint2tet(V[j], (tetrahedron) (newtet.tet)); + } + //sidx[j] = pointgeomtag(V[j]); + } // j + } // i, f_out + + // Set a handle for speeding point location. + //recenttet = newtet; + //setpoint2tet(insertpt, (tetrahedron) (newtet.tet)); + i = rand() % f_out; + recenttet = * (triface *) fastlookup(cavebdrylist, i); + // This is still an oldtet. + fsymself(recenttet); + fsymself(recenttet); + setpoint2tet(insertpt, (tetrahedron) (recenttet.tet)); + + for (i = 0; i < f_out; i++) { + pcavetet = (triface *) fastlookup(cavebdrylist, i); + oldtet = *pcavetet; + + fsym(oldtet, neightet); + fsym(neightet, newtet); + // Comment: oldtet and newtet must be at the same directed edge. + // Connect the three other faces of this newtet. + for (j = 0; j < 3; j++) { + esym(newtet, neightet); // Go to the face. + if (neightet.tet[neightet.ver & 3] == NULL) { + // Find the adjacent face of this newtet. + spintet = oldtet; + while (1) { + fnextself(spintet); + if (!infected(spintet)) break; + } + fsym(spintet, neineitet); + esymself(neineitet); + bond(neightet, neineitet); + } + enextself(newtet); + enextself(oldtet); + } // j + } // i + } // fill cavity + + // C(p) is re-meshed successfully. + + // Delete the old tets in C(p). + for (i = 0; i < cave_oldtet_list->objects; i++) { + oldtet.tet = *(tetrahedron **) fastlookup(cave_oldtet_list, i); + if (ishulltet(oldtet)) { + hullsize--; } - cavetetvertlist->restart(); + tetrahedrondealloc(oldtet.tet); } + cave_oldtet_list->restart(); + cavebdrylist->restart(); - return flipcount; + return 1; } -/////////////////////////////////////////////////////////////////////////////// -// // -// initialdelaunay() Create an initial Delaunay tetrahedralization. // -// // -// The tetrahedralization contains only one tetrahedron abcd, and four hull // -// tetrahedra. The points pa, pb, pc, and pd must be linearly independent. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// initialdelaunay() Create an initial Delaunay tetrahedralization. // +// // +// The tetrahedralization contains only one tetrahedron abcd, and four hull // +// tetrahedra. The points pa, pb, pc, and pd must be linearly independent. // +// // +//============================================================================// void tetgenmesh::initialdelaunay(point pa, point pb, point pc, point pd) { @@ -11460,17 +11902,19 @@ void tetgenmesh::initialdelaunay(point pa, point pb, point pc, point pd) } // Create the first tetrahedron. - maketetrahedron(&firsttet); - setvertices(firsttet, pa, pb, pc, pd); + maketetrahedron2(&firsttet, pa, pb, pc, pd); + //setvertices(firsttet, pa, pb, pc, pd); + // Create four hull tetrahedra. - maketetrahedron(&tetopa); - setvertices(tetopa, pb, pc, pd, dummypoint); - maketetrahedron(&tetopb); - setvertices(tetopb, pc, pa, pd, dummypoint); - maketetrahedron(&tetopc); - setvertices(tetopc, pa, pb, pd, dummypoint); - maketetrahedron(&tetopd); - setvertices(tetopd, pb, pa, pc, dummypoint); + maketetrahedron2(&tetopa, pb, pc, pd, dummypoint); + //setvertices(tetopa, pb, pc, pd, dummypoint); + maketetrahedron2(&tetopb, pc, pa, pd, dummypoint); + //setvertices(tetopb, pc, pa, pd, dummypoint); + maketetrahedron2(&tetopc, pa, pb, pd, dummypoint); + //setvertices(tetopc, pa, pb, pd, dummypoint); + maketetrahedron2(&tetopd, pb, pa, pc, dummypoint); + //setvertices(tetopd, pb, pa, pc, dummypoint); + hullsize += 4; // Connect hull tetrahedra to firsttet (at four faces of firsttet). @@ -11521,16 +11965,19 @@ void tetgenmesh::initialdelaunay(point pa, point pb, point pc, point pd) setpoint2tet(pc, encode(firsttet)); setpoint2tet(pd, encode(firsttet)); + setpoint2tet(dummypoint, encode(tetopa)); + // Remember the first tetrahedron. recenttet = firsttet; } -/////////////////////////////////////////////////////////////////////////////// -// // -// incrementaldelaunay() Create a Delaunay tetrahedralization by // -// the incremental approach. // -// // -/////////////////////////////////////////////////////////////////////////////// + +//============================================================================// +// // +// incrementaldelaunay() Create a Delaunay tetrahedralization by // +// the incremental approach. // +// // +//============================================================================// void tetgenmesh::incrementaldelaunay(clock_t& tv) @@ -11546,7 +11993,6 @@ void tetgenmesh::incrementaldelaunay(clock_t& tv) if (!b->quiet) { printf("Delaunizing vertices...\n"); } - // Form a random permuation (uniformly at random) of the set of vertices. permutarray = new point[in->numberofpoints]; points->traversalinit(); @@ -11603,16 +12049,13 @@ void tetgenmesh::incrementaldelaunay(clock_t& tv) } // Make sure the third vertex is not collinear with the first two. - // Acknowledgement: Thanks Jan Pomplun for his correction by using - // epsilon^2 and epsilon^3 (instead of epsilon). 2013-08-15. i = 2; for (j = 0; j < 3; j++) { v1[j] = permutarray[1][j] - permutarray[0][j]; v2[j] = permutarray[i][j] - permutarray[0][j]; } cross(v1, v2, n); - while ((sqrt(norm2(n[0], n[1], n[2])) / bboxsize2) < - (b->epsilon * b->epsilon)) { + while ((sqrt(norm2(n[0], n[1], n[2])) / bboxsize2) < b->epsilon) { i++; if (i == in->numberofpoints - 1) { printf("Exception: All vertices are (nearly) collinear (Tol = %g).\n", @@ -11635,7 +12078,7 @@ void tetgenmesh::incrementaldelaunay(clock_t& tv) i = 3; ori = orient3dfast(permutarray[0], permutarray[1], permutarray[2], permutarray[i]); - while ((fabs(ori) / bboxsize3) < (b->epsilon * b->epsilon * b->epsilon)) { + while ((fabs(ori) / bboxsize3) < b->epsilon) { i++; if (i == in->numberofpoints) { printf("Exception: All vertices are coplanar (Tol = %g).\n", @@ -11671,15 +12114,8 @@ void tetgenmesh::incrementaldelaunay(clock_t& tv) insertvertexflags ivf; flipconstraints fc; - // Choose algorithm: Bowyer-Watson (default) or Incremental Flip - if (b->incrflip) { - ivf.bowywat = 0; - ivf.lawson = 1; - fc.enqflag = 1; - } else { - ivf.bowywat = 1; - ivf.lawson = 0; - } + ivf.bowywat = 1; // Use Bowyer-Watson algorithm + ivf.lawson = 0; for (i = 4; i < in->numberofpoints; i++) { @@ -11695,16 +12131,10 @@ void tetgenmesh::incrementaldelaunay(clock_t& tv) } ivf.iloc = (int) OUTSIDE; // Insert the vertex. - if (insertpoint(permutarray[i], &searchtet, NULL, NULL, &ivf)) { - if (flipstack != NULL) { - // Perform flip to recover Delaunayness. - incrementalflip(permutarray[i], (ivf.iloc == (int) OUTSIDE), &fc); - } - } else { + if (!insert_vertex_bw(permutarray[i], &searchtet, &ivf)) { if (ivf.iloc == (int) ONVERTEX) { // The point already exists. Mark it and do nothing on it. swapvertex = org(searchtet); - assert(swapvertex != permutarray[i]); // SELF_CHECK if (b->object != tetgenbehavior::STL) { if (!b->quiet) { printf("Warning: Point #%d is coincident with #%d. Ignored!\n", @@ -11715,42 +12145,38 @@ void tetgenmesh::incrementaldelaunay(clock_t& tv) setpointtype(permutarray[i], DUPLICATEDVERTEX); dupverts++; } else if (ivf.iloc == (int) NEARVERTEX) { - swapvertex = point2ppt(permutarray[i]); - if (!b->quiet) { - printf("Warning: Point %d is replaced by point %d.\n", - pointmark(permutarray[i]), pointmark(swapvertex)); - printf(" Avoid creating a very short edge (len = %g) (< %g).\n", - permutarray[i][3], b->minedgelength); - printf(" You may try a smaller tolerance (-T) (current is %g)\n", - b->epsilon); - printf(" or use the option -M0/1 to avoid such replacement.\n"); - } - // Remember it is a duplicated point. - setpointtype(permutarray[i], DUPLICATEDVERTEX); - // Count the number of duplicated points. - dupverts++; + // This should not happen by insert_point_bw(). + terminatetetgen(this, 2); // report a bug. + } else if (ivf.iloc == (int) NONREGULAR) { + // The point is non-regular. Skipped. + if (b->verbose) { + printf(" Point #%d is non-regular, skipped.\n", + pointmark(permutarray[i])); + } + setpointtype(permutarray[i], NREGULARVERTEX); + nonregularcount++; } } } - + delete [] permutarray; } -//// //// -//// //// -//// delaunay_cxx ///////////////////////////////////////////////////////////// +// // +// // +//== delaunay_cxx ============================================================// -//// surface_cxx ////////////////////////////////////////////////////////////// -//// //// -//// //// +//== surface_cxx =============================================================// +// // +// // -/////////////////////////////////////////////////////////////////////////////// -// // -// flipshpush() Push a facet edge into flip stack. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// flipshpush() Push a facet edge into flip stack. // +// // +//============================================================================// void tetgenmesh::flipshpush(face* flipedge) { @@ -11764,15 +12190,15 @@ void tetgenmesh::flipshpush(face* flipedge) flipstack = newflipface; } -/////////////////////////////////////////////////////////////////////////////// -// // -// flip22() Perform a 2-to-2 flip in surface mesh. // -// // -// 'flipfaces' is an array of two subfaces. On input, they are [a,b,c] and // -// [b,a,d]. On output, they are [c,d,b] and [d,c,a]. As a result, edge [a,b] // -// is replaced by edge [c,d]. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// flip22() Perform a 2-to-2 flip in surface mesh. // +// // +// 'flipfaces' is an array of two subfaces. On input, they are [a,b,c] and // +// [b,a,d]. On output, they are [c,d,b] and [d,c,a]. As a result, edge [a,b] // +// is replaced by edge [c,d]. // +// // +//============================================================================// void tetgenmesh::flip22(face* flipfaces, int flipflag, int chkencflag) { @@ -11881,20 +12307,20 @@ void tetgenmesh::flip22(face* flipfaces, int flipflag, int chkencflag) } } -/////////////////////////////////////////////////////////////////////////////// -// // -// flip31() Remove a vertex by transforming 3-to-1 subfaces. // -// // -// 'flipfaces' is an array of subfaces. Its length is at least 4. On input, // -// the first three faces are: [p,a,b], [p,b,c], and [p,c,a]. This routine // -// replaces them by one face [a,b,c], it is returned in flipfaces[3]. // -// // -// NOTE: The three old subfaces are not deleted within this routine. They // -// still hold pointers to their adjacent subfaces. These informations are // -// needed by the routine 'sremovevertex()' for recovering a segment. // -// The caller of this routine must delete the old subfaces after their uses. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// flip31() Remove a vertex by transforming 3-to-1 subfaces. // +// // +// 'flipfaces' is an array of subfaces. Its length is at least 4. On input, // +// the first three faces are: [p,a,b], [p,b,c], and [p,c,a]. This routine // +// replaces them by one face [a,b,c], it is returned in flipfaces[3]. // +// // +// NOTE: The three old subfaces are not deleted within this routine. They // +// still hold pointers to their adjacent subfaces. These informations are // +// needed by the routine 'sremovevertex()' for recovering a segment. // +// The caller of this routine must delete the old subfaces after their uses. // +// // +//============================================================================// void tetgenmesh::flip31(face* flipfaces, int flipflag) { @@ -11983,11 +12409,11 @@ void tetgenmesh::flip31(face* flipfaces, int flipflag) } } -/////////////////////////////////////////////////////////////////////////////// -// // -// lawsonflip() Flip non-locally Delaunay edges. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// lawsonflip() Flip non-locally Delaunay edges. // +// // +//============================================================================// long tetgenmesh::lawsonflip() { @@ -12040,36 +12466,36 @@ long tetgenmesh::lawsonflip() return flipcount; } -/////////////////////////////////////////////////////////////////////////////// -// // -// sinsertvertex() Insert a vertex into a triangulation of a facet. // -// // -// This function uses three global arrays: 'caveshlist', 'caveshbdlist', and // -// 'caveshseglist'. On return, 'caveshlist' contains old subfaces in C(p), // -// 'caveshbdlist' contains new subfaces in C(p). If the new point lies on a // -// segment, 'cavesegshlist' returns the two new subsegments. // -// // -// 'iloc' suggests the location of the point. If it is OUTSIDE, this routine // -// will first locate the point. It starts searching from 'searchsh' or 'rec- // -// entsh' if 'searchsh' is NULL. // -// // -// If 'bowywat' is set (1), the Bowyer-Watson algorithm is used to insert // -// the vertex. Otherwise, only insert the vertex in the initial cavity. // -// // -// If 'iloc' is 'INSTAR', this means the cavity of this vertex was already // -// provided in the list 'caveshlist'. // -// // -// If 'splitseg' is not NULL, the new vertex lies on the segment and it will // -// be split. 'iloc' must be either 'ONEDGE' or 'INSTAR'. // -// // -// 'rflag' (rounding) is a parameter passed to slocate() function. If it is // -// set, after the location of the point is found, either ONEDGE or ONFACE, // -// round the result using an epsilon. // -// // -// NOTE: the old subfaces in C(p) are not deleted. They're needed in case we // -// want to remove the new point immediately. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// sinsertvertex() Insert a vertex into a triangulation of a facet. // +// // +// This function uses three global arrays: 'caveshlist', 'caveshbdlist', and // +// 'caveshseglist'. On return, 'caveshlist' contains old subfaces in C(p), // +// 'caveshbdlist' contains new subfaces in C(p). If the new point lies on a // +// segment, 'cavesegshlist' returns the two new subsegments. // +// // +// 'iloc' suggests the location of the point. If it is OUTSIDE, this routine // +// will first locate the point. It starts searching from 'searchsh' or 'rec- // +// entsh' if 'searchsh' is NULL. // +// // +// If 'bowywat' is set (1), the Bowyer-Watson algorithm is used to insert // +// the vertex. Otherwise, only insert the vertex in the initial cavity. // +// // +// If 'iloc' is 'INSTAR', this means the cavity of this vertex was already // +// provided in the list 'caveshlist'. // +// // +// If 'splitseg' is not NULL, the new vertex lies on the segment and it will // +// be split. 'iloc' must be either 'ONEDGE' or 'INSTAR'. // +// // +// 'rflag' (rounding) is a parameter passed to slocate() function. If it is // +// set, after the location of the point is found, either ONEDGE or ONFACE, // +// round the result using an epsilon. // +// // +// NOTE: the old subfaces in C(p) are not deleted. They're needed in case we // +// want to remove the new point immediately. // +// // +//============================================================================// int tetgenmesh::sinsertvertex(point insertpt, face *searchsh, face *splitseg, int iloc, int bowywat, int rflag) @@ -12371,9 +12797,6 @@ int tetgenmesh::sinsertvertex(point insertpt, face *searchsh, face *splitseg, if (sorg(neighsh) != pb) sesymself(neighsh); senext2self(neighsh); // Go to the open edge [p, b]. sbond(newsh, neighsh); - } else { - // There is no adjacent new face at this side. - assert(loc == OUTSIDE); // SELF_CHECK } } spivot(*parysh, newsh); // The new subface [a, b, p]. @@ -12395,9 +12818,6 @@ int tetgenmesh::sinsertvertex(point insertpt, face *searchsh, face *splitseg, if (sdest(neighsh) != pa) sesymself(neighsh); senextself(neighsh); // Go to the open edge [a, p]. sbond(newsh, neighsh); - } else { - // There is no adjacent new face at this side. - assert(loc == OUTSIDE); // SELF_CHECK } } } @@ -12426,9 +12846,7 @@ int tetgenmesh::sinsertvertex(point insertpt, face *searchsh, face *splitseg, // Adjust cavesh and neighsh both at edge a->b, and has p as apex. if (sorg(neighsh) != sorg(cavesh)) { sesymself(neighsh); - assert(sorg(neighsh) == sorg(cavesh)); // SELF_CHECK } - assert(sapex(neighsh) == insertpt); // SELF_CHECK // Connect adjacent faces at two other edges of cavesh and neighsh. // As a result, the two degenerated new faces are squeezed from the // new triangulation of the cavity. Note that the squeezed faces @@ -12561,24 +12979,24 @@ int tetgenmesh::sinsertvertex(point insertpt, face *searchsh, face *splitseg, return (int) loc; } -/////////////////////////////////////////////////////////////////////////////// -// // -// sremovevertex() Remove a vertex from the surface mesh. // -// // -// 'delpt' (p) is the vertex to be removed. If 'parentseg' is not NULL, p is // -// a segment vertex, and the origin of 'parentseg' is p. Otherwise, p is a // -// facet vertex, and the origin of 'parentsh' is p. // -// // -// Within each facet, we first use a sequence of 2-to-2 flips to flip any // -// edge at p, finally use a 3-to-1 flip to remove p. // -// // -// All new created subfaces are returned in the global array 'caveshbdlist'. // -// The new segment (when p is on segment) is returned in 'parentseg'. // -// // -// If 'lawson' > 0, the Lawson flip algorithm is used to recover Delaunay- // -// ness after p is removed. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// sremovevertex() Remove a vertex from the surface mesh. // +// // +// 'delpt' (p) is the vertex to be removed. If 'parentseg' is not NULL, p is // +// a segment vertex, and the origin of 'parentseg' is p. Otherwise, p is a // +// facet vertex, and the origin of 'parentsh' is p. // +// // +// Within each facet, we first use a sequence of 2-to-2 flips to flip any // +// edge at p, finally use a 3-to-1 flip to remove p. // +// // +// All new created subfaces are returned in the global array 'caveshbdlist'. // +// The new segment (when p is on segment) is returned in 'parentseg'. // +// // +// If 'lawson' > 0, the Lawson flip algorithm is used to recover Delaunay- // +// ness after p is removed. // +// // +//============================================================================// int tetgenmesh::sremovevertex(point delpt, face* parentsh, face* parentseg, int lawson) @@ -12598,7 +13016,6 @@ int tetgenmesh::sremovevertex(point delpt, face* parentsh, face* parentseg, senext2(*parentseg, prevseg); spivotself(prevseg); prevseg.shver = 0; - assert(sdest(prevseg) == delpt); // Restore the original segment [a,b]. pa = sorg(prevseg); pb = sdest(*parentseg); @@ -12620,7 +13037,6 @@ int tetgenmesh::sremovevertex(point delpt, face* parentsh, face* parentseg, spivotself(adjseg1); if (adjseg1.sh != NULL) { adjseg1.shver = 0; - assert(sdest(adjseg1) == pa); senextself(adjseg1); senext2(abseg, adjseg2); sbond(adjseg1, adjseg2); @@ -12630,7 +13046,6 @@ int tetgenmesh::sremovevertex(point delpt, face* parentsh, face* parentseg, spivotself(adjseg1); if (adjseg1.sh != NULL) { adjseg1.shver = 0; - assert(sorg(adjseg1) == pb); senext2self(adjseg1); senext(abseg, adjseg2); sbond(adjseg1, adjseg2); @@ -12650,6 +13065,9 @@ int tetgenmesh::sremovevertex(point delpt, face* parentsh, face* parentseg, *parysh = spinsh; // Go to the next face in the ring. spivotself(spinsh); + if (spinsh.sh == NULL) { + break; // It is possible there is only one facet. + } if (spinsh.sh == parentsh->sh) break; } } @@ -12661,7 +13079,6 @@ int tetgenmesh::sremovevertex(point delpt, face* parentsh, face* parentseg, startsh = *parysh; if (sorg(startsh) != delpt) { sesymself(startsh); - assert(sorg(startsh) == delpt); } // startsh is [p, b, #1], find the subface [a, p, #2]. neighsh = startsh; @@ -12670,11 +13087,9 @@ int tetgenmesh::sremovevertex(point delpt, face* parentsh, face* parentseg, sspivot(neighsh, checkseg); if (checkseg.sh != NULL) { // It must be the segment [a, p]. - assert(checkseg.sh == prevseg.sh); break; } spivotself(neighsh); - assert(neighsh.sh != NULL); if (sorg(neighsh) != delpt) sesymself(neighsh); } // Now neighsh is [a, p, #2]. @@ -12700,7 +13115,6 @@ int tetgenmesh::sremovevertex(point delpt, face* parentsh, face* parentseg, // Special case. There exists already a degenerated face [a,b,p]! // There is no need to create a faked subface here. senext2self(neighsh); // [a,b,p] - assert(sapex(neighsh) == delpt); // Since we will re-connect the face ring using the faked subfaces. // We put the adjacent face of [a,b,p] to the list. spivot(neighsh, startsh); // The original adjacent subface. @@ -12741,7 +13155,6 @@ int tetgenmesh::sremovevertex(point delpt, face* parentsh, face* parentseg, if (b->verbose > 2) { printf(" Remove vertex %d from surface.\n", pointmark(delpt)); } - assert(sorg(*parentsh) == delpt); // Let 'delpt' be its apex. senextself(*parentsh); // For unifying the code, we add parentsh to list. @@ -12759,7 +13172,6 @@ int tetgenmesh::sremovevertex(point delpt, face* parentsh, face* parentseg, // now parentsh is [p,b,#]. if (sorg(*parentsh) != delpt) { // The vertex has already been removed in above special case. - assert(!smarktested(*parentsh)); continue; } @@ -12771,10 +13183,8 @@ int tetgenmesh::sremovevertex(point delpt, face* parentsh, face* parentseg, *parysh = spinsh; senext2self(spinsh); spivotself(spinsh); - assert(spinsh.sh != NULL); if (spinsh.sh == parentsh->sh) break; if (sorg(spinsh) != delpt) sesymself(spinsh); - assert(sorg(spinsh) == delpt); } // while (1) if (caveshlist->objects == 3) { @@ -12827,9 +13237,6 @@ int tetgenmesh::sremovevertex(point delpt, face* parentsh, face* parentseg, } // i if (i == caveshlist->objects) { - // This can happen only if there are 4 edges at p, and they are - // orthogonal to each other, see Fig. 2010-11-01. - assert(caveshlist->objects == 4); // Do a flip22 and a flip31 to remove p. parysh = (face *) fastlookup(caveshlist, 0); flipfaces[0] = *parysh; @@ -12864,32 +13271,32 @@ int tetgenmesh::sremovevertex(point delpt, face* parentsh, face* parentseg, return 0; } -/////////////////////////////////////////////////////////////////////////////// -// // -// slocate() Locate a point in a surface triangulation. // -// // -// Staring the search from 'searchsh'(it should not be NULL). Perform a line // -// walk search for a subface containing the point (p). // -// // -// If 'aflag' is set, the 'dummypoint' is pre-calculated so that it lies // -// above the 'searchsh' in its current orientation. The test if c is CCW to // -// the line a->b can be done by the test if c is below the oriented plane // -// a->b->dummypoint. // -// // -// If 'cflag' is not TRUE, the triangulation may not be convex. Stop search // -// when a segment is met and return OUTSIDE. // -// // -// If 'rflag' (rounding) is set, after the location of the point is found, // -// either ONEDGE or ONFACE, round the result using an epsilon. // -// // -// The returned value indicates the following cases: // -// - ONVERTEX, p is the origin of 'searchsh'. // -// - ONEDGE, p lies on the edge of 'searchsh'. // -// - ONFACE, p lies in the interior of 'searchsh'. // -// - OUTSIDE, p lies outside of the triangulation, p is on the left-hand // -// side of the edge 'searchsh'(s), i.e., org(s), dest(s), p are CW. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// slocate() Locate a point in a surface triangulation. // +// // +// Staring the search from 'searchsh'(it should not be NULL). Perform a line // +// walk search for a subface containing the point (p). // +// // +// If 'aflag' is set, the 'dummypoint' is pre-calculated so that it lies // +// above the 'searchsh' in its current orientation. The test if c is CCW to // +// the line a->b can be done by the test if c is below the oriented plane // +// a->b->dummypoint. // +// // +// If 'cflag' is not TRUE, the triangulation may not be convex. Stop search // +// when a segment is met and return OUTSIDE. // +// // +// If 'rflag' (rounding) is set, after the location of the point is found, // +// either ONEDGE or ONFACE, round the result using an epsilon. // +// // +// The returned value indicates the following cases: // +// - ONVERTEX, p is the origin of 'searchsh'. // +// - ONEDGE, p lies on the edge of 'searchsh'. // +// - ONFACE, p lies in the interior of 'searchsh'. // +// - OUTSIDE, p lies outside of the triangulation, p is on the left-hand // +// side of the edge 'searchsh'(s), i.e., org(s), dest(s), p are CW. // +// // +//============================================================================// enum tetgenmesh::locateresult tetgenmesh::slocate(point searchpt, face* searchsh, int aflag, int cflag, int rflag) @@ -12912,9 +13319,11 @@ enum tetgenmesh::locateresult tetgenmesh::slocate(point searchpt, // 'dummypoint' is given. Make sure it is above [a,b,c] ori = orient3d(pa, pb, pc, dummypoint); - assert(ori != 0); // SELF_CHECK if (ori > 0) { sesymself(*searchsh); // Reverse the face orientation. + } else if (ori == 0.0) { + // This case should not happen theoretically. But... + return UNKNOWN; } // Find an edge of the face s.t. p lies on its right-hand side (CCW). @@ -12925,7 +13334,9 @@ enum tetgenmesh::locateresult tetgenmesh::slocate(point searchpt, if (ori > 0) break; senextself(*searchsh); } - assert(i < 3); // SELF_CHECK + if (i == 3) { + return UNKNOWN; + } pc = sapex(*searchsh); @@ -12999,7 +13410,6 @@ enum tetgenmesh::locateresult tetgenmesh::slocate(point searchpt, if (sorg(neighsh) != sdest(*searchsh)) { sesymself(neighsh); } - assert(sorg(neighsh) == sdest(*searchsh)); // SELF_CHECK // Update the newly discovered face and its endpoints. *searchsh = neighsh; @@ -13052,7 +13462,6 @@ enum tetgenmesh::locateresult tetgenmesh::slocate(point searchpt, if (area_abp == 0) { if (area_bcp == 0) { - assert(area_cap != 0); senextself(*searchsh); loc = ONVERTEX; // p is close to b. } else { @@ -13081,31 +13490,34 @@ enum tetgenmesh::locateresult tetgenmesh::slocate(point searchpt, return loc; } -/////////////////////////////////////////////////////////////////////////////// -// // -// sscoutsegment() Look for a segment in surface triangulation. // -// // -// The segment is given by the origin of 'searchsh' and 'endpt'. Assume the // -// orientation of 'searchsh' is CCW w.r.t. the above point. // -// // -// If an edge in T is found matching this segment, the segment is "locked" // -// in T at the edge. Otherwise, flip the first edge in T that the segment // -// crosses. Continue the search from the flipped face. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// sscoutsegment() Look for a segment in the surface triangulation. // +// // +// The segment is given by the origin of 'searchsh' and 'endpt'. // +// // +// If an edge in T is found matching this segment, the segment is "locked" // +// in T at the edge. Otherwise, flip the first edge in T that the segment // +// crosses. Continue the search from the flipped face. // +// // +// This routine uses 'orisent3d' to determine the search direction. It uses // +// 'dummypoint' as the 'lifted point' in 3d, and it assumes that it (dummy- // +// point) lies above the 'searchsh' (w.r.t the Right-hand rule). // +// // +//============================================================================// enum tetgenmesh::interresult tetgenmesh::sscoutsegment(face *searchsh, - point endpt) + point endpt, int insertsegflag, int reporterrorflag, int chkencflag) { face flipshs[2], neighsh; - face newseg; point startpt, pa, pb, pc, pd; enum interresult dir; enum {MOVE_AB, MOVE_CA} nextmove; REAL ori_ab, ori_ca, len; + pc = NULL; // Avoid warnings from MSVC // The origin of 'searchsh' is fixed. - startpt = sorg(*searchsh); // pa = startpt; + startpt = sorg(*searchsh); nextmove = MOVE_AB; // Avoid compiler warning. if (b->verbose > 2) { @@ -13131,6 +13543,7 @@ enum tetgenmesh::interresult tetgenmesh::sscoutsegment(face *searchsh, break; } + // Round the results. if ((sqrt(triarea(startpt, pb, endpt)) / len) < b->epsilon) { ori_ab = 0.0; @@ -13166,18 +13579,18 @@ enum tetgenmesh::interresult tetgenmesh::sscoutsegment(face *searchsh, } else { // (+0) // The segment collinear with edge [c, a]. senext2self(*searchsh); - sesymself(*searchsh); + sesymself(*searchsh); dir = ACROSSVERT; break; } } else { if (ori_ca > 0) { // (0+) - // The segment collinear with edge [a, b]. + // The segment is collinear with edge [a, b]. dir = ACROSSVERT; break; } else { // (00) // startpt == endpt. Not possible. - assert(0); // SELF_CHECK + terminatetetgen(this, 2); } } } @@ -13185,6 +13598,12 @@ enum tetgenmesh::interresult tetgenmesh::sscoutsegment(face *searchsh, // Move 'searchsh' to the next face, keep the origin unchanged. if (nextmove == MOVE_AB) { + if (chkencflag) { + // Do not cross boundary. + if (isshsubseg(*searchsh)) { + return ACROSSEDGE; // ACROSS_SEG + } + } spivot(*searchsh, neighsh); if (neighsh.sh != NULL) { if (sorg(neighsh) != pb) sesymself(neighsh); @@ -13193,13 +13612,26 @@ enum tetgenmesh::interresult tetgenmesh::sscoutsegment(face *searchsh, // This side (startpt->pb) is outside. It is caused by rounding error. // Try the next side, i.e., (pc->startpt). senext2(*searchsh, neighsh); + if (chkencflag) { + // Do not cross boundary. + if (isshsubseg(neighsh)) { + *searchsh = neighsh; + return ACROSSEDGE; // ACROSS_SEG + } + } spivotself(neighsh); - assert(neighsh.sh != NULL); if (sdest(neighsh) != pc) sesymself(neighsh); *searchsh = neighsh; } - } else { + } else { // MOVE_CA senext2(*searchsh, neighsh); + if (chkencflag) { + // Do not cross boundary. + if (isshsubseg(neighsh)) { + *searchsh = neighsh; + return ACROSSEDGE; // ACROSS_SEG + } + } spivotself(neighsh); if (neighsh.sh != NULL) { if (sdest(neighsh) != pc) sesymself(neighsh); @@ -13207,32 +13639,45 @@ enum tetgenmesh::interresult tetgenmesh::sscoutsegment(face *searchsh, } else { // The same reason as above. // Try the next side, i.e., (startpt->pb). + if (chkencflag) { + // Do not cross boundary. + if (isshsubseg(*searchsh)) { + return ACROSSEDGE; // ACROSS_SEG + } + } spivot(*searchsh, neighsh); - assert(neighsh.sh != NULL); if (sorg(neighsh) != pb) sesymself(neighsh); senext(neighsh, *searchsh); } } - assert(sorg(*searchsh) == startpt); // SELF_CHECK - } // while if (dir == SHAREEDGE) { - // Insert the segment into the triangulation. - makeshellface(subsegs, &newseg); - setshvertices(newseg, startpt, endpt, NULL); - // Set the default segment marker. - setshellmark(newseg, 1); - ssbond(*searchsh, newseg); - spivot(*searchsh, neighsh); - if (neighsh.sh != NULL) { - ssbond(neighsh, newseg); + if (insertsegflag) { + // Insert the segment into the triangulation. + face newseg; + makeshellface(subsegs, &newseg); + setshvertices(newseg, startpt, endpt, NULL); + // Set the default segment marker. + setshellmark(newseg, -1); + ssbond(*searchsh, newseg); + spivot(*searchsh, neighsh); + if (neighsh.sh != NULL) { + ssbond(neighsh, newseg); + } } return dir; } if (dir == ACROSSVERT) { // A point is found collinear with this segment. + if (reporterrorflag) { + point pp = sdest(*searchsh); + printf("PLC Error: A vertex lies in a segment in facet #%d.\n", + shellmark(*searchsh)); + printf(" Vertex: [%d] (%g,%g,%g).\n",pointmark(pp),pp[0],pp[1],pp[2]); + printf(" Segment: [%d, %d]\n", pointmark(startpt), pointmark(endpt)); + } return dir; } @@ -13240,16 +13685,19 @@ enum tetgenmesh::interresult tetgenmesh::sscoutsegment(face *searchsh, // Edge [b, c] intersects with the segment. senext(*searchsh, flipshs[0]); if (isshsubseg(flipshs[0])) { - printf("Error: Invalid PLC.\n"); - pb = sorg(flipshs[0]); - pc = sdest(flipshs[0]); - printf(" Two segments (%d, %d) and (%d, %d) intersect.\n", - pointmark(startpt), pointmark(endpt), pointmark(pb), pointmark(pc)); - terminatetetgen(this, 3); + if (reporterrorflag) { + REAL P[3], Q[3], tp = 0, tq = 0; + linelineint(startpt, endpt, pb, pc, P, Q, &tp, &tq); + printf("PLC Error: Two segments intersect at point (%g,%g,%g),", + P[0], P[1], P[2]); + printf(" in facet #%d.\n", shellmark(*searchsh)); + printf(" Segment 1: [%d, %d]\n", pointmark(pb), pointmark(pc)); + printf(" Segment 2: [%d, %d]\n", pointmark(startpt),pointmark(endpt)); + } + return dir; // ACROSS_SEG } // Flip edge [b, c], queue unflipped edges (for Delaunay checks). spivot(flipshs[0], flipshs[1]); - assert(flipshs[1].sh != NULL); // SELF_CHECK if (sorg(flipshs[1]) != sdest(flipshs[0])) sesymself(flipshs[1]); flip22(flipshs, 1, 0); // The flip may create an inverted triangle, check it. @@ -13261,27 +13709,26 @@ enum tetgenmesh::interresult tetgenmesh::sscoutsegment(face *searchsh, // Re-use ori_ab, ori_ca for the tests. ori_ab = orient3d(pc, pd, dummypoint, pb); ori_ca = orient3d(pd, pc, dummypoint, pa); - //assert(ori_ab * ori_ca != 0); // SELF_CHECK - if (ori_ab < 0) { - flipshpush(&(flipshs[0])); // push it to 'flipstack' - } else if (ori_ca < 0) { - flipshpush(&(flipshs[1])); // // push it to 'flipstack' + if (ori_ab <= 0) { + flipshpush(&(flipshs[0])); + } else if (ori_ca <= 0) { + flipshpush(&(flipshs[1])); } // Set 'searchsh' s.t. its origin is 'startpt'. *searchsh = flipshs[0]; - assert(sorg(*searchsh) == startpt); } - return sscoutsegment(searchsh, endpt); + return sscoutsegment(searchsh, endpt, insertsegflag, reporterrorflag, + chkencflag); } -/////////////////////////////////////////////////////////////////////////////// -// // -// scarveholes() Remove triangles not in the facet. // -// // -// This routine re-uses the two global arrays: caveshlist and caveshbdlist. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// scarveholes() Remove triangles not in the facet. // +// // +// This routine re-uses the two global arrays: caveshlist and caveshbdlist. // +// // +//============================================================================// void tetgenmesh::scarveholes(int holes, REAL* holelist) { @@ -13368,20 +13815,23 @@ void tetgenmesh::scarveholes(int holes, REAL* holelist) caveshbdlist->restart(); } -/////////////////////////////////////////////////////////////////////////////// -// // -// triangulate() Create a CDT for the facet. // -// // -// All vertices of the triangulation have type FACETVERTEX. The actual type // -// of boundary vertices are set by the routine unifysements(). // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::triangulate(int shmark, arraypool* ptlist, arraypool* conlist, - int holes, REAL* holelist) +//============================================================================// +// // +// triangulate() Create a CDT for the facet. // +// // +// All vertices of the triangulation have type FACETVERTEX. The actual type // +// of boundary vertices are set by the routine unifysements(). // +// // +// All segments created here will have a default marker '-1'. Some of these // +// segments will get their actual marker defined in 'edgemarkerlist'. // +// // +//============================================================================// + +int tetgenmesh::triangulate(int shmark, arraypool* ptlist, arraypool* conlist, + int holes, REAL* holelist) { face searchsh, newsh, *parysh; - face newseg; + face newseg, *paryseg; point pa, pb, pc, *ppt, *cons; int iloc; int i, j; @@ -13397,18 +13847,15 @@ void tetgenmesh::triangulate(int shmark, arraypool* ptlist, arraypool* conlist, if (ptlist->objects < 2l) { // Not a segment or a facet. - return; - } - - if (ptlist->objects == 2l) { + return 1; + } else if (ptlist->objects == 2l) { pa = * (point *) fastlookup(ptlist, 0); pb = * (point *) fastlookup(ptlist, 1); if (distance(pa, pb) > 0) { // It is a single segment. makeshellface(subsegs, &newseg); setshvertices(newseg, pa, pb, NULL); - // Set the default segment marker '1'. - setshellmark(newseg, 1); + setshellmark(newseg, -1); } if (pointtype(pa) == VOLVERTEX) { setpointtype(pa, FACETVERTEX); @@ -13416,18 +13863,18 @@ void tetgenmesh::triangulate(int shmark, arraypool* ptlist, arraypool* conlist, if (pointtype(pb) == VOLVERTEX) { setpointtype(pb, FACETVERTEX); } - return; - } - - - if (ptlist->objects == 3) { + return 1; + } else if (ptlist->objects == 3) { pa = * (point *) fastlookup(ptlist, 0); pb = * (point *) fastlookup(ptlist, 1); pc = * (point *) fastlookup(ptlist, 2); } else { // Calculate an above point of this facet. if (!calculateabovepoint(ptlist, &pa, &pb, &pc)) { - return; // The point set is degenerate. + if (!b->quiet) { + printf("Warning: Unable to triangulate facet #%d. Skipped!\n",shmark); + } + return 0; // The point set is degenerate. } } @@ -13448,14 +13895,10 @@ void tetgenmesh::triangulate(int shmark, arraypool* ptlist, arraypool* conlist, } // Are there area constraints? - if (b->quality && (in->facetconstraintlist != (REAL *) NULL)) { - int idx, fmarker; - REAL area; - idx = in->facetmarkerlist[shmark - 1]; // The actual facet marker. + if (b->quality && (in->facetconstraintlist != NULL)) { for (i = 0; i < in->numberoffacetconstraints; i++) { - fmarker = (int) in->facetconstraintlist[i * 2]; - if (fmarker == idx) { - area = in->facetconstraintlist[i * 2 + 1]; + if (shmark == ((int) in->facetconstraintlist[i * 2])) { + REAL area = in->facetconstraintlist[i * 2 + 1]; setareabound(newsh, area); break; } @@ -13467,14 +13910,20 @@ void tetgenmesh::triangulate(int shmark, arraypool* ptlist, arraypool* conlist, for (i = 0; i < 3; i++) { makeshellface(subsegs, &newseg); setshvertices(newseg, sorg(newsh), sdest(newsh), NULL); - // Set the default segment marker '1'. - setshellmark(newseg, 1); + setshellmark(newseg, -1); ssbond(newsh, newseg); senextself(newsh); } - return; + return 1; } + // Triangulate the facet. It may not success (due to rounding error, or + // incorrect input data), use 'caveencshlist' and 'caveencseglist' are + // re-used to store all the newly created subfaces and segments. So we + // can clean them if the triangulation is not successful. + caveencshlist->newindex((void **) &parysh); + *parysh = newsh; + // Incrementally build the triangulation. pinfect(pa); pinfect(pb); @@ -13486,21 +13935,55 @@ void tetgenmesh::triangulate(int shmark, arraypool* ptlist, arraypool* conlist, iloc = (int) OUTSIDE; // Insert the vertex. Use Bowyer-Watson algo. Round the location. iloc = sinsertvertex(*ppt, &searchsh, NULL, iloc, 1, 1); - if (pointtype(*ppt) == VOLVERTEX) { - setpointtype(*ppt, FACETVERTEX); + if (iloc != ((int) ONVERTEX)) { + // Point inserted successfully. + if (pointtype(*ppt) == VOLVERTEX) { + setpointtype(*ppt, FACETVERTEX); + } + // Save the set of new subfaces. + for (j = 0; j < caveshbdlist->objects; j++) { + // Get an old subface at edge [a, b]. + parysh = (face *) fastlookup(caveshbdlist, j); + spivot(*parysh, searchsh); // The new subface [a, b, p]. + // Do not save a deleted new face (degenerated). + if (searchsh.sh[3] != NULL) { + caveencshlist->newindex((void **) &parysh); + *parysh = searchsh; + } + } + // Delete all removed subfaces. + for (j = 0; j < caveshlist->objects; j++) { + parysh = (face *) fastlookup(caveshlist, j); + shellfacedealloc(subfaces, parysh->sh); + } + // Clear the global lists. + caveshbdlist->restart(); + caveshlist->restart(); + cavesegshlist->restart(); + } else { + // The facet triangulation is failed. + break; } - // Delete all removed subfaces. - for (j = 0; j < caveshlist->objects; j++) { - parysh = (face *) fastlookup(caveshlist, j); + } + } // i + puninfect(pa); + puninfect(pb); + puninfect(pc); + + if (i < ptlist->objects) { + //The facet triangulation is failed. Clean the new subfaces. + // There is no new segment be created yet. + if (!b->quiet) { + printf("Warning: Fail to triangulate facet #%d. Skipped!\n", shmark); + } + for (i = 0; i < caveencshlist->objects; i++) { + parysh = (face *) fastlookup(caveencshlist, i); + if (parysh->sh[3] != NULL) { shellfacedealloc(subfaces, parysh->sh); } - // Clear the global lists. - caveshbdlist->restart(); - caveshlist->restart(); - cavesegshlist->restart(); - } else { - puninfect(*ppt); // This point has inserted. } + caveencshlist->restart(); + return 0; } // Insert the segments. @@ -13508,91 +13991,106 @@ void tetgenmesh::triangulate(int shmark, arraypool* ptlist, arraypool* conlist, cons = (point *) fastlookup(conlist, i); searchsh = recentsh; iloc = (int) slocate(cons[0], &searchsh, 1, 1, 0); - if (iloc != (enum locateresult) ONVERTEX) { + if (iloc != (int) ONVERTEX) { // Not found due to roundoff errors. Do a brute-force search. + bool bflag = false; subfaces->traversalinit(); searchsh.sh = shellfacetraverse(subfaces); while (searchsh.sh != NULL) { // Only search the subface in the same facet. if (shellmark(searchsh) == shmark) { if ((point) searchsh.sh[3] == cons[0]) { - searchsh.shver = 0; break; + searchsh.shver = 0; bflag = true; //break; } else if ((point) searchsh.sh[4] == cons[0]) { - searchsh.shver = 2; break; + searchsh.shver = 2; bflag = true; //break; } else if ((point) searchsh.sh[5] == cons[0]) { - searchsh.shver = 4; break; + searchsh.shver = 4; bflag = true; //break; + } + } + if (bflag) { + // [2019-12-03] The subface is not guaranteed to be coplanar, + // only use "shmark" is not enough. + point pa = sorg(searchsh); + point pb = sdest(searchsh); + point pc = sapex(searchsh); + REAL chkori = orient3d(pa, pb, pc, cons[1]); + if (chkori != 0.0) { + REAL len = distance(pa, pb); + len += distance(pb, pc); + len += distance(pc, pa); + len /= 3.0; + REAL len3 = len * len * len; + REAL eps = fabs(chkori) / len3; + if (eps < 1e-5) { + break; // They are almost coplanar. + } + } else { + break; } + bflag = false; // not this subface. } searchsh.sh = shellfacetraverse(subfaces); } - assert(searchsh.sh != NULL); + //if (searchsh.sh == NULL) { + // // Failed to find a subface containing vertex cons[0]. + //} } - // Recover the segment. Some edges may be flipped. - sscoutsegment(&searchsh, cons[1]); - if (flipstack != NULL) { - // Recover locally Delaunay edges. - lawsonflip(); + if (searchsh.sh != NULL) { + // Recover the segment. Some edges may be flipped. + if (sscoutsegment(&searchsh, cons[1], 1, 1, 0) != SHAREEDGE) { + break; // Fail to recover a segment. + } + // Save this newseg. + sspivot(searchsh, newseg); + caveencseglist->newindex((void **) &paryseg); + *paryseg = newseg; + if (flipstack != NULL) { + // Recover locally Delaunay edges. + lawsonflip(); + } + } else { + break; // Failed to find a segment. + } + } // i + + if (i < conlist->objects) { + if (!b->quiet) { + printf("Warning: Fail to recover a segment in facet #%d. Skipped!\n", + shmark); + } + for (i = 0; i < caveencshlist->objects; i++) { + parysh = (face *) fastlookup(caveencshlist, i); + if (parysh->sh[3] != NULL) { + shellfacedealloc(subfaces, parysh->sh); + } + } + for (i = 0; i < caveencseglist->objects; i++) { + paryseg = (face *) fastlookup(caveencseglist, i); + if (paryseg->sh[3] != NULL) { + shellfacedealloc(subsegs, paryseg->sh); + } } + caveencshlist->restart(); + caveencseglist->restart(); + return 0; } // Remove exterior and hole triangles. scarveholes(holes, holelist); + + caveencshlist->restart(); + caveencseglist->restart(); + return 1; } -/////////////////////////////////////////////////////////////////////////////// -// // -// unifysubfaces() Unify two identical subfaces. // -// // -// Two subfaces, f1 [a, b, c] and f2 [a, b, d], share the same edge [a, b]. // -// If c = d, then f1 and f2 are identical. Otherwise, these two subfaces // -// intersect, and the mesher is stopped. // -// // -// If the two subfaces are identical, we try to replace f2 by f1, i.e, all // -// neighbors of f2 are re-connected to f1. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::unifysubfaces(face *f1, face *f2) -{ - if (b->psc) { - // In this case, it is possible that two subfaces are identical. - // While they must belong to two different surfaces. - return; - } - - point pa, pb, pc, pd; - - pa = sorg(*f1); - pb = sdest(*f1); - pc = sapex(*f1); - pd = sapex(*f2); - - if (pc != pd) { - printf("Found two facets intersect each other.\n"); - printf(" 1st: [%d, %d, %d] #%d\n", - pointmark(pa), pointmark(pb), pointmark(pc), shellmark(*f1)); - printf(" 2nd: [%d, %d, %d] #%d\n", - pointmark(pa), pointmark(pb), pointmark(pd), shellmark(*f2)); - terminatetetgen(this, 3); - } else { - printf("Found two duplicated facets.\n"); - printf(" 1st: [%d, %d, %d] #%d\n", - pointmark(pa), pointmark(pb), pointmark(pc), shellmark(*f1)); - printf(" 2nd: [%d, %d, %d] #%d\n", - pointmark(pa), pointmark(pb), pointmark(pd), shellmark(*f2)); - terminatetetgen(this, 3); - } - -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// unifysegments() Remove redundant segments and create face links. // -// // -// After this routine, although segments are unique, but some of them may be // -// removed later by mergefacet(). All vertices still have type FACETVERTEX. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// unifysegments() Remove redundant segments and create face links. // +// // +// After this routine, although segments are unique, but some of them may be // +// removed later by mergefacet(). All vertices still have type FACETVERTEX. // +// // +//============================================================================// void tetgenmesh::unifysegments() { @@ -13600,90 +14098,22 @@ void tetgenmesh::unifysegments() face *facperverlist, sface; face subsegloop, testseg; point torg, tdest; - REAL ori1, ori2, ori3; + REAL ori1, ori2; //, ori3; REAL n1[3], n2[3]; + REAL cosang, ang, ang_tol; int *idx2faclist; int idx, k, m; if (b->verbose > 1) { printf(" Unifying segments.\n"); } + // The limit dihedral angle that two facets are not overlapping. + //ang_tol = b->facet_overlap_ang_tol / 180.0 * PI; + //if (ang_tol < 0.0) ang_tol = 0.0; // Create a mapping from vertices to subfaces. makepoint2submap(subfaces, idx2faclist, facperverlist); - if (b->psc) { - face sface1; - face seg, seg1; - int fmarker, fmarker1; - // First only connect subfaces which belong to the same surfaces. - subsegloop.shver = 0; - subsegs->traversalinit(); - subsegloop.sh = shellfacetraverse(subsegs); - while (subsegloop.sh != (shellface *) NULL) { - torg = sorg(subsegloop); - tdest = sdest(subsegloop); - - idx = pointmark(torg) - in->firstnumber; - for (k = idx2faclist[idx]; k < idx2faclist[idx + 1]; k++) { - sface = facperverlist[k]; - // The face may be deleted if it is a duplicated face. - if (sface.sh[3] == NULL) continue; - // Search the edge torg->tdest. - assert(sorg(sface) == torg); // SELF_CHECK - if (sdest(sface) != tdest) { - senext2self(sface); - sesymself(sface); - } - if (sdest(sface) != tdest) continue; - - sspivot(sface, seg); - if (seg.sh == NULL) continue; - // assert(seg.sh != NULL); It may or may not be subsegloop. - - // Find the adjacent subface on the same facet. - fmarker = in->facetmarkerlist[shellmark(sface) - 1]; - sface1.sh = NULL; - k++; - for (; k < idx2faclist[idx + 1]; k++) { - sface1 = facperverlist[k]; - // The face may be deleted if it is a duplicated face. - if (sface1.sh[3] == NULL) continue; - // Search the edge torg->tdest. - assert(sorg(sface1) == torg); // SELF_CHECK - if (sdest(sface1) != tdest) { - senext2self(sface1); - sesymself(sface1); - } - if (sdest(sface1) != tdest) continue; - // Found a subface sharing at the same edge. - fmarker1 = in->facetmarkerlist[shellmark(sface1) - 1]; - if (fmarker1 == fmarker) { - // Found a pair of adjacent subfaces. Connect them. - // Delete a redundent segment. - sspivot(sface1, seg1); - assert(seg1.sh != NULL); // SELF_CHECK - shellfacedealloc(subsegs, seg.sh); - shellfacedealloc(subsegs, seg1.sh); - ssdissolve(sface); - ssdissolve(sface1); - // Connect them. - sbond(sface, sface1); - // Set Steiner point -to- subface map. - if (pointtype(torg) == FREEFACETVERTEX) { - setpoint2sh(torg, sencode(sface)); - } - if (pointtype(tdest) == FREEFACETVERTEX) { - setpoint2sh(tdest, sencode(sface)); - } - break; - } - } - break; - } - subsegloop.sh = shellfacetraverse(subsegs); - } - } // if (b->psc) subsegloop.shver = 0; subsegs->traversalinit(); @@ -13702,7 +14132,6 @@ void tetgenmesh::unifysegments() // The face may be deleted if it is a duplicated face. if (sface.sh[3] == NULL) continue; // Search the edge torg->tdest. - assert(sorg(sface) == torg); // SELF_CHECK if (sdest(sface) != tdest) { senext2self(sface); sesymself(sface); @@ -13714,118 +14143,31 @@ void tetgenmesh::unifysegments() f1 = facelink; for (m = 0; m < flippool->items - 1; m++) { f2 = f1->nextitem; - ori1 = orient3d(torg, tdest, sapex(f1->ss), sapex(f2->ss)); - ori2 = orient3d(torg, tdest, sapex(f1->ss), sapex(sface)); - if (ori1 > 0) { - // apex(f2) is below f1. - if (ori2 > 0) { - // apex(f) is below f1 (see Fig.1). - ori3 = orient3d(torg, tdest, sapex(f2->ss), sapex(sface)); - if (ori3 > 0) { - // apex(f) is below f2, insert it. - break; - } else if (ori3 < 0) { - // apex(f) is above f2, continue. - } else { // ori3 == 0; - // f is coplanar and codirection with f2. - unifysubfaces(&(f2->ss), &sface); - break; - } - } else if (ori2 < 0) { - // apex(f) is above f1 below f2, inset it (see Fig. 2). - break; - } else { // ori2 == 0; - // apex(f) is coplanar with f1 (see Fig. 5). - ori3 = orient3d(torg, tdest, sapex(f2->ss), sapex(sface)); - if (ori3 > 0) { - // apex(f) is below f2, insert it. - break; - } else { - // f is coplanar and codirection with f1. - unifysubfaces(&(f1->ss), &sface); - break; - } - } - } else if (ori1 < 0) { - // apex(f2) is above f1. - if (ori2 > 0) { - // apex(f) is below f1, continue (see Fig. 3). - } else if (ori2 < 0) { - // apex(f) is above f1 (see Fig.4). - ori3 = orient3d(torg, tdest, sapex(f2->ss), sapex(sface)); - if (ori3 > 0) { - // apex(f) is below f2, insert it. - break; - } else if (ori3 < 0) { - // apex(f) is above f2, continue. - } else { // ori3 == 0; - // f is coplanar and codirection with f2. - unifysubfaces(&(f2->ss), &sface); - break; - } - } else { // ori2 == 0; - // f is coplanar and with f1 (see Fig. 6). - ori3 = orient3d(torg, tdest, sapex(f2->ss), sapex(sface)); - if (ori3 > 0) { - // f is also codirection with f1. - unifysubfaces(&(f1->ss), &sface); - break; - } else { - // f is above f2, continue. - } - } - } else { // ori1 == 0; - // apex(f2) is coplanar with f1. By assumption, f1 is not - // coplanar and codirection with f2. - if (ori2 > 0) { - // apex(f) is below f1, continue (see Fig. 7). - } else if (ori2 < 0) { - // apex(f) is above f1, insert it (see Fig. 7). - break; - } else { // ori2 == 0. - // apex(f) is coplanar with f1 (see Fig. 8). - // f is either codirection with f1 or is codirection with f2. - facenormal(torg, tdest, sapex(f1->ss), n1, 1, NULL); - facenormal(torg, tdest, sapex(sface), n2, 1, NULL); - if (dot(n1, n2) > 0) { - unifysubfaces(&(f1->ss), &sface); - } else { - unifysubfaces(&(f2->ss), &sface); - } - break; - } + ori1 = facedihedral(torg, tdest, sapex(f1->ss), sapex(f2->ss)); + ori2 = facedihedral(torg, tdest, sapex(f1->ss), sapex(sface)); + if (ori1 >= ori2) { + break; // insert this face between f1 and f2. } // Go to the next item; f1 = f2; } // for (m = 0; ...) - if (sface.sh[3] != NULL) { + //if (sface.sh[3] != NULL) { // Insert sface between f1 and f2. newlinkitem = (badface *) flippool->alloc(); newlinkitem->ss = sface; newlinkitem->nextitem = f1->nextitem; f1->nextitem = newlinkitem; - } + //} } else if (flippool->items == 1) { f1 = facelink; - // Make sure that f is not coplanar and codirection with f1. - ori1 = orient3d(torg, tdest, sapex(f1->ss), sapex(sface)); - if (ori1 == 0) { - // f is coplanar with f1 (see Fig. 8). - facenormal(torg, tdest, sapex(f1->ss), n1, 1, NULL); - facenormal(torg, tdest, sapex(sface), n2, 1, NULL); - if (dot(n1, n2) > 0) { - // The two faces are codirectional as well. - unifysubfaces(&(f1->ss), &sface); - } - } // Add this face to link if it is not deleted. - if (sface.sh[3] != NULL) { + //if (sface.sh[3] != NULL) { // Add this face into link. newlinkitem = (badface *) flippool->alloc(); newlinkitem->ss = sface; newlinkitem->nextitem = NULL; f1->nextitem = newlinkitem; - } + //} } else { // The first face. newlinkitem = (badface *) flippool->alloc(); @@ -13835,15 +14177,6 @@ void tetgenmesh::unifysegments() } } // for (k = idx2faclist[idx]; ...) - if (b->psc) { - // Set Steiner point -to- segment map. - if (pointtype(torg) == FREESEGVERTEX) { - setpoint2sh(torg, sencode(subsegloop)); - } - if (pointtype(tdest) == FREESEGVERTEX) { - setpoint2sh(tdest, sencode(subsegloop)); - } - } // Set the connection between this segment and faces containing it, // at the same time, remove redundant segments. @@ -13864,12 +14197,28 @@ void tetgenmesh::unifysegments() f1 = facelink; for (k = 1; k <= flippool->items; k++) { k < flippool->items ? f2 = f1->nextitem : f2 = facelink; - sbond1(f1->ss, f2->ss); + // Calculate the dihedral angle between the two facet. + facenormal(torg, tdest, sapex(f1->ss), n1, 1, NULL); + facenormal(torg, tdest, sapex(f2->ss), n2, 1, NULL); + cosang = dot(n1, n2) / (sqrt(dot(n1, n1)) * sqrt(dot(n2, n2))); + // Rounding. + if (cosang > 1.0) cosang = 1.0; + else if (cosang < -1.0) cosang = -1.0; + ang = acos(cosang); + //if (ang < ang_tol) { + // // Two facets are treated as overlapping each other. + // report_overlapping_facets(&(f1->ss), &(f2->ss), ang); + //} else { + // Record the smallest input dihedral angle. + if (ang < minfacetdihed) { + minfacetdihed = ang; + } + sbond1(f1->ss, f2->ss); + //} f1 = f2; } } - // All identified segments has an init marker "0". flippool->restart(); // Are there length constraints? @@ -13895,101 +14244,19 @@ void tetgenmesh::unifysegments() delete [] facperverlist; } -/////////////////////////////////////////////////////////////////////////////// -// // -// mergefacets() Merge adjacent facets. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::mergefacets() -{ - face parentsh, neighsh, neineish; - face segloop; - point pa, pb, pc, pd; - REAL ang_tol, ang; - int remsegcount; - int fidx1, fidx2; - int fmrk1, fmrk2; - - if (b->verbose > 1) { - printf(" Merging adjacent facets.\n"); - } - - // The dihedral angle bound for two different facets. - // Set by -p option. Default is 179 degree. - ang_tol = b->facet_ang_tol / 180.0 * PI; - remsegcount = 0; - - // Loop all segments, merge adjacent coplanar facets. - subsegs->traversalinit(); - segloop.sh = shellfacetraverse(subsegs); - while (segloop.sh != (shellface *) NULL) { - spivot(segloop, parentsh); - if (parentsh.sh != NULL) { - spivot(parentsh, neighsh); - if (neighsh.sh != NULL) { - spivot(neighsh, neineish); - if (neineish.sh == parentsh.sh) { - // Exactly two subfaces at this segment. - fidx1 = shellmark(parentsh) - 1; - fidx2 = shellmark(neighsh) - 1; - // Only merge them if they are in different facet. - if (fidx1 != fidx2) { - // The two subfaces are not in the same facet. - if (in->facetmarkerlist != NULL) { - fmrk1 = in->facetmarkerlist[fidx1]; - fmrk2 = in->facetmarkerlist[fidx2]; - } else { - fmrk1 = fmrk2 = 0; - } - // Only merge them if they have the same boundary marker. - if (fmrk1 == fmrk2) { - pa = sorg(segloop); - pb = sdest(segloop); - pc = sapex(parentsh); - pd = sapex(neighsh); - // Calculate the dihedral angle at the segment [a,b]. - ang = facedihedral(pa, pb, pc, pd); - if (ang > PI) ang = (2 * PI - ang); - if (ang > ang_tol) { - remsegcount++; - ssdissolve(parentsh); - ssdissolve(neighsh); - shellfacedealloc(subsegs, segloop.sh); - // Add the edge to flip stack. - flipshpush(&parentsh); - } // if (ang > ang_tol) - } // if (fmrk1 == fmrk2) - } // if (fidx1 != fidx2) - } // if (neineish.sh == parentsh.sh) - } - } - segloop.sh = shellfacetraverse(subsegs); - } - - if (flipstack != NULL) { - lawsonflip(); // Recover Delaunayness. - } - - if (b->verbose > 1) { - printf(" %d segments are removed.\n", remsegcount); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// identifypscedges() Identify PSC edges. // -// // -// The set of PSC edges are provided in the 'in->edgelist'. Each edge should // -// also be an edge in the surface mesh. We find the corresponding edges in // -// the surface mesh and make them segments of the mesh. // -// // -// It is possible to give an edge which is not in any facet, i.e., it is a // -// dangling edge inside the volume. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::identifypscedges(point *idx2verlist) +//============================================================================// +// // +// identifyinputedges() Identify input edges. // +// // +// A set of input edges is provided in the 'in->edgelist'. We find these // +// edges in the surface mesh and make them segments of the mesh. // +// // +// It is possible that an input edge is not in any facet, i.e.,it is a float- // +// segment inside the volume. // +// // +//============================================================================// + +void tetgenmesh::identifyinputedges(point *idx2verlist) { face* shperverlist; int* idx2shlist; @@ -14007,26 +14274,30 @@ void tetgenmesh::identifypscedges(point *idx2verlist) printf("Inserting edges ...\n"); } - // All identified segments have the initial marker '1'. - // All segments inserted here should have a marker 'k >= 0'. - - if (b->psc) { - // First mark all segments of the mesh with a marker '-1'. - subsegs->traversalinit(); - segloop.sh = shellfacetraverse(subsegs); - while (segloop.sh != NULL) { - setshellmark(segloop, -1); - segloop.sh = shellfacetraverse(subsegs); - } - } - // Construct a map from points to subfaces. makepoint2submap(subfaces, idx2shlist, shperverlist); - // Process the set of PSC edges. + // Process the set of input edges. for (i = 0; i < in->numberofedges; i++) { endpts = &(in->edgelist[(i << 1)]); - edgemarker = in->edgemarkerlist ? in->edgemarkerlist[i] : 0; + if (endpts[0] == endpts[1]) { + if (!b->quiet) { + printf("Warning: Edge #%d is degenerated. Skipped.\n", i); + } + continue; // Skip a degenerated edge. + } else if (dupverts > 0l) { + // Replace duplicated vertices. + for (j = 0; j < 2; j++) { + checkpt = idx2verlist[endpts[j]]; + if (pointtype(checkpt) == DUPLICATEDVERTEX) { + point meshpt = point2ppt(checkpt); + endpts[j] = pointmark(meshpt); + } + } + } + // Recall that all existing segments have a default marker '-1'. + // We assign all identified segments a default marker '-2'. + edgemarker = in->edgemarkerlist ? in->edgemarkerlist[i] : -2; // Find a face contains the edge. newseg.sh = NULL; @@ -14064,20 +14335,18 @@ void tetgenmesh::identifypscedges(point *idx2verlist) if (neighsh.sh != NULL) { ssbond(neighsh, newseg); } - if (b->psc) { - if (pointtype(pa) == FREESEGVERTEX) { - setpoint2sh(pa, sencode(newseg)); - } - if (pointtype(pb) == FREESEGVERTEX) { - setpoint2sh(pb, sencode(newseg)); - } - } } } else { // It is a dangling segment (not belong to any facets). // Get the two endpoints of this segment. pa = idx2verlist[endpts[0]]; pb = idx2verlist[endpts[1]]; + if (pa == pb) { + if (!b->quiet) { + printf("Warning: Edge #%d is degenerated. Skipped.\n", i); + } + continue; + } // Check if segment [a,b] already exists. // TODO: Change the brute-force search. Slow! point *ppt; @@ -14096,14 +14365,6 @@ void tetgenmesh::identifypscedges(point *idx2verlist) if (newseg.sh == NULL) { makeshellface(subsegs, &newseg); setshvertices(newseg, pa, pb, NULL); - if (b->psc) { - if (pointtype(pa) == FREESEGVERTEX) { - setpoint2sh(pa, sencode(newseg)); - } - if (pointtype(pb) == FREESEGVERTEX) { - setpoint2sh(pb, sencode(newseg)); - } - } } } @@ -14123,53 +14384,166 @@ void tetgenmesh::identifypscedges(point *idx2verlist) } } // i - delete [] shperverlist; delete [] idx2shlist; +} - if (b->psc) { - // Removing all segments with a marker '-1'. - subsegs->traversalinit(); - segloop.sh = shellfacetraverse(subsegs); - while (segloop.sh != NULL) { - if (shellmark(segloop) == -1) { - shellfacedealloc(subsegs, segloop.sh); - } +//============================================================================// +// // +// mergefacets() Merge adjacent facets. // +// // +//============================================================================// + +void tetgenmesh::mergefacets() +{ + face parentsh, neighsh, neineish; + face segloop; + point pa, pb, pc, pd; + REAL n1[3], n2[3]; + REAL cosang, cosang_tol; + + // Allocate an array to save calcaulated dihedral angles at segments. + arraypool *dihedangarray = new arraypool(sizeof(double), 10); + REAL *paryang = NULL; + + // First, remove coplanar segments. + // The dihedral angle bound for two different facets. + cosang_tol = cos(b->facet_separate_ang_tol / 180.0 * PI); + + subsegs->traversalinit(); + segloop.sh = shellfacetraverse(subsegs); + while (segloop.sh != (shellface *) NULL) { + // Only remove a segment if it has a marker '-1'. + if (shellmark(segloop) != -1) { segloop.sh = shellfacetraverse(subsegs); + continue; } - - // Connecting subsegments at Steiner points. - face seg1, seg2; - // Re-use 'idx2shlist' and 'shperverlist'. - makepoint2submap(subsegs, idx2shlist, shperverlist); - - points->traversalinit(); - pa = pointtraverse(); - while (pa != NULL) { - if (pointtype(pa) == FREESEGVERTEX) { - idx = pointmark(pa) - in->firstnumber; - // There must be only two segments containing this vertex. - assert((idx2shlist[idx + 1] - idx2shlist[idx]) == 2); - i = idx2shlist[idx]; - seg1 = shperverlist[i]; - seg2 = shperverlist[i+1]; - senextself(seg1); - senextself(seg2); - sbond(seg1, seg2); + spivot(segloop, parentsh); + if (parentsh.sh != NULL) { + spivot(parentsh, neighsh); + if (neighsh.sh != NULL) { + spivot(neighsh, neineish); + if (neineish.sh == parentsh.sh) { + // Exactly two subfaces at this segment. + // Only merge them if they have the same boundary marker. + if (shellmark(parentsh) == shellmark(neighsh)) { + pa = sorg(segloop); + pb = sdest(segloop); + pc = sapex(parentsh); + pd = sapex(neighsh); + // Calculate the dihedral angle at the segment [a,b]. + facenormal(pa, pb, pc, n1, 1, NULL); + facenormal(pa, pb, pd, n2, 1, NULL); + cosang = dot(n1, n2) / (sqrt(dot(n1, n1)) * sqrt(dot(n2, n2))); + if (cosang < cosang_tol) { + ssdissolve(parentsh); + ssdissolve(neighsh); + shellfacedealloc(subsegs, segloop.sh); + // Add the edge to flip stack. + flipshpush(&parentsh); + } else { + // Save 'cosang' to avoid re-calculate it. + // Re-use the pointer at the first segment. + dihedangarray->newindex((void **) &paryang); + *paryang = cosang; + segloop.sh[6] = (shellface) paryang; + } + } + } // if (neineish.sh == parentsh.sh) } - pa = pointtraverse(); } + segloop.sh = shellfacetraverse(subsegs); + } + + // Second, remove ridge segments at small angles. + // The dihedral angle bound for two different facets. + cosang_tol = cos(b->facet_small_ang_tol / 180.0 * PI); + REAL cosang_sep_tol = cos((b->facet_separate_ang_tol - 5.0) / 180.0 * PI); + face shloop; + face seg1, seg2; + REAL cosang1, cosang2; + int i, j; + + subfaces->traversalinit(); + shloop.sh = shellfacetraverse(subfaces); + while (shloop.sh != (shellface *) NULL) { + for (i = 0; i < 3; i++) { + if (isshsubseg(shloop)) { + senext(shloop, neighsh); + if (isshsubseg(neighsh)) { + // Found two segments sharing at one vertex. + // Check if they form a small angle. + pa = sorg(shloop); + pb = sdest(shloop); + pc = sapex(shloop); + for (j = 0; j < 3; j++) n1[j] = pa[j] - pb[j]; + for (j = 0; j < 3; j++) n2[j] = pc[j] - pb[j]; + cosang = dot(n1, n2) / (sqrt(dot(n1, n1)) * sqrt(dot(n2, n2))); + if (cosang > cosang_tol) { + // Found a small angle. + segloop.sh = NULL; + sspivot(shloop, seg1); + sspivot(neighsh, seg2); + if (seg1.sh[6] != NULL) { + paryang = (REAL *) (seg1.sh[6]); + cosang1 = *paryang; + } else { + cosang1 = 1.0; // 0 degree; + } + if (seg2.sh[6] != NULL) { + paryang = (REAL *) (seg2.sh[6]); + cosang2 = *paryang; + } else { + cosang2 = 1.0; // 0 degree; + } + if (cosang1 < cosang_sep_tol) { + if (cosang2 < cosang_sep_tol) { + if (cosang1 < cosang2) { + segloop = seg1; + } else { + segloop = seg2; + } + } else { + segloop = seg1; + } + } else { + if (cosang2 < cosang_sep_tol) { + segloop = seg2; + } + } + if (segloop.sh != NULL) { + // Remove this segment. + segloop.shver = 0; + spivot(segloop, parentsh); + spivot(parentsh, neighsh); + ssdissolve(parentsh); + ssdissolve(neighsh); + shellfacedealloc(subsegs, segloop.sh); + // Add the edge to flip stack. + flipshpush(&parentsh); + break; + } + } + } // if (isshsubseg) + } // if (isshsubseg) + senextself(shloop); + } + shloop.sh = shellfacetraverse(subfaces); + } - delete [] shperverlist; - delete [] idx2shlist; + delete dihedangarray; + + + if (flipstack != NULL) { + lawsonflip(); // Recover Delaunayness. } } -/////////////////////////////////////////////////////////////////////////////// -// // -// meshsurface() Create a surface mesh of the input PLC. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// meshsurface() Create a surface mesh of the input PLC. // +// // +//============================================================================// void tetgenmesh::meshsurface() { @@ -14295,42 +14669,45 @@ void tetgenmesh::meshsurface() } // Triangulate F into a CDT. - triangulate(shmark, ptlist, conlist, f->numberofholes, f->holelist); + // If in->facetmarklist is NULL, use the default marker -1. + triangulate(in->facetmarkerlist ? in->facetmarkerlist[shmark - 1] : -1, + ptlist, conlist, f->numberofholes, f->holelist); // Clear working lists. ptlist->restart(); conlist->restart(); } - if (!b->diagnose) { - // Remove redundant segments and build the face links. - unifysegments(); - if (!b->psc && !b->nomergefacet && !b->nobisect) { - // Merge adjacent coplanar facets. - mergefacets(); - } - if (in->numberofedges > 0) { // if (b->psc) - // There are segments specified by the user. Read and create them. - identifypscedges(idx2verlist); - } - if (!b->psc) { - // Mark all segment vertices to be RIDGEVERTEX. - face segloop; - point *ppt; - subsegs->traversalinit(); - segloop.sh = shellfacetraverse(subsegs); - while (segloop.sh != NULL) { - ppt = (point *) &(segloop.sh[3]); - setpointtype(ppt[0], RIDGEVERTEX); - setpointtype(ppt[1], RIDGEVERTEX); - segloop.sh = shellfacetraverse(subsegs); - } + + // Remove redundant segments and build the face links. + unifysegments(); + if (in->numberofedges > 0) { + // There are input segments. Insert them. + identifyinputedges(idx2verlist); + } + if (!b->diagnose && !b->nomergefacet && !b->nobisect) { // No -d -M -Y + // Merge coplanar facets. + mergefacets(); + } + + // Mark all segment vertices to be RIDGEVERTEX. + face segloop; + point *ppt; + subsegs->traversalinit(); + segloop.sh = shellfacetraverse(subsegs); + while (segloop.sh != NULL) { + ppt = (point *) &(segloop.sh[3]); + for (i = 0; i < 2; i++) { + setpointtype(ppt[i], RIDGEVERTEX); } + segloop.sh = shellfacetraverse(subsegs); } if (b->object == tetgenbehavior::STL) { // Remove redundant vertices (for .stl input mesh). jettisonnodes(); + // Update the number of input vertices. + in->numberofpoints = points->items; } if (b->verbose) { @@ -14346,366 +14723,41 @@ void tetgenmesh::meshsurface() delete conlist; } -/////////////////////////////////////////////////////////////////////////////// -// // -// interecursive() Recursively do intersection test on a set of triangles.// -// // -// Recursively split the set 'subfacearray' of subfaces into two sets using // -// a cut plane parallel to x-, or, y-, or z-axis. The split criteria are // -// follows. Assume the cut plane is H, and H+ denotes the left halfspace of // -// H, and H- denotes the right halfspace of H; and s be a subface: // -// // -// (1) If all points of s lie at H+, put it into left array; // -// (2) If all points of s lie at H-, put it into right array; // -// (3) If some points of s lie at H+ and some of lie at H-, or some // -// points lie on H, put it into both arraies. // -// // -// Partitions by x-axis if axis == '0'; by y-axis if axis == '1'; by z-axis // -// if axis == '2'. If current cut plane is parallel to the x-axis, the next // -// one will be parallel to y-axis, and the next one after the next is z-axis,// -// and then alternately return back to x-axis. // -// // -// Stop splitting when the number of triangles of the input array is not // -// decreased anymore. Do tests on the current set. // -// // -/////////////////////////////////////////////////////////////////////////////// +// // +// // +//== surface_cxx =============================================================// + +//== constrained_cxx =========================================================// +// // +// // + +//============================================================================// +// // +// finddirection() Find the tet on the path from one point to another. // +// // +// The path starts from 'searchtet''s origin and ends at 'endpt'. On finish, // +// 'searchtet' contains a tet on the path, its origin does not change. // +// // +// The return value indicates one of the following cases (let 'searchtet' be // +// abcd, a is the origin of the path): // +// - ACROSSVERT, edge ab is collinear with the path; // +// - ACROSSEDGE, edge bc intersects with the path; // +// - ACROSSFACE, face bcd intersects with the path. // +// // +// WARNING: This routine is designed for convex triangulations, and will not // +// generally work after the holes and concavities have been carved. // +// // +//============================================================================// -void tetgenmesh::interecursive(shellface** subfacearray, int arraysize, - int axis, REAL bxmin, REAL bxmax, REAL bymin, - REAL bymax, REAL bzmin, REAL bzmax, - int* internum) +enum tetgenmesh::interresult + tetgenmesh::finddirection(triface* searchtet, point endpt) { - shellface **leftarray, **rightarray; - face sface1, sface2; - point p1, p2, p3; - point p4, p5, p6; - enum interresult intersect; - REAL split; - bool toleft, toright; - int leftsize, rightsize; - int i, j; - - if (b->verbose > 2) { - printf(" Recur %d faces. Bbox (%g, %g, %g),(%g, %g, %g). %s-axis\n", - arraysize, bxmin, bymin, bzmin, bxmax, bymax, bzmax, - axis == 0 ? "x" : (axis == 1 ? "y" : "z")); - } - - leftarray = new shellface*[arraysize]; - if (leftarray == NULL) { - terminatetetgen(this, 1); - } - rightarray = new shellface*[arraysize]; - if (rightarray == NULL) { - terminatetetgen(this, 1); - } - leftsize = rightsize = 0; - - if (axis == 0) { - // Split along x-axis. - split = 0.5 * (bxmin + bxmax); - } else if (axis == 1) { - // Split along y-axis. - split = 0.5 * (bymin + bymax); - } else { - // Split along z-axis. - split = 0.5 * (bzmin + bzmax); - } - - for (i = 0; i < arraysize; i++) { - sface1.sh = subfacearray[i]; - p1 = (point) sface1.sh[3]; - p2 = (point) sface1.sh[4]; - p3 = (point) sface1.sh[5]; - toleft = toright = false; - if (p1[axis] < split) { - toleft = true; - if (p2[axis] >= split || p3[axis] >= split) { - toright = true; - } - } else if (p1[axis] > split) { - toright = true; - if (p2[axis] <= split || p3[axis] <= split) { - toleft = true; - } - } else { - // p1[axis] == split; - toleft = true; - toright = true; - } - // At least one is true; - assert(!(toleft == false && toright == false)); - if (toleft) { - leftarray[leftsize] = sface1.sh; - leftsize++; - } - if (toright) { - rightarray[rightsize] = sface1.sh; - rightsize++; - } - } - - if (leftsize < arraysize && rightsize < arraysize) { - // Continue to partition the input set. Now 'subfacearray' has been - // split into two sets, it's memory can be freed. 'leftarray' and - // 'rightarray' will be freed in the next recursive (after they're - // partitioned again or performing tests). - delete [] subfacearray; - // Continue to split these two sets. - if (axis == 0) { - interecursive(leftarray, leftsize, 1, bxmin, split, bymin, bymax, - bzmin, bzmax, internum); - interecursive(rightarray, rightsize, 1, split, bxmax, bymin, bymax, - bzmin, bzmax, internum); - } else if (axis == 1) { - interecursive(leftarray, leftsize, 2, bxmin, bxmax, bymin, split, - bzmin, bzmax, internum); - interecursive(rightarray, rightsize, 2, bxmin, bxmax, split, bymax, - bzmin, bzmax, internum); - } else { - interecursive(leftarray, leftsize, 0, bxmin, bxmax, bymin, bymax, - bzmin, split, internum); - interecursive(rightarray, rightsize, 0, bxmin, bxmax, bymin, bymax, - split, bzmax, internum); - } - } else { - if (b->verbose > 1) { - printf(" Checking intersecting faces.\n"); - } - // Perform a brute-force compare on the set. - for (i = 0; i < arraysize; i++) { - sface1.sh = subfacearray[i]; - p1 = (point) sface1.sh[3]; - p2 = (point) sface1.sh[4]; - p3 = (point) sface1.sh[5]; - for (j = i + 1; j < arraysize; j++) { - sface2.sh = subfacearray[j]; - p4 = (point) sface2.sh[3]; - p5 = (point) sface2.sh[4]; - p6 = (point) sface2.sh[5]; - intersect = (enum interresult) tri_tri_inter(p1, p2, p3, p4, p5, p6); - if (intersect == INTERSECT || intersect == SHAREFACE) { - if (!b->quiet) { - if (intersect == INTERSECT) { - printf(" Facet #%d intersects facet #%d at triangles:\n", - shellmark(sface1), shellmark(sface2)); - printf(" (%4d, %4d, %4d) and (%4d, %4d, %4d)\n", - pointmark(p1), pointmark(p2), pointmark(p3), - pointmark(p4), pointmark(p5), pointmark(p6)); - } else { - printf(" Facet #%d duplicates facet #%d at triangle:\n", - shellmark(sface1), shellmark(sface2)); - printf(" (%4d, %4d, %4d) and (%4d, %4d, %4d)\n", - pointmark(p1), pointmark(p2), pointmark(p3), - pointmark(p4), pointmark(p5), pointmark(p6)); - } - } - // Increase the number of intersecting pairs. - (*internum)++; - // Infect these two faces (although they may already be infected). - sinfect(sface1); - sinfect(sface2); - } - } - } - // Don't forget to free all three arrays. No further partition. - delete [] leftarray; - delete [] rightarray; - delete [] subfacearray; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// detectinterfaces() Detect intersecting triangles. // -// // -// Given a set of triangles, find the pairs of intersecting triangles from // -// them. Here the set of triangles is in 'subfaces' which is a surface mesh // -// of a PLC (.poly or .smesh). // -// // -// To detect whether two triangles are intersecting is done by the routine // -// 'tri_tri_inter()'. The algorithm for the test is very simple and stable. // -// It is based on geometric orientation test which uses exact arithmetics. // -// // -// Use divide-and-conquer algorithm for reducing the number of intersection // -// tests. Start from the bounding box of the input point set, recursively // -// partition the box into smaller boxes, until the number of triangles in a // -// box is not decreased anymore. Then perform triangle-triangle tests on the // -// remaining set of triangles. The memory allocated in the input set is // -// freed immediately after it has been partitioned into two arrays. So it // -// can be re-used for the consequent partitions. // -// // -// On return, the pool 'subfaces' will be cleared, and only the intersecting // -// triangles remain for output (to a .face file). // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::detectinterfaces() -{ - shellface **subfacearray; - face shloop; - int internum; - int i; - - if (!b->quiet) { - printf("Detecting self-intersecting facets...\n"); - } - - // Construct a map from indices to subfaces; - subfacearray = new shellface*[subfaces->items]; - subfaces->traversalinit(); - shloop.sh = shellfacetraverse(subfaces); - i = 0; - while (shloop.sh != (shellface *) NULL) { - subfacearray[i] = shloop.sh; - shloop.sh = shellfacetraverse(subfaces); - i++; - } - - internum = 0; - // Recursively split the set of triangles into two sets using a cut plane - // parallel to x-, or, y-, or z-axis. Stop splitting when the number - // of subfaces is not decreasing anymore. Do tests on the current set. - interecursive(subfacearray, subfaces->items, 0, xmin, xmax, ymin, ymax, - zmin, zmax, &internum); - - if (!b->quiet) { - if (internum > 0) { - printf("\n!! Found %d pairs of faces are intersecting.\n\n", internum); - } else { - printf("\nNo faces are intersecting.\n\n"); - } - } - - if (internum > 0) { - // Traverse all subfaces, deallocate those have not been infected (they - // are not intersecting faces). Uninfect those have been infected. - // After this loop, only intersecting faces remain. - subfaces->traversalinit(); - shloop.sh = shellfacetraverse(subfaces); - while (shloop.sh != (shellface *) NULL) { - if (sinfected(shloop)) { - suninfect(shloop); - } else { - shellfacedealloc(subfaces, shloop.sh); - } - shloop.sh = shellfacetraverse(subfaces); - } - } else { - // Deallocate all subfaces. - subfaces->restart(); - } -} - -//// //// -//// //// -//// surface_cxx ////////////////////////////////////////////////////////////// - -//// constrained_cxx ////////////////////////////////////////////////////////// -//// //// -//// //// - -/////////////////////////////////////////////////////////////////////////////// -// // -// makesegmentendpointsmap() Create a map from a segment to its endpoints.// -// // -// The map is saved in the array 'segmentendpointslist'. The length of this // -// array is twice the number of segments. Each segment is assigned a unique // -// index (starting from 0). // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::makesegmentendpointsmap() -{ - arraypool *segptlist; - face segloop, prevseg, nextseg; - point eorg, edest, *parypt; - int segindex = 0, idx = 0; - int i; - - if (b->verbose > 0) { - printf(" Creating the segment-endpoints map.\n"); - } - - segptlist = new arraypool(2 * sizeof(point), 10); - - // A segment s may have been split into many subsegments. Operate the one - // which contains the origin of s. Then mark the rest of subsegments. - subsegs->traversalinit(); - segloop.sh = shellfacetraverse(subsegs); - segloop.shver = 0; - while (segloop.sh != NULL) { - senext2(segloop, prevseg); - spivotself(prevseg); - if (prevseg.sh == NULL) { - eorg = sorg(segloop); - edest = sdest(segloop); - setfacetindex(segloop, segindex); - senext(segloop, nextseg); - spivotself(nextseg); - while (nextseg.sh != NULL) { - setfacetindex(nextseg, segindex); - nextseg.shver = 0; - if (sorg(nextseg) != edest) sesymself(nextseg); - assert(sorg(nextseg) == edest); - edest = sdest(nextseg); - // Go the next connected subsegment at edest. - senextself(nextseg); - spivotself(nextseg); - } - segptlist->newindex((void **) &parypt); - parypt[0] = eorg; - parypt[1] = edest; - segindex++; - } - segloop.sh = shellfacetraverse(subsegs); - } - - if (b->verbose) { - printf(" Found %ld segments.\n", segptlist->objects); - } - - segmentendpointslist = new point[segptlist->objects * 2]; - - totalworkmemory += (segptlist->objects * 2) * sizeof(point *); - - for (i = 0; i < segptlist->objects; i++) { - parypt = (point *) fastlookup(segptlist, i); - segmentendpointslist[idx++] = parypt[0]; - segmentendpointslist[idx++] = parypt[1]; - } - - delete segptlist; -} - - -/////////////////////////////////////////////////////////////////////////////// -// // -// finddirection() Find the tet on the path from one point to another. // -// // -// The path starts from 'searchtet''s origin and ends at 'endpt'. On finish, // -// 'searchtet' contains a tet on the path, its origin does not change. // -// // -// The return value indicates one of the following cases (let 'searchtet' be // -// abcd, a is the origin of the path): // -// - ACROSSVERT, edge ab is collinear with the path; // -// - ACROSSEDGE, edge bc intersects with the path; // -// - ACROSSFACE, face bcd intersects with the path. // -// // -// WARNING: This routine is designed for convex triangulations, and will not // -// generally work after the holes and concavities have been carved. // -// // -/////////////////////////////////////////////////////////////////////////////// - -enum tetgenmesh::interresult - tetgenmesh::finddirection(triface* searchtet, point endpt) -{ - triface neightet; - point pa, pb, pc, pd; - enum {HMOVE, RMOVE, LMOVE} nextmove; - REAL hori, rori, lori; - int t1ver; - int s; + triface neightet; + point pa, pb, pc, pd; + enum {HMOVE, RMOVE, LMOVE} nextmove; + REAL hori, rori, lori; + int t1ver; + int s; // The origin is fixed. pa = org(*searchtet); @@ -14720,7 +14772,6 @@ enum tetgenmesh::interresult } else if ((point) searchtet->tet[6] == pa) { searchtet->ver = 7; } else { - assert((point) searchtet->tet[7] == pa); searchtet->ver = 0; } } @@ -14753,8 +14804,11 @@ enum tetgenmesh::interresult // Check if we have entered outside of the domain. if (pd == dummypoint) { // This is possible when the mesh is non-convex. - assert(nonconvex); - return ACROSSSUB; // Hit a bounday. + if (nonconvex) { + return ACROSSFACE; // return ACROSSSUB; // Hit a bounday. + } else { + terminatetetgen(this, 2); + } } // Now assume that the base face abc coincides with the horizon plane, @@ -14782,7 +14836,6 @@ enum tetgenmesh::interresult } } else { // Two tets, below horizon and below right, are viable. - //s = randomnation(2); if (randomnation(2)) { nextmove = HMOVE; } else { @@ -14792,7 +14845,6 @@ enum tetgenmesh::interresult } else { if (lori > 0) { // Two tets, below horizon and below left, are viable. - //s = randomnation(2); if (randomnation(2)) { nextmove = HMOVE; } else { @@ -14807,7 +14859,6 @@ enum tetgenmesh::interresult if (rori > 0) { if (lori > 0) { // Two tets, below right and below left, are viable. - //s = randomnation(2); if (randomnation(2)) { nextmove = RMOVE; } else { @@ -14830,7 +14881,7 @@ enum tetgenmesh::interresult } if (lori == 0) { // pa->'endpt' is COLLINEAR with pa->pc. - eprevesymself(*searchtet); // // [a,c,d] + eprevesymself(*searchtet); // [a,c,d] return ACROSSVERT; } // pa->'endpt' crosses the edge pb->pc. @@ -14870,7 +14921,9 @@ enum tetgenmesh::interresult fsymself(*searchtet); enextself(*searchtet); } - assert(org(*searchtet) == pa); + if (org(*searchtet) != pa) { + terminatetetgen(this, 2); + } pb = dest(*searchtet); pc = apex(*searchtet); @@ -14878,30 +14931,26 @@ enum tetgenmesh::interresult } -/////////////////////////////////////////////////////////////////////////////// -// // -// scoutsegment() Search an edge in the tetrahedralization. // -// // -// If the edge is found, it returns SHAREEDGE, and 'searchtet' returns the // -// edge from startpt to endpt. // -// // -// If the edge is missing, it returns either ACROSSEDGE or ACROSSFACE, which // -// indicates that the edge intersects an edge or a face. If 'refpt' is NULL,// -// 'searchtet' returns the edge or face. If 'refpt' is not NULL, it returns // -// a vertex which encroaches upon this edge, and 'searchtet' returns a tet // -// which containing 'refpt'. // -// // -// The following cases can happen when the input PLC is not valid. // -// - ACROSSVERT, the edge intersects a vertex return by the origin of // -// 'searchtet'. // -// - ACROSSSEG, the edge intersects a segment returned by 'searchtet'. // -// - ACROSSSUB, the edge intersects a subface returned by 'searchtet'. // -// // -/////////////////////////////////////////////////////////////////////////////// - -enum tetgenmesh::interresult - tetgenmesh::scoutsegment(point startpt, point endpt, triface* searchtet, - point* refpt, arraypool* intfacelist) +//============================================================================// +// // +// scoutsegment() Search an edge in the tetrahedralization. // +// // +// If the edge is found, it returns SHAREEDGE, and 'searchtet' returns the // +// edge from startpt to endpt. // +// // +// If the edge is missing, it returns either ACROSSEDGE or ACROSSFACE, which // +// indicates that the edge intersects an edge or a face. If 'refpt' is NULL, // +// 'searchtet' returns the edge or face. If 'refpt' is not NULL, it returns // +// a vertex which encroaches upon this edge, and 'searchtet' returns a tet // +// which containing 'refpt'. // +// // +// The parameter 'sedge' is used to report self-intersection. It is the // +// whose endpoints are 'startpt' and 'endpt'. It must not be a NULL. // +// // +//============================================================================// + +enum tetgenmesh::interresult tetgenmesh::scoutsegment(point startpt,point endpt, + face *sedge, triface* searchtet, point* refpt, arraypool* intfacelist) { point pd; enum interresult dir; @@ -14917,34 +14966,39 @@ enum tetgenmesh::interresult if (dir == ACROSSVERT) { pd = dest(*searchtet); if (pd == endpt) { - // The job is done. + if (issubseg(*searchtet)) { + //report_selfint_edge(startpt, endpt, sedge, searchtet, dir); + terminatetetgen(this, 3); + } return SHAREEDGE; } else { // A point is on the path. - // Let the origin of the searchtet be the vertex. - enextself(*searchtet); - if (refpt) *refpt = pd; + //report_selfint_edge(startpt, endpt, sedge, searchtet, dir); + terminatetetgen(this, 3); return ACROSSVERT; } - } // if (dir == ACROSSVERT) + } // dir is either ACROSSEDGE or ACROSSFACE. - enextesymself(*searchtet); // Go to the opposite face. fsymself(*searchtet); // Enter the adjacent tet. if (dir == ACROSSEDGE) { // Check whether two segments are intersecting. if (issubseg(*searchtet)) { - return ACROSSSEG; + //report_selfint_edge(startpt, endpt, sedge, searchtet, dir); + terminatetetgen(this, 3); } } else if (dir == ACROSSFACE) { if (checksubfaceflag) { // Check whether a segment and a subface are intersecting. if (issubface(*searchtet)) { - return ACROSSSUB; + //report_selfint_edge(startpt, endpt, sedge, searchtet, dir); + terminatetetgen(this, 3); } } + } else { + terminatetetgen(this, 2); } if (refpt == NULL) { @@ -14980,7 +15034,6 @@ enum tetgenmesh::interresult pd = oppo(*searchtet); - assert(pd != dummypoint); // SELF_CHECK // Stop if we meet 'endpt'. @@ -15013,8 +15066,7 @@ enum tetgenmesh::interresult pos = 0; } } - assert(dir != DISJOINT); // SELF_CHECK - } else { // dir == ACROSSEDGE + } else if (dir == ACROSSEDGE) { // Check the two opposite faces (of the edge) in 'searchtet'. for (i = 0; i < 2; i++) { if (i == 0) { @@ -15048,9 +15100,10 @@ enum tetgenmesh::interresult for (i = 0; i < pos; i++) { enextself(neightet); } - pd = org(neightet); - *refpt = pd; - // break; + eprev(neightet, *searchtet); + // dest(*searchtet) lies on the segment. + //report_selfint_edge(startpt, endpt, sedge, searchtet, dir); + terminatetetgen(this, 3); return ACROSSVERT; } else if (dir == ACROSSEDGE) { // Get the edge intersects with the segment. @@ -15064,15 +15117,19 @@ enum tetgenmesh::interresult if (dir == ACROSSEDGE) { // Check whether two segments are intersecting. if (issubseg(*searchtet)) { - return ACROSSSEG; - } + //report_selfint_edge(startpt, endpt, sedge, searchtet, dir); + terminatetetgen(this, 3); + } } else if (dir == ACROSSFACE) { if (checksubfaceflag) { // Check whether a segment and a subface are intersecting. if (issubface(*searchtet)) { - return ACROSSSUB; + //report_selfint_edge(startpt, endpt, sedge, searchtet, dir); + terminatetetgen(this, 3); } } + } else { + terminatetetgen(this, 2); } } // while (1) @@ -15088,14 +15145,14 @@ enum tetgenmesh::interresult return dir; } -/////////////////////////////////////////////////////////////////////////////// -// // -// getsteinerpointonsegment() Get a Steiner point on a segment. // -// // -// Return '1' if 'refpt' lies on an adjacent segment of this segment. Other- // -// wise, return '0'. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// getsteinerpointonsegment() Get a Steiner point on a segment. // +// // +// Return '1' if 'refpt' lies on an adjacent segment of this segment. Other- // +// wise, return '0'. // +// // +//============================================================================// int tetgenmesh::getsteinerptonsegment(face* seg, point refpt, point steinpt) { @@ -15166,19 +15223,19 @@ int tetgenmesh::getsteinerptonsegment(face* seg, point refpt, point steinpt) -/////////////////////////////////////////////////////////////////////////////// -// // -// delaunizesegments() Recover segments in a DT. // -// // -// All segments need to be recovered are in 'subsegstack' (Q). They will be // -// be recovered one by one (in a random order). // -// // -// Given a segment s in the Q, this routine first queries s in the DT, if s // -// matches an edge in DT, it is 'locked' at the edge. Otherwise, s is split // -// by inserting a new point p in both the DT and itself. The two new subseg- // -// ments of s are queued in Q. The process continues until Q is empty. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// delaunizesegments() Recover segments in a DT. // +// // +// All segments need to be recovered are in 'subsegstack' (Q). They will be // +// be recovered one by one (in a random order). // +// // +// Given a segment s in the Q, this routine first queries s in the DT, if s // +// matches an edge in DT, it is 'locked' at the edge. Otherwise, s is split // +// by inserting a new point p in both the DT and itself. The two new subseg- // +// ments of s are queued in Q. The process continues until Q is empty. // +// // +//============================================================================// void tetgenmesh::delaunizesegments() { @@ -15192,9 +15249,10 @@ void tetgenmesh::delaunizesegments() ivf.bowywat = 1; // Use Bowyer-Watson insertion. - ivf.assignmeshsize = b->metric; ivf.sloc = (int) ONEDGE; // on 'sseg'. ivf.sbowywat = 1; // Use Bowyer-Watson insertion. + ivf.assignmeshsize = b->metric; + ivf.smlenflag = useinsertradius; // Return the closet mesh vertex. // Loop until 'subsegstack' is empty. while (subsegstack->objects > 0l) { @@ -15210,23 +15268,18 @@ void tetgenmesh::delaunizesegments() } // Search the segment. - dir = scoutsegment(sorg(sseg), sdest(sseg), &searchtet, &refpt, NULL); + dir = scoutsegment(sorg(sseg), sdest(sseg), &sseg,&searchtet,&refpt,NULL); if (dir == SHAREEDGE) { // Found this segment, insert it. - if (!issubseg(searchtet)) { - // Let the segment remember an adjacent tet. - sstbond1(sseg, searchtet); - // Bond the segment to all tets containing it. - spintet = searchtet; - do { - tssbond1(spintet, sseg); - fnextself(spintet); - } while (spintet.tet != searchtet.tet); - } else { - // Collision! Maybe a bug. - assert(0); - } + // Let the segment remember an adjacent tet. + sstbond1(sseg, searchtet); + // Bond the segment to all tets containing it. + spintet = searchtet; + do { + tssbond1(spintet, sseg); + fnextself(spintet); + } while (spintet.tet != searchtet.tet); } else { if ((dir == ACROSSFACE) || (dir == ACROSSEDGE)) { // The segment is missing. Split it. @@ -15244,61 +15297,88 @@ void tetgenmesh::delaunizesegments() // The new point has been inserted. st_segref_count++; if (steinerleft > 0) steinerleft--; + if (useinsertradius) { + //save_segmentpoint_insradius(newpt, ivf.parentpt, ivf.smlen); + } } else { - assert (ivf.iloc == (enum locateresult) NEARVERTEX); - terminatetetgen(this, 4); + if (ivf.iloc == (int) NEARVERTEX) { + // The new point (in the segment) is very close to an existing + // vertex -- a small feature is detected. + point nearpt = org(searchtet); + if (pointtype(nearpt) == FREESEGVERTEX) { + face parentseg; + sdecode(point2sh(nearpt), parentseg); + point p1 = farsorg(sseg); + point p2 = farsdest(sseg); + point p3 = farsorg(parentseg); + point p4 = farsdest(parentseg); + printf("Two segments are very close to each other.\n"); + printf(" Segment 1: [%d, %d] #%d\n", pointmark(p1), + pointmark(p2), shellmark(sseg)); + printf(" Segment 2: [%d, %d] #%d\n", pointmark(p3), + pointmark(p4), shellmark(parentseg)); + terminatetetgen(this, 4); + } else { + terminatetetgen(this, 2); + } + } else if (ivf.iloc == (int) ONVERTEX) { + // The new point (in the segment) is coincident with an existing + // vertex -- a self-intersection is detected. + eprevself(searchtet); + //report_selfint_edge(sorg(sseg), sdest(sseg), &sseg, &searchtet, + // ACROSSVERT); + terminatetetgen(this, 3); + } else { + // An unknown case. Report a bug. + terminatetetgen(this, 2); + } } } else { - // Indicate it is an input problem. - terminatetetgen(this, 3); + // An unknown case. Report a bug. + terminatetetgen(this, 2); } } } // while } -/////////////////////////////////////////////////////////////////////////////// -// // -// scoutsubface() Search subface in the tetrahedralization. // -// // -// 'searchsh' is searched in T. If it exists, it is 'locked' at the face in // -// T. 'searchtet' refers to the face. Otherwise, it is missing. // -// // -// The return value indicates one of the following cases: // -// - SHAREFACE, 'searchsh' exists and is inserted in T. // -// - COLLISIONFACE, 'searchsh' exists in T, but it conflicts with another // -// subface which was inserted earlier. It is not inserted. // -// // -/////////////////////////////////////////////////////////////////////////////// - -enum tetgenmesh::interresult - tetgenmesh::scoutsubface(face* searchsh, triface* searchtet) +//============================================================================// +// // +// scoutsubface() Search subface in the tetrahedralization. // +// // +// 'searchsh' is searched in T. If it exists, it is 'locked' at the face in // +// T. 'searchtet' refers to the face. Otherwise, it is missing. // +// // +// The parameter 'shflag' indicates whether 'searchsh' is a boundary face or // +// not. It is possible that 'searchsh' is a temporarily subface that is used // +// as a cavity boundary face. // +// // +//============================================================================// + +int tetgenmesh::scoutsubface(face* searchsh, triface* searchtet, int shflag) { - triface spintet; - point pa, pb, pc; - enum interresult dir; - int t1ver; - - pa = sorg(*searchsh); - pb = sdest(*searchsh); - + point pa = sorg(*searchsh); + point pb = sdest(*searchsh); // Get a tet whose origin is a. point2tetorg(pa, *searchtet); // Search the edge [a,b]. - dir = finddirection(searchtet, pb); + enum interresult dir = finddirection(searchtet, pb); if (dir == ACROSSVERT) { // Check validity of a PLC. if (dest(*searchtet) != pb) { - // A vertex lies on the search edge. - enextself(*searchtet); - // It is possible a PLC self-intersection problem. - terminatetetgen(this, 3); - return TOUCHEDGE; + if (shflag) { + // A vertex lies on the search edge. + //report_selfint_edge(pa, pb, searchsh, searchtet, dir); + terminatetetgen(this, 3); + } else { + terminatetetgen(this, 2); + } } + int t1ver; // The edge exists. Check if the face exists. - pc = sapex(*searchsh); + point pc = sapex(*searchsh); // Searchtet holds edge [a,b]. Search a face with apex c. - spintet = *searchtet; + triface spintet = *searchtet; while (1) { if (apex(spintet) == pc) { // Found a face matching to 'searchsh'! @@ -15309,19 +15389,9 @@ enum tetgenmesh::interresult sesymself(*searchsh); tsbond(spintet, *searchsh); *searchtet = spintet; - return SHAREFACE; + return 1; } else { - // Another subface is already inserted. - face checksh; - tspivot(spintet, checksh); - assert(checksh.sh != searchsh->sh); // SELF_CHECK - // This is possibly an input problem, i.e., two facets overlap. - // Report this problem and exit. - printf("Warning: Found two facets nearly overlap.\n"); - terminatetetgen(this, 5); - // unifysubfaces(&checksh, searchsh); - *searchtet = spintet; - return COLLISIONFACE; + terminatetetgen(this, 2); } } fnextself(spintet); @@ -15329,28 +15399,27 @@ enum tetgenmesh::interresult } } - // dir is either ACROSSEDGE or ACROSSFACE. - return dir; + return 0; } -/////////////////////////////////////////////////////////////////////////////// -// // -// formregion() Form the missing region of a missing subface. // -// // -// 'missh' is a missing subface. From it we form a missing region R which is // -// a connected region formed by a set of missing subfaces of a facet. // -// Comment: There should be no segment inside R. // -// // -// 'missingshs' returns the list of subfaces in R. All subfaces in this list // -// are oriented as the 'missh'. 'missingshbds' returns the list of boundary // -// edges (tetrahedral handles) of R. 'missingshverts' returns all vertices // -// of R. They are all pmarktested. // -// // -// Except the first one (which is 'missh') in 'missingshs', each subface in // -// this list represents an internal edge of R, i.e., it is missing in the // -// tetrahedralization. Since R may contain interior vertices, not all miss- // -// ing edges can be found by this way. // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// formregion() Form the missing region of a missing subface. // +// // +// 'missh' is a missing subface. From it we form a missing region R which is // +// a connected region formed by a set of missing subfaces of a facet. // +// Comment: There should be no segment inside R. // +// // +// 'missingshs' returns the list of subfaces in R. All subfaces in this list // +// are oriented as the 'missh'. 'missingshbds' returns the list of boundary // +// edges (tetrahedral handles) of R. 'missingshverts' returns all vertices // +// of R. They are all pmarktested. // +// // +// Except the first one (which is 'missh') in 'missingshs', each subface in // +// this list represents an internal edge of R, i.e., it is missing in the // +// tetrahedralization. Since R may contain interior vertices, not all miss- // +// ing edges can be found by this way. // +//============================================================================// void tetgenmesh::formregion(face* missh, arraypool* missingshs, arraypool* missingshbds, arraypool* missingshverts) @@ -15387,8 +15456,9 @@ void tetgenmesh::formregion(face* missh, arraypool* missingshs, } } else { if (dest(searchtet) != pb) { - // This might be a self-intersection problem. - terminatetetgen(this, 3); + // Report a PLC problem. + //report_selfint_edge(pa, pb, missh, &searchtet, dir); + terminatetetgen(this, 3); } } // Collect the vertices of R. @@ -15446,26 +15516,25 @@ void tetgenmesh::formregion(face* missh, arraypool* missingshs, } } -/////////////////////////////////////////////////////////////////////////////// -// // -// scoutcrossedge() Search an edge that crosses the missing region. // -// // -// Return 1 if a crossing edge is found. It is returned by 'crosstet'. More- // -// over, the edge is oriented such that its origin lies below R. Return 0 // -// if no such edge is found. // -// // -// Assumption: All vertices of the missing region are marktested. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// scoutcrossedge() Search an edge that crosses the missing region. // +// // +// Return 1 if a crossing edge is found. It is returned by 'crosstet'. More- // +// over, the edge is oriented such that its origin lies below R. Return 0 // +// if no such edge is found. // +// // +// Assumption: All vertices of the missing region are marktested. // +// // +//============================================================================// int tetgenmesh::scoutcrossedge(triface& crosstet, arraypool* missingshbds, arraypool* missingshs) { - triface searchtet, spintet; - face *parysh; + triface searchtet, spintet, neightet; + face oldsh, searchsh, *parysh; face neighseg; point pa, pb, pc, pd, pe; - enum interresult dir; REAL ori; int types[2], poss[4]; int searchflag, interflag; @@ -15474,7 +15543,113 @@ int tetgenmesh::scoutcrossedge(triface& crosstet, arraypool* missingshbds, searchflag = 0; - for (j = 0; j < missingshbds->objects && !searchflag; j++) { + // Search the first new subface to fill the region. + for (i = 0; i < missingshbds->objects && !searchflag; i++) { + parysh = (face *) fastlookup(missingshbds, i); + sspivot(*parysh, neighseg); + sstpivot1(neighseg, searchtet); + if (org(searchtet) != sorg(*parysh)) { + esymself(searchtet); + } + spintet = searchtet; + while (1) { + if (pmarktested(apex(spintet))) { + // A possible interior face. + neightet = spintet; + oldsh = *parysh; + // Try to recover an interior edge. + for (j = 0; j < 2; j++) { + enextself(neightet); + if (!issubseg(neightet)) { + if (j == 0) { + senext(oldsh, searchsh); + } else { + senext2(oldsh, searchsh); + sesymself(searchsh); + esymself(neightet); + } + // Calculate a lifted point. + pa = sorg(searchsh); + pb = sdest(searchsh); + pc = sapex(searchsh); + pd = dest(neightet); + calculateabovepoint4(pa, pb, pc, pd); + // The lifted point must lie above 'searchsh'. + ori = orient3d(pa, pb, pc, dummypoint); + if (ori > 0) { + sesymself(searchsh); + senextself(searchsh); + } else if (ori == 0) { + terminatetetgen(this, 2); + } + if (sscoutsegment(&searchsh,dest(neightet),0,0,1)==SHAREEDGE) { + // Insert a temp segment to protect the recovered edge. + face tmpseg; + makeshellface(subsegs, &tmpseg); + ssbond(searchsh, tmpseg); + spivotself(searchsh); + ssbond(searchsh, tmpseg); + // Recover locally Delaunay edges. + lawsonflip(); + // Delete the tmp segment. + spivot(tmpseg, searchsh); + ssdissolve(searchsh); + spivotself(searchsh); + ssdissolve(searchsh); + shellfacedealloc(subsegs, tmpseg.sh); + searchflag = 1; + } else { + // Undo the performed flips. + if (flipstack != NULL) { + lawsonflip(); + } + } + break; + } // if (!issubseg(neightet)) + } // j + if (searchflag) break; + } // if (pmarktested(apex(spintet))) + fnextself(spintet); + if (spintet.tet == searchtet.tet) break; + } + } // i + + if (searchflag) { + // Remove faked segments. + face checkseg; + // Remark: We should not use the array 'missingshbds', since the flips may + // change the subfaces. We search them from the subfaces in R. + for (i = 0; i < missingshs->objects; i++) { + parysh = (face *) fastlookup(missingshs, i); + oldsh = *parysh; + for (j = 0; j < 3; j++) { + if (isshsubseg(oldsh)) { + sspivot(oldsh, checkseg); + if (sinfected(checkseg)) { + // It's a faked segment. Delete it. + sstpivot1(checkseg, searchtet); + spintet = searchtet; + while (1) { + tssdissolve1(spintet); + fnextself(spintet); + if (spintet.tet == searchtet.tet) break; + } + shellfacedealloc(subsegs, checkseg.sh); + ssdissolve(oldsh); + } + } + senextself(oldsh); + } // j + } + + fillregioncount++; + + return 0; + } // if (i < missingshbds->objects) + + searchflag = -1; + + for (j = 0; j < missingshbds->objects && (searchflag == -1); j++) { parysh = (face *) fastlookup(missingshbds, j); sspivot(*parysh, neighseg); sstpivot1(neighseg, searchtet); @@ -15498,25 +15673,42 @@ int tetgenmesh::scoutcrossedge(triface& crosstet, arraypool* missingshbds, if (interflag > 0) { if (interflag == 2) { // They intersect at a single point. - dir = (enum interresult) types[0]; - if ((dir == ACROSSFACE) || (dir == ACROSSEDGE)) { - //pos = poss[0]; + if ((types[0] == (int) ACROSSFACE) || + (types[0] == (int) ACROSSEDGE)) { // Go to the crossing edge [d,e,#,#]. edestoppo(spintet, crosstet); // // [d,e,#,#]. - // Check if it is a segment. if (issubseg(crosstet)) { - //face checkseg; - //tsspivot1(crosstet, checkseg); - //reportselfintersect(&checkseg, parysh); + // It is a segment. Report a PLC problem. + //report_selfint_face(pa, pb, pc, parysh, &crosstet, + // interflag, types, poss); terminatetetgen(this, 3); - } + } else { + triface chkface = crosstet; + while (1) { + if (issubface(chkface)) break; + fsymself(chkface); + if (chkface.tet == crosstet.tet) break; + } + if (issubface(chkface)) { + // Two subfaces are intersecting. + //report_selfint_face(pa, pb, pc, parysh, &chkface, + // interflag, types, poss); + terminatetetgen(this, 3); + } + } // Adjust the edge such that d lies below [a,b,c]. ori = orient3d(pa, pb, pc, pd); - assert(ori != 0); if (ori < 0) { esymself(crosstet); } searchflag = 1; + } else { + // An improper intersection type, ACROSSVERT, TOUCHFACE, + // TOUCHEDGE, SHAREVERT, ... + // Maybe it is due to a PLC problem. + //report_selfint_face(pa, pb, pc, parysh, &crosstet, + // interflag, types, poss); + terminatetetgen(this, 3); } } break; @@ -15535,28 +15727,28 @@ int tetgenmesh::scoutcrossedge(triface& crosstet, arraypool* missingshbds, return searchflag; } -/////////////////////////////////////////////////////////////////////////////// -// // -// formcavity() Form the cavity of a missing region. // -// // -// The missing region R is formed by a set of missing subfaces 'missingshs'. // -// In the following, we assume R is horizontal and oriented. (All subfaces // -// of R are oriented in the same way.) 'searchtet' is a tetrahedron [d,e,#, // -// #] which intersects R in its interior, where the edge [d,e] intersects R, // -// and d lies below R. // -// // -// 'crosstets' returns the set of crossing tets. Every tet in it has the // -// form [d,e,#,#] where [d,e] is a crossing edge, and d lies below R. The // -// set of tets form the cavity C, which is divided into two parts by R, one // -// at top and one at bottom. 'topfaces' and 'botfaces' return the upper and // -// lower boundary faces of C. 'toppoints' contains vertices of 'crosstets' // -// in the top part of C, and so does 'botpoints'. Both 'toppoints' and // -// 'botpoints' contain vertices of R. // -// // -// Important: This routine assumes all vertices of the facet containing this // -// subface are marked, i.e., pmarktested(p) returns true. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// formcavity() Form the cavity of a missing region. // +// // +// The missing region R is formed by a set of missing subfaces 'missingshs'. // +// In the following, we assume R is horizontal and oriented. (All subfaces // +// of R are oriented in the same way.) 'searchtet' is a tetrahedron [d,e,#, // +// #] which intersects R in its interior, where the edge [d,e] intersects R, // +// and d lies below R. // +// // +// 'crosstets' returns the set of crossing tets. Every tet in it has the // +// form [d,e,#,#] where [d,e] is a crossing edge, and d lies below R. The // +// set of tets form the cavity C, which is divided into two parts by R, one // +// at top and one at bottom. 'topfaces' and 'botfaces' return the upper and // +// lower boundary faces of C. 'toppoints' contains vertices of 'crosstets' // +// in the top part of C, and so does 'botpoints'. Both 'toppoints' and // +// 'botpoints' contain vertices of R. // +// // +// Important: This routine assumes all vertices of the facet containing this // +// subface are marked, i.e., pmarktested(p) returns true. // +// // +//============================================================================// bool tetgenmesh::formcavity(triface* searchtet, arraypool* missingshs, arraypool* crosstets, arraypool* topfaces, @@ -15564,12 +15756,11 @@ bool tetgenmesh::formcavity(triface* searchtet, arraypool* missingshs, arraypool* botpoints) { arraypool *crossedges; - triface spintet, neightet, *parytet; + triface spintet, neightet, chkface, *parytet; face *parysh = NULL; point pa, pd, pe, *parypt; - enum interresult dir; bool testflag, invalidflag; - int types[2], poss[4]; + int intflag, types[2], poss[4]; int t1ver; int i, j, k; @@ -15585,14 +15776,12 @@ bool tetgenmesh::formcavity(triface* searchtet, arraypool* missingshs, *parytet = *searchtet; invalidflag = 0; - // Collect all crossing tets. Each cross tet is saved in the standard // form [d,e,#,#], where [d,e] is a crossing edge, d lies below R. // NEITHER d NOR e is a vertex of R (!pmarktested). - for (i = 0; i < crossedges->objects; i++) { + for (i = 0; i < crossedges->objects && !invalidflag; i++) { // Get a crossing edge [d,e,#,#]. searchtet = (triface *) fastlookup(crossedges, i); - // Sort vertices into the bottom and top arrays. pd = org(*searchtet); if (!pinfected(pd)) { @@ -15628,8 +15817,8 @@ bool tetgenmesh::formcavity(triface* searchtet, arraypool* missingshs, if (pa != dummypoint) { if (!pmarktested(pa)) { // There exists a crossing edge, either [e,a] or [a,d]. First check - // if the crossing edge has already be added, i.e., check if a - // tetrahedron at this edge is marked. + // if the crossing edge has already be added, i.e.,to check if one + // of the tetrahedron at this edge has been marked. testflag = true; for (j = 0; j < 2 && testflag; j++) { if (j == 0) { @@ -15655,57 +15844,73 @@ bool tetgenmesh::formcavity(triface* searchtet, arraypool* missingshs, pe = dest(spintet); for (k = 0; k < missingshs->objects; k++) { parysh = (face *) fastlookup(missingshs, k); - if (tri_edge_test(sorg(*parysh), sdest(*parysh), sapex(*parysh), - pe, pa, NULL, 1, types, poss)) { + intflag = tri_edge_test(sorg(*parysh), sdest(*parysh), + sapex(*parysh), pe, pa, NULL, 1, types, poss); + if (intflag > 0) { // Found intersection. 'a' lies below R. - enext(spintet, neightet); - dir = (enum interresult) types[0]; - if ((dir == ACROSSFACE) || (dir == ACROSSEDGE)) { - // A valid intersection. - } else { - // A non-valid intersection. Maybe a PLC problem. - invalidflag = 1; - } + if (intflag == 2) { + enext(spintet, neightet); + if ((types[0] == (int) ACROSSFACE) || + (types[0] == (int) ACROSSEDGE)) { + // Only this case is valid. + } else { + // A non-valid intersection. Maybe a PLC problem. + invalidflag = 1; + } + } else { + // Coplanar intersection. Maybe a PLC problem. + invalidflag = 1; + } break; } - if (tri_edge_test(sorg(*parysh), sdest(*parysh), sapex(*parysh), - pa, pd, NULL, 1, types, poss)) { + intflag = tri_edge_test(sorg(*parysh), sdest(*parysh), + sapex(*parysh), pa, pd, NULL, 1, types, poss); + if (intflag > 0) { // Found intersection. 'a' lies above R. - eprev(spintet, neightet); - dir = (enum interresult) types[0]; - if ((dir == ACROSSFACE) || (dir == ACROSSEDGE)) { - // A valid intersection. - } else { - // A non-valid intersection. Maybe a PLC problem. - invalidflag = 1; - } + if (intflag == 2) { + eprev(spintet, neightet); + if ((types[0] == (int) ACROSSFACE) || + (types[0] == (int) ACROSSEDGE)) { + // Only this case is valid. + } else { + // A non-valid intersection. Maybe a PLC problem. + invalidflag = 1; + } + } else { + // Coplanar intersection. Maybe a PLC problem. + invalidflag = 1; + } break; } } // k if (k < missingshs->objects) { // Found a pair of triangle - edge intersection. if (invalidflag) { - if (!b->quiet) { - printf("Warning: A non-valid facet - edge intersection\n"); - printf(" subface: (%d, %d, %d) edge: (%d, %d)\n", - pointmark(sorg(*parysh)), pointmark(sdest(*parysh)), - pointmark(sapex(*parysh)), pointmark(org(neightet)), - pointmark(dest(neightet))); - } - // It may be a PLC problem. - terminatetetgen(this, 3); + break; // the while (1) loop } // Adjust the edge direction, so that its origin lies below R, // and its destination lies above R. esymself(neightet); - // Check if this edge is a segment. + // This edge may be a segment. if (issubseg(neightet)) { - // Invalid PLC! - //face checkseg; - //tsspivot1(neightet, checkseg); - //reportselfintersect(&checkseg, parysh); + //report_selfint_face(sorg(*parysh), sdest(*parysh), + // sapex(*parysh),parysh,&neightet,intflag,types,poss); terminatetetgen(this, 3); } + // Check if it is an edge of a subface. + chkface = neightet; + while (1) { + if (issubface(chkface)) break; + fsymself(chkface); + if (chkface.tet == neightet.tet) break; + } + if (issubface(chkface)) { + // Two subfaces are intersecting. + //report_selfint_face(sorg(*parysh), sdest(*parysh), + // sapex(*parysh),parysh,&chkface,intflag,types,poss); + terminatetetgen(this, 3); + } + // Mark this edge to avoid testing it again. markedge(neightet); crossedges->newindex((void **) &parytet); @@ -15713,47 +15918,20 @@ bool tetgenmesh::formcavity(triface* searchtet, arraypool* missingshs, } else { // No intersection is found. It may be a PLC problem. invalidflag = 1; - // Split the subface intersecting [d,e]. - for (k = 0; k < missingshs->objects; k++) { - parysh = (face *) fastlookup(missingshs, k); - // Test if this face intersects [e,a]. - if (tri_edge_test(sorg(*parysh),sdest(*parysh),sapex(*parysh), - pd, pe, NULL, 1, types, poss)) { - break; - } - } // k - if (k == missingshs->objects) { - // Not found such an edge. - // Arbitrarily choose an edge (except the first) to split. - k = randomnation(missingshs->objects - 1); - parysh = (face *) fastlookup(missingshs, k + 1); - } - recentsh = *parysh; - recenttet = spintet; // For point location. break; // the while (1) loop } // if (k == missingshs->objects) } // if (testflag) - } // if (!pmarktested(pa) || b->psc) + } } // if (pa != dummypoint) // Go to the next crossing tet. fnextself(spintet); if (spintet.tet == searchtet->tet) break; } // while (1) - - //if (b->psc) { - if (invalidflag) break; - //} } // i - if (b->verbose > 2) { - printf(" Formed cavity: %ld (%ld) cross tets (edges).\n", - crosstets->objects, crossedges->objects); - } - // Unmark all marked edges. for (i = 0; i < crossedges->objects; i++) { searchtet = (triface *) fastlookup(crossedges, i); - assert(edgemarked(*searchtet)); // SELF_CHECK unmarkedge(*searchtet); } crossedges->restart(); @@ -15784,6 +15962,10 @@ bool tetgenmesh::formcavity(triface* searchtet, arraypool* missingshs, return false; } + if (b->verbose > 2) { + printf(" Formed cavity: %ld (%ld) cross tets (edges).\n", + crosstets->objects, crossedges->objects); + } // Collect the top and bottom faces and the middle vertices. Since all top // and bottom vertices have been infected. Uninfected vertices must be @@ -15852,22 +16034,22 @@ bool tetgenmesh::formcavity(triface* searchtet, arraypool* missingshs, return true; } -/////////////////////////////////////////////////////////////////////////////// -// // -// delaunizecavity() Fill a cavity by Delaunay tetrahedra. // -// // -// The cavity C to be tetrahedralized is the top or bottom part of a whole // -// cavity. 'cavfaces' contains the boundary faces of C. NOTE: faces in 'cav- // -// faces' do not form a closed polyhedron. The "open" side are subfaces of // -// the missing facet. These faces will be recovered later in fillcavity(). // -// // -// This routine first constructs the DT of the vertices. Then it identifies // -// the half boundary faces of the cavity in DT. Possiblely the cavity C will // -// be enlarged. // -// // -// The DT is returned in 'newtets'. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// delaunizecavity() Fill a cavity by Delaunay tetrahedra. // +// // +// The cavity C to be tetrahedralized is the top or bottom part of a whole // +// cavity. 'cavfaces' contains the boundary faces of C. NOTE: faces in 'cav- // +// faces' do not form a closed polyhedron. The "open" side are subfaces of // +// the missing facet. These faces will be recovered later in fillcavity(). // +// // +// This routine first constructs the DT of the vertices. Then it identifies // +// the half boundary faces of the cavity in DT. Possiblely the cavity C will // +// be enlarged. // +// // +// The DT is returned in 'newtets'. // +// // +//============================================================================// void tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, arraypool *cavshells, arraypool *newtets, @@ -15876,7 +16058,6 @@ void tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, triface searchtet, neightet, *parytet, *parytet1; face tmpsh, *parysh; point pa, pb, pc, pd, pt[3], *parypt; - enum interresult dir; insertvertexflags ivf; REAL ori; long baknum, bakhullsize; @@ -15933,7 +16114,6 @@ void tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, } if (pd != NULL) break; } - assert(i < cavfaces->objects); // SELF_CHECK // Create an init DT. initialdelaunay(pa, pb, pc, pd); @@ -15967,8 +16147,7 @@ void tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, setshvertices(tmpsh, pt[0], pt[1], pt[2]); // Insert tmpsh in DT. searchtet.tet = NULL; - dir = scoutsubface(&tmpsh, &searchtet); - if (dir == SHAREFACE) { + if (scoutsubface(&tmpsh, &searchtet, 0)) { // shflag = 0 // Inserted! 'tmpsh' must face toward the inside of the cavity. // Remember the boundary tet (outside the cavity) in tmpsh // (use the adjacent tet slot). @@ -16087,18 +16266,18 @@ void tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, b->plc = 1; } -/////////////////////////////////////////////////////////////////////////////// -// // -// fillcavity() Fill new tets into the cavity. // -// // -// The new tets are stored in two disjoint sets(which share the same facet). // -// 'topfaces' and 'botfaces' are the boundaries of these two sets, respect- // -// ively. 'midfaces' is empty on input, and will store faces in the facet. // -// // -// Important: This routine assumes all vertices of the missing region R are // -// marktested, i.e., pmarktested(p) returns true. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// fillcavity() Fill new tets into the cavity. // +// // +// The new tets are stored in two disjoint sets(which share the same facet). // +// 'topfaces' and 'botfaces' are the boundaries of these two sets, respect- // +// ively. 'midfaces' is empty on input, and will store faces in the facet. // +// // +// Important: This routine assumes all vertices of the missing region R are // +// marktested, i.e., pmarktested(p) returns true. // +// // +//============================================================================// bool tetgenmesh::fillcavity(arraypool* topshells, arraypool* botshells, arraypool* midfaces, arraypool* missingshs, @@ -16178,11 +16357,12 @@ bool tetgenmesh::fillcavity(arraypool* topshells, arraypool* botshells, // The face lies in the interior of R. // Get the tet (in topnewtets) which lies above R. ori = orient3d(pa, pb, pc, pd); - assert(ori != 0); if (ori < 0) { fsymself(toptet); pa = org(toptet); pb = dest(toptet); + } else if (ori == 0) { + terminatetetgen(this, 2); } // Search the face [b,a,c] in 'botnewtets'. for (j = 0; j < botnewtets->objects; j++) { @@ -16246,8 +16426,6 @@ bool tetgenmesh::fillcavity(arraypool* topshells, arraypool* botshells, for (i = 0; i < midfaces->objects && mflag; i++) { // Get a matched middle face [a, b, c] midface = (triface *) fastlookup(midfaces, i); - // The tet must be a new created tet (marktested). - assert(marktested(*midface)); // SELF_CHECK // Check the neighbors at the edges of this face. for (j = 0; j < 3 && mflag; j++) { toptet = *midface; @@ -16260,7 +16438,7 @@ bool tetgenmesh::fillcavity(arraypool* topshells, arraypool* botshells, break; // Find a subface. } if (pc == dummypoint) { - assert(0); // Check this case. + terminatetetgen(this, 2); // Check this case. break; // Find a subface. } // Go to the adjacent tet. @@ -16272,7 +16450,6 @@ bool tetgenmesh::fillcavity(arraypool* topshells, arraypool* botshells, } } if (!bflag) { - // assert(marktested(toptet)); // SELF_CHECK if (!facemarked(toptet)) { fsym(*midface, bottet); spintet = bottet; @@ -16299,6 +16476,10 @@ bool tetgenmesh::fillcavity(arraypool* topshells, arraypool* botshells, midfaces->newindex((void **) &parytet); *parytet = toptet; } + else { + // The 'bottet' is not inside the cavity! + terminatetetgen(this, 2); // Check this case + } } else { // mflag == false // Adjust 'toptet' and 'bottet' to be the crossing edges. fsym(*midface, bottet); @@ -16322,12 +16503,11 @@ bool tetgenmesh::fillcavity(arraypool* topshells, arraypool* botshells, eprevself(bottet); // [d,b] } else { // b,c,#,and d are coplanar!. - assert(0); + terminatetetgen(this, 2); //assert(0); } break; // Not matched } fsymself(bottet); - assert (bottet.tet != spintet.tet); } } // if (!mflag) } // if (!facemarked(toptet)) @@ -16358,6 +16538,9 @@ bool tetgenmesh::fillcavity(arraypool* topshells, arraypool* botshells, if (checkconstraints) { setareabound(newsh, areabound(oldsh)); } + if (useinsertradius) { + setfacetindex(newsh, getfacetindex(oldsh)); + } // Connect the new subface to adjacent tets. tsbond(*midface, newsh); fsym(*midface, neightet); @@ -16381,7 +16564,6 @@ bool tetgenmesh::fillcavity(arraypool* topshells, arraypool* botshells, tsspivot1(searchtet, checkseg); if (checkseg.sh != NULL) { // It's a bdry edge of R. - assert(!infected(searchtet)); // It must not be a cavity tet. // Get the old subface. checkseg.shver = 0; spivot(checkseg, oldsh); @@ -16429,7 +16611,6 @@ bool tetgenmesh::fillcavity(arraypool* topshells, arraypool* botshells, break; } fnextself(searchtet); - assert(searchtet.tet != midface->tet); } // while (1) } // if (casout.sh == NULL) enextself(*midface); @@ -16453,6 +16634,7 @@ bool tetgenmesh::fillcavity(arraypool* topshells, arraypool* botshells, // Search an edge in R which is either [a,b] or [c,d]. // Reminder: Subfaces in this list 'missingshs', except the first // one, represents an interior edge of R. + parysh = NULL; // Avoid a warning in MSVC for (i = 1; i < missingshs->objects; i++) { parysh = (face *) fastlookup(missingshs, i); if (((sorg(*parysh) == pa) && (sdest(*parysh) == pb)) || @@ -16464,8 +16646,10 @@ bool tetgenmesh::fillcavity(arraypool* topshells, arraypool* botshells, // Found. Return it. recentsh = *parysh; } else { - assert(0); + terminatetetgen(this, 2); //assert(0); } + } else { + //terminatetetgen(this, 2); // Report a bug } } @@ -16493,11 +16677,11 @@ bool tetgenmesh::fillcavity(arraypool* topshells, arraypool* botshells, return mflag; } -/////////////////////////////////////////////////////////////////////////////// -// // -// carvecavity() Delete old tets and outer new tets of the cavity. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// carvecavity() Delete old tets and outer new tets of the cavity. // +// // +//============================================================================// void tetgenmesh::carvecavity(arraypool *crosstets, arraypool *topnewtets, arraypool *botnewtets) @@ -16585,7 +16769,6 @@ void tetgenmesh::carvecavity(arraypool *crosstets, arraypool *topnewtets, if (!infected(neightet)) { // Found an outside tet. Re-connect this subface to a new tet. fsym(neightet, newtet); - assert(marktested(newtet)); // It's a new tet. sesymself(*parysh); tsbond(newtet, *parysh); } @@ -16700,11 +16883,11 @@ void tetgenmesh::carvecavity(arraypool *crosstets, arraypool *topnewtets, } } -/////////////////////////////////////////////////////////////////////////////// -// // -// restorecavity() Reconnect old tets and delete new tets of the cavity. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// restorecavity() Reconnect old tets and delete new tets of the cavity. // +// // +//============================================================================// void tetgenmesh::restorecavity(arraypool *crosstets, arraypool *topnewtets, arraypool *botnewtets, arraypool *missingshbds) @@ -16719,7 +16902,6 @@ void tetgenmesh::restorecavity(arraypool *crosstets, arraypool *topnewtets, // Reconnect crossing tets to cavity boundary. for (i = 0; i < crosstets->objects; i++) { parytet = (triface *) fastlookup(crosstets, i); - assert(infected(*parytet)); // SELF_CHECK parytet->ver = 0; for (parytet->ver = 0; parytet->ver < 4; parytet->ver++) { fsym(*parytet, neightet); @@ -16743,13 +16925,14 @@ void tetgenmesh::restorecavity(arraypool *crosstets, arraypool *topnewtets, } // Remember a live handle. - recenttet = * (triface *) fastlookup(crosstets, 0); + if (crosstets->objects > 0) { + recenttet = * (triface *) fastlookup(crosstets, 0); + } // Delete faked segments. for (i = 0; i < missingshbds->objects; i++) { parysh = (face *) fastlookup(missingshbds, i); sspivot(*parysh, checkseg); - assert(checkseg.sh != NULL); if (checkseg.sh[3] != NULL) { if (sinfected(checkseg)) { // It's a faked segment. Delete it. @@ -16787,14 +16970,14 @@ void tetgenmesh::restorecavity(arraypool *crosstets, arraypool *topnewtets, } } -/////////////////////////////////////////////////////////////////////////////// -// // -// flipcertify() Insert a crossing face into priority queue. // -// // -// A crossing face of a facet must have at least one top and one bottom ver- // -// tex of the facet. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// flipcertify() Insert a crossing face into priority queue. // +// // +// A crossing face of a facet must have at least one top and one bottom ver- // +// tex of the facet. // +// // +//============================================================================// void tetgenmesh::flipcertify(triface *chkface,badface **pqueue,point plane_pa, point plane_pb, point plane_pc) @@ -16838,7 +17021,6 @@ void tetgenmesh::flipcertify(triface *chkface,badface **pqueue,point plane_pa, tspivot(*chkface, checksh); if (checksh.sh == NULL) { insph = insphere_s(p[1], p[0], p[2], p[3], p[4]); - assert(insph != 0); if (insph > 0) { // Add the face into queue. if (b->verbose > 2) { @@ -16865,7 +17047,6 @@ void tetgenmesh::flipcertify(triface *chkface,badface **pqueue,point plane_pa, } // if (insph > 0) } // if (checksh.sh == NULL) } - //return; } return; // Test: omit this face. } @@ -16876,27 +17057,13 @@ void tetgenmesh::flipcertify(triface *chkface,badface **pqueue,point plane_pa, // A top point has a positive weight. w[i] = orient3dfast(plane_pa, plane_pb, plane_pc, p[i]); if (w[i] < 0) w[i] = -w[i]; - assert(w[i] != 0); } else { w[i] = 0; } } - // Make sure orient3d(p[1], p[0], p[2], p[3]) > 0; - // Hence if (insphere(p[1], p[0], p[2], p[3], p[4]) > 0) means that - // p[4] lies inside the circumsphere of p[1], p[0], p[2], p[3]. - // The same if orient4d(p[1], p[0], p[2], p[3], p[4]) > 0 means that - // p[4] lies below the oriented hyperplane passing through - // p[1], p[0], p[2], p[3]. - insph = insphere(p[1], p[0], p[2], p[3], p[4]); ori4 = orient4d(p[1], p[0], p[2], p[3], p[4], w[1], w[0], w[2], w[3], w[4]); - - if (b->verbose > 2) { - printf(" Heights: (%g, %g, %g, %g, %g)\n", w[0],w[1],w[2],w[3],w[4]); - printf(" Insph: %g, ori4: %g, tau = %g\n", insph, ori4, -insph/ori4); - } - if (ori4 > 0) { // Add the face into queue. if (b->verbose > 2) { @@ -16947,19 +17114,19 @@ void tetgenmesh::flipcertify(triface *chkface,badface **pqueue,point plane_pa, } } -/////////////////////////////////////////////////////////////////////////////// -// // -// flipinsertfacet() Insert a facet into a CDT by flips. // -// // -// The algorithm is described in Shewchuk's paper "Updating and Constructing // -// Constrained Delaunay and Constrained Regular Triangulations by Flips", in // -// Proc. 19th Ann. Symp. on Comput. Geom., 86--95, 2003. // -// // -// 'crosstets' contains the set of crossing tetrahedra (infected) of the // -// facet. 'toppoints' and 'botpoints' are points lies above and below the // -// facet, not on the facet. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// flipinsertfacet() Insert a facet into a CDT by flips. // +// // +// The algorithm is described in Shewchuk's paper "Updating and Constructing // +// Constrained Delaunay and Constrained Regular Triangulations by Flips", in // +// Proc. 19th Ann. Symp. on Comput. Geom., 86--95, 2003. // +// // +// 'crosstets' contains the set of crossing tetrahedra (infected) of the // +// facet. 'toppoints' and 'botpoints' are points lies above and below the // +// facet, not on the facet. // +// // +//============================================================================// void tetgenmesh::flipinsertfacet(arraypool *crosstets, arraypool *toppoints, arraypool *botpoints, arraypool *midpoints) @@ -16967,8 +17134,6 @@ void tetgenmesh::flipinsertfacet(arraypool *crosstets, arraypool *toppoints, arraypool *crossfaces, *bfacearray; triface fliptets[6], baktets[2], fliptet, newface; triface neightet, *parytet; - face checksh; - face checkseg; badface *pqueue; badface *popbf, bface; point plane_pa, plane_pb, plane_pc; @@ -17054,10 +17219,8 @@ void tetgenmesh::flipinsertfacet(arraypool *crosstets, arraypool *toppoints, // Pop a face from the priority queue. popbf = pqueue; bface = *popbf; - // Update the queue. pqueue = pqueue->nextitem; - // Delete the popped item from the pool. flippool->dealloc((void *) popbf); @@ -17067,7 +17230,6 @@ void tetgenmesh::flipinsertfacet(arraypool *crosstets, arraypool *toppoints, // It is still a crossing face of R. fliptet = bface.tt; fsym(fliptet, neightet); - assert(!isdeadtet(neightet)); if (oppo(neightet) == bface.noppo) { pd = oppo(fliptet); pe = oppo(neightet); @@ -17100,10 +17262,6 @@ void tetgenmesh::flipinsertfacet(arraypool *crosstets, arraypool *toppoints, if (convcount == 3) { // A 2-to-3 flip is found. - // The face should not be a subface. - tspivot(fliptet, checksh); - assert(checksh.sh == NULL); - fliptets[0] = fliptet; // abcd, d may be the new vertex. fliptets[1] = neightet; // bace. flip23(fliptets, 1, &fc); @@ -17120,7 +17278,6 @@ void tetgenmesh::flipinsertfacet(arraypool *crosstets, arraypool *toppoints, } flipflag = 1; } else if (convcount == 2) { - assert(copcount <= 1); //if (copcount <= 1) { // A 3-to-2 or 4-to-4 may be possible. // Get the edge which is locally non-convex or flat. @@ -17128,15 +17285,8 @@ void tetgenmesh::flipinsertfacet(arraypool *crosstets, arraypool *toppoints, if (ori[i] <= 0) break; enextself(fliptet); } - // The edge should not be a segment. - tsspivot1(fliptet, checkseg); - assert(checkseg.sh == NULL); // Collect tets sharing at this edge. - // NOTE: This operation may collect tets which lie outside the - // cavity, e.g., when the edge lies on the boundary of the - // cavity. Do not flip if there are outside tets at this edge. - // 2012-07-27. esym(fliptet, fliptets[0]); // [b,a,d,c] n = 0; do { @@ -17228,11 +17378,7 @@ void tetgenmesh::flipinsertfacet(arraypool *crosstets, arraypool *toppoints, flip32count--; flip44count++; flipflag = 1; - } else { - //n == 4, convflag != 0; assert(0); } - } else { - // n > 4 => unflipable. //assert(0); } } else { // There are more than 1 non-convex or coplanar cases. @@ -17278,14 +17424,10 @@ void tetgenmesh::flipinsertfacet(arraypool *crosstets, arraypool *toppoints, if (bfacearray->objects > 0) { if (fcount == 0) { printf("!! No flip is found in %ld faces.\n", bfacearray->objects); - assert(0); + terminatetetgen(this, 2); //assert(0); } } - // 'bfacearray' may be not empty (for what reason ??). - //dbg_unflip_facecount += bfacearray->objects; - - assert(flippool->items == 0l); delete bfacearray; // Un-mark top and bottom points. @@ -17308,360 +17450,62 @@ void tetgenmesh::flipinsertfacet(arraypool *crosstets, arraypool *toppoints, } } -/////////////////////////////////////////////////////////////////////////////// -// // -// fillregion() Fill the missing region by a set of new subfaces. // -// // -// 'missingshs' contains the list of subfaces in R. Moreover, each subface // -// (except the first one) in this list represents an interior edge of R. // -// // -// Note: We assume that all vertices of R are marktested so we can detect // -// new subface by checking the flag in apexes. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// insertpoint_cdt() Insert a new point into a CDT. // +// // +//============================================================================// -bool tetgenmesh::fillregion(arraypool* missingshs, arraypool* missingshbds, - arraypool* newshs) +int tetgenmesh::insertpoint_cdt(point newpt, triface *searchtet, face *splitsh, + face *splitseg, insertvertexflags *ivf, + arraypool *cavpoints, arraypool *cavfaces, + arraypool *cavshells, arraypool *newtets, + arraypool *crosstets, arraypool *misfaces) { - badface *newflipface, *popface; - triface searchtet, spintet, neightet; - face oldsh, newsh, opensh, *parysh; - face casout, casin, neighsh, checksh; - face neighseg, checkseg; - point pc; - int success; + triface neightet, *parytet; + face checksh, *parysh, *parysh1; + face *paryseg, *paryseg1; + point *parypt; int t1ver; - int i, j; - - - // Search the first new subface to fill the region. - for (i = 0; i < missingshbds->objects; i++) { - parysh = (face *) fastlookup(missingshbds, i); - sspivot(*parysh, neighseg); - sstpivot1(neighseg, searchtet); - j = 0; // Count the number of passes of R. - spintet = searchtet; - while (1) { - pc = apex(spintet); - if (pmarktested(pc)) { - neightet = spintet; - j++; - } - fnextself(spintet); - if (spintet.tet == searchtet.tet) break; - } - assert(j >= 1); - if (j == 1) { - // Found an interior new subface. - searchtet = neightet; - oldsh = *parysh; - break; - } - } // i + int i; - if (i == missingshbds->objects) { - // Failed to find any interior subface. - // Need Steiner points. - return false; + if (b->verbose > 2) { + printf(" Insert point %d into CDT\n", pointmark(newpt)); } - makeshellface(subfaces, &newsh); - setsorg(newsh, org(searchtet)); - setsdest(newsh, dest(searchtet)); - setsapex(newsh, apex(searchtet)); - // The new subface gets its markers from the old one. - setshellmark(newsh, shellmark(oldsh)); - if (checkconstraints) { - setareabound(newsh, areabound(oldsh)); - } - // Connect the new subface to adjacent tets. - tsbond(searchtet, newsh); - fsymself(searchtet); - sesymself(newsh); - tsbond(searchtet, newsh); - // Connect newsh to outer subfaces. - sspivot(oldsh, checkseg); - if (sinfected(checkseg)) { - // It's a faked segment. Delete it. - spintet = searchtet; - while (1) { - tssdissolve1(spintet); - fnextself(spintet); - if (spintet.tet == searchtet.tet) break; - } - shellfacedealloc(subsegs, checkseg.sh); - ssdissolve(oldsh); - checkseg.sh = NULL; + if (!insertpoint(newpt, searchtet, NULL, NULL, ivf)) { + // Point is not inserted. Check ivf->iloc for reason. + return 0; } - spivot(oldsh, casout); - if (casout.sh != NULL) { - casin = casout; - if (checkseg.sh != NULL) { - // Make sure that the subface has the right ori at the segment. - checkseg.shver = 0; - if (sorg(newsh) != sorg(checkseg)) { - sesymself(newsh); - } - spivot(casin, neighsh); - while (neighsh.sh != oldsh.sh) { - casin = neighsh; - spivot(casin, neighsh); - } - } - sbond1(newsh, casout); - sbond1(casin, newsh); + + + for (i = 0; i < cavetetvertlist->objects; i++) { + cavpoints->newindex((void **) &parypt); + *parypt = * (point *) fastlookup(cavetetvertlist, i); } - if (checkseg.sh != NULL) { - ssbond(newsh, checkseg); + // Add the new point into the point list. + cavpoints->newindex((void **) &parypt); + *parypt = newpt; + + for (i = 0; i < cavebdrylist->objects; i++) { + cavfaces->newindex((void **) &parytet); + *parytet = * (triface *) fastlookup(cavebdrylist, i); } - // Add this new subface into list. - sinfect(newsh); - newshs->newindex((void **) &parysh); - *parysh = newsh; - // Push two "open" side of the new subface into stack. - for (i = 0; i < 2; i++) { - senextself(newsh); - newflipface = (badface *) flippool->alloc(); - newflipface->ss = newsh; - newflipface->nextitem = flipstack; - flipstack = newflipface; + for (i = 0; i < caveoldtetlist->objects; i++) { + crosstets->newindex((void **) &parytet); + *parytet = * (triface *) fastlookup(caveoldtetlist, i); } - success = 1; + cavetetvertlist->restart(); + cavebdrylist->restart(); + caveoldtetlist->restart(); - // Loop until 'flipstack' is empty. - while ((flipstack != NULL) && success) { - // Pop an "open" side from the stack. - popface = flipstack; - opensh = popface->ss; - flipstack = popface->nextitem; // The next top item in stack. - flippool->dealloc((void *) popface); - - // opensh is either (1) an interior edge or (2) a bdry edge. - stpivot(opensh, searchtet); - tsspivot1(searchtet, checkseg); - if (checkseg.sh == NULL) { - // No segment. It is an interior edge of R. - // Search for a new face in R. - spintet = searchtet; - fnextself(spintet); // Skip the current face. - while (1) { - pc = apex(spintet); - if (pmarktested(pc)) { - // 'opensh' is an interior edge. - if (!issubface(spintet)) { - // Create a new subface. - makeshellface(subfaces, &newsh); - setsorg(newsh, org(spintet)); - setsdest(newsh, dest(spintet)); - setsapex(newsh, pc); - // The new subface gets its markers from its neighbor. - setshellmark(newsh, shellmark(opensh)); - if (checkconstraints) { - setareabound(newsh, areabound(opensh)); - } - // Connect the new subface to adjacent tets. - tsbond(spintet, newsh); - fsymself(spintet); - sesymself(newsh); - tsbond(spintet, newsh); - // Connect newsh to its adjacent subface. - sbond(newsh, opensh); - // Add this new subface into list. - sinfect(newsh); - newshs->newindex((void **) &parysh); - *parysh = newsh; - // Push two "open" side of the new subface into stack. - for (i = 0; i < 2; i++) { - senextself(newsh); - newflipface = (badface *) flippool->alloc(); - newflipface->ss = newsh; - newflipface->nextitem = flipstack; - flipstack = newflipface; - } - } else { - // Connect to another open edge. - tspivot(spintet, checksh); - sbond(opensh, checksh); - } - break; - } // if (pmarktested(pc)) - fnextself(spintet); - if (spintet.tet == searchtet.tet) { - // Not find any face to fill in R at this side. - // Suggest a point to split the edge. - success = 0; - break; - } - } // while (1) - } else { - // This side coincident with a boundary edge of R. - checkseg.shver = 0; - spivot(checkseg, oldsh); - if (sinfected(checkseg)) { - // It's a faked segment. Delete it. - spintet = searchtet; - while (1) { - tssdissolve1(spintet); - fnextself(spintet); - if (spintet.tet == searchtet.tet) break; - } - shellfacedealloc(subsegs, checkseg.sh); - ssdissolve(oldsh); - checkseg.sh = NULL; - } - spivot(oldsh, casout); - if (casout.sh != NULL) { - casin = casout; - if (checkseg.sh != NULL) { - // Make sure that the subface has the right ori at the segment. - checkseg.shver = 0; - if (sorg(opensh) != sorg(checkseg)) { - sesymself(opensh); - } - spivot(casin, neighsh); - while (neighsh.sh != oldsh.sh) { - casin = neighsh; - spivot(casin, neighsh); - } - } - sbond1(opensh, casout); - sbond1(casin, opensh); - } - if (checkseg.sh != NULL) { - ssbond(opensh, checkseg); - } - } // if (checkseg.sh != NULL) - } // while ((flipstack != NULL) && success) - - if (success) { - // Uninfect all new subfaces. - for (i = 0; i < newshs->objects; i++) { - parysh = (face *) fastlookup(newshs, i); - suninfect(*parysh); - } - // Delete old subfaces. - for (i = 0; i < missingshs->objects; i++) { - parysh = (face *) fastlookup(missingshs, i); - shellfacedealloc(subfaces, parysh->sh); - } - fillregioncount++; - } else { - // Failed to fill the region. - // Re-connect old subfaces at boundaries of R. - // Also delete fake segments. - for (i = 0; i < missingshbds->objects; i++) { - parysh = (face *) fastlookup(missingshbds, i); - // It still connect to 'casout'. - // Re-connect 'casin' to it. - spivot(*parysh, casout); - casin = casout; - spivot(casin, neighsh); - while (1) { - if (sinfected(neighsh)) break; - if (neighsh.sh == parysh->sh) break; - casin = neighsh; - spivot(casin, neighsh); - } - if (sinfected(neighsh)) { - sbond1(casin, *parysh); - } - sspivot(*parysh, checkseg); - if (checkseg.sh != NULL) { - if (checkseg.sh[3] != NULL) { - if (sinfected(checkseg)) { - sstpivot1(checkseg, searchtet); - spintet = searchtet; - while (1) { - tssdissolve1(spintet); - fnextself(spintet); - if (spintet.tet == searchtet.tet) break; - } - ssdissolve(*parysh); - shellfacedealloc(subsegs, checkseg.sh); - } - } - } - } - // Delete all new subfaces. - for (i = 0; i < newshs->objects; i++) { - parysh = (face *) fastlookup(newshs, i); - shellfacedealloc(subfaces, parysh->sh); - } - // Clear the flip pool. - flippool->restart(); - flipstack = NULL; - - // Choose an interior edge of R to split. - assert(missingshs->objects > 1); - // Skip the first subface in 'missingshs'. - i = randomnation(missingshs->objects - 1) + 1; - parysh = (face *) fastlookup(missingshs, i); - recentsh = *parysh; - } - - newshs->restart(); - - return success; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// insertpoint_cdt() Insert a new point into a CDT. // -// // -/////////////////////////////////////////////////////////////////////////////// - -int tetgenmesh::insertpoint_cdt(point newpt, triface *searchtet, face *splitsh, - face *splitseg, insertvertexflags *ivf, - arraypool *cavpoints, arraypool *cavfaces, - arraypool *cavshells, arraypool *newtets, - arraypool *crosstets, arraypool *misfaces) -{ - triface neightet, *parytet; - face checksh, *parysh, *parysh1; - face *paryseg, *paryseg1; - point *parypt; - int t1ver; - int i; - - if (b->verbose > 2) { - printf(" Insert point %d into CDT\n", pointmark(newpt)); - } - - if (!insertpoint(newpt, searchtet, NULL, NULL, ivf)) { - // Point is not inserted. Check ivf->iloc for reason. - return 0; - } - - - for (i = 0; i < cavetetvertlist->objects; i++) { - cavpoints->newindex((void **) &parypt); - *parypt = * (point *) fastlookup(cavetetvertlist, i); - } - // Add the new point into the point list. - cavpoints->newindex((void **) &parypt); - *parypt = newpt; - - for (i = 0; i < cavebdrylist->objects; i++) { - cavfaces->newindex((void **) &parytet); - *parytet = * (triface *) fastlookup(cavebdrylist, i); - } - - for (i = 0; i < caveoldtetlist->objects; i++) { - crosstets->newindex((void **) &parytet); - *parytet = * (triface *) fastlookup(caveoldtetlist, i); - } - - cavetetvertlist->restart(); - cavebdrylist->restart(); - caveoldtetlist->restart(); - - // Insert the point using the cavity algorithm. - delaunizecavity(cavpoints, cavfaces, cavshells, newtets, crosstets, - misfaces); - fillcavity(cavshells, NULL, NULL, NULL, NULL, NULL, NULL); - carvecavity(crosstets, newtets, NULL); + // Insert the point using the cavity algorithm. + delaunizecavity(cavpoints, cavfaces, cavshells, newtets, crosstets, + misfaces); + fillcavity(cavshells, NULL, NULL, NULL, NULL, NULL, NULL); + carvecavity(crosstets, newtets, NULL); if ((splitsh != NULL) || (splitseg != NULL)) { // Insert the point into the surface mesh. @@ -17699,10 +17543,8 @@ int tetgenmesh::insertpoint_cdt(point newpt, triface *searchtet, face *splitsh, if (neightet.tet != NULL) { if (neightet.tet[4] != NULL) { // Found an adjacent tet. It must be not in C(p). - assert(!infected(neightet)); tsdissolve(neightet); fsymself(neightet); - assert(!infected(neightet)); tsdissolve(neightet); } } @@ -17748,16 +17590,16 @@ int tetgenmesh::insertpoint_cdt(point newpt, triface *searchtet, face *splitsh, return 1; } -/////////////////////////////////////////////////////////////////////////////// -// // -// refineregion() Refine a missing region by inserting points. // -// // -// 'splitsh' represents an edge of the facet to be split. It must be not a // -// segment. -// // -// Assumption: The current mesh is a CDT and is convex. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// refineregion() Refine a missing region by inserting points. // +// // +// 'splitsh' represents an edge of the facet to be split. It must not be a // +// segment. // +// // +// Assumption: The current mesh is a CDT and is convex. // +// // +//============================================================================// void tetgenmesh::refineregion(face &splitsh, arraypool *cavpoints, arraypool *cavfaces, arraypool *cavshells, @@ -17773,6 +17615,13 @@ void tetgenmesh::refineregion(face &splitsh, arraypool *cavpoints, int t1ver; int i; + // Do not split a segment. + for (i = 0; i < 3; i++) { + sspivot(splitsh, splitseg); + if (splitseg.sh == NULL) break; + senextself(splitsh); + } + if (b->verbose > 2) { printf(" Refining region at edge (%d, %d, %d).\n", pointmark(sorg(splitsh)), pointmark(sdest(splitsh)), @@ -17793,6 +17642,7 @@ void tetgenmesh::refineregion(face &splitsh, arraypool *cavpoints, ivf.sloc = (int) ONEDGE; ivf.sbowywat = 1; ivf.assignmeshsize = b->metric; + ivf.smlenflag = useinsertradius; // Return the closet mesh vertex. point2tetorg(pa, searchtet); // Start location from it. ivf.iloc = (int) OUTSIDE; @@ -17803,7 +17653,6 @@ void tetgenmesh::refineregion(face &splitsh, arraypool *cavpoints, if (ivf.iloc == (int) ENCSEGMENT) { pointdealloc(steinpt); // Split an encroached segment. - assert(encseglist->objects > 0); i = randomnation(encseglist->objects); paryseg = (face *) fastlookup(encseglist, i); splitseg = *paryseg; @@ -17823,14 +17672,20 @@ void tetgenmesh::refineregion(face &splitsh, arraypool *cavpoints, if (!insertpoint_cdt(steinpt, &searchtet, &splitsh, &splitseg, &ivf, cavpoints, cavfaces, cavshells, newtets, crosstets, misfaces)) { - assert(0); + terminatetetgen(this, 2); + } + if (useinsertradius) { + //save_segmentpoint_insradius(steinpt, ivf.parentpt, ivf.smlen); } st_segref_count++; if (steinerleft > 0) steinerleft--; } else { - assert(0); + terminatetetgen(this, 2); // assert(0); } } else { + if (useinsertradius) { + //save_facetpoint_insradius(steinpt, ivf.parentpt, ivf.smlen); + } st_facref_count++; if (steinerleft > 0) steinerleft--; } @@ -17846,42 +17701,37 @@ void tetgenmesh::refineregion(face &splitsh, arraypool *cavpoints, if (searchtet.tet != NULL) continue; // Search the segment. - dir = scoutsegment(sorg(splitseg), sdest(splitseg), &searchtet, &refpt, - NULL); + dir = scoutsegment(sorg(splitseg), sdest(splitseg), &splitseg, &searchtet, + &refpt, NULL); if (dir == SHAREEDGE) { // Found this segment, insert it. - if (!issubseg(searchtet)) { - // Let the segment remember an adjacent tet. - sstbond1(splitseg, searchtet); - // Bond the segment to all tets containing it. - spintet = searchtet; - do { - tssbond1(spintet, splitseg); - fnextself(spintet); - } while (spintet.tet != searchtet.tet); - } else { - // Collision! Should not happen. - assert(0); - } + // Let the segment remember an adjacent tet. + sstbond1(splitseg, searchtet); + // Bond the segment to all tets containing it. + spintet = searchtet; + do { + tssbond1(spintet, splitseg); + fnextself(spintet); + } while (spintet.tet != searchtet.tet); } else { if ((dir == ACROSSFACE) || (dir == ACROSSEDGE)) { // Split the segment. - // Create a new point. makepoint(&steinpt, FREESEGVERTEX); - //setpointtype(newpt, FREESEGVERTEX); getsteinerptonsegment(&splitseg, refpt, steinpt); ivf.iloc = (int) OUTSIDE; ivf.rejflag = 0; if (!insertpoint_cdt(steinpt, &searchtet, &splitsh, &splitseg, &ivf, cavpoints, cavfaces, cavshells, newtets, crosstets, misfaces)) { - assert(0); + terminatetetgen(this, 2); + } + if (useinsertradius) { + //save_segmentpoint_insradius(steinpt, ivf.parentpt, ivf.smlen); } st_segref_count++; if (steinerleft > 0) steinerleft--; } else { - // Maybe a PLC problem. - assert(0); + terminatetetgen(this, 2); } } } // while @@ -17891,13 +17741,13 @@ void tetgenmesh::refineregion(face &splitsh, arraypool *cavpoints, } } -/////////////////////////////////////////////////////////////////////////////// -// // -// constrainedfacets() Recover constrained facets in a CDT. // -// // -// All unrecovered subfaces are queued in 'subfacestack'. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// constrainedfacets() Recover constrained facets in a CDT. // +// // +// All unrecovered subfaces are queued in 'subfacestack'. // +// // +//============================================================================// void tetgenmesh::constrainedfacets() { @@ -17954,7 +17804,6 @@ void tetgenmesh::constrainedfacets() for (j = 0; j < 3; j++) { if (!isshsubseg(*parysh)) { spivot(*parysh, searchsh); - assert(searchsh.sh != NULL); // SELF_CHECK if (!smarktested(searchsh)) { if (!isshtet(searchsh)) { smarktest(searchsh); @@ -17972,9 +17821,10 @@ void tetgenmesh::constrainedfacets() sunmarktest(*parysh); } - if (b->verbose > 2) { + + if (b->verbose > 1) { printf(" Recovering facet #%d: %ld subfaces.\n", facetcount + 1, - tg_facfaces->objects); + tg_facfaces->objects); } facetcount++; @@ -17988,14 +17838,14 @@ void tetgenmesh::constrainedfacets() if (isshtet(searchsh)) continue; // It is recovered. searchtet.tet = NULL; - dir = scoutsubface(&searchsh, &searchtet); - if (dir == SHAREFACE) continue; // The subface is inserted. + if (scoutsubface(&searchsh, &searchtet, 1)) continue; // The subface is missing. Form the missing region. // Re-use 'tg_crosstets' for 'adjtets'. formregion(&searchsh, tg_missingshs, tg_missingshbds, tg_missingshverts); - if (scoutcrossedge(searchtet, tg_missingshbds, tg_missingshs)) { + int searchflag = scoutcrossedge(searchtet, tg_missingshbds, tg_missingshs); + if (searchflag > 0) { // Save this crossing edge, will be used by fillcavity(). crossedge = searchtet; // Form a cavity of crossing tets. @@ -18025,9 +17875,13 @@ void tetgenmesh::constrainedfacets() // Use the flip algorithm of Shewchuk to recover the subfaces. flipinsertfacet(tg_crosstets, tg_toppoints, tg_botpoints, tg_missingshverts); - // Recover the missing region. - success = fillregion(tg_missingshs, tg_missingshbds, tg_topshells); - assert(success); + // Put all subfaces in R back to tg_facfaces. + for (i = 0; i < tg_missingshs->objects; i++) { + parysh = (face *) fastlookup(tg_missingshs, i); + tg_facfaces->newindex((void **) &parysh1); + *parysh1 = *parysh; + } + success = 1; // Clear working lists. tg_crosstets->restart(); tg_topfaces->restart(); @@ -18040,8 +17894,7 @@ void tetgenmesh::constrainedfacets() // Recover interior subfaces. for (i = 0; i < caveencshlist->objects; i++) { parysh = (face *) fastlookup(caveencshlist, i); - dir = scoutsubface(parysh, &searchtet); - if (dir != SHAREFACE) { + if (!scoutsubface(parysh, &searchtet, 1)) { // Add this face at the end of the list, so it will be // processed immediately. tg_facfaces->newindex((void **) &parysh1); @@ -18052,32 +17905,45 @@ void tetgenmesh::constrainedfacets() // Recover interior segments. This should always be recovered. for (i = 0; i < caveencseglist->objects; i++) { paryseg = (face *) fastlookup(caveencseglist, i); - dir = scoutsegment(sorg(*paryseg),sdest(*paryseg),&searchtet, - NULL, NULL); - assert(dir == SHAREEDGE); - // Insert this segment. - if (!issubseg(searchtet)) { - // Let the segment remember an adjacent tet. - sstbond1(*paryseg, searchtet); - // Bond the segment to all tets containing it. - neightet = searchtet; - do { - tssbond1(neightet, *paryseg); - fnextself(neightet); - } while (neightet.tet != searchtet.tet); - } else { - // Collision! Should not happen. - assert(0); + dir = scoutsegment(sorg(*paryseg), sdest(*paryseg), paryseg, + &searchtet, NULL, NULL); + if (dir != SHAREEDGE) { + terminatetetgen(this, 2); } + // Insert this segment. + // Let the segment remember an adjacent tet. + sstbond1(*paryseg, searchtet); + // Bond the segment to all tets containing it. + neightet = searchtet; + do { + tssbond1(neightet, *paryseg); + fnextself(neightet); + } while (neightet.tet != searchtet.tet); } caveencseglist->restart(); } // success - remesh cavity - } // success - form cavity + } // success - form cavity + else { + terminatetetgen(this, 2); // Report a bug. + } // Not success - form cavity } else { - // Recover subfaces by retriangulate the surface mesh. - // Re-use tg_topshells for newshs. - success = fillregion(tg_missingshs, tg_missingshbds, tg_topshells); - } + // Put all subfaces in R back to tg_facfaces. + for (i = 0; i < tg_missingshs->objects; i++) { + parysh = (face *) fastlookup(tg_missingshs, i); + tg_facfaces->newindex((void **) &parysh1); + *parysh1 = *parysh; + } + if (searchflag != -1) { + // Some edge(s) in the missing regions were flipped. + success = 1; + } else { + restorecavity(tg_crosstets, tg_topnewtets, tg_botnewtets, + tg_missingshbds); // Only remove fake segments. + // Choose an edge to split (set in recentsh) + recentsh = searchsh; + success = 0; // Do refineregion(); + } + } // if (scoutcrossedge) // Unmarktest all points of the missing region. for (i = 0; i < tg_missingshverts->objects; i++) { @@ -18092,8 +17958,6 @@ void tetgenmesh::constrainedfacets() // The missing region can not be recovered. Refine it. refineregion(recentsh, tg_toppoints, tg_topfaces, tg_topshells, tg_topnewtets, tg_crosstets, tg_midfaces); - // Clean the current list of facet subfaces. - // tg_facfaces->restart(); } } // while (tg_facfaces->objects) @@ -18126,13 +17990,14 @@ void tetgenmesh::constrainedfacets() delete tg_missingshbds; delete tg_missingshverts; delete encseglist; + encseglist = NULL; } -/////////////////////////////////////////////////////////////////////////////// -// // -// constraineddelaunay() Create a constrained Delaunay tetrahedralization.// -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// constraineddelaunay() Create a constrained Delaunay tetrahedralization. // +// // +//============================================================================// void tetgenmesh::constraineddelaunay(clock_t& tv) { @@ -18150,6 +18015,7 @@ void tetgenmesh::constraineddelaunay(clock_t& tv) } makesegmentendpointsmap(); + makefacetverticesmap(); if (b->verbose) { printf(" Delaunizing segments.\n"); @@ -18227,26 +18093,297 @@ void tetgenmesh::constraineddelaunay(clock_t& tv) } } -//// //// -//// //// -//// constrained_cxx ////////////////////////////////////////////////////////// +// // +// // +//== constrained_cxx =========================================================// -//// steiner_cxx ////////////////////////////////////////////////////////////// -//// //// -//// //// +//== steiner_cxx =============================================================// +// // +// // -/////////////////////////////////////////////////////////////////////////////// -// // -// checkflipeligibility() A call back function for boundary recovery. // -// // -// 'fliptype' indicates which elementary flip will be performed: 1 : 2-to-3, // -// and 2 : 3-to-2, respectively. // -// // -// 'pa, ..., pe' are the vertices involved in this flip, where [a,b,c] is // -// the flip face, and [d,e] is the flip edge. NOTE: 'pc' may be 'dummypoint',// -// other points must not be 'dummypoint'. // -// // -/////////////////////////////////////////////////////////////////////////////// +void tetgenmesh::sort_2pts(point p1, point p2, point ppt[2]) +{ + if (pointmark(p1) < pointmark(p2)) { + ppt[0] = p1; + ppt[1] = p2; + } else { + ppt[0] = p2; + ppt[1] = p1; + } +} + +void tetgenmesh::sort_3pts(point p1, point p2, point p3, point ppt[3]) +{ + int i1 = pointmark(p1); + int i2 = pointmark(p2); + int i3 = pointmark(p3); + + if (i1 < i2) { + if (i1 < i3) { + ppt[0] = p1; + if (i2 < i3) { + ppt[1] = p2; + ppt[2] = p3; + } else { + ppt[1] = p3; + ppt[2] = p2; + } + } else { + ppt[0] = p3; + ppt[1] = p1; + ppt[2] = p2; + } + } else { // i1 > i2 + if (i2 < i3) { + ppt[0] = p2; + if (i1 < i3) { + ppt[1] = p1; + ppt[2] = p3; + } else { + ppt[1] = p3; + ppt[2] = p1; + } + } else { + ppt[0] = p3; + ppt[1] = p2; + ppt[2] = p1; + } + } +} + + +//============================================================================// +// // +// is_collinear_at() Check if three vertices (from left to right): left, // +// mid, and right are collinear. // +// // +//============================================================================// + +bool tetgenmesh::is_collinear_at(point mid, point left, point right) +{ + REAL v1[3], v2[3]; + + v1[0] = left[0] - mid[0]; + v1[1] = left[1] - mid[1]; + v1[2] = left[2] - mid[2]; + + v2[0] = right[0] - mid[0]; + v2[1] = right[1] - mid[1]; + v2[2] = right[2] - mid[2]; + + REAL L1 = sqrt(v1[0]*v1[0]+v1[1]*v1[1]+v1[2]*v1[2]); + REAL L2 = sqrt(v2[0]*v2[0]+v2[1]*v2[1]+v2[2]*v2[2]); + REAL D = (v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2]); + + REAL cos_ang = D / (L1 * L2); + return cos_ang < cos_collinear_ang_tol; +} + +//============================================================================// +// // +// is_segment() Check if the two vertices are endpoints of a segment. // +// // +//============================================================================// + +bool tetgenmesh::is_segment(point p1, point p2) +{ + if (pointtype(p1) == RIDGEVERTEX) { + if (pointtype(p2) == RIDGEVERTEX) { + // Check if p2 is connect to p1. + int idx = pointmark(p1); + for (int i = idx_segment_ridge_vertex_list[idx]; + i < idx_segment_ridge_vertex_list[idx+1]; i++) { + if (segment_ridge_vertex_list[i] == p2) { + return true; + } + } + } else if (pointtype(p2) == FREESEGVERTEX) { + // Check if the segment contains p2 has one if its endpoints be p1. + face parsentseg; + sdecode(point2sh(p2), parsentseg); + int segidx = getfacetindex(parsentseg); + if ((segmentendpointslist[segidx*2] == p1) || + (segmentendpointslist[segidx*2+1] == p1)) { + return true; + } + } + } else { + if (pointtype(p1) == FREESEGVERTEX) { + if (pointtype(p2) == RIDGEVERTEX) { + face parsentseg; + sdecode(point2sh(p1), parsentseg); + int segidx = getfacetindex(parsentseg); + if ((segmentendpointslist[segidx*2] == p2) || + (segmentendpointslist[segidx*2+1] == p2)) { + return true; + } + } else if (pointtype(p2) == FREESEGVERTEX) { + face parsentseg1, parsentseg2; + sdecode(point2sh(p1), parsentseg1); + sdecode(point2sh(p2), parsentseg2); + int segidx1 = getfacetindex(parsentseg1); + int segidx2 = getfacetindex(parsentseg2); + if (segidx1 == segidx2) { + return true; + } + } + } + } + + return false; +} + +//============================================================================// +// // +// valid_constrained_f23() Validate a 2-3 flip. // +// // +// The purpose of the following check is to avoid creating a degenrated face // +// (and subface) whose three vertices are nearly on one segment or on two // +// nearly collinear segments. // +// // +// "checktet" is a face (a,b,c) which is 2-3 flippable, and (d,e) will be // +// the new edge after this flip. // +// // +// return true if this 2-3 flip is good, otherwise, return false. // +// // +//============================================================================// + +bool tetgenmesh::valid_constrained_f23(triface& checktet, point pd, point pe) +{ + bool validflag = true; + + triface spintet; + face checkseg1, checkseg2; + point checkpt; + + for (int k = 0; k < 3; k++) { + checkpt = org(checktet); + esym(checktet, spintet); + enextself(spintet); // [x, d], x = a,b,c + tsspivot1(spintet, checkseg1); + bool isseg = (checkseg1.sh != NULL); + if (!isseg && boundary_recovery_flag) { + isseg = is_segment(checkpt, pd); + } + if (isseg) { + fsym(checktet, spintet); + esymself(spintet); + eprevself(spintet); + tsspivot1(spintet, checkseg2); + isseg = (checkseg2.sh != NULL); + if (!isseg && boundary_recovery_flag) { + isseg = is_segment(checkpt, pe); + } + if (isseg) { + if (pointtype(checkpt) == FREESEGVERTEX) { + // In this case, the two subsegments (checkseg1, checkseg2) + // must belong to the same segment, do not flip. + validflag = false; + break; + } else { + // Check if three vertices are nearly collinear. The middle + // vertex is checkpt. + if ((checkpt != dummypoint) && + (pe != dummypoint) && + (pd != dummypoint)) { + if (is_collinear_at(checkpt, pe, pd)) { + validflag = false; + break; + } + } + } + } // if (isseg) + } // if (isseg) + enextself(checktet); + } // k + + return validflag; +} + +//============================================================================// +// // +// valid_constrained_f32() Validate a 3-2 flip. // +// // +// Avoid creating a degenerated tetrahedral face whose three vertices are on // +// one (sub)segment. abtets[0], abdtets[1], abtets[2] are three tets // +// at the flipping edge (a,b), the new face will be (c, d, e). // +// The only new face we will create is (c,d,e), make sure that it is not // +// a (nearly) degenerated face. If the vertex c is RIDGEVEETEX or // +// FREESEGVERTEX, then the edges (c, d) and (c, e) should not on one segment.// +// The same for the vertex d and e. // +// // +// return true if this 3-2 flip is good, otherwise, return false. // +// // +//============================================================================// + +bool tetgenmesh::valid_constrained_f32(triface* abtets, point pa, point pb) +{ + bool validflag = true; // default. + + triface spintet; + face checksegs[3]; // edges: [c,d], [d,e], and [e,c] + point chkpt, leftpt, rightpt; + + // Check edges [c,d], [d,e], and [e,c] + for (int k = 0; k < 3; k++) { // [a,b,c], [a,b,d], [a,b,e] + enext(abtets[k], spintet); + esymself(spintet); + eprevself(spintet); // [c,d], [d,e], and [e,c] + tsspivot1(spintet, checksegs[k]); + // Ignore a temporaray segment (used in recoversubfaces()). + if (checksegs[k].sh != NULL) { + if (smarktest2ed(checksegs[k])) { + checksegs[k].sh = NULL; + } + } + } // k + + for (int k = 0; k < 3; k++) { + chkpt = apex(abtets[k]); // pc + leftpt = apex(abtets[(k+2)%3]); // pe + rightpt = apex(abtets[(k+1)%3]); // pd + bool isseg = (checksegs[k].sh != NULL); // [c,d] + if (!isseg && boundary_recovery_flag) { + isseg = is_segment(chkpt, rightpt); + } + if (isseg) { + isseg = (checksegs[(k+2)%3].sh != NULL); // [e,c] + if (!isseg && boundary_recovery_flag) { + isseg = is_segment(chkpt, leftpt); + } + if (isseg) { + if (pointtype(chkpt) == FREESEGVERTEX) { + validflag = false; + break; + } else { + if ((chkpt != dummypoint) && + (leftpt != dummypoint) && + (rightpt != dummypoint)) { + if (is_collinear_at(chkpt, leftpt, rightpt)) { + validflag = false; + break; + } + } + } + } + } + } // k + + return validflag; +} + +//============================================================================// +// // +// checkflipeligibility() A call back function for boundary recovery. // +// // +// 'fliptype' indicates which elementary flip will be performed: 1 : 2-to-3, // +// and 2 : 3-to-2, respectively. // +// // +// 'pa, ..., pe' are the vertices involved in this flip, where [a,b,c] is // +// the flip face, and [d,e] is the flip edge. NOTE: 'pc' may be 'dummypoint', // +// other points must not be 'dummypoint'. // +// // +//============================================================================// int tetgenmesh::checkflipeligibility(int fliptype, point pa, point pb, point pc, point pd, point pe, @@ -18285,6 +18422,13 @@ int tetgenmesh::checkflipeligibility(int fliptype, point pa, point pb, rejflag = 1; } } + else { + if ((dir == ACROSSVERT) || (dir == TOUCHEDGE) || + (dir == TOUCHFACE)) { + // should be a self-intersection. + rejflag = 1; + } + } // dir } else if (intflag == 4) { // They may intersect at either a point or a line segment. dir = (enum interresult) types[0]; @@ -18295,6 +18439,17 @@ int tetgenmesh::checkflipeligibility(int fliptype, point pa, point pb, rejflag = 1; } } + else if (dir == ACROSSFACE) { + //assert(0); // This should be not possible. + terminatetetgen(this, 2); + } + else { + if ((dir == ACROSSVERT) || (dir == TOUCHEDGE) || + (dir == TOUCHFACE)) { + // This should be caused by a self-intersection. + rejflag = 1; // Do not flip. + } + } } } // if (tmppts[0] != dummypoint) } // i @@ -18366,126 +18521,151 @@ int tetgenmesh::checkflipeligibility(int fliptype, point pa, point pb, if (fc->remove_large_angle && !rejflag) { // Remove a large dihedral angle. Do not create a new small angle. + badface bf; // used by get_tetqual(...) REAL cosmaxd = 0, diff; if (fliptype == 1) { // We assume that neither 'a' nor 'b' is dummypoint. - assert((pa != dummypoint) && (pb != dummypoint)); // SELF_CHECK // A 2-to-3 flip: [a,b,c] => [e,d,a], [e,d,b], [e,d,c]. // The new tet [e,d,a,b] will be flipped later. Only two new tets: // [e,d,b,c] and [e,d,c,a] need to be checked. if ((pc != dummypoint) && (pe != dummypoint) && (pd != dummypoint)) { + REAL min_cosmaxd = 1.0, max_asp = 0; // record the worst quality. // Get the largest dihedral angle of [e,d,b,c]. - tetalldihedral(pe, pd, pb, pc, NULL, &cosmaxd, NULL); - diff = cosmaxd - fc->cosdihed_in; - if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0; // Rounding. + if (get_tetqual(pe, pd, pb, pc, &bf)) { + cosmaxd = bf.cent[0]; + diff = cosmaxd - fc->cosdihed_in; + if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0; // Rounding. + } else { + diff = 0.0; // no improve. + } if (diff <= 0) { //if (cosmaxd <= fc->cosdihed_in) { rejflag = 1; } else { // Record the largest new angle. - if (cosmaxd < fc->cosdihed_out) { - fc->cosdihed_out = cosmaxd; - } + min_cosmaxd = (min_cosmaxd < cosmaxd ? min_cosmaxd : cosmaxd); + max_asp = (max_asp > bf.key ? max_asp : bf.key); // Get the largest dihedral angle of [e,d,c,a]. - tetalldihedral(pe, pd, pc, pa, NULL, &cosmaxd, NULL); - diff = cosmaxd - fc->cosdihed_in; - if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0; // Rounding. + if (get_tetqual(pe, pd, pc, pa, &bf)) { + cosmaxd = bf.cent[0]; + diff = cosmaxd - fc->cosdihed_in; + if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0; // Rounding. + } else { + diff = 0.0; // no improve. + } if (diff <= 0) { //if (cosmaxd <= fc->cosdihed_in) { rejflag = 1; } else { // Record the largest new angle. - if (cosmaxd < fc->cosdihed_out) { - fc->cosdihed_out = cosmaxd; - } + min_cosmaxd = (min_cosmaxd < cosmaxd ? min_cosmaxd : cosmaxd); + max_asp = (max_asp > bf.key ? max_asp : bf.key); + // save the worst quality. + fc->cosdihed_out = (fc->cosdihed_out < min_cosmaxd ? fc->cosdihed_out : min_cosmaxd); + fc->max_asp_out = (fc->max_asp_out > max_asp ? fc->max_asp_out : max_asp); } } } // if (pc != dummypoint && ...) } else if (fliptype == 2) { // A 3-to-2 flip: [e,d,a], [e,d,b], [e,d,c] => [a,b,c] // We assume that neither 'e' nor 'd' is dummypoint. - assert((pe != dummypoint) && (pd != dummypoint)); // SELF_CHECK if (level == 0) { // Both new tets [a,b,c,d] and [b,a,c,e] are new tets. if ((pa != dummypoint) && (pb != dummypoint) && (pc != dummypoint)) { + REAL min_cosmaxd = 1.0, max_asp = 0; // record the worst quality. // Get the largest dihedral angle of [a,b,c,d]. - tetalldihedral(pa, pb, pc, pd, NULL, &cosmaxd, NULL); - diff = cosmaxd - fc->cosdihed_in; - if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0; // Rounding + if (get_tetqual(pa, pb, pc, pd, &bf)) { + cosmaxd = bf.cent[0]; + diff = cosmaxd - fc->cosdihed_in; + if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0; // Rounding. + } else { + diff = 0.0; // no improve. + } if (diff <= 0) { //if (cosmaxd <= fc->cosdihed_in) { rejflag = 1; } else { // Record the largest new angle. - if (cosmaxd < fc->cosdihed_out) { - fc->cosdihed_out = cosmaxd; - } + min_cosmaxd = (min_cosmaxd < cosmaxd ? min_cosmaxd : cosmaxd); + max_asp = (max_asp > bf.key ? max_asp : bf.key); // Get the largest dihedral angle of [b,a,c,e]. - tetalldihedral(pb, pa, pc, pe, NULL, &cosmaxd, NULL); - diff = cosmaxd - fc->cosdihed_in; - if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0;// Rounding + if (get_tetqual(pb, pa, pc, pe, &bf)) { + cosmaxd = bf.cent[0]; + diff = cosmaxd - fc->cosdihed_in; + if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0; // Rounding. + } else { + diff = 0.0; // no improve. + } if (diff <= 0) { //if (cosmaxd <= fc->cosdihed_in) { rejflag = 1; } else { // Record the largest new angle. - if (cosmaxd < fc->cosdihed_out) { - fc->cosdihed_out = cosmaxd; - } + min_cosmaxd = (min_cosmaxd < cosmaxd ? min_cosmaxd : cosmaxd); + max_asp = (max_asp > bf.key ? max_asp : bf.key); + // save the worst quality. + fc->cosdihed_out = (fc->cosdihed_out < min_cosmaxd ? fc->cosdihed_out : min_cosmaxd); + fc->max_asp_out = (fc->max_asp_out > max_asp ? fc->max_asp_out : max_asp); } } } } else { // level > 0 - assert(edgepivot != 0); if (edgepivot == 1) { // The new tet [a,b,c,d] will be flipped. Only check [b,a,c,e]. if ((pa != dummypoint) && (pb != dummypoint) && (pc != dummypoint)) { // Get the largest dihedral angle of [b,a,c,e]. - tetalldihedral(pb, pa, pc, pe, NULL, &cosmaxd, NULL); - diff = cosmaxd - fc->cosdihed_in; - if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0;// Rounding + if (get_tetqual(pb, pa, pc, pe, &bf)) { + cosmaxd = bf.cent[0]; + diff = cosmaxd - fc->cosdihed_in; + if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0; // Rounding. + } else { + diff = 0.0; // no improve. + } if (diff <= 0) { //if (cosmaxd <= fc->cosdihed_in) { rejflag = 1; } else { // Record the largest new angle. - if (cosmaxd < fc->cosdihed_out) { - fc->cosdihed_out = cosmaxd; - } + // save the worst quality. + fc->cosdihed_out = (fc->cosdihed_out < cosmaxd ? fc->cosdihed_out : cosmaxd); + fc->max_asp_out = (fc->max_asp_out > bf.key ? fc->max_asp_out : bf.key); } } } else { - assert(edgepivot == 2); // The new tet [b,a,c,e] will be flipped. Only check [a,b,c,d]. if ((pa != dummypoint) && (pb != dummypoint) && (pc != dummypoint)) { // Get the largest dihedral angle of [b,a,c,e]. - tetalldihedral(pa, pb, pc, pd, NULL, &cosmaxd, NULL); - diff = cosmaxd - fc->cosdihed_in; - if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0;// Rounding + if (get_tetqual(pa, pb, pc, pd, &bf)) { + cosmaxd = bf.cent[0]; + diff = cosmaxd - fc->cosdihed_in; + if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0; // Rounding. + } else { + diff = 0.0; // no improve. + } if (diff <= 0) { //if (cosmaxd <= fc->cosdihed_in) { rejflag = 1; } else { // Record the largest new angle. - if (cosmaxd < fc->cosdihed_out) { - fc->cosdihed_out = cosmaxd; - } + // save the worst quality. + fc->cosdihed_out = (fc->cosdihed_out < cosmaxd ? fc->cosdihed_out : cosmaxd); + fc->max_asp_out = (fc->max_asp_out > bf.key ? fc->max_asp_out : bf.key); } } } // edgepivot } // level } - } + } // if (fc->remove_large_angle && !rejflag) return rejflag; } -/////////////////////////////////////////////////////////////////////////////// -// // -// removeedgebyflips() Remove an edge by flips. // -// // -// 'flipedge' is a non-convex or flat edge [a,b,#,#] to be removed. // -// // -// The return value is a positive integer, it indicates whether the edge is // -// removed or not. A value "2" means the edge is removed, otherwise, the // -// edge is not removed and the value (must >= 3) is the current number of // -// tets in the edge star. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// removeedgebyflips() Attempt to remove an edge by flips. // +// // +// 'flipedge' is a non-convex or flat edge [a,b,#,#] to be removed. // +// // +// The return value is a positive integer, it indicates whether the edge is // +// removed or not. A value "2" means the edge is removed, otherwise, the // +// edge is not removed and the value (must >= 3) is the current number of // +// tets in the edge star. // +// // +//============================================================================// int tetgenmesh::removeedgebyflips(triface *flipedge, flipconstraints* fc) { @@ -18512,15 +18692,27 @@ int tetgenmesh::removeedgebyflips(triface *flipedge, flipconstraints* fc) } // Count the number of tets at edge [a,b]. + int subface_count = 0; // count the # of subfaces at this edge. n = 0; spintet = *flipedge; while (1) { + if (issubface(spintet)) subface_count++; n++; fnextself(spintet); if (spintet.tet == flipedge->tet) break; } - assert(n >= 3); + if (n < 3) { + // It is only possible when the mesh contains inverted tetrahedra. + terminatetetgen(this, 2); // Report a bug + } + + if (fc->noflip_in_surface) { + if (subface_count > 0) { + return 0; + } + } + //if ((b->flipstarsize > 0) && (n > (b->flipstarsize+4))) { if ((b->flipstarsize > 0) && (n > b->flipstarsize)) { // The star size exceeds the limit. return 0; // Do not flip it. @@ -18530,13 +18722,10 @@ int tetgenmesh::removeedgebyflips(triface *flipedge, flipconstraints* fc) abtets = new triface[n]; // Collect the tets at edge [a,b]. spintet = *flipedge; - i = 0; - while (1) { + for (i = 0; i < n; i++) { abtets[i] = spintet; - setelemcounter(abtets[i], 1); - i++; + setelemcounter(abtets[i], 1); fnextself(spintet); - if (spintet.tet == flipedge->tet) break; } @@ -18565,25 +18754,18 @@ int tetgenmesh::removeedgebyflips(triface *flipedge, flipconstraints* fc) return nn; } -/////////////////////////////////////////////////////////////////////////////// -// // -// removefacebyflips() Remove a face by flips. // -// // -// Return 1 if the face is removed. Otherwise, return 0. // -// // -// ASSUMPTIONS: // -// - 'flipface' must not be a hull face. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// removefacebyflips() Remove a face by flips. // +// // +// Return 1 if the face is removed. Otherwise, return 0. // +// // +// ASSUMPTION: 'flipface' must not be a subface or a hull face. // +// // +//============================================================================// int tetgenmesh::removefacebyflips(triface *flipface, flipconstraints* fc) { - if (checksubfaceflag) { - if (issubface(*flipface)) { - return 0; - } - } - triface fliptets[3], flipedge; point pa, pb, pc, pd, pe; REAL ori; @@ -18615,6 +18797,13 @@ int tetgenmesh::removefacebyflips(triface *flipface, flipconstraints* fc) flipedge = *flipface; // [a,b] } + if (reducflag) { + triface checkface = fliptets[0]; + if (!valid_constrained_f23(checkface, pd, pe)) { + return 0; //reducflag = 0; + } + } + if (reducflag) { // A 2-to-3 flip is found. flip23(fliptets, 0, fc); @@ -18622,6 +18811,9 @@ int tetgenmesh::removefacebyflips(triface *flipface, flipconstraints* fc) } else { // Try to flip the selected edge of this face. if (removeedgebyflips(&flipedge, fc) == 2) { + if (b->verbose > 3) { + printf(" Face is removed by removing an edge.\n"); + } return 1; } } @@ -18630,27 +18822,31 @@ int tetgenmesh::removefacebyflips(triface *flipface, flipconstraints* fc) return 0; } -/////////////////////////////////////////////////////////////////////////////// -// // -// recoveredge() Recover an edge in current tetrahedralization. // -// // -// If the edge is recovered, 'searchtet' returns a tet containing the edge. // -// // -// This edge may intersect a set of faces and edges in the mesh. All these // -// faces or edges are needed to be removed. // -// // -// If the parameter 'fullsearch' is set, it tries to flip any face or edge // -// that intersects the recovering edge. Otherwise, only the face or edge // -// which is visible by 'startpt' is tried. // -// // -/////////////////////////////////////////////////////////////////////////////// - -int tetgenmesh::recoveredgebyflips(point startpt, point endpt, - triface* searchtet, int fullsearch) +//============================================================================// +// // +// recoveredgebyflips() Recover an edge in current tetrahedralization. // +// // +// If the edge is recovered, 'searchtet' returns a tet containing the edge. // +// // +// If the parameter 'fullsearch' is set, it tries to flip any face or edge // +// that intersects the recovering edge. Otherwise, only the face or edge // +// which is visible by 'startpt' is tried. // +// // +// The parameter 'sedge' is used to report self-intersection. If it is not // +// a NULL, it is EITHER a segment OR a subface that contains this edge. // +// // +// This routine assumes that the tetrahedralization is convex. // +// // +//============================================================================// + +int tetgenmesh::recoveredgebyflips(point startpt, point endpt, face *sedge, + triface* searchtet, int fullsearch, int& idir) { flipconstraints fc; enum interresult dir; + idir = (int) DISJOINT; // init. + fc.seg[0] = startpt; fc.seg[1] = endpt; fc.checkflipeligibility = 1; @@ -18661,30 +18857,220 @@ int tetgenmesh::recoveredgebyflips(point startpt, point endpt, // Search the edge from 'startpt'. point2tetorg(startpt, *searchtet); dir = finddirection(searchtet, endpt); + if (dir == ACROSSVERT) { if (dest(*searchtet) == endpt) { return 1; // Edge is recovered. } else { - terminatetetgen(this, 3); // // It may be a PLC problem. + if (sedge != NULL) { + // It is a segment or a subedge (an edge of a facet). + // Check and report if there exists a self-intersection. + insertvertexflags ivf; + bool intersect_flag = false; + point nearpt = dest(*searchtet); + ivf.iloc = ONVERTEX; + + if (sedge->sh[5] == NULL) { + // It is a segment. + if (!issteinerpoint(nearpt)) { + // It is an input point. + if (!b->quiet && !b->nowarning) { + int segidx = getfacetindex(*sedge); + point p1 = segmentendpointslist[segidx*2]; + point p2 = segmentendpointslist[segidx*2+1]; + point tmppt = NULL; + if (is_segment(p1, nearpt)) tmppt = p1; + else if (is_segment(p2, nearpt)) tmppt = p2; + if (tmppt != NULL) { + printf("Warning: Two line segments are %s overlapping.\n", + ivf.iloc == NEARVERTEX ? "nearly" : "exactly"); + printf(" 1st: [%d,%d].\n", pointmark(p1), pointmark(p2)); + printf(" 2nd: [%d,%d].\n", pointmark(tmppt), pointmark(nearpt)); + } else { + printf("Warning: A vertex lies %s on a line segment.\n", + ivf.iloc == NEARVERTEX ? "nearly" : "exactly"); + printf(" 1st: [%d,%d].\n", pointmark(p1), pointmark(p2)); + printf(" 2nd: [%d].\n", pointmark(nearpt)); + } + } + intersect_flag = true; + } else { + if (pointtype(nearpt) == FREESEGVERTEX) { + // Check if two segments are (nearly) intersecting. + int segidx = getfacetindex(*sedge); + face parsentseg; + sdecode(point2sh(nearpt), parsentseg); + int segidx2 = getfacetindex(parsentseg); + if (segidx2 != segidx) { + if (!b->quiet && !b->nowarning) { // -no -Q no -W + point p1 = segmentendpointslist[segidx*2]; + point p2 = segmentendpointslist[segidx*2+1]; + point p3 = segmentendpointslist[segidx2*2]; + point p4 = segmentendpointslist[segidx2*2+1]; + printf("Warning: Two line segments are %s crossing.\n", + ivf.iloc == NEARVERTEX ? "nearly" : "exactly"); + printf(" 1st: [%d,%d].\n", pointmark(p1), pointmark(p2)); + printf(" 2nd: [%d,%d].\n", pointmark(p3), pointmark(p4)); + } + intersect_flag = true; + } else { + //if (ivf.iloc == ONVERTEX) { + terminatetetgen(this, 2); // This should not be possible. + //} + } + } else if (pointtype(nearpt) == FREEFACETVERTEX) { + // This case is very unlikely. + terminatetetgen(this, 2); // to debug... + if (!b->quiet && !b->nowarning) { // -no -Q no -W + //face parsentsh; + //sdecode(point2sh(nearpt), parsentsh); + printf("Warning: A segment and a facet intersect.\n"); + } + intersect_flag = true; + } else { + // other cases... + terminatetetgen(this, 2); // to be checked. + } + } + } else { + // It is an edge of a facet. + if (!issteinerpoint(nearpt)) { + if (!b->quiet && !b->nowarning) { // no "-Q -W" + point p1 = sorg(*sedge); + point p2 = sdest(*sedge); + point p3 = sapex(*sedge); + printf("Warning: A vertex lies on a facet.\n"); + printf(" vertex: [%d]\n", pointmark(nearpt)); + printf(" facet triangle: [%d,%d,%d], tag(%d).\n", + pointmark(p1), pointmark(p2), pointmark(p3), + shellmark(*sedge)); + } + intersect_flag = true; + } else { + // A Steiner point. + if (pointtype(nearpt) == FREESEGVERTEX) { + // A facet and a segment is intersecting. + if (!b->quiet && !b->nowarning) { + printf("Warning: A facet and a segment intersect.\n"); + printf(" ...\n"); + } + intersect_flag = true; + } else if (pointtype(nearpt) == FREEFACETVERTEX) { + // Check if two facets are intersecting. + if (!b->quiet && !b->nowarning) { + printf("Warning: Two facets intersect.\n"); + printf(" ...\n"); + } + intersect_flag = true; + } else { + // A FREEVOLVERTEX. + // This is not a real self-intersection. + terminatetetgen(this, 2); // check this case. + } + } + } + + if (intersect_flag) { + idir = (int) SELF_INTERSECT; + } + } // if (sedge != NULL) + return 0; } - } + } // if (dir == ACROSSVERT) // The edge is missing. - // Try to flip the first intersecting face/edge. + // Try to remove the first intersecting face/edge. enextesymself(*searchtet); // Go to the opposite face. + if (dir == ACROSSFACE) { - // A face is intersected with the segment. Try to flip it. + if (checksubfaceflag) { + if (issubface(*searchtet)) { + if (sedge) { + // A self-intersection is detected. + if (!b->quiet && !b->nowarning) { + bool is_seg = (sedge->sh[5] == NULL); + if (is_seg) { + face fac; tspivot(*searchtet, fac); + int segidx = getfacetindex(*sedge); + point p1 = segmentendpointslist[segidx*2]; + point p2 = segmentendpointslist[segidx*2+1]; + printf("Warning: A segment and a facet exactly intersect.\n"); + printf(" seg : [%d,%d].\n", pointmark(p1), pointmark(p2)); + printf(" facet triangle: [%d,%d,%d] tag(%d).\n", + pointmark(sorg(fac)), pointmark(sdest(fac)), + pointmark(sapex(fac)), shellmark(fac)); + } else { + // It is a subedge of a facet. + point *ppt = (point *) &(sedge->sh[3]); + printf("Warning: Two facets exactly intersect.\n"); + printf(" 1st facet triangle: [%d,%d,%d] tag(%d).\n", + pointmark(ppt[0]), pointmark(ppt[1]), + pointmark(ppt[2]), shellmark(*sedge)); + face fac; tspivot(*searchtet, fac); + ppt = (point *) &(fac.sh[3]); + printf(" 2nd facet triangle: [%d,%d,%d] tag(%d).\n", + pointmark(ppt[0]), pointmark(ppt[1]), + pointmark(ppt[2]), shellmark(fac)); + } + } + idir = (int) SELF_INTERSECT; + } + return 0; + } // if (issubface(*searchtet)) + } + // Try to flip a crossing face. if (removefacebyflips(searchtet, &fc)) { continue; } } else if (dir == ACROSSEDGE) { - // An edge is intersected with the segment. Try to flip it. + if (checksubsegflag) { + if (issubseg(*searchtet)) { + if (sedge) { + // A self-intersection is detected. + if (!b->quiet && !b->nowarning) { // no -Q, -W + bool is_seg = (sedge->sh[5] == NULL); + if (is_seg) { + face seg; tsspivot1(*searchtet, seg); + int segidx = getfacetindex(*sedge); + int segidx2 = getfacetindex(seg); + if (segidx != segidx2) { + point p1 = segmentendpointslist[segidx*2]; + point p2 = segmentendpointslist[segidx*2+1]; + point p3 = segmentendpointslist[segidx2*2]; + point p4 = segmentendpointslist[segidx2*2+1]; + printf("Warning: Two segments exactly intersect.\n"); + printf(" 1st seg [%d,%d] tag(%d).\n", + pointmark(p1), pointmark(p2), shellmark(*sedge)); + printf(" 2nd seg: [%d,%d] tag(%d).\n", + pointmark(p3), pointmark(p4), shellmark(seg)); + } else { + terminatetetgen(this, 2); + } + } else { + // It is a subedge of a facet. + point *ppt = (point *) &(sedge->sh[3]); + printf("Warning: A facet and a segment exactly intersect.\n"); + printf(" facet triangle: [%d,%d,%d] tag(%d).\n", + pointmark(ppt[0]), pointmark(ppt[1]), + pointmark(ppt[2]), shellmark(*sedge)); + face seg; tsspivot1(*searchtet, seg); + ppt = (point *) &(seg.sh[3]); + printf(" seg: [%d,%d] tag(%d).\n", + pointmark(ppt[0]), pointmark(ppt[1]), shellmark(seg)); + } + } + idir = (int) SELF_INTERSECT; + } + return 0; + } + } + // Try to flip an intersecting edge. if (removeedgebyflips(searchtet, &fc) == 2) { continue; } } else { - terminatetetgen(this, 3); // It may be a PLC problem. + terminatetetgen(this, 2); // report a bug } // The edge is missing. @@ -18704,7 +19090,6 @@ int tetgenmesh::recoveredgebyflips(point startpt, point endpt, // 'startpt' to 'endpt'. point2tetorg(startpt, *searchtet); dir = finddirection(searchtet, endpt); - //assert(dir != ACROSSVERT); // Go to the face/edge intersecting the searching edge. enextesymself(*searchtet); // Go to the opposite face. @@ -18733,9 +19118,10 @@ int tetgenmesh::recoveredgebyflips(point startpt, point endpt, } } // i // There must be an intersection face/edge. - assert(dir != DISJOINT); // SELF_CHECK - } else { - assert(dir == ACROSSEDGE); + if (dir == DISJOINT) { + terminatetetgen(this, 2); + } + } else if (dir == ACROSSEDGE) { while (1) { // Loop I-I-I // Check the two opposite faces (of the edge) in 'searchtet'. for (i = 0; i < 2; i++) { @@ -18764,6 +19150,8 @@ int tetgenmesh::recoveredgebyflips(point startpt, point endpt, // No intersection. Rotate to the next tet at the edge. fnextself(*searchtet); } // while (1) // Loop I-I-I + } else { + terminatetetgen(this, 2); // Report a bug } // Adjust to the intersecting edge/vertex. @@ -18778,9 +19166,7 @@ int tetgenmesh::recoveredgebyflips(point startpt, point endpt, // Failed to recover the edge. break; // Loop I-I } else { - // We need to further check this case. It might be a PLC problem - // or a Steiner point that was added at a bad location. - assert(0); + return 0; } } @@ -18795,17 +19181,29 @@ int tetgenmesh::recoveredgebyflips(point startpt, point endpt, // Try to flip this intersecting face/edge. if (dir == ACROSSFACE) { + if (checksubfaceflag) { + if (issubface(*searchtet)) { + return 0; + } + } if (removefacebyflips(searchtet, &fc)) { success = 1; break; // Loop I-I } } else if (dir == ACROSSEDGE) { + if (checksubsegflag) { + if (issubseg(*searchtet)) { + return 0; + } + } if (removeedgebyflips(searchtet, &fc) == 2) { success = 1; break; // Loop I-I } + } else if (dir == ACROSSVERT) { + return 0; } else { - assert(0); // A PLC problem. + terminatetetgen(this, 2); } // The face/edge is not flipped. @@ -18818,29 +19216,32 @@ int tetgenmesh::recoveredgebyflips(point startpt, point endpt, point2tetorg(bakface.forg, *searchtet); dir1 = finddirection(searchtet, bakface.fdest); if (dir1 == ACROSSVERT) { - assert(dest(*searchtet) == bakface.fdest); - spintet = *searchtet; - while (1) { - if (apex(spintet) == bakface.fapex) { - // Found the face. - *searchtet = spintet; - break; - } - fnextself(spintet); - if (spintet.tet == searchtet->tet) { - searchtet->tet = NULL; - break; // Not find. - } - } // while (1) - if (searchtet->tet != NULL) { - if (oppo(*searchtet) != bakface.foppo) { - fsymself(*searchtet); - if (oppo(*searchtet) != bakface.foppo) { - assert(0); // Check this case. + if (dest(*searchtet) == bakface.fdest) { + spintet = *searchtet; + while (1) { + if (apex(spintet) == bakface.fapex) { + // Found the face. + *searchtet = spintet; + break; + } + fnextself(spintet); + if (spintet.tet == searchtet->tet) { searchtet->tet = NULL; break; // Not find. } + } // while (1) + if (searchtet->tet != NULL) { + if (oppo(*searchtet) != bakface.foppo) { + fsymself(*searchtet); + if (oppo(*searchtet) != bakface.foppo) { + // The original (intersecting) tet has been flipped. + searchtet->tet = NULL; + break; // Not find. + } + } } + } else { + searchtet->tet = NULL; // Not find. } } else { searchtet->tet = NULL; // Not find. @@ -18867,22 +19268,22 @@ int tetgenmesh::recoveredgebyflips(point startpt, point endpt, return 0; } -/////////////////////////////////////////////////////////////////////////////// -// // -// add_steinerpt_in_schoenhardtpoly() Insert a Steiner point in a Schoen- // -// hardt polyhedron. // -// // -// 'abtets' is an array of n tets which all share at the edge [a,b]. Let the // -// tets are [a,b,p0,p1], [a,b,p1,p2], ..., [a,b,p_(n-2),p_(n-1)]. Moreover, // -// the edge [p0,p_(n-1)] intersects all of the tets in 'abtets'. A special // -// case is that the edge [p0,p_(n-1)] is coplanar with the edge [a,b]. // -// Such set of tets arises when we want to recover an edge from 'p0' to 'p_ // -// (n-1)', and the number of tets at [a,b] can not be reduced by any flip. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// add_steinerpt_in_schoenhardtpoly() Insert a Steiner point in a Schoen- // +// hardt polyhedron. // +// // +// 'abtets' is an array of n tets which all share at the edge [a,b]. Let the // +// tets are [a,b,p0,p1], [a,b,p1,p2], ..., [a,b,p_(n-2),p_(n-1)]. Moreover, // +// the edge [p0,p_(n-1)] intersects all of the tets in 'abtets'. A special // +// case is that the edge [p0,p_(n-1)] is coplanar with the edge [a,b]. // +// Such set of tets arises when we want to recover an edge from 'p0' to 'p_ // +// (n-1)', and the number of tets at [a,b] can not be reduced by any flip. // +// // +//============================================================================// int tetgenmesh::add_steinerpt_in_schoenhardtpoly(triface *abtets, int n, - int chkencflag) + int splitsliverflag, int chkencflag) { triface worktet, *parytet; triface faketet1, faketet2; @@ -18895,10 +19296,54 @@ int tetgenmesh::add_steinerpt_in_schoenhardtpoly(triface *abtets, int n, int it, i; + if (splitsliverflag) { + // randomly pick a tet. + int idx = rand() % n; + + // Calulcate the barycenter of this tet. + point pa = org(abtets[idx]); + point pb = dest(abtets[idx]); + pc = apex(abtets[idx]); + pd = oppo(abtets[idx]); + + makepoint(&steinerpt, FREEVOLVERTEX); + for (i = 0; i < 3; i++) { + steinerpt[i] = (pa[i] + pb[i] + pc[i] + pd[i]) / 4.; + } + + + worktet = abtets[idx]; + ivf.iloc = (int) OUTSIDE; // need point location. + ivf.bowywat = 1; + //ivf.lawson = 0; + ivf.lawson = 2; // Do flips to recover Delaunayness. + ivf.rejflag = 0; + ivf.chkencflag = chkencflag; + ivf.sloc = 0; + ivf.sbowywat = 0; + ivf.splitbdflag = 0; + ivf.validflag = 1; + ivf.respectbdflag = 1; + ivf.assignmeshsize = b->metric; + + if (insertpoint(steinerpt, &worktet, NULL, NULL, &ivf)) { + // The vertex has been inserted. + if (flipstack != NULL) { + recoverdelaunay(); + } + st_volref_count++; + if (steinerleft > 0) steinerleft--; + return 1; + } else { + // Not inserted. + pointdealloc(steinerpt); + return 0; + } + } // if (splitsliverflag) + pc = apex(abtets[0]); // pc = p0 pd = oppo(abtets[n-1]); // pd = p_(n-1) - // Find an optimial point in edge [c,d]. It is visible by all outer faces // of 'abtets', and it maxmizes the min volume. @@ -18991,52 +19436,54 @@ int tetgenmesh::add_steinerpt_in_schoenhardtpoly(triface *abtets, int n, cavetetlist->restart(); - if (!success) { - return 0; - } + if (success) { + // Insert this Steiner point. + // Insert the Steiner point. + makepoint(&steinerpt, FREEVOLVERTEX); + for (i = 0; i < 3; i++) steinerpt[i] = smtpt[i]; - // Insert the Steiner point. - makepoint(&steinerpt, FREEVOLVERTEX); - for (i = 0; i < 3; i++) steinerpt[i] = smtpt[i]; + // Insert the created Steiner point. + for (i = 0; i < n; i++) { + infect(abtets[i]); + caveoldtetlist->newindex((void **) &parytet); + *parytet = abtets[i]; + } + worktet = abtets[0]; // No need point location. + ivf.iloc = (int) INSTAR; + ivf.chkencflag = chkencflag; + ivf.assignmeshsize = b->metric; + if (ivf.assignmeshsize) { + // Search the tet containing 'steinerpt' for size interpolation. + locate(steinerpt, &(abtets[0])); + worktet = abtets[0]; + } - // Insert the created Steiner point. - for (i = 0; i < n; i++) { - infect(abtets[i]); - caveoldtetlist->newindex((void **) &parytet); - *parytet = abtets[i]; - } - worktet = abtets[0]; // No need point location. - ivf.iloc = (int) INSTAR; - ivf.chkencflag = chkencflag; - ivf.assignmeshsize = b->metric; - if (ivf.assignmeshsize) { - // Search the tet containing 'steinerpt' for size interpolation. - locate(steinerpt, &(abtets[0])); - worktet = abtets[0]; + // Insert the new point into the tetrahedralization T. + if (insertpoint(steinerpt, &worktet, NULL, NULL, &ivf)) { + // The vertex has been inserted. + st_volref_count++; + if (steinerleft > 0) steinerleft--; + return 1; + } else { + // Not inserted. + pointdealloc(steinerpt); + return 0; + } } - // Insert the new point into the tetrahedralization T. - // Note that T is convex (nonconvex = 0). - if (insertpoint(steinerpt, &worktet, NULL, NULL, &ivf)) { - // The vertex has been inserted. - st_volref_count++; - if (steinerleft > 0) steinerleft--; - return 1; - } else { - // Not inserted. - pointdealloc(steinerpt); + //if (!success) { return 0; - } + //} } -/////////////////////////////////////////////////////////////////////////////// -// // -// add_steinerpt_in_segment() Add a Steiner point inside a segment. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// add_steinerpt_in_segment() Add a Steiner point inside a segment. // +// // +//============================================================================// -int tetgenmesh::add_steinerpt_in_segment(face* misseg, int searchlevel) +int tetgenmesh::add_steinerpt_in_segment(face* misseg, int searchlevel, int& idir) { triface searchtet; face *paryseg, candseg; @@ -19051,6 +19498,15 @@ int tetgenmesh::add_steinerpt_in_segment(face* misseg, int searchlevel) startpt = sorg(*misseg); endpt = sdest(*misseg); + idir = DISJOINT; // init. + + // sort the vertices + //if (pointmark(startpt) > pointmark(endpt)) { + // endpt = sorg(*misseg); + // startpt = sdest(*misseg); + //} + + fc.seg[0] = startpt; fc.seg[1] = endpt; fc.checkflipeligibility = 1; @@ -19058,7 +19514,9 @@ int tetgenmesh::add_steinerpt_in_segment(face* misseg, int searchlevel) point2tetorg(startpt, searchtet); dir = finddirection(&searchtet, endpt); - //assert(dir != ACROSSVERT); + if (dir == ACROSSVERT) { + return 0; + } // Try to flip the first intersecting face/edge. enextesymself(searchtet); // Go to the opposite face. @@ -19069,13 +19527,9 @@ int tetgenmesh::add_steinerpt_in_segment(face* misseg, int searchlevel) if (dir == ACROSSFACE) { // A face is intersected with the segment. Try to flip it. success = removefacebyflips(&searchtet, &fc); - assert(success == 0); } else if (dir == ACROSSEDGE) { // An edge is intersected with the segment. Try to flip it. success = removeedgebyflips(&searchtet, &fc); - assert(success != 2); - } else { - terminatetetgen(this, 3); // It may be a PLC problem. } split = 0; @@ -19085,6 +19539,13 @@ int tetgenmesh::add_steinerpt_in_segment(face* misseg, int searchlevel) // Calculate the shortest edge between the two lines. pc = sorg(*paryseg); pd = sdest(*paryseg); + + // sort the vertices + //if (pointmark(pc) > pointmark(pd)) { + // pd = sorg(*paryseg); + // pc = sdest(*paryseg); + //} + tp = tq = 0; if (linelineint(startpt, endpt, pc, pd, P, Q, &tp, &tq)) { // Does the shortest edge lie between the two segments? @@ -19157,6 +19618,47 @@ int tetgenmesh::add_steinerpt_in_segment(face* misseg, int searchlevel) } } + // Check if the two segments are nearly crossing each other. + pc = sorg(candseg); + pd = sdest(candseg); + if (is_collinear_at(steinerpt, pc, pd)) { // -p///#, default 179.9 degree + if (!b->quiet && !b->nowarning) { // no -Q, -W + int segidx = getfacetindex(*misseg); + point p1 = segmentendpointslist[segidx*2]; + point p2 = segmentendpointslist[segidx*2+1]; + int segidx2 = getfacetindex(candseg); + point p3 = segmentendpointslist[segidx2*2]; + point p4 = segmentendpointslist[segidx2*2+1]; + printf("Warning: Two line segments are almost crossing.\n"); + printf(" 1st: [%d,%d].\n", pointmark(p1), pointmark(p2)); + printf(" 2nd: [%d,%d].\n", pointmark(p3), pointmark(p4)); + } + + // calculate a new angle tolerance. + REAL collinear_ang = interiorangle(steinerpt, pc, pd, NULL) / PI * 180.; + double ang_diff = collinear_ang - b->collinear_ang_tol; + double new_ang_tol = collinear_ang + ang_diff / 180.; + + if (new_ang_tol < 180.0) { // no -Q, -W + // Reduce the angle tolerance to detect collinear event. + if (!b->quiet && !b->nowarning) { + printf(" Reducing collinear tolerance from %g to %g degree.\n", + b->collinear_ang_tol, new_ang_tol); + } + b->collinear_ang_tol = new_ang_tol; + cos_collinear_ang_tol = cos(b->collinear_ang_tol / 180.0 * PI); + } else { + // Report a self-intersection event due to epsilon. + if (!b->quiet && !b->nowarning) { // no -Q, -W + printf(" Cannot reduce the current collinear tolerance (=%g degree).\n", + b->collinear_ang_tol); + } + idir = SELF_INTERSECT; + pointdealloc(steinerpt); + return 0; + } + } + // We need to locate the point. Start searching from 'searchtet'. if (split < 0.5) { point2tetorg(startpt, searchtet); @@ -19166,23 +19668,31 @@ int tetgenmesh::add_steinerpt_in_segment(face* misseg, int searchlevel) if (b->addsteiner_algo == 1) { splitseg = *misseg; spivot(*misseg, splitsh); + // for create_a_shorter_edge(). + setpoint2sh(steinerpt, sencode(*misseg)); } else { splitsh.sh = NULL; splitseg.sh = NULL; } ivf.iloc = (int) OUTSIDE; ivf.bowywat = 1; - ivf.lawson = 0; + //ivf.lawson = 0; + ivf.lawson = 2; // Do flips to recover Delaunayness. ivf.rejflag = 0; ivf.chkencflag = 0; ivf.sloc = (int) ONEDGE; - ivf.sbowywat = 1; + ivf.sbowywat = 1; // split surface mesh separately, new subsegments are + // pushed into "subsegstack". ivf.splitbdflag = 0; ivf.validflag = 1; ivf.respectbdflag = 1; ivf.assignmeshsize = b->metric; - if (!insertpoint(steinerpt, &searchtet, &splitsh, &splitseg, &ivf)) { + if (insertpoint(steinerpt, &searchtet, &splitsh, &splitseg, &ivf)) { + if (flipstack != NULL) { + recoverdelaunay(); + } + } else { pointdealloc(steinerpt); return 0; } @@ -19204,18 +19714,24 @@ int tetgenmesh::add_steinerpt_in_segment(face* misseg, int searchlevel) return 1; } -/////////////////////////////////////////////////////////////////////////////// -// // -// addsteiner4recoversegment() Add a Steiner point for recovering a seg. // -// // -/////////////////////////////////////////////////////////////////////////////// - -int tetgenmesh::addsteiner4recoversegment(face* misseg, int splitsegflag) +//============================================================================// +// // +// addsteiner4recoversegment() Add a Steiner point for recovering a seg. // +// // +// Tries to add a Steiner point in the volume (near this segment) which will // +// help to recover this segment. This segment itself is not split. // +// // +// 'splitsliverflag' is a parameter used in the subroutine add_steiner_in_ // +// schonhardpoly(). // +// // +//============================================================================// + +int tetgenmesh::add_steinerpt_to_recover_edge(point startpt, point endpt, + face* misseg, int splitsegflag, int splitsliverflag, int& idir) { triface *abtets, searchtet, spintet; face splitsh; face *paryseg; - point startpt, endpt; point pa, pb, pd, steinerpt, *parypt; enum interresult dir; insertvertexflags ivf; @@ -19224,151 +19740,553 @@ int tetgenmesh::addsteiner4recoversegment(face* misseg, int splitsegflag) int t1ver; int i; - startpt = sorg(*misseg); - if (pointtype(startpt) == FREESEGVERTEX) { - sesymself(*misseg); + idir = (int) DISJOINT; + + if (misseg != NULL) { startpt = sorg(*misseg); + if (pointtype(startpt) == FREESEGVERTEX) { + sesymself(*misseg); + startpt = sorg(*misseg); + } + endpt = sdest(*misseg); } - endpt = sdest(*misseg); - // Try to recover the edge by adding Steiner points. + point2tetorg(startpt, searchtet); dir = finddirection(&searchtet, endpt); - enextself(searchtet); - //assert(apex(searchtet) == startpt); + + + if (dir == ACROSSVERT) { + if (dest(searchtet) == endpt) { + // This edge exists. + if ((misseg != NULL) && (subsegstack != NULL)) { + // Add the missing segment back to the recovering list. + subsegstack->newindex((void **) &paryseg); + *paryseg = *misseg; + } + return 1; + } else { + // This edge crosses a vertex (not endpt). + bool intersect_flag = false; // return + if (misseg != NULL) { + // Check whether there exists a self-intersection. + point nearpt = dest(searchtet); + ivf.iloc = ONVERTEX; + // report_seg_vertex_intersect(misseg, dest(searchtet), ONVERTEX); + int segidx = getfacetindex(*misseg); + point p1 = segmentendpointslist[segidx*2]; + point p2 = segmentendpointslist[segidx*2+1]; + + if (!issteinerpoint(nearpt)) { + // It is an input point. + if (!b->quiet && !b->nowarning) { + point tmppt = NULL; + if (is_segment(p1, nearpt)) tmppt = p1; + else if (is_segment(p2, nearpt)) tmppt = p2; + if (tmppt != NULL) { + printf("Warning: Two line segments are %s overlapping.\n", + ivf.iloc == NEARVERTEX ? "nearly" : "exactly"); + printf(" 1st: [%d,%d].\n", pointmark(p1), pointmark(p2)); + printf(" 2nd: [%d,%d].\n", pointmark(tmppt), pointmark(nearpt)); + } else { + printf("Warning: A vertex lies %s on a line segment.\n", + ivf.iloc == NEARVERTEX ? "nearly" : "exactly"); + printf(" segment: [%d,%d].\n", pointmark(p1), pointmark(p2)); + printf(" vertex : [%d].\n", pointmark(nearpt)); + } + } + intersect_flag = true; + } else { + if (pointtype(nearpt) == FREESEGVERTEX) { + // Check if two segments are exactly intersecting. + face parsentseg; + sdecode(point2sh(nearpt), parsentseg); + int segidx2 = getfacetindex(parsentseg); + if (segidx2 != segidx) { + if (!b->quiet && !b->nowarning) { + point p3 = segmentendpointslist[segidx2*2]; + point p4 = segmentendpointslist[segidx2*2+1]; + printf("Warning: Two line segments are %s crossing.\n", + ivf.iloc == NEARVERTEX ? "nearly" : "exactly"); + printf(" 1st: [%d,%d].\n", pointmark(p1), pointmark(p2)); + printf(" 2nd: [%d,%d].\n", pointmark(p3), pointmark(p4)); + } + intersect_flag = true; + } else { + if (ivf.iloc == ONVERTEX) { + terminatetetgen(this, 2); // This should not be possible. + } + } + } else { + // other cases... + terminatetetgen(this, 2); + } + } + } // if (misseg != NULL) + if (intersect_flag) { + idir = (int) SELF_INTERSECT; + } + return 0; + } + } // if (dir == ACROSSVERT) { + + enextself(searchtet); if (dir == ACROSSFACE) { - // The segment is crossing at least 3 faces. Find the common edge of + // The segment is crossing at least 3 faces. Find the common edge of // the first 3 crossing faces. esymself(searchtet); fsym(searchtet, spintet); pd = oppo(spintet); + + if (pd == endpt) { + if (misseg != NULL) { + // Calclate the smallest angle between (a,b,c) and (startpt, endpt). + triface tmptet; + REAL ang, collinear_ang = 0.; + for (int k = 0; k < 3; k++) { + ang = interiorangle(org(searchtet), startpt, endpt, NULL); // in [0, PI] + if (ang > collinear_ang) { + collinear_ang = ang; + tmptet = searchtet; // org(tmptet) + } + enextself(searchtet); + } + collinear_ang = collinear_ang / PI * 180.; // in degree + + if (collinear_ang > b->collinear_ang_tol) { // -p///#, default 179.9 degree + // Report a self-intersection event due to epsilon. + if (!b->quiet && !b->nowarning) { // no -Q, -W + point nearpt = org(tmptet); + ivf.iloc = NEARVERTEX; + // report_seg_vertex_intersect(misseg, dest(searchtet), ONVERTEX); + int segidx = getfacetindex(*misseg); + point p1 = segmentendpointslist[segidx*2]; + point p2 = segmentendpointslist[segidx*2+1]; + + if (!issteinerpoint(nearpt)) { + point tmppt = NULL; + if (is_segment(p1, nearpt)) tmppt = p1; + else if (is_segment(p2, nearpt)) tmppt = p2; + if (tmppt != NULL) { + printf("Warning: Two line segments are %s overlapping.\n", + ivf.iloc == NEARVERTEX ? "nearly" : "exactly"); + printf(" 1st: [%d,%d].\n", pointmark(p1), pointmark(p2)); + printf(" 2nd: [%d,%d].\n", pointmark(tmppt), pointmark(nearpt)); + } else { + printf("Warning: A vertex lies %s on a line segment.\n", + ivf.iloc == NEARVERTEX ? "nearly" : "exactly"); + printf(" segment: [%d,%d].\n", pointmark(p1), pointmark(p2)); + printf(" vertex : [%d].\n", pointmark(nearpt)); + } + } else { + if (pointtype(nearpt) == FREESEGVERTEX) { + // Check if two segments are nearly intersecting. + face parsentseg; + sdecode(point2sh(nearpt), parsentseg); + int segidx2 = getfacetindex(parsentseg); + if (segidx2 != segidx) { + //if (!b->quiet && !b->nowarning) { + point p3 = segmentendpointslist[segidx2*2]; + point p4 = segmentendpointslist[segidx2*2+1]; + printf("Warning: Two line segments are %s crossing.\n", + ivf.iloc == NEARVERTEX ? "nearly" : "exactly"); + printf(" 1st: [%d,%d].\n", pointmark(p1), pointmark(p2)); + printf(" 2nd: [%d,%d].\n", pointmark(p3), pointmark(p4)); + //} + //intersect_flag = true; + } else { + //if (ivf.iloc == ONVERTEX) { + terminatetetgen(this, 2); // This should not be possible. + //} + } + } else { + // Other case to report. + // assert(0); // to do... + terminatetetgen(this, 2); + } + } + } + + // calculate a new angle tolerance. + double ang_diff = collinear_ang - b->collinear_ang_tol; + double new_ang_tol = collinear_ang + ang_diff / 180.; + + if (new_ang_tol < 180.) { + // Reduce the angle tolerance to detect collinear event. + if (!b->quiet && !b->nowarning) { + printf(" Reducing collinear tolerance from %g to %g degree.\n", + b->collinear_ang_tol, new_ang_tol); + } + b->collinear_ang_tol = new_ang_tol; + cos_collinear_ang_tol = cos(b->collinear_ang_tol / 180. * PI); + + // This segment can be recovered by a 2-3 flip. + if (subsegstack != NULL) { + // Add the missing segment back to the recovering list. + subsegstack->newindex((void **) &paryseg); + *paryseg = *misseg; + } + return 1; + } else { + if (!b->quiet && !b->nowarning) { + printf(" Cannot reduce the current collinear tolerance (=%g degree).\n", + b->collinear_ang_tol); + } + idir = (int) SELF_INTERSECT; + return 0; + } + } else { + // This segment can be recovered by a 2-3 flip. + if (subsegstack != NULL) { + // Add the missing segment back to the recovering list. + subsegstack->newindex((void **) &paryseg); + *paryseg = *misseg; + } + return 1; + } + } else { + // This edge (not a segment) can be recovered by a 2-3 flip. + return 1; + } + } // if (pd == endpt) + + if (issubface(searchtet)) { + if (misseg != NULL) { + terminatetetgen(this, 2); + // Report a segment and a facet intersect. + if (!b->quiet && !b->nowarning) { + face fac; tspivot(searchtet, fac); + int segidx = getfacetindex(*misseg); + point p1 = segmentendpointslist[segidx*2]; + point p2 = segmentendpointslist[segidx*2+1]; + printf("Warning: A segment and a facet exactly intersect.\n"); + printf(" segment : [%d,%d].\n", pointmark(p1), pointmark(p2)); + printf(" facet triangle: [%d,%d,%d] tag(%d).\n", + pointmark(org(searchtet)), pointmark(dest(searchtet)), + pointmark(apex(searchtet)), shellmark(fac)); + } + idir = (int) SELF_INTERSECT; + } + return 0; + } // if (issubface(searchtet)) + for (i = 0; i < 3; i++) { pa = org(spintet); pb = dest(spintet); - //pc = apex(neightet); if (tri_edge_test(pa, pb, pd, startpt, endpt, NULL, 1, types, poss)) { break; // Found the edge. } enextself(spintet); eprevself(searchtet); } - assert(i < 3); - esymself(searchtet); - } else { - assert(dir == ACROSSEDGE); - // PLC check. + esymself(searchtet); + } + else { // dir == ACROSSEDGE; if (issubseg(searchtet)) { - face checkseg; - tsspivot1(searchtet, checkseg); - printf("Found two segments intersect each other.\n"); - pa = farsorg(*misseg); - pb = farsdest(*misseg); - printf(" 1st: [%d,%d] %d.\n", pointmark(pa), pointmark(pb), - shellmark(*misseg)); - pa = farsorg(checkseg); - pb = farsdest(checkseg); - printf(" 2nd: [%d,%d] %d.\n", pointmark(pa), pointmark(pb), - shellmark(checkseg)); - terminatetetgen(this, 3); + terminatetetgen(this, 2); + if (misseg != NULL) { + // Report a self_intersection. + //bool intersect_flag = false; + //point nearpt = dest(searchtet); + ivf.iloc = ONVERTEX; + // report_seg_vertex_intersect(misseg, dest(searchtet), ONVERTEX); + int segidx = getfacetindex(*misseg); + point p1 = segmentendpointslist[segidx*2]; + point p2 = segmentendpointslist[segidx*2+1]; + face parsentseg; + //sdecode(point2sh(nearpt), parsentseg); + tsspivot1(searchtet, parsentseg); + int segidx2 = getfacetindex(parsentseg); + if (segidx2 != segidx) { + if (!b->quiet && !b->nowarning) { + point p3 = segmentendpointslist[segidx2*2]; + point p4 = segmentendpointslist[segidx2*2+1]; + printf("Warning: Two line segments are %s crossing.\n", + ivf.iloc == NEARVERTEX ? "nearly" : "exactly"); + printf(" 1st: [%d,%d].\n", pointmark(p1), pointmark(p2)); + printf(" 2nd: [%d,%d].\n", pointmark(p3), pointmark(p4)); + } + //intersect_flag = true; + } else { + if (ivf.iloc == ONVERTEX) { + terminatetetgen(this, 2); // This should not be possible. + } + } + idir = (int) SELF_INTERSECT; + } // if (misseg != NULL) + return 0; } } - assert(apex(searchtet) == startpt); - spintet = searchtet; - n = 0; endi = -1; - while (1) { - // Check if the endpt appears in the star. - if (apex(spintet) == endpt) { - endi = n; // Remember the position of endpt. - } - n++; // Count a tet in the star. - fnextself(spintet); - if (spintet.tet == searchtet.tet) break; - } - assert(n >= 3); + if (!splitsegflag) { + // Try to recover this segment by adding Steiner points near it. - if (endi > 0) { - // endpt is also in the edge star - // Get all tets in the edge star. - abtets = new triface[n]; spintet = searchtet; - for (i = 0; i < n; i++) { - abtets[i] = spintet; + n = 0; endi = -1; + while (1) { + // Check if the endpt appears in the star. + if (apex(spintet) == endpt) { + endi = n; // Remember the position of endpt. + } + n++; // Count a tet in the star. fnextself(spintet); + if (spintet.tet == searchtet.tet) break; } - success = 0; - - if (dir == ACROSSFACE) { - // Find a Steiner points inside the polyhedron. - if (add_steinerpt_in_schoenhardtpoly(abtets, endi, 0)) { - success = 1; + if (endi > 0) { + // endpt is also in the edge star + // Get all tets in the edge star. + abtets = new triface[n]; + spintet = searchtet; + for (i = 0; i < n; i++) { + abtets[i] = spintet; + fnextself(spintet); } - } else if (dir == ACROSSEDGE) { - if (n > 4) { - // In this case, 'abtets' is separated by the plane (containing the - // two intersecting edges) into two parts, P1 and P2, where P1 - // consists of 'endi' tets: abtets[0], abtets[1], ..., - // abtets[endi-1], and P2 consists of 'n - endi' tets: - // abtets[endi], abtets[endi+1], abtets[n-1]. - if (endi > 2) { // P1 - // There are at least 3 tets in the first part. - if (add_steinerpt_in_schoenhardtpoly(abtets, endi, 0)) { - success++; - } + + success = 0; + + if (dir == ACROSSFACE) { + // Find a Steiner points inside the polyhedron. + if (add_steinerpt_in_schoenhardtpoly(abtets, endi, splitsliverflag, 0)) { + success = 1; } - if ((n - endi) > 2) { // P2 - // There are at least 3 tets in the first part. - if (add_steinerpt_in_schoenhardtpoly(&(abtets[endi]), n - endi, 0)) { - success++; + } else if (dir == ACROSSEDGE) { + // PLC check. + if (issubseg(searchtet)) { + terminatetetgen(this, 2); + } + if (n > 4) { + // In this case, 'abtets' is separated by the plane (containing the + // two intersecting edges) into two parts, P1 and P2, where P1 + // consists of 'endi' tets: abtets[0], abtets[1], ..., + // abtets[endi-1], and P2 consists of 'n - endi' tets: + // abtets[endi], abtets[endi+1], abtets[n-1]. + if (endi > 2) { // P1 + // There are at least 3 tets in the first part. + if (add_steinerpt_in_schoenhardtpoly(abtets, endi, splitsliverflag, 0)) { + success++; + } } + if ((n - endi) > 2) { // P2 + // There are at least 3 tets in the first part. + if (add_steinerpt_in_schoenhardtpoly(&(abtets[endi]), n - endi, splitsliverflag, 0)) { + success++; + } + } + } else { + // In this case, a 4-to-4 flip should be re-cover the edge [c,d]. + // However, there will be invalid tets (either zero or negtive + // volume). Otherwise, [c,d] should already be recovered by the + // recoveredge() function. } } else { - // In this case, a 4-to-4 flip should be re-cover the edge [c,d]. - // However, there will be invalid tets (either zero or negtive - // volume). Otherwise, [c,d] should already be recovered by the - // recoveredge() function. - terminatetetgen(this, 2); // Report a bug. + terminatetetgen(this, 2); } - } else { - terminatetetgen(this, 10); // A PLC problem. - } - delete [] abtets; + delete [] abtets; - if (success) { - // Add the missing segment back to the recovering list. - subsegstack->newindex((void **) &paryseg); - *paryseg = *misseg; - return 1; - } - } // if (endi > 0) + if (success && (misseg != NULL)) { + // Add the missing segment back to the recovering list. + subsegstack->newindex((void **) &paryseg); + *paryseg = *misseg; + } + + if (success) { + return 1; + } + } // if (endi > 0) - if (!splitsegflag) { return 0; - } + } // if (!splitsegflag) - if (b->verbose > 2) { - printf(" Splitting segment (%d, %d)\n", pointmark(startpt), - pointmark(endpt)); + if (b->verbose > 3) { + printf(" Recover segment (%d, %d) by splitting it.\n", + pointmark(startpt), pointmark(endpt)); } steinerpt = NULL; if (b->addsteiner_algo > 0) { // -Y/1 or -Y/2 - if (add_steinerpt_in_segment(misseg, 3)) { + if (add_steinerpt_in_segment(misseg, 3, idir)) { return 1; } + if (idir == SELF_INTERSECT) { + return 0; + } sesymself(*misseg); - if (add_steinerpt_in_segment(misseg, 3)) { + if (add_steinerpt_in_segment(misseg, 3, idir)) { return 1; } sesymself(*misseg); + if (idir == SELF_INTERSECT) { + return 0; + } + } + + + // Let the face [a,b,d] be the first intersecting face of the segment + // [startpt, endpt]. We add the interseting point. + REAL ip[3], u; + point2tetorg(startpt, searchtet); + dir = finddirection(&searchtet, endpt); + if (dir == ACROSSVERT) { + if (dest(searchtet) == endpt) { + // This edge exists. + if (misseg != NULL) { + // Add the missing segment back to the recovering list. + subsegstack->newindex((void **) &paryseg); + *paryseg = *misseg; + } + return 1; + } else { + // This should be a self-intersection. + if (misseg != NULL) { + terminatetetgen(this, 2); + // report_seg_vertex_intersect(misseg, dest(searchtet), ONVERTEX); + idir = (int) SELF_INTERSECT; + } + return 0; + } } + enextself(searchtet); + pa = org(searchtet); + pb = dest(searchtet); + pd = oppo(searchtet); + + // Calculate the intersection of the face [a,b,d] and the segment. + //planelineint(pa, pb, pd, startpt, endpt, ip, &u); + + point fpt[3], ept[2]; + sort_3pts(pa, pb, pd, fpt); + sort_2pts(startpt, endpt, ept); + planelineint(fpt[0], fpt[1], fpt[2], ept[0], ept[1], ip, &u); + + if ((u > 0) && (u < 1)) { + // Create a Steiner point. + makepoint(&steinerpt, FREESEGVERTEX); + for (i = 0; i < 3; i++) steinerpt[i] = ip[i]; + + // for create_a_shorter_edge(). + setpoint2sh(steinerpt, sencode(*misseg)); + + esymself(searchtet); // The crossing face/edge. + spivot(*misseg, splitsh); + if (dir == ACROSSFACE) { + //ivf.iloc = (int) ONFACE; + ivf.refineflag = 4; // Check if the crossing face is removed. + } else { + //ivf.iloc = (int) ONEDGE; + ivf.refineflag = 8; // Check if the crossing edge is removed. + } + ivf.iloc = (int) OUTSIDE; // do point location. + ivf.refinetet = searchtet; // The crossing face/edge. + ivf.bowywat = 1; + // ivf.lawson = 0; + ivf.lawson = 2; // Recover Delaunay after inserting this vertex. + ivf.rejflag = 0; + ivf.chkencflag = 0; + ivf.sloc = (int) ONEDGE; + ivf.sbowywat = 1; // split surface mesh separately, new subsegments are + // pushed into "subsegstack". + ivf.splitbdflag = 0; + ivf.validflag = 1; + ivf.respectbdflag = 1; + ivf.assignmeshsize = b->metric; + + if (insertpoint(steinerpt, &searchtet, &splitsh, misseg, &ivf)) { + if (flipstack != NULL) { + recoverdelaunay(); + } + + // Save this Steiner point (for removal). + // Re-use the array 'subvertstack'. + subvertstack->newindex((void **) &parypt); + *parypt = steinerpt; + + st_segref_count++; + if (steinerleft > 0) steinerleft--; + + return 1; + } else { + // Check if this failure is due to a self-intersection. + if ((ivf.iloc == ONVERTEX) || (ivf.iloc == NEARVERTEX)) { + if (misseg != NULL) { + // report_seg_vertex_intersect(misseg, nearpt, ivf.iloc); + int segidx = getfacetindex(*misseg); + point p1 = segmentendpointslist[segidx*2]; + point p2 = segmentendpointslist[segidx*2+1]; + bool intersect_flag = false; + point nearpt = org(searchtet); + if (!issteinerpoint(nearpt)) { + // 'nearpt' is an input vertex. + if (!b->quiet && !b->nowarning) { + point tmppt = NULL; + if (is_segment(p1, nearpt)) tmppt = p1; + else if (is_segment(p2, nearpt)) tmppt = p2; + if (tmppt != NULL) { + // Two input segments are nearly overlapping. + printf("Warning: Two line segments are %s overlapping.\n", + ivf.iloc == NEARVERTEX ? "nearly" : "exactly"); + printf(" 1st: [%d,%d].\n", pointmark(p1), pointmark(p2)); + printf(" 2nd: [%d,%d].\n", pointmark(tmppt), pointmark(nearpt)); + } else { + // An input vertex is very close to a segment. + printf("Warning: A vertex lies %s on a line segment.\n", + ivf.iloc == NEARVERTEX ? "nearly" : "exactly"); + printf(" segment: [%d,%d].\n", pointmark(p1), pointmark(p2)); + printf(" vertex : [%d].\n", pointmark(nearpt)); + } + } // if (!b->quiet && !b->nowarning) + intersect_flag = true; + } else { + if (pointtype(nearpt) == FREESEGVERTEX) { + // Check if two segments are nearly intersecting. + face parsentseg; + sdecode(point2sh(nearpt), parsentseg); + int segidx2 = getfacetindex(parsentseg); + if (segidx2 != segidx) { + point p3 = segmentendpointslist[segidx2*2]; + point p4 = segmentendpointslist[segidx2*2+1]; + printf("Warning: Two line segments are %s crossing.\n", + ivf.iloc == NEARVERTEX ? "nearly" : "exactly"); + printf(" 1st: [%d,%d].\n", pointmark(p1), pointmark(p2)); + printf(" 2nd: [%d,%d].\n", pointmark(p3), pointmark(p4)); + intersect_flag = true; + } + } else { + // report other cases. + // to do... + terminatetetgen(this, 2); + } + } + if (intersect_flag) { + if (!b->quiet && !b->nowarning) { + if (ivf.iloc == NEARVERTEX) { + double dd = distance(steinerpt, nearpt); + double new_dd = minedgelength - dd / longest; + double new_eps = new_dd / longest; + printf("You can ignore this warning by using -T%e (default is %e) option.\n", + new_eps, b->epsilon); + printf(" This will allow a short edge (len = %.17g) (default limit is %g).\n", + dd, minedgelength); + } + } + // A self-intersection is detected. + idir = (int) SELF_INTERSECT; + } // if (intersect_flag) + } // if (misseg != NULL) + } // if ((ivf.iloc == ONVERTEX) || (ivf.iloc == NEARVERTEX)) + // The vertex is not inserted. + pointdealloc(steinerpt); + steinerpt = NULL; + } + } // if ((u > 0) && (u < 1)) + return 0; // Failed to reocver this segment. + // [2020-05-02] The following code is skipped. if (steinerpt == NULL) { // Split the segment at its midpoint. makepoint(&steinerpt, FREESEGVERTEX); @@ -19377,21 +20295,25 @@ int tetgenmesh::addsteiner4recoversegment(face* misseg, int splitsegflag) } // We need to locate the point. - assert(searchtet.tet != NULL); // Start searching from 'searchtet'. spivot(*misseg, splitsh); ivf.iloc = (int) OUTSIDE; ivf.bowywat = 1; - ivf.lawson = 0; + //ivf.lawson = 0; + ivf.lawson = 2; // do flip to recover locally Delaunay faces. ivf.rejflag = 0; ivf.chkencflag = 0; ivf.sloc = (int) ONEDGE; - ivf.sbowywat = 1; + ivf.sbowywat = 1; // mesh surface separately ivf.splitbdflag = 0; ivf.validflag = 1; ivf.respectbdflag = 1; ivf.assignmeshsize = b->metric; - if (!insertpoint(steinerpt, &searchtet, &splitsh, misseg, &ivf)) { - assert(0); + if (insertpoint(steinerpt, &searchtet, &splitsh, misseg, &ivf)) { + if (flipstack != NULL) { + recoverdelaunay(); + } + } else { + terminatetetgen(this, 2); } } // if (endi > 0) @@ -19406,17 +20328,17 @@ int tetgenmesh::addsteiner4recoversegment(face* misseg, int splitsegflag) return 1; } -/////////////////////////////////////////////////////////////////////////////// -// // -// recoversegments() Recover all segments. // -// // -// All segments need to be recovered are in 'subsegstack'. // -// // -// This routine first tries to recover each segment by only using flips. If // -// no flip is possible, and the flag 'steinerflag' is set, it then tries to // -// insert Steiner points near or in the segment. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// recoversegments() Recover all segments. // +// // +// All segments need to be recovered are in 'subsegstack'. // +// // +// This routine first tries to recover each segment by only using flips. If // +// no flip is possible, and the flag 'steinerflag' is set, it then tries to // +// insert Steiner points near or in the segment. // +// // +//============================================================================// int tetgenmesh::recoversegments(arraypool *misseglist, int fullsearch, int steinerflag) @@ -19424,8 +20346,9 @@ int tetgenmesh::recoversegments(arraypool *misseglist, int fullsearch, triface searchtet, spintet; face sseg, *paryseg; point startpt, endpt; - int success; + int success, idir; int t1ver; + long bak_inpoly_count = st_volref_count; long bak_segref_count = st_segref_count; @@ -19459,21 +20382,24 @@ int tetgenmesh::recoversegments(arraypool *misseglist, int fullsearch, success = 0; - if (recoveredgebyflips(startpt, endpt, &searchtet, 0)) { + if (recoveredgebyflips(startpt, endpt, &sseg, &searchtet, 0, idir)) { success = 1; } else { // Try to recover it from the other direction. - if (recoveredgebyflips(endpt, startpt, &searchtet, 0)) { + if ((idir != (int) SELF_INTERSECT) && + recoveredgebyflips(endpt, startpt, &sseg, &searchtet, 0, idir)) { success = 1; } } + if (!success && fullsearch) { - if (recoveredgebyflips(startpt, endpt, &searchtet, fullsearch)) { + if ((idir != (int) SELF_INTERSECT) && + recoveredgebyflips(startpt, endpt, &sseg, &searchtet, fullsearch, idir)) { success = 1; } } - + if (success) { // Segment is recovered. Insert it. // Let the segment remember an adjacent tet. @@ -19485,24 +20411,61 @@ int tetgenmesh::recoversegments(arraypool *misseglist, int fullsearch, fnextself(spintet); } while (spintet.tet != searchtet.tet); } else { - if (steinerflag > 0) { + if ((idir != (int) SELF_INTERSECT) && (steinerflag > 0)) { // Try to recover the segment but do not split it. - if (addsteiner4recoversegment(&sseg, 0)) { + if (add_steinerpt_to_recover_edge(startpt, endpt, &sseg, 0, 0, idir)) { success = 1; } - if (!success && (steinerflag > 1)) { + if (!success && (idir != (int) SELF_INTERSECT) && (steinerflag > 1)) { // Split the segment. - addsteiner4recoversegment(&sseg, 1); - success = 1; + if (add_steinerpt_to_recover_edge(startpt, endpt, &sseg, 1, 0, idir)) { + success = 1; + } } } + if (!success) { - if (misseglist != NULL) { - // Save this segment. - misseglist->newindex((void **) &paryseg); - *paryseg = sseg; - } - } + if (idir != (int) SELF_INTERSECT) { + if (misseglist != NULL) { + // Save this segment (recover it later). + misseglist->newindex((void **) &paryseg); + *paryseg = sseg; + } + } else { + // Save this segment (do not recover it again). + if (skipped_segment_list == NULL) { + skipped_segment_list = new arraypool(sizeof(badface), 10); + } + badface *bf; + skipped_segment_list->newindex((void **) &bf); + bf->init(); + bf->ss = sseg; + bf->forg = sorg(sseg); + bf->fdest = sdest(sseg); + bf->key = (double) shellmark(sseg); + smarktest3(sseg); + // Save all subfaces at this segment, do not recover them later. + if (skipped_facet_list == NULL) { + skipped_facet_list = new arraypool(sizeof(badface), 10); + } + face neighsh, spinsh; + bf->ss.shver = 0; + spivot(bf->ss, neighsh); + spinsh = neighsh; + while (spinsh.sh != NULL) { + skipped_facet_list->newindex((void **) &bf); + bf->init(); + bf->ss = spinsh; + bf->forg = (point) spinsh.sh[3]; + bf->fdest = (point) spinsh.sh[4]; + bf->fapex = (point) spinsh.sh[5]; + bf->key = (double) shellmark(spinsh); + smarktest3(spinsh); // do not recover it. + spivotself(spinsh); + if (spinsh.sh == neighsh.sh) break; + } + } + } // if (!success) } } // while (subsegstack->objects > 0l) @@ -19524,24 +20487,32 @@ int tetgenmesh::recoversegments(arraypool *misseglist, int fullsearch, return 0; } -/////////////////////////////////////////////////////////////////////////////// -// // -// recoverfacebyflips() Recover a face by flips. // -// // -// If 'searchsh' is not NULL, it is a subface to be recovered. It is only // -// used for checking self-intersections. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// recoverfacebyflips() Recover a face by flips. // +// // +// 'pa', 'pb', and 'pc' are the three vertices of this face. This routine // +// tries to recover it in the tetrahedral mesh. It is assumed that the three // +// edges, i.e., pa->pb, pb->pc, and pc->pa all exist. // +// // +// If the face is recovered, it is returned by 'searchtet'. // +// // +// If 'searchsh' is not NULL, it is a subface to be recovered. Its vertices // +// must be pa, pb, and pc. It is mainly used to check self-intersections. // +// Another use of this subface is to split it when a Steiner point is found // +// inside this subface. // +// // +//============================================================================// int tetgenmesh::recoverfacebyflips(point pa, point pb, point pc, - face *searchsh, triface* searchtet) + face *searchsh, triface* searchtet, + int &dir, point *p1, point *p2) { triface spintet, flipedge; point pd, pe; - enum interresult dir; flipconstraints fc; int types[2], poss[4], intflag; - int success, success1; + int success; int t1ver; int i, j; @@ -19550,15 +20521,15 @@ int tetgenmesh::recoverfacebyflips(point pa, point pb, point pc, fc.fac[1] = pb; fc.fac[2] = pc; fc.checkflipeligibility = 1; + + dir = (int) DISJOINT; success = 0; for (i = 0; i < 3 && !success; i++) { while (1) { // Get a tet containing the edge [a,b]. point2tetorg(fc.fac[i], *searchtet); - dir = finddirection(searchtet, fc.fac[(i+1)%3]); - //assert(dir == ACROSSVERT); - assert(dest(*searchtet) == fc.fac[(i+1)%3]); + finddirection(searchtet, fc.fac[(i+1)%3]); // Search the face [a,b,c] spintet = *searchtet; while (1) { @@ -19569,15 +20540,18 @@ int tetgenmesh::recoverfacebyflips(point pa, point pb, point pc, for (j = i; j > 0; j--) { eprevself(*searchtet); } + dir = (int) SHAREFACE; success = 1; break; } fnextself(spintet); if (spintet.tet == searchtet->tet) break; } // while (1) + if (success) break; + // The face is missing. Try to recover it. - success1 = 0; + flipedge.tet = NULL; // Find a crossing edge of this face. spintet = *searchtet; while (1) { @@ -19587,46 +20561,122 @@ int tetgenmesh::recoverfacebyflips(point pa, point pb, point pc, // Check if [d,e] intersects [a,b,c] intflag = tri_edge_test(pa, pb, pc, pd, pe, NULL, 1, types, poss); if (intflag > 0) { - // By our assumptions, they can only intersect at a single point. + // By the assumption that all edges of the face exist, they can + // only intersect at a single point. if (intflag == 2) { - // Check the intersection type. - dir = (enum interresult) types[0]; - if ((dir == ACROSSFACE) || (dir == ACROSSEDGE)) { - // Go to the edge [d,e]. - edestoppo(spintet, flipedge); // [d,e,a,b] - if (searchsh != NULL) { + // Go to the edge [d,e]. + edestoppo(spintet, flipedge); // [d,e,a,b] + if (searchsh != NULL) { + // Check the intersection type. + dir = types[0]; // return this value. + if ((types[0] == (int) ACROSSFACE) || + (types[0] == (int) ACROSSEDGE)) { // Check if [e,d] is a segment. if (issubseg(flipedge)) { - if (!b->quiet) { - face checkseg; - tsspivot1(flipedge, checkseg); - printf("Found a segment and a subface intersect.\n"); - pd = farsorg(checkseg); - pe = farsdest(checkseg); - printf(" 1st: [%d, %d] %d.\n", pointmark(pd), - pointmark(pe), shellmark(checkseg)); - printf(" 2nd: [%d,%d,%d] %d\n", pointmark(pa), - pointmark(pb), pointmark(pc), shellmark(*searchsh)); - } - terminatetetgen(this, 3); - } - } - // Try to flip the edge [d,e]. - success1 = (removeedgebyflips(&flipedge, &fc) == 2); - } else { - if (dir == TOUCHFACE) { - point touchpt, *parypt; + // This subface intersects with a segment. + if (!b->quiet && !b->nowarning) { + if (!b->quiet && !b->nowarning) { + printf("Warning: A segment and a facet intersect.\n"); + face sseg; tsspivot1(flipedge, sseg); + int segidx = getfacetindex(sseg); + point p1 = segmentendpointslist[segidx*2]; + point p2 = segmentendpointslist[segidx*2+1]; + printf(" segment: [%d,%d] tag(%d).\n", + pointmark(p1), pointmark(p2), shellmark(sseg)); + point *ppt = (point *) &(searchsh->sh[3]); + printf(" facet triangle: [%d,%d,%d] tag(%d)\n", + pointmark(ppt[0]), pointmark(ppt[1]), + pointmark(ppt[2]), shellmark(*searchsh)); + } + } + dir = (int) SELF_INTERSECT; + return 0; // Found a self-intersection. + } else { + // Check if [e,d] is an edge of a subface. + triface chkface = flipedge; + while (1) { + if (issubface(chkface)) break; + fsymself(chkface); + if (chkface.tet == flipedge.tet) break; + } + if (issubface(chkface)) { + if (searchsh != NULL) { + // Two subfaces are intersecting. + if (!b->quiet && !b->nowarning) { + printf("Warning: Found two facets intersect.\n"); + point *ppt = (point *) &(searchsh->sh[3]); + printf(" 1st facet triangle: [%d,%d,%d] tag(%d)\n", + pointmark(ppt[0]), pointmark(ppt[1]), + pointmark(ppt[2]), shellmark(*searchsh)); + face fa; tspivot(chkface, fa); + ppt = (point *) &(fa.sh[3]); + printf(" 2nd facet triangle: [%d,%d,%d] tag(%d)\n", + pointmark(ppt[0]), pointmark(ppt[1]), + pointmark(ppt[2]), shellmark(fa)); + } + dir = (int) SELF_INTERSECT; + } + return 0; // Found a self-intersection. + } + } + } else if (types[0] == TOUCHFACE) { + // This is possible when a Steiner point was added on it. + point touchpt, *parypt; if (poss[1] == 0) { touchpt = pd; // pd is a coplanar vertex. } else { touchpt = pe; // pe is a coplanar vertex. } - if (pointtype(touchpt) == FREEVOLVERTEX) { + if (!issteinerpoint(touchpt)) { + if (!b->quiet && !b->nowarning) { + printf("Warning: A vertex lies on a facet.\n"); + printf(" vertex : [%d]\n", pointmark(touchpt)); + point *ppt = (point *) &(searchsh->sh[3]); + printf(" facet triangle: [%d,%d,%d] tag(%d)\n", + pointmark(ppt[0]), pointmark(ppt[1]), + pointmark(ppt[2]), shellmark(*searchsh)); + } + dir = (int) SELF_INTERSECT; + return 0; + } else if (pointtype(touchpt) == FREESEGVERTEX) { + if (!b->quiet && !b->nowarning) { + printf("Warning: A segment and a facet intersect.\n"); + face sseg; + sdecode(point2sh(touchpt), sseg); + int segidx = getfacetindex(sseg); + point p1 = segmentendpointslist[segidx*2]; + point p2 = segmentendpointslist[segidx*2+1]; + printf(" segment: [%d,%d] tag(%d).\n", + pointmark(p1), pointmark(p2), shellmark(sseg)); + point *ppt = (point *) &(searchsh->sh[3]); + printf(" facet triangle: [%d,%d,%d] tag(%d)\n", + pointmark(ppt[0]), pointmark(ppt[1]), + pointmark(ppt[2]), shellmark(*searchsh)); + } + dir = (int) SELF_INTERSECT; + return 0; + } else if (pointtype(touchpt) == FREEFACETVERTEX) { + if (!b->quiet && !b->nowarning) { + printf("Warning: Found two facets intersect.\n"); + point *ppt = (point *) &(searchsh->sh[3]); + printf(" 1st facet triangle: [%d,%d,%d] tag(%d)\n", + pointmark(ppt[0]), pointmark(ppt[1]), + pointmark(ppt[2]), shellmark(*searchsh)); + face fa; + sdecode(point2sh(touchpt), fa); + ppt = (point *) &(fa.sh[3]); + printf(" 2nd facet triangle: [%d,%d,%d] tag(%d)\n", + pointmark(ppt[0]), pointmark(ppt[1]), + pointmark(ppt[2]), shellmark(fa)); + } + dir = (int) SELF_INTERSECT; + return 0; + } else if (pointtype(touchpt) == FREEVOLVERTEX) { // A volume Steiner point was added in this subface. // Split this subface by this point. face checksh, *parysh; int siloc = (int) ONFACE; - int sbowat = 0; // Only split this subface. + int sbowat = 0; // Only split this subface. A 1-to-3 flip. setpointtype(touchpt, FREEFACETVERTEX); sinsertvertex(touchpt, searchsh, NULL, siloc, sbowat, 0); st_volref_count--; @@ -19647,7 +20697,6 @@ int tetgenmesh::recoverfacebyflips(point pa, point pb, point pc, } } // Delete the old subfaces in sC(p). - assert(caveshlist->objects == 1); for (i = 0; i < caveshlist->objects; i++) { parysh = (face *) fastlookup(caveshlist, i); shellfacedealloc(subfaces, parysh->sh); @@ -19658,43 +20707,62 @@ int tetgenmesh::recoverfacebyflips(point pa, point pb, point pc, cavesegshlist->restart(); // We can return this function. searchsh->sh = NULL; // It has been split. - success1 = 0; - success = 1; + return 1; } else { - // It should be a PLC problem. - if (pointtype(touchpt) == FREESEGVERTEX) { - // A segment and a subface intersect. - } else if (pointtype(touchpt) == FREEFACETVERTEX) { - // Two facets self-intersect. - } - terminatetetgen(this, 3); - } + // Other cases may be due to a bug or a PLC error. + //return report_selfint_face(pa, pb, pc, searchsh, &flipedge, + // intflag, types, poss); + terminatetetgen(this, 2); // to debug... + dir = (int) SELF_INTERSECT; + return 0; // Found a self-intersection. + } } else { - assert(0); // Unknown cases. Debug. + // The other intersection types: ACROSSVERT, TOUCHEDGE, + // SHAREVERTEX should not be possible or due to a PLC error. + //return report_selfint_face(pa, pb, pc, searchsh, &flipedge, + // intflag, types, poss); + terminatetetgen(this, 2); // to report + dir = (int) SELF_INTERSECT; + return 0; } - } - break; + } // if (searchsh != NULL) } else { // intflag == 4. Coplanar case. - // This may be an input PLC error. - assert(0); + // Found a mesh edge is coplanar with this subface. + // It migh be caused by a self-intersection. + terminatetetgen(this, 2); // report this bug } + break; } // if (intflag > 0) } fnextself(spintet); - assert(spintet.tet != searchtet->tet); + if (spintet.tet == searchtet->tet) { + terminatetetgen(this, 2); + } } // while (1) - if (!success1) break; + // Try to flip the edge [d,e]. + // Remember a crossing edge. + *p1 = org(flipedge); + *p2 = dest(flipedge); + + if (removeedgebyflips(&flipedge, &fc) == 2) { + // A crossing edge is removed. + continue; + } + + // Unable to remove a crossing edge of this face. + break; } // while (1) } // i return success; } -/////////////////////////////////////////////////////////////////////////////// -// // -// recoversubfaces() Recover all subfaces. // -// // -/////////////////////////////////////////////////////////////////////////////// + +//============================================================================// +// // +// recoversubfaces() Recover all subfaces. // +// // +//============================================================================// int tetgenmesh::recoversubfaces(arraypool *misshlist, int steinerflag) { @@ -19702,10 +20770,10 @@ int tetgenmesh::recoversubfaces(arraypool *misshlist, int steinerflag) face searchsh, neighsh, neineish, *parysh; face bdsegs[3]; point startpt, endpt, apexpt, *parypt; + point cross_e1 = NULL, cross_e2 = NULL; // endpoints of a crossing edge. point steinerpt; - enum interresult dir; insertvertexflags ivf; - int success; + int success, dir; int t1ver; int i, j; @@ -19724,133 +20792,148 @@ int tetgenmesh::recoversubfaces(arraypool *misshlist, int steinerflag) searchsh = *parysh; if (searchsh.sh[3] == NULL) continue; // Skip a dead subface. + if (smarktest3ed(searchsh)) continue; // Skip a self-intersected subface. stpivot(searchsh, neightet); if (neightet.tet != NULL) continue; // Skip a recovered subface. - if (b->verbose > 2) { printf(" Recover subface (%d, %d, %d).\n",pointmark(sorg(searchsh)), pointmark(sdest(searchsh)), pointmark(sapex(searchsh))); } + dir = (int) DISJOINT; // No self intersection is detected. // The three edges of the face need to be existed first. for (i = 0; i < 3; i++) { - sspivot(searchsh, bdsegs[i]); + sspivot(searchsh, bdsegs[i]); if (bdsegs[i].sh != NULL) { - // The segment must exist. + // Check if this segment exist. sstpivot1(bdsegs[i], searchtet); if (searchtet.tet == NULL) { - assert(0); - } + // This segment is not recovered yet. Try to recover it. + success = 0; + startpt = sorg(searchsh); + endpt = sdest(searchsh); + if (recoveredgebyflips(startpt, endpt, &bdsegs[i], &searchtet, 0, dir)) { + success = 1; + } else { + if ((dir != (int) SELF_INTERSECT) && + recoveredgebyflips(endpt, startpt, &bdsegs[i], &searchtet, 0, dir)) { + success = 1; + } + } + if (success) { + // Segment is recovered. Insert it. + // Let the segment remember an adjacent tet. + sstbond1(bdsegs[i], searchtet); + // Bond the segment to all tets containing it. + spintet = searchtet; + do { + tssbond1(spintet, bdsegs[i]); + fnextself(spintet); + } while (spintet.tet != searchtet.tet); + } else { + // An edge of this subface is missing. Can't recover this subface. + // Delete any temporary segment that has been created. + for (j = (i - 1); j >= 0; j--) { + if (smarktest2ed(bdsegs[j])) { + spivot(bdsegs[j], neineish); + ssdissolve(neineish); + spivot(neineish, neighsh); + if (neighsh.sh != NULL) { + ssdissolve(neighsh); + } + sstpivot1(bdsegs[j], searchtet); + spintet = searchtet; + while (1) { + tssdissolve1(spintet); + fnextself(spintet); + if (spintet.tet == searchtet.tet) break; + } + shellfacedealloc(subsegs, bdsegs[j].sh); + } + } // j + break; // i + } // if (success) else + } // if (searchtet.tet == NULL) } else { - // This edge is not a segment (due to a Steiner point). + // This edge is not a segment. // Check whether it exists or not. success = 0; startpt = sorg(searchsh); endpt = sdest(searchsh); point2tetorg(startpt, searchtet); - dir = finddirection(&searchtet, endpt); - if (dir == ACROSSVERT) { - if (dest(searchtet) == endpt) { - success = 1; - } else { - //assert(0); // A PLC problem. - terminatetetgen(this, 3); - } + finddirection(&searchtet, endpt); + if (dest(searchtet) == endpt) { + success = 1; // Found this edge. } else { // The edge is missing. Try to recover it. - if (recoveredgebyflips(startpt, endpt, &searchtet, 0)) { + if (recoveredgebyflips(startpt, endpt, &searchsh, &searchtet, 0, dir)) { success = 1; } else { - if (recoveredgebyflips(endpt, startpt, &searchtet, 0)) { + if ((dir != (int) SELF_INTERSECT) && + recoveredgebyflips(endpt, startpt, &searchsh, &searchtet, 0, dir)) { success = 1; } } } + if (success) { - // Insert a temporary segment to protect this edge. - makeshellface(subsegs, &(bdsegs[i])); - setshvertices(bdsegs[i], startpt, endpt, NULL); - smarktest2(bdsegs[i]); // It's a temporary segment. - // Insert this segment into surface mesh. - ssbond(searchsh, bdsegs[i]); - spivot(searchsh, neighsh); - if (neighsh.sh != NULL) { - ssbond(neighsh, bdsegs[i]); - } - // Insert this segment into tetrahedralization. - sstbond1(bdsegs[i], searchtet); - // Bond the segment to all tets containing it. - spintet = searchtet; - do { - tssbond1(spintet, bdsegs[i]); - fnextself(spintet); - } while (spintet.tet != searchtet.tet); + // This edge exists. + if (issubseg(searchtet)) { + // A segment already exists at this edge! + //terminatetetgen(this, 2); // to debug + //dir = SELF_INTERSECT; + // We contnue to recover this subface instead of reporting a + // SELF_INTERSECT event. + // Eventually, we will find "a duplicated triangle" event. + } + } + + if (success && (dir != SELF_INTERSECT)) { + // This edge exists. + //if (!issubseg(searchtet)) { + // Insert a temporary segment to protect this edge. + makeshellface(subsegs, &(bdsegs[i])); + setshvertices(bdsegs[i], startpt, endpt, NULL); + smarktest2(bdsegs[i]); // It's a temporary segment. + // Insert this segment into surface mesh. + ssbond(searchsh, bdsegs[i]); + spivot(searchsh, neighsh); + if (neighsh.sh != NULL) { + ssbond(neighsh, bdsegs[i]); + } + // Insert this segment into tetrahedralization. + sstbond1(bdsegs[i], searchtet); + // Bond the segment to all tets containing it. + spintet = searchtet; + do { + tssbond1(spintet, bdsegs[i]); + fnextself(spintet); + } while (spintet.tet != searchtet.tet); + //} } else { // An edge of this subface is missing. Can't recover this subface. // Delete any temporary segment that has been created. for (j = (i - 1); j >= 0; j--) { if (smarktest2ed(bdsegs[j])) { spivot(bdsegs[j], neineish); - assert(neineish.sh != NULL); - //if (neineish.sh != NULL) { ssdissolve(neineish); spivot(neineish, neighsh); if (neighsh.sh != NULL) { ssdissolve(neighsh); - // There should be only two subfaces at this segment. - spivotself(neighsh); // SELF_CHECK - assert(neighsh.sh == neineish.sh); } - //} sstpivot1(bdsegs[j], searchtet); - assert(searchtet.tet != NULL); - //if (searchtet.tet != NULL) { spintet = searchtet; while (1) { tssdissolve1(spintet); fnextself(spintet); if (spintet.tet == searchtet.tet) break; } - //} shellfacedealloc(subsegs, bdsegs[j].sh); } } // j - if (steinerflag) { - // Add a Steiner point at the midpoint of this edge. - if (b->verbose > 2) { - printf(" Add a Steiner point in subedge (%d, %d).\n", - pointmark(startpt), pointmark(endpt)); - } - makepoint(&steinerpt, FREEFACETVERTEX); - for (j = 0; j < 3; j++) { - steinerpt[j] = 0.5 * (startpt[j] + endpt[j]); - } - - point2tetorg(startpt, searchtet); // Start from 'searchtet'. - ivf.iloc = (int) OUTSIDE; // Need point location. - ivf.bowywat = 1; - ivf.lawson = 0; - ivf.rejflag = 0; - ivf.chkencflag = 0; - ivf.sloc = (int) ONEDGE; - ivf.sbowywat = 1; // Allow flips in facet. - ivf.splitbdflag = 0; - ivf.validflag = 1; - ivf.respectbdflag = 1; - ivf.assignmeshsize = b->metric; - if (!insertpoint(steinerpt, &searchtet, &searchsh, NULL, &ivf)) { - assert(0); - } - // Save this Steiner point (for removal). - // Re-use the array 'subvertstack'. - subvertstack->newindex((void **) &parypt); - *parypt = steinerpt; - st_facref_count++; - if (steinerleft > 0) steinerleft--; - } // if (steinerflag) break; } } @@ -19858,38 +20941,31 @@ int tetgenmesh::recoversubfaces(arraypool *misshlist, int steinerflag) } // i if (i == 3) { + // All edges of this subface exist (or have been recovered). // Recover the subface. startpt = sorg(searchsh); endpt = sdest(searchsh); apexpt = sapex(searchsh); - success = recoverfacebyflips(startpt,endpt,apexpt,&searchsh,&searchtet); + success = recoverfacebyflips(startpt, endpt, apexpt,&searchsh, &searchtet, + dir, &cross_e1, &cross_e2); // Delete any temporary segment that has been created. for (j = 0; j < 3; j++) { if (smarktest2ed(bdsegs[j])) { spivot(bdsegs[j], neineish); - assert(neineish.sh != NULL); - //if (neineish.sh != NULL) { ssdissolve(neineish); spivot(neineish, neighsh); if (neighsh.sh != NULL) { ssdissolve(neighsh); - // There should be only two subfaces at this segment. - spivotself(neighsh); // SELF_CHECK - assert(neighsh.sh == neineish.sh); } - //} sstpivot1(bdsegs[j], neightet); - assert(neightet.tet != NULL); - //if (neightet.tet != NULL) { spintet = neightet; while (1) { tssdissolve1(spintet); fnextself(spintet); if (spintet.tet == neightet.tet) break; } - //} shellfacedealloc(subsegs, bdsegs[j].sh); } } // j @@ -19897,80 +20973,401 @@ int tetgenmesh::recoversubfaces(arraypool *misshlist, int steinerflag) if (success) { if (searchsh.sh != NULL) { // Face is recovered. Insert it. - tsbond(searchtet, searchsh); - fsymself(searchtet); - sesymself(searchsh); - tsbond(searchtet, searchsh); + face chkface; + tspivot(searchtet, chkface); + if (chkface.sh == NULL) { + tsbond(searchtet, searchsh); + fsymself(searchtet); + sesymself(searchsh); + tsbond(searchtet, searchsh); + } else { + // A duplicated facet is found. + if (shellmark(chkface) == shellmark(searchsh)) { + if (!b->quiet && !b->nowarning) { + point *ppt = (point *) &(searchsh.sh[3]); + printf("Warning: A duplicated triangle (%d,%d,%d) tag(%d) is ignored.\n", + pointmark(ppt[0]), pointmark(ppt[1]), pointmark(ppt[2]), + shellmark(searchsh)); + } + duplicated_facets_count++; + smarktest3(searchsh); // do not recover it. + sinfect(searchsh); // it is an igonred duplicated facet. + } else { + if (!b->quiet && !b->nowarning) { + point *ppt = (point *) &(chkface.sh[3]); + printf("Warning: Two facets are overlapping at triangle (%d,%d,%d).\n", + pointmark(ppt[0]), pointmark(ppt[1]), pointmark(ppt[2])); + printf(" 1st facet tag(%d).\n", shellmark(chkface)); + printf(" 2nd facet tag(%d).\n", shellmark(searchsh)); + } + dir = SELF_INTERSECT; + success = 0; + } + } } } else { - if (steinerflag) { + if ((dir != (int) SELF_INTERSECT) && steinerflag) { // Add a Steiner point at the barycenter of this subface. - if (b->verbose > 2) { - printf(" Add a Steiner point in subface (%d, %d, %d).\n", - pointmark(startpt), pointmark(endpt), pointmark(apexpt)); - } + REAL ip[3], u; + + //planelineint(startpt, endpt, apexpt, cross_e1, cross_e2, ip, &u); + + point fpt[3], ept[2]; + sort_3pts(startpt, endpt, apexpt, fpt); + sort_2pts(cross_e1, cross_e2, ept); + planelineint(fpt[0], fpt[1], fpt[2], ept[0], ept[1], ip, &u); + makepoint(&steinerpt, FREEFACETVERTEX); - for (j = 0; j < 3; j++) { - steinerpt[j] = (startpt[j] + endpt[j] + apexpt[j]) / 3.0; + if ((u > 0.) && (u < 1.)) { + for (j = 0; j < 3; j++) steinerpt[j] = ip[j]; + // Make sure that this Steiner point is inside the subface. + if (is_collinear_at(steinerpt, startpt, endpt) || + is_collinear_at(steinerpt, endpt, apexpt) || + is_collinear_at(steinerpt, apexpt, startpt)) { + // Add the barycenter of this missing subface. + for (j = 0; j < 3; j++) { + steinerpt[j] = (startpt[j] + endpt[j] + apexpt[j]) / 3.0; + } + // Avoid creating a very skinny triangle + if (is_collinear_at(steinerpt, startpt, endpt) || + is_collinear_at(steinerpt, endpt, apexpt) || + is_collinear_at(steinerpt, apexpt, startpt)) { + terminatetetgen(this, 2); + } + } + } else { + // Add the barycenter of this missing subface. + for (j = 0; j < 3; j++) { + steinerpt[j] = (startpt[j] + endpt[j] + apexpt[j]) / 3.0; + } + // Avoid creating a very skinny triangle + if (is_collinear_at(steinerpt, startpt, endpt) || + is_collinear_at(steinerpt, endpt, apexpt) || + is_collinear_at(steinerpt, apexpt, startpt)) { + //assert(0); // to debug... + terminatetetgen(this, 2); + } } + // for create_a_shorter_edge(). + setpoint2sh(steinerpt, sencode(searchsh)); + + ivf.init(); point2tetorg(startpt, searchtet); // Start from 'searchtet'. ivf.iloc = (int) OUTSIDE; // Need point location. ivf.bowywat = 1; - ivf.lawson = 0; + ivf.lawson = 2; // do recover delaunay. ivf.rejflag = 0; ivf.chkencflag = 0; - ivf.sloc = (int) ONFACE; - ivf.sbowywat = 1; // Allow flips in facet. + ivf.sloc = (int) ONFACE; // "searchsh" must be the subface. + ivf.sbowywat = 1; // split subface mesh separately, new subfaces + // are pushed into "subfacestack". ivf.splitbdflag = 0; ivf.validflag = 1; ivf.respectbdflag = 1; - ivf.assignmeshsize = b->metric; - if (!insertpoint(steinerpt, &searchtet, &searchsh, NULL, &ivf)) { - assert(0); - } - // Save this Steiner point (for removal). - // Re-use the array 'subvertstack'. - subvertstack->newindex((void **) &parypt); - *parypt = steinerpt; - - st_facref_count++; - if (steinerleft > 0) steinerleft--; - } // if (steinerflag) - } - } else { - success = 0; - } - - if (!success) { - if (misshlist != NULL) { - // Save this subface. - misshlist->newindex((void **) &parysh); - *parysh = searchsh; - } - } + ivf.assignmeshsize = b->metric; - } // while (subfacstack->objects > 0l) + if (insertpoint(steinerpt, &searchtet, &searchsh, NULL, &ivf)) { + if (flipstack != NULL) { + recoverdelaunay(); + } + + // Save this Steiner point (for removal). + // Re-use the array 'subvertstack'. + subvertstack->newindex((void **) &parypt); + *parypt = steinerpt; - return 0; -} + st_facref_count++; + if (steinerleft > 0) steinerleft--; + + success = 1; // This subface has been split. + } else { + // Failed to insert this point. + if (ivf.iloc == NEARVERTEX) { + // Check if this subface is nearly "touched" by an existing + // vertex. If so, report an event. + point chkpt = org(searchtet); + REAL dist = distance(steinerpt, chkpt); + if (dist < minedgelength) { + if (!issteinerpoint(chkpt)) { + if (!b->quiet && !b->nowarning) { // -no -Q -W + printf("Warning: A facet (%d,%d,%d) and a vertex %d are very close.\n", + pointmark(sorg(searchsh)), pointmark(sdest(searchsh)), + pointmark(sapex(searchsh)), pointmark(chkpt)); + double dd = dist; // distance(steinerpt, nearpt); + //assert(dd > 0.); + //minedgelength = longest * b->epsilon; + //assert(dd < minedgelength); + double new_dd = minedgelength - dd / longest; + double new_eps = new_dd / longest; + printf("You can ignore this warning by using -T%e (default is %e) option.\n", + new_eps, b->epsilon); + printf(" This will allow a short edge (len = %.17g) (default limit is %g).\n", + dd, minedgelength); + } + dir = SELF_INTERSECT; + } + } else { + // Report other types of possible (nearly) self-intersection. + terminatetetgen(this, 2); + dir = SELF_INTERSECT; + } + } -/////////////////////////////////////////////////////////////////////////////// -// // -// getvertexstar() Return the star of a vertex. // -// // -// If the flag 'fullstar' is set, return the complete star of this vertex. // -// Otherwise, only a part of the star which is bounded by facets is returned.// -// // -// 'tetlist' returns the list of tets in the star of the vertex 'searchpt'. // -// Every tet in 'tetlist' is at the face opposing to 'searchpt'. // -// // -// 'vertlist' returns the list of vertices in the star (exclude 'searchpt'). // -// // -// 'shlist' returns the list of subfaces in the star. Each subface must face // -// to the interior of this star. // -// // -/////////////////////////////////////////////////////////////////////////////// + if ((dir != SELF_INTERSECT) && (steinerflag >= 2)) { + if (ivf.iloc == NULLCAVITY) { + // Collect a list of bad quality tets which prevent the + // insertion of this Steiner point. + terminatetetgen(this, 2); + point2tetorg(startpt, searchtet); + ivf.iloc = (int) OUTSIDE; // re-do point location. + ivf.collect_inial_cavity_flag = 1; + insertpoint(steinerpt, &searchtet, &searchsh, NULL, &ivf); + } else { + terminatetetgen(this, 2); // report a bug. + } + } // if (steinerflag >= 2) + + pointdealloc(steinerpt); + steinerpt = NULL; + success = 0; // queue this subface. + } + } // if (steinerflag) + } + } else { // when i < 3 + // An edge (startpt, endpt) of this subface is missing. + if ((dir != (int) SELF_INTERSECT) && (steinerflag > 0)) { + // Split this edge by adding a Steiner point. + // Find the first face/edge crossed by the edge (startpt, endpt). + point2tetorg(startpt, searchtet); + dir = finddirection(&searchtet, endpt); + + + if (dir != (int) SELF_INTERSECT) { + // Insert a Steiner point. + REAL ip[3], u; + + enextself(searchtet); + point pa = org(searchtet); + point pb = dest(searchtet); + point pd = oppo(searchtet); + + //planelineint(pa, pb, pd, startpt, endpt, ip, &u); + + point fpt[3], ept[2]; + sort_3pts(pa, pb, pd, fpt); + sort_2pts(startpt, endpt, ept); + planelineint(fpt[0], fpt[1], fpt[2], ept[0], ept[1], ip, &u); + + makepoint(&steinerpt, FREEFACETVERTEX); + for (j = 0; j < 3; j++) steinerpt[j] = ip[j]; + + ivf.init(); + + ivf.refinetet = searchtet; // bakup the crossing face/edge. + + triface tmptet = searchtet; + ivf.iloc = locate(steinerpt, &tmptet); + + if (ivf.iloc == ONVERTEX) { + // the origin of tmptet is co-incident with this Steiner point. + searchtet = tmptet; + } + //else if (ivf.iloc == ONFACE) { + // searchtet = tmptet; + //} else if (ivf.iloc == ONEDGE) { + // searchtet = tmptet; + //} + else { + //assert(0); // to debug... + // Make sure that we can split the crossing edge/face (a,b,d). + if (dir == ACROSSFACE) { + ivf.iloc = (int) ONFACE; + //ivf.refineflag = 4; // Check if the crossing face is removed. + } else if (dir == ACROSSEDGE) { + ivf.iloc = (int) ONEDGE; + //ivf.refineflag = 8; // Check if the crossing edge is removed. + } else { + terminatetetgen(this, 2); + } + //ivf.iloc = (int) OUTSIDE; // do point location. + //ivf.refinetet = searchtet; // The crossing face/edge. + } + + ivf.bowywat = 1; + ivf.lawson = 2; // do recover delaunay. + ivf.rejflag = 0; + ivf.chkencflag = 0; + ivf.sloc = (int) ONEDGE; // "searchsh" must be the subedge. + ivf.sbowywat = 1; // split subface mesh separately, new subfaces + // are pushed into "subfacestack". + ivf.splitbdflag = 0; + ivf.validflag = 1; + ivf.respectbdflag = 1; + ivf.assignmeshsize = b->metric; + + //if (steinerflag >= 2) { + // Skip NEARVERTEX. This may create a very short edge. + //ivf.ignore_near_vertex = 1; + //} + + // searchsh may contain a missing segment. + // After splitting this subface, this segment must also be split. + // the two missing subsegments are stored in "subsegstack". + face misseg, *splitseg = NULL; + sspivot(searchsh, misseg); + if (misseg.sh != NULL) { + splitseg = &misseg; + setpointtype(steinerpt, FREESEGVERTEX); // default is FREEFACETVERTEX. + // for create_a_shorter_edge() + setpoint2sh(steinerpt, sencode(misseg)); + } else { + // for create_a_shorter_edge() + setpoint2sh(steinerpt, sencode(searchsh)); + } + + bool splitseg_flag = (splitseg != NULL); + int bak_iloc = ivf.iloc; // for collect_initial_cavity + + if (insertpoint(steinerpt, &searchtet, &searchsh, splitseg, &ivf)) { + if (flipstack != NULL) { + recoverdelaunay(); + } + + // Save this Steiner point (for removal). + // Re-use the array 'subvertstack'. + subvertstack->newindex((void **) &parypt); + *parypt = steinerpt; + + if (splitseg_flag) { + st_segref_count++; + } else { + st_facref_count++; + } + if (steinerleft > 0) steinerleft--; + + success = 1; // This subface has been split. + } else { + // Failed to insert this point. + if (ivf.iloc == NEARVERTEX) { + // Check if this subface is nearly "touched" by an existing + // vertex. If so, report an event. + point chkpt = org(searchtet); + REAL dist = distance(steinerpt, chkpt); // for reporting. + if (!issteinerpoint(chkpt)) { + if (!b->quiet && !b->nowarning) { + if (splitseg_flag) { + printf("Warning: A segment (%d,%d) and a vertex %d are very close.\n", + pointmark(sorg(searchsh)), pointmark(sdest(searchsh)), + pointmark(chkpt)); + } else { + printf("Warning: A facet (%d,%d,%d) and a vertex %d are very close.\n", + pointmark(sorg(searchsh)), pointmark(sdest(searchsh)), + pointmark(sapex(searchsh)), pointmark(chkpt)); + } + //printf(" Will result a vert short edge (len=%.17g) (< %.17g)\n", + // dist, minedgelength); + double dd = dist; // distance(steinerpt, nearpt); + //assert(dd > 0.); + //minedgelength = longest * b->epsilon; + //assert(dd < minedgelength); + double new_dd = minedgelength - dd / longest; + double new_eps = new_dd / longest; + printf("You can ignore this warning by using -T%e (default is %e) option.\n", + new_eps, b->epsilon); + printf(" This will allow a short edge (len = %.17g) (default limit is %g).\n", + dd, minedgelength); + } + dir = SELF_INTERSECT; + } + } + + if ((dir != SELF_INTERSECT) && (steinerflag >= 2)) { + success = 0; // queue this subface. + // Failed to split a crossing edge/face. + if ((ivf.iloc == ONVERTEX) || (ivf.iloc == NEARVERTEX)) { + // Get the existing vertex (must be a Steiner point). + if (dir == ACROSSEDGE) { + int idir; + if (add_steinerpt_to_recover_edge(startpt, endpt, NULL, 0, 1, idir)) { + // A Steiner point is inserted. + // Push this subface back to stack, to recover it again. + subfacstack->newindex((void **) &parysh); + *parysh = searchsh; + success = 1; + } + } else if (dir == ACROSSFACE) { + // to do... + terminatetetgen(this, 2); + } else { + terminatetetgen(this, 2); // not possible. + } + } else if (ivf.iloc == NULLCAVITY) { + // Collect a list of bad quality tets which prevent the + // insertion of this Steiner point. + terminatetetgen(this, 2); + } else { + terminatetetgen(this, 2); // report a bug. + } + } // if (steinerflag >= 2) + + pointdealloc(steinerpt); + steinerpt = NULL; + //success = 0; // queue this subface. + } + } // if (dir != SELF_INTERSECT) + } // if ((dir != SELF_INTERSECT) && steinerflag > 0) + } // if (i == 2) else + + if (success) continue; // recover the next subface. + + if (dir == (int) SELF_INTERSECT) { + // Found a self-intersection. This subface cannot be recovered. + // Save it in a separate list, and remove it from the subface pool. + if (skipped_facet_list == NULL) { + skipped_facet_list = new arraypool(sizeof(badface), 10); + } + badface *bf; + skipped_facet_list->newindex((void **) &bf); + bf->init(); + bf->ss = searchsh; + bf->forg = (point) searchsh.sh[3]; + bf->fdest = (point) searchsh.sh[4]; + bf->fapex = (point) searchsh.sh[5]; + bf->key = (double) shellmark(searchsh); + smarktest3(searchsh); // do not recover it later. + continue; // recover the next subface. + } + + // This subface is missing. + if (steinerflag >= 2) { + terminatetetgen(this, 2); + } // if (steinerflag >= 2) + + // Save this subface to recover it later. + misshlist->newindex((void **) &parysh); + *parysh = searchsh; + } // while (subfacstack->objects > 0l) + + return 0; +} + +//============================================================================// +// // +// getvertexstar() Return the star of a vertex. // +// // +// If the flag 'fullstar' is set, return the complete star of this vertex. // +// Otherwise, only a part of the star which is bounded by facets is returned. // +// // +// 'tetlist' returns the list of tets in the star of the vertex 'searchpt'. // +// Every tet in 'tetlist' is at the face opposing to 'searchpt'. // +// // +// 'vertlist' returns the list of vertices in the star (exclude 'searchpt'). // +// // +// 'shlist' returns the list of subfaces in the star. Each subface must face // +// to the interior of this star. // +// // +//============================================================================// int tetgenmesh::getvertexstar(int fullstar, point searchpt, arraypool* tetlist, arraypool* vertlist, arraypool* shlist) @@ -20008,7 +21405,7 @@ int tetgenmesh::getvertexstar(int fullstar, point searchpt, arraypool* tetlist, tspivot(neightet, checksh); if (!sinfected(checksh)) { // Collect this subface (link edge). - sinfected(checksh); + sinfect(checksh); shlist->newindex((void **) &parysh); *parysh = checksh; } @@ -20048,7 +21445,7 @@ int tetgenmesh::getvertexstar(int fullstar, point searchpt, arraypool* tetlist, tspivot(neightet, checksh); if (!sinfected(checksh)) { // Collect this subface (link edge). - sinfected(checksh); + sinfect(checksh); shlist->newindex((void **) &parysh); *parysh = checksh; } @@ -20102,17 +21499,17 @@ int tetgenmesh::getvertexstar(int fullstar, point searchpt, arraypool* tetlist, return (int) tetlist->objects; } -/////////////////////////////////////////////////////////////////////////////// -// // -// getedge() Get a tetrahedron having the two endpoints. // -// // -// The method here is to search the second vertex in the link faces of the // -// first vertex. The global array 'cavetetlist' is re-used for searching. // -// // -// This function is used for the case when the mesh is non-convex. Otherwise,// -// the function finddirection() should be faster than this. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// getedge() Get a tetrahedron having the two endpoints. // +// // +// The method here is to search the second vertex in the link faces of the // +// first vertex. The global array 'cavetetlist' is re-used for searching. // +// // +// This function is used for the case when the mesh is non-convex. Otherwise, // +// the function finddirection() should be faster than this. // +// // +//============================================================================// int tetgenmesh::getedge(point e1, point e2, triface *tedge) { @@ -20121,8 +21518,12 @@ int tetgenmesh::getedge(point e1, point e2, triface *tedge) int done; int i, j; - if (b->verbose > 2) { - printf(" Get edge from %d to %d.\n", pointmark(e1), pointmark(e2)); + if (e1 == NULL || e2 == NULL) { + return 0; + } + if ((pointtype(e1) == UNUSEDVERTEX) || + (pointtype(e2) == UNUSEDVERTEX)) { + return 0; } // Quickly check if 'tedge' is just this edge. @@ -20158,9 +21559,6 @@ int tetgenmesh::getedge(point e1, point e2, triface *tedge) // Go to the link face of e1. point2tetorg(e1, searchtet); enextesymself(searchtet); - //assert(oppo(searchtet) == e1); - - assert(cavebdrylist->objects == 0l); // It will re-use this list. arraypool *tetlist = cavebdrylist; // Search e2. @@ -20227,13 +21625,13 @@ int tetgenmesh::getedge(point e1, point e2, triface *tedge) return done; } -/////////////////////////////////////////////////////////////////////////////// -// // -// reduceedgesatvertex() Reduce the number of edges at a given vertex. // -// // -// 'endptlist' contains the endpoints of edges connecting at the vertex. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// reduceedgesatvertex() Reduce the number of edges at a given vertex. // +// // +// 'endptlist' contains the endpoints of edges connecting at the vertex. // +// // +//============================================================================// int tetgenmesh::reduceedgesatvertex(point startpt, arraypool* endptlist) { @@ -20280,8 +21678,9 @@ int tetgenmesh::reduceedgesatvertex(point startpt, arraypool* endptlist) reduceflag = 1; } } - } else { - assert(0); // A plc problem. + } + else { + terminatetetgen(this, 2); } } else { // The edge has been flipped. @@ -20308,21 +21707,21 @@ int tetgenmesh::reduceedgesatvertex(point startpt, arraypool* endptlist) return (int) endptlist->objects; } -/////////////////////////////////////////////////////////////////////////////// -// // -// removevertexbyflips() Remove a vertex by flips. // -// // -// This routine attempts to remove the given vertex 'rempt' (p) from the // -// tetrahedralization (T) by a sequence of flips. // -// // -// The algorithm used here is a simple edge reduce method. Suppose there are // -// n edges connected at p. We try to reduce the number of edges by flipping // -// any edge (not a segment) that is connecting at p. // -// // -// Unless T is a Delaunay tetrahedralization, there is no guarantee that 'p' // -// can be successfully removed. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// removevertexbyflips() Remove a vertex by flips. // +// // +// This routine attempts to remove the given vertex 'rempt' (p) from the // +// tetrahedralization (T) by a sequence of flips. // +// // +// The algorithm used here is a simple edge reduce method. Suppose there are // +// n edges connected at p. We try to reduce the number of edges by flipping // +// any edge (not a segment) that is connecting at p. // +// // +// Unless T is a Delaunay tetrahedralization, there is no guarantee that 'p' // +// can be successfully removed. // +// // +//============================================================================// int tetgenmesh::removevertexbyflips(point steinerpt) { @@ -20341,27 +21740,33 @@ int tetgenmesh::removevertexbyflips(point steinerpt) vt = pointtype(steinerpt); + if (vt == FREESEGVERTEX) { sdecode(point2sh(steinerpt), leftseg); - assert(leftseg.sh != NULL); leftseg.shver = 0; if (sdest(leftseg) == steinerpt) { senext(leftseg, rightseg); spivotself(rightseg); - assert(rightseg.sh != NULL); rightseg.shver = 0; - assert(sorg(rightseg) == steinerpt); } else { - assert(sorg(leftseg) == steinerpt); rightseg = leftseg; senext2(rightseg, leftseg); spivotself(leftseg); - assert(leftseg.sh != NULL); leftseg.shver = 0; - assert(sdest(leftseg) == steinerpt); } lpt = sorg(leftseg); rpt = sdest(rightseg); + + // Check if both leftseg and rightseg are recovered in tet mesh. + sstpivot1(leftseg, neightet); + if (neightet.tet == NULL) { + return 0; // Do not remove this Steiner point. + } + sstpivot1(rightseg, neightet); + if (neightet.tet == NULL) { + return 0; // Do not remove this Steiner point. + } + if (b->verbose > 2) { printf(" Removing Steiner point %d in segment (%d, %d).\n", pointmark(steinerpt), pointmark(lpt), pointmark(rpt)); @@ -20395,7 +21800,6 @@ int tetgenmesh::removevertexbyflips(point steinerpt) } else { valence = cavetetvertlist->objects; } - assert(cavetetlist->objects == 0l); cavetetvertlist->restart(); removeflag = 0; @@ -20413,8 +21817,6 @@ int tetgenmesh::removevertexbyflips(point steinerpt) if (org(searchtet) != steinerpt) { esymself(searchtet); } - assert(org(searchtet) == steinerpt); - assert(dest(searchtet) == lpt); i = 0; // Count the numbe of tet at the edge [p,lpt]. neightet.tet = NULL; // Init the face. spintet = searchtet; @@ -20476,17 +21878,12 @@ int tetgenmesh::removevertexbyflips(point steinerpt) // Found the edge. searchtet = wrktets[i]; break; - } else { - assert(valence == 4); } } - assert(searchtet.tet != NULL); // Note, we do not detach the three subfaces at p. // They will be removed within a 4-to-1 flip. loc = ONFACE; removeflag = 1; - } else { - // assert(0); DEBUG IT } //removeflag = 1; } @@ -20500,8 +21897,6 @@ int tetgenmesh::removevertexbyflips(point steinerpt) if (org(searchtet) != steinerpt) { esymself(searchtet); } - assert(org(searchtet) == steinerpt); - assert(dest(searchtet) == lpt); spintet = searchtet; while (1) { // Go to the bottom face of this tet. @@ -20513,6 +21908,16 @@ int tetgenmesh::removevertexbyflips(point steinerpt) // Found a non-matching adjacent tet. break; } + { + // [2017-10-15] Check if the tet is inverted? + point chkp1 = org(neightet); + point chkp2 = apex(neightet); + REAL chkori = orient3d(rpt, lpt, chkp1, chkp2); + if (chkori >= 0.0) { + // Either inverted or degenerated. + break; + } + } fnextself(spintet); if (spintet.tet == searchtet.tet) { // 'searchtet' is [p,d,p1,p2]. @@ -20552,13 +21957,9 @@ int tetgenmesh::removevertexbyflips(point steinerpt) // Clear the list for new subfaces. caveshbdlist->restart(); // Insert the new segment. - assert(org(searchtet) == lpt); - assert(dest(searchtet) == rpt); sstbond1(rightseg, searchtet); spintet = searchtet; while (1) { - tsspivot1(spintet, checkseg); // FOR DEBUG ONLY - assert(checkseg.sh == NULL); // FOR DEBUG ONLY tssbond1(spintet, rightseg); fnextself(spintet); if (spintet.tet == searchtet.tet) break; @@ -20577,8 +21978,6 @@ int tetgenmesh::removevertexbyflips(point steinerpt) return 0; } - assert(org(searchtet) == steinerpt); - if (vt == FREESEGVERTEX) { // Detach the subsegments from their surronding tets. for (i = 0; i < 2; i++) { @@ -20629,8 +22028,43 @@ int tetgenmesh::removevertexbyflips(point steinerpt) fnextself(fliptets[3]); // it is [a,p,b,c] eprevself(fliptets[3]); esymself(fliptets[3]); // [a,b,c,p]. - // Remove p by a 4-to-1 flip. - //flip41(fliptets, 1, 0, 0); + if (vt == FREEFACETVERTEX) { + // [2018-03-08] Check if the last 4-to-1 flip is valid. + // fliptets[0],[1],[2] are [p,d,a,b],[p,d,b,c],[p,d,c,a] + triface checktet, chkface; + for (i = 0; i < 3; i++) { + enext(fliptets[i], checktet); + esymself(checktet); // [a,d,b,p],[b,d,c,p],[c,d,a,p] + int scount = 0; int k; + for (k = 0; k < 3; k++) { + esym(checktet, chkface); + if (issubface(chkface)) scount++; + enextself(checktet); + } + if (scount == 3) { + break; // Found a tet which support a 3-to-1 flip. + } else if (scount == 2) { + // This is a strange configuration. Debug it. + // Do not do this flip. + delete [] fliptets; + return 0; + } + } + if (i == 3) { + // No tet in [p,d,a,b],[p,d,b,c],[p,d,c,a] support it. + int scount = 0; + for (i = 0; i < 3; i++) { + eprev(fliptets[i], checktet); + esymself(checktet); // [p,a,b,d],[p,b,c,d],[p,c,a,d] + if (issubface(chkface)) scount++; + } + if (scount != 3) { + // Do not do this flip. + delete [] fliptets; + return 0; + } + } + } // if (vt == FREEFACETVERTEX) flip41(fliptets, 1, &fc); //recenttet = fliptets[0]; } else if (loc == ONFACE) { @@ -20652,13 +22086,10 @@ int tetgenmesh::removevertexbyflips(point steinerpt) if (vt == FREEFACETVERTEX) { // We need to determine the location of three subfaces at p. valence = 0; // Re-use it. - // Check if subfaces are all located in the lower three tets. - // i.e., [e,p,a,b], [e,p,b,c], and [e,p,c,a]. for (i = 3; i < 6; i++) { if (issubface(fliptets[i])) valence++; } if (valence > 0) { - assert(valence == 2); // We must do 3-to-2 flip in the upper part. We simply re-arrange // the six tets. for (i = 0; i < 3; i++) { @@ -20674,7 +22105,42 @@ int tetgenmesh::removevertexbyflips(point steinerpt) fliptets[4] = fliptets[5]; fliptets[5] = wrktets[1]; } - } + // [2018-03-08] Check if the last 4-to-1 flip is valid. + // fliptets[0],[1],[2] are [p,d,a,b],[p,d,b,c],[p,d,c,a] + triface checktet, chkface; + for (i = 0; i < 3; i++) { + enext(fliptets[i], checktet); + esymself(checktet); // [a,d,b,p],[b,d,c,p],[c,d,a,p] + int scount = 0; int k; + for (k = 0; k < 3; k++) { + esym(checktet, chkface); + if (issubface(chkface)) scount++; + enextself(checktet); + } + if (scount == 3) { + break; // Found a tet which support a 3-to-1 flip. + } else if (scount == 2) { + // This is a strange configuration. Debug it. + // Do not do this flip. + delete [] fliptets; + return 0; + } + } + if (i == 3) { + // No tet in [p,d,a,b],[p,d,b,c],[p,d,c,a] support it. + int scount = 0; + for (i = 0; i < 3; i++) { + eprev(fliptets[i], checktet); + esymself(checktet); // [p,a,b,d],[p,b,c,d],[p,c,a,d] + if (issubface(chkface)) scount++; + } + if (scount != 3) { + // Do not do this flip. + delete [] fliptets; + return 0; + } + } + } // vt == FREEFACETVERTEX // Remove p by a 6-to-2 flip, which is a combination of two flips: // a 3-to-2 (deletes the edge [e,p]), and // a 4-to-1 (deletes the vertex p). @@ -20701,7 +22167,6 @@ int tetgenmesh::removevertexbyflips(point steinerpt) fnextself(spintet); if (spintet.tet == searchtet.tet) break; } - assert(n >= 3); // Collect the 2n tets containing 'p'. fliptets = new triface[2 * n]; fliptets[0] = searchtet; // [p,b,p_0,p_1] @@ -20786,9 +22251,7 @@ int tetgenmesh::removevertexbyflips(point steinerpt) // Save the new tet [e,d,p_n-1,p_0] // FOR DEBUG ONLY fliptets[n-1] = wrktets[0]; // [e,d,p_n-1,p_0] // FOR DEBUG ONLY //recenttet = fliptets[0]; - } else { - assert(0); // Unknown location. - } // if (iloc == ...) + } delete [] fliptets; @@ -20803,18 +22266,15 @@ int tetgenmesh::removevertexbyflips(point steinerpt) // The original segment is returned in 'rightseg'. rightseg.shver = 0; - assert(sorg(rightseg) == lpt); - assert(sdest(rightseg) == rpt); - // Insert the new segment. point2tetorg(lpt, searchtet); finddirection(&searchtet, rpt); - assert(dest(searchtet) == rpt); + if (dest(searchtet) != rpt) { + terminatetetgen(this, 2); + } sstbond1(rightseg, searchtet); spintet = searchtet; while (1) { - tsspivot1(spintet, checkseg); // FOR DEBUG ONLY - assert(checkseg.sh == NULL); // FOR DEBUG ONLY tssbond1(spintet, rightseg); fnextself(spintet); if (spintet.tet == searchtet.tet) break; @@ -20828,9 +22288,7 @@ int tetgenmesh::removevertexbyflips(point steinerpt) while (1) { if (sorg(spinsh) != lpt) { sesymself(spinsh); - assert(sorg(spinsh) == lpt); } - assert(sdest(spinsh) == rpt); apexpt = sapex(spinsh); // Find the adjacent tet of [lpt,rpt,apexpt]; spintet = searchtet; @@ -20844,8 +22302,6 @@ int tetgenmesh::removevertexbyflips(point steinerpt) break; } fnextself(spintet); - assert(spintet.tet != searchtet.tet); - //if (spintet.tet == searchtet.tet) break; } spivotself(spinsh); if (spinsh.sh == parentsh.sh) break; @@ -20877,118 +22333,295 @@ int tetgenmesh::removevertexbyflips(point steinerpt) return 1; } -/////////////////////////////////////////////////////////////////////////////// -// // -// suppressbdrysteinerpoint() Suppress a boundary Steiner point // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// smoothpoint() Moving a vertex to improve the mesh quality. // +// // +// 'smtpt' (p) is a point to be smoothed. Generally, it is a Steiner point. // +// It may be not a vertex of the mesh. // +// // +// This routine tries to move 'p' inside its star until a selected objective // +// function over all tetrahedra in the star is improved. The function may be // +// the some quality measures, i.e., aspect ratio, maximum dihedral angel, or // +// simply the volume of the tetrahedra. // +// // +// 'linkfacelist' contains the list of link faces of 'p'. Since a link face // +// has two orientations, ccw or cw, with respect to 'p'. 'ccw' indicates // +// the orientation is ccw (1) or not (0). // +// // +// 'opm' is a structure contains the parameters of the objective function. // +// It is needed by the evaluation of the function value. // +// // +// The return value indicates weather the point is smoothed or not. // +// // +// ASSUMPTION: This routine assumes that all link faces are true faces, i.e, // +// no face has 'dummypoint' as its vertex. // +// // +//============================================================================// -int tetgenmesh::suppressbdrysteinerpoint(point steinerpt) +int tetgenmesh::smoothpoint(point smtpt, arraypool *linkfacelist, int ccw, + optparameters *opm) { - face parentsh, spinsh, *parysh; - face leftseg, rightseg; - point lpt = NULL, rpt = NULL; - int i; - - verttype vt = pointtype(steinerpt); + triface *parytet, *parytet1, swaptet; + badface bf; + point pa, pb, pc; + REAL fcent[3], startpt[3], nextpt[3], bestpt[3]; + REAL oldval, minval = 0.0, val; + REAL maxcosd; // oldang, newang; + REAL ori, diff; + int numdirs, iter; + int i, j, k; - if (vt == FREESEGVERTEX) { - sdecode(point2sh(steinerpt), leftseg); - leftseg.shver = 0; - if (sdest(leftseg) == steinerpt) { - senext(leftseg, rightseg); - spivotself(rightseg); - assert(rightseg.sh != NULL); - rightseg.shver = 0; - assert(sorg(rightseg) == steinerpt); - } else { - assert(sorg(leftseg) == steinerpt); - rightseg = leftseg; - senext2(rightseg, leftseg); - spivotself(leftseg); - assert(leftseg.sh != NULL); - leftseg.shver = 0; - assert(sdest(leftseg) == steinerpt); - } - lpt = sorg(leftseg); - rpt = sdest(rightseg); - if (b->verbose > 2) { - printf(" Suppressing Steiner point %d in segment (%d, %d).\n", - pointmark(steinerpt), pointmark(lpt), pointmark(rpt)); - } - // Get all subfaces at the left segment [lpt, steinerpt]. - spivot(leftseg, parentsh); - spinsh = parentsh; - while (1) { - cavesegshlist->newindex((void **) &parysh); - *parysh = spinsh; - // Orient the face consistently. - if (sorg(*parysh)!= sorg(parentsh)) sesymself(*parysh); - spivotself(spinsh); - if (spinsh.sh == NULL) break; - if (spinsh.sh == parentsh.sh) break; - } - if (cavesegshlist->objects < 2) { - // It is a single segment. Not handle it yet. - cavesegshlist->restart(); - return 0; - } - } else if (vt == FREEFACETVERTEX) { - if (b->verbose > 2) { - printf(" Suppressing Steiner point %d from facet.\n", - pointmark(steinerpt)); - } - sdecode(point2sh(steinerpt), parentsh); - // A facet Steiner point. There are exactly two sectors. - for (i = 0; i < 2; i++) { - cavesegshlist->newindex((void **) &parysh); - *parysh = parentsh; - sesymself(parentsh); - } - } else { - return 0; + // Decide the number of moving directions. + numdirs = (int) linkfacelist->objects; + if (numdirs > opm->numofsearchdirs) { + numdirs = opm->numofsearchdirs; // Maximum search directions. } - triface searchtet, neightet, *parytet; - point pa, pb, pc, pd; - REAL v1[3], v2[3], len, u; + // Set the initial value. + opm->imprval = opm->initval; + iter = 0; - REAL startpt[3] = {0,}, samplept[3] = {0,}, candpt[3] = {0,}; - REAL ori, minvol, smallvol; - int samplesize; - int it, j, k; + for (i = 0; i < 3; i++) { + bestpt[i] = startpt[i] = smtpt[i]; + } - int n = (int) cavesegshlist->objects; - point *newsteiners = new point[n]; - for (i = 0; i < n; i++) newsteiners[i] = NULL; + // Iterate until the obj function is not improved. + while (1) { - // Search for each sector an interior vertex. - for (i = 0; i < cavesegshlist->objects; i++) { - parysh = (face *) fastlookup(cavesegshlist, i); - stpivot(*parysh, searchtet); - // Skip it if it is outside. - if (ishulltet(searchtet)) continue; - // Get the "half-ball". Tets in 'cavetetlist' all contain 'steinerpt' as - // opposite. Subfaces in 'caveshlist' all contain 'steinerpt' as apex. - // Moreover, subfaces are oriented towards the interior of the ball. - setpoint2tet(steinerpt, encode(searchtet)); - getvertexstar(0, steinerpt, cavetetlist, NULL, caveshlist); - // Calculate the searching vector. - pa = sorg(*parysh); - pb = sdest(*parysh); - pc = sapex(*parysh); - facenormal(pa, pb, pc, v1, 1, NULL); - len = sqrt(dot(v1, v1)); - assert(len > 0.0); - v1[0] /= len; - v1[1] /= len; + // Find the best next location. + oldval = opm->imprval; + + for (i = 0; i < numdirs; i++) { + // Randomly pick a link face (0 <= k <= objects - i - 1). + k = (int) randomnation(linkfacelist->objects - i); + parytet = (triface *) fastlookup(linkfacelist, k); + // Calculate a new position from 'p' to the center of this face. + pa = org(*parytet); + pb = dest(*parytet); + pc = apex(*parytet); + for (j = 0; j < 3; j++) { + fcent[j] = (pa[j] + pb[j] + pc[j]) / 3.0; + } + for (j = 0; j < 3; j++) { + nextpt[j] = startpt[j] + opm->searchstep * (fcent[j] - startpt[j]); + } + // Calculate the largest minimum function value for the new location. + for (j = 0; j < linkfacelist->objects; j++) { + parytet = (triface *) fastlookup(linkfacelist, j); + if (ccw) { + pa = org(*parytet); + pb = dest(*parytet); + } else { + pb = org(*parytet); + pa = dest(*parytet); + } + pc = apex(*parytet); + ori = orient3d(pa, pb, pc, nextpt); + if (ori < 0.0) { + // Calcuate the objective function value. + if (opm->max_min_volume) { + //val = -ori; + val = - orient3dfast(pa, pb, pc, nextpt); + } else if (opm->min_max_aspectratio) { + get_tetqual(pa, pb, pc, nextpt, &bf); + val = 1.0 / bf.key; + } else if (opm->min_max_dihedangle) { + get_tetqual(pa, pb, pc, nextpt, &bf); + maxcosd = bf.cent[0]; + if (maxcosd < -1) maxcosd = -1.0; // Rounding. + val = maxcosd + 1.0; // Make it be positive. + } else { + // Unknown objective function. + val = 0.0; + } + } else { // ori >= 0.0; + // An invalid new tet. + // This may happen if the mesh contains inverted elements. + if (opm->max_min_volume) { + //val = -ori; + val = - orient3dfast(pa, pb, pc, nextpt); + } else { + // Discard this point. + break; // j + } + } // if (ori >= 0.0) + // Stop looping when the object value is not improved. + if (val <= opm->imprval) { + break; // j + } else { + // Remember the smallest improved value. + if (j == 0) { + minval = val; + } else { + minval = (val < minval) ? val : minval; + } + } + } // j + if (j == linkfacelist->objects) { + // The function value has been improved. + opm->imprval = minval; + // Save the new location of the point. + for (j = 0; j < 3; j++) bestpt[j] = nextpt[j]; + } + // Swap k-th and (object-i-1)-th entries. + j = linkfacelist->objects - i - 1; + parytet = (triface *) fastlookup(linkfacelist, k); + parytet1 = (triface *) fastlookup(linkfacelist, j); + swaptet = *parytet1; + *parytet1 = *parytet; + *parytet = swaptet; + } // i + + diff = opm->imprval - oldval; + if (diff > 0.0) { + // Is the function value improved effectively? + if (opm->max_min_volume) { + //if ((diff / oldval) < b->epsilon) diff = 0.0; + } else if (opm->min_max_aspectratio) { + if ((diff / oldval) < 1e-3) diff = 0.0; + } else if (opm->min_max_dihedangle) { + //oldang = acos(oldval - 1.0); + //newang = acos(opm->imprval - 1.0); + //if ((oldang - newang) < 0.00174) diff = 0.0; // about 0.1 degree. + } else { + // Unknown objective function. + terminatetetgen(this, 2); + } + } + + if (diff > 0.0) { + // Yes, move p to the new location and continue. + for (j = 0; j < 3; j++) startpt[j] = bestpt[j]; + iter++; + if ((opm->maxiter > 0) && (iter >= opm->maxiter)) { + // Maximum smoothing iterations reached. + break; + } + } else { + break; + } + + } // while (1) + + if (iter > 0) { + // The point has been smoothed. + opm->smthiter = iter; // Remember the number of iterations. + // The point has been smoothed. Update it to its new position. + for (i = 0; i < 3; i++) smtpt[i] = startpt[i]; + } + + return iter; +} + +//============================================================================// +// // +// suppressbdrysteinerpoint() Suppress a boundary Steiner point // +// // +//============================================================================// + +int tetgenmesh::suppressbdrysteinerpoint(point steinerpt) +{ + face parentsh, spinsh, *parysh; + face leftseg, rightseg; + point lpt = NULL, rpt = NULL; + int i; + + verttype vt = pointtype(steinerpt); + + if (vt == FREESEGVERTEX) { + sdecode(point2sh(steinerpt), leftseg); + leftseg.shver = 0; + if (sdest(leftseg) == steinerpt) { + senext(leftseg, rightseg); + spivotself(rightseg); + rightseg.shver = 0; + } else { + rightseg = leftseg; + senext2(rightseg, leftseg); + spivotself(leftseg); + leftseg.shver = 0; + } + lpt = sorg(leftseg); + rpt = sdest(rightseg); + if (b->verbose > 2) { + printf(" Suppressing Steiner point %d in segment (%d, %d).\n", + pointmark(steinerpt), pointmark(lpt), pointmark(rpt)); + } + // Get all subfaces at the left segment [lpt, steinerpt]. + spivot(leftseg, parentsh); + if (parentsh.sh != NULL) { + // It is not a dangling segment. + spinsh = parentsh; + while (1) { + cavesegshlist->newindex((void **) &parysh); + *parysh = spinsh; + // Orient the face consistently. + if (sorg(*parysh)!= sorg(parentsh)) sesymself(*parysh); + spivotself(spinsh); + if (spinsh.sh == NULL) break; + if (spinsh.sh == parentsh.sh) break; + } + } + if (cavesegshlist->objects < 2) { + // It is a single segment. Not handle it yet. + cavesegshlist->restart(); + return 0; + } + } else if (vt == FREEFACETVERTEX) { + if (b->verbose > 2) { + printf(" Suppressing Steiner point %d from facet.\n", + pointmark(steinerpt)); + } + sdecode(point2sh(steinerpt), parentsh); + // A facet Steiner point. There are exactly two sectors. + for (i = 0; i < 2; i++) { + cavesegshlist->newindex((void **) &parysh); + *parysh = parentsh; + sesymself(parentsh); + } + } else { + return 0; // no need to suppress it. + } + + triface searchtet, neightet, *parytet; + point pa, pb, pc, pd; + REAL v1[3], v2[3], len, u; + + REAL startpt[3] = {0,}, samplept[3] = {0,}, candpt[3] = {0,}; + REAL ori, minvol, smallvol; + int samplesize; + int it, j, k; + + int n = (int) cavesegshlist->objects; + point *newsteiners = new point[n]; + for (i = 0; i < n; i++) newsteiners[i] = NULL; + + // Search for each sector an interior vertex. + for (i = 0; i < cavesegshlist->objects; i++) { + parysh = (face *) fastlookup(cavesegshlist, i); + stpivot(*parysh, searchtet); + // Skip it if it is outside. + if (ishulltet(searchtet)) continue; + // Get the "half-ball". Tets in 'cavetetlist' all contain 'steinerpt' as + // opposite. Subfaces in 'caveshlist' all contain 'steinerpt' as apex. + // Moreover, subfaces are oriented towards the interior of the ball. + setpoint2tet(steinerpt, encode(searchtet)); + getvertexstar(0, steinerpt, cavetetlist, NULL, caveshlist); + // Calculate the searching vector. + pa = sorg(*parysh); + pb = sdest(*parysh); + pc = sapex(*parysh); + facenormal(pa, pb, pc, v1, 1, NULL); + len = sqrt(dot(v1, v1)); + v1[0] /= len; + v1[1] /= len; v1[2] /= len; if (vt == FREESEGVERTEX) { parysh = (face *) fastlookup(cavesegshlist, (i + 1) % n); pd = sapex(*parysh); facenormal(pb, pa, pd, v2, 1, NULL); len = sqrt(dot(v2, v2)); - assert(len > 0.0); v2[0] /= len; v2[1] /= len; v2[2] /= len; @@ -21020,13 +22653,14 @@ int tetgenmesh::suppressbdrysteinerpoint(point steinerpt) if (ori >= 0) { // Found! Calculate the intersection. planelineint(pa, pb, pc, steinerpt, v2, startpt, &u); - assert(u != 0.0); break; } } } } // j - assert(j < cavetetlist->objects); // There must be an intersection. + if (j == cavetetlist->objects) { + break; // There is no intersection!! Debug is needed. + } // Close the ball by adding the subfaces. for (j = 0; j < caveshlist->objects; j++) { parysh = (face *) fastlookup(caveshlist, j); @@ -21054,6 +22688,15 @@ int tetgenmesh::suppressbdrysteinerpoint(point steinerpt) pb = dest(*parytet); pc = apex(*parytet); ori = orient3d(pb, pa, pc, samplept); + { + // [2017-10-15] Rounding + REAL lab = distance(pa, pb); + REAL lbc = distance(pb, pc); + REAL lca = distance(pc, pa); + REAL lv = (lab + lbc + lca) / 3.0; + REAL l3 = lv*lv*lv; + if (fabs(ori) / l3 < 1e-8) ori = 0.0; + } if (ori <= 0) { break; // An invalid tet. } @@ -21117,255 +22760,89 @@ int tetgenmesh::suppressbdrysteinerpoint(point steinerpt) return 0; } - // Remove p from the segment or the facet. - triface newtet, newface, spintet; - face newsh, neighsh; - face *splitseg, checkseg; - int slawson = 0; // Do not do flip afterword. - int t1ver; - - if (vt == FREESEGVERTEX) { - // Detach 'leftseg' and 'rightseg' from their adjacent tets. - // These two subsegments will be deleted. - sstpivot1(leftseg, neightet); - spintet = neightet; - while (1) { - tssdissolve1(spintet); - fnextself(spintet); - if (spintet.tet == neightet.tet) break; - } - sstpivot1(rightseg, neightet); - spintet = neightet; - while (1) { - tssdissolve1(spintet); - fnextself(spintet); - if (spintet.tet == neightet.tet) break; - } + // First insert Steiner points into the mesh. + // 'cavesegshlist' will be used by insertpoint(). + //int nfaces = cavesegshlist->objects; + face *segshlist = new face[n]; + for (i = 0; i < cavesegshlist->objects; i++) { + segshlist[i] = * (face *) fastlookup(cavesegshlist, i); } + cavesegshlist->restart(); - // Loop through all sectors bounded by facets at this segment. - // Within each sector, create a new Steiner point 'np', and replace 'p' - // by 'np' for all tets in this sector. - for (i = 0; i < cavesegshlist->objects; i++) { - parysh = (face *) fastlookup(cavesegshlist, i); + for (i = 0; i < n; i++) { + //assert(caveoldtetlist->objects == 0); + //assert(cavetetlist->objects == 0); + parysh = &(segshlist[i]); // 'parysh' is the face [lpt, steinerpt, #]. - stpivot(*parysh, neightet); - // Get all tets in this sector. - setpoint2tet(steinerpt, encode(neightet)); + stpivot(*parysh, searchtet); + // Skip it if it is outside. + if (ishulltet(searchtet)) continue; + + // Get the "half-ball". Tets in 'cavetetlist' all contain 'steinerpt' as + // opposite. Subfaces in 'caveshlist' all contain 'steinerpt' as apex. + // Moreover, subfaces are oriented towards the interior of the ball. + setpoint2tet(steinerpt, encode(searchtet)); getvertexstar(0, steinerpt, cavetetlist, NULL, caveshlist); - if (!ishulltet(neightet)) { - // Within each tet in the ball, replace 'p' by 'np'. - for (j = 0; j < cavetetlist->objects; j++) { - parytet = (triface *) fastlookup(cavetetlist, j); - setoppo(*parytet, newsteiners[i]); - } // j - // Point to a parent tet. - parytet = (triface *) fastlookup(cavetetlist, 0); - setpoint2tet(newsteiners[i], (tetrahedron) (parytet->tet)); - st_volref_count++; - if (steinerleft > 0) steinerleft--; - } - // Disconnect the set of boundary faces. They're temporarily open faces. - // They will be connected to the new tets after 'p' is removed. - for (j = 0; j < caveshlist->objects; j++) { - // Get a boundary face. - parysh = (face *) fastlookup(caveshlist, j); - stpivot(*parysh, neightet); - //assert(apex(neightet) == newpt); - // Clear the connection at this face. - dissolve(neightet); - tsdissolve(neightet); + + // Get all tets in this sector. + for (int j = 0; j < cavetetlist->objects; j++) { + neightet = * (triface *) fastlookup(cavetetlist, j); + infect(neightet); + caveoldtetlist->newindex((void **) &parytet); + *parytet = neightet; } - // Clear the working lists. cavetetlist->restart(); caveshlist->restart(); - } // i - cavesegshlist->restart(); - if (vt == FREESEGVERTEX) { - spivot(rightseg, parentsh); // 'rightseg' has p as its origin. - splitseg = &rightseg; - } else { - if (sdest(parentsh) == steinerpt) { - senextself(parentsh); - } else if (sapex(parentsh) == steinerpt) { - senext2self(parentsh); + insertvertexflags ivf; + searchtet = neightet; // No need point location. + ivf.iloc = (int) INSTAR; // No need point location. + // The following are default options. + //ivf.bowywat = 0; + //ivf.lawson = 0; + //ivf.validflag = 0; // no need to validate cavity. + //ivf.chkencflag = 0; //chkencflag; + ivf.assignmeshsize = b->metric; + if (ivf.assignmeshsize) { + // Search the tet containing 'steinerpt' for size interpolation. + locate(newsteiners[i], &searchtet); } - assert(sorg(parentsh) == steinerpt); - splitseg = NULL; + + // Insert the new point into the tetrahedralization T. + // Note that T is convex (nonconvex = 0). + if (insertpoint(newsteiners[i], &searchtet, NULL, NULL, &ivf)) { + // The vertex has been inserted. + st_volref_count++; + if (steinerleft > 0) steinerleft--; + //return 1; + } else { + // Not inserted. + //assert(0); + pointdealloc(newsteiners[i]); + newsteiners[i] = NULL; + break; //return 0; + } + } // i + + delete [] segshlist; + + if (i < n) { + //assert(0); + delete [] newsteiners; + return 0; } - sremovevertex(steinerpt, &parentsh, splitseg, slawson); - if (vt == FREESEGVERTEX) { - // The original segment is returned in 'rightseg'. - rightseg.shver = 0; - } - - // For each new subface, create two new tets at each side of it. - // Both of the two new tets have its opposite be dummypoint. - for (i = 0; i < caveshbdlist->objects; i++) { - parysh = (face *) fastlookup(caveshbdlist, i); - sinfect(*parysh); // Mark it for connecting new tets. - newsh = *parysh; - pa = sorg(newsh); - pb = sdest(newsh); - pc = sapex(newsh); - maketetrahedron(&newtet); - maketetrahedron(&neightet); - setvertices(newtet, pa, pb, pc, dummypoint); - setvertices(neightet, pb, pa, pc, dummypoint); - bond(newtet, neightet); - tsbond(newtet, newsh); - sesymself(newsh); - tsbond(neightet, newsh); - } - // Temporarily increase the hullsize. - hullsize += (caveshbdlist->objects * 2l); - - if (vt == FREESEGVERTEX) { - // Connecting new tets at the recovered segment. - spivot(rightseg, parentsh); - assert(parentsh.sh != NULL); - spinsh = parentsh; - while (1) { - if (sorg(spinsh) != lpt) sesymself(spinsh); - // Get the new tet at this subface. - stpivot(spinsh, newtet); - tssbond1(newtet, rightseg); - // Go to the other face at this segment. - spivot(spinsh, neighsh); - if (sorg(neighsh) != lpt) sesymself(neighsh); - sesymself(neighsh); - stpivot(neighsh, neightet); - tssbond1(neightet, rightseg); - sstbond1(rightseg, neightet); - // Connecting two adjacent tets at this segment. - esymself(newtet); - esymself(neightet); - // Connect the two tets (at rightseg) together. - bond(newtet, neightet); - // Go to the next subface. - spivotself(spinsh); - if (spinsh.sh == parentsh.sh) break; - } - } - - // Connecting new tets at new subfaces together. - for (i = 0; i < caveshbdlist->objects; i++) { - parysh = (face *) fastlookup(caveshbdlist, i); - newsh = *parysh; - //assert(sinfected(newsh)); - // Each new subface contains two new tets. - for (k = 0; k < 2; k++) { - stpivot(newsh, newtet); - for (j = 0; j < 3; j++) { - // Check if this side is open. - esym(newtet, newface); - if (newface.tet[newface.ver & 3] == NULL) { - // An open face. Connect it to its adjacent tet. - sspivot(newsh, checkseg); - if (checkseg.sh != NULL) { - // A segment. It must not be the recovered segment. - tssbond1(newtet, checkseg); - sstbond1(checkseg, newtet); - } - spivot(newsh, neighsh); - if (neighsh.sh != NULL) { - // The adjacent subface exists. It's not a dangling segment. - if (sorg(neighsh) != sdest(newsh)) sesymself(neighsh); - stpivot(neighsh, neightet); - if (sinfected(neighsh)) { - esymself(neightet); - assert(neightet.tet[neightet.ver & 3] == NULL); - } else { - // Search for an open face at this edge. - spintet = neightet; - while (1) { - esym(spintet, searchtet); - fsym(searchtet, spintet); - if (spintet.tet == NULL) break; - assert(spintet.tet != neightet.tet); - } - // Found an open face at 'searchtet'. - neightet = searchtet; - } - } else { - // The edge (at 'newsh') is a dangling segment. - assert(checkseg.sh != NULL); - // Get an adjacent tet at this segment. - sstpivot1(checkseg, neightet); - assert(!isdeadtet(neightet)); - if (org(neightet) != sdest(newsh)) esymself(neightet); - assert((org(neightet) == sdest(newsh)) && - (dest(neightet) == sorg(newsh))); - // Search for an open face at this edge. - spintet = neightet; - while (1) { - esym(spintet, searchtet); - fsym(searchtet, spintet); - if (spintet.tet == NULL) break; - assert(spintet.tet != neightet.tet); - } - // Found an open face at 'searchtet'. - neightet = searchtet; - } - pc = apex(newface); - if (apex(neightet) == steinerpt) { - // Exterior case. The 'neightet' is a hull tet which contain - // 'steinerpt'. It will be deleted after 'steinerpt' is removed. - assert(pc == dummypoint); - caveoldtetlist->newindex((void **) &parytet); - *parytet = neightet; - // Connect newface to the adjacent hull tet of 'neightet', which - // has the same edge as 'newface', and does not has 'steinerpt'. - fnextself(neightet); - } else { - if (pc == dummypoint) { - if (apex(neightet) != dummypoint) { - setapex(newface, apex(neightet)); - // A hull tet has turned into an interior tet. - hullsize--; // Must update the hullsize. - } - } - } - bond(newface, neightet); - } // if (newface.tet[newface.ver & 3] == NULL) - enextself(newtet); - senextself(newsh); - } // j - sesymself(newsh); - } // k - } // i - - // Unmark all new subfaces. - for (i = 0; i < caveshbdlist->objects; i++) { - parysh = (face *) fastlookup(caveshbdlist, i); - suninfect(*parysh); - } - caveshbdlist->restart(); - - if (caveoldtetlist->objects > 0l) { - // Delete hull tets which contain 'steinerpt'. - for (i = 0; i < caveoldtetlist->objects; i++) { - parytet = (triface *) fastlookup(caveoldtetlist, i); - tetrahedrondealloc(parytet->tet); - } - // Must update the hullsize. - hullsize -= caveoldtetlist->objects; - caveoldtetlist->restart(); + // Now remove the Steiner point from the segment. + if (!removevertexbyflips(steinerpt)) { + //assert(0); + delete [] newsteiners; + return 0; } + // We've removed a Steiner points. setpointtype(steinerpt, UNUSEDVERTEX); unuverts++; - if (vt == FREESEGVERTEX) { - st_segref_count--; - } else { // vt == FREEFACETVERTEX - st_facref_count--; - } - if (steinerleft > 0) steinerleft++; // We've removed a Steiner points. - - - point *parypt; + int steinercount = 0; int bak_fliplinklevel = b->fliplinklevel; @@ -21375,8 +22852,9 @@ int tetgenmesh::suppressbdrysteinerpoint(point steinerpt) for (i = 0; i < n; i++) { if (newsteiners[i] != NULL) { if (!removevertexbyflips(newsteiners[i])) { - if (b->nobisect_param > 0) { // Not -Y0 + if (b->supsteiner_level > 0) { // Not -Y/0 // Save it in subvertstack for removal. + point *parypt; subvertstack->newindex((void **) &parypt); *parypt = newsteiners[i]; } @@ -21388,7 +22866,7 @@ int tetgenmesh::suppressbdrysteinerpoint(point steinerpt) b->fliplinklevel = bak_fliplinklevel; if (steinercount > 0) { - if (b->verbose > 2) { + if (b->verbose > 3) { printf(" Added %d interior Steiner points.\n", steinercount); } } @@ -21399,15 +22877,15 @@ int tetgenmesh::suppressbdrysteinerpoint(point steinerpt) } -/////////////////////////////////////////////////////////////////////////////// -// // -// suppresssteinerpoints() Suppress Steiner points. // -// // -// All Steiner points have been saved in 'subvertstack' in the routines // -// carveholes() and suppresssteinerpoint(). // -// Each Steiner point is either removed or shifted into the interior. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// suppresssteinerpoints() Suppress Steiner points. // +// // +// All Steiner points have been saved in 'subvertstack' in the routines // +// carveholes() and suppresssteinerpoint(). // +// Each Steiner point is either removed or shifted into the interior. // +// // +//============================================================================// int tetgenmesh::suppresssteinerpoints() { @@ -21443,7 +22921,7 @@ int tetgenmesh::suppresssteinerpoints() } } - if (b->nobisect_param > 0) { // -Y1 + if (b->supsteiner_level > 0) { // -Y/1 for (i = 0; i < subvertstack->objects; i++) { parypt = (point *) fastlookup(subvertstack, i); rempt = *parypt; @@ -21465,7 +22943,7 @@ int tetgenmesh::suppresssteinerpoints() b->fliplinklevel = bak_fliplinklevel; - if (b->nobisect_param > 1) { // -Y2 + if (b->supsteiner_level > 1) { // -Y/2 // Smooth interior Steiner points. optparameters opm; triface *parytet; @@ -21559,11 +23037,11 @@ int tetgenmesh::suppresssteinerpoints() return 1; } -/////////////////////////////////////////////////////////////////////////////// -// // -// recoverboundary() Recover segments and facets. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// recoverboundary() Recover segments and facets. // +// // +//============================================================================// void tetgenmesh::recoverboundary(clock_t& tv) { @@ -21583,6 +23061,14 @@ void tetgenmesh::recoverboundary(clock_t& tv) printf("Recovering boundaries...\n"); } + boundary_recovery_flag = 1; + cos_collinear_ang_tol = cos(b->collinear_ang_tol / 180. * PI); + + if (segmentendpointslist == NULL) { + // We need segment adjacent information during flips. + makesegmentendpointsmap(); + } + if (b->verbose) { printf(" Recovering segments.\n"); @@ -21678,9 +23164,25 @@ void tetgenmesh::recoverboundary(clock_t& tv) } } + //int bak_verbose = b->verbose; + //if (b->verbose < 3) { + // b->verbose = 3; // debug... + //} + if (misseglist->objects > 0) { // Third, trying to recover segments by doing more flips (fullsearch) // and adding Steiner points in the volume. + + if (b->verbose) { + printf(" Recovering Delaunay.\n"); + } + + recoverdelaunay(); + + if (b->verbose) { + printf(" Recovering segments with Steiner points.\n"); + } + while (misseglist->objects > 0) { ms = misseglist->objects; for (i = 0; i < misseglist->objects; i++) { @@ -21689,7 +23191,8 @@ void tetgenmesh::recoverboundary(clock_t& tv) } misseglist->restart(); - recoversegments(misseglist, 1, 1); + //recoversegments(misseglist, 1, 1); + recoversegments(misseglist, 0, 1); // no full search if (misseglist->objects < ms) { // The number of missing segments is reduced. @@ -21707,13 +23210,35 @@ void tetgenmesh::recoverboundary(clock_t& tv) // Last, trying to recover segments by doing more flips (fullsearch), // and adding Steiner points in the volume, and splitting segments. long bak_inpoly_count = st_volref_count; //st_inpoly_count; - for (i = 0; i < misseglist->objects; i++) { - subsegstack->newindex((void **) &paryseg); - *paryseg = * (face *) fastlookup(misseglist, i); + + if (b->verbose) { + printf(" Recovering Delaunay.\n"); + } + + recoverdelaunay(); + + if (b->verbose) { + printf(" Recovering segments with Steiner points.\n"); } - misseglist->restart(); - recoversegments(misseglist, 1, 2); + while (misseglist->objects > 0) { + ms = misseglist->objects; + for (i = 0; i < misseglist->objects; i++) { + subsegstack->newindex((void **) &paryseg); + *paryseg = * (face *) fastlookup(misseglist, i); + } + misseglist->restart(); + + //recoversegments(misseglist, 1, 2); + recoversegments(misseglist, 0, 2); // no full search + + if (misseglist->objects < ms) { + // The number of missing segments is reduced. + continue; + } else { + break; + } + } // while (misseglist->objects > 0) if (b->verbose) { printf(" Added %ld Steiner points in segments.\n", st_segref_count); @@ -21722,12 +23247,34 @@ void tetgenmesh::recoverboundary(clock_t& tv) st_volref_count - bak_inpoly_count); } } - assert(misseglist->objects == 0l); + + // There may be un-recovered subsegments. + if (misseglist->objects > 0l) { + if (b->verbose) { + printf(" !! %ld subsegments are missing.\n", misseglist->objects); + } + } + } + + if (skipped_segment_list != NULL) { + if (!b->quiet) { + printf(" Skipped %ld segments due to intersections.\n", + skipped_segment_list->objects); + } + delete skipped_segment_list; } + //b->verbose = bak_verbose; // debug... + if (st_segref_count > 0) { // Try to remove the Steiner points added in segments. + if (b->verbose) { + printf(" Suppressing %ld Steiner points in segments.\n", st_segref_count); + } + int bak_fliplinklevel = b->fliplinklevel; + b->fliplinklevel = 20; // limit this value + bak_segref_count = st_segref_count; bak_volref_count = st_volref_count; for (i = 0; i < subvertstack->objects; i++) { @@ -21754,6 +23301,8 @@ void tetgenmesh::recoverboundary(clock_t& tv) } } } + + b->fliplinklevel = bak_fliplinklevel; // restore it. subvertstack->restart(); } @@ -21801,7 +23350,12 @@ void tetgenmesh::recoverboundary(clock_t& tv) if (nit >= 3) { //break; // Do the last round with unbounded flip link level. - b->fliplinklevel = 100000; + //b->fliplinklevel = 100000; // this can be very slow. + if (autofliplinklevel < 30) { + b->fliplinklevel = 30; + } else { + b->fliplinklevel = autofliplinklevel + 30; + } } } else { ms = misshlist->objects; @@ -21823,28 +23377,182 @@ void tetgenmesh::recoverboundary(clock_t& tv) } // while (1) if (b->verbose) { - printf(" %ld (%ld) subfaces are recovered (missing).\n", + printf(" %ld (%ld) subfaces are recovered (missing).\n", subfaces->items - misshlist->objects, misshlist->objects); } if (misshlist->objects > 0) { // There are missing subfaces. Add Steiner points. - for (i = 0; i < misshlist->objects; i++) { - subfacstack->newindex((void **) &parysh); - *parysh = * (face *) fastlookup(misshlist, i); + + if (b->verbose) { + printf(" Recovering Delaunay.\n"); + } + + recoverdelaunay(); + + if (b->verbose) { + printf(" Recovering facets with Steiner points.\n"); + } + + while (misshlist->objects > 0) { + ms = misshlist->objects; + for (i = 0; i < misshlist->objects; i++) { + subfacstack->newindex((void **) &parysh); + *parysh = * (face *) fastlookup(misshlist, i); + } + misshlist->restart(); + + recoversubfaces(misshlist, 1); + + if (misshlist->objects < ms) { + continue; + } else { + break; + } + } + + if (b->verbose) { + printf(" %ld (%ld) subfaces are recovered (missing).\n", + subfaces->items - misshlist->objects, misshlist->objects); + printf(" Added %ld Steiner points in facets.\n", st_facref_count); + } + } + + if (misshlist->objects > 0) { + long bak_steiner = st_facref_count; + + if (b->verbose) { + printf(" Recovering Delaunay.\n"); + } + + recoverdelaunay(); + + if (b->verbose) { + printf(" Recovering facets with Steiner points.\n"); + } + + while (misshlist->objects > 0) { + ms = misshlist->objects; + for (i = 0; i < misshlist->objects; i++) { + subfacstack->newindex((void **) &parysh); + *parysh = * (face *) fastlookup(misshlist, i); + } + misshlist->restart(); + + recoversubfaces(misshlist, 2); // steinerflag = 2; + + if (misshlist->objects < ms) { + continue; + } else { + break; + } } - misshlist->restart(); - recoversubfaces(NULL, 1); + if (subsegstack->objects > 0) { + // Save unrecovered subsegments. + triface neightet; + face checkseg; + for (i = 0; i < subsegstack->objects; i++) { + checkseg = * (face *) fastlookup(subsegstack, i); + if ((checkseg.sh == NULL) || + (checkseg.sh[3] == NULL)) continue; + // Check if this subsegment is missing. + sstpivot1(checkseg, neightet); + if (neightet.tet != NULL) continue; + // Save a missing subsegment. + misseglist->newindex((void **) &paryseg); + *paryseg = checkseg; + } + subsegstack->restart(); + } // if (subsegstack->objects > 0) if (b->verbose) { - printf(" Added %ld Steiner points in facets.\n", st_facref_count); + printf(" %ld (%ld) subfaces are recovered (missing).\n", + subfaces->items - misshlist->objects, misshlist->objects); + printf(" Added %ld Steiner points in facets.\n", + st_facref_count - bak_steiner); } } + // There may be un-recovered subsegments. + if (misshlist->objects > 0l) { + if (b->verbose) { + printf(" !! %ld subfaces are missing.\n", misshlist->objects); + } + terminatetetgen(this, 2); + // Save the list of missing subface. + //missing_tri_list = new arraypool(sizeof(face), 8); + //for (i = 0; i < misshlist->objects; i++) { + // missing_tri_list->newindex((void **) &parysh); + // *parysh = * (face *) fastlookup(misshlist, i); + //} + //misshlist->restart(); + } + + if (duplicated_facets_count > 0l) { + if (b->verbose) { + printf(" Deleting %ld duplicated facets.\n", duplicated_facets_count); + } + triface neightet, spintet; + face faceloop, sfaces[256]; // *tmp_sfaces = NULL; + face sseg; + int snum, snum_limit = 256; + int t1ver; + subfaces->traversalinit(); + faceloop.sh = shellfacetraverse(subfaces); + while (faceloop.sh != NULL) { + if (sinfected(faceloop)) { + // Delete an ignored duplicated subface. + shellfacedealloc(subfaces, faceloop.sh); + } + if (!smarktest3ed(faceloop)) { + faceloop.shver = 0; + stpivot(faceloop, neightet); + if (neightet.tet == NULL) { + terminatetetgen(this, 2); + } + // Update the subface connections at its three edges. + for (int k= 0; k < 3; k++) { + sspivot(faceloop, sseg); + if (sseg.sh != NULL) { + ssbond(faceloop, sseg); // Update segment connection. + } + // Get all subfaces at this edge. + snum = 0; + spintet = neightet; + do { + if (issubface(spintet)) { + tspivot(spintet, sfaces[snum++]); + if (snum > snum_limit) { + // Unlikely to happen. + terminatetetgen(this, 2); + //tmp_sfaces = new face[snum_limit * 2]; + } + } + fnextself(spintet); + } while (spintet.tet != neightet.tet); + // Re-create the face ring. + for (int j = 0; j < snum - 1; j++) { + sbond1(sfaces[j], sfaces[j+1]); + } + sbond1(sfaces[snum - 1], sfaces[0]); + enextself(neightet); + senextself(faceloop); + } // k + } + faceloop.sh = shellfacetraverse(subfaces); + } + } // if (duplicated_facets_count > 0l) + if (st_facref_count > 0) { // Try to remove the Steiner points added in facets. + if (b->verbose) { + printf(" Suppressing %ld Steiner points in facets.\n", st_facref_count); + } + int bak_fliplinklevel = b->fliplinklevel; + b->fliplinklevel = 30; // limit this value + bak_facref_count = st_facref_count; for (i = 0; i < subvertstack->objects; i++) { // Get the Steiner point. @@ -21862,10 +23570,40 @@ void tetgenmesh::recoverboundary(clock_t& tv) bak_facref_count - st_facref_count); } } + + b->fliplinklevel = bak_fliplinklevel; subvertstack->restart(); } + // There may be missing segments and subfaces. + if (misseglist->objects > 0) { + triface adjtet; + face checkseg; + for (i = 0; i < misseglist->objects; i++) { + checkseg = * (face *) fastlookup(misseglist, i); + // A saved missing segment might be split or recovered. + if ((checkseg.sh == NULL) || (checkseg.sh[3] == NULL)) { + continue; // it is split. + } + sstpivot1(checkseg, adjtet); + if (adjtet.tet != NULL) { + continue; // it is recovered. + } + // This is a missing segmemt. + subsegstack->newindex((void **) &paryseg); + *paryseg = checkseg; + } + if (subsegstack->objects > 0) { + if (!b->quiet && !b->nowarning) { + printf("Warning: %ld segments are not recovered.\n", subsegstack->objects); + } + //assert(0); // to do... + subsegstack->restart(); + } + } + + if (bdrysteinerptlist->objects > 0) { if (b->verbose) { printf(" %ld Steiner points remained in boundary.\n", @@ -21874,6 +23612,8 @@ void tetgenmesh::recoverboundary(clock_t& tv) } // if + boundary_recovery_flag = 0; + // Accumulate the dynamic memory. totalworkmemory += (misseglist->totalmemory + misshlist->totalmemory + bdrysteinerptlist->totalmemory); @@ -21883,20 +23623,20 @@ void tetgenmesh::recoverboundary(clock_t& tv) delete misshlist; } -//// //// -//// //// -//// steiner_cxx ////////////////////////////////////////////////////////////// +// // +// // +//== steiner_cxx =============================================================// -//// reconstruct_cxx ////////////////////////////////////////////////////////// -//// //// -//// //// +//== reconstruct_cxx =========================================================// +// // +// // -/////////////////////////////////////////////////////////////////////////////// -// // -// carveholes() Remove tetrahedra not in the mesh domain. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// carveholes() Remove tetrahedra not in the mesh domain. // +// // +//============================================================================// void tetgenmesh::carveholes() @@ -22002,6 +23742,98 @@ void tetgenmesh::carveholes() } // i } // if (in->numberofholes > 0) + if (b->hole_mesh && (b->hole_mesh_filename[0] != 0)) { + // A hole mesh (***.ele) is given. + //enum tetgenbehavior::objecttype object; + char filebasename[256]; + strcpy(filebasename, b->hole_mesh_filename); + //object = tetgenbehavior::MESH; + if (!strcmp(&filebasename[strlen(filebasename) - 4], ".ele")) { + filebasename[strlen(filebasename) - 4] = '\0'; + //object = tetgenbehavior::MESH; + } + bool hole_mesh_loaded = false; + tetgenio io; + if (io.load_node(filebasename)) { + if (io.load_tet(filebasename)) { + hole_mesh_loaded = true; + } + } + if (hole_mesh_loaded) { + if (b->verbose) { + printf(" Adding hole tets from the mesh %s\n", b->hole_mesh_filename); + } + int count = 0, hcount = 0, scount = 0; + int shift = io.firstnumber > 0 ? -1 : 0; + double *p1, *p2, *p3, *p4; + double searchpt[3]; + // Randomly select a tet. + i = randomnation(io.numberoftetrahedra); + //for (i = 0; i < io.numberoftetrahedra; i++) { + int *idx = &(io.tetrahedronlist[i * 4]); + p1 = &(io.pointlist[(idx[0]+shift)*3]); + p2 = &(io.pointlist[(idx[1]+shift)*3]); + p3 = &(io.pointlist[(idx[2]+shift)*3]); + p4 = &(io.pointlist[(idx[3]+shift)*3]); + for (j = 0; j < 3; j++) { + searchpt[j] = (p1[j]+p2[j]+p3[j]+p4[j])/4.; + } + // Search the point. + neightet.tet = NULL; + if (locate(searchpt, &neightet) != OUTSIDE) { + // The tet 'neightet' contain this point. + if (!infected(neightet)) { + infect(neightet); + tetarray->newindex((void **) &parytet); + *parytet = neightet; + count++; + // Add its adjacent tet if it is not protected. + if (!issubface(neightet)) { + decode(neightet.tet[neightet.ver & 3], tetloop); + if (!infected(tetloop)) { + infect(tetloop); + if (ishulltet(tetloop)) { + hullarray->newindex((void **) &parytet); + hcount++; + } else { + tetarray->newindex((void **) &parytet); + count++; + } + *parytet = tetloop; + } + } + else { + // It is protected. Check if its adjacent tet is a hull tet. + decode(neightet.tet[neightet.ver & 3], tetloop); + if (ishulltet(tetloop)) { + // It is hull tet, add it into the list. Moreover, the subface + // is dead, i.e., both sides are in exterior. + if (!infected(tetloop)) { + infect(tetloop); + hullarray->newindex((void **) &parytet); + *parytet = tetloop; + hcount++; + } + } + if (infected(tetloop)) { + // Both sides of this subface are in exterior. + tspivot(neightet, checksh); + sinfect(checksh); // Only queue it once. + subfacstack->newindex((void **) &parysh); + *parysh = checksh; + scount++; + } + } + } + } + //} // i + if (b->verbose) { + printf(" Added %d hole tets, %d hull tet, %d hole subfaces\n", + count, hcount, scount); + } + } // if (hole_mesh_loaded) + } + if (b->regionattrib && (in->numberofregions > 0)) { // -A option. // Record the tetrahedra that contains the region points for assigning // region attributes after the holes have been carved. @@ -22048,7 +23880,6 @@ void tetgenmesh::carveholes() // Both sides of this subface are exterior. tspivot(neightet, checksh); // Queue this subface (to be deleted later). - assert(!sinfected(checksh)); sinfect(checksh); // Only queue it once. subfacstack->newindex((void **) &parysh); *parysh = checksh; @@ -22072,7 +23903,7 @@ void tetgenmesh::carveholes() if (b->regionattrib && (in->numberofregions > 0)) { // Re-check saved region tets to see if they lie outside. for (i = 0; i < in->numberofregions; i++) { - if (infected(regiontets[i])) { + if ((regiontets[i].tet != NULL) && infected(regiontets[i])) { if (b->verbose) { printf("Warning: The %d-th region point ", i+1); printf("lies in the exterior of the domain.\n"); @@ -22096,7 +23927,7 @@ void tetgenmesh::carveholes() cavetetvertlist->newindex((void **) &parypt); *parypt = ptloop; } - if (b->nobisect && (b->nobisect_param > 0)) { // -Y1 + if ((!b->cdt || b->nobisect) && (b->supsteiner_level > 0)) { // -Y/1 // Queue it if it is a Steiner point. if (pointmark(ptloop) > (in->numberofpoints - (in->firstnumber ? 0 : 1))) { @@ -22112,7 +23943,22 @@ void tetgenmesh::carveholes() // Remove exterior tets. Hull tets are updated. arraypool *newhullfacearray; triface hulltet, casface; + face segloop, *paryseg; point pa, pb, pc; + long delsegcount = 0l; + + // Collect segments which point to infected tets. Some segments + // may get deleted after the removal of exterior tets. + subsegs->traversalinit(); + segloop.sh = shellfacetraverse(subsegs); + while (segloop.sh != NULL) { + sstpivot1(segloop, neightet); + if (infected(neightet)) { + subsegstack->newindex((void **) &paryseg); + *paryseg = segloop; + } + segloop.sh = shellfacetraverse(subsegs); + } newhullfacearray = new arraypool(sizeof(triface), 10); @@ -22191,15 +24037,14 @@ void tetgenmesh::carveholes() // Segments which are not attached to any subfaces and tets // are deleted too. face casingout, casingin; - long delsegcount = 0l; for (i = 0; i < subfacstack->objects; i++) { parysh = (face *) fastlookup(subfacstack, i); if (i == 0) { if (b->verbose) { - printf("Warning: Removing an open face (%d, %d, %d)\n", + printf("Warning: Removed an exterior face (%d, %d, %d) #%d\n", pointmark(sorg(*parysh)), pointmark(sdest(*parysh)), - pointmark(sapex(*parysh))); + pointmark(sapex(*parysh)), shellmark(*parysh)); } } // Dissolve this subface from face links. @@ -22226,11 +24071,12 @@ void tetgenmesh::carveholes() } } else { if (checkseg.sh != NULL) { - // The segment is also dead. + //if (checkseg.sh[3] != NULL) { if (delsegcount == 0) { if (b->verbose) { - printf("Warning: Removing a dangling segment (%d, %d)\n", - pointmark(sorg(checkseg)), pointmark(sdest(checkseg))); + printf("Warning: Removed an exterior segment (%d, %d) #%d\n", + pointmark(sorg(checkseg)), pointmark(sdest(checkseg)), + shellmark(checkseg)); } } shellfacedealloc(subsegs, checkseg.sh); @@ -22244,17 +24090,39 @@ void tetgenmesh::carveholes() } // i if (b->verbose) { printf(" Deleted %ld subfaces.\n", subfacstack->objects); - if (delsegcount > 0) { - printf(" Deleted %ld segments.\n", delsegcount); - } } subfacstack->restart(); } // if (subfacstack->objects > 0l) - if (cavetetvertlist->objects > 0l) { - // Some vertices may lie in exterior. Marke them as UNUSEDVERTEX. - long delvertcount = unuverts; - long delsteinercount = 0l; + if (subsegstack->objects > 0l) { + for (i = 0; i < subsegstack->objects; i++) { + paryseg = (face *) fastlookup(subsegstack, i); + if (paryseg->sh && (paryseg->sh[3] != NULL)) { + sstpivot1(*paryseg, neightet); + if (infected(neightet)) { + if (b->verbose) { + printf("Warning: Removed an exterior segment (%d, %d) #%d\n", + pointmark(sorg(*paryseg)), pointmark(sdest(*paryseg)), + shellmark(*paryseg)); + } + shellfacedealloc(subsegs, paryseg->sh); + delsegcount++; + } + } + } + subsegstack->restart(); + } // if (subsegstack->objects > 0l) + + if (delsegcount > 0) { + if (b->verbose) { + printf(" Deleted %ld segments.\n", delsegcount); + } + } + + if (cavetetvertlist->objects > 0l) { + // Some vertices may lie in exterior. Marke them as UNUSEDVERTEX. + long delvertcount = unuverts; + long delsteinercount = 0l; for (i = 0; i < cavetetvertlist->objects; i++) { parypt = (point *) fastlookup(cavetetvertlist, i); @@ -22269,7 +24137,6 @@ void tetgenmesh::carveholes() } else if (pointtype(*parypt) == FREEFACETVERTEX) { st_facref_count--; } else { - assert(pointtype(*parypt) == FREEVOLVERTEX); st_volref_count--; } delsteinercount++; @@ -22321,7 +24188,6 @@ void tetgenmesh::carveholes() if (b->convex && (tetarray->objects > 0l)) { // With -c option // In this case, all exterior tets get a region marker '-1'. - assert(b->regionattrib > 0); // -A option must be enabled. int attrnum = numelemattrib - 1; for (i = 0; i < tetarray->objects; i++) { @@ -22360,6 +24226,9 @@ void tetgenmesh::carveholes() // the element attributes. int regioncount = 0; + arraypool *tmpary = new arraypool(sizeof(int), 8); + int *paryint; + // If has user-defined region attributes. if (in->numberofregions > 0) { // Spread region attributes. @@ -22395,6 +24264,8 @@ void tetgenmesh::carveholes() } } // k } // j + tmpary->newindex((void **) &paryint); + *paryint = attr; regioncount++; } // if (regiontets[i/5].tet != NULL) } // i @@ -22429,12 +24300,13 @@ void tetgenmesh::carveholes() } } // k } // j + tmpary->newindex((void **) &paryint); + *paryint = attr; attr++; // Increase the attribute. regioncount++; } tetloop.tet = tetrahedrontraverse(); } - // Until here, every tet has a region attribute. // Uninfect processed tets. tetrahedrons->traversalinit(); @@ -22444,6 +24316,15 @@ void tetgenmesh::carveholes() tetloop.tet = tetrahedrontraverse(); } + // Until here, every tet has a region attribute. + subdomains = regioncount; // Remember it for output. + subdomain_markers = new int[subdomains]; + for (i = 0; i < subdomains; i++) { + paryint = (int *) fastlookup(tmpary, i); + subdomain_markers[i] = *paryint; + } + delete tmpary; + if (b->verbose) { //assert(regioncount > 0); if (regioncount > 1) { @@ -22489,11 +24370,95 @@ void tetgenmesh::carveholes() } // if (!b->convex) } -/////////////////////////////////////////////////////////////////////////////// -// // -// reconstructmesh() Reconstruct a tetrahedral mesh. // -// // -/////////////////////////////////////////////////////////////////////////////// +// [2018-07-30] +// Search a face with given indices (i,j,k). +// This function is only called when the default fast search fails. +// It is possible when there are non-manifold edges on the hull. +// On finish, tetloop return this face if it exists, otherwise, return 0. +int tetgenmesh::search_face(point pi, point pj, point pk, triface &tetloop) +{ + pinfect(pi); + pinfect(pj); + pinfect(pk); + + int t1ver; + triface t, t1; + point *pts, toppo; + int pcount = 0; + + t.ver = t1.ver = 0; + tetrahedrons->traversalinit(); + t.tet = tetrahedrontraverse(); + while (t.tet != NULL) { + pts = (point *) t.tet; + pcount = 0; + if (pinfected(pts[4])) pcount++; + if (pinfected(pts[5])) pcount++; + if (pinfected(pts[6])) pcount++; + if (pinfected(pts[7])) pcount++; + + if (pcount == 3) { + // Found a tet containing this face. + for (t.ver = 0; t.ver < 4; t.ver++) { + toppo = oppo(t); + if (!pinfected(toppo)) break; + } + int ii; + for (ii = 0; ii < 3; ii++) { + if (org(t) == pi) break; + enextself(t); + } + if (dest(t) == pj) { + } else { + eprevself(t); + fsymself(t); + } + break; + } + t.tet = tetrahedrontraverse(); + } + + puninfect(pi); + puninfect(pj); + puninfect(pk); + + if (t.tet != NULL) { + tetloop = t; + return 1; + } else { + return 0; + } +} + +int tetgenmesh::search_edge(point p0, point p1, triface &tetloop) +{ + triface t; + int ii; + + tetrahedrons->traversalinit(); + t.tet = tetrahedrontraverse(); + while (t.tet != NULL) { + for (ii = 0; ii < 6; ii++) { + t.ver = edge2ver[ii]; + if (((org(t) == p0) && (dest(t) == p1)) || + ((org(t) == p1) && (dest(t) == p0))) { + // Found the tet. + tetloop = t; + return 1; + } + } + t.tet = tetrahedrontraverse(); + } + + tetloop.tet = NULL; + return 0; +} + +//============================================================================// +// // +// reconstructmesh() Reconstruct a tetrahedral mesh. // +// // +//============================================================================// void tetgenmesh::reconstructmesh() { @@ -22507,7 +24472,8 @@ void tetgenmesh::reconstructmesh() shellface sptr; point p[4], q[3]; REAL ori, attrib, volume; - REAL angtol, ang; + REAL cosang_tol, cosang; + REAL n1[3], n2[3]; int eextras, marker = 0; int bondflag; int t1ver; @@ -22519,7 +24485,9 @@ void tetgenmesh::reconstructmesh() if (b->convex) { // -c option. // Assume the mesh is convex. Exterior tets have region attribute -1. - assert(in->numberoftetrahedronattributes > 0); + if (!(in->numberoftetrahedronattributes > 0)) { + terminatetetgen(this, 2); + } } else { // Assume the mesh is non-convex. nonconvex = 1; @@ -22534,9 +24502,9 @@ void tetgenmesh::reconstructmesh() // Allocate an array that maps each vertex to its adjacent tets. ver2tetarray = new tetrahedron[in->numberofpoints + 1]; + unuverts = in->numberofpoints; // All vertices are unused yet. //for (i = 0; i < in->numberofpoints + 1; i++) { for (i = in->firstnumber; i < in->numberofpoints + in->firstnumber; i++) { - setpointtype(idx2verlist[i], VOLVERTEX); // initial type. ver2tetarray[i] = NULL; } @@ -22546,6 +24514,10 @@ void tetgenmesh::reconstructmesh() idx = i * in->numberofcorners; for (j = 0; j < 4; j++) { p[j] = idx2verlist[in->tetrahedronlist[idx++]]; + if (pointtype(p[j]) == UNUSEDVERTEX) { + setpointtype(p[j], VOLVERTEX); // initial type. + unuverts--; + } } // Check the orientation. ori = orient3d(p[0], p[1], p[2], p[3]); @@ -22681,13 +24653,11 @@ void tetgenmesh::reconstructmesh() } if (face2.tet != NULL) { // Found an adjacent hull tet. - assert(face2.tet[face2.ver & 3] == NULL); esym(hulltet, face1); bond(face1, face2); } enextself(hulltet); } - //hullsize++; } // Create the point-to-tet map. setpoint2tet((point) (tetloop.tet[4 + tetloop.ver]), tptr); @@ -22708,15 +24678,16 @@ void tetgenmesh::reconstructmesh() marker = in->trifacemarkerlist[i]; } else { // Face markers are not available. Assume all of them are subfaces. - marker = 1; + marker = -1; // The default marker. } - if (marker > 0) { + if (marker != 0) { idx = i * 3; for (j = 0; j < 3; j++) { p[j] = idx2verlist[in->trifacelist[idx++]]; } // Search the subface. bondflag = 0; + neighsh.sh = NULL; // Make sure all vertices are in the mesh. Avoid crash. for (j = 0; j < 3; j++) { decode(point2tet(p[j]), checktet); @@ -22743,6 +24714,19 @@ void tetgenmesh::reconstructmesh() if (apex(tetloop) == q[2]) break; } } + if (!bondflag) { + if (neighsh.sh == NULL) { + if (b->verbose > 1) { + printf("Warning: Searching subface #%d [%d,%d,%d] mark=%d.\n", + i + in->firstnumber, pointmark(p[0]), pointmark(p[1]), + pointmark(p[2]), marker); + } + // Search it globally. + if (search_face(p[0], p[1], p[2], tetloop)) { + bondflag = 1; + } + } + } if (bondflag) { // Create a new subface. makeshellface(subfaces, &subloop); @@ -22753,28 +24737,25 @@ void tetgenmesh::reconstructmesh() setpointtype(p[j], FACETVERTEX); // initial type. setpoint2sh(p[j], sptr); } - if (in->trifacemarkerlist != NULL) { - setshellmark(subloop, in->trifacemarkerlist[i]); - } + setshellmark(subloop, marker); // Insert the subface into the mesh. tsbond(tetloop, subloop); fsymself(tetloop); sesymself(subloop); tsbond(tetloop, subloop); } else { - if (!b->quiet) { - if (neighsh.sh == NULL) { - printf("Warning: Subface #%d [%d,%d,%d] is missing.\n", - i + in->firstnumber, pointmark(p[0]), pointmark(p[1]), - pointmark(p[2])); - } else { - printf("Warning: Ignore a dunplicated subface #%d [%d,%d,%d].\n", + if (neighsh.sh != NULL) { + // The subface already exists. Only set its mark. + setshellmark(neighsh, marker); + } else { + if (!b->quiet) { + printf("Warning: Subface #%d [%d,%d,%d] mark=%d is not found.\n", i + in->firstnumber, pointmark(p[0]), pointmark(p[1]), - pointmark(p[2])); + pointmark(p[2]), marker); } } } // if (bondflag) - } // if (marker > 0) + } // if (marker != 0) } // i } // if (in->trifacelist) @@ -22816,7 +24797,7 @@ void tetgenmesh::reconstructmesh() setpointtype(p[j], FACETVERTEX); // initial type. setpoint2sh(p[j], sptr); } - setshellmark(subloop, 0); // Default marker. + setshellmark(subloop, -1); // Default marker. // Insert the subface into the mesh. tsbond(tetloop, subloop); sesymself(subloop); @@ -22844,12 +24825,14 @@ void tetgenmesh::reconstructmesh() fnextself(tetloop); tspivot(tetloop, nextsh); if (nextsh.sh != NULL) { - // Link neighsh <= nextsh. - sbond1(neighsh, nextsh); - neighsh = nextsh; + // Do not connect itself. + if (nextsh.sh != neighsh.sh) { + // Link neighsh <= nextsh. + sbond1(neighsh, nextsh); + neighsh = nextsh; + } } if (apex(tetloop) == q[2]) { - assert(nextsh.sh == subloop.sh); // It's a ring. break; } } // while (1) @@ -22869,7 +24852,7 @@ void tetgenmesh::reconstructmesh() marker = in->edgemarkerlist[i]; } else { // Edge markers are not available. Assume all of them are segments. - marker = 1; + marker = -1; // Default marker. } if (marker != 0) { // Insert a segment. @@ -22883,8 +24866,23 @@ void tetgenmesh::reconstructmesh() if (checktet.tet == NULL) break; } // Search the segment. - if ((j == 2) && getedge(p[0], p[1], &checktet)) { - // Create a new subface. + bondflag = 0; + if (j == 2) { + if (getedge(p[0], p[1], &checktet)) { + bondflag = 1; + } else { + if (b->verbose > 1) { + printf("Warning: Searching segment #%d [%d,%d] mark=%d.\n", + i + in->firstnumber, pointmark(p[0]), pointmark(p[1]), marker); + } + // Search it globally. + if (search_edge(p[0], p[1], checktet)) { + bondflag = 1; + } + } + } + if (bondflag > 0) { + // Create a new segment. makeshellface(subsegs, &segloop); setshvertices(segloop, p[0], p[1], NULL); // Create the point-to-segment map. @@ -22893,9 +24891,7 @@ void tetgenmesh::reconstructmesh() setpointtype(p[j], RIDGEVERTEX); // initial type. setpoint2sh(p[j], sptr); } - if (in->edgemarkerlist != NULL) { - setshellmark(segloop, marker); - } + setshellmark(segloop, marker); // Insert the segment into the mesh. tetloop = checktet; q[2] = apex(checktet); @@ -22925,8 +24921,8 @@ void tetgenmesh::reconstructmesh() // Identify segments from the mesh. // Create segments for non-manifold edges (which are shared by more // than two subfaces), and for non-coplanar edges, i.e., two subfaces - // form an dihedral angle > 'b->facet_ang_tol' (degree). - angtol = b->facet_ang_tol / 180.0 * PI; + // form an dihedral angle > 'b->facet_separate_ang_tol' (degree). + cosang_tol = cos(b->facet_separate_ang_tol / 180.0 * PI); subfaces->traversalinit(); subloop.shver = 0; subloop.sh = shellfacetraverse(subfaces); @@ -22938,11 +24934,17 @@ void tetgenmesh::reconstructmesh() bondflag = 0; // Counter the number of subfaces at this edge. idx = 0; - nextsh = subloop; - while (1) { - idx++; - spivotself(nextsh); - if (nextsh.sh == subloop.sh) break; + spivot(subloop, nextsh); + if (nextsh.sh != NULL) { + nextsh = subloop; + while (1) { + idx++; + spivotself(nextsh); + if (nextsh.sh == subloop.sh) break; + } + } else { + // There is only one subface at this edge. + idx = 1; } if (idx != 2) { // It's a non-manifold edge. Insert a segment. @@ -22950,6 +24952,7 @@ void tetgenmesh::reconstructmesh() p[1] = sdest(subloop); bondflag = 1; } else { + // There are two subfaces at this edge. spivot(subloop, neighsh); if (shellmark(subloop) != shellmark(neighsh)) { // It's an interior interface. Insert a segment. @@ -22963,9 +24966,13 @@ void tetgenmesh::reconstructmesh() p[1] = sdest(subloop); p[2] = sapex(subloop); p[3] = sapex(neighsh); - ang = facedihedral(p[0], p[1], p[2], p[3]); - if (ang > PI) ang = 2 * PI - ang; - if (ang < angtol) { + facenormal(p[0], p[1], p[2], n1, 1, NULL); + facenormal(p[0], p[1], p[3], n2, 1, NULL); + cosang = dot(n1, n2) / (sqrt(dot(n1, n1)) * sqrt(dot(n2, n2))); + // Rounding. + if (cosang > 1.0) cosang = 1.0; + else if (cosang < -1.0) cosang = -1.0; + if (cosang > cosang_tol) { bondflag = 1; } } @@ -22981,7 +24988,7 @@ void tetgenmesh::reconstructmesh() setpointtype(p[j], RIDGEVERTEX); // initial type. setpoint2sh(p[j], sptr); } - setshellmark(segloop, 0); // Initially has no marker. + setshellmark(segloop, -1); // Default marker. // Insert the subface into the mesh. stpivot(subloop, tetloop); q[2] = apex(tetloop); @@ -23016,7 +25023,7 @@ void tetgenmesh::reconstructmesh() int* idx2seglist; face parentseg, nextseg; verttype vt; - REAL area, len, l1, l2; + REAL area, len; // l1, l2; int fmarker; makepoint2submap(subsegs, idx2seglist, segperverlist); @@ -23041,10 +25048,7 @@ void tetgenmesh::reconstructmesh() p[0] = sorg(nextseg); p[1] = sdest(parentseg); // Check if three points p[0], ptloop, p[2] are (nearly) collinear. - len = distance(p[0], p[1]); - l1 = distance(p[0], ptloop); - l2 = distance(ptloop, p[1]); - if (((l1 + l2 - len) / len) < b->epsilon) { + if (is_collinear_at(ptloop, p[0], p[1])) { // They are (nearly) collinear. setpointtype(ptloop, FREESEGVERTEX); // Connect nextseg and parentseg together at ptloop. @@ -23108,193 +25112,121 @@ void tetgenmesh::reconstructmesh() delete [] ver2tetarray; } -/////////////////////////////////////////////////////////////////////////////// -// // -// scoutpoint() Search a point in mesh. // -// // -// This function searches the point in a mesh whose domain may be not convex.// -// In case of a convex domain, the locate() function is sufficient. // -// // -// If 'randflag' is used, randomly select a start searching tet. Otherwise, // -// start searching directly from 'searchtet'. // -// // -/////////////////////////////////////////////////////////////////////////////// - -int tetgenmesh::scoutpoint(point searchpt, triface *searchtet, int randflag) +//============================================================================// +// // +// scoutpoint() Search a point in mesh. // +// // +// This function searches the point in a mesh whose domain may be not convex. // +// In case of a convex domain, the locate() function is sufficient. // +// // +// If 'randflag' is used, randomly select a start searching tet. Otherwise, // +// start searching directly from 'searchtet'. // +// // +//============================================================================// + +int tetgenmesh::scout_point(point searchpt, triface *searchtet, int randflag) { - point pa, pb, pc, pd; + if (b->verbose > 3) { + printf(" Scout point %d.\n", pointmark(searchpt)); + } + // randflag is not used. enum locateresult loc = OUTSIDE; - REAL vol, ori1, ori2 = 0, ori3 = 0, ori4 = 0; - int t1ver; + int maxiter = 100, iter = 0; - - // Randomly select a good starting tet. - if (randflag) { - randomsample(searchpt, searchtet); - } else { + do { + // 'searchtet' must be a valid tetrahedron. if (searchtet->tet == NULL) { - *searchtet = recenttet; + // Randomly select a good starting tet. + randomsample(searchpt, searchtet); } - } - loc = locate(searchpt, searchtet); - - if (loc == OUTSIDE) { - if (b->convex) { // -c option - // The point lies outside of the convex hull. - return (int) loc; + + if (ishulltet(*searchtet)) { + if ((recenttet.tet != NULL) && !ishulltet(recenttet)) { + *searchtet = recenttet; + } } - // Test if it lies nearly on the hull face. - // Reuse vol, ori1. - pa = org(*searchtet); - pb = dest(*searchtet); - pc = apex(*searchtet); - vol = triarea(pa, pb, pc); - ori1 = orient3dfast(pa, pb, pc, searchpt); - if (fabs(ori1 / vol) < b->epsilon) { - loc = ONFACE; // On face (or on edge, or on vertex). + + if (ishulltet(*searchtet)) { + int t1ver; + searchtet->ver = 11; fsymself(*searchtet); } - } + + loc = locate_point_walk(searchpt, searchtet, 0); // encflg = 0. - if (loc != OUTSIDE) { - // Round the result of location. - pa = org(*searchtet); - pb = dest(*searchtet); - pc = apex(*searchtet); - pd = oppo(*searchtet); - vol = orient3dfast(pa, pb, pc, pd); - ori1 = orient3dfast(pa, pb, pc, searchpt); - ori2 = orient3dfast(pb, pa, pd, searchpt); - ori3 = orient3dfast(pc, pb, pd, searchpt); - ori4 = orient3dfast(pa, pc, pd, searchpt); - if (fabs(ori1 / vol) < b->epsilon) ori1 = 0; - if (fabs(ori2 / vol) < b->epsilon) ori2 = 0; - if (fabs(ori3 / vol) < b->epsilon) ori3 = 0; - if (fabs(ori4 / vol) < b->epsilon) ori4 = 0; - } else { // if (loc == OUTSIDE) { - // Do a brute force search for the point (with rounding). - tetrahedrons->traversalinit(); - searchtet->tet = tetrahedrontraverse(); - while (searchtet->tet != NULL) { - pa = org(*searchtet); - pb = dest(*searchtet); - pc = apex(*searchtet); - pd = oppo(*searchtet); - - vol = orient3dfast(pa, pb, pc, pd); - if (vol < 0) { - ori1 = orient3dfast(pa, pb, pc, searchpt); - if (fabs(ori1 / vol) < b->epsilon) ori1 = 0; // Rounding. - if (ori1 <= 0) { - ori2 = orient3dfast(pb, pa, pd, searchpt); - if (fabs(ori2 / vol) < b->epsilon) ori2 = 0; - if (ori2 <= 0) { - ori3 = orient3dfast(pc, pb, pd, searchpt); - if (fabs(ori3 / vol) < b->epsilon) ori3 = 0; - if (ori3 <= 0) { - ori4 = orient3dfast(pa, pc, pd, searchpt); - if (fabs(ori4 / vol) < b->epsilon) ori4 = 0; - if (ori4 <= 0) { - // Found the tet. Return its location. - break; - } // ori4 - } // ori3 - } // ori2 - } // ori1 - } + if (loc == OUTSIDE) { + //randomsample(searchpt, searchtet); + searchtet->tet = NULL; + } - searchtet->tet = tetrahedrontraverse(); - } // while (searchtet->tet != NULL) - nonregularcount++; // Re-use this counter. - } + iter++; + if (iter < maxiter) break; + } while (loc != OUTSIDE); - if (searchtet->tet != NULL) { - // Return the point location. - if (ori1 == 0) { // on face [a,b,c] - if (ori2 == 0) { // on edge [a,b]. - if (ori3 == 0) { // on vertex [b]. - assert(ori4 != 0); - enextself(*searchtet); // [b,c,a,d] - loc = ONVERTEX; - } else { - if (ori4 == 0) { // on vertex [a] - loc = ONVERTEX; // [a,b,c,d] - } else { - loc = ONEDGE; // [a,b,c,d] - } - } - } else { // ori2 != 0 - if (ori3 == 0) { // on edge [b,c] - if (ori4 == 0) { // on vertex [c] - eprevself(*searchtet); // [c,a,b,d] - loc = ONVERTEX; - } else { - enextself(*searchtet); // [b,c,a,d] - loc = ONEDGE; - } - } else { // ori3 != 0 - if (ori4 == 0) { // on edge [c,a] - eprevself(*searchtet); // [c,a,b,d] - loc = ONEDGE; - } else { - loc = ONFACE; - } + if (loc == INTETRAHEDRON) { + // Check if this vertex is nearly on subfacet. + triface chktet = *searchtet; + for (chktet.ver = 0; chktet.ver < 4; chktet.ver++) { + if (issubface(chktet)) { + point pa = org(chktet); + point pb = org(chktet); + point pc = org(chktet); + REAL ori = orient3d(pa, pb, pc, searchpt); + REAL averlen = (distance(pa, pb) + + distance(pb, pc) + + distance(pc, pa)) / 3.; + REAL len3 = averlen * averlen * averlen; + REAL ratio = (-ori) / len3; + if (ratio < b->epsilon) { + *searchtet = chktet; + loc = ONFACE; + break; } } - } else { // ori1 != 0 - if (ori2 == 0) { // on face [b,a,d] - esymself(*searchtet); // [b,a,d,c] - if (ori3 == 0) { // on edge [b,d] - eprevself(*searchtet); // [d,b,a,c] - if (ori4 == 0) { // on vertex [d] - loc = ONVERTEX; - } else { - loc = ONEDGE; - } - } else { // ori3 != 0 - if (ori4 == 0) { // on edge [a,d] - enextself(*searchtet); // [a,d,b,c] - loc = ONEDGE; - } else { - loc = ONFACE; - } + } + } // if (loc == INTETRAHEDRON) + + if (loc == ONFACE) { + // Check if this vertex is nearly on a subsegment. + triface chkface = *searchtet; + for (int i = 0; i < 3; i++) { + if (issubseg(chkface)) { + REAL cosang = cos_interiorangle(searchpt, org(chkface), dest(chkface)); + if (cosang < cos_collinear_ang_tol) { // -p////179.9 + *searchtet = chkface; + loc = ONEDGE; + break; } - } else { // ori2 != 0 - if (ori3 == 0) { // on face [c,b,d] - enextself(*searchtet); - esymself(*searchtet); - if (ori4 == 0) { // on edge [c,d] - eprevself(*searchtet); - loc = ONEDGE; - } else { - loc = ONFACE; - } - } else { - if (ori4 == 0) { // on face [a,c,d] - eprevself(*searchtet); - esymself(*searchtet); - loc = ONFACE; - } else { // inside tet [a,b,c,d] - loc = INTETRAHEDRON; - } // ori4 - } // ori3 - } // ori2 - } // ori1 - } else { - loc = OUTSIDE; + } + enextself(chkface); + } + } // if (loc == ONFACE) + + if (loc == ONEDGE) { + // Check if this vertex is nearly on a vertex. + triface chkedge = *searchtet; + for (int i = 0; i < 2; i++) { + REAL dd = distance(searchpt, org(chkedge)); + if (dd < minedgelength) { + *searchtet = chkedge; + loc = ONVERTEX; + break; + } + esymself(chkedge); + } } return (int) loc; } -/////////////////////////////////////////////////////////////////////////////// -// // -// getpointmeshsize() Interpolate the mesh size at given point. // -// // -// 'iloc' indicates the location of the point w.r.t. 'searchtet'. The size // -// is obtained by linear interpolation on the vertices of the tet. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// getpointmeshsize() Interpolate the mesh size at given point. // +// // +// 'iloc' indicates the location of the point w.r.t. 'searchtet'. The size // +// is obtained by linear interpolation on the vertices of the tet. // +// // +//============================================================================// REAL tetgenmesh::getpointmeshsize(point searchpt, triface *searchtet, int iloc) { @@ -23307,7 +25239,6 @@ REAL tetgenmesh::getpointmeshsize(point searchpt, triface *searchtet, int iloc) if (iloc == (int) INTETRAHEDRON) { pts = (point *) &(searchtet->tet[4]); - assert(pts[3] != dummypoint); // Only do interpolation if all vertices have non-zero sizes. if ((pts[0][pointmtrindex] > 0) && (pts[1][pointmtrindex] > 0) && (pts[2][pointmtrindex] > 0) && (pts[3][pointmtrindex] > 0)) { @@ -23356,12 +25287,12 @@ REAL tetgenmesh::getpointmeshsize(point searchpt, triface *searchtet, int iloc) return size; } -/////////////////////////////////////////////////////////////////////////////// -// // -// interpolatemeshsize() Interpolate the mesh size from a background mesh // -// (source) to the current mesh (destination). // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// interpolatemeshsize() Interpolate the mesh size from a background mesh // +// (source) to the current mesh (destination). // +// // +//============================================================================// void tetgenmesh::interpolatemeshsize() { @@ -23387,7 +25318,7 @@ void tetgenmesh::interpolatemeshsize() while (ploop != NULL) { // Search a tet in bgm which containing this point. searchtet.tet = NULL; - iloc = bgm->scoutpoint(ploop, &searchtet, 1); // randflag = 1 + iloc = bgm->scout_point(ploop, &searchtet, 1); // randflag = 1 if (iloc != (int) OUTSIDE) { // Interpolate the mesh size. ploop[pointmtrindex] = bgm->getpointmeshsize(ploop, &searchtet, iloc); @@ -23425,14 +25356,14 @@ void tetgenmesh::interpolatemeshsize() nonregularcount = bak_nonregularcount; } -/////////////////////////////////////////////////////////////////////////////// -// // -// insertconstrainedpoints() Insert a list of points into the mesh. // -// // -// Assumption: The bounding box of the insert point set should be no larger // -// than the bounding box of the mesh. (Required by point sorting). // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// insertconstrainedpoints() Insert a list of points into the mesh. // +// // +// Assumption: The bounding box of the insert point set should be no larger // +// than the bounding box of the mesh. (Required by point sorting). // +// // +//============================================================================// void tetgenmesh::insertconstrainedpoints(point *insertarray, int arylen, int rejflag) @@ -23441,7 +25372,7 @@ void tetgenmesh::insertconstrainedpoints(point *insertarray, int arylen, face splitsh; face splitseg; insertvertexflags ivf; - flipconstraints fc; + //flipconstraints fc; int randflag = 0; int t1ver; int i; @@ -23489,19 +25420,11 @@ void tetgenmesh::insertconstrainedpoints(point *insertarray, int arylen, long bak_fac_count = st_facref_count; long bak_vol_count = st_volref_count; - // Initialize the insertion parameters. - if (b->incrflip) { // -l option - // Use incremental flip algorithm. - ivf.bowywat = 0; - ivf.lawson = 1; - ivf.validflag = 0; // No need to validate the cavity. - fc.enqflag = 2; - } else { - // Use Bowyer-Watson algorithm. - ivf.bowywat = 1; - ivf.lawson = 0; - ivf.validflag = 1; // Validate the B-W cavity. - } + // Initialize the insertion parameters. + // Use Bowyer-Watson algorithm. + ivf.bowywat = 1; + ivf.lawson = 2; // do flip to recover Delaunay + ivf.validflag = 1; // Validate the B-W cavity. ivf.rejflag = rejflag; ivf.chkencflag = 0; ivf.sloc = (int) INSTAR; @@ -23512,13 +25435,13 @@ void tetgenmesh::insertconstrainedpoints(point *insertarray, int arylen, encseglist = new arraypool(sizeof(face), 8); encshlist = new arraypool(sizeof(badface), 8); + searchtet.tet = NULL; // Insert the points. for (i = 0; i < arylen; i++) { // Find the location of the inserted point. // Do not use 'recenttet', since the mesh may be non-convex. - searchtet.tet = NULL; - ivf.iloc = scoutpoint(insertarray[i], &searchtet, randflag); + ivf.iloc = scout_point(insertarray[i], &searchtet, randflag); // Decide the right type for this point. setpointtype(insertarray[i], FREEVOLVERTEX); // Default. @@ -23554,11 +25477,17 @@ void tetgenmesh::insertconstrainedpoints(point *insertarray, int arylen, // Now insert the point. if (insertpoint(insertarray[i], &searchtet, &splitsh, &splitseg, &ivf)) { if (flipstack != NULL) { - // There are queued faces. Use flips to recover Delaunayness. + flipconstraints fc; + //fc.chkencflag = chkencflag; + fc.enqflag = 2; lawsonflip3d(&fc); - // There may be unflippable edges. Ignore them. - unflipqueue->restart(); + //unflipqueue->restart(); + } + + if (later_unflip_queue->objects > b->unflip_queue_limit) { + recoverdelaunay(); } + // Update the Steiner counters. if (pointtype(insertarray[i]) == FREESEGVERTEX) { st_segref_count++; @@ -23569,16 +25498,24 @@ void tetgenmesh::insertconstrainedpoints(point *insertarray, int arylen, } } else { // Point is not inserted. - //pointdealloc(insertarray[i]); - setpointtype(insertarray[i], UNUSEDVERTEX); + if (pointtype(insertarray[i]) != UNUSEDVERTEX) { + setpointtype(insertarray[i], UNUSEDVERTEX); + } unuverts++; + encseglist->restart(); encshlist->restart(); } } // i + if (later_unflip_queue->objects > 0) { + recoverdelaunay(); + } + delete encseglist; delete encshlist; + encseglist = NULL; + encshlist = NULL; if (b->verbose) { printf(" Inserted %ld (%ld, %ld, %ld) vertices.\n", @@ -23669,11 +25606,11 @@ void tetgenmesh::insertconstrainedpoints(tetgenio *addio) delete [] insertarray; } -/////////////////////////////////////////////////////////////////////////////// -// // -// meshcoarsening() Deleting (selected) vertices. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// meshcoarsening() Deleting (selected) vertices. // +// // +//============================================================================// void tetgenmesh::collectremovepoints(arraypool *remptlist) { @@ -23688,6 +25625,13 @@ void tetgenmesh::collectremovepoints(arraypool *remptlist) points->traversalinit(); ptloop = pointtraverse(); while (ptloop != NULL) { + // Do not remove a boundary vertex + vt = pointtype(ptloop); + if ((vt == RIDGEVERTEX) || /*(vt == ACUTEVERTEX) ||*/ (vt == FACETVERTEX) || + (vt == FREEFACETVERTEX) || (vt == FREESEGVERTEX) || (vt == UNUSEDVERTEX)) { + ptloop = pointtraverse(); + continue; + } if (ptloop[pointmtrindex] > 0) { // Get the smallest edge length at this vertex. getvertexstar(1, ptloop, cavetetlist, cavetetvertlist, NULL); @@ -23742,7 +25686,6 @@ void tetgenmesh::collectremovepoints(arraypool *remptlist) if (b->coarsen_param > 0) { // -R1/# // Remove a coarsen_percent number of interior points. - assert((b->coarsen_percent > 0) && (b->coarsen_percent <= 1.0)); if (b->verbose > 1) { printf(" Coarsen %g percent of interior points.\n", b->coarsen_percent * 100.0); @@ -23838,7 +25781,6 @@ void tetgenmesh::meshcoarsening() // Remove the list of points. for (i = 0; i < remptlist->objects; i++) { parypt = (point *) fastlookup(remptlist, i); - assert(pointtype(*parypt) != UNUSEDVERTEX); if (removevertexbyflips(*parypt)) { // Move the last entry to the current place. plastpt = (point *) fastlookup(remptlist, remptlist->objects - 1); @@ -23881,72 +25823,436 @@ void tetgenmesh::meshcoarsening() delete remptlist; } -//// //// -//// //// -//// reconstruct_cxx ////////////////////////////////////////////////////////// +// // +// // +//== reconstruct_cxx =========================================================// -//// refine_cxx /////////////////////////////////////////////////////////////// -//// //// -//// //// +//== refine_cxx ==============================================================// +// // +// // -/////////////////////////////////////////////////////////////////////////////// -// // -// makefacetverticesmap() Create a map from facet to its vertices. // -// // -// All facets will be indexed (starting from 0). The map is saved in two // -// global arrays: 'idx2facetlist' and 'facetverticeslist'. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// makesegmentendpointsmap() Create a map from a segment to its endpoints. // +// // +// The map is saved in the array 'segmentendpointslist'. The length of this // +// array is twice the number of segments. Each segment is assigned a unique // +// index (starting from 0). // +// // +//============================================================================// -void tetgenmesh::makefacetverticesmap() +void tetgenmesh::makesegmentendpointsmap() { - arraypool *facetvertexlist, *vertlist, **paryvertlist; - face subloop, neighsh, *parysh, *parysh1; - point pa, *ppt, *parypt; - verttype vt; - int facetindex, totalvertices; - int i, j, k; + arraypool *segptlist; + face segloop, prevseg, nextseg; + point eorg, edest, *parypt; + int segindex = 0, idx = 0; + int i; - if (b->verbose) { - printf(" Creating the facet vertices map.\n"); + if (b->verbose > 0) { + printf(" Creating the segment-endpoints map.\n"); } + segptlist = new arraypool(2 * sizeof(point), 10); - facetvertexlist = new arraypool(sizeof(arraypool *), 10); - facetindex = totalvertices = 0; + // for creating ridge_vertex-to-segment map; + // The index might start from 0 or 1. + idx_segment_ridge_vertex_list = new int[points->items + 2]; + for (i = 0; i < points->items + 2; i++) { + idx_segment_ridge_vertex_list[i] = 0; + } - subfaces->traversalinit(); - subloop.sh = shellfacetraverse(subfaces); - while (subloop.sh != NULL) { - if (!sinfected(subloop)) { - // A new facet. Create its vertices list. - vertlist = new arraypool(sizeof(point *), 8); - ppt = (point *) &(subloop.sh[3]); - for (k = 0; k < 3; k++) { - vt = pointtype(ppt[k]); - if ((vt != FREESEGVERTEX) && (vt != FREEFACETVERTEX)) { - pinfect(ppt[k]); - vertlist->newindex((void **) &parypt); - *parypt = ppt[k]; - } + // A segment s may have been split into many subsegments. Operate the one + // which contains the origin of s. Then mark the rest of subsegments. + subsegs->traversalinit(); + segloop.sh = shellfacetraverse(subsegs); + segloop.shver = 0; + while (segloop.sh != NULL) { + senext2(segloop, prevseg); + spivotself(prevseg); + if (prevseg.sh == NULL) { + eorg = sorg(segloop); + edest = sdest(segloop); + setfacetindex(segloop, segindex); + senext(segloop, nextseg); + spivotself(nextseg); + while (nextseg.sh != NULL) { + setfacetindex(nextseg, segindex); + nextseg.shver = 0; + if (sorg(nextseg) != edest) sesymself(nextseg); + edest = sdest(nextseg); + // Go the next connected subsegment at edest. + senextself(nextseg); + spivotself(nextseg); } - sinfect(subloop); - caveshlist->newindex((void **) &parysh); - *parysh = subloop; - for (i = 0; i < caveshlist->objects; i++) { - parysh = (face *) fastlookup(caveshlist, i); - setfacetindex(*parysh, facetindex); - for (j = 0; j < 3; j++) { - if (!isshsubseg(*parysh)) { - spivot(*parysh, neighsh); - assert(neighsh.sh != NULL); - if (!sinfected(neighsh)) { - pa = sapex(neighsh); + segptlist->newindex((void **) &parypt); + parypt[0] = eorg; + parypt[1] = edest; + segindex++; + // for creating adj_ridge_vertex_list; + idx_segment_ridge_vertex_list[pointmark(eorg)]++; + idx_segment_ridge_vertex_list[pointmark(edest)]++; + } + segloop.sh = shellfacetraverse(subsegs); + } + + if (b->verbose) { + printf(" Found %ld segments.\n", segptlist->objects); + } + + segmentendpointslist_length = segptlist->objects; + segmentendpointslist = new point[segptlist->objects * 2]; + + totalworkmemory += (segptlist->objects * 2) * sizeof(point *); + + for (i = 0; i < segptlist->objects; i++) { + parypt = (point *) fastlookup(segptlist, i); + segmentendpointslist[idx++] = parypt[0]; + segmentendpointslist[idx++] = parypt[1]; + } + + // Create the adj_ridge_vertex_list. + int j = idx_segment_ridge_vertex_list[0], k; + idx_segment_ridge_vertex_list[0] = 0; + for (i = 0; i < points->items + 1; i++) { + k = idx_segment_ridge_vertex_list[i+1]; + idx_segment_ridge_vertex_list[i+1] = idx_segment_ridge_vertex_list[i] + j; + j = k; + } + + //assert(i == points->items+1); + int total_count = idx_segment_ridge_vertex_list[i] + 1; + segment_ridge_vertex_list = new point[total_count]; + for (i = 0; i < segptlist->objects; i++) { + eorg = segmentendpointslist[i*2]; + edest = segmentendpointslist[i*2+1]; + j = pointmark(eorg); + k = pointmark(edest); + segment_ridge_vertex_list[idx_segment_ridge_vertex_list[j]] = edest; //eorg; + segment_ridge_vertex_list[idx_segment_ridge_vertex_list[k]] = eorg; //edest; + idx_segment_ridge_vertex_list[j]++; + idx_segment_ridge_vertex_list[k]++; + } + + // Counters in idx_adj_ridge_vertex_list[] are shifted by 1. + for (i = points->items; i >= 0; i--) { + idx_segment_ridge_vertex_list[i+1] = idx_segment_ridge_vertex_list[i]; + } + idx_segment_ridge_vertex_list[0] = 0; + + + delete segptlist; +} + +//============================================================================// +// // +// set_ridge_vertex_protecting_ball() Calculate the protecting ball for a // +// given ridge vertex. // +// // +//============================================================================// + +REAL tetgenmesh::set_ridge_vertex_protecting_ball(point ridge_pt) +{ + REAL rv = getpointinsradius(ridge_pt); + if (rv == 0.) { + REAL mindist = 1.e+30, dist; + int idx = pointmark(ridge_pt); + for (int i = idx_segment_ridge_vertex_list[idx]; + i < idx_segment_ridge_vertex_list[idx+1]; i++) { + dist = distance(ridge_pt, segment_ridge_vertex_list[i]); + if (mindist > dist) mindist = dist; + } + rv = mindist * 0.95; // mindist / 3.0; // refer to J. Shewchuk + setpointinsradius(ridge_pt, rv); + } + return rv; +} + +//============================================================================// +// // +// get_min_diahedral_angle() Calculate the minimum (interior) dihedral // +// angle a given segment. // +// // +//============================================================================// + +REAL tetgenmesh::get_min_diahedral_angle(face* seg) +{ + triface adjtet, spintet; + face startsh, neighsh; + point pa, pb, pc1, pc2; + REAL n1[3], n2[3]; + REAL n1len, n2len; + REAL costheta; //, ori; + REAL theta, sum_theta, minang = 2.0 * PI; + int t1ver; + + pa = sorg(*seg); + pb = sdest(*seg); + spivot(*seg, startsh); + if (startsh.sh == NULL) { + // This segment is not connected by any facet. + sstpivot1(*seg, adjtet); + if (adjtet.tet != NULL) { + // This segment is completely inside the volume. + return 360.; // 2*pi. + } + } else { + if (sorg(startsh) != pa) sesymself(startsh); + stpivot(startsh, adjtet); + } + if (adjtet.tet == NULL) { + // This segment is not inserted (recovered) yet. + return 0.; + } + + + sum_theta = 0.; + spintet = adjtet; + while (true) { + if (!ishulltet(spintet)) { + // Increase the interior dihedral angle (sum_theta). + pc1 = apex(spintet); + pc2 = oppo(spintet); + facenormal(pa, pb, pc1, n1, 1, NULL); + facenormal(pa, pb, pc2, n2, 1, NULL); + n1len = sqrt(dot(n1, n1)); + n2len = sqrt(dot(n2, n2)); + costheta = dot(n1, n2) / (n1len * n2len); + // Be careful rounding error! + if (costheta > 1.0) { + costheta = 1.0; + } else if (costheta < -1.0) { + costheta = -1.0; + } + theta = acos(costheta); + sum_theta += theta; + } + // Go to the next adjacent tetrahedron at this segment. + fnextself(spintet); + // Check if we meet a subface. + tspivot(spintet, neighsh); + if ((neighsh.sh != NULL) && (sum_theta > 0.)) { + // Update the smallest dihedral angle. + if (sum_theta < minang) minang = sum_theta; + sum_theta = 0.; // clear it + } + if (spintet.tet == adjtet.tet) break; + } + + double mindihedang = minang / PI * 180.; + return mindihedang; +} + +//============================================================================// +// // +// get_min_angle_at_ridge_vertex() Calculate the minimum face angle at a // +// given ridge vertex. // +// // +//============================================================================// + +REAL tetgenmesh::get_min_angle_at_ridge_vertex(face* seg) +{ + face startsh, spinsh, neighsh; + point pa, pb, pc; + REAL theta, sum_theta, minang = 2.0 * PI; + + pa = sorg(*seg); + spivot(*seg, startsh); + if (startsh.sh == NULL) { + // This segment does not belong to any facet. + return 360.; // 2*pi. + } else { + if (sorg(startsh) != pa) sesymself(startsh); + } + + spinsh = startsh; + while (spinsh.sh != NULL) { + sum_theta = 0.; + neighsh = spinsh; + while (true) { + pb = sdest(neighsh); + pc = sapex(neighsh); + theta = interiorangle(pa, pb, pc, NULL); + sum_theta += theta; + senext2self(neighsh); + if (isshsubseg(neighsh)) break; + spivotself(neighsh); + if (sorg(neighsh) != pa) sesymself(neighsh); + } + if (sum_theta < minang) { + minang = sum_theta; + } + // Go to the next facet at this segment. + spivotself(spinsh); + if (spinsh.sh == startsh.sh) break; + if (spinsh.sh == NULL) break; // A single facet may happen. + if (sorg(spinsh) != pa) sesymself(spinsh); + } + + return minang / PI * 180.; +} + +//============================================================================// +// // +// create_segment_info_list() Calculate the minimum dihedral angle at a // +// a given segment. // +// // +// segment_info_list = new double[segmentendpointslist_length * 4]; // +// - [0] min_dihedral_angle (degree) at this segment, // +// - [1] min_protect_cylinder_radius at this segment (for bookkeeping only), // +// - [2] min_seg_seg_angle (degree) at its endpoint [0], // +// - [3] min_seg_seg_angle (degree) at its endpoint [1]. // +// // +// This function must be called after makesegmentendpointsmap(). The number // +// of unique segments (segmentendpointslist_length) is calculated. // +// // +//============================================================================// + +void tetgenmesh::create_segment_info_list() +{ + face min_dihedral_ang_seg; + point min_face_ang_vertex; + REAL min_dihedral_ang = 360.; + REAL min_face_ang = 360.; + + if (b->verbose > 0) { + printf(" Creating the segment_info_list.\n"); + } + if (segment_info_list != NULL) { + delete [] segment_info_list; + } + + if (subsegs->items == 0) { + return; // There is no segments. + } + + int count = (segmentendpointslist_length + 1) * 4; + segment_info_list = new double[count]; + for (int i = 0; i < count; i++) { + segment_info_list[i] = 0.; + } + + // Loop through the list of segments. + face segloop; + subsegs->traversalinit(); + segloop.sh = shellfacetraverse(subsegs); + while (segloop.sh != NULL) { + int segidx = getfacetindex(segloop); + // Check if this segment has been already calulcated. + double *values = &(segment_info_list[segidx * 4]); + + // The min_diahedral_angle at this segment is in (0, 2pi]. + if (values[0] == 0.) { + // Get the smallest dihedral angle at this segment. + values[0] = get_min_diahedral_angle(&segloop); + if (values[0] < min_dihedral_ang) { + min_dihedral_ang = values[0]; + min_dihedral_ang_seg = segloop; + } + } + + point *endpts = &(segmentendpointslist[segidx * 2]); + + for (int k = 0; k < 2; k++) { + segloop.shver = 0; + if (values[2+k] == 0.) { + if (sorg(segloop) != endpts[k]) { + sesymself(segloop); + } + if (sorg(segloop) == endpts[k]) { + // Get the min face angle at vertex endpts[0]. + values[2+k] = get_min_angle_at_ridge_vertex(&segloop); + if (values[2+k] < min_face_ang) { + min_face_ang = values[2+k]; + min_face_ang_vertex = endpts[k]; + } + } + } + } + + segloop.sh = shellfacetraverse(subsegs); + } + + if (b->verbose) { + printf(" min_dihedral angle = %g degree, at segment [%d,%d]\n", + min_dihedral_ang, pointmark(sorg(min_dihedral_ang_seg)), + pointmark(sdest(min_dihedral_ang_seg))); + printf(" min face angle = %g degree, at vertex %d\n", + min_face_ang, pointmark(min_face_ang_vertex)); + } + +} + +//============================================================================// +// // +// makefacetverticesmap() Create a map from facet to its vertices. // +// // +// All facets will be indexed (starting from 0). The map is saved in two // +// global arrays: 'idx2facetlist' and 'facetverticeslist'. // +// // +//============================================================================// + +void tetgenmesh::makefacetverticesmap() +{ + arraypool *facetvertexlist, *vertlist, **paryvertlist; + face subloop, neighsh, *parysh, *parysh1; + point pa, *ppt, *parypt; + verttype vt; + int facetindex, totalvertices; + unsigned long max_facet_size = 0l; + int max_facet_idx = 0; + int i, j, k; + + if (b->verbose) { + printf(" Creating the facet vertices map.\n"); + } + + facetvertexlist = new arraypool(sizeof(arraypool *), 10); + facetindex = totalvertices = 0; + + // The index might start from 0 or 1. + idx_ridge_vertex_facet_list = new int[points->items + 2]; + for (i = 0; i < points->items + 2; i++) { + idx_ridge_vertex_facet_list[i] = 0; + } + + subfaces->traversalinit(); + subloop.sh = shellfacetraverse(subfaces); + while (subloop.sh != NULL) { + if (!sinfected(subloop)) { + // A new facet. Create its vertices list. + vertlist = new arraypool(sizeof(point *), 8); + ppt = (point *) &(subloop.sh[3]); + for (k = 0; k < 3; k++) { + vt = pointtype(ppt[k]); + //if ((vt != FREESEGVERTEX) && (vt != FREEFACETVERTEX)) { + if (vt == RIDGEVERTEX) { + pinfect(ppt[k]); + vertlist->newindex((void **) &parypt); + *parypt = ppt[k]; + // for creating ridge_vertex-to-facet map. + idx_ridge_vertex_facet_list[pointmark(ppt[k])]++; + } + } + sinfect(subloop); + caveshlist->newindex((void **) &parysh); + *parysh = subloop; + for (i = 0; i < caveshlist->objects; i++) { + parysh = (face *) fastlookup(caveshlist, i); + setfacetindex(*parysh, facetindex); + for (j = 0; j < 3; j++) { + if (!isshsubseg(*parysh)) { + spivot(*parysh, neighsh); + if (!sinfected(neighsh)) { + pa = sapex(neighsh); if (!pinfected(pa)) { vt = pointtype(pa); - if ((vt != FREESEGVERTEX) && (vt != FREEFACETVERTEX)) { + //if ((vt != FREESEGVERTEX) && (vt != FREEFACETVERTEX)) { + if (vt == RIDGEVERTEX) { pinfect(pa); vertlist->newindex((void **) &parypt); *parypt = pa; + // for creating ridge_vertex-to-facet map. + idx_ridge_vertex_facet_list[pointmark(pa)]++; } } sinfect(neighsh); @@ -23958,6 +26264,10 @@ void tetgenmesh::makefacetverticesmap() } } // i totalvertices += (int) vertlist->objects; + if (max_facet_size < vertlist->objects) { + max_facet_size = vertlist->objects; + max_facet_idx = facetindex; + } // Uninfect facet vertices. for (k = 0; k < vertlist->objects; k++) { parypt = (point *) fastlookup(vertlist, k); @@ -23976,18 +26286,32 @@ void tetgenmesh::makefacetverticesmap() subfaces->traversalinit(); subloop.sh = shellfacetraverse(subfaces); while (subloop.sh != NULL) { - assert(sinfected(subloop)); suninfect(subloop); subloop.sh = shellfacetraverse(subfaces); } if (b->verbose) { - printf(" Found %ld facets.\n", facetvertexlist->objects); + printf(" Found %ld facets. Max facet idx(%d), size(%ld)\n", + facetvertexlist->objects, max_facet_idx, max_facet_size); } + number_of_facets = facetindex; idx2facetlist = new int[facetindex + 1]; facetverticeslist = new point[totalvertices]; + // create ridge_vertex-to-facet map. + j = idx_ridge_vertex_facet_list[0]; //k; + idx_ridge_vertex_facet_list[0] = 0; + for (i = 0; i < points->items + 1; i++) { + k = idx_ridge_vertex_facet_list[i+1]; + idx_ridge_vertex_facet_list[i+1] = idx_ridge_vertex_facet_list[i] + j; + j = k; + } + + int total_count = idx_ridge_vertex_facet_list[i] + 1; + ridge_vertex_facet_list = new int[total_count]; + + // Bookkeeping totalworkmemory += ((facetindex + 1) * sizeof(int) + totalvertices * sizeof(point *)); @@ -24000,9 +26324,21 @@ void tetgenmesh::makefacetverticesmap() parypt = (point *) fastlookup(vertlist, j); facetverticeslist[k] = *parypt; k++; + // create ridge_vertex-to-facet map. + int ridge_idx = pointmark(*parypt); // index of this ridge vertex + // 'i' is the current facet index. + ridge_vertex_facet_list[idx_ridge_vertex_facet_list[ridge_idx]] = i; + // for the next facet index of this ridge vertex. + idx_ridge_vertex_facet_list[ridge_idx]++; } } - assert(k == totalvertices); + + // Counters in idx_ridge_vertex_facet_list[] are shifted by 1. + for (i = points->items; i >= 0; i--) { + idx_ridge_vertex_facet_list[i+1] = idx_ridge_vertex_facet_list[i]; + } + idx_ridge_vertex_facet_list[0] = 0; + // Free the lists. for (i = 0; i < facetvertexlist->objects; i++) { @@ -24013,19 +26349,151 @@ void tetgenmesh::makefacetverticesmap() delete facetvertexlist; } -/////////////////////////////////////////////////////////////////////////////// -// // -// Check whether two segments, or a segment and a facet, or two facets are // -// adjacent to each other. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// create_segment_facet_map() Create the map from segments to adjacent // +// facets. // +// // +//============================================================================// + +void tetgenmesh::create_segment_facet_map() +{ + if (b->verbose > 0) { + printf(" Creating the segment-to-facets map.\n"); + } + if (idx_segment_facet_list != NULL) { + delete [] idx_segment_facet_list; + delete [] segment_facet_list; + } + + face startsh, spinsh; + face segloop; + int segindex, facetidx; + int totalcount = 0; + int i; + + // both segment-index and facet-index start from zero. + idx_segment_facet_list = new int[segmentendpointslist_length + 1]; + for (i = 0; i < segmentendpointslist_length + 1; i++) { + idx_segment_facet_list[i] = 0; + } + + subsegs->traversalinit(); + segloop.sh = shellfacetraverse(subsegs); + while (segloop.sh != NULL) { + segindex = getfacetindex(segloop); + if (idx_segment_facet_list[segindex] == 0) { + // Count the number of facets at this segment. + spivot(segloop, startsh); + spinsh = startsh; + while (spinsh.sh != NULL) { + idx_segment_facet_list[segindex]++; + spivotself(spinsh); + if (spinsh.sh == startsh.sh) break; + } + totalcount += idx_segment_facet_list[segindex]; + } + segloop.sh = shellfacetraverse(subsegs); + } + + // A working list. + bool *bflags = new bool[segmentendpointslist_length + 1]; + + // Have got the totalcount, fill the starting indices into the list. + int j = idx_segment_facet_list[0], k; + idx_segment_facet_list[0] = 0; + //for (i = 0; i < segmentendpointslist_length + 1; i++) { + for (i = 0; i < segmentendpointslist_length; i++) { + k = idx_segment_facet_list[i+1]; + idx_segment_facet_list[i+1] = idx_segment_facet_list[i] + j; + j = k; + bflags[i] = false; + } + + segment_facet_list = new int[totalcount + 1]; + subsegs->traversalinit(); + segloop.sh = shellfacetraverse(subsegs); + while (segloop.sh != NULL) { + segindex = getfacetindex(segloop); + if (!bflags[segindex]) { + spivot(segloop, startsh); + spinsh = startsh; + while (spinsh.sh != NULL) { + facetidx = getfacetindex(spinsh); + segment_facet_list[idx_segment_facet_list[segindex]] = facetidx; + idx_segment_facet_list[segindex]++; // for the next one + spivotself(spinsh); + if (spinsh.sh == startsh.sh) break; + } + bflags[segindex] = true; + } + segloop.sh = shellfacetraverse(subsegs); + } + + // Counters in idx_segment_facet_list[] are shifted by 1. + for (i = segmentendpointslist_length - 1; i >= 0; i--) { + idx_segment_facet_list[i+1] = idx_segment_facet_list[i]; + } + idx_segment_facet_list[0] = 0; + + + delete [] bflags; +} + +//============================================================================// +// // +// ridge_vertices_adjacent() Check if two ridge vertices are connected by // +// an input segment. // +// // +//============================================================================// + +int tetgenmesh::ridge_vertices_adjacent(point e1, point e2) +{ + int idx = pointmark(e1); + int acount = idx_segment_ridge_vertex_list[idx+1]-idx_segment_ridge_vertex_list[idx]; + for (int i = 0; i < acount; i++) { + if (segment_ridge_vertex_list[idx_segment_ridge_vertex_list[idx]+i] == e2) { + return 1; // adjacent. + } + } + return 0; // not adjacent. +} + +//============================================================================// +// // +// facet_ridge_vertex_adjacent() Check if a facet and a ridge vertex is // +// adjacent by an input segment. // +// // +//============================================================================// + +int tetgenmesh::facet_ridge_vertex_adjacent(face *chkfac, point chkpt) +{ + int ridge_idx = pointmark(chkpt); + int facet_idx = getfacetindex(*chkfac); + for (int i = idx_ridge_vertex_facet_list[ridge_idx]; + i < idx_ridge_vertex_facet_list[ridge_idx+1]; i++) { + if (ridge_vertex_facet_list[i] == facet_idx) { + return 1; // They are adjacent. + } + } + return 0; +} + +//============================================================================// +// // +// segsegadjacent() Check whether two segments, or a segment and a facet, // +// or two facets are adjacent to each other. // +// // +//============================================================================// int tetgenmesh::segsegadjacent(face *seg1, face *seg2) { int segidx1 = getfacetindex(*seg1); int segidx2 = getfacetindex(*seg2); - if (segidx1 == segidx2) return 0; + if (segidx1 == segidx2) { + return 2; // Adjacent. They are the same segment. + } point pa1 = segmentendpointslist[segidx1 * 2]; point pb1 = segmentendpointslist[segidx1 * 2 + 1]; @@ -24033,33 +26501,37 @@ int tetgenmesh::segsegadjacent(face *seg1, face *seg2) point pb2 = segmentendpointslist[segidx2 * 2 + 1]; if ((pa1 == pa2) || (pa1 == pb2) || (pb1 == pa2) || (pb1 == pb2)) { - return 1; + return 1; // Adjacent. } - return 0; + return 0; // not adjacent } +//============================================================================// +// // +// segfacetadjacent() Check whether a segment and a facet are adjacent or // +// not. // +// // +//============================================================================// + int tetgenmesh::segfacetadjacent(face *subseg, face *subsh) { - int segidx = getfacetindex(*subseg); - point pa = segmentendpointslist[segidx * 2]; - point pb = segmentendpointslist[segidx * 2 + 1]; - - pinfect(pa); - pinfect(pb); - - int fidx = getfacetindex(*subsh); - int count = 0, i; - - for (i = idx2facetlist[fidx]; i < idx2facetlist[fidx+1]; i++) { - if (pinfected(facetverticeslist[i])) count++; - } - - puninfect(pa); - puninfect(pb); - - return count == 1; + int seg_idx = getfacetindex(*subseg); + int facet_idx = getfacetindex(*subsh); + for (int i = idx_segment_facet_list[seg_idx]; + i < idx_segment_facet_list[seg_idx+1]; i++) { + if (segment_facet_list[i] == facet_idx) { + return 1; // They are adjacent. + } + } + return 0; } +//============================================================================// +// // +// facetfacetadjacent() Check whether two facets are adjacent or not. // +// // +//============================================================================// + int tetgenmesh::facetfacetadjacent(face *subsh1, face *subsh2) { int count = 0, i; @@ -24067,7 +26539,9 @@ int tetgenmesh::facetfacetadjacent(face *subsh1, face *subsh2) int fidx1 = getfacetindex(*subsh1); int fidx2 = getfacetindex(*subsh2); - if (fidx1 == fidx2) return 0; + if (fidx1 == fidx2) { + return 2; // Adjacent. They are the same facet. + } for (i = idx2facetlist[fidx1]; i < idx2facetlist[fidx1+1]; i++) { pinfect(facetverticeslist[i]); @@ -24082,247 +26556,487 @@ int tetgenmesh::facetfacetadjacent(face *subsh1, face *subsh2) puninfect(facetverticeslist[i]); } - return count > 0; + if (count > 0) { + return 1; + } else { + return 0; + } } -/////////////////////////////////////////////////////////////////////////////// -// // -// checkseg4encroach() Check if an edge is encroached upon by a point. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// is_sharp_segment() Check whether a given segment is sharp or not. // +// // +//============================================================================// -int tetgenmesh::checkseg4encroach(point pa, point pb, point checkpt) +bool tetgenmesh::is_sharp_segment(face *seg) { - // Check if the point lies inside the diametrical sphere of this seg. - REAL v1[3], v2[3]; + int segidx = getfacetindex(*seg); + double mindihedang = segment_info_list[segidx*4]; + return mindihedang < 72.; // (in theory) < 72 degree is sufficient. +} - v1[0] = pa[0] - checkpt[0]; - v1[1] = pa[1] - checkpt[1]; - v1[2] = pa[2] - checkpt[2]; - v2[0] = pb[0] - checkpt[0]; - v2[1] = pb[1] - checkpt[1]; - v2[2] = pb[2] - checkpt[2]; - - if (dot(v1, v2) < 0) { - // Inside. - if (b->metric) { // -m option. - if ((pa[pointmtrindex] > 0) && (pb[pointmtrindex] > 0)) { - // The projection of 'checkpt' lies inside the segment [a,b]. - REAL prjpt[3], u, v, t; - projpt2edge(checkpt, pa, pb, prjpt); - // Interoplate the mesh size at the location 'prjpt'. - u = distance(pa, pb); - v = distance(pa, prjpt); - t = v / u; - // 'u' is the mesh size at 'prjpt' - u = pa[pointmtrindex] + t * (pb[pointmtrindex] - pa[pointmtrindex]); - v = distance(checkpt, prjpt); - if (v < u) { - return 1; // Encroached prot-ball! - } - } else { - return 1; // NO protecting ball. Encroached. +//============================================================================// +// // +// does_seg_contain_acute_vertex() Check whether one of the endpoints of a // +// given segment is a sharp corner. // +// // +//============================================================================// + +bool tetgenmesh::does_seg_contain_acute_vertex(face* seg) +{ + int segidx = getfacetindex(*seg); + point *ppt = &(segmentendpointslist[segidx * 2]); + REAL ang = 180.; + // Get the smallest angle at its endpoints. + for (int i = 0; i < 2; i++) { + if ((ppt[i] == sorg(*seg)) || (ppt[i] == sdest(*seg))) { + if (segment_info_list[segidx * 4 + 2 + i] < ang) { + ang = segment_info_list[segidx * 4 + 2 + i]; } - } else { - return 1; // Inside! Encroached. } } - - return 0; + return ang < 60.; } -/////////////////////////////////////////////////////////////////////////////// -// // -// checkseg4split() Check if we need to split a segment. // -// // -// A segment needs to be split if it is in the following case: // -// (1) It is encroached by an existing vertex. // -// (2) It has bad quality (too long). // -// (3) Its length is larger than the mesh sizes at its endpoints. // -// // -// Return 1 if it needs to be split, otherwise, return 0. 'pencpt' returns // -// an encroaching point if there exists. 'qflag' returns '1' if the segment // -// has a length larger than the desired edge length. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// create_a_shorter_edge() Can we create an edge (which is shorter than // +// minedgelength) between the two given vertices? // +// // +//============================================================================// -int tetgenmesh::checkseg4split(face *chkseg, point& encpt, int& qflag) +bool tetgenmesh::create_a_shorter_edge(point steinerpt, point nearpt) { - REAL ccent[3], len, r; - int i; + bool createflag = false; // default, do not create a shorter edge. - point forg = sorg(*chkseg); - point fdest = sdest(*chkseg); - - // Initialize the return values. - encpt = NULL; - qflag = 0; + enum verttype nearpt_type = pointtype(nearpt); + enum verttype steiner_type = pointtype(steinerpt); - len = distance(forg, fdest); - r = 0.5 * len; - for (i = 0; i < 3; i++) { - ccent[i] = 0.5 * (forg[i] + fdest[i]); - } + if (nearpt_type == RIDGEVERTEX) { + if (steiner_type == FREESEGVERTEX) { + // Create a shorter edge if the Steiner point does not on an adjacent + // segment of this ridge vertex. + face parentseg; + sdecode(point2sh(steinerpt), parentseg); + int segidx = getfacetindex(parentseg); + point pa = segmentendpointslist[segidx * 2]; + point pb = segmentendpointslist[segidx * 2 + 1]; + if ((pa != nearpt) && (pb != nearpt)) { + createflag = true; // create a shorter edge. + } + } else if (steiner_type == FREEFACETVERTEX) { + // Create a shorter edge if the Steiner point does not on an adjacent + // facet of this ridge vertex. + face parentsh; + sdecode(point2sh(steinerpt), parentsh); + if (!facet_ridge_vertex_adjacent(&parentsh, nearpt)) { + createflag = true; // create a shorter edge. + } + } + } else if (nearpt_type == FREESEGVERTEX) { + if (steiner_type == FREESEGVERTEX) { + // Check if they are on the same segment. + face seg1, seg2; + sdecode(point2sh(steinerpt), seg1); + sdecode(point2sh(nearpt), seg2); + int sidx1 = getfacetindex(seg1); + int sidx2 = getfacetindex(seg2); + if (sidx1 != sidx2) { + createflag = true; // create a shorter edge. + } + } else if (steiner_type == FREEFACETVERTEX) { + face parentseg, paresntsh; + sdecode(point2sh(steinerpt), paresntsh); + sdecode(point2sh(nearpt), parentseg); + if (!segfacetadjacent(&parentseg, &paresntsh)) { + createflag = true; // create a shorter edge. + } + } + } else if (nearpt_type == FREEFACETVERTEX) { + if (steiner_type == FREESEGVERTEX) { + //assert(0); // to debug... + face parentseg, paresntsh; + sdecode(point2sh(nearpt), paresntsh); + sdecode(point2sh(steinerpt), parentseg); + if (!segfacetadjacent(&parentseg, &paresntsh)) { + createflag = true; // create a shorter edge. + } + } else if (steiner_type == FREEFACETVERTEX) { + // Create a short edge if they are on two different facets. + face paresntsh1, paresntsh2; + sdecode(point2sh(nearpt), paresntsh1); + sdecode(point2sh(steinerpt), paresntsh2); + int sidx1 = getfacetindex(paresntsh1); + int sidx2 = getfacetindex(paresntsh2); + if (sidx1 != sidx2) { + createflag = true; // create a shorter edge. + } + } + } + + return createflag; +} - // First check its quality. - if (checkconstraints && (areabound(*chkseg) > 0.0)) { - if (len > areabound(*chkseg)) { - qflag = 1; - return 1; - } - } +//============================================================================// +// // +// enqueuesubface() Queue a subface or a subsegment for encroachment check.// +// // +//============================================================================// - if (b->fixedvolume) { - if ((len * len * len) > b->maxvolume) { - qflag = 1; - return 1; - } +void tetgenmesh::enqueuesubface(memorypool *pool, face *chkface) +{ + if (!smarktest2ed(*chkface)) { + smarktest2(*chkface); // Only queue it once. + face *queface = (face *) pool->alloc(); + *queface = *chkface; } +} - if (b->metric) { // -m option. Check mesh size. - // Check if the ccent lies outside one of the prot.balls at vertices. - if (((forg[pointmtrindex] > 0) && (r > forg[pointmtrindex])) || - ((fdest[pointmtrindex]) > 0 && (r > fdest[pointmtrindex]))) { - qflag = 1; // Enforce mesh size. - return 1; - } +//============================================================================// +// // +// enqueuetetrahedron() Queue a tetrahedron for quality check. // +// // +//============================================================================// + +void tetgenmesh::enqueuetetrahedron(triface *chktet) +{ + if (!marktest2ed(*chktet)) { + marktest2(*chktet); // Only queue it once. + triface *quetet = (triface *) badtetrahedrons->alloc(); + *quetet = *chktet; } +} + +//============================================================================// +// // +// check_encroachment() Check whether a given point encroaches upon a line // +// segment or not. // +// // +// 'checkpt' should not be dummypoint. // +// // +//============================================================================// + +bool tetgenmesh::check_encroachment(point pa, point pb, point checkpt) +{ + // dot = (pa->checkpt) * (pb->checkpt) + REAL d = (pa[0] - checkpt[0]) * (pb[0] - checkpt[0]) + + (pa[1] - checkpt[1]) * (pb[1] - checkpt[1]) + + (pa[2] - checkpt[2]) * (pb[2] - checkpt[2]); + return d < 0.; // cos\theta < 0. ==> 90 < theta <= 180 degree. +} + +//============================================================================// +// // +// check_enc_segment() Is a given segment encroached? // +// // +//============================================================================// +bool tetgenmesh::check_enc_segment(face *chkseg, point *pencpt) +{ + point *ppt = (point *) &(chkseg->sh[3]); + + if (*pencpt != NULL) { + return check_encroachment(ppt[0], ppt[1], *pencpt); + } - // Second check if it is encroached. - // Comment: There may exist more than one encroaching points of this segment. - // The 'encpt' returns the one which is closet to it. triface searchtet, spintet; - point eapex; - REAL d, diff, smdist = 0; + point encpt = NULL, tapex; + REAL prjpt[3]; // The projection point from encpt to segment. + REAL minprjdist = 0., prjdist; int t1ver; sstpivot1(*chkseg, searchtet); spintet = searchtet; while (1) { - eapex = apex(spintet); - if (eapex != dummypoint) { - d = distance(ccent, eapex); - diff = d - r; - if (fabs(diff) / r < b->epsilon) diff = 0.0; // Rounding. - if (diff < 0) { - // This segment is encroached by eapex. - if (useinsertradius) { - if (encpt == NULL) { - encpt = eapex; - smdist = d; - } else { - // Choose the closet encroaching point. - if (d < smdist) { - encpt = eapex; - smdist = d; - } - } + tapex = apex(spintet); + if (tapex != dummypoint) { + if (check_encroachment(ppt[0], ppt[1], tapex)) { + // Find one encroaching vertex. Calculate its projection distance + projpt2edge(tapex, ppt[0], ppt[1], prjpt); + prjdist = distance(tapex, prjpt); + if (encpt == NULL) { + encpt = tapex; + minprjdist = prjdist; } else { - encpt = eapex; - break; + if (prjdist < minprjdist) { + encpt = tapex; + minprjdist = prjdist; + } } } } fnextself(spintet); if (spintet.tet == searchtet.tet) break; - } // while (1) + } if (encpt != NULL) { - return 1; + *pencpt = encpt; // Return this enc point. + return true; } - return 0; // No need to split it. + return false; // do not split it. } -/////////////////////////////////////////////////////////////////////////////// -// // -// splitsegment() Split a segment. // -// // -// The segment 'splitseg' is intended to be split. It will be split if it // -// is in one of the following cases: // -// (1) It is encroached by an existing vertex 'encpt != NULL'; or // -// (2) It is in bad quality 'qflag == 1'; or // -// (3) Its length is larger than the mesh sizes at its endpoints. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// get_steiner_on_segment() Get the Steiner point to split a given segment.// +// // +//============================================================================// -int tetgenmesh::splitsegment(face *splitseg, point encpt, REAL rrp, - point encpt1, point encpt2, int qflag, - int chkencflag) +bool tetgenmesh::get_steiner_on_segment(face* seg, point refpt, point steinpt) { - point pa = sorg(*splitseg); - point pb = sdest(*splitseg); - - + point ei = sorg(*seg); + point ej = sdest(*seg); + //if (*prefpt == NULL) { + // // Check if this segment is encroached by some existing vertices. + // assert(0); // to do ... + //} + // Is this segment contains an acute seg-seg angle? + bool acute_flag = false; + int i; - if ((encpt == NULL) && (qflag == 0)) { - if (useinsertradius) { - // Do not split this segment if the length is smaller than the smaller - // insertion radius at its endpoints. - REAL len = distance(pa, pb); - REAL smrrv = getpointinsradius(pa); - REAL rrv = getpointinsradius(pb); - if (rrv > 0) { - if (smrrv > 0) { - if (rrv < smrrv) { - smrrv = rrv; + if ((refpt) != NULL) { + // This segment is encroched by an existing vertex. + REAL L, L1, t; + + if (pointtype(refpt) == FREESEGVERTEX) { + face parentseg; + sdecode(point2sh(refpt), parentseg); + int sidx1 = getfacetindex(parentseg); + point far_pi = segmentendpointslist[sidx1 * 2]; + point far_pj = segmentendpointslist[sidx1 * 2 + 1]; + int sidx2 = getfacetindex(*seg); + point far_ei = segmentendpointslist[sidx2 * 2]; + point far_ej = segmentendpointslist[sidx2 * 2 + 1]; + if ((far_pi == far_ei) || (far_pj == far_ei)) { + // Two segments are adjacent at far_ei! + // Create a Steiner point at the intersection of the segment + // [far_ei, far_ej] and the sphere centered at far_ei with + // radius |far_ei - refpt|. + L = distance(far_ei, far_ej); + L1 = distance(far_ei, refpt); + t = L1 / L; + for (i = 0; i < 3; i++) { + steinpt[i] = far_ei[i] + t * (far_ej[i] - far_ei[i]); + } + REAL lfs_at_steiner = distance(refpt, steinpt); + //REAL dist_to_ei = distance(steinpt, ei); + REAL dist_to_ej = distance(steinpt, ej); + if (/*(dist_to_ei < lfs_at_steiner) ||*/ + (dist_to_ej < lfs_at_steiner)) { + // Split the point at the middle. + for (i = 0; i < 3; i++) { + steinpt[i] = ei[i] + 0.5 * (ej[i] - ei[i]); } - } else { - smrrv = rrv; } - } - if (smrrv > 0) { - if ((fabs(smrrv - len) / len) < b->epsilon) smrrv = len; - if (len < smrrv) { - return 0; + set_ridge_vertex_protecting_ball(far_ei); + acute_flag = true; + } else if ((far_pi == far_ej) || (far_pj == far_ej)) { + // Two segments are adjacent at far_ej! + L = distance(far_ei, far_ej); + L1 = distance(far_ej, refpt); + t = L1 / L; + for (i = 0; i < 3; i++) { + steinpt[i] = far_ej[i] + t * (far_ei[i] - far_ej[i]); + } + REAL lfs_at_steiner = distance(refpt, steinpt); + REAL dist_to_ei = distance(steinpt, ei); + //REAL dist_to_ej = distance(steinpt, ej); + if ((dist_to_ei < lfs_at_steiner) /*|| + (dist_to_ej < lfs_at_steiner)*/) { + // Split the point at the middle. + for (i = 0; i < 3; i++) { + steinpt[i] = ei[i] + 0.5 * (ej[i] - ei[i]); + } + } + set_ridge_vertex_protecting_ball(far_ej); + acute_flag = true; + } else { + // Cut the segment by the projection point of refpt. + projpt2edge(refpt, ei, ej, steinpt); + REAL lfs_at_steiner = distance(refpt, steinpt); + REAL dist_to_ei = distance(steinpt, ei); + REAL dist_to_ej = distance(steinpt, ej); + if ((dist_to_ei < lfs_at_steiner) || + (dist_to_ej < lfs_at_steiner)) { + // Split the point at the middle. + for (i = 0; i < 3; i++) { + steinpt[i] = ei[i] + 0.5 * (ej[i] - ei[i]); + } } } - } - } - - if (b->nobisect) { // With -Y option. - // Only split this segment if it is allowed to be split. - if (checkconstraints) { - // Check if it has a non-zero length bound. - if (areabound(*splitseg) == 0) { - // It is not allowed. However, if all of facets containing this seg - // is allowed to be split, we still split it. - face parentsh, spinsh; - //splitseg.shver = 0; - spivot(*splitseg, parentsh); - if (parentsh.sh == NULL) { - return 0; // A dangling segment. Do not split it. + } else if (pointtype(refpt) == RIDGEVERTEX) { + int sidx2 = getfacetindex(*seg); + point far_ei = segmentendpointslist[sidx2 * 2]; + point far_ej = segmentendpointslist[sidx2 * 2 + 1]; + if (ridge_vertices_adjacent(far_ei, refpt)) { + // Thjey are adjacent at far_ei. + // Create a Steiner point at the intersection of the segment + // [far_ei, far_ej] and the sphere centered at far_ei with + // radius |far_ei - refpt|. + L = distance(far_ei, far_ej); + L1 = distance(far_ei, refpt); + t = L1 / L; + for (i = 0; i < 3; i++) { + steinpt[i] = far_ei[i] + t * (far_ej[i] - far_ei[i]); } - spinsh = parentsh; - while (1) { - if (areabound(spinsh) == 0) break; - spivotself(spinsh); - if (spinsh.sh == parentsh.sh) break; + REAL lfs_at_steiner = distance(refpt, steinpt); + //REAL dist_to_ei = distance(steinpt, ei); + REAL dist_to_ej = distance(steinpt, ej); + if (/*(dist_to_ei < lfs_at_steiner) ||*/ + (dist_to_ej < lfs_at_steiner)) { + // Split the point at the middle. + for (i = 0; i < 3; i++) { + steinpt[i] = ei[i] + 0.5 * (ej[i] - ei[i]); + } + } + set_ridge_vertex_protecting_ball(far_ei); + acute_flag = true; + } else if (ridge_vertices_adjacent(far_ej, refpt)) { + // Calulate a new point. + L = distance(far_ei, far_ej); + L1 = distance(far_ej, refpt); + t = L1 / L; + for (i = 0; i < 3; i++) { + steinpt[i] = far_ej[i] + t * (far_ei[i] - far_ej[i]); + } + REAL lfs_at_steiner = distance(refpt, steinpt); + REAL dist_to_ei = distance(steinpt, ei); + //REAL dist_to_ej = distance(steinpt, ej); + if ((dist_to_ei < lfs_at_steiner) /*|| + (dist_to_ej < lfs_at_steiner)*/) { + // Split the point at the middle. + for (i = 0; i < 3; i++) { + steinpt[i] = ei[i] + 0.5 * (ej[i] - ei[i]); + } + } + set_ridge_vertex_protecting_ball(far_ej); + acute_flag = true; + } else { + // Cut the segment by the projection point of refpt. + projpt2edge(refpt, ei, ej, steinpt); + REAL lfs_at_steiner = distance(refpt, steinpt); + REAL dist_to_ei = distance(steinpt, ei); + REAL dist_to_ej = distance(steinpt, ej); + if ((dist_to_ei < lfs_at_steiner) || + (dist_to_ej < lfs_at_steiner)) { + // Split the point at the middle. + for (i = 0; i < 3; i++) { + steinpt[i] = ei[i] + 0.5 * (ej[i] - ei[i]); + } } - if (areabound(spinsh) == 0) { - // All facets at this seg are not allowed to be split. - return 0; // Do not split it. + } + } else if (pointtype(refpt) == FREEFACETVERTEX) { + // Cut the segment by the projection point of refpt. + projpt2edge(refpt, ei, ej, steinpt); + REAL lfs_at_steiner = distance(refpt, steinpt); + REAL dist_to_ei = distance(steinpt, ei); + REAL dist_to_ej = distance(steinpt, ej); + if ((dist_to_ei < lfs_at_steiner) || + (dist_to_ej < lfs_at_steiner)) { + // Split the point at the middle. + for (i = 0; i < 3; i++) { + steinpt[i] = ei[i] + 0.5 * (ej[i] - ei[i]); } } } else { - return 0; // Do not split this segment. + // Cut the segment by the projection point of refpt. + projpt2edge(refpt, ei, ej, steinpt); + // Make sure that steinpt is not too close to ei and ej. + REAL lfs_at_steiner = distance(refpt, steinpt); + REAL dist_to_ei = distance(steinpt, ei); + REAL dist_to_ej = distance(steinpt, ej); + if ((dist_to_ei < lfs_at_steiner) || + (dist_to_ej < lfs_at_steiner)) { + // Split the point at the middle. + for (i = 0; i < 3; i++) { + steinpt[i] = ei[i] + 0.5 * (ej[i] - ei[i]); + } + } + } + + // Make sure that steinpt is not too close to ei and ej. + } else { + // Split the point at the middle. + for (i = 0; i < 3; i++) { + steinpt[i] = ei[i] + 0.5 * (ej[i] - ei[i]); } - } // if (b->nobisect) + } + + return acute_flag; +} + +//============================================================================// +// // +// split_segment() Split a given segment. // +// // +// If param != NULL, it contains the circumcenter and its insertion radius // +// of a bad quality tetrahedron. Tghis circumcenter is rejected since it // +// encroaches upon this segment. // +// // +//============================================================================// + +bool tetgenmesh::split_segment(face *splitseg, point encpt, REAL *param, + int qflag, int chkencflag, int *iloc) +{ triface searchtet; face searchsh; point newpt; insertvertexflags ivf; + + insert_point_count++; + if (!b->quiet && (b->refine_progress_ratio > 0)) { + if (insert_point_count >= report_refine_progress) { + printf(" %ld insertions, added %ld points", + insert_point_count - last_insertion_count, + points->items - last_point_count); + last_point_count = points->items; // update it. + last_insertion_count = insert_point_count; + if (check_tets_list->objects > 0l) { + printf(", %ld tetrahedra in queue.\n", check_tets_list->objects); + } else if (split_subfaces_pool->items > 0l) { + printf(", %ld subfaces in queue.\n", split_subfaces_pool->items); + } else { + printf(", %ld segments in queue.\n", split_segments_pool->items); + } + // The next report event + report_refine_progress *= (1. + b->refine_progress_ratio); + } + } + // Is this segment shared by two facets form an acute dihedral angle? + int segidx = getfacetindex(*splitseg); + bool is_sharp = is_sharp_segment(splitseg); + + if (!qflag && (encpt == NULL)) { + // The split of this segment is due to a rejected ccent of a bad quality + // subface or a tetrahedron. + if (is_sharp) { + // Do not split a sharp segment. + *iloc = (int) SHARPCORNER; + return false; + } + // Do not split this segment if one of its endpoints is a sharp corner. + if (does_seg_contain_acute_vertex(splitseg)) { + *iloc = (int) SHARPCORNER; + return false; + } + } + + // We need to know whether the segment of the new point is adjacent + // to another segment which contains the encroached point (encpt). makepoint(&newpt, FREESEGVERTEX); - getsteinerptonsegment(splitseg, encpt, newpt); + get_steiner_on_segment(splitseg, encpt, newpt); + // For create_a_shorter_edge() called in insertpoint(). + setpoint2sh(newpt, sencode(*splitseg)); + // Split the segment by the Bowyer-Watson algorithm. sstpivot1(*splitseg, searchtet); ivf.iloc = (int) ONEDGE; - // Use Bowyer-Watson algorithm. Preserve subsegments and subfaces; - ivf.bowywat = 3; + ivf.bowywat = 3; // Use Bowyer-Watson, preserve subsegments and subfaces; ivf.validflag = 1; // Validate the B-W cavity. ivf.lawson = 2; // Do flips to recover Delaunayness. ivf.rejflag = 0; // Do not check encroachment of new segments/facets. @@ -24335,436 +27049,782 @@ int tetgenmesh::splitsegment(face *splitseg, point encpt, REAL rrp, ivf.splitbdflag = 1; ivf.respectbdflag = 1; ivf.assignmeshsize = b->metric; - ivf.smlenflag = useinsertradius; - + ivf.smlenflag = useinsertradius; // Return the distance to its nearest vertex. + // Reject a near Steiner point on this segment when: + // - it is only encroached by a rejected circumcenter, or + // - the insertion of the reject ccent is not due to mesh size (qflag). + if (!qflag) { //if (!is_adjacent || !qflag) { + ivf.check_insert_radius = useinsertradius; + } + ivf.parentpt = NULL; + if (insertpoint(newpt, &searchtet, &searchsh, splitseg, &ivf)) { st_segref_count++; if (steinerleft > 0) steinerleft--; if (useinsertradius) { - // Update 'rv' (to be the shortest distance). - REAL rv = ivf.smlen, rp; - if (pointtype(ivf.parentpt) == FREESEGVERTEX) { - face parentseg1, parentseg2; - sdecode(point2sh(newpt), parentseg1); - sdecode(point2sh(ivf.parentpt), parentseg2); - if (segsegadjacent(&parentseg1, &parentseg2)) { - rp = getpointinsradius(ivf.parentpt); - if (rv < rp) { - rv = rp; // The relaxed insertion radius of 'newpt'. - } - } - } else if (pointtype(ivf.parentpt) == FREEFACETVERTEX) { - face parentseg, parentsh; - sdecode(point2sh(newpt), parentseg); - sdecode(point2sh(ivf.parentpt), parentsh); - if (segfacetadjacent(&parentseg, &parentsh)) { - rp = getpointinsradius(ivf.parentpt); - if (rv < rp) { - rv = rp; // The relaxed insertion radius of 'newpt'. - } + REAL rv = 0.0; // param[3]; // emin, maybe zero. + + if (is_sharp) { + // A Steiner point on a sharp segment needs insertion radius. + // Default use the distance to its neartest vertex. + double L = ivf.smlen * 0.95; // (ivf.smlen / 3.); + // Choose the larger one between param[3] and L + rv = (param[3] > L ? param[3] : L); + // Record the minimum insertion radius for this segment. + double minradius = segment_info_list[segidx*4+1]; + if (minradius == 0.) { + minradius = rv; + } else { + if (rv < minradius) minradius = rv; } + segment_info_list[segidx*4+1] = minradius; + } + + setpointinsradius(newpt, rv); // ivf.smlen + setpoint2ppt(newpt, ivf.parentpt); + if (ivf.smlen < smallest_insradius) { // rv? + smallest_insradius = ivf.smlen; } - setpointinsradius(newpt, rv); } if (flipstack != NULL) { flipconstraints fc; fc.chkencflag = chkencflag; fc.enqflag = 2; lawsonflip3d(&fc); - unflipqueue->restart(); + //unflipqueue->restart(); } - return 1; + + if (later_unflip_queue->objects > b->unflip_queue_limit) { + recoverdelaunay(); + } + + *iloc = ivf.iloc; + return true; } else { // Point is not inserted. + if (ivf.iloc == (int) NEARVERTEX) { + terminatetetgen(this, 2); // report a bug. + } + + pointdealloc(newpt); - return 0; + + *iloc = ivf.iloc; + return false; } } -/////////////////////////////////////////////////////////////////////////////// -// // -// repairencsegs() Repair encroached (sub) segments. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// repairencsegs() Repair encroached (sub) segments. // +// // +//============================================================================// -void tetgenmesh::repairencsegs(int chkencflag) +void tetgenmesh::repairencsegs(REAL *param, int qflag, int chkencflag) { - face *bface; - point encpt = NULL; - int qflag = 0; + int split_count = 0, rej_count = 0; + bool ref_segment = ((b->cdtrefine & 1) > 0); // -D1, -D3, -D5, -D7 - // Loop until the pool 'badsubsegs' is empty. Note that steinerleft == -1 - // if an unlimited number of Steiner points is allowed. - while ((badsubsegs->items > 0) && (steinerleft != 0)) { - badsubsegs->traversalinit(); - bface = (face *) badsubsegs->traverse(); - while ((bface != NULL) && (steinerleft != 0)) { - // Skip a deleleted element. - if (bface->shver >= 0) { + while (ref_segment && + ((badsubsegs->items > 0) || (split_segments_pool->items > 0))) { + + if (badsubsegs->items > 0) { + badsubsegs->traversalinit(); + face *bface = (face *) badsubsegs->traverse(); + while (bface != NULL) { // A queued segment may have been deleted (split). if ((bface->sh != NULL) && (bface->sh[3] != NULL)) { - // A queued segment may have been processed. + // A queued segment may have been processed. if (smarktest2ed(*bface)) { sunmarktest2(*bface); - if (checkseg4split(bface, encpt, qflag)) { - splitsegment(bface, encpt, 0, NULL, NULL, qflag, chkencflag); + point encpt = NULL; + if (check_enc_segment(bface, &encpt)) { + badface *bf = (badface *) split_segments_pool->alloc(); + bf->init(); + bf->ss = *bface; + bf->forg = sorg(*bface); + bf->fdest = sdest(*bface); + bf->noppo = encpt; + // Push it onto stack. + bf->nextitem = stack_enc_segments; + stack_enc_segments = bf; } } } - // Remove this entry from list. - bface->shver = -1; // Signal it as a deleted element. - badsubsegs->dealloc((void *) bface); + bface = (face *) badsubsegs->traverse(); + } // while (bface != NULL) + badsubsegs->restart(); + } // if (badsubsegs->items > 0) + + if (split_segments_pool->items == 0) break; + + // Stop if we have used the desried number of Steiner points. + if (steinerleft == 0) break; + // Stop if the desried number of tetrahedra is reached. + if ((elem_limit > 0) && + ((tetrahedrons->items - hullsize) > elem_limit)) break; + + // Pop up an encroached segment. + badface *bf = stack_enc_segments; + stack_enc_segments = bf->nextitem; + if ((bf->ss.sh != NULL) && + (sorg(bf->ss) == bf->forg) && + (sdest(bf->ss) == bf->fdest)) { + int iloc = (int) UNKNOWN; + split_count++; + if (!split_segment(&(bf->ss), bf->noppo, param, qflag, chkencflag, &iloc)) { + rej_count++; } - bface = (face *) badsubsegs->traverse(); } + // Return this badface to the pool. + split_segments_pool->dealloc((void *) bf); + } + + if (b->verbose > 2) { + printf(" Trying to split %d segments, %d were rejected.\n", + split_count, rej_count); } if (badsubsegs->items > 0) { + // Clean this list (due to ref_segment). + badsubsegs->traversalinit(); + face *bface = (face *) badsubsegs->traverse(); + while (bface != NULL) { + // A queued segment may have been deleted (split). + if ((bface->sh != NULL) && (bface->sh[3] != NULL)) { + // A queued segment may have been processed. + if (smarktest2ed(*bface)) { + sunmarktest2(*bface); + } + } + bface = (face *) badsubsegs->traverse(); + } // while (bface != NULL) + badsubsegs->restart(); + } // if (badsubsegs->items > 0) + + if (split_segments_pool->items > 0) { if (steinerleft == 0) { if (b->verbose) { printf("The desired number of Steiner points is reached.\n"); } - } else { - assert(0); // Unknown case. - } - badsubsegs->traversalinit(); - bface = (face *) badsubsegs->traverse(); - while (bface != NULL) { - // Skip a deleleted element. - if (bface->shver >= 0) { - if ((bface->sh != NULL) && (bface->sh[3] != NULL)) { - if (smarktest2ed(*bface)) { - sunmarktest2(*bface); - } - } + } else if (elem_limit > 0) { + if (b->verbose) { + printf("The desired number %ld of elements is reached.\n", elem_limit); } - bface = (face *) badsubsegs->traverse(); } - badsubsegs->restart(); + split_segments_pool->restart(); + stack_enc_segments = NULL; } } -/////////////////////////////////////////////////////////////////////////////// -// // -// enqueuesubface() Queue a subface or a subsegment for encroachment chk. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// get_subface_ccent() Calculate the circumcenter of the diametrical circ- // +// umsphere of a given subface. // +// // +//============================================================================// -void tetgenmesh::enqueuesubface(memorypool *pool, face *chkface) +bool tetgenmesh::get_subface_ccent(face *chkfac, REAL *pos) { - if (!smarktest2ed(*chkface)) { - smarktest2(*chkface); // Only queue it once. - face *queface = (face *) pool->alloc(); - *queface = *chkface; + point P = (point) chkfac->sh[3]; + point Q = (point) chkfac->sh[4]; + point R = (point) chkfac->sh[5]; + + if (circumsphere(P, Q, R, NULL, pos, NULL)) { + return true; + } else { + terminatetetgen(this, 2); + return false; } + } -/////////////////////////////////////////////////////////////////////////////// -// // -// checkfac4encroach() Check if a subface is encroached by a point. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// check_enc_subface() Check if a given subface is encroached or not. // +// // +//============================================================================// -int tetgenmesh::checkfac4encroach(point pa, point pb, point pc, point checkpt, - REAL* cent, REAL* r) +bool tetgenmesh::check_enc_subface(face *chkfac, point *pencpt, REAL *ccent, + REAL *radius) { - REAL rd, len; - - circumsphere(pa, pb, pc, NULL, cent, &rd); - assert(rd != 0); - len = distance(cent, checkpt); - if ((fabs(len - rd) / rd) < b->epsilon) len = rd; // Rounding. - - if (len < rd) { - // The point lies inside the circumsphere of this face. - if (b->metric) { // -m option. - if ((pa[pointmtrindex] > 0) && (pb[pointmtrindex] > 0) && - (pc[pointmtrindex] > 0)) { - // Get the projection of 'checkpt' in the plane of pa, pb, and pc. - REAL prjpt[3], n[3]; - REAL a, a1, a2, a3; - projpt2face(checkpt, pa, pb, pc, prjpt); - // Get the face area of [a,b,c]. - facenormal(pa, pb, pc, n, 1, NULL); - a = sqrt(dot(n,n)); - // Get the face areas of [a,b,p], [b,c,p], and [c,a,p]. - facenormal(pa, pb, prjpt, n, 1, NULL); - a1 = sqrt(dot(n,n)); - facenormal(pb, pc, prjpt, n, 1, NULL); - a2 = sqrt(dot(n,n)); - facenormal(pc, pa, prjpt, n, 1, NULL); - a3 = sqrt(dot(n,n)); - if ((fabs(a1 + a2 + a3 - a) / a) < b->epsilon) { - // This face contains the projection. - // Get the mesh size at the location of the projection point. - rd = a1 / a * pc[pointmtrindex] - + a2 / a * pa[pointmtrindex] - + a3 / a * pb[pointmtrindex]; - len = distance(prjpt, checkpt); - if (len < rd) { - return 1; // Encroached. - } - } - } else { - return 1; // No protecting ball. Encroached. - } - } else { - *r = rd; - return 1; // Encroached. + triface adjtet; + point encpt = NULL, pa, pb, pc, toppo; + REAL prjpt[3], minprjdist = 0., prjdist; + REAL ori; + int t1ver; + + //get_subface_ccent(chkfac, ccent); + REAL rd = distance(ccent, sorg(*chkfac)); + *radius = rd; + + if (*pencpt != NULL) { + // This is only used during the insertion of a Steiner point. + REAL len = distance(ccent, *pencpt); + if ((fabs(len - rd) / rd) < 1e-3) len = rd; // Rounding. + if (len < rd) { + return true; } + return false; } - return 0; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// checkfac4split() Check if a subface needs to be split. // -// // -// A subface needs to be split if it is in the following case: // -// (1) It is encroached by an existing vertex. // -// (2) It has bad quality (has a small angle, -q). // -// (3) It's area is larger than a prescribed value (.var). // -// // -// Return 1 if it needs to be split, otherwise, return 0. // -// 'chkfac' represents its longest edge. // -// // -/////////////////////////////////////////////////////////////////////////////// - -int tetgenmesh::checkfac4split(face *chkfac, point& encpt, int& qflag, - REAL *cent) -{ - point pa, pb, pc; - REAL area, rd, len; - REAL A[4][4], rhs[4], D; - int indx[4]; - int i; - - encpt = NULL; - qflag = 0; - - pa = sorg(*chkfac); - pb = sdest(*chkfac); - pc = sapex(*chkfac); + stpivot(*chkfac, adjtet); + if (adjtet.tet == NULL) { + // This subface is not attached to any tet. + return false; + } + for (int i = 0; i < 2; i++) { + toppo = oppo(adjtet); + if (toppo != dummypoint) { + REAL len = distance(ccent, toppo); + //if ((fabs(len - rd) / rd) < b->epsilon) len = rd; // Rounding. + if ((fabs(len - rd) / rd) < 1e-3) len = rd; // Rounding. + if (len < rd) { + int adjacent = 0; // not adjacent + if (pointtype(toppo) == RIDGEVERTEX) { + adjacent = facet_ridge_vertex_adjacent(chkfac, toppo); + } else if (pointtype(toppo) == FREESEGVERTEX) { + face parentseg; + sdecode(point2sh(toppo), parentseg); + adjacent = segfacetadjacent(&parentseg, chkfac); + } else if (pointtype(toppo) == FREEFACETVERTEX) { + face parentsh; + sdecode(point2sh(toppo), parentsh); + int facidx1 = getfacetindex(parentsh); + int facidx2 = getfacetindex(*chkfac); + if (facidx1 == facidx2) { + adjacent = 1; // They are on the same facet. + } + } + if (adjacent) { + // They are adjacent and they are on the same facet. + flippush(flipstack, &adjtet); + return false; + } + pa = org(adjtet); + pb = dest(adjtet); + pc = apex(adjtet); + projpt2face(toppo, pa, pb, pc, prjpt); + ori = orient3d(pa, pb, toppo, prjpt); + if (ori >= 0) { + ori = orient3d(pb, pc, toppo, prjpt); + if (ori >= 0) { + ori = orient3d(pc, pa, toppo, prjpt); + if (ori >= 0) { + prjdist = distance(toppo, prjpt); + if (encpt == NULL) { + encpt = toppo; + minprjdist = prjdist; + } else { + if (prjdist < minprjdist) { + encpt = toppo; + minprjdist = prjdist; + } + } + } // if (ori >= 0) + } // if (ori >= 0) + } // if (ori >= 0) + } // if (len < rd) + } + fsymself(adjtet); + } - // Compute the coefficient matrix A (3x3). - A[0][0] = pb[0] - pa[0]; - A[0][1] = pb[1] - pa[1]; - A[0][2] = pb[2] - pa[2]; // vector V1 (pa->pb) - A[1][0] = pc[0] - pa[0]; - A[1][1] = pc[1] - pa[1]; - A[1][2] = pc[2] - pa[2]; // vector V2 (pa->pc) - cross(A[0], A[1], A[2]); // vector V3 (V1 X V2) + if (encpt != NULL) { + *pencpt = encpt; + return true; + } - area = 0.5 * sqrt(dot(A[2], A[2])); // The area of [a,b,c]. + return false; // this subface is not encroached. +} - // Compute the right hand side vector b (3x1). - rhs[0] = 0.5 * dot(A[0], A[0]); // edge [a,b] - rhs[1] = 0.5 * dot(A[1], A[1]); // edge [a,c] - rhs[2] = 0.0; +//============================================================================// +// // +// check_subface() Is a given subface in a bad shape (radius-edge ratio)? // +// // +//============================================================================// - // Solve the 3 by 3 equations use LU decomposition with partial - // pivoting and backward and forward substitute. - if (!lu_decmp(A, 3, indx, &D, 0)) { - // A degenerate triangle. - assert(0); - } +bool tetgenmesh::check_subface(face *chkfac, REAL *ccent, REAL radius, REAL *param) +{ - lu_solve(A, 3, indx, rhs, 0); - cent[0] = pa[0] + rhs[0]; - cent[1] = pa[1] + rhs[1]; - cent[2] = pa[2] + rhs[2]; - rd = sqrt(rhs[0] * rhs[0] + rhs[1] * rhs[1] + rhs[2] * rhs[2]); - - if (checkconstraints && (areabound(*chkfac) > 0.0)) { - // Check if the subface has too big area. - if (area > areabound(*chkfac)) { - qflag = 1; - return 1; + // Get the shortest edge length. + REAL emin = 1.e+30, dist; + int shver = 0; + for (chkfac->shver = 0; chkfac->shver < 3; chkfac->shver++) { + dist = distance(sorg(*chkfac), sdest(*chkfac)); + if (dist < emin) { + emin = dist; + shver = chkfac->shver; } } + chkfac->shver = shver; - if (b->fixedvolume) { - if ((area * sqrt(area)) > b->maxvolume) { - qflag = 1; - return 1; + REAL ratio = radius / emin; + if (ratio > b->minratio) { + // Set a small value to protect this vertex (refer to J. Shewchuk). + // Enlarge the insertion radius (due to small angle) + point pa = sorg(*chkfac); + point pb = sdest(*chkfac); + REAL ra = getpointinsradius(pa); + REAL rb = getpointinsradius(pb); + if (ra > 0.) { + if (ra > emin) { + emin = ra; + } + } + if (rb > 0.) { + if (rb > emin) { + emin = rb; + } } + + param[3] = emin; // emin / 3.; // (emin * b->minratio); + param[4] = ratio; + param[5] = 0.; // not used. + return true; // need to split it. } - if (b->varvolume) { - triface adjtet; - REAL volbnd; - int t1ver; + return false; +} - stpivot(*chkfac, adjtet); - if (!ishulltet(adjtet)) { - volbnd = volumebound(adjtet.tet); - if ((volbnd > 0) && (area * sqrt(area)) > volbnd) { - qflag = 1; - return 1; +//============================================================================// +// // +// enqueue_subface() Push a badly-shaped subface into the priority queue. // +// // +//============================================================================// + +void tetgenmesh::enqueue_subface(face *bface, point encpt, REAL *ccent, REAL *param) +{ + badface *bf = (badface *) split_subfaces_pool->alloc(); + bf->init(); + bf->ss = *bface; + bf->forg = sorg(*bface); + bf->fdest = sdest(*bface); + bf->fapex = sapex(*bface); + bf->noppo = encpt; + int i; + for (i = 0; i < 3; i++) bf->cent[i] = ccent[i]; + for (i = 3; i < 6; i++) bf->cent[i] = param[i]; + + if (encpt != NULL) { + // Push it into the encroaching stack. + bf->nextitem = stack_enc_subfaces; + stack_enc_subfaces = bf; + } else { + // Push it into the priority queue. + REAL qual = 1.0; + if (param[4] > 1.) { + qual = 1.0 / param[4]; // 1 / radius_edge_ratio. + } + // Determine the appropriate queue to put the bad subface into. + int queuenumber = 0; + if (qual < 1) { + queuenumber = (int) (64.0 * (1 - qual)); + if (queuenumber > 63) { + queuenumber = 63; } + } else { + // It's not a bad shape; put the subface in the lowest-priority queue. + queuenumber = 0; } - fsymself(adjtet); - if (!ishulltet(adjtet)) { - volbnd = volumebound(adjtet.tet); - if ((volbnd > 0) && (area * sqrt(area)) > volbnd) { - qflag = 1; - return 1; - } + + // Are we inserting into an empty queue? + if (queuefront[queuenumber] == (badface *) NULL) { + // Yes, we are inserting into an empty queue. + // Will this become the highest-priority queue? + if (queuenumber > firstnonemptyq) { + // Yes, this is the highest-priority queue. + nextnonemptyq[queuenumber] = firstnonemptyq; + firstnonemptyq = queuenumber; + } else { + // No, this is not the highest-priority queue. + // Find the queue with next higher priority. + int i = queuenumber + 1; + while (queuefront[i] == (badface *) NULL) { + i++; + } + // Mark the newly nonempty queue as following a higher-priority queue. + nextnonemptyq[queuenumber] = nextnonemptyq[i]; + nextnonemptyq[i] = queuenumber; + } + // Put the bad subface at the beginning of the (empty) queue. + queuefront[queuenumber] = bf; + } else { + // Add the bad tetrahedron to the end of an already nonempty queue. + queuetail[queuenumber]->nextitem = bf; } + // Maintain a pointer to the last subface of the queue. + queuetail[queuenumber] = bf; } +} - if (b->metric) { // -m option. Check mesh size. - // Check if the ccent lies outside one of the prot.balls at vertices. - if (((pa[pointmtrindex] > 0) && (rd > pa[pointmtrindex])) || - ((pb[pointmtrindex] > 0) && (rd > pb[pointmtrindex])) || - ((pc[pointmtrindex] > 0) && (rd > pc[pointmtrindex]))) { - qflag = 1; // Enforce mesh size. - return 1; +// Return the subface at the front of the queue. +tetgenmesh::badface* tetgenmesh::top_subface() +{ + if (stack_enc_subfaces != NULL) { + return stack_enc_subfaces; + } else { + // Keep a record of which queue was accessed in case dequeuebadtetra() + // is called later. + recentq = firstnonemptyq; + // If no queues are nonempty, return NULL. + if (firstnonemptyq < 0) { + return (badface *) NULL; + } else { + // Return the first tetrahedron of the highest-priority queue. + return queuefront[firstnonemptyq]; } } +} - triface searchtet; - REAL smlen = 0; - - // Check if this subface is locally encroached. - for (i = 0; i < 2; i++) { - stpivot(*chkfac, searchtet); - if (!ishulltet(searchtet)) { - len = distance(oppo(searchtet), cent); - if ((fabs(len - rd) / rd) < b->epsilon) len = rd;// Rounding. - if (len < rd) { - if (smlen == 0) { - smlen = len; - encpt = oppo(searchtet); +//============================================================================// +// // +// dequeue_subface() Popup a badly-shaped subface from the priority queue. // +// // +//============================================================================// + +void tetgenmesh::dequeue_subface() +{ + badface *bf; + int i; + + if (stack_enc_subfaces != NULL) { + bf = stack_enc_subfaces; + stack_enc_subfaces = bf->nextitem; + // Return the bad subface to the pool. + split_subfaces_pool->dealloc((void *) bf); + } else { + // If queues were empty last time topbadtetra() was called, do nothing. + if (recentq >= 0) { + // Find the tetrahedron last returned by topbadtetra(). + bf = queuefront[recentq]; + // Remove the tetrahedron from the queue. + queuefront[recentq] = bf->nextitem; + // If this queue is now empty, update the list of nonempty queues. + if (bf == queuetail[recentq]) { + // Was this the highest-priority queue? + if (firstnonemptyq == recentq) { + // Yes; find the queue with next lower priority. + firstnonemptyq = nextnonemptyq[firstnonemptyq]; } else { - if (len < smlen) { - smlen = len; - encpt = oppo(searchtet); + // No; find the queue with next higher priority. + i = recentq + 1; + while (queuefront[i] == (badface *) NULL) { + i++; } + nextnonemptyq[i] = nextnonemptyq[recentq]; } - //return 1; } + // Return the bad subface to the pool. + split_subfaces_pool->dealloc((void *) bf); } - sesymself(*chkfac); + } +} + +//============================================================================// +// // +// parallel_shift() Parallel shift a triangle along its normal. // +// // +// Given a triangle (a, b, c), create a parallel triangle (pa, pb, pc) at a // +// distance above (a, b, c). // +// // +//============================================================================// + +void tetgenmesh::parallel_shift(point pa, point pb, point pc, + point pt, REAL* ppt) +{ + // Get the normal and the average edge length of this triangle. + REAL N[3], Lav; + facenormal(pa, pb, pc, N, 1, &Lav); + + // Normalize the normal. + REAL L = sqrt(N[0]*N[0]+N[1]*N[1]+N[2]*N[2]); + N[0] /= L; + N[1] /= L; + N[2] /= L; + + // Calculate the shifted vertices. + for (int i = 0; i < 3; i++) { + ppt[0] = pt[0] + Lav * N[0]; + ppt[1] = pt[1] + Lav * N[1]; + ppt[2] = pt[2] + Lav * N[2]; } - return encpt != NULL; //return 0; } -/////////////////////////////////////////////////////////////////////////////// -// // -// splitsubface() Split a subface. // -// // -// The subface may be encroached, or in bad-quality. It is split at its cir- // -// cumcenter ('ccent'). Do not split it if 'ccent' encroaches upon any seg- // -// ment. Instead, one of the encroached segments is split. It is possible // -// that none of the encroached segments can be split. // -// // -// The return value indicates whether a new point is inserted (> 0) or not // -// (= 0). Furthermore, it is inserted on an encroached segment (= 1) or // -// in-side the facet (= 2). // -// // -// 'encpt' is a vertex encroaching upon this subface, i.e., it causes the // -// split of this subface. If 'encpt' is NULL, then the cause of the split // -// this subface is a rejected tet circumcenter 'p', and 'encpt1' is the // -// parent of 'p'. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// locate_on_surface() Locate a vertex in a facet. // +// // +//============================================================================// -int tetgenmesh::splitsubface(face *splitfac, point encpt, point encpt1, - int qflag, REAL *ccent, int chkencflag) +enum tetgenmesh::locateresult +tetgenmesh::locate_on_surface(point searchpt, face* searchsh) { - point pa = sorg(*splitfac); - point pb = sdest(*splitfac); - point pc = sapex(*splitfac); + enum locateresult loc = OUTSIDE; + triface searchtet; + stpivot(*searchsh, searchtet); + if (ishulltet(searchtet)) { + sesymself(*searchsh); + stpivot(*searchsh, searchtet); + } + // Select an edge such that pt lies to CCW of it. + point pa, pb, pc; + REAL toppo[3]; // a parallel-shifted point + REAL n1[3], n2[3], cosang; + int t1ver; // used by fnextself() + int i; + + for (i = 0; i < 3; i++) { + pa = org(searchtet); + pb = dest(searchtet); + pc = apex(searchtet); + parallel_shift(pa, pb, pc, pa, toppo); + if (orient3d(pa, pb, toppo, searchpt) > 0) { + break; + } + enextself(searchtet); + } + if (i == 3) { + terminatetetgen(this, 2); + } + + while (true) { - if (b->nobisect) { // With -Y option. - if (checkconstraints) { - // Only split if it is allowed to be split. - // Check if this facet has a non-zero constraint. - if (areabound(*splitfac) == 0) { - return 0; // Do not split it. - } - } else { - return 0; + // Let E = [a,b,c] and p lies to the CCW of [a->b]. + // Make sure that the searching vertex and the current subface (a,b,c) are + // (nearly) coplanar. We check the dihedral angle between (a,b,c) and + // (a,b,searchpt). If it is within the tolerance of co-planar facets, + // then we continue the search, otherwise, the search is stopped. + facenormal(pa, pb, pc, n1, 1, NULL); + facenormal(pb, pa, searchpt, n2, 1, NULL); + cosang = dot(n1, n2) / (sqrt(dot(n1, n1)) * sqrt(dot(n2, n2))); + if (cosang > cos_facet_separate_ang_tol) { + // The searching vertex is not coplanar with this subface. + loc = NONCOPLANAR; + break; + } + + parallel_shift(pa, pb, pc, pc, toppo); + REAL ori1 = orient3d(pb, pc, toppo, searchpt); + REAL ori2 = orient3d(pc, pa, toppo, searchpt); + + if (ori1 > 0) { + if (ori2 > 0) { + //break; // Found. + loc = ONFACE; break; + } else if (ori2 < 0) { + //E.ver = _eprev_tbl[E.ver]; + eprevself(searchtet); + } else { // ori2 == 0 + //E.ver = _eprev_tbl[E.ver]; + //return LOC_ON_EDGE; // ONEDGE p lies on edge [c,a] + eprevself(searchtet); + loc = ONEDGE; break; + } + } else if (ori1 < 0) { + if (ori2 > 0) { + //E.ver = _enext_tbl[E.ver]; + enextself(searchtet); + } else if (ori2 < 0) { + // Randomly choose one. + if (rand() % 2) { // flipping a coin. + //E.ver = _enext_tbl[E.ver]; + enextself(searchtet); + } else { + //E.ver = _eprev_tbl[E.ver]; + eprevself(searchtet); + } + } else { // ori2 == 0 + //E.ver = _enext_tbl[E.ver]; + enextself(searchtet); + } + } else { // ori1 == 0 + if (ori2 > 0) { + //E.ver = _enext_tbl[E.ver]; // p lies on edge [b,c]. + //return LOC_ON_EDGE; // ONEDGE + enextself(searchtet); + loc = ONEDGE; break; + } else if (ori2 < 0) { + //E.ver = _eprev_tbl[E.ver]; + eprevself(searchtet); + } else { // ori2 == 0 + //E.ver = _eprev_tbl[E.ver]; // p is coincident with apex. + //return LOC_ON_VERT; // ONVERTEX Org(E) + eprevself(searchtet); + loc = ONVERTEX; break; + } + } + + // Check if we want to cross a segment. + if (issubseg(searchtet)) { + loc = ENCSEGMENT; break; + } + + // Goto the adjacent subface at this subedge. + int fcount = 0; + while (fcount < 100000) { + esymself(searchtet); + if (issubface(searchtet)) break; + fsymself(searchtet); + fcount++; + } + if (!issubface(searchtet)) { + terminatetetgen(this, 2); // report a bug } - } // if (b->nobisect) + // Update the vertices. + pa = org(searchtet); + pb = dest(searchtet); + pc = apex(searchtet); + //toppo = oppo(searchtet); + } // while (true) + + tspivot(searchtet, *searchsh); + + return loc; +} + +//============================================================================// +// // +// split_subface() Split a subface. // +// // +// param[6], it contains the following data: // +// [0],[1],[2] - the location of a rejected circumcent, // +// [3] - the samllest edge length ( = insertion radius) // +// [4] - ratio-edge ratio (of this subface). // +// If it is zero, it is an encroached subface. // +// [5] - no used. // +/// // +//============================================================================// + +bool tetgenmesh::split_subface(face *splitfac, point encpt, REAL *ccent, + REAL *param, int qflag, int chkencflag, int *iloc) +{ + triface searchtet; face searchsh; insertvertexflags ivf; - point newpt; - REAL rv = 0., rp; // Insertion radius of newpt. + point newpt, bak_pts[3], *ppt; + bool is_adjacent = false; + bool splitflag = false; // Indicate if any Steiner point is added. int i; - // Initialize the inserting point. - makepoint(&newpt, FREEFACETVERTEX); - // Split the subface at its circumcenter. - for (i = 0; i < 3; i++) newpt[i] = ccent[i]; + insert_point_count++; + if (!b->quiet && (b->refine_progress_ratio > 0.)) { + if (insert_point_count >= report_refine_progress) { + printf(" %ld insertions, added %ld points", + insert_point_count - last_insertion_count, + points->items - last_point_count); + last_point_count = points->items; // update it. + last_insertion_count = insert_point_count; + if (check_tets_list->objects > 0l) { + printf(", %ld tetrahedra in queue.\n", check_tets_list->objects); + } else { + printf(", %ld subfaces in queue.\n", split_subfaces_pool->items); + } + // The next report event + report_refine_progress *= (1. + b->refine_progress_ratio); + } + } - if (useinsertradius) { - if (encpt != NULL) { - rv = distance(newpt, encpt); - if (pointtype(encpt) == FREESEGVERTEX) { - face parentseg; - sdecode(point2sh(encpt), parentseg); - if (segfacetadjacent(&parentseg, splitfac)) { - rp = getpointinsradius(encpt); - if (rv < (sqrt(2.0) * rp)) { - // This insertion may cause no termination. - pointdealloc(newpt); - return 0; // Reject the insertion of newpt. - } - } - } else if (pointtype(encpt) == FREEFACETVERTEX) { - face parentsh; - sdecode(point2sh(encpt), parentsh); - if (facetfacetadjacent(&parentsh, splitfac)) { - rp = getpointinsradius(encpt); - if (rv < rp) { - pointdealloc(newpt); - return 0; // Reject the insertion of newpt. - } + // Check if this subface is adjacent to a sharp segment, i.e., it is incident + // by two facets which form an acute dihedral angle. + face checkface = *splitfac; + face checkseg; + for (i = 0; i < 3; i++) { + sspivot(checkface, checkseg); + if (checkseg.sh != NULL) { + if (is_sharp_segment(&checkseg)) { + is_adjacent = true; + break; + } + } + senext2self(checkface); + } + + if (is_adjacent) { + // Only split it either it is a bad quality triangle, or due to the + // qflag, i.e., mesh size requirement. + if (!qflag) { + if (encpt != NULL) { + *iloc = (int) SHARPCORNER; + return false; // reject splitting this subface. + } else { + if (param[4] == 0.0) { + // It is not a bad quality subface. + *iloc = (int) SHARPCORNER; + return false; // reject splitting this subface. } } } - } // if (useinsertradius) + } // if (is_adjacent) + + + // Deciding the inserting point. + if (encpt != NULL) { + // Insert at the projection of the encpt on the facet. + REAL pos[3]; + ppt = (point *) &(splitfac->sh[3]); + projpt2face(encpt, ppt[0], ppt[1], ppt[2], pos); + makepoint(&newpt, FREEFACETVERTEX); + for (i = 0; i < 3; i++) newpt[i] = pos[i]; + + //if (is_adjacent) { + // Check whether this new position is too close to an existing vertex. + REAL prjdist = distance(encpt, newpt); + REAL dist, mindist = 1.e+30; + for (i = 0; i < 3; i++) { + dist = distance(ppt[i], newpt); + if (dist < mindist) mindist = dist; + } + if (mindist < prjdist) { + // Use the circumcenter of this triange instead of the proj of encpt. + for (i = 0; i < 3; i++) newpt[i] = ccent[i]; + } + //} + } else { + // Split the subface at its circumcenter. + makepoint(&newpt, FREEFACETVERTEX); + for (i = 0; i < 3; i++) newpt[i] = ccent[i]; + } + + // This info is needed by create_a_shorter_edge() (called in insertpoint()). + setpoint2sh(newpt, sencode(*splitfac)); + - // Search a subface which contains 'newpt'. searchsh = *splitfac; - // Calculate an above point. It lies above the plane containing - // the subface [a,b,c], and save it in dummypoint. Moreover, - // the vector cent->dummypoint is the normal of the plane. - calculateabovepoint4(newpt, pa, pb, pc); - // Parameters: 'aflag' = 1, - above point exists. - // 'cflag' = 0, - non-convex, check co-planarity of the result. - // 'rflag' = 0, - no need to round the locating result. - ivf.iloc = (int) slocate(newpt, &searchsh, 1, 0, 0); - - if (!((ivf.iloc == (int) ONFACE) || (ivf.iloc == (int) ONEDGE))) { + ivf.iloc = (int) locate_on_surface(newpt, &searchsh); + + if (ivf.iloc == (int) ENCSEGMENT) { + // Point lies in the outside of the facet. pointdealloc(newpt); - return 0; + *iloc = FENSEDIN; // it is a fested in vertex. + return splitflag; + } else if (ivf.iloc == (int) ONVERTEX) { + pointdealloc(newpt); + *iloc = ONVERTEX; + return splitflag; + } else if (ivf.iloc == (int) NONCOPLANAR) { + pointdealloc(newpt); + *iloc = NONCOPLANAR; + return splitflag; } - - triface searchtet; - face *paryseg; - int splitflag; + if ((ivf.iloc != (int) ONFACE) && (ivf.iloc != (int) ONEDGE)) { + terminatetetgen(this, 2); // report a bug + } // Insert the point. stpivot(searchsh, searchtet); - //assert((ivf.iloc == (int) ONFACE) || (ivf.iloc == (int) ONEDGE)); - // Use Bowyer-Watson algorithm. Preserve subsegments and subfaces; - ivf.bowywat = 3; + ivf.bowywat = 3; // Use Bowyer-Watson. Preserve subsegments and subfaces; ivf.lawson = 2; ivf.rejflag = 1; // Do check the encroachment of segments. if (b->metric) { ivf.rejflag |= 4; // Do check encroachment of protecting balls. } - ivf.chkencflag = chkencflag; + ivf.chkencflag = (chkencflag & (~1)); ivf.sloc = (int) INSTAR; // ivf.iloc; ivf.sbowywat = 3; // ivf.bowywat; ivf.splitbdflag = 1; @@ -24773,171 +27833,487 @@ int tetgenmesh::splitsubface(face *splitfac, point encpt, point encpt1, ivf.assignmeshsize = b->metric; ivf.refineflag = 2; - ivf.refinesh = searchsh; + ivf.refinesh = *splitfac; + ivf.smlenflag = useinsertradius; // Update the insertion radius. + // Reject a near Steiner point on this subface when: + // - the insertion of the reject ccent is not due to mesh size (qflag). + if (!qflag) { + ivf.check_insert_radius = useinsertradius; + } + //if (is_adjacent) { + // ivf.parentpt = encpt; // This allows to insert a shorter edge. + //} else { + ivf.parentpt = NULL; // init + //} if (insertpoint(newpt, &searchtet, &searchsh, NULL, &ivf)) { st_facref_count++; if (steinerleft > 0) steinerleft--; if (useinsertradius) { - // Update 'rv' (to be the shortest distance). - rv = ivf.smlen; - if (pointtype(ivf.parentpt) == FREESEGVERTEX) { - face parentseg, parentsh; - sdecode(point2sh(ivf.parentpt), parentseg); - sdecode(point2sh(newpt), parentsh); - if (segfacetadjacent(&parentseg, &parentsh)) { - rp = getpointinsradius(ivf.parentpt); - if (rv < (sqrt(2.0) * rp)) { - rv = sqrt(2.0) * rp; // The relaxed insertion radius of 'newpt'. - } - } - } else if (pointtype(ivf.parentpt) == FREEFACETVERTEX) { - face parentsh1, parentsh2; - sdecode(point2sh(ivf.parentpt), parentsh1); - sdecode(point2sh(newpt), parentsh2); - if (facetfacetadjacent(&parentsh1, &parentsh2)) { - rp = getpointinsradius(ivf.parentpt); - if (rv < rp) { - rv = rp; // The relaxed insertion radius of 'newpt'. - } - } + REAL rv = 0.0; // param[3]; // emin, maybe zero. + + if (is_adjacent) { // if (encpt != NULL) { + // A sharp (dihedral) angle is involved. + // Insertion radius must be > 0. + double L = (ivf.smlen / 3.); + // Choose the larger one between param[3] and L + rv = (param[3] > L ? param[3] : L); } + setpointinsradius(newpt, rv); - } // if (useinsertradius) + setpoint2ppt(newpt, ivf.parentpt); + if (smallest_insradius > ivf.smlen) { + smallest_insradius = ivf.smlen; + } + } if (flipstack != NULL) { flipconstraints fc; - fc.chkencflag = chkencflag; + fc.chkencflag = (chkencflag & (~1)); //chkencflag; fc.enqflag = 2; lawsonflip3d(&fc); - unflipqueue->restart(); + //unflipqueue->restart(); } - return 1; - } else { - // Point was not inserted. - pointdealloc(newpt); - if (ivf.iloc == (int) ENCSEGMENT) { + + if (later_unflip_queue->objects > b->unflip_queue_limit) { + recoverdelaunay(); + } + + *iloc = ivf.iloc; + return true; + } + + // Point is not inserted. + pointdealloc(newpt); + + if (ivf.iloc == (int) ENCSEGMENT) { + // Bakup the split subface. + ppt = (point *) &(splitfac->sh[3]); + for (i = 0; i < 3; i++) bak_pts[i] = ppt[i]; + + bool ref_segment = ((b->cdtrefine & 1) > 0); // -D1, -D3, -D5, or -D7 + + if (ref_segment || qflag) { // Select an encroached segment and split it. - splitflag = 0; for (i = 0; i < encseglist->objects; i++) { - paryseg = (face *) fastlookup(encseglist, i); - if (splitsegment(paryseg, NULL, rv, encpt, encpt1, qflag, - chkencflag | 1)) { - splitflag = 1; // A point is inserted on a segment. - break; + //face *paryseg = (face *) fastlookup(encseglist, i); + badface *bf = (badface *) fastlookup(encseglist, i); + if ((bf->ss.sh == NULL) || + (sorg(bf->ss) != bf->forg) || + (sdest(bf->ss) != bf->fdest)) continue; // Skip this segment. + int tmp_iloc; + if (split_segment(&(bf->ss), NULL, param, qflag, (chkencflag | 1), &tmp_iloc)) { + // A Steiner point is inserted on an encroached segment. + // Check if this subface is split as well. + if ((splitfac->sh == NULL) || (splitfac->sh[3] == NULL)) { + splitflag = true; break; + } else { + ppt = (point *) &(splitfac->sh[3]); + if ((ppt[0] != bak_pts[0]) || + (ppt[1] != bak_pts[1]) || + (ppt[2] != bak_pts[2])) { + splitflag = true; break; + } + } } } - encseglist->restart(); - if (splitflag) { - // Some segments may need to be repaired. - repairencsegs(chkencflag | 1); - // Queue this subface if it is still alive and not queued. - //if ((splitfac->sh != NULL) && (splitfac->sh[3] != NULL)) { - // // Only queue it if 'qflag' is set. - // if (qflag) { - // enqueuesubface(badsubfacs, splitfac); - // } - //} - } - return splitflag; + } // if (ref_segment) + encseglist->restart(); + // Some segments may be encroached. + if (badsubsegs->items > 0) { + //repairencsegs(param, qflag, (chkencflag | 1)); + repairencsegs(param, 0, (chkencflag | 1)); // qflag = 0 + } + // Check if this subface is split as well. + if ((splitfac->sh == NULL) || (splitfac->sh[3] == NULL)) { + splitflag = true; } else { - return 0; + ppt = (point *) &(splitfac->sh[3]); + if ((ppt[0] != bak_pts[0]) || + (ppt[1] != bak_pts[1]) || + (ppt[2] != bak_pts[2])) { + splitflag = true; + } } + } else if (ivf.iloc == (int) NEARVERTEX) { + terminatetetgen(this, 2); // report a bug } + + *iloc = ivf.iloc; + return splitflag; } -/////////////////////////////////////////////////////////////////////////////// -// // -// repairencfacs() Repair encroached subfaces. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// repairencfacs() Repair encroached subfaces. // +// // +//============================================================================// -void tetgenmesh::repairencfacs(int chkencflag) +void tetgenmesh::repairencfacs(REAL *param, int qflag, int chkencflag) { - face *bface; point encpt = NULL; - int qflag = 0; - REAL ccent[3]; + REAL ccent[3], radius; //, param[6] = {0.,}; + int split_count = 0, rej_count = 0; + //int qflag = 0; + int i; - // Loop until the pool 'badsubfacs' is empty. Note that steinerleft == -1 - // if an unlimited number of Steiner points is allowed. - while ((badsubfacs->items > 0) && (steinerleft != 0)) { - badsubfacs->traversalinit(); - bface = (face *) badsubfacs->traverse(); - while ((bface != NULL) && (steinerleft != 0)) { - // Skip a deleted element. - if (bface->shver >= 0) { + bool ref_subface = ((b->cdtrefine & 2) > 0); // -D2, -D3, -D6, -D7 + + // This function may be called from split_tetrahedron(). In this case, the + // insertion radius of the rejected circumcenter is stored in param[3]. + // The check_subface() will return the insertion radius of the circumcenter + // of a bad quality subface also in param[3]. + REAL tet_emin = param[3]; + + while (ref_subface && + ((badsubfacs->items > 0) || (split_subfaces_pool->items > 0))) { + + if (badsubfacs->items > 0) { + badsubfacs->traversalinit(); + face *bface = (face *) badsubfacs->traverse(); + while (bface != NULL) { // A queued subface may have been deleted (split). if ((bface->sh != NULL) && (bface->sh[3] != NULL)) { - // A queued subface may have been processed. + // A queued subface may have been processed. if (smarktest2ed(*bface)) { sunmarktest2(*bface); - if (checkfac4split(bface, encpt, qflag, ccent)) { - splitsubface(bface, encpt, NULL, qflag, ccent, chkencflag); + for (i = 3; i < 6; i++) param[i] = 0.; // Clear previous values. + if (get_subface_ccent(bface, ccent)) { + encpt = NULL; + if (check_enc_subface(bface, &encpt, ccent, &radius)) { + param[3] = tet_emin; // maybe zero. + enqueue_subface(bface, encpt, ccent, param); + } else { + if (check_subface(bface, ccent, radius, param)) { + if (tet_emin > 0) { + // Use the larger one. + param[3] = (param[3] > tet_emin ? param[3] : tet_emin); + } + enqueue_subface(bface, NULL, ccent, param); + } + } + } else { + // report a bug. + terminatetetgen(this, 2); } } } - bface->shver = -1; // Signal it as a deleted element. - badsubfacs->dealloc((void *) bface); // Remove this entry from list. + bface = (face *) badsubfacs->traverse(); + } // while (bface != NULL) + + badsubfacs->restart(); // clear this pool + + // check_enc_subface() may find some non-Delaunay subfaces. + if (flippool->items > 0) { + flipconstraints fc; + fc.chkencflag = chkencflag; + fc.enqflag = 2; + lawsonflip3d(&fc); + } + } // if (badsubfacs->items > 0) + + if (split_subfaces_pool->items == 0) break; + + // Stop if we have used the desried number of Steiner points. + if (steinerleft == 0) break; + // Stop if the desried number of tetrahedra is reached. + if ((elem_limit > 0) && + ((tetrahedrons->items - hullsize) > elem_limit)) break; + + + badface *bf = top_subface(); + + if ((bf->ss.sh != NULL) && + ( sorg(bf->ss) == bf->forg) && + (sdest(bf->ss) == bf->fdest) && + (sapex(bf->ss) == bf->fapex)) { + // Try to split this subface. + encpt = bf->noppo; // The encroaching vertex. + for (i = 0; i < 3; i++) ccent[i] = bf->cent[i]; + for (i = 3; i < 6; i++) param[i] = bf->cent[i]; + split_count++; + + int iloc = (int) UNKNOWN; + if (!split_subface(&bf->ss, encpt, ccent, param, qflag, chkencflag, &iloc)) { + rej_count++; + if (qflag || ((param[4] > (3. * b->minratio)) && (iloc != SHARPCORNER))) { + // Queue a unsplit (bad quality) subface. + badface *bt = NULL; + unsplit_subfaces->newindex((void **) &bt); + //bt->init(); + *bt = *bf; + } } - bface = (face *) badsubfacs->traverse(); } + dequeue_subface(); + } // while ((badsubfacs->items > 0) || (split_subfaces_pool->items > 0)) + + if (b->verbose > 3) { + printf(" Tried to split %d subfaces, %d were rejected.\n", + split_count, rej_count); } + param[3] = tet_emin; // Restore this value. if (badsubfacs->items > 0) { + // Clean this list (due to the ref_subface flag) + badsubfacs->traversalinit(); + face *bface = (face *) badsubfacs->traverse(); + while (bface != NULL) { + // A queued subface may have been deleted (split). + if ((bface->sh != NULL) && (bface->sh[3] != NULL)) { + // A queued subface may have been processed. + if (smarktest2ed(*bface)) { + sunmarktest2(*bface); + } + } + bface = (face *) badsubfacs->traverse(); + } // while (bface != NULL) + badsubfacs->restart(); // clear this pool + } // if (badsubfacs->items > 0) + + if (split_subfaces_pool->items > 0) { if (steinerleft == 0) { if (b->verbose) { printf("The desired number of Steiner points is reached.\n"); } - } else { - assert(0); // Unknown case. - } - badsubfacs->traversalinit(); - bface = (face *) badsubfacs->traverse(); - while (bface != NULL) { - // Skip a deleted element. - if (bface->shver >= 0) { - if ((bface->sh != NULL) && (bface->sh[3] != NULL)) { - if (smarktest2ed(*bface)) { - sunmarktest2(*bface); - } - } + } else if (elem_limit > 0) { + if (b->verbose) { + printf("The desired number %ld of elements is reached.\n", elem_limit); } - bface = (face *) badsubfacs->traverse(); } - badsubfacs->restart(); + split_subfaces_pool->restart(); // Clear this pool. + unsplit_subfaces->restart(); + stack_enc_subfaces = NULL; } } -/////////////////////////////////////////////////////////////////////////////// -// // -// enqueuetetrahedron() Queue a tetrahedron for quality check. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::enqueuetetrahedron(triface *chktet) +//============================================================================// +// // +// check_tetrahedron() Check if the tet needs to be split. // +// // +// "param[6]" returns the following data: // +// [0],[1],[2] - the location of the new point // +// [3] - the samllest edge length ( = insertion radius) // +// [4] - the radius-edge ratio // +// [5] - (optional) edge ratio // +// // +// "chktet" returns the shortest edge of this tet. // +// // +//============================================================================// + +bool tetgenmesh::check_tetrahedron(triface *chktet, REAL* param, int &qflag) { - if (!marktest2ed(*chktet)) { - marktest2(*chktet); // Only queue it once. - triface *quetet = (triface *) badtetrahedrons->alloc(); - *quetet = *chktet; + point pd = (point) chktet->tet[7]; + if (pd == dummypoint) { + return false; // Do not split a hull tet. + } + + point pa = (point) chktet->tet[4]; + point pb = (point) chktet->tet[5]; + point pc = (point) chktet->tet[6]; + + + REAL D = orient3dexact(pa, pb, pc, pd); // =6*vol + + if (D >= 0.0) { + // A degenerated tetrahedron. + terminatetetgen(this, 2); + } + + qflag = 0; // default + + REAL elen[6]; + REAL vol = -D / 6.0; + REAL emin = 0., ratio = 0.; + + // Calculate the circumcenter of this tet. + point P = pa, Q = pb, R = pc, S = pd; + + REAL U[3], V[3], W[3], Z[3]; // variables. + + REAL hp = P[0]*P[0] + P[1]*P[1] + P[2]*P[2]; // - wp + REAL hq = Q[0]*Q[0] + Q[1]*Q[1] + Q[2]*Q[2]; // - wq + REAL hr = R[0]*R[0] + R[1]*R[1] + R[2]*R[2]; // - wr + REAL hs = S[0]*S[0] + S[1]*S[1] + S[2]*S[2]; // - wr + + U[0] = hp; U[1] = P[1]; U[2] = P[2]; + V[0] = hq; V[1] = Q[1]; V[2] = Q[2]; + W[0] = hr; W[1] = R[1]; W[2] = R[2]; + Z[0] = hs; Z[1] = S[1]; Z[2] = S[2]; + + REAL D1 = orient3d(U, V, W, Z); + + U[0] = P[0]; U[1] = hp; //U[2] = P[2]; + V[0] = Q[0]; V[1] = hq; //V[2] = Q[2]; + W[0] = R[0]; W[1] = hr; //W[2] = R[2]; + Z[0] = S[0]; Z[1] = hs; //Z[2] = S[2]; + + REAL D2 = orient3d(U, V, W, Z); + + /*U[0] = P[0];*/ U[1] = P[1]; U[2] = hp; + /*V[0] = Q[0];*/ V[1] = Q[1]; V[2] = hq; + /*W[0] = R[0];*/ W[1] = R[1]; W[2] = hr; + /*Z[0] = S[0];*/ Z[1] = S[1]; Z[2] = hs; + + REAL D3 = orient3d(U, V, W, Z); + + REAL DD = D * 2.; + + param[0] = D1 / DD; + param[1] = D2 / DD; + param[2] = D3 / DD; + + + param[4] = 1.0; // default a good ratio. + param[5] = vol; + + elen[0] = distance2(pc, pd); + elen[1] = distance2(pd, pa); + elen[2] = distance2(pa, pb); + elen[3] = distance2(pb, pc); + elen[4] = distance2(pb, pd); + elen[5] = distance2(pa, pc); + + // Find the shortest edge. + emin = elen[0]; + int eidx = 0; + for (int i = 1; i < 6; i++) { + if (emin > elen[i]) { + emin = elen[i]; eidx = i; + } + } + emin = sqrt(emin); + // Let chktet be the shortest edge in this tet. + chktet->ver = edge2ver[eidx]; + + // check mesh size (qflag). + if (b->varvolume || b->fixedvolume) { // -a# + if (b->fixedvolume) { + if (vol > b->maxvolume) { + // set the insertion radius, use the smaller one between the + // smallest edge length of this tet and mesh size; + emin = (emin < b->maxvolume_length ? emin : b->maxvolume_length); + qflag = 1; + } + } + if (!qflag && b->varvolume) { + REAL volbnd = volumebound(chktet->tet); + if ((volbnd > 0.0) && (vol > volbnd)) { + // set the insertion radius; + REAL msize = pow(volbnd, 1./3.) / 3.; + emin = (emin < msize ? emin : msize); + qflag = 1; + } + } + } // -a# + + if (!qflag && b->metric) { // -m + //int eidx = 0; + for (int i = 0; i < 6; i++) { + elen[i] = sqrt(elen[i]); + } + if (pa[pointmtrindex] > 0) { + // Get the longest edge {pa, pd}, {pa, pb}, {pa, pc} + REAL maxelen = elen[1]; //eidx = 1; + if (maxelen < elen[2]) {maxelen = elen[2]; /*eidx = 2;*/} + if (maxelen < elen[5]) {maxelen = elen[5]; /*eidx = 5;*/} + maxelen /= 2.0; + if (maxelen > pa[pointmtrindex]) { + emin = (emin < pa[pointmtrindex] ? emin : pa[pointmtrindex]); + //emax = maxelen; + qflag = 1; + } + } + if (!qflag && (pb[pointmtrindex] > 0)) { + // Get the longest edge at pb. + REAL maxelen = elen[2]; //eidx = 2; + if (maxelen < elen[3]) {maxelen = elen[3]; /*eidx = 3;*/} + if (maxelen < elen[4]) {maxelen = elen[4]; /*eidx = 4;*/} + maxelen /= 2.0; + if (maxelen > pb[pointmtrindex]) { + emin = (emin < pb[pointmtrindex] ? emin : pb[pointmtrindex]); + //emax = maxelen; + qflag = 1; + } + } + if (!qflag && (pc[pointmtrindex] > 0)) { + // Get the longest edge at pc. + REAL maxelen = elen[0]; //eidx = 0; + if (maxelen < elen[3]) {maxelen = elen[3]; /*eidx = 3;*/} + if (maxelen < elen[5]) {maxelen = elen[5]; /*eidx = 5;*/} + maxelen /= 2.0; + if (maxelen > pc[pointmtrindex]) { + emin = (emin < pc[pointmtrindex] ? emin : pc[pointmtrindex]); + //emax = maxelen; + qflag = 1; + } + } + if (!qflag && (pd[pointmtrindex] > 0)) { + // Get the longest edge at pd. + REAL maxelen = elen[0]; //eidx = 0; + if (maxelen < elen[1]) {maxelen = elen[1]; /*eidx = 1;*/} + if (maxelen < elen[4]) {maxelen = elen[4]; /*eidx = 4;*/} + maxelen /= 2.0; + if (maxelen > pd[pointmtrindex]) { + emin = (emin < pd[pointmtrindex] ? emin : pd[pointmtrindex]); + //emax = maxelen; + qflag = 1; + } + } + } // if (!qflag && b->metric) // -m + + if (qflag) { + param[3] = emin; // The desired mesh size. + //param[4] = 1.0; // ratio; // = 0. + //param[5] = vol; + return true; + } + + if (b->minratio > 1.0) { + REAL radius = distance(param, pa); + + ratio = radius / emin; + + + if (ratio > b->minratio) { + //qflag = 0; + // The smallest insertion radius should be at least larger than + // the smallest edge length (==> graded mesh size). + point pa = org(*chktet); + point pb = dest(*chktet); + REAL ra = getpointinsradius(pa); + REAL rb = getpointinsradius(pb); + if ((ra > 0.) && (ra > emin)) { + emin = ra; // the relaxed (enlarged) insertion radius. + } + if ((rb > 0.) && (rb > emin)) { + emin = rb; // the relaxed (enlarged) insertion radius. + } + + param[3] = emin; // (emin * b->minratio); + param[4] = ratio; + //param[5] = vol; + return true; + } } + + return false; // no need to split this tetrahedron. } -/////////////////////////////////////////////////////////////////////////////// -// // -// checktet4split() Check if the tet needs to be split. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// checktet4split() Check if a given tet has a bad shape. // +// // +//============================================================================// -int tetgenmesh::checktet4split(triface *chktet, int &qflag, REAL *ccent) +bool tetgenmesh::checktet4split(triface *chktet, REAL* param, int& qflag) { point pa, pb, pc, pd, *ppt; REAL vda[3], vdb[3], vdc[3]; REAL vab[3], vbc[3], vca[3]; REAL N[4][3], L[4], cosd[6], elen[6]; - REAL maxcosd, vol, volbnd, smlen = 0, rd; + REAL maxcosd, vol, volbnd, rd, Lmax, Lmin; REAL A[4][4], rhs[4], D; int indx[4]; int i, j; @@ -24945,11 +28321,12 @@ int tetgenmesh::checktet4split(triface *chktet, int &qflag, REAL *ccent) if (b->convex) { // -c // Skip this tet if it lies in the exterior. if (elemattribute(chktet->tet, numelemattrib - 1) == -1.0) { - return 0; + return 0; } } qflag = 0; + for (i = 0; i < 6; i++) param[i] = 0.; pd = (point) chktet->tet[7]; if (pd == dummypoint) { @@ -24960,24 +28337,38 @@ int tetgenmesh::checktet4split(triface *chktet, int &qflag, REAL *ccent) pb = (point) chktet->tet[5]; pc = (point) chktet->tet[6]; + // Get the edge vectors vda: d->a, vdb: d->b, vdc: d->c. // Set the matrix A = [vda, vdb, vdc]^T. for (i = 0; i < 3; i++) A[0][i] = vda[i] = pa[i] - pd[i]; for (i = 0; i < 3; i++) A[1][i] = vdb[i] = pb[i] - pd[i]; for (i = 0; i < 3; i++) A[2][i] = vdc[i] = pc[i] - pd[i]; - + // Get the other edge vectors. for (i = 0; i < 3; i++) vab[i] = pb[i] - pa[i]; for (i = 0; i < 3; i++) vbc[i] = pc[i] - pb[i]; for (i = 0; i < 3; i++) vca[i] = pa[i] - pc[i]; if (!lu_decmp(A, 3, indx, &D, 0)) { - // A degenerated tet (vol = 0). - // This is possible due to the use of exact arithmetic. We temporarily - // leave this tet. It should be fixed by mesh optimization. - return 0; + // Is it a degenerated tet (vol = 0). + REAL D = orient3dexact(pa, pb, pc, pd); // =6*vol + if (D >= 0.0) { + // A degenerated tetrahedron. + terminatetetgen(this, 2); + } + // We temporarily leave this tet. It should be fixed by mesh improvement. + return false; } + // Calculate the circumcenter and radius of this tet. + rhs[0] = 0.5 * dot(vda, vda); + rhs[1] = 0.5 * dot(vdb, vdb); + rhs[2] = 0.5 * dot(vdc, vdc); + lu_solve(A, 3, indx, rhs, 0); + + for (i = 0; i < 3; i++) param[i] = pd[i] + rhs[i]; + rd = sqrt(dot(rhs, rhs)); + // Check volume if '-a#' and '-a' options are used. if (b->varvolume || b->fixedvolume) { vol = fabs(A[indx[0]][0] * A[indx[1]][1] * A[indx[2]][2]) / 6.0; @@ -24985,7 +28376,7 @@ int tetgenmesh::checktet4split(triface *chktet, int &qflag, REAL *ccent) if (vol > b->maxvolume) { qflag = 1; } - } + } if (!qflag && b->varvolume) { volbnd = volumebound(chktet->tet); if ((volbnd > 0.0) && (vol > volbnd)) { @@ -24993,31 +28384,18 @@ int tetgenmesh::checktet4split(triface *chktet, int &qflag, REAL *ccent) } } if (qflag == 1) { - // Calculate the circumcenter of this tet. - rhs[0] = 0.5 * dot(vda, vda); - rhs[1] = 0.5 * dot(vdb, vdb); - rhs[2] = 0.5 * dot(vdc, vdc); - lu_solve(A, 3, indx, rhs, 0); - for (i = 0; i < 3; i++) ccent[i] = pd[i] + rhs[i]; - return 1; + return true; } } - if (b->metric) { // -m option. Check mesh size. - // Calculate the circumradius of this tet. - rhs[0] = 0.5 * dot(vda, vda); - rhs[1] = 0.5 * dot(vdb, vdb); - rhs[2] = 0.5 * dot(vdc, vdc); - lu_solve(A, 3, indx, rhs, 0); - for (i = 0; i < 3; i++) ccent[i] = pd[i] + rhs[i]; - rd = sqrt(dot(rhs, rhs)); + if (b->metric) { // -m option. Check mesh size. // Check if the ccent lies outside one of the prot.balls at vertices. ppt = (point *) &(chktet->tet[4]); for (i = 0; i < 4; i++) { if (ppt[i][pointmtrindex] > 0) { if (rd > ppt[i][pointmtrindex]) { qflag = 1; // Enforce mesh size. - return 1; + return true; } } } @@ -25026,102 +28404,46 @@ int tetgenmesh::checktet4split(triface *chktet, int &qflag, REAL *ccent) if (in->tetunsuitable != NULL) { // Execute the user-defined meshing sizing evaluation. if ((*(in->tetunsuitable))(pa, pb, pc, pd, NULL, 0)) { - // Calculate the circumcenter of this tet. - rhs[0] = 0.5 * dot(vda, vda); - rhs[1] = 0.5 * dot(vdb, vdb); - rhs[2] = 0.5 * dot(vdc, vdc); - lu_solve(A, 3, indx, rhs, 0); - for (i = 0; i < 3; i++) ccent[i] = pd[i] + rhs[i]; - return 1; + return true; } } - if (useinsertradius) { - // Do not split this tet if the shortest edge is shorter than the - // insertion radius of one of its endpoints. - triface checkedge; - point e1, e2; - REAL rrv, smrrv; - // Get the shortest edge of this tet. - checkedge.tet = chktet->tet; - for (i = 0; i < 6; i++) { - checkedge.ver = edge2ver[i]; - e1 = org(checkedge); - e2 = dest(checkedge); - elen[i] = distance(e1, e2); - if (i == 0) { - smlen = elen[i]; - j = 0; - } else { - if (elen[i] < smlen) { - smlen = elen[i]; - j = i; - } - } - } - // Check if the edge is too short. - checkedge.ver = edge2ver[j]; - // Get the smallest rrv of e1 and e2. - // Note: if rrv of e1 and e2 is zero. Do not use it. - e1 = org(checkedge); - smrrv = getpointinsradius(e1); - e2 = dest(checkedge); - rrv = getpointinsradius(e2); - if (rrv > 0) { - if (smrrv > 0) { - if (rrv < smrrv) { - smrrv = rrv; - } - } else { - smrrv = rrv; - } - } - if (smrrv > 0) { - // To avoid rounding error, round smrrv before doing comparison. - if ((fabs(smrrv - smlen) / smlen) < b->epsilon) { - smrrv = smlen; - } - if (smrrv > smlen) { - return 0; + // Check the radius-edge ratio. Set by -q#. + if (b->minratio > 0) { + elen[0] = dot(vdc, vdc); + elen[1] = dot(vda, vda); + elen[2] = dot(vab, vab); + elen[3] = dot(vbc, vbc); + elen[4] = dot(vdb, vdb); + elen[5] = dot(vca, vca); + + Lmax = Lmin = elen[0]; + int eidx = 0; + for (i = 1; i < 6; i++) { + Lmax = (Lmax < elen[i] ? elen[i] : Lmax); + //Lmin = (Lmin > elen[i] ? elen[i] : Lmin); + if (Lmin > elen[i]) { + Lmin = elen[i]; eidx = i; } } - } // if (useinsertradius) + // Let chktet be the shortest edge in this tet. + chktet->ver = edge2ver[eidx]; - // Check the radius-edge ratio. Set by -q#. - if (b->minratio > 0) { - // Calculate the circumcenter and radius of this tet. - rhs[0] = 0.5 * dot(vda, vda); - rhs[1] = 0.5 * dot(vdb, vdb); - rhs[2] = 0.5 * dot(vdc, vdc); - lu_solve(A, 3, indx, rhs, 0); - for (i = 0; i < 3; i++) ccent[i] = pd[i] + rhs[i]; - rd = sqrt(dot(rhs, rhs)); - if (!useinsertradius) { - // Calculate the shortest edge length. - elen[0] = dot(vda, vda); - elen[1] = dot(vdb, vdb); - elen[2] = dot(vdc, vdc); - elen[3] = dot(vab, vab); - elen[4] = dot(vbc, vbc); - elen[5] = dot(vca, vca); - smlen = elen[0]; //sidx = 0; - for (i = 1; i < 6; i++) { - if (smlen > elen[i]) { - smlen = elen[i]; //sidx = i; - } - } - smlen = sqrt(smlen); - } - D = rd / smlen; + //Lmax = sqrt(Lmax); + Lmin = sqrt(Lmin); + D = rd / Lmin; if (D > b->minratio) { // A bad radius-edge ratio. - return 1; + param[3] = Lmin; + param[4] = D; + param[5] = sqrt(Lmax) / Lmin; // edge ratio. + return true; } - } + } // if (b->minratio > 0) - // Check the minimum dihedral angle. Set by -qq#. - if (b->mindihedral > 0) { + // Check the minimum dihedral angle. Set by -q/#. + if (b->mindihedral > 0) { // Compute the 4 face normals (N[0], ..., N[3]). for (j = 0; j < 3; j++) { for (i = 0; i < 3; i++) N[j][i] = 0.0; @@ -25132,10 +28454,10 @@ int tetgenmesh::checktet4split(triface *chktet, int &qflag, REAL *ccent) // Normalize the normals. for (i = 0; i < 4; i++) { L[i] = sqrt(dot(N[i], N[i])); - assert(L[i] > 0); - //if (L[i] > 0.0) { - for (j = 0; j < 3; j++) N[i][j] /= L[i]; - //} + if (L[i] == 0) { + terminatetetgen(this, 2); + } + for (j = 0; j < 3; j++) N[i][j] /= L[i]; } // Calculate the six dihedral angles. cosd[0] = -dot(N[0], N[1]); // Edge cd, bd, bc. @@ -25153,214 +28475,651 @@ int tetgenmesh::checktet4split(triface *chktet, int &qflag, REAL *ccent) //mincosd = (cosd[i] < mincosd ? cosd[i] : maxcosd); } if (maxcosd > cosmindihed) { - // Calculate the circumcenter of this tet. // A bad dihedral angle. - //if ((b->quality & 1) == 0) { - rhs[0] = 0.5 * dot(vda, vda); - rhs[1] = 0.5 * dot(vdb, vdb); - rhs[2] = 0.5 * dot(vdc, vdc); - lu_solve(A, 3, indx, rhs, 0); - for (i = 0; i < 3; i++) ccent[i] = pd[i] + rhs[i]; - //*rd = sqrt(dot(rhs, rhs)); - //} - return 1; + return true; } - } + } // if (b->mindihedral > 0) return 0; } -/////////////////////////////////////////////////////////////////////////////// -// // -// splittetrahedron() Split a tetrahedron. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// locate_point_walk() Locate a point by line searching. // +// // +//============================================================================// -int tetgenmesh::splittetrahedron(triface* splittet, int qflag, REAL *ccent, - int chkencflag) +enum tetgenmesh::locateresult + tetgenmesh::locate_point_walk(point searchpt, triface* searchtet, int chkencflag) { - triface searchtet; - face *paryseg; - point newpt; - badface *bface; - insertvertexflags ivf; - int splitflag; - int i; - - - - REAL rv = 0.; // Insertion radius of 'newpt'. + // Construct the starting point to be the barycenter of 'searchtet'. + REAL startpt[3]; + point *ppt = (point *) &(searchtet->tet[4]); + for (int i = 0; i < 3; i++) { + startpt[i] = (ppt[0][i] + ppt[1][i] + ppt[2][i] + ppt[3][i]) / 4.; + } - makepoint(&newpt, FREEVOLVERTEX); - for (i = 0; i < 3; i++) newpt[i] = ccent[i]; + point torg, tdest, tapex, toppo; + REAL ori, oriorg, oridest, oriapex; + enum locateresult loc = OUTSIDE; + enum {ORGMOVE, DESTMOVE, APEXMOVE} nextmove; - if (useinsertradius) { - rv = distance(newpt, org(*splittet)); - setpointinsradius(newpt, rv); + for (searchtet->ver = 0; searchtet->ver < 4; searchtet->ver++) { + torg = org(*searchtet); + tdest = dest(*searchtet); + tapex = apex(*searchtet); + ori = orient3d(torg, tdest, tapex, searchpt); + if (ori < 0) break; } - searchtet = *splittet; - ivf.iloc = (int) OUTSIDE; - // Use Bowyer-Watson algorithm. Preserve subsegments and subfaces; - ivf.bowywat = 3; - ivf.lawson = 2; - ivf.rejflag = 3; // Do check for encroached segments and subfaces. - if (b->metric) { - ivf.rejflag |= 4; // Reject it if it lies in some protecting balls. + if (searchtet->ver == 4) { + terminatetetgen(this, 2); } - ivf.chkencflag = chkencflag; - ivf.sloc = ivf.sbowywat = 0; // No use. - ivf.splitbdflag = 0; // No use. - ivf.validflag = 1; - ivf.respectbdflag = 1; - ivf.assignmeshsize = b->metric; - - ivf.refineflag = 1; - ivf.refinetet = *splittet; + int max_visited_tets = 10000; // tetrahedrons->items; + // Walk through tetrahedra to locate the point. + while (max_visited_tets > 0) { + toppo = oppo(*searchtet); - if (insertpoint(newpt, &searchtet, NULL, NULL, &ivf)) { - // Vertex is inserted. - st_volref_count++; - if (steinerleft > 0) steinerleft--; - if (flipstack != NULL) { - flipconstraints fc; - fc.chkencflag = chkencflag; - fc.enqflag = 2; - lawsonflip3d(&fc); - unflipqueue->restart(); + // Check if the vertex is we seek. + if (toppo == searchpt) { + // Adjust the origin of searchtet to be searchpt. + esymself(*searchtet); + eprevself(*searchtet); + loc = ONVERTEX; // return ONVERTEX; + break; } - return 1; - } else { - // Point is not inserted. - pointdealloc(newpt); - // Check if there are encroached segments/subfaces. - if (ivf.iloc == (int) ENCSEGMENT) { - splitflag = 0; - //if (!b->nobisect) { // not -Y option - if (!b->nobisect || checkconstraints) { - // Select an encroached segment and split it. - for (i = 0; i < encseglist->objects; i++) { - paryseg = (face *) fastlookup(encseglist, i); - if (splitsegment(paryseg, NULL, rv, org(*splittet), NULL, qflag, - chkencflag | 3)) { - splitflag = 1; // A point is inserted on a segment. - break; + + // We enter from the crruent face of `serarchtet', which face do we exit? + // Find the next face which is intersect with the line (startpt->searchpt). + oriorg = orient3d(tdest, tapex, toppo, searchpt); + oridest = orient3d(tapex, torg, toppo, searchpt); + oriapex = orient3d( torg, tdest, toppo, searchpt); + + if (oriorg < 0) { + if (oridest < 0) { + if (oriapex < 0) { + // All three faces are possible. + if (tri_edge_test(tdest,tapex,toppo,startpt,searchpt,NULL,0,NULL,NULL)) { + nextmove = ORGMOVE; + } else if (tri_edge_test(tapex,torg,toppo,startpt,searchpt,NULL,0,NULL,NULL)) { + nextmove = DESTMOVE; + } else if (tri_edge_test(torg,tdest,toppo,startpt,searchpt,NULL,0,NULL,NULL)) { + nextmove = APEXMOVE; + } else { + int s = randomnation(3); // 's' is in {0,1,2}. + if (s == 0) { + nextmove = ORGMOVE; + } else if (s == 1) { + nextmove = DESTMOVE; + } else { + nextmove = APEXMOVE; + } } - } - } // if (!b->nobisect) - encseglist->restart(); - if (splitflag) { - // Some segments may need to be repaired. - repairencsegs(chkencflag | 3); - // Some subfaces may need to be repaired. - repairencfacs(chkencflag | 2); - // Queue the tet if it is still alive and not queued. - if ((splittet->tet != NULL) && (splittet->tet[4] != NULL)) { - enqueuetetrahedron(splittet); - } - } - return splitflag; - } else if (ivf.iloc == (int) ENCSUBFACE) { - splitflag = 0; - //if (!b->nobisect) { // not -Y option - if (!b->nobisect || checkconstraints) { - // Select an encroached subface and split it. - for (i = 0; i < encshlist->objects; i++) { - bface = (badface *) fastlookup(encshlist, i); - if (splitsubface(&(bface->ss), NULL, org(*splittet), qflag, - bface->cent, chkencflag | 2)){ - splitflag = 1; // A point is inserted on a subface or a segment. - break; + } else { + // Two faces, opposite to origin and destination, are viable. + if (tri_edge_test(tdest,tapex,toppo,startpt,searchpt,NULL,0,NULL,NULL)) { + nextmove = ORGMOVE; + } else if (tri_edge_test(tapex,torg,toppo,startpt,searchpt,NULL,0,NULL,NULL)) { + nextmove = DESTMOVE; + } else { + //s = randomnation(2); // 's' is in {0,1}. + if (randomnation(2)) { + nextmove = ORGMOVE; + } else { + nextmove = DESTMOVE; + } } } - } // if (!b->nobisect) - encshlist->restart(); - if (splitflag) { - assert(badsubsegs->items == 0l); - // Some subfaces may need to be repaired. - repairencfacs(chkencflag | 2); - // Queue the tet if it is still alive. - if ((splittet->tet != NULL) && (splittet->tet[4] != NULL)) { - enqueuetetrahedron(splittet); - } - } - return splitflag; - } - return 0; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// repairbadtets() Repair bad quality tetrahedra. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::repairbadtets(int chkencflag) -{ - triface *bface; - REAL ccent[3]; - int qflag = 0; - - - // Loop until the pool 'badsubfacs' is empty. Note that steinerleft == -1 - // if an unlimited number of Steiner points is allowed. - while ((badtetrahedrons->items > 0) && (steinerleft != 0)) { - badtetrahedrons->traversalinit(); - bface = (triface *) badtetrahedrons->traverse(); - while ((bface != NULL) && (steinerleft != 0)) { - // Skip a deleted element. - if (bface->ver >= 0) { - // A queued tet may have been deleted. - if (!isdeadtet(*bface)) { - // A queued tet may have been processed. - if (marktest2ed(*bface)) { - unmarktest2(*bface); - if (checktet4split(bface, qflag, ccent)) { - splittetrahedron(bface, qflag, ccent, chkencflag); + } else { + if (oriapex < 0) { + // Two faces, opposite to origin and apex, are viable. + if (tri_edge_test(tdest,tapex,toppo,startpt,searchpt,NULL,0,NULL,NULL)) { + nextmove = ORGMOVE; + } else if (tri_edge_test(torg,tdest,toppo,startpt,searchpt,NULL,0,NULL,NULL)) { + nextmove = APEXMOVE; + } else { + //s = randomnation(2); // 's' is in {0,1}. + if (randomnation(2)) { + nextmove = ORGMOVE; + } else { + nextmove = APEXMOVE; } } + } else { + // Only the face opposite to origin is viable. + nextmove = ORGMOVE; } - bface->ver = -1; // Signal it as a deleted element. - badtetrahedrons->dealloc((void *) bface); - } - bface = (triface *) badtetrahedrons->traverse(); - } - } - - if (badtetrahedrons->items > 0) { - if (steinerleft == 0) { - if (b->verbose) { - printf("The desired number of Steiner points is reached.\n"); } } else { - assert(0); // Unknown case. - } - // Unmark all queued tet. - badtetrahedrons->traversalinit(); - bface = (triface *) badtetrahedrons->traverse(); - while (bface != NULL) { - // Skip a deleted element. - if (bface->ver >= 0) { - if (!isdeadtet(*bface)) { - if (marktest2ed(*bface)) { - unmarktest2(*bface); + if (oridest < 0) { + if (oriapex < 0) { + // Two faces, opposite to destination and apex, are viable. + if (tri_edge_test(tapex,torg,toppo,startpt,searchpt,NULL,0,NULL,NULL)) { + nextmove = DESTMOVE; + } else if (tri_edge_test(torg,tdest,toppo,startpt,searchpt,NULL,0,NULL,NULL)) { + nextmove = APEXMOVE; + } else { + //s = randomnation(2); // 's' is in {0,1}. + if (randomnation(2)) { + nextmove = DESTMOVE; + } else { + nextmove = APEXMOVE; + } } + } else { + // Only the face opposite to destination is viable. + nextmove = DESTMOVE; } - } - bface = (triface *) badtetrahedrons->traverse(); - } - // Clear the pool. - badtetrahedrons->restart(); + } else { + if (oriapex < 0) { + // Only the face opposite to apex is viable. + nextmove = APEXMOVE; + } else { + // The point we seek must be on the boundary of or inside this + // tetrahedron. Check for boundary cases. + if (oriorg == 0) { + // Go to the face opposite to origin. + enextesymself(*searchtet); + if (oridest == 0) { + eprevself(*searchtet); // edge oppo->apex + if (oriapex == 0) { + // oppo is duplicated with p. + loc = ONVERTEX; // return ONVERTEX; + break; + } + loc = ONEDGE; // return ONEDGE; + break; + } + if (oriapex == 0) { + enextself(*searchtet); // edge dest->oppo + loc = ONEDGE; // return ONEDGE; + break; + } + loc = ONFACE; // return ONFACE; + break; + } + if (oridest == 0) { + // Go to the face opposite to destination. + eprevesymself(*searchtet); + if (oriapex == 0) { + eprevself(*searchtet); // edge oppo->org + loc = ONEDGE; // return ONEDGE; + break; + } + loc = ONFACE; // return ONFACE; + break; + } + if (oriapex == 0) { + // Go to the face opposite to apex + esymself(*searchtet); + loc = ONFACE; // return ONFACE; + break; + } + loc = INTETRAHEDRON; // return INTETRAHEDRON; + break; + } + } + } + + // Move to the selected face. + if (nextmove == ORGMOVE) { + enextesymself(*searchtet); + } else if (nextmove == DESTMOVE) { + eprevesymself(*searchtet); + } else { + esymself(*searchtet); + } + if (chkencflag) { + // Check if we are walking across a subface. + if (issubface(*searchtet)) { + loc = ENCSUBFACE; + break; + } + } + // Move to the adjacent tetrahedron (maybe a hull tetrahedron). + //fsymself(*searchtet); + //if (oppo(*searchtet) == dummypoint) { + // loc = OUTSIDE; // return OUTSIDE; + // break; + //} + decode(searchtet->tet[searchtet->ver & 3], *searchtet); // fsymself + if (ishulltet(*searchtet)) { + loc = OUTSIDE; // return OUTSIDE; + break; + } + max_visited_tets--; + + // Retreat the three vertices of the base face. + torg = org(*searchtet); + tdest = dest(*searchtet); + tapex = apex(*searchtet); + } // while (true) + + return loc; +} + +//============================================================================// +// // +// splittetrahedron() Split a tetrahedron. // +// // +//============================================================================// + +bool tetgenmesh::split_tetrahedron(triface* splittet, // the tet to be split. + REAL *param, // param[6], it contains the following data + // [0],[1],[2] - the location of the new point + // [3] - the samllest edge length ( = insertion radius) + // [4] - radius-edge ratio + // [5] - its volume + int qflag, // split due to mesh size enforcement. + int chkencflag, + insertvertexflags &ivf) +{ + triface searchtet; + point newpt, bak_pts[4], *ppt; + bool splitflag = false; + int i; + + + insert_point_count++; + if (!b->quiet && (b->refine_progress_ratio > 0.)) { + if (insert_point_count >= report_refine_progress) { + printf(" %ld insertions, added %ld points, %ld tetrahedra in queue.\n", + insert_point_count - last_insertion_count, + points->items - last_point_count, + check_tets_list->objects); + last_point_count = points->items; // update it. + last_insertion_count = insert_point_count; + // The next report event + report_refine_progress *= (1. + b->refine_progress_ratio); + } + } + + makepoint(&newpt, FREEVOLVERTEX); + for (i = 0; i < 3; i++) newpt[i] = param[i]; + + // Locate the new point. Starting from an interior point 'q' of the + // splittet. We perform a walk from q to the 'newpt', stop walking + // either we hit a subface or enter OUTSIDE. + searchtet = *splittet; + ivf.iloc = (int) OUTSIDE; + //ivf.iloc = locate(newpt, &searchtet, 1); // 'chkencflag' = 1. + ivf.iloc = locate_point_walk(newpt, &searchtet, 1); // 'chkencflag' = 1. + + + if ((ivf.iloc == (int) ENCSUBFACE) || (ivf.iloc == (int) OUTSIDE)) { + // The circumcenter 'c' is not visible from 'q' (the interior of the tet). + pointdealloc(newpt); // Do not insert this vertex. + + + ivf.iloc = (int) FENSEDIN; + return splitflag; + } // if (ivf.iloc == (int) ENCSUBFACE) + + // Use Bowyer-Watson algorithm. Preserve subsegments and subfaces; + ivf.bowywat = 3; + ivf.lawson = 2; + ivf.rejflag = 3; // Do check for encroached segments and subfaces. + if (b->metric) { + ivf.rejflag |= 4; // Reject it if it lies in some protecting balls. + } + ivf.chkencflag = (chkencflag & (~3)); // chkencflag; + ivf.sloc = ivf.sbowywat = 0; // No use. + ivf.splitbdflag = 0; // No use (its an interior vertex). + ivf.validflag = 1; + ivf.respectbdflag = 1; + ivf.assignmeshsize = b->metric; + + // Mesh refinement options. + ivf.refineflag = 1; + ivf.refinetet = *splittet; + // get the shortest edge length to the new point. + ivf.smlenflag = useinsertradius; + if (!qflag) { + // Avoid creating an unnecessarily short edge. + ivf.check_insert_radius = useinsertradius; + } else { + ivf.check_insert_radius = 0; + } + ivf.parentpt = NULL; // init. + + if (insertpoint(newpt, &searchtet, NULL, NULL, &ivf)) { + // Vertex is inserted. + st_volref_count++; + if (steinerleft > 0) steinerleft--; + if (useinsertradius) { + // Save the shortest edge between: emin and ivf.smlen + REAL rv = 0.0; // ivf.smlen; + if (param[3] > 0.0) { // The smallest edge length of this tet. + rv = (param[3] < ivf.smlen ? param[3] : ivf.smlen); + } + setpointinsradius(newpt, rv); // ivf.smlen + setpoint2ppt(newpt, ivf.parentpt); + if (ivf.smlen < smallest_insradius) { // ivf.smlen + smallest_insradius = ivf.smlen; + } + } + if (flipstack != NULL) { + flipconstraints fc; + fc.chkencflag = (chkencflag & (~3)); //chkencflag; + fc.enqflag = 2; + lawsonflip3d(&fc); + //unflipqueue->restart(); + } + + if (later_unflip_queue->objects > b->unflip_queue_limit) { + recoverdelaunay(); + } + + return true; + } + + // Point is not inserted. + pointdealloc(newpt); + + if (ivf.iloc == (int) ENCSEGMENT) { + if (!b->nobisect) { //if (!b->nobisect && qflag) { // no -Y + // bakup the vertices of this tet. + ppt = (point *) &(splittet->tet[4]); + for (i = 0; i < 4; i++) bak_pts[i] = ppt[i]; + + bool ref_segment = ((b->cdtrefine & 1) > 0); + + if (ref_segment || qflag) { + for (i = 0; i < encseglist->objects; i++) { + //face *paryseg = (face *) fastlookup(encseglist, i); + badface *bf = (badface *) fastlookup(encseglist, i); + if ((bf->ss.sh == NULL) || + (sorg(bf->ss) != bf->forg) || + (sdest(bf->ss) != bf->fdest)) { + continue; // Skip this segment. + } + int tmp_iloc; + if (split_segment(&(bf->ss), NULL, param, qflag, (chkencflag | 3), &tmp_iloc)) { + // A Steienr point is inserted on a segment. + // Check if this tet is split as well. + if ((splittet->tet == NULL) || (splittet->tet[4] == NULL)) { + splitflag = true; // The tet is split as well. + } else { + ppt = (point *) &(splittet->tet[4]); + if ((ppt[0] != bak_pts[0]) || + (ppt[1] != bak_pts[1]) || + (ppt[2] != bak_pts[2]) || + (ppt[3] != bak_pts[3])) { + splitflag = true; // The tet is split as well. + } + } + if (splitflag) { + break; // This tetrahedron is split. + } + } + } // i + } // if (ref_segment ||qflag) + encseglist->restart(); + // Some segments may need to be repaired. + if (badsubsegs->items > 0) { + //repairencsegs(param, qflag, (chkencflag | 3)); // Queue new enroached subsegments and subfaces. + repairencsegs(param, 0, (chkencflag | 3)); // qflag = 0 + } + // Some subfaces may need to be repaired. + if (badsubfacs->items > 0) { + //repairencfacs(param, qflag, (chkencflag | 2)); // Queue new encroached subfaces. + repairencfacs(param, 0, (chkencflag | 2)); // qflag = 0 + if (unsplit_subfaces->objects > 0) { + unsplit_subfaces->restart(); // clear this list; + } + } + // Check if this tet is split as well. + if ((splittet->tet == NULL) || (splittet->tet[4] == NULL)) { + splitflag = true; // The tet is split as well. + } else { + ppt = (point *) &(splittet->tet[4]); + if ((ppt[0] != bak_pts[0]) || + (ppt[1] != bak_pts[1]) || + (ppt[2] != bak_pts[2]) || + (ppt[3] != bak_pts[3])) { + splitflag = true; // The tet is split as well. + } + } + } else { // if (!b->nobisect) { // no -Y + encseglist->restart(); + } + } else if (ivf.iloc == (int) ENCSUBFACE) { + if (!b->nobisect) { //if (!b->nobisect && qflag) { // no -Y + // bakup the vertices of this tet. + ppt = (point *) &(splittet->tet[4]); + for (i = 0; i < 4; i++) bak_pts[i] = ppt[i]; + + bool ref_subface = ((b->cdtrefine & 2) > 0); + + if (ref_subface || qflag) { + // This rejected Steiner point may encroach upon more than one subfaces. + // We split the one which contains the projection of this rejected + // Steiner point. Moreover, there may be many subfaces. + triface adjtet; + point pa, pb, pc, toppo; + REAL prjpt[3], ori; + int scount = 0; + int t1ver; + + // Clean the bad radius-edge ratio, so split_subface() knows that + // the split of this subface is due to a rejected tet ccenter. + param[4] = 0.0; + + for (i = 0; i < encshlist->objects; i++) { + badface *bface = (badface *) fastlookup(encshlist, i); + // This subface may be split. + if ((bface->ss.sh == NULL) || + (sorg(bface->ss) != bface->forg) || + (sdest(bface->ss) != bface->fdest) || + (sapex(bface->ss) != bface->fapex)) { + continue; + } + stpivot(bface->ss, adjtet); + if (ishulltet(adjtet)) { + fsymself(adjtet); + } + toppo = oppo(adjtet); // used by orient3d() + //assert(toppo != dummypoint); + pa = org(adjtet); + pb = dest(adjtet); + pc = apex(adjtet); + projpt2face(param, pa, pb, pc, prjpt); + ori = orient3d(pa, pb, toppo, prjpt); + if (ori >= 0) { + ori = orient3d(pb, pc, toppo, prjpt); + if (ori >= 0) { + ori = orient3d(pc, pa, toppo, prjpt); + if (ori >= 0) { + scount++; + // Found such a subface, try to split it. + int tmp_iloc; + split_subface(&(bface->ss), NULL, bface->cent, param, qflag, + chkencflag | 2, &tmp_iloc); + // This subface may not be split while some encroached subsegments + // might be split. + // Check if this tet is split as well. + if ((splittet->tet == NULL) || (splittet->tet[4] == NULL)) { + splitflag = true; // The tet is split as well. + } else { + ppt = (point *) &(splittet->tet[4]); + if ((ppt[0] != bak_pts[0]) || + (ppt[1] != bak_pts[1]) || + (ppt[2] != bak_pts[2]) || + (ppt[3] != bak_pts[3])) { + splitflag = true; // The tet is split as well. + } + } + if (splitflag) { + break; + } + } // if (ori >= 0) + } + } + } // i + if (scount == 0) { + // Not such subface is found! This can happen due to the existence + // of small angles and non-Delaunay elements. + // Select an encroached subface and split it. + for (i = 0; i < encshlist->objects; i++) { + badface *bface = (badface *) fastlookup(encshlist, i); + if ((bface->ss.sh == NULL) || + (sorg(bface->ss) != bface->forg) || + (sdest(bface->ss) != bface->fdest) || + (sapex(bface->ss) != bface->fapex)) { + continue; + } + //if (get_subface_ccent(&(bface->ss), ccent)) { + int tmp_iloc; + split_subface(&(bface->ss), NULL, bface->cent, param, qflag, + chkencflag | 2, &tmp_iloc); + // Check if this tet is split as well. + if ((splittet->tet == NULL) || (splittet->tet[4] == NULL)) { + splitflag = true; // The tet is split as well. + } else { + ppt = (point *) &(splittet->tet[4]); + if ((ppt[0] != bak_pts[0]) || + (ppt[1] != bak_pts[1]) || + (ppt[2] != bak_pts[2]) || + (ppt[3] != bak_pts[3])) { + splitflag = true; // The tet is split as well. + } + } + if (splitflag) { + break; // This tetrahedron is split. + } + } + } // if (scount == 0) + } // if (ref_subface) + encshlist->restart(); // Clear the list. + // Some subfaces may need to be repaired. + if (badsubfacs->items > 0) { + //repairencfacs(param, qflag, (chkencflag | 2)); // Queue new encroached subfaces. + repairencfacs(param, 0, (chkencflag | 2)); // qflag = 0 + if (unsplit_subfaces->objects > 0) { + unsplit_subfaces->restart(); // clear this list. + } + } + // Check if this tet is split as well. + if ((splittet->tet == NULL) || (splittet->tet[4] == NULL)) { + splitflag = true; // The tet is split as well. + } else { + ppt = (point *) &(splittet->tet[4]); + if ((ppt[0] != bak_pts[0]) || + (ppt[1] != bak_pts[1]) || + (ppt[2] != bak_pts[2]) || + (ppt[3] != bak_pts[3])) { + splitflag = true; // The tet is split as well. + } + } + } else { // if (!b->nobisect) + encshlist->restart(); + } } + + return splitflag; } -/////////////////////////////////////////////////////////////////////////////// -// // -// delaunayrefinement() Refine the mesh by Delaunay refinement. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// repairbadtets() Repair bad quality tetrahedra. // +// // +//============================================================================// + +void tetgenmesh::repairbadtets(REAL queratio, int chkencflag) +{ + triface *bface, *quetet, *last_quetet; + triface checktet; + REAL param[6] = {0.,}; + int qflag = 0; + int i; + + while ((badtetrahedrons->items > 0) || (check_tets_list->objects > 0)) { + + if (badtetrahedrons->items > 0) { + badtetrahedrons->traversalinit(); + bface = (triface *) badtetrahedrons->traverse(); + while (bface != NULL) { + check_tets_list->newindex((void **) &quetet); + *quetet = *bface; + bface = (triface *) badtetrahedrons->traverse(); + } + badtetrahedrons->restart(); + } + + // Stop if we have used the desried number of Steiner points. + if (steinerleft == 0) break; + // Stop if the desried number of tetrahedra is reached. + if ((elem_limit > 0) && + ((tetrahedrons->items - hullsize) > elem_limit)) break; + + + // Randomly select a tet to split. + i = rand() % check_tets_list->objects; + quetet = (triface *) fastlookup(check_tets_list, i); + checktet = *quetet; + + // Fill the current position by the last tet in the list. + i = check_tets_list->objects - 1; + last_quetet = (triface *) fastlookup(check_tets_list, i); + *quetet = *last_quetet; + check_tets_list->objects--; + + if (!isdeadtet(checktet)) { + if (marktest2ed(checktet)) { + unmarktest2(checktet); + //if (check_tetrahedron(&checktet, param, qflag)) { + if (checktet4split(&checktet, param, qflag)) { + bool splitflag = false; + insertvertexflags ivf; + splitflag = split_tetrahedron(&checktet, param, qflag, chkencflag, ivf); + if (!splitflag) { + if (qflag || (param[4] > queratio)) { // radius-edge ratio + badface *bt = NULL; + unsplit_badtets->newindex((void **) &bt); + bt->init(); + bt->tt = checktet; + bt->forg = org(checktet); + bt->fdest = dest(checktet); + bt->fapex = apex(checktet); + bt->foppo = oppo(checktet); + for (i = 0; i < 6; i++) bt->cent[i] = param[i]; + bt->key = (double) qflag; + } + } + } + } + } // if (!isdeadtet(checktet)) { + + } // while ((badtetrahedrons->items > 0) || (check_tets_list->objects > 0)) + + if (check_tets_list->objects > 0) { + if (steinerleft == 0) { + if (b->verbose) { + printf("The desired number of Steiner points is reached.\n"); + } + } else if (elem_limit > 0) { + if (b->verbose) { + printf("The desired number %ld of elements is reached.\n", elem_limit); + } + } + //split_tets_pool->restart(); // Clear this pool. + // Unmark all unchecked tetrahedra. + for (i = 0; i < check_tets_list->objects; i++) { + quetet = (triface *) fastlookup(check_tets_list, i); + if (!isdeadtet(*quetet)) { + unmarktest2(*quetet); + } + } + check_tets_list->restart(); + } +} + +//============================================================================// +// // +// delaunayrefinement() Refine the mesh by Delaunay refinement. // +// // +//============================================================================// void tetgenmesh::delaunayrefinement() { @@ -25368,20 +29127,34 @@ void tetgenmesh::delaunayrefinement() face checksh; face checkseg; long steinercount; - int chkencflag; + REAL param[6] = {0., 0., 0., 0., 0., 0.}; + int qflag = 0; + int chkencflag = 0; + int i; long bak_segref_count, bak_facref_count, bak_volref_count; - long bak_flipcount = flip23count + flip32count + flip44count; if (!b->quiet) { printf("Refining mesh...\n"); } if (b->verbose) { - printf(" Min radiu-edge ratio = %g.\n", b->minratio); - printf(" Min dihedral angle = %g.\n", b->mindihedral); + printf(" Min radius-edge ratio = %g.\n", b->minratio); + if (b->mindihedral > 0.) { + printf(" Min dihedral angle = %g.\n", b->mindihedral); + } + if (b->fixedvolume) { + printf(" Max tet volume = %g.\n", b->maxvolume); + } //printf(" Min Edge length = %g.\n", b->minedgelength); } + // Used in locate_point_on_surface(); + cos_facet_separate_ang_tol = cos(b->facet_separate_ang_tol/180.*PI); // -p/# + // Used in function is_collinear_at(mid, left, right); + cos_collinear_ang_tol = cos(b->collinear_ang_tol/180.*PI); // -p///# + + // The cosine value of the min dihedral angle (-q/#) for tetrahedra. + cosmindihed = cos(b->mindihedral / 180.0 * PI); steinerleft = b->steinerleft; // Upperbound of # Steiner points (by -S#). if (steinerleft > 0) { @@ -25399,163 +29172,554 @@ void tetgenmesh::delaunayrefinement() } } - if (useinsertradius) { - if ((b->plc && b->nobisect) || b->refine) { // '-pY' or '-r' option. - makesegmentendpointsmap(); + if (b->refine && (b->elem_growth_ratio > 0.0)) { // -r# + int ntet = in->numberoftetrahedra; // tetrahedrons->items - hullsize; + elem_limit = ntet * (1.0 + b->elem_growth_ratio); + } + + if (b->refine_progress_ratio > 0) { // -r/# default is 0.333 + insert_point_count = 0l; + last_insertion_count = 0l; + last_point_count = points->items; + report_refine_progress = points->items * (1. + b->refine_progress_ratio); + } + + if (!b->nobisect) { // no -Y. + if (segmentendpointslist == NULL) { + makesegmentendpointsmap(); // create ridge_vertex-to-segment map. } - makefacetverticesmap(); + create_segment_info_list(); + makefacetverticesmap(); // create ridge_vertex-to-facet map. + create_segment_facet_map(); // vreate segment-to-facet map. } - encseglist = new arraypool(sizeof(face), 8); - encshlist = new arraypool(sizeof(badface), 8); + // Begin of memory allocation =============================================== + // Initialize the pools and priority queues. + long bls = b->shellfaceperblock; + long blt = b->tetrahedraperblock; + badsubsegs = new memorypool(sizeof(face), 256, sizeof(void *), 0); + badsubfacs = new memorypool(sizeof(face), 256, sizeof(void *), 0); + badtetrahedrons = new memorypool(sizeof(triface), blt, sizeof(void *), 0); + + split_segments_pool = new memorypool(sizeof(badface), bls, sizeof(void *), 0); + split_subfaces_pool = new memorypool(sizeof(badface), bls, sizeof(void *), 0); - //if (!b->nobisect) { // if no '-Y' option - if (!b->nobisect || checkconstraints) { - if (b->verbose) { - printf(" Splitting encroached subsegments.\n"); - } + long est_size = blt; + int log2objperblk = 0; + while (est_size >>= 1) log2objperblk++; + if (log2objperblk < 10) log2objperblk = 10; // At least 1024. - chkencflag = 1; // Only check encroaching subsegments. - steinercount = points->items; + check_tets_list = new arraypool(sizeof(triface), log2objperblk); + + unsplit_segments = new arraypool(sizeof(badface), 10); + unsplit_subfaces = new arraypool(sizeof(badface), 10); + unsplit_badtets = new arraypool(sizeof(badface), 10); + + stack_enc_segments = stack_enc_subfaces = NULL; - // Initialize the pool of encroached subsegments. - badsubsegs = new memorypool(sizeof(face), b->shellfaceperblock, - sizeof(void *), 0); + for (i = 0; i < 64; i++) { + queuefront[i] = NULL; + } + firstnonemptyq = -1; + recentq = -1; - // Add all segments into the pool. - subsegs->traversalinit(); - checkseg.sh = shellfacetraverse(subsegs); - while (checkseg.sh != (shellface *) NULL) { - enqueuesubface(badsubsegs, &checkseg); - checkseg.sh = shellfacetraverse(subsegs); - } + encseglist = new arraypool(sizeof(badface), 8); + encshlist = new arraypool(sizeof(badface), 8); + // End of memory allocation ================================================= - // Split all encroached segments. - repairencsegs(chkencflag); + // with -r and an .elem file ================================================ + if (b->refine && (in->refine_elem_list != NULL)) { if (b->verbose) { - printf(" Added %ld Steiner points.\n", points->items - steinercount); + printf(" Refining a list of given elements.\n"); } + //assert(b->varvolume > 0); // -a option must be used. + chkencflag = 4; // Check bad tetrahedra. + steinercount = points->items; - if (b->reflevel > 1) { // '-D2' option - if (b->verbose) { - printf(" Splitting encroached subfaces.\n"); - } + REAL queratio = b->minratio > 2. ? b->minratio : 2.0; + queratio *= 2.0; // queratio; // increase this value. - chkencflag = 2; // Only check encroaching subfaces. - steinercount = points->items; - bak_segref_count = st_segref_count; - bak_facref_count = st_facref_count; + // Create a map from indices to points. + point *idx2verlist; + makeindex2pointmap(idx2verlist); - // Initialize the pool of encroached subfaces. - badsubfacs = new memorypool(sizeof(face), b->shellfaceperblock, - sizeof(void *), 0); + int *elelist = in->refine_elem_list; + int elem; - // Add all subfaces into the pool. - subfaces->traversalinit(); - checksh.sh = shellfacetraverse(subfaces); - while (checksh.sh != (shellface *) NULL) { - enqueuesubface(badsubfacs, &checksh); - checksh.sh = shellfacetraverse(subfaces); + for (elem = 0; elem < in->numberofrefineelems; elem++) { + point p1 = idx2verlist[elelist[elem*4]]; + point p2 = idx2verlist[elelist[elem*4+1]]; + point p3 = idx2verlist[elelist[elem*4+2]]; + point p4 = idx2verlist[elelist[elem*4+3]]; + + if (!get_tet(p1, p2, p3, p4, &checktet)) { + continue; } - // Split all encroached subfaces. - repairencfacs(chkencflag); + REAL volume_limit; + if (in->refine_elem_vol_list != NULL) { + volume_limit = in->refine_elem_vol_list[i]; + } else { + point *ppt = (point *) &(checktet.tet[4]); + REAL volume = orient3dfast(ppt[1], ppt[0], ppt[2], ppt[3]) / 6.; + volume_limit = volume / 3.; + } + setvolumebound(checktet.tet, volume_limit); + + //assert(check_tets_list->objects == 0l); + triface *quetet; + marktest2(checktet); + check_tets_list->newindex((void **) &quetet); + *quetet = checktet; + + int maxiter = 2, iter; + + for (iter = 0; iter < maxiter; iter++) { + repairbadtets(queratio, chkencflag); + + if (later_unflip_queue->objects > 0l) { + recoverdelaunay(); + } + + // Split unsplit tetrahedra + long badtetcount = 0, splitcount = 0; + int j; + + for (i = 0; i < unsplit_badtets->objects; i++) { + badface *bt = (badface *) fastlookup(unsplit_badtets, i); + if ((bt->tt.tet != NULL) && + ( org(bt->tt) == bt->forg ) && + (dest(bt->tt) == bt->fdest) && + (apex(bt->tt) == bt->fapex) && + (oppo(bt->tt) == bt->foppo)) { + + if (steinerleft == 0) break; + if (elem_limit > 0) { + if ((tetrahedrons->items - hullsize) > elem_limit) { + break; + } + } + + // Count a live tet. + badtetcount++; + insertvertexflags ivf; + qflag = (int) bt->key; + point *ppt = (point *) &(bt->tt.tet[4]); + for (j = 0; j < 3; j++) { + param[j] = (ppt[0][j]+ppt[1][j]+ppt[2][j]+ppt[3][j]) / 4.0; + } + for (; j < 6; j++) { + param[j] = bt->cent[j]; + } + if (split_tetrahedron(&bt->tt, param, qflag, chkencflag, ivf)) { + splitcount++; + } + + if (badtetrahedrons->items > 0) { + // Push new bad quality tetrahedron into queue. + badtetrahedrons->traversalinit(); + triface *bface = (triface *) badtetrahedrons->traverse(); + while (bface != NULL) { + check_tets_list->newindex((void **) &quetet); + *quetet = *bface; + bface = (triface *) badtetrahedrons->traverse(); + } + badtetrahedrons->restart(); + } + } + } // i + + unsplit_badtets->restart(); + + if (splitcount == 0) break; + } // iter + + if (check_tets_list->objects > 0) { + // Clean the list. + for (i = 0; i < check_tets_list->objects; i++) { + quetet = (triface *) fastlookup(check_tets_list, i); + if (!isdeadtet(*quetet)) { + unmarktest2(*quetet); + } + } + check_tets_list->restart(); + } + + if (steinerleft == 0) break; + if (elem_limit > 0) { + if ((tetrahedrons->items - hullsize) > elem_limit) { + break; + } + } + + } // elem + + if (b->verbose) { + printf(" Added %ld Steiner points.\n", points->items - steinercount); + } + delete [] idx2verlist; + } // if (b->refine && (in->refine_elem_list != NULL)) + // with -r and an .elem file ================================================ + + bool force_quit_refinement = false; + + if (steinerleft == 0) { + force_quit_refinement = true; + } else if (elem_limit > 0) { + if ((tetrahedrons->items - hullsize) > elem_limit) { + force_quit_refinement = true; + } + } + + if (!b->nobisect) { // no -Y + bool ref_segment = ((b->cdtrefine & 1) > 0); // -D1, -D3, -D5, or -D7 + + if (ref_segment && !force_quit_refinement) { + if (b->verbose) { + printf(" Splitting encroached subsegments.\n"); + } + + chkencflag = 1; // Only check encroaching subsegments. + steinercount = points->items; + + // Add all segments into the pool. + subsegs->traversalinit(); + checkseg.sh = shellfacetraverse(subsegs); + while (checkseg.sh != (shellface *) NULL) { + //enqueuesubface(badsubsegs, &checkseg); + point encpt = NULL; + if (check_enc_segment(&checkseg, &encpt)) { + badface *bf = (badface *) split_segments_pool->alloc(); + bf->init(); + bf->ss = checkseg; + bf->forg = sorg(checkseg); + bf->fdest = sdest(checkseg); + bf->noppo = encpt; + // Push it into stack. + bf->nextitem = stack_enc_segments; + stack_enc_segments = bf; + } + checkseg.sh = shellfacetraverse(subsegs); + } + + // Split all encroached segments. + for (i = 0; i < 6; i++) param[i] = 0.0; + qflag = 0; + + repairencsegs(param, qflag, chkencflag); + + if (b->verbose) { + printf(" Added %ld Steiner points.\n", points->items - steinercount); + } + + if (later_unflip_queue->objects > 0l) { + recoverdelaunay(); + } + } // if (ref_segment) + + bool ref_surface = ((b->cdtrefine & 2) > 0); // -D2, -D3, or -D7 + + if (ref_surface && !force_quit_refinement) { + if (b->verbose) { + printf(" Splitting encroached and bad quality subfaces.\n"); + } + + chkencflag = 2; // only check encroaching subfaces. + steinercount = points->items; + bak_segref_count = st_segref_count; + bak_facref_count = st_facref_count; + + // Add all subfaces into the pool. + REAL ccent[3], radius; + point encpt = NULL; + + subfaces->traversalinit(); + checksh.sh = shellfacetraverse(subfaces); + while (checksh.sh != (shellface *) NULL) { + //enqueuesubface(badsubfacs, &checksh); + if (get_subface_ccent(&checksh, ccent)) { + encpt = NULL; + for (i = 3; i < 6; i++) param[i] = 0.0; + if (check_enc_subface(&checksh, &encpt, ccent, &radius)) { + enqueue_subface(&checksh, encpt, ccent, param); + } else { + if (check_subface(&checksh, ccent, radius, param)) { + enqueue_subface(&checksh, NULL, ccent, param); + } + } + } else { + terminatetetgen(this, 2); // report a bug. + } + checksh.sh = shellfacetraverse(subfaces); + } + + // check_enc_subface() may find some non-Delaunay faces. + if (flippool->items > 0) { + flipconstraints fc; + fc.chkencflag = chkencflag; + fc.enqflag = 2; + lawsonflip3d(&fc); + } + + // Split all encroached subfaces. + for (i = 0; i < 6; i++) param[i] = 0.0; + qflag = 0; + + int maxiter = 3, iter; + + for (iter = 0; iter < maxiter; iter++) { + + if (b->verbose > 1) { + printf(" iter = %d\n", iter+1); + } + long iter_steinercount = points->items; + long iter_segref_count = st_segref_count; + long iter_facref_count = st_facref_count; + + repairencfacs(param, qflag, chkencflag); + + if (b->verbose > 1) { + printf(" Added %ld (%ld,%ld) Steiner points.\n", + points->items-iter_steinercount, + st_segref_count-iter_segref_count, + st_facref_count-iter_facref_count); + } + + if (unsplit_subfaces->objects > 0) { + if (b->verbose > 1) { + printf(" splitting %ld unsplit subfaces\n", unsplit_subfaces->objects); + } + int scount = 0; // Count the split subfaces. + + for (i = 0; i < unsplit_subfaces->objects; i++) { + badface *bf = (badface *) fastlookup(unsplit_subfaces, i); + if ((bf->ss.sh != NULL) && + ( sorg(bf->ss) == bf->forg) && + (sdest(bf->ss) == bf->fdest) && + (sapex(bf->ss) == bf->fapex)) { + // Try to split it in its barycenter. + int iloc, j; + for (j = 0; j < 3; j++) { + ccent[j] = (bf->forg[j] + bf->fdest[j] + bf->fapex[j]) / 3.; + } + for (j = 3; j < 6; j++) param[j] = bf->cent[j]; + encpt = bf->noppo; // The encroaching vertex. + if (split_subface(&bf->ss, encpt, ccent, param, qflag, chkencflag, &iloc)) { + scount++; + } + } + } // i + + unsplit_subfaces->restart(); + + if (b->verbose > 1) { + printf(" Split %d subfaces.\n", scount); + } + } else { + break; // no unsplit subfaces. + } // if (unsplit_subfaces->objects > 0) + } // iter if (b->verbose) { - printf(" Added %ld (%ld,%ld) Steiner points.\n", + printf(" Added %ld (%ld,%ld) Steiner points.\n", points->items-steinercount, st_segref_count-bak_segref_count, st_facref_count-bak_facref_count); } - } // if (b->reflevel > 1) + + if (badsubfacs->items > 0) { + // Clean this pool. + badsubfacs->traversalinit(); + face *bface = (face *) badsubfacs->traverse(); + while (bface != NULL) { + if ((bface->sh != NULL) && (bface->sh[3] != NULL)) { + if (smarktest2ed(*bface)) { + sunmarktest2(*bface); + } + } + bface = (face *) badsubfacs->traverse(); + } + badsubfacs->restart(); + } + + if (later_unflip_queue->objects > 0l) { + recoverdelaunay(); + } + } // if (ref_subface) } // if (!b->nobisect) - if (b->reflevel > 2) { // '-D3' option (The default option) + if (((b->cdtrefine & 4) > 0) && !force_quit_refinement) { // -D4, -D5, or -D7 + // Begin of adding Steiner points in volume =============================== + + // A Steiner point can be added only if it does not encroach upon any + // boundary segment or subface. if (b->verbose) { printf(" Splitting bad quality tets.\n"); } - chkencflag = 4; // Only check tetrahedra. - steinercount = points->items; - bak_segref_count = st_segref_count; - bak_facref_count = st_facref_count; - bak_volref_count = st_volref_count; - - // The cosine value of the min dihedral angle (-qq) for tetrahedra. - cosmindihed = cos(b->mindihedral / 180.0 * PI); + for (i = 0; i < 6; i++) param[i] = 0.0; + qflag = 0; - // Initialize the pool of bad quality tetrahedra. - badtetrahedrons = new memorypool(sizeof(triface), b->tetrahedraperblock, - sizeof(void *), 0); // Add all tetrahedra (no hull tets) into the pool. + triface *quetet; tetrahedrons->traversalinit(); checktet.tet = tetrahedrontraverse(); while (checktet.tet != NULL) { - enqueuetetrahedron(&checktet); + marktest2(checktet); + check_tets_list->newindex((void **) &quetet); + *quetet = checktet; checktet.tet = tetrahedrontraverse(); } - // Split all bad quality tetrahedra. - repairbadtets(chkencflag); - if (b->verbose) { - printf(" Added %ld (%ld,%ld,%ld) Steiner points.\n", - points->items - steinercount, - st_segref_count - bak_segref_count, - st_facref_count - bak_facref_count, - st_volref_count - bak_volref_count); - } - } // if (b->reflevel > 2) + chkencflag = 4; // Check bad tetrahedra. - if (b->verbose) { - if (flip23count + flip32count + flip44count > bak_flipcount) { - printf(" Performed %ld flips.\n", flip23count + flip32count + - flip44count - bak_flipcount); - } - } + REAL queratio = b->minratio > 2. ? b->minratio : 2.0; + queratio *= 2.0; // queratio; // increase this value. - if (steinerleft == 0) { - if (!b->quiet) { - printf("\nWarnning: "); - printf("The desired number of Steiner points (%d) is reached.\n\n", - b->steinerleft); + int maxiter = 3, iter; + + for (iter = 0; iter < maxiter; iter++) { + steinercount = points->items; + bak_segref_count = st_segref_count; + bak_facref_count = st_facref_count; + bak_volref_count = st_volref_count; + + if (b->verbose > 1) { + printf(" iter = %d: queratio = %g\n", iter + 1, queratio); + } + + // Split all bad quality tetrahedra. + repairbadtets(queratio, chkencflag); + + if (b->verbose) { + printf(" Added %ld (%ld,%ld,%ld) Steiner points.\n", + points->items - steinercount, + st_segref_count - bak_segref_count, + st_facref_count - bak_facref_count, + st_volref_count - bak_volref_count); + } + + + if (later_unflip_queue->objects > 0l) { + recoverdelaunay(); + } + + if (unsplit_badtets->objects == 0) break; + + //queratio *= 2.0; // queratio; // increase this value. + + // Split unsplit tetrahedra + long badtetcount = 0, splitcount = 0; + int j; + + for (i = 0; i < unsplit_badtets->objects; i++) { + badface *bt = (badface *) fastlookup(unsplit_badtets, i); + if ((bt->tt.tet != NULL) && + ( org(bt->tt) == bt->forg ) && + (dest(bt->tt) == bt->fdest) && + (apex(bt->tt) == bt->fapex) && + (oppo(bt->tt) == bt->foppo)) { + if (steinerleft == 0) break; + if (elem_limit > 0) { + if ((tetrahedrons->items - hullsize) > elem_limit) { + break; + } + } + + // Count a live tet. + badtetcount++; + insertvertexflags ivf; + qflag = (int) bt->key; + point *ppt = (point *) &(bt->tt.tet[4]); + for (j = 0; j < 3; j++) { + param[j] = (ppt[0][j]+ppt[1][j]+ppt[2][j]+ppt[3][j]) / 4.0; + } + for (; j < 6; j++) { + param[j] = bt->cent[j]; + } + if (split_tetrahedron(&bt->tt, param, qflag, chkencflag, ivf)) { + splitcount++; + } + if (badtetrahedrons->items > 0) { + // Push new bad quality tetrahedron into queue. + badtetrahedrons->traversalinit(); + triface *bface = (triface *) badtetrahedrons->traverse(); + while (bface != NULL) { + check_tets_list->newindex((void **) &quetet); + *quetet = *bface; + bface = (triface *) badtetrahedrons->traverse(); + } + badtetrahedrons->restart(); + } + } + } // i + unsplit_badtets->restart(); + + + if (splitcount == 0) break; + } // iter + + if (check_tets_list->objects > 0) { + // Unmark all unchecked tetrahedra. + for (i = 0; i < check_tets_list->objects; i++) { + quetet = (triface *) fastlookup(check_tets_list, i); + if (!isdeadtet(*quetet)) { + unmarktest2(*quetet); + } + } + check_tets_list->restart(); } - } + + // End of adding Steiner points in volume ================================= + } // if ((b->cdtrefine & 4) > 0) { delete encseglist; delete encshlist; - - //if (!b->nobisect) { - if (!b->nobisect || checkconstraints) { - totalworkmemory += (badsubsegs->maxitems * badsubsegs->itembytes); - delete badsubsegs; - if (b->reflevel > 1) { - totalworkmemory += (badsubfacs->maxitems * badsubfacs->itembytes); - delete badsubfacs; - } - } - if (b->reflevel > 2) { - totalworkmemory += (badtetrahedrons->maxitems*badtetrahedrons->itembytes); - delete badtetrahedrons; - } + encseglist = NULL; + encshlist = NULL; + + totalworkmemory += (badsubsegs->maxitems * badsubsegs->itembytes); + delete badsubsegs; + badsubsegs = NULL; + totalworkmemory += (badsubfacs->maxitems * badsubfacs->itembytes); + delete badsubfacs; + badsubfacs = NULL; + totalworkmemory += (split_subfaces_pool->maxitems * split_subfaces_pool->itembytes); + delete split_subfaces_pool; + split_subfaces_pool = NULL; + delete split_segments_pool; + split_segments_pool = NULL; + delete unsplit_segments; + delete unsplit_subfaces; + unsplit_segments = unsplit_subfaces = NULL; + + totalworkmemory += (badtetrahedrons->maxitems*badtetrahedrons->itembytes); + delete badtetrahedrons; + badtetrahedrons = NULL; + totalworkmemory += (check_tets_list->totalmemory); + delete check_tets_list; + check_tets_list = NULL; + delete unsplit_badtets; + unsplit_badtets = NULL; } -//// //// -//// //// -//// refine_cxx /////////////////////////////////////////////////////////////// +// // +// // +//== refine_cxx ==============================================================// -//// optimize_cxx ///////////////////////////////////////////////////////////// -//// //// -//// //// +//== optimize_cxx ============================================================// +// // +// // -/////////////////////////////////////////////////////////////////////////////// -// // -// lawsonflip3d() A three-dimensional Lawson's algorithm. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// lawsonflip3d() A three-dimensional Lawson's algorithm. // +// // +//============================================================================// long tetgenmesh::lawsonflip3d(flipconstraints *fc) { @@ -25564,14 +29728,14 @@ long tetgenmesh::lawsonflip3d(flipconstraints *fc) badface *popface, *bface; point pd, pe, *pts; REAL sign, ori; + REAL vol, len3; long flipcount, totalcount = 0l; long sliver_peels = 0l; int t1ver; int i; - while (1) { - + while (flippool->items != 0l) { if (b->verbose > 2) { printf(" Lawson flip %ld faces.\n", flippool->items); } @@ -25591,6 +29755,7 @@ long tetgenmesh::lawsonflip3d(flipconstraints *fc) unmarkface(fliptets[0]); + if (ishulltet(fliptets[0])) continue; fsym(fliptets[0], fliptets[1]); @@ -25626,20 +29791,33 @@ long tetgenmesh::lawsonflip3d(flipconstraints *fc) } } else { // Perform a 3-to-2 flip to remove the sliver. - fliptets[0] = neightet; // [e,d,a,b] - fnext(fliptets[0], fliptets[1]); // [e,d,b,c] - fnext(fliptets[1], fliptets[2]); // [e,d,c,a] - flip32(fliptets, 1, fc); - // Update counters. - flip32count--; - flip22count--; - sliver_peels++; - if (fc->remove_ndelaunay_edge) { - // Update the volume (must be decreased). - //assert(fc->tetprism_vol_sum <= 0); - tetprism_vol_sum += fc->tetprism_vol_sum; - fc->tetprism_vol_sum = 0.0; // Clear it. - } + // To avoid creating an "inverted" subface in the surface + // Check the normals of the two new subfaces, they must + // not be opposite. + point chk_pe = org(neightet); + point chk_pd = dest(neightet); + point chk_pa = apex(neightet); + point chk_pb = oppo(neightet); + REAL n1[3], n2[3]; + facenormal(chk_pa, chk_pb, chk_pe, n1, 1, NULL); + facenormal(chk_pb, chk_pa, chk_pd, n2, 1, NULL); + double dot = n1[0]*n2[0]+n1[1]*n2[1]+n1[2]*n2[2]; + if (dot > 0.) { + fliptets[0] = neightet; // [e,d,a,b] + fnext(fliptets[0], fliptets[1]); // [e,d,b,c] + fnext(fliptets[1], fliptets[2]); // [e,d,c,a] + flip32(fliptets, 1, fc); + // Update counters. + flip32count--; + flip22count--; + sliver_peels++; + if (fc->remove_ndelaunay_edge) { + // Update the volume (must be decreased). + //assert(fc->tetprism_vol_sum <= 0); + tetprism_vol_sum += fc->tetprism_vol_sum; + fc->tetprism_vol_sum = 0.0; // Clear it. + } + } // if (dot. > 0) } } break; @@ -25665,13 +29843,32 @@ long tetgenmesh::lawsonflip3d(flipconstraints *fc) pd = oppo(fliptets[0]); pe = oppo(fliptets[1]); + // Use the length of the edge [d,e] as a reference to determine + // a nearly degenerated new tet. + len3 = distance(pd, pe); + len3 = (len3 * len3 * len3); + int round_flag = 0; // [2017-10-20] // Check the convexity of its three edges. Stop checking either a // locally non-convex edge (ori < 0) or a flat edge (ori = 0) is // encountered, and 'fliptet' represents that edge. for (i = 0; i < 3; i++) { ori = orient3d(org(fliptets[0]), dest(fliptets[0]), pd, pe); + if (ori > 0) { + // Avoid creating a nearly degenerated new tet at boundary. + // Re-use fliptets[2], fliptets[3]; + esym(fliptets[0], fliptets[2]); + esym(fliptets[1], fliptets[3]); + if (issubface(fliptets[2]) || issubface(fliptets[3])) { + vol = orient3dfast(org(fliptets[0]), dest(fliptets[0]), pd, pe); + if ((fabs(vol) / len3) < b->epsilon) { + ori = 0.0; // Do rounding. + round_flag = 1; // [2017-10-20] + } + } + } // Rounding check if (ori <= 0) break; enextself(fliptets[0]); + eprevself(fliptets[1]); } if (ori > 0) { @@ -25694,12 +29891,39 @@ long tetgenmesh::lawsonflip3d(flipconstraints *fc) // Do not flip if it is a segment. if (issubseg(fliptets[0])) continue; } + // Count the number of interior subfaces for a valid 2-2 flip. + int scount = 0; // Check if there are three or four tets sharing at this edge. esymself(fliptets[0]); // [b,a,d,c] for (i = 0; i < 3; i++) { + if (issubface(fliptets[i])) scount++; fnext(fliptets[i], fliptets[i+1]); } if (fliptets[3].tet == fliptets[0].tet) { + // A 3-2 flip is found. "scount" must be either 0 or 2. + if (scount == 1) { + // This can happen during the boundary recovery. The adjacent + // subface is either missing or not recovered yet. + continue; + } else if (scount == 2) { + // Valid if a 2-2 flip is possible. + for (i = 0; i < 3; i++) { + if (!issubface(fliptets[i])) break; + } + // Assume fliptets[i] is the tet (b,a,c,e). The two subfaces are + // fliptets[(i+1)%3] (b,a,e,d) and fliptets[(i+2)%3] (b,a,d,c). + // A 2-2 flip is possible if the two faces (d,e,a) and (e,d,b) + // are not subfaces. + triface face1, face2; + neightet = fliptets[(i+1)%3]; // (b,a,e,d) + enext(neightet, face1); + esymself(face1); // (e,a,d) + eprev(neightet, face2); + esymself(face2); // (b,e,d) + if (issubface(face1) || issubface(face2)) { + continue; + } + } // A 3-to-2 flip is found. (No hull tet.) flip32(fliptets, 0, fc); flipcount++; @@ -25714,14 +29938,16 @@ long tetgenmesh::lawsonflip3d(flipconstraints *fc) // There are more than 3 tets at this edge. fnext(fliptets[3], fliptets[4]); if (fliptets[4].tet == fliptets[0].tet) { - // There are exactly 4 tets at this edge. - if (nonconvex) { - if (apex(fliptets[3]) == dummypoint) { - // This edge is locally non-convex on the hull. - // It can be removed by a 4-to-4 flip. - ori = 0; - } - } // if (nonconvex) + if (ori != 0.) { + if (nonconvex) { + if (apex(fliptets[3]) == dummypoint) { + // This edge is locally non-convex on the hull. + // It can be removed by a 4-to-4 flip. + ori = 0; + round_flag = 1; + } + } // if (nonconvex) + } if (ori == 0) { // A 4-to-4 flip is found. (Two hull tets may be involved.) // Current tets in 'fliptets': @@ -25729,6 +29955,73 @@ long tetgenmesh::lawsonflip3d(flipconstraints *fc) // [1] [b,a,c,e] // [2] [b,a,e,f] (f may be dummypoint) // [3] [b,a,f,d] + // There are exactly 4 tets at this edge. + // Moreover, a,b,e,d are coplanar. This 4-4 flip will replace + // edge (a,b) to edge (d,e). + + // A valid 2-2 flip is when both faces (a,b,d) and (a,b,e) are + // subfaces, and (a,b,c) and (a,b,f) are not subfaces. + if (issubface(fliptets[0])) { // (a,b,d) + if (!issubface(fliptets[2])) { // (a,b,e) + continue; // not valid 2-2 flip. + } + if (issubface(fliptets[1]) || + issubface(fliptets[3])) { + continue; // The surface mesh is degnerated. + } + } else { + if (issubface(fliptets[1]) || + issubface(fliptets[2]) || + issubface(fliptets[3])) { + continue; // not valid 2-2 flip. + } + } + + if (round_flag == 1) { + //continue; // [2017-10-20] + // We want to flip (nearly coplanar) edges [a,b] to [d,e]. + // Only allow this flip if all new faces are locally Delaunay. + // Otherwise, this routine may not terminate. + point pb = org(fliptets[0]); + point pa = dest(fliptets[0]); + point pc = apex(fliptets[1]); + point pf = apex(fliptets[3]); // pf may be dummypoint + + if (is_collinear_at(pa, pd, pe) || + is_collinear_at(pb, pd, pe)) { + continue; // avoid creating a degenerated (sub)face. + } + + // Validate the four new tets (not inverted) + REAL o1, o2; + o1 = orient3d(pe, pd, pc, pa); + o2 = orient3d(pe, pd, pb, pc); + if ((o1 >= 0.) || (o2 >= 0.)) { + //assert(0); // to debug... + continue; // inverted new tets + } + if (pf != dummypoint) { + REAL o3, o4; + o3 = orient3d(pe, pd, pa, pf); + o4 = orient3d(pe, pd, pf, pb); + if ((o3 >= 0.) || (o4 >= 0.)) { + continue; // inverted new tets + } + } + // Validate locally Delaunay properties of new faces. + REAL test_sign = insphere_s(pe, pd, pc, pa, pb); + if (test_sign < 0) { + // Locally non-Delaunay. Do not perform the 4-4 flip. + continue; + } + if (pf != dummypoint) { + test_sign = insphere_s(pe, pd, pf, pb, pa); + if (test_sign < 0) { + // Locally non-Delaunay. Do not perform the 4-4 flip. + continue; + } + } + } // if (round_flag == 1) esymself(fliptets[0]); // [a,b,c,d] // A 2-to-3 flip replaces face [a,b,c] by edge [e,d]. // This creates a degenerate tet [e,d,a,b] (tmpfliptets[0]). @@ -25758,14 +30051,23 @@ long tetgenmesh::lawsonflip3d(flipconstraints *fc) } // if (ori == 0) } } + // This non-Delaunay face is unflippable. Save it. + // unflipqueue->newindex((void **) &bface); + bface = (badface *) flippool->alloc(); + bface->init(); + esymself(fliptets[0]); // *** The original non-Delaunay face **** + bface->tt = fliptets[0]; + bface->forg = org(fliptets[0]); + bface->fdest = dest(fliptets[0]); + bface->fapex = apex(fliptets[0]); + // Add it into the unflip queue. + if (unflip_queue_front == NULL) { + unflip_queue_front = bface; + } else { + unflip_queue_tail->nextitem = bface; + } + unflip_queue_tail = bface; } // if (ori <= 0) - - // This non-Delaunay face is unflippable. Save it. - unflipqueue->newindex((void **) &bface); - bface->tt = fliptets[0]; - bface->forg = org(fliptets[0]); - bface->fdest = dest(fliptets[0]); - bface->fapex = apex(fliptets[0]); } // if (sign < 0) } // while (flipstack) @@ -25773,29 +30075,54 @@ long tetgenmesh::lawsonflip3d(flipconstraints *fc) if (flipcount > 0) { printf(" Performed %ld flips.\n", flipcount); } + if (flippool->items > 0) { + printf(" Saved %ld unflippbale faces.\n", flippool->items); + } } // Accumulate the counter of flips. totalcount += flipcount; - assert(flippool->items == 0l); // Return if no unflippable faces left. - if (unflipqueue->objects == 0l) break; + //if (unflipqueue->objects == 0l) break; + if (flippool->items == 0l) break; // Return if no flip has been performed. if (flipcount == 0l) break; // Try to flip the unflippable faces. - for (i = 0; i < unflipqueue->objects; i++) { - bface = (badface *) fastlookup(unflipqueue, i); - if (!isdeadtet(bface->tt) && - (org(bface->tt) == bface->forg) && - (dest(bface->tt) == bface->fdest) && - (apex(bface->tt) == bface->fapex)) { + while (unflip_queue_front != NULL) { + bface = unflip_queue_front; + if (!isdeadtet(bface->tt) && + (org(bface->tt) == bface->forg) && + (dest(bface->tt) == bface->fdest) && + (apex(bface->tt) == bface->fapex)) { flippush(flipstack, &(bface->tt)); } + unflip_queue_front = bface->nextitem; + flippool->dealloc((void *) bface); } - unflipqueue->restart(); + unflip_queue_tail = NULL; - } // while (1) + } // while (flippool->items != 0l) + + if (flippool->items > 0l) { + // Save the unflippable faces to flip them later. + badface *bf; + while (unflip_queue_front != NULL) { + bface = unflip_queue_front; + if (!isdeadtet(bface->tt) && + (org(bface->tt) == bface->forg) && + (dest(bface->tt) == bface->fdest) && + (apex(bface->tt) == bface->fapex)) { + //flippush(flipstack, &(bface->tt)); + later_unflip_queue->newindex((void **) &bf); + *bf = *bface; + } + unflip_queue_front = bface->nextitem; + //flippool->dealloc((void *) bface); + } + //unflip_queue_tail = NULL; + flippool->restart(); // Clear the pool. + } if (b->verbose > 2) { if (totalcount > 0) { @@ -25804,59 +30131,77 @@ long tetgenmesh::lawsonflip3d(flipconstraints *fc) if (sliver_peels > 0) { printf(" Removed %ld hull slivers.\n", sliver_peels); } - if (unflipqueue->objects > 0l) { - printf(" %ld unflippable edges remained.\n", unflipqueue->objects); - } + //if (unflipqueue->objects > 0l) { + // printf(" %ld unflippable edges remained.\n", unflipqueue->objects); + //} } return totalcount + sliver_peels; } -/////////////////////////////////////////////////////////////////////////////// -// // -// recoverdelaunay() Recovery the locally Delaunay property. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// recoverdelaunay() Recovery the locally Delaunay property. // +// // +//============================================================================// void tetgenmesh::recoverdelaunay() { - arraypool *flipqueue, *nextflipqueue, *swapqueue; - triface tetloop, neightet, *parytet; badface *bface, *parybface; - point *ppt; flipconstraints fc; int i, j; - if (!b->quiet) { - printf("Recovering Delaunayness...\n"); + if (b->verbose > 2) { + printf(" Recovering Delaunayness...\n"); } - tetprism_vol_sum = 0.0; // Initialize it. - // Put all interior faces of the mesh into 'flipstack'. - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - while (tetloop.tet != NULL) { - for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) { - decode(tetloop.tet[tetloop.ver], neightet); - if (!facemarked(neightet)) { - flippush(flipstack, &tetloop); + if (later_unflip_queue->objects > 0) { + // Flip the saved unflippable faces. + for (i = 0; i < later_unflip_queue->objects; i++) { + bface = (badface *) fastlookup(later_unflip_queue, i); + if (!isdeadtet(bface->tt) && + (org(bface->tt) == bface->forg) && + (dest(bface->tt) == bface->fdest) && + (apex(bface->tt) == bface->fapex)) { + flippush(flipstack, &(bface->tt)); + } + } + later_unflip_queue->restart(); // clean it. + if (flippool->items == 0l) { + return; + } + } else { + if (flippool->items == 0l) { + // Flip all locally non-Delaunay faces of the tetrahedralisation. + triface tetloop, neightet; //, *parytet; + tetrahedrons->traversalinit(); + tetloop.tet = tetrahedrontraverse(); + while (tetloop.tet != NULL) { + for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) { + decode(tetloop.tet[tetloop.ver], neightet); + if (!facemarked(neightet)) { + flippush(flipstack, &tetloop); + } + } + point *ppt = (point *) &(tetloop.tet[4]); + tetprism_vol_sum += tetprismvol(ppt[0], ppt[1], ppt[2], ppt[3]); + tetloop.tet = tetrahedrontraverse(); } } - ppt = (point *) &(tetloop.tet[4]); - tetprism_vol_sum += tetprismvol(ppt[0], ppt[1], ppt[2], ppt[3]); - tetloop.tet = tetrahedrontraverse(); } + + recover_delaunay_count++; - // Calulate a relatively lower bound for small improvement. + // Calulate a relatively lower bound for small improvement. // Used to avoid rounding error in volume calculation. fc.bak_tetprism_vol = tetprism_vol_sum * b->epsilon * 1e-3; - if (b->verbose) { - printf(" Initial obj = %.17g\n", tetprism_vol_sum); + if (b->verbose > 2) { + printf(" Initial obj = %.17g\n", tetprism_vol_sum); } - if (b->verbose > 1) { + if (b->verbose > 2) { printf(" Recover Delaunay [Lawson] : %ld\n", flippool->items); } @@ -25866,47 +30211,53 @@ void tetgenmesh::recoverdelaunay() lawsonflip3d(&fc); - if (b->verbose > 1) { + if (b->verbose > 2) { printf(" obj (after Lawson) = %.17g\n", tetprism_vol_sum); } - if (unflipqueue->objects == 0l) { - return; // The mesh is Delaunay. + if (later_unflip_queue->objects == 0l) { + return; } - - fc.unflip = 1; // Unflip if the edge is not flipped. + + fc.unflip = 0; // fc.unflip = 1; // Unflip if the edge is not flipped. fc.collectnewtets = 1; // new tets are returned in 'cavetetlist'. fc.enqflag = 0; + int bak_autofliplinklevel = autofliplinklevel; + int bak_fliplinklevel = b->fliplinklevel; autofliplinklevel = 1; // Init level. b->fliplinklevel = -1; // No fixed level. - // For efficiency reason, we limit the maximium size of the edge star. - int bakmaxflipstarsize = b->flipstarsize; - b->flipstarsize = 10; // default + badface *bfarray = new badface[later_unflip_queue->objects]; - flipqueue = new arraypool(sizeof(badface), 10); - nextflipqueue = new arraypool(sizeof(badface), 10); - - // Swap the two flip queues. - swapqueue = flipqueue; - flipqueue = unflipqueue; - unflipqueue = swapqueue; + while ((later_unflip_queue->objects > 0) && + (autofliplinklevel < 4)) { // level = 1,2,3 //< 10 - while (flipqueue->objects > 0l) { + int nbf = later_unflip_queue->objects; + for (i = 0; i < nbf; i++) { + bfarray[i] = * (badface *) fastlookup(later_unflip_queue, i); + } + later_unflip_queue->restart(); // clean it. - if (b->verbose > 1) { - printf(" Recover Delaunay [level = %2d] #: %ld.\n", - autofliplinklevel, flipqueue->objects); + if (b->verbose > 2) { + printf(" Recover Delaunay [level = %2d] #: %d.\n", + autofliplinklevel, nbf); } - for (i = 0; i < flipqueue->objects; i++) { - bface = (badface *) fastlookup(flipqueue, i); + for (i = 0; i < nbf; i++) { + bface = &(bfarray[i]); if (getedge(bface->forg, bface->fdest, &bface->tt)) { if (removeedgebyflips(&(bface->tt), &fc) == 2) { tetprism_vol_sum += fc.tetprism_vol_sum; - fc.tetprism_vol_sum = 0.0; // Clear it. + } else { + // This edge is not removed. Save it in later_flip_queue. + later_unflip_queue->newindex((void **) &parybface); + *parybface = bfarray[i]; // *bface; + } + fc.tetprism_vol_sum = 0.0; // Clear it. + if (cavetetlist->objects > 0) { // Queue new faces for flips. + triface neightet, *parytet; for (j = 0; j < cavetetlist->objects; j++) { parytet = (triface *) fastlookup(cavetetlist, j); // A queued new tet may be dead. @@ -25921,900 +30272,1307 @@ void tetgenmesh::recoverdelaunay() } } // j cavetetlist->restart(); - // Remove locally non-Delaunay faces. New non-Delaunay edges - // may be found. They are saved in 'unflipqueue'. - fc.enqflag = 2; - lawsonflip3d(&fc); - fc.enqflag = 0; - // There may be unflipable faces. Add them in flipqueue. - for (j = 0; j < unflipqueue->objects; j++) { - bface = (badface *) fastlookup(unflipqueue, j); - flipqueue->newindex((void **) &parybface); - *parybface = *bface; - } - unflipqueue->restart(); - } else { - // Unable to remove this edge. Save it. - nextflipqueue->newindex((void **) &parybface); - *parybface = *bface; - // Normally, it should be zero. - //assert(fc.tetprism_vol_sum == 0.0); - // However, due to rounding errors, a tiny value may appear. - fc.tetprism_vol_sum = 0.0; - } + } // if (cavetetlist->objects > 0) } } // i - if (b->verbose > 1) { - printf(" obj (after level %d) = %.17g.\n", autofliplinklevel, - tetprism_vol_sum); - } - flipqueue->restart(); + autofliplinklevel++; // =b->fliplinklevelinc; + } // while (later_unflip_queue->objects > 0) - // Swap the two flip queues. - swapqueue = flipqueue; - flipqueue = nextflipqueue; - nextflipqueue = swapqueue; + delete [] bfarray; - if (flipqueue->objects > 0l) { - // default 'b->delmaxfliplevel' is 1. - if (autofliplinklevel >= b->delmaxfliplevel) { - // For efficiency reason, we do not search too far. - break; - } - autofliplinklevel+=b->fliplinklevelinc; + if (b->verbose > 2) { + if (later_unflip_queue->objects > 0l) { + printf(" %ld non-Delaunay edges remained.\n", later_unflip_queue->objects); } - } // while (flipqueue->objects > 0l) + } + + if (flippool->items > 0l) { + // Flip locally non-Delaunay faces. Unflippable faces are queued + // in later_flip_queue. + fc.remove_ndelaunay_edge = 1; + fc.enqflag = 2; // queue exteior faces of a flip. + lawsonflip3d(&fc); + //fc.enqflag = 0; // for removedgebyflips(). + } - if (flipqueue->objects > 0l) { - if (b->verbose > 1) { - printf(" %ld non-Delaunay edges remained.\n", flipqueue->objects); + if (b->verbose > 2) { + printf(" Final obj = %.17g\n", tetprism_vol_sum); + } + + if (later_unflip_queue->objects > 0l) { + if (b->verbose > 2) { + printf(" %ld non-Delaunay edges remained.\n", later_unflip_queue->objects); } + later_unflip_queue->restart(); } - if (b->verbose) { - printf(" Final obj = %.17g\n", tetprism_vol_sum); + autofliplinklevel = bak_autofliplinklevel; // Restore this value. + b->fliplinklevel = bak_fliplinklevel; +} + +//============================================================================// +// // +// get_seg_laplacian_center() Get the Laplcian center of a mesh vertex. // +// // +// "mesh_vert" must be a Steiner vertex (FREESEGVERTEX) in a segment. // +// // +//============================================================================// + +int tetgenmesh::get_seg_laplacian_center(point mesh_vert, REAL target[3]) +{ + if (pointtype(mesh_vert) == UNUSEDVERTEX) { + return 0; } - b->flipstarsize = bakmaxflipstarsize; - delete flipqueue; - delete nextflipqueue; + face leftseg, rightseg; + + sdecode(point2sh(mesh_vert), leftseg); + leftseg.shver = 0; + if (sdest(leftseg) == mesh_vert) { + senext(leftseg, rightseg); + spivotself(rightseg); + rightseg.shver = 0; + if (sorg(rightseg) != mesh_vert) { + sesymself(rightseg); + } + if (sorg(rightseg) != mesh_vert) { + terminatetetgen(this, 2); + } + } else { + rightseg = leftseg; + senext2(rightseg, leftseg); + spivotself(leftseg); + leftseg.shver = 0; + if (sdest(leftseg) != mesh_vert) { + sesymself(leftseg); + } + if (sdest(leftseg) != mesh_vert) { + terminatetetgen(this, 2); + } + } + point lpt = sorg(leftseg); + point rpt = sdest(rightseg); + + int j; + + for (j = 0; j < 3; j++) { + target[j] = 0.5 * (lpt[j] + rpt[j]); + } + + return 1; } -/////////////////////////////////////////////////////////////////////////////// -// // -// gettetrahedron() Get a tetrahedron which have the given vertices. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// get_surf_laplacian_center() Get the Laplcian center of a mesh vertex. // +// // +// "mesh_vert" must be a Steiner vertex (FREEFACETVERTEX) in a facet. // +// // +//============================================================================// -int tetgenmesh::gettetrahedron(point pa, point pb, point pc, point pd, - triface *searchtet) +int tetgenmesh::get_surf_laplacian_center(point mesh_vert, REAL target[3]) { - triface spintet; - int t1ver; + if (pointtype(mesh_vert) == UNUSEDVERTEX) { + return 0; + } - if (getedge(pa, pb, searchtet)) { - spintet = *searchtet; - while (1) { - if (apex(spintet) == pc) { - *searchtet = spintet; - break; - } - fnextself(spintet); - if (spintet.tet == searchtet->tet) break; + getvertexstar(1, mesh_vert, caveoldtetlist, NULL, caveshlist); + + // The number of vertices is the same as the number of edges. + int npt = (int) caveshlist->objects; + int i, j; + + for (j = 0; j < 3; j++) { + target[j] = 0.; + } + + for (i = 0; i < npt; i++) { + face *cavesh = (face *) fastlookup(caveshlist, i); + point e1 = sorg(*cavesh); + point e2 = sdest(*cavesh); + for (j = 0; j < 3; j++) { + target[j] += e1[j]; } - if (apex(*searchtet) == pc) { - if (oppo(*searchtet) == pd) { - return 1; - } else { - fsymself(*searchtet); - if (oppo(*searchtet) == pd) { - return 1; - } - } + for (j = 0; j < 3; j++) { + target[j] += e2[j]; } } - return 0; + // We added every link vertex twice. + int npt2 = npt * 2; + + for (j = 0; j < 3; j++) { + target[j] /= (double) npt2; + } + + caveoldtetlist->restart(); + caveshlist->restart(); + return 1; } -/////////////////////////////////////////////////////////////////////////////// -// // -// improvequalitybyflips() Improve the mesh quality by flips. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// get_laplacian_center() Get the Laplcian center of a mesh vertex. // +// // +// "mesh_vert" must be a Steiner vertex (FREEVOLVERTEX) in volume. // +// // +//============================================================================// -long tetgenmesh::improvequalitybyflips() +int tetgenmesh::get_laplacian_center(point mesh_vert, REAL target[3]) { - arraypool *flipqueue, *nextflipqueue, *swapqueue; - badface *bface, *parybface; - triface *parytet; - point *ppt; - flipconstraints fc; - REAL *cosdd, ncosdd[6], maxdd; - long totalremcount, remcount; - int remflag; - int n, i, j, k; + if (pointtype(mesh_vert) == UNUSEDVERTEX) { + return 0; + } + getvertexstar(1, mesh_vert, caveoldtetlist, cavetetvertlist, NULL); - //assert(unflipqueue->objects > 0l); - flipqueue = new arraypool(sizeof(badface), 10); - nextflipqueue = new arraypool(sizeof(badface), 10); + // Calculate the laplacian center. + int npt = (int) cavetetvertlist->objects; + int i, j; - // Backup flip edge options. - int bakautofliplinklevel = autofliplinklevel; - int bakfliplinklevel = b->fliplinklevel; - int bakmaxflipstarsize = b->flipstarsize; + for (j = 0; j < 3; j++) { + target[j] = 0.; + } - // Set flip edge options. - autofliplinklevel = 1; - b->fliplinklevel = -1; - b->flipstarsize = 10; // b->optmaxflipstarsize; + for (i = 0; i < npt; i++) { + point *pt = (point *) fastlookup(cavetetvertlist, i); + for (j = 0; j < 3; j++) { + target[j] += (*pt)[j]; + } + } - fc.remove_large_angle = 1; - fc.unflip = 1; - fc.collectnewtets = 1; - fc.checkflipeligibility = 1; + for (j = 0; j < 3; j++) { + target[j] /= (double) npt; + } - totalremcount = 0l; + cavetetvertlist->restart(); + return 1; +} - // Swap the two flip queues. - swapqueue = flipqueue; - flipqueue = unflipqueue; - unflipqueue = swapqueue; +//============================================================================// +// // +// move_vertex() Try to move a given vertex towards the target position. // +// // +//============================================================================// - while (flipqueue->objects > 0l) { +bool tetgenmesh::move_vertex(point mesh_vert, REAL target[3]) +{ + if (pointtype(mesh_vert) == UNUSEDVERTEX) { + if (caveoldtetlist->objects > 0l) { + caveoldtetlist->restart(); + } + return 0; + } + // Do not move if the target is already very close the vertex. + if (distance(mesh_vert, target) < minedgelength) { + if (caveoldtetlist->objects > 0l) { + caveoldtetlist->restart(); + } + return 0; + } + triface* cavetet; + point pa, pb, pc; + REAL ori; + int i, j; - remcount = 0l; + REAL dir[3], newpos[3]; + REAL alpha = b->smooth_alpha; // 0.3; - while (flipqueue->objects > 0l) { - if (b->verbose > 1) { - printf(" Improving mesh qualiy by flips [%d]#: %ld.\n", - autofliplinklevel, flipqueue->objects); - } - - for (k = 0; k < flipqueue->objects; k++) { - bface = (badface *) fastlookup(flipqueue, k); - if (gettetrahedron(bface->forg, bface->fdest, bface->fapex, - bface->foppo, &bface->tt)) { - //assert(!ishulltet(bface->tt)); - // There are bad dihedral angles in this tet. - if (bface->tt.ver != 11) { - // The dihedral angles are permuted. - // Here we simply re-compute them. Slow!!. - ppt = (point *) & (bface->tt.tet[4]); - tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], bface->cent, - &bface->key, NULL); - bface->forg = ppt[0]; - bface->fdest = ppt[1]; - bface->fapex = ppt[2]; - bface->foppo = ppt[3]; - bface->tt.ver = 11; - } - if (bface->key == 0) { - // Re-comput the quality values. Due to smoothing operations. - ppt = (point *) & (bface->tt.tet[4]); - tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], bface->cent, - &bface->key, NULL); - } - cosdd = bface->cent; - remflag = 0; - for (i = 0; (i < 6) && !remflag; i++) { - if (cosdd[i] < cosmaxdihed) { - // Found a large dihedral angle. - bface->tt.ver = edge2ver[i]; // Go to the edge. - fc.cosdihed_in = cosdd[i]; - fc.cosdihed_out = 0.0; // 90 degree. - n = removeedgebyflips(&(bface->tt), &fc); - if (n == 2) { - // Edge is flipped. - remflag = 1; - if (fc.cosdihed_out < cosmaxdihed) { - // Queue new bad tets for further improvements. - for (j = 0; j < cavetetlist->objects; j++) { - parytet = (triface *) fastlookup(cavetetlist, j); - if (!isdeadtet(*parytet)) { - ppt = (point *) & (parytet->tet[4]); - // Do not test a hull tet. - if (ppt[3] != dummypoint) { - tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], ncosdd, - &maxdd, NULL); - if (maxdd < cosmaxdihed) { - // There are bad dihedral angles in this tet. - nextflipqueue->newindex((void **) &parybface); - parybface->tt.tet = parytet->tet; - parybface->tt.ver = 11; - parybface->forg = ppt[0]; - parybface->fdest = ppt[1]; - parybface->fapex = ppt[2]; - parybface->foppo = ppt[3]; - parybface->key = maxdd; - for (n = 0; n < 6; n++) { - parybface->cent[n] = ncosdd[n]; - } - } - } // if (ppt[3] != dummypoint) - } - } // j - } // if (fc.cosdihed_out < cosmaxdihed) - cavetetlist->restart(); - remcount++; - } - } - } // i - if (!remflag) { - // An unremoved bad tet. Queue it again. - unflipqueue->newindex((void **) &parybface); - *parybface = *bface; - } - } // if (gettetrahedron(...)) - } // k + for (j = 0; j < 3; j++) { + dir[j] = target[j] - mesh_vert[j]; + newpos[j] = mesh_vert[j] + alpha * dir[j]; + } - flipqueue->restart(); + if (caveoldtetlist->objects == 0l) { + getvertexstar(1, mesh_vert, caveoldtetlist, NULL, NULL); + } - // Swap the two flip queues. - swapqueue = flipqueue; - flipqueue = nextflipqueue; - nextflipqueue = swapqueue; - } // while (flipqueues->objects > 0) + bool moveflag = true; + int iter = 0; - if (b->verbose > 1) { - printf(" Removed %ld bad tets.\n", remcount); + while (iter < 3) { + for (i = 0; i < caveoldtetlist->objects; i++) { + cavetet = (triface *) fastlookup(caveoldtetlist, i); + if (ishulltet(*cavetet)) continue; // Skip a hull face. + + pa = org(*cavetet); + pb = dest(*cavetet); + pc = apex(*cavetet); + ori = orient3d(pa, pb, pc, newpos); + if (ori >= 0) { + moveflag = false; + break; // This tet becomes invalid. + } + } + if (moveflag) { + break; + } else { + alpha = (alpha / 2.); + for (j = 0; j < 3; j++) { + newpos[j] = mesh_vert[j] + alpha * dir[j]; + } + iter++; } - totalremcount += remcount; + } // while (iter < 3) - if (unflipqueue->objects > 0l) { - //if (autofliplinklevel >= b->optmaxfliplevel) { - if (autofliplinklevel >= b->optlevel) { - break; + if (moveflag) { + for (j = 0; j < 3; j++) { + mesh_vert[j] = newpos[j]; + } + + triface checkface, neightet; + //int j; + + // Push all faces of this vertex star and link into queue. + for (i = 0; i < caveoldtetlist->objects; i++) { + cavetet = (triface *) fastlookup(caveoldtetlist, i); + if (ishulltet(*cavetet)) continue; // Skip a hull face. + flippush(flipstack, cavetet); + for (j = 0; j < 3; j++) { + esym(*cavetet, checkface); + fsym(checkface, neightet); + if (!facemarked(neightet)) { + flippush(flipstack, &checkface); + } + enextself(*cavetet); + } + } + + if (badtetrahedrons != NULL) { + // queue all cavity tets for quality check. + for (i = 0; i < caveoldtetlist->objects; i++) { + cavetet = (triface *) fastlookup(caveoldtetlist, i); + if (ishulltet(*cavetet)) continue; // Skip a hull face. + enqueuetetrahedron(cavetet); } - autofliplinklevel+=b->fliplinklevelinc; - //b->flipstarsize = 10 + (1 << (b->optlevel - 1)); } - // Swap the two flip queues. - swapqueue = flipqueue; - flipqueue = unflipqueue; - unflipqueue = swapqueue; - } // while (flipqueues->objects > 0) + flipconstraints fc; + fc.enqflag = 2; // queue all exterior faces of a flip. + if (badtetrahedrons != NULL) { + fc.chkencflag = 4; // queue new tets for quality check. + } + lawsonflip3d(&fc); - // Restore original flip edge options. - autofliplinklevel = bakautofliplinklevel; - b->fliplinklevel = bakfliplinklevel; - b->flipstarsize = bakmaxflipstarsize; - delete flipqueue; - delete nextflipqueue; + } // if (moveflag) - return totalremcount; + caveoldtetlist->restart(); + return moveflag; } -/////////////////////////////////////////////////////////////////////////////// -// // -// smoothpoint() Moving a vertex to improve the mesh quality. // -// // -// 'smtpt' (p) is a point to be smoothed. Generally, it is a Steiner point. // -// It may be not a vertex of the mesh. // -// // -// This routine tries to move 'p' inside its star until a selected objective // -// function over all tetrahedra in the star is improved. The function may be // -// the some quality measures, i.e., aspect ratio, maximum dihedral angel, or // -// simply the volume of the tetrahedra. // -// // -// 'linkfacelist' contains the list of link faces of 'p'. Since a link face // -// has two orientations, ccw or cw, with respect to 'p'. 'ccw' indicates // -// the orientation is ccw (1) or not (0). // -// // -// 'opm' is a structure contains the parameters of the objective function. // -// It is needed by the evaluation of the function value. // -// // -// The return value indicates weather the point is smoothed or not. // -// // -// ASSUMPTION: This routine assumes that all link faces are true faces, i.e, // -// no face has 'dummypoint' as its vertex. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// smooth_vertices() Smooth vertices. // +// // +//============================================================================// -int tetgenmesh::smoothpoint(point smtpt, arraypool *linkfacelist, int ccw, - optparameters *opm) +void tetgenmesh::smooth_vertices() { - triface *parytet, *parytet1, swaptet; - point pa, pb, pc; - REAL fcent[3], startpt[3], nextpt[3], bestpt[3]; - REAL oldval, minval = 0.0, val; - REAL maxcosd; // oldang, newang; - REAL ori, diff; - int numdirs, iter; - int i, j, k; + if (!b->quiet) { + printf("Smoothing vertices...\n"); + } - // Decide the number of moving directions. - numdirs = (int) linkfacelist->objects; - if (numdirs > opm->numofsearchdirs) { - numdirs = opm->numofsearchdirs; // Maximum search directions. + if (b->verbose) { + printf(" Smooth criterion = %d\n", b->smooth_cirterion); + printf(" Smooth iterations = %d\n", b->smooth_maxiter); + printf(" Smooth relax-alpha = %g\n", b->smooth_alpha); } - // Set the initial value. - if (!opm->max_min_volume) { - assert(opm->initval >= 0.0); + point *smpt_list = NULL; + point *surf_smpt_list = NULL; + point *seg_smpt_list = NULL; + int volcount = 0, faccount = 0, segcount = 0; + + // Only use it when we have Steiner points. + if (st_segref_count > 0) { + seg_smpt_list = new point[st_segref_count]; + } + if (st_volref_count > 0) { + smpt_list = new point[st_volref_count]; + } + if (st_facref_count > 0) { + surf_smpt_list = new point[st_facref_count]; } - opm->imprval = opm->initval; - iter = 0; - for (i = 0; i < 3; i++) { - bestpt[i] = startpt[i] = smtpt[i]; + points->traversalinit(); + point ptloop = pointtraverse(); + while (ptloop != NULL) { + enum verttype vt = pointtype(ptloop); + if (vt == FREEVOLVERTEX) { + smpt_list[volcount++] = ptloop; + } else if (vt == FREEFACETVERTEX) { + surf_smpt_list[faccount++] = ptloop; + } else if (vt == FREESEGVERTEX) { + seg_smpt_list[segcount++] = ptloop; + } + ptloop = pointtraverse(); } - // Iterate until the obj function is not improved. - while (1) { + if ((volcount != st_volref_count) || + (faccount != st_facref_count) || + (segcount != st_segref_count)) { + terminatetetgen(this, 2); + } - // Find the best next location. - oldval = opm->imprval; + if (b->verbose > 1) { + printf(" Smoothing (%ld, %ld) %ld vertices.\n", + st_segref_count, st_facref_count, st_volref_count); + } - for (i = 0; i < numdirs; i++) { - // Randomly pick a link face (0 <= k <= objects - i - 1). - k = (int) randomnation(linkfacelist->objects - i); - parytet = (triface *) fastlookup(linkfacelist, k); - // Calculate a new position from 'p' to the center of this face. - pa = org(*parytet); - pb = dest(*parytet); - pc = apex(*parytet); - for (j = 0; j < 3; j++) { - fcent[j] = (pa[j] + pb[j] + pc[j]) / 3.0; - } - for (j = 0; j < 3; j++) { - nextpt[j] = startpt[j] + opm->searchstep * (fcent[j] - startpt[j]); + // Allocate a list of target points. + REAL *target_list = NULL; + REAL *surf_target_list = NULL; + REAL *seg_target_list = NULL; + + if (st_volref_count > 0) { + target_list = new REAL[st_volref_count * 3]; + } + if (st_facref_count > 0) { + surf_target_list = new REAL[st_facref_count * 3]; + } + if (st_segref_count > 0) { + seg_target_list = new REAL[st_segref_count * 3]; + } + + long bak_flipcount = flip23count + flip32count + flip44count; + int movedcount = 0, total_movecount = 0; + int unmovedcount = 0, total_unmovedcount = 0; + int iter = 0, maxiter = b->smooth_maxiter; + int i; + + for (iter = 0; iter < maxiter; iter++) { + + movedcount = unmovedcount = 0; + + if (((b->smooth_cirterion & 4) > 0)) { // -s4, -s5, -s6, -s7, default -s3 + //if (st_segref_count > 0) { + for (i = 0; i < st_segref_count; i++) { + get_seg_laplacian_center(seg_smpt_list[i], &(seg_target_list[i*3])); } - // Calculate the largest minimum function value for the new location. - for (j = 0; j < linkfacelist->objects; j++) { - parytet = (triface *) fastlookup(linkfacelist, j); - if (ccw) { - pa = org(*parytet); - pb = dest(*parytet); + for (i = 0; i < st_segref_count; i++) { + if (move_vertex(seg_smpt_list[i], &(seg_target_list[i*3]))) { + if (later_unflip_queue->objects > b->unflip_queue_limit) { + recoverdelaunay(); + } + movedcount++; } else { - pb = org(*parytet); - pa = dest(*parytet); + unmovedcount++; } - pc = apex(*parytet); - ori = orient3d(pa, pb, pc, nextpt); - if (ori < 0.0) { - // Calcuate the objective function value. - if (opm->max_min_volume) { - //val = -ori; - val = - orient3dfast(pa, pb, pc, nextpt); - } else if (opm->max_min_aspectratio) { - val = tetaspectratio(pa, pb, pc, nextpt); - } else if (opm->min_max_dihedangle) { - tetalldihedral(pa, pb, pc, nextpt, NULL, &maxcosd, NULL); - if (maxcosd < -1) maxcosd = -1.0; // Rounding. - val = maxcosd + 1.0; // Make it be positive. - } else { - // Unknown objective function. - val = 0.0; - } - } else { // ori >= 0.0; - // An invalid new tet. - // This may happen if the mesh contains inverted elements. - if (opm->max_min_volume) { - //val = -ori; - val = - orient3dfast(pa, pb, pc, nextpt); - } else { - // Discard this point. - break; // j + } + //} // if (st_segref_count > 0) + } + + if (((b->smooth_cirterion & 2) > 0)) { // default -s3 + //if (st_facref_count > 0) { + for (i = 0; i < st_facref_count; i++) { + get_surf_laplacian_center(surf_smpt_list[i], &(surf_target_list[i*3])); + } + for (i = 0; i < st_facref_count; i++) { + if (move_vertex(surf_smpt_list[i], &(surf_target_list[i*3]))) { + if (later_unflip_queue->objects > b->unflip_queue_limit) { + recoverdelaunay(); } - } // if (ori >= 0.0) - // Stop looping when the object value is not improved. - if (val <= opm->imprval) { - break; // j + movedcount++; } else { - // Remember the smallest improved value. - if (j == 0) { - minval = val; - } else { - minval = (val < minval) ? val : minval; - } + unmovedcount++; } - } // j - if (j == linkfacelist->objects) { - // The function value has been improved. - opm->imprval = minval; - // Save the new location of the point. - for (j = 0; j < 3; j++) bestpt[j] = nextpt[j]; } - // Swap k-th and (object-i-1)-th entries. - j = linkfacelist->objects - i - 1; - parytet = (triface *) fastlookup(linkfacelist, k); - parytet1 = (triface *) fastlookup(linkfacelist, j); - swaptet = *parytet1; - *parytet1 = *parytet; - *parytet = swaptet; - } // i + //} // if (st_facref_count > 0) + } - diff = opm->imprval - oldval; - if (diff > 0.0) { - // Is the function value improved effectively? - if (opm->max_min_volume) { - //if ((diff / oldval) < b->epsilon) diff = 0.0; - } else if (opm->max_min_aspectratio) { - if ((diff / oldval) < 1e-3) diff = 0.0; - } else if (opm->min_max_dihedangle) { - //oldang = acos(oldval - 1.0); - //newang = acos(opm->imprval - 1.0); - //if ((oldang - newang) < 0.00174) diff = 0.0; // about 0.1 degree. - } else { - // Unknown objective function. - assert(0); // Not possible. + if (((b->smooth_cirterion & 1) > 0)) { // default -s3 + //if (st_volref_count > 0) { + for (i = 0; i < st_volref_count; i++) { + get_laplacian_center(smpt_list[i], &(target_list[i*3])); + caveoldtetlist->restart(); + } + for (i = 0; i < st_volref_count; i++) { + if (move_vertex(smpt_list[i], &(target_list[i*3]))) { + if (later_unflip_queue->objects > b->unflip_queue_limit) { + recoverdelaunay(); + } + movedcount++; + } else { + unmovedcount++; + } } + //} // if (st_volref_count > 0) } - if (diff > 0.0) { - // Yes, move p to the new location and continue. - for (j = 0; j < 3; j++) startpt[j] = bestpt[j]; - iter++; - if ((opm->maxiter > 0) && (iter >= opm->maxiter)) { - // Maximum smoothing iterations reached. - break; - } - } else { - break; + if (movedcount == 0) break; + + if (b->verbose > 1) { + printf(" iter=%d, smoothed %d vertices, %d unsmoothed\n", iter, + movedcount, unmovedcount); } - } // while (1) - if (iter > 0) { - // The point has been smoothed. - opm->smthiter = iter; // Remember the number of iterations. - // The point has been smoothed. Update it to its new position. - for (i = 0; i < 3; i++) smtpt[i] = startpt[i]; + total_movecount += movedcount; + total_unmovedcount += unmovedcount; + + if (later_unflip_queue->objects > 0) { + recoverdelaunay(); + } + } // iter + + if (b->verbose > 1) { + printf(" Smoothed %d (%d) times, flipped %ld faces.\n", + total_movecount, total_unmovedcount, + flip23count + flip32count + flip44count - bak_flipcount); } - return iter; + if (st_segref_count > 0) { + delete [] seg_smpt_list; + delete [] seg_target_list; + } + if (st_facref_count > 0) { + delete [] surf_target_list; + delete [] surf_smpt_list; + } + if (st_volref_count > 0) { + delete [] target_list; + delete [] smpt_list; + } } +//============================================================================// +// // +// get_tet() Get the tetrahedron with the given vertices. // +// // +//============================================================================// -/////////////////////////////////////////////////////////////////////////////// -// // -// improvequalitysmoothing() Improve mesh quality by smoothing. // -// // -/////////////////////////////////////////////////////////////////////////////// +bool tetgenmesh::get_tet(point pa, point pb, point pc, point pd, triface *searchtet) +{ + if (getedge(pa, pb, searchtet)) { + int t1ver; + triface spintet = *searchtet; + while (1) { + if (apex(spintet) == pc) { + *searchtet = spintet; + break; + } + fnextself(spintet); + if (spintet.tet == searchtet->tet) break; + } + if (apex(*searchtet) == pc) { + if (oppo(*searchtet) == pd) { + return true; + } else { + fsymself(*searchtet); + if (oppo(*searchtet) == pd) { + return true; + } + } + } + } + + return false; +} -long tetgenmesh::improvequalitybysmoothing(optparameters *opm) +//============================================================================// +// // +// get_tetqual() Calculate various quality measures of a given tetrahedron.// +// // +// Calculate the aspect ratio (Lmax / hmin), edge ratio (Lmax/Lmin), maximal // +// and minimal dihedral angles of this tetrahedron. // +// // +// These values are returned by: // +// bf->key, aspect ratio // +// bf->cent[0], cosine of maximal dihedral angle // +// bf->cent[1], cosine of minimal dihedral angle // +// bf->cent[2], edge ratio // +// bf->cent[3], minimal edge length // +// bf->cent[4], volume (used to validate whether it is modified or not). // +// bf->cent[5], (no use). // +// bf->tet, the edge with maximal dihedral angle. // +// bf->ss.shver, (re-used) count the number of dihedrals > 165 degree. // +// // +//============================================================================// + +bool tetgenmesh::get_tetqual(triface *chktet, point oppo_pt, badface *bf) { - arraypool *flipqueue, *swapqueue; - triface *parytet; - badface *bface, *parybface; - point *ppt; - long totalsmtcount, smtcount; - int smtflag; - int iter, i, j, k; + if (chktet != NULL) { + bf->init(); + if (oppo_pt == NULL) { + point *ppt = (point *) &(chktet->tet[4]); + bf->forg = ppt[0]; // pa + bf->fdest = ppt[1]; // pb + bf->fapex = ppt[2]; // pc + bf->foppo = ppt[3]; // pd + } else { + bf->forg = org(*chktet); + bf->fdest = dest(*chktet); + bf->fapex = apex(*chktet); + bf->foppo = oppo_pt; + } + } + + REAL A[4][4], rhs[4], D; + int indx[4]; + int i, j; - //assert(unflipqueue->objects > 0l); - flipqueue = new arraypool(sizeof(badface), 10); + // get the entries of A[3][3]. + for (i = 0; i < 3; i++) A[0][i] = bf->forg[i] - bf->foppo[i]; // d->a vec + for (i = 0; i < 3; i++) A[1][i] = bf->fdest[i] - bf->foppo[i]; // d->b vec + for (i = 0; i < 3; i++) A[2][i] = bf->fapex[i] - bf->foppo[i]; // d->c vec + + // Get the max-min edge length + REAL L[6], Lmax, Lmin; + REAL Vab[3], Vbc[3], Vca[3]; + + for (i = 0; i < 3; i++) Vab[i] = bf->fdest[i] - bf->forg[i]; // a->b vec + for (i = 0; i < 3; i++) Vbc[i] = bf->fapex[i] - bf->fdest[i]; // b->c vec + for (i = 0; i < 3; i++) Vca[i] = bf->forg[i] - bf->fapex[i]; // c->a vec + + // Use the idx2edge + L[0] = dot(A[2], A[2]); // edge c,d + L[1] = dot(A[0], A[0]); // edge a,d + L[2] = dot(Vab, Vab); // edge a,b + L[3] = dot(Vbc, Vbc); // edge b,c + L[4] = dot(A[1], A[1]); // edge b,d + L[5] = dot(Vca, Vca); // edge a,c + + Lmax = Lmin = L[0]; + //int idx = 0; + for (i = 1; i < 6; i++) { + Lmax = (Lmax < L[i] ? L[i] : Lmax); + Lmin = (Lmin > L[i] ? L[i] : Lmin); + //if (Lmin > L[i]) { + // Lmin = L[i]; idx = i; + //} + } - // Swap the two flip queues. - swapqueue = flipqueue; - flipqueue = unflipqueue; - unflipqueue = swapqueue; + Lmax = sqrt(Lmax); + Lmin = sqrt(Lmin); - totalsmtcount = 0l; - iter = 0; + // Caluclate the Lmax / Lmin edge ratio (to detect very short edge). + bf->cent[2] = Lmax / Lmin; + bf->cent[3] = Lmin; - while (flipqueue->objects > 0l) { + // Calculate the normals and heights. + REAL N[4][3]; // The normals of the four faces. + REAL H[4]; // H[i] is the inverse of the height of its corresponding face. + bool flat_flag = false; - smtcount = 0l; + if (lu_decmp(A, 3, indx, &D, 0)) { + // Get the volume of this tet. + bf->cent[4] = fabs((A[indx[0]][0] * A[indx[1]][1] * A[indx[2]][2])); + if (bf->cent[4] > 0.0) { + // Compute the inverse of matrix A, to get 3 normals of the 4 faces. + for (j = 0; j < 3; j++) { + for (i = 0; i < 3; i++) rhs[i] = 0.0; + rhs[j] = 1.0; // Positive means the inside direction + lu_solve(A, 3, indx, rhs, 0); + for (i = 0; i < 3; i++) N[j][i] = rhs[i]; + } + // Get the fourth normal by summing up the first three. + for (i = 0; i < 3; i++) N[3][i] = - N[0][i] - N[1][i] - N[2][i]; + } else { + // This is a very flat tet. + flat_flag = true; + } + } else { + flat_flag = true; + } + + if (flat_flag) { + // This tet is nearly degenerate. + bf->cent[4] = orient3d(bf->fdest, bf->forg, bf->fapex, bf->foppo); + if (bf->cent[4] <= 0.0) { + return false; // degenerated or inverted. + } + // Calculate the normals of the four faces. + facenormal(bf->fapex, bf->fdest, bf->foppo, N[0], 1, NULL); // face [c,b,d] + facenormal(bf->forg, bf->fapex, bf->foppo, N[1], 1, NULL); // face [a,c,d] + facenormal(bf->fdest, bf->forg, bf->foppo, N[2], 1, NULL); // face [b,a,d] + facenormal(bf->forg, bf->fdest, bf->fapex, N[3], 1, NULL); // face [a,b,c] + } // if (!success) - if (b->verbose > 1) { - printf(" Improving mesh quality by smoothing [%d]#: %ld.\n", - iter, flipqueue->objects); - } - - for (k = 0; k < flipqueue->objects; k++) { - bface = (badface *) fastlookup(flipqueue, k); - if (gettetrahedron(bface->forg, bface->fdest, bface->fapex, - bface->foppo, &bface->tt)) { - // Operate on it if it is not in 'unflipqueue'. - if (!marktested(bface->tt)) { - // Here we simply re-compute the quality. Since other smoothing - // operation may have moved the vertices of this tet. - ppt = (point *) & (bface->tt.tet[4]); - tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], bface->cent, - &bface->key, NULL); - if (bface->key < cossmtdihed) { // if (maxdd < cosslidihed) { - // It is a sliver. Try to smooth its vertices. - smtflag = 0; - opm->initval = bface->key + 1.0; - for (i = 0; (i < 4) && !smtflag; i++) { - if (pointtype(ppt[i]) == FREEVOLVERTEX) { - getvertexstar(1, ppt[i], cavetetlist, NULL, NULL); - opm->searchstep = 0.001; // Search step size - smtflag = smoothpoint(ppt[i], cavetetlist, 1, opm); - if (smtflag) { - while (opm->smthiter == opm->maxiter) { - opm->searchstep *= 10.0; // Increase the step size. - opm->initval = opm->imprval; - opm->smthiter = 0; // reset - smoothpoint(ppt[i], cavetetlist, 1, opm); - } - // This tet is modifed. - smtcount++; - if ((opm->imprval - 1.0) < cossmtdihed) { - // There are slivers in new tets. Queue them. - for (j = 0; j < cavetetlist->objects; j++) { - parytet = (triface *) fastlookup(cavetetlist, j); - assert(!isdeadtet(*parytet)); - // Operate it if it is not in 'unflipqueue'. - if (!marktested(*parytet)) { - // Evaluate its quality. - // Re-use ppt, bface->key, bface->cent. - ppt = (point *) & (parytet->tet[4]); - tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], - bface->cent, &bface->key, NULL); - if (bface->key < cossmtdihed) { - // A new sliver. Queue it. - marktest(*parytet); // It is in unflipqueue. - unflipqueue->newindex((void **) &parybface); - parybface->tt = *parytet; - parybface->forg = ppt[0]; - parybface->fdest = ppt[1]; - parybface->fapex = ppt[2]; - parybface->foppo = ppt[3]; - parybface->tt.ver = 11; - parybface->key = 0.0; - } - } - } // j - } // if ((opm->imprval - 1.0) < cossmtdihed) - } // if (smtflag) - cavetetlist->restart(); - } // if (pointtype(ppt[i]) == FREEVOLVERTEX) - } // i - if (!smtflag) { - // Didn't smooth. Queue it again. - marktest(bface->tt); // It is in unflipqueue. - unflipqueue->newindex((void **) &parybface); - parybface->tt = bface->tt; - parybface->forg = ppt[0]; - parybface->fdest = ppt[1]; - parybface->fapex = ppt[2]; - parybface->foppo = ppt[3]; - parybface->tt.ver = 11; - parybface->key = 0.0; - } - } // if (maxdd < cosslidihed) - } // if (!marktested(...)) - } // if (gettetrahedron(...)) - } // k - - flipqueue->restart(); - - // Unmark the tets in unflipqueue. - for (i = 0; i < unflipqueue->objects; i++) { - bface = (badface *) fastlookup(unflipqueue, i); - unmarktest(bface->tt); + // Normalized the normals. + for (i = 0; i < 4; i++) { + H[i] = sqrt(dot(N[i], N[i])); + if (H[i] > 0.0) { + for (j = 0; j < 3; j++) N[i][j] /= H[i]; + } else { + return false; // H[i] == 0.0; } + } - if (b->verbose > 1) { - printf(" Smooth %ld points.\n", smtcount); + if (!flat_flag) { + // Get the biggest H[i] (corresponding to the smallest height). + REAL minheightinv = H[0]; + for (i = 1; i < 4; i++) { + if (H[i] > minheightinv) minheightinv = H[i]; } - totalsmtcount += smtcount; + // Calulcate the aspect ratio = L_max / h_min. + bf->key = Lmax * minheightinv; + } else { + // A very flat tet. + //if (bf->key <= 0.0) { + bf->key = 1.e+30; // infinity. + //} + } - if (smtcount == 0l) { - // No point has been smoothed. - break; - } else { - iter++; - if (iter == 2) { //if (iter >= b->optpasses) { - break; - } + // Calculate the cosine of the dihedral angles of the edges. + REAL cosmaxd = 1.0, cosmind = -1.0, cosd; + int f1, f2, idx = 0; + bf->ss.shver = 0; // // Count the number of large dihedrals. + for (i = 0; i < 6; i++) { + switch (i) { + case 0: f1 = 0; f2 = 1; break; // [c,d]. + case 1: f1 = 1; f2 = 2; break; // [a,d]. + case 2: f1 = 2; f2 = 3; break; // [a,b]. + case 3: f1 = 0; f2 = 3; break; // [b,c]. + case 4: f1 = 2; f2 = 0; break; // [b,d]. + case 5: f1 = 1; f2 = 3; break; // [a,c]. } + cosd = -dot(N[f1], N[f2]); + if (cosd < -1.0) cosd = -1.0; // Rounding. + if (cosd > 1.0) cosd = 1.0; // Rounding. + // cosmaxd = cosd < cosmaxd ? cosd : cosmaxd; + if (cosd < cosmaxd) {cosmaxd = cosd; idx = i;} + cosmind = (cosd > cosmind ? cosd : cosmind); + // Count the number of large dihedrals. + if (cosd < cos_large_dihed) bf->ss.shver++; + } // i - // Swap the two flip queues. - swapqueue = flipqueue; - flipqueue = unflipqueue; - unflipqueue = swapqueue; - } // while + bf->cent[0] = cosmaxd; + bf->cent[1] = cosmind; + + // Remember the edge with largest dihedral angle. + if (chktet) bf->tt.tet = chktet->tet; + bf->tt.ver = edge2ver[idx]; - delete flipqueue; + bf->cent[5] = 0.0; - return totalsmtcount; + return true; } -/////////////////////////////////////////////////////////////////////////////// -// // -// splitsliver() Split a sliver. // -// // -/////////////////////////////////////////////////////////////////////////////// +bool tetgenmesh::get_tetqual(point pa, point pb, point pc, point pd, badface *bf) +{ + bf->init(); -int tetgenmesh::splitsliver(triface *slitet, REAL cosd, int chkencflag) + bf->forg = pa; + bf->fdest = pb; + bf->fapex = pc; + bf->foppo = pd; + + return get_tetqual(NULL, NULL, bf); +} + +//============================================================================// +// // +// enqueue_badtet() Push a bad-quality tet into the proority queue. // +// // +//============================================================================// + +void tetgenmesh:: enqueue_badtet(badface *bf) { - triface *abtets; - triface searchtet, spintet, *parytet; - point pa, pb, steinerpt; - optparameters opm; - insertvertexflags ivf; - REAL smtpt[3], midpt[3]; - int success; - int t1ver; - int n, i; + badface *bt = (badface *) badqual_tets_pool->alloc(); - // 'slitet' is [c,d,a,b], where [c,d] has a big dihedral angle. - // Go to the opposite edge [a,b]. - edestoppo(*slitet, searchtet); // [a,b,c,d]. + *bt = *bf; - // Do not split a segment. - if (issubseg(searchtet)) { - return 0; - } + // The following vertices are used by get_tet(...) to identify whether + // the saved tet is still alive or not. + //bt->forg = org(bf->tt); + //bt->fdest = dest(bf->tt); + //bt->fapex = apex(bf->tt); + //bt->foppo = oppo(bf->tt); - // Count the number of tets shared at [a,b]. - // Do not split it if it is a hull edge. - spintet = searchtet; - n = 0; - while (1) { - if (ishulltet(spintet)) break; - n++; - fnextself(spintet); - if (spintet.tet == searchtet.tet) break; + bt->nextitem = NULL; // important, this pointer is used to recongise the last + // item in each queue. + + // Push it into the priority queue. + REAL qual = 1.0 / log(bf->key); + + // Determine the appropriate queue to put the bad subface into. + int queuenumber = 0; + if (qual < 1.0) { + queuenumber = (int) (64.0 * (1.0 - qual)); + if (queuenumber > 63) { + queuenumber = 63; + } + } else { + // It's not a bad shape; put the subface in the lowest-priority queue. + queuenumber = 0; } - if (ishulltet(spintet)) { - return 0; // It is a hull edge. + + // Are we inserting into an empty queue? + if (bt_queuefront[queuenumber] == (badface *) NULL) { + // Yes, we are inserting into an empty queue. + // Will this become the highest-priority queue? + if (queuenumber > bt_firstnonemptyq) { + // Yes, this is the highest-priority queue. + bt_nextnonemptyq[queuenumber] = bt_firstnonemptyq; + bt_firstnonemptyq = queuenumber; + } else { + // No, this is not the highest-priority queue. + // Find the queue with next higher priority. + int i = queuenumber + 1; + while (bt_queuefront[i] == (badface *) NULL) { + i++; + } + // Mark the newly nonempty queue as following a higher-priority queue. + bt_nextnonemptyq[queuenumber] = bt_nextnonemptyq[i]; + bt_nextnonemptyq[i] = queuenumber; + } + // Put the bad subface at the beginning of the (empty) queue. + bt_queuefront[queuenumber] = bt; + } else { + // Add the bad tetrahedron to the end of an already nonempty queue. + bt_queuetail[queuenumber]->nextitem = bt; } - assert(n >= 3); + // Maintain a pointer to the last subface of the queue. + bt_queuetail[queuenumber] = bt; +} - // Get all tets at edge [a,b]. - abtets = new triface[n]; - spintet = searchtet; - for (i = 0; i < n; i++) { - abtets[i] = spintet; - fnextself(spintet); - } +//============================================================================// +// // +// top_badtet() Get a bad-quality tet from the priority queue. // +// // +//============================================================================// - // Initialize the list of 2n boundary faces. - for (i = 0; i < n; i++) { - eprev(abtets[i], searchtet); - esymself(searchtet); // [a,p_i,p_i+1]. - cavetetlist->newindex((void **) &parytet); - *parytet = searchtet; - enext(abtets[i], searchtet); - esymself(searchtet); // [p_i,b,p_i+1]. - cavetetlist->newindex((void **) &parytet); - *parytet = searchtet; +tetgenmesh::badface* tetgenmesh::top_badtet() +{ + // Keep a record of which queue was accessed in case dequeuebadtetra() + // is called later. + bt_recentq = bt_firstnonemptyq; + // If no queues are nonempty, return NULL. + if (bt_firstnonemptyq < 0) { + return (badface *) NULL; + } else { + // Return the first tetrahedron of the highest-priority queue. + return bt_queuefront[bt_firstnonemptyq]; } +} - // Init the Steiner point at the midpoint of edge [a,b]. - pa = org(abtets[0]); - pb = dest(abtets[0]); - for (i = 0; i < 3; i++) { - smtpt[i] = midpt[i] = 0.5 * (pa[i] + pb[i]); +//============================================================================// +// // +// dequeue_badtet() Popup a bad-quality tet from the priority queue. // +// // +//============================================================================// + +void tetgenmesh::dequeue_badtet() +{ + badface *bt; + int i; + + // If queues were empty last time topbadtetra() was called, do nothing. + if (bt_recentq >= 0) { + // Find the tetrahedron last returned by topbadtetra(). + bt = bt_queuefront[bt_recentq]; + // Remove the tetrahedron from the queue. + bt_queuefront[bt_recentq] = bt->nextitem; + // If this queue is now empty, update the list of nonempty queues. + if (bt == bt_queuetail[bt_recentq]) { + // Was this the highest-priority queue? + if (bt_firstnonemptyq == bt_recentq) { + // Yes; find the queue with next lower priority. + bt_firstnonemptyq = bt_nextnonemptyq[bt_firstnonemptyq]; + } else { + // No; find the queue with next higher priority. + i = bt_recentq + 1; + while (bt_queuefront[i] == (badface *) NULL) { + i++; + } + bt_nextnonemptyq[i] = bt_nextnonemptyq[bt_recentq]; + } + } + // Return the badface to the pool. + badqual_tets_pool->dealloc((void *) bt); } +} - // Point smooth options. - opm.min_max_dihedangle = 1; - opm.initval = cosd + 1.0; // Initial volume is zero. - opm.numofsearchdirs = 20; - opm.searchstep = 0.001; - opm.maxiter = 100; // Limit the maximum iterations. - success = smoothpoint(smtpt, cavetetlist, 1, &opm); +//============================================================================// +// // +// add_steinerpt_to_repair() Add Steiner to repair a bad-qaulity tet. // +// // +//============================================================================// + +bool tetgenmesh::add_steinerpt_to_repair(badface *bf, bool bSmooth) +{ + REAL cosmaxd = bf->cent[0]; + REAL eta = bf->cent[2]; + int lcount = bf->ss.shver; // the number of large dihedrals. + + triface splittet; + splittet.tet = NULL; + + if (cosmaxd < cosslidihed) { // cossmtdihed + // It is a sliver (flat) (might contain a short edge -- skinny). + triface sliver_edge; + char shape = 0; + + // Determine the outer shape of this sliver, i.e., a square of a triangle? + if (lcount == 2) { + // It is a square. Try to remove the edge [a,b] + shape = 'S'; + sliver_edge = bf->tt; + } else if (lcount == 3) { + // It is a triangle. Try to remove the edge [c,d] + shape = 'T'; + edestoppo(bf->tt, sliver_edge); // face [c,d,a] + } + + // Determine a Steiner point according to the shape of this sliver. + if (shape == 'S') { + REAL vol, max_vol = 0.0; + + triface check_sliver = sliver_edge; + for (int i = 0; i < 2; i++) { + bool is_bdry = false; + if (issubseg(check_sliver)) { + is_bdry = true; + } else { + triface spintet = check_sliver; + int t1ver; + do { + if (issubface(spintet)) { + is_bdry = true; break; + } + fnextself(spintet); + } while (spintet.tet != check_sliver.tet); + } + + if (!is_bdry) { + triface spintet = check_sliver; + int t1ver; + do { + point *ppt = (point *) &(spintet.tet[4]); + vol = orient3d(ppt[1], ppt[0], ppt[2], ppt[3]); + if (vol > max_vol) { + max_vol = vol; + splittet = spintet; + } + fnextself(spintet); + } while (spintet.tet != check_sliver.tet); + } + + // Check the opposite edge. + edestoppoself(check_sliver); + } // i + } else if (shape == 'T') { + } + } else if (eta > b->opt_max_edge_ratio) { + // It is a skinny tet. + // This tet contains a relatively short edge. Check if it can be collapsed. + REAL Lmin = bf->cent[3]; - if (success) { - while (opm.smthiter == opm.maxiter) { - // It was relocated and the prescribed maximum iteration reached. - // Try to increase the search stepsize. - opm.searchstep *= 10.0; - //opm.maxiter = 100; // Limit the maximum iterations. - opm.initval = opm.imprval; - opm.smthiter = 0; // Init. - smoothpoint(smtpt, cavetetlist, 1, &opm); + // Get the shortest edge of this tet. + triface short_edge = bf->tt; + int i; + for (i = 0; i < 6; i++) { + short_edge.ver = edge2ver[i]; + REAL dd = distance(org(short_edge), dest(short_edge)); + if ((fabs(Lmin - dd) / Lmin) < 1e-4) break; + } + if (i == 6) { + terminatetetgen(this, 2); } - } // if (success) - cavetetlist->restart(); + if (Lmin <= minedgelength) { + // A very short edge. Check if it was correctly created. + point e1 = org(short_edge); + point e2 = dest(short_edge); + if (issteinerpoint(e1)) { + if (!create_a_shorter_edge(e1, e2)) { + terminatetetgen(this, 2); + } + } else if (issteinerpoint(e2)) { + if (!create_a_shorter_edge(e2, e1)) { + terminatetetgen(this, 2); + } + } + } + } - if (!success) { - delete [] abtets; - return 0; + if (splittet.tet == NULL) { + return false; // not added. } + // Do not add if the splittet is also a bad qual tet. + badface tmpbf; + if (get_tetqual(&splittet, NULL, &tmpbf)) { + if (tmpbf.cent[0] < cosslidihed) { + return false; + } + } else { + return false; + } - // Insert the Steiner point. + point steinerpt; makepoint(&steinerpt, FREEVOLVERTEX); - for (i = 0; i < 3; i++) steinerpt[i] = smtpt[i]; - - // Insert the created Steiner point. - for (i = 0; i < n; i++) { - infect(abtets[i]); - caveoldtetlist->newindex((void **) &parytet); - *parytet = abtets[i]; + point *ppt = (point *) &(splittet.tet[4]); + for (int j = 0; j < 3; j++) { + steinerpt[j] = (ppt[0][j]+ppt[1][j]+ppt[2][j]+ppt[3][j]) / 4.0; } - searchtet = abtets[0]; // No need point location. - if (b->metric) { - locate(steinerpt, &searchtet); // For size interpolation. + insertvertexflags ivf; + + //triface searchtet = splittet; + ivf.iloc = (int) OUTSIDE; + ivf.bowywat = 3; + ivf.lawson = 2; + ivf.rejflag = 0; + if (badtetrahedrons != NULL) { + ivf.chkencflag = 4; // queue new tets. } + ivf.sloc = ivf.sbowywat = 0; // No use. + ivf.splitbdflag = 0; // No use (its an interior vertex). + ivf.validflag = 1; + ivf.respectbdflag = 1; - delete [] abtets; + ivf.smlenflag = 1; // avoid creating very short edges + ivf.parentpt = NULL; // init. - ivf.iloc = (int) INSTAR; - ivf.chkencflag = chkencflag; - ivf.assignmeshsize = b->metric; + if (insertpoint(steinerpt, &splittet, NULL, NULL, &ivf)) { + st_volref_count++; + //if (steinerleft > 0) steinerleft--; + if (flipstack != NULL) { + flipconstraints fc; + fc.enqflag = 2; + if (badtetrahedrons != NULL) { + fc.chkencflag = 4; + } + lawsonflip3d(&fc); + } - if (insertpoint(steinerpt, &searchtet, NULL, NULL, &ivf)) { - // The vertex has been inserted. - st_volref_count++; - if (steinerleft > 0) steinerleft--; - return 1; + if (later_unflip_queue->objects > b->unflip_queue_limit) { + //recoverdelaunay(); + later_unflip_queue->restart(); // clean it. + } } else { - // The Steiner point is too close to an existing vertex. Reject it. + // Point is not inserted. pointdealloc(steinerpt); - return 0; + return false; + } + + if (bSmooth) { + REAL ccent[3]; + get_laplacian_center(steinerpt, ccent); + if (move_vertex(steinerpt, ccent)) { + opt_smooth_count++; + } + } // if (bSmooth) + + if (badtetrahedrons->items > 0) { + // Push new bad quality tetrahedron into queue. + badface bf; + REAL max_asp = 0., cosmaxd = 1.; + badtetrahedrons->traversalinit(); + triface *bface = (triface *) badtetrahedrons->traverse(); + while (bface != NULL) { + if (!isdeadtet(*bface)) { + // A queued tet may have been processed. + if (marktest2ed(*bface)) { + unmarktest2(*bface); + if (!ishulltet(*bface)) { + get_tetqual(bface, NULL, &bf); + // Save the worst quality. + max_asp = (max_asp > bf.key ? max_asp : bf.key); + cosmaxd = (cosmaxd < bf.cent[0] ? cosmaxd : bf.cent[0]); + if ((bf.key > b->opt_max_asp_ratio) || (bf.cent[0] < cosmaxdihed)) { + bf.forg = org(bf.tt); + bf.fdest = dest(bf.tt); + bf.fapex = apex(bf.tt); + bf.foppo = oppo(bf.tt); + enqueue_badtet(&bf); + } + } // if (!ishulltet(*bface)) + } + } + bface = (triface *) badtetrahedrons->traverse(); + } + badtetrahedrons->restart(); + } + + // Check if the bad quality tet is removed or not. + if (get_tet(bf->forg, bf->fdest, bf->fapex, bf->foppo, &(bf->tt))) { + // Try to remove it. + if (repair_tet(bf, true, false, false)) { + return true; + } + } else { + // This tet is removed. + return true; } + + return false; // not added. } -/////////////////////////////////////////////////////////////////////////////// -// // -// removeslivers() Remove slivers by adding Steiner points. // -// // -/////////////////////////////////////////////////////////////////////////////// -long tetgenmesh::removeslivers(int chkencflag) -{ - arraypool *flipqueue, *swapqueue; - badface *bface, *parybface; - triface slitet, *parytet; - point *ppt; - REAL cosdd[6], maxcosd; - long totalsptcount, sptcount; - int iter, i, j, k; - //assert(unflipqueue->objects > 0l); - flipqueue = new arraypool(sizeof(badface), 10); - // Swap the two flip queues. - swapqueue = flipqueue; - flipqueue = unflipqueue; - unflipqueue = swapqueue; - totalsptcount = 0l; - iter = 0; - while ((flipqueue->objects > 0l) && (steinerleft != 0)) { +//============================================================================// +// // +// flip_edge_to_improve() Flip an edge of a bad-quality tet. // +// // +//============================================================================// - sptcount = 0l; +bool tetgenmesh::flip_edge_to_improve(triface *sliver_edge, REAL& improved_cosmaxd) +{ + if (issubseg(*sliver_edge)) { + return false; + } - if (b->verbose > 1) { - printf(" Splitting bad quality tets [%d]#: %ld.\n", - iter, flipqueue->objects); - } - - for (k = 0; (k < flipqueue->objects) && (steinerleft != 0); k++) { - bface = (badface *) fastlookup(flipqueue, k); - if (gettetrahedron(bface->forg, bface->fdest, bface->fapex, - bface->foppo, &bface->tt)) { - if ((bface->key == 0) || (bface->tt.ver != 11)) { - // Here we need to re-compute the quality. Since other smoothing - // operation may have moved the vertices of this tet. - ppt = (point *) & (bface->tt.tet[4]); - tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], bface->cent, - &bface->key, NULL); - } - if (bface->key < cosslidihed) { - // It is a sliver. Try to split it. - slitet.tet = bface->tt.tet; - //cosdd = bface->cent; - for (j = 0; j < 6; j++) { - if (bface->cent[j] < cosslidihed) { - // Found a large dihedral angle. - slitet.ver = edge2ver[j]; // Go to the edge. - if (splitsliver(&slitet, bface->cent[j], chkencflag)) { - sptcount++; - break; - } - } - } // j - if (j < 6) { - // A sliver is split. Queue new slivers. - badtetrahedrons->traversalinit(); - parytet = (triface *) badtetrahedrons->traverse(); - while (parytet != NULL) { - unmarktest2(*parytet); - ppt = (point *) & (parytet->tet[4]); - tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], cosdd, - &maxcosd, NULL); - if (maxcosd < cosslidihed) { - // A new sliver. Queue it. - unflipqueue->newindex((void **) &parybface); - parybface->forg = ppt[0]; - parybface->fdest = ppt[1]; - parybface->fapex = ppt[2]; - parybface->foppo = ppt[3]; - parybface->tt.tet = parytet->tet; - parybface->tt.ver = 11; - parybface->key = maxcosd; - for (i = 0; i < 6; i++) { - parybface->cent[i] = cosdd[i]; - } - } - parytet = (triface *) badtetrahedrons->traverse(); + flipconstraints fc; + + //fc.noflip_in_surface = 1; // do not flip in surface. + fc.noflip_in_surface = ((b->nobisect > 0) || ((b->cdtrefine & 2) == 0)); + fc.remove_large_angle = 1; + fc.unflip = 1; + fc.collectnewtets = 1; + fc.checkflipeligibility = 1; + fc.cosdihed_in = improved_cosmaxd; // cosmaxd; + fc.cosdihed_out = 0.0; // 90 degree. + fc.max_asp_out = 0.0; + + if (removeedgebyflips(sliver_edge, &fc) == 2) { + // This sliver is removed by flips. + if ((fc.cosdihed_out < cosmaxdihed) || (fc.max_asp_out > b->opt_max_asp_ratio)) { + // Queue new bad tets for further improvements. + badface bf; + for (int j = 0; j < cavetetlist->objects; j++) { + triface *parytet = (triface *) fastlookup(cavetetlist, j); + if (!isdeadtet(*parytet) && !ishulltet(*parytet)) { + if (get_tetqual(parytet, NULL, &bf)) { + if ((bf.key > b->opt_max_asp_ratio) || (bf.cent[0] < cosmaxdihed)) { + bf.forg = org(bf.tt); + bf.fdest = dest(bf.tt); + bf.fapex = apex(bf.tt); + bf.foppo = oppo(bf.tt); + enqueue_badtet(&bf); } - badtetrahedrons->restart(); } else { - // Didn't split. Queue it again. - unflipqueue->newindex((void **) &parybface); - *parybface = *bface; - } // if (j == 6) - } // if (bface->key < cosslidihed) - } // if (gettetrahedron(...)) - } // k + terminatetetgen(this, 2); + } + } + } // j + } + cavetetlist->restart(); + return true; + } - flipqueue->restart(); + return false; +} - if (b->verbose > 1) { - printf(" Split %ld tets.\n", sptcount); +//============================================================================// +// // +// repair_tet() Repair a bad-qaulity tet. // +// // +//============================================================================// + +bool tetgenmesh::repair_tet(badface *bf, bool bFlips, bool bSmooth, bool bSteiners) +{ + REAL cosmaxd = bf->cent[0]; + REAL eta = bf->cent[2]; + int lcount = bf->ss.shver; // the number of large dihedrals. + + if (cosmaxd < cossmtdihed) { + // It is a sliver (flat) (it might contain a short edge -- skinny). + //triface sliver_edge; + char shape = '0'; + + // Determine the outer shape of this sliver, i.e., a square of a triangle? + if (lcount == 2) { + // It is a square. Try to remove the edge [a,b] + shape = 'S'; + } else if (lcount == 3) { + // It is a triangle. Try to remove the edge [c,d] + shape = 'T'; + //edestoppo(bf->tt, sliver_edge); // face [c,d,a] + } + + if (bFlips) { + if (shape == 'S') { + triface sliver_edge = bf->tt; + if (flip_edge_to_improve(&sliver_edge, cosmaxd)) { + opt_flips_count++; + return true; + } + // Due to 'unflip', the flip function may modify the sliver. + if (get_tet(bf->forg, bf->fdest, bf->fapex, bf->foppo, &(bf->tt))) { + // Try to flip the opposite edge of this sliver. + edestoppo(bf->tt, sliver_edge); // face [c,d,a] + if (flip_edge_to_improve(&sliver_edge, cosmaxd)) { + opt_flips_count++; + return true; + } + } + } else if (shape == 'T') { + triface sliver_edge; + // flip_face_to_improve(...) + edestoppo(bf->tt, sliver_edge); // face [c,d,a] + if (flip_edge_to_improve(&sliver_edge, cosmaxd)) { + opt_flips_count++; + return true; + } + } + } + } else if (eta > b->opt_max_edge_ratio) { + // It is a skinny tet. + // This tet contains a relatively short edge. Check if it can be collapsed. + REAL Lmin = bf->cent[3]; + + // Get the shortest edge of this tet. + triface short_edge = bf->tt; + int i; + for (i = 0; i < 6; i++) { + short_edge.ver = edge2ver[i]; + REAL dd = distance(org(short_edge), dest(short_edge)); + //if (fabs(Lmin - dd) < 1e-8) break; + if ((fabs(Lmin - dd) / Lmin) < 1e-4) break; + } + if (i == 6) { + terminatetetgen(this, 2); } - totalsptcount += sptcount; - if (sptcount == 0l) { - // No point has been smoothed. - break; - } else { - iter++; - if (iter == 2) { //if (iter >= b->optpasses) { - break; + + if (Lmin <= minedgelength) { + // A very short edge. Check if it was correctly created. + point e1 = org(short_edge); + point e2 = dest(short_edge); + if (issteinerpoint(e1)) { + if (!create_a_shorter_edge(e1, e2)) { + terminatetetgen(this, 2); + } + } else if (issteinerpoint(e2)) { + if (!create_a_shorter_edge(e2, e1)) { + terminatetetgen(this, 2); + } } } - // Swap the two flip queues. - swapqueue = flipqueue; - flipqueue = unflipqueue; - unflipqueue = swapqueue; - } // while + } else { + // It is neither a flat nor skinny tet. While it has a large asp. - delete flipqueue; + } + + + if (bSteiners && + ((bf->key > opt_max_sliver_asp_ratio) || (cosmaxd < cosslidihed))) { + // This sliver is not removed. Due to 'unflip', the flip function may + // modify the sliver. + if (get_tet(bf->forg, bf->fdest, bf->fapex, bf->foppo, &(bf->tt))) { + if (add_steinerpt_to_repair(bf, bSmooth)) { + return true; + } + } + } // if (bSteiners) - return totalsptcount; + return false; // not repaired } -/////////////////////////////////////////////////////////////////////////////// -// // -// optimizemesh() Optimize mesh for specified objective functions. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// repair_badqual_tets() Repair all queued bad quality tet. // +// // +//============================================================================// -void tetgenmesh::optimizemesh() +long tetgenmesh::repair_badqual_tets(bool bFlips, bool bSmooth, bool bSteiners) { - badface *parybface; - triface checktet; - point *ppt; - int optpasses; - optparameters opm; - REAL ncosdd[6], maxdd; - long totalremcount, remcount; - long totalsmtcount, smtcount; - long totalsptcount, sptcount; - int chkencflag; - int iter; - int n; + if (b->verbose > 1) { + printf(" Repairing %ld bad quality tets.\n", badqual_tets_pool->items); + } + long repaired_count = 0l; + + while (badqual_tets_pool->items > 0) { + + // Get a badtet of highest priority. + badface *bt = top_badtet(); + if (get_tet(bt->forg, bt->fdest, bt->fapex, bt->foppo, &(bt->tt))) { + if (repair_tet(bt, bFlips, bSmooth, bSteiners)) { + repaired_count++; + } else { + // Failed to repair this tet. Save it. + badface *bf = NULL; + unsplit_badtets->newindex((void **) &bf); + *bf = *bt; + } + } // if (get_tet(...)) + + // Return the badtet to the pool. + dequeue_badtet(); + } // while (badqual_tets_pool->items > 0) + + if (unsplit_badtets->objects > 0l) { + // Re-initialise the priority queue + for (int i = 0; i < 64; i++) { + bt_queuefront[i] = bt_queuetail[i] = NULL; + } + bt_firstnonemptyq = -1; + bt_recentq = -1; + + for (int i = 0; i < unsplit_badtets->objects; i++) { + badface *bt = (badface *) fastlookup(unsplit_badtets, i); + enqueue_badtet(bt); + } + unsplit_badtets->restart(); + } // if (unsplit_badtets->objects > 0l) + + return repaired_count; +} + +//============================================================================// +// // +// improve_mesh() Mesh improvement. // +// // +//============================================================================// + +void tetgenmesh::improve_mesh() +{ if (!b->quiet) { - printf("Optimizing mesh...\n"); + printf("Improving mesh...\n"); } - optpasses = ((1 << b->optlevel) - 1); - if (b->verbose) { - printf(" Optimization level = %d.\n", b->optlevel); - printf(" Optimization scheme = %d.\n", b->optscheme); - printf(" Number of iteration = %d.\n", optpasses); - printf(" Min_Max dihed angle = %g.\n", b->optmaxdihedral); + printf(" Target maximum aspect ratio = %g.\n", b->opt_max_asp_ratio); + printf(" Target maximum dihedral angle = %g.\n", b->optmaxdihedral); + printf(" Maximum flip level = %d.\n", b->opt_max_flip_level); // -O# + printf(" Number of iterations = %d.\n", b->opt_iterations); // -O///# + } + + long blt = b->tetrahedraperblock; + badqual_tets_pool = new memorypool(sizeof(badface), blt, sizeof(void *), 0); + badtetrahedrons = new memorypool(sizeof(triface), blt, sizeof(void *), 0); + unsplit_badtets = new arraypool(sizeof(badface), 10); + + for (int i = 0; i < 64; i++) { + bt_queuefront[i] = NULL; + } + bt_firstnonemptyq = -1; + bt_recentq = -1; + + cos_large_dihed = cos(135. / 180. * PI); // used in get_tetqual + + cosmaxdihed = cos(b->optmaxdihedral / 180.0 * PI); // set by -o/# + + // The smallest dihedral angle to identify slivers. + REAL sliver_ang_tol = b->optmaxdihedral - 5.0; + if (sliver_ang_tol < 172.0) { + sliver_ang_tol = 172.; } + cossmtdihed = cos(sliver_ang_tol / 180.0 * PI); - totalsmtcount = totalsptcount = totalremcount = 0l; + // The smallest dihedral angle to split slivers. + REAL split_sliver_ang_tol = b->optmaxdihedral + 10.0; + if (split_sliver_ang_tol < 179.0) { + split_sliver_ang_tol = 179.0; + } else if (split_sliver_ang_tol > 180.0) { + split_sliver_ang_tol = 179.9; + } + cosslidihed = cos(split_sliver_ang_tol / 180.0 * PI); - cosmaxdihed = cos(b->optmaxdihedral / 180.0 * PI); - cossmtdihed = cos(b->optminsmtdihed / 180.0 * PI); - cosslidihed = cos(b->optminslidihed / 180.0 * PI); + opt_max_sliver_asp_ratio = b->opt_max_asp_ratio * 10.; // set by -o//# - int attrnum = numelemattrib - 1; + int attrnum = numelemattrib - 1; + triface checktet; badface bf; // Put all bad tetrahedra into array. tetrahedrons->traversalinit(); @@ -26827,112 +31585,121 @@ void tetgenmesh::optimizemesh() continue; } } - ppt = (point *) & (checktet.tet[4]); - tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], ncosdd, &maxdd, NULL); - if (maxdd < cosmaxdihed) { - // There are bad dihedral angles in this tet. - unflipqueue->newindex((void **) &parybface); - parybface->tt.tet = checktet.tet; - parybface->tt.ver = 11; - parybface->forg = ppt[0]; - parybface->fdest = ppt[1]; - parybface->fapex = ppt[2]; - parybface->foppo = ppt[3]; - parybface->key = maxdd; - for (n = 0; n < 6; n++) { - parybface->cent[n] = ncosdd[n]; + if (get_tetqual(&checktet, NULL, &bf)) { + if ((bf.key > b->opt_max_asp_ratio) || (bf.cent[0] < cosmaxdihed)) { + bf.forg = org(bf.tt); + bf.fdest = dest(bf.tt); + bf.fapex = apex(bf.tt); + bf.foppo = oppo(bf.tt); + enqueue_badtet(&bf); } + } else { + terminatetetgen(this, 2); // a degenerated tet. } checktet.tet = tetrahedrontraverse(); } - totalremcount = improvequalitybyflips(); + // Backup flip edge options. + int bakautofliplinklevel = autofliplinklevel; + int bakfliplinklevel = b->fliplinklevel; + int bakmaxflipstarsize = b->flipstarsize; - if ((unflipqueue->objects > 0l) && - ((b->optscheme & 2) || (b->optscheme & 4))) { - // The pool is only used by removeslivers(). - badtetrahedrons = new memorypool(sizeof(triface), b->tetrahedraperblock, - sizeof(void *), 0); + b->fliplinklevel = 1; // initial (<= b->opt_max_flip_level, -O#) + b->flipstarsize = 10; // b->optmaxflipstarsize; - // Smoothing options. - opm.min_max_dihedangle = 1; - opm.numofsearchdirs = 10; - // opm.searchstep = 0.001; - opm.maxiter = 30; // Limit the maximum iterations. - //opm.checkencflag = 4; // Queue affected tets after smoothing. - chkencflag = 4; // Queue affected tets after splitting a sliver. - iter = 0; + long total_repaired_count = 0l; + long bak_pt_count = points->items; - while (iter < optpasses) { - smtcount = sptcount = remcount = 0l; - if (b->optscheme & 2) { - smtcount += improvequalitybysmoothing(&opm); - totalsmtcount += smtcount; - if (smtcount > 0l) { - remcount = improvequalitybyflips(); - totalremcount += remcount; - } - } - if (unflipqueue->objects > 0l) { - if (b->optscheme & 4) { - sptcount += removeslivers(chkencflag); - totalsptcount += sptcount; - if (sptcount > 0l) { - remcount = improvequalitybyflips(); - totalremcount += remcount; - } - } - } - if (unflipqueue->objects > 0l) { - if (remcount > 0l) { - iter++; - } else { - break; - } - } else { - break; - } - } // while (iter) + // Only using flips. + while (badqual_tets_pool->items > 0) { + long repaired_count = repair_badqual_tets(true, false, false); + total_repaired_count += repaired_count; + if (b->fliplinklevel < b->opt_max_flip_level) { + b->fliplinklevel++; + } else { + break; // maximal flip level is reached. + } + } // while (badqual_tets_pool->items > 0) + + if (b->verbose > 1) { + printf(" Repaired %ld tetrahedra by flips.\n", total_repaired_count); + printf(" %ld badqual tets remained.\n", badqual_tets_pool->items); + } - delete badtetrahedrons; + int iter = 0; + long bak_st_count = st_volref_count; + while ((badqual_tets_pool->items > 0) && (iter < b->opt_iterations)) { + //b->fliplinklevel++; + long repaired_count = repair_badqual_tets(true, true, true); + // Break if no repair and no new Steiner point. + if ((repaired_count == 0l) && (bak_st_count == st_volref_count)) { + break; + } + total_repaired_count += repaired_count; + bak_st_count = st_volref_count; + iter++; + } // while (badqual_tets_pool->items > 0) + // Do last flips. + if (badqual_tets_pool->items > 0) { + long repaired_count = repair_badqual_tets(true, false, false); + total_repaired_count += repaired_count; } - if (unflipqueue->objects > 0l) { - if (b->verbose > 1) { - printf(" %ld bad tets remained.\n", unflipqueue->objects); - } - unflipqueue->restart(); + if (b->verbose > 1) { + printf(" Repaired %ld tetrahedra.\n", total_repaired_count); + printf(" %ld badqual tets remained.\n", badqual_tets_pool->items); + } + + if (later_unflip_queue->objects > b->unflip_queue_limit) { + //recoverdelaunay(); + later_unflip_queue->restart(); // clean it. } if (b->verbose) { - if (totalremcount > 0l) { - printf(" Removed %ld edges.\n", totalremcount); + if (opt_flips_count > 0l) { + printf(" Removed %ld edges/faces.\n", opt_flips_count); + } + if (opt_collapse_count > 0l) { + printf(" Collapsed %ld edges/faces.\n", opt_collapse_count); } - if (totalsmtcount > 0l) { - printf(" Smoothed %ld points.\n", totalsmtcount); + if (opt_smooth_count > 0l) { + printf(" Smoothed %ld vertices.\n", opt_smooth_count); } - if (totalsptcount > 0l) { - printf(" Split %ld slivers.\n", totalsptcount); + if ((points->items - bak_pt_count) > 0l) { + printf(" Added %ld Steiner points.\n", points->items - bak_pt_count); } } + + + // Restore original flip edge options. + autofliplinklevel = bakautofliplinklevel; + b->fliplinklevel = bakfliplinklevel; + b->flipstarsize = bakmaxflipstarsize; + + delete badtetrahedrons; + badtetrahedrons = NULL; + delete badqual_tets_pool; + badqual_tets_pool = NULL; + delete unsplit_badtets; + unsplit_badtets = NULL; } -//// //// -//// //// -//// optimize_cxx ///////////////////////////////////////////////////////////// +// // +// // +//== optimize_cxx ============================================================// -//// meshstat_cxx ///////////////////////////////////////////////////////////// -//// //// -//// //// +//== meshstat_cxx ============================================================// +// // +// // -/////////////////////////////////////////////////////////////////////////////// -// // -// printfcomma() Print a (large) number with the 'thousands separator'. // -// // -// The following code was simply copied from "stackoverflow". // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// printfcomma() Print a (large) number with the 'thousands separator'. // +// // +// The following code was simply copied from "stackoverflow". // +// // +//============================================================================// void tetgenmesh::printfcomma(unsigned long n) { @@ -26952,16 +31719,16 @@ void tetgenmesh::printfcomma(unsigned long n) } } -/////////////////////////////////////////////////////////////////////////////// -// // -// checkmesh() Test the mesh for topological consistency. // -// // -// If 'topoflag' is set, only check the topological connection of the mesh, // -// i.e., do not report degenerated or inverted elements. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// checkmesh() Test the mesh for topological consistency. // +// // +// If 'topoflag' is set, only check the topological connection of the mesh, // +// i.e., do not report degenerated or inverted elements. // +// // +//============================================================================// -int tetgenmesh::checkmesh(int topoflag) +int tetgenmesh::check_mesh(int topoflag) { triface tetloop, neightet, symtet; point pa, pb, pc, pd; @@ -27016,48 +31783,54 @@ int tetgenmesh::checkmesh(int topoflag) } else { // Find the neighboring tetrahedron on this face. fsym(tetloop, neightet); - // Check that the tetrahedron's neighbor knows it's a neighbor. - fsym(neightet, symtet); - if ((tetloop.tet != symtet.tet) || (tetloop.ver != symtet.ver)) { - printf(" !! !! Asymmetric tetra-tetra bond:\n"); - if (tetloop.tet == symtet.tet) { - printf(" (Right tetrahedron, wrong orientation)\n"); + if (neightet.tet != NULL) { + // Check that the tetrahedron's neighbor knows it's a neighbor. + fsym(neightet, symtet); + if ((tetloop.tet != symtet.tet) || (tetloop.ver != symtet.ver)) { + printf(" !! !! Asymmetric tetra-tetra bond:\n"); + if (tetloop.tet == symtet.tet) { + printf(" (Right tetrahedron, wrong orientation)\n"); + } + printf(" First: (%d, %d, %d, %d)\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(pd)); + printf(" Second: (%d, %d, %d, %d)\n", pointmark(org(neightet)), + pointmark(dest(neightet)), pointmark(apex(neightet)), + pointmark(oppo(neightet))); + horrors++; } - printf(" First: (%d, %d, %d, %d)\n", pointmark(pa), - pointmark(pb), pointmark(pc), pointmark(pd)); - printf(" Second: (%d, %d, %d, %d)\n", pointmark(org(neightet)), - pointmark(dest(neightet)), pointmark(apex(neightet)), - pointmark(oppo(neightet))); - horrors++; - } - // Check if they have the same edge (the bond() operation). - if ((org(neightet) != pb) || (dest(neightet) != pa)) { - printf(" !! !! Wrong edge-edge bond:\n"); - printf(" First: (%d, %d, %d, %d)\n", pointmark(pa), - pointmark(pb), pointmark(pc), pointmark(pd)); - printf(" Second: (%d, %d, %d, %d)\n", pointmark(org(neightet)), - pointmark(dest(neightet)), pointmark(apex(neightet)), - pointmark(oppo(neightet))); - horrors++; - } - // Check if they have the same apex. - if (apex(neightet) != pc) { - printf(" !! !! Wrong face-face bond:\n"); - printf(" First: (%d, %d, %d, %d)\n", pointmark(pa), - pointmark(pb), pointmark(pc), pointmark(pd)); - printf(" Second: (%d, %d, %d, %d)\n", pointmark(org(neightet)), - pointmark(dest(neightet)), pointmark(apex(neightet)), - pointmark(oppo(neightet))); - horrors++; - } - // Check if they have the same opposite. - if (oppo(neightet) == pd) { - printf(" !! !! Two identical tetra:\n"); - printf(" First: (%d, %d, %d, %d)\n", pointmark(pa), - pointmark(pb), pointmark(pc), pointmark(pd)); - printf(" Second: (%d, %d, %d, %d)\n", pointmark(org(neightet)), - pointmark(dest(neightet)), pointmark(apex(neightet)), - pointmark(oppo(neightet))); + // Check if they have the same edge (the bond() operation). + if ((org(neightet) != pb) || (dest(neightet) != pa)) { + printf(" !! !! Wrong edge-edge bond:\n"); + printf(" First: (%d, %d, %d, %d)\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(pd)); + printf(" Second: (%d, %d, %d, %d)\n", pointmark(org(neightet)), + pointmark(dest(neightet)), pointmark(apex(neightet)), + pointmark(oppo(neightet))); + horrors++; + } + // Check if they have the same apex. + if (apex(neightet) != pc) { + printf(" !! !! Wrong face-face bond:\n"); + printf(" First: (%d, %d, %d, %d)\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(pd)); + printf(" Second: (%d, %d, %d, %d)\n", pointmark(org(neightet)), + pointmark(dest(neightet)), pointmark(apex(neightet)), + pointmark(oppo(neightet))); + horrors++; + } + // Check if they have the same opposite. + if (oppo(neightet) == pd) { + printf(" !! !! Two identical tetra:\n"); + printf(" First: (%d, %d, %d, %d)\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(pd)); + printf(" Second: (%d, %d, %d, %d)\n", pointmark(org(neightet)), + pointmark(dest(neightet)), pointmark(apex(neightet)), + pointmark(oppo(neightet))); + horrors++; + } + } else { + printf(" !! !! Tet-face has no neighbor (%d, %d, %d) - %d:\n", + pointmark(pa), pointmark(pb), pointmark(pc), pointmark(pd)); horrors++; } } @@ -27091,13 +31864,13 @@ int tetgenmesh::checkmesh(int topoflag) return horrors; } -/////////////////////////////////////////////////////////////////////////////// -// // -// checkshells() Test the boundary mesh for topological consistency. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// checkshells() Test the boundary mesh for topological consistency. // +// // +//============================================================================// -int tetgenmesh::checkshells() +int tetgenmesh::check_shells() { triface neightet, symtet; face shloop, spinsh, nextsh; @@ -27130,10 +31903,10 @@ int tetgenmesh::checkshells() while ((nextsh.sh != NULL) && (nextsh.sh != shloop.sh)) { if (nextsh.sh[3] == NULL) { printf(" !! !! Wrong subface-subface connection (Dead subface).\n"); - printf(" First: x%lx (%d, %d, %d).\n", (uintptr_t) spinsh.sh, + printf(" First: x%lu (%d, %d, %d).\n", (uintptr_t) spinsh.sh, pointmark(sorg(spinsh)), pointmark(sdest(spinsh)), pointmark(sapex(spinsh))); - printf(" Second: x%lx (DEAD)\n", (uintptr_t) nextsh.sh); + printf(" Second: x%lu (DEAD)\n", (uintptr_t) nextsh.sh); horrors++; break; } @@ -27141,10 +31914,10 @@ int tetgenmesh::checkshells() if (!(((sorg(nextsh) == pa) && (sdest(nextsh) == pb)) || ((sorg(nextsh) == pb) && (sdest(nextsh) == pa)))) { printf(" !! !! Wrong subface-subface connection.\n"); - printf(" First: x%lx (%d, %d, %d).\n", (uintptr_t) spinsh.sh, + printf(" First: x%lu (%d, %d, %d).\n", (uintptr_t) spinsh.sh, pointmark(sorg(spinsh)), pointmark(sdest(spinsh)), pointmark(sapex(spinsh))); - printf(" Scond: x%lx (%d, %d, %d).\n", (uintptr_t) nextsh.sh, + printf(" Scond: x%lu (%d, %d, %d).\n", (uintptr_t) nextsh.sh, pointmark(sorg(nextsh)), pointmark(sdest(nextsh)), pointmark(sapex(nextsh))); horrors++; @@ -27153,10 +31926,10 @@ int tetgenmesh::checkshells() // Check they should not have the same apex. if (sapex(nextsh) == sapex(spinsh)) { printf(" !! !! Existing two duplicated subfaces.\n"); - printf(" First: x%lx (%d, %d, %d).\n", (uintptr_t) spinsh.sh, + printf(" First: x%lu (%d, %d, %d).\n", (uintptr_t) spinsh.sh, pointmark(sorg(spinsh)), pointmark(sdest(spinsh)), pointmark(sapex(spinsh))); - printf(" Scond: x%lx (%d, %d, %d).\n", (uintptr_t) nextsh.sh, + printf(" Scond: x%lu (%d, %d, %d).\n", (uintptr_t) nextsh.sh, pointmark(sorg(nextsh)), pointmark(sdest(nextsh)), pointmark(sapex(nextsh))); horrors++; @@ -27170,19 +31943,19 @@ int tetgenmesh::checkshells() if (checkseg.sh != NULL) { if (checkseg.sh[3] == NULL) { printf(" !! !! Wrong subface-subseg connection (Dead subseg).\n"); - printf(" Sub: x%lx (%d, %d, %d).\n", (uintptr_t) shloop.sh, + printf(" Sub: x%lu (%d, %d, %d).\n", (uintptr_t) shloop.sh, pointmark(sorg(shloop)), pointmark(sdest(shloop)), pointmark(sapex(shloop))); - printf(" Sub: x%lx (Dead)\n", (uintptr_t) checkseg.sh); + printf(" Sub: x%lu (Dead)\n", (uintptr_t) checkseg.sh); horrors++; } else { if (!(((sorg(checkseg) == pa) && (sdest(checkseg) == pb)) || ((sorg(checkseg) == pb) && (sdest(checkseg) == pa)))) { printf(" !! !! Wrong subface-subseg connection.\n"); - printf(" Sub: x%lx (%d, %d, %d).\n", (uintptr_t) shloop.sh, + printf(" Sub: x%lu (%d, %d, %d).\n", (uintptr_t) shloop.sh, pointmark(sorg(shloop)), pointmark(sdest(shloop)), pointmark(sapex(shloop))); - printf(" Seg: x%lx (%d, %d).\n", (uintptr_t) checkseg.sh, + printf(" Seg: x%lu (%d, %d).\n", (uintptr_t) checkseg.sh, pointmark(sorg(checkseg)), pointmark(sdest(checkseg))); horrors++; } @@ -27196,19 +31969,19 @@ int tetgenmesh::checkshells() if (neightet.tet != NULL) { if (neightet.tet[4] == NULL) { printf(" !! !! Wrong sub-to-tet connection (Dead tet)\n"); - printf(" Sub: x%lx (%d, %d, %d).\n", (uintptr_t) shloop.sh, + printf(" Sub: x%lu (%d, %d, %d).\n", (uintptr_t) shloop.sh, pointmark(sorg(shloop)), pointmark(sdest(shloop)), pointmark(sapex(shloop))); - printf(" Tet: x%lx (DEAD)\n", (uintptr_t) neightet.tet); + printf(" Tet: x%lu (DEAD)\n", (uintptr_t) neightet.tet); horrors++; } else { if (!((sorg(shloop) == org(neightet)) && (sdest(shloop) == dest(neightet)))) { printf(" !! !! Wrong sub-to-tet connection\n"); - printf(" Sub: x%lx (%d, %d, %d).\n", (uintptr_t) shloop.sh, + printf(" Sub: x%lu (%d, %d, %d).\n", (uintptr_t) shloop.sh, pointmark(sorg(shloop)), pointmark(sdest(shloop)), pointmark(sapex(shloop))); - printf(" Tet: x%lx (%d, %d, %d, %d).\n", + printf(" Tet: x%lu (%d, %d, %d, %d).\n", (uintptr_t) neightet.tet, pointmark(org(neightet)), pointmark(dest(neightet)), pointmark(apex(neightet)), pointmark(oppo(neightet))); @@ -27218,10 +31991,10 @@ int tetgenmesh::checkshells() if (!((sorg(spinsh) == org(neightet)) && (sdest(spinsh) == dest(neightet)))) { printf(" !! !! Wrong tet-sub connection.\n"); - printf(" Sub: x%lx (%d, %d, %d).\n", (uintptr_t) spinsh.sh, + printf(" Sub: x%lu (%d, %d, %d).\n", (uintptr_t) spinsh.sh, pointmark(sorg(spinsh)), pointmark(sdest(spinsh)), pointmark(sapex(spinsh))); - printf(" Tet: x%lx (%d, %d, %d, %d).\n", + printf(" Tet: x%lu (%d, %d, %d, %d).\n", (uintptr_t) neightet.tet, pointmark(org(neightet)), pointmark(dest(neightet)), pointmark(apex(neightet)), pointmark(oppo(neightet))); @@ -27233,10 +32006,10 @@ int tetgenmesh::checkshells() if (!((sorg(spinsh) == org(symtet)) && (sdest(spinsh) == dest(symtet)))) { printf(" !! !! Wrong tet-sub connection.\n"); - printf(" Sub: x%lx (%d, %d, %d).\n", (uintptr_t) spinsh.sh, + printf(" Sub: x%lu (%d, %d, %d).\n", (uintptr_t) spinsh.sh, pointmark(sorg(spinsh)), pointmark(sdest(spinsh)), pointmark(sapex(spinsh))); - printf(" Tet: x%lx (%d, %d, %d, %d).\n", + printf(" Tet: x%lu (%d, %d, %d, %d).\n", (uintptr_t) symtet.tet, pointmark(org(symtet)), pointmark(dest(symtet)), pointmark(apex(symtet)), pointmark(oppo(symtet))); @@ -27278,13 +32051,13 @@ int tetgenmesh::checkshells() return horrors; } -/////////////////////////////////////////////////////////////////////////////// -// // -// checksegments() Check the connections between tetrahedra and segments. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// checksegments() Check the connections between tetrahedra and segments. // +// // +//============================================================================// -int tetgenmesh::checksegments() +int tetgenmesh::check_segments() { triface tetloop, neightet, spintet; shellface *segs; @@ -27318,7 +32091,7 @@ int tetgenmesh::checksegments() if (!(((org(tetloop) == pa) && (dest(tetloop) == pb)) || ((org(tetloop) == pb) && (dest(tetloop) == pa)))) { printf(" !! Wrong tet-seg connection.\n"); - printf(" Tet: x%lx (%d, %d, %d, %d) - Seg: x%lx (%d, %d).\n", + printf(" Tet: x%lu (%d, %d, %d, %d) - Seg: x%lu (%d, %d).\n", (uintptr_t) tetloop.tet, pointmark(org(tetloop)), pointmark(dest(tetloop)), pointmark(apex(tetloop)), pointmark(oppo(tetloop)), (uintptr_t) sseg.sh, @@ -27331,12 +32104,12 @@ int tetgenmesh::checksegments() tsspivot1(neightet, checkseg); if (checkseg.sh != sseg.sh) { printf(" !! Wrong tet->seg connection.\n"); - printf(" Tet: x%lx (%d, %d, %d, %d) - ", + printf(" Tet: x%lu (%d, %d, %d, %d) - ", (uintptr_t) neightet.tet, pointmark(org(neightet)), pointmark(dest(neightet)), pointmark(apex(neightet)), pointmark(oppo(neightet))); if (checkseg.sh != NULL) { - printf("Seg x%lx (%d, %d).\n", (uintptr_t) checkseg.sh, + printf("Seg x%lu (%d, %d).\n", (uintptr_t) checkseg.sh, pointmark(sorg(checkseg)),pointmark(sdest(checkseg))); } else { printf("Seg: NULL.\n"); @@ -27355,7 +32128,7 @@ int tetgenmesh::checksegments() if (!(((org(neightet) == pa) && (dest(neightet) == pb)) || ((org(neightet) == pb) && (dest(neightet) == pa)))) { printf(" !! Wrong seg->tet connection (Wrong edge).\n"); - printf(" Tet: x%lx (%d, %d, %d, %d) - Seg: x%lx (%d, %d).\n", + printf(" Tet: x%lu (%d, %d, %d, %d) - Seg: x%lu (%d, %d).\n", (uintptr_t) neightet.tet, pointmark(org(neightet)), pointmark(dest(neightet)), pointmark(apex(neightet)), pointmark(oppo(neightet)), (uintptr_t) sseg.sh, @@ -27372,7 +32145,7 @@ int tetgenmesh::checksegments() neightet.ver = edge2ver[i]; if (edgemarked(neightet)) { // A possible bug. Report it. - printf(" !! A marked edge: (%d, %d, %d, %d) -- x%lx %d.\n", + printf(" !! A marked edge: (%d, %d, %d, %d) -- x%lu %d.\n", pointmark(org(neightet)), pointmark(dest(neightet)), pointmark(apex(neightet)), pointmark(oppo(neightet)), (uintptr_t) neightet.tet, neightet.ver); @@ -27381,7 +32154,7 @@ int tetgenmesh::checksegments() while (1) { fnextself(spintet); if (!edgemarked(spintet)) { - printf(" !! !! An unmarked edge (%d, %d, %d, %d) -- x%lx %d.\n", + printf(" !! !! An unmarked edge (%d, %d, %d, %d) -- x%lu %d.\n", pointmark(org(spintet)), pointmark(dest(spintet)), pointmark(apex(spintet)), pointmark(oppo(spintet)), (uintptr_t) spintet.tet, spintet.ver); @@ -27410,12 +32183,14 @@ int tetgenmesh::checksegments() spinsh = neighsh; while (1) { // Check seg-subface bond. - if (((sorg(spinsh) == pa) && (sdest(spinsh) == pb)) || - ((sorg(spinsh) == pb) && (sdest(spinsh) == pa))) { + point e1 = sorg(spinsh); + point e2 = sdest(spinsh); + if (((e1 == pa) && (e2 == pb)) || + ((e1 == pb) && (e2 == pa))) { // Keep the same rotate direction. //if (sorg(spinsh) != pa) { // sesymself(spinsh); - // printf(" !! Wrong ori at subface (%d, %d, %d) -- x%lx %d\n", + // printf(" !! Wrong ori at subface (%d, %d, %d) -- x%lu %d\n", // pointmark(sorg(spinsh)), pointmark(sdest(spinsh)), // pointmark(sapex(spinsh)), (uintptr_t) spinsh.sh, // spinsh.shver); @@ -27427,7 +32202,7 @@ int tetgenmesh::checksegments() while (1) { tsspivot1(spintet, checkseg); if (checkseg.sh == NULL) { - printf(" !! !! No seg at tet (%d, %d, %d, %d) -- x%lx %d\n", + printf(" !! !! No seg at tet (%d, %d, %d, %d) -- x%lu %d\n", pointmark(org(spintet)), pointmark(dest(spintet)), pointmark(apex(spintet)), pointmark(oppo(spintet)), (uintptr_t) spintet.tet, spintet.ver); @@ -27446,11 +32221,16 @@ int tetgenmesh::checksegments() if (checksh.sh != NULL) break; } // while (1) } - } else { - printf(" !! Wrong seg-subface (%d, %d, %d) -- x%lx %d connect\n", - pointmark(sorg(spinsh)), pointmark(sdest(spinsh)), - pointmark(sapex(spinsh)), (uintptr_t) spinsh.sh, - spinsh.shver); + } else { + point e3 = sapex(spinsh); + printf(" !! Wrong seg-subface (%d, %d) - (%d, %d, %d) connect\n", + pointmark(pa), pointmark(pb), + (e1 != NULL ? pointmark(e1) : -1), + (e2 != NULL ? pointmark(e2) : -1), + (e3 != NULL ? pointmark(e3) : -1) + //(uintptr_t) spinsh.sh, + //spinsh.shver + ); horrors++; break; } // if pa, pb @@ -27499,7 +32279,7 @@ int tetgenmesh::checksegments() } else { spivotself(checkseg); checkseg.shver = 0; - if (sorg(checkseg) != pa) { + if ((sorg(checkseg) != pa) && (sdest(checkseg) != pa)) { printf(" !! Wrong seg-seg connection at point %d.\n", pointmark(pa)); horrors++; @@ -27516,7 +32296,7 @@ int tetgenmesh::checksegments() } else { spivotself(checkseg); checkseg.shver = 0; - if (sdest(checkseg) != pa) { + if ((sorg(checkseg) != pa) && (sdest(checkseg) != pa)) { printf(" !! Wrong seg-seg connection at point %d.\n", pointmark(pa)); horrors++; @@ -27540,13 +32320,13 @@ int tetgenmesh::checksegments() return horrors; } -/////////////////////////////////////////////////////////////////////////////// -// // -// checkdelaunay() Ensure that the mesh is (constrained) Delaunay. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// checkdelaunay() Ensure that the mesh is (constrained) Delaunay. // +// // +//============================================================================// -int tetgenmesh::checkdelaunay() +int tetgenmesh::check_delaunay(int perturb) { triface tetloop; triface symtet; @@ -27578,7 +32358,11 @@ int tetgenmesh::checkdelaunay() pc = apex(tetloop); pd = oppo(tetloop); pe = oppo(symtet); - sign = insphere_s(pa, pb, pc, pd, pe); + if (perturb) { + sign = insphere_s(pa, pb, pc, pd, pe); + } else { + sign = insphere(pa, pb, pc, pd, pe); + } if (sign < 0.0) { ndcount++; if (checksubfaceflag) { @@ -27611,19 +32395,19 @@ int tetgenmesh::checkdelaunay() return horrors; } -/////////////////////////////////////////////////////////////////////////////// -// // -// Check if the current tetrahedralization is (constrained) regular. // -// // -// The parameter 'type' determines which regularity should be checked: // -// - 0: check the Delaunay property. // -// - 1: check the Delaunay property with symbolic perturbation. // -// - 2: check the regular property, the weights are stored in p[3]. // -// - 3: check the regular property with symbolic perturbation. // -// // -/////////////////////////////////////////////////////////////////////////////// - -int tetgenmesh::checkregular(int type) +//============================================================================// +// // +// Check if the current tetrahedralization is (constrained) regular. // +// // +// The parameter 'type' determines which regularity should be checked: // +// - 0: check the Delaunay property. // +// - 1: check the Delaunay property with symbolic perturbation. // +// - 2: check the regular property, the weights are stored in p[3]. // +// - 3: check the regular property with symbolic perturbation. // +// // +//============================================================================// + +int tetgenmesh::check_regular(int type) { triface tetloop; triface symtet; @@ -27712,16 +32496,16 @@ int tetgenmesh::checkregular(int type) return horrors; } -/////////////////////////////////////////////////////////////////////////////// -// // -// checkconforming() Ensure that the mesh is conforming Delaunay. // -// // -// If 'flag' is 1, only check subsegments. If 'flag' is 2, check subfaces. // -// If 'flag' is 3, check both subsegments and subfaces. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// checkconforming() Ensure that the mesh is conforming Delaunay. // +// // +// If 'flag' is 1, only check subsegments. If 'flag' is 2, check subfaces. // +// If 'flag' is 3, check both subsegments and subfaces. // +// // +//============================================================================// -int tetgenmesh::checkconforming(int flag) +int tetgenmesh::check_conforming(int flag) { triface searchtet, neightet, spintet; face shloop; @@ -27858,11 +32642,11 @@ int tetgenmesh::checkconforming(int flag) return encsubsegs + encsubfaces; } -/////////////////////////////////////////////////////////////////////////////// -// // -// qualitystatistics() Print statistics about the quality of the mesh. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// qualitystatistics() Print statistics about the quality of the mesh. // +// // +//============================================================================// void tetgenmesh::qualitystatistics() { @@ -27877,6 +32661,7 @@ void tetgenmesh::qualitystatistics() REAL shortest, longest; REAL smallestvolume, biggestvolume; REAL smallestratio, biggestratio; + REAL smallestradiusratio, biggestradiusratio; // radius-edge ratio. REAL smallestdiangle, biggestdiangle; REAL smallestfaangle, biggestfaangle; REAL total_tet_vol, total_tetprism_vol; @@ -27895,6 +32680,11 @@ void tetgenmesh::qualitystatistics() int aspectindex; int tendegree; int i, j; + // Report the tet which has the biggest radius-edge ratio. + triface biggestradiusratiotet; + // Report the tet which has the biggest volume. + triface biggestvolumetet; + triface longestedgetet; printf("Mesh quality statistics:\n\n"); @@ -27928,8 +32718,8 @@ void tetgenmesh::qualitystatistics() longest = 0.0; smallestvolume = minaltitude; biggestvolume = 0.0; - smallestratio = 1e+16; // minaltitude; - biggestratio = 0.0; + smallestratio = smallestradiusratio = 1e+16; // minaltitude; + biggestratio = biggestradiusratio = 0.0; smallestdiangle = smallestfaangle = 180.0; biggestdiangle = biggestfaangle = 0.0; @@ -27939,6 +32729,9 @@ void tetgenmesh::qualitystatistics() // Loop all elements, calculate quality parameters for each element. tetrahedrons->traversalinit(); tetloop.tet = tetrahedrontraverse(); + + //int tidx = 1; + while (tetloop.tet != (tetrahedron *) NULL) { if (b->convex) { @@ -27963,6 +32756,7 @@ void tetgenmesh::qualitystatistics() } if (tetvol > biggestvolume) { biggestvolume = tetvol; + biggestvolumetet.tet = tetloop.tet; } // Set the edge vectors: V[0], ..., V[5] @@ -27986,6 +32780,7 @@ void tetgenmesh::qualitystatistics() } if (edgelength[i] > longest) { longest = edgelength[i]; + longestedgetet.tet = tetloop.tet; } if (edgelength[i] < shortest) { shortest = edgelength[i]; @@ -28022,13 +32817,12 @@ void tetgenmesh::qualitystatistics() // insradius = 1.0 / (H[0] + H[1] + H[2] + H[3]); // Get the biggest H[i] (corresponding to the smallest height). minheightinv = H[0]; - for (i = 1; i < 3; i++) { + for (i = 1; i < 4; i++) { if (H[i] > minheightinv) minheightinv = H[i]; } } else { // A nearly degenerated tet. if (tetvol <= 0.0) { - // assert(tetvol != 0.0); printf(" !! Warning: A %s tet (%d,%d,%d,%d).\n", tetvol < 0 ? "inverted" : "degenerated", pointmark(p[0]), pointmark(p[1]), pointmark(p[2]), pointmark(p[3])); @@ -28049,7 +32843,7 @@ void tetgenmesh::qualitystatistics() } // Get the biggest H[i] / tetvol (corresponding to the smallest height). minheightinv = (H[0] / tetvol); - for (i = 1; i < 3; i++) { + for (i = 1; i < 4; i++) { if ((H[i] / tetvol) > minheightinv) minheightinv = (H[i] / tetvol); } // Let the circumradius to be the half of its longest edge length. @@ -28154,6 +32948,13 @@ void tetgenmesh::qualitystatistics() // Calculate aspect ratio and radius-edge ratio for this element. tetradius = cirradius / sqrt(shortlen); + if (tetradius < smallestradiusratio) { + smallestradiusratio = tetradius; + } + if (tetradius > biggestradiusratio) { + biggestradiusratio = tetradius; + biggestradiusratiotet.tet = tetloop.tet; + } // tetaspect = sqrt(longlen) / (2.0 * insradius); tetaspect = sqrt(longlen) * minheightinv; // Remember the largest and smallest aspect ratio. @@ -28252,16 +33053,15 @@ void tetgenmesh::qualitystatistics() minfacetdihed / PI * 180.0); } printf("\n"); - printf("\n"); } -/////////////////////////////////////////////////////////////////////////////// -// // -// memorystatistics() Report the memory usage. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// memorystatistics() Report the memory usage. // +// // +//============================================================================// void tetgenmesh::memorystatistics() { @@ -28347,11 +33147,11 @@ void tetgenmesh::memorystatistics() printf("\n"); } -/////////////////////////////////////////////////////////////////////////////// -// // -// statistics() Print all sorts of cool facts. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// statistics() Print all sorts of cool facts. // +// // +//============================================================================// void tetgenmesh::statistics() { @@ -28361,10 +33161,18 @@ void tetgenmesh::statistics() printf(" Input points: %d\n", in->numberofpoints); if (b->refine) { printf(" Input tetrahedra: %d\n", in->numberoftetrahedra); - } - if (b->plc) { + if (in->numberoftrifaces > 0) { + printf(" Input triangles: %d\n", in->numberoftrifaces); + } + if (in->numberofedges > 0) { + printf(" Input edges: %d\n", in->numberofedges); + } + } else if (b->plc) { printf(" Input facets: %d\n", in->numberoffacets); printf(" Input segments: %ld\n", insegments); + if (in->numberofedges > 0) { + printf(" Input edges: %d\n", in->numberofedges); + } printf(" Input holes: %d\n", in->numberofholes); printf(" Input regions: %d\n", in->numberofregions); } @@ -28391,16 +33199,20 @@ void tetgenmesh::statistics() } if (b->plc || b->refine) { - printf(" Mesh faces on facets: %ld\n", subfaces->items); - printf(" Mesh edges on segments: %ld\n", subsegs->items); - if (st_volref_count > 0l) { - printf(" Steiner points inside domain: %ld\n", st_volref_count); + printf(" Mesh faces on exterior boundary: %ld\n", hullsize); + if (meshhulledges > 0l) { + printf(" Mesh edges on exterior boundary: %ld\n", meshhulledges); } + printf(" Mesh faces on input facets: %ld\n", subfaces->items); + printf(" Mesh edges on input segments: %ld\n", subsegs->items); if (st_facref_count > 0l) { - printf(" Steiner points on facets: %ld\n", st_facref_count); + printf(" Steiner points on input facets: %ld\n", st_facref_count); } if (st_segref_count > 0l) { - printf(" Steiner points on segments: %ld\n", st_segref_count); + printf(" Steiner points on input segments: %ld\n", st_segref_count); + } + if (st_volref_count > 0l) { + printf(" Steiner points inside domain: %ld\n", st_volref_count); } } else { printf(" Convex hull faces: %ld\n", hullsize); @@ -28426,25 +33238,25 @@ void tetgenmesh::statistics() } } -//// //// -//// //// -//// meshstat_cxx ///////////////////////////////////////////////////////////// - -//// output_cxx /////////////////////////////////////////////////////////////// -//// //// -//// //// - -/////////////////////////////////////////////////////////////////////////////// -// // -// jettisonnodes() Jettison unused or duplicated vertices. // -// // -// Unused points are those input points which are outside the mesh domain or // -// have no connection (isolated) to the mesh. Duplicated points exist for // -// example if the input PLC is read from a .stl mesh file (marked during the // -// Delaunay tetrahedralization step. This routine remove these points from // -// points list. All existing points are reindexed. // -// // -/////////////////////////////////////////////////////////////////////////////// +// // +// // +//== meshstat_cxx ============================================================// + +//== output_cxx ==============================================================// +// // +// // + +//============================================================================// +// // +// jettisonnodes() Jettison unused or duplicated vertices. // +// // +// Unused points are those input points which are outside the mesh domain or // +// have no connection (isolated) to the mesh. Duplicated points exist for // +// example if the input PLC is read from a .stl mesh file (marked during the // +// Delaunay tetrahedralization step. This routine remove these points from // +// points list. All existing points are reindexed. // +// // +//============================================================================// void tetgenmesh::jettisonnodes() { @@ -28495,15 +33307,15 @@ void tetgenmesh::jettisonnodes() points->deaditemstack = (void *) NULL; } -/////////////////////////////////////////////////////////////////////////////// -// // -// highorder() Create extra nodes for quadratic subparametric elements. // -// // -// 'highordertable' is an array (size = numberoftetrahedra * 6) for storing // -// high-order nodes of each tetrahedron. This routine is used only when -o2 // -// switch is used. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// highorder() Create extra nodes for quadratic subparametric elements. // +// // +// 'highordertable' is an array (size = numberoftetrahedra * 6) for storing // +// high-order nodes of each tetrahedron. This routine is used only when -o2 // +// switch is used. // +// // +//============================================================================// void tetgenmesh::highorder() { @@ -28519,7 +33331,7 @@ void tetgenmesh::highorder() } // Initialize the 'highordertable'. - highordertable = new point[tetrahedrons->items * 6]; + point *highordertable = new point[tetrahedrons->items * 6]; if (highordertable == (point *) NULL) { terminatetetgen(this, 1); } @@ -28589,19 +33401,51 @@ void tetgenmesh::highorder() } // i tetloop.tet = tetrahedrontraverse(); } + + delete [] highordertable; } -/////////////////////////////////////////////////////////////////////////////// -// // -// numberedges() Count the number of edges, save in "meshedges". // -// // -// This routine is called when '-p' or '-r', and '-E' options are used. The // -// total number of edges depends on the genus of the input surface mesh. // -// // -// NOTE: This routine must be called after outelements(). So all elements // -// have been indexed. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// indexelements() Index all tetrahedra. // +// // +// Many output functions require that the tetrahedra are indexed. This // +// routine is called when -E option is used. // +// // +//============================================================================// + +void tetgenmesh::indexelements() +{ + triface worktet; + int eindex = b->zeroindex ? 0 : in->firstnumber; // firstindex; + tetrahedrons->traversalinit(); + worktet.tet = tetrahedrontraverse(); + while (worktet.tet != NULL) { + setelemindex(worktet.tet, eindex); + eindex++; + if (b->metric) { // -m option + // Update the point-to-tet map, so that every point is pointing + // to a real tet, not a fictious one. Used by .p2t file. + tetrahedron tptr = encode(worktet); + for (int i = 0; i < 4; i++) { + setpoint2tet((point) (worktet.tet[4 + i]), tptr); + } + } + worktet.tet = tetrahedrontraverse(); + } +} + +//============================================================================// +// // +// numberedges() Count the number of edges, save in "meshedges". // +// // +// This routine is called when '-p' or '-r', and '-E' options are used. The // +// total number of edges depends on the genus of the input surface mesh. // +// // +// NOTE: This routine must be called after outelements(). So all elements // +// have been indexed. // +// // +//============================================================================// void tetgenmesh::numberedges() { @@ -28615,39 +33459,36 @@ void tetgenmesh::numberedges() tetrahedrons->traversalinit(); worktet.tet = tetrahedrontraverse(); while (worktet.tet != NULL) { - // Count the number of Voronoi faces. Look at the six edges of this - // tet. Count an edge only if this tet's index is smaller than - // those of other non-hull tets which share this edge. for (i = 0; i < 6; i++) { worktet.ver = edge2ver[i]; ishulledge = 0; fnext(worktet, spintet); do { - if (!ishulltet(spintet)) { + if (!ishulltet(spintet)) { if (elemindex(spintet.tet) < elemindex(worktet.tet)) break; } else { ishulledge = 1; } fnextself(spintet); } while (spintet.tet != worktet.tet); - // Count this edge if no adjacent tets are smaller than this tet. if (spintet.tet == worktet.tet) { meshedges++; if (ishulledge) meshhulledges++; } } + infect(worktet); worktet.tet = tetrahedrontraverse(); } } -/////////////////////////////////////////////////////////////////////////////// -// // -// outnodes() Output the points to a .node file or a tetgenio structure. // -// // -// Note: each point has already been numbered on input (the first index is // -// 'in->firstnumber'). // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// outnodes() Output the points to a .node file or a tetgenio structure. // +// // +// Note: each point has already been numbered on input (the first index is // +// 'in->firstnumber'). // +// // +//============================================================================// void tetgenmesh::outnodes(tetgenio* out) { @@ -28688,6 +33529,9 @@ void tetgenmesh::outnodes(tetgenio* out) } // Number of points, number of dimensions, number of point attributes, // and number of boundary markers (zero or one). + //fprintf(outfile, "%ld %d %d %d\n", points->items, 3, nextras, bmark); + // [2020-01-16] added save flag (for viewing Steiner points). + //fprintf(outfile, "%ld %d %d %d 1\n", points->items, 3, nextras, bmark); fprintf(outfile, "%ld %d %d %d\n", points->items, 3, nextras, bmark); } else { // Allocate space for 'pointlist'; @@ -28746,11 +33590,6 @@ void tetgenmesh::outnodes(tetgenio* out) sdecode(point2sh(pointloop), parentsh); if (parentsh.sh != NULL) { marker = shellmark(parentsh); - if (pointtype(pointloop) == FREEFACETVERTEX) { - if (in->facetmarkerlist != NULL) { - marker = in->facetmarkerlist[marker - 1]; - } - } } } // if (pointtype(...)) } @@ -28778,8 +33617,8 @@ void tetgenmesh::outnodes(tetgenio* out) pointgeomuv(pointloop, 1), pointgeomtag(pointloop)); if (pointtype(pointloop) == RIDGEVERTEX) { fprintf(outfile, " 0"); - } else if (pointtype(pointloop) == ACUTEVERTEX) { - fprintf(outfile, " 0"); + //} else if (pointtype(pointloop) == ACUTEVERTEX) { + // fprintf(outfile, " 0"); } else if (pointtype(pointloop) == FREESEGVERTEX) { fprintf(outfile, " 1"); } else if (pointtype(pointloop) == FREEFACETVERTEX) { @@ -28790,6 +33629,12 @@ void tetgenmesh::outnodes(tetgenio* out) fprintf(outfile, " -1"); // Unknown type. } } + // // [2020-01-16] Write vertex flags + // if (pointnumber > in->numberofpoints) { + // fprintf(outfile, " 16"); // A Steiner point. + // } else { + // fprintf(outfile, " 0"); + // } fprintf(outfile, "\n"); } else { // X, y, and z coordinates. @@ -28817,8 +33662,8 @@ void tetgenmesh::outnodes(tetgenio* out) out->pointparamlist[index].tag = pointgeomtag(pointloop); if (pointtype(pointloop) == RIDGEVERTEX) { out->pointparamlist[index].type = 0; - } else if (pointtype(pointloop) == ACUTEVERTEX) { - out->pointparamlist[index].type = 0; + //} else if (pointtype(pointloop) == ACUTEVERTEX) { + // out->pointparamlist[index].type = 0; } else if (pointtype(pointloop) == FREESEGVERTEX) { out->pointparamlist[index].type = 1; } else if (pointtype(pointloop) == FREEFACETVERTEX) { @@ -28841,18 +33686,23 @@ void tetgenmesh::outnodes(tetgenio* out) } } -/////////////////////////////////////////////////////////////////////////////// -// // -// outmetrics() Output the metric to a file (*.mtr) or a tetgenio obj. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// outmetrics() Output the metric to a file (*.mtr) or a tetgenio obj. // +// // +//============================================================================// void tetgenmesh::outmetrics(tetgenio* out) { FILE *outfile = NULL; char outmtrfilename[FILENAMESIZE]; point ptloop; - int mtrindex; + int mtrindex = 0; + int i; + int msize = (sizeoftensor - useinsertradius); + if (msize == 0) { + return; + } if (out == (tetgenio *) NULL) { strcpy(outmtrfilename, b->outfilename); @@ -28874,51 +33724,105 @@ void tetgenmesh::outmetrics(tetgenio* out) terminatetetgen(this, 3); } // Number of points, number of point metrices, - // fprintf(outfile, "%ld %d\n", points->items, sizeoftensor + 3); - fprintf(outfile, "%ld %d\n", points->items, 1); + fprintf(outfile, "%ld %d\n", points->items, msize); } else { - // Allocate space for 'pointmtrlist' if necessary; - // out->pointmtrlist = new REAL[points->items * (sizeoftensor + 3)]; - out->pointmtrlist = new REAL[points->items]; + // Allocate space for 'pointmtrlist'. + out->numberofpointmtrs = msize; + out->pointmtrlist = new REAL[points->items * msize]; if (out->pointmtrlist == (REAL *) NULL) { terminatetetgen(this, 1); } - out->numberofpointmtrs = 1; // (sizeoftensor + 3); - mtrindex = 0; } points->traversalinit(); ptloop = pointtraverse(); while (ptloop != (point) NULL) { if (out == (tetgenio *) NULL) { - // for (i = 0; i < sizeoftensor; i++) { - // fprintf(outfile, "%-16.8e ", ptloop[pointmtrindex + i]); - // } - fprintf(outfile, "%-16.8e\n", ptloop[pointmtrindex]); + for (i = 0; i < msize; i++) { + fprintf(outfile, " %-16.8e", ptloop[pointmtrindex + i]); + } + fprintf(outfile, "\n"); } else { - // for (i = 0; i < sizeoftensor; i++) { - // out->pointmtrlist[mtrindex++] = ptloop[pointmtrindex + i]; - // } - out->pointmtrlist[mtrindex++] = ptloop[pointmtrindex]; + for (i = 0; i < msize; i++) { + out->pointmtrlist[mtrindex++] = ptloop[pointmtrindex + i]; + } } ptloop = pointtraverse(); } - + + // Output the point-to-tet map. + if (out == (tetgenio *) NULL) { + strcpy(outmtrfilename, b->outfilename); + strcat(outmtrfilename, ".p2t"); + } + + if (!b->quiet) { + if (out == (tetgenio *) NULL) { + printf("Writing %s.\n", outmtrfilename); + } else { + printf("Writing point-to-tet map.\n"); + } + } + + if (out == (tetgenio *) NULL) { + outfile = fopen(outmtrfilename, "w"); + if (outfile == (FILE *) NULL) { + printf("File I/O Error: Cannot create file %s.\n", outmtrfilename); + terminatetetgen(this, 3); + } + // Number of points, + //fprintf(outfile, "%ld\n", points->items); + } else { + // Allocate space for 'point2tetlist'. + out->point2tetlist = new int[points->items]; + if (out->point2tetlist == (int *) NULL) { + terminatetetgen(this, 1); + } + } + + // The list of tetrahedra must be indexed. + if (bgm != NULL) { + bgm->indexelements(); + } + // Determine the first index (0 or 1). + int firstindex = b->zeroindex ? 0 : in->firstnumber; + int pointindex = firstindex; + i = 0; + + triface parenttet; + points->traversalinit(); + ptloop = pointtraverse(); + while (ptloop != (point) NULL) { + if (bgm != NULL) { + bgm->decode(point2bgmtet(ptloop), parenttet); + } else { + decode(point2tet(ptloop), parenttet); + } + if (out == (tetgenio *) NULL) { + fprintf(outfile, "%d %d\n", pointindex, elemindex(parenttet.tet)); + } else { + out->point2tetlist[i] = elemindex(parenttet.tet); + } + pointindex++; + i++; + ptloop = pointtraverse(); + } + if (out == (tetgenio *) NULL) { fprintf(outfile, "# Generated by %s\n", b->commandline); fclose(outfile); } } -/////////////////////////////////////////////////////////////////////////////// -// // -// outelements() Output the tetrahedra to an .ele file or a tetgenio // -// structure. // -// // -// This routine also indexes all tetrahedra (exclusing hull tets) (from in-> // -// firstnumber). The total number of mesh edges is counted in 'meshedges'. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// outelements() Output the tetrahedra to an .ele file or a tetgenio // +// structure. // +// // +// This routine also indexes all tetrahedra (exclusing hull tets) (from in-> // +// firstnumber). The total number of mesh edges is counted in 'meshedges'. // +// // +//============================================================================// void tetgenmesh::outelements(tetgenio* out) { @@ -29043,6 +33947,13 @@ void tetgenmesh::outelements(tetgenio* out) } // Remember the index of this element (for counting edges). setelemindex(tptr, elementnumber); + if (b->metric) { // -m option + // Update the point-to-tet map, so that every point is pointing + // to a real tet, not a fictious one. Used by .p2t file. + for (int i = 0; i < 4; i++) { + setpoint2tet((point) (tptr[4 + i]), (tetrahedron) tptr); + } + } tptr = tetrahedrontraverse(); elementnumber++; } @@ -29054,11 +33965,16 @@ void tetgenmesh::outelements(tetgenio* out) } } -/////////////////////////////////////////////////////////////////////////////// -// // -// outfaces() Output all faces to a .face file or a tetgenio object. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// outfaces() Output all faces to a .face file or a tetgenio object. // +// // +// The total number of faces f can be calculated as following: Let t be the // +// total number of tets. Since each tet has 4 faces, the number t * 4 counts // +// each interior face twice and each hull face once. So f = (t * 4 + h) / 2, // +// where h is the total number of hull faces (which is known). // +// // +//============================================================================// void tetgenmesh::outfaces(tetgenio* out) { @@ -29070,7 +33986,7 @@ void tetgenmesh::outfaces(tetgenio* out) long ntets, faces; int *elist = NULL, *emlist = NULL; int neigh1 = 0, neigh2 = 0; - int faceid, marker = 0; + int marker = 0; int firstindex, shift; int facenumber; int index = 0; @@ -29081,6 +33997,10 @@ void tetgenmesh::outfaces(tetgenio* out) int highorderindex = 11; int o2index = 0, i; + // For -nn option. + int *tet2facelist = NULL; + int tidx; + if (out == (tetgenio *) NULL) { strcpy(facefilename, b->outfilename); strcat(facefilename, ".face"); @@ -29124,8 +34044,8 @@ void tetgenmesh::outfaces(tetgenio* out) } if (b->neighout > 1) { // '-nn' switch. - out->adjtetlist = new int[faces * 2]; - if (out->adjtetlist == (int *) NULL) { + out->face2tetlist = new int[faces * 2]; + if (out->face2tetlist == (int *) NULL) { printf("Error: Out of memory.\n"); terminatetetgen(this, 1); } @@ -29135,6 +34055,11 @@ void tetgenmesh::outfaces(tetgenio* out) emlist = out->trifacemarkerlist; } + if (b->neighout > 1) { // -nn option + // Output the tetrahedron-to-face map. + tet2facelist = new int[ntets * 4]; + } + // Determine the first index (0 or 1). firstindex = b->zeroindex ? 0 : in->firstnumber; shift = 0; // Default no shiftment. @@ -29175,13 +34100,7 @@ void tetgenmesh::outfaces(tetgenio* out) if (checkmark.sh == NULL) { marker = 0; // It is an inner face. It's marker is 0. } else { - if (in->facetmarkerlist) { - // The facet marker is given, get it. - faceid = shellmark(checkmark) - 1; - marker = in->facetmarkerlist[faceid]; - } else { - marker = 1; // The default marker for subface is 1. - } + marker = shellmark(checkmark); } } else { // Shell face is not used, only distinguish outer and inner face. @@ -29190,12 +34109,23 @@ void tetgenmesh::outfaces(tetgenio* out) } if (b->neighout > 1) { // '-nn' switch. Output adjacent tets indices. - neigh1 = elemindex(tface.tet); + if (!ishulltet(tface)) { + neigh1 = elemindex(tface.tet); + } else { + neigh1 = -1; + } if (!ishulltet(tsymface)) { neigh2 = elemindex(tsymface.tet); } else { neigh2 = -1; } + // Fill the tetrahedron-to-face map. + tidx = elemindex(tface.tet) - firstindex; + tet2facelist[tidx * 4 + tface.ver] = facenumber; + if (!ishulltet(tsymface)) { + tidx = elemindex(tsymface.tet) - firstindex; + tet2facelist[tidx * 4 + (tsymface.ver & 3)] = facenumber; + } } if (out == (tetgenio *) NULL) { // Face number, indices of three vertices. @@ -29228,8 +34158,8 @@ void tetgenmesh::outfaces(tetgenio* out) emlist[facenumber - in->firstnumber] = marker; } if (b->neighout > 1) { - out->adjtetlist[(facenumber - in->firstnumber) * 2] = neigh1; - out->adjtetlist[(facenumber - in->firstnumber) * 2 + 1] = neigh2; + out->face2tetlist[(facenumber - in->firstnumber) * 2] = neigh1; + out->face2tetlist[(facenumber - in->firstnumber) * 2 + 1] = neigh2; } } facenumber++; @@ -29242,15 +34172,44 @@ void tetgenmesh::outfaces(tetgenio* out) fprintf(outfile, "# Generated by %s\n", b->commandline); fclose(outfile); } + + if (b->neighout > 1) { // -nn option + // Output the tetrahedron-to-face map. + if (out == (tetgenio *) NULL) { + strcpy(facefilename, b->outfilename); + strcat(facefilename, ".t2f"); + } + if (!b->quiet) { + if (out == (tetgenio *) NULL) { + printf("Writing %s.\n", facefilename); + } else { + printf("Writing tetrahedron-to-face map.\n"); + } + } + if (out == (tetgenio *) NULL) { + outfile = fopen(facefilename, "w"); + for (tidx = 0; tidx < ntets; tidx++) { + index = tidx * 4; + fprintf(outfile, "%4d %d %d %d %d\n", tidx + in->firstnumber, + tet2facelist[index], tet2facelist[index+1], + tet2facelist[index+2], tet2facelist[index+3]); + } + fclose(outfile); + delete [] tet2facelist; + } else { + // Simply copy the address of the list to the output. + out->tet2facelist = tet2facelist; + } + } } -/////////////////////////////////////////////////////////////////////////////// -// // -// outhullfaces() Output hull faces to a .face file or a tetgenio object. // -// // -// The normal of each face is pointing to the outside of the domain. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// outhullfaces() Output hull faces to a .face file or a tetgenio object. // +// // +// The normal of each face is pointing to the outside of the domain. // +// // +//============================================================================// void tetgenmesh::outhullfaces(tetgenio* out) { @@ -29333,16 +34292,16 @@ void tetgenmesh::outhullfaces(tetgenio* out) } } -/////////////////////////////////////////////////////////////////////////////// -// // -// outsubfaces() Output subfaces (i.e. boundary faces) to a .face file or // -// a tetgenio structure. // -// // -// The boundary faces are found in 'subfaces'. For listing triangle vertices // -// in the same sense for all triangles in the mesh, the direction determined // -// by right-hand rule is pointer to the inside of the volume. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// outsubfaces() Output subfaces (i.e. boundary faces) to a .face file or // +// a tetgenio structure. // +// // +// The boundary faces are found in 'subfaces'. For listing triangle vertices // +// in the same sense for all triangles in the mesh, the direction determined // +// by right-hand rule is pointer to the inside of the volume. // +// // +//============================================================================// void tetgenmesh::outsubfaces(tetgenio* out) { @@ -29354,7 +34313,7 @@ void tetgenmesh::outsubfaces(tetgenio* out) triface abuttingtet; face faceloop; point torg, tdest, tapex; - int faceid = 0, marker = 0; + int marker = 0; int firstindex, shift; int neigh1 = 0, neigh2 = 0; int facenumber; @@ -29406,8 +34365,8 @@ void tetgenmesh::outsubfaces(tetgenio* out) } if (b->neighout > 1) { // '-nn' switch. - out->adjtetlist = new int[subfaces->items * 2]; - if (out->adjtetlist == (int *) NULL) { + out->face2tetlist = new int[subfaces->items * 2]; + if (out->face2tetlist == (int *) NULL) { terminatetetgen(this, 1); } } @@ -29434,7 +34393,6 @@ void tetgenmesh::outsubfaces(tetgenio* out) if (abuttingtet.tet != NULL) { if (ishulltet(abuttingtet)) { fsymself(abuttingtet); - assert(!ishulltet(abuttingtet)); } } if (abuttingtet.tet != NULL) { @@ -29463,20 +34421,7 @@ void tetgenmesh::outsubfaces(tetgenio* out) } } if (!b->nobound) { - if (b->refine) { // -r option. - if (in->trifacemarkerlist) { - marker = shellmark(faceloop); - } else { - marker = 1; // Default marker for a subface is 1. - } - } else { - if (in->facetmarkerlist) { - faceid = shellmark(faceloop) - 1; - marker = in->facetmarkerlist[faceid]; - } else { - marker = 1; // Default marker for a subface is 1. - } - } + marker = shellmark(faceloop); } if (b->neighout > 1) { // '-nn' switch. Output adjacent tets indices. @@ -29484,7 +34429,9 @@ void tetgenmesh::outsubfaces(tetgenio* out) neigh2 = -1; stpivot(faceloop, abuttingtet); if (abuttingtet.tet != NULL) { - neigh1 = elemindex(abuttingtet.tet); + if (!ishulltet(abuttingtet)) { + neigh1 = elemindex(abuttingtet.tet); + } fsymself(abuttingtet); if (!ishulltet(abuttingtet)) { neigh2 = elemindex(abuttingtet.tet); @@ -29520,8 +34467,8 @@ void tetgenmesh::outsubfaces(tetgenio* out) emlist[index1++] = marker; } if (b->neighout > 1) { - out->adjtetlist[index2++] = neigh1; - out->adjtetlist[index2++] = neigh2; + out->face2tetlist[index2++] = neigh1; + out->face2tetlist[index2++] = neigh2; } } facenumber++; @@ -29534,14 +34481,14 @@ void tetgenmesh::outsubfaces(tetgenio* out) } } -/////////////////////////////////////////////////////////////////////////////// -// // -// outedges() Output all edges to a .edge file or a tetgenio object. // -// // -// Note: This routine must be called after outelements(), so that the total // -// number of edges 'meshedges' has been counted. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// outedges() Output all edges to a .edge file or a tetgenio object. // +// // +// Note: This routine must be called after outelements(), so that the total // +// number of edges 'meshedges' has been counted. // +// // +//============================================================================// void tetgenmesh::outedges(tetgenio* out) { @@ -29550,7 +34497,6 @@ void tetgenmesh::outedges(tetgenio* out) triface tetloop, worktet, spintet; face checkseg; point torg, tdest; - int *elist = NULL, *emlist = NULL; int ishulledge; int firstindex, shift; int edgenumber, marker; @@ -29563,6 +34509,10 @@ void tetgenmesh::outedges(tetgenio* out) int highorderindex = 11; int o2index = 0; + // For -nn option. + int *tet2edgelist = NULL; + int tidx; + if (out == (tetgenio *) NULL) { strcpy(edgefilename, b->outfilename); strcat(edgefilename, ".edge"); @@ -29589,6 +34539,7 @@ void tetgenmesh::outedges(tetgenio* out) meshedges = vsize + fsize - tsize - 1; } } + meshhulledges = 0l; // It will be counted. if (out == (tetgenio *) NULL) { outfile = fopen(edgefilename, "w"); @@ -29600,6 +34551,7 @@ void tetgenmesh::outedges(tetgenio* out) fprintf(outfile, "%ld %d\n", meshedges, !b->nobound); } else { // Allocate memory for 'edgelist'. + out->numberofedges = meshedges; out->edgelist = new int[meshedges * 2]; if (out->edgelist == (int *) NULL) { printf("Error: Out of memory.\n"); @@ -29612,11 +34564,14 @@ void tetgenmesh::outedges(tetgenio* out) out->edgemarkerlist = new int[meshedges]; } if (b->neighout > 1) { // '-nn' switch. - out->edgeadjtetlist = new int[meshedges]; + out->edge2tetlist = new int[meshedges]; } - out->numberofedges = meshedges; - elist = out->edgelist; - emlist = out->edgemarkerlist; + } + + if (b->neighout > 1) { // -nn option + // Output the tetrahedron-to-edge map. + long tsize = tetrahedrons->items - hullsize; + tet2edgelist = new int[tsize * 6]; } // Determine the first index (0 or 1). @@ -29644,8 +34599,9 @@ void tetgenmesh::outedges(tetgenio* out) } fnextself(spintet); } while (spintet.tet != worktet.tet); - // Count this edge if no adjacent tets are smaller than this tet. if (spintet.tet == worktet.tet) { + // Found a new edge. + if (ishulledge) meshhulledges++; torg = org(worktet); tdest = dest(worktet); if (b->order == 2) { // -o2 @@ -29661,8 +34617,8 @@ void tetgenmesh::outedges(tetgenio* out) } } else { // Output three vertices of this face; - elist[index++] = pointmark(torg) - shift; - elist[index++] = pointmark(tdest) - shift; + out->edgelist[index++] = pointmark(torg) - shift; + out->edgelist[index++] = pointmark(tdest) - shift; if (b->order == 2) { // -o2 out->o2edgelist[o2index++] = pointmark(pp) - shift; } @@ -29673,9 +34629,6 @@ void tetgenmesh::outedges(tetgenio* out) tsspivot1(worktet, checkseg); if (checkseg.sh != NULL) { marker = shellmark(checkseg); - if (marker == 0) { // Does it have no marker? - marker = 1; // Set the default marker for this segment. - } } else { marker = 0; // It's not a segment. } @@ -29686,15 +34639,25 @@ void tetgenmesh::outedges(tetgenio* out) if (out == (tetgenio *) NULL) { fprintf(outfile, " %d", marker); } else { - emlist[index1++] = marker; + out->edgemarkerlist[index1++] = marker; } } if (b->neighout > 1) { // '-nn' switch. if (out == (tetgenio *) NULL) { fprintf(outfile, " %d", elemindex(tetloop.tet)); } else { - out->edgeadjtetlist[index2++] = elemindex(tetloop.tet); - } + out->edge2tetlist[index2++] = elemindex(tetloop.tet); + } + // Fill the tetrahedron-to-edge map. + spintet = worktet; + while (1) { + if (!ishulltet(spintet)) { + tidx = elemindex(spintet.tet) - firstindex; + tet2edgelist[tidx * 6 + ver2edge[spintet.ver]] = edgenumber; + } + fnextself(spintet); + if (spintet.tet == worktet.tet) break; + } } if (out == (tetgenio *) NULL) { fprintf(outfile, "\n"); @@ -29709,13 +34672,100 @@ void tetgenmesh::outedges(tetgenio* out) fprintf(outfile, "# Generated by %s\n", b->commandline); fclose(outfile); } + + if (b->neighout > 1) { // -nn option + long tsize = tetrahedrons->items - hullsize; + + if (b->facesout) { // -f option + // Build the face-to-edge map (use the tet-to-edge map). + long fsize = (tsize * 4l + hullsize) / 2l; + int *face2edgelist = new int[fsize * 3]; + + tetrahedrons->traversalinit(); + tetloop.tet = tetrahedrontraverse(); + int facenumber = 0; // firstindex; // in->firstnumber; + while (tetloop.tet != (tetrahedron *) NULL) { + for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) { + fsym(tetloop, spintet); + if (ishulltet(spintet) || + (elemindex(tetloop.tet) < elemindex(spintet.tet))) { + // The three edges of this face are ordered such that the + // first edge is opposite to the first vertex of this face + // that appears in the .face file, and so on. + tidx = elemindex(tetloop.tet) - firstindex; + worktet = tetloop; + for (i = 0; i < 3; i++) { + enextself(worktet); // The edge opposite to vertex i. + int eidx = tet2edgelist[tidx * 6 + ver2edge[worktet.ver]]; + face2edgelist[facenumber * 3 + i] = eidx; + } + facenumber++; + } + } + tetloop.tet = tetrahedrontraverse(); + } + + // Output the face-to-edge map. + if (out == (tetgenio *) NULL) { + strcpy(edgefilename, b->outfilename); + strcat(edgefilename, ".f2e"); + } + if (!b->quiet) { + if (out == (tetgenio *) NULL) { + printf("Writing %s.\n", edgefilename); + } else { + printf("Writing face-to-edge map.\n"); + } + } + if (out == (tetgenio *) NULL) { + outfile = fopen(edgefilename, "w"); + for (tidx = 0; tidx < fsize; tidx++) { // Re-use `tidx' + i = tidx * 3; + fprintf(outfile, "%4d %d %d %d\n", tidx + in->firstnumber, + face2edgelist[i], face2edgelist[i+1], face2edgelist[i+2]); + } + fclose(outfile); + delete [] face2edgelist; + } else { + // Simply copy the address of the list to the output. + out->face2edgelist = face2edgelist; + } + } // if (b->facesout) + + // Output the tetrahedron-to-edge map. + if (out == (tetgenio *) NULL) { + strcpy(edgefilename, b->outfilename); + strcat(edgefilename, ".t2e"); + } + if (!b->quiet) { + if (out == (tetgenio *) NULL) { + printf("Writing %s.\n", edgefilename); + } else { + printf("Writing tetrahedron-to-edge map.\n"); + } + } + if (out == (tetgenio *) NULL) { + outfile = fopen(edgefilename, "w"); + for (tidx = 0; tidx < tsize; tidx++) { + i = tidx * 6; + fprintf(outfile, "%4d %d %d %d %d %d %d\n", tidx + in->firstnumber, + tet2edgelist[i], tet2edgelist[i+1], tet2edgelist[i+2], + tet2edgelist[i+3], tet2edgelist[i+4], tet2edgelist[i+5]); + } + fclose(outfile); + delete [] tet2edgelist; + } else { + // Simply copy the address of the list to the output. + out->tet2edgelist = tet2edgelist; + } + } } -/////////////////////////////////////////////////////////////////////////////// -// // -// outsubsegments() Output segments to a .edge file or a structure. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// outsubsegments() Output segments to a .edge file or a structure. // +// // +//============================================================================// void tetgenmesh::outsubsegments(tetgenio* out) { @@ -29776,7 +34826,7 @@ void tetgenmesh::outsubsegments(tetgenio* out) terminatetetgen(this, 1); } if (b->neighout > 1) { - out->edgeadjtetlist = new int[subsegs->items]; + out->edge2tetlist = new int[subsegs->items]; } out->numberofedges = subsegs->items; elist = out->edgelist; @@ -29808,7 +34858,6 @@ void tetgenmesh::outsubsegments(tetgenio* out) if (!ishulltet(spintet)) break; if (spintet.tet == workface.tet) break; } - assert(!ishulltet(spintet)); workface = spintet; } } @@ -29853,7 +34902,7 @@ void tetgenmesh::outsubsegments(tetgenio* out) } out->edgemarkerlist[i++] = marker; if (b->neighout > 1) { // -nn - out->edgeadjtetlist[index2++] = neigh; + out->edge2tetlist[index2++] = neigh; } } edgenumber++; @@ -29866,11 +34915,11 @@ void tetgenmesh::outsubsegments(tetgenio* out) } } -/////////////////////////////////////////////////////////////////////////////// -// // -// outneighbors() Output tet neighbors to a .neigh file or a structure. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// outneighbors() Output tet neighbors to a .neigh file or a structure. // +// // +//============================================================================// void tetgenmesh::outneighbors(tetgenio* out) { @@ -29952,26 +35001,26 @@ void tetgenmesh::outneighbors(tetgenio* out) } } -/////////////////////////////////////////////////////////////////////////////// -// // -// outvoronoi() Output the Voronoi diagram to .v.node, .v.edge, v.face, // -// and .v.cell. // -// // -// The Voronoi diagram is the geometric dual of the Delaunay triangulation. // -// The Voronoi vertices are the circumcenters of Delaunay tetrahedra. Each // -// Voronoi edge connects two Voronoi vertices at two sides of a common Dela- // -// unay face. At a face of convex hull, it becomes a ray (goto the infinity).// -// A Voronoi face is the convex hull of all Voronoi vertices around a common // -// Delaunay edge. It is a closed polygon for any internal Delaunay edge. At a// -// ridge, it is unbounded. Each Voronoi cell is the convex hull of all Vor- // -// onoi vertices around a common Delaunay vertex. It is a polytope for any // -// internal Delaunay vertex. It is an unbounded polyhedron for a Delaunay // -// vertex belonging to the convex hull. // -// // -// NOTE: This routine is only used when the input is only a set of point. // -// Comment: Special thanks to Victor Liu for finding and fixing few bugs. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// outvoronoi() Output the Voronoi diagram to .v.node, .v.edge, v.face, // +// and .v.cell. // +// // +// The Voronoi diagram is the geometric dual of the Delaunay triangulation. // +// The Voronoi vertices are the circumcenters of Delaunay tetrahedra. Each // +// Voronoi edge connects two Voronoi vertices at two sides of a common Dela- // +// unay face. At a face of convex hull, it becomes a ray (goto the infinity). // +// A Voronoi face is the convex hull of all Voronoi vertices around a common // +// Delaunay edge. It is a closed polygon for any internal Delaunay edge. At a // +// ridge, it is unbounded. Each Voronoi cell is the convex hull of all Vor- // +// onoi vertices around a common Delaunay vertex. It is a polytope for any // +// internal Delaunay vertex. It is an unbounded polyhedron for a Delaunay // +// vertex belonging to the convex hull. // +// // +// NOTE: This routine is only used when the input is only a set of point. // +// Comment: Special thanks to Victor Liu for finding and fixing few bugs. // +// // +//============================================================================// void tetgenmesh::outvoronoi(tetgenio* out) { @@ -30035,7 +35084,14 @@ void tetgenmesh::outvoronoi(tetgenio* out) // The number of Delaunay edges (Voronoi faces). long vsize = points->items - dupverts - unuverts; if (b->weighted) vsize -= nonregularcount; - edges = vsize + faces - ntets - 1; + if (!nonconvex) { + edges = vsize + faces - ntets - 1; + } else { + if (meshedges == 0l) { + numberedges(); // Count edges. + } + edges = meshedges; + } if (out == (tetgenio *) NULL) { outfile = fopen(outfilename, "w"); @@ -30419,16 +35475,16 @@ void tetgenmesh::outvoronoi(tetgenio* out) } } -/////////////////////////////////////////////////////////////////////////////// -// // -// outsmesh() Write surface mesh to a .smesh file, which can be read and // -// tetrahedralized by TetGen. // -// // -// You can specify a filename (without suffix) in 'smfilename'. If you don't // -// supply a filename (let smfilename be NULL), the default name stored in // -// 'tetgenbehavior' will be used. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// outsmesh() Write surface mesh to a .smesh file, which can be read and // +// tetrahedralized by TetGen. // +// // +// You can specify a filename (without suffix) in 'smfilename'. If you don't // +// supply a filename (let smfilename be NULL), the default name stored in // +// 'tetgenbehavior' will be used. // +// // +//============================================================================// void tetgenmesh::outsmesh(char* smfilename) { @@ -30439,7 +35495,7 @@ void tetgenmesh::outsmesh(char* smfilename) point p1, p2, p3; int firstindex, shift; int bmark; - int faceid, marker; + int marker; int i; if (smfilename != (char *) NULL && smfilename[0] != '\0') { @@ -30474,7 +35530,7 @@ void tetgenmesh::outsmesh(char* smfilename) fprintf(outfile, "0 3 0 0 # nodes are found in %s.\n", nodfilename); marker = 0; // avoid compile warning. - bmark = !b->nobound && in->facetmarkerlist; + bmark = !b->nobound && (in->facetmarkerlist || in->trifacemarkerlist); fprintf(outfile, "\n# part 2: facet list.\n"); // Number of facets, boundary marker. @@ -30487,12 +35543,7 @@ void tetgenmesh::outsmesh(char* smfilename) p2 = sdest(faceloop); p3 = sapex(faceloop); if (bmark) { - faceid = shellmark(faceloop) - 1; - if (faceid >= 0) { - marker = in->facetmarkerlist[faceid]; - } else { - marker = 0; // This subface must be added manually later. - } + marker = shellmark(faceloop); } fprintf(outfile, "3 %4d %4d %4d", pointmark(p1) - shift, pointmark(p2) - shift, pointmark(p3) - shift); @@ -30526,16 +35577,16 @@ void tetgenmesh::outsmesh(char* smfilename) fclose(outfile); } -/////////////////////////////////////////////////////////////////////////////// -// // -// outmesh2medit() Write mesh to a .mesh file, which can be read and // -// rendered by Medit (a free mesh viewer from INRIA). // -// // -// You can specify a filename (without suffix) in 'mfilename'. If you don't // -// supply a filename (let mfilename be NULL), the default name stored in // -// 'tetgenbehavior' will be used. The output file will have the suffix .mesh.// -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// outmesh2medit() Write mesh to a .mesh file, which can be read and // +// rendered by Medit (a free mesh viewer from INRIA). // +// // +// You can specify a filename (without suffix) in 'mfilename'. If you don't // +// supply a filename (let mfilename be NULL), the default name stored in // +// 'tetgenbehavior' will be used. The output file will have the suffix .mesh. // +// // +//============================================================================// void tetgenmesh::outmesh2medit(char* mfilename) { @@ -30543,11 +35594,11 @@ void tetgenmesh::outmesh2medit(char* mfilename) char mefilename[FILENAMESIZE]; tetrahedron* tetptr; triface tface, tsymface; - face segloop, checkmark; + face faceloop, segloop, checkmark; point ptloop, p1, p2, p3, p4; long ntets, faces; int pointnumber; - int faceid, marker; + int marker; int i; if (mfilename != (char *) NULL && mfilename[0] != '\0') { @@ -30559,6 +35610,17 @@ void tetgenmesh::outmesh2medit(char* mfilename) } strcat(mefilename, ".mesh"); + int *subdomains_facets = NULL; + int *subdomains_facets_ori = NULL; + int sub_count = 0; // Count the number of indexed subdomains. + if (subdomains > 0) { + subdomains_facets = new int[subdomains]; + subdomains_facets_ori = new int[subdomains]; + for (i = 0; i < subdomains; i++) { + subdomains_facets_ori[i] = 0; // initialise + } + } + if (!b->quiet) { printf("Writing %s.\n", mefilename); } @@ -30590,49 +35652,90 @@ void tetgenmesh::outmesh2medit(char* mfilename) } else { fprintf(outfile, " 0\n"); } - setpointmark(ptloop, pointnumber); - ptloop = pointtraverse(); - pointnumber++; + setpointmark(ptloop, pointnumber); + ptloop = pointtraverse(); + pointnumber++; + } + + if (b->plc || b->refine) { + fprintf(outfile, "\nEdges\n"); + fprintf(outfile, "%ld\n", subsegs->items); + + subsegs->traversalinit(); + segloop.sh = shellfacetraverse(subsegs); + while (segloop.sh != (shellface *) NULL) { + p1 = sorg(segloop); + p2 = sdest(segloop); + fprintf(outfile, "%5d %5d", pointmark(p1), pointmark(p2)); + marker = shellmark(segloop); + fprintf(outfile, " %d\n", marker); + segloop.sh = shellfacetraverse(subsegs); + } } - // Compute the number of faces. ntets = tetrahedrons->items - hullsize; - faces = (ntets * 4l + hullsize) / 2l; + + faces = subfaces->items; + triface abuttingtet; + int t1ver; fprintf(outfile, "\n# Set of Triangles\n"); fprintf(outfile, "Triangles\n"); fprintf(outfile, "%ld\n", faces); - - tetrahedrons->traversalinit(); - tface.tet = tetrahedrontraverse(); - while (tface.tet != (tetrahedron *) NULL) { - for (tface.ver = 0; tface.ver < 4; tface.ver ++) { - fsym(tface, tsymface); - if (ishulltet(tsymface) || - (elemindex(tface.tet) < elemindex(tsymface.tet))) { - p1 = org (tface); - p2 = dest(tface); - p3 = apex(tface); - fprintf(outfile, "%5d %5d %5d", - pointmark(p1), pointmark(p2), pointmark(p3)); - // Check if it is a subface. - tspivot(tface, checkmark); - if (checkmark.sh == NULL) { - marker = 0; // It is an inner face. It's marker is 0. - } else { - if (in->facetmarkerlist) { - // The facet marker is given, get it. - faceid = shellmark(checkmark) - 1; - marker = in->facetmarkerlist[faceid]; - } else { - marker = 1; // The default marker for subface is 1. - } - } - fprintf(outfile, " %d\n", marker); + + subfaces->traversalinit(); + faceloop.sh = shellfacetraverse(subfaces); + int facidx = 1; // Index facets for subdomains. + while (faceloop.sh != (shellface *) NULL) { + stpivot(faceloop, abuttingtet); + if (abuttingtet.tet != NULL) { + if (ishulltet(abuttingtet)) { + fsymself(abuttingtet); } } - tface.tet = tetrahedrontraverse(); - } + if (abuttingtet.tet != NULL) { + p1 = org (abuttingtet); + p2 = dest(abuttingtet); + p3 = apex(abuttingtet); + if (subdomains) { + int attr = elemattribute(abuttingtet.tet, 0); + int idx = attr - 1; + if (subdomain_markers[idx] != attr) { + // search it. + } + if (subdomains_facets_ori[idx] == 0) { + subdomains_facets[idx] = facidx; + subdomains_facets_ori[idx] = 1; + sub_count++; + fsym(abuttingtet, tsymface); + if ((tsymface.tet != NULL) && !ishulltet(tsymface)) { + attr = elemattribute(tsymface.tet, 0); + idx = attr - 1; + if (subdomain_markers[idx] != attr) { + // search it. + } + if (subdomains_facets_ori[idx] == 0) { + subdomains_facets[idx] = facidx; + subdomains_facets_ori[idx] = -1; + sub_count++; + } + } + } + } + } else { + // A dangling subfacet. + p1 = sorg(faceloop); + p2 = sdest(faceloop); + p3 = sapex(faceloop); + } + marker = shellmark(faceloop); + fprintf(outfile, "%5d %5d %5d %d\n", + pointmark(p1), pointmark(p2), pointmark(p3), marker); + //setelemindex(faceloop.sh, facidx); + facidx++; + faceloop.sh = shellfacetraverse(subfaces); + } + fprintf(outfile, "\n# Set of Tetrahedra\n"); fprintf(outfile, "Tetrahedra\n"); @@ -30661,27 +35764,23 @@ void tetgenmesh::outmesh2medit(char* mfilename) tetptr = tetrahedrontraverse(); } - fprintf(outfile, "\nCorners\n"); - fprintf(outfile, "%d\n", in->numberofpoints); - - for (i = 0; i < in->numberofpoints; i++) { - fprintf(outfile, "%4d\n", i + 1); - } - - if (b->plc || b->refine) { - fprintf(outfile, "\nEdges\n"); - fprintf(outfile, "%ld\n", subsegs->items); + //fprintf(outfile, "\nCorners\n"); + //fprintf(outfile, "%d\n", in->numberofpoints); + //for (i = 0; i < in->numberofpoints; i++) { + // fprintf(outfile, "%4d\n", i + 1); + //} - subsegs->traversalinit(); - segloop.sh = shellfacetraverse(subsegs); - while (segloop.sh != (shellface *) NULL) { - p1 = sorg(segloop); - p2 = sdest(segloop); - fprintf(outfile, "%5d %5d", pointmark(p1), pointmark(p2)); - marker = shellmark(segloop); - fprintf(outfile, " %d\n", marker); - segloop.sh = shellfacetraverse(subsegs); - } + if (subdomains > 0) { + fprintf(outfile, "\nSubDomainFromGeom\n"); + fprintf(outfile, "%d\n", subdomains); + for (i = 0; i < subdomains; i++) { + fprintf(outfile, "3 %d %d %d\n", + subdomains_facets[i], + subdomains_facets_ori[i], + subdomain_markers[i]); + } + delete [] subdomains_facets; + delete [] subdomains_facets_ori; } fprintf(outfile, "\nEnd\n"); @@ -30690,15 +35789,17 @@ void tetgenmesh::outmesh2medit(char* mfilename) -/////////////////////////////////////////////////////////////////////////////// -// // -// outmesh2vtk() Save mesh to file in VTK Legacy format. // -// // -// This function was contributed by Bryn Llyod from ETH, 2007. // -// // -/////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::outmesh2vtk(char* ofilename) + +//============================================================================// +// // +// outmesh2vtk() Save mesh to file in VTK Legacy format. // +// // +// This function was contributed by Bryn Llyod from ETH, 2007. // +// // +//============================================================================// + +void tetgenmesh::outmesh2vtk(char* ofilename, int mesh_idx) { FILE *outfile; char vtkfilename[FILENAMESIZE]; @@ -30718,13 +35819,14 @@ void tetgenmesh::outmesh2vtk(char* ofilename) int NN = points->items; if (ofilename != (char *) NULL && ofilename[0] != '\0') { - strcpy(vtkfilename, ofilename); + //strcpy(vtkfilename, ofilename); + sprintf(vtkfilename, "%s.%d.vtk", ofilename, mesh_idx); } else if (b->outfilename[0] != '\0') { strcpy(vtkfilename, b->outfilename); + strcat(vtkfilename, ".vtk"); } else { - strcpy(vtkfilename, "unnamed"); + strcpy(vtkfilename, "noname.vtk"); } - strcat(vtkfilename, ".vtk"); if (!b->quiet) { printf("Writing %s.\n", vtkfilename); @@ -30803,43 +35905,223 @@ void tetgenmesh::outmesh2vtk(char* ofilename) fclose(outfile); } -//// //// -//// //// -//// output_cxx /////////////////////////////////////////////////////////////// +void tetgenmesh::out_surfmesh_vtk(char* ofilename, int mesh_idx) +{ + FILE *outfile; + char vtkfilename[FILENAMESIZE]; + triface abuttingtet; + face faceloop; + point pointloop, torg, tdest, tapex; + double x, y, z; + int n1, n2, n3; + int nnodes = 3; + int celltype = 5; // triangle -//// main_cxx ///////////////////////////////////////////////////////////////// -//// //// -//// //// + int t1ver; -/////////////////////////////////////////////////////////////////////////////// -// // -// tetrahedralize() The interface for users using TetGen library to // -// generate tetrahedral meshes with all features. // -// // -// The sequence is roughly as follows. Many of these steps can be skipped, // -// depending on the command line switches. // -// // -// - Initialize constants and parse the command line. // -// - Read the vertices from a file and either // -// - tetrahedralize them (no -r), or // -// - read an old mesh from files and reconstruct it (-r). // -// - Insert the boundary segments and facets (-p or -Y). // -// - Read the holes (-p), regional attributes (-pA), and regional volume // -// constraints (-pa). Carve the holes and concavities, and spread the // -// regional attributes and volume constraints. // -// - Enforce the constraints on minimum quality bound (-q) and maximum // -// volume (-a), and a mesh size function (-m). // -// - Optimize the mesh wrt. specified quality measures (-O and -o). // -// - Write the output files and print the statistics. // -// - Check the consistency of the mesh (-C). // -// // -/////////////////////////////////////////////////////////////////////////////// + if (b->order == 2) { + printf(" Write VTK not implemented for order 2 elements \n"); + return; + } + + int NEL = subfaces->items; //tetrahedrons->items - hullsize; + int NN = points->items; + + if (ofilename != (char *) NULL && ofilename[0] != '\0') { + //strcpy(vtkfilename, ofilename); + sprintf(vtkfilename, "%s.%d.vtk", ofilename, mesh_idx); + } else if (b->outfilename[0] != '\0') { + strcpy(vtkfilename, b->outfilename); + strcat(vtkfilename, ".surf.vtk"); + } else { + strcpy(vtkfilename, "noname.surf.vtk"); + } + + if (!b->quiet) { + printf("Writing %s.\n", vtkfilename); + } + outfile = fopen(vtkfilename, "w"); + if (outfile == (FILE *) NULL) { + printf("File I/O Error: Cannot create file %s.\n", vtkfilename); + return; + } + + //always write big endian + //bool ImALittleEndian = !testIsBigEndian(); + + fprintf(outfile, "# vtk DataFile Version 2.0\n"); + fprintf(outfile, "Unstructured Grid\n"); + fprintf(outfile, "ASCII\n"); // BINARY + fprintf(outfile, "DATASET UNSTRUCTURED_GRID\n"); + fprintf(outfile, "POINTS %d double\n", NN); + + points->traversalinit(); + pointloop = pointtraverse(); + for(int id=0; idtraversalinit(); + faceloop.sh = shellfacetraverse(subfaces); + //facenumber = firstindex; // in->firstnumber; + while (faceloop.sh != (shellface *) NULL) { + stpivot(faceloop, abuttingtet); + // If there is a tetrahedron containing this subface, orient it so + // that the normal of this face points to inside of the volume by + // right-hand rule. + if (abuttingtet.tet != NULL) { + if (ishulltet(abuttingtet)) { + fsymself(abuttingtet); + } + } + if (abuttingtet.tet != NULL) { + torg = org(abuttingtet); + tdest = dest(abuttingtet); + tapex = apex(abuttingtet); + } else { + // This may happen when only a surface mesh be generated. + torg = sorg(faceloop); + tdest = sdest(faceloop); + tapex = sapex(faceloop); + } + + n1 = pointmark(torg) - in->firstnumber; + n2 = pointmark(tdest) - in->firstnumber; + n3 = pointmark(tapex) - in->firstnumber; + + fprintf(outfile, "%d %4d %4d %4d\n", nnodes, n1, n2, n3); + //facenumber++; + faceloop.sh = shellfacetraverse(subfaces); + } + fprintf(outfile, "\n"); + + fprintf(outfile, "CELL_TYPES %d\n", NEL); + for(int tid=0; tidfacetmarkerlist != NULL) { //if (numelemattrib > 0) { + // Output tetrahedra region attributes. + fprintf(outfile, "CELL_DATA %d\n", NEL); + fprintf(outfile, "SCALARS cell_scalars int 1\n"); + fprintf(outfile, "LOOKUP_TABLE default\n"); + + subfaces->traversalinit(); + faceloop.sh = shellfacetraverse(subfaces); + //facenumber = firstindex; // in->firstnumber; + while (faceloop.sh != (shellface *) NULL) { + fprintf(outfile, "%d\n", (int) shellmark(faceloop)); + //facenumber++; + faceloop.sh = shellfacetraverse(subfaces); + } + fprintf(outfile, "\n"); + } + + fclose(outfile); +} + +//============================================================================// +// // +// out_intersected_facets() Save skipped subfaces. // +// // +//============================================================================// + +void tetgenmesh::out_intersected_facets() +{ + char FileName[1024], *sptr; + + strcpy(FileName, b->outfilename); + sptr = strrchr(b->outfilename, '.'); + if (sptr != NULL) *sptr = '\0'; + strcat(b->outfilename, "_skipped"); + + outnodes(NULL); + + strcpy(b->outfilename, FileName); // Restore the original file name. + + strcpy(FileName, b->outfilename); + sptr = strrchr(FileName, '.'); + if (sptr != NULL) *sptr = '\0'; + strcat(FileName, "_skipped.face"); + FILE *fout = fopen(FileName, "w"); + + if (!b->quiet) { + printf("Writing %s\n", FileName); + } + + // Determine the first index (0 or 1). + int firstindex = b->zeroindex ? 0 : in->firstnumber; + int shift = 0; // Default no shiftment. + if ((in->firstnumber == 1) && (firstindex == 0)) { + shift = 1; // Shift the output indices by 1. + } + + int facenumber = firstindex; // in->firstnumber; + + fprintf(fout, "%ld 1\n", skipped_facet_list->objects); + + for (int i = 0; i < (int) skipped_facet_list->objects; i++) { + badface *bf = (badface *) fastlookup(skipped_facet_list, i); + fprintf(fout, "%d %d %d %d %d\n", facenumber, + pointmark(bf->forg) - shift, + pointmark(bf->fdest) - shift, + pointmark(bf->fapex) - shift, + (int) bf->key); + // remove it from the pool of subfaces (do not output them to .face). + shellfacedealloc(subfaces, bf->ss.sh); + facenumber++; + } + + fclose(fout); +} + + +// // +// // +//== output_cxx ==============================================================// + +//== main_cxx ================================================================// +// // +// // + +//============================================================================// +// // +// tetrahedralize() The interface for users using TetGen library to // +// generate tetrahedral meshes with all features. // +// // +// The sequence is roughly as follows. Many of these steps can be skipped, // +// depending on the command line switches. // +// // +// - Initialize constants and parse the command line. // +// - Read the vertices from a file and either // +// - tetrahedralize them (no -r), or // +// - read an old mesh from files and reconstruct it (-r). // +// - Insert the boundary segments and facets (-p or -Y). // +// - Read the holes (-p), regional attributes (-pA), and regional volume // +// constraints (-pa). Carve the holes and concavities, and spread the // +// regional attributes and volume constraints. // +// - Enforce the constraints on minimum quality bound (-q) and maximum // +// volume (-a), and a mesh size function (-m). // +// - Optimize the mesh wrt. specified quality measures (-O and -o). // +// - Write the output files and print the statistics. // +// - Check the consistency of the mesh (-C). // +// // +//============================================================================// void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, tetgenio *addin, tetgenio *bgmin) { tetgenmesh m; - clock_t tv[12], ts[5]; // Timing informations (defined in time.h) + clock_t tv[13], ts[6]; // Timing informations (defined in time.h) REAL cps = (REAL) CLOCKS_PER_SEC; tv[0] = clock(); @@ -30857,8 +36139,6 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, m.initializepools(); m.transfernodes(); - exactinit(b->verbose, b->noexact, b->nostaticfilter, - m.xmax - m.xmin, m.ymax - m.ymin, m.zmax - m.zmin); tv[1] = clock(); @@ -30877,6 +36157,7 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, printf("Delaunay seconds: %g\n", ((REAL)(tv[2]-tv[1])) / cps); if (b->verbose) { printf(" Point sorting seconds: %g\n", ((REAL)(ts[0]-tv[1])) / cps); + } } } @@ -30889,26 +36170,9 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, if (!b->quiet) { printf("Surface mesh seconds: %g\n", ((REAL)(ts[0]-tv[2])) / cps); } - - if (b->diagnose) { // -d - m.detectinterfaces(); - - ts[1] = clock(); - - if (!b->quiet) { - printf("Self-intersection seconds: %g\n", ((REAL)(ts[1]-ts[0])) / cps); - } - - // Only output when self-intersecting faces exist. - if (m.subfaces->items > 0l) { - m.outnodes(out); - m.outsubfaces(out); - } - - return; - } } + tv[3] = clock(); if ((b->metric) && (m.bgm != NULL)) { // -m @@ -30937,7 +36201,7 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, tv[4] = clock(); if (b->plc && !b->refine) { // -p - if (b->nobisect) { // -Y + if (!b->cdt) { // no -D m.recoverboundary(ts[0]); } else { m.constraineddelaunay(ts[0]); @@ -30946,7 +36210,7 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, ts[1] = clock(); if (!b->quiet) { - if (b->nobisect) { + if (!b->cdt) { // no -D printf("Boundary recovery "); } else { printf("Constrained Delaunay "); @@ -30958,6 +36222,31 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, } } + if (m.skipped_facet_list != NULL) { + if (!b->quiet) { + printf("\n!!! %ld input triangles are skipped due to self-intersections.\n", + m.skipped_facet_list->objects); + } + + if (!b->nofacewritten) m.out_intersected_facets(); + delete m.skipped_facet_list; + m.skipped_facet_list = NULL; + + if (!b->nonodewritten) m.outnodes(out); + if (!b->noelewritten) m.outelements(out); + if (!b->nofacewritten) m.outsubfaces(out); + if (!b->nofacewritten) m.outsubsegments(out); + + terminatetetgen(NULL, 3); // This is not a normal exit. + } + + if (b->diagnose) { // -d + if (!b->quiet) { + printf("\nThe input surface mesh is correct.\n"); + } + return; + } + m.carveholes(); ts[2] = clock(); @@ -30966,42 +36255,54 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, printf("Exterior tets removal seconds: %g\n",((REAL)(ts[2]-ts[1]))/cps); } - if (b->nobisect) { // -Y + ts[3] = clock(); + + if ((!b->cdt || b->nobisect) && (b->supsteiner_level > 0)) { // no -D, -Y/1 if (m.subvertstack->objects > 0l) { m.suppresssteinerpoints(); - - ts[3] = clock(); - if (!b->quiet) { - printf("Steiner suppression seconds: %g\n", - ((REAL)(ts[3]-ts[2]))/cps); + printf("Steiner suppression seconds: %g\n", ((REAL)(ts[3]-ts[2]))/cps); } } } + + if ((b->nobisect > 1)) { // -YY + if ((m.st_segref_count > 0) || (m.st_facref_count > 0)) { + if (!b->nonodewritten) m.outnodes(out); + if (!b->noelewritten) m.outelements(out); + if (!b->nofacewritten) m.outsubfaces(out); + if (!b->nofacewritten) m.outsubsegments(out); + printf("!! Boundary contains Steiner points (-YY option). Program stopped.\n"); + terminatetetgen(&m, 200); + } + } } tv[5] = clock(); - if (b->coarsen) { // -R + if (b->metric || b->coarsen) { // -m or -R m.meshcoarsening(); } tv[6] = clock(); if (!b->quiet) { - if (b->coarsen) { + if (b->metric || b->coarsen) { printf("Mesh coarsening seconds: %g\n", ((REAL)(tv[6] - tv[5])) / cps); } } - if ((b->plc && b->nobisect) || b->coarsen) { + if (b->plc || (b->refine && b->quality && (in->refine_elem_list == NULL))) { + if (!b->quiet) { + printf("Recovering Delaunayness...\n"); + } m.recoverdelaunay(); } tv[7] = clock(); - if (!b->quiet) { - if ((b->plc && b->nobisect) || b->coarsen) { + if (b->plc || (b->refine && b->quality && (in->refine_elem_list == NULL))) { + if (!b->quiet) { printf("Delaunay recovery seconds: %g\n", ((REAL)(tv[7] - tv[6]))/cps); } } @@ -31021,8 +36322,7 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, } } } - - if (b->quality) { + if (b->quality) { // -q m.delaunayrefinement(); } @@ -31034,15 +36334,31 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, } } - if ((b->plc || b->refine) && (b->optlevel > 0)) { - m.optimizemesh(); + if ((b->plc || b->quality) && + (b->smooth_maxiter > 0) && + ((m.st_volref_count > 0) || (m.st_facref_count > 0))) { + m.smooth_vertices(); // m.optimizemesh(ts[0]); } tv[10] = clock(); if (!b->quiet) { - if ((b->plc || b->refine) && (b->optlevel > 0)) { - printf("Optimization seconds: %g\n", ((REAL)(tv[10] - tv[9])) / cps); + if ((b->plc || b->quality) && + (b->smooth_maxiter > 0) && + ((m.st_volref_count > 0) || (m.st_facref_count > 0))) { + printf("Mesh smoothing seconds: %g\n", ((REAL)(tv[10] - tv[9])) / cps); + } + } + + if (b->plc || b->quality) { + m.improve_mesh(); + } + + tv[11] = clock(); + + if (!b->quiet) { + if (b->plc || b->quality) { + printf("Mesh improvement seconds: %g\n", ((REAL)(tv[11] - tv[10])) / cps); } } @@ -31051,6 +36367,7 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, m.jettisonnodes(); } + if ((b->order == 2) && !b->convex) { m.highorder(); } @@ -31076,6 +36393,7 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, if (!b->quiet) { printf("NOT writing an .ele file.\n"); } + m.indexelements(); } else { if (m.tetrahedrons->items > 0l) { m.outelements(out); @@ -31136,33 +36454,37 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, if (!out && b->vtkview) { - m.outmesh2vtk(b->outfilename); + m.outmesh2vtk(NULL, 0); // b->outfilename + } + + if (!out && b->vtksurfview) { + m.out_surfmesh_vtk(NULL, 0); } if (b->neighout) { m.outneighbors(out); } - if ((!(b->plc || b->refine)) && b->voroout) { + if (b->voroout) { m.outvoronoi(out); } - tv[11] = clock(); + tv[12] = clock(); if (!b->quiet) { - printf("\nOutput seconds: %g\n", ((REAL)(tv[11] - tv[10])) / cps); - printf("Total running seconds: %g\n", ((REAL)(tv[11] - tv[0])) / cps); + printf("\nOutput seconds: %g\n", ((REAL)(tv[12] - tv[11])) / cps); + printf("Total running seconds: %g\n", ((REAL)(tv[12] - tv[0])) / cps); } if (b->docheck) { - m.checkmesh(0); + m.check_mesh(0); if (b->plc || b->refine) { - m.checkshells(); - m.checksegments(); + m.check_shells(); + m.check_segments(); } if (b->docheck > 1) { - m.checkdelaunay(); + m.check_delaunay(); } } @@ -31173,23 +36495,24 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, #ifndef TETLIBRARY -/////////////////////////////////////////////////////////////////////////////// -// // -// main() The command line interface of TetGen. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// main() The command line interface of TetGen. // +// // +//============================================================================// -int main(int argc, const char *argv[]) +//int tetgen_main(int argc, char *argv[]) +int main(int argc, char *argv[]) #else // with TETLIBRARY -/////////////////////////////////////////////////////////////////////////////// -// // -// tetrahedralize() The library interface of TetGen. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// tetrahedralize() The library interface of TetGen. // +// // +//============================================================================// -void tetrahedralize(const char *switches, tetgenio *in, tetgenio *out, +void tetrahedralize(char *switches, tetgenio *in, tetgenio *out, tetgenio *addin, tetgenio *bgmin) #endif // not TETLIBRARY @@ -31238,7 +36561,7 @@ void tetrahedralize(const char *switches, tetgenio *in, tetgenio *out, #endif // not TETLIBRARY } -//// //// -//// //// -//// main_cxx ///////////////////////////////////////////////////////////////// +// // +// // +//== main_cxx ================================================================// diff --git a/libraries/tetgen/tetgen.h b/libraries/tetgen/tetgen.h index 3cd1f244..ef67b5d0 100644 --- a/libraries/tetgen/tetgen.h +++ b/libraries/tetgen/tetgen.h @@ -1,17 +1,49 @@ -/////////////////////////////////////////////////////////////////////////////// -// // -// TetGen // -// // -// A Quality Tetrahedral Mesh Generator and A 3D Delaunay Triangulator // -// // -// Version 1.5 // -// November 4, 2013 // -// // -// TetGen is freely available through the website: http://www.tetgen.org. // -// It may be copied, modified, and redistributed for non-commercial use. // -// Please consult the file LICENSE for the detailed copyright notices. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// TetGen // +// // +// A Quality Tetrahedral Mesh Generator and A 3D Delaunay Triangulator // +// // +// Version 1.6.0 // +// August 31, 2020 // +// // +// Copyright (C) 2002--2020 // +// // +// Hang Si // +// Research Group: Numerical Mathematics and Scientific Computing // +// Weierstrass Institute for Applied Analysis and Stochastics (WIAS) // +// Mohrenstr. 39, 10117 Berlin, Germany // +// si@wias-berlin.de // +// // +// TetGen is a tetrahedral mesh generator. It creates 3d triangulations of // +// polyhedral domains. It generates meshes with well-shaped elements whose // +// sizes are adapted to the geometric features or user-provided sizing // +// functions. It has applications in various applications in scientific // +// computing, such as computer graphics (CG), computer-aided design (CAD), // +// geometry processing (parametrizations and computer animation), and // +// physical simulations (finite element analysis). // +// // +// TetGen computes (weighted) Delaunay triangulations for three-dimensional // +// (weighted) point sets, and constrained Delaunay triangulations for // +// three-dimensional polyhedral domains. In the latter case, input edges // +// and triangles can be completely preserved in the output meshes. TetGen // +// can refine or coarsen an existing mesh to result in good quality and // +// size-adapted mesh according to the geometric features and user-defined // +// mesh sizing functions. // +// // +// TetGen implements theoretically proven algorithms for computing the // +// Delaunay and constrained Delaunay tetrahedralizations. TetGen achieves // +// robustness and efficiency by using advanced techniques in computational // +// geometry. A technical paper describes the algorithms and methods // +// implemented in TetGen is available in ACM-TOMS, Hang Si ``TetGen, a // +// Delaunay-Based Quality Tetrahedral Mesh Generator", ACM Transactions on // +// Mathematical Software, February 2015, https://doi.org/10.1145/2629697. // +// // +// TetGen is freely available through the website: http://www.tetgen.org. // +// It may be copied, modified, and redistributed for non-commercial use. // +// Please consult the file LICENSE for the detailed copyright notices. // +// // +//============================================================================// #ifndef tetgenH @@ -22,94 +54,57 @@ // #define TETLIBRARY -// Uncomment the following line to disable assert macros. These macros were -// inserted in the code where I hoped to catch bugs. They may slow down the -// speed of TetGen. - -// #define NDEBUG -// TetGen default uses the double precision (64 bit) for a real number. -// Alternatively, one can use the single precision (32 bit) 'float' if the +// TetGen default uses the double-precision (64 bit) for a real number. +// Alternatively, one can use the single-precision (32 bit) 'float' if the // memory is limited. #define REAL double // #define REAL float -// Maximum number of characters in a file name (including the null). +// The maximum number of characters in a file name (including the null). #define FILENAMESIZE 1024 -// Maximum number of chars in a line read from a file (including the null). +// The maximum number of chars in a line read from a file (including the null). #define INPUTLINESIZE 2048 -// TetGen only uses the C standard library. +// C standard libraries to perform Input/output operations, general utililities, +// manipulate strings and arrays, compute common mathematical operations, +// get date and time information. #include #include #include #include #include -#include // The types 'intptr_t' and 'uintptr_t' are signed and unsigned integer types, // respectively. They are guaranteed to be the same width as a pointer. -// They are defined in by the C99 Standard. However, Microsoft -// Visual C++ 2003 -- 2008 (Visual C++ 7.1 - 9) doesn't ship with this header -// file. In such case, we can define them by ourself. -// Update (learned from Stack Overflow): Visual Studio 2010 and Visual C++ 2010 -// Express both have stdint.h - -// The following piece of code was provided by Steven Johnson (MIT). Define the -// symbol _MSC_VER if you are using Microsoft Visual C++. Moreover, define -// the _WIN64 symbol if you are running TetGen on Win64 systems. - -#ifdef _MSC_VER // Microsoft Visual C++ -# ifdef _WIN64 - typedef __int64 intptr_t; - typedef unsigned __int64 uintptr_t; -# else // not _WIN64 - typedef int intptr_t; - typedef unsigned int uintptr_t; -# endif -#else // not Visual C++ -# include -#endif - -/////////////////////////////////////////////////////////////////////////////// -// // -// tetgenio // -// // -// A structure for transferring data into and out of TetGen's mesh structure,// -// 'tetgenmesh' (declared below). // -// // -// The input of TetGen is either a 3D point set, or a 3D piecewise linear // -// complex (PLC), or a tetrahedral mesh. Depending on the input object and // -// the specified options, the output of TetGen is either a Delaunay (or wei- // -// ghted Delaunay) tetrahedralization, or a constrained (Delaunay) tetrahed- // -// ralization, or a quality tetrahedral mesh. // -// // -// A piecewise linear complex (PLC) represents a 3D polyhedral domain with // -// possibly internal boundaries(subdomains). It is introduced in [Miller et // -// al, 1996]. Basically it is a set of "cells", i.e., vertices, edges, poly- // -// gons, and polyhedra, and the intersection of any two of its cells is the // -// union of other cells of it. // -// // -// TetGen uses a set of files to describe the inputs and outputs. Each file // -// is identified from its file extension (.node, .ele, .face, .edge, etc). // -// // -// The 'tetgenio' structure is a collection of arrays of data, i.e., points, // -// facets, tetrahedra, and so forth. It contains functions to read and write // -// (input and output) files of TetGen as well as other supported mesh files. // -// // -// Once an object of tetgenio is declared, no array is created. One has to // -// allocate enough memory for them. On deletion of this object, the memory // -// occupied by these arrays needs to be freed. The routine deinitialize() // -// will be automatically called. It frees the memory for an array if it is // -// not a NULL. Note that it assumes that the memory is allocated by the C++ // -// "new" operator. Otherwise, the user is responsible to free them and all // -// pointers must be NULL before the call of the destructor. // -// // -/////////////////////////////////////////////////////////////////////////////// +// They are defined in by the C99 Standard. + +#include + +//============================================================================// +// // +// tetgenio // +// // +// A structure for transferring input/output data between the user and // +// TetGen's internal data structure (class tetgenmesh). // +// // +// This data structure contains a collection of arrays, i.e., points, facets, // +// tetrahedra. It contains functions to read input data from files (.node, // +// .poly, .face, .edge, .ele) as well as write output data into files. // +// // +// Once an object of tetgenio is declared, no array is created. One has to // +// allocate enough memory for them. On the deletion of this object, the // +// memory occupied by these arrays needs to be freed. The routine // +// deinitialize() will be automatically called. It frees the memory for // +// an array if it is not a NULL. Note that it assumes that the memory is // +// allocated by the C++ "new" operator. Otherwise, the user is responsible // +// to free them and all pointers must be NULL. // +// // +//============================================================================// class tetgenio { @@ -118,14 +113,14 @@ class tetgenio { // A "polygon" describes a simple polygon (no holes). It is not necessarily // convex. Each polygon contains a number of corners (points) and the same // number of sides (edges). The points of the polygon must be given in - // either counterclockwise or clockwise order and they form a ring, so + // either counterclockwise or clockwise order and they form a ring, so // every two consecutive points forms an edge of the polygon. typedef struct { int *vertexlist; int numberofvertices; } polygon; - // A "facet" describes a polygonal region possibly with holes, edges, and + // A "facet" describes a polygonal region possibly with holes, edges, and // points floating in it. Each facet consists of a list of polygons and // a list of hole points (which lie strictly inside holes). typedef struct { @@ -141,7 +136,7 @@ class tetgenio { // "infinite vertex". 'v1' and 'v2' are two indices pointing to the // list of Voronoi vertices. 'v1' must be non-negative, while 'v2' may // be -1 if it is a ray, in this case, the unit normal of this ray is - // given in 'vnormal'. + // given in 'vnormal'. typedef struct { int v1, v2; REAL vnormal[3]; @@ -161,7 +156,7 @@ class tetgenio { // Additional parameters associated with an input (or mesh) vertex. - // These informations are provided by CAD libraries. + // These informations are provided by CAD libraries. typedef struct { REAL uv[2]; int tag; @@ -179,7 +174,7 @@ class tetgenio { typedef bool (* TetSizeFunc)(REAL*, REAL*, REAL*, REAL*, REAL*, REAL); // Items are numbered starting from 'firstnumber' (0 or 1), default is 0. - int firstnumber; + int firstnumber; // Dimension of the mesh (2 or 3), default is 3. int mesh_dim; @@ -190,36 +185,41 @@ class tetgenio { // 'pointlist': An array of point coordinates. The first point's x // coordinate is at index [0] and its y coordinate at index [1], its // z coordinate is at index [2], followed by the coordinates of the - // remaining points. Each point occupies three REALs. + // remaining points. Each point occupies three REALs. // 'pointattributelist': An array of point attributes. Each point's // attributes occupy 'numberofpointattributes' REALs. // 'pointmtrlist': An array of metric tensors at points. Each point's // tensor occupies 'numberofpointmtr' REALs. // 'pointmarkerlist': An array of point markers; one integer per point. + // 'point2tetlist': An array of tetrahedra indices; one integer per point. REAL *pointlist; REAL *pointattributelist; REAL *pointmtrlist; int *pointmarkerlist; + int *point2tetlist; pointparam *pointparamlist; int numberofpoints; int numberofpointattributes; int numberofpointmtrs; - - // 'tetrahedronlist': An array of tetrahedron corners. The first - // tetrahedron's first corner is at index [0], followed by its other + + // 'tetrahedronlist': An array of tetrahedron corners. The first + // tetrahedron's first corner is at index [0], followed by its other // corners, followed by six nodes on the edges of the tetrahedron if the // second order option (-o2) is applied. Each tetrahedron occupies - // 'numberofcorners' ints. The second order nodes are ouput only. + // 'numberofcorners' ints. The second order nodes are ouput only. // 'tetrahedronattributelist': An array of tetrahedron attributes. Each // tetrahedron's attributes occupy 'numberoftetrahedronattributes' REALs. // 'tetrahedronvolumelist': An array of constraints, i.e. tetrahedron's // volume; one REAL per element. Input only. - // 'neighborlist': An array of tetrahedron neighbors; 4 ints per element. - // Output only. + // 'neighborlist': An array of tetrahedron neighbors; 4 ints per element. + // 'tet2facelist': An array of tetrahedron face indices; 4 ints per element. + // 'tet2edgelist': An array of tetrahedron edge indices; 6 ints per element. int *tetrahedronlist; REAL *tetrahedronattributelist; REAL *tetrahedronvolumelist; int *neighborlist; + int *tet2facelist; + int *tet2edgelist; int numberoftetrahedra; int numberofcorners; int numberoftetrahedronattributes; @@ -233,14 +233,14 @@ class tetgenio { // 'holelist': An array of holes (in volume). Each hole is given by a // seed (point) which lies strictly inside it. The first seed's x, y and z // coordinates are at indices [0], [1] and [2], followed by the - // remaining seeds. Three REALs per hole. + // remaining seeds. Three REALs per hole. REAL *holelist; int numberofholes; // 'regionlist': An array of regions (subdomains). Each region is given by // a seed (point) which lies strictly inside it. The first seed's x, y and // z coordinates are at indices [0], [1] and [2], followed by the regional - // attribute at index [3], followed by the maximum volume at index [4]. + // attribute at index [3], followed by the maximum volume at index [4]. // Five REALs per region. // Note that each regional attribute is used only if you select the 'A' // switch, and each volume constraint is used only if you select the @@ -248,6 +248,15 @@ class tetgenio { REAL *regionlist; int numberofregions; + // 'refine_elem_list': An array of tetrahedra to be refined. The first + // tetrahedron's first corner is at index [0], followed by its other + // corners. Four integers per element. + // 'refine_elem_vol_list': An array of constraints, i.e. tetrahedron's + // volume; one REAL per element. + int *refine_elem_list; + REAL *refine_elem_vol_list; + int numberofrefineelems; + // 'facetconstraintlist': An array of facet constraints. Each constraint // specifies a maximum area bound on the subfaces of that facet. The // first facet constraint is given by a facet marker at index [0] and its @@ -257,11 +266,11 @@ class tetgenio { REAL *facetconstraintlist; int numberoffacetconstraints; - // 'segmentconstraintlist': An array of segment constraints. Each constraint + // 'segmentconstraintlist': An array of segment constraints. Each constraint // specifies a maximum length bound on the subsegments of that segment. // The first constraint is given by the two endpoints of the segment at // index [0] and [1], and the maximum length bound at index [2], followed - // by the remaining segment constraints. Three REALs per constraint. + // by the remaining segment constraints. Three REALs per constraint. // Note the segment endpoints are actually integers. REAL *segmentconstraintlist; int numberofsegmentconstraints; @@ -275,14 +284,13 @@ class tetgenio { // It is output only if the second order option (-o2) is applied. The // first face's three second order nodes are at [0], [1], and [2], // followed by the remaining faces. Three ints per face. - // 'adjtetlist': An array of adjacent tetrahedra to the faces. The first - // face's two adjacent tetrahedra are at indices [0] and [1], followed by - // the remaining faces. A '-1' indicates outside (no adj. tet). This list - // is output when '-nn' switch is used. Output only. + // 'face2tetlist': An array of tetrahedra indices; 2 ints per face. + // 'face2edgelist': An array of edge indices; 3 ints per face. int *trifacelist; int *trifacemarkerlist; int *o2facelist; - int *adjtetlist; + int *face2tetlist; + int *face2edgelist; int numberoftrifaces; // 'edgelist': An array of edge endpoints. The first edge's endpoints @@ -291,12 +299,11 @@ class tetgenio { // 'edgemarkerlist': An array of edge markers; one int per edge. // 'o2edgelist': An array of midpoints of edges. It is output only if the // second order option (-o2) is applied. One int per edge. - // 'edgeadjtetlist': An array of adjacent tetrahedra to the edges. One - // tetrahedron (an integer) per edge. + // 'edge2tetlist': An array of tetrahedra indices. One int per edge. int *edgelist; int *edgemarkerlist; int *o2edgelist; - int *edgeadjtetlist; + int *edge2tetlist; int numberofedges; // 'vpointlist': An array of Voronoi vertex coordinates (like pointlist). @@ -314,6 +321,7 @@ class tetgenio { int numberofvfacets; int numberofvcells; + // Variable (and callback functions) for meshing PSCs. void *geomhandle; GetVertexParamOnEdge getvertexparamonedge; @@ -327,29 +335,30 @@ class tetgenio { // Input & output routines. bool load_node_call(FILE* infile, int markers, int uvflag, char*); - bool load_node(const char*); - bool load_edge(const char*); - bool load_face(const char*); - bool load_tet(const char*); - bool load_vol(const char*); - bool load_var(const char*); - bool load_mtr(const char*); - bool load_pbc(const char*); - bool load_poly(const char*); - bool load_off(const char*); - bool load_ply(const char*); - bool load_stl(const char*); - bool load_vtk(const char*); - bool load_medit(const char*, int); - bool load_plc(const char*, int); - bool load_tetmesh(const char*, int); + bool load_node(char*); + bool load_edge(char*); + bool load_face(char*); + bool load_tet(char*); + bool load_vol(char*); + bool load_var(char*); + bool load_mtr(char*); + bool load_elem(char*); + bool load_poly(char*); + bool load_off(char*); + bool load_ply(char*); + bool load_stl(char*); + bool load_vtk(char*); + bool load_medit(char*, int); + bool load_neumesh(char*, int); + bool load_plc(char*, int); + bool load_tetmesh(char*, int); void save_nodes(const char*); void save_elements(const char*); void save_faces(const char*); - void save_edges(const char*); - void save_neighbors(const char*); + void save_edges(char*); + void save_neighbors(char*); void save_poly(const char*); - void save_faces2smesh(const char*); + void save_faces2smesh(char*); // Read line and parse string functions. char *readline(char* string, FILE* infile, int *linenumber); @@ -380,6 +389,7 @@ class tetgenio { pointattributelist = (REAL *) NULL; pointmtrlist = (REAL *) NULL; pointmarkerlist = (int *) NULL; + point2tetlist = (int *) NULL; pointparamlist = (pointparam *) NULL; numberofpoints = 0; numberofpointattributes = 0; @@ -389,25 +399,28 @@ class tetgenio { tetrahedronattributelist = (REAL *) NULL; tetrahedronvolumelist = (REAL *) NULL; neighborlist = (int *) NULL; + tet2facelist = (int *) NULL; + tet2edgelist = (int *) NULL; numberoftetrahedra = 0; - numberofcorners = 4; + numberofcorners = 4; numberoftetrahedronattributes = 0; trifacelist = (int *) NULL; trifacemarkerlist = (int *) NULL; o2facelist = (int *) NULL; - adjtetlist = (int *) NULL; - numberoftrifaces = 0; + face2tetlist = (int *) NULL; + face2edgelist = (int *) NULL; + numberoftrifaces = 0; edgelist = (int *) NULL; edgemarkerlist = (int *) NULL; o2edgelist = (int *) NULL; - edgeadjtetlist = (int *) NULL; + edge2tetlist = (int *) NULL; numberofedges = 0; facetlist = (facet *) NULL; facetmarkerlist = (int *) NULL; - numberoffacets = 0; + numberoffacets = 0; holelist = (REAL *) NULL; numberofholes = 0; @@ -415,6 +428,10 @@ class tetgenio { regionlist = (REAL *) NULL; numberofregions = 0; + refine_elem_list = (int *) NULL; + refine_elem_vol_list = (REAL *) NULL; + numberofrefineelems = 0; + facetconstraintlist = (REAL *) NULL; numberoffacetconstraints = 0; segmentconstraintlist = (REAL *) NULL; @@ -423,13 +440,14 @@ class tetgenio { vpointlist = (REAL *) NULL; vedgelist = (voroedge *) NULL; - vfacetlist = (vorofacet *) NULL; - vcelllist = (int **) NULL; + vfacetlist = (vorofacet *) NULL; + vcelllist = (int **) NULL; numberofvpoints = 0; numberofvedges = 0; numberofvfacets = 0; numberofvcells = 0; + tetunsuitable = NULL; geomhandle = NULL; @@ -440,9 +458,9 @@ class tetgenio { getsteineronface = NULL; } - // Free the memory allocated in 'tetgenio'. Note that it assumes that the + // Free the memory allocated in 'tetgenio'. Note that it assumes that the // memory was allocated by the "new" operator (C++). - void deinitialize() + void clean_memory() { int i, j; @@ -457,6 +475,9 @@ class tetgenio { } if (pointmarkerlist != (int *) NULL) { delete [] pointmarkerlist; + } + if (point2tetlist != (int *) NULL) { + delete [] point2tetlist; } if (pointparamlist != (pointparam *) NULL) { delete [] pointparamlist; @@ -474,6 +495,12 @@ class tetgenio { if (neighborlist != (int *) NULL) { delete [] neighborlist; } + if (tet2facelist != (int *) NULL) { + delete [] tet2facelist; + } + if (tet2edgelist != (int *) NULL) { + delete [] tet2edgelist; + } if (trifacelist != (int *) NULL) { delete [] trifacelist; @@ -484,8 +511,11 @@ class tetgenio { if (o2facelist != (int *) NULL) { delete [] o2facelist; } - if (adjtetlist != (int *) NULL) { - delete [] adjtetlist; + if (face2tetlist != (int *) NULL) { + delete [] face2tetlist; + } + if (face2edgelist != (int *) NULL) { + delete [] face2edgelist; } if (edgelist != (int *) NULL) { @@ -497,8 +527,8 @@ class tetgenio { if (o2edgelist != (int *) NULL) { delete [] o2edgelist; } - if (edgeadjtetlist != (int *) NULL) { - delete [] edgeadjtetlist; + if (edge2tetlist != (int *) NULL) { + delete [] edge2tetlist; } if (facetlist != (facet *) NULL) { @@ -527,6 +557,12 @@ class tetgenio { if (regionlist != (REAL *) NULL) { delete [] regionlist; } + if (refine_elem_list != (int *) NULL) { + delete [] refine_elem_list; + if (refine_elem_vol_list != (REAL *) NULL) { + delete [] refine_elem_vol_list; + } + } if (facetconstraintlist != (REAL *) NULL) { delete [] facetconstraintlist; } @@ -555,47 +591,47 @@ class tetgenio { // Constructor & destructor. tetgenio() {initialize();} - ~tetgenio() {deinitialize();} + ~tetgenio() {clean_memory();} }; // class tetgenio -/////////////////////////////////////////////////////////////////////////////// -// // -// tetgenbehavior // -// // -// A structure for maintaining the switches and parameters used by TetGen's // -// mesh data structure and algorithms. // -// // -// All switches and parameters are initialized with default values. They can // -// be set by the command line arguments (a list of strings) of TetGen. // -// // -// NOTE: Some of the switches are incompatible. While some may depend on // -// other switches. The routine parse_commandline() sets the switches from // -// the command line (a list of strings) and checks the consistency of the // -// applied switches. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// tetgenbehavior // +// // +// A structure for maintaining the switches and parameters used by TetGen's // +// internal data structure and algorithms. // +// // +// All switches and parameters are initialized with default values. They are // +// set by the command line arguments (argc, argv). // +// // +// NOTE: Some switches are incompatible with others. While some may depend // +// on other switches. The routine parse_commandline() sets the switches from // +// the command line (a list of strings) and checks the consistency of the // +// applied switches. // +// // +//============================================================================// class tetgenbehavior { public: - // Switches of TetGen. + // Switches of TetGen. int plc; // '-p', 0. int psc; // '-s', 0. int refine; // '-r', 0. int quality; // '-q', 0. int nobisect; // '-Y', 0. + int cdt; // '-D', 0. + int cdtrefine; // '-D#', 7. int coarsen; // '-R', 0. int weighted; // '-w', 0. int brio_hilbert; // '-b', 1. - int incrflip; // '-l', 0. int flipinsert; // '-L', 0. int metric; // '-m', 0. int varvolume; // '-a', 0. int fixedvolume; // '-a', 0. int regionattrib; // '-A', 0. - int conforming; // '-D', 0. int insertaddpoints; // '-i', 0. int diagnose; // '-d', 0. int convex; // '-c', 0. @@ -610,49 +646,60 @@ class tetgenbehavior { int voroout; // '-v', 0. int meditview; // '-g', 0. int vtkview; // '-k', 0. + int vtksurfview; // '-k', 0. int nobound; // '-B', 0. int nonodewritten; // '-N', 0. int noelewritten; // '-E', 0. int nofacewritten; // '-F', 0. int noiterationnum; // '-I', 0. int nojettison; // '-J', 0. - int reversetetori; // '-R', 0. int docheck; // '-C', 0. int quiet; // '-Q', 0. + int nowarning; // '-W', 0. int verbose; // '-V', 0. // Parameters of TetGen. int vertexperblock; // '-x', 4092. int tetrahedraperblock; // '-x', 8188. int shellfaceperblock; // '-x', 2044. - int nobisect_param; // '-Y', 2. - int addsteiner_algo; // '-Y/', 1. + int supsteiner_level; // '-Y/', 2. + int addsteiner_algo; // '-Y//', 1. int coarsen_param; // '-R', 0. int weighted_param; // '-w', 0. int fliplinklevel; // -1. int flipstarsize; // -1. int fliplinklevelinc; // 1. - int reflevel; // '-D', 3. - int optlevel; // '-O', 2. - int optscheme; // '-O', 7. + int opt_max_flip_level; // '-O', 3. + int opt_scheme; // '-O/#', 7. + int opt_iterations; // -O//#, 3. + int smooth_cirterion; // -s, 1. + int smooth_maxiter; // -s, 7. int delmaxfliplevel; // 1. int order; // '-o', 1. + int reversetetori; // '-o/', 0. int steinerleft; // '-S', 0. + int unflip_queue_limit; // '-U#', 1000. int no_sort; // 0. int hilbert_order; // '-b///', 52. int hilbert_limit; // '-b//' 8. int brio_threshold; // '-b' 64. REAL brio_ratio; // '-b/' 0.125. - REAL facet_ang_tol; // '-p', 179.9. + REAL epsilon; // '-T', 1.0e-8. + REAL facet_separate_ang_tol; // '-p', 179.9. + REAL collinear_ang_tol; // '-p/', 179.9. + REAL facet_small_ang_tol; // '-p//', 15.0. REAL maxvolume; // '-a', -1.0. + REAL maxvolume_length; // '-a', -1.0. REAL minratio; // '-q', 0.0. + REAL opt_max_asp_ratio; // 1000.0. + REAL opt_max_edge_ratio; // 100.0. REAL mindihedral; // '-q', 5.0. - REAL optmaxdihedral; // 165.0. - REAL optminsmtdihed; // 179.0. - REAL optminslidihed; // 179.0. - REAL epsilon; // '-T', 1.0e-8. - REAL minedgelength; // 0.0. + REAL optmaxdihedral; // -o/# 177.0. + REAL metric_scale; // -m#, 1.0. + REAL smooth_alpha; // '-s', 0.3. REAL coarsen_percent; // -R1/#, 1.0. + REAL elem_growth_ratio; // Growth ratio of # elements, -r#, 0.0. + REAL refine_progress_ratio; // -r/#, 0.333. // Strings of command line arguments and input/output file names. char commandline[1024]; @@ -661,27 +708,31 @@ class tetgenbehavior { char addinfilename[1024]; char bgmeshfilename[1024]; - // The input object of TetGen. They are recognized by either the input - // file extensions or by the specified options. + // Read an additional tetrahedral mesh and treat it as holes [2018-07-30]. + int hole_mesh; // '-H', 0. + char hole_mesh_filename[1024]; + + // The input object of TetGen. They are recognized by either the input + // file extensions or by the specified options. // Currently the following objects are supported: - // - NODES, a list of nodes (.node); - // - POLY, a piecewise linear complex (.poly or .smesh); - // - OFF, a polyhedron (.off, Geomview's file format); + // - NODES, a list of nodes (.node); + // - POLY, a piecewise linear complex (.poly or .smesh); + // - OFF, a polyhedron (.off, Geomview's file format); // - PLY, a polyhedron (.ply, file format from gatech, only ASCII); // - STL, a surface mesh (.stl, stereolithography format); - // - MEDIT, a surface mesh (.mesh, Medit's file format); + // - MEDIT, a surface mesh (.mesh, Medit's file format); // - MESH, a tetrahedral mesh (.ele). // If no extension is available, the imposed command line switch - // (-p or -r) implies the object. - enum objecttype {NODES, POLY, OFF, PLY, STL, MEDIT, VTK, MESH} object; + // (-p or -r) implies the object. + enum objecttype {NODES, POLY, OFF, PLY, STL, MEDIT, VTK, MESH, NEU_MESH} object; void syntax(); void usage(); // Command line parse routine. - bool parse_commandline(int argc, const char **argv); - bool parse_commandline(const char *switches) { + bool parse_commandline(int argc, char **argv); + bool parse_commandline(char *switches) { return parse_commandline(0, &switches); } @@ -693,11 +744,12 @@ class tetgenbehavior { refine = 0; quality = 0; nobisect = 0; + cdt = 0; // set by -D (without a number following it) + cdtrefine = 7; // default, set by -D# coarsen = 0; metric = 0; weighted = 0; brio_hilbert = 1; - incrflip = 0; flipinsert = 0; varvolume = 0; fixedvolume = 0; @@ -705,7 +757,6 @@ class tetgenbehavior { nostaticfilter = 0; insertaddpoints = 0; regionattrib = 0; - conforming = 0; diagnose = 0; convex = 0; zeroindex = 0; @@ -715,6 +766,7 @@ class tetgenbehavior { voroout = 0; meditview = 0; vtkview = 0; + vtksurfview = 0; nobound = 0; nonodewritten = 0; noelewritten = 0; @@ -723,43 +775,54 @@ class tetgenbehavior { nomergefacet = 0; nomergevertex = 0; nojettison = 0; - reversetetori = 0; docheck = 0; quiet = 0; + nowarning = 0; verbose = 0; vertexperblock = 4092; tetrahedraperblock = 8188; shellfaceperblock = 4092; - nobisect_param = 2; + supsteiner_level = 2; addsteiner_algo = 1; coarsen_param = 0; weighted_param = 0; - fliplinklevel = -1; // No limit on linklevel. - flipstarsize = -1; // No limit on flip star size. + fliplinklevel = -1; + flipstarsize = -1; fliplinklevelinc = 1; - reflevel = 3; - optscheme = 7; // 1 & 2 & 4, // min_max_dihedral. - optlevel = 2; + opt_scheme = 7; + opt_max_flip_level = 3; + opt_iterations = 3; delmaxfliplevel = 1; order = 1; + reversetetori = 0; steinerleft = -1; + unflip_queue_limit = 1000; no_sort = 0; hilbert_order = 52; //-1; hilbert_limit = 8; brio_threshold = 64; brio_ratio = 0.125; - facet_ang_tol = 179.9; + facet_separate_ang_tol = 179.9; + collinear_ang_tol = 179.9; + facet_small_ang_tol = 15.0; maxvolume = -1.0; + maxvolume_length = -1.0; minratio = 2.0; - mindihedral = 0.0; // 5.0; - optmaxdihedral = 165.00; // without -q, default is 179.0 - optminsmtdihed = 179.00; // without -q, default is 179.999 - optminslidihed = 179.00; // without -q, default is 179.999 + opt_max_asp_ratio = 1000.; + opt_max_edge_ratio = 100.; + mindihedral = 3.5; + optmaxdihedral = 177.00; epsilon = 1.0e-8; - minedgelength = 0.0; coarsen_percent = 1.0; + metric_scale = 1.0; // -m# + elem_growth_ratio = 0.0; // -r# + refine_progress_ratio = 0.333; // -r/# object = NODES; + + smooth_cirterion = 3; // -s# default smooth surface and volume vertices. + smooth_maxiter = 7; // set by -s#/7 + smooth_alpha = 0.3; // relax parameter, set by -s#/#/0.3 commandline[0] = '\0'; infilename[0] = '\0'; @@ -767,85 +830,91 @@ class tetgenbehavior { addinfilename[0] = '\0'; bgmeshfilename[0] = '\0'; + hole_mesh = 0; + hole_mesh_filename[0] = '\0'; + } }; // class tetgenbehavior -/////////////////////////////////////////////////////////////////////////////// -// // -// Robust Geometric predicates // -// // -// Geometric predicates are simple tests of spatial relations of a set of d- // -// dimensional points, such as the orientation test and the point-in-sphere // -// test. Each of these tests is performed by evaluating the sign of a deter- // -// minant of a matrix whose entries are the coordinates of these points. If // -// the computation is performed by using the floating-point numbers, e.g., // -// the single or double precision numbers in C/C++, roundoff error may cause // -// an incorrect result. This may either lead to a wrong result or eventually // -// lead to a failure of the program. Computing the predicates exactly will // -// avoid the error and make the program robust. // -// // -// The following routines are the robust geometric predicates for 3D orient- // -// ation test and point-in-sphere test. They were implemented by Shewchuk. // -// The source code are generously provided by him in the public domain, // -// http://www.cs.cmu.edu/~quake/robust.html. predicates.cxx is a C++ version // -// of the original C code. // -// // -// The original predicates of Shewchuk only use "dynamic filters", i.e., it // -// computes the error at run time step by step. TetGen first adds a "static // -// filter" in each predicate. It estimates the maximal possible error in all // -// cases. So it can safely and quickly answer many easy cases. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// Robust Geometric predicates // +// // +// The following routines are the robust geometric predicates for orientation // +// test and point-in-sphere test implemented by Jonathan Shewchuk. // +// He generously provided the source code in the public domain, // +// http://www.cs.cmu.edu/~quake/robust.html. // +// predicates.cxx is a C++ version of the original C code. // +// // +// The original predicates of Shewchuk only use "dynamic filters", i.e., it // +// computes the error at runtime step by step. TetGen first uses a "static // +// filter" in each predicate. It estimates the maximal possible error in all // +// cases. It safely and quickly "filters" many easy cases. // +// // +//============================================================================// void exactinit(int, int, int, REAL, REAL, REAL); + REAL orient3d(REAL *pa, REAL *pb, REAL *pc, REAL *pd); REAL insphere(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe); REAL orient4d(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe, REAL ah, REAL bh, REAL ch, REAL dh, REAL eh); -/////////////////////////////////////////////////////////////////////////////// -// // -// tetgenmesh // -// // -// A structure for creating and updating tetrahedral meshes. // -// // -/////////////////////////////////////////////////////////////////////////////// +REAL orient2dexact(REAL *pa, REAL *pb, REAL *pc); +REAL orient3dexact(REAL *pa, REAL *pb, REAL *pc, REAL *pd); +REAL orient4dexact(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe, + REAL ah, REAL bh, REAL ch, REAL dh, REAL eh); + + +//============================================================================// +// // +// tetgenmesh TetGen's internal mesh data structure. // +// // +// It uses a tetrahedron-based mesh data structure. It implements elementary // +// flip operations to locally modify the mesh. It implements basic meshing // +// algorithms to create Delaunay tetrahedraliations, to perform boundary // +// recovery, to place Steiner points in the mesh domain, and to optimize the // +// quality of the mesh. // +// // +//============================================================================// class tetgenmesh { public: -/////////////////////////////////////////////////////////////////////////////// -// // -// Mesh data structure // -// // -// A tetrahedral mesh T of a 3D piecewise linear complex (PLC) X is a 3D // -// simplicial complex whose underlying space is equal to the space of X. T // -// contains a 2D subcomplex S which is a triangular mesh of the boundary of // -// X. S contains a 1D subcomplex L which is a linear mesh of the boundary of // -// S. Faces and edges in S and L are respectively called subfaces and segme- // -// nts to distinguish them from others in T. // -// // -// TetGen stores the tetrahedra and vertices of T. The basic structure of a // -// tetrahedron contains pointers to its vertices and adjacent tetrahedra. A // -// vertex stores its x-, y-, and z-coordinates, and a pointer to a tetrahed- // -// ron containing it. Both tetrahedra and vertices may contain user data. // -// // -// Each face of T belongs to either two tetrahedra or one tetrahedron. In // -// the latter case, the face is an exterior boundary face of T. TetGen adds // -// fictitious tetrahedra (one-to-one) at such faces, and connects them to an // -// "infinite vertex" (which has no geometric coordinates). One can imagine // -// such a vertex lies in 4D space and is visible by all exterior boundary // -// faces. The extended set of tetrahedra (including the infinite vertex) is // -// a tetrahedralization of a 3-pseudomanifold without boundary. It has the // -// property that every face is shared by exactly two tetrahedra. // -// // -// The current version of TetGen stores explicitly the subfaces and segments // -// (which are in surface mesh S and the linear mesh L), respectively. Extra // -// pointers are allocated in tetrahedra and subfaces to point each others. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// Mesh data structure // +// // +// A tetrahedral mesh T of a 3D piecewise linear complex (PLC) X is a 3D // +// simplicial complex whose underlying space is equal to the space of X. T // +// contains a 2D subcomplex S which is a triangular mesh of the boundary of // +// X. S contains a 1D subcomplex L which is a linear mesh of the boundary of // +// S. Faces and edges in S and L are respectively called subfaces and segme- // +// nts to distinguish them from others in T. // +// // +// TetGen uses a tetrahedron-based data structure. It stores tetrahedra and // +// vertices. This data structure is pointer-based. Each tetrahedron contains // +// pointers to its vertices and adjacent tetrahedra. Each vertex holds its x-,// +// y-, z-coordinates, and a pointer to one of the tetrahedra having it. Both // +// tetrahedra and vertices may contain user data. // +// // +// Let T be a tetrahedralization. Each triangular face of T belongs to either // +// two or one tetrahedron. In the latter case, it is an exterior boundary // +// face of T. TetGen attaches tetrahedra (one-to-one) to such faces. All such // +// tetrahedra contain an "infinite vertex" (which has no geometric coordinates// +// ). One can imagine such a vertex lies in 4D space and is visible by all // +// exterior boundary faces simultaneously. This extended set of tetrahedra // +// (including the infinite vertex) becomes a tetrahedralization of a 3-sphere // +// that has no boundary in 3d. It has the nice property that every triangular // +// face is shared by exactly two tetrahedra. // +// // +// The current version of TetGen stores explicitly the subfaces and segments // +// (which are in surface mesh S and the linear mesh L), respectively. Extra // +// pointers are allocated in tetrahedra and subfaces to point each other. // +// // +//============================================================================// // The tetrahedron data structure. It includes the following fields: // - a list of four adjoining tetrahedra; @@ -883,59 +952,59 @@ class tetgenmesh { // - an integer for boundary marker (point index); // - an integer for point type (and flags). // - an integer for geometry tag (optional, for -s switch). - // The structure of a point is an array of REALs. Its acutal size is + // The structure of a point is an array of REALs. Its acutal size is // determined at the runtime. typedef REAL *point; -/////////////////////////////////////////////////////////////////////////////// -// // -// Handles // -// // -// Navigation and manipulation in a tetrahedralization are accomplished by // -// operating on structures referred as ``handles". A handle is a pair (t,v), // -// where t is a pointer to a tetrahedron, and v is a 4-bit integer, in the // -// range from 0 to 11. v is called the ``version'' of a tetrahedron, it rep- // -// resents a directed edge of a specific face of the tetrahedron. // -// // -// There are 12 even permutations of the four vertices, each of them corres- // -// ponds to a directed edge (a version) of the tetrahedron. The 12 versions // -// can be grouped into 4 distinct ``edge rings'' in 4 ``oriented faces'' of // -// this tetrahedron. One can encode each version (a directed edge) into a // -// 4-bit integer such that the two upper bits encode the index (from 0 to 2) // -// of this edge in the edge ring, and the two lower bits encode the index ( // -// from 0 to 3) of the oriented face which contains this edge. // -// // -// The four vertices of a tetrahedron are indexed from 0 to 3 (according to // -// their storage in the data structure). Give each face the same index as // -// the node opposite it in the tetrahedron. Denote the edge connecting face // -// i to face j as i/j. We number the twelve versions as follows: // -// // -// | edge 0 edge 1 edge 2 // -// --------|-------------------------------- // -// face 0 | 0 (0/1) 4 (0/3) 8 (0/2) // -// face 1 | 1 (1/2) 5 (1/3) 9 (1/0) // -// face 2 | 2 (2/3) 6 (2/1) 10 (2/0) // -// face 3 | 3 (3/0) 7 (3/1) 11 (3/2) // -// // -// Similarly, navigation and manipulation in a (boundary) triangulation are // -// done by using handles of triangles. Each handle is a pair (s, v), where s // -// is a pointer to a triangle, and v is a version in the range from 0 to 5. // -// Each version corresponds to a directed edge of this triangle. // -// // -// Number the three vertices of a triangle from 0 to 2 (according to their // -// storage in the data structure). Give each edge the same index as the node // -// opposite it in the triangle. The six versions of a triangle are: // -// // -// | edge 0 edge 1 edge 2 // -// ---------------|-------------------------- // -// ccw orieation | 0 2 4 // -// cw orieation | 1 3 5 // -// // -// In the following, a 'triface' is a handle of tetrahedron, and a 'face' is // -// a handle of a triangle. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// Handles // +// // +// Navigation and manipulation in a tetrahedralization are accomplished by // +// operating on structures referred as ``handles". A handle is a pair (t,v), // +// where t is a pointer to a tetrahedron, and v is a 4-bit integer, in the // +// range from 0 to 11. v is called the ``version'' of a tetrahedron, it rep- // +// resents a directed edge of a specific face of the tetrahedron. // +// // +// There are 12 even permutations of the four vertices, each of them corres- // +// ponds to a directed edge (a version) of the tetrahedron. The 12 versions // +// can be grouped into 4 distinct ``edge rings'' in 4 ``oriented faces'' of // +// this tetrahedron. One can encode each version (a directed edge) into a // +// 4-bit integer such that the two upper bits encode the index (from 0 to 2) // +// of this edge in the edge ring, and the two lower bits encode the index ( // +// from 0 to 3) of the oriented face which contains this edge. // +// // +// The four vertices of a tetrahedron are indexed from 0 to 3 (according to // +// their storage in the data structure). Give each face the same index as // +// the node opposite it in the tetrahedron. Denote the edge connecting face // +// i to face j as i/j. We number the twelve versions as follows: // +// // +// | edge 0 edge 1 edge 2 // +// --------|-------------------------------- // +// face 0 | 0 (0/1) 4 (0/3) 8 (0/2) // +// face 1 | 1 (1/2) 5 (1/3) 9 (1/0) // +// face 2 | 2 (2/3) 6 (2/1) 10 (2/0) // +// face 3 | 3 (3/0) 7 (3/1) 11 (3/2) // +// // +// Similarly, navigation and manipulation in a (boundary) triangulation are // +// done by using handles of triangles. Each handle is a pair (s, v), where s // +// is a pointer to a triangle, and v is a version in the range from 0 to 5. // +// Each version corresponds to a directed edge of this triangle. // +// // +// Number the three vertices of a triangle from 0 to 2 (according to their // +// storage in the data structure). Give each edge the same index as the node // +// opposite it in the triangle. The six versions of a triangle are: // +// // +// | edge 0 edge 1 edge 2 // +// ---------------|-------------------------- // +// ccw orieation | 0 2 4 // +// cw orieation | 1 3 5 // +// // +// In the following, a 'triface' is a handle of tetrahedron, and a 'face' is // +// a handle of a triangle. // +// // +//============================================================================// class triface { public: @@ -959,23 +1028,23 @@ class tetgenmesh { } }; -/////////////////////////////////////////////////////////////////////////////// -// // -// Arraypool // -// // -// A dynamic linear array. (It is written by J. Shewchuk) // -// // -// Each arraypool contains an array of pointers to a number of blocks. Each // -// block contains the same fixed number of objects. Each index of the array // -// addresses a particular object in the pool. The most significant bits add- // -// ress the index of the block containing the object. The less significant // -// bits address this object within the block. // -// // -// 'objectbytes' is the size of one object in blocks; 'log2objectsperblock' // -// is the base-2 logarithm of 'objectsperblock'; 'objects' counts the number // -// of allocated objects; 'totalmemory' is the total memory in bytes. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// Arraypool // +// // +// A dynamic linear array. (It is written by J. Shewchuk) // +// // +// Each arraypool contains an array of pointers to a number of blocks. Each // +// block contains the same fixed number of objects. Each index of the array // +// addresses a particular object in the pool. The most significant bits add- // +// ress the index of the block containing the object. The less significant // +// bits address this object within the block. // +// // +// 'objectbytes' is the size of one object in blocks; 'log2objectsperblock' // +// is the base-2 logarithm of 'objectsperblock'; 'objects' counts the number // +// of allocated objects; 'totalmemory' is the total memory in bytes. // +// // +//============================================================================// class arraypool { @@ -1008,26 +1077,26 @@ class tetgenmesh { (void *) ((pool)->toparray[(index) >> (pool)->log2objectsperblock] + \ ((index) & (pool)->objectsperblockmark) * (pool)->objectbytes) -/////////////////////////////////////////////////////////////////////////////// -// // -// Memorypool // -// // -// A structure for memory allocation. (It is written by J. Shewchuk) // -// // -// firstblock is the first block of items. nowblock is the block from which // -// items are currently being allocated. nextitem points to the next slab // -// of free memory for an item. deaditemstack is the head of a linked list // -// (stack) of deallocated items that can be recycled. unallocateditems is // -// the number of items that remain to be allocated from nowblock. // -// // -// Traversal is the process of walking through the entire list of items, and // -// is separate from allocation. Note that a traversal will visit items on // -// the "deaditemstack" stack as well as live items. pathblock points to // -// the block currently being traversed. pathitem points to the next item // -// to be traversed. pathitemsleft is the number of items that remain to // -// be traversed in pathblock. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// Memorypool // +// // +// A structure for memory allocation. (It is written by J. Shewchuk) // +// // +// firstblock is the first block of items. nowblock is the block from which // +// items are currently being allocated. nextitem points to the next slab // +// of free memory for an item. deaditemstack is the head of a linked list // +// (stack) of deallocated items that can be recycled. unallocateditems is // +// the number of items that remain to be allocated from nowblock. // +// // +// Traversal is the process of walking through the entire list of items, and // +// is separate from allocation. Note that a traversal will visit items on // +// the "deaditemstack" stack as well as live items. pathblock points to // +// the block currently being traversed. pathitem points to the next item // +// to be traversed. pathitemsleft is the number of items that remain to // +// be traversed in pathblock. // +// // +//============================================================================// class memorypool { @@ -1048,47 +1117,55 @@ class tetgenmesh { memorypool(); memorypool(int, int, int, int); ~memorypool(); - + void poolinit(int, int, int, int); void restart(); void *alloc(); void dealloc(void*); void traversalinit(); void *traverse(); - }; - -/////////////////////////////////////////////////////////////////////////////// -// // -// badface // -// // -// Despite of its name, a 'badface' can be used to represent one of the // -// following objects: // -// - a face of a tetrahedron which is (possibly) non-Delaunay; // -// - an encroached subsegment or subface; // -// - a bad-quality tetrahedron, i.e, has too large radius-edge ratio; // -// - a sliver, i.e., has good radius-edge ratio but nearly zero volume; // -// - a recently flipped face (saved for undoing the flip later). // -// // -/////////////////////////////////////////////////////////////////////////////// + }; + +//============================================================================// +// // +// badface // +// // +// Despite of its name, a 'badface' can be used to represent one of the // +// following objects: // +// - a face of a tetrahedron which is (possibly) non-Delaunay; // +// - an encroached subsegment or subface; // +// - a bad-quality tetrahedron, i.e, has too large radius-edge ratio; // +// - a sliver, i.e., has good radius-edge ratio but nearly zero volume; // +// - a recently flipped face (saved for undoing the flip later). // +// // +//============================================================================// class badface { public: - triface tt; + triface tt; face ss; REAL key, cent[6]; // circumcenter or cos(dihedral angles) at 6 edges. point forg, fdest, fapex, foppo, noppo; - badface *nextitem; + badface *nextitem; badface() : key(0), forg(0), fdest(0), fapex(0), foppo(0), noppo(0), nextitem(0) {} + void init() { + key = 0.; + for (int k = 0; k < 6; k++) cent[k] = 0.; + tt.tet = NULL; tt.ver = 0; + ss.sh = NULL; ss.shver = 0; + forg = fdest = fapex = foppo = noppo = NULL; + nextitem = NULL; + } }; -/////////////////////////////////////////////////////////////////////////////// -// // -// insertvertexflags // -// // -// A collection of flags that pass to the routine insertvertex(). // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// insertvertexflags // +// // +// A collection of flags that pass to the routine insertvertex(). // +// // +//============================================================================// class insertvertexflags { @@ -1100,8 +1177,11 @@ class tetgenmesh { int rejflag, chkencflag, cdtflag; int assignmeshsize; int sloc, sbowywat; - + // Used by Delaunay refinement. + int collect_inial_cavity_flag; + int ignore_near_vertex; + int check_insert_radius; int refineflag; // 0, 1, 2, 3 triface refinetet; face refinesh; @@ -1109,29 +1189,37 @@ class tetgenmesh { REAL smlen; // for useinsertradius. point parentpt; - insertvertexflags() { + void init() { iloc = bowywat = lawson = 0; splitbdflag = validflag = respectbdflag = 0; rejflag = chkencflag = cdtflag = 0; assignmeshsize = 0; sloc = sbowywat = 0; + collect_inial_cavity_flag = 0; + ignore_near_vertex = 0; + check_insert_radius = 0; refineflag = 0; refinetet.tet = NULL; refinesh.sh = NULL; smlenflag = 0; smlen = 0.0; + parentpt = NULL; + } + + insertvertexflags() { + init(); } }; -/////////////////////////////////////////////////////////////////////////////// -// // -// flipconstraints // -// // -// A structure of a collection of data (options and parameters) which pass // -// to the edge flip function flipnm(). // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// flipconstraints // +// // +// A structure of a collection of data (options and parameters) which pass // +// to the edge flip function flipnm(). // +// // +//============================================================================// class flipconstraints { @@ -1147,12 +1235,14 @@ class tetgenmesh { int collectencsegflag; // Optimization flags. + int noflip_in_surface; // do not flip edges (not segment) in surface. int remove_ndelaunay_edge; // Remove a non-Delaunay edge. REAL bak_tetprism_vol; // The value to be minimized. REAL tetprism_vol_sum; int remove_large_angle; // Remove a large dihedral angle at edge. REAL cosdihed_in; // The input cosine of the dihedral angle (> 0). REAL cosdihed_out; // The improved cosine of the dihedral angle. + REAL max_asp_out; // Max asp ratio after the improvement of dihedral angle. // Boundary recovery flags. int checkflipeligibility; @@ -1162,19 +1252,21 @@ class tetgenmesh { flipconstraints() { - enqflag = 0; + enqflag = 0; chkencflag = 0; unflip = 0; collectnewtets = 0; collectencsegflag = 0; + noflip_in_surface = 0; remove_ndelaunay_edge = 0; bak_tetprism_vol = 0.0; tetprism_vol_sum = 0.0; remove_large_angle = 0; cosdihed_in = 0.0; cosdihed_out = 0.0; + max_asp_out = 0.0; checkflipeligibility = 0; seg[0] = NULL; @@ -1183,13 +1275,13 @@ class tetgenmesh { } }; -/////////////////////////////////////////////////////////////////////////////// -// // -// optparameters // -// // -// Optimization options and parameters. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// optparameters // +// // +// Optimization options and parameters. // +// // +//============================================================================// class optparameters { @@ -1197,7 +1289,7 @@ class tetgenmesh { // The one of goals of optimization. int max_min_volume; // Maximize the minimum volume. - int max_min_aspectratio; // Maximize the minimum aspect ratio. + int min_max_aspectratio; // Minimize the maximum aspect ratio. int min_max_dihedangle; // Minimize the maximum dihedral angle. // The initial and improved value. @@ -1211,7 +1303,7 @@ class tetgenmesh { optparameters() { max_min_volume = 0; - max_min_aspectratio = 0; + min_max_aspectratio = 0; min_max_dihedangle = 0; initval = imprval = 0.0; @@ -1225,32 +1317,33 @@ class tetgenmesh { }; -/////////////////////////////////////////////////////////////////////////////// -// // -// Labels (enumeration declarations) used by TetGen. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// Labels (enumeration declarations) used by TetGen. // +// // +//============================================================================// - // Labels that signify the type of a vertex. - enum verttype {UNUSEDVERTEX, DUPLICATEDVERTEX, RIDGEVERTEX, ACUTEVERTEX, - FACETVERTEX, VOLVERTEX, FREESEGVERTEX, FREEFACETVERTEX, + // Labels that signify the type of a vertex. + enum verttype {UNUSEDVERTEX, DUPLICATEDVERTEX, RIDGEVERTEX, /*ACUTEVERTEX,*/ + FACETVERTEX, VOLVERTEX, FREESEGVERTEX, FREEFACETVERTEX, FREEVOLVERTEX, NREGULARVERTEX, DEADVERTEX}; - + // Labels that signify the result of triangle-triangle intersection test. enum interresult {DISJOINT, INTERSECT, SHAREVERT, SHAREEDGE, SHAREFACE, TOUCHEDGE, TOUCHFACE, ACROSSVERT, ACROSSEDGE, ACROSSFACE, - COLLISIONFACE, ACROSSSEG, ACROSSSUB}; + SELF_INTERSECT}; // Labels that signify the result of point location. enum locateresult {UNKNOWN, OUTSIDE, INTETRAHEDRON, ONFACE, ONEDGE, ONVERTEX, ENCVERTEX, ENCSEGMENT, ENCSUBFACE, NEARVERTEX, NONREGULAR, - INSTAR, BADELEMENT}; + INSTAR, BADELEMENT, NULLCAVITY, SHARPCORNER, FENSEDIN, + NONCOPLANAR, SELF_ENCROACH}; -/////////////////////////////////////////////////////////////////////////////// -// // -// Variables of TetGen // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// Variables of TetGen // +// // +//============================================================================// // Pointer to the input data (a set of nodes, a PLC, or a mesh). tetgenio *in, *addin; @@ -1268,30 +1361,60 @@ class tetgenmesh { // Memorypools to store bad-quality (or encroached) elements. memorypool *badtetrahedrons, *badsubfacs, *badsubsegs; - + memorypool *split_subfaces_pool, *split_segments_pool; + arraypool *unsplit_badtets, *unsplit_subfaces, *unsplit_segments; + arraypool *check_tets_list; + + badface *stack_enc_segments, *stack_enc_subfaces; + + // Bad quality subfaces are ordered by priority queues. + badface *queuefront[64]; + badface *queuetail[64]; + int nextnonemptyq[64]; + int firstnonemptyq, recentq; + + // Bad quality tetrahedra are ordered by priority queues. + memorypool *badqual_tets_pool; + badface *bt_queuefront[64]; + badface *bt_queuetail[64]; + int bt_nextnonemptyq[64]; + int bt_firstnonemptyq, bt_recentq; + // A memorypool to store faces to be flipped. memorypool *flippool; - arraypool *unflipqueue; - badface *flipstack; + arraypool *later_unflip_queue, *unflipqueue; + badface *flipstack, *unflip_queue_front, *unflip_queue_tail; // Arrays used for point insertion (the Bowyer-Watson algorithm). arraypool *cavetetlist, *cavebdrylist, *caveoldtetlist; + arraypool *cave_oldtet_list; // only tetrahedron's arraypool *cavetetshlist, *cavetetseglist, *cavetetvertlist; arraypool *caveencshlist, *caveencseglist; arraypool *caveshlist, *caveshbdlist, *cavesegshlist; + triface _bw_faces[4096]; // _bw_faces[64][64]; // Stacks used for CDT construction and boundary recovery. arraypool *subsegstack, *subfacstack, *subvertstack; + arraypool *skipped_segment_list, *skipped_facet_list; // Arrays of encroached segments and subfaces (for mesh refinement). arraypool *encseglist, *encshlist; // The map between facets to their vertices (for mesh refinement). - int *idx2facetlist; + int number_of_facets; + int *idx2facetlist; point *facetverticeslist; + int *idx_segment_facet_list; // segment-to-facet map. + int *segment_facet_list; + int *idx_ridge_vertex_facet_list; // vertex-to-facet map. + int *ridge_vertex_facet_list; // The map between segments to their endpoints (for mesh refinement). - point *segmentendpointslist; + int segmentendpointslist_length; + point *segmentendpointslist; + double *segment_info_list; + int *idx_segment_ridge_vertex_list; // are two ridge vertices form a segment? + point *segment_ridge_vertex_list; // The infinite vertex. point dummypoint; @@ -1302,9 +1425,9 @@ class tetgenmesh { // PI is the ratio of a circle's circumference to its diameter. static REAL PI; - // Array (size = numberoftetrahedra * 6) for storing high-order nodes of - // tetrahedra (only used when -o2 switch is selected). - point *highordertable; + // The list of subdomains. (-A option). + int subdomains; // Number of subdomains. + int *subdomain_markers; // Various variables. int numpointattrib; // Number of point attributes. @@ -1314,13 +1437,16 @@ class tetgenmesh { int pointparamindex; // Index to find the u,v coordinates of a point. int point2simindex; // Index to find a simplex adjacent to a point. int pointmarkindex; // Index to find boundary marker of a point. + int pointinsradiusindex; // Index to find the insertion radius of a point. int elemattribindex; // Index to find attributes of a tetrahedron. + int polarindex; // Index to find the polar plane parameters. int volumeboundindex; // Index to find volume bound of a tetrahedron. int elemmarkerindex; // Index to find marker of a tetrahedron. int shmarkindex; // Index to find boundary marker of a subface. int areaboundindex; // Index to find area bound of a subface. int checksubsegflag; // Are there segments in the tetrahedralization yet? int checksubfaceflag; // Are there subfaces in the tetrahedralization yet? + int boundary_recovery_flag; int checkconstraints; // Are there variant (node, seg, facet) constraints? int nonconvex; // Is current mesh non-convex? int autofliplinklevel; // The increase of link levels, default is 1. @@ -1330,38 +1456,55 @@ class tetgenmesh { REAL cosmaxdihed, cosmindihed; // The cosine values of max/min dihedral. REAL cossmtdihed; // The cosine value of a bad dihedral to be smoothed. REAL cosslidihed; // The cosine value of the max dihedral of a sliver. + REAL cos_large_dihed; // The cosine value of large dihedral (135 degree). + REAL opt_max_sliver_asp_ratio; // = 10 x b->opt_max_asp_ratio. REAL minfaceang, minfacetdihed; // The minimum input (dihedral) angles. + REAL cos_facet_separate_ang_tol; + REAL cos_collinear_ang_tol; REAL tetprism_vol_sum; // The total volume of tetrahedral-prisms (in 4D). REAL longest; // The longest possible edge length. + REAL minedgelength; // = longest * b->epsion. REAL xmax, xmin, ymax, ymin, zmax, zmin; // Bounding box of points. - // Counters. - long insegments; // Number of input segments. + // Options for mesh refinement. + REAL big_radius_edge_ratio; // calculated by qualitystatistics(). + REAL smallest_insradius; // Save the smallest insertion radius. + long elem_limit; + long insert_point_count; // number of attempted insertions. + long report_refine_progress; // the next report event. + long last_point_count; // number of points after last report event. + long last_insertion_count; // number of insertions after last report event. + + // Counters. + long insegments; // Number of input segments. long hullsize; // Number of exterior boundary faces. long meshedges; // Number of mesh edges. long meshhulledges; // Number of boundary mesh edges. long steinerleft; // Number of Steiner points not yet used. long dupverts; // Are there duplicated vertices? long unuverts; // Are there unused vertices? + long duplicated_facets_count; // Are there duplicated facets.? long nonregularcount; // Are there non-regular vertices? long st_segref_count, st_facref_count, st_volref_count; // Steiner points. long fillregioncount, cavitycount, cavityexpcount; long flip14count, flip26count, flipn2ncount; long flip23count, flip32count, flip44count, flip41count; long flip31count, flip22count; + long opt_flips_count, opt_collapse_count, opt_smooth_count; + long recover_delaunay_count; unsigned long totalworkmemory; // Total memory used by working arrays. -/////////////////////////////////////////////////////////////////////////////// -// // -// Mesh manipulation primitives // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// Mesh manipulation primitives // +// // +//============================================================================// // Fast lookup tables for mesh manipulation primitives. static int bondtbl[12][12], fsymtbl[12][12]; static int esymtbl[12], enexttbl[12], eprevtbl[12]; - static int enextesymtbl[12], eprevesymtbl[12]; + static int enextesymtbl[12], eprevesymtbl[12]; static int eorgoppotbl[12], edestoppotbl[12]; static int facepivot1[12], facepivot2[12][12]; static int orgpivot[12], destpivot[12], apexpivot[12], oppopivot[12]; @@ -1377,6 +1520,8 @@ class tetgenmesh { inline tetrahedron encode(triface& t); inline tetrahedron encode2(tetrahedron* ptr, int ver); inline void decode(tetrahedron ptr, triface& t); + inline tetrahedron* decode_tet_only(tetrahedron ptr); + inline int decode_ver_only(tetrahedron ptr); inline void bond(triface& t1, triface& t2); inline void dissolve(triface& t); inline void esym(triface& t1, triface& t2); @@ -1407,6 +1552,8 @@ class tetgenmesh { inline void setoppo(triface& t, point p); inline REAL elemattribute(tetrahedron* ptr, int attnum); inline void setelemattribute(tetrahedron* ptr, int attnum, REAL value); + inline REAL* get_polar(tetrahedron* ptr); + inline REAL get_volume(tetrahedron* ptr); inline REAL volumebound(tetrahedron* ptr); inline void setvolumebound(tetrahedron* ptr, REAL value); inline int elemindex(tetrahedron* ptr); @@ -1434,7 +1581,7 @@ class tetgenmesh { inline void decreaseelemcounter(triface& t); inline bool ishulltet(triface& t); inline bool isdeadtet(triface& t); - + // Primitives for subfaces and subsegments. inline void sdecode(shellface sptr, face& s); inline shellface sencode(face& s); @@ -1474,6 +1621,7 @@ class tetgenmesh { inline bool smarktest3ed(face& s); inline void setfacetindex(face& f, int value); inline int getfacetindex(face& f); + inline bool isdeadsh(face& s); // Primitives for interacting tetrahedra and subfaces. inline void tsbond(triface& t, face& s); @@ -1527,6 +1675,7 @@ class tetgenmesh { inline void setpoint2bgmtet(point pt, tetrahedron value); inline void setpointinsradius(point pt, REAL value); inline REAL getpointinsradius(point pt); + inline bool issteinerpoint(point pt); // Advanced primitives. inline void point2tetorg(point pt, triface& t); @@ -1534,11 +1683,11 @@ class tetgenmesh { inline point farsorg(face& seg); inline point farsdest(face& seg); -/////////////////////////////////////////////////////////////////////////////// -// // -// Memory managment // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// Memory managment // +// // +//============================================================================// void tetrahedrondealloc(tetrahedron*); tetrahedron *tetrahedrontraverse(); @@ -1551,47 +1700,45 @@ class tetgenmesh { void makeindex2pointmap(point*&); void makepoint2submap(memorypool*, int*&, face*&); void maketetrahedron(triface*); + void maketetrahedron2(triface*, point, point, point, point); void makeshellface(memorypool*, face*); void makepoint(point*, enum verttype); void initializepools(); -/////////////////////////////////////////////////////////////////////////////// -// // -// Advanced geometric predicates and calculations // -// // -// TetGen uses a simplified symbolic perturbation scheme from Edelsbrunner, // -// et al [*]. Hence the point-in-sphere test never returns a zero. The idea // -// is to perturb the weights of vertices in the fourth dimension. TetGen // -// uses the indices of the vertices decide the amount of perturbation. It is // -// implemented in the routine insphere_s(). -// // -// The routine tri_edge_test() determines whether or not a triangle and an // -// edge intersect in 3D. If they intersect, their intersection type is also // -// reported. This test is a combination of n 3D orientation tests (n is bet- // -// ween 3 and 9). It uses the robust orient3d() test to make the branch dec- // -// isions. The routine tri_tri_test() determines whether or not two triang- // -// les intersect in 3D. It also uses the robust orient3d() test. // -// // -// There are a number of routines to calculate geometrical quantities, e.g., // -// circumcenters, angles, dihedral angles, face normals, face areas, etc. // -// They are so far done by the default floating-point arithmetics which are // -// non-robust. They should be improved in the future. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// Advanced geometric predicates and calculations // +// // +// the routine insphere_s() implements a simplified symbolic perturbation // +// scheme from Edelsbrunner, et al [*]. Hence the point-in-sphere test never // +// returns a zero. The idea is to perturb the weights of vertices in 4D. // +// // +// The routine tri_edge_test() determines whether or not a triangle and an // +// edge intersect in 3D. If they do cross, their intersection type is also // +// reported. This test is a combination of n 3D orientation tests (3 < n < 9).// +// It uses the robust orient3d() test to make the branch decisions. // +// // +// There are several routines to calculate geometrical quantities, e.g., // +// circumcenters, angles, dihedral angles, face normals, face areas, etc. // +// They are implemented using floating-point arithmetics. // +// // +//============================================================================// // Symbolic perturbations (robust) REAL insphere_s(REAL*, REAL*, REAL*, REAL*, REAL*); - REAL orient4d_s(REAL*, REAL*, REAL*, REAL*, REAL*, + REAL orient4d_s(REAL*, REAL*, REAL*, REAL*, REAL*, REAL, REAL, REAL, REAL, REAL); + // An embedded 2-dimensional geometric predicate (non-robust) + REAL incircle3d(point pa, point pb, point pc, point pd); + // Triangle-edge intersection test (robust) int tri_edge_2d(point, point, point, point, point, point, int, int*, int*); - int tri_edge_tail(point, point, point, point, point, point, REAL, REAL, int, - int*, int*); + int tri_edge_tail(point,point,point,point,point,point,REAL,REAL,int,int*,int*); int tri_edge_test(point, point, point, point, point, point, int, int*, int*); - // Triangle-triangle intersection test (robust) + // Triangle-triangle intersection test (robust) int tri_edge_inter_tail(point, point, point, point, point, REAL, REAL); int tri_tri_inter(point, point, point, point, point, point); @@ -1601,62 +1748,60 @@ class tetgenmesh { bool lu_decmp(REAL lu[4][4], int n, int* ps, REAL* d, int N); void lu_solve(REAL lu[4][4], int n, int* ps, REAL* b, int N); - // An embedded 2-dimensional geometric predicate (non-robust) - REAL incircle3d(point pa, point pb, point pc, point pd); - // Geometric calculations (non-robust) REAL orient3dfast(REAL *pa, REAL *pb, REAL *pc, REAL *pd); inline REAL norm2(REAL x, REAL y, REAL z); inline REAL distance(REAL* p1, REAL* p2); + inline REAL distance2(REAL* p1, REAL* p2); void facenormal(point pa, point pb, point pc, REAL *n, int pivot, REAL *lav); - REAL shortdistance(REAL* p, REAL* e1, REAL* e2); + REAL facedihedral(REAL* pa, REAL* pb, REAL* pc1, REAL* pc2); REAL triarea(REAL* pa, REAL* pb, REAL* pc); REAL interiorangle(REAL* o, REAL* p1, REAL* p2, REAL* n); + REAL cos_interiorangle(REAL* o, REAL* p1, REAL* p2); void projpt2edge(REAL* p, REAL* e1, REAL* e2, REAL* prj); void projpt2face(REAL* p, REAL* f1, REAL* f2, REAL* f3, REAL* prj); - REAL facedihedral(REAL* pa, REAL* pb, REAL* pc1, REAL* pc2); - bool tetalldihedral(point, point, point, point, REAL*, REAL*, REAL*); - void tetallnormal(point, point, point, point, REAL N[4][3], REAL* volume); - REAL tetaspectratio(point, point, point, point); bool circumsphere(REAL*, REAL*, REAL*, REAL*, REAL* cent, REAL* radius); bool orthosphere(REAL*,REAL*,REAL*,REAL*,REAL,REAL,REAL,REAL,REAL*,REAL*); void planelineint(REAL*, REAL*, REAL*, REAL*, REAL*, REAL*, REAL*); - int linelineint(REAL*, REAL*, REAL*, REAL*, REAL*, REAL*, REAL*, REAL*); + int linelineint(REAL*, REAL*, REAL*, REAL*, REAL*, REAL*, REAL*, REAL*); REAL tetprismvol(REAL* pa, REAL* pb, REAL* pc, REAL* pd); bool calculateabovepoint(arraypool*, point*, point*, point*); void calculateabovepoint4(point, point, point, point); -/////////////////////////////////////////////////////////////////////////////// -// // -// Local mesh transformations // -// // -// A local transformation replaces a small set of tetrahedra with another // -// set of tetrahedra which fills the same space and the same boundaries. // -// In 3D, the most simplest local transformations are the elementary flips // -// performed within the convex hull of five vertices: 2-to-3, 3-to-2, 1-to-4,// -// and 4-to-1 flips, where the numbers indicate the number of tetrahedra // -// before and after each flip. The 1-to-4 and 4-to-1 flip involve inserting // -// or deleting a vertex, respectively. // -// There are complex local transformations which can be decomposed as a // -// combination of elementary flips. For example,a 4-to-4 flip which replaces // -// two coplanar edges can be regarded by a 2-to-3 flip and a 3-to-2 flip. // -// Note that the first 2-to-3 flip will temporarily create a degenerate tet- // -// rahedron which is removed immediately by the followed 3-to-2 flip. More // -// generally, a n-to-m flip, where n > 3, m = (n - 2) * 2, which removes an // -// edge can be done by first performing a sequence of (n - 3) 2-to-3 flips // -// followed by a 3-to-2 flip. // -// // -// The routines flip23(), flip32(), and flip41() perform the three element- // -// ray flips. The flip14() is available inside the routine insertpoint(). // -// // -// The routines flipnm() and flipnm_post() implement a generalized edge flip // -// algorithm which uses a combination of elementary flips. // -// // -// The routine insertpoint() implements a variant of Bowyer-Watson's cavity // -// algorithm to insert a vertex. It works for arbitrary tetrahedralization, // -// either Delaunay, or constrained Delaunay, or non-Delaunay. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// Local mesh transformations // +// // +// A local transformation replaces a set of tetrahedra with another set that // +// partitions the same space and boundaries. // +// // +// In 3D, the most straightforward local transformations are the elementary // +// flips performed within the convex hull of five vertices: 2-to-3, 3-to-2, // +// 1-to-4, and 4-to-1 flips. The numbers indicate the number of tetrahedra // +// before and after each flip. The 1-to-4 and 4-to-1 flip involve inserting // +// or deleting a vertex, respectively. // +// // +// There are complex local transformations that are a combination of element- // +// ary flips. For example, a 4-to-4 flip, which replaces two coplanar edges, // +// combines a 2-to-3 flip and a 3-to-2 flip. Note that the first 2-to-3 flip // +// will temporarily create a degenerate tetrahedron removed immediately by // +// the followed 3-to-2 flip. More generally, an n-to-m flip, where n > 3, // +// m = (n - 2) * 2, which removes an edge, can be done by first performing a // +// sequence of (n - 3) 2-to-3 flips followed by a 3-to-2 flip. // +// // +// The routines flip23(), flip32(), and flip41() perform the three elementray // +// flips. The flip14() is available inside the routine insertpoint(). // +// // +// The routines flipnm() and flipnm_post() implement a generalized edge flip // +// algorithm that uses elementary flips. // +// // +// The routine insertpoint() implements the Bowyer-Watson's cavity algorithm // +// to insert a vertex. It works for arbitrary tetrahedralization, either // +// Delaunay, or constrained Delaunay, or non-Delaunay. // +// // +//============================================================================// + + void flippush(badface*&, triface*); // The elementary flips. void flip23(triface*, int, flipconstraints* fc); @@ -1671,62 +1816,64 @@ class tetgenmesh { int insertpoint(point, triface*, face*, face*, insertvertexflags*); void insertpoint_abort(face*, insertvertexflags*); -/////////////////////////////////////////////////////////////////////////////// -// // -// Delaunay tetrahedralization // -// // -// The routine incrementaldelaunay() implemented two incremental algorithms // -// for constructing Delaunay tetrahedralizations (DTs): the Bowyer-Watson // -// (B-W) algorithm and the incremental flip algorithm of Edelsbrunner and // -// Shah, "Incremental topological flipping works for regular triangulation," // -// Algorithmica, 15:233-241, 1996. // -// // -// The routine incrementalflip() implements the flip algorithm of [Edelsbru- // -// nner and Shah, 1996]. It flips a queue of locally non-Delaunay faces (in // -// an arbitrary order). The success is guaranteed when the Delaunay tetrah- // -// edralization is constructed incrementally by adding one vertex at a time. // -// // -// The routine locate() finds a tetrahedron contains a new point in current // -// DT. It uses a simple stochastic walk algorithm: starting from an arbitr- // -// ary tetrahedron in DT, it finds the destination by visit one tetrahedron // -// at a time, randomly chooses a tetrahedron if there are more than one // -// choices. This algorithm terminates due to Edelsbrunner's acyclic theorem. // -// Choose a good starting tetrahedron is crucial to the speed of the walk. // -// TetGen originally uses the "jump-and-walk" algorithm of Muecke, E.P., // -// Saias, I., and Zhu, B. "Fast Randomized Point Location Without Preproces- // -// sing." In Proceedings of the 12th ACM Symposium on Computational Geometry,// -// 274-283, 1996. It first randomly samples several tetrahedra in the DT // -// and then choosing the closet one to start walking. // -// The above algorithm slows download dramatically as the number of points // -// grows -- reported in Amenta, N., Choi, S. and Rote, G., "Incremental // -// construction con {BRIO}," In Proceedings of 19th ACM Symposium on // -// Computational Geometry, 211-219, 2003. On the other hand, Liu and // -// Snoeyink showed that the point location can be made in constant time if // -// the points are pre-sorted so that the nearby points in space have nearby // -// indices, then adding the points in this order. They sorted the points // -// along the 3D Hilbert curve. // -// // -// The routine hilbert_sort3() sorts a set of 3D points along the 3D Hilbert // -// curve. It recursively splits a point set according to the Hilbert indices // -// mapped to the subboxes of the bounding box of the point set. // -// The Hilbert indices is calculated by Butz's algorithm in 1971. A nice // -// exposition of this algorithm can be found in the paper of Hamilton, C., // -// "Compact Hilbert Indices", Technical Report CS-2006-07, Computer Science, // -// Dalhousie University, 2006 (the Section 2). My implementation also refer- // -// enced Steven Witham's implementation of "Hilbert walk" (hopefully, it is // -// still available at: http://www.tiac.net/~sw/2008/10/Hilbert/). // -// // -// TetGen sorts the points using the method in the paper of Boissonnat,J.-D.,// -// Devillers, O. and Hornus, S. "Incremental Construction of the Delaunay // -// Triangulation and the Delaunay Graph in Medium Dimension," In Proceedings // -// of the 25th ACM Symposium on Computational Geometry, 2009. // -// It first randomly sorts the points into subgroups using the Biased Rand-// -// omized Insertion Ordering (BRIO) of Amenta et al 2003, then sorts the // -// points in each subgroup along the 3D Hilbert curve. Inserting points in // -// this order ensures a randomized "sprinkling" of the points over the // -// domain, while sorting of each subset ensures locality. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// Delaunay tetrahedralization // +// // +// The routine incrementaldelaunay() implemented two incremental algorithms // +// for constructing Delaunay tetrahedralizations (DTs): the Bowyer-Watson // +// (B-W) algorithm and the incremental flip algorithm of Edelsbrunner and // +// Shah, "Incremental topological flipping works for regular triangulation," // +// Algorithmica, 15:233-241, 1996. // +// // +// The routine incrementalflip() implements the flip algorithm of [Edelsbrun- // +// ner and Shah, 1996]. It flips a queue of locally non-Delaunay faces (in // +// arbitrary order). The success is guaranteed when the Delaunay tetrahedra- // +// lization is constructed incrementally by adding one vertex at a time. // +// // +// The routine locate() finds a tetrahedron contains a new point in current // +// DT. It uses a simple stochastic walk algorithm: starting from an arbitrary // +// tetrahedron in DT, it finds the destination by visit one tetrahedron at a // +// time, randomly chooses a tetrahedron if there are more than one choices. // +// This algorithm terminates due to Edelsbrunner's acyclic theorem. // +// // +// Choose a good starting tetrahedron is crucial to the speed of the walk. // +// TetGen initially uses the "jump-and-walk" algorithm of Muecke, E.P., Saias,// +// I., and Zhu, B. "Fast Randomized Point Location Without Preprocessing." In // +// Proceedings of the 12th ACM Symposium on Computational Geometry, 274-283, // +// 1996. It first randomly samples several tetrahedra in the DT and then // +// choosing the closet one to start walking. // +// // +// The above algorithm slows download dramatically as the number of points // +// grows -- reported in Amenta, N., Choi, S. and Rote, G., "Incremental // +// construction con {BRIO}," In Proceedings of 19th ACM Symposium on Computa- // +// tional Geometry, 211-219, 2003. On the other hand, Liu and Snoeyink showed // +// that the point location could be made in constant time if the points are // +// pre-sorted so that the nearby points in space have nearby indices, then // +// adding the points in this order. They sorted the points along the 3D // +// Hilbert curve. // +// // +// The routine hilbert_sort3() sorts a set of 3D points along the 3D Hilbert // +// curve. It recursively splits a point set according to the Hilbert indices // +// mapped to the subboxes of the bounding box of the point set. The Hilbert // +// indices is calculated by Butz's algorithm in 1971. An excellent exposition // +// of this algorithm can be found in the paper of Hamilton, C., "Compact // +// Hilbert Indices", Technical Report CS-2006-07, Computer Science, Dalhousie // +// University, 2006 (the Section 2). My implementation also referenced Steven // +// Witham's performance of "Hilbert walk" (hopefully, it is still available // +// at http://www.tiac.net/~sw/2008/10/Hilbert/). // +// // +// TetGen sorts the points using the method in the paper of Boissonnat,J.-D., // +// Devillers, O. and Hornus, S. "Incremental Construction of the Delaunay // +// Triangulation and the Delaunay Graph in Medium Dimension," In Proceedings // +// of the 25th ACM Symposium on Computational Geometry, 2009. It first // +// randomly sorts the points into subgroups using the Biased Randomized // +// Insertion Ordering (BRIO) of Amenta et al 2003, then sorts the points in // +// each subgroup along the 3D Hilbert curve. Inserting points in this order // +// ensure a randomized "sprinkling" of the points over the domain, while // +// sorting of each subset provides locality. // +// // +//============================================================================// void transfernodes(); @@ -1742,21 +1889,19 @@ class tetgenmesh { // Point location. unsigned long randomnation(unsigned int choices); void randomsample(point searchpt, triface *searchtet); - enum locateresult locate(point searchpt, triface *searchtet); - - // Incremental flips. - void flippush(badface*&, triface*); - int incrementalflip(point newpt, int, flipconstraints *fc); + enum locateresult locate(point searchpt, triface *searchtet, int chkencflag = 0); // Incremental Delaunay construction. + enum locateresult locate_dt(point searchpt, triface *searchtet); + int insert_vertex_bw(point, triface*, insertvertexflags*); void initialdelaunay(point pa, point pb, point pc, point pd); void incrementaldelaunay(clock_t&); -/////////////////////////////////////////////////////////////////////////////// -// // -// Surface triangulation // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// Surface triangulation // +// // +//============================================================================// void flipshpush(face*); void flip22(face*, int, int); @@ -1766,151 +1911,138 @@ class tetgenmesh { int sremovevertex(point delpt, face*, face*, int lawson); enum locateresult slocate(point, face*, int, int, int); - enum interresult sscoutsegment(face*, point); + enum interresult sscoutsegment(face*, point, int, int, int); void scarveholes(int, REAL*); - void triangulate(int, arraypool*, arraypool*, int, REAL*); + int triangulate(int, arraypool*, arraypool*, int, REAL*); - void unifysubfaces(face*, face*); void unifysegments(); + void identifyinputedges(point*); void mergefacets(); - void identifypscedges(point*); void meshsurface(); - void interecursive(shellface** subfacearray, int arraysize, int axis, - REAL, REAL, REAL, REAL, REAL, REAL, int* internum); - void detectinterfaces(); - -/////////////////////////////////////////////////////////////////////////////// -// // -// Constrained Delaunay tetrahedralization // -// // -// A constrained Delaunay tetrahedralization (CDT) is a variation of a Dela- // -// unay tetrahedralization (DT) that is constrained to respect the boundary // -// of a 3D PLC (domain). In a CDT of a 3D PLC, every vertex or edge of the // -// PLC is also a vertex or an edge of the CDT, every polygon of the PLC is a // -// union of triangles of the CDT. A crucial difference between a CDT and a // -// DT is that triangles in the PLC's polygons are not required to be locally // -// Delaunay, which frees the CDT to better respect the PLC's polygons. CDTs // -// have optimal properties similar to those of DTs. // -// // -// Steiner Points and Steiner CDTs. It is known that even a simple 3D polyh- // -// edron may not have a tetrahedralization which only uses its own vertices. // -// Some extra points, so-called "Steiner points" are needed in order to form // -// a tetrahedralization of such polyhedron. It is true for tetrahedralizing // -// a 3D PLC as well. A Steiner CDT of a 3D PLC is a CDT containing Steiner // -// points. The CDT algorithms of TetGen in general create Steiner CDTs. // -// Almost all of the Steiner points are added in the edges of the PLC. They // -// guarantee the existence of a CDT of the modified PLC. // -// // -// The routine constraineddelaunay() starts from a DT of the vertices of a // -// PLC and creates a (Steiner) CDT of the PLC (including Steiner points). It // -// is constructed by two steps, (1) segment recovery and (2) facet (polygon) // -// recovery. Each step is accomplished by its own algorithm. // -// // -// The routine delaunizesegments() implements the segment recovery algorithm // -// of Si, H. and Gaertner, K. "Meshing Piecewise Linear Complexes by Constr- // -// ained Delaunay Tetrahedralizations," In Proceedings of the 14th Internat- // -// ional Meshing Roundtable, 147--163, 2005. It adds Steiner points into // -// non-Delaunay segments until all subsegments appear together in a DT. The // -// running time of this algorithm is proportional to the number of added // -// Steiner points. // -// // -// There are two incremental facet recovery algorithms: the cavity re-trian- // -// gulation algorithm of Si, H. and Gaertner, K. "3D Boundary Recovery by // -// Constrained Delaunay Tetrahedralization," International Journal for Numer-// -// ical Methods in Engineering, 85:1341-1364, 2011, and the flip algorithm // -// of Shewchuk, J. "Updating and Constructing Constrained Delaunay and // -// Constrained Regular Triangulations by Flips." In Proceedings of the 19th // -// ACM Symposium on Computational Geometry, 86-95, 2003. // -// // -// It is guaranteed in theory, no Steiner point is needed in both algorithms // -// However, a facet with non-coplanar vertices might cause the additions of // -// Steiner points. It is discussed in the paper of Si, H., and Shewchuk, J.,// -// "Incrementally Constructing and Updating Constrained Delaunay // -// Tetrahedralizations with Finite Precision Coordinates." In Proceedings of // -// the 21th International Meshing Roundtable, 2012. // -// // -// Our implementation of the facet recovery algorithms recover a "missing // -// region" at a time. Each missing region is a subset of connected interiors // -// of a polygon. The routine formcavity() creates the cavity of crossing // -// tetrahedra of the missing region. // -// // -// The cavity re-triangulation algorithm is implemented by three subroutines,// -// delaunizecavity(), fillcavity(), and carvecavity(). Since it may fail due // -// to non-coplanar vertices, the subroutine restorecavity() is used to rest- // -// ore the original cavity. // -// // -// The routine flipinsertfacet() implements the flip algorithm. The subrout- // -// ine flipcertify() is used to maintain the priority queue of flips. // -// // -// The routine refineregion() is called when the facet recovery algorithm // -// fail to recover a missing region. It inserts Steiner points to refine the // -// missing region. In order to avoid inserting Steiner points very close to // -// existing segments. The classical encroachment rules of the Delaunay // -// refinement algorithm are used to choose the Steiner points. // -// // -// The routine constrainedfacets() does the facet recovery by using either // -// the cavity re-triangulation algorithm (default) or the flip algorithm. It // -// results a CDT of the (modified) PLC (including Steiner points). // -// // -/////////////////////////////////////////////////////////////////////////////// - void makesegmentendpointsmap(); +//============================================================================// +// // +// Constrained Delaunay tetrahedralization // +// // +// A constrained Delaunay tetrahedralization (CDT) is a variation of a Delau- // +// nay tetrahedralization (DT) that respects the boundary of a 3D PLC (mesh // +// domain). A crucial difference between a CDT and a DT is that triangles in // +// the PLC's polygons are not required to be locally Delaunay, which frees // +// the CDT to respect the PLC's polygons better. CDTs have optimal properties // +// similar to those of DTs. // +// // +// Steiner Points and Steiner CDTs. It is well-known that even a simple 3D // +// polyhedron may not have a tetrahedralization which only uses its vertices. // +// Some extra points, so-called "Steiner points" are needed to form a tetrah- // +// edralization of such polyhedron. A Steiner CDT of a 3D PLC is a CDT // +// containing Steiner points. TetGen generates Steiner CDTs. // +// // +// The routine constraineddelaunay() creates a (Steiner) CDT of the PLC // +// (including Steiner points). It has two steps, (1) segment recovery and (2) // +// facet (polygon) recovery. // +// // +// The routine delaunizesegments() implements the segment recovery algorithm // +// of Si, H., and Gaertner, K. "Meshing Piecewise Linear Complexes by // +// Constrained Delaunay Tetrahedralizations," In Proceedings of the 14th // +// International Meshing Roundtable, 147--163, 2005. It adds Steiner points // +// into non-Delaunay segments until all subsegments appear together in a DT. // +// The running time of this algorithm is proportional to the number of // +// Steiner points. // +// // +// There are two incremental facet recovery algorithms: the cavity re- // +// triangulation algorithm of Si, H., and Gaertner, K. "3D Boundary Recovery // +// by Constrained Delaunay Tetrahedralization," International Journal for // +// Numerical Methods in Engineering, 85:1341-1364, 2011, and the flip // +// algorithm of Shewchuk, J. "Updating and Constructing Constrained Delaunay // +// and Constrained Regular Triangulations by Flips." In Proceedings of the // +// 19th ACM Symposium on Computational Geometry, 86-95, 2003. // +// // +// Although no Steiner point is needed in step (2), a facet with non-coplanar // +// vertices might need Steiner points. It is discussed in the paper of Si, H.,// +// and Shewchuk, J., "Incrementally Constructing and Updating Constrained // +// Delaunay Tetrahedralizations with Finite Precision Coordinates." In // +// Proceedings of the 21th International Meshing Roundtable, 2012. // +// // +// Our implementation of the facet recovery algorithms recovers a "missing // +// region" at a time. Each missing region is a subset of connected interiors // +// of a polygon. The routine formcavity() creates the cavity of crossing // +// tetrahedra of the missing region. The cavity re-triangulation algorithm is // +// implemented by three subroutines, delaunizecavity(), fillcavity(), and // +// carvecavity(). Since it may fail due to non-coplanar vertices, the // +// subroutine restorecavity() is used to restore the original cavity. // +// // +// The routine flipinsertfacet() implements the flip algorithm. The sub- // +// routine flipcertify() is used to maintain the priority queue of flips. // +// The routine refineregion() is called when the facet recovery algorithm // +// fails to recover a missing region. It inserts Steiner points to refine the // +// missing region. To avoid inserting Steiner points very close to existing // +// segments. The classical encroachment rules of the Delaunay refinement // +// algorithm are used to choose the Steiner points. The routine // +// constrainedfacets() does the facet recovery by using either the cavity re- // +// triangulation algorithm (default) or the flip algorithm. It results in a // +// CDT of the (modified) PLC (including Steiner points). // +// // +//============================================================================// enum interresult finddirection(triface* searchtet, point endpt); - enum interresult scoutsegment(point, point, triface*, point*, arraypool*); + enum interresult scoutsegment(point, point, face*, triface*, point*, + arraypool*); int getsteinerptonsegment(face* seg, point refpt, point steinpt); void delaunizesegments(); - enum interresult scoutsubface(face* searchsh, triface* searchtet); + int scoutsubface(face* searchsh,triface* searchtet,int shflag); void formregion(face*, arraypool*, arraypool*, arraypool*); int scoutcrossedge(triface& crosstet, arraypool*, arraypool*); - bool formcavity(triface*, arraypool*, arraypool*, arraypool*, arraypool*, + bool formcavity(triface*, arraypool*, arraypool*, arraypool*, arraypool*, arraypool*, arraypool*); - // Facet recovery by cavity re-triangulation [Si and Gaertner 2011]. - void delaunizecavity(arraypool*, arraypool*, arraypool*, arraypool*, + void delaunizecavity(arraypool*, arraypool*, arraypool*, arraypool*, arraypool*, arraypool*); bool fillcavity(arraypool*, arraypool*, arraypool*, arraypool*, arraypool*, arraypool*, triface* crossedge); void carvecavity(arraypool*, arraypool*, arraypool*); void restorecavity(arraypool*, arraypool*, arraypool*, arraypool*); - // Facet recovery by flips [Shewchuk 2003]. void flipcertify(triface *chkface, badface **pqueue, point, point, point); void flipinsertfacet(arraypool*, arraypool*, arraypool*, arraypool*); - bool fillregion(arraypool* missingshs, arraypool*, arraypool* newshs); - int insertpoint_cdt(point, triface*, face*, face*, insertvertexflags*, arraypool*, arraypool*, arraypool*, arraypool*, arraypool*, arraypool*); void refineregion(face&, arraypool*, arraypool*, arraypool*, arraypool*, arraypool*, arraypool*); - - void constrainedfacets(); + void constrainedfacets(); void constraineddelaunay(clock_t&); -/////////////////////////////////////////////////////////////////////////////// -// // -// Constrained tetrahedralizations. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// Constrained tetrahedralizations. // +// // +//============================================================================// + + void sort_2pts(point p1, point p2, point ppt[2]); + void sort_3pts(point p1, point p2, point p3, point ppt[3]); + bool is_collinear_at(point mid, point left, point right); + bool is_segment(point p1, point p2); + bool valid_constrained_f23(triface&, point pd, point pe); + bool valid_constrained_f32(triface*, point pa, point pb); + int checkflipeligibility(int fliptype, point, point, point, point, point, int level, int edgepivot, flipconstraints* fc); int removeedgebyflips(triface*, flipconstraints*); int removefacebyflips(triface*, flipconstraints*); - int recoveredgebyflips(point, point, triface*, int fullsearch); - int add_steinerpt_in_schoenhardtpoly(triface*, int, int chkencflag); - int add_steinerpt_in_segment(face*, int searchlevel); - int addsteiner4recoversegment(face*, int); + int recoveredgebyflips(point, point, face*, triface*, int fullsearch, int& idir); + int add_steinerpt_in_schoenhardtpoly(triface*, int, int, int chkencflag); + int add_steinerpt_in_segment(face*, int searchlevel, int& idir); + int add_steinerpt_to_recover_edge(point, point, face*, int, int, int& idir); int recoversegments(arraypool*, int fullsearch, int steinerflag); - int recoverfacebyflips(point, point, point, face*, triface*); + int recoverfacebyflips(point,point,point,face*,triface*,int&,point*,point*); int recoversubfaces(arraypool*, int steinerflag); int getvertexstar(int, point searchpt, arraypool*, arraypool*, arraypool*); @@ -1918,22 +2050,25 @@ class tetgenmesh { int reduceedgesatvertex(point startpt, arraypool* endptlist); int removevertexbyflips(point steinerpt); + int smoothpoint(point smtpt, arraypool*, int ccw, optparameters *opm); int suppressbdrysteinerpoint(point steinerpt); int suppresssteinerpoints(); void recoverboundary(clock_t&); -/////////////////////////////////////////////////////////////////////////////// -// // -// Mesh reconstruction // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// Mesh reconstruction // +// // +//============================================================================// void carveholes(); void reconstructmesh(); - int scoutpoint(point, triface*, int randflag); + int search_face(point p0, point p1, point p2, triface &tetloop); + int search_edge(point p0, point p1, triface &tetloop); + int scout_point(point, triface*, int randflag); REAL getpointmeshsize(point, triface*, int iloc); void interpolatemeshsize(); @@ -1943,98 +2078,129 @@ class tetgenmesh { void collectremovepoints(arraypool *remptlist); void meshcoarsening(); -/////////////////////////////////////////////////////////////////////////////// -// // -// Mesh refinement // -// // -// The purpose of mesh refinement is to obtain a tetrahedral mesh with well- // -// -shaped tetrahedra and appropriate mesh size. It is necessary to insert // -// new Steiner points to achieve this property. The questions are (1) how to // -// choose the Steiner points? and (2) how to insert them? // -// // -// Delaunay refinement is a technique first developed by Chew [1989] and // -// Ruppert [1993, 1995] to generate quality triangular meshes in the plane. // -// It provides guarantee on the smallest angle of the triangles. Rupper's // -// algorithm guarantees that the mesh is size-optimal (to within a constant // -// factor) among all meshes with the same quality. // -// Shewchuk generalized Ruppert's algorithm into 3D in his PhD thesis // -// [Shewchuk 1997]. A short version of his algorithm appears in "Tetrahedral // -// Mesh Generation by Delaunay Refinement," In Proceedings of the 14th ACM // -// Symposium on Computational Geometry, 86-95, 1998. It guarantees that all // -// tetrahedra of the output mesh have a "radius-edge ratio" (equivalent to // -// the minimal face angle) bounded. However, it does not remove slivers, a // -// type of very flat tetrahedra which can have no small face angles but have // -// very small (and large) dihedral angles. Moreover, it may not terminate if // -// the input PLC contains "sharp features", e.g., two edges (or two facets) // -// meet at an acute angle (or dihedral angle). // -// // -// TetGen uses the basic Delaunay refinement scheme to insert Steiner points.// -// While it always maintains a constrained Delaunay mesh. The algorithm is // -// described in Si, H., "Adaptive Constrained Delaunay Mesh Generation," // -// International Journal for Numerical Methods in Engineering, 75:856-880. // -// This algorithm always terminates and sharp features are easily preserved. // -// The mesh has good quality (same as Shewchuk's Delaunay refinement algori- // -// thm) in the bulk of the mesh domain. Moreover, it supports the generation // -// of adaptive mesh according to a (isotropic) mesh sizing function. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// Mesh refinement // +// // +// The purpose of mesh refinement is to obtain a tetrahedral mesh with well- // +// -shaped tetrahedra and appropriate mesh size. It is necessary to insert // +// new Steiner points to achieve this property. The questions are (1) how to // +// choose the Steiner points? and (2) how to insert them? // +// // +// Delaunay refinement is a technique first developed by Chew [1989] and // +// Ruppert [1993, 1995] to generate quality triangular meshes in the plane. // +// It provides guarantee on the smallest angle of the triangles. Rupper's // +// algorithm guarantees that the mesh is size-optimal (to within a constant // +// factor) among all meshes with the same quality. // +// Shewchuk generalized Ruppert's algorithm into 3D in his PhD thesis // +// [Shewchuk 1997]. A short version of his algorithm appears in "Tetrahedral // +// Mesh Generation by Delaunay Refinement," In Proceedings of the 14th ACM // +// Symposium on Computational Geometry, 86-95, 1998. It guarantees that all // +// tetrahedra of the output mesh have a "radius-edge ratio" (equivalent to // +// the minimal face angle) bounded. However, it does not remove slivers, a // +// type of very flat tetrahedra which can have no small face angles but have // +// very small (and large) dihedral angles. Moreover, it may not terminate if // +// the input PLC contains "sharp features", e.g., two edges (or two facets) // +// meet at an acute angle (or dihedral angle). // +// // +// TetGen uses the basic Delaunay refinement scheme to insert Steiner points. // +// While it always maintains a constrained Delaunay mesh. The algorithm is // +// described in Si, H., "Adaptive Constrained Delaunay Mesh Generation," // +// International Journal for Numerical Methods in Engineering, 75:856-880. // +// This algorithm always terminates and sharp features are easily preserved. // +// The mesh has good quality (same as Shewchuk's Delaunay refinement algori- // +// thm) in the bulk of the mesh domain. Moreover, it supports the generation // +// of adaptive mesh according to a (isotropic) mesh sizing function. // +// // +//============================================================================// + + void makesegmentendpointsmap(); + REAL set_ridge_vertex_protecting_ball(point); + REAL get_min_angle_at_ridge_vertex(face* seg); + REAL get_min_diahedral_angle(face* seg); + void create_segment_info_list(); void makefacetverticesmap(); - int segsegadjacent(face *, face *); - int segfacetadjacent(face *checkseg, face *checksh); - int facetfacetadjacent(face *, face *); + void create_segment_facet_map(); - int checkseg4encroach(point pa, point pb, point checkpt); - int checkseg4split(face *chkseg, point&, int&); - int splitsegment(face *splitseg, point encpt, REAL, point, point, int, int); - void repairencsegs(int chkencflag); + int ridge_vertices_adjacent(point, point); + int facet_ridge_vertex_adjacent(face *, point); + int segsegadjacent(face *, face *); + int segfacetadjacent(face *checkseg, face *checksh); + int facetfacetadjacent(face *, face *); + bool is_sharp_segment(face* seg); + bool does_seg_contain_acute_vertex(face* seg); + bool create_a_shorter_edge(point steinerpt, point nearpt); void enqueuesubface(memorypool*, face*); - int checkfac4encroach(point, point, point, point checkpt, REAL*, REAL*); - int checkfac4split(face *chkfac, point& encpt, int& qflag, REAL *ccent); - int splitsubface(face *splitfac, point, point, int qflag, REAL *ccent, int); - void repairencfacs(int chkencflag); - void enqueuetetrahedron(triface*); - int checktet4split(triface *chktet, int& qflag, REAL *ccent); - int splittetrahedron(triface* splittet,int qflag,REAL *ccent, int); - void repairbadtets(int chkencflag); + + bool check_encroachment(point pa, point pb, point checkpt); + bool check_enc_segment(face *chkseg, point *pencpt); + bool get_steiner_on_segment(face* seg, point encpt, point newpt); + bool split_segment(face *splitseg, point encpt, REAL *param, int qflag, int, int*); + void repairencsegs(REAL *param, int qflag, int chkencflag); + + bool get_subface_ccent(face *chkfac, REAL *ccent); + bool check_enc_subface(face *chkfac, point *pencpt, REAL *ccent, REAL *radius); + bool check_subface(face *chkfac, REAL *ccent, REAL radius, REAL *param); + void enqueue_subface(face *bface, point encpt, REAL *ccent, REAL *param); + badface* top_subface(); + void dequeue_subface(); + void parallel_shift(point pa, point pb, point pc, point pt, REAL* ppt); + enum locateresult locate_on_surface(point searchpt, face* searchsh); + bool split_subface(face *splitfac, point encpt, REAL *ccent, REAL*, int, int, int*); + void repairencfacs(REAL *param, int qflag, int chkencflag); + + bool check_tetrahedron(triface *chktet, REAL* param, int& qflag); + bool checktet4split(triface *chktet, REAL* param, int& qflag); + enum locateresult locate_point_walk(point searchpt, triface*, int chkencflag); + bool split_tetrahedron(triface*, REAL*, int, int, insertvertexflags &ivf); + void repairbadtets(REAL queratio, int chkencflag); void delaunayrefinement(); -/////////////////////////////////////////////////////////////////////////////// -// // -// Mesh optimization // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// Mesh optimization // +// // +//============================================================================// long lawsonflip3d(flipconstraints *fc); void recoverdelaunay(); - int gettetrahedron(point, point, point, point, triface *); - long improvequalitybyflips(); - - int smoothpoint(point smtpt, arraypool*, int ccw, optparameters *opm); - long improvequalitybysmoothing(optparameters *opm); - - int splitsliver(triface *, REAL, int); - long removeslivers(int); - - void optimizemesh(); - -/////////////////////////////////////////////////////////////////////////////// -// // -// Mesh check and statistics // -// // -/////////////////////////////////////////////////////////////////////////////// + int get_seg_laplacian_center(point mesh_vert, REAL target[3]); + int get_surf_laplacian_center(point mesh_vert, REAL target[3]); + int get_laplacian_center(point mesh_vert, REAL target[3]); + bool move_vertex(point mesh_vert, REAL target[3]); + void smooth_vertices(); + + bool get_tet(point, point, point, point, triface *); + bool get_tetqual(triface *chktet, point oppo_pt, badface *bf); + bool get_tetqual(point, point, point, point, badface *bf); + void enqueue_badtet(badface *bf); + badface* top_badtet(); + void dequeue_badtet(); + + bool add_steinerpt_to_repair(badface *bf, bool bSmooth); + bool flip_edge_to_improve(triface *sliver_edge, REAL& improved_cosmaxd); + bool repair_tet(badface *bf, bool bFlips, bool bSmooth, bool bSteiners); + long repair_badqual_tets(bool bFlips, bool bSmooth, bool bSteiners); + void improve_mesh(); + +//============================================================================// +// // +// Mesh check and statistics // +// // +//============================================================================// // Mesh validations. - int checkmesh(int topoflag); - int checkshells(); - int checksegments(); - int checkdelaunay(); - int checkregular(int); - int checkconforming(int); + int check_mesh(int topoflag); + int check_shells(); + int check_segments(); + int check_delaunay(int perturb = 1); + int check_regular(int); + int check_conforming(int); // Mesh statistics. void printfcomma(unsigned long n); @@ -2042,14 +2208,15 @@ class tetgenmesh { void memorystatistics(); void statistics(); -/////////////////////////////////////////////////////////////////////////////// -// // -// Mesh output // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// Mesh output // +// // +//============================================================================// void jettisonnodes(); void highorder(); + void indexelements(); void numberedges(); void outnodes(tetgenio*); void outmetrics(tetgenio*); @@ -2063,43 +2230,68 @@ class tetgenmesh { void outvoronoi(tetgenio*); void outsmesh(char*); void outmesh2medit(char*); - void outmesh2vtk(char*); + void outmesh2vtk(char*, int); + void out_surfmesh_vtk(char*, int); + void out_intersected_facets(); -/////////////////////////////////////////////////////////////////////////////// -// // -// Constructor & destructor // -// // -/////////////////////////////////////////////////////////////////////////////// - tetgenmesh() +//============================================================================// +// // +// Constructor & destructor // +// // +//============================================================================// + + void initializetetgenmesh() { in = addin = NULL; b = NULL; bgm = NULL; tetrahedrons = subfaces = subsegs = points = NULL; - badtetrahedrons = badsubfacs = badsubsegs = NULL; tet2segpool = tet2subpool = NULL; - flippool = NULL; - dummypoint = NULL; - flipstack = NULL; - unflipqueue = NULL; + + badtetrahedrons = badsubfacs = badsubsegs = NULL; + split_segments_pool = split_subfaces_pool = NULL; + unsplit_badtets = unsplit_subfaces = unsplit_segments = NULL; + check_tets_list = NULL; + badqual_tets_pool = NULL; + + stack_enc_segments = stack_enc_subfaces = NULL; + + flippool = NULL; + flipstack = unflip_queue_front = unflip_queue_tail = NULL; + later_unflip_queue = unflipqueue = NULL; cavetetlist = cavebdrylist = caveoldtetlist = NULL; + cave_oldtet_list = NULL; cavetetshlist = cavetetseglist = cavetetvertlist = NULL; caveencshlist = caveencseglist = NULL; caveshlist = caveshbdlist = cavesegshlist = NULL; subsegstack = subfacstack = subvertstack = NULL; + skipped_segment_list = skipped_facet_list = NULL; + encseglist = encshlist = NULL; + + number_of_facets = 0; idx2facetlist = NULL; facetverticeslist = NULL; + idx_segment_facet_list = NULL; + segment_facet_list = NULL; + idx_ridge_vertex_facet_list = NULL; + ridge_vertex_facet_list = NULL; + + segmentendpointslist_length = 0; segmentendpointslist = NULL; + segment_info_list = NULL; + idx_segment_ridge_vertex_list = NULL; + segment_ridge_vertex_list = NULL; - highordertable = NULL; + subdomains = 0; + subdomain_markers = NULL; numpointattrib = numelemattrib = 0; sizeoftensor = 0; @@ -2107,12 +2299,15 @@ class tetgenmesh { pointparamindex = 0; pointmarkindex = 0; point2simindex = 0; + pointinsradiusindex = 0; elemattribindex = 0; + polarindex = 0; volumeboundindex = 0; shmarkindex = 0; areaboundindex = 0; checksubsegflag = 0; checksubfaceflag = 0; + boundary_recovery_flag = 0; checkconstraints = 0; nonconvex = 0; autofliplinklevel = 1; @@ -2120,25 +2315,37 @@ class tetgenmesh { samples = 0l; randomseed = 1l; minfaceang = minfacetdihed = PI; + cos_facet_separate_ang_tol = cos(179.9/180.*PI); + cos_collinear_ang_tol = cos(179.9/180.*PI); tetprism_vol_sum = 0.0; - longest = 0.0; + longest = minedgelength = 0.0; xmax = xmin = ymax = ymin = zmax = zmin = 0.0; + smallest_insradius = 1.e+30; + big_radius_edge_ratio = 100.0; + elem_limit = 0; + insert_point_count = 0l; + report_refine_progress = 0l; + last_point_count = 0l; + last_insertion_count = 0l; + insegments = 0l; hullsize = 0l; meshedges = meshhulledges = 0l; steinerleft = -1; dupverts = 0l; unuverts = 0l; + duplicated_facets_count = 0l; nonregularcount = 0l; st_segref_count = st_facref_count = st_volref_count = 0l; fillregioncount = cavitycount = cavityexpcount = 0l; flip14count = flip26count = flipn2ncount = 0l; flip23count = flip32count = flip44count = flip41count = 0l; flip22count = flip31count = 0l; + recover_delaunay_count = 0l; + opt_flips_count = opt_collapse_count = opt_smooth_count = 0l; totalworkmemory = 0l; - } // tetgenmesh() void freememory() @@ -2151,23 +2358,37 @@ class tetgenmesh { delete points; delete [] dummypoint; } - if (tetrahedrons != (memorypool *) NULL) { delete tetrahedrons; } - if (subfaces != (memorypool *) NULL) { delete subfaces; delete subsegs; } - if (tet2segpool != NULL) { delete tet2segpool; delete tet2subpool; } + if (badtetrahedrons) { + delete badtetrahedrons; + } + if (badsubfacs) { + delete badsubfacs; + } + if (badsubsegs) { + delete badsubsegs; + } + if (unsplit_badtets) { + delete unsplit_badtets; + } + if (check_tets_list) { + delete check_tets_list; + } + if (flippool != NULL) { delete flippool; + delete later_unflip_queue; delete unflipqueue; } @@ -2176,6 +2397,7 @@ class tetgenmesh { delete cavebdrylist; delete caveoldtetlist; delete cavetetvertlist; + delete cave_oldtet_list; } if (caveshlist != NULL) { @@ -2197,15 +2419,32 @@ class tetgenmesh { if (idx2facetlist != NULL) { delete [] idx2facetlist; delete [] facetverticeslist; + delete [] idx_segment_facet_list; + delete [] segment_facet_list; + delete [] idx_ridge_vertex_facet_list; + delete [] ridge_vertex_facet_list; } if (segmentendpointslist != NULL) { delete [] segmentendpointslist; + delete [] idx_segment_ridge_vertex_list; + delete [] segment_ridge_vertex_list; + } + + if (segment_info_list != NULL) { + delete [] segment_info_list; } - if (highordertable != NULL) { - delete [] highordertable; + if (subdomain_markers != NULL) { + delete [] subdomain_markers; } + + initializetetgenmesh(); + } + + tetgenmesh() + { + initializetetgenmesh(); } ~tetgenmesh() @@ -2215,47 +2454,44 @@ class tetgenmesh { }; // End of class tetgenmesh. -/////////////////////////////////////////////////////////////////////////////// -// // -// tetrahedralize() Interface for using TetGen's library to generate // -// Delaunay tetrahedralizations, constrained Delaunay // -// tetrahedralizations, quality tetrahedral meshes. // -// // -// 'in' is an object of 'tetgenio' which contains a PLC you want to tetrahed-// -// ralize or a previously generated tetrahedral mesh you want to refine. It // -// must not be a NULL. 'out' is another object of 'tetgenio' for storing the // -// generated tetrahedral mesh. It can be a NULL. If so, the output will be // -// saved to file(s). If 'bgmin' != NULL, it contains a background mesh which // -// defines a mesh size function. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, +//============================================================================// +// // +// tetrahedralize() Interface for using TetGen's library to generate // +// Delaunay tetrahedralizations, constrained Delaunay // +// tetrahedralizations, quality tetrahedral meshes. // +// // +// 'in' is an object of 'tetgenio' containing a PLC or a previously generated // +// tetrahedral mesh you want to refine. 'out' is another object of 'tetgenio'// +// for returing the generated tetrahedral mesh. If it is a NULL pointer, the // +// output mesh is saved to file(s). If 'bgmin' != NULL, it contains a back- // +// ground mesh defining a mesh size function. // +// // +//============================================================================// + +void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, tetgenio *addin = NULL, tetgenio *bgmin = NULL); #ifdef TETLIBRARY -void tetrahedralize(const char *switches, tetgenio *in, tetgenio *out, +void tetrahedralize(char *switches, tetgenio *in, tetgenio *out, tetgenio *addin = NULL, tetgenio *bgmin = NULL); + #endif // #ifdef TETLIBRARY -/////////////////////////////////////////////////////////////////////////////// -// // -// terminatetetgen() Terminate TetGen with a given exit code. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// terminatetetgen() Terminate TetGen with a given exit code. // +// // +//============================================================================// + inline void terminatetetgen(tetgenmesh *m, int x) { #ifdef TETLIBRARY throw x; #else - // Release the allocated memory. - if (m) { - m->freememory(); - } switch (x) { case 1: // Out of memory. - printf("Error: Out of memory.\n"); + printf("Error: Out of memory.\n"); break; case 2: // Encounter an internal error. printf("Please report this bug to Hang.Si@wias-berlin.de. Include\n"); @@ -2263,32 +2499,38 @@ inline void terminatetetgen(tetgenmesh *m, int x) printf(" command line you used to run this program, thank you.\n"); break; case 3: - printf("A self-intersection was detected. Program stopped.\n"); - printf("Hint: use -d option to detect all self-intersections.\n"); + printf("The input surface mesh contain self-intersections. Program stopped.\n"); + //printf("Hint: use -d option to detect all self-intersections.\n"); break; case 4: printf("A very small input feature size was detected. Program stopped.\n"); - printf("Hint: use -T option to set a smaller tolerance.\n"); + if (m) { + printf("Hint: use -T option to set a smaller tolerance. Current is %g\n", + m->b->epsilon); + } break; case 5: printf("Two very close input facets were detected. Program stopped.\n"); printf("Hint: use -Y option to avoid adding Steiner points in boundary.\n"); break; case 10: - printf("An input error was detected. Program stopped.\n"); + printf("An input error was detected. Program stopped.\n"); + break; + case 200: + printf("Boundary contains Steiner points (-YY option). Program stopped.\n"); break; } // switch (x) exit(x); #endif // #ifdef TETLIBRARY } -/////////////////////////////////////////////////////////////////////////////// -// // -// Primitives for tetrahedra // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// Primitives for tetrahedra // +// // +//============================================================================// -// encode() compress a handle into a single pointer. It relies on the +// encode() compress a handle into a single pointer. It relies on the // assumption that all addresses of tetrahedra are aligned to sixteen- // byte boundaries, so that the last four significant bits are zero. @@ -2308,8 +2550,18 @@ inline void tetgenmesh::decode(tetrahedron ptr, triface& t) { (t).tet = (tetrahedron *) ((uintptr_t) (ptr) ^ (uintptr_t) (t).ver); } -// bond() connects two tetrahedra together. (t1,v1) and (t2,v2) must -// refer to the same face and the same edge. +inline tetgenmesh::tetrahedron* tetgenmesh::decode_tet_only(tetrahedron ptr) +{ + return (tetrahedron *) ((((uintptr_t) ptr) >> 4) << 4); +} + +inline int tetgenmesh::decode_ver_only(tetrahedron ptr) +{ + return (int) ((uintptr_t) (ptr) & (uintptr_t) 15); +} + +// bond() connects two tetrahedra together. (t1,v1) and (t2,v2) must +// refer to the same face and the same edge. inline void tetgenmesh::bond(triface& t1, triface& t2) { t1.tet[t1.ver & 3] = encode2(t2.tet, bondtbl[t1.ver][t2.ver]); @@ -2327,22 +2579,22 @@ inline void tetgenmesh::dissolve(triface& t) { inline void tetgenmesh::enext(triface& t1, triface& t2) { t2.tet = t1.tet; - t2.ver = enexttbl[t1.ver]; + t2.ver = enexttbl[t1.ver]; // (t1.ver + 4) % 12; } inline void tetgenmesh::enextself(triface& t) { - t.ver = enexttbl[t.ver]; + t.ver = enexttbl[t.ver]; // (t.ver + 4) % 12; } // eprev() finds the next edge (clockwise) in the same face. inline void tetgenmesh::eprev(triface& t1, triface& t2) { t2.tet = t1.tet; - t2.ver = eprevtbl[t1.ver]; + t2.ver = eprevtbl[t1.ver]; // (t1.ver + 8) % 12; } inline void tetgenmesh::eprevself(triface& t) { - t.ver = eprevtbl[t.ver]; + t.ver = eprevtbl[t.ver]; // (t.ver + 8) % 12; } // esym() finds the reversed edge. It is in the other face of the @@ -2358,7 +2610,7 @@ inline void tetgenmesh::esymself(triface& t) { } // enextesym() finds the reversed edge of the next edge. It is in the other -// face of the same tetrahedron. It is the combination esym() * enext(). +// face of the same tetrahedron. It is the combination esym() * enext(). inline void tetgenmesh::enextesym(triface& t1, triface& t2) { t2.tet = t1.tet; @@ -2392,7 +2644,7 @@ inline void tetgenmesh::eorgoppoself(triface& t) { t.ver = eorgoppotbl[t.ver]; } -// edestoppo() Finds the opposite face of the destination of the current +// edestoppo() Finds the opposite face of the destination of the current // edge. Return the opposite edge of the current edge. inline void tetgenmesh::edestoppo(triface& t1, triface& t2) { @@ -2474,13 +2726,23 @@ inline void tetgenmesh:: setoppo(triface& t, point p) { (t).tet[apexpivot[(t).ver]] = (tetrahedron) (tapex); \ (t).tet[oppopivot[(t).ver]] = (tetrahedron) (toppo) + +inline REAL* tetgenmesh::get_polar(tetrahedron* ptr) +{ + return &(((REAL *) (ptr))[polarindex]); +} +inline REAL tetgenmesh::get_volume(tetrahedron* ptr) +{ + return ((REAL *) (ptr))[polarindex + 4]; +} + // Check or set a tetrahedron's attributes. inline REAL tetgenmesh::elemattribute(tetrahedron* ptr, int attnum) { return ((REAL *) (ptr))[elemattribindex + attnum]; } -inline void tetgenmesh::setelemattribute(tetrahedron* ptr, int attnum, +inline void tetgenmesh::setelemattribute(tetrahedron* ptr, int attnum, REAL value) { ((REAL *) (ptr))[elemattribindex + attnum] = value; } @@ -2508,7 +2770,7 @@ inline void tetgenmesh::setelemindex(tetrahedron* ptr, int value) { iptr[0] = value; } -// Get or set a tetrahedron's marker. +// Get or set a tetrahedron's marker. // Set 'value = 0' cleans all the face/edge flags. inline int tetgenmesh::elemmarker(tetrahedron* ptr) { @@ -2545,14 +2807,14 @@ inline void tetgenmesh::marktest(triface& t) { inline void tetgenmesh::unmarktest(triface& t) { ((int *) (t.tet))[elemmarkerindex] &= ~2; } - + inline bool tetgenmesh::marktested(triface& t) { return (((int *) (t.tet))[elemmarkerindex] & 2) != 0; } // markface(), unmarkface(), facemarked() -- primitives to flag or unflag a // face of a tetrahedron. From the last 3rd to 6th bits are used for -// face markers, e.g., the last third bit corresponds to loc = 0. +// face markers, e.g., the last third bit corresponds to loc = 0. inline void tetgenmesh::markface(triface& t) { ((int *) (t.tet))[elemmarkerindex] |= (4 << (t.ver & 3)); @@ -2568,7 +2830,7 @@ inline bool tetgenmesh::facemarked(triface& t) { // markedge(), unmarkedge(), edgemarked() -- primitives to flag or unflag an // edge of a tetrahedron. From the last 7th to 12th bits are used for -// edge markers, e.g., the last 7th bit corresponds to the 0th edge, etc. +// edge markers, e.g., the last 7th bit corresponds to the 0th edge, etc. // Remark: The last 7th bit is marked by 2^6 = 64. inline void tetgenmesh::markedge(triface& t) { @@ -2580,7 +2842,7 @@ inline void tetgenmesh::unmarkedge(triface& t) { } inline bool tetgenmesh::edgemarked(triface& t) { - return (((int *) (t.tet))[elemmarkerindex] & + return (((int *) (t.tet))[elemmarkerindex] & (int) (64 << ver2edge[(t).ver])) != 0; } @@ -2601,7 +2863,7 @@ inline bool tetgenmesh::marktest2ed(triface& t) { // elemcounter(), setelemcounter() -- primitives to read or ser a (small) // integer counter in this tet. It is saved from the 16th bit. On 32 bit -// system, the range of the counter is [0, 2^15 = 32768]. +// system, the range of the counter is [0, 2^15 = 32768]. inline int tetgenmesh::elemcounter(triface& t) { return (((int *) (t.tet))[elemmarkerindex]) >> 16; @@ -2637,11 +2899,11 @@ inline bool tetgenmesh::isdeadtet(triface& t) { return ((t.tet == NULL) || (t.tet[4] == NULL)); } -/////////////////////////////////////////////////////////////////////////////// -// // -// Primitives for subfaces and subsegments // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// Primitives for subfaces and subsegments // +// // +//============================================================================// // Each subface contains three pointers to its neighboring subfaces, with // edge versions. To save memory, both information are kept in a single @@ -2667,7 +2929,7 @@ inline tetgenmesh::shellface tetgenmesh::sencode2(shellface *sh, int shver) { // sbond() bonds two subfaces (s1) and (s2) together. s1 and s2 must refer // to the same edge. No requirement is needed on their orientations. -inline void tetgenmesh::sbond(face& s1, face& s2) +inline void tetgenmesh::sbond(face& s1, face& s2) { s1.sh[s1.shver >> 1] = sencode(s2); s2.sh[s2.shver >> 1] = sencode(s1); @@ -2677,7 +2939,7 @@ inline void tetgenmesh::sbond(face& s1, face& s2) // but s2 is not pointing to s1. s1 and s2 must refer to the same edge. // No requirement is needed on their orientations. -inline void tetgenmesh::sbond1(face& s1, face& s2) +inline void tetgenmesh::sbond1(face& s1, face& s2) { s1.sh[s1.shver >> 1] = sencode(s2); } @@ -2693,13 +2955,13 @@ inline void tetgenmesh::sdissolve(face& s) // spivot() finds the adjacent subface (s2) for a given subface (s1). // s1 and s2 share at the same edge. -inline void tetgenmesh::spivot(face& s1, face& s2) +inline void tetgenmesh::spivot(face& s1, face& s2) { shellface sptr = s1.sh[s1.shver >> 1]; sdecode(sptr, s2); } -inline void tetgenmesh::spivotself(face& s) +inline void tetgenmesh::spivotself(face& s) { shellface sptr = s.sh[s.shver >> 1]; sdecode(sptr, s); @@ -2708,32 +2970,32 @@ inline void tetgenmesh::spivotself(face& s) // These primitives determine or set the origin, destination, or apex // of a subface with respect to the edge version. -inline tetgenmesh::point tetgenmesh::sorg(face& s) +inline tetgenmesh::point tetgenmesh::sorg(face& s) { return (point) s.sh[sorgpivot[s.shver]]; } -inline tetgenmesh::point tetgenmesh::sdest(face& s) +inline tetgenmesh::point tetgenmesh::sdest(face& s) { return (point) s.sh[sdestpivot[s.shver]]; } -inline tetgenmesh::point tetgenmesh::sapex(face& s) +inline tetgenmesh::point tetgenmesh::sapex(face& s) { return (point) s.sh[sapexpivot[s.shver]]; } -inline void tetgenmesh::setsorg(face& s, point pointptr) +inline void tetgenmesh::setsorg(face& s, point pointptr) { s.sh[sorgpivot[s.shver]] = (shellface) pointptr; } -inline void tetgenmesh::setsdest(face& s, point pointptr) +inline void tetgenmesh::setsdest(face& s, point pointptr) { s.sh[sdestpivot[s.shver]] = (shellface) pointptr; } -inline void tetgenmesh::setsapex(face& s, point pointptr) +inline void tetgenmesh::setsapex(face& s, point pointptr) { s.sh[sapexpivot[s.shver]] = (shellface) pointptr; } @@ -2745,13 +3007,13 @@ inline void tetgenmesh::setsapex(face& s, point pointptr) // sesym() reserves the direction of the lead edge. -inline void tetgenmesh::sesym(face& s1, face& s2) +inline void tetgenmesh::sesym(face& s1, face& s2) { s2.sh = s1.sh; s2.shver = (s1.shver ^ 1); // Inverse the last bit. } -inline void tetgenmesh::sesymself(face& s) +inline void tetgenmesh::sesymself(face& s) { s.shver ^= 1; } @@ -2759,24 +3021,24 @@ inline void tetgenmesh::sesymself(face& s) // senext() finds the next edge (counterclockwise) in the same orientation // of this face. -inline void tetgenmesh::senext(face& s1, face& s2) +inline void tetgenmesh::senext(face& s1, face& s2) { s2.sh = s1.sh; s2.shver = snextpivot[s1.shver]; } -inline void tetgenmesh::senextself(face& s) +inline void tetgenmesh::senextself(face& s) { s.shver = snextpivot[s.shver]; } -inline void tetgenmesh::senext2(face& s1, face& s2) +inline void tetgenmesh::senext2(face& s1, face& s2) { s2.sh = s1.sh; s2.shver = snextpivot[snextpivot[s1.shver]]; } -inline void tetgenmesh::senext2self(face& s) +inline void tetgenmesh::senext2self(face& s) { s.shver = snextpivot[snextpivot[s.shver]]; } @@ -2784,12 +3046,12 @@ inline void tetgenmesh::senext2self(face& s) // Check or set a subface's maximum area bound. -inline REAL tetgenmesh::areabound(face& s) +inline REAL tetgenmesh::areabound(face& s) { return ((REAL *) (s.sh))[areaboundindex]; } -inline void tetgenmesh::setareabound(face& s, REAL value) +inline void tetgenmesh::setareabound(face& s, REAL value) { ((REAL *) (s.sh))[areaboundindex] = value; } @@ -2797,12 +3059,12 @@ inline void tetgenmesh::setareabound(face& s, REAL value) // These two primitives read or set a shell marker. Shell markers are used // to hold user boundary information. -inline int tetgenmesh::shellmark(face& s) +inline int tetgenmesh::shellmark(face& s) { return ((int *) (s.sh))[shmarkindex]; } -inline void tetgenmesh::setshellmark(face& s, int value) +inline void tetgenmesh::setshellmark(face& s, int value) { ((int *) (s.sh))[shmarkindex] = value; } @@ -2812,21 +3074,21 @@ inline void tetgenmesh::setshellmark(face& s, int value) // sinfect(), sinfected(), suninfect() -- primitives to flag or unflag a // subface. The last bit of ((int *) ((s).sh))[shmarkindex+1] is flagged. -inline void tetgenmesh::sinfect(face& s) +inline void tetgenmesh::sinfect(face& s) { - ((int *) ((s).sh))[shmarkindex+1] = + ((int *) ((s).sh))[shmarkindex+1] = (((int *) ((s).sh))[shmarkindex+1] | (int) 1); } -inline void tetgenmesh::suninfect(face& s) +inline void tetgenmesh::suninfect(face& s) { - ((int *) ((s).sh))[shmarkindex+1] = + ((int *) ((s).sh))[shmarkindex+1] = (((int *) ((s).sh))[shmarkindex+1] & ~(int) 1); } // Test a subface for viral infection. -inline bool tetgenmesh::sinfected(face& s) +inline bool tetgenmesh::sinfected(face& s) { return (((int *) ((s).sh))[shmarkindex+1] & (int) 1) != 0; } @@ -2834,65 +3096,65 @@ inline bool tetgenmesh::sinfected(face& s) // smarktest(), smarktested(), sunmarktest() -- primitives to flag or unflag // a subface. The last 2nd bit of the integer is flagged. -inline void tetgenmesh::smarktest(face& s) +inline void tetgenmesh::smarktest(face& s) { - ((int *) ((s).sh))[shmarkindex+1] = + ((int *) ((s).sh))[shmarkindex+1] = (((int *)((s).sh))[shmarkindex+1] | (int) 2); } -inline void tetgenmesh::sunmarktest(face& s) +inline void tetgenmesh::sunmarktest(face& s) { - ((int *) ((s).sh))[shmarkindex+1] = + ((int *) ((s).sh))[shmarkindex+1] = (((int *)((s).sh))[shmarkindex+1] & ~(int)2); } -inline bool tetgenmesh::smarktested(face& s) +inline bool tetgenmesh::smarktested(face& s) { return ((((int *) ((s).sh))[shmarkindex+1] & (int) 2) != 0); } -// smarktest2(), smarktest2ed(), sunmarktest2() -- primitives to flag or +// smarktest2(), smarktest2ed(), sunmarktest2() -- primitives to flag or // unflag a subface. The last 3rd bit of the integer is flagged. -inline void tetgenmesh::smarktest2(face& s) +inline void tetgenmesh::smarktest2(face& s) { - ((int *) ((s).sh))[shmarkindex+1] = + ((int *) ((s).sh))[shmarkindex+1] = (((int *)((s).sh))[shmarkindex+1] | (int) 4); } -inline void tetgenmesh::sunmarktest2(face& s) +inline void tetgenmesh::sunmarktest2(face& s) { - ((int *) ((s).sh))[shmarkindex+1] = + ((int *) ((s).sh))[shmarkindex+1] = (((int *)((s).sh))[shmarkindex+1] & ~(int)4); } -inline bool tetgenmesh::smarktest2ed(face& s) +inline bool tetgenmesh::smarktest2ed(face& s) { return ((((int *) ((s).sh))[shmarkindex+1] & (int) 4) != 0); } // The last 4th bit of ((int *) ((s).sh))[shmarkindex+1] is flagged. -inline void tetgenmesh::smarktest3(face& s) +inline void tetgenmesh::smarktest3(face& s) { - ((int *) ((s).sh))[shmarkindex+1] = + ((int *) ((s).sh))[shmarkindex+1] = (((int *)((s).sh))[shmarkindex+1] | (int) 8); } -inline void tetgenmesh::sunmarktest3(face& s) +inline void tetgenmesh::sunmarktest3(face& s) { - ((int *) ((s).sh))[shmarkindex+1] = + ((int *) ((s).sh))[shmarkindex+1] = (((int *)((s).sh))[shmarkindex+1] & ~(int)8); } -inline bool tetgenmesh::smarktest3ed(face& s) +inline bool tetgenmesh::smarktest3ed(face& s) { return ((((int *) ((s).sh))[shmarkindex+1] & (int) 8) != 0); } // Each facet has a unique index (automatically indexed). Starting from '0'. -// We save this index in the same field of the shell type. +// We save this index in the same field of the shell type. inline void tetgenmesh::setfacetindex(face& s, int value) { @@ -2904,15 +3166,21 @@ inline int tetgenmesh::getfacetindex(face& s) return ((int *) (s.sh))[shmarkindex + 2]; } -/////////////////////////////////////////////////////////////////////////////// -// // -// Primitives for interacting between tetrahedra and subfaces // -// // -/////////////////////////////////////////////////////////////////////////////// +// Tests if the subface (subsegment) s is dead. + +inline bool tetgenmesh::isdeadsh(face& s) { + return ((s.sh == NULL) || (s.sh[3] == NULL)); +} + +//============================================================================// +// // +// Primitives for interacting between tetrahedra and subfaces // +// // +//============================================================================// // tsbond() bond a tetrahedron (t) and a subface (s) together. // Note that t and s must be the same face and the same edge. Moreover, -// t and s have the same orientation. +// t and s have the same orientation. // Since the edge number in t and in s can be any number in {0,1,2}. We bond // the edge in s which corresponds to t's 0th edge, and vice versa. @@ -2927,10 +3195,10 @@ inline void tetgenmesh::tsbond(triface& t, face& s) } } // Bond t <== s. - ((shellface *) (t).tet[9])[(t).ver & 3] = + ((shellface *) (t).tet[9])[(t).ver & 3] = sencode2((s).sh, tsbondtbl[t.ver][s.shver]); // Bond s <== t. - s.sh[9 + ((s).shver & 1)] = + s.sh[9 + ((s).shver & 1)] = (shellface) encode2((t).tet, stbondtbl[t.ver][s.shver]); } @@ -2939,7 +3207,7 @@ inline void tetgenmesh::tsbond(triface& t, face& s) // the subface s, and s and t must be at the same edge wth the same // orientation. -inline void tetgenmesh::tspivot(triface& t, face& s) +inline void tetgenmesh::tspivot(triface& t, face& s) { if ((t).tet[9] == NULL) { (s).sh = NULL; @@ -2958,7 +3226,7 @@ inline void tetgenmesh::tspivot(triface& t, face& s) // Return the t (if it exists) with the same edge and the same // orientation of s. -inline void tetgenmesh::stpivot(face& s, triface& t) +inline void tetgenmesh::stpivot(face& s, triface& t) { decode((tetrahedron) s.sh[9 + (s.shver & 1)], t); if ((t).tet == NULL) { @@ -2974,7 +3242,7 @@ inline void tetgenmesh::stpivot(face& s, triface& t) // tsdissolve() dissolve a bond (from the tetrahedron side). -inline void tetgenmesh::tsdissolve(triface& t) +inline void tetgenmesh::tsdissolve(triface& t) { if ((t).tet[9] != NULL) { ((shellface *) (t).tet[9])[(t).ver & 3] = NULL; @@ -2983,27 +3251,27 @@ inline void tetgenmesh::tsdissolve(triface& t) // stdissolve() dissolve a bond (from the subface side). -inline void tetgenmesh::stdissolve(face& s) +inline void tetgenmesh::stdissolve(face& s) { (s).sh[9] = NULL; (s).sh[10] = NULL; } -/////////////////////////////////////////////////////////////////////////////// -// // -// Primitives for interacting between subfaces and segments // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// Primitives for interacting between subfaces and segments // +// // +//============================================================================// // ssbond() bond a subface to a subsegment. -inline void tetgenmesh::ssbond(face& s, face& edge) +inline void tetgenmesh::ssbond(face& s, face& edge) { s.sh[6 + (s.shver >> 1)] = sencode(edge); edge.sh[0] = sencode(s); } -inline void tetgenmesh::ssbond1(face& s, face& edge) +inline void tetgenmesh::ssbond1(face& s, face& edge) { s.sh[6 + (s.shver >> 1)] = sencode(edge); //edge.sh[0] = sencode(s); @@ -3011,14 +3279,14 @@ inline void tetgenmesh::ssbond1(face& s, face& edge) // ssdisolve() dissolve a bond (from the subface side) -inline void tetgenmesh::ssdissolve(face& s) +inline void tetgenmesh::ssdissolve(face& s) { s.sh[6 + (s.shver >> 1)] = NULL; } // sspivot() finds a subsegment abutting a subface. -inline void tetgenmesh::sspivot(face& s, face& edge) +inline void tetgenmesh::sspivot(face& s, face& edge) { sdecode((shellface) s.sh[6 + (s.shver >> 1)], edge); } @@ -3028,11 +3296,11 @@ inline void tetgenmesh::sspivot(face& s, face& edge) #define isshsubseg(s) \ ((s).sh[6 + ((s).shver >> 1)]) -/////////////////////////////////////////////////////////////////////////////// -// // -// Primitives for interacting between tetrahedra and segments // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// Primitives for interacting between tetrahedra and segments // +// // +//============================================================================// inline void tetgenmesh::tssbond1(triface& t, face& s) { @@ -3044,10 +3312,10 @@ inline void tetgenmesh::tssbond1(triface& t, face& s) ((shellface *) (t).tet[8])[i] = NULL; } } - ((shellface *) (t).tet[8])[ver2edge[(t).ver]] = sencode((s)); + ((shellface *) (t).tet[8])[ver2edge[(t).ver]] = sencode((s)); } -inline void tetgenmesh::sstbond1(face& s, triface& t) +inline void tetgenmesh::sstbond1(face& s, triface& t) { ((tetrahedron *) (s).sh)[9] = encode(t); } @@ -3059,7 +3327,7 @@ inline void tetgenmesh::tssdissolve1(triface& t) } } -inline void tetgenmesh::sstdissolve1(face& s) +inline void tetgenmesh::sstdissolve1(face& s) { ((tetrahedron *) (s).sh)[9] = NULL; } @@ -3078,19 +3346,19 @@ inline void tetgenmesh::tsspivot1(triface& t, face& s) #define issubseg(t) \ ((t).tet[8] && ((t).tet[8])[ver2edge[(t).ver]]) -inline void tetgenmesh::sstpivot1(face& s, triface& t) +inline void tetgenmesh::sstpivot1(face& s, triface& t) { decode((tetrahedron) s.sh[9], t); } -/////////////////////////////////////////////////////////////////////////////// -// // -// Primitives for points // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// Primitives for points // +// // +//============================================================================// -inline int tetgenmesh::pointmark(point pt) { - return ((int *) (pt))[pointmarkindex]; +inline int tetgenmesh::pointmark(point pt) { + return ((int *) (pt))[pointmarkindex]; } inline void tetgenmesh::setpointmark(point pt, int value) { @@ -3105,30 +3373,10 @@ inline enum tetgenmesh::verttype tetgenmesh::pointtype(point pt) { } inline void tetgenmesh::setpointtype(point pt, enum verttype value) { - ((int *) (pt))[pointmarkindex + 1] = + ((int *) (pt))[pointmarkindex + 1] = ((int) value << 8) + (((int *) (pt))[pointmarkindex + 1] & (int) 255); } -// Read and set the geometry tag of the point (used by -s option). - -inline int tetgenmesh::pointgeomtag(point pt) { - return ((int *) (pt))[pointmarkindex + 2]; -} - -inline void tetgenmesh::setpointgeomtag(point pt, int value) { - ((int *) (pt))[pointmarkindex + 2] = value; -} - -// Read and set the u,v coordinates of the point (used by -s option). - -inline REAL tetgenmesh::pointgeomuv(point pt, int i) { - return pt[pointparamindex + i]; -} - -inline void tetgenmesh::setpointgeomuv(point pt, int i, REAL value) { - pt[pointparamindex + i] = value; -} - // pinfect(), puninfect(), pinfected() -- primitives to flag or unflag // a point. The last bit of the integer '[pointindex+1]' is flagged. @@ -3144,8 +3392,8 @@ inline bool tetgenmesh::pinfected(point pt) { return (((int *) (pt))[pointmarkindex + 1] & (int) 1) != 0; } -// pmarktest(), punmarktest(), pmarktested() -- more primitives to -// flag or unflag a point. +// pmarktest(), punmarktest(), pmarktested() -- more primitives to +// flag or unflag a point. inline void tetgenmesh::pmarktest(point pt) { ((int *) (pt))[pointmarkindex + 1] |= (int) 2; @@ -3183,6 +3431,28 @@ inline bool tetgenmesh::pmarktest3ed(point pt) { return (((int *) (pt))[pointmarkindex + 1] & (int) 8) != 0; } +// Read and set the geometry tag of the point (used by -s option). + +inline int tetgenmesh::pointgeomtag(point pt) { + return ((int *) (pt))[pointmarkindex + 2]; +} + +inline void tetgenmesh::setpointgeomtag(point pt, int value) { + ((int *) (pt))[pointmarkindex + 2] = value; +} + +// Read and set the u,v coordinates of the point (used by -s option). + +inline REAL tetgenmesh::pointgeomuv(point pt, int i) { + return pt[pointparamindex + i]; +} + +inline void tetgenmesh::setpointgeomuv(point pt, int i, REAL value) { + pt[pointparamindex + i] = value; +} + + + // These following primitives set and read a pointer to a tetrahedron // a subface/subsegment, a point, or a tet of background mesh. @@ -3223,12 +3493,17 @@ inline void tetgenmesh::setpoint2bgmtet(point pt, tetrahedron value) { // The primitives for saving and getting the insertion radius. inline void tetgenmesh::setpointinsradius(point pt, REAL value) { - pt[pointmtrindex + sizeoftensor - 1] = value; + pt[pointinsradiusindex] = value; } inline REAL tetgenmesh::getpointinsradius(point pt) { - return pt[pointmtrindex + sizeoftensor - 1]; + return pt[pointinsradiusindex]; +} + +inline bool tetgenmesh::issteinerpoint(point pt) { + return (pointtype(pt) == FREESEGVERTEX) || (pointtype(pt) == FREEFACETVERTEX) + || (pointtype(pt) == FREEVOLVERTEX); } // point2tetorg() Get the tetrahedron whose origin is the point. @@ -3243,7 +3518,6 @@ inline void tetgenmesh::point2tetorg(point pa, triface& searchtet) } else if ((point) searchtet.tet[6] == pa) { searchtet.ver = 7; } else { - assert((point) searchtet.tet[7] == pa); // SELF_CHECK searchtet.ver = 0; } } @@ -3256,9 +3530,8 @@ inline void tetgenmesh::point2shorg(point pa, face& searchsh) if ((point) searchsh.sh[3] == pa) { searchsh.shver = 0; } else if ((point) searchsh.sh[4] == pa) { - searchsh.shver = (searchsh.sh[5] != NULL ? 2 : 1); + searchsh.shver = (searchsh.sh[5] != NULL ? 2 : 1); } else { - assert((point) searchsh.sh[5] == pa); // SELF_CHECK searchsh.shver = 4; } } @@ -3273,25 +3546,25 @@ inline tetgenmesh::point tetgenmesh::farsorg(face& s) travesh = s; while (1) { senext2(travesh, neighsh); - spivotself(neighsh); + spivotself(neighsh); if (neighsh.sh == NULL) break; if (sorg(neighsh) != sorg(travesh)) sesymself(neighsh); - senext2(neighsh, travesh); + senext2(neighsh, travesh); } return sorg(travesh); } -inline tetgenmesh::point tetgenmesh::farsdest(face& s) +inline tetgenmesh::point tetgenmesh::farsdest(face& s) { face travesh, neighsh; travesh = s; while (1) { senext(travesh, neighsh); - spivotself(neighsh); + spivotself(neighsh); if (neighsh.sh == NULL) break; if (sdest(neighsh) != sdest(travesh)) sesymself(neighsh); - senext(neighsh, travesh); + senext(neighsh, travesh); } return sdest(travesh); } @@ -3303,13 +3576,13 @@ inline tetgenmesh::point tetgenmesh::farsdest(face& s) /////////////////////////////////////////////////////////////////////////////// // dot() returns the dot product: v1 dot v2. -inline REAL tetgenmesh::dot(REAL* v1, REAL* v2) +inline REAL tetgenmesh::dot(REAL* v1, REAL* v2) { return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; } // cross() computes the cross product: n = v1 cross v2. -inline void tetgenmesh::cross(REAL* v1, REAL* v2, REAL* n) +inline void tetgenmesh::cross(REAL* v1, REAL* v2, REAL* n) { n[0] = v1[1] * v2[2] - v2[1] * v1[2]; n[1] = -(v1[0] * v2[2] - v2[0] * v1[2]); @@ -3324,11 +3597,17 @@ inline REAL tetgenmesh::distance(REAL* p1, REAL* p2) (p2[2] - p1[2]) * (p2[2] - p1[2])); } +inline REAL tetgenmesh::distance2(REAL* p1, REAL* p2) +{ + return norm2(p2[0] - p1[0], p2[1] - p1[1], p2[2] - p1[2]); +} + inline REAL tetgenmesh::norm2(REAL x, REAL y, REAL z) { return (x) * (x) + (y) * (y) + (z) * (z); } + #endif // #ifndef tetgenH diff --git a/pygamer/CMakeLists.txt b/pygamer/CMakeLists.txt index ce86d255..3b63d1c6 100644 --- a/pygamer/CMakeLists.txt +++ b/pygamer/CMakeLists.txt @@ -19,6 +19,33 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # *************************************************************************** +include(FetchContent) + +########################### +# GET PYBIND11 +########################### +if(NOT GETPYBIND11) + find_package(pybind11 CONFIG REQUIRED) + if(NOT pybind11_FOUND) + message(FATAL_ERROR "Could not find required library pybind11." + "Please append -DGETPYBIND11=ON to your cmake call and I will download pybind11 for you.") + endif() +else() + FetchContent_Declare( + pybind11 + GIT_REPOSITORY https://github.com/pybind/pybind11.git + GIT_TAG v2.7.1 + GIT_SHALLOW TRUE + SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/pybind11-src" + BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/pybind11-build" + ) + FetchContent_GetProperties(pybind11) + if(NOT pybind11_POPULATED) + FetchContent_Populate(pybind11) + add_subdirectory(${pybind11_SOURCE_DIR} ${pybind11_BINARY_DIR}) + endif() +endif() + list(APPEND PYGAMER_SOURCES "src/Vector.cpp" @@ -46,36 +73,10 @@ pybind11_add_module(pygamer MODULE ${PYGAMER_SOURCES}) # Link pygamer to gamer static target_link_libraries(pygamer PRIVATE gamerstatic) - -execute_process(COMMAND "${PYTHON_EXECUTABLE}" "-c" - "from distutils import sysconfig as s;import sys;import struct; -print(s.get_python_lib(plat_specific=True)); -" - RESULT_VARIABLE _PYTHON_SUCCESS - OUTPUT_VARIABLE _PYTHON_VALUES - ERROR_VARIABLE _PYTHON_ERROR_VALUE) - -if(NOT _PYTHON_SUCCESS MATCHES 0) - message(FATAL_ERROR - "Python config failure:\n${_PYTHON_ERROR_VALUE}") -endif() - - -# Convert the process output into a list -string(REGEX REPLACE "\n" ";" _PYTHON_VALUES ${_PYTHON_VALUES}) -list(GET _PYTHON_VALUES 0 PYTHON_SITE_PACKAGES) - file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/__init__.py - INPUT ${CMAKE_SOURCE_DIR}/cmake-modules/__init__.py.in) + INPUT ${CMAKE_SOURCE_DIR}/cmake/__init__.py.in) if(SKBUILD) install(TARGETS pygamer LIBRARY DESTINATION pygamer) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/__init__.py DESTINATION pygamer) -else() - message(STATUS "CMake install no longer installs PyGAMer to your Python site-packages. " - "Please configure and install using 'python setup.py install' instead." - ) - # Otherwise put in PREFIX/lib if conventionally installed - # install(TARGETS pygamer LIBRARY DESTINATION "${PYTHON_SITE_PACKAGES}/pygamer") - # install(FILES ${CMAKE_CURRENT_BINARY_DIR}/__init__.py DESTINATION "${PYTHON_SITE_PACKAGES}/pygamer") -endif() \ No newline at end of file +endif() diff --git a/pygamer/src/SMEdge.cpp b/pygamer/src/SMEdge.cpp index 1dd36da4..4981517f 100644 --- a/pygamer/src/SMEdge.cpp +++ b/pygamer/src/SMEdge.cpp @@ -1,26 +1,21 @@ -/* - * *************************************************************************** - * This file is part of the GAMer software. - * Copyright (C) 2016-2019 - * by Christopher Lee, John Moody, Rommie Amaro, J. Andrew McCammon, - * and Michael Holst - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * *************************************************************************** - */ +// This file is part of the GAMer software. +// Copyright (C) 2016-2021 +// by Christopher T. Lee and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, see +// or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA #include #include @@ -46,4 +41,4 @@ void init_SMEdge(py::module& mod){ edge.def_readwrite("selected", &SMEdge::selected, "Selection status of edge."); } -} // end namespace gamer \ No newline at end of file +} // end namespace gamer diff --git a/pygamer/src/SMFace.cpp b/pygamer/src/SMFace.cpp index 2fe36bbe..fe0eecdc 100644 --- a/pygamer/src/SMFace.cpp +++ b/pygamer/src/SMFace.cpp @@ -1,26 +1,21 @@ -/* - * *************************************************************************** - * This file is part of the GAMer software. - * Copyright (C) 2016-2019 - * by Christopher Lee, John Moody, Rommie Amaro, J. Andrew McCammon, - * and Michael Holst - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * *************************************************************************** - */ +// This file is part of the GAMer software. +// Copyright (C) 2016-2021 +// by Christopher T. Lee and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, see +// or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA #include #include @@ -55,4 +50,4 @@ void init_SMFace(py::module& mod){ face.def("__repr__", &SMFace::to_string, "Pretty print"); } -} // end namespace gamer \ No newline at end of file +} // end namespace gamer diff --git a/pygamer/src/SMFunctions.cpp b/pygamer/src/SMFunctions.cpp index 35b2cc65..f725b340 100644 --- a/pygamer/src/SMFunctions.cpp +++ b/pygamer/src/SMFunctions.cpp @@ -1,26 +1,21 @@ -/* - * *************************************************************************** - * This file is part of the GAMer software. - * Copyright (C) 2016-2019 - * by Christopher Lee, John Moody, Rommie Amaro, J. Andrew McCammon, - * and Michael Holst - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * *************************************************************************** - */ +// This file is part of the GAMer software. +// Copyright (C) 2016-2021 +// by Christopher T. Lee and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, see +// or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA #include #include @@ -62,4 +57,4 @@ void init_SMFunctions(py::module& mod){ ); } -} // end namespace gamer \ No newline at end of file +} // end namespace gamer diff --git a/pygamer/src/SMGlobal.cpp b/pygamer/src/SMGlobal.cpp index bcefff1a..53a83268 100644 --- a/pygamer/src/SMGlobal.cpp +++ b/pygamer/src/SMGlobal.cpp @@ -1,26 +1,21 @@ -/* - * *************************************************************************** - * This file is part of the GAMer software. - * Copyright (C) 2016-2019 - * by Christopher Lee, John Moody, Rommie Amaro, J. Andrew McCammon, - * and Michael Holst - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * *************************************************************************** - */ +// This file is part of the GAMer software. +// Copyright (C) 2016-2021 +// by Christopher T. Lee and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, see +// or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA #include #include @@ -46,4 +41,4 @@ void init_SMGlobal(py::module& mod){ global.def_readwrite("ishole", &SMGlobal::ishole, "Does this domain represent a hole or not?"); } -} // end namespace gamer \ No newline at end of file +} // end namespace gamer diff --git a/pygamer/src/SMSimplexID.cpp b/pygamer/src/SMSimplexID.cpp index e6022eeb..60c927ce 100644 --- a/pygamer/src/SMSimplexID.cpp +++ b/pygamer/src/SMSimplexID.cpp @@ -1,26 +1,21 @@ -/* - * *************************************************************************** - * This file is part of the GAMer software. - * Copyright (C) 2016-2019 - * by Christopher Lee, John Moody, Rommie Amaro, J. Andrew McCammon, - * and Michael Holst - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * *************************************************************************** - */ +// This file is part of the GAMer software. +// Copyright (C) 2016-2021 +// by Christopher T. Lee and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, see +// or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA #include #include @@ -223,4 +218,4 @@ void init_SMSimplexID(py::module& mod){ ); } -} // end namespace gamer \ No newline at end of file +} // end namespace gamer diff --git a/pygamer/src/SMVertex.cpp b/pygamer/src/SMVertex.cpp index 93e638c3..4e0be115 100644 --- a/pygamer/src/SMVertex.cpp +++ b/pygamer/src/SMVertex.cpp @@ -1,26 +1,21 @@ -/* - * *************************************************************************** - * This file is part of the GAMer software. - * Copyright (C) 2016-2019 - * by Christopher Lee, John Moody, Rommie Amaro, J. Andrew McCammon, - * and Michael Holst - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * *************************************************************************** - */ +// This file is part of the GAMer software. +// Copyright (C) 2016-2021 +// by Christopher T. Lee and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, see +// or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA #include #include @@ -46,7 +41,7 @@ void init_SMVertex(py::module& mod){ py::arg("z") = 0, py::arg("marker") = -1, py::arg("selected") = false, - "Constructor defining doordinates, marker, and selection status." + "Constructor defining coordinates, marker, and selection status." ); @@ -71,4 +66,4 @@ void init_SMVertex(py::module& mod){ vertex.def("__repr__", &SMVertex::to_string); } -} // end namespace gamer \ No newline at end of file +} // end namespace gamer diff --git a/pygamer/src/SurfaceMesh.cpp b/pygamer/src/SurfaceMesh.cpp index 163a5e05..c6a0e6a9 100644 --- a/pygamer/src/SurfaceMesh.cpp +++ b/pygamer/src/SurfaceMesh.cpp @@ -1,26 +1,21 @@ -/* - * *************************************************************************** - * This file is part of the GAMer software. - * Copyright (C) 2016-2019 - * by Christopher Lee, John Moody, Rommie Amaro, J. Andrew McCammon, - * and Michael Holst - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * *************************************************************************** - */ +// This file is part of the GAMer software. +// Copyright (C) 2016-2021 +// by Christopher T. Lee and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, see +// or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA #include #include @@ -1033,6 +1028,14 @@ void init_SurfaceMesh(py::module& mod){ )delim" ); + SurfMeshCls.def("get_surface_area", py::overload_cast(&getArea), + R"delim( + Compute the surface area of the mesh. + + Returns: + :py:class:`float`: Surface area of the mesh. + )delim" + ); SurfMeshCls.def("fillHoles", &fillHoles, R"delim( @@ -1157,4 +1160,4 @@ void init_SurfaceMesh(py::module& mod){ ); } -} // end namespace gamer \ No newline at end of file +} // end namespace gamer diff --git a/pygamer/src/TMCell.cpp b/pygamer/src/TMCell.cpp index 0aa8137f..52ebbf7d 100644 --- a/pygamer/src/TMCell.cpp +++ b/pygamer/src/TMCell.cpp @@ -1,26 +1,21 @@ -/* - * *************************************************************************** - * This file is part of the GAMer software. - * Copyright (C) 2016-2019 - * by Christopher Lee, John Moody, Rommie Amaro, J. Andrew McCammon, - * and Michael Holst - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * *************************************************************************** - */ +// This file is part of the GAMer software. +// Copyright (C) 2016-2021 +// by Christopher T. Lee and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, see +// or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA #include #include @@ -49,4 +44,4 @@ void init_TMCell(py::module& mod){ cell.def("__repr__", &TMCell::to_string, "Pretty print"); } -} // end namespace gamer \ No newline at end of file +} // end namespace gamer diff --git a/pygamer/src/TMEdge.cpp b/pygamer/src/TMEdge.cpp index 195e5034..f0e863cc 100644 --- a/pygamer/src/TMEdge.cpp +++ b/pygamer/src/TMEdge.cpp @@ -1,26 +1,21 @@ -/* - * *************************************************************************** - * This file is part of the GAMer software. - * Copyright (C) 2016-2019 - * by Christopher Lee, John Moody, Rommie Amaro, J. Andrew McCammon, - * and Michael Holst - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * *************************************************************************** - */ +// This file is part of the GAMer software. +// Copyright (C) 2016-2021 +// by Christopher T. Lee and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, see +// or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA #include #include @@ -43,4 +38,4 @@ void init_TMEdge(py::module& mod){ edge.def_readwrite("selected", &TMEdge::selected, "Selection status of edge."); } -} // end namespace gamer \ No newline at end of file +} // end namespace gamer diff --git a/pygamer/src/TMFace.cpp b/pygamer/src/TMFace.cpp index 4985b9eb..09afefc1 100644 --- a/pygamer/src/TMFace.cpp +++ b/pygamer/src/TMFace.cpp @@ -1,26 +1,21 @@ -/* - * *************************************************************************** - * This file is part of the GAMer software. - * Copyright (C) 2016-2019 - * by Christopher Lee, John Moody, Rommie Amaro, J. Andrew McCammon, - * and Michael Holst - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * *************************************************************************** - */ +// This file is part of the GAMer software. +// Copyright (C) 2016-2021 +// by Christopher T. Lee and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, see +// or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA #include #include @@ -47,4 +42,4 @@ void init_TMFace(py::module& mod){ face.def("__repr__", &TMFace::to_string, "Pretty print"); } -} // end namespace gamer \ No newline at end of file +} // end namespace gamer diff --git a/pygamer/src/TMGlobal.cpp b/pygamer/src/TMGlobal.cpp index 2de7f449..5a33742e 100644 --- a/pygamer/src/TMGlobal.cpp +++ b/pygamer/src/TMGlobal.cpp @@ -1,26 +1,21 @@ -/* - * *************************************************************************** - * This file is part of the GAMer software. - * Copyright (C) 2016-2019 - * by Christopher Lee, John Moody, Rommie Amaro, J. Andrew McCammon, - * and Michael Holst - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * *************************************************************************** - */ +// This file is part of the GAMer software. +// Copyright (C) 2016-2021 +// by Christopher T. Lee and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, see +// or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA #include #include @@ -43,4 +38,4 @@ void init_TMGlobal(py::module& mod){ global.def_readwrite("higher_order", &TMGlobal::higher_order, "Whether or not this is a higher order mesh."); } -} // end namespace gamer \ No newline at end of file +} // end namespace gamer diff --git a/pygamer/src/TMSimplexID.cpp b/pygamer/src/TMSimplexID.cpp index 9d601549..13f9b65f 100644 --- a/pygamer/src/TMSimplexID.cpp +++ b/pygamer/src/TMSimplexID.cpp @@ -1,26 +1,21 @@ -/* - * *************************************************************************** - * This file is part of the GAMer software. - * Copyright (C) 2016-2019 - * by Christopher Lee, John Moody, Rommie Amaro, J. Andrew McCammon, - * and Michael Holst - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * *************************************************************************** - */ +// This file is part of the GAMer software. +// Copyright (C) 2016-2021 +// by Christopher T. Lee and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, see +// or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA #include #include @@ -260,4 +255,4 @@ void init_TMSimplexID(py::module& mod){ ); } -} // end namespace gamer \ No newline at end of file +} // end namespace gamer diff --git a/pygamer/src/TMVertex.cpp b/pygamer/src/TMVertex.cpp index af95d6a2..6bcde973 100644 --- a/pygamer/src/TMVertex.cpp +++ b/pygamer/src/TMVertex.cpp @@ -1,26 +1,21 @@ -/* - * *************************************************************************** - * This file is part of the GAMer software. - * Copyright (C) 2016-2019 - * by Christopher Lee, John Moody, Rommie Amaro, J. Andrew McCammon, - * and Michael Holst - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * *************************************************************************** - */ +// This file is part of the GAMer software. +// Copyright (C) 2016-2021 +// by Christopher T. Lee and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, see +// or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA #include #include @@ -72,4 +67,4 @@ void init_TMVertex(py::module& mod){ vertex.def("__repr__", &TMVertex::to_string); } -} // end namespace gamer \ No newline at end of file +} // end namespace gamer diff --git a/pygamer/src/TetMesh.cpp b/pygamer/src/TetMesh.cpp index fdd272df..c89491c0 100644 --- a/pygamer/src/TetMesh.cpp +++ b/pygamer/src/TetMesh.cpp @@ -1,26 +1,21 @@ -/* - * *************************************************************************** - * This file is part of the GAMer software. - * Copyright (C) 2016-2019 - * by Christopher Lee, John Moody, Rommie Amaro, J. Andrew McCammon, - * and Michael Holst - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * *************************************************************************** - */ +// This file is part of the GAMer software. +// Copyright (C) 2016-2021 +// by Christopher T. Lee and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, see +// or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA #include #include @@ -697,4 +692,4 @@ void init_TetMesh(py::module& mod){ ); } -} // end namespace gamer \ No newline at end of file +} // end namespace gamer diff --git a/pygamer/src/Vector.cpp b/pygamer/src/Vector.cpp index 02c68818..ff2282fb 100644 --- a/pygamer/src/Vector.cpp +++ b/pygamer/src/Vector.cpp @@ -1,3 +1,21 @@ +// This file is part of the GAMer software. +// Copyright (C) 2016-2021 +// by Christopher T. Lee and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, see +// or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA #include #include @@ -71,4 +89,4 @@ void init_Vector(py::module& mod){ ); } -} // end namespace gamer \ No newline at end of file +} // end namespace gamer diff --git a/pygamer/src/pygamer.cpp b/pygamer/src/pygamer.cpp index 963a76ef..445b1ea1 100644 --- a/pygamer/src/pygamer.cpp +++ b/pygamer/src/pygamer.cpp @@ -1,26 +1,21 @@ -/* - * *************************************************************************** - * This file is part of the GAMer software. - * Copyright (C) 2016-2019 - * by Christopher Lee, John Moody, Rommie Amaro, J. Andrew McCammon, - * and Michael Holst - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * *************************************************************************** - */ +// This file is part of the GAMer software. +// Copyright (C) 2016-2021 +// by Christopher T. Lee and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, see +// or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA #include #include @@ -293,6 +288,28 @@ PYBIND11_MODULE(pygamer, pygamer) { )delim" ); + pygamer.def("writeComsol", py::overload_cast&>(&writeComsol), + py::arg("filename"), py::arg("meshes"), + R"delim( + Write a list of surface meshes to comsol mphtxt format. + + Args: + filename (:py:class:`string`): Filename for output + meshes (:py:class:`list`(:py:class:`surfacemesh.SurfaceMesh`): List of meshes with filled metadata + )delim" + ); + + pygamer.def("writeComsol", py::overload_cast(&writeComsol), + py::arg("filename"), py::arg("mesh"), + R"delim( + Write mesh to file in Comsol mphtxt format + + Args: + filename (:py:class:`str`): Filename to write to + mesh (:py:class:`tetmesh.TetMesh`): Mesh of interest + )delim" + ); + pygamer.def("__version__", [](){ extern const std::string gVERSION; @@ -320,4 +337,4 @@ PYBIND11_MODULE(pygamer, pygamer) { ); } -} // end namespace gamer \ No newline at end of file +} // end namespace gamer diff --git a/requirements.txt b/requirements.txt index c0dc3b7a..72816a8a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,6 @@ # Required for building cmake >= 3.10 -scikit-build >= 0.10.0 -setuptools -wheel +scikit-build numpy # Recommended @@ -10,12 +8,13 @@ ninja pytest # For building documentation -sphinx>=2.0 -breathe>=4.13.0 -exhale -pandoc +sphinx>=3.0 +breathe>=4.30.0 +exhale>=0.2.3 +# pandoc sphinx-issues nbsphinx jupyter_sphinx + sphinx-rtd-theme -threevis \ No newline at end of file +threevis diff --git a/setup.cfg b/setup.cfg index dfb9b685..d195f74c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -4,4 +4,4 @@ test=pytest [tool:pytest] addopts = --verbose python_files = tests/* -norecursedirs = build cmake-modules docs examples include libraries src python .git \ No newline at end of file +norecursedirs = build cmake docs examples include libraries src python .git diff --git a/setup.py b/setup.py index 34360d15..64e282a9 100644 --- a/setup.py +++ b/setup.py @@ -27,47 +27,104 @@ import subprocess import re -# Return the git revision as a string def git_version(): + """Get the version from git describe + + Returns: + string: version string or None if invalid + """ def _minimal_ext_cmd(cmd): # construct minimal environment env = {} - for k in ['SYSTEMROOT', 'PATH', 'HOME']: + for k in ["SYSTEMROOT", "PATH", "HOME"]: v = os.environ.get(k) if v is not None: env[k] = v # LANGUAGE is used on win32 - env['LANGUAGE'] = 'C' - env['LANG'] = 'C' - env['LC_ALL'] = 'C' + env["LANGUAGE"] = "C" + env["LANG"] = "C" + env["LC_ALL"] = "C" out = subprocess.check_output(cmd, stderr=subprocess.STDOUT, env=env) return out try: - out = _minimal_ext_cmd(['git', 'describe', '--tags', '--dirty=.dirty', '--always']) - GIT_REVISION = out.strip().decode('ascii') + out = _minimal_ext_cmd(["git", "describe", "--tags", "--dirty", "--always"]) + GIT_REVISION = out.strip().decode("ascii") except (subprocess.SubprocessError, OSError): - GIT_REVISION = "Unknown" + GIT_REVISION = None return GIT_REVISION -version = git_version() -if not version == "Unknown": - match = re.search('v(\d+)\.(\d+)\.(\d+)-*(alpha|beta|dev|)-*([A-Za-z0-9_-]*)\.*(dirty|)', version) +def standardize_version(version_string): + """Standardize the version string + + Args: + version_string (str): input version string + Returns: + str: standardized version + """ + VERSION_PATTERN = r""" + v? + (?: + (?:(?P[0-9]+)!)? # epoch + (?P[0-9]+(?:\.[0-9]+)*) # release segment + (?P
                                          # pre-release
+                [-_\.]?
+                (?P(alpha|beta|a|b|c|rc))
+                (?P[0-9]+)?
+            )?
+            (?P                                          # dev release
+                [-_\.]?
+                (?Pdev)
+                [-_\.]?
+                (?P[0-9]+)?
+            )?
+            (?P
+                [-_\.]?
+                (?P[0-9]+)?
+                [-_\.]?
+                (?P[a-z0-9]*)?
+                [-_\.]? 
+                (?Pdirty)?
+            )?
+        )
+    """
+
+    _regex = re.compile(
+        r"^\s*" + VERSION_PATTERN + r"\s*$",
+        re.VERBOSE | re.IGNORECASE,
+    )
+
+    match = _regex.match(version_string)
     if match:
-        version = "%s.%s.%s"%(match.group(1), match.group(2), match.group(3))
+        if match.group("release"):
+            version = match.group("release")
+            if match.group("pre_l"):
+                version += match.group("pre_l")
+                if match.group("pre_n"):
+                    version += match.group("pre_n")
+                else:
+                    version += "0"
+            if match.group("dev"):
+                version += "dev"
+                if match.group("dev_n"):
+                    version += match.group("dev_n")
+                else:
+                    version += "0"
     else:
         version = "0.0.0"
+    return version
+
+version = git_version()
+if version is None:
+    # Git describe failed... read version from file
+    with open("VERSION", "r") as f:
+        version = f.readline()
+    version = standardize_version(version)
 else:
-    with open('VERSION', 'r') as f:
-       version = f.readline()
-    match = re.search('(\d+)\.(\d+)\.(\d+)-*(alpha|beta|dev|)', version)
-    if match:
-        version = "%s.%s.%s"%(match.group(1), match.group(2), match.group(3))
-    else:
-        version = "0.0.0"
+    version = standardize_version(version)
 
-cmake_args=['-DBUILD_PYGAMER=ON']
+cmake_args = ["-DBUILD_PYGAMER=ON"]
 
 DOCLINES = __doc__.split("\n")
 
@@ -86,50 +143,64 @@ def _minimal_ext_cmd(cmd):
 Topic :: Scientific/Engineering :: Mathematics
 Topic :: Scientific/Engineering :: Physics
 Topic :: Scientific/Engineering :: Visualization
-
 """
 
 # If building on readthedocs.io build with unix makefiles
-_on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
-if(_on_rtd):
-    sys.argv.extend(['-G','Unix Makefiles'])
-    cmake_args.append('-DGAMER_DOCS=ON')
+_on_rtd = os.environ.get("READTHEDOCS", None) == "True"
+if _on_rtd:
+    sys.argv.extend(["-G", "Unix Makefiles"])
+    cmake_args.append("-DGAMER_DOCS=ON")
 
 try:
     from skbuild import setup
 except ImportError:
-    print('\nERROR: scikit-build is required to build from source.', file=sys.stderr)
-    print('Please run:', file=sys.stderr)
-    print('', file=sys.stderr)
-    print('  python -m pip install scikit-build\n', file=sys.stderr)
+    print("\nERROR: scikit-build is required to build from source.", file=sys.stderr)
+    print("Please run:", file=sys.stderr)
+    print("", file=sys.stderr)
+    print("  python -m pip install scikit-build\n", file=sys.stderr)
+    print("  -- or --\n", file=sys.stderr)
+    print("  conda install scikit-build", file=sys.stderr)
     sys.exit(1)
 
-docs_require = ["sphinx", "breathe", "exhale", "sphinx-issues", "nbsphinx",
-"jupyter_sphinx", "threevis"]
+docs_require = [
+    "sphinx",
+    "breathe",
+    "exhale",
+    "sphinx-issues",
+    "nbsphinx",
+    "jupyter_sphinx",
+    "threevis",
+]
 tests_require = ["pytest"]
 
 setup(
-    name='pygamer',
+    name="pygamer",
     version=version,
-    maintainer='Christopher T. Lee',
-    maintainer_email='ctlee@ucsd.edu',
-    author='The GAMer Team',
-    author_email='ctlee@ucsd.edu',
-    url='https://github.com/ctlee/gamer',
-    license='LGPLv2+',
+    maintainer="Christopher T. Lee",
+    maintainer_email="ctlee@ucsd.edu",
+    author="The GAMer Team",
+    author_email="ctlee@ucsd.edu",
+    url="https://github.com/ctlee/gamer",
+    license="LGPLv2+",
     packages=["pygamer"],
     description=DOCLINES[0],
-    long_description=open('README.md', encoding='utf8').read(),
+    long_description=open("README.md", encoding="utf8").read(),
     long_description_content_type="text/markdown",
     platforms=["Windows", "Linux", "Mac OS-X", "Unix"],
-    classifiers=[c for c in CLASSIFIERS.split('\n') if c],
-    keywords='meshing ',
+    classifiers=[c for c in CLASSIFIERS.split("\n") if c],
+    keywords="meshing ",
     cmake_args=cmake_args,
-    setup_requires=["setuptools", "wheel", "scikit-build >= 0.10.0", "pytest-runner", "cmake >= 3.11"],
+    setup_requires=[
+        "setuptools",
+        "wheel",
+        "scikit-build >= 0.10.0",
+        "pytest-runner",
+        "cmake >= 3.11",
+    ],
     install_requires=["numpy>=1.8.0"],
     extras_require={
-        "docs" : docs_require,
-        "test" : tests_require,
+        "docs": docs_require,
+        "test": tests_require,
     },
     zip_safe=False,
 )
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 00000000..0a1386e8
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,34 @@
+# ***************************************************************************
+# This file is part of the GAMer software.
+# Copyright (C) 2016-2021
+# by Christopher T. Lee and contributors
+
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, see 
+# or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+# Boston, MA 02111-1307 USA
+# ***************************************************************************
+
+set(GAMER_SOURCES
+    "${CMAKE_CURRENT_SOURCE_DIR}/CurvatureCalcs.cpp"
+    "${CMAKE_CURRENT_SOURCE_DIR}/OBJ_SurfaceMesh.cpp"
+    "${CMAKE_CURRENT_SOURCE_DIR}/OFF_SurfaceMesh.cpp"
+    "${CMAKE_CURRENT_SOURCE_DIR}/PDBReader.cpp"
+    "${CMAKE_CURRENT_SOURCE_DIR}/SurfaceMesh.cpp"
+    "${CMAKE_CURRENT_SOURCE_DIR}/SurfaceMeshDetail.cpp"
+    "${CMAKE_CURRENT_SOURCE_DIR}/TetMesh.cpp"
+    "${CMAKE_CURRENT_SOURCE_DIR}/Vertex.cpp"
+    "${CMAKE_CURRENT_SOURCE_DIR}/comsol_io.cpp"
+    "${CMAKE_CURRENT_SOURCE_DIR}/pdb2mesh.cpp"
+PARENT_SCOPE
+)
diff --git a/src/CurvatureCalcs.cpp b/src/CurvatureCalcs.cpp
index 6ee5ca50..6588dc36 100644
--- a/src/CurvatureCalcs.cpp
+++ b/src/CurvatureCalcs.cpp
@@ -1,31 +1,25 @@
-/*
- * ***************************************************************************
- * This file is part of the GAMer software.
- * Copyright (C) 2016-2018
- * by Christopher Lee, John Moody, Rommie Amaro, J. Andrew McCammon,
- *    and Michael Holst
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * ***************************************************************************
- */
+// This file is part of the GAMer software.
+// Copyright (C) 2016-2021
+// by Christopher T. Lee and contributors
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, see 
+// or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+// Boston, MA 02111-1307 USA
 
 #define _USE_MATH_DEFINES
-#include 
-#include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -39,165 +33,170 @@
 #include "gamer/SurfaceMesh.h"
 
 /// Namespace for all things gamer
-namespace gamer
-{
-std::tuple>
-curvatureViaMDSB(const SurfaceMesh& mesh){
-    std::map sigma;
-
-    REAL *Amix = new REAL[mesh.size<1>()];
-    REAL *kg = new REAL[mesh.size<1>()];
-    REAL *kh = new REAL[mesh.size<1>()];
-    REAL *k1 = new REAL[mesh.size<1>()];
-    REAL *k2 = new REAL[mesh.size<1>()];
-    Vector *Kh = new Vector[mesh.size<1>()];
-    Vector *normals = new Vector[mesh.size<1>()];
-
-    // Map VertexIDs to indices
-    std::size_t i = 0;
-    for (const auto vertexID : mesh.get_level_id<1>()) {
-        Amix[i] = 0;
-        kg[i] = 0;
-        kh[i] = 0;
-        k1[i] = 0;
-        k2[i] = 0;
-        normals[i] = getNormal(mesh, vertexID);
-        sigma[vertexID.indices()[0]] = i++;
-    }
-
-    for (const auto faceID : mesh.get_level_id<3>()) {
-        auto indices = faceID.indices(); // Face vertex indices
-        std::array vertices;  // Vertex data for indices
-        std::array keysDown; // Tmp to store
-                                                               // keys
-
-        // Fill vertices in order corresponding to indices
-        for (std::size_t skip = 0; skip < 3; ++skip) {
-            std::size_t j = 0;
-            std::size_t k = 0;
-            while (k < 2) {
-                if (j == skip) ++j;
-                else{
-                    keysDown[k++] = indices[j++];
-                }
-            }
-            vertices[skip] = *mesh.get_simplex_down(faceID, keysDown);
+namespace gamer {
+std::tuple<
+    REAL *, REAL *, REAL *, REAL *,
+    std::map>
+curvatureViaMDSB(const SurfaceMesh &mesh) {
+  std::map sigma;
+
+  REAL *Amix = new REAL[mesh.size<1>()];
+  REAL *kg = new REAL[mesh.size<1>()];
+  REAL *kh = new REAL[mesh.size<1>()];
+  REAL *k1 = new REAL[mesh.size<1>()];
+  REAL *k2 = new REAL[mesh.size<1>()];
+  Vector *Kh = new Vector[mesh.size<1>()];
+  Vector *normals = new Vector[mesh.size<1>()];
+
+  // Map VertexIDs to indices
+  std::size_t i = 0;
+  for (const auto vertexID : mesh.get_level_id<1>()) {
+    Amix[i] = 0;
+    kg[i] = 0;
+    kh[i] = 0;
+    k1[i] = 0;
+    k2[i] = 0;
+    normals[i] = getNormal(mesh, vertexID);
+    sigma[vertexID.indices()[0]] = i++;
+  }
+
+  for (const auto faceID : mesh.get_level_id<3>()) {
+    auto indices = faceID.indices(); // Face vertex indices
+    std::array vertices;  // Vertex data for indices
+    std::array keysDown; // Tmp to store
+                                                           // keys
+
+    // Fill vertices in order corresponding to indices
+    for (std::size_t skip = 0; skip < 3; ++skip) {
+      std::size_t j = 0;
+      std::size_t k = 0;
+      while (k < 2) {
+        if (j == skip)
+          ++j;
+        else {
+          keysDown[k++] = indices[j++];
         }
+      }
+      vertices[skip] = *mesh.get_simplex_down(faceID, keysDown);
+    }
 
-        // TODO: (15) This section computes the same distances a bunch of time
-        std::array dist;
-        dist[0] = distance(vertices[0], vertices[1]);
-        dist[1] = distance(vertices[1], vertices[2]);
-        dist[2] = distance(vertices[0], vertices[2]);
-        std::sort(dist.begin(), dist.end());
-
-        // Check if the triangle is obtuse...
-        bool obtuse = dist[0]*dist[0] + dist[1]*dist[1] < dist[2]*dist[2];
-
-        REAL t_area = 0; // Area of the face
-        // Populate t_area if obtuse
-        if (obtuse) t_area = getArea(vertices[0], vertices[1], vertices[2]);
-
-        // List of indices to rotate
-        std::array idxmap = {0, 1, 2};
-        for (std::size_t i = 0; i < 3; ++i) {
-            // idxmap[0] is the current vertex
-            REAL ang = angle(vertices[idxmap[2]], vertices[idxmap[0]], vertices[idxmap[1]]);
-
-            std::size_t i0 = sigma[indices[idxmap[0]]];
-            std::size_t i1 = sigma[indices[idxmap[1]]];
-            std::size_t i2 = sigma[indices[idxmap[2]]];
-
-            // Add angle to Gaussian Curvature
-            kg[i0] += ang;
-
-            // Vectors of other edges
-            Vector v1 = vertices[idxmap[1]] - vertices[idxmap[2]];
-            Vector v2 = vertices[idxmap[2]] - vertices[idxmap[1]];
-
-            REAL cot = 1.0/tan(ang);
-
-            if (obtuse) {
-                if (ang > M_PI/2.0) {
-                    Amix[i0] += t_area/2.0;
-                }
-                else{
-                    Amix[i0] += t_area/4.0;
-                }
-            }
-            else{
-                REAL tmp = cot/8.0;
-                REAL lenSq = length(v1);
-                lenSq *= lenSq;
-                Amix[i1] += tmp*lenSq;
-                Amix[i2] += tmp*lenSq;
-            }
-
-            // Add value to Mean Curvature
-            Kh[i1] += cot*v1;
-            Kh[i2] += cot*v2;
-
-            std::rotate(idxmap.begin(), idxmap.begin() + 1, idxmap.end());
+    // TODO: (15) This section computes the same distances a bunch of time
+    std::array dist;
+    dist[0] = distance(vertices[0], vertices[1]);
+    dist[1] = distance(vertices[1], vertices[2]);
+    dist[2] = distance(vertices[0], vertices[2]);
+    std::sort(dist.begin(), dist.end());
+
+    // Check if the triangle is obtuse...
+    bool obtuse = dist[0] * dist[0] + dist[1] * dist[1] < dist[2] * dist[2];
+
+    REAL t_area = 0; // Area of the face
+    // Populate t_area if obtuse
+    if (obtuse)
+      t_area = getArea(vertices[0], vertices[1], vertices[2]);
+
+    // List of indices to rotate
+    std::array idxmap = {0, 1, 2};
+    for (std::size_t i = 0; i < 3; ++i) {
+      // idxmap[0] is the current vertex
+      REAL ang =
+          angle(vertices[idxmap[2]], vertices[idxmap[0]], vertices[idxmap[1]]);
+
+      std::size_t i0 = sigma[indices[idxmap[0]]];
+      std::size_t i1 = sigma[indices[idxmap[1]]];
+      std::size_t i2 = sigma[indices[idxmap[2]]];
+
+      // Add angle to Gaussian Curvature
+      kg[i0] += ang;
+
+      // Vectors of other edges
+      Vector v1 = vertices[idxmap[1]] - vertices[idxmap[2]];
+      Vector v2 = vertices[idxmap[2]] - vertices[idxmap[1]];
+
+      REAL cot = 1.0 / tan(ang);
+
+      if (obtuse) {
+        if (ang > M_PI / 2.0) {
+          Amix[i0] += t_area / 2.0;
+        } else {
+          Amix[i0] += t_area / 4.0;
         }
+      } else {
+        REAL tmp = cot / 8.0;
+        REAL lenSq = length(v1);
+        lenSq *= lenSq;
+        Amix[i1] += tmp * lenSq;
+        Amix[i2] += tmp * lenSq;
+      }
+
+      // Add value to Mean Curvature
+      Kh[i1] += cot * v1;
+      Kh[i2] += cot * v2;
+
+      std::rotate(idxmap.begin(), idxmap.begin() + 1, idxmap.end());
     }
+  }
 
-    for (std::size_t i = 0; i < mesh.size<1>(); ++i) {
-        Kh[i] = Kh[i]/(2.0*Amix[i]);
-        kh[i] = std::copysign(length(Kh[i])/2.0, -dot(Kh[i], normals[i]));
-        kg[i] = (2.0*M_PI - kg[i])/Amix[i];
+  for (std::size_t i = 0; i < mesh.size<1>(); ++i) {
+    Kh[i] = Kh[i] / (2.0 * Amix[i]);
+    kh[i] = std::copysign(length(Kh[i]) / 2.0, -dot(Kh[i], normals[i]));
+    kg[i] = (2.0 * M_PI - kg[i]) / Amix[i];
 
-        REAL kh2 = kh[i]*kh[i];
-        REAL tmp = kh2 < kg[i] ? 0 : std::sqrt(kh2-kg[i]);
+    REAL kh2 = kh[i] * kh[i];
+    REAL tmp = kh2 < kg[i] ? 0 : std::sqrt(kh2 - kg[i]);
 
-        k1[i] = kh[i] + tmp;
-        k2[i] = kh[i] - tmp;
-    }
+    k1[i] = kh[i] + tmp;
+    k2[i] = kh[i] - tmp;
+  }
 
-    delete[] Amix;
-    delete[] Kh;
-    delete[] normals;
-    return std::make_tuple(kh, kg, k1, k2, sigma);
+  delete[] Amix;
+  delete[] Kh;
+  delete[] normals;
+  return std::make_tuple(kh, kg, k1, k2, sigma);
 }
 
+std::tuple<
+    REAL *, REAL *, REAL *, REAL *,
+    std::map>
+curvatureViaJets(const SurfaceMesh &mesh, std::size_t dJet,
+                 std::size_t dPrime) {
+  std::map sigma;
+
+  REAL *kg = new REAL[mesh.size<1>()];
+  REAL *kh = new REAL[mesh.size<1>()];
+  REAL *k1 = new REAL[mesh.size<1>()];
+  REAL *k2 = new REAL[mesh.size<1>()];
+
+  int min_nb_points = (dJet + 1) * (dJet + 2) / 2;
+
+  // Map VertexIDs to indices
+  std::size_t i = 0;
+  for (const auto vertexID : mesh.get_level_id<1>()) {
+    std::vector> nbors;
+    nbors.push_back(vertexID);
+    surfacemesh_detail::vertexGrabber(mesh, min_nb_points - 1, nbors, vertexID);
+
+    if (nbors.size() < min_nb_points) {
+      std::cerr << "Not enough pts (have: " << nbors.size()
+                << ", need: " << min_nb_points
+                << ") for fitting this vertex: " << vertexID << std::endl;
+      continue;
+    }
 
-std::tuple>
-curvatureViaJets(const SurfaceMesh& mesh, std::size_t dJet, std::size_t dPrime){
-    std::map sigma;
-
-    REAL *kg = new REAL[mesh.size<1>()];
-    REAL *kh = new REAL[mesh.size<1>()];
-    REAL *k1 = new REAL[mesh.size<1>()];
-    REAL *k2 = new REAL[mesh.size<1>()];
-
-    int min_nb_points = (dJet + 1) * (dJet + 2) / 2;
-
-    // Map VertexIDs to indices
-    std::size_t i = 0;
-    for (const auto vertexID : mesh.get_level_id<1>()) {
-        std::vector> nbors;
-        nbors.push_back(vertexID);
-        surfacemesh_detail::vertexGrabber(mesh, min_nb_points-1, nbors, vertexID);
-
-        if (nbors.size() < min_nb_points) {
-            std::cerr << "Not enough pts (have: " << nbors.size() << ", need: "      << min_nb_points << ") for fitting this vertex: "
-                      << vertexID << std::endl;
-            continue;
-        }
-
-        Monge_via_jet_fitting monge_fit;
-        auto mongeForm = monge_fit(nbors.begin(), nbors.end(), dJet, dPrime);
-        mongeForm.comply_wrt_given_normal(getNormal(mesh, vertexID));
+    Monge_via_jet_fitting monge_fit;
+    auto mongeForm = monge_fit(nbors.begin(), nbors.end(), dJet, dPrime);
+    mongeForm.comply_wrt_given_normal(getNormal(mesh, vertexID));
 
-        REAL tk1 = mongeForm.principal_curvatures(0);
-        REAL tk2 = mongeForm.principal_curvatures(1);
+    REAL tk1 = mongeForm.principal_curvatures(0);
+    REAL tk2 = mongeForm.principal_curvatures(1);
 
-        k1[i] = tk1;
-        k2[i] = tk2;
+    k1[i] = tk1;
+    k2[i] = tk2;
 
-        kg[i] = tk1*tk2;
-        kh[i] = (tk1+tk2)/2.;
-        sigma[vertexID.indices()[0]] = i++;
-    }
-    return std::make_tuple(kh, kg, k1, k2, sigma);
+    kg[i] = tk1 * tk2;
+    kh[i] = (tk1 + tk2) / 2.;
+    sigma[vertexID.indices()[0]] = i++;
+  }
+  return std::make_tuple(kh, kg, k1, k2, sigma);
 }
-}
\ No newline at end of file
+} // namespace gamer
diff --git a/src/OBJ_SurfaceMesh.cpp b/src/OBJ_SurfaceMesh.cpp
index 27001511..f161624d 100644
--- a/src/OBJ_SurfaceMesh.cpp
+++ b/src/OBJ_SurfaceMesh.cpp
@@ -1,176 +1,145 @@
-/*
- * ***************************************************************************
- * This file is part of the GAMer software.
- * Copyright (C) 2016-2018
- * by Christopher Lee, John Moody, Rommie Amaro, J. Andrew McCammon,
- *    and Michael Holst
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * ***************************************************************************
- */
-
-
+// This file is part of the GAMer software.
+// Copyright (C) 2016-2021
+// by Christopher T. Lee and contributors
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, see 
+// or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+// Boston, MA 02111-1307 USA
 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
-#include 
 #include 
 
-#include "gamer/stringutil.h"
 #include "gamer/SurfaceMesh.h"
+#include "gamer/stringutil.h"
 
 /// Namespace for all things gamer
-namespace gamer
-{
-//https://en.wikipedia.org/wiki/Wavefront_.obj_file
-//http://paulbourke.net/dataformats/obj/
-std::unique_ptr readOBJ(const std::string &filename)
-{
-    // Instantiate mesh!
-    std::unique_ptr mesh(new SurfaceMesh);
-
-    std::ifstream                fin(filename);
-    if (!fin.is_open())
-    {
-        std::cerr << "Read Error: File '" << filename << "' could not be read." << std::endl;
-        return mesh;
-    }
+namespace gamer {
+// https://en.wikipedia.org/wiki/Wavefront_.obj_file
+// http://paulbourke.net/dataformats/obj/
+std::unique_ptr readOBJ(const std::string &filename) {
+  // Instantiate mesh!
+  std::unique_ptr mesh(new SurfaceMesh);
+
+  std::ifstream fin(filename);
+  if (!fin.is_open()) {
+    std::cerr << "Read Error: File '" << filename << "' could not be read."
+              << std::endl;
+    return mesh;
+  }
 
-    int                      i = 0; // index of vertices
-    std::string              line;
-    std::vector arr;
+  int i = 0; // index of vertices
+  std::string line;
+  std::vector arr;
 
-    // while the file isn't empty
-    while (fin.peek() != -1)
-    {
-        getline(fin, line);
-        stringutil::trim(line);
-
-        if (line.empty())
-            continue;       // skip empty lines
-        if (line[0] == '#')
-            continue;       // skip comments
-
-        if (line[0] == 'v') // Vertex
-        {
-            // if not space see what kind of data it is
-            if (!std::isspace(line[1]))
-            {
-                // ignore normals "vn" and textures "vt"
-                // also ignore potentially diabolical filenames
-                continue;
-            }
-            else
-            {
-                // List of geometric vertices, with (x,y,z[,w]) coordinates, w
-                // is optional and defaults to 1.0.
-                arr = stringutil::split(line, {' '});
-                SMVertex v = SMVertex(
-                    std::stod(arr[1]),
-                    std::stod(arr[2]),
-                    std::stod(arr[3])
-                    );
-                // ignore possible w for now...
-                mesh->insert<1>({++i}, v);
-            }
-        }
-
-        // Faces can be a pain also arbitrary dimension
-        // f v1 v2 v3 ....
-        // f v1/vt1 v2/vt2 v3/vt3 ...
-        // f v1/vt1/vn1 v2/vt2/vn2 v3/vt3/vn3 ...
-        // f v1//vn1 v2//vn2 v3//vn3 ...
-        if (line[0] == 'f')
-        {
-            arr = stringutil::split(line, {' '});
-            if (arr.size() != 4)
-            {
-                std::cerr << "Unsupported: Found face that is not a triangle!" << std::endl;
-                mesh.reset();
-                return mesh;
-            }
-            // again we're going to ignore textures and normals
-            for (auto it = arr.begin(); it != arr.end(); ++it)
-            {
-                *it = stringutil::split(*it, {'/'})[0];
-            }
-            mesh->insert<3>({
-                    std::stoi(arr[1]),
-                    std::stoi(arr[2]),
-                    std::stoi(arr[3])});
-        }
-
-        // everything else is ignored for now also
-    }
-    return mesh;
-}
+  // while the file isn't empty
+  while (fin.peek() != -1) {
+    getline(fin, line);
+    stringutil::trim(line);
 
-void writeOBJ(const std::string &filename, const SurfaceMesh &mesh)
-{
-    std::ofstream fout(filename);
-    if (!fout.is_open())
-    {
-        std::cerr << "File '" << filename
-                  << "' could not be writen to." << std::endl;
-        exit(1);
-    }
+    if (line.empty())
+      continue; // skip empty lines
+    if (line[0] == '#')
+      continue; // skip comments
 
-    std::map sigma;
-    typename SurfaceMesh::KeyType cnt = 1;
-    for (const auto &x : mesh.get_level_id<1>())
+    if (line[0] == 'v') // Vertex
     {
-        sigma[mesh.get_name(x)[0]] = cnt++;
+      // if not space see what kind of data it is
+      if (!std::isspace(line[1])) {
+        // ignore normals "vn" and textures "vt"
+        // also ignore potentially diabolical filenames
+        continue;
+      } else {
+        // List of geometric vertices, with (x,y,z[,w]) coordinates, w
+        // is optional and defaults to 1.0.
+        arr = stringutil::split(line, {' '});
+        SMVertex v =
+            SMVertex(std::stod(arr[1]), std::stod(arr[2]), std::stod(arr[3]));
+        // ignore possible w for now...
+        mesh->insert<1>({++i}, v);
+      }
     }
 
-    fout.precision(10);
-    // Get the vertex data directly
-    for (const auto &vertex : mesh.get_level<1>())
-    {
-        fout << "v "
-             << vertex[0] << " "
-             << vertex[1] << " "
-             << vertex[2] << " "
-             << "\n";
+    // Faces can be a pain also arbitrary dimension
+    // f v1 v2 v3 ....
+    // f v1/vt1 v2/vt2 v3/vt3 ...
+    // f v1/vt1/vn1 v2/vt2/vn2 v3/vt3/vn3 ...
+    // f v1//vn1 v2//vn2 v3//vn3 ...
+    if (line[0] == 'f') {
+      arr = stringutil::split(line, {' '});
+      if (arr.size() != 4) {
+        std::cerr << "Unsupported: Found face that is not a triangle!"
+                  << std::endl;
+        mesh.reset();
+        return mesh;
+      }
+      // again we're going to ignore textures and normals
+      for (auto it = arr.begin(); it != arr.end(); ++it) {
+        *it = stringutil::split(*it, {'/'})[0];
+      }
+      mesh->insert<3>(
+          {std::stoi(arr[1]), std::stoi(arr[2]), std::stoi(arr[3])});
     }
 
-    // Get the face nodes
-    for (auto faceNodeID : mesh.get_level_id<3>())
-    {
-        auto w = mesh.get_name(faceNodeID);
-
-        auto orientation = (*faceNodeID).orientation;
-        if (orientation == 1)
-        {
-            fout << "f " << sigma[w[2]] << " " << sigma[w[1]] << " " << sigma[w[0]] << "\n";
-        }
-        else if (orientation == -1)
-        {
-            fout << "f " << sigma[w[0]] << " " << sigma[w[1]] << " " << sigma[w[2]] << "\n";
-
-        }
-        else
-        {
-            std::cerr << "Warning: Orientation undefined..." << std::endl;
-            fout << "f " << sigma[w[0]] << " " << sigma[w[1]] << " " << sigma[w[2]] << "\n";
-        }
-    }
-    fout.close();
+    // everything else is ignored for now also
+  }
+  return mesh;
+}
 
+void writeOBJ(const std::string &filename, const SurfaceMesh &mesh) {
+  std::ofstream fout(filename);
+  if (!fout.is_open()) {
+    std::cerr << "File '" << filename << "' could not be writen to."
+              << std::endl;
+    exit(1);
+  }
+
+  std::map sigma;
+  typename SurfaceMesh::KeyType cnt = 1;
+  for (const auto &x : mesh.get_level_id<1>()) {
+    sigma[mesh.get_name(x)[0]] = cnt++;
+  }
+
+  fout.precision(10);
+  // Get the vertex data directly
+  for (const auto &vertex : mesh.get_level<1>()) {
+    fout << "v " << vertex[0] << " " << vertex[1] << " " << vertex[2] << " "
+         << "\n";
+  }
+
+  // Get the face nodes
+  for (auto faceNodeID : mesh.get_level_id<3>()) {
+    auto w = mesh.get_name(faceNodeID);
+
+    auto orientation = (*faceNodeID).orientation;
+    if (orientation == 1) {
+      fout << "f " << sigma[w[2]] << " " << sigma[w[1]] << " " << sigma[w[0]]
+           << "\n";
+    } else if (orientation == -1) {
+      fout << "f " << sigma[w[0]] << " " << sigma[w[1]] << " " << sigma[w[2]]
+           << "\n";
+
+    } else {
+      std::cerr << "Warning: Orientation undefined..." << std::endl;
+      fout << "f " << sigma[w[0]] << " " << sigma[w[1]] << " " << sigma[w[2]]
+           << "\n";
+    }
+  }
+  fout.close();
 }
 } // end namespace gamer
diff --git a/src/OFF_SurfaceMesh.cpp b/src/OFF_SurfaceMesh.cpp
index eb2c52ed..0a7f2426 100644
--- a/src/OFF_SurfaceMesh.cpp
+++ b/src/OFF_SurfaceMesh.cpp
@@ -1,41 +1,34 @@
-/*
- * ***************************************************************************
- * This file is part of the GAMer software.
- * Copyright (C) 2016-2018
- * by Christopher Lee, John Moody, Rommie Amaro, J. Andrew McCammon,
- *    and Michael Holst
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * ***************************************************************************
- */
-
+// This file is part of the GAMer software.
+// Copyright (C) 2016-2021
+// by Christopher T. Lee and contributors
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, see 
+// or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+// Boston, MA 02111-1307 USA
 
 #include "gamer/SurfaceMesh.h"
-#include 
-#include 
+#include 
 #include 
+#include 
 #include 
 #include 
 #include 
-#include 
+#include 
 #include 
 
 /// Namespace for all things gamer
-namespace gamer
-{
+namespace gamer {
 /**
  * @brief      Converts the color from float(0-1) to a marker value
  *
@@ -45,276 +38,251 @@ namespace gamer
  *
  * @return     The value of the marker.
  */
-int get_marker(float r, float g, float b)
-{
-    if (r < 0 || r > 1 || g < 0 || g > 1 || b < 0 || b > 1)
-    {
-        std::cerr << "Expected individual RGB value to be betwen 0 and 1." << std::endl;
-        exit(1);
-    }
-    return static_cast(round(r * 10) * 121 + round(g * 10) * 11 + round(b * 10));
+int get_marker(float r, float g, float b) {
+  if (r < 0 || r > 1 || g < 0 || g > 1 || b < 0 || b > 1) {
+    std::cerr << "Expected individual RGB value to be betwen 0 and 1."
+              << std::endl;
+    exit(1);
+  }
+  return static_cast(round(r * 10) * 121 + round(g * 10) * 11 +
+                          round(b * 10));
 }
 
-//http://www.geomview.org/docs/html/OFF.html
-std::unique_ptr readOFF(const std::string &filename)
-{
-    // Instantiate mesh!
-    //auto mesh = new SurfaceMesh();
-    std::unique_ptr mesh(new SurfaceMesh);
+// http://www.geomview.org/docs/html/OFF.html
+std::unique_ptr readOFF(const std::string &filename) {
+  // Instantiate mesh!
+  // auto mesh = new SurfaceMesh();
+  std::unique_ptr mesh(new SurfaceMesh);
 
-    std::ifstream                fin(filename);
-    if (!fin.is_open())
-    {
-        std::cerr << "Read Error: File '" << filename << "' could not be read." << std::endl;
-        mesh.reset();
-        return mesh;
-    }
+  std::ifstream fin(filename);
+  if (!fin.is_open()) {
+    std::cerr << "Read Error: File '" << filename << "' could not be read."
+              << std::endl;
+    mesh.reset();
+    return mesh;
+  }
 
-    std::string line;
-    // Parse the first line:
-    // [ST][C][N][4][n]OFF  # Header keyword
-    // we only read OFF's in 3 space... simplicial_complex only does triangles
-    getline(fin, line);
-    // Assume the first line must end with 'OFF\n'
-    if (!(line.find("OFF", line.size()-4) == std::string::npos))
-    {
-        std::cerr << "File Format Error: File '" << filename << "' does not look like a valid OFF file." << std::endl;
-        std::cerr << "Expected 'OFF' at end of line, found: '" << line << "'." << std::endl;
-        mesh.reset();
-        return mesh;
-    }
+  std::string line;
+  // Parse the first line:
+  // [ST][C][N][4][n]OFF  # Header keyword
+  // we only read OFF's in 3 space... simplicial_complex only does triangles
+  getline(fin, line);
+  // Assume the first line must end with 'OFF\n'
+  if (!(line.find("OFF", line.size() - 4) == std::string::npos)) {
+    std::cerr << "File Format Error: File '" << filename
+              << "' does not look like a valid OFF file." << std::endl;
+    std::cerr << "Expected 'OFF' at end of line, found: '" << line << "'."
+              << std::endl;
+    mesh.reset();
+    return mesh;
+  }
 
-    // Have the support for reading in various things. Currently we are ignoring
-    // them though...
-    bool textureCoords = false;
-    if (!(line.find("ST") == std::string::npos))
-    {
-        std::cout << "Found vertex texture coordinates flag." << std::endl;
-        textureCoords = true;
-    }
-    bool vertexColors = false;
-    if (!(line.find("C") == std::string::npos))
-    {
-        std::cout << "Found vertex colors flag." << std::endl;
-        vertexColors = true;
-    }
-    bool vertexNormals = false;
-    if (!(line.find("N") == std::string::npos))
-    {
-        std::cout << "Found vertex normals flag." << std::endl;
-        vertexNormals = true;
-    }
-    int dimension = 3;
-    if (!(line.find("4") == std::string::npos))
-    {
-        std::cout << "Found dimension flag." << std::endl;
-        dimension = 4;
-    }
+  // Have the support for reading in various things. Currently we are ignoring
+  // them though...
+  bool textureCoords = false;
+  if (!(line.find("ST") == std::string::npos)) {
+    std::cout << "Found vertex texture coordinates flag." << std::endl;
+    textureCoords = true;
+  }
+  bool vertexColors = false;
+  if (!(line.find("C") == std::string::npos)) {
+    std::cout << "Found vertex colors flag." << std::endl;
+    vertexColors = true;
+  }
+  bool vertexNormals = false;
+  if (!(line.find("N") == std::string::npos)) {
+    std::cout << "Found vertex normals flag." << std::endl;
+    vertexNormals = true;
+  }
+  int dimension = 3;
+  if (!(line.find("4") == std::string::npos)) {
+    std::cout << "Found dimension flag." << std::endl;
+    dimension = 4;
+  }
 
-    if (!(line.find("n") == std::string::npos))
-    {
-        getline(fin, line);  // Assume no comments here yet...
-        int tempDim = std::stoi(line);
-        if (dimension == 4)
-            dimension = tempDim + 1;
-        else
-            dimension = tempDim;
-    }
+  if (!(line.find("n") == std::string::npos)) {
+    getline(fin, line); // Assume no comments here yet...
+    int tempDim = std::stoi(line);
+    if (dimension == 4)
+      dimension = tempDim + 1;
+    else
+      dimension = tempDim;
+  }
 
-    // Lambda function to split the string
-    auto split = [](const std::string &cstr, std::vector delim = {' ', '\t'}) -> std::vector {
-            std::string str = cstr;
-            for (auto i = 1; i < delim.size(); i++)
-            {
-                std::replace(str.begin(), str.end(), delim[i], delim[0]);
-            }
-            std::vector result;
-            auto                     begin = str.begin();
-            do
-            {
-                auto end = begin;
-                while (*end != delim[0] && end != str.end())
-                    end++;
-                if (end != begin)
-                    result.push_back(std::string(begin, end));
-                begin = end;
-            }
-            while (begin++ != str.end());
-            return result;
-        };
+  // Lambda function to split the string
+  auto split =
+      [](const std::string &cstr,
+         std::vector delim = {' ', '\t'}) -> std::vector {
+    std::string str = cstr;
+    for (auto i = 1; i < delim.size(); i++) {
+      std::replace(str.begin(), str.end(), delim[i], delim[0]);
+    }
+    std::vector result;
+    auto begin = str.begin();
+    do {
+      auto end = begin;
+      while (*end != delim[0] && end != str.end())
+        end++;
+      if (end != begin)
+        result.push_back(std::string(begin, end));
+      begin = end;
+    } while (begin++ != str.end());
+    return result;
+  };
 
-    std::vector arr;
-    // Allow some comments denoted by #...
-    while (getline(fin, line))
-    {
-        if (line.find("#") == 0)
-        {
-            continue;
-        }
-        arr = split(line, {'#'});
-        if (arr[0].length() != 0) // Assume that comments are over
-            break;
+  std::vector arr;
+  // Allow some comments denoted by #...
+  while (getline(fin, line)) {
+    if (line.find("#") == 0) {
+      continue;
     }
+    arr = split(line, {'#'});
+    if (arr[0].length() != 0) // Assume that comments are over
+      break;
+  }
 
-    // Parse the second line:
-    // NVertices  NFaces  NEdges
-    arr = split(arr[0]);
-    int numVertices = std::stoi(arr[0]);
-    //std::cout << "#Vertices: " << numVertices << std::endl;
+  // Parse the second line:
+  // NVertices  NFaces  NEdges
+  arr = split(arr[0]);
+  int numVertices = std::stoi(arr[0]);
+  // std::cout << "#Vertices: " << numVertices << std::endl;
 
-    int numFaces = std::stoi(arr[1]);
-    //std::cout << "#Faces: " << numFaces << std::endl;
+  int numFaces = std::stoi(arr[1]);
+  // std::cout << "#Faces: " << numFaces << std::endl;
 
-    // numEdges is ignored
-    //int numEdges = std::stoi(arr[2]);
-    //std::cout << "#Edges: " << numEdges << std::endl;
+  // numEdges is ignored
+  // int numEdges = std::stoi(arr[2]);
+  // std::cout << "#Edges: " << numEdges << std::endl;
 
-    // NOTE:: Assume there are no more comments...
+  // NOTE:: Assume there are no more comments...
 
-    // Parse the vertices
-    /*
-       x[0]  y[0]  z[0]
-     # Vertices, possibly with normals,
-     # colors, and/or texture coordinates, in that order,
-     # if the prefixes N, C, ST
-     # are present.
-     # If 4OFF, each vertex has 4 components,
-     # including a final homogeneous component.
-     # If nOFF, each vertex has Ndim components.
-     # If 4nOFF, each vertex has Ndim+1 components.
-     */
-    for (int i = 0; i < numVertices; i++)
-    {
-        getline(fin, line);
-        //std::cout << line << std::endl;
-        arr = split(line);
-        if (arr.size() < dimension)
-        {
-            std::cerr << "Parse Error: Vertex line has fewer dimensions than expected (" << dimension << ")" << std::endl;
-            mesh.reset();
-            return mesh;
-        }
-        double x = std::stod(arr[0]);
-        double y = std::stod(arr[1]);
-        double z = std::stod(arr[2]);
-        mesh->insert<1>({i}, SMVertex(x, y, z));
+  // Parse the vertices
+  /*
+     x[0]  y[0]  z[0]
+   # Vertices, possibly with normals,
+   # colors, and/or texture coordinates, in that order,
+   # if the prefixes N, C, ST
+   # are present.
+   # If 4OFF, each vertex has 4 components,
+   # including a final homogeneous component.
+   # If nOFF, each vertex has Ndim components.
+   # If 4nOFF, each vertex has Ndim+1 components.
+   */
+  for (int i = 0; i < numVertices; i++) {
+    getline(fin, line);
+    // std::cout << line << std::endl;
+    arr = split(line);
+    if (arr.size() < dimension) {
+      std::cerr
+          << "Parse Error: Vertex line has fewer dimensions than expected ("
+          << dimension << ")" << std::endl;
+      mesh.reset();
+      return mesh;
     }
+    double x = std::stod(arr[0]);
+    double y = std::stod(arr[1]);
+    double z = std::stod(arr[2]);
+    mesh->insert<1>({i}, SMVertex(x, y, z));
+  }
 
-    // Parse Faces
-    /*
-     # Faces
-     # Nv = # vertices on this face
-     # v[0] ... v[Nv-1]: vertex indices
-     #       in range 0..NVertices-1
+  // Parse Faces
+  /*
+   # Faces
+   # Nv = # vertices on this face
+   # v[0] ... v[Nv-1]: vertex indices
+   #       in range 0..NVertices-1
 
-        TODO: This should parse the number of vertices in each face (10)
-        TODO: (10) Get orientation from the OFF file
-     */
-    for (int i = 0; i < numFaces; i++)
-    {
-        getline(fin, line);
-        arr = split(line);
-        if (std::stoi(arr[0]) != 3 && arr.size() < dimension+1)
-        {
-            std::cerr << "Unsupported: Found face that is not a triangle!" << std::endl;
-            mesh.reset();
-            return mesh;
-        }
-        else if (arr.size() == dimension+1)
-        {
-            auto v0 = std::stoi(arr[1]);
-            auto v1 = std::stoi(arr[2]);
-            auto v2 = std::stoi(arr[3]);
-            mesh->insert<3>({v0, v1, v2});
-        }
-        else if (arr.size() == dimension+5)
-        {
-            auto v0 = std::stoi(arr[1]);
-            auto v1 = std::stoi(arr[2]);
-            auto v2 = std::stoi(arr[3]);
-            // parse for marker/color data
-            auto r = std::stod(arr[4]);
-            auto g = std::stod(arr[5]);
-            auto b = std::stod(arr[6]);
-            //auto k = std::stod(arr[7]);
-            mesh->insert<3>({v0, v1, v2}, SMFace(get_marker(r, g, b), false));
-        }
-        else
-        {
-            std::cerr << "Parse Error: Couldn't interpret face: '" << line << "'." << std::endl;
-            mesh.reset();
-            return mesh;
-        }
+      TODO: This should parse the number of vertices in each face (10)
+      TODO: (10) Get orientation from the OFF file
+   */
+  for (int i = 0; i < numFaces; i++) {
+    getline(fin, line);
+    arr = split(line);
+    if (std::stoi(arr[0]) != 3 && arr.size() < dimension + 1) {
+      std::cerr << "Unsupported: Found face that is not a triangle!"
+                << std::endl;
+      mesh.reset();
+      return mesh;
+    } else if (arr.size() == dimension + 1) {
+      auto v0 = std::stoi(arr[1]);
+      auto v1 = std::stoi(arr[2]);
+      auto v2 = std::stoi(arr[3]);
+      mesh->insert<3>({v0, v1, v2});
+    } else if (arr.size() == dimension + 5) {
+      auto v0 = std::stoi(arr[1]);
+      auto v1 = std::stoi(arr[2]);
+      auto v2 = std::stoi(arr[3]);
+      // parse for marker/color data
+      auto r = std::stod(arr[4]);
+      auto g = std::stod(arr[5]);
+      auto b = std::stod(arr[6]);
+      // auto k = std::stod(arr[7]);
+      mesh->insert<3>({v0, v1, v2}, SMFace(get_marker(r, g, b), false));
+    } else {
+      std::cerr << "Parse Error: Couldn't interpret face: '" << line << "'."
+                << std::endl;
+      mesh.reset();
+      return mesh;
     }
-    fin.close();
-    compute_orientation(*mesh);
-    return mesh;
+  }
+  fin.close();
+  compute_orientation(*mesh);
+  return mesh;
 }
 
-void writeOFF(const std::string &filename, const SurfaceMesh &mesh)
-{
-    std::ofstream fout(filename);
-    if (!fout.is_open())
-    {
-        std::cerr << "File '" << filename
-                  << "' could not be writen to." << std::endl;
-        exit(1);
-    }
+void writeOFF(const std::string &filename, const SurfaceMesh &mesh) {
+  std::ofstream fout(filename);
+  if (!fout.is_open()) {
+    std::cerr << "File '" << filename << "' could not be writen to."
+              << std::endl;
+    exit(1);
+  }
 
-    fout << "OFF" << std::endl;
+  fout << "OFF" << std::endl;
 
-    std::size_t numVertices = mesh.size<1>();
-    std::size_t numFaces = mesh.size<3>();
-    std::size_t numEdges = mesh.size<2>();
-    fout << numVertices << " "
-         << numFaces << " "
-         << numEdges << "\n";
+  std::size_t numVertices = mesh.size<1>();
+  std::size_t numFaces = mesh.size<3>();
+  std::size_t numEdges = mesh.size<2>();
+  fout << numVertices << " " << numFaces << " " << numEdges << "\n";
 
-    std::map sigma;
-    typename SurfaceMesh::KeyType cnt = 0;
+  std::map sigma;
+  typename SurfaceMesh::KeyType cnt = 0;
 
-    fout.precision(10);
-    // Get the vertex data directly
-    for (const auto vertexID : mesh.get_level_id<1>())
-    {
-        sigma[mesh.get_name(vertexID)[0]] = cnt++;
-        auto vertex = *vertexID;
+  fout.precision(10);
+  // Get the vertex data directly
+  for (const auto vertexID : mesh.get_level_id<1>()) {
+    sigma[mesh.get_name(vertexID)[0]] = cnt++;
+    auto vertex = *vertexID;
 
-        fout << vertex[0] << " "
-             << vertex[1] << " "
-             << vertex[2] << " "
-             << "\n";
-    }
+    fout << vertex[0] << " " << vertex[1] << " " << vertex[2] << " "
+         << "\n";
+  }
 
-    bool orientationError = false;
+  bool orientationError = false;
 
-    // Get the face nodes
-    for (auto faceNodeID : mesh.get_level_id<3>())
-    {
-        auto w = mesh.get_name(faceNodeID);
+  // Get the face nodes
+  for (auto faceNodeID : mesh.get_level_id<3>()) {
+    auto w = mesh.get_name(faceNodeID);
 
-        auto orientation = (*faceNodeID).orientation;
-        if (orientation == 1)
-        {
-            fout << "3 " << sigma[w[0]] << " " << sigma[w[1]] << " " << sigma[w[2]] << "\n";
-        }
-        else if (orientation == -1)
-        {
-            fout << "3 " << sigma[w[2]] << " " << sigma[w[1]] << " " << sigma[w[0]] << "\n";
+    auto orientation = (*faceNodeID).orientation;
+    if (orientation == 1) {
+      fout << "3 " << sigma[w[0]] << " " << sigma[w[1]] << " " << sigma[w[2]]
+           << "\n";
+    } else if (orientation == -1) {
+      fout << "3 " << sigma[w[2]] << " " << sigma[w[1]] << " " << sigma[w[0]]
+           << "\n";
 
-        }
-        else
-        {
-            orientationError = true;
-            fout << "3 " << sigma[w[0]] << " " << sigma[w[1]] << " " << sigma[w[2]] << "\n";
-        }
-    }
-    if (orientationError)
-    {
-        std::cerr << "WARNING(writeOFF): The orientation of one or more faces "
-                  << "is not defined. Did you run compute_orientation()?"
-                  << std::endl;
+    } else {
+      orientationError = true;
+      fout << "3 " << sigma[w[0]] << " " << sigma[w[1]] << " " << sigma[w[2]]
+           << "\n";
     }
-    fout.close();
+  }
+  if (orientationError) {
+    std::cerr << "WARNING(writeOFF): The orientation of one or more faces "
+              << "is not defined. Did you run compute_orientation()?"
+              << std::endl;
+  }
+  fout.close();
 }
 } // end namespace gamer
diff --git a/src/PDBReader.cpp b/src/PDBReader.cpp
index 086ba467..e4253b54 100644
--- a/src/PDBReader.cpp
+++ b/src/PDBReader.cpp
@@ -1,126 +1,119 @@
-/*
- * ***************************************************************************
- * This file is part of the GAMer software.
- * Copyright (C) 2016-2018
- * by Christopher Lee, John Moody, Rommie Amaro, J. Andrew McCammon,
- *    and Michael Holst
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * ***************************************************************************
- */
+// This file is part of the GAMer software.
+// Copyright (C) 2016-2021
+// by Christopher T. Lee and contributors
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, see 
+// or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+// Boston, MA 02111-1307 USA
 
-#include 
-#include 
+#include 
 #include 
+#include 
+#include 
 #include 
 #include 
 #include 
-#include 
+#include 
 #include 
-#include 
 
-#include "gamer/SurfaceMesh.h"
 #include "gamer/MarchingCube.h"
 #include "gamer/PDBReader.h"
+#include "gamer/SurfaceMesh.h"
 #include "gamer/Vertex.h"
 
 /// Namespace for all things gamer
-namespace gamer
-{
-
-std::unique_ptr readPDB_distgrid(const std::string &filename, const float radius)
-{
-    std::unique_ptr mesh(new SurfaceMesh);
-
-    std::vector            atoms;
-    // If readPDB errors return nullptr
-    if (!readPDB(filename, std::back_inserter(atoms)))
-    {
-        mesh.reset();
-        return mesh;
-    }
-    std::cout << "Atoms: " << atoms.size() << std::endl;
-    Vector3f min, max;
-    getMinMax(atoms.cbegin(), atoms.cend(), min, max, [&radius](const float atomRadius) -> float {
-            return DIM_SCALE*(atomRadius + radius);
-        });
-
-    float min_dimension = std::min((max[0] - min[0]), std::min((max[1] - min[1]), (max[2] - min[2])));
-    std::cout << "Min Dimension: " << min_dimension << std::endl;
-
-    Vector3i dim;
-    Vector3f maxMin = max-min;
+namespace gamer {
 
-    dim = static_cast((maxMin) + Vector3f({1, 1, 1})) * DIM_SCALE;
+std::unique_ptr readPDB_distgrid(const std::string &filename,
+                                              const float radius) {
+  std::unique_ptr mesh(new SurfaceMesh);
 
-    std::cout << "Dimension: " << dim << std::endl;
-    std::cout << "Min:" << min << std::endl;
-    std::cout << "Max:" << max << std::endl;
-
-    Vector3f span = (maxMin).ElementwiseDivision(static_cast(dim) - Vector3f({1, 1, 1}));
-    std::cout << "Delta: " << span << std::endl;
-
-    // Move dataset to positive octant and scale
-    for (auto &atom : atoms)
-    {
-        atom.pos = (atom.pos-min).ElementwiseDivision(span);
-        atom.radius = (atom.radius + radius)/((span[0] + span[1] + span[2]) / 3.0);
-    }
-
-    float* dataset = new float[dim[0]*dim[1]*dim[2]];
-    for (int i = 0; i < dim[0]*dim[1]*dim[2]; ++i)
-    {
-        dataset[i] = -5.0f;
-    }
-    gridSAS(atoms.cbegin(), atoms.cend(), dim, dataset);
-
-    std::vector          holelist;
-    std::unique_ptr SASmesh = std::move(marchingCubes(dataset, 5.0f, dim, span, 0.0f, std::back_inserter(holelist)));
-
-    for (auto curr = atoms.cbegin(); curr != atoms.cend(); ++curr)
-    {
-        Vector3f pos = curr->pos;
-        // compute the dataset coordinates of the atom's center
-        Vector3i c;
-        std::transform(pos.begin(), pos.end(), c.begin(), [](float v) -> int {
-                return round(v);
+  std::vector atoms;
+  // If readPDB errors return nullptr
+  if (!readPDB(filename, std::back_inserter(atoms))) {
+    mesh.reset();
+    return mesh;
+  }
+  std::cout << "Atoms: " << atoms.size() << std::endl;
+  Vector3f min, max;
+  getMinMax(atoms.cbegin(), atoms.cend(), min, max,
+            [&radius](const float atomRadius) -> float {
+              return DIM_SCALE * (atomRadius + radius);
             });
-    }
-
-    // Reset dataset
-    for (int i = 0; i < dim[0]*dim[1]*dim[2]; ++i)
-    {
-        dataset[i] = -5.0f;
-    }
-
-    auto SASverts = SASmesh->get_level<1>();
-    gridSES(SASverts.begin(), SASverts.end(), dim, dataset, radius);
-    // for(int i = 0; i < dim[0]*dim[1]*dim[2]; ++i){
-    //     std::cout << dataset[i] << std::endl;
-    // }
-
-    mesh = std::move(marchingCubes(dataset, 5.0f, dim, span, 0.0f, std::back_inserter(holelist)));
-    delete[] dataset;
 
-    // Translate back to the original position from the positive octant
-    for (auto &v : mesh->get_level<1>())
-    {
-        v += min;
-    }
-    return mesh;
+  float min_dimension = std::min(
+      (max[0] - min[0]), std::min((max[1] - min[1]), (max[2] - min[2])));
+  std::cout << "Min Dimension: " << min_dimension << std::endl;
+
+  Vector3i dim;
+  Vector3f maxMin = max - min;
+
+  dim = static_cast((maxMin) + Vector3f({1, 1, 1})) * DIM_SCALE;
+
+  std::cout << "Dimension: " << dim << std::endl;
+  std::cout << "Min:" << min << std::endl;
+  std::cout << "Max:" << max << std::endl;
+
+  Vector3f span = (maxMin).ElementwiseDivision(static_cast(dim) -
+                                               Vector3f({1, 1, 1}));
+  std::cout << "Delta: " << span << std::endl;
+
+  // Move dataset to positive octant and scale
+  for (auto &atom : atoms) {
+    atom.pos = (atom.pos - min).ElementwiseDivision(span);
+    atom.radius =
+        (atom.radius + radius) / ((span[0] + span[1] + span[2]) / 3.0);
+  }
+
+  float *dataset = new float[dim[0] * dim[1] * dim[2]];
+  for (int i = 0; i < dim[0] * dim[1] * dim[2]; ++i) {
+    dataset[i] = -5.0f;
+  }
+  gridSAS(atoms.cbegin(), atoms.cend(), dim, dataset);
+
+  std::vector holelist;
+  std::unique_ptr SASmesh = std::move(marchingCubes(
+      dataset, 5.0f, dim, span, 0.0f, std::back_inserter(holelist)));
+
+  for (auto curr = atoms.cbegin(); curr != atoms.cend(); ++curr) {
+    Vector3f pos = curr->pos;
+    // compute the dataset coordinates of the atom's center
+    Vector3i c;
+    std::transform(pos.begin(), pos.end(), c.begin(),
+                   [](float v) -> int { return round(v); });
+  }
+
+  // Reset dataset
+  for (int i = 0; i < dim[0] * dim[1] * dim[2]; ++i) {
+    dataset[i] = -5.0f;
+  }
+
+  auto SASverts = SASmesh->get_level<1>();
+  gridSES(SASverts.begin(), SASverts.end(), dim, dataset, radius);
+  // for(int i = 0; i < dim[0]*dim[1]*dim[2]; ++i){
+  //     std::cout << dataset[i] << std::endl;
+  // }
+
+  mesh = std::move(marchingCubes(dataset, 5.0f, dim, span, 0.0f,
+                                 std::back_inserter(holelist)));
+  delete[] dataset;
+
+  // Translate back to the original position from the positive octant
+  for (auto &v : mesh->get_level<1>()) {
+    v += min;
+  }
+  return mesh;
 }
 
 /**
@@ -133,178 +126,177 @@ std::unique_ptr readPDB_distgrid(const std::string &filename, const
  * @return     { description_of_the_return_value }
  */
 std::unique_ptr readPDB_gauss(const std::string &filename,
-                                           const float        blobbyness,
-                                           float              isovalue)
-{
-    std::unique_ptr mesh(new SurfaceMesh);
-
-    std::vector            atoms;
-    // If readPDB errors return nullptr
-    if (!readPDB(filename, std::back_inserter(atoms)))
-    {
-        mesh.reset();
-        return mesh;
-    }
+                                           const float blobbyness,
+                                           float isovalue) {
+  std::unique_ptr mesh(new SurfaceMesh);
+
+  std::vector atoms;
+  // If readPDB errors return nullptr
+  if (!readPDB(filename, std::back_inserter(atoms))) {
+    mesh.reset();
+    return mesh;
+  }
 
-    std::cout << "Atoms: " << atoms.size() << std::endl;
+  std::cout << "Atoms: " << atoms.size() << std::endl;
 
-    Vector3f min, max;
-    getMinMax(atoms.cbegin(), atoms.cend(), min, max,
-              [&blobbyness](const float atomRadius) -> float{
-            return atomRadius * sqrt(1.0 + log(pdbreader_detail::EPSILON) / blobbyness);
-        });
+  Vector3f min, max;
+  getMinMax(atoms.cbegin(), atoms.cend(), min, max,
+            [&blobbyness](const float atomRadius) -> float {
+              return atomRadius *
+                     sqrt(1.0 + log(pdbreader_detail::EPSILON) / blobbyness);
+            });
 
-    float min_dimension = std::min((max[0] - min[0]), std::min((max[1] - min[1]), (max[2] - min[2])));
+  float min_dimension = std::min(
+      (max[0] - min[0]), std::min((max[1] - min[1]), (max[2] - min[2])));
 
-    std::cout << "Min Dimension: " << min_dimension << std::endl;
+  std::cout << "Min Dimension: " << min_dimension << std::endl;
 
-    Vector3i dim;
+  Vector3i dim;
 
-    Vector3f maxMin = max-min;
+  Vector3f maxMin = max - min;
 
-    dim = static_cast((maxMin) + Vector3f({1, 1, 1})) * DIM_SCALE;
+  dim = static_cast((maxMin) + Vector3f({1, 1, 1})) * DIM_SCALE;
 
-    std::cout << "Dimension: " << dim << std::endl;
-    std::cout << "Min:" << min << std::endl;
-    std::cout << "Max:" << max << std::endl;
+  std::cout << "Dimension: " << dim << std::endl;
+  std::cout << "Min:" << min << std::endl;
+  std::cout << "Max:" << max << std::endl;
 
-    Vector3f span = (maxMin).ElementwiseDivision(static_cast(dim) - Vector3f({1, 1, 1}));
-    std::cout << "Delta: " << span << std::endl;
+  Vector3f span = (maxMin).ElementwiseDivision(static_cast(dim) -
+                                               Vector3f({1, 1, 1}));
+  std::cout << "Delta: " << span << std::endl;
 
-    float* dataset = new float[dim[0]*dim[1]*dim[2]]();
+  float *dataset = new float[dim[0] * dim[1] * dim[2]]();
 
-    // Bring atoms to +++ quadrant
-    // for(auto& atom : atoms){
-    //     atom.pos = (atom.pos-min).ElementwiseDivision(span);
-    // }
+  // Bring atoms to +++ quadrant
+  // for(auto& atom : atoms){
+  //     atom.pos = (atom.pos-min).ElementwiseDivision(span);
+  // }
 
-    std::cout << "Begin blurring coordinates" << std::endl;
-    blurAtoms(atoms.cbegin(), atoms.cend(), dataset, min, maxMin, dim, blobbyness);
-    std::cout << "Done blurring coords" << std::endl;
-
-    float minval = std::numeric_limits::infinity();
-    float maxval = -std::numeric_limits::infinity();
-
-    for (int i = 0; i < dim[2] * dim[1] * dim[0]; ++i)
-    {
-        float cval = dataset[i];
-        if (cval < minval){
-            minval = cval;
-        }
-        if (cval > maxval)
-        {
-            maxval = cval;
-        }
-    }
-    // std::cout << "Min Density: " << minval << ", Max Density: " << maxval << std::endl;
-    float data_isoval = 0.44 * maxval; // Override the user's isovalue... is
-                                       // this a good idea?
-    if (data_isoval < isovalue)
-    {
-        isovalue = data_isoval;
-    }
-    std::cout << "Isovalue: " << isovalue << std::endl;
+  std::cout << "Begin blurring coordinates" << std::endl;
+  blurAtoms(atoms.cbegin(), atoms.cend(), dataset, min, maxMin, dim,
+            blobbyness);
+  std::cout << "Done blurring coords" << std::endl;
 
-    std::vector holelist;
-    mesh = std::move(marchingCubes(dataset, maxval, dim, span, isovalue, std::back_inserter(holelist)));
-    delete[] dataset;
+  float minval = std::numeric_limits::infinity();
+  float maxval = -std::numeric_limits::infinity();
 
-    // Translate back to the original position from the positive octant
-    for (auto &v : mesh->get_level<1>())
-    {
-        v += min;
+  for (int i = 0; i < dim[2] * dim[1] * dim[0]; ++i) {
+    float cval = dataset[i];
+    if (cval < minval) {
+      minval = cval;
     }
-    // TODO: (50) What to do with holelist...
-    return mesh;
+    if (cval > maxval) {
+      maxval = cval;
+    }
+  }
+  // std::cout << "Min Density: " << minval << ", Max Density: " << maxval <<
+  // std::endl;
+  float data_isoval = 0.44 * maxval; // Override the user's isovalue... is
+                                     // this a good idea?
+  if (data_isoval < isovalue) {
+    isovalue = data_isoval;
+  }
+  std::cout << "Isovalue: " << isovalue << std::endl;
+
+  std::vector holelist;
+  mesh = std::move(marchingCubes(dataset, maxval, dim, span, isovalue,
+                                 std::back_inserter(holelist)));
+  delete[] dataset;
+
+  // Translate back to the original position from the positive octant
+  for (auto &v : mesh->get_level<1>()) {
+    v += min;
+  }
+  // TODO: (50) What to do with holelist...
+  return mesh;
 }
 
 std::unique_ptr readPQR_gauss(const std::string &filename,
-                                           const float        blobbyness,
-                                           float              isovalue)
-{
-    std::unique_ptr mesh(new SurfaceMesh);
-
-    std::vector            atoms;
-    // If readPDB errors return nullptr
-    if (!readPQR(filename, std::back_inserter(atoms)))
-    {
-        mesh.reset();
-        return mesh;
-    }
+                                           const float blobbyness,
+                                           float isovalue) {
+  std::unique_ptr mesh(new SurfaceMesh);
+
+  std::vector atoms;
+  // If readPDB errors return nullptr
+  if (!readPQR(filename, std::back_inserter(atoms))) {
+    mesh.reset();
+    return mesh;
+  }
 
-    std::cout << "Atoms: " << atoms.size() << std::endl;
+  std::cout << "Atoms: " << atoms.size() << std::endl;
 
-    Vector3f min, max;
-    getMinMax(atoms.cbegin(), atoms.cend(), min, max,
-              [&blobbyness](const float atomRadius) -> float{
-            return atomRadius * sqrt(1.0 + log(pdbreader_detail::EPSILON) / blobbyness);
-        });
+  Vector3f min, max;
+  getMinMax(atoms.cbegin(), atoms.cend(), min, max,
+            [&blobbyness](const float atomRadius) -> float {
+              return atomRadius *
+                     sqrt(1.0 + log(pdbreader_detail::EPSILON) / blobbyness);
+            });
 
-    float min_dimension = std::min((max[0] - min[0]), std::min((max[1] - min[1]), (max[2] - min[2])));
+  float min_dimension = std::min(
+      (max[0] - min[0]), std::min((max[1] - min[1]), (max[2] - min[2])));
 
-    std::cout << "Min Dimension: " << min_dimension << std::endl;
+  std::cout << "Min Dimension: " << min_dimension << std::endl;
 
-    Vector3i dim;
+  Vector3i dim;
 
-    Vector3f maxMin = max-min;
+  Vector3f maxMin = max - min;
 
-    dim = static_cast((maxMin) + Vector3f({1, 1, 1})) * DIM_SCALE;
+  dim = static_cast((maxMin) + Vector3f({1, 1, 1})) * DIM_SCALE;
 
-    std::cout << "Dimension: " << dim << std::endl;
-    std::cout << "Min:" << min << std::endl;
-    std::cout << "Max:" << max << std::endl;
+  std::cout << "Dimension: " << dim << std::endl;
+  std::cout << "Min:" << min << std::endl;
+  std::cout << "Max:" << max << std::endl;
 
-    Vector3f span = (maxMin).ElementwiseDivision(static_cast(dim) - Vector3f({1, 1, 1}));
-    std::cout << "Delta: " << span << std::endl;
+  Vector3f span = (maxMin).ElementwiseDivision(static_cast(dim) -
+                                               Vector3f({1, 1, 1}));
+  std::cout << "Delta: " << span << std::endl;
 
-    float* dataset = new float[dim[0]*dim[1]*dim[2]]();
+  float *dataset = new float[dim[0] * dim[1] * dim[2]]();
 
-    // Bring atoms to +++ quadrant
-    // for(auto& atom : atoms){
-    //     atom.pos = (atom.pos-min).ElementwiseDivision(span);
-    // }
+  // Bring atoms to +++ quadrant
+  // for(auto& atom : atoms){
+  //     atom.pos = (atom.pos-min).ElementwiseDivision(span);
+  // }
 
-    std::cout << "Begin blurring coordinates" << std::endl;
-    blurAtoms(atoms.cbegin(), atoms.cend(), dataset, min, maxMin, dim, blobbyness);
-    std::cout << "Done blurring coords" << std::endl;
-
-    // float minval;
-    float maxval;
-    // minval = std::numeric_limits::infinity();
-    maxval = -std::numeric_limits::infinity();
-
-    for (int i = 0; i < dim[2] * dim[1] * dim[0]; ++i)
-    {
-        float cval = dataset[i];
-        // if (cval < minval){
-        //     minval = cval;
-        // }
-        if (cval > maxval)
-        {
-            maxval = cval;
-        }
-    }
-    // std::cout << "Min Density: " << minval << ", Max Density: " << maxval <<
-    // std::endl;
-    float data_isoval = 0.44 * maxval; // Override the user's isovalue... is
-                                       // this a good idea?
-    if (data_isoval < isovalue)
-    {
-        isovalue = data_isoval;
-    }
-    std::cout << "Isovalue: " << isovalue << std::endl;
+  std::cout << "Begin blurring coordinates" << std::endl;
+  blurAtoms(atoms.cbegin(), atoms.cend(), dataset, min, maxMin, dim,
+            blobbyness);
+  std::cout << "Done blurring coords" << std::endl;
 
-    std::vector holelist;
-    mesh = std::move(marchingCubes(dataset, maxval, dim, span, isovalue, std::back_inserter(holelist)));
-    delete[] dataset;
+  // float minval;
+  float maxval;
+  // minval = std::numeric_limits::infinity();
+  maxval = -std::numeric_limits::infinity();
 
-    // Translate back to the original position from the positive octant
-    for (auto &v : mesh->get_level<1>())
-    {
-        v += min;
+  for (int i = 0; i < dim[2] * dim[1] * dim[0]; ++i) {
+    float cval = dataset[i];
+    // if (cval < minval){
+    //     minval = cval;
+    // }
+    if (cval > maxval) {
+      maxval = cval;
     }
-    // TODO: (50) What to do with holelist...
-    return mesh;
+  }
+  // std::cout << "Min Density: " << minval << ", Max Density: " << maxval <<
+  // std::endl;
+  float data_isoval = 0.44 * maxval; // Override the user's isovalue... is
+                                     // this a good idea?
+  if (data_isoval < isovalue) {
+    isovalue = data_isoval;
+  }
+  std::cout << "Isovalue: " << isovalue << std::endl;
+
+  std::vector holelist;
+  mesh = std::move(marchingCubes(dataset, maxval, dim, span, isovalue,
+                                 std::back_inserter(holelist)));
+  delete[] dataset;
+
+  // Translate back to the original position from the positive octant
+  for (auto &v : mesh->get_level<1>()) {
+    v += min;
+  }
+  // TODO: (50) What to do with holelist...
+  return mesh;
 }
 
 } // end namespace gamer
diff --git a/src/SurfaceMesh.cpp b/src/SurfaceMesh.cpp
index b8a1bdd8..09c5cc62 100644
--- a/src/SurfaceMesh.cpp
+++ b/src/SurfaceMesh.cpp
@@ -1,31 +1,26 @@
-/*
- * ***************************************************************************
- * This file is part of the GAMer software.
- * Copyright (C) 2016-2018
- * by Christopher Lee, John Moody, Rommie Amaro, J. Andrew McCammon,
- *    and Michael Holst
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * ***************************************************************************
- */
+// This file is part of the GAMer software.
+// Copyright (C) 2016-2021
+// by Christopher T. Lee and contributors
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, see 
+// or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+// Boston, MA 02111-1307 USA
 
 #define _USE_MATH_DEFINES
-#include 
-#include 
 #include 
+#include 
+#include 
 #include 
 #include 
 #include 
@@ -33,7 +28,6 @@
 #include 
 #include 
 #include 
-#include 
 
 #include 
 #include 
@@ -43,508 +37,446 @@
 #include "gamer/Vertex.h"
 
 /// Namespace for all things gamer
-namespace gamer
-{
-void print(const SurfaceMesh& mesh)
-{
-    std::cout << "Level: 1" << std::endl;
-    for (auto node : mesh.get_level_id<1>())
-    {
-        std::cout << "    " << *node << std::endl;
-    }
-    std::cout << "Level: 2" << std::endl;
-    for (auto node : mesh.get_level_id<2>())
-    {
-        auto name = mesh.get_name(node);
-        std::cout << "    " << casc::to_string(name) << std::endl;
-    }
-    std::cout << "Level: 3" << std::endl;
-    for (auto node : mesh.get_level_id<3>())
-    {
-        auto name = mesh.get_name(node);
-        std::cout << "    " << casc::to_string(name) << std::endl;
-    }
+namespace gamer {
+void print(const SurfaceMesh &mesh) {
+  std::cout << "Level: 1" << std::endl;
+  for (auto node : mesh.get_level_id<1>()) {
+    std::cout << "    " << *node << std::endl;
+  }
+  std::cout << "Level: 2" << std::endl;
+  for (auto node : mesh.get_level_id<2>()) {
+    auto name = mesh.get_name(node);
+    std::cout << "    " << casc::to_string(name) << std::endl;
+  }
+  std::cout << "Level: 3" << std::endl;
+  for (auto node : mesh.get_level_id<3>()) {
+    auto name = mesh.get_name(node);
+    std::cout << "    " << casc::to_string(name) << std::endl;
+  }
 }
 
-void generateHistogram(const SurfaceMesh& mesh)
-{
-    // compute angle distribution
-    std::array histogram;
-    histogram.fill(0);
-    for (auto face : mesh.get_level_id<3>())
-    {
-        auto vertexIDs = mesh.get_name(face);
-        // Unpack the ID's for convenience
-        Vertex a = *mesh.get_simplex_up<1>({vertexIDs[0]});
-        Vertex b = *mesh.get_simplex_up<1>({vertexIDs[1]});
-        Vertex c = *mesh.get_simplex_up<1>({vertexIDs[2]});
-
-        auto binAngle = [&](double angle) -> std::size_t {
-                            return static_cast(std::floor(angle/10));
-                        };
-        histogram[binAngle(angleDeg(a, b, c))]++;
-        histogram[binAngle(angleDeg(b, a, c))]++;
-        histogram[binAngle(angleDeg(c, a, b))]++;
+void generateHistogram(const SurfaceMesh &mesh) {
+  // compute angle distribution
+  std::array histogram;
+  histogram.fill(0);
+  for (auto face : mesh.get_level_id<3>()) {
+    auto vertexIDs = mesh.get_name(face);
+    // Unpack the ID's for convenience
+    Vertex a = *mesh.get_simplex_up<1>({vertexIDs[0]});
+    Vertex b = *mesh.get_simplex_up<1>({vertexIDs[1]});
+    Vertex c = *mesh.get_simplex_up<1>({vertexIDs[2]});
+
+    auto binAngle = [&](double angle) -> std::size_t {
+      return static_cast(std::floor(angle / 10));
+    };
+    histogram[binAngle(angleDeg(a, b, c))]++;
+    histogram[binAngle(angleDeg(b, a, c))]++;
+    histogram[binAngle(angleDeg(c, a, b))]++;
+  }
+
+  std::size_t factor = mesh.size<3>() * 3;
+  std::for_each(histogram.begin(), histogram.end(),
+                [&factor](double &n) { n = 100.0 * n / factor; });
+
+  std::cout << "Angle Distribution:" << std::endl;
+  for (std::size_t x = 0; x < 18; x++)
+    std::cout << x * 10 << "-" << (x + 1) * 10 << ": " << std::setprecision(2)
+              << std::fixed << histogram[x] << std::endl;
+  std::cout << std::endl << std::endl;
+
+  // compute the edge length distribution
+  std::cout << "Edge Length Distribution:" << std::endl;
+  std::vector lengths;
+  for (auto edge : mesh.get_level_id<2>()) {
+    auto vertexIDs = mesh.down(edge);
+    auto t1 = *vertexIDs.cbegin();
+    auto t2 = *(++vertexIDs.cbegin());
+    auto v1 = *t1;
+    auto v2 = *t2;
+    double len = length(v2 - v1);
+    lengths.push_back(len);
+  }
+  std::sort(lengths.begin(), lengths.end());
+
+  std::array histogramLength;
+  histogramLength.fill(0);
+  double interval = (lengths.back() - lengths.front()) / 20;
+  double low = lengths.front();
+
+  if (interval <= 0.0000001) // floating point roundoff prevention
+  {
+    std::cout << lengths.front() << ": " << 100 << std::endl << std::endl;
+  } else {
+    for (auto length : lengths) {
+      histogramLength[std::floor((length - low) / interval)]++;
     }
 
-    std::size_t factor = mesh.size<3>()*3;
-    std::for_each(histogram.begin(), histogram.end(), [&factor](double& n){
-            n = 100.0*n/factor;
-        });
+    factor = mesh.size<2>();
+    std::for_each(histogramLength.begin(), histogramLength.end(),
+                  [&factor](double &n) { n = 100.0 * n / factor; });
 
-    std::cout << "Angle Distribution:" << std::endl;
-    for (std::size_t x = 0; x < 18; x++)
-        std::cout << x*10 << "-" << (x+1)*10 << ": " << std::setprecision(2)
-                  << std::fixed << histogram[x] << std::endl;
+    for (std::size_t x = 0; x < 20; x++)
+      std::cout << x * interval << "-" << (x + 1) * interval << ": "
+                << std::setprecision(2) << std::fixed << histogramLength[x]
+                << std::endl;
     std::cout << std::endl << std::endl;
+  }
+
+  // Compute the valence distribution
+  std::array histogramValence;
+  histogramValence.fill(0);
+
+  for (auto vertexID : mesh.get_level_id<1>()) {
+    // TODO bounds checking here...
+    histogramValence[getValence(mesh, vertexID)]++;
+  }
+
+  factor = mesh.size<1>();
+  // std::for_each(histogramValence.begin(), histogramValence.end(),
+  // [&factor](double& n){
+  //         n = 100.0*n/factor;});
+  std::cout << "Valence distribution:" << std::endl;
+  for (int x = 0; x < 20; x++)
+    std::cout << x << ": " << histogramValence[x] << std::endl;
+  std::cout << std::endl << std::endl;
+}
 
-    // compute the edge length distribution
-    std::cout << "Edge Length Distribution:" << std::endl;
-    std::vector lengths;
-    for (auto edge : mesh.get_level_id<2>())
-    {
-        auto vertexIDs = mesh.down(edge);
-        auto t1  = *vertexIDs.cbegin();
-        auto t2  = *(++vertexIDs.cbegin());
-        auto v1  = *t1;
-        auto v2  = *t2;
-        double len = length(v2-v1);
-        lengths.push_back(len);
-    }
-    std::sort(lengths.begin(), lengths.end());
-
-    std::array histogramLength;
-    histogramLength.fill(0);
-    double interval = (lengths.back() - lengths.front())/20;
-    double low = lengths.front();
-
-    if (interval <= 0.0000001) // floating point roundoff prevention
-    {
-        std::cout << lengths.front() << ": " << 100 << std::endl << std::endl;
-    }
-    else
-    {
-        for (auto length : lengths)
-        {
-            histogramLength[std::floor((length-low)/interval)]++;
-        }
+void printQualityInfo(const std::string &filename, const SurfaceMesh &mesh) {
 
-        factor = mesh.size<2>();
-        std::for_each(histogramLength.begin(), histogramLength.end(), [&factor](double& n){
-                n = 100.0*n/factor;
-            });
+  std::stringstream anglefilename;
+  anglefilename << filename << ".angle";
 
-        for (std::size_t x = 0; x < 20; x++)
-            std::cout << x*interval << "-" << (x+1)*interval << ": " << std::setprecision(2)
-                      << std::fixed << histogramLength[x] << std::endl;
-        std::cout << std::endl << std::endl;
-    }
+  std::ofstream angleOut(anglefilename.str());
+  if (!angleOut.is_open()) {
+    gamer_runtime_error("Couldn't open file ", filename, ".angle");
+  }
 
-    // Compute the valence distribution
-    std::array histogramValence;
-    histogramValence.fill(0);
+  std::stringstream areafilename;
+  areafilename << filename << ".area";
+  std::ofstream areaOut(areafilename.str());
+  if (!areaOut.is_open()) {
+    gamer_runtime_error("Couldn't open file ", filename, ".area");
+  }
 
-    for (auto vertexID : mesh.get_level_id<1>())
-    {
-        // TODO bounds checking here...
-        histogramValence[getValence(mesh, vertexID)]++;
-    }
+  for (auto faceID : mesh.get_level_id<3>()) {
+    auto name = mesh.get_name(faceID);
+    auto a = *mesh.get_simplex_up({name[0]});
+    auto b = *mesh.get_simplex_up({name[1]});
+    auto c = *mesh.get_simplex_up({name[2]});
 
-    factor = mesh.size<1>();
-    // std::for_each(histogramValence.begin(), histogramValence.end(),
-    // [&factor](double& n){
-    //         n = 100.0*n/factor;});
-    std::cout << "Valence distribution:" << std::endl;
-    for (int x = 0; x < 20; x++)
-        std::cout << x << ": " << histogramValence[x] << std::endl;
-    std::cout << std::endl << std::endl;
+    areaOut << getArea(a, b, c) << "\n";
+    // Print angles in degrees
+    angleOut << angleDeg(a, b, c) << "\n"
+             << angleDeg(b, a, c) << "\n"
+             << angleDeg(a, c, b) << "\n";
+  }
+  areaOut.close();
+  angleOut.close();
 }
 
-void printQualityInfo(const std::string& filename, const SurfaceMesh& mesh)
-{
-
-    std::stringstream anglefilename;
-    anglefilename << filename << ".angle";
-
-    std::ofstream angleOut(anglefilename.str());
-    if (!angleOut.is_open())
-    {
-        std::stringstream ss;
-        ss << "Couldn't open file " << filename << ".angle";
-        throw std::runtime_error(ss.str());
-    }
-
-    std::stringstream areafilename;
-    areafilename << filename << ".area";
-    std::ofstream areaOut(areafilename.str());
-    if (!areaOut.is_open())
-    {
-        std::stringstream ss;
-        ss << "Couldn't open file " << filename << ".area";
-        throw std::runtime_error(ss.str());
-    }
-
-    for (auto faceID : mesh.get_level_id<3>())
-    {
-        auto name = mesh.get_name(faceID);
-        auto a = *mesh.get_simplex_up({name[0]});
-        auto b = *mesh.get_simplex_up({name[1]});
-        auto c = *mesh.get_simplex_up({name[2]});
-
-        areaOut << getArea(a, b, c) << "\n";
-        // Print angles in degrees
-        angleOut << angleDeg(a, b, c) << "\n"
-                 << angleDeg(b, a, c) << "\n"
-                 << angleDeg(a, c, b) << "\n";
+std::tuple getMinMaxAngles(const SurfaceMesh &mesh,
+                                                     double maxMinAngle,
+                                                     double minMaxAngle) {
+  double minAngle = 360;
+  double maxAngle = 0;
+  int small = 0;
+  int large = 0;
+
+  // for each triangle
+  for (auto nid : mesh.get_level_id<3>()) {
+    auto name = mesh.get_name(nid);
+    auto a = *mesh.get_simplex_up({name[0]});
+    auto b = *mesh.get_simplex_up({name[1]});
+    auto c = *mesh.get_simplex_up({name[2]});
+    std::array angles;
+    try {
+      angles[0] = angleDeg(a, b, c);
+      angles[1] = angleDeg(b, a, c);
+      angles[2] = angleDeg(a, c, b);
+    } catch (std::runtime_error &e) {
+      std::cout << e.what() << std::endl;
+      gamer_runtime_error("ERROR(getMinMaxAngles): Cannot compute angles "
+                               "of face with zero area.");
     }
-    areaOut.close();
-    angleOut.close();
-}
-
-std::tuple getMinMaxAngles(
-    const SurfaceMesh& mesh,
-    double maxMinAngle,
-    double minMaxAngle)
-{
-    double minAngle = 360;
-    double maxAngle = 0;
-    int small = 0;
-    int large = 0;
-
-    // for each triangle
-    for (auto nid : mesh.get_level_id<3>())
-    {
-        auto name = mesh.get_name(nid);
-        auto a = *mesh.get_simplex_up({name[0]});
-        auto b = *mesh.get_simplex_up({name[1]});
-        auto c = *mesh.get_simplex_up({name[2]});
-        std::array angles;
-        try
-        {
-            angles[0] = angleDeg(a, b, c);
-            angles[1] = angleDeg(b, a, c);
-            angles[2] = angleDeg(a, c, b);
-        }
-        catch (std::runtime_error& e)
-        {
-            std::cout << e.what() << std::endl;
-            throw std::runtime_error("ERROR(getMinMaxAngles): Cannot compute angles of face with zero area.");
-        }
 
-        for (double angle : angles)
-        {
-            if (angle < minAngle)
-            {
-                minAngle = angle;
-            }
-            if (angle > maxAngle)
-            {
-                maxAngle = angle;
-            }
-            if (angle < maxMinAngle)
-            {
-                ++small;
-            }
-            if (angle > minMaxAngle)
-            {
-                ++large;
-            }
-        }
+    for (double angle : angles) {
+      if (angle < minAngle) {
+        minAngle = angle;
+      }
+      if (angle > maxAngle) {
+        maxAngle = angle;
+      }
+      if (angle < maxMinAngle) {
+        ++small;
+      }
+      if (angle > minMaxAngle) {
+        ++large;
+      }
     }
-    return std::make_tuple(minAngle, maxAngle, small, large);
+  }
+  return std::make_tuple(minAngle, maxAngle, small, large);
 }
 
-double getArea(const SurfaceMesh& mesh)
-{
-    double area = 0.0;
-    for (auto faceID : mesh.get_level_id<3>())
-        area += getArea(mesh, faceID);
-    return area;
+double getArea(const SurfaceMesh &mesh) {
+  double area = 0.0;
+  for (auto faceID : mesh.get_level_id<3>())
+    area += getArea(mesh, faceID);
+  return area;
 }
 
-double getArea(const SurfaceMesh& mesh, SurfaceMesh::SimplexID<3> faceID)
-{
-    auto name = mesh.get_name(faceID);
-    auto a = *mesh.get_simplex_up({name[0]});
-    auto b = *mesh.get_simplex_up({name[1]});
-    auto c = *mesh.get_simplex_up({name[2]});
-    return getArea(a, b, c);
+double getArea(const SurfaceMesh &mesh, SurfaceMesh::SimplexID<3> faceID) {
+  auto name = mesh.get_name(faceID);
+  auto a = *mesh.get_simplex_up({name[0]});
+  auto b = *mesh.get_simplex_up({name[1]});
+  auto c = *mesh.get_simplex_up({name[2]});
+  return getArea(a, b, c);
 }
 
-double getArea(Vertex a, Vertex b, Vertex c)
-{
-    auto wedge = (b-a)^(b-c);
-    return std::sqrt(wedge|wedge)/2;
+double getArea(Vertex a, Vertex b, Vertex c) {
+  auto wedge = (b - a) ^ (b - c);
+  return std::sqrt(wedge | wedge) / 2;
 }
 
 /**
  * http://research.microsoft.com/en-us/um/people/chazhang/publications/icip01_ChaZhang.pdf
  * http://chenlab.ece.cornell.edu/Publication/Cha/icip01_Cha.pdf
  */
-double getVolume(const SurfaceMesh& mesh)
-{
-    bool orientError = false;
-    double volume = 0;
-    for (auto faceID : mesh.get_level_id<3>())
-    {
-        auto name = mesh.get_name(faceID);
-        auto a = (*mesh.get_simplex_up({name[0]})).position;
-        auto b = (*mesh.get_simplex_up({name[1]})).position;
-        auto c = (*mesh.get_simplex_up({name[2]})).position;
-
-        Vector norm;
-        double tmp = 0.0;
-        if ((*faceID).orientation == 1)
-        {
-            // a->b->c
-            norm = cross(b, c);
-            tmp  = dot(a, norm);
-
-        }
-        else if ((*faceID).orientation == -1)
-        {
-            // c->b->a
-            norm = cross(b, a);
-            tmp  = dot(c, norm);
-        }
-        else
-        {
-            orientError = true;
-        }
+double getVolume(const SurfaceMesh &mesh) {
+  bool orientError = false;
+  double volume = 0;
+  for (auto faceID : mesh.get_level_id<3>()) {
+    auto name = mesh.get_name(faceID);
+    auto a = (*mesh.get_simplex_up({name[0]})).position;
+    auto b = (*mesh.get_simplex_up({name[1]})).position;
+    auto c = (*mesh.get_simplex_up({name[2]})).position;
 
-        // * Probably less efficient way #1
-        // tmp = dot(a, getNormal(mesh, faceID));
-
-        // * Less efficient way #2
-        // norm = getNormalFromTangent(getTangent(mesh, faceID));
-        // auto wedge = a^b^c;
-        // tmp = std::sqrt(wedge|wedge);
-        // auto sgn = dot((a+b+c)/3, norm);
-        // if(sgn <= 0) {
-        //     tmp = -1*tmp;
-        // }
-        volume += tmp;
-    }
-    if (orientError)
-    {
-        std::cerr << "ERROR getVolume(): Orientation undefined for one or more "
-                  << "simplices. Did you call compute_orientation()?" << std::endl;
+    Vector norm;
+    double tmp = 0.0;
+    if ((*faceID).orientation == 1) {
+      // a->b->c
+      norm = cross(b, c);
+      tmp = dot(a, norm);
+
+    } else if ((*faceID).orientation == -1) {
+      // c->b->a
+      norm = cross(b, a);
+      tmp = dot(c, norm);
+    } else {
+      orientError = true;
     }
-    return volume/6;
-}
 
-void translate(SurfaceMesh& mesh, Vector v)
-{
-    for (auto& vertex : mesh.get_level<1>())
-        vertex += v;
+    // * Probably less efficient way #1
+    // tmp = dot(a, getNormal(mesh, faceID));
+
+    // * Less efficient way #2
+    // norm = getNormalFromTangent(getTangent(mesh, faceID));
+    // auto wedge = a^b^c;
+    // tmp = std::sqrt(wedge|wedge);
+    // auto sgn = dot((a+b+c)/3, norm);
+    // if(sgn <= 0) {
+    //     tmp = -1*tmp;
+    // }
+    volume += tmp;
+  }
+  if (orientError) {
+    std::cerr << "ERROR getVolume(): Orientation undefined for one or more "
+              << "simplices. Did you call compute_orientation()?" << std::endl;
+  }
+  return volume / 6;
 }
 
-void translate(SurfaceMesh& mesh, double dx, double dy, double dz)
-{
-    Vector v = Vector({dx, dy, dz});
-    translate(mesh, v);
+void translate(SurfaceMesh &mesh, Vector v) {
+  for (auto &vertex : mesh.get_level<1>())
+    vertex += v;
 }
 
-void scale(SurfaceMesh& mesh, Vector v)
-{
-    for (auto& vertex : mesh.get_level<1>())
-    {
-        vertex[0] *= v[0];
-        vertex[1] *= v[1];
-        vertex[2] *= v[2];
-    }
+void translate(SurfaceMesh &mesh, double dx, double dy, double dz) {
+  Vector v = Vector({dx, dy, dz});
+  translate(mesh, v);
 }
 
-void scale(SurfaceMesh& mesh, double sx, double sy, double sz)
-{
-    Vector v = Vector();
-    v[0] = sx; v[1] = sy; v[2] = sz;
-    scale(mesh, v);
+void scale(SurfaceMesh &mesh, Vector v) {
+  for (auto &vertex : mesh.get_level<1>()) {
+    vertex[0] *= v[0];
+    vertex[1] *= v[1];
+    vertex[2] *= v[2];
+  }
 }
 
-void scale(SurfaceMesh& mesh, double s)
-{
-    Vector v = Vector();
-    v[0] = s; v[1] = s; v[2] = s;
-    scale(mesh, v);
+void scale(SurfaceMesh &mesh, double sx, double sy, double sz) {
+  Vector v = Vector();
+  v[0] = sx;
+  v[1] = sy;
+  v[2] = sz;
+  scale(mesh, v);
 }
 
-std::pair getCenterRadius(SurfaceMesh& mesh)
-{
-    Vector center = Vector();
-    double radius = 0;
-    if (mesh.size<1>() > 0)
-    {
-        for (auto vertexID : mesh.get_level_id<1>())
-        {
-            center += *vertexID;
-        }
-        center /= static_cast(mesh.size<1>());
-
-        for (auto vertexID : mesh.get_level_id<1>())
-        {
-            Vector tmp  = *vertexID - center;
-            double dist = std::sqrt(tmp|tmp);
-            if (dist > radius)
-                radius = dist;
-        }
-    }
-    return std::make_pair(center, radius);
+void scale(SurfaceMesh &mesh, double s) {
+  Vector v = Vector();
+  v[0] = s;
+  v[1] = s;
+  v[2] = s;
+  scale(mesh, v);
 }
 
-void centeralize(SurfaceMesh& mesh)
-{
-    Vector center;
-    double radius;
-    std::tie(center, radius) = getCenterRadius(mesh);
-    translate(mesh, -center);
-}
+std::pair getCenterRadius(SurfaceMesh &mesh) {
+  Vector center = Vector();
+  double radius = 0;
+  if (mesh.size<1>() > 0) {
+    for (auto vertexID : mesh.get_level_id<1>()) {
+      center += *vertexID;
+    }
+    center /= static_cast(mesh.size<1>());
 
-void normalSmooth(SurfaceMesh& mesh, double k)
-{
-    for (auto nid : mesh.get_level_id<1>())
-    {
-        surfacemesh_detail::normalSmoothH(mesh, nid, k);
+    for (auto vertexID : mesh.get_level_id<1>()) {
+      Vector tmp = *vertexID - center;
+      double dist = std::sqrt(tmp | tmp);
+      if (dist > radius)
+        radius = dist;
     }
-    double min, max;
-    int nSmall, nLarge;
-    std::tie(min, max, nSmall, nLarge) = getMinMaxAngles(mesh, 15, 150);
-    std::cout << "  Min Angle: " << min << ", Max Angle: " << max
-              << ", # Small Angles: " << nSmall
-              << ", # Large Angles: " << nLarge << std::endl;
+  }
+  return std::make_pair(center, radius);
 }
 
-tensor getTangent(const SurfaceMesh& mesh, SurfaceMesh::SimplexID<1> vertexID)
-{
-    return surfacemesh_detail::getTangentH(mesh, (*vertexID).position, vertexID);
+void centeralize(SurfaceMesh &mesh) {
+  Vector center;
+  double radius;
+  std::tie(center, radius) = getCenterRadius(mesh);
+  translate(mesh, -center);
 }
 
-tensor getTangent(const SurfaceMesh& mesh, SurfaceMesh::SimplexID<3> faceID)
-{
-    auto cover = mesh.get_name(faceID);
-    auto vertexID = mesh.get_simplex_up({cover[0]});
-    std::set next(std::begin(cover)+1, std::end(cover));
-    return surfacemesh_detail::getTangentF(mesh, (*vertexID).position, vertexID, next);
+void normalSmooth(SurfaceMesh &mesh, double k) {
+  for (auto nid : mesh.get_level_id<1>()) {
+    if (mesh.onBoundary(nid)) continue;
+    surfacemesh_detail::normalSmoothH(mesh, nid, k);
+  }
+  double min, max;
+  int nSmall, nLarge;
+  std::tie(min, max, nSmall, nLarge) = getMinMaxAngles(mesh, 15, 150);
+  std::cout << "  Min Angle: " << min << ", Max Angle: " << max
+            << ", # Small Angles: " << nSmall << ", # Large Angles: " << nLarge
+            << std::endl;
 }
 
-Vector getNormalFromTangent(const tensor tangent)
-{
-    Vector xp;
-    // upper right...
-    xp[0] = -tangent.get(1, 2);  // x
-    xp[1] = tangent.get(0, 2);   // y
-    xp[2] = -tangent.get(0, 1);  // z
-    // lower left...
-    // xp[0] = tangent.get(2,1);
-    // xp[1] = -tangent.get(2,0);
-    // xp[2] = tangent.get(1,0);
-    return xp;
+tensor getTangent(const SurfaceMesh &mesh,
+                                SurfaceMesh::SimplexID<1> vertexID) {
+  return surfacemesh_detail::getTangentH(mesh, (*vertexID).position, vertexID);
 }
 
-Vector getNormal(const SurfaceMesh& mesh, SurfaceMesh::SimplexID<1> vertexID)
-{
-    Vector norm;
-    auto faces = mesh.up(mesh.up(vertexID));
-    for (auto faceID : faces)
-    {
-        norm += getNormal(mesh, faceID);
-    }
-    // norm /= faces.size();
-    return norm;
+tensor getTangent(const SurfaceMesh &mesh,
+                                SurfaceMesh::SimplexID<3> faceID) {
+  auto cover = mesh.get_name(faceID);
+  auto vertexID = mesh.get_simplex_up({cover[0]});
+  std::set next(std::begin(cover) + 1, std::end(cover));
+  return surfacemesh_detail::getTangentF(mesh, (*vertexID).position, vertexID,
+                                         next);
 }
 
-Vector getNormal(const SurfaceMesh& mesh, SurfaceMesh::SimplexID<3> faceID)
-{
-    Vector norm;
-    auto name = mesh.get_name(faceID);
-
-    // Get the three vertices
-    auto a = *mesh.get_simplex_up({name[0]});
-    auto b = *mesh.get_simplex_up({name[1]});
-    auto c = *mesh.get_simplex_up({name[2]});
+Vector getNormalFromTangent(const tensor tangent) {
+  Vector xp;
+  // upper right...
+  xp[0] = -tangent.get(1, 2); // x
+  xp[1] = tangent.get(0, 2);  // y
+  xp[2] = -tangent.get(0, 1); // z
+  // lower left...
+  // xp[0] = tangent.get(2,1);
+  // xp[1] = -tangent.get(2,0);
+  // xp[2] = tangent.get(1,0);
+  return xp;
+}
 
-    if ((*faceID).orientation == 1)
-    {
-        norm = cross(c-b, a-b);
-    }
-    else if ((*faceID).orientation == -1)
-    {
-        norm = cross(a-b, c-b);
-    }
-    else
-    {
-        // std::cerr << "ERROR(getNormal): Orientation undefined, cannot compute
-        // "
-        // << "normal. Did you call compute_orientation()?" << std::endl;
-        throw std::runtime_error("ERROR(getNormal): Orientation undefined, cannot compute normal. Did you call compute_orientation()?");
-    }
-    return norm;
+Vector getNormal(const SurfaceMesh &mesh, SurfaceMesh::SimplexID<1> vertexID) {
+  Vector norm;
+  auto faces = mesh.up(mesh.up(vertexID));
+  for (auto faceID : faces) {
+    norm += getNormal(mesh, faceID);
+  }
+  // norm /= faces.size();
+  return norm;
 }
 
+Vector getNormal(const SurfaceMesh &mesh, SurfaceMesh::SimplexID<3> faceID) {
+  Vector norm;
+  auto name = mesh.get_name(faceID);
+
+  // Get the three vertices
+  auto a = *mesh.get_simplex_up({name[0]});
+  auto b = *mesh.get_simplex_up({name[1]});
+  auto c = *mesh.get_simplex_up({name[2]});
+
+  if ((*faceID).orientation == 1) {
+    norm = cross(c - b, a - b);
+  } else if ((*faceID).orientation == -1) {
+    norm = cross(a - b, c - b);
+  } else {
+    gamer_runtime_error("Orientation undefined, cannot compute normal. Did "
+        "you call compute_orientation()?");
+  }
+  return norm;
+}
 
-void smoothMesh(SurfaceMesh& mesh, int maxIter, bool preserveRidges, std::size_t rings, bool verbose)
-{
-    double maxMinAngle = 15;
-    double minMaxAngle = 165;
-    double minAngle, maxAngle;
-    int nSmall, nLarge;
-    int nIter = 1;
-
-    if (verbose)
-    {
-        std::tie(minAngle, maxAngle, nSmall, nLarge) = getMinMaxAngles(mesh, maxMinAngle, minMaxAngle);
-        std::cout << "Initial Quality: Min Angle = " << minAngle << ", "
-                  << "Max Angle = " << maxAngle << ", "
-                  << "# smaller-than-" << maxMinAngle << " = " << nSmall << ", "
-                  << "# larger-than-" << minMaxAngle << " = " << nLarge << std::endl;
+void smoothMesh(SurfaceMesh &mesh, int maxIter, bool preserveRidges,
+                std::size_t rings, bool verbose) {
+  double maxMinAngle = 15;
+  double minMaxAngle = 165;
+  double minAngle, maxAngle;
+  int nSmall, nLarge;
+  int nIter = 1;
+
+  if (verbose) {
+    std::tie(minAngle, maxAngle, nSmall, nLarge) =
+        getMinMaxAngles(mesh, maxMinAngle, minMaxAngle);
+    std::cout << "Initial Quality: Min Angle = " << minAngle << ", "
+              << "Max Angle = " << maxAngle << ", "
+              << "# smaller-than-" << maxMinAngle << " = " << nSmall << ", "
+              << "# larger-than-" << minMaxAngle << " = " << nLarge
+              << std::endl;
+  }
+
+  std::vector, Vector>> delta;
+
+  // Cache normals before entering loop
+  cacheNormals(mesh);
+  for (int nIter = 1; nIter <= maxIter; ++nIter) {
+    for (auto vertex : mesh.get_level_id<1>()) {
+      if ((*vertex).selected == true) {
+        // surfacemesh_detail::weightedVertexSmooth(mesh, vertex,
+        // rings);
+        delta.push_back(std::make_pair(
+            vertex, surfacemesh_detail::weightedVertexSmoothCache(mesh, vertex,
+                                                                  rings)));
+      }
+      // barycenterVertexSmooth(mesh, vertex);
     }
 
-    std::vector, Vector>> delta;
-
-    // Cache normals before entering loop
+    for (auto pair : delta) {
+      *pair.first += pair.second;
+    }
+    delta.clear();
     cacheNormals(mesh);
-    for (int nIter = 1; nIter <= maxIter; ++nIter)
-    {
-        for (auto vertex : mesh.get_level_id<1>())
-        {
-            if ((*vertex).selected == true)
-            {
-                // surfacemesh_detail::weightedVertexSmooth(mesh, vertex,
-                // rings);
-                delta.push_back(std::make_pair(vertex, surfacemesh_detail::weightedVertexSmoothCache(mesh, vertex, rings)));
-            }
-            //barycenterVertexSmooth(mesh, vertex);
-        }
 
-        for (auto pair : delta) {
-            *pair.first += pair.second;
-        }
-        delta.clear();
-        cacheNormals(mesh);
-
-        // ATOMIC EDGE FLIP
-        std::vector> edgesToFlip;
-        // Get set of good, non-interfering edges to flip according to the
-        // Angle based criteria.
-        surfacemesh_detail::selectFlipEdges(mesh,
-                                            preserveRidges,
-                                            surfacemesh_detail::checkFlipAngle,
-                                            std::back_inserter(edgesToFlip));
-        for (auto edgeID : edgesToFlip)
-        {
-            surfacemesh_detail::edgeFlipCache(mesh, edgeID);
-        }
+    // ATOMIC EDGE FLIP
+    std::vector> edgesToFlip;
+    // Get set of good, non-interfering edges to flip according to the
+    // Angle based criteria.
+    surfacemesh_detail::selectFlipEdges(mesh, preserveRidges,
+                                        surfacemesh_detail::checkFlipAngle,
+                                        std::back_inserter(edgesToFlip));
+    for (auto edgeID : edgesToFlip) {
+      surfacemesh_detail::edgeFlipCache(mesh, edgeID);
+    }
 
-        if (verbose)
-        {
-            std::tie(minAngle, maxAngle, nSmall, nLarge) = getMinMaxAngles(mesh, maxMinAngle, minMaxAngle);
-            std::cout << "Iteration " << nIter << ":" << std::endl;
-            std::cout << "Min Angle = " << minAngle << ", "
-                      << "Max Angle = " << maxAngle << ", "
-                      << "# smaller-than-" << maxMinAngle << " = " << nSmall << ", "
-                      << "# larger-than-" << minMaxAngle << " = " << nLarge << std::endl;
-        }
+    if (verbose) {
+      std::tie(minAngle, maxAngle, nSmall, nLarge) =
+          getMinMaxAngles(mesh, maxMinAngle, minMaxAngle);
+      std::cout << "Iteration " << nIter << ":" << std::endl;
+      std::cout << "Min Angle = " << minAngle << ", "
+                << "Max Angle = " << maxAngle << ", "
+                << "# smaller-than-" << maxMinAngle << " = " << nSmall << ", "
+                << "# larger-than-" << minMaxAngle << " = " << nLarge
+                << std::endl;
     }
+  }
 }
 
 /**
@@ -555,618 +487,584 @@ void smoothMesh(SurfaceMesh& mesh, int maxIter, bool preserveRidges, std::size_t
  *
  * @param      mesh  The mesh
  */
-std::unique_ptr refineMesh(const SurfaceMesh& mesh)
-{
-    std::unique_ptr refinedMesh(new SurfaceMesh);
-
-    // Copy over vertices to refinedMesh
-    for (auto vertex : mesh.get_level_id<1>())
-    {
-        auto key = mesh.get_name(vertex);
-        refinedMesh->insert(key, *vertex);
-    }
-
-    // Split edges and generate a map of names before to after
-    std::map, int> edgeMap;
-
-    for (auto edge : mesh.get_level_id<2>())
-    {
-        auto edgeName = mesh.get_name(edge);
-        Vector v1 = (*mesh.get_simplex_up({edgeName[0]})).position;
-        Vector v2 = (*mesh.get_simplex_up({edgeName[1]})).position;
+std::unique_ptr refineMesh(const SurfaceMesh &mesh) {
+  std::unique_ptr refinedMesh(new SurfaceMesh);
+
+  // Copy over vertices to refinedMesh
+  for (auto vertex : mesh.get_level_id<1>()) {
+    auto key = mesh.get_name(vertex);
+    refinedMesh->insert(key, *vertex);
+  }
+
+  // Split edges and generate a map of names before to after
+  std::map, int> edgeMap;
+
+  for (auto edge : mesh.get_level_id<2>()) {
+    auto edgeName = mesh.get_name(edge);
+    Vector v1 = (*mesh.get_simplex_up({edgeName[0]})).position;
+    Vector v2 = (*mesh.get_simplex_up({edgeName[1]})).position;
+
+    auto newVertex =
+        refinedMesh->add_vertex(SMVertex(std::move(0.5 * (v1 + v2))));
+    edgeMap.emplace(std::make_pair(edgeName, newVertex));
+  }
+
+  // Connect edges and face and copy data
+  for (auto face : mesh.get_level_id<3>()) {
+    auto name = mesh.get_name(face);
+    int a, b, c;
+
+    // Skip checking if found
+    auto it = edgeMap.find({name[0], name[1]});
+    a = it->second;
+
+    it = edgeMap.find({name[1], name[2]});
+    b = it->second;
+
+    it = edgeMap.find({name[0], name[2]});
+    c = it->second;
+
+    refinedMesh->insert({name[0], a});
+    refinedMesh->insert({a, name[1]});
+
+    refinedMesh->insert({name[1], b});
+    refinedMesh->insert({b, name[2]});
+
+    refinedMesh->insert({name[0], c});
+    refinedMesh->insert({c, name[2]});
+
+    refinedMesh->insert({a, b, c});
+    refinedMesh->insert({name[0], a, c}, *face);
+    refinedMesh->insert({name[1], a, b}, *face);
+    refinedMesh->insert({name[2], b, c}, *face);
+  }
+  return refinedMesh;
+}
 
-        auto newVertex = refinedMesh->add_vertex(SMVertex(std::move(0.5*(v1+v2))));
-        edgeMap.emplace(std::make_pair(edgeName, newVertex));
+void coarse(SurfaceMesh &mesh, double coarseRate, double flatRate,
+            double denseWeight, std::size_t rings, bool verbose) {
+  // TODO: Check if all polygons are closed (0)
+
+  // Compute the average edge length
+  double avgLen = 0;
+  if (denseWeight > 0) {
+    for (auto edgeID : mesh.get_level_id<2>()) {
+      auto name = mesh.get_name(edgeID);
+      auto v = *mesh.get_simplex_down(edgeID, name[0]) -
+               *mesh.get_simplex_down(edgeID, name[1]);
+      avgLen += length(v);
     }
+    avgLen /= static_cast(mesh.size<2>());
+  }
 
-    // Connect edges and face and copy data
-    for (auto face : mesh.get_level_id<3>())
-    {
-        auto name = mesh.get_name(face);
-        int a, b, c;
-
-        // Skip checking if found
-        auto it = edgeMap.find({name[0], name[1]});
-        a = it->second;
-
-        it = edgeMap.find({name[1], name[2]});
-        b  = it->second;
+  double sparsenessRatio = 1;
+  double flatnessRatio = 1;
 
-        it = edgeMap.find({name[0], name[2]});
-        c  = it->second;
+  auto range = mesh.get_level_id<1>();
 
-        refinedMesh->insert({name[0], a});
-        refinedMesh->insert({a, name[1]});
+  for (auto vertexIDIT = range.begin(); vertexIDIT != range.end();) {
+    // Immediately cache vertexID and increment IT so destruction of
+    // vertexID won't invalidate the iterator.
+    auto vertexID = *vertexIDIT;
+    ++vertexIDIT;
 
-        refinedMesh->insert({name[1], b});
-        refinedMesh->insert({b, name[2]});
-
-        refinedMesh->insert({name[0], c});
-        refinedMesh->insert({c, name[2]});
-
-        refinedMesh->insert({a, b, c});
-        refinedMesh->insert({name[0], a, c}, *face);
-        refinedMesh->insert({name[1], a, b}, *face);
-        refinedMesh->insert({name[2], b, c}, *face);
+    if ((*vertexID).selected == false) {
+      continue;
     }
-    return refinedMesh;
-}
-
 
-void coarse(SurfaceMesh& mesh, double coarseRate, double flatRate, double denseWeight, std::size_t rings, bool verbose)
-{
-    // TODO: Check if all polygons are closed (0)
-
-    // Compute the average edge length
-    double avgLen = 0;
-    if (denseWeight > 0)
-    {
-        for (auto edgeID : mesh.get_level_id<2>())
-        {
-            auto name =  mesh.get_name(edgeID);
-            auto v = *mesh.get_simplex_down(edgeID, name[0])
-                     - *mesh.get_simplex_down(edgeID, name[1]);
-            avgLen += length(v);
-        }
-        avgLen /= static_cast(mesh.size<2>());
+    // Sparseness as coarsening criteria
+    if (denseWeight > 0) {
+      // Get max length of edges.
+      auto edges = mesh.up(vertexID);
+      double maxLen = 0;
+      for (auto edgeID : edges) {
+        auto name = mesh.get_name(edgeID);
+        auto v = *mesh.get_simplex_down(edgeID, name[0]) -
+                 *mesh.get_simplex_down(edgeID, name[1]);
+        double tmpLen = std::sqrt(v | v);
+        if (tmpLen > maxLen)
+          maxLen = tmpLen;
+      }
+      sparsenessRatio = std::pow(maxLen / avgLen, denseWeight);
     }
 
-    double sparsenessRatio = 1;
-    double flatnessRatio   = 1;
-
-    auto range = mesh.get_level_id<1>();
-
-    for (auto vertexIDIT = range.begin(); vertexIDIT != range.end();)
-    {
-        // Immediately cache vertexID and increment IT so destruction of
-        // vertexID won't invalidate the iterator.
-        auto vertexID = *vertexIDIT;
-        ++vertexIDIT;
-
-        if ((*vertexID).selected == false)
-        {
-            continue;
-        }
-
-        // Sparseness as coarsening criteria
-        if (denseWeight > 0)
-        {
-            // Get max length of edges.
-            auto edges  = mesh.up(vertexID);
-            double maxLen = 0;
-            for (auto edgeID : edges)
-            {
-                auto name =  mesh.get_name(edgeID);
-                auto v = *mesh.get_simplex_down(edgeID, name[0])
-                         - *mesh.get_simplex_down(edgeID, name[1]);
-                double tmpLen = std::sqrt(v|v);
-                if (tmpLen > maxLen)
-                    maxLen = tmpLen;
-            }
-            sparsenessRatio = std::pow(maxLen/avgLen, denseWeight);
-        }
+    // Curvature as coarsening criteria
+    if (flatRate > 0) {
+      auto lst = surfacemesh_detail::computeLocalStructureTensor(mesh, vertexID,
+                                                                 rings);
 
-        // Curvature as coarsening criteria
-        if (flatRate > 0)
-        {
-            auto lst = surfacemesh_detail::computeLocalStructureTensor(mesh, vertexID, rings);
+      EigenVector eigenvalues;
+      EigenMatrix eigenvectors;
 
-            EigenVector eigenvalues;
-            EigenMatrix eigenvectors;
+      EigenDiagonalizeTraits::diagonalizeSelfAdjointMatrix(
+          lst, eigenvalues, eigenvectors);
 
-            EigenDiagonalizeTraits::diagonalizeSelfAdjointMatrix(lst, eigenvalues, eigenvectors);
-
-            // The closer this ratio is to 0 the flatter the local region.
-            flatnessRatio = std::pow(eigenvalues[1]/eigenvalues[2], flatRate);
-        }
+      // The closer this ratio is to 0 the flatter the local region.
+      flatnessRatio = std::pow(eigenvalues[1] / eigenvalues[2], flatRate);
+    }
 
-        // Add vertex to delete list
-        if (sparsenessRatio * flatnessRatio < coarseRate)
-        {
-            surfacemesh_detail::decimateVertex(mesh, vertexID);
-        }
+    // Add vertex to delete list
+    if (sparsenessRatio * flatnessRatio < coarseRate) {
+      surfacemesh_detail::decimateVertex(mesh, vertexID);
     }
+  }
 }
 
-void coarse_dense(SurfaceMesh& mesh, REAL threshold, REAL weight, std::size_t rings, bool verbose)
-{
-    // Compute the average edge length
-    REAL avgLen = 0;
-    for (auto edgeID : mesh.get_level_id<2>())
-    {
-        auto name =  mesh.get_name(edgeID);
-        auto v = *mesh.get_simplex_down(edgeID, name[0])
-                 - *mesh.get_simplex_down(edgeID, name[1]);
-        avgLen += length(v);
+void coarse_dense(SurfaceMesh &mesh, REAL threshold, REAL weight,
+                  std::size_t rings, bool verbose) {
+  // Compute the average edge length
+  REAL avgLen = 0;
+  for (auto edgeID : mesh.get_level_id<2>()) {
+    auto name = mesh.get_name(edgeID);
+    auto v = *mesh.get_simplex_down(edgeID, name[0]) -
+             *mesh.get_simplex_down(edgeID, name[1]);
+    avgLen += length(v);
+  }
+  avgLen /= static_cast(mesh.size<2>());
+
+  REAL sparsenessRatio = 1;
+
+  auto range = mesh.get_level_id<1>();
+  for (auto vertexIDIT = range.begin(); vertexIDIT != range.end();) {
+    // Immediately cache vertexID and increment IT so destruction of
+    // vertexID won't invalidate the iterator.
+    auto vertexID = *vertexIDIT;
+    ++vertexIDIT;
+
+    if ((*vertexID).selected == false) {
+      continue;
     }
-    avgLen /= static_cast(mesh.size<2>());
-
-    REAL sparsenessRatio = 1;
 
-    auto range = mesh.get_level_id<1>();
-    for (auto vertexIDIT = range.begin(); vertexIDIT != range.end();)
-    {
-        // Immediately cache vertexID and increment IT so destruction of
-        // vertexID won't invalidate the iterator.
-        auto vertexID = *vertexIDIT;
-        ++vertexIDIT;
-
-        if ((*vertexID).selected == false)
-        {
-            continue;
-        }
-
-        // Get max length of edges.
-        auto edges  = mesh.up(vertexID);
-        REAL maxLen = 0;
-        for (auto edgeID : edges)
-        {
-            auto name =  mesh.get_name(edgeID);
-            auto v = *mesh.get_simplex_down(edgeID, name[0])
-                     - *mesh.get_simplex_down(edgeID, name[1]);
-            REAL tmpLen = std::sqrt(v|v);
-            if (tmpLen > maxLen)
-                maxLen = tmpLen;
-        }
-        sparsenessRatio = std::pow(maxLen/avgLen, weight);
+    // Get max length of edges.
+    auto edges = mesh.up(vertexID);
+    REAL maxLen = 0;
+    for (auto edgeID : edges) {
+      auto name = mesh.get_name(edgeID);
+      auto v = *mesh.get_simplex_down(edgeID, name[0]) -
+               *mesh.get_simplex_down(edgeID, name[1]);
+      REAL tmpLen = std::sqrt(v | v);
+      if (tmpLen > maxLen)
+        maxLen = tmpLen;
+    }
+    sparsenessRatio = std::pow(maxLen / avgLen, weight);
 
-        // Decimate if under the threshold
-        if (sparsenessRatio < threshold)
-        {
-            surfacemesh_detail::decimateVertex(mesh, vertexID, rings);
-        }
+    // Decimate if under the threshold
+    if (sparsenessRatio < threshold) {
+      surfacemesh_detail::decimateVertex(mesh, vertexID, rings);
     }
+  }
 }
 
-void coarse_flat(SurfaceMesh& mesh, REAL threshold, REAL weight, std::size_t rings, bool verbose){
-    REAL flatnessRatio = 1;
+void coarse_flat(SurfaceMesh &mesh, REAL threshold, REAL weight,
+                 std::size_t rings, bool verbose) {
+  REAL flatnessRatio = 1;
 
-    auto range = mesh.get_level_id<1>();
-    for (auto vertexIDIT = range.begin(); vertexIDIT != range.end();)
-    {
-        // Immediately cache vertexID and increment IT so destruction of
-        // vertexID won't invalidate the iterator.
-        auto vertexID = *vertexIDIT;
-        ++vertexIDIT;
+  auto range = mesh.get_level_id<1>();
+  for (auto vertexIDIT = range.begin(); vertexIDIT != range.end();) {
+    // Immediately cache vertexID and increment IT so destruction of
+    // vertexID won't invalidate the iterator.
+    auto vertexID = *vertexIDIT;
+    ++vertexIDIT;
 
-        if ((*vertexID).selected == false)
-        {
-            continue;
-        }
+    if ((*vertexID).selected == false) {
+      continue;
+    }
 
-        // Curvature as coarsening criteria
-        auto lst = surfacemesh_detail::computeLocalStructureTensor(mesh, vertexID, rings);
+    // Curvature as coarsening criteria
+    auto lst =
+        surfacemesh_detail::computeLocalStructureTensor(mesh, vertexID, rings);
 
-        EigenVector eigenvalues;
-        EigenMatrix eigenvectors;
+    EigenVector eigenvalues;
+    EigenMatrix eigenvectors;
 
-        EigenDiagonalizeTraits::diagonalizeSelfAdjointMatrix(lst, eigenvalues, eigenvectors);
+    EigenDiagonalizeTraits::diagonalizeSelfAdjointMatrix(
+        lst, eigenvalues, eigenvectors);
 
-        // The closer this ratio is to 0 the flatter the local region.
-        flatnessRatio = std::pow(eigenvalues[1]/eigenvalues[2], weight);
+    // The closer this ratio is to 0 the flatter the local region.
+    flatnessRatio = std::pow(eigenvalues[1] / eigenvalues[2], weight);
 
-        // Add vertex to delete list
-        if (flatnessRatio < threshold)
-        {
-            surfacemesh_detail::decimateVertex(mesh, vertexID);
-        }
+    // Add vertex to delete list
+    if (flatnessRatio < threshold) {
+      surfacemesh_detail::decimateVertex(mesh, vertexID);
     }
+  }
 }
 
+void fillHoles(SurfaceMesh &mesh) {
+  std::vector>> holeList;
+  surfacemesh_detail::findHoles(mesh, holeList);
 
-void fillHoles(SurfaceMesh& mesh)
-{
-    std::vector>> holeList;
-    surfacemesh_detail::findHoles(mesh, holeList);
-
-    for (auto& holeEdges : holeList)
-    {
-        std::vector> sortedVertices;
-        surfacemesh_detail::edgeRingToVertices(mesh, holeEdges, std::back_inserter(sortedVertices));
+  for (auto &holeEdges : holeList) {
+    std::vector> sortedVertices;
+    surfacemesh_detail::edgeRingToVertices(mesh, holeEdges,
+                                           std::back_inserter(sortedVertices));
 
-        surfacemesh_detail::triangulateHole(mesh, sortedVertices, SMFace(), holeEdges);
-    }
+    surfacemesh_detail::triangulateHole(mesh, sortedVertices, SMFace(),
+                                        holeEdges);
+  }
 }
 
-void flipNormals(SurfaceMesh& mesh)
-{
-    for (auto& fdata : mesh.get_level<3>())
-    {
-        fdata.orientation *= -1;
-    }
+void flipNormals(SurfaceMesh &mesh) {
+  for (auto &fdata : mesh.get_level<3>()) {
+    fdata.orientation *= -1;
+  }
 }
 
-bool hasHole(const SurfaceMesh& mesh)
-{
-    for (auto eID : mesh.get_level_id<2>())
-    {
-        auto cover = mesh.get_cover(eID);
-        if (cover.size() != 2)
-        {
-            return true;
-        }
+bool hasHole(const SurfaceMesh &mesh) {
+  for (auto eID : mesh.get_level_id<2>()) {
+    auto cover = mesh.get_cover(eID);
+    if (cover.size() != 2) {
+      return true;
     }
-    return false;
+  }
+  return false;
 }
 
-std::unique_ptr sphere(int order)
-{
-    std::unique_ptr mesh(new SurfaceMesh);
-
-    mesh->insert({0}, SMVertex(0, 0, -1));
-    mesh->insert({1}, SMVertex(0.723607, -0.525725, -0.44722));
-    mesh->insert({2}, SMVertex(-0.276388, -0.850649, -0.44722));
-    mesh->insert({3}, SMVertex(-0.894426, 0, -0.447216));
-    mesh->insert({4}, SMVertex(-0.276388, 0.850649, -0.44722));
-    mesh->insert({5}, SMVertex(0.723607, 0.525725, -0.44722));
-    mesh->insert({6}, SMVertex(0.276388, -0.850649, 0.44722));
-    mesh->insert({7}, SMVertex(-0.723607, -0.525725, 0.44722));
-    mesh->insert({8}, SMVertex(-0.723607, 0.525725, 0.44722));
-    mesh->insert({9}, SMVertex(0.276388, 0.850649, 0.44722));
-    mesh->insert({10}, SMVertex(0.894426, 0, 0.447216));
-    mesh->insert({11}, SMVertex(0, 0, 1));
-    mesh->insert({12}, SMVertex(-0.162456, -0.499995, -0.850654));
-    mesh->insert({13}, SMVertex(0.425323, -0.309011, -0.850654));
-    mesh->insert({14}, SMVertex(0.262869, -0.809012, -0.525738));
-    mesh->insert({15}, SMVertex(0.850648, 0, -0.525736));
-    mesh->insert({16}, SMVertex(0.425323, 0.309011, -0.850654));
-    mesh->insert({17}, SMVertex(-0.52573, 0, -0.850652));
-    mesh->insert({18}, SMVertex(-0.688189, -0.499997, -0.525736));
-    mesh->insert({19}, SMVertex(-0.162456, 0.499995, -0.850654));
-    mesh->insert({20}, SMVertex(-0.688189, 0.499997, -0.525736));
-    mesh->insert({21}, SMVertex(0.262869, 0.809012, -0.525738));
-    mesh->insert({22}, SMVertex(0.951058, -0.309013, 0));
-    mesh->insert({23}, SMVertex(0.951058, 0.309013, 0));
-    mesh->insert({24}, SMVertex(0, -1, 0));
-    mesh->insert({25}, SMVertex(0.587786, -0.809017, 0));
-    mesh->insert({26}, SMVertex(-0.951058, -0.309013, 0));
-    mesh->insert({27}, SMVertex(-0.587786, -0.809017, 0));
-    mesh->insert({28}, SMVertex(-0.587786, 0.809017, 0));
-    mesh->insert({29}, SMVertex(-0.951058, 0.309013, 0));
-    mesh->insert({30}, SMVertex(0.587786, 0.809017, 0));
-    mesh->insert({31}, SMVertex(0, 1, 0));
-    mesh->insert({32}, SMVertex(0.688189, -0.499997, 0.525736));
-    mesh->insert({33}, SMVertex(-0.262869, -0.809012, 0.525738));
-    mesh->insert({34}, SMVertex(-0.850648, 0, 0.525736));
-    mesh->insert({35}, SMVertex(-0.262869, 0.809012, 0.525738));
-    mesh->insert({36}, SMVertex(0.688189, 0.499997, 0.525736));
-    mesh->insert({37}, SMVertex(0.162456, -0.499995, 0.850654));
-    mesh->insert({38}, SMVertex(0.52573, 0, 0.850652));
-    mesh->insert({39}, SMVertex(-0.425323, -0.309011, 0.850654));
-    mesh->insert({40}, SMVertex(-0.425323, 0.309011, 0.850654));
-    mesh->insert({41}, SMVertex(0.162456, 0.499995, 0.850654));
-    mesh->insert({0, 12, 13});
-    mesh->insert({1, 13, 15});
-    mesh->insert({0, 12, 17});
-    mesh->insert({0, 17, 19});
-    mesh->insert({0, 16, 19});
-    mesh->insert({1, 15, 22});
-    mesh->insert({2, 14, 24});
-    mesh->insert({3, 18, 26});
-    mesh->insert({4, 20, 28});
-    mesh->insert({5, 21, 30});
-    mesh->insert({1, 22, 25});
-    mesh->insert({2, 24, 27});
-    mesh->insert({3, 26, 29});
-    mesh->insert({4, 28, 31});
-    mesh->insert({5, 23, 30});
-    mesh->insert({6, 32, 37});
-    mesh->insert({7, 33, 39});
-    mesh->insert({8, 34, 40});
-    mesh->insert({9, 35, 41});
-    mesh->insert({10, 36, 38});
-    mesh->insert({11, 38, 41});
-    mesh->insert({36, 38, 41});
-    mesh->insert({9, 36, 41});
-    mesh->insert({11, 40, 41});
-    mesh->insert({35, 40, 41});
-    mesh->insert({8, 35, 40});
-    mesh->insert({11, 39, 40});
-    mesh->insert({34, 39, 40});
-    mesh->insert({7, 34, 39});
-    mesh->insert({11, 37, 39});
-    mesh->insert({33, 37, 39});
-    mesh->insert({6, 33, 37});
-    mesh->insert({11, 37, 38});
-    mesh->insert({32, 37, 38});
-    mesh->insert({10, 32, 38});
-    mesh->insert({10, 23, 36});
-    mesh->insert({23, 30, 36});
-    mesh->insert({9, 30, 36});
-    mesh->insert({9, 31, 35});
-    mesh->insert({28, 31, 35});
-    mesh->insert({8, 28, 35});
-    mesh->insert({8, 29, 34});
-    mesh->insert({26, 29, 34});
-    mesh->insert({7, 26, 34});
-    mesh->insert({7, 27, 33});
-    mesh->insert({24, 27, 33});
-    mesh->insert({6, 24, 33});
-    mesh->insert({6, 25, 32});
-    mesh->insert({22, 25, 32});
-    mesh->insert({10, 22, 32});
-    mesh->insert({9, 30, 31});
-    mesh->insert({21, 30, 31});
-    mesh->insert({4, 21, 31});
-    mesh->insert({8, 28, 29});
-    mesh->insert({20, 28, 29});
-    mesh->insert({3, 20, 29});
-    mesh->insert({7, 26, 27});
-    mesh->insert({18, 26, 27});
-    mesh->insert({2, 18, 27});
-    mesh->insert({6, 24, 25});
-    mesh->insert({14, 24, 25});
-    mesh->insert({1, 14, 25});
-    mesh->insert({10, 22, 23});
-    mesh->insert({15, 22, 23});
-    mesh->insert({5, 15, 23});
-    mesh->insert({5, 16, 21});
-    mesh->insert({16, 19, 21});
-    mesh->insert({4, 19, 21});
-    mesh->insert({4, 19, 20});
-    mesh->insert({17, 19, 20});
-    mesh->insert({3, 17, 20});
-    mesh->insert({3, 17, 18});
-    mesh->insert({12, 17, 18});
-    mesh->insert({2, 12, 18});
-    mesh->insert({5, 15, 16});
-    mesh->insert({13, 15, 16});
-    mesh->insert({0, 13, 16});
-    mesh->insert({2, 12, 14});
-    mesh->insert({12, 13, 14});
-    mesh->insert({1, 13, 14});
-
-    for (int i = 1; i < order; ++i)
-    {
-        mesh = refineMesh(*mesh);
-    }
-
-    casc::compute_orientation(*mesh);
-    if (getVolume(*mesh) < 0)
-    {
-        for (auto& data : mesh->get_level<3>())
-            data.orientation *= -1;
-    }
-    return mesh;
+std::unique_ptr sphere(int order) {
+  std::unique_ptr mesh(new SurfaceMesh);
+
+  mesh->insert({0}, SMVertex(0, 0, -1));
+  mesh->insert({1}, SMVertex(0.723607, -0.525725, -0.44722));
+  mesh->insert({2}, SMVertex(-0.276388, -0.850649, -0.44722));
+  mesh->insert({3}, SMVertex(-0.894426, 0, -0.447216));
+  mesh->insert({4}, SMVertex(-0.276388, 0.850649, -0.44722));
+  mesh->insert({5}, SMVertex(0.723607, 0.525725, -0.44722));
+  mesh->insert({6}, SMVertex(0.276388, -0.850649, 0.44722));
+  mesh->insert({7}, SMVertex(-0.723607, -0.525725, 0.44722));
+  mesh->insert({8}, SMVertex(-0.723607, 0.525725, 0.44722));
+  mesh->insert({9}, SMVertex(0.276388, 0.850649, 0.44722));
+  mesh->insert({10}, SMVertex(0.894426, 0, 0.447216));
+  mesh->insert({11}, SMVertex(0, 0, 1));
+  mesh->insert({12}, SMVertex(-0.162456, -0.499995, -0.850654));
+  mesh->insert({13}, SMVertex(0.425323, -0.309011, -0.850654));
+  mesh->insert({14}, SMVertex(0.262869, -0.809012, -0.525738));
+  mesh->insert({15}, SMVertex(0.850648, 0, -0.525736));
+  mesh->insert({16}, SMVertex(0.425323, 0.309011, -0.850654));
+  mesh->insert({17}, SMVertex(-0.52573, 0, -0.850652));
+  mesh->insert({18}, SMVertex(-0.688189, -0.499997, -0.525736));
+  mesh->insert({19}, SMVertex(-0.162456, 0.499995, -0.850654));
+  mesh->insert({20}, SMVertex(-0.688189, 0.499997, -0.525736));
+  mesh->insert({21}, SMVertex(0.262869, 0.809012, -0.525738));
+  mesh->insert({22}, SMVertex(0.951058, -0.309013, 0));
+  mesh->insert({23}, SMVertex(0.951058, 0.309013, 0));
+  mesh->insert({24}, SMVertex(0, -1, 0));
+  mesh->insert({25}, SMVertex(0.587786, -0.809017, 0));
+  mesh->insert({26}, SMVertex(-0.951058, -0.309013, 0));
+  mesh->insert({27}, SMVertex(-0.587786, -0.809017, 0));
+  mesh->insert({28}, SMVertex(-0.587786, 0.809017, 0));
+  mesh->insert({29}, SMVertex(-0.951058, 0.309013, 0));
+  mesh->insert({30}, SMVertex(0.587786, 0.809017, 0));
+  mesh->insert({31}, SMVertex(0, 1, 0));
+  mesh->insert({32}, SMVertex(0.688189, -0.499997, 0.525736));
+  mesh->insert({33}, SMVertex(-0.262869, -0.809012, 0.525738));
+  mesh->insert({34}, SMVertex(-0.850648, 0, 0.525736));
+  mesh->insert({35}, SMVertex(-0.262869, 0.809012, 0.525738));
+  mesh->insert({36}, SMVertex(0.688189, 0.499997, 0.525736));
+  mesh->insert({37}, SMVertex(0.162456, -0.499995, 0.850654));
+  mesh->insert({38}, SMVertex(0.52573, 0, 0.850652));
+  mesh->insert({39}, SMVertex(-0.425323, -0.309011, 0.850654));
+  mesh->insert({40}, SMVertex(-0.425323, 0.309011, 0.850654));
+  mesh->insert({41}, SMVertex(0.162456, 0.499995, 0.850654));
+  mesh->insert({0, 12, 13});
+  mesh->insert({1, 13, 15});
+  mesh->insert({0, 12, 17});
+  mesh->insert({0, 17, 19});
+  mesh->insert({0, 16, 19});
+  mesh->insert({1, 15, 22});
+  mesh->insert({2, 14, 24});
+  mesh->insert({3, 18, 26});
+  mesh->insert({4, 20, 28});
+  mesh->insert({5, 21, 30});
+  mesh->insert({1, 22, 25});
+  mesh->insert({2, 24, 27});
+  mesh->insert({3, 26, 29});
+  mesh->insert({4, 28, 31});
+  mesh->insert({5, 23, 30});
+  mesh->insert({6, 32, 37});
+  mesh->insert({7, 33, 39});
+  mesh->insert({8, 34, 40});
+  mesh->insert({9, 35, 41});
+  mesh->insert({10, 36, 38});
+  mesh->insert({11, 38, 41});
+  mesh->insert({36, 38, 41});
+  mesh->insert({9, 36, 41});
+  mesh->insert({11, 40, 41});
+  mesh->insert({35, 40, 41});
+  mesh->insert({8, 35, 40});
+  mesh->insert({11, 39, 40});
+  mesh->insert({34, 39, 40});
+  mesh->insert({7, 34, 39});
+  mesh->insert({11, 37, 39});
+  mesh->insert({33, 37, 39});
+  mesh->insert({6, 33, 37});
+  mesh->insert({11, 37, 38});
+  mesh->insert({32, 37, 38});
+  mesh->insert({10, 32, 38});
+  mesh->insert({10, 23, 36});
+  mesh->insert({23, 30, 36});
+  mesh->insert({9, 30, 36});
+  mesh->insert({9, 31, 35});
+  mesh->insert({28, 31, 35});
+  mesh->insert({8, 28, 35});
+  mesh->insert({8, 29, 34});
+  mesh->insert({26, 29, 34});
+  mesh->insert({7, 26, 34});
+  mesh->insert({7, 27, 33});
+  mesh->insert({24, 27, 33});
+  mesh->insert({6, 24, 33});
+  mesh->insert({6, 25, 32});
+  mesh->insert({22, 25, 32});
+  mesh->insert({10, 22, 32});
+  mesh->insert({9, 30, 31});
+  mesh->insert({21, 30, 31});
+  mesh->insert({4, 21, 31});
+  mesh->insert({8, 28, 29});
+  mesh->insert({20, 28, 29});
+  mesh->insert({3, 20, 29});
+  mesh->insert({7, 26, 27});
+  mesh->insert({18, 26, 27});
+  mesh->insert({2, 18, 27});
+  mesh->insert({6, 24, 25});
+  mesh->insert({14, 24, 25});
+  mesh->insert({1, 14, 25});
+  mesh->insert({10, 22, 23});
+  mesh->insert({15, 22, 23});
+  mesh->insert({5, 15, 23});
+  mesh->insert({5, 16, 21});
+  mesh->insert({16, 19, 21});
+  mesh->insert({4, 19, 21});
+  mesh->insert({4, 19, 20});
+  mesh->insert({17, 19, 20});
+  mesh->insert({3, 17, 20});
+  mesh->insert({3, 17, 18});
+  mesh->insert({12, 17, 18});
+  mesh->insert({2, 12, 18});
+  mesh->insert({5, 15, 16});
+  mesh->insert({13, 15, 16});
+  mesh->insert({0, 13, 16});
+  mesh->insert({2, 12, 14});
+  mesh->insert({12, 13, 14});
+  mesh->insert({1, 13, 14});
+
+  for (int i = 1; i < order; ++i) {
+    mesh = refineMesh(*mesh);
+  }
+
+  casc::compute_orientation(*mesh);
+  if (getVolume(*mesh) < 0) {
+    for (auto &data : mesh->get_level<3>())
+      data.orientation *= -1;
+  }
+  return mesh;
 }
 
-std::unique_ptr cube(int order)
-{
-    std::unique_ptr mesh(new SurfaceMesh);
+std::unique_ptr cube(int order) {
+  std::unique_ptr mesh(new SurfaceMesh);
 
-    mesh->insert({0}, SMVertex(-1, -1, -1));
-    mesh->insert({1}, SMVertex(-1, 1, -1));
-    mesh->insert({2}, SMVertex(1, 1, -1));
-    mesh->insert({3}, SMVertex(1, -1, -1));
+  mesh->insert({0}, SMVertex(-1, -1, -1));
+  mesh->insert({1}, SMVertex(-1, 1, -1));
+  mesh->insert({2}, SMVertex(1, 1, -1));
+  mesh->insert({3}, SMVertex(1, -1, -1));
 
-    mesh->insert({4}, SMVertex(-1, -1, 1));
-    mesh->insert({5}, SMVertex(-1, 1, 1));
-    mesh->insert({6}, SMVertex(1, 1, 1));
-    mesh->insert({7}, SMVertex(1, -1, 1));
+  mesh->insert({4}, SMVertex(-1, -1, 1));
+  mesh->insert({5}, SMVertex(-1, 1, 1));
+  mesh->insert({6}, SMVertex(1, 1, 1));
+  mesh->insert({7}, SMVertex(1, -1, 1));
 
-    mesh->insert({0, 1, 5});
-    mesh->insert({0, 4, 5});
+  mesh->insert({0, 1, 5});
+  mesh->insert({0, 4, 5});
 
-    mesh->insert({0, 3, 4});
-    mesh->insert({3, 4, 7});
+  mesh->insert({0, 3, 4});
+  mesh->insert({3, 4, 7});
 
-    mesh->insert({2, 3, 7});
-    mesh->insert({2, 6, 7});
+  mesh->insert({2, 3, 7});
+  mesh->insert({2, 6, 7});
 
-    mesh->insert({1, 6, 2});
-    mesh->insert({1, 5, 6});
+  mesh->insert({1, 6, 2});
+  mesh->insert({1, 5, 6});
 
-    mesh->insert({4, 5, 6});
-    mesh->insert({4, 6, 7});
+  mesh->insert({4, 5, 6});
+  mesh->insert({4, 6, 7});
 
-    mesh->insert({0, 1, 3});
-    mesh->insert({1, 2, 3});
+  mesh->insert({0, 1, 3});
+  mesh->insert({1, 2, 3});
 
-    for (int i = 1; i < order; ++i)
-    {
-        mesh = refineMesh(*mesh);
-    }
+  for (int i = 1; i < order; ++i) {
+    mesh = refineMesh(*mesh);
+  }
 
-    casc::compute_orientation(*mesh);
-    if (getVolume(*mesh) < 0)
-    {
-        for (auto& data : mesh->get_level<3>())
-            data.orientation *= -1;
-    }
-    return mesh;
+  casc::compute_orientation(*mesh);
+  if (getVolume(*mesh) < 0) {
+    for (auto &data : mesh->get_level<3>())
+      data.orientation *= -1;
+  }
+  return mesh;
 }
 
-std::vector> splitSurfaces(SurfaceMesh& mesh)
-{
-    // Queue to store the next edges to visit
-    std::deque>     frontier;
-    // Set of visited edges
-    std::set>       visited;
-
-    std::vector> meshes;
-
-    using SimplexSet = typename casc::SimplexSet;
-    SimplexSet surface;
-
-    int connected_components = 0;
-    for (auto edgeID : mesh.get_level_id<2>())
-    {
-        // This is a new edge if we haven't visited before
-        if (visited.find(edgeID) == visited.end())
-        {
-            ++connected_components;
-            frontier.push_back(edgeID);
-            surface.insert(edgeID);
-
-            // Traverse across connected edges.
-            while (!frontier.empty())
-            {
-                SurfaceMesh::SimplexID<2> curr = frontier.front();
-                if (visited.find(curr) == visited.end())
-                {
-                    visited.insert(curr);
-                    surface.insert(curr);
-                    neighbors_up(mesh, curr, std::back_inserter(frontier));
-                }
-                frontier.pop_front();
-            }
+std::vector> splitSurfaces(SurfaceMesh &mesh) {
+  // Queue to store the next edges to visit
+  std::deque> frontier;
+  // Set of visited edges
+  std::set> visited;
+
+  std::vector> meshes;
+
+  using SimplexSet = typename casc::SimplexSet;
+  SimplexSet surface;
+
+  int connected_components = 0;
+  for (auto edgeID : mesh.get_level_id<2>()) {
+    // This is a new edge if we haven't visited before
+    if (visited.find(edgeID) == visited.end()) {
+      ++connected_components;
+      frontier.push_back(edgeID);
+      surface.insert(edgeID);
+
+      // Traverse across connected edges.
+      while (!frontier.empty()) {
+        SurfaceMesh::SimplexID<2> curr = frontier.front();
+        if (visited.find(curr) == visited.end()) {
+          visited.insert(curr);
+          surface.insert(curr);
+          neighbors_up(mesh, curr, std::back_inserter(frontier));
+        }
+        frontier.pop_front();
+      }
 
-            // Extract out the surface
-            SimplexSet tmp;
-            casc::getClosure(mesh, surface, tmp);
-            surface.clear();
-            casc::getStar(mesh, tmp, surface);
+      // Extract out the surface
+      SimplexSet tmp;
+      casc::getClosure(mesh, surface, tmp);
+      surface.clear();
+      casc::getStar(mesh, tmp, surface);
 
-            std::unique_ptr newSMPtr(new SurfaceMesh);
-            auto& newSM = *newSMPtr;
+      std::unique_ptr newSMPtr(new SurfaceMesh);
+      auto &newSM = *newSMPtr;
 
-            util::int_for_each(surfacemesh_detail::CopyHelper(), mesh, newSM, surface);
+      util::int_for_each(
+          surfacemesh_detail::CopyHelper(), mesh, newSM, surface);
 
-            compute_orientation(newSM);
-            if (getVolume(newSM) < 0)
-            {
-                flipNormals(newSM);
-            }
+      compute_orientation(newSM);
+      if (getVolume(newSM) < 0) {
+        flipNormals(newSM);
+      }
 
-            meshes.push_back(std::move(newSMPtr));
-            surface.clear();
-        }
+      meshes.push_back(std::move(newSMPtr));
+      surface.clear();
     }
+  }
 
-    return meshes;
+  return meshes;
 }
 
-void cacheNormals(SurfaceMesh& mesh){
-    for (auto fID : mesh.get_level_id<3>()) {
-        auto norm = getNormal(mesh, fID);
-        normalize(norm);
-        (*fID).normal = norm;
-    }
-
-    for (auto vID: mesh.get_level_id<1>()) {
-        Vector norm;
-        auto faces = mesh.up(mesh.up(vID));
-        for (auto faceID : faces)
-        {
-            norm += (*faceID).normal;
-        }
-        normalize(norm);
-        (*vID).normal = norm;
+void cacheNormals(SurfaceMesh &mesh) {
+  for (auto fID : mesh.get_level_id<3>()) {
+    auto norm = getNormal(mesh, fID);
+    // A zero value face normal is OK for LST
+    REAL mag = length(norm);
+    if (mag != 0)
+      norm /= mag; 
+    (*fID).normal = norm;
+  }
+
+  for (auto vID : mesh.get_level_id<1>()) {
+    Vector norm;
+    auto faces = mesh.up(mesh.up(vID));
+    for (auto faceID : faces) {
+      norm += (*faceID).normal;
     }
+    REAL mag = length(norm);
+    if (mag != 0)
+      norm /= mag; 
+    (*vID).normal = norm;
+  }
 }
 
 // http://pub.ist.ac.at/~edels/Papers/1995-J-03-IncrementalBettiNumbers.pdf
-std::tuple getBettiNumbers(SurfaceMesh& mesh){
-    bool valid = true;
-    int connected_components = mesh.size<1>();
-    int holes = 0;
-    int voids = 0;
-
-    std::deque> frontier_edges;
-    std::set> visited_edges;
-    std::set> faces;
-
-    std::set visited_verts;
-    SurfaceMesh::SimplexID<2> curr;
-
-    for (auto eid : mesh.get_level_id<2>())
-    {
-        if (visited_edges.find(eid) == visited_edges.end())
-        {
-            // Does this surface have a boundary edgee?
-            bool hasboundary = false;
-            // Does this surface have consistent normals?
-            bool orientable = true;
-            // Is this surface have weird 3+ face intersections?
-            bool pseudo_manifold = true;
-            frontier_edges.push_back(eid);
-
-            while (!frontier_edges.empty())
-            {
-                curr = frontier_edges.front();
-                if (visited_edges.find(curr) == visited_edges.end())
-                {
-                    std::array n = curr.indices();
-                    if (visited_verts.find(n[0]) == visited_verts.end()) {
-                        // Have never visited n[0]
-                        visited_verts.insert(n[0]);
-                        --connected_components;
-
-                        if (visited_verts.find(n[1]) == visited_verts.end()) {
-                            // Both vertices unvisited
-                            visited_verts.insert(n[1]);
-                        }
-                    }
-                    else{
-                        if (visited_verts.find(n[1]) == visited_verts.end()) {
-                            visited_verts.insert(n[1]);
-                            --connected_components;
-                        } else {
-                            //found a closed cycle
-                            ++holes;
-                        }
-                    }
-                    visited_edges.insert(curr);
-                    // If on a boundary stop otherwise add neighboring edges to
-                    // the queue
-                    auto w = mesh.get_cover(curr);
-                    if (w.size() <= 1) {hasboundary = true;
-                    }
-                    else if (w.size() == 2)
-                    {
-                        neighbors_up(mesh, curr, std::back_inserter(frontier_edges));
-                    }
-                    else {
-                        pseudo_manifold = false;
-                        valid = false;
-                    }
-                    neighbors_up(mesh, curr, std::back_inserter(frontier_edges));
-                    for(auto n : w ){
-                        auto fid = curr.get_simplex_up(n);
-                        faces.insert(fid);
-                    }
-                }
-                frontier_edges.pop_front();
+std::tuple getBettiNumbers(SurfaceMesh &mesh) {
+  bool valid = true;
+  int connected_components = mesh.size<1>();
+  int holes = 0;
+  int voids = 0;
+
+  std::deque> frontier_edges;
+  std::set> visited_edges;
+  std::set> faces;
+
+  std::set visited_verts;
+  SurfaceMesh::SimplexID<2> curr;
+
+  for (auto eid : mesh.get_level_id<2>()) {
+    if (visited_edges.find(eid) == visited_edges.end()) {
+      // Does this surface have a boundary edgee?
+      bool hasboundary = false;
+      // Does this surface have consistent normals?
+      bool orientable = true;
+      // Is this surface have weird 3+ face intersections?
+      bool pseudo_manifold = true;
+      frontier_edges.push_back(eid);
+
+      while (!frontier_edges.empty()) {
+        curr = frontier_edges.front();
+        if (visited_edges.find(curr) == visited_edges.end()) {
+          std::array n = curr.indices();
+          if (visited_verts.find(n[0]) == visited_verts.end()) {
+            // Have never visited n[0]
+            visited_verts.insert(n[0]);
+            --connected_components;
+
+            if (visited_verts.find(n[1]) == visited_verts.end()) {
+              // Both vertices unvisited
+              visited_verts.insert(n[1]);
             }
-
-            int sz = faces.size();
-            if (pseudo_manifold){
-                if (orientable && !hasboundary){
-                    // Local space should be a sphere
-                    ++voids;
-                    --sz;
-                }
-                holes -= sz;
-            } else{
-                // TODO: Implement better detection of faces which complete 2-cycles s.t. this can be complete.
+          } else {
+            if (visited_verts.find(n[1]) == visited_verts.end()) {
+              visited_verts.insert(n[1]);
+              --connected_components;
+            } else {
+              // found a closed cycle
+              ++holes;
             }
-            faces.clear();
+          }
+          visited_edges.insert(curr);
+          // If on a boundary stop otherwise add neighboring edges to
+          // the queue
+          auto w = mesh.get_cover(curr);
+          if (w.size() <= 1) {
+            hasboundary = true;
+          } else if (w.size() == 2) {
+            neighbors_up(mesh, curr, std::back_inserter(frontier_edges));
+          } else {
+            pseudo_manifold = false;
+            valid = false;
+          }
+          neighbors_up(mesh, curr, std::back_inserter(frontier_edges));
+          for (auto n : w) {
+            auto fid = curr.get_simplex_up(n);
+            faces.insert(fid);
+          }
+        }
+        frontier_edges.pop_front();
+      }
+
+      int sz = faces.size();
+      if (pseudo_manifold) {
+        if (orientable && !hasboundary) {
+          // Local space should be a sphere
+          ++voids;
+          --sz;
         }
+        holes -= sz;
+      } else {
+        // TODO: Implement better detection of faces which complete 2-cycles
+        // s.t. this can be complete.
+      }
+      faces.clear();
     }
+  }
 
-    visited_edges.clear();
-    visited_verts.clear();
+  visited_edges.clear();
+  visited_verts.clear();
 
-    return std::make_tuple(valid, connected_components, holes, voids);
+  return std::make_tuple(valid, connected_components, holes, voids);
 }
 } // end namespace gamer
diff --git a/src/SurfaceMeshDetail.cpp b/src/SurfaceMeshDetail.cpp
index 1f7b702d..b6111488 100644
--- a/src/SurfaceMeshDetail.cpp
+++ b/src/SurfaceMeshDetail.cpp
@@ -1,1161 +1,1082 @@
-/*
- * ***************************************************************************
- * This file is part of the GAMer software.
- * Copyright (C) 2016-2018
- * by Christopher Lee, John Moody, Rommie Amaro, J. Andrew McCammon,
- *    and Michael Holst
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * ***************************************************************************
- */
+// This file is part of the GAMer software.
+// Copyright (C) 2016-2021
+// by Christopher T. Lee and contributors
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, see 
+// or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+// Boston, MA 02111-1307 USA
 
-
-#include 
 #include 
+#include 
 #include 
 #include 
 #include 
 #include 
 #include 
 
-#include 
 #include 
 #include 
+#include 
 
 #include "gamer/EigenDiagonalization.h"
 #include "gamer/SurfaceMesh.h"
 #include "gamer/Vertex.h"
 
 /// Namespace for all things gamer
-namespace gamer
-{
-namespace surfacemesh_detail
-{
-tensor computeLocalStructureTensor(const SurfaceMesh              &mesh,
-                                                 const SurfaceMesh::SimplexID<1> vertexID,
-                                                 const int                       rings)
-{
-    // Set of neighbors
-    std::set > nbors;
-    // Get list of neighbors
-    casc::kneighbors_up(mesh, vertexID, rings, nbors);
-    // local structure tensor
-    tensor lst = tensor();
-    for (SurfaceMesh::SimplexID<1> nid : nbors)
-    {
-        auto norm = getNormal(mesh, nid);           // Get Vector normal
-        auto mag  = std::sqrt(norm|norm);
-        if (mag <= 0)
-        {
-            continue;
-        }
-        norm /= mag;               // normalize
-        lst  += norm*norm;         // tensor product
+namespace gamer {
+namespace surfacemesh_detail {
+tensor
+computeLocalStructureTensor(const SurfaceMesh &mesh,
+                            const SurfaceMesh::SimplexID<1> vertexID,
+                            const int rings) {
+  // Set of neighbors
+  std::set> nbors;
+  // Get list of neighbors
+  casc::kneighbors_up(mesh, vertexID, rings, nbors);
+  // local structure tensor
+  tensor lst = tensor();
+  for (SurfaceMesh::SimplexID<1> nid : nbors) {
+    auto norm = getNormal(mesh, nid); // Get Vector normal
+    auto mag = std::sqrt(norm | norm);
+    if (mag <= 0) {
+      continue;
     }
-
-    // Print the LST nicely
-    // std::cout << "LST:\n" << std::endl;
-    // for(int i = 0; i < 3; ++i){
-    //     for(int j = 0; j < 3; ++j)
-    //         std::cout << lst.get(i,j) << " ";
-    //     std::cout << "\n";
-    // }
-    return lst;
+    norm /= mag;        // normalize
+    lst += norm * norm; // tensor product
+  }
+
+  // Print the LST nicely
+  // std::cout << "LST:\n" << std::endl;
+  // for(int i = 0; i < 3; ++i){
+  //     for(int j = 0; j < 3; ++j)
+  //         std::cout << lst.get(i,j) << " ";
+  //     std::cout << "\n";
+  // }
+  return lst;
 }
 
-tensor computeLSTFromCache(const SurfaceMesh              &mesh,
-                                                 const SurfaceMesh::SimplexID<1> vertexID,
-                                                 const int                       rings)
-{
-    // Set of neighbors
-    std::set > nbors;
-    // Get list of neighbors
-    casc::kneighbors_up(mesh, vertexID, rings, nbors);
-    // local structure tensor
-    tensor lst = tensor();
-    for (SurfaceMesh::SimplexID<1> vID : nbors)
-    {
-        auto norm = (*vID).normal;           // Get Vector normal
-        lst  += norm*norm;         // tensor product
-    }
-    return lst;
+tensor
+computeLSTFromCache(const SurfaceMesh &mesh,
+                    const SurfaceMesh::SimplexID<1> vertexID, const int rings) {
+  // Set of neighbors
+  std::set> nbors;
+  // Get list of neighbors
+  casc::kneighbors_up(mesh, vertexID, rings, nbors);
+  // local structure tensor
+  tensor lst = tensor();
+  for (SurfaceMesh::SimplexID<1> vID : nbors) {
+    auto norm = (*vID).normal; // Get Vector normal
+    lst += norm * norm;        // tensor product
+  }
+  return lst;
 }
 
-void decimateVertex(SurfaceMesh &mesh, SurfaceMesh::SimplexID<1> vertexID, std::size_t rings)
-{
-    // TODO: (10) Come up with a better scheme
-    // Pick an arbitrary face's data
-    auto fdata = **mesh.up(std::move(mesh.up(vertexID))).begin();
-    fdata.orientation = 0; // Reset the orientation accordingly
-
-    // Compute and backup ring of vertices prior to vertex removal
-    std::set > boundary;
-    casc::neighbors_up(mesh, vertexID, std::inserter(boundary, boundary.end()));
-    std::set > backupBoundary(boundary);
-
-    // Remove the vertex
-    mesh.remove(vertexID);
-
-    // Sort vertices into 'ring' order
-    std::vector > sortedVerts;
-    std::set bNames; // boundary names
-    std::vector > edgeList;
-
-    auto it = boundary.begin();
-    int  firstName = mesh.get_name(*it)[0];
-    while (boundary.size() > 0)
-    {
-        std::vector > nbors;
-        auto currID = *it; // Get SimplexID
-
-        int  currName = mesh.get_name(currID)[0];
-        bNames.insert(currName);
-
-        bool success = false; // Flag to track success
-        // Push current into list of sorted vertices.
-        std::move(it, std::next(it), std::back_inserter(sortedVerts));
-        boundary.erase(it); // Erase current from boundary
-
-        // If nothing is left in the boundary, check that the current
-        // vertex has an edge to the first vertex.
-        if (boundary.size() == 0)
-        {
-            if (mesh.exists({currName, firstName}))
-            {
-                edgeList.push_back(mesh.get_simplex_up(currID, firstName));
-                break; // Break out of while loop
-            }
-        }
-        else
-        {
-            // Get neighbors and search for next vertex
-            casc::neighbors_up(mesh, currID, std::back_inserter(nbors));
-            for (auto nbor : nbors)
-            {
-                auto result = boundary.find(nbor);
-                if (result != boundary.end())
-                {
-                    // Check that the edge is a boundary
-                    auto tmp = mesh.get_simplex_up(*result, currName);
-                    if (mesh.get_cover(tmp).size() == 1)
-                    {
-                        it = result;
-                        edgeList.push_back(tmp);
-                        success = true;
-                        break; // leave for loop
-                    }
-                }
-            }
-        }
-        // The ring isn't really a ring
-        if (!success)
-        {
-            throw std::runtime_error("ERROR(coarse): Hole ring is not closed. "
-                                     "Please contact the developers with this error.");
+void decimateVertex(SurfaceMesh &mesh, SurfaceMesh::SimplexID<1> vertexID,
+                    std::size_t rings) {
+  // TODO: (10) Come up with a better scheme
+  // Pick an arbitrary face's data
+  auto fdata = **mesh.up(std::move(mesh.up(vertexID))).begin();
+  fdata.orientation = 0; // Reset the orientation accordingly
+
+  // Compute and backup ring of vertices prior to vertex removal
+  std::set> boundary;
+  casc::neighbors_up(mesh, vertexID, std::inserter(boundary, boundary.end()));
+  std::set> backupBoundary(boundary);
+
+  // Remove the vertex
+  mesh.remove(vertexID);
+
+  // Sort vertices into 'ring' order
+  std::vector> sortedVerts;
+  std::set bNames; // boundary names
+  std::vector> edgeList;
+
+  auto it = boundary.begin();
+  int firstName = mesh.get_name(*it)[0];
+  while (boundary.size() > 0) {
+    std::vector> nbors;
+    auto currID = *it; // Get SimplexID
+
+    int currName = mesh.get_name(currID)[0];
+    bNames.insert(currName);
+
+    bool success = false; // Flag to track success
+    // Push current into list of sorted vertices.
+    std::move(it, std::next(it), std::back_inserter(sortedVerts));
+    boundary.erase(it); // Erase current from boundary
+
+    // If nothing is left in the boundary, check that the current
+    // vertex has an edge to the first vertex.
+    if (boundary.size() == 0) {
+      if (mesh.exists({currName, firstName})) {
+        edgeList.push_back(mesh.get_simplex_up(currID, firstName));
+        break; // Break out of while loop
+      }
+    } else {
+      // Get neighbors and search for next vertex
+      casc::neighbors_up(mesh, currID, std::back_inserter(nbors));
+      for (auto nbor : nbors) {
+        auto result = boundary.find(nbor);
+        if (result != boundary.end()) {
+          // Check that the edge is a boundary
+          auto tmp = mesh.get_simplex_up(*result, currName);
+          if (mesh.get_cover(tmp).size() == 1) {
+            it = result;
+            edgeList.push_back(tmp);
+            success = true;
+            break; // leave for loop
+          }
         }
+      }
     }
+    // The ring isn't really a ring
+    if (!success) {
+      gamer_runtime_error(
+          "ERROR(coarse): Hole ring is not closed. "
+          "Please contact the developers with this error.");
+    }
+  }
 
-    triangulateHole(mesh, sortedVerts, fdata, edgeList);
+  triangulateHole(mesh, sortedVerts, fdata, edgeList);
 
-    // Smooth vertices around the filled hole
-    for (auto v : backupBoundary)
-    {
-        weightedVertexSmooth(mesh, v, rings);
-    }
+  // Smooth vertices around the filled hole
+  for (auto v : backupBoundary) {
+    weightedVertexSmooth(mesh, v, rings);
+  }
 }
 
-void triangulateHoleHelper(SurfaceMesh                                                        &mesh,
-                           std::vector >                            &boundary,
-                           const SMFace                                                       &fdata,
-                           std::back_insert_iterator > > edgeListIT)
-{
-    // Terminal case
-    if (boundary.size() == 3)
-    {
-        // create the face
-        auto a = mesh.get_name(boundary[0])[0];
-        auto b = mesh.get_name(boundary[1])[0];
-        auto c = mesh.get_name(boundary[2])[0];
-        mesh.insert({a, b, c}, fdata);
-        return;
-    }
-
-    // Construct a sorted vector of pairs... (valence, vertexID) by valence
-    std::vector > > list;
-    for (auto vertexID : boundary)
-    {
-        list.push_back(std::make_pair(getValence(mesh, vertexID), vertexID));
-    }
-    std::sort(list.begin(), list.end(), [](
-                  const std::pair > &lhs,
-                  const std::pair > &rhs){
-                return lhs.first < rhs.first;
+void triangulateHoleHelper(
+    SurfaceMesh &mesh, std::vector> &boundary,
+    const SMFace &fdata,
+    std::back_insert_iterator>>
+        edgeListIT) {
+  // Terminal case
+  if (boundary.size() == 3) {
+    // create the face
+    auto a = mesh.get_name(boundary[0])[0];
+    auto b = mesh.get_name(boundary[1])[0];
+    auto c = mesh.get_name(boundary[2])[0];
+    mesh.insert({a, b, c}, fdata);
+    return;
+  }
+
+  // Construct a sorted vector of pairs... (valence, vertexID) by valence
+  std::vector>> list;
+  for (auto vertexID : boundary) {
+    list.push_back(std::make_pair(getValence(mesh, vertexID), vertexID));
+  }
+  std::sort(list.begin(), list.end(),
+            [](const std::pair> &lhs,
+               const std::pair> &rhs) {
+              return lhs.first < rhs.first;
             });
 
-    SurfaceMesh::SimplexID<1> v1, v2;
-    v1 = list[0].second;
-
-    // Find v1 and rotate so that it is first for easy splitting later
-    auto v1it =  std::find(boundary.begin(), boundary.end(), v1);
-    std::rotate(boundary.begin(), v1it, boundary.end());
-
-    // Get the next lowest valence vertex
-    for (auto it = ++list.begin(); it != list.end(); ++it)
-    {
-        v2 = (*it).second;
-        // Check that it is not already connected to v1
-        if (v2 != boundary[1] && v2 != boundary.back())
-        {
-            break;
-        }
-    }
-    // Insert new edge
-    auto a = mesh.get_name(v1)[0];
-    auto b = mesh.get_name(v2)[0];
-    mesh.insert({a, b});
-    // TODO: (0) Get the simplex from insert.
-    *edgeListIT++ = mesh.get_simplex_up({a, b});
-
-    auto v2it =  std::find(boundary.begin(), boundary.end(), v2);
-    std::vector > other;
-    other.push_back(v2);
-    std::move(v2it+1, boundary.end(), std::back_inserter(other));
-    boundary.erase(v2it+1, boundary.end());
-
-    other.push_back(v1);
-
-    // Recurse to fill sub-holes
-    triangulateHoleHelper(mesh, boundary, fdata, edgeListIT);
-    triangulateHoleHelper(mesh, other, fdata, edgeListIT);
-}
-
-void triangulateHole(SurfaceMesh                             &mesh,
-                     std::vector > &sortedVerts,
-                     const SMFace                            &fdata,
-                     std::vector > &holeEdges)
-{
-    // backup sortedVerts
-    std::vector > backupVerts = sortedVerts;
-
-    // Triangulate the hole
-    triangulateHoleHelper(mesh, sortedVerts, fdata, std::back_inserter(holeEdges));
-
-    std::set keys;
-    for (auto vertexID : backupVerts)
-    {
-        keys.insert(mesh.get_name(vertexID)[0]);
-    }
+  SurfaceMesh::SimplexID<1> v1, v2;
+  v1 = list[0].second;
 
-    initLocalOrientation >::apply(mesh, std::move(keys), backupVerts.begin(), backupVerts.end());
+  // Find v1 and rotate so that it is first for easy splitting later
+  auto v1it = std::find(boundary.begin(), boundary.end(), v1);
+  std::rotate(boundary.begin(), v1it, boundary.end());
 
-    if (!computeLocalOrientation(mesh, holeEdges))
-    {
-        throw std::runtime_error("ERROR(triangulateHole): Mesh became non-orientable");
+  // Get the next lowest valence vertex
+  for (auto it = ++list.begin(); it != list.end(); ++it) {
+    v2 = (*it).second;
+    // Check that it is not already connected to v1
+    if (v2 != boundary[1] && v2 != boundary.back()) {
+      break;
     }
+  }
+  // Insert new edge
+  auto a = mesh.get_name(v1)[0];
+  auto b = mesh.get_name(v2)[0];
+  mesh.insert({a, b});
+  // TODO: (0) Get the simplex from insert.
+  *edgeListIT++ = mesh.get_simplex_up({a, b});
+
+  auto v2it = std::find(boundary.begin(), boundary.end(), v2);
+  std::vector> other;
+  other.push_back(v2);
+  std::move(v2it + 1, boundary.end(), std::back_inserter(other));
+  boundary.erase(v2it + 1, boundary.end());
+
+  other.push_back(v1);
+
+  // Recurse to fill sub-holes
+  triangulateHoleHelper(mesh, boundary, fdata, edgeListIT);
+  triangulateHoleHelper(mesh, other, fdata, edgeListIT);
+}
+
+void triangulateHole(SurfaceMesh &mesh,
+                     std::vector> &sortedVerts,
+                     const SMFace &fdata,
+                     std::vector> &holeEdges) {
+  // backup sortedVerts
+  std::vector> backupVerts = sortedVerts;
+
+  // Triangulate the hole
+  triangulateHoleHelper(mesh, sortedVerts, fdata,
+                        std::back_inserter(holeEdges));
+
+  std::set keys;
+  for (auto vertexID : backupVerts) {
+    keys.insert(mesh.get_name(vertexID)[0]);
+  }
+
+  initLocalOrientation>::apply(
+      mesh, std::move(keys), backupVerts.begin(), backupVerts.end());
+
+  if (!computeLocalOrientation(mesh, holeEdges)) {
+    gamer_runtime_error(
+        "ERROR(triangulateHole): Mesh became non-orientable");
+  }
 }
 
-bool computeLocalOrientation(SurfaceMesh                                   &mesh,
-                             const std::vector > &edgeList)
-{
-    std::deque > frontier;
-    std::set >   visited;
-    bool orientable = true;
-
-    for (auto outer : edgeList)
-    {
-        if (visited.find(outer) == visited.end())
-        {
-            frontier.push_back(outer);
-
-            while (!frontier.empty())
-            {
-                auto curr = frontier.front();
-
-                if (visited.find(curr) == visited.end())
-                {
-                    visited.insert(curr);
-                    auto w = mesh.get_cover(curr);
-
-                    if (w.size() == 1)
-                    {
-                        //std::cout << curr << ":" << w[0] << " ~ Boundary" <<
-                        // std::endl;
-                    }
-                    else if (w.size() == 2)
-                    {
-                        auto &edge0 = *mesh.get_edge_up(curr, w[0]);
-                        auto &edge1 = *mesh.get_edge_up(curr, w[1]);
-
-                        auto &node0 = *mesh.get_simplex_up(curr, w[0]);
-                        auto &node1 = *mesh.get_simplex_up(curr, w[1]);
-
-                        if (node0.orientation == 0)
-                        {
-                            if (node1.orientation == 0)
-                            {
-                                node0.orientation = 1;
-                                node1.orientation = -edge1.orientation * edge0.orientation * node0.orientation;
-                            }
-                            else
-                            {
-                                node0.orientation = -edge0.orientation * edge1.orientation * node1.orientation;
-                            }
-                        }
-                        else
-                        {
-                            if (node1.orientation == 0)
-                            {
-                                node1.orientation = -edge1.orientation * edge0.orientation * node0.orientation;
-                            }
-                            else
-                            {
-                                if (edge0.orientation*node0.orientation + edge1.orientation*node1.orientation != 0)
-                                {
-                                    orientable = false;
-                                }
-                            }
-                        }
-                        std::vector > tmp;
-                        neighbors_up(mesh, curr, std::back_inserter(tmp));
-                        for (auto e : tmp)
-                        {
-                            if (std::find(edgeList.begin(), edgeList.end(), e) != edgeList.end())
-                                frontier.push_back(e);
-                        }
-                    }
-                    else
-                    {
-                        // TODO (0): Change to runtime error
-                        std::cerr << "ERROR(computeLocalOrientation): Found an edge"
-                                  << " connected to " << w.size() << " faces. The SurfaceMesh "
-                                  << "is no longer a surface mesh." << std::endl;
-                        return false;
-                    }
+bool computeLocalOrientation(
+    SurfaceMesh &mesh, const std::vector> &edgeList) {
+  std::deque> frontier;
+  std::set> visited;
+  bool orientable = true;
+
+  for (auto outer : edgeList) {
+    if (visited.find(outer) == visited.end()) {
+      frontier.push_back(outer);
+
+      while (!frontier.empty()) {
+        auto curr = frontier.front();
+
+        if (visited.find(curr) == visited.end()) {
+          visited.insert(curr);
+          auto w = mesh.get_cover(curr);
+
+          if (w.size() == 1) {
+            // std::cout << curr << ":" << w[0] << " ~ Boundary" <<
+            // std::endl;
+          } else if (w.size() == 2) {
+            auto &edge0 = *mesh.get_edge_up(curr, w[0]);
+            auto &edge1 = *mesh.get_edge_up(curr, w[1]);
+
+            auto &node0 = *mesh.get_simplex_up(curr, w[0]);
+            auto &node1 = *mesh.get_simplex_up(curr, w[1]);
+
+            if (node0.orientation == 0) {
+              if (node1.orientation == 0) {
+                node0.orientation = 1;
+                node1.orientation =
+                    -edge1.orientation * edge0.orientation * node0.orientation;
+              } else {
+                node0.orientation =
+                    -edge0.orientation * edge1.orientation * node1.orientation;
+              }
+            } else {
+              if (node1.orientation == 0) {
+                node1.orientation =
+                    -edge1.orientation * edge0.orientation * node0.orientation;
+              } else {
+                if (edge0.orientation * node0.orientation +
+                        edge1.orientation * node1.orientation !=
+                    0) {
+                  orientable = false;
                 }
-                frontier.pop_front();
+              }
             }
+            std::vector> tmp;
+            neighbors_up(mesh, curr, std::back_inserter(tmp));
+            for (auto e : tmp) {
+              if (std::find(edgeList.begin(), edgeList.end(), e) !=
+                  edgeList.end())
+                frontier.push_back(e);
+            }
+          } else {
+            // TODO (0): Change to runtime error
+            std::cerr << "ERROR(computeLocalOrientation): Found an edge"
+                      << " connected to " << w.size()
+                      << " faces. The SurfaceMesh "
+                      << "is no longer a surface mesh." << std::endl;
+            return false;
+          }
         }
+        frontier.pop_front();
+      }
     }
-    return orientable;
+  }
+  return orientable;
 }
 
-void weightedVertexSmooth(SurfaceMesh              &mesh,
-                          SurfaceMesh::SimplexID<1> vertexID,
-                          int                       rings)
-{
-    auto   centerName = mesh.get_name(vertexID)[0];
-    auto  ¢er = *vertexID; // get the vertex data
-
-    double sumWeights = 0;
-    Vector newPos;
-
-    // Compute the following sum to get the new position
-    // \bar{x} = \frac{1}{\sum_{i=1}^{N_2}(\alpha_i+1)}\sum_{i=1}^{N_2}(\alpha_i
-    // + 1) x_i
-    for (auto edge : mesh.up(vertexID))
-    {
-        // Get the vertex connected by edge
-        auto   edgeName = mesh.get_name(edge);
-        Vertex shared   = *mesh.get_simplex_up({(edgeName[0] == centerName) ? edgeName[1] : edgeName[0]});
-
-        // Get the vertices connected to adjacent edge by face
-        auto up = mesh.get_cover(edge);
-        if (up.size() != 2)
-        {
-            // Vertex is on a boundary or otherwise...
-            // Let's not move it
-            return;
-        }
-
-        Vertex prev = *mesh.get_simplex_up({up[0]});
-        Vertex next = *mesh.get_simplex_up({up[1]});
-
-        // std::cout << mesh.get_simplex_up({up[0]}) << " "
-        //             << mesh.get_simplex_up({up[1]}) << " "
-        //             << mesh.get_simplex_up({(edgeName[0] == centerName) ?
-        // edgeName[1] : edgeName[0]}) << std::endl;
-
-        // std::cout << prev << " " << next << " " << shared << std::endl;
-        Vector pS, nS;
-        try
-        {
-            pS = prev - shared;
-            normalize(pS);
-            nS = next - shared;
-            normalize(nS);
-        }
-        catch (std::exception &e)
-        {
-            throw std::runtime_error("ERROR: Zero length edge found. "
-                                     "weightedVertexSmooth expects no zero length edges.");
-        }
-
-        // Bisector of the 'rhombus'
-        Vector bisector = pS + nS;
-        Vector perpNorm;
-        double alpha = (pS|nS) + 1;
-
-        // Check if vertices are colinear
-        if (length(bisector) == 0 || alpha < 1e-6 || fabs(alpha-2) < 1e-6)
-        {
-            perpNorm = pS;
-            normalize(perpNorm);
-        }
-        else
-        {
-            normalize(bisector);
-            // Normal of tangent plane
-            Vector tanNorm = cross(pS, nS);
-            // Get the perpendicular plane made up of plane normal of
-            // bisector
-            perpNorm = cross(tanNorm, bisector);
-            normalize(perpNorm);
-        }
-
-        // Get a reference vector to shared which lies on the plane of interest.
-        Vector disp = center - shared;
-        auto disp_e = EigenMap(disp);
-
-        // Perpendicular projector
-        auto perpProj = perpNorm*perpNorm; // tensor product
-        // Compute perpendicular component
-        Vector perp;
-        Eigen::Map perpProj_e(perpProj.data());
-        Eigen::Map perp_e(perp.data());
-        perp_e = perpProj_e*disp_e; // matrix (3x3) * vector = vector
-
-        sumWeights += alpha;
+void weightedVertexSmooth(SurfaceMesh &mesh, SurfaceMesh::SimplexID<1> vertexID,
+                          int rings) {
+  auto centerName = mesh.get_name(vertexID)[0];
+  auto ¢er = *vertexID; // get the vertex data
+
+  double sumWeights = 0;
+  Vector newPos;
+
+  // Compute the following sum to get the new position
+  // \bar{x} = \frac{1}{\sum_{i=1}^{N_2}(\alpha_i+1)}\sum_{i=1}^{N_2}(\alpha_i
+  // + 1) x_i
+  for (auto edge : mesh.up(vertexID)) {
+    // Get the vertex connected by edge
+    auto edgeName = mesh.get_name(edge);
+    Vertex shared = *mesh.get_simplex_up(
+        {(edgeName[0] == centerName) ? edgeName[1] : edgeName[0]});
+
+    // Get the vertices connected to adjacent edge by face
+    auto up = mesh.get_cover(edge);
+    if (up.size() != 2) {
+      // Vertex is on a boundary or otherwise...
+      // Let's not move it
+      return;
+    }
 
-        newPos += alpha*(center.position - perp);
+    Vertex prev = *mesh.get_simplex_up({up[0]});
+    Vertex next = *mesh.get_simplex_up({up[1]});
+
+    // std::cout << mesh.get_simplex_up({up[0]}) << " "
+    //             << mesh.get_simplex_up({up[1]}) << " "
+    //             << mesh.get_simplex_up({(edgeName[0] == centerName) ?
+    // edgeName[1] : edgeName[0]}) << std::endl;
+
+    // std::cout << prev << " " << next << " " << shared << std::endl;
+    Vector pS, nS;
+    try {
+      pS = prev - shared;
+      normalize(pS);
+      nS = next - shared;
+      normalize(nS);
+    } catch (std::exception &e) {
+      gamer_runtime_error(
+          "ERROR: Zero length edge found. "
+          "weightedVertexSmooth expects no zero length edges.");
     }
-    newPos /= sumWeights;
-    /**
-     * Project to move onto LST eigenvector space and scale by eigenvalues.
-     *
-     * \bar{x} = x + \sum_{k=1}^3 \frac{1}{1+\lambda_k}((\bar{x} - x)\cdot
-     * \vec{e_k})\vec{e_k}
-     */
-    auto lst = surfacemesh_detail::computeLocalStructureTensor(mesh, vertexID, rings);
-
-    EigenVector eigenvalues;
-    EigenMatrix eigenvectors;
-
-    EigenDiagonalizeTraits::diagonalizeSelfAdjointMatrix(lst, eigenvalues, eigenvectors);
-
-    // auto eigen_result = getEigenvalues(lst);
-
-    // std::cout << "Eigenvalues(LST): "
-    //      << eigen_result.eigenvalues().transpose() << std::endl;
-    // std::cout << "Here's a matrix whose columns are eigenvectors of LST \n"
-    //      << "corresponding to these eigenvalues:\n"
-    //      << eigen_result.eigenvectors() << std::endl;
-
-    newPos -= center.position;  // Vector of old position to new position
-    auto newPos_e = EigenMap(newPos);
-
-    // dot product followed by elementwise-division EQN 4. w is a scale factor.
-    auto w = (
-        (eigenvectors.transpose()*newPos_e).array()
-        / (eigenvalues.array()+1)
-        ).matrix();                           // vector 3x1
-    newPos_e = eigenvectors*w; // matrix 3x3 * vector = vector
-    center.position += newPos;
-}
 
-Vector weightedVertexSmoothCache(SurfaceMesh &mesh, SurfaceMesh::SimplexID<1> vertexID, std::size_t rings){
-    auto   centerName = mesh.get_name(vertexID)[0];
-    auto  ¢er = *vertexID; // get the vertex data
-
-    double sumWeights = 0;
-    Vector newPos;
-
-    // Compute the following sum to get the new position
-    // \bar{x} = \frac{1}{\sum_{i=1}^{N_2}(\alpha_i+1)}\sum_{i=1}^{N_2}(\alpha_i
-    // + 1) x_i
-    for (auto edge : mesh.up(vertexID))
-    {
-        // Get the vertex connected by edge
-        auto   edgeName = mesh.get_name(edge);
-        Vertex shared   = *mesh.get_simplex_up({(edgeName[0] == centerName) ? edgeName[1] : edgeName[0]});
-
-        // Get the vertices connected to adjacent edge by face
-        auto cover = mesh.get_cover(edge);
-        if (cover.size() != 2)
-        {
-            // Vertex is on a boundary... Don't move it
-            return Vector({0,0,0});
-        }
-        std::vector> faces;
-        mesh.up(edge, std::back_inserter(faces));
-
-        Vertex prev = *mesh.get_simplex_down(faces[0], edgeName);
-        Vertex next = *mesh.get_simplex_down(faces[1], edgeName);
-
-        // std::cout << mesh.get_simplex_up({up[0]}) << " "
-        //             << mesh.get_simplex_up({up[1]}) << " "
-        //             << mesh.get_simplex_up({(edgeName[0] == centerName) ?
-        // edgeName[1] : edgeName[0]}) << std::endl;
-
-        // std::cout << prev << " " << next << " " << shared << std::endl;
-        Vector pS, nS;
-        try
-        {
-            pS = prev - shared;
-            normalize(pS);
-            nS = next - shared;
-            normalize(nS);
-        }
-        catch (std::exception &e)
-        {
-            throw std::runtime_error("ERROR: Zero length edge found. "
-                                     "weightedVertexSmooth expects no zero length edges.");
-        }
+    // Bisector of the 'rhombus'
+    Vector bisector = pS + nS;
+    Vector perpNorm;
+    double alpha = (pS | nS) + 1;
+
+    // Check if vertices are colinear
+    if (length(bisector) == 0 || alpha < 1e-6 || fabs(alpha - 2) < 1e-6) {
+      perpNorm = pS;
+      normalize(perpNorm);
+    } else {
+      normalize(bisector);
+      // Normal of tangent plane
+      Vector tanNorm = cross(pS, nS);
+      // Get the perpendicular plane made up of plane normal of
+      // bisector
+      perpNorm = cross(tanNorm, bisector);
+      normalize(perpNorm);
+    }
 
-        // Bisector of the 'rhombus'
-        Vector bisector = pS + nS;
-        Vector perpNorm;
-        double alpha = (pS|nS) + 1;
+    // Get a reference vector to shared which lies on the plane of interest.
+    Vector disp = center - shared;
+    auto disp_e = EigenMap(disp);
 
-        // Check if vertices are colinear
-        if (length(bisector) == 0 || alpha < 1e-6 || fabs(alpha-2) < 1e-6)
-        {
-            perpNorm = pS;
-            normalize(perpNorm);
-        }
-        else
-        {
-            normalize(bisector);
-            // Normal of tangent plane
-            Vector tanNorm = cross(pS, nS);
-            // Get the perpendicular plane made up of plane normal of
-            // bisector
-            perpNorm = cross(tanNorm, bisector);
-            normalize(perpNorm);
-        }
+    // Perpendicular projector
+    auto perpProj = perpNorm * perpNorm; // tensor product
+    // Compute perpendicular component
+    Vector perp;
+    Eigen::Map perpProj_e(perpProj.data());
+    Eigen::Map perp_e(perp.data());
+    perp_e = perpProj_e * disp_e; // matrix (3x3) * vector = vector
+
+    sumWeights += alpha;
+
+    newPos += alpha * (center.position - perp);
+  }
+  newPos /= sumWeights;
+  /**
+   * Project to move onto LST eigenvector space and scale by eigenvalues.
+   *
+   * \bar{x} = x + \sum_{k=1}^3 \frac{1}{1+\lambda_k}((\bar{x} - x)\cdot
+   * \vec{e_k})\vec{e_k}
+   */
+  auto lst =
+      surfacemesh_detail::computeLocalStructureTensor(mesh, vertexID, rings);
+
+  EigenVector eigenvalues;
+  EigenMatrix eigenvectors;
+
+  EigenDiagonalizeTraits::diagonalizeSelfAdjointMatrix(
+      lst, eigenvalues, eigenvectors);
+
+  // auto eigen_result = getEigenvalues(lst);
+
+  // std::cout << "Eigenvalues(LST): "
+  //      << eigen_result.eigenvalues().transpose() << std::endl;
+  // std::cout << "Here's a matrix whose columns are eigenvectors of LST \n"
+  //      << "corresponding to these eigenvalues:\n"
+  //      << eigen_result.eigenvectors() << std::endl;
+
+  newPos -= center.position; // Vector of old position to new position
+  auto newPos_e = EigenMap(newPos);
+
+  // dot product followed by elementwise-division EQN 4. w is a scale factor.
+  auto w = ((eigenvectors.transpose() * newPos_e).array() /
+            (eigenvalues.array() + 1))
+               .matrix();      // vector 3x1
+  newPos_e = eigenvectors * w; // matrix 3x3 * vector = vector
+  center.position += newPos;
+}
 
-        // Get a reference vector to shared which lies on the plane of interest.
-        Vector disp = center - shared;
-        auto disp_e = EigenMap(disp);
+Vector weightedVertexSmoothCache(SurfaceMesh &mesh,
+                                 SurfaceMesh::SimplexID<1> vertexID,
+                                 std::size_t rings) {
+  auto centerName = mesh.get_name(vertexID)[0];
+  auto ¢er = *vertexID; // get the vertex data
+
+  double sumWeights = 0;
+  Vector newPos;
+
+  // Compute the following sum to get the new position
+  // \bar{x} = \frac{1}{\sum_{i=1}^{N_2}(\alpha_i+1)}\sum_{i=1}^{N_2}(\alpha_i
+  // + 1) x_i
+  for (auto edge : mesh.up(vertexID)) {
+    // Get the vertex connected by edge
+    auto edgeName = mesh.get_name(edge);
+    Vertex shared = *mesh.get_simplex_up(
+        {(edgeName[0] == centerName) ? edgeName[1] : edgeName[0]});
+
+    // Get the vertices connected to adjacent edge by face
+    auto cover = mesh.get_cover(edge);
+    if (cover.size() != 2) {
+      // Vertex is on a boundary... Don't move it
+      return Vector({0, 0, 0});
+    }
+    std::vector> faces;
+    mesh.up(edge, std::back_inserter(faces));
+
+    Vertex prev = *mesh.get_simplex_down(faces[0], edgeName);
+    Vertex next = *mesh.get_simplex_down(faces[1], edgeName);
+
+    // std::cout << mesh.get_simplex_up({up[0]}) << " "
+    //             << mesh.get_simplex_up({up[1]}) << " "
+    //             << mesh.get_simplex_up({(edgeName[0] == centerName) ?
+    // edgeName[1] : edgeName[0]}) << std::endl;
+
+    // std::cout << prev << " " << next << " " << shared << std::endl;
+    Vector pS, nS;
+    try {
+      pS = prev - shared;
+      normalize(pS);
+      nS = next - shared;
+      normalize(nS);
+    } catch (std::exception &e) {
+      gamer_runtime_error(
+          "ERROR: Zero length edge found. "
+          "weightedVertexSmooth expects no zero length edges.");
+    }
 
-        // Perpendicular projector
-        auto   perpProj = perpNorm*perpNorm; // tensor product
-        // Compute perpendicular component
-        Vector perp;
-        auto perpProj_e = EigenMap(perpProj);
-        auto perp_e = EigenMap(perp);
-        perp_e = perpProj_e*disp_e; // matrix (3x3) * vector = vector
+    // Bisector of the 'rhombus'
+    Vector bisector = pS + nS;
+    Vector perpNorm;
+    double alpha = (pS | nS) + 1;
+
+    // Check if vertices are colinear
+    if (length(bisector) == 0 || alpha < 1e-6 || fabs(alpha - 2) < 1e-6) {
+      perpNorm = pS;
+      normalize(perpNorm);
+    } else {
+      normalize(bisector);
+      // Normal of tangent plane
+      Vector tanNorm = cross(pS, nS);
+      // Get the perpendicular plane made up of plane normal of
+      // bisector
+      perpNorm = cross(tanNorm, bisector);
+      normalize(perpNorm);
+    }
 
-        sumWeights += alpha;
+    // Get a reference vector to shared which lies on the plane of interest.
+    Vector disp = center - shared;
+    auto disp_e = EigenMap(disp);
 
-        newPos += alpha*(center.position - perp);
-    }
-    newPos /= sumWeights;
-    /**
-     * Project to move onto LST eigenvector space and scale by eigenvalues.
-     *
-     * \bar{x} = x + \sum_{k=1}^3 \frac{1}{1+\lambda_k}((\bar{x} - x)\cdot
-     * \vec{e_k})\vec{e_k}
-     */
-    auto lst = computeLocalStructureTensor(mesh, vertexID, rings);
-
-    EigenVector eigenvalues;
-    EigenMatrix eigenvectors;
-
-    EigenDiagonalizeTraits::diagonalizeSelfAdjointMatrix(lst, eigenvalues, eigenvectors);
-
-    // std::cout << "Eigenvalues(LST): "
-    //      << eigen_result.eigenvalues().transpose() << std::endl;
-    // std::cout << "Here's a matrix whose columns are eigenvectors of LST \n"
-    //      << "corresponding to these eigenvalues:\n"
-    //      << eigen_result.eigenvectors() << std::endl;
-
-    newPos -= center.position;  // Vector of old position to new position
-    auto newPos_e = EigenMap(newPos);
-
-    // dot product followed by elementwise-division EQN 4. w is a scale factor.
-    auto w = (
-        (eigenvectors.transpose()*newPos_e).array()
-        / (eigenvalues.array()+1)
-        ).matrix();                           // vector 3x1
-    newPos_e = eigenvectors*w; // matrix 3x3 * vector = vector
-    // center.position += newPos;
-    return newPos;
+    // Perpendicular projector
+    auto perpProj = perpNorm * perpNorm; // tensor product
+    // Compute perpendicular component
+    Vector perp;
+    auto perpProj_e = EigenMap(perpProj);
+    auto perp_e = EigenMap(perp);
+    perp_e = perpProj_e * disp_e; // matrix (3x3) * vector = vector
+
+    sumWeights += alpha;
+
+    newPos += alpha * (center.position - perp);
+  }
+  newPos /= sumWeights;
+  /**
+   * Project to move onto LST eigenvector space and scale by eigenvalues.
+   *
+   * \bar{x} = x + \sum_{k=1}^3 \frac{1}{1+\lambda_k}((\bar{x} - x)\cdot
+   * \vec{e_k})\vec{e_k}
+   */
+  auto lst = computeLocalStructureTensor(mesh, vertexID, rings);
+
+  EigenVector eigenvalues;
+  EigenMatrix eigenvectors;
+
+  EigenDiagonalizeTraits::diagonalizeSelfAdjointMatrix(
+      lst, eigenvalues, eigenvectors);
+
+  // std::cout << "Eigenvalues(LST): "
+  //      << eigen_result.eigenvalues().transpose() << std::endl;
+  // std::cout << "Here's a matrix whose columns are eigenvectors of LST \n"
+  //      << "corresponding to these eigenvalues:\n"
+  //      << eigen_result.eigenvectors() << std::endl;
+
+  newPos -= center.position; // Vector of old position to new position
+  auto newPos_e = EigenMap(newPos);
+
+  // dot product followed by elementwise-division EQN 4. w is a scale factor.
+  auto w = ((eigenvectors.transpose() * newPos_e).array() /
+            (eigenvalues.array() + 1))
+               .matrix();      // vector 3x1
+  newPos_e = eigenvectors * w; // matrix 3x3 * vector = vector
+  // center.position += newPos;
+  return newPos;
 }
 
-
 /**
  * @brief      Perona-Malik normal based smoothing algorithm
  *
  * @param      mesh      The mesh
  * @param[in]  vertexID  The vertex id
  */
-void normalSmoothH(SurfaceMesh &mesh, SurfaceMesh::SimplexID<1> vertexID, double k)
-{
-    auto            name = mesh.get_name(vertexID)[0];
-    double          areaSum = 0;
-
-    auto            p = (*vertexID).position;
-    Eigen::Vector4d pos_e;
-    pos_e << p[0], p[1], p[2], 1;   // 4D for affine3D
-    Eigen::Vector4d newPos_e;
-    newPos_e << 0, 0, 0, 0;
-
-    // For each incident face get the average normal
-    auto incidentFaces = mesh.up(mesh.up(vertexID));
-    for (auto faceID : incidentFaces)
-    {
-        // Get the area of incident face
-        double area = getArea(mesh, faceID);
-        areaSum += area;
-
-        auto normal = getNormal(mesh, faceID);
-        normal /= std::sqrt(normal|normal);
-
-        // get the incident incident faces
-        std::vector > faces;
-        neighbors(mesh, faceID, std::back_inserter(faces));
-        Vector avgNorm;
-
-        double sumWeight = 0;
-
-        for (auto face : faces)
-        {
-            auto   inorm = getNormal(mesh, face);
-            inorm /= std::sqrt(inorm|inorm);
-            double weight = exp(k*(normal|inorm));
-            avgNorm   += weight*inorm;
-            sumWeight += weight;
-        }
-        avgNorm /= sumWeight;
-
-        // Compute the edge (axis) to rotate about.
-        auto edge = mesh.get_simplex_down(faceID, name);
-        auto edgeName = mesh.get_name(edge);
-        auto a = *mesh.get_simplex_up({edgeName[0]});
-        auto b = *mesh.get_simplex_up({edgeName[1]});
-
-        auto ab = a-b;
-        ab /= std::sqrt(ab|ab); // Eigen AngleAxis requires unit vector
-
-        // Angle between normals in radians. This is the angle to rotate the
-        // normal by.
-        double angle = std::copysign(std::acos(normal|avgNorm), dot(cross(normal, avgNorm), ab));
-        // Catch for floating point dot product issues
-        if (std::isnan(angle))
-        {
-            angle = 0;
-        }
-        //Vector rotAxis = (*faceID).orientation * ab; // We don't need this
-        // because the angle is relative.
-        Vector rotAxis = ab;
-
-        // build the transformation
-        auto rotAxis_e = EigenMap(rotAxis);
-        auto center_e = EigenMap(b.position);
-        Eigen::Affine3d             A = Eigen::Translation3d(center_e) * Eigen::AngleAxisd(angle, rotAxis_e) * Eigen::Translation3d(-center_e);
-
-        // Weight the new position by the area of the current face
-        newPos_e += area*(A*pos_e);
+void normalSmoothH(SurfaceMesh &mesh, SurfaceMesh::SimplexID<1> vertexID,
+                   double k) {
+  auto name = mesh.get_name(vertexID)[0];
+  double areaSum = 0;
+
+  auto p = (*vertexID).position;
+  Eigen::Vector4d pos_e{p[0], p[1], p[2], 1}; // 4D for affine3D
+  Eigen::Vector4d newPos_e{0,0,0,0};
+
+  // For each incident face get the average normal
+  auto incidentFaces = mesh.up(mesh.up(vertexID));
+  for (auto faceID : incidentFaces) {
+    // Get the area of incident face
+    double area = getArea(mesh, faceID);
+    areaSum += area;
+
+    auto normal = getNormal(mesh, faceID);
+    normal /= std::sqrt(normal | normal);
+
+    // get the incident incident faces
+    std::vector> faces;
+    neighbors(mesh, faceID, std::back_inserter(faces));
+    Vector avgNorm;
+
+    double sumWeight = 0;
+
+    for (auto face : faces) {
+      auto inorm = getNormal(mesh, face);
+      inorm /= std::sqrt(inorm | inorm);
+      double weight = exp(k * (normal | inorm));
+      avgNorm += weight * inorm;
+      sumWeight += weight;
     }
-    newPos_e /= areaSum; // weighted by area
-
-    (*vertexID).position = Vertex({newPos_e[0], newPos_e[1], newPos_e[2]});
-}
-
-void edgeFlip(SurfaceMesh &mesh, SurfaceMesh::SimplexID<2> edgeID)
-{
-    // Assuming that the mesh is manifold and edge has been vetted for flipping
-    auto name = mesh.get_name(edgeID);
-    auto up   = mesh.get_cover(edgeID);
-
-    std::array, 2> faces;
-    mesh.up(edgeID, faces.begin());
-
-    // Assume flipped edges are always selected
-    auto fdata = SMFace(0, true);
-    if ((*faces[0]).marker == (*faces[1]).marker)
-    {
-        fdata.marker = (*faces[0]).marker;
+    avgNorm /= sumWeight;
+
+    // Compute the edge (axis) to rotate about.
+    auto edge = mesh.get_simplex_down(faceID, name);
+    auto edgeName = mesh.get_name(edge);
+    auto a = *mesh.get_simplex_up({edgeName[0]});
+    auto b = *mesh.get_simplex_up({edgeName[1]});
+
+    auto ab = a - b;
+    ab /= std::sqrt(ab | ab); // Eigen AngleAxis requires unit vector
+
+    // Angle between normals in radians. This is the angle to rotate the
+    // normal by.
+    double angle = std::copysign(std::acos(normal | avgNorm),
+                                 dot(cross(normal, avgNorm), ab));
+    // Catch for floating point dot product issues
+    if (std::isnan(angle)) {
+      angle = 0;
     }
-
-    mesh.remove<2>({name[0], name[1]});
-    mesh.insert<3>({name[0], up[0], up[1]}, fdata);
-    mesh.insert<3>({name[1], up[0], up[1]}, fdata);
-
-    edgeID = mesh.get_simplex_up({up[0], up[1]});
-    (*edgeID).selected = true;
-
-
-    std::vector> verts;
-    mesh.down(edgeID, std::back_inserter(verts));
-    initLocalOrientation >::apply(mesh, std::set({up[0], up[1], name[0], name[1]}), verts.begin(), verts.end());
-
-    std::vector> nbors;
-    casc::neighbors(mesh, edgeID, std::back_inserter(nbors));
-    nbors.push_back(edgeID);
-    computeLocalOrientation(mesh, nbors);
+    // Vector rotAxis = (*faceID).orientation * ab; // We don't need this
+    // because the angle is relative.
+    Vector rotAxis = ab;
+
+    // build the transformation
+    auto rotAxis_e = EigenMap(rotAxis);
+    auto center_e = EigenMap(b.position);
+    Eigen::Affine3d A = Eigen::Translation3d(center_e) *
+                        Eigen::AngleAxisd(angle, rotAxis_e) *
+                        Eigen::Translation3d(-center_e);
+
+    // Weight the new position by the area of the current face
+    newPos_e += area * (A * pos_e);
+  }
+  newPos_e /= areaSum; // weighted by area
+
+  (*vertexID).position = Vertex({newPos_e[0], newPos_e[1], newPos_e[2]});
 }
 
-void edgeFlipCache(SurfaceMesh &mesh, SurfaceMesh::SimplexID<2> edgeID)
-{
-    // Assuming that the mesh is manifold and edge has been vetted for flipping
-    auto name = mesh.get_name(edgeID);
-    auto up   = mesh.get_cover(edgeID);
-
-    std::array, 2> faces;
-    mesh.up(edgeID, faces.begin());
-
-    // Assume flipped edges are always selected
-    auto fdata = SMFace(0, true);
-    if ((*faces[0]).marker == (*faces[1]).marker)
-    {
-        fdata.marker = (*faces[0]).marker;
-    }
-
-    std::array, 2> newFaces;
-    mesh.remove<2>({name[0], name[1]});
-    newFaces[0] = mesh.insert<3>({name[0], up[0], up[1]}, fdata);
-    newFaces[1] = mesh.insert<3>({name[1], up[0], up[1]}, fdata);
-
-    edgeID = mesh.get_simplex_up({up[0], up[1]});
-    (*edgeID).selected = true;
-
-    std::vector> verts;
-    mesh.down(edgeID, std::back_inserter(verts));
-    initLocalOrientation >::apply(mesh, std::set({up[0], up[1], name[0], name[1]}), verts.begin(), verts.end());
-
-    std::vector> nbors;
-    casc::neighbors(mesh, edgeID, std::back_inserter(nbors));
-    nbors.push_back(edgeID);
-    computeLocalOrientation(mesh, nbors);
-
-    for(auto fID : newFaces){
-        auto norm = getNormal(mesh, fID);
-        normalize(norm);
-        (*fID).normal = norm;
-    }
-
-    for(auto vID : verts){
-        Vector norm;
-        auto   faces = mesh.up(mesh.up(vID));
-        for (auto faceID : faces)
-        {
-            norm += (*faceID).normal;
-        }
-        normalize(norm);
-        (*vID).normal = norm;
-    }
+void edgeFlip(SurfaceMesh &mesh, SurfaceMesh::SimplexID<2> edgeID) {
+  // Assuming that the mesh is manifold and edge has been vetted for flipping
+  auto name = mesh.get_name(edgeID);
+  auto up = mesh.get_cover(edgeID);
+
+  std::array, 2> faces;
+  mesh.up(edgeID, faces.begin());
+
+  // Assume flipped edges are always selected
+  auto fdata = SMFace(0, true);
+  if ((*faces[0]).marker == (*faces[1]).marker) {
+    fdata.marker = (*faces[0]).marker;
+  }
+
+  mesh.remove<2>({name[0], name[1]});
+  mesh.insert<3>({name[0], up[0], up[1]}, fdata);
+  mesh.insert<3>({name[1], up[0], up[1]}, fdata);
+
+  edgeID = mesh.get_simplex_up({up[0], up[1]});
+  (*edgeID).selected = true;
+
+  std::vector> verts;
+  mesh.down(edgeID, std::back_inserter(verts));
+  initLocalOrientation>::apply(
+      mesh, std::set({up[0], up[1], name[0], name[1]}), verts.begin(),
+      verts.end());
+
+  std::vector> nbors;
+  casc::neighbors(mesh, edgeID, std::back_inserter(nbors));
+  nbors.push_back(edgeID);
+  computeLocalOrientation(mesh, nbors);
 }
 
-bool checkFlipAngle(const SurfaceMesh &mesh, const SurfaceMesh::SimplexID<2> &edgeID)
-{
-    auto getMinAngle = [](const Vertex &a, const Vertex &b, const Vertex &c){
-            double              minAngle = 999; // dummy for now
-            double              tmp;
-            std::array triangle = {a, b, c};
-            std::array sigma = {0,1,2};
-            for(int i = 0; i < 3; ++i){
-                try
-                {
-                    tmp = angleDeg(triangle[sigma[0]], triangle[sigma[1]], triangle[sigma[2]]);
-                }
-                catch (std::runtime_error &e)
-                {
-                    throw std::runtime_error("Angle is undefined for face with zero area. Try running degenerate dissolve in Blender and ensure manifoldness.");
-                }
-                if (tmp < minAngle)
-                minAngle = tmp;
-
-                std::rotate(sigma.begin(), sigma.begin() + 1, sigma.end());
-            }
-            return minAngle;
-        };
-
-    auto name = mesh.get_name(edgeID);
-    std::pair shared;
-
-    shared.first  = *mesh.get_simplex_down(edgeID, name[0]);
-    shared.second = *mesh.get_simplex_down(edgeID, name[1]);
-
-    // TODO: (50) Benchmark for performance increase from going up to face then back down...
-    std::pair notShared;
-    auto up   = mesh.get_cover(edgeID);
-    notShared.first  = *mesh.get_simplex_up({up[0]});
-    notShared.second = *mesh.get_simplex_up({up[1]});
-
-    // TODO: (5) Change to check link condition
-    // Check if the triangles make a wedge shape. Flipping this can
-    // cause knife faces.
-    auto   f1   = (shared.first - notShared.first)^(shared.second - notShared.first);
-    auto   f2   = (shared.first - notShared.second)^(shared.second - notShared.second);
-    auto   f3   = (notShared.first - shared.first)^(notShared.second - shared.first);
-    auto   f4   = (notShared.first - shared.second)^(notShared.second - shared.second);
-    auto   area = std::pow(std::sqrt(f1|f1) + std::sqrt(f2|f2), 2);
-    auto   areaFlip = std::pow(std::sqrt(f3|f3) + std::sqrt(f4|f4), 2);
-    double edgeFlipCriterion = 1.001;
-    // If area changes by a lot continue
-    if (areaFlip/area > edgeFlipCriterion)
-        return false;
-
-    int excess = checkFlipValenceExcess(mesh, edgeID);
-    if (excess < -3){
-        // Force flip since it improves valence by a lot
-        return true;
-    }
-    else if (excess > 3){
-        // Flipping is too bad...
-        return false;
+void edgeFlipCache(SurfaceMesh &mesh, SurfaceMesh::SimplexID<2> edgeID) {
+  // Assuming that the mesh is manifold and edge has been vetted for flipping
+  auto name = mesh.get_name(edgeID);
+  auto up = mesh.get_cover(edgeID);
+
+  std::array, 2> faces;
+  mesh.up(edgeID, faces.begin());
+
+  // Assume flipped edges are always selected
+  auto fdata = SMFace(0, true);
+  if ((*faces[0]).marker == (*faces[1]).marker) {
+    fdata.marker = (*faces[0]).marker;
+  }
+
+  std::array, 2> newFaces;
+  mesh.remove<2>({name[0], name[1]});
+  newFaces[0] = mesh.insert<3>({name[0], up[0], up[1]}, fdata);
+  newFaces[1] = mesh.insert<3>({name[1], up[0], up[1]}, fdata);
+
+  edgeID = mesh.get_simplex_up({up[0], up[1]});
+  (*edgeID).selected = true;
+
+  std::vector> verts;
+  mesh.down(edgeID, std::back_inserter(verts));
+  initLocalOrientation>::apply(
+      mesh, std::set({up[0], up[1], name[0], name[1]}), verts.begin(),
+      verts.end());
+
+  std::vector> nbors;
+  casc::neighbors(mesh, edgeID, std::back_inserter(nbors));
+  nbors.push_back(edgeID);
+  computeLocalOrientation(mesh, nbors);
+
+  for (auto fID : newFaces) {
+    auto norm = getNormal(mesh, fID);
+    normalize(norm);
+    (*fID).normal = norm;
+  }
+
+  for (auto vID : verts) {
+    Vector norm;
+    auto faces = mesh.up(mesh.up(vID));
+    for (auto faceID : faces) {
+      norm += (*faceID).normal;
     }
+    normalize(norm);
+    (*vID).normal = norm;
+  }
+}
 
-    // Go through all angle combinations
+bool checkFlipAngle(const SurfaceMesh &mesh,
+                    const SurfaceMesh::SimplexID<2> &edgeID) {
+  auto getMinAngle = [](const Vertex &a, const Vertex &b, const Vertex &c) {
+    double minAngle = 999; // dummy for now
     double tmp;
-    double minAngle = getMinAngle(shared.first, shared.second, notShared.first);
-    tmp = getMinAngle(shared.first, shared.second, notShared.second);
-    if (tmp < minAngle)
+    std::array triangle = {a, b, c};
+    std::array sigma = {0, 1, 2};
+    for (int i = 0; i < 3; ++i) {
+      try {
+        tmp = angleDeg(triangle[sigma[0]], triangle[sigma[1]],
+                       triangle[sigma[2]]);
+      } catch (std::runtime_error &e) {
+        gamer_runtime_error(
+            "Angle is undefined for face with zero area. Try running "
+            "degenerate dissolve in Blender and ensure manifoldness.");
+      }
+      if (tmp < minAngle)
         minAngle = tmp;
 
-    double minAngleFlip = getMinAngle(notShared.first, notShared.second, shared.first);
-    tmp = getMinAngle(notShared.first, notShared.second, shared.second);
-    if (tmp < minAngleFlip)
-        minAngleFlip = tmp;
-
-    if (minAngleFlip > minAngle){
-        return true;
+      std::rotate(sigma.begin(), sigma.begin() + 1, sigma.end());
     }
+    return minAngle;
+  };
+
+  auto name = mesh.get_name(edgeID);
+  std::pair shared;
+
+  shared.first = *mesh.get_simplex_down(edgeID, name[0]);
+  shared.second = *mesh.get_simplex_down(edgeID, name[1]);
+
+  // TODO: (50) Benchmark for performance increase from going up to face then
+  // back down...
+  std::pair notShared;
+  auto up = mesh.get_cover(edgeID);
+  notShared.first = *mesh.get_simplex_up({up[0]});
+  notShared.second = *mesh.get_simplex_up({up[1]});
+
+  // TODO: (5) Change to check link condition
+  // Check if the triangles make a wedge shape. Flipping this can
+  // cause knife faces.
+  auto f1 =
+      (shared.first - notShared.first) ^ (shared.second - notShared.first);
+  auto f2 =
+      (shared.first - notShared.second) ^ (shared.second - notShared.second);
+  auto f3 =
+      (notShared.first - shared.first) ^ (notShared.second - shared.first);
+  auto f4 =
+      (notShared.first - shared.second) ^ (notShared.second - shared.second);
+  auto area = std::pow(std::sqrt(f1 | f1) + std::sqrt(f2 | f2), 2);
+  auto areaFlip = std::pow(std::sqrt(f3 | f3) + std::sqrt(f4 | f4), 2);
+  double edgeFlipCriterion = 1.001;
+  // If area changes by a lot continue
+  if (areaFlip / area > edgeFlipCriterion)
+    return false;
+
+  int excess = checkFlipValenceExcess(mesh, edgeID);
+  if (excess < -3) {
+    // Force flip since it improves valence by a lot
+    return true;
+  } else if (excess > 3) {
+    // Flipping is too bad...
     return false;
+  }
+
+  // Go through all angle combinations
+  double tmp;
+  double minAngle = getMinAngle(shared.first, shared.second, notShared.first);
+  tmp = getMinAngle(shared.first, shared.second, notShared.second);
+  if (tmp < minAngle)
+    minAngle = tmp;
+
+  double minAngleFlip =
+      getMinAngle(notShared.first, notShared.second, shared.first);
+  tmp = getMinAngle(notShared.first, notShared.second, shared.second);
+  if (tmp < minAngleFlip)
+    minAngleFlip = tmp;
+
+  if (minAngleFlip > minAngle) {
+    return true;
+  }
+  return false;
 }
 
-int checkFlipValenceExcess(const SurfaceMesh &mesh, const SurfaceMesh::SimplexID<2> &edgeID)
-{
-    auto name = mesh.get_name(edgeID);
-    std::pair, SurfaceMesh::SimplexID<1> > shared;
-    shared.first  = mesh.get_simplex_down(edgeID, name[0]);
-    shared.second = mesh.get_simplex_down(edgeID, name[1]);
-    std::pair, SurfaceMesh::SimplexID<1> > notShared;
-    auto up = mesh.get_cover(edgeID);
-    notShared.first  = mesh.get_simplex_up({up[0]});
-    notShared.second = mesh.get_simplex_up({up[1]});
-    std::array valence;
-
-    // assuming there are no boundaries
-    valence[0] = getValence(mesh, shared.first)-6;
-    valence[1] = getValence(mesh, shared.second)-6;
-    valence[2] = getValence(mesh, notShared.first)-6;
-    valence[3] = getValence(mesh, notShared.second)-6;
-
-    // std::cout << "Before: " << casc::to_string(valence) << std::endl;
-    int excess = 0;
-    for (int i = 0; i < 4; i++)
-    {
-        excess += std::pow(valence[i], 2);
-    }
-    // simulate the flip
-    valence[0] -= 1;
-    valence[1] -= 1;
-    valence[2] += 1;
-    valence[3] += 1;
-
-    // std::cout << "After: " << casc::to_string(valence) << std::endl;
-    int flipExcess = 0;
-    for (int i = 0; i < 4; i++)
-    {
-        flipExcess += std::pow(valence[i], 2);
-    }
-    return flipExcess - excess;
+int checkFlipValenceExcess(const SurfaceMesh &mesh,
+                           const SurfaceMesh::SimplexID<2> &edgeID) {
+  auto name = mesh.get_name(edgeID);
+  std::pair, SurfaceMesh::SimplexID<1>> shared;
+  shared.first = mesh.get_simplex_down(edgeID, name[0]);
+  shared.second = mesh.get_simplex_down(edgeID, name[1]);
+  std::pair, SurfaceMesh::SimplexID<1>> notShared;
+  auto up = mesh.get_cover(edgeID);
+  notShared.first = mesh.get_simplex_up({up[0]});
+  notShared.second = mesh.get_simplex_up({up[1]});
+  std::array valence;
+
+  // assuming there are no boundaries
+  valence[0] = getValence(mesh, shared.first) - 6;
+  valence[1] = getValence(mesh, shared.second) - 6;
+  valence[2] = getValence(mesh, notShared.first) - 6;
+  valence[3] = getValence(mesh, notShared.second) - 6;
+
+  // std::cout << "Before: " << casc::to_string(valence) << std::endl;
+  int excess = 0;
+  for (int i = 0; i < 4; i++) {
+    excess += std::pow(valence[i], 2);
+  }
+  // simulate the flip
+  valence[0] -= 1;
+  valence[1] -= 1;
+  valence[2] += 1;
+  valence[3] += 1;
+
+  // std::cout << "After: " << casc::to_string(valence) << std::endl;
+  int flipExcess = 0;
+  for (int i = 0; i < 4; i++) {
+    flipExcess += std::pow(valence[i], 2);
+  }
+  return flipExcess - excess;
 }
 
 // Angle Mesh improvement by projecting barycenter on tangent plane...
-void barycenterVertexSmooth(SurfaceMesh &mesh, SurfaceMesh::SimplexID<1> vertexID)
-{
-    // get the neighbors
-    std::vector > vertices;
-    neighbors_up(mesh, vertexID, std::back_inserter(vertices));
-    // compute the average position
-    Vector avgPos;
-    for (auto vertex : vertices)
-    {
-        avgPos += (*vertex).position;
-    }
-    avgPos /= vertices.size();
-
-    auto disp = avgPos - (*vertexID).position;
-    // Project onto tangent plane
-    // A||B = Bx(AxB/|B|)/|B|
-    // A_|_B = A.B*B/|B|^2
-    //auto norm = getNormalFromTangent(getTangent(mesh, vertexID));
-    auto norm = getNormal(mesh, vertexID);
-    norm /= std::sqrt(norm|norm); // normalize
-    auto perpProj = norm*norm;    // tensor product
-
-    auto disp_e = EigenMap(disp);
-
-    // Compute perpendicular component
-    // Vector perp;
-    // Eigen::Map perpProj_e(perpProj.data());
-    // Eigen::Map perp_e(perp.data());
-    // perp_e = perpProj_e*disp_e;
-
-    // Compute the parallel component
-    Vector               parallel;
-    tensor identity{{
-        1, 0, 0, 0, 1, 0, 0, 0, 1
-    }};
-    auto llproj = identity-perpProj; // perpendicular projector
-    auto llproj_e  = EigenMap(llproj);
-    auto parallel_e = EigenMap(parallel);
-    parallel_e = llproj_e*disp_e;
-
-    (*vertexID).position = (*vertexID).position + parallel;
+void barycenterVertexSmooth(SurfaceMesh &mesh,
+                            SurfaceMesh::SimplexID<1> vertexID) {
+  // get the neighbors
+  std::vector> vertices;
+  neighbors_up(mesh, vertexID, std::back_inserter(vertices));
+  // compute the average position
+  Vector avgPos;
+  for (auto vertex : vertices) {
+    avgPos += (*vertex).position;
+  }
+  avgPos /= vertices.size();
+
+  auto disp = avgPos - (*vertexID).position;
+  // Project onto tangent plane
+  // A||B = Bx(AxB/|B|)/|B|
+  // A_|_B = A.B*B/|B|^2
+  // auto norm = getNormalFromTangent(getTangent(mesh, vertexID));
+  auto norm = getNormal(mesh, vertexID);
+  norm /= std::sqrt(norm | norm); // normalize
+  auto perpProj = norm * norm;    // tensor product
+
+  auto disp_e = EigenMap(disp);
+
+  // Compute perpendicular component
+  // Vector perp;
+  // Eigen::Map perpProj_e(perpProj.data());
+  // Eigen::Map perp_e(perp.data());
+  // perp_e = perpProj_e*disp_e;
+
+  // Compute the parallel component
+  Vector parallel;
+  tensor identity{{1, 0, 0, 0, 1, 0, 0, 0, 1}};
+  auto llproj = identity - perpProj; // perpendicular projector
+  auto llproj_e = EigenMap(llproj);
+  auto parallel_e = EigenMap(parallel);
+  parallel_e = llproj_e * disp_e;
+
+  (*vertexID).position = (*vertexID).position + parallel;
 }
 
-void findHoles(const SurfaceMesh                                     &mesh,
-               std::vector > > &holeList)
-{
-    // TODO: (25) Update this to return a pair of edge ring and vertex ring
-    std::set > bdryEdges;
-
-    // Collect all of the boundary edges into boundarySet
-    for (auto edgeID : mesh.get_level_id<2>())
-    {
-        auto cover = mesh.get_cover(edgeID);
-        if (cover.size() == 1)
-        {
-            bdryEdges.insert(edgeID);
-        }
-    }
+void findHoles(const SurfaceMesh &mesh,
+               std::vector>> &holeList) {
+  // TODO: (25) Update this to return a pair of edge ring and vertex ring
+  std::set> bdryEdges;
 
-    // Connect the edges into rings...
-    while (bdryEdges.size() > 0)
-    {
-        // Container to store ring
-        std::vector > bdryRing;
-        // // Pop first edge from remaining edges
-        // auto it = bdryEdges.begin();
-        // auto firstEdge = *it;
-        // bdryRing.push_back(firstEdge);
-        // bdryEdges.erase(it);
-        std::vector > visitedVerts;
-        // mesh.down(firstEdge, std::back_inserter(visitedVerts));
-        // Try to complete the ring
-        if (!orderBoundaryEdgeRing(mesh, bdryEdges, visitedVerts, bdryRing))
-        {
-            throw std::runtime_error("Couldn't connect ring");
-        }
-        holeList.push_back(bdryRing);
+  // Collect all of the boundary edges into boundarySet
+  for (auto edgeID : mesh.get_level_id<2>()) {
+    auto cover = mesh.get_cover(edgeID);
+    if (cover.size() == 1) {
+      bdryEdges.insert(edgeID);
     }
-}
-
-bool orderBoundaryEdgeRing(const SurfaceMesh                       &mesh,
-                           std::set >    &unvisitedBdryEdges,
-                           std::vector > &visitedVerts,
-                           std::vector > &bdryRing)
-{
-    if (visitedVerts.empty() && bdryRing.empty())
-    {
-        // Pop first edge from unvisited edges
-        auto it = unvisitedBdryEdges.begin();
-        auto firstEdge = *it;
-        // Push the edge into the ring
-        bdryRing.push_back(firstEdge);
-        unvisitedBdryEdges.erase(it);
-        // Push vertices of edge into visited vertices
-        mesh.down(firstEdge, std::back_inserter(visitedVerts));
+  }
+
+  // Connect the edges into rings...
+  while (bdryEdges.size() > 0) {
+    // Container to store ring
+    std::vector> bdryRing;
+    // // Pop first edge from remaining edges
+    // auto it = bdryEdges.begin();
+    // auto firstEdge = *it;
+    // bdryRing.push_back(firstEdge);
+    // bdryEdges.erase(it);
+    std::vector> visitedVerts;
+    // mesh.down(firstEdge, std::back_inserter(visitedVerts));
+    // Try to complete the ring
+    if (!orderBoundaryEdgeRing(mesh, bdryEdges, visitedVerts, bdryRing)) {
+      gamer_runtime_error("Couldn't connect ring");
     }
+    holeList.push_back(bdryRing);
+  }
+}
 
-    auto firstEdge = bdryRing.front();
-    auto currEdge  = bdryRing.back();
-    auto currVert  = visitedVerts.back();
-
-    std::vector > nbors;
-    neighbors(mesh, currEdge, std::back_inserter(nbors));
-
-    // Look for connected unvisited boundary edge
-    for (auto nbor : nbors)
-    {
-        auto result = unvisitedBdryEdges.find(nbor);
-        if (result != unvisitedBdryEdges.end())
-        {
-            auto nextEdge = *result;
-            std::array, 2> verts;
-            mesh.down(nextEdge, verts.begin());
-            int found = 0;
-            for (; found < 2; ++found)
-            {
-                if (verts[found] == currVert)
-                    break;
-            }
-
-            SurfaceMesh::SimplexID<1> nextVert;
-
-            if (found == 0)
-            {
-                nextVert = verts[1];
-            }
-            else if (found == 1)
-            {
-                nextVert = verts[0];
-            }
-            else
-            {
-                continue;
-            }
-
-            // If this closes the ring...
-            if (nextVert == visitedVerts.front())
-            {
-                bdryRing.push_back(nextEdge);
-                unvisitedBdryEdges.erase(result);
-                return true;
-            }
-
-            // Check if we have visited the next vertex already
-            auto visited = std::find(visitedVerts.begin(), visitedVerts.end(), nextVert);
-            if (visited == visitedVerts.end())
-            {
-                visitedVerts.push_back(nextVert);
-                bdryRing.push_back(nextEdge);
-                unvisitedBdryEdges.erase(result);
-
-                if (orderBoundaryEdgeRing(mesh, unvisitedBdryEdges, visitedVerts, bdryRing))
-                {
-                    return true;
-                }
-            }
+bool orderBoundaryEdgeRing(
+    const SurfaceMesh &mesh,
+    std::set> &unvisitedBdryEdges,
+    std::vector> &visitedVerts,
+    std::vector> &bdryRing) {
+  if (visitedVerts.empty() && bdryRing.empty()) {
+    // Pop first edge from unvisited edges
+    auto it = unvisitedBdryEdges.begin();
+    auto firstEdge = *it;
+    // Push the edge into the ring
+    bdryRing.push_back(firstEdge);
+    unvisitedBdryEdges.erase(it);
+    // Push vertices of edge into visited vertices
+    mesh.down(firstEdge, std::back_inserter(visitedVerts));
+  }
+
+  auto firstEdge = bdryRing.front();
+  auto currEdge = bdryRing.back();
+  auto currVert = visitedVerts.back();
+
+  std::vector> nbors;
+  neighbors(mesh, currEdge, std::back_inserter(nbors));
+
+  // Look for connected unvisited boundary edge
+  for (auto nbor : nbors) {
+    auto result = unvisitedBdryEdges.find(nbor);
+    if (result != unvisitedBdryEdges.end()) {
+      auto nextEdge = *result;
+      std::array, 2> verts;
+      mesh.down(nextEdge, verts.begin());
+      int found = 0;
+      for (; found < 2; ++found) {
+        if (verts[found] == currVert)
+          break;
+      }
+
+      SurfaceMesh::SimplexID<1> nextVert;
+
+      if (found == 0) {
+        nextVert = verts[1];
+      } else if (found == 1) {
+        nextVert = verts[0];
+      } else {
+        continue;
+      }
+
+      // If this closes the ring...
+      if (nextVert == visitedVerts.front()) {
+        bdryRing.push_back(nextEdge);
+        unvisitedBdryEdges.erase(result);
+        return true;
+      }
+
+      // Check if we have visited the next vertex already
+      auto visited =
+          std::find(visitedVerts.begin(), visitedVerts.end(), nextVert);
+      if (visited == visitedVerts.end()) {
+        visitedVerts.push_back(nextVert);
+        bdryRing.push_back(nextEdge);
+        unvisitedBdryEdges.erase(result);
+
+        if (orderBoundaryEdgeRing(mesh, unvisitedBdryEdges, visitedVerts,
+                                  bdryRing)) {
+          return true;
         }
+      }
     }
+  }
 
-    // Ring isn't complete. Backtrack...
-    unvisitedBdryEdges.insert(currEdge);
-    visitedVerts.pop_back();
-    bdryRing.pop_back();
-    return false;
+  // Ring isn't complete. Backtrack...
+  unvisitedBdryEdges.insert(currEdge);
+  visitedVerts.pop_back();
+  bdryRing.pop_back();
+  return false;
 }
 
-void edgeRingToVertices(const SurfaceMesh                                                  &mesh,
-                        std::vector >                            &edgeRing,
-                        std::back_insert_iterator > > iter)
-{
-    auto edgeRingIT = edgeRing.begin();
-    std::array, 2> first, next;
-    SurfaceMesh::SimplexID<1>                prevVertex;
-
-    mesh.down(*edgeRingIT++, first.begin());
+void edgeRingToVertices(
+    const SurfaceMesh &mesh, std::vector> &edgeRing,
+    std::back_insert_iterator>> iter) {
+  auto edgeRingIT = edgeRing.begin();
+  std::array, 2> first, next;
+  SurfaceMesh::SimplexID<1> prevVertex;
+
+  mesh.down(*edgeRingIT++, first.begin());
+  mesh.down(*edgeRingIT, next.begin());
+
+  // Set the first edge
+  if (first[0] != next[0] && first[0] != next[1]) {
+    *iter++ = first[0];
+    *iter++ = first[1];
+    prevVertex = first[1];
+  } else {
+    *iter++ = first[1];
+    *iter++ = first[0];
+    prevVertex = first[0];
+  }
+
+  // Go through remaining Edges
+  for (; edgeRingIT != edgeRing.end() - 1; ++edgeRingIT) {
     mesh.down(*edgeRingIT, next.begin());
-
-    // Set the first edge
-    if (first[0] != next[0] && first[0] != next[1])
-    {
-        *iter++ = first[0];
-        *iter++ = first[1];
-        prevVertex = first[1];
-    }
-    else
-    {
-        *iter++ = first[1];
-        *iter++ = first[0];
-        prevVertex = first[0];
-    }
-
-    // Go through remaining Edges
-    for (; edgeRingIT != edgeRing.end()-1; ++edgeRingIT)
-    {
-        mesh.down(*edgeRingIT, next.begin());
-        if (next[0] == prevVertex)
-        {
-            *iter++ = next[1];
-            prevVertex = next[1];
-        }
-        else
-        {
-            *iter++ = next[0];
-            prevVertex = next[0];
-        }
+    if (next[0] == prevVertex) {
+      *iter++ = next[1];
+      prevVertex = next[1];
+    } else {
+      *iter++ = next[0];
+      prevVertex = next[0];
     }
+  }
 }
 
-bool checkEdgeFlip(const SurfaceMesh &mesh,
-                     bool preserveRidges,
-                     SurfaceMesh::SimplexID<2> edgeID,
-                     std::function &)> &&checkFlip
-                )
-{
-    if ((*edgeID).selected == false)
-        return false;
-
-    auto up = mesh.get_cover(edgeID);
-    // The mesh is not a surface mesh...
-    if (up.size() > 2)
-    {
-        // std::cerr << "This edge participates in more than 2
-        // faces. "
-        //           << "Returning..." << std::endl;
-        throw std::runtime_error("SurfaceMesh is not pseudomanifold. Found an edge connected to more than 2 faces.");
-    }
-    else if (up.size() < 2) // Edge is a boundary
-    {
-        // std::cerr << "This edge participates in fewer than 2
-        // faces. "
-        //           << "Returning..." << std::endl;
-        return false;
-    }
-    auto name = mesh.get_name(edgeID);
-    std::pair shared;
-    shared.first  = *mesh.get_simplex_up({name[0]});
-    shared.second = *mesh.get_simplex_up({name[1]});
-
-    std::pair notShared;
-    notShared.first  = *mesh.get_simplex_up({up[0]});
-    notShared.second = *mesh.get_simplex_up({up[1]});
-
-    // Check if the edge is a part of a tetrahedron.
-    if (mesh.exists<2>({up[0], up[1]}))
-    {
-        // std::cerr << "Found a tetrahedron cannot edge flip."
-        //           << std::endl;
-        return false;
-    }
+bool checkEdgeFlip(
+    const SurfaceMesh &mesh, bool preserveRidges,
+    SurfaceMesh::SimplexID<2> edgeID,
+    std::function &)>
+        &&checkFlip) {
+  if ((*edgeID).selected == false)
+    return false;
 
-    // Check if we're on a ridge. This prevents folding also.
-    if (preserveRidges)
-    {
-        auto a   = getNormal(mesh, mesh.get_simplex_up(edgeID, up[0]));
-        auto b   = getNormal(mesh, mesh.get_simplex_up(edgeID, up[1]));
-        auto val = angleDeg(a, b);
-        if (val > 60)
-        {
-            return false;
-        }
+  auto up = mesh.get_cover(edgeID);
+  // The mesh is not a surface mesh...
+  if (up.size() > 2) {
+    // std::cerr << "This edge participates in more than 2
+    // faces. "
+    //           << "Returning..." << std::endl;
+    gamer_runtime_error("SurfaceMesh is not pseudomanifold. Found an edge "
+                             "connected to more than 2 faces.");
+  } else if (up.size() < 2) // Edge is a boundary
+  {
+    // std::cerr << "This edge participates in fewer than 2
+    // faces. "
+    //           << "Returning..." << std::endl;
+    return false;
+  }
+  auto name = mesh.get_name(edgeID);
+  std::pair shared;
+  shared.first = *mesh.get_simplex_up({name[0]});
+  shared.second = *mesh.get_simplex_up({name[1]});
+
+  std::pair notShared;
+  notShared.first = *mesh.get_simplex_up({up[0]});
+  notShared.second = *mesh.get_simplex_up({up[1]});
+
+  // Check if the edge is a part of a tetrahedron.
+  if (mesh.exists<2>({up[0], up[1]})) {
+    // std::cerr << "Found a tetrahedron cannot edge flip."
+    //           << std::endl;
+    return false;
+  }
+
+  // Check if we're on a ridge. This prevents folding also.
+  if (preserveRidges) {
+    auto a = getNormal(mesh, mesh.get_simplex_up(edgeID, up[0]));
+    auto b = getNormal(mesh, mesh.get_simplex_up(edgeID, up[1]));
+    auto val = angleDeg(a, b);
+    if (val > 60) {
+      return false;
     }
+  }
+
+  // TODO: (5) Implement a better algorithm to prevent folding
+  // Check if the triangles make a wedge shape. Flipping this can
+  // cause knife faces.
+  auto f1 =
+      (shared.first - notShared.first) ^ (shared.second - notShared.first);
+  auto f2 =
+      (shared.first - notShared.second) ^ (shared.second - notShared.second);
+  auto f3 =
+      (notShared.first - shared.first) ^ (notShared.second - shared.first);
+  auto f4 =
+      (notShared.first - shared.second) ^ (notShared.second - shared.second);
+  auto area = std::pow(std::sqrt(f1 | f1) + std::sqrt(f2 | f2), 2);
+  auto areaFlip = std::pow(std::sqrt(f3 | f3) + std::sqrt(f4 | f4), 2);
+  double edgeFlipCriterion = 1.001;
+  // If area changes by a lot continue
+  if (areaFlip / area > edgeFlipCriterion)
+    return false;
 
-    // TODO: (5) Implement a better algorithm to prevent folding
-    // Check if the triangles make a wedge shape. Flipping this can
-    // cause knife faces.
-    auto   f1   = (shared.first - notShared.first)^(shared.second - notShared.first);
-    auto   f2   = (shared.first - notShared.second)^(shared.second - notShared.second);
-    auto   f3   = (notShared.first - shared.first)^(notShared.second - shared.first);
-    auto   f4   = (notShared.first - shared.second)^(notShared.second - shared.second);
-    auto   area = std::pow(std::sqrt(f1|f1) + std::sqrt(f2|f2), 2);
-    auto   areaFlip = std::pow(std::sqrt(f3|f3) + std::sqrt(f4|f4), 2);
-    double edgeFlipCriterion = 1.001;
-    // If area changes by a lot continue
-    if (areaFlip/area > edgeFlipCriterion)
-        return false;
-
-    // Check the flip using user function
-    return checkFlip(mesh, edgeID);
+  // Check the flip using user function
+  return checkFlip(mesh, edgeID);
 }
 } // end namespace surfacemesh_detail
 } // end namespace gamer
diff --git a/src/TetMesh.cpp b/src/TetMesh.cpp
index b7f735de..4b453ef4 100644
--- a/src/TetMesh.cpp
+++ b/src/TetMesh.cpp
@@ -1,954 +1,881 @@
-/*
- * ***************************************************************************
- * This file is part of the GAMer software.
- * Copyright (C) 2016-2018
- * by Christopher Lee, John Moody, Rommie Amaro, J. Andrew McCammon,
- *    and Michael Holst
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * ***************************************************************************
- */
+// This file is part of the GAMer software.
+// Copyright (C) 2016-2021
+// by Christopher T. Lee and contributors
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, see 
+// or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+// Boston, MA 02111-1307 USA
 
-#include 
 #include 
+#include 
 #include 
 #include 
-#include 
 #include 
+#include 
 #include 
 #include 
 #include 
 #include 
 #include 
-#include 
 #include 
+#include 
 #include 
 
 #include 
 
-#include "gamer/TetMesh.h"
 #include "gamer/SurfaceMesh.h"
+#include "gamer/TetMesh.h"
 
 /// Namespace for all things gamer
-namespace gamer
-{
-
-std::unique_ptr makeTetMesh(
-    const std::vector &surfmeshes,
-    std::string                      tetgen_params)
-{
-
-    // Create new tetmesh object
-    std::unique_ptr tetmesh(new TetMesh);
-
-    size_t                   nVertices = 0, nFaces = 0, nRegions = 0, nHoles = 0;
-    int i = 0;
-    for (auto &surfmesh : surfmeshes)
-    {
-        size_t nverts = surfmesh->template size<1>();
-        size_t nfaces = surfmesh->template size<3>();
-
-        if (nverts == 0 || nfaces == 0)
-        {
-            std::stringstream ss;
-            ss << "SurfaceMesh " << i << " contains no data."
-               << " Cannot tetrahedralize nothing.";
-            throw std::runtime_error(ss.str());
-        }
-
-        auto metadata = *surfmesh->get_simplex_up();
-
-
-        // TODO: (10) Use a more rigorous check of closedness
-        if (hasHole(*surfmesh))
-        {
-            std::stringstream ss;
-            ss << "SurfaceMesh " << i << " is not closed. Cannot tetrahedralize non-manifold objects.";
-            throw std::runtime_error(ss.str());
-        }
-
-        metadata.ishole ? ++nHoles : ++nRegions;
-
-        nVertices += nverts;
-        nFaces += nfaces;
-        ++i;
-    }
-
-    if (nRegions < 1)
-    {
-        throw std::runtime_error("No non-hole Surface Meshes found. makeTetMesh expects at least one non-hole SurfaceMesh");
-    }
-
-    std::cout << "Number of vertices: " << nVertices << std::endl;
-    std::cout << "Number of Faces: " << nFaces << std::endl;
-    std::cout << "Number of Regions: " << nRegions << std::endl;
-    std::cout << "Number of Holes: " << nHoles << std::endl;
+namespace gamer {
 
-    tetgenio           in, out;
+std::unique_ptr
+makeTetMesh(const std::vector &surfmeshes,
+            std::string tetgen_params) {
 
-    tetgenio::facet   *f;
-    tetgenio::polygon *p;
+  // Create new tetmesh object
+  std::unique_ptr tetmesh(new TetMesh);
 
-    // Allocate memory for tetgenio object arrays
-    in.numberofpoints = nVertices;
-    in.pointlist = new REAL[nVertices * 3];
+  size_t nVertices = 0, nFaces = 0, nRegions = 0, nHoles = 0;
+  int i = 0;
+  for (auto &surfmesh : surfmeshes) {
+    size_t nverts = surfmesh->template size<1>();
+    size_t nfaces = surfmesh->template size<3>();
 
-    in.numberoffacets = nFaces;
-    in.facetlist = new tetgenio::facet[nFaces];
-    in.facetmarkerlist = new int[nFaces];
-
-    in.numberofregions = nRegions;
-    in.regionlist = new REAL[nRegions * 5];
-
-    if (nHoles > 0)
-    {
-        in.numberofholes = nHoles;
-        in.holelist = new REAL[nHoles * 3];
-    }
-
-    // Reset counters
-    nFaces = nRegions = nHoles = 0;
-    typename TetMesh::KeyType cnt = 0;
-
-    for (auto &surfmesh : surfmeshes)
-    {
-        std::map sigma;
-
-        // Assign vertex information
-        for (const auto vertexID : surfmesh->template get_level_id<1>())
-        {
-            sigma[surfmesh->get_name(vertexID)[0]] = cnt;
-
-            auto vertex = *vertexID;
-            auto idx = cnt*3;
-            in.pointlist[idx]   = vertex[0];
-            in.pointlist[idx+1] = vertex[1];
-            in.pointlist[idx+2] = vertex[2];
-
-            ++cnt;
-        }
-
-        // Assign face information
-        for (const auto faceID : surfmesh->template get_level_id<3>())
-        {
-            auto w = surfmesh->get_name(faceID);
-
-            f = &in.facetlist[nFaces];
-            f->holelist = (REAL *)NULL;
-            f->numberofholes = 0;
-            f->numberofpolygons = 1;
-
-            f->polygonlist = new tetgenio::polygon[f->numberofpolygons];
-            p = &f->polygonlist[0];
-            p->numberofvertices = 3;
-            p->vertexlist = new int[p->numberofvertices];
-            p->vertexlist[0] = sigma[w[0]];
-            p->vertexlist[1] = sigma[w[1]];
-            p->vertexlist[2] = sigma[w[2]];
-
-            in.facetmarkerlist[nFaces] = (*faceID).marker;
-            ++nFaces;
-        }
-
-        auto metadata = *surfmesh->get_simplex_up();
-
-        // TODO: (25) Improve region point picking strategy
-        // Pick a point inside the region
-        auto   faceID = *surfmesh->template get_level_id<3>().begin();
-        Vector normal = getNormal(*surfmesh, faceID);
-        normal /= std::sqrt(normal|normal);
-
-        auto   fname = surfmesh->get_name(faceID);
-        Vector a = (*surfmesh->get_simplex_up({fname[0]})).position;
-        Vector b = (*surfmesh->get_simplex_up({fname[1]})).position;
-        Vector c = (*surfmesh->get_simplex_up({fname[2]})).position;
-
-        Vector d = a-b;
-        double weight = std::sqrt(d|d);
-
-        // flip normal and scale by weight
-        normal *= weight;
-        Vector regionPoint((a+b+c)/3.0 - normal);
-
-        std::cout << "Region point: " << regionPoint << std::endl;
-
-        if (metadata.ishole)
-        {
-            auto idx = nHoles*3;
-            in.holelist[idx]   = regionPoint[0];
-            in.holelist[idx+1] = regionPoint[1];
-            in.holelist[idx+2] = regionPoint[2];
-            ++nHoles;
-        }
-        else
-        {
-            auto idx = nRegions*5;
-            in.regionlist[idx]   = regionPoint[0];
-            in.regionlist[idx+1] = regionPoint[1];
-            in.regionlist[idx+2] = regionPoint[2];
-            in.regionlist[idx+3] = metadata.marker;
-            // std::cout << "Region marker: " << metadata.marker << std::endl;
-
-            if (metadata.useVolumeConstraint)
-            {
-                in.regionlist[idx+4] = metadata.volumeConstraint;
-            }
-            else
-            {
-                in.regionlist[idx+4] = -1;
-            }
-            ++nRegions;
-        }
-    } // endif for surfmesh :surfmeshes
-
-    // Add boundary marker on each node
-    in.pointmarkerlist = new int[in.numberofpoints];
-    for (int i = 0; i < in.numberofpoints; ++i)
-    {
-        in.pointmarkerlist[i] = 1;
-    }
-
-    // Casting away const is an evil thing to do, however, tetgen has not yet
-    // conformed...
-    // auto plc = const_cast("plc");
-    // in.save_nodes(plc);
-    // in.save_poly(plc);
-
-    // Call TetGen
-    try
-    {
-        tetrahedralize(tetgen_params.c_str(), &in, &out, NULL);
-    }
-    catch (int e)
-    {
-        switch (e)
-        {
-            case 1:
-                throw std::runtime_error("Tetgen: Out of memory");
-            case 2:
-                throw std::runtime_error("Tetgen: internal error");
-            case 3:
-                throw std::runtime_error("Tetgen: A self intersection was detected. Program stopped. Hint: use -d option to detect all self-intersections");
-            case 4:
-                throw std::runtime_error("Tetgen: A very small input feature size was detected.");
-            case 5:
-                throw std::runtime_error("Tetgen: Two very close input facets were detected");
-            case 10:
-                throw std::runtime_error("Tetgen: An input error was detected");
-        }
+    if (nverts == 0 || nfaces == 0) {
+      std::stringstream ss;
+      ss << "SurfaceMesh " << i << " contains no data."
+         << " Cannot tetrahedralize nothing.";
+      gamer_runtime_error(ss.str());
     }
-    // auto result = const_cast("result");
-    // out.save_nodes(result);
-    // out.save_elements(result);
-    // out.save_faces(result);
-    return tetgenioToTetMesh(out);
-}
 
-std::unique_ptr tetgenioToTetMesh(tetgenio &tetio)
-{
-    std::unique_ptr mesh(new TetMesh);
+    auto metadata = *surfmesh->get_simplex_up();
 
-    if (tetio.mesh_dim == 2)
-    {
-        throw std::runtime_error("tetgenioToTetMesh expects a tetrahedral tetgenio. Found surface instead.");
+    // TODO: (10) Use a more rigorous check of closedness
+    if (hasHole(*surfmesh)) {
+      std::stringstream ss;
+      ss << "SurfaceMesh " << i
+         << " is not closed. Cannot tetrahedralize non-manifold objects.";
+      gamer_runtime_error(ss.str());
     }
 
-    auto &metadata = *mesh->get_simplex_up();
+    metadata.ishole ? ++nHoles : ++nRegions;
 
-    // Check for higher order cells
-    const bool higher_order = tetio.numberofcorners == 10;
+    nVertices += nverts;
+    nFaces += nfaces;
+    ++i;
+  }
 
-    metadata.higher_order = higher_order;
+  if (nRegions < 1) {
+    gamer_runtime_error("No non-hole Surface Meshes found. makeTetMesh "
+                        "expects at least one non-hole SurfaceMesh");
+  }
 
-    std::set vertices;
+  std::cout << "Number of vertices: " << nVertices << std::endl;
+  std::cout << "Number of Faces: " << nFaces << std::endl;
+  std::cout << "Number of Regions: " << nRegions << std::endl;
+  std::cout << "Number of Holes: " << nHoles << std::endl;
 
-    // std::cout << "Number of tetrahedron attributes: " <<
-    // tetio.numberoftetrahedronattributes
-    //     << std::endl;
+  tetgenio in, out;
 
-    // Copy over tetrahedron data
-    // std::cout << "Copying over tetrahedron data..." << std::endl;
-    for (int i = 0; i < tetio.numberoftetrahedra; ++i)
-    {
-        // Set marker
-        int marker = 0;
+  tetgenio::facet *f;
+  tetgenio::polygon *p;
 
-        if (tetio.numberoftetrahedronattributes > 0){
-            marker = (int) tetio.tetrahedronattributelist[i * tetio.numberoftetrahedronattributes];
-        }
+  // Allocate memory for tetgenio object arrays
+  in.numberofpoints = nVertices;
+  in.pointlist = new REAL[nVertices * 3];
 
-        // Get vertex id's
-        int *ptr = &tetio.tetrahedronlist[i*tetio.numberofcorners];
+  in.numberoffacets = nFaces;
+  in.facetlist = new tetgenio::facet[nFaces];
+  in.facetmarkerlist = new int[nFaces];
 
-        vertices.insert({ptr[0], ptr[1], ptr[2], ptr[3]});
-        mesh->insert<4>({ptr[0], ptr[1], ptr[2], ptr[3]},
-                        TMCell(marker, false));
-    }
-
-    // std::cout << "Number of vertices: " << mesh->size<1>() << std::endl;
-    // std::cout << "Number of edges: " << mesh->size<2>() << std::endl;
-    // std::cout << "Number of faces: " << mesh->size<3>() << std::endl;
-    // std::cout << "Number of tetrahedra: " << mesh->size<4>() << std::endl;
-
-    // Copy over vertex data
-    // std::cout << "Copying over vertex data..." << std::endl;
-    for (auto i : vertices)
-    {
-        double *ptr = &tetio.pointlist[i*3];
-        auto    vertex = mesh->get_simplex_up({i});
-        if (vertex != nullptr)
-        {
-            auto &vdata = *vertex;
-            // std::cout <<
-            // casc::to_string(std::array{ptr[0],ptr[1],ptr[2]}) <<
-            // std::endl;
-            vdata = TMVertex(ptr[0], ptr[1], ptr[2], tetio.pointmarkerlist[i], false);
-        }
-    }
+  in.numberofregions = nRegions;
+  in.regionlist = new REAL[nRegions * 5];
 
-    for (auto &fdata : mesh->get_level<3>())
-    {
-        fdata.marker = 0;   // Initialize markers
-    }
+  if (nHoles > 0) {
+    in.numberofholes = nHoles;
+    in.holelist = new REAL[nHoles * 3];
+  }
 
-    // Go over faces and copy over marker information
-    // std::cout << "Copying over face data..." << std::endl;
-    for (int i = 0; i < tetio.numberoftrifaces; ++i)
-    {
-        int *ptr  = &tetio.trifacelist[i*3];
-        auto face = mesh->get_simplex_up({ptr[0], ptr[1], ptr[2]});
-        if (face != nullptr)
-        {
-            auto &fdata = *face;
-            fdata.marker = tetio.trifacemarkerlist[i];
-        }
-    }
+  // Reset counters
+  nFaces = nRegions = nHoles = 0;
+  typename TetMesh::KeyType cnt = 0;
 
-    // Copy over edge markers
-    // std::cout << "Copying over edge data..."  << std::endl;
-    for (int i = 0; i < tetio.numberofedges; ++i)
-    {
-        int *ptr = &tetio.edgelist[i*2];
-
-        auto edgeID = mesh->get_simplex_up({ptr[0], ptr[1]});
-        if (edgeID != nullptr)
-        {
-            auto &edata = *edgeID;
-            edata.marker = tetio.edgemarkerlist[i];
-
-            if (higher_order)
-            {
-                double *pos = &tetio.pointlist[tetio.o2edgelist[i]*3];
-                edata.position = Vector({pos[0], pos[1], pos[2]});
-            }
-        }
-    }
-    casc::compute_orientation(*mesh);
-
-    auto cellID = *(mesh->get_level_id<4>().begin());
-    auto indices = cellID.indices();
-    auto p0 = (*mesh->get_simplex_down(cellID, {indices[1],indices[2],indices[3]})).position;
-    auto p1 = (*mesh->get_simplex_down(cellID, {indices[0],indices[2],indices[3]})).position;
-    auto p2 = (*mesh->get_simplex_down(cellID, {indices[0],indices[1],indices[3]})).position;
-    auto p3 = (*mesh->get_simplex_down(cellID, {indices[0],indices[1],indices[2]})).position;
-    p1 = p1-p0;
-    p2 = p2-p0;
-    p3 = p3-p0;
-    auto norm12 = cross(p1,p2);
-    auto det = dot(norm12, p3);
-
-    if (det*(*cellID).orientation < 0) {
-        for (auto& cell : mesh->get_level<4>()){
-            cell.orientation *= -1;
-        }
-    }
+  for (auto &surfmesh : surfmeshes) {
+    std::map sigma;
 
-    return mesh;
+    // Assign vertex information
+    for (const auto vertexID : surfmesh->template get_level_id<1>()) {
+      sigma[surfmesh->get_name(vertexID)[0]] = cnt;
+
+      auto vertex = *vertexID;
+      auto idx = cnt * 3;
+      in.pointlist[idx] = vertex[0];
+      in.pointlist[idx + 1] = vertex[1];
+      in.pointlist[idx + 2] = vertex[2];
+
+      ++cnt;
+    }
+
+    // Assign face information
+    for (const auto faceID : surfmesh->template get_level_id<3>()) {
+      auto w = surfmesh->get_name(faceID);
+
+      f = &in.facetlist[nFaces];
+      f->holelist = (REAL *)NULL;
+      f->numberofholes = 0;
+      f->numberofpolygons = 1;
+
+      f->polygonlist = new tetgenio::polygon[f->numberofpolygons];
+      p = &f->polygonlist[0];
+      p->numberofvertices = 3;
+      p->vertexlist = new int[p->numberofvertices];
+      p->vertexlist[0] = sigma[w[0]];
+      p->vertexlist[1] = sigma[w[1]];
+      p->vertexlist[2] = sigma[w[2]];
+
+      in.facetmarkerlist[nFaces] = (*faceID).marker;
+      ++nFaces;
+    }
+
+    auto metadata = *surfmesh->get_simplex_up();
+
+    // TODO: (25) Improve region point picking strategy
+    // Pick a point inside the region
+    auto faceID = *surfmesh->template get_level_id<3>().begin();
+    Vector normal = getNormal(*surfmesh, faceID);
+    normal /= std::sqrt(normal | normal);
+
+    auto fname = surfmesh->get_name(faceID);
+    Vector a = (*surfmesh->get_simplex_up({fname[0]})).position;
+    Vector b = (*surfmesh->get_simplex_up({fname[1]})).position;
+    Vector c = (*surfmesh->get_simplex_up({fname[2]})).position;
+
+    Vector d = a - b;
+    double weight = std::sqrt(d | d);
+
+    // flip normal and scale by weight
+    normal *= weight;
+    Vector regionPoint((a + b + c) / 3.0 - normal);
+
+    std::cout << "Region point: " << regionPoint << std::endl;
+
+    if (metadata.ishole) {
+      auto idx = nHoles * 3;
+      in.holelist[idx] = regionPoint[0];
+      in.holelist[idx + 1] = regionPoint[1];
+      in.holelist[idx + 2] = regionPoint[2];
+      ++nHoles;
+    } else {
+      auto idx = nRegions * 5;
+      in.regionlist[idx] = regionPoint[0];
+      in.regionlist[idx + 1] = regionPoint[1];
+      in.regionlist[idx + 2] = regionPoint[2];
+      in.regionlist[idx + 3] = metadata.marker;
+      // std::cout << "Region marker: " << metadata.marker << std::endl;
+
+      if (metadata.useVolumeConstraint) {
+        in.regionlist[idx + 4] = metadata.volumeConstraint;
+      } else {
+        in.regionlist[idx + 4] = -1;
+      }
+      ++nRegions;
+    }
+  } // endif for surfmesh :surfmeshes
+
+  // Add boundary marker on each node
+  in.pointmarkerlist = new int[in.numberofpoints];
+  for (int i = 0; i < in.numberofpoints; ++i) {
+    in.pointmarkerlist[i] = 1;
+  }
+
+  // Casting away const is an evil thing to do, however, tetgen has not yet
+  // conformed...
+  // auto plc = const_cast("plc");
+  // in.save_nodes(plc);
+  // in.save_poly(plc);
+
+  std::vector tetgen_params_c(
+      tetgen_params.c_str(), tetgen_params.c_str() + tetgen_params.size() + 1);
+
+  // Call TetGen
+  try {
+    tetrahedralize(tetgen_params_c.data(), &in, &out, NULL);
+  } catch (int e) {
+    switch (e) {
+    case 1:
+      gamer_runtime_error("Tetgen: Out of memory");
+    case 2:
+      gamer_runtime_error("Tetgen: internal error");
+    case 3:
+      gamer_runtime_error(
+          "Tetgen: A self intersection was detected. Program stopped. Hint: "
+          "use -d option to detect all self-intersections");
+    case 4:
+      gamer_runtime_error(
+          "Tetgen: A very small input feature size was detected.");
+    case 5:
+      gamer_runtime_error("Tetgen: Two very close input facets were detected");
+    case 10:
+      gamer_runtime_error("Tetgen: An input error was detected");
+    }
+  }
+  // auto result = const_cast("result");
+  // out.save_nodes(result);
+  // out.save_elements(result);
+  // out.save_faces(result);
+  return tetgenioToTetMesh(out);
 }
 
-std::unique_ptr extractSurface(const TetMesh &tetmesh)
-{
-    // Create a new surface mesh
-    std::unique_ptr surfmesh(new SurfaceMesh);
-
-    for (auto faceID : tetmesh.get_level_id<3>())
-    {
-        if (tetmesh.onBoundary(faceID))
-        {
-            auto data = *faceID;
-            auto name = tetmesh.get_name(faceID);
-            surfmesh->insert(name, SMFace(data.marker, data.selected));
-        }
-    }
-
-    for (auto vertexID : surfmesh->get_level_id<1>())
-    {
-        auto &data = *vertexID;
-        auto  name = surfmesh->get_name(vertexID); // Same as in tetmesh
-        data.position = (*tetmesh.get_simplex_up(name)).position;
-    }
-
-    casc::compute_orientation(*surfmesh);
-    return surfmesh;
+std::unique_ptr tetgenioToTetMesh(tetgenio &tetio) {
+  std::unique_ptr mesh(new TetMesh);
+
+  if (tetio.mesh_dim == 2) {
+    gamer_runtime_error("tetgenioToTetMesh expects a tetrahedral "
+                        "tetgenio. Found surface instead.");
+  }
+
+  auto &metadata = *mesh->get_simplex_up();
+
+  // Check for higher order cells
+  const bool higher_order = tetio.numberofcorners == 10;
+
+  metadata.higher_order = higher_order;
+
+  std::set vertices;
+
+  // std::cout << "Number of tetrahedron attributes: " <<
+  // tetio.numberoftetrahedronattributes
+  //     << std::endl;
+
+  // Copy over tetrahedron data
+  // std::cout << "Copying over tetrahedron data..." << std::endl;
+  for (int i = 0; i < tetio.numberoftetrahedra; ++i) {
+    // Set marker
+    int marker = 0;
+
+    if (tetio.numberoftetrahedronattributes > 0) {
+      marker =
+          (int)tetio
+              .tetrahedronattributelist[i *
+                                        tetio.numberoftetrahedronattributes];
+    }
+
+    // Get vertex id's
+    int *ptr = &tetio.tetrahedronlist[i * tetio.numberofcorners];
+
+    vertices.insert({ptr[0], ptr[1], ptr[2], ptr[3]});
+    mesh->insert<4>({ptr[0], ptr[1], ptr[2], ptr[3]}, TMCell(marker, false));
+  }
+
+  // std::cout << "Number of vertices: " << mesh->size<1>() << std::endl;
+  // std::cout << "Number of edges: " << mesh->size<2>() << std::endl;
+  // std::cout << "Number of faces: " << mesh->size<3>() << std::endl;
+  // std::cout << "Number of tetrahedra: " << mesh->size<4>() << std::endl;
+
+  // Copy over vertex data
+  // std::cout << "Copying over vertex data..." << std::endl;
+  for (auto i : vertices) {
+    double *ptr = &tetio.pointlist[i * 3];
+    auto vertex = mesh->get_simplex_up({i});
+    if (vertex != nullptr) {
+      auto &vdata = *vertex;
+      // std::cout <<
+      // casc::to_string(std::array{ptr[0],ptr[1],ptr[2]}) <<
+      // std::endl;
+      vdata = TMVertex(ptr[0], ptr[1], ptr[2], tetio.pointmarkerlist[i], false);
+    }
+  }
+
+  for (auto &fdata : mesh->get_level<3>()) {
+    fdata.marker = 0; // Initialize markers
+  }
+
+  // Go over faces and copy over marker information
+  // std::cout << "Copying over face data..." << std::endl;
+  for (int i = 0; i < tetio.numberoftrifaces; ++i) {
+    int *ptr = &tetio.trifacelist[i * 3];
+    auto face = mesh->get_simplex_up({ptr[0], ptr[1], ptr[2]});
+    if (face != nullptr) {
+      auto &fdata = *face;
+      fdata.marker = tetio.trifacemarkerlist[i];
+    }
+  }
+
+  // Copy over edge markers
+  // std::cout << "Copying over edge data..."  << std::endl;
+  for (int i = 0; i < tetio.numberofedges; ++i) {
+    int *ptr = &tetio.edgelist[i * 2];
+
+    auto edgeID = mesh->get_simplex_up({ptr[0], ptr[1]});
+    if (edgeID != nullptr) {
+      auto &edata = *edgeID;
+      edata.marker = tetio.edgemarkerlist[i];
+
+      if (higher_order) {
+        double *pos = &tetio.pointlist[tetio.o2edgelist[i] * 3];
+        edata.position = Vector({pos[0], pos[1], pos[2]});
+      }
+    }
+  }
+  casc::compute_orientation(*mesh);
+
+  auto cellID = *(mesh->get_level_id<4>().begin());
+  auto indices = cellID.indices();
+  auto p0 =
+      (*mesh->get_simplex_down(cellID, {indices[1], indices[2], indices[3]}))
+          .position;
+  auto p1 =
+      (*mesh->get_simplex_down(cellID, {indices[0], indices[2], indices[3]}))
+          .position;
+  auto p2 =
+      (*mesh->get_simplex_down(cellID, {indices[0], indices[1], indices[3]}))
+          .position;
+  auto p3 =
+      (*mesh->get_simplex_down(cellID, {indices[0], indices[1], indices[2]}))
+          .position;
+  p1 = p1 - p0;
+  p2 = p2 - p0;
+  p3 = p3 - p0;
+  auto norm12 = cross(p1, p2);
+  auto det = dot(norm12, p3);
+
+  if (det * (*cellID).orientation < 0) {
+    for (auto &cell : mesh->get_level<4>()) {
+      cell.orientation *= -1;
+    }
+  }
+
+  return mesh;
 }
 
-void writeVTK(const std::string &filename, const TetMesh &mesh)
-{
-    std::ofstream fout(filename);
-    if (!fout.is_open())
-    {
-        std::stringstream ss;
-        ss << "File '" << filename
-                  << "' could not be written to.";
-        throw std::runtime_error(ss.str());
-    }
-
-    fout << "# vtk DataFile Version 2.0\n"
-         << "Unstructured Grid\n"
-         << "ASCII\n"  // BINARY
-         << "DATASET UNSTRUCTURED_GRID\n";
+std::unique_ptr extractSurface(const TetMesh &tetmesh) {
+  // Create a new surface mesh
+  std::unique_ptr surfmesh(new SurfaceMesh);
 
-    std::map sigma;
-    typename TetMesh::KeyType cnt = 0;
-
-    // Output vertices
-    fout << "POINTS " << mesh.size<1>() << " double" << std::endl;
-    for (auto vertexID : mesh.get_level_id<1>())
-    {
-        sigma[mesh.get_name(vertexID)[0]] = cnt++;
-        auto vertex = *vertexID;
-        fout << std::setprecision(17) << vertex[0] << " "
-             << vertex[1] << " "
-             << vertex[2] << "\n";
-    }
-    fout << "\n";
-
-    bool orientationError = false;
-
-    fout << "CELLS " << mesh.size<4>() << " " << mesh.size<4>()*(4+1) << "\n";
-    for (auto cellID : mesh.get_level_id<4>())
-    {
-        auto w = mesh.get_name(cellID);
-        auto orientation = (*cellID).orientation;
-
-        if (orientation == 1)
-        {
-            fout << "4 " << std::setw(4) << sigma[w[0]] << " "
-                 << std::setw(4) << sigma[w[1]] << " "
-                 << std::setw(4) << sigma[w[2]] << " "
-                 << std::setw(4) << sigma[w[3]] << "\n";
-        }
-        else if (orientation == -1)
-        {
-            fout << "4 " << std::setw(4) << sigma[w[3]] << " "
-                 << std::setw(4) << sigma[w[1]] << " "
-                 << std::setw(4) << sigma[w[2]] << " "
-                 << std::setw(4) << sigma[w[0]] << "\n";
-        }
-        else
-        {
-            orientationError = true;
-            fout << "4 " << std::setw(4) << sigma[w[0]] << " "
-                 << std::setw(4) << sigma[w[1]] << " "
-                 << std::setw(4) << sigma[w[2]] << " "
-                 << std::setw(4) << sigma[w[3]] << "\n";
-        }
+  for (auto faceID : tetmesh.get_level_id<3>()) {
+    if (tetmesh.onBoundary(faceID)) {
+      auto data = *faceID;
+      auto name = tetmesh.get_name(faceID);
+      surfmesh->insert(name, SMFace(data.marker, data.selected));
     }
-    fout << "\n";
+  }
 
-    fout << "CELL_TYPES " << mesh.size<4>() << "\n";
-    for (int i = 0; i < mesh.size<4>(); ++i)
-    {
-        fout << "10\n";
-    }
-    fout << "\n";
-
-    fout << "CELL_DATA " << mesh.size<4>() << "\n";
-    fout << "SCALARS cell_scalars int 1\n";
-    fout << "LOOKUP_TABLE default\n";
-    // This should output in the same order...
-    for (auto cell : mesh.get_level<4>())
-    {
-        fout << cell.marker << "\n";
-    }
-    fout << "\n";
+  for (auto vertexID : surfmesh->get_level_id<1>()) {
+    auto &data = *vertexID;
+    auto name = surfmesh->get_name(vertexID); // Same as in tetmesh
+    data.position = (*tetmesh.get_simplex_up(name)).position;
+  }
 
-    if (orientationError)
-    {
-        std::cerr << "WARNING(writeVTK): The orientation of one or more faces "
-                  << "is not defined. Did you run compute_orientation()?"
-                  << std::endl;
-    }
-    fout.close();
+  casc::compute_orientation(*surfmesh);
+  return surfmesh;
 }
 
-void writeOFF(const std::string &filename, const TetMesh &mesh)
-{
-    std::ofstream fout(filename);
-    if (!fout.is_open())
-    {
-        std::stringstream ss;
-        ss << "File '" << filename
-                  << "' could not be written to.";
-        throw std::runtime_error(ss.str());
-    }
-
-    fout << "OFF\n";
-    fout << mesh.size<1>() << " "
-         << mesh.size<4>() << " "
-         << mesh.size<2>() << "\n";
-
-    std::map sigma;
-    typename TetMesh::KeyType cnt = 0;
-
-    fout.precision(10);
-    for (const auto vertexID : mesh.get_level_id<1>())
-    {
-        sigma[mesh.get_name(vertexID)[0]] = cnt++;
-        auto vertex = *vertexID;
-
-        fout << vertex[0] << " "
-             << vertex[1] << " "
-             << vertex[2] << " "
-             << "\n";
-    }
-
-    bool orientationError = false;
-
-    for (auto cellID : mesh.get_level_id<4>())
-    {
-        auto w = mesh.get_name(cellID);
-        auto orientation = (*cellID).orientation;
-
-        if (orientation == 1)
-        {
-            fout << "4 " << std::setw(4) << sigma[w[0]] << " "
-                 << std::setw(4) << sigma[w[1]] << " "
-                 << std::setw(4) << sigma[w[2]] << " "
-                 << std::setw(4) << sigma[w[3]] << "\n";
-        }
-        else if (orientation == -1)
-        {
-            fout << "4 " << std::setw(4) << sigma[w[3]] << " "
-                 << std::setw(4) << sigma[w[1]] << " "
-                 << std::setw(4) << sigma[w[2]] << " "
-                 << std::setw(4) << sigma[w[0]] << "\n";
-        }
-        else
-        {
-            orientationError = true;
-            fout << "4 " << std::setw(4) << sigma[w[0]] << " "
-                 << std::setw(4) << sigma[w[1]] << " "
-                 << std::setw(4) << sigma[w[2]] << " "
-                 << std::setw(4) << sigma[w[3]] << "\n";
-        }
-    }
-
-    if (orientationError)
-    {
-        std::cerr << "WARNING(writeOFF): The orientation of one or more cells "
-                  << "is not defined. Did you run compute_orientation()?"
-                  << std::endl;
-    }
-    fout.close();
+void writeVTK(const std::string &filename, const TetMesh &mesh) {
+  std::ofstream fout(filename);
+  if (!fout.is_open()) {
+    std::stringstream ss;
+    ss << "File '" << filename << "' could not be written to.";
+    gamer_runtime_error(ss.str());
+  }
+
+  fout << "# vtk DataFile Version 2.0\n"
+       << "Unstructured Grid\n"
+       << "ASCII\n" // BINARY
+       << "DATASET UNSTRUCTURED_GRID\n";
+
+  std::map sigma;
+  typename TetMesh::KeyType cnt = 0;
+
+  // Output vertices
+  fout << "POINTS " << mesh.size<1>() << " double" << std::endl;
+  for (auto vertexID : mesh.get_level_id<1>()) {
+    sigma[mesh.get_name(vertexID)[0]] = cnt++;
+    auto vertex = *vertexID;
+    fout << std::setprecision(17) << vertex[0] << " " << vertex[1] << " "
+         << vertex[2] << "\n";
+  }
+  fout << "\n";
+
+  bool orientationError = false;
+
+  fout << "CELLS " << mesh.size<4>() << " " << mesh.size<4>() * (4 + 1) << "\n";
+  for (auto cellID : mesh.get_level_id<4>()) {
+    auto w = mesh.get_name(cellID);
+    auto orientation = (*cellID).orientation;
+
+    if (orientation == 1) {
+      fout << "4 " << std::setw(4) << sigma[w[0]] << " " << std::setw(4)
+           << sigma[w[1]] << " " << std::setw(4) << sigma[w[2]] << " "
+           << std::setw(4) << sigma[w[3]] << "\n";
+    } else if (orientation == -1) {
+      fout << "4 " << std::setw(4) << sigma[w[3]] << " " << std::setw(4)
+           << sigma[w[1]] << " " << std::setw(4) << sigma[w[2]] << " "
+           << std::setw(4) << sigma[w[0]] << "\n";
+    } else {
+      orientationError = true;
+      fout << "4 " << std::setw(4) << sigma[w[0]] << " " << std::setw(4)
+           << sigma[w[1]] << " " << std::setw(4) << sigma[w[2]] << " "
+           << std::setw(4) << sigma[w[3]] << "\n";
+    }
+  }
+  fout << "\n";
+
+  fout << "CELL_TYPES " << mesh.size<4>() << "\n";
+  for (int i = 0; i < mesh.size<4>(); ++i) {
+    fout << "10\n";
+  }
+  fout << "\n";
+
+  fout << "CELL_DATA " << mesh.size<4>() << "\n";
+  fout << "SCALARS cell_scalars int 1\n";
+  fout << "LOOKUP_TABLE default\n";
+  // This should output in the same order...
+  for (auto cell : mesh.get_level<4>()) {
+    fout << cell.marker << "\n";
+  }
+  fout << "\n";
+
+  if (orientationError) {
+    std::cerr << "WARNING(writeVTK): The orientation of one or more faces "
+              << "is not defined. Did you run compute_orientation()?"
+              << std::endl;
+  }
+  fout.close();
 }
 
-void writeDolfin(const std::string &filename, const TetMesh &mesh)
-{
-
-    if ((*mesh.get_simplex_up()).higher_order == true)
-    {
-        throw std::runtime_error("Dolfin output does not support higher order meshes.");
-    }
-
-    std::ofstream fout(filename);
-    if (!fout.is_open())
-    {
-        std::stringstream ss;
-        ss << "File '" << filename
-                  << "' could not be written to.";
-        throw std::runtime_error(ss.str());
-    }
-
-    fout << "\n"
-         << "\n"
-         << "  \n"
-         << "    () <<  "\">\n";
-
-    std::map sigma;
-    size_t cnt = 0;
-
-    // Print out Vertices
-    // std::cout << "Printing Vertices" << std::endl;
-    fout.precision(6);
-    for (const auto vertexID : mesh.get_level_id<1>())
-    {
-        size_t idx = cnt;
-        sigma[mesh.get_name(vertexID)[0]] = cnt++;
-        auto   vertex = *vertexID;
-
-        fout << "      \n";
-    }
-    fout << "    \n";
-
-
-    // Print out Tetrahedra
-    // std::cout << "Printing Tetrahedra" << std::endl;
-    cnt = 0;
-    std::vector > faceMarkerList;
-    std::vector > cellMarkerList;
-    bool orientationError = false;
-
-    fout << "    () << "\">\n";
-    for (const auto tetID :  mesh.get_level_id<4>())
-    {
-        std::size_t idx = cnt++;
-        auto        tetName = mesh.get_name(tetID);
-        auto orientation = (*tetID).orientation;
-
-
-        if (orientation == 1)
-        {
-            fout << "      \n";
-        }
-        else if (orientation == -1)
-        {
-           fout << "      \n";
-        }
-        else
-        {
-           orientationError = true;
-           fout << "      \n";
-        }
-
-        // First face = vertices 2,3,4
-        // Second face = vertices 1,3,4 etc...
-        for (std::size_t i = 0; i < 4; ++i)
-        {
-            auto        faceID = mesh.get_simplex_down(tetID, tetName[i]);
-            int marker = (*faceID).marker;
-            if (marker != 0)
-                faceMarkerList.push_back(std::make_tuple(idx, i, marker));
-        }
-        cellMarkerList.push_back(std::make_tuple(idx, (*tetID).marker));
-    }
-    if (orientationError)
-    {
-        std::cerr << "WARNING(writeDolfin): The orientation of one or more cells "
-                  << "is not defined. Did you run compute_orientation()?"
-                  << std::endl;
-    }
-    fout << "    \n";
-    fout << "    \n";
-
-    fout << "      \n";
-    for (const auto markerItem : faceMarkerList)
-    {
-        std::size_t idx, local_entity;
-        int marker;
-        std::tie(idx, local_entity, marker) = markerItem;
-        fout << "        \n";
-    }
-
-    fout << "      \n";
-    fout << "      () << "\">\n";
-
-    for (const auto markerItem : cellMarkerList)
-    {
-        std::size_t idx;
-        int marker;
-        std::tie(idx, marker) = markerItem;
-        fout << "        \n";
-    }
-    fout << "      \n";
-    fout << "    \n";
-    fout << "  \n";
-    fout << "\n";
-
-    fout.close();
+void writeOFF(const std::string &filename, const TetMesh &mesh) {
+  std::ofstream fout(filename);
+  if (!fout.is_open()) {
+    std::stringstream ss;
+    ss << "File '" << filename << "' could not be written to.";
+    gamer_runtime_error(ss.str());
+  }
+
+  fout << "OFF\n";
+  fout << mesh.size<1>() << " " << mesh.size<4>() << " " << mesh.size<2>()
+       << "\n";
+
+  std::map sigma;
+  typename TetMesh::KeyType cnt = 0;
+
+  fout.precision(10);
+  for (const auto vertexID : mesh.get_level_id<1>()) {
+    sigma[mesh.get_name(vertexID)[0]] = cnt++;
+    auto vertex = *vertexID;
+
+    fout << vertex[0] << " " << vertex[1] << " " << vertex[2] << " "
+         << "\n";
+  }
+
+  bool orientationError = false;
+
+  for (auto cellID : mesh.get_level_id<4>()) {
+    auto w = mesh.get_name(cellID);
+    auto orientation = (*cellID).orientation;
+
+    if (orientation == 1) {
+      fout << "4 " << std::setw(4) << sigma[w[0]] << " " << std::setw(4)
+           << sigma[w[1]] << " " << std::setw(4) << sigma[w[2]] << " "
+           << std::setw(4) << sigma[w[3]] << "\n";
+    } else if (orientation == -1) {
+      fout << "4 " << std::setw(4) << sigma[w[3]] << " " << std::setw(4)
+           << sigma[w[1]] << " " << std::setw(4) << sigma[w[2]] << " "
+           << std::setw(4) << sigma[w[0]] << "\n";
+    } else {
+      orientationError = true;
+      fout << "4 " << std::setw(4) << sigma[w[0]] << " " << std::setw(4)
+           << sigma[w[1]] << " " << std::setw(4) << sigma[w[2]] << " "
+           << std::setw(4) << sigma[w[3]] << "\n";
+    }
+  }
+
+  if (orientationError) {
+    std::cerr << "WARNING(writeOFF): The orientation of one or more cells "
+              << "is not defined. Did you run compute_orientation()?"
+              << std::endl;
+  }
+  fout.close();
 }
 
-void writeTriangle(const std::string &filename, const TetMesh &mesh)
-{
-    std::ofstream fout(filename + ".node");
-    if (!fout.is_open())
-    {
-        std::stringstream ss;
-        ss << "File '" << filename + ".node"
-                  << "' could not be written to.";
-        throw std::runtime_error(ss.str());
-    }
-
-    std::map sigma;
-    size_t cnt = 1;
-
-    // Print out Vertices
-    // std::cout << "Printing Vertices" << std::endl;
-    // nVertices, dimension, nattributes, nmarkers
-    fout << mesh.size<1>() << " 3 " << " 0 " << " 1\n";
-
-    fout.precision(6);
-    for (const auto vertexID : mesh.get_level_id<1>())
-    {
-        size_t idx = cnt;
-        sigma[mesh.get_name(vertexID)[0]] = cnt++;
-        auto   vertex = *vertexID;
-
-        fout << idx << " "
-             << vertex[0] << " "
-             << vertex[1] << " "
-             << vertex[2] << " "
-             << (*vertexID).marker << "\n";
-    }
-    fout.close(); // Close .node file
+void writeDolfin(const std::string &filename, const TetMesh &mesh) {
+
+  if ((*mesh.get_simplex_up()).higher_order == true) {
+    gamer_runtime_error("Dolfin output does not support higher order meshes.");
+  }
+
+  std::ofstream fout(filename);
+  if (!fout.is_open()) {
+    std::stringstream ss;
+    ss << "File '" << filename << "' could not be written to.";
+    gamer_runtime_error(ss.str());
+  }
+
+  fout << "\n"
+       << "\n"
+       << "  \n"
+       << "    () << "\">\n";
+
+  std::map sigma;
+  size_t cnt = 0;
+
+  // Print out Vertices
+  // std::cout << "Printing Vertices" << std::endl;
+  fout.precision(6);
+  for (const auto vertexID : mesh.get_level_id<1>()) {
+    size_t idx = cnt;
+    sigma[mesh.get_name(vertexID)[0]] = cnt++;
+    auto vertex = *vertexID;
+
+    fout << "      \n";
+  }
+  fout << "    \n";
+
+  // Print out Tetrahedra
+  // std::cout << "Printing Tetrahedra" << std::endl;
+  cnt = 0;
+  std::vector> faceMarkerList;
+  std::vector> cellMarkerList;
+  bool orientationError = false;
+
+  fout << "    () << "\">\n";
+  for (const auto tetID : mesh.get_level_id<4>()) {
+    std::size_t idx = cnt++;
+    auto tetName = mesh.get_name(tetID);
+    auto orientation = (*tetID).orientation;
+
+    if (orientation == 1) {
+      fout << "      \n";
+    } else if (orientation == -1) {
+      fout << "      \n";
+    } else {
+      orientationError = true;
+      fout << "      \n";
+    }
+
+    // First face = vertices 2,3,4
+    // Second face = vertices 1,3,4 etc...
+    for (std::size_t i = 0; i < 4; ++i) {
+      auto faceID = mesh.get_simplex_down(tetID, tetName[i]);
+      int marker = (*faceID).marker;
+      if (marker != 0)
+        faceMarkerList.push_back(std::make_tuple(idx, i, marker));
+    }
+    cellMarkerList.push_back(std::make_tuple(idx, (*tetID).marker));
+  }
+  if (orientationError) {
+    std::cerr << "WARNING(writeDolfin): The orientation of one or more cells "
+              << "is not defined. Did you run compute_orientation()?"
+              << std::endl;
+  }
+  fout << "    \n";
+  fout << "    \n";
+
+  fout << "      \n";
+  for (const auto markerItem : faceMarkerList) {
+    std::size_t idx, local_entity;
+    int marker;
+    std::tie(idx, local_entity, marker) = markerItem;
+    fout << "        \n";
+  }
+
+  fout << "      \n";
+  fout << "      () << "\">\n";
+
+  for (const auto markerItem : cellMarkerList) {
+    std::size_t idx;
+    int marker;
+    std::tie(idx, marker) = markerItem;
+    fout << "        \n";
+  }
+  fout << "      \n";
+  fout << "    \n";
+  fout << "  \n";
+  fout << "\n";
+
+  fout.close();
+}
 
+void writeTriangle(const std::string &filename, const TetMesh &mesh) {
+  std::ofstream fout(filename + ".node");
+  if (!fout.is_open()) {
+    std::stringstream ss;
+    ss << "File '" << filename + ".node"
+       << "' could not be written to.";
+    gamer_runtime_error(ss.str());
+  }
+
+  std::map sigma;
+  size_t cnt = 1;
+
+  // Print out Vertices
+  // std::cout << "Printing Vertices" << std::endl;
+  // nVertices, dimension, nattributes, nmarkers
+  fout << mesh.size<1>() << " 3 "
+       << " 0 "
+       << " 1\n";
+
+  fout.precision(6);
+  for (const auto vertexID : mesh.get_level_id<1>()) {
+    size_t idx = cnt;
+    sigma[mesh.get_name(vertexID)[0]] = cnt++;
+    auto vertex = *vertexID;
+
+    fout << idx << " " << vertex[0] << " " << vertex[1] << " " << vertex[2]
+         << " " << (*vertexID).marker << "\n";
+  }
+  fout.close(); // Close .node file
+
+  // Open file filename.ele
+  std::ofstream foutEle(filename + ".ele");
+  if (!foutEle.is_open()) {
+    std::stringstream ss;
+    ss << "File '" << filename + ".ele"
+       << "' could not be written to.";
+    gamer_runtime_error(ss.str());
+  }
+
+  // nTetrahedra, nodes per tet, nAttributes
+  foutEle << mesh.size<4>() << " 4 1\n";
+  cnt = 1;
+  for (const auto tetID : mesh.get_level_id<4>()) {
+    std::size_t idx = cnt++;
+    auto tetName = mesh.get_name(tetID);
+    foutEle << idx << " " << sigma[tetName[0]] << " " << sigma[tetName[1]]
+            << " " << sigma[tetName[2]] << " " << sigma[tetName[3]] << " "
+            << (*tetID).marker << "\n";
+  }
+  foutEle.close(); // Close .ele file
+}
 
-    // Open file filename.ele
-    std::ofstream foutEle(filename + ".ele");
-    if (!foutEle.is_open())
-    {
-        std::stringstream ss;
-        ss << "File '" << filename + ".ele"
-                  << "' could not be written to.";
-        throw std::runtime_error(ss.str());
-    }
+void smoothMesh(TetMesh &mesh) {
+  std::set> vertexIDs;
 
-    // nTetrahedra, nodes per tet, nAttributes
-    foutEle << mesh.size<4>() << " 4 1\n";
-    cnt = 1;
-    for (const auto tetID :  mesh.get_level_id<4>())
-    {
-        std::size_t idx = cnt++;
-        auto        tetName = mesh.get_name(tetID);
-        foutEle << idx << " "
-                << sigma[tetName[0]] << " "
-                << sigma[tetName[1]] << " "
-                << sigma[tetName[2]] << " "
-                << sigma[tetName[3]] << " "
-                << (*tetID).marker << "\n";
+  for (auto faceID : mesh.get_level_id<3>()) {
+    if (mesh.up(faceID).size() == 1) {
+      auto bdryVertices = mesh.down(mesh.down(faceID)); // Set of vertices
+      vertexIDs.insert(bdryVertices.begin(), bdryVertices.end());
     }
-    foutEle.close(); // Close .ele file
-}
+  }
 
-void smoothMesh(TetMesh &mesh)
-{
-    std::set > vertexIDs;
-
-    for (auto faceID : mesh.get_level_id<3>())
-    {
-        if (mesh.up(faceID).size() == 1)
-        {
-            auto bdryVertices = mesh.down(mesh.down(faceID)); // Set of vertices
-            vertexIDs.insert(bdryVertices.begin(), bdryVertices.end());
-        }
-    }
+  for (auto vID : mesh.get_level_id<1>()) {
+    if (vertexIDs.find(vID) == vertexIDs.end()) {
+      std::set> nbhd;
+      neighbors_up(mesh, vID, std::inserter(nbhd, nbhd.end()));
 
-    for (auto vID :  mesh.get_level_id<1>())
-    {
-        if (vertexIDs.find(vID) == vertexIDs.end())
-        {
-            std::set > nbhd;
-            neighbors_up(mesh, vID, std::inserter(nbhd, nbhd.end()));
-
-            Vector barycenter;
-            for (auto nbor : nbhd)
-            {
-                barycenter += *nbor;
-            }
-            barycenter /= nbhd.size();
-            auto &pos = *vID;
-            pos = barycenter;
-        }
+      Vector barycenter;
+      for (auto nbor : nbhd) {
+        barycenter += *nbor;
+      }
+      barycenter /= nbhd.size();
+      auto &pos = *vID;
+      pos = barycenter;
     }
+  }
 }
 
-std::unique_ptr readDolfin(const std::string &filename)
-{
-    // Instantiate mesh!
-    std::unique_ptr mesh(new TetMesh);
-
-    std::ifstream            fin(filename);
-    if (!fin.is_open())
-    {
-        std::cerr << "Read Error: File '" << filename << "' could not be read." << std::endl;
-        mesh.reset();
-        return mesh;
-    }
+std::unique_ptr readDolfin(const std::string &filename) {
+  // Instantiate mesh!
+  std::unique_ptr mesh(new TetMesh);
 
-    std::string line;
-    getline(fin, line);
-    getline(fin, line);
-    getline(fin, line);
+  std::ifstream fin(filename);
+  if (!fin.is_open()) {
+    std::cerr << "Read Error: File '" << filename << "' could not be read."
+              << std::endl;
+    mesh.reset();
+    return mesh;
+  }
 
-    // read number of vertices
-    getline(fin, line);
-    std::regex  vertexnum("",  std::regex_constants::ECMAScript | std::regex_constants::icase);
+  std::string line;
+  getline(fin, line);
+  getline(fin, line);
+  getline(fin, line);
 
-    std::smatch match;
-    std::regex_search(line, match, vertexnum);
+  // read number of vertices
+  getline(fin, line);
+  std::regex vertexnum("",
+                       std::regex_constants::ECMAScript |
+                           std::regex_constants::icase);
 
-    int        nvertices = std::stoi(match[1].str());
+  std::smatch match;
+  std::regex_search(line, match, vertexnum);
 
-    std::regex vertexLineRegex("",  std::regex_constants::ECMAScript | std::regex_constants::icase);
+  int nvertices = std::stoi(match[1].str());
 
-    for (int i = 0; i < nvertices; ++i)
-    {
-        getline(fin, line);
+  std::regex vertexLineRegex(
+      "",
+      std::regex_constants::ECMAScript | std::regex_constants::icase);
 
-        std::regex_search(line, match, vertexLineRegex);
+  for (int i = 0; i < nvertices; ++i) {
+    getline(fin, line);
 
-        mesh->insert<1>({std::stoi(match[1].str())}, Vertex(std::stod(match[2].str()), std::stod(match[3].str()), std::stod(match[4].str())));
-        // for (int j=0; j < match.size(); ++j){
-        //     std::cout << match[j].str() << std::endl;
-        // }
-    }
+    std::regex_search(line, match, vertexLineRegex);
 
-    // for(auto vid : mesh->get_level_id<1>()){
-    //     std::cout << vid << " " << *vid << std::endl;
+    mesh->insert<1>({std::stoi(match[1].str())},
+                    Vertex(std::stod(match[2].str()), std::stod(match[3].str()),
+                           std::stod(match[4].str())));
+    // for (int j=0; j < match.size(); ++j){
+    //     std::cout << match[j].str() << std::endl;
     // }
+  }
 
-    getline(fin, line);
-    getline(fin, line);
+  // for(auto vid : mesh->get_level_id<1>()){
+  //     std::cout << vid << " " << *vid << std::endl;
+  // }
 
-    std::regex cellnum("",  std::regex_constants::ECMAScript | std::regex_constants::icase);
+  getline(fin, line);
+  getline(fin, line);
 
-    std::regex_search(line, match, cellnum);
+  std::regex cellnum("",
+                     std::regex_constants::ECMAScript |
+                         std::regex_constants::icase);
 
-    int        ncells = std::stoi(match[1].str());
+  std::regex_search(line, match, cellnum);
 
-    std::regex tetLineRegex("",  std::regex_constants::ECMAScript | std::regex_constants::icase);
+  int ncells = std::stoi(match[1].str());
 
-    std::cout << "Reading in " << ncells << " cells" << std::endl;
+  std::regex tetLineRegex(
+      "",
+      std::regex_constants::ECMAScript | std::regex_constants::icase);
 
-    std::map > cellMap;
+  std::cout << "Reading in " << ncells << " cells" << std::endl;
 
-    for (int i = 0; i < ncells; ++i)
-    {
-        getline(fin, line);
-        std::regex_search(line, match, tetLineRegex);
+  std::map> cellMap;
 
-        std::array vals;
+  for (int i = 0; i < ncells; ++i) {
+    getline(fin, line);
+    std::regex_search(line, match, tetLineRegex);
 
-        for (int j = 0; j < 4; ++j)
-        {
-            vals[j] = std::stoi(match[j+2].str());
-        }
+    std::array vals;
 
-        mesh->insert<4>(vals);
-        cellMap.emplace(std::make_pair(std::stoi(match[1].str()), vals));
+    for (int j = 0; j < 4; ++j) {
+      vals[j] = std::stoi(match[j + 2].str());
     }
 
-    getline(fin, line);
-    getline(fin, line);
-    getline(fin, line);
+    mesh->insert<4>(vals);
+    cellMap.emplace(std::make_pair(std::stoi(match[1].str()), vals));
+  }
 
-    std::regex collectionRegex("",  std::regex_constants::ECMAScript | std::regex_constants::icase);
+  getline(fin, line);
+  getline(fin, line);
+  getline(fin, line);
 
-    std::regex_search(line, match, collectionRegex);
+  std::regex collectionRegex("",
+                             std::regex_constants::ECMAScript |
+                                 std::regex_constants::icase);
 
-    int        ncollect = std::stoi(match[2].str());
+  std::regex_search(line, match, collectionRegex);
 
-    std::regex collectLineRegex("",  std::regex_constants::ECMAScript | std::regex_constants::icase);
+  int ncollect = std::stoi(match[2].str());
 
-    std::cout << "Reading in " << ncollect << " collections" << std::endl;
+  std::regex collectLineRegex(
+      "",
+      std::regex_constants::ECMAScript | std::regex_constants::icase);
 
-    for (auto face : mesh->get_level_id<3>())
-    {
-        (*face).marker = 0;
-    }
+  std::cout << "Reading in " << ncollect << " collections" << std::endl;
 
-    for (int i = 0; i < ncollect; ++i)
-    {
-        getline(fin, line);
-        std::regex_search(line, match, collectLineRegex);
+  for (auto face : mesh->get_level_id<3>()) {
+    (*face).marker = 0;
+  }
 
-        int                idx = std::stoi(match[1].str());
-        int                entity = std::stoi(match[2].str());
-        int                value  = std::stoi(match[3].str());
+  for (int i = 0; i < ncollect; ++i) {
+    getline(fin, line);
+    std::regex_search(line, match, collectLineRegex);
 
-        auto               key = cellMap[idx];
+    int idx = std::stoi(match[1].str());
+    int entity = std::stoi(match[2].str());
+    int value = std::stoi(match[3].str());
 
-        std::array faceKey;
+    auto key = cellMap[idx];
 
-        int                k = 0;
-        for (int j = 0; j < 4; ++j)
-        {
-            if (j == entity)
-                continue;
-            else
-            {
-                faceKey[k] = key[j];
-                ++k;
-            }
-        }
+    std::array faceKey;
 
-        (*mesh->get_simplex_up(faceKey)).marker = value;
+    int k = 0;
+    for (int j = 0; j < 4; ++j) {
+      if (j == entity)
+        continue;
+      else {
+        faceKey[k] = key[j];
+        ++k;
+      }
     }
 
-    getline(fin, line);
-    getline(fin, line);
+    (*mesh->get_simplex_up(faceKey)).marker = value;
+  }
 
-    std::regex tetCollectionRegex("",  std::regex_constants::ECMAScript | std::regex_constants::icase);
+  getline(fin, line);
+  getline(fin, line);
 
-    std::regex_search(line, match, tetCollectionRegex);
+  std::regex tetCollectionRegex(
+      "",
+      std::regex_constants::ECMAScript | std::regex_constants::icase);
 
-    ncollect = std::stoi(match[2].str());
+  std::regex_search(line, match, tetCollectionRegex);
 
-    std::regex collectTetRegex("",  std::regex_constants::ECMAScript | std::regex_constants::icase);
+  ncollect = std::stoi(match[2].str());
 
-    std::cout << "Reading in " << ncollect << " collections" << std::endl;
+  std::regex collectTetRegex(
+      "",
+      std::regex_constants::ECMAScript | std::regex_constants::icase);
 
-    for (auto tet : mesh->get_level_id<4>())
-    {
-        (*tet).marker = 0;
-    }
+  std::cout << "Reading in " << ncollect << " collections" << std::endl;
+
+  for (auto tet : mesh->get_level_id<4>()) {
+    (*tet).marker = 0;
+  }
 
-    for (int i = 0; i < ncollect; ++i)
-    {
-        getline(fin, line);
-        std::regex_search(line, match, collectTetRegex);
+  for (int i = 0; i < ncollect; ++i) {
+    getline(fin, line);
+    std::regex_search(line, match, collectTetRegex);
 
-        int  idx   = std::stoi(match[1].str());
-        int  value = std::stoi(match[3].str());
+    int idx = std::stoi(match[1].str());
+    int value = std::stoi(match[3].str());
 
-        auto key = cellMap[idx];
+    auto key = cellMap[idx];
 
-        (*mesh->get_simplex_up(key)).marker = value;
-    }
+    (*mesh->get_simplex_up(key)).marker = value;
+  }
 
-    return mesh;
+  return mesh;
 }
 
 } // end namespace gamer
diff --git a/src/Vertex.cpp b/src/Vertex.cpp
index e7cb2f87..9be16dfa 100644
--- a/src/Vertex.cpp
+++ b/src/Vertex.cpp
@@ -1,27 +1,21 @@
-/*
- * ***************************************************************************
- * This file is part of the GAMer software.
- * Copyright (C) 2016-2018
- * by Christopher Lee, John Moody, Rommie Amaro, J. Andrew McCammon,
- *    and Michael Holst
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * ***************************************************************************
- */
-
+// This file is part of the GAMer software.
+// Copyright (C) 2016-2021
+// by Christopher T. Lee and contributors
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, see 
+// or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+// Boston, MA 02111-1307 USA
 
 #define _USE_MATH_DEFINES
 #include 
@@ -30,78 +24,63 @@
 #include "gamer/Vertex.h"
 
 /// Namespace for all things gamer
-namespace gamer
-{
-
-Vertex operator+(const Vertex &A, const Vector &B)
-{
-    Vertex rval(A);
-    rval += B;
-    return rval;
-}
+namespace gamer {
 
-Vertex operator-(const Vertex &A, const Vector &B)
-{
-    Vertex rval(A);
-    rval -= B;
-    return rval;
+Vertex operator+(const Vertex &A, const Vector &B) {
+  Vertex rval(A);
+  rval += B;
+  return rval;
 }
 
-Vector operator-(const Vertex &A, const Vertex &B)
-{
-    return A.position - B.position;
+Vertex operator-(const Vertex &A, const Vector &B) {
+  Vertex rval(A);
+  rval -= B;
+  return rval;
 }
 
-Vertex operator*(REAL x, const Vertex &A)
-{
-    Vertex rval(A);
-    rval *= x;
-    return rval;
+Vector operator-(const Vertex &A, const Vertex &B) {
+  return A.position - B.position;
 }
 
-Vertex operator*(const Vertex &A, REAL x)
-{
-    return x*A;
+Vertex operator*(REAL x, const Vertex &A) {
+  Vertex rval(A);
+  rval *= x;
+  return rval;
 }
 
-Vertex operator/(const Vertex &A, REAL x)
-{
-    Vertex rval(A);
-    rval /= x;
-    return rval;
-}
+Vertex operator*(const Vertex &A, REAL x) { return x * A; }
 
-REAL distance(const Vertex &A, const Vertex &B)
-{
-    return length(A - B);
+Vertex operator/(const Vertex &A, REAL x) {
+  Vertex rval(A);
+  rval /= x;
+  return rval;
 }
 
-REAL angleDeg(const Vertex &A, const Vertex &B, const Vertex &C)
-{
-    Vector AB(A-B);
-    Vector CB(C-B);
-    return angleDeg(AB, CB);
+REAL distance(const Vertex &A, const Vertex &B) { return length(A - B); }
+
+REAL angleDeg(const Vertex &A, const Vertex &B, const Vertex &C) {
+  Vector AB(A - B);
+  Vector CB(C - B);
+  return angleDeg(AB, CB);
 }
 
-REAL angleDeg(const Vector &AB, const Vector &CB)
-{
+REAL angleDeg(const Vector &AB, const Vector &CB) {
 
-    return angle(AB, CB)*180/M_PI;
+  return angle(AB, CB) * 180 / M_PI;
 }
 
-REAL angle(const Vertex &A, const Vertex &B, const Vertex &C){
-    Vector AB(A-B);
-    Vector CB(C-B);
-    return angle(AB, CB);
+REAL angle(const Vertex &A, const Vertex &B, const Vertex &C) {
+  Vector AB(A - B);
+  Vector CB(C - B);
+  return angle(AB, CB);
 }
 
-REAL angle(const Vector &v1, const Vector &v2){
-    return std::atan2(length(cross(v1,v2)), dot(v1,v2));
+REAL angle(const Vector &v1, const Vector &v2) {
+  return std::atan2(length(cross(v1, v2)), dot(v1, v2));
 }
 
-REAL signed_angle(const Vector& v1, const Vector& v2, const Vector& reference)
-{
-    return std::atan2(dot(cross(v1, v2), reference), dot(v1, v2));
+REAL signed_angle(const Vector &v1, const Vector &v2, const Vector &reference) {
+  return std::atan2(dot(cross(v1, v2), reference), dot(v1, v2));
 }
 
 } // end namespace gamer
diff --git a/src/comsol_io.cpp b/src/comsol_io.cpp
new file mode 100644
index 00000000..2a936f26
--- /dev/null
+++ b/src/comsol_io.cpp
@@ -0,0 +1,277 @@
+// This file is part of the GAMer software.
+// Copyright (C) 2016-2021
+// by Christopher T. Lee and contributors
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, see 
+// or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+// Boston, MA 02111-1307 USA
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "gamer/SurfaceMesh.h"
+#include "gamer/TetMesh.h"
+
+/// Namespace for all things gamer
+namespace gamer {
+void writeComsol(const std::string &filename,
+                 const std::vector &meshes) {
+  std::ofstream fout(filename);
+  if (!fout.is_open()) {
+    std::cerr << "File '" << filename << "' could not be written to."
+              << std::endl;
+    // exit(1);
+    return;
+  }
+
+  fout << "# Generated using GAMer\n\n";
+  fout << "# Major & minor version\n";
+  fout << "0 1\n";
+
+  fout << "1 # number of tags\n";
+  fout << "# Tags\n";
+  fout << "5 mesh1\n";
+  // for (std::size_t idx = 0; idx < meshes.size(); ++idx) {
+  //   std::stringstream ss;
+  //   ss << "mesh" << idx;
+  //   fout << ss.str().length() << " " << ss.str() << "\n";
+  // }
+
+  fout << "1 # number of types\n";
+  fout << "# Types\n";
+  // for (std::size_t idx = 0; idx < meshes.size(); ++idx)
+  fout << "3 obj\n";
+
+  fout << "\n# --------- Object 0 ----------\n\n";
+  fout << "0 0 1\n";
+  fout << "4 Mesh # class\n";
+  fout << "4 # version\n";
+  fout << "3 # sdim\n";
+
+  std::size_t nvertices = 0;
+  std::size_t ntri = 0;
+
+  for (std::size_t idx = 0; idx < meshes.size(); ++idx) {
+    const SurfaceMesh &mesh = *meshes[idx];
+    nvertices += mesh.size<1>();
+    ntri += mesh.size<3>();
+  }
+
+  fout << nvertices << " # number of mesh vertices\n";
+  fout << "0 # lowest mesh vertex index\n\n";
+
+  std::vector<
+      std::map>
+      sigma;
+  typename SurfaceMesh::KeyType cnt = 0;
+
+  fout << "# Mesh point coordinates\n";
+  fout.precision(10);
+  for (std::size_t idx = 0; idx < meshes.size(); ++idx) {
+    const SurfaceMesh &mesh = *meshes[idx];
+    sigma.emplace_back();
+
+    // Get the vertex data directly
+    for (const auto vertexID : mesh.get_level_id<1>()) {
+      sigma[idx][mesh.get_name(vertexID)[0]] = cnt++;
+      auto vertex = *vertexID;
+
+      fout << vertex[0] << " " << vertex[1] << " " << vertex[2] << " "
+           << "\n";
+    }
+  }
+  fout << "\n";
+
+  fout << "1 # number of element types\n\n";
+  fout << "# Type #0\n\n";
+  fout << "3 tri # type name\n\n";
+  fout << "3 # number of vertices per element\n";
+  fout << ntri << " # number of elements\n";
+  fout << "# Elements\n";
+
+  for (std::size_t idx = 0; idx < meshes.size(); ++idx) {
+    const SurfaceMesh &mesh = *meshes[idx];
+    bool orientationError = false;
+
+    // Get the face nodes
+    for (auto faceID : mesh.get_level_id<3>()) {
+      auto w = mesh.get_name(faceID);
+
+      auto orientation = (*faceID).orientation;
+      if (orientation == 1) {
+        fout << sigma[idx][w[0]] << " " << sigma[idx][w[1]] << " "
+             << sigma[idx][w[2]] << "\n";
+      } else if (orientation == -1) {
+        fout << sigma[idx][w[2]] << " " << sigma[idx][w[1]] << " "
+             << sigma[idx][w[0]] << "\n";
+
+      } else {
+        orientationError = true;
+        fout << sigma[idx][w[0]] << " " << sigma[idx][w[1]] << " "
+             << sigma[idx][w[2]] << "\n";
+      }
+    }
+    if (orientationError) {
+      std::cerr << "WARNING(writeComsol): The orientation of one or more faces "
+                << "is not defined. Did you run compute_orientation()?"
+                << std::endl;
+    }
+  }
+  fout << "\n" << ntri << " # number of geometric entity indices\n";
+  fout << "# Geometric entity indices\n";
+  for (std::size_t idx = 0; idx < meshes.size(); ++idx) {
+    const SurfaceMesh &mesh = *meshes[idx];
+    for (auto faceID : mesh.get_level_id<3>()) {
+      fout << (*faceID).marker << "\n";
+    }
+  }
+  fout.close();
+} // namespace gamer
+
+void writeComsol(const std::string &filename, const SurfaceMesh &mesh) {
+  std::vector v{&mesh};
+  writeComsol(filename, v);
+}
+
+void writeComsol(const std::string &filename, const TetMesh &mesh) {
+
+  if ((*mesh.get_simplex_up()).higher_order == true) {
+    gamer_runtime_error(
+        "Comsol output does not support higher order meshes at this point.");
+  }
+
+  std::ofstream fout(filename);
+  if (!fout.is_open()) {
+    std::stringstream ss;
+    ss << "File '" << filename << "' could not be written to.";
+    gamer_runtime_error(ss.str());
+  }
+
+  fout << "# Generated using GAMer\n\n";
+  fout << "# Major & minor version\n";
+  fout << "0 1\n";
+
+  fout << "1 # number of tags\n";
+  fout << "# Tags\n";
+  fout << "5 mesh1\n";
+  // for (std::size_t idx = 0; idx < meshes.size(); ++idx) {
+  //   std::stringstream ss;
+  //   ss << "mesh" << idx;
+  //   fout << ss.str().length() << " " << ss.str() << "\n";
+  // }
+
+  fout << "1 # number of types\n";
+  fout << "# Types\n";
+  // for (std::size_t idx = 0; idx < meshes.size(); ++idx)
+  fout << "3 obj\n";
+
+  fout << "\n# --------- Object 0 ----------\n\n";
+  fout << "0 0 1\n";
+  fout << "4 Mesh # class\n";
+  fout << "4 # version\n";
+  fout << "3 # sdim\n";
+
+  std::map sigma;
+  size_t cnt = 0;
+
+  fout << mesh.size<1>() << " # number of mesh vertices\n";
+  fout << "0 # lowest mesh vertex index\n\n";
+  fout << "# Mesh point coordinates\n";
+
+  fout.precision(10);
+  for (const auto vertexID : mesh.get_level_id<1>()) {
+    size_t idx = cnt;
+    sigma[mesh.get_name(vertexID)[0]] = cnt++;
+    auto vertex = *vertexID;
+    fout << vertex[0] << " " << vertex[1] << " " << vertex[2] << " "
+         << "\n";
+  }
+  fout << "\n";
+
+  fout << "2 # number of element types\n\n";
+  fout << "# Type #0\n\n";
+  fout << "3 tet # type name\n\n";
+  fout << "4 # number of vertices per element\n";
+  fout << mesh.size<4>() << " # number of elements\n";
+  fout << "# Elements\n";
+  // Print out Tetrahedra
+  cnt = 0;
+  std::vector> faceMarkerList;
+  std::vector> cellMarkerList;
+  bool orientationError = false;
+
+  for (const auto tetID : mesh.get_level_id<4>()) {
+    std::size_t idx = cnt++;
+    auto w = mesh.get_name(tetID);
+    auto orientation = (*tetID).orientation;
+
+    if (orientation == 1) {
+      fout << std::setw(4) << sigma[w[0]] << " " << std::setw(4) << sigma[w[1]]
+           << " " << std::setw(4) << sigma[w[2]] << " " << std::setw(4)
+           << sigma[w[3]] << "\n";
+    } else if (orientation == -1) {
+      fout << std::setw(4) << sigma[w[3]] << " " << std::setw(4) << sigma[w[1]]
+           << " " << std::setw(4) << sigma[w[2]] << " " << std::setw(4)
+           << sigma[w[0]] << "\n";
+    } else {
+      orientationError = true;
+      fout << std::setw(4) << sigma[w[0]] << " " << std::setw(4) << sigma[w[1]]
+           << " " << std::setw(4) << sigma[w[2]] << " " << std::setw(4)
+           << sigma[w[3]] << "\n";
+    }
+  }
+  if (orientationError) {
+    std::cerr << "WARNING(writeComsol): The orientation of one or more cells "
+              << "is not defined. Did you run compute_orientation()?"
+              << std::endl;
+  }
+
+  fout << "\n" << mesh.size<4>() << " # number of geometric entity indices\n";
+  fout << "# Geometric entity indices\n";
+  for (const auto tetID : mesh.get_level_id<4>()) {
+    fout << (*tetID).marker << "\n";
+  }
+
+  fout << "\n# Type #1\n\n";
+  fout << "3 tri # type name\n\n";
+  fout << "3 # number of vertices per element\n";
+  fout << mesh.size<3>() << " # number of elements\n";
+  fout << "# Elements\n";
+  orientationError = false;
+  // Get the face nodes
+  for (auto faceID : mesh.get_level_id<3>()) {
+    auto w = mesh.get_name(faceID);
+    fout << sigma[w[0]] << " " << sigma[w[1]] << " " << sigma[w[2]] << "\n";
+  }
+  if (orientationError) {
+    std::cerr << "WARNING(writeComsol): The orientation of one or more faces "
+              << "is not defined. Did you run compute_orientation()?"
+              << std::endl;
+  }
+
+  fout << "\n" << mesh.size<3>() << " # number of geometric entity indices\n";
+  fout << "# Geometric entity indices\n";
+  for (const auto faceID : mesh.get_level_id<3>()) {
+    fout << (*faceID).marker << "\n";
+  }
+  fout.close();
+}
+
+} // namespace gamer
diff --git a/src/pdb2mesh.cpp b/src/pdb2mesh.cpp
index 9eec3b05..590ea56c 100644
--- a/src/pdb2mesh.cpp
+++ b/src/pdb2mesh.cpp
@@ -30,85 +30,80 @@
  * ***************************************************************************
  */
 
-#include "gamer/SurfaceMesh.h"
 #include "gamer/PDBReader.h"
-#include 
-#include 
+#include "gamer/SurfaceMesh.h"
 #include 
+#include 
+#include 
 
 /// Namespace for all things gamer
-namespace gamer
-{
-
+namespace gamer {
 
-#define IndexVect1(i, j, k) ((k) * xdim1 * ydim1 + (j) * xdim1 + (i))
-#define MaxVal            999999
-#define MaxAtom           10
+#define IndexVect1(i, j, k) ((k)*xdim1 * ydim1 + (j)*xdim1 + (i))
+#define MaxVal 999999
+#define MaxAtom 10
 
 struct MOL_VERTEX {
-    float         x;     // vertex coordinate
-    float         y;
-    float         z;
-    unsigned char neigh; // first bit: +x; second bit: -x
-                         // third bit: +y; fourth bit: -y
-                         // fifth bit: +z; sixth  bit: -z
-    unsigned short px;   // the corresponding index
-    unsigned short py;
-    unsigned short pz;
+  float x; // vertex coordinate
+  float y;
+  float z;
+  unsigned char neigh; // first bit: +x; second bit: -x
+                       // third bit: +y; fourth bit: -y
+                       // fifth bit: +z; sixth  bit: -z
+  unsigned short px;   // the corresponding index
+  unsigned short py;
+  unsigned short pz;
 };
 
 /** @brief Other data structure FLTVECT (float) */
 struct FLTVECT {
-    float x;   /**< @brief x-coordinate */
-    float y;   /**< @brief y-coordinate */
-    float z;   /**< @brief z-coordinate */
-    int   m;   /**< @brief Marker */
-    bool  sel; /**< @brief selection flag */
+  float x;  /**< @brief x-coordinate */
+  float y;  /**< @brief y-coordinate */
+  float z;  /**< @brief z-coordinate */
+  int m;    /**< @brief Marker */
+  bool sel; /**< @brief selection flag */
 };
 
-
 /** @brief Other data structure FLT2VECT (float) */
 struct FLT2VECT {
-    float x; /**< @brief x-coordinate */
-    float y; /**< @brief y-coordinate */
+  float x; /**< @brief x-coordinate */
+  float y; /**< @brief y-coordinate */
 };
 
 /** @brief Other data structure INT4VECT (int) */
 struct INT4VECT {
-    int a; /**< @brief first integer */
-    int b; /**< @brief second integer */
-    int c; /**< @brief third integer */
-    int d; /**< @brief fourth integer */
+  int a; /**< @brief first integer */
+  int b; /**< @brief second integer */
+  int c; /**< @brief third integer */
+  int d; /**< @brief fourth integer */
 };
 
 /** @brief Other data structure MinHeapS */
 struct MinHeapS {
-    unsigned short *x;    /**< @brief x-coordinate */
-    unsigned short *y;    /**< @brief y-coordinate */
-    unsigned short *z;    /**< @brief z-coordinate */
-    int            *seed; /**< @brief seed */
-    float          *dist; /**< @brief distance */
-    int             size; /**< @brief size */
+  unsigned short *x; /**< @brief x-coordinate */
+  unsigned short *y; /**< @brief y-coordinate */
+  unsigned short *z; /**< @brief z-coordinate */
+  int *seed;         /**< @brief seed */
+  float *dist;       /**< @brief distance */
+  int size;          /**< @brief size */
 };
 
 /** @brief Other data structure SEEDS */
 struct SEEDS {
-    float seedx;         /**< @brief x-coordinate */
-    float seedy;         /**< @brief y-coordinate */
-    float seedz;         /**< @brief z-coordinate */
-    int   atom[MaxAtom]; /**< @brief atom array */
+  float seedx;       /**< @brief x-coordinate */
+  float seedy;       /**< @brief y-coordinate */
+  float seedz;       /**< @brief z-coordinate */
+  int atom[MaxAtom]; /**< @brief atom array */
 };
 
 /** @brief Other data structure ATOM */
 struct ATOM {
-    float x;      /**< @brief x-coordinate */
-    float y;      /**< @brief y-coordinate */
-    float z;      /**< @brief z-coordinate */
-    float radius; /**< @brief radius */
+  float x;      /**< @brief x-coordinate */
+  float y;      /**< @brief y-coordinate */
+  float z;      /**< @brief z-coordinate */
+  float radius; /**< @brief radius */
 };
 
-
-
 int GLOBAL_xdim, GLOBAL_ydim, GLOBAL_zdim;
 
 // GRID variables
@@ -116,864 +111,753 @@ int *GLOBAL_segment_index;
 int *GLOBAL_atom_index;
 
 // Border variables
-INT4VECT   *GLOBAL_quads;   int GLOBAL_vert_num;
-MOL_VERTEX *GLOBAL_vertex;  int GLOBAL_quad_num;
+INT4VECT *GLOBAL_quads;
+int GLOBAL_vert_num;
+MOL_VERTEX *GLOBAL_vertex;
+int GLOBAL_quad_num;
 
+#undef IndexVect
+#define IndexVect(i, j, k) (((k)*GLOBAL_ydim + (j)) * GLOBAL_xdim + (i))
 
+int CheckFaceCorner(float x, float y, float z);
 
-#undef IndexVect
-#define IndexVect(i, j, k) ( ( (k) * GLOBAL_ydim + (j) ) * GLOBAL_xdim + (i) )
-
-
-int  CheckFaceCorner(float x,
-                     float y,
-                     float z);
-
-void SetAtomIndex(int,
-                  int,
-                  int,
-                  int);
-int   ExtractSAS(int   atom_num,
-                 ATOM *atom_list);
-char  CheckManifold(int i,
-                    int j,
-                    int k);
-float GetAngle(int a,
-               int b,
-               int c);
-
-
-#define MaxDist    29999
-
-int            xdim1, ydim1, zdim1;
-MinHeapS      *min_heap;
-SEEDS         *AllSeeds;
-int           *heap_pointer;
-ATOM          *atom_list;
-float          threshold;
-unsigned short min_x, min_y, min_z;
-int            min_seed;
-float          min_dist;
+void SetAtomIndex(int, int, int, int);
+int ExtractSAS(int atom_num, ATOM *atom_list);
+char CheckManifold(int i, int j, int k);
+float GetAngle(int a, int b, int c);
 
+#define MaxDist 29999
+
+int xdim1, ydim1, zdim1;
+MinHeapS *min_heap;
+SEEDS *AllSeeds;
+int *heap_pointer;
+ATOM *atom_list;
+float threshold;
+unsigned short min_x, min_y, min_z;
+int min_seed;
+float min_dist;
 
 void GetMinimum(void);
-void InsertHeap(int,
-                int,
-                int,
-                float);
-void UpdateHeap(int,
-                int,
-                int,
-                float);
-void    Marching(void);
-FLTVECT FindSeed(float,
-                 float,
-                 float,
-                 int);
-
-
-void ExtractSES(MinHeapS *mheap, SEEDS *all_seeds, int *heappointer, int xd, int yd, int zd,
-                int *atom_index, int atom_num, ATOM *atomlist, float thresh)
-{
-    int     i, j, k;
-    int     m, n, l, num, c;
-    int     index, index1;
-    float   dist;
-    FLTVECT seed;
-    char    visited;
-
-
-    xdim1 = xd;
-    ydim1 = yd;
-    zdim1 = zd;
-    atom_list = atomlist;
-    min_heap  = mheap;
-    AllSeeds  = all_seeds;
-    heap_pointer = heappointer;
-    threshold = thresh;
-
-    /* Initialize */
-    index = 0;
-    min_heap->size = 0;
-
-    for (k = 0; k < zdim1; k++)
-    {
-        for (j = 0; j < ydim1; j++)
-        {
-            for (i = 0; i < xdim1; i++)
-            {
-                if (atom_index[IndexVect1(i, j, k)] < 0)
-                {
-                    for (num = 0; num < MaxAtom; num++)
-                    {
-                        AllSeeds[index].atom[num] = -1;
-                    }
-                    num = 0;
-
-                    for (l = k - 1; l <= k + 1; l++)
-                    {
-                        for (n = j - 1; n <= j + 1; n++)
-                        {
-                            for (m = i - 1; m <= i + 1; m++)
-                            {
-                                if ((m == i) || (n == j) || (l == k))
-                                {
-                                    index1 = atom_index[IndexVect1(m, n, l)];
-
-                                    if (index1 < 0)
-                                    {
-                                        index1  = -index1 - 1;
-                                        visited = 0;
-
-                                        for (c = 0; c < num; c++)
-                                        {
-                                            if (index1 == AllSeeds[index].atom[c])
-                                            {
-                                                visited = 1;
-                                            }
-                                        }
-
-                                        if (visited == 0)
-                                        {
-                                            AllSeeds[index].atom[num] = index1;
-                                            num++;
-
-                                            if (num == MaxAtom)
-                                            {
-                                                num--;
-                                            }
-                                        }
-                                    }
-                                }
-                            }
-                        }
+void InsertHeap(int, int, int, float);
+void UpdateHeap(int, int, int, float);
+void Marching(void);
+FLTVECT FindSeed(float, float, float, int);
+
+void ExtractSES(MinHeapS *mheap, SEEDS *all_seeds, int *heappointer, int xd,
+                int yd, int zd, int *atom_index, int atom_num, ATOM *atomlist,
+                float thresh) {
+  int i, j, k;
+  int m, n, l, num, c;
+  int index, index1;
+  float dist;
+  FLTVECT seed;
+  char visited;
+
+  xdim1 = xd;
+  ydim1 = yd;
+  zdim1 = zd;
+  atom_list = atomlist;
+  min_heap = mheap;
+  AllSeeds = all_seeds;
+  heap_pointer = heappointer;
+  threshold = thresh;
+
+  /* Initialize */
+  index = 0;
+  min_heap->size = 0;
+
+  for (k = 0; k < zdim1; k++) {
+    for (j = 0; j < ydim1; j++) {
+      for (i = 0; i < xdim1; i++) {
+        if (atom_index[IndexVect1(i, j, k)] < 0) {
+          for (num = 0; num < MaxAtom; num++) {
+            AllSeeds[index].atom[num] = -1;
+          }
+          num = 0;
+
+          for (l = k - 1; l <= k + 1; l++) {
+            for (n = j - 1; n <= j + 1; n++) {
+              for (m = i - 1; m <= i + 1; m++) {
+                if ((m == i) || (n == j) || (l == k)) {
+                  index1 = atom_index[IndexVect1(m, n, l)];
+
+                  if (index1 < 0) {
+                    index1 = -index1 - 1;
+                    visited = 0;
+
+                    for (c = 0; c < num; c++) {
+                      if (index1 == AllSeeds[index].atom[c]) {
+                        visited = 1;
+                      }
                     }
 
-                    seed = FindSeed(i, j, k, index);
-                    AllSeeds[index].seedx = seed.x;
-                    AllSeeds[index].seedy = seed.y;
-                    AllSeeds[index].seedz = seed.z;
-                    dist = (seed.x - i) * (seed.x - i) + (seed.y - j) * (seed.y - j) + (seed.z - k) * (seed.z - k);
-                    min_seed = index;
-                    InsertHeap(i, j, k, dist);
+                    if (visited == 0) {
+                      AllSeeds[index].atom[num] = index1;
+                      num++;
 
-                    index++;
-                }
-                else if (atom_index[IndexVect1(i, j, k)] > 0)
-                {
-                    heap_pointer[IndexVect1(i, j, k)] = MaxVal;
-                }
-                else
-                {
-                    heap_pointer[IndexVect1(i, j, k)] = -11;
+                      if (num == MaxAtom) {
+                        num--;
+                      }
+                    }
+                  }
                 }
+              }
             }
-        }
+          }
+
+          seed = FindSeed(i, j, k, index);
+          AllSeeds[index].seedx = seed.x;
+          AllSeeds[index].seedy = seed.y;
+          AllSeeds[index].seedz = seed.z;
+          dist = (seed.x - i) * (seed.x - i) + (seed.y - j) * (seed.y - j) +
+                 (seed.z - k) * (seed.z - k);
+          min_seed = index;
+          InsertHeap(i, j, k, dist);
+
+          index++;
+        } else if (atom_index[IndexVect1(i, j, k)] > 0) {
+          heap_pointer[IndexVect1(i, j, k)] = MaxVal;
+        } else {
+          heap_pointer[IndexVect1(i, j, k)] = -11;
+        }
+      }
     }
+  }
 
+  /* Fast Marching Method */
+  while (1) {
+    GetMinimum();
 
-    /* Fast Marching Method */
-    while (1)
-    {
-        GetMinimum();
+    if (min_dist >= MaxDist - 0.001) {
+      break;
+    }
 
-        if (min_dist >= MaxDist - 0.001)
-        {
-            break;
-        }
+    Marching();
+  }
+}
 
-        Marching();
+void GetMinimum(void) {
+  int pointer, left, right;
+  float dist;
+
+  min_x = min_heap->x[0];
+  min_y = min_heap->y[0];
+  min_z = min_heap->z[0];
+  min_seed = min_heap->seed[0];
+  min_dist = min_heap->dist[0];
+
+  if (min_dist == MaxDist) {
+    return;
+  }
+
+  heap_pointer[IndexVect1(min_heap->x[0], min_heap->y[0], min_heap->z[0])] = -3;
+
+  min_heap->size--;
+  dist = min_heap->dist[min_heap->size];
+
+  pointer = 1;
+
+  while (pointer <= min_heap->size / 2) {
+    left = 2 * pointer;
+    right = 2 * pointer + 1;
+
+    if ((min_heap->dist[left - 1] <= min_heap->dist[right - 1]) &&
+        (min_heap->dist[left - 1] < dist)) {
+      min_heap->x[pointer - 1] = min_heap->x[left - 1];
+      min_heap->y[pointer - 1] = min_heap->y[left - 1];
+      min_heap->z[pointer - 1] = min_heap->z[left - 1];
+      min_heap->seed[pointer - 1] = min_heap->seed[left - 1];
+      min_heap->dist[pointer - 1] = min_heap->dist[left - 1];
+      heap_pointer[IndexVect1(min_heap->x[pointer - 1],
+                              min_heap->y[pointer - 1],
+                              min_heap->z[pointer - 1])] = pointer - 1;
+      pointer = left;
+    } else if ((min_heap->dist[left - 1] > min_heap->dist[right - 1]) &&
+               (min_heap->dist[right - 1] < dist)) {
+      min_heap->x[pointer - 1] = min_heap->x[right - 1];
+      min_heap->y[pointer - 1] = min_heap->y[right - 1];
+      min_heap->z[pointer - 1] = min_heap->z[right - 1];
+      min_heap->seed[pointer - 1] = min_heap->seed[right - 1];
+      min_heap->dist[pointer - 1] = min_heap->dist[right - 1];
+      heap_pointer[IndexVect1(min_heap->x[pointer - 1],
+                              min_heap->y[pointer - 1],
+                              min_heap->z[pointer - 1])] = pointer - 1;
+      pointer = right;
+    } else {
+      break;
     }
+  }
+
+  min_heap->x[pointer - 1] = min_heap->x[min_heap->size];
+  min_heap->y[pointer - 1] = min_heap->y[min_heap->size];
+  min_heap->z[pointer - 1] = min_heap->z[min_heap->size];
+  min_heap->seed[pointer - 1] = min_heap->seed[min_heap->size];
+  min_heap->dist[pointer - 1] = dist;
+  heap_pointer[IndexVect1(min_heap->x[min_heap->size],
+                          min_heap->y[min_heap->size],
+                          min_heap->z[min_heap->size])] = pointer - 1;
 }
 
-void GetMinimum(void)
-{
-    int   pointer, left, right;
-    float dist;
-
-    min_x = min_heap->x[0];
-    min_y = min_heap->y[0];
-    min_z = min_heap->z[0];
-    min_seed = min_heap->seed[0];
-    min_dist = min_heap->dist[0];
+void InsertHeap(int x, int y, int z, float dist) {
+  int parent = 0;
 
+  min_heap->size++;
+  int pointer = min_heap->size;
 
-    if (min_dist == MaxDist)
-    {
-        return;
+  while (pointer > 1) {
+    if (pointer % 2 == 0) {
+      parent = pointer / 2;
+    } else if (pointer % 2 == 1) {
+      parent = (pointer - 1) / 2;
     }
 
-    heap_pointer[IndexVect1(min_heap->x[0], min_heap->y[0], min_heap->z[0])] = -3;
+    if (dist < min_heap->dist[parent - 1]) {
+      min_heap->x[pointer - 1] = min_heap->x[parent - 1];
+      min_heap->y[pointer - 1] = min_heap->y[parent - 1];
+      min_heap->z[pointer - 1] = min_heap->z[parent - 1];
+      min_heap->seed[pointer - 1] = min_heap->seed[parent - 1];
+      min_heap->dist[pointer - 1] = min_heap->dist[parent - 1];
 
-    min_heap->size--;
-    dist = min_heap->dist[min_heap->size];
+      int index = IndexVect1(min_heap->x[pointer - 1], min_heap->y[pointer - 1],
+                             min_heap->z[pointer - 1]);
+      heap_pointer[index] = pointer - 1;
 
-    pointer = 1;
-
-    while (pointer <= min_heap->size / 2)
-    {
-        left  = 2 * pointer;
-        right = 2 * pointer + 1;
-
-        if ((min_heap->dist[left - 1] <= min_heap->dist[right - 1]) && (min_heap->dist[left - 1] < dist))
-        {
-            min_heap->x[pointer - 1] = min_heap->x[left - 1];
-            min_heap->y[pointer - 1] = min_heap->y[left - 1];
-            min_heap->z[pointer - 1] = min_heap->z[left - 1];
-            min_heap->seed[pointer - 1] = min_heap->seed[left - 1];
-            min_heap->dist[pointer - 1] = min_heap->dist[left - 1];
-            heap_pointer[IndexVect1(min_heap->x[pointer - 1], min_heap->y[pointer - 1], min_heap->z[pointer - 1])] = pointer - 1;
-            pointer = left;
-        }
-        else if ((min_heap->dist[left - 1] > min_heap->dist[right - 1]) && (min_heap->dist[right - 1] < dist))
-        {
-            min_heap->x[pointer - 1] = min_heap->x[right - 1];
-            min_heap->y[pointer - 1] = min_heap->y[right - 1];
-            min_heap->z[pointer - 1] = min_heap->z[right - 1];
-            min_heap->seed[pointer - 1] = min_heap->seed[right - 1];
-            min_heap->dist[pointer - 1] = min_heap->dist[right - 1];
-            heap_pointer[IndexVect1(min_heap->x[pointer - 1], min_heap->y[pointer - 1], min_heap->z[pointer - 1])] = pointer - 1;
-            pointer = right;
-        }
-        else
-        {
-            break;
-        }
+      pointer = parent;
+    } else {
+      break;
     }
-
-    min_heap->x[pointer - 1] = min_heap->x[min_heap->size];
-    min_heap->y[pointer - 1] = min_heap->y[min_heap->size];
-    min_heap->z[pointer - 1] = min_heap->z[min_heap->size];
-    min_heap->seed[pointer - 1] = min_heap->seed[min_heap->size];
-    min_heap->dist[pointer - 1] = dist;
-    heap_pointer[IndexVect1(min_heap->x[min_heap->size], min_heap->y[min_heap->size], min_heap->z[min_heap->size])] = pointer - 1;
+  }
+  min_heap->x[pointer - 1] = x;
+  min_heap->y[pointer - 1] = y;
+  min_heap->z[pointer - 1] = z;
+  min_heap->seed[pointer - 1] = min_seed;
+  min_heap->dist[pointer - 1] = dist;
+
+  heap_pointer[IndexVect1(x, y, z)] = pointer - 1;
 }
 
-void InsertHeap(int x, int y, int z, float dist)
-{
-    int parent = 0;
-
-
-    min_heap->size++;
-    int pointer = min_heap->size;
-
-    while (pointer > 1)
-    {
-        if (pointer % 2 == 0)
-        {
-            parent = pointer / 2;
-        }
-        else if (pointer % 2 == 1)
-        {
-            parent = (pointer - 1) / 2;
-        }
-
-        if (dist < min_heap->dist[parent - 1])
-        {
-            min_heap->x[pointer - 1] = min_heap->x[parent - 1];
-            min_heap->y[pointer - 1] = min_heap->y[parent - 1];
-            min_heap->z[pointer - 1] = min_heap->z[parent - 1];
-            min_heap->seed[pointer - 1] = min_heap->seed[parent - 1];
-            min_heap->dist[pointer - 1] = min_heap->dist[parent - 1];
+void UpdateHeap(int x, int y, int z, float dist) {
+  int parent = 0;
+  int left, right;
+  char up = 0;
 
-            int index = IndexVect1(min_heap->x[pointer - 1], min_heap->y[pointer - 1], min_heap->z[pointer - 1]);
-            heap_pointer[index] = pointer - 1;
+  int pointer = heap_pointer[IndexVect1(x, y, z)] + 1;
 
-            pointer = parent;
-        }
-        else
-        {
-            break;
-        }
+  // checking the upper elements
+  while (pointer > 1) {
+    if (pointer % 2 == 0) {
+      parent = pointer / 2;
+    } else if (pointer % 2 == 1) {
+      parent = (pointer - 1) / 2;
     }
-    min_heap->x[pointer - 1] = x;
-    min_heap->y[pointer - 1] = y;
-    min_heap->z[pointer - 1] = z;
-    min_heap->seed[pointer - 1] = min_seed;
-    min_heap->dist[pointer - 1] = dist;
 
-    heap_pointer[IndexVect1(x, y, z)] = pointer - 1;
-}
+    if (dist < min_heap->dist[parent - 1]) {
+      min_heap->x[pointer - 1] = min_heap->x[parent - 1];
+      min_heap->y[pointer - 1] = min_heap->y[parent - 1];
+      min_heap->z[pointer - 1] = min_heap->z[parent - 1];
+      min_heap->seed[pointer - 1] = min_heap->seed[parent - 1];
+      min_heap->dist[pointer - 1] = min_heap->dist[parent - 1];
 
-void UpdateHeap(int x, int y, int z, float dist)
-{
-    int  parent = 0;
-    int  left, right;
-    char up = 0;
+      int index = IndexVect1(min_heap->x[pointer - 1], min_heap->y[pointer - 1],
+                             min_heap->z[pointer - 1]);
+      heap_pointer[index] = pointer - 1;
 
-
-    int pointer = heap_pointer[IndexVect1(x, y, z)] + 1;
-
-    // checking the upper elements
-    while (pointer > 1)
-    {
-        if (pointer % 2 == 0)
-        {
-            parent = pointer / 2;
-        }
-        else if (pointer % 2 == 1)
-        {
-            parent = (pointer - 1) / 2;
-        }
-
-        if (dist < min_heap->dist[parent - 1])
-        {
-            min_heap->x[pointer - 1] = min_heap->x[parent - 1];
-            min_heap->y[pointer - 1] = min_heap->y[parent - 1];
-            min_heap->z[pointer - 1] = min_heap->z[parent - 1];
-            min_heap->seed[pointer - 1] = min_heap->seed[parent - 1];
-            min_heap->dist[pointer - 1] = min_heap->dist[parent - 1];
-
-            int index = IndexVect1(min_heap->x[pointer - 1], min_heap->y[pointer - 1], min_heap->z[pointer - 1]);
-            heap_pointer[index] = pointer - 1;
-
-            pointer = parent;
-        }
-        else
-        {
-            break;
-        }
+      pointer = parent;
+    } else {
+      break;
     }
-
-    if (up == 0)
-    {
-        // checking the lower elements
-        while (pointer <= min_heap->size / 2)
-        {
-            left  = 2 * pointer;
-            right = 2 * pointer + 1;
-
-            if ((min_heap->dist[left - 1] <= min_heap->dist[right - 1]) && (min_heap->dist[left - 1] < dist))
-            {
-                min_heap->x[pointer - 1] = min_heap->x[left - 1];
-                min_heap->y[pointer - 1] = min_heap->y[left - 1];
-                min_heap->z[pointer - 1] = min_heap->z[left - 1];
-                min_heap->seed[pointer - 1] = min_heap->seed[left - 1];
-                min_heap->dist[pointer - 1] = min_heap->dist[left - 1];
-                heap_pointer[IndexVect1(min_heap->x[pointer - 1], min_heap->y[pointer - 1], min_heap->z[pointer - 1])] = pointer - 1;
-                pointer = left;
-            }
-            else if ((min_heap->dist[left - 1] > min_heap->dist[right - 1]) && (min_heap->dist[right - 1] < dist))
-            {
-                min_heap->x[pointer - 1] = min_heap->x[right - 1];
-                min_heap->y[pointer - 1] = min_heap->y[right - 1];
-                min_heap->z[pointer - 1] = min_heap->z[right - 1];
-                min_heap->seed[pointer - 1] = min_heap->seed[right - 1];
-                min_heap->dist[pointer - 1] = min_heap->dist[right - 1];
-                heap_pointer[IndexVect1(min_heap->x[pointer - 1], min_heap->y[pointer - 1], min_heap->z[pointer - 1])] = pointer - 1;
-                pointer = right;
-            }
-            else
-            {
-                break;
-            }
-        }
+  }
+
+  if (up == 0) {
+    // checking the lower elements
+    while (pointer <= min_heap->size / 2) {
+      left = 2 * pointer;
+      right = 2 * pointer + 1;
+
+      if ((min_heap->dist[left - 1] <= min_heap->dist[right - 1]) &&
+          (min_heap->dist[left - 1] < dist)) {
+        min_heap->x[pointer - 1] = min_heap->x[left - 1];
+        min_heap->y[pointer - 1] = min_heap->y[left - 1];
+        min_heap->z[pointer - 1] = min_heap->z[left - 1];
+        min_heap->seed[pointer - 1] = min_heap->seed[left - 1];
+        min_heap->dist[pointer - 1] = min_heap->dist[left - 1];
+        heap_pointer[IndexVect1(min_heap->x[pointer - 1],
+                                min_heap->y[pointer - 1],
+                                min_heap->z[pointer - 1])] = pointer - 1;
+        pointer = left;
+      } else if ((min_heap->dist[left - 1] > min_heap->dist[right - 1]) &&
+                 (min_heap->dist[right - 1] < dist)) {
+        min_heap->x[pointer - 1] = min_heap->x[right - 1];
+        min_heap->y[pointer - 1] = min_heap->y[right - 1];
+        min_heap->z[pointer - 1] = min_heap->z[right - 1];
+        min_heap->seed[pointer - 1] = min_heap->seed[right - 1];
+        min_heap->dist[pointer - 1] = min_heap->dist[right - 1];
+        heap_pointer[IndexVect1(min_heap->x[pointer - 1],
+                                min_heap->y[pointer - 1],
+                                min_heap->z[pointer - 1])] = pointer - 1;
+        pointer = right;
+      } else {
+        break;
+      }
     }
+  }
 
+  min_heap->x[pointer - 1] = x;
+  min_heap->y[pointer - 1] = y;
+  min_heap->z[pointer - 1] = z;
+  min_heap->seed[pointer - 1] = min_seed;
+  min_heap->dist[pointer - 1] = dist;
 
-    min_heap->x[pointer - 1] = x;
-    min_heap->y[pointer - 1] = y;
-    min_heap->z[pointer - 1] = z;
-    min_heap->seed[pointer - 1] = min_seed;
-    min_heap->dist[pointer - 1] = dist;
-
-    heap_pointer[IndexVect1(x, y, z)] = pointer - 1;
+  heap_pointer[IndexVect1(x, y, z)] = pointer - 1;
 }
 
-void Marching(void)
-{
-    int   tempt_x, tempt_y, tempt_z;
-    float dt, dist;
-    int   neighbor, seed;
-    char  boundary;
-    float min_seedx, min_seedy, min_seedz;
-    float seedx, seedy, seedz;
-
-
-    min_seedx = AllSeeds[min_seed].seedx;
-    min_seedy = AllSeeds[min_seed].seedy;
-    min_seedz = AllSeeds[min_seed].seedz;
-
-    boundary = 0;
-
-
-    /* ============================ */
-    tempt_x = std::max(min_x - 1, 0);
-    tempt_y = min_y;
-    tempt_z = min_z;
-
-    if (heap_pointer[IndexVect1(tempt_x, tempt_y, tempt_z)] == MaxVal)
-    {
-        dist = (tempt_x - min_seedx) * (tempt_x - min_seedx) + (tempt_y - min_seedy) * (tempt_y - min_seedy) + (tempt_z - min_seedz) * (tempt_z - min_seedz);
-
-        if (dist <= threshold)
-        {
-            InsertHeap(tempt_x, tempt_y, tempt_z, dist);
-        }
-        else
-        {
-            boundary = 1;
-        }
+void Marching(void) {
+  int tempt_x, tempt_y, tempt_z;
+  float dt, dist;
+  int neighbor, seed;
+  char boundary;
+  float min_seedx, min_seedy, min_seedz;
+  float seedx, seedy, seedz;
+
+  min_seedx = AllSeeds[min_seed].seedx;
+  min_seedy = AllSeeds[min_seed].seedy;
+  min_seedz = AllSeeds[min_seed].seedz;
+
+  boundary = 0;
+
+  /* ============================ */
+  tempt_x = std::max(min_x - 1, 0);
+  tempt_y = min_y;
+  tempt_z = min_z;
+
+  if (heap_pointer[IndexVect1(tempt_x, tempt_y, tempt_z)] == MaxVal) {
+    dist = (tempt_x - min_seedx) * (tempt_x - min_seedx) +
+           (tempt_y - min_seedy) * (tempt_y - min_seedy) +
+           (tempt_z - min_seedz) * (tempt_z - min_seedz);
+
+    if (dist <= threshold) {
+      InsertHeap(tempt_x, tempt_y, tempt_z, dist);
+    } else {
+      boundary = 1;
     }
-    else if (heap_pointer[IndexVect1(tempt_x, tempt_y, tempt_z)] > -1)
-    {
-        neighbor = heap_pointer[IndexVect1(tempt_x, tempt_y, tempt_z)];
-        dt = min_heap->dist[neighbor];
-
-        if (dt < MaxDist)
-        {
-            dist = (tempt_x - min_seedx) * (tempt_x - min_seedx) + (tempt_y - min_seedy) * (tempt_y - min_seedy) + (tempt_z - min_seedz) *
-                (tempt_z - min_seedz);
-
-            if (dist < dt)
-            {
-                UpdateHeap(tempt_x, tempt_y, tempt_z, dist);
-            }
-        }
-        else
-        {
-            dist = (tempt_x - min_seedx) * (tempt_x - min_seedx) + (tempt_y - min_seedy) * (tempt_y - min_seedy) + (tempt_z - min_seedz) *
-                (tempt_z - min_seedz);
-            seed  = min_heap->seed[neighbor];
-            seedx = AllSeeds[seed].seedx;
-            seedy = AllSeeds[seed].seedy;
-            seedz = AllSeeds[seed].seedz;
-
-            if (dist < (tempt_x - seedx) * (tempt_x - seedx) + (tempt_y - seedy) * (tempt_y - seedy) + (tempt_z - seedz) * (tempt_z - seedz))
-            {
-                UpdateHeap(tempt_x, tempt_y, tempt_z, MaxDist);
-            }
-        }
+  } else if (heap_pointer[IndexVect1(tempt_x, tempt_y, tempt_z)] > -1) {
+    neighbor = heap_pointer[IndexVect1(tempt_x, tempt_y, tempt_z)];
+    dt = min_heap->dist[neighbor];
+
+    if (dt < MaxDist) {
+      dist = (tempt_x - min_seedx) * (tempt_x - min_seedx) +
+             (tempt_y - min_seedy) * (tempt_y - min_seedy) +
+             (tempt_z - min_seedz) * (tempt_z - min_seedz);
+
+      if (dist < dt) {
+        UpdateHeap(tempt_x, tempt_y, tempt_z, dist);
+      }
+    } else {
+      dist = (tempt_x - min_seedx) * (tempt_x - min_seedx) +
+             (tempt_y - min_seedy) * (tempt_y - min_seedy) +
+             (tempt_z - min_seedz) * (tempt_z - min_seedz);
+      seed = min_heap->seed[neighbor];
+      seedx = AllSeeds[seed].seedx;
+      seedy = AllSeeds[seed].seedy;
+      seedz = AllSeeds[seed].seedz;
+
+      if (dist < (tempt_x - seedx) * (tempt_x - seedx) +
+                     (tempt_y - seedy) * (tempt_y - seedy) +
+                     (tempt_z - seedz) * (tempt_z - seedz)) {
+        UpdateHeap(tempt_x, tempt_y, tempt_z, MaxDist);
+      }
     }
+  }
 
-    // else if (heap_pointer[IndexVect1(tempt_x,tempt_y,tempt_z)] > -11) {
-
+  // else if (heap_pointer[IndexVect1(tempt_x,tempt_y,tempt_z)] > -11) {
 
-    /* ============================ */
-    tempt_x = std::min(min_x + 1, xdim1 - 1);
-    tempt_y = min_y;
-    tempt_z = min_z;
+  /* ============================ */
+  tempt_x = std::min(min_x + 1, xdim1 - 1);
+  tempt_y = min_y;
+  tempt_z = min_z;
 
-    if (heap_pointer[IndexVect1(tempt_x, tempt_y, tempt_z)] == MaxVal)
-    {
-        dist = (tempt_x - min_seedx) * (tempt_x - min_seedx) + (tempt_y - min_seedy) * (tempt_y - min_seedy) + (tempt_z - min_seedz) * (tempt_z - min_seedz);
+  if (heap_pointer[IndexVect1(tempt_x, tempt_y, tempt_z)] == MaxVal) {
+    dist = (tempt_x - min_seedx) * (tempt_x - min_seedx) +
+           (tempt_y - min_seedy) * (tempt_y - min_seedy) +
+           (tempt_z - min_seedz) * (tempt_z - min_seedz);
 
-        if (dist <= threshold)
-        {
-            InsertHeap(tempt_x, tempt_y, tempt_z, dist);
-        }
-        else
-        {
-            boundary = 1;
-        }
+    if (dist <= threshold) {
+      InsertHeap(tempt_x, tempt_y, tempt_z, dist);
+    } else {
+      boundary = 1;
     }
-    else if (heap_pointer[IndexVect1(tempt_x, tempt_y, tempt_z)] > -1)
-    {
-        neighbor = heap_pointer[IndexVect1(tempt_x, tempt_y, tempt_z)];
-        dt = min_heap->dist[neighbor];
-
-        if (dt < MaxDist)
-        {
-            dist = (tempt_x - min_seedx) * (tempt_x - min_seedx) + (tempt_y - min_seedy) * (tempt_y - min_seedy) + (tempt_z - min_seedz) *
-                (tempt_z - min_seedz);
-
-            if (dist < dt)
-            {
-                UpdateHeap(tempt_x, tempt_y, tempt_z, dist);
-            }
-        }
-        else
-        {
-            dist = (tempt_x - min_seedx) * (tempt_x - min_seedx) + (tempt_y - min_seedy) * (tempt_y - min_seedy) + (tempt_z - min_seedz) *
-                (tempt_z - min_seedz);
-            seed  = min_heap->seed[neighbor];
-            seedx = AllSeeds[seed].seedx;
-            seedy = AllSeeds[seed].seedy;
-            seedz = AllSeeds[seed].seedz;
-
-            if (dist < (tempt_x - seedx) * (tempt_x - seedx) + (tempt_y - seedy) * (tempt_y - seedy) + (tempt_z - seedz) * (tempt_z - seedz))
-            {
-                UpdateHeap(tempt_x, tempt_y, tempt_z, MaxDist);
-            }
-        }
+  } else if (heap_pointer[IndexVect1(tempt_x, tempt_y, tempt_z)] > -1) {
+    neighbor = heap_pointer[IndexVect1(tempt_x, tempt_y, tempt_z)];
+    dt = min_heap->dist[neighbor];
+
+    if (dt < MaxDist) {
+      dist = (tempt_x - min_seedx) * (tempt_x - min_seedx) +
+             (tempt_y - min_seedy) * (tempt_y - min_seedy) +
+             (tempt_z - min_seedz) * (tempt_z - min_seedz);
+
+      if (dist < dt) {
+        UpdateHeap(tempt_x, tempt_y, tempt_z, dist);
+      }
+    } else {
+      dist = (tempt_x - min_seedx) * (tempt_x - min_seedx) +
+             (tempt_y - min_seedy) * (tempt_y - min_seedy) +
+             (tempt_z - min_seedz) * (tempt_z - min_seedz);
+      seed = min_heap->seed[neighbor];
+      seedx = AllSeeds[seed].seedx;
+      seedy = AllSeeds[seed].seedy;
+      seedz = AllSeeds[seed].seedz;
+
+      if (dist < (tempt_x - seedx) * (tempt_x - seedx) +
+                     (tempt_y - seedy) * (tempt_y - seedy) +
+                     (tempt_z - seedz) * (tempt_z - seedz)) {
+        UpdateHeap(tempt_x, tempt_y, tempt_z, MaxDist);
+      }
     }
-
-
-    /* ============================ */
-    tempt_y = std::max(min_y - 1, 0);
-    tempt_x = min_x;
-    tempt_z = min_z;
-
-    if (heap_pointer[IndexVect1(tempt_x, tempt_y, tempt_z)] == MaxVal)
-    {
-        dist = (tempt_x - min_seedx) * (tempt_x - min_seedx) + (tempt_y - min_seedy) * (tempt_y - min_seedy) + (tempt_z - min_seedz) * (tempt_z - min_seedz);
-
-        if (dist <= threshold)
-        {
-            InsertHeap(tempt_x, tempt_y, tempt_z, dist);
-        }
-        else
-        {
-            boundary = 1;
-        }
+  }
+
+  /* ============================ */
+  tempt_y = std::max(min_y - 1, 0);
+  tempt_x = min_x;
+  tempt_z = min_z;
+
+  if (heap_pointer[IndexVect1(tempt_x, tempt_y, tempt_z)] == MaxVal) {
+    dist = (tempt_x - min_seedx) * (tempt_x - min_seedx) +
+           (tempt_y - min_seedy) * (tempt_y - min_seedy) +
+           (tempt_z - min_seedz) * (tempt_z - min_seedz);
+
+    if (dist <= threshold) {
+      InsertHeap(tempt_x, tempt_y, tempt_z, dist);
+    } else {
+      boundary = 1;
     }
-    else if (heap_pointer[IndexVect1(tempt_x, tempt_y, tempt_z)] > -1)
-    {
-        neighbor = heap_pointer[IndexVect1(tempt_x, tempt_y, tempt_z)];
-        dt = min_heap->dist[neighbor];
-
-        if (dt < MaxDist)
-        {
-            dist = (tempt_x - min_seedx) * (tempt_x - min_seedx) + (tempt_y - min_seedy) * (tempt_y - min_seedy) + (tempt_z - min_seedz) *
-                (tempt_z - min_seedz);
-
-            if (dist < dt)
-            {
-                UpdateHeap(tempt_x, tempt_y, tempt_z, dist);
-            }
-        }
-        else
-        {
-            dist = (tempt_x - min_seedx) * (tempt_x - min_seedx) + (tempt_y - min_seedy) * (tempt_y - min_seedy) + (tempt_z - min_seedz) *
-                (tempt_z - min_seedz);
-            seed  = min_heap->seed[neighbor];
-            seedx = AllSeeds[seed].seedx;
-            seedy = AllSeeds[seed].seedy;
-            seedz = AllSeeds[seed].seedz;
-
-            if (dist < (tempt_x - seedx) * (tempt_x - seedx) + (tempt_y - seedy) * (tempt_y - seedy) + (tempt_z - seedz) * (tempt_z - seedz))
-            {
-                UpdateHeap(tempt_x, tempt_y, tempt_z, MaxDist);
-            }
-        }
+  } else if (heap_pointer[IndexVect1(tempt_x, tempt_y, tempt_z)] > -1) {
+    neighbor = heap_pointer[IndexVect1(tempt_x, tempt_y, tempt_z)];
+    dt = min_heap->dist[neighbor];
+
+    if (dt < MaxDist) {
+      dist = (tempt_x - min_seedx) * (tempt_x - min_seedx) +
+             (tempt_y - min_seedy) * (tempt_y - min_seedy) +
+             (tempt_z - min_seedz) * (tempt_z - min_seedz);
+
+      if (dist < dt) {
+        UpdateHeap(tempt_x, tempt_y, tempt_z, dist);
+      }
+    } else {
+      dist = (tempt_x - min_seedx) * (tempt_x - min_seedx) +
+             (tempt_y - min_seedy) * (tempt_y - min_seedy) +
+             (tempt_z - min_seedz) * (tempt_z - min_seedz);
+      seed = min_heap->seed[neighbor];
+      seedx = AllSeeds[seed].seedx;
+      seedy = AllSeeds[seed].seedy;
+      seedz = AllSeeds[seed].seedz;
+
+      if (dist < (tempt_x - seedx) * (tempt_x - seedx) +
+                     (tempt_y - seedy) * (tempt_y - seedy) +
+                     (tempt_z - seedz) * (tempt_z - seedz)) {
+        UpdateHeap(tempt_x, tempt_y, tempt_z, MaxDist);
+      }
     }
-
-
-    /* ============================ */
-    tempt_y = std::min(min_y + 1, ydim1 - 1);
-    tempt_x = min_x;
-    tempt_z = min_z;
-
-    if (heap_pointer[IndexVect1(tempt_x, tempt_y, tempt_z)] == MaxVal)
-    {
-        dist = (tempt_x - min_seedx) * (tempt_x - min_seedx) + (tempt_y - min_seedy) * (tempt_y - min_seedy) + (tempt_z - min_seedz) * (tempt_z - min_seedz);
-
-        if (dist <= threshold)
-        {
-            InsertHeap(tempt_x, tempt_y, tempt_z, dist);
-        }
-        else
-        {
-            boundary = 1;
-        }
+  }
+
+  /* ============================ */
+  tempt_y = std::min(min_y + 1, ydim1 - 1);
+  tempt_x = min_x;
+  tempt_z = min_z;
+
+  if (heap_pointer[IndexVect1(tempt_x, tempt_y, tempt_z)] == MaxVal) {
+    dist = (tempt_x - min_seedx) * (tempt_x - min_seedx) +
+           (tempt_y - min_seedy) * (tempt_y - min_seedy) +
+           (tempt_z - min_seedz) * (tempt_z - min_seedz);
+
+    if (dist <= threshold) {
+      InsertHeap(tempt_x, tempt_y, tempt_z, dist);
+    } else {
+      boundary = 1;
     }
-    else if (heap_pointer[IndexVect1(tempt_x, tempt_y, tempt_z)] > -1)
-    {
-        neighbor = heap_pointer[IndexVect1(tempt_x, tempt_y, tempt_z)];
-        dt = min_heap->dist[neighbor];
-
-        if (dt < MaxDist)
-        {
-            dist = (tempt_x - min_seedx) * (tempt_x - min_seedx) + (tempt_y - min_seedy) * (tempt_y - min_seedy) + (tempt_z - min_seedz) *
-                (tempt_z - min_seedz);
-
-            if (dist < dt)
-            {
-                UpdateHeap(tempt_x, tempt_y, tempt_z, dist);
-            }
-        }
-        else
-        {
-            dist = (tempt_x - min_seedx) * (tempt_x - min_seedx) + (tempt_y - min_seedy) * (tempt_y - min_seedy) + (tempt_z - min_seedz) *
-                (tempt_z - min_seedz);
-            seed  = min_heap->seed[neighbor];
-            seedx = AllSeeds[seed].seedx;
-            seedy = AllSeeds[seed].seedy;
-            seedz = AllSeeds[seed].seedz;
-
-            if (dist < (tempt_x - seedx) * (tempt_x - seedx) + (tempt_y - seedy) * (tempt_y - seedy) + (tempt_z - seedz) * (tempt_z - seedz))
-            {
-                UpdateHeap(tempt_x, tempt_y, tempt_z, MaxDist);
-            }
-        }
+  } else if (heap_pointer[IndexVect1(tempt_x, tempt_y, tempt_z)] > -1) {
+    neighbor = heap_pointer[IndexVect1(tempt_x, tempt_y, tempt_z)];
+    dt = min_heap->dist[neighbor];
+
+    if (dt < MaxDist) {
+      dist = (tempt_x - min_seedx) * (tempt_x - min_seedx) +
+             (tempt_y - min_seedy) * (tempt_y - min_seedy) +
+             (tempt_z - min_seedz) * (tempt_z - min_seedz);
+
+      if (dist < dt) {
+        UpdateHeap(tempt_x, tempt_y, tempt_z, dist);
+      }
+    } else {
+      dist = (tempt_x - min_seedx) * (tempt_x - min_seedx) +
+             (tempt_y - min_seedy) * (tempt_y - min_seedy) +
+             (tempt_z - min_seedz) * (tempt_z - min_seedz);
+      seed = min_heap->seed[neighbor];
+      seedx = AllSeeds[seed].seedx;
+      seedy = AllSeeds[seed].seedy;
+      seedz = AllSeeds[seed].seedz;
+
+      if (dist < (tempt_x - seedx) * (tempt_x - seedx) +
+                     (tempt_y - seedy) * (tempt_y - seedy) +
+                     (tempt_z - seedz) * (tempt_z - seedz)) {
+        UpdateHeap(tempt_x, tempt_y, tempt_z, MaxDist);
+      }
     }
-
-
-    /* ============================ */
-    tempt_z = std::max(min_z - 1, 0);
-    tempt_x = min_x;
-    tempt_y = min_y;
-
-    if (heap_pointer[IndexVect1(tempt_x, tempt_y, tempt_z)] == MaxVal)
-    {
-        dist = (tempt_x - min_seedx) * (tempt_x - min_seedx) + (tempt_y - min_seedy) * (tempt_y - min_seedy) + (tempt_z - min_seedz) * (tempt_z - min_seedz);
-
-        if (dist <= threshold)
-        {
-            InsertHeap(tempt_x, tempt_y, tempt_z, dist);
-        }
-        else
-        {
-            boundary = 1;
-        }
+  }
+
+  /* ============================ */
+  tempt_z = std::max(min_z - 1, 0);
+  tempt_x = min_x;
+  tempt_y = min_y;
+
+  if (heap_pointer[IndexVect1(tempt_x, tempt_y, tempt_z)] == MaxVal) {
+    dist = (tempt_x - min_seedx) * (tempt_x - min_seedx) +
+           (tempt_y - min_seedy) * (tempt_y - min_seedy) +
+           (tempt_z - min_seedz) * (tempt_z - min_seedz);
+
+    if (dist <= threshold) {
+      InsertHeap(tempt_x, tempt_y, tempt_z, dist);
+    } else {
+      boundary = 1;
     }
-    else if (heap_pointer[IndexVect1(tempt_x, tempt_y, tempt_z)] > -1)
-    {
-        neighbor = heap_pointer[IndexVect1(tempt_x, tempt_y, tempt_z)];
-        dt = min_heap->dist[neighbor];
-
-        if (dt < MaxDist)
-        {
-            dist = (tempt_x - min_seedx) * (tempt_x - min_seedx) + (tempt_y - min_seedy) * (tempt_y - min_seedy) + (tempt_z - min_seedz) *
-                (tempt_z - min_seedz);
-
-            if (dist < dt)
-            {
-                UpdateHeap(tempt_x, tempt_y, tempt_z, dist);
-            }
-        }
-        else
-        {
-            dist = (tempt_x - min_seedx) * (tempt_x - min_seedx) + (tempt_y - min_seedy) * (tempt_y - min_seedy) + (tempt_z - min_seedz) *
-                (tempt_z - min_seedz);
-            seed  = min_heap->seed[neighbor];
-            seedx = AllSeeds[seed].seedx;
-            seedy = AllSeeds[seed].seedy;
-            seedz = AllSeeds[seed].seedz;
-
-            if (dist < (tempt_x - seedx) * (tempt_x - seedx) + (tempt_y - seedy) * (tempt_y - seedy) + (tempt_z - seedz) * (tempt_z - seedz))
-            {
-                UpdateHeap(tempt_x, tempt_y, tempt_z, MaxDist);
-            }
-        }
+  } else if (heap_pointer[IndexVect1(tempt_x, tempt_y, tempt_z)] > -1) {
+    neighbor = heap_pointer[IndexVect1(tempt_x, tempt_y, tempt_z)];
+    dt = min_heap->dist[neighbor];
+
+    if (dt < MaxDist) {
+      dist = (tempt_x - min_seedx) * (tempt_x - min_seedx) +
+             (tempt_y - min_seedy) * (tempt_y - min_seedy) +
+             (tempt_z - min_seedz) * (tempt_z - min_seedz);
+
+      if (dist < dt) {
+        UpdateHeap(tempt_x, tempt_y, tempt_z, dist);
+      }
+    } else {
+      dist = (tempt_x - min_seedx) * (tempt_x - min_seedx) +
+             (tempt_y - min_seedy) * (tempt_y - min_seedy) +
+             (tempt_z - min_seedz) * (tempt_z - min_seedz);
+      seed = min_heap->seed[neighbor];
+      seedx = AllSeeds[seed].seedx;
+      seedy = AllSeeds[seed].seedy;
+      seedz = AllSeeds[seed].seedz;
+
+      if (dist < (tempt_x - seedx) * (tempt_x - seedx) +
+                     (tempt_y - seedy) * (tempt_y - seedy) +
+                     (tempt_z - seedz) * (tempt_z - seedz)) {
+        UpdateHeap(tempt_x, tempt_y, tempt_z, MaxDist);
+      }
     }
-
-
-    /* ============================ */
-    tempt_z = std::min(min_z + 1, zdim1 - 1);
-    tempt_x = min_x;
-    tempt_y = min_y;
-
-    if (heap_pointer[IndexVect1(tempt_x, tempt_y, tempt_z)] == MaxVal)
-    {
-        dist = (tempt_x - min_seedx) * (tempt_x - min_seedx) + (tempt_y - min_seedy) * (tempt_y - min_seedy) + (tempt_z - min_seedz) * (tempt_z - min_seedz);
-
-        if (dist <= threshold)
-        {
-            InsertHeap(tempt_x, tempt_y, tempt_z, dist);
-        }
-        else
-        {
-            boundary = 1;
-        }
+  }
+
+  /* ============================ */
+  tempt_z = std::min(min_z + 1, zdim1 - 1);
+  tempt_x = min_x;
+  tempt_y = min_y;
+
+  if (heap_pointer[IndexVect1(tempt_x, tempt_y, tempt_z)] == MaxVal) {
+    dist = (tempt_x - min_seedx) * (tempt_x - min_seedx) +
+           (tempt_y - min_seedy) * (tempt_y - min_seedy) +
+           (tempt_z - min_seedz) * (tempt_z - min_seedz);
+
+    if (dist <= threshold) {
+      InsertHeap(tempt_x, tempt_y, tempt_z, dist);
+    } else {
+      boundary = 1;
     }
-    else if (heap_pointer[IndexVect1(tempt_x, tempt_y, tempt_z)] > -1)
-    {
-        neighbor = heap_pointer[IndexVect1(tempt_x, tempt_y, tempt_z)];
-        dt = min_heap->dist[neighbor];
-
-        if (dt < MaxDist)
-        {
-            dist = (tempt_x - min_seedx) * (tempt_x - min_seedx) + (tempt_y - min_seedy) * (tempt_y - min_seedy) + (tempt_z - min_seedz) *
-                (tempt_z - min_seedz);
-
-            if (dist < dt)
-            {
-                UpdateHeap(tempt_x, tempt_y, tempt_z, dist);
-            }
-        }
-        else
-        {
-            dist = (tempt_x - min_seedx) * (tempt_x - min_seedx) + (tempt_y - min_seedy) * (tempt_y - min_seedy) + (tempt_z - min_seedz) *
-                (tempt_z - min_seedz);
-            seed  = min_heap->seed[neighbor];
-            seedx = AllSeeds[seed].seedx;
-            seedy = AllSeeds[seed].seedy;
-            seedz = AllSeeds[seed].seedz;
-
-            if (dist < (tempt_x - seedx) * (tempt_x - seedx) + (tempt_y - seedy) * (tempt_y - seedy) + (tempt_z - seedz) * (tempt_z - seedz))
-            {
-                UpdateHeap(tempt_x, tempt_y, tempt_z, MaxDist);
-            }
-        }
+  } else if (heap_pointer[IndexVect1(tempt_x, tempt_y, tempt_z)] > -1) {
+    neighbor = heap_pointer[IndexVect1(tempt_x, tempt_y, tempt_z)];
+    dt = min_heap->dist[neighbor];
+
+    if (dt < MaxDist) {
+      dist = (tempt_x - min_seedx) * (tempt_x - min_seedx) +
+             (tempt_y - min_seedy) * (tempt_y - min_seedy) +
+             (tempt_z - min_seedz) * (tempt_z - min_seedz);
+
+      if (dist < dt) {
+        UpdateHeap(tempt_x, tempt_y, tempt_z, dist);
+      }
+    } else {
+      dist = (tempt_x - min_seedx) * (tempt_x - min_seedx) +
+             (tempt_y - min_seedy) * (tempt_y - min_seedy) +
+             (tempt_z - min_seedz) * (tempt_z - min_seedz);
+      seed = min_heap->seed[neighbor];
+      seedx = AllSeeds[seed].seedx;
+      seedy = AllSeeds[seed].seedy;
+      seedz = AllSeeds[seed].seedz;
+
+      if (dist < (tempt_x - seedx) * (tempt_x - seedx) +
+                     (tempt_y - seedy) * (tempt_y - seedy) +
+                     (tempt_z - seedz) * (tempt_z - seedz)) {
+        UpdateHeap(tempt_x, tempt_y, tempt_z, MaxDist);
+      }
     }
+  }
 
-
-    if (boundary)
-    {
-        InsertHeap(min_x, min_y, min_z, MaxDist);
-    }
+  if (boundary) {
+    InsertHeap(min_x, min_y, min_z, MaxDist);
+  }
 }
 
-FLTVECT FindSeed(float x, float y, float z, int index)
-{
-    double  cx1, cy1, cz1;
-    double  cx2, cy2, cz2;
-    double  cx3, cy3, cz3;
-    double  dist, radius1, radius2, radius3;
-    double  cos_alpha;
-    double  ax, ay, az;
-    double  bx, by, bz;
-    double  dx, dy, dz;
-    double  hx, hy, hz;
-    int     atom1, atom2, atom3;
-    int     num, total;
-    FLTVECT tmp;
-
-
-    // AllSeeds[index].atom[0] is always >= 0
-    atom1 = AllSeeds[index].atom[0];
-    atom2 = AllSeeds[index].atom[1];
-    atom3 = AllSeeds[index].atom[2];
-
-    // contain only one atom
-    if (atom2 < 0)
-    {
-        radius1 = atom_list[atom1].radius;
-        cx1  = atom_list[atom1].x;
-        cy1  = atom_list[atom1].y;
-        cz1  = atom_list[atom1].z;
-        dx   = x - cx1;
-        dy   = y - cy1;
-        dz   = z - cz1;
-        dist = sqrt(dx * dx + dy * dy + dz * dz);
-
-        tmp.x = cx1 + radius1 * dx / dist;
-        tmp.y = cy1 + radius1 * dy / dist;
-        tmp.z = cz1 + radius1 * dz / dist;
-    }
-
-    // contain only two atoms
-    else if (atom3 < 0)
-    {
-        radius1 = atom_list[atom1].radius;
-        cx1 = atom_list[atom1].x;
-        cy1 = atom_list[atom1].y;
-        cz1 = atom_list[atom1].z;
-        radius2 = atom_list[atom2].radius;
-        cx2  = atom_list[atom2].x;
-        cy2  = atom_list[atom2].y;
-        cz2  = atom_list[atom2].z;
-        dist = sqrt((cx2 - cx1) * (cx2 - cx1) + (cy2 - cy1) * (cy2 - cy1) + (cz2 - cz1) * (cz2 - cz1));
-        cos_alpha = (radius1 * radius1 + dist * dist - radius2 * radius2) / (2.0 * radius1 * dist);
-
-        ax   = (cx2 - cx1) / dist;
-        ay   = (cy2 - cy1) / dist;
-        az   = (cz2 - cz1) / dist;
-        bx   = x - cx1;
-        by   = y - cy1;
-        bz   = z - cz1;
-        dx   = ay * bz - az * by;
-        dy   = az * bx - ax * bz;
-        dz   = ax * by - ay * bx;
-        hx   = dy * az - dz * ay;
-        hy   = dz * ax - dx * az;
-        hz   = dx * ay - dy * ax;
-        dist = sqrt(hx * hx + hy * hy + hz * hz);
-        hx  /= dist;
-        hy  /= dist;
-        hz  /= dist;
-        dist = radius1 * sqrt(1.0 - cos_alpha * cos_alpha);
-
-        radius1 *= cos_alpha;
-        tmp.x    = radius1 * ax + cx1 + dist * hx;
-        tmp.y    = radius1 * ay + cy1 + dist * hy;
-        tmp.z    = radius1 * az + cz1 + dist * hz;
-    }
-
-    // contain three or more atoms
-    else
-    {
-        hx = 0;
-        hy = 0;
-        hz = 0;
-        total = 0;
-        num   = 2;
-
-        while (num < MaxAtom)
-        {
-            if (AllSeeds[index].atom[num] < 0)
-            {
-                break;
-            }
-            atom1 = AllSeeds[index].atom[num - 2];
-            atom2 = AllSeeds[index].atom[num - 1];
-            atom3 = AllSeeds[index].atom[num];
-
-            radius1 = atom_list[atom1].radius;
-            cx1 = atom_list[atom1].x;
-            cy1 = atom_list[atom1].y;
-            cz1 = atom_list[atom1].z;
-            radius2 = atom_list[atom2].radius;
-            cx2 = atom_list[atom2].x;
-            cy2 = atom_list[atom2].y;
-            cz2 = atom_list[atom2].z;
-            radius3 = atom_list[atom3].radius;
-            cx3 = atom_list[atom3].x;
-            cy3 = atom_list[atom3].y;
-            cz3 = atom_list[atom3].z;
-
-            ax = x;
-            ay = y;
-            az = z;
-            bx = ax;
-            by = ay;
-            bz = az;
-
-            while (1)
-            {
-                // projection to the first atom sphere
-                dx   = ax - cx1;
-                dy   = ay - cy1;
-                dz   = az - cz1;
-                dist = radius1 / sqrt(dx * dx + dy * dy + dz * dz);
-                ax   = cx1 + dist * dx;
-                ay   = cy1 + dist * dy;
-                az   = cz1 + dist * dz;
-
-                // projection to the second atom sphere
-                dx   = ax - cx2;
-                dy   = ay - cy2;
-                dz   = az - cz2;
-                dist = radius2 / sqrt(dx * dx + dy * dy + dz * dz);
-                ax   = cx2 + dist * dx;
-                ay   = cy2 + dist * dy;
-                az   = cz2 + dist * dz;
-
-                // projection to the third atom sphere
-                dx   = ax - cx3;
-                dy   = ay - cy3;
-                dz   = az - cz3;
-                dist = radius3 / sqrt(dx * dx + dy * dy + dz * dz);
-                ax   = cx3 + dist * dx;
-                ay   = cy3 + dist * dy;
-                az   = cz3 + dist * dz;
-
-                dist = sqrt((ax - bx) * (ax - bx) + (ay - by) * (ay - by) + (az - bz) * (az - bz));
-
-                if (dist < 0.001)
-                {
-                    hx += ax;
-                    hy += ay;
-                    hz += az;
-                    total++;
-                    break;
-                }
-                else
-                {
-                    bx = ax;
-                    by = ay;
-                    bz = az;
-                }
-            }
-            num++;
-        }
-        tmp.x = hx / (float)total;
-        tmp.y = hy / (float)total;
-        tmp.z = hz / (float)total;
+FLTVECT FindSeed(float x, float y, float z, int index) {
+  double cx1, cy1, cz1;
+  double cx2, cy2, cz2;
+  double cx3, cy3, cz3;
+  double dist, radius1, radius2, radius3;
+  double cos_alpha;
+  double ax, ay, az;
+  double bx, by, bz;
+  double dx, dy, dz;
+  double hx, hy, hz;
+  int atom1, atom2, atom3;
+  int num, total;
+  FLTVECT tmp;
+
+  // AllSeeds[index].atom[0] is always >= 0
+  atom1 = AllSeeds[index].atom[0];
+  atom2 = AllSeeds[index].atom[1];
+  atom3 = AllSeeds[index].atom[2];
+
+  // contain only one atom
+  if (atom2 < 0) {
+    radius1 = atom_list[atom1].radius;
+    cx1 = atom_list[atom1].x;
+    cy1 = atom_list[atom1].y;
+    cz1 = atom_list[atom1].z;
+    dx = x - cx1;
+    dy = y - cy1;
+    dz = z - cz1;
+    dist = sqrt(dx * dx + dy * dy + dz * dz);
+
+    tmp.x = cx1 + radius1 * dx / dist;
+    tmp.y = cy1 + radius1 * dy / dist;
+    tmp.z = cz1 + radius1 * dz / dist;
+  }
+
+  // contain only two atoms
+  else if (atom3 < 0) {
+    radius1 = atom_list[atom1].radius;
+    cx1 = atom_list[atom1].x;
+    cy1 = atom_list[atom1].y;
+    cz1 = atom_list[atom1].z;
+    radius2 = atom_list[atom2].radius;
+    cx2 = atom_list[atom2].x;
+    cy2 = atom_list[atom2].y;
+    cz2 = atom_list[atom2].z;
+    dist = sqrt((cx2 - cx1) * (cx2 - cx1) + (cy2 - cy1) * (cy2 - cy1) +
+                (cz2 - cz1) * (cz2 - cz1));
+    cos_alpha = (radius1 * radius1 + dist * dist - radius2 * radius2) /
+                (2.0 * radius1 * dist);
+
+    ax = (cx2 - cx1) / dist;
+    ay = (cy2 - cy1) / dist;
+    az = (cz2 - cz1) / dist;
+    bx = x - cx1;
+    by = y - cy1;
+    bz = z - cz1;
+    dx = ay * bz - az * by;
+    dy = az * bx - ax * bz;
+    dz = ax * by - ay * bx;
+    hx = dy * az - dz * ay;
+    hy = dz * ax - dx * az;
+    hz = dx * ay - dy * ax;
+    dist = sqrt(hx * hx + hy * hy + hz * hz);
+    hx /= dist;
+    hy /= dist;
+    hz /= dist;
+    dist = radius1 * sqrt(1.0 - cos_alpha * cos_alpha);
+
+    radius1 *= cos_alpha;
+    tmp.x = radius1 * ax + cx1 + dist * hx;
+    tmp.y = radius1 * ay + cy1 + dist * hy;
+    tmp.z = radius1 * az + cz1 + dist * hz;
+  }
+
+  // contain three or more atoms
+  else {
+    hx = 0;
+    hy = 0;
+    hz = 0;
+    total = 0;
+    num = 2;
+
+    while (num < MaxAtom) {
+      if (AllSeeds[index].atom[num] < 0) {
+        break;
+      }
+      atom1 = AllSeeds[index].atom[num - 2];
+      atom2 = AllSeeds[index].atom[num - 1];
+      atom3 = AllSeeds[index].atom[num];
+
+      radius1 = atom_list[atom1].radius;
+      cx1 = atom_list[atom1].x;
+      cy1 = atom_list[atom1].y;
+      cz1 = atom_list[atom1].z;
+      radius2 = atom_list[atom2].radius;
+      cx2 = atom_list[atom2].x;
+      cy2 = atom_list[atom2].y;
+      cz2 = atom_list[atom2].z;
+      radius3 = atom_list[atom3].radius;
+      cx3 = atom_list[atom3].x;
+      cy3 = atom_list[atom3].y;
+      cz3 = atom_list[atom3].z;
+
+      ax = x;
+      ay = y;
+      az = z;
+      bx = ax;
+      by = ay;
+      bz = az;
+
+      while (1) {
+        // projection to the first atom sphere
+        dx = ax - cx1;
+        dy = ay - cy1;
+        dz = az - cz1;
+        dist = radius1 / sqrt(dx * dx + dy * dy + dz * dz);
+        ax = cx1 + dist * dx;
+        ay = cy1 + dist * dy;
+        az = cz1 + dist * dz;
+
+        // projection to the second atom sphere
+        dx = ax - cx2;
+        dy = ay - cy2;
+        dz = az - cz2;
+        dist = radius2 / sqrt(dx * dx + dy * dy + dz * dz);
+        ax = cx2 + dist * dx;
+        ay = cy2 + dist * dy;
+        az = cz2 + dist * dz;
+
+        // projection to the third atom sphere
+        dx = ax - cx3;
+        dy = ay - cy3;
+        dz = az - cz3;
+        dist = radius3 / sqrt(dx * dx + dy * dy + dz * dz);
+        ax = cx3 + dist * dx;
+        ay = cy3 + dist * dy;
+        az = cz3 + dist * dz;
+
+        dist = sqrt((ax - bx) * (ax - bx) + (ay - by) * (ay - by) +
+                    (az - bz) * (az - bz));
+
+        if (dist < 0.001) {
+          hx += ax;
+          hy += ay;
+          hz += az;
+          total++;
+          break;
+        } else {
+          bx = ax;
+          by = ay;
+          bz = az;
+        }
+      }
+      num++;
     }
+    tmp.x = hx / (float)total;
+    tmp.y = hy / (float)total;
+    tmp.z = hz / (float)total;
+  }
 
-    return tmp;
+  return tmp;
 }
 
 /*
@@ -987,1359 +871,1211 @@ FLTVECT FindSeed(float x, float y, float z, int index)
  * ***************************************************************************
  */
 template 
-void getMinMax(Iterator begin,
-               Iterator end,
-               float    min[3],
-               float    max[3])
-{
-    float maxRad = 0.0;
-    float tempRad;
+void getMinMax(Iterator begin, Iterator end, float min[3], float max[3]) {
+  float maxRad = 0.0;
+  float tempRad;
 
-    min[0] = min[1] = min[2] = std::numeric_limits::infinity();
-    max[0] = max[1] = max[2] = -std::numeric_limits::infinity();
-    maxRad = 0;
+  min[0] = min[1] = min[2] = std::numeric_limits::infinity();
+  max[0] = max[1] = max[2] = -std::numeric_limits::infinity();
+  maxRad = 0;
 
-    for (auto curr = begin; curr != end; ++curr)
-    {
-        float x = curr->x;
-        float y = curr->y;
-        float z = curr->z;
+  for (auto curr = begin; curr != end; ++curr) {
+    float x = curr->x;
+    float y = curr->y;
+    float z = curr->z;
 
-        min[0] = std::min(min[0], x);
-        max[0] = std::max(max[0], x);
+    min[0] = std::min(min[0], x);
+    max[0] = std::max(max[0], x);
 
-        min[1] = std::min(min[1], y);
-        max[1] = std::max(max[1], y);
+    min[1] = std::min(min[1], y);
+    max[1] = std::max(max[1], y);
 
-        min[2] = std::min(min[2], z);
-        max[2] = std::max(max[2], z);
+    min[2] = std::min(min[2], z);
+    max[2] = std::max(max[2], z);
 
-        // Definition of surface boundary
-        tempRad = curr->radius * sqrt(1.0 + log(pdbreader_detail::EPSILON) / BLOBBYNESS);
+    // Definition of surface boundary
+    tempRad =
+        curr->radius * sqrt(1.0 + log(pdbreader_detail::EPSILON) / BLOBBYNESS);
 
-        maxRad = std::max(maxRad, tempRad);
-    }
+    maxRad = std::max(maxRad, tempRad);
+  }
 
-    for (int i = 0; i < 3; i++)
-    {
-        min[i] -= maxRad;
-        max[i] += maxRad;
-    }
+  for (int i = 0; i < 3; i++) {
+    min[i] -= maxRad;
+    max[i] += maxRad;
+  }
 }
 
-std::unique_ptr readPDB_molsurf(const std::string &input_name)
-{
-    int               i, j, k;
-    int               a, b, c, d;
-    float             orig[3], span[3];
-    int               dim[3];
-    int               m, n, l, num;
-    double            threshold;
-    double            nx, ny, nz;
-    int               xydim, xyzdim;
-    clock_t           begin, finish;
-    std::vector atoms;
-    std::vector atom_list;
-    float             min[3], max[3];
-    SEEDS            *AllSeeds; // Border variable
-    MinHeapS         *min_heap;
-
-    // Read in the PDB file
-    readPDB(input_name, std::back_inserter(atoms));
-
-    for (auto atom : atoms)
-    {
-
-        ATOM new_atom;
-        new_atom.x = atom.pos[0];
-        new_atom.y = atom.pos[1];
-        new_atom.z = atom.pos[2];
-        new_atom.radius = atom.radius;
-
-        atom_list.push_back(new_atom);
-    }
-
-    getMinMax(atom_list.begin(), atom_list.end(), min, max);
-
-    GLOBAL_xdim = (int)(((max[0] - min[0]) + 1) * DIM_SCALE);
-    GLOBAL_ydim = (int)(((max[1] - min[1]) + 1) * DIM_SCALE);
-    GLOBAL_zdim = (int)(((max[2] - min[2]) + 1) * DIM_SCALE);
-    xydim  = GLOBAL_xdim * GLOBAL_ydim;
-    xyzdim = xydim * GLOBAL_zdim;
-
-    // printf("dimension: %d X %d X %d\n",xdim,ydim,zdim);
-
-    GLOBAL_atom_index = (int *)malloc(sizeof(int) * xyzdim);
-    GLOBAL_segment_index = (int *)malloc(sizeof(int) * xyzdim);
-
-    for (k = 0; k < xyzdim; k++)
-    {
-        GLOBAL_atom_index[k] = 0;
-    }
-
-    orig[0] = min[0];
-    orig[1] = min[1];
-    orig[2] = min[2];
-    span[0] = (max[0] - min[0]) / (double)(GLOBAL_xdim - 1);
-    span[1] = (max[1] - min[1]) / (double)(GLOBAL_ydim - 1);
-    span[2] = (max[2] - min[2]) / (double)(GLOBAL_zdim - 1);
-    dim[0]  = GLOBAL_xdim;
-    dim[1]  = GLOBAL_ydim;
-    dim[2]  = GLOBAL_zdim;
-
-    for (m = 0; m < atom_list.size(); m++)
-    {
-        atom_list[m].x = (atom_list[m].x - orig[0]) / span[0];
-        atom_list[m].y = (atom_list[m].y - orig[1]) / span[1];
-        atom_list[m].z = (atom_list[m].z - orig[2]) / span[2];
-        atom_list[m].radius = (atom_list[m].radius + 1.5) / ((span[0] + span[1] + span[2]) / 3.0);
-    }
-
-    begin  = clock();
-    num    = ExtractSAS(atom_list.size(), atom_list.data());
-    finish = clock();
-
-    // for(int q=0; q < GLOBAL_xdim; ++q){
-    //     for(int r=0; r < GLOBAL_ydim; ++r){
-    //         for(int s=0; s < GLOBAL_zdim; ++s){
-    //             std::cout << GLOBAL_atom_index[IndexVect(q,r,s)] <<
-    // std::endl;
-    //         }
-    //     }
-    // }
-
-    // printf("   Extract SAS voxels: CPU Time = %f seconds
-    // \n",(double)(finish-begin)/CLOCKS_PER_SEC);
-    // printf("   Number of boundary voxels: %d\n\n",num);
-
-
-    begin = clock();
-    threshold   = 1.5 / ((span[0] + span[1] + span[2]) / 3.0);
-    min_heap    = (MinHeapS *)malloc(sizeof(MinHeapS));
-    min_heap->x = (unsigned short *)malloc(sizeof(unsigned short) * num * 3);
-    min_heap->y = (unsigned short *)malloc(sizeof(unsigned short) * num * 3);
-    min_heap->z = (unsigned short *)malloc(sizeof(unsigned short) * num * 3);
-    min_heap->seed = (int *)malloc(sizeof(int) * num * 3);
-    min_heap->dist = (float *)malloc(sizeof(float) * num * 3);
-    AllSeeds = (SEEDS *)malloc(sizeof(SEEDS) * num);
-    ExtractSES(min_heap,
-               AllSeeds,
-               GLOBAL_segment_index,
-               GLOBAL_xdim,
-               GLOBAL_ydim,
-               GLOBAL_zdim,
-               GLOBAL_atom_index,
-               atom_list.size(),
-               atom_list.data(),
-               threshold * threshold);
-    finish = clock();
-
-    // printf("   Extract SES voxels: CPU Time = %f seconds
-    // \n\n",(double)(finish-begin)/CLOCKS_PER_SEC);
-
-
-    // detect and fix non-manifolds !
-    while (1)
-    {
-        b = 0;
-
-        int index = 0;
-
-        for (k = 0; k < GLOBAL_zdim; k++)
-        {
-            for (j = 0; j < GLOBAL_ydim; j++)
+std::unique_ptr readPDB_molsurf(const std::string &input_name) {
+  int i, j, k;
+  int a, b, c, d;
+  float orig[3], span[3];
+  int dim[3];
+  int m, n, l, num;
+  double threshold;
+  double nx, ny, nz;
+  int xydim, xyzdim;
+  clock_t begin, finish;
+  std::vector atoms;
+  std::vector atom_list;
+  float min[3], max[3];
+  SEEDS *AllSeeds; // Border variable
+  MinHeapS *min_heap;
+
+  // Read in the PDB file
+  readPDB(input_name, std::back_inserter(atoms));
+
+  for (auto atom : atoms) {
+
+    ATOM new_atom;
+    new_atom.x = atom.pos[0];
+    new_atom.y = atom.pos[1];
+    new_atom.z = atom.pos[2];
+    new_atom.radius = atom.radius;
+
+    atom_list.push_back(new_atom);
+  }
+
+  getMinMax(atom_list.begin(), atom_list.end(), min, max);
+
+  GLOBAL_xdim = (int)(((max[0] - min[0]) + 1) * DIM_SCALE);
+  GLOBAL_ydim = (int)(((max[1] - min[1]) + 1) * DIM_SCALE);
+  GLOBAL_zdim = (int)(((max[2] - min[2]) + 1) * DIM_SCALE);
+  xydim = GLOBAL_xdim * GLOBAL_ydim;
+  xyzdim = xydim * GLOBAL_zdim;
+
+  // printf("dimension: %d X %d X %d\n",xdim,ydim,zdim);
+
+  GLOBAL_atom_index = (int *)malloc(sizeof(int) * xyzdim);
+  GLOBAL_segment_index = (int *)malloc(sizeof(int) * xyzdim);
+
+  for (k = 0; k < xyzdim; k++) {
+    GLOBAL_atom_index[k] = 0;
+  }
+
+  orig[0] = min[0];
+  orig[1] = min[1];
+  orig[2] = min[2];
+  span[0] = (max[0] - min[0]) / (double)(GLOBAL_xdim - 1);
+  span[1] = (max[1] - min[1]) / (double)(GLOBAL_ydim - 1);
+  span[2] = (max[2] - min[2]) / (double)(GLOBAL_zdim - 1);
+  dim[0] = GLOBAL_xdim;
+  dim[1] = GLOBAL_ydim;
+  dim[2] = GLOBAL_zdim;
+
+  for (m = 0; m < atom_list.size(); m++) {
+    atom_list[m].x = (atom_list[m].x - orig[0]) / span[0];
+    atom_list[m].y = (atom_list[m].y - orig[1]) / span[1];
+    atom_list[m].z = (atom_list[m].z - orig[2]) / span[2];
+    atom_list[m].radius =
+        (atom_list[m].radius + 1.5) / ((span[0] + span[1] + span[2]) / 3.0);
+  }
+
+  begin = clock();
+  num = ExtractSAS(atom_list.size(), atom_list.data());
+  finish = clock();
+
+  // for(int q=0; q < GLOBAL_xdim; ++q){
+  //     for(int r=0; r < GLOBAL_ydim; ++r){
+  //         for(int s=0; s < GLOBAL_zdim; ++s){
+  //             std::cout << GLOBAL_atom_index[IndexVect(q,r,s)] <<
+  // std::endl;
+  //         }
+  //     }
+  // }
+
+  // printf("   Extract SAS voxels: CPU Time = %f seconds
+  // \n",(double)(finish-begin)/CLOCKS_PER_SEC);
+  // printf("   Number of boundary voxels: %d\n\n",num);
+
+  begin = clock();
+  threshold = 1.5 / ((span[0] + span[1] + span[2]) / 3.0);
+  min_heap = (MinHeapS *)malloc(sizeof(MinHeapS));
+  min_heap->x = (unsigned short *)malloc(sizeof(unsigned short) * num * 3);
+  min_heap->y = (unsigned short *)malloc(sizeof(unsigned short) * num * 3);
+  min_heap->z = (unsigned short *)malloc(sizeof(unsigned short) * num * 3);
+  min_heap->seed = (int *)malloc(sizeof(int) * num * 3);
+  min_heap->dist = (float *)malloc(sizeof(float) * num * 3);
+  AllSeeds = (SEEDS *)malloc(sizeof(SEEDS) * num);
+  ExtractSES(min_heap, AllSeeds, GLOBAL_segment_index, GLOBAL_xdim, GLOBAL_ydim,
+             GLOBAL_zdim, GLOBAL_atom_index, atom_list.size(), atom_list.data(),
+             threshold * threshold);
+  finish = clock();
+
+  // printf("   Extract SES voxels: CPU Time = %f seconds
+  // \n\n",(double)(finish-begin)/CLOCKS_PER_SEC);
+
+  // detect and fix non-manifolds !
+  while (1) {
+    b = 0;
+
+    int index = 0;
+
+    for (k = 0; k < GLOBAL_zdim; k++) {
+      for (j = 0; j < GLOBAL_ydim; j++) {
+        for (i = 0; i < GLOBAL_xdim; i++, ++index) {
+          if (GLOBAL_segment_index[index] == MaxVal) {
+            if (!CheckManifold(i, j, k)) // non-manifold occurs
             {
-                for (i = 0; i < GLOBAL_xdim; i++, ++index)
-                {
-                    if (GLOBAL_segment_index[index] == MaxVal)
-                    {
-                        if (!CheckManifold(i, j, k)) // non-manifold occurs
-                        {
-                            GLOBAL_segment_index[index] = 0;
-                            min_heap->x[min_heap->size] = i;
-                            min_heap->y[min_heap->size] = j;
-                            min_heap->z[min_heap->size] = k;
-                            min_heap->size++;
-                            b++;
-                        }
-                    }
-                }
+              GLOBAL_segment_index[index] = 0;
+              min_heap->x[min_heap->size] = i;
+              min_heap->y[min_heap->size] = j;
+              min_heap->z[min_heap->size] = k;
+              min_heap->size++;
+              b++;
             }
+          }
         }
-
-        // printf("non-manifold = %d \n",b);
-        if (b == 0)
-        {
-            break;
-        }
+      }
     }
 
-
-    // generate the surface mesh
-    GLOBAL_vertex = (MOL_VERTEX *)malloc(sizeof(MOL_VERTEX) * num * 8);
-    GLOBAL_quads  = (INT4VECT *)malloc(sizeof(INT4VECT) * num * 6);
-
-    for (k = 0; k < num * 8; k++)
-    {
-        GLOBAL_vertex[k].neigh = 0;
+    // printf("non-manifold = %d \n",b);
+    if (b == 0) {
+      break;
     }
-
-    for (k = 0; k < xyzdim; k++)
-    {
-        GLOBAL_atom_index[k] = -1;
+  }
+
+  // generate the surface mesh
+  GLOBAL_vertex = (MOL_VERTEX *)malloc(sizeof(MOL_VERTEX) * num * 8);
+  GLOBAL_quads = (INT4VECT *)malloc(sizeof(INT4VECT) * num * 6);
+
+  for (k = 0; k < num * 8; k++) {
+    GLOBAL_vertex[k].neigh = 0;
+  }
+
+  for (k = 0; k < xyzdim; k++) {
+    GLOBAL_atom_index[k] = -1;
+  }
+  begin = clock();
+  GLOBAL_vert_num = 0;
+  GLOBAL_quad_num = 0;
+
+  for (num = 0; num < min_heap->size; num++) {
+    i = min_heap->x[num];
+    j = min_heap->y[num];
+    k = min_heap->z[num];
+
+    // back face
+    if (GLOBAL_segment_index[IndexVect(i - 1, j, k)] == MaxVal) {
+      a = CheckFaceCorner(i - 0.5, j - 0.5, k - 0.5);
+      GLOBAL_vertex[a].neigh |= 40; // +y and +z
+      b = CheckFaceCorner(i - 0.5, j - 0.5, k + 0.5);
+      GLOBAL_vertex[b].neigh |= 24; // +y and -z
+      c = CheckFaceCorner(i - 0.5, j + 0.5, k + 0.5);
+      GLOBAL_vertex[c].neigh |= 20; // -y and -z
+      d = CheckFaceCorner(i - 0.5, j + 0.5, k - 0.5);
+      GLOBAL_vertex[d].neigh |= 36; // -y and +z
+
+      GLOBAL_quads[GLOBAL_quad_num].a = a;
+      GLOBAL_quads[GLOBAL_quad_num].b = b;
+      GLOBAL_quads[GLOBAL_quad_num].c = c;
+      GLOBAL_quads[GLOBAL_quad_num].d = d;
+      GLOBAL_quad_num++;
     }
-    begin = clock();
-    GLOBAL_vert_num = 0;
-    GLOBAL_quad_num = 0;
-
-    for (num = 0; num < min_heap->size; num++)
-    {
-        i = min_heap->x[num];
-        j = min_heap->y[num];
-        k = min_heap->z[num];
-
-        // back face
-        if (GLOBAL_segment_index[IndexVect(i - 1, j, k)] == MaxVal)
-        {
-            a = CheckFaceCorner(i - 0.5, j - 0.5, k - 0.5);
-            GLOBAL_vertex[a].neigh |= 40; // +y and +z
-            b = CheckFaceCorner(i - 0.5, j - 0.5, k + 0.5);
-            GLOBAL_vertex[b].neigh |= 24; // +y and -z
-            c = CheckFaceCorner(i - 0.5, j + 0.5, k + 0.5);
-            GLOBAL_vertex[c].neigh |= 20; // -y and -z
-            d = CheckFaceCorner(i - 0.5, j + 0.5, k - 0.5);
-            GLOBAL_vertex[d].neigh |= 36; // -y and +z
-
-            GLOBAL_quads[GLOBAL_quad_num].a = a;
-            GLOBAL_quads[GLOBAL_quad_num].b = b;
-            GLOBAL_quads[GLOBAL_quad_num].c = c;
-            GLOBAL_quads[GLOBAL_quad_num].d = d;
-            GLOBAL_quad_num++;
-        }
-
-        // front face
-        if (GLOBAL_segment_index[IndexVect(i + 1, j, k)] == MaxVal)
-        {
-            a = CheckFaceCorner(i + 0.5, j - 0.5, k - 0.5);
-            GLOBAL_vertex[a].neigh |= 40; // +y and +z
-            b = CheckFaceCorner(i + 0.5, j + 0.5, k - 0.5);
-            GLOBAL_vertex[b].neigh |= 36; // -y and +z
-            c = CheckFaceCorner(i + 0.5, j + 0.5, k + 0.5);
-            GLOBAL_vertex[c].neigh |= 20; // -y and -z
-            d = CheckFaceCorner(i + 0.5, j - 0.5, k + 0.5);
-            GLOBAL_vertex[d].neigh |= 24; // +y and -z
-
-            GLOBAL_quads[GLOBAL_quad_num].a = a;
-            GLOBAL_quads[GLOBAL_quad_num].b = b;
-            GLOBAL_quads[GLOBAL_quad_num].c = c;
-            GLOBAL_quads[GLOBAL_quad_num].d = d;
-            GLOBAL_quad_num++;
-        }
 
-        // left face
-        if (GLOBAL_segment_index[IndexVect(i, j - 1, k)] == MaxVal)
-        {
-            a = CheckFaceCorner(i + 0.5, j - 0.5, k - 0.5);
-            GLOBAL_vertex[a].neigh |= 33; // -x and +z
-            b = CheckFaceCorner(i + 0.5, j - 0.5, k + 0.5);
-            GLOBAL_vertex[b].neigh |= 17; // -x and -z
-            c = CheckFaceCorner(i - 0.5, j - 0.5, k + 0.5);
-            GLOBAL_vertex[c].neigh |= 18; // +x and -z
-            d = CheckFaceCorner(i - 0.5, j - 0.5, k - 0.5);
-            GLOBAL_vertex[d].neigh |= 34; // +x and +z
-
-            GLOBAL_quads[GLOBAL_quad_num].a = a;
-            GLOBAL_quads[GLOBAL_quad_num].b = b;
-            GLOBAL_quads[GLOBAL_quad_num].c = c;
-            GLOBAL_quads[GLOBAL_quad_num].d = d;
-            GLOBAL_quad_num++;
-        }
-
-        // right face
-        if (GLOBAL_segment_index[IndexVect(i, j + 1, k)] == MaxVal)
-        {
-            a = CheckFaceCorner(i + 0.5, j + 0.5, k - 0.5);
-            GLOBAL_vertex[a].neigh |= 33; // -x and +z
-            b = CheckFaceCorner(i - 0.5, j + 0.5, k - 0.5);
-            GLOBAL_vertex[b].neigh |= 34; // +x and +z
-            c = CheckFaceCorner(i - 0.5, j + 0.5, k + 0.5);
-            GLOBAL_vertex[c].neigh |= 18; // +x and -z
-            d = CheckFaceCorner(i + 0.5, j + 0.5, k + 0.5);
-            GLOBAL_vertex[d].neigh |= 17; // -x and -z
-
-            GLOBAL_quads[GLOBAL_quad_num].a = a;
-            GLOBAL_quads[GLOBAL_quad_num].b = b;
-            GLOBAL_quads[GLOBAL_quad_num].c = c;
-            GLOBAL_quads[GLOBAL_quad_num].d = d;
-            GLOBAL_quad_num++;
-        }
-
-        // bottom face
-        if (GLOBAL_segment_index[IndexVect(i, j, k - 1)] == MaxVal)
-        {
-            a = CheckFaceCorner(i + 0.5, j - 0.5, k - 0.5);
-            GLOBAL_vertex[a].neigh |= 9;  // -x and +y
-            b = CheckFaceCorner(i - 0.5, j - 0.5, k - 0.5);
-            GLOBAL_vertex[b].neigh |= 10; // +x and +y
-            c = CheckFaceCorner(i - 0.5, j + 0.5, k - 0.5);
-            GLOBAL_vertex[c].neigh |= 6;  // +x and -y
-            d = CheckFaceCorner(i + 0.5, j + 0.5, k - 0.5);
-            GLOBAL_vertex[d].neigh |= 5;  // -x and -y
-
-            GLOBAL_quads[GLOBAL_quad_num].a = a;
-            GLOBAL_quads[GLOBAL_quad_num].b = b;
-            GLOBAL_quads[GLOBAL_quad_num].c = c;
-            GLOBAL_quads[GLOBAL_quad_num].d = d;
-            GLOBAL_quad_num++;
-        }
-
-        // top face
-        if (GLOBAL_segment_index[IndexVect(i, j, k + 1)] == MaxVal)
-        {
-            a = CheckFaceCorner(i + 0.5, j - 0.5, k + 0.5);
-            GLOBAL_vertex[a].neigh |= 9;  // -x and +y
-            b = CheckFaceCorner(i + 0.5, j + 0.5, k + 0.5);
-            GLOBAL_vertex[b].neigh |= 5;  // -x and -y
-            c = CheckFaceCorner(i - 0.5, j + 0.5, k + 0.5);
-            GLOBAL_vertex[c].neigh |= 6;  // +x and -y
-            d = CheckFaceCorner(i - 0.5, j - 0.5, k + 0.5);
-            GLOBAL_vertex[d].neigh |= 10; // +x and +y
-
-            GLOBAL_quads[GLOBAL_quad_num].a = a;
-            GLOBAL_quads[GLOBAL_quad_num].b = b;
-            GLOBAL_quads[GLOBAL_quad_num].c = c;
-            GLOBAL_quads[GLOBAL_quad_num].d = d;
-            GLOBAL_quad_num++;
-        }
+    // front face
+    if (GLOBAL_segment_index[IndexVect(i + 1, j, k)] == MaxVal) {
+      a = CheckFaceCorner(i + 0.5, j - 0.5, k - 0.5);
+      GLOBAL_vertex[a].neigh |= 40; // +y and +z
+      b = CheckFaceCorner(i + 0.5, j + 0.5, k - 0.5);
+      GLOBAL_vertex[b].neigh |= 36; // -y and +z
+      c = CheckFaceCorner(i + 0.5, j + 0.5, k + 0.5);
+      GLOBAL_vertex[c].neigh |= 20; // -y and -z
+      d = CheckFaceCorner(i + 0.5, j - 0.5, k + 0.5);
+      GLOBAL_vertex[d].neigh |= 24; // +y and -z
+
+      GLOBAL_quads[GLOBAL_quad_num].a = a;
+      GLOBAL_quads[GLOBAL_quad_num].b = b;
+      GLOBAL_quads[GLOBAL_quad_num].c = c;
+      GLOBAL_quads[GLOBAL_quad_num].d = d;
+      GLOBAL_quad_num++;
     }
-    finish = clock();
 
-    // printf("   Generate quad meshes: CPU Time = %f seconds
-    // \n",(double)(finish-begin)/CLOCKS_PER_SEC);
-    // printf("   vert-num : %d -- quad-num: %d \n\n",vert_num,GLOBAL_quad_num);
+    // left face
+    if (GLOBAL_segment_index[IndexVect(i, j - 1, k)] == MaxVal) {
+      a = CheckFaceCorner(i + 0.5, j - 0.5, k - 0.5);
+      GLOBAL_vertex[a].neigh |= 33; // -x and +z
+      b = CheckFaceCorner(i + 0.5, j - 0.5, k + 0.5);
+      GLOBAL_vertex[b].neigh |= 17; // -x and -z
+      c = CheckFaceCorner(i - 0.5, j - 0.5, k + 0.5);
+      GLOBAL_vertex[c].neigh |= 18; // +x and -z
+      d = CheckFaceCorner(i - 0.5, j - 0.5, k - 0.5);
+      GLOBAL_vertex[d].neigh |= 34; // +x and +z
+
+      GLOBAL_quads[GLOBAL_quad_num].a = a;
+      GLOBAL_quads[GLOBAL_quad_num].b = b;
+      GLOBAL_quads[GLOBAL_quad_num].c = c;
+      GLOBAL_quads[GLOBAL_quad_num].d = d;
+      GLOBAL_quad_num++;
+    }
 
+    // right face
+    if (GLOBAL_segment_index[IndexVect(i, j + 1, k)] == MaxVal) {
+      a = CheckFaceCorner(i + 0.5, j + 0.5, k - 0.5);
+      GLOBAL_vertex[a].neigh |= 33; // -x and +z
+      b = CheckFaceCorner(i - 0.5, j + 0.5, k - 0.5);
+      GLOBAL_vertex[b].neigh |= 34; // +x and +z
+      c = CheckFaceCorner(i - 0.5, j + 0.5, k + 0.5);
+      GLOBAL_vertex[c].neigh |= 18; // +x and -z
+      d = CheckFaceCorner(i + 0.5, j + 0.5, k + 0.5);
+      GLOBAL_vertex[d].neigh |= 17; // -x and -z
+
+      GLOBAL_quads[GLOBAL_quad_num].a = a;
+      GLOBAL_quads[GLOBAL_quad_num].b = b;
+      GLOBAL_quads[GLOBAL_quad_num].c = c;
+      GLOBAL_quads[GLOBAL_quad_num].d = d;
+      GLOBAL_quad_num++;
+    }
 
-    // Smooth the mesh
-    begin = clock();
-    unsigned char neighbor;
+    // bottom face
+    if (GLOBAL_segment_index[IndexVect(i, j, k - 1)] == MaxVal) {
+      a = CheckFaceCorner(i + 0.5, j - 0.5, k - 0.5);
+      GLOBAL_vertex[a].neigh |= 9; // -x and +y
+      b = CheckFaceCorner(i - 0.5, j - 0.5, k - 0.5);
+      GLOBAL_vertex[b].neigh |= 10; // +x and +y
+      c = CheckFaceCorner(i - 0.5, j + 0.5, k - 0.5);
+      GLOBAL_vertex[c].neigh |= 6; // +x and -y
+      d = CheckFaceCorner(i + 0.5, j + 0.5, k - 0.5);
+      GLOBAL_vertex[d].neigh |= 5; // -x and -y
+
+      GLOBAL_quads[GLOBAL_quad_num].a = a;
+      GLOBAL_quads[GLOBAL_quad_num].b = b;
+      GLOBAL_quads[GLOBAL_quad_num].c = c;
+      GLOBAL_quads[GLOBAL_quad_num].d = d;
+      GLOBAL_quad_num++;
+    }
 
-    for (num = 0; num < 3; num++)
-    {
-        for (n = 0; n < GLOBAL_vert_num; n++)
-        {
-            nx = 0;
-            ny = 0;
-            nz = 0;
-            m  = 0;
-            neighbor = GLOBAL_vertex[n].neigh;
+    // top face
+    if (GLOBAL_segment_index[IndexVect(i, j, k + 1)] == MaxVal) {
+      a = CheckFaceCorner(i + 0.5, j - 0.5, k + 0.5);
+      GLOBAL_vertex[a].neigh |= 9; // -x and +y
+      b = CheckFaceCorner(i + 0.5, j + 0.5, k + 0.5);
+      GLOBAL_vertex[b].neigh |= 5; // -x and -y
+      c = CheckFaceCorner(i - 0.5, j + 0.5, k + 0.5);
+      GLOBAL_vertex[c].neigh |= 6; // +x and -y
+      d = CheckFaceCorner(i - 0.5, j - 0.5, k + 0.5);
+      GLOBAL_vertex[d].neigh |= 10; // +x and +y
+
+      GLOBAL_quads[GLOBAL_quad_num].a = a;
+      GLOBAL_quads[GLOBAL_quad_num].b = b;
+      GLOBAL_quads[GLOBAL_quad_num].c = c;
+      GLOBAL_quads[GLOBAL_quad_num].d = d;
+      GLOBAL_quad_num++;
+    }
+  }
+  finish = clock();
+
+  // printf("   Generate quad meshes: CPU Time = %f seconds
+  // \n",(double)(finish-begin)/CLOCKS_PER_SEC);
+  // printf("   vert-num : %d -- quad-num: %d \n\n",vert_num,GLOBAL_quad_num);
+
+  // Smooth the mesh
+  begin = clock();
+  unsigned char neighbor;
+
+  for (num = 0; num < 3; num++) {
+    for (n = 0; n < GLOBAL_vert_num; n++) {
+      nx = 0;
+      ny = 0;
+      nz = 0;
+      m = 0;
+      neighbor = GLOBAL_vertex[n].neigh;
+
+      i = GLOBAL_vertex[n].px;
+      j = GLOBAL_vertex[n].py;
+      k = GLOBAL_vertex[n].pz;
+
+      if (neighbor & 1) {
+        m++;
+        l = GLOBAL_atom_index[IndexVect(i - 1, j, k)];
+        nx += GLOBAL_vertex[l].x;
+        ny += GLOBAL_vertex[l].y;
+        nz += GLOBAL_vertex[l].z;
+      }
+
+      if (neighbor & 2) {
+        m++;
+        l = GLOBAL_atom_index[IndexVect(i + 1, j, k)];
+        nx += GLOBAL_vertex[l].x;
+        ny += GLOBAL_vertex[l].y;
+        nz += GLOBAL_vertex[l].z;
+      }
+
+      if (neighbor & 4) {
+        m++;
+        l = GLOBAL_atom_index[IndexVect(i, j - 1, k)];
+        nx += GLOBAL_vertex[l].x;
+        ny += GLOBAL_vertex[l].y;
+        nz += GLOBAL_vertex[l].z;
+      }
+
+      if (neighbor & 8) {
+        m++;
+        l = GLOBAL_atom_index[IndexVect(i, j + 1, k)];
+        nx += GLOBAL_vertex[l].x;
+        ny += GLOBAL_vertex[l].y;
+        nz += GLOBAL_vertex[l].z;
+      }
+
+      if (neighbor & 16) {
+        m++;
+        l = GLOBAL_atom_index[IndexVect(i, j, k - 1)];
+        nx += GLOBAL_vertex[l].x;
+        ny += GLOBAL_vertex[l].y;
+        nz += GLOBAL_vertex[l].z;
+      }
+
+      if (neighbor & 32) {
+        m++;
+        l = GLOBAL_atom_index[IndexVect(i, j, k + 1)];
+        nx += GLOBAL_vertex[l].x;
+        ny += GLOBAL_vertex[l].y;
+        nz += GLOBAL_vertex[l].z;
+      }
+
+      // update the position
+      GLOBAL_vertex[n].x = nx / (float)m;
+      GLOBAL_vertex[n].y = ny / (float)m;
+      GLOBAL_vertex[n].z = nz / (float)m;
+    }
+  }
+  finish = clock();
 
-            i = GLOBAL_vertex[n].px;
-            j = GLOBAL_vertex[n].py;
-            k = GLOBAL_vertex[n].pz;
+  // printf("   Smooth the quad meshes: CPU Time = %f seconds
+  // \n\n",(double)(finish-begin)/CLOCKS_PER_SEC);
 
-            if (neighbor & 1)
-            {
-                m++;
-                l   = GLOBAL_atom_index[IndexVect(i - 1, j, k)];
-                nx += GLOBAL_vertex[l].x;
-                ny += GLOBAL_vertex[l].y;
-                nz += GLOBAL_vertex[l].z;
-            }
+  // Allocate memory
+  std::unique_ptr mesh(new SurfaceMesh);
 
-            if (neighbor & 2)
-            {
-                m++;
-                l   = GLOBAL_atom_index[IndexVect(i + 1, j, k)];
-                nx += GLOBAL_vertex[l].x;
-                ny += GLOBAL_vertex[l].y;
-                nz += GLOBAL_vertex[l].z;
-            }
+  // write vertices
+  for (int i = 0; i < GLOBAL_vert_num; i++) {
+    float x = GLOBAL_vertex[i].x * span[0] + orig[0];
+    float y = GLOBAL_vertex[i].y * span[1] + orig[1];
+    float z = GLOBAL_vertex[i].z * span[2] + orig[2];
 
-            if (neighbor & 4)
-            {
-                m++;
-                l   = GLOBAL_atom_index[IndexVect(i, j - 1, k)];
-                nx += GLOBAL_vertex[l].x;
-                ny += GLOBAL_vertex[l].y;
-                nz += GLOBAL_vertex[l].z;
-            }
+    mesh->insert<1>({i}, SMVertex({x, y, z}));
+  }
 
-            if (neighbor & 8)
-            {
-                m++;
-                l   = GLOBAL_atom_index[IndexVect(i, j + 1, k)];
-                nx += GLOBAL_vertex[l].x;
-                ny += GLOBAL_vertex[l].y;
-                nz += GLOBAL_vertex[l].z;
-            }
+  // write triangles
+  float angle, angle1, angle2;
 
-            if (neighbor & 16)
-            {
-                m++;
-                l   = GLOBAL_atom_index[IndexVect(i, j, k - 1)];
-                nx += GLOBAL_vertex[l].x;
-                ny += GLOBAL_vertex[l].y;
-                nz += GLOBAL_vertex[l].z;
-            }
+  for (i = 0; i < GLOBAL_quad_num; i++) {
+    a = GLOBAL_quads[i].a;
+    b = GLOBAL_quads[i].b;
+    c = GLOBAL_quads[i].c;
+    d = GLOBAL_quads[i].d;
 
-            if (neighbor & 32)
-            {
-                m++;
-                l   = GLOBAL_atom_index[IndexVect(i, j, k + 1)];
-                nx += GLOBAL_vertex[l].x;
-                ny += GLOBAL_vertex[l].y;
-                nz += GLOBAL_vertex[l].z;
-            }
+    angle1 = -999.0;
+    angle2 = -999.0;
+    angle = GetAngle(a, b, c);
 
-            // update the position
-            GLOBAL_vertex[n].x = nx / (float)m;
-            GLOBAL_vertex[n].y = ny / (float)m;
-            GLOBAL_vertex[n].z = nz / (float)m;
-        }
+    if (angle > angle1) {
+      angle1 = angle;
     }
-    finish = clock();
+    angle = GetAngle(a, d, c);
 
-    // printf("   Smooth the quad meshes: CPU Time = %f seconds
-    // \n\n",(double)(finish-begin)/CLOCKS_PER_SEC);
-
-    // Allocate memory
-    std::unique_ptr mesh(new SurfaceMesh);
-
-    // write vertices
-    for (int i = 0; i < GLOBAL_vert_num; i++)
-    {
-        float x = GLOBAL_vertex[i].x * span[0] + orig[0];
-        float y = GLOBAL_vertex[i].y * span[1] + orig[1];
-        float z = GLOBAL_vertex[i].z * span[2] + orig[2];
-
-        mesh->insert<1>({i}, SMVertex({x, y, z}));
+    if (angle > angle1) {
+      angle1 = angle;
     }
+    angle = GetAngle(c, a, b);
 
-    // write triangles
-    float angle, angle1, angle2;
-
-    for (i = 0; i < GLOBAL_quad_num; i++)
-    {
-        a = GLOBAL_quads[i].a;
-        b = GLOBAL_quads[i].b;
-        c = GLOBAL_quads[i].c;
-        d = GLOBAL_quads[i].d;
-
-        angle1 = -999.0;
-        angle2 = -999.0;
-        angle  = GetAngle(a, b, c);
-
-        if (angle > angle1)
-        {
-            angle1 = angle;
-        }
-        angle = GetAngle(a, d, c);
-
-        if (angle > angle1)
-        {
-            angle1 = angle;
-        }
-        angle = GetAngle(c, a, b);
-
-        if (angle > angle1)
-        {
-            angle1 = angle;
-        }
-        angle = GetAngle(c, a, d);
-
-        if (angle > angle1)
-        {
-            angle1 = angle;
-        }
+    if (angle > angle1) {
+      angle1 = angle;
+    }
+    angle = GetAngle(c, a, d);
 
-        angle = GetAngle(b, a, d);
+    if (angle > angle1) {
+      angle1 = angle;
+    }
 
-        if (angle > angle2)
-        {
-            angle2 = angle;
-        }
-        angle = GetAngle(b, c, d);
+    angle = GetAngle(b, a, d);
 
-        if (angle > angle2)
-        {
-            angle2 = angle;
-        }
-        angle = GetAngle(d, a, b);
+    if (angle > angle2) {
+      angle2 = angle;
+    }
+    angle = GetAngle(b, c, d);
 
-        if (angle > angle2)
-        {
-            angle2 = angle;
-        }
-        angle = GetAngle(d, b, c);
+    if (angle > angle2) {
+      angle2 = angle;
+    }
+    angle = GetAngle(d, a, b);
 
-        if (angle > angle2)
-        {
-            angle2 = angle;
-        }
+    if (angle > angle2) {
+      angle2 = angle;
+    }
+    angle = GetAngle(d, b, c);
 
-        if (angle1 <= angle2)
-        {
-            mesh->insert<3>({a, b, c});
-            mesh->insert<3>({a, c, d});
-        }
-        else
-        {
-            mesh->insert<3>({a, b, d});
-            mesh->insert<3>({b, c, d});
-        }
+    if (angle > angle2) {
+      angle2 = angle;
     }
 
-    /* write to disk
-       FILE *fout;
-       if ((fout=fopen("output1.off", "wb"))==NULL){
-       printf("write error...\n");
-       exit(0);
-       };
-
-       fprintf(fout, "OFF\n");
-       fprintf(fout, "%d %d
-          %d\n",GLOBAL_vert_num,quad_num*2,GLOBAL_vert_num+quad_num*2-2);
-       for (i = 0; i < GLOBAL_vert_num; i++)
-       fprintf(fout, "%f %f %f
-          \n",surfmesh->vertex[i].x,surfmesh->vertex[i].y,surfmesh->vertex[i].z);
-
-       for (i = 0; i < quad_num*2; i++) {
-       fprintf(fout, "3 %d %d
-          %d\n",surfmesh->face[i].a,surfmesh->face[i].b,surfmesh->face[i].c);
-       }
-       fclose(fout);
-     */
-
-    free(AllSeeds);
-    free(GLOBAL_segment_index);
-    free(GLOBAL_atom_index);
-    free(min_heap->x);
-    free(min_heap->y);
-    free(min_heap->z);
-    free(min_heap->seed);
-    free(min_heap->dist);
-    free(min_heap);
-    free(GLOBAL_vertex);
-    free(GLOBAL_quads);
-
-    compute_orientation(*mesh);
-    return mesh;
+    if (angle1 <= angle2) {
+      mesh->insert<3>({a, b, c});
+      mesh->insert<3>({a, c, d});
+    } else {
+      mesh->insert<3>({a, b, d});
+      mesh->insert<3>({b, c, d});
+    }
+  }
+
+  /* write to disk
+     FILE *fout;
+     if ((fout=fopen("output1.off", "wb"))==NULL){
+     printf("write error...\n");
+     exit(0);
+     };
+
+     fprintf(fout, "OFF\n");
+     fprintf(fout, "%d %d
+        %d\n",GLOBAL_vert_num,quad_num*2,GLOBAL_vert_num+quad_num*2-2);
+     for (i = 0; i < GLOBAL_vert_num; i++)
+     fprintf(fout, "%f %f %f
+        \n",surfmesh->vertex[i].x,surfmesh->vertex[i].y,surfmesh->vertex[i].z);
+
+     for (i = 0; i < quad_num*2; i++) {
+     fprintf(fout, "3 %d %d
+        %d\n",surfmesh->face[i].a,surfmesh->face[i].b,surfmesh->face[i].c);
+     }
+     fclose(fout);
+   */
+
+  free(AllSeeds);
+  free(GLOBAL_segment_index);
+  free(GLOBAL_atom_index);
+  free(min_heap->x);
+  free(min_heap->y);
+  free(min_heap->z);
+  free(min_heap->seed);
+  free(min_heap->dist);
+  free(min_heap);
+  free(GLOBAL_vertex);
+  free(GLOBAL_quads);
+
+  compute_orientation(*mesh);
+  return mesh;
 }
 
-float GetAngle(int a, int b, int c)
-{
-    float ax, ay, az;
-    float bx, by, bz;
-    float dist;
+float GetAngle(int a, int b, int c) {
+  float ax, ay, az;
+  float bx, by, bz;
+  float dist;
+
+  ax = GLOBAL_vertex[b].x - GLOBAL_vertex[a].x;
+  ay = GLOBAL_vertex[b].y - GLOBAL_vertex[a].y;
+  az = GLOBAL_vertex[b].z - GLOBAL_vertex[a].z;
+  dist = sqrt(ax * ax + ay * ay + az * az);
+
+  if (dist > 0) {
+    ax /= dist;
+    ay /= dist;
+    az /= dist;
+  }
+  bx = GLOBAL_vertex[c].x - GLOBAL_vertex[a].x;
+  by = GLOBAL_vertex[c].y - GLOBAL_vertex[a].y;
+  bz = GLOBAL_vertex[c].z - GLOBAL_vertex[a].z;
+  dist = sqrt(bx * bx + by * by + bz * bz);
+
+  if (dist > 0) {
+    bx /= dist;
+    by /= dist;
+    bz /= dist;
+  }
+
+  return ax * bx + ay * by + az * bz;
+}
 
-    ax   = GLOBAL_vertex[b].x - GLOBAL_vertex[a].x;
-    ay   = GLOBAL_vertex[b].y - GLOBAL_vertex[a].y;
-    az   = GLOBAL_vertex[b].z - GLOBAL_vertex[a].z;
-    dist = sqrt(ax * ax + ay * ay + az * az);
+char CheckManifold(int i, int j, int k) {
+  char manifold, nonmanifold;
+  int m, n, l;
 
-    if (dist > 0)
-    {
-        ax /= dist;
-        ay /= dist;
-        az /= dist;
-    }
-    bx   = GLOBAL_vertex[c].x - GLOBAL_vertex[a].x;
-    by   = GLOBAL_vertex[c].y - GLOBAL_vertex[a].y;
-    bz   = GLOBAL_vertex[c].z - GLOBAL_vertex[a].z;
-    dist = sqrt(bx * bx + by * by + bz * bz);
+  manifold = 1;
 
-    if (dist > 0)
-    {
-        bx /= dist;
-        by /= dist;
-        bz /= dist;
+  for (l = k - 1; l <= k + 1; l++) {
+    if (!manifold) {
+      break;
     }
 
-    return ax * bx + ay * by + az * bz;
-}
-
-char CheckManifold(int i, int j, int k)
-{
-    char manifold, nonmanifold;
-    int  m, n, l;
+    for (n = j - 1; n <= j + 1; n++) {
+      if (!manifold) {
+        break;
+      }
 
-    manifold = 1;
+      for (m = i - 1; m <= i + 1; m++) {
+        if ((m != i) || (n != j) || (l != k)) {
+          if (GLOBAL_segment_index[IndexVect(m, n, l)] == MaxVal) {
+            nonmanifold = 1;
 
-    for (l = k - 1; l <= k + 1; l++)
-    {
-        if (!manifold)
-        {
-            break;
-        }
+            if ((m != i) &&
+                (GLOBAL_segment_index[IndexVect(m, j, k)] == MaxVal)) {
+              nonmanifold = 0;
+            }
 
-        for (n = j - 1; n <= j + 1; n++)
-        {
-            if (!manifold)
-            {
-                break;
+            if ((n != j) &&
+                (GLOBAL_segment_index[IndexVect(i, n, k)] == MaxVal)) {
+              nonmanifold = 0;
             }
 
-            for (m = i - 1; m <= i + 1; m++)
-            {
-                if ((m != i) || (n != j) || (l != k))
-                {
-                    if (GLOBAL_segment_index[IndexVect(m, n, l)] == MaxVal)
-                    {
-                        nonmanifold = 1;
-
-                        if ((m != i) && (GLOBAL_segment_index[IndexVect(m, j, k)] == MaxVal))
-                        {
-                            nonmanifold = 0;
-                        }
-
-                        if ((n != j) && (GLOBAL_segment_index[IndexVect(i, n, k)] == MaxVal))
-                        {
-                            nonmanifold = 0;
-                        }
-
-                        if ((l != k) && (GLOBAL_segment_index[IndexVect(i, j, l)] == MaxVal))
-                        {
-                            nonmanifold = 0;
-                        }
-
-                        if (nonmanifold)
-                        {
-                            manifold = 0;
-                        }
-                    }
-                }
+            if ((l != k) &&
+                (GLOBAL_segment_index[IndexVect(i, j, l)] == MaxVal)) {
+              nonmanifold = 0;
+            }
 
-                if (!manifold)
-                {
-                    break;
-                }
+            if (nonmanifold) {
+              manifold = 0;
             }
+          }
+        }
+
+        if (!manifold) {
+          break;
         }
+      }
     }
+  }
 
-    return manifold;
+  return manifold;
 }
 
-int CheckFaceCorner(float x, float y, float z)
-{
-    int m, n, l;
-    int a;
-
-    m = (int)x;
-    n = (int)y;
-    l = (int)z;
-
-    if (GLOBAL_atom_index[IndexVect(m, n, l)] < 0)
-    {
-        GLOBAL_vertex[GLOBAL_vert_num].x  = x;
-        GLOBAL_vertex[GLOBAL_vert_num].y  = y;
-        GLOBAL_vertex[GLOBAL_vert_num].z  = z;
-        GLOBAL_vertex[GLOBAL_vert_num].px = m;
-        GLOBAL_vertex[GLOBAL_vert_num].py = n;
-        GLOBAL_vertex[GLOBAL_vert_num].pz = l;
-        GLOBAL_atom_index[IndexVect(m, n, l)] = GLOBAL_vert_num;
-        a = GLOBAL_vert_num;
-        GLOBAL_vert_num++;
-    }
-    else
-    {
-        a = GLOBAL_atom_index[IndexVect(m, n, l)];
-    }
-
-    return a;
+int CheckFaceCorner(float x, float y, float z) {
+  int m, n, l;
+  int a;
+
+  m = (int)x;
+  n = (int)y;
+  l = (int)z;
+
+  if (GLOBAL_atom_index[IndexVect(m, n, l)] < 0) {
+    GLOBAL_vertex[GLOBAL_vert_num].x = x;
+    GLOBAL_vertex[GLOBAL_vert_num].y = y;
+    GLOBAL_vertex[GLOBAL_vert_num].z = z;
+    GLOBAL_vertex[GLOBAL_vert_num].px = m;
+    GLOBAL_vertex[GLOBAL_vert_num].py = n;
+    GLOBAL_vertex[GLOBAL_vert_num].pz = l;
+    GLOBAL_atom_index[IndexVect(m, n, l)] = GLOBAL_vert_num;
+    a = GLOBAL_vert_num;
+    GLOBAL_vert_num++;
+  } else {
+    a = GLOBAL_atom_index[IndexVect(m, n, l)];
+  }
+
+  return a;
 }
 
-FLT2VECT FindIntersection(int n, int m, int j, int k, ATOM *atom_list)
-{
-    FLT2VECT intersect;
-    int      i;
-    char     reorder;
-    double   cx1, cy1, cz1;
-    double   cx2, cy2, cz2;
-    double   dist, radius1, radius2;
-    double   cos_alpha;
-    double   ax, ay, az;
-    double   cx, cy, cz;
-
-
-    n--;
-    m--;
-    reorder = 0;
-
-    // always make the first atom on the left
-    // and the second on the right
-    if (atom_list[n].x > atom_list[m].x)
-    {
-        i = n;
-        n = m;
-        m = i;
-        reorder = 1;
-    }
-
-    radius1 = atom_list[n].radius;
-    cx1 = atom_list[n].x;
-    cy1 = atom_list[n].y;
-    cz1 = atom_list[n].z;
-    radius2 = atom_list[m].radius;
-    cx2 = atom_list[m].x;
-    cy2 = atom_list[m].y;
-    cz2 = atom_list[m].z;
-
-    if (cx1 == cx2)
-    {
-        if ((radius1 * radius1 - (k - cz1) * (k - cz1) - (j - cy1) * (j - cy1)) >=
-            (radius2 * radius2 - (k - cz2) * (k - cz2) - (j - cy2) * (j - cy2)))
-        {
-            intersect.x = (float)MaxVal;
-            intersect.y = -(float)MaxVal;
-        }
-        else
-        {
-            intersect.x = -(float)MaxVal;
-            intersect.y = (float)MaxVal;
-        }
+FLT2VECT FindIntersection(int n, int m, int j, int k, ATOM *atom_list) {
+  FLT2VECT intersect;
+  int i;
+  char reorder;
+  double cx1, cy1, cz1;
+  double cx2, cy2, cz2;
+  double dist, radius1, radius2;
+  double cos_alpha;
+  double ax, ay, az;
+  double cx, cy, cz;
+
+  n--;
+  m--;
+  reorder = 0;
+
+  // always make the first atom on the left
+  // and the second on the right
+  if (atom_list[n].x > atom_list[m].x) {
+    i = n;
+    n = m;
+    m = i;
+    reorder = 1;
+  }
+
+  radius1 = atom_list[n].radius;
+  cx1 = atom_list[n].x;
+  cy1 = atom_list[n].y;
+  cz1 = atom_list[n].z;
+  radius2 = atom_list[m].radius;
+  cx2 = atom_list[m].x;
+  cy2 = atom_list[m].y;
+  cz2 = atom_list[m].z;
+
+  if (cx1 == cx2) {
+    if ((radius1 * radius1 - (k - cz1) * (k - cz1) - (j - cy1) * (j - cy1)) >=
+        (radius2 * radius2 - (k - cz2) * (k - cz2) - (j - cy2) * (j - cy2))) {
+      intersect.x = (float)MaxVal;
+      intersect.y = -(float)MaxVal;
+    } else {
+      intersect.x = -(float)MaxVal;
+      intersect.y = (float)MaxVal;
     }
-    else
-    {
-        dist = sqrt((cx2 - cx1) * (cx2 - cx1) + (cy2 - cy1) * (cy2 - cy1) + (cz2 - cz1) * (cz2 - cz1));
-        cos_alpha = (radius1 * radius1 + dist * dist - radius2 * radius2) / (2.0 * radius1 * dist);
-        ax   = (cx2 - cx1) / dist;
-        ay   = (cy2 - cy1) / dist;
-        az   = (cz2 - cz1) / dist;
-        dist = radius1 * cos_alpha;
-        cx   = dist * ax + cx1;
-        cy   = dist * ay + cy1;
-        cz   = dist * az + cz1;
-        dist = cx * ax + (cy - j) * ay + (cz - k) * az;
-
-        if (reorder == 0)
-        {
-            intersect.x = (float)(dist / ax);
-            intersect.y = (float)MaxVal;
-        }
-        else
-        {
-            intersect.x = -(float)MaxVal;
-            intersect.y = (float)(dist / ax);
-        }
+  } else {
+    dist = sqrt((cx2 - cx1) * (cx2 - cx1) + (cy2 - cy1) * (cy2 - cy1) +
+                (cz2 - cz1) * (cz2 - cz1));
+    cos_alpha = (radius1 * radius1 + dist * dist - radius2 * radius2) /
+                (2.0 * radius1 * dist);
+    ax = (cx2 - cx1) / dist;
+    ay = (cy2 - cy1) / dist;
+    az = (cz2 - cz1) / dist;
+    dist = radius1 * cos_alpha;
+    cx = dist * ax + cx1;
+    cy = dist * ay + cy1;
+    cz = dist * az + cz1;
+    dist = cx * ax + (cy - j) * ay + (cz - k) * az;
+
+    if (reorder == 0) {
+      intersect.x = (float)(dist / ax);
+      intersect.y = (float)MaxVal;
+    } else {
+      intersect.x = -(float)MaxVal;
+      intersect.y = (float)(dist / ax);
     }
-    return intersect;
+  }
+  return intersect;
 }
 
-int ExtractSAS(int atom_num, ATOM *atom_list)
-{
-    int      i, j, k;
-    int      m, n, l;
-    int      dim[3], c[3];
-    int      amax[3], amin[3];
-    float    radius;
-    float    x, y, z;
-    FLT2VECT intersect;
-
-
-    dim[0] = GLOBAL_xdim;
-    dim[1] = GLOBAL_ydim;
-    dim[2] = GLOBAL_zdim;
-
-
-    for (m = 0; m < atom_num; m++)
-    {
-        radius = atom_list[m].radius;
-
-        // compute the dataset coordinates of the atom's center
-        c[0] = (int)(atom_list[m].x + 0.5);
-        c[1] = (int)(atom_list[m].y + 0.5);
-        c[2] = (int)(atom_list[m].z + 0.5);
-
-        // then compute the bounding box of the atom
-        for (j = 0; j < 3; j++)
-        {
-            int tmp;
-            tmp = (int)(c[j] - radius - 1);
-            tmp = (tmp < 0) ? 0 : tmp;
-            amin[j] = tmp;
-            tmp = (int)(c[j] + radius + 1);
-            tmp = (tmp > (dim[j] - 1)) ? (dim[j] - 1) : tmp;
-            amax[j] = tmp;
-        }
-
-        // begin blurring kernel
-        radius = radius * radius;
+int ExtractSAS(int atom_num, ATOM *atom_list) {
+  int i, j, k;
+  int m, n, l;
+  int dim[3], c[3];
+  int amax[3], amin[3];
+  float radius;
+  float x, y, z;
+  FLT2VECT intersect;
+
+  dim[0] = GLOBAL_xdim;
+  dim[1] = GLOBAL_ydim;
+  dim[2] = GLOBAL_zdim;
+
+  for (m = 0; m < atom_num; m++) {
+    radius = atom_list[m].radius;
+
+    // compute the dataset coordinates of the atom's center
+    c[0] = (int)(atom_list[m].x + 0.5);
+    c[1] = (int)(atom_list[m].y + 0.5);
+    c[2] = (int)(atom_list[m].z + 0.5);
+
+    // then compute the bounding box of the atom
+    for (j = 0; j < 3; j++) {
+      int tmp;
+      tmp = (int)(c[j] - radius - 1);
+      tmp = (tmp < 0) ? 0 : tmp;
+      amin[j] = tmp;
+      tmp = (int)(c[j] + radius + 1);
+      tmp = (tmp > (dim[j] - 1)) ? (dim[j] - 1) : tmp;
+      amax[j] = tmp;
+    }
 
-        for (k = amin[2]; k <= amax[2]; k++)
-        {
-            for (j = amin[1]; j <= amax[1]; j++)
-            {
-                for (i = amin[0]; i <= amax[0]; i++)
-                {
-                    x = i;
-                    y = j;
-                    z = k;
-
-                    if ((x - atom_list[m].x) * (x - atom_list[m].x) +
-                        (y - atom_list[m].y) * (y - atom_list[m].y) +
-                        (z - atom_list[m].z) * (z - atom_list[m].z) <= radius)
-                    {
-                        if (GLOBAL_atom_index[IndexVect(i, j, k)] > 0)
-                        {
-                            intersect = FindIntersection(GLOBAL_atom_index[IndexVect(i, j, k)], m + 1, j, k, atom_list);
-
-                            if ((i >= intersect.x) && (i <= intersect.y))
-                            {
-                                GLOBAL_atom_index[IndexVect(i, j, k)] = m + 1;
-                            }
-                        }
-                        else
-                        {
-                            GLOBAL_atom_index[IndexVect(i, j, k)] = m + 1;
-                        }
-                    }
-                }
+    // begin blurring kernel
+    radius = radius * radius;
+
+    for (k = amin[2]; k <= amax[2]; k++) {
+      for (j = amin[1]; j <= amax[1]; j++) {
+        for (i = amin[0]; i <= amax[0]; i++) {
+          x = i;
+          y = j;
+          z = k;
+
+          if ((x - atom_list[m].x) * (x - atom_list[m].x) +
+                  (y - atom_list[m].y) * (y - atom_list[m].y) +
+                  (z - atom_list[m].z) * (z - atom_list[m].z) <=
+              radius) {
+            if (GLOBAL_atom_index[IndexVect(i, j, k)] > 0) {
+              intersect =
+                  FindIntersection(GLOBAL_atom_index[IndexVect(i, j, k)], m + 1,
+                                   j, k, atom_list);
+
+              if ((i >= intersect.x) && (i <= intersect.y)) {
+                GLOBAL_atom_index[IndexVect(i, j, k)] = m + 1;
+              }
+            } else {
+              GLOBAL_atom_index[IndexVect(i, j, k)] = m + 1;
             }
+          }
         }
+      }
     }
-
-
-    // find voxels on the border
-    int total = 0;
-
-    for (l = 1; l < GLOBAL_zdim - 1; l++)
-    {
-        for (n = 1; n < GLOBAL_ydim - 1; n++)
-        {
-            for (m = 1; m < GLOBAL_xdim - 1; m++)
-            {
-                if (GLOBAL_atom_index[IndexVect(m, n, l)])
-                {
-                    int count = 0;
-
-                    // Look at neighbor voxels
-                    for (k = std::max(l - 1, 0); k <= std::min(l + 1, GLOBAL_zdim - 1); k++)
-                    {
-                        for (j = std::max(n - 1, 0); j <= std::min(n + 1, GLOBAL_ydim - 1); j++)
-                        {
-                            for (i = std::max(m - 1, 0); i <= std::min(m + 1, GLOBAL_xdim - 1); i++)
-                            {
-                                if ((((i == m) && (j == n)) || ((i == m) && (k == l)) || ((k == l) && (j == n))) &&
-                                    (GLOBAL_atom_index[IndexVect(i, j, k)] == 0))
-                                {
-                                    count = 1;
-                                }
-                            }
-                        }
-                    }
-
-                    if (count)
-                    {
-                        GLOBAL_atom_index[IndexVect(m, n, l)] = -GLOBAL_atom_index[IndexVect(m, n, l)];
-                        total++;
-                    }
+  }
+
+  // find voxels on the border
+  int total = 0;
+
+  for (l = 1; l < GLOBAL_zdim - 1; l++) {
+    for (n = 1; n < GLOBAL_ydim - 1; n++) {
+      for (m = 1; m < GLOBAL_xdim - 1; m++) {
+        if (GLOBAL_atom_index[IndexVect(m, n, l)]) {
+          int count = 0;
+
+          // Look at neighbor voxels
+          for (k = std::max(l - 1, 0); k <= std::min(l + 1, GLOBAL_zdim - 1);
+               k++) {
+            for (j = std::max(n - 1, 0); j <= std::min(n + 1, GLOBAL_ydim - 1);
+                 j++) {
+              for (i = std::max(m - 1, 0);
+                   i <= std::min(m + 1, GLOBAL_xdim - 1); i++) {
+                if ((((i == m) && (j == n)) || ((i == m) && (k == l)) ||
+                     ((k == l) && (j == n))) &&
+                    (GLOBAL_atom_index[IndexVect(i, j, k)] == 0)) {
+                  count = 1;
                 }
+              }
             }
+          }
+
+          if (count) {
+            GLOBAL_atom_index[IndexVect(m, n, l)] =
+                -GLOBAL_atom_index[IndexVect(m, n, l)];
+            total++;
+          }
         }
+      }
     }
+  }
 
-
-    return total;
+  return total;
 }
 
-std::unique_ptr readPQR_molsurf(const std::string &input_name)
-{
-    int               i, j, k;
-    int               a, b, c, d;
-    float             orig[3], span[3];
-    int               dim[3];
-    int               m, n, l, num;
-    double            threshold;
-    double            nx, ny, nz;
-    int               xydim, xyzdim;
-    clock_t           begin, finish;
-    std::vector atoms;
-    std::vector atom_list;
-    float             min[3], max[3];
-    SEEDS            *AllSeeds; // Border variable
-    MinHeapS         *min_heap;
-
-    // Read in the PQR file
-    readPQR(input_name, std::back_inserter(atoms));
-
-    for (auto atom : atoms)
-    {
-
-        ATOM new_atom;
-        new_atom.x = atom.pos[0];
-        new_atom.y = atom.pos[1];
-        new_atom.z = atom.pos[2];
-        new_atom.radius = atom.radius;
-
-        atom_list.push_back(new_atom);
-    }
-
-    getMinMax(atom_list.begin(), atom_list.end(), min, max);
-
-    GLOBAL_xdim = (int)(((max[0] - min[0]) + 1) * DIM_SCALE);
-    GLOBAL_ydim = (int)(((max[1] - min[1]) + 1) * DIM_SCALE);
-    GLOBAL_zdim = (int)(((max[2] - min[2]) + 1) * DIM_SCALE);
-    xydim  = GLOBAL_xdim * GLOBAL_ydim;
-    xyzdim = xydim * GLOBAL_zdim;
-
-    // printf("dimension: %d X %d X %d\n",xdim,ydim,zdim);
-
-    GLOBAL_atom_index = (int *)malloc(sizeof(int) * xyzdim);
-    GLOBAL_segment_index = (int *)malloc(sizeof(int) * xyzdim);
-
-    for (k = 0; k < xyzdim; k++)
-    {
-        GLOBAL_atom_index[k] = 0;
-    }
-
-    orig[0] = min[0];
-    orig[1] = min[1];
-    orig[2] = min[2];
-    span[0] = (max[0] - min[0]) / (double)(GLOBAL_xdim - 1);
-    span[1] = (max[1] - min[1]) / (double)(GLOBAL_ydim - 1);
-    span[2] = (max[2] - min[2]) / (double)(GLOBAL_zdim - 1);
-    dim[0]  = GLOBAL_xdim;
-    dim[1]  = GLOBAL_ydim;
-    dim[2]  = GLOBAL_zdim;
-
-    for (m = 0; m < atom_list.size(); m++)
-    {
-        atom_list[m].x = (atom_list[m].x - orig[0]) / span[0];
-        atom_list[m].y = (atom_list[m].y - orig[1]) / span[1];
-        atom_list[m].z = (atom_list[m].z - orig[2]) / span[2];
-        atom_list[m].radius = (atom_list[m].radius + 1.5) / ((span[0] + span[1] + span[2]) / 3.0);
-    }
-
-    begin  = clock();
-    num    = ExtractSAS(atom_list.size(), atom_list.data());
-    finish = clock();
-
-    // for(int q=0; q < GLOBAL_xdim; ++q){
-    //     for(int r=0; r < GLOBAL_ydim; ++r){
-    //         for(int s=0; s < GLOBAL_zdim; ++s){
-    //             std::cout << GLOBAL_atom_index[IndexVect(q,r,s)] <<
-    // std::endl;
-    //         }
-    //     }
-    // }
-
-    // printf("   Extract SAS voxels: CPU Time = %f seconds
-    // \n",(double)(finish-begin)/CLOCKS_PER_SEC);
-    // printf("   Number of boundary voxels: %d\n\n",num);
-
-
-    begin = clock();
-    threshold   = 1.5 / ((span[0] + span[1] + span[2]) / 3.0);
-    min_heap    = (MinHeapS *)malloc(sizeof(MinHeapS));
-    min_heap->x = (unsigned short *)malloc(sizeof(unsigned short) * num * 3);
-    min_heap->y = (unsigned short *)malloc(sizeof(unsigned short) * num * 3);
-    min_heap->z = (unsigned short *)malloc(sizeof(unsigned short) * num * 3);
-    min_heap->seed = (int *)malloc(sizeof(int) * num * 3);
-    min_heap->dist = (float *)malloc(sizeof(float) * num * 3);
-    AllSeeds = (SEEDS *)malloc(sizeof(SEEDS) * num);
-    ExtractSES(min_heap,
-               AllSeeds,
-               GLOBAL_segment_index,
-               GLOBAL_xdim,
-               GLOBAL_ydim,
-               GLOBAL_zdim,
-               GLOBAL_atom_index,
-               atom_list.size(),
-               atom_list.data(),
-               threshold * threshold);
-    finish = clock();
-
-    // printf("   Extract SES voxels: CPU Time = %f seconds
-    // \n\n",(double)(finish-begin)/CLOCKS_PER_SEC);
-
-
-    // detect and fix non-manifolds !
-    while (1)
-    {
-        b = 0;
-
-        int index = 0;
-
-        for (k = 0; k < GLOBAL_zdim; k++)
-        {
-            for (j = 0; j < GLOBAL_ydim; j++)
+std::unique_ptr readPQR_molsurf(const std::string &input_name) {
+  int i, j, k;
+  int a, b, c, d;
+  float orig[3], span[3];
+  int dim[3];
+  int m, n, l, num;
+  double threshold;
+  double nx, ny, nz;
+  int xydim, xyzdim;
+  clock_t begin, finish;
+  std::vector atoms;
+  std::vector atom_list;
+  float min[3], max[3];
+  SEEDS *AllSeeds; // Border variable
+  MinHeapS *min_heap;
+
+  // Read in the PQR file
+  readPQR(input_name, std::back_inserter(atoms));
+
+  for (auto atom : atoms) {
+
+    ATOM new_atom;
+    new_atom.x = atom.pos[0];
+    new_atom.y = atom.pos[1];
+    new_atom.z = atom.pos[2];
+    new_atom.radius = atom.radius;
+
+    atom_list.push_back(new_atom);
+  }
+
+  getMinMax(atom_list.begin(), atom_list.end(), min, max);
+
+  GLOBAL_xdim = (int)(((max[0] - min[0]) + 1) * DIM_SCALE);
+  GLOBAL_ydim = (int)(((max[1] - min[1]) + 1) * DIM_SCALE);
+  GLOBAL_zdim = (int)(((max[2] - min[2]) + 1) * DIM_SCALE);
+  xydim = GLOBAL_xdim * GLOBAL_ydim;
+  xyzdim = xydim * GLOBAL_zdim;
+
+  // printf("dimension: %d X %d X %d\n",xdim,ydim,zdim);
+
+  GLOBAL_atom_index = (int *)malloc(sizeof(int) * xyzdim);
+  GLOBAL_segment_index = (int *)malloc(sizeof(int) * xyzdim);
+
+  for (k = 0; k < xyzdim; k++) {
+    GLOBAL_atom_index[k] = 0;
+  }
+
+  orig[0] = min[0];
+  orig[1] = min[1];
+  orig[2] = min[2];
+  span[0] = (max[0] - min[0]) / (double)(GLOBAL_xdim - 1);
+  span[1] = (max[1] - min[1]) / (double)(GLOBAL_ydim - 1);
+  span[2] = (max[2] - min[2]) / (double)(GLOBAL_zdim - 1);
+  dim[0] = GLOBAL_xdim;
+  dim[1] = GLOBAL_ydim;
+  dim[2] = GLOBAL_zdim;
+
+  for (m = 0; m < atom_list.size(); m++) {
+    atom_list[m].x = (atom_list[m].x - orig[0]) / span[0];
+    atom_list[m].y = (atom_list[m].y - orig[1]) / span[1];
+    atom_list[m].z = (atom_list[m].z - orig[2]) / span[2];
+    atom_list[m].radius =
+        (atom_list[m].radius + 1.5) / ((span[0] + span[1] + span[2]) / 3.0);
+  }
+
+  begin = clock();
+  num = ExtractSAS(atom_list.size(), atom_list.data());
+  finish = clock();
+
+  // for(int q=0; q < GLOBAL_xdim; ++q){
+  //     for(int r=0; r < GLOBAL_ydim; ++r){
+  //         for(int s=0; s < GLOBAL_zdim; ++s){
+  //             std::cout << GLOBAL_atom_index[IndexVect(q,r,s)] <<
+  // std::endl;
+  //         }
+  //     }
+  // }
+
+  // printf("   Extract SAS voxels: CPU Time = %f seconds
+  // \n",(double)(finish-begin)/CLOCKS_PER_SEC);
+  // printf("   Number of boundary voxels: %d\n\n",num);
+
+  begin = clock();
+  threshold = 1.5 / ((span[0] + span[1] + span[2]) / 3.0);
+  min_heap = (MinHeapS *)malloc(sizeof(MinHeapS));
+  min_heap->x = (unsigned short *)malloc(sizeof(unsigned short) * num * 3);
+  min_heap->y = (unsigned short *)malloc(sizeof(unsigned short) * num * 3);
+  min_heap->z = (unsigned short *)malloc(sizeof(unsigned short) * num * 3);
+  min_heap->seed = (int *)malloc(sizeof(int) * num * 3);
+  min_heap->dist = (float *)malloc(sizeof(float) * num * 3);
+  AllSeeds = (SEEDS *)malloc(sizeof(SEEDS) * num);
+  ExtractSES(min_heap, AllSeeds, GLOBAL_segment_index, GLOBAL_xdim, GLOBAL_ydim,
+             GLOBAL_zdim, GLOBAL_atom_index, atom_list.size(), atom_list.data(),
+             threshold * threshold);
+  finish = clock();
+
+  // printf("   Extract SES voxels: CPU Time = %f seconds
+  // \n\n",(double)(finish-begin)/CLOCKS_PER_SEC);
+
+  // detect and fix non-manifolds !
+  while (1) {
+    b = 0;
+
+    int index = 0;
+
+    for (k = 0; k < GLOBAL_zdim; k++) {
+      for (j = 0; j < GLOBAL_ydim; j++) {
+        for (i = 0; i < GLOBAL_xdim; i++, ++index) {
+          if (GLOBAL_segment_index[index] == MaxVal) {
+            if (!CheckManifold(i, j, k)) // non-manifold occurs
             {
-                for (i = 0; i < GLOBAL_xdim; i++, ++index)
-                {
-                    if (GLOBAL_segment_index[index] == MaxVal)
-                    {
-                        if (!CheckManifold(i, j, k)) // non-manifold occurs
-                        {
-                            GLOBAL_segment_index[index] = 0;
-                            min_heap->x[min_heap->size] = i;
-                            min_heap->y[min_heap->size] = j;
-                            min_heap->z[min_heap->size] = k;
-                            min_heap->size++;
-                            b++;
-                        }
-                    }
-                }
+              GLOBAL_segment_index[index] = 0;
+              min_heap->x[min_heap->size] = i;
+              min_heap->y[min_heap->size] = j;
+              min_heap->z[min_heap->size] = k;
+              min_heap->size++;
+              b++;
             }
+          }
         }
-
-        // printf("non-manifold = %d \n",b);
-        if (b == 0)
-        {
-            break;
-        }
+      }
     }
 
-
-    // generate the surface mesh
-    GLOBAL_vertex = (MOL_VERTEX *)malloc(sizeof(MOL_VERTEX) * num * 8);
-    GLOBAL_quads  = (INT4VECT *)malloc(sizeof(INT4VECT) * num * 6);
-
-    for (k = 0; k < num * 8; k++)
-    {
-        GLOBAL_vertex[k].neigh = 0;
+    // printf("non-manifold = %d \n",b);
+    if (b == 0) {
+      break;
     }
-
-    for (k = 0; k < xyzdim; k++)
-    {
-        GLOBAL_atom_index[k] = -1;
+  }
+
+  // generate the surface mesh
+  GLOBAL_vertex = (MOL_VERTEX *)malloc(sizeof(MOL_VERTEX) * num * 8);
+  GLOBAL_quads = (INT4VECT *)malloc(sizeof(INT4VECT) * num * 6);
+
+  for (k = 0; k < num * 8; k++) {
+    GLOBAL_vertex[k].neigh = 0;
+  }
+
+  for (k = 0; k < xyzdim; k++) {
+    GLOBAL_atom_index[k] = -1;
+  }
+  begin = clock();
+  GLOBAL_vert_num = 0;
+  GLOBAL_quad_num = 0;
+
+  for (num = 0; num < min_heap->size; num++) {
+    i = min_heap->x[num];
+    j = min_heap->y[num];
+    k = min_heap->z[num];
+
+    // back face
+    if (GLOBAL_segment_index[IndexVect(i - 1, j, k)] == MaxVal) {
+      a = CheckFaceCorner(i - 0.5, j - 0.5, k - 0.5);
+      GLOBAL_vertex[a].neigh |= 40; // +y and +z
+      b = CheckFaceCorner(i - 0.5, j - 0.5, k + 0.5);
+      GLOBAL_vertex[b].neigh |= 24; // +y and -z
+      c = CheckFaceCorner(i - 0.5, j + 0.5, k + 0.5);
+      GLOBAL_vertex[c].neigh |= 20; // -y and -z
+      d = CheckFaceCorner(i - 0.5, j + 0.5, k - 0.5);
+      GLOBAL_vertex[d].neigh |= 36; // -y and +z
+
+      GLOBAL_quads[GLOBAL_quad_num].a = a;
+      GLOBAL_quads[GLOBAL_quad_num].b = b;
+      GLOBAL_quads[GLOBAL_quad_num].c = c;
+      GLOBAL_quads[GLOBAL_quad_num].d = d;
+      GLOBAL_quad_num++;
     }
-    begin = clock();
-    GLOBAL_vert_num = 0;
-    GLOBAL_quad_num = 0;
-
-    for (num = 0; num < min_heap->size; num++)
-    {
-        i = min_heap->x[num];
-        j = min_heap->y[num];
-        k = min_heap->z[num];
-
-        // back face
-        if (GLOBAL_segment_index[IndexVect(i - 1, j, k)] == MaxVal)
-        {
-            a = CheckFaceCorner(i - 0.5, j - 0.5, k - 0.5);
-            GLOBAL_vertex[a].neigh |= 40; // +y and +z
-            b = CheckFaceCorner(i - 0.5, j - 0.5, k + 0.5);
-            GLOBAL_vertex[b].neigh |= 24; // +y and -z
-            c = CheckFaceCorner(i - 0.5, j + 0.5, k + 0.5);
-            GLOBAL_vertex[c].neigh |= 20; // -y and -z
-            d = CheckFaceCorner(i - 0.5, j + 0.5, k - 0.5);
-            GLOBAL_vertex[d].neigh |= 36; // -y and +z
-
-            GLOBAL_quads[GLOBAL_quad_num].a = a;
-            GLOBAL_quads[GLOBAL_quad_num].b = b;
-            GLOBAL_quads[GLOBAL_quad_num].c = c;
-            GLOBAL_quads[GLOBAL_quad_num].d = d;
-            GLOBAL_quad_num++;
-        }
-
-        // front face
-        if (GLOBAL_segment_index[IndexVect(i + 1, j, k)] == MaxVal)
-        {
-            a = CheckFaceCorner(i + 0.5, j - 0.5, k - 0.5);
-            GLOBAL_vertex[a].neigh |= 40; // +y and +z
-            b = CheckFaceCorner(i + 0.5, j + 0.5, k - 0.5);
-            GLOBAL_vertex[b].neigh |= 36; // -y and +z
-            c = CheckFaceCorner(i + 0.5, j + 0.5, k + 0.5);
-            GLOBAL_vertex[c].neigh |= 20; // -y and -z
-            d = CheckFaceCorner(i + 0.5, j - 0.5, k + 0.5);
-            GLOBAL_vertex[d].neigh |= 24; // +y and -z
-
-            GLOBAL_quads[GLOBAL_quad_num].a = a;
-            GLOBAL_quads[GLOBAL_quad_num].b = b;
-            GLOBAL_quads[GLOBAL_quad_num].c = c;
-            GLOBAL_quads[GLOBAL_quad_num].d = d;
-            GLOBAL_quad_num++;
-        }
-
-        // left face
-        if (GLOBAL_segment_index[IndexVect(i, j - 1, k)] == MaxVal)
-        {
-            a = CheckFaceCorner(i + 0.5, j - 0.5, k - 0.5);
-            GLOBAL_vertex[a].neigh |= 33; // -x and +z
-            b = CheckFaceCorner(i + 0.5, j - 0.5, k + 0.5);
-            GLOBAL_vertex[b].neigh |= 17; // -x and -z
-            c = CheckFaceCorner(i - 0.5, j - 0.5, k + 0.5);
-            GLOBAL_vertex[c].neigh |= 18; // +x and -z
-            d = CheckFaceCorner(i - 0.5, j - 0.5, k - 0.5);
-            GLOBAL_vertex[d].neigh |= 34; // +x and +z
-
-            GLOBAL_quads[GLOBAL_quad_num].a = a;
-            GLOBAL_quads[GLOBAL_quad_num].b = b;
-            GLOBAL_quads[GLOBAL_quad_num].c = c;
-            GLOBAL_quads[GLOBAL_quad_num].d = d;
-            GLOBAL_quad_num++;
-        }
 
-        // right face
-        if (GLOBAL_segment_index[IndexVect(i, j + 1, k)] == MaxVal)
-        {
-            a = CheckFaceCorner(i + 0.5, j + 0.5, k - 0.5);
-            GLOBAL_vertex[a].neigh |= 33; // -x and +z
-            b = CheckFaceCorner(i - 0.5, j + 0.5, k - 0.5);
-            GLOBAL_vertex[b].neigh |= 34; // +x and +z
-            c = CheckFaceCorner(i - 0.5, j + 0.5, k + 0.5);
-            GLOBAL_vertex[c].neigh |= 18; // +x and -z
-            d = CheckFaceCorner(i + 0.5, j + 0.5, k + 0.5);
-            GLOBAL_vertex[d].neigh |= 17; // -x and -z
-
-            GLOBAL_quads[GLOBAL_quad_num].a = a;
-            GLOBAL_quads[GLOBAL_quad_num].b = b;
-            GLOBAL_quads[GLOBAL_quad_num].c = c;
-            GLOBAL_quads[GLOBAL_quad_num].d = d;
-            GLOBAL_quad_num++;
-        }
-
-        // bottom face
-        if (GLOBAL_segment_index[IndexVect(i, j, k - 1)] == MaxVal)
-        {
-            a = CheckFaceCorner(i + 0.5, j - 0.5, k - 0.5);
-            GLOBAL_vertex[a].neigh |= 9;  // -x and +y
-            b = CheckFaceCorner(i - 0.5, j - 0.5, k - 0.5);
-            GLOBAL_vertex[b].neigh |= 10; // +x and +y
-            c = CheckFaceCorner(i - 0.5, j + 0.5, k - 0.5);
-            GLOBAL_vertex[c].neigh |= 6;  // +x and -y
-            d = CheckFaceCorner(i + 0.5, j + 0.5, k - 0.5);
-            GLOBAL_vertex[d].neigh |= 5;  // -x and -y
-
-            GLOBAL_quads[GLOBAL_quad_num].a = a;
-            GLOBAL_quads[GLOBAL_quad_num].b = b;
-            GLOBAL_quads[GLOBAL_quad_num].c = c;
-            GLOBAL_quads[GLOBAL_quad_num].d = d;
-            GLOBAL_quad_num++;
-        }
-
-        // top face
-        if (GLOBAL_segment_index[IndexVect(i, j, k + 1)] == MaxVal)
-        {
-            a = CheckFaceCorner(i + 0.5, j - 0.5, k + 0.5);
-            GLOBAL_vertex[a].neigh |= 9;  // -x and +y
-            b = CheckFaceCorner(i + 0.5, j + 0.5, k + 0.5);
-            GLOBAL_vertex[b].neigh |= 5;  // -x and -y
-            c = CheckFaceCorner(i - 0.5, j + 0.5, k + 0.5);
-            GLOBAL_vertex[c].neigh |= 6;  // +x and -y
-            d = CheckFaceCorner(i - 0.5, j - 0.5, k + 0.5);
-            GLOBAL_vertex[d].neigh |= 10; // +x and +y
-
-            GLOBAL_quads[GLOBAL_quad_num].a = a;
-            GLOBAL_quads[GLOBAL_quad_num].b = b;
-            GLOBAL_quads[GLOBAL_quad_num].c = c;
-            GLOBAL_quads[GLOBAL_quad_num].d = d;
-            GLOBAL_quad_num++;
-        }
+    // front face
+    if (GLOBAL_segment_index[IndexVect(i + 1, j, k)] == MaxVal) {
+      a = CheckFaceCorner(i + 0.5, j - 0.5, k - 0.5);
+      GLOBAL_vertex[a].neigh |= 40; // +y and +z
+      b = CheckFaceCorner(i + 0.5, j + 0.5, k - 0.5);
+      GLOBAL_vertex[b].neigh |= 36; // -y and +z
+      c = CheckFaceCorner(i + 0.5, j + 0.5, k + 0.5);
+      GLOBAL_vertex[c].neigh |= 20; // -y and -z
+      d = CheckFaceCorner(i + 0.5, j - 0.5, k + 0.5);
+      GLOBAL_vertex[d].neigh |= 24; // +y and -z
+
+      GLOBAL_quads[GLOBAL_quad_num].a = a;
+      GLOBAL_quads[GLOBAL_quad_num].b = b;
+      GLOBAL_quads[GLOBAL_quad_num].c = c;
+      GLOBAL_quads[GLOBAL_quad_num].d = d;
+      GLOBAL_quad_num++;
     }
-    finish = clock();
 
-    // printf("   Generate quad meshes: CPU Time = %f seconds
-    // \n",(double)(finish-begin)/CLOCKS_PER_SEC);
-    // printf("   vert-num : %d -- quad-num: %d \n\n",vert_num,GLOBAL_quad_num);
+    // left face
+    if (GLOBAL_segment_index[IndexVect(i, j - 1, k)] == MaxVal) {
+      a = CheckFaceCorner(i + 0.5, j - 0.5, k - 0.5);
+      GLOBAL_vertex[a].neigh |= 33; // -x and +z
+      b = CheckFaceCorner(i + 0.5, j - 0.5, k + 0.5);
+      GLOBAL_vertex[b].neigh |= 17; // -x and -z
+      c = CheckFaceCorner(i - 0.5, j - 0.5, k + 0.5);
+      GLOBAL_vertex[c].neigh |= 18; // +x and -z
+      d = CheckFaceCorner(i - 0.5, j - 0.5, k - 0.5);
+      GLOBAL_vertex[d].neigh |= 34; // +x and +z
+
+      GLOBAL_quads[GLOBAL_quad_num].a = a;
+      GLOBAL_quads[GLOBAL_quad_num].b = b;
+      GLOBAL_quads[GLOBAL_quad_num].c = c;
+      GLOBAL_quads[GLOBAL_quad_num].d = d;
+      GLOBAL_quad_num++;
+    }
 
+    // right face
+    if (GLOBAL_segment_index[IndexVect(i, j + 1, k)] == MaxVal) {
+      a = CheckFaceCorner(i + 0.5, j + 0.5, k - 0.5);
+      GLOBAL_vertex[a].neigh |= 33; // -x and +z
+      b = CheckFaceCorner(i - 0.5, j + 0.5, k - 0.5);
+      GLOBAL_vertex[b].neigh |= 34; // +x and +z
+      c = CheckFaceCorner(i - 0.5, j + 0.5, k + 0.5);
+      GLOBAL_vertex[c].neigh |= 18; // +x and -z
+      d = CheckFaceCorner(i + 0.5, j + 0.5, k + 0.5);
+      GLOBAL_vertex[d].neigh |= 17; // -x and -z
+
+      GLOBAL_quads[GLOBAL_quad_num].a = a;
+      GLOBAL_quads[GLOBAL_quad_num].b = b;
+      GLOBAL_quads[GLOBAL_quad_num].c = c;
+      GLOBAL_quads[GLOBAL_quad_num].d = d;
+      GLOBAL_quad_num++;
+    }
 
-    // Smooth the mesh
-    begin = clock();
-    unsigned char neighbor;
+    // bottom face
+    if (GLOBAL_segment_index[IndexVect(i, j, k - 1)] == MaxVal) {
+      a = CheckFaceCorner(i + 0.5, j - 0.5, k - 0.5);
+      GLOBAL_vertex[a].neigh |= 9; // -x and +y
+      b = CheckFaceCorner(i - 0.5, j - 0.5, k - 0.5);
+      GLOBAL_vertex[b].neigh |= 10; // +x and +y
+      c = CheckFaceCorner(i - 0.5, j + 0.5, k - 0.5);
+      GLOBAL_vertex[c].neigh |= 6; // +x and -y
+      d = CheckFaceCorner(i + 0.5, j + 0.5, k - 0.5);
+      GLOBAL_vertex[d].neigh |= 5; // -x and -y
+
+      GLOBAL_quads[GLOBAL_quad_num].a = a;
+      GLOBAL_quads[GLOBAL_quad_num].b = b;
+      GLOBAL_quads[GLOBAL_quad_num].c = c;
+      GLOBAL_quads[GLOBAL_quad_num].d = d;
+      GLOBAL_quad_num++;
+    }
 
-    for (num = 0; num < 3; num++)
-    {
-        for (n = 0; n < GLOBAL_vert_num; n++)
-        {
-            nx = 0;
-            ny = 0;
-            nz = 0;
-            m  = 0;
-            neighbor = GLOBAL_vertex[n].neigh;
+    // top face
+    if (GLOBAL_segment_index[IndexVect(i, j, k + 1)] == MaxVal) {
+      a = CheckFaceCorner(i + 0.5, j - 0.5, k + 0.5);
+      GLOBAL_vertex[a].neigh |= 9; // -x and +y
+      b = CheckFaceCorner(i + 0.5, j + 0.5, k + 0.5);
+      GLOBAL_vertex[b].neigh |= 5; // -x and -y
+      c = CheckFaceCorner(i - 0.5, j + 0.5, k + 0.5);
+      GLOBAL_vertex[c].neigh |= 6; // +x and -y
+      d = CheckFaceCorner(i - 0.5, j - 0.5, k + 0.5);
+      GLOBAL_vertex[d].neigh |= 10; // +x and +y
+
+      GLOBAL_quads[GLOBAL_quad_num].a = a;
+      GLOBAL_quads[GLOBAL_quad_num].b = b;
+      GLOBAL_quads[GLOBAL_quad_num].c = c;
+      GLOBAL_quads[GLOBAL_quad_num].d = d;
+      GLOBAL_quad_num++;
+    }
+  }
+  finish = clock();
+
+  // printf("   Generate quad meshes: CPU Time = %f seconds
+  // \n",(double)(finish-begin)/CLOCKS_PER_SEC);
+  // printf("   vert-num : %d -- quad-num: %d \n\n",vert_num,GLOBAL_quad_num);
+
+  // Smooth the mesh
+  begin = clock();
+  unsigned char neighbor;
+
+  for (num = 0; num < 3; num++) {
+    for (n = 0; n < GLOBAL_vert_num; n++) {
+      nx = 0;
+      ny = 0;
+      nz = 0;
+      m = 0;
+      neighbor = GLOBAL_vertex[n].neigh;
+
+      i = GLOBAL_vertex[n].px;
+      j = GLOBAL_vertex[n].py;
+      k = GLOBAL_vertex[n].pz;
+
+      if (neighbor & 1) {
+        m++;
+        l = GLOBAL_atom_index[IndexVect(i - 1, j, k)];
+        nx += GLOBAL_vertex[l].x;
+        ny += GLOBAL_vertex[l].y;
+        nz += GLOBAL_vertex[l].z;
+      }
+
+      if (neighbor & 2) {
+        m++;
+        l = GLOBAL_atom_index[IndexVect(i + 1, j, k)];
+        nx += GLOBAL_vertex[l].x;
+        ny += GLOBAL_vertex[l].y;
+        nz += GLOBAL_vertex[l].z;
+      }
+
+      if (neighbor & 4) {
+        m++;
+        l = GLOBAL_atom_index[IndexVect(i, j - 1, k)];
+        nx += GLOBAL_vertex[l].x;
+        ny += GLOBAL_vertex[l].y;
+        nz += GLOBAL_vertex[l].z;
+      }
+
+      if (neighbor & 8) {
+        m++;
+        l = GLOBAL_atom_index[IndexVect(i, j + 1, k)];
+        nx += GLOBAL_vertex[l].x;
+        ny += GLOBAL_vertex[l].y;
+        nz += GLOBAL_vertex[l].z;
+      }
+
+      if (neighbor & 16) {
+        m++;
+        l = GLOBAL_atom_index[IndexVect(i, j, k - 1)];
+        nx += GLOBAL_vertex[l].x;
+        ny += GLOBAL_vertex[l].y;
+        nz += GLOBAL_vertex[l].z;
+      }
+
+      if (neighbor & 32) {
+        m++;
+        l = GLOBAL_atom_index[IndexVect(i, j, k + 1)];
+        nx += GLOBAL_vertex[l].x;
+        ny += GLOBAL_vertex[l].y;
+        nz += GLOBAL_vertex[l].z;
+      }
+
+      // update the position
+      GLOBAL_vertex[n].x = nx / (float)m;
+      GLOBAL_vertex[n].y = ny / (float)m;
+      GLOBAL_vertex[n].z = nz / (float)m;
+    }
+  }
+  finish = clock();
 
-            i = GLOBAL_vertex[n].px;
-            j = GLOBAL_vertex[n].py;
-            k = GLOBAL_vertex[n].pz;
+  // printf("   Smooth the quad meshes: CPU Time = %f seconds
+  // \n\n",(double)(finish-begin)/CLOCKS_PER_SEC);
 
-            if (neighbor & 1)
-            {
-                m++;
-                l   = GLOBAL_atom_index[IndexVect(i - 1, j, k)];
-                nx += GLOBAL_vertex[l].x;
-                ny += GLOBAL_vertex[l].y;
-                nz += GLOBAL_vertex[l].z;
-            }
+  // Allocate memory
+  std::unique_ptr mesh(new SurfaceMesh);
 
-            if (neighbor & 2)
-            {
-                m++;
-                l   = GLOBAL_atom_index[IndexVect(i + 1, j, k)];
-                nx += GLOBAL_vertex[l].x;
-                ny += GLOBAL_vertex[l].y;
-                nz += GLOBAL_vertex[l].z;
-            }
+  // write vertices
+  for (int i = 0; i < GLOBAL_vert_num; i++) {
+    float x = GLOBAL_vertex[i].x * span[0] + orig[0];
+    float y = GLOBAL_vertex[i].y * span[1] + orig[1];
+    float z = GLOBAL_vertex[i].z * span[2] + orig[2];
 
-            if (neighbor & 4)
-            {
-                m++;
-                l   = GLOBAL_atom_index[IndexVect(i, j - 1, k)];
-                nx += GLOBAL_vertex[l].x;
-                ny += GLOBAL_vertex[l].y;
-                nz += GLOBAL_vertex[l].z;
-            }
+    mesh->insert<1>({i}, SMVertex({x, y, z}));
+  }
 
-            if (neighbor & 8)
-            {
-                m++;
-                l   = GLOBAL_atom_index[IndexVect(i, j + 1, k)];
-                nx += GLOBAL_vertex[l].x;
-                ny += GLOBAL_vertex[l].y;
-                nz += GLOBAL_vertex[l].z;
-            }
+  // write triangles
+  float angle, angle1, angle2;
 
-            if (neighbor & 16)
-            {
-                m++;
-                l   = GLOBAL_atom_index[IndexVect(i, j, k - 1)];
-                nx += GLOBAL_vertex[l].x;
-                ny += GLOBAL_vertex[l].y;
-                nz += GLOBAL_vertex[l].z;
-            }
+  for (i = 0; i < GLOBAL_quad_num; i++) {
+    a = GLOBAL_quads[i].a;
+    b = GLOBAL_quads[i].b;
+    c = GLOBAL_quads[i].c;
+    d = GLOBAL_quads[i].d;
 
-            if (neighbor & 32)
-            {
-                m++;
-                l   = GLOBAL_atom_index[IndexVect(i, j, k + 1)];
-                nx += GLOBAL_vertex[l].x;
-                ny += GLOBAL_vertex[l].y;
-                nz += GLOBAL_vertex[l].z;
-            }
+    angle1 = -999.0;
+    angle2 = -999.0;
+    angle = GetAngle(a, b, c);
 
-            // update the position
-            GLOBAL_vertex[n].x = nx / (float)m;
-            GLOBAL_vertex[n].y = ny / (float)m;
-            GLOBAL_vertex[n].z = nz / (float)m;
-        }
+    if (angle > angle1) {
+      angle1 = angle;
     }
-    finish = clock();
-
-    // printf("   Smooth the quad meshes: CPU Time = %f seconds
-    // \n\n",(double)(finish-begin)/CLOCKS_PER_SEC);
+    angle = GetAngle(a, d, c);
 
-    // Allocate memory
-    std::unique_ptr mesh(new SurfaceMesh);
-
-    // write vertices
-    for (int i = 0; i < GLOBAL_vert_num; i++)
-    {
-        float x = GLOBAL_vertex[i].x * span[0] + orig[0];
-        float y = GLOBAL_vertex[i].y * span[1] + orig[1];
-        float z = GLOBAL_vertex[i].z * span[2] + orig[2];
-
-        mesh->insert<1>({i}, SMVertex({x, y, z}));
+    if (angle > angle1) {
+      angle1 = angle;
     }
+    angle = GetAngle(c, a, b);
 
-    // write triangles
-    float angle, angle1, angle2;
-
-    for (i = 0; i < GLOBAL_quad_num; i++)
-    {
-        a = GLOBAL_quads[i].a;
-        b = GLOBAL_quads[i].b;
-        c = GLOBAL_quads[i].c;
-        d = GLOBAL_quads[i].d;
-
-        angle1 = -999.0;
-        angle2 = -999.0;
-        angle  = GetAngle(a, b, c);
-
-        if (angle > angle1)
-        {
-            angle1 = angle;
-        }
-        angle = GetAngle(a, d, c);
-
-        if (angle > angle1)
-        {
-            angle1 = angle;
-        }
-        angle = GetAngle(c, a, b);
-
-        if (angle > angle1)
-        {
-            angle1 = angle;
-        }
-        angle = GetAngle(c, a, d);
-
-        if (angle > angle1)
-        {
-            angle1 = angle;
-        }
+    if (angle > angle1) {
+      angle1 = angle;
+    }
+    angle = GetAngle(c, a, d);
 
-        angle = GetAngle(b, a, d);
+    if (angle > angle1) {
+      angle1 = angle;
+    }
 
-        if (angle > angle2)
-        {
-            angle2 = angle;
-        }
-        angle = GetAngle(b, c, d);
+    angle = GetAngle(b, a, d);
 
-        if (angle > angle2)
-        {
-            angle2 = angle;
-        }
-        angle = GetAngle(d, a, b);
+    if (angle > angle2) {
+      angle2 = angle;
+    }
+    angle = GetAngle(b, c, d);
 
-        if (angle > angle2)
-        {
-            angle2 = angle;
-        }
-        angle = GetAngle(d, b, c);
+    if (angle > angle2) {
+      angle2 = angle;
+    }
+    angle = GetAngle(d, a, b);
 
-        if (angle > angle2)
-        {
-            angle2 = angle;
-        }
+    if (angle > angle2) {
+      angle2 = angle;
+    }
+    angle = GetAngle(d, b, c);
 
-        if (angle1 <= angle2)
-        {
-            mesh->insert<3>({a, b, c});
-            mesh->insert<3>({a, c, d});
-        }
-        else
-        {
-            mesh->insert<3>({a, b, d});
-            mesh->insert<3>({b, c, d});
-        }
+    if (angle > angle2) {
+      angle2 = angle;
     }
 
-    /* write to disk
-       FILE *fout;
-       if ((fout=fopen("output1.off", "wb"))==NULL){
-       printf("write error...\n");
-       exit(0);
-       };
-
-       fprintf(fout, "OFF\n");
-       fprintf(fout, "%d %d
-          %d\n",GLOBAL_vert_num,quad_num*2,GLOBAL_vert_num+quad_num*2-2);
-       for (i = 0; i < GLOBAL_vert_num; i++)
-       fprintf(fout, "%f %f %f
-          \n",surfmesh->vertex[i].x,surfmesh->vertex[i].y,surfmesh->vertex[i].z);
-
-       for (i = 0; i < quad_num*2; i++) {
-       fprintf(fout, "3 %d %d
-          %d\n",surfmesh->face[i].a,surfmesh->face[i].b,surfmesh->face[i].c);
-       }
-       fclose(fout);
-     */
-
-    free(AllSeeds);
-    free(GLOBAL_segment_index);
-    free(GLOBAL_atom_index);
-    free(min_heap->x);
-    free(min_heap->y);
-    free(min_heap->z);
-    free(min_heap->seed);
-    free(min_heap->dist);
-    free(min_heap);
-    free(GLOBAL_vertex);
-    free(GLOBAL_quads);
-
-    compute_orientation(*mesh);
-    return mesh;
+    if (angle1 <= angle2) {
+      mesh->insert<3>({a, b, c});
+      mesh->insert<3>({a, c, d});
+    } else {
+      mesh->insert<3>({a, b, d});
+      mesh->insert<3>({b, c, d});
+    }
+  }
+
+  /* write to disk
+     FILE *fout;
+     if ((fout=fopen("output1.off", "wb"))==NULL){
+     printf("write error...\n");
+     exit(0);
+     };
+
+     fprintf(fout, "OFF\n");
+     fprintf(fout, "%d %d
+        %d\n",GLOBAL_vert_num,quad_num*2,GLOBAL_vert_num+quad_num*2-2);
+     for (i = 0; i < GLOBAL_vert_num; i++)
+     fprintf(fout, "%f %f %f
+        \n",surfmesh->vertex[i].x,surfmesh->vertex[i].y,surfmesh->vertex[i].z);
+
+     for (i = 0; i < quad_num*2; i++) {
+     fprintf(fout, "3 %d %d
+        %d\n",surfmesh->face[i].a,surfmesh->face[i].b,surfmesh->face[i].c);
+     }
+     fclose(fout);
+   */
+
+  free(AllSeeds);
+  free(GLOBAL_segment_index);
+  free(GLOBAL_atom_index);
+  free(min_heap->x);
+  free(min_heap->y);
+  free(min_heap->z);
+  free(min_heap->seed);
+  free(min_heap->dist);
+  free(min_heap);
+  free(GLOBAL_vertex);
+  free(GLOBAL_quads);
+
+  compute_orientation(*mesh);
+  return mesh;
 }
 
 } // end namespace gamer
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 298e0148..ee4fdec0 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -27,10 +27,8 @@ endif()
 
 FetchContent_Declare(
     googletest
-    # URL https://github.com/google/googletest/archive/master.zip
-    # URL https://github.com/google/googletest/archive/release-1.8.1.tar.gz
     GIT_REPOSITORY    https://github.com/google/googletest.git
-    GIT_TAG           release-1.8.1
+    GIT_TAG           origin/master
     SOURCE_DIR        "${CMAKE_CURRENT_BINARY_DIR}/googletest-src"
     BINARY_DIR        "${CMAKE_CURRENT_BINARY_DIR}/googletest-build"
 )
@@ -38,25 +36,17 @@ FetchContent_Declare(
 # Prevent overriding the parent project's compiler/linker
 # settings on Windows
 set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
+FetchContent_MakeAvailable(googletest)
 
-FetchContent_GetProperties(googletest)
-if(NOT googletest_POPULATED)
-    FetchContent_Populate(googletest)
-    # Add googletest directly to our build. This defines
-    # the gtest and gtest_main targets.
-    add_subdirectory(${googletest_SOURCE_DIR}
-                     ${googletest_BINARY_DIR}
-                     EXCLUDE_FROM_ALL)
-endif()
-
-# The gtest/gtest_main targets carry header search path
-# dependencies automatically when using CMake 2.8.11 or
-# later. Otherwise we have to add them here ourselves.
-if (CMAKE_VERSION VERSION_LESS 2.8.11)
-  include_directories("${googletest_SOURCE_DIR}/include")
-endif()
+set(GAMER_TEST_SOURCES
+    "main.cpp" 
+    "VertexTest.cpp" 
+    "tensorTest.cpp" 
+    "SurfaceMeshTest.cpp"
+    "tetrahedralizationTest.cpp"
+)
 
-add_executable(objecttests main.cpp VertexTest.cpp tensorTest.cpp SurfaceMeshTest.cpp)
+add_executable(objecttests ${GAMER_TEST_SOURCES})
 target_link_libraries(objecttests gamerstatic gtest_main)
 # target_compile_options(objecttests PRIVATE -Werror -Wall -Weverything
 #           -Wextra -pedantic-errors -Wconversion -Wsign-conversion
@@ -73,7 +63,7 @@ if(NOT PYTEST_FOUND AND BUILD_PYGAMER)
 elseif(BUILD_PYGAMER)
     # Add a test for pytest
     add_test(NAME python-tests
-      COMMAND ${PYTHON_EXECUTABLE} -m pytest ${CMAKE_CURRENT_SOURCE_DIR}
+      COMMAND ${Python_EXECUTABLE} -m pytest ${CMAKE_CURRENT_SOURCE_DIR}
       WORKING_DIRECTORY $
     )
 endif()
diff --git a/tests/SurfaceMeshTest.cpp b/tests/SurfaceMeshTest.cpp
index 38191017..d4dff4e5 100644
--- a/tests/SurfaceMeshTest.cpp
+++ b/tests/SurfaceMeshTest.cpp
@@ -1,3 +1,22 @@
+// This file is part of the GAMer software.
+// Copyright (C) 2016-2021
+// by Christopher T. Lee and contributors
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, see 
+// or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+// Boston, MA 02111-1307 USA
+
 
 #include 
 #include 
@@ -64,4 +83,4 @@ TEST_F(SurfaceMeshTest, Smooth){
     EXPECT_EQ(fbefore, 80);
 }
 
-} // end namespace gamer
\ No newline at end of file
+} // end namespace gamer
diff --git a/tests/VertexTest.cpp b/tests/VertexTest.cpp
index f9d3e122..09ddc6ae 100644
--- a/tests/VertexTest.cpp
+++ b/tests/VertexTest.cpp
@@ -1,3 +1,23 @@
+// This file is part of the GAMer software.
+// Copyright (C) 2016-2021
+// by Christopher T. Lee and contributors
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, see 
+// or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+// Boston, MA 02111-1307 USA
+
+
 #define _USE_MATH_DEFINES
 #include 
 #include 
diff --git a/tests/scratchtest.cpp b/tests/scratchtest.cpp
index be44f157..768cad93 100644
--- a/tests/scratchtest.cpp
+++ b/tests/scratchtest.cpp
@@ -31,7 +31,7 @@ int  main(int argc, char *argv[])
         gamer::flipNormals(*mesh);
     }
 
-    gamer::curvatureViaJets(*mesh, 2, 2);
-
+    // gamer::curvatureViaJets(*mesh, 2, 2);
+    gamer::writeComsol("test.mphtxt", *mesh);
     std::cout << "EOF" << std::endl;
-}
\ No newline at end of file
+}
diff --git a/tests/tensorTest.cpp b/tests/tensorTest.cpp
index b992a468..45a26466 100644
--- a/tests/tensorTest.cpp
+++ b/tests/tensorTest.cpp
@@ -1,3 +1,22 @@
+// This file is part of the GAMer software.
+// Copyright (C) 2016-2021
+// by Christopher T. Lee and contributors
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, see 
+// or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+// Boston, MA 02111-1307 USA
+
 
 #include 
 #include 
@@ -110,4 +129,4 @@ TEST(TensorTest, Constructor){
         ASSERT_EQ(v3.get((*it)[0],(*it)[1],(*it)[2]), 5);
     }
 }
-} // end namespace gamer
\ No newline at end of file
+} // end namespace gamer
diff --git a/tests/tetrahedralizationTest.cpp b/tests/tetrahedralizationTest.cpp
new file mode 100644
index 00000000..83100cfc
--- /dev/null
+++ b/tests/tetrahedralizationTest.cpp
@@ -0,0 +1,107 @@
+// This file is part of the GAMer software.
+// Copyright (C) 2016-2021
+// by Christopher T. Lee and contributors
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, see 
+// or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+// Boston, MA 02111-1307 USA
+
+#include "gamer/SurfaceMesh.h"
+#include "gamer/TetMesh.h"
+#include "gtest/gtest.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+/// Namespace for all things gamer
+namespace gamer {
+
+class TetrahedralizationTest : public testing::Test {
+protected:
+  TetrahedralizationTest() {}
+  ~TetrahedralizationTest() {}
+  virtual void SetUp() {
+    outermesh = sphere(0);
+    scale(*outermesh, outerScaleFactor);
+
+    auto &omGlobal = outermesh->get_simplex_up().data();
+    omGlobal.ishole = false;
+    omGlobal.marker = volumeMarker;
+    for (auto &fdata : outermesh->get_level<3>()) {
+      fdata.marker = outerSurfaceMarker;
+    }
+
+    for (auto &vdata : outermesh->get_level<1>()) {
+      EXPECT_TRUE((vdata.position | vdata.position) -
+                      (outerScaleFactor * outerScaleFactor) <
+                  tolerance);
+    }
+
+    innermesh = sphere(0);
+    scale(*innermesh, innerScaleFactor);
+    auto &imGlobal = innermesh->get_simplex_up().data();
+    imGlobal.ishole = true;
+
+    for (auto &fdata : innermesh->get_level<3>()) {
+      fdata.marker = innerSurfaceMarker;
+    }
+
+    for (auto &vdata : innermesh->get_level<1>()) {
+      EXPECT_TRUE((vdata.position | vdata.position) -
+                      (innerScaleFactor * innerScaleFactor) <
+                  tolerance);
+    }
+  }
+  virtual void TearDown() {}
+
+  std::unique_ptr outermesh;
+  std::unique_ptr innermesh;
+  int volumeMarker = 25;
+  int outerSurfaceMarker = 2;
+  int innerSurfaceMarker = 7;
+  double innerScaleFactor = 2;
+  double outerScaleFactor = 7;
+  double tolerance = 1e-3;
+};
+
+TEST_F(TetrahedralizationTest, tetrahedralize) {
+  std::vector meshes{outermesh.get(), innermesh.get()};
+  auto tetmesh = makeTetMesh(meshes, "q1.3/10a1O8/7AYCQ");
+  EXPECT_TRUE(tetmesh->size<4>() > 0);
+  EXPECT_TRUE(tetmesh->size<3>() > outermesh->size<3>() + innermesh->size<3>());
+  EXPECT_TRUE(tetmesh->size<2>() > outermesh->size<2>() + innermesh->size<2>());
+  EXPECT_TRUE(tetmesh->size<1>() > outermesh->size<1>() + innermesh->size<1>());
+
+  for (auto &tetData : tetmesh->get_level<4>()) {
+    EXPECT_EQ(tetData.marker, volumeMarker);
+  }
+
+  for (auto fID : tetmesh->get_level_id<3>()) {
+    if (tetmesh->onBoundary(fID)) {
+      EXPECT_TRUE(fID.data().marker == outerSurfaceMarker ||
+                  fID.data().marker == innerSurfaceMarker);
+    }
+  }
+
+  for (auto &vData : tetmesh->get_level<1>()) {
+    double dist = vData.position | vData.position;
+    EXPECT_TRUE(dist - (innerScaleFactor * innerScaleFactor) > -tolerance);
+    EXPECT_TRUE(dist - (outerScaleFactor * outerScaleFactor) < tolerance);
+  }
+}
+
+} // end namespace gamer
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
index a6c2b4b7..584ae6a3 100644
--- a/tools/CMakeLists.txt
+++ b/tools/CMakeLists.txt
@@ -19,43 +19,38 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 # ***************************************************************************
 
-
 set(_BPY_STRICT FALSE)
 if(BLENDER_PLUGIN_INSTALL OR BLENDER_VERSION_STRICT)
     set(_BPY_STRICT TRUE)
 
-    execute_process(COMMAND "${PYTHON_EXECUTABLE}" "-c"
-    "import sys;import struct;
-print('.'.join(str(v) for v in sys.version_info[0:3]));
-print(struct.calcsize('@P'));
-"
+    execute_process(COMMAND "${Python_EXECUTABLE}" "-c"
+    "import sys;import struct;print(struct.calcsize('@P'));"
         RESULT_VARIABLE _PYTHON_SUCCESS
         OUTPUT_VARIABLE _PYTHON_VALUES
         ERROR_VARIABLE _PYTHON_ERROR_VALUE)
 
     if(NOT _PYTHON_SUCCESS MATCHES 0)
         message(FATAL_ERROR
-            "Python config failure:\n${_PYTHON_ERROR_VALUE}")
+            "Python config failure:\n${_PYTHON_VALUES}\n${_PYTHON_ERROR_VALUE}")
     endif()
 
     # Convert the process output into a list
     string(REGEX REPLACE "\n" ";" _PYTHON_VALUES ${_PYTHON_VALUES})
-    list(GET _PYTHON_VALUES 0 PYTHON_VERSION)
-    list(GET _PYTHON_VALUES 1 PYTHON_SIZEOF_VOID_P)
+    list(GET _PYTHON_VALUES 0 PYTHON_SIZEOF_VOID_P)
 endif()
 
 
 # If strict then ensure system Python matches Blender's closely
 if(_BPY_STRICT)
     find_package(Blender REQUIRED)
-    set(_PYTHON_CMP_VERSION "${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}"
+    set(_PYTHON_CMP_VERSION "${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}"
         )
     set(_BPY_CMP_VERSION "${BLENDER_PYTHON_VERSION_MAJOR}.${BLENDER_PYTHON_VERSION_MINOR}")
     if(NOT _PYTHON_CMP_VERSION VERSION_EQUAL _BPY_CMP_VERSION)
         message(FATAL_ERROR
             "Blender Addon Config Failure: "
             "Blender embedded Python version (${BLENDER_PYTHON_VERSION}) "
-            "does not match the linked version of Python (${PYTHON_VERSION}).")
+            "does not match the linked version of Python (${Python_VERSION}).")
     elseif(NOT PYTHON_SIZEOF_VOID_P EQUAL BLENDER_PYTHON_SIZEOF_VOID_P)
         math(EXPR _PYTHON_BITS "${PYTHON_SIZEOF_VOID_P} * 8")
         math(EXPR _BLENDER_BITS "${BLENDER_PYTHON_SIZEOF_VOID_P} * 8")
@@ -97,13 +92,20 @@ add_dependencies(stage_files pygamer)
 # Generate the name of the build
 set(_ZIPNAME "blendgamer-${VERSION_SHORT}-")
 if(VERSION_INFO)
-    string(CONCAT _ZIPNAME ${_ZIPNAME} "${VERSION_INFO}-${VERSION_SHA1}-")
+    string(CONCAT _ZIPNAME ${_ZIPNAME} "${VERSION_INFO}-")
+    if(VERSION_SHA1)
+        string(CONCAT _ZIPNAME ${_ZIPNAME} "${VERSION_SHA1}-")
+    endif()
     if(VERSION_DIRTY)
         string(CONCAT _ZIPNAME ${_ZIPNAME} "${VERSION_DIRTY}-")
     endif()
 endif()
 
-string(CONCAT _ZIPNAME ${_ZIPNAME} "b${BLENDER_VERSION_MAJOR}.${BLENDER_VERSION_MINOR}-")
+if(BLENDER_VERSION_OVERRIDE)
+    string(CONCAT _ZIPNAME ${_ZIPNAME} "b${BLENDER_VERSION_OVERRIDE}-") 
+else()
+    string(CONCAT _ZIPNAME ${_ZIPNAME} "b${BLENDER_VERSION_MAJOR}.${BLENDER_VERSION_MINOR}-")
+endif()
 
 string(CONCAT _ZIPNAME ${_ZIPNAME} "${CMAKE_HOST_SYSTEM_NAME}")
 
@@ -137,7 +139,7 @@ add_custom_target(zip_plugin
         ../${_ZIPNAME} --format=zip
         blendgamer
     WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/plugin
-    COMMENT "Zipping up addon components"
+    COMMENT "Zipping up addon components to: ${_ZIPNAME}"
     VERBATIM
 )
 add_dependencies(zip_plugin stage_files)
diff --git a/tools/blendgamer/__init__.py.in b/tools/blendgamer/__init__.py.in
index a25df3f0..122761bd 100644
--- a/tools/blendgamer/__init__.py.in
+++ b/tools/blendgamer/__init__.py.in
@@ -1,31 +1,30 @@
 # ***************************************************************************
 # This file is part of the GAMer software.
-# Copyright (C) 2016-2018
-# by Christopher Lee, Tom Bartol, John Moody, Rommie Amaro, J. Andrew McCammon,
-#    and Michael Holst
-
+# Copyright (C) 2016-2021
+# by Christopher T. Lee and contributors
+#
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
 # License as published by the Free Software Foundation; either
 # version 2.1 of the License, or (at your option) any later version.
-
+#
 # This library is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 # Lesser General Public License for more details.
-
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-# ***************************************************************************
 #
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, see 
+# or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+# Boston, MA 02111-1307 USA
+# **************************************************************************
 
 bl_info = {
         "name": "GAMer: Geometry-preserving Adaptive Mesher",
         "description": "Utilities for generating finite elements simulation compatible meshes",
-        "author": "Christopher T. Lee, John B. Moody, Zeyun Yu, Tom Bartol, Johan Hake, and Michael Holst",
+        "author": "Christopher T. Lee, Justin Laughlin, John B. Moody, Zeyun Yu, Tom Bartol, Johan Hake, and Michael Holst",
         "version": (@PROJECT_VERSION_MAJOR@, @PROJECT_VERSION_MINOR@, @PROJECT_VERSION_PATCH@),
-        "blender": (2, 80, 0),
+        "blender": (2, 93, 0),
         "location": "3D View > Tool Shelf",
         "wiki_url": "https://github.com/ctlee/gamer",
         "tracker_url": "https://github.com/ctlee/gamer/issues",
@@ -61,6 +60,11 @@ else:
 import bpy
 from bpy.props import PointerProperty
 
+if bpy.app.version < (2, 90, 0) and bpy.app.version >= (2, 80, 0):
+    bl_info['blender'] = (2, 83, 0)
+elif bpy.app.version < (2, 80, 0):
+    bl_info['blender'] = (2, 79, 0)
+
 # Tuple of modules to register
 modules = (curvatures,
            markers,
diff --git a/tools/blendgamer/src/blendgamer.py b/tools/blendgamer/src/blendgamer.py
index 147f000e..15001440 100644
--- a/tools/blendgamer/src/blendgamer.py
+++ b/tools/blendgamer/src/blendgamer.py
@@ -1,37 +1,46 @@
 # ***************************************************************************
 # This file is part of the GAMer software.
-# Copyright (C) 2016-2018
-# by Christopher Lee, Tom Bartol, John Moody, Rommie Amaro, J. Andrew McCammon,
-#    and Michael Holst
-
+# Copyright (C) 2016-2021
+# by Christopher T. Lee and contributors
+#
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
 # License as published by the Free Software Foundation; either
 # version 2.1 of the License, or (at your option) any later version.
-
+#
 # This library is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 # Lesser General Public License for more details.
-
+#
 # You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+# License along with this library; if not, see 
+# or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+# Boston, MA 02111-1307 USA
 # ***************************************************************************
 
 import bpy
-from bpy.props import (BoolProperty, CollectionProperty, EnumProperty,
-                       FloatProperty, FloatVectorProperty, IntProperty, IntVectorProperty,
-                       PointerProperty, StringProperty, BoolVectorProperty)
+from bpy.props import (
+    BoolProperty,
+    CollectionProperty,
+    EnumProperty,
+    FloatProperty,
+    FloatVectorProperty,
+    IntProperty,
+    IntVectorProperty,
+    PointerProperty,
+    StringProperty,
+    BoolVectorProperty,
+)
 from bpy.app.handlers import persistent
 
 from blendgamer.surfacemesh_ops import SurfaceMeshImprovementProperties
-from blendgamer.versions import (checkVersion, getGamerVersion)
+from blendgamer.versions import checkVersion, getGamerVersion
 from blendgamer.meshstats import MeshQualityReportProperties
 from blendgamer.tetrahedralization import GAMerTetrahedralizationPropertyGroup
 from blendgamer.markers import GAMerBoundaryMarkersList
 from blendgamer.curvatures import GAMerCurvaturesList
-from blendgamer.util import UNSETID, make_annotations, getMatByBndID, getBndUnsetMat
+from blendgamer.util import UNSETID, make_annotations, get_material_by_bnd_id, getBndUnsetMat
 
 import blendgamer.pygamer as pygamer
 
@@ -48,8 +57,10 @@ def gamer_load_post(dummy):
     plugin version matches the metadata layout stored in a blendfile. This
     function also sets a flag noting whether matplotlib is installed or not.
     """
-    print('Loading BlendGAMer v%s with PyGAMer %s' %
-          (getGamerVersion(), pygamer.__version__()))
+    print(
+        "Loading BlendGAMer v%s with PyGAMer %s"
+        % (getGamerVersion(), pygamer.__version__())
+    )
     scene = bpy.context.scene
     scene.gamer.check_for_matplotlib()
     if not scene.gamer.initialized:
@@ -67,30 +78,28 @@ class GAMerAddonProperties(bpy.types.PropertyGroup):
     """
     Property group to store GAMer addon metadata
     """
+
     initialized = BoolProperty(name="GAMer Initialized", default=False)
-    matplotlib_found = BoolProperty(
-        name="Is matplotlib available", default=False)
+    matplotlib_found = BoolProperty(name="Is matplotlib available", default=False)
     gamer_version = StringProperty(name="GAMer Version", default="0")
     boundary_id_counter = IntProperty(name="GAMer Boundary id Counter")
     versionerror = IntProperty(name="Version mismatch", default=0)
 
     surfmesh_improvement_properties = PointerProperty(
         type=SurfaceMeshImprovementProperties,
-        name="GAMer Surface Mesh Improvement Properties"
+        name="GAMer Surface Mesh Improvement Properties",
     )
 
     mesh_quality_properties = PointerProperty(
-        type=MeshQualityReportProperties,
-        name="GAMer Mesh Quality Reporting"
+        type=MeshQualityReportProperties, name="GAMer Mesh Quality Reporting"
     )
 
     tet_group = PointerProperty(
-        type=GAMerTetrahedralizationPropertyGroup,
-        name="GAMer Tetrahedralization"
+        type=GAMerTetrahedralizationPropertyGroup, name="GAMer Tetrahedralization"
     )
 
     def allocate_boundary_id(self):
-        """Allocate the next avilable boundary ID.
+        """Allocate the next available boundary ID.
 
         Returns
         -------
@@ -101,21 +110,20 @@ def allocate_boundary_id(self):
         return self.boundary_id_counter
 
     def init_properties(self):
-        """Initialize BlendGAMer addon properties
-        """
+        """Initialize BlendGAMer addon properties"""
         self.gamer_version = str(getGamerVersion())
         self.boundary_id_counter = 0  # Start counting at 0
 
-        if 'bnd_unset_mat' not in bpy.data.materials:
-            bnd_unset_mat = bpy.data.materials.new('bnd_unset_mat')
+        if "bnd_unset_mat" not in bpy.data.materials:
+            bnd_unset_mat = bpy.data.materials.new("bnd_unset_mat")
             bnd_unset_mat.use_fake_user = True
             bnd_unset_mat.gamer.boundary_id = UNSETID
             self.initialized = True
 
     def check_for_matplotlib(self):
-        """Check if matplotlib is available and set an internal flag.
-        """
+        """Check if matplotlib is available and set an internal flag."""
         import importlib.util
+
         mpl_spec = importlib.util.find_spec("matplotlib")
         self.matplotlib_found = mpl_spec is not None
 
@@ -124,27 +132,19 @@ class GAMerObjectProperties(bpy.types.PropertyGroup):
     """
     PropertyGroup of properties to link into Blender Objects
     """
-    markers = PointerProperty(
-        type=GAMerBoundaryMarkersList,
-        name="Boundary Markers"
-    )
-    curvatures = PointerProperty(
-        type=GAMerCurvaturesList,
-        name="Curvature Lists"
-    )
+
+    markers = PointerProperty(type=GAMerBoundaryMarkersList, name="Boundary Markers")
+    curvatures = PointerProperty(type=GAMerCurvaturesList, name="Curvature Lists")
 
 
-classes = [GAMerAddonProperties,
-           GAMerObjectProperties]
+classes = [GAMerAddonProperties, GAMerObjectProperties]
 
 
 def register():
-    from bpy.utils import register_class
     for cls in classes:
-        register_class(make_annotations(cls))
+        bpy.utils.register_class(make_annotations(cls))
 
 
 def unregister():
-    from bpy.utils import unregister_class
     for cls in reversed(classes):
-        unregister_class(make_annotations(cls))
+        bpy.utils.unregister_class(make_annotations(cls))
diff --git a/tools/blendgamer/src/colormap.py b/tools/blendgamer/src/colormap.py
index 5ee06a01..1cdb8bfb 100644
--- a/tools/blendgamer/src/colormap.py
+++ b/tools/blendgamer/src/colormap.py
@@ -1,22 +1,22 @@
 # ***************************************************************************
 # This file is part of the GAMer software.
-# Copyright (C) 2016-2018
-# by Christopher Lee, Tom Bartol, John Moody, Rommie Amaro, J. Andrew McCammon,
-#    and Michael Holst
-
+# Copyright (C) 2016-2021
+# by Christopher T. Lee and contributors
+#
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
 # License as published by the Free Software Foundation; either
 # version 2.1 of the License, or (at your option) any later version.
-
+#
 # This library is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 # Lesser General Public License for more details.
-
+#
 # You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+# License along with this library; if not, see 
+# or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+# Boston, MA 02111-1307 USA
 # ***************************************************************************
 
 import bpy
@@ -29,15 +29,16 @@
 # $ pip install pyqt5
 # mpl.use('QT5Agg')
 
-mpl.rcParams['pdf.fonttype'] = 42
-mpl.rcParams['ps.fonttype'] = 42
+mpl.rcParams["pdf.fonttype"] = 42
+mpl.rcParams["ps.fonttype"] = 42
 
 # Mapping colormap_enums.colormap_enums to values
 colormapDict = {
-  'VIRIDIS': plt.cm.viridis,
-  'PRGN' : plt.cm.PRGn,
+    "VIRIDIS": plt.cm.viridis,
+    "PRGN": plt.cm.PRGn,
 }
 
+
 class DivergingNorm(mpl.colors.Normalize):
     def __init__(self, vcenter, vmin=None, vmax=None):
         """
@@ -75,8 +76,10 @@ def __init__(self, vcenter, vmin=None, vmax=None):
         self.vmin = vmin
         self.vmax = vmax
         if not (vcenter and vmin and vmax) and (vcenter >= vmax or vcenter <= vmin):
-            raise ValueError('vmin(%f), vcenter(%f), and vmax(%f) must be in '
-                             'ascending order'%(vmin, vcenter, vmax))
+            raise ValueError(
+                "vmin(%f), vcenter(%f), and vmax(%f) must be in "
+                "ascending order" % (vmin, vcenter, vmax)
+            )
 
     def autoscale_None(self, A):
         """
@@ -88,7 +91,6 @@ def autoscale_None(self, A):
         if self.vmax < self.vcenter:
             self.vmax = self.vcenter
 
-
     def __call__(self, value, clip=False):
         """
         Map value to the interval [0, 1].
@@ -99,8 +101,9 @@ def __call__(self, value, clip=False):
         if not self.vmin <= self.vcenter <= self.vmax:
             raise ValueError("vmin, vcenter, vmax must increase monotonically")
         result = np.ma.masked_array(
-            np.interp(result, [self.vmin, self.vcenter, self.vmax],
-                      [0, 0.5, 1.]), mask=np.ma.getmask(result))
+            np.interp(result, [self.vmin, self.vcenter, self.vmax], [0, 0.5, 1.0]),
+            mask=np.ma.getmask(result),
+        )
         if is_scalar:
             result = np.atleast_1d(result)[0]
         return result
@@ -130,7 +133,7 @@ def curveToData(crv, context):
 
     data = np.zeros(len(obj.data.vertices), dtype=np.float)
     # Copy curvatures over
-    layer.foreach_get('value', data)
+    layer.foreach_get("value", data)
 
     if crv.curveIter > 0:
         for i in range(0, crv.curveIter):
@@ -167,17 +170,18 @@ def dataToVertexColor(crv, context, showplot=False, saveplot=False):
 
     data = curveToData(crv, context)
     cmap = colormapDict[crv.colormap]
-    file_prefix = "%s_%s_m%dM%dI%dmx%0.2f%s"%(
-            bpy.path.basename(bpy.context.blend_data.filepath).split('.')[0],
-            context.object.name,
-            crv.minCurve,
-            crv.maxCurve,
-            crv.curveIter,
-            crv.mixpoint,
-            crv.curvatureType)
-
-    fig = plt.figure(figsize=(8,5))
-    ax = fig.add_axes([0.1,0.05,0.6,0.9])
+    file_prefix = "%s_%s_m%dM%dI%dmx%0.2f%s" % (
+        bpy.path.basename(bpy.context.blend_data.filepath).split(".")[0],
+        context.object.name,
+        crv.minCurve,
+        crv.maxCurve,
+        crv.curveIter,
+        crv.mixpoint,
+        crv.curvatureType,
+    )
+
+    fig = plt.figure(figsize=(8, 5))
+    ax = fig.add_axes([0.1, 0.05, 0.6, 0.9])
 
     ## This code helps make the plots easier to read...
     # tmin = np.percentile(data,1)
@@ -193,12 +197,12 @@ def dataToVertexColor(crv, context, showplot=False, saveplot=False):
     amedian = np.median(data)
 
     if showplot:
-        ax.hist(data, bins='auto')
-        ax.set_title("%s Distribution"%(file_prefix))
-        ax.axvline(amin, color='r', linestyle='dashed', linewidth=1)
-        ax.axvline(amax, color='r', linestyle='dashed', linewidth=1)
+        ax.hist(data, bins="auto")
+        ax.set_title("%s Distribution" % (file_prefix))
+        ax.axvline(amin, color="r", linestyle="dashed", linewidth=1)
+        ax.axvline(amax, color="r", linestyle="dashed", linewidth=1)
 
-    extend = 'neither'
+    extend = "neither"
     # the tmin/tmax values are percentiles instead
     if crv.limitsArePercentiles:
         # print("Using min/max values as percentiles!")
@@ -214,23 +218,26 @@ def dataToVertexColor(crv, context, showplot=False, saveplot=False):
         else:
             upperPercentile = int(tmax)
 
-        tmin = np.percentile(data,lowerPercentile)
-        tmax = np.percentile(data,upperPercentile)
-        print("Data truncated at %f and %f percentiles\n"%(lowerPercentile,upperPercentile) )
+        tmin = np.percentile(data, lowerPercentile)
+        tmax = np.percentile(data, upperPercentile)
+        print(
+            "Data truncated at %f and %f percentiles\n"
+            % (lowerPercentile, upperPercentile)
+        )
 
     if showplot:
-        ax.axvline(tmin, color='g', linestyle='dashed', linewidth=2)
-        ax.axvline(tmax, color='g', linestyle='dashed', linewidth=2)
+        ax.axvline(tmin, color="g", linestyle="dashed", linewidth=2)
+        ax.axvline(tmax, color="g", linestyle="dashed", linewidth=2)
 
         if tmin > amin and tmax < amax:
-            extend = 'both'
+            extend = "both"
         elif tmin > amin:
-            extend = 'min'
+            extend = "min"
         elif tmax < amax:
-            extend = 'max'
+            extend = "max"
 
-    data[data < tmin] =tmin
-    data[data > tmax] =tmax
+    data[data < tmin] = tmin
+    data[data > tmax] = tmax
 
     amin = np.amin(data)
     amax = np.amax(data)
@@ -249,7 +256,9 @@ def dataToVertexColor(crv, context, showplot=False, saveplot=False):
         colors_pos = cmap(np.linspace(crv.mixpoint, 1, 256))
 
         all_colors = np.vstack((colors_neg, colors_pos))
-        curvature_map = mpl.colors.LinearSegmentedColormap.from_list('curvature_map', all_colors)
+        curvature_map = mpl.colors.LinearSegmentedColormap.from_list(
+            "curvature_map", all_colors
+        )
     else:
         norm = mpl.colors.Normalize(vmin=amin, vmax=amax)
         curvature_map = cmap
@@ -258,15 +267,17 @@ def dataToVertexColor(crv, context, showplot=False, saveplot=False):
     colors = curvature_map(norm(data))
 
     # Create view without alpha channel if Blender < 2.80
-    if bpy.app.version < (2,80,0):
-        colors = colors[:,:3]
+    if bpy.app.version < (2, 80, 0):
+        colors = colors[:, :3]
 
     mesh = bpy.context.object.data
-    vlayer = "%s%s"%(crv.algorithm, crv.curvatureType)
+    vlayer = "%s%s" % (crv.algorithm, crv.curvatureType)
 
     if vlayer not in mesh.vertex_colors:
         if len(mesh.vertex_colors) == 8:
-            raise RuntimeError("Maximum of 8 vertex Layers reached cannot create a new layer. Please delete a layer to continue.")
+            raise RuntimeError(
+                "Maximum of 8 vertex Layers reached cannot create a new layer. Please delete a layer to continue."
+            )
         mesh.vertex_colors.new(name=vlayer)
 
     color_layer = mesh.vertex_colors[vlayer]
@@ -277,9 +288,10 @@ def dataToVertexColor(crv, context, showplot=False, saveplot=False):
     color_layer.data.foreach_set("color", colors[mloops].flatten())
 
     # Add axis for colorbar and plot it
-    ax = fig.add_axes([0.75,0.05,0.05,0.9])
-    cb = mpl.colorbar.ColorbarBase(ax, cmap=curvature_map, norm=norm,
-                orientation='vertical')
+    ax = fig.add_axes([0.75, 0.05, 0.05, 0.9])
+    cb = mpl.colorbar.ColorbarBase(
+        ax, cmap=curvature_map, norm=norm, orientation="vertical"
+    )
 
     ticks = cb.get_ticks()
     ticks.sort()
@@ -292,39 +304,39 @@ def dataToVertexColor(crv, context, showplot=False, saveplot=False):
 
     ticklabels = [r"{:0.1f}".format(tick) for tick in ticks]
 
-    if extend == 'neither':
-       pass
-    elif extend == 'both':
+    if extend == "neither":
+        pass
+    elif extend == "both":
         ticklabels[0] = "< " + ticklabels[0]
         ticklabels[-1] = "> " + ticklabels[-1]
-    elif extend == 'max':
+    elif extend == "max":
         ticklabels[-1] = "> " + ticklabels[-1]
-    elif extend == 'min':
+    elif extend == "min":
         ticklabels[0] = "< " + ticklabels[0]
     cb.set_ticklabels(ticklabels)
     cb.ax.tick_params(labelsize=14)
-    cb.set_label("%s [$\mu m^{-1}$]"%(vlayer), size=16)
+    cb.set_label("%s [$\mu m^{-1}$]" % (vlayer), size=16)
 
     if saveplot:
-        plt.savefig(file_prefix+'.pdf', format='pdf')
+        plt.savefig(file_prefix + ".pdf", format="pdf")
     if showplot:
         plt.show()
     plt.close()
 
 
-def differencePlotter(context, difftype='K1'):
+def differencePlotter(context, difftype="K1"):
     obj = getActiveMeshObject()
     bm = bmesh_from_object(obj)
 
     with ObjectMode():
-        mdsb = getCurvatureLayer(obj, 'MDSB', difftype)
-        jets = getCurvatureLayer(obj, 'JETS', difftype)
+        mdsb = getCurvatureLayer(obj, "MDSB", difftype)
+        jets = getCurvatureLayer(obj, "JETS", difftype)
 
         mdsb_data = np.zeros(len(obj.data.vertices), dtype=np.float)
-        mdsb.foreach_get('value', mdsb_data)
+        mdsb.foreach_get("value", mdsb_data)
 
         jets_data = np.zeros(len(obj.data.vertices), dtype=np.float)
-        jets.foreach_get('value', jets_data)
+        jets.foreach_get("value", jets_data)
 
         tmpmdsb = np.zeros(len(obj.data.vertices), dtype=np.float)
         tmpjets = np.zeros(len(obj.data.vertices), dtype=np.float)
@@ -345,42 +357,42 @@ def differencePlotter(context, difftype='K1'):
         jets_data = np.array(tmpjets, copy=True)
     data = mdsb_data - jets_data
 
-    cmap = colormapDict['PRGN']
-    file_prefix = "%s_difference"%(difftype)
+    cmap = colormapDict["PRGN"]
+    file_prefix = "%s_difference" % (difftype)
 
-    fig = plt.figure(figsize=(8,5))
-    ax = fig.add_axes([0.1,0.05,0.6,0.9])
+    fig = plt.figure(figsize=(8, 5))
+    ax = fig.add_axes([0.1, 0.05, 0.6, 0.9])
 
     amin = np.amin(data)
     amax = np.amax(data)
     amean = np.mean(data)
     amedian = np.median(data)
 
-    plt.hist(data, bins='auto')
-    plt.title("%s Distribution"%(file_prefix))
-    plt.axvline(amin, color='r', linestyle='dashed', linewidth=1)
-    plt.axvline(amax, color='r', linestyle='dashed', linewidth=1)
+    plt.hist(data, bins="auto")
+    plt.title("%s Distribution" % (file_prefix))
+    plt.axvline(amin, color="r", linestyle="dashed", linewidth=1)
+    plt.axvline(amax, color="r", linestyle="dashed", linewidth=1)
 
     # Save full data
-    np.savez(context.object.name+'difference'+difftype+'.npz', data)
-    extend = 'neither'
+    np.savez(context.object.name + "difference" + difftype + ".npz", data)
+    extend = "neither"
     # tmin = amin
     # tmax = amax
-    tmin = np.percentile(data,1)
-    tmax = np.percentile(data,99)
+    tmin = np.percentile(data, 1)
+    tmax = np.percentile(data, 99)
 
-    ax.axvline(tmin, color='g', linestyle='dashed', linewidth=2)
-    ax.axvline(tmax, color='g', linestyle='dashed', linewidth=2)
+    ax.axvline(tmin, color="g", linestyle="dashed", linewidth=2)
+    ax.axvline(tmax, color="g", linestyle="dashed", linewidth=2)
 
     if tmin > amin and tmax < amax:
-        extend = 'both'
+        extend = "both"
     elif tmin > amin:
-        extend = 'min'
+        extend = "min"
     elif tmax < amax:
-        extend = 'max'
+        extend = "max"
 
-    data[data < tmin] =tmin
-    data[data > tmax] =tmax
+    data[data < tmin] = tmin
+    data[data > tmax] = tmax
 
     amin = np.amin(data)
     amax = np.amax(data)
@@ -390,11 +402,13 @@ def differencePlotter(context, difftype='K1'):
         norm = DivergingNorm(vmin=amin, vcenter=0, vmax=amax)
         # Python 3.5 matplotlib may not support?
         # norm = mpl.colors.DivergingNorm(vmin=amin, vcenter=0, vmax=amax)
-        colors_neg = cmap(np.linspace(0, .5, 256))
-        colors_pos = cmap(np.linspace(.5, 1, 256))
+        colors_neg = cmap(np.linspace(0, 0.5, 256))
+        colors_pos = cmap(np.linspace(0.5, 1, 256))
 
         all_colors = np.vstack((colors_neg, colors_pos))
-        curvature_map = mpl.colors.LinearSegmentedColormap.from_list('curvature_map', all_colors)
+        curvature_map = mpl.colors.LinearSegmentedColormap.from_list(
+            "curvature_map", all_colors
+        )
     else:
         norm = mpl.colors.Normalize(vmin=amin, vmax=amax)
         curvature_map = cmap
@@ -403,11 +417,11 @@ def differencePlotter(context, difftype='K1'):
     colors = curvature_map(norm(data))
 
     # Create view without alpha channel if Blender < 2.80
-    if bpy.app.version < (2,80,0):
-        colors = colors[:,:3]
+    if bpy.app.version < (2, 80, 0):
+        colors = colors[:, :3]
 
     mesh = bpy.context.object.data
-    vlayer = "%s_diff"%(difftype)
+    vlayer = "%s_diff" % (difftype)
 
     if vlayer not in mesh.vertex_colors:
         mesh.vertex_colors.new(name=vlayer)
@@ -420,9 +434,10 @@ def differencePlotter(context, difftype='K1'):
     color_layer.data.foreach_set("color", colors[mloops].flatten())
 
     # Add axis for colorbar and plot it
-    ax = fig.add_axes([0.75,0.05,0.05,0.9])
-    cb = mpl.colorbar.ColorbarBase(ax, cmap=curvature_map, norm=norm,
-                orientation='vertical')
+    ax = fig.add_axes([0.75, 0.05, 0.05, 0.9])
+    cb = mpl.colorbar.ColorbarBase(
+        ax, cmap=curvature_map, norm=norm, orientation="vertical"
+    )
 
     ticks = cb.get_ticks()
     ticks.sort()
@@ -436,26 +451,25 @@ def differencePlotter(context, difftype='K1'):
     ticklabels = [r"{:0.1f}".format(tick) for tick in ticks]
 
     # extend = 'neither'
-    if extend == 'neither':
-       pass
-    elif extend == 'both':
+    if extend == "neither":
+        pass
+    elif extend == "both":
         ticklabels[0] = "< " + ticklabels[0]
         ticklabels[-1] = "> " + ticklabels[-1]
-    elif extend == 'max':
+    elif extend == "max":
         ticklabels[-1] = "> " + ticklabels[-1]
-    elif extend == 'min':
+    elif extend == "min":
         ticklabels[0] = "< " + ticklabels[0]
     cb.set_ticklabels(ticklabels)
     cb.ax.tick_params(labelsize=14)
-    cb.set_label("%s [$\mu m^{-1}$]"%(vlayer), size=16)
+    cb.set_label("%s [$\mu m^{-1}$]" % (vlayer), size=16)
 
-    plt.savefig(context.object.name+'difference'+difftype+'.pdf', format='pdf')
+    plt.savefig(context.object.name + "difference" + difftype + ".pdf", format="pdf")
     # plt.show()
     plt.close()
 
 
-
-def eng_notation(x,pos):
-    num, power = '{:.1e}'.format(x).split('e')
-    power=int(power)
-    return r'${} \times 10^{{{}}}$'.format(num, power)
\ No newline at end of file
+def eng_notation(x, pos):
+    num, power = "{:.1e}".format(x).split("e")
+    power = int(power)
+    return r"${} \times 10^{{{}}}$".format(num, power)
diff --git a/tools/blendgamer/src/colormap_enums.py b/tools/blendgamer/src/colormap_enums.py
index 063e67f1..bfae781d 100644
--- a/tools/blendgamer/src/colormap_enums.py
+++ b/tools/blendgamer/src/colormap_enums.py
@@ -1,25 +1,25 @@
 # ***************************************************************************
 # This file is part of the GAMer software.
-# Copyright (C) 2016-2018
-# by Christopher Lee, Tom Bartol, John Moody, Rommie Amaro, J. Andrew McCammon,
-#    and Michael Holst
-
+# Copyright (C) 2016-2021
+# by Christopher T. Lee and contributors
+#
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
 # License as published by the Free Software Foundation; either
 # version 2.1 of the License, or (at your option) any later version.
-
+#
 # This library is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 # Lesser General Public License for more details.
-
+#
 # You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+# License along with this library; if not, see 
+# or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+# Boston, MA 02111-1307 USA
 # ***************************************************************************
 
 colormap_enums = [
-  ('VIRIDIS', 'viridis', 'Viridis'),
-  ('PRGN', 'PRGn', 'Purple-Green diverging'),
-]
\ No newline at end of file
+    ("VIRIDIS", "viridis", "Viridis"),
+    ("PRGN", "PRGn", "Purple-Green diverging"),
+]
diff --git a/tools/blendgamer/src/curvatures.py b/tools/blendgamer/src/curvatures.py
index af289d3f..e2c07cd8 100644
--- a/tools/blendgamer/src/curvatures.py
+++ b/tools/blendgamer/src/curvatures.py
@@ -1,33 +1,42 @@
 # ***************************************************************************
 # This file is part of the GAMer software.
-# Copyright (C) 2016-2018
-# by Christopher Lee, Tom Bartol, John Moody, Rommie Amaro, J. Andrew McCammon,
-#    and Michael Holst
-
+# Copyright (C) 2016-2021
+# by Christopher T. Lee and contributors
+#
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
 # License as published by the Free Software Foundation; either
 # version 2.1 of the License, or (at your option) any later version.
-
+#
 # This library is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 # Lesser General Public License for more details.
-
+#
 # You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+# License along with this library; if not, see 
+# or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+# Boston, MA 02111-1307 USA
 # ***************************************************************************
 
 import bpy
 import bmesh
 from bpy.props import (
-        BoolProperty, CollectionProperty, EnumProperty,
-        FloatProperty, FloatVectorProperty, IntProperty, IntVectorProperty,
-        PointerProperty, StringProperty, BoolVectorProperty)
+    BoolProperty,
+    CollectionProperty,
+    EnumProperty,
+    FloatProperty,
+    FloatVectorProperty,
+    IntProperty,
+    IntVectorProperty,
+    PointerProperty,
+    StringProperty,
+    BoolVectorProperty,
+)
 from blendgamer.colormap_enums import colormap_enums
 
 import importlib.util
+
 mpl_spec = importlib.util.find_spec("matplotlib")
 mpl_found = mpl_spec is not None
 
@@ -37,76 +46,83 @@
 from blendgamer.util import *
 
 curvatureTypeEnums = [
-    ('K1', 'k1', 'First principle curvature'),
-    ('K2', 'k2', 'Second principle curvature'),
-    ('KG', 'kg', 'Gaussian curvature'),
-    ('KH', 'kh', 'Mean curvature'),
+    ("K1", "k1", "First principle curvature"),
+    ("K2", "k2", "Second principle curvature"),
+    ("KG", "kg", "Gaussian curvature"),
+    ("KH", "kh", "Mean curvature"),
 ]
 
 curvatureCalcEnums = [
-    ('MDSB', 'MDSB', 'Meyer, Desbrun, Schroder, Barr Algorithm'),
-    ('JETS', 'Jet Fitting', 'Cazal and Pouget Jet Fitting'),
+    ("MDSB", "MDSB", "Meyer, Desbrun, Schroder, Barr Algorithm"),
+    ("JETS", "Jet Fitting", "Cazal and Pouget Jet Fitting"),
 ]
 curvatureCalcDict = {
-  'MDSB': 'curvatureViaMDSB',
-  'JETS' : 'curvatureViaJets',
+    "MDSB": "curvatureViaMDSB",
+    "JETS": "curvatureViaJets",
 }
 
+
 class GAMER_OT_remove_curvature(bpy.types.Operator):
-    bl_idname       = "gamer.remove_curvature"
-    bl_label        = "Remove Curvature"
-    bl_description  = "Remove selected curvature from object"
-    bl_options      = {'REGISTER'}
+    bl_idname = "gamer.remove_curvature"
+    bl_label = "Remove Curvature"
+    bl_description = "Remove selected curvature from object"
+    bl_options = {"REGISTER"}
 
     def execute(self, context):
         try:
             context.object.gamer.curvatures.remove_curvature(context)
-            return {'FINISHED'}
+            return {"FINISHED"}
         except Exception as e:
-            self.report({'ERROR'}, str(e))
-            return {'CANCELLED'}
+            self.report({"ERROR"}, str(e))
+            return {"CANCELLED"}
+
 
 class GAMER_OT_remove_all_curvatures(bpy.types.Operator):
-    bl_idname       = "gamer.remove_all_curvatures"
-    bl_label        = "Remove All Curvatures"
-    bl_description  = "Remove all curvatures from object"
-    bl_options      = {'REGISTER'}
+    bl_idname = "gamer.remove_all_curvatures"
+    bl_label = "Remove All Curvatures"
+    bl_description = "Remove all curvatures from object"
+    bl_options = {"REGISTER"}
 
     def execute(self, context):
         try:
             context.object.gamer.curvatures.remove_all_curvatures(context)
-            return {'FINISHED'}
+            return {"FINISHED"}
         except Exception as e:
-            self.report({'ERROR'}, str(e))
-            return {'CANCELLED'}
+            self.report({"ERROR"}, str(e))
+            return {"CANCELLED"}
+
 
 class GAMER_OT_plot_curvature(bpy.types.Operator):
-    bl_idname       = "gamer.plot_curvature"
-    bl_label        = "Plot the selected curvature"
-    bl_description  = "Plot the selected curvature as a heatmap and generate a corresponding vertex color layer"
-    bl_options      = {'REGISTER'}
+    bl_idname = "gamer.plot_curvature"
+    bl_label = "Plot the selected curvature"
+    bl_description = "Plot the selected curvature as a heatmap and generate a corresponding vertex color layer"
+    bl_options = {"REGISTER"}
 
     def execute(self, context):
         try:
             context.object.gamer.curvatures.plot_curvature(context)
-            return {'FINISHED'}
+            return {"FINISHED"}
         except Exception as e:
-            self.report({'ERROR'}, str(e))
-            return {'CANCELLED'}
+            self.report({"ERROR"}, str(e))
+            return {"CANCELLED"}
+
 
 class GAMER_OT_plot_all_curvatures(bpy.types.Operator):
-    bl_idname       = "gamer.plot_all_curvatures"
-    bl_label        = "Plot all curvatures"
-    bl_description  = "Plot all computed curvatures as a heatmap and generate vertex color layers"
-    bl_options      = {'REGISTER'}
+    bl_idname = "gamer.plot_all_curvatures"
+    bl_label = "Plot all curvatures"
+    bl_description = (
+        "Plot all computed curvatures as a heatmap and generate vertex color layers"
+    )
+    bl_options = {"REGISTER"}
 
     def execute(self, context):
         try:
             context.object.gamer.curvatures.plot_all_curvatures(context)
-            return {'FINISHED'}
+            return {"FINISHED"}
         except Exception as e:
-            self.report({'ERROR'}, str(e))
-            return {'CANCELLED'}
+            self.report({"ERROR"}, str(e))
+            return {"CANCELLED"}
+
 
 # class GAMER_OT_plot_differences(bpy.types.Operator):
 #     bl_idname       = "gamer.plot_differences"
@@ -122,11 +138,12 @@ def execute(self, context):
 #             self.report({'ERROR'}, str(e))
 #             return {'CANCELLED'}
 
+
 class GAMER_OT_compute_curvatures(bpy.types.Operator):
-    bl_idname       = "gamer.compute_curvatures"
-    bl_label        = "Compute Curvatures"
-    bl_description  = "Compute curvatures"
-    bl_options      = {'REGISTER'}
+    bl_idname = "gamer.compute_curvatures"
+    bl_label = "Compute Curvatures"
+    bl_description = "Compute curvatures"
+    bl_options = {"REGISTER"}
 
     def execute(self, context):
         try:
@@ -134,29 +151,29 @@ def execute(self, context):
             curvatures = obj.gamer.curvatures
             algorithm = obj.gamer.curvatures.algorithm
 
-            gmesh = blenderToGamer()
+            gmesh = blender_to_gamer()
             kh, kg, k1, k2 = getattr(gmesh, curvatureCalcDict[algorithm])(0)
 
             with ObjectMode():
-                ml = getCurvatureLayer(obj, algorithm, 'K1')
+                ml = getCurvatureLayer(obj, algorithm, "K1")
                 for i in range(0, len(k1)):
                     ml[i].value = k1[i]
-                curvatures.add_curvature(context, 'K1')
+                curvatures.add_curvature(context, "K1")
 
-                ml = getCurvatureLayer(obj, algorithm, 'K2')
+                ml = getCurvatureLayer(obj, algorithm, "K2")
                 for i in range(0, len(k1)):
                     ml[i].value = k2[i]
-                curvatures.add_curvature(context, 'K2')
+                curvatures.add_curvature(context, "K2")
 
-                ml = getCurvatureLayer(obj, algorithm, 'KG')
+                ml = getCurvatureLayer(obj, algorithm, "KG")
                 for i in range(0, len(k1)):
                     ml[i].value = kg[i]
-                curvatures.add_curvature(context, 'KG')
+                curvatures.add_curvature(context, "KG")
 
-                ml = getCurvatureLayer(obj, algorithm, 'KH')
+                ml = getCurvatureLayer(obj, algorithm, "KH")
                 for i in range(0, len(k1)):
                     ml[i].value = kh[i]
-                curvatures.add_curvature(context, 'KH')
+                curvatures.add_curvature(context, "KH")
 
             # Explicitly free curvature arrays
             del kh
@@ -164,71 +181,82 @@ def execute(self, context):
             del k1
             del k2
 
-            self.report({'INFO'}, "GAMer: Compute Curvatures complete")
-            return {'FINISHED'}
+            self.report({"INFO"}, "GAMer: Compute Curvatures complete")
+            return {"FINISHED"}
         except Exception as e:
-            self.report({'ERROR'}, str(e))
-            return {'CANCELLED'}
+            self.report({"ERROR"}, str(e))
+            return {"CANCELLED"}
+
 
 class GAMerCurvatureItem(bpy.types.PropertyGroup):
     curvatureType = EnumProperty(
-        name = "Curvature Type",
-        description = "Which curvature?",
-        items = curvatureTypeEnums
-        )
+        name="Curvature Type", description="Which curvature?", items=curvatureTypeEnums
+    )
     algorithm = EnumProperty(
-        name = "Curvature algorithm used",
-        description = "Which algorithm was used?",
-        items = curvatureCalcEnums)
+        name="Curvature algorithm used",
+        description="Which algorithm was used?",
+        items=curvatureCalcEnums,
+    )
 
     ## Addition metadata for saving plot states...
     minCurve = FloatProperty(
-        name="Minimum curvature", default=0,
-        description="Lower bound percentile truncation")
+        name="Minimum curvature",
+        default=0,
+        description="Lower bound percentile truncation",
+    )
 
     maxCurve = FloatProperty(
-        name="Maximum curvature", default=100,
-        description="Upper bound percentile truncation")
+        name="Maximum curvature",
+        default=100,
+        description="Upper bound percentile truncation",
+    )
 
     curveIter = IntProperty(
-        name="Smooth Curvature Iterations", min=0, default = 0,
-        description="How many iterations of curvature smoothing?")
+        name="Smooth Curvature Iterations",
+        min=0,
+        default=0,
+        description="How many iterations of curvature smoothing?",
+    )
 
     limitsArePercentiles = BoolProperty(
-        name="Treat Min/Max as percentiles", default = True,
-        description="Treat min and max as percentiles?")
+        name="Treat Min/Max as percentiles",
+        default=True,
+        description="Treat min and max as percentiles?",
+    )
 
     mixpoint = FloatProperty(
-        name = "Color mixing point", default = 0.5, min=0, max=1,
-        description="Value for color mixing")
+        name="Color mixing point",
+        default=0.5,
+        min=0,
+        max=1,
+        description="Value for color mixing",
+    )
 
     colormap = EnumProperty(
-            name = "Colormap colors",
-            description="Colormap used",
-            items = colormap_enums)
+        name="Colormap colors", description="Colormap used", items=colormap_enums
+    )
 
 
 class GAMerCurvaturesList(bpy.types.PropertyGroup):
     algorithm = EnumProperty(
-        name = "Curvature Algorithm",
-        description = "Which algorithm was used?",
-        items = curvatureCalcEnums)
+        name="Curvature Algorithm",
+        description="Which algorithm was used?",
+        items=curvatureCalcEnums,
+    )
 
     curvature_list = CollectionProperty(
-                            type=GAMerCurvatureItem,
-                            name="List of computed curvatures")
+        type=GAMerCurvatureItem, name="List of computed curvatures"
+    )
 
-    active_index = IntProperty(
-                            name="Active Index",
-                            default=0)
+    active_index = IntProperty(name="Active Index", default=0)
 
     showplots = BoolProperty(
-        name="Show plot", default=False,
-        description="Display the plots")
+        name="Show plot", default=False, description="Display the plots"
+    )
 
     saveplots = BoolProperty(
-        name="Save plots", default=False,
-        description="Save the generated plots")
+        name="Save plots", default=False, description="Save the generated plots"
+    )
 
     def add_curvature(self, context, curvatureType):
         if len(self.curvature_list) > 0:
@@ -240,20 +268,19 @@ def add_curvature(self, context, curvatureType):
         new_curve.curvatureType = curvatureType
         new_curve.algorithm = self.algorithm
 
-
     def remove_curvature(self, context):
         crv = self.get_active_index()
         if crv:
             obj = getActiveMeshObject()
 
             with BMeshContext(obj) as bm:
-                name = "%s%s"%(crv.algorithm, crv.curvatureType)
+                name = "%s%s" % (crv.algorithm, crv.curvatureType)
                 layer = bm.verts.layers.float.get(name)
                 bm.verts.layers.float.remove(layer)
 
             self.curvature_list.remove(self.active_index)
             self.active_index -= 1
-            if (self.active_index < 0):
+            if self.active_index < 0:
                 self.active_index = 0
 
     def remove_all_curvatures(self, context):
@@ -263,14 +290,13 @@ def remove_all_curvatures(self, context):
             for i in range(len(self.curvature_list)):
                 crv = self.curvature_list[0]
 
-                name = "%s%s"%(crv.algorithm, crv.curvatureType)
+                name = "%s%s" % (crv.algorithm, crv.curvatureType)
                 layer = bm.verts.layers.float.get(name)
                 bm.verts.layers.float.remove(layer)
                 self.curvature_list.remove(0)
 
         self.active_index = 0
 
-
     def get_active_index(self):
         idx = None
         if len(self.curvature_list) > 0:
@@ -278,19 +304,24 @@ def get_active_index(self):
         return idx
 
     if mpl_found:
+
         def plot_curvature(self, context):
             """
             Wrapper function to avoid importing lots of matplotlib files here.
             """
             crv = self.get_active_index()
-            dataToVertexColor(crv, context, showplot=self.showplots, saveplot=self.saveplots)
+            dataToVertexColor(
+                crv, context, showplot=self.showplots, saveplot=self.saveplots
+            )
 
         def plot_all_curvatures(self, context):
             """
             Wrapper function to plot all curvatures
             """
             for crv in self.curvature_list:
-                dataToVertexColor(crv, context, showplot=self.showplots, saveplot=self.saveplots)
+                dataToVertexColor(
+                    crv, context, showplot=self.showplots, saveplot=self.saveplots
+                )
 
         # def plot_differences(self, context):
         #     differencePlotter(context, 'K1')
@@ -298,22 +329,28 @@ def plot_all_curvatures(self, context):
         #     # differencePlotter(context, 'KG')
         #     # differencePlotter(context, 'KH')
 
-classes = [GAMER_OT_compute_curvatures,
-           GAMER_OT_remove_curvature,
-           GAMER_OT_remove_all_curvatures,
-           GAMER_OT_plot_curvature,
-           GAMER_OT_plot_all_curvatures,
-           # GAMER_OT_plot_differences,
-           GAMerCurvatureItem,
-           GAMerCurvaturesList]
+
+classes = [
+    GAMER_OT_compute_curvatures,
+    GAMER_OT_remove_curvature,
+    GAMER_OT_remove_all_curvatures,
+    GAMER_OT_plot_curvature,
+    GAMER_OT_plot_all_curvatures,
+    # GAMER_OT_plot_differences,
+    GAMerCurvatureItem,
+    GAMerCurvaturesList,
+]
+
 
 def register():
     from bpy.utils import register_class
+
     for cls in classes:
         register_class(make_annotations(cls))
 
+
 def unregister():
     from bpy.utils import unregister_class
+
     for cls in reversed(classes):
         unregister_class(make_annotations(cls))
-
diff --git a/tools/blendgamer/src/markers.py b/tools/blendgamer/src/markers.py
index 6303c968..b36f0bc9 100644
--- a/tools/blendgamer/src/markers.py
+++ b/tools/blendgamer/src/markers.py
@@ -1,30 +1,38 @@
 # ***************************************************************************
 # This file is part of the GAMer software.
-# Copyright (C) 2016-2018
-# by Christopher Lee, Tom Bartol, John Moody, Rommie Amaro, J. Andrew McCammon,
-#    and Michael Holst
-
+# Copyright (C) 2016-2021
+# by Christopher T. Lee and contributors
+#
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
 # License as published by the Free Software Foundation; either
 # version 2.1 of the License, or (at your option) any later version.
-
+#
 # This library is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 # Lesser General Public License for more details.
-
+#
 # You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+# License along with this library; if not, see 
+# or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+# Boston, MA 02111-1307 USA
 # ***************************************************************************
 
 import bpy
 import bmesh
 from bpy.props import (
-    BoolProperty, CollectionProperty, EnumProperty,
-    FloatProperty, FloatVectorProperty, IntProperty, IntVectorProperty,
-    PointerProperty, StringProperty, BoolVectorProperty)
+    BoolProperty,
+    CollectionProperty,
+    EnumProperty,
+    FloatProperty,
+    FloatVectorProperty,
+    IntProperty,
+    IntVectorProperty,
+    PointerProperty,
+    StringProperty,
+    BoolVectorProperty,
+)
 
 from blendgamer.util import *
 
@@ -35,145 +43,143 @@ class GAMER_OT_add_boundary(bpy.types.Operator):
     bl_idname = "gamer.add_boundary"
     bl_label = "Add New Boundary"
     bl_description = "Add a new boundary to an object"
-    bl_options = {'REGISTER', 'UNDO'}
+    bl_options = {"REGISTER", "UNDO"}
 
     def execute(self, context):
         context.object.gamer.markers.add_boundary(context)
-        return {'FINISHED'}
+        return {"FINISHED"}
 
 
 class GAMER_OT_remove_boundary(bpy.types.Operator):
     bl_idname = "gamer.remove_boundary"
     bl_label = "Remove Boundary"
     bl_description = "Remove selected boundary from object"
-    bl_options = {'REGISTER', 'UNDO'}
+    bl_options = {"REGISTER", "UNDO"}
 
     def execute(self, context):
         context.object.gamer.markers.remove_boundary(context)
-        return {'FINISHED'}
+        return {"FINISHED"}
 
 
 class GAMER_OT_remove_all_boundaries(bpy.types.Operator):
     bl_idname = "gamer.remove_all_boundaries"
     bl_label = "Remove All Boundaries"
     bl_description = "Remove all boundaries from object"
-    bl_options = {'REGISTER', 'UNDO'}
+    bl_options = {"REGISTER", "UNDO"}
 
     def execute(self, context):
         context.object.gamer.markers.remove_all_boundaries(context)
-        return {'FINISHED'}
+        return {"FINISHED"}
 
 
 class GAMER_OT_assign_boundary_faces(bpy.types.Operator):
     bl_idname = "gamer.assign_boundary_faces"
     bl_label = "Assign Selected Faces To Boundary"
     bl_description = "Assign selected faces to boundary"
-    bl_options = {'REGISTER', 'UNDO'}
+    bl_options = {"REGISTER", "UNDO"}
 
     def execute(self, context):
         bnd = context.object.gamer.markers.get_active_boundary()
         if bnd:
             bnd.assign_boundary_faces(context)
-            return {'FINISHED'}
-        self.report({'WARNING'},
-                    "Cannot assign faces, no active boundary selected")
-        return {'CANCELLED'}
+            return {"FINISHED"}
+        self.report({"WARNING"}, "Cannot assign faces, no active boundary selected")
+        return {"CANCELLED"}
 
 
 class GAMER_OT_remove_boundary_faces(bpy.types.Operator):
     bl_idname = "gamer.remove_boundary_faces"
     bl_label = "Remove Selected Faces From Boundary"
     bl_description = "Remove selected faces from boundary"
-    bl_options = {'REGISTER', 'UNDO'}
+    bl_options = {"REGISTER", "UNDO"}
 
     def execute(self, context):
         bnd = context.object.gamer.markers.get_active_boundary()
         if bnd:
             bnd.remove_boundary_faces(context)
-            return{'FINISHED'}
-        self.report({'WARNING'},
-                    "Cannot remove faces, no active boundary selected")
-        return {'CANCELLED'}
+            return {"FINISHED"}
+        self.report({"WARNING"}, "Cannot remove faces, no active boundary selected")
+        return {"CANCELLED"}
 
 
 class GAMER_OT_select_boundary_faces(bpy.types.Operator):
     bl_idname = "gamer.select_boundary_faces"
     bl_label = "Select Faces of Selected Boundary"
     bl_description = "Select faces of selected boundary"
-    bl_options = {'REGISTER', 'UNDO'}
+    bl_options = {"REGISTER", "UNDO"}
 
     def execute(self, context):
         bnd = context.object.gamer.markers.get_active_boundary()
         if bnd:
             bnd.select_boundary_faces(context)
-            return {'FINISHED'}
-        return {'CANCELLED'}
+            return {"FINISHED"}
+        return {"CANCELLED"}
 
 
 class GAMER_OT_deselect_boundary_faces(bpy.types.Operator):
     bl_idname = "gamer.deselect_boundary_faces"
     bl_label = "Deselect Faces of Selected Boundary"
     bl_description = "Deselect faces of selected boundary"
-    bl_options = {'REGISTER', 'UNDO'}
+    bl_options = {"REGISTER", "UNDO"}
 
     def execute(self, context):
         bnd = context.object.gamer.markers.get_active_boundary()
         if bnd:
             bnd.deselect_boundary_faces(context)
-            return {'FINISHED'}
-        return {'CANCELLED'}
+            return {"FINISHED"}
+        return {"CANCELLED"}
 
 
 class GAMER_OT_select_all_boundary_faces(bpy.types.Operator):
     bl_idname = "gamer.select_all_boundary_faces"
     bl_label = "Select All Faces of Selected Boundary"
     bl_description = "Select all faces of selected boundary"
-    bl_options = {'REGISTER', 'UNDO'}
+    bl_options = {"REGISTER", "UNDO"}
 
     def execute(self, context):
         for bnd in context.object.gamer.markers.boundary_list:
             bnd.select_boundary_faces(context)
-        return {'FINISHED'}
+        return {"FINISHED"}
+
 
 class GAMER_OT_deselect_all_boundary_faces(bpy.types.Operator):
     bl_idname = "gamer.deselect_all_boundary_faces"
     bl_label = "Deselect all marked faces"
     bl_description = "Deselect all faces of selected boundary"
-    bl_options = {'REGISTER', 'UNDO'}
+    bl_options = {"REGISTER", "UNDO"}
 
     def execute(self, context):
         for bnd in context.object.gamer.markers.boundary_list:
             bnd.deselect_boundary_faces(context)
-        return {'FINISHED'}
+        return {"FINISHED"}
 
 
 class GAMerBoundaryMaterial(bpy.types.PropertyGroup):
     """
     Class for GAMer boundary material property group.
     """
-    boundary_id = IntProperty(
-        name="Boundary ID associated with material", default=-1)
+
+    boundary_id = IntProperty(name="Boundary ID associated with material", default=-1)
 
 
 class GAMerBoundaryMarker(bpy.types.PropertyGroup):
     """
     Class for GAMer boundary markers property group.
     """
+
     boundary_id = IntProperty(
         name="Boundary ID",
         description="Unique identifier of this boundary",
         min=-1,
-        default=-1
+        default=-1,
     )
     boundary_name = StringProperty(
-        name="Boundary Name",
-        description="Name of the boundary",
-        default="Boundary"
+        name="Boundary Name", description="Name of the boundary", default="Boundary"
     )
     marker = IntProperty(
         name="Marker Value",
         description="Marker value to associate with this boundary",
-        default=1
+        default=1,
     )
     status = BoolProperty(name="Status", default=False)
 
@@ -225,7 +231,7 @@ def delete_boundary(self, context):
 
         # Clean up the materials
         mats = bpy.data.materials
-        bnd_mat = getMatByBndID(self.boundary_id)
+        bnd_mat = get_material_by_bnd_id(self.boundary_id)
 
         with ObjectMode():
             # Material slots can only be removed in object mode!
@@ -234,10 +240,9 @@ def delete_boundary(self, context):
             while bnd_mat.name in objmats:
                 idx = objmats.find(bnd_mat.name)
                 if bpy.app.version < (2, 81, 0):
-                    objmats.pop(index = idx, update_data = True)
+                    objmats.pop(index=idx, update_data=True)
                 else:
-                    objmats.pop(index = idx)
-
+                    objmats.pop(index=idx)
 
         # Remove the global material
         mats.remove(bnd_mat)
@@ -258,7 +263,6 @@ def delete_boundary(self, context):
                     face[ml] = UNSETID
                     face.material_index = bnd_unset_mat_idx
 
-
     def assign_boundary_faces(self, context):
         """Assign boundary marker to selected faces
 
@@ -269,7 +273,7 @@ def assign_boundary_faces(self, context):
         mesh = obj.data
 
         # Material to associate
-        bnd_mat = getMatByBndID(self.boundary_id)
+        bnd_mat = get_material_by_bnd_id(self.boundary_id)
         matID = obj.material_slots.find(bnd_mat.name)
         # Link the material to the object if it's somehow missing
         if matID == -1:
@@ -285,7 +289,6 @@ def assign_boundary_faces(self, context):
                         face[ml] = self.boundary_id
                         face.material_index = matID
 
-
     def repaint_boundary_faces(self, context):
         obj = context.active_object
         mats = bpy.data.materials
@@ -299,7 +302,7 @@ def repaint_boundary_faces(self, context):
             matID = obj.material_slots.find(bnd_mat.name)
 
         # Material to associate
-        bnd_mat = getMatByBndID(self.boundary_id)
+        bnd_mat = get_material_by_bnd_id(self.boundary_id)
         matID = obj.material_slots.find(bnd_mat.name)
         # Link the material to the object if it's somehow missing
         if matID == -1:
@@ -325,7 +328,7 @@ def remove_boundary_faces(self, context):
             obj.data.materials.append(bnd_mat)
             matID = obj.material_slots.find(bnd_mat.name)
 
-        if (mesh.total_face_sel > 0):
+        if mesh.total_face_sel > 0:
             with BMeshContext(obj) as bm:
                 ml = getBMeshMarkerLayer(bm)
                 for face in bm.faces:
@@ -372,14 +375,8 @@ def deselect_boundary_faces(self, context):
 
 
 class GAMerBoundaryMarkersList(bpy.types.PropertyGroup):
-    boundary_list = CollectionProperty(
-        type=GAMerBoundaryMarker,
-        name="Boundary List"
-    )
-    active_bnd_index = IntProperty(
-        name="Active Boundary Index",
-        default=0
-    )
+    boundary_list = CollectionProperty(type=GAMerBoundaryMarker, name="Boundary List")
+    active_bnd_index = IntProperty(name="Active Boundary Index", default=0)
 
     def get_active_boundary(self):
         """
@@ -429,31 +426,35 @@ def remove_boundary(self, context):
             # Now remove the RNA boundary from the object
             self.boundary_list.remove(self.active_bnd_index)
             self.active_bnd_index -= 1
-            if (self.active_bnd_index < 0):
+            if self.active_bnd_index < 0:
                 self.active_bnd_index = 0
 
 
-classes = [GAMerBoundaryMarker,
-           GAMerBoundaryMaterial,
-           GAMerBoundaryMarkersList,
-           GAMER_OT_add_boundary,
-           GAMER_OT_remove_boundary,
-           GAMER_OT_remove_all_boundaries,
-           GAMER_OT_assign_boundary_faces,
-           GAMER_OT_remove_boundary_faces,
-           GAMER_OT_select_boundary_faces,
-           GAMER_OT_deselect_boundary_faces,
-           GAMER_OT_select_all_boundary_faces,
-           GAMER_OT_deselect_all_boundary_faces]
+classes = [
+    GAMerBoundaryMarker,
+    GAMerBoundaryMaterial,
+    GAMerBoundaryMarkersList,
+    GAMER_OT_add_boundary,
+    GAMER_OT_remove_boundary,
+    GAMER_OT_remove_all_boundaries,
+    GAMER_OT_assign_boundary_faces,
+    GAMER_OT_remove_boundary_faces,
+    GAMER_OT_select_boundary_faces,
+    GAMER_OT_deselect_boundary_faces,
+    GAMER_OT_select_all_boundary_faces,
+    GAMER_OT_deselect_all_boundary_faces,
+]
 
 
 def register():
     from bpy.utils import register_class
+
     for cls in classes:
         register_class(make_annotations(cls))
 
 
 def unregister():
     from bpy.utils import unregister_class
+
     for cls in reversed(classes):
         unregister_class(make_annotations(cls))
diff --git a/tools/blendgamer/src/meshstats.py b/tools/blendgamer/src/meshstats.py
index 5a041336..8b8e3b36 100644
--- a/tools/blendgamer/src/meshstats.py
+++ b/tools/blendgamer/src/meshstats.py
@@ -1,34 +1,46 @@
-# ##### BEGIN GPL LICENSE BLOCK #####
+# ***************************************************************************
+# This file is part of the GAMer software.
+# Copyright (C) 2016-2021
+# by Christopher T. Lee and contributors
 #
-#  This program is free software; you can redistribute it and/or
-#  modify it under the terms of the GNU General Public License
-#  as published by the Free Software Foundation; either version 2
-#  of the License, or (at your option) any later version.
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
 #
-#  This program is distributed in the hope that it will be useful,
-#  but WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-#  GNU General Public License for more details.
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
 #
-#  You should have received a copy of the GNU General Public License
-#  along with this program; if not, write to the Free Software Foundation,
-#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# ##### END GPL LICENSE BLOCK #####
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, see 
+# or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+# Boston, MA 02111-1307 USA
+# ***************************************************************************
 
 import array
 import mathutils
 import math
 import bpy
 from bpy.props import (
-        BoolProperty, CollectionProperty, EnumProperty,
-        FloatProperty, FloatVectorProperty, IntProperty, IntVectorProperty,
-        PointerProperty, StringProperty, BoolVectorProperty)
+    BoolProperty,
+    CollectionProperty,
+    EnumProperty,
+    FloatProperty,
+    FloatVectorProperty,
+    IntProperty,
+    IntVectorProperty,
+    PointerProperty,
+    StringProperty,
+    BoolVectorProperty,
+)
 import bmesh
 
 import blendgamer.pygamer as g
 
 import importlib.util
+
 mpl_spec = importlib.util.find_spec("matplotlib")
 mpl_found = mpl_spec is not None
 
@@ -44,23 +56,24 @@
 ## Following ops are from 3D Print Addon
 class GAMER_OT_MeshStats_Select_Report(bpy.types.Operator):
     """Select the data associated with this report"""
-    bl_idname  = "gamer.meshstats_select_report"
-    bl_label   = "Select Report"
-    bl_options = {'INTERNAL'}
+
+    bl_idname = "gamer.meshstats_select_report"
+    bl_label = "Select Report"
+    bl_options = {"INTERNAL"}
 
     index = IntProperty()
 
     _type_to_mode = {
-        bmesh.types.BMVert: 'VERT',
-        bmesh.types.BMEdge: 'EDGE',
-        bmesh.types.BMFace: 'FACE',
-        }
+        bmesh.types.BMVert: "VERT",
+        bmesh.types.BMEdge: "EDGE",
+        bmesh.types.BMFace: "FACE",
+    }
 
     _type_to_attr = {
         bmesh.types.BMVert: "verts",
         bmesh.types.BMEdge: "edges",
         bmesh.types.BMFace: "faces",
-        }
+    }
 
     def execute(self, context):
         obj = context.edit_object
@@ -69,7 +82,7 @@ def execute(self, context):
         bm_type, bm_array = data
 
         bpy.ops.mesh.reveal()
-        bpy.ops.mesh.select_all(action='DESELECT')
+        bpy.ops.mesh.select_all(action="DESELECT")
         bpy.ops.mesh.select_mode(type=self._type_to_mode[bm_type])
 
         bm = bmesh.from_edit_mesh(obj.data)
@@ -80,13 +93,16 @@ def execute(self, context):
                 elems[i].select_set(True)
         except:
             # possible arrays are out of sync
-            self.report({'WARNING'}, "Report is out of date, re-run check")
-        return {'FINISHED'}
+            self.report({"WARNING"}, "Report is out of date, re-run check")
+        return {"FINISHED"}
 
 
 def multiple_obj_warning(self, context):
     if len(context.selected_objects) > 1:
-        self.report({"INFO"}, "Multiple selected objects. Only the active one will be evaluated")
+        self.report(
+            {"INFO"}, "Multiple selected objects. Only the active one will be evaluated"
+        )
+
 
 # Helper method to get object and run main_check
 def execute_check(self, context):
@@ -95,21 +111,24 @@ def execute_check(self, context):
     self.main_check(obj, info)
     meshreport.update(*info)
     multiple_obj_warning(self, context)
-    return {'FINISHED'}
+    return {"FINISHED"}
 
 
 class GAMER_OT_MeshStats_Info_Volume(bpy.types.Operator):
     """Report the volume of the active mesh"""
-    bl_idname  = "gamer.meshstats_info_volume"
-    bl_label   = "MeshStats Info Volume"
+
+    bl_idname = "gamer.meshstats_info_volume"
+    bl_label = "MeshStats Info Volume"
 
     @staticmethod
-    def main_check(obj,info):
+    def main_check(obj, info):
         with copiedBMeshContext(obj) as bm:
             volume = 0.0
             for face in bm.faces:
                 if len(face.loops) != 3:
-                    info.append(("Cannot compute volume for non triangulated object.", None))
+                    info.append(
+                        ("Cannot compute volume for non triangulated object.", None)
+                    )
                     return
                 tv0 = face.loops[0].vert.co
                 tv1 = face.loops[1].vert.co
@@ -123,9 +142,13 @@ def main_check(obj,info):
                 x2 = tv2.x
                 y2 = tv2.y
                 z2 = tv2.z
-                det = x0*(y1*z2-y2*z1)+x1*(y2*z0-y0*z2)+x2*(y0*z1-y1*z0)
+                det = (
+                    x0 * (y1 * z2 - y2 * z1)
+                    + x1 * (y2 * z0 - y0 * z2)
+                    + x2 * (y0 * z1 - y1 * z0)
+                )
                 volume = volume + det
-            volume = volume/6.0
+            volume = volume / 6.0
             info.append(("Volume: %s" % clean_float("%.8f" % volume), None))
 
     def execute(self, context):
@@ -134,8 +157,9 @@ def execute(self, context):
 
 class GAMER_OT_MeshStats_Info_Area(bpy.types.Operator):
     """Report the surface area of the active mesh"""
-    bl_idname   = "gamer.meshstats_info_area"
-    bl_label    = "MeshStats Info Area"
+
+    bl_idname = "gamer.meshstats_info_area"
+    bl_label = "MeshStats Info Area"
 
     @staticmethod
     def main_check(obj, info):
@@ -146,31 +170,53 @@ def main_check(obj, info):
     def execute(self, context):
         return execute_check(self, context)
 
+
 class GAMER_OT_MeshStats_Check_Solid(bpy.types.Operator):
     """Check for geometry is solid (has valid inside/outside) and correct normals"""
-    bl_idname   = "gamer.meshstats_check_solid"
-    bl_label    = "MeshStats Check Solid"
+
+    bl_idname = "gamer.meshstats_check_solid"
+    bl_label = "MeshStats Check Solid"
     bl_description = "Check for non-manifolds and inconsistent normals"
 
     @staticmethod
     def main_check(obj, info):
         with copiedBMeshContext(obj) as bm:
-            edges_non_manifold = array.array('i', (i for i, ele in enumerate(bm.edges)
-                    if not ele.is_manifold))
-            edges_non_contig = array.array('i', (i for i, ele in enumerate(bm.edges)
-                    if ele.is_manifold and (not ele.is_contiguous)))
-
-            verts_non_manifold = array.array('i', (i for i, ele in enumerate(bm.verts)
-                    if not ele.is_manifold))
-
-            info.append(("Non Manifold Edge: %d" % len(edges_non_manifold),
-                        (bmesh.types.BMEdge, edges_non_manifold)))
-
-            info.append(("Bad Contig. Edges: %d" % len(edges_non_contig),
-                        (bmesh.types.BMEdge, edges_non_contig)))
-
-            info.append(("Non Manifold Vertices: %d" % len(verts_non_manifold),
-                (bmesh.types.BMVert, verts_non_manifold)))
+            edges_non_manifold = array.array(
+                "i", (i for i, ele in enumerate(bm.edges) if not ele.is_manifold)
+            )
+            edges_non_contig = array.array(
+                "i",
+                (
+                    i
+                    for i, ele in enumerate(bm.edges)
+                    if ele.is_manifold and (not ele.is_contiguous)
+                ),
+            )
+
+            verts_non_manifold = array.array(
+                "i", (i for i, ele in enumerate(bm.verts) if not ele.is_manifold)
+            )
+
+            info.append(
+                (
+                    "Non Manifold Edge: %d" % len(edges_non_manifold),
+                    (bmesh.types.BMEdge, edges_non_manifold),
+                )
+            )
+
+            info.append(
+                (
+                    "Bad Contig. Edges: %d" % len(edges_non_contig),
+                    (bmesh.types.BMEdge, edges_non_contig),
+                )
+            )
+
+            info.append(
+                (
+                    "Non Manifold Vertices: %d" % len(verts_non_manifold),
+                    (bmesh.types.BMVert, verts_non_manifold),
+                )
+            )
 
     def execute(self, context):
         return execute_check(self, context)
@@ -178,23 +224,28 @@ def execute(self, context):
 
 class GAMER_OT_MeshStats_Check_Intersections(bpy.types.Operator):
     """Check geometry for self intersections"""
-    bl_idname   = "gamer.meshstats_check_intersect"
-    bl_label    = "MeshStats Check Intersections"
+
+    bl_idname = "gamer.meshstats_check_intersect"
+    bl_label = "MeshStats Check Intersections"
 
     @staticmethod
     def main_check(obj, info):
         epsilon = bpy.context.scene.gamer.mesh_quality_properties.intersect_epsilon
         if not obj.data.polygons:
-            faces_intersect =  array.array('i', ())
+            faces_intersect = array.array("i", ())
         else:
             with copiedBMeshContext(obj) as bm:
                 tree = mathutils.bvhtree.BVHTree.FromBMesh(bm, epsilon=epsilon)
                 overlap = tree.overlap(tree)
                 faces_error = {i for i_pair in overlap for i in i_pair}
 
-                faces_intersect = array.array('i', faces_error)
-            info.append(("Intersect Face: %d" % len(faces_intersect),
-                        (bmesh.types.BMFace, faces_intersect)))
+                faces_intersect = array.array("i", faces_error)
+            info.append(
+                (
+                    "Intersect Face: %d" % len(faces_intersect),
+                    (bmesh.types.BMFace, faces_intersect),
+                )
+            )
 
     def execute(self, context):
         return execute_check(self, context)
@@ -204,49 +255,67 @@ class GAMER_OT_MeshStats_Check_Degenerate(bpy.types.Operator):
     """Check for degenerate geometry that may not print properly
     (zero area faces, zero length edges)
     """
-    bl_idname    = "gamer.meshstats_check_degenerate"
-    bl_label     = "Check Degenerate Faces and Edges"
-    bl_description  = "Check for zero length/area edges and faces"
+
+    bl_idname = "gamer.meshstats_check_degenerate"
+    bl_label = "Check Degenerate Faces and Edges"
+    bl_description = "Check for zero length/area edges and faces"
 
     @staticmethod
     def main_check(obj, info):
         threshold = 0
         with copiedBMeshContext(obj) as bm:
-            faces_zero = array.array('i', (i for i, ele in enumerate(bm.faces) if ele.calc_area() <= threshold))
-            edges_zero = array.array('i', (i for i, ele in enumerate(bm.edges) if ele.calc_length() <= threshold))
-
-        info.append(("Zero Area Faces: %d" % len(faces_zero),
-                    (bmesh.types.BMFace, faces_zero)))
+            faces_zero = array.array(
+                "i",
+                (i for i, ele in enumerate(bm.faces) if ele.calc_area() <= threshold),
+            )
+            edges_zero = array.array(
+                "i",
+                (i for i, ele in enumerate(bm.edges) if ele.calc_length() <= threshold),
+            )
+
+        info.append(
+            ("Zero Area Faces: %d" % len(faces_zero), (bmesh.types.BMFace, faces_zero))
+        )
 
-        info.append(("Zero Len. Edges: %d" % len(edges_zero),
-                    (bmesh.types.BMEdge, edges_zero)))
+        info.append(
+            ("Zero Len. Edges: %d" % len(edges_zero), (bmesh.types.BMEdge, edges_zero))
+        )
 
     def execute(self, context):
         return execute_check(self, context)
 
 
 class GAMER_OT_MeshStats_Check_Wagonwheels(bpy.types.Operator):
-    bl_idname    = "gamer.meshstats_check_wagonwheels"
-    bl_label     = "Check for wagon wheels"
+    bl_idname = "gamer.meshstats_check_wagonwheels"
+    bl_label = "Check for wagon wheels"
     bl_description = "Check for vertices connected to many edges"
 
     @staticmethod
     def main_check(obj, info):
         n_wagon_edges = bpy.context.scene.gamer.mesh_quality_properties.n_wagon_edges
         with copiedBMeshContext(obj) as bm:
-            wagon_edges = array.array('i',
-                    (i for i, ele in enumerate(bm.verts)
-                        if len(ele.link_edges) >= n_wagon_edges))
-        info.append(("Number of Wagonwheels: %d"%len(wagon_edges),
-                (bmesh.types.BMVert, wagon_edges)))
+            wagon_edges = array.array(
+                "i",
+                (
+                    i
+                    for i, ele in enumerate(bm.verts)
+                    if len(ele.link_edges) >= n_wagon_edges
+                ),
+            )
+        info.append(
+            (
+                "Number of Wagonwheels: %d" % len(wagon_edges),
+                (bmesh.types.BMVert, wagon_edges),
+            )
+        )
 
     def execute(self, context):
-        return execute_check(self,context)
+        return execute_check(self, context)
 
 
 class GAMER_OT_MeshStats_Check_Sharp(bpy.types.Operator):
-    bl_idname   = "gamer.meshstats_check_sharp"
-    bl_label    = "Check for small angles"
+    bl_idname = "gamer.meshstats_check_sharp"
+    bl_label = "Check for small angles"
     bl_description = "Check for faces with small angles"
 
     @staticmethod
@@ -254,50 +323,59 @@ def main_check(obj, info):
         min_angle = bpy.context.scene.gamer.mesh_quality_properties.min_angle
 
         with copiedBMeshContext(obj) as bm:
-            sharp_list = {i for i, face in enumerate(bm.faces) for loop in face.loops if loop.calc_angle()*180/math.pi <= min_angle}
-            sharp = array.array('i', sharp_list)
+            sharp_list = {
+                i
+                for i, face in enumerate(bm.faces)
+                for loop in face.loops
+                if loop.calc_angle() * 180 / math.pi <= min_angle
+            }
+            sharp = array.array("i", sharp_list)
 
-        info.append((
-                "Sharp faces: %d"%len(sharp),
-                (bmesh.types.BMFace, sharp)
-            ))
+        info.append(("Sharp faces: %d" % len(sharp), (bmesh.types.BMFace, sharp)))
 
     def execute(self, context):
-        return execute_check(self,context)
+        return execute_check(self, context)
 
 
 class GAMER_OT_MeshStats_Betti_Numbers(bpy.types.Operator):
-    bl_idname   = "gamer.meshstats_compute_betti"
-    bl_label    = "Report Betti numbers"
+    bl_idname = "gamer.meshstats_compute_betti"
+    bl_label = "Report Betti numbers"
     bl_description = "Compute the first three Betti numbers"
 
     @staticmethod
     def main_check(obj, info):
         try:
-            gmesh = blenderToGamer(autocorrect_normals=False)
+            gmesh = blender_to_gamer(autocorrect_normals=False)
         except Exception as e:
             info.append((str(e), None))
             return
         valid, k, h, v = gmesh.getBettiNumbers()
 
-        info.append(("Euler Characteristic: %d"%(gmesh.nVertices-gmesh.nEdges+gmesh.nFaces), None))
+        info.append(
+            (
+                "Euler Characteristic: %d"
+                % (gmesh.nVertices - gmesh.nEdges + gmesh.nFaces),
+                None,
+            )
+        )
 
         if valid:
-            info.append(("B0 Connected Components: %d"%(k), None))
-            info.append(("B1 Holes: %d"%(h), None))
-            info.append(("B2 Voids: %d"%(v), None))
+            info.append(("B0 Connected Components: %d" % (k), None))
+            info.append(("B1 Holes: %d" % (h), None))
+            info.append(("B2 Voids: %d" % (v), None))
         else:
-            info.append(("B0 Connected Components: %d"%(k), None))
+            info.append(("B0 Connected Components: %d" % (k), None))
             info.append(("Higher order Betti numbers undetermined", None))
 
     def execute(self, context):
-        return execute_check(self,context)
+        return execute_check(self, context)
 
 
 class GAMER_OT_MeshStats_Check_All(bpy.types.Operator):
     """Run all checks"""
+
     bl_idname = "gamer.meshstats_check_all"
-    bl_label  = "MeshStats Check All"
+    bl_label = "MeshStats Check All"
     bl_description = "Check all"
 
     def execute(self, context):
@@ -318,66 +396,86 @@ def execute(self, context):
             for cls in check_classes:
                 cls.main_check(obj, info)
         except Exception as e:
-            self.report({'ERROR'}, str(e))
-            return {'CANCELLED'}
+            self.report({"ERROR"}, str(e))
+            return {"CANCELLED"}
 
         meshreport.update(*info)
         multiple_obj_warning(self, context)
-        return {'FINISHED'}
+        return {"FINISHED"}
 
 
 class GAMER_OT_write_quality_info(bpy.types.Operator):
-    bl_idname      = "gamer.write_quality_info"
-    bl_label       = "Print mesh quality info to files"
+    bl_idname = "gamer.write_quality_info"
+    bl_label = "Print mesh quality info to files"
     bl_description = "Dump quality info to files"
-    bl_options     = {'REGISTER'}
+    bl_options = {"REGISTER"}
 
     def execute(self, context):
         mqp = bpy.context.scene.gamer.mesh_quality_properties
         for obj in context.selected_objects:
-            if obj.type == 'MESH':
+            if obj.type == "MESH":
                 fname = mqp.export_path + mqp.export_filebase + "_" + obj.name
-                print("Dumping quality info of mesh %s to file %s"%(obj.name, fname))
+                print("Dumping quality info of mesh %s to file %s" % (obj.name, fname))
                 try:
-                    gmesh = blenderToGamer(obj=obj)
+                    gmesh = blender_to_gamer(obj=obj)
                     g.printQualityInfo(fname, gmesh)
                 except Exception as e:
-                    self.report({'ERROR'}, str(e))
-                    return {'CANCELLED'}
-        return {'FINISHED'}
+                    self.report({"ERROR"}, str(e))
+                    return {"CANCELLED"}
+        return {"FINISHED"}
 
 
 class MeshQualityReportProperties(bpy.types.PropertyGroup):
     n_wagon_edges = IntProperty(
-        name="N Edges", default=8, min=1,
-        description="The number of incident edges to a vertex to be selected")
+        name="N Edges",
+        default=8,
+        min=1,
+        description="The number of incident edges to a vertex to be selected",
+    )
 
     export_path = StringProperty(
-            name="Export Directory",
-            description="Path to directory where files will be created",
-            default="//", maxlen=1024, subtype='DIR_PATH')
+        name="Export Directory",
+        description="Path to directory where files will be created",
+        default="//",
+        maxlen=1024,
+        subtype="DIR_PATH",
+    )
 
     export_filebase = StringProperty(
-            name="Filename",
-            description="Base name of the files to export",
-            default="meshquality", maxlen=1024, subtype='FILE_NAME')
+        name="Filename",
+        description="Base name of the files to export",
+        default="meshquality",
+        maxlen=1024,
+        subtype="FILE_NAME",
+    )
 
     min_angle = IntProperty(
-        name="Angle Threshold", default=15, min=0, max=180,
-        description="Select faces with angles less than this criteria")
+        name="Angle Threshold",
+        default=15,
+        min=0,
+        max=180,
+        description="Select faces with angles less than this criteria",
+    )
 
     compute_betti = BoolProperty(
-            name="Compute Betti Numbers",
-            description="Calculate the first 3 betti numbers of a mesh",
-            default=False)
+        name="Compute Betti Numbers",
+        description="Calculate the first 3 betti numbers of a mesh",
+        default=False,
+    )
 
     intersect_epsilon = FloatProperty(
-        name="Intersection tolerance", default=0.00001, min=0, step=0.000001,
-        description="Tolerance use to search for intersecting faces.")
+        name="Intersection tolerance",
+        default=0.00001,
+        min=0,
+        step=0.000001,
+        description="Tolerance use to search for intersecting faces.",
+    )
 
     show_extras = BoolProperty(
-        name="Additional Reports", default=False,
-        description="Show additional report generation options")
+        name="Additional Reports",
+        default=False,
+        description="Show additional report generation options",
+    )
 
 
 classes = [
@@ -392,14 +490,19 @@ class MeshQualityReportProperties(bpy.types.PropertyGroup):
     GAMER_OT_MeshStats_Betti_Numbers,
     GAMER_OT_MeshStats_Check_All,
     GAMER_OT_write_quality_info,
-    MeshQualityReportProperties]
+    MeshQualityReportProperties,
+]
+
 
 def register():
     from bpy.utils import register_class
+
     for cls in classes:
         register_class(make_annotations(cls))
 
+
 def unregister():
     from bpy.utils import unregister_class
+
     for cls in reversed(classes):
-        unregister_class(make_annotations(cls))
\ No newline at end of file
+        unregister_class(make_annotations(cls))
diff --git a/tools/blendgamer/src/report.py b/tools/blendgamer/src/report.py
index 1a075444..cdf354aa 100644
--- a/tools/blendgamer/src/report.py
+++ b/tools/blendgamer/src/report.py
@@ -1,22 +1,23 @@
-# ##### BEGIN GPL LICENSE BLOCK #####
+# ***************************************************************************
+# This file is part of the GAMer software.
+# Copyright (C) 2016-2021
+# by Christopher T. Lee and contributors
 #
-#  This program is free software; you can redistribute it and/or
-#  modify it under the terms of the GNU General Public License
-#  as published by the Free Software Foundation; either version 2
-#  of the License, or (at your option) any later version.
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
 #
-#  This program is distributed in the hope that it will be useful,
-#  but WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-#  GNU General Public License for more details.
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
 #
-#  You should have received a copy of the GNU General Public License
-#  along with this program; if not, write to the Free Software Foundation,
-#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# ##### END GPL LICENSE BLOCK #####
-
-# 
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, see 
+# or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+# Boston, MA 02111-1307 USA
+# ***************************************************************************
 
 # Report errors with the mesh.
 # This file was copied from Blender 3D Print Addon
diff --git a/tools/blendgamer/src/surfacemesh_ops.py b/tools/blendgamer/src/surfacemesh_ops.py
index 7196285a..4b7fdb0b 100644
--- a/tools/blendgamer/src/surfacemesh_ops.py
+++ b/tools/blendgamer/src/surfacemesh_ops.py
@@ -1,29 +1,37 @@
 # ***************************************************************************
 # This file is part of the GAMer software.
-# Copyright (C) 2016-2018
-# by Christopher Lee, Tom Bartol, John Moody, Rommie Amaro, J. Andrew McCammon,
-#    and Michael Holst
-
+# Copyright (C) 2016-2021
+# by Christopher T. Lee and contributors
+#
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
 # License as published by the Free Software Foundation; either
 # version 2.1 of the License, or (at your option) any later version.
-
+#
 # This library is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 # Lesser General Public License for more details.
-
+#
 # You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-# ***************************************************************************
+# License along with this library; if not, see 
+# or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+# Boston, MA 02111-1307 USA
+# **************************************************************************
 
 import bpy
 from bpy.props import (
-        BoolProperty, CollectionProperty, EnumProperty,
-        FloatProperty, FloatVectorProperty, IntProperty, IntVectorProperty,
-        PointerProperty, StringProperty, BoolVectorProperty)
+    BoolProperty,
+    CollectionProperty,
+    EnumProperty,
+    FloatProperty,
+    FloatVectorProperty,
+    IntProperty,
+    IntVectorProperty,
+    PointerProperty,
+    StringProperty,
+    BoolVectorProperty,
+)
 
 import blendgamer.pygamer as g
 from blendgamer.util import *
@@ -37,162 +45,220 @@
 
 
 class GAMER_OT_coarse_dense(bpy.types.Operator):
-    bl_idname       = "gamer.coarse_dense"
-    bl_label        = "Coarse Dense"
-    bl_description  = "Decimate selected dense areas of the mesh"
-    bl_options      = {'REGISTER', 'UNDO'}
+    bl_idname = "gamer.coarse_dense"
+    bl_label = "Coarse Dense"
+    bl_description = "Decimate selected dense areas of the mesh"
+    bl_options = {"REGISTER", "UNDO"}
 
     def execute(self, context):
         try:
             p = context.scene.gamer.surfmesh_improvement_properties
-            gmesh = blenderToGamer(autocorrect_normals=p.autocorrect_normals)
-            gmesh.coarse_dense(rate=p.dense_rate, numiter=p.dense_iter, rings=p.rings, verbose=p.verbose)
-            gamerToBlender(gmesh)
-
-            self.report({'INFO'}, "GAMer: coarse dense complete")
-            return {'FINISHED'}
+            gmesh = blender_to_gamer(autocorrect_normals=p.autocorrect_normals)
+            gmesh.coarse_dense(
+                rate=p.dense_rate,
+                numiter=p.dense_iter,
+                rings=p.rings,
+                verbose=p.verbose,
+            )
+            gamer_to_blender(gmesh)
+
+            self.report({"INFO"}, "GAMer: coarse dense complete")
+            return {"FINISHED"}
         except Exception as e:
-            self.report({'ERROR'}, str(e))
-            return {'CANCELLED'}
+            self.report({"ERROR"}, str(e))
+            return {"CANCELLED"}
 
 
 class GAMER_OT_coarse_flat(bpy.types.Operator):
-    bl_idname       = "gamer.coarse_flat"
-    bl_label        = "Coarse Flat"
-    bl_description  = "Decimate selected flat areas of the mesh"
-    bl_options      = {'REGISTER', 'UNDO'}
+    bl_idname = "gamer.coarse_flat"
+    bl_label = "Coarse Flat"
+    bl_description = "Decimate selected flat areas of the mesh"
+    bl_options = {"REGISTER", "UNDO"}
 
     def execute(self, context):
         try:
 
             p = context.scene.gamer.surfmesh_improvement_properties
-            gmesh = blenderToGamer(autocorrect_normals=p.autocorrect_normals)
-            gmesh.coarse_flat(rate=p.flat_rate, numiter=p.flat_iter, rings=p.rings, verbose=p.verbose)
-            gamerToBlender(gmesh)
-
-            self.report({'INFO'}, "GAMer: coarse flat complete")
-            return {'FINISHED'}
+            gmesh = blender_to_gamer(autocorrect_normals=p.autocorrect_normals)
+            gmesh.coarse_flat(
+                rate=p.flat_rate, numiter=p.flat_iter, rings=p.rings, verbose=p.verbose
+            )
+            gamer_to_blender(gmesh)
+
+            self.report({"INFO"}, "GAMer: coarse flat complete")
+            return {"FINISHED"}
         except Exception as e:
-            self.report({'ERROR'}, str(e))
-            return {'CANCELLED'}
+            self.report({"ERROR"}, str(e))
+            return {"CANCELLED"}
+
 
 class GAMER_OT_smooth(bpy.types.Operator):
-    bl_idname       = "gamer.smooth"
-    bl_label        = "Smooth"
-    bl_description  = "Smooth selected vertices of the mesh"
-    bl_options      = {'REGISTER', 'UNDO'}
+    bl_idname = "gamer.smooth"
+    bl_label = "Smooth"
+    bl_description = "Smooth selected vertices of the mesh"
+    bl_options = {"REGISTER", "UNDO"}
 
     def execute(self, context):
         try:
             p = context.scene.gamer.surfmesh_improvement_properties
-            gmesh = blenderToGamer(autocorrect_normals=p.autocorrect_normals)
-            gmesh.smooth(max_iter=p.smooth_iter, preserve_ridges=p.preserve_ridges, rings=p.rings, verbose=p.verbose)
-            gamerToBlender(gmesh)
-
-            self.report({'INFO'}, "GAMer: Smooth Mesh complete")
-            return {'FINISHED'}
+            gmesh = blender_to_gamer(autocorrect_normals=p.autocorrect_normals)
+            gmesh.smooth(
+                max_iter=p.smooth_iter,
+                preserve_ridges=p.preserve_ridges,
+                rings=p.rings,
+                verbose=p.verbose,
+            )
+            gamer_to_blender(gmesh)
+
+            self.report({"INFO"}, "GAMer: Smooth Mesh complete")
+            return {"FINISHED"}
         except Exception as e:
-            self.report({'ERROR'}, str(e))
-            return {'CANCELLED'}
+            self.report({"ERROR"}, str(e))
+            return {"CANCELLED"}
 
 
 class GAMER_OT_normal_smooth(bpy.types.Operator):
-    bl_idname       = "gamer.normal_smooth"
-    bl_label        = "Normal Smooth"
-    bl_description  = "Smooth facet normals of selected faces of the mesh"
-    bl_options      = {'REGISTER', 'UNDO'}
+    bl_idname = "gamer.normal_smooth"
+    bl_label = "Normal Smooth"
+    bl_description = "Smooth facet normals of selected faces of the mesh"
+    bl_options = {"REGISTER", "UNDO"}
 
     def execute(self, context):
         try:
             p = context.scene.gamer.surfmesh_improvement_properties
-            gmesh = blenderToGamer(autocorrect_normals=p.autocorrect_normals)
+            gmesh = blender_to_gamer(autocorrect_normals=p.autocorrect_normals)
             gmesh.normalSmooth(p.normSmoothAniso)
-            gamerToBlender(gmesh)
+            gamer_to_blender(gmesh)
 
-            self.report({'INFO'}, "GAMer: Normal Smooth complete")
-            return {'FINISHED'}
+            self.report({"INFO"}, "GAMer: Normal Smooth complete")
+            return {"FINISHED"}
         except Exception as e:
-            self.report({'ERROR'}, str(e))
-            return {'CANCELLED'}
+            self.report({"ERROR"}, str(e))
+            return {"CANCELLED"}
+
 
 class GAMER_OT_fill_holes(bpy.types.Operator):
-    bl_idname       = "gamer.fill_holes"
-    bl_label        = "Fill Holes"
-    bl_description  = "Triangulate holes"
-    bl_options      = {'REGISTER', 'UNDO'}
+    bl_idname = "gamer.fill_holes"
+    bl_label = "Fill Holes"
+    bl_description = "Triangulate holes"
+    bl_options = {"REGISTER", "UNDO"}
 
     def execute(self, context):
         try:
             p = context.scene.gamer.surfmesh_improvement_properties
-            gmesh = blenderToGamer(autocorrect_normals=p.autocorrect_normals)
+            gmesh = blender_to_gamer(autocorrect_normals=p.autocorrect_normals)
             gmesh.fillHoles()
-            gamerToBlender(gmesh)
+            gamer_to_blender(gmesh)
 
-            self.report({'INFO'}, "GAMer: Fill Holes complete")
-            return {'FINISHED'}
+            self.report({"INFO"}, "GAMer: Fill Holes complete")
+            return {"FINISHED"}
         except Exception as e:
-            self.report({'ERROR'}, str(e))
-            return {'CANCELLED'}
+            self.report({"ERROR"}, str(e))
+            return {"CANCELLED"}
 
 
 class SurfaceMeshImprovementProperties(bpy.types.PropertyGroup):
-    dense_rate      = FloatProperty(
-        name="Thresh", default=1, min=0.001, max=50.0, precision=4,
-        description="Threshold for coarsening dense areas")
-
-    dense_iter      = IntProperty(
-        name="Iter", default=1, min=1, max=15,
-        description="The number of coarse dense iterations to apply")
-
-    flat_rate       = FloatProperty(
-        name="Threshold", default=0.016, min=0.00001, max=0.5, precision=4,
-        description="The rate for coarsening flat areas")
-
-    flat_iter       = IntProperty(
-        name="Iter", default=1, min=1, max=15,
-        description="The number of coarse flat iterations to apply")
-
-    smooth_iter     = IntProperty(
-        name="Iter", default=10, min=1, max=50,
-        description="The number smoothing iterations to apply")
+    dense_rate = FloatProperty(
+        name="Thresh",
+        default=1,
+        min=0.001,
+        max=50.0,
+        precision=4,
+        description="Threshold for coarsening dense areas",
+    )
+
+    dense_iter = IntProperty(
+        name="Iter",
+        default=1,
+        min=1,
+        max=15,
+        description="The number of coarse dense iterations to apply",
+    )
+
+    flat_rate = FloatProperty(
+        name="Threshold",
+        default=0.016,
+        min=0.00001,
+        max=0.5,
+        precision=4,
+        description="The rate for coarsening flat areas",
+    )
+
+    flat_iter = IntProperty(
+        name="Iter",
+        default=1,
+        min=1,
+        max=15,
+        description="The number of coarse flat iterations to apply",
+    )
+
+    smooth_iter = IntProperty(
+        name="Iter",
+        default=10,
+        min=1,
+        max=50,
+        description="The number smoothing iterations to apply",
+    )
 
     preserve_ridges = BoolProperty(
-        name="Preserve ridges", default=False,
-        description="Skip flipping of edges which lie on ridges")
+        name="Preserve ridges",
+        default=False,
+        description="Skip flipping of edges which lie on ridges",
+    )
 
     normSmoothAniso = FloatProperty(
-        name="Anisotropic control", default=1.0, min=0,
-        description="The degree of anisotropy in LST correction for normal smooth")
+        name="Anisotropic control",
+        default=1.0,
+        min=0,
+        description="The degree of anisotropy in LST correction for normal smooth",
+    )
 
     advanced_options = BoolProperty(
-        name="Advanced options", default=False,
-        description="Show additional surface mesh improvement options")
+        name="Advanced options",
+        default=False,
+        description="Show additional surface mesh improvement options",
+    )
 
     autocorrect_normals = BoolProperty(
-        name="Autocorrect normals", default=True,
-        description="Automatically flip normals when mesh volume is 'negative'")
-
-    verbose         = BoolProperty(name="Verbose", default=False,
-        description="Print additional information to console")
-
-    rings           = IntProperty(
-        name="LST rings", default=2, min=1, max=5,
-        description="The number of neighborhood rings to consider for LST calculation")
+        name="Autocorrect normals",
+        default=True,
+        description="Automatically flip normals when mesh volume is 'negative'",
+    )
+
+    verbose = BoolProperty(
+        name="Verbose",
+        default=False,
+        description="Print additional information to console",
+    )
+
+    rings = IntProperty(
+        name="LST rings",
+        default=2,
+        min=1,
+        max=5,
+        description="The number of neighborhood rings to consider for LST calculation",
+    )
+
+
+classes = [
+    GAMER_OT_coarse_dense,
+    GAMER_OT_coarse_flat,
+    GAMER_OT_smooth,
+    GAMER_OT_normal_smooth,
+    GAMER_OT_fill_holes,
+    SurfaceMeshImprovementProperties,
+]
 
 
-classes = [GAMER_OT_coarse_dense,
-           GAMER_OT_coarse_flat,
-           GAMER_OT_smooth,
-           GAMER_OT_normal_smooth,
-           GAMER_OT_fill_holes,
-           SurfaceMeshImprovementProperties]
-
 def register():
     from bpy.utils import register_class
+
     for cls in classes:
         register_class(make_annotations(cls))
 
+
 def unregister():
     from bpy.utils import unregister_class
+
     for cls in reversed(classes):
         unregister_class(make_annotations(cls))
diff --git a/tools/blendgamer/src/tetrahedralization.py b/tools/blendgamer/src/tetrahedralization.py
index bf532ad5..617c85ea 100644
--- a/tools/blendgamer/src/tetrahedralization.py
+++ b/tools/blendgamer/src/tetrahedralization.py
@@ -1,28 +1,37 @@
 # ***************************************************************************
 # This file is part of the GAMer software.
-# Copyright (C) 2016-2018
-# by Christopher Lee, Tom Bartol, John Moody, Rommie Amaro, J. Andrew McCammon,
-#    and Michael Holst
-
+# Copyright (C) 2016-2021
+# by Christopher T. Lee and contributors
+#
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
 # License as published by the Free Software Foundation; either
 # version 2.1 of the License, or (at your option) any later version.
-
+#
 # This library is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 # Lesser General Public License for more details.
-
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-# ***************************************************************************
 #
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, see 
+# or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+# Boston, MA 02111-1307 USA
+# **************************************************************************
+
 import bpy
-from bpy.props import BoolProperty, CollectionProperty, EnumProperty, \
-    FloatProperty, FloatVectorProperty, IntProperty, IntVectorProperty, \
-    PointerProperty, StringProperty, BoolVectorProperty
+from bpy.props import (
+    BoolProperty,
+    CollectionProperty,
+    EnumProperty,
+    FloatProperty,
+    FloatVectorProperty,
+    IntProperty,
+    IntVectorProperty,
+    PointerProperty,
+    StringProperty,
+    BoolVectorProperty,
+)
 
 import blendgamer.pygamer as g
 from blendgamer.util import *
@@ -36,47 +45,74 @@ class GAMER_OT_tet_domain_add(bpy.types.Operator):
     bl_idname = "gamer.tet_domain_add"
     bl_label = "Add a Tet Domain"
     bl_description = "Add a new tetrahedralization domain"
-    bl_options = {'REGISTER', 'UNDO'}
+    bl_options = {"REGISTER", "UNDO"}
 
     def execute(self, context):
-        context.scene.gamer.tet_group.add_tet_domain(context)
-        self.report({'INFO'}, "Added a new Tet domain")
-        return {'FINISHED'}
+        context.scene.gamer.tet_group.add_tet_domain(self.report, context)
+        return {"FINISHED"}
 
 
 class GAMER_OT_tet_domain_remove(bpy.types.Operator):
     bl_idname = "gamer.tet_domain_remove"
     bl_label = "Remove a Tet Domain"
     bl_description = "Remove a tetrahedralization domain"
-    bl_options = {'REGISTER', 'UNDO'}
+    bl_options = {"REGISTER", "UNDO"}
 
     def execute(self, context):
         context.scene.gamer.tet_group.remove_active_tet_domain(context)
-        self.report({'INFO'}, "Deleted Active Tet Group")
-        return {'FINISHED'}
+        self.report({"INFO"}, "Deleted Active Tet Group")
+        return {"FINISHED"}
 
 
 class GAMER_OT_tet_domain_remove_all(bpy.types.Operator):
     bl_idname = "gamer.tet_domain_remove_all"
     bl_label = "Remove a Tet Domain"
     bl_description = "Remove a tetrahedralization domain"
-    bl_options = {'REGISTER', 'UNDO'}
+    bl_options = {"REGISTER", "UNDO"}
 
     def execute(self, context):
         context.scene.gamer.tet_group.remove_all_tet_domains(context)
-        self.report({'INFO'}, "Deleted Active Tet Groups")
-        return {'FINISHED'}
+        self.report({"INFO"}, "Deleted Active Tet Groups")
+        return {"FINISHED"}
 
 
 class GAMER_OT_tetrahedralize(bpy.types.Operator):
     bl_idname = "gamer.tetrahedralize"
     bl_label = "Tetrahedralize"
-    bl_description = ("Tetrahedralize")
-    bl_options = {'REGISTER'}
+    bl_description = "Tetrahedralize"
+    bl_options = {"REGISTER"}
+
+    def execute(self, context):
+        context.scene.gamer.tet_group.tetrahedralize(context, self.report)
+        return {"FINISHED"}
+
+    def invoke(self, context, event):
+        return self.execute(context)
+
+
+class GAMER_OT_surfaces_to_comsol(bpy.types.Operator):
+    bl_idname = "gamer.surfaces_to_comsol"
+    bl_label = "Export to Comsol"
+    bl_description = "Export surface meshes to Comsol"
+    bl_options = {"REGISTER"}
+
+    def execute(self, context):
+        context.scene.gamer.tet_group.surfaces_to_comsol(context, self.report)
+        return {"FINISHED"}
+
+    def invoke(self, context, event):
+        return self.execute(context)
+
+
+class GAMER_OT_cleanup_domains(bpy.types.Operator):
+    bl_idname = "gamer.cleanup_domains"
+    bl_label = "Clean up domains"
+    bl_description = "Clean up missing objects from domains"
+    bl_options = {"REGISTER"}
 
     def execute(self, context):
-        context.scene.gamer.tet_group.tetrahedralize(self.report)
-        return {'FINISHED'}
+        context.scene.gamer.tet_group.validate_domain_objects(context, self.report)
+        return {"FINISHED"}
 
     def invoke(self, context, event):
         return self.execute(context)
@@ -84,24 +120,27 @@ def invoke(self, context, event):
 
 class GAMerTetDomainPropertyGroup(bpy.types.PropertyGroup):
     # name = StringProperty()  # This is a reminder that "name" is already defined for all subclasses of PropertyGroup
-    domain_id = IntProperty(
-            name="ID", default=-1,
-            description="Domain ID")
-    object_name = StringProperty(
-            name="ObjName", default="",
-            description="Object Name")
-    marker = IntProperty(
-            name="Marker", default=-1,
-            description="Domain Marker Integer")
+    domain_id = IntProperty(name="ID", default=-1, description="Domain ID")
+    # Deprecated in 2.0.7 in favor of pointer properties
+    # object_name = StringProperty(name="ObjName", default="", description="Object Name")
+    object_pointer = PointerProperty(
+        type=bpy.types.Object, name="Test", description="Object"
+    )
+    marker = IntProperty(name="Marker", default=-1, description="Domain Marker Integer")
     is_hole = BoolProperty(
-            name="Hole", default=False,
-            description="Use this domain as a hole")
+        name="Hole", default=False, description="Use this domain as a hole"
+    )
     constrain_vol = BoolProperty(
-            name="Constrain Volume", default=False,
-            description="Constrain Volume")
+        name="Constrain Volume", default=False, description="Constrain Volume"
+    )
     vol_constraint = FloatProperty(
-            name="Vol Constraint", default=10.0,
-            description="Volume Constraint")
+        name="Vol Constraint", default=10.0, description="Volume Constraint"
+    )
+
+    def get_name(self):
+        if self.object_pointer is not None:
+            return self.object_pointer.name
+        return None
 
     def draw_layout(self, layout):
         row = layout.row()
@@ -117,9 +156,19 @@ def draw_layout(self, layout):
                 col = row.column()
                 col.prop(self, "vol_constraint")
 
-    def draw_item_in_row ( self, row ):
+    def draw_item_in_row(self, row):
+        name = self.get_name()
+        if name is None:
+            row.alert = True
+            row.label(text="Object is missing")
+            return
+        elif bpy.context.scene.objects.get(self.object_pointer.name) == None:
+            row.alert = True
+            row.label(text="Object(%s) is not linked to scene" % (name))
+            return
+
         col = row.column()
-        col.label(text=str(self.object_name))
+        col.label(text=name)
         col = row.column()
         col.label(text="Domain ID: " + str(self.domain_id))
         col = row.column()
@@ -128,109 +177,181 @@ def draw_item_in_row ( self, row ):
         else:
             col.label(text="Domain Marker: " + str(self.marker))
 
+
 class GAMerTetrahedralizationPropertyGroup(bpy.types.PropertyGroup):
     export_path = StringProperty(
-            name="Export Directory",
-            description="Path to directory where files will be created",
-            default="//", maxlen=1024, subtype='DIR_PATH'
-            )
+        name="Export Directory",
+        description="Path to directory where files will be created",
+        default="//",
+        maxlen=1024,
+        subtype="DIR_PATH",
+    )
     export_filebase = StringProperty(
-            name="Filename",
-            description="Base name of the files to export",
-            default="gamertetmesh", maxlen=1024, subtype='FILE_NAME'
-            )
+        name="Filename",
+        description="Base name of the files to export",
+        default="gamertetmesh",
+        maxlen=1024,
+        subtype="FILE_NAME",
+    )
     domain_list = CollectionProperty(
-            type=GAMerTetDomainPropertyGroup, name="Domain List")
-    active_domain_index = IntProperty(
-            name="Active Domain Index", default=0)
+        type=GAMerTetDomainPropertyGroup, name="Domain List"
+    )
+    active_domain_index = IntProperty(name="Active Domain Index", default=0)
     next_id = IntProperty(
-            name="Counter for Unique Domain IDs", default=1)  # Start ID's at 1 to confirm initialization
+        name="Counter for Unique Domain IDs", default=1
+    )  # Start ID's at 1 to confirm initialization
 
     show_settings = BoolProperty(
-            name="Tetrahedralization Settings", default=False,
-            description="Show more detailed settings")
+        name="Tetrahedralization Settings",
+        default=False,
+        description="Show more detailed settings",
+    )
 
     min_dihedral = FloatProperty(
-            name="Min Dihedral", default=10.0,
-            description="Minimum Dihedral in Degrees")
+        name="Min Dihedral", default=10.0, description="Minimum Dihedral in Degrees"
+    )
     max_aspect_ratio = FloatProperty(
-            name="Max Aspect Ratio", default=1.3,
-            description="Maximum Aspect Ratio")
+        name="Max Aspect Ratio", default=1.3, description="Maximum Aspect Ratio"
+    )
 
     ho_mesh = BoolProperty(
-            name="Higher order mesh generation", default=False,
-            description="Higher order mesh generation")
+        name="Higher order mesh generation",
+        default=False,
+        description="Higher order mesh generation",
+    )
 
     dolfin = BoolProperty(
-            name="DOLFIN", default=False,
-            description="Generate DOLFIN output")
+        name="DOLFIN", default=False, description="Generate DOLFIN output"
+    )
     # diffpack = BoolProperty(
     #         name="Diffpack", default=False,
     #         description="Generate Diffpack output")
     paraview = BoolProperty(
-            name="Paraview", default=False,
-            description="Generate Paraview output")
-
-    status = StringProperty ( name="status", default="" )
-
-
-    def add_tet_domain(self, context):
-        # print("Adding a Tet Domain")
-        """ Add a new tet domain to the list of tet domains for each selected object """
-        #mcell = context.scene.mcell
-
+        name="Paraview", default=False, description="Generate Paraview output"
+    )
+    comsol = BoolProperty(
+        name="Comsol (β)",
+        default=False,
+        description="(Untested) Generate Comsol mphtxt output",
+    )
+
+    status = StringProperty(name="status", default="")
+
+    def add_tet_domain(self, report, context):
+        """Add a new tet domain to the list of tet domains for each selected object"""
         # From the list of selected objects, only add MESH objects.
-        objs = [obj for obj in context.selected_objects if obj.type == 'MESH']
+        objs = [obj for obj in context.selected_objects if obj.type == "MESH"]
         if len(objs) > 0:
             for obj in objs:
                 # Check by name to see if it's already listed
-                current_domain_names = [d.object_name for d in self.domain_list]
-                print("Current domains = " + str(current_domain_names))
+                current_domain_names = [d.get_name() for d in self.domain_list]
+                # print("Current domains = " + str(current_domain_names))
                 if not (obj.name in current_domain_names):
-                    new_id = self.allocate_available_id()  # Do this first to check for empty list before adding
+                    new_id = (
+                        self.allocate_available_id()
+                    )  # Do this first to check for empty list before adding
                     new_dom = self.domain_list.add()
+                    new_dom.name = str(new_id)
                     new_dom.domain_id = new_id
                     new_dom.marker = new_id
-                    new_dom.object_name = obj.name
+                    new_dom.object_pointer = obj
                     self.active_domain_index = len(self.domain_list) - 1
+                    report({"INFO"}, "Added a new Tet domain")
 
     def remove_active_tet_domain(self, context):
         # print("Removing active Tet Domain")
-        """ Remove the active tet domain from the list of domains """
+        """Remove the active tet domain from the list of domains"""
         self.domain_list.remove(self.active_domain_index)
         self.active_domain_index -= 1
         if self.active_domain_index < 0:
             self.active_domain_index = 0
 
+    def remove_domain_by_index(self, idx):
+        """Remove the tet domain by index from the list of domains"""
+        self.domain_list.remove(idx)
+        if not (
+            self.active_domain_index >= 0
+            and self.active_domain_index < len(self.domain_list)
+        ):
+            self.active_domain_index = 0
+
     def remove_all_tet_domains(self, context):
         # print("Removing All Tet Domains")
-        """ Remove all tet domains from the list of domains """
+        """Remove all tet domains from the list of domains"""
         while len(self.domain_list) > 0:
             self.domain_list.remove(0)
         self.active_domain_index = 0
 
-
     def allocate_available_id(self):
-        """ Return a unique domain ID for a new domain """
+        """Return a unique domain ID for a new domain"""
         # print ("Next ID is " + str(self.next_id))
         if len(self.domain_list) <= 0:
             # Reset the ID to 1 when there are no more molecules
             self.next_id = 1
         self.next_id += 1
-        return(self.next_id - 1)
+        return self.next_id - 1
 
-    # def draw_panel(self, context, panel):
-    #     layout = panel.layout
-    #     self.draw_layout(context, layout)
+    def validate_domain_objects(self, context, report):
+        for i in range(len(self.domain_list) - 1, -1, -1):
+            d = self.domain_list[i]
+            if d.object_pointer is None:
+                self.remove_domain_by_index(i)
+            elif context.scene.objects.get(d.object_pointer.name) is None:
+                bpy.data.objects.remove(d.object_pointer)
+                self.remove_domain_by_index(i)
 
-    def tetrahedralize(self, report):
-        print ("######################## Begin Tetrahedralize ########################")
+    def surfaces_to_comsol(self, context, report):
+        self.validate_domain_objects(context, report)
+
+        filename = self.export_path + self.export_filebase
+        filename = bpy.path.abspath(filename)
+        gmeshes = list()
+
+        current_domain_names = [d.get_name() for d in self.domain_list]
+        print("Current domains = " + str(current_domain_names))
+
+        for d in self.domain_list:
+            obj = d.object_pointer
+            gmesh = blender_to_gamer(obj=obj, map_boundaries=True)
+            if not gmesh:
+                print("blender_to_gamer returned a gmesh of None")
+            else:
+                print(
+                    "Mesh %s: num verts: %d numfaces: %d"
+                    % (obj.name, gmesh.nVertices, gmesh.nFaces)
+                )
+                # Set the domain data on the SurfaceMesh these are the per/domain items as_hole, marker, and volume constraints
+                print("Closed: %d; Marker: %d" % (d.is_hole, d.marker))
+
+                globalInfo = gmesh.getRoot()
+                globalInfo.ishole = d.is_hole
+                globalInfo.marker = d.marker
+                globalInfo.useVolumeConstraint = d.constrain_vol
+                globalInfo.volumeConstraint = d.vol_constraint
+
+                # Write surface mesh to file for debug
+                # g.writeOFF("surfmesh_%s.off"%(obj_name), gmesh)
+
+                # Add the mesh
+                gmeshes.append(gmesh)
+        if len(gmeshes) > 0:
+            report(
+                {"INFO"},
+                "Writing surface meshes to Comsol file: " + filename + ".mphtxt",
+            )
+            g.writeComsol(filename + ".mphtxt", gmeshes)
+
+    def tetrahedralize(self, context, report):
+        self.validate_domain_objects(context, report)
+        print("######################## Begin Tetrahedralize ########################")
 
         # filename = self.tet_path
         filename = self.export_path + self.export_filebase
         filename = bpy.path.abspath(filename)
-        if not (self.dolfin or self.paraview):
-            self.status = "Please select an output format in Tetrahedralization Settings"
+        if not (self.dolfin or self.paraview or self.comsol):
+            self.status = (
+                "Please select an output format in Tetrahedralization Settings"
+            )
             print(self.status)
         else:
             self.status = ""
@@ -240,25 +361,33 @@ def tetrahedralize(self, report):
                 mesh_formats.append("dolfin")
             if self.paraview:
                 mesh_formats.append("paraview")
+            if self.comsol:
+                mesh_formats.append("comsol")
 
             # Vector of SurfaceMeshes
             # gmeshes = g.VectorSM()
             gmeshes = list()
-            for (obj_name,tet_domain) in [ (d.object_name,d) for d in self.domain_list ]:
-                print ( "obj_name = " + obj_name + ", tet_domain = " + str(tet_domain) )
+            # for (obj_name, tet_domain) in [
+            #     (d.object_name, d) for d in self.domain_list
+            # ]:
+            #     print("obj_name = " + obj_name + ", tet_domain = " + str(tet_domain))
 
-            current_domain_names = [ d.object_name for d in self.domain_list ]
-            print ( "Current domains = " + str(current_domain_names) )
+            current_domain_names = [d.get_name() for d in self.domain_list]
+            print("Current domains = " + str(current_domain_names))
 
             for d in self.domain_list:
-                obj = bpy.data.objects[d.object_name]
-                gmesh = blenderToGamer(obj=obj, map_boundaries=True)
+                obj = d.object_pointer
+                gmesh = blender_to_gamer(obj=obj, map_boundaries=True)
+
                 if not gmesh:
-                    print("blenderToGamer returned a gmesh of None")
+                    print("blender_to_gamer returned a gmesh of None")
                 else:
-                    print("Mesh %s: num verts: %d numfaces: %d" %(obj_name, gmesh.nVertices, gmesh.nFaces))
+                    print(
+                        "Mesh %s: num verts: %d numfaces: %d"
+                        % (obj.name, gmesh.nVertices, gmesh.nFaces)
+                    )
                     # Set the domain data on the SurfaceMesh these are the per/domain items as_hole, marker, and volume constraints
-                    print("Closed: %d; Marker: %d"%(d.is_hole, d.marker))
+                    print("Closed: %d; Marker: %d" % (d.is_hole, d.marker))
 
                     globalInfo = gmesh.getRoot()
                     globalInfo.ishole = d.is_hole
@@ -275,7 +404,10 @@ def tetrahedralize(self, report):
             # Tetrahedralize mesh
             if len(gmeshes) > 0:
 
-                quality_str = "q%.4f/%.4fO8/7AYVCa"%(self.max_aspect_ratio,self.min_dihedral)
+                quality_str = "q%.4f/%.4fO8/7AYVCa" % (
+                    self.max_aspect_ratio,
+                    self.min_dihedral,
+                )
 
                 # if self.constrain_vol:
                 #     # a%.8f volume constraint..
@@ -293,35 +425,49 @@ def tetrahedralize(self, report):
                 #     g.smoothMesh(tetmesh)
 
                 # Store mesh to files
-                tetmesh_formats  = ["dolfin", "paraview"]
-                tetmesh_suffices = [  ".xml", ".vtk"]
+                tetmesh_formats = ["dolfin", "paraview", "comsol"]
+                tetmesh_suffices = [".xml", ".vtk", ".mphtxt"]
 
                 for fmt in mesh_formats:
                     try:
                         suffix = tetmesh_suffices[tetmesh_formats.index(fmt)]
-                        report({'INFO'}, "Writing to " + fmt + " file: " + filename + suffix)
-                        if fmt == 'dolfin':
-                                g.writeDolfin(filename+suffix, tetmesh)
-                        if fmt == 'paraview':
-                            g.writeVTK(filename+suffix, tetmesh)
+                        report(
+                            {"INFO"},
+                            "Writing to " + fmt + " file: " + filename + suffix,
+                        )
+                        if fmt == "dolfin":
+                            g.writeDolfin(filename + suffix, tetmesh)
+                        if fmt == "paraview":
+                            g.writeVTK(filename + suffix, tetmesh)
+                        if fmt == "comsol":
+                            g.writeComsol(filename + suffix, tetmesh)
                     except Exception as ex:
-                        report({'ERROR'}, str(ex))
+                        report({"ERROR"}, str(ex))
 
-        print ( "######################## End Tetrahedralize ########################" )
+        print("######################## End Tetrahedralize ########################")
+
+
+classes = [
+    GAMER_OT_tet_domain_add,
+    GAMER_OT_tet_domain_remove,
+    GAMER_OT_tet_domain_remove_all,
+    GAMER_OT_tetrahedralize,
+    GAMER_OT_surfaces_to_comsol,
+    GAMER_OT_cleanup_domains,
+    GAMerTetDomainPropertyGroup,
+    GAMerTetrahedralizationPropertyGroup,
+]
 
-classes = [GAMER_OT_tet_domain_add,
-           GAMER_OT_tet_domain_remove,
-           GAMER_OT_tet_domain_remove_all,
-           GAMER_OT_tetrahedralize,
-           GAMerTetDomainPropertyGroup,
-           GAMerTetrahedralizationPropertyGroup]
 
 def register():
     from bpy.utils import register_class
+
     for cls in classes:
         register_class(make_annotations(cls))
 
+
 def unregister():
     from bpy.utils import unregister_class
+
     for cls in reversed(classes):
-        unregister_class(make_annotations(cls))
\ No newline at end of file
+        unregister_class(make_annotations(cls))
diff --git a/tools/blendgamer/src/ui.py b/tools/blendgamer/src/ui.py
index 7b629998..bce49cb7 100644
--- a/tools/blendgamer/src/ui.py
+++ b/tools/blendgamer/src/ui.py
@@ -1,49 +1,54 @@
-#****************************************************************************
+# ***************************************************************************
 # This file is part of the GAMer software.
-# Copyright (C) 2016-2018
-# by Christopher Lee, Tom Bartol, John Moody, Rommie Amaro, J. Andrew McCammon,
-#    and Michael Holst
-
+# Copyright (C) 2016-2021
+# by Christopher T. Lee and contributors
+#
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
 # License as published by the Free Software Foundation; either
 # version 2.1 of the License, or (at your option) any later version.
-
+#
 # This library is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 # Lesser General Public License for more details.
-
+#
 # You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+# License along with this library; if not, see 
+# or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+# Boston, MA 02111-1307 USA
 # ***************************************************************************
 
 import bpy
 import bmesh
 
 import blendgamer.report as report
-from blendgamer.util import UNSETID, UNSETMARKER, make_annotations, getMatByBndID
+from blendgamer.util import (
+    UNSETMARKER,
+    make_annotations,
+    get_material_by_bnd_id,
+    getBndUnsetMat,
+)
 import blendgamer.pygamer as pygamer
 
 
 if bpy.app.version < (2, 80, 0):
     REGION = "TOOLS"
-    BULB_ICON = 'LAMP'
-    ADD_ICON = 'ZOOMIN'
-    REMOVE_ICON = 'ZOOMOUT'
-    LOVE_ICON = 'COLOR_RED'
+    BULB_ICON = "LAMP"
+    ADD_ICON = "ZOOMIN"
+    REMOVE_ICON = "ZOOMOUT"
+    LOVE_ICON = "COLOR_RED"
 else:
     REGION = "UI"
-    BULB_ICON = 'LIGHT'
-    ADD_ICON = 'ADD'
-    REMOVE_ICON = 'REMOVE'
-    LOVE_ICON = 'FUND'
+    BULB_ICON = "LIGHT"
+    ADD_ICON = "ADD"
+    REMOVE_ICON = "REMOVE"
+    LOVE_ICON = "FUND"
 
 
 class GAMER_PT_versionerror(bpy.types.Panel):
     bl_label = "GAMer Version Mismatch"
-    bl_space_type = 'VIEW_3D'
+    bl_space_type = "VIEW_3D"
     bl_region_type = REGION
     bl_category = "GAMer"
 
@@ -52,7 +57,7 @@ def poll(cls, context):
         return (context.scene is not None) and context.scene.gamer.versionerror != 0
 
     def draw_header(self, context):
-        self.layout.label(text="", icon='CANCEL')
+        self.layout.label(text="", icon="CANCEL")
 
     def draw(self, context):
         layout = self.layout
@@ -60,14 +65,18 @@ def draw(self, context):
 
         if context.scene.gamer.versionerror > 0:
             layout.label(
-                text="Warning the current file was generated with a newer version of GAMer!")
+                text="Warning the current file was generated with a newer version of GAMer!"
+            )
             layout.label(
-                text="We strongly recommend you update the plugin before manipulating this file.")
+                text="We strongly recommend you update the plugin before manipulating this file."
+            )
         elif context.scene.gamer.versionerror < 0:
             layout.label(
-                text="Warning the current file was generated with an older version of GAMer which does not support autoupdate!")
+                text="Warning the current file was generated with an older version of GAMer could not autoupdate!"
+            )
             layout.label(
-                text="Please be careful manipulating this file as data may be lost.")
+                text="Please be careful manipulating this file as data may be lost."
+            )
             layout.label(text="Use the following converters at your own risk!")
             col = layout.column()
             col.operator("gamer.update_to_2_0_1_from_v_0_1")
@@ -75,33 +84,31 @@ def draw(self, context):
 
 class GAMER_PT_surfacemesh(bpy.types.Panel):
     bl_label = "Surface Mesh Conditioning"
-    bl_space_type = 'VIEW_3D'
+    bl_space_type = "VIEW_3D"
     bl_region_type = REGION
     bl_category = "GAMer"
-    bl_options = {'DEFAULT_CLOSED'}
+    bl_options = {"DEFAULT_CLOSED"}
 
     # Panel will be drawn if this returns True
     @classmethod
     def poll(cls, context):
-        return (context.scene is not None)
+        return context.scene is not None
 
     def draw_header(self, context):
-        self.layout.label(text="", icon='OUTLINER_DATA_MESH')
+        self.layout.label(text="", icon="OUTLINER_DATA_MESH")
 
     def draw(self, context):
         layout = self.layout
 
         smprops = context.scene.gamer.surfmesh_improvement_properties
         active_obj = context.active_object
-        if active_obj and (active_obj.type == 'MESH'):
+        if active_obj and (active_obj.type == "MESH"):
             box = layout.box()
             col = box.column(align=True)
             if not smprops.advanced_options:
-                col.prop(smprops, "advanced_options",
-                         icon='TRIA_RIGHT', emboss=False)
+                col.prop(smprops, "advanced_options", icon="TRIA_RIGHT", emboss=False)
             else:
-                col.prop(smprops, "advanced_options",
-                         icon='TRIA_DOWN', emboss=False)
+                col.prop(smprops, "advanced_options", icon="TRIA_DOWN", emboss=False)
                 col.prop(smprops, "autocorrect_normals")
                 col.prop(smprops, "verbose")
                 col.prop(smprops, "rings")
@@ -113,7 +120,7 @@ def draw(self, context):
             row.prop(smprops, "normSmoothAniso")
             col.operator("gamer.fill_holes")
 
-            if active_obj.mode == 'EDIT':
+            if active_obj.mode == "EDIT":
                 col = layout.column()
                 col.label(text="Local operations (applied to selection only): ")
 
@@ -137,10 +144,15 @@ def draw(self, context):
             else:
                 col = layout.column()
                 col.label(
-                    text="Change to Edit Mode to enable local improvement options", icon='INFO')
+                    text="Change to Edit Mode to enable local improvement options",
+                    icon="INFO",
+                )
         else:
             layout.label(
-                text="Select a mesh object to use GAMer mesh processing features", icon='HAND')
+                text="Select a mesh object to use GAMer mesh processing features",
+                icon="HAND",
+            )
+
 
 # class GAMER_PT_advanced_options(bpy.types.Panel):
 #     bl_label = "Advanced Options"
@@ -165,21 +177,21 @@ def draw(self, context):
 
 class GAMER_PT_mesh_quality(bpy.types.Panel):
     bl_label = "Mesh Quality Reporting"
-    bl_space_type = 'VIEW_3D'
+    bl_space_type = "VIEW_3D"
     bl_region_type = REGION
     bl_category = "GAMer"
-    bl_options = {'DEFAULT_CLOSED'}
+    bl_options = {"DEFAULT_CLOSED"}
 
     _type_to_icon = {
-        bmesh.types.BMVert: 'VERTEXSEL',
-        bmesh.types.BMEdge: 'EDGESEL',
-        bmesh.types.BMFace: 'FACESEL',
+        bmesh.types.BMVert: "VERTEXSEL",
+        bmesh.types.BMEdge: "EDGESEL",
+        bmesh.types.BMFace: "FACESEL",
     }
 
     # Panel will be drawn if this returns True
     @classmethod
     def poll(cls, context):
-        return (context.scene is not None)
+        return context.scene is not None
 
     # This method is from 3D Print Addon
     @staticmethod
@@ -195,10 +207,12 @@ def draw_report(layout, context):
             # box.alert = True
             for i, (text, data) in enumerate(info):
                 if obj and data and data[1]:
-                    bm_type, bm_array = data
-                    col.operator("gamer.meshstats_select_report",
-                                 text=text,
-                                 icon=GAMER_PT_mesh_quality._type_to_icon[bm_type]).index = i
+                    bm_type, _ = data
+                    col.operator(
+                        "gamer.meshstats_select_report",
+                        text=text,
+                        icon=GAMER_PT_mesh_quality._type_to_icon[bm_type],
+                    ).index = i
                 else:
                     col.label(text=text)
 
@@ -231,30 +245,32 @@ def draw(self, context):
 
         if context.scene.gamer.matplotlib_found:
             active_obj = context.active_object
-            if active_obj and (active_obj.type == 'MESH'):
+            if active_obj and (active_obj.type == "MESH"):
                 curveProp = active_obj.gamer.curvatures
                 row = col.row(align=True)
                 row.operator("gamer.compute_curvatures")
                 row.prop(curveProp, "algorithm", text="")
 
                 row = layout.row()
-                row.label(text="Computed Curvatures:", icon='FACESEL')
+                row.label(text="Computed Curvatures:", icon="FACESEL")
                 row = layout.row()
                 col = row.column()
-                col.template_list("GAMER_UL_curvature_list",
-                                  "GAMer Curvature List",
-                                  curveProp, "curvature_list",
-                                  curveProp, "active_index",
-                                  rows=2,
-                                  type='DEFAULT'
-                                  )
+                col.template_list(
+                    "GAMER_UL_curvature_list",
+                    "GAMer Curvature List",
+                    curveProp,
+                    "curvature_list",
+                    curveProp,
+                    "active_index",
+                    rows=2,
+                    type="DEFAULT",
+                )
                 col = row.column(align=True)
-                col.operator("gamer.remove_curvature",
-                             icon=REMOVE_ICON, text="")
-                col.operator("gamer.remove_all_curvatures", icon='X', text="")
+                col.operator("gamer.remove_curvature", icon=REMOVE_ICON, text="")
+                col.operator("gamer.remove_all_curvatures", icon="X", text="")
 
                 crv = curveProp.get_active_index()
-                if (crv != None):
+                if crv != None:
                     col = layout.column()
                     row = col.row()
                     row.label(text="Set active curvature properties:")
@@ -284,80 +300,83 @@ def draw(self, context):
                     # row.operator("gamer.plot_differences")
             else:
                 col.label(
-                    text="Select a mesh object to enable estimation of curvatures", icon=BULB_ICON)
+                    text="Select a mesh object to enable estimation of curvatures",
+                    icon=BULB_ICON,
+                )
         else:
-            col.label(
-                text="Curvature estimations require matplotlib.", icon=BULB_ICON)
+            col.label(text="Curvature estimations require matplotlib.", icon=BULB_ICON)
 
         box = layout.box()
         col = box.column()
 
         if not qProps.show_extras:
-            col.prop(qProps, "show_extras", icon='TRIA_RIGHT', emboss=False)
+            col.prop(qProps, "show_extras", icon="TRIA_RIGHT", emboss=False)
         else:
-            col.prop(qProps, "show_extras", icon='TRIA_DOWN', emboss=False)
+            col.prop(qProps, "show_extras", icon="TRIA_DOWN", emboss=False)
             col.prop(qProps, "export_path")
             col.prop(qProps, "export_filebase")
             col.operator("gamer.write_quality_info")
 
 
 class GAMER_UL_curvature_list(bpy.types.UIList):
-    def draw_item(self, context, layout, data, item, icon, active_data,
-                  active_propname, index):
+    def draw_item(
+        self, context, layout, data, item, icon, active_data, active_propname, index
+    ):
         """
         Draw the UI list for boundary markers
         """
         row = layout.row()
-        row.label(text=layout.enum_item_description(
-            item, 'algorithm', item.algorithm))
+        row.label(text=layout.enum_item_description(item, "algorithm", item.algorithm))
         # row.prop_enum(item, 'algorithm', item.algorithm, text=layout.enum_item_description(item, 'algorithm', item.algorithm))
-        row.prop_enum(item, 'curvatureType', item.curvatureType)
+        row.prop_enum(item, "curvatureType", item.curvatureType)
 
 
 class GAMER_PT_boundary_marking(bpy.types.Panel):
     bl_label = "Boundary Marking"
-    bl_space_type = 'VIEW_3D'
+    bl_space_type = "VIEW_3D"
     bl_region_type = REGION
     bl_category = "GAMer"
-    bl_options = {'DEFAULT_CLOSED'}
+    bl_options = {"DEFAULT_CLOSED"}
 
     @classmethod
     def poll(cls, context):
-        return (context.scene is not None)
+        return context.scene is not None
 
     def draw_header(self, context):
-        self.layout.label(text="", icon='TPAINT_HLT')
+        self.layout.label(text="", icon="TPAINT_HLT")
 
     def draw(self, context):
         layout = self.layout
 
         active_obj = context.active_object
-        if active_obj and (active_obj.type == 'MESH'):
+        if active_obj and (active_obj.type == "MESH"):
             markerProp = active_obj.gamer.markers
 
             row = layout.row()
             row.label(text="Unmarked marker value = %d" % (UNSETMARKER))
             row = layout.row()
 
-            bnd_unset_mat = getMatByBndID(UNSETID)
-            row.prop(bnd_unset_mat, 'diffuse_color',
-                     text="Unmarked boundary color")
+            bnd_unset_mat = getBndUnsetMat()
+            row.prop(bnd_unset_mat, "diffuse_color", text="Unmarked boundary color")
 
             row = layout.row()
-            row.label(text="Defined Boundaries:", icon='FACESEL')
+            row.label(text="Defined Boundaries:", icon="FACESEL")
             row = layout.row()
             col = row.column()
-            col.template_list("GAMER_UL_boundary_list",
-                              "GAMer Boundary List",
-                              markerProp, "boundary_list",
-                              markerProp, "active_bnd_index",
-                              rows=2,
-                              type='DEFAULT'
-                              )
+            col.template_list(
+                "GAMER_UL_boundary_list",
+                "GAMer Boundary List",
+                markerProp,
+                "boundary_list",
+                markerProp,
+                "active_bnd_index",
+                rows=2,
+                type="DEFAULT",
+            )
             col = row.column(align=True)
             col.operator("gamer.add_boundary", icon=ADD_ICON, text="")
             col.operator("gamer.remove_boundary", icon=REMOVE_ICON, text="")
-            col.operator("gamer.remove_all_boundaries", icon='X', text="")
+            col.operator("gamer.remove_all_boundaries", icon="X", text="")
 
             # Could have boundary item draw itself in new row here:
             active_bnd = markerProp.get_active_boundary()
@@ -374,11 +393,13 @@ def draw(self, context):
                 row.prop(active_bnd, "marker", text="")  # suppress default txt
 
                 row = layout.row()
-                if active_obj.mode == 'OBJECT':
+                if active_obj.mode == "OBJECT":
                     row.label(
-                        text="Change to Edit Mode to enable boundary assignment", icon='INFO')
+                        text="Change to Edit Mode to enable boundary assignment",
+                        icon="INFO",
+                    )
 
-            if active_obj.mode == 'EDIT' and active_bnd:
+            if active_obj.mode == "EDIT" and active_bnd:
                 row = layout.row()
                 sub = row.row(align=True)
                 sub.operator("gamer.assign_boundary_faces", text="Assign")
@@ -390,23 +411,30 @@ def draw(self, context):
 
                 layout.separator()
                 row = layout.row()
-                row.operator("gamer.select_all_boundary_faces",
-                             text="Select All Marked Boundaries")
-                row.operator("gamer.deselect_all_boundary_faces",
-                             text="Deselect All Marked Boundaries")
+                row.operator(
+                    "gamer.select_all_boundary_faces",
+                    text="Select All Marked Boundaries",
+                )
+                row.operator(
+                    "gamer.deselect_all_boundary_faces",
+                    text="Deselect All Marked Boundaries",
+                )
         else:
             layout.label(
-                text="Select a mesh object to use GAMer boundary marking features", icon='HAND')
+                text="Select a mesh object to use GAMer boundary marking features",
+                icon="HAND",
+            )
 
 
 class GAMER_UL_boundary_list(bpy.types.UIList):
-    def draw_item(self, context, layout, data, item, icon, active_data,
-                  active_propname, index):
+    def draw_item(
+        self, context, layout, data, item, icon, active_data, active_propname, index
+    ):
         """
         Draw the UI list for boundary markers
         """
         if item.status:
-            layout.label(text=item.boundary_name, icon='ERROR')
+            layout.label(text=item.boundary_name, icon="ERROR")
         else:
             layout.label(text=item.boundary_name)
 
@@ -418,47 +446,47 @@ def draw_item(self, context, layout, data, item, icon, active_data,
         col = split.column()
         col = split.column()
 
-        bnd_mat = getMatByBndID(item.boundary_id)
-        col.prop(bnd_mat, 'diffuse_color', text='')
+        bnd_mat = get_material_by_bnd_id(item.boundary_id)
+        col.prop(bnd_mat, "diffuse_color", text="")
         # Currently no check for if the referenced material does not exist
         # Also doesn't check if it's associated in a material slot for the
         # active object.
 
 
 class GAMER_UL_domain(bpy.types.UIList):
-    def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
-        # The draw_item function is called for each item of the collection that is visible in the list.
-        #   data is the RNA object containing the collection,
-        #   item is the current drawn item of the collection,
-        #   icon is the "computed" icon for the item (as an integer, because some objects like materials or textures
-        #        have custom icons ID, which are not available as enum items).
-        #   active_data is the RNA object containing the active property for the collection (i.e. integer pointing to the
-        #        active item of the collection).
-        #   active_propname is the name of the active property (use 'getattr(active_data, active_propname)').
-        #   index is index of the current item in the collection.
-        #   flt_flag is the result of the filtering process for this item.
-        #   Note: as index and flt_flag are optional arguments, you do not have to use/declare them here if you don't
-        #         need them.
-
+    def draw_item(
+        self, context, layout, data, item, icon, active_data, active_propname, index
+    ):
+        """The draw_item function is called for each item of the collection that is visible in the list.
+
+        Args:
+            context ([type]): the current Blender context
+            layout ([type]): the local layout
+            data ([type]): the RNA object containing the collection
+            item ([type]): the current drawn item of the collection
+            icon ([type]): the "computed" icon for the item (as an integer, because some objects like materials or textures  have custom icons ID, which are not available as enum items).
+            active_data ([type]): ct containing the active property for the collection (i.e. integer pointing to the active item of the collection).
+            active_propname ([type]): the name of the active property (use 'getattr(active_data, active_propname)')
+            index ([type]): index of the current item in the collection
+        """
         # The item will be a GAMerTetDomainPropertyGroup
         # Let it draw itself in a new row:
-
         item.draw_item_in_row(layout.row())
 
 
 class GAMER_PT_tetrahedralization(bpy.types.Panel):
     bl_label = "Tetrahedralization"
-    bl_space_type = 'VIEW_3D'
+    bl_space_type = "VIEW_3D"
     bl_region_type = REGION
     bl_category = "GAMer"
-    bl_options = {'DEFAULT_CLOSED'}
+    bl_options = {"DEFAULT_CLOSED"}
 
     @classmethod
     def poll(cls, context):
-        return (context.scene is not None)
+        return context.scene is not None
 
     def draw_header(self, context):
-        self.layout.label(text="", icon='MESH_ICOSPHERE')
+        self.layout.label(text="", icon="MESH_ICOSPHERE")
 
     def draw(self, context):
         layout = self.layout
@@ -471,15 +499,22 @@ def draw(self, context):
         row = layout.row()
         col = row.column()
 
-        col.template_list("GAMER_UL_domain", "",
-                          tetprops, "domain_list",
-                          tetprops, "active_domain_index",
-                          rows=2)
+        col.template_list(
+            "GAMER_UL_domain",
+            "",
+            tetprops,
+            "domain_list",
+            tetprops,
+            "active_domain_index",
+            rows=2,
+        )
 
         col = row.column(align=True)
         col.operator("gamer.tet_domain_add", icon=ADD_ICON, text="")
         col.operator("gamer.tet_domain_remove", icon=REMOVE_ICON, text="")
-        col.operator("gamer.tet_domain_remove_all", icon='X', text="")
+        col.operator("gamer.tet_domain_remove_all", icon="X", text="")
+        row = col.row()
+        row.operator("gamer.cleanup_domains", icon="GHOST_DISABLED", text="")
 
         if len(tetprops.domain_list) > 0:
             domain = tetprops.domain_list[tetprops.active_domain_index]
@@ -491,13 +526,11 @@ def draw(self, context):
 
             box = layout.box()
             row = box.row(align=True)
-            row.alignment = 'LEFT'
+            row.alignment = "LEFT"
             if not tetprops.show_settings:
-                row.prop(tetprops, "show_settings",
-                         icon='TRIA_RIGHT', emboss=False)
+                row.prop(tetprops, "show_settings", icon="TRIA_RIGHT", emboss=False)
             else:
-                row.prop(tetprops, "show_settings",
-                         icon='TRIA_DOWN', emboss=False)
+                row.prop(tetprops, "show_settings", icon="TRIA_DOWN", emboss=False)
 
                 row = box.row()
                 row.prop(tetprops, "export_path")
@@ -524,13 +557,14 @@ def draw(self, context):
                 col.prop(tetprops, "dolfin")
                 col = row.column()
                 col.prop(tetprops, "paraview")
+                col = row.column()
+                col.prop(tetprops, "comsol")
 
             row = layout.row()
-            icon = 'PROP_OFF'
+            icon = "PROP_OFF"
             if tetprops.dolfin or tetprops.paraview:
-                icon = 'PROP_ON'
-            row.operator("gamer.tetrahedralize",
-                         text="Tetrahedralize", icon=icon)
+                icon = "PROP_ON"
+            row.operator("gamer.tetrahedralize", text="Tetrahedralize", icon=icon)
             if len(tetprops.status) > 0:
                 row = layout.row()
                 row.label(text=tetprops.status, icon="ERROR")
@@ -538,14 +572,14 @@ def draw(self, context):
 
 class GAMER_PT_version(bpy.types.Panel):
     bl_label = "BlendGAMer Info"
-    bl_space_type = 'VIEW_3D'
+    bl_space_type = "VIEW_3D"
     bl_region_type = REGION
     bl_category = "GAMer"
-    bl_options = {'DEFAULT_CLOSED'}
+    bl_options = {"DEFAULT_CLOSED"}
 
     @classmethod
     def poll(cls, context):
-        return (context.scene is not None)
+        return context.scene is not None
 
     def draw_header(self, context):
         self.layout.label(text="", icon=LOVE_ICON)
@@ -556,28 +590,33 @@ def draw(self, context):
 
         layout.label(text="BlendGAMer %s" % (pygamer.__version__()))
         layout.operator(
-            "wm.url_open", text="How to Acknowledge").url = "https://gamer.readthedocs.io/en/latest/#acknowleding-the-use-of-gamer-in-your-work"
+            "wm.url_open", text="How to Acknowledge"
+        ).url = "https://gamer.readthedocs.io/en/latest/#acknowleding-the-use-of-gamer-in-your-work"
 
 
-classes = [GAMER_PT_versionerror,
-           GAMER_PT_surfacemesh,
-           # GAMER_PT_advanced_options,
-           GAMER_PT_mesh_quality,
-           GAMER_UL_curvature_list,
-           GAMER_PT_boundary_marking,
-           GAMER_UL_boundary_list,
-           GAMER_UL_domain,
-           GAMER_PT_tetrahedralization,
-           GAMER_PT_version]
+classes = [
+    GAMER_PT_versionerror,
+    GAMER_PT_surfacemesh,
+    # GAMER_PT_advanced_options,
+    GAMER_PT_mesh_quality,
+    GAMER_UL_curvature_list,
+    GAMER_PT_boundary_marking,
+    GAMER_UL_boundary_list,
+    GAMER_UL_domain,
+    GAMER_PT_tetrahedralization,
+    GAMER_PT_version,
+]
 
 
 def register():
     from bpy.utils import register_class
+
     for cls in classes:
         register_class(make_annotations(cls))
 
 
 def unregister():
     from bpy.utils import unregister_class
+
     for cls in reversed(classes):
         unregister_class(make_annotations(cls))
diff --git a/tools/blendgamer/src/util.py b/tools/blendgamer/src/util.py
index 43572843..2b13c8c0 100644
--- a/tools/blendgamer/src/util.py
+++ b/tools/blendgamer/src/util.py
@@ -1,23 +1,23 @@
 # ***************************************************************************
 # This file is part of the GAMer software.
-# Copyright (C) 2016-2018
-# by Christopher Lee, Tom Bartol, John Moody, Rommie Amaro, J. Andrew McCammon,
-#    and Michael Holst
-
+# Copyright (C) 2016-2021
+# by Christopher T. Lee and contributors
+#
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
 # License as published by the Free Software Foundation; either
 # version 2.1 of the License, or (at your option) any later version.
-
+#
 # This library is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 # Lesser General Public License for more details.
-
+#
 # You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-# ***************************************************************************
+# License along with this library; if not, see 
+# or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+# Boston, MA 02111-1307 USA
+# **************************************************************************
 
 import bpy
 import bmesh
@@ -30,13 +30,14 @@
 import blendgamer.pygamer.surfacemesh as sm
 
 # DEFINITIONS
-UNSETID = 0         # ID value for unset markers
-                    # Default should match MeshPolygonIntProperty default
-UNSETMARKER = -1    # Marker value to output for unset faces
-materialNamer = lambda bnd_id: "%s_mat"%(bnd_id)
+UNSETID = 0  # ID value for unset markers
+# Default should match MeshPolygonIntProperty default
+UNSETMARKER = -1  # Marker value to output for unset faces
+materialNamer = lambda bnd_id: "%s_mat" % (bnd_id)
 ##
 
-class ObjectMode():
+
+class ObjectMode:
     """Class defining a Blender Object mode context for with statement semantics
 
     Attributes
@@ -44,12 +45,13 @@ class ObjectMode():
     mode : str
         Enum of Blender's mode
     """
+
     def __enter__(self):
         """
         Enter runtime context to cache current mode and switch to Object mode.
         """
         self.mode = bpy.context.active_object.mode
-        bpy.ops.object.mode_set(mode='OBJECT')
+        bpy.ops.object.mode_set(mode="OBJECT")
 
     def __exit__(self, exc_type, exc_value, traceback):
         """
@@ -60,13 +62,14 @@ def __exit__(self, exc_type, exc_value, traceback):
         # Uncomment to handle exceptions
         # return True
 
+
 @contextmanager
 def BMeshContext(obj):
-    if obj.type != 'MESH':
-        raise RuntimeError('Expected an object of MESH type.')
+    if obj.type != "MESH":
+        raise RuntimeError("Expected an object of MESH type.")
 
     me = obj.data
-    if obj.mode == 'EDIT':
+    if obj.mode == "EDIT":
         bm = bmesh.from_edit_mesh(me)
     else:
         bm = bmesh.new()
@@ -74,7 +77,7 @@ def BMeshContext(obj):
 
     yield bm
 
-    if obj.mode == 'EDIT':
+    if obj.mode == "EDIT":
         if bpy.app.version < (2, 80, 0):
             bmesh.update_edit_mesh(me, tessface=True)
         else:
@@ -86,16 +89,18 @@ def BMeshContext(obj):
         bm.free()
         del bm
 
+
 @contextmanager
-def copiedBMeshContext(obj, transform = False, triangulate = False, apply_modifiers = False):
-    """Context for a bmesh copied from the mesh. Changes are not written back to the original mesh.
-    """
-    if obj.type != 'MESH':
-        raise RuntimeError('Expected object of MESH type.')
+def copiedBMeshContext(obj, transform=False, triangulate=False, apply_modifiers=False):
+    """Context for a bmesh copied from the mesh. Changes are not written back to the original mesh."""
+    if obj.type != "MESH":
+        raise RuntimeError("Expected object of MESH type.")
 
     if apply_modifiers and obj.modifiers:
         if bpy.app.version < (2, 80, 0):
-            me = obj.to_mesh(bpy.context.scene, apply_modifiers=True, settings='PREVIEW')
+            me = obj.to_mesh(
+                bpy.context.scene, apply_modifiers=True, settings="PREVIEW"
+            )
         else:
             me = obj.to_mesh()
         bm = bmesh.new()
@@ -103,7 +108,7 @@ def copiedBMeshContext(obj, transform = False, triangulate = False, apply_modifi
         bpy.data.meshes.remove(me)
     else:
         me = obj.data
-        if obj.mode == 'EDIT':
+        if obj.mode == "EDIT":
             bm_orig = bmesh.from_edit_mesh(me)
             bm = bm_orig.copy()
         else:
@@ -122,7 +127,7 @@ def copiedBMeshContext(obj, transform = False, triangulate = False, apply_modifi
     del bm
 
 
-def getMatByBndID(boundary_id):
+def get_material_by_bnd_id(boundary_id):
     """
     Gets a boundary material from bpy.data.materials by boundary ID.
 
@@ -154,15 +159,16 @@ def getBndUnsetMat():
     bpy.types.Material
         Returns the material corresponding to unmarked boundaries.
     """
-    bnd_unset_mat = getMatByBndID(UNSETID)
+    bnd_unset_mat = get_material_by_bnd_id(UNSETID)
     # if bnd_unset_mat is not defined, then create it
     if bnd_unset_mat is None:
-        bnd_unset_mat = bpy.data.materials.new('bnd_unset_mat')
+        bnd_unset_mat = bpy.data.materials.new("bnd_unset_mat")
         bnd_unset_mat.use_fake_user = True
         bnd_unset_mat.gamer.boundary_id = UNSETID
 
     return bnd_unset_mat
 
+
 def getMarkerLayer(obj):
     """Gets the boundary marker layer data of an object
 
@@ -181,20 +187,32 @@ def getMarkerLayer(obj):
     RuntimeError
         Data layers can only be accessed in 'OBJECT' mode
     """
-    if obj.mode != 'OBJECT':
-        raise RuntimeError("Blender Layers (Markers) can only be accessed in 'OBJECT' mode.")
-    markerLayer = obj.data.polygon_layers_int.get('marker')
+    if obj.mode != "OBJECT":
+        raise RuntimeError(
+            "Blender Layers (Markers) can only be accessed in 'OBJECT' mode."
+        )
+    markerLayer = obj.data.polygon_layers_int.get("marker")
     # If the layer doesn't exist yet, create it!
     if not markerLayer:
-        markerLayer = obj.data.polygon_layers_int.new(name='marker')
+        markerLayer = obj.data.polygon_layers_int.new(name="marker")
     return markerLayer.data
 
+
 def getBMeshMarkerLayer(bm):
-    markerLayer = bm.faces.layers.int.get('marker')
+    """Get the boundary marker layer from a BMesh
+
+    Args:
+        bm (bpy.BMesh): BMesh object
+
+    Returns:
+        MarkerLayer: Marker layer
+    """
+    markerLayer = bm.faces.layers.int.get("marker")
     if not markerLayer:
-        markerLayer = bm.faces.layers.int.new('marker')
+        markerLayer = bm.faces.layers.int.new("marker")
     return markerLayer
 
+
 def getCurvatureLayer(obj, algo, curvatureType):
     """Get the data layer corresponding to a specific curvature type
 
@@ -217,15 +235,18 @@ def getCurvatureLayer(obj, algo, curvatureType):
     RuntimeError
     Description
     """
-    name = "%s%s"%(algo,curvatureType)
-    if obj.mode != 'OBJECT':
-        raise RuntimeError("Blender Layers (%s) can only be accessed in 'OBJECT' mode."%(name))
+    name = "%s%s" % (algo, curvatureType)
+    if obj.mode != "OBJECT":
+        raise RuntimeError(
+            "Blender Layers (%s) can only be accessed in 'OBJECT' mode." % (name)
+        )
     layer = obj.data.vertex_layers_float.get(name)
     # If the layer doesn't exist yet, create it!
     if not layer:
         layer = obj.data.vertex_layers_float.new(name=name)
     return layer.data
 
+
 def getActiveMeshObject():
     """
     Returns the active object if it is a mesh or None
@@ -242,7 +263,7 @@ def getActiveMeshObject():
     """
     obj = bpy.context.active_object
     if obj:
-        if obj.type == 'MESH':
+        if obj.type == "MESH":
             #  # Auto select object if in EDIT mode
             # # This prevents being 'selected' in EDIT mode but really unselected
             # if obj.mode == 'EDIT':
@@ -250,9 +271,13 @@ def getActiveMeshObject():
             # if obj.select:
             return obj
         else:
-            raise RuntimeError("Active object is not a MESH. Please select a MESH to use this feature.")
+            raise RuntimeError(
+                "Active object is not a MESH. Please select a MESH to use this feature."
+            )
     else:
-        raise RuntimeError("No active object! Please select a MESH to use this feature.")
+        raise RuntimeError(
+            "No active object! Please select a MESH to use this feature."
+        )
 
 
 def getMeshVertices(obj, get_selected_vertices=False):
@@ -272,7 +297,7 @@ def getMeshVertices(obj, get_selected_vertices=False):
         Description
     """
     mesh = obj.data
-    vertToVec = lambda v : [v[0], v[1], v[2]]
+    vertToVec = lambda v: [v[0], v[1], v[2]]
 
     vertices = [vertToVec(v.co) for v in mesh.vertices]
 
@@ -283,28 +308,19 @@ def getMeshVertices(obj, get_selected_vertices=False):
         return vertices
 
 
-def blenderToGamer(obj=None, map_boundaries=False, autocorrect_normals=True):
-    """Convert object to GAMer mesh.
+def blender_to_gamer(obj=None, map_boundaries=False, autocorrect_normals=True):
+    """Construct a GAMer mesh from a triangulated blender mesh
 
-    Parameters
-    ----------
-    obj : None, optional
-        Object to convert
-    map_boundaries : bool, optional
-        True if boundary values should be mapped to markers
-        instead of boundary_id
-    autocorrect_normals : bool, optional
-        Automatically flip normals so that volume is positive
+    Args:
+        obj (, optional): Object to convert. Defaults to None which gets the active mesh object.
+        map_boundaries (bool, optional): True if boundary values should be mapped to markers instead of boundary_id. Defaults to False.
+        autocorrect_normals (bool, optional): Automatically flip normals so that volume is positive. Defaults to True.
 
-    Returns
-    -------
-    gamer.SurfaceMesh
-        GAMer surface mesh from Blender mesh
+    Raises:
+        RuntimeError: Complains if something prevents conversion to a valid GAMer mesh
 
-    Raises
-    ------
-    RuntimeError
-        Complains if something prevents conversion to a valid GAMer mesh.
+    Returns:
+        gamer.SurfaceMesh: GAMer surface mesh from Blender mesh
     """
     # Get the selected mesh
     if not obj:
@@ -315,25 +331,27 @@ def blenderToGamer(obj=None, map_boundaries=False, autocorrect_normals=True):
 
     with ObjectMode():
         # Grab vertices
-        vertices, selected_vertices = getMeshVertices(obj, get_selected_vertices = True)
+        vertices, selected_vertices = getMeshVertices(obj, get_selected_vertices=True)
         # Get world location and offset each vertex with this value
-        gmesh = sm.SurfaceMesh()   # Init GAMer SurfaceMesh
-
-        def addVertex(co, sel): # functor to addVertices
-            gmesh.addVertex(sm.Vertex(
-                co[0],   # x position
-                co[1],   # y position
-                co[2],   # z position
-                0,
-                bool(sel)                 # selected flag
-            ))
+        gmesh = sm.SurfaceMesh()  # Init GAMer SurfaceMesh
+
+        def addVertex(co, sel):  # functor to addVertices
+            gmesh.addVertex(
+                sm.Vertex(
+                    co[0],  # x position
+                    co[1],  # y position
+                    co[2],  # z position
+                    0,
+                    bool(sel),  # selected flag
+                )
+            )
 
         # If all vertices are selected
         if len(selected_vertices) == len(vertices):
             selected_vertices = np.ones(len(vertices), dtype=bool)
         else:
             selection = np.zeros(len(vertices), dtype=bool)
-            selection[selected_vertices] = 1    # Set selected to True
+            selection[selected_vertices] = 1  # Set selected to True
             selected_vertices = selection
 
         # Zip args and pass to addVertex functor
@@ -360,18 +378,23 @@ def addVertex(co, sel): # functor to addVertices
         for face in faces:
             vertices = deque(face.vertices)
             if len(vertices) != 3:
-                raise RuntimeError("Encountered a non-triangular face. GAMer only works with triangulated meshes.")
+                raise RuntimeError(
+                    "Encountered a non-triangular face. GAMer only works with triangulated meshes."
+                )
 
             # Get the orientation from Blender
             max_val = max(vertices)
             max_idx = vertices.index(max_val)
             if max_idx != 2:
-                vertices.rotate(2-max_idx)
-            orientation = -1    # 1 < 0 < 2
-            if(vertices[0] < vertices[1]):
+                vertices.rotate(2 - max_idx)
+            orientation = -1  # 1 < 0 < 2
+            if vertices[0] < vertices[1]:
                 # 0 < 1 < 2
                 orientation = 1
-            gmesh.insertFace(list(vertices), sm.Face(orientation, boundaries[face.index], bool(face.select)))
+            gmesh.insertFace(
+                list(vertices),
+                sm.Face(orientation, boundaries[face.index], bool(face.select)),
+            )
     # Ensure all face orientations are set
     gmesh.init_orientation()
     gmesh.check_orientation()
@@ -380,32 +403,29 @@ def addVertex(co, sel): # functor to addVertices
         if autocorrect_normals:
             gmesh.flipNormals()
         else:
-            raise RuntimeError("Mesh has negative volume. Recompute normals to be outward facing.")
+            raise RuntimeError(
+                "Mesh has negative volume. Recompute normals to be outward facing."
+            )
     return gmesh
 
 
-def gamerToBlender(gmesh,
-        obj = None,
-        mesh_name="gamer_improved"):
-    """Pass GAMer Surface Mesh back to Blender
+def gamer_to_blender(gmesh, obj=None, mesh_name="gamer_improved"):
+    """Pass GAMer Surface Mesh back to Blender]
 
-    Parameters
-    ----------
-    gmesh : gamer.SurfaceMesh
-        GAMer surface mesh
-    obj : None, optional
-        Object to operate on. Active object is used if unset
-    mesh_name : str, optional
-        Name of the new mesh data block
+    Args:
+        gmesh (gamer.SurfaceMesh): GAMer surface mesh
+        obj (bpy.types.Object, optional):  Object to operate on. Active object is used if unset. Defaults to None.
+        mesh_name (str, optional): Name of the new mesh data block. Defaults to "gamer_improved".
 
-    Raises
-    ------
-    RuntimeError
-        If an undefined behavior encountered
+    Raises:
+        RuntimeError: If an undefined behavior encountered.
     """
+
     # Check arguments
     if not isinstance(gmesh, sm.SurfaceMesh):
-        raise RuntimeError("gamerToBlender expected a pygamer.surfacemesh.SurfaceMesh object.")
+        raise RuntimeError(
+            "gamer_to_blender expected a pygamer.surfacemesh.SurfaceMesh object."
+        )
 
     if not obj:
         obj = getActiveMeshObject()
@@ -414,12 +434,12 @@ def gamerToBlender(gmesh,
 
     mode = obj.mode
 
-    verts = []      # Array of vertex coordinates
-    idxMap = {}     # Dictionary of gamer indices to renumbered indices
+    verts = []  # Array of vertex coordinates
+    idxMap = {}  # Dictionary of gamer indices to renumbered indices
     for i, vid in enumerate(gmesh.vertexIDs):
         v = vid.data()
         verts.append((v[0], v[1], v[2]))
-        idxMap[gmesh.getName(vid)[0]] = i    # Reindex starting at 0
+        idxMap[gmesh.getName(vid)[0]] = i  # Reindex starting at 0
 
     faces = []
     selectedFaces = []
@@ -464,9 +484,9 @@ def gamerToBlender(gmesh,
     # Repaint boundaries
     obj.gamer.markers.repaint_boundaries(bpy.context)
     # Deselect all first
-    bpy.ops.object.mode_set(mode='EDIT')
-    bpy.ops.mesh.select_all(action='DESELECT')
-    bpy.ops.object.mode_set(mode='OBJECT')
+    bpy.ops.object.mode_set(mode="EDIT")
+    bpy.ops.mesh.select_all(action="DESELECT")
+    bpy.ops.object.mode_set(mode="OBJECT")
 
     bm = bmesh_from_object(obj)
 
@@ -490,7 +510,6 @@ def clean_float(text):
     return text
 
 
-
 def bmesh_from_object(obj):
     """
     Object/Edit Mode get mesh, use bmesh_to_object() to write back.
@@ -506,7 +525,7 @@ def bmesh_from_object(obj):
         Description
     """
     me = obj.data
-    is_editmode = (obj.mode == 'EDIT')
+    is_editmode = obj.mode == "EDIT"
     if is_editmode:
         bm = bmesh.from_edit_mesh(me)
     else:
@@ -528,7 +547,7 @@ def bmesh_to_object(obj, bm):
         Description
     """
     me = obj.data
-    is_editmode = (obj.mode == 'EDIT')
+    is_editmode = obj.mode == "EDIT"
     if is_editmode:
         bmesh.update_edit_mesh(me, True)
     else:
@@ -552,12 +571,21 @@ def make_annotations(cls):
     """
     if bpy.app.version < (2, 80):
         return cls
-    bl_props = {k: v for k, v in cls.__dict__.items() if isinstance(v, tuple)}
+
+    if bpy.app.version >= (2, 93, 0):
+        bl_props = {
+            k: v
+            for k, v in cls.__dict__.items()
+            if isinstance(v, bpy.props._PropertyDeferred)
+        }
+    else:
+        bl_props = {k: v for k, v in cls.__dict__.items() if isinstance(v, tuple)}
+
     if bl_props:
-        if '__annotations__' not in cls.__dict__:
-            setattr(cls, '__annotations__', {})
-        annotations = cls.__dict__['__annotations__']
+        if "__annotations__" not in cls.__dict__:
+            setattr(cls, "__annotations__", {})
+        annotations = cls.__dict__["__annotations__"]
         for k, v in bl_props.items():
             annotations[k] = v
             delattr(cls, k)
-    return cls
\ No newline at end of file
+    return cls
diff --git a/tools/blendgamer/src/versions.py b/tools/blendgamer/src/versions.py
index 2e607cfe..a4458998 100644
--- a/tools/blendgamer/src/versions.py
+++ b/tools/blendgamer/src/versions.py
@@ -1,23 +1,23 @@
 # ***************************************************************************
 # This file is part of the GAMer software.
-# Copyright (C) 2016-2018
-# by Christopher Lee, Tom Bartol, John Moody, Rommie Amaro, J. Andrew McCammon,
-#    and Michael Holst
-
+# Copyright (C) 2016-2021
+# by Christopher T. Lee and contributors
+#
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
 # License as published by the Free Software Foundation; either
 # version 2.1 of the License, or (at your option) any later version.
-
+#
 # This library is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 # Lesser General Public License for more details.
-
+#
 # You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-# ***************************************************************************
+# License along with this library; if not, see 
+# or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+# Boston, MA 02111-1307 USA
+# **************************************************************************
 
 import bpy
 import sys
@@ -31,80 +31,85 @@
 class GAMER_OT_prompt_update(bpy.types.Operator):
     bl_idname = "gamer.prompt_update"
     bl_label = "Warn to update GAMer addon"
-    bl_options = {'BLOCKING', 'INTERNAL'}
+    bl_options = {"BLOCKING", "INTERNAL"}
 
     def execute(self, context):
-        self.report({'WARNING'},
-                    "Blendfile was generated with a newer version of GAMer.")
-        return {'FINISHED'}
+        self.report(
+            {"WARNING"}, "Blendfile was generated with a newer version of GAMer."
+        )
+        return {"FINISHED"}
 
 
 class GAMER_OT_prompt_old_version(bpy.types.Operator):
     bl_idname = "gamer.prompt_old_version"
     bl_label = "Warn that GAMer cannot convert file automatically"
-    bl_options = {'BLOCKING', 'INTERNAL'}
+    bl_options = {"BLOCKING", "INTERNAL"}
 
     def execute(self, context):
-        self.report({'WARNING'},
-                    "Blendfile was generated with a version that does not support automatic conversion.")
-        return {'FINISHED'}
+        self.report(
+            {"WARNING"},
+            "Blendfile was generated with a version that does not support automatic conversion.",
+        )
+        return {"FINISHED"}
 
 
 class GAMER_OT_update_to_2_0_1_from_v_0_1(bpy.types.Operator):
     bl_idname = "gamer.update_to_2_0_1_from_v_0_1"
     bl_label = "Update from v0.1 to v2.0.1"
     bl_description = "Update GAMer version to 2.0.1 from v0.1"
-    bl_options = {'REGISTER', 'UNDO'}
+    bl_options = {"REGISTER", "UNDO"}
 
     def execute(self, context):
         for obj in context.scene.objects:
             # bpy.context.scene.objects.active = obj
-            if obj.type == 'MESH':
+            if obj.type == "MESH":
                 print("\n" + "=" * 30 + "\n", obj, "\n" + "=" * 30)
-                if not 'boundaries' in obj:
+                if not "boundaries" in obj:
                     continue
                 hidden = False
                 if obj.hide:
                     hidden = True
                     obj.hide = False
-                obj.gamer.remove_all_boundaries(context)
-                for key, bdry in obj['boundaries'].items():
+                obj.gamer.markers.remove_all_boundaries(context)
+                for key, bdry in obj["boundaries"].items():
                     print("Migrating boundary: %s" % (key))
 
                     # First move the material...
-                    mat = bpy.data.materials[key + '_mat']
+                    mat = bpy.data.materials[key + "_mat"]
                     newBdryID = context.scene.gamer.boundary_id_counter + 1
                     mat.name = materialNamer(newBdryID)
                     mat.gamer.boundary_id = newBdryID
                     mat.use_fake_user = True
 
-                    obj.gamer.add_boundary(context)
-                    newBdry = obj.gamer.markers.boundary_list[obj.gamer.active_bnd_index]
+                    obj.gamer.markers.add_boundary(context)
+                    newBdry = obj.gamer.markers.boundary_list[
+                        obj.gamer.markers.active_bnd_index
+                    ]
 
-                    newBdry.boundary_name = 'NewBoundaryFrom_%s' % (key)
-                    newBdry.marker = bdry['marker']
+                    newBdry.boundary_name = "NewBoundaryFrom_%s" % (key)
+                    newBdry.marker = bdry["marker"]
 
                     # # Deselect all
-                    bpy.ops.object.mode_set(mode='EDIT')
-                    bpy.ops.mesh.select_all(action='DESELECT')
-                    bpy.ops.object.mode_set(mode='OBJECT')
+                    bpy.ops.object.mode_set(mode="EDIT")
+                    bpy.ops.mesh.select_all(action="DESELECT")
+                    bpy.ops.object.mode_set(mode="OBJECT")
 
                     # Select faces of interest
-                    for faces in bdry['faces'].values():
+                    for faces in bdry["faces"].values():
                         for i in faces:
                             obj.data.polygons[i].select = True
-                    bpy.ops.object.mode_set(mode='EDIT')
+                    bpy.ops.object.mode_set(mode="EDIT")
                     newBdry.assign_boundary_faces(context)
-                    bpy.ops.object.mode_set(mode='OBJECT')
-                del obj['boundaries']
-                if 'id_counter' in obj.gamer:
-                    del obj.gamer['id_counter']
-                if 'include' in obj.gamer:
-                    del obj.gamer['include']
+                    bpy.ops.object.mode_set(mode="OBJECT")
+                del obj["boundaries"]
+                if "id_counter" in obj.gamer:
+                    del obj.gamer["id_counter"]
+                if "include" in obj.gamer:
+                    del obj.gamer["include"]
                 obj.hide = hidden
         context.scene.gamer.gamer_version = "(2,0,1)"
         checkVersion()
-        return {'FINISHED'}
+        return {"FINISHED"}
 
 
 def migrate2_0_1__2_0_6():
@@ -112,22 +117,52 @@ def migrate2_0_1__2_0_6():
     Migrate metadata formats from 2.0.1 <= v < 2.0.6 to v2.0.6
     """
     for obj in bpy.data.objects:
-        if obj.type == 'MESH':
+        if obj.type == "MESH":
             # First initialize the data-blocks
             markerProp = obj.gamer.markers
             bl = markerProp.boundary_list
 
             # Move boundary_list
-            if 'boundary_list' in obj['gamer']:
+            if "boundary_list" in obj["gamer"]:
                 # Migrate boundary marker info
-                obj['gamer']['markers']['boundary_list'] = obj['gamer']['boundary_list']
-                del obj['gamer']['boundary_list']
+                obj["gamer"]["markers"]["boundary_list"] = obj["gamer"]["boundary_list"]
+                del obj["gamer"]["boundary_list"]
             # Remove active_bnd_index
-            if 'active_bnd_index' in obj['gamer']:
-                del obj['gamer']['active_bnd_index']
+            if "active_bnd_index" in obj["gamer"]:
+                del obj["gamer"]["active_bnd_index"]
             # Remove include
-            if 'include' in obj['gamer']:
-                del obj['gamer']['include']
+            if "include" in obj["gamer"]:
+                del obj["gamer"]["include"]
+
+
+def migrate2_0_6__2_0_7():
+    """
+    Migrate metadata formats from 2.0.6 <= v < 2.0.7 to v2.0.7
+    """
+    scene = bpy.context.scene
+    domain_list = scene.gamer.tet_group.domain_list
+    for i in range(len(domain_list) - 1, -1, -1):
+        domain = domain_list[i]
+        # print("Processing: ", domain["object_name"])
+
+        if "object_name" in domain:
+            if domain["object_name"] not in scene.objects: 
+                print("Domain %s is missing... removing it from the domain list" % (domain["object_name"]))
+                scene.gamer.tet_group.remove_domain_by_index(i)
+            else:
+                obj = scene.objects[domain["object_name"]]
+                if obj is not None:
+                    domain.object_pointer = obj
+                    del domain["object_name"]
+                else:
+                    print("Domain %s is missing from the scene... removing it from the domain list" % (domain["object_name"]))
+                    scene.gamer.tet_group.remove_domain_by_index(i)
+        else:
+            if domain.object_pointer is None:
+                print("Unrecoverable domain found... removing it")
+                scene.gamer.tet_group.remove_domain_by_index(i)
+
+    scene.gamer.tet_group.validate_domain_objects(bpy.context, None)
 
 
 def getGamerVersion():
@@ -138,7 +173,7 @@ def getGamerVersion():
     tuple
         Tuple corresponding to the BlendGAMer version
     """
-    return sys.modules['blendgamer'].bl_info.get('version', (-1, -1, -1))
+    return sys.modules["blendgamer"].bl_info.get("version", (-1, -1, -1))
 
 
 def checkVersion():
@@ -154,54 +189,66 @@ def checkVersion():
     if it cannot migrate metadata automatically.
     """
     scene = bpy.context.scene
-    print("Blendfile contains GAMer v%s metadata" %
-          (scene.gamer.gamer_version))
+    print("Blendfile contains GAMer v%s metadata" % (scene.gamer.gamer_version))
 
     fileVer = scene.gamer.gamer_version
-    isTupleStr = re.compile('\(.*\)')
+    isTupleStr = re.compile("\(.*\)")
     if isTupleStr.match(fileVer):
         fileVer = literal_eval(fileVer)
     else:
-        fileVer = tuple(fileVer.split('.'))
+        fileVer = tuple(fileVer.split("."))
 
     currVer = getGamerVersion()
     scene.gamer.versionerror = compare_version(fileVer, currVer)
 
-    while(scene.gamer.versionerror < 0):
+    while scene.gamer.versionerror < 0:
         if scene.gamer.versionerror == -1:
-
             # Throw an error for older versions of GAMer
             if compare_version(fileVer, (2, 0, 0)) < 0:
+                print("trigger old version")
                 bpy.ops.gamer.prompt_old_version()
                 break
 
-            # Update from 2.0.0 to current
+            # Update from 2.0.0 to 2.0.1
             elif compare_version(fileVer, (2, 0, 0)) == 0:
                 newver = (2, 0, 1)
-                print("Metadata version is out of date.",
-                      "Migrating from v(2,0,0) to v%s" % (str(newver)))
+                print(
+                    "Metadata version is out of date.",
+                    "Migrating from v(2,0,0) to v%s" % (str(newver)),
+                )
                 for obj in bpy.data.objects:
-                    if obj.type == 'MESH':
+                    if obj.type == "MESH":
                         # Migrate name to boundary_name
                         for bdry in obj.gamer.markers.boundary_list:
                             bdry.boundary_name = bdry.name
                             bdry.name = str(bdry.boundary_id)
-                            if 'boundaries' in obj.keys():
-                                del obj['boundaries']
+                            if "boundaries" in obj.keys():
+                                del obj["boundaries"]
+                scene.gamer.gamer_version = str(newver)
+
+            # Update 2.0.1--2.0.5 metadata to 2.0.6
+            elif (
+                compare_version(fileVer, (2, 0, 1)) >= 0
+                and compare_version(fileVer, (2, 0, 6)) < 0
+            ):
+                newver = (2, 0, 6)
+                print("Migrating from v%s to v%s" % (str(fileVer), str(newver)))
+                migrate2_0_1__2_0_6()
                 scene.gamer.gamer_version = str(newver)
 
-            # Update 2.0.1--2.0.4 metadata to 2.0.5
-            elif compare_version(fileVer, (2, 0, 1)) >= 0 and compare_version(fileVer, (2, 0, 5)) < 0:
-                newver = (2, 0, 5)
-                print("Migrating from v%s to v%s" %
-                      (str(fileVer), str(newver)))
-                migrate2_0_1__2_0_5()
+            # Update 2.0.6 to 2.0.7
+            elif (
+                compare_version(fileVer, (2, 0, 6)) >= 0
+                and compare_version(fileVer, (2, 0, 7)) < 0
+            ):
+                newver = (2, 0, 7)
+                print("Migrating from v%s to v%s" % (str(fileVer), str(newver)))
+                migrate2_0_6__2_0_7()
                 scene.gamer.gamer_version = str(newver)
 
-            # No changes since 2.0.5... yet!
-            elif compare_version(fileVer, (2, 0, 5)) >= 0:
-                print("Migrating from v%s to v%s" %
-                      (str(fileVer), str(currVer)))
+            # No changes since 2.0.7... yet!
+            elif compare_version(fileVer, (2, 0, 7)) >= 0:
+                print("Migrating from v%s to v%s" % (str(fileVer), str(currVer)))
                 scene.gamer.gamer_version = str(currVer)
 
         fileVer = literal_eval(scene.gamer.gamer_version)
@@ -212,45 +259,42 @@ def checkVersion():
 
 
 # VERSION UTILITY FUNCTIONS
-def cmp(a, b):
-    """
-    Compare a and b. Returns -1 if b > a, 1 if a > b, or 0 if a == b
-    """
-    return (a > b) - (a < b)
+def compare_version(v1, v2):
+    """Compare version tuples
 
+    Args:
+        v1 (tuple): The version
+        v2 (tuple): The other version
 
-def compare_version(v1, v2):
-    """
-    Compare version tuples
-
-    Parameters
-    ----------
-    v1 : tuple
-    v2 : tuple
-
-    Returns
-    -------
-    int
-        Return 1:  v1 >  v2
-        Return 0:  v1 == v2
-        Return -1: v1 <  v2
+    Returns:
+        int:  Return 1: v1 > v2, Return 0:  v1 == v2, Return -1: v1 <  v2
     """
-    return cmp(*zip(*map(lambda x, y: (x or 0, y or 0),
-                         map(int, v1), map(int, v2))))
+
+    def cmp(a, b):
+        """
+        Compare a and b. Returns -1 if b > a, 1 if a > b, or 0 if a == b
+        """
+        return (a > b) - (a < b)
+
+    return cmp(*zip(*map(lambda x, y: (x or 0, y or 0), map(int, v1), map(int, v2))))
 
 
-classes = [GAMER_OT_prompt_update,
-           GAMER_OT_prompt_old_version,
-           GAMER_OT_update_to_2_0_1_from_v_0_1]
+classes = [
+    GAMER_OT_prompt_update,
+    GAMER_OT_prompt_old_version,
+    GAMER_OT_update_to_2_0_1_from_v_0_1,
+]
 
 
 def register():
     from bpy.utils import register_class
+
     for cls in classes:
         register_class(make_annotations(cls))
 
 
 def unregister():
     from bpy.utils import unregister_class
+
     for cls in reversed(classes):
         unregister_class(make_annotations(cls))
diff --git a/uncrustify.cfg b/uncrustify.cfg
deleted file mode 100644
index e1c0bbeb..00000000
--- a/uncrustify.cfg
+++ /dev/null
@@ -1,2807 +0,0 @@
-# Uncrustify-0.69.0_f
-
-#
-# General options
-#
-
-# The type of line endings.
-#
-# Default: auto
-newlines                        = auto     # lf/crlf/cr/auto
-
-# The original size of tabs in the input.
-#
-# Default: 8
-input_tab_size                  = 4        # unsigned number
-
-# The size of tabs in the output (only used if align_with_tabs=true).
-#
-# Default: 8
-output_tab_size                 = 4        # unsigned number
-
-# The ASCII value of the string escape char, usually 92 (\) or (Pawn) 94 (^).
-#
-# Default: 92
-string_escape_char              = 92       # unsigned number
-
-# Alternate string escape char (usually only used for Pawn).
-# Only works right before the quote char.
-string_escape_char2             = 0        # unsigned number
-
-# Replace tab characters found in string literals with the escape sequence \t
-# instead.
-string_replace_tab_chars        = false    # true/false
-
-# Allow interpreting '>=' and '>>=' as part of a template in code like
-# 'void f(list>=val);'. If true, 'assert(x<0 && y>=3)' will be broken.
-# Improvements to template detection may make this option obsolete.
-tok_split_gte                   = false    # true/false
-
-# Specify the marker used in comments to disable processing of part of the
-# file.
-#
-# Default:  *INDENT-OFF*
-disable_processing_cmt          = " *INDENT-OFF*"      # string
-
-# Specify the marker used in comments to (re)enable processing in a file.
-#
-# Default:  *INDENT-ON*
-enable_processing_cmt           = " *INDENT-ON*"     # string
-
-# Enable parsing of digraphs.
-enable_digraphs                 = false    # true/false
-
-# Add or remove the UTF-8 BOM (recommend 'remove').
-utf8_bom                        = ignore   # ignore/add/remove/force
-
-# If the file contains bytes with values between 128 and 255, but is not
-# UTF-8, then output as UTF-8.
-utf8_byte                       = false    # true/false
-
-# Force the output encoding to UTF-8.
-utf8_force                      = false    # true/false
-
-#
-# Spacing options
-#
-
-# Add or remove space around non-assignment symbolic operators ('+', '/', '%',
-# '<<', and so forth).
-sp_arith                        = ignore   # ignore/add/remove/force
-
-# Add or remove space around arithmetic operators '+' and '-'.
-#
-# Overrides sp_arith.
-sp_arith_additive               = ignore   # ignore/add/remove/force
-
-# Add or remove space around assignment operator '=', '+=', etc.
-sp_assign                       = ignore   # ignore/add/remove/force
-
-# Add or remove space around '=' in C++11 lambda capture specifications.
-#
-# Overrides sp_assign.
-sp_cpp_lambda_assign            = ignore   # ignore/add/remove/force
-
-# Add or remove space after the capture specification in C++11 lambda.
-sp_cpp_lambda_paren             = ignore   # ignore/add/remove/force
-
-# Add or remove space around assignment operator '=' in a prototype.
-#
-# If set to ignore, use sp_assign.
-sp_assign_default               = ignore   # ignore/add/remove/force
-
-# Add or remove space before assignment operator '=', '+=', etc.
-#
-# Overrides sp_assign.
-sp_before_assign                = ignore   # ignore/add/remove/force
-
-# Add or remove space after assignment operator '=', '+=', etc.
-#
-# Overrides sp_assign.
-sp_after_assign                 = ignore   # ignore/add/remove/force
-
-# Add or remove space in 'NS_ENUM ('.
-sp_enum_paren                   = ignore   # ignore/add/remove/force
-
-# Add or remove space around assignment '=' in enum.
-sp_enum_assign                  = ignore   # ignore/add/remove/force
-
-# Add or remove space before assignment '=' in enum.
-#
-# Overrides sp_enum_assign.
-sp_enum_before_assign           = ignore   # ignore/add/remove/force
-
-# Add or remove space after assignment '=' in enum.
-#
-# Overrides sp_enum_assign.
-sp_enum_after_assign            = ignore   # ignore/add/remove/force
-
-# Add or remove space around assignment ':' in enum.
-sp_enum_colon                   = ignore   # ignore/add/remove/force
-
-# Add or remove space around preprocessor '##' concatenation operator.
-#
-# Default: add
-sp_pp_concat                    = add      # ignore/add/remove/force
-
-# Add or remove space after preprocessor '#' stringify operator.
-# Also affects the '#@' charizing operator.
-sp_pp_stringify                 = ignore   # ignore/add/remove/force
-
-# Add or remove space before preprocessor '#' stringify operator
-# as in '#define x(y) L#y'.
-sp_before_pp_stringify          = ignore   # ignore/add/remove/force
-
-# Add or remove space around boolean operators '&&' and '||'.
-sp_bool                         = ignore   # ignore/add/remove/force
-
-# Add or remove space around compare operator '<', '>', '==', etc.
-sp_compare                      = ignore   # ignore/add/remove/force
-
-# Add or remove space inside '(' and ')'.
-sp_inside_paren                 = ignore   # ignore/add/remove/force
-
-# Add or remove space between nested parentheses, i.e. '((' vs. ') )'.
-sp_paren_paren                  = ignore   # ignore/add/remove/force
-
-# Add or remove space between back-to-back parentheses, i.e. ')(' vs. ') ('.
-sp_cparen_oparen                = ignore   # ignore/add/remove/force
-
-# Whether to balance spaces inside nested parentheses.
-sp_balance_nested_parens        = false    # true/false
-
-# Add or remove space between ')' and '{'.
-sp_paren_brace                  = ignore   # ignore/add/remove/force
-
-# Add or remove space between nested braces, i.e. '{{' vs '{ {'.
-sp_brace_brace                  = ignore   # ignore/add/remove/force
-
-# Add or remove space before pointer star '*'.
-sp_before_ptr_star              = ignore   # ignore/add/remove/force
-
-# Add or remove space before pointer star '*' that isn't followed by a
-# variable name. If set to 'ignore', sp_before_ptr_star is used instead.
-sp_before_unnamed_ptr_star      = ignore   # ignore/add/remove/force
-
-# Add or remove space between pointer stars '*'.
-sp_between_ptr_star             = ignore   # ignore/add/remove/force
-
-# Add or remove space after pointer star '*', if followed by a word.
-sp_after_ptr_star               = ignore   # ignore/add/remove/force
-
-# Add or remove space after pointer caret '^', if followed by a word.
-sp_after_ptr_block_caret        = ignore   # ignore/add/remove/force
-
-# Add or remove space after pointer star '*', if followed by a qualifier.
-sp_after_ptr_star_qualifier     = ignore   # ignore/add/remove/force
-
-# Add or remove space after a pointer star '*', if followed by a function
-# prototype or function definition.
-sp_after_ptr_star_func          = ignore   # ignore/add/remove/force
-
-# Add or remove space after a pointer star '*', if followed by an open
-# parenthesis, as in 'void* (*)().
-sp_ptr_star_paren               = ignore   # ignore/add/remove/force
-
-# Add or remove space before a pointer star '*', if followed by a function
-# prototype or function definition.
-sp_before_ptr_star_func         = ignore   # ignore/add/remove/force
-
-# Add or remove space before a reference sign '&'.
-sp_before_byref                 = remove   # ignore/add/remove/force
-
-# Add or remove space before a reference sign '&' that isn't followed by a
-# variable name. If set to 'ignore', sp_before_byref is used instead.
-sp_before_unnamed_byref         = remove   # ignore/add/remove/force
-
-# Add or remove space after reference sign '&', if followed by a word.
-sp_after_byref                  = force    # ignore/add/remove/force
-
-# Add or remove space after a reference sign '&', if followed by a function
-# prototype or function definition.
-sp_after_byref_func             = force    # ignore/add/remove/force
-
-# Add or remove space before a reference sign '&', if followed by a function
-# prototype or function definition.
-sp_before_byref_func            = remove   # ignore/add/remove/force
-
-# Add or remove space between type and word.
-#
-# Default: force
-sp_after_type                   = force    # ignore/add/remove/force
-
-# Add or remove space between 'decltype(...)' and word.
-sp_after_decltype               = ignore   # ignore/add/remove/force
-
-# (D) Add or remove space before the parenthesis in the D constructs
-# 'template Foo(' and 'class Foo('.
-sp_before_template_paren        = ignore   # ignore/add/remove/force
-
-# Add or remove space between 'template' and '<'.
-# If set to ignore, sp_before_angle is used.
-sp_template_angle               = ignore   # ignore/add/remove/force
-
-# Add or remove space before '<'.
-sp_before_angle                 = ignore   # ignore/add/remove/force
-
-# Add or remove space inside '<' and '>'.
-sp_inside_angle                 = ignore   # ignore/add/remove/force
-
-# Add or remove space inside '<>'.
-sp_inside_angle_empty           = remove   # ignore/add/remove/force
-
-# Add or remove space between '>' and ':'.
-sp_angle_colon                  = ignore   # ignore/add/remove/force
-
-# Add or remove space after '<>'.
-sp_after_angle                  = ignore   # ignore/add/remove/force
-
-# Add or remove space between '>' and '(' as found in 'new List(foo);'.
-sp_angle_paren                  = remove   # ignore/add/remove/force
-
-# Add or remove space between '>' and '()' as found in 'new List();'.
-sp_angle_paren_empty            = remove   # ignore/add/remove/force
-
-# Add or remove space between '>' and a word as in 'List m;' or
-# 'template  static ...'.
-sp_angle_word                   = ignore   # ignore/add/remove/force
-
-# Add or remove space between '>' and '>' in '>>' (template stuff).
-#
-# Default: add
-sp_angle_shift                  = remove   # ignore/add/remove/force
-
-# (C++11) Permit removal of the space between '>>' in 'foo >'. Note
-# that sp_angle_shift cannot remove the space without this option.
-sp_permit_cpp11_shift           = true     # true/false
-
-# Add or remove space before '(' of control statements ('if', 'for', 'switch',
-# 'while', etc.).
-sp_before_sparen                = force    # ignore/add/remove/force
-
-# Add or remove space inside '(' and ')' of control statements.
-sp_inside_sparen                = remove   # ignore/add/remove/force
-
-# Add or remove space after '(' of control statements.
-#
-# Overrides sp_inside_sparen.
-sp_inside_sparen_open           = ignore   # ignore/add/remove/force
-
-# Add or remove space before ')' of control statements.
-#
-# Overrides sp_inside_sparen.
-sp_inside_sparen_close          = ignore   # ignore/add/remove/force
-
-# Add or remove space after ')' of control statements.
-sp_after_sparen                 = ignore   # ignore/add/remove/force
-
-# Add or remove space between ')' and '{' of of control statements.
-sp_sparen_brace                 = ignore   # ignore/add/remove/force
-
-# (D) Add or remove space between 'invariant' and '('.
-sp_invariant_paren              = ignore   # ignore/add/remove/force
-
-# (D) Add or remove space after the ')' in 'invariant (C) c'.
-sp_after_invariant_paren        = ignore   # ignore/add/remove/force
-
-# Add or remove space before empty statement ';' on 'if', 'for' and 'while'.
-sp_special_semi                 = ignore   # ignore/add/remove/force
-
-# Add or remove space before ';'.
-#
-# Default: remove
-sp_before_semi                  = remove   # ignore/add/remove/force
-
-# Add or remove space before ';' in non-empty 'for' statements.
-sp_before_semi_for              = ignore   # ignore/add/remove/force
-
-# Add or remove space before a semicolon of an empty part of a for statement.
-sp_before_semi_for_empty        = ignore   # ignore/add/remove/force
-
-# Add or remove space after ';', except when followed by a comment.
-#
-# Default: add
-sp_after_semi                   = add      # ignore/add/remove/force
-
-# Add or remove space after ';' in non-empty 'for' statements.
-#
-# Default: force
-sp_after_semi_for               = force    # ignore/add/remove/force
-
-# Add or remove space after the final semicolon of an empty part of a for
-# statement, as in 'for ( ; ;  )'.
-sp_after_semi_for_empty         = ignore   # ignore/add/remove/force
-
-# Add or remove space before '[' (except '[]').
-sp_before_square                = ignore   # ignore/add/remove/force
-
-# Add or remove space before '[]'.
-sp_before_squares               = ignore   # ignore/add/remove/force
-
-# Add or remove space before C++17 structured bindings.
-sp_cpp_before_struct_binding    = ignore   # ignore/add/remove/force
-
-# Add or remove space inside a non-empty '[' and ']'.
-sp_inside_square                = ignore   # ignore/add/remove/force
-
-# (OC) Add or remove space inside a non-empty Objective-C boxed array '@[' and
-# ']'. If set to ignore, sp_inside_square is used.
-sp_inside_square_oc_array       = ignore   # ignore/add/remove/force
-
-# Add or remove space after ',', i.e. 'a,b' vs. 'a, b'.
-sp_after_comma                  = force    # ignore/add/remove/force
-
-# Add or remove space before ','.
-#
-# Default: remove
-sp_before_comma                 = remove   # ignore/add/remove/force
-
-# (C#) Add or remove space between ',' and ']' in multidimensional array type
-# like 'int[,,]'.
-sp_after_mdatype_commas         = ignore   # ignore/add/remove/force
-
-# (C#) Add or remove space between '[' and ',' in multidimensional array type
-# like 'int[,,]'.
-sp_before_mdatype_commas        = ignore   # ignore/add/remove/force
-
-# (C#) Add or remove space between ',' in multidimensional array type
-# like 'int[,,]'.
-sp_between_mdatype_commas       = ignore   # ignore/add/remove/force
-
-# Add or remove space between an open parenthesis and comma,
-# i.e. '(,' vs. '( ,'.
-#
-# Default: force
-sp_paren_comma                  = force    # ignore/add/remove/force
-
-# Add or remove space before the variadic '...' when preceded by a
-# non-punctuator.
-sp_before_ellipsis              = ignore   # ignore/add/remove/force
-
-# Add or remove space between a type and '...'.
-sp_type_ellipsis                = ignore   # ignore/add/remove/force
-
-# (D) Add or remove space between a type and '?'.
-sp_type_question                = ignore   # ignore/add/remove/force
-
-# Add or remove space between ')' and '...'.
-sp_paren_ellipsis               = ignore   # ignore/add/remove/force
-
-# Add or remove space between ')' and a qualifier such as 'const'.
-sp_paren_qualifier              = ignore   # ignore/add/remove/force
-
-# Add or remove space between ')' and 'noexcept'.
-sp_paren_noexcept               = ignore   # ignore/add/remove/force
-
-# Add or remove space after class ':'.
-sp_after_class_colon            = ignore   # ignore/add/remove/force
-
-# Add or remove space before class ':'.
-sp_before_class_colon           = ignore   # ignore/add/remove/force
-
-# Add or remove space after class constructor ':'.
-sp_after_constr_colon           = ignore   # ignore/add/remove/force
-
-# Add or remove space before class constructor ':'.
-sp_before_constr_colon          = ignore   # ignore/add/remove/force
-
-# Add or remove space before case ':'.
-#
-# Default: remove
-sp_before_case_colon            = remove   # ignore/add/remove/force
-
-# Add or remove space between 'operator' and operator sign.
-sp_after_operator               = ignore   # ignore/add/remove/force
-
-# Add or remove space between the operator symbol and the open parenthesis, as
-# in 'operator ++('.
-sp_after_operator_sym           = ignore   # ignore/add/remove/force
-
-# Overrides sp_after_operator_sym when the operator has no arguments, as in
-# 'operator *()'.
-sp_after_operator_sym_empty     = ignore   # ignore/add/remove/force
-
-# Add or remove space after C/D cast, i.e. 'cast(int)a' vs. 'cast(int) a' or
-# '(int)a' vs. '(int) a'.
-sp_after_cast                   = ignore   # ignore/add/remove/force
-
-# Add or remove spaces inside cast parentheses.
-sp_inside_paren_cast            = ignore   # ignore/add/remove/force
-
-# Add or remove space between the type and open parenthesis in a C++ cast,
-# i.e. 'int(exp)' vs. 'int (exp)'.
-sp_cpp_cast_paren               = ignore   # ignore/add/remove/force
-
-# Add or remove space between 'sizeof' and '('.
-sp_sizeof_paren                 = ignore   # ignore/add/remove/force
-
-# Add or remove space between 'sizeof' and '...'.
-sp_sizeof_ellipsis              = ignore   # ignore/add/remove/force
-
-# Add or remove space between 'sizeof...' and '('.
-sp_sizeof_ellipsis_paren        = ignore   # ignore/add/remove/force
-
-# Add or remove space between 'decltype' and '('.
-sp_decltype_paren               = ignore   # ignore/add/remove/force
-
-# (Pawn) Add or remove space after the tag keyword.
-sp_after_tag                    = ignore   # ignore/add/remove/force
-
-# Add or remove space inside enum '{' and '}'.
-sp_inside_braces_enum           = ignore   # ignore/add/remove/force
-
-# Add or remove space inside struct/union '{' and '}'.
-sp_inside_braces_struct         = ignore   # ignore/add/remove/force
-
-# (OC) Add or remove space inside Objective-C boxed dictionary '{' and '}'
-sp_inside_braces_oc_dict        = ignore   # ignore/add/remove/force
-
-# Add or remove space after open brace in an unnamed temporary
-# direct-list-initialization.
-sp_after_type_brace_init_lst_open = ignore   # ignore/add/remove/force
-
-# Add or remove space before close brace in an unnamed temporary
-# direct-list-initialization.
-sp_before_type_brace_init_lst_close = ignore   # ignore/add/remove/force
-
-# Add or remove space inside an unnamed temporary direct-list-initialization.
-sp_inside_type_brace_init_lst   = ignore   # ignore/add/remove/force
-
-# Add or remove space inside '{' and '}'.
-sp_inside_braces                = ignore   # ignore/add/remove/force
-
-# Add or remove space inside '{}'.
-sp_inside_braces_empty          = ignore   # ignore/add/remove/force
-
-# Add or remove space between return type and function name. A minimum of 1
-# is forced except for pointer return types.
-sp_type_func                    = ignore   # ignore/add/remove/force
-
-# Add or remove space between type and open brace of an unnamed temporary
-# direct-list-initialization.
-sp_type_brace_init_lst          = ignore   # ignore/add/remove/force
-
-# Add or remove space between function name and '(' on function declaration.
-sp_func_proto_paren             = ignore   # ignore/add/remove/force
-
-# Add or remove space between function name and '()' on function declaration
-# without parameters.
-sp_func_proto_paren_empty       = ignore   # ignore/add/remove/force
-
-# Add or remove space between function name and '(' on function definition.
-sp_func_def_paren               = ignore   # ignore/add/remove/force
-
-# Add or remove space between function name and '()' on function definition
-# without parameters.
-sp_func_def_paren_empty         = ignore   # ignore/add/remove/force
-
-# Add or remove space inside empty function '()'.
-sp_inside_fparens               = ignore   # ignore/add/remove/force
-
-# Add or remove space inside function '(' and ')'.
-sp_inside_fparen                = ignore   # ignore/add/remove/force
-
-# Add or remove space inside the first parentheses in a function type, as in
-# 'void (*x)(...)'.
-sp_inside_tparen                = ignore   # ignore/add/remove/force
-
-# Add or remove space between the ')' and '(' in a function type, as in
-# 'void (*x)(...)'.
-sp_after_tparen_close           = ignore   # ignore/add/remove/force
-
-# Add or remove space between ']' and '(' when part of a function call.
-sp_square_fparen                = ignore   # ignore/add/remove/force
-
-# Add or remove space between ')' and '{' of function.
-sp_fparen_brace                 = ignore   # ignore/add/remove/force
-
-# Add or remove space between ')' and '{' of s function call in object
-# initialization.
-#
-# Overrides sp_fparen_brace.
-sp_fparen_brace_initializer     = ignore   # ignore/add/remove/force
-
-# (Java) Add or remove space between ')' and '{{' of double brace initializer.
-sp_fparen_dbrace                = ignore   # ignore/add/remove/force
-
-# Add or remove space between function name and '(' on function calls.
-sp_func_call_paren              = ignore   # ignore/add/remove/force
-
-# Add or remove space between function name and '()' on function calls without
-# parameters. If set to 'ignore' (the default), sp_func_call_paren is used.
-sp_func_call_paren_empty        = ignore   # ignore/add/remove/force
-
-# Add or remove space between the user function name and '(' on function
-# calls. You need to set a keyword to be a user function in the config file,
-# like:
-#   set func_call_user tr _ i18n
-sp_func_call_user_paren         = ignore   # ignore/add/remove/force
-
-# Add or remove space inside user function '(' and ')'.
-sp_func_call_user_inside_fparen = ignore   # ignore/add/remove/force
-
-# Add or remove space between nested parentheses with user functions,
-# i.e. '((' vs. '( ('.
-sp_func_call_user_paren_paren   = ignore   # ignore/add/remove/force
-
-# Add or remove space between a constructor/destructor and the open
-# parenthesis.
-sp_func_class_paren             = ignore   # ignore/add/remove/force
-
-# Add or remove space between a constructor without parameters or destructor
-# and '()'.
-sp_func_class_paren_empty       = ignore   # ignore/add/remove/force
-
-# Add or remove space between 'return' and '('.
-sp_return_paren                 = ignore   # ignore/add/remove/force
-
-# Add or remove space between 'return' and '{'.
-sp_return_brace                 = ignore   # ignore/add/remove/force
-
-# Add or remove space between '__attribute__' and '('.
-sp_attribute_paren              = ignore   # ignore/add/remove/force
-
-# Add or remove space between 'defined' and '(' in '#if defined (FOO)'.
-sp_defined_paren                = ignore   # ignore/add/remove/force
-
-# Add or remove space between 'throw' and '(' in 'throw (something)'.
-sp_throw_paren                  = ignore   # ignore/add/remove/force
-
-# Add or remove space between 'throw' and anything other than '(' as in
-# '@throw [...];'.
-sp_after_throw                  = ignore   # ignore/add/remove/force
-
-# Add or remove space between 'catch' and '(' in 'catch (something) { }'.
-# If set to ignore, sp_before_sparen is used.
-sp_catch_paren                  = ignore   # ignore/add/remove/force
-
-# (OC) Add or remove space between '@catch' and '('
-# in '@catch (something) { }'. If set to ignore, sp_catch_paren is used.
-sp_oc_catch_paren               = ignore   # ignore/add/remove/force
-
-# (OC) Add or remove space between class name and '('
-# in '@interface className(categoryName):BaseClass'
-sp_oc_classname_paren           = ignore   # ignore/add/remove/force
-
-# (D) Add or remove space between 'version' and '('
-# in 'version (something) { }'. If set to ignore, sp_before_sparen is used.
-sp_version_paren                = ignore   # ignore/add/remove/force
-
-# (D) Add or remove space between 'scope' and '('
-# in 'scope (something) { }'. If set to ignore, sp_before_sparen is used.
-sp_scope_paren                  = ignore   # ignore/add/remove/force
-
-# Add or remove space between 'super' and '(' in 'super (something)'.
-#
-# Default: remove
-sp_super_paren                  = remove   # ignore/add/remove/force
-
-# Add or remove space between 'this' and '(' in 'this (something)'.
-#
-# Default: remove
-sp_this_paren                   = remove   # ignore/add/remove/force
-
-# Add or remove space between a macro name and its definition.
-sp_macro                        = ignore   # ignore/add/remove/force
-
-# Add or remove space between a macro function ')' and its definition.
-sp_macro_func                   = ignore   # ignore/add/remove/force
-
-# Add or remove space between 'else' and '{' if on the same line.
-sp_else_brace                   = ignore   # ignore/add/remove/force
-
-# Add or remove space between '}' and 'else' if on the same line.
-sp_brace_else                   = ignore   # ignore/add/remove/force
-
-# Add or remove space between '}' and the name of a typedef on the same line.
-sp_brace_typedef                = ignore   # ignore/add/remove/force
-
-# Add or remove space before the '{' of a 'catch' statement, if the '{' and
-# 'catch' are on the same line, as in 'catch (decl)  {'.
-sp_catch_brace                  = ignore   # ignore/add/remove/force
-
-# (OC) Add or remove space before the '{' of a '@catch' statement, if the '{'
-# and '@catch' are on the same line, as in '@catch (decl)  {'.
-# If set to ignore, sp_catch_brace is used.
-sp_oc_catch_brace               = ignore   # ignore/add/remove/force
-
-# Add or remove space between '}' and 'catch' if on the same line.
-sp_brace_catch                  = ignore   # ignore/add/remove/force
-
-# (OC) Add or remove space between '}' and '@catch' if on the same line.
-# If set to ignore, sp_brace_catch is used.
-sp_oc_brace_catch               = ignore   # ignore/add/remove/force
-
-# Add or remove space between 'finally' and '{' if on the same line.
-sp_finally_brace                = ignore   # ignore/add/remove/force
-
-# Add or remove space between '}' and 'finally' if on the same line.
-sp_brace_finally                = ignore   # ignore/add/remove/force
-
-# Add or remove space between 'try' and '{' if on the same line.
-sp_try_brace                    = ignore   # ignore/add/remove/force
-
-# Add or remove space between get/set and '{' if on the same line.
-sp_getset_brace                 = ignore   # ignore/add/remove/force
-
-# Add or remove space between a variable and '{' for C++ uniform
-# initialization.
-#
-# Default: add
-sp_word_brace                   = add      # ignore/add/remove/force
-
-# Add or remove space between a variable and '{' for a namespace.
-#
-# Default: add
-sp_word_brace_ns                = add      # ignore/add/remove/force
-
-# Add or remove space before the '::' operator.
-sp_before_dc                    = ignore   # ignore/add/remove/force
-
-# Add or remove space after the '::' operator.
-sp_after_dc                     = ignore   # ignore/add/remove/force
-
-# (D) Add or remove around the D named array initializer ':' operator.
-sp_d_array_colon                = ignore   # ignore/add/remove/force
-
-# Add or remove space after the '!' (not) unary operator.
-#
-# Default: remove
-sp_not                          = remove   # ignore/add/remove/force
-
-# Add or remove space after the '~' (invert) unary operator.
-#
-# Default: remove
-sp_inv                          = remove   # ignore/add/remove/force
-
-# Add or remove space after the '&' (address-of) unary operator. This does not
-# affect the spacing after a '&' that is part of a type.
-#
-# Default: remove
-sp_addr                         = remove   # ignore/add/remove/force
-
-# Add or remove space around the '.' or '->' operators.
-#
-# Default: remove
-sp_member                       = remove   # ignore/add/remove/force
-
-# Add or remove space after the '*' (dereference) unary operator. This does
-# not affect the spacing after a '*' that is part of a type.
-#
-# Default: remove
-sp_deref                        = remove   # ignore/add/remove/force
-
-# Add or remove space after '+' or '-', as in 'x = -5' or 'y = +7'.
-#
-# Default: remove
-sp_sign                         = remove   # ignore/add/remove/force
-
-# Add or remove space between '++' and '--' the word to which it is being
-# applied, as in '(--x)' or 'y++;'.
-#
-# Default: remove
-sp_incdec                       = remove   # ignore/add/remove/force
-
-# Add or remove space before a backslash-newline at the end of a line.
-#
-# Default: add
-sp_before_nl_cont               = add      # ignore/add/remove/force
-
-# (OC) Add or remove space after the scope '+' or '-', as in '-(void) foo;'
-# or '+(int) bar;'.
-sp_after_oc_scope               = ignore   # ignore/add/remove/force
-
-# (OC) Add or remove space after the colon in message specs,
-# i.e. '-(int) f:(int) x;' vs. '-(int) f: (int) x;'.
-sp_after_oc_colon               = ignore   # ignore/add/remove/force
-
-# (OC) Add or remove space before the colon in message specs,
-# i.e. '-(int) f: (int) x;' vs. '-(int) f : (int) x;'.
-sp_before_oc_colon              = ignore   # ignore/add/remove/force
-
-# (OC) Add or remove space after the colon in immutable dictionary expression
-# 'NSDictionary *test = @{@"foo" :@"bar"};'.
-sp_after_oc_dict_colon          = ignore   # ignore/add/remove/force
-
-# (OC) Add or remove space before the colon in immutable dictionary expression
-# 'NSDictionary *test = @{@"foo" :@"bar"};'.
-sp_before_oc_dict_colon         = ignore   # ignore/add/remove/force
-
-# (OC) Add or remove space after the colon in message specs,
-# i.e. '[object setValue:1];' vs. '[object setValue: 1];'.
-sp_after_send_oc_colon          = ignore   # ignore/add/remove/force
-
-# (OC) Add or remove space before the colon in message specs,
-# i.e. '[object setValue:1];' vs. '[object setValue :1];'.
-sp_before_send_oc_colon         = ignore   # ignore/add/remove/force
-
-# (OC) Add or remove space after the (type) in message specs,
-# i.e. '-(int)f: (int) x;' vs. '-(int)f: (int)x;'.
-sp_after_oc_type                = ignore   # ignore/add/remove/force
-
-# (OC) Add or remove space after the first (type) in message specs,
-# i.e. '-(int) f:(int)x;' vs. '-(int)f:(int)x;'.
-sp_after_oc_return_type         = ignore   # ignore/add/remove/force
-
-# (OC) Add or remove space between '@selector' and '(',
-# i.e. '@selector(msgName)' vs. '@selector (msgName)'.
-# Also applies to '@protocol()' constructs.
-sp_after_oc_at_sel              = ignore   # ignore/add/remove/force
-
-# (OC) Add or remove space between '@selector(x)' and the following word,
-# i.e. '@selector(foo) a:' vs. '@selector(foo)a:'.
-sp_after_oc_at_sel_parens       = ignore   # ignore/add/remove/force
-
-# (OC) Add or remove space inside '@selector' parentheses,
-# i.e. '@selector(foo)' vs. '@selector( foo )'.
-# Also applies to '@protocol()' constructs.
-sp_inside_oc_at_sel_parens      = ignore   # ignore/add/remove/force
-
-# (OC) Add or remove space before a block pointer caret,
-# i.e. '^int (int arg){...}' vs. ' ^int (int arg){...}'.
-sp_before_oc_block_caret        = ignore   # ignore/add/remove/force
-
-# (OC) Add or remove space after a block pointer caret,
-# i.e. '^int (int arg){...}' vs. '^ int (int arg){...}'.
-sp_after_oc_block_caret         = ignore   # ignore/add/remove/force
-
-# (OC) Add or remove space between the receiver and selector in a message,
-# as in '[receiver selector ...]'.
-sp_after_oc_msg_receiver        = ignore   # ignore/add/remove/force
-
-# (OC) Add or remove space after '@property'.
-sp_after_oc_property            = ignore   # ignore/add/remove/force
-
-# (OC) Add or remove space between '@synchronized' and the open parenthesis,
-# i.e. '@synchronized(foo)' vs. '@synchronized (foo)'.
-sp_after_oc_synchronized        = ignore   # ignore/add/remove/force
-
-# Add or remove space around the ':' in 'b ? t : f'.
-sp_cond_colon                   = ignore   # ignore/add/remove/force
-
-# Add or remove space before the ':' in 'b ? t : f'.
-#
-# Overrides sp_cond_colon.
-sp_cond_colon_before            = ignore   # ignore/add/remove/force
-
-# Add or remove space after the ':' in 'b ? t : f'.
-#
-# Overrides sp_cond_colon.
-sp_cond_colon_after             = ignore   # ignore/add/remove/force
-
-# Add or remove space around the '?' in 'b ? t : f'.
-sp_cond_question                = ignore   # ignore/add/remove/force
-
-# Add or remove space before the '?' in 'b ? t : f'.
-#
-# Overrides sp_cond_question.
-sp_cond_question_before         = ignore   # ignore/add/remove/force
-
-# Add or remove space after the '?' in 'b ? t : f'.
-#
-# Overrides sp_cond_question.
-sp_cond_question_after          = ignore   # ignore/add/remove/force
-
-# In the abbreviated ternary form '(a ?: b)', add or remove space between '?'
-# and ':'.
-#
-# Overrides all other sp_cond_* options.
-sp_cond_ternary_short           = ignore   # ignore/add/remove/force
-
-# Fix the spacing between 'case' and the label. Only 'ignore' and 'force' make
-# sense here.
-sp_case_label                   = ignore   # ignore/add/remove/force
-
-# (D) Add or remove space around the D '..' operator.
-sp_range                        = ignore   # ignore/add/remove/force
-
-# Add or remove space after ':' in a Java/C++11 range-based 'for',
-# as in 'for (Type var : expr)'.
-sp_after_for_colon              = ignore   # ignore/add/remove/force
-
-# Add or remove space before ':' in a Java/C++11 range-based 'for',
-# as in 'for (Type var : expr)'.
-sp_before_for_colon             = ignore   # ignore/add/remove/force
-
-# (D) Add or remove space between 'extern' and '(' as in 'extern (C)'.
-sp_extern_paren                 = ignore   # ignore/add/remove/force
-
-# Add or remove space after the opening of a C++ comment,
-# i.e. '// A' vs. '//A'.
-sp_cmt_cpp_start                = ignore   # ignore/add/remove/force
-
-# If true, space is added with sp_cmt_cpp_start will be added after doxygen
-# sequences like '///', '///<', '//!' and '//!<'.
-sp_cmt_cpp_doxygen              = false    # true/false
-
-# If true, space is added with sp_cmt_cpp_start will be added after Qt
-# translator or meta-data comments like '//:', '//=', and '//~'.
-sp_cmt_cpp_qttr                 = false    # true/false
-
-# Add or remove space between #else or #endif and a trailing comment.
-sp_endif_cmt                    = ignore   # ignore/add/remove/force
-
-# Add or remove space after 'new', 'delete' and 'delete[]'.
-sp_after_new                    = ignore   # ignore/add/remove/force
-
-# Add or remove space between 'new' and '(' in 'new()'.
-sp_between_new_paren            = ignore   # ignore/add/remove/force
-
-# Add or remove space between ')' and type in 'new(foo) BAR'.
-sp_after_newop_paren            = ignore   # ignore/add/remove/force
-
-# Add or remove space inside parenthesis of the new operator
-# as in 'new(foo) BAR'.
-sp_inside_newop_paren           = ignore   # ignore/add/remove/force
-
-# Add or remove space after the open parenthesis of the new operator,
-# as in 'new(foo) BAR'.
-#
-# Overrides sp_inside_newop_paren.
-sp_inside_newop_paren_open      = ignore   # ignore/add/remove/force
-
-# Add or remove space before the close parenthesis of the new operator,
-# as in 'new(foo) BAR'.
-#
-# Overrides sp_inside_newop_paren.
-sp_inside_newop_paren_close     = ignore   # ignore/add/remove/force
-
-# Add or remove space before a trailing or embedded comment.
-sp_before_tr_emb_cmt            = ignore   # ignore/add/remove/force
-
-# Number of spaces before a trailing or embedded comment.
-sp_num_before_tr_emb_cmt        = 0        # unsigned number
-
-# (Java) Add or remove space between an annotation and the open parenthesis.
-sp_annotation_paren             = ignore   # ignore/add/remove/force
-
-# If true, vbrace tokens are dropped to the previous token and skipped.
-sp_skip_vbrace_tokens           = false    # true/false
-
-# Add or remove space after 'noexcept'.
-sp_after_noexcept               = ignore   # ignore/add/remove/force
-
-# Add or remove space after '_'.
-sp_vala_after_translation       = ignore   # ignore/add/remove/force
-
-# If true, a  is inserted after #define.
-force_tab_after_define          = false    # true/false
-
-#
-# Indenting options
-#
-
-# The number of columns to indent per level. Usually 2, 3, 4, or 8.
-#
-# Default: 8
-indent_columns                  = 4        # unsigned number
-
-# The continuation indent. If non-zero, this overrides the indent of '(', '['
-# and '=' continuation indents. Negative values are OK; negative value is
-# absolute and not increased for each '(' or '[' level.
-#
-# For FreeBSD, this is set to 4.
-indent_continue                 = 0        # number
-
-# The continuation indent, only for class header line(s). If non-zero, this
-# overrides the indent of 'class' continuation indents.
-indent_continue_class_head      = 0        # unsigned number
-
-# Whether to indent empty lines (i.e. lines which contain only spaces before
-# the newline character).
-indent_single_newlines          = false    # true/false
-
-# The continuation indent for func_*_param if they are true. If non-zero, this
-# overrides the indent.
-indent_param                    = 0        # unsigned number
-
-# How to use tabs when indenting code.
-#
-# 0: Spaces only
-# 1: Indent with tabs to brace level, align with spaces (default)
-# 2: Indent and align with tabs, using spaces when not on a tabstop
-#
-# Default: 1
-indent_with_tabs                = 0        # unsigned number
-
-# Whether to indent comments that are not at a brace level with tabs on a
-# tabstop. Requires indent_with_tabs=2. If false, will use spaces.
-indent_cmt_with_tabs            = false    # true/false
-
-# Whether to indent strings broken by '\' so that they line up.
-indent_align_string             = false    # true/false
-
-# The number of spaces to indent multi-line XML strings.
-# Requires indent_align_string=true.
-indent_xml_string               = 0        # unsigned number
-
-# Spaces to indent '{' from level.
-indent_brace                    = 0        # unsigned number
-
-# Whether braces are indented to the body level.
-indent_braces                   = false    # true/false
-
-# Whether to disable indenting function braces if indent_braces=true.
-indent_braces_no_func           = false    # true/false
-
-# Whether to disable indenting class braces if indent_braces=true.
-indent_braces_no_class          = false    # true/false
-
-# Whether to disable indenting struct braces if indent_braces=true.
-indent_braces_no_struct         = false    # true/false
-
-# Whether to indent based on the size of the brace parent,
-# i.e. 'if' => 3 spaces, 'for' => 4 spaces, etc.
-indent_brace_parent             = false    # true/false
-
-# Whether to indent based on the open parenthesis instead of the open brace
-# in '({\n'.
-indent_paren_open_brace         = false    # true/false
-
-# (C#) Whether to indent the brace of a C# delegate by another level.
-indent_cs_delegate_brace        = false    # true/false
-
-# (C#) Whether to indent a C# delegate (to handle delegates with no brace) by
-# another level.
-indent_cs_delegate_body         = false    # true/false
-
-# Whether to indent the body of a 'namespace'.
-indent_namespace                = false    # true/false
-
-# Whether to indent only the first namespace, and not any nested namespaces.
-# Requires indent_namespace=true.
-indent_namespace_single_indent  = false    # true/false
-
-# The number of spaces to indent a namespace block.
-# If set to zero, use the value indent_columns
-indent_namespace_level          = 0        # unsigned number
-
-# If the body of the namespace is longer than this number, it won't be
-# indented. Requires indent_namespace=true. 0 means no limit.
-indent_namespace_limit          = 0        # unsigned number
-
-# Whether the 'extern "C"' body is indented.
-indent_extern                   = false    # true/false
-
-# Whether the 'class' body is indented.
-indent_class                    = true    # true/false
-
-# Whether to indent the stuff after a leading base class colon.
-indent_class_colon              = false    # true/false
-
-# Whether to indent based on a class colon instead of the stuff after the
-# colon. Requires indent_class_colon=true.
-indent_class_on_colon           = false    # true/false
-
-# Whether to indent the stuff after a leading class initializer colon.
-indent_constr_colon             = false    # true/false
-
-# Virtual indent from the ':' for member initializers.
-#
-# Default: 2
-indent_ctor_init_leading        = 2        # unsigned number
-
-# Additional indent for constructor initializer list.
-# Negative values decrease indent down to the first column.
-indent_ctor_init                = 0        # number
-
-# Whether to indent 'if' following 'else' as a new block under the 'else'.
-# If false, 'else\nif' is treated as 'else if' for indenting purposes.
-indent_else_if                  = false    # true/false
-
-# Amount to indent variable declarations after a open brace.
-#
-#  <0: Relative
-# >=0: Absolute
-indent_var_def_blk              = 0        # number
-
-# Whether to indent continued variable declarations instead of aligning.
-indent_var_def_cont             = false    # true/false
-
-# Whether to indent continued shift expressions ('<<' and '>>') instead of
-# aligning. Set align_left_shift=false when enabling this.
-indent_shift                    = false    # true/false
-
-# Whether to force indentation of function definitions to start in column 1.
-indent_func_def_force_col1      = false    # true/false
-
-# Whether to indent continued function call parameters one indent level,
-# rather than aligning parameters under the open parenthesis.
-indent_func_call_param          = false    # true/false
-
-# Same as indent_func_call_param, but for function definitions.
-indent_func_def_param           = false    # true/false
-
-# Same as indent_func_call_param, but for function prototypes.
-indent_func_proto_param         = false    # true/false
-
-# Same as indent_func_call_param, but for class declarations.
-indent_func_class_param         = false    # true/false
-
-# Same as indent_func_call_param, but for class variable constructors.
-indent_func_ctor_var_param      = false    # true/false
-
-# Same as indent_func_call_param, but for template parameter lists.
-indent_template_param           = false    # true/false
-
-# Double the indent for indent_func_xxx_param options.
-# Use both values of the options indent_columns and indent_param.
-indent_func_param_double        = false    # true/false
-
-# Indentation column for standalone 'const' qualifier on a function
-# prototype.
-indent_func_const               = 0        # unsigned number
-
-# Indentation column for standalone 'throw' qualifier on a function
-# prototype.
-indent_func_throw               = 0        # unsigned number
-
-# The number of spaces to indent a continued '->' or '.'.
-# Usually set to 0, 1, or indent_columns.
-indent_member                   = 0        # unsigned number
-
-# Whether lines broken at '.' or '->' should be indented by a single indent.
-# The indent_member option will not be effective if this is set to true.
-indent_member_single            = false    # true/false
-
-# Spaces to indent single line ('//') comments on lines before code.
-indent_sing_line_comments       = 0        # unsigned number
-
-# Whether to indent trailing single line ('//') comments relative to the code
-# instead of trying to keep the same absolute column.
-indent_relative_single_line_comments = false    # true/false
-
-# Spaces to indent 'case' from 'switch'. Usually 0 or indent_columns.
-indent_switch_case              = 0        # unsigned number
-
-# Whether to indent preprocessor statements inside of switch statements.
-#
-# Default: true
-indent_switch_pp                = true     # true/false
-
-# Spaces to shift the 'case' line, without affecting any other lines.
-# Usually 0.
-indent_case_shift               = 0        # unsigned number
-
-# Spaces to indent '{' from 'case'. By default, the brace will appear under
-# the 'c' in case. Usually set to 0 or indent_columns. Negative values are OK.
-indent_case_brace               = 0        # number
-
-# Whether to indent comments found in first column.
-indent_col1_comment             = true     # true/false
-
-# Whether to indent multi string literal in first column.
-indent_col1_multi_string_literal = false    # true/false
-
-# How to indent goto labels.
-#
-#  >0: Absolute column where 1 is the leftmost column
-# <=0: Subtract from brace indent
-#
-# Default: 1
-indent_label                    = 1        # number
-
-# Same as indent_label, but for access specifiers that are followed by a
-# colon.
-#
-# Default: 1
-indent_access_spec              = -2       # number
-
-# Whether to indent the code after an access specifier by one level.
-# If true, this option forces 'indent_access_spec=0'.
-indent_access_spec_body         = false    # true/false
-
-# If an open parenthesis is followed by a newline, whether to indent the next
-# line so that it lines up after the open parenthesis (not recommended).
-indent_paren_nl                 = false    # true/false
-
-# How to indent a close parenthesis after a newline.
-#
-# 0: Indent to body level (default)
-# 1: Align under the open parenthesis
-# 2: Indent to the brace level
-indent_paren_close              = 0        # unsigned number
-
-# Whether to indent the open parenthesis of a function definition,
-# if the parenthesis is on its own line.
-indent_paren_after_func_def     = false    # true/false
-
-# Whether to indent the open parenthesis of a function declaration,
-# if the parenthesis is on its own line.
-indent_paren_after_func_decl    = false    # true/false
-
-# Whether to indent the open parenthesis of a function call,
-# if the parenthesis is on its own line.
-indent_paren_after_func_call    = false    # true/false
-
-# Whether to indent a comma when inside a parenthesis.
-# If true, aligns under the open parenthesis.
-indent_comma_paren              = false    # true/false
-
-# Whether to indent a Boolean operator when inside a parenthesis.
-# If true, aligns under the open parenthesis.
-indent_bool_paren               = false    # true/false
-
-# Whether to indent a semicolon when inside a for parenthesis.
-# If true, aligns under the open for parenthesis.
-indent_semicolon_for_paren      = false    # true/false
-
-# Whether to align the first expression to following ones
-# if indent_bool_paren=true.
-indent_first_bool_expr          = false    # true/false
-
-# Whether to align the first expression to following ones
-# if indent_semicolon_for_paren=true.
-indent_first_for_expr           = false    # true/false
-
-# If an open square is followed by a newline, whether to indent the next line
-# so that it lines up after the open square (not recommended).
-indent_square_nl                = false    # true/false
-
-# (ESQL/C) Whether to preserve the relative indent of 'EXEC SQL' bodies.
-indent_preserve_sql             = false    # true/false
-
-# Whether to align continued statements at the '='. If false or if the '=' is
-# followed by a newline, the next line is indent one tab.
-#
-# Default: true
-indent_align_assign             = true     # true/false
-
-# Whether to align continued statements at the '('. If false or the '(' is
-# followed by a newline, the next line indent is one tab.
-#
-# Default: true
-indent_align_paren              = true     # true/false
-
-# (OC) Whether to indent Objective-C blocks at brace level instead of usual
-# rules.
-indent_oc_block                 = false    # true/false
-
-# (OC) Indent for Objective-C blocks in a message relative to the parameter
-# name.
-#
-# =0: Use indent_oc_block rules
-# >0: Use specified number of spaces to indent
-indent_oc_block_msg             = 0        # unsigned number
-
-# (OC) Minimum indent for subsequent parameters
-indent_oc_msg_colon             = 0        # unsigned number
-
-# (OC) Whether to prioritize aligning with initial colon (and stripping spaces
-# from lines, if necessary).
-#
-# Default: true
-indent_oc_msg_prioritize_first_colon = true     # true/false
-
-# (OC) Whether to indent blocks the way that Xcode does by default
-# (from the keyword if the parameter is on its own line; otherwise, from the
-# previous indentation level). Requires indent_oc_block_msg=true.
-indent_oc_block_msg_xcode_style = false    # true/false
-
-# (OC) Whether to indent blocks from where the brace is, relative to a
-# message keyword. Requires indent_oc_block_msg=true.
-indent_oc_block_msg_from_keyword = false    # true/false
-
-# (OC) Whether to indent blocks from where the brace is, relative to a message
-# colon. Requires indent_oc_block_msg=true.
-indent_oc_block_msg_from_colon  = false    # true/false
-
-# (OC) Whether to indent blocks from where the block caret is.
-# Requires indent_oc_block_msg=true.
-indent_oc_block_msg_from_caret  = false    # true/false
-
-# (OC) Whether to indent blocks from where the brace caret is.
-# Requires indent_oc_block_msg=true.
-indent_oc_block_msg_from_brace  = false    # true/false
-
-# When indenting after virtual brace open and newline add further spaces to
-# reach this minimum indent.
-indent_min_vbrace_open          = 0        # unsigned number
-
-# Whether to add further spaces after regular indent to reach next tabstop
-# when identing after virtual brace open and newline.
-indent_vbrace_open_on_tabstop   = false    # true/false
-
-# How to indent after a brace followed by another token (not a newline).
-# true:  indent all contained lines to match the token
-# false: indent all contained lines to match the brace
-#
-# Default: true
-indent_token_after_brace        = true     # true/false
-
-# Whether to indent the body of a C++11 lambda.
-indent_cpp_lambda_body          = false    # true/false
-
-# (C#) Whether to indent a 'using' block if no braces are used.
-#
-# Default: true
-indent_using_block              = true     # true/false
-
-# How to indent the continuation of ternary operator.
-#
-# 0: Off (default)
-# 1: When the `if_false` is a continuation, indent it under `if_false`
-# 2: When the `:` is a continuation, indent it under `?`
-indent_ternary_operator         = 0        # unsigned number
-
-# If true, the indentation of the chunks after a `return new` sequence will be set at return indentation column.
-indent_off_after_return_new     = false    # true/false
-
-# If true, the tokens after return are indented with regular single indentation. By default (false) the indentation is after the return token.
-indent_single_after_return      = false    # true/false
-
-# Whether to ignore indent and alignment for 'asm' blocks (i.e. assume they
-# have their own indentation).
-indent_ignore_asm_block         = false    # true/false
-
-#
-# Newline adding and removing options
-#
-
-# Whether to collapse empty blocks between '{' and '}'.
-nl_collapse_empty_body          = false    # true/false
-
-# Don't split one-line braced assignments, as in 'foo_t f = { 1, 2 };'.
-nl_assign_leave_one_liners      = false    # true/false
-
-# Don't split one-line braced statements inside a 'class xx { }' body.
-nl_class_leave_one_liners       = false    # true/false
-
-# Don't split one-line enums, as in 'enum foo { BAR = 15 };'
-nl_enum_leave_one_liners        = false    # true/false
-
-# Don't split one-line get or set functions.
-nl_getset_leave_one_liners      = false    # true/false
-
-# (C#) Don't split one-line property get or set functions.
-nl_cs_property_leave_one_liners = false    # true/false
-
-# Don't split one-line function definitions, as in 'int foo() { return 0; }'.
-nl_func_leave_one_liners        = false    # true/false
-
-# Don't split one-line C++11 lambdas, as in '[]() { return 0; }'.
-nl_cpp_lambda_leave_one_liners  = false    # true/false
-
-# Don't split one-line if/else statements, as in 'if(...) b++;'.
-nl_if_leave_one_liners          = false    # true/false
-
-# Don't split one-line while statements, as in 'while(...) b++;'.
-nl_while_leave_one_liners       = false    # true/false
-
-# Don't split one-line for statements, as in 'for(...) b++;'.
-nl_for_leave_one_liners         = false    # true/false
-
-# (OC) Don't split one-line Objective-C messages.
-nl_oc_msg_leave_one_liner       = false    # true/false
-
-# (OC) Add or remove newline between method declaration and '{'.
-nl_oc_mdef_brace                = ignore   # ignore/add/remove/force
-
-# (OC) Add or remove newline between Objective-C block signature and '{'.
-nl_oc_block_brace               = ignore   # ignore/add/remove/force
-
-# (OC) Add or remove newline between '@interface' and '{'.
-nl_oc_interface_brace           = ignore   # ignore/add/remove/force
-
-# (OC) Add or remove newline between '@implementation' and '{'.
-nl_oc_implementation_brace      = ignore   # ignore/add/remove/force
-
-# Add or remove newlines at the start of the file.
-nl_start_of_file                = ignore   # ignore/add/remove/force
-
-# The minimum number of newlines at the start of the file (only used if
-# nl_start_of_file is 'add' or 'force').
-nl_start_of_file_min            = 0        # unsigned number
-
-# Add or remove newline at the end of the file.
-nl_end_of_file                  = ignore   # ignore/add/remove/force
-
-# The minimum number of newlines at the end of the file (only used if
-# nl_end_of_file is 'add' or 'force').
-nl_end_of_file_min              = 0        # unsigned number
-
-# Add or remove newline between '=' and '{'.
-nl_assign_brace                 = ignore   # ignore/add/remove/force
-
-# (D) Add or remove newline between '=' and '['.
-nl_assign_square                = ignore   # ignore/add/remove/force
-
-# Add or remove newline between '[]' and '{'.
-nl_tsquare_brace                = ignore   # ignore/add/remove/force
-
-# (D) Add or remove newline after '= ['. Will also affect the newline before
-# the ']'.
-nl_after_square_assign          = ignore   # ignore/add/remove/force
-
-# Add or remove newline between a function call's ')' and '{', as in
-# 'list_for_each(item, &list) { }'.
-nl_fcall_brace                  = ignore   # ignore/add/remove/force
-
-# Add or remove newline between 'enum' and '{'.
-nl_enum_brace                   = ignore   # ignore/add/remove/force
-
-# Add or remove newline between 'enum' and 'class'.
-nl_enum_class                   = ignore   # ignore/add/remove/force
-
-# Add or remove newline between 'enum class' and the identifier.
-nl_enum_class_identifier        = ignore   # ignore/add/remove/force
-
-# Add or remove newline between 'enum class' type and ':'.
-nl_enum_identifier_colon        = ignore   # ignore/add/remove/force
-
-# Add or remove newline between 'enum class identifier :' and type.
-nl_enum_colon_type              = ignore   # ignore/add/remove/force
-
-# Add or remove newline between 'struct and '{'.
-nl_struct_brace                 = ignore   # ignore/add/remove/force
-
-# Add or remove newline between 'union' and '{'.
-nl_union_brace                  = ignore   # ignore/add/remove/force
-
-# Add or remove newline between 'if' and '{'.
-nl_if_brace                     = ignore   # ignore/add/remove/force
-
-# Add or remove newline between '}' and 'else'.
-nl_brace_else                   = ignore   # ignore/add/remove/force
-
-# Add or remove newline between 'else if' and '{'. If set to ignore,
-# nl_if_brace is used instead.
-nl_elseif_brace                 = ignore   # ignore/add/remove/force
-
-# Add or remove newline between 'else' and '{'.
-nl_else_brace                   = ignore   # ignore/add/remove/force
-
-# Add or remove newline between 'else' and 'if'.
-nl_else_if                      = ignore   # ignore/add/remove/force
-
-# Add or remove newline before 'if'/'else if' closing parenthesis.
-nl_before_if_closing_paren      = ignore   # ignore/add/remove/force
-
-# Add or remove newline between '}' and 'finally'.
-nl_brace_finally                = ignore   # ignore/add/remove/force
-
-# Add or remove newline between 'finally' and '{'.
-nl_finally_brace                = ignore   # ignore/add/remove/force
-
-# Add or remove newline between 'try' and '{'.
-nl_try_brace                    = ignore   # ignore/add/remove/force
-
-# Add or remove newline between get/set and '{'.
-nl_getset_brace                 = ignore   # ignore/add/remove/force
-
-# Add or remove newline between 'for' and '{'.
-nl_for_brace                    = ignore   # ignore/add/remove/force
-
-# Add or remove newline before the '{' of a 'catch' statement, as in
-# 'catch (decl)  {'.
-nl_catch_brace                  = ignore   # ignore/add/remove/force
-
-# (OC) Add or remove newline before the '{' of a '@catch' statement, as in
-# '@catch (decl)  {'. If set to ignore, nl_catch_brace is used.
-nl_oc_catch_brace               = ignore   # ignore/add/remove/force
-
-# Add or remove newline between '}' and 'catch'.
-nl_brace_catch                  = ignore   # ignore/add/remove/force
-
-# (OC) Add or remove newline between '}' and '@catch'. If set to ignore,
-# nl_brace_catch is used.
-nl_oc_brace_catch               = ignore   # ignore/add/remove/force
-
-# Add or remove newline between '}' and ']'.
-nl_brace_square                 = ignore   # ignore/add/remove/force
-
-# Add or remove newline between '}' and ')' in a function invocation.
-nl_brace_fparen                 = ignore   # ignore/add/remove/force
-
-# Add or remove newline between 'while' and '{'.
-nl_while_brace                  = ignore   # ignore/add/remove/force
-
-# (D) Add or remove newline between 'scope (x)' and '{'.
-nl_scope_brace                  = ignore   # ignore/add/remove/force
-
-# (D) Add or remove newline between 'unittest' and '{'.
-nl_unittest_brace               = ignore   # ignore/add/remove/force
-
-# (D) Add or remove newline between 'version (x)' and '{'.
-nl_version_brace                = ignore   # ignore/add/remove/force
-
-# (C#) Add or remove newline between 'using' and '{'.
-nl_using_brace                  = ignore   # ignore/add/remove/force
-
-# Add or remove newline between two open or close braces. Due to general
-# newline/brace handling, REMOVE may not work.
-nl_brace_brace                  = ignore   # ignore/add/remove/force
-
-# Add or remove newline between 'do' and '{'.
-nl_do_brace                     = ignore   # ignore/add/remove/force
-
-# Add or remove newline between '}' and 'while' of 'do' statement.
-nl_brace_while                  = ignore   # ignore/add/remove/force
-
-# Add or remove newline between 'switch' and '{'.
-nl_switch_brace                 = ignore   # ignore/add/remove/force
-
-# Add or remove newline between 'synchronized' and '{'.
-nl_synchronized_brace           = ignore   # ignore/add/remove/force
-
-# Add a newline between ')' and '{' if the ')' is on a different line than the
-# if/for/etc.
-#
-# Overrides nl_for_brace, nl_if_brace, nl_switch_brace, nl_while_switch and
-# nl_catch_brace.
-nl_multi_line_cond              = false    # true/false
-
-# Force a newline in a define after the macro name for multi-line defines.
-nl_multi_line_define            = false    # true/false
-
-# Whether to add a newline before 'case', and a blank line before a 'case'
-# statement that follows a ';' or '}'.
-nl_before_case                  = false    # true/false
-
-# Whether to add a newline after a 'case' statement.
-nl_after_case                   = false    # true/false
-
-# Add or remove newline between a case ':' and '{'.
-#
-# Overrides nl_after_case.
-nl_case_colon_brace             = ignore   # ignore/add/remove/force
-
-# Add or remove newline between ')' and 'throw'.
-nl_before_throw                 = ignore   # ignore/add/remove/force
-
-# Add or remove newline between 'namespace' and '{'.
-nl_namespace_brace              = ignore   # ignore/add/remove/force
-
-# Add or remove newline between 'template<>' and whatever follows.
-nl_template_class               = ignore   # ignore/add/remove/force
-
-# Add or remove newline between 'class' and '{'.
-nl_class_brace                  = ignore   # ignore/add/remove/force
-
-# Add or remove newline before or after (depending on pos_class_comma,
-# may not be IGNORE) each',' in the base class list.
-nl_class_init_args              = ignore   # ignore/add/remove/force
-
-# Add or remove newline after each ',' in the constructor member
-# initialization. Related to nl_constr_colon, pos_constr_colon and
-# pos_constr_comma.
-nl_constr_init_args             = ignore   # ignore/add/remove/force
-
-# Add or remove newline before first element, after comma, and after last
-# element, in 'enum'.
-nl_enum_own_lines               = ignore   # ignore/add/remove/force
-
-# Add or remove newline between return type and function name in a function
-# definition.
-nl_func_type_name               = ignore   # ignore/add/remove/force
-
-# Add or remove newline between return type and function name inside a class
-# definition. If set to ignore, nl_func_type_name or nl_func_proto_type_name
-# is used instead.
-nl_func_type_name_class         = ignore   # ignore/add/remove/force
-
-# Add or remove newline between class specification and '::'
-# in 'void A::f() { }'. Only appears in separate member implementation (does
-# not appear with in-line implementation).
-nl_func_class_scope             = ignore   # ignore/add/remove/force
-
-# Add or remove newline between function scope and name, as in
-# 'void A ::  f() { }'.
-nl_func_scope_name              = ignore   # ignore/add/remove/force
-
-# Add or remove newline between return type and function name in a prototype.
-nl_func_proto_type_name         = ignore   # ignore/add/remove/force
-
-# Add or remove newline between a function name and the opening '(' in the
-# declaration.
-nl_func_paren                   = ignore   # ignore/add/remove/force
-
-# Overrides nl_func_paren for functions with no parameters.
-nl_func_paren_empty             = ignore   # ignore/add/remove/force
-
-# Add or remove newline between a function name and the opening '(' in the
-# definition.
-nl_func_def_paren               = ignore   # ignore/add/remove/force
-
-# Overrides nl_func_def_paren for functions with no parameters.
-nl_func_def_paren_empty         = ignore   # ignore/add/remove/force
-
-# Add or remove newline between a function name and the opening '(' in the
-# call.
-nl_func_call_paren              = ignore   # ignore/add/remove/force
-
-# Overrides nl_func_call_paren for functions with no parameters.
-nl_func_call_paren_empty        = ignore   # ignore/add/remove/force
-
-# Add or remove newline after '(' in a function declaration.
-nl_func_decl_start              = ignore   # ignore/add/remove/force
-
-# Add or remove newline after '(' in a function definition.
-nl_func_def_start               = ignore   # ignore/add/remove/force
-
-# Overrides nl_func_decl_start when there is only one parameter.
-nl_func_decl_start_single       = ignore   # ignore/add/remove/force
-
-# Overrides nl_func_def_start when there is only one parameter.
-nl_func_def_start_single        = ignore   # ignore/add/remove/force
-
-# Whether to add a newline after '(' in a function declaration if '(' and ')'
-# are in different lines. If false, nl_func_decl_start is used instead.
-nl_func_decl_start_multi_line   = false    # true/false
-
-# Whether to add a newline after '(' in a function definition if '(' and ')'
-# are in different lines. If false, nl_func_def_start is used instead.
-nl_func_def_start_multi_line    = false    # true/false
-
-# Add or remove newline after each ',' in a function declaration.
-nl_func_decl_args               = ignore   # ignore/add/remove/force
-
-# Add or remove newline after each ',' in a function definition.
-nl_func_def_args                = ignore   # ignore/add/remove/force
-
-# Whether to add a newline after each ',' in a function declaration if '('
-# and ')' are in different lines. If false, nl_func_decl_args is used instead.
-nl_func_decl_args_multi_line    = false    # true/false
-
-# Whether to add a newline after each ',' in a function definition if '('
-# and ')' are in different lines. If false, nl_func_def_args is used instead.
-nl_func_def_args_multi_line     = false    # true/false
-
-# Add or remove newline before the ')' in a function declaration.
-nl_func_decl_end                = ignore   # ignore/add/remove/force
-
-# Add or remove newline before the ')' in a function definition.
-nl_func_def_end                 = ignore   # ignore/add/remove/force
-
-# Overrides nl_func_decl_end when there is only one parameter.
-nl_func_decl_end_single         = ignore   # ignore/add/remove/force
-
-# Overrides nl_func_def_end when there is only one parameter.
-nl_func_def_end_single          = ignore   # ignore/add/remove/force
-
-# Whether to add a newline before ')' in a function declaration if '(' and ')'
-# are in different lines. If false, nl_func_decl_end is used instead.
-nl_func_decl_end_multi_line     = false    # true/false
-
-# Whether to add a newline before ')' in a function definition if '(' and ')'
-# are in different lines. If false, nl_func_def_end is used instead.
-nl_func_def_end_multi_line      = false    # true/false
-
-# Add or remove newline between '()' in a function declaration.
-nl_func_decl_empty              = ignore   # ignore/add/remove/force
-
-# Add or remove newline between '()' in a function definition.
-nl_func_def_empty               = ignore   # ignore/add/remove/force
-
-# Add or remove newline between '()' in a function call.
-nl_func_call_empty              = ignore   # ignore/add/remove/force
-
-# Whether to add a newline after '(' in a function call,
-# has preference over nl_func_call_start_multi_line.
-nl_func_call_start              = ignore   # ignore/add/remove/force
-
-# Whether to add a newline after '(' in a function call if '(' and ')' are in
-# different lines.
-nl_func_call_start_multi_line   = false    # true/false
-
-# Whether to add a newline after each ',' in a function call if '(' and ')'
-# are in different lines.
-nl_func_call_args_multi_line    = false    # true/false
-
-# Whether to add a newline before ')' in a function call if '(' and ')' are in
-# different lines.
-nl_func_call_end_multi_line     = false    # true/false
-
-# (OC) Whether to put each Objective-C message parameter on a separate line.
-# See nl_oc_msg_leave_one_liner.
-nl_oc_msg_args                  = false    # true/false
-
-# Add or remove newline between function signature and '{'.
-nl_fdef_brace                   = ignore   # ignore/add/remove/force
-
-# Add or remove newline between function signature and '{',
-# if signature ends with ')'. Overrides nl_fdef_brace.
-nl_fdef_brace_cond              = ignore   # ignore/add/remove/force
-
-# Add or remove newline between C++11 lambda signature and '{'.
-nl_cpp_ldef_brace               = ignore   # ignore/add/remove/force
-
-# Add or remove newline between 'return' and the return expression.
-nl_return_expr                  = ignore   # ignore/add/remove/force
-
-# Whether to add a newline after semicolons, except in 'for' statements.
-nl_after_semicolon              = false    # true/false
-
-# (Java) Add or remove newline between the ')' and '{{' of the double brace
-# initializer.
-nl_paren_dbrace_open            = ignore   # ignore/add/remove/force
-
-# Whether to add a newline after the type in an unnamed temporary
-# direct-list-initialization.
-nl_type_brace_init_lst          = ignore   # ignore/add/remove/force
-
-# Whether to add a newline after the open brace in an unnamed temporary
-# direct-list-initialization.
-nl_type_brace_init_lst_open     = ignore   # ignore/add/remove/force
-
-# Whether to add a newline before the close brace in an unnamed temporary
-# direct-list-initialization.
-nl_type_brace_init_lst_close    = ignore   # ignore/add/remove/force
-
-# Whether to add a newline after '{'. This also adds a newline before the
-# matching '}'.
-nl_after_brace_open             = false    # true/false
-
-# Whether to add a newline between the open brace and a trailing single-line
-# comment. Requires nl_after_brace_open=true.
-nl_after_brace_open_cmt         = false    # true/false
-
-# Whether to add a newline after a virtual brace open with a non-empty body.
-# These occur in un-braced if/while/do/for statement bodies.
-nl_after_vbrace_open            = false    # true/false
-
-# Whether to add a newline after a virtual brace open with an empty body.
-# These occur in un-braced if/while/do/for statement bodies.
-nl_after_vbrace_open_empty      = false    # true/false
-
-# Whether to add a newline after '}'. Does not apply if followed by a
-# necessary ';'.
-nl_after_brace_close            = false    # true/false
-
-# Whether to add a newline after a virtual brace close,
-# as in 'if (foo) a++;  return;'.
-nl_after_vbrace_close           = false    # true/false
-
-# Add or remove newline between the close brace and identifier,
-# as in 'struct { int a; }  b;'. Affects enumerations, unions and
-# structures. If set to ignore, uses nl_after_brace_close.
-nl_brace_struct_var             = ignore   # ignore/add/remove/force
-
-# Whether to alter newlines in '#define' macros.
-nl_define_macro                 = false    # true/false
-
-# Whether to alter newlines between consecutive parenthesis closes. The number
-# of closing parentheses in a line will depend on respective open parenthesis
-# lines.
-nl_squeeze_paren_close          = false    # true/false
-
-# Whether to remove blanks after '#ifxx' and '#elxx', or before '#elxx' and
-# '#endif'. Does not affect top-level #ifdefs.
-nl_squeeze_ifdef                = false    # true/false
-
-# Makes the nl_squeeze_ifdef option affect the top-level #ifdefs as well.
-nl_squeeze_ifdef_top_level      = false    # true/false
-
-# Add or remove blank line before 'if'.
-nl_before_if                    = ignore   # ignore/add/remove/force
-
-# Add or remove blank line after 'if' statement. Add/Force work only if the
-# next token is not a closing brace.
-nl_after_if                     = ignore   # ignore/add/remove/force
-
-# Add or remove blank line before 'for'.
-nl_before_for                   = ignore   # ignore/add/remove/force
-
-# Add or remove blank line after 'for' statement.
-nl_after_for                    = ignore   # ignore/add/remove/force
-
-# Add or remove blank line before 'while'.
-nl_before_while                 = ignore   # ignore/add/remove/force
-
-# Add or remove blank line after 'while' statement.
-nl_after_while                  = ignore   # ignore/add/remove/force
-
-# Add or remove blank line before 'switch'.
-nl_before_switch                = ignore   # ignore/add/remove/force
-
-# Add or remove blank line after 'switch' statement.
-nl_after_switch                 = ignore   # ignore/add/remove/force
-
-# Add or remove blank line before 'synchronized'.
-nl_before_synchronized          = ignore   # ignore/add/remove/force
-
-# Add or remove blank line after 'synchronized' statement.
-nl_after_synchronized           = ignore   # ignore/add/remove/force
-
-# Add or remove blank line before 'do'.
-nl_before_do                    = ignore   # ignore/add/remove/force
-
-# Add or remove blank line after 'do/while' statement.
-nl_after_do                     = ignore   # ignore/add/remove/force
-
-# Whether to put a blank line before 'return' statements, unless after an open
-# brace.
-nl_before_return                = false    # true/false
-
-# Whether to put a blank line after 'return' statements, unless followed by a
-# close brace.
-nl_after_return                 = false    # true/false
-
-# Whether to double-space commented-entries in 'struct'/'union'/'enum'.
-nl_ds_struct_enum_cmt           = false    # true/false
-
-# Whether to force a newline before '}' of a 'struct'/'union'/'enum'.
-# (Lower priority than eat_blanks_before_close_brace.)
-nl_ds_struct_enum_close_brace   = false    # true/false
-
-# Add or remove newline before or after (depending on pos_class_colon) a class
-# colon, as in 'class Foo  :  public Bar'.
-nl_class_colon                  = ignore   # ignore/add/remove/force
-
-# Add or remove newline around a class constructor colon. The exact position
-# depends on nl_constr_init_args, pos_constr_colon and pos_constr_comma.
-nl_constr_colon                 = ignore   # ignore/add/remove/force
-
-# Whether to collapse a two-line namespace, like 'namespace foo\n{ decl; }'
-# into a single line. If true, prevents other brace newline rules from turning
-# such code into four lines.
-nl_namespace_two_to_one_liner   = false    # true/false
-
-# Whether to remove a newline in simple unbraced if statements, turning them
-# into one-liners, as in 'if(b)\n i++;' => 'if(b) i++;'.
-nl_create_if_one_liner          = false    # true/false
-
-# Whether to remove a newline in simple unbraced for statements, turning them
-# into one-liners, as in 'for (...)\n stmt;' => 'for (...) stmt;'.
-nl_create_for_one_liner         = false    # true/false
-
-# Whether to remove a newline in simple unbraced while statements, turning
-# them into one-liners, as in 'while (expr)\n stmt;' => 'while (expr) stmt;'.
-nl_create_while_one_liner       = false    # true/false
-
-# Whether to collapse a function definition whose body (not counting braces)
-# is only one line so that the entire definition (prototype, braces, body) is
-# a single line.
-nl_create_func_def_one_liner    = false    # true/false
-
-# Whether to split one-line simple unbraced if statements into two lines by
-# adding a newline, as in 'if(b)  i++;'.
-nl_split_if_one_liner           = false    # true/false
-
-# Whether to split one-line simple unbraced for statements into two lines by
-# adding a newline, as in 'for (...)  stmt;'.
-nl_split_for_one_liner          = false    # true/false
-
-# Whether to split one-line simple unbraced while statements into two lines by
-# adding a newline, as in 'while (expr)  stmt;'.
-nl_split_while_one_liner        = false    # true/false
-
-#
-# Blank line options
-#
-
-# The maximum number of consecutive newlines (3 = 2 blank lines).
-nl_max                          = 0        # unsigned number
-
-# The maximum number of consecutive newlines in a function.
-nl_max_blank_in_func            = 0        # unsigned number
-
-# The number of newlines before a function prototype.
-nl_before_func_body_proto       = 0        # unsigned number
-
-# The number of newlines before a multi-line function definition.
-nl_before_func_body_def         = 0        # unsigned number
-
-# The number of newlines before a class constructor/destructor prototype.
-nl_before_func_class_proto      = 0        # unsigned number
-
-# The number of newlines before a class constructor/destructor definition.
-nl_before_func_class_def        = 0        # unsigned number
-
-# The number of newlines after a function prototype.
-nl_after_func_proto             = 0        # unsigned number
-
-# The number of newlines after a function prototype, if not followed by
-# another function prototype.
-nl_after_func_proto_group       = 0        # unsigned number
-
-# The number of newlines after a class constructor/destructor prototype.
-nl_after_func_class_proto       = 0        # unsigned number
-
-# The number of newlines after a class constructor/destructor prototype,
-# if not followed by another constructor/destructor prototype.
-nl_after_func_class_proto_group = 0        # unsigned number
-
-# Whether one-line method definitions inside a class body should be treated
-# as if they were prototypes for the purposes of adding newlines.
-#
-# Requires nl_class_leave_one_liners=true. Overrides nl_before_func_body_def
-# and nl_before_func_class_def for one-liners.
-nl_class_leave_one_liner_groups = false    # true/false
-
-# The number of newlines after '}' of a multi-line function body.
-nl_after_func_body              = 0        # unsigned number
-
-# The number of newlines after '}' of a multi-line function body in a class
-# declaration. Also affects class constructors/destructors.
-#
-# Overrides nl_after_func_body.
-nl_after_func_body_class        = 0        # unsigned number
-
-# The number of newlines after '}' of a single line function body. Also
-# affects class constructors/destructors.
-#
-# Overrides nl_after_func_body and nl_after_func_body_class.
-nl_after_func_body_one_liner    = 0        # unsigned number
-
-# The number of blank lines after a block of variable definitions at the top
-# of a function body.
-#
-# 0 = No change (default).
-nl_func_var_def_blk             = 0        # unsigned number
-
-# The number of newlines before a block of typedefs. If nl_after_access_spec
-# is non-zero, that option takes precedence.
-#
-# 0 = No change (default).
-nl_typedef_blk_start            = 0        # unsigned number
-
-# The number of newlines after a block of typedefs.
-#
-# 0 = No change (default).
-nl_typedef_blk_end              = 0        # unsigned number
-
-# The maximum number of consecutive newlines within a block of typedefs.
-#
-# 0 = No change (default).
-nl_typedef_blk_in               = 0        # unsigned number
-
-# The number of newlines before a block of variable definitions not at the top
-# of a function body. If nl_after_access_spec is non-zero, that option takes
-# precedence.
-#
-# 0 = No change (default).
-nl_var_def_blk_start            = 0        # unsigned number
-
-# The number of newlines after a block of variable definitions not at the top
-# of a function body.
-#
-# 0 = No change (default).
-nl_var_def_blk_end              = 0        # unsigned number
-
-# The maximum number of consecutive newlines within a block of variable
-# definitions.
-#
-# 0 = No change (default).
-nl_var_def_blk_in               = 0        # unsigned number
-
-# The minimum number of newlines before a multi-line comment.
-# Doesn't apply if after a brace open or another multi-line comment.
-nl_before_block_comment         = 1        # unsigned number
-
-# The minimum number of newlines before a single-line C comment.
-# Doesn't apply if after a brace open or other single-line C comments.
-nl_before_c_comment             = 0        # unsigned number
-
-# The minimum number of newlines before a CPP comment.
-# Doesn't apply if after a brace open or other CPP comments.
-nl_before_cpp_comment           = 0        # unsigned number
-
-# Whether to force a newline after a multi-line comment.
-nl_after_multiline_comment      = false    # true/false
-
-# Whether to force a newline after a label's colon.
-nl_after_label_colon            = false    # true/false
-
-# The number of newlines after '}' or ';' of a struct/enum/union definition.
-nl_after_struct                 = 1        # unsigned number
-
-# The number of newlines before a class definition.
-nl_before_class                 = 1        # unsigned number
-
-# The number of newlines after '}' or ';' of a class definition.
-nl_after_class                  = 0        # unsigned number
-
-# The number of newlines before an access specifier label. This also includes
-# the Qt-specific 'signals:' and 'slots:'. Will not change the newline count
-# if after a brace open.
-#
-# 0 = No change (default).
-nl_before_access_spec           = 0        # unsigned number
-
-# The number of newlines after an access specifier label. This also includes
-# the Qt-specific 'signals:' and 'slots:'. Will not change the newline count
-# if after a brace open.
-#
-# 0 = No change (default).
-#
-# Overrides nl_typedef_blk_start and nl_var_def_blk_start.
-nl_after_access_spec            = 0        # unsigned number
-
-# The number of newlines between a function definition and the function
-# comment, as in '// comment\n  void foo() {...}'.
-#
-# 0 = No change (default).
-nl_comment_func_def             = 0        # unsigned number
-
-# The number of newlines after a try-catch-finally block that isn't followed
-# by a brace close.
-#
-# 0 = No change (default).
-nl_after_try_catch_finally      = 0        # unsigned number
-
-# (C#) The number of newlines before and after a property, indexer or event
-# declaration.
-#
-# 0 = No change (default).
-nl_around_cs_property           = 0        # unsigned number
-
-# (C#) The number of newlines between the get/set/add/remove handlers.
-#
-# 0 = No change (default).
-nl_between_get_set              = 0        # unsigned number
-
-# (C#) Add or remove newline between property and the '{'.
-nl_property_brace               = ignore   # ignore/add/remove/force
-
-# The number of newlines after '{' of a namespace. This also adds newlines
-# before the matching '}'.
-#
-# 0 = Apply eat_blanks_after_open_brace or eat_blanks_before_close_brace if
-#     applicable, otherwise no change.
-#
-# Overrides eat_blanks_after_open_brace and eat_blanks_before_close_brace.
-nl_inside_namespace             = 0        # unsigned number
-
-# Whether to remove blank lines after '{'.
-eat_blanks_after_open_brace     = false    # true/false
-
-# Whether to remove blank lines before '}'.
-eat_blanks_before_close_brace   = false    # true/false
-
-# How aggressively to remove extra newlines not in preprocessor.
-#
-# 0: No change (default)
-# 1: Remove most newlines not handled by other config
-# 2: Remove all newlines and reformat completely by config
-nl_remove_extra_newlines        = 0        # unsigned number
-
-# (Java) Add or remove newline after an annotation statement. Only affects
-# annotations that are after a newline.
-nl_after_annotation             = ignore   # ignore/add/remove/force
-
-# (Java) Add or remove newline between two annotations.
-nl_between_annotation           = ignore   # ignore/add/remove/force
-
-#
-# Positioning options
-#
-
-# The position of arithmetic operators in wrapped expressions.
-pos_arith                       = ignore   # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force
-
-# The position of assignment in wrapped expressions. Do not affect '='
-# followed by '{'.
-pos_assign                      = ignore   # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force
-
-# The position of Boolean operators in wrapped expressions.
-pos_bool                        = ignore   # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force
-
-# The position of comparison operators in wrapped expressions.
-pos_compare                     = ignore   # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force
-
-# The position of conditional operators, as in the '?' and ':' of
-# 'expr ? stmt : stmt', in wrapped expressions.
-pos_conditional                 = ignore   # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force
-
-# The position of the comma in wrapped expressions.
-pos_comma                       = ignore   # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force
-
-# The position of the comma in enum entries.
-pos_enum_comma                  = ignore   # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force
-
-# The position of the comma in the base class list if there is more than one
-# line. Affects nl_class_init_args.
-pos_class_comma                 = ignore   # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force
-
-# The position of the comma in the constructor initialization list.
-# Related to nl_constr_colon, nl_constr_init_args and pos_constr_colon.
-pos_constr_comma                = ignore   # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force
-
-# The position of trailing/leading class colon, between class and base class
-# list. Affects nl_class_colon.
-pos_class_colon                 = ignore   # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force
-
-# The position of colons between constructor and member initialization.
-# Related to nl_constr_colon, nl_constr_init_args and pos_constr_comma.
-pos_constr_colon                = ignore   # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force
-
-#
-# Line splitting options
-#
-
-# Try to limit code width to N columns.
-code_width                      = 0        # unsigned number
-
-# Whether to fully split long 'for' statements at semi-colons.
-ls_for_split_full               = false    # true/false
-
-# Whether to fully split long function prototypes/calls at commas.
-# The option ls_code_width has priority over the option ls_func_split_full.
-ls_func_split_full              = false    # true/false
-
-# Whether to split lines as close to code_width as possible and ignore some
-# groupings.
-# The option ls_code_width has priority over the option ls_func_split_full.
-ls_code_width                   = false    # true/false
-
-#
-# Code alignment options (not left column spaces/tabs)
-#
-
-# Whether to keep non-indenting tabs.
-align_keep_tabs                 = false    # true/false
-
-# Whether to use tabs for aligning.
-align_with_tabs                 = false    # true/false
-
-# Whether to bump out to the next tab when aligning.
-align_on_tabstop                = false    # true/false
-
-# Whether to right-align numbers.
-align_number_right              = false    # true/false
-
-# Whether to keep whitespace not required for alignment.
-align_keep_extra_space          = false    # true/false
-
-# Whether to align variable definitions in prototypes and functions.
-align_func_params               = false    # true/false
-
-# The span for aligning parameter definitions in function on parameter name.
-#
-# 0 = Don't align (default).
-align_func_params_span          = 0        # unsigned number
-
-# The threshold for aligning function parameter definitions.
-# Use a negative number for absolute thresholds.
-#
-# 0 = No limit (default).
-align_func_params_thresh        = 0        # number
-
-# The gap for aligning function parameter definitions.
-align_func_params_gap           = 0        # unsigned number
-
-# The span for aligning constructor value.
-#
-# 0 = Don't align (default).
-align_constr_value_span         = 0        # unsigned number
-
-# The threshold for aligning constructor value.
-# Use a negative number for absolute thresholds.
-#
-# 0 = No limit (default).
-align_constr_value_thresh       = 0        # number
-
-# The gap for aligning constructor value.
-align_constr_value_gap          = 0        # unsigned number
-
-# Whether to align parameters in single-line functions that have the same
-# name. The function names must already be aligned with each other.
-align_same_func_call_params     = false    # true/false
-
-# The span for aligning function-call parameters for single line functions.
-#
-# 0 = Don't align (default).
-align_same_func_call_params_span = 0        # unsigned number
-
-# The threshold for aligning function-call parameters for single line
-# functions.
-# Use a negative number for absolute thresholds.
-#
-# 0 = No limit (default).
-align_same_func_call_params_thresh = 0        # number
-
-# The span for aligning variable definitions.
-#
-# 0 = Don't align (default).
-align_var_def_span              = 0        # unsigned number
-
-# How to consider (or treat) the '*' in the alignment of variable definitions.
-#
-# 0: Part of the type     'void *   foo;' (default)
-# 1: Part of the variable 'void     *foo;'
-# 2: Dangling             'void    *foo;'
-# Dangling: the '*' will not be taken into account when aligning.
-align_var_def_star_style        = 0        # unsigned number
-
-# How to consider (or treat) the '&' in the alignment of variable definitions.
-#
-# 0: Part of the type     'long &   foo;' (default)
-# 1: Part of the variable 'long     &foo;'
-# 2: Dangling             'long    &foo;'
-# Dangling: the '&' will not be taken into account when aligning.
-align_var_def_amp_style         = 0        # unsigned number
-
-# The threshold for aligning variable definitions.
-# Use a negative number for absolute thresholds.
-#
-# 0 = No limit (default).
-align_var_def_thresh            = 0        # number
-
-# The gap for aligning variable definitions.
-align_var_def_gap               = 0        # unsigned number
-
-# Whether to align the colon in struct bit fields.
-align_var_def_colon             = false    # true/false
-
-# The gap for aligning the colon in struct bit fields.
-align_var_def_colon_gap         = 0        # unsigned number
-
-# Whether to align any attribute after the variable name.
-align_var_def_attribute         = false    # true/false
-
-# Whether to align inline struct/enum/union variable definitions.
-align_var_def_inline            = false    # true/false
-
-# The span for aligning on '=' in assignments.
-#
-# 0 = Don't align (default).
-align_assign_span               = 0        # unsigned number
-
-# The span for aligning on '=' in function prototype modifier.
-#
-# 0 = Don't align (default).
-align_assign_func_proto_span    = 0        # unsigned number
-
-# The threshold for aligning on '=' in assignments.
-# Use a negative number for absolute thresholds.
-#
-# 0 = No limit (default).
-align_assign_thresh             = 0        # number
-
-# How to apply align_assign_span to function declaration "assignments", i.e.
-# 'virtual void foo() = 0' or '~foo() = {default|delete}'.
-#
-# 0: Align with other assignments (default)
-# 1: Align with each other, ignoring regular assignments
-# 2: Don't align
-align_assign_decl_func          = 0        # unsigned number
-
-# The span for aligning on '=' in enums.
-#
-# 0 = Don't align (default).
-align_enum_equ_span             = 0        # unsigned number
-
-# The threshold for aligning on '=' in enums.
-# Use a negative number for absolute thresholds.
-#
-# 0 = no limit (default).
-align_enum_equ_thresh           = 0        # number
-
-# The span for aligning class member definitions.
-#
-# 0 = Don't align (default).
-align_var_class_span            = 0        # unsigned number
-
-# The threshold for aligning class member definitions.
-# Use a negative number for absolute thresholds.
-#
-# 0 = No limit (default).
-align_var_class_thresh          = 0        # number
-
-# The gap for aligning class member definitions.
-align_var_class_gap             = 0        # unsigned number
-
-# The span for aligning struct/union member definitions.
-#
-# 0 = Don't align (default).
-align_var_struct_span           = 0        # unsigned number
-
-# The threshold for aligning struct/union member definitions.
-# Use a negative number for absolute thresholds.
-#
-# 0 = No limit (default).
-align_var_struct_thresh         = 0        # number
-
-# The gap for aligning struct/union member definitions.
-align_var_struct_gap            = 0        # unsigned number
-
-# The span for aligning struct initializer values.
-#
-# 0 = Don't align (default).
-align_struct_init_span          = 0        # unsigned number
-
-# The span for aligning single-line typedefs.
-#
-# 0 = Don't align (default).
-align_typedef_span              = 0        # unsigned number
-
-# The minimum space between the type and the synonym of a typedef.
-align_typedef_gap               = 0        # unsigned number
-
-# How to align typedef'd functions with other typedefs.
-#
-# 0: Don't mix them at all (default)
-# 1: Align the open parenthesis with the types
-# 2: Align the function type name with the other type names
-align_typedef_func              = 0        # unsigned number
-
-# How to consider (or treat) the '*' in the alignment of typedefs.
-#
-# 0: Part of the typedef type, 'typedef int * pint;' (default)
-# 1: Part of type name:        'typedef int   *pint;'
-# 2: Dangling:                 'typedef int  *pint;'
-# Dangling: the '*' will not be taken into account when aligning.
-align_typedef_star_style        = 0        # unsigned number
-
-# How to consider (or treat) the '&' in the alignment of typedefs.
-#
-# 0: Part of the typedef type, 'typedef int & intref;' (default)
-# 1: Part of type name:        'typedef int   &intref;'
-# 2: Dangling:                 'typedef int  &intref;'
-# Dangling: the '&' will not be taken into account when aligning.
-align_typedef_amp_style         = 0        # unsigned number
-
-# The span for aligning comments that end lines.
-#
-# 0 = Don't align (default).
-align_right_cmt_span            = 0        # unsigned number
-
-# Minimum number of columns between preceding text and a trailing comment in
-# order for the comment to qualify for being aligned. Must be non-zero to have
-# an effect.
-align_right_cmt_gap             = 0        # unsigned number
-
-# If aligning comments, whether to mix with comments after '}' and #endif with
-# less than three spaces before the comment.
-align_right_cmt_mix             = false    # true/false
-
-# Whether to only align trailing comments that are at the same brace level.
-align_right_cmt_same_level      = false    # true/false
-
-# Minimum column at which to align trailing comments. Comments which are
-# aligned beyond this column, but which can be aligned in a lesser column,
-# may be "pulled in".
-#
-# 0 = Ignore (default).
-align_right_cmt_at_col          = 0        # unsigned number
-
-# The span for aligning function prototypes.
-#
-# 0 = Don't align (default).
-align_func_proto_span           = 0        # unsigned number
-
-# The threshold for aligning function prototypes.
-# Use a negative number for absolute thresholds.
-#
-# 0 = No limit (default).
-align_func_proto_thresh         = 0        # number
-
-# Minimum gap between the return type and the function name.
-align_func_proto_gap            = 0        # unsigned number
-
-# Whether to align function prototypes on the 'operator' keyword instead of
-# what follows.
-align_on_operator               = false    # true/false
-
-# Whether to mix aligning prototype and variable declarations. If true,
-# align_var_def_XXX options are used instead of align_func_proto_XXX options.
-align_mix_var_proto             = false    # true/false
-
-# Whether to align single-line functions with function prototypes.
-# Uses align_func_proto_span.
-align_single_line_func          = false    # true/false
-
-# Whether to align the open brace of single-line functions.
-# Requires align_single_line_func=true. Uses align_func_proto_span.
-align_single_line_brace         = false    # true/false
-
-# Gap for align_single_line_brace.
-align_single_line_brace_gap     = 0        # unsigned number
-
-# (OC) The span for aligning Objective-C message specifications.
-#
-# 0 = Don't align (default).
-align_oc_msg_spec_span          = 0        # unsigned number
-
-# Whether to align macros wrapped with a backslash and a newline. This will
-# not work right if the macro contains a multi-line comment.
-align_nl_cont                   = false    # true/false
-
-# Whether to align macro functions and variables together.
-align_pp_define_together        = false    # true/false
-
-# The span for aligning on '#define' bodies.
-#
-# =0: Don't align (default)
-# >0: Number of lines (including comments) between blocks
-align_pp_define_span            = 0        # unsigned number
-
-# The minimum space between label and value of a preprocessor define.
-align_pp_define_gap             = 0        # unsigned number
-
-# Whether to align lines that start with '<<' with previous '<<'.
-#
-# Default: true
-align_left_shift                = true     # true/false
-
-# Whether to align text after 'asm volatile ()' colons.
-align_asm_colon                 = false    # true/false
-
-# (OC) Span for aligning parameters in an Objective-C message call
-# on the ':'.
-#
-# 0 = Don't align.
-align_oc_msg_colon_span         = 0        # unsigned number
-
-# (OC) Whether to always align with the first parameter, even if it is too
-# short.
-align_oc_msg_colon_first        = false    # true/false
-
-# (OC) Whether to align parameters in an Objective-C '+' or '-' declaration
-# on the ':'.
-align_oc_decl_colon             = false    # true/false
-
-#
-# Comment modification options
-#
-
-# Try to wrap comments at N columns.
-cmt_width                       = 80       # unsigned number
-
-# How to reflow comments.
-#
-# 0: No reflowing (apart from the line wrapping due to cmt_width) (default)
-# 1: No touching at all
-# 2: Full reflow
-cmt_reflow_mode                 = 2        # unsigned number
-
-# Whether to convert all tabs to spaces in comments. If false, tabs in
-# comments are left alone, unless used for indenting.
-cmt_convert_tab_to_spaces       = true     # true/false
-
-# Whether to apply changes to multi-line comments, including cmt_width,
-# keyword substitution and leading chars.
-#
-# Default: true
-cmt_indent_multi                = true     # true/false
-
-# Whether to group c-comments that look like they are in a block.
-cmt_c_group                     = false    # true/false
-
-# Whether to put an empty '/*' on the first line of the combined c-comment.
-cmt_c_nl_start                  = false    # true/false
-
-# Whether to add a newline before the closing '*/' of the combined c-comment.
-cmt_c_nl_end                    = false    # true/false
-
-# Whether to change cpp-comments into c-comments.
-cmt_cpp_to_c                    = false    # true/false
-
-# Whether to group cpp-comments that look like they are in a block. Only
-# meaningful if cmt_cpp_to_c=true.
-cmt_cpp_group                   = false    # true/false
-
-# Whether to put an empty '/*' on the first line of the combined cpp-comment
-# when converting to a c-comment.
-#
-# Requires cmt_cpp_to_c=true and cmt_cpp_group=true.
-cmt_cpp_nl_start                = false    # true/false
-
-# Whether to add a newline before the closing '*/' of the combined cpp-comment
-# when converting to a c-comment.
-#
-# Requires cmt_cpp_to_c=true and cmt_cpp_group=true.
-cmt_cpp_nl_end                  = false    # true/false
-
-# Whether to put a star on subsequent comment lines.
-cmt_star_cont                   = false    # true/false
-
-# The number of spaces to insert at the start of subsequent comment lines.
-cmt_sp_before_star_cont         = 0        # unsigned number
-
-# The number of spaces to insert after the star on subsequent comment lines.
-cmt_sp_after_star_cont          = 0        # unsigned number
-
-# For multi-line comments with a '*' lead, remove leading spaces if the first
-# and last lines of the comment are the same length.
-#
-# Default: true
-cmt_multi_check_last            = true     # true/false
-
-# For multi-line comments with a '*' lead, remove leading spaces if the first
-# and last lines of the comment are the same length AND if the length is
-# bigger as the first_len minimum.
-#
-# Default: 4
-cmt_multi_first_len_minimum     = 4        # unsigned number
-
-# Path to a file that contains text to insert at the beginning of a file if
-# the file doesn't start with a C/C++ comment. If the inserted text contains
-# '$(filename)', that will be replaced with the current file's name.
-cmt_insert_file_header          = ""         # string
-
-# Path to a file that contains text to insert at the end of a file if the
-# file doesn't end with a C/C++ comment. If the inserted text contains
-# '$(filename)', that will be replaced with the current file's name.
-cmt_insert_file_footer          = ""         # string
-
-# Path to a file that contains text to insert before a function definition if
-# the function isn't preceded by a C/C++ comment. If the inserted text
-# contains '$(function)', '$(javaparam)' or '$(fclass)', these will be
-# replaced with, respectively, the name of the function, the javadoc '@param'
-# and '@return' stuff, or the name of the class to which the member function
-# belongs.
-cmt_insert_func_header          = ""         # string
-
-# Path to a file that contains text to insert before a class if the class
-# isn't preceded by a C/C++ comment. If the inserted text contains '$(class)',
-# that will be replaced with the class name.
-cmt_insert_class_header         = ""         # string
-
-# Path to a file that contains text to insert before an Objective-C message
-# specification, if the method isn't preceded by a C/C++ comment. If the
-# inserted text contains '$(message)' or '$(javaparam)', these will be
-# replaced with, respectively, the name of the function, or the javadoc
-# '@param' and '@return' stuff.
-cmt_insert_oc_msg_header        = ""         # string
-
-# Whether a comment should be inserted if a preprocessor is encountered when
-# stepping backwards from a function name.
-#
-# Applies to cmt_insert_oc_msg_header, cmt_insert_func_header and
-# cmt_insert_class_header.
-cmt_insert_before_preproc       = false    # true/false
-
-# Whether a comment should be inserted if a function is declared inline to a
-# class definition.
-#
-# Applies to cmt_insert_func_header.
-#
-# Default: true
-cmt_insert_before_inlines       = true     # true/false
-
-# Whether a comment should be inserted if the function is a class constructor
-# or destructor.
-#
-# Applies to cmt_insert_func_header.
-cmt_insert_before_ctor_dtor     = false    # true/false
-
-#
-# Code modifying options (non-whitespace)
-#
-
-# Add or remove braces on a single-line 'do' statement.
-mod_full_brace_do               = ignore   # ignore/add/remove/force
-
-# Add or remove braces on a single-line 'for' statement.
-mod_full_brace_for              = ignore   # ignore/add/remove/force
-
-# (Pawn) Add or remove braces on a single-line function definition.
-mod_full_brace_function         = ignore   # ignore/add/remove/force
-
-# Add or remove braces on a single-line 'if' statement. Braces will not be
-# removed if the braced statement contains an 'else'.
-mod_full_brace_if               = ignore   # ignore/add/remove/force
-
-# Whether to enforce that all blocks of an 'if'/'else if'/'else' chain either
-# have, or do not have, braces. If true, braces will be added if any block
-# needs braces, and will only be removed if they can be removed from all
-# blocks.
-#
-# Overrides mod_full_brace_if.
-mod_full_brace_if_chain         = false    # true/false
-
-# Whether to add braces to all blocks of an 'if'/'else if'/'else' chain.
-# If true, mod_full_brace_if_chain will only remove braces from an 'if' that
-# does not have an 'else if' or 'else'.
-mod_full_brace_if_chain_only    = false    # true/false
-
-# Add or remove braces on single-line 'while' statement.
-mod_full_brace_while            = ignore   # ignore/add/remove/force
-
-# Add or remove braces on single-line 'using ()' statement.
-mod_full_brace_using            = ignore   # ignore/add/remove/force
-
-# Don't remove braces around statements that span N newlines
-mod_full_brace_nl               = 0        # unsigned number
-
-# Whether to prevent removal of braces from 'if'/'for'/'while'/etc. blocks
-# which span multiple lines.
-#
-# Affects:
-#   mod_full_brace_for
-#   mod_full_brace_if
-#   mod_full_brace_if_chain
-#   mod_full_brace_if_chain_only
-#   mod_full_brace_while
-#   mod_full_brace_using
-#
-# Does not affect:
-#   mod_full_brace_do
-#   mod_full_brace_function
-mod_full_brace_nl_block_rem_mlcond = false    # true/false
-
-# Add or remove unnecessary parenthesis on 'return' statement.
-mod_paren_on_return             = ignore   # ignore/add/remove/force
-
-# (Pawn) Whether to change optional semicolons to real semicolons.
-mod_pawn_semicolon              = false    # true/false
-
-# Whether to fully parenthesize Boolean expressions in 'while' and 'if'
-# statement, as in 'if (a && b > c)' => 'if (a && (b > c))'.
-mod_full_paren_if_bool          = false    # true/false
-
-# Whether to remove superfluous semicolons.
-mod_remove_extra_semicolon      = false    # true/false
-
-# If a function body exceeds the specified number of newlines and doesn't have
-# a comment after the close brace, a comment will be added.
-mod_add_long_function_closebrace_comment = 0        # unsigned number
-
-# If a namespace body exceeds the specified number of newlines and doesn't
-# have a comment after the close brace, a comment will be added.
-mod_add_long_namespace_closebrace_comment = 0        # unsigned number
-
-# If a class body exceeds the specified number of newlines and doesn't have a
-# comment after the close brace, a comment will be added.
-mod_add_long_class_closebrace_comment = 0        # unsigned number
-
-# If a switch body exceeds the specified number of newlines and doesn't have a
-# comment after the close brace, a comment will be added.
-mod_add_long_switch_closebrace_comment = 0        # unsigned number
-
-# If an #ifdef body exceeds the specified number of newlines and doesn't have
-# a comment after the #endif, a comment will be added.
-mod_add_long_ifdef_endif_comment = 0        # unsigned number
-
-# If an #ifdef or #else body exceeds the specified number of newlines and
-# doesn't have a comment after the #else, a comment will be added.
-mod_add_long_ifdef_else_comment = 0        # unsigned number
-
-# Whether to sort consecutive single-line 'import' statements.
-mod_sort_import                 = false    # true/false
-
-# (C#) Whether to sort consecutive single-line 'using' statements.
-mod_sort_using                  = false    # true/false
-
-# Whether to sort consecutive single-line '#include' statements (C/C++) and
-# '#import' statements (Objective-C). Be aware that this has the potential to
-# break your code if your includes/imports have ordering dependencies.
-mod_sort_include                = false    # true/false
-
-# Whether to move a 'break' that appears after a fully braced 'case' before
-# the close brace, as in 'case X: { ... } break;' => 'case X: { ... break; }'.
-mod_move_case_break             = false    # true/false
-
-# Add or remove braces around a fully braced case statement. Will only remove
-# braces if there are no variable declarations in the block.
-mod_case_brace                  = ignore   # ignore/add/remove/force
-
-# Whether to remove a void 'return;' that appears as the last statement in a
-# function.
-mod_remove_empty_return         = false    # true/false
-
-# Add or remove the comma after the last value of an enumeration.
-mod_enum_last_comma             = ignore   # ignore/add/remove/force
-
-# (OC) Whether to organize the properties. If true, properties will be
-# rearranged according to the mod_sort_oc_property_*_weight factors.
-mod_sort_oc_properties          = false    # true/false
-
-# (OC) Weight of a class property modifier.
-mod_sort_oc_property_class_weight = 0        # number
-
-# (OC) Weight of 'atomic' and 'nonatomic'.
-mod_sort_oc_property_thread_safe_weight = 0        # number
-
-# (OC) Weight of 'readwrite' when organizing properties.
-mod_sort_oc_property_readwrite_weight = 0        # number
-
-# (OC) Weight of a reference type specifier ('retain', 'copy', 'assign',
-# 'weak', 'strong') when organizing properties.
-mod_sort_oc_property_reference_weight = 0        # number
-
-# (OC) Weight of getter type ('getter=') when organizing properties.
-mod_sort_oc_property_getter_weight = 0        # number
-
-# (OC) Weight of setter type ('setter=') when organizing properties.
-mod_sort_oc_property_setter_weight = 0        # number
-
-# (OC) Weight of nullability type ('nullable', 'nonnull', 'null_unspecified',
-# 'null_resettable') when organizing properties.
-mod_sort_oc_property_nullability_weight = 0        # number
-
-#
-# Preprocessor options
-#
-
-# Add or remove indentation of preprocessor directives inside #if blocks
-# at brace level 0 (file-level).
-pp_indent                       = ignore   # ignore/add/remove/force
-
-# Whether to indent #if/#else/#endif at the brace level. If false, these are
-# indented from column 1.
-pp_indent_at_level              = false    # true/false
-
-# Specifies the number of columns to indent preprocessors per level
-# at brace level 0 (file-level). If pp_indent_at_level=false, also specifies
-# the number of columns to indent preprocessors per level
-# at brace level > 0 (function-level).
-#
-# Default: 1
-pp_indent_count                 = 1        # unsigned number
-
-# Add or remove space after # based on pp_level of #if blocks.
-pp_space                        = ignore   # ignore/add/remove/force
-
-# Sets the number of spaces per level added with pp_space.
-pp_space_count                  = 0        # unsigned number
-
-# The indent for '#region' and '#endregion' in C# and '#pragma region' in
-# C/C++. Negative values decrease indent down to the first column.
-pp_indent_region                = 0        # number
-
-# Whether to indent the code between #region and #endregion.
-pp_region_indent_code           = false    # true/false
-
-# If pp_indent_at_level=true, sets the indent for #if, #else and #endif when
-# not at file-level. Negative values decrease indent down to the first column.
-#
-# =0: Indent preprocessors using output_tab_size
-# >0: Column at which all preprocessors will be indented
-pp_indent_if                    = 0        # number
-
-# Whether to indent the code between #if, #else and #endif.
-pp_if_indent_code               = false    # true/false
-
-# Whether to indent '#define' at the brace level. If false, these are
-# indented from column 1.
-pp_define_at_level              = false    # true/false
-
-# Whether to ignore the '#define' body while formatting.
-pp_ignore_define_body           = false    # true/false
-
-# Whether to indent case statements between #if, #else, and #endif.
-# Only applies to the indent of the preprocesser that the case statements
-# directly inside of.
-#
-# Default: true
-pp_indent_case                  = true     # true/false
-
-# Whether to indent whole function definitions between #if, #else, and #endif.
-# Only applies to the indent of the preprocesser that the function definition
-# is directly inside of.
-#
-# Default: true
-pp_indent_func_def              = true     # true/false
-
-# Whether to indent extern C blocks between #if, #else, and #endif.
-# Only applies to the indent of the preprocesser that the extern block is
-# directly inside of.
-#
-# Default: true
-pp_indent_extern                = true     # true/false
-
-# Whether to indent braces directly inside #if, #else, and #endif.
-# Only applies to the indent of the preprocesser that the braces are directly
-# inside of.
-#
-# Default: true
-pp_indent_brace                 = true     # true/false
-
-#
-# Sort includes options
-#
-
-# The regex for include category with priority 0.
-include_category_0              = ""         # string
-
-# The regex for include category with priority 1.
-include_category_1              = ""         # string
-
-# The regex for include category with priority 2.
-include_category_2              = ""         # string
-
-#
-# Use or Do not Use options
-#
-
-# true:  indent_func_call_param will be used (default)
-# false: indent_func_call_param will NOT be used
-#
-# Default: true
-use_indent_func_call_param      = true     # true/false
-
-# The value of the indentation for a continuation line is calculated
-# differently if the statement is:
-# - a declaration: your case with QString fileName ...
-# - an assignment: your case with pSettings = new QSettings( ...
-#
-# At the second case the indentation value might be used twice:
-# - at the assignment
-# - at the function call (if present)
-#
-# To prevent the double use of the indentation value, use this option with the
-# value 'true'.
-#
-# true:  indent_continue will be used only once
-# false: indent_continue will be used every time (default)
-use_indent_continue_only_once   = false    # true/false
-
-# The value might be used twice:
-# - at the assignment
-# - at the opening brace
-#
-# To prevent the double use of the indentation value, use this option with the
-# value 'true'.
-#
-# true:  indentation will be used only once
-# false: indentation will be used every time (default)
-indent_cpp_lambda_only_once     = false    # true/false
-
-# Whether to apply special formatting for Qt SIGNAL/SLOT macros. Essentially,
-# this tries to format these so that they match Qt's normalized form (i.e. the
-# result of QMetaObject::normalizedSignature), which can slightly improve the
-# performance of the QObject::connect call, rather than how they would
-# otherwise be formatted.
-#
-# See options_for_QT.cpp for details.
-#
-# Default: true
-use_options_overriding_for_qt_macros = true     # true/false
-
-#
-# Warn levels - 1: error, 2: warning (default), 3: note
-#
-
-# (C#) Warning is given if doing tab-to-\t replacement and we have found one
-# in a C# verbatim string literal.
-#
-# Default: 2
-warn_level_tabs_found_in_verbatim_string_literals = 2        # unsigned number
-
-# Meaning of the settings:
-#   Ignore - do not do any changes
-#   Add    - makes sure there is 1 or more space/brace/newline/etc
-#   Force  - makes sure there is exactly 1 space/brace/newline/etc,
-#            behaves like Add in some contexts
-#   Remove - removes space/brace/newline/etc
-#
-#
-# - Token(s) can be treated as specific type(s) with the 'set' option:
-#     `set tokenType tokenString [tokenString...]`
-#
-#     Example:
-#       `set BOOL __AND__ __OR__`
-#
-#     tokenTypes are defined in src/token_enum.h, use them without the
-#     'CT_' prefix: 'CT_BOOL' => 'BOOL'
-#
-#
-# - Token(s) can be treated as type(s) with the 'type' option.
-#     `type tokenString [tokenString...]`
-#
-#     Example:
-#       `type int c_uint_8 Rectangle`
-#
-#     This can also be achieved with `set TYPE int c_uint_8 Rectangle`
-#
-#
-# To embed whitespace in tokenStrings use the '\' escape character, or quote
-# the tokenStrings. These quotes are supported: "'`
-#
-#
-# - Support for the auto detection of languages through the file ending can be
-#   added using the 'file_ext' command.
-#     `file_ext langType langString [langString..]`
-#
-#     Example:
-#       `file_ext CPP .ch .cxx .cpp.in`
-#
-#     langTypes are defined in uncrusify_types.h in the lang_flag_e enum, use
-#     them without the 'LANG_' prefix: 'LANG_CPP' => 'CPP'
-#
-#
-# - Custom macro-based indentation can be set up using 'macro-open',
-#   'macro-else' and 'macro-close'.
-#     `(macro-open | macro-else | macro-close) tokenString`
-#
-#     Example:
-#       `macro-open  BEGIN_TEMPLATE_MESSAGE_MAP`
-#       `macro-open  BEGIN_MESSAGE_MAP`
-#       `macro-close END_MESSAGE_MAP`
-#
-#
-# option(s) with 'not default' value: 0
-#

From 8bef089e4d82e21c6cead417f1ca8459d9ec6428 Mon Sep 17 00:00:00 2001
From: "Christopher T. Lee" 
Date: Tue, 17 Aug 2021 15:35:26 -0700
Subject: [PATCH 2/3] chore: Bump version to v2.0.7

Signed-off-by: Christopher T. Lee 
---
 VERSION                |  2 +-
 docs/src/changelog.rst | 24 +++++++++++++++++++++++-
 2 files changed, 24 insertions(+), 2 deletions(-)

diff --git a/VERSION b/VERSION
index 47af7abd..f1547e6d 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-2.0.7-alpha
+2.0.7
diff --git a/docs/src/changelog.rst b/docs/src/changelog.rst
index a7bc5021..aea48619 100644
--- a/docs/src/changelog.rst
+++ b/docs/src/changelog.rst
@@ -2,6 +2,28 @@
 Changelog
 #########
 
+******************
+2.0.7 (08-17-2021)
+******************
+
+This is a maintenance release which improves compatibility with ``blender`` add-on development ideals along with some fixes.
+
+**New Features**:
+
+- CI and packaging using Github Actions
+- Make runtime errors more informative
+- Preliminary support for exporting to COMSOL
+- BlendGAMer support for Blender 2.93LTS
+
+**Fixes**:
+
+- Normal smooth no longer applies on the boundaries of a mesh
+- Resolves an issue where computing normals fails due to zero-area faces
+
+**Other**:
+
+- Update versions of dependencies (Eigen, Tetgen, Pybind11, Google-Test)
+
 ******************
 2.0.6 (02-25-2020)
 ******************
@@ -87,4 +109,4 @@ This is a maintenance release which improves compatibility with ``blender`` add-
 
 **New Features**:
 
-- Stable beta release! Compilation is supported on major operating systems (:pr:`16`).
\ No newline at end of file
+- Stable beta release! Compilation is supported on major operating systems (:pr:`16`).

From 299600632780533bc129f78f256bd9d53ad5e3a9 Mon Sep 17 00:00:00 2001
From: Joshua Sacher 
Date: Tue, 23 May 2023 18:06:54 -0500
Subject: [PATCH 3/3] adding  to pygamer

---
 pygamer/src/pygamer.cpp | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/pygamer/src/pygamer.cpp b/pygamer/src/pygamer.cpp
index 445b1ea1..0bdc1630 100644
--- a/pygamer/src/pygamer.cpp
+++ b/pygamer/src/pygamer.cpp
@@ -116,6 +116,21 @@ PYBIND11_MODULE(pygamer, pygamer) {
         )delim"
     );
 
+    pygamer.def("readPDB_distgrid", &readPDB_distgrid,
+        py::arg("filename"),
+        py::arg("radius") = 1.4,
+        R"delim(
+            Compute the Connolly surface using a distance grid based strategy
+
+            Args:
+                filename (:py:class:`str`): PDB file to read.
+                radius (:py:class:`float`): Radius in Angstroms of ball to roll over surface.
+            
+            Returns:
+                :py:class:`surfacemesh.SurfaceMesh`: Meshed object.
+        )delim"
+    );
+    
     pygamer.def("readPDB_gauss", &readPDB_gauss,
         py::arg("filename"),
         py::arg("blobbyness") = -0.2,