Repository: inducer/meshpy
Branch: main
Commit: a4f35d5f9293
Files: 71
Total size: 2.3 MB
Directory structure:
gitextract_18fujhnm/
├── .editorconfig
├── .gitattributes
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ ├── config.yml
│ │ └── feature_request.md
│ ├── dependabot.yml
│ └── workflows/
│ ├── autopush.yml
│ ├── ci.yml
│ └── wheels.yml
├── .gitignore
├── .gitlab-ci.yml
├── CITATION.cff
├── LICENSE
├── README.rst
├── doc/
│ ├── conf.py
│ ├── faq.rst
│ ├── geometry.rst
│ ├── index.rst
│ ├── installation.rst
│ ├── tri-tet.rst
│ └── upload-docs.sh
├── examples/
│ ├── MESH_README.txt
│ ├── airfoil3d.py
│ ├── box-in-box.py
│ ├── clean.sh
│ ├── demo.py
│ ├── jw_mesh_examples.ipynb
│ ├── jw_meshtools.py
│ ├── ka-6d.ply
│ ├── mesh_ply.py
│ ├── nico_mesh.py
│ ├── test_ball.py
│ ├── test_cylinder.py
│ ├── test_tet_torus.py
│ ├── test_tetgen.py
│ ├── test_tetgen_2.py
│ ├── test_tri_pml.py
│ ├── test_tri_quadratic.py
│ ├── test_tri_simple_square.py
│ ├── test_triangle.py
│ ├── test_triangle_refine.py
│ ├── test_triangle_with_specified_points.py
│ ├── tet-size-control.py
│ ├── tri-boundary-markers.py
│ ├── tri-refinement-spec.py
│ └── write_dolfin.py
├── meshpy/
│ ├── __init__.py
│ ├── common.py
│ ├── geometry.py
│ ├── naca.py
│ ├── ply.py
│ ├── tet.py
│ ├── tools.py
│ └── triangle.py
├── meson.build
├── patches/
│ ├── mk-patch
│ ├── tetgen-1.4.2.patch
│ └── tetgen-1.4.3.patch
├── pyproject.toml
├── src/
│ └── cpp/
│ ├── foreign_array.hpp
│ ├── foreign_array_wrap.hpp
│ ├── predicates.cpp
│ ├── tetgen-LICENSE
│ ├── tetgen.cpp
│ ├── tetgen.h
│ ├── triangle.cpp
│ ├── triangle.h
│ ├── wrap_tetgen.cpp
│ ├── wrap_triangle.cpp
│ └── wrapper.cpp
└── test/
└── test_meshpy.py
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
# https://editorconfig.org/
# https://github.com/editorconfig/editorconfig-vim
# https://github.com/editorconfig/editorconfig-emacs
root = true
[*]
indent_style = space
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.py]
indent_size = 4
[*.rst]
indent_size = 4
[*.cpp]
indent_size = 2
[*.hpp]
indent_size = 2
[*.yml]
indent_size = 4
# There may be one in doc/
[Makefile]
indent_style = tab
# https://github.com/microsoft/vscode/issues/1679
[*.md]
trim_trailing_whitespace = false
================================================
FILE: .gitattributes
================================================
.github/ export-ignore
.gitignore export-ignore
.gitattributes export-ignore
.gitlab-ci.yml export-ignore
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Environment (please complete the following information):**
- OS: [e.g. iOS]
- MeshPy version: [e.g 2021.1]
- Python version: [e.g. 3.10]
**Additional context**
Add any other context about the problem here.
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: true
contact_links:
- name: ❓ Question
url: https://github.com/inducer/meshpy/discussions/categories/q-a
about: Ask and answer questions about MeshPy on Discussions
- name: 🔧 Troubleshooting
url: https://github.com/inducer/meshpy/discussions/categories/troubleshooting
about: For troubleshooting help, see the Discussions
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
# Set update schedule for GitHub Actions
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
groups:
actions:
patterns:
- "*"
# vim: sw=4
================================================
FILE: .github/workflows/autopush.yml
================================================
name: Gitlab mirror
on:
push:
branches:
- main
jobs:
autopush:
name: Automatic push to gitlab.tiker.net
if: startsWith(github.repository, 'inducer/')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- run: |
curl -L -O https://tiker.net/ci-support-v0
. ./ci-support-v0
mirror_github_to_gitlab
env:
GITLAB_AUTOPUSH_KEY: ${{ secrets.GITLAB_AUTOPUSH_KEY }}
# vim: sw=4
================================================
FILE: .github/workflows/ci.yml
================================================
name: CI
on:
push:
branches:
- main
pull_request:
schedule:
- cron: '17 3 * * 0'
concurrency:
group: ${{ github.head_ref || github.ref_name }}
cancel-in-progress: true
jobs:
ruff:
name: Ruff
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
submodules: true
- uses: actions/setup-python@v6
- name: "Main Script"
run: |
pip install ruff
ruff check
pytest:
name: Pytest on Py${{ matrix.python-version }}
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.10', '3.12', '3.x']
steps:
- uses: actions/checkout@v6
-
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- name: "Main Script"
run: |
PROJECT_INSTALL_FLAGS="--no-build-isolation"
EXTRA_INSTALL="numpy pybind11 meson-python ninja"
curl -L -O https://gitlab.tiker.net/inducer/ci-support/raw/main/build-and-test-py-project.sh
. ./build-and-test-py-project.sh
pytest-macos:
name: Pytest on macOS
runs-on: macos-latest
steps:
- uses: actions/checkout@v6
-
uses: actions/setup-python@v6
with:
python-version: '3.x'
- name: "Main Script"
run: |
export CC=gcc
PROJECT_INSTALL_FLAGS="--no-build-isolation"
EXTRA_INSTALL="numpy pybind11 meson-python ninja"
curl -L -O https://gitlab.tiker.net/inducer/ci-support/raw/main/build-and-test-py-project.sh
. ./build-and-test-py-project.sh
docs:
name: Documentation
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
-
uses: actions/setup-python@v6
with:
python-version: '3.x'
- name: "Main Script"
run: |
PROJECT_INSTALL_FLAGS="--no-build-isolation"
EXTRA_INSTALL="numpy pybind11 meson-python ninja"
curl -L -O https://gitlab.tiker.net/inducer/ci-support/raw/main/ci-support.sh
. ci-support.sh
build_py_project_in_venv
build_docs
================================================
FILE: .github/workflows/wheels.yml
================================================
name: Build wheels
# inspired by: https://github.com/pypa/cibuildwheel/blob/main/examples/github-deploy.yml
on:
push:
branches:
- main
tags:
- v*
pull_request:
release:
types: [created]
jobs:
build_sdist:
name: Build source distribution
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
submodules: true
- name: Build sdist
run: pipx run build --sdist
- uses: actions/upload-artifact@v7
with:
name: cibw-sdist
path: dist/*.tar.gz
build_wheels:
name: Build wheels on ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-15-intel, macos-14]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v6
- name: Build wheels
uses: pypa/cibuildwheel@v3.4.1
env:
CIBW_SKIP: "*-musllinux_i686 *-manylinux_i686 *-win32 cp31?t-*"
- uses: actions/upload-artifact@v7
with:
name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}
path: ./wheelhouse/*.whl
deploy:
needs: [build_sdist, build_wheels]
runs-on: ubuntu-latest
environment: pypi
permissions:
id-token: write
name: Deploy
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
steps:
- uses: actions/checkout@v6
- name: download artifacts
uses: actions/download-artifact@v8
with:
pattern: cibw-*
path: dist
merge-multiple: true
- name: deploy
uses: pypa/gh-action-pypi-publish@v1.13.0
================================================
FILE: .gitignore
================================================
build
*.dat
test/*.lua
MANIFEST
*.vtk
*.ele
*.face
*.neu
*.poly
*.node
.*.swp
test/ParaViewTrace*.pvs
dist
*~
Makefile
siteconf.py
tags
doc/html
*.orig
*.egg-info
*.pyc
*.pyo
doc/.build
*.pyd
*.so
setuptools*egg
setuptools*pth
setuptools*tar.gz
setuptools*pth
.sw[op]
.eggs
.cache
CMakeCache*
CMakeFiles
cmake_install.cmake
tmp
.ipynb_checkpoints
================================================
FILE: .gitlab-ci.yml
================================================
Ruff:
script: |
pipx install ruff
ruff check
tags:
- docker-runner
except:
- tags
Python 3:
script: |
PROJECT_INSTALL_FLAGS="--no-build-isolation"
EXTRA_INSTALL="numpy pybind11 meson-python ninja"
curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/main/build-and-test-py-project.sh
. ./build-and-test-py-project.sh
tags:
- python3
except:
- tags
artifacts:
reports:
junit: test/pytest.xml
Documentation:
script: |
PROJECT_INSTALL_FLAGS="--no-build-isolation"
EXTRA_INSTALL="numpy pybind11 meson-python ninja"
curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/main/build-docs.sh
. ./build-docs.sh
tags:
- python3
================================================
FILE: CITATION.cff
================================================
cff-version: 1.2.0
message: "If you use this software, please cite it as below."
authors:
- family-names: "Kloeckner"
given-names: "Andreas"
orcid: "https://orcid.org/0000-0003-1228-519X"
- family-names: Brun
given-names: Lorenz
- family-names: Liu
given-names: Benyuan
- family-names: Klemenc
given-names: Simon
- family-names: Fkikl
given-names: Alex
- family-names: Gohlke
given-names: Christoph
- family-names: Coon
given-names: Ethan
- family-names: Oxberry
given-names: Geoffrey
- family-names: Veselý
given-names: Jozef
- family-names: Wala
given-names: Matt
- family-names: Smith
given-names: Matt
- family-names: Potrowl
given-names: Peter
- family-names: Kurtz
given-names: Aidan
title: "MeshPy"
version: 2022.1.1
doi: 10.5281/zenodo.7296572
date-released: 2022-11-06
url: "https://github.com/inducer/meshpy"
license: MIT
================================================
FILE: LICENSE
================================================
-------------------------------------------------------------------------------
Triangle License
-------------------------------------------------------------------------------
Copyright 1993, 1995, 1997, 1998, 2002, 2005 Jonathan Richard Shewchuk
2360 Woolsey #H
Berkeley, California 94705-1927
Please send bugs and comments to jrs@cs.berkeley.edu
Created as part of the Quake project (tools for earthquake simulation).
Supported in part by NSF Grant CMS-9318163 and an NSERC 1967 Scholarship.
There is no warranty whatsoever. Use at your own risk.
Triangle generates exact Delaunay triangulations, constrained Delaunay
triangulations, conforming Delaunay triangulations, Voronoi diagrams, and
high-quality triangular meshes. The latter can be generated with no small
or large angles, and are thus suitable for finite element analysis.
Show Me graphically displays the contents of the geometric files used by
Triangle. Show Me can also write images in PostScript form.
Information on the algorithms used by Triangle, including complete
references, can be found in the comments at the beginning of the triangle.c
source file. Another listing of these references, with PostScript copies
of some of the papers, is available from the Web page
http://www.cs.cmu.edu/~quake/triangle.research.html
------------------------------------------------------------------------------
These programs may be freely redistributed under the condition that the
copyright notices (including the copy of this notice in the code comments
and the copyright notice printed when the `-h' switch is selected) are
not removed, and no compensation is received. Private, research, and
institutional use is free. You may distribute modified versions of this
code UNDER THE CONDITION THAT THIS CODE AND ANY MODIFICATIONS MADE TO IT
IN THE SAME FILE REMAIN UNDER COPYRIGHT OF THE ORIGINAL AUTHOR, BOTH
SOURCE AND OBJECT CODE ARE MADE FREELY AVAILABLE WITHOUT CHARGE, AND
CLEAR NOTICE IS GIVEN OF THE MODIFICATIONS. Distribution of this code as
part of a commercial system is permissible ONLY BY DIRECT ARRANGEMENT
WITH THE AUTHOR. (If you are not directly supplying this code to a
customer, and you are instead telling them how they can obtain it for
free, then you are not required to make any arrangement with me.)
-------------------------------------------------------------------------------
TetGen License
-------------------------------------------------------------------------------
TetGen is distributed under a dual licensing scheme. You can
redistribute it and/or modify it under the terms of the GNU Affero
General Public License as published by the Free Software Foundation,
either version 3 of the License, or (at your option) any later
version. A copy of the GNU Affero General Public License is reproduced
below.
If the terms and conditions of the AGPL v.3. would prevent you from
using TetGen, please consider the option to obtain a commercial
license for a fee. These licenses are offered by the Weierstrass
Institute for Applied Analysis and Stochastics (WIAS). As a rule,
licenses are provided "as-is", unlimited in time for a one time
fee. Please send corresponding requests to:
tetgen@wias-berlin.de. Please do not forget to include some
description of your company and the realm of its activities.
=====================================================================
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
Copyright © 2007 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.
(See src/cpp/tetgen-LICENSE for the full license text)
-------------------------------------------------------------------------------
Wrapper license
-------------------------------------------------------------------------------
The wrapper is licensed to you under the following terms:
Copyright (c) 2004-2008 Andreas Kloeckner
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.rst
================================================
MeshPy: Simplicial Mesh Generation from Python
==============================================
.. image:: https://gitlab.tiker.net/inducer/meshpy/badges/main/pipeline.svg
:alt: Gitlab Build Status
:target: https://gitlab.tiker.net/inducer/meshpy/commits/main
.. image:: https://github.com/inducer/meshpy/actions/workflows/ci.yml/badge.svg
:alt: Github Build Status
:target: https://github.com/inducer/meshpy/actions/workflows/ci.yml
.. image:: https://badge.fury.io/py/MeshPy.svg
:alt: Python Package Index Release Page
:target: https://pypi.org/project/meshpy/
.. image:: https://zenodo.org/badge/2757253.svg
:alt: Zenodo DOI for latest release
:target: https://zenodo.org/badge/latestdoi/2757253
MeshPy offers quality triangular and tetrahedral mesh generation for Python.
Meshes of this type are mainly used in finite-element simulation codes, but
also have many other applications ranging from computer graphics to robotics.
In order to generate 2D and 3D meshes, MeshPy provides Python interfaces to
two well-regarded mesh generators: `Triangle
`__ by J. Shewchuk and `TetGen
`__ by Hang Si. The two are included in the
package in slightly modified versions.
For an interface to `gmsh `__, by Christophe Geuzaine
and Jean-Francois Remacle, see `gmsh_interop `__.
Online resources
================
* `Home page `__
* `Documentation `__
* `Source `__
* `Package index `__
* `Discussions `__
================================================
FILE: doc/conf.py
================================================
from importlib import metadata
from urllib.request import urlopen
_conf_url = \
"https://raw.githubusercontent.com/inducer/sphinxconfig/main/sphinxconfig.py"
with urlopen(_conf_url) as _inf:
exec(compile(_inf.read(), _conf_url, "exec"), globals())
copyright = "2013-24, Andreas Kloeckner and contributors"
release = metadata.version("meshpy")
version = ".".join(release.split(".")[:2])
================================================
FILE: doc/faq.rst
================================================
Licensing Information
=====================
Wrapper License
---------------
MeshPy (the wrapper) is licensed to you under the MIT/X Consortium license:
Copyright (c) 2008 Andreas Klöckner
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
Triangle License
----------------
Copyright 1993, 1995, 1997, 1998, 2002, 2005 Jonathan Richard Shewchuk
2360 Woolsey #H
Berkeley, California 94705-1927
Please send bugs and comments to jrs@cs.berkeley.edu
Created as part of the Quake project (tools for earthquake simulation).
Supported in part by NSF Grant CMS-9318163 and an NSERC 1967 Scholarship.
There is no warranty whatsoever. Use at your own risk.
Triangle generates exact Delaunay triangulations, constrained Delaunay
triangulations, conforming Delaunay triangulations, Voronoi diagrams, and
high-quality triangular meshes. The latter can be generated with no small
or large angles, and are thus suitable for finite element analysis.
Show Me graphically displays the contents of the geometric files used by
Triangle. Show Me can also write images in PostScript form.
Information on the algorithms used by Triangle, including complete
references, can be found in the comments at the beginning of the triangle.c
source file. Another listing of these references, with PostScript copies
of some of the papers, is available from the Web page
https://www.cs.cmu.edu/~quake/triangle.research.html
These programs may be freely redistributed under the condition that the
copyright notices (including the copy of this notice in the code comments
and the copyright notice printed when the '-h' switch is selected) are
not removed, and no compensation is received. Private, research, and
institutional use is free. You may distribute modified versions of this
code UNDER THE CONDITION THAT THIS CODE AND ANY MODIFICATIONS MADE TO IT
IN THE SAME FILE REMAIN UNDER COPYRIGHT OF THE ORIGINAL AUTHOR, BOTH
SOURCE AND OBJECT CODE ARE MADE FREELY AVAILABLE WITHOUT CHARGE, AND
CLEAR NOTICE IS GIVEN OF THE MODIFICATIONS. Distribution of this code as
part of a commercial system is permissible ONLY BY DIRECT ARRANGEMENT
WITH THE AUTHOR. (If you are not directly supplying this code to a
customer, and you are instead telling them how they can obtain it for
free, then you are not required to make any arrangement with me.)
TetGen License
--------------
TetGen is distributed under a dual licensing scheme. You can
redistribute it and/or modify it under the terms of the GNU Affero
General Public License as published by the Free Software Foundation,
either version 3 of the License, or (at your option) any later
version. A copy of the GNU Affero General Public License is reproduced
below.
If the terms and conditions of the AGPL v.3. would prevent you from
using TetGen, please consider the option to obtain a commercial
license for a fee. These licenses are offered by the Weierstrass
Institute for Applied Analysis and Stochastics (WIAS). As a rule,
licenses are provided "as-is", unlimited in time for a one time
fee. Please send corresponding requests to:
tetgen@wias-berlin.de. Please do not forget to include some
description of your company and the realm of its activities.
For details, see the
`TetGen web page `__
================================================
FILE: doc/geometry.rst
================================================
Geometry Generation
===================
.. automodule:: meshpy.geometry
================================================
FILE: doc/index.rst
================================================
Welcome to MeshPy's documentation!
==================================
.. toctree::
:maxdepth: 2
:hidden:
installation
tri-tet
geometry
faq
🚀 Github
💾 Download Releases
MeshPy offers quality triangular and tetrahedral mesh generation for Python.
Meshes of this type are chiefly used in finite-element simulation codes, but
also have many other applications ranging from computer graphics to robotics.
In order to generate these 2D and 3D meshes, MeshPy provides Python interfaces
to a few well-regarded mesh generators:
* `Triangle `__ by J. Shewchuk.
* `TetGen `__ by Hang Si.
Triangle and TetGen are included in the package in slightly modified versions.
An interface for `Gmsh `__ was also part of MeshPy, but is
now its own package `gmsh_interop `__.
MeshPy has its own `web page `_,
where you can find updated software, news, a forum, and documentation.
Show me! I need examples!
-------------------------
This file is included in the :mod:`meshpy` distribution as
:download:`examples/demo.py <../examples/demo.py>`.
.. literalinclude:: ../examples/demo.py
As a result of this, you will get::
Mesh Points:
0 [0.0, 0.0, 0.0]
1 [2.0, 0.0, 0.0]
2 [2.0, 2.0, 0.0]
3 [0.0, 2.0, 0.0]
4 [0.0, 0.0, 12.0]
5 [2.0, 0.0, 12.0]
6 [2.0, 2.0, 12.0]
7 [0.0, 2.0, 12.0]
8 [1.000116, 0.0, 0.0]
9 [0.0, 0.99960499999999997, 0.0]
10 [0.0, 0.99934199999999995, 12.0]
11 [1.0006170000000001, 0.0, 12.0]
...
Point numbers in tetrahedra:
0 [21, 39, 38, 52]
1 [9, 50, 2, 3]
2 [12, 45, 15, 54]
3 [39, 43, 20, 52]
4 [41, 45, 24, 54]
...
and a file :file:`test.vtk` that you can view with
`Paraview `__ or
`Visit `__.
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
================================================
FILE: doc/installation.rst
================================================
.. highlight:: sh
Installation
============
This tutorial will walk you through the process of building MeshPy. To follow,
you really only need three basic things:
* A UNIX-like machine with web access.
* A working `Python `__ installation.
* A recent C++ compiler. We use `pybind11 `__
to create the wrappers, so see their documentation for minimal required versions
if in doubt.
* `meson-python `__ and
`ninja `__, which are used to build the wrapper.
See the `[buildsystem]` section in `pyproject.toml` for an up to date list.
Step 1: Download and unpack MeshPy
----------------------------------
`Download MeshPy `_ and unpack it::
$ tar xfz MeshPy-VERSION.tar.gz
If you're downloading from ``git`` instead::
$ git clone https://github.com/inducer/meshpy.git
Step 2: Build MeshPy
--------------------
MeshPy uses `meson-python `__ as
its build system. For additional compilation options (e.g. compiling in debug mode),
see their official documentation.
First, just type::
$ cd MeshPy-VERSION # if you're not there already
If you want to just build a source distribution or a wheel for MeshPy, you can
run::
$ python -m build --sdist .
$ python -m build --wheel .
or with the trusty ``pip``::
$ python -m pip wheel --no-deps .
If you want to install MeshPy in editable mode for development, use::
$ python -m pip install --no-build-isolation --editable .
(the ``--no-build-isolation`` flag is very important!). At this point, you can
also pass additional configuration options to ``meson``. For example, to build
in debug mode, run::
$ python -m pip install \
--no-build-isolation -Csetup-args=-Dbuildtype=debug \
--editable .
Once that works, congratulations! You've successfully built MeshPy.
Step 3: Test MeshPy
-------------------
Just type::
$ python -m pytest -v -s test
================================================
FILE: doc/tri-tet.rst
================================================
Triangle/TetGen interface
=========================
.. module:: meshpy
Some common notions
-------------------
.. class:: ForeignArray
Note that :class:`ForeignArray` instances are not usually created by users,
and :class:`ForeignArray` is not a class name available in MeshPy. It is
just used to explain the interface provided.
Almost all input and output data in MeshPy can be accessed using the
:class:`ForeignArray` interface. It is called "foreign" because it
provides access to an area of memory accessible by a pointer managed by an
outside piece of software, such as a mesh generator.
Note that :class:`ForeignArray` has no *append* method. Instead, use
:meth:`ForeignArray.resize` and then set the consecutive entries of the
array.
.. method:: __len__()
Return the number of entries in the array. If the array is 2D (i.e. has
non-1 :attr:`unit`), :meth:`ForeignArray.__len__` only returns the
length of the leading dimension. For example, for an array of points
in *n*-dimensional space, :meth:`__len__` returns the number of points.
.. attribute:: unit
If this :class:`ForeignArray` represents a two-dimensional array, such
as an array of point coordinates, :meth:`ForeignArray.unit` gives the
size of the subordinate dimension.
For example, for an array of points in 3-dimensional space,
:meth:`ForeignArray.__len__` returns the number of dimensions (3).
.. attribute:: allocated
Return a :class:`bool` indicating whether storage has
been allocated for this array. This is only meaningful if the size
of this array is tied to that of another, see :meth:`ForeignArray.setup`.
.. method:: resize(new_size)
Change the length of the array as returned by :meth:`ForeignArray.__len__`.
.. method:: setup()
Set up (i.e. allocate) storage for the array. This only works on arrays
whose size is tied to that of other arrays, such as an array of point
markers, which necessarily has the same size as the associated array of
points, if it is allocated.
.. method:: deallocate()
Release any storage associated with the array.
.. method:: __getitem__(index)
__setitem__(index, value)
Get and set entries in the array. If this foreign array is 2D
(see :attr:`ForeignArray.unit`), index may be a 2-tuple of integers, as in::
points[2,1] = 17
:mod:`meshpy.triangle` -- Triangular Meshing
--------------------------------------------
.. module:: meshpy.triangle
:synopsis: Generate triangular meshes
.. moduleauthor:: Andreas Klöckner
.. class:: ForeignArray
See :class:`meshpy.ForeignArray` for shared documentation.
.. class:: MeshInfo
:class:`MeshInfo` objects are picklable.
.. attribute:: points
A 2D :class:`ForeignArray` of :class:`float` with dimension *(N,2)*,
providing a list of points that are referred to by index from other
entries of this structure.
.. attribute:: point_attributes
If :attr:`MeshInfo.number_of_point_attributes` is non-zero, this is a
:class:`ForeignArray` of :class:`float`\ s of point attributes.
This element's size is tied to that of :attr:`MeshInfo.points`.
.. attribute:: point_markers
:class:`ForeignArray` of :class:`float`\ s of point attributes.
This element's size is tied to that of :attr:`MeshInfo.points`.
.. attribute:: elements
.. attribute:: element_attributes
This element's size is tied to that of :attr:`MeshInfo.elements`.
.. attribute:: element_volumes
This element's size is tied to that of :attr:`MeshInfo.elements`.
.. attribute:: neighbors
.. attribute:: facets
.. attribute:: facet_markers
.. attribute:: holes
.. attribute:: regions
.. attribute:: faces
.. attribute:: face_markers
.. attribute:: normals
.. attribute:: number_of_point_attributes
.. attribute:: number_of_element_vertices
Defautls to 4 for linear tetrahedra. Change to 10 for second-order
tetrahedra.
.. attribute:: number_of_element_attributes
Convenient setters:
.. method:: set_points(points, point_markers=None)
.. method:: set_holes(points, hole_starts)
.. method:: set_facets(facets, facet_markers=None)
Other functionality:
.. method:: copy()
Return a duplicate copy of this object.
.. function:: subdivide_facets(subdivisions, points, facets, facet_markers)
Subdivide facets into *subdivisions* subfacets.
This routine is useful if you have to prohibit the insertion of Steiner
points on the boundary of your triangulation to allow the mesh to conform
either to itself periodically or another given mesh. In this case, you may
use this routine to create the necessary resolution along the boundary
in a predefined way.
*subdivisions* is either an :class:`int`, indicating a uniform number of
subdivisions throughout, or a list of the same length as *facets*,
specifying a subdivision count for each individual facet.
*points*
a list of points referred to from the facets list.
*facets*
a list of old facets, in the form *[(p1, p2), (p3,p4), ...]*.
*facet_markers*
either *None* or a list of facet markers of the same length
as *facets*.
Returns a tuple *(new_points, new_facets)*,
or *(new_points, new_facets, new_facet_markers)* if *facet_markers* is not
*None*.
.. function:: build(mesh_info, verbose=False, refinement_func=None, attributes=False, volume_constraints=True, max_volume=None, allow_boundary_steiner=True, allow_volume_steiner=True, quality_meshing=True, generate_edges=None, generate_faces=False, min_angle=None)
.. function:: refine(input_p, verbose=False, refinement_func=None, quality_meshing=True, min_angle=None)
.. function:: write_gnuplot_mesh(filename, out_p, facets=False)
:mod:`meshpy.tet` -- Tetrahedral Meshing
----------------------------------------
.. module:: meshpy.tet
:synopsis: Generate triangular meshes
.. moduleauthor:: Andreas Klöckner
.. class:: ForeignArray
See :class:`meshpy.ForeignArray` for shared documentation.
.. class:: Options(switches='pq', **kwargs)
Run time switches for TetGen. See the TetGen documentation for the meaning of each
switch.
Using the *kwargs* constructor argument, all the attributes defined
below can be set. This setting will occur after
:meth:`Options.parse_switches` is called with the *switches* parameter.
.. attribute:: plc
.. attribute:: quality
.. attribute:: refine
.. attribute:: coarse
.. attribute:: metric
.. attribute:: varvolume
.. attribute:: fixedvolume
.. attribute:: insertaddpoints
.. attribute:: regionattrib
.. attribute:: conformdel
.. attribute:: diagnose
.. attribute:: zeroindex
.. attribute:: optlevel
.. attribute:: optpasses
.. attribute:: order
.. attribute:: facesout
.. attribute:: edgesout
.. attribute:: neighout
.. attribute:: voroout
.. attribute:: meditview
.. attribute:: gidview
.. attribute:: geomview
.. attribute:: nobound
.. attribute:: nonodewritten
.. attribute:: noelewritten
.. attribute:: nofacewritten
.. attribute:: noiterationnum
.. attribute:: nomerge
.. attribute:: nobisect
.. attribute:: noflip
.. attribute:: nojettison
.. attribute:: steiner
.. attribute:: fliprepair
.. attribute:: docheck
.. attribute:: quiet
.. attribute:: verbose
.. attribute:: useshelles
.. attribute:: minratio
.. attribute:: goodratio
.. attribute:: minangle
.. attribute:: goodangle
.. attribute:: maxvolume
.. attribute:: maxdihedral
.. attribute:: alpha1
.. attribute:: alpha2
.. attribute:: alpha3
.. attribute:: epsilon
.. attribute:: epsilon2
.. method:: parse_switches(switches)
.. class:: Polygon
.. attribute:: vertices
.. class:: Facet
.. attribute:: polygons
.. attribute:: holes
.. class:: PBCGroup
.. attribute:: facet_marker_1
.. attribute:: facet_marker_2
.. attribute:: point_pairs
.. attribute:: matrix
.. class:: MeshInfo
.. attribute:: points
.. attribute:: point_attributes
.. attribute:: point_metric_tensors
.. attribute:: point_markers
.. attribute:: elements
.. attribute:: element_attributes
.. attribute:: element_volumes
.. attribute:: neighbors
.. attribute:: facets
.. attribute:: facet_markers
.. attribute:: holes
.. attribute:: regions
.. attribute:: facet_constraints
.. attribute:: segment_constraints
.. attribute:: pbc_groups
.. attribute:: faces
.. attribute:: adjacent_elements
.. attribute:: face_markers
.. attribute:: edges
.. attribute:: edge_markers
.. attribute:: edge_adjacent_elements
.. versionadded:: 2016.1
.. attribute:: number_of_point_attributes
.. attribute:: number_of_element_attributes
Convenient setters:
.. method:: set_points(points, point_markers=None)
.. method:: set_holes(points, hole_starts)
.. method:: set_facets(facets, markers=None)
Set a list of simple, single-polygon factes. Unlike
:meth:`MeshInfo.set_facets_ex`, this method does not allow holes and
only lets you use a single polygon per facet.
*facets*
a list of facets, where each facet is a single
polygons, represented by a list of point indices.
*markers*
Either None or a list of integers of the same
length as facets. Each integer is the facet marker assigned
to its corresponding facet.
.. note::
When the above says "list", any repeatable iterable
also accepted instead.
.. method:: set_facets_ex(facets, facet_holestarts=None, markers=None)
Set a list of complicated facets. Unlike :meth:`MeshInfo.set_facets`,
this method allows holes and multiple polygons per facet.
*facets*
a list of facets, where each facet is a list
of polygons, and each polygon is represented by a list
of point indices.
*facet_holestarts*
Either None or a list of hole starting points
for each facet. Each facet may have several hole starting points.
The mesh generator starts "eating" a hole into the facet at each
starting point and continues until it hits a polygon specified
in this facet's record in *facets*.
*markers*
Either None or a list of integers of the same
length as *facets*. Each integer is the facet marker assigned
to its corresponding facet.
.. note::
When the above says "list", any repeatable iterable
also accepted instead.
Other functionality:
.. attribute:: face_vertex_indices_to_face_marker
.. method:: dump()
.. method:: write_vtk(filename)
TetGen-provided loading and saving:
.. method:: save_nodes(filename)
.. method:: save_elements(filename)
.. method:: save_faces(filename)
.. method:: save_edges(filename)
.. method:: save_neighbors(filename)
.. method:: save_poly(filename)
.. method:: load_node(filename)
.. method:: load_pbc(filename)
.. method:: load_var(filename)
.. method:: load_mtr(filename)
.. method:: load_poly(filename)
.. method:: load_ply(filename)
.. method:: load_stl(filename)
.. method:: load_medit(filename)
.. method:: load_plc(filename)
.. method:: load_tetmesh(filename)
.. function:: build(mesh_info, options=Options("pq"), verbose=False, attributes=False, volume_constraints=False, max_volume=None, diagnose=False, insert_points=None, mesh_order=None)
:param insert_points: a :class:`MeshInfo` object specifying additional points to be inserted
================================================
FILE: doc/upload-docs.sh
================================================
#! /bin/sh
rsync --verbose --archive --delete .build/html/ doc-upload:doc/meshpy
================================================
FILE: examples/MESH_README.txt
================================================
-------------------------------------------------------------------------------
Mesh licensing
-------------------------------------------------------------------------------
This file summarizes the licenses surrounding data files in this directory.
When using them for research or demonstration purposes, please be mindful of
proper attribution.
-------------------------------------------------------------------------------
ka-6d.ply:
The mesh 'ka-6d.ply' is a derivative work of a mesh in FlightGear and is thus
licensed under the GNU GPL.
(C) Flightgear Developers & Contributors
(C) Andreas Kloeckner
-------------------------------------------------------------------------------
================================================
FILE: examples/airfoil3d.py
================================================
def main():
import numpy
from meshpy.geometry import GeometryBuilder, Marker, generate_extrusion, make_box
from meshpy.naca import get_naca_points
from meshpy.tet import MeshInfo, build
geob = GeometryBuilder()
box_marker = Marker.FIRST_USER_MARKER
wing_length = 2
wing_subdiv = 5
rz_points = (
[
(0, -wing_length * 1.05),
(0.7, -wing_length * 1.05),
]
+ [
(r, x)
for x, r in zip(
numpy.linspace(-wing_length, 0, wing_subdiv, endpoint=False),
numpy.linspace(0.8, 1, wing_subdiv, endpoint=False),
strict=True
)
]
+ [(1, 0)]
+ [
(r, x)
for x, r in zip(
numpy.linspace(wing_length, 0, wing_subdiv, endpoint=False),
numpy.linspace(0.8, 1, wing_subdiv, endpoint=False),
strict=True
)
][::-1]
+ [(0.7, wing_length * 1.05), (0, wing_length * 1.05)]
)
geob.add_geometry(
*generate_extrusion(
rz_points=rz_points,
base_shape=get_naca_points("0012", verbose=False, number_of_points=20),
ring_markers=(wing_subdiv * 2 + 4) * [box_marker],
)
)
from meshpy.tools import make_swizzle_matrix
swizzle_matrix = make_swizzle_matrix("z:x,y:y,x:z")
geob.apply_transform(lambda p: numpy.dot(swizzle_matrix, p))
def deform_wing(p):
x, y, z = p
return numpy.array(
[
x,
y + 0.1 * abs(x / wing_length) ** 2,
z + 0.8 * abs(x / wing_length) ** 1.2,
]
)
geob.apply_transform(deform_wing)
points, facets, _, facet_markers = make_box(
numpy.array([-wing_length - 1, -1, -1.5]),
numpy.array([wing_length + 1, 1, 3])
)
geob.add_geometry(points, facets, facet_markers=facet_markers)
mesh_info = MeshInfo()
geob.set(mesh_info)
mesh_info.set_holes([(0, 0, 0.5)])
mesh = build(mesh_info)
print(f"{len(mesh.elements)} elements")
mesh.write_vtk("airfoil3d.vtk")
if __name__ == "__main__":
main()
================================================
FILE: examples/box-in-box.py
================================================
def main():
import numpy
from meshpy.geometry import GeometryBuilder, Marker, make_box
from meshpy.tet import MeshInfo, build
geob = GeometryBuilder()
box_marker = Marker.FIRST_USER_MARKER
extent_small = 0.3 * numpy.ones(3, dtype=numpy.float64)
points, facets, _, _ = make_box(-extent_small, extent_small)
geob.add_geometry(points, facets, facet_markers=box_marker)
points, facets, _, facet_markers = make_box(
numpy.array([-1, -1, -1]), numpy.array([1, 1, 5])
)
geob.add_geometry(points, facets, facet_markers=facet_markers)
mesh_info = MeshInfo()
geob.set(mesh_info)
# mesh_info.set_holes([(0, 0, 0)])
# region attributes
mesh_info.regions.resize(1)
mesh_info.regions[0] = [
# point in region
0, 0, 0,
# region number
1,
# max volume in region
0.001,
]
mesh = build(mesh_info, max_volume=0.06,
volume_constraints=True, attributes=True)
print(f"{len(mesh.elements)} elements")
mesh.write_vtk("box-in-box.vtk")
if __name__ == "__main__":
main()
================================================
FILE: examples/clean.sh
================================================
#! /bin/sh
rm -f *.{vtk,ele,poly,node,lua}
================================================
FILE: examples/demo.py
================================================
from meshpy.tet import MeshInfo, build
mesh_info = MeshInfo()
mesh_info.set_points(
[
(0, 0, 0),
(2, 0, 0),
(2, 2, 0),
(0, 2, 0),
(0, 0, 12),
(2, 0, 12),
(2, 2, 12),
(0, 2, 12),
]
)
mesh_info.set_facets(
[
[0, 1, 2, 3],
[4, 5, 6, 7],
[0, 4, 5, 1],
[1, 5, 6, 2],
[2, 6, 7, 3],
[3, 7, 4, 0],
]
)
mesh = build(mesh_info, max_volume=0.5)
print("Mesh Points:")
for i, p in enumerate(mesh.points):
print(i, p)
print("Point numbers in tetrahedra:")
for i, t in enumerate(mesh.elements):
print(i, t)
mesh.write_vtk("test.vtk")
================================================
FILE: examples/jw_mesh_examples.ipynb
================================================
{
"cells": [
{
"cell_type": "markdown",
"id": "82dbbafd-ed23-43fd-924b-1916d0ea7dca",
"metadata": {},
"source": [
"## Making Meshes with MeshPy ( using meshtools )\n",
"\n",
"Gallery with short code comments\n",
"\n",
"by\n",
"\n",
"Jürgen Weizenecker\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "458bfac7-3661-433f-93fc-64d283245089",
"metadata": {},
"outputs": [],
"source": [
"import jw_meshtools as mt\n",
"import matplotlib.pyplot as plt\n",
"import numpy as np\n",
"import numpy.linalg as la\n",
"\n",
"from meshpy import triangle\n",
"\n",
"\n",
"length = 0.3"
]
},
{
"cell_type": "markdown",
"id": "b89c83ea-18f5-4209-bba5-bf3a6e3d1a06",
"metadata": {},
"source": [
"### Figure 1"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "cf6de630-1bb1-4196-af4e-a070d464ddc0",
"metadata": {},
"outputs": [],
"source": [
"# Simple mesh rectangle\n",
"\n",
"# Define closed boundary around a 2D region\n",
"p, v = mt.RectangleSegments([-1.0, -1.0], [2.5, 3.0], edge_length=length)\n",
"# Make mesh of this region\n",
"\n",
"poi, tri, BouE, li_BE, bou_elem, CuE, li_CE = mt.DoTriMesh(p, v, edge_length=length)\n",
"\n",
"print(\"Points, \", poi[0:5], \"....\", flush=True)\n",
"print(\"Elements, \", tri[0:5], \"...\", flush=True)\n",
"print(\"Boundary edges\", BouE, flush=True)\n",
"print(\"List for boundary edges\", li_BE, flush=True)\n",
"print(\"Boundary triangles\", bou_elem, flush=True)\n",
"\n",
"# Help\n",
"print(\"\\n\\n################ Help string :\")\n",
"print(mt.RectangleSegments.__doc__)\n",
"print(mt.DoTriMesh.__doc__)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "32664d46-c5e3-48b7-ab03-bdc6d5d05fb2",
"metadata": {},
"outputs": [],
"source": [
"# Simple mesh rectangle with numbers\n",
"p, v = mt.RectangleSegments([-1.0, -1.0], [2.5, 3.0], edge_length=3 * length)\n",
"poi, tri, BouE, li_BE, bou_elem, CuE, li_CE = mt.DoTriMesh(\n",
" p, v, edge_length=3 * length, show=False\n",
")\n",
"\n",
"mt.PlotMeshNumbers(poi, tri)"
]
},
{
"cell_type": "markdown",
"id": "ceda42b2-ae3e-4e76-a085-c743d4f0679f",
"metadata": {},
"source": [
"### Figure 2"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3c1294da-6150-4718-9d5f-6da18e40d777",
"metadata": {},
"outputs": [],
"source": [
"# construct boundary curve from simple lines\n",
"p1, v1 = mt.LineSegments([-0.5, 0.5], [-1, -1], edge_length=length / 5)\n",
"p2, v2 = mt.LineSegments([-1, -1], [0.0, 0.5], edge_length=length / 5)\n",
"p3, v3 = mt.LineSegments([0.0, 0.5], [1, 1], edge_length=length / 7)\n",
"p4, v4 = mt.LineSegments([1, 1], [-0.5, 0.5], edge_length=length / 7)\n",
"p, v = mt.AddMultipleSegments(p1, p2, p3, p4)\n",
"\n",
"poi, tri, BouE, li_BE, bou_elem, CuE, li_CE = mt.DoTriMesh(p, v, edge_length=length)"
]
},
{
"cell_type": "markdown",
"id": "9a442dae-a4b5-4234-8148-6829b23edd74",
"metadata": {},
"source": [
"### Figure 3"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "97c236dd-3cf2-4fc0-802f-9f0c6689f97d",
"metadata": {},
"outputs": [],
"source": [
"# circle as boundary curve\n",
"p, v = mt.CircleSegments([1, 2], 2, edge_length=length)\n",
"mt.DoTriMesh(p, v, edge_length=length)\n",
"print(mt.CircleSegments.__doc__)"
]
},
{
"cell_type": "markdown",
"id": "41f16cdb-d126-4e0e-b9e9-3a23eca164f9",
"metadata": {},
"source": [
"### Figure 4"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1780dc04-6ddf-47d3-bfdf-9add14c1b60d",
"metadata": {},
"outputs": [],
"source": [
"p1, v1 = mt.LineSegments([2, 2], [-1, -3], edge_length=length)\n",
"p2, v2 = mt.LineSegments([-1, -4], [3, -1], num_points=10)\n",
"p, v = mt.AddSegments(p1, p2, closed=True)\n",
"mt.DoTriMesh(p, v, edge_length=length);"
]
},
{
"cell_type": "markdown",
"id": "3303ac64-5d98-4d75-a9b5-81b2b348e2b2",
"metadata": {},
"source": [
"### Figure 5"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2dc39822-6da2-4c2d-8e76-be91e097da8f",
"metadata": {},
"outputs": [],
"source": [
"#\n",
"# rectangle with smooth corners\n",
"#\n",
"p, v = mt.ORecSegments([1, 2], [7, 6], 0.3, edge_length=length, num_pc=10)\n",
"mt.DoTriMesh(p, v, edge_length=length);"
]
},
{
"cell_type": "markdown",
"id": "48319034-6631-449f-9189-97826a74df87",
"metadata": {},
"source": [
"### Figure 6"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "265a94cf-c9cc-45ad-bf9b-4ca02edc5935",
"metadata": {},
"outputs": [],
"source": [
"#\n",
"# two semicircles\n",
"#\n",
"p1, v1 = mt.CircleSegments(\n",
" [1.0, 0], 1, a_min=-np.pi / 2, a_max=np.pi / 2, num_points=20\n",
")\n",
"p2, v2 = mt.CircleSegments(\n",
" [1, 0], 3, a_min=np.pi / 2.0, a_max=3.0 * np.pi / 2, num_points=20\n",
")\n",
"p, v = mt.AddSegments(p1, p2, closed=True)\n",
"# plot mesh\n",
"mt.DoTriMesh(p, v, edge_length=length);"
]
},
{
"cell_type": "markdown",
"id": "7c46029b-50cb-4b00-9785-e475883dbc44",
"metadata": {},
"source": [
"### Figure 7"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1c0403d3-bade-49a8-85fe-a5170bfdacbe",
"metadata": {},
"outputs": [],
"source": [
"#\n",
"# boundary curve defined by simple points\n",
"#\n",
"t = np.linspace(0, 2 * np.pi, 120)\n",
"r = 3 + np.sin(8 * t)\n",
"x = r * np.cos(t)\n",
"y = r * np.sin(t)\n",
"p = [(x[j], y[j]) for j in range(len(t))]\n",
"p1, v1 = mt.PointSegments(p)\n",
"mt.DoTriMesh(p1, v1, edge_length=length);"
]
},
{
"cell_type": "markdown",
"id": "d4185d69-a93b-4b2d-8dee-960487adfd59",
"metadata": {},
"source": [
"### Figure 8, without meshtools"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "aa7295b4-291c-45d8-a4b5-e6bff05773a1",
"metadata": {},
"outputs": [],
"source": [
"#\n",
"# Example for using directly triangle\n",
"#\n",
"\n",
"\n",
"def round_trip_connect(start, end):\n",
" return [(i, i + 1) for i in range(start, end)] + [(end, start)]\n",
"\n",
"\n",
"points = [(1, 0), (1, 1), (-1, 1), (-1, -1), (1, -1), (1, 0)]\n",
"facets = round_trip_connect(0, len(points) - 1)\n",
"\n",
"circ_start = len(points)\n",
"points.extend(\n",
" (3 * np.cos(angle), 3 * np.sin(angle))\n",
" for angle in np.linspace(0, 2 * np.pi, 29, endpoint=False)\n",
")\n",
"\n",
"facets.extend(round_trip_connect(circ_start, len(points) - 1))\n",
"\n",
"\n",
"def needs_refinement(vertices, area):\n",
" bary = np.sum(np.array(vertices), axis=0) / 3\n",
" max_area = 0.01 + abs(la.norm(bary, np.inf) - 1) * 0.1\n",
" return bool(area > max_area)\n",
"\n",
"\n",
"info = triangle.MeshInfo()\n",
"info.set_points(points)\n",
"info.set_holes([(0, 0)])\n",
"info.set_facets(facets)\n",
"\n",
"mesh = triangle.build(info, refinement_func=needs_refinement)\n",
"# mesh = triangle.build(info)\n",
"\n",
"mesh_points = np.array(mesh.points)\n",
"mesh_tris = np.array(mesh.elements)\n",
"\n",
"print(mesh_points[0:5], \"...\")\n",
"print(mesh_tris[0:5], \"....\")\n",
"plt.triplot(mesh_points[:, 0], mesh_points[:, 1], mesh_tris)\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"id": "84f14d0f-9610-42b0-b0df-b5d01025e8b7",
"metadata": {},
"source": [
"### Figure 9, Add inner curves"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f32c17c2-21d1-4e92-9624-62c3da778b4d",
"metadata": {},
"outputs": [],
"source": [
"#\n",
"# rectangle and inner circle\n",
"#\n",
"p1, v1 = mt.RectangleSegments([-2, -2], [2.5, 3], edge_length=length)\n",
"\n",
"p2, v2 = mt.CircleSegments([1, 1], 1, edge_length=length / 5)\n",
"p, v = mt.AddCurves(p1, v1, p2, v2)\n",
"poi, tri, BouE, li_BE, bou_elem, CuE, li_CE = mt.DoTriMesh(p, v, edge_length=length)\n",
"print(\"Points, \", poi[0:5], \"...\")\n",
"print(\"Elements, \", tri[0:5], \"...\")\n",
"print(\"Boundary Edges\", BouE[0:5], \"...\")\n",
"print(\"List boundary edges\", li_BE)\n",
"print(\"Inner Curves\", CuE[0:5], \"...\")\n",
"print(\"List inner Curve\", li_CE)"
]
},
{
"cell_type": "markdown",
"id": "4205b7df-dea3-418d-8725-bd7576b3e10a",
"metadata": {},
"source": [
"### Figure 10"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b6a82bae-0ef6-4896-b5cb-782bcb3835cf",
"metadata": {},
"outputs": [],
"source": [
"#\n",
"# rectangle and inner line\n",
"#\n",
"p1, v1 = mt.RectangleSegments([-2, -2], [2.5, 3], edge_length=length)\n",
"p2, v2 = mt.LineSegments([0, 0], [1, 1], edge_length=length / 5)\n",
"\n",
"\n",
"p3, v3 = mt.LineSegments([-1, 1], [0, -1], edge_length=length / 5)\n",
"p4, v4 = mt.LineSegments([0, -1], [1, -1], edge_length=length / 5)\n",
"# connect line 3 and 4 first\n",
"p5, v5 = mt.AddSegments(p3, p4)\n",
"\n",
"p, v, indizes = mt.AddMultipleCurves(p1, v1, p2, v2, p5, v5)\n",
"\n",
"poi, tri, BouE, li_BE, bou_elem, CuE, li_CE = mt.DoTriMesh(p, v, edge_length=length)"
]
},
{
"cell_type": "markdown",
"id": "ff201c3a-f65f-41e4-a68d-7e3a04590738",
"metadata": {},
"source": [
"### Figure 11"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "21f930ce-c156-4f9b-8db5-4cb9a3572332",
"metadata": {},
"outputs": [],
"source": [
"#\n",
"# rectangle and more complicated inner curves\n",
"#\n",
"p1, v1 = mt.RectangleSegments([-2, -2], [2.5, 3], edge_length=length)\n",
"\n",
"p2, v2 = mt.CircleSegments([1, 1], 1, edge_length=length / 5)\n",
"p, v = mt.AddCurves(p1, v1, p2, v2)\n",
"\n",
"# use connect if segments might have nearly the same points\n",
"p3, v3 = mt.LineSegments([-1, -2], [-1, 3], edge_length=length / 4)\n",
"p, v = mt.AddCurves(p, v, p3, v3, connect=True, eps=1e-12)\n",
"\n",
"p4, v4 = mt.LineSegments([-1, 1], [0, 1], edge_length=length / 5)\n",
"p, v = mt.AddCurves(p, v, p4, v4, connect=True, eps=1e-12)\n",
"\n",
"# or shift inner curve slightly\n",
"\n",
"epsilon = 1e-6\n",
"p5, v5 = mt.LineSegments([1, -2 + epsilon], [3 - epsilon, -1], edge_length=length / 5)\n",
"p, v = mt.AddCurves(p, v, p5, v5)\n",
"\n",
"poi, tri, BouE, li_BE, bou_elem, CuE, li_CE = mt.DoTriMesh(p, v, edge_length=length)"
]
},
{
"cell_type": "markdown",
"id": "90d432d9-1829-41f1-a5eb-ecd23a1fde1b",
"metadata": {},
"source": [
"### Figure 12, Holes"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5a35bb11-2ad6-4557-98b9-2446cc7fe355",
"metadata": {},
"outputs": [],
"source": [
"#\n",
"# rectangle with holes\n",
"p1, v1 = mt.LineSegments([-2, -3], [2, -3], num_points=12)\n",
"p2, v2 = mt.LineSegments([2, 3], [-2, 3], num_points=12)\n",
"p, v = mt.AddSegments(p1, p2, closed=True)\n",
"\n",
"# define the boundary curves of holes\n",
"p3, v3 = mt.CircleSegments([-0.5, 0.5], 0.5, edge_length=length)\n",
"p, v = mt.AddCurves(p, v, p3, v3)\n",
"p4, v4 = mt.CircleSegments([1, -1], 0.5, edge_length=length)\n",
"p, v = mt.AddCurves(p, v, p4, v4)\n",
"\n",
"# the array holes contain points in the regions to be removed\n",
"poi, tri, BouE, li_BE, bou_elem, CuE, li_CE = mt.DoTriMesh(\n",
" p, v, edge_length=length, holes=[(-0.4, 0.4), (0.95, -0.8)]\n",
")"
]
},
{
"cell_type": "markdown",
"id": "6af9f075-6b4d-4f26-982d-ccdb45c9a404",
"metadata": {},
"source": [
"### Figure 13, Find closest nodes"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "80d6d1d8-3aa9-4676-88ef-18011bfcc391",
"metadata": {},
"outputs": [],
"source": [
"# boundary nodes\n",
"# rectangle with holes\n",
"p, v = mt.RectangleSegments([-2, -3], [2, 3], edge_length=length)\n",
"\n",
"p3, v3 = mt.CircleSegments([-0.5, 0.5], 0.5, edge_length=length)\n",
"p4, v4 = mt.CircleSegments([1, -1], 0.5, edge_length=length)\n",
"p, v, ii = mt.AddMultipleCurves(p, v, p3, v3, p4, v4)\n",
"\n",
"poi, tri, BouE, li_BE, bou_elem, CuE, li_CE = mt.DoTriMesh(\n",
" p, v, edge_length=length, holes=[(-0.4, 0.4), (0.95, -0.8)], show=False\n",
")\n",
"\n",
"# node numbers used for search\n",
"all_nodes = np.arange(len(poi))\n",
"# points to be found\n",
"p0 = [[2 * np.cos(t), 2 * np.sin(t)] for t in np.linspace(0, 2 * np.pi, 15)]\n",
"\n",
"# search\n",
"nn, dd = mt.FindClosestNode(all_nodes, poi, p0)\n",
"\n",
"\n",
"print(\"Points given\\n\", p0)\n",
"print(\"Node number\\n\", nn)\n",
"print(\"Distance from p0\\n\", dd)\n",
"\n",
"plt.triplot(poi[:, 0], poi[:, 1], tri)\n",
"plt.plot(poi[nn, 0], poi[nn, 1], \"or\");"
]
},
{
"cell_type": "markdown",
"id": "943592f6-4f12-4bf3-b53d-26ed1950b6a1",
"metadata": {},
"source": [
"### Figure 14 , Find Boundary"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "87148906-ef5e-4750-b045-db968b96d096",
"metadata": {},
"outputs": [],
"source": [
"# take mesh from above\n",
"\n",
"# Find boundary nodes/segments between the points below\n",
"# Two types of boundaries, Nodes or segments\n",
"# Ps:\n",
"Ps = [[-2, 3], [2, -3], [-2, 3], [-0.5, 0.5], [-0.5, 0.5], [1, -1], [1, -1]]\n",
"Ps_types = [\"Nodes\", \"Segments\", \"Nodes\", \"Segments\"]\n",
"\n",
"bseg = mt.RetrieveSegments(poi, BouE, li_BE, Ps, Ps_types)\n",
"for i in range(len(Ps_types)):\n",
" print(\"bseg[\", i, \"] : \", Ps_types[i], \"\\n\", bseg[i])\n",
"\n",
"# !!!!!!!!!!!!!!!!!!!!!!!!!\n",
"# bseg[0] contains all nodes (Ps_types[0]) between Ps[0] and Ps[1]\n",
"# bseg[1] contains all segments (Ps_types[1]) between Ps[1] and Ps[2]\n",
"# No connection between Ps[2] and Ps[3] , skip\n",
"# bseg[2] contains all nodes (Ps_types[2]) between Ps[3] and Ps[4]\n",
"# bseg[3] contains all nodes (Ps_types[3]) between Ps[4] and Ps[5]\n",
"\n",
"\n",
"plt.triplot(poi[:, 0], poi[:, 1], tri)\n",
"\n",
"for i in range(len(Ps_types)):\n",
" mt.PlotBoundary(poi, bseg[i], Ps_types[i])\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"id": "79bbafb9-e201-4913-8b32-9628f2424435",
"metadata": {},
"source": [
"### Figure 15 , Find Inner Curves"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c99a3ba9-4f77-4a10-ac82-e2dd8932360c",
"metadata": {},
"outputs": [],
"source": [
"#\n",
"# rectangle with holes\n",
"p, v = mt.RectangleSegments([-2.0, -3.0], [2, 3.0], edge_length=length)\n",
"p3, v3 = mt.CircleSegments([-0.5, 0.5], 0.5, edge_length=length / 4)\n",
"p, v = mt.AddCurves(p, v, p3, v3)\n",
"p4, v4 = mt.CircleSegments([1, -1], 0.5, edge_length=length / 4)\n",
"p, v = mt.AddCurves(p, v, p4, v4)\n",
"poi, tri, BouE, li_BE, bou_elem, CuE, li_CE = mt.DoTriMesh(\n",
" p, v, edge_length=length, show=False\n",
")\n",
"\n",
"# same as before, with CuE and li_DE\n",
"Ps = [[-0.5, 0.5], [-0.5, 0.5], [1, -1], [1, -1]]\n",
"Ps_types = [\"Nodes\", \"Segments\"]\n",
"\n",
"cseg = mt.RetrieveSegments(poi, CuE, li_CE, Ps, Ps_types)\n",
"print(\"cseg\", cseg)\n",
"\n",
"plt.triplot(poi[:, 0], poi[:, 1], tri)\n",
"\n",
"mt.PlotBoundary(poi, cseg[0], \"Nodes\")\n",
"mt.PlotBoundary(poi, cseg[1], \"Segments\")\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"id": "228ca89f-1724-4748-9699-d2b6d8279b04",
"metadata": {},
"source": [
"### Figure 16"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4b682b06-fa37-4cee-934f-234e2dd51f70",
"metadata": {},
"outputs": [],
"source": [
"#\n",
"# rectangle and inner line\n",
"#\n",
"p1, v1 = mt.RectangleSegments([-2, -2], [2.5, 3], edge_length=length)\n",
"\n",
"p2, v2 = mt.CircleSegments([1, 1], 1, edge_length=length / 5)\n",
"p, v = mt.AddCurves(p1, v1, p2, v2)\n",
"\n",
"# use connect if segments might have nearly the same points\n",
"p3, v3 = mt.LineSegments([-1, -2], [-1, 3], edge_length=length / 4)\n",
"p, v = mt.AddCurves(p, v, p3, v3, connect=True, eps=1e-12)\n",
"\n",
"p4, v4 = mt.LineSegments([-1, 1], [0, 1], edge_length=length / 5)\n",
"p, v = mt.AddCurves(p, v, p4, v4, connect=True, eps=1e-12)\n",
"\n",
"# or shift inner curve slightly away from existing points/curves\n",
"epsilon = 1e-6\n",
"p5, v5 = mt.LineSegments([1, -2 + epsilon], [3 - epsilon, -1], edge_length=length / 5)\n",
"p, v = mt.AddCurves(p, v, p5, v5)\n",
"\n",
"poi, tri, BouE, li_BE, bou_elem, CuE, li_CE = mt.DoTriMesh(p, v, edge_length=length)\n",
"\n",
"# plot all boundaries and inner curves\n",
"mt.PlotBoundary(poi, BouE, \"Segments\")\n",
"mt.PlotBoundary(poi, CuE, \"Curves\")"
]
},
{
"cell_type": "markdown",
"id": "e74dc712-957f-4b42-af13-ec9dc2029ead",
"metadata": {},
"source": [
"### Figure 17, Refinement"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "92680253-2c90-47d0-bc74-25d9df2b247a",
"metadata": {},
"outputs": [],
"source": [
"#\n",
"# rectangle and local refinement\n",
"#\n",
"p1, v1 = mt.RectangleSegments([0, 0], [1, 1], num_points=100)\n",
"p2, v2 = mt.RectangleSegments([0.05, 0.05], [0.95, 0.95], num_points=40)\n",
"p, v = mt.AddCurves(p1, v1, p2, v2)\n",
"p3, v3 = mt.RectangleSegments([0.1, 0.1], [0.9, 0.9], num_points=20)\n",
"p, v = mt.AddCurves(p, v, p3, v3)\n",
"mt.DoTriMesh(p, v, edge_length=length);"
]
},
{
"cell_type": "markdown",
"id": "2230ff02-9618-4732-b520-b745ba69c5b0",
"metadata": {},
"source": [
"### Figure 18"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b7d7df4d-aa92-4e26-b9d5-6bd43859fb04",
"metadata": {},
"outputs": [],
"source": [
"#\n",
"# 2D curve with local mesh refinement I\n",
"#\n",
"#\n",
"t = np.linspace(0, 2 * np.pi, 120)\n",
"r = 3 + np.sin(8 * t)\n",
"x = r * np.cos(t)\n",
"y = r * np.sin(t)\n",
"p = [(x[j], y[j]) for j in range(len(t))]\n",
"p1, v1 = mt.PointSegments(p)\n",
"# function for refinement\n",
"\n",
"\n",
"def myrefine1(tri_points, area):\n",
" center_tri = np.sum(np.array(tri_points), axis=0) / 3.0\n",
" x = center_tri[0]\n",
" _y = center_tri[1]\n",
" if x > 0:\n",
" max_area = 0.05 * (1 + 3 * x)\n",
" else:\n",
" max_area = 0.05\n",
" return bool(area > max_area)\n",
"\n",
"\n",
"mt.DoTriMesh(p1, v1, tri_refine=myrefine1);"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "20215917-888d-4137-aed3-53d11ab4b275",
"metadata": {},
"outputs": [],
"source": [
"# function for refinement\n",
"def myrefine2(tri_points, area):\n",
" center_tri = np.sum(np.array(tri_points), axis=0) / 3.0\n",
" r = np.sqrt(center_tri[0] ** 2 + center_tri[1] ** 2)\n",
" max_area = 0.3 + (0.01 - 0.3) / (1 + 0.5 * (r - 1) ** 2)\n",
" return bool(area > max_area)\n",
"\n",
"\n",
"mt.DoTriMesh(p1, v1, tri_refine=myrefine2);"
]
},
{
"cell_type": "markdown",
"id": "60c3e79a-4fa1-4742-b5c5-d6bcbad9fe12",
"metadata": {},
"source": [
"### Figure 19"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "419ae0ef-271d-4b2a-8952-ac34614d67a8",
"metadata": {},
"outputs": [],
"source": [
"#\n",
"# 2D curve with local refinement II\n",
"# !! 2 plots\n",
"#\n",
"# take p1 from above\n",
"p2, v2 = mt.CircleSegments([0, 0], 1, edge_length=0.05)\n",
"p, v = mt.AddCurves(p1, v1, p2, v2)\n",
"\n",
"\n",
"def myrefine3(tri_points, area):\n",
" center_tri = np.sum(np.array(tri_points), axis=0) / 3.0\n",
" r = np.sqrt(center_tri[0] ** 2 + center_tri[1] ** 2)\n",
" max_area = 0.4 + (0.01 - 0.3) / (1 + 0.5 * (r - 1) ** 2)\n",
" return bool(area > max_area)\n",
"\n",
"\n",
"poi, tri, BouE, li_BE, bou_elem, CuE, li_CE = mt.DoTriMesh(p, v, tri_refine=myrefine3)"
]
},
{
"cell_type": "markdown",
"id": "26fde227-9d44-4b40-b72f-e38d8ad7b532",
"metadata": {},
"source": [
"### Figure 20"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a9de5900-987b-4bae-9566-7fa395b040da",
"metadata": {},
"outputs": [],
"source": [
"from scipy.spatial import cKDTree\n",
"\n",
"\n",
"#\n",
"# 2D curve with local refinement III\n",
"#\n",
"#\n",
"# take p1 from above\n",
"nodes = range(len(p1))\n",
"# define tree to speed up node search\n",
"p1tree = cKDTree(np.array(p1))\n",
"\n",
"\n",
"# function for refinement\n",
"def myrefine3(tri_points, area):\n",
" center_tri = np.sum(np.array(tri_points), axis=0) / 3.0\n",
" p0 = [(center_tri[0], center_tri[1])]\n",
" _node, r = mt.FindClosestNode(nodes, [], p0, tree=p1tree)\n",
" r = r[0]\n",
" max_area = 0.3 + (0.01 - 0.3) / (1 + r**2)\n",
" return bool(area > max_area)\n",
"\n",
"\n",
"mt.DoTriMesh(p1, v1, tri_refine=myrefine3);"
]
},
{
"cell_type": "markdown",
"id": "fa0ff70f-1d6c-4e92-89a6-b5ef2c1374ba",
"metadata": {},
"source": [
"### Figure 21"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "da73da19-a9f8-409a-b57b-5ebed6791759",
"metadata": {},
"outputs": [],
"source": [
"# Simple mesh rectangle with second order points\n",
"p, v = mt.RectangleSegments([-1.0, -1.0], [2.5, 3.0], edge_length=length)\n",
"poi, tri, BouE, li_BE, bou_elem, CuE, li_CE = mt.DoTriMesh(\n",
" p, v, edge_length=length, order=2, show=None\n",
")\n",
"\n",
"plt.triplot(poi[:, 0], poi[:, 1], tri[:, 0:3])\n",
"maxi = np.max(tri[:, 0:3]) + 1\n",
"plt.plot(poi[maxi:, 0], poi[maxi:, 1], \"k*\")\n",
"mt.PlotBoundary(poi, np.array(BouE), \"Segments\")\n",
"plt.show()\n",
"print(\"points:\", poi[0:5], \"....\")\n",
"print(\"elements\", tri[0:5], \"....\")\n",
"print(\"boundary\", BouE)"
]
},
{
"cell_type": "markdown",
"id": "a582e22d-f57b-4252-b818-dfa8841cb60c",
"metadata": {},
"source": [
"### Figure 22, connect meshes"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "df38635e-d937-4aad-9dc3-111aaa1d043b",
"metadata": {},
"outputs": [],
"source": [
"# connect mesh\n",
"\n",
"# mesh A\n",
"p1, v1 = mt.LineSegments([0, 1], [0, 0], edge_length=length)\n",
"p2, v2 = mt.LineSegments([0, 0], [1, 0], edge_length=length)\n",
"p, v = mt.AddSegments(p1, p2)\n",
"p1, v1 = mt.CircleSegments([0, 0], 1, a_min=0, a_max=np.pi / 2, edge_length=length)\n",
"p, v = mt.AddSegments(p, p1)\n",
"pA, tA, bA, lA, bou_elemA, cuA, lcA = mt.DoTriMesh(p, v, edge_length=length)\n",
"\n",
"# mesh B\n",
"p1, v1 = mt.CircleSegments([0, 0], 1, a_min=0, a_max=np.pi / 2, edge_length=length)\n",
"p2, v2 = mt.LineSegments([0, 1], [2, 2], edge_length=length)\n",
"p, v = mt.AddSegments(p1, p2)\n",
"p1, v1 = mt.CircleSegments(\n",
" [0, 0], 2 * np.sqrt(2), a_min=np.pi / 4, a_max=0, edge_length=length\n",
")\n",
"p, v = mt.AddSegments(p, p1)\n",
"p1, v1 = mt.LineSegments([2 * np.sqrt(2), 0], [1, 0], edge_length=length)\n",
"p, v = mt.AddSegments(p, p1)\n",
"pB, tB, bB, lB, bou_elemB, cuB, lcB = mt.DoTriMesh(p, v, edge_length=length)\n",
"\n",
"# connect\n",
"p, t, b, bl, idn = mt.ConnectMesh(pA, tA, bA, pB, tB, bB, epsilon=1e-8)\n",
"plt.triplot(p[:, 0], p[:, 1], t[:, 0:3])\n",
"k = [x[0] for x in idn]\n",
"plt.plot(p[k, 0], p[k, 1], \"ro\", mfc=\"none\")\n",
"\n",
"mt.PlotBoundary(p, np.array(b), \"Segments\")\n",
"plt.show()\n",
"\n",
"Ps = [[1, 0], [1, 0]]\n",
"bseg = mt.RetrieveSegments(p, b, bl, Ps, [\"Nodes\"])\n",
"mt.PlotBoundary(p, bseg[0], \"Nodes\")\n",
"\n",
"plt.show()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.14.3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
================================================
FILE: examples/jw_meshtools.py
================================================
"""Toolbox for generating a mesh."""
from collections import Counter
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
from scipy.spatial import cKDTree
from meshpy import triangle
def RefineMeshElements(poi, tri, uu, n=1):
t_new = []
if n == 1:
for t in tri:
t_new += [[t[0], t[3], t[5]]]
t_new += [[t[3], t[1], t[4]]]
t_new += [[t[3], t[4], t[5]]]
t_new += [[t[4], t[2], t[5]]]
return poi, np.array(t_new), uu
else:
p_new = list(poi)
u_new = list(uu)
NN = len(poi)
for t in tri:
n1 = t[0]
n2 = t[1]
n3 = t[2]
n4 = t[3]
n5 = t[4]
n6 = t[5]
p1 = poi[n1]
p2 = poi[n2]
p3 = poi[n3]
p4 = poi[n4]
p5 = poi[n5]
p6 = poi[n6]
# define new points
q1 = (p1 + p4 + p6) / 3.0
q2 = (p4 + p2 + p5) / 3.0
q3 = (p3 + p6 + p5) / 3.0
q4 = (p4 + p5 + p6) / 3.0
# compute values at new points
b1 = poi[n2, 1] - poi[n3, 1]
b2 = poi[n3, 1] - poi[n1, 1]
b3 = poi[n1, 1] - poi[n2, 1]
c1 = poi[n3, 0] - poi[n2, 0]
c2 = poi[n1, 0] - poi[n3, 0]
c3 = poi[n2, 0] - poi[n1, 0]
a1 = poi[n2, 0] * poi[n3, 1] - poi[n3, 0] * poi[n2, 1]
a2 = poi[n3, 0] * poi[n1, 1] - poi[n1, 0] * poi[n3, 1]
a3 = poi[n1, 0] * poi[n2, 1] - poi[n2, 0] * poi[n1, 1]
twodelta = b1 * c2 - c1 * b2
PHI = uu[t]
phi_inner = []
for Q in [q1, q2, q3, q4]:
N1 = 1.0 / twodelta * (a1 + b1 * Q[0] + c1 * Q[1])
N2 = 1.0 / twodelta * (a2 + b2 * Q[0] + c2 * Q[1])
N3 = 1.0 / twodelta * (a3 + b3 * Q[0] + c3 * Q[1])
Basefunc = [
(2 * N1 - 1) * N1,
(2 * N2 - 1) * N2,
(2 * N3 - 1) * N3,
4 * N1 * N2,
4 * N2 * N3,
4 * N1 * N3,
]
phi_inner += [sum(Basefunc * PHI)]
p_new += [q1, q2, q3, q4]
u_new += phi_inner
t_new += [[n1, NN, n6], [n1, n4, NN], [n4, n6, NN]]
t_new += [[n4, NN + 1, n5], [n4, n2, NN + 1], [n2, n5, NN + 1]]
t_new += [[n5, NN + 2, n6], [n5, n3, NN + 2], [n3, n6, NN + 2]]
t_new += [[n5, NN + 3, n4], [n5, n6, NN + 3], [n6, n4, NN + 3]]
NN += 4
print(t_new)
p_new = np.array(p_new)
t_new = np.array(t_new)
u_new = np.array(u_new)
return p_new, t_new, u_new
def PlotMeshNumbers(p, t, edges=None, pltshow=True):
"""
PlotMeshNumbers(p,t,edges=[],pltshow=True)
---------------
p : points of the triangular mesh
t : elements of the mesh (first order 3 numbers, second order 6 numbers)
"""
if edges is None:
edges = []
plt.triplot(p[:, 0], p[:, 1], t[:, 0:3])
# annotate nodes
for i in range(len(p)):
buf = f"{i}"
x = p[i, 0]
y = p[i, 1]
plt.plot(x, y, "ok", markersize=5)
plt.text(x, y, buf, color="r", fontsize=13)
# annotate elements
for n in range(len(t)):
buf = f"{n}"
ps = (p[t[n, 0], :] + p[t[n, 1], :] + p[t[n, 2], :]) / 3.0
plt.text(ps[0], ps[1], buf, color="b", fontsize=9)
if edges != []:
for n in range(len(edges)):
buf = f"{n}"
ps = (p[edges[n][0], :] + p[edges[n][1], :]) / 2.0
plt.text(ps[0], ps[1], buf, color="g", fontsize=11)
if pltshow:
plt.show()
# only for a small number of triangles fast enough
def PlotSurfaceMesh(p, t, color="b"):
ae, _b, _be = FindEdges(t)
fig = plt.figure()
ax = Axes3D(fig)
for x in ae:
P1 = p[x[0]]
P2 = p[x[1]]
X = np.array([P1[0], P2[0]])
Y = np.array([P1[1], P2[1]])
Z = np.array([P1[2], P2[2]])
ax.plot(X, Y, Z, color)
plt.show()
# btype can be 'Nodes' or 'Segments'
#
def PlotBoundary(p, boundary, btype):
"""
PlotBoundary(p,boundary,btype)
------------------------------
plot a segment curve, a node curve, or a line graph
boundary : list of edges or nodes to be plotted
btype : string of curve type ('Segments', 'Nodes', 'Curve')
"""
bound = np.array(boundary)
if btype == "Nodes":
X = p[bound, 0]
Y = p[bound, 1]
plt.plot(X, Y, "bo")
elif btype == "Lines":
X = p[bound, 0]
Y = p[bound, 1]
plt.plot(X, Y, "-og")
else:
for b in bound:
P1 = p[b[0], :]
P2 = p[b[1], :]
if btype == "Segments":
farbe = "r"
plt.plot([P1[0], P2[0]], [P1[1], P2[1]], farbe, lw=1)
# arrows at segments
dX = P2 - P1
ls = np.sqrt(np.sum(dX**2))
v1 = [-dX[0] - dX[1], dX[0] - dX[1]]
av1 = np.sqrt(v1[0] ** 2 + v1[1] ** 2)
v1 = v1 / av1 * ls / 2
v2 = [dX[1] - dX[0], -dX[0] - dX[1]]
av2 = np.sqrt(v2[0] ** 2 + v2[1] ** 2)
v2 = v2 / av2 * ls / 2
pm = (P1 + P2) / 2.0
xx = [pm[0] + v1[0], pm[0], pm[0] + v2[0]]
yy = [pm[1] + v1[1], pm[1], pm[1] + v2[1]]
plt.plot(xx, yy, "y")
else:
farbe = "g"
plt.plot([P1[0], P2[0]], [P1[1], P2[1]], farbe)
# plt.show()
def MakeSurfaceMesh(xstr, ystr, zstr, u0, u1, v0, v1, n1, n2):
eps = 1e-13
all_u = np.linspace(u0, u1, n1)
all_v = np.linspace(v0, v1, n2)
# make u-v boundary for desired mesh
uv_bound = [[all_u[i], all_v[0]] for i in range(n1)]
uv_bound += [[all_u[-1], all_v[i]] for i in range(1, n2 - 1)]
uv_bound += [[all_u[i], all_v[-1]] for i in range(n1)[-1::-1]]
uv_bound += [[all_u[0], all_v[i]] for i in range(1, n2 - 1)[-1::-1]]
max_len = max([np.max(np.diff(all_u)), np.max(np.diff(all_v))])
# make mesh
uv_points = np.array([*uv_bound, uv_bound[0]])
p1, v1 = PointSegments(uv_points)
p_uv, elements, bou, li_bou = DoTriMesh(p1, v1, edge_length=max_len, show=False)
# PlotMeshNumbers(p_uv,elements)
# sort boundary points
UVs = [[u0, v0], [u0, v0]]
bseg = RetrieveSegments(p_uv, bou, li_bou, UVs, ["Nodes"])[0]
bseg = np.sort(bseg)
N = len(bseg)
if N != len(uv_bound):
print(
"WARNING: Additional boundary points inserted by triangle.c "
"(du too different from dv)"
)
# calculate boundary points of 3D surface mesh
bound_p = np.zeros((N, 3))
for i in bseg:
# u = p_uv[i, 0]
# v = p_uv[i, 1]
p = np.array([eval(xstr), eval(ystr), eval(zstr)])
bound_p[i, :] = p
# check for identical points on boundary
NN = len(p_uv)
identical_nodes = []
table_nodes = np.arange(NN)
for i in range(N):
id = ()
for j in range(i + 1, N):
# check if points are the same
if (
bseg[j] >= 0
and bseg[i] >= 0
and np.sum((bound_p[i, :] - bound_p[j, :]) ** 2) < eps
):
id += (bseg[j],)
# mark already identified nodes
table_nodes[bseg[j]] = -1
bseg[j] = -1
if len(id) != 0:
id += (bseg[i],)
identical_nodes += [id[-1::-1]]
# delete all identical nodes
table_nodes = table_nodes[table_nodes >= 0]
# calculate 3D points from uv points
all_p = np.zeros((NN, 3))
for i in range(NN):
# u = p_uv[i, 0]
# v = p_uv[i, 1]
p = np.array([eval(xstr), eval(ystr), eval(zstr)])
all_p[i, :] = p
# final correspondace table
final_table = np.arange(NN)
# rename the nodes
table_nodes = list(table_nodes)
for j in table_nodes:
final_table[j] = table_nodes.index(j)
# rename identical nodes
del_nodes = []
for x in identical_nodes:
for j in x[1::]:
final_table[j] = final_table[x[0]]
del_nodes += [j]
# delete identical nodes
del_nodes = np.sort(del_nodes)[-1::-1]
for j in del_nodes:
all_p = np.delete(all_p, j, axis=0)
# rename nodes in elements
elements = np.array(elements).flatten()
elements = final_table[elements].reshape(-1, 3)
elements = np.array([list(x) for x in elements if len(set(x)) == 3])
# mal.triangular_mesh(all_p[:,0],all_p[:,1],all_p[:,2],elements)
# mal.show()
return all_p, elements
# PlotSurfaceMesh(all_p,elements,color='b')
# plt.show()
def MakeSphere(P0, R, mesh_len, epsilon=1e-8, type="cart"):
if type == "cart":
# make part of the sphere
xstr = f"{R:g}*np.cos(u)*np.sin(v)"
ystr = f"{R:g}*np.sin(u)*np.sin(v)"
zstr = f"{R:g}*np.cos(v)"
Nu = int(2 * np.pi * R / mesh_len)
pp, tt = MakeSurfaceMesh(
xstr, ystr, zstr, 0, 2 * np.pi, np.pi / 4.0, np.pi / 2.0, Nu, int(Nu / 3.0)
)
_edges, bou, _bel = FindEdges(tt)
# add upper part
ubou = []
# count number of points on upper circle
for x in bou:
if pp[x[0], 2] > R / 10.0:
ubou += [x]
# use upper segments for new 2D circle
ubou, _bl = SortSegments(ubou)
p2 = []
v2 = []
for j, x in enumerate(ubou):
p2 += [(pp[x[0], 0], pp[x[0], 1])]
if j == (len(ubou) - 1):
j2 = 0
else:
j2 = j + 1
v2 += [(j, j2)]
# make circle mesh and lift up the z values
ppp, ttt, bouu, _li = DoTriMesh(p2, v2, edge_length=mesh_len, show=False)
ppp = np.append(ppp, np.ones((len(ppp), 1)) * -1, axis=1)
for i in range(len(ppp)):
ppp[i, 2] = np.sqrt(R**2 - ppp[i, 0] ** 2 - ppp[i, 1] ** 2)
# connect upper sphere and lower sphere
bou = [[x[0], x[1]] for x in bou]
p, t, b, _bl, _idn = ConnectMesh(pp, tt, bou, ppp, ttt, bouu, epsilon=1e-8)
pp = p * 1
for i in range(len(pp)):
pp[i, 2] *= -1
# connect lower half circle
tt = [[x[1], x[0], x[2]] for x in t]
bb = np.copy(b)
pn, tn, _bn, _bln, _idn = ConnectMesh(pp, tt, bb, p, t, b, epsilon=1e-8)
else:
# make part of the sphere
xstr = f"{R:g}*np.cos(u)*np.sin(v)"
ystr = f"{R:g}*np.sin(u)*np.sin(v)"
zstr = f"{R:g}*np.cos(v)"
Nu = int(2.0 * np.pi * R / mesh_len)
pn, tn = MakeSurfaceMesh(
xstr, ystr, zstr, 0, 2.0 * np.pi, 0, np.pi, Nu, int(Nu / 3.0)
)
_edges, bou, _bel = FindEdges(tn)
Ps = np.array(P0)
pn = np.array([X - Ps for X in pn])
bou = []
return pn, tn, bou
# Connect two different meshes at their overlapping boundary
def ConnectMesh(p1, t1, b1, p2, t2, b2, epsilon=1e-8):
# find identical nodes on boundary
# find correspondence between boundary nodes on boundary 1 and 2
bb2 = [x[0] for x in b2]
eps = epsilon**2
id_nodes = []
for seg in b1:
X = p1[seg[0], :]
bb2_tmp = bb2.copy()
for i, node2 in enumerate(bb2):
Y = p2[node2, :]
delta = np.sum((X - Y) ** 2)
if delta < eps:
id_nodes += [(seg[0], node2)]
# print(X,Y)
bb2_tmp.pop(i)
bb2 = bb2_tmp
# make list with all mesh 2 nodes for renumbering
node2_table = np.arange(len(p2))
tab = node2_table * 0 - 1
id_b2 = [x[1] for x in id_nodes]
node2_table = np.delete(node2_table, id_b2)
for i, x in enumerate(node2_table):
tab[x] = i + len(p1)
# replace boundary nodes
for x in id_nodes:
tab[x[1]] = x[0]
# delete identical nodes in p2
idn2 = [x[1] for x in id_nodes]
p2 = np.delete(p2, idn2, axis=0)
# renumber in element list
ptot = np.append(p1, p2, axis=0)
for i in range(len(t2)):
t2[i, :] = [tab[t2[i, 0]], tab[t2[i, 1]], tab[t2[i, 2]]]
ttot = np.append(t1, t2, axis=0)
# find new boundary
idn1 = [x[0] for x in id_nodes]
boundary1 = [x for x in b1 if not (idn1.count(x[0]) == 1 and idn1.count(x[1]) == 1)]
boundary2 = [
[tab[x[0]], tab[x[1]]]
for x in b2
if not (idn2.count(x[0]) == 1 and idn2.count(x[1]) == 1)
]
boundary = np.append(boundary1, boundary2).reshape(-1, 2)
boundary = [tuple(x) for x in boundary]
btot, blist = SortSegments(boundary)
blist += [len(btot)]
# boundary=mt.CheckSegmentSense(ttot,bound,blist)
print("identical nodes found", len(id_nodes))
return ptot, ttot, btot, blist, id_nodes
# NoSe is a list containing the type of boundary 'Nodes' or 'Segments'
# points : mesh points
#
def RetrieveSegments(points, segments, curve_list, Ps, NoSe):
"""
all = RetrieveSegments(points,segments,curve_list,Ps,NoSe)
------------------
points : all points of the mesh
segments : All edges used for search
curve_list : List associated with segments
Ps : List of points.
Consecutive Points form start and endpoint of path lying in segments.
If there is no connection in segments the path is skipped
NoSe : List of strings defining type of path found ('Nodes' or 'Segments')
"""
# extract first and second row from segment list
row1 = list(np.array(segments)[:, 0])
row2 = list(np.array(segments)[:, 1])
# for second order
if len(segments[0]) == 3:
row3 = list(np.array(segments)[:, 2])
else:
row3 = []
# find node indices for desired points
bnodes = list(set(row1 + row2))
nn, _dd = FindClosestNode(bnodes, points, Ps)
# print("Ps = ",Ps)
# print("Nodes: ",nn)
# print("error ",dd)
# make different segments
ret = []
this_type = 0
for k in range(len(Ps) - 1):
Orient = False
try:
first = row1.index(nn[k])
last = row2.index(nn[k + 1])
except ValueError:
try:
first = row1.index(nn[k + 1])
last = row2.index(nn[k])
Orient = True
except ValueError:
# only possible if: last_bnr!=first_bnr
try:
first = row1.index(nn[k + 1])
last = row1.index(nn[k])
except ValueError:
first = row2.index(nn[k + 1])
last = row2.index(nn[k])
# look for the number of boundary
for j in range(len(curve_list) - 1):
if curve_list[j] <= first < curve_list[j + 1]:
first_bnr = j
if curve_list[j] <= last < curve_list[j + 1]:
last_bnr = j
# both points are on the same boundary
if last_bnr == first_bnr:
# nodes
# print("1. node , 2. node ",nn[k],nn[k+1])
# print("boundaries ",last_bnr,first_bnr)
# print("this_type = ",this_type)
if this_type >= len(NoSe):
print(
f"Error (this_type={this_type} , len(NoSe)={len(NoSe)}): "
"Increase Nodes/Segments List"
)
if NoSe[this_type] == "Nodes":
if first < last:
add = row1[first : last + 1]
add += row3[first : last + 1]
else:
add = (
row1[first : curve_list[first_bnr + 1] :]
+ row1[curve_list[first_bnr] : last + 1]
)
add += (
row3[first : curve_list[first_bnr + 1] :]
+ row3[curve_list[first_bnr] : last + 1]
)
# line not closed
if nn[k] != nn[k + 1]:
add += [row2[last]]
# segments
else:
if first < last:
add = segments[first : last + 1]
else:
add = (
segments[first : curve_list[first_bnr + 1] :]
+ segments[curve_list[first_bnr] : last + 1]
)
if Orient:
add = [x[-1::-1] for x in add[-1::-1]]
ret += [add]
this_type += 1
return ret
# find all points of the triangulation which are element of
# curve, given by a function call
def FindCurveSegments(points, t, func, inner_p=None):
if inner_p is None:
inner_p = []
ret = []
for i, X in enumerate(points):
yn = func(X)
if yn:
ret += [i]
if ret == []:
print("WARNING: No nodes found on given Curve func", func)
return [], []
else:
print(len(ret), " Nodes found on inner curve")
# construct edges
t_all = np.array([t[:, 0], t[:, 1], t[:, 1], t[:, 2], t[:, 2], t[:, 0]]).T
tt = t_all.reshape(3 * len(t), 2)
all_edges = [tuple(x) for x in tt]
# all unique edges
all_edges = list(set(all_edges))
nodes = ret[:]
seg = []
# find the segments to the node list
for k in range(len(ret)):
for y in all_edges:
if (y[0] == ret[k] and y[1] in ret) or (y[1] == ret[k] and y[0] in ret):
z = tuple(sorted(y))
seg += [z]
# make list unique
seg = list(set(seg))
# each node should be connected to other nodes not more than twice
occ = [x[0] for x in seg] + [x[1] for x in seg]
occ = Counter(occ)
for x in occ:
if occ[x] > 2:
multiple = [y for y in seg if x in y]
for z in multiple:
pm = (points[z[0], :] + points[z[1], :]) / 2.0
if not func(pm):
seg.remove(z)
occ[z[0]] -= 1
occ[z[1]] -= 1
# sort list
sseg, ls = SortSegments(seg)
ls += [len(sseg)]
if len(ls) > 2:
print(len(ls) - 1, "different curves found")
seg = [list(y) for y in sseg]
# give segments a direction in 2D if desired,
if len(points[0]) == 2 and inner_p != []:
for n in range(len(ls) - 1):
i1 = ls[n]
i2 = ls[n + 1]
# look for closest segment to inner point
this_seg = seg[i1:i2]
first_nodes = np.array(this_seg)[:, 0]
# check first forclosest node
tot_dist = []
indices = []
for pp in inner_p:
i_p = np.array(pp)
square_dist = np.sum((points[first_nodes, :] - i_p) ** 2, axis=1)
jj = np.argsort(square_dist)[0]
tot_dist += [square_dist[jj]]
indices += [jj]
nn = np.argsort(np.array(tot_dist))[0]
jj = indices[nn]
i_p = inner_p[nn]
# closest segment at position jj
n1 = this_seg[jj][0]
n2 = this_seg[jj][1]
dX = points[n2, :] - points[n1, :]
dY = i_p - points[n1, :]
# check orientation
if np.cross(dX, dY) < 0:
this_seg = [[x[1], x[0]] for x in this_seg[-1::-1]]
seg[i1:i2] = this_seg
print("change direction around", i_p)
return nodes, seg, ls
# find the nodes or segements between two points P1, P2
# btype is Nodes or Segments
##########################################
# --> ZU LANGSAM RETRIEVEBOUNDARY nutzen
##########################################
def FindBoundary(p, t, P1, P2, btype):
# find boundary
_kanten, rand_seg, _rand_elemente = FindEdges(t)
# connect boundary according to P1
rand_seg, rand_list, P1_boundary = ConnectBoundary(
rand_seg, p, pstart=[(P1[0], P1[1])]
)
# search for node at position P2
index_start = rand_list[P1_boundary]
if P1_boundary == len(rand_list) - 1:
index_end = rand_seg.shape[0]
else:
index_end = rand_list[P1_boundary + 1]
# print("index_start",index_start)
# print("index_end",index_end)
rand_seg = rand_seg[index_start:index_end]
# print("rand_seg",rand_seg)
NR = rand_seg.shape[0]
vec = np.array(P2) - p[rand_seg[0, 1], :]
this_min = np.dot(vec, vec)
last_seg = 0
for k in range(1, NR, 1):
Pt = p[rand_seg[k, 1]]
vec = Pt - P2
this_prod = np.dot(vec, vec)
if this_prod < this_min:
this_min = this_prod
last_seg = k
# make boundary segments between P1 and P2
result = np.array([rand_seg[0]])
for k in range(1, last_seg + 1):
result = np.insert(result, result.shape[0], rand_seg[k], axis=0)
# define nodes if necessary
if btype == "Nodes":
last_node = result[result.shape[0] - 1, 1]
result = result[:, 0]
# not a closed boundary
if result[0] != last_node:
result = np.append(result, last_node)
# print("Erster Punkt:",p[result[0],:])
return result
# sort a list of edges (with elemental number) along a path
# [[40, (4, 8)], [90, (12, 17)], [40, (2, 4)], [8, (4, 8)], [90, (8, 12)], [8, (8, 12)]]
# -->
# [[90, (12, 17)], [90, (8, 12)], [8, (8, 12)], [8, (4, 8)], [40, (4, 8)], [40, (2, 4)]]
# not connected pathes are possible
def SortEdgeList(folly):
fol = [[x[0], tuple(sorted(x[1]))] for x in folly]
index = -1
new_fol = []
isolist = []
while index < 0:
# find start element (boundary element or first element of list)
isolist.append(len(new_fol))
index = 0
edges = [x[1] for x in fol]
for k, x in enumerate(edges):
if edges.count(x) == 1:
index = k
break
# start sorting
new_fol.append(fol[index])
del fol[index]
while len(fol) > 0:
# find index of next list element
index = -1
for k, y in enumerate(fol):
if (new_fol[-1][0] == y[0]) | (new_fol[-1][1] == y[1]):
index = k
break
if index < 0:
break
# add to new list and delete old list element
new_fol.append(fol[index])
del fol[index]
return new_fol, isolist
def ContourSurface(p, t, u, iso_in, infig):
# make iso values
if isinstance(iso_in, int):
isolines = np.linspace(min(u), max(u), iso_in)
print("isolines", isolines)
else:
isolines = iso_in
# make all edges
NE = len(t)
all_edges = np.array([[x[0], x[1], x[1], x[2], x[2], x[0]] for x in t])
all_edges = np.resize(all_edges, (3 * NE, 2))
for iso in isolines:
# find the edges and elements with an isoline with value iso
iso_edges = [
[i // 3, tuple(x)]
for i, x in enumerate(all_edges)
if min(u[x]) <= iso <= max(u[x])
]
if len(iso_edges) > 1:
iso_edges, nis = SortEdgeList(iso_edges)
# add end of list for slicing
nis.append(len(iso_edges))
# run through all the non-connected isolines
for k in range(len(nis) - 1):
hhelp = iso_edges[nis[k] : nis[k + 1]]
# take only each second point
unique_ie = [hhelp[0], *hhelp[1:-1:2], hhelp[-1]]
# Compute polygon for this iso curve
X = Y = Z = []
for x in unique_ie:
i1 = x[1][0]
i2 = x[1][1]
t = (iso - u[i1]) / (u[i2] - u[i1])
P = p[i1] + t * (p[i2] - p[i1])
X = np.append(X, P[0])
Y = np.append(Y, P[1])
Z = np.append(Z, P[2])
plt.plot3d(
X,
Y,
Z,
tube_radius=None,
figure=infig,
color=(0.2, 0.2, 0.2),
line_width=5,
)
def ComputeGradient(p, t, u, poi=None, num=10):
"""
Compute the Gradient of a triangular mesh
Input: p array([[x1,y1],[x2,y2],...]) node points
t array([[n1,n2,n3],[n4,n5,n6],...]) elements
u array([u1,u2,u3,.....]) function at node values
poi array([[X1,Y1],[X2,Y2],...]) points for gradient evaluation
num N generate NxN points array poi
Output: x x-component of point
y y-component of point
g_x gradient, x-component at (x,y)
g_y gradient, y-component at (x,y)
"""
if poi is None:
poi = []
if poi == []:
eps = 1e-6
h1 = np.linspace(min(p[:, 0]) + eps, max(p[:, 0]) - eps, num)
h2 = np.linspace(min(p[:, 1]) + eps, max(p[:, 1]) - eps, num)
h1, h2 = np.meshgrid(h1, h2)
h1.resize(num * num, 1)
h2.resize(num * num, 1)
points = np.append(h1, h2, axis=1)
else:
points = poi
# Compute all a,b,c
a1 = p[t[:, 1], 0] * p[t[:, 2], 1] - p[t[:, 1], 1] * p[t[:, 2], 0]
a2 = p[t[:, 2], 0] * p[t[:, 0], 1] - p[t[:, 2], 1] * p[t[:, 0], 0]
b1 = p[t[:, 1], 1] - p[t[:, 2], 1]
b2 = p[t[:, 2], 1] - p[t[:, 0], 1]
c1 = p[t[:, 2], 0] - p[t[:, 1], 0]
c2 = p[t[:, 0], 0] - p[t[:, 2], 0]
delta = 0.5 * (b1 * c2 - b2 * c1)
XYUV = np.array([])
for x in points:
x = np.array(x)
ksi = 0.5 / delta * (a1 + b1 * x[0] + c1 * x[1])
eta = 0.5 / delta * (a2 + b2 * x[0] + c2 * x[1])
element = np.where((ksi >= 0) & (eta >= 0) & (eta + ksi - 1 <= 0))[0]
if len(element) > 0:
element = element[0]
bb1 = b1[element]
bb2 = b2[element]
bb3 = p[t[element, 0], 1] - p[t[element, 1], 1]
cc1 = c1[element]
cc2 = c2[element]
cc3 = p[t[element, 1], 0] - p[t[element, 0], 0]
dd = delta[element]
u1 = u[t[element, 0]]
u2 = u[t[element, 1]]
u3 = u[t[element, 2]]
gx = 0.5 / dd * (bb1 * u1 + bb2 * u2 + bb3 * u3)
gy = 0.5 / dd * (cc1 * u1 + cc2 * u2 + cc3 * u3)
help = np.append(x, np.array([gx, gy]))
XYUV = np.append(XYUV, help)
XYUV.resize(len(XYUV) // 4, 4)
return XYUV[:, 0], XYUV[:, 1], XYUV[:, 2], XYUV[:, 3]
# For a given boundary segment Seg the corresponding boundary element is found.
# A list of all boundary elements must be provided
def FindBoundaryElement(t, Seg, BoundE):
for i in range(len(BoundE)):
# check main nodes
if len(set(t[BoundE[i], 0:3]).intersection(Seg[0:2])) == 2:
# print("Boundary Segment ",Seg," Boundary Element ",t[BoundE[i]])
# sort in a way that the first two indices are boundary indices
# main indices first
ThirdIndex = next(set(t[BoundE[i], 0:3]) - set(Seg[0:2]))
dbllist = list(t[BoundE[i], 0:3]) * 2
kk = 1 + dbllist.index(ThirdIndex)
MainIndices = dbllist[kk : kk + 3]
# middle indices next
if len(Seg) == 3:
dbllist = list(t[BoundE[i], 3:]) * 2
kk = dbllist.index(Seg[2])
RestIndices = dbllist[kk : kk + 3]
else:
RestIndices = []
# print("Segment: ",Seg," Element: ---> ",MainIndices+RestIndices)
return MainIndices + RestIndices
# Compute the normal derivative along the segments in boundary
#
def NormalDerivative(boundary, p, tt, u, inner=None, BouE=None):
"""
Compute the normal derivative on a given boundary
Input: boundary [[n1,n2],[n3,n4],...] boundary segments
p array([[x1,y1],[x2,y2],...]) node points
t array([[n1,n2,n3],[n4,n5,n6],...]) elements
u array([u1,u2,u3,.....]) function at node values
right direction of inner node/element
Output: nor normal derivative at the segments
rl running length
line_int line integral over boundary of the normal derivative
"""
if BouE is None:
BouE = []
# second order
if len(boundary[0]) == 3:
if BouE == []:
edges, _segments, BouE = FindEdges(tt)
nor = []
rl = []
line_int = 0
for seg in boundary:
# find boundary element, third index is the volume index
b_elem = FindBoundaryElement(tt, seg, BouE)
xM, yM = p[b_elem[3]]
X1 = p[b_elem[0]]
X2 = p[b_elem[1]]
X3 = p[b_elem[2]]
ls = np.sqrt(np.sum((X2 - X1) ** 2))
b_1 = X2[1] - X3[1]
b_2 = X3[1] - X1[1]
b_3 = X1[1] - X2[1]
c_1 = X3[0] - X2[0]
c_2 = X1[0] - X3[0]
c_3 = X2[0] - X1[0]
a_1 = X2[0] * X3[1] - X3[0] * X2[1]
a_2 = X3[0] * X1[1] - X1[0] * X3[1]
a_3 = X1[0] * X2[1] - X2[0] * X1[1]
delta = 0.5 * (b_1 * c_2 - c_1 * b_2)
delsq = delta**2
Phi_1, Phi_2, Phi_3, Phi_4, Phi_5, Phi_6 = u[b_elem]
b12 = b_1 * b_2
b13 = b_1 * b_3
b23 = b_2 * b_3
_b11 = b_1**2
_b22 = b_2**2
b33 = b_3**2
c12 = c_1 * c_2
c13 = c_1 * c_3
c23 = c_2 * c_3
_c11 = c_1**2
_c22 = c_2**2
c33 = c_3**2
Qseq = [0, 0, 0, 0, 0, 0]
Qseq[0] = (b13 + c13) * (0.5 * delta - a_1 - b_1 * xM - c_1 * yM) / delsq
Qseq[1] = (b23 + c23) * (0.5 * delta - a_2 - b_2 * xM - c_2 * yM) / delsq
Qseq[2] = (b33 + c33) * (0.5 * delta - a_3 - b_3 * xM - c_3 * yM) / delsq
Qseq[3] = (
-(
a_1 * b23
+ a_1 * c23
+ a_2 * b13
+ a_2 * c13
+ 2 * b12 * b_3 * xM
+ b13 * c_2 * yM
+ b_1 * c23 * xM
+ b23 * c_1 * yM
+ b_2 * c13 * xM
+ 2 * c12 * c_3 * yM
)
/ delsq
)
Qseq[4] = (
-(
a_2 * b33
+ a_2 * c33
+ a_3 * b23
+ a_3 * c23
+ 2 * b_2 * b33 * xM
+ b23 * c_3 * yM
+ b_2 * c33 * xM
+ b33 * c_2 * yM
+ b_3 * c23 * xM
+ 2 * c_2 * c33 * yM
)
/ delsq
)
Qseq[5] = (
-(
a_1 * b33
+ a_1 * c33
+ a_3 * b13
+ a_3 * c13
+ 2 * b_1 * b33 * xM
+ b13 * c_3 * yM
+ b_1 * c33 * xM
+ b33 * c_1 * yM
+ b_3 * c13 * xM
+ 2 * c_1 * c33 * yM
)
/ delsq
)
density = (
Qseq[0] * Phi_1
+ Qseq[1] * Phi_2
+ Qseq[2] * Phi_3
+ Qseq[3] * Phi_4
+ Qseq[4] * Phi_5
+ Qseq[5] * Phi_6
)
line_int += density
nor += [density / ls]
rl += [ls]
# add all segment length
for i in range(1, len(rl)):
rl[i] += rl[i - 1]
rl = np.array(rl) - rl[0]
return np.array(nor), np.array(rl), line_int
elif len(boundary[0]) == 2 and inner is None:
if BouE == []:
edges, _segments, BouE = FindEdges(tt)
nor = []
rl = []
line_int = 0
for seg in boundary:
# find boundary element, third index is the volume index
b_elem = FindBoundaryElement(tt, seg, BouE)
X1 = p[b_elem[0]]
X2 = p[b_elem[1]]
X3 = p[b_elem[2]]
ls = np.sqrt(np.sum((X2 - X1) ** 2))
b_1 = X2[1] - X3[1]
b_2 = X3[1] - X1[1]
b_3 = X1[1] - X2[1]
c_1 = X3[0] - X2[0]
c_2 = X1[0] - X3[0]
c_3 = X2[0] - X1[0]
delta = 0.5 * (b_1 * c_2 - c_1 * b_2)
Phi_1, Phi_2, Phi_3 = u[b_elem]
bb = 0.5 / delta * (b_1 * Phi_1 + b_2 * Phi_2 + b_3 * Phi_3)
cc = 0.5 / delta * (c_1 * Phi_1 + c_2 * Phi_2 + c_3 * Phi_3)
density = -bb * b_3 - cc * c_3
line_int += density
nor += [density / ls]
rl += [ls]
# add all segment length
for i in range(1, len(rl)):
rl[i] += rl[i - 1]
rl = np.array(rl) - rl[0]
return np.array(nor), np.array(rl), line_int
# first order, older code --> needs update
# step through all segments
nor = []
rl = [0]
line_int = 0
# make list of edges
edges = []
index_3 = []
for lt in tt:
edges += {lt[0], lt[1]}
index_3 += [lt[2]]
edges += {lt[1], lt[2]}
index_3 += [lt[0]]
edges += {lt[0], lt[2]}
index_3 += [lt[1]]
for s in boundary:
# seg=np.array(s)
# no_el=[(np.setxor1d(tt[j],seg)[0],j) for j in range(len(tt))
# if len(np.setxor1d(tt[j],seg))==1]
# search for element containing segment s, save third index and element number
so_seg = set(s)
no_el = []
for j in range(len(edges)):
if so_seg == edges[j]:
no_el += [(index_3[j], j // 3)]
if len(no_el) == 2:
break
# for inner curves the element has to be determined. Inner='right'
# means that the element is on the right side of the oriented curve
# print("s,node",seg,no_el,tt[no_el[0][1]],tt[no_el[1][1]])
v1 = p[s[1], :] - p[s[0], :]
normal_x = v1[1]
normal_y = -v1[0]
if len(no_el) == 2:
v2 = p[no_el[0][0], :] - p[s[0], :]
he = v1[0] * v2[1] - v1[1] * v2[0]
if (he < 0 and inner == "right") or (he > 0 and inner == "left"):
n = tt[no_el[0][1], :]
else:
n = tt[no_el[1][1], :]
if inner == "right":
normal_x *= -1
normal_y *= -1
# plt.plot(
# [(p[n[0],0]+p[n[1],0]+p[n[2],0])/3],
# [(p[n[0],1]+p[n[1],1]+p[n[2],1])/3],
# "x")
else:
n = [no_el[0][0], s[0], s[1]]
# local node 1 is always in the volume
# print("normal",normal_x,normal_y)
Ls = np.sqrt(normal_x**2 + normal_y**2)
b1 = p[n[1], 1] - p[n[2], 1]
b2 = p[n[2], 1] - p[n[0], 1]
b3 = p[n[0], 1] - p[n[1], 1]
c1 = p[n[2], 0] - p[n[1], 0]
c2 = p[n[0], 0] - p[n[2], 0]
c3 = p[n[1], 0] - p[n[0], 0]
delta = 0.5 * (b1 * c2 - c1 * b2)
u1 = u[n[0]]
u2 = u[n[1]]
u3 = u[n[2]]
phi_x = b1 * u1 + b2 * u2 + b3 * u3
phi_y = c1 * u1 + c2 * u2 + c3 * u3
# print("Gradient",phi_x,phi_y)
dudn = 0.5 / Ls / delta
dudn *= phi_x * normal_x + phi_y * normal_y
# print("dudn",dudn)
line_int += Ls * dudn
rl = np.append(rl, rl[-1] + Ls)
nor = np.append(nor, dudn)
# PlotBoundary(p,boundary,'Segments')
# plt.triplot(p[:,0],p[:,1],tt)
# plt.show()
# remove last length as rl starts with 0
rl = np.delete(rl, len(rl) - 1)
return nor, rl, line_int
def GetPosition(allnodes, node):
"""
Find the position of the nodes in array node in the array allnodes
Input: allnodes array([n1,n2,n3,...]) array of nodes
node array([nn1,nn2,....]) array of nodes
Output: pos array([pos_of_nn1,pos_of_nn2,...])
"""
return np.array([np.where(allnodes == x)[0][0] for x in node])
#
# Check orientation
#
def TriOrientation(p, t):
"""
Check Orientation of a triangulation, flip if necessary
"""
for m in range(len(t)):
p1 = np.array(p[t[m, 0]])
p2 = np.array(
[
t[m, 0],
]
)
p3 = np.array(
p[
t[m, 0],
]
)
if np.cross(p2 - p1, p3 - p1) < 0:
t[m] = [t[m, 0], t[m, 2], t[m, 1]]
print("flip ", m, " element")
return p, t
# Extract the edges
# ouput, edges and boundary edges and boundary_elements
def FindEdges(t):
"""
Find all edges of a given triangulation
Input: t array([[n1,n2,n3],[n4,n5,n6],...]) elements
Output: all_edges
boundary_edges
boundary_elements
"""
# pdb.set_trace();
NE = t.shape[0]
# generate an array of all edges
tall = np.array([t[:, 0], t[:, 1], t[:, 1], t[:, 2], t[:, 2], t[:, 0]]).T
tt = tall.reshape(3 * NE, 2)
ttt = np.sort(tt, 1)
# find all boundary edges
all_edges = [tuple(x) for x in ttt]
boundary_edges = []
boundary_elements = []
for i, x in enumerate(all_edges):
if all_edges.count(x) == 1:
row = i // 3
col = 2 * (i % 3)
boundary_edges.append(tuple(tall[row, col : col + 2]))
boundary_elements.append(row)
# find all unique edges
all_edges = list(set(all_edges))
boundary_elements = np.array(boundary_elements)
return all_edges, boundary_edges, boundary_elements
##################
#
# Boundary Tools
#
##################
# check the sense of the numbering in the segments
def CheckSegmentSense(t, boundary, indices):
new_bound = []
for k, pos in enumerate(indices):
first = set(boundary[pos])
boundary_element = [x for x in t if first.issubset(set(x))]
x = boundary_element[0]
if first == {x[0], x[2]}:
first = [x[2], x[0], x[1]]
elif first == {x[1], x[2]}:
first = [x[1], x[2], x[0]]
else:
first = x
# change sense
if k == len(indices) - 1:
end = len(boundary)
else:
end = indices[k + 1]
this_bound = [list(x) for x in boundary[pos:end]]
if first[0] != boundary[pos][0]:
this_bound = [[x[1], x[0]] for x in this_bound[-1::-1]]
# this_bound=[this_bound[-1]]+this_bound[:-1]
new_bound += this_bound
return new_bound
# given one segment
# e.g. (X,2) find segment (2,Y) and delete (2,Y) from list
def FindNextSegment(all_segments, node):
"""
Find a segment in a list of segments
e.g. node=(X,2) find segment (2,Y) and delete (2,Y) from list all_segments
Output: all_segments
flag for indicating the start of a new boundary
"""
# find next connecting segment
help = [x for x in all_segments if x[0] == node or x[1] == node]
new_bound = False
if len(help) == 0: # if connecting segment does not exist (=>new boundary)
# new code, allow for open segment trajectories
rest_nodes = [x for t in all_segments for x in t]
one_occ = [
[i // 2, x] for i, x in enumerate(rest_nodes) if rest_nodes.count(x) == 1
]
# only closed segments are present
if one_occ == []:
ret = all_segments[0]
del all_segments[all_segments.index(ret)]
# open lines are present
else:
ret = all_segments[one_occ[0][0]]
del all_segments[all_segments.index(ret)]
if one_occ[0][1] == ret[1]:
# print("Change direction of segment",ret)
ret = ret[-1::-1]
# old code
# ret=all_segments[0]
# del all_segments[all_segments.index(ret)]
new_bound = True
else:
ret = help[0]
del all_segments[all_segments.index(ret)]
if ret[0] != node:
ret = ret[-1::-1]
# print("Change direction of segment",help[0])
return ret, new_bound
# sort segments: (3,6),(6,1),(1,12),(12,5),...
# on output: sorted segments and indices of the different boundaries
def SortSegments(all_segments):
"""
Sort a list of Segements accordinly [(3,6),(6,1),(1,12),(12,5),...]
Input: unsorted list
Output: sorted list
list containing the indices of a new boundary
see also FindNextSegment
"""
count = len(all_segments)
node = -1
sorted_segments = []
boundaries = []
for j in range(len(all_segments)):
seg, new_bound = FindNextSegment(all_segments, node)
node = seg[1]
sorted_segments.append(seg)
if new_bound:
boundaries.append(j)
if len(sorted_segments) != count:
print("Something is wrong, number of segments not the same")
return sorted_segments, boundaries
# connect segments in a defined way
# (see SortSegments), but start sorting with a defined point p
# multiple p'2 for different closed boundaries are possible
def ConnectBoundary(boundary_segments, Pall, pstart=None):
"""
Sort the boundary segments in a defined order
Input: boundary_segments [[n1,n2],[n3,n4],...] boundary segments
pstart array([[x1,y1],[x2,y2],...)
start number of boundary 1 with (x1,y1) ,...
Pall array([[X1,X1],[X2,X2],...) node coordinates
Output: sorted boundary segments
start position of boundary segment for new curves
this_boundary number of boundary for the last point in the list pstart
see also SortSegments
"""
if pstart is None:
pstart = []
# sort the boundary segments
allseg = boundary_segments[:]
allseg, boundaries = SortSegments(allseg)
if pstart == []:
return allseg, boundaries
max_boundaries = len(boundaries)
# find all nodes on the given boundary
nodes = [x[0] for x in allseg]
# find closest nodes to desired point list p
indices, _distances = FindClosestNode(nodes, Pall, pstart)
# print("indices,dist=",indices,distances)
# print("boundaries",boundaries)
# print("all_seg",allseg)
# change order within each closed boundary
flag_sorted = [False] * len(boundaries)
for j in range(len(indices)):
# find position of node in the boundary list
# indj gives the position of the segment in allseg
indj = nodes.index(indices[j])
# find the number of boundary the node belongs to
this_boundary = (np.where(np.array(boundaries) <= indj)[0])[-1]
if not flag_sorted[this_boundary]:
# define the indices for slicing
ind_1 = boundaries[this_boundary]
if this_boundary + 1 == max_boundaries:
ind_2 = len(allseg)
else:
ind_2 = boundaries[this_boundary + 1]
# rearange the segments in the corresponding boundary
allseg = (
allseg[:ind_1]
+ allseg[indj:ind_2]
+ allseg[ind_1:indj]
+ allseg[ind_2:]
)
# resort only once
flag_sorted[this_boundary] = True
allseg = [[x[0], x[1]] for x in allseg]
allseg = np.array(allseg)
# print("Ræckgabe: ",allseg,boundaries)
return allseg, boundaries, this_boundary
# p0: points to be mapped
# Pall: all points present in the mesh
# nodes: only these nodes are used for search
# constraint defines constraints on distance
# tree: take a special tree for the search
def FindClosestNode(nodes, Pall, p0, constraint=-1, tree=None):
"""
nodes,dist = FindClosestNode(nodes,Pall,p0,constraint=-1,tree=None)
-------
0: points to be mapped
Pall: all points present in the mesh
nodes: only these nodes are used for search
constraint defines constraints on distance
tree: take a special tree for the search
"""
# take those points of the node list
# print("len(nodes) ",len(nodes))
# print("len(Pall) ",len(Pall))
if tree is None:
p_nodes = np.array(Pall)
p_nodes = p_nodes[nodes]
# look for minimum distance, define dist function
mytree = cKDTree(p_nodes)
else:
mytree = tree
dist, index = mytree.query(np.array(p0))
node_closest = [nodes[j] for j in index]
# check constraints
num_p = len(p0)
if constraint < 0:
return node_closest, dist
elif np.isscalar(constraint):
constraint = constraint * np.ones(num_p)
elif len(p0) != len(constraint):
print("Error in constraint definition")
return [], []
# check constraint for each node
flags = [((dist[j] <= constraint[j]) | (constraint[j] < 0)) for j in range(num_p)]
for j in range(num_p):
if not flags[j]:
node_closest[j] = -1
return node_closest, dist
# check relative position of two points
def SamePoint(p1, p2, delta):
dp = np.array(p1) - np.array(p2)
d = np.sqrt(dp[0] ** 2 + dp[1] ** 2)
ret = False
if d < delta:
ret = True
return ret
#####################
#
# Make simple curves
#
#####################
#
#
#
# make a circle or part of it
#
def CircleSegments(
middle, radius, num_points=10, a_min=0.0, a_max=2.0 * np.pi, edge_length=-1
):
"""
CircleSegments(middle,radius,num_points=10,a_min=0.,a_max=2.*np.pi,edge_length=-1)
"""
# check for closed loop
number_points = num_points
if edge_length > 0:
number_points = int(np.floor(abs(radius / edge_length * (a_max - a_min)))) + 1
number_points = max(number_points, 5)
delta = (a_max - a_min) / number_points
closed = False
if abs(abs(a_max - a_min) - 2 * np.pi) < 0.1 * abs(delta):
closed = True
t = np.linspace(a_min, a_max, number_points, not closed)
# define points
points = [
(middle[0] + radius * np.cos(angle), middle[1] + radius * np.sin(angle))
for angle in t
]
# define vertices
vertices = [(j, j + 1) for j in range(0, len(points) - 1, 1)]
if closed:
vertices += [(len(points) - 1, 0)]
return points, vertices
# Straight line
def LineSegments(P1, P2, num_points=10, edge_length=-1):
"""
p,v = LineSegments(P1,P2,num_points=10,edge_length=-1)
"""
number_points = num_points
if edge_length > 0:
p1 = np.array(P1)
p2 = np.array(P2)
number_points = int(np.floor(np.sqrt(np.sum((p2 - p1) ** 2)) / edge_length)) + 1
if number_points <= 1:
number_points = 5
t = np.linspace(0, 1, number_points)
points = [
(P1[0] + param * (P2[0] - P1[0]), P1[1] + param * (P2[1] - P1[1]))
for param in t
]
vertices = [(j, j + 1) for j in range(0, len(points) - 1, 1)]
return points, vertices
# Rectangle
def RectangleSegments(
P1, P2, num_points=60, edge_length=-1, edge_lengthx=-1, edge_lengthy=-1
):
"""
p,v = RectangleSegments(P1,P2,num_points=60,
edge_length=-1,edge_lengthx=-1,edge_lengthy=-1)
"""
P11 = [P2[0], P1[1]]
P22 = [P1[0], P2[1]]
npoints = int(np.floor(num_points / 4))
lengx = edge_length
lengy = edge_length
if edge_lengthx > 0:
lengx = edge_lengthx
if edge_lengthy > 0:
lengy = edge_lengthy
p_1, _v_1 = LineSegments(P1, P11, npoints, lengx)
p_2, _v_2 = LineSegments(P11, P2, npoints, lengy)
p_3, _v_3 = LineSegments(P2, P22, npoints, lengx)
p_4, _v_4 = LineSegments(P22, P1, npoints, lengy)
p, v = AddSegments(p_1, p_2)
p, v = AddSegments(p, p_3)
p, v = AddSegments(p, p_4)
return p, v
def ORecSegments(
P1,
P2,
rho,
num_points=60,
num_pc=7,
edge_length=-1,
edge_lengthx=-1,
edge_lengthy=-1,
):
"""
p,v = ORecSegments(P1,P2,rho,num_points=60,num_pc=7,
edge_length=-1,edge_lengthx=-1,edge_lengthy=-1)
"""
x1L = P1[0]
x2L = P2[0] - rho
x1R = P1[0] + rho
x2R = P2[0]
y1L = P1[1] + rho
y3L = P2[1]
y1R = P1[1]
y3R = P2[1] - rho
Dxx = P2[0] - P1[0]
Dyy = P2[1] - P1[1]
if rho > Dxx / 3 or rho > Dyy / 3:
print("Error, rho too large")
lengx = edge_length
lengy = edge_length
if edge_lengthx > 0:
lengx = edge_lengthx
if edge_lengthy > 0:
lengy = edge_lengthy
npoints = int(np.floor(num_points / 4))
p_1, _v_1 = LineSegments([x1R, y1R], [x2L, y1R], npoints, lengx)
p_2, _v_2 = LineSegments([x2R, y1L], [x2R, y3R], npoints, lengy)
p_3, _v_3 = LineSegments([x2L, y3L], [x1R, y3L], npoints, lengx)
p_4, _v_4 = LineSegments([x1L, y3R], [x1L, y1L], npoints, lengy)
p_5, _v_5 = CircleSegments([x2L, y1L], rho, num_pc, -np.pi / 2.0, 0, edge_length)
p_6, _v_6 = CircleSegments([x2L, y3R], rho, num_pc, 0, np.pi / 2.0, edge_length)
p_7, _v_7 = CircleSegments([x1R, y3R], rho, num_pc, np.pi / 2.0, np.pi, edge_length)
p_8, _v_8 = CircleSegments(
[x1R, y1L], rho, num_pc, np.pi, 3 * np.pi / 2, edge_length
)
p, v = AddSegments(p_1, p_5)
p, v = AddSegments(p, p_2)
p, v = AddSegments(p, p_6)
p, v = AddSegments(p, p_3)
p, v = AddSegments(p, p_7)
p, v = AddSegments(p, p_4)
p, v = AddSegments(p, p_8)
return p, v
# List of points
def PointSegments(p, edge_length=-1):
"""
p,v = PointSegments(p,edge_length=-1)
"""
if edge_length != -1:
pt = np.array(p)
p1 = [pt[0]]
for i in range(1, len(pt)):
dp = pt[i] - pt[i - 1]
N = (int)(np.sqrt(np.sum(dp**2)) / edge_length) + 1
N = max(N, 2)
tvals = np.linspace(0, 1, N)
p1 += [list(pt[i - 1] + tt * dp) for tt in tvals[1:]]
p1 = np.array(p1)
else:
p1 = np.array(p)
delta = np.min(np.sqrt(np.sum((p1[1:] - p1[:-1]) ** 2, axis=1))) / 10.0
Pall = [(x[0], x[1]) for x in p1]
closed = False
if SamePoint(p1[0], p1[-1], delta):
Pall = Pall[:-1]
closed = True
vertices = [(j, j + 1) for j in range(0, len(Pall) - 1, 1)]
if closed:
vertices += [(len(Pall) - 1, 0)]
return Pall, vertices
def AddMultipleSegments(*args, **kwargs):
"""
p,v = AddMultipleSegments(*args,**kwargs)
"""
nn = len(args)
p, v = AddSegments(args[0], args[1])
for k in range(2, nn - 1):
p, v = AddSegments(p, args[k])
if kwargs:
if kwargs["closed"]:
p, v = AddSegments(p, args[nn - 1], closed=True)
else:
p, v = AddSegments(p, args[nn - 1])
else:
p, v = AddSegments(p, args[nn - 1])
return p, v
def AddMultipleCurves(*allC):
"""
p,v,ind = AddMultipleCurves(*allC)
"""
N = len(allC)
indi = N * [0]
if N % 2 != 0:
print("Number of Arguments not even")
return False
else:
p, v = AddCurves(allC[0], allC[1], allC[2], allC[3])
indi[0] = 0
indi[1] = len(allC[2])
j = 2
for i in range(4, N, 2):
p, v = AddCurves(p, v, allC[i], allC[i + 1])
indi[j] = indi[j - 1] + len(allC[i])
j += 1 # noqa: SIM113
return p, v, indi
# Connect two different polygons
def AddSegments(P1, P2, closed=False):
"""
p,v = AddSegments(P1,P2,closed=False)
"""
p1 = np.array(P1)
p2 = np.array(P2)
# find smallest distance within points p1 and p2
min1 = np.min(np.sqrt(np.sum((p1[1:] - p1[:-1]) ** 2, axis=1)))
min2 = np.min(np.sqrt(np.sum((p2[1:] - p2[:-1]) ** 2, axis=1)))
delta = np.min([min1, min2]) / 10.0
# Add second curve to first curve
del_first = SamePoint(p1[-1], p2[0], delta)
Pall = P1[:]
if del_first:
Pall += P2[1:]
else:
Pall += P2
# check if Pall is closed
del_last = SamePoint(Pall[-1], p1[0], delta)
if del_last:
Pall = Pall[:-1]
vertices = [(j, j + 1) for j in range(0, len(Pall) - 1, 1)]
if del_last or closed:
vertices += [(len(Pall) - 1, 0)]
return Pall, vertices
# Append Curves
def AddCurves(p1, v1, p2, v2, connect=False, connect_points=None, eps=1e-12):
"""
p,v = AddCurves(p1,v1,p2,v2,connect=False,connect_points=[],eps=1e-12)
"""
if connect_points is None:
connect_points = []
# make one list
p = p1 + p2
v2n = [(v2[j][0] + len(p1), v2[j][1] + len(p1)) for j in range(len(v2))]
v = v1 + v2n
# second segment array may contain
# IDENTICAL points,
# given in connect_points or indicated by the option connect
con_pt = []
if connect:
con_pt = list(p2)
elif len(connect_points) != 0:
con_pt = list(connect_points)
if len(con_pt) != 0:
nodes1 = np.arange(0, len(p1))
nodes2 = np.arange(0, len(p2))
# node numbers of connect points in p1
fn1, dn1 = FindClosestNode(nodes1, p1, con_pt)
# node numbers of connect points in p2
if not connect:
fn2, _dn2 = FindClosestNode(nodes2, p2, con_pt)
else:
fn2 = np.arange(0, len(p2), dtype=int)
_dn = 0.0 * fn2
# node numbers in p
# sort node numbers for delete process
sort_indices = np.argsort(fn2)
dn1 = np.array(dn1)
nodes2 += len(p1)
replace_ind = []
for ii in sort_indices[-1::-1]:
# connect points are identical with points in p1
if dn1[ii] < eps:
print("replace point ", fn2[ii], " --> ", fn1[ii])
replace_ind += [ii]
del p[len(p1) + fn2[ii]]
nodes2[fn2[ii] + 1 :] -= 1
for ii in replace_ind:
nodes2[fn2[ii]] = fn1[ii]
v2n = [(int(nodes2[v2[j][0]]), int(nodes2[v2[j][1]])) for j in range(len(v2))]
v = v1 + v2n
return p, v
# Generate mesh
def DoTriMesh(
points,
vertices,
edge_length=-1,
holes=None,
tri_refine=None,
show=True,
order=None,
writeTo=None,
):
"""
DoTriMesh(points,vertices,edge_length=-1,holes=[],tri_refine=None,show=True,order=None,writeTo=None)
---------
output
mesh_points , mesh_elements , bou_Edges , list_boundary_edges ,
mesh_boundary_elements , inner_curve_segments , list_inner_curve_segments
"""
if holes is None:
holes = []
info = triangle.MeshInfo()
info.set_points(points)
if len(holes) > 0:
info.set_holes(holes)
info.set_facets(vertices)
if tri_refine is not None:
mesh = triangle.build(info, refinement_func=tri_refine, mesh_order=order)
elif edge_length <= 0:
mesh = triangle.build(info, mesh_order=order)
else:
mesh = triangle.build(
info,
max_volume=0.5 * edge_length**2,
mesh_order=order,
generate_faces=True,
)
mesh_points = np.array(mesh.points, dtype=np.double)
mesh_elements = np.array(mesh.elements, dtype=np.int64)
mesh_facets = np.array(mesh.facets, dtype=np.int64)
mesh_neighbors = np.array(mesh.neighbors, dtype=np.int64)
# use generate_face=True in triangle.build for all edges
# mesh_edges = np.array(mesh.faces, dtype=np.int64)
# generate boundary elements
mesh_boundary_elements = []
mesh_boundary_edges = []
for i, x in enumerate(mesh_neighbors):
# find boundary elements
if x[0] == -1 or x[1] == -1 or x[2] == -1:
# print("boundary element: ",x[0],x[1],x[2]," element=",mesh_elements[i])
mesh_boundary_elements += [i]
# find boundary edges
if x[0] == -1:
mesh_boundary_edges += [[mesh_elements[i][1], mesh_elements[i][2]]]
if x[1] == -1:
mesh_boundary_edges += [[mesh_elements[i][0], mesh_elements[i][2]]]
if x[2] == -1:
mesh_boundary_edges += [[mesh_elements[i][0], mesh_elements[i][1]]]
# make edge --> element map
edge_element_map = {}
for i, tt in enumerate(mesh_elements):
e = [0, 0, 0]
e[0] = tuple(np.sort([tt[0], tt[1]]))
e[1] = tuple(np.sort([tt[0], tt[2]]))
e[2] = tuple(np.sort([tt[1], tt[2]]))
for j in [0, 1, 2]:
try:
edge_element_map[e[j]] += [i]
except KeyError:
edge_element_map[e[j]] = [i]
# print("--> map",edge_element_map)
# print("--> Randelemente: ",mesh_boundary_edges)
# print("--> Kurven ",mesh_facets)
# make boundary edges
edges = [tuple(x) for x in mesh_boundary_edges]
bou_Edges, list_bE = SortSegments(edges)
bou_Edges = CheckSegmentSense(mesh_elements, bou_Edges, list_bE)
list_bE += [len(bou_Edges)]
# make edges of inner curves
edges = [
tuple(x) for x in mesh_facets if len(edge_element_map[tuple(np.sort(x))]) == 2
]
Curves, list_Cu = SortSegments(edges)
Curves = CheckSegmentSense(mesh_elements, Curves, list_Cu)
list_Cu += [len(Curves)]
if show:
plt.gca().set_aspect(1)
plt.triplot(mesh_points[:, 0], mesh_points[:, 1], mesh_elements[:, 0:3])
plt.show()
# second order boundary nodes
if order == 2:
# resort indices 4,5,6
for i in range(len(mesh_elements)):
secp = list(mesh_elements[i, 3:]) * 2
mesh_elements[i, 3:] = secp[2:5]
MiddleIndices = []
for i in mesh_boundary_elements:
MiddleIndices += list(mesh_elements[i, 3:])
ps = [0.5 * (mesh_points[x[0], :] + mesh_points[x[1], :]) for x in bou_Edges]
third_node = FindClosestNode(
MiddleIndices, mesh_points, ps, constraint=-1, tree=None
)
bou_Edges = [[x[0], x[1], third_node[0][i]] for i, x in enumerate(bou_Edges)]
err = CheckElementList(mesh_points, mesh_elements)
if err > 0:
print(
"ERROR: Number of wrong sorted mesh_elements ",
err,
"# mesh_elements ",
len(mesh_elements),
)
return False
if writeTo is not None:
np.savez(
writeTo,
mesh_points,
mesh_elements,
bou_Edges,
list_bE,
mesh_boundary_elements,
Curves,
list_Cu,
)
return (
mesh_points,
mesh_elements,
bou_Edges,
list_bE,
mesh_boundary_elements,
Curves,
list_Cu,
)
# return mesh_points,mesh_elements;
def LoadTriMesh(filename, show=True):
"""
LoadTriMesh(filename,show=True)
------------------
output
mesh_points , mesh_elements , bou_Edges , list_boundary_edges ,
mesh_boundary_elements , inner_curve_segments , list_inner_curve_segments
"""
erg = np.load(filename)
poi = erg["arr_0"]
tri = erg["arr_1"]
BouE = erg["arr_2"].tolist()
li_BE = erg["arr_3"].tolist()
bou_elem = erg["arr_4"].tolist()
CuE = erg["arr_5"].tolist()
li_CE = erg["arr_6"].tolist()
erg.close()
if show:
plt.triplot(poi[:, 0], poi[:, 1], tri[:, 0:3])
plt.show()
return poi, tri, BouE, li_BE, bou_elem, CuE, li_CE
#
# Write xml file useful for fenics
#
def WriteXmlMesh(filename, p, t, do=None):
"""
WriteXmlMesh(filename,p,t,do=None)
"""
from xml.etree import ElementTree
# pretty print method
def indent(elem, level=0):
i = "\n" + level * " "
j = "\n" + (level - 1) * " "
if len(elem):
if not elem.text or not elem.text.strip():
elem.text = i + " "
if not elem.tail or not elem.tail.strip():
elem.tail = i
for subelem in elem:
indent(subelem, level + 1)
if not elem.tail or not elem.tail.strip():
elem.tail = j
else:
if level and (not elem.tail or not elem.tail.strip()):
elem.tail = j
return elem
# root element
dolf = ElementTree.Element(
"dolfin",
{
"xmlns:dolfin": (
"https://fenicsproject.org converted from meshpy and meshtools"
)
},
)
# full mesh
mesh = ElementTree.SubElement(dolf, "mesh", {"celltype": "triangle", "dim": "2"})
# coordinates
vert = ElementTree.SubElement(mesh, "vertices", {"size": str(len(p))})
for i, pp in enumerate(p):
_line = ElementTree.SubElement(
vert, "vertex", {"index": str(i), "x": str(pp[0]), "y": str(pp[1])}
)
# elements
cells = ElementTree.SubElement(mesh, "cells", {"size": str(len(t))})
for i, tt in enumerate(t):
_line = ElementTree.SubElement(
cells,
"triangle",
{"index": str(i), "v0": str(tt[0]), "v1": str(tt[1]), "v2": str(tt[2])},
)
# data
_line = ElementTree.SubElement(mesh, "data")
# domains
dom = ElementTree.SubElement(mesh, "domains")
if do is None:
do = len(t) * [0]
msh_vc = ElementTree.SubElement(
dom,
"mesh_value_collection",
{"name": "m", "type": "uint", "dim": "2", "size": "686"},
)
for i, dd in enumerate(do):
_line = ElementTree.SubElement(
msh_vc,
"value",
{"cell_index": str(i), "local_entity": "0", "value": str(dd)},
)
# write to file
tree = ElementTree.ElementTree(indent(dolf))
tree.write(filename, xml_declaration=True, encoding="utf-8")
def CheckElementList(p, t):
if len(t[0]) == 3:
return 0
else:
j = 0
for i in range(len(t)):
diff1 = np.sum(((p[t[i, 0]] + p[t[i, 1]]) / 2.0 - p[t[i, 3]]) ** 2)
diff2 = np.sum(((p[t[i, 1]] + p[t[i, 2]]) / 2.0 - p[t[i, 4]]) ** 2)
diff3 = np.sum(((p[t[i, 2]] + p[t[i, 0]]) / 2.0 - p[t[i, 5]]) ** 2)
if diff1 > 1e-10 or diff2 > 1e-10 or diff3 > 1e-10:
# print("Error in element list (wrong order), ",
# i, t[i], diff1, diff2, diff3)
j += 1
return j
def MakeSecondOrderMesh(p, t, bou):
edges, _b_ed, _b_el = FindEdges(t)
new_p = []
all_ed = []
for X in edges:
new_p += [0.5 * (p[X[0], :] + p[X[1], :])]
all_ed += [set(X)]
print("all edges=", all_ed)
new_t = []
for X in t:
print(X)
e1 = {X[0], X[1]}
e2 = {X[1], X[2]}
e3 = {X[2], X[0]}
i4 = all_ed.index(e1) + len(p)
i5 = all_ed.index(e2) + len(p)
i6 = all_ed.index(e3) + len(p)
new_t += [[X[0], X[1], X[2], i4, i5, i6]]
new_bou = []
for X in bou:
e1 = {X[0], X[1]}
i3 = all_ed.index(e1) + len(p)
new_bou += [[X[0], X[1], i3]]
p_new = np.array(list(p) + new_p)
return p_new, np.array(new_t), np.array(new_bou)
# ############## BEM #################
# points are the vertices of the segments (or elements)
# nodes are the middle position in the segments for zero order
# flags[i]=0 potential of segment i is known
# 1 normal derivative of segment i is known
#
# return middle-points in segment
# half Diff-Vector
# known PHI-segments
# known DERIVATIVE-segments
def MakeBEMBoundary(p, v, func=False, curves=None):
if curves is None:
curves = []
# zero order base function, define nodes on middle position of element
Ns = len(v)
p = np.array(p)
v = np.array(v)
# compute middle points and elemental vectors
mid_points = 0.5 * (p[v[:, 1], :] + p[v[:, 0], :])
diff_points = 0.5 * (p[v[:, 1], :] - p[v[:, 0], :])
mid_points = np.array(mid_points)
# set all nodes as dirichlet nodes
flags = Ns * [0]
# type of boundary condition
# if a function is provided dirichlet and neumann nodes are selected
if func:
flags = func(mid_points)
else:
if curves == []:
print("Provide properties of Nodes according to:")
print("0: Value known")
print("1: Derivative known")
return
else:
flags = curves
# Vec1 dirichlet nodes, Vc2 Neumann nodes
Vec1 = []
Vec2 = []
for i in range(Ns):
# the potential is known for these nodes
if flags[i] == 0:
Vec1 += [i]
# the derivative is known
else:
Vec2 += [i]
return np.array(mid_points), np.array(diff_points), Vec1, Vec2
def ChangeCurveDirection(v):
N = len(v)
out = []
for i in range(N):
out += [(v[N - i - 1][1], v[N - i - 1][0])]
return out
#
# AllBound [[p,v],[p,v],[p,v],[],...]
# Dom [[3,4,2],alpha]
#
def DoBemBoundary(AllBound, show=True, show_numbers=False):
D_seg = [0]
N_seg = [0]
I_seg = [0]
NofC = len(AllBound)
Curves = NofC * [0]
# count number of segemens for each type
for i in range(NofC):
seg = len(AllBound[i][1])
if AllBound[i][2] == 0:
a = D_seg[-1]
D_seg += [a + seg]
elif AllBound[i][2] == 1:
a = N_seg[-1]
N_seg += [a + seg]
else:
a = I_seg[-1]
I_seg += [a + seg]
Curves[i] = {"first": a, "last": a + seg - 1, "type": AllBound[i][2]}
D_nodes = D_seg[-1]
N_nodes = N_seg[-1]
I_nodes = I_seg[-1]
N_seg = [x + D_nodes for x in N_seg]
I_seg = [x + D_nodes + N_nodes for x in I_seg]
NofNodes = D_nodes + N_nodes + I_nodes
VecB = np.zeros((NofNodes, 2))
VecXm = np.zeros((NofNodes, 2))
for i in range(NofC):
# run through all boundaries
p = np.array(AllBound[i][0])
v = AllBound[i][1]
NodeType = AllBound[i][2]
# correct positions in Curves array
if NodeType == 1:
Curves[i]["first"] += D_nodes
Curves[i]["last"] += D_nodes
elif NodeType < 0:
Curves[i]["first"] += D_nodes + N_nodes
Curves[i]["last"] += D_nodes + N_nodes
# Compute middle point and direction vector
for i, s in enumerate(v):
VecB[Curves[i]["first"] + i] = np.array(
[0.5 * (p[s[1], :] - p[s[0], :])]
)
VecXm[Curves[i]["first"] + i] = np.array(
[0.5 * (p[s[1], :] + p[s[0], :])]
)
if show:
PlotBoundary(p, v, "Segments")
if show:
plt.title("Dirichlet (blue), Neumann (green), Inner Curves (red)")
for Z in Curves:
x = VecXm[Z["first"] : Z["last"] + 1][:, 0]
y = VecXm[Z["first"] : Z["last"] + 1][:, 1]
if Z["type"] == 0:
plt.plot(x, y, "ob", markersize=8)
elif Z["type"] == 1:
plt.plot(x, y, "sg", markersize=8)
else:
plt.plot(x, y, "vr", markersize=6)
if show_numbers:
for i in range(len(x)):
buf = Z["first"] + i
plt.text(x[i], y[i], f" {buf}", color="k", fontsize=13)
plt.show()
return VecXm, VecB, Curves, D_nodes, N_nodes, I_nodes
#
# Make a curve list with all properties
#
def DoBemDomain(AllDom, CC):
Curves = len(CC) * [0]
for i in range(len(CC)):
Curves[i] = {
"first": CC[i]["first"],
"last": CC[i]["last"],
"type": CC[i]["type"],
}
for DomNumb, GG in enumerate(AllDom):
for thisC in GG[0]:
i = abs(thisC)
# Add domain number and their alphas to curves list
if "dom" in Curves[i]:
if DomNumb > Curves[i]["dom"]:
Curves[i]["dom"] = (Curves[i]["dom"], DomNumb)
Curves[i]["alpha"] = (Curves[i]["alpha"], GG[1])
else:
Curves[i]["dom"] = (DomNumb, Curves[i]["dom"])
Curves[i]["alpha"] = (GG[1], Curves[i]["alpha"])
else:
Curves[i]["dom"] = DomNumb
Curves[i]["alpha"] = GG[1]
return Curves
# G=[[CurveIndex1,CurveIndex2,..],alpha]
# negative Curveindex changes direction of curve --> newB on return
# Curves is changed on return
#
def MakeDomain(DomNumb, G, CC, VecB):
D_nodes = []
N_nodes = []
I_nodes = []
newB = np.copy(VecB)
for thisC in G[0]:
i = abs(thisC)
start = CC[i]["first"]
stop = CC[i]["last"] + 1
nodes = range(start, stop)
# change curve direction
if thisC < 0:
newB[nodes] *= -1.0
if CC[i]["type"] == 0:
D_nodes += nodes
elif CC[i]["type"] == 1:
N_nodes += nodes
else:
I_nodes += nodes
all_nodes = D_nodes + N_nodes + I_nodes
lND = len(D_nodes)
lNN = len(N_nodes)
lNI = len(I_nodes)
return all_nodes, newB, lND, lNN, lNI
================================================
FILE: examples/ka-6d.ply
================================================
ply
format ascii 1.0
comment VCGLIB generated
element vertex 911
property float x
property float y
property float z
element face 1818
property list uchar int vertex_indices
end_header
-2.53779 -0.874807 0.361701
3.01063 -7.86294 0.395231
2.67315 8.00454 0.335959
2.29768 -7.86514 0.335959
2.46055 7.94854 0.335959
2.29768 7.86514 0.335959
2.17099 7.72964 0.335959
3.76896 -8.07564 0.335959
3.01063 7.86294 0.395231
3.76896 8.07564 0.335959
-0.56141 -1.93006 0.583304
-0.56141 1.93006 0.583304
-3.08403 0.942577 -0.26911
-3.08403 -0.867417 -0.236018
-3.08403 0.726857 -0.842377
-3.08403 0.818137 -0.239456
-3.08403 -0.726857 -0.842377
-3.08403 0.539307 -0.877237
-3.08403 -0.488497 -0.82562
-3.08403 -0.539307 -0.877237
-3.91989 0.426937 -0.785548
-3.93935 0.381326 -0.858948
-1.50178 -1.08824 -0.0181971
-1.23019 -1.12028 -0.168275
-1.2265 -1.09368 -0.0785382
-1.88591 1.03814 0.0786738
-2.44579 -1.04084 0.101075
9.28188 0 0.723383
9.25129 -0.133871 0.825321
9.25129 0.133871 0.825321
10.5133 0 0.97667
9.91637 0 0.850027
8.5531 0 3.48991
9.78764 0 3.5763
10.5194 0 1.16979
9.2433 -0.15548 0.91767
8.93949 0 3.55645
9.91273 -0.0934859 0.914697
9.91273 0.0934859 0.914697
8.87979 -3.05792 0.881246
8.75593 2.8863 0.923328
8.87979 3.05792 0.881246
9.79687 3.12625 0.881246
9.31763 -3.11498 0.881246
9.21955 -2.87834 0.918589
8.75593 -2.8863 0.923328
8.50119 2.8818 0.881246
8.66311 2.99346 0.881246
9.31763 3.11498 0.881246
8.66311 -2.99346 0.881246
9.51341 -2.88327 0.861804
9.51341 2.88327 0.861804
8.52379 -0.253479 0.952525
8.70712 0.225046 0.948112
8.92558 -0.197528 0.916288
8.92558 0.197528 0.916288
9.02651 0.186269 0.901245
8.53529 0.141349 1.18051
8.97217 0.14365 1.12042
7.30808 0.394589 0.929712
5.97209 0 1.33584
8.14849 -0.142818 1.19945
8.14849 0.142818 1.19945
9.15904 0.163526 0.881246
9.02651 -0.186269 0.901245
8.30491 0 3.34438
8.65189 0.141963 1.16447
8.53529 -0.248313 0.800641
8.68454 -0.226757 0.805984
9.21955 2.87834 0.85561
8.09098 0.305153 0.784654
7.53821 0.375496 0.80012
7.37388 -0.155224 1.19576
8.07831 0 3.09795
7.37388 0.155224 1.19576
9.51341 -2.88069 0.904149
9.51341 2.88069 0.904149
9.15904 -0.163526 0.881246
9.2433 0.15548 0.91767
-4.49171 -0.253801 0.647911
-6.11797 -0.111917 -0.391649
-6.11797 0.111917 -0.391649
-6.11797 0.0906809 -0.450887
-6.11797 -0.0494573 -0.511803
-6.11797 -0.0906809 -0.450887
-6.11797 0.0494573 -0.511803
-6.15074 0 -0.391649
-6.11797 -0.0904068 -0.291292
-6.11797 0.0904068 -0.291292
-6.11797 0.115235 -0.336436
-6.11797 -0.0452043 -0.248868
-4.03504 0.817169 0.14235
-2.42461 -0.906714 0.249255
-2.35673 0.868388 0.193357
-3.0811 -0.860457 0.0367379
-2.27809 -0.898125 0.147157
-2.42461 0.906714 0.249255
-4.03504 -0.817169 0.14235
-4.03512 0 -1.10113
-3.32169 -0.851618 0.348456
-3.32169 0.851618 0.348456
-2.6396 0.888605 0.316609
2.9631 -1.20617 -0.277182
2.6455 -1.30984 -0.16392
2.96423 -1.30258 -0.217357
2.9631 1.20617 -0.277182
2.96379 -1.34661 0.0301485
2.96379 1.34661 0.0301485
-2.27809 0.898125 0.147157
-2.19381 0.96254 0.158235
-2.53779 0.874807 0.361701
-2.49604 -0.895698 0.31593
-2.49604 0.895698 0.31593
-2.43952 0.916785 0.313112
1.95934 -0.874617 0.301705
-2.19381 -0.988717 0.297674
-2.19239 0.909893 0.109426
-1.88591 -1.03814 0.0786738
-1.88591 -1.05611 0.108779
-1.88591 1.05611 0.108779
0.963972 -0.957314 -0.274434
1.87193 0.953306 -0.272165
2.96382 -1.31049 0.0302124
2.96388 1.31313 -0.0368536
2.96382 1.31049 0.0302124
2.96374 1.28788 0.097497
2.96433 -1.29998 -0.100741
2.96433 1.29998 -0.100741
2.96404 1.24583 0.157554
2.96416 -1.24882 -0.191441
2.96324 -1.18716 -0.238559
2.96404 -1.24583 0.157554
2.96402 1.19128 0.203991
2.96324 1.18716 -0.238559
2.96402 -1.19128 0.203991
2.96483 -1.12241 0.226673
2.96323 -1.06586 -0.270446
2.96455 0.98315 0.2112
2.96455 -0.98315 0.2112
2.96442 0.90263 0.178142
2.96417 -0.857458 -0.174201
2.96421 -0.849759 0.125371
2.96394 0.817621 0.0490788
2.96364 -0.804482 -0.0351429
2.96364 0.804482 -0.0351429
2.96331 0.822326 -0.126532
-3.91414 -0.369186 1.16736
-3.91414 0 1.20677
-4.49712 0 0.685333
-3.91414 -0.591165 0.963749
-3.91414 0.591165 0.963749
-4.49171 0.253801 0.647911
-3.91414 -0.710333 0.668169
-3.91414 0.710333 0.668169
-3.57222 0.340054 1.30461
-3.91414 0.369186 1.16736
-2.68359 0 1.53871
-2.68359 0.554534 1.45808
-3.57222 -0.340054 1.30461
-3.57222 0 1.36881
-3.57222 -0.692627 1.00656
-2.68359 -0.80689 1.11743
-3.57222 -0.799024 0.724561
-3.57222 0.799024 0.724561
-3.57222 0.692627 1.00656
-1.98902 -0.722589 0.84449
-1.96102 -0.63848 1.13515
-1.96102 0.63848 1.13515
-1.96102 -0.466708 1.33675
-2.68359 0.916741 0.835233
-2.68359 0.80689 1.11743
-2.68359 -0.554534 1.45808
-1.96102 -0.329322 1.46632
-1.96102 0.329322 1.46632
-1.47446 0 1.51316
-1.96102 0 1.5499
-1.96102 0.466708 1.33675
-1.47446 0.565639 1.19455
-1.47446 -0.565639 1.19455
0.100958 -0.381001 1.16778
0.100958 0.381001 1.16778
0.0879 -0.250115 1.29799
0.0879 -0.360093 1.26479
0.0879 0.360093 1.26479
-1.47446 -0.447615 1.36681
-1.47446 0.447615 1.36681
0.0879 0.250115 1.29799
0.0879 -0.10998 1.3312
0.0879 0 1.34291
-1.47446 -0.246112 1.47606
-1.47446 0.246112 1.47606
0.63233 0 1.27673
0.0879 0.10998 1.3312
-4.78307 0.300902 0.583626
-4.78307 0 0.607631
-1.50363 -0.615787 1.04211
-1.98902 0.722589 0.84449
-2.29344 -0.783536 0.738058
-2.68359 -0.916741 0.835233
-4.4938 -0.451681 0.543677
-2.29344 0.783536 0.738058
-2.51432 -0.81237 0.67786
-2.69988 -0.844339 0.612684
-2.91595 0.825475 0.56758
-3.12388 -0.823567 0.511745
-2.91595 -0.825475 0.56758
-3.12388 0.823567 0.511745
-2.69988 0.844339 0.612684
-3.604 -0.76899 0.455014
-3.604 0.76899 0.455014
-3.34282 -0.800442 0.493194
-3.34282 0.800442 0.493194
5.18718 0.629555 0.24098
3.01955 0.764104 0.18742
5.18718 0.658397 0.64044
2.66615 -0.816364 0.333075
3.01955 -0.81037 0.5101
2.66615 -0.839496 0.494415
1.81272 -0.768054 0.78098
3.01955 0.728122 0.86486
2.66615 0.839496 0.494415
-3.93935 -0.381326 -0.858948
-4.03504 0.269547 -1.01235
-3.67274 -0.821997 -0.0548832
-3.32169 -0.898629 0.193905
-3.67274 0.821997 -0.0548832
-3.48098 -0.834463 -0.0251865
-3.32169 0.898629 0.193905
-3.28576 -0.335787 -1.00295
-3.49041 0.424467 -1.10943
-3.28576 0.335787 -1.00295
-3.49041 0.337427 -0.999098
-3.0811 0.424987 -1.12463
-2.39848 0.191842 -1.00067
1.60594 -0.172251 -0.568153
1.60594 0 -0.56656
-1.50574 -0.335548 -0.948802
-1.23019 -0.419587 -1.05115
-2.44579 -0.425677 -1.07659
-1.51335 -0.198093 -0.923457
-1.95655 -0.337729 -0.974066
-1.51335 0.198093 -0.923457
1.36581 0.635897 -0.653076
3.01955 0.594801 -0.24516
3.01955 -0.594801 -0.24516
5.18718 -0.45832 0.0210003
5.18718 0.45832 0.0210003
3.01955 0.471764 -0.35506
-0.473587 -0.189479 -0.805022
-1.50574 0.335548 -0.948802
-0.446537 0.333487 -0.848283
-0.446537 -0.333487 -0.848283
0.121885 -0.410137 -0.875948
0.121885 0.333247 -0.781573
0.566176 -0.180865 -0.686588
-0.473587 0.189479 -0.805022
0.121885 -0.333247 -0.781573
1.60594 0.344503 -0.569746
-3.91989 -0.426937 -0.785548
-3.07266 0.34084 -1.00068
-3.07266 -0.34084 -1.00068
-3.49041 -0.337427 -0.999098
-3.67274 -0.337117 -0.973128
-3.32169 0 -1.068
-3.67274 0.337117 -0.973128
-2.19239 -0.909893 0.109426
-2.12693 0.932102 0.0886456
-2.43421 -0.90903 0.0719294
-2.12693 -0.932102 0.0886456
-1.00616 -1.09437 -0.119192
-1.23019 1.12028 -0.168275
-1.2265 1.09368 -0.0785382
-1.00616 1.09437 -0.119192
-1.00755 -1.17122 -0.107995
-0.813925 -1.09135 -0.150126
-0.813925 1.09135 -0.150126
1.35571 1.1255 -0.311557
0.823181 -1.03949 -0.268335
1.35571 -1.1255 -0.311557
0.823181 1.03949 -0.268335
0.29065 -1.11805 -0.264808
0.822889 -1.00331 -0.257668
0.29065 1.11805 -0.264808
0.822889 1.00331 -0.257668
3.01955 -0.717839 -0.13526
3.01955 0.717839 -0.13526
1.35924 -0.901274 -0.29769
1.36581 0.823317 -0.523635
1.35924 0.901274 -0.29769
2.33557 -0.793406 -0.202174
1.88678 -0.84299 -0.24608
1.36581 -0.823317 -0.523635
1.88678 0.84299 -0.24608
2.29902 0.945676 0.290251
2.31274 -0.84549 0.31739
2.31274 -0.868623 0.47873
2.31274 0.868623 0.47873
1.60594 0.926876 0.44736
1.95934 -0.897749 0.463045
1.60594 -0.926876 0.44736
1.95934 -0.889281 0.403982
2.31274 0.84549 0.31739
1.95934 0.889281 0.403982
1.95934 0.897749 0.463045
-1.03494 -2.0487 0.396483
-1.03494 2.0487 0.396483
-2.41752 -0.865714 0.3971
-2.41752 0.865714 0.3971
-2.51432 0.81237 0.67786
-0.542631 1.93637 0.205763
-0.50905 1.29113 0.162409
1.34258 -1.35746 0.216253
1.34258 1.35746 0.216253
2.433 -0.951711 0.299235
2.433 0.951711 0.299235
2.41459 -0.837096 0.321911
2.41459 0.837096 0.321911
2.6455 -0.93418 0.280565
2.64128 1.13568 0.267791
-1.51335 -0.81079 0.836
-1.51335 0.858696 0.629739
-1.51335 -0.858696 0.629739
2.29902 -0.945676 0.290251
2.64183 1.40401 0.254034
1.95934 0.874617 0.301705
0.885366 0.911126 0.489491
2.6455 0.81442 -0.19224
1.60594 0.172251 -0.568153
-0.473587 0 -0.793893
0.566177 0 -0.680226
0.566176 0.180865 -0.686588
-1.51335 0 -0.90756
3.01955 -0.471764 -0.35506
2.96423 1.30258 -0.217357
2.96416 1.24882 -0.191441
2.96338 1.31525 -0.199665
2.96338 -1.31525 -0.199665
2.96444 1.34581 -0.126228
2.96444 -1.34581 -0.126228
2.9635 -1.2679 -0.159422
2.9635 1.2679 -0.159422
2.96388 -1.31313 -0.0368536
2.96371 -1.31679 0.104152
2.96374 -1.28788 0.097497
2.96371 1.31679 0.104152
2.96403 1.20379 0.215485
2.96499 -1.13168 0.245838
2.96483 1.12241 0.226673
2.96403 -1.20379 0.215485
3.01955 -0.787237 0.34876
2.96442 -0.90263 0.178142
3.01955 0.787237 0.34876
3.01955 -0.740972 0.0260804
2.96421 0.849759 0.125371
3.01955 -0.729405 -0.0545896
2.96394 -0.817621 0.0490788
2.96331 -0.822326 -0.126532
2.96426 0.802753 -0.217263
2.96426 -0.802753 -0.217263
2.9631 -0.929855 -0.277182
2.96323 0.942954 -0.241584
2.96417 0.857458 -0.174201
2.9631 0.929855 -0.277182
2.96323 -0.942954 -0.241584
2.96323 1.06586 -0.270446
1.87193 -0.953306 -0.272165
-2.19381 0.977533 0.227955
-1.88591 1.07823 0.194296
-2.19381 -0.96254 0.158235
-1.45061 1.21842 0.248379
-1.88591 1.09472 0.279813
-1.45061 1.19572 0.144175
-1.88591 -1.07823 0.194296
-1.11537 1.26941 0.144778
-1.00755 1.29082 0.100248
-1.45061 -1.19572 0.144175
-1.45061 -1.1653 0.0399715
-1.50178 1.08824 -0.0181971
-1.45061 1.1653 0.0399715
-1.00755 -1.29082 0.100248
-1.11537 -1.26941 0.144778
-0.44008 -1.36911 0.0597318
-0.44008 1.36911 0.0597318
-1.00755 -1.25367 -0.0199814
-0.44008 -1.23168 -0.174109
-1.00755 1.25367 -0.0199814
-0.44008 1.32642 -0.0752764
-1.00755 1.17122 -0.107995
-0.44008 1.23168 -0.174109
0.29065 -1.43061 0.0216282
-0.50905 -1.29113 0.162409
0.29065 -1.27917 -0.226433
0.29065 -1.38357 -0.12159
-0.44008 -1.32642 -0.0752764
0.29065 1.27917 -0.226433
-0.44008 -1.08547 -0.210284
1.35571 -1.48057 0.132876
0.29065 1.43061 0.0216282
0.29065 1.38357 -0.12159
1.35571 1.29006 -0.271862
1.87193 -1.26946 -0.272165
1.87193 1.11138 -0.311457
1.35571 -0.960936 -0.271862
1.35571 0.960936 -0.271862
1.35571 -1.39669 -0.163413
1.35571 -1.29006 -0.271862
1.87193 -1.37189 -0.164818
1.35571 -1.44474 -0.0152684
1.87193 1.41804 -0.0181797
1.35571 1.44474 -0.0152684
1.35571 1.39669 -0.163413
1.87193 -1.41804 -0.0181797
1.87193 -1.45247 0.128459
1.35571 1.48057 0.132876
1.87193 1.45247 0.128459
2.29902 -1.09727 -0.311357
1.87193 -1.11138 -0.311457
2.29902 -1.34709 -0.161763
1.87193 1.37189 -0.164818
1.87193 1.26946 -0.272165
2.29902 1.39135 -0.0121696
2.29902 -1.39135 -0.0121696
1.82982 1.37492 0.230421
2.29902 -1.24886 -0.271274
2.29902 1.24886 -0.271274
2.29902 -0.945676 -0.271274
2.29902 0.945676 -0.271274
2.29902 1.09727 -0.311357
2.6455 -1.07605 -0.311257
2.29902 1.34709 -0.161763
2.6455 1.30984 -0.16392
2.33557 0.793406 -0.202174
2.6455 1.35126 -0.0165825
2.2552 -1.39016 0.242791
2.29902 -1.42437 0.137425
2.29902 1.42437 0.137425
2.6455 -1.21792 -0.271779
2.6455 1.21792 -0.271779
2.9631 -1.06801 -0.311157
2.9631 1.06801 -0.311157
2.6455 -0.93418 -0.271779
2.6455 0.93418 -0.271779
2.6455 1.07605 -0.311257
2.6455 -0.81442 -0.19224
2.96478 0.954101 0.280222
2.66615 0.816364 0.333075
2.96478 -0.954101 0.280222
2.6455 0.93418 0.280565
2.96499 1.13168 0.245838
2.6455 -1.38216 0.130755
2.96406 1.26814 0.166919
2.6455 1.38216 0.130755
2.96406 -1.26814 0.166919
-2.35673 -0.868388 0.193357
-2.19381 -0.977533 0.227955
-2.43952 -0.916785 0.313112
-2.19381 0.988717 0.297674
2.64128 -1.13568 0.267791
2.96387 1.35662 -0.0478767
2.96387 -1.35662 -0.0478767
2.6455 -1.35126 -0.0165825
-2.6396 -0.888605 0.316609
-4.03504 -0.269547 -1.01235
-4.48334 0.674709 0.409919
-4.03504 -0.553424 -0.586608
-4.03504 0.553424 -0.586608
-4.03504 0.775563 -0.233459
-4.48334 0.770916 0.122363
-4.03504 -0.775563 -0.233459
-6.07414 -0.149952 -0.248702
-6.07414 0 -0.164319
-6.11797 0 -0.234529
-6.11797 0.0452043 -0.248868
-6.07414 0.0749767 -0.185635
-6.11797 -0.115235 -0.336436
-6.11797 0 -0.532746
-6.07414 0.150406 -0.485948
-6.07414 -0.150406 -0.485948
-6.07414 -0.191133 -0.31581
-6.07414 -0.185629 -0.397887
-6.07414 0.149952 -0.248702
-6.01159 0 -0.0660269
-6.07414 -0.0749767 -0.185635
-6.01159 0.10406 -0.0954448
-6.01159 -0.208118 -0.182482
-6.01159 -0.11385 -0.634879
-6.07414 0 -0.607636
-6.07414 -0.0820309 -0.576503
-6.01159 0.11385 -0.634879
-6.07414 0.0820309 -0.576503
-6.01159 -0.257635 -0.388372
-6.07414 0.185629 -0.397887
-6.01159 -0.208749 -0.509904
-6.01159 0.208749 -0.509904
-6.07414 0.191133 -0.31581
-6.01159 0.257635 -0.388372
-6.01159 -0.10406 -0.0954448
-5.91159 0.134745 0.00298204
-5.91159 -0.269489 -0.111356
-5.91159 0 0.0416271
-5.91159 -0.343498 -0.233021
-6.01159 0.208118 -0.182482
-6.01159 0 -0.677845
-5.91159 0.270306 -0.541478
-5.91159 -0.270306 -0.541478
-6.01159 0.265273 -0.275098
-6.01159 -0.265273 -0.275098
-4.48342 0 -1.10112
-4.78307 0 -1.09561
-4.48334 -0.254292 -1.01303
-4.78307 0.23337 -0.969202
-5.62946 -0.39091 0.0613448
-5.91159 0.269489 -0.111356
-5.34733 -0.57329 -0.027614
-5.62946 -0.189149 -0.833373
-5.62946 0 -0.918349
-5.91159 0 -0.762096
-5.34733 -0.207109 -0.895494
-5.34733 0.207109 -0.895494
-5.34733 0.415262 -0.594025
-5.62946 -0.364501 -0.586208
-5.91159 -0.147423 -0.705653
-5.91159 0.147423 -0.705653
-5.62946 0.364501 -0.586208
-5.62946 0.189149 -0.833373
-5.34733 -0.542854 -0.300859
-5.91159 0.333608 -0.381826
-5.91159 -0.333608 -0.381826
-5.62946 -0.485901 -0.121823
-5.62946 -0.464973 -0.345849
-5.62946 0.464973 -0.345849
-5.91159 0.343498 -0.233021
-5.62946 0.485901 -0.121823
-5.34733 0.542854 -0.300859
-5.34733 0.57329 -0.027614
-5.62946 0.195456 0.207999
-5.91159 -0.134745 0.00298204
-5.62946 0.39091 0.0613448
-5.34733 0 0.397423
-5.62946 -0.195456 0.207999
-5.62946 0 0.249735
-4.78307 0.601801 0.36173
-4.78307 -0.23337 -0.969202
-4.48334 0.254292 -1.01303
-4.48334 0.522097 -0.590555
-4.48334 -0.522097 -0.590555
-4.48334 0.731661 -0.218984
-4.48334 -0.770916 0.122363
-4.78307 -0.719087 0.089259
-4.48334 -0.731661 -0.218984
-5.34733 -0.46925 0.195797
-5.34733 0.46925 0.195797
-5.34733 0 -0.99914
-5.34733 -0.415262 -0.594025
-4.78307 -0.496539 -0.601533
-4.78307 0.671464 -0.243989
-4.78307 0.496539 -0.601533
-4.78307 -0.671464 -0.243989
-4.78307 0.719087 0.089259
-4.78307 -0.300902 0.583626
-4.78307 -0.601801 0.36173
-5.34733 0.234626 0.357559
-5.34733 -0.234626 0.357559
3.01955 -0.728122 0.86486
3.01955 0.391445 1.18532
1.60594 -0.47821 1.13988
1.60594 0.47821 1.13988
3.01955 -0.391445 1.18532
1.60594 0 1.26191
8.03896 0.309997 0.965108
8.03896 -0.309997 0.965108
8.53529 0.247516 1.06766
6.91733 0 1.60978
6.95017 -0.442124 0.86337
8.50119 -2.8818 0.881246
8.68454 0.226757 0.805984
8.53529 0.248313 0.800641
7.27895 -0.40552 0.811791
8.75593 -2.8863 0.858114
8.75593 2.8863 0.858114
7.27895 0.40552 0.811791
9.79687 -3.12625 0.881246
6.7843 0 1.48736
7.01477 0.100896 1.19651
6.59662 0 1.44692
6.28373 -0.0373285 1.19785
6.12561 0 1.4002
9.21955 -2.87834 0.85561
9.21955 2.87834 0.918589
8.97217 -0.14365 1.12042
8.65189 -0.141963 1.16447
8.70712 -0.225046 0.948112
8.52379 0.253479 0.952525
5.18718 0.595628 0.97187
5.18718 0 1.2063
3.01955 0 1.23766
5.18718 0 -0.0911597
3.01955 0 -0.40468
3.01955 0.81037 0.5101
8.53529 0 0.517751
8.53529 -0.237978 0.704231
8.53529 0.237978 0.704231
-3.08403 -0.881047 -0.670306
-3.08403 0.881047 -0.670306
-3.08403 -0.960727 -0.450042
-3.85508 0.961347 -0.450154
-3.85508 0.881677 -0.670397
-3.85508 -0.818777 -0.239589
-3.08403 -0.818137 -0.239456
-3.85508 0.818777 -0.239589
-3.85508 -0.774657 -0.260111
-3.08403 -0.475487 -0.74701
-3.08403 -0.774017 -0.25998
-3.08403 0.774017 -0.25998
-3.85508 0.476157 -0.747092
-3.85508 -0.476157 -0.747092
-3.08403 0.475487 -0.74701
-3.08403 0.488497 -0.82562
-3.85508 -0.489167 -0.825694
-3.85508 0.539977 -0.877307
-3.85508 -0.539977 -0.877307
-3.85508 0.727507 -0.84245
-3.85508 -0.600587 -0.904297
-3.08403 -0.599927 -0.904231
-3.08403 0.599927 -0.904231
-3.85508 0.600587 -0.904297
-1.23019 -1.13183 -0.353969
-2.44579 -1.14589 -0.396956
-2.44579 1.00206 -0.898507
-3.09459 -1.0016 -0.862829
-2.44579 1.14589 -0.396956
-2.44579 -1.14961 -0.186595
-3.90881 0.724167 -0.898314
-3.85508 -0.881677 -0.670397
-3.92412 0.906487 -0.69485
-3.92412 -0.906487 -0.69485
-3.85508 -0.961347 -0.450154
-3.9491 -1.00852 -0.315093
-3.9491 1.00852 -0.315093
-3.85508 0.943197 -0.26924
-3.95849 0.890367 -0.18133
-3.85508 -0.868047 -0.236151
-3.85508 0.868047 -0.236151
-3.95849 -0.832097 -0.185396
-3.95849 0.832097 -0.185396
-3.85508 0.774657 -0.260111
-3.95741 -0.776827 -0.213361
-3.95741 0.776827 -0.213361
-3.85508 0.489167 -0.825694
-3.9128 -0.442317 -0.8785
-3.90116 -0.574077 -0.971452
-3.85508 -0.727507 -0.84245
-3.85889 -0.969457 -0.343513
-3.85889 0.969457 -0.343513
-3.94279 1.0007 -0.434401
-3.85518 -1.03706 -0.15055
-3.95511 -0.979237 -0.22046
-3.95511 0.979237 -0.22046
-3.95849 -0.890367 -0.18133
-3.85518 0.920717 -0.0322682
-3.85518 -0.920717 -0.0322682
-3.85518 0.971987 -0.0510992
-3.85518 -0.802107 -0.101832
-3.85518 0.802107 -0.101832
-3.90428 -0.502407 -0.939534
-3.9128 0.442317 -0.8785
-3.85518 -0.555667 -1.0408
-3.85518 0.555667 -1.0408
-3.90428 0.502407 -0.939534
-3.85518 0.707167 -1.00036
-3.90116 0.574077 -0.971452
-3.90881 -0.724167 -0.898314
-3.85518 1.06134 -0.425472
-3.85518 -1.06134 -0.425472
-3.94279 -1.0007 -0.434401
-3.85518 1.03706 -0.15055
-3.67274 1.1045 -0.2514
-3.85518 -0.971987 -0.0510992
-3.67274 -0.945637 0.0227438
-3.85518 0.417187 -1.0176
-3.85518 -0.417187 -1.0176
-3.67274 -0.420797 -1.0768
-3.67274 0.565137 -1.10269
-3.85518 -0.707167 -1.00036
-3.67274 -0.723047 -1.05757
-3.67274 1.11345 -0.416037
-3.85518 0.933697 -0.772988
-3.85518 -0.933697 -0.772988
-3.85518 -1.07314 -0.277937
-3.85518 1.07314 -0.277937
-3.49041 1.13555 -0.231067
-3.67274 1.0669 -0.109248
-3.67274 -1.0669 -0.109248
-3.49041 -1.09644 -0.0797953
-3.67274 0.999067 0.00172983
-3.67274 -0.999067 0.00172983
-3.49041 -0.970327 0.0606648
-3.49041 0.970327 0.0606648
-3.48098 0.834463 -0.0251865
-3.67274 0.945637 0.0227438
-3.67274 0.420797 -1.0768
-3.67274 -0.565137 -1.10269
-3.49041 -0.424467 -1.10943
-3.49041 -0.574587 -1.13697
-3.49041 0.574587 -1.13697
-3.67274 -0.959167 -0.814027
-3.49041 -0.738827 -1.08895
-3.49041 0.984397 -0.836782
-3.67274 0.723047 -1.05757
-3.67274 0.959167 -0.814027
-3.67274 -1.11345 -0.416037
-3.67274 -1.1045 -0.2514
-3.0811 -1.1671 -0.186595
-3.49041 1.09644 -0.0797953
-3.0811 1.12628 -0.0250462
-3.0811 -1.12628 -0.0250462
-3.49041 -1.0259 0.0383028
-3.0811 -1.05266 0.101075
-3.49041 1.0259 0.0383028
-3.0811 -0.424987 -1.12463
-3.0811 0.581657 -1.15405
-3.49041 0.738827 -1.08895
-3.0811 0.753057 -1.10276
-3.0811 -0.753057 -1.10276
-3.49041 -1.14486 -0.406266
-3.49041 -0.984397 -0.836782
-3.09459 1.0016 -0.862829
-3.0811 1.16649 -0.399181
-3.49041 1.14486 -0.406266
-3.49041 -1.13555 -0.231067
-3.0811 -1.16649 -0.399181
-3.0811 1.1671 -0.186595
-2.44579 1.14961 -0.186595
-2.44579 -1.11081 -0.0250461
-2.44579 1.11081 -0.0250461
-3.0811 1.05266 0.101075
-3.0811 -0.994657 0.124957
-2.44579 0.985707 0.124957
-2.44579 1.04084 0.101075
-2.44579 -0.985707 0.124957
-3.0811 0.994657 0.124957
-3.0811 0.860457 0.0367379
-2.43421 0.90903 0.0719294
-3.0811 -0.581657 -1.15405
-2.44579 0.756067 -1.10276
-2.44579 0.583457 -1.15405
-1.23019 0.567517 -1.07884
-1.23019 0.419587 -1.05115
-2.44579 -0.583457 -1.15405
-1.23019 -0.567517 -1.07884
-2.44579 -0.756067 -1.10276
-1.23019 0.729347 -1.03057
-2.44579 -1.00206 -0.898507
-1.23019 0.973707 -0.838325
0.121885 -0.542747 -0.899513
0.121885 0.410137 -0.875948
-1.23019 -0.729347 -1.03057
-1.23019 -0.973707 -0.838325
0.121885 -0.906867 -0.694829
0.121885 0.687817 -0.858436
0.121885 0.542747 -0.899513
0.121885 -0.687817 -0.858436
1.36581 -0.511777 -0.685575
1.36581 0.511777 -0.685575
1.36581 -0.635897 -0.653076
1.60594 -0.344503 -0.569746
-3.08403 -0.967337 -0.349142
-3.08403 0.960727 -0.450042
-3.08403 0.967337 -0.349142
-3.08403 -0.942577 -0.26911
-3.08403 0.867417 -0.236018
-3.85508 -0.943197 -0.26924
0.963972 0.957314 -0.274434
-1.88591 -1.09472 0.279813
-1.45061 -1.21842 0.248379
-4.4938 0.451681 0.543677
-4.48334 -0.674709 0.409919
-3.97321 -0.700374 0.451518
-3.97321 0.700374 0.451518
-4.47329 -0.582013 0.479882
-4.47329 0.582013 0.479882
8.53529 -0.247516 1.06766
8.53529 -0.141349 1.18051
-0.861793 0.521872 1.09238
1.06526 -4.97046 0.270861
3.09835 -8.05894 0.335959
1.06526 4.97046 0.270861
2.67315 -8.00454 0.335959
3.09835 8.05894 0.335959
2.46055 -7.94854 0.335959
2.17099 -7.72964 0.335959
0.81418 -4.833 0.270861
0.81418 4.833 0.270861
-0.542631 -1.93637 0.205763
0.399974 -1.64691 0.211008
1.81272 0.768054 0.78098
0.887511 0.869861 0.576801
0.887511 -0.869861 0.576801
0.885366 -0.911126 0.489491
1.82982 -1.37492 0.230421
0.399974 1.64691 0.211008
2.2552 1.39016 0.242791
2.64183 -1.40401 0.254034
0.26164 -1.31874 0.18482
0.26164 1.31874 0.18482
-0.0713282 1.79164 0.208385
-0.0713282 -1.79164 0.208385
0.253484 1.80328 0.217831
0.253484 -1.80328 0.217831
1.0496 -1.67019 0.229899
1.0496 1.67019 0.229899
1.61316 -5.006 0.270861
1.61316 5.006 0.270861
-1.23019 1.13183 -0.353969
5.18718 -0.595628 0.97187
6.06868 -0.518876 0.91762
7.30808 -0.394589 0.929712
6.24013 -0.48615 1.00199
6.06868 0.518876 0.91762
6.24013 0.48615 1.00199
7.29308 0.376672 1.03212
7.29308 -0.376672 1.03212
5.18718 -0.658397 0.64044
6.23307 -0.531959 0.726115
7.53821 -0.375496 0.80012
6.75601 -0.468739 0.768953
6.23307 0.531959 0.726115
7.16202 0.418556 0.755385
6.95017 0.442124 0.86337
6.75601 0.468739 0.768953
6.63908 -0.467354 0.512817
6.86123 0.433766 0.472606
6.63908 0.467354 0.512817
8.53529 -0.127758 0.539191
6.86123 -0.378657 0.390086
8.53529 0.127758 0.539191
7.16202 -0.418556 0.755385
8.09098 -0.305153 0.784654
5.68089 -0.576805 0.369581
5.68089 0.576805 0.369581
6.02421 -0.504106 0.315533
6.02421 0.504106 0.315533
6.02421 0.531661 0.356793
6.02421 -0.531661 0.356793
6.86123 -0.433766 0.472606
6.86123 0.378657 0.390086
6.1746 -0.524055 0.498182
6.1746 0.524055 0.498182
4.10336 0.685264 0.13353
4.10336 -0.685264 0.13353
3.01955 -0.764104 0.18742
4.10336 -0.673697 0.05286
3.01955 0.729405 -0.0545896
3.01955 0.740972 0.0260804
5.18718 -0.629555 0.24098
4.10336 0.673697 0.05286
0.121885 -1.04862 -0.282625
0.350255 1.03029 -0.267193
0.120963 -1.04295 -0.240179
0.349793 -1.02745 -0.245971
0.349793 1.02745 -0.245971
0.464209 1.0197 -0.248866
-0.216134 1.06942 -0.300461
-0.554152 -1.09022 -0.318297
-0.44008 1.08547 -0.210284
-0.554152 1.09022 -0.318297
0.120963 1.04295 -0.240179
-0.216134 -1.06942 -0.300461
0.121885 1.04862 -0.282625
0.121885 0.906867 -0.694829
0.743847 0.935969 -0.40313
0.972217 0.917634 -0.387699
0.743847 -0.935969 -0.40313
0.464209 -1.0197 -0.248866
0.972217 -0.917634 -0.387699
0.578624 -1.01195 -0.251762
0.578624 1.01195 -0.251762
0.350255 -1.03029 -0.267193
-1.95655 0.337729 -0.974066
-2.39848 -0.191842 -1.00067
-2.90259 -0.339019 -0.99336
-2.90259 0.339019 -0.99336
-2.42213 -0.340666 -0.987647
-2.44579 0.425677 -1.07659
-2.42213 0.340666 -0.987647
6.64745 0.0799605 1.19695
5.86469 0.175109 1.19635
6.66784 0.227854 1.18756
6.66784 -0.227854 1.18756
7.01477 -0.100896 1.19651
5.86469 -0.175109 1.19635
6.64745 -0.0799605 1.19695
5.18718 0.312889 1.17567
5.91126 0 1.20755
6.28373 0.0373285 1.19785
6.66784 -0.369223 1.08566
6.66784 0.369223 1.08566
5.18718 -0.312889 1.17567
-0.861793 -0.521872 1.09238
0.149685 0.789422 0.80849
-1.51335 0.81079 0.836
0.149685 -0.789422 0.80849
0.169083 -0.812846 0.706241
0.154545 -0.691921 0.911545
-1.18271 -0.568829 1.06725
0.154545 0.691921 0.911545
-1.50363 0.615787 1.04211
-1.18271 0.568829 1.06725
0.169083 0.812846 0.706241
0.475464 -0.644963 0.93668
0.475464 0.644963 0.93668
3 0 304 10
3 11 305 110
3 10 802 321
3 320 323 11
3 10 304 790
3 6 305 11
3 785 7 1
3 8 9 788
3 787 785 1
3 8 788 2
3 789 787 1
3 8 2 4
3 3 789 1
3 8 4 5
3 790 3 1
3 8 5 6
3 10 790 1
3 8 6 11
3 7 802 1
3 8 323 9
3 802 10 1
3 8 11 323
3 604 766 769
3 12 768 767
3 602 604 769
3 12 767 603
3 602 769 13
3 770 12 603
3 16 602 13
3 770 603 14
3 16 13 608
3 15 770 14
3 16 608 612
3 613 15 14
3 16 612 611
3 616 613 14
3 623 16 611
3 616 14 624
3 19 623 611
3 616 624 17
3 18 19 611
3 616 17 617
3 221 649 258
3 20 665 21
3 733 24 22
3 377 271 734
3 631 24 733
3 734 271 732
3 631 23 24
3 271 270 732
3 26 22 117
3 25 377 738
3 733 22 26
3 738 377 734
3 27 37 28
3 29 38 27
3 30 37 31
3 31 38 30
3 31 37 27
3 27 38 31
3 37 35 28
3 29 78 38
3 35 32 65
3 65 32 78
3 37 34 33
3 33 34 38
3 34 37 30
3 30 38 34
3 32 35 36
3 36 78 32
3 36 37 33
3 33 38 36
3 35 37 36
3 36 38 78
3 45 49 39
3 41 47 40
3 45 39 43
3 48 41 40
3 43 581 75
3 76 42 48
3 43 44 45
3 40 588 48
3 43 75 44
3 588 76 48
3 49 45 574
3 46 40 47
3 578 43 39
3 41 48 579
3 39 49 578
3 579 47 41
3 578 49 574
3 46 47 579
3 43 50 581
3 42 51 48
3 43 587 50
3 51 69 48
3 43 578 587
3 69 579 48
3 591 52 75
3 76 592 53
3 64 54 581
3 42 55 56
3 782 590 65
3 65 66 57
3 589 35 65
3 65 78 58
3 816 573 45
3 40 828 59
3 893 585 60
3 60 894 893
3 61 782 73
3 73 57 62
3 577 824 578
3 579 71 580
3 28 35 77
3 63 78 29
3 77 35 64
3 56 78 63
3 77 64 581
3 42 56 63
3 589 65 590
3 66 65 58
3 67 68 587
3 69 575 576
3 67 587 837
3 70 69 576
3 824 837 578
3 579 70 71
3 816 570 781
3 571 569 59
3 816 44 570
3 569 588 59
3 72 61 73
3 73 62 74
3 570 75 52
3 592 76 569
3 77 68 600
3 601 575 63
3 50 68 77
3 63 575 51
3 573 577 574
3 46 580 828
3 35 54 64
3 56 55 78
3 35 591 54
3 55 53 78
3 560 79 559
3 193 151 541
3 86 80 474
3 89 81 86
3 86 84 80
3 81 82 86
3 86 83 84
3 82 85 86
3 86 475 83
3 85 475 86
3 86 474 87
3 88 89 86
3 90 471 86
3 86 471 472
3 86 87 90
3 472 88 86
3 646 97 468
3 466 91 647
3 97 99 777
3 778 100 91
3 97 224 99
3 100 227 91
3 94 92 224
3 227 96 741
3 92 94 453
3 93 741 96
3 453 94 95
3 108 741 93
3 94 265 95
3 108 116 741
3 111 461 92
3 96 101 112
3 97 223 224
3 227 225 91
3 97 662 223
3 225 663 91
3 646 662 97
3 91 663 647
3 98 263 462
3 222 263 98
3 99 306 210
3 211 307 100
3 99 0 306
3 307 110 100
3 461 0 99
3 100 110 101
3 0 461 111
3 112 101 110
3 102 103 436
3 437 430 105
3 103 102 336
3 335 105 430
3 336 102 104
3 333 105 335
3 103 336 338
3 337 335 430
3 106 342 449
3 451 344 107
3 348 346 457
3 318 448 345
3 95 368 453
3 93 109 108
3 368 95 265
3 116 108 109
3 0 111 455
3 113 112 110
3 92 455 111
3 112 113 96
3 114 322 294
3 301 293 324
3 115 0 455
3 113 110 456
3 265 118 368
3 109 119 116
3 268 118 265
3 116 119 266
3 268 117 118
3 119 25 266
3 117 376 118
3 119 378 25
3 22 376 117
3 25 378 377
3 273 395 384
3 388 864 387
3 286 402 120
3 772 403 288
3 290 425 365
3 121 426 292
3 290 289 425
3 426 431 292
3 122 341 126
3 127 123 124
3 343 122 126
3 127 124 125
3 343 126 339
3 340 127 125
3 131 343 339
3 340 125 128
3 131 339 129
3 334 340 128
3 129 130 131
3 128 133 334
3 131 130 134
3 132 133 128
3 134 130 135
3 347 133 132
3 135 130 136
3 364 133 347
3 138 135 136
3 364 347 137
3 138 136 363
3 360 364 137
3 350 138 363
3 360 137 139
3 350 363 140
3 361 360 139
3 141 350 140
3 361 139 353
3 141 140 356
3 145 361 353
3 355 141 356
3 145 353 142
3 355 356 143
3 144 145 142
3 148 146 147
3 147 155 148
3 148 79 146
3 155 151 148
3 79 149 146
3 155 150 151
3 79 152 149
3 150 153 151
3 79 199 152
3 153 775 151
3 208 152 777
3 778 153 209
3 208 162 152
3 153 163 209
3 160 152 162
3 163 153 164
3 160 149 152
3 153 150 164
3 160 146 149
3 150 155 164
3 160 158 146
3 155 154 164
3 159 146 158
3 154 155 159
3 159 147 146
3 155 147 159
3 159 171 156
3 156 157 159
3 159 158 171
3 157 154 159
3 160 171 158
3 154 157 164
3 160 161 171
3 157 170 164
3 160 198 161
3 170 169 164
3 160 162 198
3 169 163 164
3 208 198 162
3 163 169 209
3 165 166 198
3 169 167 196
3 168 198 166
3 167 169 176
3 168 161 198
3 169 170 176
3 168 171 161
3 170 157 176
3 168 172 171
3 157 173 176
3 175 171 172
3 173 157 175
3 175 156 171
3 157 156 175
3 175 189 174
3 174 190 175
3 175 172 189
3 190 173 175
3 168 189 172
3 173 190 176
3 168 184 189
3 190 185 176
3 168 178 184
3 185 177 176
3 168 166 178
3 177 167 176
3 165 178 166
3 167 177 196
3 165 195 178
3 177 906 196
3 179 182 178
3 177 183 180
3 181 178 182
3 183 177 186
3 181 184 178
3 177 185 186
3 181 189 184
3 185 190 186
3 181 187 189
3 190 192 186
3 188 189 187
3 192 190 188
3 188 174 189
3 190 174 188
3 188 187 191
3 191 192 188
3 181 191 187
3 192 191 186
3 181 182 191
3 191 183 186
3 179 191 182
3 183 191 180
3 79 194 559
3 193 194 151
3 79 148 194
3 194 148 151
3 195 165 319
3 900 196 906
3 197 165 198
3 169 196 200
3 199 79 560
3 541 151 775
3 776 779 560
3 541 780 463
3 165 197 319
3 900 200 196
3 197 201 319
3 900 308 200
3 202 201 198
3 169 308 207
3 201 197 198
3 169 200 308
3 201 202 306
3 307 207 308
3 202 205 306
3 307 203 207
3 205 204 306
3 307 206 203
3 204 210 306
3 307 211 206
3 204 205 198
3 169 203 206
3 205 202 198
3 169 207 203
3 210 208 99
3 100 209 211
3 208 777 99
3 100 778 209
3 208 210 198
3 169 211 209
3 210 204 198
3 169 206 211
3 179 565 568
3 568 566 180
3 191 179 568
3 568 180 191
3 179 898 565
3 566 783 180
3 195 904 178
3 177 907 906
3 904 898 178
3 177 783 907
3 850 854 822
3 214 212 213
3 216 349 822
3 214 351 598
3 349 850 822
3 214 213 351
3 217 295 215
3 445 296 220
3 215 349 217
3 220 351 445
3 216 217 349
3 351 220 598
3 295 563 218
3 795 219 296
3 299 298 218
3 795 303 297
3 217 216 563
3 219 598 220
3 298 295 218
3 795 296 303
3 295 217 563
3 219 220 296
3 462 221 464
3 465 21 222
3 223 226 224
3 227 698 225
3 226 94 224
3 227 741 698
3 228 719 702
3 229 232 230
3 261 228 702
3 229 230 231
3 228 260 719
3 232 259 230
3 879 263 331
3 331 263 233
3 240 879 331
3 331 233 878
3 879 880 263
3 263 881 233
3 234 235 597
3 597 235 327
3 236 237 238
3 883 747 249
3 236 239 248
3 255 241 249
3 331 239 240
3 878 241 331
3 239 236 240
3 878 249 241
3 244 764 762
3 763 242 243
3 332 244 762
3 763 243 247
3 244 284 764
3 242 285 243
3 244 245 854
3 212 246 243
3 244 332 245
3 246 247 243
3 240 236 238
3 883 249 878
3 251 236 248
3 255 249 250
3 251 252 237
3 747 755 250
3 236 251 237
3 747 250 249
3 251 256 252
3 755 253 250
3 256 248 254
3 330 255 253
3 256 251 248
3 255 250 253
3 256 765 252
3 755 257 253
3 765 254 234
3 327 330 257
3 765 256 254
3 330 253 257
3 262 221 462
3 222 21 264
3 464 646 468
3 466 647 465
3 221 646 464
3 465 647 21
3 258 646 221
3 21 647 20
3 880 260 263
3 263 259 881
3 260 228 263
3 263 230 259
3 880 238 719
3 232 883 881
3 260 880 719
3 232 881 259
3 261 263 228
3 230 263 231
3 262 462 263
3 263 222 264
3 262 263 261
3 231 263 264
3 94 267 265
3 116 742 741
3 267 268 265
3 116 266 742
3 267 739 268
3 266 737 742
3 22 24 273
3 387 271 377
3 24 269 273
3 387 272 271
3 274 269 23
3 270 272 275
3 269 24 23
3 270 271 272
3 269 274 273
3 387 275 272
3 274 395 273
3 387 864 275
3 395 274 626
3 813 275 864
3 274 23 626
3 813 270 275
3 858 280 395
3 864 282 866
3 280 277 278
3 276 279 282
3 277 402 278
3 276 403 279
3 281 277 280
3 282 279 283
3 277 281 402
3 403 283 279
3 875 281 280
3 282 283 876
3 286 120 291
3 287 772 288
3 284 291 764
3 242 287 285
3 284 289 291
3 287 431 285
3 290 286 291
3 287 288 292
3 289 290 291
3 287 292 431
3 215 315 317
3 447 316 445
3 315 294 322
3 293 301 316
3 315 215 295
3 296 445 316
3 294 295 298
3 303 296 301
3 295 294 315
3 316 301 296
3 114 300 299
3 297 302 324
3 300 298 299
3 297 303 302
3 298 300 294
3 301 302 303
3 300 114 294
3 301 324 302
3 321 319 201
3 308 900 320
3 321 306 10
3 11 307 320
3 306 0 10
3 11 110 307
3 0 115 304
3 305 456 110
3 321 201 306
3 307 308 320
3 774 380 304
3 305 373 369
3 380 793 304
3 305 309 373
3 380 390 793
3 309 310 373
3 390 803 793
3 309 804 310
3 396 311 803
3 804 312 413
3 457 313 802
3 323 314 318
3 322 313 315
3 316 314 293
3 313 317 315
3 316 447 314
3 317 313 457
3 318 314 447
3 321 902 319
3 900 908 320
3 321 798 902
3 908 325 320
3 802 798 321
3 320 325 323
3 802 313 322
3 293 314 323
3 802 322 798
3 325 293 323
3 114 798 322
3 293 325 324
3 299 798 114
3 324 325 297
3 443 289 284
3 285 431 326
3 443 284 358
3 357 285 326
3 234 254 329
3 329 330 327
3 234 329 235
3 235 329 327
3 254 248 328
3 328 255 330
3 254 328 329
3 329 328 330
3 248 239 331
3 331 241 255
3 248 331 328
3 328 331 255
3 765 234 332
3 247 327 257
3 234 597 332
3 247 597 327
3 130 102 438
3 439 105 133
3 130 438 136
3 364 439 133
3 102 130 129
3 334 133 105
3 102 129 104
3 333 334 105
3 339 336 129
3 334 335 340
3 336 104 129
3 334 333 335
3 126 338 339
3 340 337 127
3 338 336 339
3 340 335 337
3 341 459 126
3 127 458 123
3 459 338 126
3 127 337 458
3 122 106 341
3 123 107 124
3 106 459 341
3 123 458 107
3 343 342 122
3 124 344 125
3 342 106 122
3 124 107 344
3 131 452 343
3 125 450 128
3 452 342 343
3 125 344 450
3 134 348 131
3 128 345 132
3 348 452 131
3 128 450 345
3 135 346 134
3 132 448 347
3 346 348 134
3 132 345 448
3 138 446 346
3 448 444 137
3 138 346 135
3 347 448 137
3 350 349 446
3 444 351 139
3 350 446 138
3 137 444 139
3 141 850 350
3 139 213 353
3 850 349 350
3 139 351 213
3 355 352 141
3 353 853 142
3 352 850 141
3 353 213 853
3 143 354 352
3 853 852 144
3 143 352 355
3 142 853 144
3 356 284 354
3 852 285 145
3 356 354 143
3 144 852 145
3 140 358 356
3 145 357 361
3 358 284 356
3 145 285 357
3 363 359 140
3 361 362 360
3 359 358 140
3 361 357 362
3 136 438 359
3 362 439 364
3 136 359 363
3 360 362 364
3 290 365 286
3 288 121 292
3 365 402 286
3 288 403 121
3 372 773 454
3 366 370 367
3 773 115 454
3 366 456 370
3 118 372 368
3 109 367 119
3 372 454 368
3 109 366 367
3 375 774 372
3 367 369 371
3 774 773 372
3 367 370 369
3 376 375 118
3 119 371 378
3 375 372 118
3 119 367 371
3 774 375 380
3 373 371 369
3 375 379 380
3 373 374 371
3 383 379 376
3 378 374 385
3 379 375 376
3 378 371 374
3 273 383 376
3 378 385 387
3 273 376 22
3 377 378 387
3 381 390 379
3 374 310 382
3 390 380 379
3 374 373 310
3 393 381 383
3 385 382 386
3 381 379 383
3 385 374 382
3 384 393 383
3 385 386 388
3 384 383 273
3 387 385 388
3 389 803 381
3 382 804 397
3 803 390 381
3 382 310 804
3 392 389 393
3 386 397 398
3 389 381 393
3 386 382 397
3 391 392 393
3 386 398 394
3 391 393 384
3 388 386 394
3 280 391 384
3 388 394 282
3 280 384 395
3 864 388 282
3 407 396 389
3 397 413 409
3 396 803 389
3 397 804 413
3 404 407 392
3 398 409 410
3 407 389 392
3 398 397 409
3 405 404 391
3 394 410 399
3 404 392 391
3 394 398 410
3 278 405 280
3 282 399 276
3 405 391 280
3 282 394 399
3 405 278 400
3 419 276 399
3 278 416 400
3 419 401 276
3 278 402 416
3 401 403 276
3 402 365 416
3 401 121 403
3 404 405 406
3 418 399 410
3 405 400 406
3 418 419 399
3 407 404 411
3 408 410 409
3 404 406 411
3 408 418 410
3 396 407 412
3 414 409 413
3 407 411 412
3 414 408 409
3 311 396 799
3 422 413 312
3 396 412 799
3 422 414 413
3 400 416 423
3 424 401 419
3 416 415 423
3 424 427 401
3 416 365 415
3 427 121 401
3 365 425 415
3 427 426 121
3 406 400 417
3 429 419 418
3 400 423 417
3 429 424 419
3 411 406 421
3 420 418 408
3 406 417 421
3 420 429 418
3 412 411 421
3 420 408 414
3 412 421 434
3 435 420 414
3 799 412 434
3 435 414 422
3 799 434 433
3 801 435 422
3 423 415 436
3 437 427 424
3 415 428 436
3 437 442 427
3 415 425 428
3 442 426 427
3 425 440 428
3 442 441 426
3 417 423 103
3 430 424 429
3 423 436 103
3 430 437 424
3 425 289 440
3 441 431 426
3 289 443 440
3 441 326 431
3 421 417 460
3 432 429 420
3 417 103 460
3 432 430 429
3 434 421 449
3 451 420 435
3 421 460 449
3 451 432 420
3 433 434 802
3 323 435 801
3 434 449 802
3 323 451 435
3 436 428 102
3 105 442 437
3 428 438 102
3 105 439 442
3 428 440 438
3 439 441 442
3 440 359 438
3 439 362 441
3 440 443 359
3 362 326 441
3 443 358 359
3 362 357 326
3 215 317 446
3 444 447 445
3 215 446 349
3 351 444 445
3 317 457 446
3 444 318 447
3 457 346 446
3 444 448 318
3 802 449 452
3 450 451 323
3 449 342 452
3 450 344 451
3 453 368 454
3 366 109 93
3 453 454 92
3 96 366 93
3 92 454 115
3 456 366 96
3 92 115 455
3 113 456 96
3 457 802 452
3 450 323 318
3 457 452 348
3 345 450 318
3 449 460 106
3 107 432 451
3 460 459 106
3 107 458 432
3 338 459 460
3 432 458 337
3 338 460 103
3 430 432 337
3 99 224 461
3 101 227 100
3 224 92 461
3 101 96 227
3 509 507 98
3 98 507 543
3 509 98 462
3 222 98 543
3 776 547 97
3 91 467 463
3 776 97 777
3 778 91 463
3 545 509 462
3 222 543 544
3 545 462 464
3 465 222 544
3 549 545 464
3 465 544 546
3 549 464 468
3 466 465 546
3 547 549 468
3 466 546 467
3 547 468 97
3 91 466 467
3 469 482 90
3 472 473 480
3 469 90 87
3 88 472 480
3 482 470 471
3 471 470 473
3 482 471 90
3 472 471 473
3 478 469 474
3 89 480 494
3 469 87 474
3 89 88 480
3 486 487 475
3 475 489 486
3 487 83 475
3 475 85 489
3 487 477 83
3 85 476 489
3 477 84 83
3 85 82 476
3 477 479 80
3 81 491 476
3 477 80 84
3 82 81 476
3 479 478 474
3 89 494 491
3 479 474 80
3 81 89 491
3 484 496 482
3 473 483 501
3 484 482 469
3 480 473 501
3 496 481 470
3 470 481 483
3 496 470 482
3 473 470 483
3 506 484 469
3 480 501 505
3 506 469 478
3 494 480 505
3 502 485 486
3 486 488 502
3 485 487 486
3 486 489 488
3 485 492 487
3 489 493 488
3 492 477 487
3 489 476 493
3 492 490 479
3 491 495 493
3 492 479 477
3 476 491 493
3 490 506 478
3 494 505 495
3 490 478 479
3 491 494 495
3 498 536 496
3 483 497 512
3 498 496 484
3 501 483 512
3 536 499 481
3 481 499 497
3 536 481 496
3 483 481 497
3 500 498 484
3 501 512 531
3 500 484 506
3 505 501 531
3 516 521 502
3 502 522 516
3 521 485 502
3 502 488 522
3 521 504 485
3 488 503 522
3 504 492 485
3 488 493 503
3 504 527 490
3 495 526 503
3 504 490 492
3 493 495 503
3 527 500 506
3 505 531 526
3 527 506 490
3 495 505 526
3 507 509 508
3 508 543 507
3 509 542 508
3 508 510 543
3 511 498 528
3 532 512 537
3 498 500 528
3 532 531 512
3 528 513 511
3 537 534 532
3 513 550 511
3 537 551 534
3 514 521 516
3 516 522 524
3 514 516 515
3 515 516 524
3 515 552 517
3 518 552 515
3 515 517 514
3 524 518 515
3 514 517 553
3 519 518 524
3 514 553 520
3 523 519 524
3 520 504 521
3 522 503 523
3 520 521 514
3 524 522 523
3 520 553 529
3 530 519 523
3 553 525 529
3 530 533 519
3 529 527 520
3 523 526 530
3 527 504 520
3 523 503 526
3 528 500 529
3 530 531 532
3 500 527 529
3 530 526 531
3 529 525 528
3 532 533 530
3 525 513 528
3 532 534 533
3 511 550 539
3 535 551 537
3 550 562 539
3 535 561 551
3 539 536 511
3 537 497 535
3 536 498 511
3 537 512 497
3 539 562 540
3 540 561 535
3 562 538 540
3 540 538 561
3 540 499 539
3 535 499 540
3 499 536 539
3 535 497 499
3 776 560 547
3 467 541 463
3 560 548 547
3 467 558 541
3 545 554 542
3 510 556 544
3 545 542 509
3 543 510 544
3 549 557 545
3 544 555 546
3 557 554 545
3 544 556 555
3 547 548 549
3 546 558 467
3 548 557 549
3 546 555 558
3 513 548 550
3 551 558 534
3 548 560 550
3 551 541 558
3 552 508 542
3 510 508 552
3 552 542 517
3 518 510 552
3 517 542 554
3 556 510 518
3 517 554 553
3 519 556 518
3 553 554 525
3 533 556 519
3 554 557 525
3 533 555 556
3 525 557 513
3 534 555 533
3 557 548 513
3 534 558 555
3 559 194 562
3 561 194 193
3 194 538 562
3 561 538 194
3 562 550 560
3 541 551 561
3 562 560 559
3 193 541 561
3 563 567 218
3 795 564 219
3 567 565 218
3 795 566 564
3 567 595 568
3 568 595 564
3 567 568 565
3 566 568 564
3 570 44 75
3 76 588 569
3 570 52 781
3 571 592 569
3 782 65 73
3 73 65 57
3 889 72 572
3 572 74 583
3 72 73 572
3 572 73 74
3 889 572 582
3 582 572 583
3 573 574 45
3 40 46 828
3 68 50 587
3 69 51 575
3 67 600 68
3 575 601 576
3 577 578 574
3 46 579 580
3 54 591 581
3 42 53 55
3 591 75 581
3 42 76 53
3 889 582 584
3 584 582 583
3 891 889 584
3 584 583 885
3 891 584 586
3 586 584 885
3 585 891 586
3 586 885 894
3 585 586 60
3 60 586 894
3 837 587 578
3 579 69 70
3 837 600 67
3 576 601 70
3 45 44 816
3 59 588 40
3 589 590 781
3 571 66 58
3 589 781 35
3 78 571 58
3 782 781 590
3 66 571 57
3 77 581 50
3 51 42 63
3 77 600 28
3 29 601 63
3 591 35 781
3 571 78 53
3 591 781 52
3 592 571 53
3 897 567 814
3 593 564 892
3 567 563 814
3 593 219 564
3 594 595 897
3 892 595 594
3 595 567 897
3 892 564 595
3 245 332 596
3 596 247 246
3 332 597 596
3 596 597 247
3 814 563 822
3 214 219 593
3 563 216 822
3 214 598 219
3 596 599 833
3 835 599 596
3 596 833 245
3 246 835 596
3 599 27 833
3 835 27 599
3 833 27 600
3 601 27 835
3 27 28 600
3 601 29 27
3 268 739 26
3 738 737 266
3 268 26 117
3 25 738 266
3 602 16 651
3 621 14 603
3 602 651 633
3 606 621 603
3 604 602 633
3 606 603 767
3 604 633 636
3 605 606 767
3 641 607 13
3 770 609 642
3 607 608 13
3 770 15 609
3 607 610 608
3 15 645 609
3 610 612 608
3 15 613 645
3 610 615 612
3 613 614 645
3 615 611 612
3 613 616 614
3 615 618 18
3 617 648 614
3 615 18 611
3 616 617 614
3 618 620 19
3 17 619 648
3 618 19 18
3 617 17 648
3 620 622 623
3 624 625 619
3 620 623 19
3 17 624 619
3 622 651 16
3 14 621 625
3 622 16 623
3 624 14 625
3 626 627 757
3 753 630 813
3 627 752 757
3 753 628 630
3 752 627 629
3 726 630 628
3 627 730 629
3 726 727 630
3 631 627 626
3 813 630 732
3 631 626 23
3 270 813 732
3 712 730 627
3 630 727 731
3 712 627 631
3 732 630 731
3 651 671 635
3 634 632 621
3 651 635 633
3 606 634 621
3 633 635 636
3 605 634 606
3 635 674 636
3 605 654 634
3 652 637 771
3 639 638 653
3 637 656 771
3 639 657 638
3 771 656 658
3 640 657 639
3 771 658 641
3 642 640 639
3 641 658 643
3 644 640 642
3 641 643 607
3 609 644 642
3 607 643 646
3 647 644 609
3 607 646 610
3 645 647 609
3 610 646 615
3 614 647 645
3 646 258 615
3 614 20 647
3 615 258 618
3 648 20 614
3 258 649 618
3 648 665 20
3 618 649 620
3 619 665 648
3 649 664 620
3 619 668 665
3 620 664 650
3 670 668 619
3 620 650 622
3 625 670 619
3 622 650 671
3 632 670 625
3 622 671 651
3 621 632 625
3 636 674 652
3 653 654 605
3 674 637 652
3 653 638 654
3 655 656 688
3 689 657 675
3 656 637 688
3 689 638 657
3 677 658 655
3 675 640 661
3 658 656 655
3 675 657 640
3 660 643 658
3 640 644 659
3 660 658 677
3 661 640 659
3 662 646 643
3 644 647 663
3 662 643 660
3 659 644 663
3 680 664 221
3 21 668 679
3 664 649 221
3 21 665 668
3 666 650 664
3 668 670 667
3 666 664 680
3 679 668 667
3 683 671 650
3 670 632 669
3 683 650 666
3 667 670 669
3 687 635 671
3 632 634 686
3 687 671 683
3 669 632 686
3 673 674 635
3 634 654 672
3 673 635 687
3 686 634 672
3 688 637 673
3 672 638 689
3 637 674 673
3 672 654 638
3 655 688 711
3 676 689 675
3 655 711 692
3 691 676 675
3 677 655 692
3 691 675 661
3 677 692 695
3 694 691 661
3 660 677 678
3 699 661 659
3 677 695 678
3 699 694 661
3 662 660 223
3 225 659 663
3 660 678 223
3 225 699 659
3 680 221 262
3 264 21 679
3 680 262 681
3 700 264 679
3 666 680 681
3 700 679 667
3 666 681 701
3 682 700 667
3 683 666 684
3 708 667 669
3 666 701 684
3 708 682 667
3 687 683 705
3 709 669 686
3 683 684 705
3 709 708 669
3 673 687 710
3 685 686 672
3 687 705 710
3 685 709 686
3 688 673 710
3 685 672 689
3 688 710 711
3 676 685 689
3 692 711 729
3 690 676 691
3 692 729 693
3 713 690 691
3 695 692 693
3 713 691 694
3 695 693 716
3 718 713 694
3 678 695 696
3 697 694 699
3 695 716 696
3 697 718 694
3 223 678 226
3 698 699 225
3 678 696 226
3 698 697 699
3 681 262 261
3 231 264 700
3 681 261 702
3 229 231 700
3 701 681 702
3 229 700 682
3 701 702 703
3 704 229 682
3 684 701 703
3 704 682 708
3 684 703 706
3 721 704 708
3 705 684 725
3 707 708 709
3 684 706 725
3 707 721 708
3 710 705 725
3 707 709 685
3 710 725 724
3 728 707 685
3 711 710 724
3 728 685 676
3 711 724 729
3 690 728 676
3 693 729 712
3 731 690 713
3 693 712 715
3 714 731 713
3 716 693 715
3 714 713 718
3 716 715 717
3 735 714 718
3 696 716 717
3 735 718 697
3 696 717 736
3 740 735 697
3 226 696 94
3 741 697 698
3 696 736 94
3 741 740 697
3 703 702 719
3 232 229 704
3 703 719 743
3 720 232 704
3 706 703 743
3 720 704 721
3 706 743 723
3 722 720 721
3 725 706 629
3 726 721 707
3 706 723 629
3 726 722 721
3 724 725 629
3 726 707 728
3 724 629 730
3 727 726 728
3 729 724 730
3 727 728 690
3 729 730 712
3 731 727 690
3 715 712 631
3 732 731 714
3 715 631 733
3 734 732 714
3 717 715 733
3 734 714 735
3 717 733 26
3 738 734 735
3 736 717 26
3 738 735 740
3 736 26 739
3 737 738 740
3 94 736 739
3 737 740 741
3 94 739 267
3 742 737 741
3 743 719 238
3 883 232 720
3 743 238 748
3 745 883 720
3 723 743 748
3 745 720 722
3 723 748 750
3 744 745 722
3 629 723 752
3 628 722 726
3 723 750 752
3 628 744 722
3 748 238 749
3 746 883 745
3 238 237 749
3 746 747 883
3 750 748 749
3 746 745 744
3 750 749 756
3 751 746 744
3 752 750 756
3 751 744 628
3 752 756 757
3 753 751 628
3 749 237 754
3 760 747 746
3 237 252 754
3 760 755 747
3 756 749 754
3 760 746 751
3 756 754 761
3 759 760 751
3 757 756 761
3 759 751 753
3 757 761 758
3 869 759 753
3 626 757 758
3 869 753 813
3 754 252 765
3 257 755 760
3 754 765 762
3 763 257 760
3 761 754 762
3 763 760 759
3 761 762 764
3 242 763 759
3 758 761 764
3 242 759 869
3 758 764 291
3 287 242 869
3 762 765 332
3 247 257 763
3 636 652 766
3 768 653 605
3 636 766 604
3 767 768 605
3 771 641 769
3 12 642 639
3 641 13 769
3 12 770 642
3 652 771 766
3 768 639 653
3 771 769 766
3 768 12 639
3 120 281 875
3 876 283 772
3 402 281 120
3 772 283 403
3 115 773 304
3 305 370 456
3 773 774 304
3 305 369 370
3 152 199 779
3 780 775 153
3 560 779 199
3 775 780 541
3 776 777 779
3 780 778 463
3 777 152 779
3 780 153 778
3 781 821 816
3 59 820 571
3 821 781 782
3 57 571 820
3 179 178 898
3 783 177 180
3 7 811 802
3 323 812 9
3 785 784 811
3 785 811 7
3 812 786 788
3 812 788 9
3 787 784 785
3 788 786 2
3 789 791 784
3 789 784 787
3 786 792 4
3 786 4 2
3 3 791 789
3 4 792 5
3 790 791 3
3 5 792 6
3 793 791 304
3 305 792 309
3 791 790 304
3 305 6 792
3 811 784 793
3 309 786 812
3 784 791 793
3 309 792 786
3 311 794 803
3 804 800 312
3 797 299 218
3 795 297 796
3 299 797 798
3 325 796 297
3 797 902 798
3 325 908 796
3 799 809 794
3 799 794 311
3 800 810 422
3 800 422 312
3 433 809 799
3 422 810 801
3 802 809 433
3 801 810 323
3 809 802 811
3 812 323 810
3 794 806 803
3 804 805 800
3 806 793 803
3 804 309 805
3 808 793 806
3 805 309 807
3 809 808 806
3 809 806 794
3 805 807 810
3 805 810 800
3 793 808 811
3 812 807 309
3 808 809 811
3 812 810 807
3 626 863 395
3 864 865 813
3 863 626 758
3 869 813 865
3 814 895 897
3 892 896 593
3 61 895 782
3 57 896 62
3 817 814 815
3 818 593 819
3 816 817 815
3 816 815 573
3 818 819 59
3 818 59 828
3 573 815 822
3 214 818 828
3 815 814 822
3 214 593 818
3 821 817 816
3 59 819 820
3 895 814 817
3 819 593 896
3 782 895 817
3 782 817 821
3 819 896 57
3 819 57 820
3 822 823 573
3 828 826 214
3 823 822 854
3 212 214 826
3 577 825 824
3 71 829 580
3 825 823 836
3 827 826 829
3 824 825 836
3 824 836 837
3 827 829 71
3 827 71 70
3 823 825 573
3 828 829 826
3 825 577 573
3 828 580 829
3 837 830 844
3 837 844 600
3 831 832 70
3 831 70 601
3 833 834 245
3 246 845 835
3 600 844 834
3 600 834 833
3 845 831 601
3 845 601 835
3 836 846 830
3 836 830 837
3 832 847 827
3 832 827 70
3 846 836 823
3 826 827 847
3 838 854 843
3 842 212 839
3 830 838 843
3 830 843 844
3 842 839 832
3 842 832 831
3 834 840 245
3 246 841 845
3 840 854 245
3 246 212 841
3 843 854 840
3 841 212 842
3 844 843 840
3 844 840 834
3 841 842 831
3 841 831 845
3 846 838 830
3 832 839 847
3 854 838 823
3 826 839 212
3 838 846 823
3 826 847 839
3 352 849 850
3 213 848 853
3 849 854 850
3 213 212 848
3 851 854 849
3 848 212 855
3 354 851 849
3 354 849 352
3 848 855 852
3 848 852 853
3 284 851 354
3 852 855 285
3 854 851 244
3 243 855 212
3 851 284 244
3 243 285 855
3 856 877 859
3 856 859 858
3 860 857 868
3 860 868 866
3 858 859 280
3 282 860 866
3 877 873 859
3 860 861 857
3 859 873 280
3 282 861 860
3 873 875 280
3 282 876 861
3 858 867 856
3 868 862 866
3 863 867 395
3 864 862 865
3 867 858 395
3 864 866 862
3 856 867 758
3 869 862 868
3 867 863 758
3 869 865 862
3 291 872 758
3 869 870 287
3 872 856 758
3 869 868 870
3 875 874 120
3 772 871 876
3 874 291 120
3 772 287 871
3 856 872 877
3 857 870 868
3 873 874 875
3 876 871 861
3 872 291 874
3 871 287 870
3 877 872 874
3 877 874 873
3 871 870 857
3 871 857 861
3 238 882 240
3 878 884 883
3 882 879 240
3 878 233 884
3 879 882 880
3 881 884 233
3 882 238 880
3 881 883 884
3 585 890 891
3 885 886 894
3 890 897 888
3 887 892 886
3 72 890 888
3 72 888 61
3 887 886 74
3 887 74 62
3 889 890 72
3 74 886 583
3 891 890 889
3 583 886 885
3 897 890 594
3 594 886 892
3 890 893 594
3 594 893 886
3 893 890 585
3 894 886 893
3 61 888 895
3 896 887 62
3 888 897 895
3 896 892 887
3 898 909 565
3 566 910 783
3 319 901 903
3 319 903 195
3 905 899 900
3 905 900 906
3 901 319 902
3 908 900 899
3 904 909 898
3 783 910 907
3 195 903 909
3 195 909 904
3 910 905 906
3 910 906 907
3 218 902 797
3 796 908 795
3 909 218 565
3 566 795 910
3 901 218 903
3 905 795 899
3 902 218 901
3 899 795 908
3 903 218 909
3 910 795 905
================================================
FILE: examples/mesh_ply.py
================================================
def main():
import sys
from ply import parse_ply
data = parse_ply(sys.argv[1])
from meshpy.geometry import GeometryBuilder
builder = GeometryBuilder()
builder.add_geometry(
points=[pt[:3] for pt in data["vertex"].data],
facets=[fd[0] for fd in data["face"].data],
)
builder.wrap_in_box(1)
from meshpy.tet import MeshInfo, build
mi = MeshInfo()
builder.set(mi)
mi.set_holes([builder.center()])
mesh = build(mi)
print(f"{len(mesh.elements)} elements")
mesh.write_vtk("out.vtk")
if __name__ == "__main__":
main()
================================================
FILE: examples/nico_mesh.py
================================================
def main():
import math
from meshpy import triangle
points = [(1, 1), (-1, 1), (-1, -1), (1, -1)]
def round_trip_connect(start, end):
result = [(i, i + 1) for i in range(start, end)]
result.append((end, start))
return result
def needs_refinement(vertices, area):
vert_origin, vert_destination, vert_apex = vertices
bary_x = (vert_origin.x + vert_destination.x + vert_apex.x) / 3
bary_y = (vert_origin.y + vert_destination.y + vert_apex.y) / 3
dist_center = math.sqrt((bary_x - 1) ** 2 + (bary_y - 1) ** 2)
max_area = math.fabs(0.05 * (dist_center - 0.5)) + 0.01
return area > max_area
info = triangle.MeshInfo()
info.set_points(points)
info.set_facets(round_trip_connect(0, len(points) - 1))
mesh = triangle.build(info, refinement_func=needs_refinement)
with open("nico.neu", "w") as f:
mesh.write_neu(f)
triangle.write_gnuplot_mesh("triangles.dat", mesh)
if __name__ == "__main__":
main()
================================================
FILE: examples/test_ball.py
================================================
def main():
from math import cos, pi, sin
from meshpy.geometry import (
EXT_OPEN,
GeometryBuilder,
generate_surface_of_revolution,
)
from meshpy.tet import MeshInfo, build
r = 3
points = 10
dphi = pi / points
def truncate(r):
if abs(r) < 1e-10:
return 0
else:
return r
rz = [(truncate(r * sin(i * dphi)), r * cos(i * dphi))
for i in range(points + 1)]
geob = GeometryBuilder()
geob.add_geometry(
*generate_surface_of_revolution(rz, closure=EXT_OPEN, radial_subdiv=10)
)
mesh_info = MeshInfo()
geob.set(mesh_info)
mesh = build(mesh_info)
mesh.write_vtk("ball.vtk")
# mesh.write_neu(file("torus.neu", "w"),
# {1: ("pec", 0)})
if __name__ == "__main__":
main()
================================================
FILE: examples/test_cylinder.py
================================================
def main():
from meshpy.geometry import (
GeometryBuilder,
generate_surface_of_revolution,
)
from meshpy.tet import MeshInfo, build
r = 1
ell = 1
rz = [(0, 0), (r, 0), (r, ell), (0, ell)]
geob = GeometryBuilder()
geob.add_geometry(
*generate_surface_of_revolution(rz, radial_subdiv=20, ring_markers=[1, 2, 3])
)
mesh_info = MeshInfo()
geob.set(mesh_info)
mesh = build(mesh_info, max_volume=0.01)
mesh.write_vtk("cylinder.vtk")
with open("cylinder.neu", "w") as f:
mesh.write_neu(
f,
{
1: ("minus_z", 1),
2: ("outer", 2),
3: ("plus_z", 3),
},
)
if __name__ == "__main__":
main()
================================================
FILE: examples/test_tet_torus.py
================================================
def main():
from math import cos, pi, sin
from meshpy.geometry import (
EXT_CLOSED_IN_RZ,
GeometryBuilder,
generate_surface_of_revolution,
)
from meshpy.tet import MeshInfo, build
big_r = 3
little_r = 2.9
points = 50
dphi = 2 * pi / points
rz = [
(big_r + little_r * cos(i * dphi), little_r * sin(i * dphi))
for i in range(points)
]
geob = GeometryBuilder()
geob.add_geometry(
*generate_surface_of_revolution(rz, closure=EXT_CLOSED_IN_RZ,
radial_subdiv=20)
)
mesh_info = MeshInfo()
geob.set(mesh_info)
mesh_info.save_nodes("torus")
mesh_info.save_poly("torus")
mesh = build(mesh_info)
mesh.write_vtk("torus.vtk")
mesh.save_elements("torus_mesh")
mesh.save_nodes("torus_mesh")
with open("torus.neu", "w") as f:
mesh.write_neu(f, {1: ("pec", 0)})
if __name__ == "__main__":
main()
================================================
FILE: examples/test_tetgen.py
================================================
from meshpy.tet import MeshInfo, build
def main():
mesh_info = MeshInfo()
mesh_info.set_points(
[
(0, 0, 0),
(2, 0, 0),
(2, 2, 0),
(0, 2, 0),
(0, 0, 12),
(2, 0, 12),
(2, 2, 12),
(0, 2, 12),
]
)
mesh_info.set_facets(
[
[0, 1, 2, 3],
[4, 5, 6, 7],
[0, 4, 5, 1],
[1, 5, 6, 2],
[2, 6, 7, 3],
[3, 7, 4, 0],
]
)
mesh_info.save_nodes("bar")
mesh_info.save_poly("bar")
mesh = build(mesh_info)
mesh.save_nodes("barout")
mesh.save_elements("barout")
mesh.save_faces("barout")
mesh.write_vtk("test.vtk")
if __name__ == "__main__":
main()
================================================
FILE: examples/test_tetgen_2.py
================================================
def main():
from meshpy.geometry import GeometryBuilder, generate_surface_of_revolution
from meshpy.tet import MeshInfo, build
simple_rz = [
(0, 0),
(1, 1),
(1, 2),
(0, 3),
]
geob = GeometryBuilder()
geob.add_geometry(*generate_surface_of_revolution(simple_rz))
mesh_info = MeshInfo()
geob.set(mesh_info)
# mesh_info.save_nodes("test")
# mesh_info.save_poly("test")
# mesh_info.load_poly("test")
mesh = build(mesh_info)
mesh.write_vtk("my_mesh.vtk")
# mesh.save_elements("gun")
# mesh.save_nodes("gun")
if __name__ == "__main__":
main()
================================================
FILE: examples/test_tri_pml.py
================================================
def main():
from meshpy import triangle
points = [
(-5, -1),
(-1, -1),
(0, -1),
(0, 0),
(1, 0),
(5, 0),
(5, 1),
(1, 1),
(-1, 1),
(-5, 1),
]
def round_trip_connect(seq):
n = len(seq)
return [(seq[i], seq[(i + 1) % n]) for i in range(n)]
info = triangle.MeshInfo()
info.set_points(points)
info.set_facets(
round_trip_connect([0, 1, 8, 9])
+ round_trip_connect([1, 2, 3, 4, 7, 8])
+ round_trip_connect([4, 5, 6, 7])
)
info.regions.resize(3)
info.regions[0] = (-2, 0, 1, 0.1)
info.regions[1] = (-0.5, 0, 0, 0.01)
info.regions[2] = (1.5, 0.5, 1, 0.1)
mesh = triangle.build(info)
triangle.write_gnuplot_mesh("triangles.dat", mesh)
with open("tri_pml.enu", "w") as f:
mesh.write_neu(f)
if __name__ == "__main__":
main()
================================================
FILE: examples/test_tri_quadratic.py
================================================
# Quadratic element demo, by Aravind Alwan
# Utility function to create lists of the form [(1,2), (2,3), (3,4),
# (4,1)], given two numbers 1 and 4
from itertools import cycle, islice
import matplotlib.pyplot as plt
import numpy as np
from meshpy.triangle import MeshInfo, build
def loop(a, b):
return list(
zip(list(range(a, b)), islice(cycle(list(range(a, b))), 1, None),
strict=True)
)
info = MeshInfo()
info.set_points([(0, 0), (1, 0), (1, 1), (0, 1), (2, 0), (3, 0), (3, 1), (2, 1)])
info.set_facets(
loop(0, 4) + loop(4, 8), list(range(1, 9))
) # Create 8 facets and apply markers 1-8 on them
info.regions.resize(2)
info.regions[0] = [
0.5,
0.5,
1,
0.1,
] # Fourth item specifies maximum area of triangles as a region attribute
info.regions[1] = [
2.5,
0.5,
2,
0.1,
] # Replace 0.1 by a smaller value to produce a finer mesh
mesh = build(
info,
volume_constraints=True,
attributes=True,
generate_faces=True,
min_angle=33,
mesh_order=2,
)
pts = np.vstack(mesh.points) # (npoints, 2)-array of points
elements = np.vstack(
mesh.elements
) # (ntriangles, 6)-array specifying element connectivity
# Matplotlib's Triangulation module uses only linear elements, so use only
# first 3 columns when plotting
plt.triplot(pts[:, 0], pts[:, 1], elements[:, :3])
plt.plot(
pts[:, 0], pts[:, 1], "ko"
) # Manually plot all points including the ones at the midpoints of triangle faces
# Plot a filled contour plot of the function (x - 1.5)^2 + y^2 over
# the mesh. Note tricontourf interpolation uses only linear elements
plt.tricontourf(
pts[:, 0], pts[:, 1], elements[:, :3],
(pts[:, 0] - 1.5) ** 2 + pts[:, 1] ** 2,
100)
plt.axis([-0.1, 3.1, -0.8, 1.8])
plt.show()
================================================
FILE: examples/test_tri_simple_square.py
================================================
def main():
from meshpy import triangle
points = [(1, 1), (-1, 1), (-1, -1), (1, -1)]
def round_trip_connect(start, end):
result = [(i, i + 1) for i in range(start, end)]
result.append((end, start))
return result
info = triangle.MeshInfo()
info.set_points(points)
info.set_facets(round_trip_connect(0, len(points) - 1))
mesh = triangle.build(info, max_volume=1e-3, min_angle=25)
print("A")
triangle.write_gnuplot_mesh("triangles.dat", mesh)
if __name__ == "__main__":
main()
================================================
FILE: examples/test_triangle.py
================================================
import numpy as np
import numpy.linalg as la
from meshpy import triangle
def round_trip_connect(start, end):
return [(i, i + 1) for i in range(start, end)] + [(end, start)]
def main():
points = [(1, 0), (1, 1), (-1, 1), (-1, -1), (1, -1), (1, 0)]
facets = round_trip_connect(0, len(points) - 1)
circ_start = len(points)
points.extend(
(3 * np.cos(angle), 3 * np.sin(angle))
for angle in np.linspace(0, 2 * np.pi, 30, endpoint=False)
)
facets.extend(round_trip_connect(circ_start, len(points) - 1))
def needs_refinement(vertices, area):
bary = np.sum(np.array(vertices), axis=0) / 3
max_area = 0.001 + (la.norm(bary, np.inf) - 1) * 0.01
return bool(area > max_area)
info = triangle.MeshInfo()
info.set_points(points)
info.set_holes([(0, 0)])
info.set_facets(facets)
mesh = triangle.build(info, refinement_func=needs_refinement)
mesh_points = np.array(mesh.points)
mesh_tris = np.array(mesh.elements)
import matplotlib.pyplot as pt
pt.triplot(mesh_points[:, 0], mesh_points[:, 1], mesh_tris)
pt.show()
if __name__ == "__main__":
main()
================================================
FILE: examples/test_triangle_refine.py
================================================
def main():
import math
from meshpy import triangle
segments = 50
points = [(1, 0), (1, 1), (-1, 1), (-1, -1), (1, -1), (1, 0)]
for i in range(segments + 1):
angle = i * 2 * math.pi / segments
points.append((0.5 * math.cos(angle), 0.5 * math.sin(angle)))
def round_trip_connect(start, end):
result = [(i, i + 1) for i in range(start, end)]
result.append((end, start))
return result
def needs_refinement(vertices, area):
vert_origin, vert_destination, vert_apex = vertices
bary_x = (vert_origin.x + vert_destination.x + vert_apex.x) / 3
bary_y = (vert_origin.y + vert_destination.y + vert_apex.y) / 3
dist_center = math.sqrt(bary_x ** 2 + bary_y ** 2)
max_area = 100 * (math.fabs(0.002 * (dist_center - 0.5)) + 0.0001)
return area > max_area
info = triangle.MeshInfo()
info.set_points(points)
info.set_holes([(0, 0)])
info.set_facets(round_trip_connect(0, len(points) - 1))
mesh = triangle.build(
info,
refinement_func=needs_refinement,
)
triangle.write_gnuplot_mesh("triangles-unrefined.dat", mesh)
print(len(mesh.elements))
mesh.element_volumes.setup()
for i in range(len(mesh.elements)):
mesh.element_volumes[i] = -1
for i in range(0, len(mesh.elements), 10):
mesh.element_volumes[i] = 1e-8
mesh = triangle.refine(mesh)
print(len(mesh.elements))
triangle.write_gnuplot_mesh("triangles.dat", mesh)
if __name__ == "__main__":
main()
================================================
FILE: examples/test_triangle_with_specified_points.py
================================================
def main():
import numpy as np
from meshpy import triangle
points = [(1, 1), (-1, 1), (-1, -1), (1, -1)]
rng = np.random.default_rng(seed=42)
points.extend(0.1 * rng.normal(size=(100, 2)))
def round_trip_connect(start, end):
result = [(i, i + 1) for i in range(start, end)]
result.append((end, start))
return result
info = triangle.MeshInfo()
info.set_points(points)
info.set_facets(round_trip_connect(0, 3))
mesh = triangle.build(
info, allow_volume_steiner=False, allow_boundary_steiner=False
)
triangle.write_gnuplot_mesh("triangles.dat", mesh)
if __name__ == "__main__":
main()
================================================
FILE: examples/tet-size-control.py
================================================
from meshpy.tet import MeshInfo, build
mesh_info = MeshInfo()
# construct a two-box extrusion of this base
base = [(-2, -2, 0), (2, -2, 0), (2, 2, 0), (-2, 2, 0)]
# first, the nodes
mesh_info.set_points(
base + [(x, y, z + 5) for x, y, z in base] + [(x, y, z + 10) for x, y, z in base]
)
# next, the facets
# vertex indices for a box missing the -z face
box_without_minus_z = [
[4, 5, 6, 7],
[0, 4, 5, 1],
[1, 5, 6, 2],
[2, 6, 7, 3],
[3, 7, 4, 0],
]
def add_to_all_vertex_indices(facets, increment):
return [[pt + increment for pt in facet] for facet in facets]
mesh_info.set_facets([
[0, 1, 2, 3], # base
*box_without_minus_z, # first box
*add_to_all_vertex_indices(box_without_minus_z, 4) # second box
])
# set the volume properties -- this is where the tet size constraints are
mesh_info.regions.resize(2)
mesh_info.regions[0] = [
0,
0,
2, # point in volume -> first box
0, # region tag (user-defined number)
1e-1, # max tet volume in region
]
mesh_info.regions[1] = [
0,
0,
7, # point in volume -> second box
0, # region tag (user-defined number, arbitrary)
1e-2, # max tet volume in region
]
mesh = build(mesh_info, volume_constraints=True)
# this is a no-op, but it shows how to access the output data
for point in mesh.points:
[x, y, z] = point
for element in mesh.elements:
[pt_1, pt_2, pt_3, pt_4] = element
# this writes the mesh as a vtk file, requires pyvtk
mesh.write_vtk("test.vtk")
================================================
FILE: examples/tri-boundary-markers.py
================================================
# Provided by Liu Benyuan in https://github.com/inducer/meshpy/pull/11
import numpy as np
from meshpy import triangle
def round_trip_connect(start, end):
return [(i, i + 1) for i in range(start, end)] + [(end, start)]
def refinement_func(tri_points, area):
max_area = 0.1
return bool(area > max_area)
def main():
points = [(1, 0), (1, 1), (-1, 1), (-1, -1), (1, -1), (1, 0)]
facets = round_trip_connect(0, len(points) - 1)
markers = [2, 2, 2, 2, 2, 2]
outter_start = len(points)
points.extend([(2, 0), (2, 2), (-2, 2), (-2, -2), (2, -2), (2, 0)])
facets.extend(round_trip_connect(outter_start, len(points) - 1))
markers.extend([3, 3, 3, 3, 3, 3])
# build
info = triangle.MeshInfo()
info.set_points(points)
info.set_holes([(0, 0)])
info.set_facets(facets, facet_markers=markers)
mesh = triangle.build(info, refinement_func=refinement_func)
mesh_points = np.array(mesh.points)
mesh_tris = np.array(mesh.elements)
mesh_attr = np.array(mesh.point_markers)
print(mesh_attr)
import matplotlib.pyplot as plt
plt.triplot(mesh_points[:, 0], mesh_points[:, 1], mesh_tris)
plt.xlabel("x")
plt.ylabel("y")
n = np.size(mesh_attr)
inner_nodes = [i for i in range(n) if mesh_attr[i] == 2]
outer_nodes = [i for i in range(n) if mesh_attr[i] == 3]
plt.plot(mesh_points[inner_nodes, 0], mesh_points[inner_nodes, 1], "ro")
plt.plot(mesh_points[outer_nodes, 0], mesh_points[outer_nodes, 1], "go")
plt.axis([-2.5, 2.5, -2.5, 2.5])
# plt.show()
#
fig = plt.gcf()
fig.set_size_inches(4.2, 4.2)
plt.savefig("sec5-meshpy-triangle-ex5.pdf")
if __name__ == "__main__":
main()
================================================
FILE: examples/tri-refinement-spec.py
================================================
# Provided by Liu Benyuan in https://github.com/inducer/meshpy/pull/11
import numpy as np
from meshpy import triangle
def round_trip_connect(start, end):
return [(i, i + 1) for i in range(start, end)] + [(end, start)]
def main():
points = [(1, 0), (1, 1), (-1, 1), (-1, -1), (1, -1), (1, 0)]
facets = round_trip_connect(0, len(points) - 1)
circ_start = len(points)
points.extend(
(3 * np.cos(angle), 3 * np.sin(angle))
for angle in np.linspace(0, 2 * np.pi, 30, endpoint=False)
)
facets.extend(round_trip_connect(circ_start, len(points) - 1))
markers = [2, 2, 2, 2, 2, 2]
markers.extend(list(np.ones(30, dtype="int")))
markers = [int(i) for i in markers]
info = triangle.MeshInfo()
info.set_points(points)
info.set_facets(facets, facet_markers=markers)
info.regions.resize(1)
# points [x,y] in region, + region number, + regional area constraints
info.regions[0] = [0, 0, 1, 0.05]
mesh = triangle.build(info, volume_constraints=True, max_volume=0.1)
mesh_points = np.array(mesh.points)
mesh_tris = np.array(mesh.elements)
mesh_attr = np.array(mesh.point_markers)
print(mesh_attr)
import matplotlib.pyplot as plt
plt.triplot(mesh_points[:, 0], mesh_points[:, 1], mesh_tris)
plt.xlabel("x")
plt.ylabel("y")
fig = plt.gcf()
fig.set_size_inches(4.2, 4.2)
plt.savefig("sec5-meshpy-triangle-ex4.pdf")
if __name__ == "__main__":
main()
================================================
FILE: examples/write_dolfin.py
================================================
def main():
from meshpy import triangle
info = triangle.MeshInfo()
info.set_points([(1.5, 1), (-1.2, 1), (-1, -1), (1, -1)])
info.set_facets([(0, 1), (1, 2), (2, 3), (3, 0)])
mesh = triangle.build(info, max_volume=1e-3, min_angle=25)
print(
f"""
"""
)
for i, pt in enumerate(mesh.points):
print(f'')
print(
f"""
"""
)
for i, element in enumerate(mesh.elements):
print(
f''
)
print(
"""
"""
)
if __name__ == "__main__":
main()
================================================
FILE: meshpy/__init__.py
================================================
from importlib import metadata
__version__ = metadata.version("meshpy")
================================================
FILE: meshpy/common.py
================================================
class _Table:
def __init__(self):
self.Rows = []
def add_row(self, row):
self.Rows.append([str(i) for i in row])
def __str__(self):
columns = len(self.Rows[0])
col_widths = [max(len(row[i]) for row in self.Rows)
for i in range(columns)]
lines = [
" ".join([cell.ljust(col_width)
for cell, col_width in zip(row, col_widths, strict=True)])
for row in self.Rows]
return "\n".join(lines)
def _linebreak_list(list, per_line=10, pad=None):
def format(s):
if pad is None:
return str(s)
else:
return str(s).rjust(pad)
result = ""
while len(list) > per_line:
result += " ".join(format(ln) for ln in list[:per_line]) + "\n"
list = list[per_line:]
return result + " ".join(format(ln) for ln in list)
class MeshInfoBase:
@property
def face_vertex_indices_to_face_marker(self):
try:
return self._fvi2fm
except AttributeError:
result = {}
for i, face in enumerate(self.faces):
result[frozenset(face)] = self.face_markers[i]
self._fvi2fm = result
return result
def set_points(self, points, point_markers=None):
if point_markers is not None:
assert len(point_markers) == len(points)
self.points.resize(len(points))
for i, pt in enumerate(points):
self.points[i] = pt
if point_markers is not None:
self.point_markers.setup()
for i, mark in enumerate(point_markers):
self.point_markers[i] = mark
def set_holes(self, hole_starts):
self.holes.resize(len(hole_starts))
for i, hole in enumerate(hole_starts):
self.holes[i] = hole
def write_neu(self, outfile, bc=None, periodicity=None,
description="MeshPy Output"):
"""Write the mesh out in (an approximation to) Gambit neutral mesh format.
outfile is a file-like object opened for writing.
bc is a dictionary mapping single face markers (or frozensets of them)
to a tuple (bc_name, bc_code).
periodicity is either a tuple (face_marker, (px,py,..)) giving the
face marker of the periodic boundary and the period in each coordinate
direction (0 if none) or the value None for no periodicity.
"""
if bc is None:
bc = {}
from datetime import datetime, timezone
from meshpy import version
# header --------------------------------------------------------------
outfile.write("CONTROL INFO 2.1.2\n")
outfile.write("** GAMBIT NEUTRAL FILE\n")
outfile.write(f"{description}\n")
outfile.write(f"PROGRAM: MeshPy VERSION: {version}\n")
outfile.write(f"{datetime.now(timezone.utc).ctime()}\n")
bc_markers = list(bc.keys())
if periodicity:
periodic_marker, periods = periodicity
bc_markers.append(periodic_marker)
assert len(self.points)
dim = len(self.points[0])
data = (
("NUMNP", len(self.points)),
("NELEM", len(self.elements)),
("NGRPS", 1),
("NBSETS", len(bc_markers)),
("NDFCD", dim),
("NDFVL", dim),
)
tbl = _Table()
tbl.add_row(key for key, value in data)
tbl.add_row(value for key, value in data)
outfile.write(str(tbl))
outfile.write("\n")
outfile.write("ENDOFSECTION\n")
# nodes ---------------------------------------------------------------
outfile.write("NODAL COORDINATES 2.1.2\n")
for i, pt in enumerate(self.points):
point = " ".join(repr(c) for c in pt)
outfile.write(f"{i + 1} {point}\n")
outfile.write("ENDOFSECTION\n")
# elements ------------------------------------------------------------
outfile.write("ELEMENTS/CELLS 2.1.2\n")
if dim == 2:
eltype = 3
else:
eltype = 6
for i, el in enumerate(self.elements):
element = "".join(f"{p + 1:>8d}" for p in el)
outfile.write(f"{i + 1:>8d}{eltype:>3d}{len(el):>3d} {element}\n")
outfile.write("ENDOFSECTION\n")
# element groups ------------------------------------------------------
outfile.write("ELEMENT GROUP 1.3.0\n")
# FIXME
i = 0
grp_elements = list(range(len(self.elements)))
material = 1
flags = 0
outfile.write(
f"GROUP:{1:>11d} "
f"ELEMENTS:{len(grp_elements):>11d} "
f"MATERIAL:{material!r:>11} "
f"NFLAGS: {flags:>11d}\n")
outfile.write((f"epsilon: {material}\n").rjust(32)) # FIXME
outfile.write("0\n")
outfile.write(_linebreak_list([str(i+1) for i in grp_elements],
pad=8)
+ "\n")
outfile.write("ENDOFSECTION\n")
# boundary conditions -------------------------------------------------
# build mapping face -> (tet, neu_face_index)
face2el = {}
if dim == 2:
for ti, el in enumerate(self.elements):
# Sledge++ Users' Guide, figure 4
faces = [
frozenset([el[0], el[1]]),
frozenset([el[1], el[2]]),
frozenset([el[2], el[0]]),
]
for fi, face in enumerate(faces):
face2el.setdefault(face, []).append((ti, fi+1))
elif dim == 3:
face2el = {}
for ti, el in enumerate(self.elements):
# Sledge++ Users' Guide, figure 5
faces = [
frozenset([el[1], el[0], el[2]]),
frozenset([el[0], el[1], el[3]]),
frozenset([el[1], el[2], el[3]]),
frozenset([el[2], el[0], el[3]]),
]
for fi, face in enumerate(faces):
face2el.setdefault(face, []).append((ti, fi+1))
else:
raise ValueError(f"invalid number of dimensions: {dim}")
# actually output bc sections
if not self.faces.allocated:
from warnings import warn
warn("no exterior faces in mesh data structure, not writing "
"boundary conditions", stacklevel=2)
else:
# requires -f option in tetgen, -e in triangle
for bc_marker in bc_markers:
if isinstance(bc_marker, frozenset):
face_indices = [i
for i, face in enumerate(self.faces)
if self.face_markers[i] in bc_marker]
else:
face_indices = [i
for i, face in enumerate(self.faces)
if bc_marker == self.face_markers[i]]
if not face_indices:
continue
outfile.write("BOUNDARY CONDITIONS 2.1.2\n")
if bc_marker in bc:
# regular BC
bc_name, bc_code = bc[bc_marker]
outfile.write(
f"{bc_name:>32}{1:>8d}{len(face_indices):>8d}"
f"{0:>8d}{bc_code:>8d}\n"
)
else:
# periodic BC
periods_repr = " ".join(repr(p) for p in periods)
outfile.write(
f"periodic{periods_repr}{len(face_indices):>8d}{0:>8d}{0:>8d}\n"
)
for fi in face_indices:
face_nodes = frozenset(self.faces[fi])
adj_el = face2el[face_nodes]
assert len(adj_el) == 1
el_index, el_face_number = adj_el[0]
outfile.write(
f"{el_index + 1:>10d}{eltype:>5d}{el_face_number:>5d}\n")
outfile.write("ENDOFSECTION\n")
outfile.close()
# FIXME curved boundaries?
# FIXME proper element group support
def dump_array(name, array):
print(f"array {name}: {len(array)} elements, {array.unit} values per element")
if len(array) == 0 or array.unit == 0:
return
try:
array[0]
except RuntimeError:
print(" not allocated")
return
for i, entry in enumerate(array):
if isinstance(entry, list):
value = ",".join(str(sub) for sub in entry)
print(f" {i}: {value}")
else:
print(f" {i}: {entry}")
================================================
FILE: meshpy/geometry.py
================================================
__doc__ = """
Geometry builder
----------------
.. autoclass:: GeometryBuilder
Geometries
----------
These functions are designed so that their output can be splat-passed to
:meth:`GeometryBuilder.add_geometry`::
builder = GeometryBuilder()
builder.add_geometry(*make_ball(10))
.. autoclass:: Marker
:members:
:undoc-members:
.. autofunction:: make_box
.. autofunction:: make_circle
.. autofunction:: make_ball
.. autofunction:: make_cylinder
Extrusions and surfaces of revolution
-------------------------------------
.. data:: EXT_OPEN
.. data:: EXT_CLOSED_IN_RZ
.. autofunction:: generate_extrusion
.. autofunction:: generate_surface_of_revolution
"""
import numpy as np
# {{{ geometry building
def bounding_box(points):
return (
np.asarray(np.min(points, axis=0), dtype=np.float64),
np.asarray(np.max(points, axis=0), dtype=np.float64))
def is_multi_polygon(facets):
if not len(facets):
return False
try:
facets[0][0][0] # facet 0, poly 0, point 0
except TypeError:
# pure python raises this
return False
except IndexError:
# numpy raises this
return False
else:
return True
def offset_point_indices(facets, offset):
if is_multi_polygon(facets):
return [[tuple(p_i+offset for p_i in poly)
for poly in facet]
for facet in facets]
else:
return [tuple(p_i+offset for p_i in facet) for facet in facets]
class GeometryBuilder:
"""
.. automethod:: add_geometry
.. automethod:: set
.. automethod:: wrap_in_box
.. automethod:: bounding_box
.. automethod:: center
.. automethod:: apply_transform
"""
def __init__(self):
self.points = []
self.facets = []
self.facet_hole_starts = None
self.facet_markers = None
self.point_markers = None
def add_geometry(self, points, facets, facet_hole_starts=None,
facet_markers=None, point_markers=None):
if isinstance(facet_markers, int):
facet_markers = len(facets) * [facet_markers]
if facet_hole_starts and not self.facet_hole_starts:
self.facet_hole_starts = len(self.facets) * []
if facet_markers and not self.facet_markers:
self.facet_markers = len(self.facets) * [0]
if point_markers and not self.point_markers:
self.point_markers = len(self.points) * [0]
if not facet_hole_starts and self.facet_hole_starts:
facet_hole_starts = len(facets) * [[]]
if not facet_markers and self.facet_markers:
facet_markers = len(facets) * [0]
if not point_markers and self.point_markers:
point_markers = len(points) * [0]
if is_multi_polygon(facets) and not is_multi_polygon(self.facets):
self.facets = [[facet] for facet in self.facets]
if not is_multi_polygon(facets) and is_multi_polygon(self.facets):
facets = [[facet] for facet in facets]
self.facets.extend(offset_point_indices(facets, len(self.points)))
self.points.extend(points)
if facet_markers:
self.facet_markers.extend(facet_markers)
assert len(facets) == len(facet_markers)
if facet_hole_starts:
self.facet_hole_starts.extend(facet_hole_starts)
assert len(facets) == len(facet_hole_starts)
if point_markers:
self.point_markers.extend(point_markers)
assert len(points) == len(point_markers)
def add_cycle(self, points, facet_markers=None, point_markers=None):
def make_facets():
end = len(points)-1
for i in range(end):
yield i, i+1
yield end, 0
self.add_geometry(points, list(make_facets()),
facet_markers=facet_markers,
point_markers=point_markers)
def dimensions(self):
return len(self.points[0])
def set(self, mesh_info):
"""Transfer the built geometry into a :class:`meshpy.triangle.MeshInfo`
or a :class:`meshpy.tet.MeshInfo`.
"""
mesh_info.set_points(self.points, self.point_markers)
if self.facet_hole_starts or is_multi_polygon(self.facets):
mesh_info.set_facets_ex(self.facets,
self.facet_hole_starts, self.facet_markers)
else:
mesh_info.set_facets(self.facets, self.facet_markers)
def mesher_module(self):
dim = self.dimensions()
if dim == 2:
import meshpy.triangle
return meshpy.triangle
elif dim == 3:
import meshpy.tet
return meshpy.tet
else:
raise ValueError(f"unsupported dimensionality {dim}")
def bounding_box(self):
return bounding_box(self.points)
def center(self):
a, b = bounding_box(self.points)
return (a+b)/2
def wrap_in_box(self, distance, subdivisions=None):
"""
:param subdivisions: is a tuple of integers specifying
the number of subdivisions along each axis.
"""
a, b = bounding_box(self.points)
points, facets, _, facet_markers = \
make_box(a-distance, b+distance, subdivisions)
self.add_geometry(points, facets, facet_markers=facet_markers)
def apply_transform(self, f):
self.points = [f(x) for x in self.points]
# }}}
# {{{ actual geometries
class Marker:
MINUS_X = 1
PLUS_X = 2
MINUS_Y = 3
PLUS_Y = 4
MINUS_Z = 5
PLUS_Z = 6
SHELL = 100
FIRST_USER_MARKER = 1000
def make_box(a, b, subdivisions=None):
"""
:param subdivisions: is a tuple of integers specifying
the number of subdivisions along each axis.
"""
a = [float(ai) for ai in a]
b = [float(bi) for bi in b]
assert len(a) == len(b)
dimensions = len(a)
if dimensions == 2:
# CAUTION: Do not change point or facet order here.
# Other code depends on this staying the way it is.
points = [
(a[0], a[1]),
(b[0], a[1]),
(b[0], b[1]),
(a[0], b[1]),
]
facets = [(0, 1), (1, 2), (2, 3), (3, 0)]
facet_markers = [
Marker.MINUS_Y, Marker.PLUS_X,
Marker.PLUS_Y, Marker.MINUS_X]
elif dimensions == 3:
# 7--------6
# /| /|
# 4--------5 | z
# | | | | ^
# | 3------|-2 | y
# |/ |/ |/
# 0--------1 +--->x
points = [
(a[0], a[1], a[2]),
(b[0], a[1], a[2]),
(b[0], b[1], a[2]),
(a[0], b[1], a[2]),
(a[0], a[1], b[2]),
(b[0], a[1], b[2]),
(b[0], b[1], b[2]),
(a[0], b[1], b[2]),
]
facets = [
(0, 1, 2, 3),
(0, 1, 5, 4),
(1, 2, 6, 5),
(7, 6, 2, 3),
(7, 3, 0, 4),
(4, 5, 6, 7)
]
facet_markers = [Marker.MINUS_Z, Marker.MINUS_Y, Marker.PLUS_X,
Marker.PLUS_Y, Marker.MINUS_X, Marker.PLUS_Z]
else:
raise ValueError(f"unsupported dimension count: {len(a)}")
if subdivisions is not None:
if dimensions != 2:
raise NotImplementedError(
"subdivision not implemented for any "
"dimension count other than 2")
from meshpy.triangle import subdivide_facets
points, facets, facet_markers = subdivide_facets(
[subdivisions[0], subdivisions[1],
subdivisions[0], subdivisions[1]],
points, facets, facet_markers)
return points, facets, None, facet_markers
def make_circle(r, center=(0, 0), subdivisions=40, marker=Marker.SHELL):
def round_trip_connect(seq):
n = len(seq)
return [(i, (i + 1) % n) for i in range(n)]
phi = np.linspace(0, 2*np.pi, num=subdivisions, endpoint=False)
cx, cy = center
x = r*np.cos(phi) + cx
y = r*np.sin(phi) + cy
return ([np.array(pt) for pt in zip(x, y, strict=True)],
round_trip_connect(list(range(subdivisions))),
None,
subdivisions*[marker])
def make_ball(r, subdivisions=10):
from math import cos, pi, sin
dphi = pi/subdivisions
def truncate(my_r):
if abs(my_r) < 1e-9*r:
return 0
else:
return my_r
rz = [(truncate(r*sin(i*dphi)), r*cos(i*dphi)) for i in range(subdivisions+1)]
return generate_surface_of_revolution(
rz, closure=EXT_OPEN, radial_subdiv=subdivisions)
def make_cylinder(radius, height, radial_subdivisions=10,
height_subdivisions=1):
dz = height/height_subdivisions
rz = [(0, 0)] \
+ [(radius, i*dz) for i in range(height_subdivisions+1)] \
+ [(0, height)]
ring_markers = [Marker.MINUS_Z] \
+ ((height_subdivisions)*[Marker.SHELL]) \
+ [Marker.PLUS_Z]
return generate_surface_of_revolution(rz,
closure=EXT_OPEN, radial_subdiv=radial_subdivisions,
ring_markers=ring_markers)
# }}}
# {{{ extrusions
def _is_same_float(a, b, threshold=1e-10):
if abs(a) > abs(b):
a, b = b, a
# now abs(a) <= abs(b) always
return abs(b) < threshold or abs(a-b) < threshold*abs(b)
EXT_OPEN = 0
EXT_CLOSED_IN_RZ = 1
def generate_extrusion(rz_points, base_shape, closure=EXT_OPEN,
point_idx_offset=0, ring_point_indices=None,
ring_markers=None, rz_closure_marker=0):
"""Extrude a given connected *base_shape* (a list of (x,y) points)
along the z axis. For each step in the extrusion, the base shape
is multiplied by a radius and shifted in the z direction. Radius
and z offset are given by *rz_points*, which is a list of
(r, z) tuples.
Returns ``(points, facets, facet_holestarts, markers)``, where *points* is
a list of (3D) points and facets is a list of polygons. Each polygon is, in
turn, represented by a tuple of indices into *points*. If
*point_idx_offset* is not zero, these indices start at that number.
*markers* is a list equal in length to *facets*, each specifying the facet
marker of that facet. *facet_holestarts* is also equal in length to
*facets*, each element is a list of hole starting points for the
corresponding facet.
Use :meth:`~meshpy.tet.MeshInfo.set_facets_ex` to add the extrusion to a
:class:`~meshpy.tet.MeshInfo` structure.
The extrusion proceeds by generating quadrilaterals connecting each
ring. If any given radius in *rz_points* is 0, triangle fans are
produced instead of quads to provide non-degenerate closure.
If *closure* is :data:`EXT_OPEN`, no efforts are made to put end caps on the
extrusion.
If *closure* is :data:`EXT_CLOSED_IN_RZ`, then a torus-like structure
is assumed and the last ring is just connected to the first.
If *ring_markers* is not None, it is an list of markers added to each
ring. There should be len(rz_points)-1 entries in this list.
If rings are added because of closure options, they receive the
corresponding *XXX_closure_marker*. If *facet_markers* is given, this function
returns (points, facets, markers), where markers is is a list containing
a marker for each generated facet. Unspecified markers generally
default to 0.
If *ring_point_indices* is given, it must be a list of the same
length as *rz_points*. Each entry in the list may either be None,
or a list of point indices. This list must contain the same number
of points as the *base_shape*; it is taken as the indices of
pre-existing points that are to be used for the given ring, instead
of generating new points.
"""
assert len(rz_points) > 0
if ring_markers is not None:
assert len(rz_points) == len(ring_markers)+1
def get_ring(ring_idx):
try:
return rings[ring_idx]
except KeyError:
# need to generate fresh ring, continue
pass
p_indices = None
if ring_point_indices is not None:
p_indices = ring_point_indices[ring_idx]
first_idx = point_idx_offset+len(points)
r, z = rz_points[ring_idx]
if r == 0:
p_indices = (first_idx,)
points.append((0, 0, z))
else:
p_indices = tuple(range(first_idx, first_idx+len(base_shape)))
points.extend([(x*r, y*r, z) for (x, y) in base_shape])
rings[ring_idx] = p_indices
return p_indices
def pair_with_successor(ln):
n = len(ln)
return [(ln[i], ln[(i+1) % n]) for i in range(n)]
def add_polygons(new_polys, marker):
"""Add several new facets, each polygon in new_polys corresponding
to a new facet.
"""
facets.extend([poly] for poly in new_polys)
markers.extend(len(new_polys)*[marker])
holelists.extend(len(new_polys)*[[]])
def add_facet(facet_polygons, holestarts, marker):
"""Add a single facet, with each polygon in *facet_polygons*
belonging to a single facet.
"""
facets.append(facet_polygons)
markers.append(marker)
holelists.append(holestarts)
def connect_ring(ring1_idx, ring2_idx, marker):
r1, z1 = rz_points[ring1_idx]
r2, z2 = rz_points[ring2_idx]
if _is_same_float(z2, z1):
assert not _is_same_float(r1, r2)
# we're moving purely outward--this doesn't need fans, only plane
# surfaces. Special casing this leads to more freedom for TetGen
# and hence better meshes.
if r1 == 0:
# make opening surface
if r2 != 0:
add_polygons([get_ring(ring2_idx)], marker=marker)
elif r2 == 0:
# make closing surface
add_polygons([get_ring(ring1_idx)], marker=marker)
else:
# make single-surface interface with hole
add_facet([
get_ring(ring1_idx),
get_ring(ring2_idx),
],
holestarts=[(0, 0, z1)], marker=marker)
else:
ring1 = get_ring(ring1_idx)
ring2 = get_ring(ring2_idx)
if r1 == 0:
# make opening fan
assert len(ring1) == 1
start_pt = ring1[0]
if r2 != 0:
add_polygons(
[(start_pt, succ, pt)
for pt, succ in pair_with_successor(ring2)],
marker=marker)
elif r2 == 0:
# make closing fan
assert len(ring2) == 1
end_pt = ring2[0]
add_polygons(
[(pt, succ, end_pt)
for pt, succ in pair_with_successor(ring1)],
marker=marker)
else:
# make quad strip
pairs1 = pair_with_successor(ring1)
pairs2 = pair_with_successor(ring2)
add_polygons(
[(a, b, c, d)
for ((a, b), (d, c)) in zip(pairs1, pairs2, strict=True)],
marker=marker)
points = []
facets = []
markers = []
holelists = []
rings = {}
# pre-populate ring dict with ring_point_indices
if ring_point_indices is not None:
for i, ring_points in enumerate(ring_point_indices):
if ring_points is not None:
assert isinstance(ring_points, tuple)
if rz_points[i][0] == 0:
assert len(ring_points) == 1
else:
assert len(ring_points) == len(base_shape), (
f"Ring points length ({len(ring_points)}) does not "
f"match base shape length ({len(base_shape)})")
rings[i] = ring_points
for i in range(len(rz_points)-1):
if ring_markers is not None:
ring_marker = ring_markers[i]
else:
ring_marker = 0
connect_ring(i, i+1, ring_marker)
if closure == EXT_CLOSED_IN_RZ:
connect_ring(len(rz_points)-1, 0, rz_closure_marker)
return points, facets, holelists, markers
def generate_surface_of_revolution(rz_points,
closure=EXT_OPEN, radial_subdiv=16,
point_idx_offset=0, ring_point_indices=None,
ring_markers=None, rz_closure_marker=0):
from math import cos, pi, sin
dphi = 2*pi/radial_subdiv
base_shape = [(cos(dphi*i), sin(dphi*i)) for i in range(radial_subdiv)]
return generate_extrusion(
rz_points, base_shape, closure=closure,
point_idx_offset=point_idx_offset,
ring_point_indices=ring_point_indices,
ring_markers=ring_markers, rz_closure_marker=rz_closure_marker,
)
# }}}
# vim: foldmethod=marker
================================================
FILE: meshpy/naca.py
================================================
import numpy
class FourDigitsSymmetric:
def __init__(self, thickness, edge_coeff):
self.thickness = thickness
self.edge_coeff = edge_coeff
def __call__(self, x, side):
t = self.thickness
def y_upper(y):
return y
def y_lower(y):
return -y
def x_upper(x):
return x
def x_lower(x):
return x
y = t * 5 * (0.2969 * numpy.sqrt(x) + ((((-self.edge_coeff * x
+ 0.2843) * x - 0.3516) * x) - 0.126) * x)
if side == "upper":
return numpy.array([x_upper(x), y_upper(y)])
elif side == "lower":
return numpy.array([x_lower(x), y_lower(y)])
else:
raise ValueError("Neither upper nor lower side selected in the call.")
class FourDigitsCambered:
def __init__(self, thickness, max_camber, max_camber_pos, edge_coeff):
self.thickness = thickness
self.max_camber = max_camber
self.max_camber_pos = max_camber_pos
self.edge_coeff = edge_coeff
def __call__(self, x, side):
t = self.thickness
m = self.max_camber
p = self.max_camber_pos
def y_upper(y_c, y, theta):
return y_c + y * numpy.cos(theta)
def y_lower(y_c, y, theta):
return y_c - y * numpy.cos(theta)
def x_upper(x, y, theta):
return x - y * numpy.sin(theta)
def x_lower(x, y, theta):
return x + y * numpy.sin(theta)
y = t * 5 * (0.2969 * numpy.sqrt(x) + ((((-self.edge_coeff * x
+ 0.2843) * x - 0.3516) * x) - 0.126) * x)
if x <= p:
y_c = m * x / p ** 2 * (2 * p - x)
theta = numpy.arctan(2 * m / p ** 2 * (p - x))
else:
y_c = m * (1 - x) / (1 - p) ** 2 * (1 + x - 2 * p)
theta = numpy.arctan(m / (1 - p) ** 2 * (2 * p - 2 * x))
if side == "upper":
return numpy.array([x_upper(x, y, theta), y_upper(y_c, y, theta)])
elif side == "lower":
return numpy.array([x_lower(x, y, theta), y_lower(y_c, y, theta)])
else:
raise ValueError("Neither upper nor lower side selected in the call.")
class FiveDigits:
def __init__(self, thickness, m, k1, edge_coeff):
self.thickness = thickness
self.edge_coeff = edge_coeff
self.m = m
self.k1 = k1
def __call__(self, x, side):
t = self.thickness
m = self.m
k1 = self.k1
def y_upper(y_c, y, theta):
return y_c + y * numpy.cos(theta)
def y_lower(y_c, y, theta):
return y_c - y * numpy.cos(theta)
def x_upper(x, y, theta):
return x - y * numpy.sin(theta)
def x_lower(x, y, theta):
return x + y * numpy.sin(theta)
y = t * 5 * (0.2969 * numpy.sqrt(x) + ((((-self.edge_coeff * x
+ 0.2843) * x - 0.3516) * x) - 0.126) * x)
if x <= m:
y_c = k1 / 6 * x * ((x - 3 * m) * x + m ** 2 * (3 - m))
theta = numpy.arctan(k1 / 6 * ((3 * x - 6 * m) * x
+ m ** 2 * (3 - m)))
else:
y_c = k1 * m ** 3 / 6 * (1 - x)
theta = numpy.arctan(-k1 * m ** 3 / 6)
if side == "upper":
return numpy.array([x_upper(x, y, theta), y_upper(y_c, y, theta)])
elif side == "lower":
return numpy.array([x_lower(x, y, theta), y_lower(y_c, y, theta)])
else:
raise ValueError("Neither upper nor lower side selected in the call.")
def get_naca_points(naca_digits, number_of_points=100,
sharp_trailing_edge=True,
abscissa_map=lambda x: 0.03*x+0.97*x**2,
verbose=False):
"""
Return a list of coordinates of NACA 4-digit and 5-digit series
airfoils.
"""
if verbose:
def explain(*s):
print(" ".join(str(s_i) for s_i in s))
else:
def explain(*s):
pass
explain(f"Airfoil: NACA-{naca_digits}")
if sharp_trailing_edge:
explain("Sharp trailing edge")
edge_coeff = 0.1036
else:
explain("Blunt trailing edge")
edge_coeff = 0.1015
raw_abscissae = numpy.linspace(0, 1, number_of_points, endpoint=True)
abscissae = numpy.empty_like(raw_abscissae)
for i in range(number_of_points):
abscissae[i] = abscissa_map(raw_abscissae[i])
digits_int = int(naca_digits)
if len(naca_digits) == 4:
thickness = (digits_int % 100)
max_camber_pos = (digits_int % 1000) - thickness
max_camber = (digits_int % 10000) - max_camber_pos - thickness
thickness = thickness / 1e2
max_camber_pos = max_camber_pos / 1e3
max_camber = max_camber / 1e5
explain("Thickness:", thickness)
explain("Position of maximum camber:", max_camber_pos)
explain("Maximum camber:", max_camber)
if max_camber == 0 and max_camber_pos == 0:
explain("Symmetric 4-digit airfoil")
points = FourDigitsSymmetric(thickness, edge_coeff)
elif max_camber != 0 and max_camber_pos != 0:
explain("Cambered 4-digit airfoil")
points = FourDigitsCambered(thickness, max_camber,
max_camber_pos, edge_coeff)
else:
raise NotImplementedError(
"You must decide whether your airfoil shall be cambered or not!")
elif len(naca_digits) == 5:
thickness = (digits_int % 100)
max_camber_pos = (digits_int % 10000) - thickness
thickness = thickness / 1e2
max_camber_pos = max_camber_pos / 2e4
explain("Thickness:", thickness)
explain("Position of maximum camber:", max_camber_pos)
identifier = digits_int // 100
if identifier == 210:
m = 0.058
k1 = 361.4
elif identifier == 220:
m = 0.126
k1 = 51.64
elif identifier == 230:
m = 0.2025
k1 = 15.957
elif identifier == 240:
m = 0.29
k1 = 6.643
elif identifier == 250:
m = 0.391
k1 = 3.23
else:
raise NotImplementedError("5-digit series only implemented for "
"the first three digits in 210, 220, 230, 240, 250!")
explain("5-digit airfoil")
points = FiveDigits(thickness, m, k1, edge_coeff)
else:
raise NotImplementedError(
"Only the 4-digit and 5-digit series are implemented!")
points_upper = numpy.zeros((len(abscissae), 2))
points_lower = numpy.zeros((len(abscissae), 2))
for i in range(len(abscissae)):
points_upper[i] = points(abscissae[i], "upper")
points_lower[i] = points(abscissae[i], "lower")
if sharp_trailing_edge:
return list(points_upper)[1:-1] + list(points_lower[::-1])
else:
return list(points_upper)[1:] + list(points_lower[::-1])
def write_points(points, filename):
with open(filename, "w") as file:
for pt in points:
print("\t".join(repr(p_comp) for p_comp in pt), file=file)
def main():
from optparse import OptionParser
parser = OptionParser(usage="%prog AIRFOIL-ID")
parser.add_option("-o", "--output",
help="write ouput to FILE", metavar="FILE")
parser.add_option("-p", "--points", type="int",
help="generate N points", metavar="N")
parser.add_option("-s", "--sharp-trailing-edge", action="store_true")
parser.add_option("-u", "--uniform-distribution", action="store_true")
parser.add_option("-q", "--quiet",
action="store_false", dest="verbose", default=True,
help="Don't print status messages to stdout")
(options, args) = parser.parse_args()
if not args:
parser.print_help()
return
if options.points is None:
options.points = 100
digits = args[0]
points = get_naca_points(digits,
number_of_points=options.points,
sharp_trailing_edge=options.sharp_trailing_edge,
uniform_distribution=options.uniform_distribution,
verbose=options.verbose)
if options.output is None:
options.output = f"naca-{digits}.dat"
print("Output file:", options.output)
write_points(points, options.output)
if __name__ == "__main__":
main()
================================================
FILE: meshpy/ply.py
================================================
from dataclasses import dataclass
@dataclass(frozen=True)
class DataBlock:
properties: list[str]
data: list[str]
def parse_int(it):
return int(next(it))
def parse_float(it):
return float(next(it))
class ListParser:
def __init__(self, len_parser, item_parser):
self.len_parser = len_parser
self.item_parser = item_parser
def __call__(self, it):
return [self.item_parser(it) for i in range(self.len_parser(it))]
def make_parser(it):
tp = next(it)
if tp == "list":
len_parser = make_parser(it)
item_parser = make_parser(it)
return ListParser(len_parser, item_parser)
elif tp in {"float", "double", "float32", "float64"}:
return parse_float
elif tp in {
"char", "uchar", "short", "ushort", "int", "uint",
"int8", "uint8", "int16", "uint16", "int32"
}:
return parse_int
else:
raise ValueError(f"unknown type '{tp}'")
def parse_ply(name):
with open(name) as inf:
lines = [ln.strip().lower() for ln in inf]
assert lines[0] == "ply"
assert lines[1].split() == ["format", "ascii", "1.0"]
i = 2
data_queue = []
# parse header
while lines[i] != "end_header":
words = lines[i].split()
if words[0] == "element":
i += 1
props = []
lsplit = lines[i].split()
while lsplit[0] == "property":
props.append((lsplit[-1], make_parser(iter(lsplit[1:-1]))))
i += 1
lsplit = lines[i].split()
data_queue.append((words[1], int(words[2]), props))
elif words[0] in ["comment", "created"]:
i += 1
else:
raise ValueError("invalid header field")
i += 1 # skip end_header
result = {}
def parse_line(parsers, line):
it = iter(line.split())
return [p(it) for p in parsers]
for dname, line_count, props in data_queue:
prop_names, parsers = list(zip(*props, strict=True))
result[dname] = DataBlock(
properties=prop_names,
data=[parse_line(parsers, ln) for ln in lines[i:i+line_count]])
i += line_count
return result
================================================
FILE: meshpy/tet.py
================================================
import meshpy._internals as internals
from meshpy.common import MeshInfoBase, dump_array
class MeshInfo(internals.TetMeshInfo, MeshInfoBase):
def set_facets(self, facets, markers=None):
"""Set a list of simple, single-polygon factes. Unlike :meth:`set_facets_ex`,
:meth:`set_facets` does not allow hole and only lets you use a single
polygon per facet.
:param facets: a list of facets, where each facet is a single
polygons, represented by a list of point indices.
:param markers: Either None or a list of integers of the same
length as *facets*. Each integer is the facet marker assigned
to its corresponding facet.
:note: When the above says "list", any repeatable iterable
also accepted instead.
"""
if markers:
assert len(markers) == len(facets)
self.facets.resize(len(facets))
for i, vlist in enumerate(facets):
facet = self.facets[i]
polys = facet.polygons
polys.resize(1)
poly = facet.polygons[0]
poly.vertices.resize(len(vlist))
for j, pt_idx in enumerate(vlist):
poly.vertices[j] = pt_idx
if markers:
self.facet_markers.setup()
for i, mark in enumerate(markers):
self.facet_markers[i] = mark
def set_facets_ex(self, facets, facet_holestarts=None, markers=None):
"""Set a list of complicated factes. Unlike :meth:`set_facets`,
:meth:`set_facets_ex` allows holes and multiple polygons per
facet.
:param facets: a list of facets, where each facet is a list
of polygons, and each polygon is represented by a list
of point indices.
:param facet_holestarts: Either None or a list of hole starting points
for each facet. Each facet may have several hole starting points.
The mesh generator starts "eating" a hole into the facet at each
starting point and continues until it hits a polygon specified
in this facet's record in *facets*.
:param markers: Either None or a list of integers of the same
length as *facets*. Each integer is the facet marker assigned
to its corresponding facet.
:note: When the above says "list", any repeatable iterable
also accepted instead.
"""
if markers:
assert len(markers) == len(facets)
if facet_holestarts is not None:
assert len(facet_holestarts) == len(facets)
self.facets.resize(len(facets))
for i_facet, poly_list in enumerate(facets):
facet = self.facets[i_facet]
polys = facet.polygons
polys.resize(len(poly_list))
for i_poly, vertex_list in enumerate(poly_list):
poly = facet.polygons[i_poly]
poly.vertices.resize(len(vertex_list))
for i_point, point in enumerate(vertex_list):
poly.vertices[i_point] = point
if facet_holestarts is not None:
hole_list = facet_holestarts[i_facet]
facet_holes = facet.holes
facet_holes.resize(len(hole_list))
for i_hole, hole_start in enumerate(hole_list):
for i_coordinate, co_value in enumerate(hole_start):
facet_holes[i_hole, i_coordinate] = co_value
if markers:
self.facet_markers.setup()
for i, mark in enumerate(markers):
self.facet_markers[i] = mark
def dump(self):
for name in ["points"]:
dump_array(name, getattr(self, name))
for ifacet, facet in enumerate(self.faces):
print(f"facet {ifacet}:")
for ipolygon, polygon in enumerate(facet.polygons):
vertices = ",".join(str(vi) for vi in polygon.vertices)
print(f" polygon {ipolygon}: vertices [{vertices}]")
def write_vtk(self, filename):
import pyvtk
vtkelements = pyvtk.VtkData(
pyvtk.UnstructuredGrid(
self.points,
tetra=self.elements),
"Mesh")
vtkelements.tofile(filename)
def set_elements(self, elements):
self.elements.resize(len(elements))
for i, element in enumerate(elements):
self.elements[i] = element
def set_element_constraints(self, element_constraints):
self.element_volumes.setup()
for i in range(len(self.element_volumes)):
if i in element_constraints:
self.element_volumes[i] = element_constraints[i]
else:
self.element_volumes[i] = -1
class Options(internals.Options):
def __init__(self, switches, **kwargs):
internals.Options.__init__(self)
if len(switches) == 0:
from warnings import warn
warn("Recommend non-empty 'switches' for crash-free meshing",
stacklevel=2)
self.parse_switches(switches)
self.quiet = 1
for k, v in kwargs.items():
try:
getattr(self, k)
except AttributeError:
raise ValueError(f"invalid option: {k}") from None
else:
setattr(self, k, v)
def tetrahedralize(mesh_info, options):
mesh = MeshInfo()
# restore "C" locale--otherwise tetgen might mis-parse stuff like "a0.01"
try:
import locale
except ImportError:
have_locale = False
else:
have_locale = True
prev_num_locale = locale.getlocale(locale.LC_NUMERIC)
locale.setlocale(locale.LC_NUMERIC, "C")
try:
internals.tetrahedralize(options, mesh_info, mesh)
finally:
# restore previous locale if we've changed it
if have_locale:
locale.setlocale(locale.LC_NUMERIC, prev_num_locale)
return mesh
def build(mesh_info, options=None, verbose=False,
attributes=False, volume_constraints=False, max_volume=None,
diagnose=False, insert_points=None):
if options is None:
options = Options("pq")
if not verbose:
options.quiet = 1
if insert_points is not None:
options.insertaddpoints = 1
if attributes:
options.regionattrib = 1
if volume_constraints:
options.varvolume = 1
if max_volume:
options.fixedvolume = 1
options.maxvolume = max_volume
if diagnose:
options.diagnose = 1
return tetrahedralize(mesh_info, options)
================================================
FILE: meshpy/tools.py
================================================
def uniform_refine_triangles(points, elements, factor=2):
new_points = points[:]
new_elements = []
old_face_to_new_faces = {}
face_point_dict = {}
points_per_edge = factor+1
def get_refined_face(a, b):
if a > b:
a, b = b, a
flipped = True
else:
flipped = False
try:
face_points = face_point_dict[a, b]
except KeyError:
a_pt, b_pt = (points[idx] for idx in (a, b))
dx = (b_pt - a_pt)/factor
# build subdivided facet
face_points = [a]
for i in range(1, points_per_edge-1):
face_points.append(len(new_points))
new_points.append(a_pt + dx*i)
face_points.append(b)
face_point_dict[a, b] = face_points
# build old_face_to_new_faces
old_face_to_new_faces[frozenset([a, b])] = [
(face_points[i], face_points[i+1])
for i in range(factor)]
if flipped:
return face_points[::-1]
else:
return face_points
for a, b, c in elements:
a_pt, b_pt, c_pt = (points[idx] for idx in (a, b, c))
dr = (b_pt - a_pt)/factor
ds = (c_pt - a_pt)/factor
ab_refined, bc_refined, ac_refined = (
get_refined_face(*pt_indices)
for pt_indices in [(a, b), (b, c), (a, c)])
el_point_dict = {}
# fill out edges of el_point_dict
for i in range(points_per_edge):
el_point_dict[i, 0] = ab_refined[i]
el_point_dict[0, i] = ac_refined[i]
el_point_dict[points_per_edge-1-i, i] = bc_refined[i]
# fill out interior of el_point_dict
for i in range(1, points_per_edge-1):
for j in range(1, points_per_edge-1-i):
el_point_dict[i, j] = len(new_points)
new_points.append(a_pt + dr*i + ds*j)
# generate elements
for i in range(points_per_edge-1):
for j in range(points_per_edge-1-i):
new_elements.append((
el_point_dict[i, j],
el_point_dict[i+1, j],
el_point_dict[i, j+1],
))
if i+1+j+1 <= factor:
new_elements.append((
el_point_dict[i+1, j+1],
el_point_dict[i+1, j],
el_point_dict[i, j+1],
))
from meshpy.triangle import MeshInfo
mi = MeshInfo()
mi.set_points(new_points)
mi.elements.resize(len(new_elements))
for i, el in enumerate(new_elements):
mi.elements[i] = el
from meshpy.triangle import write_gnuplot_mesh
write_gnuplot_mesh("mesh.dat", mi)
return new_points, new_elements, old_face_to_new_faces
def make_swizzle_matrix(spec):
import numpy
axes = ["x", "y", "z"]
mapping = {axis: axis for axis in axes}
for one_spec in spec.split(","):
import_axis, final_axis = one_spec.split(":")
mapping[import_axis] = final_axis
assert set(mapping.keys()) == set(axes), \
"axis mapping not complete"
assert {axis.lstrip("-") for axis in mapping.values()} == set(axes), \
"Axis mapping not onto"
n = len(axes)
result = numpy.zeros((n, n), dtype=int)
for imp_axis, final_axis in mapping.items():
imp_axis = axes.index(imp_axis)
sign = 1
while final_axis.startswith("-"):
sign *= -1
final_axis = final_axis[1:]
final_axis = axes.index(final_axis)
result[final_axis, imp_axis] = sign
return result
================================================
FILE: meshpy/triangle.py
================================================
from typing import ClassVar
import meshpy._internals as internals
from meshpy.common import MeshInfoBase, dump_array
class MeshInfo(internals.TriMeshInfo, MeshInfoBase):
_constituents: ClassVar[list[str]] = [
"points", "point_attributes", "point_markers",
"elements", "element_attributes", "element_volumes",
"neighbors",
"facets", "facet_markers",
"holes",
"regions",
"faces", "face_markers",
"normals",
]
def __getstate__(self):
return self.number_of_point_attributes, \
self.number_of_element_attributes, \
[(name, getattr(self, name)) for name in self._constituents]
def __setstate__(self, xxx_todo_changeme):
(p_attr_count, e_attr_count, state) = xxx_todo_changeme
self.number_of_point_attributes = p_attr_count
self.number_of_element_attributes = e_attr_count
for name, array in state:
if name not in self._constituents:
raise RuntimeError("Unknown constituent during unpickling")
dest_array = getattr(self, name)
if array is None:
dest_array.deallocate()
else:
if len(dest_array) != len(array):
dest_array.resize(len(array))
if not dest_array.allocated and len(array) > 0:
dest_array.setup()
for i, tup in enumerate(array):
for j, v in enumerate(tup):
dest_array[i, j] = v
def set_facets(self, facets, facet_markers=None):
self.facets.resize(len(facets))
for i, facet in enumerate(facets):
self.facets[i] = facet
if facet_markers is not None:
self.facet_markers.setup()
for i, mark in enumerate(facet_markers):
self.facet_markers[i] = mark
def dump(self):
for name in self._constituents:
dump_array(name, getattr(self, name))
def subdivide_facets(subdivisions, points, facets, facet_markers=None):
"""Return a new facets array in which the original facets are
each subdivided into C{subdivisions} subfacets.
This routine is useful if you have to prohibit the insertion of Steiner
points on the boundary of your triangulation to allow the mesh to conform
either to itself periodically or another given mesh. In this case, you may
use this routine to create the necessary resolution along the boundary
in a predefined way.
@arg subdivisions: Either an C{int}, indicating a uniform number of subdivisions
throughout, or a list of the same length as C{facets}, specifying a subdivision
count for each individual facet.
@arg points: A list of points referred to from the facets list.
@arg facets: The list of old facets, in the form C{[(p1, p2), (p3,p4), ...]}.
@arg facet_markers: Either C{None} or a list of facet markers of the same length
as C{facets}.
@return: The new tuple C{(new_points, new_facets)}.
(Or C{(new_points, new_facets, new_facet_markers)} if C{facet_markers} is not
C{None}.)
"""
def intermediate_points(pa, pb, n):
for i in range(1, n):
tau = i/n
yield [pai*(1-tau) + tau*pbi for pai, pbi in zip(pa, pb, strict=True)]
if isinstance(subdivisions, int):
from itertools import repeat
subdiv_it = repeat(subdivisions, len(facets))
else:
assert len(facets) == len(subdivisions)
subdiv_it = subdivisions.__iter__()
new_points = points[:]
new_facets = []
if facet_markers is not None:
assert len(facets) == len(facet_markers)
new_facet_markers = []
for facet_idx, ((pidx_a, pidx_b), subdiv) in enumerate(
zip(facets, subdiv_it, strict=True)):
facet_points = [pidx_a]
for p in intermediate_points(points[pidx_a], points[pidx_b], subdiv):
facet_points.append(len(new_points))
new_points.append(p)
facet_points.append(pidx_b)
for i, p1 in enumerate(facet_points[:-1]):
p2 = facet_points[i+1]
new_facets.append((p1, p2))
if facet_markers is not None:
new_facet_markers.append(facet_markers[facet_idx])
if facet_markers is not None:
return new_points, new_facets, new_facet_markers
else:
return new_points, new_facets
def build(mesh_info, verbose=False, refinement_func=None, attributes=False,
volume_constraints=False, max_volume=None, allow_boundary_steiner=True,
allow_volume_steiner=True, quality_meshing=True,
generate_edges=None, generate_faces=False, min_angle=None,
mesh_order=None, generate_neighbor_lists=False):
"""Triangulate the domain given in `mesh_info'."""
opts = "pzj"
if quality_meshing:
if min_angle is not None:
opts += f"q{min_angle:f}"
else:
opts += "q"
if mesh_order is not None:
opts += f"o{mesh_order}"
if verbose:
opts += "VV"
else:
opts += "Q"
if attributes:
opts += "A"
if volume_constraints:
opts += "a"
if max_volume:
opts += f"a{max_volume:.20f}"
if refinement_func is not None:
opts += "u"
if generate_edges is not None:
from warnings import warn
warn("generate_edges is deprecated--use generate_faces instead",
stacklevel=2)
generate_faces = generate_edges
if generate_neighbor_lists is not None:
opts += "n"
if generate_faces:
opts += "e"
if not allow_volume_steiner:
opts += "YY"
if allow_boundary_steiner:
raise ValueError("cannot allow boundary Steiner points when volume "
"Steiner points are forbidden")
else:
if not allow_boundary_steiner:
opts += "Y"
# restore "C" locale--otherwise triangle might mis-parse stuff like "a0.01"
try:
import locale
except ImportError:
have_locale = False
else:
have_locale = True
prev_num_locale = locale.getlocale(locale.LC_NUMERIC)
locale.setlocale(locale.LC_NUMERIC, "C")
try:
mesh = MeshInfo()
internals.triangulate(opts, mesh_info, mesh, MeshInfo(), refinement_func)
finally:
# restore previous locale if we've changed it
if have_locale:
locale.setlocale(locale.LC_NUMERIC, prev_num_locale)
return mesh
def refine(input_p, verbose=False, refinement_func=None, quality_meshing=True,
min_angle=None, generate_neighbor_lists=False):
opts = "razj"
if quality_meshing:
if min_angle is not None:
opts += f"q{min_angle:f}"
else:
opts += "q"
if len(input_p.faces) != 0:
opts += "p"
if verbose:
opts += "VV"
else:
opts += "Q"
if refinement_func is not None:
opts += "u"
if generate_neighbor_lists is not None:
opts += "n"
output_p = MeshInfo()
internals.triangulate(opts, input_p, output_p, MeshInfo(), refinement_func)
return output_p
def write_gnuplot_mesh(filename, out_p, facets=False):
with open(filename, "w") as gp_file:
segments = out_p.facets if facets else out_p.elements
for points in segments:
for pt in points:
x, y = out_p.points[pt]
gp_file.write(f"{x:f} {y:f}\n")
x, y = out_p.points[points[0]]
gp_file.write(f"{x:f} {y:f}\n\n")
================================================
FILE: meson.build
================================================
project('meshpy', 'cpp',
license: 'MIT',
meson_version: '>=1.0.0',
default_options : ['warning_level=2', 'cpp_std=c++14'],
)
# {{{ gather includes
py_mod = import('python')
py = py_mod.find_installation(pure: false)
pybind11_dep = dependency('pybind11')
# }}}
# {{{ extension
wrapper_src = [
'src/cpp/foreign_array.hpp',
'src/cpp/foreign_array_wrap.hpp',
'src/cpp/wrapper.cpp',
'src/cpp/wrap_triangle.cpp',
'src/cpp/triangle.h',
'src/cpp/triangle.cpp',
'src/cpp/wrap_tetgen.cpp',
'src/cpp/tetgen.h',
'src/cpp/tetgen.cpp',
'src/cpp/predicates.cpp',
]
wrapper_defines = [
'-DEXTERNAL_TEST',
'-DANSI_DECLARATORS',
'-DTRILIBRARY',
'-DTETLIBRARY',
'-DSELF_CHECK'
]
py.extension_module(
'_internals',
wrapper_src,
dependencies : [pybind11_dep],
c_args: wrapper_defines,
cpp_args: wrapper_defines,
subdir: 'meshpy',
install: true,
)
py.install_sources([
'meshpy/common.py',
'meshpy/geometry.py',
'meshpy/__init__.py',
'meshpy/naca.py',
'meshpy/ply.py',
'meshpy/tet.py',
'meshpy/tools.py',
'meshpy/triangle.py',
],
subdir: 'meshpy'
)
# }}}
================================================
FILE: patches/mk-patch
================================================
#! /bin/sh
diff -u $1/tetgen.h ../src/cpp/tetgen.h
diff -u $1/tetgen.cxx ../src/cpp/tetgen.cpp
diff -u $1/predicates.cxx ../src/cpp/predicates.cpp
================================================
FILE: patches/tetgen-1.4.2.patch
================================================
--- /home/andreas/tetgen1.4.2/tetgen.h 2007-04-16 10:45:13.000000000 -0400
+++ ../src/cpp/tetgen.h 2007-08-19 03:03:45.000000000 -0400
@@ -87,6 +87,7 @@
#include // Math lib: sin(), sqrt(), pow(), ...
#include // Defined type clock_t, constant CLOCKS_PER_SEC.
#include
+#include
///////////////////////////////////////////////////////////////////////////////
// //
@@ -194,10 +195,13 @@
// 'vertexlist' is a list of vertex indices (integers), its length is
// indicated by 'numberofvertices'. The vertex indices are odered in
// either counterclockwise or clockwise way.
- typedef struct {
+ struct polygon : public boost::noncopyable {
int *vertexlist;
int numberofvertices;
- } polygon;
+
+ polygon();
+ ~polygon();
+ };
static void init(polygon* p) {
p->vertexlist = (int *) NULL;
@@ -208,12 +212,15 @@
// to represent a planar straight line graph (PSLG) in two dimension.
// A PSLG contains a list of polygons. It also may conatin holes in it,
// indicated by a list of hole points (their coordinates).
- typedef struct {
+ struct facet {
polygon *polygonlist;
int numberofpolygons;
REAL *holelist;
int numberofholes;
- } facet;
+
+ facet();
+ ~facet();
+ };
static void init(facet* f) {
f->polygonlist = (polygon *) NULL;
@@ -253,12 +260,15 @@
// maps a point in f1 into f2. An array of pbc point pairs are saved
// in 'pointpairlist'. The first point pair is at indices [0] and [1],
// followed by remaining pairs. Two integers per pair.
- typedef struct {
+ struct pbcgroup {
int fmark1, fmark2;
REAL transmat[4][4];
int numberofpointpairs;
int *pointpairlist;
- } pbcgroup;
+
+ pbcgroup();
+ ~pbcgroup();
+ };
public:
@@ -568,6 +578,7 @@
///////////////////////////////////////////////////////////////////////////////
REAL exactinit();
+void exactdeinit();
REAL orient3d(REAL *pa, REAL *pb, REAL *pc, REAL *pd);
REAL insphere(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe);
--- /home/andreas/tetgen1.4.2/tetgen.cxx 2007-04-16 10:45:11.000000000 -0400
+++ ../src/cpp/tetgen.cpp 2007-08-19 03:05:28.000000000 -0400
@@ -49,6 +49,45 @@
// Begin of class 'tetgenio' implementation
//
+tetgenio::polygon::polygon()
+{
+ vertexlist = 0;
+ numberofvertices = 0;
+}
+
+tetgenio::polygon::~polygon()
+{
+ if (vertexlist)
+ delete [] vertexlist;
+}
+
+tetgenio::facet::facet()
+{
+ polygonlist = 0;
+ numberofpolygons = 0;
+ holelist = 0;
+ numberofholes = 0;
+}
+
+tetgenio::facet::~facet()
+{
+ if (polygonlist)
+ delete[] polygonlist;
+ if (holelist)
+ delete[] holelist;
+}
+
+tetgenio::pbcgroup::pbcgroup()
+{
+ numberofpointpairs = 0;
+ pointpairlist = 0;
+}
+
+tetgenio::pbcgroup::~pbcgroup()
+{
+ delete[] pointpairlist;
+}
+
///////////////////////////////////////////////////////////////////////////////
// //
// initialize() Initialize all variables of 'tetgenio'. //
@@ -143,6 +182,7 @@
pbcgroup *pg;
int i, j;
+ using namespace std;
if (pointlist != (REAL *) NULL) {
delete [] pointlist;
}
@@ -187,19 +227,9 @@
}
if (facetlist != (facet *) NULL) {
- for (i = 0; i < numberoffacets; i++) {
- f = &facetlist[i];
- for (j = 0; j < f->numberofpolygons; j++) {
- p = &f->polygonlist[j];
- delete [] p->vertexlist;
- }
- delete [] f->polygonlist;
- if (f->holelist != (REAL *) NULL) {
- delete [] f->holelist;
- }
- }
delete [] facetlist;
}
+
if (facetmarkerlist != (int *) NULL) {
delete [] facetmarkerlist;
}
@@ -217,12 +247,6 @@
delete [] segmentconstraintlist;
}
if (pbcgrouplist != (pbcgroup *) NULL) {
- for (i = 0; i < numberofpbcgroups; i++) {
- pg = &(pbcgrouplist[i]);
- if (pg->pointpairlist != (int *) NULL) {
- delete [] pg->pointpairlist;
- }
- }
delete [] pbcgrouplist;
}
if (vpointlist != (REAL *) NULL) {
@@ -34887,6 +34911,7 @@
if (b->metric) {
delete m.bgm;
}
+ exactdeinit();
}
#ifndef TETLIBRARY
--- /home/andreas/tetgen1.4.2/predicates.cxx 2007-04-16 10:45:04.000000000 -0400
+++ ../src/cpp/predicates.cpp 2007-07-19 20:55:24.000000000 -0400
@@ -113,6 +113,10 @@
/* */
/*****************************************************************************/
+#if defined(__linux__) && defined(__i386__)
+ #define LINUX 1
+#endif
+
#include
#include
#include
@@ -149,8 +153,8 @@
/* which is disastrously slow. A faster way on IEEE machines might be to */
/* mask the appropriate bit, but that's difficult to do in C. */
-#define Absolute(a) ((a) >= 0.0 ? (a) : -(a))
-/* #define Absolute(a) fabs(a) */
+/* #define Absolute(a) ((a) >= 0.0 ? (a) : -(a))*/
+#define Absolute(a) fabs(a)
/* Many of the operations are broken up into two pieces, a main part that */
/* performs an approximate operation, and a "tail" that computes the */
@@ -660,6 +664,8 @@
/* */
/*****************************************************************************/
+static int previous_cword;
+
REAL exactinit()
{
REAL half;
@@ -676,7 +682,9 @@
_control87(_PC_53, _MCW_PC); /* Set FPU control word for double precision. */
#endif /* not SINGLE */
#endif /* CPU86 */
+
#ifdef LINUX
+ _FPU_GETCW(previous_cword);
#ifdef SINGLE
/* cword = 4223; */
cword = 4210; /* set FPU control word for single precision */
@@ -725,6 +733,13 @@
return epsilon; /* Added by H. Si 30 Juli, 2004. */
}
+void exactdeinit()
+{
+#ifdef LINUX
+ _FPU_SETCW(previous_cword);
+#endif /* LINUX */
+}
+
/*****************************************************************************/
/* */
/* grow_expansion() Add a scalar to an expansion. */
================================================
FILE: patches/tetgen-1.4.3.patch
================================================
--- /home/andreas/pack/tetgen1.4.3/tetgen.h 2009-12-13 16:20:33.000000000 -0500
+++ ../src/cpp/tetgen.h 2010-01-22 19:41:28.590818901 -0500
@@ -87,6 +87,7 @@
#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.
@@ -213,10 +214,13 @@
// Note that the points of the polygon must be given in either counter-
// clockwise or clockwise order and they form a ring, so every two
// consective points forms an edge of the polygon.
- typedef struct {
+ struct polygon : public boost::noncopyable {
int *vertexlist;
int numberofvertices;
- } polygon;
+
+ polygon();
+ ~polygon();
+ };
static void init(polygon* p) {
p->vertexlist = (int *) NULL;
@@ -225,12 +229,15 @@
// The facet data structure. A "facet" describes a facet. Each facet is
// a polygonal region possibly with holes, edges, and points in it.
- typedef struct {
+ struct facet {
polygon *polygonlist;
int numberofpolygons;
REAL *holelist;
int numberofholes;
- } facet;
+
+ facet();
+ ~facet();
+ };
static void init(facet* f) {
f->polygonlist = (polygon *) NULL;
@@ -270,12 +277,15 @@
// maps a point in f1 into f2. An array of pbc point pairs are saved
// in 'pointpairlist'. The first point pair is at indices [0] and [1],
// followed by remaining pairs. Two integers per pair.
- typedef struct {
+ struct pbcgroup {
int fmark1, fmark2;
REAL transmat[4][4];
int numberofpointpairs;
int *pointpairlist;
- } pbcgroup;
+
+ pbcgroup();
+ ~pbcgroup();
+ };
// A callback function for mesh refinement.
typedef bool (* TetSizeFunc)(REAL*, REAL*, REAL*, REAL*, REAL*, REAL);
@@ -549,17 +559,6 @@
}
if (facetlist != (facet *) NULL) {
- for (i = 0; i < numberoffacets; i++) {
- f = &facetlist[i];
- for (j = 0; j < f->numberofpolygons; j++) {
- p = &f->polygonlist[j];
- delete [] p->vertexlist;
- }
- delete [] f->polygonlist;
- if (f->holelist != (REAL *) NULL) {
- delete [] f->holelist;
- }
- }
delete [] facetlist;
}
if (facetmarkerlist != (int *) NULL) {
@@ -579,12 +578,6 @@
delete [] segmentconstraintlist;
}
if (pbcgrouplist != (pbcgroup *) NULL) {
- for (i = 0; i < numberofpbcgroups; i++) {
- pg = &(pbcgrouplist[i]);
- if (pg->pointpairlist != (int *) NULL) {
- delete [] pg->pointpairlist;
- }
- }
delete [] pbcgrouplist;
}
if (vpointlist != (REAL *) NULL) {
@@ -2381,6 +2374,7 @@
///////////////////////////////////////////////////////////////////////////////
REAL exactinit();
+void exactdeinit();
REAL orient3d(REAL *pa, REAL *pb, REAL *pc, REAL *pd);
REAL insphere(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe);
--- /home/andreas/pack/tetgen1.4.3/tetgen.cxx 2009-12-13 16:21:08.000000000 -0500
+++ ../src/cpp/tetgen.cpp 2010-01-22 19:41:28.590818901 -0500
@@ -34,6 +34,45 @@
//// ////
//// ////
+tetgenio::polygon::polygon()
+{
+ vertexlist = 0;
+ numberofvertices = 0;
+}
+
+tetgenio::polygon::~polygon()
+{
+ if (vertexlist)
+ delete [] vertexlist;
+}
+
+tetgenio::facet::facet()
+{
+ polygonlist = 0;
+ numberofpolygons = 0;
+ holelist = 0;
+ numberofholes = 0;
+}
+
+tetgenio::facet::~facet()
+{
+ if (polygonlist)
+ delete[] polygonlist;
+ if (holelist)
+ delete[] holelist;
+}
+
+tetgenio::pbcgroup::pbcgroup()
+{
+ numberofpointpairs = 0;
+ pointpairlist = 0;
+}
+
+tetgenio::pbcgroup::~pbcgroup()
+{
+ delete[] pointpairlist;
+}
+
///////////////////////////////////////////////////////////////////////////////
// //
// load_node_call() Read a list of points from a file. //
@@ -34751,6 +34790,7 @@
if (b->metric) {
delete m.bgm;
}
+ exactdeinit();
}
#ifndef TETLIBRARY
--- /home/andreas/pack/tetgen1.4.3/predicates.cxx 2009-12-13 16:18:56.000000000 -0500
+++ ../src/cpp/predicates.cpp 2010-01-22 19:41:28.576309963 -0500
@@ -113,6 +113,10 @@
/* */
/*****************************************************************************/
+#if defined(__linux__) && defined(__i386__)
+ #define LINUX 1
+#endif
+
#include
#include
#include
@@ -149,8 +153,8 @@
/* which is disastrously slow. A faster way on IEEE machines might be to */
/* mask the appropriate bit, but that's difficult to do in C. */
-#define Absolute(a) ((a) >= 0.0 ? (a) : -(a))
-/* #define Absolute(a) fabs(a) */
+/* #define Absolute(a) ((a) >= 0.0 ? (a) : -(a))*/
+#define Absolute(a) fabs(a)
/* Many of the operations are broken up into two pieces, a main part that */
/* performs an approximate operation, and a "tail" that computes the */
@@ -660,6 +664,8 @@
/* */
/*****************************************************************************/
+static int previous_cword;
+
REAL exactinit()
{
REAL half;
@@ -676,7 +682,9 @@
_control87(_PC_53, _MCW_PC); /* Set FPU control word for double precision. */
#endif /* not SINGLE */
#endif /* CPU86 */
+
#ifdef LINUX
+ _FPU_GETCW(previous_cword);
#ifdef SINGLE
/* cword = 4223; */
cword = 4210; /* set FPU control word for single precision */
@@ -725,6 +733,13 @@
return epsilon; /* Added by H. Si 30 Juli, 2004. */
}
+void exactdeinit()
+{
+#ifdef LINUX
+ _FPU_SETCW(previous_cword);
+#endif /* LINUX */
+}
+
/*****************************************************************************/
/* */
/* grow_expansion() Add a scalar to an expansion. */
================================================
FILE: pyproject.toml
================================================
[build-system]
build-backend = "mesonpy"
requires = [
"meson-python",
"numpy",
"wheel",
"pybind11",
]
[project]
name = "meshpy"
version = "2025.1.1"
description = "Triangular and Tetrahedral Mesh Generator"
readme= "README.rst"
license-files = ["LICENSE"]
authors = [{ name = "Andreas Kloeckner", email = "inform@tiker.net" }]
maintainers = [{ name = "Andreas Kloeckner", email = "inform@tiker.net" }]
requires-python = ">=3.10"
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"Intended Audience :: Other Audience",
"Intended Audience :: Science/Research",
"Natural Language :: English",
"Programming Language :: C++",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Topic :: Multimedia :: Graphics :: 3D Modeling",
"Topic :: Scientific/Engineering",
"Topic :: Scientific/Engineering :: Mathematics",
"Topic :: Scientific/Engineering :: Physics",
"Topic :: Scientific/Engineering :: Visualization",
"Topic :: Software Development :: Libraries",
]
dependencies = [
"numpy",
]
[project.optional-dependencies]
doc = [
"furo",
"sphinx>=4",
"sphinx-copybutton",
]
test = [
"mako",
"pytest",
"ruff",
]
[project.urls]
Documentation = "https://documen.tician.de/meshpy"
Repository = "https://github.com/inducer/meshpy"
[tool.ruff.lint]
preview = true
extend-select = [
"B", # flake8-bugbear
"C", # flake8-comprehensions
"E", # pycodestyle
"F", # pyflakes
"G", # flake8-logging-format
"I", # flake8-isort
"N", # pep8-naming
"NPY", # numpy
"Q", # flake8-quotes
"RUF", # ruff
"UP", # pyupgrade
"W", # pycodestyle
]
extend-ignore = [
"C90", # McCabe complexity
"E226", # missing whitespace around arithmetic operator
"E241", # multiple spaces after comma
"E242", # tab after comma
"E265", # block comment should start with #
"E402", # module level import not at the top of file
]
[tool.ruff.lint.per-file-ignores]
"test/test_*.py" = ["S102"]
"doc/conf.py" = ["S102", "DTZ002"]
"examples/jw_meshtools.py" = ["N802", "N803", "N806"]
"examples/jw_mesh_examples.ipynb" = ["N802", "N803", "N806", "N816"]
[tool.ruff.lint.flake8-quotes]
inline-quotes = "double"
docstring-quotes = "double"
multiline-quotes = "double"
[tool.ruff.lint.isort]
known-local-folder = ["pyfmmlib"]
lines-after-imports = 2
combine-as-imports = true
================================================
FILE: src/cpp/foreign_array.hpp
================================================
#ifndef _HEADER_SEEN_FOREIGN_ARRAY
#define _HEADER_SEEN_FOREIGN_ARRAY
#include
#include
// https://stackoverflow.com/a/44175911
class noncopyable {
public:
noncopyable() = default;
~noncopyable() = default;
private:
noncopyable(const noncopyable&) = delete;
noncopyable& operator=(const noncopyable&) = delete;
};
class tSizeChangeNotifier;
class tSizeChangeNotificationReceiver
{
public:
virtual ~tSizeChangeNotificationReceiver()
{ }
virtual void notifySizeChange(tSizeChangeNotifier *master, unsigned size) = 0;
};
class tSizeChangeNotifier
{
typedef std::vector tNotificationReceiverList;
tNotificationReceiverList NotificationReceivers;
public:
virtual ~tSizeChangeNotifier()
{ }
virtual unsigned size() const = 0;
virtual void setSize(unsigned size)
{
tNotificationReceiverList::iterator first = NotificationReceivers.begin(),
last = NotificationReceivers.end();
while (first != last)
(*first++)->notifySizeChange(this, size);
}
void registerForNotification(tSizeChangeNotificationReceiver *rec)
{
NotificationReceivers.push_back(rec);
}
void unregisterForNotification(tSizeChangeNotificationReceiver *rec)
{
tNotificationReceiverList::iterator first = NotificationReceivers.begin(),
last = NotificationReceivers.end();
while (first != last)
{
if (rec == *first)
{
NotificationReceivers.erase(first);
return;
}
first++;
}
}
};
template
class tReadOnlyForeignArray : public tSizeChangeNotifier, public tSizeChangeNotificationReceiver,
public noncopyable
{
protected:
ElementT *&Contents;
int &NumberOf;
unsigned Unit;
tSizeChangeNotifier *SlaveTo;
bool AssumeOwnership;
public:
typedef ElementT value_type;
tReadOnlyForeignArray(
ElementT *&cts, int &number_of, unsigned unit=1, tSizeChangeNotifier *slave_to=NULL,
bool assume_ownership=false)
: Contents(cts), NumberOf(number_of), Unit(unit), SlaveTo(slave_to),
AssumeOwnership(assume_ownership)
{
if (AssumeOwnership)
Contents = NULL;
if (SlaveTo)
{
SlaveTo->registerForNotification(this);
setSizeInternal(SlaveTo->size());
}
else
{
if (AssumeOwnership)
setSize(0);
}
}
~tReadOnlyForeignArray()
{
if (SlaveTo)
SlaveTo->unregisterForNotification(this);
if (AssumeOwnership)
{
deallocate();
if (!SlaveTo)
NumberOf = 0;
}
}
unsigned size() const
{
return NumberOf;
}
unsigned unit() const
{
return Unit;
}
bool is_allocated()
{
return Contents != NULL;
}
void deallocate()
{
if (Contents != NULL)
delete[] Contents;
Contents = NULL;
}
void setSize(unsigned size)
{
if (SlaveTo)
throw std::runtime_error("sizes of slave arrays cannot be changed");
else
setSizeInternal(size);
}
void setup()
{
if (!SlaveTo)
throw std::runtime_error("cannot setup non-slave array");
else
{
if (!Contents)
setSizeInternal(NumberOf);
}
}
void notifySizeChange(tSizeChangeNotifier *master, unsigned size)
{
if (!SlaveTo)
throw std::runtime_error("non-slave array should not get size notifications");
// only perform size change if we actually existed
if (Contents)
setSizeInternal(size);
}
void setSizeInternal(unsigned size)
{
if (!SlaveTo)
NumberOf = size;
if (Contents != NULL)
free(Contents);
if (size == 0 || Unit == 0)
Contents = NULL;
else
{
Contents = new ElementT[Unit*size];
if (Contents == NULL)
throw std::bad_alloc();
}
tSizeChangeNotifier::setSize(size);
}
/** Set the unit size of the array and reallocate the foreign array.
*/
void setUnit(unsigned unit)
{
if (unit != Unit)
{
Unit = unit;
setSizeInternal(NumberOf);
}
}
/** Set the unit size of the array without reallocating. It is assumed
* that the correct amount of memory has already been allocated.
*/
void fixUnit(unsigned unit)
{
Unit = unit;
}
ElementT &get(unsigned index)
{
if (index >= NumberOf * Unit)
throw std::runtime_error("index out of bounds");
if (!Contents)
throw std::runtime_error("Array unallocated");
return Contents[ index ];
}
ElementT &getSub(unsigned index, unsigned sub_index)
{
return get(index * Unit + sub_index);
}
};
template
class tForeignArray : public tReadOnlyForeignArray
{
typedef tReadOnlyForeignArray super;
public:
tForeignArray(
ElementT *&cts, int &number_of, unsigned unit=1,
tSizeChangeNotifier *slave_to=NULL,
bool assume_ownership=false)
: super(cts, number_of, unit, slave_to, assume_ownership)
{
}
void set(unsigned index, ElementT value)
{
if (index >= this->NumberOf * this->Unit)
throw std::runtime_error("index out of bounds");
if (!this->Contents)
throw std::runtime_error("Array unallocated");
this->Contents[ index ] = value;
}
void setSub(unsigned index, unsigned sub_index, ElementT value)
{
set(index * this->Unit + sub_index, value);
}
tForeignArray &operator=(tForeignArray const &src)
{
if (this->SlaveTo)
assert(src.size() == this->SlaveTo->size());
else
this->setSize(src.size());
this->setUnit(src.Unit);
if (src.Contents)
memcpy(this->Contents, src.Contents, sizeof(ElementT) * this->Unit * src.size());
else
this->deallocate();
return *this;
}
};
#endif
================================================
FILE: src/cpp/foreign_array_wrap.hpp
================================================
#ifndef _HEADER_SEEN_FOREIGN_ARRAY_WRAP
#define _HEADER_SEEN_FOREIGN_ARRAY_WRAP
#include "foreign_array.hpp"
#include
#define PYTHON_ERROR(TYPE, REASON) \
{ \
PyErr_SetString(PyExc_##TYPE, REASON); \
throw pybind11::error_already_set(); \
}
namespace {
/* This wrap helper works as long as the value_type is a plain old data (POD)
* type.
*
* In exchange for this, it nicely wraps the "unit" abstraction provided by
* foreign arrays.
*/
template
struct tPODForeignArrayWrapHelper
{
typedef typename FA::value_type value_type;
static pybind11::object getitem(FA &self, long idx)
{
if (idx < 0) idx += self.size();
if (idx < 0 || idx >= (long) self.size())
PYTHON_ERROR(IndexError, "index out of bounds");
if (self.unit() > 1)
{
pybind11::list l;
for (unsigned i = 0; i(idx[0]);
long i_sub = pybind11::cast(idx[1]);
if (i_main < 0 || i_main >= (long) self.size())
PYTHON_ERROR(IndexError, "index out of bounds");
if (i_sub < 0 || i_sub >= (long) self.unit())
PYTHON_ERROR(IndexError, "subindex out of bounds");
return pybind11::cast(self.getSub(i_main, i_sub));
}
static void setitem(FA &self, long idx, pybind11::object value)
{
if (idx < 0) idx += self.size();
if (idx < 0 || idx >= (long) self.size())
PYTHON_ERROR(IndexError, "index out of bounds");
if (self.unit() > 1)
{
pybind11::sequence value_seq = pybind11::cast(value);
if ((long) self.unit() != len(value))
PYTHON_ERROR(ValueError, "value must be a sequence of length self.unit");
for (size_t i = 0; i(value_seq[i]));
}
else
self.set(idx, pybind11::cast(value));
}
static void setitem_tup(FA &self, pybind11::tuple idx, const value_type &v)
{
if (len(idx) != 2)
PYTHON_ERROR(IndexError, "expected index tuple of length 2");
long i_main = pybind11::cast(idx[0]);
long i_sub = pybind11::cast(idx[1]);
if (i_main < 0 || i_main >= (long) self.size())
PYTHON_ERROR(IndexError, "index out of bounds");
if (i_main < 0 || i_sub >= (long) self.unit())
PYTHON_ERROR(IndexError, "subindex out of bounds");
self.setSub(i_main, i_sub, v);
}
};
/* This wrap helper works for more complicated data structures, for which we
* just ship out internal references--boost::python takes care of life support
* for us.
*
* In exchange for this, it does not allow setting entries or support the unit API.
*/
template
struct tStructureForeignArrayWrapHelper
{
typedef typename FA::value_type value_type;
static value_type &getitem(FA &self, long idx)
{
if (idx < 0) idx += self.size();
if (idx >= (long) self.size()) PYTHON_ERROR(IndexError, "index out of bounds");
return self.get(idx);
}
};
}
template
void exposePODForeignArray(pybind11::module &m,const std::string &name)
{
typedef tForeignArray cl;
typedef tPODForeignArrayWrapHelper w_cl;
pybind11::class_(m, name.c_str())
.def("__len__", &cl::size)
.def("resize", &cl::setSize)
.def("setup", &cl::setup)
.def_property_readonly("unit", &cl::unit)
.def_property_readonly("allocated", &cl::is_allocated)
.def("__getitem__", &w_cl::getitem)
.def("__getitem__", &w_cl::getitem_tup)
.def("__setitem__", &w_cl::setitem)
.def("__setitem__", &w_cl::setitem_tup)
.def("deallocate", &cl::deallocate)
;
}
template
void exposeStructureForeignArray(pybind11::module &m, const std::string &name)
{
typedef tForeignArray cl;
typedef tStructureForeignArrayWrapHelper w_cl;
pybind11::class_(m, name.c_str())
.def("__len__", &cl::size)
.def("resize", &cl::setSize)
.def("setup", &cl::setup)
.def_property_readonly("unit", &cl::unit)
.def_property_readonly("allocated", &cl::is_allocated)
.def("__getitem__", &w_cl::getitem, pybind11::return_value_policy::reference_internal)
.def("deallocate", &cl::deallocate)
;
}
#endif
================================================
FILE: src/cpp/predicates.cpp
================================================
/*****************************************************************************/
/* */
/* Routines for Arbitrary Precision Floating-point Arithmetic */
/* and Fast Robust Geometric Predicates */
/* (predicates.c) */
/* */
/* May 18, 1996 */
/* */
/* Placed in the public domain by */
/* Jonathan Richard Shewchuk */
/* School of Computer Science */
/* Carnegie Mellon University */
/* 5000 Forbes Avenue */
/* Pittsburgh, Pennsylvania 15213-3891 */
/* jrs@cs.cmu.edu */
/* */
/* This file contains C implementation of algorithms for exact addition */
/* and multiplication of floating-point numbers, and predicates for */
/* robustly performing the orientation and incircle tests used in */
/* computational geometry. The algorithms and underlying theory are */
/* described in Jonathan Richard Shewchuk. "Adaptive Precision Floating- */
/* Point Arithmetic and Fast Robust Geometric Predicates." Technical */
/* Report CMU-CS-96-140, School of Computer Science, Carnegie Mellon */
/* University, Pittsburgh, Pennsylvania, May 1996. (Submitted to */
/* Discrete & Computational Geometry.) */
/* */
/* This file, the paper listed above, and other information are available */
/* from the Web page http://www.cs.cmu.edu/~quake/robust.html . */
/* */
/*****************************************************************************/
/*****************************************************************************/
/* */
/* Using this code: */
/* */
/* First, read the short or long version of the paper (from the Web page */
/* above). */
/* */
/* Be sure to call exactinit() once, before calling any of the arithmetic */
/* functions or geometric predicates. Also be sure to turn on the */
/* optimizer when compiling this file. */
/* */
/* */
/* Several geometric predicates are defined. Their parameters are all */
/* points. Each point is an array of two or three floating-point */
/* numbers. The geometric predicates, described in the papers, are */
/* */
/* orient2d(pa, pb, pc) */
/* orient2dfast(pa, pb, pc) */
/* orient3d(pa, pb, pc, pd) */
/* orient3dfast(pa, pb, pc, pd) */
/* incircle(pa, pb, pc, pd) */
/* incirclefast(pa, pb, pc, pd) */
/* insphere(pa, pb, pc, pd, pe) */
/* inspherefast(pa, pb, pc, pd, pe) */
/* */
/* Those with suffix "fast" are approximate, non-robust versions. Those */
/* without the suffix are adaptive precision, robust versions. There */
/* are also versions with the suffices "exact" and "slow", which are */
/* non-adaptive, exact arithmetic versions, which I use only for timings */
/* in my arithmetic papers. */
/* */
/* */
/* An expansion is represented by an array of floating-point numbers, */
/* sorted from smallest to largest magnitude (possibly with interspersed */
/* zeros). The length of each expansion is stored as a separate integer, */
/* and each arithmetic function returns an integer which is the length */
/* of the expansion it created. */
/* */
/* Several arithmetic functions are defined. Their parameters are */
/* */
/* e, f Input expansions */
/* elen, flen Lengths of input expansions (must be >= 1) */
/* h Output expansion */
/* b Input scalar */
/* */
/* The arithmetic functions are */
/* */
/* grow_expansion(elen, e, b, h) */
/* grow_expansion_zeroelim(elen, e, b, h) */
/* expansion_sum(elen, e, flen, f, h) */
/* expansion_sum_zeroelim1(elen, e, flen, f, h) */
/* expansion_sum_zeroelim2(elen, e, flen, f, h) */
/* fast_expansion_sum(elen, e, flen, f, h) */
/* fast_expansion_sum_zeroelim(elen, e, flen, f, h) */
/* linear_expansion_sum(elen, e, flen, f, h) */
/* linear_expansion_sum_zeroelim(elen, e, flen, f, h) */
/* scale_expansion(elen, e, b, h) */
/* scale_expansion_zeroelim(elen, e, b, h) */
/* compress(elen, e, h) */
/* */
/* All of these are described in the long version of the paper; some are */
/* described in the short version. All return an integer that is the */
/* length of h. Those with suffix _zeroelim perform zero elimination, */
/* and are recommended over their counterparts. The procedure */
/* fast_expansion_sum_zeroelim() (or linear_expansion_sum_zeroelim() on */
/* processors that do not use the round-to-even tiebreaking rule) is */
/* recommended over expansion_sum_zeroelim(). Each procedure has a */
/* little note next to it (in the code below) that tells you whether or */
/* not the output expansion may be the same array as one of the input */
/* expansions. */
/* */
/* */
/* If you look around below, you'll also find macros for a bunch of */
/* simple unrolled arithmetic operations, and procedures for printing */
/* expansions (commented out because they don't work with all C */
/* compilers) and for generating random floating-point numbers whose */
/* significand bits are all random. Most of the macros have undocumented */
/* requirements that certain of their parameters should not be the same */
/* variable; for safety, better to make sure all the parameters are */
/* distinct variables. Feel free to send email to jrs@cs.cmu.edu if you */
/* have questions. */
/* */
/*****************************************************************************/
#include
#include
#include
#ifdef CPU86
#include
#endif /* CPU86 */
#ifdef LINUX
#include
#endif /* LINUX */
#include "tetgen.h" // Defines the symbol REAL (float or double).
namespace predicates {
#ifdef USE_CGAL_PREDICATES
#include
typedef CGAL::Exact_predicates_inexact_constructions_kernel cgalEpick;
typedef cgalEpick::Point_3 Point;
cgalEpick cgal_pred_obj;
#endif // #ifdef USE_CGAL_PREDICATES
/* On some machines, the exact arithmetic routines might be defeated by the */
/* use of internal extended precision floating-point registers. Sometimes */
/* this problem can be fixed by defining certain values to be volatile, */
/* thus forcing them to be stored to memory and rounded off. This isn't */
/* a great solution, though, as it slows the arithmetic down. */
/* */
/* To try this out, write "#define INEXACT volatile" below. Normally, */
/* however, INEXACT should be defined to be nothing. ("#define INEXACT".) */
#define INEXACT /* Nothing */
/* #define INEXACT volatile */
/* #define REAL double */ /* float or double */
#define REALPRINT doubleprint
#define REALRAND doublerand
#define NARROWRAND narrowdoublerand
#define UNIFORMRAND uniformdoublerand
/* Which of the following two methods of finding the absolute values is */
/* fastest is compiler-dependent. A few compilers can inline and optimize */
/* the fabs() call; but most will incur the overhead of a function call, */
/* which is disastrously slow. A faster way on IEEE machines might be to */
/* mask the appropriate bit, but that's difficult to do in C. */
//#define Absolute(a) ((a) >= 0.0 ? (a) : -(a))
#define Absolute(a) fabs(a)
/* Many of the operations are broken up into two pieces, a main part that */
/* performs an approximate operation, and a "tail" that computes the */
/* roundoff error of that operation. */
/* */
/* The operations Fast_Two_Sum(), Fast_Two_Diff(), Two_Sum(), Two_Diff(), */
/* Split(), and Two_Product() are all implemented as described in the */
/* reference. Each of these macros requires certain variables to be */
/* defined in the calling routine. The variables `bvirt', `c', `abig', */
/* `_i', `_j', `_k', `_l', `_m', and `_n' are declared `INEXACT' because */
/* they store the result of an operation that may incur roundoff error. */
/* The input parameter `x' (or the highest numbered `x_' parameter) must */
/* also be declared `INEXACT'. */
#define Fast_Two_Sum_Tail(a, b, x, y) \
bvirt = x - a; \
y = b - bvirt
#define Fast_Two_Sum(a, b, x, y) \
x = (REAL) (a + b); \
Fast_Two_Sum_Tail(a, b, x, y)
#define Fast_Two_Diff_Tail(a, b, x, y) \
bvirt = a - x; \
y = bvirt - b
#define Fast_Two_Diff(a, b, x, y) \
x = (REAL) (a - b); \
Fast_Two_Diff_Tail(a, b, x, y)
#define Two_Sum_Tail(a, b, x, y) \
bvirt = (REAL) (x - a); \
avirt = x - bvirt; \
bround = b - bvirt; \
around = a - avirt; \
y = around + bround
#define Two_Sum(a, b, x, y) \
x = (REAL) (a + b); \
Two_Sum_Tail(a, b, x, y)
#define Two_Diff_Tail(a, b, x, y) \
bvirt = (REAL) (a - x); \
avirt = x + bvirt; \
bround = bvirt - b; \
around = a - avirt; \
y = around + bround
#define Two_Diff(a, b, x, y) \
x = (REAL) (a - b); \
Two_Diff_Tail(a, b, x, y)
#define Split(a, ahi, alo) \
c = (REAL) (splitter * a); \
abig = (REAL) (c - a); \
ahi = c - abig; \
alo = a - ahi
#define Two_Product_Tail(a, b, x, y) \
Split(a, ahi, alo); \
Split(b, bhi, blo); \
err1 = x - (ahi * bhi); \
err2 = err1 - (alo * bhi); \
err3 = err2 - (ahi * blo); \
y = (alo * blo) - err3
#define Two_Product(a, b, x, y) \
x = (REAL) (a * b); \
Two_Product_Tail(a, b, x, y)
/* Two_Product_Presplit() is Two_Product() where one of the inputs has */
/* already been split. Avoids redundant splitting. */
#define Two_Product_Presplit(a, b, bhi, blo, x, y) \
x = (REAL) (a * b); \
Split(a, ahi, alo); \
err1 = x - (ahi * bhi); \
err2 = err1 - (alo * bhi); \
err3 = err2 - (ahi * blo); \
y = (alo * blo) - err3
/* Two_Product_2Presplit() is Two_Product() where both of the inputs have */
/* already been split. Avoids redundant splitting. */
#define Two_Product_2Presplit(a, ahi, alo, b, bhi, blo, x, y) \
x = (REAL) (a * b); \
err1 = x - (ahi * bhi); \
err2 = err1 - (alo * bhi); \
err3 = err2 - (ahi * blo); \
y = (alo * blo) - err3
/* Square() can be done more quickly than Two_Product(). */
#define Square_Tail(a, x, y) \
Split(a, ahi, alo); \
err1 = x - (ahi * ahi); \
err3 = err1 - ((ahi + ahi) * alo); \
y = (alo * alo) - err3
#define Square(a, x, y) \
x = (REAL) (a * a); \
Square_Tail(a, x, y)
/* Macros for summing expansions of various fixed lengths. These are all */
/* unrolled versions of Expansion_Sum(). */
#define Two_One_Sum(a1, a0, b, x2, x1, x0) \
Two_Sum(a0, b , _i, x0); \
Two_Sum(a1, _i, x2, x1)
#define Two_One_Diff(a1, a0, b, x2, x1, x0) \
Two_Diff(a0, b , _i, x0); \
Two_Sum( a1, _i, x2, x1)
#define Two_Two_Sum(a1, a0, b1, b0, x3, x2, x1, x0) \
Two_One_Sum(a1, a0, b0, _j, _0, x0); \
Two_One_Sum(_j, _0, b1, x3, x2, x1)
#define Two_Two_Diff(a1, a0, b1, b0, x3, x2, x1, x0) \
Two_One_Diff(a1, a0, b0, _j, _0, x0); \
Two_One_Diff(_j, _0, b1, x3, x2, x1)
#define Four_One_Sum(a3, a2, a1, a0, b, x4, x3, x2, x1, x0) \
Two_One_Sum(a1, a0, b , _j, x1, x0); \
Two_One_Sum(a3, a2, _j, x4, x3, x2)
#define Four_Two_Sum(a3, a2, a1, a0, b1, b0, x5, x4, x3, x2, x1, x0) \
Four_One_Sum(a3, a2, a1, a0, b0, _k, _2, _1, _0, x0); \
Four_One_Sum(_k, _2, _1, _0, b1, x5, x4, x3, x2, x1)
#define Four_Four_Sum(a3, a2, a1, a0, b4, b3, b1, b0, x7, x6, x5, x4, x3, x2, \
x1, x0) \
Four_Two_Sum(a3, a2, a1, a0, b1, b0, _l, _2, _1, _0, x1, x0); \
Four_Two_Sum(_l, _2, _1, _0, b4, b3, x7, x6, x5, x4, x3, x2)
#define Eight_One_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b, x8, x7, x6, x5, x4, \
x3, x2, x1, x0) \
Four_One_Sum(a3, a2, a1, a0, b , _j, x3, x2, x1, x0); \
Four_One_Sum(a7, a6, a5, a4, _j, x8, x7, x6, x5, x4)
#define Eight_Two_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b1, b0, x9, x8, x7, \
x6, x5, x4, x3, x2, x1, x0) \
Eight_One_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b0, _k, _6, _5, _4, _3, _2, \
_1, _0, x0); \
Eight_One_Sum(_k, _6, _5, _4, _3, _2, _1, _0, b1, x9, x8, x7, x6, x5, x4, \
x3, x2, x1)
#define Eight_Four_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b4, b3, b1, b0, x11, \
x10, x9, x8, x7, x6, x5, x4, x3, x2, x1, x0) \
Eight_Two_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b1, b0, _l, _6, _5, _4, _3, \
_2, _1, _0, x1, x0); \
Eight_Two_Sum(_l, _6, _5, _4, _3, _2, _1, _0, b4, b3, x11, x10, x9, x8, \
x7, x6, x5, x4, x3, x2)
/* Macros for multiplying expansions of various fixed lengths. */
#define Two_One_Product(a1, a0, b, x3, x2, x1, x0) \
Split(b, bhi, blo); \
Two_Product_Presplit(a0, b, bhi, blo, _i, x0); \
Two_Product_Presplit(a1, b, bhi, blo, _j, _0); \
Two_Sum(_i, _0, _k, x1); \
Fast_Two_Sum(_j, _k, x3, x2)
#define Four_One_Product(a3, a2, a1, a0, b, x7, x6, x5, x4, x3, x2, x1, x0) \
Split(b, bhi, blo); \
Two_Product_Presplit(a0, b, bhi, blo, _i, x0); \
Two_Product_Presplit(a1, b, bhi, blo, _j, _0); \
Two_Sum(_i, _0, _k, x1); \
Fast_Two_Sum(_j, _k, _i, x2); \
Two_Product_Presplit(a2, b, bhi, blo, _j, _0); \
Two_Sum(_i, _0, _k, x3); \
Fast_Two_Sum(_j, _k, _i, x4); \
Two_Product_Presplit(a3, b, bhi, blo, _j, _0); \
Two_Sum(_i, _0, _k, x5); \
Fast_Two_Sum(_j, _k, x7, x6)
#define Two_Two_Product(a1, a0, b1, b0, x7, x6, x5, x4, x3, x2, x1, x0) \
Split(a0, a0hi, a0lo); \
Split(b0, bhi, blo); \
Two_Product_2Presplit(a0, a0hi, a0lo, b0, bhi, blo, _i, x0); \
Split(a1, a1hi, a1lo); \
Two_Product_2Presplit(a1, a1hi, a1lo, b0, bhi, blo, _j, _0); \
Two_Sum(_i, _0, _k, _1); \
Fast_Two_Sum(_j, _k, _l, _2); \
Split(b1, bhi, blo); \
Two_Product_2Presplit(a0, a0hi, a0lo, b1, bhi, blo, _i, _0); \
Two_Sum(_1, _0, _k, x1); \
Two_Sum(_2, _k, _j, _1); \
Two_Sum(_l, _j, _m, _2); \
Two_Product_2Presplit(a1, a1hi, a1lo, b1, bhi, blo, _j, _0); \
Two_Sum(_i, _0, _n, _0); \
Two_Sum(_1, _0, _i, x2); \
Two_Sum(_2, _i, _k, _1); \
Two_Sum(_m, _k, _l, _2); \
Two_Sum(_j, _n, _k, _0); \
Two_Sum(_1, _0, _j, x3); \
Two_Sum(_2, _j, _i, _1); \
Two_Sum(_l, _i, _m, _2); \
Two_Sum(_1, _k, _i, x4); \
Two_Sum(_2, _i, _k, x5); \
Two_Sum(_m, _k, x7, x6)
/* An expansion of length two can be squared more quickly than finding the */
/* product of two different expansions of length two, and the result is */
/* guaranteed to have no more than six (rather than eight) components. */
#define Two_Square(a1, a0, x5, x4, x3, x2, x1, x0) \
Square(a0, _j, x0); \
_0 = a0 + a0; \
Two_Product(a1, _0, _k, _1); \
Two_One_Sum(_k, _1, _j, _l, _2, x1); \
Square(a1, _j, _1); \
Two_Two_Sum(_j, _1, _l, _2, x5, x4, x3, x2)
/* splitter = 2^ceiling(p / 2) + 1. Used to split floats in half. */
static REAL splitter;
static REAL epsilon; /* = 2^(-p). Used to estimate roundoff errors. */
/* A set of coefficients used to calculate maximum roundoff errors. */
static REAL resulterrbound;
static REAL ccwerrboundA, ccwerrboundB, ccwerrboundC;
static REAL o3derrboundA, o3derrboundB, o3derrboundC;
static REAL iccerrboundA, iccerrboundB, iccerrboundC;
static REAL isperrboundA, isperrboundB, isperrboundC;
// Options to choose types of geometric computtaions.
// Added by H. Si, 2012-08-23.
static int _use_inexact_arith; // -X option.
static int _use_static_filter; // Default option, disable it by -X1
// Static filters for orient3d() and insphere().
// They are pre-calcualted and set in exactinit().
// Added by H. Si, 2012-08-23.
static REAL o3dstaticfilter;
static REAL ispstaticfilter;
// The following codes were part of "IEEE 754 floating-point test software"
// http://www.math.utah.edu/~beebe/software/ieee/
// The original program was "fpinfo2.c".
double fppow2(int n)
{
double x, power;
x = (n < 0) ? ((double)1.0/(double)2.0) : (double)2.0;
n = (n < 0) ? -n : n;
power = (double)1.0;
while (n-- > 0)
power *= x;
return (power);
}
#ifdef SINGLE
float fstore(float x)
{
return (x);
}
int test_float(int verbose)
{
float x;
int pass = 1;
//(void)printf("float:\n");
if (verbose) {
(void)printf(" sizeof(float) = %2u\n", (unsigned int)sizeof(float));
#ifdef CPU86 //
(void)printf(" FLT_MANT_DIG = %2d\n", FLT_MANT_DIG);
#endif
}
x = (float)1.0;
while (fstore((float)1.0 + x/(float)2.0) != (float)1.0)
x /= (float)2.0;
if (verbose)
(void)printf(" machine epsilon = %13.5e ", x);
if (x == (float)fppow2(-23)) {
if (verbose)
(void)printf("[IEEE 754 32-bit macheps]\n");
} else {
(void)printf("[not IEEE 754 conformant] !!\n");
pass = 0;
}
x = (float)1.0;
while (fstore(x / (float)2.0) != (float)0.0)
x /= (float)2.0;
if (verbose)
(void)printf(" smallest positive number = %13.5e ", x);
if (x == (float)fppow2(-149)) {
if (verbose)
(void)printf("[smallest 32-bit subnormal]\n");
} else if (x == (float)fppow2(-126)) {
if (verbose)
(void)printf("[smallest 32-bit normal]\n");
} else {
(void)printf("[not IEEE 754 conformant] !!\n");
pass = 0;
}
return pass;
}
# else
double dstore(double x)
{
return (x);
}
int test_double(int verbose)
{
double x;
int pass = 1;
// (void)printf("double:\n");
if (verbose) {
(void)printf(" sizeof(double) = %2u\n", (unsigned int)sizeof(double));
#ifdef CPU86 //
(void)printf(" DBL_MANT_DIG = %2d\n", DBL_MANT_DIG);
#endif
}
x = 1.0;
while (dstore(1.0 + x/2.0) != 1.0)
x /= 2.0;
if (verbose)
(void)printf(" machine epsilon = %13.5le ", x);
if (x == (double)fppow2(-52)) {
if (verbose)
(void)printf("[IEEE 754 64-bit macheps]\n");
} else {
(void)printf("[not IEEE 754 conformant] !!\n");
pass = 0;
}
x = 1.0;
while (dstore(x / 2.0) != 0.0)
x /= 2.0;
//if (verbose)
// (void)printf(" smallest positive number = %13.5le ", x);
if (x == (double)fppow2(-1074)) {
//if (verbose)
// (void)printf("[smallest 64-bit subnormal]\n");
} else if (x == (double)fppow2(-1022)) {
//if (verbose)
// (void)printf("[smallest 64-bit normal]\n");
} else {
(void)printf("[not IEEE 754 conformant] !!\n");
pass = 0;
}
return pass;
}
#endif
/*****************************************************************************/
/* */
/* exactinit() Initialize the variables used for exact arithmetic. */
/* */
/* `epsilon' is the largest power of two such that 1.0 + epsilon = 1.0 in */
/* floating-point arithmetic. `epsilon' bounds the relative roundoff */
/* error. It is used for floating-point error analysis. */
/* */
/* `splitter' is used to split floating-point numbers into two half- */
/* length significands for exact multiplication. */
/* */
/* I imagine that a highly optimizing compiler might be too smart for its */
/* own good, and somehow cause this routine to fail, if it pretends that */
/* floating-point arithmetic is too much like real arithmetic. */
/* */
/* Don't change this routine unless you fully understand it. */
/* */
/*****************************************************************************/
static int previous_cword;
void exactinit(int verbose, int noexact, int nofilter, REAL maxx, REAL maxy,
REAL maxz)
{
REAL half;
REAL check, lastcheck;
int every_other;
#ifdef LINUX
int cword;
#endif /* LINUX */
#ifdef CPU86
#error yo
_FPU_GETCW(previous_cword);
#ifdef SINGLE
_control87(_PC_24, _MCW_PC); /* Set FPU control word for single precision. */
#else /* not SINGLE */
_control87(_PC_53, _MCW_PC); /* Set FPU control word for double precision. */
#endif /* not SINGLE */
#endif /* CPU86 */
#ifdef LINUX
#ifdef SINGLE
/* cword = 4223; */
cword = 4210; /* set FPU control word for single precision */
#else /* not SINGLE */
/* cword = 4735; */
cword = 4722; /* set FPU control word for double precision */
#endif /* not SINGLE */
_FPU_SETCW(cword);
#endif /* LINUX */
if (verbose) {
printf(" Initializing robust predicates.\n");
}
#ifdef USE_CGAL_PREDICATES
if (cgal_pred_obj.Has_static_filters) {
printf(" Use static filter.\n");
} else {
printf(" No static filter.\n");
}
#endif // USE_CGAL_PREDICATES
#ifdef SINGLE
test_float(verbose);
#else
test_double(verbose);
#endif
every_other = 1;
half = 0.5;
epsilon = 1.0;
splitter = 1.0;
check = 1.0;
/* Repeatedly divide `epsilon' by two until it is too small to add to */
/* one without causing roundoff. (Also check if the sum is equal to */
/* the previous sum, for machines that round up instead of using exact */
/* rounding. Not that this library will work on such machines anyway. */
do {
lastcheck = check;
epsilon *= half;
if (every_other) {
splitter *= 2.0;
}
every_other = !every_other;
check = 1.0 + epsilon;
} while ((check != 1.0) && (check != lastcheck));
splitter += 1.0;
/* Error bounds for orientation and incircle tests. */
resulterrbound = (3.0 + 8.0 * epsilon) * epsilon;
ccwerrboundA = (3.0 + 16.0 * epsilon) * epsilon;
ccwerrboundB = (2.0 + 12.0 * epsilon) * epsilon;
ccwerrboundC = (9.0 + 64.0 * epsilon) * epsilon * epsilon;
o3derrboundA = (7.0 + 56.0 * epsilon) * epsilon;
o3derrboundB = (3.0 + 28.0 * epsilon) * epsilon;
o3derrboundC = (26.0 + 288.0 * epsilon) * epsilon * epsilon;
iccerrboundA = (10.0 + 96.0 * epsilon) * epsilon;
iccerrboundB = (4.0 + 48.0 * epsilon) * epsilon;
iccerrboundC = (44.0 + 576.0 * epsilon) * epsilon * epsilon;
isperrboundA = (16.0 + 224.0 * epsilon) * epsilon;
isperrboundB = (5.0 + 72.0 * epsilon) * epsilon;
isperrboundC = (71.0 + 1408.0 * epsilon) * epsilon * epsilon;
// Set TetGen options. Added by H. Si, 2012-08-23.
_use_inexact_arith = noexact;
_use_static_filter = !nofilter;
// Calculate the two static filters for orient3d() and insphere() tests.
// 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;
}
if (maxy > maxz) {
half = maxy; maxy = maxz; maxz = half;
}
else if (maxy < maxx) {
half = maxy; maxy = maxx; maxx = half;
}
o3dstaticfilter = 5.1107127829973299e-15 * maxx * maxy * maxz;
ispstaticfilter = 1.2466136531027298e-13 * maxx * maxy * maxz * (maxz * maxz);
}
void exactdeinit()
{
#ifdef CPU86
_FPU_SETCW(previous_cword);
#endif
}
/*****************************************************************************/
/* */
/* grow_expansion() Add a scalar to an expansion. */
/* */
/* Sets h = e + b. See the long version of my paper for details. */
/* */
/* Maintains the nonoverlapping property. If round-to-even is used (as */
/* with IEEE 754), maintains the strongly nonoverlapping and nonadjacent */
/* properties as well. (That is, if e has one of these properties, so */
/* will h.) */
/* */
/*****************************************************************************/
int grow_expansion(int elen, REAL *e, REAL b, REAL *h)
/* e and h can be the same. */
{
REAL Q;
INEXACT REAL Qnew;
int eindex;
REAL enow;
INEXACT REAL bvirt;
REAL avirt, bround, around;
Q = b;
for (eindex = 0; eindex < elen; eindex++) {
enow = e[eindex];
Two_Sum(Q, enow, Qnew, h[eindex]);
Q = Qnew;
}
h[eindex] = Q;
return eindex + 1;
}
/*****************************************************************************/
/* */
/* grow_expansion_zeroelim() Add a scalar to an expansion, eliminating */
/* zero components from the output expansion. */
/* */
/* Sets h = e + b. See the long version of my paper for details. */
/* */
/* Maintains the nonoverlapping property. If round-to-even is used (as */
/* with IEEE 754), maintains the strongly nonoverlapping and nonadjacent */
/* properties as well. (That is, if e has one of these properties, so */
/* will h.) */
/* */
/*****************************************************************************/
int grow_expansion_zeroelim(int elen, REAL *e, REAL b, REAL *h)
/* e and h can be the same. */
{
REAL Q, hh;
INEXACT REAL Qnew;
int eindex, hindex;
REAL enow;
INEXACT REAL bvirt;
REAL avirt, bround, around;
hindex = 0;
Q = b;
for (eindex = 0; eindex < elen; eindex++) {
enow = e[eindex];
Two_Sum(Q, enow, Qnew, hh);
Q = Qnew;
if (hh != 0.0) {
h[hindex++] = hh;
}
}
if ((Q != 0.0) || (hindex == 0)) {
h[hindex++] = Q;
}
return hindex;
}
/*****************************************************************************/
/* */
/* expansion_sum() Sum two expansions. */
/* */
/* Sets h = e + f. See the long version of my paper for details. */
/* */
/* Maintains the nonoverlapping property. If round-to-even is used (as */
/* with IEEE 754), maintains the nonadjacent property as well. (That is, */
/* if e has one of these properties, so will h.) Does NOT maintain the */
/* strongly nonoverlapping property. */
/* */
/*****************************************************************************/
int expansion_sum(int elen, REAL *e, int flen, REAL *f, REAL *h)
/* e and h can be the same, but f and h cannot. */
{
REAL Q;
INEXACT REAL Qnew;
int findex, hindex, hlast;
REAL hnow;
INEXACT REAL bvirt;
REAL avirt, bround, around;
Q = f[0];
for (hindex = 0; hindex < elen; hindex++) {
hnow = e[hindex];
Two_Sum(Q, hnow, Qnew, h[hindex]);
Q = Qnew;
}
h[hindex] = Q;
hlast = hindex;
for (findex = 1; findex < flen; findex++) {
Q = f[findex];
for (hindex = findex; hindex <= hlast; hindex++) {
hnow = h[hindex];
Two_Sum(Q, hnow, Qnew, h[hindex]);
Q = Qnew;
}
h[++hlast] = Q;
}
return hlast + 1;
}
/*****************************************************************************/
/* */
/* expansion_sum_zeroelim1() Sum two expansions, eliminating zero */
/* components from the output expansion. */
/* */
/* Sets h = e + f. See the long version of my paper for details. */
/* */
/* Maintains the nonoverlapping property. If round-to-even is used (as */
/* with IEEE 754), maintains the nonadjacent property as well. (That is, */
/* if e has one of these properties, so will h.) Does NOT maintain the */
/* strongly nonoverlapping property. */
/* */
/*****************************************************************************/
int expansion_sum_zeroelim1(int elen, REAL *e, int flen, REAL *f, REAL *h)
/* e and h can be the same, but f and h cannot. */
{
REAL Q;
INEXACT REAL Qnew;
int index, findex, hindex, hlast;
REAL hnow;
INEXACT REAL bvirt;
REAL avirt, bround, around;
Q = f[0];
for (hindex = 0; hindex < elen; hindex++) {
hnow = e[hindex];
Two_Sum(Q, hnow, Qnew, h[hindex]);
Q = Qnew;
}
h[hindex] = Q;
hlast = hindex;
for (findex = 1; findex < flen; findex++) {
Q = f[findex];
for (hindex = findex; hindex <= hlast; hindex++) {
hnow = h[hindex];
Two_Sum(Q, hnow, Qnew, h[hindex]);
Q = Qnew;
}
h[++hlast] = Q;
}
hindex = -1;
for (index = 0; index <= hlast; index++) {
hnow = h[index];
if (hnow != 0.0) {
h[++hindex] = hnow;
}
}
if (hindex == -1) {
return 1;
} else {
return hindex + 1;
}
}
/*****************************************************************************/
/* */
/* expansion_sum_zeroelim2() Sum two expansions, eliminating zero */
/* components from the output expansion. */
/* */
/* Sets h = e + f. See the long version of my paper for details. */
/* */
/* Maintains the nonoverlapping property. If round-to-even is used (as */
/* with IEEE 754), maintains the nonadjacent property as well. (That is, */
/* if e has one of these properties, so will h.) Does NOT maintain the */
/* strongly nonoverlapping property. */
/* */
/*****************************************************************************/
int expansion_sum_zeroelim2(int elen, REAL *e, int flen, REAL *f, REAL *h)
/* e and h can be the same, but f and h cannot. */
{
REAL Q, hh;
INEXACT REAL Qnew;
int eindex, findex, hindex, hlast;
REAL enow;
INEXACT REAL bvirt;
REAL avirt, bround, around;
hindex = 0;
Q = f[0];
for (eindex = 0; eindex < elen; eindex++) {
enow = e[eindex];
Two_Sum(Q, enow, Qnew, hh);
Q = Qnew;
if (hh != 0.0) {
h[hindex++] = hh;
}
}
h[hindex] = Q;
hlast = hindex;
for (findex = 1; findex < flen; findex++) {
hindex = 0;
Q = f[findex];
for (eindex = 0; eindex <= hlast; eindex++) {
enow = h[eindex];
Two_Sum(Q, enow, Qnew, hh);
Q = Qnew;
if (hh != 0) {
h[hindex++] = hh;
}
}
h[hindex] = Q;
hlast = hindex;
}
return hlast + 1;
}
/*****************************************************************************/
/* */
/* fast_expansion_sum() Sum two expansions. */
/* */
/* Sets h = e + f. See the long version of my paper for details. */
/* */
/* If round-to-even is used (as with IEEE 754), maintains the strongly */
/* nonoverlapping property. (That is, if e is strongly nonoverlapping, h */
/* will be also.) Does NOT maintain the nonoverlapping or nonadjacent */
/* properties. */
/* */
/*****************************************************************************/
int fast_expansion_sum(int elen, REAL *e, int flen, REAL *f, REAL *h)
/* h cannot be e or f. */
{
REAL Q;
INEXACT REAL Qnew;
INEXACT REAL bvirt;
REAL avirt, bround, around;
int eindex, findex, hindex;
REAL enow, fnow;
enow = e[0];
fnow = f[0];
eindex = findex = 0;
if ((fnow > enow) == (fnow > -enow)) {
Q = enow;
enow = e[++eindex];
} else {
Q = fnow;
fnow = f[++findex];
}
hindex = 0;
if ((eindex < elen) && (findex < flen)) {
if ((fnow > enow) == (fnow > -enow)) {
Fast_Two_Sum(enow, Q, Qnew, h[0]);
enow = e[++eindex];
} else {
Fast_Two_Sum(fnow, Q, Qnew, h[0]);
fnow = f[++findex];
}
Q = Qnew;
hindex = 1;
while ((eindex < elen) && (findex < flen)) {
if ((fnow > enow) == (fnow > -enow)) {
Two_Sum(Q, enow, Qnew, h[hindex]);
enow = e[++eindex];
} else {
Two_Sum(Q, fnow, Qnew, h[hindex]);
fnow = f[++findex];
}
Q = Qnew;
hindex++;
}
}
while (eindex < elen) {
Two_Sum(Q, enow, Qnew, h[hindex]);
enow = e[++eindex];
Q = Qnew;
hindex++;
}
while (findex < flen) {
Two_Sum(Q, fnow, Qnew, h[hindex]);
fnow = f[++findex];
Q = Qnew;
hindex++;
}
h[hindex] = Q;
return hindex + 1;
}
/*****************************************************************************/
/* */
/* fast_expansion_sum_zeroelim() Sum two expansions, eliminating zero */
/* components from the output expansion. */
/* */
/* Sets h = e + f. See the long version of my paper for details. */
/* */
/* If round-to-even is used (as with IEEE 754), maintains the strongly */
/* nonoverlapping property. (That is, if e is strongly nonoverlapping, h */
/* will be also.) Does NOT maintain the nonoverlapping or nonadjacent */
/* properties. */
/* */
/*****************************************************************************/
int fast_expansion_sum_zeroelim(int elen, REAL *e, int flen, REAL *f, REAL *h)
/* h cannot be e or f. */
{
REAL Q;
INEXACT REAL Qnew;
INEXACT REAL hh;
INEXACT REAL bvirt;
REAL avirt, bround, around;
int eindex, findex, hindex;
REAL enow, fnow;
enow = e[0];
fnow = f[0];
eindex = findex = 0;
if ((fnow > enow) == (fnow > -enow)) {
Q = enow;
enow = e[++eindex];
} else {
Q = fnow;
fnow = f[++findex];
}
hindex = 0;
if ((eindex < elen) && (findex < flen)) {
if ((fnow > enow) == (fnow > -enow)) {
Fast_Two_Sum(enow, Q, Qnew, hh);
enow = e[++eindex];
} else {
Fast_Two_Sum(fnow, Q, Qnew, hh);
fnow = f[++findex];
}
Q = Qnew;
if (hh != 0.0) {
h[hindex++] = hh;
}
while ((eindex < elen) && (findex < flen)) {
if ((fnow > enow) == (fnow > -enow)) {
Two_Sum(Q, enow, Qnew, hh);
enow = e[++eindex];
} else {
Two_Sum(Q, fnow, Qnew, hh);
fnow = f[++findex];
}
Q = Qnew;
if (hh != 0.0) {
h[hindex++] = hh;
}
}
}
while (eindex < elen) {
Two_Sum(Q, enow, Qnew, hh);
enow = e[++eindex];
Q = Qnew;
if (hh != 0.0) {
h[hindex++] = hh;
}
}
while (findex < flen) {
Two_Sum(Q, fnow, Qnew, hh);
fnow = f[++findex];
Q = Qnew;
if (hh != 0.0) {
h[hindex++] = hh;
}
}
if ((Q != 0.0) || (hindex == 0)) {
h[hindex++] = Q;
}
return hindex;
}
/*****************************************************************************/
/* */
/* linear_expansion_sum() Sum two expansions. */
/* */
/* Sets h = e + f. See either version of my paper for details. */
/* */
/* Maintains the nonoverlapping property. (That is, if e is */
/* nonoverlapping, h will be also.) */
/* */
/*****************************************************************************/
int linear_expansion_sum(int elen, REAL *e, int flen, REAL *f, REAL *h)
/* h cannot be e or f. */
{
REAL Q, q;
INEXACT REAL Qnew;
INEXACT REAL R;
INEXACT REAL bvirt;
REAL avirt, bround, around;
int eindex, findex, hindex;
REAL enow, fnow;
REAL g0;
enow = e[0];
fnow = f[0];
eindex = findex = 0;
if ((fnow > enow) == (fnow > -enow)) {
g0 = enow;
enow = e[++eindex];
} else {
g0 = fnow;
fnow = f[++findex];
}
if ((eindex < elen) && ((findex >= flen)
|| ((fnow > enow) == (fnow > -enow)))) {
Fast_Two_Sum(enow, g0, Qnew, q);
enow = e[++eindex];
} else {
Fast_Two_Sum(fnow, g0, Qnew, q);
fnow = f[++findex];
}
Q = Qnew;
for (hindex = 0; hindex < elen + flen - 2; hindex++) {
if ((eindex < elen) && ((findex >= flen)
|| ((fnow > enow) == (fnow > -enow)))) {
Fast_Two_Sum(enow, q, R, h[hindex]);
enow = e[++eindex];
} else {
Fast_Two_Sum(fnow, q, R, h[hindex]);
fnow = f[++findex];
}
Two_Sum(Q, R, Qnew, q);
Q = Qnew;
}
h[hindex] = q;
h[hindex + 1] = Q;
return hindex + 2;
}
/*****************************************************************************/
/* */
/* linear_expansion_sum_zeroelim() Sum two expansions, eliminating zero */
/* components from the output expansion. */
/* */
/* Sets h = e + f. See either version of my paper for details. */
/* */
/* Maintains the nonoverlapping property. (That is, if e is */
/* nonoverlapping, h will be also.) */
/* */
/*****************************************************************************/
int linear_expansion_sum_zeroelim(int elen, REAL *e, int flen, REAL *f,
REAL *h)
/* h cannot be e or f. */
{
REAL Q, q, hh;
INEXACT REAL Qnew;
INEXACT REAL R;
INEXACT REAL bvirt;
REAL avirt, bround, around;
int eindex, findex, hindex;
int count;
REAL enow, fnow;
REAL g0;
enow = e[0];
fnow = f[0];
eindex = findex = 0;
hindex = 0;
if ((fnow > enow) == (fnow > -enow)) {
g0 = enow;
enow = e[++eindex];
} else {
g0 = fnow;
fnow = f[++findex];
}
if ((eindex < elen) && ((findex >= flen)
|| ((fnow > enow) == (fnow > -enow)))) {
Fast_Two_Sum(enow, g0, Qnew, q);
enow = e[++eindex];
} else {
Fast_Two_Sum(fnow, g0, Qnew, q);
fnow = f[++findex];
}
Q = Qnew;
for (count = 2; count < elen + flen; count++) {
if ((eindex < elen) && ((findex >= flen)
|| ((fnow > enow) == (fnow > -enow)))) {
Fast_Two_Sum(enow, q, R, hh);
enow = e[++eindex];
} else {
Fast_Two_Sum(fnow, q, R, hh);
fnow = f[++findex];
}
Two_Sum(Q, R, Qnew, q);
Q = Qnew;
if (hh != 0) {
h[hindex++] = hh;
}
}
if (q != 0) {
h[hindex++] = q;
}
if ((Q != 0.0) || (hindex == 0)) {
h[hindex++] = Q;
}
return hindex;
}
/*****************************************************************************/
/* */
/* scale_expansion() Multiply an expansion by a scalar. */
/* */
/* Sets h = be. See either version of my paper for details. */
/* */
/* Maintains the nonoverlapping property. If round-to-even is used (as */
/* with IEEE 754), maintains the strongly nonoverlapping and nonadjacent */
/* properties as well. (That is, if e has one of these properties, so */
/* will h.) */
/* */
/*****************************************************************************/
int scale_expansion(int elen, REAL *e, REAL b, REAL *h)
/* e and h cannot be the same. */
{
INEXACT REAL Q;
INEXACT REAL sum;
INEXACT REAL product1;
REAL product0;
int eindex, hindex;
REAL enow;
INEXACT REAL bvirt;
REAL avirt, bround, around;
INEXACT REAL c;
INEXACT REAL abig;
REAL ahi, alo, bhi, blo;
REAL err1, err2, err3;
Split(b, bhi, blo);
Two_Product_Presplit(e[0], b, bhi, blo, Q, h[0]);
hindex = 1;
for (eindex = 1; eindex < elen; eindex++) {
enow = e[eindex];
Two_Product_Presplit(enow, b, bhi, blo, product1, product0);
Two_Sum(Q, product0, sum, h[hindex]);
hindex++;
Two_Sum(product1, sum, Q, h[hindex]);
hindex++;
}
h[hindex] = Q;
return elen + elen;
}
/*****************************************************************************/
/* */
/* scale_expansion_zeroelim() Multiply an expansion by a scalar, */
/* eliminating zero components from the */
/* output expansion. */
/* */
/* Sets h = be. See either version of my paper for details. */
/* */
/* Maintains the nonoverlapping property. If round-to-even is used (as */
/* with IEEE 754), maintains the strongly nonoverlapping and nonadjacent */
/* properties as well. (That is, if e has one of these properties, so */
/* will h.) */
/* */
/*****************************************************************************/
int scale_expansion_zeroelim(int elen, REAL *e, REAL b, REAL *h)
/* e and h cannot be the same. */
{
INEXACT REAL Q, sum;
REAL hh;
INEXACT REAL product1;
REAL product0;
int eindex, hindex;
REAL enow;
INEXACT REAL bvirt;
REAL avirt, bround, around;
INEXACT REAL c;
INEXACT REAL abig;
REAL ahi, alo, bhi, blo;
REAL err1, err2, err3;
Split(b, bhi, blo);
Two_Product_Presplit(e[0], b, bhi, blo, Q, hh);
hindex = 0;
if (hh != 0) {
h[hindex++] = hh;
}
for (eindex = 1; eindex < elen; eindex++) {
enow = e[eindex];
Two_Product_Presplit(enow, b, bhi, blo, product1, product0);
Two_Sum(Q, product0, sum, hh);
if (hh != 0) {
h[hindex++] = hh;
}
Fast_Two_Sum(product1, sum, Q, hh);
if (hh != 0) {
h[hindex++] = hh;
}
}
if ((Q != 0.0) || (hindex == 0)) {
h[hindex++] = Q;
}
return hindex;
}
/*****************************************************************************/
/* */
/* compress() Compress an expansion. */
/* */
/* See the long version of my paper for details. */
/* */
/* Maintains the nonoverlapping property. If round-to-even is used (as */
/* with IEEE 754), then any nonoverlapping expansion is converted to a */
/* nonadjacent expansion. */
/* */
/*****************************************************************************/
int compress(int elen, REAL *e, REAL *h)
/* e and h may be the same. */
{
REAL Q, q;
INEXACT REAL Qnew;
int eindex, hindex;
INEXACT REAL bvirt;
REAL enow, hnow;
int top, bottom;
bottom = elen - 1;
Q = e[bottom];
for (eindex = elen - 2; eindex >= 0; eindex--) {
enow = e[eindex];
Fast_Two_Sum(Q, enow, Qnew, q);
if (q != 0) {
h[bottom--] = Qnew;
Q = q;
} else {
Q = Qnew;
}
}
top = 0;
for (hindex = bottom + 1; hindex < elen; hindex++) {
hnow = h[hindex];
Fast_Two_Sum(hnow, Q, Qnew, q);
if (q != 0) {
h[top++] = q;
}
Q = Qnew;
}
h[top] = Q;
return top + 1;
}
/*****************************************************************************/
/* */
/* estimate() Produce a one-word estimate of an expansion's value. */
/* */
/* See either version of my paper for details. */
/* */
/*****************************************************************************/
REAL estimate(int elen, REAL *e)
{
REAL Q;
int eindex;
Q = e[0];
for (eindex = 1; eindex < elen; eindex++) {
Q += e[eindex];
}
return Q;
}
/*****************************************************************************/
/* */
/* orient2dfast() Approximate 2D orientation test. Nonrobust. */
/* orient2dexact() Exact 2D orientation test. Robust. */
/* orient2dslow() Another exact 2D orientation test. Robust. */
/* orient2d() Adaptive exact 2D orientation test. Robust. */
/* */
/* Return a positive value if the points pa, pb, and pc occur */
/* in counterclockwise order; a negative value if they occur */
/* in clockwise order; and zero if they are collinear. The */
/* result is also a rough approximation of twice the signed */
/* area of the triangle defined by the three points. */
/* */
/* Only the first and last routine should be used; the middle two are for */
/* timings. */
/* */
/* The last three use exact arithmetic to ensure a correct answer. The */
/* result returned is the determinant of a matrix. In orient2d() only, */
/* this determinant is computed adaptively, in the sense that exact */
/* arithmetic is used only to the degree it is needed to ensure that the */
/* returned value has the correct sign. Hence, orient2d() is usually quite */
/* fast, but will run more slowly when the input points are collinear or */
/* nearly so. */
/* */
/*****************************************************************************/
REAL orient2dfast(REAL *pa, REAL *pb, REAL *pc)
{
REAL acx, bcx, acy, bcy;
acx = pa[0] - pc[0];
bcx = pb[0] - pc[0];
acy = pa[1] - pc[1];
bcy = pb[1] - pc[1];
return acx * bcy - acy * bcx;
}
REAL orient2dexact(REAL *pa, REAL *pb, REAL *pc)
{
INEXACT REAL axby1, axcy1, bxcy1, bxay1, cxay1, cxby1;
REAL axby0, axcy0, bxcy0, bxay0, cxay0, cxby0;
REAL aterms[4], bterms[4], cterms[4];
INEXACT REAL aterms3, bterms3, cterms3;
REAL v[8], w[12];
int vlength, wlength;
INEXACT REAL bvirt;
REAL avirt, bround, around;
INEXACT REAL c;
INEXACT REAL abig;
REAL ahi, alo, bhi, blo;
REAL err1, err2, err3;
INEXACT REAL _i, _j;
REAL _0;
Two_Product(pa[0], pb[1], axby1, axby0);
Two_Product(pa[0], pc[1], axcy1, axcy0);
Two_Two_Diff(axby1, axby0, axcy1, axcy0,
aterms3, aterms[2], aterms[1], aterms[0]);
aterms[3] = aterms3;
Two_Product(pb[0], pc[1], bxcy1, bxcy0);
Two_Product(pb[0], pa[1], bxay1, bxay0);
Two_Two_Diff(bxcy1, bxcy0, bxay1, bxay0,
bterms3, bterms[2], bterms[1], bterms[0]);
bterms[3] = bterms3;
Two_Product(pc[0], pa[1], cxay1, cxay0);
Two_Product(pc[0], pb[1], cxby1, cxby0);
Two_Two_Diff(cxay1, cxay0, cxby1, cxby0,
cterms3, cterms[2], cterms[1], cterms[0]);
cterms[3] = cterms3;
vlength = fast_expansion_sum_zeroelim(4, aterms, 4, bterms, v);
wlength = fast_expansion_sum_zeroelim(vlength, v, 4, cterms, w);
return w[wlength - 1];
}
REAL orient2dslow(REAL *pa, REAL *pb, REAL *pc)
{
INEXACT REAL acx, acy, bcx, bcy;
REAL acxtail, acytail;
REAL bcxtail, bcytail;
REAL negate, negatetail;
REAL axby[8], bxay[8];
INEXACT REAL axby7, bxay7;
REAL deter[16];
int deterlen;
INEXACT REAL bvirt;
REAL avirt, bround, around;
INEXACT REAL c;
INEXACT REAL abig;
REAL a0hi, a0lo, a1hi, a1lo, bhi, blo;
REAL err1, err2, err3;
INEXACT REAL _i, _j, _k, _l, _m, _n;
REAL _0, _1, _2;
Two_Diff(pa[0], pc[0], acx, acxtail);
Two_Diff(pa[1], pc[1], acy, acytail);
Two_Diff(pb[0], pc[0], bcx, bcxtail);
Two_Diff(pb[1], pc[1], bcy, bcytail);
Two_Two_Product(acx, acxtail, bcy, bcytail,
axby7, axby[6], axby[5], axby[4],
axby[3], axby[2], axby[1], axby[0]);
axby[7] = axby7;
negate = -acy;
negatetail = -acytail;
Two_Two_Product(bcx, bcxtail, negate, negatetail,
bxay7, bxay[6], bxay[5], bxay[4],
bxay[3], bxay[2], bxay[1], bxay[0]);
bxay[7] = bxay7;
deterlen = fast_expansion_sum_zeroelim(8, axby, 8, bxay, deter);
return deter[deterlen - 1];
}
REAL orient2dadapt(REAL *pa, REAL *pb, REAL *pc, REAL detsum)
{
INEXACT REAL acx, acy, bcx, bcy;
REAL acxtail, acytail, bcxtail, bcytail;
INEXACT REAL detleft, detright;
REAL detlefttail, detrighttail;
REAL det, errbound;
REAL B[4], C1[8], C2[12], D[16];
INEXACT REAL B3;
int C1length, C2length, Dlength;
REAL u[4];
INEXACT REAL u3;
INEXACT REAL s1, t1;
REAL s0, t0;
INEXACT REAL bvirt;
REAL avirt, bround, around;
INEXACT REAL c;
INEXACT REAL abig;
REAL ahi, alo, bhi, blo;
REAL err1, err2, err3;
INEXACT REAL _i, _j;
REAL _0;
acx = (REAL) (pa[0] - pc[0]);
bcx = (REAL) (pb[0] - pc[0]);
acy = (REAL) (pa[1] - pc[1]);
bcy = (REAL) (pb[1] - pc[1]);
Two_Product(acx, bcy, detleft, detlefttail);
Two_Product(acy, bcx, detright, detrighttail);
Two_Two_Diff(detleft, detlefttail, detright, detrighttail,
B3, B[2], B[1], B[0]);
B[3] = B3;
det = estimate(4, B);
errbound = ccwerrboundB * detsum;
if ((det >= errbound) || (-det >= errbound)) {
return det;
}
Two_Diff_Tail(pa[0], pc[0], acx, acxtail);
Two_Diff_Tail(pb[0], pc[0], bcx, bcxtail);
Two_Diff_Tail(pa[1], pc[1], acy, acytail);
Two_Diff_Tail(pb[1], pc[1], bcy, bcytail);
if ((acxtail == 0.0) && (acytail == 0.0)
&& (bcxtail == 0.0) && (bcytail == 0.0)) {
return det;
}
errbound = ccwerrboundC * detsum + resulterrbound * Absolute(det);
det += (acx * bcytail + bcy * acxtail)
- (acy * bcxtail + bcx * acytail);
if ((det >= errbound) || (-det >= errbound)) {
return det;
}
Two_Product(acxtail, bcy, s1, s0);
Two_Product(acytail, bcx, t1, t0);
Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]);
u[3] = u3;
C1length = fast_expansion_sum_zeroelim(4, B, 4, u, C1);
Two_Product(acx, bcytail, s1, s0);
Two_Product(acy, bcxtail, t1, t0);
Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]);
u[3] = u3;
C2length = fast_expansion_sum_zeroelim(C1length, C1, 4, u, C2);
Two_Product(acxtail, bcytail, s1, s0);
Two_Product(acytail, bcxtail, t1, t0);
Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]);
u[3] = u3;
Dlength = fast_expansion_sum_zeroelim(C2length, C2, 4, u, D);
return(D[Dlength - 1]);
}
REAL orient2d(REAL *pa, REAL *pb, REAL *pc)
{
REAL detleft, detright, det;
REAL detsum, errbound;
detleft = (pa[0] - pc[0]) * (pb[1] - pc[1]);
detright = (pa[1] - pc[1]) * (pb[0] - pc[0]);
det = detleft - detright;
if (detleft > 0.0) {
if (detright <= 0.0) {
return det;
} else {
detsum = detleft + detright;
}
} else if (detleft < 0.0) {
if (detright >= 0.0) {
return det;
} else {
detsum = -detleft - detright;
}
} else {
return det;
}
errbound = ccwerrboundA * detsum;
if ((det >= errbound) || (-det >= errbound)) {
return det;
}
return orient2dadapt(pa, pb, pc, detsum);
}
/*****************************************************************************/
/* */
/* orient3dfast() Approximate 3D orientation test. Nonrobust. */
/* orient3dexact() Exact 3D orientation test. Robust. */
/* orient3dslow() Another exact 3D orientation test. Robust. */
/* orient3d() Adaptive exact 3D orientation test. Robust. */
/* */
/* Return a positive value if the point pd lies below the */
/* plane passing through pa, pb, and pc; "below" is defined so */
/* that pa, pb, and pc appear in counterclockwise order when */
/* viewed from above the plane. Returns a negative value if */
/* pd lies above the plane. Returns zero if the points are */
/* coplanar. The result is also a rough approximation of six */
/* times the signed volume of the tetrahedron defined by the */
/* four points. */
/* */
/* Only the first and last routine should be used; the middle two are for */
/* timings. */
/* */
/* The last three use exact arithmetic to ensure a correct answer. The */
/* result returned is the determinant of a matrix. In orient3d() only, */
/* this determinant is computed adaptively, in the sense that exact */
/* arithmetic is used only to the degree it is needed to ensure that the */
/* returned value has the correct sign. Hence, orient3d() is usually quite */
/* fast, but will run more slowly when the input points are coplanar or */
/* nearly so. */
/* */
/*****************************************************************************/
REAL orient3dfast(REAL *pa, REAL *pb, REAL *pc, REAL *pd)
{
REAL adx, bdx, cdx;
REAL ady, bdy, cdy;
REAL adz, bdz, cdz;
adx = pa[0] - pd[0];
bdx = pb[0] - pd[0];
cdx = pc[0] - pd[0];
ady = pa[1] - pd[1];
bdy = pb[1] - pd[1];
cdy = pc[1] - pd[1];
adz = pa[2] - pd[2];
bdz = pb[2] - pd[2];
cdz = pc[2] - pd[2];
return adx * (bdy * cdz - bdz * cdy)
+ bdx * (cdy * adz - cdz * ady)
+ cdx * (ady * bdz - adz * bdy);
}
REAL orient3dexact(REAL *pa, REAL *pb, REAL *pc, REAL *pd)
{
INEXACT REAL axby1, bxcy1, cxdy1, dxay1, axcy1, bxdy1;
INEXACT REAL bxay1, cxby1, dxcy1, axdy1, cxay1, dxby1;
REAL axby0, bxcy0, cxdy0, dxay0, axcy0, bxdy0;
REAL bxay0, cxby0, dxcy0, axdy0, cxay0, dxby0;
REAL ab[4], bc[4], cd[4], da[4], ac[4], bd[4];
REAL temp8[8];
int templen;
REAL abc[12], bcd[12], cda[12], dab[12];
int abclen, bcdlen, cdalen, dablen;
REAL adet[24], bdet[24], cdet[24], ddet[24];
int alen, blen, clen, dlen;
REAL abdet[48], cddet[48];
int ablen, cdlen;
REAL deter[96];
int deterlen;
int i;
INEXACT REAL bvirt;
REAL avirt, bround, around;
INEXACT REAL c;
INEXACT REAL abig;
REAL ahi, alo, bhi, blo;
REAL err1, err2, err3;
INEXACT REAL _i, _j;
REAL _0;
Two_Product(pa[0], pb[1], axby1, axby0);
Two_Product(pb[0], pa[1], bxay1, bxay0);
Two_Two_Diff(axby1, axby0, bxay1, bxay0, ab[3], ab[2], ab[1], ab[0]);
Two_Product(pb[0], pc[1], bxcy1, bxcy0);
Two_Product(pc[0], pb[1], cxby1, cxby0);
Two_Two_Diff(bxcy1, bxcy0, cxby1, cxby0, bc[3], bc[2], bc[1], bc[0]);
Two_Product(pc[0], pd[1], cxdy1, cxdy0);
Two_Product(pd[0], pc[1], dxcy1, dxcy0);
Two_Two_Diff(cxdy1, cxdy0, dxcy1, dxcy0, cd[3], cd[2], cd[1], cd[0]);
Two_Product(pd[0], pa[1], dxay1, dxay0);
Two_Product(pa[0], pd[1], axdy1, axdy0);
Two_Two_Diff(dxay1, dxay0, axdy1, axdy0, da[3], da[2], da[1], da[0]);
Two_Product(pa[0], pc[1], axcy1, axcy0);
Two_Product(pc[0], pa[1], cxay1, cxay0);
Two_Two_Diff(axcy1, axcy0, cxay1, cxay0, ac[3], ac[2], ac[1], ac[0]);
Two_Product(pb[0], pd[1], bxdy1, bxdy0);
Two_Product(pd[0], pb[1], dxby1, dxby0);
Two_Two_Diff(bxdy1, bxdy0, dxby1, dxby0, bd[3], bd[2], bd[1], bd[0]);
templen = fast_expansion_sum_zeroelim(4, cd, 4, da, temp8);
cdalen = fast_expansion_sum_zeroelim(templen, temp8, 4, ac, cda);
templen = fast_expansion_sum_zeroelim(4, da, 4, ab, temp8);
dablen = fast_expansion_sum_zeroelim(templen, temp8, 4, bd, dab);
for (i = 0; i < 4; i++) {
bd[i] = -bd[i];
ac[i] = -ac[i];
}
templen = fast_expansion_sum_zeroelim(4, ab, 4, bc, temp8);
abclen = fast_expansion_sum_zeroelim(templen, temp8, 4, ac, abc);
templen = fast_expansion_sum_zeroelim(4, bc, 4, cd, temp8);
bcdlen = fast_expansion_sum_zeroelim(templen, temp8, 4, bd, bcd);
alen = scale_expansion_zeroelim(bcdlen, bcd, pa[2], adet);
blen = scale_expansion_zeroelim(cdalen, cda, -pb[2], bdet);
clen = scale_expansion_zeroelim(dablen, dab, pc[2], cdet);
dlen = scale_expansion_zeroelim(abclen, abc, -pd[2], ddet);
ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
cdlen = fast_expansion_sum_zeroelim(clen, cdet, dlen, ddet, cddet);
deterlen = fast_expansion_sum_zeroelim(ablen, abdet, cdlen, cddet, deter);
return deter[deterlen - 1];
}
REAL orient3dslow(REAL *pa, REAL *pb, REAL *pc, REAL *pd)
{
INEXACT REAL adx, ady, adz, bdx, bdy, bdz, cdx, cdy, cdz;
REAL adxtail, adytail, adztail;
REAL bdxtail, bdytail, bdztail;
REAL cdxtail, cdytail, cdztail;
REAL negate, negatetail;
INEXACT REAL axby7, bxcy7, axcy7, bxay7, cxby7, cxay7;
REAL axby[8], bxcy[8], axcy[8], bxay[8], cxby[8], cxay[8];
REAL temp16[16], temp32[32], temp32t[32];
int temp16len, temp32len, temp32tlen;
REAL adet[64], bdet[64], cdet[64];
int alen, blen, clen;
REAL abdet[128];
int ablen;
REAL deter[192];
int deterlen;
INEXACT REAL bvirt;
REAL avirt, bround, around;
INEXACT REAL c;
INEXACT REAL abig;
REAL a0hi, a0lo, a1hi, a1lo, bhi, blo;
REAL err1, err2, err3;
INEXACT REAL _i, _j, _k, _l, _m, _n;
REAL _0, _1, _2;
Two_Diff(pa[0], pd[0], adx, adxtail);
Two_Diff(pa[1], pd[1], ady, adytail);
Two_Diff(pa[2], pd[2], adz, adztail);
Two_Diff(pb[0], pd[0], bdx, bdxtail);
Two_Diff(pb[1], pd[1], bdy, bdytail);
Two_Diff(pb[2], pd[2], bdz, bdztail);
Two_Diff(pc[0], pd[0], cdx, cdxtail);
Two_Diff(pc[1], pd[1], cdy, cdytail);
Two_Diff(pc[2], pd[2], cdz, cdztail);
Two_Two_Product(adx, adxtail, bdy, bdytail,
axby7, axby[6], axby[5], axby[4],
axby[3], axby[2], axby[1], axby[0]);
axby[7] = axby7;
negate = -ady;
negatetail = -adytail;
Two_Two_Product(bdx, bdxtail, negate, negatetail,
bxay7, bxay[6], bxay[5], bxay[4],
bxay[3], bxay[2], bxay[1], bxay[0]);
bxay[7] = bxay7;
Two_Two_Product(bdx, bdxtail, cdy, cdytail,
bxcy7, bxcy[6], bxcy[5], bxcy[4],
bxcy[3], bxcy[2], bxcy[1], bxcy[0]);
bxcy[7] = bxcy7;
negate = -bdy;
negatetail = -bdytail;
Two_Two_Product(cdx, cdxtail, negate, negatetail,
cxby7, cxby[6], cxby[5], cxby[4],
cxby[3], cxby[2], cxby[1], cxby[0]);
cxby[7] = cxby7;
Two_Two_Product(cdx, cdxtail, ady, adytail,
cxay7, cxay[6], cxay[5], cxay[4],
cxay[3], cxay[2], cxay[1], cxay[0]);
cxay[7] = cxay7;
negate = -cdy;
negatetail = -cdytail;
Two_Two_Product(adx, adxtail, negate, negatetail,
axcy7, axcy[6], axcy[5], axcy[4],
axcy[3], axcy[2], axcy[1], axcy[0]);
axcy[7] = axcy7;
temp16len = fast_expansion_sum_zeroelim(8, bxcy, 8, cxby, temp16);
temp32len = scale_expansion_zeroelim(temp16len, temp16, adz, temp32);
temp32tlen = scale_expansion_zeroelim(temp16len, temp16, adztail, temp32t);
alen = fast_expansion_sum_zeroelim(temp32len, temp32, temp32tlen, temp32t,
adet);
temp16len = fast_expansion_sum_zeroelim(8, cxay, 8, axcy, temp16);
temp32len = scale_expansion_zeroelim(temp16len, temp16, bdz, temp32);
temp32tlen = scale_expansion_zeroelim(temp16len, temp16, bdztail, temp32t);
blen = fast_expansion_sum_zeroelim(temp32len, temp32, temp32tlen, temp32t,
bdet);
temp16len = fast_expansion_sum_zeroelim(8, axby, 8, bxay, temp16);
temp32len = scale_expansion_zeroelim(temp16len, temp16, cdz, temp32);
temp32tlen = scale_expansion_zeroelim(temp16len, temp16, cdztail, temp32t);
clen = fast_expansion_sum_zeroelim(temp32len, temp32, temp32tlen, temp32t,
cdet);
ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
deterlen = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, deter);
return deter[deterlen - 1];
}
REAL orient3dadapt(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL permanent)
{
INEXACT REAL adx, bdx, cdx, ady, bdy, cdy, adz, bdz, cdz;
REAL det, errbound;
INEXACT REAL bdxcdy1, cdxbdy1, cdxady1, adxcdy1, adxbdy1, bdxady1;
REAL bdxcdy0, cdxbdy0, cdxady0, adxcdy0, adxbdy0, bdxady0;
REAL bc[4], ca[4], ab[4];
INEXACT REAL bc3, ca3, ab3;
REAL adet[8], bdet[8], cdet[8];
int alen, blen, clen;
REAL abdet[16];
int ablen;
REAL *finnow, *finother, *finswap;
REAL fin1[192], fin2[192];
int finlength;
REAL adxtail, bdxtail, cdxtail;
REAL adytail, bdytail, cdytail;
REAL adztail, bdztail, cdztail;
INEXACT REAL at_blarge, at_clarge;
INEXACT REAL bt_clarge, bt_alarge;
INEXACT REAL ct_alarge, ct_blarge;
REAL at_b[4], at_c[4], bt_c[4], bt_a[4], ct_a[4], ct_b[4];
int at_blen, at_clen, bt_clen, bt_alen, ct_alen, ct_blen;
INEXACT REAL bdxt_cdy1, cdxt_bdy1, cdxt_ady1;
INEXACT REAL adxt_cdy1, adxt_bdy1, bdxt_ady1;
REAL bdxt_cdy0, cdxt_bdy0, cdxt_ady0;
REAL adxt_cdy0, adxt_bdy0, bdxt_ady0;
INEXACT REAL bdyt_cdx1, cdyt_bdx1, cdyt_adx1;
INEXACT REAL adyt_cdx1, adyt_bdx1, bdyt_adx1;
REAL bdyt_cdx0, cdyt_bdx0, cdyt_adx0;
REAL adyt_cdx0, adyt_bdx0, bdyt_adx0;
REAL bct[8], cat[8], abt[8];
int bctlen, catlen, abtlen;
INEXACT REAL bdxt_cdyt1, cdxt_bdyt1, cdxt_adyt1;
INEXACT REAL adxt_cdyt1, adxt_bdyt1, bdxt_adyt1;
REAL bdxt_cdyt0, cdxt_bdyt0, cdxt_adyt0;
REAL adxt_cdyt0, adxt_bdyt0, bdxt_adyt0;
REAL u[4], v[12], w[16];
INEXACT REAL u3;
int vlength, wlength;
REAL negate;
INEXACT REAL bvirt;
REAL avirt, bround, around;
INEXACT REAL c;
INEXACT REAL abig;
REAL ahi, alo, bhi, blo;
REAL err1, err2, err3;
INEXACT REAL _i, _j, _k;
REAL _0;
adx = (REAL) (pa[0] - pd[0]);
bdx = (REAL) (pb[0] - pd[0]);
cdx = (REAL) (pc[0] - pd[0]);
ady = (REAL) (pa[1] - pd[1]);
bdy = (REAL) (pb[1] - pd[1]);
cdy = (REAL) (pc[1] - pd[1]);
adz = (REAL) (pa[2] - pd[2]);
bdz = (REAL) (pb[2] - pd[2]);
cdz = (REAL) (pc[2] - pd[2]);
Two_Product(bdx, cdy, bdxcdy1, bdxcdy0);
Two_Product(cdx, bdy, cdxbdy1, cdxbdy0);
Two_Two_Diff(bdxcdy1, bdxcdy0, cdxbdy1, cdxbdy0, bc3, bc[2], bc[1], bc[0]);
bc[3] = bc3;
alen = scale_expansion_zeroelim(4, bc, adz, adet);
Two_Product(cdx, ady, cdxady1, cdxady0);
Two_Product(adx, cdy, adxcdy1, adxcdy0);
Two_Two_Diff(cdxady1, cdxady0, adxcdy1, adxcdy0, ca3, ca[2], ca[1], ca[0]);
ca[3] = ca3;
blen = scale_expansion_zeroelim(4, ca, bdz, bdet);
Two_Product(adx, bdy, adxbdy1, adxbdy0);
Two_Product(bdx, ady, bdxady1, bdxady0);
Two_Two_Diff(adxbdy1, adxbdy0, bdxady1, bdxady0, ab3, ab[2], ab[1], ab[0]);
ab[3] = ab3;
clen = scale_expansion_zeroelim(4, ab, cdz, cdet);
ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
finlength = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, fin1);
det = estimate(finlength, fin1);
errbound = o3derrboundB * permanent;
if ((det >= errbound) || (-det >= errbound)) {
return det;
}
Two_Diff_Tail(pa[0], pd[0], adx, adxtail);
Two_Diff_Tail(pb[0], pd[0], bdx, bdxtail);
Two_Diff_Tail(pc[0], pd[0], cdx, cdxtail);
Two_Diff_Tail(pa[1], pd[1], ady, adytail);
Two_Diff_Tail(pb[1], pd[1], bdy, bdytail);
Two_Diff_Tail(pc[1], pd[1], cdy, cdytail);
Two_Diff_Tail(pa[2], pd[2], adz, adztail);
Two_Diff_Tail(pb[2], pd[2], bdz, bdztail);
Two_Diff_Tail(pc[2], pd[2], cdz, cdztail);
if ((adxtail == 0.0) && (bdxtail == 0.0) && (cdxtail == 0.0)
&& (adytail == 0.0) && (bdytail == 0.0) && (cdytail == 0.0)
&& (adztail == 0.0) && (bdztail == 0.0) && (cdztail == 0.0)) {
return det;
}
errbound = o3derrboundC * permanent + resulterrbound * Absolute(det);
det += (adz * ((bdx * cdytail + cdy * bdxtail)
- (bdy * cdxtail + cdx * bdytail))
+ adztail * (bdx * cdy - bdy * cdx))
+ (bdz * ((cdx * adytail + ady * cdxtail)
- (cdy * adxtail + adx * cdytail))
+ bdztail * (cdx * ady - cdy * adx))
+ (cdz * ((adx * bdytail + bdy * adxtail)
- (ady * bdxtail + bdx * adytail))
+ cdztail * (adx * bdy - ady * bdx));
if ((det >= errbound) || (-det >= errbound)) {
return det;
}
finnow = fin1;
finother = fin2;
if (adxtail == 0.0) {
if (adytail == 0.0) {
at_b[0] = 0.0;
at_blen = 1;
at_c[0] = 0.0;
at_clen = 1;
} else {
negate = -adytail;
Two_Product(negate, bdx, at_blarge, at_b[0]);
at_b[1] = at_blarge;
at_blen = 2;
Two_Product(adytail, cdx, at_clarge, at_c[0]);
at_c[1] = at_clarge;
at_clen = 2;
}
} else {
if (adytail == 0.0) {
Two_Product(adxtail, bdy, at_blarge, at_b[0]);
at_b[1] = at_blarge;
at_blen = 2;
negate = -adxtail;
Two_Product(negate, cdy, at_clarge, at_c[0]);
at_c[1] = at_clarge;
at_clen = 2;
} else {
Two_Product(adxtail, bdy, adxt_bdy1, adxt_bdy0);
Two_Product(adytail, bdx, adyt_bdx1, adyt_bdx0);
Two_Two_Diff(adxt_bdy1, adxt_bdy0, adyt_bdx1, adyt_bdx0,
at_blarge, at_b[2], at_b[1], at_b[0]);
at_b[3] = at_blarge;
at_blen = 4;
Two_Product(adytail, cdx, adyt_cdx1, adyt_cdx0);
Two_Product(adxtail, cdy, adxt_cdy1, adxt_cdy0);
Two_Two_Diff(adyt_cdx1, adyt_cdx0, adxt_cdy1, adxt_cdy0,
at_clarge, at_c[2], at_c[1], at_c[0]);
at_c[3] = at_clarge;
at_clen = 4;
}
}
if (bdxtail == 0.0) {
if (bdytail == 0.0) {
bt_c[0] = 0.0;
bt_clen = 1;
bt_a[0] = 0.0;
bt_alen = 1;
} else {
negate = -bdytail;
Two_Product(negate, cdx, bt_clarge, bt_c[0]);
bt_c[1] = bt_clarge;
bt_clen = 2;
Two_Product(bdytail, adx, bt_alarge, bt_a[0]);
bt_a[1] = bt_alarge;
bt_alen = 2;
}
} else {
if (bdytail == 0.0) {
Two_Product(bdxtail, cdy, bt_clarge, bt_c[0]);
bt_c[1] = bt_clarge;
bt_clen = 2;
negate = -bdxtail;
Two_Product(negate, ady, bt_alarge, bt_a[0]);
bt_a[1] = bt_alarge;
bt_alen = 2;
} else {
Two_Product(bdxtail, cdy, bdxt_cdy1, bdxt_cdy0);
Two_Product(bdytail, cdx, bdyt_cdx1, bdyt_cdx0);
Two_Two_Diff(bdxt_cdy1, bdxt_cdy0, bdyt_cdx1, bdyt_cdx0,
bt_clarge, bt_c[2], bt_c[1], bt_c[0]);
bt_c[3] = bt_clarge;
bt_clen = 4;
Two_Product(bdytail, adx, bdyt_adx1, bdyt_adx0);
Two_Product(bdxtail, ady, bdxt_ady1, bdxt_ady0);
Two_Two_Diff(bdyt_adx1, bdyt_adx0, bdxt_ady1, bdxt_ady0,
bt_alarge, bt_a[2], bt_a[1], bt_a[0]);
bt_a[3] = bt_alarge;
bt_alen = 4;
}
}
if (cdxtail == 0.0) {
if (cdytail == 0.0) {
ct_a[0] = 0.0;
ct_alen = 1;
ct_b[0] = 0.0;
ct_blen = 1;
} else {
negate = -cdytail;
Two_Product(negate, adx, ct_alarge, ct_a[0]);
ct_a[1] = ct_alarge;
ct_alen = 2;
Two_Product(cdytail, bdx, ct_blarge, ct_b[0]);
ct_b[1] = ct_blarge;
ct_blen = 2;
}
} else {
if (cdytail == 0.0) {
Two_Product(cdxtail, ady, ct_alarge, ct_a[0]);
ct_a[1] = ct_alarge;
ct_alen = 2;
negate = -cdxtail;
Two_Product(negate, bdy, ct_blarge, ct_b[0]);
ct_b[1] = ct_blarge;
ct_blen = 2;
} else {
Two_Product(cdxtail, ady, cdxt_ady1, cdxt_ady0);
Two_Product(cdytail, adx, cdyt_adx1, cdyt_adx0);
Two_Two_Diff(cdxt_ady1, cdxt_ady0, cdyt_adx1, cdyt_adx0,
ct_alarge, ct_a[2], ct_a[1], ct_a[0]);
ct_a[3] = ct_alarge;
ct_alen = 4;
Two_Product(cdytail, bdx, cdyt_bdx1, cdyt_bdx0);
Two_Product(cdxtail, bdy, cdxt_bdy1, cdxt_bdy0);
Two_Two_Diff(cdyt_bdx1, cdyt_bdx0, cdxt_bdy1, cdxt_bdy0,
ct_blarge, ct_b[2], ct_b[1], ct_b[0]);
ct_b[3] = ct_blarge;
ct_blen = 4;
}
}
bctlen = fast_expansion_sum_zeroelim(bt_clen, bt_c, ct_blen, ct_b, bct);
wlength = scale_expansion_zeroelim(bctlen, bct, adz, w);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
finother);
finswap = finnow; finnow = finother; finother = finswap;
catlen = fast_expansion_sum_zeroelim(ct_alen, ct_a, at_clen, at_c, cat);
wlength = scale_expansion_zeroelim(catlen, cat, bdz, w);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
finother);
finswap = finnow; finnow = finother; finother = finswap;
abtlen = fast_expansion_sum_zeroelim(at_blen, at_b, bt_alen, bt_a, abt);
wlength = scale_expansion_zeroelim(abtlen, abt, cdz, w);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
finother);
finswap = finnow; finnow = finother; finother = finswap;
if (adztail != 0.0) {
vlength = scale_expansion_zeroelim(4, bc, adztail, v);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v,
finother);
finswap = finnow; finnow = finother; finother = finswap;
}
if (bdztail != 0.0) {
vlength = scale_expansion_zeroelim(4, ca, bdztail, v);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v,
finother);
finswap = finnow; finnow = finother; finother = finswap;
}
if (cdztail != 0.0) {
vlength = scale_expansion_zeroelim(4, ab, cdztail, v);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v,
finother);
finswap = finnow; finnow = finother; finother = finswap;
}
if (adxtail != 0.0) {
if (bdytail != 0.0) {
Two_Product(adxtail, bdytail, adxt_bdyt1, adxt_bdyt0);
Two_One_Product(adxt_bdyt1, adxt_bdyt0, cdz, u3, u[2], u[1], u[0]);
u[3] = u3;
finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
finother);
finswap = finnow; finnow = finother; finother = finswap;
if (cdztail != 0.0) {
Two_One_Product(adxt_bdyt1, adxt_bdyt0, cdztail, u3, u[2], u[1], u[0]);
u[3] = u3;
finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
finother);
finswap = finnow; finnow = finother; finother = finswap;
}
}
if (cdytail != 0.0) {
negate = -adxtail;
Two_Product(negate, cdytail, adxt_cdyt1, adxt_cdyt0);
Two_One_Product(adxt_cdyt1, adxt_cdyt0, bdz, u3, u[2], u[1], u[0]);
u[3] = u3;
finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
finother);
finswap = finnow; finnow = finother; finother = finswap;
if (bdztail != 0.0) {
Two_One_Product(adxt_cdyt1, adxt_cdyt0, bdztail, u3, u[2], u[1], u[0]);
u[3] = u3;
finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
finother);
finswap = finnow; finnow = finother; finother = finswap;
}
}
}
if (bdxtail != 0.0) {
if (cdytail != 0.0) {
Two_Product(bdxtail, cdytail, bdxt_cdyt1, bdxt_cdyt0);
Two_One_Product(bdxt_cdyt1, bdxt_cdyt0, adz, u3, u[2], u[1], u[0]);
u[3] = u3;
finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
finother);
finswap = finnow; finnow = finother; finother = finswap;
if (adztail != 0.0) {
Two_One_Product(bdxt_cdyt1, bdxt_cdyt0, adztail, u3, u[2], u[1], u[0]);
u[3] = u3;
finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
finother);
finswap = finnow; finnow = finother; finother = finswap;
}
}
if (adytail != 0.0) {
negate = -bdxtail;
Two_Product(negate, adytail, bdxt_adyt1, bdxt_adyt0);
Two_One_Product(bdxt_adyt1, bdxt_adyt0, cdz, u3, u[2], u[1], u[0]);
u[3] = u3;
finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
finother);
finswap = finnow; finnow = finother; finother = finswap;
if (cdztail != 0.0) {
Two_One_Product(bdxt_adyt1, bdxt_adyt0, cdztail, u3, u[2], u[1], u[0]);
u[3] = u3;
finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
finother);
finswap = finnow; finnow = finother; finother = finswap;
}
}
}
if (cdxtail != 0.0) {
if (adytail != 0.0) {
Two_Product(cdxtail, adytail, cdxt_adyt1, cdxt_adyt0);
Two_One_Product(cdxt_adyt1, cdxt_adyt0, bdz, u3, u[2], u[1], u[0]);
u[3] = u3;
finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
finother);
finswap = finnow; finnow = finother; finother = finswap;
if (bdztail != 0.0) {
Two_One_Product(cdxt_adyt1, cdxt_adyt0, bdztail, u3, u[2], u[1], u[0]);
u[3] = u3;
finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
finother);
finswap = finnow; finnow = finother; finother = finswap;
}
}
if (bdytail != 0.0) {
negate = -cdxtail;
Two_Product(negate, bdytail, cdxt_bdyt1, cdxt_bdyt0);
Two_One_Product(cdxt_bdyt1, cdxt_bdyt0, adz, u3, u[2], u[1], u[0]);
u[3] = u3;
finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
finother);
finswap = finnow; finnow = finother; finother = finswap;
if (adztail != 0.0) {
Two_One_Product(cdxt_bdyt1, cdxt_bdyt0, adztail, u3, u[2], u[1], u[0]);
u[3] = u3;
finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
finother);
finswap = finnow; finnow = finother; finother = finswap;
}
}
}
if (adztail != 0.0) {
wlength = scale_expansion_zeroelim(bctlen, bct, adztail, w);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
finother);
finswap = finnow; finnow = finother; finother = finswap;
}
if (bdztail != 0.0) {
wlength = scale_expansion_zeroelim(catlen, cat, bdztail, w);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
finother);
finswap = finnow; finnow = finother; finother = finswap;
}
if (cdztail != 0.0) {
wlength = scale_expansion_zeroelim(abtlen, abt, cdztail, w);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
finother);
finswap = finnow; finnow = finother; finother = finswap;
}
return finnow[finlength - 1];
}
#ifdef USE_CGAL_PREDICATES
REAL orient3d(REAL *pa, REAL *pb, REAL *pc, REAL *pd)
{
return (REAL)
- cgal_pred_obj.orientation_3_object()
(Point(pa[0], pa[1], pa[2]),
Point(pb[0], pb[1], pb[2]),
Point(pc[0], pc[1], pc[2]),
Point(pd[0], pd[1], pd[2]));
}
#else
REAL orient3d(REAL *pa, REAL *pb, REAL *pc, REAL *pd)
{
REAL adx, bdx, cdx, ady, bdy, cdy, adz, bdz, cdz;
REAL bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady;
REAL det;
adx = pa[0] - pd[0];
ady = pa[1] - pd[1];
adz = pa[2] - pd[2];
bdx = pb[0] - pd[0];
bdy = pb[1] - pd[1];
bdz = pb[2] - pd[2];
cdx = pc[0] - pd[0];
cdy = pc[1] - pd[1];
cdz = pc[2] - pd[2];
bdxcdy = bdx * cdy;
cdxbdy = cdx * bdy;
cdxady = cdx * ady;
adxcdy = adx * cdy;
adxbdy = adx * bdy;
bdxady = bdx * ady;
det = adz * (bdxcdy - cdxbdy)
+ bdz * (cdxady - adxcdy)
+ cdz * (adxbdy - bdxady);
if (_use_inexact_arith) {
return det;
}
if (_use_static_filter) {
//if (fabs(det) > o3dstaticfilter) return det;
if (det > o3dstaticfilter) return det;
if (det < -o3dstaticfilter) return det;
}
REAL permanent, errbound;
permanent = (Absolute(bdxcdy) + Absolute(cdxbdy)) * Absolute(adz)
+ (Absolute(cdxady) + Absolute(adxcdy)) * Absolute(bdz)
+ (Absolute(adxbdy) + Absolute(bdxady)) * Absolute(cdz);
errbound = o3derrboundA * permanent;
if ((det > errbound) || (-det > errbound)) {
return det;
}
return orient3dadapt(pa, pb, pc, pd, permanent);
}
#endif // #ifdef USE_CGAL_PREDICATES
/*****************************************************************************/
/* */
/* incirclefast() Approximate 2D incircle test. Nonrobust. */
/* incircleexact() Exact 2D incircle test. Robust. */
/* incircleslow() Another exact 2D incircle test. Robust. */
/* incircle() Adaptive exact 2D incircle test. Robust. */
/* */
/* Return a positive value if the point pd lies inside the */
/* circle passing through pa, pb, and pc; a negative value if */
/* it lies outside; and zero if the four points are cocircular.*/
/* The points pa, pb, and pc must be in counterclockwise */
/* order, or the sign of the result will be reversed. */
/* */
/* Only the first and last routine should be used; the middle two are for */
/* timings. */
/* */
/* The last three use exact arithmetic to ensure a correct answer. The */
/* result returned is the determinant of a matrix. In incircle() only, */
/* this determinant is computed adaptively, in the sense that exact */
/* arithmetic is used only to the degree it is needed to ensure that the */
/* returned value has the correct sign. Hence, incircle() is usually quite */
/* fast, but will run more slowly when the input points are cocircular or */
/* nearly so. */
/* */
/*****************************************************************************/
REAL incirclefast(REAL *pa, REAL *pb, REAL *pc, REAL *pd)
{
REAL adx, ady, bdx, bdy, cdx, cdy;
REAL abdet, bcdet, cadet;
REAL alift, blift, clift;
adx = pa[0] - pd[0];
ady = pa[1] - pd[1];
bdx = pb[0] - pd[0];
bdy = pb[1] - pd[1];
cdx = pc[0] - pd[0];
cdy = pc[1] - pd[1];
abdet = adx * bdy - bdx * ady;
bcdet = bdx * cdy - cdx * bdy;
cadet = cdx * ady - adx * cdy;
alift = adx * adx + ady * ady;
blift = bdx * bdx + bdy * bdy;
clift = cdx * cdx + cdy * cdy;
return alift * bcdet + blift * cadet + clift * abdet;
}
REAL incircleexact(REAL *pa, REAL *pb, REAL *pc, REAL *pd)
{
INEXACT REAL axby1, bxcy1, cxdy1, dxay1, axcy1, bxdy1;
INEXACT REAL bxay1, cxby1, dxcy1, axdy1, cxay1, dxby1;
REAL axby0, bxcy0, cxdy0, dxay0, axcy0, bxdy0;
REAL bxay0, cxby0, dxcy0, axdy0, cxay0, dxby0;
REAL ab[4], bc[4], cd[4], da[4], ac[4], bd[4];
REAL temp8[8];
int templen;
REAL abc[12], bcd[12], cda[12], dab[12];
int abclen, bcdlen, cdalen, dablen;
REAL det24x[24], det24y[24], det48x[48], det48y[48];
int xlen, ylen;
REAL adet[96], bdet[96], cdet[96], ddet[96];
int alen, blen, clen, dlen;
REAL abdet[192], cddet[192];
int ablen, cdlen;
REAL deter[384];
int deterlen;
int i;
INEXACT REAL bvirt;
REAL avirt, bround, around;
INEXACT REAL c;
INEXACT REAL abig;
REAL ahi, alo, bhi, blo;
REAL err1, err2, err3;
INEXACT REAL _i, _j;
REAL _0;
Two_Product(pa[0], pb[1], axby1, axby0);
Two_Product(pb[0], pa[1], bxay1, bxay0);
Two_Two_Diff(axby1, axby0, bxay1, bxay0, ab[3], ab[2], ab[1], ab[0]);
Two_Product(pb[0], pc[1], bxcy1, bxcy0);
Two_Product(pc[0], pb[1], cxby1, cxby0);
Two_Two_Diff(bxcy1, bxcy0, cxby1, cxby0, bc[3], bc[2], bc[1], bc[0]);
Two_Product(pc[0], pd[1], cxdy1, cxdy0);
Two_Product(pd[0], pc[1], dxcy1, dxcy0);
Two_Two_Diff(cxdy1, cxdy0, dxcy1, dxcy0, cd[3], cd[2], cd[1], cd[0]);
Two_Product(pd[0], pa[1], dxay1, dxay0);
Two_Product(pa[0], pd[1], axdy1, axdy0);
Two_Two_Diff(dxay1, dxay0, axdy1, axdy0, da[3], da[2], da[1], da[0]);
Two_Product(pa[0], pc[1], axcy1, axcy0);
Two_Product(pc[0], pa[1], cxay1, cxay0);
Two_Two_Diff(axcy1, axcy0, cxay1, cxay0, ac[3], ac[2], ac[1], ac[0]);
Two_Product(pb[0], pd[1], bxdy1, bxdy0);
Two_Product(pd[0], pb[1], dxby1, dxby0);
Two_Two_Diff(bxdy1, bxdy0, dxby1, dxby0, bd[3], bd[2], bd[1], bd[0]);
templen = fast_expansion_sum_zeroelim(4, cd, 4, da, temp8);
cdalen = fast_expansion_sum_zeroelim(templen, temp8, 4, ac, cda);
templen = fast_expansion_sum_zeroelim(4, da, 4, ab, temp8);
dablen = fast_expansion_sum_zeroelim(templen, temp8, 4, bd, dab);
for (i = 0; i < 4; i++) {
bd[i] = -bd[i];
ac[i] = -ac[i];
}
templen = fast_expansion_sum_zeroelim(4, ab, 4, bc, temp8);
abclen = fast_expansion_sum_zeroelim(templen, temp8, 4, ac, abc);
templen = fast_expansion_sum_zeroelim(4, bc, 4, cd, temp8);
bcdlen = fast_expansion_sum_zeroelim(templen, temp8, 4, bd, bcd);
xlen = scale_expansion_zeroelim(bcdlen, bcd, pa[0], det24x);
xlen = scale_expansion_zeroelim(xlen, det24x, pa[0], det48x);
ylen = scale_expansion_zeroelim(bcdlen, bcd, pa[1], det24y);
ylen = scale_expansion_zeroelim(ylen, det24y, pa[1], det48y);
alen = fast_expansion_sum_zeroelim(xlen, det48x, ylen, det48y, adet);
xlen = scale_expansion_zeroelim(cdalen, cda, pb[0], det24x);
xlen = scale_expansion_zeroelim(xlen, det24x, -pb[0], det48x);
ylen = scale_expansion_zeroelim(cdalen, cda, pb[1], det24y);
ylen = scale_expansion_zeroelim(ylen, det24y, -pb[1], det48y);
blen = fast_expansion_sum_zeroelim(xlen, det48x, ylen, det48y, bdet);
xlen = scale_expansion_zeroelim(dablen, dab, pc[0], det24x);
xlen = scale_expansion_zeroelim(xlen, det24x, pc[0], det48x);
ylen = scale_expansion_zeroelim(dablen, dab, pc[1], det24y);
ylen = scale_expansion_zeroelim(ylen, det24y, pc[1], det48y);
clen = fast_expansion_sum_zeroelim(xlen, det48x, ylen, det48y, cdet);
xlen = scale_expansion_zeroelim(abclen, abc, pd[0], det24x);
xlen = scale_expansion_zeroelim(xlen, det24x, -pd[0], det48x);
ylen = scale_expansion_zeroelim(abclen, abc, pd[1], det24y);
ylen = scale_expansion_zeroelim(ylen, det24y, -pd[1], det48y);
dlen = fast_expansion_sum_zeroelim(xlen, det48x, ylen, det48y, ddet);
ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
cdlen = fast_expansion_sum_zeroelim(clen, cdet, dlen, ddet, cddet);
deterlen = fast_expansion_sum_zeroelim(ablen, abdet, cdlen, cddet, deter);
return deter[deterlen - 1];
}
REAL incircleslow(REAL *pa, REAL *pb, REAL *pc, REAL *pd)
{
INEXACT REAL adx, bdx, cdx, ady, bdy, cdy;
REAL adxtail, bdxtail, cdxtail;
REAL adytail, bdytail, cdytail;
REAL negate, negatetail;
INEXACT REAL axby7, bxcy7, axcy7, bxay7, cxby7, cxay7;
REAL axby[8], bxcy[8], axcy[8], bxay[8], cxby[8], cxay[8];
REAL temp16[16];
int temp16len;
REAL detx[32], detxx[64], detxt[32], detxxt[64], detxtxt[64];
int xlen, xxlen, xtlen, xxtlen, xtxtlen;
REAL x1[128], x2[192];
int x1len, x2len;
REAL dety[32], detyy[64], detyt[32], detyyt[64], detytyt[64];
int ylen, yylen, ytlen, yytlen, ytytlen;
REAL y1[128], y2[192];
int y1len, y2len;
REAL adet[384], bdet[384], cdet[384], abdet[768], deter[1152];
int alen, blen, clen, ablen, deterlen;
int i;
INEXACT REAL bvirt;
REAL avirt, bround, around;
INEXACT REAL c;
INEXACT REAL abig;
REAL a0hi, a0lo, a1hi, a1lo, bhi, blo;
REAL err1, err2, err3;
INEXACT REAL _i, _j, _k, _l, _m, _n;
REAL _0, _1, _2;
Two_Diff(pa[0], pd[0], adx, adxtail);
Two_Diff(pa[1], pd[1], ady, adytail);
Two_Diff(pb[0], pd[0], bdx, bdxtail);
Two_Diff(pb[1], pd[1], bdy, bdytail);
Two_Diff(pc[0], pd[0], cdx, cdxtail);
Two_Diff(pc[1], pd[1], cdy, cdytail);
Two_Two_Product(adx, adxtail, bdy, bdytail,
axby7, axby[6], axby[5], axby[4],
axby[3], axby[2], axby[1], axby[0]);
axby[7] = axby7;
negate = -ady;
negatetail = -adytail;
Two_Two_Product(bdx, bdxtail, negate, negatetail,
bxay7, bxay[6], bxay[5], bxay[4],
bxay[3], bxay[2], bxay[1], bxay[0]);
bxay[7] = bxay7;
Two_Two_Product(bdx, bdxtail, cdy, cdytail,
bxcy7, bxcy[6], bxcy[5], bxcy[4],
bxcy[3], bxcy[2], bxcy[1], bxcy[0]);
bxcy[7] = bxcy7;
negate = -bdy;
negatetail = -bdytail;
Two_Two_Product(cdx, cdxtail, negate, negatetail,
cxby7, cxby[6], cxby[5], cxby[4],
cxby[3], cxby[2], cxby[1], cxby[0]);
cxby[7] = cxby7;
Two_Two_Product(cdx, cdxtail, ady, adytail,
cxay7, cxay[6], cxay[5], cxay[4],
cxay[3], cxay[2], cxay[1], cxay[0]);
cxay[7] = cxay7;
negate = -cdy;
negatetail = -cdytail;
Two_Two_Product(adx, adxtail, negate, negatetail,
axcy7, axcy[6], axcy[5], axcy[4],
axcy[3], axcy[2], axcy[1], axcy[0]);
axcy[7] = axcy7;
temp16len = fast_expansion_sum_zeroelim(8, bxcy, 8, cxby, temp16);
xlen = scale_expansion_zeroelim(temp16len, temp16, adx, detx);
xxlen = scale_expansion_zeroelim(xlen, detx, adx, detxx);
xtlen = scale_expansion_zeroelim(temp16len, temp16, adxtail, detxt);
xxtlen = scale_expansion_zeroelim(xtlen, detxt, adx, detxxt);
for (i = 0; i < xxtlen; i++) {
detxxt[i] *= 2.0;
}
xtxtlen = scale_expansion_zeroelim(xtlen, detxt, adxtail, detxtxt);
x1len = fast_expansion_sum_zeroelim(xxlen, detxx, xxtlen, detxxt, x1);
x2len = fast_expansion_sum_zeroelim(x1len, x1, xtxtlen, detxtxt, x2);
ylen = scale_expansion_zeroelim(temp16len, temp16, ady, dety);
yylen = scale_expansion_zeroelim(ylen, dety, ady, detyy);
ytlen = scale_expansion_zeroelim(temp16len, temp16, adytail, detyt);
yytlen = scale_expansion_zeroelim(ytlen, detyt, ady, detyyt);
for (i = 0; i < yytlen; i++) {
detyyt[i] *= 2.0;
}
ytytlen = scale_expansion_zeroelim(ytlen, detyt, adytail, detytyt);
y1len = fast_expansion_sum_zeroelim(yylen, detyy, yytlen, detyyt, y1);
y2len = fast_expansion_sum_zeroelim(y1len, y1, ytytlen, detytyt, y2);
alen = fast_expansion_sum_zeroelim(x2len, x2, y2len, y2, adet);
temp16len = fast_expansion_sum_zeroelim(8, cxay, 8, axcy, temp16);
xlen = scale_expansion_zeroelim(temp16len, temp16, bdx, detx);
xxlen = scale_expansion_zeroelim(xlen, detx, bdx, detxx);
xtlen = scale_expansion_zeroelim(temp16len, temp16, bdxtail, detxt);
xxtlen = scale_expansion_zeroelim(xtlen, detxt, bdx, detxxt);
for (i = 0; i < xxtlen; i++) {
detxxt[i] *= 2.0;
}
xtxtlen = scale_expansion_zeroelim(xtlen, detxt, bdxtail, detxtxt);
x1len = fast_expansion_sum_zeroelim(xxlen, detxx, xxtlen, detxxt, x1);
x2len = fast_expansion_sum_zeroelim(x1len, x1, xtxtlen, detxtxt, x2);
ylen = scale_expansion_zeroelim(temp16len, temp16, bdy, dety);
yylen = scale_expansion_zeroelim(ylen, dety, bdy, detyy);
ytlen = scale_expansion_zeroelim(temp16len, temp16, bdytail, detyt);
yytlen = scale_expansion_zeroelim(ytlen, detyt, bdy, detyyt);
for (i = 0; i < yytlen; i++) {
detyyt[i] *= 2.0;
}
ytytlen = scale_expansion_zeroelim(ytlen, detyt, bdytail, detytyt);
y1len = fast_expansion_sum_zeroelim(yylen, detyy, yytlen, detyyt, y1);
y2len = fast_expansion_sum_zeroelim(y1len, y1, ytytlen, detytyt, y2);
blen = fast_expansion_sum_zeroelim(x2len, x2, y2len, y2, bdet);
temp16len = fast_expansion_sum_zeroelim(8, axby, 8, bxay, temp16);
xlen = scale_expansion_zeroelim(temp16len, temp16, cdx, detx);
xxlen = scale_expansion_zeroelim(xlen, detx, cdx, detxx);
xtlen = scale_expansion_zeroelim(temp16len, temp16, cdxtail, detxt);
xxtlen = scale_expansion_zeroelim(xtlen, detxt, cdx, detxxt);
for (i = 0; i < xxtlen; i++) {
detxxt[i] *= 2.0;
}
xtxtlen = scale_expansion_zeroelim(xtlen, detxt, cdxtail, detxtxt);
x1len = fast_expansion_sum_zeroelim(xxlen, detxx, xxtlen, detxxt, x1);
x2len = fast_expansion_sum_zeroelim(x1len, x1, xtxtlen, detxtxt, x2);
ylen = scale_expansion_zeroelim(temp16len, temp16, cdy, dety);
yylen = scale_expansion_zeroelim(ylen, dety, cdy, detyy);
ytlen = scale_expansion_zeroelim(temp16len, temp16, cdytail, detyt);
yytlen = scale_expansion_zeroelim(ytlen, detyt, cdy, detyyt);
for (i = 0; i < yytlen; i++) {
detyyt[i] *= 2.0;
}
ytytlen = scale_expansion_zeroelim(ytlen, detyt, cdytail, detytyt);
y1len = fast_expansion_sum_zeroelim(yylen, detyy, yytlen, detyyt, y1);
y2len = fast_expansion_sum_zeroelim(y1len, y1, ytytlen, detytyt, y2);
clen = fast_expansion_sum_zeroelim(x2len, x2, y2len, y2, cdet);
ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
deterlen = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, deter);
return deter[deterlen - 1];
}
REAL incircleadapt(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL permanent)
{
INEXACT REAL adx, bdx, cdx, ady, bdy, cdy;
REAL det, errbound;
INEXACT REAL bdxcdy1, cdxbdy1, cdxady1, adxcdy1, adxbdy1, bdxady1;
REAL bdxcdy0, cdxbdy0, cdxady0, adxcdy0, adxbdy0, bdxady0;
REAL bc[4], ca[4], ab[4];
INEXACT REAL bc3, ca3, ab3;
REAL axbc[8], axxbc[16], aybc[8], ayybc[16], adet[32];
int axbclen, axxbclen, aybclen, ayybclen, alen;
REAL bxca[8], bxxca[16], byca[8], byyca[16], bdet[32];
int bxcalen, bxxcalen, bycalen, byycalen, blen;
REAL cxab[8], cxxab[16], cyab[8], cyyab[16], cdet[32];
int cxablen, cxxablen, cyablen, cyyablen, clen;
REAL abdet[64];
int ablen;
REAL fin1[1152], fin2[1152];
REAL *finnow, *finother, *finswap;
int finlength;
REAL adxtail, bdxtail, cdxtail, adytail, bdytail, cdytail;
INEXACT REAL adxadx1, adyady1, bdxbdx1, bdybdy1, cdxcdx1, cdycdy1;
REAL adxadx0, adyady0, bdxbdx0, bdybdy0, cdxcdx0, cdycdy0;
REAL aa[4], bb[4], cc[4];
INEXACT REAL aa3, bb3, cc3;
INEXACT REAL ti1, tj1;
REAL ti0, tj0;
REAL u[4], v[4];
INEXACT REAL u3, v3;
REAL temp8[8], temp16a[16], temp16b[16], temp16c[16];
REAL temp32a[32], temp32b[32], temp48[48], temp64[64];
int temp8len, temp16alen, temp16blen, temp16clen;
int temp32alen, temp32blen, temp48len, temp64len;
REAL axtbb[8], axtcc[8], aytbb[8], aytcc[8];
int axtbblen, axtcclen, aytbblen, aytcclen;
REAL bxtaa[8], bxtcc[8], bytaa[8], bytcc[8];
int bxtaalen, bxtcclen, bytaalen, bytcclen;
REAL cxtaa[8], cxtbb[8], cytaa[8], cytbb[8];
int cxtaalen, cxtbblen, cytaalen, cytbblen;
REAL axtbc[8], aytbc[8], bxtca[8], bytca[8], cxtab[8], cytab[8];
int axtbclen, aytbclen, bxtcalen, bytcalen, cxtablen, cytablen;
REAL axtbct[16], aytbct[16], bxtcat[16], bytcat[16], cxtabt[16], cytabt[16];
int axtbctlen, aytbctlen, bxtcatlen, bytcatlen, cxtabtlen, cytabtlen;
REAL axtbctt[8], aytbctt[8], bxtcatt[8];
REAL bytcatt[8], cxtabtt[8], cytabtt[8];
int axtbcttlen, aytbcttlen, bxtcattlen, bytcattlen, cxtabttlen, cytabttlen;
REAL abt[8], bct[8], cat[8];
int abtlen, bctlen, catlen;
REAL abtt[4], bctt[4], catt[4];
int abttlen, bcttlen, cattlen;
INEXACT REAL abtt3, bctt3, catt3;
REAL negate;
INEXACT REAL bvirt;
REAL avirt, bround, around;
INEXACT REAL c;
INEXACT REAL abig;
REAL ahi, alo, bhi, blo;
REAL err1, err2, err3;
INEXACT REAL _i, _j;
REAL _0;
// Avoid compiler warnings. H. Si, 2012-02-16.
axtbclen = aytbclen = bxtcalen = bytcalen = cxtablen = cytablen = 0;
adx = (REAL) (pa[0] - pd[0]);
bdx = (REAL) (pb[0] - pd[0]);
cdx = (REAL) (pc[0] - pd[0]);
ady = (REAL) (pa[1] - pd[1]);
bdy = (REAL) (pb[1] - pd[1]);
cdy = (REAL) (pc[1] - pd[1]);
Two_Product(bdx, cdy, bdxcdy1, bdxcdy0);
Two_Product(cdx, bdy, cdxbdy1, cdxbdy0);
Two_Two_Diff(bdxcdy1, bdxcdy0, cdxbdy1, cdxbdy0, bc3, bc[2], bc[1], bc[0]);
bc[3] = bc3;
axbclen = scale_expansion_zeroelim(4, bc, adx, axbc);
axxbclen = scale_expansion_zeroelim(axbclen, axbc, adx, axxbc);
aybclen = scale_expansion_zeroelim(4, bc, ady, aybc);
ayybclen = scale_expansion_zeroelim(aybclen, aybc, ady, ayybc);
alen = fast_expansion_sum_zeroelim(axxbclen, axxbc, ayybclen, ayybc, adet);
Two_Product(cdx, ady, cdxady1, cdxady0);
Two_Product(adx, cdy, adxcdy1, adxcdy0);
Two_Two_Diff(cdxady1, cdxady0, adxcdy1, adxcdy0, ca3, ca[2], ca[1], ca[0]);
ca[3] = ca3;
bxcalen = scale_expansion_zeroelim(4, ca, bdx, bxca);
bxxcalen = scale_expansion_zeroelim(bxcalen, bxca, bdx, bxxca);
bycalen = scale_expansion_zeroelim(4, ca, bdy, byca);
byycalen = scale_expansion_zeroelim(bycalen, byca, bdy, byyca);
blen = fast_expansion_sum_zeroelim(bxxcalen, bxxca, byycalen, byyca, bdet);
Two_Product(adx, bdy, adxbdy1, adxbdy0);
Two_Product(bdx, ady, bdxady1, bdxady0);
Two_Two_Diff(adxbdy1, adxbdy0, bdxady1, bdxady0, ab3, ab[2], ab[1], ab[0]);
ab[3] = ab3;
cxablen = scale_expansion_zeroelim(4, ab, cdx, cxab);
cxxablen = scale_expansion_zeroelim(cxablen, cxab, cdx, cxxab);
cyablen = scale_expansion_zeroelim(4, ab, cdy, cyab);
cyyablen = scale_expansion_zeroelim(cyablen, cyab, cdy, cyyab);
clen = fast_expansion_sum_zeroelim(cxxablen, cxxab, cyyablen, cyyab, cdet);
ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
finlength = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, fin1);
det = estimate(finlength, fin1);
errbound = iccerrboundB * permanent;
if ((det >= errbound) || (-det >= errbound)) {
return det;
}
Two_Diff_Tail(pa[0], pd[0], adx, adxtail);
Two_Diff_Tail(pa[1], pd[1], ady, adytail);
Two_Diff_Tail(pb[0], pd[0], bdx, bdxtail);
Two_Diff_Tail(pb[1], pd[1], bdy, bdytail);
Two_Diff_Tail(pc[0], pd[0], cdx, cdxtail);
Two_Diff_Tail(pc[1], pd[1], cdy, cdytail);
if ((adxtail == 0.0) && (bdxtail == 0.0) && (cdxtail == 0.0)
&& (adytail == 0.0) && (bdytail == 0.0) && (cdytail == 0.0)) {
return det;
}
errbound = iccerrboundC * permanent + resulterrbound * Absolute(det);
det += ((adx * adx + ady * ady) * ((bdx * cdytail + cdy * bdxtail)
- (bdy * cdxtail + cdx * bdytail))
+ 2.0 * (adx * adxtail + ady * adytail) * (bdx * cdy - bdy * cdx))
+ ((bdx * bdx + bdy * bdy) * ((cdx * adytail + ady * cdxtail)
- (cdy * adxtail + adx * cdytail))
+ 2.0 * (bdx * bdxtail + bdy * bdytail) * (cdx * ady - cdy * adx))
+ ((cdx * cdx + cdy * cdy) * ((adx * bdytail + bdy * adxtail)
- (ady * bdxtail + bdx * adytail))
+ 2.0 * (cdx * cdxtail + cdy * cdytail) * (adx * bdy - ady * bdx));
if ((det >= errbound) || (-det >= errbound)) {
return det;
}
finnow = fin1;
finother = fin2;
if ((bdxtail != 0.0) || (bdytail != 0.0)
|| (cdxtail != 0.0) || (cdytail != 0.0)) {
Square(adx, adxadx1, adxadx0);
Square(ady, adyady1, adyady0);
Two_Two_Sum(adxadx1, adxadx0, adyady1, adyady0, aa3, aa[2], aa[1], aa[0]);
aa[3] = aa3;
}
if ((cdxtail != 0.0) || (cdytail != 0.0)
|| (adxtail != 0.0) || (adytail != 0.0)) {
Square(bdx, bdxbdx1, bdxbdx0);
Square(bdy, bdybdy1, bdybdy0);
Two_Two_Sum(bdxbdx1, bdxbdx0, bdybdy1, bdybdy0, bb3, bb[2], bb[1], bb[0]);
bb[3] = bb3;
}
if ((adxtail != 0.0) || (adytail != 0.0)
|| (bdxtail != 0.0) || (bdytail != 0.0)) {
Square(cdx, cdxcdx1, cdxcdx0);
Square(cdy, cdycdy1, cdycdy0);
Two_Two_Sum(cdxcdx1, cdxcdx0, cdycdy1, cdycdy0, cc3, cc[2], cc[1], cc[0]);
cc[3] = cc3;
}
if (adxtail != 0.0) {
axtbclen = scale_expansion_zeroelim(4, bc, adxtail, axtbc);
temp16alen = scale_expansion_zeroelim(axtbclen, axtbc, 2.0 * adx,
temp16a);
axtcclen = scale_expansion_zeroelim(4, cc, adxtail, axtcc);
temp16blen = scale_expansion_zeroelim(axtcclen, axtcc, bdy, temp16b);
axtbblen = scale_expansion_zeroelim(4, bb, adxtail, axtbb);
temp16clen = scale_expansion_zeroelim(axtbblen, axtbb, -cdy, temp16c);
temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
temp16blen, temp16b, temp32a);
temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c,
temp32alen, temp32a, temp48);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
temp48, finother);
finswap = finnow; finnow = finother; finother = finswap;
}
if (adytail != 0.0) {
aytbclen = scale_expansion_zeroelim(4, bc, adytail, aytbc);
temp16alen = scale_expansion_zeroelim(aytbclen, aytbc, 2.0 * ady,
temp16a);
aytbblen = scale_expansion_zeroelim(4, bb, adytail, aytbb);
temp16blen = scale_expansion_zeroelim(aytbblen, aytbb, cdx, temp16b);
aytcclen = scale_expansion_zeroelim(4, cc, adytail, aytcc);
temp16clen = scale_expansion_zeroelim(aytcclen, aytcc, -bdx, temp16c);
temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
temp16blen, temp16b, temp32a);
temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c,
temp32alen, temp32a, temp48);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
temp48, finother);
finswap = finnow; finnow = finother; finother = finswap;
}
if (bdxtail != 0.0) {
bxtcalen = scale_expansion_zeroelim(4, ca, bdxtail, bxtca);
temp16alen = scale_expansion_zeroelim(bxtcalen, bxtca, 2.0 * bdx,
temp16a);
bxtaalen = scale_expansion_zeroelim(4, aa, bdxtail, bxtaa);
temp16blen = scale_expansion_zeroelim(bxtaalen, bxtaa, cdy, temp16b);
bxtcclen = scale_expansion_zeroelim(4, cc, bdxtail, bxtcc);
temp16clen = scale_expansion_zeroelim(bxtcclen, bxtcc, -ady, temp16c);
temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
temp16blen, temp16b, temp32a);
temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c,
temp32alen, temp32a, temp48);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
temp48, finother);
finswap = finnow; finnow = finother; finother = finswap;
}
if (bdytail != 0.0) {
bytcalen = scale_expansion_zeroelim(4, ca, bdytail, bytca);
temp16alen = scale_expansion_zeroelim(bytcalen, bytca, 2.0 * bdy,
temp16a);
bytcclen = scale_expansion_zeroelim(4, cc, bdytail, bytcc);
temp16blen = scale_expansion_zeroelim(bytcclen, bytcc, adx, temp16b);
bytaalen = scale_expansion_zeroelim(4, aa, bdytail, bytaa);
temp16clen = scale_expansion_zeroelim(bytaalen, bytaa, -cdx, temp16c);
temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
temp16blen, temp16b, temp32a);
temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c,
temp32alen, temp32a, temp48);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
temp48, finother);
finswap = finnow; finnow = finother; finother = finswap;
}
if (cdxtail != 0.0) {
cxtablen = scale_expansion_zeroelim(4, ab, cdxtail, cxtab);
temp16alen = scale_expansion_zeroelim(cxtablen, cxtab, 2.0 * cdx,
temp16a);
cxtbblen = scale_expansion_zeroelim(4, bb, cdxtail, cxtbb);
temp16blen = scale_expansion_zeroelim(cxtbblen, cxtbb, ady, temp16b);
cxtaalen = scale_expansion_zeroelim(4, aa, cdxtail, cxtaa);
temp16clen = scale_expansion_zeroelim(cxtaalen, cxtaa, -bdy, temp16c);
temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
temp16blen, temp16b, temp32a);
temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c,
temp32alen, temp32a, temp48);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
temp48, finother);
finswap = finnow; finnow = finother; finother = finswap;
}
if (cdytail != 0.0) {
cytablen = scale_expansion_zeroelim(4, ab, cdytail, cytab);
temp16alen = scale_expansion_zeroelim(cytablen, cytab, 2.0 * cdy,
temp16a);
cytaalen = scale_expansion_zeroelim(4, aa, cdytail, cytaa);
temp16blen = scale_expansion_zeroelim(cytaalen, cytaa, bdx, temp16b);
cytbblen = scale_expansion_zeroelim(4, bb, cdytail, cytbb);
temp16clen = scale_expansion_zeroelim(cytbblen, cytbb, -adx, temp16c);
temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
temp16blen, temp16b, temp32a);
temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c,
temp32alen, temp32a, temp48);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
temp48, finother);
finswap = finnow; finnow = finother; finother = finswap;
}
if ((adxtail != 0.0) || (adytail != 0.0)) {
if ((bdxtail != 0.0) || (bdytail != 0.0)
|| (cdxtail != 0.0) || (cdytail != 0.0)) {
Two_Product(bdxtail, cdy, ti1, ti0);
Two_Product(bdx, cdytail, tj1, tj0);
Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]);
u[3] = u3;
negate = -bdy;
Two_Product(cdxtail, negate, ti1, ti0);
negate = -bdytail;
Two_Product(cdx, negate, tj1, tj0);
Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]);
v[3] = v3;
bctlen = fast_expansion_sum_zeroelim(4, u, 4, v, bct);
Two_Product(bdxtail, cdytail, ti1, ti0);
Two_Product(cdxtail, bdytail, tj1, tj0);
Two_Two_Diff(ti1, ti0, tj1, tj0, bctt3, bctt[2], bctt[1], bctt[0]);
bctt[3] = bctt3;
bcttlen = 4;
} else {
bct[0] = 0.0;
bctlen = 1;
bctt[0] = 0.0;
bcttlen = 1;
}
if (adxtail != 0.0) {
temp16alen = scale_expansion_zeroelim(axtbclen, axtbc, adxtail, temp16a);
axtbctlen = scale_expansion_zeroelim(bctlen, bct, adxtail, axtbct);
temp32alen = scale_expansion_zeroelim(axtbctlen, axtbct, 2.0 * adx,
temp32a);
temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a,
temp32alen, temp32a, temp48);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
temp48, finother);
finswap = finnow; finnow = finother; finother = finswap;
if (bdytail != 0.0) {
temp8len = scale_expansion_zeroelim(4, cc, adxtail, temp8);
temp16alen = scale_expansion_zeroelim(temp8len, temp8, bdytail,
temp16a);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen,
temp16a, finother);
finswap = finnow; finnow = finother; finother = finswap;
}
if (cdytail != 0.0) {
temp8len = scale_expansion_zeroelim(4, bb, -adxtail, temp8);
temp16alen = scale_expansion_zeroelim(temp8len, temp8, cdytail,
temp16a);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen,
temp16a, finother);
finswap = finnow; finnow = finother; finother = finswap;
}
temp32alen = scale_expansion_zeroelim(axtbctlen, axtbct, adxtail,
temp32a);
axtbcttlen = scale_expansion_zeroelim(bcttlen, bctt, adxtail, axtbctt);
temp16alen = scale_expansion_zeroelim(axtbcttlen, axtbctt, 2.0 * adx,
temp16a);
temp16blen = scale_expansion_zeroelim(axtbcttlen, axtbctt, adxtail,
temp16b);
temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
temp16blen, temp16b, temp32b);
temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a,
temp32blen, temp32b, temp64);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len,
temp64, finother);
finswap = finnow; finnow = finother; finother = finswap;
}
if (adytail != 0.0) {
temp16alen = scale_expansion_zeroelim(aytbclen, aytbc, adytail, temp16a);
aytbctlen = scale_expansion_zeroelim(bctlen, bct, adytail, aytbct);
temp32alen = scale_expansion_zeroelim(aytbctlen, aytbct, 2.0 * ady,
temp32a);
temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a,
temp32alen, temp32a, temp48);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
temp48, finother);
finswap = finnow; finnow = finother; finother = finswap;
temp32alen = scale_expansion_zeroelim(aytbctlen, aytbct, adytail,
temp32a);
aytbcttlen = scale_expansion_zeroelim(bcttlen, bctt, adytail, aytbctt);
temp16alen = scale_expansion_zeroelim(aytbcttlen, aytbctt, 2.0 * ady,
temp16a);
temp16blen = scale_expansion_zeroelim(aytbcttlen, aytbctt, adytail,
temp16b);
temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
temp16blen, temp16b, temp32b);
temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a,
temp32blen, temp32b, temp64);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len,
temp64, finother);
finswap = finnow; finnow = finother; finother = finswap;
}
}
if ((bdxtail != 0.0) || (bdytail != 0.0)) {
if ((cdxtail != 0.0) || (cdytail != 0.0)
|| (adxtail != 0.0) || (adytail != 0.0)) {
Two_Product(cdxtail, ady, ti1, ti0);
Two_Product(cdx, adytail, tj1, tj0);
Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]);
u[3] = u3;
negate = -cdy;
Two_Product(adxtail, negate, ti1, ti0);
negate = -cdytail;
Two_Product(adx, negate, tj1, tj0);
Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]);
v[3] = v3;
catlen = fast_expansion_sum_zeroelim(4, u, 4, v, cat);
Two_Product(cdxtail, adytail, ti1, ti0);
Two_Product(adxtail, cdytail, tj1, tj0);
Two_Two_Diff(ti1, ti0, tj1, tj0, catt3, catt[2], catt[1], catt[0]);
catt[3] = catt3;
cattlen = 4;
} else {
cat[0] = 0.0;
catlen = 1;
catt[0] = 0.0;
cattlen = 1;
}
if (bdxtail != 0.0) {
temp16alen = scale_expansion_zeroelim(bxtcalen, bxtca, bdxtail, temp16a);
bxtcatlen = scale_expansion_zeroelim(catlen, cat, bdxtail, bxtcat);
temp32alen = scale_expansion_zeroelim(bxtcatlen, bxtcat, 2.0 * bdx,
temp32a);
temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a,
temp32alen, temp32a, temp48);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
temp48, finother);
finswap = finnow; finnow = finother; finother = finswap;
if (cdytail != 0.0) {
temp8len = scale_expansion_zeroelim(4, aa, bdxtail, temp8);
temp16alen = scale_expansion_zeroelim(temp8len, temp8, cdytail,
temp16a);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen,
temp16a, finother);
finswap = finnow; finnow = finother; finother = finswap;
}
if (adytail != 0.0) {
temp8len = scale_expansion_zeroelim(4, cc, -bdxtail, temp8);
temp16alen = scale_expansion_zeroelim(temp8len, temp8, adytail,
temp16a);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen,
temp16a, finother);
finswap = finnow; finnow = finother; finother = finswap;
}
temp32alen = scale_expansion_zeroelim(bxtcatlen, bxtcat, bdxtail,
temp32a);
bxtcattlen = scale_expansion_zeroelim(cattlen, catt, bdxtail, bxtcatt);
temp16alen = scale_expansion_zeroelim(bxtcattlen, bxtcatt, 2.0 * bdx,
temp16a);
temp16blen = scale_expansion_zeroelim(bxtcattlen, bxtcatt, bdxtail,
temp16b);
temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
temp16blen, temp16b, temp32b);
temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a,
temp32blen, temp32b, temp64);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len,
temp64, finother);
finswap = finnow; finnow = finother; finother = finswap;
}
if (bdytail != 0.0) {
temp16alen = scale_expansion_zeroelim(bytcalen, bytca, bdytail, temp16a);
bytcatlen = scale_expansion_zeroelim(catlen, cat, bdytail, bytcat);
temp32alen = scale_expansion_zeroelim(bytcatlen, bytcat, 2.0 * bdy,
temp32a);
temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a,
temp32alen, temp32a, temp48);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
temp48, finother);
finswap = finnow; finnow = finother; finother = finswap;
temp32alen = scale_expansion_zeroelim(bytcatlen, bytcat, bdytail,
temp32a);
bytcattlen = scale_expansion_zeroelim(cattlen, catt, bdytail, bytcatt);
temp16alen = scale_expansion_zeroelim(bytcattlen, bytcatt, 2.0 * bdy,
temp16a);
temp16blen = scale_expansion_zeroelim(bytcattlen, bytcatt, bdytail,
temp16b);
temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
temp16blen, temp16b, temp32b);
temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a,
temp32blen, temp32b, temp64);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len,
temp64, finother);
finswap = finnow; finnow = finother; finother = finswap;
}
}
if ((cdxtail != 0.0) || (cdytail != 0.0)) {
if ((adxtail != 0.0) || (adytail != 0.0)
|| (bdxtail != 0.0) || (bdytail != 0.0)) {
Two_Product(adxtail, bdy, ti1, ti0);
Two_Product(adx, bdytail, tj1, tj0);
Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]);
u[3] = u3;
negate = -ady;
Two_Product(bdxtail, negate, ti1, ti0);
negate = -adytail;
Two_Product(bdx, negate, tj1, tj0);
Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]);
v[3] = v3;
abtlen = fast_expansion_sum_zeroelim(4, u, 4, v, abt);
Two_Product(adxtail, bdytail, ti1, ti0);
Two_Product(bdxtail, adytail, tj1, tj0);
Two_Two_Diff(ti1, ti0, tj1, tj0, abtt3, abtt[2], abtt[1], abtt[0]);
abtt[3] = abtt3;
abttlen = 4;
} else {
abt[0] = 0.0;
abtlen = 1;
abtt[0] = 0.0;
abttlen = 1;
}
if (cdxtail != 0.0) {
temp16alen = scale_expansion_zeroelim(cxtablen, cxtab, cdxtail, temp16a);
cxtabtlen = scale_expansion_zeroelim(abtlen, abt, cdxtail, cxtabt);
temp32alen = scale_expansion_zeroelim(cxtabtlen, cxtabt, 2.0 * cdx,
temp32a);
temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a,
temp32alen, temp32a, temp48);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
temp48, finother);
finswap = finnow; finnow = finother; finother = finswap;
if (adytail != 0.0) {
temp8len = scale_expansion_zeroelim(4, bb, cdxtail, temp8);
temp16alen = scale_expansion_zeroelim(temp8len, temp8, adytail,
temp16a);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen,
temp16a, finother);
finswap = finnow; finnow = finother; finother = finswap;
}
if (bdytail != 0.0) {
temp8len = scale_expansion_zeroelim(4, aa, -cdxtail, temp8);
temp16alen = scale_expansion_zeroelim(temp8len, temp8, bdytail,
temp16a);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen,
temp16a, finother);
finswap = finnow; finnow = finother; finother = finswap;
}
temp32alen = scale_expansion_zeroelim(cxtabtlen, cxtabt, cdxtail,
temp32a);
cxtabttlen = scale_expansion_zeroelim(abttlen, abtt, cdxtail, cxtabtt);
temp16alen = scale_expansion_zeroelim(cxtabttlen, cxtabtt, 2.0 * cdx,
temp16a);
temp16blen = scale_expansion_zeroelim(cxtabttlen, cxtabtt, cdxtail,
temp16b);
temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
temp16blen, temp16b, temp32b);
temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a,
temp32blen, temp32b, temp64);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len,
temp64, finother);
finswap = finnow; finnow = finother; finother = finswap;
}
if (cdytail != 0.0) {
temp16alen = scale_expansion_zeroelim(cytablen, cytab, cdytail, temp16a);
cytabtlen = scale_expansion_zeroelim(abtlen, abt, cdytail, cytabt);
temp32alen = scale_expansion_zeroelim(cytabtlen, cytabt, 2.0 * cdy,
temp32a);
temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a,
temp32alen, temp32a, temp48);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
temp48, finother);
finswap = finnow; finnow = finother; finother = finswap;
temp32alen = scale_expansion_zeroelim(cytabtlen, cytabt, cdytail,
temp32a);
cytabttlen = scale_expansion_zeroelim(abttlen, abtt, cdytail, cytabtt);
temp16alen = scale_expansion_zeroelim(cytabttlen, cytabtt, 2.0 * cdy,
temp16a);
temp16blen = scale_expansion_zeroelim(cytabttlen, cytabtt, cdytail,
temp16b);
temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
temp16blen, temp16b, temp32b);
temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a,
temp32blen, temp32b, temp64);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len,
temp64, finother);
finswap = finnow; finnow = finother; finother = finswap;
}
}
return finnow[finlength - 1];
}
REAL incircle(REAL *pa, REAL *pb, REAL *pc, REAL *pd)
{
REAL adx, bdx, cdx, ady, bdy, cdy;
REAL bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady;
REAL alift, blift, clift;
REAL det;
REAL permanent, errbound;
adx = pa[0] - pd[0];
bdx = pb[0] - pd[0];
cdx = pc[0] - pd[0];
ady = pa[1] - pd[1];
bdy = pb[1] - pd[1];
cdy = pc[1] - pd[1];
bdxcdy = bdx * cdy;
cdxbdy = cdx * bdy;
alift = adx * adx + ady * ady;
cdxady = cdx * ady;
adxcdy = adx * cdy;
blift = bdx * bdx + bdy * bdy;
adxbdy = adx * bdy;
bdxady = bdx * ady;
clift = cdx * cdx + cdy * cdy;
det = alift * (bdxcdy - cdxbdy)
+ blift * (cdxady - adxcdy)
+ clift * (adxbdy - bdxady);
permanent = (Absolute(bdxcdy) + Absolute(cdxbdy)) * alift
+ (Absolute(cdxady) + Absolute(adxcdy)) * blift
+ (Absolute(adxbdy) + Absolute(bdxady)) * clift;
errbound = iccerrboundA * permanent;
if ((det > errbound) || (-det > errbound)) {
return det;
}
return incircleadapt(pa, pb, pc, pd, permanent);
}
/*****************************************************************************/
/* */
/* inspherefast() Approximate 3D insphere test. Nonrobust. */
/* insphereexact() Exact 3D insphere test. Robust. */
/* insphereslow() Another exact 3D insphere test. Robust. */
/* insphere() Adaptive exact 3D insphere test. Robust. */
/* */
/* Return a positive value if the point pe lies inside the */
/* sphere passing through pa, pb, pc, and pd; a negative value */
/* if it lies outside; and zero if the five points are */
/* cospherical. The points pa, pb, pc, and pd must be ordered */
/* so that they have a positive orientation (as defined by */
/* orient3d()), or the sign of the result will be reversed. */
/* */
/* Only the first and last routine should be used; the middle two are for */
/* timings. */
/* */
/* The last three use exact arithmetic to ensure a correct answer. The */
/* result returned is the determinant of a matrix. In insphere() only, */
/* this determinant is computed adaptively, in the sense that exact */
/* arithmetic is used only to the degree it is needed to ensure that the */
/* returned value has the correct sign. Hence, insphere() is usually quite */
/* fast, but will run more slowly when the input points are cospherical or */
/* nearly so. */
/* */
/*****************************************************************************/
REAL inspherefast(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe)
{
REAL aex, bex, cex, dex;
REAL aey, bey, cey, dey;
REAL aez, bez, cez, dez;
REAL alift, blift, clift, dlift;
REAL ab, bc, cd, da, ac, bd;
REAL abc, bcd, cda, dab;
aex = pa[0] - pe[0];
bex = pb[0] - pe[0];
cex = pc[0] - pe[0];
dex = pd[0] - pe[0];
aey = pa[1] - pe[1];
bey = pb[1] - pe[1];
cey = pc[1] - pe[1];
dey = pd[1] - pe[1];
aez = pa[2] - pe[2];
bez = pb[2] - pe[2];
cez = pc[2] - pe[2];
dez = pd[2] - pe[2];
ab = aex * bey - bex * aey;
bc = bex * cey - cex * bey;
cd = cex * dey - dex * cey;
da = dex * aey - aex * dey;
ac = aex * cey - cex * aey;
bd = bex * dey - dex * bey;
abc = aez * bc - bez * ac + cez * ab;
bcd = bez * cd - cez * bd + dez * bc;
cda = cez * da + dez * ac + aez * cd;
dab = dez * ab + aez * bd + bez * da;
alift = aex * aex + aey * aey + aez * aez;
blift = bex * bex + bey * bey + bez * bez;
clift = cex * cex + cey * cey + cez * cez;
dlift = dex * dex + dey * dey + dez * dez;
return (dlift * abc - clift * dab) + (blift * cda - alift * bcd);
}
REAL insphereexact(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe)
{
INEXACT REAL axby1, bxcy1, cxdy1, dxey1, exay1;
INEXACT REAL bxay1, cxby1, dxcy1, exdy1, axey1;
INEXACT REAL axcy1, bxdy1, cxey1, dxay1, exby1;
INEXACT REAL cxay1, dxby1, excy1, axdy1, bxey1;
REAL axby0, bxcy0, cxdy0, dxey0, exay0;
REAL bxay0, cxby0, dxcy0, exdy0, axey0;
REAL axcy0, bxdy0, cxey0, dxay0, exby0;
REAL cxay0, dxby0, excy0, axdy0, bxey0;
REAL ab[4], bc[4], cd[4], de[4], ea[4];
REAL ac[4], bd[4], ce[4], da[4], eb[4];
REAL temp8a[8], temp8b[8], temp16[16];
int temp8alen, temp8blen, temp16len;
REAL abc[24], bcd[24], cde[24], dea[24], eab[24];
REAL abd[24], bce[24], cda[24], deb[24], eac[24];
int abclen, bcdlen, cdelen, dealen, eablen;
int abdlen, bcelen, cdalen, deblen, eaclen;
REAL temp48a[48], temp48b[48];
int temp48alen, temp48blen;
REAL abcd[96], bcde[96], cdea[96], deab[96], eabc[96];
int abcdlen, bcdelen, cdealen, deablen, eabclen;
REAL temp192[192];
REAL det384x[384], det384y[384], det384z[384];
int xlen, ylen, zlen;
REAL detxy[768];
int xylen;
REAL adet[1152], bdet[1152], cdet[1152], ddet[1152], edet[1152];
int alen, blen, clen, dlen, elen;
REAL abdet[2304], cddet[2304], cdedet[3456];
int ablen, cdlen;
REAL deter[5760];
int deterlen;
int i;
INEXACT REAL bvirt;
REAL avirt, bround, around;
INEXACT REAL c;
INEXACT REAL abig;
REAL ahi, alo, bhi, blo;
REAL err1, err2, err3;
INEXACT REAL _i, _j;
REAL _0;
Two_Product(pa[0], pb[1], axby1, axby0);
Two_Product(pb[0], pa[1], bxay1, bxay0);
Two_Two_Diff(axby1, axby0, bxay1, bxay0, ab[3], ab[2], ab[1], ab[0]);
Two_Product(pb[0], pc[1], bxcy1, bxcy0);
Two_Product(pc[0], pb[1], cxby1, cxby0);
Two_Two_Diff(bxcy1, bxcy0, cxby1, cxby0, bc[3], bc[2], bc[1], bc[0]);
Two_Product(pc[0], pd[1], cxdy1, cxdy0);
Two_Product(pd[0], pc[1], dxcy1, dxcy0);
Two_Two_Diff(cxdy1, cxdy0, dxcy1, dxcy0, cd[3], cd[2], cd[1], cd[0]);
Two_Product(pd[0], pe[1], dxey1, dxey0);
Two_Product(pe[0], pd[1], exdy1, exdy0);
Two_Two_Diff(dxey1, dxey0, exdy1, exdy0, de[3], de[2], de[1], de[0]);
Two_Product(pe[0], pa[1], exay1, exay0);
Two_Product(pa[0], pe[1], axey1, axey0);
Two_Two_Diff(exay1, exay0, axey1, axey0, ea[3], ea[2], ea[1], ea[0]);
Two_Product(pa[0], pc[1], axcy1, axcy0);
Two_Product(pc[0], pa[1], cxay1, cxay0);
Two_Two_Diff(axcy1, axcy0, cxay1, cxay0, ac[3], ac[2], ac[1], ac[0]);
Two_Product(pb[0], pd[1], bxdy1, bxdy0);
Two_Product(pd[0], pb[1], dxby1, dxby0);
Two_Two_Diff(bxdy1, bxdy0, dxby1, dxby0, bd[3], bd[2], bd[1], bd[0]);
Two_Product(pc[0], pe[1], cxey1, cxey0);
Two_Product(pe[0], pc[1], excy1, excy0);
Two_Two_Diff(cxey1, cxey0, excy1, excy0, ce[3], ce[2], ce[1], ce[0]);
Two_Product(pd[0], pa[1], dxay1, dxay0);
Two_Product(pa[0], pd[1], axdy1, axdy0);
Two_Two_Diff(dxay1, dxay0, axdy1, axdy0, da[3], da[2], da[1], da[0]);
Two_Product(pe[0], pb[1], exby1, exby0);
Two_Product(pb[0], pe[1], bxey1, bxey0);
Two_Two_Diff(exby1, exby0, bxey1, bxey0, eb[3], eb[2], eb[1], eb[0]);
temp8alen = scale_expansion_zeroelim(4, bc, pa[2], temp8a);
temp8blen = scale_expansion_zeroelim(4, ac, -pb[2], temp8b);
temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
temp16);
temp8alen = scale_expansion_zeroelim(4, ab, pc[2], temp8a);
abclen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
abc);
temp8alen = scale_expansion_zeroelim(4, cd, pb[2], temp8a);
temp8blen = scale_expansion_zeroelim(4, bd, -pc[2], temp8b);
temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
temp16);
temp8alen = scale_expansion_zeroelim(4, bc, pd[2], temp8a);
bcdlen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
bcd);
temp8alen = scale_expansion_zeroelim(4, de, pc[2], temp8a);
temp8blen = scale_expansion_zeroelim(4, ce, -pd[2], temp8b);
temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
temp16);
temp8alen = scale_expansion_zeroelim(4, cd, pe[2], temp8a);
cdelen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
cde);
temp8alen = scale_expansion_zeroelim(4, ea, pd[2], temp8a);
temp8blen = scale_expansion_zeroelim(4, da, -pe[2], temp8b);
temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
temp16);
temp8alen = scale_expansion_zeroelim(4, de, pa[2], temp8a);
dealen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
dea);
temp8alen = scale_expansion_zeroelim(4, ab, pe[2], temp8a);
temp8blen = scale_expansion_zeroelim(4, eb, -pa[2], temp8b);
temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
temp16);
temp8alen = scale_expansion_zeroelim(4, ea, pb[2], temp8a);
eablen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
eab);
temp8alen = scale_expansion_zeroelim(4, bd, pa[2], temp8a);
temp8blen = scale_expansion_zeroelim(4, da, pb[2], temp8b);
temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
temp16);
temp8alen = scale_expansion_zeroelim(4, ab, pd[2], temp8a);
abdlen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
abd);
temp8alen = scale_expansion_zeroelim(4, ce, pb[2], temp8a);
temp8blen = scale_expansion_zeroelim(4, eb, pc[2], temp8b);
temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
temp16);
temp8alen = scale_expansion_zeroelim(4, bc, pe[2], temp8a);
bcelen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
bce);
temp8alen = scale_expansion_zeroelim(4, da, pc[2], temp8a);
temp8blen = scale_expansion_zeroelim(4, ac, pd[2], temp8b);
temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
temp16);
temp8alen = scale_expansion_zeroelim(4, cd, pa[2], temp8a);
cdalen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
cda);
temp8alen = scale_expansion_zeroelim(4, eb, pd[2], temp8a);
temp8blen = scale_expansion_zeroelim(4, bd, pe[2], temp8b);
temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
temp16);
temp8alen = scale_expansion_zeroelim(4, de, pb[2], temp8a);
deblen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
deb);
temp8alen = scale_expansion_zeroelim(4, ac, pe[2], temp8a);
temp8blen = scale_expansion_zeroelim(4, ce, pa[2], temp8b);
temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
temp16);
temp8alen = scale_expansion_zeroelim(4, ea, pc[2], temp8a);
eaclen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
eac);
temp48alen = fast_expansion_sum_zeroelim(cdelen, cde, bcelen, bce, temp48a);
temp48blen = fast_expansion_sum_zeroelim(deblen, deb, bcdlen, bcd, temp48b);
for (i = 0; i < temp48blen; i++) {
temp48b[i] = -temp48b[i];
}
bcdelen = fast_expansion_sum_zeroelim(temp48alen, temp48a,
temp48blen, temp48b, bcde);
xlen = scale_expansion_zeroelim(bcdelen, bcde, pa[0], temp192);
xlen = scale_expansion_zeroelim(xlen, temp192, pa[0], det384x);
ylen = scale_expansion_zeroelim(bcdelen, bcde, pa[1], temp192);
ylen = scale_expansion_zeroelim(ylen, temp192, pa[1], det384y);
zlen = scale_expansion_zeroelim(bcdelen, bcde, pa[2], temp192);
zlen = scale_expansion_zeroelim(zlen, temp192, pa[2], det384z);
xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy);
alen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, adet);
temp48alen = fast_expansion_sum_zeroelim(dealen, dea, cdalen, cda, temp48a);
temp48blen = fast_expansion_sum_zeroelim(eaclen, eac, cdelen, cde, temp48b);
for (i = 0; i < temp48blen; i++) {
temp48b[i] = -temp48b[i];
}
cdealen = fast_expansion_sum_zeroelim(temp48alen, temp48a,
temp48blen, temp48b, cdea);
xlen = scale_expansion_zeroelim(cdealen, cdea, pb[0], temp192);
xlen = scale_expansion_zeroelim(xlen, temp192, pb[0], det384x);
ylen = scale_expansion_zeroelim(cdealen, cdea, pb[1], temp192);
ylen = scale_expansion_zeroelim(ylen, temp192, pb[1], det384y);
zlen = scale_expansion_zeroelim(cdealen, cdea, pb[2], temp192);
zlen = scale_expansion_zeroelim(zlen, temp192, pb[2], det384z);
xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy);
blen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, bdet);
temp48alen = fast_expansion_sum_zeroelim(eablen, eab, deblen, deb, temp48a);
temp48blen = fast_expansion_sum_zeroelim(abdlen, abd, dealen, dea, temp48b);
for (i = 0; i < temp48blen; i++) {
temp48b[i] = -temp48b[i];
}
deablen = fast_expansion_sum_zeroelim(temp48alen, temp48a,
temp48blen, temp48b, deab);
xlen = scale_expansion_zeroelim(deablen, deab, pc[0], temp192);
xlen = scale_expansion_zeroelim(xlen, temp192, pc[0], det384x);
ylen = scale_expansion_zeroelim(deablen, deab, pc[1], temp192);
ylen = scale_expansion_zeroelim(ylen, temp192, pc[1], det384y);
zlen = scale_expansion_zeroelim(deablen, deab, pc[2], temp192);
zlen = scale_expansion_zeroelim(zlen, temp192, pc[2], det384z);
xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy);
clen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, cdet);
temp48alen = fast_expansion_sum_zeroelim(abclen, abc, eaclen, eac, temp48a);
temp48blen = fast_expansion_sum_zeroelim(bcelen, bce, eablen, eab, temp48b);
for (i = 0; i < temp48blen; i++) {
temp48b[i] = -temp48b[i];
}
eabclen = fast_expansion_sum_zeroelim(temp48alen, temp48a,
temp48blen, temp48b, eabc);
xlen = scale_expansion_zeroelim(eabclen, eabc, pd[0], temp192);
xlen = scale_expansion_zeroelim(xlen, temp192, pd[0], det384x);
ylen = scale_expansion_zeroelim(eabclen, eabc, pd[1], temp192);
ylen = scale_expansion_zeroelim(ylen, temp192, pd[1], det384y);
zlen = scale_expansion_zeroelim(eabclen, eabc, pd[2], temp192);
zlen = scale_expansion_zeroelim(zlen, temp192, pd[2], det384z);
xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy);
dlen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, ddet);
temp48alen = fast_expansion_sum_zeroelim(bcdlen, bcd, abdlen, abd, temp48a);
temp48blen = fast_expansion_sum_zeroelim(cdalen, cda, abclen, abc, temp48b);
for (i = 0; i < temp48blen; i++) {
temp48b[i] = -temp48b[i];
}
abcdlen = fast_expansion_sum_zeroelim(temp48alen, temp48a,
temp48blen, temp48b, abcd);
xlen = scale_expansion_zeroelim(abcdlen, abcd, pe[0], temp192);
xlen = scale_expansion_zeroelim(xlen, temp192, pe[0], det384x);
ylen = scale_expansion_zeroelim(abcdlen, abcd, pe[1], temp192);
ylen = scale_expansion_zeroelim(ylen, temp192, pe[1], det384y);
zlen = scale_expansion_zeroelim(abcdlen, abcd, pe[2], temp192);
zlen = scale_expansion_zeroelim(zlen, temp192, pe[2], det384z);
xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy);
elen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, edet);
ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
cdlen = fast_expansion_sum_zeroelim(clen, cdet, dlen, ddet, cddet);
cdelen = fast_expansion_sum_zeroelim(cdlen, cddet, elen, edet, cdedet);
deterlen = fast_expansion_sum_zeroelim(ablen, abdet, cdelen, cdedet, deter);
return deter[deterlen - 1];
}
REAL insphereslow(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe)
{
INEXACT REAL aex, bex, cex, dex, aey, bey, cey, dey, aez, bez, cez, dez;
REAL aextail, bextail, cextail, dextail;
REAL aeytail, beytail, ceytail, deytail;
REAL aeztail, beztail, ceztail, deztail;
REAL negate, negatetail;
INEXACT REAL axby7, bxcy7, cxdy7, dxay7, axcy7, bxdy7;
INEXACT REAL bxay7, cxby7, dxcy7, axdy7, cxay7, dxby7;
REAL axby[8], bxcy[8], cxdy[8], dxay[8], axcy[8], bxdy[8];
REAL bxay[8], cxby[8], dxcy[8], axdy[8], cxay[8], dxby[8];
REAL ab[16], bc[16], cd[16], da[16], ac[16], bd[16];
int ablen, bclen, cdlen, dalen, aclen, bdlen;
REAL temp32a[32], temp32b[32], temp64a[64], temp64b[64], temp64c[64];
int temp32alen, temp32blen, temp64alen, temp64blen, temp64clen;
REAL temp128[128], temp192[192];
int temp128len, temp192len;
REAL detx[384], detxx[768], detxt[384], detxxt[768], detxtxt[768];
int xlen, xxlen, xtlen, xxtlen, xtxtlen;
REAL x1[1536], x2[2304];
int x1len, x2len;
REAL dety[384], detyy[768], detyt[384], detyyt[768], detytyt[768];
int ylen, yylen, ytlen, yytlen, ytytlen;
REAL y1[1536], y2[2304];
int y1len, y2len;
REAL detz[384], detzz[768], detzt[384], detzzt[768], detztzt[768];
int zlen, zzlen, ztlen, zztlen, ztztlen;
REAL z1[1536], z2[2304];
int z1len, z2len;
REAL detxy[4608];
int xylen;
REAL adet[6912], bdet[6912], cdet[6912], ddet[6912];
int alen, blen, clen, dlen;
REAL abdet[13824], cddet[13824], deter[27648];
int deterlen;
int i;
INEXACT REAL bvirt;
REAL avirt, bround, around;
INEXACT REAL c;
INEXACT REAL abig;
REAL a0hi, a0lo, a1hi, a1lo, bhi, blo;
REAL err1, err2, err3;
INEXACT REAL _i, _j, _k, _l, _m, _n;
REAL _0, _1, _2;
Two_Diff(pa[0], pe[0], aex, aextail);
Two_Diff(pa[1], pe[1], aey, aeytail);
Two_Diff(pa[2], pe[2], aez, aeztail);
Two_Diff(pb[0], pe[0], bex, bextail);
Two_Diff(pb[1], pe[1], bey, beytail);
Two_Diff(pb[2], pe[2], bez, beztail);
Two_Diff(pc[0], pe[0], cex, cextail);
Two_Diff(pc[1], pe[1], cey, ceytail);
Two_Diff(pc[2], pe[2], cez, ceztail);
Two_Diff(pd[0], pe[0], dex, dextail);
Two_Diff(pd[1], pe[1], dey, deytail);
Two_Diff(pd[2], pe[2], dez, deztail);
Two_Two_Product(aex, aextail, bey, beytail,
axby7, axby[6], axby[5], axby[4],
axby[3], axby[2], axby[1], axby[0]);
axby[7] = axby7;
negate = -aey;
negatetail = -aeytail;
Two_Two_Product(bex, bextail, negate, negatetail,
bxay7, bxay[6], bxay[5], bxay[4],
bxay[3], bxay[2], bxay[1], bxay[0]);
bxay[7] = bxay7;
ablen = fast_expansion_sum_zeroelim(8, axby, 8, bxay, ab);
Two_Two_Product(bex, bextail, cey, ceytail,
bxcy7, bxcy[6], bxcy[5], bxcy[4],
bxcy[3], bxcy[2], bxcy[1], bxcy[0]);
bxcy[7] = bxcy7;
negate = -bey;
negatetail = -beytail;
Two_Two_Product(cex, cextail, negate, negatetail,
cxby7, cxby[6], cxby[5], cxby[4],
cxby[3], cxby[2], cxby[1], cxby[0]);
cxby[7] = cxby7;
bclen = fast_expansion_sum_zeroelim(8, bxcy, 8, cxby, bc);
Two_Two_Product(cex, cextail, dey, deytail,
cxdy7, cxdy[6], cxdy[5], cxdy[4],
cxdy[3], cxdy[2], cxdy[1], cxdy[0]);
cxdy[7] = cxdy7;
negate = -cey;
negatetail = -ceytail;
Two_Two_Product(dex, dextail, negate, negatetail,
dxcy7, dxcy[6], dxcy[5], dxcy[4],
dxcy[3], dxcy[2], dxcy[1], dxcy[0]);
dxcy[7] = dxcy7;
cdlen = fast_expansion_sum_zeroelim(8, cxdy, 8, dxcy, cd);
Two_Two_Product(dex, dextail, aey, aeytail,
dxay7, dxay[6], dxay[5], dxay[4],
dxay[3], dxay[2], dxay[1], dxay[0]);
dxay[7] = dxay7;
negate = -dey;
negatetail = -deytail;
Two_Two_Product(aex, aextail, negate, negatetail,
axdy7, axdy[6], axdy[5], axdy[4],
axdy[3], axdy[2], axdy[1], axdy[0]);
axdy[7] = axdy7;
dalen = fast_expansion_sum_zeroelim(8, dxay, 8, axdy, da);
Two_Two_Product(aex, aextail, cey, ceytail,
axcy7, axcy[6], axcy[5], axcy[4],
axcy[3], axcy[2], axcy[1], axcy[0]);
axcy[7] = axcy7;
negate = -aey;
negatetail = -aeytail;
Two_Two_Product(cex, cextail, negate, negatetail,
cxay7, cxay[6], cxay[5], cxay[4],
cxay[3], cxay[2], cxay[1], cxay[0]);
cxay[7] = cxay7;
aclen = fast_expansion_sum_zeroelim(8, axcy, 8, cxay, ac);
Two_Two_Product(bex, bextail, dey, deytail,
bxdy7, bxdy[6], bxdy[5], bxdy[4],
bxdy[3], bxdy[2], bxdy[1], bxdy[0]);
bxdy[7] = bxdy7;
negate = -bey;
negatetail = -beytail;
Two_Two_Product(dex, dextail, negate, negatetail,
dxby7, dxby[6], dxby[5], dxby[4],
dxby[3], dxby[2], dxby[1], dxby[0]);
dxby[7] = dxby7;
bdlen = fast_expansion_sum_zeroelim(8, bxdy, 8, dxby, bd);
temp32alen = scale_expansion_zeroelim(cdlen, cd, -bez, temp32a);
temp32blen = scale_expansion_zeroelim(cdlen, cd, -beztail, temp32b);
temp64alen = fast_expansion_sum_zeroelim(temp32alen, temp32a,
temp32blen, temp32b, temp64a);
temp32alen = scale_expansion_zeroelim(bdlen, bd, cez, temp32a);
temp32blen = scale_expansion_zeroelim(bdlen, bd, ceztail, temp32b);
temp64blen = fast_expansion_sum_zeroelim(temp32alen, temp32a,
temp32blen, temp32b, temp64b);
temp32alen = scale_expansion_zeroelim(bclen, bc, -dez, temp32a);
temp32blen = scale_expansion_zeroelim(bclen, bc, -deztail, temp32b);
temp64clen = fast_expansion_sum_zeroelim(temp32alen, temp32a,
temp32blen, temp32b, temp64c);
temp128len = fast_expansion_sum_zeroelim(temp64alen, temp64a,
temp64blen, temp64b, temp128);
temp192len = fast_expansion_sum_zeroelim(temp64clen, temp64c,
temp128len, temp128, temp192);
xlen = scale_expansion_zeroelim(temp192len, temp192, aex, detx);
xxlen = scale_expansion_zeroelim(xlen, detx, aex, detxx);
xtlen = scale_expansion_zeroelim(temp192len, temp192, aextail, detxt);
xxtlen = scale_expansion_zeroelim(xtlen, detxt, aex, detxxt);
for (i = 0; i < xxtlen; i++) {
detxxt[i] *= 2.0;
}
xtxtlen = scale_expansion_zeroelim(xtlen, detxt, aextail, detxtxt);
x1len = fast_expansion_sum_zeroelim(xxlen, detxx, xxtlen, detxxt, x1);
x2len = fast_expansion_sum_zeroelim(x1len, x1, xtxtlen, detxtxt, x2);
ylen = scale_expansion_zeroelim(temp192len, temp192, aey, dety);
yylen = scale_expansion_zeroelim(ylen, dety, aey, detyy);
ytlen = scale_expansion_zeroelim(temp192len, temp192, aeytail, detyt);
yytlen = scale_expansion_zeroelim(ytlen, detyt, aey, detyyt);
for (i = 0; i < yytlen; i++) {
detyyt[i] *= 2.0;
}
ytytlen = scale_expansion_zeroelim(ytlen, detyt, aeytail, detytyt);
y1len = fast_expansion_sum_zeroelim(yylen, detyy, yytlen, detyyt, y1);
y2len = fast_expansion_sum_zeroelim(y1len, y1, ytytlen, detytyt, y2);
zlen = scale_expansion_zeroelim(temp192len, temp192, aez, detz);
zzlen = scale_expansion_zeroelim(zlen, detz, aez, detzz);
ztlen = scale_expansion_zeroelim(temp192len, temp192, aeztail, detzt);
zztlen = scale_expansion_zeroelim(ztlen, detzt, aez, detzzt);
for (i = 0; i < zztlen; i++) {
detzzt[i] *= 2.0;
}
ztztlen = scale_expansion_zeroelim(ztlen, detzt, aeztail, detztzt);
z1len = fast_expansion_sum_zeroelim(zzlen, detzz, zztlen, detzzt, z1);
z2len = fast_expansion_sum_zeroelim(z1len, z1, ztztlen, detztzt, z2);
xylen = fast_expansion_sum_zeroelim(x2len, x2, y2len, y2, detxy);
alen = fast_expansion_sum_zeroelim(z2len, z2, xylen, detxy, adet);
temp32alen = scale_expansion_zeroelim(dalen, da, cez, temp32a);
temp32blen = scale_expansion_zeroelim(dalen, da, ceztail, temp32b);
temp64alen = fast_expansion_sum_zeroelim(temp32alen, temp32a,
temp32blen, temp32b, temp64a);
temp32alen = scale_expansion_zeroelim(aclen, ac, dez, temp32a);
temp32blen = scale_expansion_zeroelim(aclen, ac, deztail, temp32b);
temp64blen = fast_expansion_sum_zeroelim(temp32alen, temp32a,
temp32blen, temp32b, temp64b);
temp32alen = scale_expansion_zeroelim(cdlen, cd, aez, temp32a);
temp32blen = scale_expansion_zeroelim(cdlen, cd, aeztail, temp32b);
temp64clen = fast_expansion_sum_zeroelim(temp32alen, temp32a,
temp32blen, temp32b, temp64c);
temp128len = fast_expansion_sum_zeroelim(temp64alen, temp64a,
temp64blen, temp64b, temp128);
temp192len = fast_expansion_sum_zeroelim(temp64clen, temp64c,
temp128len, temp128, temp192);
xlen = scale_expansion_zeroelim(temp192len, temp192, bex, detx);
xxlen = scale_expansion_zeroelim(xlen, detx, bex, detxx);
xtlen = scale_expansion_zeroelim(temp192len, temp192, bextail, detxt);
xxtlen = scale_expansion_zeroelim(xtlen, detxt, bex, detxxt);
for (i = 0; i < xxtlen; i++) {
detxxt[i] *= 2.0;
}
xtxtlen = scale_expansion_zeroelim(xtlen, detxt, bextail, detxtxt);
x1len = fast_expansion_sum_zeroelim(xxlen, detxx, xxtlen, detxxt, x1);
x2len = fast_expansion_sum_zeroelim(x1len, x1, xtxtlen, detxtxt, x2);
ylen = scale_expansion_zeroelim(temp192len, temp192, bey, dety);
yylen = scale_expansion_zeroelim(ylen, dety, bey, detyy);
ytlen = scale_expansion_zeroelim(temp192len, temp192, beytail, detyt);
yytlen = scale_expansion_zeroelim(ytlen, detyt, bey, detyyt);
for (i = 0; i < yytlen; i++) {
detyyt[i] *= 2.0;
}
ytytlen = scale_expansion_zeroelim(ytlen, detyt, beytail, detytyt);
y1len = fast_expansion_sum_zeroelim(yylen, detyy, yytlen, detyyt, y1);
y2len = fast_expansion_sum_zeroelim(y1len, y1, ytytlen, detytyt, y2);
zlen = scale_expansion_zeroelim(temp192len, temp192, bez, detz);
zzlen = scale_expansion_zeroelim(zlen, detz, bez, detzz);
ztlen = scale_expansion_zeroelim(temp192len, temp192, beztail, detzt);
zztlen = scale_expansion_zeroelim(ztlen, detzt, bez, detzzt);
for (i = 0; i < zztlen; i++) {
detzzt[i] *= 2.0;
}
ztztlen = scale_expansion_zeroelim(ztlen, detzt, beztail, detztzt);
z1len = fast_expansion_sum_zeroelim(zzlen, detzz, zztlen, detzzt, z1);
z2len = fast_expansion_sum_zeroelim(z1len, z1, ztztlen, detztzt, z2);
xylen = fast_expansion_sum_zeroelim(x2len, x2, y2len, y2, detxy);
blen = fast_expansion_sum_zeroelim(z2len, z2, xylen, detxy, bdet);
temp32alen = scale_expansion_zeroelim(ablen, ab, -dez, temp32a);
temp32blen = scale_expansion_zeroelim(ablen, ab, -deztail, temp32b);
temp64alen = fast_expansion_sum_zeroelim(temp32alen, temp32a,
temp32blen, temp32b, temp64a);
temp32alen = scale_expansion_zeroelim(bdlen, bd, -aez, temp32a);
temp32blen = scale_expansion_zeroelim(bdlen, bd, -aeztail, temp32b);
temp64blen = fast_expansion_sum_zeroelim(temp32alen, temp32a,
temp32blen, temp32b, temp64b);
temp32alen = scale_expansion_zeroelim(dalen, da, -bez, temp32a);
temp32blen = scale_expansion_zeroelim(dalen, da, -beztail, temp32b);
temp64clen = fast_expansion_sum_zeroelim(temp32alen, temp32a,
temp32blen, temp32b, temp64c);
temp128len = fast_expansion_sum_zeroelim(temp64alen, temp64a,
temp64blen, temp64b, temp128);
temp192len = fast_expansion_sum_zeroelim(temp64clen, temp64c,
temp128len, temp128, temp192);
xlen = scale_expansion_zeroelim(temp192len, temp192, cex, detx);
xxlen = scale_expansion_zeroelim(xlen, detx, cex, detxx);
xtlen = scale_expansion_zeroelim(temp192len, temp192, cextail, detxt);
xxtlen = scale_expansion_zeroelim(xtlen, detxt, cex, detxxt);
for (i = 0; i < xxtlen; i++) {
detxxt[i] *= 2.0;
}
xtxtlen = scale_expansion_zeroelim(xtlen, detxt, cextail, detxtxt);
x1len = fast_expansion_sum_zeroelim(xxlen, detxx, xxtlen, detxxt, x1);
x2len = fast_expansion_sum_zeroelim(x1len, x1, xtxtlen, detxtxt, x2);
ylen = scale_expansion_zeroelim(temp192len, temp192, cey, dety);
yylen = scale_expansion_zeroelim(ylen, dety, cey, detyy);
ytlen = scale_expansion_zeroelim(temp192len, temp192, ceytail, detyt);
yytlen = scale_expansion_zeroelim(ytlen, detyt, cey, detyyt);
for (i = 0; i < yytlen; i++) {
detyyt[i] *= 2.0;
}
ytytlen = scale_expansion_zeroelim(ytlen, detyt, ceytail, detytyt);
y1len = fast_expansion_sum_zeroelim(yylen, detyy, yytlen, detyyt, y1);
y2len = fast_expansion_sum_zeroelim(y1len, y1, ytytlen, detytyt, y2);
zlen = scale_expansion_zeroelim(temp192len, temp192, cez, detz);
zzlen = scale_expansion_zeroelim(zlen, detz, cez, detzz);
ztlen = scale_expansion_zeroelim(temp192len, temp192, ceztail, detzt);
zztlen = scale_expansion_zeroelim(ztlen, detzt, cez, detzzt);
for (i = 0; i < zztlen; i++) {
detzzt[i] *= 2.0;
}
ztztlen = scale_expansion_zeroelim(ztlen, detzt, ceztail, detztzt);
z1len = fast_expansion_sum_zeroelim(zzlen, detzz, zztlen, detzzt, z1);
z2len = fast_expansion_sum_zeroelim(z1len, z1, ztztlen, detztzt, z2);
xylen = fast_expansion_sum_zeroelim(x2len, x2, y2len, y2, detxy);
clen = fast_expansion_sum_zeroelim(z2len, z2, xylen, detxy, cdet);
temp32alen = scale_expansion_zeroelim(bclen, bc, aez, temp32a);
temp32blen = scale_expansion_zeroelim(bclen, bc, aeztail, temp32b);
temp64alen = fast_expansion_sum_zeroelim(temp32alen, temp32a,
temp32blen, temp32b, temp64a);
temp32alen = scale_expansion_zeroelim(aclen, ac, -bez, temp32a);
temp32blen = scale_expansion_zeroelim(aclen, ac, -beztail, temp32b);
temp64blen = fast_expansion_sum_zeroelim(temp32alen, temp32a,
temp32blen, temp32b, temp64b);
temp32alen = scale_expansion_zeroelim(ablen, ab, cez, temp32a);
temp32blen = scale_expansion_zeroelim(ablen, ab, ceztail, temp32b);
temp64clen = fast_expansion_sum_zeroelim(temp32alen, temp32a,
temp32blen, temp32b, temp64c);
temp128len = fast_expansion_sum_zeroelim(temp64alen, temp64a,
temp64blen, temp64b, temp128);
temp192len = fast_expansion_sum_zeroelim(temp64clen, temp64c,
temp128len, temp128, temp192);
xlen = scale_expansion_zeroelim(temp192len, temp192, dex, detx);
xxlen = scale_expansion_zeroelim(xlen, detx, dex, detxx);
xtlen = scale_expansion_zeroelim(temp192len, temp192, dextail, detxt);
xxtlen = scale_expansion_zeroelim(xtlen, detxt, dex, detxxt);
for (i = 0; i < xxtlen; i++) {
detxxt[i] *= 2.0;
}
xtxtlen = scale_expansion_zeroelim(xtlen, detxt, dextail, detxtxt);
x1len = fast_expansion_sum_zeroelim(xxlen, detxx, xxtlen, detxxt, x1);
x2len = fast_expansion_sum_zeroelim(x1len, x1, xtxtlen, detxtxt, x2);
ylen = scale_expansion_zeroelim(temp192len, temp192, dey, dety);
yylen = scale_expansion_zeroelim(ylen, dety, dey, detyy);
ytlen = scale_expansion_zeroelim(temp192len, temp192, deytail, detyt);
yytlen = scale_expansion_zeroelim(ytlen, detyt, dey, detyyt);
for (i = 0; i < yytlen; i++) {
detyyt[i] *= 2.0;
}
ytytlen = scale_expansion_zeroelim(ytlen, detyt, deytail, detytyt);
y1len = fast_expansion_sum_zeroelim(yylen, detyy, yytlen, detyyt, y1);
y2len = fast_expansion_sum_zeroelim(y1len, y1, ytytlen, detytyt, y2);
zlen = scale_expansion_zeroelim(temp192len, temp192, dez, detz);
zzlen = scale_expansion_zeroelim(zlen, detz, dez, detzz);
ztlen = scale_expansion_zeroelim(temp192len, temp192, deztail, detzt);
zztlen = scale_expansion_zeroelim(ztlen, detzt, dez, detzzt);
for (i = 0; i < zztlen; i++) {
detzzt[i] *= 2.0;
}
ztztlen = scale_expansion_zeroelim(ztlen, detzt, deztail, detztzt);
z1len = fast_expansion_sum_zeroelim(zzlen, detzz, zztlen, detzzt, z1);
z2len = fast_expansion_sum_zeroelim(z1len, z1, ztztlen, detztzt, z2);
xylen = fast_expansion_sum_zeroelim(x2len, x2, y2len, y2, detxy);
dlen = fast_expansion_sum_zeroelim(z2len, z2, xylen, detxy, ddet);
ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
cdlen = fast_expansion_sum_zeroelim(clen, cdet, dlen, ddet, cddet);
deterlen = fast_expansion_sum_zeroelim(ablen, abdet, cdlen, cddet, deter);
return deter[deterlen - 1];
}
REAL insphereadapt(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe,
REAL permanent)
{
INEXACT REAL aex, bex, cex, dex, aey, bey, cey, dey, aez, bez, cez, dez;
REAL det, errbound;
INEXACT REAL aexbey1, bexaey1, bexcey1, cexbey1;
INEXACT REAL cexdey1, dexcey1, dexaey1, aexdey1;
INEXACT REAL aexcey1, cexaey1, bexdey1, dexbey1;
REAL aexbey0, bexaey0, bexcey0, cexbey0;
REAL cexdey0, dexcey0, dexaey0, aexdey0;
REAL aexcey0, cexaey0, bexdey0, dexbey0;
REAL ab[4], bc[4], cd[4], da[4], ac[4], bd[4];
INEXACT REAL ab3, bc3, cd3, da3, ac3, bd3;
REAL abeps, bceps, cdeps, daeps, aceps, bdeps;
REAL temp8a[8], temp8b[8], temp8c[8], temp16[16], temp24[24], temp48[48];
int temp8alen, temp8blen, temp8clen, temp16len, temp24len, temp48len;
REAL xdet[96], ydet[96], zdet[96], xydet[192];
int xlen, ylen, zlen, xylen;
REAL adet[288], bdet[288], cdet[288], ddet[288];
int alen, blen, clen, dlen;
REAL abdet[576], cddet[576];
int ablen, cdlen;
REAL fin1[1152];
int finlength;
REAL aextail, bextail, cextail, dextail;
REAL aeytail, beytail, ceytail, deytail;
REAL aeztail, beztail, ceztail, deztail;
INEXACT REAL bvirt;
REAL avirt, bround, around;
INEXACT REAL c;
INEXACT REAL abig;
REAL ahi, alo, bhi, blo;
REAL err1, err2, err3;
INEXACT REAL _i, _j;
REAL _0;
aex = (REAL) (pa[0] - pe[0]);
bex = (REAL) (pb[0] - pe[0]);
cex = (REAL) (pc[0] - pe[0]);
dex = (REAL) (pd[0] - pe[0]);
aey = (REAL) (pa[1] - pe[1]);
bey = (REAL) (pb[1] - pe[1]);
cey = (REAL) (pc[1] - pe[1]);
dey = (REAL) (pd[1] - pe[1]);
aez = (REAL) (pa[2] - pe[2]);
bez = (REAL) (pb[2] - pe[2]);
cez = (REAL) (pc[2] - pe[2]);
dez = (REAL) (pd[2] - pe[2]);
Two_Product(aex, bey, aexbey1, aexbey0);
Two_Product(bex, aey, bexaey1, bexaey0);
Two_Two_Diff(aexbey1, aexbey0, bexaey1, bexaey0, ab3, ab[2], ab[1], ab[0]);
ab[3] = ab3;
Two_Product(bex, cey, bexcey1, bexcey0);
Two_Product(cex, bey, cexbey1, cexbey0);
Two_Two_Diff(bexcey1, bexcey0, cexbey1, cexbey0, bc3, bc[2], bc[1], bc[0]);
bc[3] = bc3;
Two_Product(cex, dey, cexdey1, cexdey0);
Two_Product(dex, cey, dexcey1, dexcey0);
Two_Two_Diff(cexdey1, cexdey0, dexcey1, dexcey0, cd3, cd[2], cd[1], cd[0]);
cd[3] = cd3;
Two_Product(dex, aey, dexaey1, dexaey0);
Two_Product(aex, dey, aexdey1, aexdey0);
Two_Two_Diff(dexaey1, dexaey0, aexdey1, aexdey0, da3, da[2], da[1], da[0]);
da[3] = da3;
Two_Product(aex, cey, aexcey1, aexcey0);
Two_Product(cex, aey, cexaey1, cexaey0);
Two_Two_Diff(aexcey1, aexcey0, cexaey1, cexaey0, ac3, ac[2], ac[1], ac[0]);
ac[3] = ac3;
Two_Product(bex, dey, bexdey1, bexdey0);
Two_Product(dex, bey, dexbey1, dexbey0);
Two_Two_Diff(bexdey1, bexdey0, dexbey1, dexbey0, bd3, bd[2], bd[1], bd[0]);
bd[3] = bd3;
temp8alen = scale_expansion_zeroelim(4, cd, bez, temp8a);
temp8blen = scale_expansion_zeroelim(4, bd, -cez, temp8b);
temp8clen = scale_expansion_zeroelim(4, bc, dez, temp8c);
temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a,
temp8blen, temp8b, temp16);
temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c,
temp16len, temp16, temp24);
temp48len = scale_expansion_zeroelim(temp24len, temp24, aex, temp48);
xlen = scale_expansion_zeroelim(temp48len, temp48, -aex, xdet);
temp48len = scale_expansion_zeroelim(temp24len, temp24, aey, temp48);
ylen = scale_expansion_zeroelim(temp48len, temp48, -aey, ydet);
temp48len = scale_expansion_zeroelim(temp24len, temp24, aez, temp48);
zlen = scale_expansion_zeroelim(temp48len, temp48, -aez, zdet);
xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet);
alen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, adet);
temp8alen = scale_expansion_zeroelim(4, da, cez, temp8a);
temp8blen = scale_expansion_zeroelim(4, ac, dez, temp8b);
temp8clen = scale_expansion_zeroelim(4, cd, aez, temp8c);
temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a,
temp8blen, temp8b, temp16);
temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c,
temp16len, temp16, temp24);
temp48len = scale_expansion_zeroelim(temp24len, temp24, bex, temp48);
xlen = scale_expansion_zeroelim(temp48len, temp48, bex, xdet);
temp48len = scale_expansion_zeroelim(temp24len, temp24, bey, temp48);
ylen = scale_expansion_zeroelim(temp48len, temp48, bey, ydet);
temp48len = scale_expansion_zeroelim(temp24len, temp24, bez, temp48);
zlen = scale_expansion_zeroelim(temp48len, temp48, bez, zdet);
xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet);
blen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, bdet);
temp8alen = scale_expansion_zeroelim(4, ab, dez, temp8a);
temp8blen = scale_expansion_zeroelim(4, bd, aez, temp8b);
temp8clen = scale_expansion_zeroelim(4, da, bez, temp8c);
temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a,
temp8blen, temp8b, temp16);
temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c,
temp16len, temp16, temp24);
temp48len = scale_expansion_zeroelim(temp24len, temp24, cex, temp48);
xlen = scale_expansion_zeroelim(temp48len, temp48, -cex, xdet);
temp48len = scale_expansion_zeroelim(temp24len, temp24, cey, temp48);
ylen = scale_expansion_zeroelim(temp48len, temp48, -cey, ydet);
temp48len = scale_expansion_zeroelim(temp24len, temp24, cez, temp48);
zlen = scale_expansion_zeroelim(temp48len, temp48, -cez, zdet);
xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet);
clen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, cdet);
temp8alen = scale_expansion_zeroelim(4, bc, aez, temp8a);
temp8blen = scale_expansion_zeroelim(4, ac, -bez, temp8b);
temp8clen = scale_expansion_zeroelim(4, ab, cez, temp8c);
temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a,
temp8blen, temp8b, temp16);
temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c,
temp16len, temp16, temp24);
temp48len = scale_expansion_zeroelim(temp24len, temp24, dex, temp48);
xlen = scale_expansion_zeroelim(temp48len, temp48, dex, xdet);
temp48len = scale_expansion_zeroelim(temp24len, temp24, dey, temp48);
ylen = scale_expansion_zeroelim(temp48len, temp48, dey, ydet);
temp48len = scale_expansion_zeroelim(temp24len, temp24, dez, temp48);
zlen = scale_expansion_zeroelim(temp48len, temp48, dez, zdet);
xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet);
dlen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, ddet);
ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
cdlen = fast_expansion_sum_zeroelim(clen, cdet, dlen, ddet, cddet);
finlength = fast_expansion_sum_zeroelim(ablen, abdet, cdlen, cddet, fin1);
det = estimate(finlength, fin1);
errbound = isperrboundB * permanent;
if ((det >= errbound) || (-det >= errbound)) {
return det;
}
Two_Diff_Tail(pa[0], pe[0], aex, aextail);
Two_Diff_Tail(pa[1], pe[1], aey, aeytail);
Two_Diff_Tail(pa[2], pe[2], aez, aeztail);
Two_Diff_Tail(pb[0], pe[0], bex, bextail);
Two_Diff_Tail(pb[1], pe[1], bey, beytail);
Two_Diff_Tail(pb[2], pe[2], bez, beztail);
Two_Diff_Tail(pc[0], pe[0], cex, cextail);
Two_Diff_Tail(pc[1], pe[1], cey, ceytail);
Two_Diff_Tail(pc[2], pe[2], cez, ceztail);
Two_Diff_Tail(pd[0], pe[0], dex, dextail);
Two_Diff_Tail(pd[1], pe[1], dey, deytail);
Two_Diff_Tail(pd[2], pe[2], dez, deztail);
if ((aextail == 0.0) && (aeytail == 0.0) && (aeztail == 0.0)
&& (bextail == 0.0) && (beytail == 0.0) && (beztail == 0.0)
&& (cextail == 0.0) && (ceytail == 0.0) && (ceztail == 0.0)
&& (dextail == 0.0) && (deytail == 0.0) && (deztail == 0.0)) {
return det;
}
errbound = isperrboundC * permanent + resulterrbound * Absolute(det);
abeps = (aex * beytail + bey * aextail)
- (aey * bextail + bex * aeytail);
bceps = (bex * ceytail + cey * bextail)
- (bey * cextail + cex * beytail);
cdeps = (cex * deytail + dey * cextail)
- (cey * dextail + dex * ceytail);
daeps = (dex * aeytail + aey * dextail)
- (dey * aextail + aex * deytail);
aceps = (aex * ceytail + cey * aextail)
- (aey * cextail + cex * aeytail);
bdeps = (bex * deytail + dey * bextail)
- (bey * dextail + dex * beytail);
det += (((bex * bex + bey * bey + bez * bez)
* ((cez * daeps + dez * aceps + aez * cdeps)
+ (ceztail * da3 + deztail * ac3 + aeztail * cd3))
+ (dex * dex + dey * dey + dez * dez)
* ((aez * bceps - bez * aceps + cez * abeps)
+ (aeztail * bc3 - beztail * ac3 + ceztail * ab3)))
- ((aex * aex + aey * aey + aez * aez)
* ((bez * cdeps - cez * bdeps + dez * bceps)
+ (beztail * cd3 - ceztail * bd3 + deztail * bc3))
+ (cex * cex + cey * cey + cez * cez)
* ((dez * abeps + aez * bdeps + bez * daeps)
+ (deztail * ab3 + aeztail * bd3 + beztail * da3))))
+ 2.0 * (((bex * bextail + bey * beytail + bez * beztail)
* (cez * da3 + dez * ac3 + aez * cd3)
+ (dex * dextail + dey * deytail + dez * deztail)
* (aez * bc3 - bez * ac3 + cez * ab3))
- ((aex * aextail + aey * aeytail + aez * aeztail)
* (bez * cd3 - cez * bd3 + dez * bc3)
+ (cex * cextail + cey * ceytail + cez * ceztail)
* (dez * ab3 + aez * bd3 + bez * da3)));
if ((det >= errbound) || (-det >= errbound)) {
return det;
}
return insphereexact(pa, pb, pc, pd, pe);
}
#ifdef USE_CGAL_PREDICATES
REAL insphere(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe)
{
return (REAL)
- cgal_pred_obj.side_of_oriented_sphere_3_object()
(Point(pa[0], pa[1], pa[2]),
Point(pb[0], pb[1], pb[2]),
Point(pc[0], pc[1], pc[2]),
Point(pd[0], pd[1], pd[2]),
Point(pe[0], pe[1], pe[2]));
}
#else
REAL insphere(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe)
{
REAL aex, bex, cex, dex;
REAL aey, bey, cey, dey;
REAL aez, bez, cez, dez;
REAL aexbey, bexaey, bexcey, cexbey, cexdey, dexcey, dexaey, aexdey;
REAL aexcey, cexaey, bexdey, dexbey;
REAL alift, blift, clift, dlift;
REAL ab, bc, cd, da, ac, bd;
REAL abc, bcd, cda, dab;
REAL det;
aex = pa[0] - pe[0];
bex = pb[0] - pe[0];
cex = pc[0] - pe[0];
dex = pd[0] - pe[0];
aey = pa[1] - pe[1];
bey = pb[1] - pe[1];
cey = pc[1] - pe[1];
dey = pd[1] - pe[1];
aez = pa[2] - pe[2];
bez = pb[2] - pe[2];
cez = pc[2] - pe[2];
dez = pd[2] - pe[2];
aexbey = aex * bey;
bexaey = bex * aey;
ab = aexbey - bexaey;
bexcey = bex * cey;
cexbey = cex * bey;
bc = bexcey - cexbey;
cexdey = cex * dey;
dexcey = dex * cey;
cd = cexdey - dexcey;
dexaey = dex * aey;
aexdey = aex * dey;
da = dexaey - aexdey;
aexcey = aex * cey;
cexaey = cex * aey;
ac = aexcey - cexaey;
bexdey = bex * dey;
dexbey = dex * bey;
bd = bexdey - dexbey;
abc = aez * bc - bez * ac + cez * ab;
bcd = bez * cd - cez * bd + dez * bc;
cda = cez * da + dez * ac + aez * cd;
dab = dez * ab + aez * bd + bez * da;
alift = aex * aex + aey * aey + aez * aez;
blift = bex * bex + bey * bey + bez * bez;
clift = cex * cex + cey * cey + cez * cez;
dlift = dex * dex + dey * dey + dez * dez;
det = (dlift * abc - clift * dab) + (blift * cda - alift * bcd);
if (_use_inexact_arith) {
return det;
}
if (_use_static_filter) {
if (fabs(det) > ispstaticfilter) return det;
//if (det > ispstaticfilter) return det;
//if (det < minus_ispstaticfilter) return det;
}
REAL aezplus, bezplus, cezplus, dezplus;
REAL aexbeyplus, bexaeyplus, bexceyplus, cexbeyplus;
REAL cexdeyplus, dexceyplus, dexaeyplus, aexdeyplus;
REAL aexceyplus, cexaeyplus, bexdeyplus, dexbeyplus;
REAL permanent, errbound;
aezplus = Absolute(aez);
bezplus = Absolute(bez);
cezplus = Absolute(cez);
dezplus = Absolute(dez);
aexbeyplus = Absolute(aexbey);
bexaeyplus = Absolute(bexaey);
bexceyplus = Absolute(bexcey);
cexbeyplus = Absolute(cexbey);
cexdeyplus = Absolute(cexdey);
dexceyplus = Absolute(dexcey);
dexaeyplus = Absolute(dexaey);
aexdeyplus = Absolute(aexdey);
aexceyplus = Absolute(aexcey);
cexaeyplus = Absolute(cexaey);
bexdeyplus = Absolute(bexdey);
dexbeyplus = Absolute(dexbey);
permanent = ((cexdeyplus + dexceyplus) * bezplus
+ (dexbeyplus + bexdeyplus) * cezplus
+ (bexceyplus + cexbeyplus) * dezplus)
* alift
+ ((dexaeyplus + aexdeyplus) * cezplus
+ (aexceyplus + cexaeyplus) * dezplus
+ (cexdeyplus + dexceyplus) * aezplus)
* blift
+ ((aexbeyplus + bexaeyplus) * dezplus
+ (bexdeyplus + dexbeyplus) * aezplus
+ (dexaeyplus + aexdeyplus) * bezplus)
* clift
+ ((bexceyplus + cexbeyplus) * aezplus
+ (cexaeyplus + aexceyplus) * bezplus
+ (aexbeyplus + bexaeyplus) * cezplus)
* dlift;
errbound = isperrboundA * permanent;
if ((det > errbound) || (-det > errbound)) {
return det;
}
return insphereadapt(pa, pb, pc, pd, pe, permanent);
}
#endif // #ifdef USE_CGAL_PREDICATES
/*****************************************************************************/
/* */
/* orient4d() Return a positive value if the point pe lies above the */
/* hyperplane passing through pa, pb, pc, and pd; "above" is */
/* defined in a manner best found by trial-and-error. Returns */
/* a negative value if pe lies below the hyperplane. Returns */
/* zero if the points are co-hyperplanar (not affinely */
/* independent). The result is also a rough approximation of */
/* 24 times the signed volume of the 4-simplex defined by the */
/* five points. */
/* */
/* Uses exact arithmetic if necessary to ensure a correct answer. The */
/* result returned is the determinant of a matrix. This determinant is */
/* computed adaptively, in the sense that exact arithmetic is used only to */
/* the degree it is needed to ensure that the returned value has the */
/* correct sign. Hence, orient4d() is usually quite fast, but will run */
/* more slowly when the input points are hyper-coplanar or nearly so. */
/* */
/* See my Robust Predicates paper for details. */
/* */
/*****************************************************************************/
REAL orient4dexact(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe,
REAL aheight, REAL bheight, REAL cheight, REAL dheight,
REAL eheight)
{
INEXACT REAL axby1, bxcy1, cxdy1, dxey1, exay1;
INEXACT REAL bxay1, cxby1, dxcy1, exdy1, axey1;
INEXACT REAL axcy1, bxdy1, cxey1, dxay1, exby1;
INEXACT REAL cxay1, dxby1, excy1, axdy1, bxey1;
REAL axby0, bxcy0, cxdy0, dxey0, exay0;
REAL bxay0, cxby0, dxcy0, exdy0, axey0;
REAL axcy0, bxdy0, cxey0, dxay0, exby0;
REAL cxay0, dxby0, excy0, axdy0, bxey0;
REAL ab[4], bc[4], cd[4], de[4], ea[4];
REAL ac[4], bd[4], ce[4], da[4], eb[4];
REAL temp8a[8], temp8b[8], temp16[16];
int temp8alen, temp8blen, temp16len;
REAL abc[24], bcd[24], cde[24], dea[24], eab[24];
REAL abd[24], bce[24], cda[24], deb[24], eac[24];
int abclen, bcdlen, cdelen, dealen, eablen;
int abdlen, bcelen, cdalen, deblen, eaclen;
REAL temp48a[48], temp48b[48];
int temp48alen, temp48blen;
REAL abcd[96], bcde[96], cdea[96], deab[96], eabc[96];
int abcdlen, bcdelen, cdealen, deablen, eabclen;
REAL adet[192], bdet[192], cdet[192], ddet[192], edet[192];
int alen, blen, clen, dlen, elen;
REAL abdet[384], cddet[384], cdedet[576];
int ablen, cdlen;
REAL deter[960];
int deterlen;
int i;
INEXACT REAL bvirt;
REAL avirt, bround, around;
INEXACT REAL c;
INEXACT REAL abig;
REAL ahi, alo, bhi, blo;
REAL err1, err2, err3;
INEXACT REAL _i, _j;
REAL _0;
Two_Product(pa[0], pb[1], axby1, axby0);
Two_Product(pb[0], pa[1], bxay1, bxay0);
Two_Two_Diff(axby1, axby0, bxay1, bxay0, ab[3], ab[2], ab[1], ab[0]);
Two_Product(pb[0], pc[1], bxcy1, bxcy0);
Two_Product(pc[0], pb[1], cxby1, cxby0);
Two_Two_Diff(bxcy1, bxcy0, cxby1, cxby0, bc[3], bc[2], bc[1], bc[0]);
Two_Product(pc[0], pd[1], cxdy1, cxdy0);
Two_Product(pd[0], pc[1], dxcy1, dxcy0);
Two_Two_Diff(cxdy1, cxdy0, dxcy1, dxcy0, cd[3], cd[2], cd[1], cd[0]);
Two_Product(pd[0], pe[1], dxey1, dxey0);
Two_Product(pe[0], pd[1], exdy1, exdy0);
Two_Two_Diff(dxey1, dxey0, exdy1, exdy0, de[3], de[2], de[1], de[0]);
Two_Product(pe[0], pa[1], exay1, exay0);
Two_Product(pa[0], pe[1], axey1, axey0);
Two_Two_Diff(exay1, exay0, axey1, axey0, ea[3], ea[2], ea[1], ea[0]);
Two_Product(pa[0], pc[1], axcy1, axcy0);
Two_Product(pc[0], pa[1], cxay1, cxay0);
Two_Two_Diff(axcy1, axcy0, cxay1, cxay0, ac[3], ac[2], ac[1], ac[0]);
Two_Product(pb[0], pd[1], bxdy1, bxdy0);
Two_Product(pd[0], pb[1], dxby1, dxby0);
Two_Two_Diff(bxdy1, bxdy0, dxby1, dxby0, bd[3], bd[2], bd[1], bd[0]);
Two_Product(pc[0], pe[1], cxey1, cxey0);
Two_Product(pe[0], pc[1], excy1, excy0);
Two_Two_Diff(cxey1, cxey0, excy1, excy0, ce[3], ce[2], ce[1], ce[0]);
Two_Product(pd[0], pa[1], dxay1, dxay0);
Two_Product(pa[0], pd[1], axdy1, axdy0);
Two_Two_Diff(dxay1, dxay0, axdy1, axdy0, da[3], da[2], da[1], da[0]);
Two_Product(pe[0], pb[1], exby1, exby0);
Two_Product(pb[0], pe[1], bxey1, bxey0);
Two_Two_Diff(exby1, exby0, bxey1, bxey0, eb[3], eb[2], eb[1], eb[0]);
temp8alen = scale_expansion_zeroelim(4, bc, pa[2], temp8a);
temp8blen = scale_expansion_zeroelim(4, ac, -pb[2], temp8b);
temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
temp16);
temp8alen = scale_expansion_zeroelim(4, ab, pc[2], temp8a);
abclen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
abc);
temp8alen = scale_expansion_zeroelim(4, cd, pb[2], temp8a);
temp8blen = scale_expansion_zeroelim(4, bd, -pc[2], temp8b);
temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
temp16);
temp8alen = scale_expansion_zeroelim(4, bc, pd[2], temp8a);
bcdlen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
bcd);
temp8alen = scale_expansion_zeroelim(4, de, pc[2], temp8a);
temp8blen = scale_expansion_zeroelim(4, ce, -pd[2], temp8b);
temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
temp16);
temp8alen = scale_expansion_zeroelim(4, cd, pe[2], temp8a);
cdelen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
cde);
temp8alen = scale_expansion_zeroelim(4, ea, pd[2], temp8a);
temp8blen = scale_expansion_zeroelim(4, da, -pe[2], temp8b);
temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
temp16);
temp8alen = scale_expansion_zeroelim(4, de, pa[2], temp8a);
dealen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
dea);
temp8alen = scale_expansion_zeroelim(4, ab, pe[2], temp8a);
temp8blen = scale_expansion_zeroelim(4, eb, -pa[2], temp8b);
temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
temp16);
temp8alen = scale_expansion_zeroelim(4, ea, pb[2], temp8a);
eablen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
eab);
temp8alen = scale_expansion_zeroelim(4, bd, pa[2], temp8a);
temp8blen = scale_expansion_zeroelim(4, da, pb[2], temp8b);
temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
temp16);
temp8alen = scale_expansion_zeroelim(4, ab, pd[2], temp8a);
abdlen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
abd);
temp8alen = scale_expansion_zeroelim(4, ce, pb[2], temp8a);
temp8blen = scale_expansion_zeroelim(4, eb, pc[2], temp8b);
temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
temp16);
temp8alen = scale_expansion_zeroelim(4, bc, pe[2], temp8a);
bcelen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
bce);
temp8alen = scale_expansion_zeroelim(4, da, pc[2], temp8a);
temp8blen = scale_expansion_zeroelim(4, ac, pd[2], temp8b);
temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
temp16);
temp8alen = scale_expansion_zeroelim(4, cd, pa[2], temp8a);
cdalen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
cda);
temp8alen = scale_expansion_zeroelim(4, eb, pd[2], temp8a);
temp8blen = scale_expansion_zeroelim(4, bd, pe[2], temp8b);
temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
temp16);
temp8alen = scale_expansion_zeroelim(4, de, pb[2], temp8a);
deblen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
deb);
temp8alen = scale_expansion_zeroelim(4, ac, pe[2], temp8a);
temp8blen = scale_expansion_zeroelim(4, ce, pa[2], temp8b);
temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
temp16);
temp8alen = scale_expansion_zeroelim(4, ea, pc[2], temp8a);
eaclen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
eac);
temp48alen = fast_expansion_sum_zeroelim(cdelen, cde, bcelen, bce, temp48a);
temp48blen = fast_expansion_sum_zeroelim(deblen, deb, bcdlen, bcd, temp48b);
for (i = 0; i < temp48blen; i++) {
temp48b[i] = -temp48b[i];
}
bcdelen = fast_expansion_sum_zeroelim(temp48alen, temp48a,
temp48blen, temp48b, bcde);
alen = scale_expansion_zeroelim(bcdelen, bcde, aheight, adet);
temp48alen = fast_expansion_sum_zeroelim(dealen, dea, cdalen, cda, temp48a);
temp48blen = fast_expansion_sum_zeroelim(eaclen, eac, cdelen, cde, temp48b);
for (i = 0; i < temp48blen; i++) {
temp48b[i] = -temp48b[i];
}
cdealen = fast_expansion_sum_zeroelim(temp48alen, temp48a,
temp48blen, temp48b, cdea);
blen = scale_expansion_zeroelim(cdealen, cdea, bheight, bdet);
temp48alen = fast_expansion_sum_zeroelim(eablen, eab, deblen, deb, temp48a);
temp48blen = fast_expansion_sum_zeroelim(abdlen, abd, dealen, dea, temp48b);
for (i = 0; i < temp48blen; i++) {
temp48b[i] = -temp48b[i];
}
deablen = fast_expansion_sum_zeroelim(temp48alen, temp48a,
temp48blen, temp48b, deab);
clen = scale_expansion_zeroelim(deablen, deab, cheight, cdet);
temp48alen = fast_expansion_sum_zeroelim(abclen, abc, eaclen, eac, temp48a);
temp48blen = fast_expansion_sum_zeroelim(bcelen, bce, eablen, eab, temp48b);
for (i = 0; i < temp48blen; i++) {
temp48b[i] = -temp48b[i];
}
eabclen = fast_expansion_sum_zeroelim(temp48alen, temp48a,
temp48blen, temp48b, eabc);
dlen = scale_expansion_zeroelim(eabclen, eabc, dheight, ddet);
temp48alen = fast_expansion_sum_zeroelim(bcdlen, bcd, abdlen, abd, temp48a);
temp48blen = fast_expansion_sum_zeroelim(cdalen, cda, abclen, abc, temp48b);
for (i = 0; i < temp48blen; i++) {
temp48b[i] = -temp48b[i];
}
abcdlen = fast_expansion_sum_zeroelim(temp48alen, temp48a,
temp48blen, temp48b, abcd);
elen = scale_expansion_zeroelim(abcdlen, abcd, eheight, edet);
ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
cdlen = fast_expansion_sum_zeroelim(clen, cdet, dlen, ddet, cddet);
cdelen = fast_expansion_sum_zeroelim(cdlen, cddet, elen, edet, cdedet);
deterlen = fast_expansion_sum_zeroelim(ablen, abdet, cdelen, cdedet, deter);
return deter[deterlen - 1];
}
REAL orient4dadapt(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe,
REAL aheight, REAL bheight, REAL cheight, REAL dheight,
REAL eheight, REAL permanent)
{
INEXACT REAL aex, bex, cex, dex, aey, bey, cey, dey, aez, bez, cez, dez;
INEXACT REAL aeheight, beheight, ceheight, deheight;
REAL det, errbound;
INEXACT REAL aexbey1, bexaey1, bexcey1, cexbey1;
INEXACT REAL cexdey1, dexcey1, dexaey1, aexdey1;
INEXACT REAL aexcey1, cexaey1, bexdey1, dexbey1;
REAL aexbey0, bexaey0, bexcey0, cexbey0;
REAL cexdey0, dexcey0, dexaey0, aexdey0;
REAL aexcey0, cexaey0, bexdey0, dexbey0;
REAL ab[4], bc[4], cd[4], da[4], ac[4], bd[4];
INEXACT REAL ab3, bc3, cd3, da3, ac3, bd3;
REAL abeps, bceps, cdeps, daeps, aceps, bdeps;
REAL temp8a[8], temp8b[8], temp8c[8], temp16[16], temp24[24];
int temp8alen, temp8blen, temp8clen, temp16len, temp24len;
REAL adet[48], bdet[48], cdet[48], ddet[48];
int alen, blen, clen, dlen;
REAL abdet[96], cddet[96];
int ablen, cdlen;
REAL fin1[192];
int finlength;
REAL aextail, bextail, cextail, dextail;
REAL aeytail, beytail, ceytail, deytail;
REAL aeztail, beztail, ceztail, deztail;
REAL aeheighttail, beheighttail, ceheighttail, deheighttail;
INEXACT REAL bvirt;
REAL avirt, bround, around;
INEXACT REAL c;
INEXACT REAL abig;
REAL ahi, alo, bhi, blo;
REAL err1, err2, err3;
INEXACT REAL _i, _j;
REAL _0;
aex = (REAL) (pa[0] - pe[0]);
bex = (REAL) (pb[0] - pe[0]);
cex = (REAL) (pc[0] - pe[0]);
dex = (REAL) (pd[0] - pe[0]);
aey = (REAL) (pa[1] - pe[1]);
bey = (REAL) (pb[1] - pe[1]);
cey = (REAL) (pc[1] - pe[1]);
dey = (REAL) (pd[1] - pe[1]);
aez = (REAL) (pa[2] - pe[2]);
bez = (REAL) (pb[2] - pe[2]);
cez = (REAL) (pc[2] - pe[2]);
dez = (REAL) (pd[2] - pe[2]);
aeheight = (REAL) (aheight - eheight);
beheight = (REAL) (bheight - eheight);
ceheight = (REAL) (cheight - eheight);
deheight = (REAL) (dheight - eheight);
Two_Product(aex, bey, aexbey1, aexbey0);
Two_Product(bex, aey, bexaey1, bexaey0);
Two_Two_Diff(aexbey1, aexbey0, bexaey1, bexaey0, ab3, ab[2], ab[1], ab[0]);
ab[3] = ab3;
Two_Product(bex, cey, bexcey1, bexcey0);
Two_Product(cex, bey, cexbey1, cexbey0);
Two_Two_Diff(bexcey1, bexcey0, cexbey1, cexbey0, bc3, bc[2], bc[1], bc[0]);
bc[3] = bc3;
Two_Product(cex, dey, cexdey1, cexdey0);
Two_Product(dex, cey, dexcey1, dexcey0);
Two_Two_Diff(cexdey1, cexdey0, dexcey1, dexcey0, cd3, cd[2], cd[1], cd[0]);
cd[3] = cd3;
Two_Product(dex, aey, dexaey1, dexaey0);
Two_Product(aex, dey, aexdey1, aexdey0);
Two_Two_Diff(dexaey1, dexaey0, aexdey1, aexdey0, da3, da[2], da[1], da[0]);
da[3] = da3;
Two_Product(aex, cey, aexcey1, aexcey0);
Two_Product(cex, aey, cexaey1, cexaey0);
Two_Two_Diff(aexcey1, aexcey0, cexaey1, cexaey0, ac3, ac[2], ac[1], ac[0]);
ac[3] = ac3;
Two_Product(bex, dey, bexdey1, bexdey0);
Two_Product(dex, bey, dexbey1, dexbey0);
Two_Two_Diff(bexdey1, bexdey0, dexbey1, dexbey0, bd3, bd[2], bd[1], bd[0]);
bd[3] = bd3;
temp8alen = scale_expansion_zeroelim(4, cd, bez, temp8a);
temp8blen = scale_expansion_zeroelim(4, bd, -cez, temp8b);
temp8clen = scale_expansion_zeroelim(4, bc, dez, temp8c);
temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a,
temp8blen, temp8b, temp16);
temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c,
temp16len, temp16, temp24);
alen = scale_expansion_zeroelim(temp24len, temp24, -aeheight, adet);
temp8alen = scale_expansion_zeroelim(4, da, cez, temp8a);
temp8blen = scale_expansion_zeroelim(4, ac, dez, temp8b);
temp8clen = scale_expansion_zeroelim(4, cd, aez, temp8c);
temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a,
temp8blen, temp8b, temp16);
temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c,
temp16len, temp16, temp24);
blen = scale_expansion_zeroelim(temp24len, temp24, beheight, bdet);
temp8alen = scale_expansion_zeroelim(4, ab, dez, temp8a);
temp8blen = scale_expansion_zeroelim(4, bd, aez, temp8b);
temp8clen = scale_expansion_zeroelim(4, da, bez, temp8c);
temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a,
temp8blen, temp8b, temp16);
temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c,
temp16len, temp16, temp24);
clen = scale_expansion_zeroelim(temp24len, temp24, -ceheight, cdet);
temp8alen = scale_expansion_zeroelim(4, bc, aez, temp8a);
temp8blen = scale_expansion_zeroelim(4, ac, -bez, temp8b);
temp8clen = scale_expansion_zeroelim(4, ab, cez, temp8c);
temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a,
temp8blen, temp8b, temp16);
temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c,
temp16len, temp16, temp24);
dlen = scale_expansion_zeroelim(temp24len, temp24, deheight, ddet);
ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
cdlen = fast_expansion_sum_zeroelim(clen, cdet, dlen, ddet, cddet);
finlength = fast_expansion_sum_zeroelim(ablen, abdet, cdlen, cddet, fin1);
det = estimate(finlength, fin1);
errbound = isperrboundB * permanent;
if ((det >= errbound) || (-det >= errbound)) {
return det;
}
Two_Diff_Tail(pa[0], pe[0], aex, aextail);
Two_Diff_Tail(pa[1], pe[1], aey, aeytail);
Two_Diff_Tail(pa[2], pe[2], aez, aeztail);
Two_Diff_Tail(aheight, eheight, aeheight, aeheighttail);
Two_Diff_Tail(pb[0], pe[0], bex, bextail);
Two_Diff_Tail(pb[1], pe[1], bey, beytail);
Two_Diff_Tail(pb[2], pe[2], bez, beztail);
Two_Diff_Tail(bheight, eheight, beheight, beheighttail);
Two_Diff_Tail(pc[0], pe[0], cex, cextail);
Two_Diff_Tail(pc[1], pe[1], cey, ceytail);
Two_Diff_Tail(pc[2], pe[2], cez, ceztail);
Two_Diff_Tail(cheight, eheight, ceheight, ceheighttail);
Two_Diff_Tail(pd[0], pe[0], dex, dextail);
Two_Diff_Tail(pd[1], pe[1], dey, deytail);
Two_Diff_Tail(pd[2], pe[2], dez, deztail);
Two_Diff_Tail(dheight, eheight, deheight, deheighttail);
if ((aextail == 0.0) && (aeytail == 0.0) && (aeztail == 0.0)
&& (bextail == 0.0) && (beytail == 0.0) && (beztail == 0.0)
&& (cextail == 0.0) && (ceytail == 0.0) && (ceztail == 0.0)
&& (dextail == 0.0) && (deytail == 0.0) && (deztail == 0.0)
&& (aeheighttail == 0.0) && (beheighttail == 0.0)
&& (ceheighttail == 0.0) && (deheighttail == 0.0)) {
return det;
}
errbound = isperrboundC * permanent + resulterrbound * Absolute(det);
abeps = (aex * beytail + bey * aextail)
- (aey * bextail + bex * aeytail);
bceps = (bex * ceytail + cey * bextail)
- (bey * cextail + cex * beytail);
cdeps = (cex * deytail + dey * cextail)
- (cey * dextail + dex * ceytail);
daeps = (dex * aeytail + aey * dextail)
- (dey * aextail + aex * deytail);
aceps = (aex * ceytail + cey * aextail)
- (aey * cextail + cex * aeytail);
bdeps = (bex * deytail + dey * bextail)
- (bey * dextail + dex * beytail);
det += ((beheight
* ((cez * daeps + dez * aceps + aez * cdeps)
+ (ceztail * da3 + deztail * ac3 + aeztail * cd3))
+ deheight
* ((aez * bceps - bez * aceps + cez * abeps)
+ (aeztail * bc3 - beztail * ac3 + ceztail * ab3)))
- (aeheight
* ((bez * cdeps - cez * bdeps + dez * bceps)
+ (beztail * cd3 - ceztail * bd3 + deztail * bc3))
+ ceheight
* ((dez * abeps + aez * bdeps + bez * daeps)
+ (deztail * ab3 + aeztail * bd3 + beztail * da3))))
+ ((beheighttail * (cez * da3 + dez * ac3 + aez * cd3)
+ deheighttail * (aez * bc3 - bez * ac3 + cez * ab3))
- (aeheighttail * (bez * cd3 - cez * bd3 + dez * bc3)
+ ceheighttail * (dez * ab3 + aez * bd3 + bez * da3)));
if ((det >= errbound) || (-det >= errbound)) {
return det;
}
return orient4dexact(pa, pb, pc, pd, pe,
aheight, bheight, cheight, dheight, eheight);
}
REAL orient4d(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe,
REAL aheight, REAL bheight, REAL cheight, REAL dheight,
REAL eheight)
{
REAL aex, bex, cex, dex;
REAL aey, bey, cey, dey;
REAL aez, bez, cez, dez;
REAL aexbey, bexaey, bexcey, cexbey, cexdey, dexcey, dexaey, aexdey;
REAL aexcey, cexaey, bexdey, dexbey;
REAL aeheight, beheight, ceheight, deheight;
REAL ab, bc, cd, da, ac, bd;
REAL abc, bcd, cda, dab;
REAL aezplus, bezplus, cezplus, dezplus;
REAL aexbeyplus, bexaeyplus, bexceyplus, cexbeyplus;
REAL cexdeyplus, dexceyplus, dexaeyplus, aexdeyplus;
REAL aexceyplus, cexaeyplus, bexdeyplus, dexbeyplus;
REAL det;
REAL permanent, errbound;
aex = pa[0] - pe[0];
bex = pb[0] - pe[0];
cex = pc[0] - pe[0];
dex = pd[0] - pe[0];
aey = pa[1] - pe[1];
bey = pb[1] - pe[1];
cey = pc[1] - pe[1];
dey = pd[1] - pe[1];
aez = pa[2] - pe[2];
bez = pb[2] - pe[2];
cez = pc[2] - pe[2];
dez = pd[2] - pe[2];
aeheight = aheight - eheight;
beheight = bheight - eheight;
ceheight = cheight - eheight;
deheight = dheight - eheight;
aexbey = aex * bey;
bexaey = bex * aey;
ab = aexbey - bexaey;
bexcey = bex * cey;
cexbey = cex * bey;
bc = bexcey - cexbey;
cexdey = cex * dey;
dexcey = dex * cey;
cd = cexdey - dexcey;
dexaey = dex * aey;
aexdey = aex * dey;
da = dexaey - aexdey;
aexcey = aex * cey;
cexaey = cex * aey;
ac = aexcey - cexaey;
bexdey = bex * dey;
dexbey = dex * bey;
bd = bexdey - dexbey;
abc = aez * bc - bez * ac + cez * ab;
bcd = bez * cd - cez * bd + dez * bc;
cda = cez * da + dez * ac + aez * cd;
dab = dez * ab + aez * bd + bez * da;
det = (deheight * abc - ceheight * dab) + (beheight * cda - aeheight * bcd);
aezplus = Absolute(aez);
bezplus = Absolute(bez);
cezplus = Absolute(cez);
dezplus = Absolute(dez);
aexbeyplus = Absolute(aexbey);
bexaeyplus = Absolute(bexaey);
bexceyplus = Absolute(bexcey);
cexbeyplus = Absolute(cexbey);
cexdeyplus = Absolute(cexdey);
dexceyplus = Absolute(dexcey);
dexaeyplus = Absolute(dexaey);
aexdeyplus = Absolute(aexdey);
aexceyplus = Absolute(aexcey);
cexaeyplus = Absolute(cexaey);
bexdeyplus = Absolute(bexdey);
dexbeyplus = Absolute(dexbey);
permanent = ((cexdeyplus + dexceyplus) * bezplus
+ (dexbeyplus + bexdeyplus) * cezplus
+ (bexceyplus + cexbeyplus) * dezplus)
* Absolute(aeheight)
+ ((dexaeyplus + aexdeyplus) * cezplus
+ (aexceyplus + cexaeyplus) * dezplus
+ (cexdeyplus + dexceyplus) * aezplus)
* Absolute(beheight)
+ ((aexbeyplus + bexaeyplus) * dezplus
+ (bexdeyplus + dexbeyplus) * aezplus
+ (dexaeyplus + aexdeyplus) * bezplus)
* Absolute(ceheight)
+ ((bexceyplus + cexbeyplus) * aezplus
+ (cexaeyplus + aexceyplus) * bezplus
+ (aexbeyplus + bexaeyplus) * cezplus)
* Absolute(deheight);
errbound = isperrboundA * permanent;
if ((det > errbound) || (-det > errbound)) {
return det;
}
return orient4dadapt(pa, pb, pc, pd, pe,
aheight, bheight, cheight, dheight, eheight, permanent);
}
}
================================================
FILE: src/cpp/tetgen-LICENSE
================================================
TetGen License
--------------
TetGen is distributed under a dual licensing scheme. You can
redistribute it and/or modify it under the terms of the GNU Affero
General Public License as published by the Free Software Foundation,
either version 3 of the License, or (at your option) any later
version. A copy of the GNU Affero General Public License is reproduced
below.
If the terms and conditions of the AGPL v.3. would prevent you from
using TetGen, please consider the option to obtain a commercial
license for a fee. These licenses are offered by the Weierstrass
Institute for Applied Analysis and Stochastics (WIAS). As a rule,
licenses are provided "as-is", unlimited in time for a one time
fee. Please send corresponding requests to:
tetgen@wias-berlin.de. Please do not forget to include some
description of your company and the realm of its activities.
=====================================================================
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
Copyright © 2007 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.
Preamble
The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains
free software for all its users.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.
A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate. Many developers of free software are heartened and
encouraged by the resulting cooperation. However, in the case of
software used on network servers, this result may fail to come
about. The GNU General Public License permits making a modified
version and letting the public access it on a server without ever
releasing its source code to the public.
The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community. It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server. Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.
An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals. This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing
under this license.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU Affero General Public
License.
"Copyright" also means copyright-like laws that apply to other kinds
of works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of
an exact copy. The resulting work is called a "modified version" of
the earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user
through a computer network, with no transfer of a copy, is not
conveying.
An interactive user interface displays "Appropriate Legal Notices" to
the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work for
making modifications to it. "Object code" means any non-source form of
a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users can
regenerate automatically from other parts of the Corresponding Source.
The Corresponding Source for a work in source code form is that same
work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not convey,
without conditions so long as your license otherwise remains in
force. You may convey covered works to others for the sole purpose of
having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under the
conditions stated below. Sublicensing is not allowed; section 10 makes
it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such
circumvention is effected by exercising rights under this License with
respect to the covered work, and you disclaim any intention to limit
operation or modification of the work as a means of enforcing, against
the work's users, your or third parties' legal rights to forbid
circumvention of technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these
conditions:
a) The work must carry prominent notices stating that you modified it,
and giving a relevant date. b) The work must carry prominent notices
stating that it is released under this License and any conditions
added under section 7. This requirement modifies the requirement in
section 4 to "keep intact all notices". c) You must license the
entire work, as a whole, under this License to anyone who comes into
possession of a copy. This License will therefore apply, along with
any applicable section 7 additional terms, to the whole of the work,
and all its parts, regardless of how they are packaged. This License
gives no permission to license the work in any other way, but it does
not invalidate such permission if you have separately received it. d)
If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your work
need not make them do so. A compilation of a covered work with other
separate and independent works, which are not by their nature
extensions of the covered work, and which are not combined with it
such as to form a larger program, in or on a volume of a storage or
distribution medium, is called an "aggregate" if the compilation and
its resulting copyright are not used to limit the access or legal
rights of the compilation's users beyond what the individual works
permit. Inclusion of a covered work in an aggregate does not cause
this License to apply to the other parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms of
sections 4 and 5, provided that you also convey the machine-readable
Corresponding Source under the terms of this License, in one of these
ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium customarily
used for software interchange. b) Convey the object code in, or
embodied in, a physical product (including a physical distribution
medium), accompanied by a written offer, valid for at least three
years and valid for as long as you offer spare parts or customer
support for that product model, to give anyone who possesses the
object code either (1) a copy of the Corresponding Source for all the
software in the product that is covered by this License, on a durable
physical medium customarily used for software interchange, for a price
no more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the Corresponding Source
from a network server at no charge. c) Convey individual copies of
the object code with a copy of the written offer to provide the
Corresponding Source. This alternative is allowed only occasionally
and noncommercially, and only if you received the object code with
such an offer, in accord with subsection 6b. d) Convey the object
code by offering access from a designated place (gratis or for a
charge), and offer equivalent access to the Corresponding Source in
the same way through the same place at no further charge. You need not
require recipients to copy the Corresponding Source along with the
object code. If the place to copy the object code is a network server,
the Corresponding Source may be on a different server (operated by you
or a third party) that supports equivalent copying facilities,
provided you maintain clear directions next to the object code saying
where to find the Corresponding Source. Regardless of what server
hosts the Corresponding Source, you remain obligated to ensure that it
is available for as long as needed to satisfy these requirements. e)
Convey the object code using peer-to-peer transmission, provided you
inform other peers where the object code and Corresponding Source of
the work are being offered to the general public at no charge under
subsection 6d. A separable portion of the object code, whose source
code is excluded from the Corresponding Source as a System Library,
need not be included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal,
family, or household purposes, or (2) anything designed or sold for
incorporation into a dwelling. In determining whether a product is a
consumer product, doubtful cases shall be resolved in favor of
coverage. For a particular product received by a particular user,
"normally used" refers to a typical or common use of that class of
product, regardless of the status of the particular user or of the way
in which the particular user actually uses, or expects or is expected
to use, the product. A product is a consumer product regardless of
whether the product has substantial commercial, industrial or
non-consumer uses, unless such uses represent the only significant
mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to
install and execute modified versions of a covered work in that User
Product from a modified version of its Corresponding Source. The
information must suffice to ensure that the continued functioning of
the modified object code is in no case prevented or interfered with
solely because modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or
updates for a work that has been modified or installed by the
recipient, or for the User Product in which it has been modified or
installed. Access to a network may be denied when the modification
itself materially and adversely affects the operation of the network
or violates the rules and protocols for communication across the
network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its
conditions. Additional permissions that are applicable to the entire
Program shall be treated as though they were included in this License,
to the extent that they are valid under applicable law. If additional
permissions apply only to part of the Program, that part may be used
separately under those permissions, but the entire Program remains
governed by this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders
of that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or b) Requiring
preservation of specified reasonable legal notices or author
attributions in that material or in the Appropriate Legal Notices
displayed by works containing it; or c) Prohibiting misrepresentation
of the origin of that material, or requiring that modified versions of
such material be marked in reasonable ways as different from the
original version; or d) Limiting the use for publicity purposes of
names of licensors or authors of the material; or e) Declining to
grant rights under trademark law for use of some trade names,
trademarks, or service marks; or f) Requiring indemnification of
licensors and authors of that material by anyone who conveys the
material (or modified versions of it) with contractual assumptions of
liability to the recipient, for any liability that these contractual
assumptions directly impose on those licensors and authors. All other
non-permissive additional terms are considered "further restrictions"
within the meaning of section 10. If the Program as you received it,
or any part of it, contains a notice stating that it is governed by
this License along with a term that is a further restriction, you may
remove that term. If a license document contains a further restriction
but permits relicensing or conveying under this License, you may add
to a covered work material governed by the terms of that license
document, provided that the further restriction does not survive such
relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions; the
above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your license
from a particular copyright holder is reinstated (a) provisionally,
unless and until the copyright holder explicitly and finally
terminates your license, and (b) permanently, if the copyright holder
fails to notify you of the violation by some reasonable means prior to
60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or run
a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims owned
or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within the
scope of its coverage, prohibits the exercise of, or is conditioned on
the non-exercise of one or more of the rights that are specifically
granted under this License. You may not convey a covered work if you
are a party to an arrangement with a third party that is in the
business of distributing software, under which you make payment to the
third party based on the extent of your activity of conveying the
work, and under which the third party grants, to any of the parties
who would receive the covered work from you, a discriminatory patent
license (a) in connection with copies of the covered work conveyed by
you (or copies made from those copies), or (b) primarily for and in
connection with specific products or compilations that contain the
covered work, unless you entered into that arrangement, or that patent
license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under
this License and any other pertinent obligations, then as a
consequence you may not convey it at all. For example, if you agree to
terms that obligate you to collect a royalty for further conveying
from those to whom you convey the Program, the only way you could
satisfy both those terms and this License would be to refrain entirely
from conveying the Program.
13. Remote Network Interaction; Use with the GNU General Public
License.
Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your
version supports such interaction) an opportunity to receive the
Corresponding Source of your version by providing access to the
Corresponding Source from a network server at no charge, through some
standard or customary means of facilitating copying of software. This
Corresponding Source shall include the Corresponding Source for any
work covered by version 3 of the GNU General Public License that is
incorporated pursuant to the following paragraph.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Affero General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever
published by the Free Software Foundation.
If the Program specifies that a proxy can decide which future versions
of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT
WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND
PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE
DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR
CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES
ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT
NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR
LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM
TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER
PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these
terms.
To do so, attach the following notices to the program. It is safest to
attach them to the start of each source file to most effectively state
the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
Copyright (C)
This program is free software: you can redistribute it and/or
modify it under the terms of the GNU Affero General Public License
as published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public
License along with this program. If not, see
. Also add information on how to
contact you by electronic and paper mail.
If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source. For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code. There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for
the specific requirements.
You should also get your employer (if you work as a programmer) or
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. For more information on this, and how to apply and follow
the GNU AGPL, see .
================================================
FILE: src/cpp/tetgen.cpp
================================================
///////////////////////////////////////////////////////////////////////////////
// //
// 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. //
// //
///////////////////////////////////////////////////////////////////////////////
#include "tetgen.h"
// extern void exactdeinit();
using namespace predicates;
//// io_cxx ///////////////////////////////////////////////////////////////////
//// ////
//// ////
tetgenio::polygon::polygon()
{
vertexlist = 0;
numberofvertices = 0;
}
tetgenio::polygon::~polygon()
{
if (vertexlist)
delete [] vertexlist;
}
tetgenio::facet::facet()
{
polygonlist = 0;
numberofpolygons = 0;
holelist = 0;
numberofholes = 0;
}
tetgenio::facet::~facet()
{
if (polygonlist)
delete[] polygonlist;
if (holelist)
delete[] holelist;
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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)
{
char inputline[INPUTLINESIZE];
char *stringptr;
REAL x, y, z, attrib;
int firstnode, currentmarker;
int index, attribindex;
int i, j;
// Initialize 'pointlist', 'pointattributelist', and 'pointmarkerlist'.
pointlist = new REAL[numberofpoints * 3];
if (pointlist == (REAL *) NULL) {
terminatetetgen(NULL, 1);
}
if (numberofpointattributes > 0) {
pointattributelist = new REAL[numberofpoints * numberofpointattributes];
if (pointattributelist == (REAL *) NULL) {
terminatetetgen(NULL, 1);
}
}
if (markers) {
pointmarkerlist = new int[numberofpoints];
if (pointmarkerlist == (int *) NULL) {
terminatetetgen(NULL, 1);
}
}
if (uvflag) {
pointparamlist = new pointparam[numberofpoints];
if (pointparamlist == NULL) {
terminatetetgen(NULL, 1);
}
}
// Read the point section.
index = 0;
attribindex = 0;
for (i = 0; i < numberofpoints; i++) {
stringptr = readnumberline(inputline, infile, infilename);
if (useindex) {
if (i == 0) {
firstnode = (int) strtol (stringptr, &stringptr, 0);
if ((firstnode == 0) || (firstnode == 1)) {
firstnumber = firstnode;
}
}
stringptr = findnextnumber(stringptr);
} // if (useindex)
if (*stringptr == '\0') {
printf("Error: Point %d has no x coordinate.\n", firstnumber + i);
break;
}
x = (REAL) strtod(stringptr, &stringptr);
stringptr = findnextnumber(stringptr);
if (*stringptr == '\0') {
printf("Error: Point %d has no y coordinate.\n", firstnumber + i);
break;
}
y = (REAL) strtod(stringptr, &stringptr);
if (mesh_dim == 3) {
stringptr = findnextnumber(stringptr);
if (*stringptr == '\0') {
printf("Error: Point %d has no z coordinate.\n", firstnumber + i);
break;
}
z = (REAL) strtod(stringptr, &stringptr);
} else {
z = 0.0; // mesh_dim == 2;
}
pointlist[index++] = x;
pointlist[index++] = y;
pointlist[index++] = z;
// Read the point attributes.
for (j = 0; j < numberofpointattributes; j++) {
stringptr = findnextnumber(stringptr);
if (*stringptr == '\0') {
attrib = 0.0;
} else {
attrib = (REAL) strtod(stringptr, &stringptr);
}
pointattributelist[attribindex++] = attrib;
}
if (markers) {
// Read a point marker.
stringptr = findnextnumber(stringptr);
if (*stringptr == '\0') {
currentmarker = 0;
} else {
currentmarker = (int) strtol (stringptr, &stringptr, 0);
}
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.
delete [] pointlist;
pointlist = (REAL *) NULL;
if (markers) {
delete [] pointmarkerlist;
pointmarkerlist = (int *) NULL;
}
if (numberofpointattributes > 0) {
delete [] pointattributelist;
pointattributelist = (REAL *) NULL;
}
if (uvflag) {
delete [] pointparamlist;
pointparamlist = NULL;
}
numberofpoints = 0;
return false;
}
return true;
}
///////////////////////////////////////////////////////////////////////////////
// //
// load_node() Load a list of points from a .node file. //
// //
///////////////////////////////////////////////////////////////////////////////
bool tetgenio::load_node(char* filebasename)
{
FILE *infile;
char innodefilename[FILENAMESIZE];
char inputline[INPUTLINESIZE];
char *stringptr;
bool okflag;
int markers;
int uvflag; // for psc input.
// Assembling the actual file names we want to open.
strcpy(innodefilename, filebasename);
strcat(innodefilename, ".node");
// Try to open a .node file.
infile = fopen(innodefilename, "r");
if (infile == (FILE *) NULL) {
printf(" Cannot access file %s.\n", innodefilename);
return false;
}
printf("Opening %s.\n", innodefilename);
// Set initial flags.
mesh_dim = 3;
numberofpointattributes = 0; // no point attribute.
markers = 0; // no boundary marker.
uvflag = 0; // no uv parameters (required by a PSC).
// Read the first line of the file.
stringptr = readnumberline(inputline, infile, innodefilename);
// Does this file contain an index column?
stringptr = strstr(inputline, "rbox");
if (stringptr == NULL) {
// Read number of points, number of dimensions, number of point
// attributes, and number of boundary markers.
stringptr = inputline;
numberofpoints = (int) strtol (stringptr, &stringptr, 0);
stringptr = findnextnumber(stringptr);
if (*stringptr != '\0') {
mesh_dim = (int) strtol (stringptr, &stringptr, 0);
}
stringptr = findnextnumber(stringptr);
if (*stringptr != '\0') {
numberofpointattributes = (int) strtol (stringptr, &stringptr, 0);
}
stringptr = findnextnumber(stringptr);
if (*stringptr != '\0') {
markers = (int) strtol (stringptr, &stringptr, 0);
}
stringptr = findnextnumber(stringptr);
if (*stringptr != '\0') {
uvflag = (int) strtol (stringptr, &stringptr, 0);
}
} else {
// It is a rbox (qhull) input file.
stringptr = inputline;
// Get the dimension.
mesh_dim = (int) strtol (stringptr, &stringptr, 0);
// Get the number of points.
stringptr = readnumberline(inputline, infile, innodefilename);
numberofpoints = (int) strtol (stringptr, &stringptr, 0);
// There is no index column.
useindex = 0;
}
// Load the list of nodes.
okflag = load_node_call(infile, markers, uvflag, innodefilename);
fclose(infile);
return okflag;
}
///////////////////////////////////////////////////////////////////////////////
// //
// load_edge() Load a list of edges from a .edge file. //
// //
///////////////////////////////////////////////////////////////////////////////
bool tetgenio::load_edge(char* filebasename)
{
FILE *infile;
char inedgefilename[FILENAMESIZE];
char inputline[INPUTLINESIZE];
char *stringptr;
int markers, corner;
int index;
int i, j;
strcpy(inedgefilename, filebasename);
strcat(inedgefilename, ".edge");
infile = fopen(inedgefilename, "r");
if (infile != (FILE *) NULL) {
printf("Opening %s.\n", inedgefilename);
} else {
//printf(" Cannot access file %s.\n", inedgefilename);
return false;
}
// Read number of boundary edges.
stringptr = readnumberline(inputline, infile, inedgefilename);
numberofedges = (int) strtol (stringptr, &stringptr, 0);
if (numberofedges > 0) {
edgelist = new int[numberofedges * 2];
if (edgelist == (int *) NULL) {
terminatetetgen(NULL, 1);
}
stringptr = findnextnumber(stringptr);
if (*stringptr == '\0') {
markers = 0; // Default value.
} else {
markers = (int) strtol (stringptr, &stringptr, 0);
}
if (markers > 0) {
edgemarkerlist = new int[numberofedges];
}
}
// Read the list of edges.
index = 0;
for (i = 0; i < numberofedges; i++) {
// Read edge index and the edge's two endpoints.
stringptr = readnumberline(inputline, infile, inedgefilename);
for (j = 0; j < 2; j++) {
stringptr = findnextnumber(stringptr);
if (*stringptr == '\0') {
printf("Error: Edge %d is missing vertex %d in %s.\n",
i + firstnumber, j + 1, inedgefilename);
terminatetetgen(NULL, 1);
}
corner = (int) strtol(stringptr, &stringptr, 0);
if (corner < firstnumber || corner >= numberofpoints + firstnumber) {
printf("Error: Edge %d has an invalid vertex index.\n",
i + firstnumber);
terminatetetgen(NULL, 1);
}
edgelist[index++] = corner;
}
if (numberofcorners == 10) {
// Skip an extra vertex (generated by a previous -o2 option).
stringptr = findnextnumber(stringptr);
}
// Read the edge marker if it has.
if (markers) {
stringptr = findnextnumber(stringptr);
edgemarkerlist[i] = (int) strtol(stringptr, &stringptr, 0);
}
}
fclose(infile);
return true;
}
///////////////////////////////////////////////////////////////////////////////
// //
// load_face() Load a list of faces (triangles) from a .face file. //
// //
///////////////////////////////////////////////////////////////////////////////
bool tetgenio::load_face(char* filebasename)
{
FILE *infile;
char infilename[FILENAMESIZE];
char inputline[INPUTLINESIZE];
char *stringptr;
REAL attrib;
int markers, corner;
int index;
int i, j;
strcpy(infilename, filebasename);
strcat(infilename, ".face");
infile = fopen(infilename, "r");
if (infile != (FILE *) NULL) {
printf("Opening %s.\n", infilename);
} else {
return false;
}
// Read number of faces, boundary markers.
stringptr = readnumberline(inputline, infile, infilename);
numberoftrifaces = (int) strtol (stringptr, &stringptr, 0);
stringptr = findnextnumber(stringptr);
if (mesh_dim == 2) {
// Skip a number.
stringptr = findnextnumber(stringptr);
}
if (*stringptr == '\0') {
markers = 0; // Default there is no marker per face.
} else {
markers = (int) strtol (stringptr, &stringptr, 0);
}
if (numberoftrifaces > 0) {
trifacelist = new int[numberoftrifaces * 3];
if (trifacelist == (int *) NULL) {
terminatetetgen(NULL, 1);
}
if (markers) {
trifacemarkerlist = new int[numberoftrifaces];
if (trifacemarkerlist == (int *) NULL) {
terminatetetgen(NULL, 1);
}
}
}
// Read the list of faces.
index = 0;
for (i = 0; i < numberoftrifaces; i++) {
// Read face index and the face's three corners.
stringptr = readnumberline(inputline, infile, infilename);
for (j = 0; j < 3; j++) {
stringptr = findnextnumber(stringptr);
if (*stringptr == '\0') {
printf("Error: Face %d is missing vertex %d in %s.\n",
i + firstnumber, j + 1, infilename);
terminatetetgen(NULL, 1);
}
corner = (int) strtol(stringptr, &stringptr, 0);
if (corner < firstnumber || corner >= numberofpoints + firstnumber) {
printf("Error: Face %d has an invalid vertex index.\n",
i + firstnumber);
terminatetetgen(NULL, 1);
}
trifacelist[index++] = corner;
}
if (numberofcorners == 10) {
// Skip 3 extra vertices (generated by a previous -o2 option).
for (j = 0; j < 3; j++) {
stringptr = findnextnumber(stringptr);
}
}
// Read the boundary marker if it exists.
if (markers) {
stringptr = findnextnumber(stringptr);
if (*stringptr == '\0') {
attrib = 0.0;
} else {
attrib = (REAL) strtod(stringptr, &stringptr);
}
trifacemarkerlist[i] = (int) attrib;
}
}
fclose(infile);
return true;
}
///////////////////////////////////////////////////////////////////////////////
// //
// load_tet() Load a list of tetrahedra from a .ele file. //
// //
///////////////////////////////////////////////////////////////////////////////
bool tetgenio::load_tet(char* filebasename)
{
FILE *infile;
char infilename[FILENAMESIZE];
char inputline[INPUTLINESIZE];
char *stringptr;
REAL attrib;
int corner;
int index, attribindex;
int i, j;
strcpy(infilename, filebasename);
strcat(infilename, ".ele");
infile = fopen(infilename, "r");
if (infile != (FILE *) NULL) {
printf("Opening %s.\n", infilename);
} else {
return false;
}
// Read number of elements, number of corners (4 or 10), number of
// element attributes.
stringptr = readnumberline(inputline, infile, infilename);
numberoftetrahedra = (int) strtol (stringptr, &stringptr, 0);
if (numberoftetrahedra <= 0) {
printf("Error: Invalid number of tetrahedra.\n");
fclose(infile);
return false;
}
stringptr = findnextnumber(stringptr);
if (*stringptr == '\0') {
numberofcorners = 4; // Default read 4 nodes per element.
} else {
numberofcorners = (int) strtol(stringptr, &stringptr, 0);
}
stringptr = findnextnumber(stringptr);
if (*stringptr == '\0') {
numberoftetrahedronattributes = 0; // Default no attribute.
} else {
numberoftetrahedronattributes = (int) strtol(stringptr, &stringptr, 0);
}
if (numberofcorners != 4 && numberofcorners != 10) {
printf("Error: Wrong number of corners %d (should be 4 or 10).\n",
numberofcorners);
fclose(infile);
return false;
}
// Allocate memory for tetrahedra.
tetrahedronlist = new int[numberoftetrahedra * numberofcorners];
if (tetrahedronlist == (int *) NULL) {
terminatetetgen(NULL, 1);
}
// Allocate memory for output tetrahedron attributes if necessary.
if (numberoftetrahedronattributes > 0) {
tetrahedronattributelist = new REAL[numberoftetrahedra *
numberoftetrahedronattributes];
if (tetrahedronattributelist == (REAL *) NULL) {
terminatetetgen(NULL, 1);
}
}
// Read the list of tetrahedra.
index = 0;
attribindex = 0;
for (i = 0; i < numberoftetrahedra; i++) {
// Read tetrahedron index and the tetrahedron's corners.
stringptr = readnumberline(inputline, infile, infilename);
for (j = 0; j < numberofcorners; j++) {
stringptr = findnextnumber(stringptr);
if (*stringptr == '\0') {
printf("Error: Tetrahedron %d is missing vertex %d in %s.\n",
i + firstnumber, j + 1, infilename);
terminatetetgen(NULL, 1);
}
corner = (int) strtol(stringptr, &stringptr, 0);
if (corner < firstnumber || corner >= numberofpoints + firstnumber) {
printf("Error: Tetrahedron %d has an invalid vertex index.\n",
i + firstnumber);
terminatetetgen(NULL, 1);
}
tetrahedronlist[index++] = corner;
}
// Read the tetrahedron's attributes.
for (j = 0; j < numberoftetrahedronattributes; j++) {
stringptr = findnextnumber(stringptr);
if (*stringptr == '\0') {
attrib = 0.0;
} else {
attrib = (REAL) strtod(stringptr, &stringptr);
}
tetrahedronattributelist[attribindex++] = attrib;
}
}
fclose(infile);
return true;
}
///////////////////////////////////////////////////////////////////////////////
// //
// load_vol() Load a list of volume constraints from a .vol file. //
// //
///////////////////////////////////////////////////////////////////////////////
bool tetgenio::load_vol(char* filebasename)
{
FILE *infile;
char inelefilename[FILENAMESIZE];
char infilename[FILENAMESIZE];
char inputline[INPUTLINESIZE];
char *stringptr;
REAL volume;
int volelements;
int i;
strcpy(infilename, filebasename);
strcat(infilename, ".vol");
infile = fopen(infilename, "r");
if (infile != (FILE *) NULL) {
printf("Opening %s.\n", infilename);
} else {
return false;
}
// Read number of tetrahedra.
stringptr = readnumberline(inputline, infile, infilename);
volelements = (int) strtol (stringptr, &stringptr, 0);
if (volelements != numberoftetrahedra) {
strcpy(inelefilename, filebasename);
strcat(infilename, ".ele");
printf("Warning: %s and %s disagree on number of tetrahedra.\n",
inelefilename, infilename);
fclose(infile);
return false;
}
tetrahedronvolumelist = new REAL[volelements];
if (tetrahedronvolumelist == (REAL *) NULL) {
terminatetetgen(NULL, 1);
}
// Read the list of volume constraints.
for (i = 0; i < volelements; i++) {
stringptr = readnumberline(inputline, infile, infilename);
stringptr = findnextnumber(stringptr);
if (*stringptr == '\0') {
volume = -1.0; // No constraint on this tetrahedron.
} else {
volume = (REAL) strtod(stringptr, &stringptr);
}
tetrahedronvolumelist[i] = volume;
}
fclose(infile);
return true;
}
///////////////////////////////////////////////////////////////////////////////
// //
// load_var() Load constraints applied on facets, segments, and nodes //
// from a .var file. //
// //
///////////////////////////////////////////////////////////////////////////////
bool tetgenio::load_var(char* filebasename)
{
FILE *infile;
char varfilename[FILENAMESIZE];
char inputline[INPUTLINESIZE];
char *stringptr;
int index;
int i;
// Variant constraints are saved in file "filename.var".
strcpy(varfilename, filebasename);
strcat(varfilename, ".var");
infile = fopen(varfilename, "r");
if (infile != (FILE *) NULL) {
printf("Opening %s.\n", varfilename);
} else {
return false;
}
// Read the facet constraint section.
stringptr = readnumberline(inputline, infile, varfilename);
if (*stringptr != '\0') {
numberoffacetconstraints = (int) strtol (stringptr, &stringptr, 0);
} else {
numberoffacetconstraints = 0;
}
if (numberoffacetconstraints > 0) {
// Initialize 'facetconstraintlist'.
facetconstraintlist = new REAL[numberoffacetconstraints * 2];
index = 0;
for (i = 0; i < numberoffacetconstraints; i++) {
stringptr = readnumberline(inputline, infile, varfilename);
stringptr = findnextnumber(stringptr);
if (*stringptr == '\0') {
printf("Error: facet constraint %d has no facet marker.\n",
firstnumber + i);
break;
} else {
facetconstraintlist[index++] = (REAL) strtod(stringptr, &stringptr);
}
stringptr = findnextnumber(stringptr);
if (*stringptr == '\0') {
printf("Error: facet constraint %d has no maximum area bound.\n",
firstnumber + i);
break;
} else {
facetconstraintlist[index++] = (REAL) strtod(stringptr, &stringptr);
}
}
if (i < numberoffacetconstraints) {
// This must be caused by an error.
fclose(infile);
return false;
}
}
// Read the segment constraint section.
stringptr = readnumberline(inputline, infile, varfilename);
if (*stringptr != '\0') {
numberofsegmentconstraints = (int) strtol (stringptr, &stringptr, 0);
} else {
numberofsegmentconstraints = 0;
}
if (numberofsegmentconstraints > 0) {
// Initialize 'segmentconstraintlist'.
segmentconstraintlist = new REAL[numberofsegmentconstraints * 3];
index = 0;
for (i = 0; i < numberofsegmentconstraints; i++) {
stringptr = readnumberline(inputline, infile, varfilename);
stringptr = findnextnumber(stringptr);
if (*stringptr == '\0') {
printf("Error: segment constraint %d has no frist endpoint.\n",
firstnumber + i);
break;
} else {
segmentconstraintlist[index++] = (REAL) strtod(stringptr, &stringptr);
}
stringptr = findnextnumber(stringptr);
if (*stringptr == '\0') {
printf("Error: segment constraint %d has no second endpoint.\n",
firstnumber + i);
break;
} else {
segmentconstraintlist[index++] = (REAL) strtod(stringptr, &stringptr);
}
stringptr = findnextnumber(stringptr);
if (*stringptr == '\0') {
printf("Error: segment constraint %d has no maximum length bound.\n",
firstnumber + i);
break;
} else {
segmentconstraintlist[index++] = (REAL) strtod(stringptr, &stringptr);
}
}
if (i < numberofsegmentconstraints) {
// This must be caused by an error.
fclose(infile);
return false;
}
}
fclose(infile);
return true;
}
///////////////////////////////////////////////////////////////////////////////
// //
// load_mtr() Load a size specification map from a .mtr file. //
// //
///////////////////////////////////////////////////////////////////////////////
bool tetgenio::load_mtr(char* filebasename)
{
FILE *infile;
char mtrfilename[FILENAMESIZE];
char inputline[INPUTLINESIZE];
char *stringptr;
REAL mtr;
int ptnum;
int mtrindex;
int i, j;
strcpy(mtrfilename, filebasename);
strcat(mtrfilename, ".mtr");
infile = fopen(mtrfilename, "r");
if (infile != (FILE *) NULL) {
printf("Opening %s.\n", mtrfilename);
} else {
return false;
}
// Read the number of points.
stringptr = readnumberline(inputline, infile, mtrfilename);
ptnum = (int) strtol (stringptr, &stringptr, 0);
if (ptnum != numberofpoints) {
printf(" !! Point numbers are not equal. Ignored.\n");
fclose(infile);
return false;
}
// Read the number of columns (1, 3, or 6).
stringptr = findnextnumber(stringptr); // Skip number of points.
if (*stringptr != '\0') {
numberofpointmtrs = (int) strtol (stringptr, &stringptr, 0);
}
if (numberofpointmtrs == 0) {
// Column number doesn't match. Set a default number (1).
numberofpointmtrs = 1;
}
// Allocate space for pointmtrlist.
pointmtrlist = new REAL[numberofpoints * numberofpointmtrs];
if (pointmtrlist == (REAL *) NULL) {
terminatetetgen(NULL, 1);
}
mtrindex = 0;
for (i = 0; i < numberofpoints; i++) {
// Read metrics.
stringptr = readnumberline(inputline, infile, mtrfilename);
for (j = 0; j < numberofpointmtrs; j++) {
if (*stringptr == '\0') {
printf("Error: Metric %d is missing value #%d in %s.\n",
i + firstnumber, j + 1, mtrfilename);
terminatetetgen(NULL, 1);
}
mtr = (REAL) strtod(stringptr, &stringptr);
pointmtrlist[mtrindex++] = mtr;
stringptr = findnextnumber(stringptr);
}
}
fclose(infile);
return true;
}
///////////////////////////////////////////////////////////////////////////////
// //
// load_poly() Load a PL complex from a .poly or a .smesh file. //
// //
///////////////////////////////////////////////////////////////////////////////
bool tetgenio::load_poly(char* filebasename)
{
FILE *infile;
char inpolyfilename[FILENAMESIZE];
char insmeshfilename[FILENAMESIZE];
char inputline[INPUTLINESIZE];
char *stringptr, *infilename;
int smesh, markers, uvflag, currentmarker;
int index;
int i, j, k;
// Assembling the actual file names we want to open.
strcpy(inpolyfilename, filebasename);
strcpy(insmeshfilename, filebasename);
strcat(inpolyfilename, ".poly");
strcat(insmeshfilename, ".smesh");
// First assume it is a .poly file.
smesh = 0;
// Try to open a .poly file.
infile = fopen(inpolyfilename, "r");
if (infile == (FILE *) NULL) {
// .poly doesn't exist! Try to open a .smesh file.
infile = fopen(insmeshfilename, "r");
if (infile == (FILE *) NULL) {
printf(" Cannot access file %s and %s.\n",
inpolyfilename, insmeshfilename);
return false;
} else {
printf("Opening %s.\n", insmeshfilename);
infilename = insmeshfilename;
}
smesh = 1;
} else {
printf("Opening %s.\n", inpolyfilename);
infilename = inpolyfilename;
}
// Initialize the default values.
mesh_dim = 3; // Three-dimensional coordinates.
numberofpointattributes = 0; // no point attribute.
markers = 0; // no boundary marker.
uvflag = 0; // no uv parameters (required by a PSC).
// Read number of points, number of dimensions, number of point
// attributes, and number of boundary markers.
stringptr = readnumberline(inputline, infile, infilename);
numberofpoints = (int) strtol (stringptr, &stringptr, 0);
stringptr = findnextnumber(stringptr);
if (*stringptr != '\0') {
mesh_dim = (int) strtol (stringptr, &stringptr, 0);
}
stringptr = findnextnumber(stringptr);
if (*stringptr != '\0') {
numberofpointattributes = (int) strtol (stringptr, &stringptr, 0);
}
stringptr = findnextnumber(stringptr);
if (*stringptr != '\0') {
markers = (int) strtol (stringptr, &stringptr, 0);
}
if (*stringptr != '\0') {
uvflag = (int) strtol (stringptr, &stringptr, 0);
}
if (numberofpoints > 0) {
// Load the list of nodes.
if (!load_node_call(infile, markers, uvflag, infilename)) {
fclose(infile);
return false;
}
} else {
// If the .poly or .smesh file claims there are zero points, that
// means the points should be read from a separate .node file.
if (!load_node(filebasename)) {
fclose(infile);
return false;
}
}
if ((mesh_dim != 3) && (mesh_dim != 2)) {
printf("Input error: TetGen only works for 2D & 3D point sets.\n");
fclose(infile);
return false;
}
if (numberofpoints < (mesh_dim + 1)) {
printf("Input error: TetGen needs at least %d points.\n", mesh_dim + 1);
fclose(infile);
return false;
}
facet *f;
polygon *p;
if (mesh_dim == 3) {
// Read number of facets and number of boundary markers.
stringptr = readnumberline(inputline, infile, infilename);
if (stringptr == NULL) {
// No facet list, return.
fclose(infile);
return true;
}
numberoffacets = (int) strtol (stringptr, &stringptr, 0);
if (numberoffacets <= 0) {
// No facet list, return.
fclose(infile);
return true;
}
stringptr = findnextnumber(stringptr);
if (*stringptr == '\0') {
markers = 0; // no boundary marker.
} else {
markers = (int) strtol (stringptr, &stringptr, 0);
}
// Initialize the 'facetlist', 'facetmarkerlist'.
facetlist = new facet[numberoffacets];
if (markers == 1) {
facetmarkerlist = new int[numberoffacets];
}
// Read data into 'facetlist', 'facetmarkerlist'.
if (smesh == 0) {
// Facets are in .poly file format.
for (i = 1; i <= numberoffacets; i++) {
f = &(facetlist[i - 1]);
init(f);
f->numberofholes = 0;
currentmarker = 0;
// Read number of polygons, number of holes, and a boundary marker.
stringptr = readnumberline(inputline, infile, infilename);
f->numberofpolygons = (int) strtol (stringptr, &stringptr, 0);
stringptr = findnextnumber(stringptr);
if (*stringptr != '\0') {
f->numberofholes = (int) strtol (stringptr, &stringptr, 0);
if (markers == 1) {
stringptr = findnextnumber(stringptr);
if (*stringptr != '\0') {
currentmarker = (int) strtol(stringptr, &stringptr, 0);
}
}
}
// Initialize facetmarker if it needs.
if (markers == 1) {
facetmarkerlist[i - 1] = currentmarker;
}
// Each facet should has at least one polygon.
if (f->numberofpolygons <= 0) {
printf("Error: Wrong number of polygon in %d facet.\n", i);
break;
}
// Initialize the 'f->polygonlist'.
f->polygonlist = new polygon[f->numberofpolygons];
// Go through all polygons, read in their vertices.
for (j = 1; j <= f->numberofpolygons; j++) {
p = &(f->polygonlist[j - 1]);
init(p);
// Read number of vertices of this polygon.
stringptr = readnumberline(inputline, infile, infilename);
p->numberofvertices = (int) strtol(stringptr, &stringptr, 0);
if (p->numberofvertices < 1) {
printf("Error: Wrong polygon %d in facet %d\n", j, i);
break;
}
// Initialize 'p->vertexlist'.
p->vertexlist = new int[p->numberofvertices];
// Read all vertices of this polygon.
for (k = 1; k <= p->numberofvertices; k++) {
stringptr = findnextnumber(stringptr);
if (*stringptr == '\0') {
// Try to load another non-empty line and continue to read the
// rest of vertices.
stringptr = readnumberline(inputline, infile, infilename);
if (*stringptr == '\0') {
printf("Error: Missing %d endpoints of polygon %d in facet %d",
p->numberofvertices - k, j, i);
break;
}
}
p->vertexlist[k - 1] = (int) strtol (stringptr, &stringptr, 0);
}
}
if (j <= f->numberofpolygons) {
// This must be caused by an error. However, there're j - 1
// polygons have been read. Reset the 'f->numberofpolygon'.
if (j == 1) {
// This is the first polygon.
delete [] f->polygonlist;
}
f->numberofpolygons = j - 1;
// No hole will be read even it exists.
f->numberofholes = 0;
break;
}
// If this facet has hole pints defined, read them.
if (f->numberofholes > 0) {
// Initialize 'f->holelist'.
f->holelist = new REAL[f->numberofholes * 3];
// Read the holes' coordinates.
index = 0;
for (j = 1; j <= f->numberofholes; j++) {
stringptr = readnumberline(inputline, infile, infilename);
for (k = 1; k <= 3; k++) {
stringptr = findnextnumber(stringptr);
if (*stringptr == '\0') {
printf("Error: Hole %d in facet %d has no coordinates", j, i);
break;
}
f->holelist[index++] = (REAL) strtod (stringptr, &stringptr);
}
if (k <= 3) {
// This must be caused by an error.
break;
}
}
if (j <= f->numberofholes) {
// This must be caused by an error.
break;
}
}
}
if (i <= numberoffacets) {
// This must be caused by an error.
numberoffacets = i - 1;
fclose(infile);
return false;
}
} else { // poly == 0
// Read the facets from a .smesh file.
for (i = 1; i <= numberoffacets; i++) {
f = &(facetlist[i - 1]);
init(f);
// Initialize 'f->facetlist'. In a .smesh file, each facetlist only
// contains exactly one polygon, no hole.
f->numberofpolygons = 1;
f->polygonlist = new polygon[f->numberofpolygons];
p = &(f->polygonlist[0]);
init(p);
// Read number of vertices of this polygon.
stringptr = readnumberline(inputline, infile, insmeshfilename);
p->numberofvertices = (int) strtol (stringptr, &stringptr, 0);
if (p->numberofvertices < 1) {
printf("Error: Wrong number of vertex in facet %d\n", i);
break;
}
// Initialize 'p->vertexlist'.
p->vertexlist = new int[p->numberofvertices];
for (k = 1; k <= p->numberofvertices; k++) {
stringptr = findnextnumber(stringptr);
if (*stringptr == '\0') {
// Try to load another non-empty line and continue to read the
// rest of vertices.
stringptr = readnumberline(inputline, infile, infilename);
if (*stringptr == '\0') {
printf("Error: Missing %d endpoints in facet %d",
p->numberofvertices - k, i);
break;
}
}
p->vertexlist[k - 1] = (int) strtol (stringptr, &stringptr, 0);
}
if (k <= p->numberofvertices) {
// This must be caused by an error.
break;
}
// Read facet's boundary marker at last.
if (markers == 1) {
stringptr = findnextnumber(stringptr);
if (*stringptr == '\0') {
currentmarker = 0;
} else {
currentmarker = (int) strtol(stringptr, &stringptr, 0);
}
facetmarkerlist[i - 1] = currentmarker;
}
}
if (i <= numberoffacets) {
// This must be caused by an error.
numberoffacets = i - 1;
fclose(infile);
return false;
}
}
// Read the hole section.
stringptr = readnumberline(inputline, infile, infilename);
if (stringptr == NULL) {
// No hole list, return.
fclose(infile);
return true;
}
if (*stringptr != '\0') {
numberofholes = (int) strtol (stringptr, &stringptr, 0);
} else {
numberofholes = 0;
}
if (numberofholes > 0) {
// Initialize 'holelist'.
holelist = new REAL[numberofholes * 3];
for (i = 0; i < 3 * numberofholes; i += 3) {
stringptr = readnumberline(inputline, infile, infilename);
stringptr = findnextnumber(stringptr);
if (*stringptr == '\0') {
printf("Error: Hole %d has no x coord.\n", firstnumber + (i / 3));
break;
} else {
holelist[i] = (REAL) strtod(stringptr, &stringptr);
}
stringptr = findnextnumber(stringptr);
if (*stringptr == '\0') {
printf("Error: Hole %d has no y coord.\n", firstnumber + (i / 3));
break;
} else {
holelist[i + 1] = (REAL) strtod(stringptr, &stringptr);
}
stringptr = findnextnumber(stringptr);
if (*stringptr == '\0') {
printf("Error: Hole %d has no z coord.\n", firstnumber + (i / 3));
break;
} else {
holelist[i + 2] = (REAL) strtod(stringptr, &stringptr);
}
}
if (i < 3 * numberofholes) {
// This must be caused by an error.
fclose(infile);
return false;
}
}
// Read the region section. The 'region' section is optional, if we
// don't reach the end-of-file, try read it in.
stringptr = readnumberline(inputline, infile, NULL);
if (stringptr != (char *) NULL && *stringptr != '\0') {
numberofregions = (int) strtol (stringptr, &stringptr, 0);
} else {
numberofregions = 0;
}
if (numberofregions > 0) {
// Initialize 'regionlist'.
regionlist = new REAL[numberofregions * 5];
index = 0;
for (i = 0; i < numberofregions; i++) {
stringptr = readnumberline(inputline, infile, infilename);
stringptr = findnextnumber(stringptr);
if (*stringptr == '\0') {
printf("Error: Region %d has no x coordinate.\n", firstnumber + i);
break;
} else {
regionlist[index++] = (REAL) strtod(stringptr, &stringptr);
}
stringptr = findnextnumber(stringptr);
if (*stringptr == '\0') {
printf("Error: Region %d has no y coordinate.\n", firstnumber + i);
break;
} else {
regionlist[index++] = (REAL) strtod(stringptr, &stringptr);
}
stringptr = findnextnumber(stringptr);
if (*stringptr == '\0') {
printf("Error: Region %d has no z coordinate.\n", firstnumber + i);
break;
} else {
regionlist[index++] = (REAL) strtod(stringptr, &stringptr);
}
stringptr = findnextnumber(stringptr);
if (*stringptr == '\0') {
printf("Error: Region %d has no region attrib.\n", firstnumber + i);
break;
} else {
regionlist[index++] = (REAL) strtod(stringptr, &stringptr);
}
stringptr = findnextnumber(stringptr);
if (*stringptr == '\0') {
regionlist[index] = regionlist[index - 1];
} else {
regionlist[index] = (REAL) strtod(stringptr, &stringptr);
}
index++;
}
if (i < numberofregions) {
// This must be caused by an error.
fclose(infile);
return false;
}
}
} 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.
fclose(infile);
return true;
}
///////////////////////////////////////////////////////////////////////////////
// //
// load_off() Load a polyhedron from a .off file. //
// //
// The .off format is one of file formats of the Geomview, an interactive //
// program for viewing and manipulating geometric objects. More information //
// is available form: http://www.geomview.org. //
// //
///////////////////////////////////////////////////////////////////////////////
bool tetgenio::load_off(char* filebasename)
{
FILE *fp;
tetgenio::facet *f;
tetgenio::polygon *p;
char infilename[FILENAMESIZE];
char buffer[INPUTLINESIZE];
char *bufferp;
double *coord;
int nverts = 0, iverts = 0;
int nfaces = 0, ifaces = 0;
int nedges = 0;
int line_count = 0, i;
// Default, the off file's index is from '0'. We check it by remembering the
// smallest index we found in the file. It should be either 0 or 1.
int smallestidx = 0;
strncpy(infilename, filebasename, 1024 - 1);
infilename[FILENAMESIZE - 1] = '\0';
if (infilename[0] == '\0') {
printf("Error: No filename.\n");
return false;
}
if (strcmp(&infilename[strlen(infilename) - 4], ".off") != 0) {
strcat(infilename, ".off");
}
if (!(fp = fopen(infilename, "r"))) {
printf(" Unable to open file %s\n", infilename);
return false;
}
printf("Opening %s.\n", infilename);
while ((bufferp = readline(buffer, fp, &line_count)) != NULL) {
// Check section
if (nverts == 0) {
// Read header
bufferp = strstr(bufferp, "OFF");
if (bufferp != NULL) {
// Read mesh counts
bufferp = findnextnumber(bufferp); // Skip field "OFF".
if (*bufferp == '\0') {
// Read a non-empty line.
bufferp = readline(buffer, fp, &line_count);
}
if ((sscanf(bufferp, "%d%d%d", &nverts, &nfaces, &nedges) != 3)
|| (nverts == 0)) {
printf("Syntax error reading header on line %d in file %s\n",
line_count, infilename);
fclose(fp);
return false;
}
// Allocate memory for 'tetgenio'
if (nverts > 0) {
numberofpoints = nverts;
pointlist = new REAL[nverts * 3];
smallestidx = nverts + 1; // A bigger enough number.
}
if (nfaces > 0) {
numberoffacets = nfaces;
facetlist = new tetgenio::facet[nfaces];
}
}
} else if (iverts < nverts) {
// Read vertex coordinates
coord = &pointlist[iverts * 3];
for (i = 0; i < 3; i++) {
if (*bufferp == '\0') {
printf("Syntax error reading vertex coords on line %d in file %s\n",
line_count, infilename);
fclose(fp);
return false;
}
coord[i] = (REAL) strtod(bufferp, &bufferp);
bufferp = findnextnumber(bufferp);
}
iverts++;
} else if (ifaces < nfaces) {
// Get next face
f = &facetlist[ifaces];
init(f);
// In .off format, each facet has one polygon, no hole.
f->numberofpolygons = 1;
f->polygonlist = new tetgenio::polygon[1];
p = &f->polygonlist[0];
init(p);
// Read the number of vertices, it should be greater than 0.
p->numberofvertices = (int) strtol(bufferp, &bufferp, 0);
if (p->numberofvertices == 0) {
printf("Syntax error reading polygon on line %d in file %s\n",
line_count, infilename);
fclose(fp);
return false;
}
// Allocate memory for face vertices
p->vertexlist = new int[p->numberofvertices];
for (i = 0; i < p->numberofvertices; i++) {
bufferp = findnextnumber(bufferp);
if (*bufferp == '\0') {
printf("Syntax error reading polygon on line %d in file %s\n",
line_count, infilename);
fclose(fp);
return false;
}
p->vertexlist[i] = (int) strtol(bufferp, &bufferp, 0);
// Detect the smallest index.
if (p->vertexlist[i] < smallestidx) {
smallestidx = p->vertexlist[i];
}
}
ifaces++;
} else {
// Should never get here
printf("Found extra text starting at line %d in file %s\n", line_count,
infilename);
break;
}
}
// Close file
fclose(fp);
// Decide the firstnumber of the index.
if (smallestidx == 0) {
firstnumber = 0;
} else if (smallestidx == 1) {
firstnumber = 1;
} else {
printf("A wrong smallest index (%d) was detected in file %s\n",
smallestidx, infilename);
return false;
}
if (iverts != nverts) {
printf("Expected %d vertices, but read only %d vertices in file %s\n",
nverts, iverts, infilename);
return false;
}
if (ifaces != nfaces) {
printf("Expected %d faces, but read only %d faces in file %s\n",
nfaces, ifaces, infilename);
return false;
}
return true;
}
///////////////////////////////////////////////////////////////////////////////
// //
// load_ply() Load a polyhedron from a .ply file. //
// //
// This is a simplified version of reading .ply files, which only reads the //
// set of vertices and the set of faces. Other informations (such as color, //
// material, texture, etc) in .ply file are ignored. Complete routines for //
// reading and writing ,ply files are available from: http://www.cc.gatech. //
// edu/projects/large_models/ply.html. Except the header section, ply file //
// format has exactly the same format for listing vertices and polygons as //
// off file format. //
// //
///////////////////////////////////////////////////////////////////////////////
bool tetgenio::load_ply(char* filebasename)
{
FILE *fp;
tetgenio::facet *f;
tetgenio::polygon *p;
char infilename[FILENAMESIZE];
char buffer[INPUTLINESIZE];
char *bufferp, *str;
double *coord;
int endheader = 0, format = 0;
int nverts = 0, iverts = 0;
int nfaces = 0, ifaces = 0;
int line_count = 0, i;
// Default, the ply file's index is from '0'. We check it by remembering the
// smallest index we found in the file. It should be either 0 or 1.
int smallestidx = 0;
strncpy(infilename, filebasename, FILENAMESIZE - 1);
infilename[FILENAMESIZE - 1] = '\0';
if (infilename[0] == '\0') {
printf("Error: No filename.\n");
return false;
}
if (strcmp(&infilename[strlen(infilename) - 4], ".ply") != 0) {
strcat(infilename, ".ply");
}
if (!(fp = fopen(infilename, "r"))) {
printf("Error: Unable to open file %s\n", infilename);
return false;
}
printf("Opening %s.\n", infilename);
while ((bufferp = readline(buffer, fp, &line_count)) != NULL) {
if (!endheader) {
// Find if it is the keyword "end_header".
str = strstr(bufferp, "end_header");
// strstr() is case sensitive.
if (!str) str = strstr(bufferp, "End_header");
if (!str) str = strstr(bufferp, "End_Header");
if (str) {
// This is the end of the header section.
endheader = 1;
continue;
}
// Parse the number of vertices and the number of faces.
if (nverts == 0 || nfaces == 0) {
// Find if it si the keyword "element".
str = strstr(bufferp, "element");
if (!str) str = strstr(bufferp, "Element");
if (str) {
bufferp = findnextfield(str);
if (*bufferp == '\0') {
printf("Syntax error reading element type on line%d in file %s\n",
line_count, infilename);
fclose(fp);
return false;
}
if (nverts == 0) {
// Find if it is the keyword "vertex".
str = strstr(bufferp, "vertex");
if (!str) str = strstr(bufferp, "Vertex");
if (str) {
bufferp = findnextnumber(str);
if (*bufferp == '\0') {
printf("Syntax error reading vertex number on line");
printf(" %d in file %s\n", line_count, infilename);
fclose(fp);
return false;
}
nverts = (int) strtol(bufferp, &bufferp, 0);
// Allocate memory for 'tetgenio'
if (nverts > 0) {
numberofpoints = nverts;
pointlist = new REAL[nverts * 3];
smallestidx = nverts + 1; // A big enough index.
}
}
}
if (nfaces == 0) {
// Find if it is the keyword "face".
str = strstr(bufferp, "face");
if (!str) str = strstr(bufferp, "Face");
if (str) {
bufferp = findnextnumber(str);
if (*bufferp == '\0') {
printf("Syntax error reading face number on line");
printf(" %d in file %s\n", line_count, infilename);
fclose(fp);
return false;
}
nfaces = (int) strtol(bufferp, &bufferp, 0);
// Allocate memory for 'tetgenio'
if (nfaces > 0) {
numberoffacets = nfaces;
facetlist = new tetgenio::facet[nfaces];
}
}
}
} // It is not the string "element".
}
if (format == 0) {
// Find the keyword "format".
str = strstr(bufferp, "format");
if (!str) str = strstr(bufferp, "Format");
if (str) {
format = 1;
bufferp = findnextfield(str);
// Find if it is the string "ascii".
str = strstr(bufferp, "ascii");
if (!str) str = strstr(bufferp, "ASCII");
if (!str) {
printf("This routine only reads ascii format of ply files.\n");
printf("Hint: You can convert the binary to ascii format by\n");
printf(" using the provided ply tools:\n");
printf(" ply2ascii < %s > ascii_%s\n", infilename, infilename);
fclose(fp);
return false;
}
}
}
} else if (iverts < nverts) {
// Read vertex coordinates
coord = &pointlist[iverts * 3];
for (i = 0; i < 3; i++) {
if (*bufferp == '\0') {
printf("Syntax error reading vertex coords on line %d in file %s\n",
line_count, infilename);
fclose(fp);
return false;
}
coord[i] = (REAL) strtod(bufferp, &bufferp);
bufferp = findnextnumber(bufferp);
}
iverts++;
} else if (ifaces < nfaces) {
// Get next face
f = &facetlist[ifaces];
init(f);
// In .off format, each facet has one polygon, no hole.
f->numberofpolygons = 1;
f->polygonlist = new tetgenio::polygon[1];
p = &f->polygonlist[0];
init(p);
// Read the number of vertices, it should be greater than 0.
p->numberofvertices = (int) strtol(bufferp, &bufferp, 0);
if (p->numberofvertices == 0) {
printf("Syntax error reading polygon on line %d in file %s\n",
line_count, infilename);
fclose(fp);
return false;
}
// Allocate memory for face vertices
p->vertexlist = new int[p->numberofvertices];
for (i = 0; i < p->numberofvertices; i++) {
bufferp = findnextnumber(bufferp);
if (*bufferp == '\0') {
printf("Syntax error reading polygon on line %d in file %s\n",
line_count, infilename);
fclose(fp);
return false;
}
p->vertexlist[i] = (int) strtol(bufferp, &bufferp, 0);
if (p->vertexlist[i] < smallestidx) {
smallestidx = p->vertexlist[i];
}
}
ifaces++;
} else {
// Should never get here
printf("Found extra text starting at line %d in file %s\n", line_count,
infilename);
break;
}
}
// Close file
fclose(fp);
// Decide the firstnumber of the index.
if (smallestidx == 0) {
firstnumber = 0;
} else if (smallestidx == 1) {
firstnumber = 1;
} else {
printf("A wrong smallest index (%d) was detected in file %s\n",
smallestidx, infilename);
return false;
}
if (iverts != nverts) {
printf("Expected %d vertices, but read only %d vertices in file %s\n",
nverts, iverts, infilename);
return false;
}
if (ifaces != nfaces) {
printf("Expected %d faces, but read only %d faces in file %s\n",
nfaces, ifaces, infilename);
return false;
}
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. //
// //
///////////////////////////////////////////////////////////////////////////////
bool tetgenio::load_stl(char* filebasename)
{
FILE *fp;
tetgenmesh::arraypool *plist;
tetgenio::facet *f;
tetgenio::polygon *p;
char infilename[FILENAMESIZE];
char buffer[INPUTLINESIZE];
char *bufferp, *str;
double *coord;
int solid = 0;
int nverts = 0, iverts = 0;
int nfaces = 0;
int line_count = 0, i;
strncpy(infilename, filebasename, FILENAMESIZE - 1);
infilename[FILENAMESIZE - 1] = '\0';
if (infilename[0] == '\0') {
printf("Error: No filename.\n");
return false;
}
if (strcmp(&infilename[strlen(infilename) - 4], ".stl") != 0) {
strcat(infilename, ".stl");
}
if (!(fp = fopen(infilename, "r"))) {
printf("Error: Unable to open file %s\n", infilename);
return false;
}
printf("Opening %s.\n", infilename);
// 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;
} 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);
}
}
}
}
}
fclose(fp);
nverts = (int) plist->objects;
// nverts should be an integer times 3 (every 3 vertices denote a face).
if (nverts == 0 || (nverts % 3 != 0)) {
printf("Error: Wrong number of vertices in file %s.\n", infilename);
delete plist;
return false;
}
numberofpoints = nverts;
pointlist = new REAL[nverts * 3];
for (i = 0; i < nverts; i++) {
coord = (double *) fastlookup(plist, i);
iverts = i * 3;
pointlist[iverts] = (REAL) coord[0];
pointlist[iverts + 1] = (REAL) coord[1];
pointlist[iverts + 2] = (REAL) coord[2];
}
nfaces = (int) (nverts / 3);
numberoffacets = nfaces;
facetlist = new tetgenio::facet[nfaces];
// Default use '1' as the array starting index.
firstnumber = 1;
iverts = firstnumber;
for (i = 0; i < nfaces; i++) {
f = &facetlist[i];
init(f);
// In .stl format, each facet has one polygon, no hole.
f->numberofpolygons = 1;
f->polygonlist = new tetgenio::polygon[1];
p = &f->polygonlist[0];
init(p);
// Each polygon has three vertices.
p->numberofvertices = 3;
p->vertexlist = new int[p->numberofvertices];
p->vertexlist[0] = iverts;
p->vertexlist[1] = iverts + 1;
p->vertexlist[2] = iverts + 2;
iverts += 3;
}
delete plist;
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. //
// //
///////////////////////////////////////////////////////////////////////////////
bool tetgenio::load_medit(char* filebasename, int istetmesh)
{
FILE *fp;
tetgenio::facet *tmpflist, *f;
tetgenio::polygon *p;
char infilename[FILENAMESIZE];
char buffer[INPUTLINESIZE];
char *bufferp, *str;
double *coord;
int *tmpfmlist;
int dimension = 0;
int nverts = 0;
int nfaces = 0;
int ntets = 0;
int line_count = 0;
int corners = 0; // 3 (triangle) or 4 (quad).
int *plist;
int i, j;
int smallestidx = 0;
strncpy(infilename, filebasename, FILENAMESIZE - 1);
infilename[FILENAMESIZE - 1] = '\0';
if (infilename[0] == '\0') {
printf("Error: No filename.\n");
return false;
}
if (strcmp(&infilename[strlen(infilename) - 5], ".mesh") != 0) {
strcat(infilename, ".mesh");
}
if (!(fp = fopen(infilename, "r"))) {
printf("Error: Unable to open file %s\n", infilename);
return false;
}
printf("Opening %s.\n", infilename);
while ((bufferp = readline(buffer, fp, &line_count)) != NULL) {
if (*bufferp == '#') continue; // A comment line is skipped.
if (dimension == 0) {
// Find if it is the keyword "Dimension".
str = strstr(bufferp, "Dimension");
if (!str) str = strstr(bufferp, "dimension");
if (!str) str = strstr(bufferp, "DIMENSION");
if (str) {
// Read the dimensions
bufferp = findnextnumber(str); // Skip field "Dimension".
if (*bufferp == '\0') {
// Read a non-empty line.
bufferp = readline(buffer, fp, &line_count);
}
dimension = (int) strtol(bufferp, &bufferp, 0);
if (dimension != 2 && dimension != 3) {
printf("Unknown dimension in file on line %d in file %s\n",
line_count, infilename);
fclose(fp);
return false;
}
mesh_dim = dimension;
}
}
if (nverts == 0) {
// Find if it is the keyword "Vertices".
str = strstr(bufferp, "Vertices");
if (!str) str = strstr(bufferp, "vertices");
if (!str) str = strstr(bufferp, "VERTICES");
if (str) {
// Read the number of vertices.
bufferp = findnextnumber(str); // Skip field "Vertices".
if (*bufferp == '\0') {
// Read a non-empty line.
bufferp = readline(buffer, fp, &line_count);
}
nverts = (int) strtol(bufferp, &bufferp, 0);
// Initialize the smallest index.
smallestidx = nverts + 1;
// Allocate memory for 'tetgenio'
if (nverts > 0) {
numberofpoints = nverts;
pointlist = new REAL[nverts * 3];
}
// Read the follwoing node list.
for (i = 0; i < nverts; i++) {
bufferp = readline(buffer, fp, &line_count);
if (bufferp == NULL) {
printf("Unexpected end of file on line %d in file %s\n",
line_count, infilename);
fclose(fp);
return false;
}
// Read vertex coordinates
coord = &pointlist[i * 3];
for (j = 0; j < 3; j++) {
if (*bufferp == '\0') {
printf("Syntax error reading vertex coords on line");
printf(" %d in file %s\n", line_count, infilename);
fclose(fp);
return false;
}
if ((j < 2) || (dimension == 3)) {
coord[j] = (REAL) strtod(bufferp, &bufferp);
} else {
assert((j == 2) && (dimension == 2));
coord[j] = 0.0;
}
bufferp = findnextnumber(bufferp);
}
}
continue;
}
}
if (ntets == 0) {
// Find if it is the keyword "Tetrahedra"
corners = 0;
str = strstr(bufferp, "Tetrahedra");
if (!str) str = strstr(bufferp, "tetrahedra");
if (!str) str = strstr(bufferp, "TETRAHEDRA");
if (str) {
corners = 4;
}
if (corners == 4) {
// Read the number of tetrahedra
bufferp = findnextnumber(str); // Skip field "Tetrahedra".
if (*bufferp == '\0') {
// Read a non-empty line.
bufferp = readline(buffer, fp, &line_count);
}
ntets = strtol(bufferp, &bufferp, 0);
if (ntets > 0) {
// It is a tetrahedral mesh.
numberoftetrahedra = ntets;
numberofcorners = 4;
numberoftetrahedronattributes = 1;
tetrahedronlist = new int[ntets * 4];
tetrahedronattributelist = new REAL[ntets];
}
} // if (corners == 4)
// Read the list of tetrahedra.
for (i = 0; i < numberoftetrahedra; i++) {
plist = &(tetrahedronlist[i * 4]);
bufferp = readline(buffer, fp, &line_count);
if (bufferp == NULL) {
printf("Unexpected end of file on line %d in file %s\n",
line_count, infilename);
fclose(fp);
return false;
}
// Read the vertices of the tet.
for (j = 0; j < corners; j++) {
if (*bufferp == '\0') {
printf("Syntax error reading face on line %d in file %s\n",
line_count, infilename);
fclose(fp);
return false;
}
plist[j] = (int) strtol(bufferp, &bufferp, 0);
// Remember the smallest index.
if (plist[j] < smallestidx) smallestidx = plist[j];
bufferp = findnextnumber(bufferp);
}
// Read the attribute of the tet if it exists.
tetrahedronattributelist[i] = 0;
if (*bufferp != '\0') {
tetrahedronattributelist[i] = (REAL) strtol(bufferp, &bufferp, 0);
}
} // i
} // Tetrahedra
if (nfaces == 0) {
// Find if it is the keyword "Triangles" or "Quadrilaterals".
corners = 0;
str = strstr(bufferp, "Triangles");
if (!str) str = strstr(bufferp, "triangles");
if (!str) str = strstr(bufferp, "TRIANGLES");
if (str) {
corners = 3;
} else {
str = strstr(bufferp, "Quadrilaterals");
if (!str) str = strstr(bufferp, "quadrilaterals");
if (!str) str = strstr(bufferp, "QUADRILATERALS");
if (str) {
corners = 4;
}
}
if (corners == 3 || corners == 4) {
// Read the number of triangles (or quadrilaterals).
bufferp = findnextnumber(str); // Skip field "Triangles".
if (*bufferp == '\0') {
// Read a non-empty line.
bufferp = readline(buffer, fp, &line_count);
}
nfaces = strtol(bufferp, &bufferp, 0);
// Allocate memory for 'tetgenio'
if (nfaces > 0) {
if (!istetmesh) {
// It is a PLC surface mesh.
if (numberoffacets > 0) {
// facetlist has already been allocated. Enlarge arrays.
// This happens when the surface mesh contains mixed cells.
tmpflist = new tetgenio::facet[numberoffacets + nfaces];
tmpfmlist = new int[numberoffacets + nfaces];
// Copy the data of old arrays into new arrays.
for (i = 0; i < numberoffacets; i++) {
f = &(tmpflist[i]);
tetgenio::init(f);
*f = facetlist[i];
tmpfmlist[i] = facetmarkerlist[i];
}
// Release old arrays.
delete [] facetlist;
delete [] facetmarkerlist;
// Remember the new arrays.
facetlist = tmpflist;
facetmarkerlist = tmpfmlist;
} else {
// This is the first time to allocate facetlist.
facetlist = new tetgenio::facet[nfaces];
facetmarkerlist = new int[nfaces];
}
} else {
if (corners == 3) {
// It is a surface mesh of a tetrahedral mesh.
numberoftrifaces = nfaces;
trifacelist = new int[nfaces * 3];
trifacemarkerlist = new int[nfaces];
}
}
} // if (nfaces > 0)
// Read the following list of faces.
if (!istetmesh) {
for (i = numberoffacets; i < numberoffacets + nfaces; i++) {
bufferp = readline(buffer, fp, &line_count);
if (bufferp == NULL) {
printf("Unexpected end of file on line %d in file %s\n",
line_count, infilename);
fclose(fp);
return false;
}
f = &facetlist[i];
tetgenio::init(f);
// In .mesh format, each facet has one polygon, no hole.
f->numberofpolygons = 1;
f->polygonlist = new tetgenio::polygon[1];
p = &f->polygonlist[0];
tetgenio::init(p);
p->numberofvertices = corners;
// Allocate memory for face vertices
p->vertexlist = new int[p->numberofvertices];
// Read the vertices of the face.
for (j = 0; j < corners; j++) {
if (*bufferp == '\0') {
printf("Syntax error reading face on line %d in file %s\n",
line_count, infilename);
fclose(fp);
return false;
}
p->vertexlist[j] = (int) strtol(bufferp, &bufferp, 0);
// Remember the smallest index.
if (p->vertexlist[j] < smallestidx) {
smallestidx = p->vertexlist[j];
}
bufferp = findnextnumber(bufferp);
}
// Read the marker of the face if it exists.
facetmarkerlist[i] = 0;
if (*bufferp != '\0') {
facetmarkerlist[i] = (int) strtol(bufferp, &bufferp, 0);
}
}
// Have read in a list of triangles/quads.
numberoffacets += nfaces;
nfaces = 0;
} else {
// It is a surface mesh of a tetrahedral mesh.
if (corners == 3) {
for (i = 0; i < numberoftrifaces; i++) {
plist = &(trifacelist[i * 3]);
bufferp = readline(buffer, fp, &line_count);
if (bufferp == NULL) {
printf("Unexpected end of file on line %d in file %s\n",
line_count, infilename);
fclose(fp);
return false;
}
// Read the vertices of the face.
for (j = 0; j < corners; j++) {
if (*bufferp == '\0') {
printf("Syntax error reading face on line %d in file %s\n",
line_count, infilename);
fclose(fp);
return false;
}
plist[j] = (int) strtol(bufferp, &bufferp, 0);
// Remember the smallest index.
if (plist[j] < smallestidx) {
smallestidx = plist[j];
}
bufferp = findnextnumber(bufferp);
}
// Read the marker of the face if it exists.
trifacemarkerlist[i] = 0;
if (*bufferp != '\0') {
trifacemarkerlist[i] = (int) strtol(bufferp, &bufferp, 0);
}
} // i
} // if (corners == 3)
} // if (b->refine)
} // if (corners == 3 || corners == 4)
}
}
// Close file
fclose(fp);
// Decide the firstnumber of the index.
if (smallestidx == 0) {
firstnumber = 0;
} else if (smallestidx == 1) {
firstnumber = 1;
} else {
printf("A wrong smallest index (%d) was detected in file %s\n",
smallestidx, infilename);
return false;
}
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. //
// //
///////////////////////////////////////////////////////////////////////////////
// Two inline functions used in read/write VTK files.
void swapBytes(unsigned char* var, int size)
{
int i = 0;
int j = size - 1;
char c;
while (i < j) {
c = var[i]; var[i] = var[j]; var[j] = c;
i++, j--;
}
}
bool testIsBigEndian()
{
short word = 0x4321;
if((*(char *)& word) != 0x21)
return true;
else
return false;
}
bool tetgenio::load_vtk(char* filebasename)
{
FILE *fp;
tetgenio::facet *f;
tetgenio::polygon *p;
char infilename[FILENAMESIZE];
char line[INPUTLINESIZE];
char mode[128], id[256], fmt[64];
char *bufferp;
double *coord;
float _x, _y, _z;
int nverts = 0;
int nfaces = 0;
int line_count = 0;
int dummy;
int id1, id2, id3;
int nn = -1;
int nn_old = -1;
int i, j;
bool ImALittleEndian = !testIsBigEndian();
int smallestidx = 0;
strncpy(infilename, filebasename, FILENAMESIZE - 1);
infilename[FILENAMESIZE - 1] = '\0';
if (infilename[0] == '\0') {
printf("Error: No filename.\n");
return false;
}
if (strcmp(&infilename[strlen(infilename) - 4], ".vtk") != 0) {
strcat(infilename, ".vtk");
}
if (!(fp = fopen(infilename, "r"))) {
printf("Error: Unable to open file %s\n", infilename);
return false;
}
printf("Opening %s.\n", infilename);
// Default uses the index starts from '0'.
firstnumber = 0;
strcpy(mode, "BINARY");
while((bufferp = readline(line, fp, &line_count)) != NULL) {
if(strlen(line) == 0) continue;
//swallow lines beginning with a comment sign or white space
if(line[0] == '#' || line[0]=='\n' || line[0] == 10 || line[0] == 13 ||
line[0] == 32) continue;
sscanf(line, "%s", id);
if(!strcmp(id, "ASCII")) {
strcpy(mode, "ASCII");
}
if(!strcmp(id, "POINTS")) {
sscanf(line, "%s %d %s", id, &nverts, fmt);
if (nverts > 0) {
numberofpoints = nverts;
pointlist = new REAL[nverts * 3];
smallestidx = nverts + 1;
}
if(!strcmp(mode, "BINARY")) {
for(i = 0; i < nverts; i++) {
coord = &pointlist[i * 3];
if(!strcmp(fmt, "double")) {
fread((char*)(&(coord[0])), sizeof(double), 1, fp);
fread((char*)(&(coord[1])), sizeof(double), 1, fp);
fread((char*)(&(coord[2])), sizeof(double), 1, fp);
if(ImALittleEndian){
swapBytes((unsigned char *) &(coord[0]), sizeof(coord[0]));
swapBytes((unsigned char *) &(coord[1]), sizeof(coord[1]));
swapBytes((unsigned char *) &(coord[2]), sizeof(coord[2]));
}
} else if(!strcmp(fmt, "float")) {
fread((char*)(&_x), sizeof(float), 1, fp);
fread((char*)(&_y), sizeof(float), 1, fp);
fread((char*)(&_z), sizeof(float), 1, fp);
if(ImALittleEndian){
swapBytes((unsigned char *) &_x, sizeof(_x));
swapBytes((unsigned char *) &_y, sizeof(_y));
swapBytes((unsigned char *) &_z, sizeof(_z));
}
coord[0] = double(_x);
coord[1] = double(_y);
coord[2] = double(_z);
} else {
printf("Error: Only float or double formats are supported!\n");
return false;
}
}
} else if(!strcmp(mode, "ASCII")) {
for(i = 0; i < nverts; i++){
bufferp = readline(line, fp, &line_count);
if (bufferp == NULL) {
printf("Unexpected end of file on line %d in file %s\n",
line_count, infilename);
fclose(fp);
return false;
}
// Read vertex coordinates
coord = &pointlist[i * 3];
for (j = 0; j < 3; j++) {
if (*bufferp == '\0') {
printf("Syntax error reading vertex coords on line");
printf(" %d in file %s\n", line_count, infilename);
fclose(fp);
return false;
}
coord[j] = (REAL) strtod(bufferp, &bufferp);
bufferp = findnextnumber(bufferp);
}
}
}
continue;
}
if(!strcmp(id, "POLYGONS")) {
sscanf(line, "%s %d %d", id, &nfaces, &dummy);
if (nfaces > 0) {
numberoffacets = nfaces;
facetlist = new tetgenio::facet[nfaces];
}
if(!strcmp(mode, "BINARY")) {
for(i = 0; i < nfaces; i++){
fread((char*)(&nn), sizeof(int), 1, fp);
if(ImALittleEndian){
swapBytes((unsigned char *) &nn, sizeof(nn));
}
if (i == 0)
nn_old = nn;
if (nn != nn_old) {
printf("Error: No mixed cells are allowed.\n");
return false;
}
if(nn == 3){
fread((char*)(&id1), sizeof(int), 1, fp);
fread((char*)(&id2), sizeof(int), 1, fp);
fread((char*)(&id3), sizeof(int), 1, fp);
if(ImALittleEndian){
swapBytes((unsigned char *) &id1, sizeof(id1));
swapBytes((unsigned char *) &id2, sizeof(id2));
swapBytes((unsigned char *) &id3, sizeof(id3));
}
f = &facetlist[i];
init(f);
// In .off format, each facet has one polygon, no hole.
f->numberofpolygons = 1;
f->polygonlist = new tetgenio::polygon[1];
p = &f->polygonlist[0];
init(p);
// Set number of vertices
p->numberofvertices = 3;
// Allocate memory for face vertices
p->vertexlist = new int[p->numberofvertices];
p->vertexlist[0] = id1;
p->vertexlist[1] = id2;
p->vertexlist[2] = id3;
// Detect the smallest index.
for (j = 0; j < 3; j++) {
if (p->vertexlist[j] < smallestidx) {
smallestidx = p->vertexlist[j];
}
}
} else {
printf("Error: Only triangles are supported\n");
return false;
}
}
} else if(!strcmp(mode, "ASCII")) {
for(i = 0; i < nfaces; i++) {
bufferp = readline(line, fp, &line_count);
nn = (int) strtol(bufferp, &bufferp, 0);
if (i == 0)
nn_old = nn;
if (nn != nn_old) {
printf("Error: No mixed cells are allowed.\n");
return false;
}
if (nn == 3) {
bufferp = findnextnumber(bufferp); // Skip the first field.
id1 = (int) strtol(bufferp, &bufferp, 0);
bufferp = findnextnumber(bufferp);
id2 = (int) strtol(bufferp, &bufferp, 0);
bufferp = findnextnumber(bufferp);
id3 = (int) strtol(bufferp, &bufferp, 0);
f = &facetlist[i];
init(f);
// In .off format, each facet has one polygon, no hole.
f->numberofpolygons = 1;
f->polygonlist = new tetgenio::polygon[1];
p = &f->polygonlist[0];
init(p);
// Set number of vertices
p->numberofvertices = 3;
// Allocate memory for face vertices
p->vertexlist = new int[p->numberofvertices];
p->vertexlist[0] = id1;
p->vertexlist[1] = id2;
p->vertexlist[2] = id3;
// Detect the smallest index.
for (j = 0; j < 3; j++) {
if (p->vertexlist[j] < smallestidx) {
smallestidx = p->vertexlist[j];
}
}
} else {
printf("Error: Only triangles are supported.\n");
return false;
}
}
}
fclose(fp);
// Decide the firstnumber of the index.
if (smallestidx == 0) {
firstnumber = 0;
} else if (smallestidx == 1) {
firstnumber = 1;
} else {
printf("A wrong smallest index (%d) was detected in file %s\n",
smallestidx, infilename);
return false;
}
return true;
}
if(!strcmp(id,"LINES") || !strcmp(id,"CELLS")){
printf("Warning: load_vtk(): cannot read formats LINES, CELLS.\n");
}
} // while ()
return true;
}
///////////////////////////////////////////////////////////////////////////////
// //
// load_plc() Load a piecewise linear complex from file(s). //
// //
///////////////////////////////////////////////////////////////////////////////
bool tetgenio::load_plc(char* filebasename, int object)
{
bool success;
if (object == (int) tetgenbehavior::NODES) {
success = load_node(filebasename);
} else if (object == (int) tetgenbehavior::POLY) {
success = load_poly(filebasename);
} else if (object == (int) tetgenbehavior::OFF) {
success = load_off(filebasename);
} else if (object == (int) tetgenbehavior::PLY) {
success = load_ply(filebasename);
} else if (object == (int) tetgenbehavior::STL) {
success = load_stl(filebasename);
} else if (object == (int) tetgenbehavior::MEDIT) {
success = load_medit(filebasename, 0);
} else if (object == (int) tetgenbehavior::VTK) {
success = load_vtk(filebasename);
} else {
success = load_poly(filebasename);
}
if (success) {
// Try to load the following files (.edge, .var, .mtr).
load_edge(filebasename);
load_var(filebasename);
load_mtr(filebasename);
}
return success;
}
///////////////////////////////////////////////////////////////////////////////
// //
// load_mesh() Load a tetrahedral mesh from file(s). //
// //
///////////////////////////////////////////////////////////////////////////////
bool tetgenio::load_tetmesh(char* filebasename, int object)
{
bool success;
if (object == (int) tetgenbehavior::MEDIT) {
success = load_medit(filebasename, 1);
} else {
success = load_node(filebasename);
if (success) {
success = load_tet(filebasename);
}
if (success) {
// Try to load the following files (.face, .edge, .vol).
load_face(filebasename);
load_edge(filebasename);
load_vol(filebasename);
}
}
if (success) {
// Try to load the following files (.var, .mtr).
load_var(filebasename);
load_mtr(filebasename);
}
return success;
}
///////////////////////////////////////////////////////////////////////////////
// //
// save_nodes() Save points to a .node file. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenio::save_nodes(char* filebasename)
{
FILE *fout;
char outnodefilename[FILENAMESIZE];
char outmtrfilename[FILENAMESIZE];
int i, j;
sprintf(outnodefilename, "%s.node", filebasename);
printf("Saving nodes to %s\n", outnodefilename);
fout = fopen(outnodefilename, "w");
fprintf(fout, "%d %d %d %d\n", numberofpoints, mesh_dim,
numberofpointattributes, pointmarkerlist != NULL ? 1 : 0);
for (i = 0; i < numberofpoints; i++) {
if (mesh_dim == 2) {
fprintf(fout, "%d %.16g %.16g", i + firstnumber, pointlist[i * 3],
pointlist[i * 3 + 1]);
} else {
fprintf(fout, "%d %.16g %.16g %.16g", i + firstnumber,
pointlist[i * 3], pointlist[i * 3 + 1], pointlist[i * 3 + 2]);
}
for (j = 0; j < numberofpointattributes; j++) {
fprintf(fout, " %.16g",
pointattributelist[i * numberofpointattributes + j]);
}
if (pointmarkerlist != NULL) {
fprintf(fout, " %d", pointmarkerlist[i]);
}
fprintf(fout, "\n");
}
fclose(fout);
// If the point metrics exist, output them to a .mtr file.
if ((numberofpointmtrs > 0) && (pointmtrlist != (REAL *) NULL)) {
sprintf(outmtrfilename, "%s.mtr", filebasename);
printf("Saving metrics to %s\n", outmtrfilename);
fout = fopen(outmtrfilename, "w");
fprintf(fout, "%d %d\n", numberofpoints, numberofpointmtrs);
for (i = 0; i < numberofpoints; i++) {
for (j = 0; j < numberofpointmtrs; j++) {
fprintf(fout, "%.16g ", pointmtrlist[i * numberofpointmtrs + j]);
}
fprintf(fout, "\n");
}
fclose(fout);
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// save_elements() Save elements to a .ele file. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenio::save_elements(char* filebasename)
{
FILE *fout;
char outelefilename[FILENAMESIZE];
int i, j;
sprintf(outelefilename, "%s.ele", filebasename);
printf("Saving elements to %s\n", outelefilename);
fout = fopen(outelefilename, "w");
if (mesh_dim == 3) {
fprintf(fout, "%d %d %d\n", numberoftetrahedra, numberofcorners,
numberoftetrahedronattributes);
for (i = 0; i < numberoftetrahedra; i++) {
fprintf(fout, "%d", i + firstnumber);
for (j = 0; j < numberofcorners; j++) {
fprintf(fout, " %5d", tetrahedronlist[i * numberofcorners + j]);
}
for (j = 0; j < numberoftetrahedronattributes; j++) {
fprintf(fout, " %g",
tetrahedronattributelist[i * numberoftetrahedronattributes + j]);
}
fprintf(fout, "\n");
}
} else {
// Save a two-dimensional mesh.
fprintf(fout, "%d %d %d\n",numberoftrifaces,3,trifacemarkerlist ? 1 : 0);
for (i = 0; i < numberoftrifaces; i++) {
fprintf(fout, "%d", i + firstnumber);
for (j = 0; j < 3; j++) {
fprintf(fout, " %5d", trifacelist[i * 3 + j]);
}
if (trifacemarkerlist != NULL) {
fprintf(fout, " %d", trifacemarkerlist[i]);
}
fprintf(fout, "\n");
}
}
fclose(fout);
}
///////////////////////////////////////////////////////////////////////////////
// //
// save_faces() Save faces to a .face file. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenio::save_faces(char* filebasename)
{
FILE *fout;
char outfacefilename[FILENAMESIZE];
int i;
sprintf(outfacefilename, "%s.face", filebasename);
printf("Saving faces to %s\n", outfacefilename);
fout = fopen(outfacefilename, "w");
fprintf(fout, "%d %d\n", numberoftrifaces,
trifacemarkerlist != NULL ? 1 : 0);
for (i = 0; i < numberoftrifaces; i++) {
fprintf(fout, "%d %5d %5d %5d", i + firstnumber, trifacelist[i * 3],
trifacelist[i * 3 + 1], trifacelist[i * 3 + 2]);
if (trifacemarkerlist != NULL) {
fprintf(fout, " %d", trifacemarkerlist[i]);
}
fprintf(fout, "\n");
}
fclose(fout);
}
///////////////////////////////////////////////////////////////////////////////
// //
// save_edges() Save egdes to a .edge file. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenio::save_edges(char* filebasename)
{
FILE *fout;
char outedgefilename[FILENAMESIZE];
int i;
sprintf(outedgefilename, "%s.edge", filebasename);
printf("Saving edges to %s\n", outedgefilename);
fout = fopen(outedgefilename, "w");
fprintf(fout, "%d %d\n", numberofedges, edgemarkerlist != NULL ? 1 : 0);
for (i = 0; i < numberofedges; i++) {
fprintf(fout, "%d %4d %4d", i + firstnumber, edgelist[i * 2],
edgelist[i * 2 + 1]);
if (edgemarkerlist != NULL) {
fprintf(fout, " %d", edgemarkerlist[i]);
}
fprintf(fout, "\n");
}
fclose(fout);
}
///////////////////////////////////////////////////////////////////////////////
// //
// save_neighbors() Save egdes to a .neigh file. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenio::save_neighbors(char* filebasename)
{
FILE *fout;
char outneighborfilename[FILENAMESIZE];
int i;
sprintf(outneighborfilename, "%s.neigh", filebasename);
printf("Saving neighbors to %s\n", outneighborfilename);
fout = fopen(outneighborfilename, "w");
fprintf(fout, "%d %d\n", numberoftetrahedra, mesh_dim + 1);
for (i = 0; i < numberoftetrahedra; i++) {
if (mesh_dim == 2) {
fprintf(fout, "%d %5d %5d %5d", i + firstnumber, neighborlist[i * 3],
neighborlist[i * 3 + 1], neighborlist[i * 3 + 2]);
} else {
fprintf(fout, "%d %5d %5d %5d %5d", i + firstnumber,
neighborlist[i * 4], neighborlist[i * 4 + 1],
neighborlist[i * 4 + 2], neighborlist[i * 4 + 3]);
}
fprintf(fout, "\n");
}
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. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenio::save_poly(char* filebasename)
{
FILE *fout;
facet *f;
polygon *p;
char outpolyfilename[FILENAMESIZE];
int i, j, k;
sprintf(outpolyfilename, "%s.poly", filebasename);
printf("Saving poly to %s\n", outpolyfilename);
fout = fopen(outpolyfilename, "w");
// The zero indicates that the vertices are in a separate .node file.
// Followed by number of dimensions, number of vertex attributes,
// and number of boundary markers (zero or one).
fprintf(fout, "%d %d %d %d\n", 0, mesh_dim, numberofpointattributes,
pointmarkerlist != NULL ? 1 : 0);
// Save segments or facets.
if (mesh_dim == 2) {
// Number of segments, number of boundary markers (zero or one).
fprintf(fout, "%d %d\n", numberofedges, edgemarkerlist != NULL ? 1 : 0);
for (i = 0; i < numberofedges; i++) {
fprintf(fout, "%d %4d %4d", i + firstnumber, edgelist[i * 2],
edgelist[i * 2 + 1]);
if (edgemarkerlist != NULL) {
fprintf(fout, " %d", edgemarkerlist[i]);
}
fprintf(fout, "\n");
}
} else {
// Number of facets, number of boundary markers (zero or one).
fprintf(fout, "%d %d\n", numberoffacets, facetmarkerlist != NULL ? 1 : 0);
for (i = 0; i < numberoffacets; i++) {
f = &(facetlist[i]);
fprintf(fout, "%d %d %d # %d\n", f->numberofpolygons,f->numberofholes,
facetmarkerlist != NULL ? facetmarkerlist[i] : 0, i + firstnumber);
// Output polygons of this facet.
for (j = 0; j < f->numberofpolygons; j++) {
p = &(f->polygonlist[j]);
fprintf(fout, "%d ", p->numberofvertices);
for (k = 0; k < p->numberofvertices; k++) {
if (((k + 1) % 10) == 0) {
fprintf(fout, "\n ");
}
fprintf(fout, " %d", p->vertexlist[k]);
}
fprintf(fout, "\n");
}
// Output holes of this facet.
for (j = 0; j < f->numberofholes; j++) {
fprintf(fout, "%d %.12g %.12g %.12g\n", j + firstnumber,
f->holelist[j * 3], f->holelist[j * 3 + 1], f->holelist[j * 3 + 2]);
}
}
}
// Save holes.
fprintf(fout, "%d\n", numberofholes);
for (i = 0; i < numberofholes; i++) {
// Output x, y coordinates.
fprintf(fout, "%d %.12g %.12g", i + firstnumber, holelist[i * mesh_dim],
holelist[i * mesh_dim + 1]);
if (mesh_dim == 3) {
// Output z coordinate.
fprintf(fout, " %.12g", holelist[i * mesh_dim + 2]);
}
fprintf(fout, "\n");
}
// Save regions.
fprintf(fout, "%d\n", numberofregions);
for (i = 0; i < numberofregions; i++) {
if (mesh_dim == 2) {
// Output the index, x, y coordinates, attribute (region number)
// and maximum area constraint (maybe -1).
fprintf(fout, "%d %.12g %.12g %.12g %.12g\n", i + firstnumber,
regionlist[i * 4], regionlist[i * 4 + 1],
regionlist[i * 4 + 2], regionlist[i * 4 + 3]);
} else {
// Output the index, x, y, z coordinates, attribute (region number)
// and maximum volume constraint (maybe -1).
fprintf(fout, "%d %.12g %.12g %.12g %.12g %.12g\n", i + firstnumber,
regionlist[i * 5], regionlist[i * 5 + 1],
regionlist[i * 5 + 2], regionlist[i * 5 + 3],
regionlist[i * 5 + 4]);
}
}
fclose(fout);
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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)
{
FILE *fout;
char outsmeshfilename[FILENAMESIZE];
int i, j;
sprintf(outsmeshfilename, "%s.smesh", filebasename);
printf("Saving faces to %s\n", outsmeshfilename);
fout = fopen(outsmeshfilename, "w");
// The zero indicates that the vertices are in a separate .node file.
// Followed by number of dimensions, number of vertex attributes,
// and number of boundary markers (zero or one).
fprintf(fout, "%d %d %d %d\n", 0, mesh_dim, numberofpointattributes,
pointmarkerlist != NULL ? 1 : 0);
// Number of facets, number of boundary markers (zero or one).
fprintf(fout, "%d %d\n", numberoftrifaces,
trifacemarkerlist != NULL ? 1 : 0);
// Output triangular facets.
for (i = 0; i < numberoftrifaces; i++) {
j = i * 3;
fprintf(fout, "3 %d %d %d", trifacelist[j], trifacelist[j + 1],
trifacelist[j + 2]);
if (trifacemarkerlist != NULL) {
fprintf(fout, " %d", trifacemarkerlist[i]);
}
fprintf(fout, "\n");
}
// No holes and regions.
fprintf(fout, "0\n");
fprintf(fout, "0\n");
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. //
// //
///////////////////////////////////////////////////////////////////////////////
char* tetgenio::readline(char *string, FILE *infile, int *linenumber)
{
char *result;
// Search for a non-empty line.
do {
result = fgets(string, INPUTLINESIZE - 1, infile);
if (linenumber) (*linenumber)++;
if (result == (char *) NULL) {
return (char *) NULL;
}
// Skip white spaces.
while ((*result == ' ') || (*result == '\t')) result++;
// If it's end of line, read another line and try again.
} while ((*result == '\0') || (*result == '\r') || (*result == '\n'));
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. //
// //
///////////////////////////////////////////////////////////////////////////////
char* tetgenio::findnextfield(char *string)
{
char *result;
result = string;
// Skip the current field. Stop upon reaching whitespace or a comma.
while ((*result != '\0') && (*result != ' ') && (*result != '\t') &&
(*result != ',') && (*result != ';')) {
result++;
}
// Now skip the whitespace or the comma, stop at anything else that looks
// like a character, or the end of a line.
while ((*result == ' ') || (*result == '\t') || (*result == ',') ||
(*result == ';')) {
result++;
}
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. //
// //
///////////////////////////////////////////////////////////////////////////////
char* tetgenio::readnumberline(char *string, FILE *infile, char *infilename)
{
char *result;
// Search for something that looks like a number.
do {
result = fgets(string, INPUTLINESIZE, infile);
if (result == (char *) NULL) {
return result;
}
// Skip anything that doesn't look like a number, a comment,
// or the end of a line.
while ((*result != '\0') && (*result != '#')
&& (*result != '.') && (*result != '+') && (*result != '-')
&& ((*result < '0') || (*result > '9'))) {
result++;
}
// If it's a comment or end of line, read another line and try again.
} while ((*result == '#') || (*result == '\0'));
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. //
// //
///////////////////////////////////////////////////////////////////////////////
char* tetgenio::findnextnumber(char *string)
{
char *result;
result = string;
// Skip the current field. Stop upon reaching whitespace or a comma.
while ((*result != '\0') && (*result != '#') && (*result != ' ') &&
(*result != '\t') && (*result != ',')) {
result++;
}
// Now skip the whitespace and anything else that doesn't look like a
// number, a comment, or the end of a line.
while ((*result != '\0') && (*result != '#')
&& (*result != '.') && (*result != '+') && (*result != '-')
&& ((*result < '0') || (*result > '9'))) {
result++;
}
// Check for a comment (prefixed with `#').
if (*result == '#') {
*result = '\0';
}
return result;
}
//// ////
//// ////
//// io_cxx ///////////////////////////////////////////////////////////////////
//// behavior_cxx /////////////////////////////////////////////////////////////
//// ////
//// ////
///////////////////////////////////////////////////////////////////////////////
// //
// syntax() Print list of command line switches. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenbehavior::syntax()
{
printf(" tetgen [-pYrq_Aa_miO_S_T_XMwcdzfenvgkJBNEFICQVh] input_file\n");
printf(" -p Tetrahedralizes a piecewise linear complex (PLC).\n");
printf(" -Y Preserves the input surface mesh (does not modify it).\n");
printf(" -r Reconstructs a previously generated mesh.\n");
printf(" -q Refines mesh (to improve mesh quality).\n");
printf(" -R Mesh coarsening (to reduce the mesh elements).\n");
printf(" -A Assigns attributes to tetrahedra in different regions.\n");
printf(" -a Applies a maximum tetrahedron volume constraint.\n");
printf(" -m Applies a mesh sizing function.\n");
printf(" -i Inserts a list of additional points.\n");
printf(" -O Specifies the level of mesh optimization.\n");
printf(" -S Specifies maximum number of added points.\n");
printf(" -T Sets a tolerance for coplanar test (default 1e-8).\n");
printf(" -X Suppresses use of exact arithmetic.\n");
printf(" -M No merge of coplanar facets or very close vertices.\n");
printf(" -w Generates weighted Delaunay (regular) triangulation.\n");
printf(" -c Retains the convex hull of the PLC.\n");
printf(" -d Detects self-intersections of facets of the PLC.\n");
printf(" -z Numbers all output items starting from zero.\n");
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");
printf(" -B Suppresses output of boundary information.\n");
printf(" -N Suppresses output of .node file.\n");
printf(" -E Suppresses output of .ele file.\n");
printf(" -F Suppresses output of .face and .edge file.\n");
printf(" -I Suppresses mesh iteration numbers.\n");
printf(" -C Checks the consistency of the final mesh.\n");
printf(" -Q Quiet: No terminal output except errors.\n");
printf(" -V Verbose: Detailed information, more terminal output.\n");
printf(" -h Help: A brief instruction for using TetGen.\n");
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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("\n");
printf("What Can TetGen Do?\n");
printf("\n");
printf(" TetGen generates Delaunay tetrahedralizations, constrained\n");
printf(" Delaunay tetrahedralizations, and quality tetrahedral meshes.\n");
printf("\n");
printf("Command Line Syntax:\n");
printf("\n");
printf(" Below is the basic command line syntax of TetGen with a list of ");
printf("short\n");
printf(" descriptions. Underscores indicate that numbers may optionally\n");
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(" 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("manual.\n");
printf("\n");
syntax();
printf("\n");
printf("Examples of How to Use TetGen:\n");
printf("\n");
printf(" \'tetgen object\' reads vertices from object.node, and writes ");
printf("their\n Delaunay tetrahedralization to object.1.node, ");
printf("object.1.ele\n (tetrahedra), and object.1.face");
printf(" (convex hull faces).\n");
printf("\n");
printf(" \'tetgen -p object\' reads a PLC from object.poly or object.");
printf("smesh (and\n possibly object.node) and writes its constrained ");
printf("Delaunay\n tetrahedralization to object.1.node, object.1.ele, ");
printf("object.1.face,\n");
printf(" (boundary faces) and object.1.edge (boundary edges).\n");
printf("\n");
printf(" \'tetgen -pq1.414a.1 object\' reads a PLC from object.poly or\n");
printf(" object.smesh (and possibly object.node), generates a mesh ");
printf("whose\n tetrahedra have radius-edge ratio smaller than 1.414 and ");
printf("have volume\n of 0.1 or less, and writes the mesh to ");
printf("object.1.node, object.1.ele,\n object.1.face, and object.1.edge\n");
printf("\n");
printf("Please send bugs/comments to Hang Si \n");
terminatetetgen(NULL, 0);
}
///////////////////////////////////////////////////////////////////////////////
// //
// parse_commandline() Read the command line, identify switches, and set //
// up options and file names. //
// //
// 'argc' and 'argv' are the same parameters passed to the function main() //
// of a C/C++ program. They together represent the command line user invoked //
// from an environment in which TetGen is running. //
// //
///////////////////////////////////////////////////////////////////////////////
bool tetgenbehavior::parse_commandline(int argc, char **argv)
{
int startindex;
int increment;
int meshnumber;
int i, j, k;
char workstring[1024];
// First determine the input style of the switches.
if (argc == 0) {
startindex = 0; // Switches are given without a dash.
argc = 1; // For running the following for-loop once.
commandline[0] = '\0';
} else {
startindex = 1;
strcpy(commandline, argv[0]);
strcat(commandline, " ");
}
for (i = startindex; i < argc; i++) {
// Remember the command line for output.
strcat(commandline, argv[i]);
strcat(commandline, " ");
if (startindex == 1) {
// Is this string a filename?
if (argv[i][0] != '-') {
strncpy(infilename, argv[i], 1024 - 1);
infilename[1024 - 1] = '\0';
continue;
}
}
// Parse the individual switch from the string.
for (j = startindex; argv[i][j] != '\0'; j++) {
if (argv[i][j] == 'p') {
plc = 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';
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++;
}
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;
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++;
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';
mindihedral = (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';
optmaxdihedral = (REAL) strtod(workstring, (char **) NULL);
}
}
} else if (argv[i][j] == 'R') {
coarsen = 1;
if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) {
coarsen_param = (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')) ||
(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';
coarsen_percent = (REAL) strtod(workstring, (char **) NULL);
}
}
} else if (argv[i][j] == 'w') {
weighted = 1;
if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) {
weighted_param = (argv[i][j + 1] - '0');
j++;
}
} else if (argv[i][j] == 'b') {
// -b(brio_threshold/brio_ratio/hilbert_limit/hilbert_order)
brio_hilbert = 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';
brio_threshold = (int) strtol(workstring, (char **) &workstring, 0);
}
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';
brio_ratio = (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] == '.') || (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] == '-')) {
j++;
workstring[k] = argv[i][j];
k++;
}
workstring[k] = '\0';
hilbert_limit = (int) strtol(workstring, (char **) &workstring, 0);
}
}
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] == '.') || (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] == '-')) {
j++;
workstring[k] = argv[i][j];
k++;
}
workstring[k] = '\0';
hilbert_order = (REAL) strtod(workstring, (char **) NULL);
}
}
if (brio_threshold == 0) { // -b0
brio_hilbert = 0; // Turn off BRIO-Hilbert sorting.
}
if (brio_ratio >= 1.0) { // -b/1
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;
} else if (argv[i][j] == 'a') {
if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
(argv[i][j + 1] == '.')) {
fixedvolume = 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';
maxvolume = (REAL) strtod(workstring, (char **) NULL);
} 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;
j++;
}
} else if (argv[i][j] == 'i') {
insertaddpoints = 1;
} else if (argv[i][j] == 'd') {
diagnose = 1;
} else if (argv[i][j] == 'c') {
convex = 1;
} else if (argv[i][j] == 'M') {
nomergefacet = 1;
nomergevertex = 1;
if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '1')) {
nomergefacet = (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] <= '1')) {
nomergevertex = (argv[i][j + 1] - '0');
j++;
}
}
} else if (argv[i][j] == 'X') {
if (argv[i][j + 1] == '1') {
nostaticfilter = 1;
j++;
} else {
noexact = 1;
}
} else if (argv[i][j] == 'z') {
zeroindex = 1;
} 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;
} else if (argv[i][j] == 'J') {
nojettison = 1;
} else if (argv[i][j] == 'B') {
nobound = 1;
} else if (argv[i][j] == 'N') {
nonodewritten = 1;
} else if (argv[i][j] == 'E') {
noelewritten = 1;
} else if (argv[i][j] == 'F') {
nofacewritten = 1;
} else if (argv[i][j] == 'I') {
noiterationnum = 1;
} else if (argv[i][j] == 'S') {
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';
steinerleft = (int) strtol(workstring, (char **) NULL, 0);
}
} else if (argv[i][j] == 'o') {
if (argv[i][j + 1] == '2') {
order = 2;
j++;
}
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';
optmaxdihedral = (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');
j++;
}
if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) {
j++;
if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '7')) {
optscheme = (argv[i][j + 1] - '0');
j++;
}
}
} else if (argv[i][j] == 'T') {
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';
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] == 'V') {
verbose++;
} else if (argv[i][j] == 'x') {
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';
tetrahedraperblock = (int) strtol(workstring, (char **) NULL, 0);
if (tetrahedraperblock > 8188) {
vertexperblock = tetrahedraperblock / 2;
shellfaceperblock = vertexperblock / 2;
} else {
tetrahedraperblock = 8188;
}
}
} else if ((argv[i][j] == 'h') || (argv[i][j] == 'H') ||
(argv[i][j] == '?')) {
usage();
} else {
printf("Warning: Unknown switch -%c.\n", argv[i][j]);
}
}
}
if (startindex == 0) {
// Set a temporary filename for debugging output.
strcpy(infilename, "tetgen-tmpfile");
} else {
if (infilename[0] == '\0') {
// No input file name. Print the syntax and exit.
syntax();
terminatetetgen(NULL, 0);
}
// Recognize the object from file extension if it is available.
if (!strcmp(&infilename[strlen(infilename) - 5], ".node")) {
infilename[strlen(infilename) - 5] = '\0';
object = NODES;
} else if (!strcmp(&infilename[strlen(infilename) - 5], ".poly")) {
infilename[strlen(infilename) - 5] = '\0';
object = POLY;
plc = 1;
} else if (!strcmp(&infilename[strlen(infilename) - 6], ".smesh")) {
infilename[strlen(infilename) - 6] = '\0';
object = POLY;
plc = 1;
} else if (!strcmp(&infilename[strlen(infilename) - 4], ".off")) {
infilename[strlen(infilename) - 4] = '\0';
object = OFF;
plc = 1;
} else if (!strcmp(&infilename[strlen(infilename) - 4], ".ply")) {
infilename[strlen(infilename) - 4] = '\0';
object = PLY;
plc = 1;
} else if (!strcmp(&infilename[strlen(infilename) - 4], ".stl")) {
infilename[strlen(infilename) - 4] = '\0';
object = STL;
plc = 1;
} else if (!strcmp(&infilename[strlen(infilename) - 5], ".mesh")) {
infilename[strlen(infilename) - 5] = '\0';
object = MEDIT;
if (!refine) plc = 1;
} else if (!strcmp(&infilename[strlen(infilename) - 4], ".vtk")) {
infilename[strlen(infilename) - 4] = '\0';
object = VTK;
plc = 1;
} else if (!strcmp(&infilename[strlen(infilename) - 4], ".ele")) {
infilename[strlen(infilename) - 4] = '\0';
object = MESH;
refine = 1;
}
}
if (nobisect && (!plc && !refine)) { // -Y
plc = 1; // Default -p option.
}
if (quality && (!plc && !refine)) { // -q
plc = 1; // Default -p option.
}
if (diagnose && !plc) { // -d
plc = 1;
}
if (refine && !quality) { // -r only
// Reconstruct a mesh, no mesh optimization.
optlevel = 0;
}
if (insertaddpoints && (optlevel == 0)) { // with -i option
optlevel = 2;
}
if (coarsen && (optlevel == 0)) { // with -R option
optlevel = 2;
}
// Detect improper combinations of switches.
if ((refine || plc) && weighted) {
printf("Error: Switches -w cannot use together with -p or -r.\n");
return false;
}
if (convex) { // -c
if (plc && !regionattrib) {
// -A (region attribute) is needed for marking exterior tets (-1).
regionattrib = 1;
}
}
// Note: -A must not used together with -r option.
// Be careful not to add an extra attribute to each element unless the
// input supports it (PLC in, but not refining a preexisting mesh).
if (refine || !plc) {
regionattrib = 0;
}
// Be careful not to allocate space for element area constraints that
// will never be assigned any value (other than the default -1.0).
if (!refine && !plc) {
varvolume = 0;
}
// If '-a' or '-aa' is in use, enable '-q' option too.
if (fixedvolume || varvolume) {
if (quality == 0) {
quality = 1;
if (!plc && !refine) {
plc = 1; // enable -p.
}
}
}
// 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;
}
}
increment = 0;
strcpy(workstring, infilename);
j = 1;
while (workstring[j] != '\0') {
if ((workstring[j] == '.') && (workstring[j + 1] != '\0')) {
increment = j + 1;
}
j++;
}
meshnumber = 0;
if (increment > 0) {
j = increment;
do {
if ((workstring[j] >= '0') && (workstring[j] <= '9')) {
meshnumber = meshnumber * 10 + (int) (workstring[j] - '0');
} else {
increment = 0;
}
j++;
} while (workstring[j] != '\0');
}
if (noiterationnum) {
strcpy(outfilename, infilename);
} else if (increment == 0) {
strcpy(outfilename, infilename);
strcat(outfilename, ".1");
} else {
workstring[increment] = '%';
workstring[increment + 1] = 'd';
workstring[increment + 2] = '\0';
sprintf(outfilename, workstring, meshnumber + 1);
}
// Additional input file name has the end ".a".
strcpy(addinfilename, infilename);
strcat(addinfilename, ".a");
// Background filename has the form "*.b.ele", "*.b.node", ...
strcpy(bgmeshfilename, infilename);
strcat(bgmeshfilename, ".b");
return true;
}
//// ////
//// ////
//// behavior_cxx /////////////////////////////////////////////////////////////
//// mempool_cxx //////////////////////////////////////////////////////////////
//// ////
//// ////
// Initialize fast lookup tables for mesh maniplulation primitives.
int tetgenmesh::bondtbl[12][12] = {{0,},};
int tetgenmesh::enexttbl[12] = {0,};
int tetgenmesh::eprevtbl[12] = {0,};
int tetgenmesh::enextesymtbl[12] = {0,};
int tetgenmesh::eprevesymtbl[12] = {0,};
int tetgenmesh::eorgoppotbl[12] = {0,};
int tetgenmesh::edestoppotbl[12] = {0,};
int tetgenmesh::fsymtbl[12][12] = {{0,},};
int tetgenmesh::facepivot1[12] = {0,};
int tetgenmesh::facepivot2[12][12] = {{0,},};
int tetgenmesh::tsbondtbl[12][6] = {{0,},};
int tetgenmesh::stbondtbl[12][6] = {{0,},};
int tetgenmesh::tspivottbl[12][6] = {{0,},};
int tetgenmesh::stpivottbl[12][6] = {{0,},};
// Table 'esymtbl' takes an directed edge (version) as input, returns the
// inversed edge (version) of it.
int tetgenmesh::esymtbl[12] = {9, 6, 11, 4, 3, 7, 1, 5, 10, 0, 8, 2};
// The following four tables give the 12 permutations of the set {0,1,2,3}.
// An offset 4 is added to each element for a direct access of the points
// in the tetrahedron data structure.
int tetgenmesh:: orgpivot[12] = {7, 7, 5, 5, 6, 4, 4, 6, 5, 6, 7, 4};
int tetgenmesh::destpivot[12] = {6, 4, 4, 6, 5, 6, 7, 4, 7, 7, 5, 5};
int tetgenmesh::apexpivot[12] = {5, 6, 7, 4, 7, 7, 5, 5, 6, 4, 4, 6};
int tetgenmesh::oppopivot[12] = {4, 5, 6, 7, 4, 5, 6, 7, 4, 5, 6, 7};
// The twelve versions correspond to six undirected edges. The following two
// tables map a version to an undirected edge and vice versa.
int tetgenmesh::ver2edge[12] = {0, 1, 2, 3, 3, 5, 1, 5, 4, 0, 4, 2};
int tetgenmesh::edge2ver[ 6] = {0, 1, 2, 3, 8, 5};
// Edge versions whose apex or opposite may be dummypoint.
int tetgenmesh::epivot[12] = {4, 5, 2, 11, 4, 5, 2, 11, 4, 5, 2, 11};
// Table 'snextpivot' takes an edge version as input, returns the next edge
// version in the same edge ring.
int tetgenmesh::snextpivot[6] = {2, 5, 4, 1, 0, 3};
// The following three tables give the 6 permutations of the set {0,1,2}.
// An offset 3 is added to each element for a direct access of the points
// in the triangle data structure.
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. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::inittables()
{
int i, j;
// i = t1.ver; j = t2.ver;
for (i = 0; i < 12; i++) {
for (j = 0; j < 12; j++) {
bondtbl[i][j] = (j & 3) + (((i & 12) + (j & 12)) % 12);
}
}
// i = t1.ver; j = t2.ver
for (i = 0; i < 12; i++) {
for (j = 0; j < 12; j++) {
fsymtbl[i][j] = (j + 12 - (i & 12)) % 12;
}
}
for (i = 0; i < 12; i++) {
facepivot1[i] = (esymtbl[i] & 3);
}
for (i = 0; i < 12; i++) {
for (j = 0; j < 12; j++) {
facepivot2[i][j] = fsymtbl[esymtbl[i]][j];
}
}
for (i = 0; i < 12; i++) {
enexttbl[i] = (i + 4) % 12;
eprevtbl[i] = (i + 8) % 12;
}
for (i = 0; i < 12; i++) {
enextesymtbl[i] = esymtbl[enexttbl[i]];
eprevesymtbl[i] = esymtbl[eprevtbl[i]];
}
for (i = 0; i < 12; i++) {
eorgoppotbl [i] = eprevtbl[esymtbl[enexttbl[i]]];
edestoppotbl[i] = enexttbl[esymtbl[eprevtbl[i]]];
}
int soffset, toffset;
// i = t.ver, j = s.shver
for (i = 0; i < 12; i++) {
for (j = 0; j < 6; j++) {
if ((j & 1) == 0) {
soffset = (6 - ((i & 12) >> 1)) % 6;
toffset = (12 - ((j & 6) << 1)) % 12;
} else {
soffset = (i & 12) >> 1;
toffset = (j & 6) << 1;
}
tsbondtbl[i][j] = (j & 1) + (((j & 6) + soffset) % 6);
stbondtbl[i][j] = (i & 3) + (((i & 12) + toffset) % 12);
}
}
// i = t.ver, j = s.shver
for (i = 0; i < 12; i++) {
for (j = 0; j < 6; j++) {
if ((j & 1) == 0) {
soffset = (i & 12) >> 1;
toffset = (j & 6) << 1;
} else {
soffset = (6 - ((i & 12) >> 1)) % 6;
toffset = (12 - ((j & 6) << 1)) % 12;
}
tspivottbl[i][j] = (j & 1) + (((j & 6) + soffset) % 6);
stpivottbl[i][j] = (i & 3) + (((i & 12) + toffset) % 12);
}
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::arraypool::poolinit(int sizeofobject, int log2objperblk)
{
// Each object must be at least one byte long.
objectbytes = sizeofobject > 1 ? sizeofobject : 1;
log2objectsperblock = log2objperblk;
// Compute the number of objects in each block.
objectsperblock = ((int) 1) << log2objectsperblock;
objectsperblockmark = objectsperblock - 1;
// No memory has been allocated.
totalmemory = 0l;
// The top array has not been allocated yet.
toparray = (char **) NULL;
toparraylen = 0;
// Ready all indices to be allocated.
restart();
}
///////////////////////////////////////////////////////////////////////////////
// //
// arraypool() The constructor and destructor. //
// //
///////////////////////////////////////////////////////////////////////////////
tetgenmesh::arraypool::arraypool(int sizeofobject, int log2objperblk)
{
poolinit(sizeofobject, log2objperblk);
}
tetgenmesh::arraypool::~arraypool()
{
int i;
// Has anything been allocated at all?
if (toparray != (char **) NULL) {
// Walk through the top array.
for (i = 0; i < toparraylen; i++) {
// Check every pointer; NULLs may be scattered randomly.
if (toparray[i] != (char *) NULL) {
// Free an allocated block.
free((void *) toparray[i]);
}
}
// Free the top array.
free((void *) toparray);
}
// The top array is no longer allocated.
toparray = (char **) NULL;
toparraylen = 0;
objects = 0;
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). //
// //
///////////////////////////////////////////////////////////////////////////////
char* tetgenmesh::arraypool::getblock(int objectindex)
{
char **newarray;
char *block;
int newsize;
int topindex;
int i;
// Compute the index in the top array (upper bits).
topindex = objectindex >> log2objectsperblock;
// Does the top array need to be allocated or resized?
if (toparray == (char **) NULL) {
// Allocate the top array big enough to hold 'topindex', and NULL out
// its contents.
newsize = topindex + 128;
toparray = (char **) malloc((size_t) (newsize * sizeof(char *)));
toparraylen = newsize;
for (i = 0; i < newsize; i++) {
toparray[i] = (char *) NULL;
}
// Account for the memory.
totalmemory = newsize * (uintptr_t) sizeof(char *);
} else if (topindex >= toparraylen) {
// Resize the top array, making sure it holds 'topindex'.
newsize = 3 * toparraylen;
if (topindex >= newsize) {
newsize = topindex + 128;
}
// Allocate the new array, copy the contents, NULL out the rest, and
// free the old array.
newarray = (char **) malloc((size_t) (newsize * sizeof(char *)));
for (i = 0; i < toparraylen; i++) {
newarray[i] = toparray[i];
}
for (i = toparraylen; i < newsize; i++) {
newarray[i] = (char *) NULL;
}
free(toparray);
// Account for the memory.
totalmemory += (newsize - toparraylen) * sizeof(char *);
toparray = newarray;
toparraylen = newsize;
}
// Find the block, or learn that it hasn't been allocated yet.
block = toparray[topindex];
if (block == (char *) NULL) {
// Allocate a block at this index.
block = (char *) malloc((size_t) (objectsperblock * objectbytes));
toparray[topindex] = block;
// Account for the memory.
totalmemory += objectsperblock * objectbytes;
}
// Return a pointer to the block.
return block;
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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)
{
char *block;
int topindex;
// Has the top array been allocated yet?
if (toparray == (char **) NULL) {
return (void *) NULL;
}
// Compute the index in the top array (upper bits).
topindex = objectindex >> log2objectsperblock;
// Does the top index fit in the top array?
if (topindex >= toparraylen) {
return (void *) NULL;
}
// Find the block, or learn that it hasn't been allocated yet.
block = toparray[topindex];
if (block == (char *) NULL) {
return (void *) NULL;
}
// Compute a pointer to the object with the given index. Note that
// 'objectsperblock' is a power of two, so the & operation is a bit mask
// that preserves the lower bits.
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). //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::arraypool::newindex(void **newptr)
{
// Allocate an object at index 'firstvirgin'.
int newindex = objects;
*newptr = (void *) (getblock(objects) +
(objects & (objectsperblock - 1)) * objectbytes);
objects++;
return newindex;
}
///////////////////////////////////////////////////////////////////////////////
// //
// memorypool() The constructors of memorypool. //
// //
///////////////////////////////////////////////////////////////////////////////
tetgenmesh::memorypool::memorypool()
{
firstblock = nowblock = (void **) NULL;
nextitem = (void *) NULL;
deaditemstack = (void *) NULL;
pathblock = (void **) NULL;
pathitem = (void *) NULL;
alignbytes = 0;
itembytes = itemwords = 0;
itemsperblock = 0;
items = maxitems = 0l;
unallocateditems = 0;
pathitemsleft = 0;
}
tetgenmesh::memorypool::memorypool(int bytecount, int itemcount, int wsize,
int alignment)
{
poolinit(bytecount, itemcount, wsize, alignment);
}
///////////////////////////////////////////////////////////////////////////////
// //
// ~memorypool() Free to the operating system all memory taken by a pool. //
// //
///////////////////////////////////////////////////////////////////////////////
tetgenmesh::memorypool::~memorypool()
{
while (firstblock != (void **) NULL) {
nowblock = (void **) *(firstblock);
free(firstblock);
firstblock = nowblock;
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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)
{
// Find the proper alignment, which must be at least as large as:
// - The parameter `alignment'.
// - The primary word type, to avoid unaligned accesses.
// - sizeof(void *), so the stack of dead items can be maintained
// without unaligned accesses.
if (alignment > wordsize) {
alignbytes = alignment;
} else {
alignbytes = wordsize;
}
if ((int) sizeof(void *) > alignbytes) {
alignbytes = (int) sizeof(void *);
}
itemwords = ((bytecount + alignbytes - 1) / alignbytes)
* (alignbytes / wordsize);
itembytes = itemwords * wordsize;
itemsperblock = itemcount;
// Allocate a block of items. Space for `itemsperblock' items and one
// pointer (to point to the next block) are allocated, as well as space
// to ensure alignment of the items.
firstblock = (void **) malloc(itemsperblock * itembytes + sizeof(void *)
+ alignbytes);
if (firstblock == (void **) NULL) {
terminatetetgen(NULL, 1);
}
// Set the next block pointer to NULL.
*(firstblock) = (void *) NULL;
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. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::memorypool::restart()
{
uintptr_t alignptr;
items = 0;
maxitems = 0;
// Set the currently active block.
nowblock = firstblock;
// Find the first item in the pool. Increment by the size of (void *).
alignptr = (uintptr_t) (nowblock + 1);
// Align the item on an `alignbytes'-byte boundary.
nextitem = (void *)
(alignptr + (uintptr_t) alignbytes -
(alignptr % (uintptr_t) alignbytes));
// There are lots of unallocated items left in this block.
unallocateditems = itemsperblock;
// The stack of deallocated items is empty.
deaditemstack = (void *) NULL;
}
///////////////////////////////////////////////////////////////////////////////
// //
// alloc() Allocate space for an item. //
// //
///////////////////////////////////////////////////////////////////////////////
void* tetgenmesh::memorypool::alloc()
{
void *newitem;
void **newblock;
uintptr_t alignptr;
// First check the linked list of dead items. If the list is not
// empty, allocate an item from the list rather than a fresh one.
if (deaditemstack != (void *) NULL) {
newitem = deaditemstack; // Take first item in list.
deaditemstack = * (void **) deaditemstack;
} else {
// Check if there are any free items left in the current block.
if (unallocateditems == 0) {
// Check if another block must be allocated.
if (*nowblock == (void *) NULL) {
// Allocate a new block of items, pointed to by the previous block.
newblock = (void **) malloc(itemsperblock * itembytes + sizeof(void *)
+ alignbytes);
if (newblock == (void **) NULL) {
terminatetetgen(NULL, 1);
}
*nowblock = (void *) newblock;
// The next block pointer is NULL.
*newblock = (void *) NULL;
}
// Move to the new block.
nowblock = (void **) *nowblock;
// Find the first item in the block.
// Increment by the size of (void *).
alignptr = (uintptr_t) (nowblock + 1);
// Align the item on an `alignbytes'-byte boundary.
nextitem = (void *)
(alignptr + (uintptr_t) alignbytes -
(alignptr % (uintptr_t) alignbytes));
// There are lots of unallocated items left in this block.
unallocateditems = itemsperblock;
}
// Allocate a new item.
newitem = nextitem;
// Advance `nextitem' pointer to next free item in block.
nextitem = (void *) ((uintptr_t) nextitem + itembytes);
unallocateditems--;
maxitems++;
}
items++;
return newitem;
}
///////////////////////////////////////////////////////////////////////////////
// //
// dealloc() Deallocate space for an item. //
// //
// The deallocated space is stored in a queue for later reuse. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::memorypool::dealloc(void *dyingitem)
{
// Push freshly killed item onto stack.
*((void **) dyingitem) = deaditemstack;
deaditemstack = dyingitem;
items--;
}
///////////////////////////////////////////////////////////////////////////////
// //
// traversalinit() Prepare to traverse the entire list of items. //
// //
// This routine is used in conjunction with traverse(). //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::memorypool::traversalinit()
{
uintptr_t alignptr;
// Begin the traversal in the first block.
pathblock = firstblock;
// Find the first item in the block. Increment by the size of (void *).
alignptr = (uintptr_t) (pathblock + 1);
// Align with item on an `alignbytes'-byte boundary.
pathitem = (void *)
(alignptr + (uintptr_t) alignbytes -
(alignptr % (uintptr_t) alignbytes));
// Set the number of items left in the current block.
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. //
// //
///////////////////////////////////////////////////////////////////////////////
void* tetgenmesh::memorypool::traverse()
{
void *newitem;
uintptr_t alignptr;
// Stop upon exhausting the list of items.
if (pathitem == nextitem) {
return (void *) NULL;
}
// Check whether any untraversed items remain in the current block.
if (pathitemsleft == 0) {
// Find the next block.
pathblock = (void **) *pathblock;
// Find the first item in the block. Increment by the size of (void *).
alignptr = (uintptr_t) (pathblock + 1);
// Align with item on an `alignbytes'-byte boundary.
pathitem = (void *)
(alignptr + (uintptr_t) alignbytes -
(alignptr % (uintptr_t) alignbytes));
// Set the number of items left in the current block.
pathitemsleft = itemsperblock;
}
newitem = pathitem;
// Find the next item in the block.
pathitem = (void *) ((uintptr_t) pathitem + itembytes);
pathitemsleft--;
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]'. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::makeindex2pointmap(point*& idx2verlist)
{
point pointloop;
int idx;
if (b->verbose > 1) {
printf(" Constructing mapping from indices to points.\n");
}
idx2verlist = new point[points->items + 1];
points->traversalinit();
pointloop = pointtraverse();
idx = in->firstnumber;
while (pointloop != (point) NULL) {
idx2verlist[idx++] = pointloop;
pointloop = pointtraverse();
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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)
{
face shloop;
int i, j, k;
if (b->verbose > 1) {
printf(" Making a map from points to subfaces.\n");
}
// Initialize 'idx2faclist'.
idx2faclist = new int[points->items + 1];
for (i = 0; i < points->items + 1; i++) idx2faclist[i] = 0;
// Loop all subfaces, counter the number of subfaces incident at a vertex.
pool->traversalinit();
shloop.sh = shellfacetraverse(pool);
while (shloop.sh != (shellface *) NULL) {
// Increment the number of incident subfaces for each vertex.
j = pointmark((point) shloop.sh[3]) - in->firstnumber;
idx2faclist[j]++;
j = pointmark((point) shloop.sh[4]) - in->firstnumber;
idx2faclist[j]++;
// Skip the third corner if it is a segment.
if (shloop.sh[5] != NULL) {
j = pointmark((point) shloop.sh[5]) - in->firstnumber;
idx2faclist[j]++;
}
shloop.sh = shellfacetraverse(pool);
}
// Calculate the total length of array 'facperverlist'.
j = idx2faclist[0];
idx2faclist[0] = 0; // Array starts from 0 element.
for (i = 0; i < points->items; i++) {
k = idx2faclist[i + 1];
idx2faclist[i + 1] = idx2faclist[i] + j;
j = k;
}
// The total length is in the last unit of idx2faclist.
facperverlist = new face[idx2faclist[i]];
// Loop all subfaces again, remember the subfaces at each vertex.
pool->traversalinit();
shloop.sh = shellfacetraverse(pool);
while (shloop.sh != (shellface *) NULL) {
j = pointmark((point) shloop.sh[3]) - in->firstnumber;
shloop.shver = 0; // save the origin.
facperverlist[idx2faclist[j]] = shloop;
idx2faclist[j]++;
// Is it a subface or a subsegment?
if (shloop.sh[5] != NULL) {
j = pointmark((point) shloop.sh[4]) - in->firstnumber;
shloop.shver = 2; // save the origin.
facperverlist[idx2faclist[j]] = shloop;
idx2faclist[j]++;
j = pointmark((point) shloop.sh[5]) - in->firstnumber;
shloop.shver = 4; // save the origin.
facperverlist[idx2faclist[j]] = shloop;
idx2faclist[j]++;
} else {
j = pointmark((point) shloop.sh[4]) - in->firstnumber;
shloop.shver = 1; // save the origin.
facperverlist[idx2faclist[j]] = shloop;
idx2faclist[j]++;
}
shloop.sh = shellfacetraverse(pool);
}
// Contents in 'idx2faclist' are shifted, now shift them back.
for (i = points->items - 1; i >= 0; i--) {
idx2faclist[i + 1] = idx2faclist[i];
}
idx2faclist[0] = 0;
}
///////////////////////////////////////////////////////////////////////////////
// //
// tetrahedrondealloc() Deallocate space for a tet., marking it dead. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::tetrahedrondealloc(tetrahedron *dyingtetrahedron)
{
// Set tetrahedron's vertices to NULL. This makes it possible to detect
// dead tetrahedra when traversing the list of all tetrahedra.
dyingtetrahedron[4] = (tetrahedron) NULL;
// Dealloc the space to subfaces/subsegments.
if (dyingtetrahedron[8] != NULL) {
tet2segpool->dealloc((shellface *) dyingtetrahedron[8]);
}
if (dyingtetrahedron[9] != NULL) {
tet2subpool->dealloc((shellface *) dyingtetrahedron[9]);
}
tetrahedrons->dealloc((void *) dyingtetrahedron);
}
///////////////////////////////////////////////////////////////////////////////
// //
// tetrahedrontraverse() Traverse the tetrahedra, skipping dead ones. //
// //
///////////////////////////////////////////////////////////////////////////////
tetgenmesh::tetrahedron* tetgenmesh::tetrahedrontraverse()
{
tetrahedron *newtetrahedron;
do {
newtetrahedron = (tetrahedron *) tetrahedrons->traverse();
if (newtetrahedron == (tetrahedron *) NULL) {
return (tetrahedron *) NULL;
}
} while ((newtetrahedron[4] == (tetrahedron) NULL) ||
((point) newtetrahedron[7] == dummypoint));
return newtetrahedron;
}
tetgenmesh::tetrahedron* tetgenmesh::alltetrahedrontraverse()
{
tetrahedron *newtetrahedron;
do {
newtetrahedron = (tetrahedron *) tetrahedrons->traverse();
if (newtetrahedron == (tetrahedron *) NULL) {
return (tetrahedron *) NULL;
}
} while (newtetrahedron[4] == (tetrahedron) NULL); // Skip dead ones.
return newtetrahedron;
}
///////////////////////////////////////////////////////////////////////////////
// //
// shellfacedealloc() Deallocate space for a shellface, marking it dead. //
// Used both for dealloc a subface and subsegment. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::shellfacedealloc(memorypool *pool, shellface *dyingsh)
{
// Set shellface's vertices to NULL. This makes it possible to detect dead
// shellfaces when traversing the list of all shellfaces.
dyingsh[3] = (shellface) NULL;
pool->dealloc((void *) dyingsh);
}
///////////////////////////////////////////////////////////////////////////////
// //
// shellfacetraverse() Traverse the subfaces, skipping dead ones. Used //
// for both subfaces and subsegments pool traverse. //
// //
///////////////////////////////////////////////////////////////////////////////
tetgenmesh::shellface* tetgenmesh::shellfacetraverse(memorypool *pool)
{
shellface *newshellface;
do {
newshellface = (shellface *) pool->traverse();
if (newshellface == (shellface *) NULL) {
return (shellface *) NULL;
}
} while (newshellface[3] == (shellface) NULL); // Skip dead ones.
return newshellface;
}
///////////////////////////////////////////////////////////////////////////////
// //
// pointdealloc() Deallocate space for a point, marking it dead. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::pointdealloc(point dyingpoint)
{
// Mark the point as dead. This makes it possible to detect dead points
// when traversing the list of all points.
setpointtype(dyingpoint, DEADVERTEX);
points->dealloc((void *) dyingpoint);
}
///////////////////////////////////////////////////////////////////////////////
// //
// pointtraverse() Traverse the points, skipping dead ones. //
// //
///////////////////////////////////////////////////////////////////////////////
tetgenmesh::point tetgenmesh::pointtraverse()
{
point newpoint;
do {
newpoint = (point) points->traverse();
if (newpoint == (point) NULL) {
return (point) NULL;
}
} while (pointtype(newpoint) == DEADVERTEX); // Skip dead ones.
return newpoint;
}
///////////////////////////////////////////////////////////////////////////////
// //
// maketetrahedron() Create a new tetrahedron. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::maketetrahedron(triface *newtet)
{
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;
// Four NULL vertices.
newtet->tet[4] = NULL;
newtet->tet[5] = NULL;
newtet->tet[6] = NULL;
newtet->tet[7] = NULL;
// No attached segments and subfaces yet.
newtet->tet[8] = NULL;
newtet->tet[9] = NULL;
// 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)
{
newface->sh = (shellface *) pool->alloc();
// No adjointing subfaces.
newface->sh[0] = NULL;
newface->sh[1] = NULL;
newface->sh[2] = NULL;
// Three NULL vertices.
newface->sh[3] = NULL;
newface->sh[4] = NULL;
newface->sh[5] = NULL;
// No adjoining subsegments.
newface->sh[6] = NULL;
newface->sh[7] = NULL;
newface->sh[8] = NULL;
// No adjoining tetrahedra.
newface->sh[9] = NULL;
newface->sh[10] = NULL;
if (checkconstraints) {
// Initialize the maximum area bound.
setareabound(*newface, 0.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. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::makepoint(point* pnewpoint, enum verttype vtype)
{
int i;
*pnewpoint = (point) points->alloc();
// Initialize the point attributes.
for (i = 0; i < numpointattrib; i++) {
(*pnewpoint)[3 + i] = 0.0;
}
// Initialize the metric tensor.
for (i = 0; i < sizeoftensor; i++) {
(*pnewpoint)[pointmtrindex + i] = 0.0;
}
setpoint2tet(*pnewpoint, NULL);
setpoint2ppt(*pnewpoint, NULL);
if (b->plc || b->refine) {
// Initialize the point-to-simplex field.
setpoint2sh(*pnewpoint, NULL);
if (b->metric && (bgm != NULL)) {
setpoint2bgmtet(*pnewpoint, NULL);
}
}
// Initialize the point marker (starting from in->firstnumber).
setpointmark(*pnewpoint, (int) (points->items) - (!in->firstnumber));
// Clear all flags.
((int *) (*pnewpoint))[pointmarkindex + 1] = 0;
// Initialize (set) the point type.
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. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::initializepools()
{
int pointsize = 0, elesize = 0, shsize = 0;
int i;
if (b->verbose) {
printf(" Initializing memorypools.\n");
printf(" tetrahedron per block: %d.\n", b->tetrahedraperblock);
}
inittables();
// There are three input point lists available, which are in, addin,
// and bgm->in. These point lists may have different number of
// attributes. Decide the maximum number.
numpointattrib = in->numberofpointattributes;
if (bgm != NULL) {
if (bgm->in->numberofpointattributes > numpointattrib) {
numpointattrib = bgm->in->numberofpointattributes;
}
}
if (addin != NULL) {
if (addin->numberofpointattributes > numpointattrib) {
numpointattrib = addin->numberofpointattributes;
}
}
if (b->weighted || b->flipinsert) { // -w or -L.
// The internal number of point attribute needs to be at least 1
// (for storing point weights).
if (numpointattrib == 0) {
numpointattrib = 1;
}
}
// Default varconstraint = 0;
if (in->segmentconstraintlist || in->facetconstraintlist) {
checkconstraints = 1;
}
if (b->plc || b->refine) {
// Save the insertion radius for Steiner points if boundaries
// are allowed be split.
if (!b->nobisect || checkconstraints) {
useinsertradius = 1;
}
}
// The index within each point at which its metric tensor is found.
// Each vertex has three coordinates.
if (b->psc) {
// '-s' option (PSC), the u,v coordinates are provided.
pointmtrindex = 5 + numpointattrib;
// The index within each point at which its u, v coordinates are found.
// Comment: They are saved after the list of point attributes.
pointparamindex = pointmtrindex - 2;
} else {
pointmtrindex = 3 + numpointattrib;
}
// For '-m' option. A tensor field is provided (*.mtr or *.b.mtr file).
if (b->metric) {
// Decide the size (1, 3, or 6) of the metric tensor.
if (bgm != (tetgenmesh *) NULL) {
// A background mesh is allocated. It may not exist though.
sizeoftensor = (bgm->in != (tetgenio *) NULL) ?
bgm->in->numberofpointmtrs : in->numberofpointmtrs;
} else {
// No given background mesh - Itself is a background mesh.
sizeoftensor = in->numberofpointmtrs;
}
// Make sure sizeoftensor is at least 1.
sizeoftensor = (sizeoftensor > 0) ? sizeoftensor : 1;
} else {
// For '-q' option. Make sure to have space for saving a scalar value.
sizeoftensor = b->quality ? 1 : 0;
}
if (useinsertradius) {
// Increase a space (REAL) for saving point insertion radius, it is
// saved directly after the metric.
sizeoftensor++;
}
// 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) {
// 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()).
// - a pointer to a subface or segment, read by point2sh();
if (b->metric && (bgm != (tetgenmesh *) NULL)) {
// Increase one pointer into the background mesh, point2bgmtet().
pointsize = (point2simindex + 4) * sizeof(tetrahedron);
} else {
pointsize = (point2simindex + 3) * sizeof(tetrahedron);
}
} else {
// Increase the point size by two pointer, which are:
// - a pointer to a tet, read by point2tet();
// - a pointer to a parent point, read by point2ppt()). -- Used by btree.
pointsize = (point2simindex + 2) * sizeof(tetrahedron);
}
// The index within each point at which the boundary marker is found,
// Ensure the point marker is aligned to a sizeof(int)-byte address.
pointmarkindex = (pointsize + sizeof(int) - 1) / sizeof(int);
// 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);
// Initialize the pool of vertices.
points = new memorypool(pointsize, b->vertexperblock, sizeof(REAL), 0);
if (b->verbose) {
printf(" Size of a point: %d bytes.\n", points->itembytes);
}
// Initialize the infinite vertex.
dummypoint = (point) new char[pointsize];
// Initialize all fields of this point.
dummypoint[0] = 0.0;
dummypoint[1] = 0.0;
dummypoint[2] = 0.0;
for (i = 0; i < numpointattrib; i++) {
dummypoint[3 + i] = 0.0;
}
// Initialize the metric tensor.
for (i = 0; i < sizeoftensor; i++) {
dummypoint[pointmtrindex + i] = 0.0;
}
setpoint2tet(dummypoint, NULL);
setpoint2ppt(dummypoint, NULL);
if (b->plc || b->psc || b->refine) {
// Initialize the point-to-simplex field.
setpoint2sh(dummypoint, NULL);
if (b->metric && (bgm != NULL)) {
setpoint2bgmtet(dummypoint, NULL);
}
}
// Initialize the point marker (starting from in->firstnumber).
setpointmark(dummypoint, -1); // The unique marker for dummypoint.
// Clear all flags.
((int *) (dummypoint))[pointmarkindex + 1] = 0;
// Initialize (set) the point type.
setpointtype(dummypoint, UNUSEDVERTEX); // Does not matter.
// The number of bytes occupied by a tetrahedron is varying by the user-
// specified options. The contents of the first 12 pointers are listed
// in the following table:
// [0] |__ neighbor at f0 __|
// [1] |__ neighbor at f1 __|
// [2] |__ neighbor at f2 __|
// [3] |__ neighbor at f3 __|
// [4] |_____ vertex p0 ____|
// [5] |_____ vertex p1 ____|
// [6] |_____ vertex p2 ____|
// [7] |_____ vertex p3 ____|
// [8] |__ segments array __| (used by -p)
// [9] |__ subfaces array __| (used by -p)
// [10] |_____ reserved _____|
// [11] |___ elem marker ____| (used as an integer)
elesize = 12 * sizeof(tetrahedron);
// 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);
elemmarkerindex = (elesize - sizeof(tetrahedron)) / sizeof(int);
// 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) {
elesize = (volumeboundindex + 1) * sizeof(REAL);
} else if (numelemattrib > 0) {
elesize = volumeboundindex * sizeof(REAL);
}
// Having determined the memory size of an element, initialize the pool.
tetrahedrons = new memorypool(elesize, b->tetrahedraperblock, sizeof(void *),
16);
if (b->verbose) {
printf(" Size of a tetrahedron: %d (%d) bytes.\n", elesize,
tetrahedrons->itembytes);
}
if (b->plc || b->refine) { // if (b->useshelles) {
// The number of bytes occupied by a subface. The list of pointers
// stored in a subface are: three to other subfaces, three to corners,
// three to subsegments, two to tetrahedra.
shsize = 11 * sizeof(shellface);
// The index within each subface at which the maximum area bound is
// found, where the index is measured in REALs.
areaboundindex = (shsize + sizeof(REAL) - 1) / sizeof(REAL);
// If -q switch is in use, increase the number of bytes occupied by
// a subface for saving maximum area bound.
if (checkconstraints) {
shsize = (areaboundindex + 1) * sizeof(REAL);
} else {
shsize = areaboundindex * sizeof(REAL);
}
// The index within subface at which the facet marker is found. Ensure
// 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);
}
// 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
// the least three bits.
subfaces = new memorypool(shsize, b->shellfaceperblock, sizeof(void *), 8);
if (b->verbose) {
printf(" Size of a shellface: %d (%d) bytes.\n", shsize,
subfaces->itembytes);
}
// Initialize the pool of subsegments. The subsegment's record is same
// with subface.
subsegs = new memorypool(shsize, b->shellfaceperblock, sizeof(void *), 8);
// Initialize the pool for tet-subseg connections.
tet2segpool = new memorypool(6 * sizeof(shellface), b->shellfaceperblock,
sizeof(void *), 0);
// Initialize the pool for tet-subface connections.
tet2subpool = new memorypool(4 * sizeof(shellface), b->shellfaceperblock,
sizeof(void *), 0);
// Initialize arraypools for segment & facet recovery.
subsegstack = new arraypool(sizeof(face), 10);
subfacstack = new arraypool(sizeof(face), 10);
subvertstack = new arraypool(sizeof(point), 8);
// Initialize arraypools for surface point insertion/deletion.
caveshlist = new arraypool(sizeof(face), 8);
caveshbdlist = new arraypool(sizeof(face), 8);
cavesegshlist = new arraypool(sizeof(face), 4);
cavetetshlist = new arraypool(sizeof(face), 8);
cavetetseglist = new arraypool(sizeof(face), 8);
caveencshlist = new arraypool(sizeof(face), 8);
caveencseglist = new arraypool(sizeof(face), 8);
}
// Initialize the pools for flips.
flippool = new memorypool(sizeof(badface), 1024, sizeof(void *), 0);
unflipqueue = new arraypool(sizeof(badface), 10);
// Initialize the arraypools for point insertion.
cavetetlist = new arraypool(sizeof(triface), 10);
cavebdrylist = new arraypool(sizeof(triface), 10);
caveoldtetlist = new arraypool(sizeof(triface), 10);
cavetetvertlist = new arraypool(sizeof(point), 10);
}
//// ////
//// ////
//// mempool_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. //
// //
///////////////////////////////////////////////////////////////////////////////
REAL tetgenmesh::insphere_s(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe)
{
REAL sign;
sign = insphere(pa, pb, pc, pd, pe);
if (sign != 0.0) {
return sign;
}
// Symbolic perturbation.
point pt[5], swappt;
REAL oriA, oriB;
int swaps, count;
int n, i;
pt[0] = pa;
pt[1] = pb;
pt[2] = pc;
pt[3] = pd;
pt[4] = pe;
// Sort the five points such that their indices are in the increasing
// order. An optimized bubble sort algorithm is used, i.e., it has
// the worst case O(n^2) runtime, but it is usually much faster.
swaps = 0; // Record the total number of swaps.
n = 5;
do {
count = 0;
n = n - 1;
for (i = 0; i < n; i++) {
if (pointmark(pt[i]) > pointmark(pt[i+1])) {
swappt = pt[i]; pt[i] = pt[i+1]; pt[i+1] = swappt;
count++;
}
}
swaps += count;
} while (count > 0); // Continue if some points are swapped.
oriA = orient3d(pt[1], pt[2], pt[3], pt[4]);
if (oriA != 0.0) {
// Flip the sign if there are odd number of swaps.
if ((swaps % 2) != 0) oriA = -oriA;
return oriA;
}
oriB = -orient3d(pt[0], pt[2], pt[3], pt[4]);
assert(oriB != 0.0); // SELF_CHECK
// 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. //
// //
///////////////////////////////////////////////////////////////////////////////
REAL tetgenmesh::orient4d_s(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe,
REAL aheight, REAL bheight, REAL cheight,
REAL dheight, REAL eheight)
{
REAL sign;
sign = orient4d(pa, pb, pc, pd, pe,
aheight, bheight, cheight, dheight, eheight);
if (sign != 0.0) {
return sign;
}
// Symbolic perturbation.
point pt[5], swappt;
REAL oriA, oriB;
int swaps, count;
int n, i;
pt[0] = pa;
pt[1] = pb;
pt[2] = pc;
pt[3] = pd;
pt[4] = pe;
// Sort the five points such that their indices are in the increasing
// order. An optimized bubble sort algorithm is used, i.e., it has
// the worst case O(n^2) runtime, but it is usually much faster.
swaps = 0; // Record the total number of swaps.
n = 5;
do {
count = 0;
n = n - 1;
for (i = 0; i < n; i++) {
if (pointmark(pt[i]) > pointmark(pt[i+1])) {
swappt = pt[i]; pt[i] = pt[i+1]; pt[i+1] = swappt;
count++;
}
}
swaps += count;
} while (count > 0); // Continue if some points are swapped.
oriA = orient3d(pt[1], pt[2], pt[3], pt[4]);
if (oriA != 0.0) {
// Flip the sign if there are odd number of swaps.
if ((swaps % 2) != 0) oriA = -oriA;
return oriA;
}
oriB = -orient3d(pt[0], pt[2], pt[3], pt[4]);
assert(oriB != 0.0); // SELF_CHECK
// 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). //
// //
///////////////////////////////////////////////////////////////////////////////
#define SETVECTOR3(V, a0, a1, a2) (V)[0] = (a0); (V)[1] = (a1); (V)[2] = (a2)
#define SWAP2(a0, a1, tmp) (tmp) = (a0); (a0) = (a1); (a1) = (tmp)
int tetgenmesh::tri_edge_2d(point A, point B, point C, point P, point Q,
point R, int level, int *types, int *pos)
{
point U[3], V[3]; // The permuted vectors of points.
int pu[3], pv[3]; // The original positions of points.
REAL abovept[3];
REAL sA, sB, sC;
REAL s1, s2, s3, s4;
int z1;
if (R == NULL) {
// Calculate a lift point.
if (1) {
REAL n[3], len;
// Calculate a lift point, saved in dummypoint.
facenormal(A, B, C, n, 1, NULL);
len = sqrt(dot(n, n));
if (len != 0) {
n[0] /= len;
n[1] /= len;
n[2] /= len;
len = distance(A, B);
len += distance(B, C);
len += distance(C, A);
len /= 3.0;
R = abovept; //dummypoint;
R[0] = A[0] + len * n[0];
R[1] = A[1] + len * n[1];
R[2] = A[2] + len * n[2];
} 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
}
}
}
// Test A's, B's, and C's orientations wrt plane PQR.
sA = orient3d(P, Q, R, A);
sB = orient3d(P, Q, R, B);
sC = orient3d(P, Q, R, C);
if (sA < 0) {
if (sB < 0) {
if (sC < 0) { // (---).
return 0;
} else {
if (sC > 0) { // (--+).
// All points are in the right positions.
SETVECTOR3(U, A, B, C); // I3
SETVECTOR3(V, P, Q, R); // I2
SETVECTOR3(pu, 0, 1, 2);
SETVECTOR3(pv, 0, 1, 2);
z1 = 0;
} else { // (--0).
SETVECTOR3(U, A, B, C); // I3
SETVECTOR3(V, P, Q, R); // I2
SETVECTOR3(pu, 0, 1, 2);
SETVECTOR3(pv, 0, 1, 2);
z1 = 1;
}
}
} else {
if (sB > 0) {
if (sC < 0) { // (-+-).
SETVECTOR3(U, C, A, B); // PT = ST
SETVECTOR3(V, P, Q, R); // I2
SETVECTOR3(pu, 2, 0, 1);
SETVECTOR3(pv, 0, 1, 2);
z1 = 0;
} else {
if (sC > 0) { // (-++).
SETVECTOR3(U, B, C, A); // PT = ST x ST
SETVECTOR3(V, Q, P, R); // PL = SL
SETVECTOR3(pu, 1, 2, 0);
SETVECTOR3(pv, 1, 0, 2);
z1 = 0;
} else { // (-+0).
SETVECTOR3(U, C, A, B); // PT = ST
SETVECTOR3(V, P, Q, R); // I2
SETVECTOR3(pu, 2, 0, 1);
SETVECTOR3(pv, 0, 1, 2);
z1 = 2;
}
}
} else {
if (sC < 0) { // (-0-).
SETVECTOR3(U, C, A, B); // PT = ST
SETVECTOR3(V, P, Q, R); // I2
SETVECTOR3(pu, 2, 0, 1);
SETVECTOR3(pv, 0, 1, 2);
z1 = 1;
} else {
if (sC > 0) { // (-0+).
SETVECTOR3(U, B, C, A); // PT = ST x ST
SETVECTOR3(V, Q, P, R); // PL = SL
SETVECTOR3(pu, 1, 2, 0);
SETVECTOR3(pv, 1, 0, 2);
z1 = 2;
} else { // (-00).
SETVECTOR3(U, B, C, A); // PT = ST x ST
SETVECTOR3(V, Q, P, R); // PL = SL
SETVECTOR3(pu, 1, 2, 0);
SETVECTOR3(pv, 1, 0, 2);
z1 = 3;
}
}
}
}
} else {
if (sA > 0) {
if (sB < 0) {
if (sC < 0) { // (+--).
SETVECTOR3(U, B, C, A); // PT = ST x ST
SETVECTOR3(V, P, Q, R); // I2
SETVECTOR3(pu, 1, 2, 0);
SETVECTOR3(pv, 0, 1, 2);
z1 = 0;
} else {
if (sC > 0) { // (+-+).
SETVECTOR3(U, C, A, B); // PT = ST
SETVECTOR3(V, Q, P, R); // PL = SL
SETVECTOR3(pu, 2, 0, 1);
SETVECTOR3(pv, 1, 0, 2);
z1 = 0;
} else { // (+-0).
SETVECTOR3(U, C, A, B); // PT = ST
SETVECTOR3(V, Q, P, R); // PL = SL
SETVECTOR3(pu, 2, 0, 1);
SETVECTOR3(pv, 1, 0, 2);
z1 = 2;
}
}
} else {
if (sB > 0) {
if (sC < 0) { // (++-).
SETVECTOR3(U, A, B, C); // I3
SETVECTOR3(V, Q, P, R); // PL = SL
SETVECTOR3(pu, 0, 1, 2);
SETVECTOR3(pv, 1, 0, 2);
z1 = 0;
} else {
if (sC > 0) { // (+++).
return 0;
} else { // (++0).
SETVECTOR3(U, A, B, C); // I3
SETVECTOR3(V, Q, P, R); // PL = SL
SETVECTOR3(pu, 0, 1, 2);
SETVECTOR3(pv, 1, 0, 2);
z1 = 1;
}
}
} else { // (+0#)
if (sC < 0) { // (+0-).
SETVECTOR3(U, B, C, A); // PT = ST x ST
SETVECTOR3(V, P, Q, R); // I2
SETVECTOR3(pu, 1, 2, 0);
SETVECTOR3(pv, 0, 1, 2);
z1 = 2;
} else {
if (sC > 0) { // (+0+).
SETVECTOR3(U, C, A, B); // PT = ST
SETVECTOR3(V, Q, P, R); // PL = SL
SETVECTOR3(pu, 2, 0, 1);
SETVECTOR3(pv, 1, 0, 2);
z1 = 1;
} else { // (+00).
SETVECTOR3(U, B, C, A); // PT = ST x ST
SETVECTOR3(V, P, Q, R); // I2
SETVECTOR3(pu, 1, 2, 0);
SETVECTOR3(pv, 0, 1, 2);
z1 = 3;
}
}
}
}
} else {
if (sB < 0) {
if (sC < 0) { // (0--).
SETVECTOR3(U, B, C, A); // PT = ST x ST
SETVECTOR3(V, P, Q, R); // I2
SETVECTOR3(pu, 1, 2, 0);
SETVECTOR3(pv, 0, 1, 2);
z1 = 1;
} else {
if (sC > 0) { // (0-+).
SETVECTOR3(U, A, B, C); // I3
SETVECTOR3(V, P, Q, R); // I2
SETVECTOR3(pu, 0, 1, 2);
SETVECTOR3(pv, 0, 1, 2);
z1 = 2;
} else { // (0-0).
SETVECTOR3(U, C, A, B); // PT = ST
SETVECTOR3(V, Q, P, R); // PL = SL
SETVECTOR3(pu, 2, 0, 1);
SETVECTOR3(pv, 1, 0, 2);
z1 = 3;
}
}
} else {
if (sB > 0) {
if (sC < 0) { // (0+-).
SETVECTOR3(U, A, B, C); // I3
SETVECTOR3(V, Q, P, R); // PL = SL
SETVECTOR3(pu, 0, 1, 2);
SETVECTOR3(pv, 1, 0, 2);
z1 = 2;
} else {
if (sC > 0) { // (0++).
SETVECTOR3(U, B, C, A); // PT = ST x ST
SETVECTOR3(V, Q, P, R); // PL = SL
SETVECTOR3(pu, 1, 2, 0);
SETVECTOR3(pv, 1, 0, 2);
z1 = 1;
} else { // (0+0).
SETVECTOR3(U, C, A, B); // PT = ST
SETVECTOR3(V, P, Q, R); // I2
SETVECTOR3(pu, 2, 0, 1);
SETVECTOR3(pv, 0, 1, 2);
z1 = 3;
}
}
} else { // (00#)
if (sC < 0) { // (00-).
SETVECTOR3(U, A, B, C); // I3
SETVECTOR3(V, Q, P, R); // PL = SL
SETVECTOR3(pu, 0, 1, 2);
SETVECTOR3(pv, 1, 0, 2);
z1 = 3;
} else {
if (sC > 0) { // (00+).
SETVECTOR3(U, A, B, C); // I3
SETVECTOR3(V, P, Q, R); // I2
SETVECTOR3(pu, 0, 1, 2);
SETVECTOR3(pv, 0, 1, 2);
z1 = 3;
} else { // (000)
// Not possible unless ABC is degenerate.
// Avoiding compiler warnings.
SETVECTOR3(U, A, B, C); // I3
SETVECTOR3(V, P, Q, R); // I2
SETVECTOR3(pu, 0, 1, 2);
SETVECTOR3(pv, 0, 1, 2);
z1 = 4;
}
}
}
}
}
}
s1 = orient3d(U[0], U[2], R, V[1]); // A, C, R, Q
s2 = orient3d(U[1], U[2], R, V[0]); // B, C, R, P
if (s1 > 0) {
return 0;
}
if (s2 < 0) {
return 0;
}
if (level == 0) {
return 1; // They are intersected.
}
assert(z1 != 4); // SELF_CHECK
if (z1 == 1) {
if (s1 == 0) { // (0###)
// C = Q.
types[0] = (int) SHAREVERT;
pos[0] = pu[2]; // C
pos[1] = pv[1]; // Q
types[1] = (int) DISJOINT;
} else {
if (s2 == 0) { // (#0##)
// C = P.
types[0] = (int) SHAREVERT;
pos[0] = pu[2]; // C
pos[1] = pv[0]; // P
types[1] = (int) DISJOINT;
} else { // (-+##)
// C in [P, Q].
types[0] = (int) ACROSSVERT;
pos[0] = pu[2]; // C
pos[1] = pv[0]; // [P, Q]
types[1] = (int) DISJOINT;
}
}
return 4;
}
s3 = orient3d(U[0], U[2], R, V[0]); // A, C, R, P
s4 = orient3d(U[1], U[2], R, V[1]); // B, C, R, 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;
pos[0] = pu[2]; // [C, A]
pos[1] = pv[0]; // [P, Q]
types[1] = (int) TOUCHFACE;
pos[2] = 3; // [A, B, C]
pos[3] = pv[1]; // Q
} else {
if (s4 == 0) {
// Q = l, [P, Q] contains [k, l] (-++0).
types[0] = (int) ACROSSEDGE;
pos[0] = pu[2]; // [C, A]
pos[1] = pv[0]; // [P, Q]
types[1] = (int) TOUCHEDGE;
pos[2] = pu[1]; // [B, C]
pos[3] = pv[1]; // Q
} else { // s4 < 0
// [P, Q] contains [k, l] (-++-).
types[0] = (int) ACROSSEDGE;
pos[0] = pu[2]; // [C, A]
pos[1] = pv[0]; // [P, Q]
types[1] = (int) ACROSSEDGE;
pos[2] = pu[1]; // [B, C]
pos[3] = pv[0]; // [P, 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;
pos[0] = pu[2]; // [C, A]
pos[1] = pv[0]; // P
types[1] = (int) TOUCHFACE;
pos[2] = 3; // [A, B, C]
pos[3] = pv[1]; // Q
} else {
if (s4 == 0) {
// [P, Q] = [k, l] (-+00).
types[0] = (int) TOUCHEDGE;
pos[0] = pu[2]; // [C, A]
pos[1] = pv[0]; // P
types[1] = (int) TOUCHEDGE;
pos[2] = pu[1]; // [B, C]
pos[3] = pv[1]; // Q
} else {
// P = k, [P, Q] contains [k, l] (-+0-).
types[0] = (int) TOUCHEDGE;
pos[0] = pu[2]; // [C, A]
pos[1] = pv[0]; // P
types[1] = (int) ACROSSEDGE;
pos[2] = pu[1]; // [B, C]
pos[3] = pv[0]; // [P, Q]
}
}
} else { // s3 < 0
if (s2 > 0) {
if (s4 > 0) {
// [P, Q] in [k, l] (-+-+).
types[0] = (int) TOUCHFACE;
pos[0] = 3; // [A, B, C]
pos[1] = pv[0]; // P
types[1] = (int) TOUCHFACE;
pos[2] = 3; // [A, B, C]
pos[3] = pv[1]; // Q
} else {
if (s4 == 0) {
// Q = l, [P, Q] in [k, l] (-+-0).
types[0] = (int) TOUCHFACE;
pos[0] = 3; // [A, B, C]
pos[1] = pv[0]; // P
types[1] = (int) TOUCHEDGE;
pos[2] = pu[1]; // [B, C]
pos[3] = pv[1]; // Q
} else { // s4 < 0
// [P, Q] overlaps [k, l] (-+--).
types[0] = (int) TOUCHFACE;
pos[0] = 3; // [A, B, C]
pos[1] = pv[0]; // P
types[1] = (int) ACROSSEDGE;
pos[2] = pu[1]; // [B, C]
pos[3] = pv[0]; // [P, Q]
}
}
} else { // s2 == 0
// P = l (#0##).
types[0] = (int) TOUCHEDGE;
pos[0] = pu[1]; // [B, C]
pos[1] = pv[0]; // P
types[1] = (int) DISJOINT;
}
}
}
} else { // s1 == 0
// Q = k (0####)
types[0] = (int) TOUCHEDGE;
pos[0] = pu[2]; // [C, A]
pos[1] = pv[1]; // Q
types[1] = (int) DISJOINT;
}
} 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;
pos[0] = pu[0]; // A
pos[1] = pv[0]; // [P, Q]
types[1] = (int) TOUCHFACE;
pos[2] = 3; // [A, B, C]
pos[3] = pv[1]; // Q
} else {
if (s4 == 0) {
// Q = l, [P, Q] contains [A, l] (-++0).
types[0] = (int) ACROSSVERT;
pos[0] = pu[0]; // A
pos[1] = pv[0]; // [P, Q]
types[1] = (int) TOUCHEDGE;
pos[2] = pu[1]; // [B, C]
pos[3] = pv[1]; // Q
} else { // s4 < 0
// [P, Q] contains [A, l] (-++-).
types[0] = (int) ACROSSVERT;
pos[0] = pu[0]; // A
pos[1] = pv[0]; // [P, Q]
types[1] = (int) ACROSSEDGE;
pos[2] = pu[1]; // [B, C]
pos[3] = pv[0]; // [P, 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;
pos[0] = pu[0]; // A
pos[1] = pv[0]; // P
types[1] = (int) TOUCHFACE;
pos[2] = 3; // [A, B, C]
pos[3] = pv[1]; // Q
} else {
if (s4 == 0) {
// [P, Q] = [A, l] (-+00).
types[0] = (int) SHAREVERT;
pos[0] = pu[0]; // A
pos[1] = pv[0]; // P
types[1] = (int) TOUCHEDGE;
pos[2] = pu[1]; // [B, C]
pos[3] = pv[1]; // Q
} else { // s4 < 0
// Q = l, [P, Q] in [A, l] (-+0-).
types[0] = (int) SHAREVERT;
pos[0] = pu[0]; // A
pos[1] = pv[0]; // P
types[1] = (int) ACROSSEDGE;
pos[2] = pu[1]; // [B, C]
pos[3] = pv[0]; // [P, Q]
}
}
} else { // s3 < 0
if (s2 > 0) {
if (s4 > 0) {
// [P, Q] in [A, l] (-+-+).
types[0] = (int) TOUCHFACE;
pos[0] = 3; // [A, B, C]
pos[1] = pv[0]; // P
types[0] = (int) TOUCHFACE;
pos[0] = 3; // [A, B, C]
pos[1] = pv[1]; // Q
} else {
if (s4 == 0) {
// Q = l, [P, Q] in [A, l] (-+-0).
types[0] = (int) TOUCHFACE;
pos[0] = 3; // [A, B, C]
pos[1] = pv[0]; // P
types[0] = (int) TOUCHEDGE;
pos[0] = pu[1]; // [B, C]
pos[1] = pv[1]; // Q
} else { // s4 < 0
// [P, Q] overlaps [A, l] (-+--).
types[0] = (int) TOUCHFACE;
pos[0] = 3; // [A, B, C]
pos[1] = pv[0]; // P
types[0] = (int) ACROSSEDGE;
pos[0] = pu[1]; // [B, C]
pos[1] = pv[0]; // [P, Q]
}
}
} else { // s2 == 0
// P = l (#0##).
types[0] = (int) TOUCHEDGE;
pos[0] = pu[1]; // [B, C]
pos[1] = pv[0]; // P
types[1] = (int) DISJOINT;
}
}
}
} else { // s1 == 0
// Q = A (0###).
types[0] = (int) SHAREVERT;
pos[0] = pu[0]; // A
pos[1] = pv[1]; // Q
types[1] = (int) DISJOINT;
}
} 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;
pos[0] = pu[0]; // A
pos[1] = pv[0]; // [P, Q]
types[1] = (int) TOUCHEDGE;
pos[2] = pu[0]; // [A, B]
pos[3] = pv[1]; // Q
} else {
if (s4 == 0) {
// Q = B, [P, Q] contains [A, B] (-++0).
types[0] = (int) ACROSSVERT;
pos[0] = pu[0]; // A
pos[1] = pv[0]; // [P, Q]
types[1] = (int) SHAREVERT;
pos[2] = pu[1]; // B
pos[3] = pv[1]; // Q
} else { // s4 < 0
// [P, Q] contains [A, B] (-++-).
types[0] = (int) ACROSSVERT;
pos[0] = pu[0]; // A
pos[1] = pv[0]; // [P, Q]
types[1] = (int) ACROSSVERT;
pos[2] = pu[1]; // B
pos[3] = pv[0]; // [P, 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;
pos[0] = pu[0]; // A
pos[1] = pv[0]; // P
types[1] = (int) TOUCHEDGE;
pos[2] = pu[0]; // [A, B]
pos[3] = pv[1]; // Q
} else {
if (s4 == 0) {
// [P, Q] = [A, B] (-+00).
types[0] = (int) SHAREEDGE;
pos[0] = pu[0]; // [A, B]
pos[1] = pv[0]; // [P, Q]
types[1] = (int) DISJOINT;
} else { // s4 < 0
// P= A, [P, Q] in [A, B] (-+0-).
types[0] = (int) SHAREVERT;
pos[0] = pu[0]; // A
pos[1] = pv[0]; // P
types[1] = (int) ACROSSVERT;
pos[2] = pu[1]; // B
pos[3] = pv[0]; // [P, Q]
}
}
} else { // s3 < 0
if (s2 > 0) {
if (s4 > 0) {
// [P, Q] in [A, B] (-+-+).
types[0] = (int) TOUCHEDGE;
pos[0] = pu[0]; // [A, B]
pos[1] = pv[0]; // P
types[1] = (int) TOUCHEDGE;
pos[2] = pu[0]; // [A, B]
pos[3] = pv[1]; // Q
} else {
if (s4 == 0) {
// Q = B, [P, Q] in [A, B] (-+-0).
types[0] = (int) TOUCHEDGE;
pos[0] = pu[0]; // [A, B]
pos[1] = pv[0]; // P
types[1] = (int) SHAREVERT;
pos[2] = pu[1]; // B
pos[3] = pv[1]; // Q
} else { // s4 < 0
// [P, Q] overlaps [A, B] (-+--).
types[0] = (int) TOUCHEDGE;
pos[0] = pu[0]; // [A, B]
pos[1] = pv[0]; // P
types[1] = (int) ACROSSVERT;
pos[2] = pu[1]; // B
pos[3] = pv[0]; // [P, Q]
}
}
} else { // s2 == 0
// P = B (#0##).
types[0] = (int) SHAREVERT;
pos[0] = pu[1]; // B
pos[1] = pv[0]; // P
types[1] = (int) DISJOINT;
}
}
}
} else { // s1 == 0
// Q = A (0###).
types[0] = (int) SHAREVERT;
pos[0] = pu[0]; // A
pos[1] = pv[1]; // Q
types[1] = (int) DISJOINT;
}
}
return 4;
}
int tetgenmesh::tri_edge_tail(point A,point B,point C,point P,point Q,point R,
REAL sP,REAL sQ,int level,int *types,int *pos)
{
point U[3], V[3]; //, Ptmp;
int pu[3], pv[3]; //, itmp;
REAL s1, s2, s3;
int z1;
if (sP < 0) {
if (sQ < 0) { // (--) disjoint
return 0;
} else {
if (sQ > 0) { // (-+)
SETVECTOR3(U, A, B, C);
SETVECTOR3(V, P, Q, R);
SETVECTOR3(pu, 0, 1, 2);
SETVECTOR3(pv, 0, 1, 2);
z1 = 0;
} else { // (-0)
SETVECTOR3(U, A, B, C);
SETVECTOR3(V, P, Q, R);
SETVECTOR3(pu, 0, 1, 2);
SETVECTOR3(pv, 0, 1, 2);
z1 = 1;
}
}
} else {
if (sP > 0) { // (+-)
if (sQ < 0) {
SETVECTOR3(U, A, B, C);
SETVECTOR3(V, Q, P, R); // P and Q are flipped.
SETVECTOR3(pu, 0, 1, 2);
SETVECTOR3(pv, 1, 0, 2);
z1 = 0;
} else {
if (sQ > 0) { // (++) disjoint
return 0;
} else { // (+0)
SETVECTOR3(U, B, A, C); // A and B are flipped.
SETVECTOR3(V, P, Q, R);
SETVECTOR3(pu, 1, 0, 2);
SETVECTOR3(pv, 0, 1, 2);
z1 = 1;
}
}
} else { // sP == 0
if (sQ < 0) { // (0-)
SETVECTOR3(U, A, B, C);
SETVECTOR3(V, Q, P, R); // P and Q are flipped.
SETVECTOR3(pu, 0, 1, 2);
SETVECTOR3(pv, 1, 0, 2);
z1 = 1;
} else {
if (sQ > 0) { // (0+)
SETVECTOR3(U, B, A, C); // A and B are flipped.
SETVECTOR3(V, Q, P, R); // P and Q are flipped.
SETVECTOR3(pu, 1, 0, 2);
SETVECTOR3(pv, 1, 0, 2);
z1 = 1;
} else { // (00)
// A, B, C, P, and Q are coplanar.
z1 = 2;
}
}
}
}
if (z1 == 2) {
// The triangle and the edge are coplanar.
return tri_edge_2d(A, B, C, P, Q, R, level, types, pos);
}
s1 = orient3d(U[0], U[1], V[0], V[1]);
if (s1 < 0) {
return 0;
}
s2 = orient3d(U[1], U[2], V[0], V[1]);
if (s2 < 0) {
return 0;
}
s3 = orient3d(U[2], U[0], V[0], V[1]);
if (s3 < 0) {
return 0;
}
if (level == 0) {
return 1; // The are intersected.
}
types[1] = (int) DISJOINT; // No second intersection point.
if (z1 == 0) {
if (s1 > 0) {
if (s2 > 0) {
if (s3 > 0) { // (+++)
// [P, Q] passes interior of [A, B, C].
types[0] = (int) ACROSSFACE;
pos[0] = 3; // interior of [A, B, C]
pos[1] = 0; // [P, Q]
} else { // s3 == 0 (++0)
// [P, Q] intersects [C, A].
types[0] = (int) ACROSSEDGE;
pos[0] = pu[2]; // [C, A]
pos[1] = 0; // [P, Q]
}
} else { // s2 == 0
if (s3 > 0) { // (+0+)
// [P, Q] intersects [B, C].
types[0] = (int) ACROSSEDGE;
pos[0] = pu[1]; // [B, C]
pos[1] = 0; // [P, Q]
} else { // s3 == 0 (+00)
// [P, Q] passes C.
types[0] = (int) ACROSSVERT;
pos[0] = pu[2]; // C
pos[1] = 0; // [P, Q]
}
}
} else { // s1 == 0
if (s2 > 0) {
if (s3 > 0) { // (0++)
// [P, Q] intersects [A, B].
types[0] = (int) ACROSSEDGE;
pos[0] = pu[0]; // [A, B]
pos[1] = 0; // [P, Q]
} else { // s3 == 0 (0+0)
// [P, Q] passes A.
types[0] = (int) ACROSSVERT;
pos[0] = pu[0]; // A
pos[1] = 0; // [P, Q]
}
} else { // s2 == 0
if (s3 > 0) { // (00+)
// [P, Q] passes B.
types[0] = (int) ACROSSVERT;
pos[0] = pu[1]; // B
pos[1] = 0; // [P, Q]
} else { // s3 == 0 (000)
// Impossible.
assert(0);
}
}
}
} else { // z1 == 1
if (s1 > 0) {
if (s2 > 0) {
if (s3 > 0) { // (+++)
// Q lies in [A, B, C].
types[0] = (int) TOUCHFACE;
pos[0] = 0; // [A, B, C]
pos[1] = pv[1]; // Q
} else { // s3 == 0 (++0)
// Q lies on [C, A].
types[0] = (int) TOUCHEDGE;
pos[0] = pu[2]; // [C, A]
pos[1] = pv[1]; // Q
}
} else { // s2 == 0
if (s3 > 0) { // (+0+)
// Q lies on [B, C].
types[0] = (int) TOUCHEDGE;
pos[0] = pu[1]; // [B, C]
pos[1] = pv[1]; // Q
} else { // s3 == 0 (+00)
// Q = C.
types[0] = (int) SHAREVERT;
pos[0] = pu[2]; // C
pos[1] = pv[1]; // Q
}
}
} else { // s1 == 0
if (s2 > 0) {
if (s3 > 0) { // (0++)
// Q lies on [A, B].
types[0] = (int) TOUCHEDGE;
pos[0] = pu[0]; // [A, B]
pos[1] = pv[1]; // Q
} else { // s3 == 0 (0+0)
// Q = A.
types[0] = (int) SHAREVERT;
pos[0] = pu[0]; // A
pos[1] = pv[1]; // Q
}
} else { // s2 == 0
if (s3 > 0) { // (00+)
// Q = B.
types[0] = (int) SHAREVERT;
pos[0] = pu[1]; // B
pos[1] = pv[1]; // Q
} else { // s3 == 0 (000)
// Impossible.
assert(0);
}
}
}
}
// T and E intersect in a single point.
return 2;
}
int tetgenmesh::tri_edge_test(point A, point B, point C, point P, point Q,
point R, int level, int *types, int *pos)
{
REAL sP, sQ;
// Test the locations of P and Q with respect to ABC.
sP = orient3d(A, B, C, P);
sQ = orient3d(A, B, C, 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. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::tri_edge_inter_tail(REAL* A, REAL* B, REAL* C, REAL* P,
REAL* Q, REAL s_p, REAL s_q)
{
int types[2], pos[4];
int ni; // =0, 2, 4
ni = tri_edge_tail(A, B, C, P, Q, NULL, s_p, s_q, 1, types, pos);
if (ni > 0) {
if (ni == 2) {
// Get the intersection type.
if (types[0] == (int) SHAREVERT) {
return (int) SHAREVERT;
} else {
return (int) INTERSECT;
}
} else if (ni == 4) {
// There may be two intersections.
if (types[0] == (int) SHAREVERT) {
if (types[1] == (int) DISJOINT) {
return (int) SHAREVERT;
} else {
assert(types[1] != (int) SHAREVERT);
return (int) INTERSECT;
}
} else {
if (types[0] == (int) SHAREEDGE) {
return (int) SHAREEDGE;
} else {
return (int) INTERSECT;
}
}
} else {
assert(0);
}
}
return (int) DISJOINT;
}
int tetgenmesh::tri_tri_inter(REAL* A,REAL* B,REAL* C,REAL* O,REAL* P,REAL* Q)
{
REAL s_o, s_p, s_q;
REAL s_a, s_b, s_c;
s_o = orient3d(A, B, C, O);
s_p = orient3d(A, B, C, P);
s_q = orient3d(A, B, C, Q);
if ((s_o * s_p > 0.0) && (s_o * s_q > 0.0)) {
// o, p, q are all in the same halfspace of ABC.
return 0; // DISJOINT;
}
s_a = orient3d(O, P, Q, A);
s_b = orient3d(O, P, Q, B);
s_c = orient3d(O, P, Q, C);
if ((s_a * s_b > 0.0) && (s_a * s_c > 0.0)) {
// a, b, c are all in the same halfspace of OPQ.
return 0; // DISJOINT;
}
int abcop, abcpq, abcqo;
int shareedge = 0;
abcop = tri_edge_inter_tail(A, B, C, O, P, s_o, s_p);
if (abcop == (int) INTERSECT) {
return (int) INTERSECT;
} else if (abcop == (int) SHAREEDGE) {
shareedge++;
}
abcpq = tri_edge_inter_tail(A, B, C, P, Q, s_p, s_q);
if (abcpq == (int) INTERSECT) {
return (int) INTERSECT;
} else if (abcpq == (int) SHAREEDGE) {
shareedge++;
}
abcqo = tri_edge_inter_tail(A, B, C, Q, O, s_q, s_o);
if (abcqo == (int) INTERSECT) {
return (int) INTERSECT;
} else if (abcqo == (int) SHAREEDGE) {
shareedge++;
}
if (shareedge == 3) {
// opq are coincident with abc.
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;
opqab = tri_edge_inter_tail(O, P, Q, A, B, s_a, s_b);
if (opqab == (int) INTERSECT) {
return (int) INTERSECT;
}
opqbc = tri_edge_inter_tail(O, P, Q, B, C, s_b, s_c);
if (opqbc == (int) INTERSECT) {
return (int) INTERSECT;
}
opqca = tri_edge_inter_tail(O, P, Q, C, A, s_c, s_a);
if (opqca == (int) INTERSECT) {
return (int) INTERSECT;
}
// 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;
}
// They are disjoint.
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. //
// //
///////////////////////////////////////////////////////////////////////////////
bool tetgenmesh::lu_decmp(REAL lu[4][4], int n, int* ps, REAL* d, int N)
{
REAL scales[4];
REAL pivot, biggest, mult, tempf;
int pivotindex = 0;
int i, j, k;
*d = 1.0; // No row interchanges yet.
for (i = N; i < n + N; i++) { // For each row.
// Find the largest element in each row for row equilibration
biggest = 0.0;
for (j = N; j < n + N; j++)
if (biggest < (tempf = fabs(lu[i][j])))
biggest = tempf;
if (biggest != 0.0)
scales[i] = 1.0 / biggest;
else {
scales[i] = 0.0;
return false; // Zero row: singular matrix.
}
ps[i] = i; // Initialize pivot sequence.
}
for (k = N; k < n + N - 1; k++) { // For each column.
// Find the largest element in each column to pivot around.
biggest = 0.0;
for (i = k; i < n + N; i++) {
if (biggest < (tempf = fabs(lu[ps[i]][k]) * scales[ps[i]])) {
biggest = tempf;
pivotindex = i;
}
}
if (biggest == 0.0) {
return false; // Zero column: singular matrix.
}
if (pivotindex != k) { // Update pivot sequence.
j = ps[k];
ps[k] = ps[pivotindex];
ps[pivotindex] = j;
*d = -(*d); // ...and change the parity of d.
}
// Pivot, eliminating an extra variable each time
pivot = lu[ps[k]][k];
for (i = k + 1; i < n + N; i++) {
lu[ps[i]][k] = mult = lu[ps[i]][k] / pivot;
if (mult != 0.0) {
for (j = k + 1; j < n + N; j++)
lu[ps[i]][j] -= mult * lu[ps[k]][j];
}
}
}
// (lu[ps[n + N - 1]][n + N - 1] == 0.0) ==> A is singular.
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'. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::lu_solve(REAL lu[4][4], int n, int* ps, REAL* b, int N)
{
int i, j;
REAL X[4], dot;
for (i = N; i < n + N; i++) X[i] = 0.0;
// Vector reduction using U triangular matrix.
for (i = N; i < n + N; i++) {
dot = 0.0;
for (j = N; j < i + N; j++)
dot += lu[ps[i]][j] * X[j];
X[i] = b[ps[i]] - dot;
}
// Back substitution, in L triangular matrix.
for (i = n + N - 1; i >= N; i--) {
dot = 0.0;
for (j = i + 1; j < n + N; j++)
dot += lu[ps[i]][j] * X[j];
X[i] = (X[i] - dot) / lu[ps[i]][i];
}
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]. //
// //
///////////////////////////////////////////////////////////////////////////////
REAL tetgenmesh::incircle3d(point pa, point pb, point pc, point pd)
{
REAL area2[2], n1[3], n2[3], c[3];
REAL sign, r, d;
// Calculate the areas of the two triangles [a, b, c] and [b, a, d].
facenormal(pa, pb, pc, n1, 1, NULL);
area2[0] = dot(n1, n1);
facenormal(pb, pa, pd, n2, 1, NULL);
area2[1] = dot(n2, n2);
if (area2[0] > area2[1]) {
// Choose [a, b, c] as the base triangle.
circumsphere(pa, pb, pc, NULL, c, &r);
d = distance(c, pd);
} else {
// Choose [b, a, d] as the base triangle.
if (area2[1] > 0) {
circumsphere(pb, pa, pd, NULL, c, &r);
d = distance(c, pc);
} else {
// The four points are collinear. This case only happens on the boundary.
return 0; // Return "not inside".
}
}
sign = d - r;
if (fabs(sign) / r < b->epsilon) {
sign = 0;
}
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. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::facenormal(point pa, point pb, point pc, REAL *n, int pivot,
REAL* lav)
{
REAL v1[3], v2[3], v3[3], *pv1, *pv2;
REAL L1, L2, L3;
v1[0] = pb[0] - pa[0]; // edge vector v1: a->b
v1[1] = pb[1] - pa[1];
v1[2] = pb[2] - pa[2];
v2[0] = pa[0] - pc[0]; // edge vector v2: c->a
v2[1] = pa[1] - pc[1];
v2[2] = pa[2] - pc[2];
// Default, normal is calculated by: v1 x (-v2) (see Fig. fnormal).
if (pivot > 0) {
// Choose edge vectors by Burdakov's algorithm.
v3[0] = pc[0] - pb[0]; // edge vector v3: b->c
v3[1] = pc[1] - pb[1];
v3[2] = pc[2] - pb[2];
L1 = dot(v1, v1);
L2 = dot(v2, v2);
L3 = dot(v3, v3);
// Sort the three edge lengths.
if (L1 < L2) {
if (L2 < L3) {
pv1 = v1; pv2 = v2; // n = v1 x (-v2).
} else {
pv1 = v3; pv2 = v1; // n = v3 x (-v1).
}
} else {
if (L1 < L3) {
pv1 = v1; pv2 = v2; // n = v1 x (-v2).
} else {
pv1 = v2; pv2 = v3; // n = v2 x (-v3).
}
}
if (lav) {
// return the average edge length.
*lav = (sqrt(L1) + sqrt(L2) + sqrt(L3)) / 3.0;
}
} else {
pv1 = v1; pv2 = v2; // n = v1 x (-v2).
}
// Calculate the face normal.
cross(pv1, pv2, n);
// Inverse the direction;
n[0] = -n[0];
n[1] = -n[1];
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. //
// //
///////////////////////////////////////////////////////////////////////////////
REAL tetgenmesh::shortdistance(REAL* p, REAL* e1, REAL* e2)
{
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);
v1[0] /= len;
v1[1] /= len;
v1[2] /= len;
l_p = dot(v1, v2);
return sqrt(dot(v2, v2) - l_p * l_p);
}
///////////////////////////////////////////////////////////////////////////////
// //
// triarea() Return the area of a triangle. //
// //
///////////////////////////////////////////////////////////////////////////////
REAL tetgenmesh::triarea(REAL* pa, REAL* pb, REAL* pc)
{
REAL A[4][4];
// Compute the coefficient matrix A (3x3).
A[0][0] = pb[0] - pa[0];
A[0][1] = pb[1] - pa[1];
A[0][2] = pb[2] - pa[2]; // 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)
return 0.5 * sqrt(dot(A[2], A[2])); // The area of [a,b,c].
}
REAL tetgenmesh::orient3dfast(REAL *pa, REAL *pb, REAL *pc, REAL *pd)
{
REAL adx, bdx, cdx;
REAL ady, bdy, cdy;
REAL adz, bdz, cdz;
adx = pa[0] - pd[0];
bdx = pb[0] - pd[0];
cdx = pc[0] - pd[0];
ady = pa[1] - pd[1];
bdy = pb[1] - pd[1];
cdy = pc[1] - pd[1];
adz = pa[2] - pd[2];
bdz = pb[2] - pd[2];
cdz = pc[2] - pd[2];
return adx * (bdy * cdz - bdz * cdy)
+ bdx * (cdy * adz - cdz * ady)
+ 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. //
// //
///////////////////////////////////////////////////////////////////////////////
REAL tetgenmesh::interiorangle(REAL* o, REAL* p1, REAL* p2, REAL* n)
{
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;
assert(lenlen != 0.0);
costheta = dot(v1, v2) / lenlen;
if (costheta > 1.0) {
costheta = 1.0; // Roundoff.
} else if (costheta < -1.0) {
costheta = -1.0; // Roundoff.
}
theta = acos(costheta);
if (n != NULL) {
// Get a point above the face (o, p1, p2);
np[0] = o[0] + n[0];
np[1] = o[1] + n[1];
np[2] = o[2] + n[2];
// Adjust theta (0 - 2 * PI).
ori = orient3d(p1, o, np, p2);
if (ori > 0.0) {
theta = 2 * PI - theta;
}
}
return theta;
}
///////////////////////////////////////////////////////////////////////////////
// //
// projpt2edge() Return the projection point from a point to an edge. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::projpt2edge(REAL* p, REAL* e1, REAL* e2, REAL* prj)
{
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);
v1[0] /= len;
v1[1] /= len;
v1[2] /= len;
l_p = dot(v1, v2);
prj[0] = e1[0] + l_p * v1[0];
prj[1] = e1[1] + l_p * v1[1];
prj[2] = e1[2] + l_p * v1[2];
}
///////////////////////////////////////////////////////////////////////////////
// //
// projpt2face() Return the projection point from a point to a face. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::projpt2face(REAL* p, REAL* f1, REAL* f2, REAL* f3, REAL* prj)
{
REAL fnormal[3], v1[3];
REAL len, dist;
// Get the unit face normal.
facenormal(f1, f2, f3, fnormal, 1, NULL);
len = sqrt(fnormal[0]*fnormal[0] + fnormal[1]*fnormal[1] +
fnormal[2]*fnormal[2]);
fnormal[0] /= len;
fnormal[1] /= len;
fnormal[2] /= len;
// Get the vector v1 = |p - f1|.
v1[0] = p[0] - f1[0];
v1[1] = p[1] - f1[1];
v1[2] = p[2] - f1[2];
// Get the project distance.
dist = dot(fnormal, v1);
// Get the project point.
prj[0] = p[0] - dist * fnormal[0];
prj[1] = p[1] - dist * fnormal[1];
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). //
// //
///////////////////////////////////////////////////////////////////////////////
REAL tetgenmesh::facedihedral(REAL* pa, REAL* pb, REAL* pc1, REAL* pc2)
{
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];
// Compute the coefficient matrix A (3x3).
A[0][0] = pb[0] - pa[0];
A[0][1] = pb[1] - pa[1];
A[0][2] = pb[2] - pa[2];
A[1][0] = pc[0] - pa[0];
A[1][1] = pc[1] - pa[1];
A[1][2] = pc[2] - pa[2];
if (pd != NULL) {
A[2][0] = pd[0] - pa[0];
A[2][1] = pd[1] - pa[1];
A[2][2] = pd[2] - pa[2];
} else {
cross(A[0], A[1], A[2]);
}
// Compute the right hand side vector b (3x1).
rhs[0] = 0.5 * dot(A[0], A[0]);
rhs[1] = 0.5 * dot(A[1], A[1]);
if (pd != NULL) {
rhs[2] = 0.5 * dot(A[2], A[2]);
} else {
rhs[2] = 0.0;
}
// 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)) {
if (radius != (REAL *) NULL) *radius = 0.0;
return false;
}
lu_solve(A, 3, indx, rhs, 0);
if (cent != (REAL *) NULL) {
cent[0] = pa[0] + rhs[0];
cent[1] = pa[1] + rhs[1];
cent[2] = pa[2] + rhs[2];
}
if (radius != (REAL *) NULL) {
*radius = sqrt(rhs[0] * rhs[0] + rhs[1] * rhs[1] + rhs[2] * rhs[2]);
}
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. //
// //
///////////////////////////////////////////////////////////////////////////////
bool tetgenmesh::orthosphere(REAL* pa, REAL* pb, REAL* pc, REAL* pd,
REAL aheight, REAL bheight, REAL cheight,
REAL dheight, REAL* orthocent, REAL* radius)
{
REAL A[4][4], rhs[4], D;
int indx[4];
// Set the coefficient matrix A (4 x 4).
A[0][0] = 1.0; A[0][1] = pa[0]; A[0][2] = pa[1]; A[0][3] = pa[2];
A[1][0] = 1.0; A[1][1] = pb[0]; A[1][2] = pb[1]; A[1][3] = pb[2];
A[2][0] = 1.0; A[2][1] = pc[0]; A[2][2] = pc[1]; A[2][3] = pc[2];
A[3][0] = 1.0; A[3][1] = pd[0]; A[3][2] = pd[1]; A[3][3] = pd[2];
// Set the right hand side vector (4 x 1).
rhs[0] = 0.5 * aheight;
rhs[1] = 0.5 * bheight;
rhs[2] = 0.5 * cheight;
rhs[3] = 0.5 * dheight;
// Solve the 4 by 4 equations use LU decomposition with partial pivoting
// and backward and forward substitute..
if (!lu_decmp(A, 4, indx, &D, 0)) {
if (radius != (REAL *) NULL) *radius = 0.0;
return false;
}
lu_solve(A, 4, indx, rhs, 0);
if (orthocent != (REAL *) NULL) {
orthocent[0] = rhs[1];
orthocent[1] = rhs[2];
orthocent[2] = rhs[3];
}
if (radius != (REAL *) NULL) {
// rhs[0] = - rheight / 2;
// rheight = - 2 * rhs[0];
// = r[0]^2 + r[1]^2 + r[2]^2 - radius^2
// radius^2 = r[0]^2 + r[1]^2 + r[2]^2 -rheight
// = r[0]^2 + r[1]^2 + r[2]^2 + 2 * rhs[0]
*radius = sqrt(rhs[1] * rhs[1] + rhs[2] * rhs[2] + rhs[3] * rhs[3]
+ 2.0 * rhs[0]);
}
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. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::planelineint(REAL* pa, REAL* pb, REAL* pc, REAL* e1, REAL* e2,
REAL* ip, REAL* u)
{
REAL n[3], det, det1;
// 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]);
*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]);
} else {
*u = 0.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)
{
REAL vab[3], vcd[3], vca[3];
REAL vab_vab, vcd_vcd, vab_vcd;
REAL vca_vab, vca_vcd;
REAL det, eps;
int i;
for (i = 0; i < 3; i++) {
vab[i] = B[i] - A[i];
vcd[i] = D[i] - C[i];
vca[i] = A[i] - C[i];
}
vab_vab = dot(vab, vab);
vcd_vcd = dot(vcd, vcd);
vab_vcd = dot(vab, vcd);
det = vab_vab * vcd_vcd - vab_vcd * vab_vcd;
// Round the result.
eps = det / (fabs(vab_vab * vcd_vcd) + fabs(vab_vcd * vab_vcd));
if (eps < b->epsilon) {
return 0;
}
vca_vab = dot(vca, vab);
vca_vcd = dot(vca, vcd);
*tp = (vcd_vcd * (- vca_vab) + vab_vcd * vca_vcd) / det;
*tq = (vab_vcd * (- vca_vab) + vab_vab * vca_vcd) / det;
for (i = 0; i < 3; i++) P[i] = A[i] + (*tp) * vab[i];
for (i = 0; i < 3; i++) Q[i] = C[i] + (*tq) * vcd[i];
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. //
// //
///////////////////////////////////////////////////////////////////////////////
REAL tetgenmesh::tetprismvol(REAL* p0, REAL* p1, REAL* p2, REAL* p3)
{
REAL *p4, *p5, *p6, *p7;
REAL w4, w5, w6, w7;
REAL vol[4];
p4 = p0;
p5 = p1;
p6 = p2;
p7 = p3;
// TO DO: these weights can be pre-calculated!
w4 = dot(p0, p0);
w5 = dot(p1, p1);
w6 = dot(p2, p2);
w7 = dot(p3, p3);
// Calculate the volume of the tet-prism.
vol[0] = orient4d(p5, p6, p4, p3, p7, w5, w6, w4, 0, w7);
vol[1] = orient4d(p3, p6, p2, p0, p1, 0, w6, 0, 0, 0);
vol[2] = orient4d(p4, p6, p3, p0, p1, w4, w6, 0, 0, 0);
vol[3] = orient4d(p6, p5, p4, p3, p1, w6, w5, w4, 0, 0);
return fabs(vol[0]) + fabs(vol[1]) + fabs(vol[2]) + fabs(vol[3]);
}
///////////////////////////////////////////////////////////////////////////////
// //
// calculateabovepoint() Calculate a point above a facet in 'dummypoint'. //
// //
///////////////////////////////////////////////////////////////////////////////
bool tetgenmesh::calculateabovepoint(arraypool *facpoints, point *ppa,
point *ppb, point *ppc)
{
point *ppt, pa, pb, pc;
REAL v1[3], v2[3], n[3];
REAL lab, len, A, area;
REAL x, y, z;
int i;
ppt = (point *) fastlookup(facpoints, 0);
pa = *ppt; // a is the first point.
pb = pc = NULL; // Avoid compiler warnings.
// Get a point b s.t. the length of [a, b] is maximal.
lab = 0;
for (i = 1; i < facpoints->objects; i++) {
ppt = (point *) fastlookup(facpoints, i);
x = (*ppt)[0] - pa[0];
y = (*ppt)[1] - pa[1];
z = (*ppt)[2] - pa[2];
len = x * x + y * y + z * z;
if (len > lab) {
lab = len;
pb = *ppt;
}
}
lab = sqrt(lab);
if (lab == 0) {
if (!b->quiet) {
printf("Warning: All points of a facet are coincident with %d.\n",
pointmark(pa));
}
return false;
}
// Get a point c s.t. the area of [a, b, c] is maximal.
v1[0] = pb[0] - pa[0];
v1[1] = pb[1] - pa[1];
v1[2] = pb[2] - pa[2];
A = 0;
for (i = 1; i < facpoints->objects; i++) {
ppt = (point *) fastlookup(facpoints, i);
v2[0] = (*ppt)[0] - pa[0];
v2[1] = (*ppt)[1] - pa[1];
v2[2] = (*ppt)[2] - pa[2];
cross(v1, v2, n);
area = dot(n, n);
if (area > A) {
A = area;
pc = *ppt;
}
}
if (A == 0) {
// All points are collinear. No above point.
if (!b->quiet) {
printf("Warning: All points of a facet are collinaer with [%d, %d].\n",
pointmark(pa), pointmark(pb));
}
return false;
}
// Calculate an above point of this facet.
facenormal(pa, pb, pc, n, 1, NULL);
len = sqrt(dot(n, n));
n[0] /= len;
n[1] /= len;
n[2] /= len;
lab /= 2.0; // Half the maximal length.
dummypoint[0] = pa[0] + lab * n[0];
dummypoint[1] = pa[1] + lab * n[1];
dummypoint[2] = pa[2] + lab * n[2];
if (ppa != NULL) {
// Return the three points.
*ppa = pa;
*ppb = pb;
*ppc = pc;
}
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. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::calculateabovepoint4(point pa, point pb, point pc, point pd)
{
REAL n1[3], n2[3], *norm;
REAL len, len1, len2;
// Select a base.
facenormal(pa, pb, pc, n1, 1, NULL);
len1 = sqrt(dot(n1, n1));
facenormal(pa, pb, pd, n2, 1, NULL);
len2 = sqrt(dot(n2, n2));
if (len1 > len2) {
norm = n1;
len = len1;
} else {
norm = n2;
len = len2;
}
assert(len > 0);
norm[0] /= len;
norm[1] /= len;
norm[2] /= len;
len = distance(pa, pb);
dummypoint[0] = pa[0] + len * norm[0];
dummypoint[1] = pa[1] + len * norm[1];
dummypoint[2] = pa[2] + len * norm[2];
}
//// ////
//// ////
//// geom_cxx /////////////////////////////////////////////////////////////////
//// flip_cxx /////////////////////////////////////////////////////////////////
//// ////
//// ////
///////////////////////////////////////////////////////////////////////////////
// //
// 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. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::flip23(triface* fliptets, int hullflag, flipconstraints *fc)
{
triface topcastets[3], botcastets[3];
triface newface, casface;
point pa, pb, pc, pd, pe;
REAL attrib, volume;
int dummyflag = 0; // range = {-1, 0, 1, 2}.
int i;
if (hullflag > 0) {
// Check if e is dummypoint.
if (oppo(fliptets[1]) == dummypoint) {
// Swap the two old tets.
newface = fliptets[0];
fliptets[0] = fliptets[1];
fliptets[1] = newface;
dummyflag = -1; // d is dummypoint.
} else {
// Check if either a or b is dummypoint.
if (org(fliptets[0]) == dummypoint) {
dummyflag = 1; // a is dummypoint.
enextself(fliptets[0]);
eprevself(fliptets[1]);
} else if (dest(fliptets[0]) == dummypoint) {
dummyflag = 2; // b is dummypoint.
eprevself(fliptets[0]);
enextself(fliptets[1]);
} else {
dummyflag = 0; // either c or d may be dummypoint.
}
}
}
pa = org(fliptets[0]);
pb = dest(fliptets[0]);
pc = apex(fliptets[0]);
pd = oppo(fliptets[0]);
pe = oppo(fliptets[1]);
flip23count++;
// Get the outer boundary faces.
for (i = 0; i < 3; i++) {
fnext(fliptets[0], topcastets[i]);
enextself(fliptets[0]);
}
for (i = 0; i < 3; i++) {
fnext(fliptets[1], botcastets[i]);
eprevself(fliptets[1]);
}
// Re-use fliptets[0] and fliptets[1].
fliptets[0].ver = 11;
fliptets[1].ver = 11;
setelemmarker(fliptets[0].tet, 0); // Clear all flags.
setelemmarker(fliptets[1].tet, 0);
// NOTE: the element attributes and volume constraint remain unchanged.
if (checksubsegflag) {
// Dealloc the space to subsegments.
if (fliptets[0].tet[8] != NULL) {
tet2segpool->dealloc((shellface *) fliptets[0].tet[8]);
fliptets[0].tet[8] = NULL;
}
if (fliptets[1].tet[8] != NULL) {
tet2segpool->dealloc((shellface *) fliptets[1].tet[8]);
fliptets[1].tet[8] = NULL;
}
}
if (checksubfaceflag) {
// Dealloc the space to subfaces.
if (fliptets[0].tet[9] != NULL) {
tet2subpool->dealloc((shellface *) fliptets[0].tet[9]);
fliptets[0].tet[9] = NULL;
}
if (fliptets[1].tet[9] != NULL) {
tet2subpool->dealloc((shellface *) fliptets[1].tet[9]);
fliptets[1].tet[9] = NULL;
}
}
// Create a new tet.
maketetrahedron(&(fliptets[2]));
// The new tet have the same attributes from the old tet.
for (i = 0; i < numelemattrib; i++) {
attrib = elemattribute(fliptets[0].tet, i);
setelemattribute(fliptets[2].tet, i, attrib);
}
if (b->varvolume) {
volume = volumebound(fliptets[0].tet);
setvolumebound(fliptets[2].tet, volume);
}
if (hullflag > 0) {
// Check if d is dummytet.
if (pd != dummypoint) {
setvertices(fliptets[0], pe, pd, pa, pb); // [e,d,a,b] *
setvertices(fliptets[1], pe, pd, pb, pc); // [e,d,b,c] *
// Check if c is dummypoint.
if (pc != dummypoint) {
setvertices(fliptets[2], pe, pd, pc, pa); // [e,d,c,a] *
} else {
setvertices(fliptets[2], pd, pe, pa, pc); // [d,e,a,c]
esymself(fliptets[2]); // [e,d,c,a] *
}
// The hullsize does not change.
} else {
// d is dummypoint.
setvertices(fliptets[0], pa, pb, pe, pd); // [a,b,e,d]
setvertices(fliptets[1], pb, pc, pe, pd); // [b,c,e,d]
setvertices(fliptets[2], pc, pa, pe, pd); // [c,a,e,d]
// Adjust the faces to [e,d,a,b], [e,d,b,c], [e,d,c,a] *
for (i = 0; i < 3; i++) {
eprevesymself(fliptets[i]);
enextself(fliptets[i]);
}
// We deleted one hull tet, and created three hull tets.
hullsize += 2;
}
} else {
setvertices(fliptets[0], pe, pd, pa, pb); // [e,d,a,b] *
setvertices(fliptets[1], pe, pd, pb, pc); // [e,d,b,c] *
setvertices(fliptets[2], pe, pd, pc, pa); // [e,d,c,a] *
}
if (fc->remove_ndelaunay_edge) { // calc_tetprism_vol
REAL volneg[2], volpos[3], vol_diff;
if (pd != dummypoint) {
if (pc != dummypoint) {
volpos[0] = tetprismvol(pe, pd, pa, pb);
volpos[1] = tetprismvol(pe, pd, pb, pc);
volpos[2] = tetprismvol(pe, pd, pc, pa);
volneg[0] = tetprismvol(pa, pb, pc, pd);
volneg[1] = tetprismvol(pb, pa, pc, pe);
} else { // pc == dummypoint
volpos[0] = tetprismvol(pe, pd, pa, pb);
volpos[1] = 0.;
volpos[2] = 0.;
volneg[0] = 0.;
volneg[1] = 0.;
}
} else { // pd == dummypoint.
volpos[0] = 0.;
volpos[1] = 0.;
volpos[2] = 0.;
volneg[0] = 0.;
volneg[1] = tetprismvol(pb, pa, pc, pe);
}
vol_diff = volpos[0] + volpos[1] + volpos[2] - volneg[0] - volneg[1];
fc->tetprism_vol_sum += vol_diff; // Update the total sum.
}
// Bond three new tets together.
for (i = 0; i < 3; i++) {
esym(fliptets[i], newface);
bond(newface, fliptets[(i + 1) % 3]);
}
// Bond to top outer boundary faces (at [a,b,c,d]).
for (i = 0; i < 3; i++) {
eorgoppo(fliptets[i], newface); // At edges [b,a], [c,b], [a,c].
bond(newface, topcastets[i]);
}
// Bond bottom outer boundary faces (at [b,a,c,e]).
for (i = 0; i < 3; i++) {
edestoppo(fliptets[i], newface); // At edges [a,b], [b,c], [c,a].
bond(newface, botcastets[i]);
}
if (checksubsegflag) {
// Bond subsegments if there are.
// Each new tet has 5 edges to be checked (except the edge [e,d]).
face checkseg;
// The middle three: [a,b], [b,c], [c,a].
for (i = 0; i < 3; i++) {
if (issubseg(topcastets[i])) {
tsspivot1(topcastets[i], checkseg);
eorgoppo(fliptets[i], newface);
tssbond1(newface, checkseg);
sstbond1(checkseg, newface);
if (fc->chkencflag & 1) {
enqueuesubface(badsubsegs, &checkseg);
}
}
}
// The top three: [d,a], [d,b], [d,c]. Two tets per edge.
for (i = 0; i < 3; i++) {
eprev(topcastets[i], casface);
if (issubseg(casface)) {
tsspivot1(casface, checkseg);
enext(fliptets[i], newface);
tssbond1(newface, checkseg);
sstbond1(checkseg, newface);
esym(fliptets[(i + 2) % 3], newface);
eprevself(newface);
tssbond1(newface, checkseg);
sstbond1(checkseg, newface);
if (fc->chkencflag & 1) {
enqueuesubface(badsubsegs, &checkseg);
}
}
}
// The bot three: [a,e], [b,e], [c,e]. Two tets per edge.
for (i = 0; i < 3; i++) {
enext(botcastets[i], casface);
if (issubseg(casface)) {
tsspivot1(casface, checkseg);
eprev(fliptets[i], newface);
tssbond1(newface, checkseg);
sstbond1(checkseg, newface);
esym(fliptets[(i + 2) % 3], newface);
enextself(newface);
tssbond1(newface, checkseg);
sstbond1(checkseg, newface);
if (fc->chkencflag & 1) {
enqueuesubface(badsubsegs, &checkseg);
}
}
}
} // if (checksubsegflag)
if (checksubfaceflag) {
// Bond 6 subfaces if there are.
face checksh;
for (i = 0; i < 3; i++) {
if (issubface(topcastets[i])) {
tspivot(topcastets[i], checksh);
eorgoppo(fliptets[i], newface);
sesymself(checksh);
tsbond(newface, checksh);
if (fc->chkencflag & 2) {
enqueuesubface(badsubfacs, &checksh);
}
}
}
for (i = 0; i < 3; i++) {
if (issubface(botcastets[i])) {
tspivot(botcastets[i], checksh);
edestoppo(fliptets[i], newface);
sesymself(checksh);
tsbond(newface, checksh);
if (fc->chkencflag & 2) {
enqueuesubface(badsubfacs, &checksh);
}
}
}
} // if (checksubfaceflag)
if (fc->chkencflag & 4) {
// Put three new tets into check list.
for (i = 0; i < 3; i++) {
enqueuetetrahedron(&(fliptets[i]));
}
}
// Update the point-to-tet map.
setpoint2tet(pa, (tetrahedron) fliptets[0].tet);
setpoint2tet(pb, (tetrahedron) fliptets[0].tet);
setpoint2tet(pc, (tetrahedron) fliptets[1].tet);
setpoint2tet(pd, (tetrahedron) fliptets[0].tet);
setpoint2tet(pe, (tetrahedron) fliptets[0].tet);
if (hullflag > 0) {
if (dummyflag != 0) {
// Restore the original position of the points (for flipnm()).
if (dummyflag == -1) {
// Reverse the edge.
for (i = 0; i < 3; i++) {
esymself(fliptets[i]);
}
// Swap the last two new tets.
newface = fliptets[1];
fliptets[1] = fliptets[2];
fliptets[2] = newface;
} else {
// either a or b were swapped.
if (dummyflag == 1) {
// a is dummypoint.
newface = fliptets[0];
fliptets[0] = fliptets[2];
fliptets[2] = fliptets[1];
fliptets[1] = newface;
} else { // dummyflag == 2
// b is dummypoint.
newface = fliptets[0];
fliptets[0] = fliptets[1];
fliptets[1] = fliptets[2];
fliptets[2] = newface;
}
}
}
}
if (fc->enqflag > 0) {
// Queue faces which may be locally non-Delaunay.
for (i = 0; i < 3; i++) {
eprevesym(fliptets[i], newface);
flippush(flipstack, &newface);
}
if (fc->enqflag > 1) {
for (i = 0; i < 3; i++) {
enextesym(fliptets[i], newface);
flippush(flipstack, &newface);
}
}
}
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. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::flip32(triface* fliptets, int hullflag, flipconstraints *fc)
{
triface topcastets[3], botcastets[3];
triface newface, casface;
face flipshs[3];
face checkseg;
point pa, pb, pc, pd, pe;
REAL attrib, volume;
int dummyflag = 0; // Rangle = {-1, 0, 1, 2}
int spivot = -1, scount = 0; // for flip22()
int t1ver;
int i, j;
if (hullflag > 0) {
// Check if e is 'dummypoint'.
if (org(fliptets[0]) == dummypoint) {
// Reverse the edge.
for (i = 0; i < 3; i++) {
esymself(fliptets[i]);
}
// Swap the last two tets.
newface = fliptets[1];
fliptets[1] = fliptets[2];
fliptets[2] = newface;
dummyflag = -1; // e is dummypoint.
} else {
// Check if a or b is the 'dummypoint'.
if (apex(fliptets[0]) == dummypoint) {
dummyflag = 1; // a is dummypoint.
newface = fliptets[0];
fliptets[0] = fliptets[1];
fliptets[1] = fliptets[2];
fliptets[2] = newface;
} else if (apex(fliptets[1]) == dummypoint) {
dummyflag = 2; // b is dummypoint.
newface = fliptets[0];
fliptets[0] = fliptets[2];
fliptets[2] = fliptets[1];
fliptets[1] = newface;
} else {
dummyflag = 0; // either c or d may be dummypoint.
}
}
}
pa = apex(fliptets[0]);
pb = apex(fliptets[1]);
pc = apex(fliptets[2]);
pd = dest(fliptets[0]);
pe = org(fliptets[0]);
flip32count++;
// Get the outer boundary faces.
for (i = 0; i < 3; i++) {
eorgoppo(fliptets[i], casface);
fsym(casface, topcastets[i]);
}
for (i = 0; i < 3; i++) {
edestoppo(fliptets[i], casface);
fsym(casface, botcastets[i]);
}
if (checksubfaceflag) {
// Check if there are interior subfaces at the edge [e,d].
for (i = 0; i < 3; i++) {
tspivot(fliptets[i], flipshs[i]);
if (flipshs[i].sh != NULL) {
// Found an interior subface.
stdissolve(flipshs[i]); // Disconnect the sub-tet bond.
scount++;
} else {
spivot = i;
}
}
}
// Re-use fliptets[0] and fliptets[1].
fliptets[0].ver = 11;
fliptets[1].ver = 11;
setelemmarker(fliptets[0].tet, 0); // Clear all flags.
setelemmarker(fliptets[1].tet, 0);
if (checksubsegflag) {
// Dealloc the space to subsegments.
if (fliptets[0].tet[8] != NULL) {
tet2segpool->dealloc((shellface *) fliptets[0].tet[8]);
fliptets[0].tet[8] = NULL;
}
if (fliptets[1].tet[8] != NULL) {
tet2segpool->dealloc((shellface *) fliptets[1].tet[8]);
fliptets[1].tet[8] = NULL;
}
}
if (checksubfaceflag) {
// Dealloc the space to subfaces.
if (fliptets[0].tet[9] != NULL) {
tet2subpool->dealloc((shellface *) fliptets[0].tet[9]);
fliptets[0].tet[9] = NULL;
}
if (fliptets[1].tet[9] != NULL) {
tet2subpool->dealloc((shellface *) fliptets[1].tet[9]);
fliptets[1].tet[9] = NULL;
}
}
if (checksubfaceflag) {
if (scount > 0) {
// The element attributes and volume constraint must be set correctly.
// There are two subfaces involved in this flip. The three tets are
// separated into two different regions, one may be exterior. The
// first region has two tets, and the second region has only one.
// The two created tets must be in the same region as the first region.
// The element attributes and volume constraint must be set correctly.
//assert(spivot != -1);
// The tet fliptets[spivot] is in the first region.
for (j = 0; j < 2; j++) {
for (i = 0; i < numelemattrib; i++) {
attrib = elemattribute(fliptets[spivot].tet, i);
setelemattribute(fliptets[j].tet, i, attrib);
}
if (b->varvolume) {
volume = volumebound(fliptets[spivot].tet);
setvolumebound(fliptets[j].tet, volume);
}
}
}
}
// Delete an old tet.
tetrahedrondealloc(fliptets[2].tet);
if (hullflag > 0) {
// Check if c is dummypointc.
if (pc != dummypoint) {
// Check if d is dummypoint.
if (pd != dummypoint) {
// No hull tet is involved.
} else {
// We deleted three hull tets, and created one hull tet.
hullsize -= 2;
}
setvertices(fliptets[0], pa, pb, pc, pd);
setvertices(fliptets[1], pb, pa, pc, pe);
} else {
// c is dummypoint. The two new tets are hull tets.
setvertices(fliptets[0], pb, pa, pd, pc);
setvertices(fliptets[1], pa, pb, pe, pc);
// Adjust badc -> abcd.
esymself(fliptets[0]);
// Adjust abec -> bace.
esymself(fliptets[1]);
// The hullsize does not change.
}
} else {
setvertices(fliptets[0], pa, pb, pc, pd);
setvertices(fliptets[1], pb, pa, pc, pe);
}
if (fc->remove_ndelaunay_edge) { // calc_tetprism_vol
REAL volneg[3], volpos[2], vol_diff;
if (pc != dummypoint) {
if (pd != dummypoint) {
volneg[0] = tetprismvol(pe, pd, pa, pb);
volneg[1] = tetprismvol(pe, pd, pb, pc);
volneg[2] = tetprismvol(pe, pd, pc, pa);
volpos[0] = tetprismvol(pa, pb, pc, pd);
volpos[1] = tetprismvol(pb, pa, pc, pe);
} else { // pd == dummypoint
volneg[0] = 0.;
volneg[1] = 0.;
volneg[2] = 0.;
volpos[0] = 0.;
volpos[1] = tetprismvol(pb, pa, pc, pe);
}
} else { // pc == dummypoint.
volneg[0] = tetprismvol(pe, pd, pa, pb);
volneg[1] = 0.;
volneg[2] = 0.;
volpos[0] = 0.;
volpos[1] = 0.;
}
vol_diff = volpos[0] + volpos[1] - volneg[0] - volneg[1] - volneg[2];
fc->tetprism_vol_sum += vol_diff; // Update the total sum.
}
// Bond abcd <==> bace.
bond(fliptets[0], fliptets[1]);
// Bond new faces to top outer boundary faces (at abcd).
for (i = 0; i < 3; i++) {
esym(fliptets[0], newface);
bond(newface, topcastets[i]);
enextself(fliptets[0]);
}
// Bond new faces to bottom outer boundary faces (at bace).
for (i = 0; i < 3; i++) {
esym(fliptets[1], newface);
bond(newface, botcastets[i]);
eprevself(fliptets[1]);
}
if (checksubsegflag) {
// Bond 9 segments to new (flipped) tets.
for (i = 0; i < 3; i++) { // edges a->b, b->c, c->a.
if (issubseg(topcastets[i])) {
tsspivot1(topcastets[i], checkseg);
tssbond1(fliptets[0], checkseg);
sstbond1(checkseg, fliptets[0]);
tssbond1(fliptets[1], checkseg);
sstbond1(checkseg, fliptets[1]);
if (fc->chkencflag & 1) {
enqueuesubface(badsubsegs, &checkseg);
}
}
enextself(fliptets[0]);
eprevself(fliptets[1]);
}
// The three top edges.
for (i = 0; i < 3; i++) { // edges b->d, c->d, a->d.
esym(fliptets[0], newface);
eprevself(newface);
enext(topcastets[i], casface);
if (issubseg(casface)) {
tsspivot1(casface, checkseg);
tssbond1(newface, checkseg);
sstbond1(checkseg, newface);
if (fc->chkencflag & 1) {
enqueuesubface(badsubsegs, &checkseg);
}
}
enextself(fliptets[0]);
}
// The three bot edges.
for (i = 0; i < 3; i++) { // edges b<-e, c<-e, a<-e.
esym(fliptets[1], newface);
enextself(newface);
eprev(botcastets[i], casface);
if (issubseg(casface)) {
tsspivot1(casface, checkseg);
tssbond1(newface, checkseg);
sstbond1(checkseg, newface);
if (fc->chkencflag & 1) {
enqueuesubface(badsubsegs, &checkseg);
}
}
eprevself(fliptets[1]);
}
} // if (checksubsegflag)
if (checksubfaceflag) {
face checksh;
// Bond the top three casing subfaces.
for (i = 0; i < 3; i++) { // At edges [b,a], [c,b], [a,c]
if (issubface(topcastets[i])) {
tspivot(topcastets[i], checksh);
esym(fliptets[0], newface);
sesymself(checksh);
tsbond(newface, checksh);
if (fc->chkencflag & 2) {
enqueuesubface(badsubfacs, &checksh);
}
}
enextself(fliptets[0]);
}
// Bond the bottom three casing subfaces.
for (i = 0; i < 3; i++) { // At edges [a,b], [b,c], [c,a]
if (issubface(botcastets[i])) {
tspivot(botcastets[i], checksh);
esym(fliptets[1], newface);
sesymself(checksh);
tsbond(newface, checksh);
if (fc->chkencflag & 2) {
enqueuesubface(badsubfacs, &checksh);
}
}
eprevself(fliptets[1]);
}
if (scount > 0) {
face flipfaces[2];
// Perform a 2-to-2 flip in subfaces.
flipfaces[0] = flipshs[(spivot + 1) % 3];
flipfaces[1] = flipshs[(spivot + 2) % 3];
sesymself(flipfaces[1]);
flip22(flipfaces, 0, fc->chkencflag);
// Connect the flipped subfaces to flipped tets.
// First go to the corresponding flipping edge.
// Re-use top- and botcastets[0].
topcastets[0] = fliptets[0];
botcastets[0] = fliptets[1];
for (i = 0; i < ((spivot + 1) % 3); i++) {
enextself(topcastets[0]);
eprevself(botcastets[0]);
}
// Connect the top subface to the top tets.
esymself(topcastets[0]);
sesymself(flipfaces[0]);
// Check if there already exists a subface.
tspivot(topcastets[0], checksh);
if (checksh.sh == NULL) {
tsbond(topcastets[0], flipfaces[0]);
fsymself(topcastets[0]);
sesymself(flipfaces[0]);
tsbond(topcastets[0], flipfaces[0]);
} else {
// An invalid 2-to-2 flip. Report a bug.
terminatetetgen(this, 2);
}
// Connect the bot subface to the bottom tets.
esymself(botcastets[0]);
sesymself(flipfaces[1]);
// Check if there already exists a subface.
tspivot(botcastets[0], checksh);
if (checksh.sh == NULL) {
tsbond(botcastets[0], flipfaces[1]);
fsymself(botcastets[0]);
sesymself(flipfaces[1]);
tsbond(botcastets[0], flipfaces[1]);
} else {
// An invalid 2-to-2 flip. Report a bug.
terminatetetgen(this, 2);
}
} // if (scount > 0)
} // if (checksubfaceflag)
if (fc->chkencflag & 4) {
// Put two new tets into check list.
for (i = 0; i < 2; i++) {
enqueuetetrahedron(&(fliptets[i]));
}
}
setpoint2tet(pa, (tetrahedron) fliptets[0].tet);
setpoint2tet(pb, (tetrahedron) fliptets[0].tet);
setpoint2tet(pc, (tetrahedron) fliptets[0].tet);
setpoint2tet(pd, (tetrahedron) fliptets[0].tet);
setpoint2tet(pe, (tetrahedron) fliptets[1].tet);
if (hullflag > 0) {
if (dummyflag != 0) {
// Restore the original position of the points (for flipnm()).
if (dummyflag == -1) {
// e were dummypoint. Swap the two new tets.
newface = fliptets[0];
fliptets[0] = fliptets[1];
fliptets[1] = newface;
} else {
// a or b was dummypoint.
if (dummyflag == 1) {
eprevself(fliptets[0]);
enextself(fliptets[1]);
} else { // dummyflag == 2
enextself(fliptets[0]);
eprevself(fliptets[1]);
}
}
}
}
if (fc->enqflag > 0) {
// Queue faces which may be locally non-Delaunay.
// pa = org(fliptets[0]); // 'a' may be a new vertex.
enextesym(fliptets[0], newface);
flippush(flipstack, &newface);
eprevesym(fliptets[1], newface);
flippush(flipstack, &newface);
if (fc->enqflag > 1) {
//pb = dest(fliptets[0]);
eprevesym(fliptets[0], newface);
flippush(flipstack, &newface);
enextesym(fliptets[1], newface);
flippush(flipstack, &newface);
//pc = apex(fliptets[0]);
esym(fliptets[0], newface);
flippush(flipstack, &newface);
esym(fliptets[1], newface);
flippush(flipstack, &newface);
}
}
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. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::flip41(triface* fliptets, int hullflag, flipconstraints *fc)
{
triface topcastets[3], botcastet;
triface newface, neightet;
face flipshs[4];
point pa, pb, pc, pd, pp;
int dummyflag = 0; // in {0, 1, 2, 3, 4}
int spivot = -1, scount = 0;
int t1ver;
int i;
pa = org(fliptets[3]);
pb = dest(fliptets[3]);
pc = apex(fliptets[3]);
pd = dest(fliptets[0]);
pp = org(fliptets[0]); // The removing vertex.
flip41count++;
// Get the outer boundary faces.
for (i = 0; i < 3; i++) {
enext(fliptets[i], topcastets[i]);
fnextself(topcastets[i]); // [d,a,b,#], [d,b,c,#], [d,c,a,#]
enextself(topcastets[i]); // [a,b,d,#], [b,c,d,#], [c,a,d,#]
}
fsym(fliptets[3], botcastet); // [b,a,c,#]
if (checksubfaceflag) {
// Check if there are three subfaces at 'p'.
// Re-use 'newface'.
for (i = 0; i < 3; i++) {
fnext(fliptets[3], newface); // [a,b,p,d],[b,c,p,d],[c,a,p,d].
tspivot(newface, flipshs[i]);
if (flipshs[i].sh != NULL) {
spivot = i; // Remember this subface.
scount++;
}
enextself(fliptets[3]);
}
if (scount > 0) {
// 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 {
spivot = 3; // The new subface is [a,b,c].
}
}
} // if (checksubfaceflag)
// Re-use fliptets[0] for [a,b,c,d].
fliptets[0].ver = 11;
setelemmarker(fliptets[0].tet, 0); // Clean all flags.
// NOTE: the element attributes and volume constraint remain unchanged.
if (checksubsegflag) {
// Dealloc the space to subsegments.
if (fliptets[0].tet[8] != NULL) {
tet2segpool->dealloc((shellface *) fliptets[0].tet[8]);
fliptets[0].tet[8] = NULL;
}
}
if (checksubfaceflag) {
// Dealloc the space to subfaces.
if (fliptets[0].tet[9] != NULL) {
tet2subpool->dealloc((shellface *) fliptets[0].tet[9]);
fliptets[0].tet[9] = NULL;
}
}
// Delete the other three tets.
for (i = 1; i < 4; i++) {
tetrahedrondealloc(fliptets[i].tet);
}
if (pp != dummypoint) {
// Mark the point pp as unused.
setpointtype(pp, UNUSEDVERTEX);
unuverts++;
}
// Create the new tet [a,b,c,d].
if (hullflag > 0) {
// One of the five vertices may be 'dummypoint'.
if (pa == dummypoint) {
// pa is dummypoint.
setvertices(fliptets[0], pc, pb, pd, pa);
esymself(fliptets[0]); // [b,c,a,d]
eprevself(fliptets[0]); // [a,b,c,d]
dummyflag = 1;
} else if (pb == dummypoint) {
setvertices(fliptets[0], pa, pc, pd, pb);
esymself(fliptets[0]); // [c,a,b,d]
enextself(fliptets[0]); // [a,b,c,d]
dummyflag = 2;
} else if (pc == dummypoint) {
setvertices(fliptets[0], pb, pa, pd, pc);
esymself(fliptets[0]); // [a,b,c,d]
dummyflag = 3;
} else if (pd == dummypoint) {
setvertices(fliptets[0], pa, pb, pc, pd);
dummyflag = 4;
} else {
setvertices(fliptets[0], pa, pb, pc, pd);
if (pp == dummypoint) {
dummyflag = -1;
} else {
dummyflag = 0;
}
}
if (dummyflag > 0) {
// We deleted 3 hull tets, and create 1 hull tet.
hullsize -= 2;
} else if (dummyflag < 0) {
// We deleted 4 hull tets.
hullsize -= 4;
// meshedges does not change.
}
} else {
setvertices(fliptets[0], pa, pb, pc, pd);
}
if (fc->remove_ndelaunay_edge) { // calc_tetprism_vol
REAL volneg[4], volpos[1], vol_diff;
if (dummyflag > 0) {
if (pa == dummypoint) {
volneg[0] = 0.;
volneg[1] = tetprismvol(pp, pd, pb, pc);
volneg[2] = 0.;
volneg[3] = 0.;
} else if (pb == dummypoint) {
volneg[0] = 0.;
volneg[1] = 0.;
volneg[2] = tetprismvol(pp, pd, pc, pa);
volneg[3] = 0.;
} else if (pc == dummypoint) {
volneg[0] = tetprismvol(pp, pd, pa, pb);
volneg[1] = 0.;
volneg[2] = 0.;
volneg[3] = 0.;
} else { // pd == dummypoint
volneg[0] = 0.;
volneg[1] = 0.;
volneg[2] = 0.;
volneg[3] = tetprismvol(pa, pb, pc, pp);
}
volpos[0] = 0.;
} else if (dummyflag < 0) {
volneg[0] = 0.;
volneg[1] = 0.;
volneg[2] = 0.;
volneg[3] = 0.;
volpos[0] = tetprismvol(pa, pb, pc, pd);
} else {
volneg[0] = tetprismvol(pp, pd, pa, pb);
volneg[1] = tetprismvol(pp, pd, pb, pc);
volneg[2] = tetprismvol(pp, pd, pc, pa);
volneg[3] = tetprismvol(pa, pb, pc, pp);
volpos[0] = tetprismvol(pa, pb, pc, pd);
}
vol_diff = volpos[0] - volneg[0] - volneg[1] - volneg[2] - volneg[3];
fc->tetprism_vol_sum += vol_diff; // Update the total sum.
}
// Bond the new tet to adjacent tets.
for (i = 0; i < 3; i++) {
esym(fliptets[0], newface); // At faces [b,a,d], [c,b,d], [a,c,d].
bond(newface, topcastets[i]);
enextself(fliptets[0]);
}
bond(fliptets[0], botcastet);
if (checksubsegflag) {
face checkseg;
// Bond 6 segments (at edges of [a,b,c,d]) if there there are.
for (i = 0; i < 3; i++) {
eprev(topcastets[i], newface); // At edges [d,a],[d,b],[d,c].
if (issubseg(newface)) {
tsspivot1(newface, checkseg);
esym(fliptets[0], newface);
enextself(newface); // At edges [a,d], [b,d], [c,d].
tssbond1(newface, checkseg);
sstbond1(checkseg, newface);
if (fc->chkencflag & 1) {
enqueuesubface(badsubsegs, &checkseg);
}
}
enextself(fliptets[0]);
}
for (i = 0; i < 3; i++) {
if (issubseg(topcastets[i])) {
tsspivot1(topcastets[i], checkseg); // At edges [a,b],[b,c],[c,a].
tssbond1(fliptets[0], checkseg);
sstbond1(checkseg, fliptets[0]);
if (fc->chkencflag & 1) {
enqueuesubface(badsubsegs, &checkseg);
}
}
enextself(fliptets[0]);
}
}
if (checksubfaceflag) {
face checksh;
// Bond 4 subfaces (at faces of [a,b,c,d]) if there are.
for (i = 0; i < 3; i++) {
if (issubface(topcastets[i])) {
tspivot(topcastets[i], checksh); // At faces [a,b,d],[b,c,d],[c,a,d]
esym(fliptets[0], newface); // At faces [b,a,d],[c,b,d],[a,c,d]
sesymself(checksh);
tsbond(newface, checksh);
if (fc->chkencflag & 2) {
enqueuesubface(badsubfacs, &checksh);
}
}
enextself(fliptets[0]);
}
if (issubface(botcastet)) {
tspivot(botcastet, checksh); // At face [b,a,c]
sesymself(checksh);
tsbond(fliptets[0], checksh);
if (fc->chkencflag & 2) {
enqueuesubface(badsubfacs, &checksh);
}
}
if (spivot >= 0) {
// Perform a 3-to-1 flip in surface triangulation.
// Depending on the value of 'spivot', the three subfaces are:
// - 0: [a,b,p], [b,d,p], [d,a,p]
// - 1: [b,c,p], [c,d,p], [d,b,p]
// - 2: [c,a,p], [a,d,p], [d,c,p]
// - 3: [a,b,p], [b,c,p], [c,a,p]
// Adjust the three subfaces such that their origins are p, i.e.,
// - 3: [p,a,b], [p,b,c], [p,c,a]. (Required by the flip31()).
for (i = 0; i < 3; i++) {
senext2self(flipshs[i]);
}
flip31(flipshs, 0);
// Delete the three old subfaces.
for (i = 0; i < 3; i++) {
shellfacedealloc(subfaces, flipshs[i].sh);
}
if (spivot < 3) {
// // Bond the new subface to the new tet [a,b,c,d].
tsbond(topcastets[spivot], flipshs[3]);
fsym(topcastets[spivot], newface);
sesym(flipshs[3], checksh);
tsbond(newface, checksh);
} else {
// Bound the new subface [a,b,c] to the new tet [a,b,c,d].
tsbond(fliptets[0], flipshs[3]);
fsym(fliptets[0], newface);
sesym(flipshs[3], checksh);
tsbond(newface, checksh);
}
} // if (spivot > 0)
} // if (checksubfaceflag)
if (fc->chkencflag & 4) {
enqueuetetrahedron(&(fliptets[0]));
}
// Update the point-to-tet map.
setpoint2tet(pa, (tetrahedron) fliptets[0].tet);
setpoint2tet(pb, (tetrahedron) fliptets[0].tet);
setpoint2tet(pc, (tetrahedron) fliptets[0].tet);
setpoint2tet(pd, (tetrahedron) fliptets[0].tet);
if (fc->enqflag > 0) {
// Queue faces which may be locally non-Delaunay.
flippush(flipstack, &(fliptets[0])); // [a,b,c] (opposite to new point).
if (fc->enqflag > 1) {
for (i = 0; i < 3; i++) {
esym(fliptets[0], newface);
flippush(flipstack, &newface);
enextself(fliptets[0]);
}
}
}
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. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot,
flipconstraints* fc)
{
triface fliptets[3], spintet, flipedge;
triface *tmpabtets, *parytet;
point pa, pb, pc, pd, pe, pf;
REAL ori;
int hullflag, hulledgeflag;
int reducflag, rejflag;
int reflexlinkedgecount;
int edgepivot;
int n1, nn;
int t1ver;
int i, j;
pa = org(abtets[0]);
pb = dest(abtets[0]);
if (n > 3) {
// Try to reduce the size of the Star(ab) by flipping a face in it.
reflexlinkedgecount = 0;
for (i = 0; i < n; i++) {
// Let the face of 'abtets[i]' be [a,b,c].
if (checksubfaceflag) {
if (issubface(abtets[i])) {
continue; // Skip a subface.
}
}
// Do not flip this face if it is involved in two Stars.
if ((elemcounter(abtets[i]) > 1) ||
(elemcounter(abtets[(i - 1 + n) % n]) > 1)) {
continue;
}
pc = apex(abtets[i]);
pd = apex(abtets[(i + 1) % n]);
pe = apex(abtets[(i - 1 + n) % n]);
if ((pd == dummypoint) || (pe == dummypoint)) {
continue; // [a,b,c] is a hull face.
}
// Decide whether [a,b,c] is flippable or not.
reducflag = 0;
hullflag = (pc == dummypoint); // pc may be dummypoint.
hulledgeflag = 0;
if (hullflag == 0) {
ori = orient3d(pb, pc, pd, pe); // Is [b,c] locally convex?
if (ori > 0) {
ori = orient3d(pc, pa, pd, pe); // Is [c,a] locally convex?
if (ori > 0) {
// Test if [a,b] is locally convex OR flat.
ori = orient3d(pa, pb, pd, pe);
if (ori > 0) {
// Found a 2-to-3 flip: [a,b,c] => [e,d]
reducflag = 1;
} else if (ori == 0) {
// [a,b] is flat.
if (n == 4) {
// The "flat" tet can be removed immediately by a 3-to-2 flip.
reducflag = 1;
// Check if [e,d] is a hull edge.
pf = apex(abtets[(i + 2) % n]);
hulledgeflag = (pf == dummypoint);
}
}
}
}
if (!reducflag) {
reflexlinkedgecount++;
}
} else {
// 'c' is dummypoint.
if (n == 4) {
// Let the vertex opposite to 'c' is 'f'.
// A 4-to-4 flip is possible if the two tets [d,e,f,a] and [e,d,f,b]
// are valid tets.
// Note: When the mesh is not convex, it is possible that [a,b] is
// 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);
if (ori < 0) {
// Found a 4-to-4 flip: [a,b] => [e,d]
reducflag = 1;
ori = 0; // Signal as a 4-to-4 flip (like a co-planar case).
hulledgeflag = 1; // [e,d] is a hull edge.
}
}
}
} // if (hullflag)
if (reducflag) {
if (nonconvex && hulledgeflag) {
// We will create a hull edge [e,d]. Make sure it does not exist.
if (getedge(pe, pd, &spintet)) {
// The 2-to-3 flip is not a topological valid flip.
reducflag = 0;
}
}
}
if (reducflag) {
// [a,b,c] could be removed by a 2-to-3 flip.
rejflag = 0;
if (fc->checkflipeligibility) {
// Check if the flip can be performed.
rejflag = checkflipeligibility(1, pa, pb, pc, pd, pe, level,
abedgepivot, fc);
}
if (!rejflag) {
// Do flip: [a,b,c] => [e,d].
fliptets[0] = abtets[i];
fsym(fliptets[0], fliptets[1]); // abtets[i-1].
flip23(fliptets, hullflag, fc);
// Shrink the array 'abtets', maintain the original order.
// Two tets 'abtets[i-1] ([a,b,e,c])' and 'abtets[i] ([a,b,c,d])'
// are flipped, i.e., they do not in Star(ab) anymore.
// 'fliptets[0]' ([e,d,a,b]) is in Star(ab), it is saved in
// 'abtets[i-1]' (adjust it to be [a,b,e,d]), see below:
//
// before after
// [0] |___________| [0] |___________|
// ... |___________| ... |___________|
// [i-1] |_[a,b,e,c]_| [i-1] |_[a,b,e,d]_|
// [i] |_[a,b,c,d]_| --> [i] |_[a,b,d,#]_|
// [i+1] |_[a,b,d,#]_| [i+1] |_[a,b,#,*]_|
// ... |___________| ... |___________|
// [n-2] |___________| [n-2] |___________|
// [n-1] |___________| [n-1] |_[i]_2-t-3_|
//
edestoppoself(fliptets[0]); // [a,b,e,d]
// Increase the counter of this new tet (it is in Star(ab)).
increaseelemcounter(fliptets[0]);
abtets[(i - 1 + n) % n] = fliptets[0];
for (j = i; j < n - 1; j++) {
abtets[j] = abtets[j + 1]; // Upshift
}
// 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
// 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.
// Use the 5th bit in 'abtets[n - 1].ver' to signal a 2-to-3 flip.
abtets[n - 1].ver |= (1 << 4);
// The poisition [i] of this flip is saved above the 7th bit.
abtets[n - 1].ver |= (i << 6);
if (fc->collectnewtets) {
// Push the two new tets [e,d,b,c] and [e,d,c,a] into a stack.
// Re-use the global array 'cavetetlist'.
for (j = 1; j < 3; j++) {
cavetetlist->newindex((void **) &parytet);
*parytet = fliptets[j]; // fliptets[1], fliptets[2].
}
}
// Star(ab) is reduced. Try to flip the edge [a,b].
nn = flipnm(abtets, n - 1, level, abedgepivot, fc);
if (nn == 2) {
// The edge has been flipped.
return nn;
} else { // if (nn > 2)
// The edge is not flipped.
if (fc->unflip || (ori == 0)) {
// Undo the previous 2-to-3 flip, i.e., do a 3-to-2 flip to
// transform [e,d] => [a,b,c].
// 'ori == 0' means that the previous flip created a degenerated
// tet. It must be removed.
// Remember that 'abtets[i-1]' is [a,b,e,d]. We can use it to
// find another two tets [e,d,b,c] and [e,d,c,a].
fliptets[0] = abtets[(i-1 + (n-1)) % (n-1)]; // [a,b,e,d]
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).
for (j = 0; j < 2; j++) {
increaseelemcounter(fliptets[j]);
}
// Expand the array 'abtets', maintain the original order.
for (j = n - 2; j>= i; j--) {
abtets[j + 1] = abtets[j]; // Downshift
}
// Insert the two new tets 'fliptets[0]' [a,b,c,d] and
// 'fliptets[1]' [b,a,c,e] into the (i-1)-th and i-th entries,
// respectively.
esym(fliptets[1], abtets[(i - 1 + n) % n]); // [a,b,e,c]
abtets[i] = fliptets[0]; // [a,b,c,d]
nn++;
if (fc->collectnewtets) {
// Pop two (flipped) tets from the stack.
cavetetlist->objects -= 2;
}
} // if (unflip || (ori == 0))
} // if (nn > 2)
if (!fc->unflip) {
// The flips are not reversed. The current Star(ab) can not be
// further reduced. Return its current size (# of tets).
return nn;
}
// unflip is set.
// Continue the search for flips.
}
} // if (reducflag)
} // i
// The Star(ab) is not reduced.
if (reflexlinkedgecount > 0) {
// There are reflex edges in the Link(ab).
if (((b->fliplinklevel < 0) && (level < autofliplinklevel)) ||
((b->fliplinklevel >= 0) && (level < b->fliplinklevel))) {
// Try to reduce the Star(ab) by flipping a reflex edge in Link(ab).
for (i = 0; i < n; i++) {
// Do not flip this face [a,b,c] if there are two Stars involved.
if ((elemcounter(abtets[i]) > 1) ||
(elemcounter(abtets[(i - 1 + n) % n]) > 1)) {
continue;
}
pc = apex(abtets[i]);
if (pc == dummypoint) {
continue; // [a,b] is a hull edge.
}
pd = apex(abtets[(i + 1) % n]);
pe = apex(abtets[(i - 1 + n) % n]);
if ((pd == dummypoint) || (pe == dummypoint)) {
continue; // [a,b,c] is a hull face.
}
edgepivot = 0; // No edge is selected yet.
// Test if [b,c] is locally convex or flat.
ori = orient3d(pb, pc, pd, pe);
if (ori <= 0) {
// Select the edge [c,b].
enext(abtets[i], flipedge); // [b,c,a,d]
edgepivot = 1;
}
if (!edgepivot) {
// Test if [c,a] is locally convex or flat.
ori = orient3d(pc, pa, pd, pe);
if (ori <= 0) {
// Select the edge [a,c].
eprev(abtets[i], flipedge); // [c,a,b,d].
edgepivot = 2;
}
}
if (!edgepivot) continue;
// An edge is selected.
if (checksubsegflag) {
// Do not flip it if it is a segment.
if (issubseg(flipedge)) {
if (fc->collectencsegflag) {
face checkseg, *paryseg;
tsspivot1(flipedge, checkseg);
if (!sinfected(checkseg)) {
// Queue this segment in list.
sinfect(checkseg);
caveencseglist->newindex((void **) &paryseg);
*paryseg = checkseg;
}
}
continue;
}
}
// Try to flip the selected edge ([c,b] or [a,c]).
esymself(flipedge);
// Count the number of tets at the edge.
n1 = 0;
j = 0; // Sum of the star counters.
spintet = flipedge;
while (1) {
n1++;
j += (elemcounter(spintet));
fnextself(spintet);
if (spintet.tet == flipedge.tet) break;
}
assert(n1 >= 3);
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 ((b->flipstarsize > 0) && (n1 > b->flipstarsize)) {
// The star size exceeds the given limit.
continue; // Do not flip it.
}
// Allocate spaces for Star(flipedge).
tmpabtets = new triface[n1];
// Form the Star(flipedge).
j = 0;
spintet = flipedge;
while (1) {
tmpabtets[j] = spintet;
// Increase the star counter of this tet.
increaseelemcounter(tmpabtets[j]);
j++;
fnextself(spintet);
if (spintet.tet == flipedge.tet) break;
}
// Try to flip the selected edge away.
nn = flipnm(tmpabtets, n1, level + 1, edgepivot, fc);
if (nn == 2) {
// The edge is flipped. Star(ab) is reduced.
// Shrink the array 'abtets', maintain the original order.
if (edgepivot == 1) {
// 'tmpabtets[0]' is [d,a,e,b] => contains [a,b].
spintet = tmpabtets[0]; // [d,a,e,b]
enextself(spintet);
esymself(spintet);
enextself(spintet); // [a,b,e,d]
} else {
// 'tmpabtets[1]' is [b,d,e,a] => contains [a,b].
spintet = tmpabtets[1]; // [b,d,e,a]
eprevself(spintet);
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;
for (j = i; j < n - 1; j++) {
abtets[j] = abtets[j + 1]; // Upshift
}
// Remember the flips in the last entry of the array 'abtets'.
// They can be used to recover the flipped edge.
abtets[n - 1].tet = (tetrahedron *) tmpabtets; // The star(fedge).
abtets[n - 1].ver = 0; // Clear it.
// Use the 1st and 2nd bit to save 'edgepivot' (1 or 2).
abtets[n - 1].ver |= edgepivot;
// Use the 6th bit to signal this n1-to-m1 flip.
abtets[n - 1].ver |= (1 << 5);
// The poisition [i] of this flip is saved from 7th to 19th bit.
abtets[n - 1].ver |= (i << 6);
// The size of the star 'n1' is saved from 20th bit.
abtets[n - 1].ver |= (n1 << 19);
// Remember the flipped link vertex 'c'. It can be used to recover
// the original edge link of [a,b], and to collect new tets.
tmpabtets[0].tet = (tetrahedron *) pc;
tmpabtets[0].ver = (1 << 5); // Flag it as a vertex handle.
// Continue to flip the edge [a,b].
nn = flipnm(abtets, n - 1, level, abedgepivot, fc);
if (nn == 2) {
// The edge has been flipped.
return nn;
} else { // if (nn > 2) {
// 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
// Star(ab). It is the start tet to recover the flipped edge.
if (edgepivot == 1) {
// The flip edge is [c,b].
tmpabtets[0] = abtets[((i-1)+(n-1))%(n-1)]; // [a,b,e,d]
eprevself(tmpabtets[0]);
esymself(tmpabtets[0]);
eprevself(tmpabtets[0]); // [d,a,e,b]
fsym(tmpabtets[0], tmpabtets[1]); // [a,d,e,c]
} else {
// The flip edge is [a,c].
tmpabtets[1] = abtets[((i-1)+(n-1))%(n-1)]; // [a,b,e,d]
enextself(tmpabtets[1]);
esymself(tmpabtets[1]);
enextself(tmpabtets[1]); // [b,d,e,a]
fsym(tmpabtets[1], tmpabtets[0]); // [d,b,e,c]
} // if (edgepivot == 2)
// Recover the flipped edge ([c,b] or [a,c]).
flipnm_post(tmpabtets, n1, 2, edgepivot, fc);
// Insert the two recovered tets into Star(ab).
for (j = n - 2; j >= i; j--) {
abtets[j + 1] = abtets[j]; // Downshift
}
if (edgepivot == 1) {
// tmpabtets[0] is [c,b,d,a] ==> contains [a,b]
// tmpabtets[1] is [c,b,a,e] ==> contains [a,b]
// tmpabtets[2] is [c,b,e,d]
fliptets[0] = tmpabtets[1];
enextself(fliptets[0]);
esymself(fliptets[0]); // [a,b,e,c]
fliptets[1] = tmpabtets[0];
esymself(fliptets[1]);
eprevself(fliptets[1]); // [a,b,c,d]
} else {
// tmpabtets[0] is [a,c,d,b] ==> contains [a,b]
// tmpabtets[1] is [a,c,b,e] ==> contains [a,b]
// tmpabtets[2] is [a,c,e,d]
fliptets[0] = tmpabtets[1];
eprevself(fliptets[0]);
esymself(fliptets[0]); // [a,b,e,c]
fliptets[1] = tmpabtets[0];
esymself(fliptets[1]);
enextself(fliptets[1]); // [a,b,c,d]
} // edgepivot == 2
for (j = 0; j < 2; j++) {
increaseelemcounter(fliptets[j]);
}
// Insert the two recovered tets into Star(ab).
abtets[(i - 1 + n) % n] = fliptets[0];
abtets[i] = fliptets[1];
nn++;
// Release the allocated spaces.
delete [] tmpabtets;
} // if (unflip)
} // if (nn > 2)
if (!fc->unflip) {
// The flips are not reversed. The current Star(ab) can not be
// further reduced. Return its size (# of tets).
return nn;
}
// unflip is set.
// 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 {
// 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.
delete [] tmpabtets;
}
} // i
} // if (level...)
} // if (reflexlinkedgecount > 0)
} else {
// Check if a 3-to-2 flip is possible.
// Let the three apexes be c, d,and e. Hull tets may be involved. If so,
// we rearrange them such that the vertex e is dummypoint.
hullflag = 0;
if (apex(abtets[0]) == dummypoint) {
pc = apex(abtets[1]);
pd = apex(abtets[2]);
pe = apex(abtets[0]);
hullflag = 1;
} else if (apex(abtets[1]) == dummypoint) {
pc = apex(abtets[2]);
pd = apex(abtets[0]);
pe = apex(abtets[1]);
hullflag = 2;
} else {
pc = apex(abtets[0]);
pd = apex(abtets[1]);
pe = apex(abtets[2]);
hullflag = (pe == dummypoint) ? 3 : 0;
}
reducflag = 0;
rejflag = 0;
if (hullflag == 0) {
// Make sure that no inverted tet will be created, i.e. the new tets
// [d,c,e,a] and [c,d,e,b] must be valid tets.
ori = orient3d(pd, pc, pe, pa);
if (ori < 0) {
ori = orient3d(pc, pd, pe, pb);
if (ori < 0) {
reducflag = 1;
}
}
} else {
// [a,b] is a hull edge.
// Note: This can happen when it is in the middle of a 4-to-4 flip.
// Note: [a,b] may even be a non-convex hull edge.
if (!nonconvex) {
// The mesh is convex, only do flip if it is a coplanar hull edge.
ori = orient3d(pa, pb, pc, pd);
if (ori == 0) {
reducflag = 1;
}
} else { // nonconvex
reducflag = 1;
}
if (reducflag == 1) {
// [a,b], [a,b,c] and [a,b,d] are on the convex hull.
// Make sure that no inverted tet will be created.
point searchpt = NULL, chkpt;
REAL bigvol = 0.0, ori1, ori2;
// Search an interior vertex which is an apex of edge [c,d].
// In principle, it can be arbitrary interior vertex. To avoid
// numerical issue, we choose the vertex which belongs to a tet
// 't' at edge [c,d] and 't' has the biggest volume.
fliptets[0] = abtets[hullflag % 3]; // [a,b,c,d].
eorgoppoself(fliptets[0]); // [d,c,b,a]
spintet = fliptets[0];
while (1) {
fnextself(spintet);
chkpt = oppo(spintet);
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;
}
}
}
if (searchpt != NULL) {
// Now valid the configuration.
ori1 = orient3d(pd, pc, searchpt, pa);
ori2 = orient3d(pd, pc, searchpt, pb);
if (ori1 * ori2 >= 0.0) {
reducflag = 0; // Not valid.
} else {
ori1 = orient3d(pa, pb, searchpt, pc);
ori2 = orient3d(pa, pb, searchpt, pd);
if (ori1 * ori2 >= 0.0) {
reducflag = 0; // Not valid.
}
}
} else {
// No valid searchpt is found.
reducflag = 0; // Do not flip it.
}
} // if (reducflag == 1)
} // if (hullflag == 1)
if (reducflag) {
// A 3-to-2 flip is possible.
if (checksubfaceflag) {
// This edge (must not be a segment) can be flipped ONLY IF it belongs
// to either 0 or 2 subfaces. In the latter case, a 2-to-2 flip in
// the surface mesh will be automatically performed within the
// 3-to-2 flip.
nn = 0;
edgepivot = -1; // Re-use it.
for (j = 0; j < 3; j++) {
if (issubface(abtets[j])) {
nn++; // Found a subface.
} else {
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
// recovered. This edge should not be flipped at this moment.
rejflag = 1;
} else if (nn == 2) {
// Found two subfaces. A 2-to-2 flip is possible. Validate it.
// Below we check if the two faces [p,q,a] and [p,q,b] are subfaces.
eorgoppo(abtets[(edgepivot + 1) % 3], spintet); // [q,p,b,a]
if (issubface(spintet)) {
rejflag = 1; // Conflict to a 2-to-2 flip.
} else {
esymself(spintet);
if (issubface(spintet)) {
rejflag = 1; // Conflict to a 2-to-2 flip.
}
}
}
}
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
// the face [a,b,c] will be flipped and the edge [e,d] will be
// created. The two new tets are [a,b,c,d] and [b,a,c,e].
rejflag = checkflipeligibility(2, pc, pd, pe, pb, pa, level,
abedgepivot, fc);
}
if (!rejflag) {
// Do flip: [a,b] => [c,d,e]
flip32(abtets, hullflag, fc);
if (fc->remove_ndelaunay_edge) {
if (level == 0) {
// It is the desired removing edge. Check if we have improved
// the objective function.
if ((fc->tetprism_vol_sum >= 0.0) ||
(fabs(fc->tetprism_vol_sum) < fc->bak_tetprism_vol)) {
// No improvement! flip back: [c,d,e] => [a,b].
flip23(abtets, hullflag, fc);
// Increase the element counter -- They are in cavity.
for (j = 0; j < 3; j++) {
increaseelemcounter(abtets[j]);
}
return 3;
}
} // if (level == 0)
}
if (fc->collectnewtets) {
// Collect new tets.
if (level == 0) {
// Push the two new tets into stack.
for (j = 0; j < 2; j++) {
cavetetlist->newindex((void **) &parytet);
*parytet = abtets[j];
}
} else {
// Only one of the new tets is collected. The other one is inside
// the reduced edge star. 'abedgepivot' is either '1' or '2'.
cavetetlist->newindex((void **) &parytet);
if (abedgepivot == 1) { // [c,b]
*parytet = abtets[1];
} else {
assert(abedgepivot == 2); // [a,c]
*parytet = abtets[0];
}
}
} // if (fc->collectnewtets)
return 2;
}
} // if (reducflag)
} // if (n == 3)
// The current (reduced) Star size.
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. //
// //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::flipnm_post(triface* abtets, int n, int nn, int abedgepivot,
flipconstraints* fc)
{
triface fliptets[3], flipface;
triface *tmpabtets;
int fliptype;
int edgepivot;
int t, n1;
int i, j;
if (nn == 2) {
// The edge [a,b] has been flipped.
// 'abtets[0]' is [c,d,e,b] or [#,#,#,b].
// 'abtets[1]' is [d,c,e,a] or [#,#,#,a].
if (fc->unflip) {
// Do a 2-to-3 flip to recover the edge [a,b]. There may be hull tets.
flip23(abtets, 1, fc);
if (fc->collectnewtets) {
// Pop up new (flipped) tets from the stack.
if (abedgepivot == 0) {
// Two new tets were collected.
cavetetlist->objects -= 2;
} else {
// Only one of the two new tets was collected.
cavetetlist->objects -= 1;
}
}
}
// The initial size of Star(ab) is 3.
nn++;
}
// Walk through the performed flips.
for (i = nn; i < n; i++) {
// At the beginning of each step 'i', the size of the Star([a,b]) is 'i'.
// At the end of this step, the size of the Star([a,b]) is 'i+1'.
// The sizes of the Link([a,b]) are the same.
fliptype = ((abtets[i].ver >> 4) & 3); // 0, 1, or 2.
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) {
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.,
// it is created by a 2-to-3 flip [a,b,c] => [e,d].
fliptets[0] = abtets[((t - 1) + i) % i]; // [a,b,e,d]
eprevself(fliptets[0]);
esymself(fliptets[0]);
enextself(fliptets[0]); // [e,d,a,b]
fnext(fliptets[0], fliptets[1]); // [e,d,b,c]
fnext(fliptets[1], fliptets[2]); // [e,d,c,a]
// Do a 3-to-2 flip: [e,d] => [a,b,c].
// NOTE: hull tets may be invloved.
flip32(fliptets, 1, fc);
// Expand the array 'abtets', maintain the original order.
// The new array length is (i+1).
for (j = i - 1; j >= t; j--) {
abtets[j + 1] = abtets[j]; // Downshift
}
// The tet abtets[(t-1)%i] is deleted. Insert the two new tets
// 'fliptets[0]' [a,b,c,d] and 'fliptets[1]' [b,a,c,e] into
// the (t-1)-th and t-th entries, respectively.
esym(fliptets[1], abtets[((t-1) + (i+1)) % (i+1)]); // [a,b,e,c]
abtets[t] = fliptets[0]; // [a,b,c,d]
if (fc->collectnewtets) {
// Pop up two (flipped) tets from the stack.
cavetetlist->objects -= 2;
}
}
} else if (fliptype == 2) {
tmpabtets = (triface *) (abtets[i].tet);
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) {
printf(" Recover a %d-to-m flip at e[%d] of f[%d].\n", n1,
edgepivot, t);
}
// Recover the flipped edge ([c,b] or [a,c]).
// abtets[(t - 1 + i) % i] 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
// Star(ab). Use it to recover the flipped edge.
if (edgepivot == 1) {
// The flip edge is [c,b].
tmpabtets[0] = abtets[(t - 1 + i) % i]; // [a,b,e,d]
eprevself(tmpabtets[0]);
esymself(tmpabtets[0]);
eprevself(tmpabtets[0]); // [d,a,e,b]
fsym(tmpabtets[0], tmpabtets[1]); // [a,d,e,c]
} else {
// The flip edge is [a,c].
tmpabtets[1] = abtets[(t - 1 + i) % i]; // [a,b,e,d]
enextself(tmpabtets[1]);
esymself(tmpabtets[1]);
enextself(tmpabtets[1]); // [b,d,e,a]
fsym(tmpabtets[1], tmpabtets[0]); // [d,b,e,c]
} // if (edgepivot == 2)
// Do a n1-to-m1 flip to recover the flipped edge ([c,b] or [a,c]).
flipnm_post(tmpabtets, n1, 2, edgepivot, fc);
// Insert the two recovered tets into the original Star(ab).
for (j = i - 1; j >= t; j--) {
abtets[j + 1] = abtets[j]; // Downshift
}
if (edgepivot == 1) {
// tmpabtets[0] is [c,b,d,a] ==> contains [a,b]
// tmpabtets[1] is [c,b,a,e] ==> contains [a,b]
// tmpabtets[2] is [c,b,e,d]
fliptets[0] = tmpabtets[1];
enextself(fliptets[0]);
esymself(fliptets[0]); // [a,b,e,c]
fliptets[1] = tmpabtets[0];
esymself(fliptets[1]);
eprevself(fliptets[1]); // [a,b,c,d]
} else {
// tmpabtets[0] is [a,c,d,b] ==> contains [a,b]
// tmpabtets[1] is [a,c,b,e] ==> contains [a,b]
// tmpabtets[2] is [a,c,e,d]
fliptets[0] = tmpabtets[1];
eprevself(fliptets[0]);
esymself(fliptets[0]); // [a,b,e,c]
fliptets[1] = tmpabtets[0];
esymself(fliptets[1]);
enextself(fliptets[1]); // [a,b,c,d]
} // edgepivot == 2
// Insert the two recovered tets into Star(ab).
abtets[((t-1) + (i+1)) % (i+1)] = fliptets[0];
abtets[t] = fliptets[1];
}
else {
// Only free the spaces.
flipnm_post(tmpabtets, n1, 2, edgepivot, fc);
} // if (!unflip)
if (b->verbose > 2) {
printf(" Release %d spaces at f[%d].\n", n1, i);
}
delete [] tmpabtets;
}
} // i
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. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh,
face *splitseg, insertvertexflags *ivf)
{
arraypool *swaplist;
triface *cavetet, spintet, neightet, neineitet, *parytet;
triface oldtet, newtet, newneitet;
face checksh, neighsh, *parysh;
face checkseg, *paryseg;
point *pts, pa, pb, pc, *parypt;
enum locateresult loc = OUTSIDE;
REAL sign, ori;
REAL attrib, volume;
bool enqflag;
int t1ver;
int i, j, k, s;
if (b->verbose > 2) {
printf(" Insert point %d\n", pointmark(insertpt));
}
// 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;
}
}
// Locate the point.
loc = locate(insertpt, searchtet);
}
ivf->iloc = (int) loc; // The return value.
if (b->weighted) {
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++;
ivf->iloc = (int) NONREGULAR;
return 0;
}
}
}
// Create the initial cavity C(p) which contains all tetrahedra that
// intersect p. It may include 1, 2, or n tetrahedra.
// If p lies on a segment or subface, also create the initial sub-cavity
// sC(p) which contains all subfaces (and segment) which intersect p.
if (loc == OUTSIDE) {
flip14count++;
// The current hull will be enlarged.
// Add four adjacent boundary tets into list.
for (i = 0; i < 4; i++) {
decode(searchtet->tet[i], neightet);
neightet.ver = epivot[neightet.ver];
cavebdrylist->newindex((void **) &parytet);
*parytet = neightet;
}
infect(*searchtet);
caveoldtetlist->newindex((void **) &parytet);
*parytet = *searchtet;
} else if (loc == INTETRAHEDRON) {
flip14count++;
// Add four adjacent boundary tets into list.
for (i = 0; i < 4; i++) {
decode(searchtet->tet[i], neightet);
neightet.ver = epivot[neightet.ver];
cavebdrylist->newindex((void **) &parytet);
*parytet = neightet;
}
infect(*searchtet);
caveoldtetlist->newindex((void **) &parytet);
*parytet = *searchtet;
} else if (loc == ONFACE) {
flip26count++;
// Add six adjacent boundary tets into list.
j = (searchtet->ver & 3); // The current face number.
for (i = 1; i < 4; i++) {
decode(searchtet->tet[(j + i) % 4], neightet);
neightet.ver = epivot[neightet.ver];
cavebdrylist->newindex((void **) &parytet);
*parytet = neightet;
}
decode(searchtet->tet[j], spintet);
j = (spintet.ver & 3); // The current face number.
for (i = 1; i < 4; i++) {
decode(spintet.tet[(j + i) % 4], neightet);
neightet.ver = epivot[neightet.ver];
cavebdrylist->newindex((void **) &parytet);
*parytet = neightet;
}
infect(spintet);
caveoldtetlist->newindex((void **) &parytet);
*parytet = spintet;
infect(*searchtet);
caveoldtetlist->newindex((void **) &parytet);
*parytet = *searchtet;
if (ivf->splitbdflag) {
if ((splitsh != NULL) && (splitsh->sh != NULL)) {
// Create the initial sub-cavity sC(p).
smarktest(*splitsh);
caveshlist->newindex((void **) &parysh);
*parysh = *splitsh;
}
} // if (splitbdflag)
} else if (loc == ONEDGE) {
flipn2ncount++;
// Add all adjacent boundary tets into list.
spintet = *searchtet;
while (1) {
eorgoppo(spintet, neightet);
decode(neightet.tet[neightet.ver & 3], neightet);
neightet.ver = epivot[neightet.ver];
cavebdrylist->newindex((void **) &parytet);
*parytet = neightet;
edestoppo(spintet, neightet);
decode(neightet.tet[neightet.ver & 3], neightet);
neightet.ver = epivot[neightet.ver];
cavebdrylist->newindex((void **) &parytet);
*parytet = neightet;
infect(spintet);
caveoldtetlist->newindex((void **) &parytet);
*parytet = spintet;
fnextself(spintet);
if (spintet.tet == searchtet->tet) break;
} // while (1)
if (ivf->splitbdflag) {
// Create the initial sub-cavity sC(p).
if ((splitseg != NULL) && (splitseg->sh != NULL)) {
smarktest(*splitseg);
splitseg->shver = 0;
spivot(*splitseg, *splitsh);
}
if (splitsh != NULL) {
if (splitsh->sh != NULL) {
// Collect all subfaces share at this edge.
pa = sorg(*splitsh);
neighsh = *splitsh;
while (1) {
// Adjust the origin of its edge to be 'pa'.
if (sorg(neighsh) != pa) {
sesymself(neighsh);
}
// Add this face into list (in B-W cavity).
smarktest(neighsh);
caveshlist->newindex((void **) &parysh);
*parysh = neighsh;
// Add this face into face-at-splitedge list.
cavesegshlist->newindex((void **) &parysh);
*parysh = neighsh;
// Go to the next face at the edge.
spivotself(neighsh);
// Stop if all faces at the edge have been visited.
if (neighsh.sh == splitsh->sh) break;
if (neighsh.sh == NULL) break;
} // while (1)
} // if (not a dangling segment)
}
} // if (splitbdflag)
} 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.
return 0;
}
if (ivf->assignmeshsize) {
// Assign mesh size for the new point.
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);
if (bgmloc != (int) OUTSIDE) {
insertpt[pointmtrindex] =
bgm->getpointmeshsize(insertpt, &neightet, bgmloc);
setpoint2bgmtet(insertpt, bgm->encode(neightet));
}
} else {
insertpt[pointmtrindex] = getpointmeshsize(insertpt,searchtet,(int)loc);
}
} // if (assignmeshsize)
if (ivf->bowywat) {
// Update the cavity C(p) using the Bowyer-Watson algorithm.
swaplist = cavetetlist;
cavetetlist = cavebdrylist;
cavebdrylist = swaplist;
for (i = 0; i < cavetetlist->objects; i++) {
// 'cavetet' is an adjacent tet at outside of the cavity.
cavetet = (triface *) fastlookup(cavetetlist, i);
// The tet may be tested and included in the (enlarged) cavity.
if (!infected(*cavetet)) {
// Check for two possible cases for this tet:
// (1) It is a cavity tet, or
// (2) it is a cavity boundary face.
enqflag = false;
if (!marktested(*cavetet)) {
// Do Delaunay (in-sphere) test.
pts = (point *) cavetet->tet;
if (pts[7] != dummypoint) {
// A volume tet. Operate on it.
if (b->weighted) {
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]);
} else {
sign = insphere_s(pts[4], pts[5], pts[6], pts[7], insertpt);
}
enqflag = (sign < 0.0);
} else {
if (!nonconvex) {
// Test if this hull face is visible by the new point.
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);
//}
} 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)
// of this hull face is Delaunay or not.
decode(cavetet->tet[3], neineitet);
if (!infected(neineitet)) {
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],
pts[7][3], insertpt[3]);
} else {
sign = insphere_s(pts[4],pts[5],pts[6],pts[7], insertpt);
}
enqflag = (sign < 0.0);
}
} else {
// The adjacent tet is non-Delaunay. The hull face is non-
// Delaunay as well. Include it in the cavity.
enqflag = true;
} // if (!infected(neineitet))
} // if (ori == 0.0)
} else {
// A hull face (must be a subface).
// We FIRST include it in the initial cavity if the adjacent tet
// (not faked) of this hull face is not Delaunay wrt p.
// Whether it belongs to the final cavity will be determined
// during the validation process. 'validflag'.
decode(cavetet->tet[3], neineitet);
if (!infected(neineitet)) {
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],
pts[7][3], insertpt[3]);
} else {
sign = insphere_s(pts[4],pts[5],pts[6],pts[7], insertpt);
}
enqflag = (sign < 0.0);
}
} else {
// The adjacent tet is non-Delaunay. The hull face is non-
// Delaunay as well. Include it in the cavity.
enqflag = true;
} // if (infected(neineitet))
} // if (nonconvex)
} // if (pts[7] != dummypoint)
marktest(*cavetet); // Only test it once.
} // if (!marktested(*cavetet))
if (enqflag) {
// Found a tet in the cavity. Put other three faces in check list.
k = (cavetet->ver & 3); // The current face number
for (j = 1; j < 4; j++) {
decode(cavetet->tet[(j + k) % 4], neightet);
cavetetlist->newindex((void **) &parytet);
*parytet = neightet;
}
infect(*cavetet);
caveoldtetlist->newindex((void **) &parytet);
*parytet = *cavetet;
} else {
// Found a boundary face of the cavity.
cavetet->ver = epivot[cavetet->ver];
cavebdrylist->newindex((void **) &parytet);
*parytet = *cavetet;
}
} // if (!infected(*cavetet))
} // i
cavetetlist->restart(); // Clear the working list.
} // if (ivf->bowywat)
if (checksubsegflag) {
// Collect all segments of C(p).
shellface *ssptr;
for (i = 0; i < caveoldtetlist->objects; i++) {
cavetet = (triface *) fastlookup(caveoldtetlist, i);
if ((ssptr = (shellface*) cavetet->tet[8]) != NULL) {
for (j = 0; j < 6; j++) {
if (ssptr[j]) {
sdecode(ssptr[j], checkseg);
if (!sinfected(checkseg)) {
sinfect(checkseg);
cavetetseglist->newindex((void **) &paryseg);
*paryseg = checkseg;
}
}
} // j
}
} // i
// Uninfect collected segments.
for (i = 0; i < cavetetseglist->objects; i++) {
paryseg = (face *) fastlookup(cavetetseglist, i);
suninfect(*paryseg);
}
if (ivf->rejflag & 1) {
// Reject this point if it encroaches upon any segment.
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;
}
} // i
if (encseglist->objects > 0) {
insertpoint_abort(splitseg, ivf);
ivf->iloc = (int) ENCSEGMENT;
return 0;
}
}
} // if (checksubsegflag)
if (checksubfaceflag) {
// Collect all subfaces of C(p).
shellface *sptr;
for (i = 0; i < caveoldtetlist->objects; i++) {
cavetet = (triface *) fastlookup(caveoldtetlist, i);
if ((sptr = (shellface*) cavetet->tet[9]) != NULL) {
for (j = 0; j < 4; j++) {
if (sptr[j]) {
sdecode(sptr[j], checksh);
if (!sinfected(checksh)) {
sinfect(checksh);
cavetetshlist->newindex((void **) &parysh);
*parysh = checksh;
}
}
} // j
}
} // i
// Uninfect collected subfaces.
for (i = 0; i < cavetetshlist->objects; i++) {
parysh = (face *) fastlookup(cavetetshlist, i);
suninfect(*parysh);
}
if (ivf->rejflag & 2) {
REAL rd, cent[3];
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 (encshlist->objects > 0) {
insertpoint_abort(splitseg, ivf);
ivf->iloc = (int) ENCSUBFACE;
return 0;
}
}
} // if (checksubfaceflag)
if ((ivf->iloc == (int) OUTSIDE) && ivf->refineflag) {
// The vertex lies outside of the domain. And it does not encroach
// upon any boundary segment or subface. Do not insert it.
insertpoint_abort(splitseg, ivf);
return 0;
}
if (ivf->splitbdflag) {
// The new point locates in surface mesh. Update the sC(p).
// We have already 'smarktested' the subfaces which directly intersect
// with p in 'caveshlist'. From them, we 'smarktest' their neighboring
// 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)) {
fsymself(neightet);
if (infected(neightet)) {
// This subface is inside C(p).
// Check if its diametrical circumsphere encloses 'p'.
// The purpose of this check is to avoid forming invalid
// subcavity in surface mesh.
sign = incircle3d(sorg(neighsh), sdest(neighsh),
sapex(neighsh), insertpt);
if (sign < 0) {
smarktest(neighsh);
caveshlist->newindex((void **) &parysh);
*parysh = neighsh;
}
}
}
}
}
senextself(checksh);
} // j
} // i
} // if (ivf->splitbdflag)
if (ivf->validflag) {
// Validate C(p) and update it if it is not star-shaped.
int cutcount = 0;
if (ivf->respectbdflag) {
// The initial cavity may include subfaces which are not on the facets
// being splitting. Find them and make them as boundary of C(p).
// Comment: We have already 'smarktested' the subfaces in sC(p). They
// are completely inside C(p).
for (i = 0; i < cavetetshlist->objects; i++) {
parysh = (face *) fastlookup(cavetetshlist, i);
stpivot(*parysh, neightet);
if (infected(neightet)) {
fsymself(neightet);
if (infected(neightet)) {
// Found a subface inside C(p).
if (!smarktested(*parysh)) {
// It is possible that this face is a boundary subface.
// Check if it is a hull face.
//assert(apex(neightet) != dummypoint);
if (oppo(neightet) != dummypoint) {
fsymself(neightet);
}
if (oppo(neightet) != dummypoint) {
ori = orient3d(org(neightet), dest(neightet), apex(neightet),
insertpt);
if (ori < 0) {
// A visible face, get its neighbor face.
fsymself(neightet);
ori = -ori; // It must be invisible by p.
}
} else {
// A hull tet. It needs to be cut.
ori = 1;
}
// Cut this tet if it is either invisible by or coplanar with p.
if (ori >= 0) {
uninfect(neightet);
unmarktest(neightet);
cutcount++;
neightet.ver = epivot[neightet.ver];
cavebdrylist->newindex((void **) &parytet);
*parytet = neightet;
// Add three new faces to find new boundaries.
for (j = 0; j < 3; j++) {
esym(neightet, neineitet);
neineitet.ver = epivot[neineitet.ver];
cavebdrylist->newindex((void **) &parytet);
*parytet = neineitet;
enextself(neightet);
}
} // if (ori >= 0)
}
}
}
} // i
// The initial cavity may include segments in its interior. We need to
// Update the cavity so that these segments are on the boundary of
// the cavity.
for (i = 0; i < cavetetseglist->objects; i++) {
paryseg = (face *) fastlookup(cavetetseglist, i);
// Check this segment if it is not a splitting segment.
if (!smarktested(*paryseg)) {
sstpivot1(*paryseg, neightet);
spintet = neightet;
while (1) {
if (!infected(spintet)) break;
fnextself(spintet);
if (spintet.tet == neightet.tet) break;
}
if (infected(spintet)) {
// Find an adjacent tet at this segment such that both faces
// at this segment are not visible by p.
pa = org(neightet);
pb = dest(neightet);
spintet = neightet;
j = 0;
while (1) {
// Check if this face is visible by p.
pc = apex(spintet);
if (pc != dummypoint) {
ori = orient3d(pa, pb, pc, insertpt);
if (ori >= 0) {
// Not visible. Check another face in this tet.
esym(spintet, neineitet);
pc = apex(neineitet);
if (pc != dummypoint) {
ori = orient3d(pb, pa, pc, insertpt);
if (ori >= 0) {
// Not visible. Found this face.
j = 1; // Flag that it is found.
break;
}
}
}
}
fnextself(spintet);
if (spintet.tet == neightet.tet) break;
}
if (j == 0) {
// Not found such a face.
assert(0); // debug this case.
}
neightet = spintet;
if (b->verbose > 3) {
printf(" Cut tet (%d, %d, %d, %d)\n",
pointmark(org(neightet)), pointmark(dest(neightet)),
pointmark(apex(neightet)), pointmark(oppo(neightet)));
}
uninfect(neightet);
unmarktest(neightet);
cutcount++;
neightet.ver = epivot[neightet.ver];
cavebdrylist->newindex((void **) &parytet);
*parytet = neightet;
// Add three new faces to find new boundaries.
for (j = 0; j < 3; j++) {
esym(neightet, neineitet);
neineitet.ver = epivot[neineitet.ver];
cavebdrylist->newindex((void **) &parytet);
*parytet = neineitet;
enextself(neightet);
}
}
}
} // i
} // if (ivf->respectbdflag)
// Update the cavity by removing invisible faces until it is star-shaped.
for (i = 0; i < cavebdrylist->objects; i++) {
cavetet = (triface *) fastlookup(cavebdrylist, i);
// 'cavetet' is an exterior tet adjacent to the cavity.
// Check if its neighbor is inside C(p).
fsym(*cavetet, neightet);
if (infected(neightet)) {
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);
enqflag = (ori > 0);
// Comment: if ori == 0 (coplanar case), we also cut the tet.
} else {
// It is a hull face. And its adjacent tet (at inside of the
// domain) has been cut from the cavity. Cut it as well.
//assert(nonconvex);
enqflag = false;
}
} else {
enqflag = true; // A hull edge.
}
if (enqflag) {
// This face is valid, save it.
cavetetlist->newindex((void **) &parytet);
*parytet = *cavetet;
} else {
uninfect(neightet);
unmarktest(neightet);
cutcount++;
// Add three new faces to find new boundaries.
for (j = 0; j < 3; j++) {
esym(neightet, neineitet);
neineitet.ver = epivot[neineitet.ver];
cavebdrylist->newindex((void **) &parytet);
*parytet = neineitet;
enextself(neightet);
}
// 'cavetet' is not on the cavity boundary anymore.
unmarktest(*cavetet);
}
} else {
// 'cavetet' is not on the cavity boundary anymore.
unmarktest(*cavetet);
}
} // i
if (cutcount > 0) {
// The cavity has been updated.
// Update the cavity boundary faces.
cavebdrylist->restart();
for (i = 0; i < cavetetlist->objects; i++) {
cavetet = (triface *) fastlookup(cavetetlist, i);
// 'cavetet' was an exterior tet adjacent to the cavity.
fsym(*cavetet, neightet);
if (infected(neightet)) {
// It is a cavity boundary face.
cavebdrylist->newindex((void **) &parytet);
*parytet = *cavetet;
} else {
// Not a cavity boundary face.
unmarktest(*cavetet);
}
}
// Update the list of old tets.
cavetetlist->restart();
for (i = 0; i < caveoldtetlist->objects; i++) {
cavetet = (triface *) fastlookup(caveoldtetlist, i);
if (infected(*cavetet)) {
cavetetlist->newindex((void **) &parytet);
*parytet = *cavetet;
}
}
// Swap 'cavetetlist' and 'caveoldtetlist'.
swaplist = caveoldtetlist;
caveoldtetlist = cavetetlist;
cavetetlist = swaplist;
// The cavity should contain at least one tet.
if (caveoldtetlist->objects == 0l) {
insertpoint_abort(splitseg, ivf);
ivf->iloc = (int) BADELEMENT;
return 0;
}
if (ivf->splitbdflag) {
int cutshcount = 0;
// Update the sub-cavity sC(p).
for (i = 0; i < caveshlist->objects; i++) {
parysh = (face *) fastlookup(caveshlist, i);
if (smarktested(*parysh)) {
enqflag = false;
stpivot(*parysh, neightet);
if (infected(neightet)) {
fsymself(neightet);
if (infected(neightet)) {
enqflag = true;
}
}
if (!enqflag) {
sunmarktest(*parysh);
// Use the last entry of this array to fill this entry.
j = caveshlist->objects - 1;
checksh = * (face *) fastlookup(caveshlist, j);
*parysh = checksh;
cutshcount++;
caveshlist->objects--; // The list is shrinked.
i--;
}
}
}
if (cutshcount > 0) {
i = 0; // Count the number of invalid subfaces/segments.
// Valid the updated sub-cavity sC(p).
if (loc == ONFACE) {
if ((splitsh != NULL) && (splitsh->sh != NULL)) {
// The to-be split subface should be in sC(p).
if (!smarktested(*splitsh)) i++;
}
} else if (loc == ONEDGE) {
if ((splitseg != NULL) && (splitseg->sh != NULL)) {
// The to-be split segment should be in sC(p).
if (!smarktested(*splitseg)) i++;
}
if ((splitsh != NULL) && (splitsh->sh != NULL)) {
// All subfaces at this edge should be in sC(p).
pa = sorg(*splitsh);
neighsh = *splitsh;
while (1) {
// Adjust the origin of its edge to be 'pa'.
if (sorg(neighsh) != pa) {
sesymself(neighsh);
}
// Add this face into list (in B-W cavity).
if (!smarktested(neighsh)) i++;
// Go to the next face at the edge.
spivotself(neighsh);
// Stop if all faces at the edge have been visited.
if (neighsh.sh == splitsh->sh) break;
if (neighsh.sh == NULL) break;
} // while (1)
}
}
if (i > 0) {
// The updated sC(p) is invalid. Do not insert this vertex.
insertpoint_abort(splitseg, ivf);
ivf->iloc = (int) BADELEMENT;
return 0;
}
} // if (cutshcount > 0)
} // if (ivf->splitbdflag)
} // if (cutcount > 0)
} // if (ivf->validflag)
if (ivf->refineflag) {
// 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)) ||
((ivf->refineflag == 2) && !smarktested(ivf->refinesh))) {
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);
}
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;
}
}
}
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) {
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;
}
} else { // loc == ENCVERTEX
// The point lies inside the protection ball.
setpoint2ppt(insertpt, *pts);
insertpoint_abort(splitseg, ivf);
ivf->iloc = (int) loc;
return 0;
}
}
} // if (b->plc && (loc != INSTAR))
if (b->weighted || ivf->cdtflag || ivf->smlenflag
) {
// 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++) {
cavetet = (triface *) fastlookup(caveoldtetlist, i);
//assert(infected(*cavetet));
pts = (point *) &(cavetet->tet[4]);
for (j = 0; j < 4; j++) {
if (pts[j] != dummypoint) {
if (!pinfected(pts[j])) {
pinfect(pts[j]);
cavetetvertlist->newindex((void **) &parypt);
*parypt = pts[j];
}
}
} // j
} // i
// Uninfect all collected (cavity) vertices.
for (i = 0; i < cavetetvertlist->objects; i++) {
parypt = (point *) fastlookup(cavetetvertlist, i);
puninfect(*parypt);
}
if (ivf->smlenflag) {
REAL len;
// Get the length of the shortest edge connecting to 'newpt'.
parypt = (point *) fastlookup(cavetetvertlist, 0);
ivf->smlen = distance(*parypt, insertpt);
ivf->parentpt = *parypt;
for (i = 1; i < cavetetvertlist->objects; i++) {
parypt = (point *) fastlookup(cavetetvertlist, i);
len = distance(*parypt, insertpt);
if (len < ivf->smlen) {
ivf->smlen = len;
ivf->parentpt = *parypt;
}
}
}
}
if (ivf->cdtflag) {
// Unmark tets.
for (i = 0; i < caveoldtetlist->objects; i++) {
cavetet = (triface *) fastlookup(caveoldtetlist, i);
unmarktest(*cavetet);
}
for (i = 0; i < cavebdrylist->objects; i++) {
cavetet = (triface *) fastlookup(cavebdrylist, i);
unmarktest(*cavetet);
}
// Clean up arrays which are not needed.
cavetetlist->restart();
if (checksubsegflag) {
cavetetseglist->restart();
}
if (checksubfaceflag) {
cavetetshlist->restart();
}
return 1;
}
// Before re-mesh C(p). Process the segments and subfaces which are on the
// boundary of C(p). Make sure that each such segment or subface is
// connecting to a tet outside C(p). So we can re-connect them to the
// new tets inside the C(p) later.
if (checksubsegflag) {
for (i = 0; i < cavetetseglist->objects; i++) {
paryseg = (face *) fastlookup(cavetetseglist, i);
// Operate on it if it is not the splitting segment, i.e., in sC(p).
if (!smarktested(*paryseg)) {
// Check if the segment is inside the cavity.
// 'j' counts the num of adjacent tets of this seg.
// 'k' counts the num of adjacent tets which are 'sinfected'.
j = k = 0;
sstpivot1(*paryseg, neightet);
spintet = neightet;
while (1) {
j++;
if (!infected(spintet)) {
neineitet = spintet; // An outer tet. Remember it.
} else {
k++; // An in tet.
}
fnextself(spintet);
if (spintet.tet == neightet.tet) break;
}
// assert(j > 0);
if (k == 0) {
// The segment is not connect to C(p) anymore. Remove it by
// Replacing it by the last entry of this list.
s = cavetetseglist->objects - 1;
checkseg = * (face *) fastlookup(cavetetseglist, s);
*paryseg = checkseg;
cavetetseglist->objects--;
i--;
} else if (k < j) {
// The segment is on the boundary of C(p).
sstbond1(*paryseg, neineitet);
} else { // k == j
// The segment is inside C(p).
if (!ivf->splitbdflag) {
checkseg = *paryseg;
sinfect(checkseg); // Flag it as an interior segment.
caveencseglist->newindex((void **) &paryseg);
*paryseg = checkseg;
} else {
assert(0); // Not possible.
}
}
} else {
// assert(smarktested(*paryseg));
// Flag it as an interior segment. Do not queue it, since it will
// be deleted after the segment splitting.
sinfect(*paryseg);
}
} // i
} // if (checksubsegflag)
if (checksubfaceflag) {
for (i = 0; i < cavetetshlist->objects; i++) {
parysh = (face *) fastlookup(cavetetshlist, i);
// Operate on it if it is not inside the sub-cavity sC(p).
if (!smarktested(*parysh)) {
// Check if this subface is inside the cavity.
k = 0;
for (j = 0; j < 2; j++) {
stpivot(*parysh, neightet);
if (!infected(neightet)) {
checksh = *parysh; // Remember this side.
} else {
k++;
}
sesymself(*parysh);
}
if (k == 0) {
// The subface is not connected to C(p). Remove it.
s = cavetetshlist->objects - 1;
checksh = * (face *) fastlookup(cavetetshlist, s);
*parysh = checksh;
cavetetshlist->objects--;
i--;
} else if (k == 1) {
// This side is the outer boundary of C(p).
*parysh = checksh;
} else { // k == 2
if (!ivf->splitbdflag) {
checksh = *parysh;
sinfect(checksh); // Flag it.
caveencshlist->newindex((void **) &parysh);
*parysh = checksh;
} else {
assert(0); // Not possible.
}
}
} else {
// assert(smarktested(*parysh));
// Flag it as an interior subface. Do not queue it. It will be
// deleted after the facet point insertion.
sinfect(*parysh);
}
} // i
} // if (checksubfaceflag)
// Create new tetrahedra to fill the cavity.
for (i = 0; i < cavebdrylist->objects; i++) {
cavetet = (triface *) fastlookup(cavebdrylist, i);
neightet = *cavetet;
unmarktest(neightet); // Unmark it.
// Get the oldtet (inside the cavity).
fsym(neightet, oldtet);
if (apex(neightet) != dummypoint) {
// Create a new tet in the cavity.
maketetrahedron(&newtet);
setorg(newtet, dest(neightet));
setdest(newtet, org(neightet));
setapex(newtet, apex(neightet));
setoppo(newtet, insertpt);
} else {
// Create a new hull tet.
hullsize++;
maketetrahedron(&newtet);
setorg(newtet, org(neightet));
setdest(newtet, dest(neightet));
setapex(newtet, insertpt);
setoppo(newtet, dummypoint); // It must opposite to face 3.
// Adjust back to the cavity bounday face.
esymself(newtet);
}
// The new tet inherits attribtes from the old tet.
for (j = 0; j < numelemattrib; j++) {
attrib = elemattribute(oldtet.tet, j);
setelemattribute(newtet.tet, j, attrib);
}
if (b->varvolume) {
volume = volumebound(oldtet.tet);
setvolumebound(newtet.tet, volume);
}
// Connect newtet <==> neightet, this also disconnect the old bond.
bond(newtet, neightet);
// oldtet still connects to neightet.
*cavetet = oldtet; // *cavetet = newtet;
} // i
// Set a handle for speeding point location.
recenttet = newtet;
//setpoint2tet(insertpt, encode(newtet));
setpoint2tet(insertpt, (tetrahedron) (newtet.tet));
// Re-use this list to save new interior cavity faces.
cavetetlist->restart();
// Connect adjacent new tetrahedra together.
for (i = 0; i < cavebdrylist->objects; i++) {
cavetet = (triface *) fastlookup(cavebdrylist, i);
// cavtet is an oldtet, get the newtet at this face.
oldtet = *cavetet;
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, newneitet);
esymself(newneitet);
assert(newneitet.tet[newneitet.ver & 3] == NULL);
bond(neightet, newneitet);
if (ivf->lawson > 1) {
cavetetlist->newindex((void **) &parytet);
*parytet = neightet;
}
}
//setpoint2tet(org(newtet), encode(newtet));
setpoint2tet(org(newtet), (tetrahedron) (newtet.tet));
enextself(newtet);
enextself(oldtet);
}
*cavetet = newtet; // Save the new tet.
} // i
if (checksubfaceflag) {
// Connect subfaces on the boundary of the cavity to the new tets.
for (i = 0; i < cavetetshlist->objects; i++) {
parysh = (face *) fastlookup(cavetetshlist, i);
// Connect it if it is not a missing subface.
if (!sinfected(*parysh)) {
stpivot(*parysh, neightet);
fsym(neightet, spintet);
sesymself(*parysh);
tsbond(spintet, *parysh);
}
}
}
if (checksubsegflag) {
// Connect segments on the boundary of the cavity to the new tets.
for (i = 0; i < cavetetseglist->objects; i++) {
paryseg = (face *) fastlookup(cavetetseglist, i);
// Connect it if it is not a missing segment.
if (!sinfected(*paryseg)) {
sstpivot1(*paryseg, neightet);
spintet = neightet;
while (1) {
tssbond1(spintet, *paryseg);
fnextself(spintet);
if (spintet.tet == neightet.tet) break;
}
}
}
}
if (((splitsh != NULL) && (splitsh->sh != NULL)) ||
((splitseg != NULL) && (splitseg->sh != NULL))) {
// Split a subface or a segment.
sinsertvertex(insertpt, splitsh, splitseg, ivf->sloc, ivf->sbowywat, 0);
}
if (checksubfaceflag) {
if (ivf->splitbdflag) {
// Recover new subfaces in C(p).
for (i = 0; i < caveshbdlist->objects; i++) {
// Get an old subface at edge [a, b].
parysh = (face *) fastlookup(caveshbdlist, i);
spivot(*parysh, checksh); // The new subface [a, b, p].
// Do not recover a deleted new face (degenerated).
if (checksh.sh[3] != NULL) {
// 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);
}
// 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);
sesymself(checksh);
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.
for (i = 0; i < caveshbdlist->objects; i++) {
// Get an old subface at edge [a, b].
parysh = (face *) fastlookup(caveshbdlist, i);
spivot(*parysh, checksh); // The new subface [a, b, p].
// Do not recover a deleted new face (degenerated).
if (checksh.sh[3] != NULL) {
subfacstack->newindex((void **) &parysh);
*parysh = checksh;
}
}
// 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)) {
checksh = *parysh;
suninfect(checksh);
stdissolve(checksh); // Detach connections to old tets.
subfacstack->newindex((void **) &parysh);
*parysh = checksh;
}
}
}
} // if (checksubfaceflag)
if (checksubsegflag) {
if (ivf->splitbdflag) {
if (splitseg != NULL) {
// Recover the two new subsegments in C(p).
for (i = 0; i < cavesegshlist->objects; i++) {
paryseg = (face *) fastlookup(cavesegshlist, i);
// Insert this subsegment into C(p).
checkseg = *paryseg;
// Get the adjacent new subface.
checkseg.shver = 0;
spivot(checkseg, checksh);
if (checksh.sh != NULL) {
// Get the adjacent new tetrahedron.
stpivot(checksh, neightet);
} else {
// It's a dangling segment.
point2tetorg(sorg(checkseg), neightet);
finddirection(&neightet, sdest(checkseg));
assert(dest(neightet) == sdest(checkseg));
}
assert(!infected(neightet));
sstbond1(checkseg, neightet);
spintet = neightet;
while (1) {
tssbond1(spintet, checkseg);
fnextself(spintet);
if (spintet.tet == neightet.tet) break;
}
}
} // 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.
if (splitseg != NULL) {
// Queue two new subsegments in C(p) for recovery.
for (i = 0; i < cavesegshlist->objects; i++) {
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);
sstdissolve1(checkseg); // Detach connections to old tets.
s = randomnation(subsegstack->objects + 1);
subsegstack->newindex((void **) &paryseg);
*paryseg = * (face *) fastlookup(subsegstack, s);
paryseg = (face *) fastlookup(subsegstack, s);
*paryseg = checkseg;
}
}
}
} // if (checksubsegflag)
if (b->weighted
) {
// 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) {
printf(" Point #%d is non-regular after the insertion of #%d.\n",
pointmark(*pts), pointmark(insertpt));
}
setpointtype(*pts, NREGULARVERTEX);
nonregularcount++;
}
}
}
}
if (ivf->chkencflag & 1) {
// Queue all segment outside C(p).
for (i = 0; i < cavetetseglist->objects; i++) {
paryseg = (face *) fastlookup(cavetetseglist, i);
// Skip if it is the split segment.
if (!sinfected(*paryseg)) {
enqueuesubface(badsubsegs, paryseg);
}
}
if (splitseg != NULL) {
// Queue the two new subsegments inside C(p).
for (i = 0; i < cavesegshlist->objects; i++) {
paryseg = (face *) fastlookup(cavesegshlist, i);
enqueuesubface(badsubsegs, paryseg);
}
}
} // if (chkencflag & 1)
if (ivf->chkencflag & 2) {
// Queue all subfaces outside C(p).
for (i = 0; i < cavetetshlist->objects; i++) {
parysh = (face *) fastlookup(cavetetshlist, i);
// Skip if it is a split subface.
if (!sinfected(*parysh)) {
enqueuesubface(badsubfacs, parysh);
}
}
// Queue all new subfaces inside C(p).
for (i = 0; i < caveshbdlist->objects; i++) {
// Get an old subface at edge [a, b].
parysh = (face *) fastlookup(caveshbdlist, i);
spivot(*parysh, checksh); // checksh is a new subface [a, b, p].
// Do not recover a deleted new face (degenerated).
if (checksh.sh[3] != NULL) {
enqueuesubface(badsubfacs, &checksh);
}
}
} // if (chkencflag & 2)
if (ivf->chkencflag & 4) {
// Queue all new tetrahedra in C(p).
for (i = 0; i < cavebdrylist->objects; i++) {
cavetet = (triface *) fastlookup(cavebdrylist, i);
enqueuetetrahedron(cavetet);
}
}
// C(p) is re-meshed successfully.
// Delete the old tets in C(p).
for (i = 0; i < caveoldtetlist->objects; i++) {
searchtet = (triface *) fastlookup(caveoldtetlist, i);
if (ishulltet(*searchtet)) {
hullsize--;
}
tetrahedrondealloc(searchtet->tet);
}
if (((splitsh != NULL) && (splitsh->sh != NULL)) ||
((splitseg != NULL) && (splitseg->sh != NULL))) {
// Delete the old subfaces in sC(p).
for (i = 0; i < caveshlist->objects; i++) {
parysh = (face *) fastlookup(caveshlist, i);
if (checksubfaceflag) {//if (bowywat == 2) {
// It is possible that this subface still connects to adjacent
// tets which are not in C(p). If so, clear connections in the
// adjacent tets at this subface.
stpivot(*parysh, neightet);
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);
}
}
}
shellfacedealloc(subfaces, parysh->sh);
}
if ((splitseg != NULL) && (splitseg->sh != NULL)) {
// Delete the old segment in sC(p).
shellfacedealloc(subsegs, splitseg->sh);
}
}
if (ivf->lawson) {
for (i = 0; i < cavebdrylist->objects; i++) {
searchtet = (triface *) fastlookup(cavebdrylist, i);
flippush(flipstack, searchtet);
}
if (ivf->lawson > 1) {
for (i = 0; i < cavetetlist->objects; i++) {
searchtet = (triface *) fastlookup(cavetetlist, i);
flippush(flipstack, searchtet);
}
}
}
// Clean the working lists.
caveoldtetlist->restart();
cavebdrylist->restart();
cavetetlist->restart();
if (checksubsegflag) {
cavetetseglist->restart();
caveencseglist->restart();
}
if (checksubfaceflag) {
cavetetshlist->restart();
caveencshlist->restart();
}
if (b->weighted || ivf->validflag) {
cavetetvertlist->restart();
}
if (((splitsh != NULL) && (splitsh->sh != NULL)) ||
((splitseg != NULL) && (splitseg->sh != NULL))) {
caveshlist->restart();
caveshbdlist->restart();
cavesegshlist->restart();
}
return 1; // Point is inserted.
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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)
{
triface *cavetet;
face *parysh;
int i;
for (i = 0; i < caveoldtetlist->objects; i++) {
cavetet = (triface *) fastlookup(caveoldtetlist, i);
uninfect(*cavetet);
unmarktest(*cavetet);
}
for (i = 0; i < cavebdrylist->objects; i++) {
cavetet = (triface *) fastlookup(cavebdrylist, i);
unmarktest(*cavetet);
}
cavetetlist->restart();
cavebdrylist->restart();
caveoldtetlist->restart();
cavetetseglist->restart();
cavetetshlist->restart();
if (ivf->splitbdflag) {
if ((splitseg != NULL) && (splitseg->sh != NULL)) {
sunmarktest(*splitseg);
}
for (i = 0; i < caveshlist->objects; i++) {
parysh = (face *) fastlookup(caveshlist, i);
assert(smarktested(*parysh));
sunmarktest(*parysh);
}
caveshlist->restart();
cavesegshlist->restart();
}
}
//// ////
//// ////
//// 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;
int coordindex;
int attribindex;
int mtrindex;
int i, j;
if (b->psc) {
assert(in->pointparamlist != NULL);
}
// Read the points.
coordindex = 0;
attribindex = 0;
mtrindex = 0;
for (i = 0; i < in->numberofpoints; i++) {
makepoint(&pointloop, UNUSEDVERTEX);
// Read the point coordinates.
x = pointloop[0] = in->pointlist[coordindex++];
y = pointloop[1] = in->pointlist[coordindex++];
z = pointloop[2] = in->pointlist[coordindex++];
// Read the point attributes. (Including point weights.)
for (j = 0; j < in->numberofpointattributes; j++) {
pointloop[3 + j] = in->pointattributelist[attribindex++];
}
// Read the point metric tensor.
for (j = 0; j < in->numberofpointmtrs; j++) {
pointloop[pointmtrindex + j] = in->pointmtrlist[mtrindex++];
}
if (b->weighted) { // -w option
if (in->numberofpointattributes > 0) {
// The first point attribute is its weight.
//w = in->pointattributelist[in->numberofpointattributes * i];
w = pointloop[3];
} else {
// No given weight available. Default choose the maximum
// absolute value among its coordinates.
w = fabs(x);
if (w < fabs(y)) w = fabs(y);
if (w < fabs(z)) w = fabs(z);
}
if (b->weighted_param == 0) {
pointloop[3] = x * x + y * y + z * z - w; // Weighted DT.
} else { // -w1 option
pointloop[3] = w; // Regular tetrahedralization.
}
}
// Determine the smallest and largest x, y and z coordinates.
if (i == 0) {
xmin = xmax = x;
ymin = ymax = y;
zmin = zmax = z;
} else {
xmin = (x < xmin) ? x : xmin;
xmax = (x > xmax) ? x : xmax;
ymin = (y < ymin) ? y : ymin;
ymax = (y > ymax) ? y : ymax;
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;
longest = sqrt(x * x + y * y + z * z);
if (longest == 0.0) {
printf("Error: The point set is trivial.\n");
terminatetetgen(this, 3);
}
// Two identical points are distinguished by 'lengthlimit'.
if (b->minedgelength == 0.0) {
b->minedgelength = longest * b->epsilon;
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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)
{
int gc[8], N, mask, travel_bit;
int e, d, f, k, g;
int v, c;
int i;
N = (n == 2) ? 4 : 8;
mask = (n == 2) ? 3 : 7;
// Generate the Gray code sequence.
for (i = 0; i < N; i++) {
gc[i] = i ^ (i >> 1);
}
for (e = 0; e < N; e++) {
for (d = 0; d < n; d++) {
// Calculate the end point (f).
f = e ^ (1 << d); // Toggle the d-th bit of 'e'.
// travel_bit = 2**p, the bit we want to travel.
travel_bit = e ^ f;
for (i = 0; i < N; i++) {
// // Rotate gc[i] left by (p + 1) % n bits.
k = gc[i] * (travel_bit * 2);
g = ((k | (k / N)) & mask);
// 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
// Count the consecutive '1' bits (trailing) on the right.
tsb1mod3[0] = 0;
for (i = 1; i < N; i++) {
v = ~i; // Count the 0s.
v = (v ^ (v - 1)) >> 1; // Set v's trailing 0s to 1s and zero rest
for (c = 0; v; c++) {
v >>= 1;
}
tsb1mod3[i] = c % n;
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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,
REAL bzmin, REAL bzmax)
{
point swapvert;
int axis, d;
REAL split;
int i, j;
// Find the current splitting axis. 'axis' is a value 0, or 1, or 2, which
// correspoding to x-, or y- or z-axis.
axis = (gc0 ^ gc1) >> 1;
// Calulate the split position along the axis.
if (axis == 0) {
split = 0.5 * (bxmin + bxmax);
} else if (axis == 1) {
split = 0.5 * (bymin + bymax);
} else { // == 2
split = 0.5 * (bzmin + bzmax);
}
// Find the direction (+1 or -1) of the axis. If 'd' is +1, the direction
// of the axis is to the positive of the axis, otherwise, it is -1.
d = ((gc0 & (1< 0) {
do {
for (; i < arraysize; i++) {
if (vertexarray[i][axis] >= split) break;
}
for (; j >= 0; j--) {
if (vertexarray[j][axis] < split) break;
}
// Is the partition finished?
if (i == (j + 1)) break;
// Swap i-th and j-th vertices.
swapvert = vertexarray[i];
vertexarray[i] = vertexarray[j];
vertexarray[j] = swapvert;
// Continue patitioning the array;
} while (true);
} else {
do {
for (; i < arraysize; i++) {
if (vertexarray[i][axis] <= split) break;
}
for (; j >= 0; j--) {
if (vertexarray[j][axis] > split) break;
}
// Is the partition finished?
if (i == (j + 1)) break;
// Swap i-th and j-th vertices.
swapvert = vertexarray[i];
vertexarray[i] = vertexarray[j];
vertexarray[j] = swapvert;
// Continue patitioning the array;
} while (true);
}
return i;
}
void tetgenmesh::hilbert_sort3(point* vertexarray, int arraysize, int e, int d,
REAL bxmin, REAL bxmax, REAL bymin, REAL bymax,
REAL bzmin, REAL bzmax, int depth)
{
REAL x1, x2, y1, y2, z1, z2;
int p[9], w, e_w, d_w, k, ei, di;
int n = 3, mask = 7;
p[0] = 0;
p[8] = arraysize;
// Sort the points according to the 1st order Hilbert curve in 3d.
p[4] = hilbert_split(vertexarray, p[8], transgc[e][d][3], transgc[e][d][4],
bxmin, bxmax, bymin, bymax, bzmin, bzmax);
p[2] = hilbert_split(vertexarray, p[4], transgc[e][d][1], transgc[e][d][2],
bxmin, bxmax, bymin, bymax, bzmin, bzmax);
p[1] = hilbert_split(vertexarray, p[2], transgc[e][d][0], transgc[e][d][1],
bxmin, bxmax, bymin, bymax, bzmin, bzmax);
p[3] = hilbert_split(&(vertexarray[p[2]]), p[4] - p[2],
transgc[e][d][2], transgc[e][d][3],
bxmin, bxmax, bymin, bymax, bzmin, bzmax) + p[2];
p[6] = hilbert_split(&(vertexarray[p[4]]), p[8] - p[4],
transgc[e][d][5], transgc[e][d][6],
bxmin, bxmax, bymin, bymax, bzmin, bzmax) + p[4];
p[5] = hilbert_split(&(vertexarray[p[4]]), p[6] - p[4],
transgc[e][d][4], transgc[e][d][5],
bxmin, bxmax, bymin, bymax, bzmin, bzmax) + p[4];
p[7] = hilbert_split(&(vertexarray[p[6]]), p[8] - p[6],
transgc[e][d][6], transgc[e][d][7],
bxmin, bxmax, bymin, bymax, bzmin, bzmax) + p[6];
if (b->hilbert_order > 0) {
// A maximum order is prescribed.
if ((depth + 1) == b->hilbert_order) {
// The maximum prescribed order is reached.
return;
}
}
// Recursively sort the points in sub-boxes.
for (w = 0; w < 8; w++) {
// w is the local Hilbert index (NOT Gray code).
// Sort into the sub-box either there are more than 2 points in it, or
// the prescribed order of the curve is not reached yet.
//if ((p[w+1] - p[w] > b->hilbert_limit) || (b->hilbert_order > 0)) {
if ((p[w+1] - p[w]) > b->hilbert_limit) {
// Calculcate the start point (ei) of the curve in this sub-box.
// update e = e ^ (e(w) left_rotate (d+1)).
if (w == 0) {
e_w = 0;
} else {
// calculate e(w) = gc(2 * floor((w - 1) / 2)).
k = 2 * ((w - 1) / 2);
e_w = k ^ (k >> 1); // = gc(k).
}
k = e_w;
e_w = ((k << (d+1)) & mask) | ((k >> (n-d-1)) & mask);
ei = e ^ e_w;
// Calulcate the direction (di) of the curve in this sub-box.
// update d = (d + d(w) + 1) % n
if (w == 0) {
d_w = 0;
} else {
d_w = ((w % 2) == 0) ? tsb1mod3[w - 1] : tsb1mod3[w];
}
di = (d + d_w + 1) % n;
// Calculate the bounding box of the sub-box.
if (transgc[e][d][w] & 1) { // x-axis
x1 = 0.5 * (bxmin + bxmax);
x2 = bxmax;
} else {
x1 = bxmin;
x2 = 0.5 * (bxmin + bxmax);
}
if (transgc[e][d][w] & 2) { // y-axis
y1 = 0.5 * (bymin + bymax);
y2 = bymax;
} else {
y1 = bymin;
y2 = 0.5 * (bymin + bymax);
}
if (transgc[e][d][w] & 4) { // z-axis
z1 = 0.5 * (bzmin + bzmax);
z2 = bzmax;
} else {
z1 = bzmin;
z2 = 0.5 * (bzmin + bzmax);
}
hilbert_sort3(&(vertexarray[p[w]]), p[w+1] - p[w], ei, di,
x1, x2, y1, y2, z1, z2, depth+1);
} // if (p[w+1] - p[w] > 1)
} // w
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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)
{
int middle;
middle = 0;
if (arraysize >= threshold) {
(*depth)++;
middle = arraysize * ratio;
brio_multiscale_sort(vertexarray, middle, threshold, ratio, depth);
}
// Sort the right-array (rnd-th round) using the Hilbert curve.
hilbert_sort3(&(vertexarray[middle]), arraysize - middle, 0, 0, // e, d
xmin, xmax, ymin, ymax, zmin, zmax, 0); // depth.
}
///////////////////////////////////////////////////////////////////////////////
// //
// randomnation() Generate a random number between 0 and 'choices' - 1. //
// //
///////////////////////////////////////////////////////////////////////////////
unsigned long tetgenmesh::randomnation(unsigned int choices)
{
unsigned long newrandom;
if (choices >= 714025l) {
newrandom = (randomseed * 1366l + 150889l) % 714025l;
randomseed = (newrandom * 1366l + 150889l) % 714025l;
newrandom = newrandom * (choices / 714025l) + randomseed;
if (newrandom >= choices) {
return newrandom - choices;
} else {
return newrandom;
}
} else {
randomseed = (randomseed * 1366l + 150889l) % 714025l;
return randomseed % 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. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::randomsample(point searchpt,triface *searchtet)
{
tetrahedron *firsttet, *tetptr;
point torg;
void **sampleblock;
uintptr_t alignptr;
long sampleblocks, samplesperblock, samplenum;
long tetblocks, i, j;
REAL searchdist, dist;
if (b->verbose > 2) {
printf(" Random sampling tetrahedra for searching point %d.\n",
pointmark(searchpt));
}
if (!nonconvex) {
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
// whose vertices must not be 'dummypoint'.
searchtet->ver = 3;
// Record the distance from its origin to the searching point.
torg = org(*searchtet);
searchdist = (searchpt[0] - torg[0]) * (searchpt[0] - torg[0]) +
(searchpt[1] - torg[1]) * (searchpt[1] - torg[1]) +
(searchpt[2] - torg[2]) * (searchpt[2] - torg[2]);
// If a recently encountered tetrahedron has been recorded and has not
// been deallocated, test it as a good starting point.
if (recenttet.tet != searchtet->tet) {
recenttet.ver = 3;
torg = org(recenttet);
dist = (searchpt[0] - torg[0]) * (searchpt[0] - torg[0]) +
(searchpt[1] - torg[1]) * (searchpt[1] - torg[1]) +
(searchpt[2] - torg[2]) * (searchpt[2] - torg[2]);
if (dist < searchdist) {
*searchtet = recenttet;
searchdist = dist;
}
}
} else {
// The mesh is non-convex. Do not use 'recenttet'.
assert(samples >= 1l); // Make sure at least 1 sample.
searchdist = longest;
}
// Select "good" candidate using k random samples, taking the closest one.
// The number of random samples taken is proportional to the fourth root
// of the number of tetrahedra in the mesh.
while (samples * samples * samples * samples < tetrahedrons->items) {
samples++;
}
// Find how much blocks in current tet pool.
tetblocks = (tetrahedrons->maxitems + b->tetrahedraperblock - 1)
/ b->tetrahedraperblock;
// Find the average samples per block. Each block at least have 1 sample.
samplesperblock = 1 + (samples / tetblocks);
sampleblocks = samples / samplesperblock;
sampleblock = tetrahedrons->firstblock;
for (i = 0; i < sampleblocks; i++) {
alignptr = (uintptr_t) (sampleblock + 1);
firsttet = (tetrahedron *)
(alignptr + (uintptr_t) tetrahedrons->alignbytes
- (alignptr % (uintptr_t) tetrahedrons->alignbytes));
for (j = 0; j < samplesperblock; j++) {
if (i == tetblocks - 1) {
// This is the last block.
samplenum = randomnation((int)
(tetrahedrons->maxitems - (i * b->tetrahedraperblock)));
} else {
samplenum = randomnation(b->tetrahedraperblock);
}
tetptr = (tetrahedron *)
(firsttet + (samplenum * tetrahedrons->itemwords));
torg = (point) tetptr[4];
if (torg != (point) NULL) {
dist = (searchpt[0] - torg[0]) * (searchpt[0] - torg[0]) +
(searchpt[1] - torg[1]) * (searchpt[1] - torg[1]) +
(searchpt[2] - torg[2]) * (searchpt[2] - torg[2]);
if (dist < searchdist) {
searchtet->tet = tetptr;
searchtet->ver = 11; // torg = org(t);
searchdist = dist;
}
} else {
// A dead tet. Re-sample it.
if (i != tetblocks - 1) j--;
}
}
sampleblock = (void **) *sampleblock;
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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(point searchpt,
triface* searchtet)
{
point torg, tdest, tapex, toppo;
enum {ORGMOVE, DESTMOVE, APEXMOVE} nextmove;
REAL ori, oriorg, oridest, oriapex;
enum locateresult loc = OUTSIDE;
int t1ver;
int s;
if (searchtet->tet == NULL) {
// A null tet. Choose the recenttet as the starting tet.
searchtet->tet = recenttet.tet;
}
// 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);
}
// Let searchtet be the face such that 'searchpt' lies above to it.
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.0) break;
}
assert(searchtet->ver != 4);
// Walk through tetrahedra to locate the point.
while (true) {
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?
oriorg = orient3d(tdest, tapex, toppo, searchpt);
oridest = orient3d(tapex, torg, toppo, searchpt);
oriapex = orient3d(torg, tdest, toppo, searchpt);
// Now decide which face to move. It is possible there are more than one
// faces are viable moves. If so, randomly choose one.
if (oriorg < 0) {
if (oridest < 0) {
if (oriapex < 0) {
// All three faces are possible.
s = randomnation(3); // 's' is in {0,1,2}.
if (s == 0) {
nextmove = ORGMOVE;
} else if (s == 1) {
nextmove = DESTMOVE;
} else {
nextmove = APEXMOVE;
}
} else {
// Two faces, opposite to origin and destination, are viable.
//s = randomnation(2); // 's' is in {0,1}.
if (randomnation(2)) {
nextmove = ORGMOVE;
} else {
nextmove = DESTMOVE;
}
}
} else {
if (oriapex < 0) {
// Two faces, opposite to origin and apex, are viable.
//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;
}
}
} else {
if (oridest < 0) {
if (oriapex < 0) {
// Two faces, opposite to destination and apex, are viable.
//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);
}
// Move to the adjacent tetrahedron (maybe a hull tetrahedron).
fsymself(*searchtet);
if (oppo(*searchtet) == dummypoint) {
loc = OUTSIDE; // return OUTSIDE;
break;
}
// Retreat the three vertices of the base face.
torg = org(*searchtet);
tdest = dest(*searchtet);
tapex = apex(*searchtet);
} // while (true)
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)
{
badface *popface;
triface fliptets[5], *parytet;
point *pts, *parypt, pe;
REAL sign, ori;
int flipcount = 0;
int t1ver;
int i;
if (b->verbose > 2) {
printf(" Lawson flip (%ld faces).\n", flippool->items);
}
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];
}
}
}
popface = popface->nextitem;
}
}
// Loop until the queue is empty.
while (flipstack != NULL) {
// 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);
// 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;
unmarkface(fliptets[0]);
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);
} 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);
}
}
}
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 (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 (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];
}
} // while (flipstack)
// Unmark saved tetrahedra.
for (i = 0; i < cavebdrylist->objects; i++) {
parytet = (triface *) fastlookup(cavebdrylist, i);
unmarktest(*parytet);
}
cavebdrylist->restart();
if (hullflag) {
// Unmark infected vertices.
for (i = 0; i < cavetetvertlist->objects; i++) {
parypt = (point *) fastlookup(cavetetvertlist, i);
puninfect(*parypt);
}
cavetetvertlist->restart();
}
return flipcount;
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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)
{
triface firsttet, tetopa, tetopb, tetopc, tetopd;
triface worktet, worktet1;
if (b->verbose > 2) {
printf(" Create init tet (%d, %d, %d, %d)\n", pointmark(pa),
pointmark(pb), pointmark(pc), pointmark(pd));
}
// Create the first tetrahedron.
maketetrahedron(&firsttet);
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);
hullsize += 4;
// Connect hull tetrahedra to firsttet (at four faces of firsttet).
bond(firsttet, tetopd);
esym(firsttet, worktet);
bond(worktet, tetopc); // ab
enextesym(firsttet, worktet);
bond(worktet, tetopa); // bc
eprevesym(firsttet, worktet);
bond(worktet, tetopb); // ca
// Connect hull tetrahedra together (at six edges of firsttet).
esym(tetopc, worktet);
esym(tetopd, worktet1);
bond(worktet, worktet1); // ab
esym(tetopa, worktet);
eprevesym(tetopd, worktet1);
bond(worktet, worktet1); // bc
esym(tetopb, worktet);
enextesym(tetopd, worktet1);
bond(worktet, worktet1); // ca
eprevesym(tetopc, worktet);
enextesym(tetopb, worktet1);
bond(worktet, worktet1); // da
eprevesym(tetopa, worktet);
enextesym(tetopc, worktet1);
bond(worktet, worktet1); // db
eprevesym(tetopb, worktet);
enextesym(tetopa, worktet1);
bond(worktet, worktet1); // dc
// Set the vertex type.
if (pointtype(pa) == UNUSEDVERTEX) {
setpointtype(pa, VOLVERTEX);
}
if (pointtype(pb) == UNUSEDVERTEX) {
setpointtype(pb, VOLVERTEX);
}
if (pointtype(pc) == UNUSEDVERTEX) {
setpointtype(pc, VOLVERTEX);
}
if (pointtype(pd) == UNUSEDVERTEX) {
setpointtype(pd, VOLVERTEX);
}
setpoint2tet(pa, encode(firsttet));
setpoint2tet(pb, encode(firsttet));
setpoint2tet(pc, encode(firsttet));
setpoint2tet(pd, encode(firsttet));
// Remember the first tetrahedron.
recenttet = firsttet;
}
///////////////////////////////////////////////////////////////////////////////
// //
// incrementaldelaunay() Create a Delaunay tetrahedralization by //
// the incremental approach. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::incrementaldelaunay(clock_t& tv)
{
triface searchtet;
point *permutarray, swapvertex;
REAL v1[3], v2[3], n[3];
REAL bboxsize, bboxsize2, bboxsize3, ori;
int randindex;
int ngroup = 0;
int i, j;
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();
if (b->no_sort) {
if (b->verbose) {
printf(" Using the input order.\n");
}
for (i = 0; i < in->numberofpoints; i++) {
permutarray[i] = (point) points->traverse();
}
} else {
if (b->verbose) {
printf(" Permuting vertices.\n");
}
srand(in->numberofpoints);
for (i = 0; i < in->numberofpoints; i++) {
randindex = rand() % (i + 1); // randomnation(i + 1);
permutarray[i] = permutarray[randindex];
permutarray[randindex] = (point) points->traverse();
}
if (b->brio_hilbert) { // -b option
if (b->verbose) {
printf(" Sorting vertices.\n");
}
hilbert_init(in->mesh_dim);
brio_multiscale_sort(permutarray, in->numberofpoints, b->brio_threshold,
b->brio_ratio, &ngroup);
}
}
tv = clock(); // Remember the time for sorting points.
// Calculate the diagonal size of its bounding box.
bboxsize = sqrt(norm2(xmax - xmin, ymax - ymin, zmax - zmin));
bboxsize2 = bboxsize * bboxsize;
bboxsize3 = bboxsize2 * bboxsize;
// Make sure the second vertex is not identical with the first one.
i = 1;
while ((distance(permutarray[0],permutarray[i])/bboxsize)epsilon) {
i++;
if (i == in->numberofpoints - 1) {
printf("Exception: All vertices are (nearly) identical (Tol = %g).\n",
b->epsilon);
terminatetetgen(this, 10);
}
}
if (i > 1) {
// Swap to move the non-identical vertex from index i to index 1.
swapvertex = permutarray[i];
permutarray[i] = permutarray[1];
permutarray[1] = swapvertex;
}
// 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)) {
i++;
if (i == in->numberofpoints - 1) {
printf("Exception: All vertices are (nearly) collinear (Tol = %g).\n",
b->epsilon);
terminatetetgen(this, 10);
}
for (j = 0; j < 3; j++) {
v2[j] = permutarray[i][j] - permutarray[0][j];
}
cross(v1, v2, n);
}
if (i > 2) {
// Swap to move the non-identical vertex from index i to index 1.
swapvertex = permutarray[i];
permutarray[i] = permutarray[2];
permutarray[2] = swapvertex;
}
// Make sure the fourth vertex is not coplanar with the first three.
i = 3;
ori = orient3dfast(permutarray[0], permutarray[1], permutarray[2],
permutarray[i]);
while ((fabs(ori) / bboxsize3) < (b->epsilon * b->epsilon * b->epsilon)) {
i++;
if (i == in->numberofpoints) {
printf("Exception: All vertices are coplanar (Tol = %g).\n",
b->epsilon);
terminatetetgen(this, 10);
}
ori = orient3dfast(permutarray[0], permutarray[1], permutarray[2],
permutarray[i]);
}
if (i > 3) {
// Swap to move the non-identical vertex from index i to index 1.
swapvertex = permutarray[i];
permutarray[i] = permutarray[3];
permutarray[3] = swapvertex;
}
// Orient the first four vertices in permutarray so that they follow the
// right-hand rule.
if (ori > 0.0) {
// Swap the first two vertices.
swapvertex = permutarray[0];
permutarray[0] = permutarray[1];
permutarray[1] = swapvertex;
}
// Create the initial Delaunay tetrahedralization.
initialdelaunay(permutarray[0], permutarray[1], permutarray[2],
permutarray[3]);
if (b->verbose) {
printf(" Incrementally inserting vertices.\n");
}
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;
}
for (i = 4; i < in->numberofpoints; i++) {
if (pointtype(permutarray[i]) == UNUSEDVERTEX) {
setpointtype(permutarray[i], VOLVERTEX);
}
if (b->brio_hilbert || b->no_sort) { // -b or -b/1
// Start the last updated tet.
searchtet.tet = recenttet.tet;
} else { // -b0
// Randomly choose the starting tet for point location.
searchtet.tet = NULL;
}
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 (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",
pointmark(permutarray[i]), pointmark(swapvertex));
}
}
setpoint2ppt(permutarray[i], swapvertex);
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++;
}
}
}
delete [] permutarray;
}
//// ////
//// ////
//// delaunay_cxx /////////////////////////////////////////////////////////////
//// surface_cxx //////////////////////////////////////////////////////////////
//// ////
//// ////
///////////////////////////////////////////////////////////////////////////////
// //
// flipshpush() Push a facet edge into flip stack. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::flipshpush(face* flipedge)
{
badface *newflipface;
newflipface = (badface *) flippool->alloc();
newflipface->ss = *flipedge;
newflipface->forg = sorg(*flipedge);
newflipface->fdest = sdest(*flipedge);
newflipface->nextitem = flipstack;
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]. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::flip22(face* flipfaces, int flipflag, int chkencflag)
{
face bdedges[4], outfaces[4], infaces[4];
face bdsegs[4];
face checkface;
point pa, pb, pc, pd;
int i;
pa = sorg(flipfaces[0]);
pb = sdest(flipfaces[0]);
pc = sapex(flipfaces[0]);
pd = sapex(flipfaces[1]);
if (sorg(flipfaces[1]) != pb) {
sesymself(flipfaces[1]);
}
flip22count++;
// Collect the four boundary edges.
senext(flipfaces[0], bdedges[0]);
senext2(flipfaces[0], bdedges[1]);
senext(flipfaces[1], bdedges[2]);
senext2(flipfaces[1], bdedges[3]);
// Collect outer boundary faces.
for (i = 0; i < 4; i++) {
spivot(bdedges[i], outfaces[i]);
infaces[i] = outfaces[i];
sspivot(bdedges[i], bdsegs[i]);
if (outfaces[i].sh != NULL) {
if (isshsubseg(bdedges[i])) {
spivot(infaces[i], checkface);
while (checkface.sh != bdedges[i].sh) {
infaces[i] = checkface;
spivot(infaces[i], checkface);
}
}
}
}
// The flags set in these two subfaces do not change.
// Shellmark does not change.
// area constraint does not change.
// Transform [a,b,c] -> [c,d,b].
setshvertices(flipfaces[0], pc, pd, pb);
// Transform [b,a,d] -> [d,c,a].
setshvertices(flipfaces[1], pd, pc, pa);
// Update the point-to-subface map.
if (pointtype(pa) == FREEFACETVERTEX) {
setpoint2sh(pa, sencode(flipfaces[1]));
}
if (pointtype(pb) == FREEFACETVERTEX) {
setpoint2sh(pb, sencode(flipfaces[0]));
}
if (pointtype(pc) == FREEFACETVERTEX) {
setpoint2sh(pc, sencode(flipfaces[0]));
}
if (pointtype(pd) == FREEFACETVERTEX) {
setpoint2sh(pd, sencode(flipfaces[0]));
}
// Reconnect boundary edges to outer boundary faces.
for (i = 0; i < 4; i++) {
if (outfaces[(3 + i) % 4].sh != NULL) {
// Make sure that the subface has the ori as the segment.
if (bdsegs[(3 + i) % 4].sh != NULL) {
bdsegs[(3 + i) % 4].shver = 0;
if (sorg(bdedges[i]) != sorg(bdsegs[(3 + i) % 4])) {
sesymself(bdedges[i]);
}
}
sbond1(bdedges[i], outfaces[(3 + i) % 4]);
sbond1(infaces[(3 + i) % 4], bdedges[i]);
} else {
sdissolve(bdedges[i]);
}
if (bdsegs[(3 + i) % 4].sh != NULL) {
ssbond(bdedges[i], bdsegs[(3 + i) % 4]);
if (chkencflag & 1) {
// Queue this segment for encroaching check.
enqueuesubface(badsubsegs, &(bdsegs[(3 + i) % 4]));
}
} else {
ssdissolve(bdedges[i]);
}
}
if (chkencflag & 2) {
// Queue the flipped subfaces for quality/encroaching checks.
for (i = 0; i < 2; i++) {
enqueuesubface(badsubfacs, &(flipfaces[i]));
}
}
recentsh = flipfaces[0];
if (flipflag) {
// Put the boundary edges into flip stack.
for (i = 0; i < 4; i++) {
flipshpush(&(bdedges[i]));
}
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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)
{
face bdedges[3], outfaces[3], infaces[3];
face bdsegs[3];
face checkface;
point pa, pb, pc;
int i;
pa = sdest(flipfaces[0]);
pb = sdest(flipfaces[1]);
pc = sdest(flipfaces[2]);
flip31count++;
// Collect all infos at the three boundary edges.
for (i = 0; i < 3; i++) {
senext(flipfaces[i], bdedges[i]);
spivot(bdedges[i], outfaces[i]);
infaces[i] = outfaces[i];
sspivot(bdedges[i], bdsegs[i]);
if (outfaces[i].sh != NULL) {
if (isshsubseg(bdedges[i])) {
spivot(infaces[i], checkface);
while (checkface.sh != bdedges[i].sh) {
infaces[i] = checkface;
spivot(infaces[i], checkface);
}
}
}
} // i
// Create a new subface.
makeshellface(subfaces, &(flipfaces[3]));
setshvertices(flipfaces[3], pa, pb,pc);
setshellmark(flipfaces[3], shellmark(flipfaces[0]));
if (checkconstraints) {
//area = areabound(flipfaces[0]);
setareabound(flipfaces[3], areabound(flipfaces[0]));
}
if (useinsertradius) {
setfacetindex(flipfaces[3], getfacetindex(flipfaces[0]));
}
// Update the point-to-subface map.
if (pointtype(pa) == FREEFACETVERTEX) {
setpoint2sh(pa, sencode(flipfaces[3]));
}
if (pointtype(pb) == FREEFACETVERTEX) {
setpoint2sh(pb, sencode(flipfaces[3]));
}
if (pointtype(pc) == FREEFACETVERTEX) {
setpoint2sh(pc, sencode(flipfaces[3]));
}
// Update the three new boundary edges.
bdedges[0] = flipfaces[3]; // [a,b]
senext(flipfaces[3], bdedges[1]); // [b,c]
senext2(flipfaces[3], bdedges[2]); // [c,a]
// Reconnect boundary edges to outer boundary faces.
for (i = 0; i < 3; i++) {
if (outfaces[i].sh != NULL) {
// Make sure that the subface has the ori as the segment.
if (bdsegs[i].sh != NULL) {
bdsegs[i].shver = 0;
if (sorg(bdedges[i]) != sorg(bdsegs[i])) {
sesymself(bdedges[i]);
}
}
sbond1(bdedges[i], outfaces[i]);
sbond1(infaces[i], bdedges[i]);
}
if (bdsegs[i].sh != NULL) {
ssbond(bdedges[i], bdsegs[i]);
}
}
recentsh = flipfaces[3];
if (flipflag) {
// Put the boundary edges into flip stack.
for (i = 0; i < 3; i++) {
flipshpush(&(bdedges[i]));
}
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// lawsonflip() Flip non-locally Delaunay edges. //
// //
///////////////////////////////////////////////////////////////////////////////
long tetgenmesh::lawsonflip()
{
badface *popface;
face flipfaces[2];
point pa, pb, pc, pd;
REAL sign;
long flipcount = 0;
if (b->verbose > 2) {
printf(" Lawson flip %ld edges.\n", flippool->items);
}
while (flipstack != (badface *) NULL) {
// Pop an edge from the stack.
popface = flipstack;
flipfaces[0] = popface->ss;
pa = popface->forg;
pb = popface->fdest;
flipstack = popface->nextitem; // The next top item in stack.
flippool->dealloc((void *) popface);
// Skip it if it is dead.
if (flipfaces[0].sh[3] == NULL) continue;
// Skip it if it is not the same edge as we saved.
if ((sorg(flipfaces[0]) != pa) || (sdest(flipfaces[0]) != pb)) continue;
// Skip it if it is a subsegment.
if (isshsubseg(flipfaces[0])) continue;
// Get the adjacent face.
spivot(flipfaces[0], flipfaces[1]);
if (flipfaces[1].sh == NULL) continue; // Skip a hull edge.
pc = sapex(flipfaces[0]);
pd = sapex(flipfaces[1]);
sign = incircle3d(pa, pb, pc, pd);
if (sign < 0) {
// It is non-locally Delaunay. Flip it.
flip22(flipfaces, 1, 0);
flipcount++;
}
}
if (b->verbose > 2) {
printf(" Performed %ld flips.\n", flipcount);
}
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. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::sinsertvertex(point insertpt, face *searchsh, face *splitseg,
int iloc, int bowywat, int rflag)
{
face cavesh, neighsh, *parysh;
face newsh, casout, casin;
face checkseg;
point pa, pb;
enum locateresult loc = OUTSIDE;
REAL sign, ori;
int i, j;
if (b->verbose > 2) {
printf(" Insert facet point %d.\n", pointmark(insertpt));
}
if (bowywat == 3) {
loc = INSTAR;
}
if ((splitseg != NULL) && (splitseg->sh != NULL)) {
// A segment is going to be split, no point location.
spivot(*splitseg, *searchsh);
if (loc != INSTAR) loc = ONEDGE;
} else {
if (loc != INSTAR) loc = (enum locateresult) iloc;
if (loc == OUTSIDE) {
// Do point location in surface mesh.
if (searchsh->sh == NULL) {
*searchsh = recentsh;
}
// Search the vertex. An above point must be provided ('aflag' = 1).
loc = slocate(insertpt, searchsh, 1, 1, rflag);
}
}
// Form the initial sC(p).
if (loc == ONFACE) {
// Add the face into list (in B-W cavity).
smarktest(*searchsh);
caveshlist->newindex((void **) &parysh);
*parysh = *searchsh;
} else if (loc == ONEDGE) {
if ((splitseg != NULL) && (splitseg->sh != NULL)) {
splitseg->shver = 0;
pa = sorg(*splitseg);
} else {
pa = sorg(*searchsh);
}
if (searchsh->sh != NULL) {
// Collect all subfaces share at this edge.
neighsh = *searchsh;
while (1) {
// Adjust the origin of its edge to be 'pa'.
if (sorg(neighsh) != pa) sesymself(neighsh);
// Add this face into list (in B-W cavity).
smarktest(neighsh);
caveshlist->newindex((void **) &parysh);
*parysh = neighsh;
// Add this face into face-at-splitedge list.
cavesegshlist->newindex((void **) &parysh);
*parysh = neighsh;
// Go to the next face at the edge.
spivotself(neighsh);
// Stop if all faces at the edge have been visited.
if (neighsh.sh == searchsh->sh) break;
if (neighsh.sh == NULL) break;
}
} // If (not a non-dangling segment).
} else if (loc == ONVERTEX) {
return (int) loc;
} else if (loc == OUTSIDE) {
// Comment: This should only happen during the surface meshing step.
// Enlarge the convex hull of the triangulation by including p.
// An above point of the facet is set in 'dummypoint' to replace
// orient2d tests by orient3d tests.
// Imagine that the current edge a->b (in 'searchsh') is horizontal in a
// plane, and a->b is directed from left to right, p lies above a->b.
// Find the right-most edge of the triangulation which is visible by p.
neighsh = *searchsh;
while (1) {
senext2self(neighsh);
spivot(neighsh, casout);
if (casout.sh == NULL) {
// A convex hull edge. Is it visible by p.
ori = orient3d(sorg(neighsh), sdest(neighsh), dummypoint, insertpt);
if (ori < 0) {
*searchsh = neighsh; // Visible, update 'searchsh'.
} else {
break; // 'searchsh' is the right-most visible edge.
}
} else {
if (sorg(casout) != sdest(neighsh)) sesymself(casout);
neighsh = casout;
}
}
// Create new triangles for all visible edges of p (from right to left).
casin.sh = NULL; // No adjacent face at right.
pa = sorg(*searchsh);
pb = sdest(*searchsh);
while (1) {
// Create a new subface on top of the (visible) edge.
makeshellface(subfaces, &newsh);
setshvertices(newsh, pb, pa, insertpt);
setshellmark(newsh, shellmark(*searchsh));
if (checkconstraints) {
//area = areabound(*searchsh);
setareabound(newsh, areabound(*searchsh));
}
if (useinsertradius) {
setfacetindex(newsh, getfacetindex(*searchsh));
}
// Connect the new subface to the bottom subfaces.
sbond1(newsh, *searchsh);
sbond1(*searchsh, newsh);
// Connect the new subface to its right-adjacent subface.
if (casin.sh != NULL) {
senext(newsh, casout);
sbond1(casout, casin);
sbond1(casin, casout);
}
// The left-adjacent subface has not been created yet.
senext2(newsh, casin);
// Add the new face into list (inside the B-W cavity).
smarktest(newsh);
caveshlist->newindex((void **) &parysh);
*parysh = newsh;
// Move to the convex hull edge at the left of 'searchsh'.
neighsh = *searchsh;
while (1) {
senextself(neighsh);
spivot(neighsh, casout);
if (casout.sh == NULL) {
*searchsh = neighsh;
break;
}
if (sorg(casout) != sdest(neighsh)) sesymself(casout);
neighsh = casout;
}
// A convex hull edge. Is it visible by p.
pa = sorg(*searchsh);
pb = sdest(*searchsh);
ori = orient3d(pa, pb, dummypoint, insertpt);
// Finish the process if p is not visible by the hull edge.
if (ori >= 0) break;
}
} else if (loc == INSTAR) {
// Under this case, the sub-cavity sC(p) has already been formed in
// insertvertex().
}
// Form the Bowyer-Watson cavity sC(p).
for (i = 0; i < caveshlist->objects; i++) {
cavesh = * (face *) fastlookup(caveshlist, i);
for (j = 0; j < 3; j++) {
if (!isshsubseg(cavesh)) {
spivot(cavesh, neighsh);
if (neighsh.sh != NULL) {
// The adjacent face exists.
if (!smarktested(neighsh)) {
if (bowywat) {
if (loc == INSTAR) { // if (bowywat > 2) {
// It must be a boundary edge.
sign = 1;
} else {
// Check if this subface is connected to adjacent tet(s).
if (!isshtet(neighsh)) {
// Check if the subface is non-Delaunay wrt. the new pt.
sign = incircle3d(sorg(neighsh), sdest(neighsh),
sapex(neighsh), insertpt);
} else {
// It is connected to an adjacent tet. A boundary edge.
sign = 1;
}
}
if (sign < 0) {
// Add the adjacent face in list (in B-W cavity).
smarktest(neighsh);
caveshlist->newindex((void **) &parysh);
*parysh = neighsh;
}
} else {
sign = 1; // A boundary edge.
}
} else {
sign = -1; // Not a boundary edge.
}
} else {
// No adjacent face. It is a hull edge.
if (loc == OUTSIDE) {
// It is a boundary edge if it does not contain p.
if ((sorg(cavesh) == insertpt) || (sdest(cavesh) == insertpt)) {
sign = -1; // Not a boundary edge.
} else {
sign = 1; // A boundary edge.
}
} else {
sign = 1; // A boundary edge.
}
}
} else {
// Do not across a segment. It is a boundary edge.
sign = 1;
}
if (sign >= 0) {
// Add a boundary edge.
caveshbdlist->newindex((void **) &parysh);
*parysh = cavesh;
}
senextself(cavesh);
} // j
} // i
// Creating new subfaces.
for (i = 0; i < caveshbdlist->objects; i++) {
parysh = (face *) fastlookup(caveshbdlist, i);
sspivot(*parysh, checkseg);
if ((parysh->shver & 01) != 0) sesymself(*parysh);
pa = sorg(*parysh);
pb = sdest(*parysh);
// Create a new subface.
makeshellface(subfaces, &newsh);
setshvertices(newsh, pa, pb, insertpt);
setshellmark(newsh, shellmark(*parysh));
if (checkconstraints) {
//area = areabound(*parysh);
setareabound(newsh, areabound(*parysh));
}
if (useinsertradius) {
setfacetindex(newsh, getfacetindex(*parysh));
}
// Update the point-to-subface map.
if (pointtype(pa) == FREEFACETVERTEX) {
setpoint2sh(pa, sencode(newsh));
}
if (pointtype(pb) == FREEFACETVERTEX) {
setpoint2sh(pb, sencode(newsh));
}
// Connect newsh to outer subfaces.
spivot(*parysh, casout);
if (casout.sh != NULL) {
casin = casout;
if (checkseg.sh != NULL) {
// Make sure that newsh has the right ori at this segment.
checkseg.shver = 0;
if (sorg(newsh) != sorg(checkseg)) {
sesymself(newsh);
sesymself(*parysh); // This side should also be inverse.
}
spivot(casin, neighsh);
while (neighsh.sh != parysh->sh) {
casin = neighsh;
spivot(casin, neighsh);
}
}
sbond1(newsh, casout);
sbond1(casin, newsh);
}
if (checkseg.sh != NULL) {
ssbond(newsh, checkseg);
}
// Connect oldsh <== newsh (for connecting adjacent new subfaces).
// *parysh and newsh point to the same edge and the same ori.
sbond1(*parysh, newsh);
}
if (newsh.sh != NULL) {
// Set a handle for searching.
recentsh = newsh;
}
// Update the point-to-subface map.
if (pointtype(insertpt) == FREEFACETVERTEX) {
setpoint2sh(insertpt, sencode(newsh));
}
// Connect adjacent new subfaces together.
for (i = 0; i < caveshbdlist->objects; i++) {
// Get an old subface at edge [a, b].
parysh = (face *) fastlookup(caveshbdlist, i);
spivot(*parysh, newsh); // The new subface [a, b, p].
senextself(newsh); // At edge [b, p].
spivot(newsh, neighsh);
if (neighsh.sh == NULL) {
// Find the adjacent new subface at edge [b, p].
pb = sdest(*parysh);
neighsh = *parysh;
while (1) {
senextself(neighsh);
spivotself(neighsh);
if (neighsh.sh == NULL) break;
if (!smarktested(neighsh)) break;
if (sdest(neighsh) != pb) sesymself(neighsh);
}
if (neighsh.sh != NULL) {
// Now 'neighsh' is a new subface at edge [b, #].
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].
senext2self(newsh); // At edge [p, a].
spivot(newsh, neighsh);
if (neighsh.sh == NULL) {
// Find the adjacent new subface at edge [p, a].
pa = sorg(*parysh);
neighsh = *parysh;
while (1) {
senext2self(neighsh);
spivotself(neighsh);
if (neighsh.sh == NULL) break;
if (!smarktested(neighsh)) break;
if (sorg(neighsh) != pa) sesymself(neighsh);
}
if (neighsh.sh != NULL) {
// Now 'neighsh' is a new subface at edge [#, a].
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
}
}
}
if ((loc == ONEDGE) || ((splitseg != NULL) && (splitseg->sh != NULL))
|| (cavesegshlist->objects > 0l)) {
// An edge is being split. We distinguish two cases:
// (1) the edge is not on the boundary of the cavity;
// (2) the edge is on the boundary of the cavity.
// In case (2), the edge is either a segment or a hull edge. There are
// degenerated new faces in the cavity. They must be removed.
face aseg, bseg, aoutseg, boutseg;
for (i = 0; i < cavesegshlist->objects; i++) {
// Get the saved old subface.
parysh = (face *) fastlookup(cavesegshlist, i);
// Get a possible new degenerated subface.
spivot(*parysh, cavesh);
if (sapex(cavesh) == insertpt) {
// Found a degenerated new subface, i.e., case (2).
if (cavesegshlist->objects > 1) {
// There are more than one subface share at this edge.
j = (i + 1) % (int) cavesegshlist->objects;
parysh = (face *) fastlookup(cavesegshlist, j);
spivot(*parysh, neighsh);
// 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
// still hold the adjacent informations which will be used in
// re-connecting subsegments (if they exist).
for (j = 0; j < 2; j++) {
senextself(cavesh);
senextself(neighsh);
spivot(cavesh, newsh);
spivot(neighsh, casout);
sbond1(newsh, casout); // newsh <- casout.
}
} else {
// There is only one subface containing this edge [a,b]. Squeeze the
// degenerated new face [a,b,c] by disconnecting it from its two
// adjacent subfaces at edges [b,c] and [c,a]. Note that the face
// [a,b,c] still hold the connection to them.
for (j = 0; j < 2; j++) {
senextself(cavesh);
spivot(cavesh, newsh);
sdissolve(newsh);
}
}
//recentsh = newsh;
// Update the point-to-subface map.
if (pointtype(insertpt) == FREEFACETVERTEX) {
setpoint2sh(insertpt, sencode(newsh));
}
}
}
if ((splitseg != NULL) && (splitseg->sh != NULL)) {
if (loc != INSTAR) { // if (bowywat < 3) {
smarktest(*splitseg); // Mark it as being processed.
}
aseg = *splitseg;
pa = sorg(*splitseg);
pb = sdest(*splitseg);
// Insert the new point p.
makeshellface(subsegs, &aseg);
makeshellface(subsegs, &bseg);
setshvertices(aseg, pa, insertpt, NULL);
setshvertices(bseg, insertpt, pb, NULL);
setshellmark(aseg, shellmark(*splitseg));
setshellmark(bseg, shellmark(*splitseg));
if (checkconstraints) {
setareabound(aseg, areabound(*splitseg));
setareabound(bseg, areabound(*splitseg));
}
if (useinsertradius) {
setfacetindex(aseg, getfacetindex(*splitseg));
setfacetindex(bseg, getfacetindex(*splitseg));
}
// Connect [#, a]<->[a, p].
senext2(*splitseg, boutseg); // Temporarily use boutseg.
spivotself(boutseg);
if (boutseg.sh != NULL) {
senext2(aseg, aoutseg);
sbond(boutseg, aoutseg);
}
// Connect [p, b]<->[b, #].
senext(*splitseg, aoutseg);
spivotself(aoutseg);
if (aoutseg.sh != NULL) {
senext(bseg, boutseg);
sbond(boutseg, aoutseg);
}
// Connect [a, p] <-> [p, b].
senext(aseg, aoutseg);
senext2(bseg, boutseg);
sbond(aoutseg, boutseg);
// Connect subsegs [a, p] and [p, b] to adjacent new subfaces.
// Although the degenerated new faces have been squeezed. They still
// hold the connections to the actual new faces.
for (i = 0; i < cavesegshlist->objects; i++) {
parysh = (face *) fastlookup(cavesegshlist, i);
spivot(*parysh, neighsh);
// neighsh is a degenerated new face.
if (sorg(neighsh) != pa) {
sesymself(neighsh);
}
senext2(neighsh, newsh);
spivotself(newsh); // The edge [p, a] in newsh
ssbond(newsh, aseg);
senext(neighsh, newsh);
spivotself(newsh); // The edge [b, p] in newsh
ssbond(newsh, bseg);
}
// Let the point remember the segment it lies on.
if (pointtype(insertpt) == FREESEGVERTEX) {
setpoint2sh(insertpt, sencode(aseg));
}
// Update the point-to-seg map.
if (pointtype(pa) == FREESEGVERTEX) {
setpoint2sh(pa, sencode(aseg));
}
if (pointtype(pb) == FREESEGVERTEX) {
setpoint2sh(pb, sencode(bseg));
}
} // if ((splitseg != NULL) && (splitseg->sh != NULL))
// Delete all degenerated new faces.
for (i = 0; i < cavesegshlist->objects; i++) {
parysh = (face *) fastlookup(cavesegshlist, i);
spivotself(*parysh);
if (sapex(*parysh) == insertpt) {
shellfacedealloc(subfaces, parysh->sh);
}
}
cavesegshlist->restart();
if ((splitseg != NULL) && (splitseg->sh != NULL)) {
// Return the two new subsegments (for further process).
// Re-use 'cavesegshlist'.
cavesegshlist->newindex((void **) &parysh);
*parysh = aseg;
cavesegshlist->newindex((void **) &parysh);
*parysh = bseg;
}
} // if (loc == ONEDGE)
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. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::sremovevertex(point delpt, face* parentsh, face* parentseg,
int lawson)
{
face flipfaces[4], spinsh, *parysh;
point pa, pb, pc, pd;
REAL ori1, ori2;
int it, i, j;
if (parentseg != NULL) {
// 'delpt' (p) should be a Steiner point inserted in a segment [a,b],
// where 'parentseg' should be [p,b]. Find the segment [a,p].
face startsh, neighsh, nextsh;
face abseg, prevseg, checkseg;
face adjseg1, adjseg2;
face fakesh;
senext2(*parentseg, prevseg);
spivotself(prevseg);
prevseg.shver = 0;
assert(sdest(prevseg) == delpt);
// Restore the original segment [a,b].
pa = sorg(prevseg);
pb = sdest(*parentseg);
if (b->verbose > 2) {
printf(" Remove vertex %d from segment [%d, %d].\n",
pointmark(delpt), pointmark(pa), pointmark(pb));
}
makeshellface(subsegs, &abseg);
setshvertices(abseg, pa, pb, NULL);
setshellmark(abseg, shellmark(*parentseg));
if (checkconstraints) {
setareabound(abseg, areabound(*parentseg));
}
if (useinsertradius) {
setfacetindex(abseg, getfacetindex(*parentseg));
}
// Connect [#, a]<->[a, b].
senext2(prevseg, adjseg1);
spivotself(adjseg1);
if (adjseg1.sh != NULL) {
adjseg1.shver = 0;
assert(sdest(adjseg1) == pa);
senextself(adjseg1);
senext2(abseg, adjseg2);
sbond(adjseg1, adjseg2);
}
// Connect [a, b]<->[b, #].
senext(*parentseg, adjseg1);
spivotself(adjseg1);
if (adjseg1.sh != NULL) {
adjseg1.shver = 0;
assert(sorg(adjseg1) == pb);
senext2self(adjseg1);
senext(abseg, adjseg2);
sbond(adjseg1, adjseg2);
}
// Update the point-to-segment map.
setpoint2sh(pa, sencode(abseg));
setpoint2sh(pb, sencode(abseg));
// Get the faces in face ring at segment [p, b].
// Re-use array 'caveshlist'.
spivot(*parentseg, *parentsh);
if (parentsh->sh != NULL) {
spinsh = *parentsh;
while (1) {
// Save this face in list.
caveshlist->newindex((void **) &parysh);
*parysh = spinsh;
// Go to the next face in the ring.
spivotself(spinsh);
if (spinsh.sh == parentsh->sh) break;
}
}
// Create the face ring of the new segment [a,b]. Each face in the ring
// is [a,b,p] (degenerated!). It will be removed (automatically).
for (i = 0; i < caveshlist->objects; i++) {
parysh = (face *) fastlookup(caveshlist, i);
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;
while (1) {
senext2self(neighsh);
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].
if (neighsh.sh != startsh.sh) {
// Detach the two subsegments [a,p] and [p,b] from subfaces.
ssdissolve(startsh);
ssdissolve(neighsh);
// Create a degenerated subface [a,b,p]. It is used to: (1) hold the
// new segment [a,b]; (2) connect to the two adjacent subfaces
// [p,b,#] and [a,p,#].
makeshellface(subfaces, &fakesh);
setshvertices(fakesh, pa, pb, delpt);
setshellmark(fakesh, shellmark(startsh));
// Connect fakesh to the segment [a,b].
ssbond(fakesh, abseg);
// Connect fakesh to adjacent subfaces: [p,b,#1] and [a,p,#2].
senext(fakesh, nextsh);
sbond(nextsh, startsh);
senext2(fakesh, nextsh);
sbond(nextsh, neighsh);
smarktest(fakesh); // Mark it as faked.
} else {
// 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.
if (sorg(startsh) != pa) sesymself(startsh);
sdissolve(startsh);
// Connect fakesh to the segment [a,b].
ssbond(startsh, abseg);
fakesh = startsh; // Do not mark it!
// Delete the degenerated subface.
shellfacedealloc(subfaces, neighsh.sh);
}
// Save the fakesh in list (for re-creating the face ring).
cavesegshlist->newindex((void **) &parysh);
*parysh = fakesh;
} // i
caveshlist->restart();
// Re-create the face ring.
if (cavesegshlist->objects > 1) {
for (i = 0; i < cavesegshlist->objects; i++) {
parysh = (face *) fastlookup(cavesegshlist, i);
fakesh = *parysh;
// Get the next face in the ring.
j = (i + 1) % cavesegshlist->objects;
parysh = (face *) fastlookup(cavesegshlist, j);
nextsh = *parysh;
sbond1(fakesh, nextsh);
}
}
// Delete the two subsegments containing p.
shellfacedealloc(subsegs, parentseg->sh);
shellfacedealloc(subsegs, prevseg.sh);
// Return the new segment.
*parentseg = abseg;
} else {
// p is inside the surface.
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.
cavesegshlist->newindex((void **) &parysh);
*parysh = *parentsh;
}
// Remove the point (p).
for (it = 0; it < cavesegshlist->objects; it++) {
parentsh = (face *) fastlookup(cavesegshlist, it); // [a,b,p]
senextself(*parentsh); // [b,p,a].
spivotself(*parentsh);
if (sorg(*parentsh) != delpt) sesymself(*parentsh);
// now parentsh is [p,b,#].
if (sorg(*parentsh) != delpt) {
// The vertex has already been removed in above special case.
assert(!smarktested(*parentsh));
continue;
}
while (1) {
// Initialize the flip edge list. Re-use 'caveshlist'.
spinsh = *parentsh; // [p, b, #]
while (1) {
caveshlist->newindex((void **) &parysh);
*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) {
// Delete the point by a 3-to-1 flip.
for (i = 0; i < 3; i++) {
parysh = (face *) fastlookup(caveshlist, i);
flipfaces[i] = *parysh;
}
flip31(flipfaces, lawson);
for (i = 0; i < 3; i++) {
shellfacedealloc(subfaces, flipfaces[i].sh);
}
caveshlist->restart();
// Save the new subface.
caveshbdlist->newindex((void **) &parysh);
*parysh = flipfaces[3];
// The vertex is removed.
break;
}
// Search an edge to flip.
for (i = 0; i < caveshlist->objects; i++) {
parysh = (face *) fastlookup(caveshlist, i);
flipfaces[0] = *parysh;
spivot(flipfaces[0], flipfaces[1]);
if (sorg(flipfaces[0]) != sdest(flipfaces[1]))
sesymself(flipfaces[1]);
// Skip this edge if it belongs to a faked subface.
if (!smarktested(flipfaces[0]) && !smarktested(flipfaces[1])) {
pa = sorg(flipfaces[0]);
pb = sdest(flipfaces[0]);
pc = sapex(flipfaces[0]);
pd = sapex(flipfaces[1]);
calculateabovepoint4(pa, pb, pc, pd);
// Check if a 2-to-2 flip is possible.
ori1 = orient3d(pc, pd, dummypoint, pa);
ori2 = orient3d(pc, pd, dummypoint, pb);
if (ori1 * ori2 < 0) {
// A 2-to-2 flip is found.
flip22(flipfaces, lawson, 0);
// The i-th edge is flipped. The i-th and (i-1)-th subfaces are
// changed. The 'flipfaces[1]' contains p as its apex.
senext2(flipfaces[1], *parentsh);
// Save the new subface.
caveshbdlist->newindex((void **) &parysh);
*parysh = flipfaces[0];
break;
}
} //
} // 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;
spivot(flipfaces[0], flipfaces[1]);
if (sorg(flipfaces[0]) != sdest(flipfaces[1])) {
sesymself(flipfaces[1]);
}
flip22(flipfaces, lawson, 0);
senext2(flipfaces[1], *parentsh);
// Save the new subface.
caveshbdlist->newindex((void **) &parysh);
*parysh = flipfaces[0];
}
// The edge list at p are changed.
caveshlist->restart();
} // while (1)
} // it
cavesegshlist->restart();
if (b->verbose > 2) {
printf(" Created %ld new subfaces.\n", caveshbdlist->objects);
}
if (lawson) {
lawsonflip();
}
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. //
// //
///////////////////////////////////////////////////////////////////////////////
enum tetgenmesh::locateresult tetgenmesh::slocate(point searchpt,
face* searchsh, int aflag, int cflag, int rflag)
{
face neighsh;
point pa, pb, pc;
enum locateresult loc;
enum {MOVE_BC, MOVE_CA} nextmove;
REAL ori, ori_bc, ori_ca;
int i;
pa = sorg(*searchsh);
pb = sdest(*searchsh);
pc = sapex(*searchsh);
if (!aflag) {
// No above point is given. Calculate an above point for this facet.
calculateabovepoint4(pa, pb, pc, 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.
}
// Find an edge of the face s.t. p lies on its right-hand side (CCW).
for (i = 0; i < 3; i++) {
pa = sorg(*searchsh);
pb = sdest(*searchsh);
ori = orient3d(pa, pb, dummypoint, searchpt);
if (ori > 0) break;
senextself(*searchsh);
}
assert(i < 3); // SELF_CHECK
pc = sapex(*searchsh);
if (pc == searchpt) {
senext2self(*searchsh);
return ONVERTEX;
}
while (1) {
ori_bc = orient3d(pb, pc, dummypoint, searchpt);
ori_ca = orient3d(pc, pa, dummypoint, searchpt);
if (ori_bc < 0) {
if (ori_ca < 0) { // (--)
// Any of the edges is a viable move.
if (randomnation(2)) {
nextmove = MOVE_CA;
} else {
nextmove = MOVE_BC;
}
} else { // (-#)
// Edge [b, c] is viable.
nextmove = MOVE_BC;
}
} else {
if (ori_ca < 0) { // (#-)
// Edge [c, a] is viable.
nextmove = MOVE_CA;
} else {
if (ori_bc > 0) {
if (ori_ca > 0) { // (++)
loc = ONFACE; // Inside [a, b, c].
break;
} else { // (+0)
senext2self(*searchsh); // On edge [c, a].
loc = ONEDGE;
break;
}
} else { // ori_bc == 0
if (ori_ca > 0) { // (0+)
senextself(*searchsh); // On edge [b, c].
loc = ONEDGE;
break;
} else { // (00)
// p is coincident with vertex c.
senext2self(*searchsh);
return ONVERTEX;
}
}
}
}
// Move to the next face.
if (nextmove == MOVE_BC) {
senextself(*searchsh);
} else {
senext2self(*searchsh);
}
if (!cflag) {
// NON-convex case. Check if we will cross a boundary.
if (isshsubseg(*searchsh)) {
return ENCSEGMENT;
}
}
spivot(*searchsh, neighsh);
if (neighsh.sh == NULL) {
return OUTSIDE; // A hull edge.
}
// Adjust the edge orientation.
if (sorg(neighsh) != sdest(*searchsh)) {
sesymself(neighsh);
}
assert(sorg(neighsh) == sdest(*searchsh)); // SELF_CHECK
// Update the newly discovered face and its endpoints.
*searchsh = neighsh;
pa = sorg(*searchsh);
pb = sdest(*searchsh);
pc = sapex(*searchsh);
if (pc == searchpt) {
senext2self(*searchsh);
return ONVERTEX;
}
} // while (1)
// assert(loc == ONFACE || loc == ONEDGE);
if (rflag) {
// Round the locate result before return.
REAL n[3], area_abc, area_abp, area_bcp, area_cap;
pa = sorg(*searchsh);
pb = sdest(*searchsh);
pc = sapex(*searchsh);
facenormal(pa, pb, pc, n, 1, NULL);
area_abc = sqrt(dot(n, n));
facenormal(pb, pc, searchpt, n, 1, NULL);
area_bcp = sqrt(dot(n, n));
if ((area_bcp / area_abc) < b->epsilon) {
area_bcp = 0; // Rounding.
}
facenormal(pc, pa, searchpt, n, 1, NULL);
area_cap = sqrt(dot(n, n));
if ((area_cap / area_abc) < b->epsilon) {
area_cap = 0; // Rounding
}
if ((loc == ONFACE) || (loc == OUTSIDE)) {
facenormal(pa, pb, searchpt, n, 1, NULL);
area_abp = sqrt(dot(n, n));
if ((area_abp / area_abc) < b->epsilon) {
area_abp = 0; // Rounding
}
} else { // loc == ONEDGE
area_abp = 0;
}
if (area_abp == 0) {
if (area_bcp == 0) {
assert(area_cap != 0);
senextself(*searchsh);
loc = ONVERTEX; // p is close to b.
} else {
if (area_cap == 0) {
loc = ONVERTEX; // p is close to a.
} else {
loc = ONEDGE; // p is on edge [a,b].
}
}
} else if (area_bcp == 0) {
if (area_cap == 0) {
senext2self(*searchsh);
loc = ONVERTEX; // p is close to c.
} else {
senextself(*searchsh);
loc = ONEDGE; // p is on edge [b,c].
}
} else if (area_cap == 0) {
senext2self(*searchsh);
loc = ONEDGE; // p is on edge [c,a].
} else {
loc = ONFACE; // p is on face [a,b,c].
}
} // if (rflag)
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. //
// //
///////////////////////////////////////////////////////////////////////////////
enum tetgenmesh::interresult tetgenmesh::sscoutsegment(face *searchsh,
point endpt)
{
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;
// The origin of 'searchsh' is fixed.
startpt = sorg(*searchsh); // pa = startpt;
nextmove = MOVE_AB; // Avoid compiler warning.
if (b->verbose > 2) {
printf(" Scout segment (%d, %d).\n", pointmark(startpt),
pointmark(endpt));
}
len = distance(startpt, endpt);
// Search an edge in 'searchsh' on the path of this segment.
while (1) {
pb = sdest(*searchsh);
if (pb == endpt) {
dir = SHAREEDGE; // Found!
break;
}
pc = sapex(*searchsh);
if (pc == endpt) {
senext2self(*searchsh);
sesymself(*searchsh);
dir = SHAREEDGE; // Found!
break;
}
// Round the results.
if ((sqrt(triarea(startpt, pb, endpt)) / len) < b->epsilon) {
ori_ab = 0.0;
} else {
ori_ab = orient3d(startpt, pb, dummypoint, endpt);
}
if ((sqrt(triarea(pc, startpt, endpt)) / len) < b->epsilon) {
ori_ca = 0.0;
} else {
ori_ca = orient3d(pc, startpt, dummypoint, endpt);
}
if (ori_ab < 0) {
if (ori_ca < 0) { // (--)
// Both sides are viable moves.
if (randomnation(2)) {
nextmove = MOVE_CA;
} else {
nextmove = MOVE_AB;
}
} else { // (-#)
nextmove = MOVE_AB;
}
} else {
if (ori_ca < 0) { // (#-)
nextmove = MOVE_CA;
} else {
if (ori_ab > 0) {
if (ori_ca > 0) { // (++)
// The segment intersects with edge [b, c].
dir = ACROSSEDGE;
break;
} else { // (+0)
// The segment collinear with edge [c, a].
senext2self(*searchsh);
sesymself(*searchsh);
dir = ACROSSVERT;
break;
}
} else {
if (ori_ca > 0) { // (0+)
// The segment collinear with edge [a, b].
dir = ACROSSVERT;
break;
} else { // (00)
// startpt == endpt. Not possible.
assert(0); // SELF_CHECK
}
}
}
}
// Move 'searchsh' to the next face, keep the origin unchanged.
if (nextmove == MOVE_AB) {
spivot(*searchsh, neighsh);
if (neighsh.sh != NULL) {
if (sorg(neighsh) != pb) sesymself(neighsh);
senext(neighsh, *searchsh);
} else {
// This side (startpt->pb) is outside. It is caused by rounding error.
// Try the next side, i.e., (pc->startpt).
senext2(*searchsh, neighsh);
spivotself(neighsh);
assert(neighsh.sh != NULL);
if (sdest(neighsh) != pc) sesymself(neighsh);
*searchsh = neighsh;
}
} else {
senext2(*searchsh, neighsh);
spivotself(neighsh);
if (neighsh.sh != NULL) {
if (sdest(neighsh) != pc) sesymself(neighsh);
*searchsh = neighsh;
} else {
// The same reason as above.
// Try the next side, i.e., (startpt->pb).
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);
}
return dir;
}
if (dir == ACROSSVERT) {
// A point is found collinear with this segment.
return dir;
}
if (dir == ACROSSEDGE) {
// 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);
}
// 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.
pa = sapex(flipshs[1]);
pb = sapex(flipshs[0]);
pc = sorg(flipshs[0]);
pd = sdest(flipshs[0]);
// Check if pa and pb are on the different sides of [pc, pd].
// 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'
}
// Set 'searchsh' s.t. its origin is 'startpt'.
*searchsh = flipshs[0];
assert(sorg(*searchsh) == startpt);
}
return sscoutsegment(searchsh, endpt);
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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)
{
face *parysh, searchsh, neighsh;
enum locateresult loc;
int i, j;
// Get all triangles. Infect unprotected convex hull triangles.
smarktest(recentsh);
caveshlist->newindex((void **) &parysh);
*parysh = recentsh;
for (i = 0; i < caveshlist->objects; i++) {
parysh = (face *) fastlookup(caveshlist, i);
searchsh = *parysh;
searchsh.shver = 0;
for (j = 0; j < 3; j++) {
spivot(searchsh, neighsh);
// Is this side on the convex hull?
if (neighsh.sh != NULL) {
if (!smarktested(neighsh)) {
smarktest(neighsh);
caveshlist->newindex((void **) &parysh);
*parysh = neighsh;
}
} else {
// A hull side. Check if it is protected by a segment.
if (!isshsubseg(searchsh)) {
// Not protected. Save this face.
if (!sinfected(searchsh)) {
sinfect(searchsh);
caveshbdlist->newindex((void **) &parysh);
*parysh = searchsh;
}
}
}
senextself(searchsh);
}
}
// Infect the triangles in the holes.
for (i = 0; i < 3 * holes; i += 3) {
searchsh = recentsh;
loc = slocate(&(holelist[i]), &searchsh, 1, 1, 0);
if (loc != OUTSIDE) {
sinfect(searchsh);
caveshbdlist->newindex((void **) &parysh);
*parysh = searchsh;
}
}
// Find and infect all exterior triangles.
for (i = 0; i < caveshbdlist->objects; i++) {
parysh = (face *) fastlookup(caveshbdlist, i);
searchsh = *parysh;
searchsh.shver = 0;
for (j = 0; j < 3; j++) {
spivot(searchsh, neighsh);
if (neighsh.sh != NULL) {
if (!isshsubseg(searchsh)) {
if (!sinfected(neighsh)) {
sinfect(neighsh);
caveshbdlist->newindex((void **) &parysh);
*parysh = neighsh;
}
} else {
sdissolve(neighsh); // Disconnect a protected face.
}
}
senextself(searchsh);
}
}
// Delete exterior triangles, unmark interior triangles.
for (i = 0; i < caveshlist->objects; i++) {
parysh = (face *) fastlookup(caveshlist, i);
if (sinfected(*parysh)) {
shellfacedealloc(subfaces, parysh->sh);
} else {
sunmarktest(*parysh);
}
}
caveshlist->restart();
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)
{
face searchsh, newsh, *parysh;
face newseg;
point pa, pb, pc, *ppt, *cons;
int iloc;
int i, j;
if (b->verbose > 2) {
printf(" f%d: %ld vertices, %ld segments", shmark, ptlist->objects,
conlist->objects);
if (holes > 0) {
printf(", %d holes", holes);
}
printf(".\n");
}
if (ptlist->objects < 2l) {
// Not a segment or a facet.
return;
}
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);
}
if (pointtype(pa) == VOLVERTEX) {
setpointtype(pa, FACETVERTEX);
}
if (pointtype(pb) == VOLVERTEX) {
setpointtype(pb, FACETVERTEX);
}
return;
}
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.
}
}
// Create an initial triangulation.
makeshellface(subfaces, &newsh);
setshvertices(newsh, pa, pb, pc);
setshellmark(newsh, shmark);
recentsh = newsh;
if (pointtype(pa) == VOLVERTEX) {
setpointtype(pa, FACETVERTEX);
}
if (pointtype(pb) == VOLVERTEX) {
setpointtype(pb, FACETVERTEX);
}
if (pointtype(pc) == VOLVERTEX) {
setpointtype(pc, FACETVERTEX);
}
// 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.
for (i = 0; i < in->numberoffacetconstraints; i++) {
fmarker = (int) in->facetconstraintlist[i * 2];
if (fmarker == idx) {
area = in->facetconstraintlist[i * 2 + 1];
setareabound(newsh, area);
break;
}
}
}
if (ptlist->objects == 3) {
// The triangulation only has one element.
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);
ssbond(newsh, newseg);
senextself(newsh);
}
return;
}
// Incrementally build the triangulation.
pinfect(pa);
pinfect(pb);
pinfect(pc);
for (i = 0; i < ptlist->objects; i++) {
ppt = (point *) fastlookup(ptlist, i);
if (!pinfected(*ppt)) {
searchsh = recentsh; // Start from 'recentsh'.
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);
}
// 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 {
puninfect(*ppt); // This point has inserted.
}
}
// Insert the segments.
for (i = 0; i < conlist->objects; i++) {
cons = (point *) fastlookup(conlist, i);
searchsh = recentsh;
iloc = (int) slocate(cons[0], &searchsh, 1, 1, 0);
if (iloc != (enum locateresult) ONVERTEX) {
// Not found due to roundoff errors. Do a brute-force search.
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;
} else if ((point) searchsh.sh[4] == cons[0]) {
searchsh.shver = 2; break;
} else if ((point) searchsh.sh[5] == cons[0]) {
searchsh.shver = 4; break;
}
}
searchsh.sh = shellfacetraverse(subfaces);
}
assert(searchsh.sh != NULL);
}
// Recover the segment. Some edges may be flipped.
sscoutsegment(&searchsh, cons[1]);
if (flipstack != NULL) {
// Recover locally Delaunay edges.
lawsonflip();
}
}
// Remove exterior and hole triangles.
scarveholes(holes, holelist);
}
///////////////////////////////////////////////////////////////////////////////
// //
// unifysubfaces() Unify two identical subfaces. //
// //
// Two subfaces, f1 [a, b, c] and f2 [a, b, d], share the same edge [a, b]. //
// If c = d, then f1 and f2 are identical. Otherwise, these two subfaces //
// intersect, and the mesher is stopped. //
// //
// If the two subfaces are identical, we try to replace f2 by f1, i.e, all //
// neighbors of f2 are re-connected to f1. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::unifysubfaces(face *f1, face *f2)
{
if (b->psc) {
// In this case, it is possible that two subfaces are identical.
// While they must belong to two different surfaces.
return;
}
point pa, pb, pc, pd;
pa = sorg(*f1);
pb = sdest(*f1);
pc = sapex(*f1);
pd = sapex(*f2);
if (pc != pd) {
printf("Found two facets intersect each other.\n");
printf(" 1st: [%d, %d, %d] #%d\n",
pointmark(pa), pointmark(pb), pointmark(pc), shellmark(*f1));
printf(" 2nd: [%d, %d, %d] #%d\n",
pointmark(pa), pointmark(pb), pointmark(pd), shellmark(*f2));
terminatetetgen(this, 3);
} else {
printf("Found two duplicated facets.\n");
printf(" 1st: [%d, %d, %d] #%d\n",
pointmark(pa), pointmark(pb), pointmark(pc), shellmark(*f1));
printf(" 2nd: [%d, %d, %d] #%d\n",
pointmark(pa), pointmark(pb), pointmark(pd), shellmark(*f2));
terminatetetgen(this, 3);
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// unifysegments() Remove redundant segments and create face links. //
// //
// After this routine, although segments are unique, but some of them may be //
// removed later by mergefacet(). All vertices still have type FACETVERTEX. //
// //
///////////////////////////////////////////////////////////////////////////////
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");
}
// 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();
subsegloop.sh = shellfacetraverse(subsegs);
while (subsegloop.sh != (shellface *) NULL) {
torg = sorg(subsegloop);
tdest = sdest(subsegloop);
idx = pointmark(torg) - in->firstnumber;
// Loop through the set of subfaces containing 'torg'. Get all the
// subfaces containing the edge (torg, tdest). Save and order them
// in 'sfacelist', the ordering is defined by the right-hand rule
// with thumb points from torg to tdest.
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;
// Save the face f in facelink.
if (flippool->items >= 2) {
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;
}
}
// Go to the next item;
f1 = f2;
} // for (m = 0; ...)
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) {
// 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();
newlinkitem->ss = sface;
newlinkitem->nextitem = NULL;
facelink = newlinkitem;
}
} // 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.
f1 = facelink;
for (k = 0; k < flippool->items; k++) {
sspivot(f1->ss, testseg);
// If 'testseg' is not 'subsegloop' and is not dead, it is redundant.
if ((testseg.sh != subsegloop.sh) && (testseg.sh[3] != NULL)) {
shellfacedealloc(subsegs, testseg.sh);
}
// Bonds the subface and the segment together.
ssbond(f1->ss, subsegloop);
f1 = f1->nextitem;
}
// Create the face ring at the segment.
if (flippool->items > 1) {
f1 = facelink;
for (k = 1; k <= flippool->items; k++) {
k < flippool->items ? f2 = f1->nextitem : f2 = facelink;
sbond1(f1->ss, f2->ss);
f1 = f2;
}
}
// All identified segments has an init marker "0".
flippool->restart();
// Are there length constraints?
if (b->quality && (in->segmentconstraintlist != (REAL *) NULL)) {
int e1, e2;
REAL len;
for (k = 0; k < in->numberofsegmentconstraints; k++) {
e1 = (int) in->segmentconstraintlist[k * 3];
e2 = (int) in->segmentconstraintlist[k * 3 + 1];
if (((pointmark(torg) == e1) && (pointmark(tdest) == e2)) ||
((pointmark(torg) == e2) && (pointmark(tdest) == e1))) {
len = in->segmentconstraintlist[k * 3 + 2];
setareabound(subsegloop, len);
break;
}
}
}
subsegloop.sh = shellfacetraverse(subsegs);
}
delete [] idx2faclist;
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)
{
face* shperverlist;
int* idx2shlist;
face searchsh, neighsh;
face segloop, checkseg, newseg;
point checkpt, pa = NULL, pb = NULL;
int *endpts;
int edgemarker;
int idx, i, j;
int e1, e2;
REAL len;
if (!b->quiet) {
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.
for (i = 0; i < in->numberofedges; i++) {
endpts = &(in->edgelist[(i << 1)]);
edgemarker = in->edgemarkerlist ? in->edgemarkerlist[i] : 0;
// Find a face contains the edge.
newseg.sh = NULL;
searchsh.sh = NULL;
idx = endpts[0] - in->firstnumber;
for (j = idx2shlist[idx]; j < idx2shlist[idx + 1]; j++) {
checkpt = sdest(shperverlist[j]);
if (pointmark(checkpt) == endpts[1]) {
searchsh = shperverlist[j];
break; // Found.
} else {
checkpt = sapex(shperverlist[j]);
if (pointmark(checkpt) == endpts[1]) {
senext2(shperverlist[j], searchsh);
sesymself(searchsh);
break;
}
}
} // j
if (searchsh.sh != NULL) {
// Check if this edge is already a segment of the mesh.
sspivot(searchsh, checkseg);
if (checkseg.sh != NULL) {
// This segment already exist.
newseg = checkseg;
} else {
// Create a new segment at this edge.
pa = sorg(searchsh);
pb = sdest(searchsh);
makeshellface(subsegs, &newseg);
setshvertices(newseg, pa, pb, NULL);
ssbond(searchsh, newseg);
spivot(searchsh, neighsh);
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]];
// Check if segment [a,b] already exists.
// TODO: Change the brute-force search. Slow!
point *ppt;
subsegs->traversalinit();
segloop.sh = shellfacetraverse(subsegs);
while (segloop.sh != NULL) {
ppt = (point *) &(segloop.sh[3]);
if (((ppt[0] == pa) && (ppt[1] == pb)) ||
((ppt[0] == pb) && (ppt[1] == pa))) {
// Found!
newseg = segloop;
break;
}
segloop.sh = shellfacetraverse(subsegs);
}
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));
}
}
}
}
setshellmark(newseg, edgemarker);
if (b->quality && (in->segmentconstraintlist != (REAL *) NULL)) {
for (i = 0; i < in->numberofsegmentconstraints; i++) {
e1 = (int) in->segmentconstraintlist[i * 3];
e2 = (int) in->segmentconstraintlist[i * 3 + 1];
if (((pointmark(pa) == e1) && (pointmark(pb) == e2)) ||
((pointmark(pa) == e2) && (pointmark(pb) == e1))) {
len = in->segmentconstraintlist[i * 3 + 2];
setareabound(newseg, len);
break;
}
}
}
} // 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);
}
segloop.sh = shellfacetraverse(subsegs);
}
// 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);
}
pa = pointtraverse();
}
delete [] shperverlist;
delete [] idx2shlist;
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// meshsurface() Create a surface mesh of the input PLC. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::meshsurface()
{
arraypool *ptlist, *conlist;
point *idx2verlist;
point tstart, tend, *pnewpt, *cons;
tetgenio::facet *f;
tetgenio::polygon *p;
int end1, end2;
int shmark, i, j;
if (!b->quiet) {
printf("Creating surface mesh ...\n");
}
// Create a map from indices to points.
makeindex2pointmap(idx2verlist);
// Initialize arrays (block size: 2^8 = 256).
ptlist = new arraypool(sizeof(point *), 8);
conlist = new arraypool(2 * sizeof(point *), 8);
// Loop the facet list, triangulate each facet.
for (shmark = 1; shmark <= in->numberoffacets; shmark++) {
// Get a facet F.
f = &in->facetlist[shmark - 1];
// Process the duplicated points first, they are marked with type
// DUPLICATEDVERTEX. If p and q are duplicated, and p'index > q's,
// then p is substituted by q.
if (dupverts > 0l) {
// Loop all polygons of this facet.
for (i = 0; i < f->numberofpolygons; i++) {
p = &(f->polygonlist[i]);
// Loop other vertices of this polygon.
for (j = 0; j < p->numberofvertices; j++) {
end1 = p->vertexlist[j];
tstart = idx2verlist[end1];
if (pointtype(tstart) == DUPLICATEDVERTEX) {
// Reset the index of vertex-j.
tend = point2ppt(tstart);
end2 = pointmark(tend);
p->vertexlist[j] = end2;
}
}
}
}
// Loop polygons of F, get the set of vertices and segments.
for (i = 0; i < f->numberofpolygons; i++) {
// Get a polygon.
p = &(f->polygonlist[i]);
// Get the first vertex.
end1 = p->vertexlist[0];
if ((end1 < in->firstnumber) ||
(end1 >= in->firstnumber + in->numberofpoints)) {
if (!b->quiet) {
printf("Warning: Invalid the 1st vertex %d of polygon", end1);
printf(" %d in facet %d.\n", i + 1, shmark);
}
continue; // Skip this polygon.
}
tstart = idx2verlist[end1];
// Add tstart to V if it haven't been added yet.
if (!pinfected(tstart)) {
pinfect(tstart);
ptlist->newindex((void **) &pnewpt);
*pnewpt = tstart;
}
// Loop other vertices of this polygon.
for (j = 1; j <= p->numberofvertices; j++) {
// get a vertex.
if (j < p->numberofvertices) {
end2 = p->vertexlist[j];
} else {
end2 = p->vertexlist[0]; // Form a loop from last to first.
}
if ((end2 < in->firstnumber) ||
(end2 >= in->firstnumber + in->numberofpoints)) {
if (!b->quiet) {
printf("Warning: Invalid vertex %d in polygon %d", end2, i + 1);
printf(" in facet %d.\n", shmark);
}
} else {
if (end1 != end2) {
// 'end1' and 'end2' form a segment.
tend = idx2verlist[end2];
// Add tstart to V if it haven't been added yet.
if (!pinfected(tend)) {
pinfect(tend);
ptlist->newindex((void **) &pnewpt);
*pnewpt = tend;
}
// Save the segment in S (conlist).
conlist->newindex((void **) &cons);
cons[0] = tstart;
cons[1] = tend;
// Set the start for next continuous segment.
end1 = end2;
tstart = tend;
} else {
// Two identical vertices mean an isolated vertex of F.
if (p->numberofvertices > 2) {
// This may be an error in the input, anyway, we can continue
// by simply skipping this segment.
if (!b->quiet) {
printf("Warning: Polygon %d has two identical verts", i + 1);
printf(" in facet %d.\n", shmark);
}
}
// Ignore this vertex.
}
}
// Is the polygon degenerate (a segment or a vertex)?
if (p->numberofvertices == 2) break;
}
}
// Unmark vertices.
for (i = 0; i < ptlist->objects; i++) {
pnewpt = (point *) fastlookup(ptlist, i);
puninfect(*pnewpt);
}
// Triangulate F into a CDT.
triangulate(shmark, 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);
}
}
}
if (b->object == tetgenbehavior::STL) {
// Remove redundant vertices (for .stl input mesh).
jettisonnodes();
}
if (b->verbose) {
printf(" %ld (%ld) subfaces (segments).\n", subfaces->items,
subsegs->items);
}
// The total number of iunput segments.
insegments = subsegs->items;
delete [] idx2verlist;
delete ptlist;
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. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::interecursive(shellface** subfacearray, int arraysize,
int axis, REAL bxmin, REAL bxmax, REAL bymin,
REAL bymax, REAL bzmin, REAL bzmax,
int* internum)
{
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;
// The origin is fixed.
pa = org(*searchtet);
if ((point) searchtet->tet[7] == dummypoint) {
// A hull tet. Choose the neighbor of its base face.
decode(searchtet->tet[3], *searchtet);
// Reset the origin to be pa.
if ((point) searchtet->tet[4] == pa) {
searchtet->ver = 11;
} else if ((point) searchtet->tet[5] == pa) {
searchtet->ver = 3;
} else if ((point) searchtet->tet[6] == pa) {
searchtet->ver = 7;
} else {
assert((point) searchtet->tet[7] == pa);
searchtet->ver = 0;
}
}
pb = dest(*searchtet);
// Check whether the destination or apex is 'endpt'.
if (pb == endpt) {
// pa->pb is the search edge.
return ACROSSVERT;
}
pc = apex(*searchtet);
if (pc == endpt) {
// pa->pc is the search edge.
eprevesymself(*searchtet);
return ACROSSVERT;
}
// Walk through tets around pa until the right one is found.
while (1) {
pd = oppo(*searchtet);
// Check whether the opposite vertex is 'endpt'.
if (pd == endpt) {
// pa->pd is the search edge.
esymself(*searchtet);
enextself(*searchtet);
return ACROSSVERT;
}
// 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.
}
// Now assume that the base face abc coincides with the horizon plane,
// and d lies above the horizon. The search point 'endpt' may lie
// above or below the horizon. We test the orientations of 'endpt'
// with respect to three planes: abc (horizon), bad (right plane),
// and acd (left plane).
hori = orient3d(pa, pb, pc, endpt);
rori = orient3d(pb, pa, pd, endpt);
lori = orient3d(pa, pc, pd, endpt);
// Now decide the tet to move. It is possible there are more than one
// tets are viable moves. Is so, randomly choose one.
if (hori > 0) {
if (rori > 0) {
if (lori > 0) {
// Any of the three neighbors is a viable move.
s = randomnation(3);
if (s == 0) {
nextmove = HMOVE;
} else if (s == 1) {
nextmove = RMOVE;
} else {
nextmove = LMOVE;
}
} else {
// Two tets, below horizon and below right, are viable.
//s = randomnation(2);
if (randomnation(2)) {
nextmove = HMOVE;
} else {
nextmove = RMOVE;
}
}
} else {
if (lori > 0) {
// Two tets, below horizon and below left, are viable.
//s = randomnation(2);
if (randomnation(2)) {
nextmove = HMOVE;
} else {
nextmove = LMOVE;
}
} else {
// The tet below horizon is chosen.
nextmove = HMOVE;
}
}
} else {
if (rori > 0) {
if (lori > 0) {
// Two tets, below right and below left, are viable.
//s = randomnation(2);
if (randomnation(2)) {
nextmove = RMOVE;
} else {
nextmove = LMOVE;
}
} else {
// The tet below right is chosen.
nextmove = RMOVE;
}
} else {
if (lori > 0) {
// The tet below left is chosen.
nextmove = LMOVE;
} else {
// 'endpt' lies either on the plane(s) or across face bcd.
if (hori == 0) {
if (rori == 0) {
// pa->'endpt' is COLLINEAR with pa->pb.
return ACROSSVERT;
}
if (lori == 0) {
// pa->'endpt' is COLLINEAR with pa->pc.
eprevesymself(*searchtet); // // [a,c,d]
return ACROSSVERT;
}
// pa->'endpt' crosses the edge pb->pc.
return ACROSSEDGE;
}
if (rori == 0) {
if (lori == 0) {
// pa->'endpt' is COLLINEAR with pa->pd.
esymself(*searchtet); // face bad.
enextself(*searchtet); // face [a,d,b]
return ACROSSVERT;
}
// pa->'endpt' crosses the edge pb->pd.
esymself(*searchtet); // face bad.
enextself(*searchtet); // face adb
return ACROSSEDGE;
}
if (lori == 0) {
// pa->'endpt' crosses the edge pc->pd.
eprevesymself(*searchtet); // [a,c,d]
return ACROSSEDGE;
}
// pa->'endpt' crosses the face bcd.
return ACROSSFACE;
}
}
}
// Move to the next tet, fix pa as its origin.
if (nextmove == RMOVE) {
fnextself(*searchtet);
} else if (nextmove == LMOVE) {
eprevself(*searchtet);
fnextself(*searchtet);
enextself(*searchtet);
} else { // HMOVE
fsymself(*searchtet);
enextself(*searchtet);
}
assert(org(*searchtet) == pa);
pb = dest(*searchtet);
pc = apex(*searchtet);
} // while (1)
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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)
{
point pd;
enum interresult dir;
int t1ver;
if (b->verbose > 2) {
printf(" Scout seg (%d, %d).\n",pointmark(startpt),pointmark(endpt));
}
point2tetorg(startpt, *searchtet);
dir = finddirection(searchtet, endpt);
if (dir == ACROSSVERT) {
pd = dest(*searchtet);
if (pd == endpt) {
// The job is done.
return SHAREEDGE;
} else {
// A point is on the path.
// Let the origin of the searchtet be the vertex.
enextself(*searchtet);
if (refpt) *refpt = pd;
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;
}
} else if (dir == ACROSSFACE) {
if (checksubfaceflag) {
// Check whether a segment and a subface are intersecting.
if (issubface(*searchtet)) {
return ACROSSSUB;
}
}
}
if (refpt == NULL) {
// Do not need a reference point. Return.
return dir;
}
triface neightet, reftet;
point pa, pb, pc;
REAL angmax, ang;
int types[2], poss[4];
int pos = 0, i, j;
pa = org(*searchtet);
angmax = interiorangle(pa, startpt, endpt, NULL);
*refpt = pa;
pb = dest(*searchtet);
ang = interiorangle(pb, startpt, endpt, NULL);
if (ang > angmax) {
angmax = ang;
*refpt = pb;
}
pc = apex(*searchtet);
ang = interiorangle(pc, startpt, endpt, NULL);
if (ang > angmax) {
angmax = ang;
*refpt = pc;
}
reftet = *searchtet; // Save the tet containing the refpt.
// Search intersecting faces along the segment.
while (1) {
pd = oppo(*searchtet);
assert(pd != dummypoint); // SELF_CHECK
// Stop if we meet 'endpt'.
if (pd == endpt) break;
ang = interiorangle(pd, startpt, endpt, NULL);
if (ang > angmax) {
angmax = ang;
*refpt = pd;
reftet = *searchtet;
}
// Find a face intersecting the segment.
if (dir == ACROSSFACE) {
// One of the three oppo faces in 'searchtet' intersects the segment.
neightet = *searchtet;
j = (neightet.ver & 3); // j is the current face number.
for (i = j + 1; i < j + 4; i++) {
neightet.ver = (i % 4);
pa = org(neightet);
pb = dest(neightet);
pc = apex(neightet);
pd = oppo(neightet); // The above point.
if (tri_edge_test(pa, pb, pc, startpt, endpt, pd, 1, types, poss)) {
dir = (enum interresult) types[0];
pos = poss[0];
break;
} else {
dir = DISJOINT;
pos = 0;
}
}
assert(dir != DISJOINT); // SELF_CHECK
} else { // dir == ACROSSEDGE
// Check the two opposite faces (of the edge) in 'searchtet'.
for (i = 0; i < 2; i++) {
if (i == 0) {
enextesym(*searchtet, neightet);
} else {
eprevesym(*searchtet, neightet);
}
pa = org(neightet);
pb = dest(neightet);
pc = apex(neightet);
pd = oppo(neightet); // The above point.
if (tri_edge_test(pa, pb, pc, startpt, endpt, pd, 1, types, poss)) {
dir = (enum interresult) types[0];
pos = poss[0];
break;
} else {
dir = DISJOINT;
pos = 0;
}
}
if (dir == DISJOINT) {
// No intersection. Rotate to the next tet at the edge.
dir = ACROSSEDGE;
fnextself(*searchtet);
continue;
}
}
if (dir == ACROSSVERT) {
// This segment passing a vertex. Choose it and return.
for (i = 0; i < pos; i++) {
enextself(neightet);
}
pd = org(neightet);
*refpt = pd;
// break;
return ACROSSVERT;
} else if (dir == ACROSSEDGE) {
// Get the edge intersects with the segment.
for (i = 0; i < pos; i++) {
enextself(neightet);
}
}
// Go to the next tet.
fsym(neightet, *searchtet);
if (dir == ACROSSEDGE) {
// Check whether two segments are intersecting.
if (issubseg(*searchtet)) {
return ACROSSSEG;
}
} else if (dir == ACROSSFACE) {
if (checksubfaceflag) {
// Check whether a segment and a subface are intersecting.
if (issubface(*searchtet)) {
return ACROSSSUB;
}
}
}
} // while (1)
// A valid reference point should inside the diametrial circumsphere of
// the missing segment, i.e., it encroaches upon it.
if (2.0 * angmax < PI) {
*refpt = NULL;
}
*searchtet = reftet;
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'. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::getsteinerptonsegment(face* seg, point refpt, point steinpt)
{
point ei = sorg(*seg);
point ej = sdest(*seg);
int adjflag = 0, i;
if (refpt != NULL) {
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)) {
// 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]);
}
adjflag = 1;
} else if ((far_pi == far_ej) || (far_pj == 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]);
}
adjflag = 1;
} else {
// Cut the segment by the projection point of refpt.
projpt2edge(refpt, ei, ej, steinpt);
}
} else {
// 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.
L = distance(ei, ej);
L1 = distance(steinpt, ei);
t = L1 / L;
if ((t < 0.2) || (t > 0.8)) {
// Split the point at the middle.
for (i = 0; i < 3; i++) {
steinpt[i] = ei[i] + 0.5 * (ej[i] - ei[i]);
}
}
} else {
// Split the point at the middle.
for (i = 0; i < 3; i++) {
steinpt[i] = ei[i] + 0.5 * (ej[i] - ei[i]);
}
}
return adjflag;
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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()
{
triface searchtet, spintet;
face searchsh;
face sseg, *psseg;
point refpt, newpt;
enum interresult dir;
insertvertexflags ivf;
int t1ver;
ivf.bowywat = 1; // Use Bowyer-Watson insertion.
ivf.assignmeshsize = b->metric;
ivf.sloc = (int) ONEDGE; // on 'sseg'.
ivf.sbowywat = 1; // Use Bowyer-Watson insertion.
// Loop until 'subsegstack' is empty.
while (subsegstack->objects > 0l) {
// seglist is used as a stack.
subsegstack->objects--;
psseg = (face *) fastlookup(subsegstack, subsegstack->objects);
sseg = *psseg;
// Check if this segment has been recovered.
sstpivot1(sseg, searchtet);
if (searchtet.tet != NULL) {
continue; // Not a missing segment.
}
// Search the segment.
dir = scoutsegment(sorg(sseg), sdest(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);
}
} else {
if ((dir == ACROSSFACE) || (dir == ACROSSEDGE)) {
// The segment is missing. Split it.
// Create a new point.
makepoint(&newpt, FREESEGVERTEX);
//setpointtype(newpt, FREESEGVERTEX);
getsteinerptonsegment(&sseg, refpt, newpt);
// Start searching from 'searchtet'.
ivf.iloc = (int) OUTSIDE;
// Insert the new point into the tetrahedralization T.
// Missing segments and subfaces are queued for recovery.
// Note that T is convex (nonconvex = 0).
if (insertpoint(newpt, &searchtet, &searchsh, &sseg, &ivf)) {
// The new point has been inserted.
st_segref_count++;
if (steinerleft > 0) steinerleft--;
} else {
assert (ivf.iloc == (enum locateresult) NEARVERTEX);
terminatetetgen(this, 4);
}
} else {
// Indicate it is an input problem.
terminatetetgen(this, 3);
}
}
} // 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)
{
triface spintet;
point pa, pb, pc;
enum interresult dir;
int t1ver;
pa = sorg(*searchsh);
pb = sdest(*searchsh);
// Get a tet whose origin is a.
point2tetorg(pa, *searchtet);
// Search the edge [a,b].
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;
}
// The edge exists. Check if the face exists.
pc = sapex(*searchsh);
// Searchtet holds edge [a,b]. Search a face with apex c.
spintet = *searchtet;
while (1) {
if (apex(spintet) == pc) {
// Found a face matching to 'searchsh'!
if (!issubface(spintet)) {
// Insert 'searchsh'.
tsbond(spintet, *searchsh);
fsymself(spintet);
sesymself(*searchsh);
tsbond(spintet, *searchsh);
*searchtet = spintet;
return SHAREFACE;
} 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;
}
}
fnextself(spintet);
if (spintet.tet == searchtet->tet) break;
}
}
// dir is either ACROSSEDGE or ACROSSFACE.
return dir;
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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)
{
triface searchtet, spintet;
face neighsh, *parysh;
face neighseg, fakeseg;
point pa, pb, *parypt;
enum interresult dir;
int t1ver;
int i, j;
smarktest(*missh);
missingshs->newindex((void **) &parysh);
*parysh = *missh;
// Incrementally find other missing subfaces.
for (i = 0; i < missingshs->objects; i++) {
missh = (face *) fastlookup(missingshs, i);
for (j = 0; j < 3; j++) {
pa = sorg(*missh);
pb = sdest(*missh);
point2tetorg(pa, searchtet);
dir = finddirection(&searchtet, pb);
if (dir != ACROSSVERT) {
// This edge is missing. Its neighbor is a missing subface.
spivot(*missh, neighsh);
if (!smarktested(neighsh)) {
// Adjust the face orientation.
if (sorg(neighsh) != pb) sesymself(neighsh);
smarktest(neighsh);
missingshs->newindex((void **) &parysh);
*parysh = neighsh;
}
} else {
if (dest(searchtet) != pb) {
// This might be a self-intersection problem.
terminatetetgen(this, 3);
}
}
// Collect the vertices of R.
if (!pmarktested(pa)) {
pmarktest(pa);
missingshverts->newindex((void **) &parypt);
*parypt = pa;
}
senextself(*missh);
} // j
} // i
// Get the boundary edges of R.
for (i = 0; i < missingshs->objects; i++) {
missh = (face *) fastlookup(missingshs, i);
for (j = 0; j < 3; j++) {
spivot(*missh, neighsh);
if ((neighsh.sh == NULL) || !smarktested(neighsh)) {
// A boundary edge of R.
// Let the segment point to the adjacent tet.
point2tetorg(sorg(*missh), searchtet);
finddirection(&searchtet, sdest(*missh));
missingshbds->newindex((void **) &parysh);
*parysh = *missh;
// Check if this edge is a segment.
sspivot(*missh, neighseg);
if (neighseg.sh == NULL) {
// Temporarily create a segment at this edge.
makeshellface(subsegs, &fakeseg);
setsorg(fakeseg, sorg(*missh));
setsdest(fakeseg, sdest(*missh));
sinfect(fakeseg); // Mark it as faked.
// Connect it to all tets at this edge.
spintet = searchtet;
while (1) {
tssbond1(spintet, fakeseg);
fnextself(spintet);
if (spintet.tet == searchtet.tet) break;
}
neighseg = fakeseg;
}
// Let the segment and the boundary edge point to each other.
ssbond(*missh, neighseg);
sstbond1(neighseg, searchtet);
}
senextself(*missh);
} // j
} // i
// Unmarktest collected missing subfaces.
for (i = 0; i < missingshs->objects; i++) {
parysh = (face *) fastlookup(missingshs, i);
sunmarktest(*parysh);
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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;
face neighseg;
point pa, pb, pc, pd, pe;
enum interresult dir;
REAL ori;
int types[2], poss[4];
int searchflag, interflag;
int t1ver;
int i, j;
searchflag = 0;
for (j = 0; j < missingshbds->objects && !searchflag; j++) {
parysh = (face *) fastlookup(missingshbds, j);
sspivot(*parysh, neighseg);
sstpivot1(neighseg, searchtet);
interflag = 0;
// Let 'spintet' be [#,#,d,e] where [#,#] is the boundary edge of R.
spintet = searchtet;
while (1) {
pd = apex(spintet);
pe = oppo(spintet);
// Skip a hull edge.
if ((pd != dummypoint) && (pe != dummypoint)) {
// Skip an edge containing a vertex of R.
if (!pmarktested(pd) && !pmarktested(pe)) {
// Check if [d,e] intersects R.
for (i = 0; i < missingshs->objects && !interflag; i++) {
parysh = (face *) fastlookup(missingshs, i);
pa = sorg(*parysh);
pb = sdest(*parysh);
pc = sapex(*parysh);
interflag=tri_edge_test(pa, pb, pc, pd, pe, NULL, 1, types, poss);
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];
// 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);
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;
}
}
break;
} // if (interflag > 0)
}
}
}
// Leave search at this bdry edge if an intersection is found.
if (interflag > 0) break;
// Go to the next tetrahedron.
fnextself(spintet);
if (spintet.tet == searchtet.tet) break;
} // while (1)
} // j
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. //
// //
///////////////////////////////////////////////////////////////////////////////
bool tetgenmesh::formcavity(triface* searchtet, arraypool* missingshs,
arraypool* crosstets, arraypool* topfaces,
arraypool* botfaces, arraypool* toppoints,
arraypool* botpoints)
{
arraypool *crossedges;
triface spintet, neightet, *parytet;
face *parysh = NULL;
point pa, pd, pe, *parypt;
enum interresult dir;
bool testflag, invalidflag;
int types[2], poss[4];
int t1ver;
int i, j, k;
// Temporarily re-use 'topfaces' for all crossing edges.
crossedges = topfaces;
if (b->verbose > 2) {
printf(" Form the cavity of a missing region.\n");
}
// Mark this edge to avoid testing it later.
markedge(*searchtet);
crossedges->newindex((void **) &parytet);
*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++) {
// 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)) {
pinfect(pd);
botpoints->newindex((void **) &parypt);
*parypt = pd;
}
pe = dest(*searchtet);
if (!pinfected(pe)) {
pinfect(pe);
toppoints->newindex((void **) &parypt);
*parypt = pe;
}
// All tets sharing this edge are crossing tets.
spintet = *searchtet;
while (1) {
if (!infected(spintet)) {
infect(spintet);
crosstets->newindex((void **) &parytet);
*parytet = spintet;
}
// Go to the next crossing tet.
fnextself(spintet);
if (spintet.tet == searchtet->tet) break;
} // while (1)
// Detect new crossing edges.
spintet = *searchtet;
while (1) {
// spintet is [d,e,a,#], where d lies below R, and e lies above R.
pa = apex(spintet);
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.
testflag = true;
for (j = 0; j < 2 && testflag; j++) {
if (j == 0) {
enext(spintet, neightet);
} else {
eprev(spintet, neightet);
}
while (1) {
if (edgemarked(neightet)) {
// This crossing edge has already been tested. Skip it.
testflag = false;
break;
}
fnextself(neightet);
if (neightet.tet == spintet.tet) break;
}
} // j
if (testflag) {
// Test if [e,a] or [a,d] intersects R.
// Do a brute-force search in the set of subfaces of R. Slow!
// Need to be improved!
pd = org(spintet);
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)) {
// 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;
}
break;
}
if (tri_edge_test(sorg(*parysh), sdest(*parysh), sapex(*parysh),
pa, pd, NULL, 1, types, poss)) {
// 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;
}
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);
}
// 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.
if (issubseg(neightet)) {
// Invalid PLC!
//face checkseg;
//tsspivot1(neightet, checkseg);
//reportselfintersect(&checkseg, parysh);
terminatetetgen(this, 3);
}
// Mark this edge to avoid testing it again.
markedge(neightet);
crossedges->newindex((void **) &parytet);
*parytet = neightet;
} 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();
if (invalidflag) {
// Unmark all collected tets.
for (i = 0; i < crosstets->objects; i++) {
searchtet = (triface *) fastlookup(crosstets, i);
uninfect(*searchtet);
}
// Unmark all collected vertices.
for (i = 0; i < botpoints->objects; i++) {
parypt = (point *) fastlookup(botpoints, i);
puninfect(*parypt);
}
for (i = 0; i < toppoints->objects; i++) {
parypt = (point *) fastlookup(toppoints, i);
puninfect(*parypt);
}
crosstets->restart();
botpoints->restart();
toppoints->restart();
// Randomly split an interior edge of R.
i = randomnation(missingshs->objects - 1);
recentsh = * (face *) fastlookup(missingshs, i);
return false;
}
// Collect the top and bottom faces and the middle vertices. Since all top
// and bottom vertices have been infected. Uninfected vertices must be
// middle vertices (i.e., the vertices of R).
// NOTE 1: Hull tets may be collected. Process them as a normal one.
// NOTE 2: Some previously recovered subfaces may be completely inside the
// cavity. In such case, we remove these subfaces from the cavity and put
// them into 'subfacstack'. They will be recovered later.
// NOTE 3: Some segments may be completely inside the cavity, e.g., they
// attached to a subface which is inside the cavity. Such segments are
// put in 'subsegstack'. They will be recovered later.
// NOTE4 : The interior subfaces and segments mentioned in NOTE 2 and 3
// are identified in the routine "carvecavity()".
for (i = 0; i < crosstets->objects; i++) {
searchtet = (triface *) fastlookup(crosstets, i);
// searchtet is [d,e,a,b].
eorgoppo(*searchtet, spintet);
fsym(spintet, neightet); // neightet is [a,b,e,#]
if (!infected(neightet)) {
// A top face.
topfaces->newindex((void **) &parytet);
*parytet = neightet;
}
edestoppo(*searchtet, spintet);
fsym(spintet, neightet); // neightet is [b,a,d,#]
if (!infected(neightet)) {
// A bottom face.
botfaces->newindex((void **) &parytet);
*parytet = neightet;
}
// Add middle vertices if there are (skip dummypoint).
pa = org(neightet);
if (!pinfected(pa)) {
if (pa != dummypoint) {
pinfect(pa);
botpoints->newindex((void **) &parypt);
*parypt = pa;
toppoints->newindex((void **) &parypt);
*parypt = pa;
}
}
pa = dest(neightet);
if (!pinfected(pa)) {
if (pa != dummypoint) {
pinfect(pa);
botpoints->newindex((void **) &parypt);
*parypt = pa;
toppoints->newindex((void **) &parypt);
*parypt = pa;
}
}
} // i
// Uninfect all collected top, bottom, and middle vertices.
for (i = 0; i < toppoints->objects; i++) {
parypt = (point *) fastlookup(toppoints, i);
puninfect(*parypt);
}
for (i = 0; i < botpoints->objects; i++) {
parypt = (point *) fastlookup(botpoints, i);
puninfect(*parypt);
}
cavitycount++;
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'. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces,
arraypool *cavshells, arraypool *newtets,
arraypool *crosstets, arraypool *misfaces)
{
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;
int bakchecksubsegflag, bakchecksubfaceflag;
int t1ver;
int i, j;
if (b->verbose > 2) {
printf(" Delaunizing cavity: %ld points, %ld faces.\n",
cavpoints->objects, cavfaces->objects);
}
// Remember the current number of crossing tets. It may be enlarged later.
baknum = crosstets->objects;
bakhullsize = hullsize;
bakchecksubsegflag = checksubsegflag;
bakchecksubfaceflag = checksubfaceflag;
hullsize = 0l;
checksubsegflag = 0;
checksubfaceflag = 0;
b->verbose--; // Suppress informations for creating Delaunay tetra.
b->plc = 0; // Do not check near vertices.
ivf.bowywat = 1; // Use Bowyer-Watson algorithm.
// Get four non-coplanar points (no dummypoint).
pa = pb = pc = NULL;
for (i = 0; i < cavfaces->objects; i++) {
parytet = (triface *) fastlookup(cavfaces, i);
parytet->ver = epivot[parytet->ver];
if (apex(*parytet) != dummypoint) {
pa = org(*parytet);
pb = dest(*parytet);
pc = apex(*parytet);
break;
}
}
pd = NULL;
for (; i < cavfaces->objects; i++) {
parytet = (triface *) fastlookup(cavfaces, i);
pt[0] = org(*parytet);
pt[1] = dest(*parytet);
pt[2] = apex(*parytet);
for (j = 0; j < 3; j++) {
if (pt[j] != dummypoint) { // Do not include a hull point.
ori = orient3d(pa, pb, pc, pt[j]);
if (ori != 0) {
pd = pt[j];
if (ori > 0) { // Swap pa and pb.
pt[j] = pa; pa = pb; pb = pt[j];
}
break;
}
}
}
if (pd != NULL) break;
}
assert(i < cavfaces->objects); // SELF_CHECK
// Create an init DT.
initialdelaunay(pa, pb, pc, pd);
// Incrementally insert the vertices (duplicated vertices are ignored).
for (i = 0; i < cavpoints->objects; i++) {
pt[0] = * (point *) fastlookup(cavpoints, i);
searchtet = recenttet;
ivf.iloc = (int) OUTSIDE;
insertpoint(pt[0], &searchtet, NULL, NULL, &ivf);
}
if (b->verbose > 2) {
printf(" Identifying %ld boundary faces of the cavity.\n",
cavfaces->objects);
}
while (1) {
// Identify boundary faces. Mark interior tets. Save missing faces.
for (i = 0; i < cavfaces->objects; i++) {
parytet = (triface *) fastlookup(cavfaces, i);
// Skip an interior face (due to the enlargement of the cavity).
if (infected(*parytet)) continue;
parytet->ver = epivot[parytet->ver];
pt[0] = org(*parytet);
pt[1] = dest(*parytet);
pt[2] = apex(*parytet);
// Create a temp subface.
makeshellface(subfaces, &tmpsh);
setshvertices(tmpsh, pt[0], pt[1], pt[2]);
// Insert tmpsh in DT.
searchtet.tet = NULL;
dir = scoutsubface(&tmpsh, &searchtet);
if (dir == SHAREFACE) {
// Inserted! 'tmpsh' must face toward the inside of the cavity.
// Remember the boundary tet (outside the cavity) in tmpsh
// (use the adjacent tet slot).
tmpsh.sh[0] = (shellface) encode(*parytet);
// Save this subface.
cavshells->newindex((void **) &parysh);
*parysh = tmpsh;
}
else {
// This boundary face is missing.
shellfacedealloc(subfaces, tmpsh.sh);
// Save this face in list.
misfaces->newindex((void **) &parytet1);
*parytet1 = *parytet;
}
} // i
if (misfaces->objects > 0) {
if (b->verbose > 2) {
printf(" Enlarging the cavity. %ld missing bdry faces\n",
misfaces->objects);
}
// Removing all temporary subfaces.
for (i = 0; i < cavshells->objects; i++) {
parysh = (face *) fastlookup(cavshells, i);
stpivot(*parysh, neightet);
tsdissolve(neightet); // Detach it from adj. tets.
fsymself(neightet);
tsdissolve(neightet);
shellfacedealloc(subfaces, parysh->sh);
}
cavshells->restart();
// Infect the points which are of the cavity.
for (i = 0; i < cavpoints->objects; i++) {
pt[0] = * (point *) fastlookup(cavpoints, i);
pinfect(pt[0]); // Mark it as inserted.
}
// Enlarge the cavity.
for (i = 0; i < misfaces->objects; i++) {
// Get a missing face.
parytet = (triface *) fastlookup(misfaces, i);
if (!infected(*parytet)) {
// Put it into crossing tet list.
infect(*parytet);
crosstets->newindex((void **) &parytet1);
*parytet1 = *parytet;
// Insert the opposite point if it is not in DT.
pd = oppo(*parytet);
if (!pinfected(pd)) {
searchtet = recenttet;
ivf.iloc = (int) OUTSIDE;
insertpoint(pd, &searchtet, NULL, NULL, &ivf);
pinfect(pd);
cavpoints->newindex((void **) &parypt);
*parypt = pd;
}
// Add three opposite faces into the boundary list.
for (j = 0; j < 3; j++) {
esym(*parytet, neightet);
fsymself(neightet);
if (!infected(neightet)) {
cavfaces->newindex((void **) &parytet1);
*parytet1 = neightet;
}
enextself(*parytet);
} // j
} // if (!infected(parytet))
} // i
// Uninfect the points which are of the cavity.
for (i = 0; i < cavpoints->objects; i++) {
pt[0] = * (point *) fastlookup(cavpoints, i);
puninfect(pt[0]);
}
misfaces->restart();
continue;
} // if (misfaces->objects > 0)
break;
} // while (1)
// Collect all tets of the DT. All new tets are marktested.
marktest(recenttet);
newtets->newindex((void **) &parytet);
*parytet = recenttet;
for (i = 0; i < newtets->objects; i++) {
searchtet = * (triface *) fastlookup(newtets, i);
for (j = 0; j < 4; j++) {
decode(searchtet.tet[j], neightet);
if (!marktested(neightet)) {
marktest(neightet);
newtets->newindex((void **) &parytet);
*parytet = neightet;
}
}
}
cavpoints->restart();
cavfaces->restart();
if (crosstets->objects > baknum) {
// The cavity has been enlarged.
cavityexpcount++;
}
// Restore the original values.
hullsize = bakhullsize;
checksubsegflag = bakchecksubsegflag;
checksubfaceflag = bakchecksubfaceflag;
b->verbose++;
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. //
// //
///////////////////////////////////////////////////////////////////////////////
bool tetgenmesh::fillcavity(arraypool* topshells, arraypool* botshells,
arraypool* midfaces, arraypool* missingshs,
arraypool* topnewtets, arraypool* botnewtets,
triface* crossedge)
{
arraypool *cavshells;
triface bdrytet, neightet, *parytet;
triface searchtet, spintet;
face *parysh;
face checkseg;
point pa, pb, pc;
bool mflag;
int t1ver;
int i, j;
// Connect newtets to tets outside the cavity. These connections are needed
// for identifying the middle faces (which belong to R).
for (j = 0; j < 2; j++) {
cavshells = (j == 0 ? topshells : botshells);
if (cavshells != NULL) {
for (i = 0; i < cavshells->objects; i++) {
// Get a temp subface.
parysh = (face *) fastlookup(cavshells, i);
// Get the boundary tet outside the cavity (saved in sh[0]).
decode(parysh->sh[0], bdrytet);
pa = org(bdrytet);
pb = dest(bdrytet);
pc = apex(bdrytet);
// Get the adjacent new tet inside the cavity.
stpivot(*parysh, neightet);
// Mark neightet as an interior tet of this cavity.
infect(neightet);
// Connect the two tets (the old connections are replaced).
bond(bdrytet, neightet);
tsdissolve(neightet); // Clear the pointer to tmpsh.
// Update the point-to-tets map.
setpoint2tet(pa, (tetrahedron) neightet.tet);
setpoint2tet(pb, (tetrahedron) neightet.tet);
setpoint2tet(pc, (tetrahedron) neightet.tet);
} // i
} // if (cavshells != NULL)
} // j
if (crossedge != NULL) {
// Glue top and bottom tets at their common facet.
triface toptet, bottet, spintet, *midface;
point pd, pe;
REAL ori;
int types[2], poss[4];
int interflag;
int bflag;
mflag = false;
pd = org(*crossedge);
pe = dest(*crossedge);
// Search the first (middle) face in R.
// Since R may be non-convex, we must make sure that the face is in the
// interior of R. We search a face in 'topnewtets' whose three vertices
// are on R and it intersects 'crossedge' in its interior. Then search
// a matching face in 'botnewtets'.
for (i = 0; i < topnewtets->objects && !mflag; i++) {
searchtet = * (triface *) fastlookup(topnewtets, i);
for (searchtet.ver = 0; searchtet.ver < 4 && !mflag; searchtet.ver++) {
pa = org(searchtet);
if (pmarktested(pa)) {
pb = dest(searchtet);
if (pmarktested(pb)) {
pc = apex(searchtet);
if (pmarktested(pc)) {
// Check if this face intersects [d,e].
interflag = tri_edge_test(pa,pb,pc,pd,pe,NULL,1,types,poss);
if (interflag == 2) {
// They intersect at a single point. Found.
toptet = searchtet;
// 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);
}
// Search the face [b,a,c] in 'botnewtets'.
for (j = 0; j < botnewtets->objects; j++) {
neightet = * (triface *) fastlookup(botnewtets, j);
// Is neightet contains 'b'.
if ((point) neightet.tet[4] == pb) {
neightet.ver = 11;
} else if ((point) neightet.tet[5] == pb) {
neightet.ver = 3;
} else if ((point) neightet.tet[6] == pb) {
neightet.ver = 7;
} else if ((point) neightet.tet[7] == pb) {
neightet.ver = 0;
} else {
continue;
}
// Is the 'neightet' contains edge [b,a].
if (dest(neightet) == pa) {
// 'neightet' is just the edge.
} else if (apex(neightet) == pa) {
eprevesymself(neightet);
} else if (oppo(neightet) == pa) {
esymself(neightet);
enextself(neightet);
} else {
continue;
}
// Is 'neightet' the face [b,a,c].
if (apex(neightet) == pc) {
bottet = neightet;
mflag = true;
break;
}
} // j
} // if (interflag == 2)
} // pc
} // pb
} // pa
} // toptet.ver
} // i
if (mflag) {
// Found a pair of matched faces in 'toptet' and 'bottet'.
bond(toptet, bottet);
// Both are interior tets.
infect(toptet);
infect(bottet);
// Add this face into search list.
markface(toptet);
midfaces->newindex((void **) &parytet);
*parytet = toptet;
} else {
// No pair of 'toptet' and 'bottet'.
toptet.tet = NULL;
// Randomly split an interior edge of R.
i = randomnation(missingshs->objects - 1);
recentsh = * (face *) fastlookup(missingshs, i);
}
// Find other middle faces, connect top and bottom tets.
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;
bflag = false;
while (1) {
// Go to the next face in the same tet.
esymself(toptet);
pc = apex(toptet);
if (pmarktested(pc)) {
break; // Find a subface.
}
if (pc == dummypoint) {
assert(0); // Check this case.
break; // Find a subface.
}
// Go to the adjacent tet.
fsymself(toptet);
// Do we walk outside the cavity?
if (!marktested(toptet)) {
// Yes, the adjacent face is not a middle face.
bflag = true; break;
}
}
if (!bflag) {
// assert(marktested(toptet)); // SELF_CHECK
if (!facemarked(toptet)) {
fsym(*midface, bottet);
spintet = bottet;
while (1) {
esymself(bottet);
pd = apex(bottet);
if (pd == pc) break; // Face matched.
fsymself(bottet);
if (bottet.tet == spintet.tet) {
// Not found a matched bottom face.
mflag = false;
break;
}
} // while (1)
if (mflag) {
if (marktested(bottet)) {
// Connect two tets together.
bond(toptet, bottet);
// Both are interior tets.
infect(toptet);
infect(bottet);
// Add this face into list.
markface(toptet);
midfaces->newindex((void **) &parytet);
*parytet = toptet;
}
} else { // mflag == false
// Adjust 'toptet' and 'bottet' to be the crossing edges.
fsym(*midface, bottet);
spintet = bottet;
while (1) {
esymself(bottet);
pd = apex(bottet);
if (pmarktested(pd)) {
// assert(pd != pc);
// Let 'toptet' be [a,b,c,#], and 'bottet' be [b,a,d,*].
// Adjust 'toptet' and 'bottet' to be the crossing edges.
// Test orient3d(b,c,#,d).
ori = orient3d(dest(toptet), pc, oppo(toptet), pd);
if (ori < 0) {
// Edges [a,d] and [b,c] cross each other.
enextself(toptet); // [b,c]
enextself(bottet); // [a,d]
} else if (ori > 0) {
// Edges [a,c] and [b,d] cross each other.
eprevself(toptet); // [c,a]
eprevself(bottet); // [d,b]
} else {
// b,c,#,and d are coplanar!.
assert(0);
}
break; // Not matched
}
fsymself(bottet);
assert (bottet.tet != spintet.tet);
}
} // if (!mflag)
} // if (!facemarked(toptet))
} // if (!bflag)
enextself(*midface);
} // j
} // i
if (mflag) {
if (b->verbose > 2) {
printf(" Found %ld middle subfaces.\n", midfaces->objects);
}
face oldsh, newsh, casout, casin, neighsh;
oldsh = * (face *) fastlookup(missingshs, 0);
// Create new subfaces to fill the region R.
for (i = 0; i < midfaces->objects; i++) {
// Get a matched middle face [a, b, c]
midface = (triface *) fastlookup(midfaces, i);
unmarkface(*midface);
makeshellface(subfaces, &newsh);
setsorg(newsh, org(*midface));
setsdest(newsh, dest(*midface));
setsapex(newsh, apex(*midface));
// 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(*midface, newsh);
fsym(*midface, neightet);
sesymself(newsh);
tsbond(neightet, newsh);
}
// Connect new subfaces together and to the bdry of R.
// Delete faked segments.
for (i = 0; i < midfaces->objects; i++) {
// Get a matched middle face [a, b, c]
midface = (triface *) fastlookup(midfaces, i);
for (j = 0; j < 3; j++) {
tspivot(*midface, newsh);
spivot(newsh, casout);
if (casout.sh == NULL) {
// Search its neighbor.
fnext(*midface, searchtet);
while (1) {
// (1) First check if this side is a bdry edge of R.
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);
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(newsh) != sorg(checkseg)) {
sesymself(newsh);
}
spivot(casin, neighsh);
while (neighsh.sh != oldsh.sh) {
casin = neighsh;
spivot(casin, neighsh);
}
}
sbond1(newsh, casout);
sbond1(casin, newsh);
}
if (checkseg.sh != NULL) {
ssbond(newsh, checkseg);
}
break;
} // if (checkseg.sh != NULL)
// (2) Second check if this side is an interior edge of R.
tspivot(searchtet, neighsh);
if (neighsh.sh != NULL) {
// Found an adjacent subface of newsh (an interior edge).
sbond(newsh, neighsh);
break;
}
fnextself(searchtet);
assert(searchtet.tet != midface->tet);
} // while (1)
} // if (casout.sh == NULL)
enextself(*midface);
} // j
} // i
// Delete old subfaces.
for (i = 0; i < missingshs->objects; i++) {
parysh = (face *) fastlookup(missingshs, i);
shellfacedealloc(subfaces, parysh->sh);
}
} else {
if (toptet.tet != NULL) {
// Faces at top and bottom are not matched.
// Choose a Steiner point in R.
// Split one of the crossing edges.
pa = org(toptet);
pb = dest(toptet);
pc = org(bottet);
pd = dest(bottet);
// 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.
for (i = 1; i < missingshs->objects; i++) {
parysh = (face *) fastlookup(missingshs, i);
if (((sorg(*parysh) == pa) && (sdest(*parysh) == pb)) ||
((sorg(*parysh) == pb) && (sdest(*parysh) == pa))) break;
if (((sorg(*parysh) == pc) && (sdest(*parysh) == pd)) ||
((sorg(*parysh) == pd) && (sdest(*parysh) == pc))) break;
}
if (i < missingshs->objects) {
// Found. Return it.
recentsh = *parysh;
} else {
assert(0);
}
}
}
midfaces->restart();
} else {
mflag = true;
}
// Delete the temp subfaces.
for (j = 0; j < 2; j++) {
cavshells = (j == 0 ? topshells : botshells);
if (cavshells != NULL) {
for (i = 0; i < cavshells->objects; i++) {
parysh = (face *) fastlookup(cavshells, i);
shellfacedealloc(subfaces, parysh->sh);
}
}
}
topshells->restart();
if (botshells != NULL) {
botshells->restart();
}
return mflag;
}
///////////////////////////////////////////////////////////////////////////////
// //
// carvecavity() Delete old tets and outer new tets of the cavity. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::carvecavity(arraypool *crosstets, arraypool *topnewtets,
arraypool *botnewtets)
{
arraypool *newtets;
shellface *sptr, *ssptr;
triface *parytet, *pnewtet, newtet, neightet, spintet;
face checksh, *parysh;
face checkseg, *paryseg;
int t1ver;
int i, j;
if (b->verbose > 2) {
printf(" Carve cavity: %ld old tets.\n", crosstets->objects);
}
// First process subfaces and segments which are adjacent to the cavity.
// They must be re-connected to new tets in the cavity.
// Comment: It is possible that some subfaces and segments are completely
// inside the cavity. This can happen even if the cavity is not enlarged.
// Before deleting the old tets, find and queue all interior subfaces
// and segments. They will be recovered later. 2010-05-06.
// Collect all subfaces and segments which attached to the old tets.
for (i = 0; i < crosstets->objects; i++) {
parytet = (triface *) fastlookup(crosstets, i);
if ((sptr = (shellface*) parytet->tet[9]) != NULL) {
for (j = 0; j < 4; j++) {
if (sptr[j]) {
sdecode(sptr[j], checksh);
if (!sinfected(checksh)) {
sinfect(checksh);
cavetetshlist->newindex((void **) &parysh);
*parysh = checksh;
}
}
} // j
}
if ((ssptr = (shellface*) parytet->tet[8]) != NULL) {
for (j = 0; j < 6; j++) {
if (ssptr[j]) {
sdecode(ssptr[j], checkseg);
// Skip a deleted segment (was a faked segment)
if (checkseg.sh[3] != NULL) {
if (!sinfected(checkseg)) {
sinfect(checkseg);
cavetetseglist->newindex((void **) &paryseg);
*paryseg = checkseg;
}
}
}
} // j
}
} // i
// Uninfect collected subfaces.
for (i = 0; i < cavetetshlist->objects; i++) {
parysh = (face *) fastlookup(cavetetshlist, i);
suninfect(*parysh);
}
// Uninfect collected segments.
for (i = 0; i < cavetetseglist->objects; i++) {
paryseg = (face *) fastlookup(cavetetseglist, i);
suninfect(*paryseg);
}
// Connect subfaces to new tets.
for (i = 0; i < cavetetshlist->objects; i++) {
parysh = (face *) fastlookup(cavetetshlist, i);
// Get an adjacent tet at this subface.
stpivot(*parysh, neightet);
// Does this tet lie inside the cavity.
if (infected(neightet)) {
// Yes. Get the other adjacent tet at this subface.
sesymself(*parysh);
stpivot(*parysh, neightet);
// Does this tet lie inside the cavity.
if (infected(neightet)) {
checksh = *parysh;
stdissolve(checksh);
caveencshlist->newindex((void **) &parysh);
*parysh = checksh;
}
}
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);
}
} // i
for (i = 0; i < cavetetseglist->objects; i++) {
checkseg = * (face *) fastlookup(cavetetseglist, i);
// Check if the segment is inside the cavity.
sstpivot1(checkseg, neightet);
spintet = neightet;
while (1) {
if (!infected(spintet)) {
// This segment is on the boundary of the cavity.
break;
}
fnextself(spintet);
if (spintet.tet == neightet.tet) {
sstdissolve1(checkseg);
caveencseglist->newindex((void **) &paryseg);
*paryseg = checkseg;
break;
}
}
if (!infected(spintet)) {
// A boundary segment. Connect this segment to the new tets.
sstbond1(checkseg, spintet);
neightet = spintet;
while (1) {
tssbond1(spintet, checkseg);
fnextself(spintet);
if (spintet.tet == neightet.tet) break;
}
}
} // i
cavetetshlist->restart();
cavetetseglist->restart();
// Delete the old tets in cavity.
for (i = 0; i < crosstets->objects; i++) {
parytet = (triface *) fastlookup(crosstets, i);
if (ishulltet(*parytet)) {
hullsize--;
}
tetrahedrondealloc(parytet->tet);
}
crosstets->restart(); // crosstets will be re-used.
// Collect new tets in cavity. Some new tets have already been found
// (and infected) in the fillcavity(). We first collect them.
for (j = 0; j < 2; j++) {
newtets = (j == 0 ? topnewtets : botnewtets);
if (newtets != NULL) {
for (i = 0; i < newtets->objects; i++) {
parytet = (triface *) fastlookup(newtets, i);
if (infected(*parytet)) {
crosstets->newindex((void **) &pnewtet);
*pnewtet = *parytet;
}
} // i
}
} // j
// Now we collect all new tets in cavity.
for (i = 0; i < crosstets->objects; i++) {
parytet = (triface *) fastlookup(crosstets, i);
for (j = 0; j < 4; j++) {
decode(parytet->tet[j], neightet);
if (marktested(neightet)) { // Is it a new tet?
if (!infected(neightet)) {
// Find an interior tet.
//assert((point) neightet.tet[7] != dummypoint); // SELF_CHECK
infect(neightet);
crosstets->newindex((void **) &pnewtet);
*pnewtet = neightet;
}
}
} // j
} // i
parytet = (triface *) fastlookup(crosstets, 0);
recenttet = *parytet; // Remember a live handle.
// Delete outer new tets.
for (j = 0; j < 2; j++) {
newtets = (j == 0 ? topnewtets : botnewtets);
if (newtets != NULL) {
for (i = 0; i < newtets->objects; i++) {
parytet = (triface *) fastlookup(newtets, i);
if (infected(*parytet)) {
// This is an interior tet.
uninfect(*parytet);
unmarktest(*parytet);
if (ishulltet(*parytet)) {
hullsize++;
}
} else {
// An outer tet. Delete it.
tetrahedrondealloc(parytet->tet);
}
}
}
}
crosstets->restart();
topnewtets->restart();
if (botnewtets != NULL) {
botnewtets->restart();
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// restorecavity() Reconnect old tets and delete new tets of the cavity. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::restorecavity(arraypool *crosstets, arraypool *topnewtets,
arraypool *botnewtets, arraypool *missingshbds)
{
triface *parytet, neightet, spintet;
face *parysh;
face checkseg;
point *ppt;
int t1ver;
int i, j;
// 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);
if (!infected(neightet)) {
// Restore the old connections of tets.
bond(*parytet, neightet);
}
}
// Update the point-to-tet map.
parytet->ver = 0;
ppt = (point *) &(parytet->tet[4]);
for (j = 0; j < 4; j++) {
setpoint2tet(ppt[j], encode(*parytet));
}
}
// Uninfect all crossing tets.
for (i = 0; i < crosstets->objects; i++) {
parytet = (triface *) fastlookup(crosstets, i);
uninfect(*parytet);
}
// Remember a live handle.
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.
sstpivot1(checkseg, neightet);
spintet = neightet;
while (1) {
tssdissolve1(spintet);
fnextself(spintet);
if (spintet.tet == neightet.tet) break;
}
shellfacedealloc(subsegs, checkseg.sh);
ssdissolve(*parysh);
//checkseg.sh = NULL;
}
}
} // i
// Delete new tets.
for (i = 0; i < topnewtets->objects; i++) {
parytet = (triface *) fastlookup(topnewtets, i);
tetrahedrondealloc(parytet->tet);
}
if (botnewtets != NULL) {
for (i = 0; i < botnewtets->objects; i++) {
parytet = (triface *) fastlookup(botnewtets, i);
tetrahedrondealloc(parytet->tet);
}
}
crosstets->restart();
topnewtets->restart();
if (botnewtets != NULL) {
botnewtets->restart();
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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)
{
badface *parybf, *prevbf, *nextbf;
triface neightet;
face checksh;
point p[5];
REAL w[5];
REAL insph, ori4;
int topi, boti;
int i;
// Compute the flip time \tau.
fsym(*chkface, neightet);
p[0] = org(*chkface);
p[1] = dest(*chkface);
p[2] = apex(*chkface);
p[3] = oppo(*chkface);
p[4] = oppo(neightet);
// Check if the face is a crossing face.
topi = boti = 0;
for (i = 0; i < 3; i++) {
if (pmarktest2ed(p[i])) topi++;
if (pmarktest3ed(p[i])) boti++;
}
if ((topi == 0) || (boti == 0)) {
// It is not a crossing face.
// return;
for (i = 3; i < 5; i++) {
if (pmarktest2ed(p[i])) topi++;
if (pmarktest3ed(p[i])) boti++;
}
if ((topi == 0) || (boti == 0)) {
// The two tets sharing at this face are on one side of the facet.
// Check if this face is locally Delaunay (due to rounding error).
if ((p[3] != dummypoint) && (p[4] != dummypoint)) {
// Do not check it if it is a subface.
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) {
printf(" A locally non-Delanay face (%d, %d, %d)-%d,%d\n",
pointmark(p[0]), pointmark(p[1]), pointmark(p[2]),
pointmark(p[3]), pointmark(p[4]));
}
parybf = (badface *) flippool->alloc();
parybf->key = 0.; // tau = 0, do immediately.
parybf->tt = *chkface;
parybf->forg = p[0];
parybf->fdest = p[1];
parybf->fapex = p[2];
parybf->foppo = p[3];
parybf->noppo = p[4];
// Add it at the top of the priority queue.
if (*pqueue == NULL) {
*pqueue = parybf;
parybf->nextitem = NULL;
} else {
parybf->nextitem = *pqueue;
*pqueue = parybf;
}
} // if (insph > 0)
} // if (checksh.sh == NULL)
}
//return;
}
return; // Test: omit this face.
}
// Decide the "height" for each point.
for (i = 0; i < 5; i++) {
if (pmarktest2ed(p[i])) {
// 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) {
printf(" Insert face (%d, %d, %d) - %d, %d\n", pointmark(p[0]),
pointmark(p[1]), pointmark(p[2]), pointmark(p[3]), pointmark(p[4]));
}
parybf = (badface *) flippool->alloc();
parybf->key = -insph / ori4;
parybf->tt = *chkface;
parybf->forg = p[0];
parybf->fdest = p[1];
parybf->fapex = p[2];
parybf->foppo = p[3];
parybf->noppo = p[4];
// Push the face into priority queue.
//pq.push(bface);
if (*pqueue == NULL) {
*pqueue = parybf;
parybf->nextitem = NULL;
} else {
// Search an item whose key is larger or equal to current key.
prevbf = NULL;
nextbf = *pqueue;
//if (!b->flipinsert_random) { // Default use a priority queue.
// Insert the item into priority queue.
while (nextbf != NULL) {
if (nextbf->key < parybf->key) {
prevbf = nextbf;
nextbf = nextbf->nextitem;
} else {
break;
}
}
//} // if (!b->flipinsert_random)
// Insert the new item between prev and next items.
if (prevbf == NULL) {
*pqueue = parybf;
} else {
prevbf->nextitem = parybf;
}
parybf->nextitem = nextbf;
}
} else if (ori4 == 0) {
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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)
{
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;
point p1, p2, pd, pe;
point *parypt;
flipconstraints fc;
REAL ori[3];
int convcount, copcount;
int flipflag, fcount;
int n, i;
long f23count, f32count, f44count;
long totalfcount;
f23count = flip23count;
f32count = flip32count;
f44count = flip44count;
// Get three affinely independent vertices in the missing region R.
calculateabovepoint(midpoints, &plane_pa, &plane_pb, &plane_pc);
// Mark top and bottom points. Do not mark midpoints.
for (i = 0; i < toppoints->objects; i++) {
parypt = (point *) fastlookup(toppoints, i);
if (!pmarktested(*parypt)) {
pmarktest2(*parypt);
}
}
for (i = 0; i < botpoints->objects; i++) {
parypt = (point *) fastlookup(botpoints, i);
if (!pmarktested(*parypt)) {
pmarktest3(*parypt);
}
}
// Collect crossing faces.
crossfaces = cavetetlist; // Re-use array 'cavetetlist'.
// Each crossing face contains at least one bottom vertex and
// one top vertex.
for (i = 0; i < crosstets->objects; i++) {
parytet = (triface *) fastlookup(crosstets, i);
fliptet = *parytet;
for (fliptet.ver = 0; fliptet.ver < 4; fliptet.ver++) {
fsym(fliptet, neightet);
if (infected(neightet)) { // It is an interior face.
if (!marktested(neightet)) { // It is an unprocessed face.
crossfaces->newindex((void **) &parytet);
*parytet = fliptet;
}
}
}
marktest(fliptet);
}
if (b->verbose > 1) {
printf(" Found %ld crossing faces.\n", crossfaces->objects);
}
for (i = 0; i < crosstets->objects; i++) {
parytet = (triface *) fastlookup(crosstets, i);
unmarktest(*parytet);
uninfect(*parytet);
}
// Initialize the priority queue.
pqueue = NULL;
for (i = 0; i < crossfaces->objects; i++) {
parytet = (triface *) fastlookup(crossfaces, i);
flipcertify(parytet, &pqueue, plane_pa, plane_pb, plane_pc);
}
crossfaces->restart();
// The list for temporarily storing unflipable faces.
bfacearray = new arraypool(sizeof(triface), 4);
fcount = 0; // Count the number of flips.
// Flip insert the facet.
while (pqueue != NULL) {
// 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);
if (!isdeadtet(bface.tt)) {
if ((org(bface.tt) == bface.forg) && (dest(bface.tt) == bface.fdest) &&
(apex(bface.tt) == bface.fapex) && (oppo(bface.tt) == bface.foppo)) {
// 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);
if (b->verbose > 2) {
printf(" Get face (%d, %d, %d) - %d, %d, tau = %.17g\n",
pointmark(bface.forg), pointmark(bface.fdest),
pointmark(bface.fapex), pointmark(bface.foppo),
pointmark(bface.noppo), bface.key);
}
flipflag = 0;
// Check for which type of flip can we do.
convcount = 3;
copcount = 0;
for (i = 0; i < 3; i++) {
p1 = org(fliptet);
p2 = dest(fliptet);
ori[i] = orient3d(p1, p2, pd, pe);
if (ori[i] < 0) {
convcount--;
//break;
} else if (ori[i] == 0) {
convcount--; // Possible 4-to-4 flip.
copcount++;
//break;
}
enextself(fliptet);
}
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);
// Put the link faces into check list.
for (i = 0; i < 3; i++) {
eprevesym(fliptets[i], newface);
crossfaces->newindex((void **) &parytet);
*parytet = newface;
}
for (i = 0; i < 3; i++) {
enextesym(fliptets[i], newface);
crossfaces->newindex((void **) &parytet);
*parytet = newface;
}
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.
for (i = 0; i < 3; i++) {
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 {
p1 = apex(fliptets[n]);
if (!(pmarktested(p1) || pmarktest2ed(p1) || pmarktest3ed(p1))) {
// This apex is not on the cavity. Hence the face does not
// lie inside the cavity. Do not flip this edge.
n = 1000; break;
}
fnext(fliptets[n], fliptets[n + 1]);
n++;
} while ((fliptets[n].tet != fliptet.tet) && (n < 5));
if (n == 3) {
// Found a 3-to-2 flip.
flip32(fliptets, 1, &fc);
// Put the link faces into check list.
for (i = 0; i < 3; i++) {
esym(fliptets[0], newface);
crossfaces->newindex((void **) &parytet);
*parytet = newface;
enextself(fliptets[0]);
}
for (i = 0; i < 3; i++) {
esym(fliptets[1], newface);
crossfaces->newindex((void **) &parytet);
*parytet = newface;
enextself(fliptets[1]);
}
flipflag = 1;
} else if (n == 4) {
if (copcount == 1) {
// Found a 4-to-4 flip.
// Let the six vertices are: a,b,c,d,e,f, where
// fliptets[0] = [b,a,d,c]
// [1] = [b,a,c,e]
// [2] = [b,a,e,f]
// [3] = [b,a,f,d]
// After the 4-to-4 flip, edge [a,b] is flipped, edge [e,d]
// is created.
// First do a 2-to-3 flip.
// Comment: This flip temporarily creates a degenerated
// tet (whose volume is zero). It will be removed by the
// followed 3-to-2 flip.
fliptets[0] = fliptet; // = [a,b,c,d], d is the new vertex.
// fliptets[1]; // = [b,a,c,e].
baktets[0] = fliptets[2]; // = [b,a,e,f]
baktets[1] = fliptets[3]; // = [b,a,f,d]
// The flip may involve hull tets.
flip23(fliptets, 1, &fc);
// Put the "outer" link faces into check list.
// fliptets[0] = [e,d,a,b] => will be flipped, so
// [a,b,d] and [a,b,e] are not "outer" link faces.
for (i = 1; i < 3; i++) {
eprevesym(fliptets[i], newface);
crossfaces->newindex((void **) &parytet);
*parytet = newface;
}
for (i = 1; i < 3; i++) {
enextesym(fliptets[i], newface);
crossfaces->newindex((void **) &parytet);
*parytet = newface;
}
// Then do a 3-to-2 flip.
enextesymself(fliptets[0]); // fliptets[0] is [e,d,a,b].
eprevself(fliptets[0]); // = [b,a,d,c], d is the new vertex.
fliptets[1] = baktets[0]; // = [b,a,e,f]
fliptets[2] = baktets[1]; // = [b,a,f,d]
flip32(fliptets, 1, &fc);
// Put the "outer" link faces into check list.
// fliptets[0] = [d,e,f,a]
// fliptets[1] = [e,d,f,b]
// Faces [a,b,d] and [a,b,e] are not "outer" link faces.
enextself(fliptets[0]);
for (i = 1; i < 3; i++) {
esym(fliptets[0], newface);
crossfaces->newindex((void **) &parytet);
*parytet = newface;
enextself(fliptets[0]);
}
enextself(fliptets[1]);
for (i = 1; i < 3; i++) {
esym(fliptets[1], newface);
crossfaces->newindex((void **) &parytet);
*parytet = newface;
enextself(fliptets[1]);
}
flip23count--;
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.
flipflag = -1; // Ignore this face.
if (b->verbose > 2) {
printf(" Ignore face (%d, %d, %d) - %d, %d, tau = %.17g\n",
pointmark(bface.forg), pointmark(bface.fdest),
pointmark(bface.fapex), pointmark(bface.foppo),
pointmark(bface.noppo), bface.key);
}
} // if (convcount == 1)
if (flipflag == 1) {
// Update the priority queue.
for (i = 0; i < crossfaces->objects; i++) {
parytet = (triface *) fastlookup(crossfaces, i);
flipcertify(parytet, &pqueue, plane_pa, plane_pb, plane_pc);
}
crossfaces->restart();
if (1) { // if (!b->flipinsert_random) {
// Insert all queued unflipped faces.
for (i = 0; i < bfacearray->objects; i++) {
parytet = (triface *) fastlookup(bfacearray, i);
// This face may be changed.
if (!isdeadtet(*parytet)) {
flipcertify(parytet, &pqueue, plane_pa, plane_pb, plane_pc);
}
}
bfacearray->restart();
}
fcount++;
} else if (flipflag == 0) {
// Queue an unflippable face. To process it later.
bfacearray->newindex((void **) &parytet);
*parytet = fliptet;
}
} // if (pe == bface.noppo)
} // if ((pa == bface.forg) && ...)
} // if (bface.tt != NULL)
} // while (pqueue != NULL)
if (bfacearray->objects > 0) {
if (fcount == 0) {
printf("!! No flip is found in %ld faces.\n", bfacearray->objects);
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.
for (i = 0; i < toppoints->objects; i++) {
parypt = (point *) fastlookup(toppoints, i);
punmarktest2(*parypt);
}
for (i = 0; i < botpoints->objects; i++) {
parypt = (point *) fastlookup(botpoints, i);
punmarktest3(*parypt);
}
f23count = flip23count - f23count;
f32count = flip32count - f32count;
f44count = flip44count - f44count;
totalfcount = f23count + f32count + f44count;
if (b->verbose > 2) {
printf(" Total %ld flips. f23(%ld), f32(%ld), f44(%ld).\n",
totalfcount, f23count, f32count, f44count);
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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. //
// //
///////////////////////////////////////////////////////////////////////////////
bool tetgenmesh::fillregion(arraypool* missingshs, arraypool* missingshbds,
arraypool* newshs)
{
badface *newflipface, *popface;
triface searchtet, spintet, neightet;
face oldsh, newsh, opensh, *parysh;
face casout, casin, neighsh, checksh;
face neighseg, checkseg;
point pc;
int success;
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
if (i == missingshbds->objects) {
// Failed to find any interior subface.
// Need Steiner points.
return false;
}
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;
}
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);
}
if (checkseg.sh != NULL) {
ssbond(newsh, checkseg);
}
// 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;
}
success = 1;
// 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);
if ((splitsh != NULL) || (splitseg != NULL)) {
// Insert the point into the surface mesh.
sinsertvertex(newpt, splitsh, splitseg, ivf->sloc, ivf->sbowywat, 0);
// Put all new subfaces into stack.
for (i = 0; i < caveshbdlist->objects; i++) {
// Get an old subface at edge [a, b].
parysh = (face *) fastlookup(caveshbdlist, i);
spivot(*parysh, checksh); // The new subface [a, b, p].
// Do not recover a deleted new face (degenerated).
if (checksh.sh[3] != NULL) {
subfacstack->newindex((void **) &parysh);
*parysh = checksh;
}
}
if (splitseg != NULL) {
// Queue two new subsegments in C(p) for recovery.
for (i = 0; i < cavesegshlist->objects; i++) {
paryseg = (face *) fastlookup(cavesegshlist, i);
subsegstack->newindex((void **) &paryseg1);
*paryseg1 = *paryseg;
}
} // if (splitseg != NULL)
// Delete the old subfaces in sC(p).
for (i = 0; i < caveshlist->objects; i++) {
parysh = (face *) fastlookup(caveshlist, i);
if (checksubfaceflag) {
// It is possible that this subface still connects to adjacent
// tets which are not in C(p). If so, clear connections in the
// adjacent tets at this subface.
stpivot(*parysh, neightet);
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);
}
}
}
shellfacedealloc(subfaces, parysh->sh);
}
if (splitseg != NULL) {
// Delete the old segment in sC(p).
shellfacedealloc(subsegs, splitseg->sh);
}
// Clear working lists.
caveshlist->restart();
caveshbdlist->restart();
cavesegshlist->restart();
} // if ((splitsh != NULL) || (splitseg != NULL))
// Put all interior subfaces into stack for recovery.
// They were collected in carvecavity().
// Note: Some collected subfaces may be deleted by sinsertvertex().
for (i = 0; i < caveencshlist->objects; i++) {
parysh = (face *) fastlookup(caveencshlist, i);
if (parysh->sh[3] != NULL) {
subfacstack->newindex((void **) &parysh1);
*parysh1 = *parysh;
}
}
// Put all interior segments into stack for recovery.
// They were collected in carvecavity().
// Note: Some collected segments may be deleted by sinsertvertex().
for (i = 0; i < caveencseglist->objects; i++) {
paryseg = (face *) fastlookup(caveencseglist, i);
if (paryseg->sh[3] != NULL) {
subsegstack->newindex((void **) &paryseg1);
*paryseg1 = *paryseg;
}
}
caveencshlist->restart();
caveencseglist->restart();
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. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::refineregion(face &splitsh, arraypool *cavpoints,
arraypool *cavfaces, arraypool *cavshells,
arraypool *newtets, arraypool *crosstets,
arraypool *misfaces)
{
triface searchtet, spintet;
face splitseg, *paryseg;
point steinpt, pa, pb, refpt;
insertvertexflags ivf;
enum interresult dir;
long baknum = points->items;
int t1ver;
int i;
if (b->verbose > 2) {
printf(" Refining region at edge (%d, %d, %d).\n",
pointmark(sorg(splitsh)), pointmark(sdest(splitsh)),
pointmark(sapex(splitsh)));
}
// Add the Steiner point at the barycenter of the face.
pa = sorg(splitsh);
pb = sdest(splitsh);
// Create a new point.
makepoint(&steinpt, FREEFACETVERTEX);
for (i = 0; i < 3; i++) {
steinpt[i] = 0.5 * (pa[i] + pb[i]);
}
ivf.bowywat = 1; // Use the Bowyer-Watson algorrithm.
ivf.cdtflag = 1; // Only create the initial cavity.
ivf.sloc = (int) ONEDGE;
ivf.sbowywat = 1;
ivf.assignmeshsize = b->metric;
point2tetorg(pa, searchtet); // Start location from it.
ivf.iloc = (int) OUTSIDE;
ivf.rejflag = 1; // Reject it if it encroaches upon any segment.
if (!insertpoint_cdt(steinpt, &searchtet, &splitsh, NULL, &ivf, cavpoints,
cavfaces, cavshells, newtets, crosstets, misfaces)) {
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;
encseglist->restart();
// Split the segment.
pa = sorg(splitseg);
pb = sdest(splitseg);
// Create a new point.
makepoint(&steinpt, FREESEGVERTEX);
for (i = 0; i < 3; i++) {
steinpt[i] = 0.5 * (pa[i] + pb[i]);
}
point2tetorg(pa, searchtet);
ivf.iloc = (int) OUTSIDE;
ivf.rejflag = 0;
if (!insertpoint_cdt(steinpt, &searchtet, &splitsh, &splitseg, &ivf,
cavpoints, cavfaces, cavshells, newtets,
crosstets, misfaces)) {
assert(0);
}
st_segref_count++;
if (steinerleft > 0) steinerleft--;
} else {
assert(0);
}
} else {
st_facref_count++;
if (steinerleft > 0) steinerleft--;
}
while (subsegstack->objects > 0l) {
// seglist is used as a stack.
subsegstack->objects--;
paryseg = (face *) fastlookup(subsegstack, subsegstack->objects);
splitseg = *paryseg;
// Check if this segment has been recovered.
sstpivot1(splitseg, searchtet);
if (searchtet.tet != NULL) continue;
// Search the segment.
dir = scoutsegment(sorg(splitseg), sdest(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);
}
} 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);
}
st_segref_count++;
if (steinerleft > 0) steinerleft--;
} else {
// Maybe a PLC problem.
assert(0);
}
}
} // while
if (b->verbose > 2) {
printf(" Added %ld Steiner points.\n", points->items - baknum);
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// constrainedfacets() Recover constrained facets in a CDT. //
// //
// All unrecovered subfaces are queued in 'subfacestack'. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::constrainedfacets()
{
arraypool *tg_crosstets, *tg_topnewtets, *tg_botnewtets;
arraypool *tg_topfaces, *tg_botfaces, *tg_midfaces;
arraypool *tg_topshells, *tg_botshells, *tg_facfaces;
arraypool *tg_toppoints, *tg_botpoints;
arraypool *tg_missingshs, *tg_missingshbds, *tg_missingshverts;
triface searchtet, neightet, crossedge;
face searchsh, *parysh, *parysh1;
face *paryseg;
point *parypt;
enum interresult dir;
int facetcount;
int success;
int t1ver;
int i, j;
// Initialize arrays.
tg_crosstets = new arraypool(sizeof(triface), 10);
tg_topnewtets = new arraypool(sizeof(triface), 10);
tg_botnewtets = new arraypool(sizeof(triface), 10);
tg_topfaces = new arraypool(sizeof(triface), 10);
tg_botfaces = new arraypool(sizeof(triface), 10);
tg_midfaces = new arraypool(sizeof(triface), 10);
tg_toppoints = new arraypool(sizeof(point), 8);
tg_botpoints = new arraypool(sizeof(point), 8);
tg_facfaces = new arraypool(sizeof(face), 10);
tg_topshells = new arraypool(sizeof(face), 10);
tg_botshells = new arraypool(sizeof(face), 10);
tg_missingshs = new arraypool(sizeof(face), 10);
tg_missingshbds = new arraypool(sizeof(face), 10);
tg_missingshverts = new arraypool(sizeof(point), 8);
// This is a global array used by refineregion().
encseglist = new arraypool(sizeof(face), 4);
facetcount = 0;
while (subfacstack->objects > 0l) {
subfacstack->objects--;
parysh = (face *) fastlookup(subfacstack, subfacstack->objects);
searchsh = *parysh;
if (searchsh.sh[3] == NULL) continue; // It is dead.
if (isshtet(searchsh)) continue; // It is recovered.
// Collect all unrecovered subfaces which are co-facet.
smarktest(searchsh);
tg_facfaces->newindex((void **) &parysh);
*parysh = searchsh;
for (i = 0; i < tg_facfaces->objects; i++) {
parysh = (face *) fastlookup(tg_facfaces, i);
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);
tg_facfaces->newindex((void **) &parysh1);
*parysh1 = searchsh;
}
}
}
senextself(*parysh);
} // j
} // i
// Have found all facet subfaces. Unmark them.
for (i = 0; i < tg_facfaces->objects; i++) {
parysh = (face *) fastlookup(tg_facfaces, i);
sunmarktest(*parysh);
}
if (b->verbose > 2) {
printf(" Recovering facet #%d: %ld subfaces.\n", facetcount + 1,
tg_facfaces->objects);
}
facetcount++;
while (tg_facfaces->objects > 0l) {
tg_facfaces->objects--;
parysh = (face *) fastlookup(tg_facfaces, tg_facfaces->objects);
searchsh = *parysh;
if (searchsh.sh[3] == NULL) continue; // It is dead.
if (isshtet(searchsh)) continue; // It is recovered.
searchtet.tet = NULL;
dir = scoutsubface(&searchsh, &searchtet);
if (dir == SHAREFACE) continue; // The subface is inserted.
// 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)) {
// Save this crossing edge, will be used by fillcavity().
crossedge = searchtet;
// Form a cavity of crossing tets.
success = formcavity(&searchtet, tg_missingshs, tg_crosstets,
tg_topfaces, tg_botfaces, tg_toppoints,
tg_botpoints);
if (success) {
if (!b->flipinsert) {
// Tetrahedralize the top part. Re-use 'tg_midfaces'.
delaunizecavity(tg_toppoints, tg_topfaces, tg_topshells,
tg_topnewtets, tg_crosstets, tg_midfaces);
// Tetrahedralize the bottom part. Re-use 'tg_midfaces'.
delaunizecavity(tg_botpoints, tg_botfaces, tg_botshells,
tg_botnewtets, tg_crosstets, tg_midfaces);
// Fill the cavity with new tets.
success = fillcavity(tg_topshells, tg_botshells, tg_midfaces,
tg_missingshs, tg_topnewtets, tg_botnewtets,
&crossedge);
if (success) {
// Cavity is remeshed. Delete old tets and outer new tets.
carvecavity(tg_crosstets, tg_topnewtets, tg_botnewtets);
} else {
restorecavity(tg_crosstets, tg_topnewtets, tg_botnewtets,
tg_missingshbds);
}
} else {
// 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);
// Clear working lists.
tg_crosstets->restart();
tg_topfaces->restart();
tg_botfaces->restart();
tg_toppoints->restart();
tg_botpoints->restart();
} // b->flipinsert
if (success) {
// Recover interior subfaces.
for (i = 0; i < caveencshlist->objects; i++) {
parysh = (face *) fastlookup(caveencshlist, i);
dir = scoutsubface(parysh, &searchtet);
if (dir != SHAREFACE) {
// Add this face at the end of the list, so it will be
// processed immediately.
tg_facfaces->newindex((void **) &parysh1);
*parysh1 = *parysh;
}
}
caveencshlist->restart();
// 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);
}
}
caveencseglist->restart();
} // success - remesh cavity
} // 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);
}
// Unmarktest all points of the missing region.
for (i = 0; i < tg_missingshverts->objects; i++) {
parypt = (point *) fastlookup(tg_missingshverts, i);
punmarktest(*parypt);
}
tg_missingshverts->restart();
tg_missingshbds->restart();
tg_missingshs->restart();
if (!success) {
// 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)
} // while ((subfacstack->objects)
// Accumulate the dynamic memory.
totalworkmemory += (tg_crosstets->totalmemory + tg_topnewtets->totalmemory +
tg_botnewtets->totalmemory + tg_topfaces->totalmemory +
tg_botfaces->totalmemory + tg_midfaces->totalmemory +
tg_toppoints->totalmemory + tg_botpoints->totalmemory +
tg_facfaces->totalmemory + tg_topshells->totalmemory +
tg_botshells->totalmemory + tg_missingshs->totalmemory +
tg_missingshbds->totalmemory +
tg_missingshverts->totalmemory +
encseglist->totalmemory);
// Delete arrays.
delete tg_crosstets;
delete tg_topnewtets;
delete tg_botnewtets;
delete tg_topfaces;
delete tg_botfaces;
delete tg_midfaces;
delete tg_toppoints;
delete tg_botpoints;
delete tg_facfaces;
delete tg_topshells;
delete tg_botshells;
delete tg_missingshs;
delete tg_missingshbds;
delete tg_missingshverts;
delete encseglist;
}
///////////////////////////////////////////////////////////////////////////////
// //
// constraineddelaunay() Create a constrained Delaunay tetrahedralization.//
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::constraineddelaunay(clock_t& tv)
{
face searchsh, *parysh;
face searchseg, *paryseg;
int s, i;
// Statistics.
long bakfillregioncount;
long bakcavitycount, bakcavityexpcount;
long bakseg_ref_count;
if (!b->quiet) {
printf("Constrained Delaunay...\n");
}
makesegmentendpointsmap();
if (b->verbose) {
printf(" Delaunizing segments.\n");
}
checksubsegflag = 1;
// Put all segments into the list (in random order).
subsegs->traversalinit();
for (i = 0; i < subsegs->items; i++) {
s = randomnation(i + 1);
// Move the s-th seg to the i-th.
subsegstack->newindex((void **) &paryseg);
*paryseg = * (face *) fastlookup(subsegstack, s);
// Put i-th seg to be the s-th.
searchseg.sh = shellfacetraverse(subsegs);
//sinfect(searchseg); // Only save it once.
paryseg = (face *) fastlookup(subsegstack, s);
*paryseg = searchseg;
}
// Recover non-Delaunay segments.
delaunizesegments();
if (b->verbose) {
printf(" Inserted %ld Steiner points.\n", st_segref_count);
}
tv = clock();
if (b->verbose) {
printf(" Constraining facets.\n");
}
// Subfaces will be introduced.
checksubfaceflag = 1;
bakfillregioncount = fillregioncount;
bakcavitycount = cavitycount;
bakcavityexpcount = cavityexpcount;
bakseg_ref_count = st_segref_count;
// Randomly order the subfaces.
subfaces->traversalinit();
for (i = 0; i < subfaces->items; i++) {
s = randomnation(i + 1);
// Move the s-th subface to the i-th.
subfacstack->newindex((void **) &parysh);
*parysh = * (face *) fastlookup(subfacstack, s);
// Put i-th subface to be the s-th.
searchsh.sh = shellfacetraverse(subfaces);
parysh = (face *) fastlookup(subfacstack, s);
*parysh = searchsh;
}
// Recover facets.
constrainedfacets();
if (b->verbose) {
if (fillregioncount > bakfillregioncount) {
printf(" Remeshed %ld regions.\n", fillregioncount-bakfillregioncount);
}
if (cavitycount > bakcavitycount) {
printf(" Remeshed %ld cavities", cavitycount - bakcavitycount);
if (cavityexpcount - bakcavityexpcount) {
printf(" (%ld enlarged)", cavityexpcount - bakcavityexpcount);
}
printf(".\n");
}
if (st_segref_count + st_facref_count - bakseg_ref_count > 0) {
printf(" Inserted %ld (%ld, %ld) refine points.\n",
st_segref_count + st_facref_count - bakseg_ref_count,
st_segref_count - bakseg_ref_count, st_facref_count);
}
}
}
//// ////
//// ////
//// constrained_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'. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::checkflipeligibility(int fliptype, point pa, point pb,
point pc, point pd, point pe,
int level, int edgepivot,
flipconstraints* fc)
{
point tmppts[3];
enum interresult dir;
int types[2], poss[4];
int intflag;
int rejflag = 0;
int i;
if (fc->seg[0] != NULL) {
// A constraining edge is given (e.g., for edge recovery).
if (fliptype == 1) {
// A 2-to-3 flip: [a,b,c] => [e,d,a], [e,d,b], [e,d,c].
tmppts[0] = pa;
tmppts[1] = pb;
tmppts[2] = pc;
for (i = 0; i < 3 && !rejflag; i++) {
if (tmppts[i] != dummypoint) {
// Test if the face [e,d,#] intersects the edge.
intflag = tri_edge_test(pe, pd, tmppts[i], fc->seg[0], fc->seg[1],
NULL, 1, types, poss);
if (intflag == 2) {
// They intersect at a single point.
dir = (enum interresult) types[0];
if (dir == ACROSSFACE) {
// The interior of [e,d,#] intersect the segment.
rejflag = 1;
} else if (dir == ACROSSEDGE) {
if (poss[0] == 0) {
// The interior of [e,d] intersect the segment.
// Since [e,d] is the newly created edge. Reject this flip.
rejflag = 1;
}
}
} else if (intflag == 4) {
// They may intersect at either a point or a line segment.
dir = (enum interresult) types[0];
if (dir == ACROSSEDGE) {
if (poss[0] == 0) {
// The interior of [e,d] intersect the segment.
// Since [e,d] is the newly created edge. Reject this flip.
rejflag = 1;
}
}
}
} // if (tmppts[0] != dummypoint)
} // i
} else if (fliptype == 2) {
// A 3-to-2 flip: [e,d,a], [e,d,b], [e,d,c] => [a,b,c]
if (pc != dummypoint) {
// Check if the new face [a,b,c] intersect the edge in its interior.
intflag = tri_edge_test(pa, pb, pc, fc->seg[0], fc->seg[1], NULL,
1, types, poss);
if (intflag == 2) {
// They intersect at a single point.
dir = (enum interresult) types[0];
if (dir == ACROSSFACE) {
// The interior of [a,b,c] intersect the segment.
rejflag = 1; // Do not flip.
}
} else if (intflag == 4) {
// [a,b,c] is coplanar with the edge.
dir = (enum interresult) types[0];
if (dir == ACROSSEDGE) {
// The boundary of [a,b,c] intersect the segment.
rejflag = 1; // Do not flip.
}
}
} // if (pc != dummypoint)
}
} // if (fc->seg[0] != NULL)
if ((fc->fac[0] != NULL) && !rejflag) {
// A constraining face is given (e.g., for face recovery).
if (fliptype == 1) {
// A 2-to-3 flip.
// Test if the new edge [e,d] intersects the face.
intflag = tri_edge_test(fc->fac[0], fc->fac[1], fc->fac[2], pe, pd,
NULL, 1, types, poss);
if (intflag == 2) {
// They intersect at a single point.
dir = (enum interresult) types[0];
if (dir == ACROSSFACE) {
rejflag = 1;
} else if (dir == ACROSSEDGE) {
rejflag = 1;
}
} else if (intflag == 4) {
// The edge [e,d] is coplanar with the face.
// There may be two intersections.
for (i = 0; i < 2 && !rejflag; i++) {
dir = (enum interresult) types[i];
if (dir == ACROSSFACE) {
rejflag = 1;
} else if (dir == ACROSSEDGE) {
rejflag = 1;
}
}
}
} // if (fliptype == 1)
} // if (fc->fac[0] != NULL)
if ((fc->remvert != NULL) && !rejflag) {
// The vertex is going to be removed. Do not create a new edge which
// contains this vertex.
if (fliptype == 1) {
// A 2-to-3 flip.
if ((pd == fc->remvert) || (pe == fc->remvert)) {
rejflag = 1;
}
}
}
if (fc->remove_large_angle && !rejflag) {
// Remove a large dihedral angle. Do not create a new small angle.
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)) {
// 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 (diff <= 0) { //if (cosmaxd <= fc->cosdihed_in) {
rejflag = 1;
} else {
// Record the largest new angle.
if (cosmaxd < fc->cosdihed_out) {
fc->cosdihed_out = cosmaxd;
}
// 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 (diff <= 0) { //if (cosmaxd <= fc->cosdihed_in) {
rejflag = 1;
} else {
// Record the largest new angle.
if (cosmaxd < fc->cosdihed_out) {
fc->cosdihed_out = cosmaxd;
}
}
}
} // 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)) {
// 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 (diff <= 0) { //if (cosmaxd <= fc->cosdihed_in) {
rejflag = 1;
} else {
// Record the largest new angle.
if (cosmaxd < fc->cosdihed_out) {
fc->cosdihed_out = cosmaxd;
}
// 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) {
rejflag = 1;
} else {
// Record the largest new angle.
if (cosmaxd < fc->cosdihed_out) {
fc->cosdihed_out = cosmaxd;
}
}
}
}
} 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) {
rejflag = 1;
} else {
// Record the largest new angle.
if (cosmaxd < fc->cosdihed_out) {
fc->cosdihed_out = cosmaxd;
}
}
}
} 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 (diff <= 0) { //if (cosmaxd <= fc->cosdihed_in) {
rejflag = 1;
} else {
// Record the largest new angle.
if (cosmaxd < fc->cosdihed_out) {
fc->cosdihed_out = cosmaxd;
}
}
}
} // edgepivot
} // level
}
}
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. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::removeedgebyflips(triface *flipedge, flipconstraints* fc)
{
triface *abtets, spintet;
int t1ver;
int n, nn, i;
if (checksubsegflag) {
// Do not flip a segment.
if (issubseg(*flipedge)) {
if (fc->collectencsegflag) {
face checkseg, *paryseg;
tsspivot1(*flipedge, checkseg);
if (!sinfected(checkseg)) {
// Queue this segment in list.
sinfect(checkseg);
caveencseglist->newindex((void **) &paryseg);
*paryseg = checkseg;
}
}
return 0;
}
}
// Count the number of tets at edge [a,b].
n = 0;
spintet = *flipedge;
while (1) {
n++;
fnextself(spintet);
if (spintet.tet == flipedge->tet) break;
}
assert(n >= 3);
if ((b->flipstarsize > 0) && (n > b->flipstarsize)) {
// The star size exceeds the limit.
return 0; // Do not flip it.
}
// Allocate spaces.
abtets = new triface[n];
// Collect the tets at edge [a,b].
spintet = *flipedge;
i = 0;
while (1) {
abtets[i] = spintet;
setelemcounter(abtets[i], 1);
i++;
fnextself(spintet);
if (spintet.tet == flipedge->tet) break;
}
// Try to flip the edge (level = 0, edgepivot = 0).
nn = flipnm(abtets, n, 0, 0, fc);
if (nn > 2) {
// Edge is not flipped. Unmarktest the remaining tets in Star(ab).
for (i = 0; i < nn; i++) {
setelemcounter(abtets[i], 0);
}
// Restore the input edge (needed by Lawson's flip).
*flipedge = abtets[0];
}
// Release the temporary allocated spaces.
// NOTE: fc->unflip must be 0.
int bakunflip = fc->unflip;
fc->unflip = 0;
flipnm_post(abtets, n, nn, 0, fc);
fc->unflip = bakunflip;
delete [] abtets;
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. //
// //
///////////////////////////////////////////////////////////////////////////////
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;
int reducflag = 0;
fliptets[0] = *flipface;
fsym(*flipface, fliptets[1]);
pa = org(fliptets[0]);
pb = dest(fliptets[0]);
pc = apex(fliptets[0]);
pd = oppo(fliptets[0]);
pe = oppo(fliptets[1]);
ori = orient3d(pa, pb, pd, pe);
if (ori > 0) {
ori = orient3d(pb, pc, pd, pe);
if (ori > 0) {
ori = orient3d(pc, pa, pd, pe);
if (ori > 0) {
// Found a 2-to-3 flip.
reducflag = 1;
} else {
eprev(*flipface, flipedge); // [c,a]
}
} else {
enext(*flipface, flipedge); // [b,c]
}
} else {
flipedge = *flipface; // [a,b]
}
if (reducflag) {
// A 2-to-3 flip is found.
flip23(fliptets, 0, fc);
return 1;
} else {
// Try to flip the selected edge of this face.
if (removeedgebyflips(&flipedge, fc) == 2) {
return 1;
}
}
// Face is not removed.
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)
{
flipconstraints fc;
enum interresult dir;
fc.seg[0] = startpt;
fc.seg[1] = endpt;
fc.checkflipeligibility = 1;
// The mainloop of the edge reocvery.
while (1) { // Loop I
// 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.
}
}
// The edge is missing.
// Try to flip 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 (removefacebyflips(searchtet, &fc)) {
continue;
}
} else if (dir == ACROSSEDGE) {
// An edge is intersected with the segment. Try to flip it.
if (removeedgebyflips(searchtet, &fc) == 2) {
continue;
}
} else {
terminatetetgen(this, 3); // It may be a PLC problem.
}
// The edge is missing.
if (fullsearch) {
// Try to flip one of the faces/edges which intersects the edge.
triface neightet, spintet;
point pa, pb, pc, pd;
badface bakface;
enum interresult dir1;
int types[2], poss[4], pos = 0;
int success = 0;
int t1ver;
int i, j;
// Loop through the sequence of intersecting faces/edges from
// '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.
// This face/edge has been tried in previous step.
while (1) { // Loop I-I
// Find the next intersecting face/edge.
fsymself(*searchtet);
if (dir == ACROSSFACE) {
neightet = *searchtet;
j = (neightet.ver & 3); // j is the current face number.
for (i = j + 1; i < j + 4; i++) {
neightet.ver = (i % 4);
pa = org(neightet);
pb = dest(neightet);
pc = apex(neightet);
pd = oppo(neightet); // The above point.
if (tri_edge_test(pa,pb,pc,startpt,endpt, pd, 1, types, poss)) {
dir = (enum interresult) types[0];
pos = poss[0];
break;
} else {
dir = DISJOINT;
pos = 0;
}
} // i
// There must be an intersection face/edge.
assert(dir != DISJOINT); // SELF_CHECK
} else {
assert(dir == ACROSSEDGE);
while (1) { // Loop I-I-I
// Check the two opposite faces (of the edge) in 'searchtet'.
for (i = 0; i < 2; i++) {
if (i == 0) {
enextesym(*searchtet, neightet);
} else {
eprevesym(*searchtet, neightet);
}
pa = org(neightet);
pb = dest(neightet);
pc = apex(neightet);
pd = oppo(neightet); // The above point.
if (tri_edge_test(pa,pb,pc,startpt,endpt,pd,1, types, poss)) {
dir = (enum interresult) types[0];
pos = poss[0];
break; // for loop
} else {
dir = DISJOINT;
pos = 0;
}
} // i
if (dir != DISJOINT) {
// Find an intersection face/edge.
break; // Loop I-I-I
}
// No intersection. Rotate to the next tet at the edge.
fnextself(*searchtet);
} // while (1) // Loop I-I-I
}
// Adjust to the intersecting edge/vertex.
for (i = 0; i < pos; i++) {
enextself(neightet);
}
if (dir == SHAREVERT) {
// Check if we have reached the 'endpt'.
pd = org(neightet);
if (pd == 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);
}
}
// The next to be flipped face/edge.
*searchtet = neightet;
// Bakup this face (tetrahedron).
bakface.forg = org(*searchtet);
bakface.fdest = dest(*searchtet);
bakface.fapex = apex(*searchtet);
bakface.foppo = oppo(*searchtet);
// Try to flip this intersecting face/edge.
if (dir == ACROSSFACE) {
if (removefacebyflips(searchtet, &fc)) {
success = 1;
break; // Loop I-I
}
} else if (dir == ACROSSEDGE) {
if (removeedgebyflips(searchtet, &fc) == 2) {
success = 1;
break; // Loop I-I
}
} else {
assert(0); // A PLC problem.
}
// The face/edge is not flipped.
if ((searchtet->tet == NULL) ||
(org(*searchtet) != bakface.forg) ||
(dest(*searchtet) != bakface.fdest) ||
(apex(*searchtet) != bakface.fapex) ||
(oppo(*searchtet) != bakface.foppo)) {
// 'searchtet' was flipped. We must restore it.
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.
searchtet->tet = NULL;
break; // Not find.
}
}
}
} else {
searchtet->tet = NULL; // Not find.
}
if (searchtet->tet == NULL) {
success = 0; // This face/edge has been destroyed.
break; // Loop I-I
}
}
} // while (1) // Loop I-I
if (success) {
// One of intersecting faces/edges is flipped.
continue;
}
} // if (fullsearch)
// The edge is missing.
break; // Loop I
} // while (1) // Loop I
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. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::add_steinerpt_in_schoenhardtpoly(triface *abtets, int n,
int chkencflag)
{
triface worktet, *parytet;
triface faketet1, faketet2;
point pc, pd, steinerpt;
insertvertexflags ivf;
optparameters opm;
REAL vcd[3], sampt[3], smtpt[3];
REAL maxminvol = 0.0, minvol = 0.0, ori;
int success, maxidx = 0;
int it, i;
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.
// initialize the list of 2n boundary faces.
for (i = 0; i < n; i++) {
edestoppo(abtets[i], worktet); // [p_i,p_i+1,a]
cavetetlist->newindex((void **) &parytet);
*parytet = worktet;
eorgoppo(abtets[i], worktet); // [p_i+1,p_i,b]
cavetetlist->newindex((void **) &parytet);
*parytet = worktet;
}
int N = 100;
REAL stepi = 0.01;
// Search the point along the edge [c,d].
for (i = 0; i < 3; i++) vcd[i] = pd[i] - pc[i];
// Sample N points in edge [c,d].
for (it = 1; it < N; it++) {
for (i = 0; i < 3; i++) {
sampt[i] = pc[i] + (stepi * (double) it) * vcd[i];
}
for (i = 0; i < cavetetlist->objects; i++) {
parytet = (triface *) fastlookup(cavetetlist, i);
ori = orient3d(dest(*parytet), org(*parytet), apex(*parytet), sampt);
if (i == 0) {
minvol = ori;
} else {
if (minvol > ori) minvol = ori;
}
} // i
if (it == 1) {
maxminvol = minvol;
maxidx = it;
} else {
if (maxminvol < minvol) {
maxminvol = minvol;
maxidx = it;
}
}
} // it
if (maxminvol <= 0) {
cavetetlist->restart();
return 0;
}
for (i = 0; i < 3; i++) {
smtpt[i] = pc[i] + (stepi * (double) maxidx) * vcd[i];
}
// Create two faked tets to hold the two non-existing boundary faces:
// [d,c,a] and [c,d,b].
maketetrahedron(&faketet1);
setvertices(faketet1, pd, pc, org(abtets[0]), dummypoint);
cavetetlist->newindex((void **) &parytet);
*parytet = faketet1;
maketetrahedron(&faketet2);
setvertices(faketet2, pc, pd, dest(abtets[0]), dummypoint);
cavetetlist->newindex((void **) &parytet);
*parytet = faketet2;
// Point smooth options.
opm.max_min_volume = 1;
opm.numofsearchdirs = 20;
opm.searchstep = 0.001;
opm.maxiter = 100; // Limit the maximum iterations.
opm.initval = 0.0; // Initial volume is zero.
// Try to relocate the point into the inside of the polyhedron.
success = smoothpoint(smtpt, cavetetlist, 1, &opm);
if (success) {
while (opm.smthiter == 100) {
// 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);
}
} // if (success)
// Delete the two faked tets.
tetrahedrondealloc(faketet1.tet);
tetrahedrondealloc(faketet2.tet);
cavetetlist->restart();
if (!success) {
return 0;
}
// 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 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);
return 0;
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// add_steinerpt_in_segment() Add a Steiner point inside a segment. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::add_steinerpt_in_segment(face* misseg, int searchlevel)
{
triface searchtet;
face *paryseg, candseg;
point startpt, endpt, pc, pd;
flipconstraints fc;
enum interresult dir;
REAL P[3], Q[3], tp, tq;
REAL len, smlen = 0, split = 0, split_q = 0;
int success;
int i;
startpt = sorg(*misseg);
endpt = sdest(*misseg);
fc.seg[0] = startpt;
fc.seg[1] = endpt;
fc.checkflipeligibility = 1;
fc.collectencsegflag = 1;
point2tetorg(startpt, searchtet);
dir = finddirection(&searchtet, endpt);
//assert(dir != ACROSSVERT);
// Try to flip the first intersecting face/edge.
enextesymself(searchtet); // Go to the opposite face.
int bak_fliplinklevel = b->fliplinklevel;
b->fliplinklevel = 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;
for (i = 0; i < caveencseglist->objects; i++) {
paryseg = (face *) fastlookup(caveencseglist, i);
suninfect(*paryseg);
// Calculate the shortest edge between the two lines.
pc = sorg(*paryseg);
pd = sdest(*paryseg);
tp = tq = 0;
if (linelineint(startpt, endpt, pc, pd, P, Q, &tp, &tq)) {
// Does the shortest edge lie between the two segments?
// Round tp and tq.
if ((tp > 0) && (tq < 1)) {
if (tp < 0.5) {
if (tp < (b->epsilon * 1e+3)) tp = 0.0;
} else {
if ((1.0 - tp) < (b->epsilon * 1e+3)) tp = 1.0;
}
}
if ((tp <= 0) || (tp >= 1)) continue;
if ((tq > 0) && (tq < 1)) {
if (tq < 0.5) {
if (tq < (b->epsilon * 1e+3)) tq = 0.0;
} else {
if ((1.0 - tq) < (b->epsilon * 1e+3)) tq = 1.0;
}
}
if ((tq <= 0) || (tq >= 1)) continue;
// It is a valid shortest edge. Calculate its length.
len = distance(P, Q);
if (split == 0) {
smlen = len;
split = tp;
split_q = tq;
candseg = *paryseg;
} else {
if (len < smlen) {
smlen = len;
split = tp;
split_q = tq;
candseg = *paryseg;
}
}
}
}
caveencseglist->restart();
b->fliplinklevel = bak_fliplinklevel;
if (split == 0) {
// Found no crossing segment.
return 0;
}
face splitsh;
face splitseg;
point steinerpt, *parypt;
insertvertexflags ivf;
if (b->addsteiner_algo == 1) {
// Split the segment at the closest point to a near segment.
makepoint(&steinerpt, FREESEGVERTEX);
for (i = 0; i < 3; i++) {
steinerpt[i] = startpt[i] + split * (endpt[i] - startpt[i]);
}
} else { // b->addsteiner_algo == 2
for (i = 0; i < 3; i++) {
P[i] = startpt[i] + split * (endpt[i] - startpt[i]);
}
pc = sorg(candseg);
pd = sdest(candseg);
for (i = 0; i < 3; i++) {
Q[i] = pc[i] + split_q * (pd[i] - pc[i]);
}
makepoint(&steinerpt, FREEVOLVERTEX);
for (i = 0; i < 3; i++) {
steinerpt[i] = 0.5 * (P[i] + Q[i]);
}
}
// We need to locate the point. Start searching from 'searchtet'.
if (split < 0.5) {
point2tetorg(startpt, searchtet);
} else {
point2tetorg(endpt, searchtet);
}
if (b->addsteiner_algo == 1) {
splitseg = *misseg;
spivot(*misseg, splitsh);
} else {
splitsh.sh = NULL;
splitseg.sh = NULL;
}
ivf.iloc = (int) OUTSIDE;
ivf.bowywat = 1;
ivf.lawson = 0;
ivf.rejflag = 0;
ivf.chkencflag = 0;
ivf.sloc = (int) ONEDGE;
ivf.sbowywat = 1;
ivf.splitbdflag = 0;
ivf.validflag = 1;
ivf.respectbdflag = 1;
ivf.assignmeshsize = b->metric;
if (!insertpoint(steinerpt, &searchtet, &splitsh, &splitseg, &ivf)) {
pointdealloc(steinerpt);
return 0;
}
if (b->addsteiner_algo == 1) {
// Save this Steiner point (for removal).
// Re-use the array 'subvertstack'.
subvertstack->newindex((void **) &parypt);
*parypt = steinerpt;
st_segref_count++;
} else { // b->addsteiner_algo == 2
// Queue the segment for recovery.
subsegstack->newindex((void **) &paryseg);
*paryseg = *misseg;
st_volref_count++;
}
if (steinerleft > 0) steinerleft--;
return 1;
}
///////////////////////////////////////////////////////////////////////////////
// //
// addsteiner4recoversegment() Add a Steiner point for recovering a seg. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::addsteiner4recoversegment(face* misseg, int splitsegflag)
{
triface *abtets, searchtet, spintet;
face splitsh;
face *paryseg;
point startpt, endpt;
point pa, pb, pd, steinerpt, *parypt;
enum interresult dir;
insertvertexflags ivf;
int types[2], poss[4];
int n, endi, success;
int t1ver;
int i;
startpt = sorg(*misseg);
if (pointtype(startpt) == FREESEGVERTEX) {
sesymself(*misseg);
startpt = sorg(*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 == ACROSSFACE) {
// 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);
}
success = 0;
if (dir == ACROSSFACE) {
// Find a Steiner points inside the polyhedron.
if (add_steinerpt_in_schoenhardtpoly(abtets, endi, 0)) {
success = 1;
}
} 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++;
}
}
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 {
// 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.
}
} else {
terminatetetgen(this, 10); // A PLC problem.
}
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 (!splitsegflag) {
return 0;
}
if (b->verbose > 2) {
printf(" Splitting segment (%d, %d)\n", pointmark(startpt),
pointmark(endpt));
}
steinerpt = NULL;
if (b->addsteiner_algo > 0) { // -Y/1 or -Y/2
if (add_steinerpt_in_segment(misseg, 3)) {
return 1;
}
sesymself(*misseg);
if (add_steinerpt_in_segment(misseg, 3)) {
return 1;
}
sesymself(*misseg);
}
if (steinerpt == NULL) {
// Split the segment at its midpoint.
makepoint(&steinerpt, FREESEGVERTEX);
for (i = 0; i < 3; i++) {
steinerpt[i] = 0.5 * (startpt[i] + endpt[i]);
}
// 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.rejflag = 0;
ivf.chkencflag = 0;
ivf.sloc = (int) ONEDGE;
ivf.sbowywat = 1;
ivf.splitbdflag = 0;
ivf.validflag = 1;
ivf.respectbdflag = 1;
ivf.assignmeshsize = b->metric;
if (!insertpoint(steinerpt, &searchtet, &splitsh, misseg, &ivf)) {
assert(0);
}
} // if (endi > 0)
// 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;
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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)
{
triface searchtet, spintet;
face sseg, *paryseg;
point startpt, endpt;
int success;
int t1ver;
long bak_inpoly_count = st_volref_count;
long bak_segref_count = st_segref_count;
if (b->verbose > 1) {
printf(" Recover segments [%s level = %2d] #: %ld.\n",
(b->fliplinklevel > 0) ? "fixed" : "auto",
(b->fliplinklevel > 0) ? b->fliplinklevel : autofliplinklevel,
subsegstack->objects);
}
// Loop until 'subsegstack' is empty.
while (subsegstack->objects > 0l) {
// seglist is used as a stack.
subsegstack->objects--;
paryseg = (face *) fastlookup(subsegstack, subsegstack->objects);
sseg = *paryseg;
// Check if this segment has been recovered.
sstpivot1(sseg, searchtet);
if (searchtet.tet != NULL) {
continue; // Not a missing segment.
}
startpt = sorg(sseg);
endpt = sdest(sseg);
if (b->verbose > 2) {
printf(" Recover segment (%d, %d).\n", pointmark(startpt),
pointmark(endpt));
}
success = 0;
if (recoveredgebyflips(startpt, endpt, &searchtet, 0)) {
success = 1;
} else {
// Try to recover it from the other direction.
if (recoveredgebyflips(endpt, startpt, &searchtet, 0)) {
success = 1;
}
}
if (!success && fullsearch) {
if (recoveredgebyflips(startpt, endpt, &searchtet, fullsearch)) {
success = 1;
}
}
if (success) {
// Segment is recovered. Insert it.
// 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 (steinerflag > 0) {
// Try to recover the segment but do not split it.
if (addsteiner4recoversegment(&sseg, 0)) {
success = 1;
}
if (!success && (steinerflag > 1)) {
// Split the segment.
addsteiner4recoversegment(&sseg, 1);
success = 1;
}
}
if (!success) {
if (misseglist != NULL) {
// Save this segment.
misseglist->newindex((void **) &paryseg);
*paryseg = sseg;
}
}
}
} // while (subsegstack->objects > 0l)
if (steinerflag) {
if (b->verbose > 1) {
// Report the number of added Steiner points.
if (st_volref_count > bak_inpoly_count) {
printf(" Add %ld Steiner points in volume.\n",
st_volref_count - bak_inpoly_count);
}
if (st_segref_count > bak_segref_count) {
printf(" Add %ld Steiner points in segments.\n",
st_segref_count - bak_segref_count);
}
}
}
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. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::recoverfacebyflips(point pa, point pb, point pc,
face *searchsh, triface* searchtet)
{
triface spintet, flipedge;
point pd, pe;
enum interresult dir;
flipconstraints fc;
int types[2], poss[4], intflag;
int success, success1;
int t1ver;
int i, j;
fc.fac[0] = pa;
fc.fac[1] = pb;
fc.fac[2] = pc;
fc.checkflipeligibility = 1;
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]);
// Search the face [a,b,c]
spintet = *searchtet;
while (1) {
if (apex(spintet) == fc.fac[(i+2)%3]) {
// Found the face.
*searchtet = spintet;
// Return the face [a,b,c].
for (j = i; j > 0; j--) {
eprevself(*searchtet);
}
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;
// Find a crossing edge of this face.
spintet = *searchtet;
while (1) {
pd = apex(spintet);
pe = oppo(spintet);
if ((pd != dummypoint) && (pe != dummypoint)) {
// 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.
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) {
// 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;
if (poss[1] == 0) {
touchpt = pd; // pd is a coplanar vertex.
} else {
touchpt = pe; // pe is a coplanar vertex.
}
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.
setpointtype(touchpt, FREEFACETVERTEX);
sinsertvertex(touchpt, searchsh, NULL, siloc, sbowat, 0);
st_volref_count--;
st_facref_count++;
// Queue this vertex for removal.
subvertstack->newindex((void **) &parypt);
*parypt = touchpt;
// Queue new subfaces for recovery.
// Put all new subfaces into stack for recovery.
for (i = 0; i < caveshbdlist->objects; i++) {
// Get an old subface at edge [a, b].
parysh = (face *) fastlookup(caveshbdlist, i);
spivot(*parysh, checksh); // The new subface [a, b, p].
// Do not recover a deleted new face (degenerated).
if (checksh.sh[3] != NULL) {
subfacstack->newindex((void **) &parysh);
*parysh = checksh;
}
}
// 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);
}
// Clear working lists.
caveshlist->restart();
caveshbdlist->restart();
cavesegshlist->restart();
// We can return this function.
searchsh->sh = NULL; // It has been split.
success1 = 0;
success = 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);
}
} else {
assert(0); // Unknown cases. Debug.
}
}
break;
} else { // intflag == 4. Coplanar case.
// This may be an input PLC error.
assert(0);
}
} // if (intflag > 0)
}
fnextself(spintet);
assert(spintet.tet != searchtet->tet);
} // while (1)
if (!success1) break;
} // while (1)
} // i
return success;
}
///////////////////////////////////////////////////////////////////////////////
// //
// recoversubfaces() Recover all subfaces. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::recoversubfaces(arraypool *misshlist, int steinerflag)
{
triface searchtet, neightet, spintet;
face searchsh, neighsh, neineish, *parysh;
face bdsegs[3];
point startpt, endpt, apexpt, *parypt;
point steinerpt;
enum interresult dir;
insertvertexflags ivf;
int success;
int t1ver;
int i, j;
if (b->verbose > 1) {
printf(" Recover subfaces [%s level = %2d] #: %ld.\n",
(b->fliplinklevel > 0) ? "fixed" : "auto",
(b->fliplinklevel > 0) ? b->fliplinklevel : autofliplinklevel,
subfacstack->objects);
}
// Loop until 'subfacstack' is empty.
while (subfacstack->objects > 0l) {
subfacstack->objects--;
parysh = (face *) fastlookup(subfacstack, subfacstack->objects);
searchsh = *parysh;
if (searchsh.sh[3] == NULL) continue; // Skip a dead 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)));
}
// The three edges of the face need to be existed first.
for (i = 0; i < 3; i++) {
sspivot(searchsh, bdsegs[i]);
if (bdsegs[i].sh != NULL) {
// The segment must exist.
sstpivot1(bdsegs[i], searchtet);
if (searchtet.tet == NULL) {
assert(0);
}
} else {
// This edge is not a segment (due to a Steiner point).
// 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);
}
} else {
// The edge is missing. Try to recover it.
if (recoveredgebyflips(startpt, endpt, &searchtet, 0)) {
success = 1;
} else {
if (recoveredgebyflips(endpt, startpt, &searchtet, 0)) {
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);
} 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;
}
}
senextself(searchsh);
} // i
if (i == 3) {
// Recover the subface.
startpt = sorg(searchsh);
endpt = sdest(searchsh);
apexpt = sapex(searchsh);
success = recoverfacebyflips(startpt,endpt,apexpt,&searchsh,&searchtet);
// 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
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));
}
makepoint(&steinerpt, FREEFACETVERTEX);
for (j = 0; j < 3; j++) {
steinerpt[j] = (startpt[j] + endpt[j] + apexpt[j]) / 3.0;
}
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) ONFACE;
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)
}
} else {
success = 0;
}
if (!success) {
if (misshlist != NULL) {
// Save this subface.
misshlist->newindex((void **) &parysh);
*parysh = searchsh;
}
}
} // while (subfacstack->objects > 0l)
return 0;
}
///////////////////////////////////////////////////////////////////////////////
// //
// getvertexstar() Return the star of a vertex. //
// //
// If the flag 'fullstar' is set, return the complete star of this vertex. //
// Otherwise, only a part of the star which is bounded by facets is returned.//
// //
// 'tetlist' returns the list of tets in the star of the vertex 'searchpt'. //
// Every tet in 'tetlist' is at the face opposing to 'searchpt'. //
// //
// 'vertlist' returns the list of vertices in the star (exclude 'searchpt'). //
// //
// 'shlist' returns the list of subfaces in the star. Each subface must face //
// to the interior of this star. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::getvertexstar(int fullstar, point searchpt, arraypool* tetlist,
arraypool* vertlist, arraypool* shlist)
{
triface searchtet, neightet, *parytet;
face checksh, *parysh;
point pt, *parypt;
int collectflag;
int t1ver;
int i, j;
point2tetorg(searchpt, searchtet);
// Go to the opposite face (the link face) of the vertex.
enextesymself(searchtet);
//assert(oppo(searchtet) == searchpt);
infect(searchtet); // Collect this tet (link face).
tetlist->newindex((void **) &parytet);
*parytet = searchtet;
if (vertlist != NULL) {
// Collect three (link) vertices.
j = (searchtet.ver & 3); // The current vertex index.
for (i = 1; i < 4; i++) {
pt = (point) searchtet.tet[4 + ((j + i) % 4)];
pinfect(pt);
vertlist->newindex((void **) &parypt);
*parypt = pt;
}
}
collectflag = 1;
esym(searchtet, neightet);
if (issubface(neightet)) {
if (shlist != NULL) {
tspivot(neightet, checksh);
if (!sinfected(checksh)) {
// Collect this subface (link edge).
sinfected(checksh);
shlist->newindex((void **) &parysh);
*parysh = checksh;
}
}
if (!fullstar) {
collectflag = 0;
}
}
if (collectflag) {
fsymself(neightet); // Goto the adj tet of this face.
esymself(neightet); // Goto the oppo face of this vertex.
// assert(oppo(neightet) == searchpt);
infect(neightet); // Collect this tet (link face).
tetlist->newindex((void **) &parytet);
*parytet = neightet;
if (vertlist != NULL) {
// Collect its apex.
pt = apex(neightet);
pinfect(pt);
vertlist->newindex((void **) &parypt);
*parypt = pt;
}
} // if (collectflag)
// Continue to collect all tets in the star.
for (i = 0; i < tetlist->objects; i++) {
searchtet = * (triface *) fastlookup(tetlist, i);
// Note that 'searchtet' is a face opposite to 'searchpt', and the neighbor
// tet at the current edge is already collected.
// Check the neighbors at the other two edges of this face.
for (j = 0; j < 2; j++) {
collectflag = 1;
enextself(searchtet);
esym(searchtet, neightet);
if (issubface(neightet)) {
if (shlist != NULL) {
tspivot(neightet, checksh);
if (!sinfected(checksh)) {
// Collect this subface (link edge).
sinfected(checksh);
shlist->newindex((void **) &parysh);
*parysh = checksh;
}
}
if (!fullstar) {
collectflag = 0;
}
}
if (collectflag) {
fsymself(neightet);
if (!infected(neightet)) {
esymself(neightet); // Go to the face opposite to 'searchpt'.
infect(neightet);
tetlist->newindex((void **) &parytet);
*parytet = neightet;
if (vertlist != NULL) {
// Check if a vertex is collected.
pt = apex(neightet);
if (!pinfected(pt)) {
pinfect(pt);
vertlist->newindex((void **) &parypt);
*parypt = pt;
}
}
} // if (!infected(neightet))
} // if (collectflag)
} // j
} // i
// Uninfect the list of tets and vertices.
for (i = 0; i < tetlist->objects; i++) {
parytet = (triface *) fastlookup(tetlist, i);
uninfect(*parytet);
}
if (vertlist != NULL) {
for (i = 0; i < vertlist->objects; i++) {
parypt = (point *) fastlookup(vertlist, i);
puninfect(*parypt);
}
}
if (shlist != NULL) {
for (i = 0; i < shlist->objects; i++) {
parysh = (face *) fastlookup(shlist, i);
suninfect(*parysh);
}
}
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. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::getedge(point e1, point e2, triface *tedge)
{
triface searchtet, neightet, *parytet;
point pt;
int done;
int i, j;
if (b->verbose > 2) {
printf(" Get edge from %d to %d.\n", pointmark(e1), pointmark(e2));
}
// Quickly check if 'tedge' is just this edge.
if (!isdeadtet(*tedge)) {
if (org(*tedge) == e1) {
if (dest(*tedge) == e2) {
return 1;
}
} else if (org(*tedge) == e2) {
if (dest(*tedge) == e1) {
esymself(*tedge);
return 1;
}
}
}
// Search for the edge [e1, e2].
point2tetorg(e1, *tedge);
finddirection(tedge, e2);
if (dest(*tedge) == e2) {
return 1;
} else {
// Search for the edge [e2, e1].
point2tetorg(e2, *tedge);
finddirection(tedge, e1);
if (dest(*tedge) == e1) {
esymself(*tedge);
return 1;
}
}
// 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.
for (i = 0; i < 3; i++) {
pt = apex(searchtet);
if (pt == e2) {
// Found. 'searchtet' is [#,#,e2,e1].
eorgoppo(searchtet, *tedge); // [e1,e2,#,#].
return 1;
}
enextself(searchtet);
}
// Get the adjacent link face at 'searchtet'.
fnext(searchtet, neightet);
esymself(neightet);
// assert(oppo(neightet) == e1);
pt = apex(neightet);
if (pt == e2) {
// Found. 'neightet' is [#,#,e2,e1].
eorgoppo(neightet, *tedge); // [e1,e2,#,#].
return 1;
}
// Continue searching in the link face of e1.
infect(searchtet);
tetlist->newindex((void **) &parytet);
*parytet = searchtet;
infect(neightet);
tetlist->newindex((void **) &parytet);
*parytet = neightet;
done = 0;
for (i = 0; (i < tetlist->objects) && !done; i++) {
parytet = (triface *) fastlookup(tetlist, i);
searchtet = *parytet;
for (j = 0; (j < 2) && !done; j++) {
enextself(searchtet);
fnext(searchtet, neightet);
if (!infected(neightet)) {
esymself(neightet);
pt = apex(neightet);
if (pt == e2) {
// Found. 'neightet' is [#,#,e2,e1].
eorgoppo(neightet, *tedge);
done = 1;
} else {
infect(neightet);
tetlist->newindex((void **) &parytet);
*parytet = neightet;
}
}
} // j
} // i
// Uninfect the list of visited tets.
for (i = 0; i < tetlist->objects; i++) {
parytet = (triface *) fastlookup(tetlist, i);
uninfect(*parytet);
}
tetlist->restart();
return done;
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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)
{
triface searchtet;
point *pendpt, *parypt;
enum interresult dir;
flipconstraints fc;
int reduceflag;
int count;
int n, i, j;
fc.remvert = startpt;
fc.checkflipeligibility = 1;
while (1) {
count = 0;
for (i = 0; i < endptlist->objects; i++) {
pendpt = (point *) fastlookup(endptlist, i);
if (*pendpt == dummypoint) {
continue; // Do not reduce a virtual edge.
}
reduceflag = 0;
// Find the edge.
if (nonconvex) {
if (getedge(startpt, *pendpt, &searchtet)) {
dir = ACROSSVERT;
} else {
// The edge does not exist (was flipped).
dir = INTERSECT;
}
} else {
point2tetorg(startpt, searchtet);
dir = finddirection(&searchtet, *pendpt);
}
if (dir == ACROSSVERT) {
if (dest(searchtet) == *pendpt) {
// Do not flip a segment.
if (!issubseg(searchtet)) {
n = removeedgebyflips(&searchtet, &fc);
if (n == 2) {
reduceflag = 1;
}
}
} else {
assert(0); // A plc problem.
}
} else {
// The edge has been flipped.
reduceflag = 1;
}
if (reduceflag) {
count++;
// Move the last vertex into this slot.
j = endptlist->objects - 1;
parypt = (point *) fastlookup(endptlist, j);
*pendpt = *parypt;
endptlist->objects--;
i--;
}
} // i
if (count == 0) {
// No edge is reduced.
break;
}
} // while (1)
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. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::removevertexbyflips(point steinerpt)
{
triface *fliptets = NULL, wrktets[4];
triface searchtet, spintet, neightet;
face parentsh, spinsh, checksh;
face leftseg, rightseg, checkseg;
point lpt = NULL, rpt = NULL, apexpt; //, *parypt;
flipconstraints fc;
enum verttype vt;
enum locateresult loc;
int valence, removeflag;
int slawson;
int t1ver;
int n, i;
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);
if (b->verbose > 2) {
printf(" Removing Steiner point %d in segment (%d, %d).\n",
pointmark(steinerpt), pointmark(lpt), pointmark(rpt));
}
} else if (vt == FREEFACETVERTEX) {
if (b->verbose > 2) {
printf(" Removing Steiner point %d in facet.\n",
pointmark(steinerpt));
}
} else if (vt == FREEVOLVERTEX) {
if (b->verbose > 2) {
printf(" Removing Steiner point %d in volume.\n",
pointmark(steinerpt));
}
} else if (vt == VOLVERTEX) {
if (b->verbose > 2) {
printf(" Removing a point %d in volume.\n",
pointmark(steinerpt));
}
} else {
// It is not a Steiner point.
return 0;
}
// Try to reduce the number of edges at 'p' by flips.
getvertexstar(1, steinerpt, cavetetlist, cavetetvertlist, NULL);
cavetetlist->restart(); // This list may be re-used.
if (cavetetvertlist->objects > 3l) {
valence = reduceedgesatvertex(steinerpt, cavetetvertlist);
} else {
valence = cavetetvertlist->objects;
}
assert(cavetetlist->objects == 0l);
cavetetvertlist->restart();
removeflag = 0;
if (valence == 4) {
// Only 4 vertices (4 tets) left! 'p' is inside the convex hull of the 4
// vertices. This case is due to that 'p' is not exactly on the segment.
point2tetorg(steinerpt, searchtet);
loc = INTETRAHEDRON;
removeflag = 1;
} else if (valence == 5) {
// There are 5 edges.
if (vt == FREESEGVERTEX) {
sstpivot1(leftseg, searchtet);
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;
while (1) {
i++;
if (apex(spintet) == rpt) {
// Remember the face containing the edge [lpt, rpt].
neightet = spintet;
}
fnextself(spintet);
if (spintet.tet == searchtet.tet) break;
}
if (i == 3) {
// This case has been checked below.
} else if (i == 4) {
// There are 4 tets sharing at [p,lpt]. There must be 4 tets sharing
// at [p,rpt]. There must be a face [p, lpt, rpt].
if (apex(neightet) == rpt) {
// The edge (segment) has been already recovered!
// Check if a 6-to-2 flip is possible (to remove 'p').
// Let 'searchtet' be [p,d,a,b]
esym(neightet, searchtet);
enextself(searchtet);
// Check if there are exactly three tets at edge [p,d].
wrktets[0] = searchtet; // [p,d,a,b]
for (i = 0; i < 2; i++) {
fnext(wrktets[i], wrktets[i+1]); // [p,d,b,c], [p,d,c,a]
}
if (apex(wrktets[0]) == oppo(wrktets[2])) {
loc = ONFACE;
removeflag = 1;
}
}
}
} else if (vt == FREEFACETVERTEX) {
// It is possible to do a 6-to-2 flip to remove the vertex.
point2tetorg(steinerpt, searchtet);
// Get the three faces of 'searchtet' which share at p.
// All faces has p as origin.
wrktets[0] = searchtet;
wrktets[1] = searchtet;
esymself(wrktets[1]);
enextself(wrktets[1]);
wrktets[2] = searchtet;
eprevself(wrktets[2]);
esymself(wrktets[2]);
// All internal edges of the six tets have valance either 3 or 4.
// Get one edge which has valance 3.
searchtet.tet = NULL;
for (i = 0; i < 3; i++) {
spintet = wrktets[i];
valence = 0;
while (1) {
valence++;
fnextself(spintet);
if (spintet.tet == wrktets[i].tet) break;
}
if (valence == 3) {
// 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;
}
if (!removeflag) {
if (vt == FREESEGVERTEX) {
// Check is it possible to recover the edge [lpt,rpt].
// The condition to check is: Whether each tet containing 'leftseg' is
// adjacent to a tet containing 'rightseg'.
sstpivot1(leftseg, searchtet);
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.
eprev(spintet, neightet);
esymself(neightet); // [steinerpt, p1, p2, lpt]
// Get the adjacent tet.
fsymself(neightet); // [p1, steinerpt, p2, rpt]
if (oppo(neightet) != rpt) {
// Found a non-matching adjacent tet.
break;
}
fnextself(spintet);
if (spintet.tet == searchtet.tet) {
// 'searchtet' is [p,d,p1,p2].
loc = ONEDGE;
removeflag = 1;
break;
}
}
} // if (vt == FREESEGVERTEX)
}
if (!removeflag) {
if (vt == FREESEGVERTEX) {
// Check if the edge [lpt, rpt] exists.
if (getedge(lpt, rpt, &searchtet)) {
// We have recovered this edge. Shift the vertex into the volume.
// We can recover this edge if the subfaces are not recovered yet.
if (!checksubfaceflag) {
// Remove the vertex from the surface mesh.
// This will re-create the segment [lpt, rpt] and re-triangulate
// all the facets at the segment.
// Detach the subsegments from their surrounding tets.
for (i = 0; i < 2; i++) {
checkseg = (i == 0) ? leftseg : rightseg;
sstpivot1(checkseg, neightet);
spintet = neightet;
while (1) {
tssdissolve1(spintet);
fnextself(spintet);
if (spintet.tet == neightet.tet) break;
}
sstdissolve1(checkseg);
} // i
slawson = 1; // Do lawson flip after removal.
spivot(rightseg, parentsh); // 'rightseg' has p as its origin.
sremovevertex(steinerpt, &parentsh, &rightseg, slawson);
// 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;
}
// The Steiner point has been shifted into the volume.
setpointtype(steinerpt, FREEVOLVERTEX);
st_segref_count--;
st_volref_count++;
return 1;
} // if (!checksubfaceflag)
} // if (getedge(...))
} // if (vt == FREESEGVERTEX)
} // if (!removeflag)
if (!removeflag) {
return 0;
}
assert(org(searchtet) == steinerpt);
if (vt == FREESEGVERTEX) {
// Detach the subsegments from their surronding tets.
for (i = 0; i < 2; i++) {
checkseg = (i == 0) ? leftseg : rightseg;
sstpivot1(checkseg, neightet);
spintet = neightet;
while (1) {
tssdissolve1(spintet);
fnextself(spintet);
if (spintet.tet == neightet.tet) break;
}
sstdissolve1(checkseg);
} // i
if (checksubfaceflag) {
// Detach the subfaces at the subsegments from their attached tets.
for (i = 0; i < 2; i++) {
checkseg = (i == 0) ? leftseg : rightseg;
spivot(checkseg, parentsh);
if (parentsh.sh != NULL) {
spinsh = parentsh;
while (1) {
stpivot(spinsh, neightet);
if (neightet.tet != NULL) {
tsdissolve(neightet);
}
sesymself(spinsh);
stpivot(spinsh, neightet);
if (neightet.tet != NULL) {
tsdissolve(neightet);
}
stdissolve(spinsh);
spivotself(spinsh); // Go to the next subface.
if (spinsh.sh == parentsh.sh) break;
}
}
} // i
} // if (checksubfaceflag)
}
if (loc == INTETRAHEDRON) {
// Collect the four tets containing 'p'.
fliptets = new triface[4];
fliptets[0] = searchtet; // [p,d,a,b]
for (i = 0; i < 2; i++) {
fnext(fliptets[i], fliptets[i+1]); // [p,d,b,c], [p,d,c,a]
}
eprev(fliptets[0], fliptets[3]);
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);
flip41(fliptets, 1, &fc);
//recenttet = fliptets[0];
} else if (loc == ONFACE) {
// Let the original two tets be [a,b,c,d] and [b,a,c,e]. And p is in
// face [a,b,c]. Let 'searchtet' be the tet [p,d,a,b].
// Collect the six tets containing 'p'.
fliptets = new triface[6];
fliptets[0] = searchtet; // [p,d,a,b]
for (i = 0; i < 2; i++) {
fnext(fliptets[i], fliptets[i+1]); // [p,d,b,c], [p,d,c,a]
}
eprev(fliptets[0], fliptets[3]);
fnextself(fliptets[3]); // [a,p,b,e]
esymself(fliptets[3]); // [p,a,e,b]
eprevself(fliptets[3]); // [e,p,a,b]
for (i = 3; i < 5; i++) {
fnext(fliptets[i], fliptets[i+1]); // [e,p,b,c], [e,p,c,a]
}
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++) {
esym(fliptets[i+3], wrktets[i]);
esym(fliptets[i], fliptets[i+3]);
fliptets[i] = wrktets[i];
}
// Swap the last two pairs, i.e., [1]<->[[2], and [4]<->[5]
wrktets[1] = fliptets[1];
fliptets[1] = fliptets[2];
fliptets[2] = wrktets[1];
wrktets[1] = fliptets[4];
fliptets[4] = fliptets[5];
fliptets[5] = wrktets[1];
}
}
// 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).
// First do a 3-to-2 flip on [e,p,a,b],[e,p,b,c],[e,p,c,a]. It creates
// two new tets: [a,b,c,p] and [b,a,c,e]. The new tet [a,b,c,p] is
// degenerate (has zero volume). It will be deleted in the followed
// 4-to-1 flip.
//flip32(&(fliptets[3]), 1, 0, 0);
flip32(&(fliptets[3]), 1, &fc);
// Second do a 4-to-1 flip on [p,d,a,b],[p,d,b,c],[p,d,c,a],[a,b,c,p].
// This creates a new tet [a,b,c,d].
//flip41(fliptets, 1, 0, 0);
flip41(fliptets, 1, &fc);
//recenttet = fliptets[0];
} else if (loc == ONEDGE) {
// Let the original edge be [e,d] and p is in [e,d]. Assume there are n
// tets sharing at edge [e,d] originally. We number the link vertices
// of [e,d]: p_0, p_1, ..., p_n-1. 'searchtet' is [p,d,p_0,p_1].
// Count the number of tets at edge [e,p] and [p,d] (this is n).
n = 0;
spintet = searchtet;
while (1) {
n++;
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]
for (i = 0; i < (n - 1); i++) {
fnext(fliptets[i], fliptets[i+1]); // [p,d,p_i,p_i+1].
}
eprev(fliptets[0], fliptets[n]);
fnextself(fliptets[n]); // [p_0,p,p_1,e]
esymself(fliptets[n]); // [p,p_0,e,p_1]
eprevself(fliptets[n]); // [e,p,p_0,p_1]
for (i = n; i < (2 * n - 1); i++) {
fnext(fliptets[i], fliptets[i+1]); // [e,p,p_i,p_i+1].
}
// Remove p by a 2n-to-n flip, it is a sequence of n flips:
// - Do a 2-to-3 flip on
// [p_0,p_1,p,d] and
// [p,p_1,p_0,e].
// This produces:
// [e,d,p_0,p_1],
// [e,d,p_1,p] (degenerated), and
// [e,d,p,p_0] (degenerated).
wrktets[0] = fliptets[0]; // [p,d,p_0,p_1]
eprevself(wrktets[0]); // [p_0,p,d,p_1]
esymself(wrktets[0]); // [p,p_0,p_1,d]
enextself(wrktets[0]); // [p_0,p_1,p,d] [0]
wrktets[1] = fliptets[n]; // [e,p,p_0,p_1]
enextself(wrktets[1]); // [p,p_0,e,p_1]
esymself(wrktets[1]); // [p_0,p,p_1,e]
eprevself(wrktets[1]); // [p_1,p_0,p,e] [1]
//flip23(wrktets, 1, 0, 0);
flip23(wrktets, 1, &fc);
// Save the new tet [e,d,p,p_0] (degenerated).
fliptets[n] = wrktets[2];
// Save the new tet [e,d,p_0,p_1].
fliptets[0] = wrktets[0];
// - Repeat from i = 1 to n-2: (n - 2) flips
// - Do a 3-to-2 flip on
// [p,p_i,d,e],
// [p,p_i,e,p_i+1], and
// [p,p_i,p_i+1,d].
// This produces:
// [d,e,p_i+1,p_i], and
// [e,d,p_i+1,p] (degenerated).
for (i = 1; i < (n - 1); i++) {
wrktets[0] = wrktets[1]; // [e,d,p_i,p] (degenerated).
enextself(wrktets[0]); // [d,p_i,e,p] (...)
esymself(wrktets[0]); // [p_i,d,p,e] (...)
eprevself(wrktets[0]); // [p,p_i,d,e] (degenerated) [0].
wrktets[1] = fliptets[n+i]; // [e,p,p_i,p_i+1]
enextself(wrktets[1]); // [p,p_i,e,p_i+1] [1]
wrktets[2] = fliptets[i]; // [p,d,p_i,p_i+1]
eprevself(wrktets[2]); // [p_i,p,d,p_i+1]
esymself(wrktets[2]); // [p,p_i,p_i+1,d] [2]
//flip32(wrktets, 1, 0, 0);
flip32(wrktets, 1, &fc);
// Save the new tet [e,d,p_i,p_i+1]. // FOR DEBUG ONLY
fliptets[i] = wrktets[0]; // [d,e,p_i+1,p_i] // FOR DEBUG ONLY
esymself(fliptets[i]); // [e,d,p_i,p_i+1] // FOR DEBUG ONLY
}
// - Do a 4-to-1 flip on
// [p,p_0,e,d], [d,e,p_0,p],
// [p,p_0,d,p_n-1], [e,p_n-1,p_0,p],
// [p,p_0,p_n-1,e], [p_0,p_n-1,d,p], and
// [e,d,p_n-1,p].
// This produces
// [e,d,p_n-1,p_0] and
// deletes p.
wrktets[3] = wrktets[1]; // [e,d,p_n-1,p] (degenerated) [3]
wrktets[0] = fliptets[n]; // [e,d,p,p_0] (degenerated)
eprevself(wrktets[0]); // [p,e,d,p_0] (...)
esymself(wrktets[0]); // [e,p,p_0,d] (...)
enextself(wrktets[0]); // [p,p_0,e,d] (degenerated) [0]
wrktets[1] = fliptets[n-1]; // [p,d,p_n-1,p_0]
esymself(wrktets[1]); // [d,p,p_0,p_n-1]
enextself(wrktets[1]); // [p,p_0,d,p_n-1] [1]
wrktets[2] = fliptets[2*n-1]; // [e,p,p_n-1,p_0]
enextself(wrktets[2]); // [p_p_n-1,e,p_0]
esymself(wrktets[2]); // [p_n-1,p,p_0,e]
enextself(wrktets[2]); // [p,p_0,p_n-1,e] [2]
//flip41(wrktets, 1, 0, 0);
flip41(wrktets, 1, &fc);
// 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;
if (vt == FREESEGVERTEX) {
// Remove the vertex from the surface mesh.
// This will re-create the segment [lpt, rpt] and re-triangulate
// all the facets at the segment.
// Only do lawson flip when subfaces are not recovery yet.
slawson = (checksubfaceflag ? 0 : 1);
spivot(rightseg, parentsh); // 'rightseg' has p as its origin.
sremovevertex(steinerpt, &parentsh, &rightseg, slawson);
// 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);
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;
}
if (checksubfaceflag) {
// Insert subfaces at segment [lpt,rpt] into the tetrahedralization.
spivot(rightseg, parentsh);
if (parentsh.sh != NULL) {
spinsh = parentsh;
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;
while (1) {
if (apex(spintet) == apexpt) {
tsbond(spintet, spinsh);
sesymself(spinsh); // Get to another side of this face.
fsym(spintet, neightet);
tsbond(neightet, spinsh);
sesymself(spinsh); // Get back to the original side.
break;
}
fnextself(spintet);
assert(spintet.tet != searchtet.tet);
//if (spintet.tet == searchtet.tet) break;
}
spivotself(spinsh);
if (spinsh.sh == parentsh.sh) break;
}
}
} // if (checksubfaceflag)
// Clear the set of new subfaces.
caveshbdlist->restart();
} // if (vt == FREESEGVERTEX)
// The point has been removed.
if (pointtype(steinerpt) != 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++;
}
return 1;
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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);
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;
}
triface searchtet, neightet, *parytet;
point pa, pb, pc, pd;
REAL v1[3], v2[3], len, u;
REAL startpt[3] = {0,}, samplept[3] = {0,}, candpt[3] = {0,};
REAL ori, minvol, smallvol;
int samplesize;
int it, j, k;
int n = (int) cavesegshlist->objects;
point *newsteiners = new point[n];
for (i = 0; i < n; i++) newsteiners[i] = NULL;
// Search for each sector an interior vertex.
for (i = 0; i < cavesegshlist->objects; i++) {
parysh = (face *) fastlookup(cavesegshlist, i);
stpivot(*parysh, searchtet);
// Skip it if it is outside.
if (ishulltet(searchtet)) continue;
// Get the "half-ball". Tets in 'cavetetlist' all contain 'steinerpt' as
// opposite. Subfaces in 'caveshlist' all contain 'steinerpt' as apex.
// Moreover, subfaces are oriented towards the interior of the ball.
setpoint2tet(steinerpt, encode(searchtet));
getvertexstar(0, steinerpt, cavetetlist, NULL, caveshlist);
// Calculate the searching vector.
pa = sorg(*parysh);
pb = sdest(*parysh);
pc = sapex(*parysh);
facenormal(pa, pb, pc, v1, 1, NULL);
len = sqrt(dot(v1, v1));
assert(len > 0.0);
v1[0] /= len;
v1[1] /= len;
v1[2] /= len;
if (vt == FREESEGVERTEX) {
parysh = (face *) fastlookup(cavesegshlist, (i + 1) % n);
pd = sapex(*parysh);
facenormal(pb, pa, pd, v2, 1, NULL);
len = sqrt(dot(v2, v2));
assert(len > 0.0);
v2[0] /= len;
v2[1] /= len;
v2[2] /= len;
// Average the two vectors.
v1[0] = 0.5 * (v1[0] + v2[0]);
v1[1] = 0.5 * (v1[1] + v2[1]);
v1[2] = 0.5 * (v1[2] + v2[2]);
}
// Search the intersection of the ray starting from 'steinerpt' to
// the search direction 'v1' and the shell of the half-ball.
// - Construct an endpoint.
len = distance(pa, pb);
v2[0] = steinerpt[0] + len * v1[0];
v2[1] = steinerpt[1] + len * v1[1];
v2[2] = steinerpt[2] + len * v1[2];
for (j = 0; j < cavetetlist->objects; j++) {
parytet = (triface *) fastlookup(cavetetlist, j);
pa = org(*parytet);
pb = dest(*parytet);
pc = apex(*parytet);
// Test if the ray startpt->v2 lies in the cone: where 'steinerpt'
// is the apex, and three sides are defined by the triangle
// [pa, pb, pc].
ori = orient3d(steinerpt, pa, pb, v2);
if (ori >= 0) {
ori = orient3d(steinerpt, pb, pc, v2);
if (ori >= 0) {
ori = orient3d(steinerpt, pc, pa, v2);
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.
// Close the ball by adding the subfaces.
for (j = 0; j < caveshlist->objects; j++) {
parysh = (face *) fastlookup(caveshlist, j);
stpivot(*parysh, neightet);
cavetetlist->newindex((void **) &parytet);
*parytet = neightet;
}
// Search a best point inside the segment [startpt, steinerpt].
it = 0;
samplesize = 100;
v1[0] = steinerpt[0] - startpt[0];
v1[1] = steinerpt[1] - startpt[1];
v1[2] = steinerpt[2] - startpt[2];
minvol = -1.0;
while (it < 3) {
for (j = 1; j < samplesize - 1; j++) {
samplept[0] = startpt[0] + ((REAL) j / (REAL) samplesize) * v1[0];
samplept[1] = startpt[1] + ((REAL) j / (REAL) samplesize) * v1[1];
samplept[2] = startpt[2] + ((REAL) j / (REAL) samplesize) * v1[2];
// Find the minimum volume for 'samplept'.
smallvol = -1;
for (k = 0; k < cavetetlist->objects; k++) {
parytet = (triface *) fastlookup(cavetetlist, k);
pa = org(*parytet);
pb = dest(*parytet);
pc = apex(*parytet);
ori = orient3d(pb, pa, pc, samplept);
if (ori <= 0) {
break; // An invalid tet.
}
if (smallvol == -1) {
smallvol = ori;
} else {
if (ori < smallvol) smallvol = ori;
}
} // k
if (k == cavetetlist->objects) {
// Found a valid point. Remember it.
if (minvol == -1.0) {
candpt[0] = samplept[0];
candpt[1] = samplept[1];
candpt[2] = samplept[2];
minvol = smallvol;
} else {
if (minvol < smallvol) {
// It is a better location. Remember it.
candpt[0] = samplept[0];
candpt[1] = samplept[1];
candpt[2] = samplept[2];
minvol = smallvol;
} else {
// No improvement of smallest volume.
// Since we are searching along the line [startpt, steinerpy],
// The smallest volume can only be decreased later.
break;
}
}
}
} // j
if (minvol > 0) break;
samplesize *= 10;
it++;
} // while (it < 3)
if (minvol == -1.0) {
// Failed to find a valid point.
cavetetlist->restart();
caveshlist->restart();
break;
}
// Create a new Steiner point inside this section.
makepoint(&(newsteiners[i]), FREEVOLVERTEX);
newsteiners[i][0] = candpt[0];
newsteiners[i][1] = candpt[1];
newsteiners[i][2] = candpt[2];
cavetetlist->restart();
caveshlist->restart();
} // i
if (i < cavesegshlist->objects) {
// Failed to suppress the vertex.
for (; i > 0; i--) {
if (newsteiners[i - 1] != NULL) {
pointdealloc(newsteiners[i - 1]);
}
}
delete [] newsteiners;
cavesegshlist->restart();
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;
}
}
// 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);
// 'parysh' is the face [lpt, steinerpt, #].
stpivot(*parysh, neightet);
// Get all tets in this sector.
setpoint2tet(steinerpt, encode(neightet));
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);
}
// 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);
}
assert(sorg(parentsh) == steinerpt);
splitseg = NULL;
}
sremovevertex(steinerpt, &parentsh, splitseg, slawson);
if (vt == FREESEGVERTEX) {
// The original segment is returned in 'rightseg'.
rightseg.shver = 0;
}
// For each new subface, create two new tets at each side of it.
// Both of the two new tets have its opposite be dummypoint.
for (i = 0; i < caveshbdlist->objects; i++) {
parysh = (face *) fastlookup(caveshbdlist, i);
sinfect(*parysh); // Mark it for connecting new tets.
newsh = *parysh;
pa = sorg(newsh);
pb = sdest(newsh);
pc = sapex(newsh);
maketetrahedron(&newtet);
maketetrahedron(&neightet);
setvertices(newtet, pa, pb, pc, dummypoint);
setvertices(neightet, pb, pa, pc, dummypoint);
bond(newtet, neightet);
tsbond(newtet, newsh);
sesymself(newsh);
tsbond(neightet, newsh);
}
// Temporarily increase the hullsize.
hullsize += (caveshbdlist->objects * 2l);
if (vt == FREESEGVERTEX) {
// Connecting new tets at the recovered segment.
spivot(rightseg, parentsh);
assert(parentsh.sh != NULL);
spinsh = parentsh;
while (1) {
if (sorg(spinsh) != lpt) sesymself(spinsh);
// Get the new tet at this subface.
stpivot(spinsh, newtet);
tssbond1(newtet, rightseg);
// Go to the other face at this segment.
spivot(spinsh, neighsh);
if (sorg(neighsh) != lpt) sesymself(neighsh);
sesymself(neighsh);
stpivot(neighsh, neightet);
tssbond1(neightet, rightseg);
sstbond1(rightseg, neightet);
// Connecting two adjacent tets at this segment.
esymself(newtet);
esymself(neightet);
// Connect the two tets (at rightseg) together.
bond(newtet, neightet);
// Go to the next subface.
spivotself(spinsh);
if (spinsh.sh == parentsh.sh) break;
}
}
// Connecting new tets at new subfaces together.
for (i = 0; i < caveshbdlist->objects; i++) {
parysh = (face *) fastlookup(caveshbdlist, i);
newsh = *parysh;
//assert(sinfected(newsh));
// Each new subface contains two new tets.
for (k = 0; k < 2; k++) {
stpivot(newsh, newtet);
for (j = 0; j < 3; j++) {
// Check if this side is open.
esym(newtet, newface);
if (newface.tet[newface.ver & 3] == NULL) {
// An open face. Connect it to its adjacent tet.
sspivot(newsh, checkseg);
if (checkseg.sh != NULL) {
// A segment. It must not be the recovered segment.
tssbond1(newtet, checkseg);
sstbond1(checkseg, newtet);
}
spivot(newsh, neighsh);
if (neighsh.sh != NULL) {
// The adjacent subface exists. It's not a dangling segment.
if (sorg(neighsh) != sdest(newsh)) sesymself(neighsh);
stpivot(neighsh, neightet);
if (sinfected(neighsh)) {
esymself(neightet);
assert(neightet.tet[neightet.ver & 3] == NULL);
} else {
// Search for an open face at this edge.
spintet = neightet;
while (1) {
esym(spintet, searchtet);
fsym(searchtet, spintet);
if (spintet.tet == NULL) break;
assert(spintet.tet != neightet.tet);
}
// Found an open face at 'searchtet'.
neightet = searchtet;
}
} else {
// The edge (at 'newsh') is a dangling segment.
assert(checkseg.sh != NULL);
// Get an adjacent tet at this segment.
sstpivot1(checkseg, neightet);
assert(!isdeadtet(neightet));
if (org(neightet) != sdest(newsh)) esymself(neightet);
assert((org(neightet) == sdest(newsh)) &&
(dest(neightet) == sorg(newsh)));
// Search for an open face at this edge.
spintet = neightet;
while (1) {
esym(spintet, searchtet);
fsym(searchtet, spintet);
if (spintet.tet == NULL) break;
assert(spintet.tet != neightet.tet);
}
// Found an open face at 'searchtet'.
neightet = searchtet;
}
pc = apex(newface);
if (apex(neightet) == steinerpt) {
// Exterior case. The 'neightet' is a hull tet which contain
// 'steinerpt'. It will be deleted after 'steinerpt' is removed.
assert(pc == dummypoint);
caveoldtetlist->newindex((void **) &parytet);
*parytet = neightet;
// Connect newface to the adjacent hull tet of 'neightet', which
// has the same edge as 'newface', and does not has 'steinerpt'.
fnextself(neightet);
} else {
if (pc == dummypoint) {
if (apex(neightet) != dummypoint) {
setapex(newface, apex(neightet));
// A hull tet has turned into an interior tet.
hullsize--; // Must update the hullsize.
}
}
}
bond(newface, neightet);
} // if (newface.tet[newface.ver & 3] == NULL)
enextself(newtet);
senextself(newsh);
} // j
sesymself(newsh);
} // k
} // i
// Unmark all new subfaces.
for (i = 0; i < caveshbdlist->objects; i++) {
parysh = (face *) fastlookup(caveshbdlist, i);
suninfect(*parysh);
}
caveshbdlist->restart();
if (caveoldtetlist->objects > 0l) {
// Delete hull tets which contain 'steinerpt'.
for (i = 0; i < caveoldtetlist->objects; i++) {
parytet = (triface *) fastlookup(caveoldtetlist, i);
tetrahedrondealloc(parytet->tet);
}
// Must update the hullsize.
hullsize -= caveoldtetlist->objects;
caveoldtetlist->restart();
}
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;
b->fliplinklevel = 100000; // Unlimited flip level.
// Try to remove newly added Steiner points.
for (i = 0; i < n; i++) {
if (newsteiners[i] != NULL) {
if (!removevertexbyflips(newsteiners[i])) {
if (b->nobisect_param > 0) { // Not -Y0
// Save it in subvertstack for removal.
subvertstack->newindex((void **) &parypt);
*parypt = newsteiners[i];
}
steinercount++;
}
}
}
b->fliplinklevel = bak_fliplinklevel;
if (steinercount > 0) {
if (b->verbose > 2) {
printf(" Added %d interior Steiner points.\n", steinercount);
}
}
delete [] newsteiners;
return 1;
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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()
{
if (!b->quiet) {
printf("Suppressing Steiner points ...\n");
}
point rempt, *parypt;
int bak_fliplinklevel = b->fliplinklevel;
b->fliplinklevel = 100000; // Unlimited flip level.
int suppcount = 0, remcount = 0;
int i;
// Try to suppress boundary Steiner points.
for (i = 0; i < subvertstack->objects; i++) {
parypt = (point *) fastlookup(subvertstack, i);
rempt = *parypt;
if (pointtype(rempt) != UNUSEDVERTEX) {
if ((pointtype(rempt) == FREESEGVERTEX) ||
(pointtype(rempt) == FREEFACETVERTEX)) {
if (suppressbdrysteinerpoint(rempt)) {
suppcount++;
}
}
}
} // i
if (suppcount > 0) {
if (b->verbose) {
printf(" Suppressed %d boundary Steiner points.\n", suppcount);
}
}
if (b->nobisect_param > 0) { // -Y1
for (i = 0; i < subvertstack->objects; i++) {
parypt = (point *) fastlookup(subvertstack, i);
rempt = *parypt;
if (pointtype(rempt) != UNUSEDVERTEX) {
if (pointtype(rempt) == FREEVOLVERTEX) {
if (removevertexbyflips(rempt)) {
remcount++;
}
}
}
}
}
if (remcount > 0) {
if (b->verbose) {
printf(" Removed %d interior Steiner points.\n", remcount);
}
}
b->fliplinklevel = bak_fliplinklevel;
if (b->nobisect_param > 1) { // -Y2
// Smooth interior Steiner points.
optparameters opm;
triface *parytet;
point *ppt;
REAL ori;
int smtcount, count, ivcount;
int nt, j;
// Point smooth options.
opm.max_min_volume = 1;
opm.numofsearchdirs = 20;
opm.searchstep = 0.001;
opm.maxiter = 30; // Limit the maximum iterations.
smtcount = 0;
do {
nt = 0;
while (1) {
count = 0;
ivcount = 0; // Clear the inverted count.
for (i = 0; i < subvertstack->objects; i++) {
parypt = (point *) fastlookup(subvertstack, i);
rempt = *parypt;
if (pointtype(rempt) == FREEVOLVERTEX) {
getvertexstar(1, rempt, cavetetlist, NULL, NULL);
// Calculate the initial smallest volume (maybe zero or negative).
for (j = 0; j < cavetetlist->objects; j++) {
parytet = (triface *) fastlookup(cavetetlist, j);
ppt = (point *) &(parytet->tet[4]);
ori = orient3dfast(ppt[1], ppt[0], ppt[2], ppt[3]);
if (j == 0) {
opm.initval = ori;
} else {
if (opm.initval > ori) opm.initval = ori;
}
}
if (smoothpoint(rempt, cavetetlist, 1, &opm)) {
count++;
}
if (opm.imprval <= 0.0) {
ivcount++; // The mesh contains inverted elements.
}
cavetetlist->restart();
}
} // i
smtcount += count;
if (count == 0) {
// No point has been smoothed.
break;
}
nt++;
if (nt > 2) {
break; // Already three iterations.
}
} // while
if (ivcount > 0) {
// There are inverted elements!
if (opm.maxiter > 0) {
// Set unlimited smoothing steps. Try again.
opm.numofsearchdirs = 30;
opm.searchstep = 0.0001;
opm.maxiter = -1;
continue;
}
}
break;
} while (1); // Additional loop for (ivcount > 0)
if (ivcount > 0) {
printf("BUG Report! The mesh contain inverted elements.\n");
}
if (b->verbose) {
if (smtcount > 0) {
printf(" Smoothed %d Steiner points.\n", smtcount);
}
}
} // -Y2
subvertstack->restart();
return 1;
}
///////////////////////////////////////////////////////////////////////////////
// //
// recoverboundary() Recover segments and facets. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::recoverboundary(clock_t& tv)
{
arraypool *misseglist, *misshlist;
arraypool *bdrysteinerptlist;
face searchsh, *parysh;
face searchseg, *paryseg;
point rempt, *parypt;
long ms; // The number of missing segments/subfaces.
int nit; // The number of iterations.
int s, i;
// Counters.
long bak_segref_count, bak_facref_count, bak_volref_count;
if (!b->quiet) {
printf("Recovering boundaries...\n");
}
if (b->verbose) {
printf(" Recovering segments.\n");
}
// Segments will be introduced.
checksubsegflag = 1;
misseglist = new arraypool(sizeof(face), 8);
bdrysteinerptlist = new arraypool(sizeof(point), 8);
// In random order.
subsegs->traversalinit();
for (i = 0; i < subsegs->items; i++) {
s = randomnation(i + 1);
// Move the s-th seg to the i-th.
subsegstack->newindex((void **) &paryseg);
*paryseg = * (face *) fastlookup(subsegstack, s);
// Put i-th seg to be the s-th.
searchseg.sh = shellfacetraverse(subsegs);
paryseg = (face *) fastlookup(subsegstack, s);
*paryseg = searchseg;
}
// The init number of missing segments.
ms = subsegs->items;
nit = 0;
if (b->fliplinklevel < 0) {
autofliplinklevel = 1; // Init value.
}
// First, trying to recover segments by only doing flips.
while (1) {
recoversegments(misseglist, 0, 0);
if (misseglist->objects > 0) {
if (b->fliplinklevel >= 0) {
break;
} else {
if (misseglist->objects >= ms) {
nit++;
if (nit >= 3) {
//break;
// Do the last round with unbounded flip link level.
b->fliplinklevel = 100000;
}
} else {
ms = misseglist->objects;
if (nit > 0) {
nit--;
}
}
for (i = 0; i < misseglist->objects; i++) {
subsegstack->newindex((void **) &paryseg);
*paryseg = * (face *) fastlookup(misseglist, i);
}
misseglist->restart();
autofliplinklevel+=b->fliplinklevelinc;
}
} else {
// All segments are recovered.
break;
}
} // while (1)
if (b->verbose) {
printf(" %ld (%ld) segments are recovered (missing).\n",
subsegs->items - misseglist->objects, misseglist->objects);
}
if (misseglist->objects > 0) {
// Second, trying to recover segments by doing more flips (fullsearch).
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, 0);
if (misseglist->objects < ms) {
// The number of missing segments is reduced.
continue;
} else {
break;
}
}
if (b->verbose) {
printf(" %ld (%ld) segments are recovered (missing).\n",
subsegs->items - misseglist->objects, misseglist->objects);
}
}
if (misseglist->objects > 0) {
// Third, trying to recover segments by doing more flips (fullsearch)
// and adding Steiner points in the volume.
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, 1);
if (misseglist->objects < ms) {
// The number of missing segments is reduced.
continue;
} else {
break;
}
}
if (b->verbose) {
printf(" Added %ld Steiner points in volume.\n", st_volref_count);
}
}
if (misseglist->objects > 0) {
// 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);
}
misseglist->restart();
recoversegments(misseglist, 1, 2);
if (b->verbose) {
printf(" Added %ld Steiner points in segments.\n", st_segref_count);
if (st_volref_count > bak_inpoly_count) {
printf(" Added another %ld Steiner points in volume.\n",
st_volref_count - bak_inpoly_count);
}
}
assert(misseglist->objects == 0l);
}
if (st_segref_count > 0) {
// Try to remove the Steiner points added in segments.
bak_segref_count = st_segref_count;
bak_volref_count = st_volref_count;
for (i = 0; i < subvertstack->objects; i++) {
// Get the Steiner point.
parypt = (point *) fastlookup(subvertstack, i);
rempt = *parypt;
if (!removevertexbyflips(rempt)) {
// Save it in list.
bdrysteinerptlist->newindex((void **) &parypt);
*parypt = rempt;
}
}
if (b->verbose) {
if (st_segref_count < bak_segref_count) {
if (bak_volref_count < st_volref_count) {
printf(" Suppressed %ld Steiner points in segments.\n",
st_volref_count - bak_volref_count);
}
if ((st_segref_count + (st_volref_count - bak_volref_count)) <
bak_segref_count) {
printf(" Removed %ld Steiner points in segments.\n",
bak_segref_count -
(st_segref_count + (st_volref_count - bak_volref_count)));
}
}
}
subvertstack->restart();
}
tv = clock();
if (b->verbose) {
printf(" Recovering facets.\n");
}
// Subfaces will be introduced.
checksubfaceflag = 1;
misshlist = new arraypool(sizeof(face), 8);
// Randomly order the subfaces.
subfaces->traversalinit();
for (i = 0; i < subfaces->items; i++) {
s = randomnation(i + 1);
// Move the s-th subface to the i-th.
subfacstack->newindex((void **) &parysh);
*parysh = * (face *) fastlookup(subfacstack, s);
// Put i-th subface to be the s-th.
searchsh.sh = shellfacetraverse(subfaces);
parysh = (face *) fastlookup(subfacstack, s);
*parysh = searchsh;
}
ms = subfaces->items;
nit = 0;
b->fliplinklevel = -1; // Init.
if (b->fliplinklevel < 0) {
autofliplinklevel = 1; // Init value.
}
while (1) {
recoversubfaces(misshlist, 0);
if (misshlist->objects > 0) {
if (b->fliplinklevel >= 0) {
break;
} else {
if (misshlist->objects >= ms) {
nit++;
if (nit >= 3) {
//break;
// Do the last round with unbounded flip link level.
b->fliplinklevel = 100000;
}
} else {
ms = misshlist->objects;
if (nit > 0) {
nit--;
}
}
for (i = 0; i < misshlist->objects; i++) {
subfacstack->newindex((void **) &parysh);
*parysh = * (face *) fastlookup(misshlist, i);
}
misshlist->restart();
autofliplinklevel+=b->fliplinklevelinc;
}
} else {
// All subfaces are recovered.
break;
}
} // while (1)
if (b->verbose) {
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);
}
misshlist->restart();
recoversubfaces(NULL, 1);
if (b->verbose) {
printf(" Added %ld Steiner points in facets.\n", st_facref_count);
}
}
if (st_facref_count > 0) {
// Try to remove the Steiner points added in facets.
bak_facref_count = st_facref_count;
for (i = 0; i < subvertstack->objects; i++) {
// Get the Steiner point.
parypt = (point *) fastlookup(subvertstack, i);
rempt = *parypt;
if (!removevertexbyflips(*parypt)) {
// Save it in list.
bdrysteinerptlist->newindex((void **) &parypt);
*parypt = rempt;
}
}
if (b->verbose) {
if (st_facref_count < bak_facref_count) {
printf(" Removed %ld Steiner points in facets.\n",
bak_facref_count - st_facref_count);
}
}
subvertstack->restart();
}
if (bdrysteinerptlist->objects > 0) {
if (b->verbose) {
printf(" %ld Steiner points remained in boundary.\n",
bdrysteinerptlist->objects);
}
} // if
// Accumulate the dynamic memory.
totalworkmemory += (misseglist->totalmemory + misshlist->totalmemory +
bdrysteinerptlist->totalmemory);
delete bdrysteinerptlist;
delete misseglist;
delete misshlist;
}
//// ////
//// ////
//// steiner_cxx //////////////////////////////////////////////////////////////
//// reconstruct_cxx //////////////////////////////////////////////////////////
//// ////
//// ////
///////////////////////////////////////////////////////////////////////////////
// //
// carveholes() Remove tetrahedra not in the mesh domain. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::carveholes()
{
arraypool *tetarray, *hullarray;
triface tetloop, neightet, *parytet, *parytet1;
triface *regiontets = NULL;
face checksh, *parysh;
face checkseg;
point ptloop, *parypt;
int t1ver;
int i, j, k;
if (!b->quiet) {
if (b->convex) {
printf("Marking exterior tetrahedra ...\n");
} else {
printf("Removing exterior tetrahedra ...\n");
}
}
// Initialize the pool of exterior tets.
tetarray = new arraypool(sizeof(triface), 10);
hullarray = new arraypool(sizeof(triface), 10);
// Collect unprotected tets and hull tets.
tetrahedrons->traversalinit();
tetloop.ver = 11; // The face opposite to dummypoint.
tetloop.tet = alltetrahedrontraverse();
while (tetloop.tet != (tetrahedron *) NULL) {
if (ishulltet(tetloop)) {
// Is this side protected by a subface?
if (!issubface(tetloop)) {
// Collect an unprotected hull tet and tet.
infect(tetloop);
hullarray->newindex((void **) &parytet);
*parytet = tetloop;
// tetloop's face number is 11 & 3 = 3.
decode(tetloop.tet[3], neightet);
if (!infected(neightet)) {
infect(neightet);
tetarray->newindex((void **) &parytet);
*parytet = neightet;
}
}
}
tetloop.tet = alltetrahedrontraverse();
}
if (in->numberofholes > 0) {
// Mark as infected any tets inside volume holes.
for (i = 0; i < 3 * in->numberofholes; i += 3) {
// Search a tet containing the i-th hole point.
neightet.tet = NULL;
randomsample(&(in->holelist[i]), &neightet);
if (locate(&(in->holelist[i]), &neightet) != OUTSIDE) {
// The tet 'neightet' contain this point.
if (!infected(neightet)) {
infect(neightet);
tetarray->newindex((void **) &parytet);
*parytet = neightet;
// 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);
} else {
tetarray->newindex((void **) &parytet);
}
*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;
}
}
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;
}
}
} // if (!infected(neightet))
} else {
// A hole point locates outside of the convex hull.
if (!b->quiet) {
printf("Warning: The %d-th hole point ", i/3 + 1);
printf("lies outside the convex hull.\n");
}
}
} // i
} // if (in->numberofholes > 0)
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.
regiontets = new triface[in->numberofregions];
// Mark as marktested any tetrahedra inside volume regions.
for (i = 0; i < 5 * in->numberofregions; i += 5) {
// Search a tet containing the i-th region point.
neightet.tet = NULL;
randomsample(&(in->regionlist[i]), &neightet);
if (locate(&(in->regionlist[i]), &neightet) != OUTSIDE) {
regiontets[i/5] = neightet;
} else {
if (!b->quiet) {
printf("Warning: The %d-th region point ", i/5+1);
printf("lies outside the convex hull.\n");
}
regiontets[i/5].tet = NULL;
}
}
}
// Collect all exterior tets (in concave place and in holes).
for (i = 0; i < tetarray->objects; i++) {
parytet = (triface *) fastlookup(tetarray, i);
j = (parytet->ver & 3); // j is the current face number.
// Check the other three adjacent tets.
for (k = 1; k < 4; k++) {
decode(parytet->tet[(j + k) % 4], neightet);
// neightet may be a hull tet.
if (!infected(neightet)) {
// Is neightet protected by a subface.
if (!issubface(neightet)) {
// Not proected. Collect it. (It must not be a hull tet).
infect(neightet);
tetarray->newindex((void **) &parytet1);
*parytet1 = neightet;
} else {
// Protected. Check if it is a hull tet.
if (ishulltet(neightet)) {
// A hull tet. Collect it.
infect(neightet);
hullarray->newindex((void **) &parytet1);
*parytet1 = neightet;
// 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;
}
}
} else {
// Both sides of this face are in exterior.
// If there is a subface. It should be collected.
if (issubface(neightet)) {
tspivot(neightet, checksh);
if (!sinfected(checksh)) {
sinfect(checksh);
subfacstack->newindex((void **) &parysh);
*parysh = checksh;
}
}
}
} // j, k
} // i
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 (b->verbose) {
printf("Warning: The %d-th region point ", i+1);
printf("lies in the exterior of the domain.\n");
}
regiontets[i].tet = NULL;
}
}
}
// Collect vertices which point to infected tets. These vertices
// may get deleted after the removal of exterior tets.
// If -Y1 option is used, collect all Steiner points for removal.
// The lists 'cavetetvertlist' and 'subvertstack' are re-used.
points->traversalinit();
ptloop = pointtraverse();
while (ptloop != NULL) {
if ((pointtype(ptloop) != UNUSEDVERTEX) &&
(pointtype(ptloop) != DUPLICATEDVERTEX)) {
decode(point2tet(ptloop), neightet);
if (infected(neightet)) {
cavetetvertlist->newindex((void **) &parypt);
*parypt = ptloop;
}
if (b->nobisect && (b->nobisect_param > 0)) { // -Y1
// Queue it if it is a Steiner point.
if (pointmark(ptloop) >
(in->numberofpoints - (in->firstnumber ? 0 : 1))) {
subvertstack->newindex((void **) &parypt);
*parypt = ptloop;
}
}
}
ptloop = pointtraverse();
}
if (!b->convex && (tetarray->objects > 0l)) { // No -c option.
// Remove exterior tets. Hull tets are updated.
arraypool *newhullfacearray;
triface hulltet, casface;
point pa, pb, pc;
newhullfacearray = new arraypool(sizeof(triface), 10);
// Create and save new hull tets.
for (i = 0; i < tetarray->objects; i++) {
parytet = (triface *) fastlookup(tetarray, i);
for (j = 0; j < 4; j++) {
decode(parytet->tet[j], tetloop);
if (!infected(tetloop)) {
// Found a new hull face (must be a subface).
tspivot(tetloop, checksh);
maketetrahedron(&hulltet);
pa = org(tetloop);
pb = dest(tetloop);
pc = apex(tetloop);
setvertices(hulltet, pb, pa, pc, dummypoint);
bond(tetloop, hulltet);
// Update the subface-to-tet map.
sesymself(checksh);
tsbond(hulltet, checksh);
// Update the segment-to-tet map.
for (k = 0; k < 3; k++) {
if (issubseg(tetloop)) {
tsspivot1(tetloop, checkseg);
tssbond1(hulltet, checkseg);
sstbond1(checkseg, hulltet);
}
enextself(tetloop);
eprevself(hulltet);
}
// Update the point-to-tet map.
setpoint2tet(pa, (tetrahedron) tetloop.tet);
setpoint2tet(pb, (tetrahedron) tetloop.tet);
setpoint2tet(pc, (tetrahedron) tetloop.tet);
// Save the exterior tet at this hull face. It still holds pointer
// to the adjacent interior tet. Use it to connect new hull tets.
newhullfacearray->newindex((void **) &parytet1);
parytet1->tet = parytet->tet;
parytet1->ver = j;
} // if (!infected(tetloop))
} // j
} // i
// Connect new hull tets.
for (i = 0; i < newhullfacearray->objects; i++) {
parytet = (triface *) fastlookup(newhullfacearray, i);
fsym(*parytet, neightet);
// Get the new hull tet.
fsym(neightet, hulltet);
for (j = 0; j < 3; j++) {
esym(hulltet, casface);
if (casface.tet[casface.ver & 3] == NULL) {
// Since the boundary of the domain may not be a manifold, we
// find the adjacent hull face by traversing the tets in the
// exterior (which are all infected tets).
neightet = *parytet;
while (1) {
fnextself(neightet);
if (!infected(neightet)) break;
}
if (!ishulltet(neightet)) {
// An interior tet. Get the new hull tet.
fsymself(neightet);
esymself(neightet);
}
// Bond them together.
bond(casface, neightet);
}
enextself(hulltet);
enextself(*parytet);
} // j
} // i
if (subfacstack->objects > 0l) {
// Remove all subfaces which do not attach to any tetrahedron.
// 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",
pointmark(sorg(*parysh)), pointmark(sdest(*parysh)),
pointmark(sapex(*parysh)));
}
}
// Dissolve this subface from face links.
for (j = 0; j < 3; j++) {
spivot(*parysh, casingout);
sspivot(*parysh, checkseg);
if (casingout.sh != NULL) {
casingin = casingout;
while (1) {
spivot(casingin, checksh);
if (checksh.sh == parysh->sh) break;
casingin = checksh;
}
if (casingin.sh != casingout.sh) {
// Update the link: ... -> casingin -> casingout ->...
sbond1(casingin, casingout);
} else {
// Only one subface at this edge is left.
sdissolve(casingout);
}
if (checkseg.sh != NULL) {
// Make sure the segment does not connect to a dead one.
ssbond(casingout, checkseg);
}
} else {
if (checkseg.sh != NULL) {
// The segment is also dead.
if (delsegcount == 0) {
if (b->verbose) {
printf("Warning: Removing a dangling segment (%d, %d)\n",
pointmark(sorg(checkseg)), pointmark(sdest(checkseg)));
}
}
shellfacedealloc(subsegs, checkseg.sh);
delsegcount++;
}
}
senextself(*parysh);
} // j
// Delete this subface.
shellfacedealloc(subfaces, parysh->sh);
} // i
if (b->verbose) {
printf(" Deleted %ld subfaces.\n", subfacstack->objects);
if (delsegcount > 0) {
printf(" Deleted %ld segments.\n", delsegcount);
}
}
subfacstack->restart();
} // if (subfacstack->objects > 0l)
if (cavetetvertlist->objects > 0l) {
// Some vertices may lie in exterior. Marke them as UNUSEDVERTEX.
long delvertcount = unuverts;
long delsteinercount = 0l;
for (i = 0; i < cavetetvertlist->objects; i++) {
parypt = (point *) fastlookup(cavetetvertlist, i);
decode(point2tet(*parypt), neightet);
if (infected(neightet)) {
// Found an exterior vertex.
if (pointmark(*parypt) >
(in->numberofpoints - (in->firstnumber ? 0 : 1))) {
// A Steiner point.
if (pointtype(*parypt) == FREESEGVERTEX) {
st_segref_count--;
} else if (pointtype(*parypt) == FREEFACETVERTEX) {
st_facref_count--;
} else {
assert(pointtype(*parypt) == FREEVOLVERTEX);
st_volref_count--;
}
delsteinercount++;
if (steinerleft > 0) steinerleft++;
}
setpointtype(*parypt, UNUSEDVERTEX);
unuverts++;
}
}
if (b->verbose) {
if (unuverts > delvertcount) {
if (delsteinercount > 0l) {
if (unuverts > (delvertcount + delsteinercount)) {
printf(" Removed %ld exterior input vertices.\n",
unuverts - delvertcount - delsteinercount);
}
printf(" Removed %ld exterior Steiner vertices.\n",
delsteinercount);
} else {
printf(" Removed %ld exterior input vertices.\n",
unuverts - delvertcount);
}
}
}
cavetetvertlist->restart();
// Comment: 'subvertstack' will be cleaned in routine
// suppresssteinerpoints().
} // if (cavetetvertlist->objects > 0l)
// Update the hull size.
hullsize += (newhullfacearray->objects - hullarray->objects);
// Delete all exterior tets and old hull tets.
for (i = 0; i < tetarray->objects; i++) {
parytet = (triface *) fastlookup(tetarray, i);
tetrahedrondealloc(parytet->tet);
}
tetarray->restart();
for (i = 0; i < hullarray->objects; i++) {
parytet = (triface *) fastlookup(hullarray, i);
tetrahedrondealloc(parytet->tet);
}
hullarray->restart();
delete newhullfacearray;
} // if (!b->convex && (tetarray->objects > 0l))
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++) {
parytet = (triface *) fastlookup(tetarray, i);
setelemattribute(parytet->tet, attrnum, -1);
}
tetarray->restart();
for (i = 0; i < hullarray->objects; i++) {
parytet = (triface *) fastlookup(hullarray, i);
uninfect(*parytet);
}
hullarray->restart();
if (subfacstack->objects > 0l) {
for (i = 0; i < subfacstack->objects; i++) {
parysh = (face *) fastlookup(subfacstack, i);
suninfect(*parysh);
}
subfacstack->restart();
}
if (cavetetvertlist->objects > 0l) {
cavetetvertlist->restart();
}
} // if (b->convex && (tetarray->objects > 0l))
if (b->regionattrib) { // With -A option.
if (!b->quiet) {
printf("Spreading region attributes.\n");
}
REAL volume;
int attr, maxattr = 0; // Choose a small number here.
int attrnum = numelemattrib - 1;
// Comment: The element region marker is at the end of the list of
// the element attributes.
int regioncount = 0;
// If has user-defined region attributes.
if (in->numberofregions > 0) {
// Spread region attributes.
for (i = 0; i < 5 * in->numberofregions; i += 5) {
if (regiontets[i/5].tet != NULL) {
attr = (int) in->regionlist[i + 3];
if (attr > maxattr) {
maxattr = attr;
}
volume = in->regionlist[i + 4];
tetarray->restart(); // Re-use this array.
infect(regiontets[i/5]);
tetarray->newindex((void **) &parytet);
*parytet = regiontets[i/5];
// Collect and set attrs for all tets of this region.
for (j = 0; j < tetarray->objects; j++) {
parytet = (triface *) fastlookup(tetarray, j);
tetloop = *parytet;
setelemattribute(tetloop.tet, attrnum, attr);
if (b->varvolume) { // If has -a option.
setvolumebound(tetloop.tet, volume);
}
for (k = 0; k < 4; k++) {
decode(tetloop.tet[k], neightet);
// Is the adjacent already checked?
if (!infected(neightet)) {
// Is this side protected by a subface?
if (!issubface(neightet)) {
infect(neightet);
tetarray->newindex((void **) &parytet);
*parytet = neightet;
}
}
} // k
} // j
regioncount++;
} // if (regiontets[i/5].tet != NULL)
} // i
}
// Set attributes for all tetrahedra.
attr = maxattr + 1;
tetrahedrons->traversalinit();
tetloop.tet = tetrahedrontraverse();
while (tetloop.tet != (tetrahedron *) NULL) {
if (!infected(tetloop)) {
// An unmarked region.
tetarray->restart(); // Re-use this array.
infect(tetloop);
tetarray->newindex((void **) &parytet);
*parytet = tetloop;
// Find and mark all tets.
for (j = 0; j < tetarray->objects; j++) {
parytet = (triface *) fastlookup(tetarray, j);
tetloop = *parytet;
setelemattribute(tetloop.tet, attrnum, attr);
for (k = 0; k < 4; k++) {
decode(tetloop.tet[k], neightet);
// Is the adjacent tet already checked?
if (!infected(neightet)) {
// Is this side protected by a subface?
if (!issubface(neightet)) {
infect(neightet);
tetarray->newindex((void **) &parytet);
*parytet = neightet;
}
}
} // k
} // j
attr++; // Increase the attribute.
regioncount++;
}
tetloop.tet = tetrahedrontraverse();
}
// Until here, every tet has a region attribute.
// Uninfect processed tets.
tetrahedrons->traversalinit();
tetloop.tet = tetrahedrontraverse();
while (tetloop.tet != (tetrahedron *) NULL) {
uninfect(tetloop);
tetloop.tet = tetrahedrontraverse();
}
if (b->verbose) {
//assert(regioncount > 0);
if (regioncount > 1) {
printf(" Found %d subdomains.\n", regioncount);
} else {
printf(" Found %d domain.\n", regioncount);
}
}
} // if (b->regionattrib)
if (regiontets != NULL) {
delete [] regiontets;
}
delete tetarray;
delete hullarray;
if (!b->convex) { // No -c option
// The mesh is non-convex now.
nonconvex = 1;
// Push all hull tets into 'flipstack'.
tetrahedrons->traversalinit();
tetloop.ver = 11; // The face opposite to dummypoint.
tetloop.tet = alltetrahedrontraverse();
while (tetloop.tet != (tetrahedron *) NULL) {
if ((point) tetloop.tet[7] == dummypoint) {
fsym(tetloop, neightet);
flippush(flipstack, &neightet);
}
tetloop.tet = alltetrahedrontraverse();
}
flipconstraints fc;
fc.enqflag = 2;
long sliver_peel_count = lawsonflip3d(&fc);
if (sliver_peel_count > 0l) {
if (b->verbose) {
printf(" Removed %ld hull slivers.\n", sliver_peel_count);
}
}
unflipqueue->restart();
} // if (!b->convex)
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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 angtol, ang;
int eextras, marker = 0;
int bondflag;
int t1ver;
int idx, i, j, k;
if (!b->quiet) {
printf("Reconstructing mesh ...\n");
}
if (b->convex) { // -c option.
// Assume the mesh is convex. Exterior tets have region attribute -1.
assert(in->numberoftetrahedronattributes > 0);
} else {
// Assume the mesh is non-convex.
nonconvex = 1;
}
// Create a map from indices to vertices.
makeindex2pointmap(idx2verlist);
// 'idx2verlist' has length 'in->numberofpoints + 1'.
if (in->firstnumber == 1) {
idx2verlist[0] = dummypoint; // Let 0th-entry be dummypoint.
}
// Allocate an array that maps each vertex to its adjacent tets.
ver2tetarray = new tetrahedron[in->numberofpoints + 1];
//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;
}
// Create the tetrahedra and connect those that share a common face.
for (i = 0; i < in->numberoftetrahedra; i++) {
// Get the four vertices.
idx = i * in->numberofcorners;
for (j = 0; j < 4; j++) {
p[j] = idx2verlist[in->tetrahedronlist[idx++]];
}
// Check the orientation.
ori = orient3d(p[0], p[1], p[2], p[3]);
if (ori > 0.0) {
// Swap the first two vertices.
q[0] = p[0]; p[0] = p[1]; p[1] = q[0];
} else if (ori == 0.0) {
if (!b->quiet) {
printf("Warning: Tet #%d is degenerate.\n", i + in->firstnumber);
}
}
// Create a new tetrahedron.
maketetrahedron(&tetloop); // tetloop.ver = 11.
setvertices(tetloop, p[0], p[1], p[2], p[3]);
// Set element attributes if they exist.
for (j = 0; j < in->numberoftetrahedronattributes; j++) {
idx = i * in->numberoftetrahedronattributes;
attrib = in->tetrahedronattributelist[idx + j];
setelemattribute(tetloop.tet, j, attrib);
}
// If -a switch is used (with no number follows) Set a volume
// constraint if it exists.
if (b->varvolume) {
if (in->tetrahedronvolumelist != (REAL *) NULL) {
volume = in->tetrahedronvolumelist[i];
} else {
volume = -1.0;
}
setvolumebound(tetloop.tet, volume);
}
// Try connecting this tet to others that share the common faces.
for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) {
p[3] = oppo(tetloop);
// Look for other tets having this vertex.
idx = pointmark(p[3]);
tptr = ver2tetarray[idx];
// Link the current tet to the next one in the stack.
tetloop.tet[8 + tetloop.ver] = tptr;
// Push the current tet onto the stack.
ver2tetarray[idx] = encode(tetloop);
decode(tptr, checktet);
if (checktet.tet != NULL) {
p[0] = org(tetloop); // a
p[1] = dest(tetloop); // b
p[2] = apex(tetloop); // c
prevchktet = tetloop;
do {
q[0] = org(checktet); // a'
q[1] = dest(checktet); // b'
q[2] = apex(checktet); // c'
// Check the three faces at 'd' in 'checktet'.
bondflag = 0;
for (j = 0; j < 3; j++) {
// Go to the face [b',a',d], or [c',b',d], or [a',c',d].
esym(checktet, face2);
if (face2.tet[face2.ver & 3] == NULL) {
k = ((j + 1) % 3);
if (q[k] == p[0]) { // b', c', a' = a
if (q[j] == p[1]) { // a', b', c' = b
// [#,#,d] is matched to [b,a,d].
esym(tetloop, face1);
bond(face1, face2);
bondflag++;
}
}
if (q[k] == p[1]) { // b',c',a' = b
if (q[j] == p[2]) { // a',b',c' = c
// [#,#,d] is matched to [c,b,d].
enext(tetloop, face1);
esymself(face1);
bond(face1, face2);
bondflag++;
}
}
if (q[k] == p[2]) { // b',c',a' = c
if (q[j] == p[0]) { // a',b',c' = a
// [#,#,d] is matched to [a,c,d].
eprev(tetloop, face1);
esymself(face1);
bond(face1, face2);
bondflag++;
}
}
} else {
bondflag++;
}
enextself(checktet);
} // j
// Go to the next tet in the link.
tptr = checktet.tet[8 + checktet.ver];
if (bondflag == 3) {
// All three faces at d in 'checktet' have been connected.
// It can be removed from the link.
prevchktet.tet[8 + prevchktet.ver] = tptr;
} else {
// Bakup the previous tet in the link.
prevchktet = checktet;
}
decode(tptr, checktet);
} while (checktet.tet != NULL);
} // if (checktet.tet != NULL)
} // for (tetloop.ver = 0; ...
} // i
// Remember a tet of the mesh.
recenttet = tetloop;
// Create hull tets, create the point-to-tet map, and clean up the
// temporary spaces used in each tet.
hullsize = tetrahedrons->items;
tetrahedrons->traversalinit();
tetloop.tet = tetrahedrontraverse();
while (tetloop.tet != (tetrahedron *) NULL) {
tptr = encode(tetloop);
for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) {
if (tetloop.tet[tetloop.ver] == NULL) {
// Create a hull tet.
maketetrahedron(&hulltet);
p[0] = org(tetloop);
p[1] = dest(tetloop);
p[2] = apex(tetloop);
setvertices(hulltet, p[1], p[0], p[2], dummypoint);
bond(tetloop, hulltet);
// Try connecting this to others that share common hull edges.
for (j = 0; j < 3; j++) {
fsym(hulltet, face2);
while (1) {
if (face2.tet == NULL) break;
esymself(face2);
if (apex(face2) == dummypoint) break;
fsymself(face2);
}
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);
// Clean the temporary used space.
tetloop.tet[8 + tetloop.ver] = NULL;
}
tetloop.tet = tetrahedrontraverse();
}
hullsize = tetrahedrons->items - hullsize;
// Subfaces will be inserted into the mesh.
if (in->trifacelist != NULL) {
// A .face file is given. It may contain boundary faces. Insert them.
for (i = 0; i < in->numberoftrifaces; i++) {
// Is it a subface?
if (in->trifacemarkerlist != NULL) {
marker = in->trifacemarkerlist[i];
} else {
// Face markers are not available. Assume all of them are subfaces.
marker = 1;
}
if (marker > 0) {
idx = i * 3;
for (j = 0; j < 3; j++) {
p[j] = idx2verlist[in->trifacelist[idx++]];
}
// Search the subface.
bondflag = 0;
// Make sure all vertices are in the mesh. Avoid crash.
for (j = 0; j < 3; j++) {
decode(point2tet(p[j]), checktet);
if (checktet.tet == NULL) break;
}
if ((j == 3) && getedge(p[0], p[1], &checktet)) {
tetloop = checktet;
q[2] = apex(checktet);
while (1) {
if (apex(tetloop) == p[2]) {
// Found the face.
// Check if there exist a subface already?
tspivot(tetloop, neighsh);
if (neighsh.sh != NULL) {
// Found a duplicated subface.
// This happens when the mesh was generated by other mesher.
bondflag = 0;
} else {
bondflag = 1;
}
break;
}
fnextself(tetloop);
if (apex(tetloop) == q[2]) break;
}
}
if (bondflag) {
// Create a new subface.
makeshellface(subfaces, &subloop);
setshvertices(subloop, p[0], p[1], p[2]);
// Create the point-to-subface map.
sptr = sencode(subloop);
for (j = 0; j < 3; j++) {
setpointtype(p[j], FACETVERTEX); // initial type.
setpoint2sh(p[j], sptr);
}
if (in->trifacemarkerlist != NULL) {
setshellmark(subloop, in->trifacemarkerlist[i]);
}
// 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",
i + in->firstnumber, pointmark(p[0]), pointmark(p[1]),
pointmark(p[2]));
}
}
} // if (bondflag)
} // if (marker > 0)
} // i
} // if (in->trifacelist)
// Indentify subfaces from the mesh.
// Create subfaces for hull faces (if they're not subface yet) and
// interior faces which separate two different materials.
eextras = in->numberoftetrahedronattributes;
tetrahedrons->traversalinit();
tetloop.tet = tetrahedrontraverse();
while (tetloop.tet != (tetrahedron *) NULL) {
for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) {
tspivot(tetloop, neighsh);
if (neighsh.sh == NULL) {
bondflag = 0;
fsym(tetloop, checktet);
if (ishulltet(checktet)) {
// A hull face.
if (!b->convex) {
bondflag = 1; // Insert a hull subface.
}
} else {
if (eextras > 0) {
if (elemattribute(tetloop.tet, eextras - 1) !=
elemattribute(checktet.tet, eextras - 1)) {
bondflag = 1; // Insert an interior interface.
}
}
}
if (bondflag) {
// Create a new subface.
makeshellface(subfaces, &subloop);
p[0] = org(tetloop);
p[1] = dest(tetloop);
p[2] = apex(tetloop);
setshvertices(subloop, p[0], p[1], p[2]);
// Create the point-to-subface map.
sptr = sencode(subloop);
for (j = 0; j < 3; j++) {
setpointtype(p[j], FACETVERTEX); // initial type.
setpoint2sh(p[j], sptr);
}
setshellmark(subloop, 0); // Default marker.
// Insert the subface into the mesh.
tsbond(tetloop, subloop);
sesymself(subloop);
tsbond(checktet, subloop);
} // if (bondflag)
} // if (neighsh.sh == NULL)
}
tetloop.tet = tetrahedrontraverse();
}
// Connect subfaces together.
subfaces->traversalinit();
subloop.shver = 0;
subloop.sh = shellfacetraverse(subfaces);
while (subloop.sh != (shellface *) NULL) {
for (i = 0; i < 3; i++) {
spivot(subloop, neighsh);
if (neighsh.sh == NULL) {
// Form a subface ring by linking all subfaces at this edge.
// Traversing all faces of the tets at this edge.
stpivot(subloop, tetloop);
q[2] = apex(tetloop);
neighsh = subloop;
while (1) {
fnextself(tetloop);
tspivot(tetloop, nextsh);
if (nextsh.sh != NULL) {
// 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)
} // if (neighsh.sh == NULL)
senextself(subloop);
}
subloop.sh = shellfacetraverse(subfaces);
}
// Segments will be introduced.
if (in->edgelist != NULL) {
// A .edge file is given. It may contain boundary edges. Insert them.
for (i = 0; i < in->numberofedges; i++) {
// Is it a segment?
if (in->edgemarkerlist != NULL) {
marker = in->edgemarkerlist[i];
} else {
// Edge markers are not available. Assume all of them are segments.
marker = 1;
}
if (marker != 0) {
// Insert a segment.
idx = i * 2;
for (j = 0; j < 2; j++) {
p[j] = idx2verlist[in->edgelist[idx++]];
}
// Make sure all vertices are in the mesh. Avoid crash.
for (j = 0; j < 2; j++) {
decode(point2tet(p[j]), checktet);
if (checktet.tet == NULL) break;
}
// Search the segment.
if ((j == 2) && getedge(p[0], p[1], &checktet)) {
// Create a new subface.
makeshellface(subsegs, &segloop);
setshvertices(segloop, p[0], p[1], NULL);
// Create the point-to-segment map.
sptr = sencode(segloop);
for (j = 0; j < 2; j++) {
setpointtype(p[j], RIDGEVERTEX); // initial type.
setpoint2sh(p[j], sptr);
}
if (in->edgemarkerlist != NULL) {
setshellmark(segloop, marker);
}
// Insert the segment into the mesh.
tetloop = checktet;
q[2] = apex(checktet);
subloop.sh = NULL;
while (1) {
tssbond1(tetloop, segloop);
tspivot(tetloop, subloop);
if (subloop.sh != NULL) {
ssbond1(subloop, segloop);
sbond1(segloop, subloop);
}
fnextself(tetloop);
if (apex(tetloop) == q[2]) break;
} // while (1)
// Remember an adjacent tet for this segment.
sstbond1(segloop, tetloop);
} else {
if (!b->quiet) {
printf("Warning: Segment #%d [%d,%d] is missing.\n",
i + in->firstnumber, pointmark(p[0]), pointmark(p[1]));
}
}
} // if (marker != 0)
} // i
} // if (in->edgelist)
// 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;
subfaces->traversalinit();
subloop.shver = 0;
subloop.sh = shellfacetraverse(subfaces);
while (subloop.sh != (shellface *) NULL) {
for (i = 0; i < 3; i++) {
sspivot(subloop, segloop);
if (segloop.sh == NULL) {
// Check if this edge is a segment.
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;
}
if (idx != 2) {
// It's a non-manifold edge. Insert a segment.
p[0] = sorg(subloop);
p[1] = sdest(subloop);
bondflag = 1;
} else {
spivot(subloop, neighsh);
if (shellmark(subloop) != shellmark(neighsh)) {
// It's an interior interface. Insert a segment.
p[0] = sorg(subloop);
p[1] = sdest(subloop);
bondflag = 1;
} else {
if (!b->convex) {
// Check the dihedral angle formed by the two subfaces.
p[0] = sorg(subloop);
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) {
bondflag = 1;
}
}
}
}
if (bondflag) {
// Create a new segment.
makeshellface(subsegs, &segloop);
setshvertices(segloop, p[0], p[1], NULL);
// Create the point-to-segment map.
sptr = sencode(segloop);
for (j = 0; j < 2; j++) {
setpointtype(p[j], RIDGEVERTEX); // initial type.
setpoint2sh(p[j], sptr);
}
setshellmark(segloop, 0); // Initially has no marker.
// Insert the subface into the mesh.
stpivot(subloop, tetloop);
q[2] = apex(tetloop);
while (1) {
tssbond1(tetloop, segloop);
tspivot(tetloop, neighsh);
if (neighsh.sh != NULL) {
ssbond1(neighsh, segloop);
}
fnextself(tetloop);
if (apex(tetloop) == q[2]) break;
} // while (1)
// Remember an adjacent tet for this segment.
sstbond1(segloop, tetloop);
sbond1(segloop, subloop);
} // if (bondflag)
} // if (neighsh.sh == NULL)
senextself(subloop);
} // i
subloop.sh = shellfacetraverse(subfaces);
}
// Remember the number of input segments.
insegments = subsegs->items;
if (!b->nobisect || checkconstraints) {
// Mark Steiner points on segments and facets.
// - all vertices which remaining type FEACTVERTEX become
// Steiner points in facets (= FREEFACERVERTEX).
// - vertices on segment need to be checked.
face* segperverlist;
int* idx2seglist;
face parentseg, nextseg;
verttype vt;
REAL area, len, l1, l2;
int fmarker;
makepoint2submap(subsegs, idx2seglist, segperverlist);
points->traversalinit();
point ptloop = pointtraverse();
while (ptloop != NULL) {
vt = pointtype(ptloop);
if (vt == VOLVERTEX) {
setpointtype(ptloop, FREEVOLVERTEX);
st_volref_count++;
} else if (vt == FACETVERTEX) {
setpointtype(ptloop, FREEFACETVERTEX);
st_facref_count++;
} else if (vt == RIDGEVERTEX) {
idx = pointmark(ptloop) - in->firstnumber;
if ((idx2seglist[idx + 1] - idx2seglist[idx]) == 2) {
i = idx2seglist[idx];
parentseg = segperverlist[i];
nextseg = segperverlist[i + 1];
sesymself(nextseg);
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) {
// They are (nearly) collinear.
setpointtype(ptloop, FREESEGVERTEX);
// Connect nextseg and parentseg together at ptloop.
senextself(nextseg);
senext2self(parentseg);
sbond(nextseg, parentseg);
st_segref_count++;
}
}
}
ptloop = pointtraverse();
}
// Are there area constraints?
if (b->quality && (in->facetconstraintlist != (REAL *) NULL)) {
// Set maximum area constraints on facets.
for (i = 0; i < in->numberoffacetconstraints; i++) {
fmarker = (int) in->facetconstraintlist[i * 2];
area = in->facetconstraintlist[i * 2 + 1];
subfaces->traversalinit();
subloop.sh = shellfacetraverse(subfaces);
while (subloop.sh != NULL) {
if (shellmark(subloop) == fmarker) {
setareabound(subloop, area);
}
subloop.sh = shellfacetraverse(subfaces);
}
}
}
// Are there length constraints?
if (b->quality && (in->segmentconstraintlist != (REAL *) NULL)) {
// Set maximum length constraints on segments.
int e1, e2;
for (i = 0; i < in->numberofsegmentconstraints; i++) {
e1 = (int) in->segmentconstraintlist[i * 3];
e2 = (int) in->segmentconstraintlist[i * 3 + 1];
len = in->segmentconstraintlist[i * 3 + 2];
// Search for edge [e1, e2].
idx = e1 - in->firstnumber;
for (j = idx2seglist[idx]; j < idx2seglist[idx + 1]; j++) {
parentseg = segperverlist[j];
if (pointmark(sdest(parentseg)) == e2) {
setareabound(parentseg, len);
break;
}
}
}
}
delete [] idx2seglist;
delete [] segperverlist;
}
// Set global flags.
checksubsegflag = 1;
checksubfaceflag = 1;
delete [] idx2verlist;
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)
{
point pa, pb, pc, pd;
enum locateresult loc = OUTSIDE;
REAL vol, ori1, ori2 = 0, ori3 = 0, ori4 = 0;
int t1ver;
// Randomly select a good starting tet.
if (randflag) {
randomsample(searchpt, searchtet);
} else {
if (searchtet->tet == NULL) {
*searchtet = recenttet;
}
}
loc = locate(searchpt, searchtet);
if (loc == OUTSIDE) {
if (b->convex) { // -c option
// The point lies outside of the convex hull.
return (int) loc;
}
// 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).
fsymself(*searchtet);
}
}
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
}
searchtet->tet = tetrahedrontraverse();
} // while (searchtet->tet != NULL)
nonregularcount++; // Re-use this counter.
}
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;
}
}
}
} 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;
}
}
} 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;
}
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. //
// //
///////////////////////////////////////////////////////////////////////////////
REAL tetgenmesh::getpointmeshsize(point searchpt, triface *searchtet, int iloc)
{
point *pts, pa, pb, pc;
REAL volume, vol[4], wei[4];
REAL size;
int i;
size = 0;
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)) {
// P1 interpolation.
volume = orient3dfast(pts[0], pts[1], pts[2], pts[3]);
vol[0] = orient3dfast(searchpt, pts[1], pts[2], pts[3]);
vol[1] = orient3dfast(pts[0], searchpt, pts[2], pts[3]);
vol[2] = orient3dfast(pts[0], pts[1], searchpt, pts[3]);
vol[3] = orient3dfast(pts[0], pts[1], pts[2], searchpt);
for (i = 0; i < 4; i++) {
wei[i] = fabs(vol[i] / volume);
size += (wei[i] * pts[i][pointmtrindex]);
}
}
} else if (iloc == (int) ONFACE) {
pa = org(*searchtet);
pb = dest(*searchtet);
pc = apex(*searchtet);
if ((pa[pointmtrindex] > 0) && (pb[pointmtrindex] > 0) &&
(pc[pointmtrindex] > 0)) {
volume = triarea(pa, pb, pc);
vol[0] = triarea(searchpt, pb, pc);
vol[1] = triarea(pa, searchpt, pc);
vol[2] = triarea(pa, pb, searchpt);
size = (vol[0] / volume) * pa[pointmtrindex]
+ (vol[1] / volume) * pb[pointmtrindex]
+ (vol[2] / volume) * pc[pointmtrindex];
}
} else if (iloc == (int) ONEDGE) {
pa = org(*searchtet);
pb = dest(*searchtet);
if ((pa[pointmtrindex] > 0) && (pb[pointmtrindex] > 0)) {
volume = distance(pa, pb);
vol[0] = distance(searchpt, pb);
vol[1] = distance(pa, searchpt);
size = (vol[0] / volume) * pa[pointmtrindex]
+ (vol[1] / volume) * pb[pointmtrindex];
}
} else if (iloc == (int) ONVERTEX) {
pa = org(*searchtet);
if (pa[pointmtrindex] > 0) {
size = pa[pointmtrindex];
}
}
return size;
}
///////////////////////////////////////////////////////////////////////////////
// //
// interpolatemeshsize() Interpolate the mesh size from a background mesh //
// (source) to the current mesh (destination). //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::interpolatemeshsize()
{
triface searchtet;
point ploop;
REAL minval = 0.0, maxval = 0.0;
int iloc;
int count;
if (!b->quiet) {
printf("Interpolating mesh size ...\n");
}
long bak_nonregularcount = nonregularcount;
nonregularcount = 0l; // Count the number of (slow) global searches.
long baksmaples = bgm->samples;
bgm->samples = 3l;
count = 0; // Count the number of interpolated points.
// Interpolate sizes for all points in the current mesh.
points->traversalinit();
ploop = pointtraverse();
while (ploop != NULL) {
// Search a tet in bgm which containing this point.
searchtet.tet = NULL;
iloc = bgm->scoutpoint(ploop, &searchtet, 1); // randflag = 1
if (iloc != (int) OUTSIDE) {
// Interpolate the mesh size.
ploop[pointmtrindex] = bgm->getpointmeshsize(ploop, &searchtet, iloc);
setpoint2bgmtet(ploop, bgm->encode(searchtet));
if (count == 0) {
// This is the first interpolated point.
minval = maxval = ploop[pointmtrindex];
} else {
if (ploop[pointmtrindex] < minval) {
minval = ploop[pointmtrindex];
}
if (ploop[pointmtrindex] > maxval) {
maxval = ploop[pointmtrindex];
}
}
count++;
} else {
if (!b->quiet) {
printf("Warnning: Failed to locate point %d in source mesh.\n",
pointmark(ploop));
}
}
ploop = pointtraverse();
}
if (b->verbose) {
printf(" Interoplated %d points.\n", count);
if (nonregularcount > 0l) {
printf(" Performed %ld brute-force searches.\n", nonregularcount);
}
printf(" Size rangle [%.17g, %.17g].\n", minval, maxval);
}
bgm->samples = baksmaples;
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). //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::insertconstrainedpoints(point *insertarray, int arylen,
int rejflag)
{
triface searchtet, spintet;
face splitsh;
face splitseg;
insertvertexflags ivf;
flipconstraints fc;
int randflag = 0;
int t1ver;
int i;
if (b->verbose) {
printf(" Inserting %d constrained points\n", arylen);
}
if (b->no_sort) { // -b/1 option.
if (b->verbose) {
printf(" Using the input order.\n");
}
} else {
if (b->verbose) {
printf(" Permuting vertices.\n");
}
point swappoint;
int randindex;
srand(arylen);
for (i = 0; i < arylen; i++) {
randindex = rand() % (i + 1);
swappoint = insertarray[i];
insertarray[i] = insertarray[randindex];
insertarray[randindex] = swappoint;
}
if (b->brio_hilbert) { // -b1 option
if (b->verbose) {
printf(" Sorting vertices.\n");
}
hilbert_init(in->mesh_dim);
int ngroup = 0;
brio_multiscale_sort(insertarray, arylen, b->brio_threshold,
b->brio_ratio, &ngroup);
} else { // -b0 option.
randflag = 1;
} // if (!b->brio_hilbert)
} // if (!b->no_sort)
long bak_nonregularcount = nonregularcount;
nonregularcount = 0l;
long baksmaples = samples;
samples = 3l; // Use at least 3 samples. Updated in randomsample().
long bak_seg_count = st_segref_count;
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.
}
ivf.rejflag = rejflag;
ivf.chkencflag = 0;
ivf.sloc = (int) INSTAR;
ivf.sbowywat = 3;
ivf.splitbdflag = 1;
ivf.respectbdflag = 1;
ivf.assignmeshsize = b->metric;
encseglist = new arraypool(sizeof(face), 8);
encshlist = new arraypool(sizeof(badface), 8);
// 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);
// Decide the right type for this point.
setpointtype(insertarray[i], FREEVOLVERTEX); // Default.
splitsh.sh = NULL;
splitseg.sh = NULL;
if (ivf.iloc == (int) ONEDGE) {
if (issubseg(searchtet)) {
tsspivot1(searchtet, splitseg);
setpointtype(insertarray[i], FREESEGVERTEX);
//ivf.rejflag = 0;
} else {
// Check if it is a subface edge.
spintet = searchtet;
while (1) {
if (issubface(spintet)) {
tspivot(spintet, splitsh);
setpointtype(insertarray[i], FREEFACETVERTEX);
//ivf.rejflag |= 1;
break;
}
fnextself(spintet);
if (spintet.tet == searchtet.tet) break;
}
}
} else if (ivf.iloc == (int) ONFACE) {
if (issubface(searchtet)) {
tspivot(searchtet, splitsh);
setpointtype(insertarray[i], FREEFACETVERTEX);
//ivf.rejflag |= 1;
}
}
// Now insert the point.
if (insertpoint(insertarray[i], &searchtet, &splitsh, &splitseg, &ivf)) {
if (flipstack != NULL) {
// There are queued faces. Use flips to recover Delaunayness.
lawsonflip3d(&fc);
// There may be unflippable edges. Ignore them.
unflipqueue->restart();
}
// Update the Steiner counters.
if (pointtype(insertarray[i]) == FREESEGVERTEX) {
st_segref_count++;
} else if (pointtype(insertarray[i]) == FREEFACETVERTEX) {
st_facref_count++;
} else {
st_volref_count++;
}
} else {
// Point is not inserted.
//pointdealloc(insertarray[i]);
setpointtype(insertarray[i], UNUSEDVERTEX);
unuverts++;
encseglist->restart();
encshlist->restart();
}
} // i
delete encseglist;
delete encshlist;
if (b->verbose) {
printf(" Inserted %ld (%ld, %ld, %ld) vertices.\n",
st_segref_count + st_facref_count + st_volref_count -
(bak_seg_count + bak_fac_count + bak_vol_count),
st_segref_count - bak_seg_count, st_facref_count - bak_fac_count,
st_volref_count - bak_vol_count);
if (nonregularcount > 0l) {
printf(" Performed %ld brute-force searches.\n", nonregularcount);
}
}
nonregularcount = bak_nonregularcount;
samples = baksmaples;
}
void tetgenmesh::insertconstrainedpoints(tetgenio *addio)
{
point *insertarray, newpt;
REAL x, y, z, w;
int index, attribindex, mtrindex;
int arylen, i, j;
if (!b->quiet) {
printf("Inserting constrained points ...\n");
}
insertarray = new point[addio->numberofpoints];
arylen = 0;
index = 0;
attribindex = 0;
mtrindex = 0;
for (i = 0; i < addio->numberofpoints; i++) {
x = addio->pointlist[index++];
y = addio->pointlist[index++];
z = addio->pointlist[index++];
// Test if this point lies inside the bounding box.
if ((x < xmin) || (x > xmax) || (y < ymin) || (y > ymax) ||
(z < zmin) || (z > zmax)) {
if (b->verbose) {
printf("Warning: Point #%d lies outside the bounding box. Ignored\n",
i + in->firstnumber);
}
continue;
}
makepoint(&newpt, UNUSEDVERTEX);
newpt[0] = x;
newpt[1] = y;
newpt[2] = z;
// Read the point attributes. (Including point weights.)
for (j = 0; j < addio->numberofpointattributes; j++) {
newpt[3 + j] = addio->pointattributelist[attribindex++];
}
// Read the point metric tensor.
for (j = 0; j < addio->numberofpointmtrs; j++) {
newpt[pointmtrindex + j] = addio->pointmtrlist[mtrindex++];
}
if (b->weighted) { // -w option
if (addio->numberofpointattributes > 0) {
// The first point attribute is its weight.
w = newpt[3];
} else {
// No given weight available. Default choose the maximum
// absolute value among its coordinates.
w = fabs(x);
if (w < fabs(y)) w = fabs(y);
if (w < fabs(z)) w = fabs(z);
}
if (b->weighted_param == 0) {
newpt[3] = x * x + y * y + z * z - w; // Weighted DT.
} else { // -w1 option
newpt[3] = w; // Regular tetrahedralization.
}
}
insertarray[arylen] = newpt;
arylen++;
} // i
// Insert the points.
int rejflag = 0; // Do not check encroachment.
if (b->metric) { // -m option.
rejflag |= 4; // Reject it if it lies in some protecting balls.
}
insertconstrainedpoints(insertarray, arylen, rejflag);
delete [] insertarray;
}
///////////////////////////////////////////////////////////////////////////////
// //
// meshcoarsening() Deleting (selected) vertices. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::collectremovepoints(arraypool *remptlist)
{
point ptloop, *parypt;
verttype vt;
// If a mesh sizing function is given. Collect vertices whose mesh size
// is greater than its smallest edge length.
if (b->metric) { // -m option
REAL len, smlen;
int i;
points->traversalinit();
ptloop = pointtraverse();
while (ptloop != NULL) {
if (ptloop[pointmtrindex] > 0) {
// Get the smallest edge length at this vertex.
getvertexstar(1, ptloop, cavetetlist, cavetetvertlist, NULL);
parypt = (point *) fastlookup(cavetetvertlist, 0);
smlen = distance(ptloop, *parypt);
for (i = 1; i < cavetetvertlist->objects; i++) {
parypt = (point *) fastlookup(cavetetvertlist, i);
len = distance(ptloop, *parypt);
if (len < smlen) {
smlen = len;
}
}
cavetetvertlist->restart();
cavetetlist->restart();
if (smlen < ptloop[pointmtrindex]) {
pinfect(ptloop);
remptlist->newindex((void **) &parypt);
*parypt = ptloop;
}
}
ptloop = pointtraverse();
}
if (b->verbose > 1) {
printf(" Coarsen %ld oversized points.\n", remptlist->objects);
}
}
// If 'in->pointmarkerlist' exists, Collect vertices with markers '-1'.
if (in->pointmarkerlist != NULL) {
long bak_count = remptlist->objects;
points->traversalinit();
ptloop = pointtraverse();
int index = 0;
while (ptloop != NULL) {
if (index < in->numberofpoints) {
if (in->pointmarkerlist[index] == -1) {
pinfect(ptloop);
remptlist->newindex((void **) &parypt);
*parypt = ptloop;
}
} else {
// Remaining are not input points. Stop here.
break;
}
index++;
ptloop = pointtraverse();
}
if (b->verbose > 1) {
printf(" Coarsen %ld marked points.\n", remptlist->objects - bak_count);
}
} // if (in->pointmarkerlist != NULL)
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);
}
arraypool *intptlist = new arraypool(sizeof(point *), 10);
// Count the total number of interior points.
points->traversalinit();
ptloop = pointtraverse();
while (ptloop != NULL) {
vt = pointtype(ptloop);
if ((vt == VOLVERTEX) || (vt == FREEVOLVERTEX) ||
(vt == FREEFACETVERTEX) || (vt == FREESEGVERTEX)) {
intptlist->newindex((void **) &parypt);
*parypt = ptloop;
}
ptloop = pointtraverse();
}
if (intptlist->objects > 0l) {
// Sort the list of points randomly.
point *parypt_i, swappt;
int randindex, i;
srand(intptlist->objects);
for (i = 0; i < intptlist->objects; i++) {
randindex = rand() % (i + 1); // randomnation(i + 1);
parypt_i = (point *) fastlookup(intptlist, i);
parypt = (point *) fastlookup(intptlist, randindex);
// Swap this two points.
swappt = *parypt_i;
*parypt_i = *parypt;
*parypt = swappt;
}
int remcount = (int) ((REAL) intptlist->objects * b->coarsen_percent);
// Return the first remcount points.
for (i = 0; i < remcount; i++) {
parypt_i = (point *) fastlookup(intptlist, i);
if (!pinfected(*parypt_i)) {
pinfected(*parypt_i);
remptlist->newindex((void **) &parypt);
*parypt = *parypt_i;
}
}
}
delete intptlist;
}
// Unmark all collected vertices.
for (int i = 0; i < remptlist->objects; i++) {
parypt = (point *) fastlookup(remptlist, i);
puninfect(*parypt);
}
}
void tetgenmesh::meshcoarsening()
{
arraypool *remptlist;
if (!b->quiet) {
printf("Mesh coarsening ...\n");
}
// Collect the set of points to be removed
remptlist = new arraypool(sizeof(point *), 10);
collectremovepoints(remptlist);
if (remptlist->objects == 0l) {
delete remptlist;
return;
}
if (b->verbose) {
if (remptlist->objects > 0l) {
printf(" Removing %ld points...\n", remptlist->objects);
}
}
point *parypt, *plastpt;
long ms = remptlist->objects;
int nit = 0;
int bak_fliplinklevel = b->fliplinklevel;
b->fliplinklevel = -1;
autofliplinklevel = 1; // Init value.
int i;
while (1) {
if (b->verbose > 1) {
printf(" Removing points [%s level = %2d] #: %ld.\n",
(b->fliplinklevel > 0) ? "fixed" : "auto",
(b->fliplinklevel > 0) ? b->fliplinklevel : autofliplinklevel,
remptlist->objects);
}
// 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);
*parypt = *plastpt;
remptlist->objects--;
i--;
}
}
if (remptlist->objects > 0l) {
if (b->fliplinklevel >= 0) {
break; // We have tried all levels.
}
if (remptlist->objects == ms) {
nit++;
if (nit >= 3) {
// Do the last round with unbounded flip link level.
b->fliplinklevel = 100000;
}
} else {
ms = remptlist->objects;
if (nit > 0) {
nit--;
}
}
autofliplinklevel+=b->fliplinklevelinc;
} else {
// All points are removed.
break;
}
} // while (1)
if (remptlist->objects > 0l) {
if (b->verbose) {
printf(" %ld points are not removed !\n", remptlist->objects);
}
}
b->fliplinklevel = bak_fliplinklevel;
delete remptlist;
}
//// ////
//// ////
//// reconstruct_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'. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::makefacetverticesmap()
{
arraypool *facetvertexlist, *vertlist, **paryvertlist;
face subloop, neighsh, *parysh, *parysh1;
point pa, *ppt, *parypt;
verttype vt;
int facetindex, totalvertices;
int i, j, k;
if (b->verbose) {
printf(" Creating the facet vertices map.\n");
}
facetvertexlist = new arraypool(sizeof(arraypool *), 10);
facetindex = totalvertices = 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];
}
}
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;
}
}
sinfect(neighsh);
caveshlist->newindex((void **) &parysh1);
*parysh1 = neighsh;
}
}
senextself(*parysh);
}
} // i
totalvertices += (int) vertlist->objects;
// Uninfect facet vertices.
for (k = 0; k < vertlist->objects; k++) {
parypt = (point *) fastlookup(vertlist, k);
puninfect(*parypt);
}
caveshlist->restart();
// Save this vertex list.
facetvertexlist->newindex((void **) &paryvertlist);
*paryvertlist = vertlist;
facetindex++;
}
subloop.sh = shellfacetraverse(subfaces);
}
// All subfaces are infected. Uninfect them.
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);
}
idx2facetlist = new int[facetindex + 1];
facetverticeslist = new point[totalvertices];
totalworkmemory += ((facetindex + 1) * sizeof(int) +
totalvertices * sizeof(point *));
idx2facetlist[0] = 0;
for (i = 0, k = 0; i < facetindex; i++) {
paryvertlist = (arraypool **) fastlookup(facetvertexlist, i);
vertlist = *paryvertlist;
idx2facetlist[i + 1] = (idx2facetlist[i] + (int) vertlist->objects);
for (j = 0; j < vertlist->objects; j++) {
parypt = (point *) fastlookup(vertlist, j);
facetverticeslist[k] = *parypt;
k++;
}
}
assert(k == totalvertices);
// Free the lists.
for (i = 0; i < facetvertexlist->objects; i++) {
paryvertlist = (arraypool **) fastlookup(facetvertexlist, i);
vertlist = *paryvertlist;
delete vertlist;
}
delete facetvertexlist;
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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;
point pa1 = segmentendpointslist[segidx1 * 2];
point pb1 = segmentendpointslist[segidx1 * 2 + 1];
point pa2 = segmentendpointslist[segidx2 * 2];
point pb2 = segmentendpointslist[segidx2 * 2 + 1];
if ((pa1 == pa2) || (pa1 == pb2) || (pb1 == pa2) || (pb1 == pb2)) {
return 1;
}
return 0;
}
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 tetgenmesh::facetfacetadjacent(face *subsh1, face *subsh2)
{
int count = 0, i;
int fidx1 = getfacetindex(*subsh1);
int fidx2 = getfacetindex(*subsh2);
if (fidx1 == fidx2) return 0;
for (i = idx2facetlist[fidx1]; i < idx2facetlist[fidx1+1]; i++) {
pinfect(facetverticeslist[i]);
}
for (i = idx2facetlist[fidx2]; i < idx2facetlist[fidx2+1]; i++) {
if (pinfected(facetverticeslist[i])) count++;
}
// Uninfect the vertices.
for (i = idx2facetlist[fidx1]; i < idx2facetlist[fidx1+1]; i++) {
puninfect(facetverticeslist[i]);
}
return count > 0;
}
///////////////////////////////////////////////////////////////////////////////
// //
// checkseg4encroach() Check if an edge is encroached upon by a point. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::checkseg4encroach(point pa, point pb, point checkpt)
{
// Check if the point lies inside the diametrical sphere of this seg.
REAL v1[3], v2[3];
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.
}
} else {
return 1; // Inside! Encroached.
}
}
return 0;
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::checkseg4split(face *chkseg, point& encpt, int& qflag)
{
REAL ccent[3], len, r;
int i;
point forg = sorg(*chkseg);
point fdest = sdest(*chkseg);
// Initialize the return values.
encpt = NULL;
qflag = 0;
len = distance(forg, fdest);
r = 0.5 * len;
for (i = 0; i < 3; i++) {
ccent[i] = 0.5 * (forg[i] + fdest[i]);
}
// First check its quality.
if (checkconstraints && (areabound(*chkseg) > 0.0)) {
if (len > areabound(*chkseg)) {
qflag = 1;
return 1;
}
}
if (b->fixedvolume) {
if ((len * len * len) > b->maxvolume) {
qflag = 1;
return 1;
}
}
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;
}
}
// 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;
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;
}
}
} else {
encpt = eapex;
break;
}
}
}
fnextself(spintet);
if (spintet.tet == searchtet.tet) break;
} // while (1)
if (encpt != NULL) {
return 1;
}
return 0; // No need to 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. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::splitsegment(face *splitseg, point encpt, REAL rrp,
point encpt1, point encpt2, int qflag,
int chkencflag)
{
point pa = sorg(*splitseg);
point pb = sdest(*splitseg);
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;
}
} else {
smrrv = rrv;
}
}
if (smrrv > 0) {
if ((fabs(smrrv - len) / len) < b->epsilon) smrrv = len;
if (len < smrrv) {
return 0;
}
}
}
}
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.
}
spinsh = parentsh;
while (1) {
if (areabound(spinsh) == 0) break;
spivotself(spinsh);
if (spinsh.sh == parentsh.sh) break;
}
if (areabound(spinsh) == 0) {
// All facets at this seg are not allowed to be split.
return 0; // Do not split it.
}
}
} else {
return 0; // Do not split this segment.
}
} // if (b->nobisect)
triface searchtet;
face searchsh;
point newpt;
insertvertexflags ivf;
makepoint(&newpt, FREESEGVERTEX);
getsteinerptonsegment(splitseg, encpt, newpt);
// 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.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.
if (b->metric) {
ivf.rejflag |= 4; // Do check encroachment of protecting balls.
}
ivf.chkencflag = chkencflag;
ivf.sloc = (int) INSTAR; // ivf.iloc;
ivf.sbowywat = 3; // ivf.bowywat; // Surface mesh options.
ivf.splitbdflag = 1;
ivf.respectbdflag = 1;
ivf.assignmeshsize = b->metric;
ivf.smlenflag = useinsertradius;
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'.
}
}
}
setpointinsradius(newpt, rv);
}
if (flipstack != NULL) {
flipconstraints fc;
fc.chkencflag = chkencflag;
fc.enqflag = 2;
lawsonflip3d(&fc);
unflipqueue->restart();
}
return 1;
} else {
// Point is not inserted.
pointdealloc(newpt);
return 0;
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// repairencsegs() Repair encroached (sub) segments. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::repairencsegs(int chkencflag)
{
face *bface;
point encpt = NULL;
int qflag = 0;
// 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) {
// 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);
if (checkseg4split(bface, encpt, qflag)) {
splitsegment(bface, encpt, 0, NULL, NULL, qflag, chkencflag);
}
}
}
// Remove this entry from list.
bface->shver = -1; // Signal it as a deleted element.
badsubsegs->dealloc((void *) bface);
}
bface = (face *) badsubsegs->traverse();
}
}
if (badsubsegs->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);
}
}
}
bface = (face *) badsubsegs->traverse();
}
badsubsegs->restart();
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// enqueuesubface() Queue a subface or a subsegment for encroachment chk. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::enqueuesubface(memorypool *pool, face *chkface)
{
if (!smarktest2ed(*chkface)) {
smarktest2(*chkface); // Only queue it once.
face *queface = (face *) pool->alloc();
*queface = *chkface;
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// checkfac4encroach() Check if a subface is encroached by a point. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::checkfac4encroach(point pa, point pb, point pc, point checkpt,
REAL* cent, REAL* r)
{
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.
}
}
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);
// 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)
area = 0.5 * sqrt(dot(A[2], A[2])); // The area of [a,b,c].
// 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;
// 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);
}
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;
}
}
if (b->fixedvolume) {
if ((area * sqrt(area)) > b->maxvolume) {
qflag = 1;
return 1;
}
}
if (b->varvolume) {
triface adjtet;
REAL volbnd;
int t1ver;
stpivot(*chkfac, adjtet);
if (!ishulltet(adjtet)) {
volbnd = volumebound(adjtet.tet);
if ((volbnd > 0) && (area * sqrt(area)) > volbnd) {
qflag = 1;
return 1;
}
}
fsymself(adjtet);
if (!ishulltet(adjtet)) {
volbnd = volumebound(adjtet.tet);
if ((volbnd > 0) && (area * sqrt(area)) > volbnd) {
qflag = 1;
return 1;
}
}
}
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;
}
}
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);
} else {
if (len < smlen) {
smlen = len;
encpt = oppo(searchtet);
}
}
//return 1;
}
}
sesymself(*chkfac);
}
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'. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::splitsubface(face *splitfac, point encpt, point encpt1,
int qflag, REAL *ccent, int chkencflag)
{
point pa = sorg(*splitfac);
point pb = sdest(*splitfac);
point pc = sapex(*splitfac);
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;
}
} // if (b->nobisect)
face searchsh;
insertvertexflags ivf;
point newpt;
REAL rv = 0., rp; // Insertion radius of newpt.
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];
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.
}
}
}
}
} // if (useinsertradius)
// 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))) {
pointdealloc(newpt);
return 0;
}
triface searchtet;
face *paryseg;
int splitflag;
// 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.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.sloc = (int) INSTAR; // ivf.iloc;
ivf.sbowywat = 3; // ivf.bowywat;
ivf.splitbdflag = 1;
ivf.validflag = 1;
ivf.respectbdflag = 1;
ivf.assignmeshsize = b->metric;
ivf.refineflag = 2;
ivf.refinesh = searchsh;
ivf.smlenflag = useinsertradius; // Update the insertion radius.
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'.
}
}
}
setpointinsradius(newpt, rv);
} // if (useinsertradius)
if (flipstack != NULL) {
flipconstraints fc;
fc.chkencflag = chkencflag;
fc.enqflag = 2;
lawsonflip3d(&fc);
unflipqueue->restart();
}
return 1;
} else {
// Point was not inserted.
pointdealloc(newpt);
if (ivf.iloc == (int) ENCSEGMENT) {
// 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;
}
}
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;
} else {
return 0;
}
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// repairencfacs() Repair encroached subfaces. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::repairencfacs(int chkencflag)
{
face *bface;
point encpt = NULL;
int qflag = 0;
REAL ccent[3];
// 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) {
// 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);
if (checkfac4split(bface, encpt, qflag, ccent)) {
splitsubface(bface, encpt, NULL, qflag, ccent, chkencflag);
}
}
}
bface->shver = -1; // Signal it as a deleted element.
badsubfacs->dealloc((void *) bface); // Remove this entry from list.
}
bface = (face *) badsubfacs->traverse();
}
}
if (badsubfacs->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);
}
}
}
bface = (face *) badsubfacs->traverse();
}
badsubfacs->restart();
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// enqueuetetrahedron() Queue a tetrahedron for quality check. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::enqueuetetrahedron(triface *chktet)
{
if (!marktest2ed(*chktet)) {
marktest2(*chktet); // Only queue it once.
triface *quetet = (triface *) badtetrahedrons->alloc();
*quetet = *chktet;
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// checktet4split() Check if the tet needs to be split. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::checktet4split(triface *chktet, int &qflag, REAL *ccent)
{
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 A[4][4], rhs[4], D;
int indx[4];
int i, j;
if (b->convex) { // -c
// Skip this tet if it lies in the exterior.
if (elemattribute(chktet->tet, numelemattrib - 1) == -1.0) {
return 0;
}
}
qflag = 0;
pd = (point) chktet->tet[7];
if (pd == dummypoint) {
return 0; // Do not split a hull tet.
}
pa = (point) chktet->tet[4];
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;
}
// 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;
if (b->fixedvolume) {
if (vol > b->maxvolume) {
qflag = 1;
}
}
if (!qflag && b->varvolume) {
volbnd = volumebound(chktet->tet);
if ((volbnd > 0.0) && (vol > volbnd)) {
qflag = 1;
}
}
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;
}
}
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));
// 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;
}
}
}
}
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;
}
}
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;
}
}
} // if (useinsertradius)
// 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;
if (D > b->minratio) {
// A bad radius-edge ratio.
return 1;
}
}
// Check the minimum dihedral angle. Set by -qq#.
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;
N[j][j] = 1.0; // Positive means the inside direction
lu_solve(A, 3, indx, N[j], 0);
}
for (i = 0; i < 3; i++) N[3][i] = - N[0][i] - N[1][i] - N[2][i];
// 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];
//}
}
// Calculate the six dihedral angles.
cosd[0] = -dot(N[0], N[1]); // Edge cd, bd, bc.
cosd[1] = -dot(N[0], N[2]);
cosd[2] = -dot(N[0], N[3]);
cosd[3] = -dot(N[1], N[2]); // Edge ad, ac
cosd[4] = -dot(N[1], N[3]);
cosd[5] = -dot(N[2], N[3]); // Edge ab
// Get the smallest dihedral angle.
//maxcosd = mincosd = cosd[0];
maxcosd = cosd[0];
for (i = 1; i < 6; i++) {
//if (cosd[i] > maxcosd) maxcosd = cosd[i];
maxcosd = (cosd[i] > maxcosd ? cosd[i] : maxcosd);
//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 0;
}
///////////////////////////////////////////////////////////////////////////////
// //
// splittetrahedron() Split a tetrahedron. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::splittetrahedron(triface* splittet, int qflag, REAL *ccent,
int chkencflag)
{
triface searchtet;
face *paryseg;
point newpt;
badface *bface;
insertvertexflags ivf;
int splitflag;
int i;
REAL rv = 0.; // Insertion radius of 'newpt'.
makepoint(&newpt, FREEVOLVERTEX);
for (i = 0; i < 3; i++) newpt[i] = ccent[i];
if (useinsertradius) {
rv = distance(newpt, org(*splittet));
setpointinsradius(newpt, rv);
}
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.
}
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;
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();
}
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;
}
}
} // 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;
}
}
} // 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);
}
}
}
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);
}
}
}
bface = (triface *) badtetrahedrons->traverse();
}
// Clear the pool.
badtetrahedrons->restart();
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// delaunayrefinement() Refine the mesh by Delaunay refinement. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::delaunayrefinement()
{
triface checktet;
face checksh;
face checkseg;
long steinercount;
int chkencflag;
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 Edge length = %g.\n", b->minedgelength);
}
steinerleft = b->steinerleft; // Upperbound of # Steiner points (by -S#).
if (steinerleft > 0) {
// Check if we've already used up the given number of Steiner points.
steinercount = st_segref_count + st_facref_count + st_volref_count;
if (steinercount < steinerleft) {
steinerleft -= steinercount;
} else {
if (!b->quiet) {
printf("\nWarning: ");
printf("The desired number of Steiner points (%d) has reached.\n\n",
b->steinerleft);
}
return; // No more Steiner points.
}
}
if (useinsertradius) {
if ((b->plc && b->nobisect) || b->refine) { // '-pY' or '-r' option.
makesegmentendpointsmap();
}
makefacetverticesmap();
}
encseglist = new arraypool(sizeof(face), 8);
encshlist = new arraypool(sizeof(badface), 8);
//if (!b->nobisect) { // if no '-Y' option
if (!b->nobisect || checkconstraints) {
if (b->verbose) {
printf(" Splitting encroached subsegments.\n");
}
chkencflag = 1; // Only check encroaching subsegments.
steinercount = points->items;
// Initialize the pool of encroached subsegments.
badsubsegs = new memorypool(sizeof(face), b->shellfaceperblock,
sizeof(void *), 0);
// Add all segments into the pool.
subsegs->traversalinit();
checkseg.sh = shellfacetraverse(subsegs);
while (checkseg.sh != (shellface *) NULL) {
enqueuesubface(badsubsegs, &checkseg);
checkseg.sh = shellfacetraverse(subsegs);
}
// Split all encroached segments.
repairencsegs(chkencflag);
if (b->verbose) {
printf(" Added %ld Steiner points.\n", points->items - steinercount);
}
if (b->reflevel > 1) { // '-D2' option
if (b->verbose) {
printf(" Splitting encroached subfaces.\n");
}
chkencflag = 2; // Only check encroaching subfaces.
steinercount = points->items;
bak_segref_count = st_segref_count;
bak_facref_count = st_facref_count;
// Initialize the pool of encroached subfaces.
badsubfacs = new memorypool(sizeof(face), b->shellfaceperblock,
sizeof(void *), 0);
// Add all subfaces into the pool.
subfaces->traversalinit();
checksh.sh = shellfacetraverse(subfaces);
while (checksh.sh != (shellface *) NULL) {
enqueuesubface(badsubfacs, &checksh);
checksh.sh = shellfacetraverse(subfaces);
}
// Split all encroached subfaces.
repairencfacs(chkencflag);
if (b->verbose) {
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 (!b->nobisect)
if (b->reflevel > 2) { // '-D3' option (The default option)
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);
// 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.
tetrahedrons->traversalinit();
checktet.tet = tetrahedrontraverse();
while (checktet.tet != NULL) {
enqueuetetrahedron(&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)
if (b->verbose) {
if (flip23count + flip32count + flip44count > bak_flipcount) {
printf(" Performed %ld flips.\n", flip23count + flip32count +
flip44count - bak_flipcount);
}
}
if (steinerleft == 0) {
if (!b->quiet) {
printf("\nWarnning: ");
printf("The desired number of Steiner points (%d) is reached.\n\n",
b->steinerleft);
}
}
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;
}
}
//// ////
//// ////
//// refine_cxx ///////////////////////////////////////////////////////////////
//// optimize_cxx /////////////////////////////////////////////////////////////
//// ////
//// ////
///////////////////////////////////////////////////////////////////////////////
// //
// lawsonflip3d() A three-dimensional Lawson's algorithm. //
// //
///////////////////////////////////////////////////////////////////////////////
long tetgenmesh::lawsonflip3d(flipconstraints *fc)
{
triface fliptets[5], neightet, hulltet;
face checksh, casingout;
badface *popface, *bface;
point pd, pe, *pts;
REAL sign, ori;
long flipcount, totalcount = 0l;
long sliver_peels = 0l;
int t1ver;
int i;
while (1) {
if (b->verbose > 2) {
printf(" Lawson flip %ld faces.\n", flippool->items);
}
flipcount = 0l;
while (flipstack != (badface *) NULL) {
// 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);
// 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;
unmarkface(fliptets[0]);
if (ishulltet(fliptets[0])) continue;
fsym(fliptets[0], fliptets[1]);
if (ishulltet(fliptets[1])) {
if (nonconvex) {
// Check if 'fliptets[0]' it is a hull sliver.
tspivot(fliptets[0], checksh);
for (i = 0; i < 3; i++) {
if (!isshsubseg(checksh)) {
spivot(checksh, casingout);
//assert(casingout.sh != NULL);
if (sorg(checksh) != sdest(casingout)) sesymself(casingout);
stpivot(casingout, neightet);
if (neightet.tet == fliptets[0].tet) {
// Found a hull sliver 'neightet'. Let it be [e,d,a,b], where
// [e,d,a] and [d,e,b] are hull faces.
edestoppo(neightet, hulltet); // [a,b,e,d]
fsymself(hulltet); // [b,a,e,#]
if (oppo(hulltet) == dummypoint) {
pe = org(neightet);
if ((pointtype(pe) == FREEFACETVERTEX) ||
(pointtype(pe) == FREESEGVERTEX)) {
removevertexbyflips(pe);
}
} else {
eorgoppo(neightet, hulltet); // [b,a,d,e]
fsymself(hulltet); // [a,b,d,#]
if (oppo(hulltet) == dummypoint) {
pd = dest(neightet);
if ((pointtype(pd) == FREEFACETVERTEX) ||
(pointtype(pd) == FREESEGVERTEX)) {
removevertexbyflips(pd);
}
} 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.
}
}
}
break;
} // if (neightet.tet == fliptets[0].tet)
} // if (!isshsubseg(checksh))
senextself(checksh);
} // i
} // if (nonconvex)
continue;
}
if (checksubfaceflag) {
// Do not flip if it is a subface.
if (issubface(fliptets[0])) continue;
}
// Test whether the face is locally Delaunay or not.
pts = (point *) fliptets[1].tet;
sign = insphere_s(pts[4], pts[5], pts[6], pts[7], oppo(fliptets[0]));
if (sign < 0) {
// A non-Delaunay face. Try to flip it.
pd = oppo(fliptets[0]);
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++;
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.
}
continue;
} 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].
if (checksubsegflag) {
// Do not flip if it is a segment.
if (issubseg(fliptets[0])) continue;
}
// 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 (fliptets[3].tet == fliptets[0].tet) {
// A 3-to-2 flip is found. (No hull tet.)
flip32(fliptets, 0, fc);
flipcount++;
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.
}
continue;
} else {
// 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) {
// 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++;
flip23count--;
flip32count--;
flip44count++;
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.
}
continue;
} // if (ori == 0)
}
}
} // 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)
if (b->verbose > 2) {
if (flipcount > 0) {
printf(" Performed %ld flips.\n", flipcount);
}
}
// Accumulate the counter of flips.
totalcount += flipcount;
assert(flippool->items == 0l);
// Return if no unflippable faces left.
if (unflipqueue->objects == 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)) {
flippush(flipstack, &(bface->tt));
}
}
unflipqueue->restart();
} // while (1)
if (b->verbose > 2) {
if (totalcount > 0) {
printf(" Performed %ld flips.\n", totalcount);
}
if (sliver_peels > 0) {
printf(" Removed %ld hull slivers.\n", sliver_peels);
}
if (unflipqueue->objects > 0l) {
printf(" %ld unflippable edges remained.\n", unflipqueue->objects);
}
}
return totalcount + sliver_peels;
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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");
}
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);
}
}
ppt = (point *) &(tetloop.tet[4]);
tetprism_vol_sum += tetprismvol(ppt[0], ppt[1], ppt[2], ppt[3]);
tetloop.tet = tetrahedrontraverse();
}
// 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 > 1) {
printf(" Recover Delaunay [Lawson] : %ld\n", flippool->items);
}
// First only use the basic Lawson's flip.
fc.remove_ndelaunay_edge = 1;
fc.enqflag = 2;
lawsonflip3d(&fc);
if (b->verbose > 1) {
printf(" obj (after Lawson) = %.17g\n", tetprism_vol_sum);
}
if (unflipqueue->objects == 0l) {
return; // The mesh is Delaunay.
}
fc.unflip = 1; // Unflip if the edge is not flipped.
fc.collectnewtets = 1; // new tets are returned in 'cavetetlist'.
fc.enqflag = 0;
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
flipqueue = new arraypool(sizeof(badface), 10);
nextflipqueue = new arraypool(sizeof(badface), 10);
// Swap the two flip queues.
swapqueue = flipqueue;
flipqueue = unflipqueue;
unflipqueue = swapqueue;
while (flipqueue->objects > 0l) {
if (b->verbose > 1) {
printf(" Recover Delaunay [level = %2d] #: %ld.\n",
autofliplinklevel, flipqueue->objects);
}
for (i = 0; i < flipqueue->objects; i++) {
bface = (badface *) fastlookup(flipqueue, 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.
// Queue new faces for flips.
for (j = 0; j < cavetetlist->objects; j++) {
parytet = (triface *) fastlookup(cavetetlist, j);
// A queued new tet may be dead.
if (!isdeadtet(*parytet)) {
for (parytet->ver = 0; parytet->ver < 4; parytet->ver++) {
// Avoid queue a face twice.
decode(parytet->tet[parytet->ver], neightet);
if (!facemarked(neightet)) {
flippush(flipstack, parytet);
}
} // parytet->ver
}
} // 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;
}
}
} // i
if (b->verbose > 1) {
printf(" obj (after level %d) = %.17g.\n", autofliplinklevel,
tetprism_vol_sum);
}
flipqueue->restart();
// Swap the two flip queues.
swapqueue = flipqueue;
flipqueue = nextflipqueue;
nextflipqueue = swapqueue;
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;
}
} // while (flipqueue->objects > 0l)
if (flipqueue->objects > 0l) {
if (b->verbose > 1) {
printf(" %ld non-Delaunay edges remained.\n", flipqueue->objects);
}
}
if (b->verbose) {
printf(" Final obj = %.17g\n", tetprism_vol_sum);
}
b->flipstarsize = bakmaxflipstarsize;
delete flipqueue;
delete nextflipqueue;
}
///////////////////////////////////////////////////////////////////////////////
// //
// gettetrahedron() Get a tetrahedron which have the given vertices. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::gettetrahedron(point pa, point pb, point pc, point pd,
triface *searchtet)
{
triface spintet;
int t1ver;
if (getedge(pa, pb, searchtet)) {
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 1;
} else {
fsymself(*searchtet);
if (oppo(*searchtet) == pd) {
return 1;
}
}
}
}
return 0;
}
///////////////////////////////////////////////////////////////////////////////
// //
// improvequalitybyflips() Improve the mesh quality by flips. //
// //
///////////////////////////////////////////////////////////////////////////////
long tetgenmesh::improvequalitybyflips()
{
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;
//assert(unflipqueue->objects > 0l);
flipqueue = new arraypool(sizeof(badface), 10);
nextflipqueue = new arraypool(sizeof(badface), 10);
// Backup flip edge options.
int bakautofliplinklevel = autofliplinklevel;
int bakfliplinklevel = b->fliplinklevel;
int bakmaxflipstarsize = b->flipstarsize;
// Set flip edge options.
autofliplinklevel = 1;
b->fliplinklevel = -1;
b->flipstarsize = 10; // b->optmaxflipstarsize;
fc.remove_large_angle = 1;
fc.unflip = 1;
fc.collectnewtets = 1;
fc.checkflipeligibility = 1;
totalremcount = 0l;
// Swap the two flip queues.
swapqueue = flipqueue;
flipqueue = unflipqueue;
unflipqueue = swapqueue;
while (flipqueue->objects > 0l) {
remcount = 0l;
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
flipqueue->restart();
// Swap the two flip queues.
swapqueue = flipqueue;
flipqueue = nextflipqueue;
nextflipqueue = swapqueue;
} // while (flipqueues->objects > 0)
if (b->verbose > 1) {
printf(" Removed %ld bad tets.\n", remcount);
}
totalremcount += remcount;
if (unflipqueue->objects > 0l) {
//if (autofliplinklevel >= b->optmaxfliplevel) {
if (autofliplinklevel >= b->optlevel) {
break;
}
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)
// Restore original flip edge options.
autofliplinklevel = bakautofliplinklevel;
b->fliplinklevel = bakfliplinklevel;
b->flipstarsize = bakmaxflipstarsize;
delete flipqueue;
delete nextflipqueue;
return totalremcount;
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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::smoothpoint(point smtpt, arraypool *linkfacelist, int ccw,
optparameters *opm)
{
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;
// Decide the number of moving directions.
numdirs = (int) linkfacelist->objects;
if (numdirs > opm->numofsearchdirs) {
numdirs = opm->numofsearchdirs; // Maximum search directions.
}
// Set the initial value.
if (!opm->max_min_volume) {
assert(opm->initval >= 0.0);
}
opm->imprval = opm->initval;
iter = 0;
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->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 (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->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 (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;
}
///////////////////////////////////////////////////////////////////////////////
// //
// improvequalitysmoothing() Improve mesh quality by smoothing. //
// //
///////////////////////////////////////////////////////////////////////////////
long tetgenmesh::improvequalitybysmoothing(optparameters *opm)
{
arraypool *flipqueue, *swapqueue;
triface *parytet;
badface *bface, *parybface;
point *ppt;
long totalsmtcount, smtcount;
int smtflag;
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;
totalsmtcount = 0l;
iter = 0;
while (flipqueue->objects > 0l) {
smtcount = 0l;
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 (b->verbose > 1) {
printf(" Smooth %ld points.\n", smtcount);
}
totalsmtcount += smtcount;
if (smtcount == 0l) {
// No point has been smoothed.
break;
} else {
iter++;
if (iter == 2) { //if (iter >= b->optpasses) {
break;
}
}
// Swap the two flip queues.
swapqueue = flipqueue;
flipqueue = unflipqueue;
unflipqueue = swapqueue;
} // while
delete flipqueue;
return totalsmtcount;
}
///////////////////////////////////////////////////////////////////////////////
// //
// splitsliver() Split a sliver. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::splitsliver(triface *slitet, REAL cosd, int chkencflag)
{
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;
// '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].
// Do not split a segment.
if (issubseg(searchtet)) {
return 0;
}
// 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;
}
if (ishulltet(spintet)) {
return 0; // It is a hull edge.
}
assert(n >= 3);
// Get all tets at edge [a,b].
abtets = new triface[n];
spintet = searchtet;
for (i = 0; i < n; i++) {
abtets[i] = spintet;
fnextself(spintet);
}
// 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;
}
// 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]);
}
// 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);
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);
}
} // if (success)
cavetetlist->restart();
if (!success) {
delete [] abtets;
return 0;
}
// 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];
}
searchtet = abtets[0]; // No need point location.
if (b->metric) {
locate(steinerpt, &searchtet); // For size interpolation.
}
delete [] abtets;
ivf.iloc = (int) INSTAR;
ivf.chkencflag = chkencflag;
ivf.assignmeshsize = b->metric;
if (insertpoint(steinerpt, &searchtet, NULL, NULL, &ivf)) {
// The vertex has been inserted.
st_volref_count++;
if (steinerleft > 0) steinerleft--;
return 1;
} else {
// The Steiner point is too close to an existing vertex. Reject it.
pointdealloc(steinerpt);
return 0;
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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)) {
sptcount = 0l;
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();
}
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
flipqueue->restart();
if (b->verbose > 1) {
printf(" Split %ld tets.\n", sptcount);
}
totalsptcount += sptcount;
if (sptcount == 0l) {
// No point has been smoothed.
break;
} else {
iter++;
if (iter == 2) { //if (iter >= b->optpasses) {
break;
}
}
// Swap the two flip queues.
swapqueue = flipqueue;
flipqueue = unflipqueue;
unflipqueue = swapqueue;
} // while
delete flipqueue;
return totalsptcount;
}
///////////////////////////////////////////////////////////////////////////////
// //
// optimizemesh() Optimize mesh for specified objective functions. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::optimizemesh()
{
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->quiet) {
printf("Optimizing 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);
}
totalsmtcount = totalsptcount = totalremcount = 0l;
cosmaxdihed = cos(b->optmaxdihedral / 180.0 * PI);
cossmtdihed = cos(b->optminsmtdihed / 180.0 * PI);
cosslidihed = cos(b->optminslidihed / 180.0 * PI);
int attrnum = numelemattrib - 1;
// Put all bad tetrahedra into array.
tetrahedrons->traversalinit();
checktet.tet = tetrahedrontraverse();
while (checktet.tet != NULL) {
if (b->convex) { // -c
// Skip this tet if it lies in the exterior.
if (elemattribute(checktet.tet, attrnum) == -1.0) {
checktet.tet = tetrahedrontraverse();
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];
}
}
checktet.tet = tetrahedrontraverse();
}
totalremcount = improvequalitybyflips();
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);
// 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;
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)
delete badtetrahedrons;
}
if (unflipqueue->objects > 0l) {
if (b->verbose > 1) {
printf(" %ld bad tets remained.\n", unflipqueue->objects);
}
unflipqueue->restart();
}
if (b->verbose) {
if (totalremcount > 0l) {
printf(" Removed %ld edges.\n", totalremcount);
}
if (totalsmtcount > 0l) {
printf(" Smoothed %ld points.\n", totalsmtcount);
}
if (totalsptcount > 0l) {
printf(" Split %ld slivers.\n", totalsptcount);
}
}
}
//// ////
//// ////
//// optimize_cxx /////////////////////////////////////////////////////////////
//// meshstat_cxx /////////////////////////////////////////////////////////////
//// ////
//// ////
///////////////////////////////////////////////////////////////////////////////
// //
// printfcomma() Print a (large) number with the 'thousands separator'. //
// //
// The following code was simply copied from "stackoverflow". //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::printfcomma(unsigned long n)
{
unsigned long n2 = 0;
int scale = 1;
while (n >= 1000) {
n2 = n2 + scale * (n % 1000);
n /= 1000;
scale *= 1000;
}
printf ("%ld", n);
while (scale != 1) {
scale /= 1000;
n = n2 / scale;
n2 = n2 % scale;
printf (",%03ld", 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. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::checkmesh(int topoflag)
{
triface tetloop, neightet, symtet;
point pa, pb, pc, pd;
REAL ori;
int horrors, i;
if (!b->quiet) {
printf(" Checking consistency of mesh...\n");
}
horrors = 0;
tetloop.ver = 0;
// Run through the list of tetrahedra, checking each one.
tetrahedrons->traversalinit();
tetloop.tet = alltetrahedrontraverse();
while (tetloop.tet != (tetrahedron *) NULL) {
// Check all four faces of the tetrahedron.
for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) {
pa = org(tetloop);
pb = dest(tetloop);
pc = apex(tetloop);
pd = oppo(tetloop);
if (tetloop.ver == 0) { // Only test for inversion once.
if (!ishulltet(tetloop)) { // Only do test if it is not a hull tet.
if (!topoflag) {
ori = orient3d(pa, pb, pc, pd);
if (ori >= 0.0) {
printf(" !! !! %s ", ori > 0.0 ? "Inverted" : "Degenerated");
printf(" (%d, %d, %d, %d) (ori = %.17g)\n", pointmark(pa),
pointmark(pb), pointmark(pc), pointmark(pd), ori);
horrors++;
}
}
}
if (infected(tetloop)) {
// This may be a bug. Report it.
printf(" !! (%d, %d, %d, %d) is infected.\n", pointmark(pa),
pointmark(pb), pointmark(pc), pointmark(pd));
horrors++;
}
if (marktested(tetloop)) {
// This may be a bug. Report it.
printf(" !! (%d, %d, %d, %d) is marked.\n", pointmark(pa),
pointmark(pb), pointmark(pc), pointmark(pd));
horrors++;
}
}
if (tetloop.tet[tetloop.ver] == NULL) {
printf(" !! !! No neighbor at face (%d, %d, %d).\n", pointmark(pa),
pointmark(pb), pointmark(pc));
horrors++;
} 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");
}
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)));
horrors++;
}
}
if (facemarked(tetloop)) {
// This may be a bug. Report it.
printf(" !! tetface (%d, %d, %d) %d is marked.\n", pointmark(pa),
pointmark(pb), pointmark(pc), pointmark(pd));
}
}
// Check the six edges of this tet.
for (i = 0; i < 6; i++) {
tetloop.ver = edge2ver[i];
if (edgemarked(tetloop)) {
// This may be a bug. Report it.
printf(" !! tetedge (%d, %d) %d, %d is marked.\n",
pointmark(org(tetloop)), pointmark(dest(tetloop)),
pointmark(apex(tetloop)), pointmark(oppo(tetloop)));
}
}
tetloop.tet = alltetrahedrontraverse();
}
if (horrors == 0) {
if (!b->quiet) {
printf(" In my studied opinion, the mesh appears to be consistent.\n");
}
} else {
printf(" !! !! !! !! %d %s witnessed.\n", horrors,
horrors > 1 ? "abnormity" : "abnormities");
}
return horrors;
}
///////////////////////////////////////////////////////////////////////////////
// //
// checkshells() Test the boundary mesh for topological consistency. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::checkshells()
{
triface neightet, symtet;
face shloop, spinsh, nextsh;
face checkseg;
point pa, pb;
int bakcount;
int horrors, i;
if (!b->quiet) {
printf(" Checking consistency of the mesh boundary...\n");
}
horrors = 0;
void **bakpathblock = subfaces->pathblock;
void *bakpathitem = subfaces->pathitem;
int bakpathitemsleft = subfaces->pathitemsleft;
int bakalignbytes = subfaces->alignbytes;
subfaces->traversalinit();
shloop.sh = shellfacetraverse(subfaces);
while (shloop.sh != NULL) {
shloop.shver = 0;
for (i = 0; i < 3; i++) {
// Check the face ring at this edge.
pa = sorg(shloop);
pb = sdest(shloop);
spinsh = shloop;
spivot(spinsh, nextsh);
bakcount = horrors;
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,
pointmark(sorg(spinsh)), pointmark(sdest(spinsh)),
pointmark(sapex(spinsh)));
printf(" Second: x%lx (DEAD)\n", (uintptr_t) nextsh.sh);
horrors++;
break;
}
// check if they have the same edge.
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,
pointmark(sorg(spinsh)), pointmark(sdest(spinsh)),
pointmark(sapex(spinsh)));
printf(" Scond: x%lx (%d, %d, %d).\n", (uintptr_t) nextsh.sh,
pointmark(sorg(nextsh)), pointmark(sdest(nextsh)),
pointmark(sapex(nextsh)));
horrors++;
break;
}
// 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,
pointmark(sorg(spinsh)), pointmark(sdest(spinsh)),
pointmark(sapex(spinsh)));
printf(" Scond: x%lx (%d, %d, %d).\n", (uintptr_t) nextsh.sh,
pointmark(sorg(nextsh)), pointmark(sdest(nextsh)),
pointmark(sapex(nextsh)));
horrors++;
break;
}
spinsh = nextsh;
spivot(spinsh, nextsh);
}
// Check subface-subseg bond.
sspivot(shloop, checkseg);
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,
pointmark(sorg(shloop)), pointmark(sdest(shloop)),
pointmark(sapex(shloop)));
printf(" Sub: x%lx (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,
pointmark(sorg(shloop)), pointmark(sdest(shloop)),
pointmark(sapex(shloop)));
printf(" Seg: x%lx (%d, %d).\n", (uintptr_t) checkseg.sh,
pointmark(sorg(checkseg)), pointmark(sdest(checkseg)));
horrors++;
}
}
}
if (horrors > bakcount) break; // An error detected.
senextself(shloop);
}
// Check tet-subface connection.
stpivot(shloop, neightet);
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,
pointmark(sorg(shloop)), pointmark(sdest(shloop)),
pointmark(sapex(shloop)));
printf(" Tet: x%lx (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,
pointmark(sorg(shloop)), pointmark(sdest(shloop)),
pointmark(sapex(shloop)));
printf(" Tet: x%lx (%d, %d, %d, %d).\n",
(uintptr_t) neightet.tet, pointmark(org(neightet)),
pointmark(dest(neightet)), pointmark(apex(neightet)),
pointmark(oppo(neightet)));
horrors++;
}
tspivot(neightet, spinsh);
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,
pointmark(sorg(spinsh)), pointmark(sdest(spinsh)),
pointmark(sapex(spinsh)));
printf(" Tet: x%lx (%d, %d, %d, %d).\n",
(uintptr_t) neightet.tet, pointmark(org(neightet)),
pointmark(dest(neightet)), pointmark(apex(neightet)),
pointmark(oppo(neightet)));
horrors++;
}
fsym(neightet, symtet);
tspivot(symtet, spinsh);
if (spinsh.sh != NULL) {
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,
pointmark(sorg(spinsh)), pointmark(sdest(spinsh)),
pointmark(sapex(spinsh)));
printf(" Tet: x%lx (%d, %d, %d, %d).\n",
(uintptr_t) symtet.tet, pointmark(org(symtet)),
pointmark(dest(symtet)), pointmark(apex(symtet)),
pointmark(oppo(symtet)));
horrors++;
}
} else {
printf(" Warning: Broken tet-sub-tet connection.\n");
}
}
}
if (sinfected(shloop)) {
// This may be a bug. report it.
printf(" !! A infected subface: (%d, %d, %d).\n",
pointmark(sorg(shloop)), pointmark(sdest(shloop)),
pointmark(sapex(shloop)));
}
if (smarktested(shloop)) {
// This may be a bug. report it.
printf(" !! A marked subface: (%d, %d, %d).\n", pointmark(sorg(shloop)),
pointmark(sdest(shloop)), pointmark(sapex(shloop)));
}
shloop.sh = shellfacetraverse(subfaces);
}
if (horrors == 0) {
if (!b->quiet) {
printf(" Mesh boundaries connected correctly.\n");
}
} else {
printf(" !! !! !! !! %d boundary connection viewed with horror.\n",
horrors);
}
subfaces->pathblock = bakpathblock;
subfaces->pathitem = bakpathitem;
subfaces->pathitemsleft = bakpathitemsleft;
subfaces->alignbytes = bakalignbytes;
return horrors;
}
///////////////////////////////////////////////////////////////////////////////
// //
// checksegments() Check the connections between tetrahedra and segments. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::checksegments()
{
triface tetloop, neightet, spintet;
shellface *segs;
face neighsh, spinsh, checksh;
face sseg, checkseg;
point pa, pb;
int miscount;
int t1ver;
int horrors, i;
if (!b->quiet) {
printf(" Checking tet->seg connections...\n");
}
horrors = 0;
tetrahedrons->traversalinit();
tetloop.tet = tetrahedrontraverse();
while (tetloop.tet != NULL) {
// Loop the six edges of the tet.
if (tetloop.tet[8] != NULL) {
segs = (shellface *) tetloop.tet[8];
for (i = 0; i < 6; i++) {
sdecode(segs[i], sseg);
if (sseg.sh != NULL) {
// Get the edge of the tet.
tetloop.ver = edge2ver[i];
// Check if they are the same edge.
pa = (point) sseg.sh[3];
pb = (point) sseg.sh[4];
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",
(uintptr_t) tetloop.tet, pointmark(org(tetloop)),
pointmark(dest(tetloop)), pointmark(apex(tetloop)),
pointmark(oppo(tetloop)), (uintptr_t) sseg.sh,
pointmark(pa), pointmark(pb));
horrors++;
} else {
// Loop all tets sharing at this edge.
neightet = tetloop;
do {
tsspivot1(neightet, checkseg);
if (checkseg.sh != sseg.sh) {
printf(" !! Wrong tet->seg connection.\n");
printf(" Tet: x%lx (%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,
pointmark(sorg(checkseg)),pointmark(sdest(checkseg)));
} else {
printf("Seg: NULL.\n");
}
horrors++;
}
fnextself(neightet);
} while (neightet.tet != tetloop.tet);
}
// Check the seg->tet pointer.
sstpivot1(sseg, neightet);
if (neightet.tet == NULL) {
printf(" !! Wrong seg->tet connection (A NULL tet).\n");
horrors++;
} else {
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",
(uintptr_t) neightet.tet, pointmark(org(neightet)),
pointmark(dest(neightet)), pointmark(apex(neightet)),
pointmark(oppo(neightet)), (uintptr_t) sseg.sh,
pointmark(pa), pointmark(pb));
horrors++;
}
}
}
}
}
// Loop the six edge of this tet.
neightet.tet = tetloop.tet;
for (i = 0; i < 6; i++) {
neightet.ver = edge2ver[i];
if (edgemarked(neightet)) {
// A possible bug. Report it.
printf(" !! A marked edge: (%d, %d, %d, %d) -- x%lx %d.\n",
pointmark(org(neightet)), pointmark(dest(neightet)),
pointmark(apex(neightet)), pointmark(oppo(neightet)),
(uintptr_t) neightet.tet, neightet.ver);
// Check if all tets at the edge are marked.
spintet = neightet;
while (1) {
fnextself(spintet);
if (!edgemarked(spintet)) {
printf(" !! !! An unmarked edge (%d, %d, %d, %d) -- x%lx %d.\n",
pointmark(org(spintet)), pointmark(dest(spintet)),
pointmark(apex(spintet)), pointmark(oppo(spintet)),
(uintptr_t) spintet.tet, spintet.ver);
horrors++;
}
if (spintet.tet == neightet.tet) break;
}
}
}
tetloop.tet = tetrahedrontraverse();
}
if (!b->quiet) {
printf(" Checking seg->tet connections...\n");
}
miscount = 0; // Count the number of unrecovered segments.
subsegs->traversalinit();
sseg.shver = 0;
sseg.sh = shellfacetraverse(subsegs);
while (sseg.sh != NULL) {
pa = sorg(sseg);
pb = sdest(sseg);
spivot(sseg, neighsh);
if (neighsh.sh != NULL) {
spinsh = neighsh;
while (1) {
// Check seg-subface bond.
if (((sorg(spinsh) == pa) && (sdest(spinsh) == pb)) ||
((sorg(spinsh) == pb) && (sdest(spinsh) == pa))) {
// Keep the same rotate direction.
//if (sorg(spinsh) != pa) {
// sesymself(spinsh);
// printf(" !! Wrong ori at subface (%d, %d, %d) -- x%lx %d\n",
// pointmark(sorg(spinsh)), pointmark(sdest(spinsh)),
// pointmark(sapex(spinsh)), (uintptr_t) spinsh.sh,
// spinsh.shver);
// horrors++;
//}
stpivot(spinsh, spintet);
if (spintet.tet != NULL) {
// Check if all tets at this segment.
while (1) {
tsspivot1(spintet, checkseg);
if (checkseg.sh == NULL) {
printf(" !! !! No seg at tet (%d, %d, %d, %d) -- x%lx %d\n",
pointmark(org(spintet)), pointmark(dest(spintet)),
pointmark(apex(spintet)), pointmark(oppo(spintet)),
(uintptr_t) spintet.tet, spintet.ver);
horrors++;
}
if (checkseg.sh != sseg.sh) {
printf(" !! !! Wrong seg (%d, %d) at tet (%d, %d, %d, %d)\n",
pointmark(sorg(checkseg)), pointmark(sdest(checkseg)),
pointmark(org(spintet)), pointmark(dest(spintet)),
pointmark(apex(spintet)), pointmark(oppo(spintet)));
horrors++;
}
fnextself(spintet);
// Stop at the next subface.
tspivot(spintet, checksh);
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);
horrors++;
break;
} // if pa, pb
spivotself(spinsh);
if (spinsh.sh == NULL) break; // A dangling segment.
if (spinsh.sh == neighsh.sh) break;
} // while (1)
} // if (neighsh.sh != NULL)
// Count the number of "un-recovered" segments.
sstpivot1(sseg, neightet);
if (neightet.tet == NULL) {
miscount++;
}
sseg.sh = shellfacetraverse(subsegs);
}
if (!b->quiet) {
printf(" Checking seg->seg connections...\n");
}
points->traversalinit();
pa = pointtraverse();
while (pa != NULL) {
if (pointtype(pa) == FREESEGVERTEX) {
// There should be two subsegments connected at 'pa'.
// Get a subsegment containing 'pa'.
sdecode(point2sh(pa), sseg);
if ((sseg.sh == NULL) || sseg.sh[3] == NULL) {
printf(" !! Dead point-to-seg pointer at point %d.\n",
pointmark(pa));
horrors++;
} else {
sseg.shver = 0;
if (sorg(sseg) != pa) {
if (sdest(sseg) != pa) {
printf(" !! Wrong point-to-seg pointer at point %d.\n",
pointmark(pa));
horrors++;
} else {
// Find the next subsegment at 'pa'.
senext(sseg, checkseg);
if ((checkseg.sh == NULL) || (checkseg.sh[3] == NULL)) {
printf(" !! Dead seg-seg connection at point %d.\n",
pointmark(pa));
horrors++;
} else {
spivotself(checkseg);
checkseg.shver = 0;
if (sorg(checkseg) != pa) {
printf(" !! Wrong seg-seg connection at point %d.\n",
pointmark(pa));
horrors++;
}
}
}
} else {
// Find the previous subsegment at 'pa'.
senext2(sseg, checkseg);
if ((checkseg.sh == NULL) || (checkseg.sh[3] == NULL)) {
printf(" !! Dead seg-seg connection at point %d.\n",
pointmark(pa));
horrors++;
} else {
spivotself(checkseg);
checkseg.shver = 0;
if (sdest(checkseg) != pa) {
printf(" !! Wrong seg-seg connection at point %d.\n",
pointmark(pa));
horrors++;
}
}
}
}
}
pa = pointtraverse();
}
if (horrors == 0) {
printf(" Segments are connected properly.\n");
} else {
printf(" !! !! !! !! Found %d missing connections.\n", horrors);
}
if (miscount > 0) {
printf(" !! !! Found %d missing segments.\n", miscount);
}
return horrors;
}
///////////////////////////////////////////////////////////////////////////////
// //
// checkdelaunay() Ensure that the mesh is (constrained) Delaunay. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::checkdelaunay()
{
triface tetloop;
triface symtet;
face checksh;
point pa, pb, pc, pd, pe;
REAL sign;
int ndcount; // Count the non-locally Delaunay faces.
int horrors;
if (!b->quiet) {
printf(" Checking Delaunay property of the mesh...\n");
}
ndcount = 0;
horrors = 0;
tetloop.ver = 0;
// Run through the list of triangles, checking each one.
tetrahedrons->traversalinit();
tetloop.tet = tetrahedrontraverse();
while (tetloop.tet != (tetrahedron *) NULL) {
// Check all four faces of the tetrahedron.
for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) {
fsym(tetloop, symtet);
// Only do test if its adjoining tet is not a hull tet or its pointer
// is larger (to ensure that each pair isn't tested twice).
if (((point) symtet.tet[7] != dummypoint)&&(tetloop.tet < symtet.tet)) {
pa = org(tetloop);
pb = dest(tetloop);
pc = apex(tetloop);
pd = oppo(tetloop);
pe = oppo(symtet);
sign = insphere_s(pa, pb, pc, pd, pe);
if (sign < 0.0) {
ndcount++;
if (checksubfaceflag) {
tspivot(tetloop, checksh);
}
if (checksh.sh == NULL) {
printf(" !! Non-locally Delaunay (%d, %d, %d) - %d, %d\n",
pointmark(pa), pointmark(pb), pointmark(pc), pointmark(pd),
pointmark(pe));
horrors++;
}
}
}
}
tetloop.tet = tetrahedrontraverse();
}
if (horrors == 0) {
if (!b->quiet) {
if (ndcount > 0) {
printf(" The mesh is constrained Delaunay.\n");
} else {
printf(" The mesh is Delaunay.\n");
}
}
} else {
printf(" !! !! !! !! Found %d non-Delaunay faces.\n", horrors);
}
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)
{
triface tetloop;
triface symtet;
face checksh;
point p[5];
REAL sign;
int ndcount; // Count the non-locally Delaunay faces.
int horrors;
if (!b->quiet) {
printf(" Checking %s %s property of the mesh...\n",
(type & 2) == 0 ? "Delaunay" : "regular",
(type & 1) == 0 ? " " : "(s)");
}
// 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].
ndcount = 0;
horrors = 0;
tetloop.ver = 0;
// Run through the list of triangles, checking each one.
tetrahedrons->traversalinit();
tetloop.tet = tetrahedrontraverse();
while (tetloop.tet != (tetrahedron *) NULL) {
// Check all four faces of the tetrahedron.
for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) {
fsym(tetloop, symtet);
// Only do test if its adjoining tet is not a hull tet or its pointer
// is larger (to ensure that each pair isn't tested twice).
if (((point) symtet.tet[7] != dummypoint)&&(tetloop.tet < symtet.tet)) {
p[0] = org(tetloop); // pa
p[1] = dest(tetloop); // pb
p[2] = apex(tetloop); // pc
p[3] = oppo(tetloop); // pd
p[4] = oppo(symtet); // pe
if (type == 0) {
sign = insphere(p[1], p[0], p[2], p[3], p[4]);
} else if (type == 1) {
sign = insphere_s(p[1], p[0], p[2], p[3], p[4]);
} else if (type == 2) {
sign = orient4d(p[1], p[0], p[2], p[3], p[4],
p[1][3], p[0][3], p[2][3], p[3][3], p[4][3]);
} else { // type == 3
sign = orient4d_s(p[1], p[0], p[2], p[3], p[4],
p[1][3], p[0][3], p[2][3], p[3][3], p[4][3]);
}
if (sign > 0.0) {
ndcount++;
if (checksubfaceflag) {
tspivot(tetloop, checksh);
}
if (checksh.sh == NULL) {
printf(" !! Non-locally %s (%d, %d, %d) - %d, %d\n",
(type & 2) == 0 ? "Delaunay" : "regular",
pointmark(p[0]), pointmark(p[1]), pointmark(p[2]),
pointmark(p[3]), pointmark(p[4]));
horrors++;
}
}
}
}
tetloop.tet = tetrahedrontraverse();
}
if (horrors == 0) {
if (!b->quiet) {
if (ndcount > 0) {
printf(" The mesh is constrained %s.\n",
(type & 2) == 0 ? "Delaunay" : "regular");
} else {
printf(" The mesh is %s.\n", (type & 2) == 0 ? "Delaunay" : "regular");
}
}
} else {
printf(" !! !! !! !! Found %d non-%s faces.\n", horrors,
(type & 2) == 0 ? "Delaunay" : "regular");
}
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. //
// //
///////////////////////////////////////////////////////////////////////////////
int tetgenmesh::checkconforming(int flag)
{
triface searchtet, neightet, spintet;
face shloop;
face segloop;
point eorg, edest, eapex, pa, pb, pc;
REAL cent[3], radius, dist, diff, rd, len;
bool enq;
int encsubsegs, encsubfaces;
int t1ver;
int i;
REAL A[4][4], rhs[4], D;
int indx[4];
REAL elen[3];
encsubsegs = 0;
if (flag & 1) {
if (!b->quiet) {
printf(" Checking conforming property of segments...\n");
}
encsubsegs = 0;
// Run through the list of subsegments, check each one.
subsegs->traversalinit();
segloop.sh = shellfacetraverse(subsegs);
while (segloop.sh != (shellface *) NULL) {
eorg = (point) segloop.sh[3];
edest = (point) segloop.sh[4];
radius = 0.5 * distance(eorg, edest);
for (i = 0; i < 3; i++) cent[i] = 0.5 * (eorg[i] + edest[i]);
enq = false;
sstpivot1(segloop, neightet);
if (neightet.tet != NULL) {
spintet = neightet;
while (1) {
eapex= apex(spintet);
if (eapex != dummypoint) {
dist = distance(eapex, cent);
diff = dist - radius;
if (fabs(diff) / radius <= b->epsilon) diff = 0.0; // Rounding.
if (diff < 0) {
enq = true; break;
}
}
fnextself(spintet);
if (spintet.tet == neightet.tet) break;
}
}
if (enq) {
printf(" !! !! Non-conforming segment: (%d, %d)\n",
pointmark(eorg), pointmark(edest));
encsubsegs++;
}
segloop.sh = shellfacetraverse(subsegs);
}
if (encsubsegs == 0) {
if (!b->quiet) {
printf(" The segments are conforming Delaunay.\n");
}
} else {
printf(" !! !! %d subsegments are non-conforming.\n", encsubsegs);
}
} // if (flag & 1)
encsubfaces = 0;
if (flag & 2) {
if (!b->quiet) {
printf(" Checking conforming property of subfaces...\n");
}
// Run through the list of subfaces, check each one.
subfaces->traversalinit();
shloop.sh = shellfacetraverse(subfaces);
while (shloop.sh != (shellface *) NULL) {
pa = (point) shloop.sh[3];
pb = (point) shloop.sh[4];
pc = (point) shloop.sh[5];
// 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)
// Compute the right hand side vector b (3x1).
elen[0] = dot(A[0], A[0]);
elen[1] = dot(A[1], A[1]);
rhs[0] = 0.5 * elen[0];
rhs[1] = 0.5 * elen[1];
rhs[2] = 0.0;
if (lu_decmp(A, 3, indx, &D, 0)) {
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]);
// Check if this subface is encroached.
for (i = 0; i < 2; i++) {
stpivot(shloop, searchtet);
if (!ishulltet(searchtet)) {
len = distance(oppo(searchtet), cent);
if ((fabs(len - rd) / rd) < b->epsilon) len = rd; // Rounding.
if (len < rd) {
printf(" !! !! Non-conforming subface: (%d, %d, %d)\n",
pointmark(pa), pointmark(pb), pointmark(pc));
encsubfaces++;
enq = true; break;
}
}
sesymself(shloop);
}
}
shloop.sh = shellfacetraverse(subfaces);
}
if (encsubfaces == 0) {
if (!b->quiet) {
printf(" The subfaces are conforming Delaunay.\n");
}
} else {
printf(" !! !! %d subfaces are non-conforming.\n", encsubfaces);
}
} // if (flag & 2)
return encsubsegs + encsubfaces;
}
///////////////////////////////////////////////////////////////////////////////
// //
// qualitystatistics() Print statistics about the quality of the mesh. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::qualitystatistics()
{
triface tetloop, neightet;
point p[4];
char sbuf[128];
REAL radiusratiotable[12];
REAL aspectratiotable[12];
REAL A[4][4], rhs[4], D;
REAL V[6][3], N[4][3], H[4]; // edge-vectors, face-normals, face-heights.
REAL edgelength[6], alldihed[6], faceangle[3];
REAL shortest, longest;
REAL smallestvolume, biggestvolume;
REAL smallestratio, biggestratio;
REAL smallestdiangle, biggestdiangle;
REAL smallestfaangle, biggestfaangle;
REAL total_tet_vol, total_tetprism_vol;
REAL tetvol, minaltitude;
REAL cirradius, minheightinv; // insradius;
REAL shortlen, longlen;
REAL tetaspect, tetradius;
REAL smalldiangle, bigdiangle;
REAL smallfaangle, bigfaangle;
unsigned long radiustable[12];
unsigned long aspecttable[16];
unsigned long dihedangletable[18];
unsigned long faceangletable[18];
int indx[4];
int radiusindex;
int aspectindex;
int tendegree;
int i, j;
printf("Mesh quality statistics:\n\n");
shortlen = longlen = 0.0;
smalldiangle = bigdiangle = 0.0;
total_tet_vol = 0.0;
total_tetprism_vol = 0.0;
radiusratiotable[0] = 0.707; radiusratiotable[1] = 1.0;
radiusratiotable[2] = 1.1; radiusratiotable[3] = 1.2;
radiusratiotable[4] = 1.4; radiusratiotable[5] = 1.6;
radiusratiotable[6] = 1.8; radiusratiotable[7] = 2.0;
radiusratiotable[8] = 2.5; radiusratiotable[9] = 3.0;
radiusratiotable[10] = 10.0; radiusratiotable[11] = 0.0;
aspectratiotable[0] = 1.5; aspectratiotable[1] = 2.0;
aspectratiotable[2] = 2.5; aspectratiotable[3] = 3.0;
aspectratiotable[4] = 4.0; aspectratiotable[5] = 6.0;
aspectratiotable[6] = 10.0; aspectratiotable[7] = 15.0;
aspectratiotable[8] = 25.0; aspectratiotable[9] = 50.0;
aspectratiotable[10] = 100.0; aspectratiotable[11] = 0.0;
for (i = 0; i < 12; i++) radiustable[i] = 0l;
for (i = 0; i < 12; i++) aspecttable[i] = 0l;
for (i = 0; i < 18; i++) dihedangletable[i] = 0l;
for (i = 0; i < 18; i++) faceangletable[i] = 0l;
minaltitude = xmax - xmin + ymax - ymin + zmax - zmin;
minaltitude = minaltitude * minaltitude;
shortest = minaltitude;
longest = 0.0;
smallestvolume = minaltitude;
biggestvolume = 0.0;
smallestratio = 1e+16; // minaltitude;
biggestratio = 0.0;
smallestdiangle = smallestfaangle = 180.0;
biggestdiangle = biggestfaangle = 0.0;
int attrnum = numelemattrib - 1;
// Loop all elements, calculate quality parameters for each element.
tetrahedrons->traversalinit();
tetloop.tet = tetrahedrontraverse();
while (tetloop.tet != (tetrahedron *) NULL) {
if (b->convex) {
// Skip tets in the exterior.
if (elemattribute(tetloop.tet, attrnum) == -1.0) {
tetloop.tet = tetrahedrontraverse();
continue;
}
}
// Get four vertices: p0, p1, p2, p3.
for (i = 0; i < 4; i++) p[i] = (point) tetloop.tet[4 + i];
// Get the tet volume.
tetvol = orient3dfast(p[1], p[0], p[2], p[3]) / 6.0;
total_tet_vol += tetvol;
total_tetprism_vol += tetprismvol(p[0], p[1], p[2], p[3]);
// Calculate the largest and smallest volume.
if (tetvol < smallestvolume) {
smallestvolume = tetvol;
}
if (tetvol > biggestvolume) {
biggestvolume = tetvol;
}
// Set the edge vectors: V[0], ..., V[5]
for (i = 0; i < 3; i++) V[0][i] = p[0][i] - p[3][i]; // V[0]: p3->p0.
for (i = 0; i < 3; i++) V[1][i] = p[1][i] - p[3][i]; // V[1]: p3->p1.
for (i = 0; i < 3; i++) V[2][i] = p[2][i] - p[3][i]; // V[2]: p3->p2.
for (i = 0; i < 3; i++) V[3][i] = p[1][i] - p[0][i]; // V[3]: p0->p1.
for (i = 0; i < 3; i++) V[4][i] = p[2][i] - p[1][i]; // V[4]: p1->p2.
for (i = 0; i < 3; i++) V[5][i] = p[0][i] - p[2][i]; // V[5]: p2->p0.
// Get the squares of the edge lengths.
for (i = 0; i < 6; i++) edgelength[i] = dot(V[i], V[i]);
// Calculate the longest and shortest edge length.
for (i = 0; i < 6; i++) {
if (i == 0) {
shortlen = longlen = edgelength[i];
} else {
shortlen = edgelength[i] < shortlen ? edgelength[i] : shortlen;
longlen = edgelength[i] > longlen ? edgelength[i] : longlen;
}
if (edgelength[i] > longest) {
longest = edgelength[i];
}
if (edgelength[i] < shortest) {
shortest = edgelength[i];
}
}
// Set the matrix A = [V[0], V[1], V[2]]^T.
for (j = 0; j < 3; j++) {
for (i = 0; i < 3; i++) A[j][i] = V[j][i];
}
// Decompose A just once.
if (lu_decmp(A, 3, indx, &D, 0)) {
// Get the three faces normals.
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 face 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];
// Get the radius of the circumsphere.
for (i = 0; i < 3; i++) rhs[i] = 0.5 * dot(V[i], V[i]);
lu_solve(A, 3, indx, rhs, 0);
cirradius = sqrt(dot(rhs, rhs));
// Normalize the face normals.
for (i = 0; i < 4; i++) {
// H[i] is the inverse of height of its corresponding face.
H[i] = sqrt(dot(N[i], N[i]));
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];
}
} 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]));
// Skip it.
tetloop.tet = tetrahedrontraverse();
continue;
}
// Calculate the four face normals.
facenormal(p[2], p[1], p[3], N[0], 1, NULL);
facenormal(p[0], p[2], p[3], N[1], 1, NULL);
facenormal(p[1], p[0], p[3], N[2], 1, NULL);
facenormal(p[0], p[1], p[2], N[3], 1, NULL);
// Normalize the face normals.
for (i = 0; i < 4; i++) {
// H[i] is the twice of the area of the face.
H[i] = sqrt(dot(N[i], N[i]));
for (j = 0; j < 3; j++) N[i][j] /= H[i];
}
// Get the biggest H[i] / tetvol (corresponding to the smallest height).
minheightinv = (H[0] / tetvol);
for (i = 1; i < 3; i++) {
if ((H[i] / tetvol) > minheightinv) minheightinv = (H[i] / tetvol);
}
// Let the circumradius to be the half of its longest edge length.
cirradius = 0.5 * sqrt(longlen);
}
// Get the dihedrals (in degree) at each edges.
j = 0;
for (i = 1; i < 4; i++) {
alldihed[j] = -dot(N[0], N[i]); // Edge cd, bd, bc.
if (alldihed[j] < -1.0) alldihed[j] = -1; // Rounding.
else if (alldihed[j] > 1.0) alldihed[j] = 1;
alldihed[j] = acos(alldihed[j]) / PI * 180.0;
j++;
}
for (i = 2; i < 4; i++) {
alldihed[j] = -dot(N[1], N[i]); // Edge ad, ac.
if (alldihed[j] < -1.0) alldihed[j] = -1; // Rounding.
else if (alldihed[j] > 1.0) alldihed[j] = 1;
alldihed[j] = acos(alldihed[j]) / PI * 180.0;
j++;
}
alldihed[j] = -dot(N[2], N[3]); // Edge ab.
if (alldihed[j] < -1.0) alldihed[j] = -1; // Rounding.
else if (alldihed[j] > 1.0) alldihed[j] = 1;
alldihed[j] = acos(alldihed[j]) / PI * 180.0;
// Calculate the largest and smallest dihedral angles.
for (i = 0; i < 6; i++) {
if (i == 0) {
smalldiangle = bigdiangle = alldihed[i];
} else {
smalldiangle = alldihed[i] < smalldiangle ? alldihed[i] : smalldiangle;
bigdiangle = alldihed[i] > bigdiangle ? alldihed[i] : bigdiangle;
}
if (alldihed[i] < smallestdiangle) {
smallestdiangle = alldihed[i];
}
if (alldihed[i] > biggestdiangle) {
biggestdiangle = alldihed[i];
}
// Accumulate the corresponding number in the dihedral angle histogram.
if (alldihed[i] < 5.0) {
tendegree = 0;
} else if (alldihed[i] >= 5.0 && alldihed[i] < 10.0) {
tendegree = 1;
} else if (alldihed[i] >= 80.0 && alldihed[i] < 110.0) {
tendegree = 9; // Angles between 80 to 110 degree are in one entry.
} else if (alldihed[i] >= 170.0 && alldihed[i] < 175.0) {
tendegree = 16;
} else if (alldihed[i] >= 175.0) {
tendegree = 17;
} else {
tendegree = (int) (alldihed[i] / 10.);
if (alldihed[i] < 80.0) {
tendegree++; // In the left column.
} else {
tendegree--; // In the right column.
}
}
dihedangletable[tendegree]++;
}
// Calculate the largest and smallest face angles.
for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) {
fsym(tetloop, neightet);
// Only do the calulation once for a face.
if (((point) neightet.tet[7] == dummypoint) ||
(tetloop.tet < neightet.tet)) {
p[0] = org(tetloop);
p[1] = dest(tetloop);
p[2] = apex(tetloop);
faceangle[0] = interiorangle(p[0], p[1], p[2], NULL);
faceangle[1] = interiorangle(p[1], p[2], p[0], NULL);
faceangle[2] = PI - (faceangle[0] + faceangle[1]);
// Translate angles into degrees.
for (i = 0; i < 3; i++) {
faceangle[i] = (faceangle[i] * 180.0) / PI;
}
// Calculate the largest and smallest face angles.
for (i = 0; i < 3; i++) {
if (i == 0) {
smallfaangle = bigfaangle = faceangle[i];
} else {
smallfaangle = faceangle[i] < smallfaangle ?
faceangle[i] : smallfaangle;
bigfaangle = faceangle[i] > bigfaangle ? faceangle[i] : bigfaangle;
}
if (faceangle[i] < smallestfaangle) {
smallestfaangle = faceangle[i];
}
if (faceangle[i] > biggestfaangle) {
biggestfaangle = faceangle[i];
}
tendegree = (int) (faceangle[i] / 10.);
faceangletable[tendegree]++;
}
}
}
// Calculate aspect ratio and radius-edge ratio for this element.
tetradius = cirradius / sqrt(shortlen);
// tetaspect = sqrt(longlen) / (2.0 * insradius);
tetaspect = sqrt(longlen) * minheightinv;
// Remember the largest and smallest aspect ratio.
if (tetaspect < smallestratio) {
smallestratio = tetaspect;
}
if (tetaspect > biggestratio) {
biggestratio = tetaspect;
}
// Accumulate the corresponding number in the aspect ratio histogram.
aspectindex = 0;
while ((tetaspect > aspectratiotable[aspectindex]) && (aspectindex < 11)) {
aspectindex++;
}
aspecttable[aspectindex]++;
radiusindex = 0;
while ((tetradius > radiusratiotable[radiusindex]) && (radiusindex < 11)) {
radiusindex++;
}
radiustable[radiusindex]++;
tetloop.tet = tetrahedrontraverse();
}
shortest = sqrt(shortest);
longest = sqrt(longest);
minaltitude = sqrt(minaltitude);
printf(" Smallest volume: %16.5g | Largest volume: %16.5g\n",
smallestvolume, biggestvolume);
printf(" Shortest edge: %16.5g | Longest edge: %16.5g\n",
shortest, longest);
printf(" Smallest asp.ratio: %13.5g | Largest asp.ratio: %13.5g\n",
smallestratio, biggestratio);
sprintf(sbuf, "%.17g", biggestfaangle);
if (strlen(sbuf) > 8) {
sbuf[8] = '\0';
}
printf(" Smallest facangle: %14.5g | Largest facangle: %s\n",
smallestfaangle, sbuf);
sprintf(sbuf, "%.17g", biggestdiangle);
if (strlen(sbuf) > 8) {
sbuf[8] = '\0';
}
printf(" Smallest dihedral: %14.5g | Largest dihedral: %s\n\n",
smallestdiangle, sbuf);
printf(" Aspect ratio histogram:\n");
printf(" < %-6.6g : %8ld | %6.6g - %-6.6g : %8ld\n",
aspectratiotable[0], aspecttable[0], aspectratiotable[5],
aspectratiotable[6], aspecttable[6]);
for (i = 1; i < 5; i++) {
printf(" %6.6g - %-6.6g : %8ld | %6.6g - %-6.6g : %8ld\n",
aspectratiotable[i - 1], aspectratiotable[i], aspecttable[i],
aspectratiotable[i + 5], aspectratiotable[i + 6],
aspecttable[i + 6]);
}
printf(" %6.6g - %-6.6g : %8ld | %6.6g - : %8ld\n",
aspectratiotable[4], aspectratiotable[5], aspecttable[5],
aspectratiotable[10], aspecttable[11]);
printf(" (A tetrahedron's aspect ratio is its longest edge length");
printf(" divided by its\n");
printf(" smallest side height)\n\n");
printf(" Face angle histogram:\n");
for (i = 0; i < 9; i++) {
printf(" %3d - %3d degrees: %8ld | %3d - %3d degrees: %8ld\n",
i * 10, i * 10 + 10, faceangletable[i],
i * 10 + 90, i * 10 + 100, faceangletable[i + 9]);
}
if (minfaceang != PI) {
printf(" Minimum input face angle is %g (degree).\n",
minfaceang / PI * 180.0);
}
printf("\n");
printf(" Dihedral angle histogram:\n");
// Print the three two rows:
printf(" %3d - %2d degrees: %8ld | %3d - %3d degrees: %8ld\n",
0, 5, dihedangletable[0], 80, 110, dihedangletable[9]);
printf(" %3d - %2d degrees: %8ld | %3d - %3d degrees: %8ld\n",
5, 10, dihedangletable[1], 110, 120, dihedangletable[10]);
// Print the third to seventh rows.
for (i = 2; i < 7; i++) {
printf(" %3d - %2d degrees: %8ld | %3d - %3d degrees: %8ld\n",
(i - 1) * 10, (i - 1) * 10 + 10, dihedangletable[i],
(i - 1) * 10 + 110, (i - 1) * 10 + 120, dihedangletable[i + 9]);
}
// Print the last two rows.
printf(" %3d - %2d degrees: %8ld | %3d - %3d degrees: %8ld\n",
60, 70, dihedangletable[7], 170, 175, dihedangletable[16]);
printf(" %3d - %2d degrees: %8ld | %3d - %3d degrees: %8ld\n",
70, 80, dihedangletable[8], 175, 180, dihedangletable[17]);
if (minfacetdihed != PI) {
printf(" Minimum input dihedral angle is %g (degree).\n",
minfacetdihed / PI * 180.0);
}
printf("\n");
printf("\n");
}
///////////////////////////////////////////////////////////////////////////////
// //
// memorystatistics() Report the memory usage. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::memorystatistics()
{
printf("Memory usage statistics:\n\n");
// Count the number of blocks of tetrahedra.
int tetblocks = 0;
tetrahedrons->pathblock = tetrahedrons->firstblock;
while (tetrahedrons->pathblock != NULL) {
tetblocks++;
tetrahedrons->pathblock = (void **) *(tetrahedrons->pathblock);
}
// Calculate the total memory (in bytes) used by storing meshes.
unsigned long totalmeshmemory = 0l, totalt2shmemory = 0l;
totalmeshmemory = points->maxitems * points->itembytes +
tetrahedrons->maxitems * tetrahedrons->itembytes;
if (b->plc || b->refine) {
totalmeshmemory += (subfaces->maxitems * subfaces->itembytes +
subsegs->maxitems * subsegs->itembytes);
totalt2shmemory = (tet2subpool->maxitems * tet2subpool->itembytes +
tet2segpool->maxitems * tet2segpool->itembytes);
}
unsigned long totalalgomemory = 0l;
totalalgomemory = cavetetlist->totalmemory + cavebdrylist->totalmemory +
caveoldtetlist->totalmemory +
flippool->maxitems * flippool->itembytes;
if (b->plc || b->refine) {
totalalgomemory += (subsegstack->totalmemory + subfacstack->totalmemory +
subvertstack->totalmemory +
caveshlist->totalmemory + caveshbdlist->totalmemory +
cavesegshlist->totalmemory +
cavetetshlist->totalmemory +
cavetetseglist->totalmemory +
caveencshlist->totalmemory +
caveencseglist->totalmemory +
cavetetvertlist->totalmemory +
unflipqueue->totalmemory);
}
printf(" Maximum number of tetrahedra: %ld\n", tetrahedrons->maxitems);
printf(" Maximum number of tet blocks (blocksize = %d): %d\n",
b->tetrahedraperblock, tetblocks);
/*
if (b->plc || b->refine) {
printf(" Approximate memory for tetrahedral mesh (bytes): %ld\n",
totalmeshmemory);
printf(" Approximate memory for extra pointers (bytes): %ld\n",
totalt2shmemory);
} else {
printf(" Approximate memory for tetrahedralization (bytes): %ld\n",
totalmeshmemory);
}
printf(" Approximate memory for algorithms (bytes): %ld\n",
totalalgomemory);
printf(" Approximate memory for working arrays (bytes): %ld\n",
totalworkmemory);
printf(" Approximate total used memory (bytes): %ld\n",
totalmeshmemory + totalt2shmemory + totalalgomemory +
totalworkmemory);
*/
if (b->plc || b->refine) {
printf(" Approximate memory for tetrahedral mesh (bytes): ");
printfcomma(totalmeshmemory); printf("\n");
printf(" Approximate memory for extra pointers (bytes): ");
printfcomma(totalt2shmemory); printf("\n");
} else {
printf(" Approximate memory for tetrahedralization (bytes): ");
printfcomma(totalmeshmemory); printf("\n");
}
printf(" Approximate memory for algorithms (bytes): ");
printfcomma(totalalgomemory); printf("\n");
printf(" Approximate memory for working arrays (bytes): ");
printfcomma(totalworkmemory); printf("\n");
printf(" Approximate total used memory (bytes): ");
printfcomma(totalmeshmemory + totalt2shmemory + totalalgomemory +
totalworkmemory);
printf("\n");
printf("\n");
}
///////////////////////////////////////////////////////////////////////////////
// //
// statistics() Print all sorts of cool facts. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::statistics()
{
long tetnumber, facenumber;
printf("\nStatistics:\n\n");
printf(" Input points: %d\n", in->numberofpoints);
if (b->refine) {
printf(" Input tetrahedra: %d\n", in->numberoftetrahedra);
}
if (b->plc) {
printf(" Input facets: %d\n", in->numberoffacets);
printf(" Input segments: %ld\n", insegments);
printf(" Input holes: %d\n", in->numberofholes);
printf(" Input regions: %d\n", in->numberofregions);
}
tetnumber = tetrahedrons->items - hullsize;
facenumber = (tetnumber * 4l + hullsize) / 2l;
if (b->weighted) { // -w option
printf("\n Mesh points: %ld\n", points->items - nonregularcount);
} else {
printf("\n Mesh points: %ld\n", points->items);
}
printf(" Mesh tetrahedra: %ld\n", tetnumber);
printf(" Mesh faces: %ld\n", facenumber);
if (meshedges > 0l) {
printf(" Mesh edges: %ld\n", meshedges);
} else {
if (!nonconvex) {
long vsize = points->items - dupverts - unuverts;
if (b->weighted) vsize -= nonregularcount;
meshedges = vsize + facenumber - tetnumber - 1;
printf(" Mesh edges: %ld\n", meshedges);
}
}
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);
}
if (st_facref_count > 0l) {
printf(" Steiner points on facets: %ld\n", st_facref_count);
}
if (st_segref_count > 0l) {
printf(" Steiner points on segments: %ld\n", st_segref_count);
}
} else {
printf(" Convex hull faces: %ld\n", hullsize);
if (meshhulledges > 0l) {
printf(" Convex hull edges: %ld\n", meshhulledges);
}
}
if (b->weighted) { // -w option
printf(" Skipped non-regular points: %ld\n", nonregularcount);
}
printf("\n");
if (b->verbose > 0) {
if (b->plc || b->refine) { // -p or -r
if (tetrahedrons->items > 0l) {
qualitystatistics();
}
}
if (tetrahedrons->items > 0l) {
memorystatistics();
}
}
}
//// ////
//// ////
//// 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()
{
point pointloop;
bool jetflag;
int oldidx, newidx;
int remcount;
if (!b->quiet) {
printf("Jettisoning redundant points.\n");
}
points->traversalinit();
pointloop = pointtraverse();
oldidx = newidx = 0; // in->firstnumber;
remcount = 0;
while (pointloop != (point) NULL) {
jetflag = (pointtype(pointloop) == DUPLICATEDVERTEX) ||
(pointtype(pointloop) == UNUSEDVERTEX);
if (jetflag) {
// It is a duplicated or unused point, delete it.
pointdealloc(pointloop);
remcount++;
} else {
// Re-index it.
setpointmark(pointloop, newidx + in->firstnumber);
if (in->pointmarkerlist != (int *) NULL) {
if (oldidx < in->numberofpoints) {
// Re-index the point marker as well.
in->pointmarkerlist[newidx] = in->pointmarkerlist[oldidx];
}
}
newidx++;
}
oldidx++;
pointloop = pointtraverse();
}
if (b->verbose) {
printf(" %ld duplicated vertices are removed.\n", dupverts);
printf(" %ld unused vertices are removed.\n", unuverts);
}
dupverts = 0l;
unuverts = 0l;
// The following line ensures that dead items in the pool of nodes cannot
// be allocated for the new created nodes. This ensures that the input
// nodes will occur earlier in the output files, and have lower indices.
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. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::highorder()
{
triface tetloop, worktet, spintet;
point *extralist, *adjextralist;
point torg, tdest, newpoint;
int highorderindex;
int t1ver;
int i, j;
if (!b->quiet) {
printf("Adding vertices for second-order tetrahedra.\n");
}
// Initialize the 'highordertable'.
highordertable = new point[tetrahedrons->items * 6];
if (highordertable == (point *) NULL) {
terminatetetgen(this, 1);
}
// This will overwrite the slot for element markers.
highorderindex = 11;
// The following line ensures that dead items in the pool of nodes cannot
// be allocated for the extra nodes associated with high order elements.
// This ensures that the primary nodes (at the corners of elements) will
// occur earlier in the output files, and have lower indices, than the
// extra nodes.
points->deaditemstack = (void *) NULL;
// Assign an entry for each tetrahedron to find its extra nodes. At the
// mean while, initialize all extra nodes be NULL.
i = 0;
tetrahedrons->traversalinit();
tetloop.tet = tetrahedrontraverse();
while (tetloop.tet != (tetrahedron *) NULL) {
tetloop.tet[highorderindex] = (tetrahedron) &highordertable[i];
for (j = 0; j < 6; j++) {
highordertable[i + j] = (point) NULL;
}
i += 6;
tetloop.tet = tetrahedrontraverse();
}
// To create a unique node on each edge. Loop over all tetrahedra, and
// look at the six edges of each tetrahedron. If the extra node in
// the tetrahedron corresponding to this edge is NULL, create a node
// for this edge, at the same time, set the new node into the extra
// node lists of all other tetrahedra sharing this edge.
tetrahedrons->traversalinit();
tetloop.tet = tetrahedrontraverse();
while (tetloop.tet != (tetrahedron *) NULL) {
// Get the list of extra nodes.
extralist = (point *) tetloop.tet[highorderindex];
worktet.tet = tetloop.tet;
for (i = 0; i < 6; i++) {
if (extralist[i] == (point) NULL) {
// Go to the ith-edge.
worktet.ver = edge2ver[i];
// Create a new point in the middle of this edge.
torg = org(worktet);
tdest = dest(worktet);
makepoint(&newpoint, FREEVOLVERTEX);
for (j = 0; j < 3 + numpointattrib; j++) {
newpoint[j] = 0.5 * (torg[j] + tdest[j]);
}
// Interpolate its metrics.
for (j = 0; j < in->numberofpointmtrs; j++) {
newpoint[pointmtrindex + j] =
0.5 * (torg[pointmtrindex + j] + tdest[pointmtrindex + j]);
}
// Set this point into all extra node lists at this edge.
spintet = worktet;
while (1) {
if (!ishulltet(spintet)) {
adjextralist = (point *) spintet.tet[highorderindex];
adjextralist[ver2edge[spintet.ver]] = newpoint;
}
fnextself(spintet);
if (spintet.tet == worktet.tet) break;
}
} // if (!extralist[i])
} // i
tetloop.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()
{
triface worktet, spintet;
int ishulledge;
int t1ver;
int i;
meshedges = meshhulledges = 0l;
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 (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++;
}
}
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'). //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::outnodes(tetgenio* out)
{
FILE *outfile = NULL;
char outnodefilename[FILENAMESIZE];
face parentsh;
point pointloop;
int nextras, bmark, marker = 0, weightDT = 0;
int coordindex, attribindex;
int pointnumber, firstindex;
int index, i;
if (out == (tetgenio *) NULL) {
strcpy(outnodefilename, b->outfilename);
strcat(outnodefilename, ".node");
}
if (!b->quiet) {
if (out == (tetgenio *) NULL) {
printf("Writing %s.\n", outnodefilename);
} else {
printf("Writing nodes.\n");
}
}
nextras = numpointattrib;
if (b->weighted) { // -w
if (b->weighted_param == 0) weightDT = 1; // Weighted DT.
}
bmark = !b->nobound && in->pointmarkerlist;
if (out == (tetgenio *) NULL) {
outfile = fopen(outnodefilename, "w");
if (outfile == (FILE *) NULL) {
printf("File I/O Error: Cannot create file %s.\n", outnodefilename);
terminatetetgen(this, 1);
}
// 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);
} else {
// Allocate space for 'pointlist';
out->pointlist = new REAL[points->items * 3];
if (out->pointlist == (REAL *) NULL) {
printf("Error: Out of memory.\n");
terminatetetgen(this, 1);
}
// Allocate space for 'pointattributelist' if necessary;
if (nextras > 0) {
out->pointattributelist = new REAL[points->items * nextras];
if (out->pointattributelist == (REAL *) NULL) {
printf("Error: Out of memory.\n");
terminatetetgen(this, 1);
}
}
// Allocate space for 'pointmarkerlist' if necessary;
if (bmark) {
out->pointmarkerlist = new int[points->items];
if (out->pointmarkerlist == (int *) NULL) {
printf("Error: Out of memory.\n");
terminatetetgen(this, 1);
}
}
if (b->psc) {
out->pointparamlist = new tetgenio::pointparam[points->items];
if (out->pointparamlist == NULL) {
printf("Error: Out of memory.\n");
terminatetetgen(this, 1);
}
}
out->numberofpoints = points->items;
out->numberofpointattributes = nextras;
coordindex = 0;
attribindex = 0;
}
// Determine the first index (0 or 1).
firstindex = b->zeroindex ? 0 : in->firstnumber;
points->traversalinit();
pointloop = pointtraverse();
pointnumber = firstindex; // in->firstnumber;
index = 0;
while (pointloop != (point) NULL) {
if (bmark) {
// Default the vertex has a zero marker.
marker = 0;
// Is it an input vertex?
if (index < in->numberofpoints) {
// Input point's marker is directly copied to output.
marker = in->pointmarkerlist[index];
} else {
if ((pointtype(pointloop) == FREESEGVERTEX) ||
(pointtype(pointloop) == FREEFACETVERTEX)) {
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(...))
}
}
if (out == (tetgenio *) NULL) {
// Point number, x, y and z coordinates.
fprintf(outfile, "%4d %.17g %.17g %.17g", pointnumber,
pointloop[0], pointloop[1], pointloop[2]);
for (i = 0; i < nextras; i++) {
// Write an attribute.
if ((i == 0) && weightDT) {
fprintf(outfile, " %.17g", pointloop[0] * pointloop[0] +
pointloop[1] * pointloop[1] + pointloop[2] * pointloop[2]
- pointloop[3 + i]);
} else {
fprintf(outfile, " %.17g", pointloop[3 + i]);
}
}
if (bmark) {
// Write the boundary marker.
fprintf(outfile, " %d", marker);
}
if (b->psc) {
fprintf(outfile, " %.8g %.8g %d", pointgeomuv(pointloop, 0),
pointgeomuv(pointloop, 1), pointgeomtag(pointloop));
if (pointtype(pointloop) == RIDGEVERTEX) {
fprintf(outfile, " 0");
} else if (pointtype(pointloop) == ACUTEVERTEX) {
fprintf(outfile, " 0");
} else if (pointtype(pointloop) == FREESEGVERTEX) {
fprintf(outfile, " 1");
} else if (pointtype(pointloop) == FREEFACETVERTEX) {
fprintf(outfile, " 2");
} else if (pointtype(pointloop) == FREEVOLVERTEX) {
fprintf(outfile, " 3");
} else {
fprintf(outfile, " -1"); // Unknown type.
}
}
fprintf(outfile, "\n");
} else {
// X, y, and z coordinates.
out->pointlist[coordindex++] = pointloop[0];
out->pointlist[coordindex++] = pointloop[1];
out->pointlist[coordindex++] = pointloop[2];
// Point attributes.
for (i = 0; i < nextras; i++) {
// Output an attribute.
if ((i == 0) && weightDT) {
out->pointattributelist[attribindex++] =
pointloop[0] * pointloop[0] + pointloop[1] * pointloop[1] +
pointloop[2] * pointloop[2] - pointloop[3 + i];
} else {
out->pointattributelist[attribindex++] = pointloop[3 + i];
}
}
if (bmark) {
// Output the boundary marker.
out->pointmarkerlist[index] = marker;
}
if (b->psc) {
out->pointparamlist[index].uv[0] = pointgeomuv(pointloop, 0);
out->pointparamlist[index].uv[1] = pointgeomuv(pointloop, 1);
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) == FREESEGVERTEX) {
out->pointparamlist[index].type = 1;
} else if (pointtype(pointloop) == FREEFACETVERTEX) {
out->pointparamlist[index].type = 2;
} else if (pointtype(pointloop) == FREEVOLVERTEX) {
out->pointparamlist[index].type = 3;
} else {
out->pointparamlist[index].type = -1; // Unknown type.
}
}
}
pointloop = pointtraverse();
pointnumber++;
index++;
}
if (out == (tetgenio *) NULL) {
fprintf(outfile, "# Generated by %s\n", b->commandline);
fclose(outfile);
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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;
if (out == (tetgenio *) NULL) {
strcpy(outmtrfilename, b->outfilename);
strcat(outmtrfilename, ".mtr");
}
if (!b->quiet) {
if (out == (tetgenio *) NULL) {
printf("Writing %s.\n", outmtrfilename);
} else {
printf("Writing metrics.\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, number of point metrices,
// fprintf(outfile, "%ld %d\n", points->items, sizeoftensor + 3);
fprintf(outfile, "%ld %d\n", points->items, 1);
} else {
// Allocate space for 'pointmtrlist' if necessary;
// out->pointmtrlist = new REAL[points->items * (sizeoftensor + 3)];
out->pointmtrlist = new REAL[points->items];
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]);
} else {
// for (i = 0; i < sizeoftensor; i++) {
// out->pointmtrlist[mtrindex++] = ptloop[pointmtrindex + i];
// }
out->pointmtrlist[mtrindex++] = ptloop[pointmtrindex];
}
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'. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::outelements(tetgenio* out)
{
FILE *outfile = NULL;
char outelefilename[FILENAMESIZE];
tetrahedron* tptr;
point p1, p2, p3, p4;
point *extralist;
REAL *talist = NULL;
int *tlist = NULL;
long ntets;
int firstindex, shift;
int pointindex, attribindex;
int highorderindex = 11;
int elementnumber;
int eextras;
int i;
if (out == (tetgenio *) NULL) {
strcpy(outelefilename, b->outfilename);
strcat(outelefilename, ".ele");
}
if (!b->quiet) {
if (out == (tetgenio *) NULL) {
printf("Writing %s.\n", outelefilename);
} else {
printf("Writing elements.\n");
}
}
// The number of tets excluding hull tets.
ntets = tetrahedrons->items - hullsize;
eextras = numelemattrib;
if (out == (tetgenio *) NULL) {
outfile = fopen(outelefilename, "w");
if (outfile == (FILE *) NULL) {
printf("File I/O Error: Cannot create file %s.\n", outelefilename);
terminatetetgen(this, 1);
}
// Number of tetras, points per tetra, attributes per tetra.
fprintf(outfile, "%ld %d %d\n", ntets, b->order == 1 ? 4 : 10, eextras);
} else {
// Allocate memory for output tetrahedra.
out->tetrahedronlist = new int[ntets * (b->order == 1 ? 4 : 10)];
if (out->tetrahedronlist == (int *) NULL) {
printf("Error: Out of memory.\n");
terminatetetgen(this, 1);
}
// Allocate memory for output tetrahedron attributes if necessary.
if (eextras > 0) {
out->tetrahedronattributelist = new REAL[ntets * eextras];
if (out->tetrahedronattributelist == (REAL *) NULL) {
printf("Error: Out of memory.\n");
terminatetetgen(this, 1);
}
}
out->numberoftetrahedra = ntets;
out->numberofcorners = b->order == 1 ? 4 : 10;
out->numberoftetrahedronattributes = eextras;
tlist = out->tetrahedronlist;
talist = out->tetrahedronattributelist;
pointindex = 0;
attribindex = 0;
}
// Determine the first index (0 or 1).
firstindex = b->zeroindex ? 0 : in->firstnumber;
shift = 0; // Default no shift.
if ((in->firstnumber == 1) && (firstindex == 0)) {
shift = 1; // Shift the output indices by 1.
}
tetrahedrons->traversalinit();
tptr = tetrahedrontraverse();
elementnumber = firstindex; // in->firstnumber;
while (tptr != (tetrahedron *) NULL) {
if (!b->reversetetori) {
p1 = (point) tptr[4];
p2 = (point) tptr[5];
} else {
p1 = (point) tptr[5];
p2 = (point) tptr[4];
}
p3 = (point) tptr[6];
p4 = (point) tptr[7];
if (out == (tetgenio *) NULL) {
// Tetrahedron number, indices for four points.
fprintf(outfile, "%5d %5d %5d %5d %5d", elementnumber,
pointmark(p1) - shift, pointmark(p2) - shift,
pointmark(p3) - shift, pointmark(p4) - shift);
if (b->order == 2) {
extralist = (point *) tptr[highorderindex];
// indices for six extra points.
fprintf(outfile, " %5d %5d %5d %5d %5d %5d",
pointmark(extralist[0]) - shift, pointmark(extralist[1]) - shift,
pointmark(extralist[2]) - shift, pointmark(extralist[3]) - shift,
pointmark(extralist[4]) - shift, pointmark(extralist[5]) - shift);
}
for (i = 0; i < eextras; i++) {
fprintf(outfile, " %.17g", elemattribute(tptr, i));
}
fprintf(outfile, "\n");
} else {
tlist[pointindex++] = pointmark(p1) - shift;
tlist[pointindex++] = pointmark(p2) - shift;
tlist[pointindex++] = pointmark(p3) - shift;
tlist[pointindex++] = pointmark(p4) - shift;
if (b->order == 2) {
extralist = (point *) tptr[highorderindex];
tlist[pointindex++] = pointmark(extralist[0]) - shift;
tlist[pointindex++] = pointmark(extralist[1]) - shift;
tlist[pointindex++] = pointmark(extralist[2]) - shift;
tlist[pointindex++] = pointmark(extralist[3]) - shift;
tlist[pointindex++] = pointmark(extralist[4]) - shift;
tlist[pointindex++] = pointmark(extralist[5]) - shift;
}
for (i = 0; i < eextras; i++) {
talist[attribindex++] = elemattribute(tptr, i);
}
}
// Remember the index of this element (for counting edges).
setelemindex(tptr, elementnumber);
tptr = tetrahedrontraverse();
elementnumber++;
}
if (out == (tetgenio *) NULL) {
fprintf(outfile, "# Generated by %s\n", b->commandline);
fclose(outfile);
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// outfaces() Output all faces to a .face file or a tetgenio object. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::outfaces(tetgenio* out)
{
FILE *outfile = NULL;
char facefilename[FILENAMESIZE];
triface tface, tsymface;
face checkmark;
point torg, tdest, tapex;
long ntets, faces;
int *elist = NULL, *emlist = NULL;
int neigh1 = 0, neigh2 = 0;
int faceid, marker = 0;
int firstindex, shift;
int facenumber;
int index = 0;
// For -o2 option.
triface workface;
point *extralist, pp[3] = {0,0,0};
int highorderindex = 11;
int o2index = 0, i;
if (out == (tetgenio *) NULL) {
strcpy(facefilename, b->outfilename);
strcat(facefilename, ".face");
}
if (!b->quiet) {
if (out == (tetgenio *) NULL) {
printf("Writing %s.\n", facefilename);
} else {
printf("Writing faces.\n");
}
}
ntets = tetrahedrons->items - hullsize;
faces = (ntets * 4l + hullsize) / 2l;
if (out == (tetgenio *) NULL) {
outfile = fopen(facefilename, "w");
if (outfile == (FILE *) NULL) {
printf("File I/O Error: Cannot create file %s.\n", facefilename);
terminatetetgen(this, 1);
}
fprintf(outfile, "%ld %d\n", faces, !b->nobound);
} else {
// Allocate memory for 'trifacelist'.
out->trifacelist = new int[faces * 3];
if (out->trifacelist == (int *) NULL) {
printf("Error: Out of memory.\n");
terminatetetgen(this, 1);
}
if (b->order == 2) {
out->o2facelist = new int[faces * 3];
}
// Allocate memory for 'trifacemarkerlist' if necessary.
if (!b->nobound) {
out->trifacemarkerlist = new int[faces];
if (out->trifacemarkerlist == (int *) NULL) {
printf("Error: Out of memory.\n");
terminatetetgen(this, 1);
}
}
if (b->neighout > 1) {
// '-nn' switch.
out->adjtetlist = new int[faces * 2];
if (out->adjtetlist == (int *) NULL) {
printf("Error: Out of memory.\n");
terminatetetgen(this, 1);
}
}
out->numberoftrifaces = faces;
elist = out->trifacelist;
emlist = out->trifacemarkerlist;
}
// Determine the first index (0 or 1).
firstindex = b->zeroindex ? 0 : in->firstnumber;
shift = 0; // Default no shiftment.
if ((in->firstnumber == 1) && (firstindex == 0)) {
shift = 1; // Shift the output indices by 1.
}
tetrahedrons->traversalinit();
tface.tet = tetrahedrontraverse();
facenumber = firstindex; // in->firstnumber;
// To loop over the set of faces, loop over all tetrahedra, and look at
// the four faces of each one. If its adjacent tet is a hull tet,
// operate on the face, otherwise, operate on the face only if the
// current tet has a smaller index than its neighbor.
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))) {
torg = org(tface);
tdest = dest(tface);
tapex = apex(tface);
if (b->order == 2) { // -o2
// Get the three extra vertices on edges.
extralist = (point *) (tface.tet[highorderindex]);
// The extra vertices are on edges opposite the corners.
enext(tface, workface);
for (i = 0; i < 3; i++) {
pp[i] = extralist[ver2edge[workface.ver]];
enextself(workface);
}
}
if (!b->nobound) {
// Get the boundary marker of this face.
if (b->plc || b->refine) {
// Shell face is used.
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.
}
}
} else {
// Shell face is not used, only distinguish outer and inner face.
marker = (int) ishulltet(tsymface);
}
}
if (b->neighout > 1) {
// '-nn' switch. Output adjacent tets indices.
neigh1 = elemindex(tface.tet);
if (!ishulltet(tsymface)) {
neigh2 = elemindex(tsymface.tet);
} else {
neigh2 = -1;
}
}
if (out == (tetgenio *) NULL) {
// Face number, indices of three vertices.
fprintf(outfile, "%5d %4d %4d %4d", facenumber,
pointmark(torg) - shift, pointmark(tdest) - shift,
pointmark(tapex) - shift);
if (b->order == 2) { // -o2
fprintf(outfile, " %4d %4d %4d", pointmark(pp[0]) - shift,
pointmark(pp[1]) - shift, pointmark(pp[2]) - shift);
}
if (!b->nobound) {
// Output a boundary marker.
fprintf(outfile, " %d", marker);
}
if (b->neighout > 1) {
fprintf(outfile, " %5d %5d", neigh1, neigh2);
}
fprintf(outfile, "\n");
} else {
// Output indices of three vertices.
elist[index++] = pointmark(torg) - shift;
elist[index++] = pointmark(tdest) - shift;
elist[index++] = pointmark(tapex) - shift;
if (b->order == 2) { // -o2
out->o2facelist[o2index++] = pointmark(pp[0]) - shift;
out->o2facelist[o2index++] = pointmark(pp[1]) - shift;
out->o2facelist[o2index++] = pointmark(pp[2]) - shift;
}
if (!b->nobound) {
emlist[facenumber - in->firstnumber] = marker;
}
if (b->neighout > 1) {
out->adjtetlist[(facenumber - in->firstnumber) * 2] = neigh1;
out->adjtetlist[(facenumber - in->firstnumber) * 2 + 1] = neigh2;
}
}
facenumber++;
}
}
tface.tet = tetrahedrontraverse();
}
if (out == (tetgenio *) NULL) {
fprintf(outfile, "# Generated by %s\n", b->commandline);
fclose(outfile);
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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)
{
FILE *outfile = NULL;
char facefilename[FILENAMESIZE];
triface hulltet;
point torg, tdest, tapex;
int *elist = NULL;
int firstindex, shift;
int facenumber;
int index;
if (out == (tetgenio *) NULL) {
strcpy(facefilename, b->outfilename);
strcat(facefilename, ".face");
}
if (!b->quiet) {
if (out == (tetgenio *) NULL) {
printf("Writing %s.\n", facefilename);
} else {
printf("Writing faces.\n");
}
}
if (out == (tetgenio *) NULL) {
outfile = fopen(facefilename, "w");
if (outfile == (FILE *) NULL) {
printf("File I/O Error: Cannot create file %s.\n", facefilename);
terminatetetgen(this, 1);
}
fprintf(outfile, "%ld 0\n", hullsize);
} else {
// Allocate memory for 'trifacelist'.
out->trifacelist = new int[hullsize * 3];
if (out->trifacelist == (int *) NULL) {
printf("Error: Out of memory.\n");
terminatetetgen(this, 1);
}
out->numberoftrifaces = hullsize;
elist = out->trifacelist;
index = 0;
}
// Determine the first index (0 or 1).
firstindex = b->zeroindex ? 0 : in->firstnumber;
shift = 0; // Default no shiftment.
if ((in->firstnumber == 1) && (firstindex == 0)) {
shift = 1; // Shift the output indices by 1.
}
tetrahedrons->traversalinit();
hulltet.tet = alltetrahedrontraverse();
facenumber = firstindex;
while (hulltet.tet != (tetrahedron *) NULL) {
if (ishulltet(hulltet)) {
torg = (point) hulltet.tet[4];
tdest = (point) hulltet.tet[5];
tapex = (point) hulltet.tet[6];
if (out == (tetgenio *) NULL) {
// Face number, indices of three vertices.
fprintf(outfile, "%5d %4d %4d %4d", facenumber,
pointmark(torg) - shift, pointmark(tdest) - shift,
pointmark(tapex) - shift);
fprintf(outfile, "\n");
} else {
// Output indices of three vertices.
elist[index++] = pointmark(torg) - shift;
elist[index++] = pointmark(tdest) - shift;
elist[index++] = pointmark(tapex) - shift;
}
facenumber++;
}
hulltet.tet = alltetrahedrontraverse();
}
if (out == (tetgenio *) NULL) {
fprintf(outfile, "# Generated by %s\n", b->commandline);
fclose(outfile);
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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)
{
FILE *outfile = NULL;
char facefilename[FILENAMESIZE];
int *elist = NULL;
int *emlist = NULL;
int index = 0, index1 = 0, index2 = 0;
triface abuttingtet;
face faceloop;
point torg, tdest, tapex;
int faceid = 0, marker = 0;
int firstindex, shift;
int neigh1 = 0, neigh2 = 0;
int facenumber;
// For -o2 option.
triface workface;
point *extralist, pp[3] = {0,0,0};
int highorderindex = 11;
int o2index = 0, i;
int t1ver; // used by fsymself()
if (out == (tetgenio *) NULL) {
strcpy(facefilename, b->outfilename);
strcat(facefilename, ".face");
}
if (!b->quiet) {
if (out == (tetgenio *) NULL) {
printf("Writing %s.\n", facefilename);
} else {
printf("Writing faces.\n");
}
}
if (out == (tetgenio *) NULL) {
outfile = fopen(facefilename, "w");
if (outfile == (FILE *) NULL) {
printf("File I/O Error: Cannot create file %s.\n", facefilename);
terminatetetgen(this, 3);
}
// Number of subfaces.
fprintf(outfile, "%ld %d\n", subfaces->items, !b->nobound);
} else {
// Allocate memory for 'trifacelist'.
out->trifacelist = new int[subfaces->items * 3];
if (out->trifacelist == (int *) NULL) {
terminatetetgen(this, 1);
}
if (b->order == 2) {
out->o2facelist = new int[subfaces->items * 3];
}
if (!b->nobound) {
// Allocate memory for 'trifacemarkerlist'.
out->trifacemarkerlist = new int[subfaces->items];
if (out->trifacemarkerlist == (int *) NULL) {
terminatetetgen(this, 1);
}
}
if (b->neighout > 1) {
// '-nn' switch.
out->adjtetlist = new int[subfaces->items * 2];
if (out->adjtetlist == (int *) NULL) {
terminatetetgen(this, 1);
}
}
out->numberoftrifaces = subfaces->items;
elist = out->trifacelist;
emlist = out->trifacemarkerlist;
}
// Determine the first index (0 or 1).
firstindex = b->zeroindex ? 0 : in->firstnumber;
shift = 0; // Default no shiftment.
if ((in->firstnumber == 1) && (firstindex == 0)) {
shift = 1; // Shift the output indices by 1.
}
subfaces->traversalinit();
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);
assert(!ishulltet(abuttingtet));
}
}
if (abuttingtet.tet != NULL) {
torg = org(abuttingtet);
tdest = dest(abuttingtet);
tapex = apex(abuttingtet);
if (b->order == 2) { // -o2
// Get the three extra vertices on edges.
extralist = (point *) (abuttingtet.tet[highorderindex]);
workface = abuttingtet;
for (i = 0; i < 3; i++) {
pp[i] = extralist[ver2edge[workface.ver]];
enextself(workface);
}
}
} else {
// This may happen when only a surface mesh be generated.
torg = sorg(faceloop);
tdest = sdest(faceloop);
tapex = sapex(faceloop);
if (b->order == 2) { // -o2
// There is no extra node list available.
pp[0] = torg;
pp[1] = tdest;
pp[2] = tapex;
}
}
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.
}
}
}
if (b->neighout > 1) {
// '-nn' switch. Output adjacent tets indices.
neigh1 = -1;
neigh2 = -1;
stpivot(faceloop, abuttingtet);
if (abuttingtet.tet != NULL) {
neigh1 = elemindex(abuttingtet.tet);
fsymself(abuttingtet);
if (!ishulltet(abuttingtet)) {
neigh2 = elemindex(abuttingtet.tet);
}
}
}
if (out == (tetgenio *) NULL) {
fprintf(outfile, "%5d %4d %4d %4d", facenumber,
pointmark(torg) - shift, pointmark(tdest) - shift,
pointmark(tapex) - shift);
if (b->order == 2) { // -o2
fprintf(outfile, " %4d %4d %4d", pointmark(pp[0]) - shift,
pointmark(pp[1]) - shift, pointmark(pp[2]) - shift);
}
if (!b->nobound) {
fprintf(outfile, " %d", marker);
}
if (b->neighout > 1) {
fprintf(outfile, " %5d %5d", neigh1, neigh2);
}
fprintf(outfile, "\n");
} else {
// Output three vertices of this face;
elist[index++] = pointmark(torg) - shift;
elist[index++] = pointmark(tdest) - shift;
elist[index++] = pointmark(tapex) - shift;
if (b->order == 2) { // -o2
out->o2facelist[o2index++] = pointmark(pp[0]) - shift;
out->o2facelist[o2index++] = pointmark(pp[1]) - shift;
out->o2facelist[o2index++] = pointmark(pp[2]) - shift;
}
if (!b->nobound) {
emlist[index1++] = marker;
}
if (b->neighout > 1) {
out->adjtetlist[index2++] = neigh1;
out->adjtetlist[index2++] = neigh2;
}
}
facenumber++;
faceloop.sh = shellfacetraverse(subfaces);
}
if (out == (tetgenio *) NULL) {
fprintf(outfile, "# Generated by %s\n", b->commandline);
fclose(outfile);
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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)
{
FILE *outfile = NULL;
char edgefilename[FILENAMESIZE];
triface tetloop, worktet, spintet;
face checkseg;
point torg, tdest;
int *elist = NULL, *emlist = NULL;
int ishulledge;
int firstindex, shift;
int edgenumber, marker;
int index = 0, index1 = 0, index2 = 0;
int t1ver;
int i;
// For -o2 option.
point *extralist, pp = NULL;
int highorderindex = 11;
int o2index = 0;
if (out == (tetgenio *) NULL) {
strcpy(edgefilename, b->outfilename);
strcat(edgefilename, ".edge");
}
if (!b->quiet) {
if (out == (tetgenio *) NULL) {
printf("Writing %s.\n", edgefilename);
} else {
printf("Writing edges.\n");
}
}
if (meshedges == 0l) {
if (nonconvex) {
numberedges(); // Count the edges.
} else {
// Use Euler's characteristic to get the numbe of edges.
// It states V - E + F - C = 1, hence E = V + F - C - 1.
long tsize = tetrahedrons->items - hullsize;
long fsize = (tsize * 4l + hullsize) / 2l;
long vsize = points->items - dupverts - unuverts;
if (b->weighted) vsize -= nonregularcount;
meshedges = vsize + fsize - tsize - 1;
}
}
if (out == (tetgenio *) NULL) {
outfile = fopen(edgefilename, "w");
if (outfile == (FILE *) NULL) {
printf("File I/O Error: Cannot create file %s.\n", edgefilename);
terminatetetgen(this, 1);
}
// Write the number of edges, boundary markers (0 or 1).
fprintf(outfile, "%ld %d\n", meshedges, !b->nobound);
} else {
// Allocate memory for 'edgelist'.
out->edgelist = new int[meshedges * 2];
if (out->edgelist == (int *) NULL) {
printf("Error: Out of memory.\n");
terminatetetgen(this, 1);
}
if (b->order == 2) { // -o2 switch
out->o2edgelist = new int[meshedges];
}
if (!b->nobound) {
out->edgemarkerlist = new int[meshedges];
}
if (b->neighout > 1) { // '-nn' switch.
out->edgeadjtetlist = new int[meshedges];
}
out->numberofedges = meshedges;
elist = out->edgelist;
emlist = out->edgemarkerlist;
}
// Determine the first index (0 or 1).
firstindex = b->zeroindex ? 0 : in->firstnumber;
shift = 0; // Default no shiftment.
if ((in->firstnumber == 1) && (firstindex == 0)) {
shift = 1; // Shift (reduce) the output indices by 1.
}
tetrahedrons->traversalinit();
tetloop.tet = tetrahedrontraverse();
edgenumber = firstindex; // in->firstnumber;
while (tetloop.tet != (tetrahedron *) NULL) {
// Count the number of Voronoi faces.
worktet.tet = tetloop.tet;
for (i = 0; i < 6; i++) {
worktet.ver = edge2ver[i];
ishulledge = 0;
fnext(worktet, spintet);
do {
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) {
torg = org(worktet);
tdest = dest(worktet);
if (b->order == 2) { // -o2
// Get the extra vertex on this edge.
extralist = (point *) worktet.tet[highorderindex];
pp = extralist[ver2edge[worktet.ver]];
}
if (out == (tetgenio *) NULL) {
fprintf(outfile, "%5d %4d %4d", edgenumber,
pointmark(torg) - shift, pointmark(tdest) - shift);
if (b->order == 2) { // -o2
fprintf(outfile, " %4d", pointmark(pp) - shift);
}
} else {
// Output three vertices of this face;
elist[index++] = pointmark(torg) - shift;
elist[index++] = pointmark(tdest) - shift;
if (b->order == 2) { // -o2
out->o2edgelist[o2index++] = pointmark(pp) - shift;
}
}
if (!b->nobound) {
if (b->plc || b->refine) {
// Check if the edge is a segment.
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.
}
} else {
// Mark it if it is a hull edge.
marker = ishulledge ? 1 : 0;
}
if (out == (tetgenio *) NULL) {
fprintf(outfile, " %d", marker);
} else {
emlist[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);
}
}
if (out == (tetgenio *) NULL) {
fprintf(outfile, "\n");
}
edgenumber++;
}
}
tetloop.tet = tetrahedrontraverse();
}
if (out == (tetgenio *) NULL) {
fprintf(outfile, "# Generated by %s\n", b->commandline);
fclose(outfile);
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// outsubsegments() Output segments to a .edge file or a structure. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::outsubsegments(tetgenio* out)
{
FILE *outfile = NULL;
char edgefilename[FILENAMESIZE];
int *elist = NULL;
int index, i;
face edgeloop;
point torg, tdest;
int firstindex, shift;
int marker;
int edgenumber;
// For -o2 option.
triface workface, spintet;
point *extralist, pp = NULL;
int highorderindex = 11;
int o2index = 0;
// For -nn option.
int neigh = -1;
int index2 = 0;
int t1ver; // used by fsymself()
if (out == (tetgenio *) NULL) {
strcpy(edgefilename, b->outfilename);
strcat(edgefilename, ".edge");
}
if (!b->quiet) {
if (out == (tetgenio *) NULL) {
printf("Writing %s.\n", edgefilename);
} else {
printf("Writing edges.\n");
}
}
if (out == (tetgenio *) NULL) {
outfile = fopen(edgefilename, "w");
if (outfile == (FILE *) NULL) {
printf("File I/O Error: Cannot create file %s.\n", edgefilename);
terminatetetgen(this, 3);
}
// Number of subsegments.
fprintf(outfile, "%ld 1\n", subsegs->items);
} else {
// Allocate memory for 'edgelist'.
out->edgelist = new int[subsegs->items * (b->order == 1 ? 2 : 3)];
if (out->edgelist == (int *) NULL) {
terminatetetgen(this, 1);
}
if (b->order == 2) {
out->o2edgelist = new int[subsegs->items];
}
out->edgemarkerlist = new int[subsegs->items];
if (out->edgemarkerlist == (int *) NULL) {
terminatetetgen(this, 1);
}
if (b->neighout > 1) {
out->edgeadjtetlist = new int[subsegs->items];
}
out->numberofedges = subsegs->items;
elist = out->edgelist;
}
// Determine the first index (0 or 1).
firstindex = b->zeroindex ? 0 : in->firstnumber;
shift = 0; // Default no shiftment.
if ((in->firstnumber == 1) && (firstindex == 0)) {
shift = 1; // Shift the output indices by 1.
}
index = 0;
i = 0;
subsegs->traversalinit();
edgeloop.sh = shellfacetraverse(subsegs);
edgenumber = firstindex; // in->firstnumber;
while (edgeloop.sh != (shellface *) NULL) {
torg = sorg(edgeloop);
tdest = sdest(edgeloop);
if ((b->order == 2) || (b->neighout > 1)) {
sstpivot1(edgeloop, workface);
if (workface.tet != NULL) {
// We must find a non-hull tet.
if (ishulltet(workface)) {
spintet = workface;
while (1) {
fnextself(spintet);
if (!ishulltet(spintet)) break;
if (spintet.tet == workface.tet) break;
}
assert(!ishulltet(spintet));
workface = spintet;
}
}
}
if (b->order == 2) { // -o2
// Get the extra vertex on this edge.
if (workface.tet != NULL) {
extralist = (point *) workface.tet[highorderindex];
pp = extralist[ver2edge[workface.ver]];
} else {
pp = torg; // There is no extra node available.
}
}
if (b->neighout > 1) { // -nn
if (workface.tet != NULL) {
neigh = elemindex(workface.tet);
} else {
neigh = -1;
}
}
marker = shellmark(edgeloop);
if (marker == 0) {
marker = 1; // Default marker of a boundary edge is 1.
}
if (out == (tetgenio *) NULL) {
fprintf(outfile, "%5d %4d %4d", edgenumber,
pointmark(torg) - shift, pointmark(tdest) - shift);
if (b->order == 2) { // -o2
fprintf(outfile, " %4d", pointmark(pp) - shift);
}
fprintf(outfile, " %d", marker);
if (b->neighout > 1) { // -nn
fprintf(outfile, " %4d", neigh);
}
fprintf(outfile, "\n");
} else {
// Output three vertices of this face;
elist[index++] = pointmark(torg) - shift;
elist[index++] = pointmark(tdest) - shift;
if (b->order == 2) { // -o2
out->o2edgelist[o2index++] = pointmark(pp) - shift;
}
out->edgemarkerlist[i++] = marker;
if (b->neighout > 1) { // -nn
out->edgeadjtetlist[index2++] = neigh;
}
}
edgenumber++;
edgeloop.sh = shellfacetraverse(subsegs);
}
if (out == (tetgenio *) NULL) {
fprintf(outfile, "# Generated by %s\n", b->commandline);
fclose(outfile);
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// outneighbors() Output tet neighbors to a .neigh file or a structure. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::outneighbors(tetgenio* out)
{
FILE *outfile = NULL;
char neighborfilename[FILENAMESIZE];
int *nlist = NULL;
int index = 0;
triface tetloop, tetsym;
int neighbori[4];
int firstindex;
int elementnumber;
long ntets;
if (out == (tetgenio *) NULL) {
strcpy(neighborfilename, b->outfilename);
strcat(neighborfilename, ".neigh");
}
if (!b->quiet) {
if (out == (tetgenio *) NULL) {
printf("Writing %s.\n", neighborfilename);
} else {
printf("Writing neighbors.\n");
}
}
ntets = tetrahedrons->items - hullsize;
if (out == (tetgenio *) NULL) {
outfile = fopen(neighborfilename, "w");
if (outfile == (FILE *) NULL) {
printf("File I/O Error: Cannot create file %s.\n", neighborfilename);
terminatetetgen(this, 1);
}
// Number of tetrahedra, four faces per tetrahedron.
fprintf(outfile, "%ld %d\n", ntets, 4);
} else {
// Allocate memory for 'neighborlist'.
out->neighborlist = new int[ntets * 4];
if (out->neighborlist == (int *) NULL) {
printf("Error: Out of memory.\n");
terminatetetgen(this, 1);
}
nlist = out->neighborlist;
}
// Determine the first index (0 or 1).
firstindex = b->zeroindex ? 0 : in->firstnumber;
tetrahedrons->traversalinit();
tetloop.tet = tetrahedrontraverse();
elementnumber = firstindex; // in->firstnumber;
while (tetloop.tet != (tetrahedron *) NULL) {
for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) {
fsym(tetloop, tetsym);
if (!ishulltet(tetsym)) {
neighbori[tetloop.ver] = elemindex(tetsym.tet);
} else {
neighbori[tetloop.ver] = -1;
}
}
if (out == (tetgenio *) NULL) {
// Tetrahedra number, neighboring tetrahedron numbers.
fprintf(outfile, "%4d %4d %4d %4d %4d\n", elementnumber,
neighbori[0], neighbori[1], neighbori[2], neighbori[3]);
} else {
nlist[index++] = neighbori[0];
nlist[index++] = neighbori[1];
nlist[index++] = neighbori[2];
nlist[index++] = neighbori[3];
}
tetloop.tet = tetrahedrontraverse();
elementnumber++;
}
if (out == (tetgenio *) NULL) {
fprintf(outfile, "# Generated by %s\n", b->commandline);
fclose(outfile);
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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)
{
FILE *outfile = NULL;
char outfilename[FILENAMESIZE];
tetgenio::voroedge *vedge = NULL;
tetgenio::vorofacet *vfacet = NULL;
arraypool *tetlist, *ptlist;
triface tetloop, worktet, spintet, firsttet;
point pt[4], ploop, neipt;
REAL ccent[3], infvec[3], vec1[3], vec2[3], L;
long ntets, faces, edges;
int *indexarray, *fidxs, *eidxs;
int arraysize, *vertarray = NULL;
int vpointcount, vedgecount, vfacecount, tcount;
int ishullvert, ishullface;
int index, shift, end1, end2;
int i, j;
int t1ver; // used by fsymself()
// Output Voronoi vertices to .v.node file.
if (out == (tetgenio *) NULL) {
strcpy(outfilename, b->outfilename);
strcat(outfilename, ".v.node");
}
if (!b->quiet) {
if (out == (tetgenio *) NULL) {
printf("Writing %s.\n", outfilename);
} else {
printf("Writing Voronoi vertices.\n");
}
}
// Determine the first index (0 or 1).
shift = (b->zeroindex ? 0 : in->firstnumber);
// Each face and edge of the tetrahedral mesh will be indexed for indexing
// the Voronoi edges and facets. Indices of faces and edges are saved in
// each tetrahedron (including hull tets).
// Allocate the total space once.
indexarray = new int[tetrahedrons->items * 10];
// Allocate space (10 integers) into each tetrahedron. It re-uses the slot
// for element markers, flags.
i = 0;
tetrahedrons->traversalinit();
tetloop.tet = alltetrahedrontraverse();
while (tetloop.tet != NULL) {
tetloop.tet[11] = (tetrahedron) &(indexarray[i * 10]);
i++;
tetloop.tet = alltetrahedrontraverse();
}
// The number of tetrahedra (excluding hull tets) (Voronoi vertices).
ntets = tetrahedrons->items - hullsize;
// The number of Delaunay faces (Voronoi edges).
faces = (4l * ntets + hullsize) / 2l;
// The number of Delaunay edges (Voronoi faces).
long vsize = points->items - dupverts - unuverts;
if (b->weighted) vsize -= nonregularcount;
edges = vsize + faces - ntets - 1;
if (out == (tetgenio *) NULL) {
outfile = fopen(outfilename, "w");
if (outfile == (FILE *) NULL) {
printf("File I/O Error: Cannot create file %s.\n", outfilename);
terminatetetgen(this, 3);
}
// Number of voronoi points, 3 dim, no attributes, no marker.
fprintf(outfile, "%ld 3 0 0\n", ntets);
} else {
// Allocate space for 'vpointlist'.
out->numberofvpoints = (int) ntets;
out->vpointlist = new REAL[out->numberofvpoints * 3];
if (out->vpointlist == (REAL *) NULL) {
terminatetetgen(this, 1);
}
}
// Output Voronoi vertices (the circumcenters of tetrahedra).
tetrahedrons->traversalinit();
tetloop.tet = tetrahedrontraverse();
vpointcount = 0; // The (internal) v-index always starts from 0.
index = 0;
while (tetloop.tet != (tetrahedron *) NULL) {
for (i = 0; i < 4; i++) {
pt[i] = (point) tetloop.tet[4 + i];
setpoint2tet(pt[i], encode(tetloop));
}
if (b->weighted) {
orthosphere(pt[0], pt[1], pt[2], pt[3], pt[0][3], pt[1][3], pt[2][3],
pt[3][3], ccent, NULL);
} else {
circumsphere(pt[0], pt[1], pt[2], pt[3], ccent, NULL);
}
if (out == (tetgenio *) NULL) {
fprintf(outfile, "%4d %16.8e %16.8e %16.8e\n", vpointcount + shift,
ccent[0], ccent[1], ccent[2]);
} else {
out->vpointlist[index++] = ccent[0];
out->vpointlist[index++] = ccent[1];
out->vpointlist[index++] = ccent[2];
}
setelemindex(tetloop.tet, vpointcount);
vpointcount++;
tetloop.tet = tetrahedrontraverse();
}
if (out == (tetgenio *) NULL) {
fprintf(outfile, "# Generated by %s\n", b->commandline);
fclose(outfile);
}
// Output Voronoi edges to .v.edge file.
if (out == (tetgenio *) NULL) {
strcpy(outfilename, b->outfilename);
strcat(outfilename, ".v.edge");
}
if (!b->quiet) {
if (out == (tetgenio *) NULL) {
printf("Writing %s.\n", outfilename);
} else {
printf("Writing Voronoi edges.\n");
}
}
if (out == (tetgenio *) NULL) {
outfile = fopen(outfilename, "w");
if (outfile == (FILE *) NULL) {
printf("File I/O Error: Cannot create file %s.\n", outfilename);
terminatetetgen(this, 3);
}
// Number of Voronoi edges, no marker.
fprintf(outfile, "%ld 0\n", faces);
} else {
// Allocate space for 'vpointlist'.
out->numberofvedges = (int) faces;
out->vedgelist = new tetgenio::voroedge[out->numberofvedges];
}
// Output the Voronoi edges.
tetrahedrons->traversalinit();
tetloop.tet = tetrahedrontraverse();
vedgecount = 0; // D-Face (V-edge) index (from zero).
index = 0; // The Delaunay-face index.
while (tetloop.tet != (tetrahedron *) NULL) {
// Count the number of Voronoi edges. Look at the four faces of each
// tetrahedron. Count the face if the tetrahedron's index is
// smaller than its neighbor's or the neighbor is outside.
end1 = elemindex(tetloop.tet);
for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) {
fsym(tetloop, worktet);
if (ishulltet(worktet) ||
(elemindex(tetloop.tet) < elemindex(worktet.tet))) {
// Found a Voronoi edge. Operate on it.
if (out == (tetgenio *) NULL) {
fprintf(outfile, "%4d %4d", vedgecount + shift, end1 + shift);
} else {
vedge = &(out->vedgelist[index++]);
vedge->v1 = end1 + shift;
}
if (!ishulltet(worktet)) {
end2 = elemindex(worktet.tet);
} else {
end2 = -1;
}
// Note that end2 may be -1 (worktet.tet is outside).
if (end2 == -1) {
// Calculate the out normal of this hull face.
pt[0] = dest(worktet);
pt[1] = org(worktet);
pt[2] = apex(worktet);
for (j = 0; j < 3; j++) vec1[j] = pt[1][j] - pt[0][j];
for (j = 0; j < 3; j++) vec2[j] = pt[2][j] - pt[0][j];
cross(vec1, vec2, infvec);
// Normalize it.
L = sqrt(infvec[0] * infvec[0] + infvec[1] * infvec[1]
+ infvec[2] * infvec[2]);
if (L > 0) for (j = 0; j < 3; j++) infvec[j] /= L;
if (out == (tetgenio *) NULL) {
fprintf(outfile, " -1");
fprintf(outfile, " %g %g %g\n", infvec[0], infvec[1], infvec[2]);
} else {
vedge->v2 = -1;
vedge->vnormal[0] = infvec[0];
vedge->vnormal[1] = infvec[1];
vedge->vnormal[2] = infvec[2];
}
} else {
if (out == (tetgenio *) NULL) {
fprintf(outfile, " %4d\n", end2 + shift);
} else {
vedge->v2 = end2 + shift;
vedge->vnormal[0] = 0.0;
vedge->vnormal[1] = 0.0;
vedge->vnormal[2] = 0.0;
}
}
// Save the V-edge index in this tet and its neighbor.
fidxs = (int *) (tetloop.tet[11]);
fidxs[tetloop.ver] = vedgecount;
fidxs = (int *) (worktet.tet[11]);
fidxs[worktet.ver & 3] = vedgecount;
vedgecount++;
}
} // tetloop.ver
tetloop.tet = tetrahedrontraverse();
}
if (out == (tetgenio *) NULL) {
fprintf(outfile, "# Generated by %s\n", b->commandline);
fclose(outfile);
}
// Output Voronoi faces to .v.face file.
if (out == (tetgenio *) NULL) {
strcpy(outfilename, b->outfilename);
strcat(outfilename, ".v.face");
}
if (!b->quiet) {
if (out == (tetgenio *) NULL) {
printf("Writing %s.\n", outfilename);
} else {
printf("Writing Voronoi faces.\n");
}
}
if (out == (tetgenio *) NULL) {
outfile = fopen(outfilename, "w");
if (outfile == (FILE *) NULL) {
printf("File I/O Error: Cannot create file %s.\n", outfilename);
terminatetetgen(this, 3);
}
// Number of Voronoi faces.
fprintf(outfile, "%ld 0\n", edges);
} else {
out->numberofvfacets = edges;
out->vfacetlist = new tetgenio::vorofacet[out->numberofvfacets];
if (out->vfacetlist == (tetgenio::vorofacet *) NULL) {
terminatetetgen(this, 1);
}
}
// Output the Voronoi facets.
tetrahedrons->traversalinit();
tetloop.tet = tetrahedrontraverse();
vfacecount = 0; // D-edge (V-facet) index (from zero).
while (tetloop.tet != (tetrahedron *) NULL) {
// Count the number of Voronoi faces. Look at the six edges of each
// tetrahedron. Count the edge only if the tetrahedron's index is
// smaller than those of all other tetrahedra that share the edge.
worktet.tet = tetloop.tet;
for (i = 0; i < 6; i++) {
worktet.ver = edge2ver[i];
// Count the number of faces at this edge. If the edge is a hull edge,
// the face containing dummypoint is also counted.
//ishulledge = 0; // Is it a hull edge.
tcount = 0;
firsttet = worktet;
spintet = worktet;
while (1) {
tcount++;
fnextself(spintet);
if (spintet.tet == worktet.tet) break;
if (!ishulltet(spintet)) {
if (elemindex(spintet.tet) < elemindex(worktet.tet)) break;
} else {
//ishulledge = 1;
if (apex(spintet) == dummypoint) {
// We make this V-edge appear in the end of the edge list.
fnext(spintet, firsttet);
}
}
} // while (1)
if (spintet.tet == worktet.tet) {
// Found a Voronoi facet. Operate on it.
pt[0] = org(worktet);
pt[1] = dest(worktet);
end1 = pointmark(pt[0]) - in->firstnumber; // V-cell index
end2 = pointmark(pt[1]) - in->firstnumber;
if (out == (tetgenio *) NULL) {
fprintf(outfile, "%4d %4d %4d %-2d ", vfacecount + shift,
end1 + shift, end2 + shift, tcount);
} else {
vfacet = &(out->vfacetlist[vfacecount]);
vfacet->c1 = end1 + shift;
vfacet->c2 = end2 + shift;
vfacet->elist = new int[tcount + 1];
vfacet->elist[0] = tcount;
index = 1;
}
// Output V-edges of this V-facet.
spintet = firsttet; //worktet;
while (1) {
fidxs = (int *) (spintet.tet[11]);
if (apex(spintet) != dummypoint) {
vedgecount = fidxs[spintet.ver & 3];
ishullface = 0;
} else {
ishullface = 1; // It's not a real face.
}
if (out == (tetgenio *) NULL) {
fprintf(outfile, " %d", !ishullface ? (vedgecount + shift) : -1);
} else {
vfacet->elist[index++] = !ishullface ? (vedgecount + shift) : -1;
}
// Save the V-facet index in this tet at this edge.
eidxs = &(fidxs[4]);
eidxs[ver2edge[spintet.ver]] = vfacecount;
// Go to the next face.
fnextself(spintet);
if (spintet.tet == firsttet.tet) break;
} // while (1)
if (out == (tetgenio *) NULL) {
fprintf(outfile, "\n");
}
vfacecount++;
} // if (spintet.tet == worktet.tet)
} // if (i = 0; i < 6; i++)
tetloop.tet = tetrahedrontraverse();
}
if (out == (tetgenio *) NULL) {
fprintf(outfile, "# Generated by %s\n", b->commandline);
fclose(outfile);
}
// Output Voronoi cells to .v.cell file.
if (out == (tetgenio *) NULL) {
strcpy(outfilename, b->outfilename);
strcat(outfilename, ".v.cell");
}
if (!b->quiet) {
if (out == (tetgenio *) NULL) {
printf("Writing %s.\n", outfilename);
} else {
printf("Writing Voronoi cells.\n");
}
}
if (out == (tetgenio *) NULL) {
outfile = fopen(outfilename, "w");
if (outfile == (FILE *) NULL) {
printf("File I/O Error: Cannot create file %s.\n", outfilename);
terminatetetgen(this, 3);
}
// Number of Voronoi cells.
fprintf(outfile, "%ld\n", points->items - unuverts - dupverts);
} else {
out->numberofvcells = points->items - unuverts - dupverts;
out->vcelllist = new int*[out->numberofvcells];
if (out->vcelllist == (int **) NULL) {
terminatetetgen(this, 1);
}
}
// Output Voronoi cells.
tetlist = cavetetlist;
ptlist = cavetetvertlist;
points->traversalinit();
ploop = pointtraverse();
vpointcount = 0;
while (ploop != (point) NULL) {
if ((pointtype(ploop) != UNUSEDVERTEX) &&
(pointtype(ploop) != DUPLICATEDVERTEX) &&
(pointtype(ploop) != NREGULARVERTEX)) {
getvertexstar(1, ploop, tetlist, ptlist, NULL);
// Mark all vertices. Check if it is a hull vertex.
ishullvert = 0;
for (i = 0; i < ptlist->objects; i++) {
neipt = * (point *) fastlookup(ptlist, i);
if (neipt != dummypoint) {
pinfect(neipt);
} else {
ishullvert = 1;
}
}
tcount = (int) ptlist->objects;
if (out == (tetgenio *) NULL) {
fprintf(outfile, "%4d %-2d ", vpointcount + shift, tcount);
} else {
arraysize = tcount;
vertarray = new int[arraysize + 1];
out->vcelllist[vpointcount] = vertarray;
vertarray[0] = tcount;
index = 1;
}
// List Voronoi facets bounding this cell.
for (i = 0; i < tetlist->objects; i++) {
worktet = * (triface *) fastlookup(tetlist, i);
// Let 'worktet' be [a,b,c,d] where d = ploop.
for (j = 0; j < 3; j++) {
neipt = org(worktet); // neipt is a, or b, or c
// Skip the dummypoint.
if (neipt != dummypoint) {
if (pinfected(neipt)) {
// It's not processed yet.
puninfect(neipt);
// Go to the DT edge [a,d], or [b,d], or [c,d].
esym(worktet, spintet);
enextself(spintet);
// Get the V-face dual to this edge.
eidxs = (int *) spintet.tet[11];
vfacecount = eidxs[4 + ver2edge[spintet.ver]];
if (out == (tetgenio *) NULL) {
fprintf(outfile, " %d", vfacecount + shift);
} else {
vertarray[index++] = vfacecount + shift;
}
}
}
enextself(worktet);
} // j
} // i
if (ishullvert) {
// Add a hull facet (-1) to the facet list.
if (out == (tetgenio *) NULL) {
fprintf(outfile, " -1");
} else {
vertarray[index++] = -1;
}
}
if (out == (tetgenio *) NULL) {
fprintf(outfile, "\n");
}
tetlist->restart();
ptlist->restart();
vpointcount++;
}
ploop = pointtraverse();
}
// Delete the space for face/edge indices.
delete [] indexarray;
if (out == (tetgenio *) NULL) {
fprintf(outfile, "# Generated by %s\n", b->commandline);
fclose(outfile);
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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)
{
FILE *outfile;
char nodfilename[FILENAMESIZE];
char smefilename[FILENAMESIZE];
face faceloop;
point p1, p2, p3;
int firstindex, shift;
int bmark;
int faceid, marker;
int i;
if (smfilename != (char *) NULL && smfilename[0] != '\0') {
strcpy(smefilename, smfilename);
} else if (b->outfilename[0] != '\0') {
strcpy(smefilename, b->outfilename);
} else {
strcpy(smefilename, "unnamed");
}
strcpy(nodfilename, smefilename);
strcat(smefilename, ".smesh");
strcat(nodfilename, ".node");
if (!b->quiet) {
printf("Writing %s.\n", smefilename);
}
outfile = fopen(smefilename, "w");
if (outfile == (FILE *) NULL) {
printf("File I/O Error: Cannot create file %s.\n", smefilename);
return;
}
// Determine the first index (0 or 1).
firstindex = b->zeroindex ? 0 : in->firstnumber;
shift = 0; // Default no shiftment.
if ((in->firstnumber == 1) && (firstindex == 0)) {
shift = 1; // Shift the output indices by 1.
}
fprintf(outfile, "# %s. TetGen's input file.\n", smefilename);
fprintf(outfile, "\n# part 1: node list.\n");
fprintf(outfile, "0 3 0 0 # nodes are found in %s.\n", nodfilename);
marker = 0; // avoid compile warning.
bmark = !b->nobound && in->facetmarkerlist;
fprintf(outfile, "\n# part 2: facet list.\n");
// Number of facets, boundary marker.
fprintf(outfile, "%ld %d\n", subfaces->items, bmark);
subfaces->traversalinit();
faceloop.sh = shellfacetraverse(subfaces);
while (faceloop.sh != (shellface *) NULL) {
p1 = sorg(faceloop);
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.
}
}
fprintf(outfile, "3 %4d %4d %4d", pointmark(p1) - shift,
pointmark(p2) - shift, pointmark(p3) - shift);
if (bmark) {
fprintf(outfile, " %d", marker);
}
fprintf(outfile, "\n");
faceloop.sh = shellfacetraverse(subfaces);
}
// Copy input holelist.
fprintf(outfile, "\n# part 3: hole list.\n");
fprintf(outfile, "%d\n", in->numberofholes);
for (i = 0; i < in->numberofholes; i++) {
fprintf(outfile, "%d %g %g %g\n", i + in->firstnumber,
in->holelist[i * 3], in->holelist[i * 3 + 1],
in->holelist[i * 3 + 2]);
}
// Copy input regionlist.
fprintf(outfile, "\n# part 4: region list.\n");
fprintf(outfile, "%d\n", in->numberofregions);
for (i = 0; i < in->numberofregions; i++) {
fprintf(outfile, "%d %g %g %g %d %g\n", i + in->firstnumber,
in->regionlist[i * 5], in->regionlist[i * 5 + 1],
in->regionlist[i * 5 + 2], (int) in->regionlist[i * 5 + 3],
in->regionlist[i * 5 + 4]);
}
fprintf(outfile, "# Generated by %s\n", b->commandline);
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.//
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::outmesh2medit(char* mfilename)
{
FILE *outfile;
char mefilename[FILENAMESIZE];
tetrahedron* tetptr;
triface tface, tsymface;
face segloop, checkmark;
point ptloop, p1, p2, p3, p4;
long ntets, faces;
int pointnumber;
int faceid, marker;
int i;
if (mfilename != (char *) NULL && mfilename[0] != '\0') {
strcpy(mefilename, mfilename);
} else if (b->outfilename[0] != '\0') {
strcpy(mefilename, b->outfilename);
} else {
strcpy(mefilename, "unnamed");
}
strcat(mefilename, ".mesh");
if (!b->quiet) {
printf("Writing %s.\n", mefilename);
}
outfile = fopen(mefilename, "w");
if (outfile == (FILE *) NULL) {
printf("File I/O Error: Cannot create file %s.\n", mefilename);
return;
}
fprintf(outfile, "MeshVersionFormatted 1\n");
fprintf(outfile, "\n");
fprintf(outfile, "Dimension\n");
fprintf(outfile, "3\n");
fprintf(outfile, "\n");
fprintf(outfile, "\n# Set of mesh vertices\n");
fprintf(outfile, "Vertices\n");
fprintf(outfile, "%ld\n", points->items);
points->traversalinit();
ptloop = pointtraverse();
pointnumber = 1; // Medit need start number form 1.
while (ptloop != (point) NULL) {
// Point coordinates.
fprintf(outfile, "%.17g %.17g %.17g", ptloop[0], ptloop[1], ptloop[2]);
if (in->numberofpointattributes > 0) {
// Write an attribute, ignore others if more than one.
fprintf(outfile, " %.17g\n", ptloop[3]);
} else {
fprintf(outfile, " 0\n");
}
setpointmark(ptloop, pointnumber);
ptloop = pointtraverse();
pointnumber++;
}
// Compute the number of faces.
ntets = tetrahedrons->items - hullsize;
faces = (ntets * 4l + hullsize) / 2l;
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);
}
}
tface.tet = tetrahedrontraverse();
}
fprintf(outfile, "\n# Set of Tetrahedra\n");
fprintf(outfile, "Tetrahedra\n");
fprintf(outfile, "%ld\n", ntets);
tetrahedrons->traversalinit();
tetptr = tetrahedrontraverse();
while (tetptr != (tetrahedron *) NULL) {
if (!b->reversetetori) {
p1 = (point) tetptr[4];
p2 = (point) tetptr[5];
} else {
p1 = (point) tetptr[5];
p2 = (point) tetptr[4];
}
p3 = (point) tetptr[6];
p4 = (point) tetptr[7];
fprintf(outfile, "%5d %5d %5d %5d",
pointmark(p1), pointmark(p2), pointmark(p3), pointmark(p4));
if (numelemattrib > 0) {
fprintf(outfile, " %.17g", elemattribute(tetptr, 0));
} else {
fprintf(outfile, " 0");
}
fprintf(outfile, "\n");
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);
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);
}
}
fprintf(outfile, "\nEnd\n");
fclose(outfile);
}
///////////////////////////////////////////////////////////////////////////////
// //
// outmesh2vtk() Save mesh to file in VTK Legacy format. //
// //
// This function was contributed by Bryn Llyod from ETH, 2007. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetgenmesh::outmesh2vtk(char* ofilename)
{
FILE *outfile;
char vtkfilename[FILENAMESIZE];
point pointloop, p1, p2, p3, p4;
tetrahedron* tptr;
double x, y, z;
int n1, n2, n3, n4;
int nnodes = 4;
int celltype = 10;
if (b->order == 2) {
printf(" Write VTK not implemented for order 2 elements \n");
return;
}
int NEL = tetrahedrons->items - hullsize;
int NN = points->items;
if (ofilename != (char *) NULL && ofilename[0] != '\0') {
strcpy(vtkfilename, ofilename);
} else if (b->outfilename[0] != '\0') {
strcpy(vtkfilename, b->outfilename);
} else {
strcpy(vtkfilename, "unnamed");
}
strcat(vtkfilename, ".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();
tptr = tetrahedrontraverse();
//elementnumber = firstindex; // in->firstnumber;
while (tptr != (tetrahedron *) NULL) {
if (!b->reversetetori) {
p1 = (point) tptr[4];
p2 = (point) tptr[5];
} else {
p1 = (point) tptr[5];
p2 = (point) tptr[4];
}
p3 = (point) tptr[6];
p4 = (point) tptr[7];
n1 = pointmark(p1) - in->firstnumber;
n2 = pointmark(p2) - in->firstnumber;
n3 = pointmark(p3) - in->firstnumber;
n4 = pointmark(p4) - in->firstnumber;
fprintf(outfile, "%d %4d %4d %4d %4d\n", nnodes, n1, n2, n3, n4);
tptr = tetrahedrontraverse();
}
fprintf(outfile, "\n");
fprintf(outfile, "CELL_TYPES %d\n", NEL);
for(int tid=0; tid 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");
tetrahedrons->traversalinit();
tptr = tetrahedrontraverse();
while (tptr != (tetrahedron *) NULL) {
fprintf(outfile, "%d\n", (int) elemattribute(tptr, numelemattrib - 1));
tptr = tetrahedrontraverse();
}
fprintf(outfile, "\n");
}
fclose(outfile);
}
//// ////
//// ////
//// 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)
REAL cps = (REAL) CLOCKS_PER_SEC;
tv[0] = clock();
m.b = b;
m.in = in;
m.addin = addin;
if (b->metric && bgmin && (bgmin->numberofpoints > 0)) {
m.bgm = new tetgenmesh(); // Create an empty background mesh.
m.bgm->b = b;
m.bgm->in = bgmin;
}
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();
if (b->refine) { // -r
m.reconstructmesh();
} else { // -p
m.incrementaldelaunay(ts[0]);
}
tv[2] = clock();
if (!b->quiet) {
if (b->refine) {
printf("Mesh reconstruction seconds: %g\n", ((REAL)(tv[2]-tv[1])) / cps);
} else {
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);
}
}
}
if (b->plc && !b->refine) { // -p
m.meshsurface();
ts[0] = clock();
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
m.bgm->initializepools();
m.bgm->transfernodes();
m.bgm->reconstructmesh();
ts[0] = clock();
if (!b->quiet) {
printf("Background mesh reconstruct seconds: %g\n",
((REAL)(ts[0] - tv[3])) / cps);
}
if (b->metric) { // -m
m.interpolatemeshsize();
ts[1] = clock();
if (!b->quiet) {
printf("Size interpolating seconds: %g\n",((REAL)(ts[1]-ts[0])) / cps);
}
}
}
tv[4] = clock();
if (b->plc && !b->refine) { // -p
if (b->nobisect) { // -Y
m.recoverboundary(ts[0]);
} else {
m.constraineddelaunay(ts[0]);
}
ts[1] = clock();
if (!b->quiet) {
if (b->nobisect) {
printf("Boundary recovery ");
} else {
printf("Constrained Delaunay ");
}
printf("seconds: %g\n", ((REAL)(ts[1] - tv[4])) / cps);
if (b->verbose) {
printf(" Segment recovery seconds: %g\n",((REAL)(ts[0]-tv[4]))/ cps);
printf(" Facet recovery seconds: %g\n", ((REAL)(ts[1]-ts[0])) / cps);
}
}
m.carveholes();
ts[2] = clock();
if (!b->quiet) {
printf("Exterior tets removal seconds: %g\n",((REAL)(ts[2]-ts[1]))/cps);
}
if (b->nobisect) { // -Y
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);
}
}
}
}
tv[5] = clock();
if (b->coarsen) { // -R
m.meshcoarsening();
}
tv[6] = clock();
if (!b->quiet) {
if (b->coarsen) {
printf("Mesh coarsening seconds: %g\n", ((REAL)(tv[6] - tv[5])) / cps);
}
}
if ((b->plc && b->nobisect) || b->coarsen) {
m.recoverdelaunay();
}
tv[7] = clock();
if (!b->quiet) {
if ((b->plc && b->nobisect) || b->coarsen) {
printf("Delaunay recovery seconds: %g\n", ((REAL)(tv[7] - tv[6]))/cps);
}
}
if ((b->plc || b->refine) && b->insertaddpoints) { // -i
if ((addin != NULL) && (addin->numberofpoints > 0)) {
m.insertconstrainedpoints(addin);
}
}
tv[8] = clock();
if (!b->quiet) {
if ((b->plc || b->refine) && b->insertaddpoints) { // -i
if ((addin != NULL) && (addin->numberofpoints > 0)) {
printf("Constrained points seconds: %g\n", ((REAL)(tv[8]-tv[7]))/cps);
}
}
}
if (b->quality) {
m.delaunayrefinement();
}
tv[9] = clock();
if (!b->quiet) {
if (b->quality) {
printf("Refinement seconds: %g\n", ((REAL)(tv[9] - tv[8])) / cps);
}
}
if ((b->plc || b->refine) && (b->optlevel > 0)) {
m.optimizemesh();
}
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->nojettison && ((m.dupverts > 0) || (m.unuverts > 0)
|| (b->refine && (in->numberofcorners == 10)))) {
m.jettisonnodes();
}
if ((b->order == 2) && !b->convex) {
m.highorder();
}
if (!b->quiet) {
printf("\n");
}
if (out != (tetgenio *) NULL) {
out->firstnumber = in->firstnumber;
out->mesh_dim = in->mesh_dim;
}
if (b->nonodewritten || b->noiterationnum) {
if (!b->quiet) {
printf("NOT writing a .node file.\n");
}
} else {
m.outnodes(out);
}
if (b->noelewritten) {
if (!b->quiet) {
printf("NOT writing an .ele file.\n");
}
} else {
if (m.tetrahedrons->items > 0l) {
m.outelements(out);
}
}
if (b->nofacewritten) {
if (!b->quiet) {
printf("NOT writing an .face file.\n");
}
} else {
if (b->facesout) {
if (m.tetrahedrons->items > 0l) {
m.outfaces(out); // Output all faces.
}
} else {
if (b->plc || b->refine) {
if (m.subfaces->items > 0l) {
m.outsubfaces(out); // Output boundary faces.
}
} else {
if (m.tetrahedrons->items > 0l) {
m.outhullfaces(out); // Output convex hull faces.
}
}
}
}
if (b->nofacewritten) {
if (!b->quiet) {
printf("NOT writing an .edge file.\n");
}
} else {
if (b->edgesout) { // -e
m.outedges(out); // output all mesh edges.
} else {
if (b->plc || b->refine) {
m.outsubsegments(out); // output subsegments.
}
}
}
if ((b->plc || b->refine) && b->metric) { // -m
m.outmetrics(out);
}
if (!out && b->plc &&
((b->object == tetgenbehavior::OFF) ||
(b->object == tetgenbehavior::PLY) ||
(b->object == tetgenbehavior::STL))) {
m.outsmesh(b->outfilename);
}
if (!out && b->meditview) {
m.outmesh2medit(b->outfilename);
}
if (!out && b->vtkview) {
m.outmesh2vtk(b->outfilename);
}
if (b->neighout) {
m.outneighbors(out);
}
if ((!(b->plc || b->refine)) && b->voroout) {
m.outvoronoi(out);
}
tv[11] = 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);
}
if (b->docheck) {
m.checkmesh(0);
if (b->plc || b->refine) {
m.checkshells();
m.checksegments();
}
if (b->docheck > 1) {
m.checkdelaunay();
}
}
if (!b->quiet) {
m.statistics();
}
exactdeinit();
}
#ifndef TETLIBRARY
///////////////////////////////////////////////////////////////////////////////
// //
// main() The command line interface of TetGen. //
// //
///////////////////////////////////////////////////////////////////////////////
int main(int argc, char *argv[])
#else // with TETLIBRARY
///////////////////////////////////////////////////////////////////////////////
// //
// tetrahedralize() The library interface of TetGen. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetrahedralize(char *switches, tetgenio *in, tetgenio *out,
tetgenio *addin, tetgenio *bgmin)
#endif // not TETLIBRARY
{
tetgenbehavior b;
#ifndef TETLIBRARY
tetgenio in, addin, bgmin;
if (!b.parse_commandline(argc, argv)) {
terminatetetgen(NULL, 10);
}
// Read input files.
if (b.refine) { // -r
if (!in.load_tetmesh(b.infilename, (int) b.object)) {
terminatetetgen(NULL, 10);
}
} else { // -p
if (!in.load_plc(b.infilename, (int) b.object)) {
terminatetetgen(NULL, 10);
}
}
if (b.insertaddpoints) { // -i
// Try to read a .a.node file.
addin.load_node(b.addinfilename);
}
if (b.metric) { // -m
// Try to read a background mesh in files .b.node, .b.ele.
bgmin.load_tetmesh(b.bgmeshfilename, (int) b.object);
}
tetrahedralize(&b, &in, NULL, &addin, &bgmin);
return 0;
#else // with TETLIBRARY
if (!b.parse_commandline(switches)) {
terminatetetgen(NULL, 10);
}
tetrahedralize(&b, in, out, addin, bgmin);
#endif // not TETLIBRARY
}
//// ////
//// ////
//// main_cxx /////////////////////////////////////////////////////////////////
================================================
FILE: src/cpp/tetgen.h
================================================
///////////////////////////////////////////////////////////////////////////////
// //
// 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. //
// //
///////////////////////////////////////////////////////////////////////////////
#ifndef tetgenH
#define tetgenH
// To compile TetGen as a library instead of an executable program, define
// the TETLIBRARY symbol.
// #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
// memory is limited.
#define REAL double // #define REAL float
// 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).
#define INPUTLINESIZE 2048
// TetGen only uses the C standard library.
#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. //
// //
///////////////////////////////////////////////////////////////////////////////
class tetgenio {
public:
// A "polygon" describes a simple polygon (no holes). It is not necessarily
// convex. Each polygon contains a number of corners (points) and the same
// number of sides (edges). The points of the polygon must be given in
// either counterclockwise or clockwise order and they form a ring, so
// every two consecutive points forms an edge of the polygon.
struct polygon {
int *vertexlist;
int numberofvertices;
polygon();
~polygon();
};
// A "facet" describes a polygonal region possibly with holes, edges, and
// points floating in it. Each facet consists of a list of polygons and
// a list of hole points (which lie strictly inside holes).
struct facet{
polygon *polygonlist;
int numberofpolygons;
REAL *holelist;
int numberofholes;
facet();
~facet();
};
// A "voroedge" is an edge of the Voronoi diagram. It corresponds to a
// Delaunay face. Each voroedge is either a line segment connecting
// two Voronoi vertices or a ray starting from a Voronoi vertex to an
// "infinite vertex". 'v1' and 'v2' are two indices pointing to the
// list of Voronoi vertices. 'v1' must be non-negative, while 'v2' may
// be -1 if it is a ray, in this case, the unit normal of this ray is
// given in 'vnormal'.
typedef struct {
int v1, v2;
REAL vnormal[3];
} voroedge;
// A "vorofacet" is an facet of the Voronoi diagram. It corresponds to a
// Delaunay edge. Each Voronoi facet is a convex polygon formed by a
// list of Voronoi edges, it may not be closed. 'c1' and 'c2' are two
// indices pointing into the list of Voronoi cells, i.e., the two cells
// share this facet. 'elist' is an array of indices pointing into the
// list of Voronoi edges, 'elist[0]' saves the number of Voronoi edges
// (including rays) of this facet.
typedef struct {
int c1, c2;
int *elist;
} vorofacet;
// Additional parameters associated with an input (or mesh) vertex.
// These informations are provided by CAD libraries.
typedef struct {
REAL uv[2];
int tag;
int type; // 0, 1, or 2.
} pointparam;
// Callback functions for meshing PSCs.
typedef REAL (* GetVertexParamOnEdge)(void*, int, int);
typedef void (* GetSteinerOnEdge)(void*, int, REAL, REAL*);
typedef void (* GetVertexParamOnFace)(void*, int, int, REAL*);
typedef void (* GetEdgeSteinerParamOnFace)(void*, int, REAL, int, REAL*);
typedef void (* GetSteinerOnFace)(void*, int, REAL*, REAL*);
// A callback function for mesh refinement.
typedef bool (* TetSizeFunc)(REAL*, REAL*, REAL*, REAL*, REAL*, REAL);
// Items are numbered starting from 'firstnumber' (0 or 1), default is 0.
int firstnumber;
// Dimension of the mesh (2 or 3), default is 3.
int mesh_dim;
// Does the lines in .node file contain index or not, default is 1.
int useindex;
// 'pointlist': An array of point coordinates. The first point's x
// coordinate is at index [0] and its y coordinate at index [1], its
// z coordinate is at index [2], followed by the coordinates of the
// remaining points. Each point occupies three REALs.
// 'pointattributelist': An array of point attributes. Each point's
// attributes occupy 'numberofpointattributes' REALs.
// 'pointmtrlist': An array of metric tensors at points. Each point's
// tensor occupies 'numberofpointmtr' REALs.
// 'pointmarkerlist': An array of point markers; one integer per point.
REAL *pointlist;
REAL *pointattributelist;
REAL *pointmtrlist;
int *pointmarkerlist;
pointparam *pointparamlist;
int numberofpoints;
int numberofpointattributes;
int numberofpointmtrs;
// 'tetrahedronlist': An array of tetrahedron corners. The first
// tetrahedron's first corner is at index [0], followed by its other
// corners, followed by six nodes on the edges of the tetrahedron if the
// second order option (-o2) is applied. Each tetrahedron occupies
// 'numberofcorners' ints. The second order nodes are ouput only.
// 'tetrahedronattributelist': An array of tetrahedron attributes. Each
// tetrahedron's attributes occupy 'numberoftetrahedronattributes' REALs.
// 'tetrahedronvolumelist': An array of constraints, i.e. tetrahedron's
// volume; one REAL per element. Input only.
// 'neighborlist': An array of tetrahedron neighbors; 4 ints per element.
// Output only.
int *tetrahedronlist;
REAL *tetrahedronattributelist;
REAL *tetrahedronvolumelist;
int *neighborlist;
int numberoftetrahedra;
int numberofcorners;
int numberoftetrahedronattributes;
// 'facetlist': An array of facets. Each entry is a structure of facet.
// 'facetmarkerlist': An array of facet markers; one int per facet.
facet *facetlist;
int *facetmarkerlist;
int numberoffacets;
// 'holelist': An array of holes (in volume). Each hole is given by a
// seed (point) which lies strictly inside it. The first seed's x, y and z
// coordinates are at indices [0], [1] and [2], followed by the
// remaining seeds. Three REALs per hole.
REAL *holelist;
int numberofholes;
// 'regionlist': An array of regions (subdomains). Each region is given by
// a seed (point) which lies strictly inside it. The first seed's x, y and
// z coordinates are at indices [0], [1] and [2], followed by the regional
// attribute at index [3], followed by the maximum volume at index [4].
// Five REALs per region.
// Note that each regional attribute is used only if you select the 'A'
// switch, and each volume constraint is used only if you select the
// 'a' switch (with no number following).
REAL *regionlist;
int numberofregions;
// '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
// maximum area bound at index [1], followed by the remaining facet con-
// straints. Two REALs per facet constraint. Note: the facet marker is
// actually an integer.
REAL *facetconstraintlist;
int numberoffacetconstraints;
// 'segmentconstraintlist': An array of segment constraints. Each constraint
// specifies a maximum length bound on the subsegments of that segment.
// The first constraint is given by the two endpoints of the segment at
// index [0] and [1], and the maximum length bound at index [2], followed
// by the remaining segment constraints. Three REALs per constraint.
// Note the segment endpoints are actually integers.
REAL *segmentconstraintlist;
int numberofsegmentconstraints;
// 'trifacelist': An array of face (triangle) corners. The first face's
// three corners are at indices [0], [1] and [2], followed by the remaining
// faces. Three ints per face.
// 'trifacemarkerlist': An array of face markers; one int per face.
// 'o2facelist': An array of second order nodes (on the edges) of the face.
// 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.
int *trifacelist;
int *trifacemarkerlist;
int *o2facelist;
int *adjtetlist;
int numberoftrifaces;
// 'edgelist': An array of edge endpoints. The first edge's endpoints
// are at indices [0] and [1], followed by the remaining edges.
// Two ints per edge.
// '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.
int *edgelist;
int *edgemarkerlist;
int *o2edgelist;
int *edgeadjtetlist;
int numberofedges;
// 'vpointlist': An array of Voronoi vertex coordinates (like pointlist).
// 'vedgelist': An array of Voronoi edges. Each entry is a 'voroedge'.
// 'vfacetlist': An array of Voronoi facets. Each entry is a 'vorofacet'.
// 'vcelllist': An array of Voronoi cells. Each entry is an array of
// indices pointing into 'vfacetlist'. The 0th entry is used to store
// the length of this array.
REAL *vpointlist;
voroedge *vedgelist;
vorofacet *vfacetlist;
int **vcelllist;
int numberofvpoints;
int numberofvedges;
int numberofvfacets;
int numberofvcells;
// Variable (and callback functions) for meshing PSCs.
void *geomhandle;
GetVertexParamOnEdge getvertexparamonedge;
GetSteinerOnEdge getsteineronedge;
GetVertexParamOnFace getvertexparamonface;
GetEdgeSteinerParamOnFace getedgesteinerparamonface;
GetSteinerOnFace getsteineronface;
// A callback function.
TetSizeFunc tetunsuitable;
// Input & output routines.
bool load_node_call(FILE* infile, int markers, int uvflag, char*);
bool load_node(char*);
bool load_edge(char*);
bool load_face(char*);
bool load_tet(char*);
bool load_vol(char*);
bool load_var(char*);
bool load_mtr(char*);
bool load_pbc(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_plc(char*, int);
bool load_tetmesh(char*, int);
void save_nodes(char*);
void save_elements(char*);
void save_faces(char*);
void save_edges(char*);
void save_neighbors(char*);
void save_poly(char*);
void save_faces2smesh(char*);
// Read line and parse string functions.
char *readline(char* string, FILE* infile, int *linenumber);
char *findnextfield(char* string);
char *readnumberline(char* string, FILE* infile, char* infilename);
char *findnextnumber(char* string);
static void init(polygon* p) {
p->vertexlist = (int *) NULL;
p->numberofvertices = 0;
}
static void init(facet* f) {
f->polygonlist = (polygon *) NULL;
f->numberofpolygons = 0;
f->holelist = (REAL *) NULL;
f->numberofholes = 0;
}
// Initialize routine.
void initialize()
{
firstnumber = 0;
mesh_dim = 3;
useindex = 1;
pointlist = (REAL *) NULL;
pointattributelist = (REAL *) NULL;
pointmtrlist = (REAL *) NULL;
pointmarkerlist = (int *) NULL;
pointparamlist = (pointparam *) NULL;
numberofpoints = 0;
numberofpointattributes = 0;
numberofpointmtrs = 0;
tetrahedronlist = (int *) NULL;
tetrahedronattributelist = (REAL *) NULL;
tetrahedronvolumelist = (REAL *) NULL;
neighborlist = (int *) NULL;
numberoftetrahedra = 0;
numberofcorners = 4;
numberoftetrahedronattributes = 0;
trifacelist = (int *) NULL;
trifacemarkerlist = (int *) NULL;
o2facelist = (int *) NULL;
adjtetlist = (int *) NULL;
numberoftrifaces = 0;
edgelist = (int *) NULL;
edgemarkerlist = (int *) NULL;
o2edgelist = (int *) NULL;
edgeadjtetlist = (int *) NULL;
numberofedges = 0;
facetlist = (facet *) NULL;
facetmarkerlist = (int *) NULL;
numberoffacets = 0;
holelist = (REAL *) NULL;
numberofholes = 0;
regionlist = (REAL *) NULL;
numberofregions = 0;
facetconstraintlist = (REAL *) NULL;
numberoffacetconstraints = 0;
segmentconstraintlist = (REAL *) NULL;
numberofsegmentconstraints = 0;
vpointlist = (REAL *) NULL;
vedgelist = (voroedge *) NULL;
vfacetlist = (vorofacet *) NULL;
vcelllist = (int **) NULL;
numberofvpoints = 0;
numberofvedges = 0;
numberofvfacets = 0;
numberofvcells = 0;
tetunsuitable = NULL;
geomhandle = NULL;
getvertexparamonedge = NULL;
getsteineronedge = NULL;
getvertexparamonface = NULL;
getedgesteinerparamonface = NULL;
getsteineronface = NULL;
}
// Free the memory allocated in 'tetgenio'. Note that it assumes that the
// memory was allocated by the "new" operator (C++).
void deinitialize()
{
int i, j;
if (pointlist != (REAL *) NULL) {
delete [] pointlist;
}
if (pointattributelist != (REAL *) NULL) {
delete [] pointattributelist;
}
if (pointmtrlist != (REAL *) NULL) {
delete [] pointmtrlist;
}
if (pointmarkerlist != (int *) NULL) {
delete [] pointmarkerlist;
}
if (pointparamlist != (pointparam *) NULL) {
delete [] pointparamlist;
}
if (tetrahedronlist != (int *) NULL) {
delete [] tetrahedronlist;
}
if (tetrahedronattributelist != (REAL *) NULL) {
delete [] tetrahedronattributelist;
}
if (tetrahedronvolumelist != (REAL *) NULL) {
delete [] tetrahedronvolumelist;
}
if (neighborlist != (int *) NULL) {
delete [] neighborlist;
}
if (trifacelist != (int *) NULL) {
delete [] trifacelist;
}
if (trifacemarkerlist != (int *) NULL) {
delete [] trifacemarkerlist;
}
if (o2facelist != (int *) NULL) {
delete [] o2facelist;
}
if (adjtetlist != (int *) NULL) {
delete [] adjtetlist;
}
if (edgelist != (int *) NULL) {
delete [] edgelist;
}
if (edgemarkerlist != (int *) NULL) {
delete [] edgemarkerlist;
}
if (o2edgelist != (int *) NULL) {
delete [] o2edgelist;
}
if (edgeadjtetlist != (int *) NULL) {
delete [] edgeadjtetlist;
}
if (facetlist != (facet *) NULL) {
delete [] facetlist;
}
if (facetmarkerlist != (int *) NULL) {
delete [] facetmarkerlist;
}
if (holelist != (REAL *) NULL) {
delete [] holelist;
}
if (regionlist != (REAL *) NULL) {
delete [] regionlist;
}
if (facetconstraintlist != (REAL *) NULL) {
delete [] facetconstraintlist;
}
if (segmentconstraintlist != (REAL *) NULL) {
delete [] segmentconstraintlist;
}
if (vpointlist != (REAL *) NULL) {
delete [] vpointlist;
}
if (vedgelist != (voroedge *) NULL) {
delete [] vedgelist;
}
if (vfacetlist != (vorofacet *) NULL) {
for (i = 0; i < numberofvfacets; i++) {
delete [] vfacetlist[i].elist;
}
delete [] vfacetlist;
}
if (vcelllist != (int **) NULL) {
for (i = 0; i < numberofvcells; i++) {
delete [] vcelllist[i];
}
delete [] vcelllist;
}
}
// Constructor & destructor.
tetgenio() {initialize();}
~tetgenio() {deinitialize();}
}; // 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. //
// //
///////////////////////////////////////////////////////////////////////////////
class tetgenbehavior {
public:
// Switches of TetGen.
int plc; // '-p', 0.
int psc; // '-s', 0.
int refine; // '-r', 0.
int quality; // '-q', 0.
int nobisect; // '-Y', 0.
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.
int nomergefacet; // '-M', 0.
int nomergevertex; // '-M', 0.
int noexact; // '-X', 0.
int nostaticfilter; // '-X', 0.
int zeroindex; // '-z', 0.
int facesout; // '-f', 0.
int edgesout; // '-e', 0.
int neighout; // '-n', 0.
int voroout; // '-v', 0.
int meditview; // '-g', 0.
int vtkview; // '-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 verbose; // '-V', 0.
// Parameters of TetGen.
int vertexperblock; // '-x', 4092.
int tetrahedraperblock; // '-x', 8188.
int shellfaceperblock; // '-x', 2044.
int nobisect_param; // '-Y', 2.
int addsteiner_algo; // '-Y/', 1.
int 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 delmaxfliplevel; // 1.
int order; // '-o', 1.
int steinerleft; // '-S', 0.
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 maxvolume; // '-a', -1.0.
REAL minratio; // '-q', 0.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 coarsen_percent; // -R1/#, 1.0.
// Strings of command line arguments and input/output file names.
char commandline[1024];
char infilename[1024];
char outfilename[1024];
char addinfilename[1024];
char bgmeshfilename[1024];
// The input object of TetGen. They are recognized by either the input
// file extensions or by the specified options.
// Currently the following objects are supported:
// - NODES, a list of nodes (.node);
// - POLY, a piecewise linear complex (.poly or .smesh);
// - OFF, a polyhedron (.off, Geomview's file format);
// - PLY, a polyhedron (.ply, file format from gatech, only ASCII);
// - STL, a surface mesh (.stl, stereolithography format);
// - MEDIT, a surface mesh (.mesh, Medit's file format);
// - 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;
void syntax();
void usage();
// Command line parse routine.
bool parse_commandline(int argc, char **argv);
bool parse_commandline(char *switches) {
return parse_commandline(0, &switches);
}
// Initialize all variables.
tetgenbehavior()
{
plc = 0;
psc = 0;
refine = 0;
quality = 0;
nobisect = 0;
coarsen = 0;
metric = 0;
weighted = 0;
brio_hilbert = 1;
incrflip = 0;
flipinsert = 0;
varvolume = 0;
fixedvolume = 0;
noexact = 0;
nostaticfilter = 0;
insertaddpoints = 0;
regionattrib = 0;
conforming = 0;
diagnose = 0;
convex = 0;
zeroindex = 0;
facesout = 0;
edgesout = 0;
neighout = 0;
voroout = 0;
meditview = 0;
vtkview = 0;
nobound = 0;
nonodewritten = 0;
noelewritten = 0;
nofacewritten = 0;
noiterationnum = 0;
nomergefacet = 0;
nomergevertex = 0;
nojettison = 0;
reversetetori = 0;
docheck = 0;
quiet = 0;
verbose = 0;
vertexperblock = 4092;
tetrahedraperblock = 8188;
shellfaceperblock = 4092;
nobisect_param = 2;
addsteiner_algo = 1;
coarsen_param = 0;
weighted_param = 0;
fliplinklevel = -1; // No limit on linklevel.
flipstarsize = -1; // No limit on flip star size.
fliplinklevelinc = 1;
reflevel = 3;
optscheme = 7; // 1 & 2 & 4, // min_max_dihedral.
optlevel = 2;
delmaxfliplevel = 1;
order = 1;
steinerleft = -1;
no_sort = 0;
hilbert_order = 52; //-1;
hilbert_limit = 8;
brio_threshold = 64;
brio_ratio = 0.125;
facet_ang_tol = 179.9;
maxvolume = -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
epsilon = 1.0e-8;
minedgelength = 0.0;
coarsen_percent = 1.0;
object = NODES;
commandline[0] = '\0';
infilename[0] = '\0';
outfilename[0] = '\0';
addinfilename[0] = '\0';
bgmeshfilename[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. //
// //
///////////////////////////////////////////////////////////////////////////////
namespace predicates {
void exactinit(int, int, int, REAL, REAL, REAL);
void exactdeinit();
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. //
// //
///////////////////////////////////////////////////////////////////////////////
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. //
// //
///////////////////////////////////////////////////////////////////////////////
// The tetrahedron data structure. It includes the following fields:
// - a list of four adjoining tetrahedra;
// - a list of four vertices;
// - a pointer to a list of four subfaces (optional, for -p switch);
// - a pointer to a list of six segments (optional, for -p switch);
// - a list of user-defined floating-point attributes (optional);
// - a volume constraint (optional, for -a switch);
// - an integer of element marker (and flags);
// The structure of a tetrahedron is an array of pointers. Its actual size
// (the length of the array) is determined at runtime.
typedef REAL **tetrahedron;
// The subface data structure. It includes the following fields:
// - a list of three adjoining subfaces;
// - a list of three vertices;
// - a list of three adjoining segments;
// - two adjoining tetrahedra;
// - an area constraint (optional, for -q switch);
// - an integer for boundary marker;
// - an integer for type, flags, etc.
typedef REAL **shellface;
// The point data structure. It includes the following fields:
// - x, y and z coordinates;
// - a list of user-defined point attributes (optional);
// - u, v coordinates (optional, for -s switch);
// - a metric tensor (optional, for -q or -m switch);
// - a pointer to an adjacent tetrahedron;
// - a pointer to a parent (or a duplicate) point;
// - a pointer to an adjacent subface or segment (optional, -p switch);
// - a pointer to a tet in background mesh (optional, for -m switch);
// - an integer for boundary marker (point index);
// - an integer for point type (and flags).
// - an integer for geometry tag (optional, for -s switch).
// The structure of a point is an array of REALs. Its acutal size is
// determined at the runtime.
typedef REAL *point;
///////////////////////////////////////////////////////////////////////////////
// //
// Handles //
// //
// Navigation and manipulation in a tetrahedralization are accomplished by //
// operating on structures referred as ``handles". A handle is a pair (t,v), //
// where t is a pointer to a tetrahedron, and v is a 4-bit integer, in the //
// range from 0 to 11. v is called the ``version'' of a tetrahedron, it rep- //
// resents a directed edge of a specific face of the tetrahedron. //
// //
// There are 12 even permutations of the four vertices, each of them corres- //
// ponds to a directed edge (a version) of the tetrahedron. The 12 versions //
// can be grouped into 4 distinct ``edge rings'' in 4 ``oriented faces'' of //
// this tetrahedron. One can encode each version (a directed edge) into a //
// 4-bit integer such that the two upper bits encode the index (from 0 to 2) //
// of this edge in the edge ring, and the two lower bits encode the index ( //
// from 0 to 3) of the oriented face which contains this edge. //
// //
// The four vertices of a tetrahedron are indexed from 0 to 3 (according to //
// their storage in the data structure). Give each face the same index as //
// the node opposite it in the tetrahedron. Denote the edge connecting face //
// i to face j as i/j. We number the twelve versions as follows: //
// //
// | edge 0 edge 1 edge 2 //
// --------|-------------------------------- //
// face 0 | 0 (0/1) 4 (0/3) 8 (0/2) //
// face 1 | 1 (1/2) 5 (1/3) 9 (1/0) //
// face 2 | 2 (2/3) 6 (2/1) 10 (2/0) //
// face 3 | 3 (3/0) 7 (3/1) 11 (3/2) //
// //
// Similarly, navigation and manipulation in a (boundary) triangulation are //
// done by using handles of triangles. Each handle is a pair (s, v), where s //
// is a pointer to a triangle, and v is a version in the range from 0 to 5. //
// Each version corresponds to a directed edge of this triangle. //
// //
// Number the three vertices of a triangle from 0 to 2 (according to their //
// storage in the data structure). Give each edge the same index as the node //
// opposite it in the triangle. The six versions of a triangle are: //
// //
// | edge 0 edge 1 edge 2 //
// ---------------|-------------------------- //
// ccw orieation | 0 2 4 //
// cw orieation | 1 3 5 //
// //
// In the following, a 'triface' is a handle of tetrahedron, and a 'face' is //
// a handle of a triangle. //
// //
///////////////////////////////////////////////////////////////////////////////
class triface {
public:
tetrahedron *tet;
int ver; // Range from 0 to 11.
triface() : tet(0), ver(0) {}
triface& operator=(const triface& t) {
tet = t.tet; ver = t.ver;
return *this;
}
};
class face {
public:
shellface *sh;
int shver; // Range from 0 to 5.
face() : sh(0), shver(0) {}
face& operator=(const face& s) {
sh = s.sh; shver = s.shver;
return *this;
}
};
///////////////////////////////////////////////////////////////////////////////
// //
// 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 {
public:
int objectbytes;
int objectsperblock;
int log2objectsperblock;
int objectsperblockmark;
int toparraylen;
char **toparray;
long objects;
unsigned long totalmemory;
void restart();
void poolinit(int sizeofobject, int log2objperblk);
char* getblock(int objectindex);
void* lookup(int objectindex);
int newindex(void **newptr);
arraypool(int sizeofobject, int log2objperblk);
~arraypool();
};
// fastlookup() -- A fast, unsafe operation. Return the pointer to the object
// with a given index. Note: The object's block must have been allocated,
// i.e., by the function newindex().
#define fastlookup(pool, index) \
(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. //
// //
///////////////////////////////////////////////////////////////////////////////
class memorypool {
public:
void **firstblock, **nowblock;
void *nextitem;
void *deaditemstack;
void **pathblock;
void *pathitem;
int alignbytes;
int itembytes, itemwords;
int itemsperblock;
long items, maxitems;
int unallocateditems;
int pathitemsleft;
memorypool();
memorypool(int, int, int, int);
~memorypool();
void poolinit(int, int, int, int);
void restart();
void *alloc();
void dealloc(void*);
void traversalinit();
void *traverse();
};
///////////////////////////////////////////////////////////////////////////////
// //
// badface //
// //
// Despite of its name, a 'badface' can be used to represent one of the //
// following objects: //
// - a face of a tetrahedron which is (possibly) non-Delaunay; //
// - an encroached subsegment or subface; //
// - a bad-quality tetrahedron, i.e, has too large radius-edge ratio; //
// - a sliver, i.e., has good radius-edge ratio but nearly zero volume; //
// - a recently flipped face (saved for undoing the flip later). //
// //
///////////////////////////////////////////////////////////////////////////////
class badface {
public:
triface tt;
face ss;
REAL key, cent[6]; // circumcenter or cos(dihedral angles) at 6 edges.
point forg, fdest, fapex, foppo, noppo;
badface *nextitem;
badface() : key(0), forg(0), fdest(0), fapex(0), foppo(0), noppo(0),
nextitem(0) {}
};
///////////////////////////////////////////////////////////////////////////////
// //
// insertvertexflags //
// //
// A collection of flags that pass to the routine insertvertex(). //
// //
///////////////////////////////////////////////////////////////////////////////
class insertvertexflags {
public:
int iloc; // input/output.
int bowywat, lawson;
int splitbdflag, validflag, respectbdflag;
int rejflag, chkencflag, cdtflag;
int assignmeshsize;
int sloc, sbowywat;
// Used by Delaunay refinement.
int refineflag; // 0, 1, 2, 3
triface refinetet;
face refinesh;
int smlenflag; // for useinsertradius.
REAL smlen; // for useinsertradius.
point parentpt;
insertvertexflags() {
iloc = bowywat = lawson = 0;
splitbdflag = validflag = respectbdflag = 0;
rejflag = chkencflag = cdtflag = 0;
assignmeshsize = 0;
sloc = sbowywat = 0;
refineflag = 0;
refinetet.tet = NULL;
refinesh.sh = NULL;
smlenflag = 0;
smlen = 0.0;
}
};
///////////////////////////////////////////////////////////////////////////////
// //
// flipconstraints //
// //
// A structure of a collection of data (options and parameters) which pass //
// to the edge flip function flipnm(). //
// //
///////////////////////////////////////////////////////////////////////////////
class flipconstraints {
public:
// Elementary flip flags.
int enqflag; // (= flipflag)
int chkencflag;
// Control flags
int unflip; // Undo the performed flips.
int collectnewtets; // Collect the new tets created by flips.
int collectencsegflag;
// Optimization flags.
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.
// Boundary recovery flags.
int checkflipeligibility;
point seg[2]; // A constraining edge to be recovered.
point fac[3]; // A constraining face to be recovered.
point remvert; // A vertex to be removed.
flipconstraints() {
enqflag = 0;
chkencflag = 0;
unflip = 0;
collectnewtets = 0;
collectencsegflag = 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;
checkflipeligibility = 0;
seg[0] = NULL;
fac[0] = NULL;
remvert = NULL;
}
};
///////////////////////////////////////////////////////////////////////////////
// //
// optparameters //
// //
// Optimization options and parameters. //
// //
///////////////////////////////////////////////////////////////////////////////
class optparameters {
public:
// 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.
// The initial and improved value.
REAL initval, imprval;
int numofsearchdirs;
REAL searchstep;
int maxiter; // Maximum smoothing iterations (disabled by -1).
int smthiter; // Performed iterations.
optparameters() {
max_min_volume = 0;
max_min_aspectratio = 0;
min_max_dihedangle = 0;
initval = imprval = 0.0;
numofsearchdirs = 10;
searchstep = 0.01;
maxiter = -1; // Unlimited smoothing iterations.
smthiter = 0;
}
};
///////////////////////////////////////////////////////////////////////////////
// //
// Labels (enumeration declarations) used by TetGen. //
// //
///////////////////////////////////////////////////////////////////////////////
// Labels that signify the type of a vertex.
enum verttype {UNUSEDVERTEX, DUPLICATEDVERTEX, RIDGEVERTEX, ACUTEVERTEX,
FACETVERTEX, VOLVERTEX, FREESEGVERTEX, FREEFACETVERTEX,
FREEVOLVERTEX, NREGULARVERTEX, DEADVERTEX};
// Labels that signify the result of triangle-triangle intersection test.
enum interresult {DISJOINT, INTERSECT, SHAREVERT, SHAREEDGE, SHAREFACE,
TOUCHEDGE, TOUCHFACE, ACROSSVERT, ACROSSEDGE, ACROSSFACE,
COLLISIONFACE, ACROSSSEG, ACROSSSUB};
// Labels that signify the result of point location.
enum locateresult {UNKNOWN, OUTSIDE, INTETRAHEDRON, ONFACE, ONEDGE, ONVERTEX,
ENCVERTEX, ENCSEGMENT, ENCSUBFACE, NEARVERTEX, NONREGULAR,
INSTAR, BADELEMENT};
///////////////////////////////////////////////////////////////////////////////
// //
// Variables of TetGen //
// //
///////////////////////////////////////////////////////////////////////////////
// Pointer to the input data (a set of nodes, a PLC, or a mesh).
tetgenio *in, *addin;
// Pointer to the switches and parameters.
tetgenbehavior *b;
// Pointer to a background mesh (contains size specification map).
tetgenmesh *bgm;
// Memorypools to store mesh elements (points, tetrahedra, subfaces, and
// segments) and extra pointers between tetrahedra, subfaces, and segments.
memorypool *tetrahedrons, *subfaces, *subsegs, *points;
memorypool *tet2subpool, *tet2segpool;
// Memorypools to store bad-quality (or encroached) elements.
memorypool *badtetrahedrons, *badsubfacs, *badsubsegs;
// A memorypool to store faces to be flipped.
memorypool *flippool;
arraypool *unflipqueue;
badface *flipstack;
// Arrays used for point insertion (the Bowyer-Watson algorithm).
arraypool *cavetetlist, *cavebdrylist, *caveoldtetlist;
arraypool *cavetetshlist, *cavetetseglist, *cavetetvertlist;
arraypool *caveencshlist, *caveencseglist;
arraypool *caveshlist, *caveshbdlist, *cavesegshlist;
// Stacks used for CDT construction and boundary recovery.
arraypool *subsegstack, *subfacstack, *subvertstack;
// Arrays of encroached segments and subfaces (for mesh refinement).
arraypool *encseglist, *encshlist;
// The map between facets to their vertices (for mesh refinement).
int *idx2facetlist;
point *facetverticeslist;
// The map between segments to their endpoints (for mesh refinement).
point *segmentendpointslist;
// The infinite vertex.
point dummypoint;
// The recently visited tetrahedron, subface.
triface recenttet;
face recentsh;
// 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;
// Various variables.
int numpointattrib; // Number of point attributes.
int numelemattrib; // Number of tetrahedron attributes.
int sizeoftensor; // Number of REALs per metric tensor.
int pointmtrindex; // Index to find the metric tensor of a point.
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 elemattribindex; // Index to find attributes of a tetrahedron.
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 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.
int useinsertradius; // Save the insertion radius for Steiner points.
long samples; // Number of random samples for point location.
unsigned long randomseed; // Current random number seed.
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 minfaceang, minfacetdihed; // The minimum input (dihedral) angles.
REAL tetprism_vol_sum; // The total volume of tetrahedral-prisms (in 4D).
REAL longest; // The longest possible edge length.
REAL xmax, xmin, ymax, ymin, zmax, zmin; // Bounding box of points.
// 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 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;
unsigned long totalworkmemory; // Total memory used by working arrays.
///////////////////////////////////////////////////////////////////////////////
// //
// Mesh manipulation primitives //
// //
///////////////////////////////////////////////////////////////////////////////
// Fast lookup tables for mesh manipulation primitives.
static int bondtbl[12][12], fsymtbl[12][12];
static int esymtbl[12], enexttbl[12], eprevtbl[12];
static int enextesymtbl[12], eprevesymtbl[12];
static int eorgoppotbl[12], edestoppotbl[12];
static int facepivot1[12], facepivot2[12][12];
static int orgpivot[12], destpivot[12], apexpivot[12], oppopivot[12];
static int tsbondtbl[12][6], stbondtbl[12][6];
static int tspivottbl[12][6], stpivottbl[12][6];
static int ver2edge[12], edge2ver[6], epivot[12];
static int sorgpivot [6], sdestpivot[6], sapexpivot[6];
static int snextpivot[6];
void inittables();
// Primitives for tetrahedra.
inline tetrahedron encode(triface& t);
inline tetrahedron encode2(tetrahedron* ptr, int ver);
inline void decode(tetrahedron ptr, triface& t);
inline void bond(triface& t1, triface& t2);
inline void dissolve(triface& t);
inline void esym(triface& t1, triface& t2);
inline void esymself(triface& t);
inline void enext(triface& t1, triface& t2);
inline void enextself(triface& t);
inline void eprev(triface& t1, triface& t2);
inline void eprevself(triface& t);
inline void enextesym(triface& t1, triface& t2);
inline void enextesymself(triface& t);
inline void eprevesym(triface& t1, triface& t2);
inline void eprevesymself(triface& t);
inline void eorgoppo(triface& t1, triface& t2);
inline void eorgoppoself(triface& t);
inline void edestoppo(triface& t1, triface& t2);
inline void edestoppoself(triface& t);
inline void fsym(triface& t1, triface& t2);
inline void fsymself(triface& t);
inline void fnext(triface& t1, triface& t2);
inline void fnextself(triface& t);
inline point org (triface& t);
inline point dest(triface& t);
inline point apex(triface& t);
inline point oppo(triface& t);
inline void setorg (triface& t, point p);
inline void setdest(triface& t, point p);
inline void setapex(triface& t, point p);
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 volumebound(tetrahedron* ptr);
inline void setvolumebound(tetrahedron* ptr, REAL value);
inline int elemindex(tetrahedron* ptr);
inline void setelemindex(tetrahedron* ptr, int value);
inline int elemmarker(tetrahedron* ptr);
inline void setelemmarker(tetrahedron* ptr, int value);
inline void infect(triface& t);
inline void uninfect(triface& t);
inline bool infected(triface& t);
inline void marktest(triface& t);
inline void unmarktest(triface& t);
inline bool marktested(triface& t);
inline void markface(triface& t);
inline void unmarkface(triface& t);
inline bool facemarked(triface& t);
inline void markedge(triface& t);
inline void unmarkedge(triface& t);
inline bool edgemarked(triface& t);
inline void marktest2(triface& t);
inline void unmarktest2(triface& t);
inline bool marktest2ed(triface& t);
inline int elemcounter(triface& t);
inline void setelemcounter(triface& t, int value);
inline void increaseelemcounter(triface& t);
inline void decreaseelemcounter(triface& t);
inline bool ishulltet(triface& t);
inline bool isdeadtet(triface& t);
// Primitives for subfaces and subsegments.
inline void sdecode(shellface sptr, face& s);
inline shellface sencode(face& s);
inline shellface sencode2(shellface *sh, int shver);
inline void spivot(face& s1, face& s2);
inline void spivotself(face& s);
inline void sbond(face& s1, face& s2);
inline void sbond1(face& s1, face& s2);
inline void sdissolve(face& s);
inline point sorg(face& s);
inline point sdest(face& s);
inline point sapex(face& s);
inline void setsorg(face& s, point pointptr);
inline void setsdest(face& s, point pointptr);
inline void setsapex(face& s, point pointptr);
inline void sesym(face& s1, face& s2);
inline void sesymself(face& s);
inline void senext(face& s1, face& s2);
inline void senextself(face& s);
inline void senext2(face& s1, face& s2);
inline void senext2self(face& s);
inline REAL areabound(face& s);
inline void setareabound(face& s, REAL value);
inline int shellmark(face& s);
inline void setshellmark(face& s, int value);
inline void sinfect(face& s);
inline void suninfect(face& s);
inline bool sinfected(face& s);
inline void smarktest(face& s);
inline void sunmarktest(face& s);
inline bool smarktested(face& s);
inline void smarktest2(face& s);
inline void sunmarktest2(face& s);
inline bool smarktest2ed(face& s);
inline void smarktest3(face& s);
inline void sunmarktest3(face& s);
inline bool smarktest3ed(face& s);
inline void setfacetindex(face& f, int value);
inline int getfacetindex(face& f);
// Primitives for interacting tetrahedra and subfaces.
inline void tsbond(triface& t, face& s);
inline void tsdissolve(triface& t);
inline void stdissolve(face& s);
inline void tspivot(triface& t, face& s);
inline void stpivot(face& s, triface& t);
// Primitives for interacting tetrahedra and segments.
inline void tssbond1(triface& t, face& seg);
inline void sstbond1(face& s, triface& t);
inline void tssdissolve1(triface& t);
inline void sstdissolve1(face& s);
inline void tsspivot1(triface& t, face& s);
inline void sstpivot1(face& s, triface& t);
// Primitives for interacting subfaces and segments.
inline void ssbond(face& s, face& edge);
inline void ssbond1(face& s, face& edge);
inline void ssdissolve(face& s);
inline void sspivot(face& s, face& edge);
// Primitives for points.
inline int pointmark(point pt);
inline void setpointmark(point pt, int value);
inline enum verttype pointtype(point pt);
inline void setpointtype(point pt, enum verttype value);
inline int pointgeomtag(point pt);
inline void setpointgeomtag(point pt, int value);
inline REAL pointgeomuv(point pt, int i);
inline void setpointgeomuv(point pt, int i, REAL value);
inline void pinfect(point pt);
inline void puninfect(point pt);
inline bool pinfected(point pt);
inline void pmarktest(point pt);
inline void punmarktest(point pt);
inline bool pmarktested(point pt);
inline void pmarktest2(point pt);
inline void punmarktest2(point pt);
inline bool pmarktest2ed(point pt);
inline void pmarktest3(point pt);
inline void punmarktest3(point pt);
inline bool pmarktest3ed(point pt);
inline tetrahedron point2tet(point pt);
inline void setpoint2tet(point pt, tetrahedron value);
inline shellface point2sh(point pt);
inline void setpoint2sh(point pt, shellface value);
inline point point2ppt(point pt);
inline void setpoint2ppt(point pt, point value);
inline tetrahedron point2bgmtet(point pt);
inline void setpoint2bgmtet(point pt, tetrahedron value);
inline void setpointinsradius(point pt, REAL value);
inline REAL getpointinsradius(point pt);
// Advanced primitives.
inline void point2tetorg(point pt, triface& t);
inline void point2shorg(point pa, face& s);
inline point farsorg(face& seg);
inline point farsdest(face& seg);
///////////////////////////////////////////////////////////////////////////////
// //
// Memory managment //
// //
///////////////////////////////////////////////////////////////////////////////
void tetrahedrondealloc(tetrahedron*);
tetrahedron *tetrahedrontraverse();
tetrahedron *alltetrahedrontraverse();
void shellfacedealloc(memorypool*, shellface*);
shellface *shellfacetraverse(memorypool*);
void pointdealloc(point);
point pointtraverse();
void makeindex2pointmap(point*&);
void makepoint2submap(memorypool*, int*&, face*&);
void maketetrahedron(triface*);
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. //
// //
///////////////////////////////////////////////////////////////////////////////
// Symbolic perturbations (robust)
REAL insphere_s(REAL*, REAL*, REAL*, REAL*, REAL*);
REAL orient4d_s(REAL*, REAL*, REAL*, REAL*, REAL*,
REAL, REAL, REAL, REAL, REAL);
// 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_test(point, point, point, point, point, point, int, int*, int*);
// 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);
// Linear algebra functions
inline REAL dot(REAL* v1, REAL* v2);
inline void cross(REAL* v1, REAL* v2, REAL* n);
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);
void facenormal(point pa, point pb, point pc, REAL *n, int pivot, REAL *lav);
REAL shortdistance(REAL* p, REAL* e1, REAL* e2);
REAL triarea(REAL* pa, REAL* pb, REAL* pc);
REAL interiorangle(REAL* o, REAL* p1, REAL* p2, REAL* n);
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*);
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. //
// //
///////////////////////////////////////////////////////////////////////////////
// The elementary flips.
void flip23(triface*, int, flipconstraints* fc);
void flip32(triface*, int, flipconstraints* fc);
void flip41(triface*, int, flipconstraints* fc);
// A generalized edge flip.
int flipnm(triface*, int n, int level, int, flipconstraints* fc);
int flipnm_post(triface*, int n, int nn, int, flipconstraints* fc);
// Point insertion.
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. //
// //
///////////////////////////////////////////////////////////////////////////////
void transfernodes();
// Point sorting.
int transgc[8][3][8], tsb1mod3[8];
void hilbert_init(int n);
int hilbert_split(point* vertexarray, int arraysize, int gc0, int gc1,
REAL, REAL, REAL, REAL, REAL, REAL);
void hilbert_sort3(point* vertexarray, int arraysize, int e, int d,
REAL, REAL, REAL, REAL, REAL, REAL, int depth);
void brio_multiscale_sort(point*,int,int threshold,REAL ratio,int* depth);
// 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);
// Incremental Delaunay construction.
void initialdelaunay(point pa, point pb, point pc, point pd);
void incrementaldelaunay(clock_t&);
///////////////////////////////////////////////////////////////////////////////
// //
// Surface triangulation //
// //
///////////////////////////////////////////////////////////////////////////////
void flipshpush(face*);
void flip22(face*, int, int);
void flip31(face*, int);
long lawsonflip();
int sinsertvertex(point newpt, face*, face*, int iloc, int bowywat, int);
int sremovevertex(point delpt, face*, face*, int lawson);
enum locateresult slocate(point, face*, int, int, int);
enum interresult sscoutsegment(face*, point);
void scarveholes(int, REAL*);
void triangulate(int, arraypool*, arraypool*, int, REAL*);
void unifysubfaces(face*, face*);
void unifysegments();
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();
enum interresult finddirection(triface* searchtet, point endpt);
enum interresult scoutsegment(point, point, triface*, point*, arraypool*);
int getsteinerptonsegment(face* seg, point refpt, point steinpt);
void delaunizesegments();
enum interresult scoutsubface(face* searchsh, triface* searchtet);
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*);
bool fillcavity(arraypool*, arraypool*, arraypool*, arraypool*,
arraypool*, arraypool*, triface* crossedge);
void carvecavity(arraypool*, arraypool*, arraypool*);
void restorecavity(arraypool*, arraypool*, arraypool*, arraypool*);
// Facet recovery by flips [Shewchuk 2003].
void flipcertify(triface *chkface, badface **pqueue, point, point, point);
void flipinsertfacet(arraypool*, arraypool*, arraypool*, arraypool*);
bool fillregion(arraypool* missingshs, arraypool*, arraypool* newshs);
int insertpoint_cdt(point, triface*, face*, face*, insertvertexflags*,
arraypool*, arraypool*, arraypool*, arraypool*,
arraypool*, arraypool*);
void refineregion(face&, arraypool*, arraypool*, arraypool*, arraypool*,
arraypool*, arraypool*);
void constrainedfacets();
void constraineddelaunay(clock_t&);
///////////////////////////////////////////////////////////////////////////////
// //
// Constrained tetrahedralizations. //
// //
///////////////////////////////////////////////////////////////////////////////
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 recoversegments(arraypool*, int fullsearch, int steinerflag);
int recoverfacebyflips(point, point, point, face*, triface*);
int recoversubfaces(arraypool*, int steinerflag);
int getvertexstar(int, point searchpt, arraypool*, arraypool*, arraypool*);
int getedge(point, point, triface*);
int reduceedgesatvertex(point startpt, arraypool* endptlist);
int removevertexbyflips(point steinerpt);
int suppressbdrysteinerpoint(point steinerpt);
int suppresssteinerpoints();
void recoverboundary(clock_t&);
///////////////////////////////////////////////////////////////////////////////
// //
// Mesh reconstruction //
// //
///////////////////////////////////////////////////////////////////////////////
void carveholes();
void reconstructmesh();
int scoutpoint(point, triface*, int randflag);
REAL getpointmeshsize(point, triface*, int iloc);
void interpolatemeshsize();
void insertconstrainedpoints(point *insertarray, int arylen, int rejflag);
void insertconstrainedpoints(tetgenio *addio);
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. //
// //
///////////////////////////////////////////////////////////////////////////////
void makefacetverticesmap();
int segsegadjacent(face *, face *);
int segfacetadjacent(face *checkseg, face *checksh);
int facetfacetadjacent(face *, face *);
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);
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);
void delaunayrefinement();
///////////////////////////////////////////////////////////////////////////////
// //
// 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 //
// //
///////////////////////////////////////////////////////////////////////////////
// Mesh validations.
int checkmesh(int topoflag);
int checkshells();
int checksegments();
int checkdelaunay();
int checkregular(int);
int checkconforming(int);
// Mesh statistics.
void printfcomma(unsigned long n);
void qualitystatistics();
void memorystatistics();
void statistics();
///////////////////////////////////////////////////////////////////////////////
// //
// Mesh output //
// //
///////////////////////////////////////////////////////////////////////////////
void jettisonnodes();
void highorder();
void numberedges();
void outnodes(tetgenio*);
void outmetrics(tetgenio*);
void outelements(tetgenio*);
void outfaces(tetgenio*);
void outhullfaces(tetgenio*);
void outsubfaces(tetgenio*);
void outedges(tetgenio*);
void outsubsegments(tetgenio*);
void outneighbors(tetgenio*);
void outvoronoi(tetgenio*);
void outsmesh(char*);
void outmesh2medit(char*);
void outmesh2vtk(char*);
///////////////////////////////////////////////////////////////////////////////
// //
// Constructor & destructor //
// //
///////////////////////////////////////////////////////////////////////////////
tetgenmesh()
{
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;
cavetetlist = cavebdrylist = caveoldtetlist = NULL;
cavetetshlist = cavetetseglist = cavetetvertlist = NULL;
caveencshlist = caveencseglist = NULL;
caveshlist = caveshbdlist = cavesegshlist = NULL;
subsegstack = subfacstack = subvertstack = NULL;
encseglist = encshlist = NULL;
idx2facetlist = NULL;
facetverticeslist = NULL;
segmentendpointslist = NULL;
highordertable = NULL;
numpointattrib = numelemattrib = 0;
sizeoftensor = 0;
pointmtrindex = 0;
pointparamindex = 0;
pointmarkindex = 0;
point2simindex = 0;
elemattribindex = 0;
volumeboundindex = 0;
shmarkindex = 0;
areaboundindex = 0;
checksubsegflag = 0;
checksubfaceflag = 0;
checkconstraints = 0;
nonconvex = 0;
autofliplinklevel = 1;
useinsertradius = 0;
samples = 0l;
randomseed = 1l;
minfaceang = minfacetdihed = PI;
tetprism_vol_sum = 0.0;
longest = 0.0;
xmax = xmin = ymax = ymin = zmax = zmin = 0.0;
insegments = 0l;
hullsize = 0l;
meshedges = meshhulledges = 0l;
steinerleft = -1;
dupverts = 0l;
unuverts = 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;
totalworkmemory = 0l;
} // tetgenmesh()
void freememory()
{
if (bgm != NULL) {
delete bgm;
}
if (points != (memorypool *) NULL) {
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 (flippool != NULL) {
delete flippool;
delete unflipqueue;
}
if (cavetetlist != NULL) {
delete cavetetlist;
delete cavebdrylist;
delete caveoldtetlist;
delete cavetetvertlist;
}
if (caveshlist != NULL) {
delete caveshlist;
delete caveshbdlist;
delete cavesegshlist;
delete cavetetshlist;
delete cavetetseglist;
delete caveencshlist;
delete caveencseglist;
}
if (subsegstack != NULL) {
delete subsegstack;
delete subfacstack;
delete subvertstack;
}
if (idx2facetlist != NULL) {
delete [] idx2facetlist;
delete [] facetverticeslist;
}
if (segmentendpointslist != NULL) {
delete [] segmentendpointslist;
}
if (highordertable != NULL) {
delete [] highordertable;
}
}
~tetgenmesh()
{
freememory();
} // ~tetgenmesh()
}; // End of class tetgenmesh.
///////////////////////////////////////////////////////////////////////////////
// //
// tetrahedralize() Interface for using TetGen's library to generate //
// Delaunay tetrahedralizations, constrained Delaunay //
// tetrahedralizations, quality tetrahedral meshes. //
// //
// 'in' is an object of 'tetgenio' which contains a PLC you want to tetrahed-//
// ralize or a previously generated tetrahedral mesh you want to refine. It //
// must not be a NULL. 'out' is another object of 'tetgenio' for storing the //
// generated tetrahedral mesh. It can be a NULL. If so, the output will be //
// saved to file(s). If 'bgmin' != NULL, it contains a background mesh which //
// defines a mesh size function. //
// //
///////////////////////////////////////////////////////////////////////////////
void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out,
tetgenio *addin = NULL, tetgenio *bgmin = NULL);
#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. //
// //
///////////////////////////////////////////////////////////////////////////////
inline void terminatetetgen(tetgenmesh *m, int x)
{
// Release the allocated memory.
if (m) {
m->freememory();
}
#ifdef TETLIBRARY
throw x;
#else
switch (x) {
case 1: // Out of memory.
printf("Error: Out of memory.\n");
break;
case 2: // Encounter an internal error.
printf("Please report this bug to Hang.Si@wias-berlin.de. Include\n");
printf(" the message above, your input data set, and the exact\n");
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");
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");
break;
case 5:
printf("Two very close input facets were detected. Program stopped.\n");
printf("Hint: use -Y option to avoid adding Steiner points in boundary.\n");
break;
case 10:
printf("An input error was detected. Program stopped.\n");
break;
} // switch (x)
exit(x);
#endif // #ifdef TETLIBRARY
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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-
// byte boundaries, so that the last four significant bits are zero.
inline tetgenmesh::tetrahedron tetgenmesh::encode(triface& t) {
return (tetrahedron) ((uintptr_t) (t).tet | (uintptr_t) (t).ver);
}
inline tetgenmesh::tetrahedron tetgenmesh::encode2(tetrahedron* ptr, int ver) {
return (tetrahedron) ((uintptr_t) (ptr) | (uintptr_t) (ver));
}
// decode() converts a pointer to a handle. The version is extracted from
// the four least significant bits of the pointer.
inline void tetgenmesh::decode(tetrahedron ptr, triface& t) {
(t).ver = (int) ((uintptr_t) (ptr) & (uintptr_t) 15);
(t).tet = (tetrahedron *) ((uintptr_t) (ptr) ^ (uintptr_t) (t).ver);
}
// bond() connects two tetrahedra together. (t1,v1) and (t2,v2) must
// refer to the same face and the same edge.
inline void tetgenmesh::bond(triface& t1, triface& t2) {
t1.tet[t1.ver & 3] = encode2(t2.tet, bondtbl[t1.ver][t2.ver]);
t2.tet[t2.ver & 3] = encode2(t1.tet, bondtbl[t2.ver][t1.ver]);
}
// dissolve() a bond (from one side).
inline void tetgenmesh::dissolve(triface& t) {
t.tet[t.ver & 3] = NULL;
}
// enext() finds the next edge (counterclockwise) in the same face.
inline void tetgenmesh::enext(triface& t1, triface& t2) {
t2.tet = t1.tet;
t2.ver = enexttbl[t1.ver];
}
inline void tetgenmesh::enextself(triface& t) {
t.ver = enexttbl[t.ver];
}
// 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];
}
inline void tetgenmesh::eprevself(triface& t) {
t.ver = eprevtbl[t.ver];
}
// esym() finds the reversed edge. It is in the other face of the
// same tetrahedron.
inline void tetgenmesh::esym(triface& t1, triface& t2) {
(t2).tet = (t1).tet;
(t2).ver = esymtbl[(t1).ver];
}
inline void tetgenmesh::esymself(triface& t) {
(t).ver = esymtbl[(t).ver];
}
// enextesym() finds the reversed edge of the next edge. It is in the other
// face of the same tetrahedron. It is the combination esym() * enext().
inline void tetgenmesh::enextesym(triface& t1, triface& t2) {
t2.tet = t1.tet;
t2.ver = enextesymtbl[t1.ver];
}
inline void tetgenmesh::enextesymself(triface& t) {
t.ver = enextesymtbl[t.ver];
}
// eprevesym() finds the reversed edge of the previous edge.
inline void tetgenmesh::eprevesym(triface& t1, triface& t2) {
t2.tet = t1.tet;
t2.ver = eprevesymtbl[t1.ver];
}
inline void tetgenmesh::eprevesymself(triface& t) {
t.ver = eprevesymtbl[t.ver];
}
// eorgoppo() Finds the opposite face of the origin of the current edge.
// Return the opposite edge of the current edge.
inline void tetgenmesh::eorgoppo(triface& t1, triface& t2) {
t2.tet = t1.tet;
t2.ver = eorgoppotbl[t1.ver];
}
inline void tetgenmesh::eorgoppoself(triface& t) {
t.ver = eorgoppotbl[t.ver];
}
// edestoppo() Finds the opposite face of the destination of the current
// edge. Return the opposite edge of the current edge.
inline void tetgenmesh::edestoppo(triface& t1, triface& t2) {
t2.tet = t1.tet;
t2.ver = edestoppotbl[t1.ver];
}
inline void tetgenmesh::edestoppoself(triface& t) {
t.ver = edestoppotbl[t.ver];
}
// fsym() finds the adjacent tetrahedron at the same face and the same edge.
inline void tetgenmesh::fsym(triface& t1, triface& t2) {
decode((t1).tet[(t1).ver & 3], t2);
t2.ver = fsymtbl[t1.ver][t2.ver];
}
#define fsymself(t) \
t1ver = (t).ver; \
decode((t).tet[(t).ver & 3], (t));\
(t).ver = fsymtbl[t1ver][(t).ver]
// fnext() finds the next face while rotating about an edge according to
// a right-hand rule. The face is in the adjacent tetrahedron. It is
// the combination: fsym() * esym().
inline void tetgenmesh::fnext(triface& t1, triface& t2) {
decode(t1.tet[facepivot1[t1.ver]], t2);
t2.ver = facepivot2[t1.ver][t2.ver];
}
#define fnextself(t) \
t1ver = (t).ver; \
decode((t).tet[facepivot1[(t).ver]], (t)); \
(t).ver = facepivot2[t1ver][(t).ver]
// The following primtives get or set the origin, destination, face apex,
// or face opposite of an ordered tetrahedron.
inline tetgenmesh::point tetgenmesh::org(triface& t) {
return (point) (t).tet[orgpivot[(t).ver]];
}
inline tetgenmesh::point tetgenmesh:: dest(triface& t) {
return (point) (t).tet[destpivot[(t).ver]];
}
inline tetgenmesh::point tetgenmesh:: apex(triface& t) {
return (point) (t).tet[apexpivot[(t).ver]];
}
inline tetgenmesh::point tetgenmesh:: oppo(triface& t) {
return (point) (t).tet[oppopivot[(t).ver]];
}
inline void tetgenmesh:: setorg(triface& t, point p) {
(t).tet[orgpivot[(t).ver]] = (tetrahedron) (p);
}
inline void tetgenmesh:: setdest(triface& t, point p) {
(t).tet[destpivot[(t).ver]] = (tetrahedron) (p);
}
inline void tetgenmesh:: setapex(triface& t, point p) {
(t).tet[apexpivot[(t).ver]] = (tetrahedron) (p);
}
inline void tetgenmesh:: setoppo(triface& t, point p) {
(t).tet[oppopivot[(t).ver]] = (tetrahedron) (p);
}
#define setvertices(t, torg, tdest, tapex, toppo) \
(t).tet[orgpivot[(t).ver]] = (tetrahedron) (torg);\
(t).tet[destpivot[(t).ver]] = (tetrahedron) (tdest); \
(t).tet[apexpivot[(t).ver]] = (tetrahedron) (tapex); \
(t).tet[oppopivot[(t).ver]] = (tetrahedron) (toppo)
// Check or set a tetrahedron's attributes.
inline REAL tetgenmesh::elemattribute(tetrahedron* ptr, int attnum) {
return ((REAL *) (ptr))[elemattribindex + attnum];
}
inline void tetgenmesh::setelemattribute(tetrahedron* ptr, int attnum,
REAL value) {
((REAL *) (ptr))[elemattribindex + attnum] = value;
}
// Check or set a tetrahedron's maximum volume bound.
inline REAL tetgenmesh::volumebound(tetrahedron* ptr) {
return ((REAL *) (ptr))[volumeboundindex];
}
inline void tetgenmesh::setvolumebound(tetrahedron* ptr, REAL value) {
((REAL *) (ptr))[volumeboundindex] = value;
}
// Get or set a tetrahedron's index (only used for output).
// These two routines use the reserved slot ptr[10].
inline int tetgenmesh::elemindex(tetrahedron* ptr) {
int *iptr = (int *) &(ptr[10]);
return iptr[0];
}
inline void tetgenmesh::setelemindex(tetrahedron* ptr, int value) {
int *iptr = (int *) &(ptr[10]);
iptr[0] = value;
}
// Get or set a tetrahedron's marker.
// Set 'value = 0' cleans all the face/edge flags.
inline int tetgenmesh::elemmarker(tetrahedron* ptr) {
return ((int *) (ptr))[elemmarkerindex];
}
inline void tetgenmesh::setelemmarker(tetrahedron* ptr, int value) {
((int *) (ptr))[elemmarkerindex] = value;
}
// infect(), infected(), uninfect() -- primitives to flag or unflag a
// tetrahedron. The last bit of the element marker is flagged (1)
// or unflagged (0).
inline void tetgenmesh::infect(triface& t) {
((int *) (t.tet))[elemmarkerindex] |= 1;
}
inline void tetgenmesh::uninfect(triface& t) {
((int *) (t.tet))[elemmarkerindex] &= ~1;
}
inline bool tetgenmesh::infected(triface& t) {
return (((int *) (t.tet))[elemmarkerindex] & 1) != 0;
}
// marktest(), marktested(), unmarktest() -- primitives to flag or unflag a
// tetrahedron. Use the second lowerest bit of the element marker.
inline void tetgenmesh::marktest(triface& t) {
((int *) (t.tet))[elemmarkerindex] |= 2;
}
inline void tetgenmesh::unmarktest(triface& t) {
((int *) (t.tet))[elemmarkerindex] &= ~2;
}
inline bool tetgenmesh::marktested(triface& t) {
return (((int *) (t.tet))[elemmarkerindex] & 2) != 0;
}
// markface(), unmarkface(), facemarked() -- primitives to flag or unflag a
// face of a tetrahedron. From the last 3rd to 6th bits are used for
// face markers, e.g., the last third bit corresponds to loc = 0.
inline void tetgenmesh::markface(triface& t) {
((int *) (t.tet))[elemmarkerindex] |= (4 << (t.ver & 3));
}
inline void tetgenmesh::unmarkface(triface& t) {
((int *) (t.tet))[elemmarkerindex] &= ~(4 << (t.ver & 3));
}
inline bool tetgenmesh::facemarked(triface& t) {
return (((int *) (t.tet))[elemmarkerindex] & (4 << (t.ver & 3))) != 0;
}
// markedge(), unmarkedge(), edgemarked() -- primitives to flag or unflag an
// edge of a tetrahedron. From the last 7th to 12th bits are used for
// edge markers, e.g., the last 7th bit corresponds to the 0th edge, etc.
// Remark: The last 7th bit is marked by 2^6 = 64.
inline void tetgenmesh::markedge(triface& t) {
((int *) (t.tet))[elemmarkerindex] |= (int) (64 << ver2edge[(t).ver]);
}
inline void tetgenmesh::unmarkedge(triface& t) {
((int *) (t.tet))[elemmarkerindex] &= ~(int) (64 << ver2edge[(t).ver]);
}
inline bool tetgenmesh::edgemarked(triface& t) {
return (((int *) (t.tet))[elemmarkerindex] &
(int) (64 << ver2edge[(t).ver])) != 0;
}
// marktest2(), unmarktest2(), marktest2ed() -- primitives to flag and unflag
// a tetrahedron. The 13th bit (2^12 = 4096) is used for this flag.
inline void tetgenmesh::marktest2(triface& t) {
((int *) (t.tet))[elemmarkerindex] |= (int) (4096);
}
inline void tetgenmesh::unmarktest2(triface& t) {
((int *) (t.tet))[elemmarkerindex] &= ~(int) (4096);
}
inline bool tetgenmesh::marktest2ed(triface& t) {
return (((int *) (t.tet))[elemmarkerindex] & (int) (4096)) != 0;
}
// elemcounter(), setelemcounter() -- primitives to read or ser a (small)
// integer counter in this tet. It is saved from the 16th bit. On 32 bit
// system, the range of the counter is [0, 2^15 = 32768].
inline int tetgenmesh::elemcounter(triface& t) {
return (((int *) (t.tet))[elemmarkerindex]) >> 16;
}
inline void tetgenmesh::setelemcounter(triface& t, int value) {
int c = ((int *) (t.tet))[elemmarkerindex];
// Clear the old counter while keep the other flags.
c &= 65535; // sum_{i=0^15} 2^i
c |= (value << 16);
((int *) (t.tet))[elemmarkerindex] = c;
}
inline void tetgenmesh::increaseelemcounter(triface& t) {
int c = elemcounter(t);
setelemcounter(t, c + 1);
}
inline void tetgenmesh::decreaseelemcounter(triface& t) {
int c = elemcounter(t);
setelemcounter(t, c - 1);
}
// ishulltet() tests if t is a hull tetrahedron.
inline bool tetgenmesh::ishulltet(triface& t) {
return (point) (t).tet[7] == dummypoint;
}
// isdeadtet() tests if t is a tetrahedron is dead.
inline bool tetgenmesh::isdeadtet(triface& t) {
return ((t.tet == NULL) || (t.tet[4] == NULL));
}
///////////////////////////////////////////////////////////////////////////////
// //
// 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
// pointer. To make this possible, all subfaces are aligned to eight-byte
// boundaries, so that the last three bits of each pointer are zeros. An
// edge version (in the range 0 to 5) is compressed into the last three
// bits of each pointer by 'sencode()'. 'sdecode()' decodes a pointer,
// extracting an edge version and a pointer to the beginning of a subface.
inline void tetgenmesh::sdecode(shellface sptr, face& s) {
s.shver = (int) ((uintptr_t) (sptr) & (uintptr_t) 7);
s.sh = (shellface *) ((uintptr_t) (sptr) ^ (uintptr_t) (s.shver));
}
inline tetgenmesh::shellface tetgenmesh::sencode(face& s) {
return (shellface) ((uintptr_t) s.sh | (uintptr_t) s.shver);
}
inline tetgenmesh::shellface tetgenmesh::sencode2(shellface *sh, int shver) {
return (shellface) ((uintptr_t) sh | (uintptr_t) shver);
}
// sbond() bonds two subfaces (s1) and (s2) together. s1 and s2 must refer
// to the same edge. No requirement is needed on their orientations.
inline void tetgenmesh::sbond(face& s1, face& s2)
{
s1.sh[s1.shver >> 1] = sencode(s2);
s2.sh[s2.shver >> 1] = sencode(s1);
}
// sbond1() bonds s1 <== s2, i.e., after bonding, s1 is pointing to s2,
// but s2 is not pointing to s1. s1 and s2 must refer to the same edge.
// No requirement is needed on their orientations.
inline void tetgenmesh::sbond1(face& s1, face& s2)
{
s1.sh[s1.shver >> 1] = sencode(s2);
}
// Dissolve a subface bond (from one side). Note that the other subface
// will still think it's connected to this subface.
inline void tetgenmesh::sdissolve(face& s)
{
s.sh[s.shver >> 1] = NULL;
}
// spivot() finds the adjacent subface (s2) for a given subface (s1).
// s1 and s2 share at the same edge.
inline void tetgenmesh::spivot(face& s1, face& s2)
{
shellface sptr = s1.sh[s1.shver >> 1];
sdecode(sptr, s2);
}
inline void tetgenmesh::spivotself(face& s)
{
shellface sptr = s.sh[s.shver >> 1];
sdecode(sptr, s);
}
// These primitives determine or set the origin, destination, or apex
// of a subface with respect to the edge version.
inline tetgenmesh::point tetgenmesh::sorg(face& s)
{
return (point) s.sh[sorgpivot[s.shver]];
}
inline tetgenmesh::point tetgenmesh::sdest(face& s)
{
return (point) s.sh[sdestpivot[s.shver]];
}
inline tetgenmesh::point tetgenmesh::sapex(face& s)
{
return (point) s.sh[sapexpivot[s.shver]];
}
inline void tetgenmesh::setsorg(face& s, point pointptr)
{
s.sh[sorgpivot[s.shver]] = (shellface) pointptr;
}
inline void tetgenmesh::setsdest(face& s, point pointptr)
{
s.sh[sdestpivot[s.shver]] = (shellface) pointptr;
}
inline void tetgenmesh::setsapex(face& s, point pointptr)
{
s.sh[sapexpivot[s.shver]] = (shellface) pointptr;
}
#define setshvertices(s, pa, pb, pc)\
setsorg(s, pa);\
setsdest(s, pb);\
setsapex(s, pc)
// sesym() reserves the direction of the lead edge.
inline void tetgenmesh::sesym(face& s1, face& s2)
{
s2.sh = s1.sh;
s2.shver = (s1.shver ^ 1); // Inverse the last bit.
}
inline void tetgenmesh::sesymself(face& s)
{
s.shver ^= 1;
}
// senext() finds the next edge (counterclockwise) in the same orientation
// of this face.
inline void tetgenmesh::senext(face& s1, face& s2)
{
s2.sh = s1.sh;
s2.shver = snextpivot[s1.shver];
}
inline void tetgenmesh::senextself(face& s)
{
s.shver = snextpivot[s.shver];
}
inline void tetgenmesh::senext2(face& s1, face& s2)
{
s2.sh = s1.sh;
s2.shver = snextpivot[snextpivot[s1.shver]];
}
inline void tetgenmesh::senext2self(face& s)
{
s.shver = snextpivot[snextpivot[s.shver]];
}
// Check or set a subface's maximum area bound.
inline REAL tetgenmesh::areabound(face& s)
{
return ((REAL *) (s.sh))[areaboundindex];
}
inline void tetgenmesh::setareabound(face& s, REAL value)
{
((REAL *) (s.sh))[areaboundindex] = value;
}
// These two primitives read or set a shell marker. Shell markers are used
// to hold user boundary information.
inline int tetgenmesh::shellmark(face& s)
{
return ((int *) (s.sh))[shmarkindex];
}
inline void tetgenmesh::setshellmark(face& s, int value)
{
((int *) (s.sh))[shmarkindex] = value;
}
// sinfect(), sinfected(), suninfect() -- primitives to flag or unflag a
// subface. The last bit of ((int *) ((s).sh))[shmarkindex+1] is flagged.
inline void tetgenmesh::sinfect(face& s)
{
((int *) ((s).sh))[shmarkindex+1] =
(((int *) ((s).sh))[shmarkindex+1] | (int) 1);
}
inline void tetgenmesh::suninfect(face& s)
{
((int *) ((s).sh))[shmarkindex+1] =
(((int *) ((s).sh))[shmarkindex+1] & ~(int) 1);
}
// Test a subface for viral infection.
inline bool tetgenmesh::sinfected(face& s)
{
return (((int *) ((s).sh))[shmarkindex+1] & (int) 1) != 0;
}
// smarktest(), smarktested(), sunmarktest() -- primitives to flag or unflag
// a subface. The last 2nd bit of the integer is flagged.
inline void tetgenmesh::smarktest(face& s)
{
((int *) ((s).sh))[shmarkindex+1] =
(((int *)((s).sh))[shmarkindex+1] | (int) 2);
}
inline void tetgenmesh::sunmarktest(face& s)
{
((int *) ((s).sh))[shmarkindex+1] =
(((int *)((s).sh))[shmarkindex+1] & ~(int)2);
}
inline bool tetgenmesh::smarktested(face& s)
{
return ((((int *) ((s).sh))[shmarkindex+1] & (int) 2) != 0);
}
// smarktest2(), smarktest2ed(), sunmarktest2() -- primitives to flag or
// unflag a subface. The last 3rd bit of the integer is flagged.
inline void tetgenmesh::smarktest2(face& s)
{
((int *) ((s).sh))[shmarkindex+1] =
(((int *)((s).sh))[shmarkindex+1] | (int) 4);
}
inline void tetgenmesh::sunmarktest2(face& s)
{
((int *) ((s).sh))[shmarkindex+1] =
(((int *)((s).sh))[shmarkindex+1] & ~(int)4);
}
inline bool tetgenmesh::smarktest2ed(face& s)
{
return ((((int *) ((s).sh))[shmarkindex+1] & (int) 4) != 0);
}
// The last 4th bit of ((int *) ((s).sh))[shmarkindex+1] is flagged.
inline void tetgenmesh::smarktest3(face& s)
{
((int *) ((s).sh))[shmarkindex+1] =
(((int *)((s).sh))[shmarkindex+1] | (int) 8);
}
inline void tetgenmesh::sunmarktest3(face& s)
{
((int *) ((s).sh))[shmarkindex+1] =
(((int *)((s).sh))[shmarkindex+1] & ~(int)8);
}
inline bool tetgenmesh::smarktest3ed(face& s)
{
return ((((int *) ((s).sh))[shmarkindex+1] & (int) 8) != 0);
}
// Each facet has a unique index (automatically indexed). Starting from '0'.
// We save this index in the same field of the shell type.
inline void tetgenmesh::setfacetindex(face& s, int value)
{
((int *) (s.sh))[shmarkindex + 2] = value;
}
inline int tetgenmesh::getfacetindex(face& s)
{
return ((int *) (s.sh))[shmarkindex + 2];
}
///////////////////////////////////////////////////////////////////////////////
// //
// Primitives for interacting between tetrahedra and subfaces //
// //
///////////////////////////////////////////////////////////////////////////////
// tsbond() bond a tetrahedron (t) and a subface (s) together.
// Note that t and s must be the same face and the same edge. Moreover,
// t and s have the same orientation.
// Since the edge number in t and in s can be any number in {0,1,2}. We bond
// the edge in s which corresponds to t's 0th edge, and vice versa.
inline void tetgenmesh::tsbond(triface& t, face& s)
{
if ((t).tet[9] == NULL) {
// Allocate space for this tet.
(t).tet[9] = (tetrahedron) tet2subpool->alloc();
// Initialize.
for (int i = 0; i < 4; i++) {
((shellface *) (t).tet[9])[i] = NULL;
}
}
// Bond t <== s.
((shellface *) (t).tet[9])[(t).ver & 3] =
sencode2((s).sh, tsbondtbl[t.ver][s.shver]);
// Bond s <== t.
s.sh[9 + ((s).shver & 1)] =
(shellface) encode2((t).tet, stbondtbl[t.ver][s.shver]);
}
// tspivot() finds a subface (s) abutting on the given tetrahdera (t).
// Return s.sh = NULL if there is no subface at t. Otherwise, return
// the subface s, and s and t must be at the same edge wth the same
// orientation.
inline void tetgenmesh::tspivot(triface& t, face& s)
{
if ((t).tet[9] == NULL) {
(s).sh = NULL;
return;
}
// Get the attached subface s.
sdecode(((shellface *) (t).tet[9])[(t).ver & 3], (s));
(s).shver = tspivottbl[t.ver][s.shver];
}
// Quickly check if the handle (t, v) is a subface.
#define issubface(t) \
((t).tet[9] && ((t).tet[9])[(t).ver & 3])
// stpivot() finds a tetrahedron (t) abutting a given subface (s).
// Return the t (if it exists) with the same edge and the same
// orientation of s.
inline void tetgenmesh::stpivot(face& s, triface& t)
{
decode((tetrahedron) s.sh[9 + (s.shver & 1)], t);
if ((t).tet == NULL) {
return;
}
(t).ver = stpivottbl[t.ver][s.shver];
}
// Quickly check if this subface is attached to a tetrahedron.
#define isshtet(s) \
((s).sh[9 + ((s).shver & 1)])
// tsdissolve() dissolve a bond (from the tetrahedron side).
inline void tetgenmesh::tsdissolve(triface& t)
{
if ((t).tet[9] != NULL) {
((shellface *) (t).tet[9])[(t).ver & 3] = NULL;
}
}
// stdissolve() dissolve a bond (from the subface side).
inline void tetgenmesh::stdissolve(face& s)
{
(s).sh[9] = NULL;
(s).sh[10] = NULL;
}
///////////////////////////////////////////////////////////////////////////////
// //
// Primitives for interacting between subfaces and segments //
// //
///////////////////////////////////////////////////////////////////////////////
// ssbond() bond a subface to a subsegment.
inline void tetgenmesh::ssbond(face& s, face& edge)
{
s.sh[6 + (s.shver >> 1)] = sencode(edge);
edge.sh[0] = sencode(s);
}
inline void tetgenmesh::ssbond1(face& s, face& edge)
{
s.sh[6 + (s.shver >> 1)] = sencode(edge);
//edge.sh[0] = sencode(s);
}
// ssdisolve() dissolve a bond (from the subface side)
inline void tetgenmesh::ssdissolve(face& s)
{
s.sh[6 + (s.shver >> 1)] = NULL;
}
// sspivot() finds a subsegment abutting a subface.
inline void tetgenmesh::sspivot(face& s, face& edge)
{
sdecode((shellface) s.sh[6 + (s.shver >> 1)], edge);
}
// Quickly check if the edge is a subsegment.
#define isshsubseg(s) \
((s).sh[6 + ((s).shver >> 1)])
///////////////////////////////////////////////////////////////////////////////
// //
// Primitives for interacting between tetrahedra and segments //
// //
///////////////////////////////////////////////////////////////////////////////
inline void tetgenmesh::tssbond1(triface& t, face& s)
{
if ((t).tet[8] == NULL) {
// Allocate space for this tet.
(t).tet[8] = (tetrahedron) tet2segpool->alloc();
// Initialization.
for (int i = 0; i < 6; i++) {
((shellface *) (t).tet[8])[i] = NULL;
}
}
((shellface *) (t).tet[8])[ver2edge[(t).ver]] = sencode((s));
}
inline void tetgenmesh::sstbond1(face& s, triface& t)
{
((tetrahedron *) (s).sh)[9] = encode(t);
}
inline void tetgenmesh::tssdissolve1(triface& t)
{
if ((t).tet[8] != NULL) {
((shellface *) (t).tet[8])[ver2edge[(t).ver]] = NULL;
}
}
inline void tetgenmesh::sstdissolve1(face& s)
{
((tetrahedron *) (s).sh)[9] = NULL;
}
inline void tetgenmesh::tsspivot1(triface& t, face& s)
{
if ((t).tet[8] != NULL) {
sdecode(((shellface *) (t).tet[8])[ver2edge[(t).ver]], s);
} else {
(s).sh = NULL;
}
}
// Quickly check whether 't' is a segment or not.
#define issubseg(t) \
((t).tet[8] && ((t).tet[8])[ver2edge[(t).ver]])
inline void tetgenmesh::sstpivot1(face& s, triface& t)
{
decode((tetrahedron) s.sh[9], t);
}
///////////////////////////////////////////////////////////////////////////////
// //
// Primitives for points //
// //
///////////////////////////////////////////////////////////////////////////////
inline int tetgenmesh::pointmark(point pt) {
return ((int *) (pt))[pointmarkindex];
}
inline void tetgenmesh::setpointmark(point pt, int value) {
((int *) (pt))[pointmarkindex] = value;
}
// These two primitives set and read the type of the point.
inline enum tetgenmesh::verttype tetgenmesh::pointtype(point pt) {
return (enum verttype) (((int *) (pt))[pointmarkindex + 1] >> (int) 8);
}
inline void tetgenmesh::setpointtype(point pt, enum verttype value) {
((int *) (pt))[pointmarkindex + 1] =
((int) value << 8) + (((int *) (pt))[pointmarkindex + 1] & (int) 255);
}
// Read and set the geometry tag of the point (used by -s option).
inline int tetgenmesh::pointgeomtag(point pt) {
return ((int *) (pt))[pointmarkindex + 2];
}
inline void tetgenmesh::setpointgeomtag(point pt, int value) {
((int *) (pt))[pointmarkindex + 2] = value;
}
// Read and set the u,v coordinates of the point (used by -s option).
inline REAL tetgenmesh::pointgeomuv(point pt, int i) {
return pt[pointparamindex + i];
}
inline void tetgenmesh::setpointgeomuv(point pt, int i, REAL value) {
pt[pointparamindex + i] = value;
}
// pinfect(), puninfect(), pinfected() -- primitives to flag or unflag
// a point. The last bit of the integer '[pointindex+1]' is flagged.
inline void tetgenmesh::pinfect(point pt) {
((int *) (pt))[pointmarkindex + 1] |= (int) 1;
}
inline void tetgenmesh::puninfect(point pt) {
((int *) (pt))[pointmarkindex + 1] &= ~(int) 1;
}
inline bool tetgenmesh::pinfected(point pt) {
return (((int *) (pt))[pointmarkindex + 1] & (int) 1) != 0;
}
// pmarktest(), punmarktest(), pmarktested() -- more primitives to
// flag or unflag a point.
inline void tetgenmesh::pmarktest(point pt) {
((int *) (pt))[pointmarkindex + 1] |= (int) 2;
}
inline void tetgenmesh::punmarktest(point pt) {
((int *) (pt))[pointmarkindex + 1] &= ~(int) 2;
}
inline bool tetgenmesh::pmarktested(point pt) {
return (((int *) (pt))[pointmarkindex + 1] & (int) 2) != 0;
}
inline void tetgenmesh::pmarktest2(point pt) {
((int *) (pt))[pointmarkindex + 1] |= (int) 4;
}
inline void tetgenmesh::punmarktest2(point pt) {
((int *) (pt))[pointmarkindex + 1] &= ~(int) 4;
}
inline bool tetgenmesh::pmarktest2ed(point pt) {
return (((int *) (pt))[pointmarkindex + 1] & (int) 4) != 0;
}
inline void tetgenmesh::pmarktest3(point pt) {
((int *) (pt))[pointmarkindex + 1] |= (int) 8;
}
inline void tetgenmesh::punmarktest3(point pt) {
((int *) (pt))[pointmarkindex + 1] &= ~(int) 8;
}
inline bool tetgenmesh::pmarktest3ed(point pt) {
return (((int *) (pt))[pointmarkindex + 1] & (int) 8) != 0;
}
// These following primitives set and read a pointer to a tetrahedron
// a subface/subsegment, a point, or a tet of background mesh.
inline tetgenmesh::tetrahedron tetgenmesh::point2tet(point pt) {
return ((tetrahedron *) (pt))[point2simindex];
}
inline void tetgenmesh::setpoint2tet(point pt, tetrahedron value) {
((tetrahedron *) (pt))[point2simindex] = value;
}
inline tetgenmesh::point tetgenmesh::point2ppt(point pt) {
return (point) ((tetrahedron *) (pt))[point2simindex + 1];
}
inline void tetgenmesh::setpoint2ppt(point pt, point value) {
((tetrahedron *) (pt))[point2simindex + 1] = (tetrahedron) value;
}
inline tetgenmesh::shellface tetgenmesh::point2sh(point pt) {
return (shellface) ((tetrahedron *) (pt))[point2simindex + 2];
}
inline void tetgenmesh::setpoint2sh(point pt, shellface value) {
((tetrahedron *) (pt))[point2simindex + 2] = (tetrahedron) value;
}
inline tetgenmesh::tetrahedron tetgenmesh::point2bgmtet(point pt) {
return ((tetrahedron *) (pt))[point2simindex + 3];
}
inline void tetgenmesh::setpoint2bgmtet(point pt, tetrahedron value) {
((tetrahedron *) (pt))[point2simindex + 3] = value;
}
// The primitives for saving and getting the insertion radius.
inline void tetgenmesh::setpointinsradius(point pt, REAL value)
{
pt[pointmtrindex + sizeoftensor - 1] = value;
}
inline REAL tetgenmesh::getpointinsradius(point pt)
{
return pt[pointmtrindex + sizeoftensor - 1];
}
// point2tetorg() Get the tetrahedron whose origin is the point.
inline void tetgenmesh::point2tetorg(point pa, triface& searchtet)
{
decode(point2tet(pa), searchtet);
if ((point) searchtet.tet[4] == pa) {
searchtet.ver = 11;
} else if ((point) searchtet.tet[5] == pa) {
searchtet.ver = 3;
} else if ((point) searchtet.tet[6] == pa) {
searchtet.ver = 7;
} else {
assert((point) searchtet.tet[7] == pa); // SELF_CHECK
searchtet.ver = 0;
}
}
// point2shorg() Get the subface/segment whose origin is the point.
inline void tetgenmesh::point2shorg(point pa, face& searchsh)
{
sdecode(point2sh(pa), searchsh);
if ((point) searchsh.sh[3] == pa) {
searchsh.shver = 0;
} else if ((point) searchsh.sh[4] == pa) {
searchsh.shver = (searchsh.sh[5] != NULL ? 2 : 1);
} else {
assert((point) searchsh.sh[5] == pa); // SELF_CHECK
searchsh.shver = 4;
}
}
// farsorg() Return the origin of the subsegment.
// farsdest() Return the destination of the subsegment.
inline tetgenmesh::point tetgenmesh::farsorg(face& s)
{
face travesh, neighsh;
travesh = s;
while (1) {
senext2(travesh, neighsh);
spivotself(neighsh);
if (neighsh.sh == NULL) break;
if (sorg(neighsh) != sorg(travesh)) sesymself(neighsh);
senext2(neighsh, travesh);
}
return sorg(travesh);
}
inline tetgenmesh::point tetgenmesh::farsdest(face& s)
{
face travesh, neighsh;
travesh = s;
while (1) {
senext(travesh, neighsh);
spivotself(neighsh);
if (neighsh.sh == NULL) break;
if (sdest(neighsh) != sdest(travesh)) sesymself(neighsh);
senext(neighsh, travesh);
}
return sdest(travesh);
}
///////////////////////////////////////////////////////////////////////////////
// //
// Linear algebra operators. //
// //
///////////////////////////////////////////////////////////////////////////////
// dot() returns the dot product: v1 dot v2.
inline REAL tetgenmesh::dot(REAL* v1, REAL* v2)
{
return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];
}
// cross() computes the cross product: n = v1 cross v2.
inline void tetgenmesh::cross(REAL* v1, REAL* v2, REAL* n)
{
n[0] = v1[1] * v2[2] - v2[1] * v1[2];
n[1] = -(v1[0] * v2[2] - v2[0] * v1[2]);
n[2] = v1[0] * v2[1] - v2[0] * v1[1];
}
// distance() computes the Euclidean distance between two points.
inline REAL tetgenmesh::distance(REAL* p1, REAL* p2)
{
return sqrt((p2[0] - p1[0]) * (p2[0] - p1[0]) +
(p2[1] - p1[1]) * (p2[1] - p1[1]) +
(p2[2] - p1[2]) * (p2[2] - p1[2]));
}
inline REAL tetgenmesh::norm2(REAL x, REAL y, REAL z)
{
return (x) * (x) + (y) * (y) + (z) * (z);
}
#endif // #ifndef tetgenH
================================================
FILE: src/cpp/triangle.cpp
================================================
/*****************************************************************************/
/* */
/* 888888888 ,o, / 888 */
/* 888 88o88o " o8888o 88o8888o o88888o 888 o88888o */
/* 888 888 888 88b 888 888 888 888 888 d888 88b */
/* 888 888 888 o88^o888 888 888 "88888" 888 8888oo888 */
/* 888 888 888 C888 888 888 888 / 888 q888 */
/* 888 888 888 "88o^888 888 888 Cb 888 "88oooo" */
/* "8oo8D */
/* */
/* A Two-Dimensional Quality Mesh Generator and Delaunay Triangulator. */
/* (triangle.c) */
/* */
/* Version 1.6 */
/* July 28, 2005 */
/* */
/* Copyright 1993, 1995, 1997, 1998, 2002, 2005 */
/* Jonathan Richard Shewchuk */
/* 2360 Woolsey #H */
/* Berkeley, California 94705-1927 */
/* jrs@cs.berkeley.edu */
/* */
/* This program may be freely redistributed under the condition that the */
/* copyright notices (including this entire header and the copyright */
/* notice printed when the `-h' switch is selected) are not removed, and */
/* no compensation is received. Private, research, and institutional */
/* use is free. You may distribute modified versions of this code UNDER */
/* THE CONDITION THAT THIS CODE AND ANY MODIFICATIONS MADE TO IT IN THE */
/* SAME FILE REMAIN UNDER COPYRIGHT OF THE ORIGINAL AUTHOR, BOTH SOURCE */
/* AND OBJECT CODE ARE MADE FREELY AVAILABLE WITHOUT CHARGE, AND CLEAR */
/* NOTICE IS GIVEN OF THE MODIFICATIONS. Distribution of this code as */
/* part of a commercial system is permissible ONLY BY DIRECT ARRANGEMENT */
/* WITH THE AUTHOR. (If you are not directly supplying this code to a */
/* customer, and you are instead telling them how they can obtain it for */
/* free, then you are not required to make any arrangement with me.) */
/* */
/* Hypertext instructions for Triangle are available on the Web at */
/* */
/* http://www.cs.cmu.edu/~quake/triangle.html */
/* */
/* Disclaimer: Neither I nor Carnegie Mellon warrant this code in any way */
/* whatsoever. This code is provided "as-is". Use at your own risk. */
/* */
/* Some of the references listed below are marked with an asterisk. [*] */
/* These references are available for downloading from the Web page */
/* */
/* http://www.cs.cmu.edu/~quake/triangle.research.html */
/* */
/* Three papers discussing aspects of Triangle are available. A short */
/* overview appears in "Triangle: Engineering a 2D Quality Mesh */
/* Generator and Delaunay Triangulator," in Applied Computational */
/* Geometry: Towards Geometric Engineering, Ming C. Lin and Dinesh */
/* Manocha, editors, Lecture Notes in Computer Science volume 1148, */
/* pages 203-222, Springer-Verlag, Berlin, May 1996 (from the First ACM */
/* Workshop on Applied Computational Geometry). [*] */
/* */
/* The algorithms are discussed in the greatest detail in "Delaunay */
/* Refinement Algorithms for Triangular Mesh Generation," Computational */
/* Geometry: Theory and Applications 22(1-3):21-74, May 2002. [*] */
/* */
/* More detail about the data structures may be found in my dissertation: */
/* "Delaunay Refinement Mesh Generation," Ph.D. thesis, Technical Report */
/* CMU-CS-97-137, School of Computer Science, Carnegie Mellon University, */
/* Pittsburgh, Pennsylvania, 18 May 1997. [*] */
/* */
/* Triangle was created as part of the Quake Project in the School of */
/* Computer Science at Carnegie Mellon University. For further */
/* information, see Hesheng Bao, Jacobo Bielak, Omar Ghattas, Loukas F. */
/* Kallivokas, David R. O'Hallaron, Jonathan R. Shewchuk, and Jifeng Xu, */
/* "Large-scale Simulation of Elastic Wave Propagation in Heterogeneous */
/* Media on Parallel Computers," Computer Methods in Applied Mechanics */
/* and Engineering 152(1-2):85-102, 22 January 1998. */
/* */
/* Triangle's Delaunay refinement algorithm for quality mesh generation is */
/* a hybrid of one due to Jim Ruppert, "A Delaunay Refinement Algorithm */
/* for Quality 2-Dimensional Mesh Generation," Journal of Algorithms */
/* 18(3):548-585, May 1995 [*], and one due to L. Paul Chew, "Guaranteed- */
/* Quality Mesh Generation for Curved Surfaces," Proceedings of the Ninth */
/* Annual Symposium on Computational Geometry (San Diego, California), */
/* pages 274-280, Association for Computing Machinery, May 1993, */
/* http://portal.acm.org/citation.cfm?id=161150 . */
/* */
/* The Delaunay refinement algorithm has been modified so that it meshes */
/* domains with small input angles well, as described in Gary L. Miller, */
/* Steven E. Pav, and Noel J. Walkington, "When and Why Ruppert's */
/* Algorithm Works," Twelfth International Meshing Roundtable, pages */
/* 91-102, Sandia National Laboratories, September 2003. [*] */
/* */
/* My implementation of the divide-and-conquer and incremental Delaunay */
/* triangulation algorithms follows closely the presentation of Guibas */
/* and Stolfi, even though I use a triangle-based data structure instead */
/* of their quad-edge data structure. (In fact, I originally implemented */
/* Triangle using the quad-edge data structure, but the switch to a */
/* triangle-based data structure sped Triangle by a factor of two.) The */
/* mesh manipulation primitives and the two aforementioned Delaunay */
/* triangulation algorithms are described by Leonidas J. Guibas and Jorge */
/* Stolfi, "Primitives for the Manipulation of General Subdivisions and */
/* the Computation of Voronoi Diagrams," ACM Transactions on Graphics */
/* 4(2):74-123, April 1985, http://portal.acm.org/citation.cfm?id=282923 .*/
/* */
/* Their O(n log n) divide-and-conquer algorithm is adapted from Der-Tsai */
/* Lee and Bruce J. Schachter, "Two Algorithms for Constructing the */
/* Delaunay Triangulation," International Journal of Computer and */
/* Information Science 9(3):219-242, 1980. Triangle's improvement of the */
/* divide-and-conquer algorithm by alternating between vertical and */
/* horizontal cuts was introduced by Rex A. Dwyer, "A Faster Divide-and- */
/* Conquer Algorithm for Constructing Delaunay Triangulations," */
/* Algorithmica 2(2):137-151, 1987. */
/* */
/* The incremental insertion algorithm was first proposed by C. L. Lawson, */
/* "Software for C1 Surface Interpolation," in Mathematical Software III, */
/* John R. Rice, editor, Academic Press, New York, pp. 161-194, 1977. */
/* For point location, I use the algorithm of Ernst P. Mucke, Isaac */
/* Saias, and Binhai Zhu, "Fast Randomized Point Location Without */
/* Preprocessing in Two- and Three-Dimensional Delaunay Triangulations," */
/* Proceedings of the Twelfth Annual Symposium on Computational Geometry, */
/* ACM, May 1996. [*] If I were to randomize the order of vertex */
/* insertion (I currently don't bother), their result combined with the */
/* result of Kenneth L. Clarkson and Peter W. Shor, "Applications of */
/* Random Sampling in Computational Geometry II," Discrete & */
/* Computational Geometry 4(1):387-421, 1989, would yield an expected */
/* O(n^{4/3}) bound on running time. */
/* */
/* The O(n log n) sweepline Delaunay triangulation algorithm is taken from */
/* Steven Fortune, "A Sweepline Algorithm for Voronoi Diagrams", */
/* Algorithmica 2(2):153-174, 1987. A random sample of edges on the */
/* boundary of the triangulation are maintained in a splay tree for the */
/* purpose of point location. Splay trees are described by Daniel */
/* Dominic Sleator and Robert Endre Tarjan, "Self-Adjusting Binary Search */
/* Trees," Journal of the ACM 32(3):652-686, July 1985, */
/* http://portal.acm.org/citation.cfm?id=3835 . */
/* */
/* The algorithms for exact computation of the signs of determinants are */
/* described in Jonathan Richard Shewchuk, "Adaptive Precision Floating- */
/* Point Arithmetic and Fast Robust Geometric Predicates," Discrete & */
/* Computational Geometry 18(3):305-363, October 1997. (Also available */
/* as Technical Report CMU-CS-96-140, School of Computer Science, */
/* Carnegie Mellon University, Pittsburgh, Pennsylvania, May 1996.) [*] */
/* An abbreviated version appears as Jonathan Richard Shewchuk, "Robust */
/* Adaptive Floating-Point Geometric Predicates," Proceedings of the */
/* Twelfth Annual Symposium on Computational Geometry, ACM, May 1996. [*] */
/* Many of the ideas for my exact arithmetic routines originate with */
/* Douglas M. Priest, "Algorithms for Arbitrary Precision Floating Point */
/* Arithmetic," Tenth Symposium on Computer Arithmetic, pp. 132-143, IEEE */
/* Computer Society Press, 1991. [*] Many of the ideas for the correct */
/* evaluation of the signs of determinants are taken from Steven Fortune */
/* and Christopher J. Van Wyk, "Efficient Exact Arithmetic for Computa- */
/* tional Geometry," Proceedings of the Ninth Annual Symposium on */
/* Computational Geometry, ACM, pp. 163-172, May 1993, and from Steven */
/* Fortune, "Numerical Stability of Algorithms for 2D Delaunay Triangu- */
/* lations," International Journal of Computational Geometry & Applica- */
/* tions 5(1-2):193-213, March-June 1995. */
/* */
/* The method of inserting new vertices off-center (not precisely at the */
/* circumcenter of every poor-quality triangle) is from Alper Ungor, */
/* "Off-centers: A New Type of Steiner Points for Computing Size-Optimal */
/* Quality-Guaranteed Delaunay Triangulations," Proceedings of LATIN */
/* 2004 (Buenos Aires, Argentina), April 2004. */
/* */
/* For definitions of and results involving Delaunay triangulations, */
/* constrained and conforming versions thereof, and other aspects of */
/* triangular mesh generation, see the excellent survey by Marshall Bern */
/* and David Eppstein, "Mesh Generation and Optimal Triangulation," in */
/* Computing and Euclidean Geometry, Ding-Zhu Du and Frank Hwang, */
/* editors, World Scientific, Singapore, pp. 23-90, 1992. [*] */
/* */
/* The time for incrementally adding PSLG (planar straight line graph) */
/* segments to create a constrained Delaunay triangulation is probably */
/* O(t^2) per segment in the worst case and O(t) per segment in the */
/* common case, where t is the number of triangles that intersect the */
/* segment before it is inserted. This doesn't count point location, */
/* which can be much more expensive. I could improve this to O(d log d) */
/* time, but d is usually quite small, so it's not worth the bother. */
/* (This note does not apply when the -s switch is used, invoking a */
/* different method is used to insert segments.) */
/* */
/* The time for deleting a vertex from a Delaunay triangulation is O(d^2) */
/* in the worst case and O(d) in the common case, where d is the degree */
/* of the vertex being deleted. I could improve this to O(d log d) time, */
/* but d is usually quite small, so it's not worth the bother. */
/* */
/* Ruppert's Delaunay refinement algorithm typically generates triangles */
/* at a linear rate (constant time per triangle) after the initial */
/* triangulation is formed. There may be pathological cases where */
/* quadratic time is required, but these never arise in practice. */
/* */
/* The geometric predicates (circumcenter calculations, segment */
/* intersection formulae, etc.) appear in my "Lecture Notes on Geometric */
/* Robustness" at http://www.cs.berkeley.edu/~jrs/mesh . */
/* */
/* If you make any improvements to this code, please please please let me */
/* know, so that I may obtain the improvements. Even if you don't change */
/* the code, I'd still love to hear what it's being used for. */
/* */
/*****************************************************************************/
/* For single precision (which will save some memory and reduce paging), */
/* define the symbol SINGLE by using the -DSINGLE compiler switch or by */
/* writing "#define SINGLE" below. */
/* */
/* For double precision (which will allow you to refine meshes to a smaller */
/* edge length), leave SINGLE undefined. */
/* */
/* Double precision uses more memory, but improves the resolution of the */
/* meshes you can generate with Triangle. It also reduces the likelihood */
/* of a floating exception due to overflow. Finally, it is much faster */
/* than single precision on 64-bit architectures like the DEC Alpha. I */
/* recommend double precision unless you want to generate a mesh for which */
/* you do not have enough memory. */
/* #define SINGLE */
#ifdef SINGLE
#define REAL float
#else /* not SINGLE */
#define REAL double
#endif /* not SINGLE */
/* If yours is not a Unix system, define the NO_TIMER compiler switch to */
/* remove the Unix-specific timing code. */
#define NO_TIMER
/* To insert lots of self-checks for internal errors, define the SELF_CHECK */
/* symbol. This will slow down the program significantly. It is best to */
/* define the symbol using the -DSELF_CHECK compiler switch, but you could */
/* write "#define SELF_CHECK" below. If you are modifying this code, I */
/* recommend you turn self-checks on until your work is debugged. */
/* #define SELF_CHECK */
/* To compile Triangle as a callable object library (triangle.o), define the */
/* TRILIBRARY symbol. Read the file triangle.h for details on how to call */
/* the procedure triangulate() that results. */
/* #define TRILIBRARY */
/* It is possible to generate a smaller version of Triangle using one or */
/* both of the following symbols. Define the REDUCED symbol to eliminate */
/* all features that are primarily of research interest; specifically, the */
/* -i, -F, -s, and -C switches. Define the CDT_ONLY symbol to eliminate */
/* all meshing algorithms above and beyond constrained Delaunay */
/* triangulation; specifically, the -r, -q, -a, -u, -D, -S, and -s */
/* switches. These reductions are most likely to be useful when */
/* generating an object library (triangle.o) by defining the TRILIBRARY */
/* symbol. */
/* #define REDUCED */
/* #define CDT_ONLY */
/* On some machines, my exact arithmetic routines might be defeated by the */
/* use of internal extended precision floating-point registers. The best */
/* way to solve this problem is to set the floating-point registers to use */
/* single or double precision internally. On 80x86 processors, this may */
/* be accomplished by setting the CPU86 symbol for the Microsoft C */
/* compiler, or the LINUX symbol for the gcc compiler running on Linux. */
/* */
/* An inferior solution is to declare certain values as `volatile', thus */
/* forcing them to be stored to memory and rounded off. Unfortunately, */
/* this solution might slow Triangle down quite a bit. To use volatile */
/* values, write "#define INEXACT volatile" below. Normally, however, */
/* INEXACT should be defined to be nothing. ("#define INEXACT".) */
/* */
/* For more discussion, see http://www.cs.cmu.edu/~quake/robust.pc.html . */
/* For yet more discussion, see Section 5 of my paper, "Adaptive Precision */
/* Floating-Point Arithmetic and Fast Robust Geometric Predicates" (also */
/* available as Section 6.6 of my dissertation). */
void exactdeinit(void);
/* #define CPU86 */
/* #define LINUX */
#if defined(__linux__) && defined(__i386__)
#define LINUX 1
#endif
#define INEXACT /* Nothing */
/* #define INEXACT volatile */
/* Maximum number of characters in a file name (including the null). */
#define FILENAMESIZE 2048
/* Maximum number of characters in a line read from a file (including the */
/* null). */
#define INPUTLINESIZE 1024
/* For efficiency, a variety of data structures are allocated in bulk. The */
/* following constants determine how many of each structure is allocated */
/* at once. */
#define TRIPERBLOCK 4092 /* Number of triangles allocated at once. */
#define SUBSEGPERBLOCK 508 /* Number of subsegments allocated at once. */
#define VERTEXPERBLOCK 4092 /* Number of vertices allocated at once. */
#define VIRUSPERBLOCK 1020 /* Number of virus triangles allocated at once. */
/* Number of encroached subsegments allocated at once. */
#define BADSUBSEGPERBLOCK 252
/* Number of skinny triangles allocated at once. */
#define BADTRIPERBLOCK 4092
/* Number of flipped triangles allocated at once. */
#define FLIPSTACKERPERBLOCK 252
/* Number of splay tree nodes allocated at once. */
#define SPLAYNODEPERBLOCK 508
/* The vertex types. A DEADVERTEX has been deleted entirely. An */
/* UNDEADVERTEX is not part of the mesh, but is written to the output */
/* .node file and affects the node indexing in the other output files. */
#define INPUTVERTEX 0
#define SEGMENTVERTEX 1
#define FREEVERTEX 2
#define DEADVERTEX -32768
#define UNDEADVERTEX -32767
/* The next line is used to outsmart some very stupid compilers. If your */
/* compiler is smarter, feel free to replace the "int" with "void". */
/* Not that it matters. */
// #define VOID int
/* Two constants for algorithms based on random sampling. Both constants */
/* have been chosen empirically to optimize their respective algorithms. */
/* Used for the point location scheme of Mucke, Saias, and Zhu, to decide */
/* how large a random sample of triangles to inspect. */
#define SAMPLEFACTOR 11
/* Used in Fortune's sweepline Delaunay algorithm to determine what fraction */
/* of boundary edges should be maintained in the splay tree for point */
/* location on the front. */
#define SAMPLERATE 10
/* A number that speaks for itself, every kissable digit. */
#define PI 3.141592653589793238462643383279502884197169399375105820974944592308
/* Another fave. */
#define SQUAREROOTTWO 1.4142135623730950488016887242096980785696718753769480732
/* And here's one for those of you who are intimidated by math. */
#define ONETHIRD 0.333333333333333333333333333333333333333333333333333333333333
#include
#include
#include
#include
#ifndef NO_TIMER
#include
#endif /* not NO_TIMER */
#ifdef CPU86
#include
#endif /* CPU86 */
#ifdef LINUX
#include
#endif /* LINUX */
#ifdef TRILIBRARY
#include "triangle.h"
#endif /* TRILIBRARY */
/* A few forward declarations. */
#ifndef TRILIBRARY
char *readline();
char *findfield();
#endif /* not TRILIBRARY */
/* Labels that signify the result of point location. The result of a */
/* search indicates that the point falls in the interior of a triangle, on */
/* an edge, on a vertex, or outside the mesh. */
enum locateresult {INTRIANGLE, ONEDGE, ONVERTEX, OUTSIDE};
/* Labels that signify the result of vertex insertion. The result indicates */
/* that the vertex was inserted with complete success, was inserted but */
/* encroaches upon a subsegment, was not inserted because it lies on a */
/* segment, or was not inserted because another vertex occupies the same */
/* location. */
enum insertvertexresult {SUCCESSFULVERTEX, ENCROACHINGVERTEX, VIOLATINGVERTEX,
DUPLICATEVERTEX};
/* Labels that signify the result of direction finding. The result */
/* indicates that a segment connecting the two query points falls within */
/* the direction triangle, along the left edge of the direction triangle, */
/* or along the right edge of the direction triangle. */
enum finddirectionresult {WITHIN, LEFTCOLLINEAR, RIGHTCOLLINEAR};
/*****************************************************************************/
/* */
/* The basic mesh data structures */
/* */
/* There are three: vertices, triangles, and subsegments (abbreviated */
/* `subseg'). These three data structures, linked by pointers, comprise */
/* the mesh. A vertex simply represents a mesh vertex and its properties. */
/* A triangle is a triangle. A subsegment is a special data structure used */
/* to represent an impenetrable edge of the mesh (perhaps on the outer */
/* boundary, on the boundary of a hole, or part of an internal boundary */
/* separating two triangulated regions). Subsegments represent boundaries, */
/* defined by the user, that triangles may not lie across. */
/* */
/* A triangle consists of a list of three vertices, a list of three */
/* adjoining triangles, a list of three adjoining subsegments (when */
/* segments exist), an arbitrary number of optional user-defined */
/* floating-point attributes, and an optional area constraint. The latter */
/* is an upper bound on the permissible area of each triangle in a region, */
/* used for mesh refinement. */
/* */
/* For a triangle on a boundary of the mesh, some or all of the neighboring */
/* triangles may not be present. For a triangle in the interior of the */
/* mesh, often no neighboring subsegments are present. Such absent */
/* triangles and subsegments are never represented by NULL pointers; they */
/* are represented by two special records: `dummytri', the triangle that */
/* fills "outer space", and `dummysub', the omnipresent subsegment. */
/* `dummytri' and `dummysub' are used for several reasons; for instance, */
/* they can be dereferenced and their contents examined without violating */
/* protected memory. */
/* */
/* However, it is important to understand that a triangle includes other */
/* information as well. The pointers to adjoining vertices, triangles, and */
/* subsegments are ordered in a way that indicates their geometric relation */
/* to each other. Furthermore, each of these pointers contains orientation */
/* information. Each pointer to an adjoining triangle indicates which face */
/* of that triangle is contacted. Similarly, each pointer to an adjoining */
/* subsegment indicates which side of that subsegment is contacted, and how */
/* the subsegment is oriented relative to the triangle. */
/* */
/* The data structure representing a subsegment may be thought to be */
/* abutting the edge of one or two triangle data structures: either */
/* sandwiched between two triangles, or resting against one triangle on an */
/* exterior boundary or hole boundary. */
/* */
/* A subsegment consists of a list of four vertices--the vertices of the */
/* subsegment, and the vertices of the segment it is a part of--a list of */
/* two adjoining subsegments, and a list of two adjoining triangles. One */
/* of the two adjoining triangles may not be present (though there should */
/* always be one), and neighboring subsegments might not be present. */
/* Subsegments also store a user-defined integer "boundary marker". */
/* Typically, this integer is used to indicate what boundary conditions are */
/* to be applied at that location in a finite element simulation. */
/* */
/* Like triangles, subsegments maintain information about the relative */
/* orientation of neighboring objects. */
/* */
/* Vertices are relatively simple. A vertex is a list of floating-point */
/* numbers, starting with the x, and y coordinates, followed by an */
/* arbitrary number of optional user-defined floating-point attributes, */
/* followed by an integer boundary marker. During the segment insertion */
/* phase, there is also a pointer from each vertex to a triangle that may */
/* contain it. Each pointer is not always correct, but when one is, it */
/* speeds up segment insertion. These pointers are assigned values once */
/* at the beginning of the segment insertion phase, and are not used or */
/* updated except during this phase. Edge flipping during segment */
/* insertion will render some of them incorrect. Hence, don't rely upon */
/* them for anything. */
/* */
/* Other than the exception mentioned above, vertices have no information */
/* about what triangles, subfacets, or subsegments they are linked to. */
/* */
/*****************************************************************************/
/*****************************************************************************/
/* */
/* Handles */
/* */
/* The oriented triangle (`otri') and oriented subsegment (`osub') data */
/* structures defined below do not themselves store any part of the mesh. */
/* The mesh itself is made of `triangle's, `subseg's, and `vertex's. */
/* */
/* Oriented triangles and oriented subsegments will usually be referred to */
/* as "handles." A handle is essentially a pointer into the mesh; it */
/* allows you to "hold" one particular part of the mesh. Handles are used */
/* to specify the regions in which one is traversing and modifying the mesh.*/
/* A single `triangle' may be held by many handles, or none at all. (The */
/* latter case is not a memory leak, because the triangle is still */
/* connected to other triangles in the mesh.) */
/* */
/* An `otri' is a handle that holds a triangle. It holds a specific edge */
/* of the triangle. An `osub' is a handle that holds a subsegment. It */
/* holds either the left or right side of the subsegment. */
/* */
/* Navigation about the mesh is accomplished through a set of mesh */
/* manipulation primitives, further below. Many of these primitives take */
/* a handle and produce a new handle that holds the mesh near the first */
/* handle. Other primitives take two handles and glue the corresponding */
/* parts of the mesh together. The orientation of the handles is */
/* important. For instance, when two triangles are glued together by the */
/* bond() primitive, they are glued at the edges on which the handles lie. */
/* */
/* Because vertices have no information about which triangles they are */
/* attached to, I commonly represent a vertex by use of a handle whose */
/* origin is the vertex. A single handle can simultaneously represent a */
/* triangle, an edge, and a vertex. */
/* */
/*****************************************************************************/
/* The triangle data structure. Each triangle contains three pointers to */
/* adjoining triangles, plus three pointers to vertices, plus three */
/* pointers to subsegments (declared below; these pointers are usually */
/* `dummysub'). It may or may not also contain user-defined attributes */
/* and/or a floating-point "area constraint." It may also contain extra */
/* pointers for nodes, when the user asks for high-order elements. */
/* Because the size and structure of a `triangle' is not decided until */
/* runtime, I haven't simply declared the type `triangle' as a struct. */
typedef REAL **triangle; /* Really: typedef triangle *triangle */
/* An oriented triangle: includes a pointer to a triangle and orientation. */
/* The orientation denotes an edge of the triangle. Hence, there are */
/* three possible orientations. By convention, each edge always points */
/* counterclockwise about the corresponding triangle. */
struct otri {
triangle *tri;
int orient; /* Ranges from 0 to 2. */
};
/* The subsegment data structure. Each subsegment contains two pointers to */
/* adjoining subsegments, plus four pointers to vertices, plus two */
/* pointers to adjoining triangles, plus one boundary marker, plus one */
/* segment number. */
typedef REAL **subseg; /* Really: typedef subseg *subseg */
/* An oriented subsegment: includes a pointer to a subsegment and an */
/* orientation. The orientation denotes a side of the edge. Hence, there */
/* are two possible orientations. By convention, the edge is always */
/* directed so that the "side" denoted is the right side of the edge. */
struct osub {
subseg *ss;
int ssorient; /* Ranges from 0 to 1. */
};
/* A queue used to store encroached subsegments. Each subsegment's vertices */
/* are stored so that we can check whether a subsegment is still the same. */
struct badsubseg {
subseg encsubseg; /* An encroached subsegment. */
vertex subsegorg, subsegdest; /* Its two vertices. */
};
/* A queue used to store bad triangles. The key is the square of the cosine */
/* of the smallest angle of the triangle. Each triangle's vertices are */
/* stored so that one can check whether a triangle is still the same. */
struct badtriang {
triangle poortri; /* A skinny or too-large triangle. */
REAL key; /* cos^2 of smallest (apical) angle. */
vertex triangorg, triangdest, triangapex; /* Its three vertices. */
struct badtriang *nexttriang; /* Pointer to next bad triangle. */
};
/* A stack of triangles flipped during the most recent vertex insertion. */
/* The stack is used to undo the vertex insertion if the vertex encroaches */
/* upon a subsegment. */
struct flipstacker {
triangle flippedtri; /* A recently flipped triangle. */
struct flipstacker *prevflip; /* Previous flip in the stack. */
};
/* A node in a heap used to store events for the sweepline Delaunay */
/* algorithm. Nodes do not point directly to their parents or children in */
/* the heap. Instead, each node knows its position in the heap, and can */
/* look up its parent and children in a separate array. The `eventptr' */
/* points either to a `vertex' or to a triangle (in encoded format, so */
/* that an orientation is included). In the latter case, the origin of */
/* the oriented triangle is the apex of a "circle event" of the sweepline */
/* algorithm. To distinguish site events from circle events, all circle */
/* events are given an invalid (smaller than `xmin') x-coordinate `xkey'. */
struct event {
REAL xkey, ykey; /* Coordinates of the event. */
VOID *eventptr; /* Can be a vertex or the location of a circle event. */
int heapposition; /* Marks this event's position in the heap. */
};
/* A node in the splay tree. Each node holds an oriented ghost triangle */
/* that represents a boundary edge of the growing triangulation. When a */
/* circle event covers two boundary edges with a triangle, so that they */
/* are no longer boundary edges, those edges are not immediately deleted */
/* from the tree; rather, they are lazily deleted when they are next */
/* encountered. (Since only a random sample of boundary edges are kept */
/* in the tree, lazy deletion is faster.) `keydest' is used to verify */
/* that a triangle is still the same as when it entered the splay tree; if */
/* it has been rotated (due to a circle event), it no longer represents a */
/* boundary edge and should be deleted. */
struct splaynode {
struct otri keyedge; /* Lprev of an edge on the front. */
vertex keydest; /* Used to verify that splay node is still live. */
struct splaynode *lchild, *rchild; /* Children in splay tree. */
};
/* A type used to allocate memory. 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. */
/* */
/* alignbytes determines how new records should be aligned in memory. */
/* itembytes is the length of a record in bytes (after rounding up). */
/* itemsperblock is the number of items allocated at once in a single */
/* block. itemsfirstblock is the number of items in the first block, */
/* which can vary from the others. items is the number of currently */
/* allocated items. maxitems is the maximum number of items that have */
/* been allocated at once; it is the current number of items plus the */
/* number of records kept on deaditemstack. */
struct memorypool {
VOID **firstblock, **nowblock;
VOID *nextitem;
VOID *deaditemstack;
VOID **pathblock;
VOID *pathitem;
int alignbytes;
int itembytes;
int itemsperblock;
int itemsfirstblock;
long items, maxitems;
int unallocateditems;
int pathitemsleft;
};
/* Global constants. */
REAL splitter; /* Used to split REAL factors for exact multiplication. */
REAL epsilon; /* Floating-point machine epsilon. */
REAL resulterrbound;
REAL ccwerrboundA, ccwerrboundB, ccwerrboundC;
REAL iccerrboundA, iccerrboundB, iccerrboundC;
REAL o3derrboundA, o3derrboundB, o3derrboundC;
/* Random number seed is not constant, but I've made it global anyway. */
unsigned long randomseed; /* Current random number seed. */
/* Mesh data structure. Triangle operates on only one mesh, but the mesh */
/* structure is used (instead of global variables) to allow reentrancy. */
struct mesh {
/* Variables used to allocate memory for triangles, subsegments, vertices, */
/* viri (triangles being eaten), encroached segments, bad (skinny or too */
/* large) triangles, and splay tree nodes. */
struct memorypool triangles;
struct memorypool subsegs;
struct memorypool vertices;
struct memorypool viri;
struct memorypool badsubsegs;
struct memorypool badtriangles;
struct memorypool flipstackers;
struct memorypool splaynodes;
/* Variables that maintain the bad triangle queues. The queues are */
/* ordered from 4095 (highest priority) to 0 (lowest priority). */
struct badtriang *queuefront[4096];
struct badtriang *queuetail[4096];
int nextnonemptyq[4096];
int firstnonemptyq;
/* Variable that maintains the stack of recently flipped triangles. */
struct flipstacker *lastflip;
/* Other variables. */
REAL xmin, xmax, ymin, ymax; /* x and y bounds. */
REAL xminextreme; /* Nonexistent x value used as a flag in sweepline. */
int invertices; /* Number of input vertices. */
int inelements; /* Number of input triangles. */
int insegments; /* Number of input segments. */
int holes; /* Number of input holes. */
int regions; /* Number of input regions. */
int undeads; /* Number of input vertices that don't appear in the mesh. */
long edges; /* Number of output edges. */
int mesh_dim; /* Dimension (ought to be 2). */
int nextras; /* Number of attributes per vertex. */
int eextras; /* Number of attributes per triangle. */
long hullsize; /* Number of edges in convex hull. */
int steinerleft; /* Number of Steiner points not yet used. */
int vertexmarkindex; /* Index to find boundary marker of a vertex. */
int vertex2triindex; /* Index to find a triangle adjacent to a vertex. */
int highorderindex; /* Index to find extra nodes for high-order elements. */
int elemattribindex; /* Index to find attributes of a triangle. */
int areaboundindex; /* Index to find area bound of a triangle. */
int checksegments; /* Are there segments in the triangulation yet? */
int checkquality; /* Has quality triangulation begun yet? */
int readnodefile; /* Has a .node file been read? */
long samples; /* Number of random samples for point location. */
long incirclecount; /* Number of incircle tests performed. */
long counterclockcount; /* Number of counterclockwise tests performed. */
long orient3dcount; /* Number of 3D orientation tests performed. */
long hyperbolacount; /* Number of right-of-hyperbola tests performed. */
long circumcentercount; /* Number of circumcenter calculations performed. */
long circletopcount; /* Number of circle top calculations performed. */
/* Triangular bounding box vertices. */
vertex infvertex1, infvertex2, infvertex3;
/* Pointer to the `triangle' that occupies all of "outer space." */
triangle *dummytri;
triangle *dummytribase; /* Keep base address so we can free() it later. */
/* Pointer to the omnipresent subsegment. Referenced by any triangle or */
/* subsegment that isn't really connected to a subsegment at that */
/* location. */
subseg *dummysub;
subseg *dummysubbase; /* Keep base address so we can free() it later. */
/* Pointer to a recently visited triangle. Improves point location if */
/* proximate vertices are inserted sequentially. */
struct otri recenttri;
}; /* End of `struct mesh'. */
/* Data structure for command line switches and file names. This structure */
/* is used (instead of global variables) to allow reentrancy. */
struct behavior {
/* Switches for the triangulator. */
/* poly: -p switch. refine: -r switch. */
/* quality: -q switch. */
/* minangle: minimum angle bound, specified after -q switch. */
/* goodangle: cosine squared of minangle. */
/* offconstant: constant used to place off-center Steiner points. */
/* vararea: -a switch without number. */
/* fixedarea: -a switch with number. */
/* maxarea: maximum area bound, specified after -a switch. */
/* usertest: -u switch. */
/* regionattrib: -A switch. convex: -c switch. */
/* weighted: 1 for -w switch, 2 for -W switch. jettison: -j switch */
/* firstnumber: inverse of -z switch. All items are numbered starting */
/* from `firstnumber'. */
/* edgesout: -e switch. voronoi: -v switch. */
/* neighbors: -n switch. geomview: -g switch. */
/* nobound: -B switch. nopolywritten: -P switch. */
/* nonodewritten: -N switch. noelewritten: -E switch. */
/* noiterationnum: -I switch. noholes: -O switch. */
/* noexact: -X switch. */
/* order: element order, specified after -o switch. */
/* nobisect: count of how often -Y switch is selected. */
/* steiner: maximum number of Steiner points, specified after -S switch. */
/* incremental: -i switch. sweepline: -F switch. */
/* dwyer: inverse of -l switch. */
/* splitseg: -s switch. */
/* conformdel: -D switch. docheck: -C switch. */
/* quiet: -Q switch. verbose: count of how often -V switch is selected. */
/* usesegments: -p, -r, -q, or -c switch; determines whether segments are */
/* used at all. */
/* */
/* Read the instructions to find out the meaning of these switches. */
int poly, refine, quality, vararea, fixedarea, usertest;
int regionattrib, convex, weighted, jettison;
int firstnumber;
int edgesout, voronoi, neighbors, geomview;
int nobound, nopolywritten, nonodewritten, noelewritten, noiterationnum;
int noholes, noexact, conformdel;
int incremental, sweepline, dwyer;
int splitseg;
int docheck;
int quiet, verbose;
int usesegments;
int order;
int nobisect;
int steiner;
REAL minangle, goodangle, offconstant;
REAL maxarea;
/* Variables for file names. */
#ifndef TRILIBRARY
char innodefilename[FILENAMESIZE];
char inelefilename[FILENAMESIZE];
char inpolyfilename[FILENAMESIZE];
char areafilename[FILENAMESIZE];
char outnodefilename[FILENAMESIZE];
char outelefilename[FILENAMESIZE];
char outpolyfilename[FILENAMESIZE];
char edgefilename[FILENAMESIZE];
char vnodefilename[FILENAMESIZE];
char vedgefilename[FILENAMESIZE];
char neighborfilename[FILENAMESIZE];
char offfilename[FILENAMESIZE];
#endif /* not TRILIBRARY */
}; /* End of `struct behavior'. */
/*****************************************************************************/
/* */
/* Mesh manipulation primitives. Each triangle contains three pointers to */
/* other triangles, with orientations. Each pointer points not to the */
/* first byte of a triangle, but to one of the first three bytes of a */
/* triangle. It is necessary to extract both the triangle itself and the */
/* orientation. To save memory, I keep both pieces of information in one */
/* pointer. To make this possible, I assume that all triangles are aligned */
/* to four-byte boundaries. The decode() routine below decodes a pointer, */
/* extracting an orientation (in the range 0 to 2) and a pointer to the */
/* beginning of a triangle. The encode() routine compresses a pointer to a */
/* triangle and an orientation into a single pointer. My assumptions that */
/* triangles are four-byte-aligned and that the `unsigned long' type is */
/* long enough to hold a pointer are two of the few kludges in this program.*/
/* */
/* Subsegments are manipulated similarly. A pointer to a subsegment */
/* carries both an address and an orientation in the range 0 to 1. */
/* */
/* The other primitives take an oriented triangle or oriented subsegment, */
/* and return an oriented triangle or oriented subsegment or vertex; or */
/* they change the connections in the data structure. */
/* */
/* Below, triangles and subsegments are denoted by their vertices. The */
/* triangle abc has origin (org) a, destination (dest) b, and apex (apex) */
/* c. These vertices occur in counterclockwise order about the triangle. */
/* The handle abc may simultaneously denote vertex a, edge ab, and triangle */
/* abc. */
/* */
/* Similarly, the subsegment ab has origin (sorg) a and destination (sdest) */
/* b. If ab is thought to be directed upward (with b directly above a), */
/* then the handle ab is thought to grasp the right side of ab, and may */
/* simultaneously denote vertex a and edge ab. */
/* */
/* An asterisk (*) denotes a vertex whose identity is unknown. */
/* */
/* Given this notation, a partial list of mesh manipulation primitives */
/* follows. */
/* */
/* */
/* For triangles: */
/* */
/* sym: Find the abutting triangle; same edge. */
/* sym(abc) -> ba* */
/* */
/* lnext: Find the next edge (counterclockwise) of a triangle. */
/* lnext(abc) -> bca */
/* */
/* lprev: Find the previous edge (clockwise) of a triangle. */
/* lprev(abc) -> cab */
/* */
/* onext: Find the next edge counterclockwise with the same origin. */
/* onext(abc) -> ac* */
/* */
/* oprev: Find the next edge clockwise with the same origin. */
/* oprev(abc) -> a*b */
/* */
/* dnext: Find the next edge counterclockwise with the same destination. */
/* dnext(abc) -> *ba */
/* */
/* dprev: Find the next edge clockwise with the same destination. */
/* dprev(abc) -> cb* */
/* */
/* rnext: Find the next edge (counterclockwise) of the adjacent triangle. */
/* rnext(abc) -> *a* */
/* */
/* rprev: Find the previous edge (clockwise) of the adjacent triangle. */
/* rprev(abc) -> b** */
/* */
/* org: Origin dest: Destination apex: Apex */
/* org(abc) -> a dest(abc) -> b apex(abc) -> c */
/* */
/* bond: Bond two triangles together at the resepective handles. */
/* bond(abc, bad) */
/* */
/* */
/* For subsegments: */
/* */
/* ssym: Reverse the orientation of a subsegment. */
/* ssym(ab) -> ba */
/* */
/* spivot: Find adjoining subsegment with the same origin. */
/* spivot(ab) -> a* */
/* */
/* snext: Find next subsegment in sequence. */
/* snext(ab) -> b* */
/* */
/* sorg: Origin sdest: Destination */
/* sorg(ab) -> a sdest(ab) -> b */
/* */
/* sbond: Bond two subsegments together at the respective origins. */
/* sbond(ab, ac) */
/* */
/* */
/* For interacting tetrahedra and subfacets: */
/* */
/* tspivot: Find a subsegment abutting a triangle. */
/* tspivot(abc) -> ba */
/* */
/* stpivot: Find a triangle abutting a subsegment. */
/* stpivot(ab) -> ba* */
/* */
/* tsbond: Bond a triangle to a subsegment. */
/* tsbond(abc, ba) */
/* */
/*****************************************************************************/
/********* Mesh manipulation primitives begin here *********/
/** **/
/** **/
/* Fast lookup arrays to speed some of the mesh manipulation primitives. */
int plus1mod3[3] = {1, 2, 0};
int minus1mod3[3] = {2, 0, 1};
/********* Primitives for triangles *********/
/* */
/* */
/* decode() converts a pointer to an oriented triangle. The orientation is */
/* extracted from the two least significant bits of the pointer. */
#define decode(ptr, otri) \
(otri).orient = (int) ((size_t) (ptr) & (size_t) 3l); \
(otri).tri = (triangle *) \
((size_t) (ptr) ^ (size_t) (otri).orient)
/* encode() compresses an oriented triangle into a single pointer. It */
/* relies on the assumption that all triangles are aligned to four-byte */
/* boundaries, so the two least significant bits of (otri).tri are zero. */
#define encode(otri) \
(triangle) ((size_t) (otri).tri | (size_t) (otri).orient)
/* The following handle manipulation primitives are all described by Guibas */
/* and Stolfi. However, Guibas and Stolfi use an edge-based data */
/* structure, whereas I use a triangle-based data structure. */
/* sym() finds the abutting triangle, on the same edge. Note that the edge */
/* direction is necessarily reversed, because the handle specified by an */
/* oriented triangle is directed counterclockwise around the triangle. */
#define sym(otri1, otri2) \
ptr = (otri1).tri[(otri1).orient]; \
decode(ptr, otri2);
#define symself(otri) \
ptr = (otri).tri[(otri).orient]; \
decode(ptr, otri);
/* lnext() finds the next edge (counterclockwise) of a triangle. */
#define lnext(otri1, otri2) \
(otri2).tri = (otri1).tri; \
(otri2).orient = plus1mod3[(otri1).orient]
#define lnextself(otri) \
(otri).orient = plus1mod3[(otri).orient]
/* lprev() finds the previous edge (clockwise) of a triangle. */
#define lprev(otri1, otri2) \
(otri2).tri = (otri1).tri; \
(otri2).orient = minus1mod3[(otri1).orient]
#define lprevself(otri) \
(otri).orient = minus1mod3[(otri).orient]
/* onext() spins counterclockwise around a vertex; that is, it finds the */
/* next edge with the same origin in the counterclockwise direction. This */
/* edge is part of a different triangle. */
#define onext(otri1, otri2) \
lprev(otri1, otri2); \
symself(otri2);
#define onextself(otri) \
lprevself(otri); \
symself(otri);
/* oprev() spins clockwise around a vertex; that is, it finds the next edge */
/* with the same origin in the clockwise direction. This edge is part of */
/* a different triangle. */
#define oprev(otri1, otri2) \
sym(otri1, otri2); \
lnextself(otri2);
#define oprevself(otri) \
symself(otri); \
lnextself(otri);
/* dnext() spins counterclockwise around a vertex; that is, it finds the */
/* next edge with the same destination in the counterclockwise direction. */
/* This edge is part of a different triangle. */
#define dnext(otri1, otri2) \
sym(otri1, otri2); \
lprevself(otri2);
#define dnextself(otri) \
symself(otri); \
lprevself(otri);
/* dprev() spins clockwise around a vertex; that is, it finds the next edge */
/* with the same destination in the clockwise direction. This edge is */
/* part of a different triangle. */
#define dprev(otri1, otri2) \
lnext(otri1, otri2); \
symself(otri2);
#define dprevself(otri) \
lnextself(otri); \
symself(otri);
/* rnext() moves one edge counterclockwise about the adjacent triangle. */
/* (It's best understood by reading Guibas and Stolfi. It involves */
/* changing triangles twice.) */
#define rnext(otri1, otri2) \
sym(otri1, otri2); \
lnextself(otri2); \
symself(otri2);
#define rnextself(otri) \
symself(otri); \
lnextself(otri); \
symself(otri);
/* rprev() moves one edge clockwise about the adjacent triangle. */
/* (It's best understood by reading Guibas and Stolfi. It involves */
/* changing triangles twice.) */
#define rprev(otri1, otri2) \
sym(otri1, otri2); \
lprevself(otri2); \
symself(otri2);
#define rprevself(otri) \
symself(otri); \
lprevself(otri); \
symself(otri);
/* These primitives determine or set the origin, destination, or apex of a */
/* triangle. */
#define org(otri, vertexptr) \
vertexptr = (vertex) (otri).tri[plus1mod3[(otri).orient] + 3]
#define dest(otri, vertexptr) \
vertexptr = (vertex) (otri).tri[minus1mod3[(otri).orient] + 3]
#define apex(otri, vertexptr) \
vertexptr = (vertex) (otri).tri[(otri).orient + 3]
#define setorg(otri, vertexptr) \
(otri).tri[plus1mod3[(otri).orient] + 3] = (triangle) vertexptr
#define setdest(otri, vertexptr) \
(otri).tri[minus1mod3[(otri).orient] + 3] = (triangle) vertexptr
#define setapex(otri, vertexptr) \
(otri).tri[(otri).orient + 3] = (triangle) vertexptr
/* Bond two triangles together. */
#define bond(otri1, otri2) \
(otri1).tri[(otri1).orient] = encode(otri2); \
(otri2).tri[(otri2).orient] = encode(otri1)
/* Dissolve a bond (from one side). Note that the other triangle will still */
/* think it's connected to this triangle. Usually, however, the other */
/* triangle is being deleted entirely, or bonded to another triangle, so */
/* it doesn't matter. */
#define dissolve(otri) \
(otri).tri[(otri).orient] = (triangle) m->dummytri
/* Copy an oriented triangle. */
#define otricopy(otri1, otri2) \
(otri2).tri = (otri1).tri; \
(otri2).orient = (otri1).orient
/* Test for equality of oriented triangles. */
#define otriequal(otri1, otri2) \
(((otri1).tri == (otri2).tri) && \
((otri1).orient == (otri2).orient))
/* Primitives to infect or cure a triangle with the virus. These rely on */
/* the assumption that all subsegments are aligned to four-byte boundaries.*/
#define infect(otri) \
(otri).tri[6] = (triangle) \
((size_t) (otri).tri[6] | (size_t) 2l)
#define uninfect(otri) \
(otri).tri[6] = (triangle) \
((size_t) (otri).tri[6] & ~ (size_t) 2l)
/* Test a triangle for viral infection. */
#define infected(otri) \
(((size_t) (otri).tri[6] & (size_t) 2l) != 0l)
/* Check or set a triangle's attributes. */
#define elemattribute(otri, attnum) \
((REAL *) (otri).tri)[m->elemattribindex + (attnum)]
#define setelemattribute(otri, attnum, value) \
((REAL *) (otri).tri)[m->elemattribindex + (attnum)] = value
/* Check or set a triangle's maximum area bound. */
#define areabound(otri) ((REAL *) (otri).tri)[m->areaboundindex]
#define setareabound(otri, value) \
((REAL *) (otri).tri)[m->areaboundindex] = value
/* Check or set a triangle's deallocation. Its second pointer is set to */
/* NULL to indicate that it is not allocated. (Its first pointer is used */
/* for the stack of dead items.) Its fourth pointer (its first vertex) */
/* is set to NULL in case a `badtriang' structure points to it. */
#define deadtri(tria) ((tria)[1] == (triangle) NULL)
#define killtri(tria) \
(tria)[1] = (triangle) NULL; \
(tria)[3] = (triangle) NULL
/********* Primitives for subsegments *********/
/* */
/* */
/* sdecode() converts a pointer to an oriented subsegment. The orientation */
/* is extracted from the least significant bit of the pointer. The two */
/* least significant bits (one for orientation, one for viral infection) */
/* are masked out to produce the real pointer. */
#define sdecode(sptr, osub) \
(osub).ssorient = (int) ((size_t) (sptr) & (size_t) 1l); \
(osub).ss = (subseg *) \
((size_t) (sptr) & ~ (size_t) 3l)
/* sencode() compresses an oriented subsegment into a single pointer. It */
/* relies on the assumption that all subsegments are aligned to two-byte */
/* boundaries, so the least significant bit of (osub).ss is zero. */
#define sencode(osub) \
(subseg) ((size_t) (osub).ss | (size_t) (osub).ssorient)
/* ssym() toggles the orientation of a subsegment. */
#define ssym(osub1, osub2) \
(osub2).ss = (osub1).ss; \
(osub2).ssorient = 1 - (osub1).ssorient
#define ssymself(osub) \
(osub).ssorient = 1 - (osub).ssorient
/* spivot() finds the other subsegment (from the same segment) that shares */
/* the same origin. */
#define spivot(osub1, osub2) \
sptr = (osub1).ss[(osub1).ssorient]; \
sdecode(sptr, osub2)
#define spivotself(osub) \
sptr = (osub).ss[(osub).ssorient]; \
sdecode(sptr, osub)
/* snext() finds the next subsegment (from the same segment) in sequence; */
/* one whose origin is the input subsegment's destination. */
#define snext(osub1, osub2) \
sptr = (osub1).ss[1 - (osub1).ssorient]; \
sdecode(sptr, osub2)
#define snextself(osub) \
sptr = (osub).ss[1 - (osub).ssorient]; \
sdecode(sptr, osub)
/* These primitives determine or set the origin or destination of a */
/* subsegment or the segment that includes it. */
#define sorg(osub, vertexptr) \
vertexptr = (vertex) (osub).ss[2 + (osub).ssorient]
#define sdest(osub, vertexptr) \
vertexptr = (vertex) (osub).ss[3 - (osub).ssorient]
#define setsorg(osub, vertexptr) \
(osub).ss[2 + (osub).ssorient] = (subseg) vertexptr
#define setsdest(osub, vertexptr) \
(osub).ss[3 - (osub).ssorient] = (subseg) vertexptr
#define segorg(osub, vertexptr) \
vertexptr = (vertex) (osub).ss[4 + (osub).ssorient]
#define segdest(osub, vertexptr) \
vertexptr = (vertex) (osub).ss[5 - (osub).ssorient]
#define setsegorg(osub, vertexptr) \
(osub).ss[4 + (osub).ssorient] = (subseg) vertexptr
#define setsegdest(osub, vertexptr) \
(osub).ss[5 - (osub).ssorient] = (subseg) vertexptr
/* These primitives read or set a boundary marker. Boundary markers are */
/* used to hold user-defined tags for setting boundary conditions in */
/* finite element solvers. */
#define mark(osub) (* (int *) ((osub).ss + 8))
#define setmark(osub, value) \
* (int *) ((osub).ss + 8) = value
/* Bond two subsegments together. */
#define sbond(osub1, osub2) \
(osub1).ss[(osub1).ssorient] = sencode(osub2); \
(osub2).ss[(osub2).ssorient] = sencode(osub1)
/* Dissolve a subsegment bond (from one side). Note that the other */
/* subsegment will still think it's connected to this subsegment. */
#define sdissolve(osub) \
(osub).ss[(osub).ssorient] = (subseg) m->dummysub
/* Copy a subsegment. */
#define subsegcopy(osub1, osub2) \
(osub2).ss = (osub1).ss; \
(osub2).ssorient = (osub1).ssorient
/* Test for equality of subsegments. */
#define subsegequal(osub1, osub2) \
(((osub1).ss == (osub2).ss) && \
((osub1).ssorient == (osub2).ssorient))
/* Check or set a subsegment's deallocation. Its second pointer is set to */
/* NULL to indicate that it is not allocated. (Its first pointer is used */
/* for the stack of dead items.) Its third pointer (its first vertex) */
/* is set to NULL in case a `badsubseg' structure points to it. */
#define deadsubseg(sub) ((sub)[1] == (subseg) NULL)
#define killsubseg(sub) \
(sub)[1] = (subseg) NULL; \
(sub)[2] = (subseg) NULL
/********* Primitives for interacting triangles and subsegments *********/
/* */
/* */
/* tspivot() finds a subsegment abutting a triangle. */
#define tspivot(otri, osub) \
sptr = (subseg) (otri).tri[6 + (otri).orient]; \
sdecode(sptr, osub)
/* stpivot() finds a triangle abutting a subsegment. It requires that the */
/* variable `ptr' of type `triangle' be defined. */
#define stpivot(osub, otri) \
ptr = (triangle) (osub).ss[6 + (osub).ssorient]; \
decode(ptr, otri)
/* Bond a triangle to a subsegment. */
#define tsbond(otri, osub) \
(otri).tri[6 + (otri).orient] = (triangle) sencode(osub); \
(osub).ss[6 + (osub).ssorient] = (subseg) encode(otri)
/* Dissolve a bond (from the triangle side). */
#define tsdissolve(otri) \
(otri).tri[6 + (otri).orient] = (triangle) m->dummysub
/* Dissolve a bond (from the subsegment side). */
#define stdissolve(osub) \
(osub).ss[6 + (osub).ssorient] = (subseg) m->dummytri
/********* Primitives for vertices *********/
/* */
/* */
#define vertexmark(vx) ((int *) (vx))[m->vertexmarkindex]
#define setvertexmark(vx, value) \
((int *) (vx))[m->vertexmarkindex] = value
#define vertextype(vx) ((int *) (vx))[m->vertexmarkindex + 1]
#define setvertextype(vx, value) \
((int *) (vx))[m->vertexmarkindex + 1] = value
#define vertex2tri(vx) ((triangle *) (vx))[m->vertex2triindex]
#define setvertex2tri(vx, value) \
((triangle *) (vx))[m->vertex2triindex] = value
/** **/
/** **/
/********* Mesh manipulation primitives end here *********/
/********* User-defined triangle evaluation routine begins here *********/
/** **/
/** **/
/*****************************************************************************/
/* */
/* triunsuitable() Determine if a triangle is unsuitable, and thus must */
/* be further refined. */
/* */
/* You may write your own procedure that decides whether or not a selected */
/* triangle is too big (and needs to be refined). There are two ways to do */
/* this. */
/* */
/* (1) Modify the procedure `triunsuitable' below, then recompile */
/* Triangle. */
/* */
/* (2) Define the symbol EXTERNAL_TEST (either by adding the definition */
/* to this file, or by using the appropriate compiler switch). This way, */
/* you can compile triangle.c separately from your test. Write your own */
/* `triunsuitable' procedure in a separate C file (using the same prototype */
/* as below). Compile it and link the object code with triangle.o. */
/* */
/* This procedure returns 1 if the triangle is too large and should be */
/* refined; 0 otherwise. */
/* */
/*****************************************************************************/
#ifdef EXTERNAL_TEST
//int triunsuitable(void);
#else /* not EXTERNAL_TEST */
#ifdef ANSI_DECLARATORS
int triunsuitable(vertex triorg, vertex tridest, vertex triapex, REAL area)
#else /* not ANSI_DECLARATORS */
int triunsuitable(triorg, tridest, triapex, area)
vertex triorg; /* The triangle's origin vertex. */
vertex tridest; /* The triangle's destination vertex. */
vertex triapex; /* The triangle's apex vertex. */
REAL area; /* The area of the triangle. */
#endif /* not ANSI_DECLARATORS */
{
REAL dxoa, dxda, dxod;
REAL dyoa, dyda, dyod;
REAL oalen, dalen, odlen;
REAL maxlen;
dxoa = triorg[0] - triapex[0];
dyoa = triorg[1] - triapex[1];
dxda = tridest[0] - triapex[0];
dyda = tridest[1] - triapex[1];
dxod = triorg[0] - tridest[0];
dyod = triorg[1] - tridest[1];
/* Find the squares of the lengths of the triangle's three edges. */
oalen = dxoa * dxoa + dyoa * dyoa;
dalen = dxda * dxda + dyda * dyda;
odlen = dxod * dxod + dyod * dyod;
/* Find the square of the length of the longest edge. */
maxlen = (dalen > oalen) ? dalen : oalen;
maxlen = (odlen > maxlen) ? odlen : maxlen;
if (maxlen > 0.05 * (triorg[0] * triorg[0] + triorg[1] * triorg[1]) + 0.02) {
return 1;
} else {
return 0;
}
}
#endif /* not EXTERNAL_TEST */
/** **/
/** **/
/********* User-defined triangle evaluation routine ends here *********/
/********* Memory allocation and program exit wrappers begin here *********/
/** **/
/** **/
#ifdef ANSI_DECLARATORS
void triexit(int status)
#else /* not ANSI_DECLARATORS */
void triexit(status)
int status;
#endif /* not ANSI_DECLARATORS */
{
exit(status);
}
#ifdef ANSI_DECLARATORS
VOID *trimalloc(int size)
#else /* not ANSI_DECLARATORS */
VOID *trimalloc(size)
int size;
#endif /* not ANSI_DECLARATORS */
{
VOID *memptr;
memptr = (VOID *) malloc((unsigned int) size);
if (memptr == (VOID *) NULL) {
printf("Error: Out of memory.\n");
triexit(1);
}
return(memptr);
}
#ifdef ANSI_DECLARATORS
void trifree(VOID *memptr)
#else /* not ANSI_DECLARATORS */
void trifree(memptr)
VOID *memptr;
#endif /* not ANSI_DECLARATORS */
{
free(memptr);
}
/** **/
/** **/
/********* Memory allocation and program exit wrappers end here *********/
/********* User interaction routines begin here *********/
/** **/
/** **/
/*****************************************************************************/
/* */
/* syntax() Print list of command line switches. */
/* */
/*****************************************************************************/
#ifndef TRILIBRARY
void syntax()
{
#ifdef CDT_ONLY
#ifdef REDUCED
printf("triangle [-pAcjevngBPNEIOXzo_lQVh] input_file\n");
#else /* not REDUCED */
printf("triangle [-pAcjevngBPNEIOXzo_iFlCQVh] input_file\n");
#endif /* not REDUCED */
#else /* not CDT_ONLY */
#ifdef REDUCED
printf("triangle [-prq__a__uAcDjevngBPNEIOXzo_YS__lQVh] input_file\n");
#else /* not REDUCED */
printf("triangle [-prq__a__uAcDjevngBPNEIOXzo_YS__iFlsCQVh] input_file\n");
#endif /* not REDUCED */
#endif /* not CDT_ONLY */
printf(" -p Triangulates a Planar Straight Line Graph (.poly file).\n");
#ifndef CDT_ONLY
printf(" -r Refines a previously generated mesh.\n");
printf(
" -q Quality mesh generation. A minimum angle may be specified.\n");
printf(" -a Applies a maximum triangle area constraint.\n");
printf(" -u Applies a user-defined triangle constraint.\n");
#endif /* not CDT_ONLY */
printf(
" -A Applies attributes to identify triangles in certain regions.\n");
printf(" -c Encloses the convex hull with segments.\n");
#ifndef CDT_ONLY
printf(" -D Conforming Delaunay: all triangles are truly Delaunay.\n");
#endif /* not CDT_ONLY */
/*
printf(" -w Weighted Delaunay triangulation.\n");
printf(" -W Regular triangulation (lower hull of a height field).\n");
*/
printf(" -j Jettison unused vertices from output .node file.\n");
printf(" -e Generates an edge list.\n");
printf(" -v Generates a Voronoi diagram.\n");
printf(" -n Generates a list of triangle neighbors.\n");
printf(" -g Generates an .off file for Geomview.\n");
printf(" -B Suppresses output of boundary information.\n");
printf(" -P Suppresses output of .poly file.\n");
printf(" -N Suppresses output of .node file.\n");
printf(" -E Suppresses output of .ele file.\n");
printf(" -I Suppresses mesh iteration numbers.\n");
printf(" -O Ignores holes in .poly file.\n");
printf(" -X Suppresses use of exact arithmetic.\n");
printf(" -z Numbers all items starting from zero (rather than one).\n");
printf(" -o2 Generates second-order subparametric elements.\n");
#ifndef CDT_ONLY
printf(" -Y Suppresses boundary segment splitting.\n");
printf(" -S Specifies maximum number of added Steiner points.\n");
#endif /* not CDT_ONLY */
#ifndef REDUCED
printf(" -i Uses incremental method, rather than divide-and-conquer.\n");
printf(" -F Uses Fortune's sweepline algorithm, rather than d-and-c.\n");
#endif /* not REDUCED */
printf(" -l Uses vertical cuts only, rather than alternating cuts.\n");
#ifndef REDUCED
#ifndef CDT_ONLY
printf(
" -s Force segments into mesh by splitting (instead of using CDT).\n");
#endif /* not CDT_ONLY */
printf(" -C Check consistency of final mesh.\n");
#endif /* not REDUCED */
printf(" -Q Quiet: No terminal output except errors.\n");
printf(" -V Verbose: Detailed information on what I'm doing.\n");
printf(" -h Help: Detailed instructions for Triangle.\n");
triexit(0);
}
#endif /* not TRILIBRARY */
/*****************************************************************************/
/* */
/* info() Print out complete instructions. */
/* */
/*****************************************************************************/
#ifndef TRILIBRARY
void info()
{
printf("Triangle\n");
printf(
"A Two-Dimensional Quality Mesh Generator and Delaunay Triangulator.\n");
printf("Version 1.6\n\n");
printf(
"Copyright 1993, 1995, 1997, 1998, 2002, 2005 Jonathan Richard Shewchuk\n");
printf("2360 Woolsey #H / Berkeley, California 94705-1927\n");
printf("Bugs/comments to jrs@cs.berkeley.edu\n");
printf(
"Created as part of the Quake project (tools for earthquake simulation).\n");
printf(
"Supported in part by NSF Grant CMS-9318163 and an NSERC 1967 Scholarship.\n");
printf("There is no warranty whatsoever. Use at your own risk.\n");
#ifdef SINGLE
printf("This executable is compiled for single precision arithmetic.\n\n\n");
#else /* not SINGLE */
printf("This executable is compiled for double precision arithmetic.\n\n\n");
#endif /* not SINGLE */
printf(
"Triangle generates exact Delaunay triangulations, constrained Delaunay\n");
printf(
"triangulations, conforming Delaunay triangulations, Voronoi diagrams, and\n");
printf(
"high-quality triangular meshes. The latter can be generated with no small\n"
);
printf(
"or large angles, and are thus suitable for finite element analysis. If no\n"
);
printf(
"command line switch is specified, your .node input file is read, and the\n");
printf(
"Delaunay triangulation is returned in .node and .ele output files. The\n");
printf("command syntax is:\n\n");
printf("triangle [-prq__a__uAcDjevngBPNEIOXzo_YS__iFlsCQVh] input_file\n\n");
printf(
"Underscores indicate that numbers may optionally follow certain switches.\n");
printf(
"Do not leave any space between a switch and its numeric parameter.\n");
printf(
"input_file must be a file with extension .node, or extension .poly if the\n");
printf(
"-p switch is used. If -r is used, you must supply .node and .ele files,\n");
printf(
"and possibly a .poly file and an .area file as well. The formats of these\n"
);
printf("files are described below.\n\n");
printf("Command Line Switches:\n\n");
printf(
" -p Reads a Planar Straight Line Graph (.poly file), which can specify\n"
);
printf(
" vertices, segments, holes, regional attributes, and regional area\n");
printf(
" constraints. Generates a constrained Delaunay triangulation (CDT)\n"
);
printf(
" fitting the input; or, if -s, -q, -a, or -u is used, a conforming\n");
printf(
" constrained Delaunay triangulation (CCDT). If you want a truly\n");
printf(
" Delaunay (not just constrained Delaunay) triangulation, use -D as\n");
printf(
" well. When -p is not used, Triangle reads a .node file by default.\n"
);
printf(
" -r Refines a previously generated mesh. The mesh is read from a .node\n"
);
printf(
" file and an .ele file. If -p is also used, a .poly file is read\n");
printf(
" and used to constrain segments in the mesh. If -a is also used\n");
printf(
" (with no number following), an .area file is read and used to\n");
printf(
" impose area constraints on the mesh. Further details on refinement\n"
);
printf(" appear below.\n");
printf(
" -q Quality mesh generation by Delaunay refinement (a hybrid of Paul\n");
printf(
" Chew's and Jim Ruppert's algorithms). Adds vertices to the mesh to\n"
);
printf(
" ensure that all angles are between 20 and 140 degrees. An\n");
printf(
" alternative bound on the minimum angle, replacing 20 degrees, may\n");
printf(
" be specified after the `q'. The specified angle may include a\n");
printf(
" decimal point, but not exponential notation. Note that a bound of\n"
);
printf(
" theta degrees on the smallest angle also implies a bound of\n");
printf(
" (180 - 2 theta) on the largest angle. If the minimum angle is 28.6\n"
);
printf(
" degrees or smaller, Triangle is mathematically guaranteed to\n");
printf(
" terminate (assuming infinite precision arithmetic--Triangle may\n");
printf(
" fail to terminate if you run out of precision). In practice,\n");
printf(
" Triangle often succeeds for minimum angles up to 34 degrees. For\n");
printf(
" some meshes, however, you might need to reduce the minimum angle to\n"
);
printf(
" avoid problems associated with insufficient floating-point\n");
printf(" precision.\n");
printf(
" -a Imposes a maximum triangle area. If a number follows the `a', no\n");
printf(
" triangle is generated whose area is larger than that number. If no\n"
);
printf(
" number is specified, an .area file (if -r is used) or .poly file\n");
printf(
" (if -r is not used) specifies a set of maximum area constraints.\n");
printf(
" An .area file contains a separate area constraint for each\n");
printf(
" triangle, and is useful for refining a finite element mesh based on\n"
);
printf(
" a posteriori error estimates. A .poly file can optionally contain\n"
);
printf(
" an area constraint for each segment-bounded region, thereby\n");
printf(
" controlling triangle densities in a first triangulation of a PSLG.\n"
);
printf(
" You can impose both a fixed area constraint and a varying area\n");
printf(
" constraint by invoking the -a switch twice, once with and once\n");
printf(
" without a number following. Each area specified may include a\n");
printf(" decimal point.\n");
printf(
" -u Imposes a user-defined constraint on triangle size. There are two\n"
);
printf(
" ways to use this feature. One is to edit the triunsuitable()\n");
printf(
" procedure in triangle.c to encode any constraint you like, then\n");
printf(
" recompile Triangle. The other is to compile triangle.c with the\n");
printf(
" EXTERNAL_TEST symbol set (compiler switch -DEXTERNAL_TEST), then\n");
printf(
" link Triangle with a separate object file that implements\n");
printf(
" triunsuitable(). In either case, the -u switch causes the user-\n");
printf(" defined test to be applied to every triangle.\n");
printf(
" -A Assigns an additional floating-point attribute to each triangle\n");
printf(
" that identifies what segment-bounded region each triangle belongs\n");
printf(
" to. Attributes are assigned to regions by the .poly file. If a\n");
printf(
" region is not explicitly marked by the .poly file, triangles in\n");
printf(
" that region are assigned an attribute of zero. The -A switch has\n");
printf(
" an effect only when the -p switch is used and the -r switch is not.\n"
);
printf(
" -c Creates segments on the convex hull of the triangulation. If you\n");
printf(
" are triangulating a vertex set, this switch causes a .poly file to\n"
);
printf(
" be written, containing all edges of the convex hull. If you are\n");
printf(
" triangulating a PSLG, this switch specifies that the whole convex\n");
printf(
" hull of the PSLG should be triangulated, regardless of what\n");
printf(
" segments the PSLG has. If you do not use this switch when\n");
printf(
" triangulating a PSLG, Triangle assumes that you have identified the\n"
);
printf(
" region to be triangulated by surrounding it with segments of the\n");
printf(
" input PSLG. Beware: if you are not careful, this switch can cause\n"
);
printf(
" the introduction of an extremely thin angle between a PSLG segment\n"
);
printf(
" and a convex hull segment, which can cause overrefinement (and\n");
printf(
" possibly failure if Triangle runs out of precision). If you are\n");
printf(
" refining a mesh, the -c switch works differently: it causes a\n");
printf(
" .poly file to be written containing the boundary edges of the mesh\n"
);
printf(" (useful if no .poly file was read).\n");
printf(
" -D Conforming Delaunay triangulation: use this switch if you want to\n"
);
printf(
" ensure that all the triangles in the mesh are Delaunay, and not\n");
printf(
" merely constrained Delaunay; or if you want to ensure that all the\n"
);
printf(
" Voronoi vertices lie within the triangulation. (Some finite volume\n"
);
printf(
" methods have this requirement.) This switch invokes Ruppert's\n");
printf(
" original algorithm, which splits every subsegment whose diametral\n");
printf(
" circle is encroached. It usually increases the number of vertices\n"
);
printf(" and triangles.\n");
printf(
" -j Jettisons vertices that are not part of the final triangulation\n");
printf(
" from the output .node file. By default, Triangle copies all\n");
printf(
" vertices in the input .node file to the output .node file, in the\n");
printf(
" same order, so their indices do not change. The -j switch prevents\n"
);
printf(
" duplicated input vertices, or vertices `eaten' by holes, from\n");
printf(
" appearing in the output .node file. Thus, if two input vertices\n");
printf(
" have exactly the same coordinates, only the first appears in the\n");
printf(
" output. If any vertices are jettisoned, the vertex numbering in\n");
printf(
" the output .node file differs from that of the input .node file.\n");
printf(
" -e Outputs (to an .edge file) a list of edges of the triangulation.\n");
printf(
" -v Outputs the Voronoi diagram associated with the triangulation.\n");
printf(
" Does not attempt to detect degeneracies, so some Voronoi vertices\n");
printf(
" may be duplicated. See the discussion of Voronoi diagrams below.\n");
printf(
" -n Outputs (to a .neigh file) a list of triangles neighboring each\n");
printf(" triangle.\n");
printf(
" -g Outputs the mesh to an Object File Format (.off) file, suitable for\n"
);
printf(" viewing with the Geometry Center's Geomview package.\n");
printf(
" -B No boundary markers in the output .node, .poly, and .edge output\n");
printf(
" files. See the detailed discussion of boundary markers below.\n");
printf(
" -P No output .poly file. Saves disk space, but you lose the ability\n");
printf(
" to maintain constraining segments on later refinements of the mesh.\n"
);
printf(" -N No output .node file.\n");
printf(" -E No output .ele file.\n");
printf(
" -I No iteration numbers. Suppresses the output of .node and .poly\n");
printf(
" files, so your input files won't be overwritten. (If your input is\n"
);
printf(
" a .poly file only, a .node file is written.) Cannot be used with\n");
printf(
" the -r switch, because that would overwrite your input .ele file.\n");
printf(
" Shouldn't be used with the -q, -a, -u, or -s switch if you are\n");
printf(
" using a .node file for input, because no .node file is written, so\n"
);
printf(" there is no record of any added Steiner points.\n");
printf(" -O No holes. Ignores the holes in the .poly file.\n");
printf(
" -X No exact arithmetic. Normally, Triangle uses exact floating-point\n"
);
printf(
" arithmetic for certain tests if it thinks the inexact tests are not\n"
);
printf(
" accurate enough. Exact arithmetic ensures the robustness of the\n");
printf(
" triangulation algorithms, despite floating-point roundoff error.\n");
printf(
" Disabling exact arithmetic with the -X switch causes a small\n");
printf(
" improvement in speed and creates the possibility that Triangle will\n"
);
printf(" fail to produce a valid mesh. Not recommended.\n");
printf(
" -z Numbers all items starting from zero (rather than one). Note that\n"
);
printf(
" this switch is normally overridden by the value used to number the\n"
);
printf(
" first vertex of the input .node or .poly file. However, this\n");
printf(
" switch is useful when calling Triangle from another program.\n");
printf(
" -o2 Generates second-order subparametric elements with six nodes each.\n"
);
printf(
" -Y No new vertices on the boundary. This switch is useful when the\n");
printf(
" mesh boundary must be preserved so that it conforms to some\n");
printf(
" adjacent mesh. Be forewarned that you will probably sacrifice much\n"
);
printf(
" of the quality of the mesh; Triangle will try, but the resulting\n");
printf(
" mesh may contain poorly shaped triangles. Works well if all the\n");
printf(
" boundary vertices are closely spaced. Specify this switch twice\n");
printf(
" (`-YY') to prevent all segment splitting, including internal\n");
printf(" boundaries.\n");
printf(
" -S Specifies the maximum number of Steiner points (vertices that are\n");
printf(
" not in the input, but are added to meet the constraints on minimum\n"
);
printf(
" angle and maximum area). The default is to allow an unlimited\n");
printf(
" number. If you specify this switch with no number after it,\n");
printf(
" the limit is set to zero. Triangle always adds vertices at segment\n"
);
printf(
" intersections, even if it needs to use more vertices than the limit\n"
);
printf(
" you set. When Triangle inserts segments by splitting (-s), it\n");
printf(
" always adds enough vertices to ensure that all the segments of the\n"
);
printf(" PLSG are recovered, ignoring the limit if necessary.\n");
printf(
" -i Uses an incremental rather than a divide-and-conquer algorithm to\n");
printf(
" construct a Delaunay triangulation. Try it if the divide-and-\n");
printf(" conquer algorithm fails.\n");
printf(
" -F Uses Steven Fortune's sweepline algorithm to construct a Delaunay\n");
printf(
" triangulation. Warning: does not use exact arithmetic for all\n");
printf(" calculations. An exact result is not guaranteed.\n");
printf(
" -l Uses only vertical cuts in the divide-and-conquer algorithm. By\n");
printf(
" default, Triangle alternates between vertical and horizontal cuts,\n"
);
printf(
" which usually improve the speed except with vertex sets that are\n");
printf(
" small or short and wide. This switch is primarily of theoretical\n");
printf(" interest.\n");
printf(
" -s Specifies that segments should be forced into the triangulation by\n"
);
printf(
" recursively splitting them at their midpoints, rather than by\n");
printf(
" generating a constrained Delaunay triangulation. Segment splitting\n"
);
printf(
" is true to Ruppert's original algorithm, but can create needlessly\n"
);
printf(
" small triangles. This switch is primarily of theoretical interest.\n"
);
printf(
" -C Check the consistency of the final mesh. Uses exact arithmetic for\n"
);
printf(
" checking, even if the -X switch is used. Useful if you suspect\n");
printf(" Triangle is buggy.\n");
printf(
" -Q Quiet: Suppresses all explanation of what Triangle is doing,\n");
printf(" unless an error occurs.\n");
printf(
" -V Verbose: Gives detailed information about what Triangle is doing.\n"
);
printf(
" Add more `V's for increasing amount of detail. `-V' is most\n");
printf(
" useful; itgives information on algorithmic progress and much more\n");
printf(
" detailed statistics. `-VV' gives vertex-by-vertex details, and\n");
printf(
" prints so much that Triangle runs much more slowly. `-VVVV' gives\n"
);
printf(" information only a debugger could love.\n");
printf(" -h Help: Displays these instructions.\n");
printf("\n");
printf("Definitions:\n");
printf("\n");
printf(
" A Delaunay triangulation of a vertex set is a triangulation whose\n");
printf(
" vertices are the vertex set, that covers the convex hull of the vertex\n");
printf(
" set. A Delaunay triangulation has the property that no vertex lies\n");
printf(
" inside the circumscribing circle (circle that passes through all three\n");
printf(" vertices) of any triangle in the triangulation.\n\n");
printf(
" A Voronoi diagram of a vertex set is a subdivision of the plane into\n");
printf(
" polygonal cells (some of which may be unbounded, meaning infinitely\n");
printf(
" large), where each cell is the set of points in the plane that are closer\n"
);
printf(
" to some input vertex than to any other input vertex. The Voronoi diagram\n"
);
printf(" is a geometric dual of the Delaunay triangulation.\n\n");
printf(
" A Planar Straight Line Graph (PSLG) is a set of vertices and segments.\n");
printf(
" Segments are simply edges, whose endpoints are all vertices in the PSLG.\n"
);
printf(
" Segments may intersect each other only at their endpoints. The file\n");
printf(" format for PSLGs (.poly files) is described below.\n\n");
printf(
" A constrained Delaunay triangulation (CDT) of a PSLG is similar to a\n");
printf(
" Delaunay triangulation, but each PSLG segment is present as a single edge\n"
);
printf(
" of the CDT. (A constrained Delaunay triangulation is not truly a\n");
printf(
" Delaunay triangulation, because some of its triangles might not be\n");
printf(
" Delaunay.) By definition, a CDT does not have any vertices other than\n");
printf(
" those specified in the input PSLG. Depending on context, a CDT might\n");
printf(
" cover the convex hull of the PSLG, or it might cover only a segment-\n");
printf(" bounded region (e.g. a polygon).\n\n");
printf(
" A conforming Delaunay triangulation of a PSLG is a triangulation in which\n"
);
printf(
" each triangle is truly Delaunay, and each PSLG segment is represented by\n"
);
printf(
" a linear contiguous sequence of edges of the triangulation. New vertices\n"
);
printf(
" (not part of the PSLG) may appear, and each input segment may have been\n");
printf(
" subdivided into shorter edges (subsegments) by these additional vertices.\n"
);
printf(
" The new vertices are frequently necessary to maintain the Delaunay\n");
printf(" property while ensuring that every segment is represented.\n\n");
printf(
" A conforming constrained Delaunay triangulation (CCDT) of a PSLG is a\n");
printf(
" triangulation of a PSLG whose triangles are constrained Delaunay. New\n");
printf(" vertices may appear, and input segments may be subdivided into\n");
printf(
" subsegments, but not to guarantee that segments are respected; rather, to\n"
);
printf(
" improve the quality of the triangles. The high-quality meshes produced\n");
printf(
" by the -q switch are usually CCDTs, but can be made conforming Delaunay\n");
printf(" with the -D switch.\n\n");
printf("File Formats:\n\n");
printf(
" All files may contain comments prefixed by the character '#'. Vertices,\n"
);
printf(
" triangles, edges, holes, and maximum area constraints must be numbered\n");
printf(
" consecutively, starting from either 1 or 0. Whichever you choose, all\n");
printf(
" input files must be consistent; if the vertices are numbered from 1, so\n");
printf(
" must be all other objects. Triangle automatically detects your choice\n");
printf(
" while reading the .node (or .poly) file. (When calling Triangle from\n");
printf(
" another program, use the -z switch if you wish to number objects from\n");
printf(" zero.) Examples of these file formats are given below.\n\n");
printf(" .node files:\n");
printf(
" First line: <# of vertices> <# of attributes>\n"
);
printf(
" <# of boundary markers (0 or 1)>\n"
);
printf(
" Remaining lines: [attributes] [boundary marker]\n");
printf("\n");
printf(
" The attributes, which are typically floating-point values of physical\n");
printf(
" quantities (such as mass or conductivity) associated with the nodes of\n"
);
printf(
" a finite element mesh, are copied unchanged to the output mesh. If -q,\n"
);
printf(
" -a, -u, -D, or -s is selected, each new Steiner point added to the mesh\n"
);
printf(" has attributes assigned to it by linear interpolation.\n\n");
printf(
" If the fourth entry of the first line is `1', the last column of the\n");
printf(
" remainder of the file is assumed to contain boundary markers. Boundary\n"
);
printf(
" markers are used to identify boundary vertices and vertices resting on\n"
);
printf(
" PSLG segments; a complete description appears in a section below. The\n"
);
printf(
" .node file produced by Triangle contains boundary markers in the last\n");
printf(" column unless they are suppressed by the -B switch.\n\n");
printf(" .ele files:\n");
printf(
" First line: <# of triangles> <# of attributes>\n");
printf(
" Remaining lines: ... [attributes]\n");
printf("\n");
printf(
" Nodes are indices into the corresponding .node file. The first three\n");
printf(
" nodes are the corner vertices, and are listed in counterclockwise order\n"
);
printf(
" around each triangle. (The remaining nodes, if any, depend on the type\n"
);
printf(" of finite element used.)\n\n");
printf(
" The attributes are just like those of .node files. Because there is no\n"
);
printf(
" simple mapping from input to output triangles, Triangle attempts to\n");
printf(
" interpolate attributes, and may cause a lot of diffusion of attributes\n"
);
printf(
" among nearby triangles as the triangulation is refined. Attributes do\n"
);
printf(" not diffuse across segments, so attributes used to identify\n");
printf(" segment-bounded regions remain intact.\n\n");
printf(
" In .ele files produced by Triangle, each triangular element has three\n");
printf(
" nodes (vertices) unless the -o2 switch is used, in which case\n");
printf(
" subparametric quadratic elements with six nodes each are generated.\n");
printf(
" The first three nodes are the corners in counterclockwise order, and\n");
printf(
" the fourth, fifth, and sixth nodes lie on the midpoints of the edges\n");
printf(
" opposite the first, second, and third vertices, respectively.\n");
printf("\n");
printf(" .poly files:\n");
printf(
" First line: <# of vertices> <# of attributes>\n"
);
printf(
" <# of boundary markers (0 or 1)>\n"
);
printf(
" Following lines: [attributes] [boundary marker]\n");
printf(" One line: <# of segments> <# of boundary markers (0 or 1)>\n");
printf(
" Following lines: [boundary marker]\n");
printf(" One line: <# of holes>\n");
printf(" Following lines: \n");
printf(
" Optional line: <# of regional attributes and/or area constraints>\n");
printf(
" Optional following lines: \n");
printf("\n");
printf(
" A .poly file represents a PSLG, as well as some additional information.\n"
);
printf(
" The first section lists all the vertices, and is identical to the\n");
printf(
" format of .node files. <# of vertices> may be set to zero to indicate\n"
);
printf(
" that the vertices are listed in a separate .node file; .poly files\n");
printf(
" produced by Triangle always have this format. A vertex set represented\n"
);
printf(
" this way has the advantage that it may easily be triangulated with or\n");
printf(
" without segments (depending on whether the -p switch is invoked).\n");
printf("\n");
printf(
" The second section lists the segments. Segments are edges whose\n");
printf(
" presence in the triangulation is enforced. (Depending on the choice of\n"
);
printf(
" switches, segment might be subdivided into smaller edges). Each\n");
printf(
" segment is specified by listing the indices of its two endpoints. This\n"
);
printf(
" means that you must include its endpoints in the vertex list. Each\n");
printf(" segment, like each point, may have a boundary marker.\n\n");
printf(
" If -q, -a, -u, and -s are not selected, Triangle produces a constrained\n"
);
printf(
" Delaunay triangulation (CDT), in which each segment appears as a single\n"
);
printf(
" edge in the triangulation. If -q, -a, -u, or -s is selected, Triangle\n"
);
printf(
" produces a conforming constrained Delaunay triangulation (CCDT), in\n");
printf(
" which segments may be subdivided into smaller edges. If -D is\n");
printf(
" selected, Triangle produces a conforming Delaunay triangulation, so\n");
printf(
" that every triangle is Delaunay, and not just constrained Delaunay.\n");
printf("\n");
printf(
" The third section lists holes (and concavities, if -c is selected) in\n");
printf(
" the triangulation. Holes are specified by identifying a point inside\n");
printf(
" each hole. After the triangulation is formed, Triangle creates holes\n");
printf(
" by eating triangles, spreading out from each hole point until its\n");
printf(
" progress is blocked by segments in the PSLG. You must be careful to\n");
printf(
" enclose each hole in segments, or your whole triangulation might be\n");
printf(
" eaten away. If the two triangles abutting a segment are eaten, the\n");
printf(
" segment itself is also eaten. Do not place a hole directly on a\n");
printf(" segment; if you do, Triangle chooses one side of the segment\n");
printf(" arbitrarily.\n\n");
printf(
" The optional fourth section lists regional attributes (to be assigned\n");
printf(
" to all triangles in a region) and regional constraints on the maximum\n");
printf(
" triangle area. Triangle reads this section only if the -A switch is\n");
printf(
" used or the -a switch is used without a number following it, and the -r\n"
);
printf(
" switch is not used. Regional attributes and area constraints are\n");
printf(
" propagated in the same manner as holes: you specify a point for each\n");
printf(
" attribute and/or constraint, and the attribute and/or constraint\n");
printf(
" affects the whole region (bounded by segments) containing the point.\n");
printf(
" If two values are written on a line after the x and y coordinate, the\n");
printf(
" first such value is assumed to be a regional attribute (but is only\n");
printf(
" applied if the -A switch is selected), and the second value is assumed\n"
);
printf(
" to be a regional area constraint (but is only applied if the -a switch\n"
);
printf(
" is selected). You may specify just one value after the coordinates,\n");
printf(
" which can serve as both an attribute and an area constraint, depending\n"
);
printf(
" on the choice of switches. If you are using the -A and -a switches\n");
printf(
" simultaneously and wish to assign an attribute to some region without\n");
printf(" imposing an area constraint, use a negative maximum area.\n\n");
printf(
" When a triangulation is created from a .poly file, you must either\n");
printf(
" enclose the entire region to be triangulated in PSLG segments, or\n");
printf(
" use the -c switch, which automatically creates extra segments that\n");
printf(
" enclose the convex hull of the PSLG. If you do not use the -c switch,\n"
);
printf(
" Triangle eats all triangles that are not enclosed by segments; if you\n");
printf(
" are not careful, your whole triangulation may be eaten away. If you do\n"
);
printf(
" use the -c switch, you can still produce concavities by the appropriate\n"
);
printf(
" placement of holes just inside the boundary of the convex hull.\n");
printf("\n");
printf(
" An ideal PSLG has no intersecting segments, nor any vertices that lie\n");
printf(
" upon segments (except, of course, the endpoints of each segment). You\n"
);
printf(
" aren't required to make your .poly files ideal, but you should be aware\n"
);
printf(
" of what can go wrong. Segment intersections are relatively safe--\n");
printf(
" Triangle calculates the intersection points for you and adds them to\n");
printf(
" the triangulation--as long as your machine's floating-point precision\n");
printf(
" doesn't become a problem. You are tempting the fates if you have three\n"
);
printf(
" segments that cross at the same location, and expect Triangle to figure\n"
);
printf(
" out where the intersection point is. Thanks to floating-point roundoff\n"
);
printf(
" error, Triangle will probably decide that the three segments intersect\n"
);
printf(
" at three different points, and you will find a minuscule triangle in\n");
printf(
" your output--unless Triangle tries to refine the tiny triangle, uses\n");
printf(
" up the last bit of machine precision, and fails to terminate at all.\n");
printf(
" You're better off putting the intersection point in the input files,\n");
printf(
" and manually breaking up each segment into two. Similarly, if you\n");
printf(
" place a vertex at the middle of a segment, and hope that Triangle will\n"
);
printf(
" break up the segment at that vertex, you might get lucky. On the other\n"
);
printf(
" hand, Triangle might decide that the vertex doesn't lie precisely on\n");
printf(
" the segment, and you'll have a needle-sharp triangle in your output--or\n"
);
printf(" a lot of tiny triangles if you're generating a quality mesh.\n");
printf("\n");
printf(
" When Triangle reads a .poly file, it also writes a .poly file, which\n");
printf(
" includes all the subsegments--the edges that are parts of input\n");
printf(
" segments. If the -c switch is used, the output .poly file also\n");
printf(
" includes all of the edges on the convex hull. Hence, the output .poly\n"
);
printf(
" file is useful for finding edges associated with input segments and for\n"
);
printf(
" setting boundary conditions in finite element simulations. Moreover,\n");
printf(
" you will need the output .poly file if you plan to refine the output\n");
printf(
" mesh, and don't want segments to be missing in later triangulations.\n");
printf("\n");
printf(" .area files:\n");
printf(" First line: <# of triangles>\n");
printf(" Following lines: \n");
printf("\n");
printf(
" An .area file associates with each triangle a maximum area that is used\n"
);
printf(
" for mesh refinement. As with other file formats, every triangle must\n");
printf(
" be represented, and the triangles must be numbered consecutively. A\n");
printf(
" triangle may be left unconstrained by assigning it a negative maximum\n");
printf(" area.\n\n");
printf(" .edge files:\n");
printf(" First line: <# of edges> <# of boundary markers (0 or 1)>\n");
printf(
" Following lines: [boundary marker]\n");
printf("\n");
printf(
" Endpoints are indices into the corresponding .node file. Triangle can\n"
);
printf(
" produce .edge files (use the -e switch), but cannot read them. The\n");
printf(
" optional column of boundary markers is suppressed by the -B switch.\n");
printf("\n");
printf(
" In Voronoi diagrams, one also finds a special kind of edge that is an\n");
printf(
" infinite ray with only one endpoint. For these edges, a different\n");
printf(" format is used:\n\n");
printf(" -1 \n\n");
printf(
" The `direction' is a floating-point vector that indicates the direction\n"
);
printf(" of the infinite ray.\n\n");
printf(" .neigh files:\n");
printf(
" First line: <# of triangles> <# of neighbors per triangle (always 3)>\n"
);
printf(
" Following lines: \n");
printf("\n");
printf(
" Neighbors are indices into the corresponding .ele file. An index of -1\n"
);
printf(
" indicates no neighbor (because the triangle is on an exterior\n");
printf(
" boundary). The first neighbor of triangle i is opposite the first\n");
printf(" corner of triangle i, and so on.\n\n");
printf(
" Triangle can produce .neigh files (use the -n switch), but cannot read\n"
);
printf(" them.\n\n");
printf("Boundary Markers:\n\n");
printf(
" Boundary markers are tags used mainly to identify which output vertices\n");
printf(
" and edges are associated with which PSLG segment, and to identify which\n");
printf(
" vertices and edges occur on a boundary of the triangulation. A common\n");
printf(
" use is to determine where boundary conditions should be applied to a\n");
printf(
" finite element mesh. You can prevent boundary markers from being written\n"
);
printf(" into files produced by Triangle by using the -B switch.\n\n");
printf(
" The boundary marker associated with each segment in an output .poly file\n"
);
printf(" and each edge in an output .edge file is chosen as follows:\n");
printf(
" - If an output edge is part or all of a PSLG segment with a nonzero\n");
printf(
" boundary marker, then the edge is assigned the same marker.\n");
printf(
" - Otherwise, if the edge lies on a boundary of the triangulation\n");
printf(
" (even the boundary of a hole), then the edge is assigned the marker\n");
printf(" one (1).\n");
printf(" - Otherwise, the edge is assigned the marker zero (0).\n");
printf(
" The boundary marker associated with each vertex in an output .node file\n");
printf(" is chosen as follows:\n");
printf(
" - If a vertex is assigned a nonzero boundary marker in the input file,\n"
);
printf(
" then it is assigned the same marker in the output .node file.\n");
printf(
" - Otherwise, if the vertex lies on a PSLG segment (even if it is an\n");
printf(
" endpoint of the segment) with a nonzero boundary marker, then the\n");
printf(
" vertex is assigned the same marker. If the vertex lies on several\n");
printf(" such segments, one of the markers is chosen arbitrarily.\n");
printf(
" - Otherwise, if the vertex occurs on a boundary of the triangulation,\n");
printf(" then the vertex is assigned the marker one (1).\n");
printf(" - Otherwise, the vertex is assigned the marker zero (0).\n");
printf("\n");
printf(
" If you want Triangle to determine for you which vertices and edges are on\n"
);
printf(
" the boundary, assign them the boundary marker zero (or use no markers at\n"
);
printf(
" all) in your input files. In the output files, all boundary vertices,\n");
printf(" edges, and segments will be assigned the value one.\n\n");
printf("Triangulation Iteration Numbers:\n\n");
printf(
" Because Triangle can read and refine its own triangulations, input\n");
printf(
" and output files have iteration numbers. For instance, Triangle might\n");
printf(
" read the files mesh.3.node, mesh.3.ele, and mesh.3.poly, refine the\n");
printf(
" triangulation, and output the files mesh.4.node, mesh.4.ele, and\n");
printf(" mesh.4.poly. Files with no iteration number are treated as if\n");
printf(
" their iteration number is zero; hence, Triangle might read the file\n");
printf(
" points.node, triangulate it, and produce the files points.1.node and\n");
printf(" points.1.ele.\n\n");
printf(
" Iteration numbers allow you to create a sequence of successively finer\n");
printf(
" meshes suitable for multigrid methods. They also allow you to produce a\n"
);
printf(
" sequence of meshes using error estimate-driven mesh refinement.\n");
printf("\n");
printf(
" If you're not using refinement or quality meshing, and you don't like\n");
printf(
" iteration numbers, use the -I switch to disable them. This switch also\n");
printf(
" disables output of .node and .poly files to prevent your input files from\n"
);
printf(
" being overwritten. (If the input is a .poly file that contains its own\n");
printf(
" points, a .node file is written. This can be quite convenient for\n");
printf(" computing CDTs or quality meshes.)\n\n");
printf("Examples of How to Use Triangle:\n\n");
printf(
" `triangle dots' reads vertices from dots.node, and writes their Delaunay\n"
);
printf(
" triangulation to dots.1.node and dots.1.ele. (dots.1.node is identical\n");
printf(
" to dots.node.) `triangle -I dots' writes the triangulation to dots.ele\n");
printf(
" instead. (No additional .node file is needed, so none is written.)\n");
printf("\n");
printf(
" `triangle -pe object.1' reads a PSLG from object.1.poly (and possibly\n");
printf(
" object.1.node, if the vertices are omitted from object.1.poly) and writes\n"
);
printf(
" its constrained Delaunay triangulation to object.2.node and object.2.ele.\n"
);
printf(
" The segments are copied to object.2.poly, and all edges are written to\n");
printf(" object.2.edge.\n\n");
printf(
" `triangle -pq31.5a.1 object' reads a PSLG from object.poly (and possibly\n"
);
printf(
" object.node), generates a mesh whose angles are all between 31.5 and 117\n"
);
printf(
" degrees and whose triangles all have areas of 0.1 or less, and writes the\n"
);
printf(
" mesh to object.1.node and object.1.ele. Each segment may be broken up\n");
printf(" into multiple subsegments; these are written to object.1.poly.\n");
printf("\n");
printf(
" Here is a sample file `box.poly' describing a square with a square hole:\n"
);
printf("\n");
printf(
" # A box with eight vertices in 2D, no attributes, one boundary marker.\n"
);
printf(" 8 2 0 1\n");
printf(" # Outer box has these vertices:\n");
printf(" 1 0 0 0\n");
printf(" 2 0 3 0\n");
printf(" 3 3 0 0\n");
printf(" 4 3 3 33 # A special marker for this vertex.\n");
printf(" # Inner square has these vertices:\n");
printf(" 5 1 1 0\n");
printf(" 6 1 2 0\n");
printf(" 7 2 1 0\n");
printf(" 8 2 2 0\n");
printf(" # Five segments with boundary markers.\n");
printf(" 5 1\n");
printf(" 1 1 2 5 # Left side of outer box.\n");
printf(" # Square hole has these segments:\n");
printf(" 2 5 7 0\n");
printf(" 3 7 8 0\n");
printf(" 4 8 6 10\n");
printf(" 5 6 5 0\n");
printf(" # One hole in the middle of the inner square.\n");
printf(" 1\n");
printf(" 1 1.5 1.5\n");
printf("\n");
printf(
" Note that some segments are missing from the outer square, so you must\n");
printf(
" use the `-c' switch. After `triangle -pqc box.poly', here is the output\n"
);
printf(
" file `box.1.node', with twelve vertices. The last four vertices were\n");
printf(
" added to meet the angle constraint. Vertices 1, 2, and 9 have markers\n");
printf(
" from segment 1. Vertices 6 and 8 have markers from segment 4. All the\n");
printf(
" other vertices but 4 have been marked to indicate that they lie on a\n");
printf(" boundary.\n\n");
printf(" 12 2 0 1\n");
printf(" 1 0 0 5\n");
printf(" 2 0 3 5\n");
printf(" 3 3 0 1\n");
printf(" 4 3 3 33\n");
printf(" 5 1 1 1\n");
printf(" 6 1 2 10\n");
printf(" 7 2 1 1\n");
printf(" 8 2 2 10\n");
printf(" 9 0 1.5 5\n");
printf(" 10 1.5 0 1\n");
printf(" 11 3 1.5 1\n");
printf(" 12 1.5 3 1\n");
printf(" # Generated by triangle -pqc box.poly\n");
printf("\n");
printf(" Here is the output file `box.1.ele', with twelve triangles.\n");
printf("\n");
printf(" 12 3 0\n");
printf(" 1 5 6 9\n");
printf(" 2 10 3 7\n");
printf(" 3 6 8 12\n");
printf(" 4 9 1 5\n");
printf(" 5 6 2 9\n");
printf(" 6 7 3 11\n");
printf(" 7 11 4 8\n");
printf(" 8 7 5 10\n");
printf(" 9 12 2 6\n");
printf(" 10 8 7 11\n");
printf(" 11 5 1 10\n");
printf(" 12 8 4 12\n");
printf(" # Generated by triangle -pqc box.poly\n\n");
printf(
" Here is the output file `box.1.poly'. Note that segments have been added\n"
);
printf(
" to represent the convex hull, and some segments have been subdivided by\n");
printf(
" newly added vertices. Note also that <# of vertices> is set to zero to\n");
printf(" indicate that the vertices should be read from the .node file.\n");
printf("\n");
printf(" 0 2 0 1\n");
printf(" 12 1\n");
printf(" 1 1 9 5\n");
printf(" 2 5 7 1\n");
printf(" 3 8 7 1\n");
printf(" 4 6 8 10\n");
printf(" 5 5 6 1\n");
printf(" 6 3 10 1\n");
printf(" 7 4 11 1\n");
printf(" 8 2 12 1\n");
printf(" 9 9 2 5\n");
printf(" 10 10 1 1\n");
printf(" 11 11 3 1\n");
printf(" 12 12 4 1\n");
printf(" 1\n");
printf(" 1 1.5 1.5\n");
printf(" # Generated by triangle -pqc box.poly\n");
printf("\n");
printf("Refinement and Area Constraints:\n");
printf("\n");
printf(
" The -r switch causes a mesh (.node and .ele files) to be read and\n");
printf(
" refined. If the -p switch is also used, a .poly file is read and used to\n"
);
printf(
" specify edges that are constrained and cannot be eliminated (although\n");
printf(
" they can be subdivided into smaller edges) by the refinement process.\n");
printf("\n");
printf(
" When you refine a mesh, you generally want to impose tighter constraints.\n"
);
printf(
" One way to accomplish this is to use -q with a larger angle, or -a\n");
printf(
" followed by a smaller area than you used to generate the mesh you are\n");
printf(
" refining. Another way to do this is to create an .area file, which\n");
printf(
" specifies a maximum area for each triangle, and use the -a switch\n");
printf(
" (without a number following). Each triangle's area constraint is applied\n"
);
printf(
" to that triangle. Area constraints tend to diffuse as the mesh is\n");
printf(
" refined, so if there are large variations in area constraint between\n");
printf(
" adjacent triangles, you may not get the results you want. In that case,\n"
);
printf(
" consider instead using the -u switch and writing a C procedure that\n");
printf(" determines which triangles are too large.\n\n");
printf(
" If you are refining a mesh composed of linear (three-node) elements, the\n"
);
printf(
" output mesh contains all the nodes present in the input mesh, in the same\n"
);
printf(
" order, with new nodes added at the end of the .node file. However, the\n");
printf(
" refinement is not hierarchical: there is no guarantee that each output\n");
printf(
" element is contained in a single input element. Often, an output element\n"
);
printf(
" can overlap two or three input elements, and some input edges are not\n");
printf(
" present in the output mesh. Hence, a sequence of refined meshes forms a\n"
);
printf(
" hierarchy of nodes, but not a hierarchy of elements. If you refine a\n");
printf(
" mesh of higher-order elements, the hierarchical property applies only to\n"
);
printf(
" the nodes at the corners of an element; the midpoint nodes on each edge\n");
printf(" are discarded before the mesh is refined.\n\n");
printf(
" Maximum area constraints in .poly files operate differently from those in\n"
);
printf(
" .area files. A maximum area in a .poly file applies to the whole\n");
printf(
" (segment-bounded) region in which a point falls, whereas a maximum area\n");
printf(
" in an .area file applies to only one triangle. Area constraints in .poly\n"
);
printf(
" files are used only when a mesh is first generated, whereas area\n");
printf(
" constraints in .area files are used only to refine an existing mesh, and\n"
);
printf(
" are typically based on a posteriori error estimates resulting from a\n");
printf(" finite element simulation on that mesh.\n\n");
printf(
" `triangle -rq25 object.1' reads object.1.node and object.1.ele, then\n");
printf(
" refines the triangulation to enforce a 25 degree minimum angle, and then\n"
);
printf(
" writes the refined triangulation to object.2.node and object.2.ele.\n");
printf("\n");
printf(
" `triangle -rpaa6.2 z.3' reads z.3.node, z.3.ele, z.3.poly, and z.3.area.\n"
);
printf(
" After reconstructing the mesh and its subsegments, Triangle refines the\n");
printf(
" mesh so that no triangle has area greater than 6.2, and furthermore the\n");
printf(
" triangles satisfy the maximum area constraints in z.3.area. No angle\n");
printf(
" bound is imposed at all. The output is written to z.4.node, z.4.ele, and\n"
);
printf(" z.4.poly.\n\n");
printf(
" The sequence `triangle -qa1 x', `triangle -rqa.3 x.1', `triangle -rqa.1\n");
printf(
" x.2' creates a sequence of successively finer meshes x.1, x.2, and x.3,\n");
printf(" suitable for multigrid.\n\n");
printf("Convex Hulls and Mesh Boundaries:\n\n");
printf(
" If the input is a vertex set (not a PSLG), Triangle produces its convex\n");
printf(
" hull as a by-product in the output .poly file if you use the -c switch.\n");
printf(
" There are faster algorithms for finding a two-dimensional convex hull\n");
printf(" than triangulation, of course, but this one comes for free.\n\n");
printf(
" If the input is an unconstrained mesh (you are using the -r switch but\n");
printf(
" not the -p switch), Triangle produces a list of its boundary edges\n");
printf(
" (including hole boundaries) as a by-product when you use the -c switch.\n");
printf(
" If you also use the -p switch, the output .poly file contains all the\n");
printf(" segments from the input .poly file as well.\n\n");
printf("Voronoi Diagrams:\n\n");
printf(
" The -v switch produces a Voronoi diagram, in files suffixed .v.node and\n");
printf(
" .v.edge. For example, `triangle -v points' reads points.node, produces\n");
printf(
" its Delaunay triangulation in points.1.node and points.1.ele, and\n");
printf(
" produces its Voronoi diagram in points.1.v.node and points.1.v.edge. The\n"
);
printf(
" .v.node file contains a list of all Voronoi vertices, and the .v.edge\n");
printf(
" file contains a list of all Voronoi edges, some of which may be infinite\n"
);
printf(
" rays. (The choice of filenames makes it easy to run the set of Voronoi\n");
printf(" vertices through Triangle, if so desired.)\n\n");
printf(
" This implementation does not use exact arithmetic to compute the Voronoi\n"
);
printf(
" vertices, and does not check whether neighboring vertices are identical.\n"
);
printf(
" Be forewarned that if the Delaunay triangulation is degenerate or\n");
printf(
" near-degenerate, the Voronoi diagram may have duplicate vertices or\n");
printf(" crossing edges.\n\n");
printf(
" The result is a valid Voronoi diagram only if Triangle's output is a true\n"
);
printf(
" Delaunay triangulation. The Voronoi output is usually meaningless (and\n");
printf(
" may contain crossing edges and other pathology) if the output is a CDT or\n"
);
printf(
" CCDT, or if it has holes or concavities. If the triangulated domain is\n");
printf(
" convex and has no holes, you can use -D switch to force Triangle to\n");
printf(
" construct a conforming Delaunay triangulation instead of a CCDT, so the\n");
printf(" Voronoi diagram will be valid.\n\n");
printf("Mesh Topology:\n\n");
printf(
" You may wish to know which triangles are adjacent to a certain Delaunay\n");
printf(
" edge in an .edge file, which Voronoi cells are adjacent to a certain\n");
printf(
" Voronoi edge in a .v.edge file, or which Voronoi cells are adjacent to\n");
printf(
" each other. All of this information can be found by cross-referencing\n");
printf(
" output files with the recollection that the Delaunay triangulation and\n");
printf(" the Voronoi diagram are planar duals.\n\n");
printf(
" Specifically, edge i of an .edge file is the dual of Voronoi edge i of\n");
printf(
" the corresponding .v.edge file, and is rotated 90 degrees counterclock-\n");
printf(
" wise from the Voronoi edge. Triangle j of an .ele file is the dual of\n");
printf(
" vertex j of the corresponding .v.node file. Voronoi cell k is the dual\n");
printf(" of vertex k of the corresponding .node file.\n\n");
printf(
" Hence, to find the triangles adjacent to a Delaunay edge, look at the\n");
printf(
" vertices of the corresponding Voronoi edge. If the endpoints of a\n");
printf(
" Voronoi edge are Voronoi vertices 2 and 6 respectively, then triangles 2\n"
);
printf(
" and 6 adjoin the left and right sides of the corresponding Delaunay edge,\n"
);
printf(
" respectively. To find the Voronoi cells adjacent to a Voronoi edge, look\n"
);
printf(
" at the endpoints of the corresponding Delaunay edge. If the endpoints of\n"
);
printf(
" a Delaunay edge are input vertices 7 and 12, then Voronoi cells 7 and 12\n"
);
printf(
" adjoin the right and left sides of the corresponding Voronoi edge,\n");
printf(
" respectively. To find which Voronoi cells are adjacent to each other,\n");
printf(" just read the list of Delaunay edges.\n\n");
printf(
" Triangle does not write a list of the edges adjoining each Voronoi cell,\n"
);
printf(
" but you can reconstructed it straightforwardly. For instance, to find\n");
printf(
" all the edges of Voronoi cell 1, search the output .edge file for every\n");
printf(
" edge that has input vertex 1 as an endpoint. The corresponding dual\n");
printf(
" edges in the output .v.edge file form the boundary of Voronoi cell 1.\n");
printf("\n");
printf(
" For each Voronoi vertex, the .neigh file gives a list of the three\n");
printf(
" Voronoi vertices attached to it. You might find this more convenient\n");
printf(" than the .v.edge file.\n\n");
printf("Quadratic Elements:\n\n");
printf(
" Triangle generates meshes with subparametric quadratic elements if the\n");
printf(
" -o2 switch is specified. Quadratic elements have six nodes per element,\n"
);
printf(
" rather than three. `Subparametric' means that the edges of the triangles\n"
);
printf(
" are always straight, so that subparametric quadratic elements are\n");
printf(
" geometrically identical to linear elements, even though they can be used\n"
);
printf(
" with quadratic interpolating functions. The three extra nodes of an\n");
printf(
" element fall at the midpoints of the three edges, with the fourth, fifth,\n"
);
printf(
" and sixth nodes appearing opposite the first, second, and third corners\n");
printf(" respectively.\n\n");
printf("Domains with Small Angles:\n\n");
printf(
" If two input segments adjoin each other at a small angle, clearly the -q\n"
);
printf(
" switch cannot remove the small angle. Moreover, Triangle may have no\n");
printf(
" choice but to generate additional triangles whose smallest angles are\n");
printf(
" smaller than the specified bound. However, these triangles only appear\n");
printf(
" between input segments separated by small angles. Moreover, if you\n");
printf(
" request a minimum angle of theta degrees, Triangle will generally produce\n"
);
printf(
" no angle larger than 180 - 2 theta, even if it is forced to compromise on\n"
);
printf(" the minimum angle.\n\n");
printf("Statistics:\n\n");
printf(
" After generating a mesh, Triangle prints a count of entities in the\n");
printf(
" output mesh, including the number of vertices, triangles, edges, exterior\n"
);
printf(
" boundary edges (i.e. subsegments on the boundary of the triangulation,\n");
printf(
" including hole boundaries), interior boundary edges (i.e. subsegments of\n"
);
printf(
" input segments not on the boundary), and total subsegments. If you've\n");
printf(
" forgotten the statistics for an existing mesh, run Triangle on that mesh\n"
);
printf(
" with the -rNEP switches to read the mesh and print the statistics without\n"
);
printf(
" writing any files. Use -rpNEP if you've got a .poly file for the mesh.\n");
printf("\n");
printf(
" The -V switch produces extended statistics, including a rough estimate\n");
printf(
" of memory use, the number of calls to geometric predicates, and\n");
printf(
" histograms of the angles and the aspect ratios of the triangles in the\n");
printf(" mesh.\n\n");
printf("Exact Arithmetic:\n\n");
printf(
" Triangle uses adaptive exact arithmetic to perform what computational\n");
printf(
" geometers call the `orientation' and `incircle' tests. If the floating-\n"
);
printf(
" point arithmetic of your machine conforms to the IEEE 754 standard (as\n");
printf(
" most workstations do), and does not use extended precision internal\n");
printf(
" floating-point registers, then your output is guaranteed to be an\n");
printf(
" absolutely true Delaunay or constrained Delaunay triangulation, roundoff\n"
);
printf(
" error notwithstanding. The word `adaptive' implies that these arithmetic\n"
);
printf(
" routines compute the result only to the precision necessary to guarantee\n"
);
printf(
" correctness, so they are usually nearly as fast as their approximate\n");
printf(" counterparts.\n\n");
printf(
" May CPUs, including Intel x86 processors, have extended precision\n");
printf(
" floating-point registers. These must be reconfigured so their precision\n"
);
printf(
" is reduced to memory precision. Triangle does this if it is compiled\n");
printf(" correctly. See the makefile for details.\n\n");
printf(
" The exact tests can be disabled with the -X switch. On most inputs, this\n"
);
printf(
" switch reduces the computation time by about eight percent--it's not\n");
printf(
" worth the risk. There are rare difficult inputs (having many collinear\n");
printf(
" and cocircular vertices), however, for which the difference in speed\n");
printf(
" could be a factor of two. Be forewarned that these are precisely the\n");
printf(
" inputs most likely to cause errors if you use the -X switch. Hence, the\n"
);
printf(" -X switch is not recommended.\n\n");
printf(
" Unfortunately, the exact tests don't solve every numerical problem.\n");
printf(
" Exact arithmetic is not used to compute the positions of new vertices,\n");
printf(
" because the bit complexity of vertex coordinates would grow without\n");
printf(
" bound. Hence, segment intersections aren't computed exactly; in very\n");
printf(
" unusual cases, roundoff error in computing an intersection point might\n");
printf(
" actually lead to an inverted triangle and an invalid triangulation.\n");
printf(
" (This is one reason to specify your own intersection points in your .poly\n"
);
printf(
" files.) Similarly, exact arithmetic is not used to compute the vertices\n"
);
printf(" of the Voronoi diagram.\n\n");
printf(
" Another pair of problems not solved by the exact arithmetic routines is\n");
printf(
" underflow and overflow. If Triangle is compiled for double precision\n");
printf(
" arithmetic, I believe that Triangle's geometric predicates work correctly\n"
);
printf(
" if the exponent of every input coordinate falls in the range [-148, 201].\n"
);
printf(
" Underflow can silently prevent the orientation and incircle tests from\n");
printf(
" being performed exactly, while overflow typically causes a floating\n");
printf(" exception.\n\n");
printf("Calling Triangle from Another Program:\n\n");
printf(" Read the file triangle.h for details.\n\n");
printf("Troubleshooting:\n\n");
printf(" Please read this section before mailing me bugs.\n\n");
printf(" `My output mesh has no triangles!'\n\n");
printf(
" If you're using a PSLG, you've probably failed to specify a proper set\n"
);
printf(
" of bounding segments, or forgotten to use the -c switch. Or you may\n");
printf(
" have placed a hole badly, thereby eating all your triangles. To test\n");
printf(" these possibilities, try again with the -c and -O switches.\n");
printf(
" Alternatively, all your input vertices may be collinear, in which case\n"
);
printf(" you can hardly expect to triangulate them.\n\n");
printf(" `Triangle doesn't terminate, or just crashes.'\n\n");
printf(
" Bad things can happen when triangles get so small that the distance\n");
printf(
" between their vertices isn't much larger than the precision of your\n");
printf(
" machine's arithmetic. If you've compiled Triangle for single-precision\n"
);
printf(
" arithmetic, you might do better by recompiling it for double-precision.\n"
);
printf(
" Then again, you might just have to settle for more lenient constraints\n"
);
printf(
" on the minimum angle and the maximum area than you had planned.\n");
printf("\n");
printf(
" You can minimize precision problems by ensuring that the origin lies\n");
printf(
" inside your vertex set, or even inside the densest part of your\n");
printf(
" mesh. If you're triangulating an object whose x-coordinates all fall\n");
printf(
" between 6247133 and 6247134, you're not leaving much floating-point\n");
printf(" precision for Triangle to work with.\n\n");
printf(
" Precision problems can occur covertly if the input PSLG contains two\n");
printf(
" segments that meet (or intersect) at an extremely small angle, or if\n");
printf(
" such an angle is introduced by the -c switch. If you don't realize\n");
printf(
" that a tiny angle is being formed, you might never discover why\n");
printf(
" Triangle is crashing. To check for this possibility, use the -S switch\n"
);
printf(
" (with an appropriate limit on the number of Steiner points, found by\n");
printf(
" trial-and-error) to stop Triangle early, and view the output .poly file\n"
);
printf(
" with Show Me (described below). Look carefully for regions where dense\n"
);
printf(
" clusters of vertices are forming and for small angles between segments.\n"
);
printf(
" Zoom in closely, as such segments might look like a single segment from\n"
);
printf(" a distance.\n\n");
printf(
" If some of the input values are too large, Triangle may suffer a\n");
printf(
" floating exception due to overflow when attempting to perform an\n");
printf(
" orientation or incircle test. (Read the section on exact arithmetic\n");
printf(
" above.) Again, I recommend compiling Triangle for double (rather\n");
printf(" than single) precision arithmetic.\n\n");
printf(
" Unexpected problems can arise if you use quality meshing (-q, -a, or\n");
printf(
" -u) with an input that is not segment-bounded--that is, if your input\n");
printf(
" is a vertex set, or you're using the -c switch. If the convex hull of\n"
);
printf(
" your input vertices has collinear vertices on its boundary, an input\n");
printf(
" vertex that you think lies on the convex hull might actually lie just\n");
printf(
" inside the convex hull. If so, the vertex and the nearby convex hull\n");
printf(
" edge form an extremely thin triangle. When Triangle tries to refine\n");
printf(
" the mesh to enforce angle and area constraints, Triangle might generate\n"
);
printf(
" extremely tiny triangles, or it might fail because of insufficient\n");
printf(" floating-point precision.\n\n");
printf(
" `The numbering of the output vertices doesn't match the input vertices.'\n"
);
printf("\n");
printf(
" You may have had duplicate input vertices, or you may have eaten some\n");
printf(
" of your input vertices with a hole, or by placing them outside the area\n"
);
printf(
" enclosed by segments. In any case, you can solve the problem by not\n");
printf(" using the -j switch.\n\n");
printf(
" `Triangle executes without incident, but when I look at the resulting\n");
printf(
" mesh, it has overlapping triangles or other geometric inconsistencies.'\n");
printf("\n");
printf(
" If you select the -X switch, Triangle occasionally makes mistakes due\n");
printf(
" to floating-point roundoff error. Although these errors are rare,\n");
printf(
" don't use the -X switch. If you still have problems, please report the\n"
);
printf(" bug.\n\n");
printf(
" `Triangle executes without incident, but when I look at the resulting\n");
printf(" Voronoi diagram, it has overlapping edges or other geometric\n");
printf(" inconsistencies.'\n");
printf("\n");
printf(
" If your input is a PSLG (-p), you can only expect a meaningful Voronoi\n"
);
printf(
" diagram if the domain you are triangulating is convex and free of\n");
printf(
" holes, and you use the -D switch to construct a conforming Delaunay\n");
printf(" triangulation (instead of a CDT or CCDT).\n\n");
printf(
" Strange things can happen if you've taken liberties with your PSLG. Do\n");
printf(
" you have a vertex lying in the middle of a segment? Triangle sometimes\n");
printf(
" copes poorly with that sort of thing. Do you want to lay out a collinear\n"
);
printf(
" row of evenly spaced, segment-connected vertices? Have you simply\n");
printf(
" defined one long segment connecting the leftmost vertex to the rightmost\n"
);
printf(
" vertex, and a bunch of vertices lying along it? This method occasionally\n"
);
printf(
" works, especially with horizontal and vertical lines, but often it\n");
printf(
" doesn't, and you'll have to connect each adjacent pair of vertices with a\n"
);
printf(" separate segment. If you don't like it, tough.\n\n");
printf(
" Furthermore, if you have segments that intersect other than at their\n");
printf(
" endpoints, try not to let the intersections fall extremely close to PSLG\n"
);
printf(" vertices or each other.\n\n");
printf(
" If you have problems refining a triangulation not produced by Triangle:\n");
printf(
" Are you sure the triangulation is geometrically valid? Is it formatted\n");
printf(
" correctly for Triangle? Are the triangles all listed so the first three\n"
);
printf(
" vertices are their corners in counterclockwise order? Are all of the\n");
printf(
" triangles constrained Delaunay? Triangle's Delaunay refinement algorithm\n"
);
printf(" assumes that it starts with a CDT.\n\n");
printf("Show Me:\n\n");
printf(
" Triangle comes with a separate program named `Show Me', whose primary\n");
printf(
" purpose is to draw meshes on your screen or in PostScript. Its secondary\n"
);
printf(
" purpose is to check the validity of your input files, and do so more\n");
printf(
" thoroughly than Triangle does. Unlike Triangle, Show Me requires that\n");
printf(
" you have the X Windows system. Sorry, Microsoft Windows users.\n");
printf("\n");
printf("Triangle on the Web:\n");
printf("\n");
printf(" To see an illustrated version of these instructions, check out\n");
printf("\n");
printf(" http://www.cs.cmu.edu/~quake/triangle.html\n");
printf("\n");
printf("A Brief Plea:\n");
printf("\n");
printf(
" If you use Triangle, and especially if you use it to accomplish real\n");
printf(
" work, I would like very much to hear from you. A short letter or email\n");
printf(
" (to jrs@cs.berkeley.edu) describing how you use Triangle will mean a lot\n"
);
printf(
" to me. The more people I know are using this program, the more easily I\n"
);
printf(
" can justify spending time on improvements, which in turn will benefit\n");
printf(
" you. Also, I can put you on a list to receive email whenever a new\n");
printf(" version of Triangle is available.\n\n");
printf(
" If you use a mesh generated by Triangle in a publication, please include\n"
);
printf(
" an acknowledgment as well. And please spell Triangle with a capital `T'!\n"
);
printf(
" If you want to include a citation, use `Jonathan Richard Shewchuk,\n");
printf(
" ``Triangle: Engineering a 2D Quality Mesh Generator and Delaunay\n");
printf(
" Triangulator,'' in Applied Computational Geometry: Towards Geometric\n");
printf(
" Engineering (Ming C. Lin and Dinesh Manocha, editors), volume 1148 of\n");
printf(
" Lecture Notes in Computer Science, pages 203-222, Springer-Verlag,\n");
printf(
" Berlin, May 1996. (From the First ACM Workshop on Applied Computational\n"
);
printf(" Geometry.)'\n\n");
printf("Research credit:\n\n");
printf(
" Of course, I can take credit for only a fraction of the ideas that made\n");
printf(
" this mesh generator possible. Triangle owes its existence to the efforts\n"
);
printf(
" of many fine computational geometers and other researchers, including\n");
printf(
" Marshall Bern, L. Paul Chew, Kenneth L. Clarkson, Boris Delaunay, Rex A.\n"
);
printf(
" Dwyer, David Eppstein, Steven Fortune, Leonidas J. Guibas, Donald E.\n");
printf(
" Knuth, Charles L. Lawson, Der-Tsai Lee, Gary L. Miller, Ernst P. Mucke,\n");
printf(
" Steven E. Pav, Douglas M. Priest, Jim Ruppert, Isaac Saias, Bruce J.\n");
printf(
" Schachter, Micha Sharir, Peter W. Shor, Daniel D. Sleator, Jorge Stolfi,\n"
);
printf(" Robert E. Tarjan, Alper Ungor, Christopher J. Van Wyk, Noel J.\n");
printf(
" Walkington, and Binhai Zhu. See the comments at the beginning of the\n");
printf(" source code for references.\n\n");
triexit(0);
}
#endif /* not TRILIBRARY */
/*****************************************************************************/
/* */
/* internalerror() Ask the user to send me the defective product. Exit. */
/* */
/*****************************************************************************/
void internalerror(void)
{
printf(" Please report this bug to jrs@cs.berkeley.edu\n");
printf(" Include the message above, your input data set, and the exact\n");
printf(" command line you used to run Triangle.\n");
triexit(1);
}
/*****************************************************************************/
/* */
/* parsecommandline() Read the command line, identify switches, and set */
/* up options and file names. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
void parsecommandline(int argc, char **argv, struct behavior *b)
#else /* not ANSI_DECLARATORS */
void parsecommandline(argc, argv, b)
int argc;
char **argv;
struct behavior *b;
#endif /* not ANSI_DECLARATORS */
{
#ifdef TRILIBRARY
#define STARTINDEX 0
#else /* not TRILIBRARY */
#define STARTINDEX 1
int increment;
int meshnumber;
#endif /* not TRILIBRARY */
int i, j, k;
char workstring[FILENAMESIZE];
b->poly = b->refine = b->quality = 0;
b->vararea = b->fixedarea = b->usertest = 0;
b->regionattrib = b->convex = b->weighted = b->jettison = 0;
b->firstnumber = 1;
b->edgesout = b->voronoi = b->neighbors = b->geomview = 0;
b->nobound = b->nopolywritten = b->nonodewritten = b->noelewritten = 0;
b->noiterationnum = 0;
b->noholes = b->noexact = 0;
b->incremental = b->sweepline = 0;
b->dwyer = 1;
b->splitseg = 0;
b->docheck = 0;
b->nobisect = 0;
b->conformdel = 0;
b->steiner = -1;
b->order = 1;
b->minangle = 0.0;
b->maxarea = -1.0;
b->quiet = b->verbose = 0;
#ifndef TRILIBRARY
b->innodefilename[0] = '\0';
#endif /* not TRILIBRARY */
for (i = STARTINDEX; i < argc; i++) {
#ifndef TRILIBRARY
if (argv[i][0] == '-') {
#endif /* not TRILIBRARY */
for (j = STARTINDEX; argv[i][j] != '\0'; j++) {
if (argv[i][j] == 'p') {
b->poly = 1;
}
#ifndef CDT_ONLY
if (argv[i][j] == 'r') {
b->refine = 1;
}
if (argv[i][j] == 'q') {
b->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';
b->minangle = (REAL) strtod(workstring, (char **) NULL);
} else {
b->minangle = 20.0;
}
}
if (argv[i][j] == 'a') {
b->quality = 1;
if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
(argv[i][j + 1] == '.')) {
b->fixedarea = 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';
b->maxarea = (REAL) strtod(workstring, (char **) NULL);
if (b->maxarea <= 0.0) {
printf("Error: Maximum area must be greater than zero.\n");
triexit(1);
}
} else {
b->vararea = 1;
}
}
if (argv[i][j] == 'u') {
b->quality = 1;
b->usertest = 1;
}
#endif /* not CDT_ONLY */
if (argv[i][j] == 'A') {
b->regionattrib = 1;
}
if (argv[i][j] == 'c') {
b->convex = 1;
}
if (argv[i][j] == 'w') {
b->weighted = 1;
}
if (argv[i][j] == 'W') {
b->weighted = 2;
}
if (argv[i][j] == 'j') {
b->jettison = 1;
}
if (argv[i][j] == 'z') {
b->firstnumber = 0;
}
if (argv[i][j] == 'e') {
b->edgesout = 1;
}
if (argv[i][j] == 'v') {
b->voronoi = 1;
}
if (argv[i][j] == 'n') {
b->neighbors = 1;
}
if (argv[i][j] == 'g') {
b->geomview = 1;
}
if (argv[i][j] == 'B') {
b->nobound = 1;
}
if (argv[i][j] == 'P') {
b->nopolywritten = 1;
}
if (argv[i][j] == 'N') {
b->nonodewritten = 1;
}
if (argv[i][j] == 'E') {
b->noelewritten = 1;
}
#ifndef TRILIBRARY
if (argv[i][j] == 'I') {
b->noiterationnum = 1;
}
#endif /* not TRILIBRARY */
if (argv[i][j] == 'O') {
b->noholes = 1;
}
if (argv[i][j] == 'X') {
b->noexact = 1;
}
if (argv[i][j] == 'o') {
if (argv[i][j + 1] == '2') {
j++;
b->order = 2;
}
}
#ifndef CDT_ONLY
if (argv[i][j] == 'Y') {
b->nobisect++;
}
if (argv[i][j] == 'S') {
b->steiner = 0;
while ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) {
j++;
b->steiner = b->steiner * 10 + (int) (argv[i][j] - '0');
}
}
#endif /* not CDT_ONLY */
#ifndef REDUCED
if (argv[i][j] == 'i') {
b->incremental = 1;
}
if (argv[i][j] == 'F') {
b->sweepline = 1;
}
#endif /* not REDUCED */
if (argv[i][j] == 'l') {
b->dwyer = 0;
}
#ifndef REDUCED
#ifndef CDT_ONLY
if (argv[i][j] == 's') {
b->splitseg = 1;
}
if ((argv[i][j] == 'D') || (argv[i][j] == 'L')) {
b->quality = 1;
b->conformdel = 1;
}
#endif /* not CDT_ONLY */
if (argv[i][j] == 'C') {
b->docheck = 1;
}
#endif /* not REDUCED */
if (argv[i][j] == 'Q') {
b->quiet = 1;
}
if (argv[i][j] == 'V') {
b->verbose++;
}
#ifndef TRILIBRARY
if ((argv[i][j] == 'h') || (argv[i][j] == 'H') ||
(argv[i][j] == '?')) {
info();
}
#endif /* not TRILIBRARY */
}
#ifndef TRILIBRARY
} else {
strncpy(b->innodefilename, argv[i], FILENAMESIZE - 1);
b->innodefilename[FILENAMESIZE - 1] = '\0';
}
#endif /* not TRILIBRARY */
}
#ifndef TRILIBRARY
if (b->innodefilename[0] == '\0') {
syntax();
}
if (!strcmp(&b->innodefilename[strlen(b->innodefilename) - 5], ".node")) {
b->innodefilename[strlen(b->innodefilename) - 5] = '\0';
}
if (!strcmp(&b->innodefilename[strlen(b->innodefilename) - 5], ".poly")) {
b->innodefilename[strlen(b->innodefilename) - 5] = '\0';
b->poly = 1;
}
#ifndef CDT_ONLY
if (!strcmp(&b->innodefilename[strlen(b->innodefilename) - 4], ".ele")) {
b->innodefilename[strlen(b->innodefilename) - 4] = '\0';
b->refine = 1;
}
if (!strcmp(&b->innodefilename[strlen(b->innodefilename) - 5], ".area")) {
b->innodefilename[strlen(b->innodefilename) - 5] = '\0';
b->refine = 1;
b->quality = 1;
b->vararea = 1;
}
#endif /* not CDT_ONLY */
#endif /* not TRILIBRARY */
b->usesegments = b->poly || b->refine || b->quality || b->convex;
b->goodangle = cos(b->minangle * PI / 180.0);
if (b->goodangle == 1.0) {
b->offconstant = 0.0;
} else {
b->offconstant = 0.475 * sqrt((1.0 + b->goodangle) / (1.0 - b->goodangle));
}
b->goodangle *= b->goodangle;
if (b->refine && b->noiterationnum) {
printf(
"Error: You cannot use the -I switch when refining a triangulation.\n");
triexit(1);
}
/* Be careful not to allocate space for element area constraints that */
/* will never be assigned any value (other than the default -1.0). */
if (!b->refine && !b->poly) {
b->vararea = 0;
}
/* Be careful not to add an extra attribute to each element unless the */
/* input supports it (PSLG in, but not refining a preexisting mesh). */
if (b->refine || !b->poly) {
b->regionattrib = 0;
}
/* Regular/weighted triangulations are incompatible with PSLGs */
/* and meshing. */
if (b->weighted && (b->poly || b->quality)) {
b->weighted = 0;
if (!b->quiet) {
printf("Warning: weighted triangulations (-w, -W) are incompatible\n");
printf(" with PSLGs (-p) and meshing (-q, -a, -u). Weights ignored.\n"
);
}
}
if (b->jettison && b->nonodewritten && !b->quiet) {
printf("Warning: -j and -N switches are somewhat incompatible.\n");
printf(" If any vertices are jettisoned, you will need the output\n");
printf(" .node file to reconstruct the new node indices.");
}
#ifndef TRILIBRARY
strcpy(b->inpolyfilename, b->innodefilename);
strcpy(b->inelefilename, b->innodefilename);
strcpy(b->areafilename, b->innodefilename);
increment = 0;
strcpy(workstring, b->innodefilename);
j = 1;
while (workstring[j] != '\0') {
if ((workstring[j] == '.') && (workstring[j + 1] != '\0')) {
increment = j + 1;
}
j++;
}
meshnumber = 0;
if (increment > 0) {
j = increment;
do {
if ((workstring[j] >= '0') && (workstring[j] <= '9')) {
meshnumber = meshnumber * 10 + (int) (workstring[j] - '0');
} else {
increment = 0;
}
j++;
} while (workstring[j] != '\0');
}
if (b->noiterationnum) {
strcpy(b->outnodefilename, b->innodefilename);
strcpy(b->outelefilename, b->innodefilename);
strcpy(b->edgefilename, b->innodefilename);
strcpy(b->vnodefilename, b->innodefilename);
strcpy(b->vedgefilename, b->innodefilename);
strcpy(b->neighborfilename, b->innodefilename);
strcpy(b->offfilename, b->innodefilename);
strcat(b->outnodefilename, ".node");
strcat(b->outelefilename, ".ele");
strcat(b->edgefilename, ".edge");
strcat(b->vnodefilename, ".v.node");
strcat(b->vedgefilename, ".v.edge");
strcat(b->neighborfilename, ".neigh");
strcat(b->offfilename, ".off");
} else if (increment == 0) {
strcpy(b->outnodefilename, b->innodefilename);
strcpy(b->outpolyfilename, b->innodefilename);
strcpy(b->outelefilename, b->innodefilename);
strcpy(b->edgefilename, b->innodefilename);
strcpy(b->vnodefilename, b->innodefilename);
strcpy(b->vedgefilename, b->innodefilename);
strcpy(b->neighborfilename, b->innodefilename);
strcpy(b->offfilename, b->innodefilename);
strcat(b->outnodefilename, ".1.node");
strcat(b->outpolyfilename, ".1.poly");
strcat(b->outelefilename, ".1.ele");
strcat(b->edgefilename, ".1.edge");
strcat(b->vnodefilename, ".1.v.node");
strcat(b->vedgefilename, ".1.v.edge");
strcat(b->neighborfilename, ".1.neigh");
strcat(b->offfilename, ".1.off");
} else {
workstring[increment] = '%';
workstring[increment + 1] = 'd';
workstring[increment + 2] = '\0';
sprintf(b->outnodefilename, workstring, meshnumber + 1);
strcpy(b->outpolyfilename, b->outnodefilename);
strcpy(b->outelefilename, b->outnodefilename);
strcpy(b->edgefilename, b->outnodefilename);
strcpy(b->vnodefilename, b->outnodefilename);
strcpy(b->vedgefilename, b->outnodefilename);
strcpy(b->neighborfilename, b->outnodefilename);
strcpy(b->offfilename, b->outnodefilename);
strcat(b->outnodefilename, ".node");
strcat(b->outpolyfilename, ".poly");
strcat(b->outelefilename, ".ele");
strcat(b->edgefilename, ".edge");
strcat(b->vnodefilename, ".v.node");
strcat(b->vedgefilename, ".v.edge");
strcat(b->neighborfilename, ".neigh");
strcat(b->offfilename, ".off");
}
strcat(b->innodefilename, ".node");
strcat(b->inpolyfilename, ".poly");
strcat(b->inelefilename, ".ele");
strcat(b->areafilename, ".area");
#endif /* not TRILIBRARY */
}
/** **/
/** **/
/********* User interaction routines begin here *********/
/********* Debugging routines begin here *********/
/** **/
/** **/
/*****************************************************************************/
/* */
/* printtriangle() Print out the details of an oriented triangle. */
/* */
/* I originally wrote this procedure to simplify debugging; it can be */
/* called directly from the debugger, and presents information about an */
/* oriented triangle in digestible form. It's also used when the */
/* highest level of verbosity (`-VVV') is specified. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
void printtriangle(struct mesh *m, struct behavior *b, struct otri *t)
#else /* not ANSI_DECLARATORS */
void printtriangle(m, b, t)
struct mesh *m;
struct behavior *b;
struct otri *t;
#endif /* not ANSI_DECLARATORS */
{
struct otri printtri;
struct osub printsh;
vertex printvertex;
printf("triangle x%zx with orientation %d:\n", (size_t) t->tri,
t->orient);
decode(t->tri[0], printtri);
if (printtri.tri == m->dummytri) {
printf(" [0] = Outer space\n");
} else {
printf(" [0] = x%zx %d\n", (size_t) printtri.tri,
printtri.orient);
}
decode(t->tri[1], printtri);
if (printtri.tri == m->dummytri) {
printf(" [1] = Outer space\n");
} else {
printf(" [1] = x%zx %d\n", (size_t) printtri.tri,
printtri.orient);
}
decode(t->tri[2], printtri);
if (printtri.tri == m->dummytri) {
printf(" [2] = Outer space\n");
} else {
printf(" [2] = x%zx %d\n", (size_t) printtri.tri,
printtri.orient);
}
org(*t, printvertex);
if (printvertex == (vertex) NULL)
printf(" Origin[%d] = NULL\n", (t->orient + 1) % 3 + 3);
else
printf(" Origin[%d] = x%zx (%.12g, %.12g)\n",
(t->orient + 1) % 3 + 3, (size_t) printvertex,
printvertex[0], printvertex[1]);
dest(*t, printvertex);
if (printvertex == (vertex) NULL)
printf(" Dest [%d] = NULL\n", (t->orient + 2) % 3 + 3);
else
printf(" Dest [%d] = x%zx (%.12g, %.12g)\n",
(t->orient + 2) % 3 + 3, (size_t) printvertex,
printvertex[0], printvertex[1]);
apex(*t, printvertex);
if (printvertex == (vertex) NULL)
printf(" Apex [%d] = NULL\n", t->orient + 3);
else
printf(" Apex [%d] = x%zx (%.12g, %.12g)\n",
t->orient + 3, (size_t) printvertex,
printvertex[0], printvertex[1]);
if (b->usesegments) {
sdecode(t->tri[6], printsh);
if (printsh.ss != m->dummysub) {
printf(" [6] = x%zx %d\n", (size_t) printsh.ss,
printsh.ssorient);
}
sdecode(t->tri[7], printsh);
if (printsh.ss != m->dummysub) {
printf(" [7] = x%zx %d\n", (size_t) printsh.ss,
printsh.ssorient);
}
sdecode(t->tri[8], printsh);
if (printsh.ss != m->dummysub) {
printf(" [8] = x%zx %d\n", (size_t) printsh.ss,
printsh.ssorient);
}
}
if (b->vararea) {
printf(" Area constraint: %.4g\n", areabound(*t));
}
}
/*****************************************************************************/
/* */
/* printsubseg() Print out the details of an oriented subsegment. */
/* */
/* I originally wrote this procedure to simplify debugging; it can be */
/* called directly from the debugger, and presents information about an */
/* oriented subsegment in digestible form. It's also used when the highest */
/* level of verbosity (`-VVV') is specified. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
void printsubseg(struct mesh *m, struct behavior *b, struct osub *s)
#else /* not ANSI_DECLARATORS */
void printsubseg(m, b, s)
struct mesh *m;
struct behavior *b;
struct osub *s;
#endif /* not ANSI_DECLARATORS */
{
struct osub printsh;
struct otri printtri;
vertex printvertex;
printf("subsegment x%zx with orientation %d and mark %d:\n",
(size_t) s->ss, s->ssorient, mark(*s));
sdecode(s->ss[0], printsh);
if (printsh.ss == m->dummysub) {
printf(" [0] = No subsegment\n");
} else {
printf(" [0] = x%zx %d\n", (size_t) printsh.ss,
printsh.ssorient);
}
sdecode(s->ss[1], printsh);
if (printsh.ss == m->dummysub) {
printf(" [1] = No subsegment\n");
} else {
printf(" [1] = x%zx %d\n", (size_t) printsh.ss,
printsh.ssorient);
}
sorg(*s, printvertex);
if (printvertex == (vertex) NULL)
printf(" Origin[%d] = NULL\n", 2 + s->ssorient);
else
printf(" Origin[%d] = x%zx (%.12g, %.12g)\n",
2 + s->ssorient, (size_t) printvertex,
printvertex[0], printvertex[1]);
sdest(*s, printvertex);
if (printvertex == (vertex) NULL)
printf(" Dest [%d] = NULL\n", 3 - s->ssorient);
else
printf(" Dest [%d] = x%zx (%.12g, %.12g)\n",
3 - s->ssorient, (size_t) printvertex,
printvertex[0], printvertex[1]);
decode(s->ss[6], printtri);
if (printtri.tri == m->dummytri) {
printf(" [6] = Outer space\n");
} else {
printf(" [6] = x%zx %d\n", (size_t) printtri.tri,
printtri.orient);
}
decode(s->ss[7], printtri);
if (printtri.tri == m->dummytri) {
printf(" [7] = Outer space\n");
} else {
printf(" [7] = x%zx %d\n", (size_t) printtri.tri,
printtri.orient);
}
segorg(*s, printvertex);
if (printvertex == (vertex) NULL)
printf(" Segment origin[%d] = NULL\n", 4 + s->ssorient);
else
printf(" Segment origin[%d] = x%zx (%.12g, %.12g)\n",
4 + s->ssorient, (size_t) printvertex,
printvertex[0], printvertex[1]);
segdest(*s, printvertex);
if (printvertex == (vertex) NULL)
printf(" Segment dest [%d] = NULL\n", 5 - s->ssorient);
else
printf(" Segment dest [%d] = x%zx (%.12g, %.12g)\n",
5 - s->ssorient, (size_t) printvertex,
printvertex[0], printvertex[1]);
}
/** **/
/** **/
/********* Debugging routines end here *********/
/********* Memory management routines begin here *********/
/** **/
/** **/
/*****************************************************************************/
/* */
/* poolzero() Set all of a pool's fields to zero. */
/* */
/* This procedure should never be called on a pool that has any memory */
/* allocated to it, as that memory would leak. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
void poolzero(struct memorypool *pool)
#else /* not ANSI_DECLARATORS */
void poolzero(pool)
struct memorypool *pool;
#endif /* not ANSI_DECLARATORS */
{
pool->firstblock = (VOID **) NULL;
pool->nowblock = (VOID **) NULL;
pool->nextitem = (VOID *) NULL;
pool->deaditemstack = (VOID *) NULL;
pool->pathblock = (VOID **) NULL;
pool->pathitem = (VOID *) NULL;
pool->alignbytes = 0;
pool->itembytes = 0;
pool->itemsperblock = 0;
pool->itemsfirstblock = 0;
pool->items = 0;
pool->maxitems = 0;
pool->unallocateditems = 0;
pool->pathitemsleft = 0;
}
/*****************************************************************************/
/* */
/* poolrestart() Deallocate all items in a 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. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
void poolrestart(struct memorypool *pool)
#else /* not ANSI_DECLARATORS */
void poolrestart(pool)
struct memorypool *pool;
#endif /* not ANSI_DECLARATORS */
{
size_t alignptr;
pool->items = 0;
pool->maxitems = 0;
/* Set the currently active block. */
pool->nowblock = pool->firstblock;
/* Find the first item in the pool. Increment by the size of (VOID *). */
alignptr = (size_t) (pool->nowblock + 1);
/* Align the item on an `alignbytes'-byte boundary. */
pool->nextitem = (VOID *)
(alignptr + (size_t) pool->alignbytes -
(alignptr % (size_t) pool->alignbytes));
/* There are lots of unallocated items left in this block. */
pool->unallocateditems = pool->itemsfirstblock;
/* The stack of deallocated items is empty. */
pool->deaditemstack = (VOID *) NULL;
}
/*****************************************************************************/
/* */
/* poolinit() Initialize a pool of memory for allocation of items. */
/* */
/* This routine initializes the machinery for allocating 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. */
/* */
/* Don't change this routine unless you understand it. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
void poolinit(struct memorypool *pool, int bytecount, int itemcount,
int firstitemcount, int alignment)
#else /* not ANSI_DECLARATORS */
void poolinit(pool, bytecount, itemcount, firstitemcount, alignment)
struct memorypool *pool;
int bytecount;
int itemcount;
int firstitemcount;
int alignment;
#endif /* not ANSI_DECLARATORS */
{
/* Find the proper alignment, which must be at least as large as: */
/* - The parameter `alignment'. */
/* - sizeof(VOID *), so the stack of dead items can be maintained */
/* without unaligned accesses. */
if (alignment > sizeof(VOID *)) {
pool->alignbytes = alignment;
} else {
pool->alignbytes = sizeof(VOID *);
}
pool->itembytes = ((bytecount - 1) / pool->alignbytes + 1) *
pool->alignbytes;
pool->itemsperblock = itemcount;
if (firstitemcount == 0) {
pool->itemsfirstblock = itemcount;
} else {
pool->itemsfirstblock = firstitemcount;
}
/* Allocate a block of items. Space for `itemsfirstblock' items and one */
/* pointer (to point to the next block) are allocated, as well as space */
/* to ensure alignment of the items. */
pool->firstblock = (VOID **)
trimalloc(pool->itemsfirstblock * pool->itembytes + (int) sizeof(VOID *) +
pool->alignbytes);
/* Set the next block pointer to NULL. */
*(pool->firstblock) = (VOID *) NULL;
poolrestart(pool);
}
/*****************************************************************************/
/* */
/* pooldeinit() Free to the operating system all memory taken by a pool. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
void pooldeinit(struct memorypool *pool)
#else /* not ANSI_DECLARATORS */
void pooldeinit(pool)
struct memorypool *pool;
#endif /* not ANSI_DECLARATORS */
{
while (pool->firstblock != (VOID **) NULL) {
pool->nowblock = (VOID **) *(pool->firstblock);
trifree((VOID *) pool->firstblock);
pool->firstblock = pool->nowblock;
}
}
/*****************************************************************************/
/* */
/* poolalloc() Allocate space for an item. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
VOID *poolalloc(struct memorypool *pool)
#else /* not ANSI_DECLARATORS */
VOID *poolalloc(pool)
struct memorypool *pool;
#endif /* not ANSI_DECLARATORS */
{
VOID *newitem;
VOID **newblock;
size_t alignptr;
/* First check the linked list of dead items. If the list is not */
/* empty, allocate an item from the list rather than a fresh one. */
if (pool->deaditemstack != (VOID *) NULL) {
newitem = pool->deaditemstack; /* Take first item in list. */
pool->deaditemstack = * (VOID **) pool->deaditemstack;
} else {
/* Check if there are any free items left in the current block. */
if (pool->unallocateditems == 0) {
/* Check if another block must be allocated. */
if (*(pool->nowblock) == (VOID *) NULL) {
/* Allocate a new block of items, pointed to by the previous block. */
newblock = (VOID **) trimalloc(pool->itemsperblock * pool->itembytes +
(int) sizeof(VOID *) +
pool->alignbytes);
*(pool->nowblock) = (VOID *) newblock;
/* The next block pointer is NULL. */
*newblock = (VOID *) NULL;
}
/* Move to the new block. */
pool->nowblock = (VOID **) *(pool->nowblock);
/* Find the first item in the block. */
/* Increment by the size of (VOID *). */
alignptr = (size_t) (pool->nowblock + 1);
/* Align the item on an `alignbytes'-byte boundary. */
pool->nextitem = (VOID *)
(alignptr + (size_t) pool->alignbytes -
(alignptr % (size_t) pool->alignbytes));
/* There are lots of unallocated items left in this block. */
pool->unallocateditems = pool->itemsperblock;
}
/* Allocate a new item. */
newitem = pool->nextitem;
/* Advance `nextitem' pointer to next free item in block. */
pool->nextitem = (VOID *) ((char *) pool->nextitem + pool->itembytes);
pool->unallocateditems--;
pool->maxitems++;
}
pool->items++;
return newitem;
}
/*****************************************************************************/
/* */
/* pooldealloc() Deallocate space for an item. */
/* */
/* The deallocated space is stored in a queue for later reuse. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
void pooldealloc(struct memorypool *pool, VOID *dyingitem)
#else /* not ANSI_DECLARATORS */
void pooldealloc(pool, dyingitem)
struct memorypool *pool;
VOID *dyingitem;
#endif /* not ANSI_DECLARATORS */
{
/* Push freshly killed item onto stack. */
*((VOID **) dyingitem) = pool->deaditemstack;
pool->deaditemstack = dyingitem;
pool->items--;
}
/*****************************************************************************/
/* */
/* traversalinit() Prepare to traverse the entire list of items. */
/* */
/* This routine is used in conjunction with traverse(). */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
void traversalinit(struct memorypool *pool)
#else /* not ANSI_DECLARATORS */
void traversalinit(pool)
struct memorypool *pool;
#endif /* not ANSI_DECLARATORS */
{
size_t alignptr;
/* Begin the traversal in the first block. */
pool->pathblock = pool->firstblock;
/* Find the first item in the block. Increment by the size of (VOID *). */
alignptr = (size_t) (pool->pathblock + 1);
/* Align with item on an `alignbytes'-byte boundary. */
pool->pathitem = (VOID *)
(alignptr + (size_t) pool->alignbytes -
(alignptr % (size_t) pool->alignbytes));
/* Set the number of items left in the current block. */
pool->pathitemsleft = pool->itemsfirstblock;
}
/*****************************************************************************/
/* */
/* 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. Why? I don't want to allocate extra */
/* space just to demarcate dead items. It can usually be done more */
/* space-efficiently by a routine that knows something about the structure */
/* of the item. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
VOID *traverse(struct memorypool *pool)
#else /* not ANSI_DECLARATORS */
VOID *traverse(pool)
struct memorypool *pool;
#endif /* not ANSI_DECLARATORS */
{
VOID *newitem;
size_t alignptr;
/* Stop upon exhausting the list of items. */
if (pool->pathitem == pool->nextitem) {
return (VOID *) NULL;
}
/* Check whether any untraversed items remain in the current block. */
if (pool->pathitemsleft == 0) {
/* Find the next block. */
pool->pathblock = (VOID **) *(pool->pathblock);
/* Find the first item in the block. Increment by the size of (VOID *). */
alignptr = (size_t) (pool->pathblock + 1);
/* Align with item on an `alignbytes'-byte boundary. */
pool->pathitem = (VOID *)
(alignptr + (size_t) pool->alignbytes -
(alignptr % (size_t) pool->alignbytes));
/* Set the number of items left in the current block. */
pool->pathitemsleft = pool->itemsperblock;
}
newitem = pool->pathitem;
/* Find the next item in the block. */
pool->pathitem = (VOID *) ((char *) pool->pathitem + pool->itembytes);
pool->pathitemsleft--;
return newitem;
}
/*****************************************************************************/
/* */
/* dummyinit() Initialize the triangle that fills "outer space" and the */
/* omnipresent subsegment. */
/* */
/* The triangle that fills "outer space," called `dummytri', is pointed to */
/* by every triangle and subsegment on a boundary (be it outer or inner) of */
/* the triangulation. Also, `dummytri' points to one of the triangles on */
/* the convex hull (until the holes and concavities are carved), making it */
/* possible to find a starting triangle for point location. */
/* */
/* The omnipresent subsegment, `dummysub', is pointed to by every triangle */
/* or subsegment that doesn't have a full complement of real subsegments */
/* to point to. */
/* */
/* `dummytri' and `dummysub' are generally required to fulfill only a few */
/* invariants: their vertices must remain NULL and `dummytri' must always */
/* be bonded (at offset zero) to some triangle on the convex hull of the */
/* mesh, via a boundary edge. Otherwise, the connections of `dummytri' and */
/* `dummysub' may change willy-nilly. This makes it possible to avoid */
/* writing a good deal of special-case code (in the edge flip, for example) */
/* for dealing with the boundary of the mesh, places where no subsegment is */
/* present, and so forth. Other entities are frequently bonded to */
/* `dummytri' and `dummysub' as if they were real mesh entities, with no */
/* harm done. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
void dummyinit(struct mesh *m, struct behavior *b, int trianglebytes,
int subsegbytes)
#else /* not ANSI_DECLARATORS */
void dummyinit(m, b, trianglebytes, subsegbytes)
struct mesh *m;
struct behavior *b;
int trianglebytes;
int subsegbytes;
#endif /* not ANSI_DECLARATORS */
{
size_t alignptr;
/* Set up `dummytri', the `triangle' that occupies "outer space." */
m->dummytribase = (triangle *) trimalloc(trianglebytes +
m->triangles.alignbytes);
/* Align `dummytri' on a `triangles.alignbytes'-byte boundary. */
alignptr = (size_t) m->dummytribase;
m->dummytri = (triangle *)
(alignptr + (size_t) m->triangles.alignbytes -
(alignptr % (size_t) m->triangles.alignbytes));
/* Initialize the three adjoining triangles to be "outer space." These */
/* will eventually be changed by various bonding operations, but their */
/* values don't really matter, as long as they can legally be */
/* dereferenced. */
m->dummytri[0] = (triangle) m->dummytri;
m->dummytri[1] = (triangle) m->dummytri;
m->dummytri[2] = (triangle) m->dummytri;
/* Three NULL vertices. */
m->dummytri[3] = (triangle) NULL;
m->dummytri[4] = (triangle) NULL;
m->dummytri[5] = (triangle) NULL;
if (b->usesegments) {
/* Set up `dummysub', the omnipresent subsegment pointed to by any */
/* triangle side or subsegment end that isn't attached to a real */
/* subsegment. */
m->dummysubbase = (subseg *) trimalloc(subsegbytes +
m->subsegs.alignbytes);
/* Align `dummysub' on a `subsegs.alignbytes'-byte boundary. */
alignptr = (size_t) m->dummysubbase;
m->dummysub = (subseg *)
(alignptr + (size_t) m->subsegs.alignbytes -
(alignptr % (size_t) m->subsegs.alignbytes));
/* Initialize the two adjoining subsegments to be the omnipresent */
/* subsegment. These will eventually be changed by various bonding */
/* operations, but their values don't really matter, as long as they */
/* can legally be dereferenced. */
m->dummysub[0] = (subseg) m->dummysub;
m->dummysub[1] = (subseg) m->dummysub;
/* Four NULL vertices. */
m->dummysub[2] = (subseg) NULL;
m->dummysub[3] = (subseg) NULL;
m->dummysub[4] = (subseg) NULL;
m->dummysub[5] = (subseg) NULL;
/* Initialize the two adjoining triangles to be "outer space." */
m->dummysub[6] = (subseg) m->dummytri;
m->dummysub[7] = (subseg) m->dummytri;
/* Set the boundary marker to zero. */
* (int *) (m->dummysub + 8) = 0;
/* Initialize the three adjoining subsegments of `dummytri' to be */
/* the omnipresent subsegment. */
m->dummytri[6] = (triangle) m->dummysub;
m->dummytri[7] = (triangle) m->dummysub;
m->dummytri[8] = (triangle) m->dummysub;
}
}
/*****************************************************************************/
/* */
/* initializevertexpool() Calculate the size of the vertex data structure */
/* and initialize its memory pool. */
/* */
/* This routine also computes the `vertexmarkindex' and `vertex2triindex' */
/* indices used to find values within each vertex. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
void initializevertexpool(struct mesh *m, struct behavior *b)
#else /* not ANSI_DECLARATORS */
void initializevertexpool(m, b)
struct mesh *m;
struct behavior *b;
#endif /* not ANSI_DECLARATORS */
{
int vertexsize;
/* The index within each vertex at which the boundary marker is found, */
/* followed by the vertex type. Ensure the vertex marker is aligned to */
/* a sizeof(int)-byte address. */
m->vertexmarkindex = ((m->mesh_dim + m->nextras) * sizeof(REAL) +
sizeof(int) - 1) /
sizeof(int);
vertexsize = (m->vertexmarkindex + 2) * sizeof(int);
if (b->poly) {
/* The index within each vertex at which a triangle pointer is found. */
/* Ensure the pointer is aligned to a sizeof(triangle)-byte address. */
m->vertex2triindex = (vertexsize + sizeof(triangle) - 1) /
sizeof(triangle);
vertexsize = (m->vertex2triindex + 1) * sizeof(triangle);
}
/* Initialize the pool of vertices. */
poolinit(&m->vertices, vertexsize, VERTEXPERBLOCK,
m->invertices > VERTEXPERBLOCK ? m->invertices : VERTEXPERBLOCK,
sizeof(REAL));
}
/*****************************************************************************/
/* */
/* initializetrisubpools() Calculate the sizes of the triangle and */
/* subsegment data structures and initialize */
/* their memory pools. */
/* */
/* This routine also computes the `highorderindex', `elemattribindex', and */
/* `areaboundindex' indices used to find values within each triangle. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
void initializetrisubpools(struct mesh *m, struct behavior *b)
#else /* not ANSI_DECLARATORS */
void initializetrisubpools(m, b)
struct mesh *m;
struct behavior *b;
#endif /* not ANSI_DECLARATORS */
{
int trisize;
/* The index within each triangle at which the extra nodes (above three) */
/* associated with high order elements are found. There are three */
/* pointers to other triangles, three pointers to corners, and possibly */
/* three pointers to subsegments before the extra nodes. */
m->highorderindex = 6 + (b->usesegments * 3);
/* The number of bytes occupied by a triangle. */
trisize = ((b->order + 1) * (b->order + 2) / 2 + (m->highorderindex - 3)) *
sizeof(triangle);
/* The index within each triangle at which its attributes are found, */
/* where the index is measured in REALs. */
m->elemattribindex = (trisize + sizeof(REAL) - 1) / sizeof(REAL);
/* The index within each triangle at which the maximum area constraint */
/* is found, where the index is measured in REALs. Note that if the */
/* `regionattrib' flag is set, an additional attribute will be added. */
m->areaboundindex = m->elemattribindex + m->eextras + b->regionattrib;
/* If triangle attributes or an area bound are needed, increase the number */
/* of bytes occupied by a triangle. */
if (b->vararea) {
trisize = (m->areaboundindex + 1) * sizeof(REAL);
} else if (m->eextras + b->regionattrib > 0) {
trisize = m->areaboundindex * sizeof(REAL);
}
/* If a Voronoi diagram or triangle neighbor graph is requested, make */
/* sure there's room to store an integer index in each triangle. This */
/* integer index can occupy the same space as the subsegment pointers */
/* or attributes or area constraint or extra nodes. */
if ((b->voronoi || b->neighbors) &&
(trisize < 6 * sizeof(triangle) + sizeof(int))) {
trisize = 6 * sizeof(triangle) + sizeof(int);
}
/* Having determined the memory size of a triangle, initialize the pool. */
poolinit(&m->triangles, trisize, TRIPERBLOCK,
(2 * m->invertices - 2) > TRIPERBLOCK ? (2 * m->invertices - 2) :
TRIPERBLOCK, 4);
if (b->usesegments) {
/* Initialize the pool of subsegments. Take into account all eight */
/* pointers and one boundary marker. */
poolinit(&m->subsegs, 8 * sizeof(triangle) + sizeof(int),
SUBSEGPERBLOCK, SUBSEGPERBLOCK, 4);
/* Initialize the "outer space" triangle and omnipresent subsegment. */
dummyinit(m, b, m->triangles.itembytes, m->subsegs.itembytes);
} else {
/* Initialize the "outer space" triangle. */
dummyinit(m, b, m->triangles.itembytes, 0);
}
}
/*****************************************************************************/
/* */
/* triangledealloc() Deallocate space for a triangle, marking it dead. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
void triangledealloc(struct mesh *m, triangle *dyingtriangle)
#else /* not ANSI_DECLARATORS */
void triangledealloc(m, dyingtriangle)
struct mesh *m;
triangle *dyingtriangle;
#endif /* not ANSI_DECLARATORS */
{
/* Mark the triangle as dead. This makes it possible to detect dead */
/* triangles when traversing the list of all triangles. */
killtri(dyingtriangle);
pooldealloc(&m->triangles, (VOID *) dyingtriangle);
}
/*****************************************************************************/
/* */
/* triangletraverse() Traverse the triangles, skipping dead ones. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
triangle *triangletraverse(struct mesh *m)
#else /* not ANSI_DECLARATORS */
triangle *triangletraverse(m)
struct mesh *m;
#endif /* not ANSI_DECLARATORS */
{
triangle *newtriangle;
do {
newtriangle = (triangle *) traverse(&m->triangles);
if (newtriangle == (triangle *) NULL) {
return (triangle *) NULL;
}
} while (deadtri(newtriangle)); /* Skip dead ones. */
return newtriangle;
}
/*****************************************************************************/
/* */
/* subsegdealloc() Deallocate space for a subsegment, marking it dead. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
void subsegdealloc(struct mesh *m, subseg *dyingsubseg)
#else /* not ANSI_DECLARATORS */
void subsegdealloc(m, dyingsubseg)
struct mesh *m;
subseg *dyingsubseg;
#endif /* not ANSI_DECLARATORS */
{
/* Mark the subsegment as dead. This makes it possible to detect dead */
/* subsegments when traversing the list of all subsegments. */
killsubseg(dyingsubseg);
pooldealloc(&m->subsegs, (VOID *) dyingsubseg);
}
/*****************************************************************************/
/* */
/* subsegtraverse() Traverse the subsegments, skipping dead ones. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
subseg *subsegtraverse(struct mesh *m)
#else /* not ANSI_DECLARATORS */
subseg *subsegtraverse(m)
struct mesh *m;
#endif /* not ANSI_DECLARATORS */
{
subseg *newsubseg;
do {
newsubseg = (subseg *) traverse(&m->subsegs);
if (newsubseg == (subseg *) NULL) {
return (subseg *) NULL;
}
} while (deadsubseg(newsubseg)); /* Skip dead ones. */
return newsubseg;
}
/*****************************************************************************/
/* */
/* vertexdealloc() Deallocate space for a vertex, marking it dead. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
void vertexdealloc(struct mesh *m, vertex dyingvertex)
#else /* not ANSI_DECLARATORS */
void vertexdealloc(m, dyingvertex)
struct mesh *m;
vertex dyingvertex;
#endif /* not ANSI_DECLARATORS */
{
/* Mark the vertex as dead. This makes it possible to detect dead */
/* vertices when traversing the list of all vertices. */
setvertextype(dyingvertex, DEADVERTEX);
pooldealloc(&m->vertices, (VOID *) dyingvertex);
}
/*****************************************************************************/
/* */
/* vertextraverse() Traverse the vertices, skipping dead ones. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
vertex vertextraverse(struct mesh *m)
#else /* not ANSI_DECLARATORS */
vertex vertextraverse(m)
struct mesh *m;
#endif /* not ANSI_DECLARATORS */
{
vertex newvertex;
do {
newvertex = (vertex) traverse(&m->vertices);
if (newvertex == (vertex) NULL) {
return (vertex) NULL;
}
} while (vertextype(newvertex) == DEADVERTEX); /* Skip dead ones. */
return newvertex;
}
/*****************************************************************************/
/* */
/* badsubsegdealloc() Deallocate space for a bad subsegment, marking it */
/* dead. */
/* */
/*****************************************************************************/
#ifndef CDT_ONLY
#ifdef ANSI_DECLARATORS
void badsubsegdealloc(struct mesh *m, struct badsubseg *dyingseg)
#else /* not ANSI_DECLARATORS */
void badsubsegdealloc(m, dyingseg)
struct mesh *m;
struct badsubseg *dyingseg;
#endif /* not ANSI_DECLARATORS */
{
/* Set subsegment's origin to NULL. This makes it possible to detect dead */
/* badsubsegs when traversing the list of all badsubsegs . */
dyingseg->subsegorg = (vertex) NULL;
pooldealloc(&m->badsubsegs, (VOID *) dyingseg);
}
#endif /* not CDT_ONLY */
/*****************************************************************************/
/* */
/* badsubsegtraverse() Traverse the bad subsegments, skipping dead ones. */
/* */
/*****************************************************************************/
#ifndef CDT_ONLY
#ifdef ANSI_DECLARATORS
struct badsubseg *badsubsegtraverse(struct mesh *m)
#else /* not ANSI_DECLARATORS */
struct badsubseg *badsubsegtraverse(m)
struct mesh *m;
#endif /* not ANSI_DECLARATORS */
{
struct badsubseg *newseg;
do {
newseg = (struct badsubseg *) traverse(&m->badsubsegs);
if (newseg == (struct badsubseg *) NULL) {
return (struct badsubseg *) NULL;
}
} while (newseg->subsegorg == (vertex) NULL); /* Skip dead ones. */
return newseg;
}
#endif /* not CDT_ONLY */
/*****************************************************************************/
/* */
/* getvertex() Get a specific vertex, by number, from the list. */
/* */
/* The first vertex is number 'firstnumber'. */
/* */
/* Note that this takes O(n) time (with a small constant, if VERTEXPERBLOCK */
/* is large). I don't care to take the trouble to make it work in constant */
/* time. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
vertex getvertex(struct mesh *m, struct behavior *b, int number)
#else /* not ANSI_DECLARATORS */
vertex getvertex(m, b, number)
struct mesh *m;
struct behavior *b;
int number;
#endif /* not ANSI_DECLARATORS */
{
VOID **getblock;
char *foundvertex;
size_t alignptr;
int current;
getblock = m->vertices.firstblock;
current = b->firstnumber;
/* Find the right block. */
if (current + m->vertices.itemsfirstblock <= number) {
getblock = (VOID **) *getblock;
current += m->vertices.itemsfirstblock;
while (current + m->vertices.itemsperblock <= number) {
getblock = (VOID **) *getblock;
current += m->vertices.itemsperblock;
}
}
/* Now find the right vertex. */
alignptr = (size_t) (getblock + 1);
foundvertex = (char *) (alignptr + (size_t) m->vertices.alignbytes -
(alignptr % (size_t) m->vertices.alignbytes));
return (vertex) (foundvertex + m->vertices.itembytes * (number - current));
}
/*****************************************************************************/
/* */
/* triangledeinit() Free all remaining allocated memory. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
void triangledeinit(struct mesh *m, struct behavior *b)
#else /* not ANSI_DECLARATORS */
void triangledeinit(m, b)
struct mesh *m;
struct behavior *b;
#endif /* not ANSI_DECLARATORS */
{
pooldeinit(&m->triangles);
trifree((VOID *) m->dummytribase);
if (b->usesegments) {
pooldeinit(&m->subsegs);
trifree((VOID *) m->dummysubbase);
}
pooldeinit(&m->vertices);
#ifndef CDT_ONLY
if (b->quality) {
pooldeinit(&m->badsubsegs);
if ((b->minangle > 0.0) || b->vararea || b->fixedarea || b->usertest) {
pooldeinit(&m->badtriangles);
pooldeinit(&m->flipstackers);
}
}
#endif /* not CDT_ONLY */
exactdeinit();
}
/** **/
/** **/
/********* Memory management routines end here *********/
/********* Constructors begin here *********/
/** **/
/** **/
/*****************************************************************************/
/* */
/* maketriangle() Create a new triangle with orientation zero. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
void maketriangle(struct mesh *m, struct behavior *b, struct otri *newotri)
#else /* not ANSI_DECLARATORS */
void maketriangle(m, b, newotri)
struct mesh *m;
struct behavior *b;
struct otri *newotri;
#endif /* not ANSI_DECLARATORS */
{
int i;
newotri->tri = (triangle *) poolalloc(&m->triangles);
/* Initialize the three adjoining triangles to be "outer space". */
newotri->tri[0] = (triangle) m->dummytri;
newotri->tri[1] = (triangle) m->dummytri;
newotri->tri[2] = (triangle) m->dummytri;
/* Three NULL vertices. */
newotri->tri[3] = (triangle) NULL;
newotri->tri[4] = (triangle) NULL;
newotri->tri[5] = (triangle) NULL;
if (b->usesegments) {
/* Initialize the three adjoining subsegments to be the omnipresent */
/* subsegment. */
newotri->tri[6] = (triangle) m->dummysub;
newotri->tri[7] = (triangle) m->dummysub;
newotri->tri[8] = (triangle) m->dummysub;
}
for (i = 0; i < m->eextras; i++) {
setelemattribute(*newotri, i, 0.0);
}
if (b->vararea) {
setareabound(*newotri, -1.0);
}
newotri->orient = 0;
}
/*****************************************************************************/
/* */
/* makesubseg() Create a new subsegment with orientation zero. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
void makesubseg(struct mesh *m, struct osub *newsubseg)
#else /* not ANSI_DECLARATORS */
void makesubseg(m, newsubseg)
struct mesh *m;
struct osub *newsubseg;
#endif /* not ANSI_DECLARATORS */
{
newsubseg->ss = (subseg *) poolalloc(&m->subsegs);
/* Initialize the two adjoining subsegments to be the omnipresent */
/* subsegment. */
newsubseg->ss[0] = (subseg) m->dummysub;
newsubseg->ss[1] = (subseg) m->dummysub;
/* Four NULL vertices. */
newsubseg->ss[2] = (subseg) NULL;
newsubseg->ss[3] = (subseg) NULL;
newsubseg->ss[4] = (subseg) NULL;
newsubseg->ss[5] = (subseg) NULL;
/* Initialize the two adjoining triangles to be "outer space." */
newsubseg->ss[6] = (subseg) m->dummytri;
newsubseg->ss[7] = (subseg) m->dummytri;
/* Set the boundary marker to zero. */
setmark(*newsubseg, 0);
newsubseg->ssorient = 0;
}
/** **/
/** **/
/********* Constructors end here *********/
/********* Geometric primitives begin here *********/
/** **/
/** **/
/* The adaptive exact arithmetic geometric predicates implemented herein are */
/* described in detail in my paper, "Adaptive Precision Floating-Point */
/* Arithmetic and Fast Robust Geometric Predicates." See the header for a */
/* full citation. */
/* Which of the following two methods of finding the absolute values is */
/* fastest is compiler-dependent. A few compilers can inline and optimize */
/* the fabs() call; but most will incur the overhead of a function call, */
/* which is disastrously slow. A faster way on IEEE machines might be to */
/* mask the appropriate bit, but that's difficult to do in C without */
/* forcing the value to be stored to memory (rather than be kept in the */
/* register to which the optimizer assigned it). */
#define Absolute(a) ((a) >= 0.0 ? (a) : -(a))
/* #define Absolute(a) fabs(a) */
/* Many of the operations are broken up into two pieces, a main part that */
/* performs an approximate operation, and a "tail" that computes the */
/* roundoff error of that operation. */
/* */
/* The operations Fast_Two_Sum(), Fast_Two_Diff(), Two_Sum(), Two_Diff(), */
/* Split(), and Two_Product() are all implemented as described in the */
/* reference. Each of these macros requires certain variables to be */
/* defined in the calling routine. The variables `bvirt', `c', `abig', */
/* `_i', `_j', `_k', `_l', `_m', and `_n' are declared `INEXACT' because */
/* they store the result of an operation that may incur roundoff error. */
/* The input parameter `x' (or the highest numbered `x_' parameter) must */
/* also be declared `INEXACT'. */
#define Fast_Two_Sum_Tail(a, b, x, y) \
bvirt = x - a; \
y = b - bvirt
#define Fast_Two_Sum(a, b, x, y) \
x = (REAL) (a + b); \
Fast_Two_Sum_Tail(a, b, x, y)
#define Two_Sum_Tail(a, b, x, y) \
bvirt = (REAL) (x - a); \
avirt = x - bvirt; \
bround = b - bvirt; \
around = a - avirt; \
y = around + bround
#define Two_Sum(a, b, x, y) \
x = (REAL) (a + b); \
Two_Sum_Tail(a, b, x, y)
#define Two_Diff_Tail(a, b, x, y) \
bvirt = (REAL) (a - x); \
avirt = x + bvirt; \
bround = bvirt - b; \
around = a - avirt; \
y = around + bround
#define Two_Diff(a, b, x, y) \
x = (REAL) (a - b); \
Two_Diff_Tail(a, b, x, y)
#define Split(a, ahi, alo) \
c = (REAL) (splitter * a); \
abig = (REAL) (c - a); \
ahi = c - abig; \
alo = a - ahi
#define Two_Product_Tail(a, b, x, y) \
Split(a, ahi, alo); \
Split(b, bhi, blo); \
err1 = x - (ahi * bhi); \
err2 = err1 - (alo * bhi); \
err3 = err2 - (ahi * blo); \
y = (alo * blo) - err3
#define Two_Product(a, b, x, y) \
x = (REAL) (a * b); \
Two_Product_Tail(a, b, x, y)
/* Two_Product_Presplit() is Two_Product() where one of the inputs has */
/* already been split. Avoids redundant splitting. */
#define Two_Product_Presplit(a, b, bhi, blo, x, y) \
x = (REAL) (a * b); \
Split(a, ahi, alo); \
err1 = x - (ahi * bhi); \
err2 = err1 - (alo * bhi); \
err3 = err2 - (ahi * blo); \
y = (alo * blo) - err3
/* Square() can be done more quickly than Two_Product(). */
#define Square_Tail(a, x, y) \
Split(a, ahi, alo); \
err1 = x - (ahi * ahi); \
err3 = err1 - ((ahi + ahi) * alo); \
y = (alo * alo) - err3
#define Square(a, x, y) \
x = (REAL) (a * a); \
Square_Tail(a, x, y)
/* Macros for summing expansions of various fixed lengths. These are all */
/* unrolled versions of Expansion_Sum(). */
#define Two_One_Sum(a1, a0, b, x2, x1, x0) \
Two_Sum(a0, b , _i, x0); \
Two_Sum(a1, _i, x2, x1)
#define Two_One_Diff(a1, a0, b, x2, x1, x0) \
Two_Diff(a0, b , _i, x0); \
Two_Sum( a1, _i, x2, x1)
#define Two_Two_Sum(a1, a0, b1, b0, x3, x2, x1, x0) \
Two_One_Sum(a1, a0, b0, _j, _0, x0); \
Two_One_Sum(_j, _0, b1, x3, x2, x1)
#define Two_Two_Diff(a1, a0, b1, b0, x3, x2, x1, x0) \
Two_One_Diff(a1, a0, b0, _j, _0, x0); \
Two_One_Diff(_j, _0, b1, x3, x2, x1)
/* Macro for multiplying a two-component expansion by a single component. */
#define Two_One_Product(a1, a0, b, x3, x2, x1, x0) \
Split(b, bhi, blo); \
Two_Product_Presplit(a0, b, bhi, blo, _i, x0); \
Two_Product_Presplit(a1, b, bhi, blo, _j, _0); \
Two_Sum(_i, _0, _k, x1); \
Fast_Two_Sum(_j, _k, x3, x2)
/*****************************************************************************/
/* */
/* exactinit() Initialize the variables used for exact arithmetic. */
/* */
/* `epsilon' is the largest power of two such that 1.0 + epsilon = 1.0 in */
/* floating-point arithmetic. `epsilon' bounds the relative roundoff */
/* error. It is used for floating-point error analysis. */
/* */
/* `splitter' is used to split floating-point numbers into two half- */
/* length significands for exact multiplication. */
/* */
/* I imagine that a highly optimizing compiler might be too smart for its */
/* own good, and somehow cause this routine to fail, if it pretends that */
/* floating-point arithmetic is too much like real arithmetic. */
/* */
/* Don't change this routine unless you fully understand it. */
/* */
/*****************************************************************************/
static int previous_cword;
void exactinit(void)
{
REAL half;
REAL check, lastcheck;
int every_other;
#ifdef LINUX
int cword;
#endif /* LINUX */
#ifdef CPU86
#ifdef SINGLE
_control87(_PC_24, _MCW_PC); /* Set FPU control word for single precision. */
#else /* not SINGLE */
_control87(_PC_53, _MCW_PC); /* Set FPU control word for double precision. */
#endif /* not SINGLE */
#endif /* CPU86 */
#ifdef LINUX
_FPU_GETCW(previous_cword);
#ifdef SINGLE
/* cword = 4223; */
cword = 4210; /* set FPU control word for single precision */
#else /* not SINGLE */
/* cword = 4735; */
cword = 4722; /* set FPU control word for double precision */
#endif /* not SINGLE */
_FPU_SETCW(cword);
#endif /* LINUX */
every_other = 1;
half = 0.5;
epsilon = 1.0;
splitter = 1.0;
check = 1.0;
/* Repeatedly divide `epsilon' by two until it is too small to add to */
/* one without causing roundoff. (Also check if the sum is equal to */
/* the previous sum, for machines that round up instead of using exact */
/* rounding. Not that these routines will work on such machines.) */
do {
lastcheck = check;
epsilon *= half;
if (every_other) {
splitter *= 2.0;
}
every_other = !every_other;
check = 1.0 + epsilon;
} while ((check != 1.0) && (check != lastcheck));
splitter += 1.0;
/* Error bounds for orientation and incircle tests. */
resulterrbound = (3.0 + 8.0 * epsilon) * epsilon;
ccwerrboundA = (3.0 + 16.0 * epsilon) * epsilon;
ccwerrboundB = (2.0 + 12.0 * epsilon) * epsilon;
ccwerrboundC = (9.0 + 64.0 * epsilon) * epsilon * epsilon;
iccerrboundA = (10.0 + 96.0 * epsilon) * epsilon;
iccerrboundB = (4.0 + 48.0 * epsilon) * epsilon;
iccerrboundC = (44.0 + 576.0 * epsilon) * epsilon * epsilon;
o3derrboundA = (7.0 + 56.0 * epsilon) * epsilon;
o3derrboundB = (3.0 + 28.0 * epsilon) * epsilon;
o3derrboundC = (26.0 + 288.0 * epsilon) * epsilon * epsilon;
}
void exactdeinit()
{
#ifdef LINUX
_FPU_SETCW(previous_cword);
#endif /* LINUX */
}
/*****************************************************************************/
/* */
/* fast_expansion_sum_zeroelim() Sum two expansions, eliminating zero */
/* components from the output expansion. */
/* */
/* Sets h = e + f. See my Robust Predicates paper for details. */
/* */
/* If round-to-even is used (as with IEEE 754), maintains the strongly */
/* nonoverlapping property. (That is, if e is strongly nonoverlapping, h */
/* will be also.) Does NOT maintain the nonoverlapping or nonadjacent */
/* properties. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
int fast_expansion_sum_zeroelim(int elen, REAL *e, int flen, REAL *f, REAL *h)
#else /* not ANSI_DECLARATORS */
int fast_expansion_sum_zeroelim(elen, e, flen, f, h) /* h cannot be e or f. */
int elen;
REAL *e;
int flen;
REAL *f;
REAL *h;
#endif /* not ANSI_DECLARATORS */
{
REAL Q;
INEXACT REAL Qnew;
INEXACT REAL hh;
INEXACT REAL bvirt;
REAL avirt, bround, around;
int eindex, findex, hindex;
REAL enow, fnow;
enow = e[0];
fnow = f[0];
eindex = findex = 0;
if ((fnow > enow) == (fnow > -enow)) {
Q = enow;
enow = e[++eindex];
} else {
Q = fnow;
fnow = f[++findex];
}
hindex = 0;
if ((eindex < elen) && (findex < flen)) {
if ((fnow > enow) == (fnow > -enow)) {
Fast_Two_Sum(enow, Q, Qnew, hh);
enow = e[++eindex];
} else {
Fast_Two_Sum(fnow, Q, Qnew, hh);
fnow = f[++findex];
}
Q = Qnew;
if (hh != 0.0) {
h[hindex++] = hh;
}
while ((eindex < elen) && (findex < flen)) {
if ((fnow > enow) == (fnow > -enow)) {
Two_Sum(Q, enow, Qnew, hh);
enow = e[++eindex];
} else {
Two_Sum(Q, fnow, Qnew, hh);
fnow = f[++findex];
}
Q = Qnew;
if (hh != 0.0) {
h[hindex++] = hh;
}
}
}
while (eindex < elen) {
Two_Sum(Q, enow, Qnew, hh);
enow = e[++eindex];
Q = Qnew;
if (hh != 0.0) {
h[hindex++] = hh;
}
}
while (findex < flen) {
Two_Sum(Q, fnow, Qnew, hh);
fnow = f[++findex];
Q = Qnew;
if (hh != 0.0) {
h[hindex++] = hh;
}
}
if ((Q != 0.0) || (hindex == 0)) {
h[hindex++] = Q;
}
return hindex;
}
/*****************************************************************************/
/* */
/* scale_expansion_zeroelim() Multiply an expansion by a scalar, */
/* eliminating zero components from the */
/* output expansion. */
/* */
/* Sets h = be. See my Robust Predicates paper for details. */
/* */
/* Maintains the nonoverlapping property. If round-to-even is used (as */
/* with IEEE 754), maintains the strongly nonoverlapping and nonadjacent */
/* properties as well. (That is, if e has one of these properties, so */
/* will h.) */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
int scale_expansion_zeroelim(int elen, REAL *e, REAL b, REAL *h)
#else /* not ANSI_DECLARATORS */
int scale_expansion_zeroelim(elen, e, b, h) /* e and h cannot be the same. */
int elen;
REAL *e;
REAL b;
REAL *h;
#endif /* not ANSI_DECLARATORS */
{
INEXACT REAL Q, sum;
REAL hh;
INEXACT REAL product1;
REAL product0;
int eindex, hindex;
REAL enow;
INEXACT REAL bvirt;
REAL avirt, bround, around;
INEXACT REAL c;
INEXACT REAL abig;
REAL ahi, alo, bhi, blo;
REAL err1, err2, err3;
Split(b, bhi, blo);
Two_Product_Presplit(e[0], b, bhi, blo, Q, hh);
hindex = 0;
if (hh != 0) {
h[hindex++] = hh;
}
for (eindex = 1; eindex < elen; eindex++) {
enow = e[eindex];
Two_Product_Presplit(enow, b, bhi, blo, product1, product0);
Two_Sum(Q, product0, sum, hh);
if (hh != 0) {
h[hindex++] = hh;
}
Fast_Two_Sum(product1, sum, Q, hh);
if (hh != 0) {
h[hindex++] = hh;
}
}
if ((Q != 0.0) || (hindex == 0)) {
h[hindex++] = Q;
}
return hindex;
}
/*****************************************************************************/
/* */
/* estimate() Produce a one-word estimate of an expansion's value. */
/* */
/* See my Robust Predicates paper for details. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
REAL estimate(int elen, REAL *e)
#else /* not ANSI_DECLARATORS */
REAL estimate(elen, e)
int elen;
REAL *e;
#endif /* not ANSI_DECLARATORS */
{
REAL Q;
int eindex;
Q = e[0];
for (eindex = 1; eindex < elen; eindex++) {
Q += e[eindex];
}
return Q;
}
/*****************************************************************************/
/* */
/* counterclockwise() Return a positive value if the points pa, pb, and */
/* pc occur in counterclockwise order; a negative */
/* value if they occur in clockwise order; and zero */
/* if they are collinear. The result is also a rough */
/* approximation of twice the signed area of the */
/* triangle defined by the three points. */
/* */
/* Uses exact arithmetic if necessary to ensure a correct answer. The */
/* result returned is the determinant of a matrix. This determinant is */
/* computed adaptively, in the sense that exact arithmetic is used only to */
/* the degree it is needed to ensure that the returned value has the */
/* correct sign. Hence, this function is usually quite fast, but will run */
/* more slowly when the input points are collinear or nearly so. */
/* */
/* See my Robust Predicates paper for details. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
REAL counterclockwiseadapt(vertex pa, vertex pb, vertex pc, REAL detsum)
#else /* not ANSI_DECLARATORS */
REAL counterclockwiseadapt(pa, pb, pc, detsum)
vertex pa;
vertex pb;
vertex pc;
REAL detsum;
#endif /* not ANSI_DECLARATORS */
{
INEXACT REAL acx, acy, bcx, bcy;
REAL acxtail, acytail, bcxtail, bcytail;
INEXACT REAL detleft, detright;
REAL detlefttail, detrighttail;
REAL det, errbound;
REAL B[4], C1[8], C2[12], D[16];
INEXACT REAL B3;
int C1length, C2length, Dlength;
REAL u[4];
INEXACT REAL u3;
INEXACT REAL s1, t1;
REAL s0, t0;
INEXACT REAL bvirt;
REAL avirt, bround, around;
INEXACT REAL c;
INEXACT REAL abig;
REAL ahi, alo, bhi, blo;
REAL err1, err2, err3;
INEXACT REAL _i, _j;
REAL _0;
acx = (REAL) (pa[0] - pc[0]);
bcx = (REAL) (pb[0] - pc[0]);
acy = (REAL) (pa[1] - pc[1]);
bcy = (REAL) (pb[1] - pc[1]);
Two_Product(acx, bcy, detleft, detlefttail);
Two_Product(acy, bcx, detright, detrighttail);
Two_Two_Diff(detleft, detlefttail, detright, detrighttail,
B3, B[2], B[1], B[0]);
B[3] = B3;
det = estimate(4, B);
errbound = ccwerrboundB * detsum;
if ((det >= errbound) || (-det >= errbound)) {
return det;
}
Two_Diff_Tail(pa[0], pc[0], acx, acxtail);
Two_Diff_Tail(pb[0], pc[0], bcx, bcxtail);
Two_Diff_Tail(pa[1], pc[1], acy, acytail);
Two_Diff_Tail(pb[1], pc[1], bcy, bcytail);
if ((acxtail == 0.0) && (acytail == 0.0)
&& (bcxtail == 0.0) && (bcytail == 0.0)) {
return det;
}
errbound = ccwerrboundC * detsum + resulterrbound * Absolute(det);
det += (acx * bcytail + bcy * acxtail)
- (acy * bcxtail + bcx * acytail);
if ((det >= errbound) || (-det >= errbound)) {
return det;
}
Two_Product(acxtail, bcy, s1, s0);
Two_Product(acytail, bcx, t1, t0);
Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]);
u[3] = u3;
C1length = fast_expansion_sum_zeroelim(4, B, 4, u, C1);
Two_Product(acx, bcytail, s1, s0);
Two_Product(acy, bcxtail, t1, t0);
Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]);
u[3] = u3;
C2length = fast_expansion_sum_zeroelim(C1length, C1, 4, u, C2);
Two_Product(acxtail, bcytail, s1, s0);
Two_Product(acytail, bcxtail, t1, t0);
Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]);
u[3] = u3;
Dlength = fast_expansion_sum_zeroelim(C2length, C2, 4, u, D);
return(D[Dlength - 1]);
}
#ifdef ANSI_DECLARATORS
REAL counterclockwise(struct mesh *m, struct behavior *b,
vertex pa, vertex pb, vertex pc)
#else /* not ANSI_DECLARATORS */
REAL counterclockwise(m, b, pa, pb, pc)
struct mesh *m;
struct behavior *b;
vertex pa;
vertex pb;
vertex pc;
#endif /* not ANSI_DECLARATORS */
{
REAL detleft, detright, det;
REAL detsum, errbound;
m->counterclockcount++;
detleft = (pa[0] - pc[0]) * (pb[1] - pc[1]);
detright = (pa[1] - pc[1]) * (pb[0] - pc[0]);
det = detleft - detright;
if (b->noexact) {
return det;
}
if (detleft > 0.0) {
if (detright <= 0.0) {
return det;
} else {
detsum = detleft + detright;
}
} else if (detleft < 0.0) {
if (detright >= 0.0) {
return det;
} else {
detsum = -detleft - detright;
}
} else {
return det;
}
errbound = ccwerrboundA * detsum;
if ((det >= errbound) || (-det >= errbound)) {
return det;
}
return counterclockwiseadapt(pa, pb, pc, detsum);
}
/*****************************************************************************/
/* */
/* incircle() Return a positive value if the point pd lies inside the */
/* circle passing through pa, pb, and pc; a negative value if */
/* it lies outside; and zero if the four points are cocircular.*/
/* The points pa, pb, and pc must be in counterclockwise */
/* order, or the sign of the result will be reversed. */
/* */
/* Uses exact arithmetic if necessary to ensure a correct answer. The */
/* result returned is the determinant of a matrix. This determinant is */
/* computed adaptively, in the sense that exact arithmetic is used only to */
/* the degree it is needed to ensure that the returned value has the */
/* correct sign. Hence, this function is usually quite fast, but will run */
/* more slowly when the input points are cocircular or nearly so. */
/* */
/* See my Robust Predicates paper for details. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
REAL incircleadapt(vertex pa, vertex pb, vertex pc, vertex pd, REAL permanent)
#else /* not ANSI_DECLARATORS */
REAL incircleadapt(pa, pb, pc, pd, permanent)
vertex pa;
vertex pb;
vertex pc;
vertex pd;
REAL permanent;
#endif /* not ANSI_DECLARATORS */
{
INEXACT REAL adx, bdx, cdx, ady, bdy, cdy;
REAL det, errbound;
INEXACT REAL bdxcdy1, cdxbdy1, cdxady1, adxcdy1, adxbdy1, bdxady1;
REAL bdxcdy0, cdxbdy0, cdxady0, adxcdy0, adxbdy0, bdxady0;
REAL bc[4], ca[4], ab[4];
INEXACT REAL bc3, ca3, ab3;
REAL axbc[8], axxbc[16], aybc[8], ayybc[16], adet[32];
int axbclen, axxbclen, aybclen, ayybclen, alen;
REAL bxca[8], bxxca[16], byca[8], byyca[16], bdet[32];
int bxcalen, bxxcalen, bycalen, byycalen, blen;
REAL cxab[8], cxxab[16], cyab[8], cyyab[16], cdet[32];
int cxablen, cxxablen, cyablen, cyyablen, clen;
REAL abdet[64];
int ablen;
REAL fin1[1152], fin2[1152];
REAL *finnow, *finother, *finswap;
int finlength;
REAL adxtail, bdxtail, cdxtail, adytail, bdytail, cdytail;
INEXACT REAL adxadx1, adyady1, bdxbdx1, bdybdy1, cdxcdx1, cdycdy1;
REAL adxadx0, adyady0, bdxbdx0, bdybdy0, cdxcdx0, cdycdy0;
REAL aa[4], bb[4], cc[4];
INEXACT REAL aa3, bb3, cc3;
INEXACT REAL ti1, tj1;
REAL ti0, tj0;
REAL u[4], v[4];
INEXACT REAL u3, v3;
REAL temp8[8], temp16a[16], temp16b[16], temp16c[16];
REAL temp32a[32], temp32b[32], temp48[48], temp64[64];
int temp8len, temp16alen, temp16blen, temp16clen;
int temp32alen, temp32blen, temp48len, temp64len;
REAL axtbb[8], axtcc[8], aytbb[8], aytcc[8];
int axtbblen, axtcclen, aytbblen, aytcclen;
REAL bxtaa[8], bxtcc[8], bytaa[8], bytcc[8];
int bxtaalen, bxtcclen, bytaalen, bytcclen;
REAL cxtaa[8], cxtbb[8], cytaa[8], cytbb[8];
int cxtaalen, cxtbblen, cytaalen, cytbblen;
REAL axtbc[8], aytbc[8], bxtca[8], bytca[8], cxtab[8], cytab[8];
int axtbclen, aytbclen, bxtcalen, bytcalen, cxtablen, cytablen;
REAL axtbct[16], aytbct[16], bxtcat[16], bytcat[16], cxtabt[16], cytabt[16];
int axtbctlen, aytbctlen, bxtcatlen, bytcatlen, cxtabtlen, cytabtlen;
REAL axtbctt[8], aytbctt[8], bxtcatt[8];
REAL bytcatt[8], cxtabtt[8], cytabtt[8];
int axtbcttlen, aytbcttlen, bxtcattlen, bytcattlen, cxtabttlen, cytabttlen;
REAL abt[8], bct[8], cat[8];
int abtlen, bctlen, catlen;
REAL abtt[4], bctt[4], catt[4];
int abttlen, bcttlen, cattlen;
INEXACT REAL abtt3, bctt3, catt3;
REAL negate;
INEXACT REAL bvirt;
REAL avirt, bround, around;
INEXACT REAL c;
INEXACT REAL abig;
REAL ahi, alo, bhi, blo;
REAL err1, err2, err3;
INEXACT REAL _i, _j;
REAL _0;
adx = (REAL) (pa[0] - pd[0]);
bdx = (REAL) (pb[0] - pd[0]);
cdx = (REAL) (pc[0] - pd[0]);
ady = (REAL) (pa[1] - pd[1]);
bdy = (REAL) (pb[1] - pd[1]);
cdy = (REAL) (pc[1] - pd[1]);
Two_Product(bdx, cdy, bdxcdy1, bdxcdy0);
Two_Product(cdx, bdy, cdxbdy1, cdxbdy0);
Two_Two_Diff(bdxcdy1, bdxcdy0, cdxbdy1, cdxbdy0, bc3, bc[2], bc[1], bc[0]);
bc[3] = bc3;
axbclen = scale_expansion_zeroelim(4, bc, adx, axbc);
axxbclen = scale_expansion_zeroelim(axbclen, axbc, adx, axxbc);
aybclen = scale_expansion_zeroelim(4, bc, ady, aybc);
ayybclen = scale_expansion_zeroelim(aybclen, aybc, ady, ayybc);
alen = fast_expansion_sum_zeroelim(axxbclen, axxbc, ayybclen, ayybc, adet);
Two_Product(cdx, ady, cdxady1, cdxady0);
Two_Product(adx, cdy, adxcdy1, adxcdy0);
Two_Two_Diff(cdxady1, cdxady0, adxcdy1, adxcdy0, ca3, ca[2], ca[1], ca[0]);
ca[3] = ca3;
bxcalen = scale_expansion_zeroelim(4, ca, bdx, bxca);
bxxcalen = scale_expansion_zeroelim(bxcalen, bxca, bdx, bxxca);
bycalen = scale_expansion_zeroelim(4, ca, bdy, byca);
byycalen = scale_expansion_zeroelim(bycalen, byca, bdy, byyca);
blen = fast_expansion_sum_zeroelim(bxxcalen, bxxca, byycalen, byyca, bdet);
Two_Product(adx, bdy, adxbdy1, adxbdy0);
Two_Product(bdx, ady, bdxady1, bdxady0);
Two_Two_Diff(adxbdy1, adxbdy0, bdxady1, bdxady0, ab3, ab[2], ab[1], ab[0]);
ab[3] = ab3;
cxablen = scale_expansion_zeroelim(4, ab, cdx, cxab);
cxxablen = scale_expansion_zeroelim(cxablen, cxab, cdx, cxxab);
cyablen = scale_expansion_zeroelim(4, ab, cdy, cyab);
cyyablen = scale_expansion_zeroelim(cyablen, cyab, cdy, cyyab);
clen = fast_expansion_sum_zeroelim(cxxablen, cxxab, cyyablen, cyyab, cdet);
ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
finlength = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, fin1);
det = estimate(finlength, fin1);
errbound = iccerrboundB * permanent;
if ((det >= errbound) || (-det >= errbound)) {
return det;
}
Two_Diff_Tail(pa[0], pd[0], adx, adxtail);
Two_Diff_Tail(pa[1], pd[1], ady, adytail);
Two_Diff_Tail(pb[0], pd[0], bdx, bdxtail);
Two_Diff_Tail(pb[1], pd[1], bdy, bdytail);
Two_Diff_Tail(pc[0], pd[0], cdx, cdxtail);
Two_Diff_Tail(pc[1], pd[1], cdy, cdytail);
if ((adxtail == 0.0) && (bdxtail == 0.0) && (cdxtail == 0.0)
&& (adytail == 0.0) && (bdytail == 0.0) && (cdytail == 0.0)) {
return det;
}
errbound = iccerrboundC * permanent + resulterrbound * Absolute(det);
det += ((adx * adx + ady * ady) * ((bdx * cdytail + cdy * bdxtail)
- (bdy * cdxtail + cdx * bdytail))
+ 2.0 * (adx * adxtail + ady * adytail) * (bdx * cdy - bdy * cdx))
+ ((bdx * bdx + bdy * bdy) * ((cdx * adytail + ady * cdxtail)
- (cdy * adxtail + adx * cdytail))
+ 2.0 * (bdx * bdxtail + bdy * bdytail) * (cdx * ady - cdy * adx))
+ ((cdx * cdx + cdy * cdy) * ((adx * bdytail + bdy * adxtail)
- (ady * bdxtail + bdx * adytail))
+ 2.0 * (cdx * cdxtail + cdy * cdytail) * (adx * bdy - ady * bdx));
if ((det >= errbound) || (-det >= errbound)) {
return det;
}
finnow = fin1;
finother = fin2;
if ((bdxtail != 0.0) || (bdytail != 0.0)
|| (cdxtail != 0.0) || (cdytail != 0.0)) {
Square(adx, adxadx1, adxadx0);
Square(ady, adyady1, adyady0);
Two_Two_Sum(adxadx1, adxadx0, adyady1, adyady0, aa3, aa[2], aa[1], aa[0]);
aa[3] = aa3;
}
if ((cdxtail != 0.0) || (cdytail != 0.0)
|| (adxtail != 0.0) || (adytail != 0.0)) {
Square(bdx, bdxbdx1, bdxbdx0);
Square(bdy, bdybdy1, bdybdy0);
Two_Two_Sum(bdxbdx1, bdxbdx0, bdybdy1, bdybdy0, bb3, bb[2], bb[1], bb[0]);
bb[3] = bb3;
}
if ((adxtail != 0.0) || (adytail != 0.0)
|| (bdxtail != 0.0) || (bdytail != 0.0)) {
Square(cdx, cdxcdx1, cdxcdx0);
Square(cdy, cdycdy1, cdycdy0);
Two_Two_Sum(cdxcdx1, cdxcdx0, cdycdy1, cdycdy0, cc3, cc[2], cc[1], cc[0]);
cc[3] = cc3;
}
if (adxtail != 0.0) {
axtbclen = scale_expansion_zeroelim(4, bc, adxtail, axtbc);
temp16alen = scale_expansion_zeroelim(axtbclen, axtbc, 2.0 * adx,
temp16a);
axtcclen = scale_expansion_zeroelim(4, cc, adxtail, axtcc);
temp16blen = scale_expansion_zeroelim(axtcclen, axtcc, bdy, temp16b);
axtbblen = scale_expansion_zeroelim(4, bb, adxtail, axtbb);
temp16clen = scale_expansion_zeroelim(axtbblen, axtbb, -cdy, temp16c);
temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
temp16blen, temp16b, temp32a);
temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c,
temp32alen, temp32a, temp48);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
temp48, finother);
finswap = finnow; finnow = finother; finother = finswap;
}
if (adytail != 0.0) {
aytbclen = scale_expansion_zeroelim(4, bc, adytail, aytbc);
temp16alen = scale_expansion_zeroelim(aytbclen, aytbc, 2.0 * ady,
temp16a);
aytbblen = scale_expansion_zeroelim(4, bb, adytail, aytbb);
temp16blen = scale_expansion_zeroelim(aytbblen, aytbb, cdx, temp16b);
aytcclen = scale_expansion_zeroelim(4, cc, adytail, aytcc);
temp16clen = scale_expansion_zeroelim(aytcclen, aytcc, -bdx, temp16c);
temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
temp16blen, temp16b, temp32a);
temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c,
temp32alen, temp32a, temp48);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
temp48, finother);
finswap = finnow; finnow = finother; finother = finswap;
}
if (bdxtail != 0.0) {
bxtcalen = scale_expansion_zeroelim(4, ca, bdxtail, bxtca);
temp16alen = scale_expansion_zeroelim(bxtcalen, bxtca, 2.0 * bdx,
temp16a);
bxtaalen = scale_expansion_zeroelim(4, aa, bdxtail, bxtaa);
temp16blen = scale_expansion_zeroelim(bxtaalen, bxtaa, cdy, temp16b);
bxtcclen = scale_expansion_zeroelim(4, cc, bdxtail, bxtcc);
temp16clen = scale_expansion_zeroelim(bxtcclen, bxtcc, -ady, temp16c);
temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
temp16blen, temp16b, temp32a);
temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c,
temp32alen, temp32a, temp48);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
temp48, finother);
finswap = finnow; finnow = finother; finother = finswap;
}
if (bdytail != 0.0) {
bytcalen = scale_expansion_zeroelim(4, ca, bdytail, bytca);
temp16alen = scale_expansion_zeroelim(bytcalen, bytca, 2.0 * bdy,
temp16a);
bytcclen = scale_expansion_zeroelim(4, cc, bdytail, bytcc);
temp16blen = scale_expansion_zeroelim(bytcclen, bytcc, adx, temp16b);
bytaalen = scale_expansion_zeroelim(4, aa, bdytail, bytaa);
temp16clen = scale_expansion_zeroelim(bytaalen, bytaa, -cdx, temp16c);
temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
temp16blen, temp16b, temp32a);
temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c,
temp32alen, temp32a, temp48);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
temp48, finother);
finswap = finnow; finnow = finother; finother = finswap;
}
if (cdxtail != 0.0) {
cxtablen = scale_expansion_zeroelim(4, ab, cdxtail, cxtab);
temp16alen = scale_expansion_zeroelim(cxtablen, cxtab, 2.0 * cdx,
temp16a);
cxtbblen = scale_expansion_zeroelim(4, bb, cdxtail, cxtbb);
temp16blen = scale_expansion_zeroelim(cxtbblen, cxtbb, ady, temp16b);
cxtaalen = scale_expansion_zeroelim(4, aa, cdxtail, cxtaa);
temp16clen = scale_expansion_zeroelim(cxtaalen, cxtaa, -bdy, temp16c);
temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
temp16blen, temp16b, temp32a);
temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c,
temp32alen, temp32a, temp48);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
temp48, finother);
finswap = finnow; finnow = finother; finother = finswap;
}
if (cdytail != 0.0) {
cytablen = scale_expansion_zeroelim(4, ab, cdytail, cytab);
temp16alen = scale_expansion_zeroelim(cytablen, cytab, 2.0 * cdy,
temp16a);
cytaalen = scale_expansion_zeroelim(4, aa, cdytail, cytaa);
temp16blen = scale_expansion_zeroelim(cytaalen, cytaa, bdx, temp16b);
cytbblen = scale_expansion_zeroelim(4, bb, cdytail, cytbb);
temp16clen = scale_expansion_zeroelim(cytbblen, cytbb, -adx, temp16c);
temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
temp16blen, temp16b, temp32a);
temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c,
temp32alen, temp32a, temp48);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
temp48, finother);
finswap = finnow; finnow = finother; finother = finswap;
}
if ((adxtail != 0.0) || (adytail != 0.0)) {
if ((bdxtail != 0.0) || (bdytail != 0.0)
|| (cdxtail != 0.0) || (cdytail != 0.0)) {
Two_Product(bdxtail, cdy, ti1, ti0);
Two_Product(bdx, cdytail, tj1, tj0);
Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]);
u[3] = u3;
negate = -bdy;
Two_Product(cdxtail, negate, ti1, ti0);
negate = -bdytail;
Two_Product(cdx, negate, tj1, tj0);
Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]);
v[3] = v3;
bctlen = fast_expansion_sum_zeroelim(4, u, 4, v, bct);
Two_Product(bdxtail, cdytail, ti1, ti0);
Two_Product(cdxtail, bdytail, tj1, tj0);
Two_Two_Diff(ti1, ti0, tj1, tj0, bctt3, bctt[2], bctt[1], bctt[0]);
bctt[3] = bctt3;
bcttlen = 4;
} else {
bct[0] = 0.0;
bctlen = 1;
bctt[0] = 0.0;
bcttlen = 1;
}
if (adxtail != 0.0) {
temp16alen = scale_expansion_zeroelim(axtbclen, axtbc, adxtail, temp16a);
axtbctlen = scale_expansion_zeroelim(bctlen, bct, adxtail, axtbct);
temp32alen = scale_expansion_zeroelim(axtbctlen, axtbct, 2.0 * adx,
temp32a);
temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a,
temp32alen, temp32a, temp48);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
temp48, finother);
finswap = finnow; finnow = finother; finother = finswap;
if (bdytail != 0.0) {
temp8len = scale_expansion_zeroelim(4, cc, adxtail, temp8);
temp16alen = scale_expansion_zeroelim(temp8len, temp8, bdytail,
temp16a);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen,
temp16a, finother);
finswap = finnow; finnow = finother; finother = finswap;
}
if (cdytail != 0.0) {
temp8len = scale_expansion_zeroelim(4, bb, -adxtail, temp8);
temp16alen = scale_expansion_zeroelim(temp8len, temp8, cdytail,
temp16a);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen,
temp16a, finother);
finswap = finnow; finnow = finother; finother = finswap;
}
temp32alen = scale_expansion_zeroelim(axtbctlen, axtbct, adxtail,
temp32a);
axtbcttlen = scale_expansion_zeroelim(bcttlen, bctt, adxtail, axtbctt);
temp16alen = scale_expansion_zeroelim(axtbcttlen, axtbctt, 2.0 * adx,
temp16a);
temp16blen = scale_expansion_zeroelim(axtbcttlen, axtbctt, adxtail,
temp16b);
temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
temp16blen, temp16b, temp32b);
temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a,
temp32blen, temp32b, temp64);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len,
temp64, finother);
finswap = finnow; finnow = finother; finother = finswap;
}
if (adytail != 0.0) {
temp16alen = scale_expansion_zeroelim(aytbclen, aytbc, adytail, temp16a);
aytbctlen = scale_expansion_zeroelim(bctlen, bct, adytail, aytbct);
temp32alen = scale_expansion_zeroelim(aytbctlen, aytbct, 2.0 * ady,
temp32a);
temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a,
temp32alen, temp32a, temp48);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
temp48, finother);
finswap = finnow; finnow = finother; finother = finswap;
temp32alen = scale_expansion_zeroelim(aytbctlen, aytbct, adytail,
temp32a);
aytbcttlen = scale_expansion_zeroelim(bcttlen, bctt, adytail, aytbctt);
temp16alen = scale_expansion_zeroelim(aytbcttlen, aytbctt, 2.0 * ady,
temp16a);
temp16blen = scale_expansion_zeroelim(aytbcttlen, aytbctt, adytail,
temp16b);
temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
temp16blen, temp16b, temp32b);
temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a,
temp32blen, temp32b, temp64);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len,
temp64, finother);
finswap = finnow; finnow = finother; finother = finswap;
}
}
if ((bdxtail != 0.0) || (bdytail != 0.0)) {
if ((cdxtail != 0.0) || (cdytail != 0.0)
|| (adxtail != 0.0) || (adytail != 0.0)) {
Two_Product(cdxtail, ady, ti1, ti0);
Two_Product(cdx, adytail, tj1, tj0);
Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]);
u[3] = u3;
negate = -cdy;
Two_Product(adxtail, negate, ti1, ti0);
negate = -cdytail;
Two_Product(adx, negate, tj1, tj0);
Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]);
v[3] = v3;
catlen = fast_expansion_sum_zeroelim(4, u, 4, v, cat);
Two_Product(cdxtail, adytail, ti1, ti0);
Two_Product(adxtail, cdytail, tj1, tj0);
Two_Two_Diff(ti1, ti0, tj1, tj0, catt3, catt[2], catt[1], catt[0]);
catt[3] = catt3;
cattlen = 4;
} else {
cat[0] = 0.0;
catlen = 1;
catt[0] = 0.0;
cattlen = 1;
}
if (bdxtail != 0.0) {
temp16alen = scale_expansion_zeroelim(bxtcalen, bxtca, bdxtail, temp16a);
bxtcatlen = scale_expansion_zeroelim(catlen, cat, bdxtail, bxtcat);
temp32alen = scale_expansion_zeroelim(bxtcatlen, bxtcat, 2.0 * bdx,
temp32a);
temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a,
temp32alen, temp32a, temp48);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
temp48, finother);
finswap = finnow; finnow = finother; finother = finswap;
if (cdytail != 0.0) {
temp8len = scale_expansion_zeroelim(4, aa, bdxtail, temp8);
temp16alen = scale_expansion_zeroelim(temp8len, temp8, cdytail,
temp16a);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen,
temp16a, finother);
finswap = finnow; finnow = finother; finother = finswap;
}
if (adytail != 0.0) {
temp8len = scale_expansion_zeroelim(4, cc, -bdxtail, temp8);
temp16alen = scale_expansion_zeroelim(temp8len, temp8, adytail,
temp16a);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen,
temp16a, finother);
finswap = finnow; finnow = finother; finother = finswap;
}
temp32alen = scale_expansion_zeroelim(bxtcatlen, bxtcat, bdxtail,
temp32a);
bxtcattlen = scale_expansion_zeroelim(cattlen, catt, bdxtail, bxtcatt);
temp16alen = scale_expansion_zeroelim(bxtcattlen, bxtcatt, 2.0 * bdx,
temp16a);
temp16blen = scale_expansion_zeroelim(bxtcattlen, bxtcatt, bdxtail,
temp16b);
temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
temp16blen, temp16b, temp32b);
temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a,
temp32blen, temp32b, temp64);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len,
temp64, finother);
finswap = finnow; finnow = finother; finother = finswap;
}
if (bdytail != 0.0) {
temp16alen = scale_expansion_zeroelim(bytcalen, bytca, bdytail, temp16a);
bytcatlen = scale_expansion_zeroelim(catlen, cat, bdytail, bytcat);
temp32alen = scale_expansion_zeroelim(bytcatlen, bytcat, 2.0 * bdy,
temp32a);
temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a,
temp32alen, temp32a, temp48);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
temp48, finother);
finswap = finnow; finnow = finother; finother = finswap;
temp32alen = scale_expansion_zeroelim(bytcatlen, bytcat, bdytail,
temp32a);
bytcattlen = scale_expansion_zeroelim(cattlen, catt, bdytail, bytcatt);
temp16alen = scale_expansion_zeroelim(bytcattlen, bytcatt, 2.0 * bdy,
temp16a);
temp16blen = scale_expansion_zeroelim(bytcattlen, bytcatt, bdytail,
temp16b);
temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
temp16blen, temp16b, temp32b);
temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a,
temp32blen, temp32b, temp64);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len,
temp64, finother);
finswap = finnow; finnow = finother; finother = finswap;
}
}
if ((cdxtail != 0.0) || (cdytail != 0.0)) {
if ((adxtail != 0.0) || (adytail != 0.0)
|| (bdxtail != 0.0) || (bdytail != 0.0)) {
Two_Product(adxtail, bdy, ti1, ti0);
Two_Product(adx, bdytail, tj1, tj0);
Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]);
u[3] = u3;
negate = -ady;
Two_Product(bdxtail, negate, ti1, ti0);
negate = -adytail;
Two_Product(bdx, negate, tj1, tj0);
Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]);
v[3] = v3;
abtlen = fast_expansion_sum_zeroelim(4, u, 4, v, abt);
Two_Product(adxtail, bdytail, ti1, ti0);
Two_Product(bdxtail, adytail, tj1, tj0);
Two_Two_Diff(ti1, ti0, tj1, tj0, abtt3, abtt[2], abtt[1], abtt[0]);
abtt[3] = abtt3;
abttlen = 4;
} else {
abt[0] = 0.0;
abtlen = 1;
abtt[0] = 0.0;
abttlen = 1;
}
if (cdxtail != 0.0) {
temp16alen = scale_expansion_zeroelim(cxtablen, cxtab, cdxtail, temp16a);
cxtabtlen = scale_expansion_zeroelim(abtlen, abt, cdxtail, cxtabt);
temp32alen = scale_expansion_zeroelim(cxtabtlen, cxtabt, 2.0 * cdx,
temp32a);
temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a,
temp32alen, temp32a, temp48);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
temp48, finother);
finswap = finnow; finnow = finother; finother = finswap;
if (adytail != 0.0) {
temp8len = scale_expansion_zeroelim(4, bb, cdxtail, temp8);
temp16alen = scale_expansion_zeroelim(temp8len, temp8, adytail,
temp16a);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen,
temp16a, finother);
finswap = finnow; finnow = finother; finother = finswap;
}
if (bdytail != 0.0) {
temp8len = scale_expansion_zeroelim(4, aa, -cdxtail, temp8);
temp16alen = scale_expansion_zeroelim(temp8len, temp8, bdytail,
temp16a);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen,
temp16a, finother);
finswap = finnow; finnow = finother; finother = finswap;
}
temp32alen = scale_expansion_zeroelim(cxtabtlen, cxtabt, cdxtail,
temp32a);
cxtabttlen = scale_expansion_zeroelim(abttlen, abtt, cdxtail, cxtabtt);
temp16alen = scale_expansion_zeroelim(cxtabttlen, cxtabtt, 2.0 * cdx,
temp16a);
temp16blen = scale_expansion_zeroelim(cxtabttlen, cxtabtt, cdxtail,
temp16b);
temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
temp16blen, temp16b, temp32b);
temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a,
temp32blen, temp32b, temp64);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len,
temp64, finother);
finswap = finnow; finnow = finother; finother = finswap;
}
if (cdytail != 0.0) {
temp16alen = scale_expansion_zeroelim(cytablen, cytab, cdytail, temp16a);
cytabtlen = scale_expansion_zeroelim(abtlen, abt, cdytail, cytabt);
temp32alen = scale_expansion_zeroelim(cytabtlen, cytabt, 2.0 * cdy,
temp32a);
temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a,
temp32alen, temp32a, temp48);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
temp48, finother);
finswap = finnow; finnow = finother; finother = finswap;
temp32alen = scale_expansion_zeroelim(cytabtlen, cytabt, cdytail,
temp32a);
cytabttlen = scale_expansion_zeroelim(abttlen, abtt, cdytail, cytabtt);
temp16alen = scale_expansion_zeroelim(cytabttlen, cytabtt, 2.0 * cdy,
temp16a);
temp16blen = scale_expansion_zeroelim(cytabttlen, cytabtt, cdytail,
temp16b);
temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
temp16blen, temp16b, temp32b);
temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a,
temp32blen, temp32b, temp64);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len,
temp64, finother);
finswap = finnow; finnow = finother; finother = finswap;
}
}
return finnow[finlength - 1];
}
#ifdef ANSI_DECLARATORS
REAL incircle(struct mesh *m, struct behavior *b,
vertex pa, vertex pb, vertex pc, vertex pd)
#else /* not ANSI_DECLARATORS */
REAL incircle(m, b, pa, pb, pc, pd)
struct mesh *m;
struct behavior *b;
vertex pa;
vertex pb;
vertex pc;
vertex pd;
#endif /* not ANSI_DECLARATORS */
{
REAL adx, bdx, cdx, ady, bdy, cdy;
REAL bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady;
REAL alift, blift, clift;
REAL det;
REAL permanent, errbound;
m->incirclecount++;
adx = pa[0] - pd[0];
bdx = pb[0] - pd[0];
cdx = pc[0] - pd[0];
ady = pa[1] - pd[1];
bdy = pb[1] - pd[1];
cdy = pc[1] - pd[1];
bdxcdy = bdx * cdy;
cdxbdy = cdx * bdy;
alift = adx * adx + ady * ady;
cdxady = cdx * ady;
adxcdy = adx * cdy;
blift = bdx * bdx + bdy * bdy;
adxbdy = adx * bdy;
bdxady = bdx * ady;
clift = cdx * cdx + cdy * cdy;
det = alift * (bdxcdy - cdxbdy)
+ blift * (cdxady - adxcdy)
+ clift * (adxbdy - bdxady);
if (b->noexact) {
return det;
}
permanent = (Absolute(bdxcdy) + Absolute(cdxbdy)) * alift
+ (Absolute(cdxady) + Absolute(adxcdy)) * blift
+ (Absolute(adxbdy) + Absolute(bdxady)) * clift;
errbound = iccerrboundA * permanent;
if ((det > errbound) || (-det > errbound)) {
return det;
}
return incircleadapt(pa, pb, pc, pd, permanent);
}
/*****************************************************************************/
/* */
/* orient3d() Return a positive value if the point pd lies below the */
/* plane passing through pa, pb, and pc; "below" is defined so */
/* that pa, pb, and pc appear in counterclockwise order when */
/* viewed from above the plane. Returns a negative value if */
/* pd lies above the plane. Returns zero if the points are */
/* coplanar. The result is also a rough approximation of six */
/* times the signed volume of the tetrahedron defined by the */
/* four points. */
/* */
/* Uses exact arithmetic if necessary to ensure a correct answer. The */
/* result returned is the determinant of a matrix. This determinant is */
/* computed adaptively, in the sense that exact arithmetic is used only to */
/* the degree it is needed to ensure that the returned value has the */
/* correct sign. Hence, this function is usually quite fast, but will run */
/* more slowly when the input points are coplanar or nearly so. */
/* */
/* See my Robust Predicates paper for details. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
REAL orient3dadapt(vertex pa, vertex pb, vertex pc, vertex pd,
REAL aheight, REAL bheight, REAL cheight, REAL dheight,
REAL permanent)
#else /* not ANSI_DECLARATORS */
REAL orient3dadapt(pa, pb, pc, pd,
aheight, bheight, cheight, dheight, permanent)
vertex pa;
vertex pb;
vertex pc;
vertex pd;
REAL aheight;
REAL bheight;
REAL cheight;
REAL dheight;
REAL permanent;
#endif /* not ANSI_DECLARATORS */
{
INEXACT REAL adx, bdx, cdx, ady, bdy, cdy, adheight, bdheight, cdheight;
REAL det, errbound;
INEXACT REAL bdxcdy1, cdxbdy1, cdxady1, adxcdy1, adxbdy1, bdxady1;
REAL bdxcdy0, cdxbdy0, cdxady0, adxcdy0, adxbdy0, bdxady0;
REAL bc[4], ca[4], ab[4];
INEXACT REAL bc3, ca3, ab3;
REAL adet[8], bdet[8], cdet[8];
int alen, blen, clen;
REAL abdet[16];
int ablen;
REAL *finnow, *finother, *finswap;
REAL fin1[192], fin2[192];
int finlength;
REAL adxtail, bdxtail, cdxtail;
REAL adytail, bdytail, cdytail;
REAL adheighttail, bdheighttail, cdheighttail;
INEXACT REAL at_blarge, at_clarge;
INEXACT REAL bt_clarge, bt_alarge;
INEXACT REAL ct_alarge, ct_blarge;
REAL at_b[4], at_c[4], bt_c[4], bt_a[4], ct_a[4], ct_b[4];
int at_blen, at_clen, bt_clen, bt_alen, ct_alen, ct_blen;
INEXACT REAL bdxt_cdy1, cdxt_bdy1, cdxt_ady1;
INEXACT REAL adxt_cdy1, adxt_bdy1, bdxt_ady1;
REAL bdxt_cdy0, cdxt_bdy0, cdxt_ady0;
REAL adxt_cdy0, adxt_bdy0, bdxt_ady0;
INEXACT REAL bdyt_cdx1, cdyt_bdx1, cdyt_adx1;
INEXACT REAL adyt_cdx1, adyt_bdx1, bdyt_adx1;
REAL bdyt_cdx0, cdyt_bdx0, cdyt_adx0;
REAL adyt_cdx0, adyt_bdx0, bdyt_adx0;
REAL bct[8], cat[8], abt[8];
int bctlen, catlen, abtlen;
INEXACT REAL bdxt_cdyt1, cdxt_bdyt1, cdxt_adyt1;
INEXACT REAL adxt_cdyt1, adxt_bdyt1, bdxt_adyt1;
REAL bdxt_cdyt0, cdxt_bdyt0, cdxt_adyt0;
REAL adxt_cdyt0, adxt_bdyt0, bdxt_adyt0;
REAL u[4], v[12], w[16];
INEXACT REAL u3;
int vlength, wlength;
REAL negate;
INEXACT REAL bvirt;
REAL avirt, bround, around;
INEXACT REAL c;
INEXACT REAL abig;
REAL ahi, alo, bhi, blo;
REAL err1, err2, err3;
INEXACT REAL _i, _j, _k;
REAL _0;
adx = (REAL) (pa[0] - pd[0]);
bdx = (REAL) (pb[0] - pd[0]);
cdx = (REAL) (pc[0] - pd[0]);
ady = (REAL) (pa[1] - pd[1]);
bdy = (REAL) (pb[1] - pd[1]);
cdy = (REAL) (pc[1] - pd[1]);
adheight = (REAL) (aheight - dheight);
bdheight = (REAL) (bheight - dheight);
cdheight = (REAL) (cheight - dheight);
Two_Product(bdx, cdy, bdxcdy1, bdxcdy0);
Two_Product(cdx, bdy, cdxbdy1, cdxbdy0);
Two_Two_Diff(bdxcdy1, bdxcdy0, cdxbdy1, cdxbdy0, bc3, bc[2], bc[1], bc[0]);
bc[3] = bc3;
alen = scale_expansion_zeroelim(4, bc, adheight, adet);
Two_Product(cdx, ady, cdxady1, cdxady0);
Two_Product(adx, cdy, adxcdy1, adxcdy0);
Two_Two_Diff(cdxady1, cdxady0, adxcdy1, adxcdy0, ca3, ca[2], ca[1], ca[0]);
ca[3] = ca3;
blen = scale_expansion_zeroelim(4, ca, bdheight, bdet);
Two_Product(adx, bdy, adxbdy1, adxbdy0);
Two_Product(bdx, ady, bdxady1, bdxady0);
Two_Two_Diff(adxbdy1, adxbdy0, bdxady1, bdxady0, ab3, ab[2], ab[1], ab[0]);
ab[3] = ab3;
clen = scale_expansion_zeroelim(4, ab, cdheight, cdet);
ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
finlength = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, fin1);
det = estimate(finlength, fin1);
errbound = o3derrboundB * permanent;
if ((det >= errbound) || (-det >= errbound)) {
return det;
}
Two_Diff_Tail(pa[0], pd[0], adx, adxtail);
Two_Diff_Tail(pb[0], pd[0], bdx, bdxtail);
Two_Diff_Tail(pc[0], pd[0], cdx, cdxtail);
Two_Diff_Tail(pa[1], pd[1], ady, adytail);
Two_Diff_Tail(pb[1], pd[1], bdy, bdytail);
Two_Diff_Tail(pc[1], pd[1], cdy, cdytail);
Two_Diff_Tail(aheight, dheight, adheight, adheighttail);
Two_Diff_Tail(bheight, dheight, bdheight, bdheighttail);
Two_Diff_Tail(cheight, dheight, cdheight, cdheighttail);
if ((adxtail == 0.0) && (bdxtail == 0.0) && (cdxtail == 0.0) &&
(adytail == 0.0) && (bdytail == 0.0) && (cdytail == 0.0) &&
(adheighttail == 0.0) &&
(bdheighttail == 0.0) &&
(cdheighttail == 0.0)) {
return det;
}
errbound = o3derrboundC * permanent + resulterrbound * Absolute(det);
det += (adheight * ((bdx * cdytail + cdy * bdxtail) -
(bdy * cdxtail + cdx * bdytail)) +
adheighttail * (bdx * cdy - bdy * cdx)) +
(bdheight * ((cdx * adytail + ady * cdxtail) -
(cdy * adxtail + adx * cdytail)) +
bdheighttail * (cdx * ady - cdy * adx)) +
(cdheight * ((adx * bdytail + bdy * adxtail) -
(ady * bdxtail + bdx * adytail)) +
cdheighttail * (adx * bdy - ady * bdx));
if ((det >= errbound) || (-det >= errbound)) {
return det;
}
finnow = fin1;
finother = fin2;
if (adxtail == 0.0) {
if (adytail == 0.0) {
at_b[0] = 0.0;
at_blen = 1;
at_c[0] = 0.0;
at_clen = 1;
} else {
negate = -adytail;
Two_Product(negate, bdx, at_blarge, at_b[0]);
at_b[1] = at_blarge;
at_blen = 2;
Two_Product(adytail, cdx, at_clarge, at_c[0]);
at_c[1] = at_clarge;
at_clen = 2;
}
} else {
if (adytail == 0.0) {
Two_Product(adxtail, bdy, at_blarge, at_b[0]);
at_b[1] = at_blarge;
at_blen = 2;
negate = -adxtail;
Two_Product(negate, cdy, at_clarge, at_c[0]);
at_c[1] = at_clarge;
at_clen = 2;
} else {
Two_Product(adxtail, bdy, adxt_bdy1, adxt_bdy0);
Two_Product(adytail, bdx, adyt_bdx1, adyt_bdx0);
Two_Two_Diff(adxt_bdy1, adxt_bdy0, adyt_bdx1, adyt_bdx0,
at_blarge, at_b[2], at_b[1], at_b[0]);
at_b[3] = at_blarge;
at_blen = 4;
Two_Product(adytail, cdx, adyt_cdx1, adyt_cdx0);
Two_Product(adxtail, cdy, adxt_cdy1, adxt_cdy0);
Two_Two_Diff(adyt_cdx1, adyt_cdx0, adxt_cdy1, adxt_cdy0,
at_clarge, at_c[2], at_c[1], at_c[0]);
at_c[3] = at_clarge;
at_clen = 4;
}
}
if (bdxtail == 0.0) {
if (bdytail == 0.0) {
bt_c[0] = 0.0;
bt_clen = 1;
bt_a[0] = 0.0;
bt_alen = 1;
} else {
negate = -bdytail;
Two_Product(negate, cdx, bt_clarge, bt_c[0]);
bt_c[1] = bt_clarge;
bt_clen = 2;
Two_Product(bdytail, adx, bt_alarge, bt_a[0]);
bt_a[1] = bt_alarge;
bt_alen = 2;
}
} else {
if (bdytail == 0.0) {
Two_Product(bdxtail, cdy, bt_clarge, bt_c[0]);
bt_c[1] = bt_clarge;
bt_clen = 2;
negate = -bdxtail;
Two_Product(negate, ady, bt_alarge, bt_a[0]);
bt_a[1] = bt_alarge;
bt_alen = 2;
} else {
Two_Product(bdxtail, cdy, bdxt_cdy1, bdxt_cdy0);
Two_Product(bdytail, cdx, bdyt_cdx1, bdyt_cdx0);
Two_Two_Diff(bdxt_cdy1, bdxt_cdy0, bdyt_cdx1, bdyt_cdx0,
bt_clarge, bt_c[2], bt_c[1], bt_c[0]);
bt_c[3] = bt_clarge;
bt_clen = 4;
Two_Product(bdytail, adx, bdyt_adx1, bdyt_adx0);
Two_Product(bdxtail, ady, bdxt_ady1, bdxt_ady0);
Two_Two_Diff(bdyt_adx1, bdyt_adx0, bdxt_ady1, bdxt_ady0,
bt_alarge, bt_a[2], bt_a[1], bt_a[0]);
bt_a[3] = bt_alarge;
bt_alen = 4;
}
}
if (cdxtail == 0.0) {
if (cdytail == 0.0) {
ct_a[0] = 0.0;
ct_alen = 1;
ct_b[0] = 0.0;
ct_blen = 1;
} else {
negate = -cdytail;
Two_Product(negate, adx, ct_alarge, ct_a[0]);
ct_a[1] = ct_alarge;
ct_alen = 2;
Two_Product(cdytail, bdx, ct_blarge, ct_b[0]);
ct_b[1] = ct_blarge;
ct_blen = 2;
}
} else {
if (cdytail == 0.0) {
Two_Product(cdxtail, ady, ct_alarge, ct_a[0]);
ct_a[1] = ct_alarge;
ct_alen = 2;
negate = -cdxtail;
Two_Product(negate, bdy, ct_blarge, ct_b[0]);
ct_b[1] = ct_blarge;
ct_blen = 2;
} else {
Two_Product(cdxtail, ady, cdxt_ady1, cdxt_ady0);
Two_Product(cdytail, adx, cdyt_adx1, cdyt_adx0);
Two_Two_Diff(cdxt_ady1, cdxt_ady0, cdyt_adx1, cdyt_adx0,
ct_alarge, ct_a[2], ct_a[1], ct_a[0]);
ct_a[3] = ct_alarge;
ct_alen = 4;
Two_Product(cdytail, bdx, cdyt_bdx1, cdyt_bdx0);
Two_Product(cdxtail, bdy, cdxt_bdy1, cdxt_bdy0);
Two_Two_Diff(cdyt_bdx1, cdyt_bdx0, cdxt_bdy1, cdxt_bdy0,
ct_blarge, ct_b[2], ct_b[1], ct_b[0]);
ct_b[3] = ct_blarge;
ct_blen = 4;
}
}
bctlen = fast_expansion_sum_zeroelim(bt_clen, bt_c, ct_blen, ct_b, bct);
wlength = scale_expansion_zeroelim(bctlen, bct, adheight, w);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
finother);
finswap = finnow; finnow = finother; finother = finswap;
catlen = fast_expansion_sum_zeroelim(ct_alen, ct_a, at_clen, at_c, cat);
wlength = scale_expansion_zeroelim(catlen, cat, bdheight, w);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
finother);
finswap = finnow; finnow = finother; finother = finswap;
abtlen = fast_expansion_sum_zeroelim(at_blen, at_b, bt_alen, bt_a, abt);
wlength = scale_expansion_zeroelim(abtlen, abt, cdheight, w);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
finother);
finswap = finnow; finnow = finother; finother = finswap;
if (adheighttail != 0.0) {
vlength = scale_expansion_zeroelim(4, bc, adheighttail, v);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v,
finother);
finswap = finnow; finnow = finother; finother = finswap;
}
if (bdheighttail != 0.0) {
vlength = scale_expansion_zeroelim(4, ca, bdheighttail, v);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v,
finother);
finswap = finnow; finnow = finother; finother = finswap;
}
if (cdheighttail != 0.0) {
vlength = scale_expansion_zeroelim(4, ab, cdheighttail, v);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v,
finother);
finswap = finnow; finnow = finother; finother = finswap;
}
if (adxtail != 0.0) {
if (bdytail != 0.0) {
Two_Product(adxtail, bdytail, adxt_bdyt1, adxt_bdyt0);
Two_One_Product(adxt_bdyt1, adxt_bdyt0, cdheight, u3, u[2], u[1], u[0]);
u[3] = u3;
finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
finother);
finswap = finnow; finnow = finother; finother = finswap;
if (cdheighttail != 0.0) {
Two_One_Product(adxt_bdyt1, adxt_bdyt0, cdheighttail,
u3, u[2], u[1], u[0]);
u[3] = u3;
finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
finother);
finswap = finnow; finnow = finother; finother = finswap;
}
}
if (cdytail != 0.0) {
negate = -adxtail;
Two_Product(negate, cdytail, adxt_cdyt1, adxt_cdyt0);
Two_One_Product(adxt_cdyt1, adxt_cdyt0, bdheight, u3, u[2], u[1], u[0]);
u[3] = u3;
finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
finother);
finswap = finnow; finnow = finother; finother = finswap;
if (bdheighttail != 0.0) {
Two_One_Product(adxt_cdyt1, adxt_cdyt0, bdheighttail,
u3, u[2], u[1], u[0]);
u[3] = u3;
finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
finother);
finswap = finnow; finnow = finother; finother = finswap;
}
}
}
if (bdxtail != 0.0) {
if (cdytail != 0.0) {
Two_Product(bdxtail, cdytail, bdxt_cdyt1, bdxt_cdyt0);
Two_One_Product(bdxt_cdyt1, bdxt_cdyt0, adheight, u3, u[2], u[1], u[0]);
u[3] = u3;
finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
finother);
finswap = finnow; finnow = finother; finother = finswap;
if (adheighttail != 0.0) {
Two_One_Product(bdxt_cdyt1, bdxt_cdyt0, adheighttail,
u3, u[2], u[1], u[0]);
u[3] = u3;
finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
finother);
finswap = finnow; finnow = finother; finother = finswap;
}
}
if (adytail != 0.0) {
negate = -bdxtail;
Two_Product(negate, adytail, bdxt_adyt1, bdxt_adyt0);
Two_One_Product(bdxt_adyt1, bdxt_adyt0, cdheight, u3, u[2], u[1], u[0]);
u[3] = u3;
finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
finother);
finswap = finnow; finnow = finother; finother = finswap;
if (cdheighttail != 0.0) {
Two_One_Product(bdxt_adyt1, bdxt_adyt0, cdheighttail,
u3, u[2], u[1], u[0]);
u[3] = u3;
finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
finother);
finswap = finnow; finnow = finother; finother = finswap;
}
}
}
if (cdxtail != 0.0) {
if (adytail != 0.0) {
Two_Product(cdxtail, adytail, cdxt_adyt1, cdxt_adyt0);
Two_One_Product(cdxt_adyt1, cdxt_adyt0, bdheight, u3, u[2], u[1], u[0]);
u[3] = u3;
finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
finother);
finswap = finnow; finnow = finother; finother = finswap;
if (bdheighttail != 0.0) {
Two_One_Product(cdxt_adyt1, cdxt_adyt0, bdheighttail,
u3, u[2], u[1], u[0]);
u[3] = u3;
finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
finother);
finswap = finnow; finnow = finother; finother = finswap;
}
}
if (bdytail != 0.0) {
negate = -cdxtail;
Two_Product(negate, bdytail, cdxt_bdyt1, cdxt_bdyt0);
Two_One_Product(cdxt_bdyt1, cdxt_bdyt0, adheight, u3, u[2], u[1], u[0]);
u[3] = u3;
finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
finother);
finswap = finnow; finnow = finother; finother = finswap;
if (adheighttail != 0.0) {
Two_One_Product(cdxt_bdyt1, cdxt_bdyt0, adheighttail,
u3, u[2], u[1], u[0]);
u[3] = u3;
finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
finother);
finswap = finnow; finnow = finother; finother = finswap;
}
}
}
if (adheighttail != 0.0) {
wlength = scale_expansion_zeroelim(bctlen, bct, adheighttail, w);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
finother);
finswap = finnow; finnow = finother; finother = finswap;
}
if (bdheighttail != 0.0) {
wlength = scale_expansion_zeroelim(catlen, cat, bdheighttail, w);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
finother);
finswap = finnow; finnow = finother; finother = finswap;
}
if (cdheighttail != 0.0) {
wlength = scale_expansion_zeroelim(abtlen, abt, cdheighttail, w);
finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
finother);
finswap = finnow; finnow = finother; finother = finswap;
}
return finnow[finlength - 1];
}
#ifdef ANSI_DECLARATORS
REAL orient3d(struct mesh *m, struct behavior *b,
vertex pa, vertex pb, vertex pc, vertex pd,
REAL aheight, REAL bheight, REAL cheight, REAL dheight)
#else /* not ANSI_DECLARATORS */
REAL orient3d(m, b, pa, pb, pc, pd, aheight, bheight, cheight, dheight)
struct mesh *m;
struct behavior *b;
vertex pa;
vertex pb;
vertex pc;
vertex pd;
REAL aheight;
REAL bheight;
REAL cheight;
REAL dheight;
#endif /* not ANSI_DECLARATORS */
{
REAL adx, bdx, cdx, ady, bdy, cdy, adheight, bdheight, cdheight;
REAL bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady;
REAL det;
REAL permanent, errbound;
m->orient3dcount++;
adx = pa[0] - pd[0];
bdx = pb[0] - pd[0];
cdx = pc[0] - pd[0];
ady = pa[1] - pd[1];
bdy = pb[1] - pd[1];
cdy = pc[1] - pd[1];
adheight = aheight - dheight;
bdheight = bheight - dheight;
cdheight = cheight - dheight;
bdxcdy = bdx * cdy;
cdxbdy = cdx * bdy;
cdxady = cdx * ady;
adxcdy = adx * cdy;
adxbdy = adx * bdy;
bdxady = bdx * ady;
det = adheight * (bdxcdy - cdxbdy)
+ bdheight * (cdxady - adxcdy)
+ cdheight * (adxbdy - bdxady);
if (b->noexact) {
return det;
}
permanent = (Absolute(bdxcdy) + Absolute(cdxbdy)) * Absolute(adheight)
+ (Absolute(cdxady) + Absolute(adxcdy)) * Absolute(bdheight)
+ (Absolute(adxbdy) + Absolute(bdxady)) * Absolute(cdheight);
errbound = o3derrboundA * permanent;
if ((det > errbound) || (-det > errbound)) {
return det;
}
return orient3dadapt(pa, pb, pc, pd, aheight, bheight, cheight, dheight,
permanent);
}
/*****************************************************************************/
/* */
/* nonregular() Return a positive value if the point pd is incompatible */
/* with the circle or plane passing through pa, pb, and pc */
/* (meaning that pd is inside the circle or below the */
/* plane); a negative value if it is compatible; and zero if */
/* the four points are cocircular/coplanar. The points pa, */
/* pb, and pc must be in counterclockwise order, or the sign */
/* of the result will be reversed. */
/* */
/* If the -w switch is used, the points are lifted onto the parabolic */
/* lifting map, then they are dropped according to their weights, then the */
/* 3D orientation test is applied. If the -W switch is used, the points' */
/* heights are already provided, so the 3D orientation test is applied */
/* directly. If neither switch is used, the incircle test is applied. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
REAL nonregular(struct mesh *m, struct behavior *b,
vertex pa, vertex pb, vertex pc, vertex pd)
#else /* not ANSI_DECLARATORS */
REAL nonregular(m, b, pa, pb, pc, pd)
struct mesh *m;
struct behavior *b;
vertex pa;
vertex pb;
vertex pc;
vertex pd;
#endif /* not ANSI_DECLARATORS */
{
if (b->weighted == 0) {
return incircle(m, b, pa, pb, pc, pd);
} else if (b->weighted == 1) {
return orient3d(m, b, pa, pb, pc, pd,
pa[0] * pa[0] + pa[1] * pa[1] - pa[2],
pb[0] * pb[0] + pb[1] * pb[1] - pb[2],
pc[0] * pc[0] + pc[1] * pc[1] - pc[2],
pd[0] * pd[0] + pd[1] * pd[1] - pd[2]);
} else {
return orient3d(m, b, pa, pb, pc, pd, pa[2], pb[2], pc[2], pd[2]);
}
}
/*****************************************************************************/
/* */
/* findcircumcenter() Find the circumcenter of a triangle. */
/* */
/* The result is returned both in terms of x-y coordinates and xi-eta */
/* (barycentric) coordinates. The xi-eta coordinate system is defined in */
/* terms of the triangle: the origin of the triangle is the origin of the */
/* coordinate system; the destination of the triangle is one unit along the */
/* xi axis; and the apex of the triangle is one unit along the eta axis. */
/* This procedure also returns the square of the length of the triangle's */
/* shortest edge. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
void findcircumcenter(struct mesh *m, struct behavior *b,
vertex torg, vertex tdest, vertex tapex,
vertex circumcenter, REAL *xi, REAL *eta, int offcenter)
#else /* not ANSI_DECLARATORS */
void findcircumcenter(m, b, torg, tdest, tapex, circumcenter, xi, eta,
offcenter)
struct mesh *m;
struct behavior *b;
vertex torg;
vertex tdest;
vertex tapex;
vertex circumcenter;
REAL *xi;
REAL *eta;
int offcenter;
#endif /* not ANSI_DECLARATORS */
{
REAL xdo, ydo, xao, yao;
REAL dodist, aodist, dadist;
REAL denominator;
REAL dx, dy, dxoff, dyoff;
m->circumcentercount++;
/* Compute the circumcenter of the triangle. */
xdo = tdest[0] - torg[0];
ydo = tdest[1] - torg[1];
xao = tapex[0] - torg[0];
yao = tapex[1] - torg[1];
dodist = xdo * xdo + ydo * ydo;
aodist = xao * xao + yao * yao;
dadist = (tdest[0] - tapex[0]) * (tdest[0] - tapex[0]) +
(tdest[1] - tapex[1]) * (tdest[1] - tapex[1]);
if (b->noexact) {
denominator = 0.5 / (xdo * yao - xao * ydo);
} else {
/* Use the counterclockwise() routine to ensure a positive (and */
/* reasonably accurate) result, avoiding any possibility of */
/* division by zero. */
denominator = 0.5 / counterclockwise(m, b, tdest, tapex, torg);
/* Don't count the above as an orientation test. */
m->counterclockcount--;
}
dx = (yao * dodist - ydo * aodist) * denominator;
dy = (xdo * aodist - xao * dodist) * denominator;
/* Find the (squared) length of the triangle's shortest edge. This */
/* serves as a conservative estimate of the insertion radius of the */
/* circumcenter's parent. The estimate is used to ensure that */
/* the algorithm terminates even if very small angles appear in */
/* the input PSLG. */
if ((dodist < aodist) && (dodist < dadist)) {
if (offcenter && (b->offconstant > 0.0)) {
/* Find the position of the off-center, as described by Alper Ungor. */
dxoff = 0.5 * xdo - b->offconstant * ydo;
dyoff = 0.5 * ydo + b->offconstant * xdo;
/* If the off-center is closer to the origin than the */
/* circumcenter, use the off-center instead. */
if (dxoff * dxoff + dyoff * dyoff < dx * dx + dy * dy) {
dx = dxoff;
dy = dyoff;
}
}
} else if (aodist < dadist) {
if (offcenter && (b->offconstant > 0.0)) {
dxoff = 0.5 * xao + b->offconstant * yao;
dyoff = 0.5 * yao - b->offconstant * xao;
/* If the off-center is closer to the origin than the */
/* circumcenter, use the off-center instead. */
if (dxoff * dxoff + dyoff * dyoff < dx * dx + dy * dy) {
dx = dxoff;
dy = dyoff;
}
}
} else {
if (offcenter && (b->offconstant > 0.0)) {
dxoff = 0.5 * (tapex[0] - tdest[0]) -
b->offconstant * (tapex[1] - tdest[1]);
dyoff = 0.5 * (tapex[1] - tdest[1]) +
b->offconstant * (tapex[0] - tdest[0]);
/* If the off-center is closer to the destination than the */
/* circumcenter, use the off-center instead. */
if (dxoff * dxoff + dyoff * dyoff <
(dx - xdo) * (dx - xdo) + (dy - ydo) * (dy - ydo)) {
dx = xdo + dxoff;
dy = ydo + dyoff;
}
}
}
circumcenter[0] = torg[0] + dx;
circumcenter[1] = torg[1] + dy;
/* To interpolate vertex attributes for the new vertex inserted at */
/* the circumcenter, define a coordinate system with a xi-axis, */
/* directed from the triangle's origin to its destination, and */
/* an eta-axis, directed from its origin to its apex. */
/* Calculate the xi and eta coordinates of the circumcenter. */
*xi = (yao * dx - xao * dy) * (2.0 * denominator);
*eta = (xdo * dy - ydo * dx) * (2.0 * denominator);
}
/** **/
/** **/
/********* Geometric primitives end here *********/
/*****************************************************************************/
/* */
/* triangleinit() Initialize some variables. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
void triangleinit(struct mesh *m)
#else /* not ANSI_DECLARATORS */
void triangleinit(m)
struct mesh *m;
#endif /* not ANSI_DECLARATORS */
{
poolzero(&m->vertices);
poolzero(&m->triangles);
poolzero(&m->subsegs);
poolzero(&m->viri);
poolzero(&m->badsubsegs);
poolzero(&m->badtriangles);
poolzero(&m->flipstackers);
poolzero(&m->splaynodes);
m->recenttri.tri = (triangle *) NULL; /* No triangle has been visited yet. */
m->undeads = 0; /* No eliminated input vertices yet. */
m->samples = 1; /* Point location should take at least one sample. */
m->checksegments = 0; /* There are no segments in the triangulation yet. */
m->checkquality = 0; /* The quality triangulation stage has not begun. */
m->incirclecount = m->counterclockcount = m->orient3dcount = 0;
m->hyperbolacount = m->circletopcount = m->circumcentercount = 0;
randomseed = 1;
exactinit(); /* Initialize exact arithmetic constants. */
}
/*****************************************************************************/
/* */
/* randomnation() Generate a random number between 0 and `choices' - 1. */
/* */
/* This is a simple linear congruential random number generator. Hence, it */
/* is a bad random number generator, but good enough for most randomized */
/* geometric algorithms. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
unsigned long randomnation(unsigned int choices)
#else /* not ANSI_DECLARATORS */
unsigned long randomnation(choices)
unsigned int choices;
#endif /* not ANSI_DECLARATORS */
{
randomseed = (randomseed * 1366l + 150889l) % 714025l;
return randomseed / (714025l / choices + 1);
}
/********* Mesh quality testing routines begin here *********/
/** **/
/** **/
/*****************************************************************************/
/* */
/* checkmesh() Test the mesh for topological consistency. */
/* */
/*****************************************************************************/
#ifndef REDUCED
#ifdef ANSI_DECLARATORS
void checkmesh(struct mesh *m, struct behavior *b)
#else /* not ANSI_DECLARATORS */
void checkmesh(m, b)
struct mesh *m;
struct behavior *b;
#endif /* not ANSI_DECLARATORS */
{
struct otri triangleloop;
struct otri oppotri, oppooppotri;
vertex triorg, tridest, triapex;
vertex oppoorg, oppodest;
int horrors;
int saveexact;
triangle ptr; /* Temporary variable used by sym(). */
/* Temporarily turn on exact arithmetic if it's off. */
saveexact = b->noexact;
b->noexact = 0;
if (!b->quiet) {
printf(" Checking consistency of mesh...\n");
}
horrors = 0;
/* Run through the list of triangles, checking each one. */
traversalinit(&m->triangles);
triangleloop.tri = triangletraverse(m);
while (triangleloop.tri != (triangle *) NULL) {
/* Check all three edges of the triangle. */
for (triangleloop.orient = 0; triangleloop.orient < 3;
triangleloop.orient++) {
org(triangleloop, triorg);
dest(triangleloop, tridest);
if (triangleloop.orient == 0) { /* Only test for inversion once. */
/* Test if the triangle is flat or inverted. */
apex(triangleloop, triapex);
if (counterclockwise(m, b, triorg, tridest, triapex) <= 0.0) {
printf(" !! !! Inverted ");
printtriangle(m, b, &triangleloop);
horrors++;
}
}
/* Find the neighboring triangle on this edge. */
sym(triangleloop, oppotri);
if (oppotri.tri != m->dummytri) {
/* Check that the triangle's neighbor knows it's a neighbor. */
sym(oppotri, oppooppotri);
if ((triangleloop.tri != oppooppotri.tri)
|| (triangleloop.orient != oppooppotri.orient)) {
printf(" !! !! Asymmetric triangle-triangle bond:\n");
if (triangleloop.tri == oppooppotri.tri) {
printf(" (Right triangle, wrong orientation)\n");
}
printf(" First ");
printtriangle(m, b, &triangleloop);
printf(" Second (nonreciprocating) ");
printtriangle(m, b, &oppotri);
horrors++;
}
/* Check that both triangles agree on the identities */
/* of their shared vertices. */
org(oppotri, oppoorg);
dest(oppotri, oppodest);
if ((triorg != oppodest) || (tridest != oppoorg)) {
printf(" !! !! Mismatched edge coordinates between two triangles:\n"
);
printf(" First mismatched ");
printtriangle(m, b, &triangleloop);
printf(" Second mismatched ");
printtriangle(m, b, &oppotri);
horrors++;
}
}
}
triangleloop.tri = triangletraverse(m);
}
if (horrors == 0) {
if (!b->quiet) {
printf(" In my studied opinion, the mesh appears to be consistent.\n");
}
} else if (horrors == 1) {
printf(" !! !! !! !! Precisely one festering wound discovered.\n");
} else {
printf(" !! !! !! !! %d abominations witnessed.\n", horrors);
}
/* Restore the status of exact arithmetic. */
b->noexact = saveexact;
}
#endif /* not REDUCED */
/*****************************************************************************/
/* */
/* checkdelaunay() Ensure that the mesh is (constrained) Delaunay. */
/* */
/*****************************************************************************/
#ifndef REDUCED
#ifdef ANSI_DECLARATORS
void checkdelaunay(struct mesh *m, struct behavior *b)
#else /* not ANSI_DECLARATORS */
void checkdelaunay(m, b)
struct mesh *m;
struct behavior *b;
#endif /* not ANSI_DECLARATORS */
{
struct otri triangleloop;
struct otri oppotri;
struct osub opposubseg;
vertex triorg, tridest, triapex;
vertex oppoapex;
int shouldbedelaunay;
int horrors;
int saveexact;
triangle ptr; /* Temporary variable used by sym(). */
subseg sptr; /* Temporary variable used by tspivot(). */
/* Temporarily turn on exact arithmetic if it's off. */
saveexact = b->noexact;
b->noexact = 0;
if (!b->quiet) {
printf(" Checking Delaunay property of mesh...\n");
}
horrors = 0;
/* Run through the list of triangles, checking each one. */
traversalinit(&m->triangles);
triangleloop.tri = triangletraverse(m);
while (triangleloop.tri != (triangle *) NULL) {
/* Check all three edges of the triangle. */
for (triangleloop.orient = 0; triangleloop.orient < 3;
triangleloop.orient++) {
org(triangleloop, triorg);
dest(triangleloop, tridest);
apex(triangleloop, triapex);
sym(triangleloop, oppotri);
apex(oppotri, oppoapex);
/* Only test that the edge is locally Delaunay if there is an */
/* adjoining triangle whose pointer is larger (to ensure that */
/* each pair isn't tested twice). */
shouldbedelaunay = (oppotri.tri != m->dummytri) &&
!deadtri(oppotri.tri) && (triangleloop.tri < oppotri.tri) &&
(triorg != m->infvertex1) && (triorg != m->infvertex2) &&
(triorg != m->infvertex3) &&
(tridest != m->infvertex1) && (tridest != m->infvertex2) &&
(tridest != m->infvertex3) &&
(triapex != m->infvertex1) && (triapex != m->infvertex2) &&
(triapex != m->infvertex3) &&
(oppoapex != m->infvertex1) && (oppoapex != m->infvertex2) &&
(oppoapex != m->infvertex3);
if (m->checksegments && shouldbedelaunay) {
/* If a subsegment separates the triangles, then the edge is */
/* constrained, so no local Delaunay test should be done. */
tspivot(triangleloop, opposubseg);
if (opposubseg.ss != m->dummysub){
shouldbedelaunay = 0;
}
}
if (shouldbedelaunay) {
if (nonregular(m, b, triorg, tridest, triapex, oppoapex) > 0.0) {
if (!b->weighted) {
printf(" !! !! Non-Delaunay pair of triangles:\n");
printf(" First non-Delaunay ");
printtriangle(m, b, &triangleloop);
printf(" Second non-Delaunay ");
} else {
printf(" !! !! Non-regular pair of triangles:\n");
printf(" First non-regular ");
printtriangle(m, b, &triangleloop);
printf(" Second non-regular ");
}
printtriangle(m, b, &oppotri);
horrors++;
}
}
}
triangleloop.tri = triangletraverse(m);
}
if (horrors == 0) {
if (!b->quiet) {
printf(
" By virtue of my perceptive intelligence, I declare the mesh Delaunay.\n");
}
} else if (horrors == 1) {
printf(
" !! !! !! !! Precisely one terrifying transgression identified.\n");
} else {
printf(" !! !! !! !! %d obscenities viewed with horror.\n", horrors);
}
/* Restore the status of exact arithmetic. */
b->noexact = saveexact;
}
#endif /* not REDUCED */
/*****************************************************************************/
/* */
/* enqueuebadtriang() Add a bad triangle data structure to the end of a */
/* queue. */
/* */
/* The queue is actually a set of 4096 queues. I use multiple queues to */
/* give priority to smaller angles. I originally implemented a heap, but */
/* the queues are faster by a larger margin than I'd suspected. */
/* */
/*****************************************************************************/
#ifndef CDT_ONLY
#ifdef ANSI_DECLARATORS
void enqueuebadtriang(struct mesh *m, struct behavior *b,
struct badtriang *badtri)
#else /* not ANSI_DECLARATORS */
void enqueuebadtriang(m, b, badtri)
struct mesh *m;
struct behavior *b;
struct badtriang *badtri;
#endif /* not ANSI_DECLARATORS */
{
REAL length, multiplier;
int exponent, expincrement;
int queuenumber;
int posexponent;
int i;
if (b->verbose > 2) {
printf(" Queueing bad triangle:\n");
printf(" (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n",
badtri->triangorg[0], badtri->triangorg[1],
badtri->triangdest[0], badtri->triangdest[1],
badtri->triangapex[0], badtri->triangapex[1]);
}
/* Determine the appropriate queue to put the bad triangle into. */
/* Recall that the key is the square of its shortest edge length. */
if (badtri->key >= 1.0) {
length = badtri->key;
posexponent = 1;
} else {
/* `badtri->key' is 2.0 to a negative exponent, so we'll record that */
/* fact and use the reciprocal of `badtri->key', which is > 1.0. */
length = 1.0 / badtri->key;
posexponent = 0;
}
/* `length' is approximately 2.0 to what exponent? The following code */
/* determines the answer in time logarithmic in the exponent. */
exponent = 0;
while (length > 2.0) {
/* Find an approximation by repeated squaring of two. */
expincrement = 1;
multiplier = 0.5;
while (length * multiplier * multiplier > 1.0) {
expincrement *= 2;
multiplier *= multiplier;
}
/* Reduce the value of `length', then iterate if necessary. */
exponent += expincrement;
length *= multiplier;
}
/* `length' is approximately squareroot(2.0) to what exponent? */
exponent = 2.0 * exponent + (length > SQUAREROOTTWO);
/* `exponent' is now in the range 0...2047 for IEEE double precision. */
/* Choose a queue in the range 0...4095. The shortest edges have the */
/* highest priority (queue 4095). */
if (posexponent) {
queuenumber = 2047 - exponent;
} else {
queuenumber = 2048 + exponent;
}
/* Are we inserting into an empty queue? */
if (m->queuefront[queuenumber] == (struct badtriang *) NULL) {
/* Yes, we are inserting into an empty queue. */
/* Will this become the highest-priority queue? */
if (queuenumber > m->firstnonemptyq) {
/* Yes, this is the highest-priority queue. */
m->nextnonemptyq[queuenumber] = m->firstnonemptyq;
m->firstnonemptyq = queuenumber;
} else {
/* No, this is not the highest-priority queue. */
/* Find the queue with next higher priority. */
i = queuenumber + 1;
while (m->queuefront[i] == (struct badtriang *) NULL) {
i++;
}
/* Mark the newly nonempty queue as following a higher-priority queue. */
m->nextnonemptyq[queuenumber] = m->nextnonemptyq[i];
m->nextnonemptyq[i] = queuenumber;
}
/* Put the bad triangle at the beginning of the (empty) queue. */
m->queuefront[queuenumber] = badtri;
} else {
/* Add the bad triangle to the end of an already nonempty queue. */
m->queuetail[queuenumber]->nexttriang = badtri;
}
/* Maintain a pointer to the last triangle of the queue. */
m->queuetail[queuenumber] = badtri;
/* Newly enqueued bad triangle has no successor in the queue. */
badtri->nexttriang = (struct badtriang *) NULL;
}
#endif /* not CDT_ONLY */
/*****************************************************************************/
/* */
/* enqueuebadtri() Add a bad triangle to the end of a queue. */
/* */
/* Allocates a badtriang data structure for the triangle, then passes it to */
/* enqueuebadtriang(). */
/* */
/*****************************************************************************/
#ifndef CDT_ONLY
#ifdef ANSI_DECLARATORS
void enqueuebadtri(struct mesh *m, struct behavior *b, struct otri *enqtri,
REAL minedge, vertex enqapex, vertex enqorg, vertex enqdest)
#else /* not ANSI_DECLARATORS */
void enqueuebadtri(m, b, enqtri, minedge, enqapex, enqorg, enqdest)
struct mesh *m;
struct behavior *b;
struct otri *enqtri;
REAL minedge;
vertex enqapex;
vertex enqorg;
vertex enqdest;
#endif /* not ANSI_DECLARATORS */
{
struct badtriang *newbad;
/* Allocate space for the bad triangle. */
newbad = (struct badtriang *) poolalloc(&m->badtriangles);
newbad->poortri = encode(*enqtri);
newbad->key = minedge;
newbad->triangapex = enqapex;
newbad->triangorg = enqorg;
newbad->triangdest = enqdest;
enqueuebadtriang(m, b, newbad);
}
#endif /* not CDT_ONLY */
/*****************************************************************************/
/* */
/* dequeuebadtriang() Remove a triangle from the front of the queue. */
/* */
/*****************************************************************************/
#ifndef CDT_ONLY
#ifdef ANSI_DECLARATORS
struct badtriang *dequeuebadtriang(struct mesh *m)
#else /* not ANSI_DECLARATORS */
struct badtriang *dequeuebadtriang(m)
struct mesh *m;
#endif /* not ANSI_DECLARATORS */
{
struct badtriang *result;
/* If no queues are nonempty, return NULL. */
if (m->firstnonemptyq < 0) {
return (struct badtriang *) NULL;
}
/* Find the first triangle of the highest-priority queue. */
result = m->queuefront[m->firstnonemptyq];
/* Remove the triangle from the queue. */
m->queuefront[m->firstnonemptyq] = result->nexttriang;
/* If this queue is now empty, note the new highest-priority */
/* nonempty queue. */
if (result == m->queuetail[m->firstnonemptyq]) {
m->firstnonemptyq = m->nextnonemptyq[m->firstnonemptyq];
}
return result;
}
#endif /* not CDT_ONLY */
/*****************************************************************************/
/* */
/* checkseg4encroach() Check a subsegment to see if it is encroached; add */
/* it to the list if it is. */
/* */
/* A subsegment is encroached if there is a vertex in its diametral lens. */
/* For Ruppert's algorithm (-D switch), the "diametral lens" is the */
/* diametral circle. For Chew's algorithm (default), the diametral lens is */
/* just big enough to enclose two isosceles triangles whose bases are the */
/* subsegment. Each of the two isosceles triangles has two angles equal */
/* to `b->minangle'. */
/* */
/* Chew's algorithm does not require diametral lenses at all--but they save */
/* time. Any vertex inside a subsegment's diametral lens implies that the */
/* triangle adjoining the subsegment will be too skinny, so it's only a */
/* matter of time before the encroaching vertex is deleted by Chew's */
/* algorithm. It's faster to simply not insert the doomed vertex in the */
/* first place, which is why I use diametral lenses with Chew's algorithm. */
/* */
/* Returns a nonzero value if the subsegment is encroached. */
/* */
/*****************************************************************************/
#ifndef CDT_ONLY
#ifdef ANSI_DECLARATORS
int checkseg4encroach(struct mesh *m, struct behavior *b,
struct osub *testsubseg)
#else /* not ANSI_DECLARATORS */
int checkseg4encroach(m, b, testsubseg)
struct mesh *m;
struct behavior *b;
struct osub *testsubseg;
#endif /* not ANSI_DECLARATORS */
{
struct otri neighbortri;
struct osub testsym;
struct badsubseg *encroachedseg;
REAL dotproduct;
int encroached;
int sides;
vertex eorg, edest, eapex;
triangle ptr; /* Temporary variable used by stpivot(). */
encroached = 0;
sides = 0;
sorg(*testsubseg, eorg);
sdest(*testsubseg, edest);
/* Check one neighbor of the subsegment. */
stpivot(*testsubseg, neighbortri);
/* Does the neighbor exist, or is this a boundary edge? */
if (neighbortri.tri != m->dummytri) {
sides++;
/* Find a vertex opposite this subsegment. */
apex(neighbortri, eapex);
/* Check whether the apex is in the diametral lens of the subsegment */
/* (the diametral circle if `conformdel' is set). A dot product */
/* of two sides of the triangle is used to check whether the angle */
/* at the apex is greater than (180 - 2 `minangle') degrees (for */
/* lenses; 90 degrees for diametral circles). */
dotproduct = (eorg[0] - eapex[0]) * (edest[0] - eapex[0]) +
(eorg[1] - eapex[1]) * (edest[1] - eapex[1]);
if (dotproduct < 0.0) {
if (b->conformdel ||
(dotproduct * dotproduct >=
(2.0 * b->goodangle - 1.0) * (2.0 * b->goodangle - 1.0) *
((eorg[0] - eapex[0]) * (eorg[0] - eapex[0]) +
(eorg[1] - eapex[1]) * (eorg[1] - eapex[1])) *
((edest[0] - eapex[0]) * (edest[0] - eapex[0]) +
(edest[1] - eapex[1]) * (edest[1] - eapex[1])))) {
encroached = 1;
}
}
}
/* Check the other neighbor of the subsegment. */
ssym(*testsubseg, testsym);
stpivot(testsym, neighbortri);
/* Does the neighbor exist, or is this a boundary edge? */
if (neighbortri.tri != m->dummytri) {
sides++;
/* Find the other vertex opposite this subsegment. */
apex(neighbortri, eapex);
/* Check whether the apex is in the diametral lens of the subsegment */
/* (or the diametral circle, if `conformdel' is set). */
dotproduct = (eorg[0] - eapex[0]) * (edest[0] - eapex[0]) +
(eorg[1] - eapex[1]) * (edest[1] - eapex[1]);
if (dotproduct < 0.0) {
if (b->conformdel ||
(dotproduct * dotproduct >=
(2.0 * b->goodangle - 1.0) * (2.0 * b->goodangle - 1.0) *
((eorg[0] - eapex[0]) * (eorg[0] - eapex[0]) +
(eorg[1] - eapex[1]) * (eorg[1] - eapex[1])) *
((edest[0] - eapex[0]) * (edest[0] - eapex[0]) +
(edest[1] - eapex[1]) * (edest[1] - eapex[1])))) {
encroached += 2;
}
}
}
if (encroached && (!b->nobisect || ((b->nobisect == 1) && (sides == 2)))) {
if (b->verbose > 2) {
printf(
" Queueing encroached subsegment (%.12g, %.12g) (%.12g, %.12g).\n",
eorg[0], eorg[1], edest[0], edest[1]);
}
/* Add the subsegment to the list of encroached subsegments. */
/* Be sure to get the orientation right. */
encroachedseg = (struct badsubseg *) poolalloc(&m->badsubsegs);
if (encroached == 1) {
encroachedseg->encsubseg = sencode(*testsubseg);
encroachedseg->subsegorg = eorg;
encroachedseg->subsegdest = edest;
} else {
encroachedseg->encsubseg = sencode(testsym);
encroachedseg->subsegorg = edest;
encroachedseg->subsegdest = eorg;
}
}
return encroached;
}
#endif /* not CDT_ONLY */
/*****************************************************************************/
/* */
/* testtriangle() Test a triangle for quality and size. */
/* */
/* Tests a triangle to see if it satisfies the minimum angle condition and */
/* the maximum area condition. Triangles that aren't up to spec are added */
/* to the bad triangle queue. */
/* */
/*****************************************************************************/
#ifndef CDT_ONLY
#ifdef ANSI_DECLARATORS
void testtriangle(struct mesh *m, struct behavior *b, struct otri *testtri)
#else /* not ANSI_DECLARATORS */
void testtriangle(m, b, testtri)
struct mesh *m;
struct behavior *b;
struct otri *testtri;
#endif /* not ANSI_DECLARATORS */
{
struct otri tri1, tri2;
struct osub testsub;
vertex torg, tdest, tapex;
vertex base1, base2;
vertex org1, dest1, org2, dest2;
vertex joinvertex;
REAL dxod, dyod, dxda, dyda, dxao, dyao;
REAL dxod2, dyod2, dxda2, dyda2, dxao2, dyao2;
REAL apexlen, orglen, destlen, minedge;
REAL angle;
REAL area;
REAL dist1, dist2;
subseg sptr; /* Temporary variable used by tspivot(). */
triangle ptr; /* Temporary variable used by oprev() and dnext(). */
org(*testtri, torg);
dest(*testtri, tdest);
apex(*testtri, tapex);
dxod = torg[0] - tdest[0];
dyod = torg[1] - tdest[1];
dxda = tdest[0] - tapex[0];
dyda = tdest[1] - tapex[1];
dxao = tapex[0] - torg[0];
dyao = tapex[1] - torg[1];
dxod2 = dxod * dxod;
dyod2 = dyod * dyod;
dxda2 = dxda * dxda;
dyda2 = dyda * dyda;
dxao2 = dxao * dxao;
dyao2 = dyao * dyao;
/* Find the lengths of the triangle's three edges. */
apexlen = dxod2 + dyod2;
orglen = dxda2 + dyda2;
destlen = dxao2 + dyao2;
if ((apexlen < orglen) && (apexlen < destlen)) {
/* The edge opposite the apex is shortest. */
minedge = apexlen;
/* Find the square of the cosine of the angle at the apex. */
angle = dxda * dxao + dyda * dyao;
angle = angle * angle / (orglen * destlen);
base1 = torg;
base2 = tdest;
otricopy(*testtri, tri1);
} else if (orglen < destlen) {
/* The edge opposite the origin is shortest. */
minedge = orglen;
/* Find the square of the cosine of the angle at the origin. */
angle = dxod * dxao + dyod * dyao;
angle = angle * angle / (apexlen * destlen);
base1 = tdest;
base2 = tapex;
lnext(*testtri, tri1);
} else {
/* The edge opposite the destination is shortest. */
minedge = destlen;
/* Find the square of the cosine of the angle at the destination. */
angle = dxod * dxda + dyod * dyda;
angle = angle * angle / (apexlen * orglen);
base1 = tapex;
base2 = torg;
lprev(*testtri, tri1);
}
if (b->vararea || b->fixedarea || b->usertest) {
/* Check whether the area is larger than permitted. */
area = 0.5 * (dxod * dyda - dyod * dxda);
if (b->fixedarea && (area > b->maxarea)) {
/* Add this triangle to the list of bad triangles. */
enqueuebadtri(m, b, testtri, minedge, tapex, torg, tdest);
return;
}
/* Nonpositive area constraints are treated as unconstrained. */
if ((b->vararea) && (area > areabound(*testtri)) &&
(areabound(*testtri) > 0.0)) {
/* Add this triangle to the list of bad triangles. */
enqueuebadtri(m, b, testtri, minedge, tapex, torg, tdest);
return;
}
if (b->usertest) {
/* Check whether the user thinks this triangle is too large. */
if (triunsuitable(torg, tdest, tapex, area)) {
enqueuebadtri(m, b, testtri, minedge, tapex, torg, tdest);
return;
}
}
}
/* Check whether the angle is smaller than permitted. */
if (angle > b->goodangle) {
/* Use the rules of Miller, Pav, and Walkington to decide that certain */
/* triangles should not be split, even if they have bad angles. */
/* A skinny triangle is not split if its shortest edge subtends a */
/* small input angle, and both endpoints of the edge lie on a */
/* concentric circular shell. For convenience, I make a small */
/* adjustment to that rule: I check if the endpoints of the edge */
/* both lie in segment interiors, equidistant from the apex where */
/* the two segments meet. */
/* First, check if both points lie in segment interiors. */
if ((vertextype(base1) == SEGMENTVERTEX) &&
(vertextype(base2) == SEGMENTVERTEX)) {
/* Check if both points lie in a common segment. If they do, the */
/* skinny triangle is enqueued to be split as usual. */
tspivot(tri1, testsub);
if (testsub.ss == m->dummysub) {
/* No common segment. Find a subsegment that contains `torg'. */
otricopy(tri1, tri2);
do {
oprevself(tri1);
tspivot(tri1, testsub);
} while (testsub.ss == m->dummysub);
/* Find the endpoints of the containing segment. */
segorg(testsub, org1);
segdest(testsub, dest1);
/* Find a subsegment that contains `tdest'. */
do {
dnextself(tri2);
tspivot(tri2, testsub);
} while (testsub.ss == m->dummysub);
/* Find the endpoints of the containing segment. */
segorg(testsub, org2);
segdest(testsub, dest2);
/* Check if the two containing segments have an endpoint in common. */
joinvertex = (vertex) NULL;
if ((dest1[0] == org2[0]) && (dest1[1] == org2[1])) {
joinvertex = dest1;
} else if ((org1[0] == dest2[0]) && (org1[1] == dest2[1])) {
joinvertex = org1;
}
if (joinvertex != (vertex) NULL) {
/* Compute the distance from the common endpoint (of the two */
/* segments) to each of the endpoints of the shortest edge. */
dist1 = ((base1[0] - joinvertex[0]) * (base1[0] - joinvertex[0]) +
(base1[1] - joinvertex[1]) * (base1[1] - joinvertex[1]));
dist2 = ((base2[0] - joinvertex[0]) * (base2[0] - joinvertex[0]) +
(base2[1] - joinvertex[1]) * (base2[1] - joinvertex[1]));
/* If the two distances are equal, don't split the triangle. */
if ((dist1 < 1.001 * dist2) && (dist1 > 0.999 * dist2)) {
/* Return now to avoid enqueueing the bad triangle. */
return;
}
}
}
}
/* Add this triangle to the list of bad triangles. */
enqueuebadtri(m, b, testtri, minedge, tapex, torg, tdest);
}
}
#endif /* not CDT_ONLY */
/** **/
/** **/
/********* Mesh quality testing routines end here *********/
/********* Point location routines begin here *********/
/** **/
/** **/
/*****************************************************************************/
/* */
/* makevertexmap() Construct a mapping from vertices to triangles to */
/* improve the speed of point location for segment */
/* insertion. */
/* */
/* Traverses all the triangles, and provides each corner of each triangle */
/* with a pointer to that triangle. Of course, pointers will be */
/* overwritten by other pointers because (almost) each vertex is a corner */
/* of several triangles, but in the end every vertex will point to some */
/* triangle that contains it. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
void makevertexmap(struct mesh *m, struct behavior *b)
#else /* not ANSI_DECLARATORS */
void makevertexmap(m, b)
struct mesh *m;
struct behavior *b;
#endif /* not ANSI_DECLARATORS */
{
struct otri triangleloop;
vertex triorg;
if (b->verbose) {
printf(" Constructing mapping from vertices to triangles.\n");
}
traversalinit(&m->triangles);
triangleloop.tri = triangletraverse(m);
while (triangleloop.tri != (triangle *) NULL) {
/* Check all three vertices of the triangle. */
for (triangleloop.orient = 0; triangleloop.orient < 3;
triangleloop.orient++) {
org(triangleloop, triorg);
setvertex2tri(triorg, encode(triangleloop));
}
triangleloop.tri = triangletraverse(m);
}
}
/*****************************************************************************/
/* */
/* preciselocate() Find a triangle or edge containing a given point. */
/* */
/* Begins its search from `searchtri'. It is important that `searchtri' */
/* be a handle with the property that `searchpoint' is strictly to the left */
/* of the edge denoted by `searchtri', or is collinear with that edge and */
/* does not intersect that edge. (In particular, `searchpoint' should not */
/* be the origin or destination of that edge.) */
/* */
/* These conditions are imposed because preciselocate() is normally used in */
/* one of two situations: */
/* */
/* (1) To try to find the location to insert a new point. Normally, we */
/* know an edge that the point is strictly to the left of. In the */
/* incremental Delaunay algorithm, that edge is a bounding box edge. */
/* In Ruppert's Delaunay refinement algorithm for quality meshing, */
/* that edge is the shortest edge of the triangle whose circumcenter */
/* is being inserted. */
/* */
/* (2) To try to find an existing point. In this case, any edge on the */
/* convex hull is a good starting edge. You must screen out the */
/* possibility that the vertex sought is an endpoint of the starting */
/* edge before you call preciselocate(). */
/* */
/* On completion, `searchtri' is a triangle that contains `searchpoint'. */
/* */
/* This implementation differs from that given by Guibas and Stolfi. It */
/* walks from triangle to triangle, crossing an edge only if `searchpoint' */
/* is on the other side of the line containing that edge. After entering */
/* a triangle, there are two edges by which one can leave that triangle. */
/* If both edges are valid (`searchpoint' is on the other side of both */
/* edges), one of the two is chosen by drawing a line perpendicular to */
/* the entry edge (whose endpoints are `forg' and `fdest') passing through */
/* `fapex'. Depending on which side of this perpendicular `searchpoint' */
/* falls on, an exit edge is chosen. */
/* */
/* This implementation is empirically faster than the Guibas and Stolfi */
/* point location routine (which I originally used), which tends to spiral */
/* in toward its target. */
/* */
/* Returns ONVERTEX if the point lies on an existing vertex. `searchtri' */
/* is a handle whose origin is the existing vertex. */
/* */
/* Returns ONEDGE if the point lies on a mesh edge. `searchtri' is a */
/* handle whose primary edge is the edge on which the point lies. */
/* */
/* Returns INTRIANGLE if the point lies strictly within a triangle. */
/* `searchtri' is a handle on the triangle that contains the point. */
/* */
/* Returns OUTSIDE if the point lies outside the mesh. `searchtri' is a */
/* handle whose primary edge the point is to the right of. This might */
/* occur when the circumcenter of a triangle falls just slightly outside */
/* the mesh due to floating-point roundoff error. It also occurs when */
/* seeking a hole or region point that a foolish user has placed outside */
/* the mesh. */
/* */
/* If `stopatsubsegment' is nonzero, the search will stop if it tries to */
/* walk through a subsegment, and will return OUTSIDE. */
/* */
/* WARNING: This routine is designed for convex triangulations, and will */
/* not generally work after the holes and concavities have been carved. */
/* However, it can still be used to find the circumcenter of a triangle, as */
/* long as the search is begun from the triangle in question. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
enum locateresult preciselocate(struct mesh *m, struct behavior *b,
vertex searchpoint, struct otri *searchtri,
int stopatsubsegment)
#else /* not ANSI_DECLARATORS */
enum locateresult preciselocate(m, b, searchpoint, searchtri, stopatsubsegment)
struct mesh *m;
struct behavior *b;
vertex searchpoint;
struct otri *searchtri;
int stopatsubsegment;
#endif /* not ANSI_DECLARATORS */
{
struct otri backtracktri;
struct osub checkedge;
vertex forg, fdest, fapex;
REAL orgorient, destorient;
int moveleft;
triangle ptr; /* Temporary variable used by sym(). */
subseg sptr; /* Temporary variable used by tspivot(). */
if (b->verbose > 2) {
printf(" Searching for point (%.12g, %.12g).\n",
searchpoint[0], searchpoint[1]);
}
/* Where are we? */
org(*searchtri, forg);
dest(*searchtri, fdest);
apex(*searchtri, fapex);
while (1) {
if (b->verbose > 2) {
printf(" At (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n",
forg[0], forg[1], fdest[0], fdest[1], fapex[0], fapex[1]);
}
/* Check whether the apex is the point we seek. */
if ((fapex[0] == searchpoint[0]) && (fapex[1] == searchpoint[1])) {
lprevself(*searchtri);
return ONVERTEX;
}
/* Does the point lie on the other side of the line defined by the */
/* triangle edge opposite the triangle's destination? */
destorient = counterclockwise(m, b, forg, fapex, searchpoint);
/* Does the point lie on the other side of the line defined by the */
/* triangle edge opposite the triangle's origin? */
orgorient = counterclockwise(m, b, fapex, fdest, searchpoint);
if (destorient > 0.0) {
if (orgorient > 0.0) {
/* Move left if the inner product of (fapex - searchpoint) and */
/* (fdest - forg) is positive. This is equivalent to drawing */
/* a line perpendicular to the line (forg, fdest) and passing */
/* through `fapex', and determining which side of this line */
/* `searchpoint' falls on. */
moveleft = (fapex[0] - searchpoint[0]) * (fdest[0] - forg[0]) +
(fapex[1] - searchpoint[1]) * (fdest[1] - forg[1]) > 0.0;
} else {
moveleft = 1;
}
} else {
if (orgorient > 0.0) {
moveleft = 0;
} else {
/* The point we seek must be on the boundary of or inside this */
/* triangle. */
if (destorient == 0.0) {
lprevself(*searchtri);
return ONEDGE;
}
if (orgorient == 0.0) {
lnextself(*searchtri);
return ONEDGE;
}
return INTRIANGLE;
}
}
/* Move to another triangle. Leave a trace `backtracktri' in case */
/* floating-point roundoff or some such bogey causes us to walk */
/* off a boundary of the triangulation. */
if (moveleft) {
lprev(*searchtri, backtracktri);
fdest = fapex;
} else {
lnext(*searchtri, backtracktri);
forg = fapex;
}
sym(backtracktri, *searchtri);
if (m->checksegments && stopatsubsegment) {
/* Check for walking through a subsegment. */
tspivot(backtracktri, checkedge);
if (checkedge.ss != m->dummysub) {
/* Go back to the last triangle. */
otricopy(backtracktri, *searchtri);
return OUTSIDE;
}
}
/* Check for walking right out of the triangulation. */
if (searchtri->tri == m->dummytri) {
/* Go back to the last triangle. */
otricopy(backtracktri, *searchtri);
return OUTSIDE;
}
apex(*searchtri, fapex);
}
}
/*****************************************************************************/
/* */
/* locate() Find a triangle or edge containing a given point. */
/* */
/* Searching begins from one of: the input `searchtri', a recently */
/* encountered triangle `recenttri', or from a triangle chosen from a */
/* random sample. The choice is made by determining which triangle's */
/* origin is closest to the point we are searching for. Normally, */
/* `searchtri' should be a handle on the convex hull of the triangulation. */
/* */
/* Details on the random sampling method can be found in the Mucke, Saias, */
/* and Zhu paper cited in the header of this code. */
/* */
/* On completion, `searchtri' is a triangle that contains `searchpoint'. */
/* */
/* Returns ONVERTEX if the point lies on an existing vertex. `searchtri' */
/* is a handle whose origin is the existing vertex. */
/* */
/* Returns ONEDGE if the point lies on a mesh edge. `searchtri' is a */
/* handle whose primary edge is the edge on which the point lies. */
/* */
/* Returns INTRIANGLE if the point lies strictly within a triangle. */
/* `searchtri' is a handle on the triangle that contains the point. */
/* */
/* Returns OUTSIDE if the point lies outside the mesh. `searchtri' is a */
/* handle whose primary edge the point is to the right of. This might */
/* occur when the circumcenter of a triangle falls just slightly outside */
/* the mesh due to floating-point roundoff error. It also occurs when */
/* seeking a hole or region point that a foolish user has placed outside */
/* the mesh. */
/* */
/* WARNING: This routine is designed for convex triangulations, and will */
/* not generally work after the holes and concavities have been carved. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
enum locateresult locate(struct mesh *m, struct behavior *b,
vertex searchpoint, struct otri *searchtri)
#else /* not ANSI_DECLARATORS */
enum locateresult locate(m, b, searchpoint, searchtri)
struct mesh *m;
struct behavior *b;
vertex searchpoint;
struct otri *searchtri;
#endif /* not ANSI_DECLARATORS */
{
VOID **sampleblock;
char *firsttri;
struct otri sampletri;
vertex torg, tdest;
size_t alignptr;
REAL searchdist, dist;
REAL ahead;
long samplesperblock, totalsamplesleft, samplesleft;
long population, totalpopulation;
triangle ptr; /* Temporary variable used by sym(). */
if (b->verbose > 2) {
printf(" Randomly sampling for a triangle near point (%.12g, %.12g).\n",
searchpoint[0], searchpoint[1]);
}
/* Record the distance from the suggested starting triangle to the */
/* point we seek. */
org(*searchtri, torg);
searchdist = (searchpoint[0] - torg[0]) * (searchpoint[0] - torg[0]) +
(searchpoint[1] - torg[1]) * (searchpoint[1] - torg[1]);
if (b->verbose > 2) {
printf(" Boundary triangle has origin (%.12g, %.12g).\n",
torg[0], torg[1]);
}
/* If a recently encountered triangle has been recorded and has not been */
/* deallocated, test it as a good starting point. */
if (m->recenttri.tri != (triangle *) NULL) {
if (!deadtri(m->recenttri.tri)) {
org(m->recenttri, torg);
if ((torg[0] == searchpoint[0]) && (torg[1] == searchpoint[1])) {
otricopy(m->recenttri, *searchtri);
return ONVERTEX;
}
dist = (searchpoint[0] - torg[0]) * (searchpoint[0] - torg[0]) +
(searchpoint[1] - torg[1]) * (searchpoint[1] - torg[1]);
if (dist < searchdist) {
otricopy(m->recenttri, *searchtri);
searchdist = dist;
if (b->verbose > 2) {
printf(" Choosing recent triangle with origin (%.12g, %.12g).\n",
torg[0], torg[1]);
}
}
}
}
/* The number of random samples taken is proportional to the cube root of */
/* the number of triangles in the mesh. The next bit of code assumes */
/* that the number of triangles increases monotonically (or at least */
/* doesn't decrease enough to matter). */
while (SAMPLEFACTOR * m->samples * m->samples * m->samples <
m->triangles.items) {
m->samples++;
}
/* We'll draw ceiling(samples * TRIPERBLOCK / maxitems) random samples */
/* from each block of triangles (except the first)--until we meet the */
/* sample quota. The ceiling means that blocks at the end might be */
/* neglected, but I don't care. */
samplesperblock = (m->samples * TRIPERBLOCK - 1) / m->triangles.maxitems + 1;
/* We'll draw ceiling(samples * itemsfirstblock / maxitems) random samples */
/* from the first block of triangles. */
samplesleft = (m->samples * m->triangles.itemsfirstblock - 1) /
m->triangles.maxitems + 1;
totalsamplesleft = m->samples;
population = m->triangles.itemsfirstblock;
totalpopulation = m->triangles.maxitems;
sampleblock = m->triangles.firstblock;
sampletri.orient = 0;
while (totalsamplesleft > 0) {
/* If we're in the last block, `population' needs to be corrected. */
if (population > totalpopulation) {
population = totalpopulation;
}
/* Find a pointer to the first triangle in the block. */
alignptr = (size_t) (sampleblock + 1);
firsttri = (char *) (alignptr +
(size_t) m->triangles.alignbytes -
(alignptr %
(size_t) m->triangles.alignbytes));
/* Choose `samplesleft' randomly sampled triangles in this block. */
do {
sampletri.tri = (triangle *) (firsttri +
(randomnation((unsigned int) population) *
m->triangles.itembytes));
if (!deadtri(sampletri.tri)) {
org(sampletri, torg);
dist = (searchpoint[0] - torg[0]) * (searchpoint[0] - torg[0]) +
(searchpoint[1] - torg[1]) * (searchpoint[1] - torg[1]);
if (dist < searchdist) {
otricopy(sampletri, *searchtri);
searchdist = dist;
if (b->verbose > 2) {
printf(" Choosing triangle with origin (%.12g, %.12g).\n",
torg[0], torg[1]);
}
}
}
samplesleft--;
totalsamplesleft--;
} while ((samplesleft > 0) && (totalsamplesleft > 0));
if (totalsamplesleft > 0) {
sampleblock = (VOID **) *sampleblock;
samplesleft = samplesperblock;
totalpopulation -= population;
population = TRIPERBLOCK;
}
}
/* Where are we? */
org(*searchtri, torg);
dest(*searchtri, tdest);
/* Check the starting triangle's vertices. */
if ((torg[0] == searchpoint[0]) && (torg[1] == searchpoint[1])) {
return ONVERTEX;
}
if ((tdest[0] == searchpoint[0]) && (tdest[1] == searchpoint[1])) {
lnextself(*searchtri);
return ONVERTEX;
}
/* Orient `searchtri' to fit the preconditions of calling preciselocate(). */
ahead = counterclockwise(m, b, torg, tdest, searchpoint);
if (ahead < 0.0) {
/* Turn around so that `searchpoint' is to the left of the */
/* edge specified by `searchtri'. */
symself(*searchtri);
} else if (ahead == 0.0) {
/* Check if `searchpoint' is between `torg' and `tdest'. */
if (((torg[0] < searchpoint[0]) == (searchpoint[0] < tdest[0])) &&
((torg[1] < searchpoint[1]) == (searchpoint[1] < tdest[1]))) {
return ONEDGE;
}
}
return preciselocate(m, b, searchpoint, searchtri, 0);
}
/** **/
/** **/
/********* Point location routines end here *********/
/********* Mesh transformation routines begin here *********/
/** **/
/** **/
/*****************************************************************************/
/* */
/* insertsubseg() Create a new subsegment and insert it between two */
/* triangles. */
/* */
/* The new subsegment is inserted at the edge described by the handle */
/* `tri'. Its vertices are properly initialized. The marker `subsegmark' */
/* is applied to the subsegment and, if appropriate, its vertices. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
void insertsubseg(struct mesh *m, struct behavior *b, struct otri *tri,
int subsegmark)
#else /* not ANSI_DECLARATORS */
void insertsubseg(m, b, tri, subsegmark)
struct mesh *m;
struct behavior *b;
struct otri *tri; /* Edge at which to insert the new subsegment. */
int subsegmark; /* Marker for the new subsegment. */
#endif /* not ANSI_DECLARATORS */
{
struct otri oppotri;
struct osub newsubseg;
vertex triorg, tridest;
triangle ptr; /* Temporary variable used by sym(). */
subseg sptr; /* Temporary variable used by tspivot(). */
org(*tri, triorg);
dest(*tri, tridest);
/* Mark vertices if possible. */
if (vertexmark(triorg) == 0) {
setvertexmark(triorg, subsegmark);
}
if (vertexmark(tridest) == 0) {
setvertexmark(tridest, subsegmark);
}
/* Check if there's already a subsegment here. */
tspivot(*tri, newsubseg);
if (newsubseg.ss == m->dummysub) {
/* Make new subsegment and initialize its vertices. */
makesubseg(m, &newsubseg);
setsorg(newsubseg, tridest);
setsdest(newsubseg, triorg);
setsegorg(newsubseg, tridest);
setsegdest(newsubseg, triorg);
/* Bond new subsegment to the two triangles it is sandwiched between. */
/* Note that the facing triangle `oppotri' might be equal to */
/* `dummytri' (outer space), but the new subsegment is bonded to it */
/* all the same. */
tsbond(*tri, newsubseg);
sym(*tri, oppotri);
ssymself(newsubseg);
tsbond(oppotri, newsubseg);
setmark(newsubseg, subsegmark);
if (b->verbose > 2) {
printf(" Inserting new ");
printsubseg(m, b, &newsubseg);
}
} else {
if (mark(newsubseg) == 0) {
setmark(newsubseg, subsegmark);
}
}
}
/*****************************************************************************/
/* */
/* Terminology */
/* */
/* A "local transformation" replaces a small set of triangles with another */
/* set of triangles. This may or may not involve inserting or deleting a */
/* vertex. */
/* */
/* The term "casing" is used to describe the set of triangles that are */
/* attached to the triangles being transformed, but are not transformed */
/* themselves. Think of the casing as a fixed hollow structure inside */
/* which all the action happens. A "casing" is only defined relative to */
/* a single transformation; each occurrence of a transformation will */
/* involve a different casing. */
/* */
/*****************************************************************************/
/*****************************************************************************/
/* */
/* flip() Transform two triangles to two different triangles by flipping */
/* an edge counterclockwise within a quadrilateral. */
/* */
/* Imagine the original triangles, abc and bad, oriented so that the */
/* shared edge ab lies in a horizontal plane, with the vertex b on the left */
/* and the vertex a on the right. The vertex c lies below the edge, and */
/* the vertex d lies above the edge. The `flipedge' handle holds the edge */
/* ab of triangle abc, and is directed left, from vertex a to vertex b. */
/* */
/* The triangles abc and bad are deleted and replaced by the triangles cdb */
/* and dca. The triangles that represent abc and bad are NOT deallocated; */
/* they are reused for dca and cdb, respectively. Hence, any handles that */
/* may have held the original triangles are still valid, although not */
/* directed as they were before. */
/* */
/* Upon completion of this routine, the `flipedge' handle holds the edge */
/* dc of triangle dca, and is directed down, from vertex d to vertex c. */
/* (Hence, the two triangles have rotated counterclockwise.) */
/* */
/* WARNING: This transformation is geometrically valid only if the */
/* quadrilateral adbc is convex. Furthermore, this transformation is */
/* valid only if there is not a subsegment between the triangles abc and */
/* bad. This routine does not check either of these preconditions, and */
/* it is the responsibility of the calling routine to ensure that they are */
/* met. If they are not, the streets shall be filled with wailing and */
/* gnashing of teeth. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
void flip(struct mesh *m, struct behavior *b, struct otri *flipedge)
#else /* not ANSI_DECLARATORS */
void flip(m, b, flipedge)
struct mesh *m;
struct behavior *b;
struct otri *flipedge; /* Handle for the triangle abc. */
#endif /* not ANSI_DECLARATORS */
{
struct otri botleft, botright;
struct otri topleft, topright;
struct otri top;
struct otri botlcasing, botrcasing;
struct otri toplcasing, toprcasing;
struct osub botlsubseg, botrsubseg;
struct osub toplsubseg, toprsubseg;
vertex leftvertex, rightvertex, botvertex;
vertex farvertex;
triangle ptr; /* Temporary variable used by sym(). */
subseg sptr; /* Temporary variable used by tspivot(). */
/* Identify the vertices of the quadrilateral. */
org(*flipedge, rightvertex);
dest(*flipedge, leftvertex);
apex(*flipedge, botvertex);
sym(*flipedge, top);
#ifdef SELF_CHECK
if (top.tri == m->dummytri) {
printf("Internal error in flip(): Attempt to flip on boundary.\n");
lnextself(*flipedge);
return;
}
if (m->checksegments) {
tspivot(*flipedge, toplsubseg);
if (toplsubseg.ss != m->dummysub) {
printf("Internal error in flip(): Attempt to flip a segment.\n");
lnextself(*flipedge);
return;
}
}
#endif /* SELF_CHECK */
apex(top, farvertex);
/* Identify the casing of the quadrilateral. */
lprev(top, topleft);
sym(topleft, toplcasing);
lnext(top, topright);
sym(topright, toprcasing);
lnext(*flipedge, botleft);
sym(botleft, botlcasing);
lprev(*flipedge, botright);
sym(botright, botrcasing);
/* Rotate the quadrilateral one-quarter turn counterclockwise. */
bond(topleft, botlcasing);
bond(botleft, botrcasing);
bond(botright, toprcasing);
bond(topright, toplcasing);
if (m->checksegments) {
/* Check for subsegments and rebond them to the quadrilateral. */
tspivot(topleft, toplsubseg);
tspivot(botleft, botlsubseg);
tspivot(botright, botrsubseg);
tspivot(topright, toprsubseg);
if (toplsubseg.ss == m->dummysub) {
tsdissolve(topright);
} else {
tsbond(topright, toplsubseg);
}
if (botlsubseg.ss == m->dummysub) {
tsdissolve(topleft);
} else {
tsbond(topleft, botlsubseg);
}
if (botrsubseg.ss == m->dummysub) {
tsdissolve(botleft);
} else {
tsbond(botleft, botrsubseg);
}
if (toprsubseg.ss == m->dummysub) {
tsdissolve(botright);
} else {
tsbond(botright, toprsubseg);
}
}
/* New vertex assignments for the rotated quadrilateral. */
setorg(*flipedge, farvertex);
setdest(*flipedge, botvertex);
setapex(*flipedge, rightvertex);
setorg(top, botvertex);
setdest(top, farvertex);
setapex(top, leftvertex);
if (b->verbose > 2) {
printf(" Edge flip results in left ");
printtriangle(m, b, &top);
printf(" and right ");
printtriangle(m, b, flipedge);
}
}
/*****************************************************************************/
/* */
/* unflip() Transform two triangles to two different triangles by */
/* flipping an edge clockwise within a quadrilateral. Reverses */
/* the flip() operation so that the data structures representing */
/* the triangles are back where they were before the flip(). */
/* */
/* Imagine the original triangles, abc and bad, oriented so that the */
/* shared edge ab lies in a horizontal plane, with the vertex b on the left */
/* and the vertex a on the right. The vertex c lies below the edge, and */
/* the vertex d lies above the edge. The `flipedge' handle holds the edge */
/* ab of triangle abc, and is directed left, from vertex a to vertex b. */
/* */
/* The triangles abc and bad are deleted and replaced by the triangles cdb */
/* and dca. The triangles that represent abc and bad are NOT deallocated; */
/* they are reused for cdb and dca, respectively. Hence, any handles that */
/* may have held the original triangles are still valid, although not */
/* directed as they were before. */
/* */
/* Upon completion of this routine, the `flipedge' handle holds the edge */
/* cd of triangle cdb, and is directed up, from vertex c to vertex d. */
/* (Hence, the two triangles have rotated clockwise.) */
/* */
/* WARNING: This transformation is geometrically valid only if the */
/* quadrilateral adbc is convex. Furthermore, this transformation is */
/* valid only if there is not a subsegment between the triangles abc and */
/* bad. This routine does not check either of these preconditions, and */
/* it is the responsibility of the calling routine to ensure that they are */
/* met. If they are not, the streets shall be filled with wailing and */
/* gnashing of teeth. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
void unflip(struct mesh *m, struct behavior *b, struct otri *flipedge)
#else /* not ANSI_DECLARATORS */
void unflip(m, b, flipedge)
struct mesh *m;
struct behavior *b;
struct otri *flipedge; /* Handle for the triangle abc. */
#endif /* not ANSI_DECLARATORS */
{
struct otri botleft, botright;
struct otri topleft, topright;
struct otri top;
struct otri botlcasing, botrcasing;
struct otri toplcasing, toprcasing;
struct osub botlsubseg, botrsubseg;
struct osub toplsubseg, toprsubseg;
vertex leftvertex, rightvertex, botvertex;
vertex farvertex;
triangle ptr; /* Temporary variable used by sym(). */
subseg sptr; /* Temporary variable used by tspivot(). */
/* Identify the vertices of the quadrilateral. */
org(*flipedge, rightvertex);
dest(*flipedge, leftvertex);
apex(*flipedge, botvertex);
sym(*flipedge, top);
#ifdef SELF_CHECK
if (top.tri == m->dummytri) {
printf("Internal error in unflip(): Attempt to flip on boundary.\n");
lnextself(*flipedge);
return;
}
if (m->checksegments) {
tspivot(*flipedge, toplsubseg);
if (toplsubseg.ss != m->dummysub) {
printf("Internal error in unflip(): Attempt to flip a subsegment.\n");
lnextself(*flipedge);
return;
}
}
#endif /* SELF_CHECK */
apex(top, farvertex);
/* Identify the casing of the quadrilateral. */
lprev(top, topleft);
sym(topleft, toplcasing);
lnext(top, topright);
sym(topright, toprcasing);
lnext(*flipedge, botleft);
sym(botleft, botlcasing);
lprev(*flipedge, botright);
sym(botright, botrcasing);
/* Rotate the quadrilateral one-quarter turn clockwise. */
bond(topleft, toprcasing);
bond(botleft, toplcasing);
bond(botright, botlcasing);
bond(topright, botrcasing);
if (m->checksegments) {
/* Check for subsegments and rebond them to the quadrilateral. */
tspivot(topleft, toplsubseg);
tspivot(botleft, botlsubseg);
tspivot(botright, botrsubseg);
tspivot(topright, toprsubseg);
if (toplsubseg.ss == m->dummysub) {
tsdissolve(botleft);
} else {
tsbond(botleft, toplsubseg);
}
if (botlsubseg.ss == m->dummysub) {
tsdissolve(botright);
} else {
tsbond(botright, botlsubseg);
}
if (botrsubseg.ss == m->dummysub) {
tsdissolve(topright);
} else {
tsbond(topright, botrsubseg);
}
if (toprsubseg.ss == m->dummysub) {
tsdissolve(topleft);
} else {
tsbond(topleft, toprsubseg);
}
}
/* New vertex assignments for the rotated quadrilateral. */
setorg(*flipedge, botvertex);
setdest(*flipedge, farvertex);
setapex(*flipedge, leftvertex);
setorg(top, farvertex);
setdest(top, botvertex);
setapex(top, rightvertex);
if (b->verbose > 2) {
printf(" Edge unflip results in left ");
printtriangle(m, b, flipedge);
printf(" and right ");
printtriangle(m, b, &top);
}
}
/*****************************************************************************/
/* */
/* insertvertex() Insert a vertex into a Delaunay triangulation, */
/* performing flips as necessary to maintain the Delaunay */
/* property. */
/* */
/* The point `insertvertex' is located. If `searchtri.tri' is not NULL, */
/* the search for the containing triangle begins from `searchtri'. If */
/* `searchtri.tri' is NULL, a full point location procedure is called. */
/* If `insertvertex' is found inside a triangle, the triangle is split into */
/* three; if `insertvertex' lies on an edge, the edge is split in two, */
/* thereby splitting the two adjacent triangles into four. Edge flips are */
/* used to restore the Delaunay property. If `insertvertex' lies on an */
/* existing vertex, no action is taken, and the value DUPLICATEVERTEX is */
/* returned. On return, `searchtri' is set to a handle whose origin is the */
/* existing vertex. */
/* */
/* Normally, the parameter `splitseg' is set to NULL, implying that no */
/* subsegment should be split. In this case, if `insertvertex' is found to */
/* lie on a segment, no action is taken, and the value VIOLATINGVERTEX is */
/* returned. On return, `searchtri' is set to a handle whose primary edge */
/* is the violated subsegment. */
/* */
/* If the calling routine wishes to split a subsegment by inserting a */
/* vertex in it, the parameter `splitseg' should be that subsegment. In */
/* this case, `searchtri' MUST be the triangle handle reached by pivoting */
/* from that subsegment; no point location is done. */
/* */
/* `segmentflaws' and `triflaws' are flags that indicate whether or not */
/* there should be checks for the creation of encroached subsegments or bad */
/* quality triangles. If a newly inserted vertex encroaches upon */
/* subsegments, these subsegments are added to the list of subsegments to */
/* be split if `segmentflaws' is set. If bad triangles are created, these */
/* are added to the queue if `triflaws' is set. */
/* */
/* If a duplicate vertex or violated segment does not prevent the vertex */
/* from being inserted, the return value will be ENCROACHINGVERTEX if the */
/* vertex encroaches upon a subsegment (and checking is enabled), or */
/* SUCCESSFULVERTEX otherwise. In either case, `searchtri' is set to a */
/* handle whose origin is the newly inserted vertex. */
/* */
/* insertvertex() does not use flip() for reasons of speed; some */
/* information can be reused from edge flip to edge flip, like the */
/* locations of subsegments. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
enum insertvertexresult insertvertex(struct mesh *m, struct behavior *b,
vertex newvertex, struct otri *searchtri,
struct osub *splitseg,
int segmentflaws, int triflaws)
#else /* not ANSI_DECLARATORS */
enum insertvertexresult insertvertex(m, b, newvertex, searchtri, splitseg,
segmentflaws, triflaws)
struct mesh *m;
struct behavior *b;
vertex newvertex;
struct otri *searchtri;
struct osub *splitseg;
int segmentflaws;
int triflaws;
#endif /* not ANSI_DECLARATORS */
{
struct otri horiz;
struct otri top;
struct otri botleft, botright;
struct otri topleft, topright;
struct otri newbotleft, newbotright;
struct otri newtopright;
struct otri botlcasing, botrcasing;
struct otri toplcasing, toprcasing;
struct otri testtri;
struct osub botlsubseg, botrsubseg;
struct osub toplsubseg, toprsubseg;
struct osub brokensubseg;
struct osub checksubseg;
struct osub rightsubseg;
struct osub newsubseg;
struct badsubseg *encroached;
struct flipstacker *newflip;
vertex first;
vertex leftvertex, rightvertex, botvertex, topvertex, farvertex;
vertex segmentorg, segmentdest;
REAL attrib;
REAL area;
enum insertvertexresult success;
enum locateresult intersect;
int doflip;
int mirrorflag;
int enq;
int i;
triangle ptr; /* Temporary variable used by sym(). */
subseg sptr; /* Temporary variable used by spivot() and tspivot(). */
if (b->verbose > 1) {
printf(" Inserting (%.12g, %.12g).\n", newvertex[0], newvertex[1]);
}
if (splitseg == (struct osub *) NULL) {
/* Find the location of the vertex to be inserted. Check if a good */
/* starting triangle has already been provided by the caller. */
if (searchtri->tri == m->dummytri) {
/* Find a boundary triangle. */
horiz.tri = m->dummytri;
horiz.orient = 0;
symself(horiz);
/* Search for a triangle containing `newvertex'. */
intersect = locate(m, b, newvertex, &horiz);
} else {
/* Start searching from the triangle provided by the caller. */
otricopy(*searchtri, horiz);
intersect = preciselocate(m, b, newvertex, &horiz, 1);
}
} else {
/* The calling routine provides the subsegment in which */
/* the vertex is inserted. */
otricopy(*searchtri, horiz);
intersect = ONEDGE;
}
if (intersect == ONVERTEX) {
/* There's already a vertex there. Return in `searchtri' a triangle */
/* whose origin is the existing vertex. */
otricopy(horiz, *searchtri);
otricopy(horiz, m->recenttri);
return DUPLICATEVERTEX;
}
if ((intersect == ONEDGE) || (intersect == OUTSIDE)) {
/* The vertex falls on an edge or boundary. */
if (m->checksegments && (splitseg == (struct osub *) NULL)) {
/* Check whether the vertex falls on a subsegment. */
tspivot(horiz, brokensubseg);
if (brokensubseg.ss != m->dummysub) {
/* The vertex falls on a subsegment, and hence will not be inserted. */
if (segmentflaws) {
enq = b->nobisect != 2;
if (enq && (b->nobisect == 1)) {
/* This subsegment may be split only if it is an */
/* internal boundary. */
sym(horiz, testtri);
enq = testtri.tri != m->dummytri;
}
if (enq) {
/* Add the subsegment to the list of encroached subsegments. */
encroached = (struct badsubseg *) poolalloc(&m->badsubsegs);
encroached->encsubseg = sencode(brokensubseg);
sorg(brokensubseg, encroached->subsegorg);
sdest(brokensubseg, encroached->subsegdest);
if (b->verbose > 2) {
printf(
" Queueing encroached subsegment (%.12g, %.12g) (%.12g, %.12g).\n",
encroached->subsegorg[0], encroached->subsegorg[1],
encroached->subsegdest[0], encroached->subsegdest[1]);
}
}
}
/* Return a handle whose primary edge contains the vertex, */
/* which has not been inserted. */
otricopy(horiz, *searchtri);
otricopy(horiz, m->recenttri);
return VIOLATINGVERTEX;
}
}
/* Insert the vertex on an edge, dividing one triangle into two (if */
/* the edge lies on a boundary) or two triangles into four. */
lprev(horiz, botright);
sym(botright, botrcasing);
sym(horiz, topright);
/* Is there a second triangle? (Or does this edge lie on a boundary?) */
mirrorflag = topright.tri != m->dummytri;
if (mirrorflag) {
lnextself(topright);
sym(topright, toprcasing);
maketriangle(m, b, &newtopright);
} else {
/* Splitting a boundary edge increases the number of boundary edges. */
m->hullsize++;
}
maketriangle(m, b, &newbotright);
/* Set the vertices of changed and new triangles. */
org(horiz, rightvertex);
dest(horiz, leftvertex);
apex(horiz, botvertex);
setorg(newbotright, botvertex);
setdest(newbotright, rightvertex);
setapex(newbotright, newvertex);
setorg(horiz, newvertex);
for (i = 0; i < m->eextras; i++) {
/* Set the element attributes of a new triangle. */
setelemattribute(newbotright, i, elemattribute(botright, i));
}
if (b->vararea) {
/* Set the area constraint of a new triangle. */
setareabound(newbotright, areabound(botright));
}
if (mirrorflag) {
dest(topright, topvertex);
setorg(newtopright, rightvertex);
setdest(newtopright, topvertex);
setapex(newtopright, newvertex);
setorg(topright, newvertex);
for (i = 0; i < m->eextras; i++) {
/* Set the element attributes of another new triangle. */
setelemattribute(newtopright, i, elemattribute(topright, i));
}
if (b->vararea) {
/* Set the area constraint of another new triangle. */
setareabound(newtopright, areabound(topright));
}
}
/* There may be subsegments that need to be bonded */
/* to the new triangle(s). */
if (m->checksegments) {
tspivot(botright, botrsubseg);
if (botrsubseg.ss != m->dummysub) {
tsdissolve(botright);
tsbond(newbotright, botrsubseg);
}
if (mirrorflag) {
tspivot(topright, toprsubseg);
if (toprsubseg.ss != m->dummysub) {
tsdissolve(topright);
tsbond(newtopright, toprsubseg);
}
}
}
/* Bond the new triangle(s) to the surrounding triangles. */
bond(newbotright, botrcasing);
lprevself(newbotright);
bond(newbotright, botright);
lprevself(newbotright);
if (mirrorflag) {
bond(newtopright, toprcasing);
lnextself(newtopright);
bond(newtopright, topright);
lnextself(newtopright);
bond(newtopright, newbotright);
}
if (splitseg != (struct osub *) NULL) {
/* Split the subsegment into two. */
setsdest(*splitseg, newvertex);
segorg(*splitseg, segmentorg);
segdest(*splitseg, segmentdest);
ssymself(*splitseg);
spivot(*splitseg, rightsubseg);
insertsubseg(m, b, &newbotright, mark(*splitseg));
tspivot(newbotright, newsubseg);
setsegorg(newsubseg, segmentorg);
setsegdest(newsubseg, segmentdest);
sbond(*splitseg, newsubseg);
ssymself(newsubseg);
sbond(newsubseg, rightsubseg);
ssymself(*splitseg);
/* Transfer the subsegment's boundary marker to the vertex */
/* if required. */
if (vertexmark(newvertex) == 0) {
setvertexmark(newvertex, mark(*splitseg));
}
}
if (m->checkquality) {
poolrestart(&m->flipstackers);
m->lastflip = (struct flipstacker *) poolalloc(&m->flipstackers);
m->lastflip->flippedtri = encode(horiz);
m->lastflip->prevflip = (struct flipstacker *) &insertvertex;
}
#ifdef SELF_CHECK
if (counterclockwise(m, b, rightvertex, leftvertex, botvertex) < 0.0) {
printf("Internal error in insertvertex():\n");
printf(
" Clockwise triangle prior to edge vertex insertion (bottom).\n");
}
if (mirrorflag) {
if (counterclockwise(m, b, leftvertex, rightvertex, topvertex) < 0.0) {
printf("Internal error in insertvertex():\n");
printf(" Clockwise triangle prior to edge vertex insertion (top).\n");
}
if (counterclockwise(m, b, rightvertex, topvertex, newvertex) < 0.0) {
printf("Internal error in insertvertex():\n");
printf(
" Clockwise triangle after edge vertex insertion (top right).\n");
}
if (counterclockwise(m, b, topvertex, leftvertex, newvertex) < 0.0) {
printf("Internal error in insertvertex():\n");
printf(
" Clockwise triangle after edge vertex insertion (top left).\n");
}
}
if (counterclockwise(m, b, leftvertex, botvertex, newvertex) < 0.0) {
printf("Internal error in insertvertex():\n");
printf(
" Clockwise triangle after edge vertex insertion (bottom left).\n");
}
if (counterclockwise(m, b, botvertex, rightvertex, newvertex) < 0.0) {
printf("Internal error in insertvertex():\n");
printf(
" Clockwise triangle after edge vertex insertion (bottom right).\n");
}
#endif /* SELF_CHECK */
if (b->verbose > 2) {
printf(" Updating bottom left ");
printtriangle(m, b, &botright);
if (mirrorflag) {
printf(" Updating top left ");
printtriangle(m, b, &topright);
printf(" Creating top right ");
printtriangle(m, b, &newtopright);
}
printf(" Creating bottom right ");
printtriangle(m, b, &newbotright);
}
/* Position `horiz' on the first edge to check for */
/* the Delaunay property. */
lnextself(horiz);
} else {
/* Insert the vertex in a triangle, splitting it into three. */
lnext(horiz, botleft);
lprev(horiz, botright);
sym(botleft, botlcasing);
sym(botright, botrcasing);
maketriangle(m, b, &newbotleft);
maketriangle(m, b, &newbotright);
/* Set the vertices of changed and new triangles. */
org(horiz, rightvertex);
dest(horiz, leftvertex);
apex(horiz, botvertex);
setorg(newbotleft, leftvertex);
setdest(newbotleft, botvertex);
setapex(newbotleft, newvertex);
setorg(newbotright, botvertex);
setdest(newbotright, rightvertex);
setapex(newbotright, newvertex);
setapex(horiz, newvertex);
for (i = 0; i < m->eextras; i++) {
/* Set the element attributes of the new triangles. */
attrib = elemattribute(horiz, i);
setelemattribute(newbotleft, i, attrib);
setelemattribute(newbotright, i, attrib);
}
if (b->vararea) {
/* Set the area constraint of the new triangles. */
area = areabound(horiz);
setareabound(newbotleft, area);
setareabound(newbotright, area);
}
/* There may be subsegments that need to be bonded */
/* to the new triangles. */
if (m->checksegments) {
tspivot(botleft, botlsubseg);
if (botlsubseg.ss != m->dummysub) {
tsdissolve(botleft);
tsbond(newbotleft, botlsubseg);
}
tspivot(botright, botrsubseg);
if (botrsubseg.ss != m->dummysub) {
tsdissolve(botright);
tsbond(newbotright, botrsubseg);
}
}
/* Bond the new triangles to the surrounding triangles. */
bond(newbotleft, botlcasing);
bond(newbotright, botrcasing);
lnextself(newbotleft);
lprevself(newbotright);
bond(newbotleft, newbotright);
lnextself(newbotleft);
bond(botleft, newbotleft);
lprevself(newbotright);
bond(botright, newbotright);
if (m->checkquality) {
poolrestart(&m->flipstackers);
m->lastflip = (struct flipstacker *) poolalloc(&m->flipstackers);
m->lastflip->flippedtri = encode(horiz);
m->lastflip->prevflip = (struct flipstacker *) NULL;
}
#ifdef SELF_CHECK
if (counterclockwise(m, b, rightvertex, leftvertex, botvertex) < 0.0) {
printf("Internal error in insertvertex():\n");
printf(" Clockwise triangle prior to vertex insertion.\n");
}
if (counterclockwise(m, b, rightvertex, leftvertex, newvertex) < 0.0) {
printf("Internal error in insertvertex():\n");
printf(" Clockwise triangle after vertex insertion (top).\n");
}
if (counterclockwise(m, b, leftvertex, botvertex, newvertex) < 0.0) {
printf("Internal error in insertvertex():\n");
printf(" Clockwise triangle after vertex insertion (left).\n");
}
if (counterclockwise(m, b, botvertex, rightvertex, newvertex) < 0.0) {
printf("Internal error in insertvertex():\n");
printf(" Clockwise triangle after vertex insertion (right).\n");
}
#endif /* SELF_CHECK */
if (b->verbose > 2) {
printf(" Updating top ");
printtriangle(m, b, &horiz);
printf(" Creating left ");
printtriangle(m, b, &newbotleft);
printf(" Creating right ");
printtriangle(m, b, &newbotright);
}
}
/* The insertion is successful by default, unless an encroached */
/* subsegment is found. */
success = SUCCESSFULVERTEX;
/* Circle around the newly inserted vertex, checking each edge opposite */
/* it for the Delaunay property. Non-Delaunay edges are flipped. */
/* `horiz' is always the edge being checked. `first' marks where to */
/* stop circling. */
org(horiz, first);
rightvertex = first;
dest(horiz, leftvertex);
/* Circle until finished. */
while (1) {
/* By default, the edge will be flipped. */
doflip = 1;
if (m->checksegments) {
/* Check for a subsegment, which cannot be flipped. */
tspivot(horiz, checksubseg);
if (checksubseg.ss != m->dummysub) {
/* The edge is a subsegment and cannot be flipped. */
doflip = 0;
#ifndef CDT_ONLY
if (segmentflaws) {
/* Does the new vertex encroach upon this subsegment? */
if (checkseg4encroach(m, b, &checksubseg)) {
success = ENCROACHINGVERTEX;
}
}
#endif /* not CDT_ONLY */
}
}
if (doflip) {
/* Check if the edge is a boundary edge. */
sym(horiz, top);
if (top.tri == m->dummytri) {
/* The edge is a boundary edge and cannot be flipped. */
doflip = 0;
} else {
/* Find the vertex on the other side of the edge. */
apex(top, farvertex);
/* In the incremental Delaunay triangulation algorithm, any of */
/* `leftvertex', `rightvertex', and `farvertex' could be vertices */
/* of the triangular bounding box. These vertices must be */
/* treated as if they are infinitely distant, even though their */
/* "coordinates" are not. */
if ((leftvertex == m->infvertex1) || (leftvertex == m->infvertex2) ||
(leftvertex == m->infvertex3)) {
/* `leftvertex' is infinitely distant. Check the convexity of */
/* the boundary of the triangulation. 'farvertex' might be */
/* infinite as well, but trust me, this same condition should */
/* be applied. */
doflip = counterclockwise(m, b, newvertex, rightvertex, farvertex)
> 0.0;
} else if ((rightvertex == m->infvertex1) ||
(rightvertex == m->infvertex2) ||
(rightvertex == m->infvertex3)) {
/* `rightvertex' is infinitely distant. Check the convexity of */
/* the boundary of the triangulation. 'farvertex' might be */
/* infinite as well, but trust me, this same condition should */
/* be applied. */
doflip = counterclockwise(m, b, farvertex, leftvertex, newvertex)
> 0.0;
} else if ((farvertex == m->infvertex1) ||
(farvertex == m->infvertex2) ||
(farvertex == m->infvertex3)) {
/* `farvertex' is infinitely distant and cannot be inside */
/* the circumcircle of the triangle `horiz'. */
doflip = 0;
} else {
/* Test whether the edge is locally Delaunay. */
doflip = incircle(m, b, leftvertex, newvertex, rightvertex,
farvertex) > 0.0;
}
if (doflip) {
/* We made it! Flip the edge `horiz' by rotating its containing */
/* quadrilateral (the two triangles adjacent to `horiz'). */
/* Identify the casing of the quadrilateral. */
lprev(top, topleft);
sym(topleft, toplcasing);
lnext(top, topright);
sym(topright, toprcasing);
lnext(horiz, botleft);
sym(botleft, botlcasing);
lprev(horiz, botright);
sym(botright, botrcasing);
/* Rotate the quadrilateral one-quarter turn counterclockwise. */
bond(topleft, botlcasing);
bond(botleft, botrcasing);
bond(botright, toprcasing);
bond(topright, toplcasing);
if (m->checksegments) {
/* Check for subsegments and rebond them to the quadrilateral. */
tspivot(topleft, toplsubseg);
tspivot(botleft, botlsubseg);
tspivot(botright, botrsubseg);
tspivot(topright, toprsubseg);
if (toplsubseg.ss == m->dummysub) {
tsdissolve(topright);
} else {
tsbond(topright, toplsubseg);
}
if (botlsubseg.ss == m->dummysub) {
tsdissolve(topleft);
} else {
tsbond(topleft, botlsubseg);
}
if (botrsubseg.ss == m->dummysub) {
tsdissolve(botleft);
} else {
tsbond(botleft, botrsubseg);
}
if (toprsubseg.ss == m->dummysub) {
tsdissolve(botright);
} else {
tsbond(botright, toprsubseg);
}
}
/* New vertex assignments for the rotated quadrilateral. */
setorg(horiz, farvertex);
setdest(horiz, newvertex);
setapex(horiz, rightvertex);
setorg(top, newvertex);
setdest(top, farvertex);
setapex(top, leftvertex);
for (i = 0; i < m->eextras; i++) {
/* Take the average of the two triangles' attributes. */
attrib = 0.5 * (elemattribute(top, i) + elemattribute(horiz, i));
setelemattribute(top, i, attrib);
setelemattribute(horiz, i, attrib);
}
if (b->vararea) {
if ((areabound(top) <= 0.0) || (areabound(horiz) <= 0.0)) {
area = -1.0;
} else {
/* Take the average of the two triangles' area constraints. */
/* This prevents small area constraints from migrating a */
/* long, long way from their original location due to flips. */
area = 0.5 * (areabound(top) + areabound(horiz));
}
setareabound(top, area);
setareabound(horiz, area);
}
if (m->checkquality) {
newflip = (struct flipstacker *) poolalloc(&m->flipstackers);
newflip->flippedtri = encode(horiz);
newflip->prevflip = m->lastflip;
m->lastflip = newflip;
}
#ifdef SELF_CHECK
if (newvertex != (vertex) NULL) {
if (counterclockwise(m, b, leftvertex, newvertex, rightvertex) <
0.0) {
printf("Internal error in insertvertex():\n");
printf(" Clockwise triangle prior to edge flip (bottom).\n");
}
/* The following test has been removed because constrainededge() */
/* sometimes generates inverted triangles that insertvertex() */
/* removes. */
/*
if (counterclockwise(m, b, rightvertex, farvertex, leftvertex) <
0.0) {
printf("Internal error in insertvertex():\n");
printf(" Clockwise triangle prior to edge flip (top).\n");
}
*/
if (counterclockwise(m, b, farvertex, leftvertex, newvertex) <
0.0) {
printf("Internal error in insertvertex():\n");
printf(" Clockwise triangle after edge flip (left).\n");
}
if (counterclockwise(m, b, newvertex, rightvertex, farvertex) <
0.0) {
printf("Internal error in insertvertex():\n");
printf(" Clockwise triangle after edge flip (right).\n");
}
}
#endif /* SELF_CHECK */
if (b->verbose > 2) {
printf(" Edge flip results in left ");
lnextself(topleft);
printtriangle(m, b, &topleft);
printf(" and right ");
printtriangle(m, b, &horiz);
}
/* On the next iterations, consider the two edges that were */
/* exposed (this is, are now visible to the newly inserted */
/* vertex) by the edge flip. */
lprevself(horiz);
leftvertex = farvertex;
}
}
}
if (!doflip) {
/* The handle `horiz' is accepted as locally Delaunay. */
#ifndef CDT_ONLY
if (triflaws) {
/* Check the triangle `horiz' for quality. */
testtriangle(m, b, &horiz);
}
#endif /* not CDT_ONLY */
/* Look for the next edge around the newly inserted vertex. */
lnextself(horiz);
sym(horiz, testtri);
/* Check for finishing a complete revolution about the new vertex, or */
/* falling outside of the triangulation. The latter will happen */
/* when a vertex is inserted at a boundary. */
if ((leftvertex == first) || (testtri.tri == m->dummytri)) {
/* We're done. Return a triangle whose origin is the new vertex. */
lnext(horiz, *searchtri);
lnext(horiz, m->recenttri);
return success;
}
/* Finish finding the next edge around the newly inserted vertex. */
lnext(testtri, horiz);
rightvertex = leftvertex;
dest(horiz, leftvertex);
}
}
}
/*****************************************************************************/
/* */
/* triangulatepolygon() Find the Delaunay triangulation of a polygon that */
/* has a certain "nice" shape. This includes the */
/* polygons that result from deletion of a vertex or */
/* insertion of a segment. */
/* */
/* This is a conceptually difficult routine. The starting assumption is */
/* that we have a polygon with n sides. n - 1 of these sides are currently */
/* represented as edges in the mesh. One side, called the "base", need not */
/* be. */
/* */
/* Inside the polygon is a structure I call a "fan", consisting of n - 1 */
/* triangles that share a common origin. For each of these triangles, the */
/* edge opposite the origin is one of the sides of the polygon. The */
/* primary edge of each triangle is the edge directed from the origin to */
/* the destination; note that this is not the same edge that is a side of */
/* the polygon. `firstedge' is the primary edge of the first triangle. */
/* From there, the triangles follow in counterclockwise order about the */
/* polygon, until `lastedge', the primary edge of the last triangle. */
/* `firstedge' and `lastedge' are probably connected to other triangles */
/* beyond the extremes of the fan, but their identity is not important, as */
/* long as the fan remains connected to them. */
/* */
/* Imagine the polygon oriented so that its base is at the bottom. This */
/* puts `firstedge' on the far right, and `lastedge' on the far left. */
/* The right vertex of the base is the destination of `firstedge', and the */
/* left vertex of the base is the apex of `lastedge'. */
/* */
/* The challenge now is to find the right sequence of edge flips to */
/* transform the fan into a Delaunay triangulation of the polygon. Each */
/* edge flip effectively removes one triangle from the fan, committing it */
/* to the polygon. The resulting polygon has one fewer edge. If `doflip' */
/* is set, the final flip will be performed, resulting in a fan of one */
/* (useless?) triangle. If `doflip' is not set, the final flip is not */
/* performed, resulting in a fan of two triangles, and an unfinished */
/* triangular polygon that is not yet filled out with a single triangle. */
/* On completion of the routine, `lastedge' is the last remaining triangle, */
/* or the leftmost of the last two. */
/* */
/* Although the flips are performed in the order described above, the */
/* decisions about what flips to perform are made in precisely the reverse */
/* order. The recursive triangulatepolygon() procedure makes a decision, */
/* uses up to two recursive calls to triangulate the "subproblems" */
/* (polygons with fewer edges), and then performs an edge flip. */
/* */
/* The "decision" it makes is which vertex of the polygon should be */
/* connected to the base. This decision is made by testing every possible */
/* vertex. Once the best vertex is found, the two edges that connect this */
/* vertex to the base become the bases for two smaller polygons. These */
/* are triangulated recursively. Unfortunately, this approach can take */
/* O(n^2) time not only in the worst case, but in many common cases. It's */
/* rarely a big deal for vertex deletion, where n is rarely larger than */
/* ten, but it could be a big deal for segment insertion, especially if */
/* there's a lot of long segments that each cut many triangles. I ought to */
/* code a faster algorithm some day. */
/* */
/* The `edgecount' parameter is the number of sides of the polygon, */
/* including its base. `triflaws' is a flag that determines whether the */
/* new triangles should be tested for quality, and enqueued if they are */
/* bad. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
void triangulatepolygon(struct mesh *m, struct behavior *b,
struct otri *firstedge, struct otri *lastedge,
int edgecount, int doflip, int triflaws)
#else /* not ANSI_DECLARATORS */
void triangulatepolygon(m, b, firstedge, lastedge, edgecount, doflip, triflaws)
struct mesh *m;
struct behavior *b;
struct otri *firstedge;
struct otri *lastedge;
int edgecount;
int doflip;
int triflaws;
#endif /* not ANSI_DECLARATORS */
{
struct otri testtri;
struct otri besttri;
struct otri tempedge;
vertex leftbasevertex, rightbasevertex;
vertex testvertex;
vertex bestvertex;
int bestnumber;
int i;
triangle ptr; /* Temporary variable used by sym(), onext(), and oprev(). */
/* Identify the base vertices. */
apex(*lastedge, leftbasevertex);
dest(*firstedge, rightbasevertex);
if (b->verbose > 2) {
printf(" Triangulating interior polygon at edge\n");
printf(" (%.12g, %.12g) (%.12g, %.12g)\n", leftbasevertex[0],
leftbasevertex[1], rightbasevertex[0], rightbasevertex[1]);
}
/* Find the best vertex to connect the base to. */
onext(*firstedge, besttri);
dest(besttri, bestvertex);
otricopy(besttri, testtri);
bestnumber = 1;
for (i = 2; i <= edgecount - 2; i++) {
onextself(testtri);
dest(testtri, testvertex);
/* Is this a better vertex? */
if (incircle(m, b, leftbasevertex, rightbasevertex, bestvertex,
testvertex) > 0.0) {
otricopy(testtri, besttri);
bestvertex = testvertex;
bestnumber = i;
}
}
if (b->verbose > 2) {
printf(" Connecting edge to (%.12g, %.12g)\n", bestvertex[0],
bestvertex[1]);
}
if (bestnumber > 1) {
/* Recursively triangulate the smaller polygon on the right. */
oprev(besttri, tempedge);
triangulatepolygon(m, b, firstedge, &tempedge, bestnumber + 1, 1,
triflaws);
}
if (bestnumber < edgecount - 2) {
/* Recursively triangulate the smaller polygon on the left. */
sym(besttri, tempedge);
triangulatepolygon(m, b, &besttri, lastedge, edgecount - bestnumber, 1,
triflaws);
/* Find `besttri' again; it may have been lost to edge flips. */
sym(tempedge, besttri);
}
if (doflip) {
/* Do one final edge flip. */
flip(m, b, &besttri);
#ifndef CDT_ONLY
if (triflaws) {
/* Check the quality of the newly committed triangle. */
sym(besttri, testtri);
testtriangle(m, b, &testtri);
}
#endif /* not CDT_ONLY */
}
/* Return the base triangle. */
otricopy(besttri, *lastedge);
}
/*****************************************************************************/
/* */
/* deletevertex() Delete a vertex from a Delaunay triangulation, ensuring */
/* that the triangulation remains Delaunay. */
/* */
/* The origin of `deltri' is deleted. The union of the triangles adjacent */
/* to this vertex is a polygon, for which the Delaunay triangulation is */
/* found. Two triangles are removed from the mesh. */
/* */
/* Only interior vertices that do not lie on segments or boundaries may be */
/* deleted. */
/* */
/*****************************************************************************/
#ifndef CDT_ONLY
#ifdef ANSI_DECLARATORS
void deletevertex(struct mesh *m, struct behavior *b, struct otri *deltri)
#else /* not ANSI_DECLARATORS */
void deletevertex(m, b, deltri)
struct mesh *m;
struct behavior *b;
struct otri *deltri;
#endif /* not ANSI_DECLARATORS */
{
struct otri countingtri;
struct otri firstedge, lastedge;
struct otri deltriright;
struct otri lefttri, righttri;
struct otri leftcasing, rightcasing;
struct osub leftsubseg, rightsubseg;
vertex delvertex;
vertex neworg;
int edgecount;
triangle ptr; /* Temporary variable used by sym(), onext(), and oprev(). */
subseg sptr; /* Temporary variable used by tspivot(). */
org(*deltri, delvertex);
if (b->verbose > 1) {
printf(" Deleting (%.12g, %.12g).\n", delvertex[0], delvertex[1]);
}
vertexdealloc(m, delvertex);
/* Count the degree of the vertex being deleted. */
onext(*deltri, countingtri);
edgecount = 1;
while (!otriequal(*deltri, countingtri)) {
#ifdef SELF_CHECK
if (countingtri.tri == m->dummytri) {
printf("Internal error in deletevertex():\n");
printf(" Attempt to delete boundary vertex.\n");
internalerror();
}
#endif /* SELF_CHECK */
edgecount++;
onextself(countingtri);
}
#ifdef SELF_CHECK
if (edgecount < 3) {
printf("Internal error in deletevertex():\n Vertex has degree %d.\n",
edgecount);
internalerror();
}
#endif /* SELF_CHECK */
if (edgecount > 3) {
/* Triangulate the polygon defined by the union of all triangles */
/* adjacent to the vertex being deleted. Check the quality of */
/* the resulting triangles. */
onext(*deltri, firstedge);
oprev(*deltri, lastedge);
triangulatepolygon(m, b, &firstedge, &lastedge, edgecount, 0,
!b->nobisect);
}
/* Splice out two triangles. */
lprev(*deltri, deltriright);
dnext(*deltri, lefttri);
sym(lefttri, leftcasing);
oprev(deltriright, righttri);
sym(righttri, rightcasing);
bond(*deltri, leftcasing);
bond(deltriright, rightcasing);
tspivot(lefttri, leftsubseg);
if (leftsubseg.ss != m->dummysub) {
tsbond(*deltri, leftsubseg);
}
tspivot(righttri, rightsubseg);
if (rightsubseg.ss != m->dummysub) {
tsbond(deltriright, rightsubseg);
}
/* Set the new origin of `deltri' and check its quality. */
org(lefttri, neworg);
setorg(*deltri, neworg);
if (!b->nobisect) {
testtriangle(m, b, deltri);
}
/* Delete the two spliced-out triangles. */
triangledealloc(m, lefttri.tri);
triangledealloc(m, righttri.tri);
}
#endif /* not CDT_ONLY */
/*****************************************************************************/
/* */
/* undovertex() Undo the most recent vertex insertion. */
/* */
/* Walks through the list of transformations (flips and a vertex insertion) */
/* in the reverse of the order in which they were done, and undoes them. */
/* The inserted vertex is removed from the triangulation and deallocated. */
/* Two triangles (possibly just one) are also deallocated. */
/* */
/*****************************************************************************/
#ifndef CDT_ONLY
#ifdef ANSI_DECLARATORS
void undovertex(struct mesh *m, struct behavior *b)
#else /* not ANSI_DECLARATORS */
void undovertex(m, b)
struct mesh *m;
struct behavior *b;
#endif /* not ANSI_DECLARATORS */
{
struct otri fliptri;
struct otri botleft, botright, topright;
struct otri botlcasing, botrcasing, toprcasing;
struct otri gluetri;
struct osub botlsubseg, botrsubseg, toprsubseg;
vertex botvertex, rightvertex;
triangle ptr; /* Temporary variable used by sym(). */
subseg sptr; /* Temporary variable used by tspivot(). */
/* Walk through the list of transformations (flips and a vertex insertion) */
/* in the reverse of the order in which they were done, and undo them. */
while (m->lastflip != (struct flipstacker *) NULL) {
/* Find a triangle involved in the last unreversed transformation. */
decode(m->lastflip->flippedtri, fliptri);
/* We are reversing one of three transformations: a trisection of one */
/* triangle into three (by inserting a vertex in the triangle), a */
/* bisection of two triangles into four (by inserting a vertex in an */
/* edge), or an edge flip. */
if (m->lastflip->prevflip == (struct flipstacker *) NULL) {
/* Restore a triangle that was split into three triangles, */
/* so it is again one triangle. */
dprev(fliptri, botleft);
lnextself(botleft);
onext(fliptri, botright);
lprevself(botright);
sym(botleft, botlcasing);
sym(botright, botrcasing);
dest(botleft, botvertex);
setapex(fliptri, botvertex);
lnextself(fliptri);
bond(fliptri, botlcasing);
tspivot(botleft, botlsubseg);
tsbond(fliptri, botlsubseg);
lnextself(fliptri);
bond(fliptri, botrcasing);
tspivot(botright, botrsubseg);
tsbond(fliptri, botrsubseg);
/* Delete the two spliced-out triangles. */
triangledealloc(m, botleft.tri);
triangledealloc(m, botright.tri);
} else if (m->lastflip->prevflip == (struct flipstacker *) &insertvertex) {
/* Restore two triangles that were split into four triangles, */
/* so they are again two triangles. */
lprev(fliptri, gluetri);
sym(gluetri, botright);
lnextself(botright);
sym(botright, botrcasing);
dest(botright, rightvertex);
setorg(fliptri, rightvertex);
bond(gluetri, botrcasing);
tspivot(botright, botrsubseg);
tsbond(gluetri, botrsubseg);
/* Delete the spliced-out triangle. */
triangledealloc(m, botright.tri);
sym(fliptri, gluetri);
if (gluetri.tri != m->dummytri) {
lnextself(gluetri);
dnext(gluetri, topright);
sym(topright, toprcasing);
setorg(gluetri, rightvertex);
bond(gluetri, toprcasing);
tspivot(topright, toprsubseg);
tsbond(gluetri, toprsubseg);
/* Delete the spliced-out triangle. */
triangledealloc(m, topright.tri);
}
/* This is the end of the list, sneakily encoded. */
m->lastflip->prevflip = (struct flipstacker *) NULL;
} else {
/* Undo an edge flip. */
unflip(m, b, &fliptri);
}
/* Go on and process the next transformation. */
m->lastflip = m->lastflip->prevflip;
}
}
#endif /* not CDT_ONLY */
/** **/
/** **/
/********* Mesh transformation routines end here *********/
/********* Divide-and-conquer Delaunay triangulation begins here *********/
/** **/
/** **/
/*****************************************************************************/
/* */
/* The divide-and-conquer bounding box */
/* */
/* I originally implemented the divide-and-conquer and incremental Delaunay */
/* triangulations using the edge-based data structure presented by Guibas */
/* and Stolfi. Switching to a triangle-based data structure doubled the */
/* speed. However, I had to think of a few extra tricks to maintain the */
/* elegance of the original algorithms. */
/* */
/* The "bounding box" used by my variant of the divide-and-conquer */
/* algorithm uses one triangle for each edge of the convex hull of the */
/* triangulation. These bounding triangles all share a common apical */
/* vertex, which is represented by NULL and which represents nothing. */
/* The bounding triangles are linked in a circular fan about this NULL */
/* vertex, and the edges on the convex hull of the triangulation appear */
/* opposite the NULL vertex. You might find it easiest to imagine that */
/* the NULL vertex is a point in 3D space behind the center of the */
/* triangulation, and that the bounding triangles form a sort of cone. */
/* */
/* This bounding box makes it easy to represent degenerate cases. For */
/* instance, the triangulation of two vertices is a single edge. This edge */
/* is represented by two bounding box triangles, one on each "side" of the */
/* edge. These triangles are also linked together in a fan about the NULL */
/* vertex. */
/* */
/* The bounding box also makes it easy to traverse the convex hull, as the */
/* divide-and-conquer algorithm needs to do. */
/* */
/*****************************************************************************/
/*****************************************************************************/
/* */
/* vertexsort() Sort an array of vertices by x-coordinate, using the */
/* y-coordinate as a secondary key. */
/* */
/* Uses quicksort. Randomized O(n log n) time. No, I did not make any of */
/* the usual quicksort mistakes. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
void vertexsort(vertex *sortarray, int arraysize)
#else /* not ANSI_DECLARATORS */
void vertexsort(sortarray, arraysize)
vertex *sortarray;
int arraysize;
#endif /* not ANSI_DECLARATORS */
{
int left, right;
int pivot;
REAL pivotx, pivoty;
vertex temp;
if (arraysize == 2) {
/* Recursive base case. */
if ((sortarray[0][0] > sortarray[1][0]) ||
((sortarray[0][0] == sortarray[1][0]) &&
(sortarray[0][1] > sortarray[1][1]))) {
temp = sortarray[1];
sortarray[1] = sortarray[0];
sortarray[0] = temp;
}
return;
}
/* Choose a random pivot to split the array. */
pivot = (int) randomnation((unsigned int) arraysize);
pivotx = sortarray[pivot][0];
pivoty = sortarray[pivot][1];
/* Split the array. */
left = -1;
right = arraysize;
while (left < right) {
/* Search for a vertex whose x-coordinate is too large for the left. */
do {
left++;
} while ((left <= right) && ((sortarray[left][0] < pivotx) ||
((sortarray[left][0] == pivotx) &&
(sortarray[left][1] < pivoty))));
/* Search for a vertex whose x-coordinate is too small for the right. */
do {
right--;
} while ((left <= right) && ((sortarray[right][0] > pivotx) ||
((sortarray[right][0] == pivotx) &&
(sortarray[right][1] > pivoty))));
if (left < right) {
/* Swap the left and right vertices. */
temp = sortarray[left];
sortarray[left] = sortarray[right];
sortarray[right] = temp;
}
}
if (left > 1) {
/* Recursively sort the left subset. */
vertexsort(sortarray, left);
}
if (right < arraysize - 2) {
/* Recursively sort the right subset. */
vertexsort(&sortarray[right + 1], arraysize - right - 1);
}
}
/*****************************************************************************/
/* */
/* vertexmedian() An order statistic algorithm, almost. Shuffles an */
/* array of vertices so that the first `median' vertices */
/* occur lexicographically before the remaining vertices. */
/* */
/* Uses the x-coordinate as the primary key if axis == 0; the y-coordinate */
/* if axis == 1. Very similar to the vertexsort() procedure, but runs in */
/* randomized linear time. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
void vertexmedian(vertex *sortarray, int arraysize, int median, int axis)
#else /* not ANSI_DECLARATORS */
void vertexmedian(sortarray, arraysize, median, axis)
vertex *sortarray;
int arraysize;
int median;
int axis;
#endif /* not ANSI_DECLARATORS */
{
int left, right;
int pivot;
REAL pivot1, pivot2;
vertex temp;
if (arraysize == 2) {
/* Recursive base case. */
if ((sortarray[0][axis] > sortarray[1][axis]) ||
((sortarray[0][axis] == sortarray[1][axis]) &&
(sortarray[0][1 - axis] > sortarray[1][1 - axis]))) {
temp = sortarray[1];
sortarray[1] = sortarray[0];
sortarray[0] = temp;
}
return;
}
/* Choose a random pivot to split the array. */
pivot = (int) randomnation((unsigned int) arraysize);
pivot1 = sortarray[pivot][axis];
pivot2 = sortarray[pivot][1 - axis];
/* Split the array. */
left = -1;
right = arraysize;
while (left < right) {
/* Search for a vertex whose x-coordinate is too large for the left. */
do {
left++;
} while ((left <= right) && ((sortarray[left][axis] < pivot1) ||
((sortarray[left][axis] == pivot1) &&
(sortarray[left][1 - axis] < pivot2))));
/* Search for a vertex whose x-coordinate is too small for the right. */
do {
right--;
} while ((left <= right) && ((sortarray[right][axis] > pivot1) ||
((sortarray[right][axis] == pivot1) &&
(sortarray[right][1 - axis] > pivot2))));
if (left < right) {
/* Swap the left and right vertices. */
temp = sortarray[left];
sortarray[left] = sortarray[right];
sortarray[right] = temp;
}
}
/* Unlike in vertexsort(), at most one of the following */
/* conditionals is true. */
if (left > median) {
/* Recursively shuffle the left subset. */
vertexmedian(sortarray, left, median, axis);
}
if (right < median - 1) {
/* Recursively shuffle the right subset. */
vertexmedian(&sortarray[right + 1], arraysize - right - 1,
median - right - 1, axis);
}
}
/*****************************************************************************/
/* */
/* alternateaxes() Sorts the vertices as appropriate for the divide-and- */
/* conquer algorithm with alternating cuts. */
/* */
/* Partitions by x-coordinate if axis == 0; by y-coordinate if axis == 1. */
/* For the base case, subsets containing only two or three vertices are */
/* always sorted by x-coordinate. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
void alternateaxes(vertex *sortarray, int arraysize, int axis)
#else /* not ANSI_DECLARATORS */
void alternateaxes(sortarray, arraysize, axis)
vertex *sortarray;
int arraysize;
int axis;
#endif /* not ANSI_DECLARATORS */
{
int divider;
divider = arraysize >> 1;
if (arraysize <= 3) {
/* Recursive base case: subsets of two or three vertices will be */
/* handled specially, and should always be sorted by x-coordinate. */
axis = 0;
}
/* Partition with a horizontal or vertical cut. */
vertexmedian(sortarray, arraysize, divider, axis);
/* Recursively partition the subsets with a cross cut. */
if (arraysize - divider >= 2) {
if (divider >= 2) {
alternateaxes(sortarray, divider, 1 - axis);
}
alternateaxes(&sortarray[divider], arraysize - divider, 1 - axis);
}
}
/*****************************************************************************/
/* */
/* mergehulls() Merge two adjacent Delaunay triangulations into a */
/* single Delaunay triangulation. */
/* */
/* This is similar to the algorithm given by Guibas and Stolfi, but uses */
/* a triangle-based, rather than edge-based, data structure. */
/* */
/* The algorithm walks up the gap between the two triangulations, knitting */
/* them together. As they are merged, some of their bounding triangles */
/* are converted into real triangles of the triangulation. The procedure */
/* pulls each hull's bounding triangles apart, then knits them together */
/* like the teeth of two gears. The Delaunay property determines, at each */
/* step, whether the next "tooth" is a bounding triangle of the left hull */
/* or the right. When a bounding triangle becomes real, its apex is */
/* changed from NULL to a real vertex. */
/* */
/* Only two new triangles need to be allocated. These become new bounding */
/* triangles at the top and bottom of the seam. They are used to connect */
/* the remaining bounding triangles (those that have not been converted */
/* into real triangles) into a single fan. */
/* */
/* On entry, `farleft' and `innerleft' are bounding triangles of the left */
/* triangulation. The origin of `farleft' is the leftmost vertex, and */
/* the destination of `innerleft' is the rightmost vertex of the */
/* triangulation. Similarly, `innerright' and `farright' are bounding */
/* triangles of the right triangulation. The origin of `innerright' and */
/* destination of `farright' are the leftmost and rightmost vertices. */
/* */
/* On completion, the origin of `farleft' is the leftmost vertex of the */
/* merged triangulation, and the destination of `farright' is the rightmost */
/* vertex. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
void mergehulls(struct mesh *m, struct behavior *b, struct otri *farleft,
struct otri *innerleft, struct otri *innerright,
struct otri *farright, int axis)
#else /* not ANSI_DECLARATORS */
void mergehulls(m, b, farleft, innerleft, innerright, farright, axis)
struct mesh *m;
struct behavior *b;
struct otri *farleft;
struct otri *innerleft;
struct otri *innerright;
struct otri *farright;
int axis;
#endif /* not ANSI_DECLARATORS */
{
struct otri leftcand, rightcand;
struct otri baseedge;
struct otri nextedge;
struct otri sidecasing, topcasing, outercasing;
struct otri checkedge;
vertex innerleftdest;
vertex innerrightorg;
vertex innerleftapex, innerrightapex;
vertex farleftpt, farrightpt;
vertex farleftapex, farrightapex;
vertex lowerleft, lowerright;
vertex upperleft, upperright;
vertex nextapex;
vertex checkvertex;
int changemade;
int badedge;
int leftfinished, rightfinished;
triangle ptr; /* Temporary variable used by sym(). */
dest(*innerleft, innerleftdest);
apex(*innerleft, innerleftapex);
org(*innerright, innerrightorg);
apex(*innerright, innerrightapex);
/* Special treatment for horizontal cuts. */
if (b->dwyer && (axis == 1)) {
org(*farleft, farleftpt);
apex(*farleft, farleftapex);
dest(*farright, farrightpt);
apex(*farright, farrightapex);
/* The pointers to the extremal vertices are shifted to point to the */
/* topmost and bottommost vertex of each hull, rather than the */
/* leftmost and rightmost vertices. */
while (farleftapex[1] < farleftpt[1]) {
lnextself(*farleft);
symself(*farleft);
farleftpt = farleftapex;
apex(*farleft, farleftapex);
}
sym(*innerleft, checkedge);
apex(checkedge, checkvertex);
while (checkvertex[1] > innerleftdest[1]) {
lnext(checkedge, *innerleft);
innerleftapex = innerleftdest;
innerleftdest = checkvertex;
sym(*innerleft, checkedge);
apex(checkedge, checkvertex);
}
while (innerrightapex[1] < innerrightorg[1]) {
lnextself(*innerright);
symself(*innerright);
innerrightorg = innerrightapex;
apex(*innerright, innerrightapex);
}
sym(*farright, checkedge);
apex(checkedge, checkvertex);
while (checkvertex[1] > farrightpt[1]) {
lnext(checkedge, *farright);
farrightapex = farrightpt;
farrightpt = checkvertex;
sym(*farright, checkedge);
apex(checkedge, checkvertex);
}
}
/* Find a line tangent to and below both hulls. */
do {
changemade = 0;
/* Make innerleftdest the "bottommost" vertex of the left hull. */
if (counterclockwise(m, b, innerleftdest, innerleftapex, innerrightorg) >
0.0) {
lprevself(*innerleft);
symself(*innerleft);
innerleftdest = innerleftapex;
apex(*innerleft, innerleftapex);
changemade = 1;
}
/* Make innerrightorg the "bottommost" vertex of the right hull. */
if (counterclockwise(m, b, innerrightapex, innerrightorg, innerleftdest) >
0.0) {
lnextself(*innerright);
symself(*innerright);
innerrightorg = innerrightapex;
apex(*innerright, innerrightapex);
changemade = 1;
}
} while (changemade);
/* Find the two candidates to be the next "gear tooth." */
sym(*innerleft, leftcand);
sym(*innerright, rightcand);
/* Create the bottom new bounding triangle. */
maketriangle(m, b, &baseedge);
/* Connect it to the bounding boxes of the left and right triangulations. */
bond(baseedge, *innerleft);
lnextself(baseedge);
bond(baseedge, *innerright);
lnextself(baseedge);
setorg(baseedge, innerrightorg);
setdest(baseedge, innerleftdest);
/* Apex is intentionally left NULL. */
if (b->verbose > 2) {
printf(" Creating base bounding ");
printtriangle(m, b, &baseedge);
}
/* Fix the extreme triangles if necessary. */
org(*farleft, farleftpt);
if (innerleftdest == farleftpt) {
lnext(baseedge, *farleft);
}
dest(*farright, farrightpt);
if (innerrightorg == farrightpt) {
lprev(baseedge, *farright);
}
/* The vertices of the current knitting edge. */
lowerleft = innerleftdest;
lowerright = innerrightorg;
/* The candidate vertices for knitting. */
apex(leftcand, upperleft);
apex(rightcand, upperright);
/* Walk up the gap between the two triangulations, knitting them together. */
while (1) {
/* Have we reached the top? (This isn't quite the right question, */
/* because even though the left triangulation might seem finished now, */
/* moving up on the right triangulation might reveal a new vertex of */
/* the left triangulation. And vice-versa.) */
leftfinished = counterclockwise(m, b, upperleft, lowerleft, lowerright) <=
0.0;
rightfinished = counterclockwise(m, b, upperright, lowerleft, lowerright)
<= 0.0;
if (leftfinished && rightfinished) {
/* Create the top new bounding triangle. */
maketriangle(m, b, &nextedge);
setorg(nextedge, lowerleft);
setdest(nextedge, lowerright);
/* Apex is intentionally left NULL. */
/* Connect it to the bounding boxes of the two triangulations. */
bond(nextedge, baseedge);
lnextself(nextedge);
bond(nextedge, rightcand);
lnextself(nextedge);
bond(nextedge, leftcand);
if (b->verbose > 2) {
printf(" Creating top bounding ");
printtriangle(m, b, &nextedge);
}
/* Special treatment for horizontal cuts. */
if (b->dwyer && (axis == 1)) {
org(*farleft, farleftpt);
apex(*farleft, farleftapex);
dest(*farright, farrightpt);
apex(*farright, farrightapex);
sym(*farleft, checkedge);
apex(checkedge, checkvertex);
/* The pointers to the extremal vertices are restored to the */
/* leftmost and rightmost vertices (rather than topmost and */
/* bottommost). */
while (checkvertex[0] < farleftpt[0]) {
lprev(checkedge, *farleft);
farleftapex = farleftpt;
farleftpt = checkvertex;
sym(*farleft, checkedge);
apex(checkedge, checkvertex);
}
while (farrightapex[0] > farrightpt[0]) {
lprevself(*farright);
symself(*farright);
farrightpt = farrightapex;
apex(*farright, farrightapex);
}
}
return;
}
/* Consider eliminating edges from the left triangulation. */
if (!leftfinished) {
/* What vertex would be exposed if an edge were deleted? */
lprev(leftcand, nextedge);
symself(nextedge);
apex(nextedge, nextapex);
/* If nextapex is NULL, then no vertex would be exposed; the */
/* triangulation would have been eaten right through. */
if (nextapex != (vertex) NULL) {
/* Check whether the edge is Delaunay. */
badedge = incircle(m, b, lowerleft, lowerright, upperleft, nextapex) >
0.0;
while (badedge) {
/* Eliminate the edge with an edge flip. As a result, the */
/* left triangulation will have one more boundary triangle. */
lnextself(nextedge);
sym(nextedge, topcasing);
lnextself(nextedge);
sym(nextedge, sidecasing);
bond(nextedge, topcasing);
bond(leftcand, sidecasing);
lnextself(leftcand);
sym(leftcand, outercasing);
lprevself(nextedge);
bond(nextedge, outercasing);
/* Correct the vertices to reflect the edge flip. */
setorg(leftcand, lowerleft);
setdest(leftcand, NULL);
setapex(leftcand, nextapex);
setorg(nextedge, NULL);
setdest(nextedge, upperleft);
setapex(nextedge, nextapex);
/* Consider the newly exposed vertex. */
upperleft = nextapex;
/* What vertex would be exposed if another edge were deleted? */
otricopy(sidecasing, nextedge);
apex(nextedge, nextapex);
if (nextapex != (vertex) NULL) {
/* Check whether the edge is Delaunay. */
badedge = incircle(m, b, lowerleft, lowerright, upperleft,
nextapex) > 0.0;
} else {
/* Avoid eating right through the triangulation. */
badedge = 0;
}
}
}
}
/* Consider eliminating edges from the right triangulation. */
if (!rightfinished) {
/* What vertex would be exposed if an edge were deleted? */
lnext(rightcand, nextedge);
symself(nextedge);
apex(nextedge, nextapex);
/* If nextapex is NULL, then no vertex would be exposed; the */
/* triangulation would have been eaten right through. */
if (nextapex != (vertex) NULL) {
/* Check whether the edge is Delaunay. */
badedge = incircle(m, b, lowerleft, lowerright, upperright, nextapex) >
0.0;
while (badedge) {
/* Eliminate the edge with an edge flip. As a result, the */
/* right triangulation will have one more boundary triangle. */
lprevself(nextedge);
sym(nextedge, topcasing);
lprevself(nextedge);
sym(nextedge, sidecasing);
bond(nextedge, topcasing);
bond(rightcand, sidecasing);
lprevself(rightcand);
sym(rightcand, outercasing);
lnextself(nextedge);
bond(nextedge, outercasing);
/* Correct the vertices to reflect the edge flip. */
setorg(rightcand, NULL);
setdest(rightcand, lowerright);
setapex(rightcand, nextapex);
setorg(nextedge, upperright);
setdest(nextedge, NULL);
setapex(nextedge, nextapex);
/* Consider the newly exposed vertex. */
upperright = nextapex;
/* What vertex would be exposed if another edge were deleted? */
otricopy(sidecasing, nextedge);
apex(nextedge, nextapex);
if (nextapex != (vertex) NULL) {
/* Check whether the edge is Delaunay. */
badedge = incircle(m, b, lowerleft, lowerright, upperright,
nextapex) > 0.0;
} else {
/* Avoid eating right through the triangulation. */
badedge = 0;
}
}
}
}
if (leftfinished || (!rightfinished &&
(incircle(m, b, upperleft, lowerleft, lowerright, upperright) >
0.0))) {
/* Knit the triangulations, adding an edge from `lowerleft' */
/* to `upperright'. */
bond(baseedge, rightcand);
lprev(rightcand, baseedge);
setdest(baseedge, lowerleft);
lowerright = upperright;
sym(baseedge, rightcand);
apex(rightcand, upperright);
} else {
/* Knit the triangulations, adding an edge from `upperleft' */
/* to `lowerright'. */
bond(baseedge, leftcand);
lnext(leftcand, baseedge);
setorg(baseedge, lowerright);
lowerleft = upperleft;
sym(baseedge, leftcand);
apex(leftcand, upperleft);
}
if (b->verbose > 2) {
printf(" Connecting ");
printtriangle(m, b, &baseedge);
}
}
}
/*****************************************************************************/
/* */
/* divconqrecurse() Recursively form a Delaunay triangulation by the */
/* divide-and-conquer method. */
/* */
/* Recursively breaks down the problem into smaller pieces, which are */
/* knitted together by mergehulls(). The base cases (problems of two or */
/* three vertices) are handled specially here. */
/* */
/* On completion, `farleft' and `farright' are bounding triangles such that */
/* the origin of `farleft' is the leftmost vertex (breaking ties by */
/* choosing the highest leftmost vertex), and the destination of */
/* `farright' is the rightmost vertex (breaking ties by choosing the */
/* lowest rightmost vertex). */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
void divconqrecurse(struct mesh *m, struct behavior *b, vertex *sortarray,
int vertices, int axis,
struct otri *farleft, struct otri *farright)
#else /* not ANSI_DECLARATORS */
void divconqrecurse(m, b, sortarray, vertices, axis, farleft, farright)
struct mesh *m;
struct behavior *b;
vertex *sortarray;
int vertices;
int axis;
struct otri *farleft;
struct otri *farright;
#endif /* not ANSI_DECLARATORS */
{
struct otri midtri, tri1, tri2, tri3;
struct otri innerleft, innerright;
REAL area;
int divider;
if (b->verbose > 2) {
printf(" Triangulating %d vertices.\n", vertices);
}
if (vertices == 2) {
/* The triangulation of two vertices is an edge. An edge is */
/* represented by two bounding triangles. */
maketriangle(m, b, farleft);
setorg(*farleft, sortarray[0]);
setdest(*farleft, sortarray[1]);
/* The apex is intentionally left NULL. */
maketriangle(m, b, farright);
setorg(*farright, sortarray[1]);
setdest(*farright, sortarray[0]);
/* The apex is intentionally left NULL. */
bond(*farleft, *farright);
lprevself(*farleft);
lnextself(*farright);
bond(*farleft, *farright);
lprevself(*farleft);
lnextself(*farright);
bond(*farleft, *farright);
if (b->verbose > 2) {
printf(" Creating ");
printtriangle(m, b, farleft);
printf(" Creating ");
printtriangle(m, b, farright);
}
/* Ensure that the origin of `farleft' is sortarray[0]. */
lprev(*farright, *farleft);
return;
} else if (vertices == 3) {
/* The triangulation of three vertices is either a triangle (with */
/* three bounding triangles) or two edges (with four bounding */
/* triangles). In either case, four triangles are created. */
maketriangle(m, b, &midtri);
maketriangle(m, b, &tri1);
maketriangle(m, b, &tri2);
maketriangle(m, b, &tri3);
area = counterclockwise(m, b, sortarray[0], sortarray[1], sortarray[2]);
if (area == 0.0) {
/* Three collinear vertices; the triangulation is two edges. */
setorg(midtri, sortarray[0]);
setdest(midtri, sortarray[1]);
setorg(tri1, sortarray[1]);
setdest(tri1, sortarray[0]);
setorg(tri2, sortarray[2]);
setdest(tri2, sortarray[1]);
setorg(tri3, sortarray[1]);
setdest(tri3, sortarray[2]);
/* All apices are intentionally left NULL. */
bond(midtri, tri1);
bond(tri2, tri3);
lnextself(midtri);
lprevself(tri1);
lnextself(tri2);
lprevself(tri3);
bond(midtri, tri3);
bond(tri1, tri2);
lnextself(midtri);
lprevself(tri1);
lnextself(tri2);
lprevself(tri3);
bond(midtri, tri1);
bond(tri2, tri3);
/* Ensure that the origin of `farleft' is sortarray[0]. */
otricopy(tri1, *farleft);
/* Ensure that the destination of `farright' is sortarray[2]. */
otricopy(tri2, *farright);
} else {
/* The three vertices are not collinear; the triangulation is one */
/* triangle, namely `midtri'. */
setorg(midtri, sortarray[0]);
setdest(tri1, sortarray[0]);
setorg(tri3, sortarray[0]);
/* Apices of tri1, tri2, and tri3 are left NULL. */
if (area > 0.0) {
/* The vertices are in counterclockwise order. */
setdest(midtri, sortarray[1]);
setorg(tri1, sortarray[1]);
setdest(tri2, sortarray[1]);
setapex(midtri, sortarray[2]);
setorg(tri2, sortarray[2]);
setdest(tri3, sortarray[2]);
} else {
/* The vertices are in clockwise order. */
setdest(midtri, sortarray[2]);
setorg(tri1, sortarray[2]);
setdest(tri2, sortarray[2]);
setapex(midtri, sortarray[1]);
setorg(tri2, sortarray[1]);
setdest(tri3, sortarray[1]);
}
/* The topology does not depend on how the vertices are ordered. */
bond(midtri, tri1);
lnextself(midtri);
bond(midtri, tri2);
lnextself(midtri);
bond(midtri, tri3);
lprevself(tri1);
lnextself(tri2);
bond(tri1, tri2);
lprevself(tri1);
lprevself(tri3);
bond(tri1, tri3);
lnextself(tri2);
lprevself(tri3);
bond(tri2, tri3);
/* Ensure that the origin of `farleft' is sortarray[0]. */
otricopy(tri1, *farleft);
/* Ensure that the destination of `farright' is sortarray[2]. */
if (area > 0.0) {
otricopy(tri2, *farright);
} else {
lnext(*farleft, *farright);
}
}
if (b->verbose > 2) {
printf(" Creating ");
printtriangle(m, b, &midtri);
printf(" Creating ");
printtriangle(m, b, &tri1);
printf(" Creating ");
printtriangle(m, b, &tri2);
printf(" Creating ");
printtriangle(m, b, &tri3);
}
return;
} else {
/* Split the vertices in half. */
divider = vertices >> 1;
/* Recursively triangulate each half. */
divconqrecurse(m, b, sortarray, divider, 1 - axis, farleft, &innerleft);
divconqrecurse(m, b, &sortarray[divider], vertices - divider, 1 - axis,
&innerright, farright);
if (b->verbose > 1) {
printf(" Joining triangulations with %d and %d vertices.\n", divider,
vertices - divider);
}
/* Merge the two triangulations into one. */
mergehulls(m, b, farleft, &innerleft, &innerright, farright, axis);
}
}
#ifdef ANSI_DECLARATORS
long removeghosts(struct mesh *m, struct behavior *b, struct otri *startghost)
#else /* not ANSI_DECLARATORS */
long removeghosts(m, b, startghost)
struct mesh *m;
struct behavior *b;
struct otri *startghost;
#endif /* not ANSI_DECLARATORS */
{
struct otri searchedge;
struct otri dissolveedge;
struct otri deadtriangle;
vertex markorg;
long hullsize;
triangle ptr; /* Temporary variable used by sym(). */
if (b->verbose) {
printf(" Removing ghost triangles.\n");
}
/* Find an edge on the convex hull to start point location from. */
lprev(*startghost, searchedge);
symself(searchedge);
m->dummytri[0] = encode(searchedge);
/* Remove the bounding box and count the convex hull edges. */
otricopy(*startghost, dissolveedge);
hullsize = 0;
do {
hullsize++;
lnext(dissolveedge, deadtriangle);
lprevself(dissolveedge);
symself(dissolveedge);
/* If no PSLG is involved, set the boundary markers of all the vertices */
/* on the convex hull. If a PSLG is used, this step is done later. */
if (!b->poly) {
/* Watch out for the case where all the input vertices are collinear. */
if (dissolveedge.tri != m->dummytri) {
org(dissolveedge, markorg);
if (vertexmark(markorg) == 0) {
setvertexmark(markorg, 1);
}
}
}
/* Remove a bounding triangle from a convex hull triangle. */
dissolve(dissolveedge);
/* Find the next bounding triangle. */
sym(deadtriangle, dissolveedge);
/* Delete the bounding triangle. */
triangledealloc(m, deadtriangle.tri);
} while (!otriequal(dissolveedge, *startghost));
return hullsize;
}
/*****************************************************************************/
/* */
/* divconqdelaunay() Form a Delaunay triangulation by the divide-and- */
/* conquer method. */
/* */
/* Sorts the vertices, calls a recursive procedure to triangulate them, and */
/* removes the bounding box, setting boundary markers as appropriate. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
long divconqdelaunay(struct mesh *m, struct behavior *b)
#else /* not ANSI_DECLARATORS */
long divconqdelaunay(m, b)
struct mesh *m;
struct behavior *b;
#endif /* not ANSI_DECLARATORS */
{
vertex *sortarray;
struct otri hullleft, hullright;
int divider;
int i, j;
if (b->verbose) {
printf(" Sorting vertices.\n");
}
/* Allocate an array of pointers to vertices for sorting. */
sortarray = (vertex *) trimalloc(m->invertices * (int) sizeof(vertex));
traversalinit(&m->vertices);
for (i = 0; i < m->invertices; i++) {
sortarray[i] = vertextraverse(m);
}
/* Sort the vertices. */
vertexsort(sortarray, m->invertices);
/* Discard duplicate vertices, which can really mess up the algorithm. */
i = 0;
for (j = 1; j < m->invertices; j++) {
if ((sortarray[i][0] == sortarray[j][0])
&& (sortarray[i][1] == sortarray[j][1])) {
if (!b->quiet) {
printf(
"Warning: A duplicate vertex at (%.12g, %.12g) appeared and was ignored.\n",
sortarray[j][0], sortarray[j][1]);
}
setvertextype(sortarray[j], UNDEADVERTEX);
m->undeads++;
} else {
i++;
sortarray[i] = sortarray[j];
}
}
i++;
if (b->dwyer) {
/* Re-sort the array of vertices to accommodate alternating cuts. */
divider = i >> 1;
if (i - divider >= 2) {
if (divider >= 2) {
alternateaxes(sortarray, divider, 1);
}
alternateaxes(&sortarray[divider], i - divider, 1);
}
}
if (b->verbose) {
printf(" Forming triangulation.\n");
}
/* Form the Delaunay triangulation. */
divconqrecurse(m, b, sortarray, i, 0, &hullleft, &hullright);
trifree((VOID *) sortarray);
return removeghosts(m, b, &hullleft);
}
/** **/
/** **/
/********* Divide-and-conquer Delaunay triangulation ends here *********/
/********* Incremental Delaunay triangulation begins here *********/
/** **/
/** **/
/*****************************************************************************/
/* */
/* boundingbox() Form an "infinite" bounding triangle to insert vertices */
/* into. */
/* */
/* The vertices at "infinity" are assigned finite coordinates, which are */
/* used by the point location routines, but (mostly) ignored by the */
/* Delaunay edge flip routines. */
/* */
/*****************************************************************************/
#ifndef REDUCED
#ifdef ANSI_DECLARATORS
void boundingbox(struct mesh *m, struct behavior *b)
#else /* not ANSI_DECLARATORS */
void boundingbox(m, b)
struct mesh *m;
struct behavior *b;
#endif /* not ANSI_DECLARATORS */
{
struct otri inftri; /* Handle for the triangular bounding box. */
REAL width;
if (b->verbose) {
printf(" Creating triangular bounding box.\n");
}
/* Find the width (or height, whichever is larger) of the triangulation. */
width = m->xmax - m->xmin;
if (m->ymax - m->ymin > width) {
width = m->ymax - m->ymin;
}
if (width == 0.0) {
width = 1.0;
}
/* Create the vertices of the bounding box. */
m->infvertex1 = (vertex) trimalloc(m->vertices.itembytes);
m->infvertex2 = (vertex) trimalloc(m->vertices.itembytes);
m->infvertex3 = (vertex) trimalloc(m->vertices.itembytes);
m->infvertex1[0] = m->xmin - 50.0 * width;
m->infvertex1[1] = m->ymin - 40.0 * width;
m->infvertex2[0] = m->xmax + 50.0 * width;
m->infvertex2[1] = m->ymin - 40.0 * width;
m->infvertex3[0] = 0.5 * (m->xmin + m->xmax);
m->infvertex3[1] = m->ymax + 60.0 * width;
/* Create the bounding box. */
maketriangle(m, b, &inftri);
setorg(inftri, m->infvertex1);
setdest(inftri, m->infvertex2);
setapex(inftri, m->infvertex3);
/* Link dummytri to the bounding box so we can always find an */
/* edge to begin searching (point location) from. */
m->dummytri[0] = (triangle) inftri.tri;
if (b->verbose > 2) {
printf(" Creating ");
printtriangle(m, b, &inftri);
}
}
#endif /* not REDUCED */
/*****************************************************************************/
/* */
/* removebox() Remove the "infinite" bounding triangle, setting boundary */
/* markers as appropriate. */
/* */
/* The triangular bounding box has three boundary triangles (one for each */
/* side of the bounding box), and a bunch of triangles fanning out from */
/* the three bounding box vertices (one triangle for each edge of the */
/* convex hull of the inner mesh). This routine removes these triangles. */
/* */
/* Returns the number of edges on the convex hull of the triangulation. */
/* */
/*****************************************************************************/
#ifndef REDUCED
#ifdef ANSI_DECLARATORS
long removebox(struct mesh *m, struct behavior *b)
#else /* not ANSI_DECLARATORS */
long removebox(m, b)
struct mesh *m;
struct behavior *b;
#endif /* not ANSI_DECLARATORS */
{
struct otri deadtriangle;
struct otri searchedge;
struct otri checkedge;
struct otri nextedge, finaledge, dissolveedge;
vertex markorg;
long hullsize;
triangle ptr; /* Temporary variable used by sym(). */
if (b->verbose) {
printf(" Removing triangular bounding box.\n");
}
/* Find a boundary triangle. */
nextedge.tri = m->dummytri;
nextedge.orient = 0;
symself(nextedge);
/* Mark a place to stop. */
lprev(nextedge, finaledge);
lnextself(nextedge);
symself(nextedge);
/* Find a triangle (on the boundary of the vertex set) that isn't */
/* a bounding box triangle. */
lprev(nextedge, searchedge);
symself(searchedge);
/* Check whether nextedge is another boundary triangle */
/* adjacent to the first one. */
lnext(nextedge, checkedge);
symself(checkedge);
if (checkedge.tri == m->dummytri) {
/* Go on to the next triangle. There are only three boundary */
/* triangles, and this next triangle cannot be the third one, */
/* so it's safe to stop here. */
lprevself(searchedge);
symself(searchedge);
}
/* Find a new boundary edge to search from, as the current search */
/* edge lies on a bounding box triangle and will be deleted. */
m->dummytri[0] = encode(searchedge);
hullsize = -2l;
while (!otriequal(nextedge, finaledge)) {
hullsize++;
lprev(nextedge, dissolveedge);
symself(dissolveedge);
/* If not using a PSLG, the vertices should be marked now. */
/* (If using a PSLG, markhull() will do the job.) */
if (!b->poly) {
/* Be careful! One must check for the case where all the input */
/* vertices are collinear, and thus all the triangles are part of */
/* the bounding box. Otherwise, the setvertexmark() call below */
/* will cause a bad pointer reference. */
if (dissolveedge.tri != m->dummytri) {
org(dissolveedge, markorg);
if (vertexmark(markorg) == 0) {
setvertexmark(markorg, 1);
}
}
}
/* Disconnect the bounding box triangle from the mesh triangle. */
dissolve(dissolveedge);
lnext(nextedge, deadtriangle);
sym(deadtriangle, nextedge);
/* Get rid of the bounding box triangle. */
triangledealloc(m, deadtriangle.tri);
/* Do we need to turn the corner? */
if (nextedge.tri == m->dummytri) {
/* Turn the corner. */
otricopy(dissolveedge, nextedge);
}
}
triangledealloc(m, finaledge.tri);
trifree((VOID *) m->infvertex1); /* Deallocate the bounding box vertices. */
trifree((VOID *) m->infvertex2);
trifree((VOID *) m->infvertex3);
return hullsize;
}
#endif /* not REDUCED */
/*****************************************************************************/
/* */
/* incrementaldelaunay() Form a Delaunay triangulation by incrementally */
/* inserting vertices. */
/* */
/* Returns the number of edges on the convex hull of the triangulation. */
/* */
/*****************************************************************************/
#ifndef REDUCED
#ifdef ANSI_DECLARATORS
long incrementaldelaunay(struct mesh *m, struct behavior *b)
#else /* not ANSI_DECLARATORS */
long incrementaldelaunay(m, b)
struct mesh *m;
struct behavior *b;
#endif /* not ANSI_DECLARATORS */
{
struct otri starttri;
vertex vertexloop;
/* Create a triangular bounding box. */
boundingbox(m, b);
if (b->verbose) {
printf(" Incrementally inserting vertices.\n");
}
traversalinit(&m->vertices);
vertexloop = vertextraverse(m);
while (vertexloop != (vertex) NULL) {
starttri.tri = m->dummytri;
if (insertvertex(m, b, vertexloop, &starttri, (struct osub *) NULL, 0, 0)
== DUPLICATEVERTEX) {
if (!b->quiet) {
printf(
"Warning: A duplicate vertex at (%.12g, %.12g) appeared and was ignored.\n",
vertexloop[0], vertexloop[1]);
}
setvertextype(vertexloop, UNDEADVERTEX);
m->undeads++;
}
vertexloop = vertextraverse(m);
}
/* Remove the bounding box. */
return removebox(m, b);
}
#endif /* not REDUCED */
/** **/
/** **/
/********* Incremental Delaunay triangulation ends here *********/
/********* Sweepline Delaunay triangulation begins here *********/
/** **/
/** **/
#ifndef REDUCED
#ifdef ANSI_DECLARATORS
void eventheapinsert(struct event **heap, int heapsize, struct event *newevent)
#else /* not ANSI_DECLARATORS */
void eventheapinsert(heap, heapsize, newevent)
struct event **heap;
int heapsize;
struct event *newevent;
#endif /* not ANSI_DECLARATORS */
{
REAL eventx, eventy;
int eventnum;
int parent;
int notdone;
eventx = newevent->xkey;
eventy = newevent->ykey;
eventnum = heapsize;
notdone = eventnum > 0;
while (notdone) {
parent = (eventnum - 1) >> 1;
if ((heap[parent]->ykey < eventy) ||
((heap[parent]->ykey == eventy)
&& (heap[parent]->xkey <= eventx))) {
notdone = 0;
} else {
heap[eventnum] = heap[parent];
heap[eventnum]->heapposition = eventnum;
eventnum = parent;
notdone = eventnum > 0;
}
}
heap[eventnum] = newevent;
newevent->heapposition = eventnum;
}
#endif /* not REDUCED */
#ifndef REDUCED
#ifdef ANSI_DECLARATORS
void eventheapify(struct event **heap, int heapsize, int eventnum)
#else /* not ANSI_DECLARATORS */
void eventheapify(heap, heapsize, eventnum)
struct event **heap;
int heapsize;
int eventnum;
#endif /* not ANSI_DECLARATORS */
{
struct event *thisevent;
REAL eventx, eventy;
int leftchild, rightchild;
int smallest;
int notdone;
thisevent = heap[eventnum];
eventx = thisevent->xkey;
eventy = thisevent->ykey;
leftchild = 2 * eventnum + 1;
notdone = leftchild < heapsize;
while (notdone) {
if ((heap[leftchild]->ykey < eventy) ||
((heap[leftchild]->ykey == eventy)
&& (heap[leftchild]->xkey < eventx))) {
smallest = leftchild;
} else {
smallest = eventnum;
}
rightchild = leftchild + 1;
if (rightchild < heapsize) {
if ((heap[rightchild]->ykey < heap[smallest]->ykey) ||
((heap[rightchild]->ykey == heap[smallest]->ykey)
&& (heap[rightchild]->xkey < heap[smallest]->xkey))) {
smallest = rightchild;
}
}
if (smallest == eventnum) {
notdone = 0;
} else {
heap[eventnum] = heap[smallest];
heap[eventnum]->heapposition = eventnum;
heap[smallest] = thisevent;
thisevent->heapposition = smallest;
eventnum = smallest;
leftchild = 2 * eventnum + 1;
notdone = leftchild < heapsize;
}
}
}
#endif /* not REDUCED */
#ifndef REDUCED
#ifdef ANSI_DECLARATORS
void eventheapdelete(struct event **heap, int heapsize, int eventnum)
#else /* not ANSI_DECLARATORS */
void eventheapdelete(heap, heapsize, eventnum)
struct event **heap;
int heapsize;
int eventnum;
#endif /* not ANSI_DECLARATORS */
{
struct event *moveevent;
REAL eventx, eventy;
int parent;
int notdone;
moveevent = heap[heapsize - 1];
if (eventnum > 0) {
eventx = moveevent->xkey;
eventy = moveevent->ykey;
do {
parent = (eventnum - 1) >> 1;
if ((heap[parent]->ykey < eventy) ||
((heap[parent]->ykey == eventy)
&& (heap[parent]->xkey <= eventx))) {
notdone = 0;
} else {
heap[eventnum] = heap[parent];
heap[eventnum]->heapposition = eventnum;
eventnum = parent;
notdone = eventnum > 0;
}
} while (notdone);
}
heap[eventnum] = moveevent;
moveevent->heapposition = eventnum;
eventheapify(heap, heapsize - 1, eventnum);
}
#endif /* not REDUCED */
#ifndef REDUCED
#ifdef ANSI_DECLARATORS
void createeventheap(struct mesh *m, struct event ***eventheap,
struct event **events, struct event **freeevents)
#else /* not ANSI_DECLARATORS */
void createeventheap(m, eventheap, events, freeevents)
struct mesh *m;
struct event ***eventheap;
struct event **events;
struct event **freeevents;
#endif /* not ANSI_DECLARATORS */
{
vertex thisvertex;
int maxevents;
int i;
maxevents = (3 * m->invertices) / 2;
*eventheap = (struct event **) trimalloc(maxevents *
(int) sizeof(struct event *));
*events = (struct event *) trimalloc(maxevents * (int) sizeof(struct event));
traversalinit(&m->vertices);
for (i = 0; i < m->invertices; i++) {
thisvertex = vertextraverse(m);
(*events)[i].eventptr = (VOID *) thisvertex;
(*events)[i].xkey = thisvertex[0];
(*events)[i].ykey = thisvertex[1];
eventheapinsert(*eventheap, i, *events + i);
}
*freeevents = (struct event *) NULL;
for (i = maxevents - 1; i >= m->invertices; i--) {
(*events)[i].eventptr = (VOID *) *freeevents;
*freeevents = *events + i;
}
}
#endif /* not REDUCED */
#ifndef REDUCED
#ifdef ANSI_DECLARATORS
int rightofhyperbola(struct mesh *m, struct otri *fronttri, vertex newsite)
#else /* not ANSI_DECLARATORS */
int rightofhyperbola(m, fronttri, newsite)
struct mesh *m;
struct otri *fronttri;
vertex newsite;
#endif /* not ANSI_DECLARATORS */
{
vertex leftvertex, rightvertex;
REAL dxa, dya, dxb, dyb;
m->hyperbolacount++;
dest(*fronttri, leftvertex);
apex(*fronttri, rightvertex);
if ((leftvertex[1] < rightvertex[1]) ||
((leftvertex[1] == rightvertex[1]) &&
(leftvertex[0] < rightvertex[0]))) {
if (newsite[0] >= rightvertex[0]) {
return 1;
}
} else {
if (newsite[0] <= leftvertex[0]) {
return 0;
}
}
dxa = leftvertex[0] - newsite[0];
dya = leftvertex[1] - newsite[1];
dxb = rightvertex[0] - newsite[0];
dyb = rightvertex[1] - newsite[1];
return dya * (dxb * dxb + dyb * dyb) > dyb * (dxa * dxa + dya * dya);
}
#endif /* not REDUCED */
#ifndef REDUCED
#ifdef ANSI_DECLARATORS
REAL circletop(struct mesh *m, vertex pa, vertex pb, vertex pc, REAL ccwabc)
#else /* not ANSI_DECLARATORS */
REAL circletop(m, pa, pb, pc, ccwabc)
struct mesh *m;
vertex pa;
vertex pb;
vertex pc;
REAL ccwabc;
#endif /* not ANSI_DECLARATORS */
{
REAL xac, yac, xbc, ybc, xab, yab;
REAL aclen2, bclen2, ablen2;
m->circletopcount++;
xac = pa[0] - pc[0];
yac = pa[1] - pc[1];
xbc = pb[0] - pc[0];
ybc = pb[1] - pc[1];
xab = pa[0] - pb[0];
yab = pa[1] - pb[1];
aclen2 = xac * xac + yac * yac;
bclen2 = xbc * xbc + ybc * ybc;
ablen2 = xab * xab + yab * yab;
return pc[1] + (xac * bclen2 - xbc * aclen2 + sqrt(aclen2 * bclen2 * ablen2))
/ (2.0 * ccwabc);
}
#endif /* not REDUCED */
#ifndef REDUCED
#ifdef ANSI_DECLARATORS
void check4deadevent(struct otri *checktri, struct event **freeevents,
struct event **eventheap, int *heapsize)
#else /* not ANSI_DECLARATORS */
void check4deadevent(checktri, freeevents, eventheap, heapsize)
struct otri *checktri;
struct event **freeevents;
struct event **eventheap;
int *heapsize;
#endif /* not ANSI_DECLARATORS */
{
struct event *deadevent;
vertex eventvertex;
int eventnum;
org(*checktri, eventvertex);
if (eventvertex != (vertex) NULL) {
deadevent = (struct event *) eventvertex;
eventnum = deadevent->heapposition;
deadevent->eventptr = (VOID *) *freeevents;
*freeevents = deadevent;
eventheapdelete(eventheap, *heapsize, eventnum);
(*heapsize)--;
setorg(*checktri, NULL);
}
}
#endif /* not REDUCED */
#ifndef REDUCED
#ifdef ANSI_DECLARATORS
struct splaynode *splay(struct mesh *m, struct splaynode *splaytree,
vertex searchpoint, struct otri *searchtri)
#else /* not ANSI_DECLARATORS */
struct splaynode *splay(m, splaytree, searchpoint, searchtri)
struct mesh *m;
struct splaynode *splaytree;
vertex searchpoint;
struct otri *searchtri;
#endif /* not ANSI_DECLARATORS */
{
struct splaynode *child, *grandchild;
struct splaynode *lefttree, *righttree;
struct splaynode *leftright;
vertex checkvertex;
int rightofroot, rightofchild;
if (splaytree == (struct splaynode *) NULL) {
return (struct splaynode *) NULL;
}
dest(splaytree->keyedge, checkvertex);
if (checkvertex == splaytree->keydest) {
rightofroot = rightofhyperbola(m, &splaytree->keyedge, searchpoint);
if (rightofroot) {
otricopy(splaytree->keyedge, *searchtri);
child = splaytree->rchild;
} else {
child = splaytree->lchild;
}
if (child == (struct splaynode *) NULL) {
return splaytree;
}
dest(child->keyedge, checkvertex);
if (checkvertex != child->keydest) {
child = splay(m, child, searchpoint, searchtri);
if (child == (struct splaynode *) NULL) {
if (rightofroot) {
splaytree->rchild = (struct splaynode *) NULL;
} else {
splaytree->lchild = (struct splaynode *) NULL;
}
return splaytree;
}
}
rightofchild = rightofhyperbola(m, &child->keyedge, searchpoint);
if (rightofchild) {
otricopy(child->keyedge, *searchtri);
grandchild = splay(m, child->rchild, searchpoint, searchtri);
child->rchild = grandchild;
} else {
grandchild = splay(m, child->lchild, searchpoint, searchtri);
child->lchild = grandchild;
}
if (grandchild == (struct splaynode *) NULL) {
if (rightofroot) {
splaytree->rchild = child->lchild;
child->lchild = splaytree;
} else {
splaytree->lchild = child->rchild;
child->rchild = splaytree;
}
return child;
}
if (rightofchild) {
if (rightofroot) {
splaytree->rchild = child->lchild;
child->lchild = splaytree;
} else {
splaytree->lchild = grandchild->rchild;
grandchild->rchild = splaytree;
}
child->rchild = grandchild->lchild;
grandchild->lchild = child;
} else {
if (rightofroot) {
splaytree->rchild = grandchild->lchild;
grandchild->lchild = splaytree;
} else {
splaytree->lchild = child->rchild;
child->rchild = splaytree;
}
child->lchild = grandchild->rchild;
grandchild->rchild = child;
}
return grandchild;
} else {
lefttree = splay(m, splaytree->lchild, searchpoint, searchtri);
righttree = splay(m, splaytree->rchild, searchpoint, searchtri);
pooldealloc(&m->splaynodes, (VOID *) splaytree);
if (lefttree == (struct splaynode *) NULL) {
return righttree;
} else if (righttree == (struct splaynode *) NULL) {
return lefttree;
} else if (lefttree->rchild == (struct splaynode *) NULL) {
lefttree->rchild = righttree->lchild;
righttree->lchild = lefttree;
return righttree;
} else if (righttree->lchild == (struct splaynode *) NULL) {
righttree->lchild = lefttree->rchild;
lefttree->rchild = righttree;
return lefttree;
} else {
/* printf("Holy Toledo!!!\n"); */
leftright = lefttree->rchild;
while (leftright->rchild != (struct splaynode *) NULL) {
leftright = leftright->rchild;
}
leftright->rchild = righttree;
return lefttree;
}
}
}
#endif /* not REDUCED */
#ifndef REDUCED
#ifdef ANSI_DECLARATORS
struct splaynode *splayinsert(struct mesh *m, struct splaynode *splayroot,
struct otri *newkey, vertex searchpoint)
#else /* not ANSI_DECLARATORS */
struct splaynode *splayinsert(m, splayroot, newkey, searchpoint)
struct mesh *m;
struct splaynode *splayroot;
struct otri *newkey;
vertex searchpoint;
#endif /* not ANSI_DECLARATORS */
{
struct splaynode *newsplaynode;
newsplaynode = (struct splaynode *) poolalloc(&m->splaynodes);
otricopy(*newkey, newsplaynode->keyedge);
dest(*newkey, newsplaynode->keydest);
if (splayroot == (struct splaynode *) NULL) {
newsplaynode->lchild = (struct splaynode *) NULL;
newsplaynode->rchild = (struct splaynode *) NULL;
} else if (rightofhyperbola(m, &splayroot->keyedge, searchpoint)) {
newsplaynode->lchild = splayroot;
newsplaynode->rchild = splayroot->rchild;
splayroot->rchild = (struct splaynode *) NULL;
} else {
newsplaynode->lchild = splayroot->lchild;
newsplaynode->rchild = splayroot;
splayroot->lchild = (struct splaynode *) NULL;
}
return newsplaynode;
}
#endif /* not REDUCED */
#ifndef REDUCED
#ifdef ANSI_DECLARATORS
struct splaynode *circletopinsert(struct mesh *m, struct behavior *b,
struct splaynode *splayroot,
struct otri *newkey,
vertex pa, vertex pb, vertex pc, REAL topy)
#else /* not ANSI_DECLARATORS */
struct splaynode *circletopinsert(m, b, splayroot, newkey, pa, pb, pc, topy)
struct mesh *m;
struct behavior *b;
struct splaynode *splayroot;
struct otri *newkey;
vertex pa;
vertex pb;
vertex pc;
REAL topy;
#endif /* not ANSI_DECLARATORS */
{
REAL ccwabc;
REAL xac, yac, xbc, ybc;
REAL aclen2, bclen2;
REAL searchpoint[2];
struct otri dummytri;
ccwabc = counterclockwise(m, b, pa, pb, pc);
xac = pa[0] - pc[0];
yac = pa[1] - pc[1];
xbc = pb[0] - pc[0];
ybc = pb[1] - pc[1];
aclen2 = xac * xac + yac * yac;
bclen2 = xbc * xbc + ybc * ybc;
searchpoint[0] = pc[0] - (yac * bclen2 - ybc * aclen2) / (2.0 * ccwabc);
searchpoint[1] = topy;
return splayinsert(m, splay(m, splayroot, (vertex) searchpoint, &dummytri),
newkey, (vertex) searchpoint);
}
#endif /* not REDUCED */
#ifndef REDUCED
#ifdef ANSI_DECLARATORS
struct splaynode *frontlocate(struct mesh *m, struct splaynode *splayroot,
struct otri *bottommost, vertex searchvertex,
struct otri *searchtri, int *farright)
#else /* not ANSI_DECLARATORS */
struct splaynode *frontlocate(m, splayroot, bottommost, searchvertex,
searchtri, farright)
struct mesh *m;
struct splaynode *splayroot;
struct otri *bottommost;
vertex searchvertex;
struct otri *searchtri;
int *farright;
#endif /* not ANSI_DECLARATORS */
{
int farrightflag;
triangle ptr; /* Temporary variable used by onext(). */
otricopy(*bottommost, *searchtri);
splayroot = splay(m, splayroot, searchvertex, searchtri);
farrightflag = 0;
while (!farrightflag && rightofhyperbola(m, searchtri, searchvertex)) {
onextself(*searchtri);
farrightflag = otriequal(*searchtri, *bottommost);
}
*farright = farrightflag;
return splayroot;
}
#endif /* not REDUCED */
#ifndef REDUCED
#ifdef ANSI_DECLARATORS
long sweeplinedelaunay(struct mesh *m, struct behavior *b)
#else /* not ANSI_DECLARATORS */
long sweeplinedelaunay(m, b)
struct mesh *m;
struct behavior *b;
#endif /* not ANSI_DECLARATORS */
{
struct event **eventheap;
struct event *events;
struct event *freeevents;
struct event *nextevent;
struct event *newevent;
struct splaynode *splayroot;
struct otri bottommost;
struct otri searchtri;
struct otri fliptri;
struct otri lefttri, righttri, farlefttri, farrighttri;
struct otri inserttri;
vertex firstvertex, secondvertex;
vertex nextvertex, lastvertex;
vertex connectvertex;
vertex leftvertex, midvertex, rightvertex;
REAL lefttest, righttest;
int heapsize;
int check4events, farrightflag;
triangle ptr; /* Temporary variable used by sym(), onext(), and oprev(). */
poolinit(&m->splaynodes, sizeof(struct splaynode), SPLAYNODEPERBLOCK,
SPLAYNODEPERBLOCK, 0);
splayroot = (struct splaynode *) NULL;
if (b->verbose) {
printf(" Placing vertices in event heap.\n");
}
createeventheap(m, &eventheap, &events, &freeevents);
heapsize = m->invertices;
if (b->verbose) {
printf(" Forming triangulation.\n");
}
maketriangle(m, b, &lefttri);
maketriangle(m, b, &righttri);
bond(lefttri, righttri);
lnextself(lefttri);
lprevself(righttri);
bond(lefttri, righttri);
lnextself(lefttri);
lprevself(righttri);
bond(lefttri, righttri);
firstvertex = (vertex) eventheap[0]->eventptr;
eventheap[0]->eventptr = (VOID *) freeevents;
freeevents = eventheap[0];
eventheapdelete(eventheap, heapsize, 0);
heapsize--;
do {
if (heapsize == 0) {
printf("Error: Input vertices are all identical.\n");
triexit(1);
}
secondvertex = (vertex) eventheap[0]->eventptr;
eventheap[0]->eventptr = (VOID *) freeevents;
freeevents = eventheap[0];
eventheapdelete(eventheap, heapsize, 0);
heapsize--;
if ((firstvertex[0] == secondvertex[0]) &&
(firstvertex[1] == secondvertex[1])) {
if (!b->quiet) {
printf(
"Warning: A duplicate vertex at (%.12g, %.12g) appeared and was ignored.\n",
secondvertex[0], secondvertex[1]);
}
setvertextype(secondvertex, UNDEADVERTEX);
m->undeads++;
}
} while ((firstvertex[0] == secondvertex[0]) &&
(firstvertex[1] == secondvertex[1]));
setorg(lefttri, firstvertex);
setdest(lefttri, secondvertex);
setorg(righttri, secondvertex);
setdest(righttri, firstvertex);
lprev(lefttri, bottommost);
lastvertex = secondvertex;
while (heapsize > 0) {
nextevent = eventheap[0];
eventheapdelete(eventheap, heapsize, 0);
heapsize--;
check4events = 1;
if (nextevent->xkey < m->xmin) {
decode(nextevent->eventptr, fliptri);
oprev(fliptri, farlefttri);
check4deadevent(&farlefttri, &freeevents, eventheap, &heapsize);
onext(fliptri, farrighttri);
check4deadevent(&farrighttri, &freeevents, eventheap, &heapsize);
if (otriequal(farlefttri, bottommost)) {
lprev(fliptri, bottommost);
}
flip(m, b, &fliptri);
setapex(fliptri, NULL);
lprev(fliptri, lefttri);
lnext(fliptri, righttri);
sym(lefttri, farlefttri);
if (randomnation(SAMPLERATE) == 0) {
symself(fliptri);
dest(fliptri, leftvertex);
apex(fliptri, midvertex);
org(fliptri, rightvertex);
splayroot = circletopinsert(m, b, splayroot, &lefttri, leftvertex,
midvertex, rightvertex, nextevent->ykey);
}
} else {
nextvertex = (vertex) nextevent->eventptr;
if ((nextvertex[0] == lastvertex[0]) &&
(nextvertex[1] == lastvertex[1])) {
if (!b->quiet) {
printf(
"Warning: A duplicate vertex at (%.12g, %.12g) appeared and was ignored.\n",
nextvertex[0], nextvertex[1]);
}
setvertextype(nextvertex, UNDEADVERTEX);
m->undeads++;
check4events = 0;
} else {
lastvertex = nextvertex;
splayroot = frontlocate(m, splayroot, &bottommost, nextvertex,
&searchtri, &farrightflag);
/*
otricopy(bottommost, searchtri);
farrightflag = 0;
while (!farrightflag && rightofhyperbola(m, &searchtri, nextvertex)) {
onextself(searchtri);
farrightflag = otriequal(searchtri, bottommost);
}
*/
check4deadevent(&searchtri, &freeevents, eventheap, &heapsize);
otricopy(searchtri, farrighttri);
sym(searchtri, farlefttri);
maketriangle(m, b, &lefttri);
maketriangle(m, b, &righttri);
dest(farrighttri, connectvertex);
setorg(lefttri, connectvertex);
setdest(lefttri, nextvertex);
setorg(righttri, nextvertex);
setdest(righttri, connectvertex);
bond(lefttri, righttri);
lnextself(lefttri);
lprevself(righttri);
bond(lefttri, righttri);
lnextself(lefttri);
lprevself(righttri);
bond(lefttri, farlefttri);
bond(righttri, farrighttri);
if (!farrightflag && otriequal(farrighttri, bottommost)) {
otricopy(lefttri, bottommost);
}
if (randomnation(SAMPLERATE) == 0) {
splayroot = splayinsert(m, splayroot, &lefttri, nextvertex);
} else if (randomnation(SAMPLERATE) == 0) {
lnext(righttri, inserttri);
splayroot = splayinsert(m, splayroot, &inserttri, nextvertex);
}
}
}
nextevent->eventptr = (VOID *) freeevents;
freeevents = nextevent;
if (check4events) {
apex(farlefttri, leftvertex);
dest(lefttri, midvertex);
apex(lefttri, rightvertex);
lefttest = counterclockwise(m, b, leftvertex, midvertex, rightvertex);
if (lefttest > 0.0) {
newevent = freeevents;
freeevents = (struct event *) freeevents->eventptr;
newevent->xkey = m->xminextreme;
newevent->ykey = circletop(m, leftvertex, midvertex, rightvertex,
lefttest);
newevent->eventptr = (VOID *) encode(lefttri);
eventheapinsert(eventheap, heapsize, newevent);
heapsize++;
setorg(lefttri, newevent);
}
apex(righttri, leftvertex);
org(righttri, midvertex);
apex(farrighttri, rightvertex);
righttest = counterclockwise(m, b, leftvertex, midvertex, rightvertex);
if (righttest > 0.0) {
newevent = freeevents;
freeevents = (struct event *) freeevents->eventptr;
newevent->xkey = m->xminextreme;
newevent->ykey = circletop(m, leftvertex, midvertex, rightvertex,
righttest);
newevent->eventptr = (VOID *) encode(farrighttri);
eventheapinsert(eventheap, heapsize, newevent);
heapsize++;
setorg(farrighttri, newevent);
}
}
}
pooldeinit(&m->splaynodes);
lprevself(bottommost);
return removeghosts(m, b, &bottommost);
}
#endif /* not REDUCED */
/** **/
/** **/
/********* Sweepline Delaunay triangulation ends here *********/
/********* General mesh construction routines begin here *********/
/** **/
/** **/
/*****************************************************************************/
/* */
/* delaunay() Form a Delaunay triangulation. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
long delaunay(struct mesh *m, struct behavior *b)
#else /* not ANSI_DECLARATORS */
long delaunay(m, b)
struct mesh *m;
struct behavior *b;
#endif /* not ANSI_DECLARATORS */
{
long hulledges;
m->eextras = 0;
initializetrisubpools(m, b);
#ifdef REDUCED
if (!b->quiet) {
printf(
"Constructing Delaunay triangulation by divide-and-conquer method.\n");
}
hulledges = divconqdelaunay(m, b);
#else /* not REDUCED */
if (!b->quiet) {
printf("Constructing Delaunay triangulation ");
if (b->incremental) {
printf("by incremental method.\n");
} else if (b->sweepline) {
printf("by sweepline method.\n");
} else {
printf("by divide-and-conquer method.\n");
}
}
if (b->incremental) {
hulledges = incrementaldelaunay(m, b);
} else if (b->sweepline) {
hulledges = sweeplinedelaunay(m, b);
} else {
hulledges = divconqdelaunay(m, b);
}
#endif /* not REDUCED */
if (m->triangles.items == 0) {
/* The input vertices were all collinear, so there are no triangles. */
return 0l;
} else {
return hulledges;
}
}
/*****************************************************************************/
/* */
/* reconstruct() Reconstruct a triangulation from its .ele (and possibly */
/* .poly) file. Used when the -r switch is used. */
/* */
/* Reads an .ele file and reconstructs the original mesh. If the -p switch */
/* is used, this procedure will also read a .poly file and reconstruct the */
/* subsegments of the original mesh. If the -a switch is used, this */
/* procedure will also read an .area file and set a maximum area constraint */
/* on each triangle. */
/* */
/* Vertices that are not corners of triangles, such as nodes on edges of */
/* subparametric elements, are discarded. */
/* */
/* This routine finds the adjacencies between triangles (and subsegments) */
/* by forming one stack of triangles for each vertex. Each triangle is on */
/* three different stacks simultaneously. Each triangle's subsegment */
/* pointers are used to link the items in each stack. This memory-saving */
/* feature makes the code harder to read. The most important thing to keep */
/* in mind is that each triangle is removed from a stack precisely when */
/* the corresponding pointer is adjusted to refer to a subsegment rather */
/* than the next triangle of the stack. */
/* */
/*****************************************************************************/
#ifndef CDT_ONLY
#ifdef TRILIBRARY
#ifdef ANSI_DECLARATORS
int reconstruct(struct mesh *m, struct behavior *b, int *trianglelist,
REAL *triangleattriblist, REAL *trianglearealist,
int elements, int corners, int attribs,
int *segmentlist,int *segmentmarkerlist, int numberofsegments)
#else /* not ANSI_DECLARATORS */
int reconstruct(m, b, trianglelist, triangleattriblist, trianglearealist,
elements, corners, attribs, segmentlist, segmentmarkerlist,
numberofsegments)
struct mesh *m;
struct behavior *b;
int *trianglelist;
REAL *triangleattriblist;
REAL *trianglearealist;
int elements;
int corners;
int attribs;
int *segmentlist;
int *segmentmarkerlist;
int numberofsegments;
#endif /* not ANSI_DECLARATORS */
#else /* not TRILIBRARY */
#ifdef ANSI_DECLARATORS
long reconstruct(struct mesh *m, struct behavior *b, char *elefilename,
char *areafilename, char *polyfilename, FILE *polyfile)
#else /* not ANSI_DECLARATORS */
long reconstruct(m, b, elefilename, areafilename, polyfilename, polyfile)
struct mesh *m;
struct behavior *b;
char *elefilename;
char *areafilename;
char *polyfilename;
FILE *polyfile;
#endif /* not ANSI_DECLARATORS */
#endif /* not TRILIBRARY */
{
#ifdef TRILIBRARY
int vertexindex;
int attribindex;
#else /* not TRILIBRARY */
FILE *elefile;
FILE *areafile;
char inputline[INPUTLINESIZE];
char *stringptr;
int areaelements;
#endif /* not TRILIBRARY */
struct otri triangleloop;
struct otri triangleleft;
struct otri checktri;
struct otri checkleft;
struct otri checkneighbor;
struct osub subsegloop;
triangle *vertexarray;
triangle *prevlink;
triangle nexttri;
vertex tdest, tapex;
vertex checkdest, checkapex;
vertex shorg;
vertex killvertex;
vertex segmentorg, segmentdest;
REAL area;
int corner[3];
int end[2];
int killvertexindex;
int incorners;
int segmentmarkers;
int boundmarker;
int aroundvertex;
long hullsize;
int notfound;
long elementnumber, segmentnumber;
int i, j;
triangle ptr; /* Temporary variable used by sym(). */
#ifdef TRILIBRARY
m->inelements = elements;
incorners = corners;
if (incorners < 3) {
printf("Error: Triangles must have at least 3 vertices.\n");
triexit(1);
}
m->eextras = attribs;
#else /* not TRILIBRARY */
/* Read the triangles from an .ele file. */
if (!b->quiet) {
printf("Opening %s.\n", elefilename);
}
elefile = fopen(elefilename, "r");
if (elefile == (FILE *) NULL) {
printf(" Error: Cannot access file %s.\n", elefilename);
triexit(1);
}
/* Read number of triangles, number of vertices per triangle, and */
/* number of triangle attributes from .ele file. */
stringptr = readline(inputline, elefile, elefilename);
m->inelements = (int) strtol(stringptr, &stringptr, 0);
stringptr = findfield(stringptr);
if (*stringptr == '\0') {
incorners = 3;
} else {
incorners = (int) strtol(stringptr, &stringptr, 0);
if (incorners < 3) {
printf("Error: Triangles in %s must have at least 3 vertices.\n",
elefilename);
triexit(1);
}
}
stringptr = findfield(stringptr);
if (*stringptr == '\0') {
m->eextras = 0;
} else {
m->eextras = (int) strtol(stringptr, &stringptr, 0);
}
#endif /* not TRILIBRARY */
initializetrisubpools(m, b);
/* Create the triangles. */
for (elementnumber = 1; elementnumber <= m->inelements; elementnumber++) {
maketriangle(m, b, &triangleloop);
/* Mark the triangle as living. */
triangleloop.tri[3] = (triangle) triangleloop.tri;
}
segmentmarkers = 0;
if (b->poly) {
#ifdef TRILIBRARY
m->insegments = numberofsegments;
segmentmarkers = segmentmarkerlist != (int *) NULL;
#else /* not TRILIBRARY */
/* Read number of segments and number of segment */
/* boundary markers from .poly file. */
stringptr = readline(inputline, polyfile, b->inpolyfilename);
m->insegments = (int) strtol(stringptr, &stringptr, 0);
stringptr = findfield(stringptr);
if (*stringptr != '\0') {
segmentmarkers = (int) strtol(stringptr, &stringptr, 0);
}
#endif /* not TRILIBRARY */
/* Create the subsegments. */
for (segmentnumber = 1; segmentnumber <= m->insegments; segmentnumber++) {
makesubseg(m, &subsegloop);
/* Mark the subsegment as living. */
subsegloop.ss[2] = (subseg) subsegloop.ss;
}
}
#ifdef TRILIBRARY
vertexindex = 0;
attribindex = 0;
#else /* not TRILIBRARY */
if (b->vararea) {
/* Open an .area file, check for consistency with the .ele file. */
if (!b->quiet) {
printf("Opening %s.\n", areafilename);
}
areafile = fopen(areafilename, "r");
if (areafile == (FILE *) NULL) {
printf(" Error: Cannot access file %s.\n", areafilename);
triexit(1);
}
stringptr = readline(inputline, areafile, areafilename);
areaelements = (int) strtol(stringptr, &stringptr, 0);
if (areaelements != m->inelements) {
printf("Error: %s and %s disagree on number of triangles.\n",
elefilename, areafilename);
triexit(1);
}
}
#endif /* not TRILIBRARY */
if (!b->quiet) {
printf("Reconstructing mesh.\n");
}
/* Allocate a temporary array that maps each vertex to some adjacent */
/* triangle. I took care to allocate all the permanent memory for */
/* triangles and subsegments first. */
vertexarray = (triangle *) trimalloc(m->vertices.items *
(int) sizeof(triangle));
/* Each vertex is initially unrepresented. */
for (i = 0; i < m->vertices.items; i++) {
vertexarray[i] = (triangle) m->dummytri;
}
if (b->verbose) {
printf(" Assembling triangles.\n");
}
/* Read the triangles from the .ele file, and link */
/* together those that share an edge. */
traversalinit(&m->triangles);
triangleloop.tri = triangletraverse(m);
elementnumber = b->firstnumber;
while (triangleloop.tri != (triangle *) NULL) {
#ifdef TRILIBRARY
/* Copy the triangle's three corners. */
for (j = 0; j < 3; j++) {
corner[j] = trianglelist[vertexindex++];
if ((corner[j] < b->firstnumber) ||
(corner[j] >= b->firstnumber + m->invertices)) {
printf("Error: Triangle %ld has an invalid vertex index.\n",
elementnumber);
triexit(1);
}
}
#else /* not TRILIBRARY */
/* Read triangle number and the triangle's three corners. */
stringptr = readline(inputline, elefile, elefilename);
for (j = 0; j < 3; j++) {
stringptr = findfield(stringptr);
if (*stringptr == '\0') {
printf("Error: Triangle %ld is missing vertex %d in %s.\n",
elementnumber, j + 1, elefilename);
triexit(1);
} else {
corner[j] = (int) strtol(stringptr, &stringptr, 0);
if ((corner[j] < b->firstnumber) ||
(corner[j] >= b->firstnumber + m->invertices)) {
printf("Error: Triangle %ld has an invalid vertex index.\n",
elementnumber);
triexit(1);
}
}
}
#endif /* not TRILIBRARY */
/* Find out about (and throw away) extra nodes. */
for (j = 3; j < incorners; j++) {
#ifdef TRILIBRARY
killvertexindex = trianglelist[vertexindex++];
#else /* not TRILIBRARY */
stringptr = findfield(stringptr);
if (*stringptr != '\0') {
killvertexindex = (int) strtol(stringptr, &stringptr, 0);
#endif /* not TRILIBRARY */
if ((killvertexindex >= b->firstnumber) &&
(killvertexindex < b->firstnumber + m->invertices)) {
/* Delete the non-corner vertex if it's not already deleted. */
killvertex = getvertex(m, b, killvertexindex);
if (vertextype(killvertex) != DEADVERTEX) {
vertexdealloc(m, killvertex);
}
}
#ifndef TRILIBRARY
}
#endif /* not TRILIBRARY */
}
/* Read the triangle's attributes. */
for (j = 0; j < m->eextras; j++) {
#ifdef TRILIBRARY
setelemattribute(triangleloop, j, triangleattriblist[attribindex++]);
#else /* not TRILIBRARY */
stringptr = findfield(stringptr);
if (*stringptr == '\0') {
setelemattribute(triangleloop, j, 0);
} else {
setelemattribute(triangleloop, j,
(REAL) strtod(stringptr, &stringptr));
}
#endif /* not TRILIBRARY */
}
if (b->vararea) {
#ifdef TRILIBRARY
area = trianglearealist[elementnumber - b->firstnumber];
#else /* not TRILIBRARY */
/* Read an area constraint from the .area file. */
stringptr = readline(inputline, areafile, areafilename);
stringptr = findfield(stringptr);
if (*stringptr == '\0') {
area = -1.0; /* No constraint on this triangle. */
} else {
area = (REAL) strtod(stringptr, &stringptr);
}
#endif /* not TRILIBRARY */
setareabound(triangleloop, area);
}
/* Set the triangle's vertices. */
triangleloop.orient = 0;
setorg(triangleloop, getvertex(m, b, corner[0]));
setdest(triangleloop, getvertex(m, b, corner[1]));
setapex(triangleloop, getvertex(m, b, corner[2]));
/* Try linking the triangle to others that share these vertices. */
for (triangleloop.orient = 0; triangleloop.orient < 3;
triangleloop.orient++) {
/* Take the number for the origin of triangleloop. */
aroundvertex = corner[triangleloop.orient];
/* Look for other triangles having this vertex. */
nexttri = vertexarray[aroundvertex - b->firstnumber];
/* Link the current triangle to the next one in the stack. */
triangleloop.tri[6 + triangleloop.orient] = nexttri;
/* Push the current triangle onto the stack. */
vertexarray[aroundvertex - b->firstnumber] = encode(triangleloop);
decode(nexttri, checktri);
if (checktri.tri != m->dummytri) {
dest(triangleloop, tdest);
apex(triangleloop, tapex);
/* Look for other triangles that share an edge. */
do {
dest(checktri, checkdest);
apex(checktri, checkapex);
if (tapex == checkdest) {
/* The two triangles share an edge; bond them together. */
lprev(triangleloop, triangleleft);
bond(triangleleft, checktri);
}
if (tdest == checkapex) {
/* The two triangles share an edge; bond them together. */
lprev(checktri, checkleft);
bond(triangleloop, checkleft);
}
/* Find the next triangle in the stack. */
nexttri = checktri.tri[6 + checktri.orient];
decode(nexttri, checktri);
} while (checktri.tri != m->dummytri);
}
}
triangleloop.tri = triangletraverse(m);
elementnumber++;
}
#ifdef TRILIBRARY
vertexindex = 0;
#else /* not TRILIBRARY */
fclose(elefile);
if (b->vararea) {
fclose(areafile);
}
#endif /* not TRILIBRARY */
hullsize = 0; /* Prepare to count the boundary edges. */
if (b->poly) {
if (b->verbose) {
printf(" Marking segments in triangulation.\n");
}
/* Read the segments from the .poly file, and link them */
/* to their neighboring triangles. */
boundmarker = 0;
traversalinit(&m->subsegs);
subsegloop.ss = subsegtraverse(m);
segmentnumber = b->firstnumber;
while (subsegloop.ss != (subseg *) NULL) {
#ifdef TRILIBRARY
end[0] = segmentlist[vertexindex++];
end[1] = segmentlist[vertexindex++];
if (segmentmarkers) {
boundmarker = segmentmarkerlist[segmentnumber - b->firstnumber];
}
#else /* not TRILIBRARY */
/* Read the endpoints of each segment, and possibly a boundary marker. */
stringptr = readline(inputline, polyfile, b->inpolyfilename);
/* Skip the first (segment number) field. */
stringptr = findfield(stringptr);
if (*stringptr == '\0') {
printf("Error: Segment %ld has no endpoints in %s.\n", segmentnumber,
polyfilename);
triexit(1);
} else {
end[0] = (int) strtol(stringptr, &stringptr, 0);
}
stringptr = findfield(stringptr);
if (*stringptr == '\0') {
printf("Error: Segment %ld is missing its second endpoint in %s.\n",
segmentnumber, polyfilename);
triexit(1);
} else {
end[1] = (int) strtol(stringptr, &stringptr, 0);
}
if (segmentmarkers) {
stringptr = findfield(stringptr);
if (*stringptr == '\0') {
boundmarker = 0;
} else {
boundmarker = (int) strtol(stringptr, &stringptr, 0);
}
}
#endif /* not TRILIBRARY */
for (j = 0; j < 2; j++) {
if ((end[j] < b->firstnumber) ||
(end[j] >= b->firstnumber + m->invertices)) {
printf("Error: Segment %ld has an invalid vertex index.\n",
segmentnumber);
triexit(1);
}
}
/* set the subsegment's vertices. */
subsegloop.ssorient = 0;
segmentorg = getvertex(m, b, end[0]);
segmentdest = getvertex(m, b, end[1]);
setsorg(subsegloop, segmentorg);
setsdest(subsegloop, segmentdest);
setsegorg(subsegloop, segmentorg);
setsegdest(subsegloop, segmentdest);
setmark(subsegloop, boundmarker);
/* Try linking the subsegment to triangles that share these vertices. */
for (subsegloop.ssorient = 0; subsegloop.ssorient < 2;
subsegloop.ssorient++) {
/* Take the number for the destination of subsegloop. */
aroundvertex = end[1 - subsegloop.ssorient];
/* Look for triangles having this vertex. */
prevlink = &vertexarray[aroundvertex - b->firstnumber];
nexttri = vertexarray[aroundvertex - b->firstnumber];
decode(nexttri, checktri);
sorg(subsegloop, shorg);
notfound = 1;
/* Look for triangles having this edge. Note that I'm only */
/* comparing each triangle's destination with the subsegment; */
/* each triangle's apex is handled through a different vertex. */
/* Because each triangle appears on three vertices' lists, each */
/* occurrence of a triangle on a list can (and does) represent */
/* an edge. In this way, most edges are represented twice, and */
/* every triangle-subsegment bond is represented once. */
while (notfound && (checktri.tri != m->dummytri)) {
dest(checktri, checkdest);
if (shorg == checkdest) {
/* We have a match. Remove this triangle from the list. */
*prevlink = checktri.tri[6 + checktri.orient];
/* Bond the subsegment to the triangle. */
tsbond(checktri, subsegloop);
/* Check if this is a boundary edge. */
sym(checktri, checkneighbor);
if (checkneighbor.tri == m->dummytri) {
/* The next line doesn't insert a subsegment (because there's */
/* already one there), but it sets the boundary markers of */
/* the existing subsegment and its vertices. */
insertsubseg(m, b, &checktri, 1);
hullsize++;
}
notfound = 0;
}
/* Find the next triangle in the stack. */
prevlink = &checktri.tri[6 + checktri.orient];
nexttri = checktri.tri[6 + checktri.orient];
decode(nexttri, checktri);
}
}
subsegloop.ss = subsegtraverse(m);
segmentnumber++;
}
}
/* Mark the remaining edges as not being attached to any subsegment. */
/* Also, count the (yet uncounted) boundary edges. */
for (i = 0; i < m->vertices.items; i++) {
/* Search the stack of triangles adjacent to a vertex. */
nexttri = vertexarray[i];
decode(nexttri, checktri);
while (checktri.tri != m->dummytri) {
/* Find the next triangle in the stack before this */
/* information gets overwritten. */
nexttri = checktri.tri[6 + checktri.orient];
/* No adjacent subsegment. (This overwrites the stack info.) */
tsdissolve(checktri);
sym(checktri, checkneighbor);
if (checkneighbor.tri == m->dummytri) {
insertsubseg(m, b, &checktri, 1);
hullsize++;
}
decode(nexttri, checktri);
}
}
trifree((VOID *) vertexarray);
return hullsize;
}
#endif /* not CDT_ONLY */
/** **/
/** **/
/********* General mesh construction routines end here *********/
/********* Segment insertion begins here *********/
/** **/
/** **/
/*****************************************************************************/
/* */
/* finddirection() Find the first triangle on the path from one point */
/* to another. */
/* */
/* Finds the triangle that intersects a line segment drawn from the */
/* origin of `searchtri' to the point `searchpoint', and returns the result */
/* in `searchtri'. The origin of `searchtri' does not change, even though */
/* the triangle returned may differ from the one passed in. This routine */
/* is used to find the direction to move in to get from one point to */
/* another. */
/* */
/* The return value notes whether the destination or apex of the found */
/* triangle is collinear with the two points in question. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
enum finddirectionresult finddirection(struct mesh *m, struct behavior *b,
struct otri *searchtri,
vertex searchpoint)
#else /* not ANSI_DECLARATORS */
enum finddirectionresult finddirection(m, b, searchtri, searchpoint)
struct mesh *m;
struct behavior *b;
struct otri *searchtri;
vertex searchpoint;
#endif /* not ANSI_DECLARATORS */
{
struct otri checktri;
vertex startvertex;
vertex leftvertex, rightvertex;
REAL leftccw, rightccw;
int leftflag, rightflag;
triangle ptr; /* Temporary variable used by onext() and oprev(). */
org(*searchtri, startvertex);
dest(*searchtri, rightvertex);
apex(*searchtri, leftvertex);
/* Is `searchpoint' to the left? */
leftccw = counterclockwise(m, b, searchpoint, startvertex, leftvertex);
leftflag = leftccw > 0.0;
/* Is `searchpoint' to the right? */
rightccw = counterclockwise(m, b, startvertex, searchpoint, rightvertex);
rightflag = rightccw > 0.0;
if (leftflag && rightflag) {
/* `searchtri' faces directly away from `searchpoint'. We could go left */
/* or right. Ask whether it's a triangle or a boundary on the left. */
onext(*searchtri, checktri);
if (checktri.tri == m->dummytri) {
leftflag = 0;
} else {
rightflag = 0;
}
}
while (leftflag) {
/* Turn left until satisfied. */
onextself(*searchtri);
if (searchtri->tri == m->dummytri) {
printf("Internal error in finddirection(): Unable to find a\n");
printf(" triangle leading from (%.12g, %.12g) to", startvertex[0],
startvertex[1]);
printf(" (%.12g, %.12g).\n", searchpoint[0], searchpoint[1]);
internalerror();
}
apex(*searchtri, leftvertex);
rightccw = leftccw;
leftccw = counterclockwise(m, b, searchpoint, startvertex, leftvertex);
leftflag = leftccw > 0.0;
}
while (rightflag) {
/* Turn right until satisfied. */
oprevself(*searchtri);
if (searchtri->tri == m->dummytri) {
printf("Internal error in finddirection(): Unable to find a\n");
printf(" triangle leading from (%.12g, %.12g) to", startvertex[0],
startvertex[1]);
printf(" (%.12g, %.12g).\n", searchpoint[0], searchpoint[1]);
internalerror();
}
dest(*searchtri, rightvertex);
leftccw = rightccw;
rightccw = counterclockwise(m, b, startvertex, searchpoint, rightvertex);
rightflag = rightccw > 0.0;
}
if (leftccw == 0.0) {
return LEFTCOLLINEAR;
} else if (rightccw == 0.0) {
return RIGHTCOLLINEAR;
} else {
return WITHIN;
}
}
/*****************************************************************************/
/* */
/* segmentintersection() Find the intersection of an existing segment */
/* and a segment that is being inserted. Insert */
/* a vertex at the intersection, splitting an */
/* existing subsegment. */
/* */
/* The segment being inserted connects the apex of splittri to endpoint2. */
/* splitsubseg is the subsegment being split, and MUST adjoin splittri. */
/* Hence, endpoints of the subsegment being split are the origin and */
/* destination of splittri. */
/* */
/* On completion, splittri is a handle having the newly inserted */
/* intersection point as its origin, and endpoint1 as its destination. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
void segmentintersection(struct mesh *m, struct behavior *b,
struct otri *splittri, struct osub *splitsubseg,
vertex endpoint2)
#else /* not ANSI_DECLARATORS */
void segmentintersection(m, b, splittri, splitsubseg, endpoint2)
struct mesh *m;
struct behavior *b;
struct otri *splittri;
struct osub *splitsubseg;
vertex endpoint2;
#endif /* not ANSI_DECLARATORS */
{
struct osub opposubseg;
vertex endpoint1;
vertex torg, tdest;
vertex leftvertex, rightvertex;
vertex newvertex;
enum insertvertexresult success;
enum finddirectionresult collinear;
REAL ex, ey;
REAL tx, ty;
REAL etx, ety;
REAL split, denom;
int i;
triangle ptr; /* Temporary variable used by onext(). */
subseg sptr; /* Temporary variable used by snext(). */
/* Find the other three segment endpoints. */
apex(*splittri, endpoint1);
org(*splittri, torg);
dest(*splittri, tdest);
/* Segment intersection formulae; see the Antonio reference. */
tx = tdest[0] - torg[0];
ty = tdest[1] - torg[1];
ex = endpoint2[0] - endpoint1[0];
ey = endpoint2[1] - endpoint1[1];
etx = torg[0] - endpoint2[0];
ety = torg[1] - endpoint2[1];
denom = ty * ex - tx * ey;
if (denom == 0.0) {
printf("Internal error in segmentintersection():");
printf(" Attempt to find intersection of parallel segments.\n");
internalerror();
}
split = (ey * etx - ex * ety) / denom;
/* Create the new vertex. */
newvertex = (vertex) poolalloc(&m->vertices);
/* Interpolate its coordinate and attributes. */
for (i = 0; i < 2 + m->nextras; i++) {
newvertex[i] = torg[i] + split * (tdest[i] - torg[i]);
}
setvertexmark(newvertex, mark(*splitsubseg));
setvertextype(newvertex, INPUTVERTEX);
if (b->verbose > 1) {
printf(
" Splitting subsegment (%.12g, %.12g) (%.12g, %.12g) at (%.12g, %.12g).\n",
torg[0], torg[1], tdest[0], tdest[1], newvertex[0], newvertex[1]);
}
/* Insert the intersection vertex. This should always succeed. */
success = insertvertex(m, b, newvertex, splittri, splitsubseg, 0, 0);
if (success != SUCCESSFULVERTEX) {
printf("Internal error in segmentintersection():\n");
printf(" Failure to split a segment.\n");
internalerror();
}
/* Record a triangle whose origin is the new vertex. */
setvertex2tri(newvertex, encode(*splittri));
if (m->steinerleft > 0) {
m->steinerleft--;
}
/* Divide the segment into two, and correct the segment endpoints. */
ssymself(*splitsubseg);
spivot(*splitsubseg, opposubseg);
sdissolve(*splitsubseg);
sdissolve(opposubseg);
do {
setsegorg(*splitsubseg, newvertex);
snextself(*splitsubseg);
} while (splitsubseg->ss != m->dummysub);
do {
setsegorg(opposubseg, newvertex);
snextself(opposubseg);
} while (opposubseg.ss != m->dummysub);
/* Inserting the vertex may have caused edge flips. We wish to rediscover */
/* the edge connecting endpoint1 to the new intersection vertex. */
collinear = finddirection(m, b, splittri, endpoint1);
dest(*splittri, rightvertex);
apex(*splittri, leftvertex);
if ((leftvertex[0] == endpoint1[0]) && (leftvertex[1] == endpoint1[1])) {
onextself(*splittri);
} else if ((rightvertex[0] != endpoint1[0]) ||
(rightvertex[1] != endpoint1[1])) {
printf("Internal error in segmentintersection():\n");
printf(" Topological inconsistency after splitting a segment.\n");
internalerror();
}
/* `splittri' should have destination endpoint1. */
}
/*****************************************************************************/
/* */
/* scoutsegment() Scout the first triangle on the path from one endpoint */
/* to another, and check for completion (reaching the */
/* second endpoint), a collinear vertex, or the */
/* intersection of two segments. */
/* */
/* Returns one if the entire segment is successfully inserted, and zero if */
/* the job must be finished by conformingedge() or constrainededge(). */
/* */
/* If the first triangle on the path has the second endpoint as its */
/* destination or apex, a subsegment is inserted and the job is done. */
/* */
/* If the first triangle on the path has a destination or apex that lies on */
/* the segment, a subsegment is inserted connecting the first endpoint to */
/* the collinear vertex, and the search is continued from the collinear */
/* vertex. */
/* */
/* If the first triangle on the path has a subsegment opposite its origin, */
/* then there is a segment that intersects the segment being inserted. */
/* Their intersection vertex is inserted, splitting the subsegment. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
int scoutsegment(struct mesh *m, struct behavior *b, struct otri *searchtri,
vertex endpoint2, int newmark)
#else /* not ANSI_DECLARATORS */
int scoutsegment(m, b, searchtri, endpoint2, newmark)
struct mesh *m;
struct behavior *b;
struct otri *searchtri;
vertex endpoint2;
int newmark;
#endif /* not ANSI_DECLARATORS */
{
struct otri crosstri;
struct osub crosssubseg;
vertex leftvertex, rightvertex;
enum finddirectionresult collinear;
subseg sptr; /* Temporary variable used by tspivot(). */
collinear = finddirection(m, b, searchtri, endpoint2);
dest(*searchtri, rightvertex);
apex(*searchtri, leftvertex);
if (((leftvertex[0] == endpoint2[0]) && (leftvertex[1] == endpoint2[1])) ||
((rightvertex[0] == endpoint2[0]) && (rightvertex[1] == endpoint2[1]))) {
/* The segment is already an edge in the mesh. */
if ((leftvertex[0] == endpoint2[0]) && (leftvertex[1] == endpoint2[1])) {
lprevself(*searchtri);
}
/* Insert a subsegment, if there isn't already one there. */
insertsubseg(m, b, searchtri, newmark);
return 1;
} else if (collinear == LEFTCOLLINEAR) {
/* We've collided with a vertex between the segment's endpoints. */
/* Make the collinear vertex be the triangle's origin. */
lprevself(*searchtri);
insertsubseg(m, b, searchtri, newmark);
/* Insert the remainder of the segment. */
return scoutsegment(m, b, searchtri, endpoint2, newmark);
} else if (collinear == RIGHTCOLLINEAR) {
/* We've collided with a vertex between the segment's endpoints. */
insertsubseg(m, b, searchtri, newmark);
/* Make the collinear vertex be the triangle's origin. */
lnextself(*searchtri);
/* Insert the remainder of the segment. */
return scoutsegment(m, b, searchtri, endpoint2, newmark);
} else {
lnext(*searchtri, crosstri);
tspivot(crosstri, crosssubseg);
/* Check for a crossing segment. */
if (crosssubseg.ss == m->dummysub) {
return 0;
} else {
/* Insert a vertex at the intersection. */
segmentintersection(m, b, &crosstri, &crosssubseg, endpoint2);
otricopy(crosstri, *searchtri);
insertsubseg(m, b, searchtri, newmark);
/* Insert the remainder of the segment. */
return scoutsegment(m, b, searchtri, endpoint2, newmark);
}
}
}
/*****************************************************************************/
/* */
/* conformingedge() Force a segment into a conforming Delaunay */
/* triangulation by inserting a vertex at its midpoint, */
/* and recursively forcing in the two half-segments if */
/* necessary. */
/* */
/* Generates a sequence of subsegments connecting `endpoint1' to */
/* `endpoint2'. `newmark' is the boundary marker of the segment, assigned */
/* to each new splitting vertex and subsegment. */
/* */
/* Note that conformingedge() does not always maintain the conforming */
/* Delaunay property. Once inserted, segments are locked into place; */
/* vertices inserted later (to force other segments in) may render these */
/* fixed segments non-Delaunay. The conforming Delaunay property will be */
/* restored by enforcequality() by splitting encroached subsegments. */
/* */
/*****************************************************************************/
#ifndef REDUCED
#ifndef CDT_ONLY
#ifdef ANSI_DECLARATORS
void conformingedge(struct mesh *m, struct behavior *b,
vertex endpoint1, vertex endpoint2, int newmark)
#else /* not ANSI_DECLARATORS */
void conformingedge(m, b, endpoint1, endpoint2, newmark)
struct mesh *m;
struct behavior *b;
vertex endpoint1;
vertex endpoint2;
int newmark;
#endif /* not ANSI_DECLARATORS */
{
struct otri searchtri1, searchtri2;
struct osub brokensubseg;
vertex newvertex;
vertex midvertex1, midvertex2;
enum insertvertexresult success;
int i;
subseg sptr; /* Temporary variable used by tspivot(). */
if (b->verbose > 2) {
printf("Forcing segment into triangulation by recursive splitting:\n");
printf(" (%.12g, %.12g) (%.12g, %.12g)\n", endpoint1[0], endpoint1[1],
endpoint2[0], endpoint2[1]);
}
/* Create a new vertex to insert in the middle of the segment. */
newvertex = (vertex) poolalloc(&m->vertices);
/* Interpolate coordinates and attributes. */
for (i = 0; i < 2 + m->nextras; i++) {
newvertex[i] = 0.5 * (endpoint1[i] + endpoint2[i]);
}
setvertexmark(newvertex, newmark);
setvertextype(newvertex, SEGMENTVERTEX);
/* No known triangle to search from. */
searchtri1.tri = m->dummytri;
/* Attempt to insert the new vertex. */
success = insertvertex(m, b, newvertex, &searchtri1, (struct osub *) NULL,
0, 0);
if (success == DUPLICATEVERTEX) {
if (b->verbose > 2) {
printf(" Segment intersects existing vertex (%.12g, %.12g).\n",
newvertex[0], newvertex[1]);
}
/* Use the vertex that's already there. */
vertexdealloc(m, newvertex);
org(searchtri1, newvertex);
} else {
if (success == VIOLATINGVERTEX) {
if (b->verbose > 2) {
printf(" Two segments intersect at (%.12g, %.12g).\n",
newvertex[0], newvertex[1]);
}
/* By fluke, we've landed right on another segment. Split it. */
tspivot(searchtri1, brokensubseg);
success = insertvertex(m, b, newvertex, &searchtri1, &brokensubseg,
0, 0);
if (success != SUCCESSFULVERTEX) {
printf("Internal error in conformingedge():\n");
printf(" Failure to split a segment.\n");
internalerror();
}
}
/* The vertex has been inserted successfully. */
if (m->steinerleft > 0) {
m->steinerleft--;
}
}
otricopy(searchtri1, searchtri2);
/* `searchtri1' and `searchtri2' are fastened at their origins to */
/* `newvertex', and will be directed toward `endpoint1' and `endpoint2' */
/* respectively. First, we must get `searchtri2' out of the way so it */
/* won't be invalidated during the insertion of the first half of the */
/* segment. */
finddirection(m, b, &searchtri2, endpoint2);
if (!scoutsegment(m, b, &searchtri1, endpoint1, newmark)) {
/* The origin of searchtri1 may have changed if a collision with an */
/* intervening vertex on the segment occurred. */
org(searchtri1, midvertex1);
conformingedge(m, b, midvertex1, endpoint1, newmark);
}
if (!scoutsegment(m, b, &searchtri2, endpoint2, newmark)) {
/* The origin of searchtri2 may have changed if a collision with an */
/* intervening vertex on the segment occurred. */
org(searchtri2, midvertex2);
conformingedge(m, b, midvertex2, endpoint2, newmark);
}
}
#endif /* not CDT_ONLY */
#endif /* not REDUCED */
/*****************************************************************************/
/* */
/* delaunayfixup() Enforce the Delaunay condition at an edge, fanning out */
/* recursively from an existing vertex. Pay special */
/* attention to stacking inverted triangles. */
/* */
/* This is a support routine for inserting segments into a constrained */
/* Delaunay triangulation. */
/* */
/* The origin of fixuptri is treated as if it has just been inserted, and */
/* the local Delaunay condition needs to be enforced. It is only enforced */
/* in one sector, however, that being the angular range defined by */
/* fixuptri. */
/* */
/* This routine also needs to make decisions regarding the "stacking" of */
/* triangles. (Read the description of constrainededge() below before */
/* reading on here, so you understand the algorithm.) If the position of */
/* the new vertex (the origin of fixuptri) indicates that the vertex before */
/* it on the polygon is a reflex vertex, then "stack" the triangle by */
/* doing nothing. (fixuptri is an inverted triangle, which is how stacked */
/* triangles are identified.) */
/* */
/* Otherwise, check whether the vertex before that was a reflex vertex. */
/* If so, perform an edge flip, thereby eliminating an inverted triangle */
/* (popping it off the stack). The edge flip may result in the creation */
/* of a new inverted triangle, depending on whether or not the new vertex */
/* is visible to the vertex three edges behind on the polygon. */
/* */
/* If neither of the two vertices behind the new vertex are reflex */
/* vertices, fixuptri and fartri, the triangle opposite it, are not */
/* inverted; hence, ensure that the edge between them is locally Delaunay. */
/* */
/* `leftside' indicates whether or not fixuptri is to the left of the */
/* segment being inserted. (Imagine that the segment is pointing up from */
/* endpoint1 to endpoint2.) */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
void delaunayfixup(struct mesh *m, struct behavior *b,
struct otri *fixuptri, int leftside)
#else /* not ANSI_DECLARATORS */
void delaunayfixup(m, b, fixuptri, leftside)
struct mesh *m;
struct behavior *b;
struct otri *fixuptri;
int leftside;
#endif /* not ANSI_DECLARATORS */
{
struct otri neartri;
struct otri fartri;
struct osub faredge;
vertex nearvertex, leftvertex, rightvertex, farvertex;
triangle ptr; /* Temporary variable used by sym(). */
subseg sptr; /* Temporary variable used by tspivot(). */
lnext(*fixuptri, neartri);
sym(neartri, fartri);
/* Check if the edge opposite the origin of fixuptri can be flipped. */
if (fartri.tri == m->dummytri) {
return;
}
tspivot(neartri, faredge);
if (faredge.ss != m->dummysub) {
return;
}
/* Find all the relevant vertices. */
apex(neartri, nearvertex);
org(neartri, leftvertex);
dest(neartri, rightvertex);
apex(fartri, farvertex);
/* Check whether the previous polygon vertex is a reflex vertex. */
if (leftside) {
if (counterclockwise(m, b, nearvertex, leftvertex, farvertex) <= 0.0) {
/* leftvertex is a reflex vertex too. Nothing can */
/* be done until a convex section is found. */
return;
}
} else {
if (counterclockwise(m, b, farvertex, rightvertex, nearvertex) <= 0.0) {
/* rightvertex is a reflex vertex too. Nothing can */
/* be done until a convex section is found. */
return;
}
}
if (counterclockwise(m, b, rightvertex, leftvertex, farvertex) > 0.0) {
/* fartri is not an inverted triangle, and farvertex is not a reflex */
/* vertex. As there are no reflex vertices, fixuptri isn't an */
/* inverted triangle, either. Hence, test the edge between the */
/* triangles to ensure it is locally Delaunay. */
if (incircle(m, b, leftvertex, farvertex, rightvertex, nearvertex) <=
0.0) {
return;
}
/* Not locally Delaunay; go on to an edge flip. */
} /* else fartri is inverted; remove it from the stack by flipping. */
flip(m, b, &neartri);
lprevself(*fixuptri); /* Restore the origin of fixuptri after the flip. */
/* Recursively process the two triangles that result from the flip. */
delaunayfixup(m, b, fixuptri, leftside);
delaunayfixup(m, b, &fartri, leftside);
}
/*****************************************************************************/
/* */
/* constrainededge() Force a segment into a constrained Delaunay */
/* triangulation by deleting the triangles it */
/* intersects, and triangulating the polygons that */
/* form on each side of it. */
/* */
/* Generates a single subsegment connecting `endpoint1' to `endpoint2'. */
/* The triangle `starttri' has `endpoint1' as its origin. `newmark' is the */
/* boundary marker of the segment. */
/* */
/* To insert a segment, every triangle whose interior intersects the */
/* segment is deleted. The union of these deleted triangles is a polygon */
/* (which is not necessarily monotone, but is close enough), which is */
/* divided into two polygons by the new segment. This routine's task is */
/* to generate the Delaunay triangulation of these two polygons. */
/* */
/* You might think of this routine's behavior as a two-step process. The */
/* first step is to walk from endpoint1 to endpoint2, flipping each edge */
/* encountered. This step creates a fan of edges connected to endpoint1, */
/* including the desired edge to endpoint2. The second step enforces the */
/* Delaunay condition on each side of the segment in an incremental manner: */
/* proceeding along the polygon from endpoint1 to endpoint2 (this is done */
/* independently on each side of the segment), each vertex is "enforced" */
/* as if it had just been inserted, but affecting only the previous */
/* vertices. The result is the same as if the vertices had been inserted */
/* in the order they appear on the polygon, so the result is Delaunay. */
/* */
/* In truth, constrainededge() interleaves these two steps. The procedure */
/* walks from endpoint1 to endpoint2, and each time an edge is encountered */
/* and flipped, the newly exposed vertex (at the far end of the flipped */
/* edge) is "enforced" upon the previously flipped edges, usually affecting */
/* only one side of the polygon (depending upon which side of the segment */
/* the vertex falls on). */
/* */
/* The algorithm is complicated by the need to handle polygons that are not */
/* convex. Although the polygon is not necessarily monotone, it can be */
/* triangulated in a manner similar to the stack-based algorithms for */
/* monotone polygons. For each reflex vertex (local concavity) of the */
/* polygon, there will be an inverted triangle formed by one of the edge */
/* flips. (An inverted triangle is one with negative area - that is, its */
/* vertices are arranged in clockwise order - and is best thought of as a */
/* wrinkle in the fabric of the mesh.) Each inverted triangle can be */
/* thought of as a reflex vertex pushed on the stack, waiting to be fixed */
/* later. */
/* */
/* A reflex vertex is popped from the stack when a vertex is inserted that */
/* is visible to the reflex vertex. (However, if the vertex behind the */
/* reflex vertex is not visible to the reflex vertex, a new inverted */
/* triangle will take its place on the stack.) These details are handled */
/* by the delaunayfixup() routine above. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
void constrainededge(struct mesh *m, struct behavior *b,
struct otri *starttri, vertex endpoint2, int newmark)
#else /* not ANSI_DECLARATORS */
void constrainededge(m, b, starttri, endpoint2, newmark)
struct mesh *m;
struct behavior *b;
struct otri *starttri;
vertex endpoint2;
int newmark;
#endif /* not ANSI_DECLARATORS */
{
struct otri fixuptri, fixuptri2;
struct osub crosssubseg;
vertex endpoint1;
vertex farvertex;
REAL area;
int collision;
int done;
triangle ptr; /* Temporary variable used by sym() and oprev(). */
subseg sptr; /* Temporary variable used by tspivot(). */
org(*starttri, endpoint1);
lnext(*starttri, fixuptri);
flip(m, b, &fixuptri);
/* `collision' indicates whether we have found a vertex directly */
/* between endpoint1 and endpoint2. */
collision = 0;
done = 0;
do {
org(fixuptri, farvertex);
/* `farvertex' is the extreme point of the polygon we are "digging" */
/* to get from endpoint1 to endpoint2. */
if ((farvertex[0] == endpoint2[0]) && (farvertex[1] == endpoint2[1])) {
oprev(fixuptri, fixuptri2);
/* Enforce the Delaunay condition around endpoint2. */
delaunayfixup(m, b, &fixuptri, 0);
delaunayfixup(m, b, &fixuptri2, 1);
done = 1;
} else {
/* Check whether farvertex is to the left or right of the segment */
/* being inserted, to decide which edge of fixuptri to dig */
/* through next. */
area = counterclockwise(m, b, endpoint1, endpoint2, farvertex);
if (area == 0.0) {
/* We've collided with a vertex between endpoint1 and endpoint2. */
collision = 1;
oprev(fixuptri, fixuptri2);
/* Enforce the Delaunay condition around farvertex. */
delaunayfixup(m, b, &fixuptri, 0);
delaunayfixup(m, b, &fixuptri2, 1);
done = 1;
} else {
if (area > 0.0) { /* farvertex is to the left of the segment. */
oprev(fixuptri, fixuptri2);
/* Enforce the Delaunay condition around farvertex, on the */
/* left side of the segment only. */
delaunayfixup(m, b, &fixuptri2, 1);
/* Flip the edge that crosses the segment. After the edge is */
/* flipped, one of its endpoints is the fan vertex, and the */
/* destination of fixuptri is the fan vertex. */
lprevself(fixuptri);
} else { /* farvertex is to the right of the segment. */
delaunayfixup(m, b, &fixuptri, 0);
/* Flip the edge that crosses the segment. After the edge is */
/* flipped, one of its endpoints is the fan vertex, and the */
/* destination of fixuptri is the fan vertex. */
oprevself(fixuptri);
}
/* Check for two intersecting segments. */
tspivot(fixuptri, crosssubseg);
if (crosssubseg.ss == m->dummysub) {
flip(m, b, &fixuptri); /* May create inverted triangle at left. */
} else {
/* We've collided with a segment between endpoint1 and endpoint2. */
collision = 1;
/* Insert a vertex at the intersection. */
segmentintersection(m, b, &fixuptri, &crosssubseg, endpoint2);
done = 1;
}
}
}
} while (!done);
/* Insert a subsegment to make the segment permanent. */
insertsubseg(m, b, &fixuptri, newmark);
/* If there was a collision with an interceding vertex, install another */
/* segment connecting that vertex with endpoint2. */
if (collision) {
/* Insert the remainder of the segment. */
if (!scoutsegment(m, b, &fixuptri, endpoint2, newmark)) {
constrainededge(m, b, &fixuptri, endpoint2, newmark);
}
}
}
/*****************************************************************************/
/* */
/* insertsegment() Insert a PSLG segment into a triangulation. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
void insertsegment(struct mesh *m, struct behavior *b,
vertex endpoint1, vertex endpoint2, int newmark)
#else /* not ANSI_DECLARATORS */
void insertsegment(m, b, endpoint1, endpoint2, newmark)
struct mesh *m;
struct behavior *b;
vertex endpoint1;
vertex endpoint2;
int newmark;
#endif /* not ANSI_DECLARATORS */
{
struct otri searchtri1, searchtri2;
triangle encodedtri;
vertex checkvertex;
triangle ptr; /* Temporary variable used by sym(). */
if (b->verbose > 1) {
printf(" Connecting (%.12g, %.12g) to (%.12g, %.12g).\n",
endpoint1[0], endpoint1[1], endpoint2[0], endpoint2[1]);
}
/* Find a triangle whose origin is the segment's first endpoint. */
checkvertex = (vertex) NULL;
encodedtri = vertex2tri(endpoint1);
if (encodedtri != (triangle) NULL) {
decode(encodedtri, searchtri1);
org(searchtri1, checkvertex);
}
if (checkvertex != endpoint1) {
/* Find a boundary triangle to search from. */
searchtri1.tri = m->dummytri;
searchtri1.orient = 0;
symself(searchtri1);
/* Search for the segment's first endpoint by point location. */
if (locate(m, b, endpoint1, &searchtri1) != ONVERTEX) {
printf(
"Internal error in insertsegment(): Unable to locate PSLG vertex\n");
printf(" (%.12g, %.12g) in triangulation.\n",
endpoint1[0], endpoint1[1]);
internalerror();
}
}
/* Remember this triangle to improve subsequent point location. */
otricopy(searchtri1, m->recenttri);
/* Scout the beginnings of a path from the first endpoint */
/* toward the second. */
if (scoutsegment(m, b, &searchtri1, endpoint2, newmark)) {
/* The segment was easily inserted. */
return;
}
/* The first endpoint may have changed if a collision with an intervening */
/* vertex on the segment occurred. */
org(searchtri1, endpoint1);
/* Find a triangle whose origin is the segment's second endpoint. */
checkvertex = (vertex) NULL;
encodedtri = vertex2tri(endpoint2);
if (encodedtri != (triangle) NULL) {
decode(encodedtri, searchtri2);
org(searchtri2, checkvertex);
}
if (checkvertex != endpoint2) {
/* Find a boundary triangle to search from. */
searchtri2.tri = m->dummytri;
searchtri2.orient = 0;
symself(searchtri2);
/* Search for the segment's second endpoint by point location. */
if (locate(m, b, endpoint2, &searchtri2) != ONVERTEX) {
printf(
"Internal error in insertsegment(): Unable to locate PSLG vertex\n");
printf(" (%.12g, %.12g) in triangulation.\n",
endpoint2[0], endpoint2[1]);
internalerror();
}
}
/* Remember this triangle to improve subsequent point location. */
otricopy(searchtri2, m->recenttri);
/* Scout the beginnings of a path from the second endpoint */
/* toward the first. */
if (scoutsegment(m, b, &searchtri2, endpoint1, newmark)) {
/* The segment was easily inserted. */
return;
}
/* The second endpoint may have changed if a collision with an intervening */
/* vertex on the segment occurred. */
org(searchtri2, endpoint2);
#ifndef REDUCED
#ifndef CDT_ONLY
if (b->splitseg) {
/* Insert vertices to force the segment into the triangulation. */
conformingedge(m, b, endpoint1, endpoint2, newmark);
} else {
#endif /* not CDT_ONLY */
#endif /* not REDUCED */
/* Insert the segment directly into the triangulation. */
constrainededge(m, b, &searchtri1, endpoint2, newmark);
#ifndef REDUCED
#ifndef CDT_ONLY
}
#endif /* not CDT_ONLY */
#endif /* not REDUCED */
}
/*****************************************************************************/
/* */
/* markhull() Cover the convex hull of a triangulation with subsegments. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
void markhull(struct mesh *m, struct behavior *b)
#else /* not ANSI_DECLARATORS */
void markhull(m, b)
struct mesh *m;
struct behavior *b;
#endif /* not ANSI_DECLARATORS */
{
struct otri hulltri;
struct otri nexttri;
struct otri starttri;
triangle ptr; /* Temporary variable used by sym() and oprev(). */
/* Find a triangle handle on the hull. */
hulltri.tri = m->dummytri;
hulltri.orient = 0;
symself(hulltri);
/* Remember where we started so we know when to stop. */
otricopy(hulltri, starttri);
/* Go once counterclockwise around the convex hull. */
do {
/* Create a subsegment if there isn't already one here. */
insertsubseg(m, b, &hulltri, 1);
/* To find the next hull edge, go clockwise around the next vertex. */
lnextself(hulltri);
oprev(hulltri, nexttri);
while (nexttri.tri != m->dummytri) {
otricopy(nexttri, hulltri);
oprev(hulltri, nexttri);
}
} while (!otriequal(hulltri, starttri));
}
/*****************************************************************************/
/* */
/* formskeleton() Create the segments of a triangulation, including PSLG */
/* segments and edges on the convex hull. */
/* */
/* The PSLG segments are read from a .poly file. The return value is the */
/* number of segments in the file. */
/* */
/*****************************************************************************/
#ifdef TRILIBRARY
#ifdef ANSI_DECLARATORS
void formskeleton(struct mesh *m, struct behavior *b, int *segmentlist,
int *segmentmarkerlist, int numberofsegments)
#else /* not ANSI_DECLARATORS */
void formskeleton(m, b, segmentlist, segmentmarkerlist, numberofsegments)
struct mesh *m;
struct behavior *b;
int *segmentlist;
int *segmentmarkerlist;
int numberofsegments;
#endif /* not ANSI_DECLARATORS */
#else /* not TRILIBRARY */
#ifdef ANSI_DECLARATORS
void formskeleton(struct mesh *m, struct behavior *b,
FILE *polyfile, char *polyfilename)
#else /* not ANSI_DECLARATORS */
void formskeleton(m, b, polyfile, polyfilename)
struct mesh *m;
struct behavior *b;
FILE *polyfile;
char *polyfilename;
#endif /* not ANSI_DECLARATORS */
#endif /* not TRILIBRARY */
{
#ifdef TRILIBRARY
char polyfilename[6];
int index;
#else /* not TRILIBRARY */
char inputline[INPUTLINESIZE];
char *stringptr;
#endif /* not TRILIBRARY */
vertex endpoint1, endpoint2;
int segmentmarkers;
int end1, end2;
int boundmarker;
int i;
if (b->poly) {
if (!b->quiet) {
printf("Recovering segments in Delaunay triangulation.\n");
}
#ifdef TRILIBRARY
strcpy(polyfilename, "input");
m->insegments = numberofsegments;
segmentmarkers = segmentmarkerlist != (int *) NULL;
index = 0;
#else /* not TRILIBRARY */
/* Read the segments from a .poly file. */
/* Read number of segments and number of boundary markers. */
stringptr = readline(inputline, polyfile, polyfilename);
m->insegments = (int) strtol(stringptr, &stringptr, 0);
stringptr = findfield(stringptr);
if (*stringptr == '\0') {
segmentmarkers = 0;
} else {
segmentmarkers = (int) strtol(stringptr, &stringptr, 0);
}
#endif /* not TRILIBRARY */
/* If the input vertices are collinear, there is no triangulation, */
/* so don't try to insert segments. */
if (m->triangles.items == 0) {
return;
}
/* If segments are to be inserted, compute a mapping */
/* from vertices to triangles. */
if (m->insegments > 0) {
makevertexmap(m, b);
if (b->verbose) {
printf(" Recovering PSLG segments.\n");
}
}
boundmarker = 0;
/* Read and insert the segments. */
for (i = 0; i < m->insegments; i++) {
#ifdef TRILIBRARY
end1 = segmentlist[index++];
end2 = segmentlist[index++];
if (segmentmarkers) {
boundmarker = segmentmarkerlist[i];
}
#else /* not TRILIBRARY */
stringptr = readline(inputline, polyfile, b->inpolyfilename);
stringptr = findfield(stringptr);
if (*stringptr == '\0') {
printf("Error: Segment %d has no endpoints in %s.\n",
b->firstnumber + i, polyfilename);
triexit(1);
} else {
end1 = (int) strtol(stringptr, &stringptr, 0);
}
stringptr = findfield(stringptr);
if (*stringptr == '\0') {
printf("Error: Segment %d is missing its second endpoint in %s.\n",
b->firstnumber + i, polyfilename);
triexit(1);
} else {
end2 = (int) strtol(stringptr, &stringptr, 0);
}
if (segmentmarkers) {
stringptr = findfield(stringptr);
if (*stringptr == '\0') {
boundmarker = 0;
} else {
boundmarker = (int) strtol(stringptr, &stringptr, 0);
}
}
#endif /* not TRILIBRARY */
if ((end1 < b->firstnumber) ||
(end1 >= b->firstnumber + m->invertices)) {
if (!b->quiet) {
printf("Warning: Invalid first endpoint of segment %d in %s.\n",
b->firstnumber + i, polyfilename);
}
} else if ((end2 < b->firstnumber) ||
(end2 >= b->firstnumber + m->invertices)) {
if (!b->quiet) {
printf("Warning: Invalid second endpoint of segment %d in %s.\n",
b->firstnumber + i, polyfilename);
}
} else {
/* Find the vertices numbered `end1' and `end2'. */
endpoint1 = getvertex(m, b, end1);
endpoint2 = getvertex(m, b, end2);
if ((endpoint1[0] == endpoint2[0]) && (endpoint1[1] == endpoint2[1])) {
if (!b->quiet) {
printf("Warning: Endpoints of segment %d are coincident in %s.\n",
b->firstnumber + i, polyfilename);
}
} else {
insertsegment(m, b, endpoint1, endpoint2, boundmarker);
}
}
}
} else {
m->insegments = 0;
}
if (b->convex || !b->poly) {
/* Enclose the convex hull with subsegments. */
if (b->verbose) {
printf(" Enclosing convex hull with segments.\n");
}
markhull(m, b);
}
}
/** **/
/** **/
/********* Segment insertion ends here *********/
/********* Carving out holes and concavities begins here *********/
/** **/
/** **/
/*****************************************************************************/
/* */
/* infecthull() Virally infect all of the triangles of the convex hull */
/* that are not protected by subsegments. Where there are */
/* subsegments, set boundary markers as appropriate. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
void infecthull(struct mesh *m, struct behavior *b)
#else /* not ANSI_DECLARATORS */
void infecthull(m, b)
struct mesh *m;
struct behavior *b;
#endif /* not ANSI_DECLARATORS */
{
struct otri hulltri;
struct otri nexttri;
struct otri starttri;
struct osub hullsubseg;
triangle **deadtriangle;
vertex horg, hdest;
triangle ptr; /* Temporary variable used by sym(). */
subseg sptr; /* Temporary variable used by tspivot(). */
if (b->verbose) {
printf(" Marking concavities (external triangles) for elimination.\n");
}
/* Find a triangle handle on the hull. */
hulltri.tri = m->dummytri;
hulltri.orient = 0;
symself(hulltri);
/* Remember where we started so we know when to stop. */
otricopy(hulltri, starttri);
/* Go once counterclockwise around the convex hull. */
do {
/* Ignore triangles that are already infected. */
if (!infected(hulltri)) {
/* Is the triangle protected by a subsegment? */
tspivot(hulltri, hullsubseg);
if (hullsubseg.ss == m->dummysub) {
/* The triangle is not protected; infect it. */
if (!infected(hulltri)) {
infect(hulltri);
deadtriangle = (triangle **) poolalloc(&m->viri);
*deadtriangle = hulltri.tri;
}
} else {
/* The triangle is protected; set boundary markers if appropriate. */
if (mark(hullsubseg) == 0) {
setmark(hullsubseg, 1);
org(hulltri, horg);
dest(hulltri, hdest);
if (vertexmark(horg) == 0) {
setvertexmark(horg, 1);
}
if (vertexmark(hdest) == 0) {
setvertexmark(hdest, 1);
}
}
}
}
/* To find the next hull edge, go clockwise around the next vertex. */
lnextself(hulltri);
oprev(hulltri, nexttri);
while (nexttri.tri != m->dummytri) {
otricopy(nexttri, hulltri);
oprev(hulltri, nexttri);
}
} while (!otriequal(hulltri, starttri));
}
/*****************************************************************************/
/* */
/* plague() Spread the virus from all infected triangles to any neighbors */
/* not protected by subsegments. Delete all infected triangles. */
/* */
/* This is the procedure that actually creates holes and concavities. */
/* */
/* This procedure operates in two phases. The first phase identifies all */
/* the triangles that will die, and marks them as infected. They are */
/* marked to ensure that each triangle is added to the virus pool only */
/* once, so the procedure will terminate. */
/* */
/* The second phase actually eliminates the infected triangles. It also */
/* eliminates orphaned vertices. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
void plague(struct mesh *m, struct behavior *b)
#else /* not ANSI_DECLARATORS */
void plague(m, b)
struct mesh *m;
struct behavior *b;
#endif /* not ANSI_DECLARATORS */
{
struct otri testtri;
struct otri neighbor;
triangle **virusloop;
triangle **deadtriangle;
struct osub neighborsubseg;
vertex testvertex;
vertex norg, ndest;
vertex deadorg, deaddest, deadapex;
int killorg;
triangle ptr; /* Temporary variable used by sym() and onext(). */
subseg sptr; /* Temporary variable used by tspivot(). */
if (b->verbose) {
printf(" Marking neighbors of marked triangles.\n");
}
/* Loop through all the infected triangles, spreading the virus to */
/* their neighbors, then to their neighbors' neighbors. */
traversalinit(&m->viri);
virusloop = (triangle **) traverse(&m->viri);
while (virusloop != (triangle **) NULL) {
testtri.tri = *virusloop;
/* A triangle is marked as infected by messing with one of its pointers */
/* to subsegments, setting it to an illegal value. Hence, we have to */
/* temporarily uninfect this triangle so that we can examine its */
/* adjacent subsegments. */
uninfect(testtri);
if (b->verbose > 2) {
/* Assign the triangle an orientation for convenience in */
/* checking its vertices. */
testtri.orient = 0;
org(testtri, deadorg);
dest(testtri, deaddest);
apex(testtri, deadapex);
printf(" Checking (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n",
deadorg[0], deadorg[1], deaddest[0], deaddest[1],
deadapex[0], deadapex[1]);
}
/* Check each of the triangle's three neighbors. */
for (testtri.orient = 0; testtri.orient < 3; testtri.orient++) {
/* Find the neighbor. */
sym(testtri, neighbor);
/* Check for a subsegment between the triangle and its neighbor. */
tspivot(testtri, neighborsubseg);
/* Check if the neighbor is nonexistent or already infected. */
if ((neighbor.tri == m->dummytri) || infected(neighbor)) {
if (neighborsubseg.ss != m->dummysub) {
/* There is a subsegment separating the triangle from its */
/* neighbor, but both triangles are dying, so the subsegment */
/* dies too. */
subsegdealloc(m, neighborsubseg.ss);
if (neighbor.tri != m->dummytri) {
/* Make sure the subsegment doesn't get deallocated again */
/* later when the infected neighbor is visited. */
uninfect(neighbor);
tsdissolve(neighbor);
infect(neighbor);
}
}
} else { /* The neighbor exists and is not infected. */
if (neighborsubseg.ss == m->dummysub) {
/* There is no subsegment protecting the neighbor, so */
/* the neighbor becomes infected. */
if (b->verbose > 2) {
org(neighbor, deadorg);
dest(neighbor, deaddest);
apex(neighbor, deadapex);
printf(
" Marking (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n",
deadorg[0], deadorg[1], deaddest[0], deaddest[1],
deadapex[0], deadapex[1]);
}
infect(neighbor);
/* Ensure that the neighbor's neighbors will be infected. */
deadtriangle = (triangle **) poolalloc(&m->viri);
*deadtriangle = neighbor.tri;
} else { /* The neighbor is protected by a subsegment. */
/* Remove this triangle from the subsegment. */
stdissolve(neighborsubseg);
/* The subsegment becomes a boundary. Set markers accordingly. */
if (mark(neighborsubseg) == 0) {
setmark(neighborsubseg, 1);
}
org(neighbor, norg);
dest(neighbor, ndest);
if (vertexmark(norg) == 0) {
setvertexmark(norg, 1);
}
if (vertexmark(ndest) == 0) {
setvertexmark(ndest, 1);
}
}
}
}
/* Remark the triangle as infected, so it doesn't get added to the */
/* virus pool again. */
infect(testtri);
virusloop = (triangle **) traverse(&m->viri);
}
if (b->verbose) {
printf(" Deleting marked triangles.\n");
}
traversalinit(&m->viri);
virusloop = (triangle **) traverse(&m->viri);
while (virusloop != (triangle **) NULL) {
testtri.tri = *virusloop;
/* Check each of the three corners of the triangle for elimination. */
/* This is done by walking around each vertex, checking if it is */
/* still connected to at least one live triangle. */
for (testtri.orient = 0; testtri.orient < 3; testtri.orient++) {
org(testtri, testvertex);
/* Check if the vertex has already been tested. */
if (testvertex != (vertex) NULL) {
killorg = 1;
/* Mark the corner of the triangle as having been tested. */
setorg(testtri, NULL);
/* Walk counterclockwise about the vertex. */
onext(testtri, neighbor);
/* Stop upon reaching a boundary or the starting triangle. */
while ((neighbor.tri != m->dummytri) &&
(!otriequal(neighbor, testtri))) {
if (infected(neighbor)) {
/* Mark the corner of this triangle as having been tested. */
setorg(neighbor, NULL);
} else {
/* A live triangle. The vertex survives. */
killorg = 0;
}
/* Walk counterclockwise about the vertex. */
onextself(neighbor);
}
/* If we reached a boundary, we must walk clockwise as well. */
if (neighbor.tri == m->dummytri) {
/* Walk clockwise about the vertex. */
oprev(testtri, neighbor);
/* Stop upon reaching a boundary. */
while (neighbor.tri != m->dummytri) {
if (infected(neighbor)) {
/* Mark the corner of this triangle as having been tested. */
setorg(neighbor, NULL);
} else {
/* A live triangle. The vertex survives. */
killorg = 0;
}
/* Walk clockwise about the vertex. */
oprevself(neighbor);
}
}
if (killorg) {
if (b->verbose > 1) {
printf(" Deleting vertex (%.12g, %.12g)\n",
testvertex[0], testvertex[1]);
}
setvertextype(testvertex, UNDEADVERTEX);
m->undeads++;
}
}
}
/* Record changes in the number of boundary edges, and disconnect */
/* dead triangles from their neighbors. */
for (testtri.orient = 0; testtri.orient < 3; testtri.orient++) {
sym(testtri, neighbor);
if (neighbor.tri == m->dummytri) {
/* There is no neighboring triangle on this edge, so this edge */
/* is a boundary edge. This triangle is being deleted, so this */
/* boundary edge is deleted. */
m->hullsize--;
} else {
/* Disconnect the triangle from its neighbor. */
dissolve(neighbor);
/* There is a neighboring triangle on this edge, so this edge */
/* becomes a boundary edge when this triangle is deleted. */
m->hullsize++;
}
}
/* Return the dead triangle to the pool of triangles. */
triangledealloc(m, testtri.tri);
virusloop = (triangle **) traverse(&m->viri);
}
/* Empty the virus pool. */
poolrestart(&m->viri);
}
/*****************************************************************************/
/* */
/* regionplague() Spread regional attributes and/or area constraints */
/* (from a .poly file) throughout the mesh. */
/* */
/* This procedure operates in two phases. The first phase spreads an */
/* attribute and/or an area constraint through a (segment-bounded) region. */
/* The triangles are marked to ensure that each triangle is added to the */
/* virus pool only once, so the procedure will terminate. */
/* */
/* The second phase uninfects all infected triangles, returning them to */
/* normal. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
void regionplague(struct mesh *m, struct behavior *b,
REAL attribute, REAL area)
#else /* not ANSI_DECLARATORS */
void regionplague(m, b, attribute, area)
struct mesh *m;
struct behavior *b;
REAL attribute;
REAL area;
#endif /* not ANSI_DECLARATORS */
{
struct otri testtri;
struct otri neighbor;
triangle **virusloop;
triangle **regiontri;
struct osub neighborsubseg;
vertex regionorg, regiondest, regionapex;
triangle ptr; /* Temporary variable used by sym() and onext(). */
subseg sptr; /* Temporary variable used by tspivot(). */
if (b->verbose > 1) {
printf(" Marking neighbors of marked triangles.\n");
}
/* Loop through all the infected triangles, spreading the attribute */
/* and/or area constraint to their neighbors, then to their neighbors' */
/* neighbors. */
traversalinit(&m->viri);
virusloop = (triangle **) traverse(&m->viri);
while (virusloop != (triangle **) NULL) {
testtri.tri = *virusloop;
/* A triangle is marked as infected by messing with one of its pointers */
/* to subsegments, setting it to an illegal value. Hence, we have to */
/* temporarily uninfect this triangle so that we can examine its */
/* adjacent subsegments. */
uninfect(testtri);
if (b->regionattrib) {
/* Set an attribute. */
setelemattribute(testtri, m->eextras, attribute);
}
if (b->vararea) {
/* Set an area constraint. */
setareabound(testtri, area);
}
if (b->verbose > 2) {
/* Assign the triangle an orientation for convenience in */
/* checking its vertices. */
testtri.orient = 0;
org(testtri, regionorg);
dest(testtri, regiondest);
apex(testtri, regionapex);
printf(" Checking (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n",
regionorg[0], regionorg[1], regiondest[0], regiondest[1],
regionapex[0], regionapex[1]);
}
/* Check each of the triangle's three neighbors. */
for (testtri.orient = 0; testtri.orient < 3; testtri.orient++) {
/* Find the neighbor. */
sym(testtri, neighbor);
/* Check for a subsegment between the triangle and its neighbor. */
tspivot(testtri, neighborsubseg);
/* Make sure the neighbor exists, is not already infected, and */
/* isn't protected by a subsegment. */
if ((neighbor.tri != m->dummytri) && !infected(neighbor)
&& (neighborsubseg.ss == m->dummysub)) {
if (b->verbose > 2) {
org(neighbor, regionorg);
dest(neighbor, regiondest);
apex(neighbor, regionapex);
printf(" Marking (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n",
regionorg[0], regionorg[1], regiondest[0], regiondest[1],
regionapex[0], regionapex[1]);
}
/* Infect the neighbor. */
infect(neighbor);
/* Ensure that the neighbor's neighbors will be infected. */
regiontri = (triangle **) poolalloc(&m->viri);
*regiontri = neighbor.tri;
}
}
/* Remark the triangle as infected, so it doesn't get added to the */
/* virus pool again. */
infect(testtri);
virusloop = (triangle **) traverse(&m->viri);
}
/* Uninfect all triangles. */
if (b->verbose > 1) {
printf(" Unmarking marked triangles.\n");
}
traversalinit(&m->viri);
virusloop = (triangle **) traverse(&m->viri);
while (virusloop != (triangle **) NULL) {
testtri.tri = *virusloop;
uninfect(testtri);
virusloop = (triangle **) traverse(&m->viri);
}
/* Empty the virus pool. */
poolrestart(&m->viri);
}
/*****************************************************************************/
/* */
/* carveholes() Find the holes and infect them. Find the area */
/* constraints and infect them. Infect the convex hull. */
/* Spread the infection and kill triangles. Spread the */
/* area constraints. */
/* */
/* This routine mainly calls other routines to carry out all these */
/* functions. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
void carveholes(struct mesh *m, struct behavior *b, REAL *holelist, int holes,
REAL *regionlist, int regions)
#else /* not ANSI_DECLARATORS */
void carveholes(m, b, holelist, holes, regionlist, regions)
struct mesh *m;
struct behavior *b;
REAL *holelist;
int holes;
REAL *regionlist;
int regions;
#endif /* not ANSI_DECLARATORS */
{
struct otri searchtri;
struct otri triangleloop;
struct otri *regiontris;
triangle **holetri;
triangle **regiontri;
vertex searchorg, searchdest;
enum locateresult intersect;
int i;
triangle ptr; /* Temporary variable used by sym(). */
if (!(b->quiet || (b->noholes && b->convex))) {
printf("Removing unwanted triangles.\n");
if (b->verbose && (holes > 0)) {
printf(" Marking holes for elimination.\n");
}
}
if (regions > 0) {
/* Allocate storage for the triangles in which region points fall. */
regiontris = (struct otri *) trimalloc(regions *
(int) sizeof(struct otri));
} else {
regiontris = (struct otri *) NULL;
}
if (((holes > 0) && !b->noholes) || !b->convex || (regions > 0)) {
/* Initialize a pool of viri to be used for holes, concavities, */
/* regional attributes, and/or regional area constraints. */
poolinit(&m->viri, sizeof(triangle *), VIRUSPERBLOCK, VIRUSPERBLOCK, 0);
}
if (!b->convex) {
/* Mark as infected any unprotected triangles on the boundary. */
/* This is one way by which concavities are created. */
infecthull(m, b);
}
if ((holes > 0) && !b->noholes) {
/* Infect each triangle in which a hole lies. */
for (i = 0; i < 2 * holes; i += 2) {
/* Ignore holes that aren't within the bounds of the mesh. */
if ((holelist[i] >= m->xmin) && (holelist[i] <= m->xmax)
&& (holelist[i + 1] >= m->ymin) && (holelist[i + 1] <= m->ymax)) {
/* Start searching from some triangle on the outer boundary. */
searchtri.tri = m->dummytri;
searchtri.orient = 0;
symself(searchtri);
/* Ensure that the hole is to the left of this boundary edge; */
/* otherwise, locate() will falsely report that the hole */
/* falls within the starting triangle. */
org(searchtri, searchorg);
dest(searchtri, searchdest);
if (counterclockwise(m, b, searchorg, searchdest, &holelist[i]) >
0.0) {
/* Find a triangle that contains the hole. */
intersect = locate(m, b, &holelist[i], &searchtri);
if ((intersect != OUTSIDE) && (!infected(searchtri))) {
/* Infect the triangle. This is done by marking the triangle */
/* as infected and including the triangle in the virus pool. */
infect(searchtri);
holetri = (triangle **) poolalloc(&m->viri);
*holetri = searchtri.tri;
}
}
}
}
}
/* Now, we have to find all the regions BEFORE we carve the holes, because */
/* locate() won't work when the triangulation is no longer convex. */
/* (Incidentally, this is the reason why regional attributes and area */
/* constraints can't be used when refining a preexisting mesh, which */
/* might not be convex; they can only be used with a freshly */
/* triangulated PSLG.) */
if (regions > 0) {
/* Find the starting triangle for each region. */
for (i = 0; i < regions; i++) {
regiontris[i].tri = m->dummytri;
/* Ignore region points that aren't within the bounds of the mesh. */
if ((regionlist[4 * i] >= m->xmin) && (regionlist[4 * i] <= m->xmax) &&
(regionlist[4 * i + 1] >= m->ymin) &&
(regionlist[4 * i + 1] <= m->ymax)) {
/* Start searching from some triangle on the outer boundary. */
searchtri.tri = m->dummytri;
searchtri.orient = 0;
symself(searchtri);
/* Ensure that the region point is to the left of this boundary */
/* edge; otherwise, locate() will falsely report that the */
/* region point falls within the starting triangle. */
org(searchtri, searchorg);
dest(searchtri, searchdest);
if (counterclockwise(m, b, searchorg, searchdest, ®ionlist[4 * i]) >
0.0) {
/* Find a triangle that contains the region point. */
intersect = locate(m, b, ®ionlist[4 * i], &searchtri);
if ((intersect != OUTSIDE) && (!infected(searchtri))) {
/* Record the triangle for processing after the */
/* holes have been carved. */
otricopy(searchtri, regiontris[i]);
}
}
}
}
}
if (m->viri.items > 0) {
/* Carve the holes and concavities. */
plague(m, b);
}
/* The virus pool should be empty now. */
if (regions > 0) {
if (!b->quiet) {
if (b->regionattrib) {
if (b->vararea) {
printf("Spreading regional attributes and area constraints.\n");
} else {
printf("Spreading regional attributes.\n");
}
} else {
printf("Spreading regional area constraints.\n");
}
}
if (b->regionattrib && !b->refine) {
/* Assign every triangle a regional attribute of zero. */
traversalinit(&m->triangles);
triangleloop.orient = 0;
triangleloop.tri = triangletraverse(m);
while (triangleloop.tri != (triangle *) NULL) {
setelemattribute(triangleloop, m->eextras, 0.0);
triangleloop.tri = triangletraverse(m);
}
}
for (i = 0; i < regions; i++) {
if (regiontris[i].tri != m->dummytri) {
/* Make sure the triangle under consideration still exists. */
/* It may have been eaten by the virus. */
if (!deadtri(regiontris[i].tri)) {
/* Put one triangle in the virus pool. */
infect(regiontris[i]);
regiontri = (triangle **) poolalloc(&m->viri);
*regiontri = regiontris[i].tri;
/* Apply one region's attribute and/or area constraint. */
regionplague(m, b, regionlist[4 * i + 2], regionlist[4 * i + 3]);
/* The virus pool should be empty now. */
}
}
}
if (b->regionattrib && !b->refine) {
/* Note the fact that each triangle has an additional attribute. */
m->eextras++;
}
}
/* Free up memory. */
if (((holes > 0) && !b->noholes) || !b->convex || (regions > 0)) {
pooldeinit(&m->viri);
}
if (regions > 0) {
trifree((VOID *) regiontris);
}
}
/** **/
/** **/
/********* Carving out holes and concavities ends here *********/
/********* Mesh quality maintenance begins here *********/
/** **/
/** **/
/*****************************************************************************/
/* */
/* tallyencs() Traverse the entire list of subsegments, and check each */
/* to see if it is encroached. If so, add it to the list. */
/* */
/*****************************************************************************/
#ifndef CDT_ONLY
#ifdef ANSI_DECLARATORS
void tallyencs(struct mesh *m, struct behavior *b)
#else /* not ANSI_DECLARATORS */
void tallyencs(m, b)
struct mesh *m;
struct behavior *b;
#endif /* not ANSI_DECLARATORS */
{
struct osub subsegloop;
int dummy;
traversalinit(&m->subsegs);
subsegloop.ssorient = 0;
subsegloop.ss = subsegtraverse(m);
while (subsegloop.ss != (subseg *) NULL) {
/* If the segment is encroached, add it to the list. */
dummy = checkseg4encroach(m, b, &subsegloop);
subsegloop.ss = subsegtraverse(m);
}
}
#endif /* not CDT_ONLY */
/*****************************************************************************/
/* */
/* precisionerror() Print an error message for precision problems. */
/* */
/*****************************************************************************/
#ifndef CDT_ONLY
void precisionerror(void)
{
printf("Try increasing the area criterion and/or reducing the minimum\n");
printf(" allowable angle so that tiny triangles are not created.\n");
#ifdef SINGLE
printf("Alternatively, try recompiling me with double precision\n");
printf(" arithmetic (by removing \"#define SINGLE\" from the\n");
printf(" source file or \"-DSINGLE\" from the makefile).\n");
#endif /* SINGLE */
}
#endif /* not CDT_ONLY */
/*****************************************************************************/
/* */
/* splitencsegs() Split all the encroached subsegments. */
/* */
/* Each encroached subsegment is repaired by splitting it - inserting a */
/* vertex at or near its midpoint. Newly inserted vertices may encroach */
/* upon other subsegments; these are also repaired. */
/* */
/* `triflaws' is a flag that specifies whether one should take note of new */
/* bad triangles that result from inserting vertices to repair encroached */
/* subsegments. */
/* */
/*****************************************************************************/
#ifndef CDT_ONLY
#ifdef ANSI_DECLARATORS
void splitencsegs(struct mesh *m, struct behavior *b, int triflaws)
#else /* not ANSI_DECLARATORS */
void splitencsegs(m, b, triflaws)
struct mesh *m;
struct behavior *b;
int triflaws;
#endif /* not ANSI_DECLARATORS */
{
struct otri enctri;
struct otri testtri;
struct osub testsh;
struct osub currentenc;
struct badsubseg *encloop;
vertex eorg, edest, eapex;
vertex newvertex;
enum insertvertexresult success;
REAL segmentlength, nearestpoweroftwo;
REAL split;
REAL multiplier, divisor;
int acuteorg, acuteorg2, acutedest, acutedest2;
int dummy;
int i;
triangle ptr; /* Temporary variable used by stpivot(). */
subseg sptr; /* Temporary variable used by snext(). */
/* Note that steinerleft == -1 if an unlimited number */
/* of Steiner points is allowed. */
while ((m->badsubsegs.items > 0) && (m->steinerleft != 0)) {
traversalinit(&m->badsubsegs);
encloop = badsubsegtraverse(m);
while ((encloop != (struct badsubseg *) NULL) && (m->steinerleft != 0)) {
sdecode(encloop->encsubseg, currentenc);
sorg(currentenc, eorg);
sdest(currentenc, edest);
/* Make sure that this segment is still the same segment it was */
/* when it was determined to be encroached. If the segment was */
/* enqueued multiple times (because several newly inserted */
/* vertices encroached it), it may have already been split. */
if (!deadsubseg(currentenc.ss) &&
(eorg == encloop->subsegorg) && (edest == encloop->subsegdest)) {
/* To decide where to split a segment, we need to know if the */
/* segment shares an endpoint with an adjacent segment. */
/* The concern is that, if we simply split every encroached */
/* segment in its center, two adjacent segments with a small */
/* angle between them might lead to an infinite loop; each */
/* vertex added to split one segment will encroach upon the */
/* other segment, which must then be split with a vertex that */
/* will encroach upon the first segment, and so on forever. */
/* To avoid this, imagine a set of concentric circles, whose */
/* radii are powers of two, about each segment endpoint. */
/* These concentric circles determine where the segment is */
/* split. (If both endpoints are shared with adjacent */
/* segments, split the segment in the middle, and apply the */
/* concentric circles for later splittings.) */
/* Is the origin shared with another segment? */
stpivot(currentenc, enctri);
lnext(enctri, testtri);
tspivot(testtri, testsh);
acuteorg = testsh.ss != m->dummysub;
/* Is the destination shared with another segment? */
lnextself(testtri);
tspivot(testtri, testsh);
acutedest = testsh.ss != m->dummysub;
/* If we're using Chew's algorithm (rather than Ruppert's) */
/* to define encroachment, delete free vertices from the */
/* subsegment's diametral circle. */
if (!b->conformdel && !acuteorg && !acutedest) {
apex(enctri, eapex);
while ((vertextype(eapex) == FREEVERTEX) &&
((eorg[0] - eapex[0]) * (edest[0] - eapex[0]) +
(eorg[1] - eapex[1]) * (edest[1] - eapex[1]) < 0.0)) {
deletevertex(m, b, &testtri);
stpivot(currentenc, enctri);
apex(enctri, eapex);
lprev(enctri, testtri);
}
}
/* Now, check the other side of the segment, if there's a triangle */
/* there. */
sym(enctri, testtri);
if (testtri.tri != m->dummytri) {
/* Is the destination shared with another segment? */
lnextself(testtri);
tspivot(testtri, testsh);
acutedest2 = testsh.ss != m->dummysub;
acutedest = acutedest || acutedest2;
/* Is the origin shared with another segment? */
lnextself(testtri);
tspivot(testtri, testsh);
acuteorg2 = testsh.ss != m->dummysub;
acuteorg = acuteorg || acuteorg2;
/* Delete free vertices from the subsegment's diametral circle. */
if (!b->conformdel && !acuteorg2 && !acutedest2) {
org(testtri, eapex);
while ((vertextype(eapex) == FREEVERTEX) &&
((eorg[0] - eapex[0]) * (edest[0] - eapex[0]) +
(eorg[1] - eapex[1]) * (edest[1] - eapex[1]) < 0.0)) {
deletevertex(m, b, &testtri);
sym(enctri, testtri);
apex(testtri, eapex);
lprevself(testtri);
}
}
}
/* Use the concentric circles if exactly one endpoint is shared */
/* with another adjacent segment. */
if (acuteorg || acutedest) {
segmentlength = sqrt((edest[0] - eorg[0]) * (edest[0] - eorg[0]) +
(edest[1] - eorg[1]) * (edest[1] - eorg[1]));
/* Find the power of two that most evenly splits the segment. */
/* The worst case is a 2:1 ratio between subsegment lengths. */
nearestpoweroftwo = 1.0;
while (segmentlength > 3.0 * nearestpoweroftwo) {
nearestpoweroftwo *= 2.0;
}
while (segmentlength < 1.5 * nearestpoweroftwo) {
nearestpoweroftwo *= 0.5;
}
/* Where do we split the segment? */
split = nearestpoweroftwo / segmentlength;
if (acutedest) {
split = 1.0 - split;
}
} else {
/* If we're not worried about adjacent segments, split */
/* this segment in the middle. */
split = 0.5;
}
/* Create the new vertex. */
newvertex = (vertex) poolalloc(&m->vertices);
/* Interpolate its coordinate and attributes. */
for (i = 0; i < 2 + m->nextras; i++) {
newvertex[i] = eorg[i] + split * (edest[i] - eorg[i]);
}
if (!b->noexact) {
/* Roundoff in the above calculation may yield a `newvertex' */
/* that is not precisely collinear with `eorg' and `edest'. */
/* Improve collinearity by one step of iterative refinement. */
multiplier = counterclockwise(m, b, eorg, edest, newvertex);
divisor = ((eorg[0] - edest[0]) * (eorg[0] - edest[0]) +
(eorg[1] - edest[1]) * (eorg[1] - edest[1]));
if ((multiplier != 0.0) && (divisor != 0.0)) {
multiplier = multiplier / divisor;
/* Watch out for NANs. */
if (multiplier == multiplier) {
newvertex[0] += multiplier * (edest[1] - eorg[1]);
newvertex[1] += multiplier * (eorg[0] - edest[0]);
}
}
}
setvertexmark(newvertex, mark(currentenc));
setvertextype(newvertex, SEGMENTVERTEX);
if (b->verbose > 1) {
printf(
" Splitting subsegment (%.12g, %.12g) (%.12g, %.12g) at (%.12g, %.12g).\n",
eorg[0], eorg[1], edest[0], edest[1],
newvertex[0], newvertex[1]);
}
/* Check whether the new vertex lies on an endpoint. */
if (((newvertex[0] == eorg[0]) && (newvertex[1] == eorg[1])) ||
((newvertex[0] == edest[0]) && (newvertex[1] == edest[1]))) {
printf("Error: Ran out of precision at (%.12g, %.12g).\n",
newvertex[0], newvertex[1]);
printf("I attempted to split a segment to a smaller size than\n");
printf(" can be accommodated by the finite precision of\n");
printf(" floating point arithmetic.\n");
precisionerror();
triexit(1);
}
/* Insert the splitting vertex. This should always succeed. */
success = insertvertex(m, b, newvertex, &enctri, ¤tenc,
1, triflaws);
if ((success != SUCCESSFULVERTEX) && (success != ENCROACHINGVERTEX)) {
printf("Internal error in splitencsegs():\n");
printf(" Failure to split a segment.\n");
internalerror();
}
if (m->steinerleft > 0) {
m->steinerleft--;
}
/* Check the two new subsegments to see if they're encroached. */
dummy = checkseg4encroach(m, b, ¤tenc);
snextself(currentenc);
dummy = checkseg4encroach(m, b, ¤tenc);
}
badsubsegdealloc(m, encloop);
encloop = badsubsegtraverse(m);
}
}
}
#endif /* not CDT_ONLY */
/*****************************************************************************/
/* */
/* tallyfaces() Test every triangle in the mesh for quality measures. */
/* */
/*****************************************************************************/
#ifndef CDT_ONLY
#ifdef ANSI_DECLARATORS
void tallyfaces(struct mesh *m, struct behavior *b)
#else /* not ANSI_DECLARATORS */
void tallyfaces(m, b)
struct mesh *m;
struct behavior *b;
#endif /* not ANSI_DECLARATORS */
{
struct otri triangleloop;
if (b->verbose) {
printf(" Making a list of bad triangles.\n");
}
traversalinit(&m->triangles);
triangleloop.orient = 0;
triangleloop.tri = triangletraverse(m);
while (triangleloop.tri != (triangle *) NULL) {
/* If the triangle is bad, enqueue it. */
testtriangle(m, b, &triangleloop);
triangleloop.tri = triangletraverse(m);
}
}
#endif /* not CDT_ONLY */
/*****************************************************************************/
/* */
/* splittriangle() Inserts a vertex at the circumcenter of a triangle. */
/* Deletes the newly inserted vertex if it encroaches */
/* upon a segment. */
/* */
/*****************************************************************************/
#ifndef CDT_ONLY
#ifdef ANSI_DECLARATORS
void splittriangle(struct mesh *m, struct behavior *b,
struct badtriang *badtri)
#else /* not ANSI_DECLARATORS */
void splittriangle(m, b, badtri)
struct mesh *m;
struct behavior *b;
struct badtriang *badtri;
#endif /* not ANSI_DECLARATORS */
{
struct otri badotri;
vertex borg, bdest, bapex;
vertex newvertex;
REAL xi, eta;
enum insertvertexresult success;
int errorflag;
int i;
decode(badtri->poortri, badotri);
org(badotri, borg);
dest(badotri, bdest);
apex(badotri, bapex);
/* Make sure that this triangle is still the same triangle it was */
/* when it was tested and determined to be of bad quality. */
/* Subsequent transformations may have made it a different triangle. */
if (!deadtri(badotri.tri) && (borg == badtri->triangorg) &&
(bdest == badtri->triangdest) && (bapex == badtri->triangapex)) {
if (b->verbose > 1) {
printf(" Splitting this triangle at its circumcenter:\n");
printf(" (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n", borg[0],
borg[1], bdest[0], bdest[1], bapex[0], bapex[1]);
}
errorflag = 0;
/* Create a new vertex at the triangle's circumcenter. */
newvertex = (vertex) poolalloc(&m->vertices);
findcircumcenter(m, b, borg, bdest, bapex, newvertex, &xi, &eta, 1);
/* Check whether the new vertex lies on a triangle vertex. */
if (((newvertex[0] == borg[0]) && (newvertex[1] == borg[1])) ||
((newvertex[0] == bdest[0]) && (newvertex[1] == bdest[1])) ||
((newvertex[0] == bapex[0]) && (newvertex[1] == bapex[1]))) {
if (!b->quiet) {
printf(
"Warning: New vertex (%.12g, %.12g) falls on existing vertex.\n",
newvertex[0], newvertex[1]);
errorflag = 1;
}
vertexdealloc(m, newvertex);
} else {
for (i = 2; i < 2 + m->nextras; i++) {
/* Interpolate the vertex attributes at the circumcenter. */
newvertex[i] = borg[i] + xi * (bdest[i] - borg[i])
+ eta * (bapex[i] - borg[i]);
}
/* The new vertex must be in the interior, and therefore is a */
/* free vertex with a marker of zero. */
setvertexmark(newvertex, 0);
setvertextype(newvertex, FREEVERTEX);
/* Ensure that the handle `badotri' does not represent the longest */
/* edge of the triangle. This ensures that the circumcenter must */
/* fall to the left of this edge, so point location will work. */
/* (If the angle org-apex-dest exceeds 90 degrees, then the */
/* circumcenter lies outside the org-dest edge, and eta is */
/* negative. Roundoff error might prevent eta from being */
/* negative when it should be, so I test eta against xi.) */
if (eta < xi) {
lprevself(badotri);
}
/* Insert the circumcenter, searching from the edge of the triangle, */
/* and maintain the Delaunay property of the triangulation. */
success = insertvertex(m, b, newvertex, &badotri, (struct osub *) NULL,
1, 1);
if (success == SUCCESSFULVERTEX) {
if (m->steinerleft > 0) {
m->steinerleft--;
}
} else if (success == ENCROACHINGVERTEX) {
/* If the newly inserted vertex encroaches upon a subsegment, */
/* delete the new vertex. */
undovertex(m, b);
if (b->verbose > 1) {
printf(" Rejecting (%.12g, %.12g).\n", newvertex[0], newvertex[1]);
}
vertexdealloc(m, newvertex);
} else if (success == VIOLATINGVERTEX) {
/* Failed to insert the new vertex, but some subsegment was */
/* marked as being encroached. */
vertexdealloc(m, newvertex);
} else { /* success == DUPLICATEVERTEX */
/* Couldn't insert the new vertex because a vertex is already there. */
if (!b->quiet) {
printf(
"Warning: New vertex (%.12g, %.12g) falls on existing vertex.\n",
newvertex[0], newvertex[1]);
errorflag = 1;
}
vertexdealloc(m, newvertex);
}
}
if (errorflag) {
if (b->verbose) {
printf(" The new vertex is at the circumcenter of triangle\n");
printf(" (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n",
borg[0], borg[1], bdest[0], bdest[1], bapex[0], bapex[1]);
}
printf("This probably means that I am trying to refine triangles\n");
printf(" to a smaller size than can be accommodated by the finite\n");
printf(" precision of floating point arithmetic. (You can be\n");
printf(" sure of this if I fail to terminate.)\n");
precisionerror();
}
}
}
#endif /* not CDT_ONLY */
/*****************************************************************************/
/* */
/* enforcequality() Remove all the encroached subsegments and bad */
/* triangles from the triangulation. */
/* */
/*****************************************************************************/
#ifndef CDT_ONLY
#ifdef ANSI_DECLARATORS
void enforcequality(struct mesh *m, struct behavior *b)
#else /* not ANSI_DECLARATORS */
void enforcequality(m, b)
struct mesh *m;
struct behavior *b;
#endif /* not ANSI_DECLARATORS */
{
struct badtriang *badtri;
int i;
if (!b->quiet) {
printf("Adding Steiner points to enforce quality.\n");
}
/* Initialize the pool of encroached subsegments. */
poolinit(&m->badsubsegs, sizeof(struct badsubseg), BADSUBSEGPERBLOCK,
BADSUBSEGPERBLOCK, 0);
if (b->verbose) {
printf(" Looking for encroached subsegments.\n");
}
/* Test all segments to see if they're encroached. */
tallyencs(m, b);
if (b->verbose && (m->badsubsegs.items > 0)) {
printf(" Splitting encroached subsegments.\n");
}
/* Fix encroached subsegments without noting bad triangles. */
splitencsegs(m, b, 0);
/* At this point, if we haven't run out of Steiner points, the */
/* triangulation should be (conforming) Delaunay. */
/* Next, we worry about enforcing triangle quality. */
if ((b->minangle > 0.0) || b->vararea || b->fixedarea || b->usertest) {
/* Initialize the pool of bad triangles. */
poolinit(&m->badtriangles, sizeof(struct badtriang), BADTRIPERBLOCK,
BADTRIPERBLOCK, 0);
/* Initialize the queues of bad triangles. */
for (i = 0; i < 4096; i++) {
m->queuefront[i] = (struct badtriang *) NULL;
}
m->firstnonemptyq = -1;
/* Test all triangles to see if they're bad. */
tallyfaces(m, b);
/* Initialize the pool of recently flipped triangles. */
poolinit(&m->flipstackers, sizeof(struct flipstacker), FLIPSTACKERPERBLOCK,
FLIPSTACKERPERBLOCK, 0);
m->checkquality = 1;
if (b->verbose) {
printf(" Splitting bad triangles.\n");
}
while ((m->badtriangles.items > 0) && (m->steinerleft != 0)) {
/* Fix one bad triangle by inserting a vertex at its circumcenter. */
badtri = dequeuebadtriang(m);
splittriangle(m, b, badtri);
if (m->badsubsegs.items > 0) {
/* Put bad triangle back in queue for another try later. */
enqueuebadtriang(m, b, badtri);
/* Fix any encroached subsegments that resulted. */
/* Record any new bad triangles that result. */
splitencsegs(m, b, 1);
} else {
/* Return the bad triangle to the pool. */
pooldealloc(&m->badtriangles, (VOID *) badtri);
}
}
}
/* At this point, if the "-D" switch was selected and we haven't run out */
/* of Steiner points, the triangulation should be (conforming) Delaunay */
/* and have no low-quality triangles. */
/* Might we have run out of Steiner points too soon? */
if (!b->quiet && b->conformdel && (m->badsubsegs.items > 0) &&
(m->steinerleft == 0)) {
printf("\nWarning: I ran out of Steiner points, but the mesh has\n");
if (m->badsubsegs.items == 1) {
printf(" one encroached subsegment, and therefore might not be truly\n"
);
} else {
printf(" %ld encroached subsegments, and therefore might not be truly\n"
, m->badsubsegs.items);
}
printf(" Delaunay. If the Delaunay property is important to you,\n");
printf(" try increasing the number of Steiner points (controlled by\n");
printf(" the -S switch) slightly and try again.\n\n");
}
}
#endif /* not CDT_ONLY */
/** **/
/** **/
/********* Mesh quality maintenance ends here *********/
/*****************************************************************************/
/* */
/* highorder() Create extra nodes for quadratic subparametric elements. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
void highorder(struct mesh *m, struct behavior *b)
#else /* not ANSI_DECLARATORS */
void highorder(m, b)
struct mesh *m;
struct behavior *b;
#endif /* not ANSI_DECLARATORS */
{
struct otri triangleloop, trisym;
struct osub checkmark;
vertex newvertex;
vertex torg, tdest;
int i;
triangle ptr; /* Temporary variable used by sym(). */
subseg sptr; /* Temporary variable used by tspivot(). */
if (!b->quiet) {
printf("Adding vertices for second-order triangles.\n");
}
/* The following line ensures that dead items in the pool of nodes */
/* cannot be allocated for the extra nodes associated with high */
/* order elements. This ensures that the primary nodes (at the */
/* corners of elements) will occur earlier in the output files, and */
/* have lower indices, than the extra nodes. */
m->vertices.deaditemstack = (VOID *) NULL;
traversalinit(&m->triangles);
triangleloop.tri = triangletraverse(m);
/* To loop over the set of edges, loop over all triangles, and look at */
/* the three edges of each triangle. If there isn't another triangle */
/* adjacent to the edge, operate on the edge. If there is another */
/* adjacent triangle, operate on the edge only if the current triangle */
/* has a smaller pointer than its neighbor. This way, each edge is */
/* considered only once. */
while (triangleloop.tri != (triangle *) NULL) {
for (triangleloop.orient = 0; triangleloop.orient < 3;
triangleloop.orient++) {
sym(triangleloop, trisym);
if ((triangleloop.tri < trisym.tri) || (trisym.tri == m->dummytri)) {
org(triangleloop, torg);
dest(triangleloop, tdest);
/* Create a new node in the middle of the edge. Interpolate */
/* its attributes. */
newvertex = (vertex) poolalloc(&m->vertices);
for (i = 0; i < 2 + m->nextras; i++) {
newvertex[i] = 0.5 * (torg[i] + tdest[i]);
}
/* Set the new node's marker to zero or one, depending on */
/* whether it lies on a boundary. */
setvertexmark(newvertex, trisym.tri == m->dummytri);
setvertextype(newvertex,
trisym.tri == m->dummytri ? FREEVERTEX : SEGMENTVERTEX);
if (b->usesegments) {
tspivot(triangleloop, checkmark);
/* If this edge is a segment, transfer the marker to the new node. */
if (checkmark.ss != m->dummysub) {
setvertexmark(newvertex, mark(checkmark));
setvertextype(newvertex, SEGMENTVERTEX);
}
}
if (b->verbose > 1) {
printf(" Creating (%.12g, %.12g).\n", newvertex[0], newvertex[1]);
}
/* Record the new node in the (one or two) adjacent elements. */
triangleloop.tri[m->highorderindex + triangleloop.orient] =
(triangle) newvertex;
if (trisym.tri != m->dummytri) {
trisym.tri[m->highorderindex + trisym.orient] = (triangle) newvertex;
}
}
}
triangleloop.tri = triangletraverse(m);
}
}
/********* File I/O routines begin here *********/
/** **/
/** **/
/*****************************************************************************/
/* */
/* readline() Read a nonempty line from a file. */
/* */
/* A line is considered "nonempty" if it contains something that looks like */
/* a number. Comments (prefaced by `#') are ignored. */
/* */
/*****************************************************************************/
#ifndef TRILIBRARY
#ifdef ANSI_DECLARATORS
char *readline(char *string, FILE *infile, char *infilename)
#else /* not ANSI_DECLARATORS */
char *readline(string, infile, infilename)
char *string;
FILE *infile;
char *infilename;
#endif /* not ANSI_DECLARATORS */
{
char *result;
/* Search for something that looks like a number. */
do {
result = fgets(string, INPUTLINESIZE, infile);
if (result == (char *) NULL) {
printf(" Error: Unexpected end of file in %s.\n", infilename);
triexit(1);
}
/* Skip anything that doesn't look like a number, a comment, */
/* or the end of a line. */
while ((*result != '\0') && (*result != '#')
&& (*result != '.') && (*result != '+') && (*result != '-')
&& ((*result < '0') || (*result > '9'))) {
result++;
}
/* If it's a comment or end of line, read another line and try again. */
} while ((*result == '#') || (*result == '\0'));
return result;
}
#endif /* not TRILIBRARY */
/*****************************************************************************/
/* */
/* findfield() Find the next field of a string. */
/* */
/* Jumps past the current field by searching for whitespace, then jumps */
/* past the whitespace to find the next field. */
/* */
/*****************************************************************************/
#ifndef TRILIBRARY
#ifdef ANSI_DECLARATORS
char *findfield(char *string)
#else /* not ANSI_DECLARATORS */
char *findfield(string)
char *string;
#endif /* not ANSI_DECLARATORS */
{
char *result;
result = string;
/* Skip the current field. Stop upon reaching whitespace. */
while ((*result != '\0') && (*result != '#')
&& (*result != ' ') && (*result != '\t')) {
result++;
}
/* Now skip the whitespace and anything else that doesn't look like a */
/* number, a comment, or the end of a line. */
while ((*result != '\0') && (*result != '#')
&& (*result != '.') && (*result != '+') && (*result != '-')
&& ((*result < '0') || (*result > '9'))) {
result++;
}
/* Check for a comment (prefixed with `#'). */
if (*result == '#') {
*result = '\0';
}
return result;
}
#endif /* not TRILIBRARY */
/*****************************************************************************/
/* */
/* readnodes() Read the vertices from a file, which may be a .node or */
/* .poly file. */
/* */
/*****************************************************************************/
#ifndef TRILIBRARY
#ifdef ANSI_DECLARATORS
void readnodes(struct mesh *m, struct behavior *b, char *nodefilename,
char *polyfilename, FILE **polyfile)
#else /* not ANSI_DECLARATORS */
void readnodes(m, b, nodefilename, polyfilename, polyfile)
struct mesh *m;
struct behavior *b;
char *nodefilename;
char *polyfilename;
FILE **polyfile;
#endif /* not ANSI_DECLARATORS */
{
FILE *infile;
vertex vertexloop;
char inputline[INPUTLINESIZE];
char *stringptr;
char *infilename;
REAL x, y;
int firstnode;
int nodemarkers;
int currentmarker;
int i, j;
if (b->poly) {
/* Read the vertices from a .poly file. */
if (!b->quiet) {
printf("Opening %s.\n", polyfilename);
}
*polyfile = fopen(polyfilename, "r");
if (*polyfile == (FILE *) NULL) {
printf(" Error: Cannot access file %s.\n", polyfilename);
triexit(1);
}
/* Read number of vertices, number of dimensions, number of vertex */
/* attributes, and number of boundary markers. */
stringptr = readline(inputline, *polyfile, polyfilename);
m->invertices = (int) strtol(stringptr, &stringptr, 0);
stringptr = findfield(stringptr);
if (*stringptr == '\0') {
m->mesh_dim = 2;
} else {
m->mesh_dim = (int) strtol(stringptr, &stringptr, 0);
}
stringptr = findfield(stringptr);
if (*stringptr == '\0') {
m->nextras = 0;
} else {
m->nextras = (int) strtol(stringptr, &stringptr, 0);
}
stringptr = findfield(stringptr);
if (*stringptr == '\0') {
nodemarkers = 0;
} else {
nodemarkers = (int) strtol(stringptr, &stringptr, 0);
}
if (m->invertices > 0) {
infile = *polyfile;
infilename = polyfilename;
m->readnodefile = 0;
} else {
/* If the .poly file claims there are zero vertices, that means that */
/* the vertices should be read from a separate .node file. */
m->readnodefile = 1;
infilename = nodefilename;
}
} else {
m->readnodefile = 1;
infilename = nodefilename;
*polyfile = (FILE *) NULL;
}
if (m->readnodefile) {
/* Read the vertices from a .node file. */
if (!b->quiet) {
printf("Opening %s.\n", nodefilename);
}
infile = fopen(nodefilename, "r");
if (infile == (FILE *) NULL) {
printf(" Error: Cannot access file %s.\n", nodefilename);
triexit(1);
}
/* Read number of vertices, number of dimensions, number of vertex */
/* attributes, and number of boundary markers. */
stringptr = readline(inputline, infile, nodefilename);
m->invertices = (int) strtol(stringptr, &stringptr, 0);
stringptr = findfield(stringptr);
if (*stringptr == '\0') {
m->mesh_dim = 2;
} else {
m->mesh_dim = (int) strtol(stringptr, &stringptr, 0);
}
stringptr = findfield(stringptr);
if (*stringptr == '\0') {
m->nextras = 0;
} else {
m->nextras = (int) strtol(stringptr, &stringptr, 0);
}
stringptr = findfield(stringptr);
if (*stringptr == '\0') {
nodemarkers = 0;
} else {
nodemarkers = (int) strtol(stringptr, &stringptr, 0);
}
}
if (m->invertices < 3) {
printf("Error: Input must have at least three input vertices.\n");
triexit(1);
}
if (m->mesh_dim != 2) {
printf("Error: Triangle only works with two-dimensional meshes.\n");
triexit(1);
}
if (m->nextras == 0) {
b->weighted = 0;
}
initializevertexpool(m, b);
/* Read the vertices. */
for (i = 0; i < m->invertices; i++) {
vertexloop = (vertex) poolalloc(&m->vertices);
stringptr = readline(inputline, infile, infilename);
if (i == 0) {
firstnode = (int) strtol(stringptr, &stringptr, 0);
if ((firstnode == 0) || (firstnode == 1)) {
b->firstnumber = firstnode;
}
}
stringptr = findfield(stringptr);
if (*stringptr == '\0') {
printf("Error: Vertex %d has no x coordinate.\n", b->firstnumber + i);
triexit(1);
}
x = (REAL) strtod(stringptr, &stringptr);
stringptr = findfield(stringptr);
if (*stringptr == '\0') {
printf("Error: Vertex %d has no y coordinate.\n", b->firstnumber + i);
triexit(1);
}
y = (REAL) strtod(stringptr, &stringptr);
vertexloop[0] = x;
vertexloop[1] = y;
/* Read the vertex attributes. */
for (j = 2; j < 2 + m->nextras; j++) {
stringptr = findfield(stringptr);
if (*stringptr == '\0') {
vertexloop[j] = 0.0;
} else {
vertexloop[j] = (REAL) strtod(stringptr, &stringptr);
}
}
if (nodemarkers) {
/* Read a vertex marker. */
stringptr = findfield(stringptr);
if (*stringptr == '\0') {
setvertexmark(vertexloop, 0);
} else {
currentmarker = (int) strtol(stringptr, &stringptr, 0);
setvertexmark(vertexloop, currentmarker);
}
} else {
/* If no markers are specified in the file, they default to zero. */
setvertexmark(vertexloop, 0);
}
setvertextype(vertexloop, INPUTVERTEX);
/* Determine the smallest and largest x and y coordinates. */
if (i == 0) {
m->xmin = m->xmax = x;
m->ymin = m->ymax = y;
} else {
m->xmin = (x < m->xmin) ? x : m->xmin;
m->xmax = (x > m->xmax) ? x : m->xmax;
m->ymin = (y < m->ymin) ? y : m->ymin;
m->ymax = (y > m->ymax) ? y : m->ymax;
}
}
if (m->readnodefile) {
fclose(infile);
}
/* Nonexistent x value used as a flag to mark circle events in sweepline */
/* Delaunay algorithm. */
m->xminextreme = 10 * m->xmin - 9 * m->xmax;
}
#endif /* not TRILIBRARY */
/*****************************************************************************/
/* */
/* transfernodes() Read the vertices from memory. */
/* */
/*****************************************************************************/
#ifdef TRILIBRARY
#ifdef ANSI_DECLARATORS
void transfernodes(struct mesh *m, struct behavior *b, REAL *pointlist,
REAL *pointattriblist, int *pointmarkerlist,
int numberofpoints, int numberofpointattribs)
#else /* not ANSI_DECLARATORS */
void transfernodes(m, b, pointlist, pointattriblist, pointmarkerlist,
numberofpoints, numberofpointattribs)
struct mesh *m;
struct behavior *b;
REAL *pointlist;
REAL *pointattriblist;
int *pointmarkerlist;
int numberofpoints;
int numberofpointattribs;
#endif /* not ANSI_DECLARATORS */
{
vertex vertexloop;
REAL x, y;
int i, j;
int coordindex;
int attribindex;
m->invertices = numberofpoints;
m->mesh_dim = 2;
m->nextras = numberofpointattribs;
m->readnodefile = 0;
if (m->invertices < 3) {
printf("Error: Input must have at least three input vertices.\n");
triexit(1);
}
if (m->nextras == 0) {
b->weighted = 0;
}
initializevertexpool(m, b);
/* Read the vertices. */
coordindex = 0;
attribindex = 0;
for (i = 0; i < m->invertices; i++) {
vertexloop = (vertex) poolalloc(&m->vertices);
/* Read the vertex coordinates. */
x = vertexloop[0] = pointlist[coordindex++];
y = vertexloop[1] = pointlist[coordindex++];
/* Read the vertex attributes. */
for (j = 0; j < numberofpointattribs; j++) {
vertexloop[2 + j] = pointattriblist[attribindex++];
}
if (pointmarkerlist != (int *) NULL) {
/* Read a vertex marker. */
setvertexmark(vertexloop, pointmarkerlist[i]);
} else {
/* If no markers are specified, they default to zero. */
setvertexmark(vertexloop, 0);
}
setvertextype(vertexloop, INPUTVERTEX);
/* Determine the smallest and largest x and y coordinates. */
if (i == 0) {
m->xmin = m->xmax = x;
m->ymin = m->ymax = y;
} else {
m->xmin = (x < m->xmin) ? x : m->xmin;
m->xmax = (x > m->xmax) ? x : m->xmax;
m->ymin = (y < m->ymin) ? y : m->ymin;
m->ymax = (y > m->ymax) ? y : m->ymax;
}
}
/* Nonexistent x value used as a flag to mark circle events in sweepline */
/* Delaunay algorithm. */
m->xminextreme = 10 * m->xmin - 9 * m->xmax;
}
#endif /* TRILIBRARY */
/*****************************************************************************/
/* */
/* readholes() Read the holes, and possibly regional attributes and area */
/* constraints, from a .poly file. */
/* */
/*****************************************************************************/
#ifndef TRILIBRARY
#ifdef ANSI_DECLARATORS
void readholes(struct mesh *m, struct behavior *b,
FILE *polyfile, char *polyfilename, REAL **hlist, int *holes,
REAL **rlist, int *regions)
#else /* not ANSI_DECLARATORS */
void readholes(m, b, polyfile, polyfilename, hlist, holes, rlist, regions)
struct mesh *m;
struct behavior *b;
FILE *polyfile;
char *polyfilename;
REAL **hlist;
int *holes;
REAL **rlist;
int *regions;
#endif /* not ANSI_DECLARATORS */
{
REAL *holelist;
REAL *regionlist;
char inputline[INPUTLINESIZE];
char *stringptr;
int index;
int i;
/* Read the holes. */
stringptr = readline(inputline, polyfile, polyfilename);
*holes = (int) strtol(stringptr, &stringptr, 0);
if (*holes > 0) {
holelist = (REAL *) trimalloc(2 * *holes * (int) sizeof(REAL));
*hlist = holelist;
for (i = 0; i < 2 * *holes; i += 2) {
stringptr = readline(inputline, polyfile, polyfilename);
stringptr = findfield(stringptr);
if (*stringptr == '\0') {
printf("Error: Hole %d has no x coordinate.\n",
b->firstnumber + (i >> 1));
triexit(1);
} else {
holelist[i] = (REAL) strtod(stringptr, &stringptr);
}
stringptr = findfield(stringptr);
if (*stringptr == '\0') {
printf("Error: Hole %d has no y coordinate.\n",
b->firstnumber + (i >> 1));
triexit(1);
} else {
holelist[i + 1] = (REAL) strtod(stringptr, &stringptr);
}
}
} else {
*hlist = (REAL *) NULL;
}
#ifndef CDT_ONLY
if ((b->regionattrib || b->vararea) && !b->refine) {
/* Read the area constraints. */
stringptr = readline(inputline, polyfile, polyfilename);
*regions = (int) strtol(stringptr, &stringptr, 0);
if (*regions > 0) {
regionlist = (REAL *) trimalloc(4 * *regions * (int) sizeof(REAL));
*rlist = regionlist;
index = 0;
for (i = 0; i < *regions; i++) {
stringptr = readline(inputline, polyfile, polyfilename);
stringptr = findfield(stringptr);
if (*stringptr == '\0') {
printf("Error: Region %d has no x coordinate.\n",
b->firstnumber + i);
triexit(1);
} else {
regionlist[index++] = (REAL) strtod(stringptr, &stringptr);
}
stringptr = findfield(stringptr);
if (*stringptr == '\0') {
printf("Error: Region %d has no y coordinate.\n",
b->firstnumber + i);
triexit(1);
} else {
regionlist[index++] = (REAL) strtod(stringptr, &stringptr);
}
stringptr = findfield(stringptr);
if (*stringptr == '\0') {
printf(
"Error: Region %d has no region attribute or area constraint.\n",
b->firstnumber + i);
triexit(1);
} else {
regionlist[index++] = (REAL) strtod(stringptr, &stringptr);
}
stringptr = findfield(stringptr);
if (*stringptr == '\0') {
regionlist[index] = regionlist[index - 1];
} else {
regionlist[index] = (REAL) strtod(stringptr, &stringptr);
}
index++;
}
}
} else {
/* Set `*regions' to zero to avoid an accidental free() later. */
*regions = 0;
*rlist = (REAL *) NULL;
}
#endif /* not CDT_ONLY */
fclose(polyfile);
}
#endif /* not TRILIBRARY */
/*****************************************************************************/
/* */
/* finishfile() Write the command line to the output file so the user */
/* can remember how the file was generated. Close the file. */
/* */
/*****************************************************************************/
#ifndef TRILIBRARY
#ifdef ANSI_DECLARATORS
void finishfile(FILE *outfile, int argc, char **argv)
#else /* not ANSI_DECLARATORS */
void finishfile(outfile, argc, argv)
FILE *outfile;
int argc;
char **argv;
#endif /* not ANSI_DECLARATORS */
{
int i;
fprintf(outfile, "# Generated by");
for (i = 0; i < argc; i++) {
fprintf(outfile, " ");
fputs(argv[i], outfile);
}
fprintf(outfile, "\n");
fclose(outfile);
}
#endif /* not TRILIBRARY */
/*****************************************************************************/
/* */
/* writenodes() Number the vertices and write them to a .node file. */
/* */
/* To save memory, the vertex numbers are written over the boundary markers */
/* after the vertices are written to a file. */
/* */
/*****************************************************************************/
#ifdef TRILIBRARY
#ifdef ANSI_DECLARATORS
void writenodes(struct mesh *m, struct behavior *b, REAL **pointlist,
REAL **pointattriblist, int **pointmarkerlist)
#else /* not ANSI_DECLARATORS */
void writenodes(m, b, pointlist, pointattriblist, pointmarkerlist)
struct mesh *m;
struct behavior *b;
REAL **pointlist;
REAL **pointattriblist;
int **pointmarkerlist;
#endif /* not ANSI_DECLARATORS */
#else /* not TRILIBRARY */
#ifdef ANSI_DECLARATORS
void writenodes(struct mesh *m, struct behavior *b, char *nodefilename,
int argc, char **argv)
#else /* not ANSI_DECLARATORS */
void writenodes(m, b, nodefilename, argc, argv)
struct mesh *m;
struct behavior *b;
char *nodefilename;
int argc;
char **argv;
#endif /* not ANSI_DECLARATORS */
#endif /* not TRILIBRARY */
{
#ifdef TRILIBRARY
REAL *plist;
REAL *palist;
int *pmlist;
int coordindex;
int attribindex;
#else /* not TRILIBRARY */
FILE *outfile;
#endif /* not TRILIBRARY */
vertex vertexloop;
long outvertices;
int vertexnumber;
int i;
if (b->jettison) {
outvertices = m->vertices.items - m->undeads;
} else {
outvertices = m->vertices.items;
}
#ifdef TRILIBRARY
if (!b->quiet) {
printf("Writing vertices.\n");
}
/* Allocate memory for output vertices if necessary. */
if (*pointlist == (REAL *) NULL) {
*pointlist = (REAL *) trimalloc((int) (outvertices * 2 * sizeof(REAL)));
}
/* Allocate memory for output vertex attributes if necessary. */
if ((m->nextras > 0) && (*pointattriblist == (REAL *) NULL)) {
*pointattriblist = (REAL *) trimalloc((int) (outvertices * m->nextras *
sizeof(REAL)));
}
/* Allocate memory for output vertex markers if necessary. */
if (!b->nobound && (*pointmarkerlist == (int *) NULL)) {
*pointmarkerlist = (int *) trimalloc((int) (outvertices * sizeof(int)));
}
plist = *pointlist;
palist = *pointattriblist;
pmlist = *pointmarkerlist;
coordindex = 0;
attribindex = 0;
#else /* not TRILIBRARY */
if (!b->quiet) {
printf("Writing %s.\n", nodefilename);
}
outfile = fopen(nodefilename, "w");
if (outfile == (FILE *) NULL) {
printf(" Error: Cannot create file %s.\n", nodefilename);
triexit(1);
}
/* Number of vertices, number of dimensions, number of vertex attributes, */
/* and number of boundary markers (zero or one). */
fprintf(outfile, "%ld %d %d %d\n", outvertices, m->mesh_dim,
m->nextras, 1 - b->nobound);
#endif /* not TRILIBRARY */
traversalinit(&m->vertices);
vertexnumber = b->firstnumber;
vertexloop = vertextraverse(m);
while (vertexloop != (vertex) NULL) {
if (!b->jettison || (vertextype(vertexloop) != UNDEADVERTEX)) {
#ifdef TRILIBRARY
/* X and y coordinates. */
plist[coordindex++] = vertexloop[0];
plist[coordindex++] = vertexloop[1];
/* Vertex attributes. */
for (i = 0; i < m->nextras; i++) {
palist[attribindex++] = vertexloop[2 + i];
}
if (!b->nobound) {
/* Copy the boundary marker. */
pmlist[vertexnumber - b->firstnumber] = vertexmark(vertexloop);
}
#else /* not TRILIBRARY */
/* Vertex number, x and y coordinates. */
fprintf(outfile, "%4d %.17g %.17g", vertexnumber, vertexloop[0],
vertexloop[1]);
for (i = 0; i < m->nextras; i++) {
/* Write an attribute. */
fprintf(outfile, " %.17g", vertexloop[i + 2]);
}
if (b->nobound) {
fprintf(outfile, "\n");
} else {
/* Write the boundary marker. */
fprintf(outfile, " %d\n", vertexmark(vertexloop));
}
#endif /* not TRILIBRARY */
setvertexmark(vertexloop, vertexnumber);
vertexnumber++;
}
vertexloop = vertextraverse(m);
}
#ifndef TRILIBRARY
finishfile(outfile, argc, argv);
#endif /* not TRILIBRARY */
}
/*****************************************************************************/
/* */
/* numbernodes() Number the vertices. */
/* */
/* Each vertex is assigned a marker equal to its number. */
/* */
/* Used when writenodes() is not called because no .node file is written. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
void numbernodes(struct mesh *m, struct behavior *b)
#else /* not ANSI_DECLARATORS */
void numbernodes(m, b)
struct mesh *m;
struct behavior *b;
#endif /* not ANSI_DECLARATORS */
{
vertex vertexloop;
int vertexnumber;
traversalinit(&m->vertices);
vertexnumber = b->firstnumber;
vertexloop = vertextraverse(m);
while (vertexloop != (vertex) NULL) {
setvertexmark(vertexloop, vertexnumber);
if (!b->jettison || (vertextype(vertexloop) != UNDEADVERTEX)) {
vertexnumber++;
}
vertexloop = vertextraverse(m);
}
}
/*****************************************************************************/
/* */
/* writeelements() Write the triangles to an .ele file. */
/* */
/*****************************************************************************/
#ifdef TRILIBRARY
#ifdef ANSI_DECLARATORS
void writeelements(struct mesh *m, struct behavior *b,
int **trianglelist, REAL **triangleattriblist)
#else /* not ANSI_DECLARATORS */
void writeelements(m, b, trianglelist, triangleattriblist)
struct mesh *m;
struct behavior *b;
int **trianglelist;
REAL **triangleattriblist;
#endif /* not ANSI_DECLARATORS */
#else /* not TRILIBRARY */
#ifdef ANSI_DECLARATORS
void writeelements(struct mesh *m, struct behavior *b, char *elefilename,
int argc, char **argv)
#else /* not ANSI_DECLARATORS */
void writeelements(m, b, elefilename, argc, argv)
struct mesh *m;
struct behavior *b;
char *elefilename;
int argc;
char **argv;
#endif /* not ANSI_DECLARATORS */
#endif /* not TRILIBRARY */
{
#ifdef TRILIBRARY
int *tlist;
REAL *talist;
int vertexindex;
int attribindex;
#else /* not TRILIBRARY */
FILE *outfile;
#endif /* not TRILIBRARY */
struct otri triangleloop;
vertex p1, p2, p3;
vertex mid1, mid2, mid3;
long elementnumber;
int i;
#ifdef TRILIBRARY
if (!b->quiet) {
printf("Writing triangles.\n");
}
/* Allocate memory for output triangles if necessary. */
if (*trianglelist == (int *) NULL) {
*trianglelist = (int *) trimalloc((int) (m->triangles.items *
((b->order + 1) * (b->order + 2) /
2) * sizeof(int)));
}
/* Allocate memory for output triangle attributes if necessary. */
if ((m->eextras > 0) && (*triangleattriblist == (REAL *) NULL)) {
*triangleattriblist = (REAL *) trimalloc((int) (m->triangles.items *
m->eextras *
sizeof(REAL)));
}
tlist = *trianglelist;
talist = *triangleattriblist;
vertexindex = 0;
attribindex = 0;
#else /* not TRILIBRARY */
if (!b->quiet) {
printf("Writing %s.\n", elefilename);
}
outfile = fopen(elefilename, "w");
if (outfile == (FILE *) NULL) {
printf(" Error: Cannot create file %s.\n", elefilename);
triexit(1);
}
/* Number of triangles, vertices per triangle, attributes per triangle. */
fprintf(outfile, "%ld %d %d\n", m->triangles.items,
(b->order + 1) * (b->order + 2) / 2, m->eextras);
#endif /* not TRILIBRARY */
traversalinit(&m->triangles);
triangleloop.tri = triangletraverse(m);
triangleloop.orient = 0;
elementnumber = b->firstnumber;
while (triangleloop.tri != (triangle *) NULL) {
org(triangleloop, p1);
dest(triangleloop, p2);
apex(triangleloop, p3);
if (b->order == 1) {
#ifdef TRILIBRARY
tlist[vertexindex++] = vertexmark(p1);
tlist[vertexindex++] = vertexmark(p2);
tlist[vertexindex++] = vertexmark(p3);
#else /* not TRILIBRARY */
/* Triangle number, indices for three vertices. */
fprintf(outfile, "%4ld %4d %4d %4d", elementnumber,
vertexmark(p1), vertexmark(p2), vertexmark(p3));
#endif /* not TRILIBRARY */
} else {
mid1 = (vertex) triangleloop.tri[m->highorderindex + 1];
mid2 = (vertex) triangleloop.tri[m->highorderindex + 2];
mid3 = (vertex) triangleloop.tri[m->highorderindex];
#ifdef TRILIBRARY
tlist[vertexindex++] = vertexmark(p1);
tlist[vertexindex++] = vertexmark(p2);
tlist[vertexindex++] = vertexmark(p3);
tlist[vertexindex++] = vertexmark(mid1);
tlist[vertexindex++] = vertexmark(mid2);
tlist[vertexindex++] = vertexmark(mid3);
#else /* not TRILIBRARY */
/* Triangle number, indices for six vertices. */
fprintf(outfile, "%4ld %4d %4d %4d %4d %4d %4d", elementnumber,
vertexmark(p1), vertexmark(p2), vertexmark(p3), vertexmark(mid1),
vertexmark(mid2), vertexmark(mid3));
#endif /* not TRILIBRARY */
}
#ifdef TRILIBRARY
for (i = 0; i < m->eextras; i++) {
talist[attribindex++] = elemattribute(triangleloop, i);
}
#else /* not TRILIBRARY */
for (i = 0; i < m->eextras; i++) {
fprintf(outfile, " %.17g", elemattribute(triangleloop, i));
}
fprintf(outfile, "\n");
#endif /* not TRILIBRARY */
triangleloop.tri = triangletraverse(m);
elementnumber++;
}
#ifndef TRILIBRARY
finishfile(outfile, argc, argv);
#endif /* not TRILIBRARY */
}
/*****************************************************************************/
/* */
/* writepoly() Write the segments and holes to a .poly file. */
/* */
/*****************************************************************************/
#ifdef TRILIBRARY
#ifdef ANSI_DECLARATORS
void writepoly(struct mesh *m, struct behavior *b,
int **segmentlist, int **segmentmarkerlist)
#else /* not ANSI_DECLARATORS */
void writepoly(m, b, segmentlist, segmentmarkerlist)
struct mesh *m;
struct behavior *b;
int **segmentlist;
int **segmentmarkerlist;
#endif /* not ANSI_DECLARATORS */
#else /* not TRILIBRARY */
#ifdef ANSI_DECLARATORS
void writepoly(struct mesh *m, struct behavior *b, char *polyfilename,
REAL *holelist, int holes, REAL *regionlist, int regions,
int argc, char **argv)
#else /* not ANSI_DECLARATORS */
void writepoly(m, b, polyfilename, holelist, holes, regionlist, regions,
argc, argv)
struct mesh *m;
struct behavior *b;
char *polyfilename;
REAL *holelist;
int holes;
REAL *regionlist;
int regions;
int argc;
char **argv;
#endif /* not ANSI_DECLARATORS */
#endif /* not TRILIBRARY */
{
#ifdef TRILIBRARY
int *slist;
int *smlist;
int index;
#else /* not TRILIBRARY */
FILE *outfile;
long holenumber, regionnumber;
#endif /* not TRILIBRARY */
struct osub subsegloop;
vertex endpoint1, endpoint2;
long subsegnumber;
#ifdef TRILIBRARY
if (!b->quiet) {
printf("Writing segments.\n");
}
/* Allocate memory for output segments if necessary. */
if (*segmentlist == (int *) NULL) {
*segmentlist = (int *) trimalloc((int) (m->subsegs.items * 2 *
sizeof(int)));
}
/* Allocate memory for output segment markers if necessary. */
if (!b->nobound && (*segmentmarkerlist == (int *) NULL)) {
*segmentmarkerlist = (int *) trimalloc((int) (m->subsegs.items *
sizeof(int)));
}
slist = *segmentlist;
smlist = *segmentmarkerlist;
index = 0;
#else /* not TRILIBRARY */
if (!b->quiet) {
printf("Writing %s.\n", polyfilename);
}
outfile = fopen(polyfilename, "w");
if (outfile == (FILE *) NULL) {
printf(" Error: Cannot create file %s.\n", polyfilename);
triexit(1);
}
/* The zero indicates that the vertices are in a separate .node file. */
/* Followed by number of dimensions, number of vertex attributes, */
/* and number of boundary markers (zero or one). */
fprintf(outfile, "%d %d %d %d\n", 0, m->mesh_dim, m->nextras,
1 - b->nobound);
/* Number of segments, number of boundary markers (zero or one). */
fprintf(outfile, "%ld %d\n", m->subsegs.items, 1 - b->nobound);
#endif /* not TRILIBRARY */
traversalinit(&m->subsegs);
subsegloop.ss = subsegtraverse(m);
subsegloop.ssorient = 0;
subsegnumber = b->firstnumber;
while (subsegloop.ss != (subseg *) NULL) {
sorg(subsegloop, endpoint1);
sdest(subsegloop, endpoint2);
#ifdef TRILIBRARY
/* Copy indices of the segment's two endpoints. */
slist[index++] = vertexmark(endpoint1);
slist[index++] = vertexmark(endpoint2);
if (!b->nobound) {
/* Copy the boundary marker. */
smlist[subsegnumber - b->firstnumber] = mark(subsegloop);
}
#else /* not TRILIBRARY */
/* Segment number, indices of its two endpoints, and possibly a marker. */
if (b->nobound) {
fprintf(outfile, "%4ld %4d %4d\n", subsegnumber,
vertexmark(endpoint1), vertexmark(endpoint2));
} else {
fprintf(outfile, "%4ld %4d %4d %4d\n", subsegnumber,
vertexmark(endpoint1), vertexmark(endpoint2), mark(subsegloop));
}
#endif /* not TRILIBRARY */
subsegloop.ss = subsegtraverse(m);
subsegnumber++;
}
#ifndef TRILIBRARY
#ifndef CDT_ONLY
fprintf(outfile, "%d\n", holes);
if (holes > 0) {
for (holenumber = 0; holenumber < holes; holenumber++) {
/* Hole number, x and y coordinates. */
fprintf(outfile, "%4ld %.17g %.17g\n", b->firstnumber + holenumber,
holelist[2 * holenumber], holelist[2 * holenumber + 1]);
}
}
if (regions > 0) {
fprintf(outfile, "%d\n", regions);
for (regionnumber = 0; regionnumber < regions; regionnumber++) {
/* Region number, x and y coordinates, attribute, maximum area. */
fprintf(outfile, "%4ld %.17g %.17g %.17g %.17g\n",
b->firstnumber + regionnumber,
regionlist[4 * regionnumber], regionlist[4 * regionnumber + 1],
regionlist[4 * regionnumber + 2],
regionlist[4 * regionnumber + 3]);
}
}
#endif /* not CDT_ONLY */
finishfile(outfile, argc, argv);
#endif /* not TRILIBRARY */
}
/*****************************************************************************/
/* */
/* writeedges() Write the edges to an .edge file. */
/* */
/*****************************************************************************/
#ifdef TRILIBRARY
#ifdef ANSI_DECLARATORS
void writeedges(struct mesh *m, struct behavior *b,
int **edgelist, int **edgemarkerlist)
#else /* not ANSI_DECLARATORS */
void writeedges(m, b, edgelist, edgemarkerlist)
struct mesh *m;
struct behavior *b;
int **edgelist;
int **edgemarkerlist;
#endif /* not ANSI_DECLARATORS */
#else /* not TRILIBRARY */
#ifdef ANSI_DECLARATORS
void writeedges(struct mesh *m, struct behavior *b, char *edgefilename,
int argc, char **argv)
#else /* not ANSI_DECLARATORS */
void writeedges(m, b, edgefilename, argc, argv)
struct mesh *m;
struct behavior *b;
char *edgefilename;
int argc;
char **argv;
#endif /* not ANSI_DECLARATORS */
#endif /* not TRILIBRARY */
{
#ifdef TRILIBRARY
int *elist;
int *emlist;
int index;
#else /* not TRILIBRARY */
FILE *outfile;
#endif /* not TRILIBRARY */
struct otri triangleloop, trisym;
struct osub checkmark;
vertex p1, p2;
long edgenumber;
triangle ptr; /* Temporary variable used by sym(). */
subseg sptr; /* Temporary variable used by tspivot(). */
#ifdef TRILIBRARY
if (!b->quiet) {
printf("Writing edges.\n");
}
/* Allocate memory for edges if necessary. */
if (*edgelist == (int *) NULL) {
*edgelist = (int *) trimalloc((int) (m->edges * 2 * sizeof(int)));
}
/* Allocate memory for edge markers if necessary. */
if (!b->nobound && (*edgemarkerlist == (int *) NULL)) {
*edgemarkerlist = (int *) trimalloc((int) (m->edges * sizeof(int)));
}
elist = *edgelist;
emlist = *edgemarkerlist;
index = 0;
#else /* not TRILIBRARY */
if (!b->quiet) {
printf("Writing %s.\n", edgefilename);
}
outfile = fopen(edgefilename, "w");
if (outfile == (FILE *) NULL) {
printf(" Error: Cannot create file %s.\n", edgefilename);
triexit(1);
}
/* Number of edges, number of boundary markers (zero or one). */
fprintf(outfile, "%ld %d\n", m->edges, 1 - b->nobound);
#endif /* not TRILIBRARY */
traversalinit(&m->triangles);
triangleloop.tri = triangletraverse(m);
edgenumber = b->firstnumber;
/* To loop over the set of edges, loop over all triangles, and look at */
/* the three edges of each triangle. If there isn't another triangle */
/* adjacent to the edge, operate on the edge. If there is another */
/* adjacent triangle, operate on the edge only if the current triangle */
/* has a smaller pointer than its neighbor. This way, each edge is */
/* considered only once. */
while (triangleloop.tri != (triangle *) NULL) {
for (triangleloop.orient = 0; triangleloop.orient < 3;
triangleloop.orient++) {
sym(triangleloop, trisym);
if ((triangleloop.tri < trisym.tri) || (trisym.tri == m->dummytri)) {
org(triangleloop, p1);
dest(triangleloop, p2);
#ifdef TRILIBRARY
elist[index++] = vertexmark(p1);
elist[index++] = vertexmark(p2);
#endif /* TRILIBRARY */
if (b->nobound) {
#ifndef TRILIBRARY
/* Edge number, indices of two endpoints. */
fprintf(outfile, "%4ld %d %d\n", edgenumber,
vertexmark(p1), vertexmark(p2));
#endif /* not TRILIBRARY */
} else {
/* Edge number, indices of two endpoints, and a boundary marker. */
/* If there's no subsegment, the boundary marker is zero. */
if (b->usesegments) {
tspivot(triangleloop, checkmark);
if (checkmark.ss == m->dummysub) {
#ifdef TRILIBRARY
emlist[edgenumber - b->firstnumber] = 0;
#else /* not TRILIBRARY */
fprintf(outfile, "%4ld %d %d %d\n", edgenumber,
vertexmark(p1), vertexmark(p2), 0);
#endif /* not TRILIBRARY */
} else {
#ifdef TRILIBRARY
emlist[edgenumber - b->firstnumber] = mark(checkmark);
#else /* not TRILIBRARY */
fprintf(outfile, "%4ld %d %d %d\n", edgenumber,
vertexmark(p1), vertexmark(p2), mark(checkmark));
#endif /* not TRILIBRARY */
}
} else {
#ifdef TRILIBRARY
emlist[edgenumber - b->firstnumber] = trisym.tri == m->dummytri;
#else /* not TRILIBRARY */
fprintf(outfile, "%4ld %d %d %d\n", edgenumber,
vertexmark(p1), vertexmark(p2), trisym.tri == m->dummytri);
#endif /* not TRILIBRARY */
}
}
edgenumber++;
}
}
triangleloop.tri = triangletraverse(m);
}
#ifndef TRILIBRARY
finishfile(outfile, argc, argv);
#endif /* not TRILIBRARY */
}
/*****************************************************************************/
/* */
/* writevoronoi() Write the Voronoi diagram to a .v.node and .v.edge */
/* file. */
/* */
/* The Voronoi diagram is the geometric dual of the Delaunay triangulation. */
/* Hence, the Voronoi vertices are listed by traversing the Delaunay */
/* triangles, and the Voronoi edges are listed by traversing the Delaunay */
/* edges. */
/* */
/* WARNING: In order to assign numbers to the Voronoi vertices, this */
/* procedure messes up the subsegments or the extra nodes of every */
/* element. Hence, you should call this procedure last. */
/* */
/*****************************************************************************/
#ifdef TRILIBRARY
#ifdef ANSI_DECLARATORS
void writevoronoi(struct mesh *m, struct behavior *b, REAL **vpointlist,
REAL **vpointattriblist, int **vpointmarkerlist,
int **vedgelist, int **vedgemarkerlist, REAL **vnormlist)
#else /* not ANSI_DECLARATORS */
void writevoronoi(m, b, vpointlist, vpointattriblist, vpointmarkerlist,
vedgelist, vedgemarkerlist, vnormlist)
struct mesh *m;
struct behavior *b;
REAL **vpointlist;
REAL **vpointattriblist;
int **vpointmarkerlist;
int **vedgelist;
int **vedgemarkerlist;
REAL **vnormlist;
#endif /* not ANSI_DECLARATORS */
#else /* not TRILIBRARY */
#ifdef ANSI_DECLARATORS
void writevoronoi(struct mesh *m, struct behavior *b, char *vnodefilename,
char *vedgefilename, int argc, char **argv)
#else /* not ANSI_DECLARATORS */
void writevoronoi(m, b, vnodefilename, vedgefilename, argc, argv)
struct mesh *m;
struct behavior *b;
char *vnodefilename;
char *vedgefilename;
int argc;
char **argv;
#endif /* not ANSI_DECLARATORS */
#endif /* not TRILIBRARY */
{
#ifdef TRILIBRARY
REAL *plist;
REAL *palist;
int *elist;
REAL *normlist;
int coordindex;
int attribindex;
#else /* not TRILIBRARY */
FILE *outfile;
#endif /* not TRILIBRARY */
struct otri triangleloop, trisym;
vertex torg, tdest, tapex;
REAL circumcenter[2];
REAL xi, eta;
long vnodenumber, vedgenumber;
int p1, p2;
int i;
triangle ptr; /* Temporary variable used by sym(). */
#ifdef TRILIBRARY
if (!b->quiet) {
printf("Writing Voronoi vertices.\n");
}
/* Allocate memory for Voronoi vertices if necessary. */
if (*vpointlist == (REAL *) NULL) {
*vpointlist = (REAL *) trimalloc((int) (m->triangles.items * 2 *
sizeof(REAL)));
}
/* Allocate memory for Voronoi vertex attributes if necessary. */
if (*vpointattriblist == (REAL *) NULL) {
*vpointattriblist = (REAL *) trimalloc((int) (m->triangles.items *
m->nextras * sizeof(REAL)));
}
*vpointmarkerlist = (int *) NULL;
plist = *vpointlist;
palist = *vpointattriblist;
coordindex = 0;
attribindex = 0;
#else /* not TRILIBRARY */
if (!b->quiet) {
printf("Writing %s.\n", vnodefilename);
}
outfile = fopen(vnodefilename, "w");
if (outfile == (FILE *) NULL) {
printf(" Error: Cannot create file %s.\n", vnodefilename);
triexit(1);
}
/* Number of triangles, two dimensions, number of vertex attributes, */
/* no markers. */
fprintf(outfile, "%ld %d %d %d\n", m->triangles.items, 2, m->nextras, 0);
#endif /* not TRILIBRARY */
traversalinit(&m->triangles);
triangleloop.tri = triangletraverse(m);
triangleloop.orient = 0;
vnodenumber = b->firstnumber;
while (triangleloop.tri != (triangle *) NULL) {
org(triangleloop, torg);
dest(triangleloop, tdest);
apex(triangleloop, tapex);
findcircumcenter(m, b, torg, tdest, tapex, circumcenter, &xi, &eta, 0);
#ifdef TRILIBRARY
/* X and y coordinates. */
plist[coordindex++] = circumcenter[0];
plist[coordindex++] = circumcenter[1];
for (i = 2; i < 2 + m->nextras; i++) {
/* Interpolate the vertex attributes at the circumcenter. */
palist[attribindex++] = torg[i] + xi * (tdest[i] - torg[i])
+ eta * (tapex[i] - torg[i]);
}
#else /* not TRILIBRARY */
/* Voronoi vertex number, x and y coordinates. */
fprintf(outfile, "%4ld %.17g %.17g", vnodenumber, circumcenter[0],
circumcenter[1]);
for (i = 2; i < 2 + m->nextras; i++) {
/* Interpolate the vertex attributes at the circumcenter. */
fprintf(outfile, " %.17g", torg[i] + xi * (tdest[i] - torg[i])
+ eta * (tapex[i] - torg[i]));
}
fprintf(outfile, "\n");
#endif /* not TRILIBRARY */
* (int *) (triangleloop.tri + 6) = (int) vnodenumber;
triangleloop.tri = triangletraverse(m);
vnodenumber++;
}
#ifndef TRILIBRARY
finishfile(outfile, argc, argv);
#endif /* not TRILIBRARY */
#ifdef TRILIBRARY
if (!b->quiet) {
printf("Writing Voronoi edges.\n");
}
/* Allocate memory for output Voronoi edges if necessary. */
if (*vedgelist == (int *) NULL) {
*vedgelist = (int *) trimalloc((int) (m->edges * 2 * sizeof(int)));
}
*vedgemarkerlist = (int *) NULL;
/* Allocate memory for output Voronoi norms if necessary. */
if (*vnormlist == (REAL *) NULL) {
*vnormlist = (REAL *) trimalloc((int) (m->edges * 2 * sizeof(REAL)));
}
elist = *vedgelist;
normlist = *vnormlist;
coordindex = 0;
#else /* not TRILIBRARY */
if (!b->quiet) {
printf("Writing %s.\n", vedgefilename);
}
outfile = fopen(vedgefilename, "w");
if (outfile == (FILE *) NULL) {
printf(" Error: Cannot create file %s.\n", vedgefilename);
triexit(1);
}
/* Number of edges, zero boundary markers. */
fprintf(outfile, "%ld %d\n", m->edges, 0);
#endif /* not TRILIBRARY */
traversalinit(&m->triangles);
triangleloop.tri = triangletraverse(m);
vedgenumber = b->firstnumber;
/* To loop over the set of edges, loop over all triangles, and look at */
/* the three edges of each triangle. If there isn't another triangle */
/* adjacent to the edge, operate on the edge. If there is another */
/* adjacent triangle, operate on the edge only if the current triangle */
/* has a smaller pointer than its neighbor. This way, each edge is */
/* considered only once. */
while (triangleloop.tri != (triangle *) NULL) {
for (triangleloop.orient = 0; triangleloop.orient < 3;
triangleloop.orient++) {
sym(triangleloop, trisym);
if ((triangleloop.tri < trisym.tri) || (trisym.tri == m->dummytri)) {
/* Find the number of this triangle (and Voronoi vertex). */
p1 = * (int *) (triangleloop.tri + 6);
if (trisym.tri == m->dummytri) {
org(triangleloop, torg);
dest(triangleloop, tdest);
#ifdef TRILIBRARY
/* Copy an infinite ray. Index of one endpoint, and -1. */
elist[coordindex] = p1;
normlist[coordindex++] = tdest[1] - torg[1];
elist[coordindex] = -1;
normlist[coordindex++] = torg[0] - tdest[0];
#else /* not TRILIBRARY */
/* Write an infinite ray. Edge number, index of one endpoint, -1, */
/* and x and y coordinates of a vector representing the */
/* direction of the ray. */
fprintf(outfile, "%4ld %d %d %.17g %.17g\n", vedgenumber,
p1, -1, tdest[1] - torg[1], torg[0] - tdest[0]);
#endif /* not TRILIBRARY */
} else {
/* Find the number of the adjacent triangle (and Voronoi vertex). */
p2 = * (int *) (trisym.tri + 6);
/* Finite edge. Write indices of two endpoints. */
#ifdef TRILIBRARY
elist[coordindex] = p1;
normlist[coordindex++] = 0.0;
elist[coordindex] = p2;
normlist[coordindex++] = 0.0;
#else /* not TRILIBRARY */
fprintf(outfile, "%4ld %d %d\n", vedgenumber, p1, p2);
#endif /* not TRILIBRARY */
}
vedgenumber++;
}
}
triangleloop.tri = triangletraverse(m);
}
#ifndef TRILIBRARY
finishfile(outfile, argc, argv);
#endif /* not TRILIBRARY */
}
#ifdef TRILIBRARY
#ifdef ANSI_DECLARATORS
void writeneighbors(struct mesh *m, struct behavior *b, int **neighborlist)
#else /* not ANSI_DECLARATORS */
void writeneighbors(m, b, neighborlist)
struct mesh *m;
struct behavior *b;
int **neighborlist;
#endif /* not ANSI_DECLARATORS */
#else /* not TRILIBRARY */
#ifdef ANSI_DECLARATORS
void writeneighbors(struct mesh *m, struct behavior *b, char *neighborfilename,
int argc, char **argv)
#else /* not ANSI_DECLARATORS */
void writeneighbors(m, b, neighborfilename, argc, argv)
struct mesh *m;
struct behavior *b;
char *neighborfilename;
int argc;
char **argv;
#endif /* not ANSI_DECLARATORS */
#endif /* not TRILIBRARY */
{
#ifdef TRILIBRARY
int *nlist;
int index;
#else /* not TRILIBRARY */
FILE *outfile;
#endif /* not TRILIBRARY */
struct otri triangleloop, trisym;
long elementnumber;
int neighbor1, neighbor2, neighbor3;
triangle ptr; /* Temporary variable used by sym(). */
#ifdef TRILIBRARY
if (!b->quiet) {
printf("Writing neighbors.\n");
}
/* Allocate memory for neighbors if necessary. */
if (*neighborlist == (int *) NULL) {
*neighborlist = (int *) trimalloc((int) (m->triangles.items * 3 *
sizeof(int)));
}
nlist = *neighborlist;
index = 0;
#else /* not TRILIBRARY */
if (!b->quiet) {
printf("Writing %s.\n", neighborfilename);
}
outfile = fopen(neighborfilename, "w");
if (outfile == (FILE *) NULL) {
printf(" Error: Cannot create file %s.\n", neighborfilename);
triexit(1);
}
/* Number of triangles, three neighbors per triangle. */
fprintf(outfile, "%ld %d\n", m->triangles.items, 3);
#endif /* not TRILIBRARY */
traversalinit(&m->triangles);
triangleloop.tri = triangletraverse(m);
triangleloop.orient = 0;
elementnumber = b->firstnumber;
while (triangleloop.tri != (triangle *) NULL) {
* (int *) (triangleloop.tri + 6) = (int) elementnumber;
triangleloop.tri = triangletraverse(m);
elementnumber++;
}
* (int *) (m->dummytri + 6) = -1;
traversalinit(&m->triangles);
triangleloop.tri = triangletraverse(m);
elementnumber = b->firstnumber;
while (triangleloop.tri != (triangle *) NULL) {
triangleloop.orient = 1;
sym(triangleloop, trisym);
neighbor1 = * (int *) (trisym.tri + 6);
triangleloop.orient = 2;
sym(triangleloop, trisym);
neighbor2 = * (int *) (trisym.tri + 6);
triangleloop.orient = 0;
sym(triangleloop, trisym);
neighbor3 = * (int *) (trisym.tri + 6);
#ifdef TRILIBRARY
nlist[index++] = neighbor1;
nlist[index++] = neighbor2;
nlist[index++] = neighbor3;
#else /* not TRILIBRARY */
/* Triangle number, neighboring triangle numbers. */
fprintf(outfile, "%4ld %d %d %d\n", elementnumber,
neighbor1, neighbor2, neighbor3);
#endif /* not TRILIBRARY */
triangleloop.tri = triangletraverse(m);
elementnumber++;
}
#ifndef TRILIBRARY
finishfile(outfile, argc, argv);
#endif /* not TRILIBRARY */
}
/*****************************************************************************/
/* */
/* writeoff() Write the triangulation to an .off file. */
/* */
/* OFF stands for the Object File Format, a format used by the Geometry */
/* Center's Geomview package. */
/* */
/*****************************************************************************/
#ifndef TRILIBRARY
#ifdef ANSI_DECLARATORS
void writeoff(struct mesh *m, struct behavior *b, char *offfilename,
int argc, char **argv)
#else /* not ANSI_DECLARATORS */
void writeoff(m, b, offfilename, argc, argv)
struct mesh *m;
struct behavior *b;
char *offfilename;
int argc;
char **argv;
#endif /* not ANSI_DECLARATORS */
{
FILE *outfile;
struct otri triangleloop;
vertex vertexloop;
vertex p1, p2, p3;
long outvertices;
if (!b->quiet) {
printf("Writing %s.\n", offfilename);
}
if (b->jettison) {
outvertices = m->vertices.items - m->undeads;
} else {
outvertices = m->vertices.items;
}
outfile = fopen(offfilename, "w");
if (outfile == (FILE *) NULL) {
printf(" Error: Cannot create file %s.\n", offfilename);
triexit(1);
}
/* Number of vertices, triangles, and edges. */
fprintf(outfile, "OFF\n%ld %ld %ld\n", outvertices, m->triangles.items,
m->edges);
/* Write the vertices. */
traversalinit(&m->vertices);
vertexloop = vertextraverse(m);
while (vertexloop != (vertex) NULL) {
if (!b->jettison || (vertextype(vertexloop) != UNDEADVERTEX)) {
/* The "0.0" is here because the OFF format uses 3D coordinates. */
fprintf(outfile, " %.17g %.17g %.17g\n", vertexloop[0], vertexloop[1],
0.0);
}
vertexloop = vertextraverse(m);
}
/* Write the triangles. */
traversalinit(&m->triangles);
triangleloop.tri = triangletraverse(m);
triangleloop.orient = 0;
while (triangleloop.tri != (triangle *) NULL) {
org(triangleloop, p1);
dest(triangleloop, p2);
apex(triangleloop, p3);
/* The "3" means a three-vertex polygon. */
fprintf(outfile, " 3 %4d %4d %4d\n", vertexmark(p1) - b->firstnumber,
vertexmark(p2) - b->firstnumber, vertexmark(p3) - b->firstnumber);
triangleloop.tri = triangletraverse(m);
}
finishfile(outfile, argc, argv);
}
#endif /* not TRILIBRARY */
/** **/
/** **/
/********* File I/O routines end here *********/
/*****************************************************************************/
/* */
/* quality_statistics() Print statistics about the quality of the mesh. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
void quality_statistics(struct mesh *m, struct behavior *b)
#else /* not ANSI_DECLARATORS */
void quality_statistics(m, b)
struct mesh *m;
struct behavior *b;
#endif /* not ANSI_DECLARATORS */
{
struct otri triangleloop;
vertex p[3];
REAL cossquaretable[8];
REAL ratiotable[16];
REAL dx[3], dy[3];
REAL edgelength[3];
REAL dotproduct;
REAL cossquare;
REAL triarea;
REAL shortest, longest;
REAL trilongest2;
REAL smallestarea, biggestarea;
REAL triminaltitude2;
REAL minaltitude;
REAL triaspect2;
REAL worstaspect;
REAL smallestangle, biggestangle;
REAL radconst, degconst;
int angletable[18];
int aspecttable[16];
int aspectindex;
int tendegree;
int acutebiggest;
int i, ii, j, k;
printf("Mesh quality statistics:\n\n");
radconst = PI / 18.0;
degconst = 180.0 / PI;
for (i = 0; i < 8; i++) {
cossquaretable[i] = cos(radconst * (REAL) (i + 1));
cossquaretable[i] = cossquaretable[i] * cossquaretable[i];
}
for (i = 0; i < 18; i++) {
angletable[i] = 0;
}
ratiotable[0] = 1.5; ratiotable[1] = 2.0;
ratiotable[2] = 2.5; ratiotable[3] = 3.0;
ratiotable[4] = 4.0; ratiotable[5] = 6.0;
ratiotable[6] = 10.0; ratiotable[7] = 15.0;
ratiotable[8] = 25.0; ratiotable[9] = 50.0;
ratiotable[10] = 100.0; ratiotable[11] = 300.0;
ratiotable[12] = 1000.0; ratiotable[13] = 10000.0;
ratiotable[14] = 100000.0; ratiotable[15] = 0.0;
for (i = 0; i < 16; i++) {
aspecttable[i] = 0;
}
worstaspect = 0.0;
minaltitude = m->xmax - m->xmin + m->ymax - m->ymin;
minaltitude = minaltitude * minaltitude;
shortest = minaltitude;
longest = 0.0;
smallestarea = minaltitude;
biggestarea = 0.0;
worstaspect = 0.0;
smallestangle = 0.0;
biggestangle = 2.0;
acutebiggest = 1;
traversalinit(&m->triangles);
triangleloop.tri = triangletraverse(m);
triangleloop.orient = 0;
while (triangleloop.tri != (triangle *) NULL) {
org(triangleloop, p[0]);
dest(triangleloop, p[1]);
apex(triangleloop, p[2]);
trilongest2 = 0.0;
for (i = 0; i < 3; i++) {
j = plus1mod3[i];
k = minus1mod3[i];
dx[i] = p[j][0] - p[k][0];
dy[i] = p[j][1] - p[k][1];
edgelength[i] = dx[i] * dx[i] + dy[i] * dy[i];
if (edgelength[i] > trilongest2) {
trilongest2 = edgelength[i];
}
if (edgelength[i] > longest) {
longest = edgelength[i];
}
if (edgelength[i] < shortest) {
shortest = edgelength[i];
}
}
triarea = counterclockwise(m, b, p[0], p[1], p[2]);
if (triarea < smallestarea) {
smallestarea = triarea;
}
if (triarea > biggestarea) {
biggestarea = triarea;
}
triminaltitude2 = triarea * triarea / trilongest2;
if (triminaltitude2 < minaltitude) {
minaltitude = triminaltitude2;
}
triaspect2 = trilongest2 / triminaltitude2;
if (triaspect2 > worstaspect) {
worstaspect = triaspect2;
}
aspectindex = 0;
while ((triaspect2 > ratiotable[aspectindex] * ratiotable[aspectindex])
&& (aspectindex < 15)) {
aspectindex++;
}
aspecttable[aspectindex]++;
for (i = 0; i < 3; i++) {
j = plus1mod3[i];
k = minus1mod3[i];
dotproduct = dx[j] * dx[k] + dy[j] * dy[k];
cossquare = dotproduct * dotproduct / (edgelength[j] * edgelength[k]);
tendegree = 8;
for (ii = 7; ii >= 0; ii--) {
if (cossquare > cossquaretable[ii]) {
tendegree = ii;
}
}
if (dotproduct <= 0.0) {
angletable[tendegree]++;
if (cossquare > smallestangle) {
smallestangle = cossquare;
}
if (acutebiggest && (cossquare < biggestangle)) {
biggestangle = cossquare;
}
} else {
angletable[17 - tendegree]++;
if (acutebiggest || (cossquare > biggestangle)) {
biggestangle = cossquare;
acutebiggest = 0;
}
}
}
triangleloop.tri = triangletraverse(m);
}
shortest = sqrt(shortest);
longest = sqrt(longest);
minaltitude = sqrt(minaltitude);
worstaspect = sqrt(worstaspect);
smallestarea *= 0.5;
biggestarea *= 0.5;
if (smallestangle >= 1.0) {
smallestangle = 0.0;
} else {
smallestangle = degconst * acos(sqrt(smallestangle));
}
if (biggestangle >= 1.0) {
biggestangle = 180.0;
} else {
if (acutebiggest) {
biggestangle = degconst * acos(sqrt(biggestangle));
} else {
biggestangle = 180.0 - degconst * acos(sqrt(biggestangle));
}
}
printf(" Smallest area: %16.5g | Largest area: %16.5g\n",
smallestarea, biggestarea);
printf(" Shortest edge: %16.5g | Longest edge: %16.5g\n",
shortest, longest);
printf(" Shortest altitude: %12.5g | Largest aspect ratio: %8.5g\n\n",
minaltitude, worstaspect);
printf(" Triangle aspect ratio histogram:\n");
printf(" 1.1547 - %-6.6g : %8d | %6.6g - %-6.6g : %8d\n",
ratiotable[0], aspecttable[0], ratiotable[7], ratiotable[8],
aspecttable[8]);
for (i = 1; i < 7; i++) {
printf(" %6.6g - %-6.6g : %8d | %6.6g - %-6.6g : %8d\n",
ratiotable[i - 1], ratiotable[i], aspecttable[i],
ratiotable[i + 7], ratiotable[i + 8], aspecttable[i + 8]);
}
printf(" %6.6g - %-6.6g : %8d | %6.6g - : %8d\n",
ratiotable[6], ratiotable[7], aspecttable[7], ratiotable[14],
aspecttable[15]);
printf(" (Aspect ratio is longest edge divided by shortest altitude)\n\n");
printf(" Smallest angle: %15.5g | Largest angle: %15.5g\n\n",
smallestangle, biggestangle);
printf(" Angle histogram:\n");
for (i = 0; i < 9; i++) {
printf(" %3d - %3d degrees: %8d | %3d - %3d degrees: %8d\n",
i * 10, i * 10 + 10, angletable[i],
i * 10 + 90, i * 10 + 100, angletable[i + 9]);
}
printf("\n");
}
/*****************************************************************************/
/* */
/* statistics() Print all sorts of cool facts. */
/* */
/*****************************************************************************/
#ifdef ANSI_DECLARATORS
void statistics(struct mesh *m, struct behavior *b)
#else /* not ANSI_DECLARATORS */
void statistics(m, b)
struct mesh *m;
struct behavior *b;
#endif /* not ANSI_DECLARATORS */
{
printf("\nStatistics:\n\n");
printf(" Input vertices: %d\n", m->invertices);
if (b->refine) {
printf(" Input triangles: %d\n", m->inelements);
}
if (b->poly) {
printf(" Input segments: %d\n", m->insegments);
if (!b->refine) {
printf(" Input holes: %d\n", m->holes);
}
}
printf("\n Mesh vertices: %ld\n", m->vertices.items - m->undeads);
printf(" Mesh triangles: %ld\n", m->triangles.items);
printf(" Mesh edges: %ld\n", m->edges);
printf(" Mesh exterior boundary edges: %ld\n", m->hullsize);
if (b->poly || b->refine) {
printf(" Mesh interior boundary edges: %ld\n",
m->subsegs.items - m->hullsize);
printf(" Mesh subsegments (constrained edges): %ld\n",
m->subsegs.items);
}
printf("\n");
if (b->verbose) {
quality_statistics(m, b);
printf("Memory allocation statistics:\n\n");
printf(" Maximum number of vertices: %ld\n", m->vertices.maxitems);
printf(" Maximum number of triangles: %ld\n", m->triangles.maxitems);
if (m->subsegs.maxitems > 0) {
printf(" Maximum number of subsegments: %ld\n", m->subsegs.maxitems);
}
if (m->viri.maxitems > 0) {
printf(" Maximum number of viri: %ld\n", m->viri.maxitems);
}
if (m->badsubsegs.maxitems > 0) {
printf(" Maximum number of encroached subsegments: %ld\n",
m->badsubsegs.maxitems);
}
if (m->badtriangles.maxitems > 0) {
printf(" Maximum number of bad triangles: %ld\n",
m->badtriangles.maxitems);
}
if (m->flipstackers.maxitems > 0) {
printf(" Maximum number of stacked triangle flips: %ld\n",
m->flipstackers.maxitems);
}
if (m->splaynodes.maxitems > 0) {
printf(" Maximum number of splay tree nodes: %ld\n",
m->splaynodes.maxitems);
}
printf(" Approximate heap memory use (bytes): %ld\n\n",
m->vertices.maxitems * m->vertices.itembytes +
m->triangles.maxitems * m->triangles.itembytes +
m->subsegs.maxitems * m->subsegs.itembytes +
m->viri.maxitems * m->viri.itembytes +
m->badsubsegs.maxitems * m->badsubsegs.itembytes +
m->badtriangles.maxitems * m->badtriangles.itembytes +
m->flipstackers.maxitems * m->flipstackers.itembytes +
m->splaynodes.maxitems * m->splaynodes.itembytes);
printf("Algorithmic statistics:\n\n");
if (!b->weighted) {
printf(" Number of incircle tests: %ld\n", m->incirclecount);
} else {
printf(" Number of 3D orientation tests: %ld\n", m->orient3dcount);
}
printf(" Number of 2D orientation tests: %ld\n", m->counterclockcount);
if (m->hyperbolacount > 0) {
printf(" Number of right-of-hyperbola tests: %ld\n",
m->hyperbolacount);
}
if (m->circletopcount > 0) {
printf(" Number of circle top computations: %ld\n",
m->circletopcount);
}
if (m->circumcentercount > 0) {
printf(" Number of triangle circumcenter computations: %ld\n",
m->circumcentercount);
}
printf("\n");
}
}
/*****************************************************************************/
/* */
/* main() or triangulate() Gosh, do everything. */
/* */
/* 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 */
/* - triangulate them (no -r), or */
/* - read an old mesh from files and reconstruct it (-r). */
/* - Insert the PSLG segments (-p), and possibly segments on the convex */
/* hull (-c). */
/* - Read the holes (-p), regional attributes (-pA), and regional area */
/* constraints (-pa). Carve the holes and concavities, and spread the */
/* regional attributes and area constraints. */
/* - Enforce the constraints on minimum angle (-q) and maximum area (-a). */
/* Also enforce the conforming Delaunay property (-q and -a). */
/* - Compute the number of edges in the resulting mesh. */
/* - Promote the mesh's linear triangles to higher order elements (-o). */
/* - Write the output files and print the statistics. */
/* - Check the consistency and Delaunay property of the mesh (-C). */
/* */
/*****************************************************************************/
#ifdef TRILIBRARY
#ifdef ANSI_DECLARATORS
void triangulate(char *triswitches, struct triangulateio *in,
struct triangulateio *out, struct triangulateio *vorout)
#else /* not ANSI_DECLARATORS */
void triangulate(triswitches, in, out, vorout)
char *triswitches;
struct triangulateio *in;
struct triangulateio *out;
struct triangulateio *vorout;
#endif /* not ANSI_DECLARATORS */
#else /* not TRILIBRARY */
#ifdef ANSI_DECLARATORS
int main(int argc, char **argv)
#else /* not ANSI_DECLARATORS */
int main(argc, argv)
int argc;
char **argv;
#endif /* not ANSI_DECLARATORS */
#endif /* not TRILIBRARY */
{
struct mesh m;
struct behavior b;
REAL *holearray; /* Array of holes. */
REAL *regionarray; /* Array of regional attributes and area constraints. */
#ifndef TRILIBRARY
FILE *polyfile;
#endif /* not TRILIBRARY */
#ifndef NO_TIMER
/* Variables for timing the performance of Triangle. The types are */
/* defined in sys/time.h. */
struct timeval tv0, tv1, tv2, tv3, tv4, tv5, tv6;
struct timezone tz;
#endif /* not NO_TIMER */
#ifndef NO_TIMER
gettimeofday(&tv0, &tz);
#endif /* not NO_TIMER */
triangleinit(&m);
#ifdef TRILIBRARY
parsecommandline(1, &triswitches, &b);
#else /* not TRILIBRARY */
parsecommandline(argc, argv, &b);
#endif /* not TRILIBRARY */
m.steinerleft = b.steiner;
#ifdef TRILIBRARY
transfernodes(&m, &b, in->pointlist, in->pointattributelist,
in->pointmarkerlist, in->numberofpoints,
in->numberofpointattributes);
#else /* not TRILIBRARY */
readnodes(&m, &b, b.innodefilename, b.inpolyfilename, &polyfile);
#endif /* not TRILIBRARY */
#ifndef NO_TIMER
if (!b.quiet) {
gettimeofday(&tv1, &tz);
}
#endif /* not NO_TIMER */
#ifdef CDT_ONLY
m.hullsize = delaunay(&m, &b); /* Triangulate the vertices. */
#else /* not CDT_ONLY */
if (b.refine) {
/* Read and reconstruct a mesh. */
#ifdef TRILIBRARY
m.hullsize = reconstruct(&m, &b, in->trianglelist,
in->triangleattributelist, in->trianglearealist,
in->numberoftriangles, in->numberofcorners,
in->numberoftriangleattributes,
in->segmentlist, in->segmentmarkerlist,
in->numberofsegments);
#else /* not TRILIBRARY */
m.hullsize = reconstruct(&m, &b, b.inelefilename, b.areafilename,
b.inpolyfilename, polyfile);
#endif /* not TRILIBRARY */
} else {
m.hullsize = delaunay(&m, &b); /* Triangulate the vertices. */
}
#endif /* not CDT_ONLY */
#ifndef NO_TIMER
if (!b.quiet) {
gettimeofday(&tv2, &tz);
if (b.refine) {
printf("Mesh reconstruction");
} else {
printf("Delaunay");
}
printf(" milliseconds: %ld\n", 1000l * (tv2.tv_sec - tv1.tv_sec) +
(tv2.tv_usec - tv1.tv_usec) / 1000l);
}
#endif /* not NO_TIMER */
/* Ensure that no vertex can be mistaken for a triangular bounding */
/* box vertex in insertvertex(). */
m.infvertex1 = (vertex) NULL;
m.infvertex2 = (vertex) NULL;
m.infvertex3 = (vertex) NULL;
if (b.usesegments) {
m.checksegments = 1; /* Segments will be introduced next. */
if (!b.refine) {
/* Insert PSLG segments and/or convex hull segments. */
#ifdef TRILIBRARY
formskeleton(&m, &b, in->segmentlist,
in->segmentmarkerlist, in->numberofsegments);
#else /* not TRILIBRARY */
formskeleton(&m, &b, polyfile, b.inpolyfilename);
#endif /* not TRILIBRARY */
}
}
#ifndef NO_TIMER
if (!b.quiet) {
gettimeofday(&tv3, &tz);
if (b.usesegments && !b.refine) {
printf("Segment milliseconds: %ld\n",
1000l * (tv3.tv_sec - tv2.tv_sec) +
(tv3.tv_usec - tv2.tv_usec) / 1000l);
}
}
#endif /* not NO_TIMER */
if (b.poly && (m.triangles.items > 0)) {
#ifdef TRILIBRARY
holearray = in->holelist;
m.holes = in->numberofholes;
regionarray = in->regionlist;
m.regions = in->numberofregions;
#else /* not TRILIBRARY */
readholes(&m, &b, polyfile, b.inpolyfilename, &holearray, &m.holes,
®ionarray, &m.regions);
#endif /* not TRILIBRARY */
if (!b.refine) {
/* Carve out holes and concavities. */
carveholes(&m, &b, holearray, m.holes, regionarray, m.regions);
}
} else {
/* Without a PSLG, there can be no holes or regional attributes */
/* or area constraints. The following are set to zero to avoid */
/* an accidental free() later. */
m.holes = 0;
m.regions = 0;
}
#ifndef NO_TIMER
if (!b.quiet) {
gettimeofday(&tv4, &tz);
if (b.poly && !b.refine) {
printf("Hole milliseconds: %ld\n", 1000l * (tv4.tv_sec - tv3.tv_sec) +
(tv4.tv_usec - tv3.tv_usec) / 1000l);
}
}
#endif /* not NO_TIMER */
#ifndef CDT_ONLY
if (b.quality && (m.triangles.items > 0)) {
enforcequality(&m, &b); /* Enforce angle and area constraints. */
}
#endif /* not CDT_ONLY */
#ifndef NO_TIMER
if (!b.quiet) {
gettimeofday(&tv5, &tz);
#ifndef CDT_ONLY
if (b.quality) {
printf("Quality milliseconds: %ld\n",
1000l * (tv5.tv_sec - tv4.tv_sec) +
(tv5.tv_usec - tv4.tv_usec) / 1000l);
}
#endif /* not CDT_ONLY */
}
#endif /* not NO_TIMER */
/* Calculate the number of edges. */
m.edges = (3l * m.triangles.items + m.hullsize) / 2l;
if (b.order > 1) {
highorder(&m, &b); /* Promote elements to higher polynomial order. */
}
if (!b.quiet) {
printf("\n");
}
#ifdef TRILIBRARY
if (b.jettison) {
out->numberofpoints = m.vertices.items - m.undeads;
} else {
out->numberofpoints = m.vertices.items;
}
out->numberofpointattributes = m.nextras;
out->numberoftriangles = m.triangles.items;
out->numberofcorners = (b.order + 1) * (b.order + 2) / 2;
out->numberoftriangleattributes = m.eextras;
out->numberofedges = m.edges;
if (b.usesegments) {
out->numberofsegments = m.subsegs.items;
} else {
out->numberofsegments = m.hullsize;
}
if (vorout != (struct triangulateio *) NULL) {
vorout->numberofpoints = m.triangles.items;
vorout->numberofpointattributes = m.nextras;
vorout->numberofedges = m.edges;
}
#endif /* TRILIBRARY */
/* If not using iteration numbers, don't write a .node file if one was */
/* read, because the original one would be overwritten! */
if (b.nonodewritten || (b.noiterationnum && m.readnodefile)) {
if (!b.quiet) {
#ifdef TRILIBRARY
printf("NOT writing vertices.\n");
#else /* not TRILIBRARY */
printf("NOT writing a .node file.\n");
#endif /* not TRILIBRARY */
}
numbernodes(&m, &b); /* We must remember to number the vertices. */
} else {
/* writenodes() numbers the vertices too. */
#ifdef TRILIBRARY
writenodes(&m, &b, &out->pointlist, &out->pointattributelist,
&out->pointmarkerlist);
#else /* not TRILIBRARY */
writenodes(&m, &b, b.outnodefilename, argc, argv);
#endif /* TRILIBRARY */
}
if (b.noelewritten) {
if (!b.quiet) {
#ifdef TRILIBRARY
printf("NOT writing triangles.\n");
#else /* not TRILIBRARY */
printf("NOT writing an .ele file.\n");
#endif /* not TRILIBRARY */
}
} else {
#ifdef TRILIBRARY
writeelements(&m, &b, &out->trianglelist, &out->triangleattributelist);
#else /* not TRILIBRARY */
writeelements(&m, &b, b.outelefilename, argc, argv);
#endif /* not TRILIBRARY */
}
/* The -c switch (convex switch) causes a PSLG to be written */
/* even if none was read. */
if (b.poly || b.convex) {
/* If not using iteration numbers, don't overwrite the .poly file. */
if (b.nopolywritten || b.noiterationnum) {
if (!b.quiet) {
#ifdef TRILIBRARY
printf("NOT writing segments.\n");
#else /* not TRILIBRARY */
printf("NOT writing a .poly file.\n");
#endif /* not TRILIBRARY */
}
} else {
#ifdef TRILIBRARY
writepoly(&m, &b, &out->segmentlist, &out->segmentmarkerlist);
out->numberofholes = m.holes;
out->numberofregions = m.regions;
if (b.poly) {
out->holelist = in->holelist;
out->regionlist = in->regionlist;
} else {
out->holelist = (REAL *) NULL;
out->regionlist = (REAL *) NULL;
}
#else /* not TRILIBRARY */
writepoly(&m, &b, b.outpolyfilename, holearray, m.holes, regionarray,
m.regions, argc, argv);
#endif /* not TRILIBRARY */
}
}
#ifndef TRILIBRARY
#ifndef CDT_ONLY
if (m.regions > 0) {
trifree((VOID *) regionarray);
}
#endif /* not CDT_ONLY */
if (m.holes > 0) {
trifree((VOID *) holearray);
}
if (b.geomview) {
writeoff(&m, &b, b.offfilename, argc, argv);
}
#endif /* not TRILIBRARY */
if (b.edgesout) {
#ifdef TRILIBRARY
writeedges(&m, &b, &out->edgelist, &out->edgemarkerlist);
#else /* not TRILIBRARY */
writeedges(&m, &b, b.edgefilename, argc, argv);
#endif /* not TRILIBRARY */
}
if (b.voronoi) {
#ifdef TRILIBRARY
writevoronoi(&m, &b, &vorout->pointlist, &vorout->pointattributelist,
&vorout->pointmarkerlist, &vorout->edgelist,
&vorout->edgemarkerlist, &vorout->normlist);
#else /* not TRILIBRARY */
writevoronoi(&m, &b, b.vnodefilename, b.vedgefilename, argc, argv);
#endif /* not TRILIBRARY */
}
if (b.neighbors) {
#ifdef TRILIBRARY
writeneighbors(&m, &b, &out->neighborlist);
#else /* not TRILIBRARY */
writeneighbors(&m, &b, b.neighborfilename, argc, argv);
#endif /* not TRILIBRARY */
}
if (!b.quiet) {
#ifndef NO_TIMER
gettimeofday(&tv6, &tz);
printf("\nOutput milliseconds: %ld\n",
1000l * (tv6.tv_sec - tv5.tv_sec) +
(tv6.tv_usec - tv5.tv_usec) / 1000l);
printf("Total running milliseconds: %ld\n",
1000l * (tv6.tv_sec - tv0.tv_sec) +
(tv6.tv_usec - tv0.tv_usec) / 1000l);
#endif /* not NO_TIMER */
statistics(&m, &b);
}
#ifndef REDUCED
if (b.docheck) {
checkmesh(&m, &b);
checkdelaunay(&m, &b);
}
#endif /* not REDUCED */
triangledeinit(&m, &b);
#ifndef TRILIBRARY
return 0;
#endif /* not TRILIBRARY */
}
================================================
FILE: src/cpp/triangle.h
================================================
#ifndef _HEADER_SEEN_TRIANGLE_H
#define _HEADER_SEEN_TRIANGLE_H
#ifdef __cplusplus
extern "C"
{
#endif
/*****************************************************************************/
/* */
/* (triangle.h) */
/* */
/* Include file for programs that call Triangle. */
/* */
/* Accompanies Triangle Version 1.6 */
/* July 28, 2005 */
/* */
/* Copyright 1996, 2005 */
/* Jonathan Richard Shewchuk */
/* 2360 Woolsey #H */
/* Berkeley, California 94705-1927 */
/* jrs@cs.berkeley.edu */
/* */
/*****************************************************************************/
/*****************************************************************************/
/* */
/* How to call Triangle from another program */
/* */
/* */
/* If you haven't read Triangle's instructions (run "triangle -h" to read */
/* them), you won't understand what follows. */
/* */
/* Triangle must be compiled into an object file (triangle.o) with the */
/* TRILIBRARY symbol defined (generally by using the -DTRILIBRARY compiler */
/* switch). The makefile included with Triangle will do this for you if */
/* you run "make trilibrary". The resulting object file can be called via */
/* the procedure triangulate(). */
/* */
/* If the size of the object file is important to you, you may wish to */
/* generate a reduced version of triangle.o. The REDUCED symbol gets rid */
/* of all features that are primarily of research interest. Specifically, */
/* the -DREDUCED switch eliminates Triangle's -i, -F, -s, and -C switches. */
/* The CDT_ONLY symbol gets rid of all meshing algorithms above and beyond */
/* constrained Delaunay triangulation. Specifically, the -DCDT_ONLY switch */
/* eliminates Triangle's -r, -q, -a, -u, -D, -Y, -S, and -s switches. */
/* */
/* IMPORTANT: These definitions (TRILIBRARY, REDUCED, CDT_ONLY) must be */
/* made in the makefile or in triangle.c itself. Putting these definitions */
/* in this file (triangle.h) will not create the desired effect. */
/* */
/* */
/* The calling convention for triangulate() follows. */
/* */
/* void triangulate(triswitches, in, out, vorout) */
/* char *triswitches; */
/* struct triangulateio *in; */
/* struct triangulateio *out; */
/* struct triangulateio *vorout; */
/* */
/* `triswitches' is a string containing the command line switches you wish */
/* to invoke. No initial dash is required. Some suggestions: */
/* */
/* - You'll probably find it convenient to use the `z' switch so that */
/* points (and other items) are numbered from zero. This simplifies */
/* indexing, because the first item of any type always starts at index */
/* [0] of the corresponding array, whether that item's number is zero or */
/* one. */
/* - You'll probably want to use the `Q' (quiet) switch in your final code, */
/* but you can take advantage of Triangle's printed output (including the */
/* `V' switch) while debugging. */
/* - If you are not using the `q', `a', `u', `D', `j', or `s' switches, */
/* then the output points will be identical to the input points, except */
/* possibly for the boundary markers. If you don't need the boundary */
/* markers, you should use the `N' (no nodes output) switch to save */
/* memory. (If you do need boundary markers, but need to save memory, a */
/* good nasty trick is to set out->pointlist equal to in->pointlist */
/* before calling triangulate(), so that Triangle overwrites the input */
/* points with identical copies.) */
/* - The `I' (no iteration numbers) and `g' (.off file output) switches */
/* have no effect when Triangle is compiled with TRILIBRARY defined. */
/* */
/* `in', `out', and `vorout' are descriptions of the input, the output, */
/* and the Voronoi output. If the `v' (Voronoi output) switch is not used, */
/* `vorout' may be NULL. `in' and `out' may never be NULL. */
/* */
/* Certain fields of the input and output structures must be initialized, */
/* as described below. */
/* */
/*****************************************************************************/
/*****************************************************************************/
/* */
/* The `triangulateio' structure. */
/* */
/* Used to pass data into and out of the triangulate() procedure. */
/* */
/* */
/* Arrays are used to store points, triangles, markers, and so forth. In */
/* all cases, the first item in any array is stored starting at index [0]. */
/* However, that item is item number `1' unless the `z' switch is used, in */
/* which case it is item number `0'. Hence, you may find it easier to */
/* index points (and triangles in the neighbor list) if you use the `z' */
/* switch. Unless, of course, you're calling Triangle from a Fortran */
/* program. */
/* */
/* Description of fields (except the `numberof' fields, which are obvious): */
/* */
/* `pointlist': An array of point coordinates. The first point's x */
/* coordinate is at index [0] and its y coordinate at index [1], followed */
/* by the coordinates of the remaining points. Each point occupies two */
/* REALs. */
/* `pointattributelist': An array of point attributes. Each point's */
/* attributes occupy `numberofpointattributes' REALs. */
/* `pointmarkerlist': An array of point markers; one int per point. */
/* */
/* `trianglelist': An array of triangle corners. The first triangle's */
/* first corner is at index [0], followed by its other two corners in */
/* counterclockwise order, followed by any other nodes if the triangle */
/* represents a nonlinear element. Each triangle occupies */
/* `numberofcorners' ints. */
/* `triangleattributelist': An array of triangle attributes. Each */
/* triangle's attributes occupy `numberoftriangleattributes' REALs. */
/* `trianglearealist': An array of triangle area constraints; one REAL per */
/* triangle. Input only. */
/* `neighborlist': An array of triangle neighbors; three ints per */
/* triangle. Output only. */
/* */
/* `segmentlist': An array of segment endpoints. The first segment's */
/* endpoints are at indices [0] and [1], followed by the remaining */
/* segments. Two ints per segment. */
/* `segmentmarkerlist': An array of segment markers; one int per segment. */
/* */
/* `holelist': An array of holes. The first hole's x and y coordinates */
/* are at indices [0] and [1], followed by the remaining holes. Two */
/* REALs per hole. Input only, although the pointer is copied to the */
/* output structure for your convenience. */
/* */
/* `regionlist': An array of regional attributes and area constraints. */
/* The first constraint's x and y coordinates are at indices [0] and [1], */
/* followed by the regional attribute at index [2], followed by the */
/* maximum area at index [3], followed by the remaining area constraints. */
/* Four REALs per area constraint. Note that each regional attribute is */
/* used only if you select the `A' switch, and each area constraint is */
/* used only if you select the `a' switch (with no number following), but */
/* omitting one of these switches does not change the memory layout. */
/* Input only, although the pointer is copied to the output structure for */
/* your convenience. */
/* */
/* `edgelist': An array of edge endpoints. The first edge's endpoints are */
/* at indices [0] and [1], followed by the remaining edges. Two ints per */
/* edge. Output only. */
/* `edgemarkerlist': An array of edge markers; one int per edge. Output */
/* only. */
/* `normlist': An array of normal vectors, used for infinite rays in */
/* Voronoi diagrams. The first normal vector's x and y magnitudes are */
/* at indices [0] and [1], followed by the remaining vectors. For each */
/* finite edge in a Voronoi diagram, the normal vector written is the */
/* zero vector. Two REALs per edge. Output only. */
/* */
/* */
/* Any input fields that Triangle will examine must be initialized. */
/* Furthermore, for each output array that Triangle will write to, you */
/* must either provide space by setting the appropriate pointer to point */
/* to the space you want the data written to, or you must initialize the */
/* pointer to NULL, which tells Triangle to allocate space for the results. */
/* The latter option is preferable, because Triangle always knows exactly */
/* how much space to allocate. The former option is provided mainly for */
/* people who need to call Triangle from Fortran code, though it also makes */
/* possible some nasty space-saving tricks, like writing the output to the */
/* same arrays as the input. */
/* */
/* Triangle will not free() any input or output arrays, including those it */
/* allocates itself; that's up to you. You should free arrays allocated by */
/* Triangle by calling the trifree() procedure defined below. (By default, */
/* trifree() just calls the standard free() library procedure, but */
/* applications that call triangulate() may replace trimalloc() and */
/* trifree() in triangle.c to use specialized memory allocators.) */
/* */
/* Here's a guide to help you decide which fields you must initialize */
/* before you call triangulate(). */
/* */
/* `in': */
/* */
/* - `pointlist' must always point to a list of points; `numberofpoints' */
/* and `numberofpointattributes' must be properly set. */
/* `pointmarkerlist' must either be set to NULL (in which case all */
/* markers default to zero), or must point to a list of markers. If */
/* `numberofpointattributes' is not zero, `pointattributelist' must */
/* point to a list of point attributes. */
/* - If the `r' switch is used, `trianglelist' must point to a list of */
/* triangles, and `numberoftriangles', `numberofcorners', and */
/* `numberoftriangleattributes' must be properly set. If */
/* `numberoftriangleattributes' is not zero, `triangleattributelist' */
/* must point to a list of triangle attributes. If the `a' switch is */
/* used (with no number following), `trianglearealist' must point to a */
/* list of triangle area constraints. `neighborlist' may be ignored. */
/* - If the `p' switch is used, `segmentlist' must point to a list of */
/* segments, `numberofsegments' must be properly set, and */
/* `segmentmarkerlist' must either be set to NULL (in which case all */
/* markers default to zero), or must point to a list of markers. */
/* - If the `p' switch is used without the `r' switch, then */
/* `numberofholes' and `numberofregions' must be properly set. If */
/* `numberofholes' is not zero, `holelist' must point to a list of */
/* holes. If `numberofregions' is not zero, `regionlist' must point to */
/* a list of region constraints. */
/* - If the `p' switch is used, `holelist', `numberofholes', */
/* `regionlist', and `numberofregions' is copied to `out'. (You can */
/* nonetheless get away with not initializing them if the `r' switch is */
/* used.) */
/* - `edgelist', `edgemarkerlist', `normlist', and `numberofedges' may be */
/* ignored. */
/* */
/* `out': */
/* */
/* - `pointlist' must be initialized (NULL or pointing to memory) unless */
/* the `N' switch is used. `pointmarkerlist' must be initialized */
/* unless the `N' or `B' switch is used. If `N' is not used and */
/* `in->numberofpointattributes' is not zero, `pointattributelist' must */
/* be initialized. */
/* - `trianglelist' must be initialized unless the `E' switch is used. */
/* `neighborlist' must be initialized if the `n' switch is used. If */
/* the `E' switch is not used and (`in->numberofelementattributes' is */
/* not zero or the `A' switch is used), `elementattributelist' must be */
/* initialized. `trianglearealist' may be ignored. */
/* - `segmentlist' must be initialized if the `p' or `c' switch is used, */
/* and the `P' switch is not used. `segmentmarkerlist' must also be */
/* initialized under these circumstances unless the `B' switch is used. */
/* - `edgelist' must be initialized if the `e' switch is used. */
/* `edgemarkerlist' must be initialized if the `e' switch is used and */
/* the `B' switch is not. */
/* - `holelist', `regionlist', `normlist', and all scalars may be ignored.*/
/* */
/* `vorout' (only needed if `v' switch is used): */
/* */
/* - `pointlist' must be initialized. If `in->numberofpointattributes' */
/* is not zero, `pointattributelist' must be initialized. */
/* `pointmarkerlist' may be ignored. */
/* - `edgelist' and `normlist' must both be initialized. */
/* `edgemarkerlist' may be ignored. */
/* - Everything else may be ignored. */
/* */
/* After a call to triangulate(), the valid fields of `out' and `vorout' */
/* will depend, in an obvious way, on the choice of switches used. Note */
/* that when the `p' switch is used, the pointers `holelist' and */
/* `regionlist' are copied from `in' to `out', but no new space is */
/* allocated; be careful that you don't free() the same array twice. On */
/* the other hand, Triangle will never copy the `pointlist' pointer (or any */
/* others); new space is allocated for `out->pointlist', or if the `N' */
/* switch is used, `out->pointlist' remains uninitialized. */
/* */
/* All of the meaningful `numberof' fields will be properly set; for */
/* instance, `numberofedges' will represent the number of edges in the */
/* triangulation whether or not the edges were written. If segments are */
/* not used, `numberofsegments' will indicate the number of boundary edges. */
/* */
/*****************************************************************************/
#ifdef SINGLE
#define REAL float
#else /* not SINGLE */
#define REAL double
#endif /* not SINGLE */
#define VOID void
/* The vertex data structure. Each vertex is actually an array of REALs. */
/* The number of REALs is unknown until runtime. An integer boundary */
/* marker, and sometimes a pointer to a triangle, is appended after the */
/* REALs. */
typedef REAL *vertex;
int triunsuitable(vertex triorg, vertex tridest, vertex triapex, REAL area);
struct triangulateio {
REAL *pointlist; /* In / out */
REAL *pointattributelist; /* In / out */
int *pointmarkerlist; /* In / out */
int numberofpoints; /* In / out */
int numberofpointattributes; /* In / out */
int *trianglelist; /* In / out */
REAL *triangleattributelist; /* In / out */
REAL *trianglearealist; /* In only */
int *neighborlist; /* Out only */
int numberoftriangles; /* In / out */
int numberofcorners; /* In / out */
int numberoftriangleattributes; /* In / out */
int *segmentlist; /* In / out */
int *segmentmarkerlist; /* In / out */
int numberofsegments; /* In / out */
REAL *holelist; /* In / pointer to array copied out */
int numberofholes; /* In / copied out */
REAL *regionlist; /* In / pointer to array copied out */
int numberofregions; /* In / copied out */
int *edgelist; /* Out only */
int *edgemarkerlist; /* Not used with Voronoi diagram; out only */
REAL *normlist; /* Used only with Voronoi diagram; out only */
int numberofedges; /* Out only */
};
#ifdef ANSI_DECLARATORS
void triangulate(char *, struct triangulateio *, struct triangulateio *,
struct triangulateio *);
void trifree(VOID *memptr);
#else /* not ANSI_DECLARATORS */
void triangulate();
void trifree();
#endif /* not ANSI_DECLARATORS */
#ifdef __cplusplus
}
#endif
#endif
================================================
FILE: src/cpp/wrap_tetgen.cpp
================================================
#include "tetgen.h"
#include
#include
#include
#include
#include "foreign_array_wrap.hpp"
namespace py = pybind11;
using namespace std;
namespace
{
struct tMeshInfo : public tetgenio, public noncopyable
{
private:
typedef tetgenio super;
public:
tForeignArray Points; // in/out
tForeignArray PointAttributes; // in/out
tForeignArray PointMetricTensors; // in/out
tForeignArray PointMarkers; // in/out
tForeignArray Elements; // in/out
tForeignArray ElementAttributes; // in/out
tForeignArray ElementVolumes; // out
tForeignArray Neighbors; // out
tForeignArray Facets;
tForeignArray FacetMarkers;
tForeignArray Holes;
tForeignArray Regions;
tForeignArray FacetConstraints;
tForeignArray SegmentConstraints;
tForeignArray Faces;
tForeignArray AdjacentElements;
tForeignArray FaceMarkers;
tForeignArray Edges;
tForeignArray EdgeMarkers;
tForeignArray EdgeAdjTetList;
public:
tMeshInfo()
: Points(pointlist, numberofpoints, 3),
PointAttributes(pointattributelist, numberofpoints, 0, &Points),
PointMetricTensors(pointmtrlist, numberofpoints, 0, &Points),
PointMarkers(pointmarkerlist, numberofpoints, 1, &Points),
Elements(tetrahedronlist, numberoftetrahedra, 0),
ElementAttributes(tetrahedronattributelist,
numberoftetrahedra, 0, &Elements),
ElementVolumes(tetrahedronvolumelist, numberoftetrahedra, 1, &Elements),
Neighbors(neighborlist, numberoftetrahedra, 4, &Elements),
Facets(facetlist, numberoffacets),
FacetMarkers(facetmarkerlist, numberoffacets, 1, &Facets),
Holes(holelist, numberofholes, 3),
Regions(regionlist, numberofregions, 5),
FacetConstraints(facetconstraintlist, numberoffacetconstraints, 2),
SegmentConstraints(segmentconstraintlist, numberofsegmentconstraints, 3),
Faces(trifacelist, numberoftrifaces, 3),
AdjacentElements(adjtetlist, numberoftrifaces, 2, &Faces),
FaceMarkers(trifacemarkerlist, numberoftrifaces, 1, &Faces),
Edges(edgelist, numberofedges, 2),
EdgeMarkers(edgemarkerlist, numberofedges, 1, &Edges),
EdgeAdjTetList(edgeadjtetlist, numberofedges, 1, &Edges)
{
Elements.fixUnit(numberofcorners);
}
unsigned numberOfPointAttributes() const
{
return numberofpointattributes;
}
unsigned numberOfPointMetricTensors() const
{
return numberofpointmtrs;
}
unsigned numberOfElementVertices() const
{
return numberofcorners;
}
unsigned numberOfElementAttributes() const
{
return numberoftetrahedronattributes;
}
void setNumberOfPointAttributes(unsigned attrs)
{
PointAttributes.setUnit(attrs);
numberofpointattributes = attrs;
}
void setNumberOfPointMetricTensors(unsigned mtrs)
{
PointMetricTensors.setUnit(mtrs);
numberofpointmtrs = mtrs;
}
void setNumberOfElementVertices(unsigned verts)
{
Elements.setUnit(verts);
numberofcorners = verts;
}
void setNumberOfElementAttributes(unsigned attrs)
{
ElementAttributes.setUnit(attrs);
numberoftetrahedronattributes = attrs;
}
#define OVERRIDE_LOAD_WITH_ERROR_CHECK(WHAT, POSTPROC) \
void load_##WHAT(char* filename) \
{ \
if (!super::load_##WHAT(filename)) \
throw std::runtime_error("load_" #WHAT " failed"); \
POSTPROC; \
}
OVERRIDE_LOAD_WITH_ERROR_CHECK(node,);
OVERRIDE_LOAD_WITH_ERROR_CHECK(var,);
OVERRIDE_LOAD_WITH_ERROR_CHECK(mtr,);
OVERRIDE_LOAD_WITH_ERROR_CHECK(poly,);
OVERRIDE_LOAD_WITH_ERROR_CHECK(off,);
OVERRIDE_LOAD_WITH_ERROR_CHECK(ply,);
OVERRIDE_LOAD_WITH_ERROR_CHECK(stl,);
OVERRIDE_LOAD_WITH_ERROR_CHECK(vtk,);
void load_plc(char* filename, int object)
{
if (!super::load_plc(filename, object))
throw std::runtime_error("load_plc failed");
}
void load_medit(char* filename, int object)
{
if (!super::load_medit(filename, object))
throw std::runtime_error("load_tetmesh failed");
}
void load_tetmesh(char* filename, int object)
{
if (!super::load_tetmesh(filename, object))
throw std::runtime_error("load_tetmesh failed");
Elements.fixUnit(numberofcorners);
}
/*
tTriangulationParameters &operator=(const tTriangulationParameters &src)
{
numberofpointattributes = src.numberofpointattributes ;
numberofcorners = src.numberofcorners;
numberoftriangleattributes = src.numberoftriangleattributes;
Points = src.Points;
PointAttributes = src.PointAttributes;
PointMarkers = src.PointMarkers;
Triangles = src.Triangles;
TriangleAttributes = src.TriangleAttributes;
TriangleAreas = src.TriangleAreas;
Neighbors = src.Neighbors;
Segments = src.Segments;
SegmentMarkers = src.SegmentMarkers;
Holes = src.Holes;
Regions = src.Regions;
Edges = src.Edges;
EdgeMarkers = src.EdgeMarkers;
Normals = src.Normals;
return *this;
}
*/
};
/*
tTriangulationParameters *copyTriangulationParameters(const tTriangulationParameters &src)
{
auto_ptr copy(new tTriangulationParameters);
*copy = src;
return copy.release();
}
*/
void tetrahedralizeWrapper(tetgenbehavior &bhv, tMeshInfo &in, tMeshInfo &out,
tMeshInfo *addin)
{
try
{
tetrahedralize(&bhv, &in, &out, addin);
}
catch (int &i)
{
throw runtime_error("TetGen runtime error code "+std::to_string(i));
}
out.Elements.fixUnit(out.numberofcorners);
out.PointAttributes.fixUnit(out.numberofpointattributes);
out.PointMetricTensors.fixUnit(out.numberofpointmtrs);
out.ElementAttributes.fixUnit(out.numberoftetrahedronattributes);
}
tForeignArray *facet_get_polygons(tetgenio::facet &self)
{
return new tForeignArray(
self.polygonlist, self.numberofpolygons);
}
tForeignArray *facet_get_holes(tetgenio::facet &self)
{
return new tForeignArray(self.holelist, self.numberofholes, 3);
}
tForeignArray *polygon_get_vertices(tetgenio::polygon &self)
{
return new tForeignArray(self.vertexlist, self.numberofvertices);
}
}
#define DEF_RW_MEMBER(NAME) \
def_readwrite(#NAME, &cl::NAME)
#define DEF_METHOD(NAME) \
def(#NAME, &cl::NAME)
void expose_tetgen(pybind11::module &m)
{
m.def("tetrahedralize", tetrahedralizeWrapper,
py::arg("behavior"), py::arg("in"), py::arg("out"),
py::arg("addin").none(true)=py::none());
{
typedef tMeshInfo cl;
py::class_(m, "TetMeshInfo")
.def(py::init<>())
.def_readonly("points", &cl::Points)
.def_readonly("point_attributes", &cl::PointAttributes)
.def_readonly("point_metric_tensors", &cl::PointMetricTensors)
.def_readonly("point_markers", &cl::PointMarkers)
.def_readonly("elements", &cl::Elements)
.def_readonly("element_attributes", &cl::ElementAttributes)
.def_readonly("element_volumes", &cl::ElementVolumes)
.def_readonly("neighbors", &cl::Neighbors)
.def_readonly("facets", &cl::Facets)
.def_readonly("facet_markers", &cl::FacetMarkers)
.def_readonly("holes", &cl::Holes)
.def_readonly("regions", &cl::Regions)
.def_readonly("facet_constraints", &cl::FacetConstraints)
.def_readonly("segment_constraints", &cl::SegmentConstraints)
.def_readonly("faces", &cl::Faces)
.def_readonly("adjacent_elements", &cl::AdjacentElements)
.def_readonly("face_markers", &cl::FaceMarkers)
.def_readonly("edges", &cl::Edges)
.def_readonly("edge_markers", &cl::EdgeMarkers)
.def_readonly("edge_adjacent_elements", &cl::EdgeAdjTetList)
.def_property("number_of_point_attributes",
&cl::numberOfPointAttributes,
&cl::setNumberOfPointAttributes)
.def_property("number_of_element_vertices",
&cl::numberOfElementVertices,
&cl::setNumberOfElementVertices)
.def_property("number_of_element_attributes",
&cl::numberOfElementAttributes,
&cl::setNumberOfElementAttributes)
.DEF_METHOD(save_nodes)
.DEF_METHOD(save_elements)
.DEF_METHOD(save_faces)
.DEF_METHOD(save_edges)
.DEF_METHOD(save_neighbors)
.DEF_METHOD(save_poly)
.DEF_METHOD(load_node)
.DEF_METHOD(load_var)
.DEF_METHOD(load_mtr)
.DEF_METHOD(load_poly)
.DEF_METHOD(load_off)
.DEF_METHOD(load_ply)
.DEF_METHOD(load_stl)
.DEF_METHOD(load_medit)
.DEF_METHOD(load_plc)
.DEF_METHOD(load_tetmesh)
/*
.def("copy", ©TriangulationParameters,
return_value_policy())
*/
//.enable_pickling()
;
}
{
typedef tetgenio::facet cl;
py::class_(m, "Facet")
.def_property_readonly("polygons",
facet_get_polygons, py::return_value_policy::reference_internal)
.def_property_readonly("holes",
facet_get_holes, py::return_value_policy::reference_internal)
;
}
{
typedef tetgenio::polygon cl;
py::class_(m, "Polygon")
.def_property_readonly("vertices",
polygon_get_vertices, py::return_value_policy::reference_internal)
;
}
{
typedef tetgenbehavior cl;
py::class_(m, "Options")
.def(py::init<>())
.DEF_RW_MEMBER(plc)
.DEF_RW_MEMBER(psc)
.DEF_RW_MEMBER(refine)
.DEF_RW_MEMBER(quality)
.DEF_RW_MEMBER(nobisect)
.DEF_RW_MEMBER(coarsen)
.DEF_RW_MEMBER(weighted)
.DEF_RW_MEMBER(brio_hilbert)
.DEF_RW_MEMBER(incrflip)
.DEF_RW_MEMBER(flipinsert)
.DEF_RW_MEMBER(metric)
.DEF_RW_MEMBER(varvolume)
.DEF_RW_MEMBER(fixedvolume)
.DEF_RW_MEMBER(regionattrib)
.DEF_RW_MEMBER(conforming)
.DEF_RW_MEMBER(insertaddpoints)
.DEF_RW_MEMBER(diagnose)
.DEF_RW_MEMBER(convex)
.DEF_RW_MEMBER(nomergefacet)
.DEF_RW_MEMBER(nomergevertex)
.DEF_RW_MEMBER(noexact)
.DEF_RW_MEMBER(nostaticfilter)
.DEF_RW_MEMBER(zeroindex)
.DEF_RW_MEMBER(facesout)
.DEF_RW_MEMBER(edgesout)
.DEF_RW_MEMBER(neighout)
.DEF_RW_MEMBER(voroout)
.DEF_RW_MEMBER(meditview)
.DEF_RW_MEMBER(vtkview)
.DEF_RW_MEMBER(nobound)
.DEF_RW_MEMBER(nonodewritten)
.DEF_RW_MEMBER(noelewritten)
.DEF_RW_MEMBER(nofacewritten)
.DEF_RW_MEMBER(noiterationnum)
.DEF_RW_MEMBER(nojettison)
.DEF_RW_MEMBER(reversetetori)
.DEF_RW_MEMBER(docheck)
.DEF_RW_MEMBER(quiet)
.DEF_RW_MEMBER(verbose)
.DEF_RW_MEMBER(vertexperblock)
.DEF_RW_MEMBER(tetrahedraperblock)
.DEF_RW_MEMBER(shellfaceperblock)
.DEF_RW_MEMBER(nobisect_param)
.DEF_RW_MEMBER(addsteiner_algo)
.DEF_RW_MEMBER(coarsen_param)
.DEF_RW_MEMBER(weighted_param)
.DEF_RW_MEMBER(fliplinklevel)
.DEF_RW_MEMBER(flipstarsize)
.DEF_RW_MEMBER(fliplinklevelinc)
.DEF_RW_MEMBER(reflevel)
.DEF_RW_MEMBER(optlevel)
.DEF_RW_MEMBER(optscheme)
.DEF_RW_MEMBER(delmaxfliplevel)
.DEF_RW_MEMBER(order)
.DEF_RW_MEMBER(steinerleft)
.DEF_RW_MEMBER(no_sort)
.DEF_RW_MEMBER(hilbert_order)
.DEF_RW_MEMBER(hilbert_limit)
.DEF_RW_MEMBER(brio_threshold)
.DEF_RW_MEMBER(brio_ratio)
.DEF_RW_MEMBER(facet_ang_tol)
.DEF_RW_MEMBER(maxvolume)
.DEF_RW_MEMBER(minratio)
.DEF_RW_MEMBER(mindihedral)
.DEF_RW_MEMBER(optmaxdihedral)
.DEF_RW_MEMBER(optminsmtdihed)
.DEF_RW_MEMBER(optminslidihed)
.DEF_RW_MEMBER(epsilon)
.DEF_RW_MEMBER(minedgelength)
.DEF_RW_MEMBER(coarsen_percent)
.def("parse_switches", (bool (tetgenbehavior::*)(char *)) &cl::parse_commandline)
;
}
exposeStructureForeignArray(m, "FacetArray");
exposeStructureForeignArray(m, "PolygonArray");
}
================================================
FILE: src/cpp/wrap_triangle.cpp
================================================
#include "triangle.h"
#include
#include
#include
#include
#include "foreign_array_wrap.hpp"
namespace py = pybind11;
using namespace std;
struct tMeshInfo : public triangulateio, public noncopyable
{
public:
tForeignArray Points; // in/out
tForeignArray PointAttributes; // in/out
tForeignArray PointMarkers; // in/out
tForeignArray Elements; // in/out
tForeignArray ElementAttributes; // in/out
tForeignArray ElementVolumes; // in only
tForeignArray Neighbors; // out only
tForeignArray Facets; // in/out
tForeignArray FacetMarkers; // in/out
tForeignArray Holes; // in only
tForeignArray Regions; // in only
tForeignArray Faces; // out only
tForeignArray FaceMarkers; // out only
tForeignArray Normals; // out only
public:
tMeshInfo()
: Points(pointlist, numberofpoints, 2, NULL, true),
PointAttributes(pointattributelist, numberofpoints, 0, &Points, true),
PointMarkers(pointmarkerlist, numberofpoints, 1, &Points, true),
Elements(trianglelist, numberoftriangles, 3, NULL, true),
ElementAttributes(triangleattributelist,
numberoftriangles, 0, &Elements, true),
ElementVolumes(trianglearealist,
numberoftriangles, 1, &Elements, true),
Neighbors(neighborlist,
numberoftriangles, 3, &Elements, true),
Facets(segmentlist, numberofsegments, 2, NULL, true),
FacetMarkers(segmentmarkerlist, numberofsegments, 1, &Facets, true),
Holes(holelist, numberofholes, 2, NULL, true),
Regions(regionlist, numberofregions, 4, NULL, true),
Faces(edgelist, numberofedges, 2, NULL, true),
FaceMarkers(edgemarkerlist, numberofedges, 1, &Faces, true),
Normals(normlist, numberofedges, 2, &Faces, true)
{
numberofpointattributes = 0;
numberofcorners = 3;
numberoftriangleattributes = 0;
}
unsigned numberOfPointAttributes() const
{
return numberofpointattributes;
}
unsigned numberOfElementAttributes() const
{
return numberoftriangleattributes;
}
void setNumberOfPointAttributes(unsigned attrs)
{
PointAttributes.setUnit(attrs);
numberofpointattributes = attrs;
}
void setNumberOfElementAttributes(unsigned attrs)
{
ElementAttributes.setUnit(attrs);
numberoftriangleattributes = attrs;
}
tMeshInfo &operator=(const tMeshInfo &src)
{
numberofpointattributes = src.numberofpointattributes ;
numberofcorners = src.numberofcorners;
numberoftriangleattributes = src.numberoftriangleattributes;
Points = src.Points;
PointAttributes = src.PointAttributes;
PointMarkers = src.PointMarkers;
Elements = src.Elements;
ElementAttributes = src.ElementAttributes;
ElementVolumes = src.ElementVolumes;
Neighbors = src.Neighbors;
Facets = src.Facets;
FacetMarkers = src.FacetMarkers;
Holes = src.Holes;
Regions = src.Regions;
Faces = src.Faces;
FaceMarkers = src.FaceMarkers;
Normals = src.Normals;
return *this;
}
};
tMeshInfo *copyMesh(const tMeshInfo &src)
{
std::unique_ptr copy(new tMeshInfo);
*copy = src;
return copy.release();
}
PyObject *RefinementFunction;
class tVertex : public noncopyable
{
public:
REAL *Data;
public:
tVertex(REAL *data)
: Data(data)
{
}
REAL operator[](unsigned i)
{
if (i >= 2)
PYTHON_ERROR(IndexError, "vertex index out of bounds");
return Data[i];
}
unsigned size()
{
return 2;
}
REAL x() { return Data[0]; }
REAL y() { return Data[1]; }
};
int triunsuitable(vertex triorg, vertex tridest, vertex triapex, REAL area)
{
// return 1 if triangle is too large, 0 otherwise
tVertex org(triorg);
tVertex dest(tridest);
tVertex apex(triapex);
py::handle refine_func = py::reinterpret_borrow(RefinementFunction);
try
{
return py::cast(refine_func(
py::make_tuple(
py::cast(org, py::return_value_policy::reference),
py::cast(dest, py::return_value_policy::reference),
py::cast(apex, py::return_value_policy::reference)), area));
}
catch (py::error_already_set &)
{
std::cout << "[MeshPy warning] A Python exception occurred in "
"a Python refinement query:" << std::endl;
PyErr_Print();
std::cout << "[MeshPy] Aborting now." << std::endl;
abort();
}
catch (std::exception &e)
{
std::cout << "[MeshPy warning] An exception occurred in "
"a Python refinement query:" << std::endl
<< e.what() << std::endl;
std::cout << "[MeshPy] Aborting now." << std::endl;
abort();
}
}
void triangulateWrapper(char *options, tMeshInfo &in,
tMeshInfo &out,
tMeshInfo &voronoi,
py::object refinement_func)
{
RefinementFunction = refinement_func.ptr();
triangulate(options, &in, &out, &voronoi);
out.holelist = NULL;
out.numberofholes = 0;
out.regionlist = NULL;
out.numberofregions = 0;
out.Elements.fixUnit(out.numberofcorners);
out.PointAttributes.fixUnit(out.numberofpointattributes);
out.ElementAttributes.fixUnit(out.numberoftriangleattributes);
}
void expose_triangle(pybind11::module &m)
{
m.def("triangulate", triangulateWrapper);
{
typedef tMeshInfo cl;
py::class_(m, "TriMeshInfo")
.def(py::init<>())
.def_readonly("points", &cl::Points)
.def_readonly("point_attributes", &cl::PointAttributes)
.def_readonly("point_markers", &cl::PointMarkers)
.def_readonly("elements", &cl::Elements)
.def_readonly("element_attributes", &cl::ElementAttributes)
.def_readonly("element_volumes", &cl::ElementVolumes)
.def_readonly("neighbors", &cl::Neighbors)
.def_readonly("facets", &cl::Facets)
.def_readonly("facet_markers", &cl::FacetMarkers)
.def_readonly("holes", &cl::Holes)
.def_readonly("regions", &cl::Regions)
.def_readonly("faces", &cl::Faces)
.def_readonly("face_markers", &cl::FaceMarkers)
.def_readonly("normals", &cl::Normals)
.def_property("number_of_point_attributes",
&cl::numberOfPointAttributes,
&cl::setNumberOfPointAttributes)
.def_property("number_of_element_attributes",
&cl::numberOfElementAttributes,
&cl::setNumberOfElementAttributes)
.def("copy", ©Mesh)
// .enable_pickling()
;
}
{
typedef tVertex cl;
py::class_(m, "Vertex")
.def_property_readonly("x", &cl::x)
.def_property_readonly("y", &cl::y)
.def("__len__", &cl::size)
.def("__getitem__", &cl::operator[])
;
}
}
================================================
FILE: src/cpp/wrapper.cpp
================================================
#include
#include "foreign_array_wrap.hpp"
void expose_triangle(pybind11::module &m);
void expose_tetgen(pybind11::module &m);
PYBIND11_MODULE(_internals, m)
{
exposePODForeignArray(m, "RealArray");
exposePODForeignArray(m, "IntArray");
expose_triangle(m);
expose_tetgen(m);
}
================================================
FILE: test/test_meshpy.py
================================================
__copyright__ = "Copyright (C) 2013 Andreas Kloeckner"
__license__ = """
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
"""
import math
# {{{ triangle
def test_triangle_refine():
from meshpy import triangle
segments = 50
points = [(1, 0), (1, 1), (-1, 1), (-1, -1), (1, -1)]
n_outer_points = len(points)
for i in range(segments):
angle = i * 2 * math.pi / segments
points.append((0.5 * math.cos(angle), 0.5 * math.sin(angle)))
def round_trip_connect(start, end):
result = [(i, i + 1) for i in range(start, end)]
result.append((end, start))
return result
def needs_refinement(vertices, area):
vert_origin, vert_destination, vert_apex = vertices
bary_x = (vert_origin.x + vert_destination.x + vert_apex.x) / 3
bary_y = (vert_origin.y + vert_destination.y + vert_apex.y) / 3
dist_center = math.sqrt(bary_x**2 + bary_y**2)
max_area = 100*(math.fabs(0.002 * (dist_center-0.3)) + 0.0001)
return area > max_area
info = triangle.MeshInfo()
info.set_points(points)
info.set_holes([(0, 0)])
info.set_facets(
round_trip_connect(0, n_outer_points-1)
+ round_trip_connect(n_outer_points, len(points)-1))
mesh = triangle.build(info, refinement_func=needs_refinement)
triangle.write_gnuplot_mesh("triangles-unrefined.dat", mesh)
mesh.element_volumes.setup()
for i in range(len(mesh.elements)):
mesh.element_volumes[i] = -1
for i in range(0, len(mesh.elements), 10):
mesh.element_volumes[i] = 1e-8
mesh = triangle.refine(mesh)
def test_point_attributes():
from meshpy import triangle
points = [(1, 1), (-1, 1), (-1, -1), (1, -1)]
info = triangle.MeshInfo()
info.set_points(points)
info.number_of_point_attributes = 2
info.point_attributes.setup()
for i in range(len(points)):
info.point_attributes[i] = [0, 0]
triangle.build(info)
# }}}
# {{{ tetgen
def test_tetgen():
from meshpy.tet import MeshInfo, build
mesh_info = MeshInfo()
mesh_info.set_points([
(0, 0, 0),
(2, 0, 0),
(2, 2, 0),
(0, 2, 0),
(0, 0, 12),
(2, 0, 12),
(2, 2, 12),
(0, 2, 12),
])
mesh_info.set_facets([
[0, 1, 2, 3],
[4, 5, 6, 7],
[0, 4, 5, 1],
[1, 5, 6, 2],
[2, 6, 7, 3],
[3, 7, 4, 0],
])
build(mesh_info)
def test_torus():
from math import cos, pi, sin
from meshpy.geometry import (
EXT_CLOSED_IN_RZ,
GeometryBuilder,
generate_surface_of_revolution,
)
from meshpy.tet import MeshInfo, build
big_r = 3
little_r = 2.9
points = 50
dphi = 2*pi/points
rz = [(big_r+little_r*cos(i*dphi), little_r*sin(i*dphi))
for i in range(points)]
geob = GeometryBuilder()
geob.add_geometry(*generate_surface_of_revolution(rz,
closure=EXT_CLOSED_IN_RZ, radial_subdiv=20))
mesh_info = MeshInfo()
geob.set(mesh_info)
build(mesh_info)
def test_tetgen_points():
import numpy as np
from meshpy.tet import MeshInfo, Options, build
rng = np.random.default_rng(seed=42)
points = rng.normal(size=(10000, 3))
mesh_info = MeshInfo()
mesh_info.set_points(points)
options = Options("")
mesh = build(mesh_info, options=options)
print(len(mesh.points))
print(len(mesh.elements))
#mesh.write_vtk("test.vtk")
# }}}
if __name__ == "__main__":
import sys
if len(sys.argv) > 1:
exec(sys.argv[1])
else:
from pytest import main
main([__file__])
# vim: foldmethod=marker