diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml new file mode 100644 index 00000000..9ec01df8 --- /dev/null +++ b/.github/workflows/cmake.yml @@ -0,0 +1,84 @@ +name: CMake + +on: [workflow_dispatch, push, pull_request] + + +concurrency: + group: FC-CI-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +env: + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + BUILD_TYPE: Release + +jobs: + build: + # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. + # You can convert this to a matrix build if you need cross-platform coverage. + # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix + runs-on: ubuntu-22.04 + env: + CPM_SOURCE_CACHE: ~/.cache/CPM + steps: + - uses: actions/checkout@v3 + - name: Install I-SIMPA dependencies + run: | + sudo sed -i 's/azure\.//' /etc/apt/sources.list + sudo apt-get update + sudo apt-get install -y --no-install-recommends \ + build-essential \ + libgtk-3-dev \ + freeglut3-dev \ + libxmu-dev \ + libxi-dev \ + libpng-dev \ + libjpeg-dev \ + libxxf86vm1 \ + libxxf86vm-dev \ + libxi-dev \ + libxrandr-dev \ + mesa-common-dev \ + mesa-utils \ + libgl1-mesa-dev \ + libglapi-mesa \ + byacc \ + gettext \ + cmake \ + python3-dev \ + swig \ + libboost-all-dev + - name: Cache CPM dependency packages + uses: actions/cache@v3 + with: + path: | + ${{ env.CPM_SOURCE_CACHE }} + key: ${{ runner.os }}-isimpa + + - name: Configure CMake + # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. + # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type + run: | + cmake -DCPM_SOURCE_CACHE=${{ env.CPM_SOURCE_CACHE }} -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} + + - name: Build + working-directory: ${{github.workspace}}/build + # Compile i-simpa + run: make + + - name: Install + working-directory: ${{github.workspace}}/build + # Copy stuff to bin subdirectory for "standalone" application + run: make install + + - name: Test + working-directory: ${{github.workspace}}/build + # Execute tests defined by the CMake configuration. + # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + run: make test + + - name: Archive production artifacts + uses: actions/upload-artifact@v3 + with: + name: isimpa-amd64.zip + path: ${{github.workspace}}/build/bin + diff --git a/.gitignore b/.gitignore index d74a7ee0..eee8348b 100644 --- a/.gitignore +++ b/.gitignore @@ -46,4 +46,5 @@ files.txt *.pot # cmake should convert .po files into .mo files if necessary *.mo -.vscode/ \ No newline at end of file +.vscode +.DS_Store diff --git a/CMakeLists.txt b/CMakeLists.txt index 9c991e91..3f9e41e3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,9 +1,13 @@ -CMAKE_MINIMUM_REQUIRED(VERSION 3.6.0) +# begin basic metadata +# minimum CMake version required for C++20 support, among other things +cmake_minimum_required(VERSION 3.15) # Maps to a solution file (isimpa.sln). The solution will # have all targets (exe, lib, dll) as projects (.vcproj) project (isimpa VERSION 1.3.4) +# a better way to load dependencies +include(cmake/CPM.cmake) # convert path of a .lib file into a .dll file # findboost does not store .dll locations @@ -49,13 +53,15 @@ IF (CMAKE_CXX_COMPILER_ID MATCHES "GNU") EXECUTE_PROCESS(COMMAND ${CMAKE_CXX_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION) IF (GCC_VERSION VERSION_GREATER 4.7 OR GCC_VERSION VERSION_EQUAL 4.7) SET(USE_CXX_11 TRUE) - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall --std=c++11 -O3 -fPIC" ) + set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -O3 -fPIC" ) ENDIF() ELSEIF (CMAKE_CXX_COMPILER_ID MATCHES "Clang") IF ((NOT APPLE AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "3.2") OR (APPLE AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "4.1")) SET(USE_CXX_11 TRUE) - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wno-error=c++11-narrowing") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall --std=c++11 -Wno-error=c++11-narrowing -O3 -fPIC" ) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -O3 -fPIC" ) ENDIF() ELSEIF (MSVC AND MSVC_VERSION GREATER 1600) SET(USE_CXX_11 TRUE) diff --git a/Docs/Building.md b/Docs/Building.md index 44ba3400..3399ddd0 100644 --- a/Docs/Building.md +++ b/Docs/Building.md @@ -10,10 +10,10 @@ #### Software & Dependencies requirements * CMake -* Boost 1.60 -* wxWidget 3.1.0 -* Swig -* Python 2.7 +* Boost 1.73 +* wxWidget 3.1.4 +* Swig 3.0.11 +* Python 3.8 :warning: This project uses OpenGL, check if your graphic driver is up to date ! @@ -21,22 +21,22 @@ ### On Windows with Microsoft Visual Studio (64bits) -Last (free) version in date : [Visual Studio Community 2015](https://www.visualstudio.com/) +Last (free) version in date : [Visual Studio Community 2019](https://www.visualstudio.com/) #### Libraries installation We recommend you to create a lib folder in `C:` and to use it when extracting extracting externals libraries source. * **Boost** - * [Download Boost 1.60](http://www.boost.org/users/history/) + * [Download Boost 1.73](http://www.boost.org/users/history/) * Extract the archive * To build Boost : from the Visual Studio shell (Menu > All Programs > Visual Studio Tools), in the Boost folder .\bootstrap.bat # build just what we need - .\b2. toolset=msvc-14.0 --with-thread --with-random --with-python --with-date_time --with-test --with-filesystem --with-regex --build-type=complete stage + .\b2. toolset=msvc-16.0 --with-thread --with-random --with-python --with-date_time --with-test --with-filesystem --with-regex --build-type=complete stage # or everything - .\b2. toolset=msvc-14.0 --build-type=complete stage + .\b2. toolset=msvc-16.0 --build-type=complete stage * Move the lib/ folder (inside stage/) to the Boost folder root * Create/check environment variable @@ -49,9 +49,9 @@ We recommend you to create a lib folder in `C:` and to use it when extracting ex * Extract the archive * Add to Path the Swig folder path * **wxWidgets** - * [Download the wxWidget 3.1.0 Windows Installer](http://www.wxwidgets.org/downloads/) + * [Download the wxWidget 3.1.4 Windows Installer](http://www.wxwidgets.org/downloads/) * The installer extract the source in a folder - * In the wxWidget folder, in build/msw, launch wx_vc14.sl + * In the wxWidget folder, in build/msw, launch wx_vc16.sl * Choose the "debug" mode and generate the solution * Choose the "release" mode and generate the solution again * Quit Visual Studio @@ -60,7 +60,7 @@ We recommend you to create a lib folder in `C:` and to use it when extracting ex WXWIN path\to\wxwidget * **Python** - * [Download Python 2.7](https://www.python.org/downloads/) + * [Download Python 3.8](https://www.python.org/downloads/) * Install it (the installer has an option that can do the two next steps for you) * Add to Path the Python installation folder path * Create/check environment variable @@ -89,7 +89,7 @@ In order to build under linux you have to download/install the following librari - gcc-4.8 - g++-4.8 -- python 2.7 +- python 3.8 - freeglut3-dev - libxmu-dev - libxi-dev @@ -111,13 +111,13 @@ Swig from here: https://github.com/swig/swig/archive/rel-3.0.10.tar.gz Boost from here: -https://sourceforge.net/projects/boost/files/boost/1.61.0/boost_1_61_0.tar.bz2 +https://sourceforge.net/projects/boost/files/boost/1.73.0/boost_1_73_0.tar.bz2/download wxWidgets from here: -https://github.com/wxWidgets/wxWidgets/releases/download/v3.1.0/wxWidgets-3.1.0.tar.bz2 +https://github.com/wxWidgets/wxWidgets/releases/download/v3.1.4/wxWidgets-3.1.4.tar.bz2 -Cmake from here -https://cmake.org/files/v3.6/cmake-3.6.1-Linux-x86_64.tar.gz +Cmake from here (or from apt-get) +https://cmake.org/files/LatestRelease/cmake-3.22.0.tar.gz #### Building instructions diff --git a/Docs/code_TCR.rst b/Docs/code_TCR.rst index f4a22552..4b4b0702 100644 --- a/Docs/code_TCR.rst +++ b/Docs/code_TCR.rst @@ -3,7 +3,7 @@ Presentation **TCR is a numerical code based on the Classical Theory or Reverberation.** -- `Visit the offical I-Simpa website`_ for more information about SPPS code. +- `Visit the offical I-Simpa website`_ for more information about TCR code. - See the main `characteristics of the code`_ that is embedded in I-Simpa. diff --git a/Docs/index.rst b/Docs/index.rst index ddb4fba2..1b116007 100644 --- a/Docs/index.rst +++ b/Docs/index.rst @@ -36,15 +36,16 @@ the TCR numerical codes (embedded within the I-Simpa software). `instructions`_ .. note:: - - Some illustrations may referred to previous versions of I-Simpa. - - Depending of your OS, screenchots may differs. - - Some texts and translations in I-Simpa may have changed. - - If the present documentation is the 'Offline documentation' you may refer to the online version at http://i-simpa-wiki.readthedocs.io/fr/latest/ for an up-to-date documentation. - - If you observe some mistakes or errors, please `write an Issue on GitHub`_ - - You can also `contribute to the documentation`_. - - The official documentation is available in English only. - -.. _visit the offical I-Simpa website: https://i-simpa.univ-gustave-eiffel.fr/ + - This user guide is currently not complete. Additions are underway. + - Some illustrations may referred to previous versions of I-Simpa. + - Depending of your OS, screenchots may differs. + - Some texts and translations in I-Simpa may have changed. + - If the present documentation is the 'Offline documentation' you may refer to the online version at http://i-simpa-wiki.readthedocs.io/en/latest/ for an up-to-date documentation. + - If you observe some mistakes or errors, please `write an Issue on GitHub`_ + - You can also `contribute to the documentation`_. + - The official documentation is available in English only. + +.. _visit the offical I-Simpa website: http://i-simpa.ifsttar.fr .. _instructions: https://github.com/Ifsttar/I-Simpa/wiki .. _contribute to the documentation: https://github.com/Ifsttar/I-Simpa/wiki/Write-documentation .. _write an Issue on GitHub: https://github.com/Ifsttar/I-Simpa/issues @@ -111,7 +112,6 @@ the TCR numerical codes (embedded within the I-Simpa software). :maxdepth: 2 :caption: Appendices - errors_messages I_Simpa_standard room_acoustics_parameters glossary diff --git a/Docs/presentation.rst b/Docs/presentation.rst index cc027394..3fe14bf8 100644 --- a/Docs/presentation.rst +++ b/Docs/presentation.rst @@ -20,14 +20,14 @@ Windows properties: - User can **rearrange and resize** all components in the computer screen by selecting and moving the corresponding component. - You can come back to the **default view** by selecting the option 'Reinitialize interface' in the 'Windows' menu. -.. _'File': Menu_File.html -.. _'Edition': Menu_Edition.html -.. _'Simulation': Menu_Simulation.html -.. _'View': Menu_View.html -.. _'Windows': Menu_Windows.html -.. _'Help': Menu_Help.html -.. _'Toolbars': Toolbars.html -.. _'Project' window: Project_window.html -.. _'Properties' window: Properties_window.html -.. _'Console' window: Console_window.html -.. _'Main' window: Main_window.html +.. _'File': menu_file.html +.. _'Edition': menu_edition.html +.. _'Simulation': menu_simulation.html +.. _'View': menu_view.html +.. _'Windows': menu_windows.html +.. _'Help': menu_help.html +.. _'Toolbars': toolbars.html +.. _'Project' window: project_window.html +.. _'Properties' window: properties_window.html +.. _'Console' window: console_window.html +.. _'Main' window: main_window.html diff --git a/Docs/requirements.txt b/Docs/requirements.txt index 7af3d88e..3c490d8b 100644 --- a/Docs/requirements.txt +++ b/Docs/requirements.txt @@ -1,2 +1 @@ sphinxcontrib-bibtex==1.0.0 -docutils==0.16 diff --git a/appveyor.yml b/appveyor.yml index 6dd862f0..1075ce23 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -12,21 +12,15 @@ environment: BOOST_LIBRARYDIR: C:\Libraries\boost_1_73_0\lib64-msvc-14.2 PYTHON_LIBRARY: C:\Python38-x64\libs\python38.lib PYTHON_INCLUDE_DIR: C:\Python38-x64\include - WXWIN: C:\wxWidgets-3.1.4\ - wxWidgets_ROOT_DIR: C:\wxWidgets-3.1.4\ - wxWidgets_INCLUDE_DIRS: C:\wxWidgets-3.1.4\include - wxWidgets_LIBRARIES: C:\wxWidgets-3.1.4\lib\vc14x_x64_dll - SWIG_DIR: C:\Libraries\swigwin-3.0.11 - SWIG_EXECUTABLE: C:\Libraries\swigwin-3.0.11\swig.exe + WXWIN: C:\wxWidgets-3.1.5\ + wxWidgets_ROOT_DIR: C:\wxWidgets-3.1.5\ + wxWidgets_INCLUDE_DIRS: C:\wxWidgets-3.1.5\include + wxWidgets_LIBRARIES: C:\wxWidgets-3.1.5\lib\vc14x_x64_dll + SWIG_DIR: C:\ProgramData\chocolatey\lib\swig\tools + #SWIG_EXECUTABLE: C:\Libraries\swigwin-3.0.11\swig.exe PYTHON_EXECUTABLE: C:\Python38-x64\python.exe CTEST_OUTPUT_ON_FAILURE: 1 -# branches to build -branches: - # whitelist - only: - - master - # Operating system (build VM template) os: Visual Studio 2019 @@ -50,18 +44,19 @@ before_build: # Copy x64 dll from system32 to python27 x64 #- cmd: copy c:\windows\system32\PYTHON27.DLL C:\Python27-x64\python27.dll - echo Download wxWidgets lib - - ps: Start-FileDownload 'https://github.com/wxWidgets/wxWidgets/releases/download/v3.1.4/wxWidgets-3.1.4.7z' - - ps: Start-FileDownload 'https://github.com/wxWidgets/wxWidgets/releases/download/v3.1.4/wxMSW-3.1.4_vc14x_x64_Dev.7z' - - cmd: 7z x -aoa wxWidgets-3.1.4.7z -oC:\wxWidgets-3.1.4 - - cmd: 7z x -aoa wxMSW-3.1.4_vc14x_x64_Dev.7z -oC:\wxWidgets-3.1.4 + - ps: Start-FileDownload 'https://github.com/wxWidgets/wxWidgets/releases/download/v3.1.5/wxWidgets-3.1.5.7z' + - ps: Start-FileDownload 'https://github.com/wxWidgets/wxWidgets/releases/download/v3.1.5/wxMSW-3.1.5_vc14x_x64_Dev.7z' + - cmd: 7z x -aoa wxWidgets-3.1.5.7z -oC:\wxWidgets-3.1.5 + - cmd: 7z x -aoa wxMSW-3.1.5_vc14x_x64_Dev.7z -oC:\wxWidgets-3.1.5 - cmd: dir %wxWidgets_LIBRARIES% # Download Release wxWidgets DLL's - - ps: Start-FileDownload 'https://github.com/wxWidgets/wxWidgets/releases/download/v3.1.4/wxMSW-3.1.4_vc14x_x64_ReleaseDLL.7z' -Timeout 15000 - - cmd: 7z x -aoa wxMSW-3.1.4_vc14x_x64_ReleaseDLL.7z -oC:\wxWidgets-3.1.4 + - ps: Start-FileDownload 'https://github.com/wxWidgets/wxWidgets/releases/download/v3.1.5/wxMSW-3.1.5_vc14x_x64_ReleaseDLL.7z' -Timeout 15000 + - cmd: 7z x -aoa wxMSW-3.1.5_vc14x_x64_ReleaseDLL.7z -oC:\wxWidgets-3.1.5 # install dependencies: - choco install poedit nsis 7zip.install - - ps: Start-FileDownload 'https://github.com/nicolas-f/build_resources/raw/master/swigwin-3.0.11.zip' -FileName 'c:\Libraries\swigwin-3.0.11.zip' -Timeout 15000 - - ps: 7z x -aoa c:\Libraries\swigwin-3.0.11.zip -oC:\Libraries -r -y + #- ps: Start-FileDownload 'https://github.com/nicolas-f/build_resources/raw/master/swigwin-3.0.11.zip' -FileName 'c:\Libraries\swigwin-3.0.11.zip' -Timeout 15000 + #- ps: 7z x c:\Libraries\swigwin-3.0.11.zip -oC:\Libraries -r -y + - choco install swig - cmd: SET PATH=%PATH%;%WXWIN%;%SWIG_DIR%;%wxWidgets_LIB_DIR%;C:\Program Files (x86)\Poedit\Gettexttools\bin;%BOOST_LIBRARYDIR%; - ps: swig.exe -version - echo Running cmake... diff --git a/ci/travis/linux/after_script.sh b/ci/travis/linux/after_script.sh deleted file mode 100755 index 139597f9..00000000 --- a/ci/travis/linux/after_script.sh +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/ci/travis/linux/before_install.sh b/ci/travis/linux/before_install.sh deleted file mode 100755 index 5eb78e50..00000000 --- a/ci/travis/linux/before_install.sh +++ /dev/null @@ -1,55 +0,0 @@ -export DEBIAN_FRONTEND=noninteractive - -# -# Boost install - -if [ -d $HOME/boost-install/boost ] ; then - echo "Boost already built (and in travis cache)" -else - cd - wget https://sourceforge.net/projects/boost/files/boost/1.73.0/boost_1_73_0.tar.bz2 - tar -xjf boost_1_73_0.tar.bz2 --strip-components=1 -C $HOME/boost-install - ls -l $HOME/boost-install - cd $HOME/boost-install && echo "using python : 3.8 : /usr/bin/python3 : /usr/include/python3.8 : /usr/lib ;" > tools/build/src/user-config.jam - cd $HOME/boost-install && ./bootstrap.sh link=static variant=release address-model=64 cxxflags="-std=c++11 -fPIC" boost.locale.icu=off --with-libraries=filesystem,system,test,regex,python,random,thread,timer,date_time --prefix=$HOME/boost-install && ./b2 install -fi - -export BOOST_LIBRARYDIR=$HOME/boost-install/lib/ -export BOOST_INCLUDEDIR=$HOME/boost-install/ -export BOOST_ROOT=$HOME/boost-install/ - -# -# Swig install -if [ -f $HOME/swig-install/bin/swig ] ; then - echo "Swig already built (and in travis cache)" -else - cd - wget https://github.com/swig/swig/archive/rel-3.0.10.tar.gz - tar zxvf rel-3.0.10.tar.gz - mkdir $HOME/swig-install - cd $HOME/swig-rel-3.0.10 && ./autogen.sh && ./configure --prefix=$HOME/swig-install && make && make install -fi - -# -# WXWidget install - -if [ -d $HOME/wxWidgets-install/include ] ; then - echo "wxWidget already built (and in travis cache)" -else - cd - wget https://github.com/wxWidgets/wxWidgets/releases/download/v3.1.4/wxWidgets-3.1.4.tar.bz2 - tar -xjf wxWidgets-3.1.4.tar.bz2 - mkdir $HOME/wxWidgets-install - cd $HOME/wxWidgets-3.1.4 && ./configure --prefix=$HOME/wxWidgets-install --disable-shared && make && make install -fi - -# check wxWidget install -export PATH=$HOME/wxWidgets-install/bin/:$PATH -echo "wxWidget version : " -wx-config --version - -# Download CMake -cd -wget --no-check-certificate https://cmake.org/files/v3.17/cmake-3.17.2-Linux-x86_64.tar.gz -mkdir $HOME/cmake-install -tar zxvf cmake-3.17.2-Linux-x86_64.tar.gz -C $HOME/cmake-install --strip 1 diff --git a/ci/travis/linux/before_script.sh b/ci/travis/linux/before_script.sh deleted file mode 100755 index c0a8bc05..00000000 --- a/ci/travis/linux/before_script.sh +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env bash -# Update translation key files - -find src/isimpa -type f -regex '.*/.*\.\(c\|cpp\|h\|hpp\)$' > files.txt -find src/spps -type f -regex '.*/.*\.\(c\|cpp\|h\|hpp\)$' >> files.txt -find src/theorie_classique -type f -regex '.*/.*\.\(c\|cpp\|h\|hpp\)$' >> files.txt -find src/lib_interface -type f -regex '.*/.*\.\(c\|cpp\|h\|hpp\)$' >> files.txt - -mkdir -p src/isimpa/lang/ -xgettext --keyword=_ --keyword=wxTRANSLATE --from-code=UTF-8 -s --no-wrap -no-hash --escape -ffiles.txt -osrc/isimpa/lang/internat.pot -msginit --no-translator --input src/isimpa/lang/internat.pot -o src/isimpa/lang/internat.pot -l en.UTF-8 - -find currentRelease/SystemScript/job_tool -type f -name "*.py" > files.txt -xgettext --keyword=_ --from-code=UTF-8 -s --no-wrap -no-hash --escape -ffiles.txt -ocurrentRelease/SystemScript/job_tool/internat.pot -msginit --no-translator --input currentRelease/SystemScript/job_tool/internat.pot -o currentRelease/SystemScript/job_tool/internat.pot -l en.UTF-8 - -find currentRelease/SystemScript/moveto_vertex -type f -name "*.py" > files.txt -xgettext --keyword=_ --from-code=UTF-8 -s --no-wrap -no-hash --escape -ffiles.txt -ocurrentRelease/SystemScript/moveto_vertex/internat.pot -msginit --no-translator --input currentRelease/SystemScript/moveto_vertex/internat.pot -o currentRelease/SystemScript/moveto_vertex/internat.pot -l en.UTF-8 - -find currentRelease/SystemScript/preceiv_sourceTracker -type f -name "*.py" > files.txt -xgettext --keyword=_ --from-code=UTF-8 -s --no-wrap -no-hash --escape -ffiles.txt -ocurrentRelease/SystemScript/preceiv_sourceTracker/internat.pot -msginit --no-translator --input currentRelease/SystemScript/preceiv_sourceTracker/internat.pot -o currentRelease/SystemScript/preceiv_sourceTracker/internat.pot -l en.UTF-8 - -find currentRelease/SystemScript/recp_tool -type f -name "*.py" > files.txt -xgettext --keyword=_ --from-code=UTF-8 -s --no-wrap -no-hash --escape -ffiles.txt -ocurrentRelease/SystemScript/recp_tool/internat.pot -msginit --no-translator --input currentRelease/SystemScript/recp_tool/internat.pot -o currentRelease/SystemScript/recp_tool/internat.pot -l en.UTF-8 - -find currentRelease/SystemScript/source_tools -type f -name "*.py" > files.txt -xgettext --keyword=_ --from-code=UTF-8 -s --no-wrap -no-hash --escape -ffiles.txt -ocurrentRelease/SystemScript/source_tools/internat.pot -msginit --no-translator --input currentRelease/SystemScript/source_tools/internat.pot -o currentRelease/SystemScript/source_tools/internat.pot -l en.UTF-8 - -# Disabled -#find currentRelease/SystemScript/SppsReportSample -type f -name "*.py" > files.txt -#xgettext --keyword=_ --from-code=UTF-8 -s --no-wrap -no-hash --escape -ffiles.txt -ocurrentRelease/SystemScript/SppsReportSample/internat.pot -#msginit --no-translator --input currentRelease/SystemScript/SppsReportSample/internat.pot -o currentRelease/SystemScript/SppsReportSample/internat.pot -l en.UTF-8 - -# Now replace all ASCII charset by UTF-8 in pot files -find . -type f -name "*.pot" -exec sed -i 's/charset=ASCII/charset=UTF-8/g' {} + - -# Push translation keys to transifex -# Update setup-tools -pip install --user setuptools --upgrade -pip install --user urllib3 --force-reinstall --upgrade -# install transifex client (version 0.13 have an issue) -pip install --user transifex-client==0.12.5 -if [ -z "$TRANSIFEXPWD" ]; then - echo "Not in master branch do not push transifex keys" -else - # Write transifex config file - printf "[https://www.transifex.com]\nhostname = https://www.transifex.com\npassword = $TRANSIFEXPWD\ntoken =\nusername = travis_lae\n" > ~/.transifexrc - # push transifex keys - tx push -s -fi diff --git a/ci/travis/linux/install.sh b/ci/travis/linux/install.sh deleted file mode 100755 index f6ff675a..00000000 --- a/ci/travis/linux/install.sh +++ /dev/null @@ -1,18 +0,0 @@ -mkdir build -cd build - -CLANG_WARNINGS="" - -# wxWidget path (only in script scope) -export PATH=$HOME/wxWidgets-install/bin/:$HOME/swig-install/bin/:$HOME/cmake-install/bin/:$PATH -export BOOST_LIBRARYDIR=$HOME/boost-install/lib/ -export BOOST_INCLUDEDIR=$HOME/boost-install/ -export BOOST_ROOT=$HOME/boost-install/ - -cmake --version -${CC} --version -${CXX} --version - -ls $HOME/boost-install/lib/ - -cmake .. diff --git a/ci/travis/linux/script.sh b/ci/travis/linux/script.sh deleted file mode 100755 index 5210e801..00000000 --- a/ci/travis/linux/script.sh +++ /dev/null @@ -1,6 +0,0 @@ -cd build -pwd -make VERBOSE=1 -make install -export LD_LIBRARY_PATH=$HOME/boost-install/lib:$LD_LIBRARY_PATH -CTEST_OUTPUT_ON_FAILURE=1 make test diff --git a/cmake/CPM.cmake b/cmake/CPM.cmake new file mode 100644 index 00000000..e9674a77 --- /dev/null +++ b/cmake/CPM.cmake @@ -0,0 +1,32 @@ +set(CPM_DOWNLOAD_VERSION 0.37.0) + +if(CPM_SOURCE_CACHE) + set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") +elseif(DEFINED ENV{CPM_SOURCE_CACHE}) + set(CPM_DOWNLOAD_LOCATION "$ENV{CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") +else() + set(CPM_DOWNLOAD_LOCATION "${CMAKE_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake") +endif() + +# Expand relative path. This is important if the provided path contains a tilde (~) +get_filename_component(CPM_DOWNLOAD_LOCATION ${CPM_DOWNLOAD_LOCATION} ABSOLUTE) + +function(download_cpm) + message(STATUS "Downloading CPM.cmake to ${CPM_DOWNLOAD_LOCATION}") + file(DOWNLOAD + https://github.com/cpm-cmake/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake + ${CPM_DOWNLOAD_LOCATION} + ) +endfunction() + +if(NOT (EXISTS ${CPM_DOWNLOAD_LOCATION})) + download_cpm() +else() + # resume download if it previously failed + file(READ ${CPM_DOWNLOAD_LOCATION} check) + if("${check}" STREQUAL "") + download_cpm() + endif() +endif() + +include(${CPM_DOWNLOAD_LOCATION}) diff --git a/currentRelease/ExperimentalCore/md_octave/md_octave.py b/currentRelease/ExperimentalCore/md_octave/md_octave.py index 68f59967..7f0ea035 100644 --- a/currentRelease/ExperimentalCore/md_octave/md_octave.py +++ b/currentRelease/ExperimentalCore/md_octave/md_octave.py @@ -77,16 +77,19 @@ def process_face(tetraface, modelImport, sharedVertices, fileOut): ## # \~english -# This method run the Classical physics core for each source independently and return the results +# This method runs the Classical physics core for each source independently and returns the results # @return Dict object with source id in key and sound_level_layer.SoundLevelLayer instance in dict values def runTC(xmlPathTc, coreconf): """ - This method run the Classical physics core for each source independently and return the results + This method runs the Classical physics core for each source independently and returns the results """ # TODO option to disable direct field computation #if not coreconf.const["ajouter_son_direct"]: # return {} + if platform.system() == 'Windows': tcpath = os.path.normpath(os.path.join(os.getcwd(), "core", "classical_theory", "classicalTheory.exe")) + else: + tcpath = os.path.normpath(os.path.join(os.getcwd(), "core", "classical_theory", "classicalTheory")) if not os.path.exists(tcpath): print("Cant find classical theory program!\n %s" % tcpath, file=sys.stderr) diff --git a/currentRelease/doc/documentation/Offline_documentation/html/.buildinfo b/currentRelease/doc/documentation/Offline_documentation/html/.buildinfo index 7bcdc0ed..140ec18e 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/.buildinfo +++ b/currentRelease/doc/documentation/Offline_documentation/html/.buildinfo @@ -1,4 +1,4 @@ # Sphinx build info version 1 # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. -config: d0d1c3ed07784eb067a6789eb9b0a19d +config: d2d1b65af3cb8bba0b4ef7502c8b4ab6 tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/currentRelease/doc/documentation/Offline_documentation/html/I_Simpa_overview.html b/currentRelease/doc/documentation/Offline_documentation/html/I_Simpa_overview.html index e9873348..4c640551 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/I_Simpa_overview.html +++ b/currentRelease/doc/documentation/Offline_documentation/html/I_Simpa_overview.html @@ -401,9 +401,9 @@

Other features

- © Copyright 2022, Université Gustave Eiffel + © Copyright 2020, Université Gustave Eiffel - Last updated on Jan 12, 2022. + Last updated on Jan 04, 2022.

diff --git a/currentRelease/doc/documentation/Offline_documentation/html/I_Simpa_standard.html b/currentRelease/doc/documentation/Offline_documentation/html/I_Simpa_standard.html index 5b011075..c4e2b7f9 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/I_Simpa_standard.html +++ b/currentRelease/doc/documentation/Offline_documentation/html/I_Simpa_standard.html @@ -291,9 +291,9 @@

Other

- © Copyright 2022, Université Gustave Eiffel + © Copyright 2020, Université Gustave Eiffel - Last updated on Jan 12, 2022. + Last updated on Jan 04, 2022.

diff --git a/currentRelease/doc/documentation/Offline_documentation/html/_sources/code_TCR.rst.txt b/currentRelease/doc/documentation/Offline_documentation/html/_sources/code_TCR.rst.txt index f4a22552..4b4b0702 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/_sources/code_TCR.rst.txt +++ b/currentRelease/doc/documentation/Offline_documentation/html/_sources/code_TCR.rst.txt @@ -3,7 +3,7 @@ Presentation **TCR is a numerical code based on the Classical Theory or Reverberation.** -- `Visit the offical I-Simpa website`_ for more information about SPPS code. +- `Visit the offical I-Simpa website`_ for more information about TCR code. - See the main `characteristics of the code`_ that is embedded in I-Simpa. diff --git a/currentRelease/doc/documentation/Offline_documentation/html/_sources/index.rst.txt b/currentRelease/doc/documentation/Offline_documentation/html/_sources/index.rst.txt index ddb4fba2..1b116007 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/_sources/index.rst.txt +++ b/currentRelease/doc/documentation/Offline_documentation/html/_sources/index.rst.txt @@ -36,15 +36,16 @@ the TCR numerical codes (embedded within the I-Simpa software). `instructions`_ .. note:: - - Some illustrations may referred to previous versions of I-Simpa. - - Depending of your OS, screenchots may differs. - - Some texts and translations in I-Simpa may have changed. - - If the present documentation is the 'Offline documentation' you may refer to the online version at http://i-simpa-wiki.readthedocs.io/fr/latest/ for an up-to-date documentation. - - If you observe some mistakes or errors, please `write an Issue on GitHub`_ - - You can also `contribute to the documentation`_. - - The official documentation is available in English only. - -.. _visit the offical I-Simpa website: https://i-simpa.univ-gustave-eiffel.fr/ + - This user guide is currently not complete. Additions are underway. + - Some illustrations may referred to previous versions of I-Simpa. + - Depending of your OS, screenchots may differs. + - Some texts and translations in I-Simpa may have changed. + - If the present documentation is the 'Offline documentation' you may refer to the online version at http://i-simpa-wiki.readthedocs.io/en/latest/ for an up-to-date documentation. + - If you observe some mistakes or errors, please `write an Issue on GitHub`_ + - You can also `contribute to the documentation`_. + - The official documentation is available in English only. + +.. _visit the offical I-Simpa website: http://i-simpa.ifsttar.fr .. _instructions: https://github.com/Ifsttar/I-Simpa/wiki .. _contribute to the documentation: https://github.com/Ifsttar/I-Simpa/wiki/Write-documentation .. _write an Issue on GitHub: https://github.com/Ifsttar/I-Simpa/issues @@ -111,7 +112,6 @@ the TCR numerical codes (embedded within the I-Simpa software). :maxdepth: 2 :caption: Appendices - errors_messages I_Simpa_standard room_acoustics_parameters glossary diff --git a/currentRelease/doc/documentation/Offline_documentation/html/_sources/presentation.rst.txt b/currentRelease/doc/documentation/Offline_documentation/html/_sources/presentation.rst.txt index cc027394..3fe14bf8 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/_sources/presentation.rst.txt +++ b/currentRelease/doc/documentation/Offline_documentation/html/_sources/presentation.rst.txt @@ -20,14 +20,14 @@ Windows properties: - User can **rearrange and resize** all components in the computer screen by selecting and moving the corresponding component. - You can come back to the **default view** by selecting the option 'Reinitialize interface' in the 'Windows' menu. -.. _'File': Menu_File.html -.. _'Edition': Menu_Edition.html -.. _'Simulation': Menu_Simulation.html -.. _'View': Menu_View.html -.. _'Windows': Menu_Windows.html -.. _'Help': Menu_Help.html -.. _'Toolbars': Toolbars.html -.. _'Project' window: Project_window.html -.. _'Properties' window: Properties_window.html -.. _'Console' window: Console_window.html -.. _'Main' window: Main_window.html +.. _'File': menu_file.html +.. _'Edition': menu_edition.html +.. _'Simulation': menu_simulation.html +.. _'View': menu_view.html +.. _'Windows': menu_windows.html +.. _'Help': menu_help.html +.. _'Toolbars': toolbars.html +.. _'Project' window: project_window.html +.. _'Properties' window: properties_window.html +.. _'Console' window: console_window.html +.. _'Main' window: main_window.html diff --git a/currentRelease/doc/documentation/Offline_documentation/html/code_SPPS.html b/currentRelease/doc/documentation/Offline_documentation/html/code_SPPS.html index 4e999b4c..940be1d3 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/code_SPPS.html +++ b/currentRelease/doc/documentation/Offline_documentation/html/code_SPPS.html @@ -207,7 +207,7 @@

Presentation

SPPS is a sound particles-tracing code, based on geometrical, energetical and probabilistic approaches.

The simulation principle of the SPPS code (from French “Simulation de la Propagation @@ -253,9 +253,9 @@

Presentation

- © Copyright 2022, Université Gustave Eiffel + © Copyright 2020, Université Gustave Eiffel - Last updated on Jan 12, 2022. + Last updated on Jan 04, 2022.

diff --git a/currentRelease/doc/documentation/Offline_documentation/html/code_SPPS_Validation.html b/currentRelease/doc/documentation/Offline_documentation/html/code_SPPS_Validation.html index ce697831..6c5c54a0 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/code_SPPS_Validation.html +++ b/currentRelease/doc/documentation/Offline_documentation/html/code_SPPS_Validation.html @@ -236,9 +236,9 @@

Verification of the SPPS code

- © Copyright 2022, Université Gustave Eiffel + © Copyright 2020, Université Gustave Eiffel - Last updated on Jan 12, 2022. + Last updated on Jan 04, 2022.

diff --git a/currentRelease/doc/documentation/Offline_documentation/html/code_SPPS_modelling.html b/currentRelease/doc/documentation/Offline_documentation/html/code_SPPS_modelling.html index 6e68c24b..0bb6d44f 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/code_SPPS_modelling.html +++ b/currentRelease/doc/documentation/Offline_documentation/html/code_SPPS_modelling.html @@ -1025,9 +1025,9 @@

Calculation of the overall sound pressure level in the model

- © Copyright 2022, Université Gustave Eiffel + © Copyright 2020, Université Gustave Eiffel - Last updated on Jan 12, 2022. + Last updated on Jan 04, 2022.

diff --git a/currentRelease/doc/documentation/Offline_documentation/html/code_SPPS_principle.html b/currentRelease/doc/documentation/Offline_documentation/html/code_SPPS_principle.html index 966bfe7a..27c74f57 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/code_SPPS_principle.html +++ b/currentRelease/doc/documentation/Offline_documentation/html/code_SPPS_principle.html @@ -281,9 +281,9 @@

Temporal meshing

- © Copyright 2022, Université Gustave Eiffel + © Copyright 2020, Université Gustave Eiffel - Last updated on Jan 12, 2022. + Last updated on Jan 04, 2022.

diff --git a/currentRelease/doc/documentation/Offline_documentation/html/code_TCR.html b/currentRelease/doc/documentation/Offline_documentation/html/code_TCR.html index cb3910f8..2d580281 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/code_TCR.html +++ b/currentRelease/doc/documentation/Offline_documentation/html/code_TCR.html @@ -207,7 +207,7 @@

Presentation

TCR is a numerical code based on the Classical Theory or Reverberation.

The simulation code TCR (from French “Théorie Classique de la Réverbération”) is a numerical application of the Classical @@ -236,9 +236,9 @@

Presentation

- © Copyright 2022, Université Gustave Eiffel + © Copyright 2020, Université Gustave Eiffel - Last updated on Jan 12, 2022. + Last updated on Jan 04, 2022.

diff --git a/currentRelease/doc/documentation/Offline_documentation/html/code_characteristics.html b/currentRelease/doc/documentation/Offline_documentation/html/code_characteristics.html index ae47dffa..fc7db6dc 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/code_characteristics.html +++ b/currentRelease/doc/documentation/Offline_documentation/html/code_characteristics.html @@ -436,9 +436,9 @@

Calculation code characteristics

- © Copyright 2022, Université Gustave Eiffel + © Copyright 2020, Université Gustave Eiffel - Last updated on Jan 12, 2022. + Last updated on Jan 04, 2022.

diff --git a/currentRelease/doc/documentation/Offline_documentation/html/code_configuration_SPPS.html b/currentRelease/doc/documentation/Offline_documentation/html/code_configuration_SPPS.html index 42247f8b..07fb41ba 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/code_configuration_SPPS.html +++ b/currentRelease/doc/documentation/Offline_documentation/html/code_configuration_SPPS.html @@ -788,9 +788,9 @@

Sound level (surface receiver)

- © Copyright 2022, Université Gustave Eiffel + © Copyright 2020, Université Gustave Eiffel - Last updated on Jan 12, 2022. + Last updated on Jan 04, 2022.

diff --git a/currentRelease/doc/documentation/Offline_documentation/html/code_configuration_TCR.html b/currentRelease/doc/documentation/Offline_documentation/html/code_configuration_TCR.html index 5c0c07f9..5dcbfb06 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/code_configuration_TCR.html +++ b/currentRelease/doc/documentation/Offline_documentation/html/code_configuration_TCR.html @@ -322,9 +322,9 @@

TCR Calculation parameters

- © Copyright 2022, Université Gustave Eiffel + © Copyright 2020, Université Gustave Eiffel - Last updated on Jan 12, 2022. + Last updated on Jan 04, 2022.

diff --git a/currentRelease/doc/documentation/Offline_documentation/html/code_configuration_frequency_bands.html b/currentRelease/doc/documentation/Offline_documentation/html/code_configuration_frequency_bands.html index 38285de8..4f44e0ad 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/code_configuration_frequency_bands.html +++ b/currentRelease/doc/documentation/Offline_documentation/html/code_configuration_frequency_bands.html @@ -271,9 +271,9 @@

Frequency bands

- © Copyright 2022, Université Gustave Eiffel + © Copyright 2020, Université Gustave Eiffel - Last updated on Jan 12, 2022. + Last updated on Jan 04, 2022.

diff --git a/currentRelease/doc/documentation/Offline_documentation/html/code_configuration_job.html b/currentRelease/doc/documentation/Offline_documentation/html/code_configuration_job.html index af1d3a07..b4b29fe4 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/code_configuration_job.html +++ b/currentRelease/doc/documentation/Offline_documentation/html/code_configuration_job.html @@ -243,9 +243,9 @@

Job list

- © Copyright 2022, Université Gustave Eiffel + © Copyright 2020, Université Gustave Eiffel - Last updated on Jan 12, 2022. + Last updated on Jan 04, 2022.

diff --git a/currentRelease/doc/documentation/Offline_documentation/html/code_configuration_meshing.html b/currentRelease/doc/documentation/Offline_documentation/html/code_configuration_meshing.html index aba983eb..f9045585 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/code_configuration_meshing.html +++ b/currentRelease/doc/documentation/Offline_documentation/html/code_configuration_meshing.html @@ -278,9 +278,9 @@

Meshing

- © Copyright 2022, Université Gustave Eiffel + © Copyright 2020, Université Gustave Eiffel - Last updated on Jan 12, 2022. + Last updated on Jan 04, 2022.

diff --git a/currentRelease/doc/documentation/Offline_documentation/html/code_configuration_run.html b/currentRelease/doc/documentation/Offline_documentation/html/code_configuration_run.html index c02264dc..6346a86a 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/code_configuration_run.html +++ b/currentRelease/doc/documentation/Offline_documentation/html/code_configuration_run.html @@ -217,9 +217,9 @@

Run calculation

- © Copyright 2022, Université Gustave Eiffel + © Copyright 2020, Université Gustave Eiffel - Last updated on Jan 12, 2022. + Last updated on Jan 04, 2022.

diff --git a/currentRelease/doc/documentation/Offline_documentation/html/console_window.html b/currentRelease/doc/documentation/Offline_documentation/html/console_window.html index 674d69ad..885ddf19 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/console_window.html +++ b/currentRelease/doc/documentation/Offline_documentation/html/console_window.html @@ -257,9 +257,9 @@

‘Console’ window

- © Copyright 2022, Université Gustave Eiffel + © Copyright 2020, Université Gustave Eiffel - Last updated on Jan 12, 2022. + Last updated on Jan 04, 2022.

diff --git a/currentRelease/doc/documentation/Offline_documentation/html/create_charts.html b/currentRelease/doc/documentation/Offline_documentation/html/create_charts.html index 0a39822c..b4a7cbe6 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/create_charts.html +++ b/currentRelease/doc/documentation/Offline_documentation/html/create_charts.html @@ -269,9 +269,9 @@

Chart Options

- © Copyright 2022, Université Gustave Eiffel + © Copyright 2020, Université Gustave Eiffel - Last updated on Jan 12, 2022. + Last updated on Jan 04, 2022.

diff --git a/currentRelease/doc/documentation/Offline_documentation/html/define_position.html b/currentRelease/doc/documentation/Offline_documentation/html/define_position.html index 9f1580bf..af63acad 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/define_position.html +++ b/currentRelease/doc/documentation/Offline_documentation/html/define_position.html @@ -241,9 +241,9 @@

Define a position using the pointer on the 3D view

- © Copyright 2022, Université Gustave Eiffel + © Copyright 2020, Université Gustave Eiffel - Last updated on Jan 12, 2022. + Last updated on Jan 04, 2022.

diff --git a/currentRelease/doc/documentation/Offline_documentation/html/element_properties_sound_level_results.html b/currentRelease/doc/documentation/Offline_documentation/html/element_properties_sound_level_results.html index 56703ba3..dd1bb5ea 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/element_properties_sound_level_results.html +++ b/currentRelease/doc/documentation/Offline_documentation/html/element_properties_sound_level_results.html @@ -229,9 +229,9 @@

Define the properties of a sound map

- © Copyright 2022, Université Gustave Eiffel + © Copyright 2020, Université Gustave Eiffel - Last updated on Jan 12, 2022. + Last updated on Jan 04, 2022.

diff --git a/currentRelease/doc/documentation/Offline_documentation/html/file_csbin.html b/currentRelease/doc/documentation/Offline_documentation/html/file_csbin.html index e6ba7afc..d26eb282 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/file_csbin.html +++ b/currentRelease/doc/documentation/Offline_documentation/html/file_csbin.html @@ -216,9 +216,9 @@

Surface receiver file file .csbin

- © Copyright 2022, Université Gustave Eiffel + © Copyright 2020, Université Gustave Eiffel - Last updated on Jan 12, 2022. + Last updated on Jan 04, 2022.

diff --git a/currentRelease/doc/documentation/Offline_documentation/html/file_gabe.html b/currentRelease/doc/documentation/Offline_documentation/html/file_gabe.html index 2e8bf679..6821619a 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/file_gabe.html +++ b/currentRelease/doc/documentation/Offline_documentation/html/file_gabe.html @@ -216,9 +216,9 @@

Tabulated data file .gabe

- © Copyright 2022, Université Gustave Eiffel + © Copyright 2020, Université Gustave Eiffel - Last updated on Jan 12, 2022. + Last updated on Jan 04, 2022.

diff --git a/currentRelease/doc/documentation/Offline_documentation/html/file_gap.html b/currentRelease/doc/documentation/Offline_documentation/html/file_gap.html index cab787f0..657e4791 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/file_gap.html +++ b/currentRelease/doc/documentation/Offline_documentation/html/file_gap.html @@ -216,9 +216,9 @@

Punctual receiver file .gap

- © Copyright 2022, Université Gustave Eiffel + © Copyright 2020, Université Gustave Eiffel - Last updated on Jan 12, 2022. + Last updated on Jan 04, 2022.

diff --git a/currentRelease/doc/documentation/Offline_documentation/html/file_pbin.html b/currentRelease/doc/documentation/Offline_documentation/html/file_pbin.html index 63441645..d3f78cd5 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/file_pbin.html +++ b/currentRelease/doc/documentation/Offline_documentation/html/file_pbin.html @@ -216,9 +216,9 @@

Animated data file .pbin

- © Copyright 2022, Université Gustave Eiffel + © Copyright 2020, Université Gustave Eiffel - Last updated on Jan 12, 2022. + Last updated on Jan 04, 2022.

diff --git a/currentRelease/doc/documentation/Offline_documentation/html/file_recp.html b/currentRelease/doc/documentation/Offline_documentation/html/file_recp.html index fd421d49..e0376a94 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/file_recp.html +++ b/currentRelease/doc/documentation/Offline_documentation/html/file_recp.html @@ -223,9 +223,9 @@

Punctual Receiver data file .recp

- © Copyright 2022, Université Gustave Eiffel + © Copyright 2020, Université Gustave Eiffel - Last updated on Jan 12, 2022. + Last updated on Jan 04, 2022.

diff --git a/currentRelease/doc/documentation/Offline_documentation/html/file_recps.html b/currentRelease/doc/documentation/Offline_documentation/html/file_recps.html index 607eca76..89ac5d06 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/file_recps.html +++ b/currentRelease/doc/documentation/Offline_documentation/html/file_recps.html @@ -216,9 +216,9 @@

Sound level per source .recps

- © Copyright 2022, Université Gustave Eiffel + © Copyright 2020, Université Gustave Eiffel - Last updated on Jan 12, 2022. + Last updated on Jan 04, 2022.

diff --git a/currentRelease/doc/documentation/Offline_documentation/html/file_rpi.html b/currentRelease/doc/documentation/Offline_documentation/html/file_rpi.html index 2f07f403..088d7c37 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/file_rpi.html +++ b/currentRelease/doc/documentation/Offline_documentation/html/file_rpi.html @@ -216,9 +216,9 @@

Animated intensity file .rpi

- © Copyright 2022, Université Gustave Eiffel + © Copyright 2020, Université Gustave Eiffel - Last updated on Jan 12, 2022. + Last updated on Jan 04, 2022.

diff --git a/currentRelease/doc/documentation/Offline_documentation/html/freecad_create_room.html b/currentRelease/doc/documentation/Offline_documentation/html/freecad_create_room.html index a317a55a..0f5405f4 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/freecad_create_room.html +++ b/currentRelease/doc/documentation/Offline_documentation/html/freecad_create_room.html @@ -227,9 +227,9 @@

Create body

- © Copyright 2022, Université Gustave Eiffel + © Copyright 2020, Université Gustave Eiffel - Last updated on Jan 12, 2022. + Last updated on Jan 04, 2022.

diff --git a/currentRelease/doc/documentation/Offline_documentation/html/genindex.html b/currentRelease/doc/documentation/Offline_documentation/html/genindex.html index 57d8580d..b313aec7 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/genindex.html +++ b/currentRelease/doc/documentation/Offline_documentation/html/genindex.html @@ -134,7 +134,6 @@

Appendices

-
-

Use TetGen to identify problems in 3D models

-

There is a way inside I-Simpa to identify problems in 3D geometries, after importing the 3D scene.

+
+

Use TetGen to identify problemns in 3D models

+

There is a way inside I-Simpa to identify problems in 3D geometries, after imprting the 3D scene.

  1. Import your 3D model in I-simpa
  2. Go to the ‘Calculation’ tab of the ‘Project’ window
  3. @@ -278,9 +273,9 @@

    Use TetGen to identify problems in 3D models

    - © Copyright 2022, Université Gustave Eiffel + © Copyright 2020, Université Gustave Eiffel - Last updated on Feb 18, 2022. + Last updated on Jan 04, 2022.

    diff --git a/currentRelease/doc/documentation/Offline_documentation/html/import_options.html b/currentRelease/doc/documentation/Offline_documentation/html/import_options.html index 73d6e3dc..7c2f4596 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/import_options.html +++ b/currentRelease/doc/documentation/Offline_documentation/html/import_options.html @@ -307,9 +307,9 @@

    ‘Keep existing group’

    - © Copyright 2022, Université Gustave Eiffel + © Copyright 2020, Université Gustave Eiffel - Last updated on Jan 12, 2022. + Last updated on Jan 04, 2022.

    diff --git a/currentRelease/doc/documentation/Offline_documentation/html/index.html b/currentRelease/doc/documentation/Offline_documentation/html/index.html index ee193c96..7d785cd5 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/index.html +++ b/currentRelease/doc/documentation/Offline_documentation/html/index.html @@ -134,7 +134,6 @@

    Appendices

@@ -375,8 +370,8 @@

Early stage support \(\text{ST}_\

Expected values

Table A.1 of the NF EN ISO 3382-1 standard proposes recommended values for most of these acoustic parameters, for concert halls and unoccupied multi-purpose halls up to 25000 m3:

- - +
Expected values for few room acoustics parameters. From Table A.1 of the NF EN ISO 3382-1 standard.
+@@ -447,9 +442,9 @@

Expected values

- © Copyright 2022, Université Gustave Eiffel + © Copyright 2020, Université Gustave Eiffel - Last updated on Feb 18, 2022. + Last updated on Jan 04, 2022.

diff --git a/currentRelease/doc/documentation/Offline_documentation/html/search.html b/currentRelease/doc/documentation/Offline_documentation/html/search.html index 93f370dc..6a1c15e2 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/search.html +++ b/currentRelease/doc/documentation/Offline_documentation/html/search.html @@ -134,7 +134,6 @@

Appendices

    -
  • I-Simpa errors and messages
  • Standards
  • Room acoustics parameters
  • Glossary
  • @@ -225,9 +224,9 @@

    - © Copyright 2022, Université Gustave Eiffel + © Copyright 2020, Université Gustave Eiffel - Last updated on Feb 18, 2022. + Last updated on Jan 04, 2022.

    diff --git a/currentRelease/doc/documentation/Offline_documentation/html/searchindex.js b/currentRelease/doc/documentation/Offline_documentation/html/searchindex.js index 70364155..819fba4d 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/searchindex.js +++ b/currentRelease/doc/documentation/Offline_documentation/html/searchindex.js @@ -1 +1 @@ -Search.setIndex({docnames:["I_Simpa_overview","I_Simpa_standard","code_SPPS","code_SPPS_Validation","code_SPPS_modelling","code_SPPS_principle","code_TCR","code_characteristics","code_configuration_SPPS","code_configuration_TCR","code_configuration_frequency_bands","code_configuration_job","code_configuration_meshing","code_configuration_run","console_window","create_charts","define_position","element_properties_sound_level_results","errors_messages","file_csbin","file_gabe","file_gap","file_pbin","file_recp","file_recps","file_rpi","freecad_create_room","glossary","import_file_recommandations","import_options","index","main_windows","manipulate_sources_receivers","menu_edition","menu_file","menu_help","menu_simulation","menu_view","menu_windows","menus","presentation","project_database","project_window","properties_window","references","room_acoustics_parameters","setup","surface_selection","tab_calculation","tab_results","tab_scene","toolbar_meshing","toolbar_project","toolbar_selection","toolbar_simulation","toolbar_view_camera","toolbars","tutorial_Elmia_hall","tutorial_industrial_hall","tutorial_teaching_room","using_directivity","using_spectrum","validations/validation_atmospheric_absorption","validations/validation_clarity_calculation","validations/validation_notice"],envversion:{"sphinx.domains.c":1,"sphinx.domains.changeset":1,"sphinx.domains.cpp":1,"sphinx.domains.javascript":1,"sphinx.domains.math":2,"sphinx.domains.python":1,"sphinx.domains.rst":1,"sphinx.domains.std":1,"sphinx.ext.intersphinx":1,sphinx:54},filenames:["I_Simpa_overview.rst","I_Simpa_standard.rst","code_SPPS.rst","code_SPPS_Validation.rst","code_SPPS_modelling.rst","code_SPPS_principle.rst","code_TCR.rst","code_characteristics.rst","code_configuration_SPPS.rst","code_configuration_TCR.rst","code_configuration_frequency_bands.rst","code_configuration_job.rst","code_configuration_meshing.rst","code_configuration_run.rst","console_window.rst","create_charts.rst","define_position.rst","element_properties_sound_level_results.rst","errors_messages.rst","file_csbin.rst","file_gabe.rst","file_gap.rst","file_pbin.rst","file_recp.rst","file_recps.rst","file_rpi.rst","freecad_create_room.rst","glossary.rst","import_file_recommandations.rst","import_options.rst","index.rst","main_windows.rst","manipulate_sources_receivers.rst","menu_edition.rst","menu_file.rst","menu_help.rst","menu_simulation.rst","menu_view.rst","menu_windows.rst","menus.rst","presentation.rst","project_database.rst","project_window.rst","properties_window.rst","references.rst","room_acoustics_parameters.rst","setup.rst","surface_selection.rst","tab_calculation.rst","tab_results.rst","tab_scene.rst","toolbar_meshing.rst","toolbar_project.rst","toolbar_selection.rst","toolbar_simulation.rst","toolbar_view_camera.rst","toolbars.rst","tutorial_Elmia_hall.rst","tutorial_industrial_hall.rst","tutorial_teaching_room.rst","using_directivity.rst","using_spectrum.rst","validations\\validation_atmospheric_absorption.rst","validations\\validation_clarity_calculation.rst","validations\\validation_notice.rst"],objects:{},objnames:{},objtypes:{},terms:{"095605219090049q":44,"100db":62,"100hz":[8,10,42,48],"10log":61,"10m":[59,63],"1250hz":63,"125hz":[8,10,42,48],"125m\u00b3":63,"128x128x128":29,"145b":44,"1984papm":44,"1\u00e8re":44,"2000hz":61,"2003lema1016":44,"20m":63,"2\u00e8me":44,"2nd":57,"30db":[8,42,48],"30m":63,"3DS":[0,28,34,39],"4000hz":[8,10,42,48],"5000hz":[8,10,42,48],"500hz":61,"5ms":63,"60db":[8,42,48],"682x":44,"6translat":29,"80db":[57,58],"\u00e9tude":44,"\u03b1":[42,50],"\u03b4t":2,"\u03b5":2,"\u03bb":[42,50],"case":[2,4,5,16,27,42,50,57,58,61],"cos\u00b2":7,"default":[4,8,9,12,15,29,34,37,38,39,40,41,42,48,50,58,59,61],"encombr\u00e9":44,"export":[0,8,9,14,15,26,28,34,38,42,48,57,58,59],"final":[8,42,48],"float":[38,39],"function":[0,1,2,4,5,28,29,36,39,46,50],"goto":[3,58,62,63],"import":[0,4,18,30,34,36,39,41,42,50,61],"int":4,"long":[4,8,9,12,42,48],"mod\u00e9lis":44,"new":[4,5,8,9,12,14,26,29,30,34,39,41,42,44,46,48,50,52,56,57,58],"null":4,"poll\u00e8":44,"r\u00e9verb\u00e9rat":6,"return":[4,28,37,39,54,56],"short":[4,42,50],"th\u00e9ori":[6,44],"th\u00e9oriqu":44,"throw":5,"transient":4,"true":[42,49],"try":[8,9,12,28,29,42,48],"universit\u00e9":44,"vorl\u00e4nder":44,"while":[4,27,41],Des:[42,44,50],For:[4,5,8,14,29,34,39,41,42,48,49,50,57,58,59],Les:44,N_s:45,One:[0,5,45,58],Such:58,The:[0,2,4,5,6,8,9,12,14,15,18,23,26,27,29,30,31,33,37,39,40,41,42,44,45,46,48,49,50,51,53,56,57,58,59,61,62,63,64],Then:[4,16,17,26,28,58],There:[0,8,28,29,42,48,58],These:[4,5,42,50,58],Use:[0,15,30,42,57],Used:42,Uses:59,Using:[2,4,30,34,39,41,50,57,63],With:[4,5,8,9,12,29,42,48],a_log:[42,50],aaua:44,abil:4,abl:[5,29,34,39],aborbing_materi:58,about:[2,6,28,35,39,58],abov:[4,5,45,58],abs:44,absenc:4,absolut:4,absorb:[2,4,5,8,42,44,48,58,59],absorpt:[0,1,3,5,7,8,9,27,41,42,44,48,50,58,59,63],abt1:57,academ:44,acceler:5,accept:[8,9,12,28,42,48],access:[0,8,42,48,49],accord:[2,4,5,8,14,29,42,48,49,57,58,59,61,62],accound:41,account:[4,41,42,50,61],accur:4,accuraci:[2,5],achiev:4,acouct:[8,42,48],acoust:[5,6,27,28,29,30,34,39,41,42,44,50,57,59,62,63],acoustiqu:[1,44],act:[33,39,58],acta:[44,57],action:[8,9,15,23,31,33,39,42,47,48,49,50,57,59],activ:[0,8,9,12,33,39,42,48,50,58],actual:4,acustica:[44,57],adapt:[29,30],add:[0,8,9,11,12,42,48,50,61],added:[0,34,39,42,49,57],adding:28,addit:[4,8,9,12,34,39,42,45,48,49,57,58,59,63],additionn:[2,8,42,48],adjac:[4,5],adjust:[16,28,41,61],adsab:44,advanc:[42,57,59],advantag:[4,5],aerodynam:4,affect:[4,16,29,34,39,41,42,50,57,59,61],afnor:1,after:[4,5,8,11,28,42,47,48,50,58],afterward:4,again:4,agre:46,agreement:[4,46,62,63,64],air:[4,42,50],algebra:4,algorithm:[0,2],align:[4,15,42,45],aliv:[8,42,48],all:[0,4,5,8,9,10,11,14,15,28,29,34,37,38,39,40,41,42,45,46,47,48,49,50,55,56,57,58,59,61],allow:[4,5,6,8,10,14,27,29,34,37,39,41,42,48,50,51,56,57,58,59],alon:0,along:[2,5,42,50,51,56],alpha:[1,4,5,41,42,50,58],alpha_:4,alpha_c:4,alreadi:[8,9,28,42,46,48,50,57,58],also:[0,4,5,8,27,28,29,30,36,37,39,41,42,45,48,50,58,59,63,64],although:[4,5,30,34,39,61],altitud:4,alwai:[4,8,42,48],ambigu:4,america:44,among:4,amount:[2,4],amplitud:[4,5],amta:[8,42,48],analazi:[8,42,48],analog:4,analys:63,analyt:[4,5],angl:[4,5,32,41,42,45,50],ani:[4,5,7,42,50],anim:[0,7,8,34,36,39,42,48,49,54,56,58,59],anoth:[4,42,47,58],antoh:[15,42],antonio:44,anyon:46,apart:4,appear:[4,57,59],appendic:30,appli:[4,5,8,34,39,42,44,48,58,61],applic:[0,1,4,6,15,30,34,39,41,42,44,50],approach:[0,2,4,5,8,30,42,48,50],approx:41,approxim:[0,4,28,29],aps:44,arbitrari:[4,5,57],archambeau:44,architectural:44,archiv:[42,49],area:[4,42,48,50],aris:23,around:[4,32,37,39,42,50,62,63],arrai:4,arrow:[37,39,42,50],art00004:44,art00007:44,articl:[1,44],artifici:4,arxiv:44,asc:[34,39],ascii:[34,39],aslo:[8,42,48],assign:[8,29,37,39,41,42,48,57,58],assimil:[4,5],associ:[4,8,23,29,37,39,41,42,48,49,50,57,64],assum:[4,58,63],assumpt:[4,64],assur:29,atmospeh:[42,50],atmospher:[0,1,3,5,7,8,9,42,44,48,50,58,63],atmospheric_absorpt:62,atmospheric_absorption_geometri:62,att_atmo:4,att_atmos_press:4,att_atmos_pression_i:4,attent:[3,18,34,39,62,63],attenu:[1,4,44,61,62],attribut:[4,41],audienc:[41,57],auditorium:44,aul85:[4,44],aul86:[4,44],auletta:44,auswertung_phase_ii:57,author:[4,42,50],auto:[42,50],automat:[0,4,8,10,15,32,34,39,41,42,48,49,50,57,58,61],avail:[0,30,34,39,42,47,50,57,58,64],averag:[4,5,8,28,30,42,48,57,58],avoid:[4,8,9,12,18,42,48],awai:4,axi:[4,15,29,32,37,39,42,45,50,57],azimuth:4,b_lin:[42,50],back:[4,17,40],background:[0,34,39,41,42,45,50,61],backup:46,backward:[8,42,48],bad:28,balanc:4,balloon:[42,50],band:[0,4,9,23,30,41,50,57,58,59,61,62,63],bank:29,bar:40,barbri:[4,44],barron:[44,63],base:[0,2,4,5,6,29,30,42,48],basi:[6,27,45],bass:44,beam:5,becaus:58,becom:[4,5],been:[0,36,37,39,57,58,59,61],befor:[4,8,9,12,42,46,48,57,58,61],begin:[4,45],behavior:[62,64],behaviour:4,being:[4,5,45,58],below:[5,8,32,42,45,48,62,63],bend:4,benefit:5,besid:[42,50],beta:[4,41],better:[4,5,28,29],between:[0,2,4,5,8,9,10,12,17,27,28,32,41,42,45,48,50,57,58,61,62,63,64],bilater:41,bin:[34,39],bl88:[44,63],black:14,blue:14,bmc90:[4,44],bmp:[15,37,39,42],book:[6,29,44],border:[42,50],bork:57,both:[2,4,14,41,59,61],bottom:[8,42,48],boundari:[4,5,41],boutiqu:1,box:[4,29,34,35,37,38,39,41,42,50,51,56,57,58],branch:42,bright:4,brought:4,bruit:1,bspe84:[4,44],build:[0,4,5,8,10,28,29,30,34,39,42,48,57,58],built:[0,28,34,39,41,59,61],bulk:4,burn:44,burns1990:5,button:[15,28,42,47,51,56],c50:[7,45],c80:[7,45,63],c_0:4,c_1:4,c_2:4,c_i:4,c_p:4,cad:[26,28,34,37,39,58,59],calcul:[1,3,5,10,11,12,28,29,30,40,45,49,50,51,56,61,62],call:[4,5,64],camera:[30,31,53,55],can:[0,2,4,5,6,8,9,10,11,12,14,15,16,23,27,28,29,30,31,32,33,34,37,39,40,41,42,43,45,46,47,48,49,50,57,58,59,61,63,64],cancel:[33,39],cannot:4,carlo:[2,5,44],carrefulli:64,carri:[2,4,5,64],catalogue_detail:[1,62,63],catalogue_tc:[1,63],catt:[0,41,57],caus:4,caution:58,caviti:[0,30],cdantonio16:[4,44],cdf_dist_theta:4,cdot:4,ceil:[29,41,57,59],celer:[42,50],celerite_log_lin:4,cell:[15,16,42,59],celsisu:4,center:[4,32,37,39,42,45,50,62],centr:[0,4,8,10,42,48],central:[8,42,48],certain:[4,5],chain:42,chair:58,chamber:[4,5,44],champ:44,chang:[4,8,11,17,29,30,34,37,39,41,42,46,48,49,50,57,59,61,63],charact:42,character:[0,4],characterist:[0,2,4,5,6,28],chart:[8,30,48],check:[4,8,9,10,12,28,41,42,48,50,57,58],chen:44,choic:[4,5,15,42,57],choix_phi:4,choos:[4,5,15,29,41,42,46,48,49,50,57,58,59],choosen:[45,62],chose:4,chosen:[4,5,29,45],chossen:[8,10,42,48],christon:44,cite:5,clariti:[0,3,7,8,30,42,48,50],clarity_calcul:63,clarity_room_1000_m3:63,clarity_room_12000_m3:63,clarity_room_125_m3:63,clarity_room_504_m3:63,clarity_room_8000_m3:63,clarity_valid:63,classic:[0,4,5,6,8,27,30,42,44,45,48,50,59,62],classiqu:6,clear:[11,42,48,59],clic:[37,39],click:[8,9,15,16,17,28,41,42,43,47,48,49,50,57,58,59],clipboard:[15,42],close:[5,28,29,42,48,50,57,58,62],closest:41,cms:57,code:[0,1,2,4,6,8,9,10,12,13,14,18,28,29,30,34,39,41,42,48,49,50,51,56,57,58,62,63,64],coeffici:[0,1,4,5,7,27,41,42,44,50,58,62,63],col:[32,58],collaps:29,collid:[4,5],collis:[2,4,5,27],color:[14,15,17,28,34,41,42,50,53,56,58],colormap:[34,39,53,56,59],column:[8,32,42,48,50,58],com:[0,44],combin:[45,57],come:[4,40,45],comma:17,comment:[8,42,48],commerci:0,common:[0,4,50],commun:0,compar:[4,5,8,29,42,48,57,58,63],comparaison:64,comparison:[4,44,57,63,64],compat:[26,29,36,39],complet:5,complex:[4,5,7,28,30],complianc:4,complic:[29,44,59],compon:[0,4,40,46],compos:[5,40,41,58],comprehens:1,compromis:[4,5],comput:[5,14,29,30,40,44,46,57,63],computaion:[8,42,48],computationn:[8,42,48],concentr:[4,42,50],concept:[4,30,42,50,64],concern:[0,30,34,35,39,42,50,58,59],concert:[44,45],cond_g:4,cond_p:4,conder:63,condit:[0,4,5,45,62],config:[8,42,48,49],configur:[8,9,15,38,39,48,63],conform:61,confus:[4,58],congest:[4,5],connect:26,conserv:4,consid:[2,4,5,8,10,15,34,39,41,42,45,48,50,58,59,62,63],consider:5,considerig:[42,50],consist:[4,15,42,57],consol:[11,28,30,40,42,48],constant:[2,4,5],constraint:[5,8,9,12,29,42,48,59],construct:[4,5],consum:29,contact:[4,5],contain:[5,8,9,12,14,15,18,23,27,31,41,42,48,49,50,57,58,59,61],content:[8,42,44,48,49],context:57,contextu:[8,10,15,16,42,43,48,49,50,59],continu:[4,5,28],contour:[0,17,34,37,39,42,50],contrast:5,contribut:[0,4,8,30,42,44,48],control:[8,42,48],convent:[4,5],convers:[42,50],coordin:[4,28,32,42,50,57,58,59],copi:[0,34,39,41,58],coplanar:[47,58],core:[8,42,48],corespond:58,corner:[57,58],correct:[0,4,8,9,12,14,28,34,39,42,48,57,61],correctli:[4,28,58],corresond:[42,50],correspond:[4,8,10,14,15,16,17,23,37,39,40,41,42,43,45,47,48,49,50,53,56,57,58,59,61,64],correspondig:58,correspondng:58,corridor:[4,26,58],cos:[4,7,8,42,45,48],cosgamma2:4,cosin:45,cosinu:41,cost:5,could:[5,8,9,12,42,48,57,63,64],count:[4,42,50],coupl:[4,58],cours:63,cover:[4,26],coverag:1,cox:44,crc:[6,44],creat:[0,8,11,18,28,29,30,34,39,41,46,48,49,50,52,56,58,61],creation:[0,4,8,26,30,34,39,42,48,50,58],criterion:45,crop:4,cross:4,crowd:4,crucial:4,csbin:[8,42,48,49],csnumber:[1,62,63],csv:59,ctrl:[47,58,59],cube:[0,29,57],cubic:[29,62,63],cumul:[0,4,7,8,42,48,58,59],current:[4,5,29,34,39,52,54,56],cursor:[51,56,57],curv:[2,4,5,7,15,17,42,45,59],curvatur:4,cut:[0,42,50],d50:[7,45],d_0:4,d_n:4,d_o:4,dan:[1,44],dash:59,data:[0,4,7,8,14,15,16,31,34,36,39,41,46,47,48,49,53,54,56,58,59,64],databas:[0,30,57,58,61],date:[30,42,49,50,59],date_fold:[8,42,48],dav:44,dba:7,dband:45,debug:[8,9,12,42,48],decai:[8,42,44,48],decemb:57,decibel:[45,61],decid:29,decompos:[5,29],decreas:[2,4,5,8,42,48,62],deduc:4,deeper:63,defin:[0,4,5,7,8,9,10,12,29,30,32,34,38,39,41,42,45,48,50],definit:[0,4,7,8,30,41,44,48,49,57,59],deform:29,degre:[32,42,50],delai:[0,42,50],delaunai:[0,8,9,12,42,48],delet:[14,38,39,42,49,59],delimit:29,delta:4,denot:4,dens:[42,50],densite_energie_recepteur_volumiqu:4,densiti:[5,41],depen:57,depend:[2,4,5,8,29,30,31,42,48,49,50,58,63],deriv:4,des:44,desactiv:[0,42,50],descart:4,descri:42,describ:[4,27,41],descript:[0,5,41,42,44,50],descritpion:[42,50],deselect:59,design:[0,26,44],desktop:46,despit:4,destin:[42,50,58],detail:[2,29,31,42,50,57],detect:[0,42,50],determin:[1,4,5,44,45],determinist:[4,5],develop:[0,14,30,44],deviat:[4,62,64],devic:61,diagram:59,dialog:[29,34,37,38,39,41,42,50,51,56],diff_wal:58,differ:[4,5,8,28,30,34,39,42,48,58,59,64],differenti:29,difficult:[4,8,42,48,63,64],difficulti:5,diffract:4,diffu:[4,44],diffus:[0,1,2,5,6,7,8,27,41,42,44,48,50,58,59,62,63],digit:5,dimens:[0,4],dimension:30,dirac:4,direct:[0,1,5,7,8,30,32,42,45,48,50,58,59,63],directli:[4,23,42,49,58,59],disabl:[8,33,34,39,42,48,50],disadvantag:5,disapear:[8,42,48],disappear:[2,4,5],disbal:[42,50],discret:4,disk:[42,49,59],dispers:[4,5],displac:4,displai:[0,7,8,9,14,15,17,28,31,34,37,39,41,43,48,51,53,55,56,57,58,59],dispoi:4,dispos:57,distanc:[4,5,27,29,45,57,63],distinct:4,distinguish:4,distribut:[0,4,5,30,42,50,61],divid:[4,57],doc:[57,58,59,62,63],dockabl:[31,40],document:[4,8,9,12,14,30,35,39,41,42,48,49,58,60],doe:[4,5],doi:44,domain:[2,4,5,8,9,12,30,42,45,48,50],don:29,done:[0,8,10,28,30,42,48,50,57,58,61],door:58,door_room1:58,door_room2:58,dot:[15,42],doubl:[8,9,42,43,47,48,49,58,59],down:[33,37,39,57],download:46,downward:4,drag:[42,50,58],drastic:[8,42,48],draw:[4,5,64],drop:[42,50,57,58],due:[4,5,28,29,62,64],duplic:[4,30,42],durat:[5,8,42,48],dure:[0,1,2,4,5,8,28,34,39,41,42,46,48,62],dynam:[0,5,42,50],each:[2,4,5,8,9,11,29,34,39,41,42,45,48,49,50,57,58,59,61,62,63],ear:45,earli:[0,8,42,48,63],eas01:[4,44],easi:4,easili:[0,4,30,42,50],eceiv:23,echogram:[8,42,48,58],echogramm:[7,57],eckard:44,edg:[7,8,9,12,29,42,48],edit:[6,30,40,41],editor:42,edt:[7,8,42,48],edu:44,educ:[0,30],effect:[4,7,8,29,30,34,39,41,42,44,48,50,57,62,63],effici:0,either:[4,5,41,42,43,50,58,59,61],electron:44,element:[0,4,7,8,10,16,17,29,31,34,37,39,41,43,44,47,48,50,57,58,59,61],elementari:5,ell_i:4,ellipt:29,elmia:[30,58],elmia_coordin:57,elmia_stag:57,elmiahal:57,embed:[2,6,8,9,30,42,48],embrecht:44,emiss:[5,41],emit:[2,4,5],emphas:4,empti:[30,34,39],enabl:[8,9,34,39,42,48,50,53,56,59],enclosur:44,encount:4,encumbr:5,end:[4,8,42,45,48],energet:[2,5,8,30,42,48,57,58],energi:[0,2,5,8,42,44,48],energie_elementair:4,engin:[30,44],english:30,enhanc:29,enough:[4,57],ensur:5,enter:[4,59],entir:4,entiti:1,environ:[0,4,5,30,45,58],environment:[0,30,42,50],environn:1,epsilon:[4,5],epsilon_i:4,eq_flux_1:4,eq_flux_1b:4,equal:[4,41,45,63],equat:29,equiprob:4,equiv:4,equival:[0,2,4,8,23,42,48,59],ergod:44,erreor:[],error:[0,28,30,63],especi:4,essenti:[33,39],estim:[27,63],etc:[4,64],evalu:[6,8,29,30,42,45,48,63],evan:44,even:[4,5],eventu:42,everyth:4,evid:44,evolut:[4,5,8,23,42,48],evolv:4,exact:[4,44],exactli:[4,5,11,42,48,49,58,59],exampl:[4,5,8,9,12,14,17,29,32,38,39,41,42,48,49,50,57,58,59,61,64],excatli:64,exce:4,excel:4,except:[4,34,39,42],exchang:4,execut:[8,42,48],exist:[4,30,34,39,42,50,57],exit:[34,39],exp:[4,63],expect:[4,64],experi:61,experienc:[8,42,48],experiment:[4,41,60,64],explor:[30,42,49,59],express:[4,45],extend:[0,30],extens:[0,41,42,49],exterior:[37,39],exteriorwal:41,extern:[4,14,42,49],extinct:[8,42,48,58],extract:[31,53,56],extrapol:41,extrem:61,eyes:[37,39],eyr:[7,42,48,59,63],fa043864:1,fa104093:1,fa138697:1,fa138698:1,fa138699:1,fa148722:1,fa169343:1,face:[4,5,7,16,17,18,28,29,31,34,41,42,47,50,53,55,56,57,58,59],fachabteilungen:57,fact:[4,28],factor:4,factori:44,fail:18,famili:1,farm:[42,50],favor:[4,42,45,50],favour:45,fdc:4,fdci:4,fdci_2:4,featur:[14,30,41,60],few:[8,42,48,61],field:[1,4,5,6,7,8,27,29,41,42,44,48,58,59,62,63],fig:4,figur:[4,62,63],fiit:58,file:[0,8,14,15,17,28,30,34,35,37,38,40,41,46,48,57,58,59],filenam:[34,39,52,56],fill:[15,16,42,50,57],find:[28,58],finish:[57,58],fiorst:59,first:[2,4,5,8,26,37,39,42,45,48,50,54,55,56,57,58,59,61],fit:[0,7,8,27,29,30,44,48],fix:[8,42,48,64],floor:[41,42,50,57,58,59],flow:4,focu:[42,50],folder:[8,17,34,39,41,42,46,48,49,50,57,58,59,61,62,63],follow:[2,4,6,8,9,29,30,37,39,40,42,45,48,50,57,58,59,62,63,64],follw:45,font:[34,39],forest:4,form:[4,5,8,15,42,45,48,50,58],format:[14,23,34,37,39,41,57],formul:4,formula:[4,42,48,50,59,63],forward:[54,56],found:4,frac:[4,45,63],fraction:[0,4,8,42,48],frame:4,franc:44,free:[0,1,4,26,27,42,50,58],freeli:0,french:[2,6],frequenc:[0,1,4,9,23,30,34,39,41,45,50,57,58,59,61,62,63],fresnel:4,from:[0,2,4,5,6,8,14,23,29,30,34,39,41,46,47,48,49,50,51,54,56,57,58,59,61,63],front:[17,42],frquenci:41,full:41,fulli:0,functionn:0,funtion:4,further:4,furthermor:41,fusion:29,g_n:45,gabe:[42,49],gain:58,gamma_1:4,gamma_2:4,gap:[8,42,48,49],garden:[42,50],gas:5,gener:[1,2,8,9,12,27,28,29,30,34,35,36,37,39,42,45,48,49,50,51,56,57,59],geo_sourc:4,geometr:[2,4,5,30,42,48,50,57,58],geometri:[4,5,16,28,29,30,34,36,37,39,42,50],geq:41,github:[0,30],give:[0,4,42,45,50,57,59,61],given:[0,4,5,8,9,11,12,23,29,34,37,39,41,42,43,45,48,50,57,58,59,61,63],global:[4,8,9,41,42,48,57,58,59,61],glossari:30,goal:[57,59],good:[4,8,29,42,48,59,62],got:28,govern:4,gradient:[4,42,50],gradual:5,graphic:[0,8,15,17,30,40,42,46,48,50,59],grass:4,graviti:45,great:18,greater:[4,41],green:58,grid:[4,30,42,50,57],ground:[4,5,42,45,50,58],group:[0,1,4,30,34,39,41,42,47,50,53,56,57,58,59],gtart:42,guarante:26,gui:30,guid:[41,42,50],guidelin:1,habitat:[42,50],half:4,hall:[4,30,42,45,50,58],hand:[4,5,8,10,42,48,59],handli:[42,50],hard:[42,49,59],harvard:44,has:[0,4,16,34,37,39,41,57,58],hat:4,have:[0,4,5,8,11,28,29,30,32,34,36,39,42,46,48,50,57,58,59,63,64],head:[37,39],heat:4,hedg:[42,50],height:[4,5,26,42,50,57,59],heinrich:[6,44],help:[0,30,40],here:[0,3,5,34,39,53,56,58,59],hide:[15,34,42],high:29,higher:[4,62],highlight:[47,58],histori:[34,39],hod91:[4,44],hodgson:44,hold:[58,59],hole:[18,28],homogen:[4,5,23,42,50],horizont:4,host:30,how:[4,31,41,42,50],howev:[4,42,50,57,58,61],htm:[1,62,63],html:[1,42,49,57],http:[0,1,30,44,46,57,62,63],humid:[4,42,50,62],hundr:[5,8,42,48],huygen:4,hybrid:[4,44,63],hygrometri:4,hypothesi:[42,48,62],i_0:4,icon:[28,46,57,59],ident:[4,58,64],identifi:[30,58,59],ifsttar:[0,35,39,46],iint:4,illustr:[4,30,45,58],illustration_refract:4,imag:[5,15,42,57,58,59,62],immedi:4,impact:63,imped:[1,4],implement:[3,4,5],impos:4,imposs:4,impress:57,impuls:[8,23,42,45,48],inc:44,inceras:[8,42,48],incid:[1,4,41,44],includ:[30,42,45,46,48,58],increas:[4,8,17,29,34,39,42,48,57,62],inde:[4,5,29],independ:[4,38,39,40,58],index:[4,28,30],indic:[8,27,42,48,50],individu:[4,8,42,48,58,63],industri:[0,4,5,30,42,50],industrial_hal:58,industriel:44,infin:5,infinit:4,infinitesim:5,influenc:4,inform:[0,2,6,8,9,12,14,28,30,35,38,39,41,48,49,57,58],infti:[4,45],ingentaconnect:44,ingolf:57,inher:62,initi:[0,2,4,5,8,42,45,48,58,59,64],initla:[15,42],insert:[4,30],insid:[2,5,8,28,37,39,42,48,50],instabl:64,instal:[30,57,58,59,62,63],instantan:[7,8,23,42,45,48],instantanea:59,instead:[11,29,42,45,48,58,59,64],instruct:[8,9,30,42,48],int_0:[4,45],int_:[4,45],int_t:45,integ:4,integr:[0,8,42,45,48],intellig:[30,42,50],intend:57,intens:[0,5,7,42,49],interact:[4,5,8,42,48],interdepend:61,interest:[4,8,42,48,50,57,58,61],interfac:[0,23,30,34,38,39,40,42,49,50],interior:[0,30,37,39,55,56,57],interiorwal:41,intern:[4,14,58,59],interpret:[42,49],intersect:[18,28,57],interv:45,introduc:4,invers:[4,42,50],involv:5,irregular:[4,57],isbn:44,iso:[0,1,4,17,34,39,62,63],iso_catalogu:[1,63],issu:30,item:58,its:[2,4,5,8,37,39,42,48,50,58,59],itself:[4,58],jincai:44,job:[8,9],join:5,journal:44,joy74:[4,44],joy75:[4,44],joy78:[4,44],joyc:44,joyce1974:5,jpg:[15,37,39,42,57],jun:44,just:[0,34,39,61],justifi:4,keep:[4,8,11,30,34,39,42,48,59],kei:[37,39,58,59],keyboard:58,khz:4,kind:[8,42,48],know:[4,5,41],knowledg:[4,5,45],known:4,kut16:[4,44],kut81:[4,44],kuttruff:[6,44],l_j:45,l_receiver_surfac:4,label:[4,15,34,39,42],laboratori:0,lam96:[4,44],lam:44,lambda:[42,50,58],lambda_c:4,lambert:[41,58],lan:1,langl:4,languag:[1,34,39,46],larg:[4,5,8,29,42,44,48,58],larger:41,last:[4,16,28,33,34,39,41,42,45,50,54,56,58,59,61],late:[0,63],later:[0,7,8,42,48,61],latest:30,latex:5,latter:[4,5],launch:46,law:[0,2,5,41,42,50,58],lawn:[42,50],layer:4,lead:[4,59,61],learn:30,leav:[5,59],lee:44,left:[4,8,9,15,37,39,42,45,47,48,49,59,63],legend:[34,39],lenght:[8,9,12,42,48],length:[4,8,42,48,50,58,59],lepolles2003:[4,5],less:[4,8,42,48],let:4,level:[0,1,6,7,17,29,30,34,39,42,50,57,58,59,61,62],lf80:[7,45],lfc80:[7,45],lfc:[4,7,8,42,48],li8:41,li_recepteur_volumiqu:4,licens:[35,39,46],lie:5,lies:5,like:[4,28,34,39,42,45,50],liken:5,limit:[3,4,5,8,29,42,45,48,57,58,62,63,64],lin82:[4,44],lin:[0,4],lindqvist:44,line:[2,4,5,7,17,30,34,42,50,55,56],linear:[4,8,15,42,45,48],link:[0,35,39,41,42,44,48,50],linoleum:41,list:[8,9,17,34,39,41,50,57,58,59,61],listen:45,literatur:64,llll:4,load:[8,29,34,36,39,42,48,49,57,58,59],local:5,localindustriel:58,locat:[29,38,39,42,45,46,50,57,58,59,62,63],locaux:44,log:[0,4,41,45,63],log_lin:[42,50],logarithm:[4,45],loi_1:4,loi_1b:4,loi_2:4,loi_2b:4,loi_3:4,longer:[5,8,42,48],loss:[0,4,41,58],low:[4,8,42,48,50],lower:[4,41],lp03:[4,44],machin:[4,5,30],macroscop:4,made:[4,5,42,49,50,58],magnitud:4,mai:[2,4,5,8,9,12,29,30,42,48,50,58,59,64],main:[2,4,5,6,28,30,34,38,39,40,44,57,59],mainli:[2,8,42,48,57,62],major:[4,5],make:[4,5,23,28,57],maltbi:44,man_pag:17,manag:[5,42,50],mani:[0,4,8,16,27,28,30,42,48,50,58],manipul:[0,30,42,50,57,59,61],manual:[11,42,48,50],map:[0,4,7,34,39,42,50,58,59],march:[0,29],marker:4,mass:41,mat:[34,39],materi:[4,5,27,28,29,30,34,42,50,59],material_catt:57,math:5,mathbf:4,mathemat:4,maximum:[4,8,9,12,17,29,42,48,57,59],maximun:[8,9,12,42,48],mean:[0,4,8,14,27,28,34,39,42,48,50,58,59,63,64],measur:[1,4,42,44,45,50,57,63],mechan:4,medium:[4,5],memori:[8,42,48,49,54,56],mention:4,menu:[8,10,14,15,16,30,31,34,37,40,42,43,46,48,49,50,53,56,57,58,59],merg:[37,39],mesh:[0,18,28,30,34,36,50,51,57,58,59],messag:[11,14,30,35,38,39,40,42,48,53,56],meteorol:[42,50],meteorolog:[0,5,7,42,50],meter:[4,27],method:[0,1,2,4,5,8,29,42,44,48,50,57,58,64],michael:44,micrometeorolog:4,milieu:44,mill:58,million:57,mind:4,minim:4,minimum:[17,59],minor:29,misalign:57,miss:57,mistak:30,mode:[4,5,8,9,12,15,31,37,39,42,47,48,50,53,55,56,58,59],model:[0,2,5,8,26,27,30,31,41,42,44,48,50,58,62,64],moder:4,modif:[34,39],modifi:[8,9,12,29,41,42,46,48,49,50,58,61],modifii:17,molecular:4,moment:4,mommertz:44,monitor:5,mont:[2,5,44],more:[0,4,5,6,8,9,12,29,30,34,39,42,45,47,48,49,50,57,58,61],moreov:5,most:[5,8,15,18,23,34,39,42,45,48,50,59,63],motion:[37,39],mous:[15,37,39,42],move:[4,37,39,40,47,51,56,57,58,59],movement:4,multi:45,multipl:[8,42,47,48],multithread:[8,42,48],multitud:5,murrai:44,must:[0,4,18,34,39,41,42,48,50,57,58,61],mutual:5,n_0:4,n_c:4,n_n:4,name:[1,4,8,15,17,42,48,49,50,57,58,59],nativ:[42,49],natur:[2,8,42,48],navig:31,necessari:[4,5,8,28,41,42,46,48,50,58,61,63],necessarili:64,need:[8,29,34,39,42,48,49,50,57],neg:4,netherland:44,nevertheless:[4,5],next:[4,8,9,42,48,54,56,57,58],nff:[34,39],nimber:[8,42,48],nitrogen:4,niveaux:1,nois:[0,1,5,30,41,42,50,57,58,59,61],noisemap:[0,57],noisi:58,non:[4,29],none:[4,29,37,39],nonumb:45,nor:4,norm:[1,4],normal:[41,42,45,50],notat:4,note:[4,8,28,29,41,42,45,48,50,64],noth:4,notic:57,notion:4,novemb:[44,57],now:58,nu_c:4,number:[2,4,5,8,27,29,32,42,45,48,50,57,58,64],numer:[0,4,5,6,18,29,30,34,39,41,42,48,50,57,62,63,64],ob89:[4,44],object:[0,2,4,5,8,27,28,42,45,48,50,58],observ:[30,45,64],obstacl:[4,5,27,58],obstruct:4,obtain:[0,4,6,8,42,45,48,57,61],obviou:61,obvious:4,ocav:[8,10,42,48],occur:[2,4,5,8,18,42,48,50],octav:[4,8,10,41,42,48,57,58,61],octob:44,odeon:[0,41],offer:29,offic:[2,6,30],offici:30,offlin:30,often:58,old:29,omega:4,omnidirect:[4,45,57,59],omnidirectionn:7,omnidrectionn:[42,50],onc:[4,11,15,42,47,48,58,59,63],ondet:[4,44],ondulatori:30,one:[4,5,8,11,29,30,34,38,39,41,42,47,48,50,57,59,61,63],ones:[58,63],onli:[4,5,8,9,11,12,23,27,29,30,33,34,36,37,39,41,42,48,50,57,58,59,61],onlin:[2,30,35,39],opac:[17,42,50],opaqu:17,open:[0,5,8,26,34,35,37,38,39,41,42,48,49,50,51,52,56,57,58,59],oper:[14,23,42,49,57,58],opposit:[41,42,50,58],optic:4,optim:[5,30,57],option:[4,8,14,16,17,28,30,31,33,34,36,37,38,39,40,48,49,50,57,58,59,64],order:[0,4,5,8,9,12,18,29,34,39,40,41,42,43,48,50,57,58,63,64],org:[1,44,62,63],organ:[0,42,49,50],organis:[42,50],orient:[0,4,15,37,39,42,45,50,57],origin:[0,4,15,29,32,34,37,39,41,42,50,57,58],other:[4,5,11,29,30,38,39,42,45,48,50,58,59,63,64],otherwis:4,our:[4,18,57,58],oustid:[37,39,57],out:[4,5,15,42,64],outcom:[42,49],outdoor:[1,4,62],outsid:[5,37,39],over:[4,5,61],overview:30,own:[0,37,39,58,59],oxygen:4,p2_:4,p_0:[4,45],p_i:4,p_t:4,packag:29,page:[3,44,62,63],pai:[34,39],paid:[3,62,63],panelwal:41,par:[4,44],paragraph:4,parallel:[5,8,42,48],parallelepiped:[0,34,39,42,50,58],parallelipiped:30,paramat:[8,42,48],paramatr:[8,9,12,42,48],paramet:[0,1,4,11,12,15,29,30,32,50,51,56,57,59,63],parameter:45,parametr:[0,26,42],paramt:[8,9,12,42,48],paremet:[8,42,48,50,57],part:[1,4,5,26,41,57,62,63],parti:44,partial:[4,5,29],particl:[0,2,8,30,34,39,42,44,48,57,58],particul:[2,5],particular:[4,26,34,39,57],particularli:[5,18,45],partit:4,partli:4,partticl:57,pass:[4,45],past:[8,15,41,48,58],path:[0,4,5,27,42,50,58],pattern:61,paus:[36,39,54,56,59],pbin:[42,49],pdf:[42,49],per:[4,7,41,42,57,58,62],perfeclti:58,perfect:27,perfectli:[4,5,27],perfom:59,perfomr:[],perform:[1,4,5,14,23,42,50,57,58,63],period:4,perpendicular:45,person:[37,39,55,56],pertin:57,peter:44,phd:44,phenomena:[2,5,8,30,42,48],phenomenon:[4,42,50],phi:4,phonon:[5,44],photon:44,phy:44,physic:[0,2,5,8,30,42,44,48,49,61],physrevd:44,pi_:4,pictur:57,pierci:44,pii:44,pink:58,place:[4,57,58],plai:[36,39,54,56],plan:[42,50],planar:[29,37,39,59],plane:[0,7,30,32,42,50,51,56],plasteredconcret:41,plateform:0,pleas:[29,30,46],pltmg:29,ply:[0,34,39,57,58],png:[15,37,39,42,57,58,59,62],point:[0,5,16,23,30,42,45,50,57,58],pointer:[30,31,42,47,50,53,56,59],poisson:4,polar:4,pole:4,poli:[0,34,39],ponder:61,poor:[29,59],pose:4,posit:[0,4,5,30,32,42,45,50,57,58,59,63],possibl:[4,5,8,9,12,15,29,31,34,39,41,42,48,49,50,57,58,62,64],post:0,power:[5,42,50,57,58,59,61],practic:4,pre:[0,5],precis:29,predefin:42,predict:[2,5,30],prefer:[1,4,29,34,39],prejudg:64,premis:5,prepar:58,presenc:[4,5],present:[0,4,5,30,45,53,56,57,58,59,64],preserv:[4,8,42,48],press:[6,44,47],pressur:[8,23,42,48,50],previou:[4,29,30,34,39,57,58,59],principl:[1,2,4,30,44,61],principle_receiver_volumiqu:4,print:4,probabilist:[0,2,5,8,42,48,64],probabl:5,probilist:57,problem:[0,4,18,30,57],problemn:[],proce:[8,42,48],procedur:[4,5,15,28,29,42,46,53,56,57,58,59],process:[0,4,5,8,18,42,48,50,57,58],processor:[0,8,42,48],produc:[4,8,29,42,48,49,50],product:4,profession:0,profil:[0,5,42,50],program:[14,42,49],prohibit:5,proj:[34,39,57,58,59,62,63],project:[0,4,11,17,26,28,29,30,33,34,39,40,48,49,52,53,57,58,59,62,63],projet:41,projet_config:[8,42,48,49],propag:[0,1,2,5,8,30,41,42,44,45,48,50,62],properti:[0,1,5,15,16,28,29,30,37,39,40,42,50,57,59,61],proport:[4,5],proportion:5,propos:[0,3,4,5,6,29,30,37,39,42,45,50,57,62,63],propto:4,provid:[4,8,42,48,57,58],ptb:57,punctual:[0,4,7,30,32,49,61,62,63],purpos:[45,58],python:[0,14,40,42,46],q_c:4,quad:4,quadrat:[4,45],qualiti:[4,8,9,12,17,28,29,34,39,42,45,48],quantiti:[4,8,23,42,48],quarter:4,quasi:[62,63],question:64,quick:[42,50,57],quit:[4,63],r01:57,r02:57,r03:57,r04:57,r05:57,r06:57,r_i:4,r_n:4,r_p:4,radiat:[4,5],radio:[51,56],radiu:[4,8,9,12,42,48,58],rai:[0,4,30,44],randolph:29,random:[1,2,5,8,15,42,44,48,58,64],randomli:[4,5],rang:[4,8,10,42,45,48],rangl:4,rapidli:4,rate:45,rather:4,ratio:[1,4,8,9,12,42,45,48,58],raw:5,read:[34,39,42,48,64],reader:4,readthedoc:30,real:[4,15,27,41,42,64],realis:42,realist:[34,39],realiti:4,realiz:[4,5,8,9,12,29,33,39,42,48,50],rearaudi:41,rearrang:40,rec:4,receiv:[5,9,12,17,30,34,39,41,45,49,61,62,63],recent:[4,34,39],receptor:4,receveir:58,reciproc:4,recogn:[42,49],recommand:[8,18,34,39,42,48],recommend:[30,33,34,39,45],recov:57,recp:[8,42,48,49],rectangular:[34,39,42,50,58,59],rectilinear:5,recurs:[33,39],red:[14,58],redo:[33,34,39],reduc:[4,5,29],reduct:29,ref:4,refer:[0,4,6,8,14,30,41,42,45,48,50,57,58,61,64],refernc:45,reflect:[0,2,5,7,8,27,41,42,44,45,48,50,58,62,63],reflector:41,refract:4,refresh:[42,49],regard:[29,63],regardless:4,regress:45,reiniti:[38,40,55,56],rel:[4,5,41,42,50],relat:[1,4,6,41,44,45,63],relation_snel:4,relationship:4,relax:4,relev:[8,42,48],reli:2,remain:5,rememb:59,remesh:[28,30,57],remov:[8,34,39,46,48,51,54,56,59],renam:[8,48,58,59],render:[34,38,39,42,50],repair:[0,8,9,12,28,30,42,48],repeat:[8,11,42,48,57,58,59,64],replac:[8,9,12,42,48],repres:[4,8,15,23,42,48,50],represent:[0,1,4,15,17,34,37,39,41,42,50,58],requir:[4,5,8,9,12,16,18,29,42,46,48,59],resampl:59,research:[0,29,30],reset:[4,14,15,42],residenti:[42,50],resist:41,resiz:40,resolut:[29,37,39,42,50],resourc:[8,42,48,57,58,59],respect:[4,5,41,42,48,58],respons:[8,23,42,45,48],ressourc:57,rest:4,restart:[34,39],restor:[33,38,39],result:[4,5,9,17,28,30,40,58,61,64],resum:[0,59],retain:57,rev:44,reverber:[0,1,4,6,8,27,30,42,44,48,50,59,62,63],revers:4,revu:44,reward:[54,56],rho_0:4,right:[4,8,9,15,16,17,37,39,41,42,43,45,47,48,50,57,58,59,63],rise:57,road:[8,10,42,48,57,58],robin:57,room:[0,1,4,6,8,27,29,30,42,44,48,50,57,62,63],root:[8,42,48,50,57,58,59],rotat:[0,4,30,37,39,42,50,55,56],rough:[4,42,44,50],round:57,row:[8,32,42,48,50,58],rows5:58,rpi:[42,49],rs_cut:58,rt15:[7,45],rt30:[7,62,63],rule:61,run:[8,9,11,14,28,46,57,58,59],s0003682x01000548:44,s0003682x99000560:44,s0003:44,s01:57,s02:57,s03:57,s31:1,s_c:4,s_x:4,s_y:4,s_z:4,sabin:[6,7,42,44,48,59],sai:[4,5],sal01:[4,44],salomon:44,same:[4,5,8,11,28,34,39,41,42,46,48,50,57,58,59,61,64],satisfactori:5,satisfi:4,save:[14,34,37,39,52,56,59],scale:[4,44],scaterr:58,scatter:[0,1,2,4,5,7,27,41,42,44,50],scene:[0,4,7,8,9,12,18,29,30,31,34,37,39,40,41,47,48,49,57],sch65:[8,42,44,48],schemat:4,schroeder:[7,42,44,45,59],scienc:44,sciencedirect:44,scientif:64,screen:[31,38,39,40],screenchot:30,screenshot:[57,58,59],screenshot_tutorial_1:59,script:[0,14,42,46],scroll:42,sctar:63,secn:[],second:[2,4,8,31,42,48,50,57,58,59],section:[4,31,41,42,49,50,62,63],see:[2,4,5,6,8,9,12,18,28,29,31,33,34,36,39,41,42,48,49,50,53,56,58,62,64],seem:[29,63],seen:4,select:[0,8,9,10,13,15,16,17,26,30,31,34,37,39,40,42,43,46,48,50,51,53,57,58,59,61],semicolon:[8,42,48],send:59,sens:23,sensit:[28,29,34,39],separ:[4,17],sepctrum:41,sequenti:[42,49],seri:[8,15,42,48,64],set:[26,27,33,34,38,39,41,42,45,49,50,57,58,59,61],setup:30,sever:[4,5,8,15,18,27,31,37,39,40,41,42,48,50,57,58,59,63],shape:[4,5,28,29,59],shift:41,shock:5,should:[2,4,23,29,37,39,42,57,58,59,61],show:[4,8,11,15,17,34,37,39,42,48,50,57,58,59,62,63,64],shown:[4,29],side:41,sidereflector:41,sigma_:4,significantli:5,similar:[4,5,42,50],simpa:[2,6,12,14,23,26,28,29,32,33,34,35,36,37,39,40,41,45,46,49,50,57,58,59,61,62,63,64],simpl:[4,28,29,34,39,59],simplement:4,simpler:58,simplest:4,simpli:4,simplifi:28,simul:[2,4,5,6,8,30,40,42,44,48,57,58,59,61,62,64],simultan:[4,45,57,61],sin:4,sinc:[2,4,5,33,39,41,57,58,59,61,62,63],singl:[4,6,34,39,57,58,62],site:46,situ:4,situat:[4,42,48],sixth:6,size:[4,8,29,42,48,50,57,59],sketch:26,slider:[51,56,57],slope:45,slow:[33,39],small:[4,5,8,42,48,64],smooth:17,snell:4,societi:44,softwar:[0,4,5,26,27,28,29,30,34,39,41,42,50,57,58,59],soil:4,solid:4,solut:4,solv:[29,57],some:[3,4,5,8,9,11,12,28,29,30,34,39,42,48,50,57,59,64],someth:28,sometim:4,son:44,sonor:[1,2],sound:[1,2,6,23,27,29,30,32,34,39,41,44,61,62,63],soundmap:[8,42,48,58],sourc:[2,5,26,30,34,39,41,61,62,63,64],space:[1,4,5,15,42,44,48,50,63],spars:[42,50],spatial:[8,30,32,42,48],spatialis:30,speak:4,speaker_s1:57,speakerwindow:41,special:5,specif:[8,11,18,37,39,42,48,49,50,57,58,59],specifi:[15,32,37,39,42],spectrum:[0,7,8,30,42,48,50,58,59],specular:[5,7,27,41,58],specularli:4,speech:[42,50],speed:[0,4,5,34,39],sphere:[4,8,9,12,42,48],spheric:[4,5,44],spl:[4,8,42,48,58,59],spl_:4,spl_global:4,spl_receiver_surfac:4,spl_recepteur_volumiqu:4,split:[34,39],spp:[0,2,5,6,7,9,12,18,28,30,34,39,41,49,50,62,63,64],spread:4,spreadshe:58,spreadsheet:[0,16,58,59],springer:44,sqrt:4,squar:[4,5,23,45],stabl:4,stage:[8,41,42,48,57],stair:[41,57],stamp:14,stan:44,stand:1,standard:[4,30],standardis:4,stanford:0,start:[8,13,28,32,34,39,42,46,48,49,57,58,59],state:[0,4,45],stationari:[0,45],statist:[4,5,8,27,42,48,50],ste:27,steadi:45,step:[2,4,5,8,26,30,32,34,39,42,48,50,54,56,57,58,59,63],stereolithographi:0,still:[28,29],stl:[0,34,39],stochast:4,stop:[36,39,54,56,59],straight:[2,5],strength:[0,5,8,42,48],string:41,strong:4,structur:[0,42,49],studi:[4,5,8,30,42,48,50],studio:0,style:[15,42],sub:[0,41],subfold:[42,46,61],subject:[45,57],subsoil:4,suburb:[42,50],success:[4,5,27,29,64],suffici:[4,5],suggest:[8,42,48,57,58],sum:[4,5,8,42,48],sum_:[4,45],sum_i:4,sun:44,support:[0,5,8,41,42,48],suppos:5,surc:[42,50],surf:4,surfac:[1,5,7,9,12,17,27,28,30,31,34,37,39,41,44,45,49,53,56,62,63],sutherland:44,symbol:[4,42],system:[0,5,42,44,49],t15:45,t_S:45,t_e:45,t_i:4,t_k:4,tab:[8,11,14,15,17,28,30,38,39,40,47,57,58,59],tabl:[8,15,42,48,58,59],tabul:[42,49],take:[4,41,42,50,61],taken:[4,45],tangent:4,target:[42,50],task:57,tau:[4,41,45],taylorfr:44,tcr:[0,6,7,8,30,62],teach:[30,57,58],techniqu:29,temperatur:[4,42,50,62],tempor:[8,23,30,42,45,48],term:[4,5,46,62],test:[4,8,9,12,30,42,48],tetgen:[0,8,9,12,29,30,36,39,42,48],tetrahedr:[0,5,8,9,12,42,48],tetrahedron:30,text:[4,14,15,30,34,39,41,42,50,63],textur:[34,37,39],than:[4,5,8,41,42,45,48,57,58],theater:57,thei:[4,5,47],them:[58,59],themat:30,theoret:[4,42,50],theori:[0,4,5,6,27,30,42,44,48,59,62,63],thereaft:4,therefor:[4,5,57],thermal:4,thermodynam:4,theses:44,thesi:44,theta:[4,8,42,45,48],theta_1:4,theta_i:4,thi:[0,2,3,4,5,8,9,10,12,16,18,23,26,27,28,29,30,34,36,37,38,39,41,42,43,45,48,49,50,57,58,59,60,61,62,63],thierri:44,third:[4,8,10,41,42,48,61],those:4,thousand:[5,8,42,48],three:[4,8,30,33,39,40,41,42,44,48,57,58],thrid:[8,10,42,48],through:[4,8,42,48,58],throughout:4,thu:[4,5,8,11,42,45,48,58],time:[0,2,4,5,6,14,18,23,29,30,34,39,44,46,50,54,56,57,58,59,62,63],times10:45,to70:45,togeth:41,too:[4,28],tool:[0,14,46,57,58],toolbar:[8,28,30,36,39,40,42,47,48,50,57,58,59],toolbox:0,topographi:4,topolog:[8,9,12,28,42,48],total:[4,5,7,8,27,29,42,45,48,50,59,61,63],tow:58,toward:[4,45],trace:[0,2,5,30,42,44,48],track:[2,5],tradit:5,trajectori:[2,5],trans_ro1:58,trans_room:58,transfer:[1,4],transform:4,translat:[0,4,29,30,42,50,58],transmiss:[0,5,8,41,42,48,58],transmit:[2,4,5],transpar:[17,34,39,41,58],transport:44,travel:4,treat:5,treatment:[4,14,42,49],tree:[0,16,37,39,41,43,47,49,53,56,57,58,59],trevor:44,triangul:[8,9,12,29,42,48],triangular:[34,39,59],troubl:28,tube:[1,4],turbul:[4,5],turn:4,tutori:[26,30,57,59],tutorial_1:59,tutorial_2:57,tutorial_3:58,tuturi:58,two:[0,2,4,5,8,14,27,29,30,32,34,38,39,40,41,42,45,48,49,50,57,58,63,64],txt:[38,39,41,42,49,57],type:[4,23,29,42,49,50],typic:[4,23],uncertainti:64,unchang:[34,39],uncheck:[8,9,10,12,41,42,48,57,58],under:[4,5,45],underlin:59,understand:[31,41,42,50],understood:4,underwai:4,undo:[33,34,39],undock:40,unfavor:[4,42,50],unfold:[57,58,59],unidirectionn:[42,50],uniform:[41,58,61,62,63],uniformli:[4,44,61],unilater:41,uninstal:46,unit:[4,27,42,44,50,57],unlik:5,unoccupi:45,unselect:[8,10,42,48],until:[2,5,8,42,48],unwant:29,updat:61,upon:2,upper:41,upward:4,urbain:44,urban:[4,42,48,50],url:44,use:[0,4,11,29,30,34,37,39,41,42,48,50,57,58,59,64],used:[4,5,8,9,10,12,18,27,29,34,39,41,42,44,48,49,50,57,58,59,61],useful:[0,8,9,12,29,34,38,39,42,48,50,61],user:[0,7,8,9,12,14,15,16,28,29,32,34,37,39,40,41,42,43,48,49,50,57,58,59,61],uses:41,using:[0,1,4,7,8,9,10,12,14,15,18,28,29,30,34,36,37,38,39,42,44,45,47,48,50,57,58,59,61,63],usual:[4,8,42,48],v_c:4,v_i:4,v_x:4,v_y:4,v_z:4,valid:[2,3,4,42,57,62,64],validation_atmospheric_absorpt:62,valu:[4,5,8,9,12,15,16,17,27,41,42,48,50,53,56,57,58,59,61,63,64],varepsilon_0:4,varepsilon_i:4,vari:[0,2,4,5,45,59],variabl:4,variant:1,variat:[4,8,42,48],vector:[0,32,42,50],vehicl:[0,30],veloc:[0,5],ventilationgrid:41,veri:[0,4,5,8,28,34,39,42,48,50,59,61,62],verif:[30,64],verifi:[4,28,57,58,64],verification_diffusion_encrowd:4,version:[5,30],vertex:[42,50,57],vertic:[4,29,42,50,57],veryfavor:[42,50],via:[5,44,57],vibratori:4,vicin:4,view:[4,30,31,34,40,41,42,47,50,51,53,57,58,59],virtual:44,visibl:[4,16,37,39,57],visit:[2,6,30],visual:[37,39],vm00:[4,44],volum:[0,5,8,9,12,27,29,30,44,48,57,63],volumetr:5,w_d:4,w_i:4,w_k:4,w_r:4,w_t:4,wai:[0,4,5,28,34,39,59,61],wait:46,wall:[5,8,27,30,41,42,48,58,59,62,63],want:[8,17,29,34,39,42,48,58,61],warn:14,water:[42,50],wave:[1,4,5],wavelength:4,web:46,websit:[2,6,30,35,39,41],weight:[4,5,45],weigth:61,weihgt:[8,42,48],well:[0,4,5,29,30,32,37,39,42,48,50,57,61],were:[3,58],what:[28,64],whatev:4,wheat:[42,50],when:[4,5,8,18,27,28,29,34,38,39,42,45,47,48,49,58,59,62,63],where:[4,5,45,63],whether:4,which:[4,5,8,29,41,42,45,48,58,61],white:[57,59],whole:[2,8,10,42,48,59,63],whose:[4,5,8,42,48],wide:4,widh:[42,50],width:[42,50,59],wiki:30,wind:4,window:[15,16,17,28,30,38,40,50,53,56,57,58,59,61],winodw:[8,42,48],within:[0,2,4,12,18,28,29,30,34,36,39,40,45,46,47,49,50,58,59,61,62],without:[4,5,29,34,39,55,56,57,58,59,61],word:29,work:[4,29],workbench:26,workshop:44,workstat:58,would:[4,8,9,12,42,48],write:[4,30],written:4,wrong:[42,50],wrongli:4,www:[1,44,57,62,63],xi_i:4,xi_n:4,xiangyang:44,xkaj02:[4,44],xls:57,xlsx:[62,63],xml:[42,49],xoi:4,xoz:4,xyz:[37,39,42,50],yet:4,you:[0,8,11,14,28,29,30,31,34,39,40,42,46,48,50,57,58,59,61],your:[0,28,30,46,57],yoz:4,z_0:4,zeng:44,zero:4,zeta:4,zone:[0,4,8,30,48],zoom:[15,37,39,42,55,56]},titles:["I-Simpa overview","Standards","Presentation","Verification of the SPPS code","Modelling of physical phenomena in SPPS","General principle","Presentation","Calculation code characteristics","Using SPPS within I-Simpa","Using TCR within I-Simpa","Frequency bands","Job list","Meshing","Run calculation","\u2018Console\u2019 window","Creating charts","Define a position using the pointer on the 3D view","Define the properties of a sound map","I-Simpa errors and messages","Surface receiver file file .csbin","Tabulated data file .gabe","Punctual receiver file .gap","Animated data file .pbin","Punctual Receiver data file .recp","Sound level per source .recps","Animated intensity file .rpi","Create a room with FreeCAD","Glossary","Recommendations to import a 3D scene","Import options","I-Simpa User Guide","\u2018Main\u2019 window","Manipulating sources and receivers","<no title>","<no title>","<no title>","<no title>","\u2018Face\u2019","\u20183D View\u2019","Menus","GUI Presentation","Project database","\u2018Project\u2019 window","\u2018Properties\u2019 window","References","Room acoustics parameters","(Un)Setup","Surface selection","\u2018Calculation\u2019 tab","\u2018Results\u2019 tab","\u2018Scene\u2019 tab","<no title>","<no title>","<no title>","<no title>","<no title>","Toolbars","Study of the Elmia hall","Study of an industrial room","Study of a teaching room","Using directivity","Define and use spectrum","Atmospheric absorption implementation verification","Clarity calculation implementation validation","<no title>"],titleterms:{"export":[37,39],"function":42,"import":[28,29,57,58],"new":59,Use:28,Using:[8,9,42,48,60,61],absorbing_materi:58,absorpt:[4,62],acoust:[0,1,4,7,8,45,48,58],advanc:[8,48],anim:[22,25],appar:45,approxim:57,asw:45,atmospher:[4,62],auditor:45,averag:29,bad:57,band:[8,10,42,48],bodi:26,calcul:[0,4,7,8,9,13,18,42,48,57,58,59,63],camera:[37,39,56],can:18,centr:45,chang:58,characterist:7,chart:[15,42],clariti:[45,63],code:[3,5,7,59],collaps:42,color:[37,39],common:42,comput:[8,42,48],concept:5,configur:[42,50],consol:[14,38,39],contextu:41,copi:42,correct:29,creat:[15,26,32,42,57,59],creation:[28,59,61],criteria:45,csbin:19,curv:[8,48],data:[20,22,23,42,50,61,62,63],databas:[41,42,50],decai:45,defin:[16,17,57,58,59,61],definit:[42,45,61],densiti:4,descript:[4,18],diffus:4,direct:[4,41,60],displai:[42,50],dissip:4,done:18,duplic:58,earli:45,edit:39,edt:45,effect:58,element:42,elementari:4,elmia:57,emiss:4,empti:18,energet:4,energi:[4,45],envelop:45,environ:[42,50],error:18,evalu:58,exist:29,expand:42,expect:[45,62,63],explor:57,face:[37,39],featur:[0,42],few:45,file:[18,19,20,21,22,23,25,39,42,49,62,63],fit:[4,42,50,58],floor:26,formal:4,format:[42,49],fraction:45,freecad:26,frequenc:[8,10,42,48],from:[42,45],gabe:20,gap:21,gener:[4,5,61],geometri:[0,57,58,59,62,63],glossari:27,graphic:7,grid:[32,37,39,58],group:[29,32],gui:40,guid:30,hall:57,help:39,hide:[37,39],identifi:28,implement:[62,63],indic:30,industri:58,inform:[42,50,61],insert:58,instal:46,intellig:45,intens:[4,8,25,48],iso:45,job:[11,42,48],keep:29,lambert:4,late:45,later:[4,45],law:4,learn:59,lev:45,level:[4,8,24,45,48],lfc:45,line:[37,39,58],list:[11,42,48],machin:58,main:31,manipul:32,map:17,materi:[0,37,39,41,57,58],menu:[39,41],mesh:[5,8,9,12,29,37,39,42,48,56],messag:18,model:[4,28,29,57,63],more:59,multipl:45,normal:4,observ:4,omnidirectionn:4,one:58,open_door:58,optim:[8,42,48],option:[15,29,42],other:[0,1],overal:4,overview:0,parallelipiped:58,paramet:[7,8,9,42,45,48,58],particl:[4,5],past:42,pbin:22,per:[8,24,48],phenomena:[4,7],physic:[4,7],plane:[4,57,58],point:4,pointer:16,posit:16,power:4,present:[2,6,40,42,62,63],pressur:[4,45],principl:[5,42],probabl:4,problem:28,profil:4,project:[41,42,50,56,61],propag:4,properti:[17,41,43,58],punctual:[8,21,23,42,48,50,57,58,59],rai:5,random:4,receier:[8,48],receiv:[0,4,7,8,19,21,23,32,42,48,50,57,58,59],recommend:28,recp:[23,24],refer:[44,62,63],reflect:4,reiniti:[37,39],remesh:29,remov:42,renam:42,repair:29,represent:7,result:[8,42,48,49,57,59,62,63],reverber:45,room:[26,45,58,59],rotat:32,rpi:25,run:[13,42,48],scene:[28,42,45,50,58,59],schroeder:[8,48],secn:[],select:[47,56],setup:46,simpa:[0,8,9,18,30,42,48],simul:[39,56],singl:45,skech:26,solut:18,sound:[0,4,5,7,8,17,24,42,45,48,50,57,58,59],sourc:[0,4,7,8,24,32,42,45,48,50,57,58,59],spatial:5,spatialis:45,spectrum:[41,61],specular:4,spl:45,spp:[3,4,8,42,48,57,58,59],spreadsheet:42,stage:45,standard:[1,45],step:28,strength:45,structur:[8,48],studi:[57,58,59],support:45,surfac:[0,4,8,19,29,42,47,48,50,57,58,59],t_s:45,tab:[42,48,49,50],tabl:[30,45],tabul:20,tabular:42,tcr:[9,42,48,59],teach:59,tempor:5,test:28,tetgen:28,tetrahedron:18,text:45,theoret:63,time:[8,42,45,48],toolbar:56,trans_materi:58,translat:32,transmiss:4,tree:[8,42,48,50,61],tutori:58,two:59,type:7,unidirect:4,uniform:4,use:61,user:30,using:16,valid:63,valu:45,vector:4,veloc:4,verif:[3,4,62],view:[16,37,38,39,56],volum:[4,42,50,58],wall:4,what:[62,63],width:45,window:[14,31,39,42,43],within:[8,9,42,48],zone:[42,50,58]}}) \ No newline at end of file +Search.setIndex({docnames:["I_Simpa_overview","I_Simpa_standard","code_SPPS","code_SPPS_Validation","code_SPPS_modelling","code_SPPS_principle","code_TCR","code_characteristics","code_configuration_SPPS","code_configuration_TCR","code_configuration_frequency_bands","code_configuration_job","code_configuration_meshing","code_configuration_run","console_window","create_charts","define_position","element_properties_sound_level_results","file_csbin","file_gabe","file_gap","file_pbin","file_recp","file_recps","file_rpi","freecad_create_room","glossary","import_file_recommandations","import_options","index","main_windows","manipulate_sources_receivers","menu_edition","menu_file","menu_help","menu_simulation","menu_view","menu_windows","menus","presentation","project_database","project_window","properties_window","references","room_acoustics_parameters","setup","surface_selection","tab_calculation","tab_results","tab_scene","toolbar_meshing","toolbar_project","toolbar_selection","toolbar_simulation","toolbar_view_camera","toolbars","tutorial_Elmia_hall","tutorial_industrial_hall","tutorial_teaching_room","using_directivity","using_spectrum","validations/validation_atmospheric_absorption","validations/validation_clarity_calculation","validations/validation_notice"],envversion:{"sphinx.domains.c":1,"sphinx.domains.changeset":1,"sphinx.domains.cpp":1,"sphinx.domains.javascript":1,"sphinx.domains.math":2,"sphinx.domains.python":1,"sphinx.domains.rst":1,"sphinx.domains.std":1,"sphinx.ext.intersphinx":1,sphinx:54},filenames:["I_Simpa_overview.rst","I_Simpa_standard.rst","code_SPPS.rst","code_SPPS_Validation.rst","code_SPPS_modelling.rst","code_SPPS_principle.rst","code_TCR.rst","code_characteristics.rst","code_configuration_SPPS.rst","code_configuration_TCR.rst","code_configuration_frequency_bands.rst","code_configuration_job.rst","code_configuration_meshing.rst","code_configuration_run.rst","console_window.rst","create_charts.rst","define_position.rst","element_properties_sound_level_results.rst","file_csbin.rst","file_gabe.rst","file_gap.rst","file_pbin.rst","file_recp.rst","file_recps.rst","file_rpi.rst","freecad_create_room.rst","glossary.rst","import_file_recommandations.rst","import_options.rst","index.rst","main_windows.rst","manipulate_sources_receivers.rst","menu_edition.rst","menu_file.rst","menu_help.rst","menu_simulation.rst","menu_view.rst","menu_windows.rst","menus.rst","presentation.rst","project_database.rst","project_window.rst","properties_window.rst","references.rst","room_acoustics_parameters.rst","setup.rst","surface_selection.rst","tab_calculation.rst","tab_results.rst","tab_scene.rst","toolbar_meshing.rst","toolbar_project.rst","toolbar_selection.rst","toolbar_simulation.rst","toolbar_view_camera.rst","toolbars.rst","tutorial_Elmia_hall.rst","tutorial_industrial_hall.rst","tutorial_teaching_room.rst","using_directivity.rst","using_spectrum.rst","validations\\validation_atmospheric_absorption.rst","validations\\validation_clarity_calculation.rst","validations\\validation_notice.rst"],objects:{},objnames:{},objtypes:{},terms:{"095605219090049q":43,"100db":61,"100hz":[8,10,41,47],"10log":60,"10logfrac":[],"10logtau":[],"10m":[58,62],"1250hz":62,"125hz":[8,10,41,47],"125m\u00b3":62,"128x128x128":28,"145b":43,"1984papm":43,"1\u00e8re":43,"2000hz":60,"2003lema1016":43,"20m":62,"2\u00e8me":43,"2nd":56,"30db":[8,41,47],"30m":62,"3DS":[0,27,33,38],"4000hz":[8,10,41,47],"5000hz":[8,10,41,47],"500hz":60,"5ms":62,"60db":[8,41,47],"682x":43,"6translat":28,"80db":[56,57],"\u00e9galement":[],"\u00e9nergi":[],"\u00e9tude":43,"\u03b1":[41,49],"\u03b4t":2,"\u03b5":2,"\u03bb":[41,49],"carr\u00e9":[],"case":[2,4,5,16,26,41,49,56,57,60],"consid\u00e9r\u00e9":[],"cos\u00b2":7,"d\u00e9finit":[],"default":[4,8,9,12,15,28,33,36,37,38,39,40,41,47,49,57,58,60],"encombr\u00e9":43,"export":[0,8,9,14,15,25,27,33,37,41,47,56,57,58],"final":[8,41,47],"float":[37,38],"function":[0,1,2,4,5,27,28,35,38,45,49],"goto":[3,57,61,62],"import":[0,4,29,33,35,38,40,41,49,60],"int":4,"lat\u00e9ral":[],"long":[4,8,9,12,41,47],"mod\u00e9lis":43,"new":[4,5,8,9,12,14,25,28,29,33,38,40,41,43,45,47,49,51,55,56,57],"not\u00e9":[],"null":4,"poll\u00e8":43,"pond\u00e9rat":[],"pr\u00e9coce":[],"r\u00e9verb\u00e9rat":6,"return":[4,27,36,38,53,55],"short":[4,41,49],"th\u00e9ori":[6,43],"th\u00e9oriqu":43,"throw":5,"transient":4,"true":[41,48],"try":[8,9,12,27,28,41,47],"universit\u00e9":43,"vorl\u00e4nder":43,"while":[4,26,40],Des:[41,43,49],For:[4,5,8,14,28,33,38,40,41,47,48,49,56,57,58],Les:43,N_s:44,One:[0,5,44,57],Such:57,The:[0,2,4,5,6,8,9,12,14,15,22,25,26,28,29,30,32,36,38,39,40,41,43,44,45,47,48,49,50,52,55,56,57,58,60,61,62,63],Then:[4,16,17,25,27,57],There:[0,8,27,28,41,47,57],These:[4,5,41,49,57],Une:[],Use:[0,15,29,41,56],Used:41,Uses:58,Using:[2,4,29,33,38,40,49,56,62],With:[4,5,8,9,12,28,41,47],___date_fold:[],___frequency_fold:[],___intens:[],__acoust:[],__adavanc:[],__advanc:[],__date_fold:[],__frequency_fold:[],__intens:[],__punctual:[],__schroeder:[],__sound:[],_text:[],a_log:[41,49],aaua:43,abil:4,abl:[5,28,33,38],aborbing_materi:57,about:[2,6,27,34,38,57],abov:[4,5,44,57],abs:43,absenc:4,absolut:4,absorb:[2,4,5,8,41,43,47,57,58],absorpt:[0,1,3,5,7,8,9,26,40,41,43,47,49,57,58,62],abt1:56,academ:43,acceler:5,accept:[8,9,12,27,41,47],access:[0,8,41,47,48],accord:[2,4,5,8,14,28,41,47,48,56,57,58,60,61],accound:40,account:[4,40,41,49,60],accur:4,accuraci:[2,5],achiev:4,acouct:[8,41,47],acoust:[5,6,26,27,28,29,33,38,40,41,43,49,56,58,61,62],acoustiqu:[1,43],act:[32,38,57],acta:[43,56],action:[8,9,15,22,30,32,38,41,46,47,48,49,56,58],activ:[0,8,9,12,32,38,41,47,49,57],actual:4,acustica:[43,56],adapt:[28,29],adavanc:[],add:[0,8,9,11,12,41,47,49,60],added:[0,33,38,41,48,56],adding:27,addit:[4,8,9,12,29,33,38,41,44,47,48,56,57,58,62],additionn:[2,8,41,47],additnion:[],adjac:[4,5],adjust:[16,27,40,60],adsab:43,advanc:[41,56,58],advantag:[4,5],aerodynam:4,affect:[4,16,28,33,38,40,41,49,56,58,60],afnor:1,after:[4,5,8,11,27,41,46,47,49,57],afterward:4,again:4,agre:45,agreement:[4,45,61,62,63],air:[4,41,49],algebra:4,algorithm:[0,2],align:[4,15,41,44],aliv:[8,41,47],all:[0,4,5,8,9,10,11,14,15,27,28,33,36,37,38,39,40,41,44,45,46,47,48,49,54,55,56,57,58,60],allow:[4,5,6,8,10,14,26,28,33,36,38,40,41,47,49,50,55,56,57,58],alon:0,along:[2,5,41,49,50,55],alor:[],alpha:[1,4,5,40,41,49,57],alpha_:4,alpha_c:4,alreadi:[8,9,27,41,45,47,49,56,57],also:[0,4,5,8,26,27,28,29,35,36,38,40,41,44,47,49,57,58,62,63],although:[4,5,29,33,38,60],altitud:4,alwai:[4,8,41,47],ambigu:4,america:43,among:4,amount:[2,4],amplitud:[4,5],amta:[8,41,47],analazi:[8,41,47],analog:4,analys:62,analyt:[4,5],angl:[4,5,31,40,41,44,49],ani:[4,5,7,41,49],anim:[0,7,8,33,35,38,41,47,48,53,55,57,58],anoth:[4,41,46,57],antoh:[15,41],antonio:43,anyon:45,apart:4,appear:[4,56,58],appendic:29,appendparam:[],appli:[4,5,8,33,38,41,43,47,57,60],applic:[0,1,4,6,15,29,33,38,40,41,43,49],approach:[0,2,4,5,8,29,41,47,49],approx:40,approxim:[0,4,27,28],aps:43,arbitrari:[4,5,56],archambeau:43,architectural:43,archiv:[41,48],area:[4,41,47,49],aris:22,around:[4,31,36,38,41,49,61,62],arrai:4,arrow:[36,38,41,49],art00004:43,art00007:43,articl:[1,43],artifici:4,arxiv:43,asc:[33,38],ascii:[33,38],aslo:[8,41,47],assign:[8,28,36,38,40,41,47,56,57],assimil:[4,5],associ:[4,8,22,28,36,38,40,41,47,48,49,56,63],assum:[4,57,62],assumin:[],assumpt:[4,63],assur:28,atmospeh:[41,49],atmospher:[0,1,3,5,7,8,9,41,43,47,49,57,62],atmospheric_absorpt:61,atmospheric_absorption_geometri:61,ats:[],att_atmo:4,att_atmos_press:4,att_atmos_pression_i:4,attent:[3,33,38,61,62],attenu:[1,4,43,60,61],attribut:[4,40],audienc:[40,56],auditorium:43,aul85:[4,43],aul86:[4,43],auletta:43,auswertung_phase_ii:56,author:[4,41,49],auto:[41,49],automat:[0,4,8,10,15,31,33,38,40,41,47,48,49,56,57,60],avail:[0,29,33,38,41,46,49,56,57,63],avec:[],averag:[4,5,8,27,29,41,47,56,57],avoid:[4,8,9,12,41,47],awai:4,axi:[4,15,28,31,36,38,41,44,49,56],azimuth:4,b_lin:[41,49],back:[4,17,39],background:[0,33,38,40,41,44,49,60],backup:45,backward:[8,41,47],bad:27,balanc:4,balloon:[41,49],band:[0,4,9,22,29,40,49,56,57,58,60,61,62],bank:28,bar:39,barbri:[4,43],barron:[43,62],base:[0,2,4,5,6,28,29,41,47],basi:[6,26,44],bass:43,beam:5,becaus:57,becom:[4,5],been:[0,35,36,38,56,57,58,60],befor:[4,8,9,12,41,45,47,56,57,60],begin:[4,44],behavior:[61,63],behaviour:4,being:[4,5,44,57],below:[5,8,31,41,44,47,61,62],bend:4,benefit:5,besid:[41,49],beta:[4,40],better:[4,5,27,28],between:[0,2,4,5,8,9,10,12,17,26,27,31,40,41,44,47,49,56,57,60,61,62,63],bewteen:[],bilater:40,bin:[33,38],bl88:[43,62],black:14,blue:14,bmc90:[4,43],bmp:[15,36,38,41],book:[6,28,43],border:[41,49],bork:56,both:[2,4,14,40,58,60],bottom:[8,41,47],boundari:[4,5,40],boutiqu:1,box:[4,28,33,34,36,37,38,40,41,49,50,55,56,57],branch:41,bright:4,brought:4,bruit:1,bspe84:[4,43],build:[0,4,5,8,10,27,28,29,33,38,41,47,56,57],built:[0,27,33,38,40,58,60],bulk:4,burn:43,burns1990:5,button:[15,27,41,46,50,55],c50:[7,44],c80:[7,44,62],c_0:4,c_1:4,c_2:4,c_80:[],c_i:4,c_p:4,cad:[25,27,33,36,38,57,58],calcul:[1,3,5,10,11,12,27,28,29,39,44,48,49,50,55,60,61],call:[4,5,63],camera:[29,30,52,54],can:[0,2,4,5,6,8,9,10,11,12,14,15,16,22,26,27,28,29,30,31,32,33,36,38,39,40,41,42,44,45,46,47,48,49,56,57,58,60,62,63],cancel:[32,38],cannot:4,carlo:[2,5,43],carrefulli:63,carri:[2,4,5,63],catalogue_detail:[1,61,62],catalogue_tc:[1,62],catt:[0,40,56],caus:4,caution:57,caviti:[0,29],cdantonio16:[4,43],cdf_dist_theta:4,cdot:4,ceci:[],ceil:[28,40,56,58],celer:[41,49],celerite_log_lin:4,cell:[15,16,41,58],celsisu:4,center:[4,31,36,38,41,44,49,61],centr:[0,4,8,10,41,47],central:[8,41,47],certain:[4,5],chain:41,chair:57,chamber:[4,5,43],champ:43,chang:[4,8,11,17,28,29,33,36,38,40,41,45,47,48,49,56,58,60,62],charact:41,character:[0,4],characterist:[0,2,4,5,6,27],chart:[8,29,47],check:[4,8,9,10,12,27,40,41,47,49,56,57],chen:43,choic:[4,5,15,41,56],choix_phi:4,choos:[4,5,15,28,40,41,45,47,48,49,56,57,58],choosen:[44,61],chose:4,chosen:[4,5,28,44],chossen:[8,10,41,47],christon:43,cite:5,clariti:[0,3,7,8,29,41,47,49],clarity_calcul:62,clarity_room_1000_m3:62,clarity_room_12000_m3:62,clarity_room_125_m3:62,clarity_room_504_m3:62,clarity_room_8000_m3:62,clarity_valid:62,classic:[0,4,5,6,8,26,29,41,43,44,47,49,58,61],classiqu:6,clear:[11,41,47,58],clic:[36,38],click:[8,9,15,16,17,27,40,41,42,46,47,48,49,56,57,58],clipboard:[15,41],close:[5,27,28,41,47,49,56,57,61],closest:40,cms:56,code:[0,1,2,4,6,8,9,10,12,13,14,27,28,29,33,38,40,41,47,48,49,50,55,56,57,61,62,63],coeffici:[0,1,4,5,7,26,40,41,43,49,57,61,62],col:[31,57],collaps:28,collid:[4,5],collis:[2,4,5,26],color:[14,15,17,27,33,40,41,49,52,55,57],colormap:[33,38,52,55,58],column:[8,31,41,47,49,57],com:[0,43],combin:[44,56],come:[4,39,44],comma:17,comment:[8,41,47],commerci:0,common:[0,4,49],commun:0,compar:[4,5,8,28,41,47,56,57,62],comparaison:63,comparison:[4,43,56,62,63],compat:[25,28,35,38],complet:[5,29],complex:[4,5,7,27,29],complianc:4,complic:[28,43,58],compon:[0,4,39,45],compos:[5,39,40,57],comprehens:1,compromis:[4,5],comput:[5,14,28,29,39,43,45,56,62],computaion:[8,41,47],computationn:[8,41,47],concentr:[4,41,49],concept:[4,29,41,49,63],concern:[0,29,33,34,38,41,49,57,58],concert:[43,44],cond_g:4,cond_p:4,conder:62,condit:[0,4,5,44,61],config:[8,41,47,48],configur:[8,9,15,37,38,47,62],conform:60,confus:[4,57],congest:[4,5],connect:25,conserv:4,consid:[2,4,5,8,10,15,33,38,40,41,44,47,49,57,58,61,62],consider:5,considerig:[41,49],consist:[4,15,41,56],consol:[11,27,29,39,41,47],console_window:[],constant:[2,4,5],constraint:[5,8,9,12,28,41,47,58],constraintrecepteurss:[],construct:[4,5],consum:28,contact:[4,5],contain:[5,8,9,12,14,15,22,26,30,40,41,47,48,49,56,57,58,60],content:[8,41,43,47,48],context:56,contextu:[8,10,15,16,41,42,47,48,49,58],continu:[4,5,27],contour:[0,17,33,36,38,41,49],contrast:5,contribut:[0,4,8,29,41,43,47],control:[8,41,47],convent:[4,5],convers:[41,49],coordin:[4,27,31,41,49,56,57,58],copi:[0,33,38,40,57],coplanar:[46,57],core:[8,41,47],corespond:57,corner:[56,57],correct:[0,4,8,9,12,14,27,33,38,41,47,56,60],correctli:[4,27,57],corresond:[41,49],correspond:[4,8,10,14,15,16,17,22,36,38,39,40,41,42,44,46,47,48,49,52,55,56,57,58,60,63],correspondig:57,correspondng:57,corridor:[4,25,57],cos:[4,7,8,41,44,47],cosgamma2:4,cosin:44,cosinu:40,cost:5,could:[5,8,9,12,41,47,56,62,63],count:[4,41,49],coupl:[4,57],cours:62,cover:[4,25],coverag:1,cox:43,crc:[6,43],creat:[0,8,11,27,28,29,33,38,40,45,47,48,49,51,55,57,60],creation:[0,4,8,25,29,33,38,41,47,49,57],criteria:[],criterion:44,crop:4,cross:4,crowd:4,crucial:4,csbin:[8,41,47,48],csnumber:[1,61,62],csv:58,ctrl:[46,57,58],cube:[0,28,56],cubic:[28,61,62],cumul:[0,4,7,8,41,47,57,58],current:[4,5,28,29,33,38,51,53,55],cursor:[50,55,56],curv:[2,4,5,7,15,17,41,44,58],curvatur:4,cut:[0,41,49],d50:[7,44],d_0:4,d_n:4,d_o:4,dafault:[],dan:[1,43],dash:58,data:[0,4,7,8,14,15,16,30,33,35,38,40,45,46,47,48,52,53,55,57,58,63],data_manag:[],databas:[0,29,56,57,60],date:[29,41,48,49,58],date_fold:[8,41,47],dav:43,dba:7,dband:44,debug:[8,9,12,41,47],debugmod:[],decai:[8,41,43,47],decemb:56,decibel:[44,60],decid:28,decompos:[5,28],decreas:[2,4,5,8,41,47,61],deduc:4,deeper:62,defin:[0,4,5,7,8,9,10,12,28,29,31,33,37,38,40,41,44,47,49],definit:[0,4,7,8,29,40,43,47,48,56,58],deform:28,degre:[31,41,49],deinit:[],delai:[0,41,49],delaunai:[0,8,9,12,41,47],delet:[14,37,38,41,48,58],delimit:28,delta:4,denot:4,dens:[41,49],densite_energie_recepteur_volumiqu:4,densiti:[5,40],depen:56,depend:[2,4,5,8,28,29,30,41,47,48,49,57,62],deriv:4,des:43,desactiv:[0,41,49],descart:4,descri:41,describ:[4,26,40],descript:[0,5,40,41,43,49],descritpion:[41,49],deselect:58,design:[0,25,43],desktop:45,despit:4,destin:[41,49,57],detail:[2,28,30,41,49,56],detect:[0,41,49],determin:[1,4,5,43,44],determinist:[4,5],develop:[0,14,29,43],deviat:[4,61,63],devic:60,diagram:58,dialog:[28,33,36,37,38,40,41,49,50,55],diff_wal:57,differ:[4,5,8,27,29,33,38,41,47,57,58,63],differenti:28,difficult:[4,8,41,47,62,63],difficulti:5,diffract:4,diffu:[4,43],diffus:[0,1,2,5,6,7,8,26,40,41,43,47,49,57,58,61,62],digit:5,dimens:[0,4],dimension:29,dirac:4,direct:[0,1,5,7,8,29,31,41,44,47,49,57,58,62],directli:[4,22,41,48,57,58],disabl:[8,32,33,38,41,47,49],disadvantag:5,disapear:[8,41,47],disappear:[2,4,5],disbal:[41,49],discret:4,disk:[41,48,58],dispers:[4,5],displac:4,displai:[0,7,8,9,14,15,17,27,30,33,36,38,40,42,47,50,52,54,55,56,57,58],dispoi:4,dispos:56,distanc:[4,5,26,28,44,56,62],distinct:4,distinguish:4,distribut:[0,4,5,29,41,49,60],divid:[4,56],doc:[56,57,58,61,62],dockabl:[30,39],document:[4,8,9,12,14,29,34,38,40,41,47,48,57,59],doe:[4,5],doi:43,domain:[2,4,5,8,9,12,29,41,44,47,49],don:28,done:[0,8,10,27,41,47,49,56,57,60],door:57,door_room1:57,door_room2:57,dot:[15,41],doubl:[8,9,41,42,46,47,48,57,58],down:[32,36,38,56],download:45,downward:4,drag:[41,49,57],drastic:[8,41,47],draw:[4,5,63],drop:[41,49,56,57],due:[4,5,27,28,61,63],duplic:[4,29,41],durat:[5,8,41,47],dure:[0,1,2,4,5,8,27,33,38,40,41,45,47,61],dynam:[0,5,41,49],each:[2,4,5,8,9,11,28,33,38,40,41,44,47,48,49,56,57,58,60,61,62],ear:44,earli:[0,8,41,47,62],eas01:[4,43],easi:4,easili:[0,4,29,41,49],eceiv:22,echogram:[8,41,47,57],echogramm:[7,56],eckard:43,edg:[7,8,9,12,28,41,47],edit:[6,29,39,40],editor:41,edt:[7,8,41,47],edu:43,educ:[0,29],effect:[4,7,8,28,29,33,38,40,41,43,47,49,56,61,62],effici:0,either:[4,5,40,41,42,49,57,58,60],electron:43,element:[0,4,7,8,10,16,17,28,30,33,36,38,40,42,43,46,47,49,56,57,58,60],elementari:5,ell_i:4,ellipt:28,elmia:[29,57],elmia_coordin:56,elmia_stag:56,elmiahal:56,email:[],embed:[2,6,8,9,29,41,47],embrecht:43,emiss:[5,40],emit:[2,4,5],emphas:4,empti:[33,38],enabl:[8,9,33,38,41,47,49,52,55,58],enclosur:43,encount:4,encumbr:5,end:[4,8,41,44,47],energet:[2,5,8,29,41,47,56,57],energi:[0,2,5,8,41,43,47],energie_elementair:4,engin:[29,43],english:29,enhanc:28,enough:[4,56],ensur:5,enter:[4,58],entir:4,entiti:1,environ:[0,4,5,29,44,57],environment:[0,29,41,49],environn:1,epsilon:[4,5],epsilon_i:4,eq_flux_1:4,eq_flux_1b:4,equal:[4,40,44,62],equat:28,equiprob:4,equiv:4,equival:[0,2,4,8,22,41,47,58],erali:[],ergod:43,error:[0,27,29,62],especi:4,essai:[],essenti:[32,38],est:[],estim:[26,62],etc:[4,63],evalu:[6,8,28,29,41,44,47,62],evan:43,even:[4,5],eventu:41,everyth:4,evid:43,evolut:[4,5,8,22,41,47],evolv:4,exact:[4,43],exactli:[4,5,11,41,47,48,57,58],exampl:[4,5,8,9,12,14,17,28,31,37,38,40,41,47,48,49,56,57,58,60,63],excatli:63,exce:4,excel:4,except:[4,33,38,41],exchang:4,execut:[8,41,47],exist:[4,29,33,38,41,49,56],exit:[33,38],exp:[4,62],expect:[4,63],expectd:[],experi:60,experienc:[8,41,47],experiment:[4,40,59,63],explor:[29,41,48,58],express:[4,44],extend:[0,29],extens:[0,40,41,48],exterior:[36,38],exteriorwal:40,extern:[4,14,41,48],extinct:[8,41,47,57],extract:[30,52,55],extrapol:40,extrem:60,eyes:[36,38],eyr:[7,41,47,58,62],fa043864:1,fa104093:1,fa138697:1,fa138698:1,fa138699:1,fa148722:1,fa169343:1,face:[4,5,7,16,17,27,28,30,33,40,41,46,49,52,54,55,56,57,58],fachabteilungen:56,fact:[4,27],factor:4,factori:43,famili:1,farm:[41,49],favor:[4,41,44,49],favour:44,fdc:4,fdci:4,fdci_2:4,featur:[14,29,40,59],few:[8,41,47,60],field:[1,4,5,6,7,8,26,28,40,41,43,47,57,58,61,62],fig:4,figur:[4,61,62],fiit:57,file:[0,8,14,15,17,27,29,33,34,36,37,39,40,45,47,56,57,58],filenam:[33,38,51,55],fill:[15,16,41,49,56],find:[27,57],finish:[56,57],fiorst:58,first:[2,4,5,8,25,36,38,41,44,47,49,53,54,55,56,57,58,60],fit:[0,7,8,26,28,29,43,47],fix:[8,41,47,63],floor:[40,41,49,56,57,58],flow:4,focu:[41,49],folder:[8,17,33,38,40,41,45,47,48,49,56,57,58,60,61,62],follow:[2,4,6,8,9,28,29,36,38,39,41,44,47,49,56,57,58,61,62,63],follw:44,font:[33,38],forest:4,form:[4,5,8,15,41,44,47,49,57],format:[14,22,33,36,38,40,56],formul:4,formula:[4,41,47,49,58,62],forward:[53,55],found:4,frac:[4,44,62],fraction:[0,4,8,41,47],frame:4,franc:43,free:[0,1,4,25,26,41,49,57],freeli:0,french:[2,6],frequenc:[0,1,4,9,22,29,33,38,40,44,49,56,57,58,60,61,62],frequency_fold:[],fresnel:4,from:[0,2,4,5,6,8,14,22,28,29,33,38,40,45,46,47,48,49,50,53,55,56,57,58,60,62],front:[17,41],frquenci:40,full:40,fulli:0,functionn:0,funtion:4,further:4,furthermor:40,fusion:28,g_n:44,gabe:[41,48],gain:57,gamma_1:4,gamma_2:4,gap:[8,41,47,48],garden:[41,49],gas:5,gener:[1,2,8,9,12,26,27,28,29,33,34,35,36,38,41,44,47,48,49,50,55,56,58],geo_sourc:4,geometr:[2,4,5,29,41,47,49,56,57],geometri:[4,5,16,27,28,29,33,35,36,38,41,49],geq:40,github:[0,29],give:[0,4,41,44,49,56,58,60],given:[0,4,5,8,9,11,12,22,28,33,36,38,40,41,42,44,47,49,56,57,58,60,62],global:[4,8,9,40,41,47,56,57,58,60],glossari:29,goal:[56,58],good:[4,8,28,41,47,58,61],got:27,govern:4,gradient:[4,41,49],gradual:5,graphic:[0,8,15,17,29,39,41,45,47,49,58],grass:4,graviti:44,greater:[4,40],green:57,grid:[4,29,41,49,56],ground:[4,5,41,44,49,57],group:[0,1,4,29,33,38,40,41,46,49,52,55,56,57,58],gtart:41,guarante:25,gui:29,guid:[40,41,49],guidelin:1,habitat:[41,49],half:4,hall:[4,29,41,44,49,57],hand:[4,5,8,10,41,47,58],handli:[41,49],hard:[41,48,58],harvard:43,has:[0,4,16,33,36,38,40,56,57],hat:4,have:[0,4,5,8,11,27,28,29,31,33,35,38,41,45,47,49,56,57,58,62,63],head:[36,38],heat:4,hedg:[41,49],height:[4,5,25,41,49,56,58],heinrich:[6,43],help:[0,29,39],here:[0,3,5,33,38,52,55,57,58],hide:[15,33,41],high:28,higher:[4,61],highlight:[46,57],histori:[33,38],hod91:[4,43],hodgson:43,hold:[57,58],hole:27,homogen:[4,5,22,41,49],horizont:4,host:29,how:[4,30,40,41,49],howev:[4,41,49,56,57,60],htm:[1,61,62],html:[1,41,48,56],http:[0,1,29,43,45,56,61,62],humid:[4,41,49,61],hundr:[5,8,41,47],huygen:4,hybrid:[4,43,62],hygrometri:4,hypothesi:[41,47,61],i_0:4,icon:[27,45,56,58],ident:[4,57,63],identifi:[29,57,58],ifsttar:[0,34,38,45],iint:4,illustr:[4,29,57],illustration_refract:4,imag:[5,15,41,56,57,58,61],immedi:4,impact:62,imped:[1,4],implement:[3,4,5],impos:4,imposs:4,impress:56,imprt:27,impuls:[8,22,41,44,47],inc:43,inceras:[8,41,47],incid:[1,4,40,43],includ:[29,41,44,45,47,57],increas:[4,8,17,28,33,38,41,47,56,61],inde:[4,5,28],independ:[4,37,38,39,57],index:[4,27,29],indic:[8,26,41,47,49],individu:[4,8,41,47,57,62],industri:[0,4,5,29,41,49],industrial_hal:57,industriel:43,infin:5,infinit:4,infinitesim:5,influenc:4,inform:[0,2,6,8,9,12,14,27,29,34,37,38,40,47,48,56,57],infti:[4,44],ingentaconnect:43,ingolf:56,inher:61,initi:[0,2,4,5,8,41,44,47,57,58,63],initla:[15,41],insert:[4,29],insid:[2,5,8,27,36,38,41,47,49],instabl:63,instal:[29,56,57,58,61,62],instantan:[7,8,22,41,44,47],instantanea:58,instead:[11,28,41,44,47,57,58,63],instruct:[8,9,29,41,47],int_0:[4,44],int_:[4,44],int_t:44,integ:4,integr:[0,8,41,44,47],intellig:[29,41,49],intend:56,intens:[0,5,7,41,48],interact:[4,5,8,41,47],interdepend:60,interest:[4,8,41,47,49,56,57,60],interfac:[0,22,29,33,37,38,39,41,48,49],interior:[0,29,36,38,54,55,56],interiorwal:40,intern:[4,14,57,58],interpret:[41,48],intersect:[27,56],interv:44,introduc:4,invers:[4,41,49],involv:5,irregular:[4,56],isareaconstraint:[],isbn:43,ismaxvol:[],iso:[0,1,4,17,33,38,61,62],iso_catalogu:[1,62],issu:29,item:57,its:[2,4,5,8,36,38,41,47,49,57,58],itself:[4,57],jincai:43,job:[8,9],join:5,journal:43,joy74:[4,43],joy75:[4,43],joy78:[4,43],joyc:43,joyce1974:5,jpg:[15,36,38,41,56],jun:43,just:[0,33,38,60],justifi:4,keep:[4,8,11,29,33,38,41,47,58],kei:[36,38,57,58],keyboard:57,khz:4,kind:[8,41,47],know:[4,5,40],knowledg:[4,5,44],known:4,kut16:[4,43],kut81:[4,43],kuttruff:[6,43],l_j:44,l_receiver_surfac:4,label:[4,15,33,38,41],laboratori:0,lam96:[4,43],lam:43,lambda:[41,49,57],lambda_c:4,lambert:[40,57],lan:1,langl:4,languag:[1,33,38,45],larg:[4,5,8,28,41,43,47,57],larger:40,last:[4,16,27,32,33,38,40,41,44,49,53,55,57,58,60],late:[0,62],later:[0,7,8,41,47,60],latest:29,latex:5,latter:[4,5],launch:45,law:[0,2,5,40,41,49,57],lawn:[41,49],layer:4,lead:[4,58,60],learn:29,leav:[5,58],lee:43,left:[4,8,9,15,36,38,41,44,46,47,48,58,62],legend:[33,38],lenght:[8,9,12,41,47],length:[4,8,41,47,49,57,58],lepolles2003:[4,5],less:[4,8,41,47],let:4,level:[0,1,6,7,17,28,29,33,38,41,49,56,57,58,60,61],lf80:[7,44],lfc80:[7,44],lfc:[4,7,8,41,47],li8:40,li_recepteur_volumiqu:4,licens:[34,38,45],lie:5,lies:5,lieu:[],like:[4,27,33,38,41,44,49],liken:5,limit:[3,4,5,8,28,41,44,47,56,57,61,62,63],lin82:[4,43],lin:[0,4],lindqvist:43,line:[2,4,5,7,17,29,33,41,49,54,55],linear:[4,8,15,41,44,47],link:[0,34,38,40,41,43,47,49],linoleum:40,list:[8,9,17,33,38,40,49,56,57,58,60],listen:44,literatur:63,llll:4,load:[8,28,33,35,38,41,47,48,56,57,58],local:5,localindustriel:57,locat:[28,37,38,41,44,45,49,56,57,58,61,62],locaux:43,log:[0,4,40,44,62],log_lin:[41,49],logarithm:[4,44],loi_1:4,loi_1b:4,loi_2:4,loi_2b:4,loi_3:4,longer:[5,8,41,47],loss:[0,4,40,57],low:[4,8,41,47,49],lower:[4,40],lp03:[4,43],machin:[4,5,29],macroscop:4,made:[4,5,41,48,49,57],magnitud:4,mai:[2,4,5,8,9,12,28,29,41,47,49,57,58,63],main:[2,4,5,6,27,29,33,37,38,39,43,56,58],mainli:[2,8,41,47,56,61],major:[4,5],make:[4,5,22,27,56],maltbi:43,man_pag:17,manag:[5,41,49],mani:[0,4,8,16,26,27,29,41,47,49,57],manipul:[0,29,41,49,56,58,60],manual:[11,41,47,49],map:[0,4,7,33,38,41,49,57,58],march:[0,28],marker:4,mass:40,mat:[33,38],materi:[4,5,26,27,28,29,33,41,49,58],material_catt:56,math:5,mathbf:4,mathemat:4,maximum:[4,8,9,12,17,28,41,47,56,58],maximun:[8,9,12,41,47],maxvol:[],mean:[0,4,8,14,26,27,33,38,41,47,49,57,58,62,63],measur:[1,4,41,43,44,49,56,62],mechan:4,medium:[4,5],memori:[8,41,47,48,53,55],mention:4,menu:[8,10,14,15,16,29,30,33,36,39,41,42,45,47,48,49,52,55,56,57,58],merg:[36,38],mesh:[0,27,29,33,35,49,50,56,57,58],messag:[11,14,34,37,38,39,41,47,52,55],meteorol:[41,49],meteorolog:[0,5,7,41,49],meter:[4,26],method:[0,1,2,4,5,8,28,41,43,47,49,56,57,63],michael:43,micrometeorolog:4,milieu:43,mill:57,million:56,mind:4,minim:4,minimum:[17,58],minor:28,minratio:[],misalign:56,miss:56,mistak:29,mode:[4,5,8,9,12,15,30,36,38,41,46,47,49,52,54,55,57,58],model:[0,2,5,8,25,26,29,30,40,41,43,47,49,57,61,63],moder:4,modif:[33,38],modifi:[8,9,12,28,40,41,45,47,48,49,57,60],modifii:17,molecular:4,moment:4,mommertz:43,monitor:5,mont:[2,5,43],more:[0,4,5,6,8,9,12,28,29,33,38,41,44,46,47,48,49,56,57,60],moreov:5,most:[5,8,15,22,33,38,41,44,47,49,58,62],motion:[36,38],mous:[15,36,38,41],move:[4,36,38,39,46,50,55,56,57,58],movement:4,multi:44,multipl:[8,41,46,47],multithread:[8,41,47],multitud:5,murrai:43,must:[0,4,33,38,40,41,47,49,56,57,60],mutual:5,n_0:4,n_c:4,n_n:4,name:[1,4,8,15,17,41,47,48,49,56,57,58],nativ:[41,48],natur:[2,8,41,47],navig:30,necessari:[4,5,8,27,40,41,45,47,49,57,60,62],necessarili:63,need:[8,28,33,38,41,47,48,49,56],neg:4,netherland:43,nevertheless:[4,5],next:[4,8,9,41,47,53,55,56,57],nff:[33,38],nimber:[8,41,47],nitrogen:4,niveaux:1,nois:[0,1,5,29,40,41,49,56,57,58,60],noisemap:[0,56],noisi:57,non:[4,28],none:[4,28,36,38],nonumb:44,nor:4,norm:[1,4],normal:[40,41,44,49],notat:4,note:[4,8,27,28,40,41,44,47,49,56,63],noth:4,notic:56,notion:4,novemb:[43,56],now:57,nu_c:4,number:[2,4,5,8,26,28,31,41,44,47,49,56,57,63],numer:[0,4,5,6,28,29,33,38,40,41,47,49,56,61,62,63],ob89:[4,43],object:[0,2,4,5,8,26,27,41,44,47,49,57],observ:[29,44,63],obstacl:[4,5,26,57],obstruct:4,obtain:[0,4,6,8,41,44,47,56,60],obviou:60,obvious:4,ocav:[8,10,41,47],occur:[2,4,5,8,41,47,49],octav:[4,8,10,40,41,47,56,57,60],octob:43,odeon:[0,40],offer:28,offic:[2,6,29],offici:29,offlin:29,often:57,old:28,omega:4,omnidirect:[4,44,56,58],omnidirectionn:7,omnidrectionn:[41,49],onc:[4,11,15,41,46,47,57,58,62],ondet:[4,43],ondulatori:29,one:[4,5,8,11,28,29,33,37,38,40,41,46,47,49,56,58,60,62],ones:[57,62],onli:[4,5,8,9,11,12,22,26,28,29,32,33,35,36,38,40,41,47,49,56,57,58,60],onlin:[2,29,34,38],opac:[17,41,49],opaqu:17,open:[0,5,8,25,33,34,36,37,38,40,41,47,48,49,50,51,55,56,57,58],oper:[14,22,41,48,56,57],opposit:[40,41,49,57],optic:4,optim:[5,29,56],option:[4,8,14,16,17,27,29,30,32,33,35,36,37,38,39,47,48,49,56,57,58,63],order:[0,4,5,8,9,12,28,33,38,39,40,41,42,47,49,56,57,62,63],org:[1,43,61,62],organ:[0,41,48,49],organis:[41,49],orient:[0,4,15,36,38,41,44,49,56],origin:[0,4,15,28,31,33,36,38,40,41,49,56,57],other:[4,5,11,28,29,37,38,41,44,47,49,57,58,62,63],otherwis:4,our:[4,56,57],oustid:[36,38,56],out:[4,5,15,41,63],outcom:[41,48],outdoor:[1,4,61],outsid:[5,36,38],over:[4,5,60],overview:29,own:[0,36,38,57,58],oxygen:4,p2_:4,p_0:[4,44],p_i:4,p_t:4,packag:28,page:[3,43,61,62],pai:[33,38],paid:[3,61,62],panelwal:40,par:[4,43],paragraph:4,parallel:[5,8,41,47],parallelepiped:[0,33,38,41,49,57],parallelipiped:29,paramat:[8,41,47],paramatr:[8,9,12,41,47],paramet:[0,1,4,11,12,15,28,29,31,49,50,55,56,58,62],parameter:44,parametr:[0,25,41],paramt:[8,9,12,41,47],paremet:[8,41,47,49,56],part:[1,4,5,25,40,56,61,62],parti:43,partial:[4,5,28],particl:[0,2,8,29,33,38,41,43,47,56,57],particul:[2,5],particular:[4,25,33,38,56],particularli:[5,44],partit:4,partli:4,partticl:56,pass:[4,44],past:[8,15,40,47,57],path:[0,4,5,26,41,49,57],pattern:60,paus:[35,38,53,55,58],pbin:[41,48],pdf:[41,48],per:[4,7,40,41,56,57,61],perfeclti:57,perfect:26,perfectli:[4,5,26],perfomr:58,perform:[1,4,5,14,22,41,49,56,57,62],period:4,perpendicular:44,person:[36,38,54,55],pertin:56,pertinent:[],peter:43,phd:43,phenomena:[2,5,8,29,41,47],phenomenon:[4,41,49],phi:4,phonon:[5,43],photon:43,phy:43,physic:[0,2,5,8,29,41,43,47,48,60],physrevd:43,pi_:4,pictur:56,pierci:43,pii:43,pink:57,place:[4,56,57],plai:[35,38,53,55],plan:[41,49],planar:[28,36,38,58],plane:[0,7,29,31,41,49,50,55],plasteredconcret:40,plateform:0,pleas:[28,29,45],pltmg:28,plu:[],ply:[0,33,38,56,57],png:[15,36,38,41,56,57,58,61],point:[0,5,16,22,29,41,44,49,56,57],pointer:[29,30,41,46,49,52,55,58],poisson:4,polar:4,pole:4,poli:[0,33,38],ponder:60,poor:[28,58],pose:4,posit:[0,4,5,29,31,41,44,49,56,57,58,62],possibl:[4,5,8,9,12,15,28,30,33,38,40,41,47,48,49,56,57,61,63],post:0,power:[5,41,49,56,57,58,60],practic:4,pre:[0,5],precis:28,predefin:41,predict:[2,5,29],prefer:[1,4,28,33,38],prejudg:63,premis:5,prepar:57,preprocess:[],presenc:[4,5],present:[0,4,5,29,44,52,55,56,57,58,63],preserv:[4,8,41,47],press:[6,43,46],pressur:[8,22,41,47,49],preview:[],previou:[4,28,29,33,38,56,57,58],principl:[1,2,4,29,43,60],principle_receiver_volumiqu:4,print:4,probabilist:[0,2,5,8,41,47,63],probabl:5,probilist:56,problem:[0,4,27,56],problemn:29,proce:[8,41,47],procedur:[4,5,15,27,28,41,45,52,55,56,57,58],process:[0,4,5,8,41,47,49,56,57],processor:[0,8,41,47],produc:[4,8,28,41,47,48,49],product:4,profession:0,profil:[0,5,41,49],program:[14,41,48],prohibit:5,proj:[33,38,56,57,58,61,62],project:[0,4,11,17,25,27,28,29,32,33,38,39,47,48,51,52,56,57,58,61,62],projet:40,projet_config:[8,41,47,48],propag:[0,1,2,5,8,29,40,41,43,44,47,49,61],properti:[0,1,5,15,16,27,28,29,36,38,39,41,49,56,58,60],proport:[4,5],proportion:5,propos:[0,3,4,5,6,28,29,36,38,41,44,49,56,61,62],propto:4,provid:[4,8,41,47,56,57],ptb:56,punctual:[0,4,7,29,31,48,60,61,62],purpos:[44,57],python:[0,14,39,41,45],q_c:4,quad:4,quadrat:[4,44],qualiti:[4,8,9,12,17,27,28,33,38,41,44,47],quantiti:[4,8,22,41,47],quarter:4,quasi:[61,62],question:63,quick:[41,49,56],quit:[4,62],r01:56,r02:56,r03:56,r04:56,r05:56,r06:56,r_i:4,r_n:4,r_p:4,radiat:[4,5],radio:[50,55],radiu:[4,8,9,12,41,47,57],rai:[0,4,29,43],randolph:28,random:[1,2,5,8,15,41,43,47,57,63],randomli:[4,5],rang:[4,8,10,41,44,47],rangl:4,rapidli:4,rate:44,rather:4,ratio:[1,4,8,9,12,41,44,47,57],raw:5,read:[33,38,41,47,63],reader:4,readthedoc:29,real:[4,15,26,40,41,63],realis:41,realist:[33,38],realiti:4,realiz:[4,5,8,9,12,28,32,38,41,47,49],rearaudi:40,rearrang:39,rec:4,receiv:[5,9,12,17,29,33,38,40,44,48,60,61,62],recent:[4,33,38],receptor:4,receveir:57,reciproc:4,recogn:[41,48],recommand:[8,33,38,41,47],recommend:[29,32,33,38,44],recov:56,recp:[8,41,47,48],rectangular:[33,38,41,49,57,58],rectilinear:5,recurs:[32,38],red:[14,57],redo:[32,33,38],reduc:[4,5,28],reduct:28,ref:4,refer:[0,4,6,8,14,29,40,41,44,47,49,56,57,60,63],refernc:44,reflect:[0,2,5,7,8,26,40,41,43,44,47,49,57,61,62],reflector:40,refract:4,refresh:[41,48],regard:[28,62],regardless:4,regress:44,reiniti:[37,39,54,55],rel:[4,5,40,41,49],relat:[1,4,6,40,43,44,62],relation_snel:4,relationship:4,relax:4,relev:[8,41,47],reli:2,remain:5,rememb:58,remesh:[27,29,56],remov:[8,33,38,45,47,50,53,55,58],renam:[8,47,57,58],render:[33,37,38,41,49],repair:[0,8,9,12,27,29,41,47],repeat:[8,11,41,47,56,57,58,63],replac:[8,9,12,41,47],repres:[4,8,15,22,41,47,49],represent:[0,1,4,15,17,33,36,38,40,41,49,57],requir:[4,5,8,9,12,16,28,41,45,47,58],resampl:58,research:[0,28,29],reset:[4,14,15,41],residenti:[41,49],resist:40,resiz:39,resolut:[28,36,38,41,49],resourc:[8,41,47,56,57,58],respect:[4,5,40,41,47,57],respons:[8,22,41,44,47],ressourc:56,rest:4,restart:[33,38],restor:[32,37,38],result:[4,5,9,17,27,29,39,57,60,63],resum:[0,58],retain:56,rev:43,reverber:[0,1,4,6,8,26,29,41,43,47,49,58,61,62],revers:4,revu:43,reward:[53,55],rho_0:4,right:[4,8,9,15,16,17,36,38,40,41,42,44,46,47,49,56,57,58,62],rise:56,road:[8,10,41,47,56,57],robin:56,room:[0,1,4,6,8,26,28,29,41,43,47,49,56,61,62],root:[8,41,47,49,56,57,58],rotat:[0,4,29,36,38,41,49,54,55],rough:[4,41,43,49],round:56,row:[8,31,41,47,49,57],rows5:57,rpi:[41,48],rs_cut:57,rt15:[7,44],rt30:[7,61,62],rule:60,run:[8,9,11,14,27,45,56,57,58],s0003682x01000548:43,s0003682x99000560:43,s0003:43,s01:56,s02:56,s03:56,s31:1,s_c:4,s_x:4,s_y:4,s_z:4,sabin:[6,7,41,43,47,58],sai:[4,5],sal01:[4,43],salomon:43,same:[4,5,8,11,27,33,38,40,41,45,47,49,56,57,58,60,63],satisfactori:5,satisfi:4,save:[14,33,36,38,51,55,58],scale:[4,43],scaterr:57,scatter:[0,1,2,4,5,7,26,40,41,43,49],scene:[0,4,7,8,9,12,28,29,30,33,36,38,39,40,46,47,48,56],sch65:[8,41,43,47],schemat:4,schroeder:[7,41,43,58],scienc:43,sciencedirect:43,scientif:63,screen:[30,37,38,39],screenchot:29,screenshot:[56,57,58],screenshot_tutorial_1:58,script:[0,14,41,45],scroll:41,sctar:62,second:[2,4,8,30,41,47,49,56,57,58],section:[4,30,40,41,48,49,61,62],see:[2,4,5,6,8,9,12,27,28,30,32,33,35,38,40,41,47,48,49,52,55,57,61,63],seem:[28,62],seen:4,select:[0,8,9,10,13,15,16,17,25,29,30,33,36,38,39,41,42,45,47,49,50,52,56,57,58,60],semicolon:[8,41,47],send:58,sens:22,sensit:[27,28,33,38],separ:[4,17],sepctrum:40,sequenti:[41,48],seri:[8,15,41,47,63],set:[25,26,32,33,37,38,40,41,44,48,49,56,57,58,60],setup:29,sever:[4,5,8,15,26,30,36,38,39,40,41,47,49,56,57,58,62],shape:[4,5,27,28,58],shift:40,shock:5,should:[2,4,22,28,36,38,41,56,57,58,60],show:[4,8,11,15,17,33,36,38,41,47,49,56,57,58,61,62,63],shown:[4,28],side:40,sidereflector:40,sigma_:4,significantli:5,similar:[4,5,41,49],simpa:[2,6,12,14,22,25,27,28,31,32,33,34,35,36,38,39,40,44,45,48,49,56,57,58,60,61,62,63],simpl:[4,27,28,33,38,58],simplement:4,simpler:57,simplest:4,simpli:4,simplifi:27,simul:[2,4,5,6,8,29,39,41,43,47,56,57,58,60,61,63],simultan:[4,44,56,60],sin:4,sinc:[2,4,5,32,38,40,56,57,58,60,61,62],singl:[4,6,33,38,56,57,61],site:45,situ:4,situat:[4,41,47],sixth:6,size:[4,8,28,41,47,49,56,58],sketch:25,slider:[50,55,56],slope:44,slow:[32,38],small:[4,5,8,41,47,63],smaller:[],smooth:17,snell:4,societi:43,softwar:[0,4,5,25,26,27,28,29,33,38,40,41,49,56,57,58],soil:4,solid:4,solut:4,solv:[28,56],some:[3,4,5,8,9,11,12,27,28,29,33,38,41,47,49,56,58,63],someth:27,sometim:4,somm:[],son:43,sonor:[1,2],soud:[],sound:[1,2,6,22,26,28,29,31,33,38,40,43,60,61,62],soundmap:[8,41,47,57],sourc:[2,5,25,29,33,38,40,60,61,62,63],space:[1,4,5,15,41,43,47,49,62],spars:[41,49],spatial:[8,29,31,41,47],spatialis:29,speak:4,speaker_s1:56,speakerwindow:40,special:5,specif:[8,11,36,38,41,47,48,49,56,57,58],specifi:[15,31,36,38,41],spectrum:[0,7,8,29,41,47,49,57,58],specular:[5,7,26,40,57],specularli:4,speech:[41,49],speed:[0,4,5,33,38],sphere:[4,8,9,12,41,47],spheric:[4,5,43],spl:[4,8,41,47,57,58],spl_:4,spl_global:4,spl_receiver_surfac:4,spl_recepteur_volumiqu:4,split:[33,38],spp:[0,2,5,7,9,12,27,29,33,38,40,48,49,61,62,63],spread:4,spreadshe:57,spreadsheet:[0,16,57,58],springer:43,sqrt:4,squar:[4,5,22,44],stabl:4,stage:[8,40,41,47,56],stair:[40,56],stamp:14,stan:43,stand:1,standard:[4,29],standardis:4,stanford:0,start:[8,13,27,31,33,38,41,45,47,48,56,57,58],state:[0,4,44],stationari:[0,44],statist:[4,5,8,26,41,47,49],ste:26,steadi:44,step:[2,4,5,8,25,27,31,33,38,41,47,49,53,55,56,57,58,62],stereolithographi:0,still:[27,28],stl:[0,33,38],stochast:4,stop:[35,38,53,55,58],straight:[2,5],strength:[0,5,8,41,47],string:40,strong:4,structur:[0,41,48],studi:[4,5,8,29,41,47,49],studio:0,style:[15,41],sub:[0,40],subfold:[41,45,60],subject:[44,56],subjectiv:[],subsoil:4,suburb:[41,49],success:[4,5,26,28,63],suffici:[4,5],suggest:[8,41,47,56,57],sum:[4,5,8,41,47],sum_:[4,44],sum_i:4,sun:43,support:[0,5,8,40,41,47],suppos:5,surc:[41,49],surf:4,surfac:[1,5,7,9,12,17,26,27,29,30,33,36,38,40,43,44,48,52,55,61,62],sutherland:43,symbol:[4,41],system:[0,5,41,43,48],t15:44,t_S:44,t_e:44,t_i:4,t_k:4,tab:[8,11,14,15,17,27,29,37,38,39,46,56,57,58],tabl:[8,15,41,47,57,58],tabul:[41,48],take:[4,40,41,49,60],taken:[4,44],tangent:4,target:[41,49],task:56,tau:[4,40,44],taylorfr:43,tcr:[0,6,7,8,29,61],teach:[29,56,57],team:[],techniqu:28,temperatur:[4,41,49,61],tempor:[8,22,29,41,44,47],term:[4,5,45,61],test:[4,8,9,12,27,41,47],tetgen:[0,8,9,12,28,29,35,38,41,47],tetrahedr:[0,5,8,9,12,41,47],text:[4,14,15,29,33,38,40,41,49,62],textur:[33,36,38],than:[4,5,8,40,41,44,47,56,57],theater:56,thei:[4,5,46],them:[57,58],themat:29,theoret:[4,41,49],theori:[0,4,5,6,26,29,41,43,47,58,61,62],thereaft:4,therefor:[4,5,56],thermal:4,thermodynam:4,theses:43,thesi:43,theta:[4,8,41,44,47],theta_1:4,theta_i:4,thi:[0,2,3,4,5,8,9,10,12,16,22,25,26,27,28,29,33,35,36,37,38,40,41,42,44,47,48,49,56,57,58,59,60,61,62],thierri:43,third:[4,8,10,40,41,47,60],those:4,thousand:[5,8,41,47],three:[4,8,29,32,38,39,40,41,43,47,56,57],thrid:[8,10,41,47],through:[4,8,41,47,57],throughout:4,thu:[4,5,8,11,41,44,47,57],time:[0,2,4,5,6,14,22,28,29,33,38,43,45,49,53,55,56,57,58,61,62],times10:44,to70:44,togeth:40,too:[4,27],tool:[0,14,45,56,57],toolbar:[8,27,29,35,38,39,41,46,47,49,56,57,58],toolbox:0,topographi:4,topolog:[8,9,12,27,41,47],total:[4,5,7,8,26,28,41,44,47,49,58,60,62],tow:57,toward:[4,44],trace:[0,2,5,29,41,43,47],track:[2,5],tradit:5,trajectori:[2,5],trans_ro1:57,trans_room:57,transfer:[1,4],transform:4,translat:[0,4,28,29,41,49,57],transmiss:[0,5,8,40,41,47,57],transmit:[2,4,5],transpar:[17,33,38,40,57],transport:43,travel:4,treat:5,treatment:[4,14,41,48],tree:[0,16,36,38,40,42,46,48,52,55,56,57,58],trevor:43,triangul:[8,9,12,28,41,47],triangular:[33,38,58],troubl:27,tube:[1,4],turbul:[4,5],turn:4,tutori:[25,29,56,58],tutorial_1:58,tutorial_2:56,tutorial_3:57,tuturi:57,two:[0,2,4,5,8,14,26,28,29,31,33,37,38,39,40,41,44,47,48,49,56,57,62,63],txt:[37,38,40,41,48,56],type:[4,22,28,41,48,49],typic:[4,22],uncertainti:63,unchang:[33,38],uncheck:[8,9,10,12,40,41,47,56,57],under:[4,5,44],underlin:58,understand:[30,40,41,49],understood:4,underwai:[4,29],undo:[32,33,38],undock:39,unfavor:[4,41,49],unfold:[56,57,58],unidirectionn:[41,49],uniform:[40,57,60,61,62],uniformli:[4,43,60],unilater:40,uninstal:45,unit:[4,26,41,43,49,56],unlik:5,unoccupi:44,unselect:[8,10,41,47],until:[2,5,8,41,47],unwant:28,updat:60,upon:2,upper:40,upward:4,urbain:43,urban:[4,41,47,49],url:43,use:[0,4,11,28,29,33,36,38,40,41,47,49,56,57,58,63],used:[4,5,8,9,10,12,26,28,33,38,40,41,43,47,48,49,56,57,58,60],useful:[0,8,9,12,28,33,37,38,41,47,49,60],user:[0,7,8,9,12,14,15,16,27,28,31,33,36,38,39,40,41,42,47,48,49,56,57,58,60],userdefineparam:[],uses:40,using:[0,1,4,7,8,9,10,12,14,15,27,28,29,33,35,36,37,38,41,43,44,46,47,49,56,57,58,60,62],usual:[4,8,41,47],v_c:4,v_i:4,v_x:4,v_y:4,v_z:4,valid:[2,3,4,41,56,61,63],validation_atmospheric_absorpt:61,valu:[4,5,8,9,12,15,16,17,26,40,41,47,49,52,55,56,57,58,60,62,63],varepsilon_0:4,varepsilon_i:4,vari:[0,2,4,5,44,58],variabl:4,variant:1,variat:[4,8,41,47],vector:[0,31,41,49],vehicl:[0,29],veloc:[0,5],ventilationgrid:40,veri:[0,4,5,8,27,33,38,41,47,49,58,60,61],verif:[29,63],verifi:[4,27,56,57,63],verification_diffusion_encrowd:4,version:[5,29],vertex:[41,49,56],vertic:[4,28,41,49,56],veryfavor:[41,49],via:[5,43,56],vibratori:4,vicin:4,view:[4,29,30,33,39,40,41,46,49,50,52,56,57,58],virtual:43,visibl:[4,16,36,38,56],visit:[2,6,29],visual:[36,38],vm00:[4,43],volum:[0,5,8,9,12,26,28,29,43,47,56,62],volumetr:5,w_d:4,w_i:4,w_k:4,w_r:4,w_t:4,wai:[0,4,5,27,33,38,58,60],wait:45,wall:[5,8,26,29,40,41,47,57,58,61,62],want:[8,17,28,33,38,41,47,57,60],warn:14,water:[41,49],wave:[1,4,5],wavelength:4,web:45,websit:[2,6,29,34,38,40],weight:[4,5,44],weigth:60,weihgt:[8,41,47],well:[0,4,5,28,29,31,36,38,41,47,49,56,60],were:[3,57],what:[27,63],whatev:4,wheat:[41,49],when:[4,5,8,26,27,28,33,37,38,41,44,46,47,48,57,58,61,62],where:[4,5,44,62],whether:4,which:[4,5,8,28,40,41,44,47,57,60],white:[56,58],whole:[2,8,10,41,47,58,62],whose:[4,5,8,41,47],wide:4,widh:[41,49],width:[41,49,58],wiki:29,wind:4,window:[15,16,17,27,29,37,39,49,52,55,56,57,58,60],winodw:[8,41,47],within:[0,2,4,12,27,28,29,33,35,38,39,44,45,46,48,49,57,58,60,61],without:[4,5,28,33,38,54,55,56,57,58,60],word:28,work:[4,28],workbench:25,workshop:43,workstat:57,would:[4,8,9,12,41,47],write:[4,29],written:4,wrong:[41,49],wrongli:4,www:[1,43,56,61,62],xi_i:4,xi_n:4,xiangyang:43,xkaj02:[4,43],xls:56,xlsx:[61,62],xml:[41,48],xoi:4,xoz:4,xyz:[36,38,41,49],yet:4,you:[0,8,11,14,27,28,29,30,33,38,39,41,45,47,49,56,57,58,60],your:[0,27,29,45,56],yoz:4,z_0:4,zeng:43,zero:4,zeta:4,zone:[0,4,8,29,47],zoom:[15,36,38,41,54,55]},titles:["I-Simpa overview","Standards","Presentation","Verification of the SPPS code","Modelling of physical phenomena in SPPS","General principle","Presentation","Calculation code characteristics","Using SPPS within I-Simpa","Using TCR within I-Simpa","Frequency bands","Job list","Meshing","Run calculation","\u2018Console\u2019 window","Creating charts","Define a position using the pointer on the 3D view","Define the properties of a sound map","Surface receiver file file .csbin","Tabulated data file .gabe","Punctual receiver file .gap","Animated data file .pbin","Punctual Receiver data file .recp","Sound level per source .recps","Animated intensity file .rpi","Create a room with FreeCAD","Glossary","Recommendations to import a 3D scene","Import options","I-Simpa User Guide","\u2018Main\u2019 window","Manipulating sources and receivers","<no title>","<no title>","<no title>","<no title>","\u2018Face\u2019","\u20183D View\u2019","Menus","GUI Presentation","Project database","\u2018Project\u2019 window","\u2018Properties\u2019 window","References","Room acoustics parameters","(Un)Setup","Surface selection","\u2018Calculation\u2019 tab","\u2018Results\u2019 tab","\u2018Scene\u2019 tab","<no title>","<no title>","<no title>","<no title>","<no title>","Toolbars","Study of the Elmia hall","Study of an industrial room","Study of a teaching room","Using directivity","Define and use spectrum","Atmospheric absorption implementation verification","Clarity calculation implementation validation","<no title>"],titleterms:{"dur\u00e9":[],"export":[36,38],"function":41,"import":[27,28,56,57],"new":58,"r\u00e9verb\u00e9rat":[],Use:27,Using:[8,9,41,47,59,60],absorbing_materi:57,absorpt:[4,61],acoust:[0,1,4,7,8,44,47,57],adavanc:[],advanc:[8,47],anim:[21,24],appar:44,apparent:[],approxim:56,asw:44,atmospher:[4,61],auditor:44,averag:28,bad:56,band:[8,10,41,47],bodi:25,calcul:[0,4,7,8,9,13,41,47,56,57,58,62],camera:[36,38,55],centr:44,central:[],chang:57,characterist:7,chart:[15,41],clariti:[44,62],code:[3,5,7,58],collaps:41,color:[36,38],common:41,comput:[8,41,47],concept:5,configur:[41,49],consol:[14,37,38],contextu:40,copi:41,correct:28,creat:[15,25,31,41,56,58],creation:[58,60],criteria:44,csbin:18,curv:[8,47],data:[19,21,22,41,49,60,61,62],databas:[40,41,49],decai:44,defin:[16,17,56,57,58,60],definit:[41,44,60],densiti:4,descript:4,diffus:4,direct:[4,40,59],displai:[41,49],dissip:4,duplic:57,earli:44,edit:38,edt:44,effect:57,element:41,elementari:4,elmia:56,emiss:4,energet:4,energi:[4,44],envelop:44,environ:[41,49],evalu:57,exist:28,expand:41,expect:[44,61,62],explor:56,face:[36,38],featur:[0,41],few:44,file:[18,19,20,21,22,24,38,41,48,61,62],fit:[4,41,49,57],floor:25,forc:[],formal:4,format:[41,48],fraction:44,freecad:25,frequenc:[8,10,41,47],from:[41,44],gabe:19,gap:20,gener:[4,5,60],geometri:[0,56,57,58,61,62],glossari:26,graphic:7,grid:[31,36,38,57],group:[28,31],gui:39,guid:29,hall:56,help:38,hide:[36,38],identifi:27,implement:[61,62],implemnt:[],indic:29,industri:57,inform:[41,49,60],insert:57,instal:45,intellig:44,intens:[4,8,24,47],iso:44,job:[11,41,47],keep:28,lambert:4,largeur:[],late:44,later:[4,44],law:4,learn:58,lev:44,level:[4,8,23,44,47],lfc:44,line:[36,38,57],list:[11,41,47],machin:57,main:30,manipul:31,map:17,materi:[0,36,38,40,56,57],menu:[38,40],mesh:[5,8,9,12,28,36,38,41,47,55],model:[4,27,28,56,62],more:58,multipl:44,normal:4,nuemric:[],numer:[],observ:4,omnidirectionn:4,one:57,open_door:57,optim:[8,41,47],option:[15,28,41],other:[0,1],overal:4,overview:0,parallelipiped:57,paramet:[7,8,9,41,44,47,57],particl:[4,5],past:41,pbin:21,per:[8,23,47],phenomena:[4,7],physic:[4,7],plane:[4,56,57],point:4,pointer:16,posit:16,power:4,present:[2,6,39,41,61,62],pressur:[4,44],principl:[5,41],probabl:4,problemn:27,profil:4,project:[40,41,49,55,60],propag:4,properti:[17,40,42,57],punctual:[8,20,22,41,47,49,56,57,58],rai:5,random:4,receier:[8,47],receiv:[0,4,7,8,18,20,22,31,41,47,49,56,57,58],recommend:27,recp:[22,23],refer:[43,61,62],reflect:4,reiniti:[36,38],remesh:28,remov:41,renam:41,repair:28,represent:7,result:[8,41,47,48,56,58,61,62],reverber:44,room:[25,44,57,58],rotat:31,rpi:24,run:[13,41,47],scene:[27,41,44,49,57,58],schroeder:[8,47],select:[46,55],setup:45,simpa:[0,8,9,29,41,47],simul:[38,55],singl:44,skech:25,sonor:[],sound:[0,4,5,7,8,17,23,41,44,47,49,56,57,58],sourc:[0,4,7,8,23,31,41,44,47,49,56,57,58],spatial:5,spatialis:44,spectrum:[40,60],specular:4,spl:44,spp:[3,4,8,41,47,56,57,58],spreadsheet:41,stage:44,standard:[1,44],strength:44,structur:[8,47],strutur:[],studi:[56,57,58],support:44,surfac:[0,4,8,18,28,41,46,47,49,56,57,58],t_s:44,tab:[41,47,48,49],tabl:[29,44],tabul:19,tabular:41,tcr:[9,41,47,58],teach:58,tempor:5,test:[],tetgen:27,text:44,theoret:62,time:[8,41,44,47],toolbar:55,trans_materi:57,translat:31,transmiss:4,tree:[8,41,47,49,60],tutori:57,two:58,type:7,unidirect:4,uniform:4,uniqu:[],use:60,user:29,using:16,valid:62,valu:44,vector:4,veloc:4,verif:[3,4,61],view:[16,36,37,38,55],volum:[4,41,49,57],wall:4,what:[61,62],width:44,window:[14,30,38,41,42],within:[8,9,41,47],zone:[41,49,57]}}) \ No newline at end of file diff --git a/currentRelease/doc/documentation/Offline_documentation/html/setup.html b/currentRelease/doc/documentation/Offline_documentation/html/setup.html index ab3cedc2..e630fb2a 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/setup.html +++ b/currentRelease/doc/documentation/Offline_documentation/html/setup.html @@ -265,9 +265,9 @@

    Un-installation

    - © Copyright 2022, Université Gustave Eiffel + © Copyright 2020, Université Gustave Eiffel - Last updated on Jan 12, 2022. + Last updated on Jan 04, 2022.

    diff --git a/currentRelease/doc/documentation/Offline_documentation/html/surface_selection.html b/currentRelease/doc/documentation/Offline_documentation/html/surface_selection.html index 7af626ca..155b82bc 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/surface_selection.html +++ b/currentRelease/doc/documentation/Offline_documentation/html/surface_selection.html @@ -239,9 +239,9 @@

    Surface selection

    - © Copyright 2022, Université Gustave Eiffel + © Copyright 2020, Université Gustave Eiffel - Last updated on Jan 12, 2022. + Last updated on Jan 04, 2022.

    diff --git a/currentRelease/doc/documentation/Offline_documentation/html/tab_calculation.html b/currentRelease/doc/documentation/Offline_documentation/html/tab_calculation.html index 638afde0..3a0d3e76 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/tab_calculation.html +++ b/currentRelease/doc/documentation/Offline_documentation/html/tab_calculation.html @@ -906,9 +906,9 @@

    Job list

    - © Copyright 2022, Université Gustave Eiffel + © Copyright 2020, Université Gustave Eiffel - Last updated on Jan 12, 2022. + Last updated on Jan 04, 2022.

    diff --git a/currentRelease/doc/documentation/Offline_documentation/html/tab_results.html b/currentRelease/doc/documentation/Offline_documentation/html/tab_results.html index 14526932..645cd783 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/tab_results.html +++ b/currentRelease/doc/documentation/Offline_documentation/html/tab_results.html @@ -275,9 +275,9 @@

    File formats

    - © Copyright 2022, Université Gustave Eiffel + © Copyright 2020, Université Gustave Eiffel - Last updated on Jan 12, 2022. + Last updated on Jan 04, 2022.

    diff --git a/currentRelease/doc/documentation/Offline_documentation/html/tab_scene.html b/currentRelease/doc/documentation/Offline_documentation/html/tab_scene.html index c432a75f..e92538b3 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/tab_scene.html +++ b/currentRelease/doc/documentation/Offline_documentation/html/tab_scene.html @@ -1207,9 +1207,9 @@

    Project database

    - © Copyright 2022, Université Gustave Eiffel + © Copyright 2020, Université Gustave Eiffel - Last updated on Jan 12, 2022. + Last updated on Jan 04, 2022.

    diff --git a/currentRelease/doc/documentation/Offline_documentation/html/toolbar_meshing.html b/currentRelease/doc/documentation/Offline_documentation/html/toolbar_meshing.html index d9e5d9a3..56a18a7a 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/toolbar_meshing.html +++ b/currentRelease/doc/documentation/Offline_documentation/html/toolbar_meshing.html @@ -219,9 +219,9 @@

    - © Copyright 2022, Université Gustave Eiffel + © Copyright 2020, Université Gustave Eiffel - Last updated on Jan 12, 2022. + Last updated on Jan 04, 2022.

    diff --git a/currentRelease/doc/documentation/Offline_documentation/html/toolbar_project.html b/currentRelease/doc/documentation/Offline_documentation/html/toolbar_project.html index 5607618f..2f526289 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/toolbar_project.html +++ b/currentRelease/doc/documentation/Offline_documentation/html/toolbar_project.html @@ -219,9 +219,9 @@

    - © Copyright 2022, Université Gustave Eiffel + © Copyright 2020, Université Gustave Eiffel - Last updated on Jan 12, 2022. + Last updated on Jan 04, 2022.

    diff --git a/currentRelease/doc/documentation/Offline_documentation/html/toolbar_selection.html b/currentRelease/doc/documentation/Offline_documentation/html/toolbar_selection.html index 0abbf15f..cf96d950 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/toolbar_selection.html +++ b/currentRelease/doc/documentation/Offline_documentation/html/toolbar_selection.html @@ -218,9 +218,9 @@

    - © Copyright 2022, Université Gustave Eiffel + © Copyright 2020, Université Gustave Eiffel - Last updated on Jan 12, 2022. + Last updated on Jan 04, 2022.

    diff --git a/currentRelease/doc/documentation/Offline_documentation/html/toolbar_simulation.html b/currentRelease/doc/documentation/Offline_documentation/html/toolbar_simulation.html index 30223d39..c76a3424 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/toolbar_simulation.html +++ b/currentRelease/doc/documentation/Offline_documentation/html/toolbar_simulation.html @@ -221,9 +221,9 @@

    - © Copyright 2022, Université Gustave Eiffel + © Copyright 2020, Université Gustave Eiffel - Last updated on Jan 12, 2022. + Last updated on Jan 04, 2022.

    diff --git a/currentRelease/doc/documentation/Offline_documentation/html/toolbar_view_camera.html b/currentRelease/doc/documentation/Offline_documentation/html/toolbar_view_camera.html index d483961a..d3880981 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/toolbar_view_camera.html +++ b/currentRelease/doc/documentation/Offline_documentation/html/toolbar_view_camera.html @@ -235,9 +235,9 @@

    - © Copyright 2022, Université Gustave Eiffel + © Copyright 2020, Université Gustave Eiffel - Last updated on Jan 12, 2022. + Last updated on Jan 04, 2022.

    diff --git a/currentRelease/doc/documentation/Offline_documentation/html/toolbars.html b/currentRelease/doc/documentation/Offline_documentation/html/toolbars.html index 03fed0f4..b1809b9d 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/toolbars.html +++ b/currentRelease/doc/documentation/Offline_documentation/html/toolbars.html @@ -296,9 +296,9 @@

    Toolbar ‘Simulation’

    - © Copyright 2022, Université Gustave Eiffel + © Copyright 2020, Université Gustave Eiffel - Last updated on Jan 12, 2022. + Last updated on Jan 04, 2022.

    diff --git a/currentRelease/doc/documentation/Offline_documentation/html/tutorial_Elmia_hall.html b/currentRelease/doc/documentation/Offline_documentation/html/tutorial_Elmia_hall.html index 461cc108..7078c4ed 100644 --- a/currentRelease/doc/documentation/Offline_documentation/html/tutorial_Elmia_hall.html +++ b/currentRelease/doc/documentation/Offline_documentation/html/tutorial_Elmia_hall.html @@ -148,7 +148,6 @@

Appendices

Appendices

Expected values for few room acoustics parameters. From Table A.1 of the NF EN ISO 3382-1 standard.
\n" "\n" - "\n" - "\n" + "\n" + "\n" "\n" "\n" diff --git a/src/isimpa/IHM/ComboTreeCtrl.cpp b/src/isimpa/IHM/ComboTreeCtrl.cpp index 55931975..2c9c4f38 100644 --- a/src/isimpa/IHM/ComboTreeCtrl.cpp +++ b/src/isimpa/IHM/ComboTreeCtrl.cpp @@ -64,7 +64,7 @@ wxSize ComboTreeCtrl::GetAdjustedSize( int minWidth, int WXUNUSED(prefHeight), int maxHeight ) { - return wxSize(wxMax(200,minWidth),wxMin(125,maxHeight)); + return wxSize(FromDIP(wxMax(200,minWidth)),FromDIP(wxMin(150,maxHeight))); } wxWindow *ComboTreeCtrl::GetControl() { return this; } diff --git a/src/isimpa/IHM/PropGrid.cpp b/src/isimpa/IHM/PropGrid.cpp index 230cacfa..240b6514 100644 --- a/src/isimpa/IHM/PropGrid.cpp +++ b/src/isimpa/IHM/PropGrid.cpp @@ -74,6 +74,10 @@ PropGrid::PropGrid(wxWindow* parent, wxWindowID id, const wxPoint& pos, const wx wxAcceleratorTable accel(2, entries); this->SetAcceleratorTable(accel); SetRowLabelAlignment(wxALIGN_LEFT, wxALIGN_CENTRE); + + // Bind events for automatic resizing of last column + GetGridWindow()->Bind(wxEVT_SIZE, &PropGrid::OnGridWindowSize, this); + Bind(wxEVT_GRID_COL_SIZE, &PropGrid::OnColHeaderSize, this); } void PropGrid::OnLostFocus( wxFocusEvent& ev) @@ -308,3 +312,30 @@ void PropGrid::AutoSizeLibelle( int row ) */ //AutoSizeRowLabelSize(row); } + +void PropGrid::OnGridWindowSize(wxSizeEvent& event) +{ + if (GetNumberCols() > 0) + AutoSizeLastCol(); +} + +void PropGrid::OnColHeaderSize(wxGridSizeEvent& event) +{ + if (GetNumberCols() > 0) + AutoSizeLastCol(); +} + +void PropGrid::AutoSizeLastCol() +{ + int colWidths = 0; + + for (int i = 0; i < GetNumberCols() - 1; i++) + colWidths += GetColWidth(i); + + int finalColWidth = GetGridWindow()->GetSize().x - colWidths; + + if (finalColWidth > FromDIP(50)) + SetColSize(GetNumberCols() - 1, finalColWidth); + else + SetColSize(GetNumberCols() - 1, FromDIP(50)); +} diff --git a/src/isimpa/IHM/PropGrid.h b/src/isimpa/IHM/PropGrid.h index a49ff5d0..dceddb20 100644 --- a/src/isimpa/IHM/PropGrid.h +++ b/src/isimpa/IHM/PropGrid.h @@ -84,6 +84,11 @@ class PropGrid: public wxGrid DECLARE_EVENT_TABLE() bool allowPaste; + + //Functions for automatic resizing of last column + void OnGridWindowSize(wxSizeEvent& event); + void OnColHeaderSize(wxGridSizeEvent& event); + void AutoSizeLastCol(); public: /** * Constructeur diff --git a/src/isimpa/IHM/simpleGraph.cpp b/src/isimpa/IHM/simpleGraph.cpp index d7158635..b45dc717 100644 --- a/src/isimpa/IHM/simpleGraph.cpp +++ b/src/isimpa/IHM/simpleGraph.cpp @@ -1072,7 +1072,7 @@ namespace sgSpace wxPen oldPen = currentElementStyle->GetPen(); wxPen newPen = oldPen; newPen.SetWidth(1); - newPen.SetStyle(wxSOLID); + newPen.SetStyle(wxPENSTYLE_SOLID); currentContext->SetPen(newPen); @@ -1170,9 +1170,9 @@ namespace sgSpace } void SG_Renderer::DrawRectangle(const wxRect& rectArea) { - if (currentElementStyle->GetBrush().GetStyle() != wxTRANSPARENT) + if (currentElementStyle->GetBrush().GetStyle() != wxBRUSHSTYLE_TRANSPARENT) { - currentContext->SetBrush(wxBrush(currentElementStyle->GetPen().GetColour(), wxSOLID)); + currentContext->SetBrush(wxBrush(currentElementStyle->GetPen().GetColour(), wxBRUSHSTYLE_SOLID)); currentContext->DrawRectangle(rectArea); } //Dessin du rect de style @@ -1282,7 +1282,7 @@ namespace sgSpace backingStoreDc(NULL) { InheritAttributes(); - wxPen pen = wxPen(*wxBLACK, 1, wxDOT); + wxPen pen = wxPen(*wxBLACK, 1, wxPENSTYLE_DOT); zoomAreaHintStyle.SetPen(&pen); zoomAreaHintStyle.SetBrush(wxTRANSPARENT_BRUSH); } diff --git a/src/isimpa/IHM/simpleGraphDialogs.cpp b/src/isimpa/IHM/simpleGraphDialogs.cpp index 04d5e041..af305004 100644 --- a/src/isimpa/IHM/simpleGraphDialogs.cpp +++ b/src/isimpa/IHM/simpleGraphDialogs.cpp @@ -400,7 +400,7 @@ void SG_PropertySheetDialog::OnUserChoosePenStyle(wxCommandEvent& ev) if(drawingPenStyleCombo) { wxPen drawingPen=elementsModifications.Item(selectedElementIndice).elStyle.GetPen(); - drawingPen.SetStyle(penStyles.Item(drawingPenStyleCombo->GetSelection()).enumId); + drawingPen.SetStyle((wxPenStyle)penStyles.Item(drawingPenStyleCombo->GetSelection()).enumId); elementsModifications.Item(selectedElementIndice).elStyle.SetPen(&drawingPen); OnCurveChange(); } @@ -498,7 +498,7 @@ void SG_PropertySheetDialog::OnUserChooseBrushStyle(wxCommandEvent& ev) if(drawingBrushStyleCombo) { wxBrush drawingBrush=elementsModifications.Item(selectedElementIndice).elStyle.GetBrush(); - drawingBrush.SetStyle(brushStyles.Item(drawingBrushStyleCombo->GetSelection()).enumId); + drawingBrush.SetStyle((wxBrushStyle)brushStyles.Item(drawingBrushStyleCombo->GetSelection()).enumId); elementsModifications.Item(selectedElementIndice).elStyle.SetBrush(&drawingBrush); OnCurveChange(); } diff --git a/src/isimpa/IHM/simpleGraphEnumerators.cpp b/src/isimpa/IHM/simpleGraphEnumerators.cpp index 562c765e..90fc7c01 100644 --- a/src/isimpa/IHM/simpleGraphEnumerators.cpp +++ b/src/isimpa/IHM/simpleGraphEnumerators.cpp @@ -60,27 +60,27 @@ namespace sgSpace } void SG_EnumeratorFiller::FillPenStyleEnumeration(StyleArray& arrayToFeed) { - arrayToFeed.Add(t_LstEnums(_("Transparent"),wxTRANSPARENT)); - arrayToFeed.Add(t_LstEnums(_("Continuous line"),wxSOLID)); - arrayToFeed.Add(t_LstEnums(_("Point"),wxDOT)); - arrayToFeed.Add(t_LstEnums(_("Left diagonal dash"),wxBDIAGONAL_HATCH)); - arrayToFeed.Add(t_LstEnums(_("Cross"),wxCROSS_HATCH)); - arrayToFeed.Add(t_LstEnums(_("Horizontal line"),wxHORIZONTAL_HATCH)); - arrayToFeed.Add(t_LstEnums(_("Vertical line"),wxVERTICAL_HATCH)); - arrayToFeed.Add(t_LstEnums(_("Dotted-dashed line"),wxDOT_DASH)); - arrayToFeed.Add(t_LstEnums(_("Small dash"),wxSHORT_DASH)); - arrayToFeed.Add(t_LstEnums(_("Long dash"),wxLONG_DASH)); + arrayToFeed.Add(t_LstEnums(_("Transparent"),wxPENSTYLE_TRANSPARENT)); + arrayToFeed.Add(t_LstEnums(_("Continuous line"), wxPENSTYLE_SOLID)); + arrayToFeed.Add(t_LstEnums(_("Point"), wxPENSTYLE_DOT)); + arrayToFeed.Add(t_LstEnums(_("Left diagonal dash"), wxPENSTYLE_BDIAGONAL_HATCH)); + arrayToFeed.Add(t_LstEnums(_("Cross"), wxPENSTYLE_CROSS_HATCH)); + arrayToFeed.Add(t_LstEnums(_("Horizontal line"), wxPENSTYLE_HORIZONTAL_HATCH)); + arrayToFeed.Add(t_LstEnums(_("Vertical line"), wxPENSTYLE_VERTICAL_HATCH)); + arrayToFeed.Add(t_LstEnums(_("Dotted-dashed line"), wxPENSTYLE_DOT_DASH)); + arrayToFeed.Add(t_LstEnums(_("Small dash"), wxPENSTYLE_SHORT_DASH)); + arrayToFeed.Add(t_LstEnums(_("Long dash"), wxPENSTYLE_LONG_DASH)); } void SG_EnumeratorFiller::FillBrushStyleEnumeration(StyleArray& arrayToFeed) { - arrayToFeed.Add(t_LstEnums(_("Transparent"),wxTRANSPARENT)); - arrayToFeed.Add(t_LstEnums(_("Full"),wxSOLID)); - arrayToFeed.Add(t_LstEnums(_("Left diagonal dash"),wxBDIAGONAL_HATCH)); - arrayToFeed.Add(t_LstEnums(_("Cross"),wxCROSSDIAG_HATCH)); - arrayToFeed.Add(t_LstEnums(_("Right diagonal dash"),wxFDIAGONAL_HATCH)); - arrayToFeed.Add(t_LstEnums(_("Plus"),wxCROSS_HATCH)); - arrayToFeed.Add(t_LstEnums(_("Horizontal line"),wxHORIZONTAL_HATCH)); - arrayToFeed.Add(t_LstEnums(_("Vertical line"),wxVERTICAL_HATCH)); + arrayToFeed.Add(t_LstEnums(_("Transparent"), wxBRUSHSTYLE_TRANSPARENT)); + arrayToFeed.Add(t_LstEnums(_("Full"), wxBRUSHSTYLE_SOLID)); + arrayToFeed.Add(t_LstEnums(_("Left diagonal dash"), wxBRUSHSTYLE_BDIAGONAL_HATCH)); + arrayToFeed.Add(t_LstEnums(_("Cross"), wxBRUSHSTYLE_CROSSDIAG_HATCH)); + arrayToFeed.Add(t_LstEnums(_("Right diagonal dash"), wxBRUSHSTYLE_FDIAGONAL_HATCH)); + arrayToFeed.Add(t_LstEnums(_("Plus"), wxBRUSHSTYLE_CROSS_HATCH)); + arrayToFeed.Add(t_LstEnums(_("Horizontal line"), wxBRUSHSTYLE_HORIZONTAL_HATCH)); + arrayToFeed.Add(t_LstEnums(_("Vertical line"), wxBRUSHSTYLE_VERTICAL_HATCH)); } void SG_EnumeratorFiller::FillMarkersStyleEnumeration(StyleArray& arrayToFeed) { diff --git a/src/isimpa/IHM/uiTreeCtrl.cpp b/src/isimpa/IHM/uiTreeCtrl.cpp index 39381add..34d02cb3 100644 --- a/src/isimpa/IHM/uiTreeCtrl.cpp +++ b/src/isimpa/IHM/uiTreeCtrl.cpp @@ -424,6 +424,7 @@ void uiTreeCtrl::OnMouseMove(wxMouseEvent& event) { mousePos=event.GetPosition(); } + event.Skip(true); } void uiTreeCtrl::OnTimer( wxTimerEvent& event) { diff --git a/src/isimpa/data_manager/appconfig.cpp b/src/isimpa/data_manager/appconfig.cpp index 5b6c9473..9d878a28 100644 --- a/src/isimpa/data_manager/appconfig.cpp +++ b/src/isimpa/data_manager/appconfig.cpp @@ -294,7 +294,7 @@ wxFileConfig* ApplicationConfiguration::GetFileConfig() if(projectConfig.get() == NULL) { wxString userDir = wxStandardPaths::Get().GetUserDataDir(); - projectConfig=new wxFileConfig("i-simpa","Ifsttar", userDir+wxFileName::GetPathSeparator()+"isimpa_conf.ini", userDir+"\\isimpa_conf.ini",wxCONFIG_USE_LOCAL_FILE); + projectConfig=new wxFileConfig("i-simpa","Ifsttar", userDir+wxFileName::GetPathSeparator()+"isimpa_conf.ini", userDir+wxFileName::GetPathSeparator()+"isimpa_conf.ini",wxCONFIG_USE_LOCAL_FILE); } return projectConfig.get(); } diff --git a/src/isimpa/data_manager/customEditor/wxGridCellTreeEditor.cpp b/src/isimpa/data_manager/customEditor/wxGridCellTreeEditor.cpp index a5b38e1a..68443d26 100644 --- a/src/isimpa/data_manager/customEditor/wxGridCellTreeEditor.cpp +++ b/src/isimpa/data_manager/customEditor/wxGridCellTreeEditor.cpp @@ -39,7 +39,7 @@ wxGridCellTreeEditor::wxGridCellTreeEditor(Element* _rootItem,Element* _defaultSelection,const std::list& elementFilter) :wxGridCellEditor(),rootItem(_rootItem),currentSelection(_defaultSelection),currentFilters(elementFilter) { - + } void wxGridCellTreeEditor::BeginEdit(int row, int col, wxGrid* grid) @@ -59,7 +59,20 @@ void wxGridCellTreeEditor::BeginEdit(int row, int col, wxGrid* grid) Combo()->SetInsertionPointEnd(); Combo()->SetFocus(); + // Save currently edited cell and bind combo closeup with edit end handler + edited_row = row; + edited_col = col; + currentGrid = grid; + Combo()->Bind(wxEVT_COMBOBOX_CLOSEUP, &wxGridCellTreeEditor::onEndEdit, this); } + +void wxGridCellTreeEditor::onEndEdit(wxCommandEvent& event) { + wxString value = wxString(); + this->EndEdit(edited_row, edited_col, currentGrid, "", &value); // Apply changes 'locally' + wxPostEvent(Combo(), wxGridEvent(0, wxEVT_GRID_CELL_CHANGED, currentGrid, edited_row, edited_col)); //Post event so that e_data_tree action is triggered + event.Skip(); +} + bool wxGridCellTreeEditor::EndEdit(int row, int col, const wxGrid* grid, const wxString& oldval, wxString *newval) { ComboTreeCtrl* popupCtrlTree=dynamic_cast( Combo()->GetPopupControl()); @@ -74,8 +87,10 @@ bool wxGridCellTreeEditor::EndEdit(int row, int col, const wxGrid* grid, const w currentSelection=newSelection; grid->GetTable()->SetValue(row, col, value); + Combo()->Unbind(wxEVT_COMBOBOX_CLOSEUP, &wxGridCellTreeEditor::onEndEdit, this); //Not sure if it is needed(?) return true; }else{ + Combo()->Unbind(wxEVT_COMBOBOX_CLOSEUP, &wxGridCellTreeEditor::onEndEdit, this); return false; } } @@ -144,5 +159,5 @@ void wxGridCellTreeEditor::Reset(void) wxString wxGridCellTreeEditor::GetValue() const { - return Combo()->GetValue(); + return Combo()->GetValue(); } diff --git a/src/isimpa/data_manager/customEditor/wxGridCellTreeEditor.h b/src/isimpa/data_manager/customEditor/wxGridCellTreeEditor.h index f4dc8f9d..647a3040 100644 --- a/src/isimpa/data_manager/customEditor/wxGridCellTreeEditor.h +++ b/src/isimpa/data_manager/customEditor/wxGridCellTreeEditor.h @@ -71,6 +71,12 @@ class wxGridCellTreeEditor : public wxGridCellEditor * @param grid Pointeur vers le contrôle */ virtual void BeginEdit(int row, int col, wxGrid* grid); + + /** + * Event handler for cell edit end. Called when combobox is closed to automatically apply changes. + */ + void onEndEdit(wxCommandEvent& event); + /** * Fin d'édition du champ * @param row Ligne @@ -90,6 +96,8 @@ class wxGridCellTreeEditor : public wxGridCellEditor ComboTreeCtrl* GetTree(); protected: wxGenericComboCtrl *Combo() const { return (wxGenericComboCtrl *)m_control; } + int edited_row, edited_col; + wxGrid* currentGrid; Element *rootItem; Element *currentSelection; std::list currentFilters; diff --git a/src/isimpa/data_manager/projet.cpp b/src/isimpa/data_manager/projet.cpp index abbc95e7..ce6233c0 100644 --- a/src/isimpa/data_manager/projet.cpp +++ b/src/isimpa/data_manager/projet.cpp @@ -31,6 +31,7 @@ /* Projet.cpp */ + #include "projet.h" #include "python_interface/pythonstdioredirect.hpp" @@ -727,12 +728,17 @@ void ProjectManager::RunCoreCalculation(Element* coreCalculation) wxDateTime timeDebOperation=wxDateTime::UNow(); wxString modelFileName=coreCalculation->GetStringConfig("modelName"); wxString corePath=coreCalculation->GetStringConfig("corePath"); +#ifdef __linux__ + corePath.Replace("\\","/",true); +#endif wxString reportFolderName=dossierCourant+ApplicationConfiguration::CONST_REPORT_FOLDER_PATH; wxString xmlCoreFileName="config.xml"; wxString exeName=coreCalculation->GetStringConfig("exeName"); wxString tetraFileName=coreCalculation->GetStringConfig("tetrameshFileName"); wxString rootCorePath=this->PathCores+corePath; - +#ifdef __linux__ + rootCorePath.Replace("\\","/",true); +#endif /////////////////////////////////////////// //Ajout du dossier daté de résultat /////////////////////////////////////////// @@ -762,6 +768,7 @@ void ProjectManager::RunCoreCalculation(Element* coreCalculation) ext = "exe"; } #endif + if(!wxFileExists(this->PathCores+corePath+exeName)) { wxLogError(_("Calculation program file not found.")); @@ -870,7 +877,6 @@ void ProjectManager::RunCoreCalculation(Element* coreCalculation) wxLongLong durationOperation=wxDateTime::UNow().GetValue()-timeDebOperation.GetValue(); wxLogMessage(_("Total time calculation: %lld ms"),durationOperation.GetValue()); - } @@ -2182,7 +2188,7 @@ bool ProjectManager::UpdateZip(wxString folderfrom,wxString zipfilename) long nbfichier=tabFichiers.Count(); wxString tmpFileName=zipfilename+".tmp"; using namespace std; - auto_ptr in(new wxFFileInputStream(zipfilename)); + unique_ptr in(new wxFFileInputStream(zipfilename)); wxTempFileOutputStream out(tmpFileName); wxZipInputStream inzip(*in); diff --git a/src/isimpa/data_manager/projet_maillage.cpp b/src/isimpa/data_manager/projet_maillage.cpp index 9a0bc3b8..c640f85b 100644 --- a/src/isimpa/data_manager/projet_maillage.cpp +++ b/src/isimpa/data_manager/projet_maillage.cpp @@ -202,6 +202,7 @@ bool ProjectManager::RunTetGenMaillage(param_TetGenMaillage& paramMaillage) wxString ele=cacheFolder+sceneName+".1.ele"; wxString node=cacheFolder+sceneName+".1.node"; wxString neigh=cacheFolder+sceneName+".1.neigh"; + wxString skipped_face_file = cacheFolder + sceneName + "_skipped.face"; /////////////////////////////////////////// /// Conversion de la scène au format POLY @@ -250,7 +251,16 @@ bool ProjectManager::RunTetGenMaillage(param_TetGenMaillage& paramMaillage) sceneMesh.LoadMaillage(WXSTRINGTOSTDSTRING(face),WXSTRINGTOSTDSTRING(ele),WXSTRINGTOSTDSTRING(node),WXSTRINGTOSTDSTRING(neigh)); wxLogMessage(_("Loading ASCII files from mesh generator complete")); }else{ - std::vector& faces=logger->GetFaces(); + std::vector& faces = logger->GetFaces(); + + // Load faces from skipped_faces file (works with tetgen 1.6) + std::vector skippedFaces; + bool has_markers = sceneMesh._LoadFaceListeWithMarkers(skippedFaces, WXSTRINGTOSTDSTRING(skipped_face_file)); + if(has_markers){ + for (ivec4 face : skippedFaces) { + faces.push_back(face.d+1); + } + } std::vector faceInErr; faceInErr.reserve(faceInd.size()); diff --git a/src/isimpa/data_manager/python_interface/boost_python_header.h b/src/isimpa/data_manager/python_interface/boost_python_header.h index d05bbb69..f4ff85b2 100644 --- a/src/isimpa/data_manager/python_interface/boost_python_header.h +++ b/src/isimpa/data_manager/python_interface/boost_python_header.h @@ -37,8 +37,14 @@ #ifndef BOOST_PYTHON_STATIC_LIB #define BOOST_PYTHON_STATIC_LIB 1 #endif + +//#define BOOST_BIND_GLOBAL_PLACEHOLDERS +#include +using namespace boost::placeholders; + #include #include + #include #include diff --git a/src/isimpa/data_manager/python_interface/py_ui_module/samples/add_data_sample.py b/src/isimpa/data_manager/python_interface/py_ui_module/samples/add_data_sample.py index 831452e3..29e22f85 100644 --- a/src/isimpa/data_manager/python_interface/py_ui_module/samples/add_data_sample.py +++ b/src/isimpa/data_manager/python_interface/py_ui_module/samples/add_data_sample.py @@ -51,7 +51,10 @@ def __init__(self,idel): uictrl.element(self.appendpropertytext("modelName","","mesh.cbin",True,True)).hide() uictrl.element(self.appendpropertytext("tetrameshFileName","","tetramesh.mbin",True,True)).hide() uictrl.element(self.appendpropertytext("exeName","","md.py")).hide() + if sys.platform() == "Windows": uictrl.element(self.appendpropertytext("corePath","","md\\")).hide() + else: + uictrl.element(self.appendpropertytext("corePath","","md/")).hide() #User options coreconf.appendpropertylist("solver_mode","Calculation mode",[["Time","Static"],[0,1]],0,False,1,True) diff --git a/src/isimpa/data_manager/python_interface/pythonshell.cpp b/src/isimpa/data_manager/python_interface/pythonshell.cpp index 479b774f..80c16baa 100644 --- a/src/isimpa/data_manager/python_interface/pythonshell.cpp +++ b/src/isimpa/data_manager/python_interface/pythonshell.cpp @@ -33,6 +33,7 @@ #include #include "UtfConverter.h" #include "last_cpp_include.hpp" +#include #ifdef USE_PYTHON @@ -345,6 +346,7 @@ bool PythonShell::GetPythonManagedMenu(const int& element_type,const int& elemen }else{ return false; } + return true; } bool PythonShell::ins_pyelement(boost::python::object& py_el,const wxInt32& wxid, const wxString& module,const wxString& _pyclass) { diff --git a/src/isimpa/data_manager/tree_core/e_core_tlmcore.h b/src/isimpa/data_manager/tree_core/e_core_tlmcore.h index bef4b3a2..e48ec713 100644 --- a/src/isimpa/data_manager/tree_core/e_core_tlmcore.h +++ b/src/isimpa/data_manager/tree_core/e_core_tlmcore.h @@ -66,7 +66,7 @@ class E_Core_Tlm: public E_Core_Core this->AppendPropertyText("modelName","","mesh.cbin",true,true)->Hide(); this->AppendPropertyText("exeName","","TLM")->Hide(); - this->AppendPropertyText("corePath","","tlm\\")->Hide(); + this->AppendPropertyText("corePath","","tlm",wxFileName::GetPathSeparator())->Hide(); this->AppendPropertyText("tetrameshFileName","","tetramesh.mbin",true,true)->Hide(); } diff --git a/src/isimpa/data_manager/tree_rapport/e_report_file.cpp b/src/isimpa/data_manager/tree_rapport/e_report_file.cpp index ae37e140..1e4fad45 100644 --- a/src/isimpa/data_manager/tree_rapport/e_report_file.cpp +++ b/src/isimpa/data_manager/tree_rapport/e_report_file.cpp @@ -247,6 +247,7 @@ void E_Report_File::RefreshFolderContents() wxString fullPath; this->BuildFullPath(fullPath); + //Chargement des éléments déjà présent comme fils wxSortedArrayString currentFileChilds; wxString thisFullPath; diff --git a/src/isimpa/data_manager/tree_scene/e_scene_groupesurfaces_groupe.cpp b/src/isimpa/data_manager/tree_scene/e_scene_groupesurfaces_groupe.cpp index 1c7397de..6cd6167b 100644 --- a/src/isimpa/data_manager/tree_scene/e_scene_groupesurfaces_groupe.cpp +++ b/src/isimpa/data_manager/tree_scene/e_scene_groupesurfaces_groupe.cpp @@ -229,20 +229,27 @@ void E_Scene_Groupesurfaces_Groupe::InitProp() void E_Scene_Groupesurfaces_Groupe::PushFace(std::vector >& vectorToFeed,t_faceIndex faceIndex) { - while(faceIndex.group+1>vectorToFeed.size()) + // First resize vector to fit the expected faceIndex or groupIndex array index + while(faceIndex.group == -1 || faceIndex.group >= vectorToFeed.size()) { std::vector nvGroup; vectorToFeed.push_back(nvGroup); + if(faceIndex.group == -1 < 0) { + faceIndex.group = vectorToFeed.size() - 1; + } } - while(faceIndex.face+1>vectorToFeed[faceIndex.group].size()) + while(faceIndex.face == -1 || faceIndex.face >= vectorToFeed[faceIndex.group].size()) { vectorToFeed[faceIndex.group].push_back(ApplicationConfiguration::t_PropFace()); + if(faceIndex.face == -1 < 0) { + faceIndex.face = vectorToFeed[faceIndex.group].size() - 1; + } } - //Il faut renseigner l'element de la face en fonction du type de groupe - if(!isPointerGroup) + // Feed the arrays with data + // The face element must be filled in according to the type of group + if(!isPointerGroup) { vectorToFeed[faceIndex.group][faceIndex.face].idMaterial=this->GetIntegerConfig("idmat"); - else - { + } else { // gather parent element of the surface group if(this->pere) { diff --git a/src/isimpa/main/i_simpa_main.cpp b/src/isimpa/main/i_simpa_main.cpp index ed814f23..79beede7 100644 --- a/src/isimpa/main/i_simpa_main.cpp +++ b/src/isimpa/main/i_simpa_main.cpp @@ -386,7 +386,8 @@ MainUiFrame::MainUiFrame(wxLocale &lang) : wxFrame(NULL, -1, _("Interface ")+APP { WX_GL_RGBA, WX_GL_MIN_RED, 1, WX_GL_MIN_GREEN, 1, WX_GL_MIN_BLUE, 1, WX_GL_DEPTH_SIZE, 1, WX_GL_DOUBLEBUFFER, -# if defined(__WXMAC__) || defined(__WXQT__) || defined(__linux__) + +# if defined(__WXMAC__) || defined(__WXQT__) || defined(__linux__) GL_NONE }; # else None }; diff --git a/src/isimpa/manager/processManager.cpp b/src/isimpa/manager/processManager.cpp index 595140a9..e8152ee6 100644 --- a/src/isimpa/manager/processManager.cpp +++ b/src/isimpa/manager/processManager.cpp @@ -81,6 +81,9 @@ void processManager::HandleOutput() { if (IsErrorAvailable()) { wxTextInputStream tis(*GetErrorStream()); const wxString& errMsg(tis.ReadLine()); + // The IsErrorAvailable function seems to always return true. I think there must be an empty line in the error stream. -- Larry Pyeatt + if(errMsg.Len() > 0) + { if (!this->outlogs.empty()) { for (std::vector >::iterator itlogs = this->outlogs.begin(); itlogs != this->outlogs.end(); itlogs++) @@ -95,6 +98,7 @@ void processManager::HandleOutput() { errorMessage += msg + "\n"; } } + } if (!message.IsEmpty()) { wxLogMessage(message); } diff --git a/src/lib_interface/input_output/poly/poly.cpp b/src/lib_interface/input_output/poly/poly.cpp index b77cf53e..f6963855 100644 --- a/src/lib_interface/input_output/poly/poly.cpp +++ b/src/lib_interface/input_output/poly/poly.cpp @@ -430,7 +430,7 @@ bool CPoly::ImportPOLY(t_model& scene,const std::string& mfilename) } } } - + return true; } } //fin namespace \ No newline at end of file diff --git a/src/preprocess/tools/intersection_algo.h b/src/preprocess/tools/intersection_algo.h index 07980b77..ec1f48f1 100644 --- a/src/preprocess/tools/intersection_algo.h +++ b/src/preprocess/tools/intersection_algo.h @@ -583,7 +583,7 @@ inline void isect2(double VTX0[3], double VTX1[3], double VTX2[3], double VV0, d isect0=VV0+(VV1-VV0)*tmp; \ SUB(diff,VTX1,VTX0); \ MULT(diff,diff,tmp); \ - ADD(isectpoint0,diff,VTX0); \ + ADD(isectpoint0,diff,VTX0); \ tmp=D0/(D0-D2); /* isect1=VV0+(VV2-VV0)*tmp; \ */ /* SUB(diff,VTX2,VTX0); \ */ diff --git a/src/python_bindings/CMakeLists.txt b/src/python_bindings/CMakeLists.txt index 285257aa..d86c9337 100644 --- a/src/python_bindings/CMakeLists.txt +++ b/src/python_bindings/CMakeLists.txt @@ -13,9 +13,9 @@ FIND_PACKAGE(SWIG REQUIRED) INCLUDE(${SWIG_USE_FILE}) # Find package for building -FIND_PACKAGE(PythonLibs 3.8 EXACT REQUIRED) +FIND_PACKAGE(PythonLibs REQUIRED) # Find package for testing -FIND_PACKAGE(PythonInterp 3.8 EXACT REQUIRED) +FIND_PACKAGE(PythonInterp REQUIRED) INCLUDE_DIRECTORIES(${PYTHON_INCLUDE_PATH}) @@ -129,4 +129,4 @@ add_test(NAME check_retrocompat_recsurf set_property(TEST test_modelio check_retrocompat check_retrocompat_mesh check_retrocompat_gabe test_math check_retrocompat_recsurf PROPERTY ENVIRONMENT "PYTHONPATH=${CMAKE_INSTALL_PREFIX}" -) \ No newline at end of file +) diff --git a/src/python_bindings/libsimpa.i b/src/python_bindings/libsimpa.i index bb0ac245..8f077995 100644 --- a/src/python_bindings/libsimpa.i +++ b/src/python_bindings/libsimpa.i @@ -31,6 +31,22 @@ SWIG Python lib interface module declaration %array_class(formatMBIN::bintetraface, bintetrafaceArray); +%extend core_mathlib::ivec2 { + %rename(Index) operator[](int) const; + %ignore operator=(long _i); + %ignore operator long*; + long __getitem__(int i) { + return (*($self))[i]; + } + int __len__() { + return 3; + } + %pythoncode %{ + def __repr__(self): + return "[%s,%s,%s]" % (self[0],self[1],self[2]) + %} +} + %extend core_mathlib::ivec3 { %rename(Index) operator[](int) const; %ignore operator long*; @@ -59,6 +75,9 @@ SWIG Python lib interface module declaration %extend core_mathlib::base_vec3 { %rename(Index) operator[](int) const; + %ignore operator[](int); + + %rename(Assign) operator=(base_t _f); %ignore operator double*; double __getitem__(int i) { return (*($self))[i]; @@ -72,6 +91,19 @@ SWIG Python lib interface module declaration %} } +%extend core_mathlib::vec2 { + %ignore operator=(decimal _f); + %rename(Index) operator[](int _i) const; + %ignore operator[](int _i); + }; + +%extend core_mathlib::vec4 { + %ignore operator=(decimal _f); + %rename(Index) operator[](int _i) const; + %ignore operator[](int _i); + }; + + // Map a Python sequence into any sized C double array %typemap(in) double[ANY](double temp[$1_dim0]) { int i; diff --git a/src/spps/sppsNantes.cpp b/src/spps/sppsNantes.cpp index 32ccecf7..a2c79cdb 100644 --- a/src/spps/sppsNantes.cpp +++ b/src/spps/sppsNantes.cpp @@ -435,7 +435,7 @@ int MainProcess(int argc, char* argv[]) } } if((double)totalLost / (double)total > PARTICLE_LOST_WARNING_RATIO) { - fprintf(stderr, _("Warning %il particles has been in error on %il particles. The computation result may be wrong, please check the particles statitics file for more details."), totalLost, total); + fprintf(stderr, _("Warning %li particles has been in error on %li particles. The computation result may be wrong, please check the particles statitics file for more details."), totalLost, total); } //************************************************** // 9: Libère l'espace mémoire diff --git a/src/tetgen/predicates.cxx b/src/tetgen/predicates.cxx index 33817d79..bb713704 100644 --- a/src/tetgen/predicates.cxx +++ b/src/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/src/tetgen/tetgen.cxx b/src/tetgen/tetgen.cxx index da4ef3d7..899a47b6 100644 --- a/src/tetgen/tetgen.cxx +++ b/src/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,11 +181,11 @@ 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(char* filebasename) { @@ -258,11 +259,11 @@ bool tetgenio::load_node(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(char* filebasename) { @@ -339,11 +340,11 @@ bool tetgenio::load_edge(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(char* filebasename) { @@ -435,11 +436,11 @@ bool tetgenio::load_face(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(char* filebasename) { @@ -542,11 +543,11 @@ bool tetgenio::load_tet(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(char* filebasename) { @@ -603,12 +604,12 @@ bool tetgenio::load_vol(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(char* filebasename) { @@ -631,6 +632,11 @@ bool tetgenio::load_var(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(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,11 +726,11 @@ bool tetgenio::load_var(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(char* filebasename) { @@ -754,9 +765,13 @@ bool tetgenio::load_mtr(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,11 +799,58 @@ bool tetgenio::load_mtr(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(char* filebasename) { @@ -1177,56 +1239,6 @@ bool tetgenio::load_poly(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,15 +1246,15 @@ bool tetgenio::load_poly(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. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// 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) { @@ -1364,7 +1376,6 @@ bool tetgenio::load_off(char* filebasename) } } - // Close file fclose(fp); // Decide the firstnumber of the index. @@ -1392,19 +1403,19 @@ bool tetgenio::load_off(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. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// 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) { @@ -1583,7 +1594,6 @@ bool tetgenio::load_ply(char* filebasename) } } - // Close file fclose(fp); // Decide the firstnumber of the index. @@ -1611,19 +1621,31 @@ bool tetgenio::load_ply(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(char* filebasename) { @@ -1650,52 +1672,97 @@ bool tetgenio::load_stl(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,14 +1810,14 @@ bool tetgenio::load_stl(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(char* filebasename, int istetmesh) { @@ -1776,7 +1843,7 @@ bool tetgenio::load_medit(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(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(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(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(char* filebasename, int istetmesh) } } - // Close file fclose(fp); // Decide the firstnumber of the index. @@ -2079,14 +2144,14 @@ bool tetgenio::load_medit(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,7 +2176,6 @@ bool testIsBigEndian() return false; } - bool tetgenio::load_vtk(char* filebasename) { FILE *fp; @@ -2351,11 +2415,11 @@ bool tetgenio::load_vtk(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(char* filebasename, int object) { @@ -2389,18 +2453,20 @@ bool tetgenio::load_plc(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(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(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(char* filebasename) +void tetgenio::save_nodes(const char *filebasename) { FILE *fout; char outnodefilename[FILENAMESIZE]; @@ -2476,13 +2543,13 @@ void tetgenio::save_nodes(char* filebasename) } } -/////////////////////////////////////////////////////////////////////////////// -// // -// save_elements() Save elements to a .ele file. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// save_elements() Save elements to a .ele file. // +// // +//============================================================================// -void tetgenio::save_elements(char* filebasename) +void tetgenio::save_elements(const char* filebasename) { FILE *fout; char outelefilename[FILENAMESIZE]; @@ -2523,13 +2590,13 @@ void tetgenio::save_elements(char* filebasename) fclose(fout); } -/////////////////////////////////////////////////////////////////////////////// -// // -// save_faces() Save faces to a .face file. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// save_faces() Save faces to a .face file. // +// // +//============================================================================// -void tetgenio::save_faces(char* filebasename) +void tetgenio::save_faces(const char* filebasename) { FILE *fout; char outfacefilename[FILENAMESIZE]; @@ -2552,11 +2619,11 @@ void tetgenio::save_faces(char* filebasename) fclose(fout); } -/////////////////////////////////////////////////////////////////////////////// -// // -// save_edges() Save egdes to a .edge file. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// save_edges() Save egdes to a .edge file. // +// // +//============================================================================// void tetgenio::save_edges(char* filebasename) { @@ -2580,11 +2647,11 @@ void tetgenio::save_edges(char* filebasename) fclose(fout); } -/////////////////////////////////////////////////////////////////////////////// -// // -// save_neighbors() Save egdes to a .neigh file. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// save_neighbors() Save egdes to a .neigh file. // +// // +//============================================================================// void tetgenio::save_neighbors(char* filebasename) { @@ -2611,15 +2678,15 @@ void tetgenio::save_neighbors(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(char* filebasename) +void tetgenio::save_poly(const char *filebasename) { FILE *fout; facet *f; @@ -2711,13 +2778,13 @@ void tetgenio::save_poly(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(char* filebasename) { @@ -2757,17 +2824,17 @@ void tetgenio::save_faces2smesh(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,16 +3060,16 @@ 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. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// 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) { @@ -3047,27 +3116,76 @@ bool tetgenbehavior::parse_commandline(int argc, 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')) { - addsteiner_algo = (argv[i][j + 1] - '0'); - 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'); + j++; } } } 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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,273 +6711,26 @@ 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; - - 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]; + REAL A[4][4], rhs[4], D; + int indx[4]; // Compute the coefficient matrix A (3x3). A[0][0] = pb[0] - pa[0]; @@ -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; - } - } - } - } else if (loc == ONVERTEX) { - // The point already exist. Do nothing and return. + 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); -enum tetgenmesh::locateresult tetgenmesh::locate(point searchpt, - triface* searchtet) + return loc; +} + +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; + } + } + } - // 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; + // Create the initial cavity C(p) which contains all tetrahedra that + // intersect p. It may include 1, 2, or n tetrahedra. - unmarkface(fliptets[0]); + 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) { - 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); + // 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; + } + + // Create the cavity C(p). + + 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; - } - - // 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); + 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 + // 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 (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]); + if (v_out < 1024) { + for (i = 0; i < f_out; i++) { + pcavetet = (triface *) fastlookup(cavebdrylist, i); + oldtet = *pcavetet; + + // 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,182 +13991,129 @@ 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. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// 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::unifysubfaces(face *f1, face *f2) +void tetgenmesh::unifysegments() { - 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. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::unifysegments() -{ - badface *facelink = NULL, *newlinkitem, *f1, *f2; - face *facperverlist, sface; - face subsegloop, testseg; - point torg, tdest; - REAL ori1, ori2, ori3; - REAL n1[3], n2[3]; - int *idx2faclist; - int idx, k, m; - - if (b->verbose > 1) { - printf(" Unifying segments.\n"); + badface *facelink = NULL, *newlinkitem, *f1, *f2; + face *facperverlist, sface; + face subsegloop, testseg; + point torg, tdest; + 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 dihedangarray; - delete [] shperverlist; - delete [] idx2shlist; + + 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 (diff <= 0) { //if (cosmaxd <= fc->cosdihed_in) { + 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); - 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. - 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); - } - } - 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 (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); + 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); + if (tri_edge_test(pa, pb, pd, startpt, endpt, NULL, 1, types, poss)) { + break; // Found the edge. + } + enextself(spintet); + eprevself(searchtet); + } + esymself(searchtet); + } + else { // dir == ACROSSEDGE; + if (issubseg(searchtet)) { + 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; } + } - success = 0; + if (!splitsegflag) { + // Try to recover this segment by adding Steiner points near it. - if (dir == ACROSSFACE) { - // Find a Steiner points inside the polyhedron. - if (add_steinerpt_in_schoenhardtpoly(abtets, endi, 0)) { - success = 1; + 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. } - } 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++; - } + n++; // Count a tet in the star. + fnextself(spintet); + if (spintet.tet == searchtet.tet) break; + } + + 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); + } + + 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); - } - } else { - if (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)); + 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 ((dir != (int) SELF_INTERSECT) && steinerflag) { + // Add a Steiner point at the barycenter of this subface. + 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; + ivf.assignmeshsize = b->metric; - st_facref_count++; - if (steinerleft > 0) steinerleft--; + 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; + + 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; + } + } + + 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 { - success = 0; - } + } 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 (!success) { - if (misshlist != NULL) { - // Save this subface. - misshlist->newindex((void **) &parysh); - *parysh = searchsh; - } - } + 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. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// 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,84 +22333,263 @@ 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; + for (i = 0; i < 3; i++) { + bestpt[i] = startpt[i] = smtpt[i]; + } + + // Iterate until the obj function is not improved. + while (1) { + + // 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; @@ -20979,7 +22614,6 @@ int tetgenmesh::suppressbdrysteinerpoint(point steinerpt) 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; v1[2] /= len; @@ -20988,7 +22622,6 @@ int tetgenmesh::suppressbdrysteinerpoint(point steinerpt) 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; - } - sremovevertex(steinerpt, &parentsh, splitseg, slawson); - if (vt == FREESEGVERTEX) { - // The original segment is returned in 'rightseg'. - rightseg.shver = 0; - } + // 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 - // 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); + delete [] segshlist; + + if (i < n) { + //assert(0); + delete [] newsteiners; + return 0; } - // 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,13 +24090,35 @@ 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 (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; @@ -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,26 +24370,111 @@ void tetgenmesh::carveholes() } // if (!b->convex) } -/////////////////////////////////////////////////////////////////////////////// -// // -// reconstructmesh() Reconstruct a tetrahedral mesh. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::reconstructmesh() +// [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) { - tetrahedron *ver2tetarray; - point *idx2verlist; - triface tetloop, checktet, prevchktet; - triface hulltet, face1, face2; - tetrahedron tptr; - face subloop, neighsh, nextsh; - face segloop; - shellface sptr; - point p[4], q[3]; - REAL ori, attrib, volume; - REAL angtol, ang; - int eextras, marker = 0; + 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() +{ + tetrahedron *ver2tetarray; + point *idx2verlist; + triface tetloop, checktet, prevchktet; + triface hulltet, face1, face2; + tetrahedron tptr; + face subloop, neighsh, nextsh; + face segloop; + shellface sptr; + point p[4], q[3]; + REAL ori, attrib, volume; + REAL cosang_tol, cosang; + REAL n1[3], n2[3]; + int eextras, marker = 0; int bondflag; int t1ver; int idx, i, j, k; @@ -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); - if (!pinfected(pa)) { - vt = pointtype(pa); - if ((vt != FREESEGVERTEX) && (vt != FREEFACETVERTEX)) { - pinfect(pa); - vertlist->newindex((void **) &parypt); - *parypt = pa; + 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 == 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); + enum verttype nearpt_type = pointtype(nearpt); + enum verttype steiner_type = pointtype(steinerpt); - // Initialize the return values. - encpt = NULL; - qflag = 0; + 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; +} - len = distance(forg, fdest); - r = 0.5 * len; - for (i = 0; i < 3; i++) { - ccent[i] = 0.5 * (forg[i] + fdest[i]); - } +//============================================================================// +// // +// enqueuesubface() Queue a subface or a subsegment for encroachment check.// +// // +//============================================================================// - // First check its quality. - if (checkconstraints && (areabound(*chkseg) > 0.0)) { - if (len > areabound(*chkseg)) { - 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->fixedvolume) { - if ((len * len * len) > b->maxvolume) { - qflag = 1; - return 1; - } - } +//============================================================================// +// // +// enqueuetetrahedron() Queue a tetrahedron for quality check. // +// // +//============================================================================// - 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; - } +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) { + // 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; + } - 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; + // Goto the adjacent subface at this subedge. + int fcount = 0; + while (fcount < 100000) { + esymself(searchtet); + if (issubface(searchtet)) break; + fsymself(searchtet); + fcount++; } - } // if (b->nobisect) + if (!issubface(searchtet)) { + terminatetetgen(this, 2); // report a bug + } + + // 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; } - } + } 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; +} + +//============================================================================// +// // +// 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(); } - // Clear the pool. - 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. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// 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; + } + } - //assert(unflipqueue->objects > 0l); - flipqueue = new arraypool(sizeof(badface), 10); + REAL A[4][4], rhs[4], D; + int indx[4]; + int i, j; - // Swap the two flip queues. - swapqueue = flipqueue; - flipqueue = unflipqueue; - unflipqueue = swapqueue; + // 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; + //} + } - totalsmtcount = 0l; - iter = 0; + Lmax = sqrt(Lmax); + Lmin = sqrt(Lmin); - while (flipqueue->objects > 0l) { + // Caluclate the Lmax / Lmin edge ratio (to detect very short edge). + bf->cent[2] = Lmax / Lmin; + bf->cent[3] = Lmin; - smtcount = 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; - 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); + 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(" Smooth %ld points.\n", smtcount); + // 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; } - totalsmtcount += smtcount; + } - if (smtcount == 0l) { - // No point has been smoothed. - break; - } else { - iter++; - if (iter == 2) { //if (iter >= b->optpasses) { - break; - } + 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]; } + // 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. + //} + } - // Swap the two flip queues. - swapqueue = flipqueue; - flipqueue = unflipqueue; - unflipqueue = swapqueue; - } // while + // 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 + + 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,21 +36495,22 @@ 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 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(char *switches, tetgenio *in, tetgenio *out, tetgenio *addin, tetgenio *bgmin) @@ -31238,7 +36561,7 @@ void tetrahedralize(char *switches, tetgenio *in, tetgenio *out, #endif // not TETLIBRARY } -//// //// -//// //// -//// main_cxx ///////////////////////////////////////////////////////////////// +// // +// // +//== main_cxx ================================================================// diff --git a/src/tetgen/tetgen.h b/src/tetgen/tetgen.h index 3196e031..ef67b5d0 100644 --- a/src/tetgen/tetgen.h +++ b/src/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 { @@ -196,10 +191,12 @@ class tetgenio { // '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; @@ -215,11 +212,14 @@ class tetgenio { // '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. + // '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; @@ -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 @@ -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; @@ -334,21 +342,22 @@ class tetgenio { bool load_vol(char*); bool load_var(char*); bool load_mtr(char*); - bool load_pbc(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(char*); - void save_elements(char*); - void save_faces(char*); + void save_nodes(const char*); + void save_elements(const char*); + void save_faces(const char*); void save_edges(char*); void save_neighbors(char*); - void save_poly(char*); + void save_poly(const char*); void save_faces2smesh(char*); // Read line and parse string functions. @@ -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,6 +399,8 @@ class tetgenio { tetrahedronattributelist = (REAL *) NULL; tetrahedronvolumelist = (REAL *) NULL; neighborlist = (int *) NULL; + tet2facelist = (int *) NULL; + tet2edgelist = (int *) NULL; numberoftetrahedra = 0; numberofcorners = 4; numberoftetrahedronattributes = 0; @@ -396,13 +408,14 @@ class tetgenio { trifacelist = (int *) NULL; trifacemarkerlist = (int *) NULL; o2facelist = (int *) NULL; - adjtetlist = (int *) NULL; + 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; @@ -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; @@ -430,6 +447,7 @@ class tetgenio { numberofvfacets = 0; numberofvcells = 0; + tetunsuitable = NULL; geomhandle = NULL; @@ -442,7 +460,7 @@ class tetgenio { // 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,26 +591,26 @@ 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 { @@ -586,16 +622,16 @@ class tetgenbehavior { 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. + // 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,6 +708,10 @@ class tetgenbehavior { char addinfilename[1024]; char bgmeshfilename[1024]; + // 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: @@ -673,7 +724,7 @@ class tetgenbehavior { // - 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; + enum objecttype {NODES, POLY, OFF, PLY, STL, MEDIT, VTK, MESH, NEU_MESH} object; void syntax(); @@ -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; @@ -888,54 +957,54 @@ class tetgenmesh { 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 { @@ -1057,19 +1126,19 @@ class tetgenmesh { 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: @@ -1080,15 +1149,23 @@ class tetgenmesh { 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; @@ -1169,12 +1259,14 @@ class tetgenmesh { 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,8 +1289,8 @@ 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_dihedangle; // Minimize the maximum dihedral angle. + int min_max_aspectratio; // Minimize the maximum aspect ratio. + int min_max_dihedangle; // Minimize the maximum dihedral angle. // The initial and improved value. REAL initval, imprval; @@ -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, + 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}; + TOUCHEDGE, TOUCHFACE, ACROSSVERT, ACROSSEDGE, ACROSSFACE, + 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,33 +1456,50 @@ 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]; @@ -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); @@ -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, 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,108 +1911,91 @@ 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*, arraypool*, arraypool*); - // Facet recovery by cavity re-triangulation [Si and Gaertner 2011]. void delaunizecavity(arraypool*, arraypool*, arraypool*, arraypool*, arraypool*, arraypool*); @@ -1875,42 +2003,46 @@ class tetgenmesh { 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 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,9 +2315,19 @@ 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; - xmax = xmin = ymax = ymin = zmax = zmin = 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; @@ -2130,15 +2335,17 @@ class tetgenmesh { 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 (highordertable != NULL) { - delete [] highordertable; + if (segment_info_list != NULL) { + delete [] segment_info_list; } + + if (subdomain_markers != NULL) { + delete [] subdomain_markers; + } + + initializetetgenmesh(); + } + + tetgenmesh() + { + initializetetgenmesh(); } ~tetgenmesh() @@ -2215,20 +2454,19 @@ 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. // -// // -/////////////////////////////////////////////////////////////////////////////// +//============================================================================// +// // +// 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); @@ -2236,20 +2474,18 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, #ifdef TETLIBRARY 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) { - // Release the allocated memory. - if (m) { - m->freememory(); - } #ifdef TETLIBRARY throw x; #else @@ -2263,30 +2499,36 @@ 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: + case 10: 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 // assumption that all addresses of tetrahedra are aligned to sixteen- @@ -2308,6 +2550,16 @@ inline void tetgenmesh::decode(tetrahedron ptr, triface& t) { (t).tet = (tetrahedron *) ((uintptr_t) (ptr) ^ (uintptr_t) (t).ver); } +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. @@ -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 @@ -2474,6 +2726,16 @@ 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) { @@ -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 @@ -2904,11 +3166,17 @@ 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, @@ -2989,11 +3257,11 @@ inline void tetgenmesh::stdissolve(face& s) (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. @@ -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) { @@ -3083,11 +3351,11 @@ 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]; @@ -3109,26 +3377,6 @@ inline void tetgenmesh::setpointtype(point pt, enum verttype value) { ((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. @@ -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; } } @@ -3258,7 +3532,6 @@ inline void tetgenmesh::point2shorg(point pa, face& searchsh) } else if ((point) searchsh.sh[4] == pa) { searchsh.shver = (searchsh.sh[5] != NULL ? 2 : 1); } else { - assert((point) searchsh.sh[5] == pa); // SELF_CHECK searchsh.shver = 4; } } @@ -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
" + _("Developers") + "
Judicaël PicautIfsttar
Nicolas FortinIfsttar
Judicaël PicautUniversité Gustave Eiffel (formely Ifsttar, LCPC)
Nicolas FortinUniversité Gustave Eiffel (formely Ifsttar, LCPC)
 
" + _("Contributors") + "