Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/extern
diff options
context:
space:
mode:
authorover0219 <over0219@umn.edu>2020-06-08 22:38:27 +0300
committerover0219 <over0219@umn.edu>2020-06-08 22:38:27 +0300
commitb86af3ecb70dd2f38e1f7c84af15a0b82662d81d (patch)
treeeab99c4c4cbb4874469b2df592db79041ce30ff6 /extern
parent9ceb298156044e616bcea97b3c82f1c416ec4385 (diff)
added tetgen
Diffstat (limited to 'extern')
-rw-r--r--extern/CMakeLists.txt4
-rw-r--r--extern/tetgen/CMakeLists.txt36
-rw-r--r--extern/tetgen/LICENSE666
-rw-r--r--extern/tetgen/README25
-rw-r--r--extern/tetgen/makefile62
-rw-r--r--extern/tetgen/predicates.cxx4706
-rw-r--r--extern/tetgen/tetgen.cxx31250
-rw-r--r--extern/tetgen/tetgen.h3340
8 files changed, 40089 insertions, 0 deletions
diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt
index 7d4023cf52a..78dc955a315 100644
--- a/extern/CMakeLists.txt
+++ b/extern/CMakeLists.txt
@@ -107,6 +107,10 @@ if(WITH_QUADRIFLOW)
add_subdirectory(quadriflow)
endif()
+if(WITH_TETGEN)
+ add_subdirectory(tetgen)
+endif()
+
if(WITH_MOD_FLUID)
add_subdirectory(mantaflow)
endif()
diff --git a/extern/tetgen/CMakeLists.txt b/extern/tetgen/CMakeLists.txt
new file mode 100644
index 00000000000..4f372790419
--- /dev/null
+++ b/extern/tetgen/CMakeLists.txt
@@ -0,0 +1,36 @@
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# The Original Code is Copyright (C) 2006, Blender Foundation
+# All rights reserved.
+# ***** END GPL LICENSE BLOCK *****
+
+set(INC
+ .
+)
+
+set(INC_SYS
+)
+
+set(SRC
+ tetgen.cxx
+ predicates.cxx
+)
+
+set(LIB
+)
+
+blender_add_lib(extern_tetgen "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") \ No newline at end of file
diff --git a/extern/tetgen/LICENSE b/extern/tetgen/LICENSE
new file mode 100644
index 00000000000..e253c3d965d
--- /dev/null
+++ b/extern/tetgen/LICENSE
@@ -0,0 +1,666 @@
+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. <http://fsf.org/>
+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.
+
+ <one line to give the program's name and a brief idea of what it
+ does.> Copyright (C) <year> <name of author>
+
+ 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
+ <http://www.gnu.org/licenses/>. 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 <http://www.gnu.org/licenses/>. \ No newline at end of file
diff --git a/extern/tetgen/README b/extern/tetgen/README
new file mode 100644
index 00000000000..bc5cfa04c0e
--- /dev/null
+++ b/extern/tetgen/README
@@ -0,0 +1,25 @@
+This is TetGen version 1.5 (released on November 4, 2013)
+
+Please see the documentation of TetGen for compiling and using TetGen.
+It is available at the following link:
+
+ http://www.tetgen.org
+
+For more information on this product, contact :
+
+ Hang Si
+ Research Group of Numerical Mathematics and Scientific Computing
+ Weierstrass Institute for Applied Analysis and Stochastics
+ Mohrenstr. 39
+ 10117 Berlin, Germany
+
+ Phone: +49 (0) 30-20372-446 Fax: +49 (0) 30-2044975
+ EMail: <si@wias-berlin.de>
+ Web Site: http://www.wias-berlin.de/~si
+
+------------------- IMPORTANCE NOTICE -----------------------------
+
+BEFORE INTALLING OR USING TetGen(R) READ the
+GENERAL LICENSE TERMS AND CONDITIONS
+
+------------------------------------------------------------------- \ No newline at end of file
diff --git a/extern/tetgen/makefile b/extern/tetgen/makefile
new file mode 100644
index 00000000000..b3b131cfef2
--- /dev/null
+++ b/extern/tetgen/makefile
@@ -0,0 +1,62 @@
+###############################################################################
+# #
+# makefile for TetGen #
+# #
+# Type "make" to compile TetGen into an executable program (tetgen). #
+# Type "make tetlib" to compile TetGen into a library (libtet.a). #
+# Type "make distclean" to delete all object (*.o) files. #
+# #
+###############################################################################
+
+# CXX should be set to the name of your favorite C++ compiler.
+# ===========================================================
+
+CXX = g++
+#CXX = icpc
+#CXX = CC
+
+# CXXFLAGS is the level of optimiztion, default is -O. One should try
+# -O2, -O3 ... to find the best optimization level.
+# ===================================================================
+
+CXXFLAGS = -O3
+
+# PREDCXXFLAGS is for compiling J. Shewchuk's predicates.
+
+PREDCXXFLAGS = -O0
+
+# SWITCHES is a list of switches to compile TetGen.
+# =================================================
+#
+# By default, TetGen uses double precision floating point numbers. If you
+# prefer single precision, use the -DSINGLE switch.
+#
+# The source code of TetGen includes a lot of assertions, which are mainly
+# used for catching bugs at that places. These assertions somewhat slow
+# down the speed of TetGen. They can be skipped by define the -DNDEBUG
+# switch.
+
+SWITCHES =
+
+# RM should be set to the name of your favorite rm (file deletion program).
+
+RM = /bin/rm
+
+# The action starts here.
+
+tetgen: tetgen.cxx predicates.o
+ $(CXX) $(CXXFLAGS) $(SWITCHES) -o tetgen tetgen.cxx predicates.o -lm
+
+tetlib: tetgen.cxx predicates.o
+ $(CXX) $(CXXFLAGS) $(SWITCHES) -DTETLIBRARY -c tetgen.cxx
+ ar r libtet.a tetgen.o predicates.o
+
+predicates.o: predicates.cxx
+ $(CXX) $(PREDCXXFLAGS) -c predicates.cxx
+
+clean:
+ $(RM) *.o *.a tetgen *~
+
+
+
+
diff --git a/extern/tetgen/predicates.cxx b/extern/tetgen/predicates.cxx
new file mode 100644
index 00000000000..33817d7999c
--- /dev/null
+++ b/extern/tetgen/predicates.cxx
@@ -0,0 +1,4706 @@
+/*****************************************************************************/
+/* */
+/* 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 <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#ifdef CPU86
+#include <float.h>
+#endif /* CPU86 */
+#ifdef LINUX
+#include <fpu_control.h>
+#endif /* LINUX */
+
+#include "tetgen.h" // Defines the symbol REAL (float or double).
+
+#ifdef USE_CGAL_PREDICATES
+ #include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
+ 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 // <float.h>
+ (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 // <float.h>
+ (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. */
+/* */
+/*****************************************************************************/
+
+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
+#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);
+
+}
+
+/*****************************************************************************/
+/* */
+/* 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);
+}
+
+
+
diff --git a/extern/tetgen/tetgen.cxx b/extern/tetgen/tetgen.cxx
new file mode 100644
index 00000000000..0449b2096fd
--- /dev/null
+++ b/extern/tetgen/tetgen.cxx
@@ -0,0 +1,31250 @@
+///////////////////////////////////////////////////////////////////////////////
+// //
+// 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"
+
+//// io_cxx ///////////////////////////////////////////////////////////////////
+//// ////
+//// ////
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// load_node_call() Read a list of points from a file. //
+// //
+// 'infile' is the file handle contains the node list. It may point to a //
+// .node, or .poly or .smesh file. 'markers' indicates each node contains an //
+// additional marker (integer) or not. 'uvflag' indicates each node contains //
+// u,v coordinates or not. It is reuqired by a PSC. 'infilename' is the name //
+// of the file being read, it is only used in error messages. //
+// //
+// The 'firstnumber' (0 or 1) is automatically determined by the number of //
+// the first index of the first point. //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+bool tetgenio::load_node_call(FILE* infile, int markers, int uvflag,
+ char* infilename)
+{
+ 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")) {
+ size_t frr = fread((char*)(&(coord[0])), sizeof(double), 1, fp);
+ frr = fread((char*)(&(coord[1])), sizeof(double), 1, fp);
+ frr = fread((char*)(&(coord[2])), sizeof(double), 1, fp);
+TETGEN_UNUSED(frr);
+ 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")) {
+ size_t frr = fread((char*)(&_x), sizeof(float), 1, fp);
+ frr = fread((char*)(&_y), sizeof(float), 1, fp);
+ frr = fread((char*)(&_z), sizeof(float), 1, fp);
+TETGEN_UNUSED(frr);
+ 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++){
+ size_t frr = 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){
+ frr = fread((char*)(&id1), sizeof(int), 1, fp);
+ frr = fread((char*)(&id2), sizeof(int), 1, fp);
+ frr = 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;
+ }
+TETGEN_UNUSED(frr);
+ }
+ } 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)
+{
+TETGEN_UNUSED(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 <si@wias-berlin.de>\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<<axis)) == 0) ? 1 : -1;
+
+
+ // Partition the vertices into left- and right-arrays such that left points
+ // have Hilbert indices lower than the right points.
+ i = 0;
+ j = arraysize - 1;
+
+ // Partition the vertices into left- and right-arrays.
+ if (d > 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)<b->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)
+{
+TETGEN_UNUSED(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--;
+TETGEN_UNUSED(success);
+ 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);
+TETGEN_UNUSED(rrp);
+TETGEN_UNUSED(encpt1);
+TETGEN_UNUSED(encpt2);
+
+ 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; id<NN && pointloop != (point) NULL; id++){
+ x = pointloop[0];
+ y = pointloop[1];
+ z = pointloop[2];
+ fprintf(outfile, "%.17g %.17g %.17g\n", x, y, z);
+ pointloop = pointtraverse();
+ }
+ fprintf(outfile, "\n");
+
+ fprintf(outfile, "CELLS %d %d\n", NEL, NEL*(4+1));
+ //NEL rows, each has 1 type id + 4 node id's
+
+ 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];
+ 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<NEL; tid++){
+ fprintf(outfile, "%d\n", celltype);
+ }
+ fprintf(outfile, "\n");
+
+ if (numelemattrib > 0) {
+ // Output tetrahedra region attributes.
+ fprintf(outfile, "CELL_DATA %d\n", NEL);
+ fprintf(outfile, "SCALARS cell_scalars int 1\n");
+ fprintf(outfile, "LOOKUP_TABLE default\n");
+ 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();
+ }
+}
+
+#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 /////////////////////////////////////////////////////////////////
+
diff --git a/extern/tetgen/tetgen.h b/extern/tetgen/tetgen.h
new file mode 100644
index 00000000000..170f58941da
--- /dev/null
+++ b/extern/tetgen/tetgen.h
@@ -0,0 +1,3340 @@
+///////////////////////////////////////////////////////////////////////////////
+// //
+// 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
+
+#ifndef TETGEN_UNUSED
+#define TETGEN_UNUSED(x) (void)(x)
+#endif
+
+#define TETLIBRARY
+
+// 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <time.h>
+#include <assert.h>
+
+// 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 <stdint.h> 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 <stdint.h>
+#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.
+ typedef struct {
+ int *vertexlist;
+ int numberofvertices;
+ } 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).
+ typedef struct {
+ polygon *polygonlist;
+ int numberofpolygons;
+ REAL *holelist;
+ int numberofholes;
+ } 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) {
+ facet *f;
+ polygon *p;
+ 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;
+ }
+
+ 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. //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+void exactinit(int, int, int, REAL, REAL, REAL);
+REAL orient3d(REAL *pa, REAL *pb, REAL *pc, REAL *pd);
+REAL insphere(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe);
+REAL orient4d(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe,
+ REAL ah, REAL bh, REAL ch, REAL dh, REAL eh);
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// tetgenmesh //
+// //
+// A structure for creating and updating tetrahedral meshes. //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+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
+