diff --git a/contrib/Tetgen1.5/LICENSE b/contrib/Tetgen1.5/LICENSE index f291c306fb843c223b0ccc95f19d968806802735..e253c3d965ddda3797c1ce80fedde3c84c54ecd4 100644 --- a/contrib/Tetgen1.5/LICENSE +++ b/contrib/Tetgen1.5/LICENSE @@ -1,73 +1,666 @@ TetGen License -------------- -The software (TetGen) is licensed under the terms of the MIT license -with the following exceptions: +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. -Distribution of modified versions of this code is permissible UNDER -THE CONDITION THAT THIS CODE AND ANY MODIFICATIONS MADE TO IT IN THE -SAME SOURCE FILES tetgen.h AND tetgen.cxx REMAIN UNDER COPYRIGHT OF -THE ORIGINAL AUTHOR, BOTH SOURCE AND OBJECT CODE ARE MADE FREELY -AVAILABLE WITHOUT CHARGE, AND CLEAR NOTICE IS GIVEN OF THE -MODIFICATIONS. +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. -Distribution of this code for any commercial purpose is permissible -ONLY BY DIRECT ARRANGEMENT WITH THE COPYRIGHT OWNER. +===================================================================== +GNU AFFERO GENERAL PUBLIC LICENSE -The full license text is reproduced below. +Version 3, 19 November 2007 -This means that TetGen is no free software, but for private, research, -and educational purposes it can be used at absolutely no cost and -without further arrangements. +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 -For details, see http://tetgen.org +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. -TetGen -A Quality Tetrahedral Mesh Generator and 3D Delaunay Triangulator -Version 1.5 (Released on February 21, 2012). +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. -Copyright 2002 -- 2012 +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. -Hang Si -Research Group of Numerical Mathematics and Scientific Computing -Weierstrass Institute for Applied Analysis and Stochastics -Mohrenstr. 39 -10117 Berlin, Germany +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. -Phone: +49 (0) 30-20372-446 Fax: +49 (0) 30-2044975 -EMail: <si@wias-berlin.de> -Web Site: http://www.wias-berlin.de/~si +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. -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: +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. -Distribution of modified versions of this code is permissible UNDER -THE CONDITION THAT THIS CODE AND ANY MODIFICATIONS MADE TO IT IN THE -SAME SOURCE FILES tetgen.h AND tetgen.cxx REMAIN UNDER COPYRIGHT OF -THE ORIGINAL AUTHOR, BOTH SOURCE AND OBJECT CODE ARE MADE FREELY -AVAILABLE WITHOUT CHARGE, AND CLEAR NOTICE IS GIVEN OF THE -MODIFICATIONS. +The precise terms and conditions for copying, distribution and +modification follow. -Distribution of this code for any commercial purpose is permissible -ONLY BY DIRECT ARRANGEMENT WITH THE COPYRIGHT OWNER. +TERMS AND CONDITIONS -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. +0. Definitions. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +"This License" refers to version 3 of the GNU Affero General Public +License. -============================================================================== \ No newline at end of file +"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/contrib/Tetgen1.5/README b/contrib/Tetgen1.5/README index 34b6f2372c8655310babf0ef24207086cd80d5cb..bc5cfa04c0e901cb5c5890b9783a781fa31eda1a 100644 --- a/contrib/Tetgen1.5/README +++ b/contrib/Tetgen1.5/README @@ -1,4 +1,4 @@ -This is TetGen version 1.5 (released on February 21, 2012) +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: diff --git a/contrib/Tetgen1.5/example.poly b/contrib/Tetgen1.5/example.poly index 9394d8dbbbca0d27498645e0926ee1737be161f7..e924956668a3402fd15bfb21f236c4161963d152 100644 --- a/contrib/Tetgen1.5/example.poly +++ b/contrib/Tetgen1.5/example.poly @@ -1,139 +1,84 @@ -# -# example.poly - Sample file of TetGen. -# -# A .poly file describes a piecewise linear complex (PLC) -# This file represents a compensated magic tee junction. -# -# The source file is from: Vali Catina <catina@uni-bremen.de> -# - -# Part 1 - node list -56 3 0 0 - 1 -13.716000000000001 -5.0800000000000001 0 - 2 -13.716000000000001 5.0800000000000001 0 - 3 -11.43 5.0800000000000001 0 - 4 11.43 -5.0800000000000001 0 - 5 11.43 7.3659999999999997 0 - 6 -11.43 7.3659999999999997 0 - 7 0.95105651629515364 -0.37999999999999989 0 - 8 -0.95105651629515364 -0.37999999999999989 0 - 9 -0.95105651629515364 4.6200000000000001 0 - 10 0.95105651629515364 4.6200000000000001 0 - 11 -0.95105651629515353 4.6200000000000001 -0.30901699437494756 - 12 -0.58778525229247303 4.6200000000000001 -0.80901699437494756 - 13 1.2246063538223773e-16 4.6200000000000001 -1 - 14 0.58778525229247325 4.6200000000000001 -0.80901699437494734 - 15 0.95105651629515364 4.6200000000000001 -0.30901699437494734 - 16 -0.95105651629515353 -0.37999999999999989 -0.30901699437494756 - 17 -0.58778525229247303 -0.37999999999999989 -0.80901699437494756 - 18 1.2246063538223773e-16 -0.37999999999999989 -1 - 19 0.58778525229247325 -0.37999999999999989 -0.80901699437494734 - 20 0.95105651629515364 -0.37999999999999989 -0.30901699437494734 - 21 -1.5874999999999999 -0.37999999999999989 -2.9160938800395356e-16 - 22 -1.5098022196185561 -0.37999999999999989 -0.49056447857022928 - 23 -1.2843144785702287 -0.37999999999999989 -0.93310908801430126 - 24 -0.93310908801430081 -0.37999999999999989 -1.2843144785702292 - 25 -0.49056447857022878 -0.37999999999999989 -1.5098022196185563 - 26 1.9440625866930238e-16 -0.37999999999999989 -1.5874999999999999 - 27 0.49056447857022917 -0.37999999999999989 -1.5098022196185561 - 28 0.93310908801430115 -0.37999999999999989 -1.284314478570229 - 29 1.284314478570229 -0.37999999999999989 -0.93310908801430092 - 30 1.5098022196185563 -0.37999999999999989 -0.49056447857022889 - 31 1.5874999999999999 -0.37999999999999989 9.7203129334651192e-17 - 32 -1.5874999999999999 -5.0800000000000001 -2.9160938800395356e-16 - 33 -1.5098022196185561 -5.0800000000000001 -0.49056447857022928 - 34 -1.2843144785702287 -5.0800000000000001 -0.93310908801430126 - 35 -0.93310908801430081 -5.0800000000000001 -1.2843144785702292 - 36 -0.49056447857022878 -5.0800000000000001 -1.5098022196185563 - 37 1.9440625866930238e-16 -5.0800000000000001 -1.5874999999999999 - 38 0.49056447857022917 -5.0800000000000001 -1.5098022196185561 - 39 0.93310908801430115 -5.0800000000000001 -1.284314478570229 - 40 1.284314478570229 -5.0800000000000001 -0.93310908801430092 - 41 1.5098022196185563 -5.0800000000000001 -0.49056447857022889 - 42 1.5874999999999999 -5.0800000000000001 9.7203129334651192e-17 - 43 -11.43 7.3659999999999997 -5.0800000000000001 - 44 11.43 7.3659999999999997 -5.0800000000000001 - 45 -11.43 5.0800000000000001 -5.0800000000000001 - 46 11.43 5.0800000000000001 -5.0800000000000001 - 47 -13.716000000000001 5.0800000000000001 -11.43 - 48 -13.716000000000001 -5.0800000000000001 -11.43 - 49 -11.43 -5.0800000000000001 -11.43 - 50 -11.43 5.0800000000000001 -11.43 - 51 -11.43 -5.0800000000000001 -13.715999999999999 - 52 11.43 -5.0800000000000001 -13.715999999999999 - 53 11.43 5.0800000000000001 -13.715999999999999 - 54 -11.43 5.0800000000000001 -13.715999999999999 - -55 -6.6628777242868491 -0.9950764223207208 -6.66287772428685 -56 4.2067426959563958 0.1964514680670118 -5.7864151699132815 - -# Part 2 - facet list -30 1 -1 0 2 -14 1 2 3 6 5 4 42 31 7 10 9 8 21 32 -1 0 1 -7 9 10 15 14 13 12 11 -1 0 1 -4 8 9 11 16 -1 0 1 -4 11 12 17 16 -1 0 1 -4 12 13 18 17 -1 0 1 -4 13 14 19 18 -1 0 1 -4 14 15 20 19 -1 0 1 -4 10 7 20 15 -1 0 3 -18 7 31 30 29 28 27 26 25 24 23 22 21 8 16 17 18 19 20 -1 0 1 -4 21 22 33 32 -1 0 1 -4 22 23 34 33 -1 0 1 -4 23 24 35 34 -1 0 1 -4 24 25 36 35 -1 0 1 -4 25 26 37 36 -1 0 1 -4 26 27 38 37 -1 0 1 -4 27 28 39 38 -1 0 1 -4 28 29 40 39 -1 0 1 -4 29 30 41 40 -1 0 1 -4 30 31 42 41 -1 0 1 -4 43 45 46 44 -1 0 1 -4 5 6 43 44 -1 0 1 -6 4 5 44 46 53 52 -1 0 1 -4 6 3 45 43 -1 0 1 -4 47 48 49 50 -1 0 1 -8 3 2 47 50 54 53 46 45 -1 0 4 -17 42 4 52 51 49 48 1 32 33 34 35 36 37 38 39 40 41 -1 0 1 -4 2 1 48 47 -1 0 1 -4 50 49 51 54 -1 0 1 -4 54 51 52 53 - -# The following facet is a single segment with marker -1 -1 0 -1 -2 55 56 - -# Part 3 - hole list -0 - -# Part 4 - region list -0 +28 3 0 1 +1 0 0 0 1 +2 2 0 0 1 +3 2 2 0 1 +4 0 2 0 1 +5 0 0 4 9 +6 2 0 4 9 +7 2 2 3 9 +8 0 2 3 9 +9 0 0 5 2 +10 2 0 5 2 +11 2 2 5 2 +12 0 2 5 2 +13 0.25 0.25 0.5 4 +14 1.75 0.25 0.5 4 +15 1.75 1.5 0.5 4 +16 0.25 1.5 0.5 4 +17 0.25 0.25 1 4 +18 1.75 0.25 1 4 +19 1.75 1.5 1 4 +20 0.25 1.5 1 4 +21 0.25 0 2 4 +22 1.75 0 2 4 +23 1.75 1.5 2 4 +24 0.25 1.5 2 4 +25 0.25 0 2.5 4 +26 1.75 0 2.5 4 +27 1.75 1.5 2.5 4 +28 0.25 1.5 2.5 4 +23 1 +1 0 1 # 1 +4 1 2 3 4 +1 0 9 # 2 +4 5 6 7 8 +2 1 3 # 3 +4 1 2 6 5 +4 21 22 26 25 +1 1 0 2.25 +1 0 3 # 4 +4 2 3 7 6 +1 0 3 # 5 +4 3 4 8 7 +1 0 3 # 6 +4 4 1 5 8 +1 0 2 # 7 +4 9 10 11 12 +1 0 3 # 8 +4 9 10 6 5 +1 0 3 # 9 +4 10 11 7 6 +1 0 3 # 10 +4 11 12 8 7 +1 0 3 # 11 +4 12 9 5 8 +1 0 4 # 12 +4 13 14 15 16 +1 0 4 # 13 +4 17 18 19 20 +1 0 4 # 14 +4 13 14 18 17 +1 0 4 # 15 +4 14 15 19 18 +1 0 4 # 16 +4 15 16 20 19 +1 0 4 # 17 +4 16 13 17 20 +1 0 4 # 18 +4 21 22 23 24 +1 0 4 # 19 +4 25 26 27 28 +1 0 4 # 20 +4 21 22 26 25 +1 0 4 # 21 +4 22 23 27 26 +1 0 4 # 22 +4 23 24 28 27 +1 0 4 # 23 +4 24 21 25 28 +2 +1 1 0.4 2.25 +2 1 0.4 0.75 +2 +1 1 0.25 0.1 10 0.001 +2 1 0.5 4 20 0.01 diff --git a/contrib/Tetgen1.5/makefile b/contrib/Tetgen1.5/makefile index 5c886a3aeef3c494e7dd73705f07d7dc747021c5..b3b131cfef2767bcad4610b5a3cd50488a0f80a6 100644 --- a/contrib/Tetgen1.5/makefile +++ b/contrib/Tetgen1.5/makefile @@ -19,11 +19,9 @@ CXX = g++ # -O2, -O3 ... to find the best optimization level. # =================================================================== -CXXFLAGS = -g +CXXFLAGS = -O3 -# PREDCXXFLAGS is for compiling J. Shewchuk's predicates. It should -# always be equal to -O0 (no optimization). Otherwise, TetGen may not -# work properly. +# PREDCXXFLAGS is for compiling J. Shewchuk's predicates. PREDCXXFLAGS = -O0 @@ -38,14 +36,7 @@ PREDCXXFLAGS = -O0 # down the speed of TetGen. They can be skipped by define the -DNDEBUG # switch. -SWITCHES = -Wall -DSELF_CHECK - -# SWITCHES = -Wall -Wabi -Wctor-dtor-privacy \ -# -Woverloaded-virtual -Wno-pmf-conversions -Wsign-promo \ -# -Wsynth -Wchar-subscripts -Wconversion -Wsign-compare \ -# -Wcomment -Wimplicit -Wmissing-braces -Wparentheses \ -# -Wreturn-type -Wswitch -Wswitch-default \ -# -Wswitch-enum -Wtrigraphs -W -DSELF_CHECK +SWITCHES = # RM should be set to the name of your favorite rm (file deletion program). diff --git a/contrib/Tetgen1.5/predicates.cxx b/contrib/Tetgen1.5/predicates.cxx index 4120704c3521a80f27aa144feb1387ccf78c66a2..33817d7999cf04ee831bcae75e053a39d9b783cb 100644 --- a/contrib/Tetgen1.5/predicates.cxx +++ b/contrib/Tetgen1.5/predicates.cxx @@ -125,6 +125,13 @@ #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, */ @@ -149,8 +156,8 @@ /* which is disastrously slow. A faster way on IEEE machines might be to */ /* mask the appropriate bit, but that's difficult to do in C. */ -#define Absolute(a) ((a) >= 0.0 ? (a) : -(a)) -/* #define Absolute(a) fabs(a) */ +//#define Absolute(a) ((a) >= 0.0 ? (a) : -(a)) +#define Absolute(a) fabs(a) /* Many of the operations are broken up into two pieces, a main part that */ /* performs an approximate operation, and a "tail" that computes the */ @@ -375,271 +382,144 @@ static REAL o3derrboundA, o3derrboundB, o3derrboundC; static REAL iccerrboundA, iccerrboundB, iccerrboundC; static REAL isperrboundA, isperrboundB, isperrboundC; -/*****************************************************************************/ -/* */ -/* doubleprint() Print the bit representation of a double. */ -/* */ -/* Useful for debugging exact arithmetic routines. */ -/* */ -/*****************************************************************************/ +// 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". -/* -void doubleprint(number) -double number; +double fppow2(int n) { - unsigned long long no; - unsigned long long sign, expo; - int exponent; - int i, bottomi; - - no = *(unsigned long long *) &number; - sign = no & 0x8000000000000000ll; - expo = (no >> 52) & 0x7ffll; - exponent = (int) expo; - exponent = exponent - 1023; - if (sign) { - printf("-"); - } else { - printf(" "); - } - if (exponent == -1023) { - printf( - "0.0000000000000000000000000000000000000000000000000000_ ( )"); - } else { - printf("1."); - bottomi = -1; - for (i = 0; i < 52; i++) { - if (no & 0x0008000000000000ll) { - printf("1"); - bottomi = i; - } else { - printf("0"); - } - no <<= 1; - } - printf("_%d (%d)", exponent, exponent - 1 - bottomi); - } + 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); } -*/ -/*****************************************************************************/ -/* */ -/* floatprint() Print the bit representation of a float. */ -/* */ -/* Useful for debugging exact arithmetic routines. */ -/* */ -/*****************************************************************************/ +#ifdef SINGLE -/* -void floatprint(number) -float number; +float fstore(float x) { - unsigned no; - unsigned sign, expo; - int exponent; - int i, bottomi; - - no = *(unsigned *) &number; - sign = no & 0x80000000; - expo = (no >> 23) & 0xff; - exponent = (int) expo; - exponent = exponent - 127; - if (sign) { - printf("-"); - } else { - printf(" "); - } - if (exponent == -127) { - printf("0.00000000000000000000000_ ( )"); - } else { - printf("1."); - bottomi = -1; - for (i = 0; i < 23; i++) { - if (no & 0x00400000) { - printf("1"); - bottomi = i; - } else { - printf("0"); - } - no <<= 1; - } - printf("_%3d (%3d)", exponent, exponent - 1 - bottomi); - } + return (x); } -*/ -/*****************************************************************************/ -/* */ -/* expansion_print() Print the bit representation of an expansion. */ -/* */ -/* Useful for debugging exact arithmetic routines. */ -/* */ -/*****************************************************************************/ - -/* -void expansion_print(elen, e) -int elen; -REAL *e; +int test_float(int verbose) { - int i; + float x; + int pass = 1; - for (i = elen - 1; i >= 0; i--) { - REALPRINT(e[i]); - if (i > 0) { - printf(" +\n"); - } else { - printf("\n"); - } + //(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 } -} -*/ -/*****************************************************************************/ -/* */ -/* doublerand() Generate a double with random 53-bit significand and a */ -/* random exponent in [0, 511]. */ -/* */ -/*****************************************************************************/ + 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); -/* -double doublerand() -{ - double result; - double expo; - long a, b, c; - long i; - - a = random(); - b = random(); - c = random(); - result = (double) (a - 1073741824) * 8388608.0 + (double) (b >> 8); - for (i = 512, expo = 2; i <= 131072; i *= 2, expo = expo * expo) { - if (c & i) { - result *= expo; - } + 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; } - return result; -} -*/ -/*****************************************************************************/ -/* */ -/* narrowdoublerand() Generate a double with random 53-bit significand */ -/* and a random exponent in [0, 7]. */ -/* */ -/*****************************************************************************/ - -/* -double narrowdoublerand() -{ - double result; - double expo; - long a, b, c; - long i; - - a = random(); - b = random(); - c = random(); - result = (double) (a - 1073741824) * 8388608.0 + (double) (b >> 8); - for (i = 512, expo = 2; i <= 2048; i *= 2, expo = expo * expo) { - if (c & i) { - result *= expo; - } + 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 result; + + return pass; } -*/ -/*****************************************************************************/ -/* */ -/* uniformdoublerand() Generate a double with random 53-bit significand. */ -/* */ -/*****************************************************************************/ +# else -/* -double uniformdoublerand() +double dstore(double x) { - double result; - long a, b; - - a = random(); - b = random(); - result = (double) (a - 1073741824) * 8388608.0 + (double) (b >> 8); - return result; + return (x); } -*/ - -/*****************************************************************************/ -/* */ -/* floatrand() Generate a float with random 24-bit significand and a */ -/* random exponent in [0, 63]. */ -/* */ -/*****************************************************************************/ -/* -float floatrand() +int test_double(int verbose) { - float result; - float expo; - long a, c; - long i; - - a = random(); - c = random(); - result = (float) ((a - 1073741824) >> 6); - for (i = 512, expo = 2; i <= 16384; i *= 2, expo = expo * expo) { - if (c & i) { - result *= expo; - } + 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 } - return result; -} -*/ -/*****************************************************************************/ -/* */ -/* narrowfloatrand() Generate a float with random 24-bit significand and */ -/* a random exponent in [0, 7]. */ -/* */ -/*****************************************************************************/ + x = 1.0; + while (dstore(1.0 + x/2.0) != 1.0) + x /= 2.0; + if (verbose) + (void)printf(" machine epsilon = %13.5le ", x); -/* -float narrowfloatrand() -{ - float result; - float expo; - long a, c; - long i; - - a = random(); - c = random(); - result = (float) ((a - 1073741824) >> 6); - for (i = 512, expo = 2; i <= 2048; i *= 2, expo = expo * expo) { - if (c & i) { - result *= expo; - } + 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; } - return result; -} -*/ - -/*****************************************************************************/ -/* */ -/* uniformfloatrand() Generate a float with random 24-bit significand. */ -/* */ -/*****************************************************************************/ -/* -float uniformfloatrand() -{ - float result; - long a; + 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; + } - a = random(); - result = (float) ((a - 1073741824) >> 6); - return result; + return pass; } -*/ + +#endif /*****************************************************************************/ /* */ @@ -660,7 +540,8 @@ float uniformfloatrand() /* */ /*****************************************************************************/ -REAL exactinit() +void exactinit(int verbose, int noexact, int nofilter, REAL maxx, REAL maxy, + REAL maxz) { REAL half; REAL check, lastcheck; @@ -687,6 +568,24 @@ REAL exactinit() _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; @@ -722,7 +621,31 @@ REAL exactinit() isperrboundB = (5.0 + 72.0 * epsilon) * epsilon; isperrboundC = (71.0 + 1408.0 * epsilon) * epsilon * epsilon; - return epsilon; /* Added by H. Si 30 Juli, 2004. */ + // 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); + } /*****************************************************************************/ @@ -1869,16 +1792,6 @@ REAL orient3dadapt(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL permanent) REAL fin1[192], fin2[192]; int finlength; - //////////////////////////////////////////////////////// - // To avoid uninitialized warnings reported by valgrind. - int i; - for (i = 0; i < 8; i++) { - adet[i] = bdet[i] = cdet[i] = 0.0; - } - for (i = 0; i < 16; i++) { - abdet[i] = 0.0; - } - //////////////////////////////////////////////////////// REAL adxtail, bdxtail, cdxtail; REAL adytail, bdytail, cdytail; @@ -1916,6 +1829,7 @@ REAL orient3dadapt(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL permanent) 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]); @@ -2263,27 +2177,16 @@ REAL orient3dadapt(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL permanent) return finnow[finlength - 1]; } -#ifdef INEXACT_GEOM_PRED +#ifdef USE_CGAL_PREDICATES REAL orient3d(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); + 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 @@ -2293,16 +2196,16 @@ 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; - 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]; 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; @@ -2318,6 +2221,19 @@ REAL orient3d(REAL *pa, REAL *pb, REAL *pc, REAL *pd) + 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); @@ -2329,7 +2245,7 @@ REAL orient3d(REAL *pa, REAL *pb, REAL *pc, REAL *pd) return orient3dadapt(pa, pb, pc, pd, permanent); } -#endif // ifdef INEXACT_GEOM_PRED +#endif // #ifdef USE_CGAL_PREDICATES /*****************************************************************************/ /* */ @@ -3362,6 +3278,7 @@ REAL insphereexact(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe) 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]); @@ -3938,6 +3855,7 @@ REAL insphereadapt(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe, INEXACT REAL _i, _j; REAL _0; + aex = (REAL) (pa[0] - pe[0]); bex = (REAL) (pb[0] - pe[0]); cex = (REAL) (pc[0] - pe[0]); @@ -4114,50 +4032,19 @@ REAL insphereadapt(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe, return insphereexact(pa, pb, pc, pd, pe); } -#ifdef INEXACT_GEOM_PRED +#ifdef USE_CGAL_PREDICATES 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 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); + 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) @@ -4170,12 +4057,8 @@ REAL insphere(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe) REAL alift, blift, clift, dlift; 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]; @@ -4222,6 +4105,23 @@ REAL insphere(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe) 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); @@ -4262,7 +4162,7 @@ REAL insphere(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe) return insphereadapt(pa, pb, pc, pd, pe, permanent); } -#endif // #ifdef INEXACT_GEOM_PRED +#endif // #ifdef USE_CGAL_PREDICATES /*****************************************************************************/ /* */ @@ -4327,6 +4227,7 @@ REAL orient4dexact(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe, 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]); @@ -4540,6 +4441,7 @@ REAL orient4dadapt(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe, INEXACT REAL _i, _j; REAL _0; + aex = (REAL) (pa[0] - pe[0]); bex = (REAL) (pb[0] - pe[0]); cex = (REAL) (pc[0] - pe[0]); @@ -4699,108 +4601,106 @@ 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; - - //orient4dcount++; - - 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); + 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); +} - if (0) { //if (noexact) { - return det; - } - 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) - * aeheight - + ((dexaeyplus + aexdeyplus) * cezplus - + (aexceyplus + cexaeyplus) * dezplus - + (cexdeyplus + dexceyplus) * aezplus) - * beheight - + ((aexbeyplus + bexaeyplus) * dezplus - + (bexdeyplus + dexbeyplus) * aezplus - + (dexaeyplus + aexdeyplus) * bezplus) - * ceheight - + ((bexceyplus + cexbeyplus) * aezplus - + (cexaeyplus + aexceyplus) * bezplus - + (aexbeyplus + bexaeyplus) * cezplus) - * 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/contrib/Tetgen1.5/tetgen.cxx b/contrib/Tetgen1.5/tetgen.cxx index 0a66de153c7e2825e01c02a2f1fdb01be35381ed..da4ef3d73513af1acad895153b65e5d80f66842b 100644 --- a/contrib/Tetgen1.5/tetgen.cxx +++ b/contrib/Tetgen1.5/tetgen.cxx @@ -2,21 +2,10 @@ // // // TetGen // // // -// A Quality Tetrahedral Mesh Generator and 3D Delaunay Triangulator // +// A Quality Tetrahedral Mesh Generator and A 3D Delaunay Triangulator // // // // Version 1.5 // -// February 21, 2012 // -// // -// PRE-RELEASE TEST CODE. // -// PLEASE DO NOT DISTRIBUTE !! // -// PLEASE HELP ME TO IMPROVE IT !! // -// // -// Copyright (C) 2002--2012 // -// Hang Si // -// Research Group: Numerical Mathematics and Scientific Computing // -// Weierstrass Institute for Applied Analysis and Stochastics (WIAS) // -// Mohrenstr. 39, 10117 Berlin, Germany // -// Hang.Si@wias-berlin.de // +// 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. // @@ -58,24 +47,24 @@ bool tetgenio::load_node_call(FILE* infile, int markers, int uvflag, // Initialize 'pointlist', 'pointattributelist', and 'pointmarkerlist'. pointlist = new REAL[numberofpoints * 3]; if (pointlist == (REAL *) NULL) { - terminatetetgen(1); + terminatetetgen(NULL, 1); } if (numberofpointattributes > 0) { pointattributelist = new REAL[numberofpoints * numberofpointattributes]; if (pointattributelist == (REAL *) NULL) { - terminatetetgen(1); + terminatetetgen(NULL, 1); } } if (markers) { pointmarkerlist = new int[numberofpoints]; if (pointmarkerlist == (int *) NULL) { - terminatetetgen(1); + terminatetetgen(NULL, 1); } } if (uvflag) { pointparamlist = new pointparam[numberofpoints]; if (pointparamlist == NULL) { - terminatetetgen(1); + terminatetetgen(NULL, 1); } } @@ -223,11 +212,11 @@ bool tetgenio::load_node(char* filebasename) mesh_dim = 3; numberofpointattributes = 0; // no point attribute. markers = 0; // no boundary marker. - uvflag = 0; // no uv parameters (reuqired by a PSC). + 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 colume? + // Does this file contain an index column? stringptr = strstr(inputline, "rbox"); if (stringptr == NULL) { // Read number of points, number of dimensions, number of point @@ -302,7 +291,7 @@ bool tetgenio::load_edge(char* filebasename) if (numberofedges > 0) { edgelist = new int[numberofedges * 2]; if (edgelist == (int *) NULL) { - terminatetetgen(1); + terminatetetgen(NULL, 1); } stringptr = findnextnumber(stringptr); if (*stringptr == '\0') { @@ -325,16 +314,20 @@ bool tetgenio::load_edge(char* filebasename) if (*stringptr == '\0') { printf("Error: Edge %d is missing vertex %d in %s.\n", i + firstnumber, j + 1, inedgefilename); - terminatetetgen(1); + 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(1); + 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); @@ -389,12 +382,12 @@ bool tetgenio::load_face(char* filebasename) if (numberoftrifaces > 0) { trifacelist = new int[numberoftrifaces * 3]; if (trifacelist == (int *) NULL) { - terminatetetgen(1); + terminatetetgen(NULL, 1); } if (markers) { trifacemarkerlist = new int[numberoftrifaces]; if (trifacemarkerlist == (int *) NULL) { - terminatetetgen(1); + terminatetetgen(NULL, 1); } } } @@ -409,16 +402,22 @@ bool tetgenio::load_face(char* filebasename) if (*stringptr == '\0') { printf("Error: Face %d is missing vertex %d in %s.\n", i + firstnumber, j + 1, infilename); - terminatetetgen(1); + 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(1); + 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); @@ -494,14 +493,14 @@ bool tetgenio::load_tet(char* filebasename) // Allocate memory for tetrahedra. tetrahedronlist = new int[numberoftetrahedra * numberofcorners]; if (tetrahedronlist == (int *) NULL) { - terminatetetgen(1); + terminatetetgen(NULL, 1); } // Allocate memory for output tetrahedron attributes if necessary. if (numberoftetrahedronattributes > 0) { tetrahedronattributelist = new REAL[numberoftetrahedra * numberoftetrahedronattributes]; if (tetrahedronattributelist == (REAL *) NULL) { - terminatetetgen(1); + terminatetetgen(NULL, 1); } } @@ -516,13 +515,13 @@ bool tetgenio::load_tet(char* filebasename) if (*stringptr == '\0') { printf("Error: Tetrahedron %d is missing vertex %d in %s.\n", i + firstnumber, j + 1, infilename); - terminatetetgen(1); + 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(1); + terminatetetgen(NULL, 1); } tetrahedronlist[index++] = corner; } @@ -584,7 +583,7 @@ bool tetgenio::load_vol(char* filebasename) tetrahedronvolumelist = new REAL[volelements]; if (tetrahedronvolumelist == (REAL *) NULL) { - terminatetetgen(1); + terminatetetgen(NULL, 1); } // Read the list of volume constraints. @@ -627,7 +626,6 @@ bool tetgenio::load_var(char* filebasename) if (infile != (FILE *) NULL) { printf("Opening %s.\n", varfilename); } else { - // No such file. Ignore it without a message. return false; } @@ -740,7 +738,6 @@ bool tetgenio::load_mtr(char* filebasename) if (infile != (FILE *) NULL) { printf("Opening %s.\n", mtrfilename); } else { - // No such file. Return. return false; } @@ -765,7 +762,7 @@ bool tetgenio::load_mtr(char* filebasename) // Allocate space for pointmtrlist. pointmtrlist = new REAL[numberofpoints * numberofpointmtrs]; if (pointmtrlist == (REAL *) NULL) { - terminatetetgen(1); + terminatetetgen(NULL, 1); } mtrindex = 0; for (i = 0; i < numberofpoints; i++) { @@ -775,7 +772,7 @@ bool tetgenio::load_mtr(char* filebasename) if (*stringptr == '\0') { printf("Error: Metric %d is missing value #%d in %s.\n", i + firstnumber, j + 1, mtrfilename); - terminatetetgen(1); + terminatetetgen(NULL, 1); } mtr = (REAL) strtod(stringptr, &stringptr); pointmtrlist[mtrindex++] = mtr; @@ -832,10 +829,10 @@ bool tetgenio::load_poly(char* filebasename) } // Initialize the default values. - mesh_dim = 3; // Three-dimemsional accoordinates. + mesh_dim = 3; // Three-dimensional coordinates. numberofpointattributes = 0; // no point attribute. markers = 0; // no boundary marker. - uvflag = 0; // no uv parameters (reuqired by a PSC). + 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. @@ -1381,14 +1378,11 @@ bool tetgenio::load_off(char* filebasename) return false; } - // Check whether read all points if (iverts != nverts) { printf("Expected %d vertices, but read only %d vertices in file %s\n", nverts, iverts, infilename); return false; } - - // Check whether read all faces if (ifaces != nfaces) { printf("Expected %d faces, but read only %d faces in file %s\n", nfaces, ifaces, infilename); @@ -1603,14 +1597,11 @@ bool tetgenio::load_ply(char* filebasename) return false; } - // Check whether read all points if (iverts != nverts) { printf("Expected %d vertices, but read only %d vertices in file %s\n", nverts, iverts, infilename); return false; } - - // Check whether read all faces if (ifaces != nfaces) { printf("Expected %d faces, but read only %d faces in file %s\n", nfaces, ifaces, infilename); @@ -2092,7 +2083,7 @@ bool tetgenio::load_medit(char* filebasename, int istetmesh) // // // load_vtk() Load VTK surface mesh from file (.vtk ascii or binary). // // // -// This function is contributed by: Bryn Lloyd, Computer Vision Laborator, // +// This function is contributed by: Bryn Lloyd, Computer Vision Laboratory, // // ETH, Zuerich. May 7, 2007. // // // /////////////////////////////////////////////////////////////////////////////// @@ -2389,6 +2380,7 @@ bool tetgenio::load_plc(char* filebasename, int object) } if (success) { + // Try to load the following files (.edge, .var, .mtr). load_edge(filebasename); load_var(filebasename); load_mtr(filebasename); @@ -2415,6 +2407,7 @@ bool tetgenio::load_tetmesh(char* filebasename, int object) success = load_tet(filebasename); } if (success) { + // Try to load the following files (.face, .edge, .vol). load_face(filebasename); load_edge(filebasename); load_vol(filebasename); @@ -2422,6 +2415,7 @@ bool tetgenio::load_tetmesh(char* filebasename, int object) } if (success) { + // Try to load the following files (.var, .mtr). load_var(filebasename); load_mtr(filebasename); } @@ -2902,16 +2896,23 @@ char* tetgenio::findnextnumber(char *string) void tetgenbehavior::syntax() { - printf(" tetgen [-pYrq_a_AiS_T_dzfenvgKJBNEFICQVh] input_file\n"); + printf(" tetgen [-pYrq_Aa_miO_S_T_XMwcdzfenvgkJBNEFICQVh] input_file\n"); printf(" -p Tetrahedralizes a piecewise linear complex (PLC).\n"); - printf(" -Y No splitting of input boundaries (facets and segments).\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(" -a Applies a maximum tetrahedron volume constraint.\n"); + printf(" -R Mesh coarsening (to reduce the mesh elements).\n"); printf(" -A Assigns attributes to tetrahedra in different regions.\n"); - printf(" -i Inserts a list of additional points into mesh.\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"); @@ -2919,12 +2920,12 @@ void tetgenbehavior::syntax() 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(" -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 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"); @@ -2943,20 +2944,13 @@ void tetgenbehavior::usage() printf("TetGen\n"); printf("A Quality Tetrahedral Mesh Generator and 3D Delaunay "); printf("Triangulator\n"); - printf("Version 1.5 (February 21, 2012).\n"); - printf("\n"); - printf("Copyright (C) 2002 - 2012\n"); - printf("Hang Si\n"); - printf("Mohrenstr. 39, 10117 Berlin, Germany\n"); - printf("Hang.Si@wias-berlin.de\n"); + printf("Version 1.5\n"); + printf("November 4, 2013\n"); printf("\n"); printf("What Can TetGen Do?\n"); printf("\n"); - printf(" TetGen generates exact Delaunay tetrahedralizations, exact\n"); - printf(" constrained Delaunay tetrahedralizations, and quality "); - printf("tetrahedral\n meshes. The latter are nicely graded and whose "); - printf("tetrahedra have\n radius-edge ratio bounded, thus are suitable "); - printf("for finite element and\n finite volume analysis.\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"); @@ -2977,22 +2971,24 @@ void tetgenbehavior::usage() 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 and "); - printf("object.1.ele.\n"); + 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 and "); - printf("object.1.face.\n"); + 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 and object.1.face.\n"); + 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(0); + terminatetetgen(NULL, 0); } /////////////////////////////////////////////////////////////////////////////// @@ -3004,19 +3000,6 @@ void tetgenbehavior::usage() // of a C/C++ program. They together represent the command line user invoked // // from an environment in which TetGen is running. // // // -// When TetGen is invoked from an environment. 'argc' is nonzero, switches // -// and input filename should be supplied as zero-terminated strings in // -// argv[0] through argv[argc - 1] and argv[0] shall be the name used to // -// invoke TetGen, i.e. "tetgen". Switches are previously started with a // -// dash '-' to identify them from the input filename. // -// // -// When TetGen is called from within another program. 'argc' is set to zero. // -// switches are given in one zero-terminated string (no previous dash is // -// required.), and 'argv' is a pointer points to this string. No input // -// filename is required (usually the input data has been directly created by // -// user in the 'tetgenio' structure). A default filename 'tetgen-tmpfile' // -// will be created for debugging output purpose. // -// // /////////////////////////////////////////////////////////////////////////////// bool tetgenbehavior::parse_commandline(int argc, char **argv) @@ -3024,7 +3007,6 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) int startindex; int increment; int meshnumber; - int scount, ocount; int i, j, k; char workstring[1024]; @@ -3038,12 +3020,9 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) strcpy(commandline, argv[0]); strcat(commandline, " "); } - - // Count the number of '-O' and '-o' be used. - scount = ocount = 0; for (i = startindex; i < argc; i++) { - // Remember the command line switches. + // Remember the command line for output. strcat(commandline, argv[i]); strcat(commandline, " "); if (startindex == 1) { @@ -3051,7 +3030,6 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) if (argv[i][0] != '-') { strncpy(infilename, argv[i], 1024 - 1); infilename[1024 - 1] = '\0'; - // Go to the next string directly. continue; } } @@ -3072,11 +3050,24 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) facet_ang_tol = (REAL) strtod(workstring, (char **) NULL); } } else if (argv[i][j] == 's') { - psc = 1; + 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++; + refine = 1; } else if (argv[i][j] == 'q') { - quality++; + quality = 1; if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || (argv[i][j + 1] == '.')) { k = 0; @@ -3087,30 +3078,68 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) k++; } workstring[k] = '\0'; - if (quality == 1) { // -q# - minratio = (REAL) strtod(workstring, (char **) NULL); - } else if (quality == 2) { // -qq# + 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); } } - } else if (argv[i][j] == 'm') { - metric++; - } else if (argv[i][j] == 'Y') { - nobisect = 1; - if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || - (argv[i][j + 1] == '.')) { - k = 0; - while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || - (argv[i][j + 1] == '.') || (argv[i][j + 1] == 'e')) { - j++; - workstring[k] = argv[i][j]; - k++; + 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); } - workstring[k] = '\0'; - nobisect_param = (int) strtol(workstring, (char **) NULL, 0); } } 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; @@ -3121,8 +3150,66 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) k++; } workstring[k] = '\0'; - weighted_param = (int) strtol(workstring, (char **) NULL, 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] == '.')) { @@ -3141,71 +3228,44 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) varvolume = 1; } } else if (argv[i][j] == 'A') { - regionattrib++; - } else if (argv[i][j] == 'l') { - incrflip = 1; - // Check if a smallest edge length is given. - 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'; - minedgelength = (REAL) strtod(workstring, (char **) NULL); - } - } else if (argv[i][j] == 'L') { - flipinsert++; - 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'; - if (flipinsert == 1) { // -L - fliplinklevel = (int) strtol(workstring, (char **) NULL, 0); - } else if (flipinsert == 2) { // -LL - flipstarsize = (int) strtol(workstring, (char **) NULL, 0); - } - } - } else if (argv[i][j] == 'u') { - // Set the maximum btree node size, -u0 means do not use btree. - 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'; - max_btreenode_size = (int) strtol(workstring, (char **) NULL, 0); - } - if (max_btreenode_size == 0) { - btree = 0; + 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] == 'U') { - hilbertcurve = 1; - btree = 0; } 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 = 1; + facesout++; } else if (argv[i][j] == 'e') { edgesout++; } else if (argv[i][j] == 'n') { @@ -3214,10 +3274,8 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) voroout = 1; } else if (argv[i][j] == 'g') { meditview = 1; - } else if (argv[i][j] == 'K') { + } else if (argv[i][j] == 'k') { vtkview = 1; - } else if (argv[i][j] == 'M') { - nomerge = 1; } else if (argv[i][j] == 'J') { nojettison = 1; } else if (argv[i][j] == 'B') { @@ -3226,10 +3284,6 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) nonodewritten = 1; } else if (argv[i][j] == 'E') { noelewritten = 1; - if (argv[i][j + 1] == '2') { - j++; - noelewritten = 2; - } } else if (argv[i][j] == 'F') { nofacewritten = 1; } else if (argv[i][j] == 'I') { @@ -3249,53 +3303,38 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) steinerleft = (int) strtol(workstring, (char **) NULL, 0); } } else if (argv[i][j] == 'o') { - ocount++; - 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'; - if (ocount == 1) { // -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 (ocount == 2) { // -oo# - optminsmtdihed = (REAL) strtod(workstring, (char **) NULL); - } else if (ocount == 3) { // -ooo# - optminslidihed = (REAL) strtod(workstring, (char **) NULL); - } + } } } else if (argv[i][j] == 'O') { - scount++; - 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] == '+')) { + 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++; - workstring[k] = argv[i][j]; - k++; - } - workstring[k] = '\0'; - if (scount == 1) { // -O - optlevel = (int) strtol(workstring, (char **) NULL, 0); - } else if (scount == 2) { // -OO - optpasses = (int) strtol(workstring, (char **) NULL, 0); - } else if (scount == 3) { // -OOO - optmaxfliplevel = (int) strtol(workstring, (char **) NULL, 0); - } else if (scount == 4) { // -OOOO - delmaxfliplevel = (int) strtol(workstring, (char **) NULL, 0); - } else if (scount == 5) { // -OOOOO (5 Os) - optmaxflipstarsize = (int) strtol(workstring, (char **) NULL, 0); } } - } else if (argv[i][j] == 'D') { - conforming = 1; + } else if (argv[i][j] == 'T') { if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || (argv[i][j + 1] == '.')) { k = 0; @@ -3307,9 +3346,17 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) k++; } workstring[k] = '\0'; - reflevel = (int) strtol(workstring, (char **) NULL, 0); + epsilon = (REAL) strtod(workstring, (char **) NULL); } - } else if (argv[i][j] == 'T') { + } 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; @@ -3321,14 +3368,14 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) k++; } workstring[k] = '\0'; - epsilon = (REAL) strtod(workstring, (char **) NULL); + tetrahedraperblock = (int) strtol(workstring, (char **) NULL, 0); + if (tetrahedraperblock > 8188) { + vertexperblock = tetrahedraperblock / 2; + shellfaceperblock = vertexperblock / 2; + } else { + tetrahedraperblock = 8188; + } } - } 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] == 'h') || (argv[i][j] == 'H') || (argv[i][j] == '?')) { usage(); @@ -3345,7 +3392,7 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) if (infilename[0] == '\0') { // No input file name. Print the syntax and exit. syntax(); - terminatetetgen(0); + terminatetetgen(NULL, 0); } // Recognize the object from file extension if it is available. if (!strcmp(&infilename[strlen(infilename) - 5], ".node")) { @@ -3395,32 +3442,41 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) if (diagnose && !plc) { // -d plc = 1; } - - // Detect improper combinations of switches. - if (plc && refine) { - printf("Error: Switch -r cannot use together with -p.\n"); - return false; + if (refine && !quality) { // -r only + // Reconstruct a mesh, no mesh optimization. + optlevel = 0; } - if (refine && (plc || noiterationnum)) { - printf("Error: Switches %s cannot use together with -r.\n", - "-p, -d, and -I"); - return false; + 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; } - // 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 (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) { @@ -3430,18 +3486,20 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) } } } - if (ocount == 0) { - // No user-specified dihedral angle bound. Use default ones. - if (!quality) { - if (optmaxdihedral < 179.0) { + // 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.99; - } + } + if (optminsmtdihed < 179.999) { + optminsmtdihed = 179.999; + } + if (optminslidihed < 179.999) { + optminslidihed = 179.999; } } @@ -3497,18 +3555,25 @@ bool tetgenbehavior::parse_commandline(int argc, char **argv) // Initialize fast lookup tables for mesh maniplulation primitives. -int tetgenmesh::mod12[36] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; - -int tetgenmesh::mod6[18] = {0, 1, 2, 3, 4, 5, - 0, 1, 2, 3, 4, 5, - 0, 1, 2, 3, 4, 5}; - -// Table 'edgepivot' takes an directed edge (version) as input, returns the +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::edgepivot[12] = {9, 6, 11, 4, 3, 7, 1, 5, 10, 0, 8, 2}; +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 @@ -3525,6 +3590,11 @@ int tetgenmesh::oppopivot[12] = {4, 5, 6, 7, 4, 5, 6, 7, 4, 5, 6, 7}; 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. @@ -3538,11 +3608,91 @@ 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}; -// Edge versions whose apex or opposite may be dummypoint. +/////////////////////////////////////////////////////////////////////////////// +// // +// 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]; + } + } -int tetgenmesh::epivot[4] = {4, 5, 2, 11}; + 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); + } + } +} /////////////////////////////////////////////////////////////////////////////// // // @@ -3576,6 +3726,7 @@ void tetgenmesh::arraypool::poolinit(int sizeofobject, int log2objperblk) log2objectsperblock = log2objperblk; // Compute the number of objects in each block. objectsperblock = ((int) 1) << log2objectsperblock; + objectsperblockmark = objectsperblock - 1; // No memory has been allocated. totalmemory = 0l; @@ -3733,26 +3884,22 @@ void* tetgenmesh::arraypool::lookup(int objectindex) // // // 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) { - void *newobject; - int newindex; - // Allocate an object at index 'firstvirgin'. - newindex = objects; - newobject = (void *) (getblock(objects) + + int newindex = objects; + *newptr = (void *) (getblock(objects) + (objects & (objectsperblock - 1)) * objectbytes); objects++; - // If 'newptr' is not NULL, use it to return a pointer to the object. - if (newptr != (void **) NULL) { - *newptr = newobject; - } return newindex; } + /////////////////////////////////////////////////////////////////////////////// // // // memorypool() The constructors of memorypool. // @@ -3766,7 +3913,6 @@ tetgenmesh::memorypool::memorypool() deaditemstack = (void *) NULL; pathblock = (void **) NULL; pathitem = (void *) NULL; - itemwordtype = POINTER; alignbytes = 0; itembytes = itemwords = 0; itemsperblock = 0; @@ -3775,10 +3921,10 @@ tetgenmesh::memorypool::memorypool() pathitemsleft = 0; } -tetgenmesh::memorypool:: -memorypool(int bytecount, int itemcount, enum wordtype wtype, int alignment) +tetgenmesh::memorypool::memorypool(int bytecount, int itemcount, int wsize, + int alignment) { - poolinit(bytecount, itemcount, wtype, alignment); + poolinit(bytecount, itemcount, wsize, alignment); } /////////////////////////////////////////////////////////////////////////////// @@ -3812,14 +3958,9 @@ tetgenmesh::memorypool::~memorypool() // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::memorypool:: -poolinit(int bytecount, int itemcount, enum wordtype wtype, int alignment) +void tetgenmesh::memorypool::poolinit(int bytecount,int itemcount,int wordsize, + int alignment) { - int wordsize; - - // Initialize values in the pool. - itemwordtype = wtype; - wordsize = (itemwordtype == POINTER) ? sizeof(void *) : sizeof(REAL); // Find the proper alignment, which must be at least as large as: // - The parameter `alignment'. // - The primary word type, to avoid unaligned accesses. @@ -3844,7 +3985,7 @@ poolinit(int bytecount, int itemcount, enum wordtype wtype, int alignment) firstblock = (void **) malloc(itemsperblock * itembytes + sizeof(void *) + alignbytes); if (firstblock == (void **) NULL) { - terminatetetgen(1); + terminatetetgen(NULL, 1); } // Set the next block pointer to NULL. *(firstblock) = (void *) NULL; @@ -3908,7 +4049,7 @@ void* tetgenmesh::memorypool::alloc() newblock = (void **) malloc(itemsperblock * itembytes + sizeof(void *) + alignbytes); if (newblock == (void **) NULL) { - terminatetetgen(1); + terminatetetgen(NULL, 1); } *nowblock = (void *) newblock; // The next block pointer is NULL. @@ -3929,11 +4070,7 @@ void* tetgenmesh::memorypool::alloc() // Allocate a new item. newitem = nextitem; // Advance `nextitem' pointer to next free item in block. - if (itemwordtype == POINTER) { - nextitem = (void *) ((void **) nextitem + itemwords); - } else { - nextitem = (void *) ((REAL *) nextitem + itemwords); - } + nextitem = (void *) ((uintptr_t) nextitem + itembytes); unallocateditems--; maxitems++; } @@ -4017,25 +4154,18 @@ void* tetgenmesh::memorypool::traverse() } newitem = pathitem; // Find the next item in the block. - if (itemwordtype == POINTER) { - pathitem = (void *) ((void **) pathitem + itemwords); - } else { - pathitem = (void *) ((REAL *) pathitem + itemwords); - } + 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[0]'. Don't forget to minus 'in->firstnumber' when // -// to get the vertex form its index. // +// saved in 'idx2verlist[in->firstnumber]'. // // // /////////////////////////////////////////////////////////////////////////////// @@ -4052,14 +4182,13 @@ void tetgenmesh::makeindex2pointmap(point*& idx2verlist) points->traversalinit(); pointloop = pointtraverse(); - idx = in->firstnumber;; + idx = in->firstnumber; while (pointloop != (point) NULL) { idx2verlist[idx++] = pointloop; pointloop = pointtraverse(); } } - /////////////////////////////////////////////////////////////////////////////// // // // makesubfacemap() Create a map from vertex to subfaces incident at it. // @@ -4163,15 +4292,13 @@ void tetgenmesh::tetrahedrondealloc(tetrahedron *dyingtetrahedron) // dead tetrahedra when traversing the list of all tetrahedra. dyingtetrahedron[4] = (tetrahedron) NULL; - //if (b->plc || b->refine) { //if (b->useshelles) { - // Dealloc the space to subfaces/subsegments. - if (dyingtetrahedron[8] != NULL) { - tet2segpool->dealloc((shellface *) dyingtetrahedron[8]); - } - if (dyingtetrahedron[9] != NULL) { - tet2subpool->dealloc((shellface *) dyingtetrahedron[9]); - } - //} + // 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); } @@ -4244,38 +4371,6 @@ tetgenmesh::shellface* tetgenmesh::shellfacetraverse(memorypool *pool) return newshellface; } -/////////////////////////////////////////////////////////////////////////////// -// // -// badfacedealloc() Deallocate space for a badface, marking it dead. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::badfacedealloc(memorypool *pool, badface *dying) -{ - // Set badface's forg to NULL. This makes it possible to detect dead - // ones when traversing the list of all items. - dying->forg = (point) NULL; - pool->dealloc((void *) dying); -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// badfacetraverse() Traverse the pools, skipping dead ones. // -// // -/////////////////////////////////////////////////////////////////////////////// - -tetgenmesh::badface* tetgenmesh::badfacetraverse(memorypool *pool) -{ - badface *newsh; - - do { - newsh = (badface *) pool->traverse(); - if (newsh == (badface *) NULL) { - return (badface *) NULL; - } - } while (newsh->forg == (point) NULL); // Skip dead ones. - return newsh; -} /////////////////////////////////////////////////////////////////////////////// // // @@ -4319,6 +4414,7 @@ tetgenmesh::point tetgenmesh::pointtraverse() 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; @@ -4329,12 +4425,12 @@ void tetgenmesh::maketetrahedron(triface *newtet) newtet->tet[5] = NULL; newtet->tet[6] = NULL; newtet->tet[7] = NULL; - // No attached segments and sbfaces yet. + // No attached segments and subfaces yet. newtet->tet[8] = NULL; newtet->tet[9] = NULL; - // Initialize the marker (for flags). + // Initialize the marker (clear all flags). setelemmarker(newtet->tet, 0); - for (int i = 0; i < in->numberoftetrahedronattributes; i++) { + for (int i = 0; i < numelemattrib; i++) { setelemattribute(newtet->tet, i, 0.0); } if (b->varvolume) { @@ -4348,13 +4444,14 @@ void tetgenmesh::maketetrahedron(triface *newtet) /////////////////////////////////////////////////////////////////////////////// // // // makeshellface() Create a new shellface with version zero. Used for // -// both subfaces and seusegments. // +// 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; @@ -4370,19 +4467,18 @@ void tetgenmesh::makeshellface(memorypool *pool, face *newface) // No adjoining tetrahedra. newface->sh[9] = NULL; newface->sh[10] = NULL; - if (b->quality && checkconstraints) { + 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); - // Set the default face type. - setshelltype(*newface, NSHARP); - // Initialize the version to be Zero. + newface->shver = 0; } @@ -4394,16 +4490,21 @@ void tetgenmesh::makeshellface(memorypool *pool, face *newface) void tetgenmesh::makepoint(point* pnewpoint, enum verttype vtype) { - int ptmark, i; + 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->psc || b->refine) { + if (b->plc || b->refine) { // Initialize the point-to-simplex field. setpoint2sh(*pnewpoint, NULL); if (b->metric && (bgm != NULL)) { @@ -4411,20 +4512,11 @@ void tetgenmesh::makepoint(point* pnewpoint, enum verttype vtype) } } // Initialize the point marker (starting from in->firstnumber). - ptmark = (int) points->items - (in->firstnumber == 1 ? 0 : 1); - setpointmark(*pnewpoint, ptmark); - // Initialize the point type. + setpointmark(*pnewpoint, (int) (points->items) - (!in->firstnumber)); + // Clear all flags. + ((int *) (*pnewpoint))[pointmarkindex + 1] = 0; + // Initialize (set) the point type. setpointtype(*pnewpoint, vtype); - // Clear the point flags. - puninfect(*pnewpoint); - punmarktest(*pnewpoint); - if (b->psc) { - // Initialize the u,v coordinates. - setpointgeomuv(*pnewpoint, 0, 0); - setpointgeomuv(*pnewpoint, 1, 0); - // Initialize the geometry tag. - setpointgeomtag(*pnewpoint, 0); - } } /////////////////////////////////////////////////////////////////////////////// @@ -4440,29 +4532,61 @@ void tetgenmesh::makepoint(point* pnewpoint, enum verttype vtype) void tetgenmesh::initializepools() { - enum memorypool::wordtype wtype; - int pointsize, elesize, shsize; + 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; + } + } - // Each vertex has three coordinates plus a weight, hence 4 REALs. // The index within each point at which its metric tensor is found. + // Each vertex has three coordinates. if (b->psc) { - // For '-s' option (PSC), the u,v coordinates are provided. It is - // saved directly after the list of point attributes. - pointmtrindex = 6 + in->numberofpointattributes; + // '-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 = 4 + in->numberofpointattributes; + pointmtrindex = 3 + numpointattrib; } - // The index within each point at which its u, v coordinates are found. - pointparamindex = pointmtrindex - 2; // 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. @@ -4480,6 +4604,11 @@ void tetgenmesh::initializepools() // 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. @@ -4491,19 +4620,11 @@ void tetgenmesh::initializepools() // - 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 to into the background mesh, point2bgmtet(). + // Increase one pointer into the background mesh, point2bgmtet(). pointsize = (point2simindex + 4) * sizeof(tetrahedron); } else { pointsize = (point2simindex + 3) * sizeof(tetrahedron); } - // The index within each point at which a pbc point is found. - point2pbcptindex = (pointsize + sizeof(tetrahedron) - 1) - / sizeof(tetrahedron); - if (checkpbcs) { - // Increase the size by one pointer to a corresponding pbc point, - // read by point2pbcpt(). - pointsize = (point2pbcptindex + 1) * sizeof(tetrahedron); - } } else { // Increase the point size by two pointer, which are: // - a pointer to a tet, read by point2tet(); @@ -4513,18 +4634,14 @@ void tetgenmesh::initializepools() // 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 (inidcated by pointmarkindex) plus: + // 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)*sizeof(int); // Wrong for 64 bit system. pointsize = (pointmarkindex + 2 + (b->psc ? 1 : 0)) * sizeof(tetrahedron); - // Decide the wordtype used in vertex pool. - wtype = (sizeof(REAL) >= sizeof(tetrahedron)) ? - memorypool::FLOATINGPOINT : memorypool::POINTER; // Initialize the pool of vertices. - points = new memorypool(pointsize, b->vertexperblock, wtype, 0); + points = new memorypool(pointsize, b->vertexperblock, sizeof(REAL), 0); if (b->verbose) { printf(" Size of a point: %d bytes.\n", points->itembytes); @@ -4532,7 +4649,32 @@ void tetgenmesh::initializepools() // Initialize the infinite vertex. dummypoint = (point) new char[pointsize]; - setpointmark(dummypoint, -1); + // 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 @@ -4558,26 +4700,28 @@ void tetgenmesh::initializepools() 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 voulme bound is - // found, where the index is measured in REALs. Note that if the - // `b->regionattrib' flag is set, an additional attribute will be added. - volumeboundindex = elemattribindex + in->numberoftetrahedronattributes - + (b->regionattrib > 0); + // 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 (in->numberoftetrahedronattributes + b->regionattrib > 0) { + } 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, - memorypool::POINTER, 16); + tetrahedrons = new memorypool(elesize, b->tetrahedraperblock, sizeof(void *), + 16); if (b->verbose) { printf(" Size of a tetrahedron: %d (%d) bytes.\n", elesize, @@ -4594,7 +4738,7 @@ void tetgenmesh::initializepools() 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 (b->quality && checkconstraints) { + if (checkconstraints) { shsize = (areaboundindex + 1) * sizeof(REAL); } else { shsize = areaboundindex * sizeof(REAL); @@ -4604,13 +4748,17 @@ void tetgenmesh::initializepools() 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 + checkpbcs) * sizeof(shellface); + 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, - memorypool::POINTER, 8); + subfaces = new memorypool(shsize, b->shellfaceperblock, sizeof(void *), 8); if (b->verbose) { printf(" Size of a shellface: %d (%d) bytes.\n", shsize, @@ -4619,40 +4767,36 @@ void tetgenmesh::initializepools() // Initialize the pool of subsegments. The subsegment's record is same // with subface. - subsegs = new memorypool(shsize, b->shellfaceperblock, - memorypool::POINTER, 8); + subsegs = new memorypool(shsize, b->shellfaceperblock, sizeof(void *), 8); // Initialize the pool for tet-subseg connections. tet2segpool = new memorypool(6 * sizeof(shellface), b->shellfaceperblock, - memorypool::POINTER, 0); + sizeof(void *), 0); // Initialize the pool for tet-subface connections. tet2subpool = new memorypool(4 * sizeof(shellface), b->shellfaceperblock, - memorypool::POINTER, 0); + 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); - suppsteinerptlist = new arraypool(sizeof(point), 8); - - // Initialize arraypools for surface Bowyer-Watson algorithm. + // 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 pool for flips. - flippool = new memorypool(sizeof(badface), 1024, memorypool::POINTER, 0); + // Initialize the pools for flips. + flippool = new memorypool(sizeof(badface), 1024, sizeof(void *), 0); unflipqueue = new arraypool(sizeof(badface), 10); - // Initialize the arraypools for Bowyer-Watson algorithm. + // Initialize the arraypools for point insertion. cavetetlist = new arraypool(sizeof(triface), 10); cavebdrylist = new arraypool(sizeof(triface), 10); caveoldtetlist = new arraypool(sizeof(triface), 10); @@ -4668,9 +4812,148 @@ void tetgenmesh::initializepools() //// //// // 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. // @@ -4684,7 +4967,7 @@ REAL tetgenmesh::PI = 3.14159265358979323846264338327950288419716939937510582; // 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 retrun value indicates one of the following cases: // +// 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. // @@ -4693,6 +4976,10 @@ REAL tetgenmesh::PI = 3.14159265358979323846264338327950288419716939937510582; // // /////////////////////////////////////////////////////////////////////////////// +#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) { @@ -4709,14 +4996,14 @@ int tetgenmesh::tri_edge_2d(point A, point B, point C, point P, point Q, REAL n[3], len; // Calculate a lift point, saved in dummypoint. facenormal(A, B, C, n, 1, NULL); - len = sqrt(DOT(n, n)); + len = sqrt(dot(n, n)); if (len != 0) { n[0] /= len; n[1] /= len; n[2] /= len; - len = DIST(A, B); - len += DIST(B, C); - len += DIST(C, A); + 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]; @@ -4737,7 +5024,6 @@ int tetgenmesh::tri_edge_2d(point A, point B, point C, point P, point Q, sB = orient3d(P, Q, R, B); sC = orient3d(P, Q, R, C); - triedgcopcount++; if (sA < 0) { if (sB < 0) { @@ -5321,7 +5607,6 @@ int tetgenmesh::tri_edge_tail(point A,point B,point C,point P,point Q,point R, REAL s1, s2, s3; int z1; - triedgcount++; if (sP < 0) { if (sQ < 0) { // (--) disjoint @@ -5816,6 +6101,9 @@ void tetgenmesh::lu_solve(REAL lu[4][4], int n, int* ps, REAL* b, int N) // 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) @@ -5825,19 +6113,19 @@ REAL tetgenmesh::incircle3d(point pa, point pb, point pc, point pd) // 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); + area2[0] = dot(n1, n1); facenormal(pb, pa, pd, n2, 1, NULL); - area2[1] = DOT(n2, n2); + 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 = DIST(c, pd); + 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 = DIST(c, pc); + d = distance(c, pc); } else { // The four points are collinear. This case only happens on the boundary. return 0; // Return "not inside". @@ -5854,182 +6142,33 @@ REAL tetgenmesh::incircle3d(point pa, point pb, point pc, point pd) /////////////////////////////////////////////////////////////////////////////// // // -// 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 circumscirbed sphere of the four points. // +// facenormal() Calculate the normal of the face. // // // -// 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. // +// 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. // // // -// 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. // +// 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. // // // /////////////////////////////////////////////////////////////////////////////// -REAL tetgenmesh::insphere_s(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe) +void tetgenmesh::facenormal(point pa, point pb, point pc, REAL *n, int pivot, + REAL* lav) { - REAL sign; - - inspherecount++; - - sign = insphere(pa, pb, pc, pd, pe); - if (sign != 0.0) { - return sign; - } - - insphere_sos_count++; + REAL v1[3], v2[3], v3[3], *pv1, *pv2; + REAL L1, L2, L3; - // 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 hyperplance 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; - - inspherecount++; - - sign = orient4d(pa, pb, pc, pd, pe, - aheight, bheight, cheight, dheight, eheight); - if (sign != 0.0) { - return sign; - } - - insphere_sos_count++; - - // 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; -} - - -/////////////////////////////////////////////////////////////////////////////// -// // -// 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]; + 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) { @@ -6037,9 +6176,9 @@ void tetgenmesh::facenormal(point pa, point pb, point pc, REAL *n, int pivot, 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); + L1 = dot(v1, v1); + L2 = dot(v2, v2); + L3 = dot(v3, v3); // Sort the three edge lengths. if (L1 < L2) { if (L2 < L3) { @@ -6063,7 +6202,7 @@ void tetgenmesh::facenormal(point pa, point pb, point pc, REAL *n, int pivot, } // Calculate the face normal. - CROSS(pv1, pv2, n); + cross(pv1, pv2, n); // Inverse the direction; n[0] = -n[0]; n[1] = -n[1]; @@ -6097,9 +6236,8 @@ REAL tetgenmesh::shortdistance(REAL* p, REAL* e1, REAL* e2) v2[2] = p[2] - e1[2]; len = sqrt(dot(v1, v1)); -#ifdef SELF_CHECK assert(len != 0.0); -#endif + v1[0] /= len; v1[1] /= len; v1[2] /= len; @@ -6108,7 +6246,6 @@ REAL tetgenmesh::shortdistance(REAL* p, REAL* e1, REAL* e2) return sqrt(dot(v2, v2) - l_p * l_p); } - /////////////////////////////////////////////////////////////////////////////// // // // triarea() Return the area of a triangle. // @@ -6132,6 +6269,27 @@ REAL tetgenmesh::triarea(REAL* pa, REAL* pb, REAL* pc) 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 // @@ -6161,9 +6319,8 @@ REAL tetgenmesh::interiorangle(REAL* o, REAL* p1, REAL* p2, REAL* n) len1 = sqrt(dot(v1, v1)); len2 = sqrt(dot(v2, v2)); lenlen = len1 * len2; -#ifdef SELF_CHECK assert(lenlen != 0.0); -#endif + costheta = dot(v1, v2) / lenlen; if (costheta > 1.0) { costheta = 1.0; // Roundoff. @@ -6205,9 +6362,7 @@ void tetgenmesh::projpt2edge(REAL* p, REAL* e1, REAL* e2, REAL* prj) v2[2] = p[2] - e1[2]; len = sqrt(dot(v1, v1)); -#ifdef SELF_CHECK assert(len != 0.0); -#endif v1[0] /= len; v1[1] /= len; v1[2] /= len; @@ -6249,7 +6404,6 @@ void tetgenmesh::projpt2face(REAL* p, REAL* f1, REAL* f2, REAL* f3, REAL* prj) prj[2] = p[2] - dist * fnormal[2]; } - /////////////////////////////////////////////////////////////////////////////// // // // facedihedral() Return the dihedral angle (in radian) between two // @@ -6303,7 +6457,7 @@ 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, f2, i, j; + int f1 = 0, f2 = 0, i, j; vol = 0; // Check if the tet is valid or not. @@ -6359,7 +6513,7 @@ bool tetgenmesh::tetalldihedral(point pa, point pb, point pc, point pd, } } - // Calculate the consine of the dihedral angles of the edges. + // 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]. @@ -6371,6 +6525,7 @@ bool tetgenmesh::tetalldihedral(point pa, point pb, point pc, point pd, } 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) { @@ -6388,7 +6543,7 @@ bool tetgenmesh::tetalldihedral(point pa, point pb, point pc, point pd, /////////////////////////////////////////////////////////////////////////////// // // -// tetallnormal() Get the in-noramls of the four faces of a given tet. // +// 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 // @@ -6507,7 +6662,7 @@ REAL tetgenmesh::tetaspectratio(point pa, point pb, point pc, point pd) // // // 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 indicated the points are degenrate.// +// are not NULLs. Otherwise, return FALSE, the four points are co-planar. // // // /////////////////////////////////////////////////////////////////////////////// @@ -6559,7 +6714,59 @@ bool tetgenmesh::circumsphere(REAL* pa, REAL* pb, REAL* pc, REAL* pd, return true; } +/////////////////////////////////////////////////////////////////////////////// +// // +// orthosphere() Calulcate the orthosphere of four weighted points. // +// // +// A weighted point (p, P^2) can be interpreted as a sphere centered at the // +// point 'p' with a radius 'P'. The 'height' of 'p' is pheight = p[0]^2 + // +// p[1]^2 + p[2]^2 - P^2. // +// // +/////////////////////////////////////////////////////////////////////////////// + +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; +} /////////////////////////////////////////////////////////////////////////////// // // @@ -6607,7 +6814,55 @@ void tetgenmesh::planelineint(REAL* pa, REAL* pb, REAL* pc, REAL* e1, REAL* e2, } } +/////////////////////////////////////////////////////////////////////////////// +// // +// 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; +} /////////////////////////////////////////////////////////////////////////////// // // @@ -6656,75 +6911,168 @@ REAL tetgenmesh::tetprismvol(REAL* p0, REAL* p1, REAL* p2, REAL* p3) return fabs(vol[0]) + fabs(vol[1]) + fabs(vol[2]) + fabs(vol[3]); } -//// //// -//// //// -//// geom_cxx ///////////////////////////////////////////////////////////////// - -//// flip_cxx ///////////////////////////////////////////////////////////////// -//// //// -//// //// - /////////////////////////////////////////////////////////////////////////////// // // -// flippush() Push a face (possibly will be flipped) into flipstack. // -// // -// The face is marked. The flag is used to check the validity of the face on // -// its popup. Some other flips may change it already. // +// calculateabovepoint() Calculate a point above a facet in 'dummypoint'. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::flippush(badface*& fstack, triface* flipface) +bool tetgenmesh::calculateabovepoint(arraypool *facpoints, point *ppa, + point *ppb, point *ppc) { - badface *newflipface; + point *ppt, pa, pb, pc; + REAL v1[3], v2[3], n[3]; + REAL lab, len, A, area; + REAL x, y, z; + int i; - if (!facemarked(*flipface)) { - newflipface = (badface *) flippool->alloc(); - newflipface->tt = *flipface; - markface(newflipface->tt); - // Push this face into stack. - newflipface->nextitem = fstack; - fstack = newflipface; - } -} + ppt = (point *) fastlookup(facpoints, 0); + pa = *ppt; // a is the first point. + pb = pc = NULL; // Avoid compiler warnings. -/////////////////////////////////////////////////////////////////////////////// -// // -// flip23() Perform a 2-to-3 flip (face-to-edge flip). // -// // -// 'fliptets' is an array of tetrahedra. On input it contains two tets // -// [a,b,c,d] and [b,a,c,e]. It returns three new tets: [e,d,a,b], [e,d,b,c], // -// [e,d,c,a]. 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 'flipflag > 0', faces on the convex hull of the five vertices might // -// need to be flipped, e.g., for incremental DT construction or mesh quality // -// improvement. They will be queued in 'flipstack'. // -// // -// If 'flipflag = 1', it is in the process of incrmental flip DT algorithm, // -// and we assume that 'd' must be the newly inserted vertex. In such case, // -// only the link faces at 'd', i.e., three faces [a,b,e], [b,c,e], and [c,a, // -// e] needs to be queued, see [Edelsbrunner & Shah'1996] and [M\"ucke'1998]. // -// // -/////////////////////////////////////////////////////////////////////////////// + // 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; + } -void tetgenmesh::flip23(triface* fliptets, int hullflag, int flipflag, - int chkencflag) + // 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; - face checksh; - face checkseg; - badface *bface; // used by chkencflag point pa, pb, pc, pd, pe; - REAL volneg[2], volpos[3], vol_diff; // volumes of involved tet-prisms. + REAL attrib, volume; int dummyflag = 0; // range = {-1, 0, 1, 2}. int i; @@ -6752,16 +7100,12 @@ void tetgenmesh::flip23(triface* fliptets, int hullflag, int flipflag, } } - pa = org(fliptets[0]); + pa = org(fliptets[0]); pb = dest(fliptets[0]); pc = apex(fliptets[0]); pd = oppo(fliptets[0]); pe = oppo(fliptets[1]); - if (b->verbose > 3) { - printf(" flip 2-to-3: (%d, %d, %d, %d, %d)\n", pointmark(pa), - pointmark(pb), pointmark(pc), pointmark(pd), pointmark(pe)); - } flip23count++; // Get the outer boundary faces. @@ -6779,6 +7123,7 @@ void tetgenmesh::flip23(triface* fliptets, int hullflag, int flipflag, 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) { @@ -6803,6 +7148,15 @@ void tetgenmesh::flip23(triface* fliptets, int hullflag, int flipflag, } // 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. @@ -6836,7 +7190,8 @@ void tetgenmesh::flip23(triface* fliptets, int hullflag, int flipflag, setvertices(fliptets[2], pe, pd, pc, pa); // [e,d,c,a] * } - if (calc_tetprism_vol) { + 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); @@ -6859,8 +7214,8 @@ void tetgenmesh::flip23(triface* fliptets, int hullflag, int flipflag, volneg[1] = tetprismvol(pb, pa, pc, pe); } vol_diff = volpos[0] + volpos[1] + volpos[2] - volneg[0] - volneg[1]; - tetprism_vol_sum += vol_diff; // Update the total sum. - } // if (check_tetprism_vol_diff) + fc->tetprism_vol_sum += vol_diff; // Update the total sum. + } // Bond three new tets together. for (i = 0; i < 3; i++) { @@ -6869,43 +7224,36 @@ void tetgenmesh::flip23(triface* fliptets, int hullflag, int flipflag, } // Bond to top outer boundary faces (at [a,b,c,d]). for (i = 0; i < 3; i++) { - enextesym(fliptets[i], newface); - eprevself(newface); // At edges [b,a], [c,b], [a,c]. + 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++) { - eprevesym(fliptets[i], newface); - enextself(newface); // At edges [a,b], [b,c], [c,a]. + edestoppo(fliptets[i], newface); // At edges [a,b], [b,c], [c,a]. bond(newface, botcastets[i]); } - // Bond 15 subsegments if there are. 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++) { - tsspivot1(topcastets[i], checkseg); - if (checkseg.sh != NULL) { - enextesym(fliptets[i], newface); - eprevself(newface); // At edges [b,a], [c,b], [a,c]. + 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 (chkencflag & 1) { - // Skip it if it has already queued. - if (!smarktest2ed(checkseg)) { - bface = (badface *) badsubsegs->alloc(); - bface->ss = checkseg; - smarktest2(bface->ss); // Only queue it once. - bface->forg = sorg(checkseg); // An alive badface. - } + 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); - tsspivot1(casface, checkseg); - if (checkseg.sh != NULL) { + eprev(topcastets[i], casface); + if (issubseg(casface)) { + tsspivot1(casface, checkseg); enext(fliptets[i], newface); tssbond1(newface, checkseg); sstbond1(checkseg, newface); @@ -6913,22 +7261,16 @@ void tetgenmesh::flip23(triface* fliptets, int hullflag, int flipflag, eprevself(newface); tssbond1(newface, checkseg); sstbond1(checkseg, newface); - if (chkencflag & 1) { - // Skip it if it has already queued. - if (!smarktest2ed(checkseg)) { - bface = (badface *) badsubsegs->alloc(); - bface->ss = checkseg; - smarktest2(bface->ss); // Only queue it once. - bface->forg = sorg(checkseg); // An alive badface. - } + 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); - tsspivot1(casface, checkseg); - if (checkseg.sh != NULL) { + if (issubseg(casface)) { + tsspivot1(casface, checkseg); eprev(fliptets[i], newface); tssbond1(newface, checkseg); sstbond1(checkseg, newface); @@ -6936,75 +7278,53 @@ void tetgenmesh::flip23(triface* fliptets, int hullflag, int flipflag, enextself(newface); tssbond1(newface, checkseg); sstbond1(checkseg, newface); - if (chkencflag & 1) { - // Skip it if it has already queued. - if (!smarktest2ed(checkseg)) { - bface = (badface *) badsubsegs->alloc(); - bface->ss = checkseg; - smarktest2(bface->ss); // Only queue it once. - bface->forg = sorg(checkseg); // An alive badface. - } + if (fc->chkencflag & 1) { + enqueuesubface(badsubsegs, &checkseg); } } } - } + } // if (checksubsegflag) - // Bond 6 subfaces if there are. if (checksubfaceflag) { - for (i = 0; i < 3; i++) { - tspivot(topcastets[i], checksh); - if (checksh.sh != NULL) { - enextesym(fliptets[i], newface); - eprevself(newface); // At edge [b,a], [c,b], [a,c]. + // 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 (chkencflag & 2) { - if (!smarktest2ed(checksh)) { - bface = (badface *) badsubfacs->alloc(); - bface->ss = checksh; - smarktest2(bface->ss); // Only queue it once. - bface->forg = sorg(checksh); // An alive badface - } + if (fc->chkencflag & 2) { + enqueuesubface(badsubfacs, &checksh); } } } for (i = 0; i < 3; i++) { - tspivot(botcastets[i], checksh); - if (checksh.sh != NULL) { - eprevesym(fliptets[i], newface); - enextself(newface); // At edge [a,b], [b,c], [c,a] + if (issubface(botcastets[i])) { + tspivot(botcastets[i], checksh); + edestoppo(fliptets[i], newface); sesymself(checksh); tsbond(newface, checksh); - if (chkencflag & 2) { - if (!smarktest2ed(checksh)) { - bface = (badface *) badsubfacs->alloc(); - bface->ss = checksh; - smarktest2(bface->ss); // Only queue it once. - bface->forg = sorg(checksh); // An alive badface - } + if (fc->chkencflag & 2) { + enqueuesubface(badsubfacs, &checksh); } } } - } + } // if (checksubfaceflag) - if (chkencflag & 4) { + if (fc->chkencflag & 4) { // Put three new tets into check list. for (i = 0; i < 3; i++) { - if (!marktest2ed(fliptets[i])) { - bface = (badface *) badtetrahedrons->alloc(); - bface->tt = fliptets[i]; - marktest2(bface->tt); - bface->forg = org(fliptets[i]); - } + enqueuetetrahedron(&(fliptets[i])); } } // Update the point-to-tet map. - setpoint2tet(pa, encode(fliptets[0])); - setpoint2tet(pb, encode(fliptets[0])); - setpoint2tet(pc, encode(fliptets[1])); - setpoint2tet(pd, encode(fliptets[0])); - setpoint2tet(pe, encode(fliptets[0])); + 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) { @@ -7037,19 +7357,15 @@ void tetgenmesh::flip23(triface* fliptets, int hullflag, int flipflag, } } - if (flipflag > 0) { + if (fc->enqflag > 0) { // Queue faces which may be locally non-Delaunay. - //pd = dest(fliptets[0]); // 'd' may be a new vertex. for (i = 0; i < 3; i++) { eprevesym(fliptets[i], newface); - //flippush(flipstack, &newface, pd); flippush(flipstack, &newface); } - if (flipflag > 1) { - //pe = org(fliptets[0]); + if (fc->enqflag > 1) { for (i = 0; i < 3; i++) { enextesym(fliptets[i], newface); - //flippush(flipstack, &newface, pe); flippush(flipstack, &newface); } } @@ -7062,9 +7378,10 @@ void tetgenmesh::flip23(triface* fliptets, int hullflag, int flipflag, // // // flip32() Perform a 3-to-2 flip (edge-to-face flip). // // // -// 'fliptets' is an array of three tetrahedra. On input, it contains three // -// tets: [e,d,a,b], [e,d,b,c], and [e,d,c,a]. It returns tw tets: [a,b,c,d], // -// and [b,a,c,e]. The edge [e,d] is replaced by the face [a,b,c]. // +// '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: // @@ -7075,35 +7392,32 @@ void tetgenmesh::flip23(triface* fliptets, int hullflag, int flipflag, // three old tets counterclockwisely (right-hand rule) until a or b // // is in c's position. // // // -// If 'flipflag > 0', faces on the convex hull of the five vertices might // -// need to be flipped, e.g., for incremental DT construction or mesh quality // -// improvement. They will be queued in 'flipstack'. // -// // -// If 'flipflag = 1', it is in the process of incrmental flip DT algorithm, // -// and we assume that 'a' must be the newly inserted vertex. In such case, // -// only the link faces at 'a', i.e., two faces [c,b,d] and [b,c,e] needs to // -// be queued, refer to [Edelsbrunner & Shah'1996] and [M\"ucke'1998]. // +// 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, int flipflag, - int chkencflag) +void tetgenmesh::flip32(triface* fliptets, int hullflag, flipconstraints *fc) { triface topcastets[3], botcastets[3]; triface newface, casface; - face checksh; + face flipshs[3]; face checkseg; - badface *bface; // used by chkencflag point pa, pb, pc, pd, pe; - REAL volneg[3], volpos[2], vol_diff; // volumes of involved tet-prisms. + REAL attrib, volume; int dummyflag = 0; // Rangle = {-1, 0, 1, 2} - int i; - - // For 2-to-2 flip (subfaces). - face flipshs[3], flipfaces[2]; - point rempt; - int spivot = -1, scount = 0; + int spivot = -1, scount = 0; // for flip22() + int t1ver; + int i, j; if (hullflag > 0) { // Check if e is 'dummypoint'. @@ -7143,36 +7457,24 @@ void tetgenmesh::flip32(triface* fliptets, int hullflag, int flipflag, pd = dest(fliptets[0]); pe = org(fliptets[0]); - if (b->verbose > 3) { - printf(" flip 3-to-2: (%d, %d, %d, %d, %d)\n", pointmark(pa), - pointmark(pb), pointmark(pc), pointmark(pd), pointmark(pe)); - } flip32count++; // Get the outer boundary faces. for (i = 0; i < 3; i++) { - enextesym(fliptets[i], casface); - eprevself(casface); + eorgoppo(fliptets[i], casface); fsym(casface, topcastets[i]); } for (i = 0; i < 3; i++) { - eprevesym(fliptets[i], casface); - enextself(casface); + edestoppo(fliptets[i], casface); fsym(casface, botcastets[i]); } if (checksubfaceflag) { // Check if there are interior subfaces at the edge [e,d]. - spivot = -1; - scount = 0; for (i = 0; i < 3; i++) { tspivot(fliptets[i], flipshs[i]); if (flipshs[i].sh != NULL) { - if (b->verbose > 3) { - printf(" Found an interior subface (%d, %d, %d).\n", - pointmark(sorg(flipshs[i])), pointmark(sdest(flipshs[i])), - pointmark(sapex(flipshs[i]))); - } + // Found an interior subface. stdissolve(flipshs[i]); // Disconnect the sub-tet bond. scount++; } else { @@ -7208,7 +7510,28 @@ void tetgenmesh::flip32(triface* fliptets, int hullflag, int flipflag, 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); @@ -7232,14 +7555,15 @@ void tetgenmesh::flip32(triface* fliptets, int hullflag, int flipflag, esymself(fliptets[0]); // Adjust abec -> bace. esymself(fliptets[1]); - // The hullsize does not changle. + // The hullsize does not change. } } else { setvertices(fliptets[0], pa, pb, pc, pd); setvertices(fliptets[1], pb, pa, pc, pe); } - if (calc_tetprism_vol) { + 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); @@ -7262,7 +7586,7 @@ void tetgenmesh::flip32(triface* fliptets, int hullflag, int flipflag, volpos[1] = 0.; } vol_diff = volpos[0] + volpos[1] - volneg[0] - volneg[1] - volneg[2]; - tetprism_vol_sum += vol_diff; // Update the total sum. + fc->tetprism_vol_sum += vol_diff; // Update the total sum. } // Bond abcd <==> bace. @@ -7281,133 +7605,89 @@ void tetgenmesh::flip32(triface* fliptets, int hullflag, int flipflag, } if (checksubsegflag) { - // Bond segments to new (flipped) tets. - for (i = 0; i < 3; i++) { - tsspivot1(topcastets[i], checkseg); - if (checkseg.sh != NULL) { + // 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]); - if (chkencflag & 1) { - // Skip it if it has already queued. - if (!smarktest2ed(checkseg)) { - bface = (badface *) badsubsegs->alloc(); - bface->ss = checkseg; - smarktest2(bface->ss); // Only queue it once. - bface->forg = sorg(checkseg); // An alive badface. - } + 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++) { + for (i = 0; i < 3; i++) { // edges b->d, c->d, a->d. esym(fliptets[0], newface); - eprevself(newface); // edge b->d, c->d, a->d. - enext(topcastets[i], casface); - tsspivot1(casface, checkseg); - if (checkseg.sh != NULL) { + eprevself(newface); + enext(topcastets[i], casface); + if (issubseg(casface)) { + tsspivot1(casface, checkseg); tssbond1(newface, checkseg); sstbond1(checkseg, newface); - if (chkencflag & 1) { - // Skip it if it has already queued. - if (!smarktest2ed(checkseg)) { - bface = (badface *) badsubsegs->alloc(); - bface->ss = checkseg; - smarktest2(bface->ss); // Only queue it once. - bface->forg = sorg(checkseg); // An alive badface. - } + if (fc->chkencflag & 1) { + enqueuesubface(badsubsegs, &checkseg); } } enextself(fliptets[0]); } - // Process the bottom tet bace. - for (i = 0; i < 3; i++) { - tsspivot1(botcastets[i], checkseg); - if (checkseg.sh != NULL) { - tssbond1(fliptets[1], checkseg); - sstbond1(checkseg, fliptets[1]); - if (chkencflag & 1) { - // Skip it if it has already queued. - if (!smarktest2ed(checkseg)) { - bface = (badface *) badsubsegs->alloc(); - bface->ss = checkseg; - smarktest2(bface->ss); // Only queue it once. - bface->forg = sorg(checkseg); // An alive badface. - } - } - } - eprevself(fliptets[1]); - } // The three bot edges. - for (i = 0; i < 3; i++) { + for (i = 0; i < 3; i++) { // edges b<-e, c<-e, a<-e. esym(fliptets[1], newface); - enextself(newface); // edge b<-e, c<-e, a<-e. + enextself(newface); eprev(botcastets[i], casface); - tsspivot1(casface, checkseg); - if (checkseg.sh != NULL) { + if (issubseg(casface)) { + tsspivot1(casface, checkseg); tssbond1(newface, checkseg); sstbond1(checkseg, newface); - if (chkencflag & 1) { - // Skip it if it has already queued. - if (!smarktest2ed(checkseg)) { - bface = (badface *) badsubsegs->alloc(); - bface->ss = checkseg; - smarktest2(bface->ss); // Only queue it once. - bface->forg = sorg(checkseg); // An alive badface. - } + 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++) { - tspivot(topcastets[i], checksh); - if (checksh.sh != NULL) { - esym(fliptets[0], newface); // At edge [b,a], [c,b], [a,c] + 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 (chkencflag & 2) { - if (!smarktest2ed(checksh)) { - bface = (badface *) badsubfacs->alloc(); - bface->ss = checksh; - smarktest2(bface->ss); // Only queue it once. - bface->forg = sorg(checksh); // An alive badface - } + if (fc->chkencflag & 2) { + enqueuesubface(badsubfacs, &checksh); } } enextself(fliptets[0]); } // Bond the bottom three casing subfaces. - for (i = 0; i < 3; i++) { - tspivot(botcastets[i], checksh); - if (checksh.sh != NULL) { - esym(fliptets[1], newface); // // At edge [a,b], [b,c], [c,a] + 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 (chkencflag & 2) { - if (!smarktest2ed(checksh)) { - bface = (badface *) badsubfacs->alloc(); - bface->ss = checksh; - smarktest2(bface->ss); // Only queue it once. - bface->forg = sorg(checksh); // An alive badface - } + if (fc->chkencflag & 2) { + enqueuesubface(badsubfacs, &checksh); } } eprevself(fliptets[1]); } - } - if (checksubfaceflag) { if (scount > 0) { - assert(spivot != -1); // spivot = i, in {0,1,2} + 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, chkencflag); + 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]. @@ -7428,49 +7708,9 @@ void tetgenmesh::flip32(triface* fliptets, int hullflag, int flipflag, sesymself(flipfaces[0]); tsbond(topcastets[0], flipfaces[0]); } else { - // Found two subfaces are duplicated at the same tet face. - // Due to the same reason explained below. - assert(sapex(checksh) == sapex(flipfaces[0])); - sspivot(checksh, checkseg); - assert(checkseg.sh == NULL); - // Delete the two duplicated subfaces. - rempt = sapex(checksh); - if (b->verbose > 2) { - printf(" Remove vertex %d from surface.\n", pointmark(rempt)); - } - // Make sure we do not delete a Steiner points in segment. - assert(pointtype(rempt) == FREEFACETVERTEX); - setpointtype(rempt, FREEVOLVERTEX); - // Re-use flipshs. - //spivot(checksh, flipshs[0]); - flipshs[0] = checksh; - spivotself(flipshs[0]); - if (flipshs[0].sh == flipfaces[0].sh) { - sesym(checksh, flipshs[0]); - spivotself(flipshs[0]); - } - assert(flipshs[0].sh != flipfaces[0].sh); - //spivot(flipfaces[0], flipshs[1]); - flipshs[1] = flipfaces[0]; - spivotself(flipshs[1]); - if (flipshs[1].sh == checksh.sh) { - sesym(flipfaces[0], flipshs[1]); - spivotself(flipshs[1]); - } - assert(flipshs[1].sh != checksh.sh); - // Bond the two subfaces together. - sbond(flipshs[0], flipshs[1]); - // Detach 'checksh' from the adjacent tets. - tsdissolve(topcastets[0]); - fsymself(topcastets[0]); - tsdissolve(topcastets[0]); - // Delete the two duplicated subfaces. - shellfacedealloc(subfaces, checksh.sh); - shellfacedealloc(subfaces, flipfaces[0].sh); - } - // // Push topcastets[0] into queue for checking new sliver. - // assert(oppo(topcastets[0]) != dummypoint); - // flippush(&(topcastets[0]), oppo(topcastets[0])); + // 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]); @@ -7482,74 +7722,24 @@ void tetgenmesh::flip32(triface* fliptets, int hullflag, int flipflag, sesymself(flipfaces[1]); tsbond(botcastets[0], flipfaces[1]); } else { - // Found two subfaces are duplicated at the same tet face. - assert(sapex(checksh) == sapex(flipfaces[1])); - // This happens in case when a Steiner point is not exactly coplanar - // or collinear with the subface or subedge where it was added. - // See figs illustrated in 2011-11-09. - sspivot(checksh, checkseg); - assert(checkseg.sh == NULL); - // Since the edge [p,q] is not a segment, both subfaces must be - // removed. The effect is that the Steiner point is removed from - // the surface triangulation. - // Delete the two duplicated subfaces. - rempt = sapex(checksh); - if (b->verbose > 2) { - printf(" Remove vertex %d from surface.\n", pointmark(rempt)); - } - // Make sure we do not delete a Steiner points in segment. - assert(pointtype(rempt) == FREEFACETVERTEX); - setpointtype(rempt, FREEVOLVERTEX); - // Re-use flipshs. - //spivot(checksh, flipshs[0]); - flipshs[0] = checksh; - spivotself(flipshs[0]); - if (flipshs[0].sh == flipfaces[1].sh) { - sesym(checksh, flipshs[0]); - spivotself(flipshs[0]); - } - assert(flipshs[0].sh != flipfaces[1].sh); - //spivot(flipfaces[1], flipshs[1]); - flipshs[1] = flipfaces[1]; - spivotself(flipshs[1]); - if (flipshs[1].sh == checksh.sh) { - sesym(flipfaces[1], flipshs[1]); - spivotself(flipshs[1]); - } - assert(flipshs[1].sh != checksh.sh); - // Bond the two subfaces together. - sbond(flipshs[0], flipshs[1]); - // Detach 'checksh' from the adjacent tets. - tsdissolve(botcastets[0]); - fsymself(botcastets[0]); - tsdissolve(botcastets[0]); - // Delete the two duplicated subfaces. - shellfacedealloc(subfaces, checksh.sh); - shellfacedealloc(subfaces, flipfaces[1].sh); + // An invalid 2-to-2 flip. Report a bug. + terminatetetgen(this, 2); } - // // Push botcastets[0] into queue for checking new sliver. - // assert(oppo(botcastets[0]) != dummypoint); - // flippush(&(botcastets[0]), oppo(botcastets[0])); - } - } + } // if (scount > 0) + } // if (checksubfaceflag) - if (chkencflag & 4) { + if (fc->chkencflag & 4) { // Put two new tets into check list. for (i = 0; i < 2; i++) { - if (!marktest2ed(fliptets[i])) { - bface = (badface *) badtetrahedrons->alloc(); - bface->tt = fliptets[i]; - marktest2(bface->tt); - bface->forg = org(fliptets[i]); - } + enqueuetetrahedron(&(fliptets[i])); } } - setpoint2tet(pa, encode(fliptets[0])); - setpoint2tet(pb, encode(fliptets[0])); - setpoint2tet(pc, encode(fliptets[0])); - setpoint2tet(pd, encode(fliptets[0])); - setpoint2tet(pe, encode(fliptets[1])); + 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) { @@ -7572,14 +7762,14 @@ void tetgenmesh::flip32(triface* fliptets, int hullflag, int flipflag, } } - if (flipflag > 0) { + 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 (flipflag > 1) { + if (fc->enqflag > 1) { //pb = dest(fliptets[0]); eprevesym(fliptets[0], newface); flippush(flipstack, &newface); @@ -7605,28 +7795,28 @@ void tetgenmesh::flip32(triface* fliptets, int hullflag, int flipflag, // 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 four vertices may be 'duumypoint'. // -// The 'hullsize' may be changed. // +// 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, int flipflag, - int chkencflag) +void tetgenmesh::flip41(triface* fliptets, int hullflag, flipconstraints *fc) { triface topcastets[3], botcastet; triface newface, neightet; face flipshs[4]; - face checksh; - face checkseg; point pa, pb, pc, pd, pp; - badface *bface; // used by chkencflag - REAL volneg[4], volpos[1], vol_diff; // volumes of involved tet-prisms. int dummyflag = 0; // in {0, 1, 2, 3, 4} int spivot = -1, scount = 0; + int t1ver; int i; pa = org(fliptets[3]); @@ -7635,11 +7825,7 @@ void tetgenmesh::flip41(triface* fliptets, int hullflag, int flipflag, pd = dest(fliptets[0]); pp = org(fliptets[0]); // The removing vertex. - if (b->verbose > 3) { - printf(" flip 4-to-1: (%d, %d, %d, %d, %d)\n", pointmark(pa), - pointmark(pb), pointmark(pc), pointmark(pd), pointmark(pp)); - } - // flip41count++; + flip41count++; // Get the outer boundary faces. for (i = 0; i < 3; i++) { @@ -7652,8 +7838,6 @@ void tetgenmesh::flip41(triface* fliptets, int hullflag, int flipflag, if (checksubfaceflag) { // Check if there are three subfaces at 'p'. // Re-use 'newface'. - spivot = -1; - scount = 0; 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]); @@ -7683,9 +7867,11 @@ void tetgenmesh::flip41(triface* fliptets, int hullflag, int flipflag, } } // 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) { @@ -7700,19 +7886,20 @@ void tetgenmesh::flip41(triface* fliptets, int hullflag, int flipflag, fliptets[0].tet[9] = NULL; } } - // Delete the other three tets. for (i = 1; i < 4; i++) { tetrahedrondealloc(fliptets[i].tet); } - // Mark the point pp as unused. - setpointtype(pp, UNUSEDVERTEX); - unuverts++; + 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 four vertices may be 'dummypoint'. + // One of the five vertices may be 'dummypoint'. if (pa == dummypoint) { // pa is dummypoint. setvertices(fliptets[0], pc, pb, pd, pa); @@ -7733,17 +7920,26 @@ void tetgenmesh::flip41(triface* fliptets, int hullflag, int flipflag, dummyflag = 4; } else { setvertices(fliptets[0], pa, pb, pc, pd); - dummyflag = 0; + if (pp == dummypoint) { + dummyflag = -1; + } else { + dummyflag = 0; + } } if (dummyflag > 0) { - // We delete 3 hull tets, and create 1 hull tet. + // 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 (calc_tetprism_vol) { + if (fc->remove_ndelaunay_edge) { // calc_tetprism_vol + REAL volneg[4], volpos[1], vol_diff; if (dummyflag > 0) { if (pa == dummypoint) { volneg[0] = 0.; @@ -7767,6 +7963,12 @@ void tetgenmesh::flip41(triface* fliptets, int hullflag, int flipflag, 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); @@ -7775,7 +7977,7 @@ void tetgenmesh::flip41(triface* fliptets, int hullflag, int flipflag, volpos[0] = tetprismvol(pa, pb, pc, pd); } vol_diff = volpos[0] - volneg[0] - volneg[1] - volneg[2] - volneg[3]; - tetprism_vol_sum += vol_diff; // Update the total sum. + fc->tetprism_vol_sum += vol_diff; // Update the total sum. } // Bond the new tet to adjacent tets. @@ -7787,40 +7989,29 @@ void tetgenmesh::flip41(triface* fliptets, int hullflag, int flipflag, 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]. - tsspivot1(newface, checkseg); - if (checkseg.sh != NULL) { + 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 (chkencflag & 1) { - // Skip it if it has already queued. - if (!smarktest2ed(checkseg)) { - bface = (badface *) badsubsegs->alloc(); - bface->ss = checkseg; - smarktest2(bface->ss); // Only queue it once. - bface->forg = sorg(checkseg); // An alive badface. - } + if (fc->chkencflag & 1) { + enqueuesubface(badsubsegs, &checkseg); } } enextself(fliptets[0]); } for (i = 0; i < 3; i++) { - tsspivot1(topcastets[i], checkseg); // At edges [a,b],[b,c],[c,a]. - if (checkseg.sh != NULL) { + 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 (chkencflag & 1) { - // Skip it if it has already queued. - if (!smarktest2ed(checkseg)) { - bface = (badface *) badsubsegs->alloc(); - bface->ss = checkseg; - smarktest2(bface->ss); // Only queue it once. - bface->forg = sorg(checkseg); // An alive badface. - } + if (fc->chkencflag & 1) { + enqueuesubface(badsubsegs, &checkseg); } } enextself(fliptets[0]); @@ -7828,40 +8019,29 @@ void tetgenmesh::flip41(triface* fliptets, int hullflag, int flipflag, } if (checksubfaceflag) { + face checksh; // Bond 4 subfaces (at faces of [a,b,c,d]) if there are. for (i = 0; i < 3; i++) { - tspivot(topcastets[i], checksh); // At faces [a,b,d],[b,c,d],[c,a,d] - if (checksh.sh != NULL) { + 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 (chkencflag & 2) { - if (!smarktest2ed(checksh)) { - bface = (badface *) badsubfacs->alloc(); - bface->ss = checksh; - smarktest2(bface->ss); // Only queue it once. - bface->forg = sorg(checksh); // An alive badface - } + if (fc->chkencflag & 2) { + enqueuesubface(badsubfacs, &checksh); } } enextself(fliptets[0]); } - tspivot(botcastet, checksh); // At face [b,a,c] - if (checksh.sh != NULL) { + if (issubface(botcastet)) { + tspivot(botcastet, checksh); // At face [b,a,c] sesymself(checksh); tsbond(fliptets[0], checksh); - if (chkencflag & 2) { - if (!smarktest2ed(checksh)) { - bface = (badface *) badsubfacs->alloc(); - bface->ss = checksh; - smarktest2(bface->ss); // Only queue it once. - bface->forg = sorg(checksh); // An alive badface - } + if (fc->chkencflag & 2) { + enqueuesubface(badsubfacs, &checksh); } } - } - if (checksubfaceflag) { if (spivot >= 0) { // Perform a 3-to-1 flip in surface triangulation. // Depending on the value of 'spivot', the three subfaces are: @@ -7895,30 +8075,26 @@ void tetgenmesh::flip41(triface* fliptets, int hullflag, int flipflag, } // if (spivot > 0) } // if (checksubfaceflag) - if (chkencflag & 4) { - // Put the new tet into check list. - if (!marktest2ed(fliptets[0])) { - bface = (badface *) badtetrahedrons->alloc(); - bface->tt = fliptets[0]; - marktest2(bface->tt); - bface->forg = org(fliptets[0]); - } + if (fc->chkencflag & 4) { + enqueuetetrahedron(&(fliptets[0])); } // Update the point-to-tet map. - setpoint2tet(pa, encode(fliptets[0])); - setpoint2tet(pb, encode(fliptets[0])); - setpoint2tet(pc, encode(fliptets[0])); - setpoint2tet(pd, encode(fliptets[0])); + 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 (flipflag > 0) { + if (fc->enqflag > 0) { // Queue faces which may be locally non-Delaunay. - for (i = 0; i < 3; i++) { - esym(fliptets[0], newface); - flippush(flipstack, &newface); - enextself(fliptets[0]); + 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]); + } } - flippush(flipstack, &(fliptets[0])); } recenttet = fliptets[0]; @@ -7926,7 +8102,7 @@ void tetgenmesh::flip41(triface* fliptets, int hullflag, int flipflag, /////////////////////////////////////////////////////////////////////////////// // // -// flipnm() Try to flip an edge through a sequence of elementary flips. // +// 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.,// @@ -7951,7 +8127,6 @@ void tetgenmesh::flip41(triface* fliptets, int hullflag, int flipflag, // - Neither a nor b is 'dummypoint'. // // - [a,b] must not be a segment. // // // -// // /////////////////////////////////////////////////////////////////////////////// int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, @@ -7959,27 +8134,19 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, { triface fliptets[3], spintet, flipedge; triface *tmpabtets, *parytet; - face checksh; - face checkseg, *paryseg; point pa, pb, pc, pd, pe, pf; - point tmppts[3]; - REAL abovept[3]; - REAL ori, ori1, ori2; + REAL ori; + int hullflag, hulledgeflag; int reducflag, rejflag; - int hullflag; int reflexlinkedgecount; int edgepivot; int n1, nn; + int t1ver; int i, j; pa = org(abtets[0]); pb = dest(abtets[0]); - if (b->verbose > 2) { - printf(" flipnm(%d): (%d, %d) - n(%d), e(%d).\n", level, pointmark(pa), - pointmark(pb), n, abedgepivot); - } - if (n > 3) { // Try to reduce the size of the Star(ab) by flipping a face in it. reflexlinkedgecount = 0; @@ -7987,9 +8154,7 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, for (i = 0; i < n; i++) { // Let the face of 'abtets[i]' be [a,b,c]. if (checksubfaceflag) { - // Do not flip this face if it is a constraining face. - tspivot(abtets[i], checksh); - if (checksh.sh != NULL) { + if (issubface(abtets[i])) { continue; // Skip a subface. } } @@ -7998,32 +8163,20 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, (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)) { - // [a,b,c] is a hull face, it is not flipable. - continue; + continue; // [a,b,c] is a hull face. } - if (checkinverttetflag) { - // The mesh contains inverted (or degenerated) elements. - // Only do check if both elements are valid. - if (pc != dummypoint) { - ori = orient3d(pa, pb, pc, pd); - if (ori < 0) { - ori = orient3d(pb, pa, pc, pe); - } - if (ori >= 0) { - continue; // An invalid tet. - } - } else { - continue; - } - } // if (checkinverttetflag) - reducflag = 0; // Not reducible. + + // 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) { @@ -8037,8 +8190,11 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, } else if (ori == 0) { // [a,b] is flat. if (n == 4) { - // The "flat" tet can be removed immedately by a 3-to-2 flip. + // 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); } } } @@ -8063,16 +8219,27 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, 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-palanar case). + 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 != NULL) { + if (fc->checkflipeligibility) { // Check if the flip can be performed. rejflag = checkflipeligibility(1, pa, pb, pc, pd, pe, level, abedgepivot, fc); @@ -8081,7 +8248,7 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, // Do flip: [a,b,c] => [e,d]. fliptets[0] = abtets[i]; fsym(fliptets[0], fliptets[1]); // abtets[i-1]. - flip23(fliptets, hullflag, 0, 0); + 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])' @@ -8099,21 +8266,19 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, // [n-2] |___________| [n-2] |___________| // [n-1] |___________| [n-1] |_[i]_2-t-3_| // - eprevself(fliptets[0]); - esymself(fliptets[0]); - enextself(fliptets[0]); // [a,b,e,d] + edestoppoself(fliptets[0]); // [a,b,e,d] // Increase the counter of this new tet (it is in Star(ab)). - increaseelemcounter(fliptets[0]); //marktest(fliptets[0]); + 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 remebers the vertex 'c' (in 'abtets[n-1].tet'), and - // (ii) it remebers the position [i] where this flip took place. + // (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 remebered. + //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. @@ -8134,27 +8299,28 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, // Star(ab) is reduced. Try to flip the edge [a,b]. nn = flipnm(abtets, n - 1, level, abedgepivot, fc); - if (nn > 2) { + 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 degenrated + // 'ori == 0' means that the previous flip created a degenerated // tet. It must be removed. - // Remeber that 'abtets[i-1]' is [a,b,e,d]. We can use it to + // 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] - eprevself(fliptets[0]); - esymself(fliptets[0]); - enextself(fliptets[0]); // [e,d,a,b] + 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, 0, 0); + flip32(fliptets, hullflag, fc); // Marktest the two restored tets in Star(ab). for (j = 0; j < 2; j++) { - increaseelemcounter(fliptets[j]); //marktest(fliptets[j]); + increaseelemcounter(fliptets[j]); } // Expand the array 'abtets', maintain the original order. for (j = n - 2; j>= i; j--) { @@ -8170,30 +8336,17 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, // Pop two (flipped) tets from the stack. cavetetlist->objects -= 2; } - } // if (upflip || (ori == 0)) + } // if (unflip || (ori == 0)) } // if (nn > 2) - if (nn == 2) { //if ((nn == 2) || !fullsearch) { - // The edge has been flipped. - return nn; - } if (!fc->unflip) { // The flips are not reversed. The current Star(ab) can not be - // further reduced. Return its size (# of tets). + // further reduced. Return its current size (# of tets). return nn; } // unflip is set. // Continue the search for flips. - } else { - if (b->verbose > 2) { - printf(" -- Reject a 2-to-3 flip at star face (%d, %d, %d)", - pointmark(pa), pointmark(pb), pointmark(pc)); - printf(", link (%d)\n", level); - } - if (fc != NULL) { - fc->rejf23count++; - } - } // if (rejflag) + } } // if (reducflag) } // i @@ -8202,16 +8355,6 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, // There are reflex edges in the Link(ab). if (((b->fliplinklevel < 0) && (level < autofliplinklevel)) || ((b->fliplinklevel >= 0) && (level < b->fliplinklevel))) { - // Record the largest level. - if ((level + 1) > maxfliplinklevel) { - maxfliplinklevel = level + 1; - } - if (fc != NULL) { - // Increase the link level counter. - if ((level + 1) > fc->maxflippedlinklevelcount) { - fc->maxflippedlinklevelcount = level + 1; - } - } // 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. @@ -8221,25 +8364,14 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, } pc = apex(abtets[i]); if (pc == dummypoint) { - continue; // [a,b,dummypoint] is a hull edge. + 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. } - if (checkinverttetflag) { - // The mesh contains inverted (or degenerated) elements. - // Only do check if both elements are valid. - // assert(pc != dummypoint); - ori = orient3d(pa, pb, pc, pd); - if (ori < 0) { - ori = orient3d(pb, pa, pc, pe); - } - if (ori >= 0) { - continue; // An invalid tet. - } - } // if (checkinverttetflag) + edgepivot = 0; // No edge is selected yet. @@ -8265,21 +8397,15 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, // An edge is selected. if (checksubsegflag) { // Do not flip it if it is a segment. - tsspivot1(flipedge, checkseg); - if (checkseg.sh != NULL) { - if (b->verbose > 2) { - printf(" -- Can't flip a link(%d) segment (%d, %d).\n", - level, pointmark(org(flipedge)), pointmark(dest(flipedge))); - } - if (fc != NULL) { - fc->encsegcount++; - if (fc->collectencsegflag) { - if (!sinfected(checkseg)) { - // Queue this segment in list. - sinfect(checkseg); - caveencseglist->newindex((void **) &paryseg); - *paryseg = checkseg; - } + 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; @@ -8294,7 +8420,7 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, spintet = flipedge; while (1) { n1++; - j += (elemcounter(spintet)); //if (marktested(spintet)) j++; + j += (elemcounter(spintet)); fnextself(spintet); if (spintet.tet == flipedge.tet) break; } @@ -8306,14 +8432,8 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, // Only two tets can be marktested. assert(j == 2); - flipstarcount++; - // Record the maximum star size. - if (n1 > maxflipstarsize) { - maxflipstarsize = n1; - } if ((b->flipstarsize > 0) && (n1 > b->flipstarsize)) { - // The star size exceeds the given limit (-LL__). - skpflipstarcount++; + // The star size exceeds the given limit. continue; // Do not flip it. } @@ -8330,15 +8450,6 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, fnextself(spintet); if (spintet.tet == flipedge.tet) break; } - // SELF_CHECK BEGIN - // These two tets are inside both of the Stars. - assert(elemcounter(tmpabtets[0]) == 2); - assert(elemcounter(tmpabtets[1]) == 2); - // Marktest the tets in Star(flipedge) but not in Star(ab). - for (j = 2; j < n1; j++) { - assert(elemcounter(tmpabtets[j]) == 1); - //marktest(tmpabtets[j]); - } // Try to flip the selected edge away. nn = flipnm(tmpabtets, n1, level + 1, edgepivot, fc); @@ -8359,10 +8470,8 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, esymself(spintet); eprevself(spintet); // [a,b,e,d] } // edgepivot == 2 - //assert(!marktested(spintet)); // It's a new tet. - assert(elemcounter(spintet) == 0); - //marktest(spintet); // It is in Star(ab). - increaseelemcounter(spintet); + 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++) { @@ -8389,7 +8498,10 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, // Continue to flip the edge [a,b]. nn = flipnm(abtets, n - 1, level, abedgepivot, fc); - if (nn > 2) { + 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]). @@ -8443,7 +8555,6 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, enextself(fliptets[1]); // [a,b,c,d] } // edgepivot == 2 for (j = 0; j < 2; j++) { - assert(elemcounter(fliptets[j]) == 0); // SELF_CHECK increaseelemcounter(fliptets[j]); } // Insert the two recovered tets into Star(ab). @@ -8455,10 +8566,6 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, } // if (unflip) } // if (nn > 2) - if (nn == 2) { //if ((nn == 2) || !fullsearch) { - // The edge has been flipped. - return nn; - } if (!fc->unflip) { // The flips are not reversed. The current Star(ab) can not be // further reduced. Return its size (# of tets). @@ -8467,7 +8574,7 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, // unflip is set. // Continue the search for flips. } else { - // The seclected edge is not flipped. + // The selected edge is not flipped. if (fc->unflip) { // The memory should already be freed. assert(nn == n1); @@ -8484,72 +8591,34 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, delete [] tmpabtets; } } // i - } else { - if (b->verbose > 2) { - printf(" -- Maximal link level (%d) reached at edge (%d, %d).\n", - level, pointmark(org(abtets[0])), pointmark(dest(abtets[0]))); - } - if (fc != NULL) { - fc->misfliplinklevelcount++; - } } // if (level...) } // if (reflexlinkedgecount > 0) } else { // Check if a 3-to-2 flip is possible. - pc = apex(abtets[0]); - pd = apex(abtets[1]); - pe = apex(abtets[2]); - - // Check if one of them is dummypoint. If so, we rearrange the vertices - // c, d, and e into p0, p1, and p2, such that p2 is the dummypoint. + // 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 (pc == dummypoint) { - hullflag = 1; - tmppts[0] = pd; - tmppts[1] = pe; - tmppts[2] = pc; - } else if (pd == dummypoint) { - hullflag = 1; - tmppts[0] = pe; - tmppts[1] = pc; - tmppts[2] = pd; - } else if (pe == dummypoint) { + + if (apex(abtets[0]) == dummypoint) { + pc = apex(abtets[1]); + pd = apex(abtets[2]); + pe = apex(abtets[0]); hullflag = 1; - tmppts[0] = pc; - tmppts[1] = pd; - tmppts[2] = pe; + } else if (apex(abtets[1]) == dummypoint) { + pc = apex(abtets[2]); + pd = apex(abtets[0]); + pe = apex(abtets[1]); + hullflag = 2; } else { - tmppts[0] = pc; - tmppts[1] = pd; - tmppts[2] = pe; + pc = apex(abtets[0]); + pd = apex(abtets[1]); + pe = apex(abtets[2]); + hullflag = (pe == dummypoint) ? 3 : 0; } reducflag = 0; rejflag = 0; - if (checkinverttetflag) { - // Only do flip if no tet is inverted (or degenerated). - if (hullflag == 0) { - ori = orient3d(pa, pb, pc, pd); - if (ori < 0) { - ori = orient3d(pa, pb, pd, pe); - if (ori < 0) { - ori = orient3d(pa, pb, pe, pc); - } - } - } else { - ori = orient3d(pa, pb, tmppts[0], tmppts[1]); - } - if (ori >= 0) { - if (b->verbose > 2) { - printf(" -- Hit a non-valid tet (%d, %d) - (%d, %d, %d)", - pointmark(pa), pointmark(pb), pointmark(pc), pointmark(pd), - pointmark(pe)); - printf(" at link(%d)\n", level); - } - return 3; - } - } // if (checkinverttetflag) if (hullflag == 0) { // Make sure that no inverted tet will be created, i.e. the new tets @@ -8560,98 +8629,63 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, if (ori < 0) { reducflag = 1; } - } else { - if (b->verbose > 2) { - printf(" -- Hit a chrismastree (%d, %d) - (%d, %d, %d)", - pointmark(pa), pointmark(pb), pointmark(pc), pointmark(pd), - pointmark(pe)); - printf(" at link(%d)\n", level); - } - if (fc != NULL) { - fc->chrismastreecount++; - } } } else { - // [a,b] is a hull edge. Moreover, the tet [a,b,p0,p1] is a hull tet - // ([a,b,p0] and [a,b,p1] are two hull faces). - // This can happen when it is in the middle of a 4-to-4 flip. - // Note that [a,b] may even be a non-convex hull edge. + // [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) { - // [a,b], [a,b,p0] and [a,b,p1] are on the convex hull. - ori = orient3d(pa, pb, tmppts[0], tmppts[1]); + // The mesh is convex, only do flip if it is a coplanar hull edge. + ori = orient3d(pa, pb, pc, pd); if (ori == 0) { - // They four vertices are coplanar. A 2-to-2 flip is possible if - // [a,b] and [p0,p1] are intersecting each other. - // NOTE: The following test is not robust, should be replaced in - // the future. 2011-12-01. - calculateabovepoint4(pa, pb, tmppts[0], tmppts[1]); - for (j = 0; j < 3; j++) { - abovept[j] = dummypoint[j]; - } - // Make sure that no inverted face will be created, i.e., [p1,p0, - // abvpt,pa] and [p0,p1,abvpt,pb] must be valid tets. - ori1 = orient3d(tmppts[0], tmppts[1], abovept, pa); - ori2 = orient3d(tmppts[0], tmppts[1], abovept, pb); - if (ori1 * ori2 < 0) { - reducflag = 1; // Flipable. - } - if (!reducflag) { - if (b->verbose > 2) { - printf(" -- Hit a degenerate chrismastree (%d, %d)", - pointmark(pa), pointmark(pb)); - printf(" - (%d, %d, -1) at link(%d)\n", - pointmark(tmppts[0]), pointmark(tmppts[1]), level); - } - if (fc != NULL) { - fc->chrismastreecount++; - } - } - } else { - if (b->verbose > 2) { - printf(" -- Hit a convex hull edge (%d, %d) at link(%d).\n", - pointmark(pa), pointmark(pb), level); - } - if (fc != NULL) { - fc->convexhulledgecount++; - } - } - } else { // if (nonconvex) - // [a,b,p0] and [a,b,p1] must be two subfaces. - // Since [a,b] is not a segment. A 3-to-2 flip (including a 2-to-2 - // flip) is possible. - // Here we only do flip if there are exactly three tets containing - // the edge [p0,p1]. In this case, the other two tets at [p0,p1] - // (not [a,b,p0,p1]) must be valid. Since they already exist. - for (j = 0; j < 3; j++) { - if (apex(abtets[j]) == dummypoint) { - flipedge = abtets[(j + 1) % 3]; // [a,b,p0,p1]. - break; - } + reducflag = 1; } - // assert(j < 3); - eprevself(flipedge); - esymself(flipedge); - enextself(flipedge); // [p0,p1,a,b]. - assert(apex(flipedge) == pa); - spintet = flipedge; - j = 0; + } 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) { - j++; fnextself(spintet); - if (spintet.tet == flipedge.tet) break; + 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 (j == 3) { - reducflag = 1; - } else { - if (b->verbose > 2) { - printf(" -- Hit a hull edge (%d, %d) at link(%d).\n", - pointmark(pa), pointmark(pb), level); + 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. + } } - //if (fc != NULL) { - // fc->convexhulledgecount++; - //} + } else { + // No valid searchpt is found. + reducflag = 0; // Do not flip it. } - } + } // if (reducflag == 1) } // if (hullflag == 1) if (reducflag) { @@ -8662,10 +8696,12 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, // 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++) { - tspivot(abtets[j], checksh); - if (checksh.sh != NULL) { + if (issubface(abtets[j])) { nn++; // Found a subface. + } else { + edgepivot = j; } } assert(nn < 3); @@ -8674,34 +8710,39 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, // 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 != NULL)) { - //rejflag = checkflipeligibility(2, tmppts[0], tmppts[1], tmppts[2], - // pa, pb, level, abedgepivot, fc); - // Here we must permute 'a' and 'b'. Since in the check... function, + 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, tmppts[0], tmppts[1], tmppts[2], - pb, pa, level, abedgepivot, fc); + rejflag = checkflipeligibility(2, pc, pd, pe, pb, pa, level, + abedgepivot, fc); } if (!rejflag) { // Do flip: [a,b] => [c,d,e] - flip32(abtets, hullflag, 0, 0); - sucflipstarcount++; + flip32(abtets, hullflag, fc); if (fc->remove_ndelaunay_edge) { if (level == 0) { - // It is the desired removing edge. - if (tetprism_vol_sum >= fc->bak_tetprism_vol) { - if (b->verbose > 2) { - printf(" -- Reject to flip (%d, %d) at link(%d)\n", - pointmark(pa), pointmark(pb), level); - printf(" due to an increased volume (%.17g).\n", - tetprism_vol_sum - fc->bak_tetprism_vol); - } - // flip back: [c,d,e] => [a,b]. - flip23(abtets, hullflag, 0, 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]); @@ -8731,15 +8772,7 @@ int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, } } // if (fc->collectnewtets) return 2; - } else { - if (b->verbose > 2) { - printf(" -- Reject a 3-to-2 flip (%d, %d) at link(%d).\n", - pointmark(pa), pointmark(pb), level); - } - if (fc != NULL) { - fc->rejf32count++; - } - } // if (rejflag) + } } // if (reducflag) } // if (n == 3) @@ -8794,7 +8827,7 @@ int tetgenmesh::flipnm_post(triface* abtets, int n, int nn, int abedgepivot, // '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, 0, 0); + flip23(abtets, 1, fc); if (fc->collectnewtets) { // Pop up new (flipped) tets from the stack. if (abedgepivot == 0) { @@ -8808,9 +8841,7 @@ int tetgenmesh::flipnm_post(triface* abtets, int n, int nn, int abedgepivot, } // The initial size of Star(ab) is 3. nn++; - } else { // nn > 2. - // The edge [a,b] exists. - } + } // Walk through the performed flips. for (i = nn; i < n; i++) { @@ -8836,7 +8867,7 @@ int tetgenmesh::flipnm_post(triface* abtets, int n, int nn, int abedgepivot, 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, 0, 0); + 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--) { @@ -8923,9 +8954,6 @@ int tetgenmesh::flipnm_post(triface* abtets, int n, int nn, int abedgepivot, printf(" Release %d spaces at f[%d].\n", n1, i); } delete [] tmpabtets; - } else { - assert(fliptype == 0); // Not a saved flip. - assert(0); // Should be not possible. } } // i @@ -8934,916 +8962,178 @@ int tetgenmesh::flipnm_post(triface* abtets, int n, int nn, int abedgepivot, /////////////////////////////////////////////////////////////////////////////// // // -// lawsonflip3d() A three-dimensional Lawson's flip algorithm. // -// // -// The basic idea of Lawson's algorithm is to flip every face of the triang- // -// ulation which is not locally Delaunay until no such face exists, then the // -// triangulation is a DT. However, in 3D, it is common that a face which is // -// not locally Delaunay and is not flippable. Hence, Laowson's algorithm may // -// get stuck. It is still an open problem, whether there exists a flip algo- // -// rithm which has a guarantee to create a DT in 3D. // -// // -// If only one vertex is added into a DT, then Lawson's flip algorithm is // -// guaranteed to transform it into a new DT [Joe'91]. Moreover, an arbitrary // -// order of flips is sufficient [Edelsbrunner & Shah'96]. // -// // -// In practice, it is desired to remove not locally Delaunay faces by flips // -// as many as possible. For this purpose, a second queue is used to store // -// the not locally Delaunay faces which are not flippable, and try them at a // -// later time. // +// insertpoint() Insert a point into current tetrahedralization. // // // -// If 'newpt' (p) is not NULL, it is a new vertex just inserted into the // -// tetrahedralization T. // -// // -// 'flipflag' indicates the property of the tetrahedralization 'T' which // -// does not include 'p' yet. // -// // -// If 'peelsliverflag' is set, the purpose of calling Lawson's flip is to // -// remove "hull slivers". This flag only works with a non-convex mesh, i.e., // -// the mesh must contains boundaries (segments and subfaces). // -// // -// 'chkencflag' indicates whether segments, subfaces, and tets should be // -// checked (for encroaching and quality) after flips. // +// 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. // // // /////////////////////////////////////////////////////////////////////////////// -long tetgenmesh::lawsonflip3d(point newpt, int flipflag, int peelsliverflag, - int chkencflag, int flipedgeflag) +int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, + face *splitseg, insertvertexflags *ivf) { - badface *popface, *bface; - triface fliptets[5], baktets[2]; - triface fliptet, neightet, *parytet; - face checksh, *parysh; + arraypool *swaplist; + triface *cavetet, spintet, neightet, neineitet, *parytet; + triface oldtet, newtet, newneitet; + face checksh, neighsh, *parysh; face checkseg, *paryseg; - point *ppt, pd, pe, pf; - long flipcount; + point *pts, pa, pb, pc, *parypt; + enum locateresult loc = OUTSIDE; REAL sign, ori; - int convflag; - int n, i; - - // For removing hull slivers. - face neighsh; - point p1, p2; - point pa, pb, pc, rempt; - REAL ang; - long tetpeelcount; - int remflag; - - flipconstraints fc; + REAL attrib, volume; + bool enqflag; + int t1ver; + int i, j, k, s; if (b->verbose > 2) { - printf(" Lawson flip %ld faces.\n", flippool->items); + printf(" Insert point %d\n", pointmark(insertpt)); } - flipcount = flip23count + flip32count + flip44count; - tetpeelcount = opt_sliver_peels; - - if (flipedgeflag) { - fc.remove_ndelaunay_edge = 1; - fc.unflip = 1; // Unflip if the edge is not flipped. - fc.collectnewtets = 1; - assert(cavetetlist->objects == 0l); - assert(calc_tetprism_vol == 1); // Swith on. - } else { - assert(unflipqueue->objects == 0); // The second queue must be empty. + // Locate the point. + if (searchtet->tet != NULL) { + loc = (enum locateresult) ivf->iloc; } - while (1) { - - while (flipstack != (badface *) NULL) { - - // Pop a face from the stack. - popface = flipstack; - flipstack = flipstack->nextitem; // The next top item in stack. - fliptet = popface->tt; - flippool->dealloc((void *) popface); - - // Skip it if it is a dead tet (destroyed by previous flips). - if (isdeadtet(fliptet)) continue; - // Skip it if it is not the same tet as we saved. - if (!facemarked(fliptet)) continue; + 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); + } - unmarkface(fliptet); + ivf->iloc = (int) loc; // The return value. - // FOR DEBUG - if (flipflag == 1) { - assert(oppo(fliptet) == newpt); + 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; } + } + } - if (ishulltet(fliptet)) { - // It is a hull tet. - if (((flipflag == 4) || peelsliverflag) && !b->convex) { - fliptet.ver = epivot[fliptet.ver & 3]; - if (oppo(fliptet) == dummypoint) { - // It's a hull face (oppo(fliptet) == dummypoint). - // Check if there exists a "hull sliver". - fsymself(fliptet); - tspivot(fliptet, checksh); - assert(checksh.sh != NULL); - for (i = 0; i < 3; i++) { - sspivot(checksh, checkseg); - if (checkseg.sh == NULL) { - spivot(checksh, neighsh); - assert(neighsh.sh != NULL); - if (sorg(checksh) != sdest(neighsh)) { - sesymself(neighsh); - } - stpivot(neighsh, neightet); - if (neightet.tet == fliptet.tet) { - // Found a hull sliver 'neightet' [d,e,a,b], where [d,e,a] - // and [e,d,b] are two hull faces. Normally, a 3-to-2 flip - // (including a 2-to-2 flip on hull subfaces) can remove - // this hull sliver. - // A special case is the existence of a hull tet [b,a,d,-1] - // or [a,b,e,-1]. It was creared by a previous hull tet - // removal. Moreover, 'd' or 'e' might be Steiner points - // on segments [a,b]. In this case, eithe [a,d],[b,d] or - // [a,e],[b,e] are subsegments. If so, a 4-to-1 flip - // (including a 3-to-1, and maybe a 2-to-1 flip) should be - // applied to remove an exterior vertex. - // See figures (2011-11-13 and 15) for illustraions. - - // First check if the face [b,a,d] is a hull face. - eprev(neightet, fliptets[0]); - esymself(fliptets[0]); // [d,a,b,e] - enextself(fliptets[0]); // [a,b,d,e] - fsymself(fliptets[0]); // [b,a,d,#] - if (oppo(fliptets[0]) != dummypoint) { - // Second check if the face [a,b,e] is a hull face. - enext(neightet, fliptets[0]); - esymself(fliptets[0]); // [a,e,b,d] - eprevself(fliptets[0]); // [b,a,e,d] - fsymself(fliptets[0]); // [b,a,e,#] - } - - if (oppo(fliptets[0]) != dummypoint) { - // Make sure we do not create an "inverted triangle" in the - // boundary, i.e., in exactly planar case, d and e must - // lie in the different sides of the edge [a,b]. - // If the dihedral angle formed by [a,b,e] and [a,b,d] is - // larger than 90 degree, we can remove [a,b,e,d]. - fliptets[0] = neightet; // [e,d,a,b] - eprevself(fliptets[0]); - esymself(fliptets[0]); - enextself(fliptets[0]); // [a,b,e,d]. - pa = org(fliptets[0]); - pb = dest(fliptets[0]); - p1 = apex(fliptets[0]); // pe - p2 = oppo(fliptets[0]); // pd - ang = facedihedral(pa, pb, p1, p2); - ang *= 2.0; - if (ang > PI) { - if (b->verbose > 2) { - printf(" Remove a hull sliver (%d, %d, %d, %d).\n", - pointmark(org(fliptet)), pointmark(dest(fliptet)), - pointmark(apex(fliptet)), pointmark(oppo(fliptet))); - } - // Remove the ill tet from bounday. - 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] - // FOR DEBUG - fnext(fliptets[2], fliptets[3]); - assert(fliptets[3].tet == neightet.tet); - assert(oppo(fliptets[1]) == dummypoint); - // Do a 3-to-2 flip to remove the ill tet. Two hull tets - // are removed toether. Two hull subfaces are flipped. - flip32(fliptets, 1, flipflag, 0); - // Update counters. - flip32count--; - flip22count--; - opt_sliver_peels++; - } - } else { - // There exists a thrid hull tet at vertex. - rempt = apex(fliptets[0]); - if (pointmark(rempt) > - (in->numberofpoints - (in->firstnumber ? 0 : 1))) { - if (pointtype(rempt) == FREESEGVERTEX) { - st_segref_count--; - } else if (pointtype(rempt) == FREEFACETVERTEX) { - st_facref_count--; - } else { - assert(0); // Impossible. - } - if (b->verbose > 2) { - printf(" Remove an exterior Steiner vertex %d.\n", - pointmark(rempt)); - } - if (removevertexbyflips(rempt)) { - // exsteinercount++; - } else { - assert(0); // Not possible. - } - } else { - //if (b->verbose > 2) { - // printf(" Remove an exterior input vertex %d.\n", - // pointmark(rempt)); - //} - // Comment: We do not remove an input point. - } - } - break; - } - } // if (checkseg.sh == NULL) - senextself(checksh); - } // i - } else { - // It's a hull edge. - assert(apex(fliptet) == dummypoint); - if (!peelsliverflag) { - // The hull edge may be not locally Delaunay. Put interior - // faces at this edge into 'flipstack' for flipping. - neightet = fliptet; // [a,b,c,d] ('c' is dummypoint). - fnextself(neightet); // [a,b,d,#1] ([a,b,d] is a hull face). - while (1) { - fnextself(neightet); // [a,b,#1,#2] - if (oppo(neightet) != dummypoint) { - // It is an interior face. - flippush(flipstack, &neightet); - } else { - // We assume the interior of the domain is connected. - // Hence we can hit hull faces only twice. - break; - } - } // while (1) - } // if (!peelsliverflag) - } - } // if ((flipflag == 4) || peelsliverflag) + // 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. - // Do not flip a hull face/edge UNLESS it is in the process of - // incrementally creating a DT in which the convex hull may be - // enlarged by the flips (when p lies outside of it). - if (flipflag != 1) { - continue; - } - } // if (ishulltet(fliptet)) + 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 (peelsliverflag) { - continue; // Only check hull tets. + 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) - // Let 'fliptet' be [a,b,c,d], the face [a,b,c] is the flip face. - // Get its opposite tet [b,a,c,e]. - fsym(fliptet, neightet); - - if (ishulltet(neightet)) { - // It is a hull tet. - if (flipflag == 1) { - // Check if the new point is visible by the hull face. - ppt = (point *) neightet.tet; - ori = orient3d(ppt[4], ppt[5], ppt[6], newpt); orient3dcount++; - if (ori < 0) { - // Visible. Perform a 2-to-3 flip on the flip face. - fliptets[0] = fliptet; // [a,b,c,d], d = newpt. - fliptets[1] = neightet; // [b,a,c,e], c = dummypoint. - flip23(fliptets, 1, flipflag, chkencflag); // flip a hull tet. - //recenttet = fliptets[0]; - } else if (ori == 0) { - // Handle degenerate case ori == 0. - if (oppo(neightet) == newpt) { - // Two hull tets have the same base face. - if (b->verbose > 2) { - printf(" Close an open face (%d, %d, %d)\n", - pointmark(org(fliptet)), pointmark(dest(fliptet)), - pointmark(apex(fliptet))); - } - // The following code connect adjacent tets at corresponding - // sides of the two hull tets. It is hard to understand. - // See an example in 2011-11-11. - // First infect the two hull tets (they will be deleted). - infect(fliptet); - infect(neightet); - // Connect the actual adjacent tets. - for (i = 0; i < 3; i++) { - fnext(fliptet, fliptets[0]); - fnext(neightet, fliptets[1]); - if (!infected(fliptets[0])) { - assert(!infected(fliptets[1])); - bond(fliptets[0], fliptets[1]); - // Update the point-to-tet map. - pa = org(fliptet); - pb = dest(fliptet); - setpoint2tet(pa, encode(fliptets[0])); - setpoint2tet(pb, encode(fliptets[0])); - // Remeber a recent tet for point location. - recenttet = fliptets[0]; - // apex(fliptets[0]) is the new point. The opposite face may - // be not locally Delaunay. Put it in flip stack. - assert(apex(fliptets[0]) == newpt); // SELF_CHECK - esymself(fliptets[0]); - flippush(flipstack, &(fliptets[0])); - assert(apex(fliptets[1]) == newpt); // SELF_CHECK - esymself(fliptets[1]); - flippush(flipstack, &(fliptets[1])); - } - enextself(fliptet); - eprevself(neightet); - } - // Delete the two tets. - tetrahedrondealloc(fliptet.tet); - tetrahedrondealloc(neightet.tet); - // Update the hull size. - hullsize -= 2; - } - } - } // if (flipflag == 1) - - continue; // Do not flip a hull face. - } // if (ishulltet(neightet)) - - if (ishulltet(fliptet)) { - continue; // Do not flip a hull tet. + 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 ((flipflag == 3) || (flipflag == 4)) { - if (checksubfaceflag) { - // Do not flip a subface. - tspivot(fliptet, checksh); - if (checksh.sh != NULL) { - if (chkencflag & 2) { - // Mesh refinement. - // Put this subface into list. - if (!smarktest2ed(checksh)) { - bface = (badface *) badsubfacs->alloc(); - bface->ss = checksh; - smarktest2(checksh); // Only queue it once. - bface->forg = sorg(checksh); // An alive badface. - } - } - continue; - } - } - } // if ((flipflag == 3) || (flipflag == 4)) - - ppt = (point *) fliptet.tet; - pe = oppo(neightet); - - sign = insphere_s(ppt[4], ppt[5], ppt[6], ppt[7], pe); - - if (sign < 0) { - if (b->verbose > 3) { - printf(" A non-Delaunay face (%d, %d, %d) - %d, %d\n", - pointmark(org(fliptet)), pointmark(dest(fliptet)), - pointmark(apex(fliptet)), pointmark(oppo(fliptet)), - pointmark(pe)); - } - - // Try to flip this face. - pd = oppo(fliptet); - // Check the convexity of its three edges. - convflag = 1; - for (i = 0; i < 3; i++) { - p1 = org(fliptet); - p2 = dest(fliptet); - ori = orient3d(p1, p2, pd, pe); orient3dcount++; - if (ori < 0) { - // A locally non-convex edge. - convflag = -1; - break; - } else if (ori == 0) { - // A locally flat edge. - convflag = 0; - break; - } - enextself(fliptet); - } - - if (convflag > 0) { - // A 2-to-3 flip is found. - fliptets[0] = fliptet; // abcd, d may be the new vertex. - fliptets[1] = neightet; // bace. - if ((flipflag == 1) || (flipflag == 2)) { // CDT boundary recovery. - if (checksubfaceflag) { - // Check if a subface will be flipped. - tspivot(fliptets[0], checksh); - if (checksh.sh != NULL) { - assert(flipflag < 3); // 1 or 2. - // It is updateing a conforming DT or a CDT. - if (b->verbose > 3) { - printf(" Queue a flipped subface (%d, %d, %d).\n", - pointmark(sorg(checksh)), pointmark(sdest(checksh)), - pointmark(sapex(checksh))); - } - for (i = 0; i < 2; i++) { - tsdissolve(fliptets[i]); // Disconnect the tet->sub bond. - } - stdissolve(checksh); // Disconnect the sub->tet bond. - // Add the missing subface into list. - subfacstack->newindex((void **) &parysh); - *parysh = checksh; - } // if (checksh.sh != NULL) - } - } // if ((flipflag == 1) || (flipflag == 2)) - flip23(fliptets, 0, flipflag, chkencflag); - //recenttet = fliptets[0]; // for point location. - } else { - // The edge ('fliptet') is non-convex or flat. - if ((flipflag == 3) || (flipflag == 4)) { - // Do not flip a subsegment. - tsspivot1(fliptet, checkseg); - if (checkseg.sh != NULL) { - if (b->verbose > 3) { - printf(" Found a non-Delaunay segment (%d, %d).\n", - pointmark(sorg(checkseg)), pointmark(sdest(checkseg))); - } - // Comment: this should be only possible when a new Steiner - // point is inserted on a segment nearby. - if (chkencflag & 1) { - // Put this segment into list. - if (!smarktest2ed(checkseg)) { - bface = (badface *) badsubsegs->alloc(); - bface->ss = checkseg; - smarktest2(checkseg); // Only queue it once. - bface->forg = sorg(checkseg); // An alive badface. - } - } - continue; - } - } - - // A 3-to-2 or 4-to-4 may be possible. - esym(fliptet, fliptets[0]); // [b,a,d,c] - // assert(apex(fliptets[0]) == pd); - n = 0; - do { - fnext(fliptets[n], fliptets[n + 1]); - n++; - } while ((fliptets[n].tet != fliptet.tet) && (n < 5)); - - if (n == 3) { - // Found a 3-to-2 flip. - if ((flipflag == 1) || (flipflag == 2)) { // CDT boundary recovery. - if (checksubsegflag) { - // Check if the flip edge is subsegment. - tsspivot1(fliptets[0], checkseg); - if (checkseg.sh != NULL) { - if (!sinfected(checkseg)) { - // This subsegment will be flipped. Queue it. - if (b->verbose > 3) { - printf(" Queue a flipped segment (%d, %d).\n", - pointmark(sorg(checkseg)), pointmark(sdest(checkseg))); - } - sinfect(checkseg); // Only save it once. - subsegstack->newindex((void **) &paryseg); - *paryseg = checkseg; - } - // Clean tet-to-seg pointers. - for (i = 0; i < 3; i++) { - tssdissolve1(fliptets[i]); - } - // Clean the seg-to-tet pointer. - sstdissolve1(checkseg); - } - } - if (checksubfaceflag) { - // Check if there are subfaces to be flipped. - for (i = 0; i < 3; i++) { - tspivot(fliptets[i], checksh); - if (checksh.sh != NULL) {//if (flipshs[i].sh != NULL) { - if (b->verbose > 2) { - printf(" Queue a flipped subface (%d, %d, %d).\n", - pointmark(sorg(checksh)), pointmark(sdest(checksh)), - pointmark(sapex(checksh))); - } - tsdissolve(fliptets[i]); // Disconnect the tet->sub bond. - stdissolve(checksh); // Disconnect the sub->tet bond. - // Add the missing subface into list. - subfacstack->newindex((void **) &parysh); - *parysh = checksh; - } - } - } - } // if ((flipflag == 1) || (flipflag == 2)) - - // Now flip the edge. - flip32(fliptets, 0, flipflag, chkencflag); - //recenttet = fliptets[0]; // for point location. - } else { - // There are more than 3 tets shared at this edge. - if ((n == 4) && (convflag < 0)) { - // Check if a 4-to-4 flip is possible. - pf = apex(fliptets[3]); - if (pf == dummypoint) { - // It is a non-convex hull edge shared by four tets (two hull - // tets and two interior tets). - // Let the two interior tets be [a,b,c,d] and [b,a,c,e] where - // [a,b] be the hull edge, [a,b,c] be the interior face. - // [a,b,d] and [a,b,e] are two hull faces. - // A 4-to-4 flip is possible if the two new tets [e,d,b,c] - // and [e,d,c,a] are valid tets. - // Current status: - // 'fliptets[0]' is [a,b,e,c] - // 'fliptets[1]' is [a,b,c,d] - // 'fliptets[2]' is [a,b,d,f] (hull tet) - // 'fliptets[3]' is [a,b,f,e] (hull tet) - pa = org(fliptets[1]); - pb = dest(fliptets[1]); - pc = apex(fliptets[1]); - p1 = oppo(fliptets[1]); // pd - p2 = apex(fliptets[0]); // pe - ori = orient3d(p2, p1, pb, pc); - if (ori < 0) { - ori = orient3d(p2, p1, pc, pa); - if (ori < 0) { - convflag = -2; // A 4-to-4 flip is possible. - } - } - } - } // if ((n == 4) && (convflag < 0)) - if ((n == 4) && ((convflag == 0) || (convflag == -2))) { - // Found a 4-to-4 flip. - if (b->verbose > 3) { - printf(" A 4-to-4 flip (%d, %d) - (%d, %d).\n", - pointmark(org(fliptet)), pointmark(dest(fliptet)), - pointmark(pd), pointmark(pe)); - } - if ((flipflag == 1) || (flipflag == 2)) { // CDT boundary recovery - if (checksubsegflag) { - // Check if the flip edge is subsegment. - tsspivot1(fliptets[0], checkseg); - if (checkseg.sh != NULL) { - if (!sinfected(checkseg)) { - // This subsegment will be flipped. Queue it. - if (b->verbose > 3) { - printf(" Queue a flipped segment (%d, %d).\n", - pointmark(sorg(checkseg)), pointmark(sdest(checkseg))); - } - sinfect(checkseg); // Only save it once. - subsegstack->newindex((void **) &paryseg); - *paryseg = checkseg; - } - // Clean the tet-to-seg pointers. - for (i = 0; i < 4; i++) { - tssdissolve1(fliptets[i]); - } - // Clean the seg-to-tet pointer. - sstdissolve1(checkseg); - } - } - if (checksubfaceflag) { - // Check if there are subfaces to be flipped. - for (i = 0; i < 4; i++) { - tspivot(fliptets[i], checksh); - if (checksh.sh != NULL) { - if (b->verbose > 3) { - printf(" Queue a flipped subface (%d,%d,%d).\n", - pointmark(sorg(checksh)), pointmark(sdest(checksh)), - pointmark(sapex(checksh))); - } - tsdissolve(fliptets[i]); // Disconnect the tet->sub bond. - stdissolve(checksh); // Disconnect the sub->tet bond. - // Add the missing subface into list. - subfacstack->newindex((void **) &parysh); - *parysh = checksh; - } - } - } - } // if ((flipflag == 1) || (flipflag == 2)) - - // First do a 2-to-3 flip. - // Comment: This flip temporarily creates either a degenerated - // tet (convflag == 0) or an inverted tet (convflag < 0). - // It is removed by the followed 3-to-2 flip. - fliptets[0] = fliptet; // tet abcd, d is the new vertex. - baktets[0] = fliptets[2]; - baktets[1] = fliptets[3]; - // The flip may involve hull tets. - flip23(fliptets, 1, flipflag, chkencflag); - // Then do a 3-to-2 flip. - enextesymself(fliptets[0]); // fliptets[0] is edab. - eprevself(fliptets[0]); // tet badc, d is the new vertex. - fliptets[1] = baktets[0]; - fliptets[2] = baktets[1]; - flip32(fliptets, 1, flipflag, chkencflag); - flip23count--; - flip32count--; - flip44count++; - //recenttet = fliptets[0]; // for point location. - } else { - // This edge is shared by more than 4 tets. - if (b->verbose > 2) { - printf(" An unflippable non-Delaunay edge (%d,%d).\n", - pointmark(org(fliptet)), pointmark(dest(fliptet))); - } - remflag = 0; - if (flipedgeflag == 2) { - // Try to flip this edge by my edge flip algorithm. - // Remember the the objective value (volume of all tetprisms). - fc.bak_tetprism_vol = tetprism_vol_sum; - if (removeedgebyflips(&fliptet, &fc) == 2) { - if (b->verbose > 2) { - printf(" Decreased quantity: %.17g.\n", - fc.bak_tetprism_vol - tetprism_vol_sum); - } - // Queue new faces in flipstack. - for (i = 0; i < cavetetlist->objects; i++) { - parytet = (triface *) fastlookup(cavetetlist, i); - if (!isdeadtet(*parytet)) { // Skip a dead tet. - for (parytet->ver = 0; parytet->ver < 4; parytet->ver++) { - // Avoid queue a face twice. - fsym(*parytet, neightet); - if (!facemarked(neightet)) { - //flippush(flipstack, parytet); - bface = (badface *) flippool->alloc(); - bface->tt = *parytet; - markface(bface->tt); - bface->forg = org(bface->tt); // An alive badface. - bface->fdest = dest(bface->tt); - bface->fapex = apex(bface->tt); - // bface->foppo = oppo(bface->tt); - // Push this face into stack. - bface->nextitem = flipstack; - flipstack = bface; - } - } // parytet->ver - } - } // i - cavetetlist->restart(); - remflag = 1; - } - } - if (!remflag) { - // Found an unflippable non-Delaunay edge. - if (flipedgeflag > 0) { // if (flipflag > 1) { - // Save this face (of the edge) in a second queue. - unflipqueue->newindex((void **) &bface); - bface->tt = fliptet; - bface->forg = org(fliptet); - bface->fdest = dest(fliptet); - bface->fapex = apex(fliptet); // FOR DEBUG. - } - } - } - } // if (n > 3) - } // if (convflag <= 0) - } // if (sign < 0) - - } // while (flipstack != NULL) - - - break; - - } // while (1) - - - if (b->verbose > 2) { - printf(" Total %ld flips", flip23count + flip32count + flip44count - - flipcount); - if ((flipflag == 4) || peelsliverflag) { - printf(", %ld sliver peels", opt_sliver_peels - tetpeelcount); - } - printf("\n"); - } - - - return flip23count + flip32count + flip44count - flipcount; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// insertvertex() Insert a point into current tetrahedralization. // -// // -// This routine implements the famous Bowyer-Watson (B-W) algorithm to add a // -// new point p into current tetrahedralization, denoted as T. The baisc idea // -// of B-W algorithm is: first finds a "cavity", denoted as C inside T, where // -// C is a simplicial polyhedron formed by a union of tetrahedra in T. If all // -// boundary faces (triangles) of C are visible by p, i.e., C is star-shaped, // -// then T can be re-tetrahedralized by first deleting all old tetrahedra in // -// C, then replacing new tetrahedra formed by boundary faces of C and p. The // -// result is that p becomesis a vertex of T. // -// // -// // -/////////////////////////////////////////////////////////////////////////////// - -int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, - face *splitseg, insertvertexflags *ivf) -{ - arraypool *swaplist; // for updating cavity. - triface *cavetet, spintet, neightet, neineitet, *parytet; - triface oldtet, newtet, newneitet; - face checksh, *parysh, neighsh, spinsh; - face checkseg, *paryseg; - point *pts, pa, pb, pc, *parypt; - badface *bface; - enum locateresult loc; - REAL sign, ori; - REAL rd, cent[3]; - REAL attrib, volume; - long cutcount, cutshcount, tetcount = 0; - long bakhullsize; - bool enqflag; - int i, j, k, s; - - int rejptflag, encptflag; // for protecting balls. - int bgmloc; - -#ifdef WITH_RUNTIME_COUNTERS - clock_t tstart, tend; -#endif - - if (b->verbose > 2) { - printf(" Insert point %d\n", pointmark(insertpt)); - } - - - // Locate the point. - loc = OUTSIDE; // Set a default value. - - if (searchtet->tet != NULL) { - loc = (enum locateresult) ivf->iloc; - } - - if (loc == OUTSIDE) { -#ifdef WITH_RUNTIME_COUNTERS - tstart = clock(); -#endif - tetcount = ptloc_count; // Count the number of walked tets. - if (searchtet->tet == NULL) { - if (!b->weighted) { - if (b->btree) { - btree_search(insertpt, searchtet); - } else if (b->hilbertcurve) { // -U - *searchtet = recenttet; - } else { // -u0 - randomsample(insertpt, searchtet); - } - } else { - // There may exist dangling vertex. - *searchtet = recenttet; - } - } - // Locate the point. Use 'randflag' if the mesh is non-convex. - loc = locate(insertpt, searchtet, ivf->chkencflag, checksubfaceflag); - if (b->verbose > 3) { - printf(" Walk distance (# tets): %ld\n", ptloc_count-tetcount); - } - if (ptloc_max_count < (ptloc_count - tetcount)) { - ptloc_max_count = (ptloc_count - tetcount); - } -#ifdef WITH_RUNTIME_COUNTERS - tend = clock(); - t_ptloc += (tend - tstart); -#endif - } - - if (b->verbose > 3) { - printf(" Located tet (%d, %d, %d, %d).\n", - pointmark(org(*searchtet)), pointmark(dest(*searchtet)), - pointmark(apex(*searchtet)), pointmark(oppo(*searchtet))); - } - -#ifdef WITH_RUNTIME_COUNTERS - tstart = clock(); -#endif - - 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. - if (b->verbose > 2) { - printf(" The point is above the lower hull, skipped.\n"); - } - return OUTSIDE; - } - } - } - - // Create the initial cavity C(p) which contains all tetrahedra directly - // intersect with p. - // If 'bowywat > 2' and p lies on a segment or subface, also create the - // initial sub-cavity sC(p) which contains all subfaces (and segment) - // which directly intersect with p. - // If 'bowywat > 2', the initial C(p) is validated, i.e., all boundary - // faces of C(p) should be visible by p. - - // Remember the current hullsize. It is used to restore the hullsize - // if the new point is rejected for insertion. - bakhullsize = hullsize; - - if (loc == OUTSIDE) { - if (b->verbose > 3) { - printf(" Outside hull.\n"); - } - // 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 & 3]; - cavetetlist->newindex((void **) &parytet); - *parytet = neightet; - } - if ((point) searchtet->tet[7] == dummypoint) hullsize--; - // tetrahedrondealloc(searchtet->tet); - infect(*searchtet); - caveoldtetlist->newindex((void **) &parytet); - *parytet = *searchtet; - flip14count++; - } else if (loc == INTETRAHEDRON) { - if (b->verbose > 3) { - printf(" Inside tet.\n"); - } - // Add four adjacent boundary tets into list. - for (i = 0; i < 4; i++) { - decode(searchtet->tet[i], neightet); - neightet.ver = epivot[neightet.ver & 3]; - cavetetlist->newindex((void **) &parytet); - *parytet = neightet; - } - // tetrahedrondealloc(searchtet->tet); - infect(*searchtet); - caveoldtetlist->newindex((void **) &parytet); - *parytet = *searchtet; - flip14count++; - } else if (loc == ONFACE) { - if (b->verbose > 3) { - printf(" On face.\n"); - } - // 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 & 3]; - cavetetlist->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 & 3]; - cavetetlist->newindex((void **) &parytet); - *parytet = neightet; - } - if ((point) spintet.tet[7] == dummypoint) hullsize--; - if ((point) searchtet->tet[7] == dummypoint) hullsize--; - // tetrahedrondealloc(spintet.tet); - infect(spintet); - caveoldtetlist->newindex((void **) &parytet); - *parytet = spintet; - // tetrahedrondealloc(searchtet->tet); - infect(*searchtet); - caveoldtetlist->newindex((void **) &parytet); - *parytet = *searchtet; - flip26count++; - - if (ivf->splitbdflag) { //if (bowywat > 2) { - if (splitsh != NULL) { - // Create the initial sub-cavity sC(p). - smarktest(*splitsh); - caveshlist->newindex((void **) &parysh); - *parysh = *splitsh; - } - } // if (splitbdflag) - } else if (loc == ONEDGE) { - if (b->verbose > 3) { - printf(" On edge.\n"); - } - // Add all adjacent boundary tets into list. - spintet = *searchtet; - while (1) { - enextesym(spintet, neightet); - fsymself(neightet); - neightet.ver = epivot[neightet.ver & 3]; - cavetetlist->newindex((void **) &parytet); - *parytet = neightet; - eprevesym(spintet, neightet); - fsymself(neightet); - neightet.ver = epivot[neightet.ver & 3]; - cavetetlist->newindex((void **) &parytet); - *parytet = neightet; - if ((point) spintet.tet[7] == dummypoint) hullsize--; - // tetrahedrondealloc(spintet.tet); - infect(spintet); - caveoldtetlist->newindex((void **) &parytet); - *parytet = spintet; - fnextself(spintet); - if (spintet.tet == searchtet->tet) break; - } // while (1) - flipn2ncount++; - - if (ivf->splitbdflag) { //if (bowywat > 2) { - // Create the initial sub-cavity sC(p). - if (splitseg != 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); + 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); @@ -9862,9 +9152,6 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, } } // if (splitbdflag) } else if (loc == INSTAR) { - if (b->verbose > 3) { - printf(" Inside star.\n"); - } // We assume that all tets in the star are given in 'caveoldtetlist', // and they are all infected. assert(caveoldtetlist->objects > 0); @@ -9876,140 +9163,49 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, decode(cavetet->tet[j], neightet); if (!infected(neightet)) { // It's a boundary face. - neightet.ver = epivot[neightet.ver & 3]; - cavetetlist->newindex((void **) &parytet); + neightet.ver = epivot[neightet.ver]; + cavebdrylist->newindex((void **) &parytet); *parytet = neightet; } } } } else if (loc == ONVERTEX) { - pa = org(*searchtet); - if (b->verbose > 3) { - printf(" On vertex %d.\n", pointmark(pa)); - } - if (insertpt != pa) { - // Remember it is a duplicated point. - setpointtype(insertpt, DUPLICATEDVERTEX); - // Set a pointer to the point it duplicates. - setpoint2ppt(insertpt, pa); - } // The point already exist. Do nothing and return. - return (int) loc; - } else if (loc == ENCSUBFACE) { - if (b->verbose > 3) { - printf(" Encroached.\n"); - } - // The vertex lies outside of the region boundary. - // Treated it as outside - loc = OUTSIDE; - return (int) loc; - } else { - assert(0); // Unknown type. - } + return 0; + } if (ivf->assignmeshsize) { // Assign mesh size for the new point. if (bgm != NULL) { // Interpolate the mesh size from the background mesh. - pa = org(*searchtet); - bgm->decode(point2bgmtet(pa), neightet); // neightet is in 'bgm'! - bgmloc = bgm->scoutpoint(insertpt, &neightet, 0); // randflag = 0 + bgm->decode(point2bgmtet(org(*searchtet)), neightet); + int bgmloc = (int) bgm->scoutpoint(insertpt, &neightet, 0); if (bgmloc != (int) OUTSIDE) { - insertpt[pointmtrindex] = // posflag = 1 - bgm->getpointmeshsize(insertpt, &neightet, bgmloc, 1); + insertpt[pointmtrindex] = + bgm->getpointmeshsize(insertpt, &neightet, bgmloc); setpoint2bgmtet(insertpt, bgm->encode(neightet)); } } else { - insertpt[pointmtrindex] = // posflag = 1 - getpointmeshsize(insertpt, searchtet, (int) loc, 1); + insertpt[pointmtrindex] = getpointmeshsize(insertpt,searchtet,(int)loc); } } // if (assignmeshsize) - if (ivf->validflag) { //if (bowywat > 2) { - // Validate the initial C(p). Enlarge it at a face which is not visible - // by p. This removes (interior) slivers. Re-use 'cavebdrylist'. - tetcount = 0l; - + 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); - // Other expansions may make this face inside C(p). + // The tet may be tested and included in the (enlarged) cavity. if (!infected(*cavetet)) { - pc = apex(*cavetet); - // Do valid if it is a face (not a hull edge). - if (pc != dummypoint) { - pa = org(*cavetet); - pb = dest(*cavetet); - ori = orient3d(pa, pb, pc, insertpt); - if (ori <= 0) { - // An invalid face. Enlarge the cavity. - //if (oppo(*cavetet) != dummypoint) { - if (b->verbose > 3) { - printf(" Enlarge cavity at (%d, %d, %d)\n", - pointmark(pa), pointmark(pb), pointmark(pc)); - } - // Add the other three faces into list. - j = (cavetet->ver & 3); // The current face number. - for (k = 1; k < 4; k++) { - decode(cavetet->tet[(j + k) % 4], neightet); - neightet.ver = epivot[neightet.ver & 3]; - cavetetlist->newindex((void **) &parytet); - *parytet = neightet; - } - if ((point) cavetet->tet[7] == dummypoint) hullsize--; - infect(*cavetet); - caveoldtetlist->newindex((void **) &parytet); - *parytet = *cavetet; - tetcount++; - //} else { - // printf("Error in insertvertex %d: ", pointmark(insertpt)); - // printf("Invalid initial cavity at face %d.\n", i + 1); - // assert(0); - //} - } else { - // A valid face. - cavebdrylist->newindex((void **) &parytet); - *parytet = *cavetet; - } - } else { - // A hull edge is valid. - cavebdrylist->newindex((void **) &parytet); - *parytet = *cavetet; - } - } // if (!infected(*cavetet)) - } // i - - if (tetcount > 0l) { - // The cavity has been enlarged. Update it. - cavetetlist->restart(); - for (i = 0; i < cavebdrylist->objects; i++) { - cavetet = (triface *) fastlookup(cavebdrylist, i); - if (!infected(*cavetet)) { - cavetetlist->newindex((void **) &parytet); - *parytet = *cavetet; - } - } // i - } // if (tetcount) - - cavebdrylist->restart(); - tetcount = 0l; - } // if (bowywat > 2) - - // Update the cavity C(p) using the Bowyer-Watson approach (bowywat > 0). - - 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. - // In case (1), this tet is grabbed in the cavity and three adjacent - // tets on other faces of this tet are added into 'cavetetlist'. - enqflag = false; - if (!marktested(*cavetet)) { - if (ivf->bowywat) { + // 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) { @@ -10026,20 +9222,17 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, if (!nonconvex) { // Test if this hull face is visible by the new point. ori = orient3d(pts[4], pts[5], pts[6], insertpt); - orient3dcount++; 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. - neightet = *cavetet; - neightet.ver = 3; // The face opposite to dummypoint. - fsym(neightet, neineitet); + decode(cavetet->tet[3], neineitet); if (!infected(neineitet)) { if (!marktested(neineitet)) { // Do Delaunay test on this tet. @@ -10053,12 +9246,7 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, sign = insphere_s(pts[4],pts[5],pts[6],pts[7], insertpt); } enqflag = (sign < 0.0); - } else { - // The adjacent tet has been tested (marktested), and it - // is Delaunay (not get infected). Hence the the hull - // face is Delaunay as well. - // enqflag = false; - } + } } else { // The adjacent tet is non-Delaunay. The hull face is non- // Delaunay as well. Include it in the cavity. @@ -10067,15 +9255,11 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, } // if (ori == 0.0) } else { // A hull face (must be a subface). - assert(checksubfaceflag); - assert(ivf->validflag); // 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'. - neightet = *cavetet; - neightet.ver = 3; // The face opposite to dummypoint. - fsym(neightet, neineitet); + decode(cavetet->tet[3], neineitet); if (!infected(neineitet)) { if (!marktested(neineitet)) { // Do Delaunay test on this tet. @@ -10089,12 +9273,7 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, sign = insphere_s(pts[4],pts[5],pts[6],pts[7], insertpt); } enqflag = (sign < 0.0); - } else { - // The adjacent tet has been tested (marktested), and it - // is Delaunay (not get infected). Hence the the hull - // face is Delaunay as well. - // enqflag = false; - } // if (marktested(neineitet)) + } } else { // The adjacent tet is non-Delaunay. The hull face is non- // Delaunay as well. Include it in the cavity. @@ -10102,200 +9281,131 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, } // if (infected(neineitet)) } // if (nonconvex) } // if (pts[7] != dummypoint) - } // if (bowywat) - 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); - neightet.ver = epivot[neightet.ver & 3]; - cavetetlist->newindex((void **) &parytet); - *parytet = neightet; - } - if ((point) cavetet->tet[7] == dummypoint) hullsize--; - // tetrahedrondealloc(cavetet->tet); - infect(*cavetet); - caveoldtetlist->newindex((void **) &parytet); - *parytet = *cavetet; - } else { - // Found a boundary face of the cavity. It may be a face of a hull - // tet which contains 'dummypoint'. Choose the edge in the face - // such that its endpoints are not 'dummypoint', while its apex - // may be 'dummypoint'. - //j = (cavetet->ver & 3); // j is the face number. - //cavetet->ver = epivot[j]; // [4,5,2,11] - cavebdrylist->newindex((void **) &parytet); - *parytet = *cavetet; - } - } // if (!infected(*cavetet)) - } // i + marktest(*cavetet); // Only test it once. + } // if (!marktested(*cavetet)) - if (b->verbose > 3) { - printf(" Initial cavity size: %ld tets, %ld faces.\n", - caveoldtetlist->objects, cavebdrylist->objects); - } + 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); - for (j = 0; j < 6; j++) { - cavetet->ver = edge2ver[j]; - tsspivot1(*cavetet, checkseg); - if (checkseg.sh != NULL) { - if (!sinfected(checkseg)) { - sinfect(checkseg); - cavetetseglist->newindex((void **) &paryseg); - *paryseg = checkseg; + 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++) { - checkseg = * (face *) fastlookup(cavetetseglist, i); - suninfect(checkseg); + 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); - oldtet = *cavetet; - for (oldtet.ver = 0; oldtet.ver < 4; oldtet.ver++) { - tspivot(oldtet, checksh); - if (checksh.sh != NULL) { - if (!sinfected(checksh)) { - sinfect(checksh); - cavetetshlist->newindex((void **) &parysh); - *parysh = checksh; + 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++) { - checksh = * (face *) fastlookup(cavetetshlist, i); - suninfect(checksh); + parysh = (face *) fastlookup(cavetetshlist, i); + suninfect(*parysh); } - } // if (checksubfaceflag) - if (ivf->rejflag & 1) { - // Reject insertion of this point if it encroaches upon any segment. - for (i = 0; i < cavetetseglist->objects; i++) { - checkseg = * (face *) fastlookup(cavetetseglist, i); - pa = sorg(checkseg); - pb = sdest(checkseg); - if (checkseg4encroach(pa, pb, insertpt)) { - if (b->verbose > 3) { - printf(" Found an encroached seg (%d, %d).\n", - pointmark(pa), pointmark(pb)); - } - encseglist->newindex((void **) &paryseg); - *paryseg = checkseg; - } - } // i - if (encseglist->objects > 0) { - if (b->verbose > 3) { - printf(" Found %ld encroached segments. Reject it.\n", - encseglist->objects); - } - 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); // Unmark it. - } - // Clear working lists. - cavetetlist->restart(); - cavebdrylist->restart(); - caveoldtetlist->restart(); - cavetetseglist->restart(); - cavetetshlist->restart(); - if (ivf->splitbdflag) { //if (bowywat > 2) { - if (splitseg != NULL) { - sunmarktest(*splitseg); - } - for (i = 0; i < caveshlist->objects; i++) { - parysh = (face *) fastlookup(caveshlist, i); - assert(smarktested(*parysh)); - sunmarktest(*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; } - caveshlist->restart(); - cavesegshlist->restart(); - } - // Restore the hullsize. - hullsize = bakhullsize; - return (int) ENCSEGMENT; - } - } // if (reject & 1) - - if (ivf->rejflag & 2) { - // Reject insertion of this point if it encroaches upon any subface. - for (i = 0; i < cavetetshlist->objects; i++) { - checksh = * (face *) fastlookup(cavetetshlist, i); - pa = sorg(checksh); - pb = sdest(checksh); - pc = sapex(checksh); - if (checkfac4encroach(pa, pb, pc, insertpt, cent, &rd)) { - if (b->verbose > 3) { - printf(" Found an encroached subface (%d, %d, %d).\n", - pointmark(pa), pointmark(pb), pointmark(pc)); - } - encshlist->newindex((void **) &bface); - bface->ss = checksh; - bface->forg = pa; // Not a dad one. - for (j = 0; j < 3; j++) bface->cent[j] = cent[j]; - bface->key = rd; - } - } // i - if (encshlist->objects > 0) { - if (b->verbose > 3) { - printf(" Found %ld encroached subfaces. Reject it.\n", - caveencshlist->objects); - } - 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); // Unmark it. } - cavetetlist->restart(); - cavebdrylist->restart(); - caveoldtetlist->restart(); - cavetetseglist->restart(); - cavetetshlist->restart(); - if (ivf->splitbdflag) { //if (bowywat > 2) { - if (splitseg != NULL) { - sunmarktest(*splitseg); - } - for (i = 0; i < caveshlist->objects; i++) { - parysh = (face *) fastlookup(caveshlist, i); - assert(smarktested(*parysh)); - sunmarktest(*parysh); - } - caveshlist->restart(); - cavesegshlist->restart(); + if (encshlist->objects > 0) { + insertpoint_abort(splitseg, ivf); + ivf->iloc = (int) ENCSUBFACE; + return 0; } - // Restore the hullsize. - hullsize = bakhullsize; - return (int) ENCSUBFACE; } - } // if (reject & 2) + } // 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) { //if (bowywat > 2) { // if (bowywat == 3) { - // Update the sC(p). + 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. @@ -10304,19 +9414,25 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, assert(smarktested(*parysh)); checksh = *parysh; for (j = 0; j < 3; j++) { - sspivot(checksh, checkseg); - if (checkseg.sh == NULL) { + if (!isshsubseg(checksh)) { spivot(checksh, neighsh); assert(neighsh.sh != NULL); if (!smarktested(neighsh)) { - // Add this subface if it is inside C(p). stpivot(neighsh, neightet); if (infected(neightet)) { fsymself(neightet); if (infected(neightet)) { - smarktest(neighsh); - caveshlist->newindex((void **) &parysh); - *parysh = neighsh; + // 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; + } } } } @@ -10324,48 +9440,34 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, senextself(checksh); } // j } // i - if (b->verbose > 3) { - printf(" Initial subcavity size: %ld subfacess.\n", - caveshlist->objects); - } - } + } // if (ivf->splitbdflag) - cutcount = 0l; + if (ivf->validflag) { + // Validate C(p) and update it if it is not star-shaped. + int cutcount = 0; - if (ivf->validflag) { - //if (bowywat > 1) { // if (bowywat == 2 || bowywat == 3) { - // T is a CT. Validation is needed (fig/dump-cavity-case8). - cavetetlist->restart(); // Re-use it. - - //if (splitbdflag) { //if (bowywat > 2) { // if (bowywat == 3) { 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). - // It is needed by 'splitbdflag'. + // 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)) { - if (b->verbose > 3) { - printf(" Found a subface (%d, %d, %d) inside cavity.\n", - pointmark(sorg(*parysh)), pointmark(sdest(*parysh)), - pointmark(sapex(*parysh))); - } // It is possible that this face is a boundary subface. // Check if it is a hull face. - assert(apex(neightet) != dummypoint); + //assert(apex(neightet) != dummypoint); if (oppo(neightet) != dummypoint) { fsymself(neightet); } if (oppo(neightet) != dummypoint) { - pa = org(neightet); - pb = dest(neightet); - pc = apex(neightet); - ori = orient3d(pa, pb, pc, insertpt); + ori = orient3d(org(neightet), dest(neightet), apex(neightet), + insertpt); if (ori < 0) { // A visible face, get its neighbor face. fsymself(neightet); @@ -10377,27 +9479,20 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, } // Cut this tet if it is either invisible by or coplanar with p. if (ori >= 0) { - 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 & 3]; + 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 & 3]; + neineitet.ver = epivot[neineitet.ver]; cavebdrylist->newindex((void **) &parytet); *parytet = neineitet; enextself(neightet); } - // Update hullsize. - if (oppo(neightet) == dummypoint) hullsize++; } // if (ori >= 0) } } @@ -10419,10 +9514,6 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, if (spintet.tet == neightet.tet) break; } if (infected(spintet)) { - if (b->verbose > 3) { - printf(" Found an interior segment (%d, %d).\n", - pointmark(sorg(*paryseg)), pointmark(sdest(*paryseg))); - } // Find an adjacent tet at this segment such that both faces // at this segment are not visible by p. pa = org(neightet); @@ -10447,7 +9538,6 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, } } } - } else { } fnextself(spintet); if (spintet.tet == neightet.tet) break; @@ -10465,42 +9555,34 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, uninfect(neightet); unmarktest(neightet); cutcount++; - neightet.ver = epivot[neightet.ver & 3]; + 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 & 3]; + neineitet.ver = epivot[neineitet.ver]; cavebdrylist->newindex((void **) &parytet); *parytet = neineitet; enextself(neightet); } - // Update hullsize. - //if (oppo(neightet) == dummypoint) hullsize++; - if ((point) (neightet.tet[7]) == dummypoint) hullsize++; } } } // i - } // if (bowywat > 2) + } // 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. - assert(cavetet->ver == epivot[cavetet->ver & 3]); // SELF_CHECK - // It must be not inside the cavity (since we only cut tets). - assert(!infected(*cavetet)); + // '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) { - pa = org(*cavetet); - pb = dest(*cavetet); - pc = apex(*cavetet); - ori = orient3d(pa, pb, pc, insertpt); orient3dcount++; + ori = orient3d(org(*cavetet), dest(*cavetet), apex(*cavetet), + insertpt); enqflag = (ori > 0); // Comment: if ori == 0 (coplanar case), we also cut the tet. } else { @@ -10517,24 +9599,17 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, cavetetlist->newindex((void **) &parytet); *parytet = *cavetet; } else { - 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++; // Add three new faces to find new boundaries. for (j = 0; j < 3; j++) { esym(neightet, neineitet); - neineitet.ver = epivot[neineitet.ver & 3]; + neineitet.ver = epivot[neineitet.ver]; cavebdrylist->newindex((void **) &parytet); *parytet = neineitet; enextself(neightet); } - // Update the hullsize. - if (oppo(neightet) == dummypoint) hullsize++; // 'cavetet' is not on the cavity boundary anymore. unmarktest(*cavetet); } @@ -10546,14 +9621,11 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, 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. - assert(cavetet->ver == epivot[cavetet->ver & 3]); // SELF_CHECK - assert(!infected(*cavetet)); fsym(*cavetet, neightet); if (infected(neightet)) { // It is a cavity boundary face. @@ -10581,12 +9653,13 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, // The cavity should contain at least one tet. if (caveoldtetlist->objects == 0l) { - printf("Invalid cavity of Steiner point %d.\n", pointmark(insertpt)); - assert(0); + insertpoint_abort(splitseg, ivf); + ivf->iloc = (int) BADELEMENT; + return 0; } - if (ivf->splitbdflag) { //if (bowywat > 2) { // if (bowywat == 3) { - cutshcount = 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); @@ -10600,11 +9673,6 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, } } if (!enqflag) { - if (b->verbose > 3) { - printf(" Cut subface (%d, %d, %d).\n", - pointmark(sorg(*parysh)), pointmark(sdest(*parysh)), - pointmark(sapex(*parysh))); - } sunmarktest(*parysh); // Use the last entry of this array to fill this entry. j = caveshlist->objects - 1; @@ -10621,16 +9689,16 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, i = 0; // Count the number of invalid subfaces/segments. // Valid the updated sub-cavity sC(p). if (loc == ONFACE) { - if (splitsh != NULL) { + 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) { + if ((splitseg != NULL) && (splitseg->sh != NULL)) { // The to-be split segment should be in sC(p). if (!smarktested(*splitseg)) i++; } - if (splitsh != NULL) { + if ((splitsh != NULL) && (splitsh->sh != NULL)) { // All subfaces at this edge should be in sC(p). pa = sorg(*splitsh); neighsh = *splitsh; @@ -10652,127 +9720,140 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, if (i > 0) { // The updated sC(p) is invalid. Do not insert this vertex. - if (b->verbose > 3) { - printf(" Found %d invalid items. Reject it.\n", 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); // Unmark it. - } - cavetetlist->restart(); - cavebdrylist->restart(); - caveoldtetlist->restart(); - cavetetseglist->restart(); - cavetetshlist->restart(); - if (ivf->splitbdflag) { //if (bowywat > 2) { - if (splitseg != NULL) { - sunmarktest(*splitseg); - } - for (i = 0; i < caveshlist->objects; i++) { - parysh = (face *) fastlookup(caveshlist, i); - assert(smarktested(*parysh)); - sunmarktest(*parysh); - } - caveshlist->restart(); - cavesegshlist->restart(); - } - // Restore the hullsize. - hullsize = bakhullsize; - return (int) BADELEMENT; + insertpoint_abort(splitseg, ivf); + ivf->iloc = (int) BADELEMENT; + return 0; } } // if (cutshcount > 0) - } // if (bowywat > 2) - + } // if (ivf->splitbdflag) } // if (cutcount > 0) - } // if (validflag) // if (bowywat > 1) - - if (b->verbose > 3) { - printf(" Final cavity: %ld tets, %ld faces.", - caveoldtetlist->objects, cavebdrylist->objects); - if (cutcount > 0l) { - printf(" Updated %ld times.", cutcount); - } - printf("\n"); - } - + } // 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. - rejptflag = 0; - if (ivf->refineflag == 1) { - // The new point is the circumcenter of a tetrahedron. - assert(!isdeadtet(ivf->refinetet)); - if (!infected(ivf->refinetet)) { - rejrefinetetcount++; - rejptflag = 1; - } - } else if (ivf->refineflag == 2) { - // The new point is the circumcenter of a subface. - assert(ivf->refinesh.sh != NULL); - if (!smarktested(ivf->refinesh)) { - rejrefineshcount++; - rejptflag = 1; - } - } - if (rejptflag) { - if (b->verbose > 2) { - printf(" Point %d does not refine its element. Rejected.\n", - pointmark(insertpt)); + 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]; } - // Restore the original status. - for (i = 0; i < caveoldtetlist->objects; i++) { - cavetet = (triface *) fastlookup(caveoldtetlist, i); - uninfect(*cavetet); - unmarktest(*cavetet); + } 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]; } - for (i = 0; i < cavebdrylist->objects; i++) { - cavetet = (triface *) fastlookup(cavebdrylist, i); - unmarktest(*cavetet); // Unmark it. + if (pts[3] != dummypoint) { + cavetetvertlist->newindex((void **) &parypt); + *parypt = pts[3]; } - // Clear working lists. - cavetetlist->restart(); - cavebdrylist->restart(); - caveoldtetlist->restart(); - cavetetshlist->restart(); - cavetetseglist->restart(); - cavetetvertlist->restart(); - if (ivf->splitbdflag) { //if (bowywat > 2) { // if (bowywat == 3) { - if (splitseg != NULL) { - sunmarktest(*splitseg); + 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); } - for (i = 0; i < caveshlist->objects; i++) { - parysh = (face *) fastlookup(caveshlist, i); - assert(smarktested(*parysh)); - sunmarktest(*parysh); + 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; } - caveshlist->restart(); - cavesegshlist->restart(); } + } + cavetetvertlist->restart(); // Clear the work list. - // Restore the hullsize. - hullsize = bakhullsize; - loc = BADELEMENT; - return (int) loc; - } // if (rejptflag) - } // if (ivf->refineflag) - - rejptflag = (ivf->rejflag & 4); - encptflag = 0; + 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 || b->plc || rejptflag) { + 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)); + //assert(infected(*cavetet)); pts = (point *) &(cavetet->tet[4]); for (j = 0; j < 4; j++) { if (pts[j] != dummypoint) { @@ -10784,118 +9865,48 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, } } // j } // i - if (b->verbose > 3) { - printf(" %ld cavity vertices.\n", cavetetvertlist->objects); - } // Uninfect all collected (cavity) vertices. for (i = 0; i < cavetetvertlist->objects; i++) { parypt = (point *) fastlookup(cavetetvertlist, i); puninfect(*parypt); } - if (b->plc || rejptflag) { - // Check if p is too close to an existing vertex. - pts = NULL; - for (i = 0; i < cavetetvertlist->objects; i++) { + 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); - rd = distance(*parypt, insertpt); - // Is the point very close to an existing point? - if (rd < b->minedgelength) { - pts = parypt; - break; - } - if (rejptflag) { - // Is the point encroaches upon an existing point? - if (rd < (*parypt)[pointmtrindex]) { - // The point lies inside the protection ball. - if (b->verbose > 2) { - printf(" Point %d lies in protball of %d. Rejected.\n", - pointmark(insertpt), pointmark(*parypt)); - } - pts = parypt; - encptflag = 1; - break; - } + len = distance(*parypt, insertpt); + if (len < ivf->smlen) { + ivf->smlen = len; + ivf->parentpt = *parypt; } - } // i - if (pts != NULL) { - // p is too close to *pts. - if (ivf->iloc != (int) INSTAR) { - if (pointmark(insertpt) <= in->numberofpoints) { - // It's an input point. - if (!b->quiet) { - printf("Warning: Point %d is replaced by point %d.\n", - pointmark(insertpt), pointmark(*pts)); - } - // Count the number of duplicated points. - dupverts++; - } else { // It's a Steiner point. - if (b->verbose) { - if (!rejptflag) { - printf("Warning: Reject a Steiner point %d (close to %d).\n", - pointmark(insertpt), pointmark(*pts)); - } - } - } - // Remember it is a duplicated point. - setpointtype(insertpt, DUPLICATEDVERTEX); - // Set a pointer to the point it duplicates. - setpoint2ppt(insertpt, *pts); - - // Restore the original status. - 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); // Unmark it. - } - // Clear working lists. - cavetetlist->restart(); - cavebdrylist->restart(); - caveoldtetlist->restart(); - cavetetshlist->restart(); - cavetetseglist->restart(); - cavetetvertlist->restart(); - if (ivf->splitbdflag) { //if (bowywat > 2) { // if (bowywat == 3) { - if (splitseg != NULL) { - sunmarktest(*splitseg); - } - for (i = 0; i < caveshlist->objects; i++) { - parysh = (face *) fastlookup(caveshlist, i); - assert(smarktested(*parysh)); - sunmarktest(*parysh); - } - caveshlist->restart(); - cavesegshlist->restart(); - } + } + } + } - // Restore the hullsize. - hullsize = bakhullsize; - if (!encptflag) { - loc = NEARVERTEX; - } else { - loc = ENCVERTEX; - } - return (int) loc; - } else { // (iloc == (int) INSTAR) - // The cavity is guaranteed to be valid by the caller of this - // function. We still insert this vertex. - if (b->verbose) { - printf("Warning: The Steiner point %d is very close to %d.\n", - pointmark(insertpt), pointmark(*pts)); - } - } - } // if (pts != NULL) - } - } - // The new point will be inserted. - totaldeadtets += caveoldtetlist->objects; - totalbowatcavsize += cavebdrylist->objects; - if (maxbowatcavsize < cavebdrylist->objects) { - maxbowatcavsize = cavebdrylist->objects; + 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 @@ -10917,7 +9928,7 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, while (1) { j++; if (!infected(spintet)) { - neineitet = spintet; // An outer tet. Remember it. + neineitet = spintet; // An outer tet. Remember it. } else { k++; // An in tet. } @@ -10938,12 +9949,8 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, sstbond1(*paryseg, neineitet); } else { // k == j // The segment is inside C(p). - if (!ivf->splitbdflag) {//if (bowywat < 3) { // if (bowywat == 2) { + if (!ivf->splitbdflag) { checkseg = *paryseg; - if (b->verbose > 3) { - printf(" Queueing a missing seg (%d, %d)\n", - pointmark(sorg(checkseg)), pointmark(sdest(checkseg))); - } sinfect(checkseg); // Flag it as an interior segment. caveencseglist->newindex((void **) &paryseg); *paryseg = checkseg; @@ -10958,10 +9965,6 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, sinfect(*paryseg); } } // i - if (b->verbose > 3) { - printf(" %ld (%ld) cavity (interior) segments.\n", - cavetetseglist->objects, caveencseglist->objects); - } } // if (checksubsegflag) if (checksubfaceflag) { @@ -10974,7 +9977,7 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, for (j = 0; j < 2; j++) { stpivot(*parysh, neightet); if (!infected(neightet)) { - checksh = *parysh; // Remeber this side. + checksh = *parysh; // Remember this side. } else { k++; } @@ -10991,13 +9994,8 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, // This side is the outer boundary of C(p). *parysh = checksh; } else { // k == 2 - if (!ivf->splitbdflag) { //if (bowywat < 3) { // if (bowywat == 2) { + if (!ivf->splitbdflag) { checksh = *parysh; - if (b->verbose > 3) { - printf(" Queueing a missing subface (%d, %d, %d)\n", - pointmark(sorg(checksh)), pointmark(sdest(checksh)), - pointmark(sapex(checksh))); - } sinfect(checksh); // Flag it. caveencshlist->newindex((void **) &parysh); *parysh = checksh; @@ -11012,40 +10010,26 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, sinfect(*parysh); } } // i - if (b->verbose > 3) { - printf(" %ld (%ld) cavity (interior) subfaces.\n", - cavetetshlist->objects, caveencshlist->objects); - } - } // if (checksubfaceflag) { + } // if (checksubfaceflag) // Create new tetrahedra to fill the cavity. for (i = 0; i < cavebdrylist->objects; i++) { cavetet = (triface *) fastlookup(cavebdrylist, i); neightet = *cavetet; - assert(!infected(neightet)); unmarktest(neightet); // Unmark it. // Get the oldtet (inside the cavity). fsym(neightet, oldtet); if (apex(neightet) != dummypoint) { - // Create a new tet in the cavity (see Fig. bowyerwatson 1 or 3). + // Create a new tet in the cavity. maketetrahedron(&newtet); setorg(newtet, dest(neightet)); setdest(newtet, org(neightet)); setapex(newtet, apex(neightet)); setoppo(newtet, insertpt); - // The new tet inherits attribtes from the old tet. - for (j = 0; j < in->numberoftetrahedronattributes; j++) { - attrib = elemattribute(oldtet.tet, j); - setelemattribute(newtet.tet, j, attrib); - } - if (b->varvolume) { - volume = volumebound(oldtet.tet); - setvolumebound(newtet.tet, volume); - } } else { - // Create a new hull tet (see Fig. bowyerwatson 2). - hullsize++; + // Create a new hull tet. + hullsize++; maketetrahedron(&newtet); setorg(newtet, org(neightet)); setdest(newtet, dest(neightet)); @@ -11054,6 +10038,15 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, // 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. @@ -11062,12 +10055,11 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, // Set a handle for speeding point location. recenttet = newtet; - setpoint2tet(insertpt, encode(newtet)); + //setpoint2tet(insertpt, encode(newtet)); + setpoint2tet(insertpt, (tetrahedron) (newtet.tet)); - if (ivf->lawson > 1) { // if (lawson == 2 || lawson == 3) { - // Re-use this list to save new interior cavity faces. - cavetetlist->restart(); - } + // Re-use this list to save new interior cavity faces. + cavetetlist->restart(); // Connect adjacent new tetrahedra together. for (i = 0; i < cavebdrylist->objects; i++) { @@ -11089,16 +10081,15 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, } fsym(spintet, newneitet); esymself(newneitet); - assert(newneitet.tet[newneitet.ver & 3] == NULL); // FOR DEBUG + assert(newneitet.tet[newneitet.ver & 3] == NULL); bond(neightet, newneitet); - if (ivf->lawson > 1) { - // We are updateing a CDT. Queue the internal face. - // See also fig/dump-cavity-case13, -case21. + if (ivf->lawson > 1) { cavetetlist->newindex((void **) &parytet); *parytet = neightet; } } - setpoint2tet(org(newtet), encode(newtet)); + //setpoint2tet(org(newtet), encode(newtet)); + setpoint2tet(org(newtet), (tetrahedron) (newtet.tet)); enextself(newtet); enextself(oldtet); } @@ -11136,13 +10127,14 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, } } - if (splitsh != NULL) { + 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); + sinsertvertex(insertpt, splitsh, splitseg, ivf->sloc, ivf->sbowywat, 0); } if (checksubfaceflag) { - if (ivf->splitbdflag) { //if (bowywat > 2) { // if (bowywat == 3) { + if (ivf->splitbdflag) { // Recover new subfaces in C(p). for (i = 0; i < caveshbdlist->objects; i++) { // Get an old subface at edge [a, b]. @@ -11187,8 +10179,7 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, // There should be no missing interior subfaces in C(p). assert(caveencshlist->objects == 0l); } else { - // bowywat = 1 or bowywat = 2. - // The Boundary reocvery phase. + // 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]. @@ -11196,12 +10187,6 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, spivot(*parysh, checksh); // The new subface [a, b, p]. // Do not recover a deleted new face (degenerated). if (checksh.sh[3] != NULL) { - if (b->verbose > 3) { - printf(" Queue new subface (%d, %d, %d).\n", - pointmark(sorg(checksh)), pointmark(sdest(checksh)), - pointmark(sapex(checksh))); - } - //sdissolve(checksh); // It has not been connected yet. subfacstack->newindex((void **) &parysh); *parysh = checksh; } @@ -11213,11 +10198,6 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, // Some subfaces inside C(p) might be split in sinsertvertex(). // Only queue those faces which are not split. if (!smarktested(*parysh)) { - if (b->verbose > 3) { - printf(" Queue a missing subface (%d, %d, %d) x%lx.\n", - pointmark(sorg(*parysh)), pointmark(sdest(*parysh)), - pointmark(sapex(*parysh)), (uintptr_t) parysh->sh); - } checksh = *parysh; suninfect(checksh); stdissolve(checksh); // Detach connections to old tets. @@ -11229,7 +10209,7 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, } // if (checksubfaceflag) if (checksubsegflag) { - if (ivf->splitbdflag) { //if (bowywat > 2) { // if (bowywat == 3) { + if (ivf->splitbdflag) { if (splitseg != NULL) { // Recover the two new subsegments in C(p). for (i = 0; i < cavesegshlist->objects; i++) { @@ -11244,11 +10224,9 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, stpivot(checksh, neightet); } else { // It's a dangling segment. - pa = sorg(checkseg); - pb = sdest(checkseg); - point2tetorg(pa, neightet); - finddirection(&neightet, pb, 1); - assert(dest(neightet) == pb); + point2tetorg(sorg(checkseg), neightet); + finddirection(&neightet, sdest(checkseg)); + assert(dest(neightet) == sdest(checkseg)); } assert(!infected(neightet)); sstbond1(checkseg, neightet); @@ -11263,17 +10241,12 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, // There should be no interior segment in C(p). assert(caveencseglist->objects == 0l); } else { - // bowywat == 1 or bowywat == 2; // 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); - if (b->verbose > 3) { - printf(" Queue new subseg (%d, %d)\n", - pointmark(sorg(*paryseg)), pointmark(sdest(*paryseg))); - } checkseg = *paryseg; //sstdissolve1(checkseg); // It has not been connected yet. s = randomnation(subsegstack->objects + 1); @@ -11287,10 +10260,6 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, paryseg = (face *) fastlookup(caveencseglist, i); assert(sinfected(*paryseg)); if (!smarktested(*paryseg)) { // It may be split. - if (b->verbose > 3) { - printf(" Queue a missing segment (%d, %d).\n", - pointmark(sorg(*paryseg)), pointmark(sdest(*paryseg))); - } checkseg = *paryseg; suninfect(checkseg); sstdissolve1(checkseg); // Detach connections to old tets. @@ -11304,12 +10273,10 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, } } // if (checksubsegflag) - if (b->plc || b->weighted) { + if (b->weighted + ) { // Some vertices may be completed inside the cavity. They must be // detected and added to recovering list. - if (b->plc) { - tetcount = subvertstack->objects; // Re-use tetcount; - } // 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++) { @@ -11318,28 +10285,15 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, assert(searchtet->tet != NULL); // No tet has been deleted yet. if (infected(*searchtet)) { if (b->weighted) { - if (b->verbose > 2) { - printf(" Point #%d is removed from the hull.\n", - pointmark(*pts)); - } - setpointtype(*pts, UNUSEDVERTEX); - } else { - if (b->verbose > 3) { - printf(" Queue a dangling vertex %d.\n", pointmark(*pts)); + if (b->verbose > 1) { + printf(" Point #%d is non-regular after the insertion of #%d.\n", + pointmark(*pts), pointmark(insertpt)); } - subvertstack->newindex((void **) &parypt); - *parypt = *pts; + setpointtype(*pts, NREGULARVERTEX); + nonregularcount++; } } } - if (b->plc) { - if (subvertstack->objects > tetcount) { - // There are missing vertices after inserting the new point. - printf("DBG: Insert %d. Found %ld interior vertices.\n", - pointmark(insertpt), subvertstack->objects); - assert(0); // NEED TO DEBUG. - } - } } if (ivf->chkencflag & 1) { @@ -11348,23 +10302,14 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, paryseg = (face *) fastlookup(cavetetseglist, i); // Skip if it is the split segment. if (!sinfected(*paryseg)) { - // Skip it if it has already queued. - if (!smarktest2ed(*paryseg)) { - bface = (badface *) badsubsegs->alloc(); - bface->ss = *paryseg; - smarktest2(bface->ss); // Only queue it once. - bface->forg = sorg(*paryseg); // An alive badface. - } + 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); - bface = (badface *) badsubsegs->alloc(); - bface->ss = *paryseg; - smarktest2(bface->ss); // Only queue it once. - bface->forg = sorg(*paryseg); // An alive badface. + enqueuesubface(badsubsegs, paryseg); } } } // if (chkencflag & 1) @@ -11375,15 +10320,7 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, parysh = (face *) fastlookup(cavetetshlist, i); // Skip if it is a split subface. if (!sinfected(*parysh)) { - // Skip it if it has already queued. - if (!smarktest2ed(*parysh)) { - bface = (badface *) badsubfacs->alloc(); - bface->ss = *parysh; - smarktest2(bface->ss); // Only queue it once. - bface->forg = sorg(*parysh); // An alive badface. - //bface->fdest = sdest(*parysh); - //bface->fapex = sapex(*parysh); - } + enqueuesubface(badsubfacs, parysh); } } // Queue all new subfaces inside C(p). @@ -11393,11 +10330,7 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, spivot(*parysh, checksh); // checksh is a new subface [a, b, p]. // Do not recover a deleted new face (degenerated). if (checksh.sh[3] != NULL) { - //assert(!smarktest2ed(checksh)); - bface = (badface *) badsubfacs->alloc(); - bface->ss = checksh; - smarktest2(bface->ss); // Only queue it once. - bface->forg = sorg(checksh); // An alive badface. + enqueuesubface(badsubfacs, &checksh); } } } // if (chkencflag & 2) @@ -11406,23 +10339,23 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, // Queue all new tetrahedra in C(p). for (i = 0; i < cavebdrylist->objects; i++) { cavetet = (triface *) fastlookup(cavebdrylist, i); - //assert(!marktest2ed(*cavetet)); - bface = (badface *) badtetrahedrons->alloc(); - bface->tt = *cavetet; - marktest2(bface->tt); - bface->forg = org(*cavetet); + enqueuetetrahedron(cavetet); } } - // C(p) is re-meshed successfully. + // C(p) is re-meshed successfully. - // Deleted the old tets in C(p). + // 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) { + 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); @@ -11444,7 +10377,7 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, } shellfacedealloc(subfaces, parysh->sh); } - if (splitseg != NULL) { + if ((splitseg != NULL) && (splitseg->sh != NULL)) { // Delete the old segment in sC(p). shellfacedealloc(subsegs, splitseg->sh); } @@ -11453,29 +10386,16 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, if (ivf->lawson) { for (i = 0; i < cavebdrylist->objects; i++) { searchtet = (triface *) fastlookup(cavebdrylist, i); - //flippush(flipstack, searchtet, insertpt); flippush(flipstack, searchtet); } if (ivf->lawson > 1) { for (i = 0; i < cavetetlist->objects; i++) { searchtet = (triface *) fastlookup(cavetetlist, i); - //flippush(flipstack, searchtet, oppo(*searchtet)); flippush(flipstack, searchtet); } } } - // The vertex should already have a type. - assert(pointtype(insertpt) != UNUSEDVERTEX); - -#ifdef WITH_RUNTIME_COUNTERS - tend = clock(); - t_ptinsert += (tend - tstart); -#endif - - if (b->btree) { - btree_insert(insertpt); - } // Clean the working lists. @@ -11493,17 +10413,60 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, caveencshlist->restart(); } - if (b->plc || b->weighted) { + if (b->weighted || ivf->validflag) { cavetetvertlist->restart(); } - if (splitsh != NULL) { + if (((splitsh != NULL) && (splitsh->sh != NULL)) || + ((splitseg != NULL) && (splitseg->sh != NULL))) { caveshlist->restart(); caveshbdlist->restart(); cavesegshlist->restart(); } - return (int) loc; + 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(); + } } //// //// @@ -11518,10 +10481,10 @@ int tetgenmesh::insertvertex(point insertpt, triface *searchtet, face *splitsh, // // // transfernodes() Read the vertices from the input (tetgenio). // // // -// Initializing 'this->points'. Transferring all points from 'in->pointlist'// -// into it. All points are indexed (start from in->firstnumber). Each point // -// is initialized be UNUSEDVERTEX. The bounding box (xmin, xmax, ymin, ymax,// -// zmin, zmax) and the diameter (longest) of the point set are calculated. // +// 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. // // // /////////////////////////////////////////////////////////////////////////////// @@ -11547,32 +10510,34 @@ void tetgenmesh::transfernodes() // Read the point coordinates. x = pointloop[0] = in->pointlist[coordindex++]; y = pointloop[1] = in->pointlist[coordindex++]; - z = pointloop[2] = 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 weight. - w = in->pointattributelist[in->numberofpointattributes * i]; + // The first point attribute is its weight. + //w = in->pointattributelist[in->numberofpointattributes * i]; + w = pointloop[3]; } else { - // No given weight available. - w = 0; + // 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. } - } else { - pointloop[3] = 0; - } - // Read the point attributes. - for (j = 0; j < in->numberofpointattributes; j++) { - pointloop[4 + j] = in->pointattributelist[attribindex++]; - } - // Read the point metric tensor. - for (j = 0; j < in->numberofpointmtrs; j++) { - pointloop[pointmtrindex + j] = in->pointmtrlist[mtrindex++]; } - // Determine the smallest and largests x, y and z coordinates. + // Determine the smallest and largest x, y and z coordinates. if (i == 0) { xmin = xmax = x; ymin = ymax = y; @@ -11609,7 +10574,7 @@ void tetgenmesh::transfernodes() longest = sqrt(x * x + y * y + z * z); if (longest == 0.0) { printf("Error: The point set is trivial.\n"); - terminatetetgen(3); + terminatetetgen(this, 3); } // Two identical points are distinguished by 'lengthlimit'. @@ -11620,293 +10585,256 @@ void tetgenmesh::transfernodes() /////////////////////////////////////////////////////////////////////////////// // // -// btree_sort() Sort vertices using a binary space partition (bsp) tree. // +// 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::btree_sort(point* vertexarray, int arraysize, int axis, - REAL bxmin, REAL bxmax, REAL bymin, REAL bymax, - REAL bzmin, REAL bzmax, int depth) +void tetgenmesh::hilbert_init(int n) { - point *leftarray, *rightarray; - point **pptary, swapvert; - REAL split; - bool lflag, rflag; - int i, j, k; // *iptr, + int gc[8], N, mask, travel_bit; + int e, d, f, k, g; + int v, c; + int i; - if (b->verbose > 3) { - printf(" Depth %d, %d verts. Bbox (%g, %g, %g),(%g, %g, %g). %s-axis\n", - depth, arraysize, bxmin, bymin, bzmin, bxmax, bymax, bzmax, - axis == 0 ? "x" : (axis == 1 ? "y" : "z")); + 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; } +} - if (depth > max_btree_depth) { - max_btree_depth = depth; - } +/////////////////////////////////////////////////////////////////////////////// +// // +// 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 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. + } 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-arraies. - 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); - - if (b->verbose > 3) { - printf(" leftsize = %d, rightsize = %d\n", i, arraysize - i); - } - lflag = rflag = false; - - // Do not continue the partition if one of the array sizes is 0. - // if (depth < max_tree_depth) { - if (!((i == 0) || (i == arraysize))) { - if (i > b->max_btreenode_size) { - // Recursively partition the left array (length = i). - if (axis == 0) { // x - btree_sort(vertexarray, i, (axis + 1) % 3, bxmin, split, bymin, - bymax, bzmin, bzmax, depth + 1); - } else if (axis == 1) { // y - btree_sort(vertexarray, i, (axis + 1) % 3, bxmin, bxmax, bymin, - split, bzmin, bzmax, depth + 1); - } else { // z - btree_sort(vertexarray, i, (axis + 1) % 3, bxmin, bxmax, bymin, - bymax, bzmin, split, depth + 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; } - } else { - lflag = true; - } - if ((arraysize - i) > b->max_btreenode_size) { - // Recursively partition the right array (length = arraysize - i). - if (axis == 0) { // x - btree_sort(&(vertexarray[i]), arraysize - i, (axis + 1) % 3, split, - bxmax, bymin, bymax, bzmin, bzmax, depth + 1); - } else if (axis == 1) { // y - btree_sort(&(vertexarray[i]), arraysize - i, (axis + 1) % 3, bxmin, - bxmax, split, bymax, bzmin, bzmax, depth + 1); - } else { // z - btree_sort(&(vertexarray[i]), arraysize - i, (axis + 1) % 3, bxmin, - bxmax, bymin, bymax, split, bzmax, depth + 1); + for (; j >= 0; j--) { + if (vertexarray[j][axis] > split) break; } - } else { - rflag = true; - } - } else { - // Both left and right are done. - lflag = rflag = true; - } - - if (lflag && (i > 0)) { - // Remember the maximal length of the partitions. - if (i > max_btreenode_size) { - max_btreenode_size = i; - } - // Allocate space for the left array (use the first entry to save - // the length of this array). - leftarray = new point[i + 1]; - leftarray[0] = (point) i; // The array lenth. - //iptr = (int *) &(leftarray[0]); - //*iptr = i; // Save the array lenth in the first entry. - // Put all points in this array. - for (k = 0; k < i; k++) { - leftarray[k + 1] = vertexarray[k]; - setpoint2ppt(leftarray[k + 1], (point) leftarray); - } - // Save this array in list. - btreenode_list->newindex((void **) &pptary); - *pptary = leftarray; - } - - // Get the length of the right array. - j = arraysize - i; - if (rflag && (j > 0)) { - if (j > max_btreenode_size) { - max_btreenode_size = j; - } - // Allocate space for the right array (use the first entry to save - // the length of this array). - rightarray = new point[j + 1]; - rightarray[0] = (point) j; // The array lenth. - //iptr = (int *) &(rightarray[0]); - //*iptr = j; // Save the array length in the first entry. - // Put all points in this array. - for (k = 0; k < j; k++) { - rightarray[k + 1] = vertexarray[i + k]; - setpoint2ppt(rightarray[k + 1], (point) rightarray); - } - // Save this array in list. - btreenode_list->newindex((void **) &pptary); - *pptary = rightarray; + // 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); } -} -/////////////////////////////////////////////////////////////////////////////// -// // -// btree_insert() Add a vertex into a tree node. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::btree_insert(point insertpt) -{ - point *ptary; - long arylen; // The array lenhgth is saved in ptary[0]. - - // Get the tree node (save in this point). - ptary = (point *) point2ppt(insertpt); - // Get the current array length. - arylen = (long long) ptary[0]; - // Insert the point into the node. - ptary[arylen + 1] = insertpt; - // Increase the array length by 1. - ptary[0] = (point) (arylen + 1); + return i; } -/////////////////////////////////////////////////////////////////////////////// -// // -// btree_search() Search a near point for an inserting point. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::btree_search(point insertpt, triface* searchtet) +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) { - point *ptary; - point nearpt, candpt; - REAL dist2, mindist2; - int ptsamples, ptidx; - long arylen; - int i; - - // Get the tree node (save in this point). - ptary = (point *) point2ppt(insertpt); - // Get the current array length. - arylen = (long long) ptary[0]; - - if (arylen == 0) { - searchtet->tet = NULL; - return; - } - - if (1) { - - if (arylen < 5) { //if (arylen < 10) { - ptsamples = arylen; - } else { - ptsamples = 5; // Take at least 10 samples. - // The number of random samples taken is proportional to the third root - // of the number of points in the cell. - while (ptsamples * ptsamples * ptsamples < arylen) { - ptsamples++; + 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]; } - } - - // Select "good" candidate using k random samples, taking the closest one. - mindist2 = 1.79769E+308; // The largest double value (8 byte). - nearpt = NULL; - - for (i = 0; i < ptsamples; i++) { - ptidx = randomnation((unsigned long) arylen); - candpt = ptary[ptidx + 1]; - dist2 = (candpt[0] - insertpt[0]) * (candpt[0] - insertpt[0]) - + (candpt[1] - insertpt[1]) * (candpt[1] - insertpt[1]) - + (candpt[2] - insertpt[2]) * (candpt[2] - insertpt[2]); - if (dist2 < mindist2) { - mindist2 = dist2; - nearpt = candpt; + 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); } - } - - } else { - - // TEST, randomly select a point. - ptidx = randomnation((unsigned long) arylen); - nearpt = ptary[ptidx + 1]; - - } - - if (b->verbose > 2) { - printf(" Get point %d (cell size %ld).\n", pointmark(nearpt), arylen); - } - - decode(point2tet(nearpt), *searchtet); + 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 } /////////////////////////////////////////////////////////////////////////////// // // -// ordervertices() Order the vertices for incremental inserting. // -// // -// We assume the vertices have been sorted by a binary tree. // +// brio_multiscale_sort() Sort the points using BRIO and Hilbert curve. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::ordervertices(point* vertexarray, int arraysize) +void tetgenmesh::brio_multiscale_sort(point* vertexarray, int arraysize, + int threshold, REAL ratio, int *depth) { - point **ipptary, **jpptary, *swappptary; - point *ptary; - long arylen; - int index, i, j; - - // First pick one vertex from each tree node. - for (i = 0; i < (int) btreenode_list->objects; i++) { - ipptary = (point **) fastlookup(btreenode_list, i); - ptary = *ipptary; - vertexarray[i] = ptary[1]; // Skip the first entry. - } - - index = i; - // Then put all other points in the array node by node. - for (i = (int) btreenode_list->objects - 1; i >= 0; i--) { - // Randomly pick a tree node. - j = randomnation(i + 1); - // Save the i-th node. - ipptary = (point **) fastlookup(btreenode_list, i); - // Get the j-th node. - jpptary = (point **) fastlookup(btreenode_list, j); - // Order the points in the node. - ptary = *jpptary; - arylen = (long long) ptary[0]; - for (j = 2; j <= arylen; j++) { // Skip the first point. - vertexarray[index] = ptary[j]; - index++; - } - // Clear this tree node. - ptary[0] = (point) 0; - // Swap i-th node to j-th node. - swappptary = *ipptary; - *ipptary = *jpptary; // [i] <= [j] - *jpptary = swappptary; // [j] <= [i] - } + int middle; - // Make sure we've done correctly. - assert(index == arraysize); + 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. } /////////////////////////////////////////////////////////////////////////////// @@ -11938,17 +10866,14 @@ unsigned long tetgenmesh::randomnation(unsigned int choices) // // // randomsample() Randomly sample the tetrahedra for point loation. // // // -// This routine implements Muecke's Jump-and-walk point location algorithm. // -// It improves the simple walk-through by "jumping" to a good starting point // -// via random sampling. 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 searcing for. Having chosen the // -// starting tetrahedron, the simple Walk-through algorithm is executed. // +// 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) +void tetgenmesh::randomsample(point searchpt,triface *searchtet) { tetrahedron *firsttet, *tetptr; point torg; @@ -11963,44 +10888,40 @@ void tetgenmesh::randomsample(point searchpt, triface *searchtet) pointmark(searchpt)); } - 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 (b->verbose > 3) { - printf(" Dist %g from tet (%d, %d, %d, %d).\n", searchdist, - pointmark(torg), pointmark(dest(*searchtet)), - pointmark(apex(*searchtet)), pointmark(oppo(*searchtet))); - } - - // 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) { + if (!nonconvex) { + if (searchtet->tet == NULL) { + // A null tet. Choose the recenttet as the starting tet. *searchtet = recenttet; - searchdist = dist; - if (b->verbose > 3) { - printf(" Dist %g from recent tet (%d, %d, %d, %d).\n", - searchdist, pointmark(torg), pointmark(dest(*searchtet)), - pointmark(apex(*searchtet)), pointmark(oppo(*searchtet))); + // 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. @@ -12040,11 +10961,6 @@ void tetgenmesh::randomsample(point searchpt, triface *searchtet) searchtet->tet = tetptr; searchtet->ver = 11; // torg = org(t); searchdist = dist; - if (b->verbose > 3) { - printf(" Dist %g from tet (%d, %d, %d, %d).\n", searchdist, - pointmark(torg), pointmark(dest(*searchtet)), - pointmark(apex(*searchtet)), pointmark(oppo(*searchtet))); - } } } else { // A dead tet. Re-sample it. @@ -12059,7 +10975,6 @@ void tetgenmesh::randomsample(point searchpt, triface *searchtet) // // // locate() Find a tetrahedron containing a given point. // // // -// This routine implements the simple Walk-through point location algorithm. // // 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. // @@ -12071,34 +10986,26 @@ void tetgenmesh::randomsample(point searchpt, triface *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 tetrahedron whose base face is visible by the search point. // +// 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. // // // -// If 'randflag' is set (> 0). Randomly choose a tetrahedron when there are // -// multiple choices in the path. // -// // /////////////////////////////////////////////////////////////////////////////// -enum tetgenmesh::locateresult - tetgenmesh::locate(point searchpt, triface* searchtet, int chkencflag, - int randflag) +enum tetgenmesh::locateresult tetgenmesh::locate(point searchpt, + triface* searchtet) { - triface neightet; - face checksh; point torg, tdest, tapex, toppo; enum {ORGMOVE, DESTMOVE, APEXMOVE} nextmove; REAL ori, oriorg, oridest, oriapex; - enum locateresult loc; - int s; // i; - + enum locateresult loc = OUTSIDE; + int t1ver; + int s; 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->tet = recenttet.tet; } // Check if we are in the outside of the convex hull. @@ -12106,7 +11013,6 @@ enum tetgenmesh::locateresult // Get its adjacent tet (inside the hull). searchtet->ver = 3; fsymself(*searchtet); - assert(!ishulltet(*searchtet)); } // Let searchtet be the face such that 'searchpt' lies above to it. @@ -12114,22 +11020,14 @@ enum tetgenmesh::locateresult torg = org(*searchtet); tdest = dest(*searchtet); tapex = apex(*searchtet); - ori = orient3d(torg, tdest, tapex, searchpt); orient3dcount++; + ori = orient3d(torg, tdest, tapex, searchpt); if (ori < 0.0) break; } - if (searchtet->ver == 4) { - // Either 'searchtet' is a very flat tet, or the 'searchpt' lies in - // infinity, or both of them. Return OUTSIDE. - assert(0); // return OUTSIDE; - } - - loc = OUTSIDE; // Set a default return value. + assert(searchtet->ver != 4); // Walk through tetrahedra to locate the point. while (true) { - ptloc_count++; // Algorithimic count. - toppo = oppo(*searchtet); // Check if the vertex is we seek. @@ -12141,58 +11039,43 @@ enum tetgenmesh::locateresult break; } - // We enter from serarchtet's base face. There are three other faces in - // searchtet (all connecting to toppo), which one is the exit? + // 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); - orient3dcount+=3; // Now decide which face to move. It is possible there are more than one - // faces are viable moves. Use the opposite points of thier neighbors - // to discriminate, i.e., we choose the face whose opposite point has - // the shortest distance to searchpt. + // faces are viable moves. If so, randomly choose one. if (oriorg < 0) { if (oridest < 0) { if (oriapex < 0) { - if (0) { //if (!randflag) { + // 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 { - // Randomly choose a direction. - s = randomnation(3); // 's' is in {0,1,2}. - if (s == 0) { - nextmove = ORGMOVE; - } else if (s == 1) { - nextmove = DESTMOVE; - } else { - nextmove = APEXMOVE; - } - } // if (randflag) + nextmove = APEXMOVE; + } } else { // Two faces, opposite to origin and destination, are viable. - if (0) { // if (!randflag) { + //s = randomnation(2); // 's' is in {0,1}. + if (randomnation(2)) { + nextmove = ORGMOVE; } else { - // Randomly choose a direction. - s = randomnation(2); // 's' is in {0,1}. - if (s == 0) { - nextmove = ORGMOVE; - } else { - nextmove = DESTMOVE; - } - } // if (randflag) + nextmove = DESTMOVE; + } } } else { if (oriapex < 0) { // Two faces, opposite to origin and apex, are viable. - if (0) { // if (!randflag) { + //s = randomnation(2); // 's' is in {0,1}. + if (randomnation(2)) { + nextmove = ORGMOVE; } else { - // Randomly choose a direction. - s = randomnation(2); // 's' is in {0,1}. - if (s == 0) { - nextmove = ORGMOVE; - } else { - nextmove = APEXMOVE; - } - } // if (randflag) + nextmove = APEXMOVE; + } } else { // Only the face opposite to origin is viable. nextmove = ORGMOVE; @@ -12202,16 +11085,12 @@ enum tetgenmesh::locateresult if (oridest < 0) { if (oriapex < 0) { // Two faces, opposite to destination and apex, are viable. - if (0) { // if (!randflag) { + //s = randomnation(2); // 's' is in {0,1}. + if (randomnation(2)) { + nextmove = DESTMOVE; } else { - // Randomly choose a direction. - s = randomnation(2); // 's' is in {0,1}. - if (s == 0) { - nextmove = DESTMOVE; - } else { - nextmove = APEXMOVE; - } - } // if (randflag) + nextmove = APEXMOVE; + } } else { // Only the face opposite to destination is viable. nextmove = DESTMOVE; @@ -12225,10 +11104,8 @@ enum tetgenmesh::locateresult // tetrahedron. Check for boundary cases. if (oriorg == 0) { // Go to the face opposite to origin. - //enextfnextself(*searchtet); enextesymself(*searchtet); if (oridest == 0) { - //enextself(*searchtet); // edge apex->oppo eprevself(*searchtet); // edge oppo->apex if (oriapex == 0) { // oppo is duplicated with p. @@ -12239,7 +11116,6 @@ enum tetgenmesh::locateresult break; } if (oriapex == 0) { - //enext2self(*searchtet); enextself(*searchtet); // edge dest->oppo loc = ONEDGE; // return ONEDGE; break; @@ -12249,10 +11125,8 @@ enum tetgenmesh::locateresult } if (oridest == 0) { // Go to the face opposite to destination. - //enext2fnextself(*searchtet); eprevesymself(*searchtet); if (oriapex == 0) { - //enextself(*searchtet); eprevself(*searchtet); // edge oppo->org loc = ONEDGE; // return ONEDGE; break; @@ -12262,7 +11136,6 @@ enum tetgenmesh::locateresult } if (oriapex == 0) { // Go to the face opposite to apex - //fnextself(*searchtet); esymself(*searchtet); loc = ONFACE; // return ONFACE; break; @@ -12281,14 +11154,6 @@ enum tetgenmesh::locateresult } else { esymself(*searchtet); } - if (chkencflag) { - // Check if we are walking across a subface. - tspivot(*searchtet, checksh); - if (checksh.sh != NULL) { - loc = ENCSUBFACE; - break; - } - } // Move to the adjacent tetrahedron (maybe a hull tetrahedron). fsymself(*searchtet); if (oppo(*searchtet) == dummypoint) { @@ -12306,6 +11171,274 @@ enum tetgenmesh::locateresult 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; +} /////////////////////////////////////////////////////////////////////////////// // // @@ -12383,13 +11516,6 @@ void tetgenmesh::initialdelaunay(point pa, point pb, point pc, point pd) setpointtype(pd, VOLVERTEX); } - if (b->btree) { - btree_insert(pa); - btree_insert(pb); - btree_insert(pc); - btree_insert(pd); - } - setpoint2tet(pa, encode(firsttet)); setpoint2tet(pb, encode(firsttet)); setpoint2tet(pc, encode(firsttet)); @@ -12406,53 +11532,29 @@ void tetgenmesh::initialdelaunay(point pa, point pb, point pc, point pd) // // /////////////////////////////////////////////////////////////////////////////// + void tetgenmesh::incrementaldelaunay(clock_t& tv) { triface searchtet; point *permutarray, swapvertex; - insertvertexflags ivf; REAL v1[3], v2[3], n[3]; REAL bboxsize, bboxsize2, bboxsize3, ori; - int randindex, loc; + int randindex; + int ngroup = 0; int i, j; if (!b->quiet) { printf("Delaunizing vertices...\n"); } - if (b->max_btreenode_size > 0) { // not -u0. - b->btree = 1; - btreenode_list = new arraypool(sizeof(point*), 10); - max_btreenode_size = 0; - max_btree_depth = 0; - } - - // Form a random permuation (uniformly at random) of the set of vertices. permutarray = new point[in->numberofpoints]; points->traversalinit(); - if (b->btree) { // -u option - for (i = 0; i < in->numberofpoints; i++) { - permutarray[i] = (point) points->traverse(); - } - if (b->verbose) { - printf(" Sorting vertices by a bsp-tree.\n"); - } - // Sort the points using a binary tree recursively. - btree_sort(permutarray, in->numberofpoints, 0, xmin, xmax, ymin, ymax, - zmin, zmax, 0); - if (b->verbose) { - printf(" Number of tree nodes: %ld.\n", btreenode_list->objects); - printf(" Maximum tree node size: %d.\n", max_btreenode_size); - printf(" Maximum tree depth: %d.\n", max_btree_depth); - } - // Order the sorted points. - ordervertices(permutarray, in->numberofpoints); - } else if (b->hilbertcurve) { + + if (b->no_sort) { if (b->verbose) { - printf(" Sorting vertices by hilbert curve.\n"); + printf(" Using the input order.\n"); } - // To be done... for (i = 0; i < in->numberofpoints; i++) { permutarray[i] = (point) points->traverse(); } @@ -12460,58 +11562,70 @@ void tetgenmesh::incrementaldelaunay(clock_t& tv) if (b->verbose) { printf(" Permuting vertices.\n"); } + srand(in->numberofpoints); for (i = 0; i < in->numberofpoints; i++) { - randindex = randomnation(i + 1); + 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)); + 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 ((DIST(permutarray[0], permutarray[i]) / bboxsize) < b->epsilon) { + 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(10); + terminatetetgen(this, 10); } } if (i > 1) { - // Swap to move the non-indetical vertex from index i to index 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) { + 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(10); + terminatetetgen(this, 10); } for (j = 0; j < 3; j++) { v2[j] = permutarray[i][j] - permutarray[0][j]; } - CROSS(v1, v2, n); + cross(v1, v2, n); } if (i > 2) { - // Swap to move the non-indetical vertex from index i to index 1. + // Swap to move the non-identical vertex from index i to index 1. swapvertex = permutarray[i]; permutarray[i] = permutarray[2]; permutarray[2] = swapvertex; @@ -12519,221 +11633,119 @@ void tetgenmesh::incrementaldelaunay(clock_t& tv) // Make sure the fourth vertex is not coplanar with the first three. i = 3; - ori = orient3d(permutarray[0], permutarray[1], permutarray[2], - permutarray[i]); - while ((fabs(ori) / bboxsize3) < b->epsilon) { + 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(10); - } - ori = orient3d(permutarray[0], permutarray[1], permutarray[2], - permutarray[i]); - } - if (i > 3) { - // Swap to move the non-indetical 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"); - } - - // Choose algorithm: Bowyer-Watson (default) or Incremental Flip (-l). - if (b->incrflip) { - ivf.bowywat = 0; - ivf.lawson = 1; - } else { - ivf.bowywat = 1; - ivf.lawson = 0; - } - - for (i = 4; i < in->numberofpoints; i++) { - if (b->verbose > 2) printf(" #%d", i); - if (pointtype(permutarray[i]) == UNUSEDVERTEX) { - setpointtype(permutarray[i], VOLVERTEX); - } - // Auto choose the starting tet for point location. - searchtet.tet = NULL; - ivf.iloc = (int) OUTSIDE; - // Insert the vertex. - loc = insertvertex(permutarray[i], &searchtet, NULL, NULL, &ivf); - if (loc == (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++; - continue; - } - if (ivf.lawson) { - // If -l option. Perform flip to recover Delaunayness. - lawsonflip3d(permutarray[i], ivf.lawson, 0, 0, 0); - } - } - - if (b->btree) { - // Bsp-tree is used only in DT construction. - point **pptary; - for (i = 0; i < (int) btreenode_list->objects; i++) { - pptary = (point **) fastlookup(btreenode_list, i); - delete [] *pptary; - } - delete btreenode_list; - b->btree = 0; // Disable it. - } - - delete [] permutarray; -} - -//// //// -//// //// -//// delaunay_cxx ///////////////////////////////////////////////////////////// - -//// surface_cxx ////////////////////////////////////////////////////////////// -//// //// -//// //// - -/////////////////////////////////////////////////////////////////////////////// -// // -// 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; + terminatetetgen(this, 10); } + ori = orient3dfast(permutarray[0], permutarray[1], permutarray[2], + permutarray[i]); } - 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; + 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; } - // 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; + // 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; } - return true; -} + // Create the initial Delaunay tetrahedralization. + initialdelaunay(permutarray[0], permutarray[1], permutarray[2], + permutarray[3]); -/////////////////////////////////////////////////////////////////////////////// -// // -// 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. // -// // -/////////////////////////////////////////////////////////////////////////////// + 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; + } -void tetgenmesh::calculateabovepoint4(point pa, point pb, point pc, point pd) -{ - arraypool *ptarray; - point *parypt; - ptarray = new arraypool(sizeof(point), 4); + 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++; + } + } + } - ptarray->newindex((void **) &parypt); - *parypt = pa; - ptarray->newindex((void **) &parypt); - *parypt = pb; - ptarray->newindex((void **) &parypt); - *parypt = pc; - ptarray->newindex((void **) &parypt); - *parypt = pd; - calculateabovepoint(ptarray, NULL, NULL, NULL); - delete ptarray; + delete [] permutarray; } +//// //// +//// //// +//// delaunay_cxx ///////////////////////////////////////////////////////////// + +//// surface_cxx ////////////////////////////////////////////////////////////// +//// //// +//// //// + /////////////////////////////////////////////////////////////////////////////// // // // flipshpush() Push a facet edge into flip stack. // @@ -12754,19 +11766,20 @@ void tetgenmesh::flipshpush(face* flipedge) /////////////////////////////////////////////////////////////////////////////// // // -// flip22() Remove an edge by transforming 2-to-2 subfaces. // +// flip22() Perform a 2-to-2 flip in surface mesh. // // // -// 'flipfaces' contains two faces: abc and bad. This routine removes these 2 // -// faces and replaces them by two new faces: cdb and dca. // +// '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], bdsegs[4]; - face checkface, checkseg; + face bdedges[4], outfaces[4], infaces[4]; + face bdsegs[4]; + face checkface; point pa, pb, pc, pd; - badface *bface; int i; pa = sorg(flipfaces[0]); @@ -12778,10 +11791,6 @@ void tetgenmesh::flip22(face* flipfaces, int flipflag, int chkencflag) sesymself(flipfaces[1]); } - if (b->verbose > 3) { - printf(" flip 2-to-2: (%d, %d, %d, %d)\n", pointmark(pa), - pointmark(pb), pointmark(pc), pointmark(pd)); - } flip22count++; // Collect the four boundary edges. @@ -12796,8 +11805,7 @@ void tetgenmesh::flip22(face* flipfaces, int flipflag, int chkencflag) infaces[i] = outfaces[i]; sspivot(bdedges[i], bdsegs[i]); if (outfaces[i].sh != NULL) { - sspivot(bdedges[i], checkseg); - if (checkseg.sh != NULL) { + if (isshsubseg(bdedges[i])) { spivot(infaces[i], checkface); while (checkface.sh != bdedges[i].sh) { infaces[i] = checkface; @@ -12811,9 +11819,9 @@ void tetgenmesh::flip22(face* flipfaces, int flipflag, int chkencflag) // Shellmark does not change. // area constraint does not change. - // Transform abc -> cdb. + // Transform [a,b,c] -> [c,d,b]. setshvertices(flipfaces[0], pc, pd, pb); - // Transform bad -> dca. + // Transform [b,a,d] -> [d,c,a]. setshvertices(flipfaces[1], pd, pc, pa); // Update the point-to-subface map. @@ -12849,12 +11857,7 @@ void tetgenmesh::flip22(face* flipfaces, int flipflag, int chkencflag) ssbond(bdedges[i], bdsegs[(3 + i) % 4]); if (chkencflag & 1) { // Queue this segment for encroaching check. - if (!smarktest2ed(bdsegs[(3 + i) % 4])) { - bface = (badface *) badsubsegs->alloc(); - bface->ss = bdsegs[(3 + i) % 4]; - smarktest2(bface->ss); // Only queue it once. - bface->forg = sorg(bface->ss); // An alive badface. - } + enqueuesubface(badsubsegs, &(bdsegs[(3 + i) % 4])); } } else { ssdissolve(bdedges[i]); @@ -12864,12 +11867,7 @@ void tetgenmesh::flip22(face* flipfaces, int flipflag, int chkencflag) if (chkencflag & 2) { // Queue the flipped subfaces for quality/encroaching checks. for (i = 0; i < 2; i++) { - if (!smarktest2ed(flipfaces[i])) { - bface = (badface *) badsubfacs->alloc(); - bface->ss = flipfaces[i]; - smarktest2(bface->ss); // Only queue it once. - bface->forg = sorg(bface->ss); // An alive badface. - } + enqueuesubface(badsubfacs, &(flipfaces[i])); } } @@ -12900,22 +11898,17 @@ void tetgenmesh::flip22(face* flipfaces, int flipflag, int chkencflag) void tetgenmesh::flip31(face* flipfaces, int flipflag) { - face bdedges[3], outfaces[3], infaces[3], bdsegs[3]; - face checkface, checkseg; - point pa, pb, pc, delpt; - REAL area; + face bdedges[3], outfaces[3], infaces[3]; + face bdsegs[3]; + face checkface; + point pa, pb, pc; int i; - delpt = sorg(flipfaces[0]); pa = sdest(flipfaces[0]); pb = sdest(flipfaces[1]); pc = sdest(flipfaces[2]); - if (b->verbose > 3) { - printf(" flip 3-to-1: (%d, %d, %d) - %d)\n", pointmark(pa), - pointmark(pb), pointmark(pc), pointmark(delpt)); - } - // flip31count++; + flip31count++; // Collect all infos at the three boundary edges. for (i = 0; i < 3; i++) { @@ -12924,8 +11917,7 @@ void tetgenmesh::flip31(face* flipfaces, int flipflag) infaces[i] = outfaces[i]; sspivot(bdedges[i], bdsegs[i]); if (outfaces[i].sh != NULL) { - sspivot(bdedges[i], checkseg); - if (checkseg.sh != NULL) { + if (isshsubseg(bdedges[i])) { spivot(infaces[i], checkface); while (checkface.sh != bdedges[i].sh) { infaces[i] = checkface; @@ -12940,8 +11932,11 @@ void tetgenmesh::flip31(face* flipfaces, int flipflag) setshvertices(flipfaces[3], pa, pb,pc); setshellmark(flipfaces[3], shellmark(flipfaces[0])); if (checkconstraints) { - area = areabound(flipfaces[0]); - setareabound(flipfaces[3], area); + //area = areabound(flipfaces[0]); + setareabound(flipfaces[3], areabound(flipfaces[0])); + } + if (useinsertradius) { + setfacetindex(flipfaces[3], getfacetindex(flipfaces[0])); } // Update the point-to-subface map. @@ -12998,15 +11993,13 @@ long tetgenmesh::lawsonflip() { badface *popface; face flipfaces[2]; - face checkseg; point pa, pb, pc, pd; REAL sign; - long flipcount; + long flipcount = 0; if (b->verbose > 2) { printf(" Lawson flip %ld edges.\n", flippool->items); } - flipcount = flip22count; while (flipstack != (badface *) NULL) { @@ -13023,8 +12016,7 @@ long tetgenmesh::lawsonflip() // 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. - sspivot(flipfaces[0], checkseg); - if (checkseg.sh != NULL) continue; + if (isshsubseg(flipfaces[0])) continue; // Get the adjacent face. spivot(flipfaces[0], flipfaces[1]); @@ -13037,16 +12029,15 @@ long tetgenmesh::lawsonflip() if (sign < 0) { // It is non-locally Delaunay. Flip it. flip22(flipfaces, 1, 0); + flipcount++; } } if (b->verbose > 2) { - printf(" %ld edges stacked, %ld flips.\n", flippool->items, - flip22count - flipcount); + printf(" Performed %ld flips.\n", flipcount); } - assert(flippool->items == 0l); // SELF_CHECK - return flip22count - flipcount; + return flipcount; } /////////////////////////////////////////////////////////////////////////////// @@ -13058,75 +12049,72 @@ long tetgenmesh::lawsonflip() // 'caveshbdlist' contains new subfaces in C(p). If the new point lies on a // // segment, 'cavesegshlist' returns the two new subsegments. // // // -// NOTE: the old subfaces in C(p) are not deleted. Theyare needed in case we // -// want to remove the new point immedately. // +// '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 iloc, int bowywat, int rflag) { - triface adjtet; face cavesh, neighsh, *parysh; face newsh, casout, casin; - face aseg, bseg, aoutseg, boutseg; face checkseg; - point pa, pb, pc; - enum locateresult loc; - REAL sign, ori, area; + 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 (splitseg != NULL) { + if (bowywat == 3) { + loc = INSTAR; + } + + if ((splitseg != NULL) && (splitseg->sh != NULL)) { // A segment is going to be split, no point location. spivot(*splitseg, *searchsh); - loc = ONEDGE; + if (loc != INSTAR) loc = ONEDGE; } else { - loc = (enum locateresult) iloc; + if (loc != INSTAR) loc = (enum locateresult) iloc; if (loc == OUTSIDE) { // Do point location in surface mesh. if (searchsh->sh == NULL) { *searchsh = recentsh; } - // Start searching from 'searchsh'. - loc = slocate(insertpt, searchsh, 1, 1, 0); - } - } - - if (b->verbose > 2) { - if (searchsh->sh != NULL) { - pa = sorg(*searchsh); - pb = sdest(*searchsh); - pc = sapex(*searchsh); - printf(" Located subface (%d, %d, %d).\n", pointmark(pa), - pointmark(pb), pointmark(pc)); - } else { - assert(splitseg != NULL); - pa = sorg(*splitseg); - pb = sdest(*splitseg); - printf(" Located segment (%d, %d).\n", pointmark(pa),pointmark(pb)); + // Search the vertex. An above point must be provided ('aflag' = 1). + loc = slocate(insertpt, searchsh, 1, 1, rflag); } } -if (bowywat < 3) { // if (bowywat == 1 || bowywat == 2) { // Form the initial sC(p). if (loc == ONFACE) { - if (b->verbose > 2) { - printf(" Inside face.\n"); - } // Add the face into list (in B-W cavity). smarktest(*searchsh); caveshlist->newindex((void **) &parysh); *parysh = *searchsh; } else if (loc == ONEDGE) { - if (b->verbose > 2) { - printf(" On edge.\n"); - } - if (splitseg != NULL) { + if ((splitseg != NULL) && (splitseg->sh != NULL)) { splitseg->shver = 0; pa = sorg(*splitseg); } else { @@ -13137,9 +12125,7 @@ if (bowywat < 3) { // if (bowywat == 1 || bowywat == 2) { neighsh = *searchsh; while (1) { // Adjust the origin of its edge to be 'pa'. - if (sorg(neighsh) != pa) { - sesymself(neighsh); - } + if (sorg(neighsh) != pa) sesymself(neighsh); // Add this face into list (in B-W cavity). smarktest(neighsh); caveshlist->newindex((void **) &parysh); @@ -13155,18 +12141,12 @@ if (bowywat < 3) { // if (bowywat == 1 || bowywat == 2) { } } // If (not a non-dangling segment). } else if (loc == ONVERTEX) { - if (b->verbose > 2) { - printf(" On vertex.\n"); - } 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. - if (b->verbose > 2) { - printf(" Outside face.\n"); - } // 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. @@ -13176,9 +12156,7 @@ if (bowywat < 3) { // if (bowywat == 1 || bowywat == 2) { spivot(neighsh, casout); if (casout.sh == NULL) { // A convex hull edge. Is it visible by p. - pa = sorg(neighsh); - pb = sdest(neighsh); - ori = orient3d(pa, pb, dummypoint, insertpt); + ori = orient3d(sorg(neighsh), sdest(neighsh), dummypoint, insertpt); if (ori < 0) { *searchsh = neighsh; // Visible, update 'searchsh'. } else { @@ -13199,8 +12177,11 @@ if (bowywat < 3) { // if (bowywat == 1 || bowywat == 2) { setshvertices(newsh, pb, pa, insertpt); setshellmark(newsh, shellmark(*searchsh)); if (checkconstraints) { - area = areabound(*searchsh); - setareabound(newsh, area); + //area = areabound(*searchsh); + setareabound(newsh, areabound(*searchsh)); + } + if (useinsertradius) { + setfacetindex(newsh, getfacetindex(*searchsh)); } // Connect the new subface to the bottom subfaces. sbond1(newsh, *searchsh); @@ -13236,47 +12217,30 @@ if (bowywat < 3) { // if (bowywat == 1 || bowywat == 2) { // 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(). } -} else { - - // Under this case, the sub-cavity sC(p) has already been formed in - // insertvertex(). Check it. - // FOR DEBUG ONLY. - for (i = 0; i < caveshlist->objects; i++) { - cavesh = * (face *) fastlookup(caveshlist, i); - assert(smarktested(cavesh)); - } - if (splitseg != NULL) { - assert(smarktested(*splitseg)); - } - - -}// if (bowywat < 3) - // 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++) { - sspivot(cavesh, checkseg); - if (checkseg.sh == NULL) { + if (!isshsubseg(cavesh)) { spivot(cavesh, neighsh); if (neighsh.sh != NULL) { // The adjacent face exists. if (!smarktested(neighsh)) { if (bowywat) { - if (bowywat > 2) { + 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). - stpivot(neighsh, adjtet); - if (adjtet.tet == NULL) { + if (!isshtet(neighsh)) { // Check if the subface is non-Delaunay wrt. the new pt. - pa = sorg(neighsh); - pb = sdest(neighsh); - pc = sapex(neighsh); - sign = incircle3d(pa, pb, pc, insertpt); + sign = incircle3d(sorg(neighsh), sdest(neighsh), + sapex(neighsh), insertpt); } else { // It is connected to an adjacent tet. A boundary edge. sign = 1; @@ -13320,10 +12284,6 @@ if (bowywat < 3) { // if (bowywat == 1 || bowywat == 2) { } // j } // i - if (b->verbose > 3) { - printf(" Size of cavity: %ld faces, %ld bdry edges.\n", - caveshlist->objects, caveshbdlist->objects); - } // Creating new subfaces. for (i = 0; i < caveshbdlist->objects; i++) { @@ -13336,10 +12296,12 @@ if (bowywat < 3) { // if (bowywat == 1 || bowywat == 2) { makeshellface(subfaces, &newsh); setshvertices(newsh, pa, pb, insertpt); setshellmark(newsh, shellmark(*parysh)); - setshelltype(newsh, shelltype(*parysh)); if (checkconstraints) { - area = areabound(*parysh); - setareabound(newsh, area); + //area = areabound(*parysh); + setareabound(newsh, areabound(*parysh)); + } + if (useinsertradius) { + setfacetindex(newsh, getfacetindex(*parysh)); } // Update the point-to-subface map. if (pointtype(pa) == FREEFACETVERTEX) { @@ -13357,7 +12319,7 @@ if (bowywat < 3) { // if (bowywat == 1 || bowywat == 2) { checkseg.shver = 0; if (sorg(newsh) != sorg(checkseg)) { sesymself(newsh); - sesymself(*parysh); // This side should also be inversed. + sesymself(*parysh); // This side should also be inverse. } spivot(casin, neighsh); while (neighsh.sh != parysh->sh) { @@ -13376,8 +12338,10 @@ if (bowywat < 3) { // if (bowywat == 1 || bowywat == 2) { sbond1(*parysh, newsh); } - // Set a handle for searching. - recentsh = newsh; + if (newsh.sh != NULL) { + // Set a handle for searching. + recentsh = newsh; + } // Update the point-to-subface map. if (pointtype(insertpt) == FREEFACETVERTEX) { @@ -13405,8 +12369,6 @@ if (bowywat < 3) { // if (bowywat == 1 || bowywat == 2) { if (neighsh.sh != NULL) { // Now 'neighsh' is a new subface at edge [b, #]. if (sorg(neighsh) != pb) sesymself(neighsh); - assert(sorg(neighsh) == pb); // SELF_CHECK - assert(sapex(neighsh) == insertpt); // SELF_CHECK senext2self(neighsh); // Go to the open edge [p, b]. sbond(newsh, neighsh); } else { @@ -13431,8 +12393,6 @@ if (bowywat < 3) { // if (bowywat == 1 || bowywat == 2) { if (neighsh.sh != NULL) { // Now 'neighsh' is a new subface at edge [#, a]. if (sdest(neighsh) != pa) sesymself(neighsh); - assert(sdest(neighsh) == pa); // SELF_CHECK - assert(sapex(neighsh) == insertpt); // SELF_CHECK senextself(neighsh); // Go to the open edge [a, p]. sbond(newsh, neighsh); } else { @@ -13442,13 +12402,15 @@ if (bowywat < 3) { // if (bowywat == 1 || bowywat == 2) { } } - if (loc == ONEDGE) { - + 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); @@ -13468,7 +12430,7 @@ if (bowywat < 3) { // if (bowywat == 1 || bowywat == 2) { } assert(sapex(neighsh) == insertpt); // SELF_CHECK // Connect adjacent faces at two other edges of cavesh and neighsh. - // As a result, the two degenrated new faces are squessed from the + // 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). @@ -13480,7 +12442,7 @@ if (bowywat < 3) { // if (bowywat == 1 || bowywat == 2) { sbond1(newsh, casout); // newsh <- casout. } } else { - // There is only one subface containing this edge [a,b]. Squeese the + // 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. @@ -13490,7 +12452,7 @@ if (bowywat < 3) { // if (bowywat == 1 || bowywat == 2) { sdissolve(newsh); } } - recentsh = newsh; + //recentsh = newsh; // Update the point-to-subface map. if (pointtype(insertpt) == FREEFACETVERTEX) { setpoint2sh(insertpt, sencode(newsh)); @@ -13498,18 +12460,14 @@ if (bowywat < 3) { // if (bowywat == 1 || bowywat == 2) { } } - if (splitseg != NULL) { - if (bowywat < 3) { + 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); - if (b->verbose > 2) { - printf(" Split seg (%d, %d) by %d.\n", pointmark(pa), - pointmark(pb), pointmark(insertpt)); - } // Insert the new point p. makeshellface(subsegs, &aseg); @@ -13519,12 +12477,14 @@ if (bowywat < 3) { // if (bowywat == 1 || bowywat == 2) { setshvertices(bseg, insertpt, pb, NULL); setshellmark(aseg, shellmark(*splitseg)); setshellmark(bseg, shellmark(*splitseg)); - setshelltype(aseg, shelltype(*splitseg)); - setshelltype(bseg, shelltype(*splitseg)); if (checkconstraints) { - setareabound(bseg, areabound(*splitseg)); + 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. @@ -13546,7 +12506,7 @@ if (bowywat < 3) { // if (bowywat == 1 || bowywat == 2) { sbond(aoutseg, boutseg); // Connect subsegs [a, p] and [p, b] to adjacent new subfaces. - // Although the degenerated new faces have been squeesed. They still + // 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); @@ -13565,11 +12525,17 @@ if (bowywat < 3) { // if (bowywat == 1 || bowywat == 2) { // Let the point remember the segment it lies on. - setpoint2sh(insertpt, sencode(aseg)); + if (pointtype(insertpt) == FREESEGVERTEX) { + setpoint2sh(insertpt, sencode(aseg)); + } // Update the point-to-seg map. - setpoint2sh(pa, sencode(aseg)); - setpoint2sh(pb, sencode(bseg)); - } // if (splitseg != NULL) + 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++) { @@ -13581,7 +12547,7 @@ if (bowywat < 3) { // if (bowywat == 1 || bowywat == 2) { } cavesegshlist->restart(); - if (splitseg != NULL) { + if ((splitseg != NULL) && (splitseg->sh != NULL)) { // Return the two new subsegments (for further process). // Re-use 'cavesegshlist'. cavesegshlist->newindex((void **) &parysh); @@ -13589,7 +12555,6 @@ if (bowywat < 3) { // if (bowywat == 1 || bowywat == 2) { cavesegshlist->newindex((void **) &parysh); *parysh = bseg; } - } // if (loc == ONEDGE) @@ -13604,39 +12569,34 @@ if (bowywat < 3) { // if (bowywat == 1 || bowywat == 2) { // a segment vertex, and the origin of 'parentseg' is p. Otherwise, p is a // // facet vertex, and the origin of 'parentsh' is p. // // // -// If 'lawson' > 0, the Lawson flip algorithm is used to recover Delaunay- // -// ness after p is removed. // -// // // 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], *parysh; - face spinsh, startsh, neighsh, nextsh, fakesh; - face abseg, prevseg, checkseg; - face adjseg1, adjseg2; + face flipfaces[4], spinsh, *parysh; point pa, pb, pc, pd; - int it, i, j; - - REAL *norm, n1[3], n2[3]; - REAL len, len1, len2; REAL ori1, ori2; + int it, i, j; if (parentseg != NULL) { - assert(sorg(*parentseg) == delpt); - assert(parentseg->shver == 0); // '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); - assert(prevseg.sh != NULL); prevseg.shver = 0; assert(sdest(prevseg) == delpt); // Restore the original segment [a,b]. @@ -13649,10 +12609,12 @@ int tetgenmesh::sremovevertex(point delpt, face* parentsh, face* parentseg, makeshellface(subsegs, &abseg); setshvertices(abseg, pa, pb, NULL); setshellmark(abseg, shellmark(*parentseg)); - setshelltype(abseg, shelltype(*parentseg)); if (checkconstraints) { setareabound(abseg, areabound(*parentseg)); } + if (useinsertradius) { + setfacetindex(abseg, getfacetindex(*parentseg)); + } // Connect [#, a]<->[a, b]. senext2(prevseg, adjseg1); spivotself(adjseg1); @@ -13680,16 +12642,17 @@ int tetgenmesh::sremovevertex(point delpt, face* parentsh, face* parentseg, // Get the faces in face ring at segment [p, b]. // Re-use array 'caveshlist'. spivot(*parentseg, *parentsh); - 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 == NULL) break; - if (spinsh.sh == parentsh->sh) break; - } + 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). @@ -13741,12 +12704,7 @@ int tetgenmesh::sremovevertex(point delpt, face* parentsh, face* parentseg, // 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); - } - assert(sorg(startsh) == pa); - assert(sdest(startsh) == pb); - assert(sapex(startsh) != delpt); + if (sorg(startsh) != pa) sesymself(startsh); sdissolve(startsh); // Connect fakesh to the segment [a,b]. ssbond(startsh, abseg); @@ -13797,9 +12755,7 @@ int tetgenmesh::sremovevertex(point delpt, face* parentsh, face* parentseg, parentsh = (face *) fastlookup(cavesegshlist, it); // [a,b,p] senextself(*parentsh); // [b,p,a]. spivotself(*parentsh); - if (sorg(*parentsh) != delpt) { - sesymself(*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. @@ -13817,10 +12773,8 @@ int tetgenmesh::sremovevertex(point delpt, face* parentsh, face* parentseg, spivotself(spinsh); assert(spinsh.sh != NULL); if (spinsh.sh == parentsh->sh) break; - if (sorg(spinsh) != delpt) { - sesymself(spinsh); - assert(sorg(spinsh) == delpt); - } + if (sorg(spinsh) != delpt) sesymself(spinsh); + assert(sorg(spinsh) == delpt); } // while (1) if (caveshlist->objects == 3) { @@ -13839,9 +12793,6 @@ int tetgenmesh::sremovevertex(point delpt, face* parentsh, face* parentseg, *parysh = flipfaces[3]; // The vertex is removed. break; - } else { - // There should be more than 3 subfaces in list. - assert(caveshlist->objects > 3); } // Search an edge to flip. @@ -13849,35 +12800,15 @@ int tetgenmesh::sremovevertex(point delpt, face* parentsh, face* parentseg, parysh = (face *) fastlookup(caveshlist, i); flipfaces[0] = *parysh; spivot(flipfaces[0], flipfaces[1]); - if (sorg(flipfaces[0]) != sdest(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]); - // 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 = DIST(pa, pb); - dummypoint[0] = pa[0] + len * norm[0]; - dummypoint[1] = pa[1] + len * norm[1]; - dummypoint[2] = pa[2] + len * norm[2]; + 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); @@ -13894,6 +12825,7 @@ int tetgenmesh::sremovevertex(point delpt, face* parentsh, face* parentseg, } } // } // i + if (i == caveshlist->objects) { // This can happen only if there are 4 edges at p, and they are // orthogonal to each other, see Fig. 2010-11-01. @@ -13911,6 +12843,7 @@ int tetgenmesh::sremovevertex(point delpt, face* parentsh, face* parentseg, caveshbdlist->newindex((void **) &parysh); *parysh = flipfaces[0]; } + // The edge list at p are changed. caveshlist->restart(); } // while (1) @@ -13949,7 +12882,7 @@ int tetgenmesh::sremovevertex(point delpt, face* parentsh, face* parentseg, // 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 inducates the following cases: // +// 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'. // @@ -13962,35 +12895,19 @@ enum tetgenmesh::locateresult tetgenmesh::slocate(point searchpt, face* searchsh, int aflag, int cflag, int rflag) { face neighsh; - face checkseg; - point pa, pb, pc, pd, *parypt; + point pa, pb, pc; enum locateresult loc; enum {MOVE_BC, MOVE_CA} nextmove; REAL ori, ori_bc, ori_ca; - REAL dist_bc, dist_ca; int i; - // For finding an approximate location. - //REAL n[3], len, len3; - REAL n[3], area_abc, area_abp, area_bcp, area_cap; - pa = sorg(*searchsh); pb = sdest(*searchsh); pc = sapex(*searchsh); if (!aflag) { // No above point is given. Calculate an above point for this facet. - // Re-use the 'cavetetvertlist'. - cavetetvertlist->newindex((void **) &parypt); - *parypt = pa; - cavetetvertlist->newindex((void **) &parypt); - *parypt = pb; - cavetetvertlist->newindex((void **) &parypt); - *parypt = pc; - cavetetvertlist->newindex((void **) &parypt); - *parypt = searchpt; - calculateabovepoint(cavetetvertlist, NULL, NULL, NULL); - cavetetvertlist->restart(); + calculateabovepoint4(pa, pb, pc, searchpt); } // 'dummypoint' is given. Make sure it is above [a,b,c] @@ -14025,25 +12942,7 @@ enum tetgenmesh::locateresult tetgenmesh::slocate(point searchpt, if (ori_bc < 0) { if (ori_ca < 0) { // (--) // Any of the edges is a viable move. - senext(*searchsh, neighsh); // At edge [b, c]. - spivotself(neighsh); - if (neighsh.sh != NULL) { - pd = sapex(neighsh); - dist_bc = NORM2(searchpt[0] - pd[0], searchpt[1] - pd[1], - searchpt[2] - pd[2]); - } else { - dist_bc = NORM2(xmax - xmin, ymax - ymin, zmax - zmin); - } - senext2(*searchsh, neighsh); // At edge [c, a]. - spivotself(neighsh); - if (neighsh.sh != NULL) { - pd = sapex(neighsh); - dist_ca = NORM2(searchpt[0] - pd[0], searchpt[1] - pd[1], - searchpt[2] - pd[2]); - } else { - dist_ca = dist_bc; - } - if (dist_ca < dist_bc) { + if (randomnation(2)) { nextmove = MOVE_CA; } else { nextmove = MOVE_BC; @@ -14088,8 +12987,7 @@ enum tetgenmesh::locateresult tetgenmesh::slocate(point searchpt, } if (!cflag) { // NON-convex case. Check if we will cross a boundary. - sspivot(*searchsh, checkseg); - if (checkseg.sh != NULL) { + if (isshsubseg(*searchsh)) { return ENCSEGMENT; } } @@ -14121,6 +13019,8 @@ enum tetgenmesh::locateresult tetgenmesh::slocate(point searchpt, 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); @@ -14188,23 +13088,21 @@ enum tetgenmesh::locateresult tetgenmesh::slocate(point searchpt, // 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 "locaked" // +// 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) +enum tetgenmesh::interresult tetgenmesh::sscoutsegment(face *searchsh, + point endpt) { face flipshs[2], neighsh; - face newseg, checkseg; + face newseg; point startpt, pa, pb, pc, pd; enum interresult dir; enum {MOVE_AB, MOVE_CA} nextmove; - REAL ori_ab, ori_ca; - REAL dist_b, dist_c; - int shmark = 0; + REAL ori_ab, ori_ca, len; // The origin of 'searchsh' is fixed. startpt = sorg(*searchsh); // pa = startpt; @@ -14214,6 +13112,7 @@ enum tetgenmesh::interresult 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) { @@ -14232,22 +13131,22 @@ enum tetgenmesh::interresult break; } - ori_ab = orient3d(startpt, pb, dummypoint, endpt); - ori_ca = orient3d(pc, startpt, dummypoint, endpt); + // 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. - spivot(*searchsh, neighsh); // At edge [a, b]. - assert(neighsh.sh != NULL); // SELF_CHECK - pd = sapex(neighsh); - dist_b = NORM2(endpt[0] - pd[0], endpt[1] - pd[1], endpt[2] - pd[2]); - senext2(*searchsh, neighsh); // At edge [c, a]. - spivotself(neighsh); - assert(neighsh.sh != NULL); // SELF_CHECK - pd = sapex(neighsh); - dist_c = NORM2(endpt[0] - pd[0], endpt[1] - pd[1], endpt[2] - pd[2]); - if (dist_c < dist_b) { + if (randomnation(2)) { nextmove = MOVE_CA; } else { nextmove = MOVE_AB; @@ -14287,13 +13186,32 @@ enum tetgenmesh::interresult // Move 'searchsh' to the next face, keep the origin unchanged. if (nextmove == MOVE_AB) { spivot(*searchsh, neighsh); - if (sorg(neighsh) != pb) sesymself(neighsh); - senext(neighsh, *searchsh); + 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 (sdest(neighsh) != pc) sesymself(neighsh); - *searchsh = 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 @@ -14303,11 +13221,8 @@ enum tetgenmesh::interresult // Insert the segment into the triangulation. makeshellface(subsegs, &newseg); setshvertices(newseg, startpt, endpt, NULL); - // Set the actual segment marker. - if (in->facetmarkerlist != NULL) { - shmark = shellmark(*searchsh); - setshellmark(newseg, in->facetmarkerlist[shmark - 1]); - } + // Set the default segment marker. + setshellmark(newseg, 1); ssbond(*searchsh, newseg); spivot(*searchsh, neighsh); if (neighsh.sh != NULL) { @@ -14324,21 +13239,20 @@ enum tetgenmesh::interresult if (dir == ACROSSEDGE) { // Edge [b, c] intersects with the segment. senext(*searchsh, flipshs[0]); - sspivot(flipshs[0], checkseg); - if (checkseg.sh != NULL) { + 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(3); + 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 invered triangle, check it. + // The flip may create an inverted triangle, check it. pa = sapex(flipshs[1]); pb = sapex(flipshs[0]); pc = sorg(flipshs[0]); @@ -14349,16 +13263,8 @@ enum tetgenmesh::interresult ori_ca = orient3d(pd, pc, dummypoint, pa); //assert(ori_ab * ori_ca != 0); // SELF_CHECK if (ori_ab < 0) { - if (b->verbose > 2) { - printf(" Queue an inversed triangle (%d, %d, %d) %d\n", - pointmark(pc), pointmark(pd), pointmark(pb), pointmark(pa)); - } flipshpush(&(flipshs[0])); // push it to 'flipstack' } else if (ori_ca < 0) { - if (b->verbose > 2) { - printf(" Queue an inversed triangle (%d, %d, %d) %d\n", - pointmark(pd), pointmark(pc), pointmark(pa), pointmark(pb)); - } flipshpush(&(flipshs[1])); // // push it to 'flipstack' } // Set 'searchsh' s.t. its origin is 'startpt'. @@ -14380,7 +13286,6 @@ enum tetgenmesh::interresult void tetgenmesh::scarveholes(int holes, REAL* holelist) { face *parysh, searchsh, neighsh; - face checkseg; enum locateresult loc; int i, j; @@ -14403,8 +13308,7 @@ void tetgenmesh::scarveholes(int holes, REAL* holelist) } } else { // A hull side. Check if it is protected by a segment. - sspivot(searchsh, checkseg); - if (checkseg.sh == NULL) { + if (!isshsubseg(searchsh)) { // Not protected. Save this face. if (!sinfected(searchsh)) { sinfect(searchsh); @@ -14436,8 +13340,7 @@ void tetgenmesh::scarveholes(int holes, REAL* holelist) for (j = 0; j < 3; j++) { spivot(searchsh, neighsh); if (neighsh.sh != NULL) { - sspivot(searchsh, checkseg); - if (checkseg.sh == NULL) { + if (!isshsubseg(searchsh)) { if (!sinfected(neighsh)) { sinfect(neighsh); caveshbdlist->newindex((void **) &parysh); @@ -14469,6 +13372,9 @@ void tetgenmesh::scarveholes(int holes, REAL* holelist) // // // triangulate() Create a CDT for the facet. // // // +// All vertices of the triangulation have type FACETVERTEX. The actual type // +// of boundary vertices are set by the routine unifysements(). // +// // /////////////////////////////////////////////////////////////////////////////// void tetgenmesh::triangulate(int shmark, arraypool* ptlist, arraypool* conlist, @@ -14477,7 +13383,6 @@ void tetgenmesh::triangulate(int shmark, arraypool* ptlist, arraypool* conlist, face searchsh, newsh, *parysh; face newseg; point pa, pb, pc, *ppt, *cons; - enum locateresult loc; int iloc; int i, j; @@ -14493,61 +13398,37 @@ void tetgenmesh::triangulate(int shmark, arraypool* ptlist, arraypool* conlist, if (ptlist->objects < 2l) { // Not a segment or a facet. return; - } if (ptlist->objects == 2l) { + } + + 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 actual segment marker. - if (in->facetmarkerlist != NULL) { - setshellmark(newseg, in->facetmarkerlist[shmark - 1]); - } + // Set the default segment marker '1'. + setshellmark(newseg, 1); } if (pointtype(pa) == VOLVERTEX) { - setpointtype(pa, RIDGEVERTEX); + setpointtype(pa, FACETVERTEX); } if (pointtype(pb) == VOLVERTEX) { - setpointtype(pb, RIDGEVERTEX); + setpointtype(pb, FACETVERTEX); } return; - } if (ptlist->objects == 3l) { - // The facet has only one triangle. + } + + + if (ptlist->objects == 3) { pa = * (point *) fastlookup(ptlist, 0); pb = * (point *) fastlookup(ptlist, 1); pc = * (point *) fastlookup(ptlist, 2); - if (triarea(pa, pb, pc) > 0) { - makeshellface(subfaces, &newsh); - setshvertices(newsh, pa, pb, pc); - setshellmark(newsh, shmark); - // Create three new segments. - for (i = 0; i < 3; i++) { - makeshellface(subsegs, &newseg); - setshvertices(newseg, sorg(newsh), sdest(newsh), NULL); - // Set the actual segment marker. - if (in->facetmarkerlist != NULL) { - setshellmark(newseg, in->facetmarkerlist[shmark - 1]); - } - ssbond(newsh, newseg); - senextself(newsh); - } - if (pointtype(pa) == VOLVERTEX) { - setpointtype(pa, FACETVERTEX); - } - if (pointtype(pb) == VOLVERTEX) { - setpointtype(pb, FACETVERTEX); - } - if (pointtype(pc) == VOLVERTEX) { - setpointtype(pc, FACETVERTEX); - } + } else { + // Calculate an above point of this facet. + if (!calculateabovepoint(ptlist, &pa, &pb, &pc)) { + return; // The point set is degenerate. } - return; - } - - // Calulcate an above point of this facet. - if (!calculateabovepoint(ptlist, &pa, &pb, &pc)) { - return; // The point set is degenerate. } // Create an initial triangulation. @@ -14562,8 +13443,36 @@ void tetgenmesh::triangulate(int shmark, arraypool* ptlist, arraypool* conlist, if (pointtype(pb) == VOLVERTEX) { setpointtype(pb, FACETVERTEX); } - if (pointtype(pc) == VOLVERTEX) { - setpointtype(pc, 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. @@ -14575,9 +13484,8 @@ void tetgenmesh::triangulate(int shmark, arraypool* ptlist, arraypool* conlist, if (!pinfected(*ppt)) { searchsh = recentsh; // Start from 'recentsh'. iloc = (int) OUTSIDE; - if (b->verbose > 2) printf(" # %d", i); - loc = (enum locateresult) sinsertvertex(*ppt, &searchsh, NULL, iloc, 1); - assert(loc != ONVERTEX); // SELF_CHECK + // 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); } @@ -14599,8 +13507,26 @@ void tetgenmesh::triangulate(int shmark, arraypool* ptlist, arraypool* conlist, for (i = 0; i < conlist->objects; i++) { cons = (point *) fastlookup(conlist, i); searchsh = recentsh; - loc = slocate(cons[0], &searchsh, 1, 1, 0); - assert(loc == ONVERTEX); // SELF_CHECK + 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) { @@ -14621,115 +13547,51 @@ void tetgenmesh::triangulate(int shmark, arraypool* ptlist, arraypool* conlist, // If c = d, then f1 and f2 are identical. Otherwise, these two subfaces // // intersect, and the mesher is stopped. // // // -// If the two subfaces are indentical, we try to replace f2 by f1, i.e, all // +// 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) { - face casout, casin, neighsh; - face sseg, checkseg; - point pa, pb, pc, pd; - int i; + if (b->psc) { + // In this case, it is possible that two subfaces are identical. + // While they must belong to two different surfaces. + return; + } - assert(f1->sh != f2->sh); // SELF_CHECK + point pa, pb, pc, pd; pa = sorg(*f1); pb = sdest(*f1); pc = sapex(*f1); - - assert(sorg(*f2) == pa); // SELF_CHECK - assert(sdest(*f2) == pb); // SELF_CHECK 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)); + pointmark(pa), pointmark(pb), pointmark(pc), shellmark(*f1)); printf(" 2nd: [%d, %d, %d] #%d\n", - pointmark(pa), pointmark(pb), pointmark(pd), shellmark(*f2)); - terminatetetgen(3); + 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)); + pointmark(pa), pointmark(pb), pointmark(pc), shellmark(*f1)); printf(" 2nd: [%d, %d, %d] #%d\n", - pointmark(pa), pointmark(pb), pointmark(pd), shellmark(*f2)); - terminatetetgen(3); - } - - // f1 and f2 are identical, replace f2 by f1. - if (!b->quiet) { - printf("Warning: Facet #%d is duplicated with Facet #%d. Removed!\n", - shellmark(*f2), shellmark(*f1)); + pointmark(pa), pointmark(pb), pointmark(pd), shellmark(*f2)); + terminatetetgen(this, 3); } - // Make possible disconnections/reconnections at neighbors of f2. - for (i = 0; i < 3; i++) { - spivot(*f1, casout); - if (casout.sh == NULL) { - // f1 has no adjacent subfaces yet. - spivot(*f2, casout); - if (casout.sh != NULL) { - // Re-direct the adjacent connections of f2 to f1. - casin = casout; - spivot(casin, neighsh); - while (neighsh.sh != f2->sh) { - casin = neighsh; - spivot(casin, neighsh); - } - // Connect casout <= f1 <= casin. - sbond1(*f1, casout); - sbond1(casin, *f1); - } - } - sspivot(*f2, sseg); - if (sseg.sh != NULL) { - // f2 has a segment. It must be different to f1's. - sspivot(*f1, checkseg); // SELF_CHECK - if (checkseg.sh != NULL) { // SELF_CHECK - assert(checkseg.sh != sseg.sh); // SELF_CHECK - } - // Disconnect bonds of subfaces to this segment. - spivot(*f2, casout); - if (casout.sh != NULL) { - casin = casout; - ssdissolve(casin); - spivot(casin, neighsh); - while (neighsh.sh != f2->sh) { - casin = neighsh; - ssdissolve(casin); - spivot(casin, neighsh); - } - } - // Delete the segment. - shellfacedealloc(subsegs, sseg.sh); - } - spivot(*f2, casout); - if (casout.sh != NULL) { - // Find the subface (casin) pointing to f2. - casin = casout; - spivot(casin, neighsh); - while (neighsh.sh != f2->sh) { - casin = neighsh; - spivot(casin, neighsh); - } - // Disconnect f2 <= casin. - sdissolve(casin); - } - senextself(*f1); - senextself(*f2); - } // i - - // Delete f2. - shellfacedealloc(subfaces, f2->sh); } /////////////////////////////////////////////////////////////////////////////// // // // 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() @@ -14750,6 +13612,79 @@ void tetgenmesh::unifysegments() // 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); @@ -14852,7 +13787,7 @@ void tetgenmesh::unifysegments() // 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) { + if (dot(n1, n2) > 0) { unifysubfaces(&(f1->ss), &sface); } else { unifysubfaces(&(f2->ss), &sface); @@ -14878,7 +13813,7 @@ void tetgenmesh::unifysegments() // 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) { + if (dot(n1, n2) > 0) { // The two faces are codirectional as well. unifysubfaces(&(f1->ss), &sface); } @@ -14900,17 +13835,16 @@ void tetgenmesh::unifysegments() } } // for (k = idx2faclist[idx]; ...) - if (b->verbose > 2) { - printf(" Found %ld segments at (%d %d).\n", flippool->items, - pointmark(torg), pointmark(tdest)); + 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)); + } } - //if (b->nobisect || b->nomerge) { // -Y or -M - // Set the vertex types of the endpoints of the segment. - setpointtype(torg, RIDGEVERTEX); - setpointtype(tdest, RIDGEVERTEX); - //} - // Set the connection between this segment and faces containing it, // at the same time, remove redundant segments. f1 = facelink; @@ -14930,21 +13864,30 @@ void tetgenmesh::unifysegments() f1 = facelink; for (k = 1; k <= flippool->items; k++) { k < flippool->items ? f2 = f1->nextitem : f2 = facelink; - if (b->verbose > 3) { - printf(" Bond subfaces (%d, %d, %d) and (%d, %d, %d).\n", - pointmark(torg), pointmark(tdest), pointmark(sapex(f1->ss)), - pointmark(torg), pointmark(tdest), pointmark(sapex(f2->ss))); - } sbond1(f1->ss, f2->ss); f1 = f2; } } - // // All identified segments has a marker "-1". - //setshellmark(subsegloop, -1); // 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); } @@ -15009,11 +13952,6 @@ void tetgenmesh::mergefacets() ang = facedihedral(pa, pb, pc, pd); if (ang > PI) ang = (2 * PI - ang); if (ang > ang_tol) { - if (b->verbose > 2) { - printf(" Merge at segment (%d, %d)-(%d, %d) ang = %g\n", - pointmark(pa), pointmark(pb), pointmark(pc), - pointmark(pd), ang / PI * 180.0); - } remsegcount++; ssdissolve(parentsh); ssdissolve(neighsh); @@ -15033,7 +13971,6 @@ void tetgenmesh::mergefacets() lawsonflip(); // Recover Delaunayness. } - if (b->verbose > 1) { printf(" %d segments are removed.\n", remsegcount); } @@ -15058,19 +13995,30 @@ void tetgenmesh::identifypscedges(point *idx2verlist) int* idx2shlist; face searchsh, neighsh; face segloop, checkseg, newseg; - point checkpt, pa, pb; + 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"); } - //assert(in->numberofedges > 0); // SELF_CHECK - //assert(in->edgemarkerlist != NULL); // SELF_CHECK - // All identified segments have the initial marker '0'. - // All segments inserted here should have a non-zero marker. + // 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); @@ -15078,7 +14026,10 @@ void tetgenmesh::identifypscedges(point *idx2verlist) // 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++) { @@ -15095,43 +14046,31 @@ void tetgenmesh::identifypscedges(point *idx2verlist) } } } // j - edgemarker = 0; - if (in->edgemarkerlist) { - edgemarker = in->edgemarkerlist[i]; - } - if (edgemarker == 0) { - edgemarker = 1; - } - // We should find a subface having this edge. + if (searchsh.sh != NULL) { // Check if this edge is already a segment of the mesh. sspivot(searchsh, checkseg); if (checkseg.sh != NULL) { - // There should be no duplicated edges. - assert(shellmark(checkseg) == 0); - setshellmark(checkseg, edgemarker); + // This segment already exist. + newseg = checkseg; } else { // Create a new segment at this edge. pa = sorg(searchsh); pb = sdest(searchsh); - if (b->verbose > 2) { - printf(" Create a new segment (%d, %d).\n", - pointmark(pa), pointmark(pb)); - } makeshellface(subsegs, &newseg); setshvertices(newseg, pa, pb, NULL); - setshellmark(newseg, edgemarker); ssbond(searchsh, newseg); spivot(searchsh, neighsh); if (neighsh.sh != NULL) { ssbond(neighsh, newseg); - // There should be only two subfaces at this segment. - spivotself(neighsh); // SELF_CHECK - assert(neighsh.sh == searchsh.sh); } - if (!b->psc) { - setpointtype(pa, RIDGEVERTEX); - setpointtype(pb, RIDGEVERTEX); + if (b->psc) { + if (pointtype(pa) == FREESEGVERTEX) { + setpoint2sh(pa, sencode(newseg)); + } + if (pointtype(pb) == FREESEGVERTEX) { + setpoint2sh(pb, sencode(newseg)); + } } } } else { @@ -15139,49 +14078,91 @@ void tetgenmesh::identifypscedges(point *idx2verlist) // Get the two endpoints of this segment. pa = idx2verlist[endpts[0]]; pb = idx2verlist[endpts[1]]; - if (b->verbose > 2) { - printf(" Create a new segment (%d, %d) - dangling.\n", - pointmark(pa), pointmark(pb)); + // 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; + } } - makeshellface(subsegs, &newseg); - setshvertices(newseg, pa, pb, NULL); - setshellmark(newseg, edgemarker); - //if (!b->psc) { - setpointtype(pa, RIDGEVERTEX); - setpointtype(pb, RIDGEVERTEX); - //} } } // i + + delete [] shperverlist; + delete [] idx2shlist; + if (b->psc) { - // Delete all segments of the mesh with a marker '0'. + // Removing all segments with a marker '-1'. subsegs->traversalinit(); segloop.sh = shellfacetraverse(subsegs); while (segloop.sh != NULL) { - if (shellmark(segloop) == 0) { - if (b->verbose > 2) { - printf(" Remove a segment (%d, %d).\n", - pointmark(sorg(segloop)), pointmark(sdest(segloop))); - } - spivot(segloop, searchsh); - if (searchsh.sh != NULL) { - ssdissolve(searchsh); - spivot(searchsh, neighsh); - if (neighsh.sh != NULL) { - ssdissolve(neighsh); - // There should be only two subfaces at this segment. - spivotself(neighsh); // SELF_CHECK - assert(neighsh.sh == searchsh.sh); - } - } + 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); - delete [] shperverlist; - delete [] idx2shlist; + 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; + } } /////////////////////////////////////////////////////////////////////////////// @@ -15324,16 +14305,27 @@ void tetgenmesh::meshsurface() if (!b->diagnose) { // Remove redundant segments and build the face links. unifysegments(); - } - - if (!b->nomerge && !b->nobisect && !b->diagnose) { - // 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 && !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) { @@ -15359,7 +14351,7 @@ void tetgenmesh::meshsurface() // 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-axies. The split criteria are // +// 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: // // // @@ -15401,11 +14393,11 @@ void tetgenmesh::interecursive(shellface** subfacearray, int arraysize, leftarray = new shellface*[arraysize]; if (leftarray == NULL) { - terminatetetgen(1); + terminatetetgen(this, 1); } rightarray = new shellface*[arraysize]; if (rightarray == NULL) { - terminatetetgen(1); + terminatetetgen(this, 1); } leftsize = rightsize = 0; @@ -15442,9 +14434,7 @@ void tetgenmesh::interecursive(shellface** subfacearray, int arraysize, toright = true; } // At least one is true; -#ifdef SELF_CHECK assert(!(toleft == false && toright == false)); -#endif if (toleft) { leftarray[leftsize] = sface1.sh; leftsize++; @@ -15574,7 +14564,7 @@ void tetgenmesh::detectinterfaces() internum = 0; // Recursively split the set of triangles into two sets using a cut plane - // parallel to x-, or, y-, or z-axies. Stop splitting when the number + // 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); @@ -15617,141 +14607,78 @@ void tetgenmesh::detectinterfaces() /////////////////////////////////////////////////////////////////////////////// // // -// markacutevertices() Classify vertices as ACUTEVERTEXs or RIDGEVERTEXs. // +// makesegmentendpointsmap() Create a map from a segment to its endpoints.// // // -// Initially all segment vertices have type RIDGEVERTEX. A segment is acute // -// if there are at least two segments incident at it form an angle less than // -// theta (= 60 degree). // -// // -// The minimum segment-segment angle (minfaceang) is calculated. // +// 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::markacutevertices() +void tetgenmesh::makesegmentendpointsmap() { - face* segperverlist; - int* idx2seglist; - point pa, pb, pc; - REAL anglimit, ang; - bool acuteflag; - int acutecount; - int idx, i, j; - - REAL sharpanglimit; - int sharpsegcount; + arraypool *segptlist; + face segloop, prevseg, nextseg; + point eorg, edest, *parypt; + int segindex = 0, idx = 0; + int i; - if (b->verbose) { - printf(" Marking acute vertices.\n"); + if (b->verbose > 0) { + printf(" Creating the segment-endpoints map.\n"); } - anglimit = PI / 3.0; // 60 degree. - sharpanglimit = 10.0 / 180.0 * PI; // 10 degree. - minfaceang = PI; // 180 degree. - acutecount = sharpsegcount = 0; - // Construct a map from points to segments. - makepoint2submap(subsegs, idx2seglist, segperverlist); + segptlist = new arraypool(2 * sizeof(point), 10); - // Loop over the set of vertices. - points->traversalinit(); - pa = pointtraverse(); - while (pa != NULL) { - idx = pointmark(pa) - in->firstnumber; - // Mark it if it is an endpoint of some segments. - if (idx2seglist[idx + 1] > idx2seglist[idx]) { - if (b->psc) { - // Only test it if it is an input vertex. - if (pointtype(pa) == FREESEGVERTEX) { - pa = pointtraverse(); - continue; - } - } - acuteflag = false; - // Do a brute-force pair-pair check. - for (i=idx2seglist[idx]; i<idx2seglist[idx + 1]; i++) { - pb = sdest(segperverlist[i]); - //for (j = i + 1; j < idx2seglist[idx + 1] && !acuteflag; j++) { - for (j = i + 1; j < idx2seglist[idx + 1]; j++) { - pc = sdest(segperverlist[j]); - ang = interiorangle(pa, pb, pc, NULL); - //acuteflag = ang < anglimit; - if (!acuteflag) { - acuteflag = ang < anglimit; - } - // Remember the smallest angle. - if (ang < minfaceang) minfaceang = ang; - // Mark segments at extremely small angle. - if (ang < sharpanglimit) { - if (shelltype(segperverlist[i]) != SHARP) { - setshelltype(segperverlist[i], SHARP); - sharpsegcount++; - } - if (shelltype(segperverlist[j]) != SHARP) { - setshelltype(segperverlist[j], SHARP); - sharpsegcount++; - } - } - } // j - } // i - if (!acuteflag) { - if ((idx2seglist[idx + 1] - idx2seglist[idx]) > 4) { - // There are at least 5 segments shared at this vertices. - acuteflag = true; - } - } - if (acuteflag) { - if (b->verbose > 2) { - printf(" Mark %d as ACUTEVERTEX.\n", pointmark(pa)); - } - setpointtype(pa, ACUTEVERTEX); - acutecount++; + // 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++; } - pa = pointtraverse(); + segloop.sh = shellfacetraverse(subsegs); } if (b->verbose) { - if (acutecount > 0) { - printf(" Found %d acute vertices.\n", acutecount); - } - if (sharpsegcount > 0) { - printf(" Found %d sharp segments.\n", sharpsegcount); - } - printf(" Minimum seg-seg angle = %g.\n", minfaceang / PI * 180.0); + printf(" Found %ld segments.\n", segptlist->objects); } - delete [] idx2seglist; - delete [] segperverlist; -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// reportselfintersect() Report a self-intersection. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::reportselfintersect(face *checkseg, face *checksh) -{ - face parentsh; - point pa, pb, pc, pd, pe; - point fa, fb; - - pa = sorg(*checkseg); - pb = sdest(*checkseg); - fa = farsorg(*checkseg); - fb = farsdest(*checkseg); + segmentendpointslist = new point[segptlist->objects * 2]; - pc = sorg(*checksh); - pd = sdest(*checksh); - pe = sapex(*checksh); + totalworkmemory += (segptlist->objects * 2) * sizeof(point *); - printf(" !! Detected a self-intersection between:\n"); - printf(" A segment [%d,%d] < [%d,%d], \n", pointmark(pa), pointmark(pb), - pointmark(fa), pointmark(fb)); - printf(" a subface [%d,%d,%d] in facet #%d.\n", pointmark(pc), - pointmark(pd), pointmark(pe), shellmark(*checksh)); + 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. // @@ -15771,21 +14698,20 @@ void tetgenmesh::reportselfintersect(face *checkseg, face *checksh) /////////////////////////////////////////////////////////////////////////////// enum tetgenmesh::interresult - tetgenmesh::finddirection(triface* searchtet, point endpt, int randflag) + 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. - searchtet->ver = 11; - fsymself(*searchtet); + decode(searchtet->tet[3], *searchtet); // Reset the origin to be pa. if ((point) searchtet->tet[4] == pa) { searchtet->ver = 11; @@ -15794,7 +14720,7 @@ enum tetgenmesh::interresult } else if ((point) searchtet->tet[6] == pa) { searchtet->ver = 7; } else { - assert((point) searchtet->tet[7] == pa); // SELF_CHECK + assert((point) searchtet->tet[7] == pa); searchtet->ver = 0; } } @@ -15809,8 +14735,7 @@ enum tetgenmesh::interresult pc = apex(*searchtet); if (pc == endpt) { // pa->pc is the search edge. - eprevself(*searchtet); - esymself(*searchtet); + eprevesymself(*searchtet); return ACROSSVERT; } @@ -15818,12 +14743,6 @@ enum tetgenmesh::interresult while (1) { pd = oppo(*searchtet); - - if (b->verbose > 3) { - printf(" From tet (%d, %d, %d, %d) to %d.\n", pointmark(pa), - pointmark(pb), pointmark(pc), pointmark(pd), pointmark(endpt)); - } - // Check whether the opposite vertex is 'endpt'. if (pd == endpt) { // pa->pd is the search edge. @@ -15846,54 +14765,39 @@ enum tetgenmesh::interresult hori = orient3d(pa, pb, pc, endpt); rori = orient3d(pb, pa, pd, endpt); lori = orient3d(pa, pc, pd, endpt); - orient3dcount += 3; // Now decide the tet to move. It is possible there are more than one - // tet are viable moves. Use the opposite points of thier neighbors - // to discriminate, i.e., we choose the tet whose opposite point has - // the shortest distance to 'endpt'. + // 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. - if (0) { // if (!randflag) { + s = randomnation(3); + if (s == 0) { + nextmove = HMOVE; + } else if (s == 1) { + nextmove = RMOVE; } else { - // Randomly choose a direction. - s = randomnation(3); // 's' is in {0,1,2}. - if (s == 0) { - nextmove = HMOVE; - } else if (s == 1) { - nextmove = RMOVE; - } else { - nextmove = LMOVE; - } - } // if (randflag) + nextmove = LMOVE; + } } else { // Two tets, below horizon and below right, are viable. - if (0) { // if (!randflag) { + //s = randomnation(2); + if (randomnation(2)) { + nextmove = HMOVE; } else { - // Randomly choose a direction. - s = randomnation(2); // 's' is in {0,1}. - if (s == 0) { - nextmove = HMOVE; - } else { - nextmove = RMOVE; - } - } // if (randflag) + nextmove = RMOVE; + } } } else { if (lori > 0) { // Two tets, below horizon and below left, are viable. - if (0) { // if (!randflag) { + //s = randomnation(2); + if (randomnation(2)) { + nextmove = HMOVE; } else { - // Randomly choose a direction. - s = randomnation(2); // 's' is in {0,1}. - if (s == 0) { - nextmove = HMOVE; - } else { - nextmove = LMOVE; - } - } // if (randflag) + nextmove = LMOVE; + } } else { // The tet below horizon is chosen. nextmove = HMOVE; @@ -15903,16 +14807,12 @@ enum tetgenmesh::interresult if (rori > 0) { if (lori > 0) { // Two tets, below right and below left, are viable. - if (0) { // if (!randflag) { + //s = randomnation(2); + if (randomnation(2)) { + nextmove = RMOVE; } else { - // Randomly choose a direction. - s = randomnation(2); // 's' is in {0,1}. - if (s == 0) { - nextmove = RMOVE; - } else { - nextmove = LMOVE; - } - } // if (randflag) + nextmove = LMOVE; + } } else { // The tet below right is chosen. nextmove = RMOVE; @@ -15930,8 +14830,7 @@ enum tetgenmesh::interresult } if (lori == 0) { // pa->'endpt' is COLLINEAR with pa->pc. - eprevself(*searchtet); - esymself(*searchtet); // [a,c,d] + eprevesymself(*searchtet); // // [a,c,d] return ACROSSVERT; } // pa->'endpt' crosses the edge pb->pc. @@ -15951,8 +14850,7 @@ enum tetgenmesh::interresult } if (lori == 0) { // pa->'endpt' crosses the edge pc->pd. - eprevself(*searchtet); - esymself(*searchtet); // face acd + eprevesymself(*searchtet); // [a,c,d] return ACROSSEDGE; } // pa->'endpt' crosses the face bcd. @@ -15972,7 +14870,7 @@ enum tetgenmesh::interresult fsymself(*searchtet); enextself(*searchtet); } - assert(org(*searchtet) == pa); // SELF_CHECK + assert(org(*searchtet) == pa); pb = dest(*searchtet); pc = apex(*searchtet); @@ -15982,25 +14880,22 @@ enum tetgenmesh::interresult /////////////////////////////////////////////////////////////////////////////// // // -// scoutsegment() Look for a given segment in the tetrahedralization T. // +// scoutsegment() Search an edge in the tetrahedralization. // // // -// Search an edge in the tetrahedralization that matches the given segmment. // -// If such an edge exists, the segment is 'locked' at the edge. 'searchtet' // -// returns this (constrained) edge. Otherwise, the segment is missing. // +// If the edge is found, it returns SHAREEDGE, and 'searchtet' returns the // +// edge from startpt to endpt. // // // -// The returned value indicates one of the following cases: // -// - SHAREEDGE, the segment exists and is inserted in T; // -// - ACROSSEDGE, the segment intersects an edge (in 'searchtet'). // -// - ACROSSFACE, the segment crosses a face (in 'searchtet'). // +// 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 segment intersects a vertex ('refpt'). // -// - ACROSSSEG, the segment intersects a segment(returned by 'searchtet'). // -// - ACROSSSUB, the segment intersects a subface(returned by 'searchtet'). // -// // -// If the returned value is ACROSSEDGE or ACROSSFACE, i.e., the segment is // -// missing, 'refpt' returns the reference point for splitting thus segment, // -// 'searchtet' returns a tet containing the 'refpt'. // +// - 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'. // // // /////////////////////////////////////////////////////////////////////////////// @@ -16008,22 +14903,16 @@ enum tetgenmesh::interresult tetgenmesh::scoutsegment(point startpt, point endpt, triface* searchtet, point* refpt, arraypool* intfacelist) { - triface neightet, reftet; - face checkseg, checksh; - point pa, pb, pc, pd; - badface *bface; + point pd; enum interresult dir; - REAL angmax, ang; - long facecount; - int types[2], poss[4]; - int pos, i, j; + int t1ver; if (b->verbose > 2) { - printf(" Scout seg (%d, %d).\n", pointmark(startpt), pointmark(endpt)); + printf(" Scout seg (%d, %d).\n",pointmark(startpt),pointmark(endpt)); } point2tetorg(startpt, *searchtet); - dir = finddirection(searchtet, endpt, 0); + dir = finddirection(searchtet, endpt); if (dir == ACROSSVERT) { pd = dest(*searchtet); @@ -16032,14 +14921,13 @@ enum tetgenmesh::interresult return SHAREEDGE; } else { // A point is on the path. - *refpt = pd; + // Let the origin of the searchtet be the vertex. + enextself(*searchtet); + if (refpt) *refpt = pd; return ACROSSVERT; } } // if (dir == ACROSSVERT) - if (b->verbose > 2) { - printf(" Seg is missing.\n"); - } // dir is either ACROSSEDGE or ACROSSFACE. enextesymself(*searchtet); // Go to the opposite face. @@ -16047,29 +14935,28 @@ enum tetgenmesh::interresult if (dir == ACROSSEDGE) { // Check whether two segments are intersecting. - tsspivot1(*searchtet, checkseg); - if (checkseg.sh != NULL) { + if (issubseg(*searchtet)) { return ACROSSSEG; } - across_edge_count++; } else if (dir == ACROSSFACE) { if (checksubfaceflag) { // Check whether a segment and a subface are intersecting. - tspivot(*searchtet, checksh); - if (checksh.sh != NULL) { + if (issubface(*searchtet)) { return ACROSSSUB; } } } if (refpt == NULL) { + // Do not need a reference point. Return. return dir; } - if (b->verbose > 2) { - printf(" Scout a ref-point for it.\n"); - } - facecount = across_face_count; + 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); @@ -16091,53 +14978,10 @@ enum tetgenmesh::interresult // Search intersecting faces along the segment. while (1) { - if (intfacelist != NULL) { - if (dir == ACROSSFACE) { - // Save the intersecting face. - intfacelist->newindex((void **) &bface); - bface->tt = *searchtet; - bface->forg = org(*searchtet); - bface->fdest = dest(*searchtet); - bface->fapex = apex(*searchtet); - // Save the intersection type (ACROSSFACE or ACROSSEDGE). - bface->key = (REAL) dir; - } else { // dir == ACROSSEDGE - i = 0; - if (intfacelist->objects > 0l) { - // Get the last saved one. - bface = (badface *) fastlookup(intfacelist, intfacelist->objects - 1); - if (((enum interresult) (int) bface->key) == ACROSSEDGE) { - // Skip this edge if it is the same as the last saved one. - if (((bface->forg == org(*searchtet)) && - (bface->fdest == dest(*searchtet))) || - ((bface->forg == dest(*searchtet)) && - (bface->fdest == org(*searchtet)))) { - i = 1; // Skip this edge. - } - } - } - if (i == 0) { - // Save this crossing edge. - intfacelist->newindex((void **) &bface); - bface->tt = *searchtet; - bface->forg = org(*searchtet); - bface->fdest = dest(*searchtet); - // bface->fapex = apex(*searchtet); - // Save the intersection type (ACROSSFACE or ACROSSEDGE). - bface->key = (REAL) dir; - } - } - } pd = oppo(*searchtet); assert(pd != dummypoint); // SELF_CHECK - if (b->verbose > 3) { - printf(" Passing face (%d, %d, %d, %d), dir(%d).\n", - pointmark(pa), pointmark(pb), pointmark(pc), pointmark(pd), - (int) dir); - } - across_face_count++; // Stop if we meet 'endpt'. if (pd == endpt) break; @@ -16205,9 +15049,6 @@ enum tetgenmesh::interresult enextself(neightet); } pd = org(neightet); - if (b->verbose > 2) { - angmax = interiorangle(pd, startpt, endpt, NULL); - } *refpt = pd; // break; return ACROSSVERT; @@ -16222,16 +15063,13 @@ enum tetgenmesh::interresult if (dir == ACROSSEDGE) { // Check whether two segments are intersecting. - tsspivot1(*searchtet, checkseg); - if (checkseg.sh != NULL) { + if (issubseg(*searchtet)) { return ACROSSSEG; } - across_edge_count++; } else if (dir == ACROSSFACE) { if (checksubfaceflag) { // Check whether a segment and a subface are intersecting. - tspivot(*searchtet, checksh); - if (checksh.sh != NULL) { + if (issubface(*searchtet)) { return ACROSSSUB; } } @@ -16245,202 +15083,89 @@ enum tetgenmesh::interresult *refpt = NULL; } - // dir is either ACROSSVERT, or ACROSSEDGE, or ACROSSFACE. - if (b->verbose > 2) { - if (*refpt != NULL) { - printf(" Refpt %d (%g), visited %ld faces.\n", pointmark(*refpt), - angmax / PI * 180.0, across_face_count - facecount); - } else { - printf(" No refpt (%g) is found, visited %ld faces.\n", - angmax / PI * 180.0, across_face_count - facecount); - } - } - if (across_face_count - facecount > across_max_count) { - across_max_count = across_face_count - facecount; - } *searchtet = reftet; return dir; } /////////////////////////////////////////////////////////////////////////////// -// // -// getsteinerpointonsegment() Get a Steiner point on a segment. // -// // -/////////////////////////////////////////////////////////////////////////////// - -void tetgenmesh::getsteinerptonsegment(face* seg, point refpt, point steinpt) -{ - point ei, ej; - REAL Li, Lj, L, dj, dr; - REAL ti = 0.0, tj = 0.0, t; - int type, eid = 0, i; - - REAL diff, stept = 0.0, L1; - int iter; - - ei = sorg(*seg); - ej = sdest(*seg); - - - if (b->verbose > 2) { - printf(" Get Steiner point on seg [%d (%c), %d (%c)].\n", - pointmark(ei), pointtype(ei) == ACUTEVERTEX ? 'A' : 'N', - pointmark(ej), pointtype(ej) == ACUTEVERTEX ? 'A' : 'N'); - } +// // +// getsteinerpointonsegment() Get a Steiner point on a segment. // +// // +// Return '1' if 'refpt' lies on an adjacent segment of this segment. Other- // +// wise, return '0'. // +// // +/////////////////////////////////////////////////////////////////////////////// - if (b->psc) { - eid = shellmark(*seg); - if (pointtype(ei) != FREESEGVERTEX) { - ti = in->getvertexparamonedge(in->geomhandle, pointmark(ei), eid); - } else { - ti = pointgeomuv(ei, 0); - } - if (pointtype(ej) != FREESEGVERTEX) { - tj = in->getvertexparamonedge(in->geomhandle, pointmark(ej), eid); - } else { - tj = pointgeomuv(ej, 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) { - if (pointtype(ei) == ACUTEVERTEX) { - if (pointtype(ej) == ACUTEVERTEX) { - // Choose the vertex which is closer to refpt. - Li = distance(ei, refpt); - Lj = distance(ej, refpt); - if (Li > Lj) { - // Swap ei and ej; - sesymself(*seg); - ei = sorg(*seg); - ej = sdest(*seg); - t = ti; - ti = tj; - tj = t; - } - type = 1; + 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 { - type = 1; + // Cut the segment by the projection point of refpt. + projpt2edge(refpt, ei, ej, steinpt); } } else { - if (pointtype(ej) == ACUTEVERTEX) { - type = 1; - // Swap ei and ej; - sesymself(*seg); - ei = sorg(*seg); - ej = sdest(*seg); - t = ti; - ti = tj; - tj = t; - } else { - type = 0; - } + // Cut the segment by the projection point of refpt. + projpt2edge(refpt, ei, ej, steinpt); } - } else { - type = 0; - } - if (type == 1) { + // Make sure that steinpt is not too close to ei and ej. L = distance(ei, ej); - Li = distance(ei, refpt); - // Cut the segment by a sphere centered at ei with radius Li. - if (b->psc) { - stept = (tj - ti) / 100.0; - iter = 0; - t = ti + (Li / L) * (tj - ti); - while (1) { - in->getsteineronedge(in->geomhandle, eid, t, steinpt); - L1 = distance(steinpt, ei); - diff = L1 - Li; - if ((fabs(diff) / L) < 1e-3) { - break; - } - if (diff > 0) { - t -= stept; // Move it towards ei. - } else { - t += stept; // Move it towards ej. - } - iter++; - if (iter > 10) { - printf("Warning: Get the right Steiner point failed.\n"); - break; - } - } // while (1) - } else { - t = Li / L; + 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] + t * (ej[i] - ei[i]); - } - } - // Avoid creating a too short edge. - dj = distance(steinpt, ej); - dr = distance(steinpt, refpt); - if (dj < dr) { - // Cut the segment by the radius equal to Li / 2. - if (b->psc) { - iter = 0; - t = ti + ((Li / 2.0) / L) * (tj - ti); - while (1) { - in->getsteineronedge(in->geomhandle, eid, t, steinpt); - L1 = distance(steinpt, ei); - diff = L1 - (Li / 2.0); - if ((fabs(diff) / L) < 1e-3) { - break; - } - if (diff > 0) { - t -= stept; // Move it towards ei. - } else { - t += stept; // Move it towards ej. - } - iter++; - if (iter > 10) { - printf("Warning: Get the right Steiner point failed.\n"); - break; - } - } // while (1) - } else { - t = (Li / 2.0) / L; - for (i = 0; i < 3; i++) { - steinpt[i] = ei[i] + t * (ej[i] - ei[i]); - } + steinpt[i] = ei[i] + 0.5 * (ej[i] - ei[i]); } - r3count++; - } else { - r2count++; } } else { // Split the point at the middle. - if (b->psc) { - t = 0.5 * (ti + tj); - in->getsteineronedge(in->geomhandle, eid, t, steinpt); - } else { - t = 0.5; - for (i = 0; i < 3; i++) { - steinpt[i] = ei[i] + t * (ej[i] - ei[i]); - } + for (i = 0; i < 3; i++) { + steinpt[i] = ei[i] + 0.5 * (ej[i] - ei[i]); } - r1count++; } - if (b->psc) { - setpointgeomuv(steinpt, 0, t); - setpointgeomtag(steinpt, eid); - } - - if (pointtype(steinpt) == UNUSEDVERTEX) { - setpointtype(steinpt, FREESEGVERTEX); - } - if (b->verbose > 2) { - printf(" Split at t(%g)", t); - if (b->psc) { - printf(", ti(%g), tj(%g)", ti, tj); - } - printf(".\n"); - } + return adjflag; } + /////////////////////////////////////////////////////////////////////////////// // // // delaunizesegments() Recover segments in a DT. // @@ -16458,29 +15183,18 @@ void tetgenmesh::getsteinerptonsegment(face* seg, point refpt, point steinpt) void tetgenmesh::delaunizesegments() { triface searchtet, spintet; - face searchsh, checksh; - face sseg, checkseg, *psseg; + face searchsh; + face sseg, *psseg; point refpt, newpt; enum interresult dir; insertvertexflags ivf; - int loc; - - // For reporting PLC problems. - point forg1, fdest1; // The 1st segment. - point forg2, fdest2, fapex2; // The 2nd segment. + int t1ver; - // Does this mesh containing subfaces? - if (checksubfaceflag) { - ivf.bowywat = 2; // The mesh is a CDT. - ivf.lawson = 2; // Do flip to recover Delaunayness. - ivf.validflag = 1; // Validation is needed. - } else { - ivf.bowywat = 1; // The mesh is a DT. - ivf.lawson = 0; // No need to do flip. - ivf.validflag = 0; // No need to valid the B-W cavity. - } - searchsh.sh = NULL; + 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) { @@ -16489,14 +15203,9 @@ void tetgenmesh::delaunizesegments() psseg = (face *) fastlookup(subsegstack, subsegstack->objects); sseg = *psseg; - assert(!sinfected(sseg)); // FOR DEBUG // Check if this segment has been recovered. sstpivot1(sseg, searchtet); if (searchtet.tet != NULL) { - // FOR DEBUG - // Check if the tet contains the same segment. - tsspivot1(searchtet, checkseg); - assert(checkseg.sh == sseg.sh); continue; // Not a missing segment. } @@ -16505,8 +15214,7 @@ void tetgenmesh::delaunizesegments() if (dir == SHAREEDGE) { // Found this segment, insert it. - tsspivot1(searchtet, checkseg); // SELF_CHECK - if (checkseg.sh == NULL) { + if (!issubseg(searchtet)) { // Let the segment remember an adjacent tet. sstbond1(sseg, searchtet); // Bond the segment to all tets containing it. @@ -16516,7 +15224,7 @@ void tetgenmesh::delaunizesegments() fnextself(spintet); } while (spintet.tet != searchtet.tet); } else { - // Collision! Should not happen. + // Collision! Maybe a bug. assert(0); } } else { @@ -16527,145 +15235,30 @@ void tetgenmesh::delaunizesegments() //setpointtype(newpt, FREESEGVERTEX); getsteinerptonsegment(&sseg, refpt, newpt); - // Start searching from the 'searchtet'. + // Start searching from 'searchtet'. ivf.iloc = (int) OUTSIDE; - //ivf.bowywat; - //ivf.lawson; - ivf.rejflag = 0; - ivf.chkencflag = 0; - ivf.sloc = ivf.iloc; - ivf.sbowywat = ivf.bowywat; - ivf.splitbdflag = 0; - // ivf.validflag - ivf.respectbdflag = 0; - ivf.assignmeshsize = 0; // Insert the new point into the tetrahedralization T. // Missing segments and subfaces are queued for recovery. // Note that T is convex (nonconvex = 0). - loc = insertvertex(newpt, &searchtet, &searchsh, &sseg, &ivf); - - assert(loc != (int) ONVERTEX); - if (loc != (int) NEARVERTEX) { + if (insertpoint(newpt, &searchtet, &searchsh, &sseg, &ivf)) { // The new point has been inserted. - if (ivf.lawson > 0) { - // For CDT, use flips to reocver Delaunayness. - lawsonflip3d(newpt, ivf.lawson, 0, 0, 0); - } - st_segref_count++; //st_segpro_count++; + st_segref_count++; if (steinerleft > 0) steinerleft--; } else { - // The new point is either ON or VERY CLOSE to an existing point. - refpt = point2ppt(newpt); - printf(" !! Avoid to create a short edge (length = %g)\n", - distance(newpt, refpt)); - - // It is probably an input problem. Two possible cases are: - // (1) An input vertex is very close an input segment; or - // (2) Two input segments are nearly intersect each other. - forg1 = farsorg(sseg); - fdest1 = farsdest(sseg); - - if ((pointtype(refpt) == RIDGEVERTEX) || - (pointtype(refpt) == ACUTEVERTEX) || - (pointtype(refpt) == VOLVERTEX)) { - // Case (1) - printf(" !! Point %d is very close to segment (%d, %d).\n", - pointmark(refpt), pointmark(forg1), pointmark(fdest1)); - } else if (pointtype(refpt) == FREESEGVERTEX) { - // Case (2). Find a subsegment contain 'refpt'. - subsegs->traversalinit(); - checkseg.sh = shellfacetraverse(subsegs); - while (checkseg.sh != NULL) { - if (((point) checkseg.sh[3] == refpt) || - ((point) checkseg.sh[4] == refpt)) break; - checkseg.sh = shellfacetraverse(subsegs); - } - assert(checkseg.sh != NULL); - checkseg.shver = 0; - forg2 = farsorg(checkseg); - fdest2 = farsdest(checkseg); - printf(" !! Two segments are very close to each other.\n"); - printf(" 1st: (%d, %d), 2nd: (%d, %d)\n", pointmark(forg1), - pointmark(fdest1), pointmark(forg2), pointmark(fdest2)); - } else { - // Unknown case - assert(0); - } - // Indicate it may be an input problem. - printf(" Short edge length bound is: %g. Tolerance is %g.\n", - b->minedgelength, b->epsilon); - terminatetetgen(4); + assert (ivf.iloc == (enum locateresult) NEARVERTEX); + terminatetetgen(this, 4); } } else { - // The input PLC contains self-intersections. - if (dir == ACROSSVERT) { - // refpt is the vertex intersecting the segment. - forg1 = farsorg(sseg); - fdest1 = farsdest(sseg); - if ((pointtype(refpt) == RIDGEVERTEX) || - (pointtype(refpt) == ACUTEVERTEX) || - (pointtype(refpt) == FACETVERTEX) || - (pointtype(refpt) == VOLVERTEX)) { - printf("Point %d is on segment (%d, %d).\n", - pointmark(refpt), pointmark(forg1), pointmark(fdest1)); - } else if (pointtype(refpt) == FREESEGVERTEX) { - // Case (2). Find a subsegment contain 'refpt'. - subsegs->traversalinit(); - checkseg.sh = shellfacetraverse(subsegs); - while (checkseg.sh != NULL) { - if (((point) checkseg.sh[3] == refpt) || - ((point) checkseg.sh[4] == refpt)) break; - checkseg.sh = shellfacetraverse(subsegs); - } - assert(checkseg.sh != NULL); - checkseg.shver = 0; - forg2 = farsorg(checkseg); - fdest2 = farsdest(checkseg); - printf("Two segments intersect.\n"); - printf(" 1st: (%d, %d), 2nd: (%d, %d)", pointmark(forg1), - pointmark(fdest1), pointmark(forg2), pointmark(fdest2)); - } else if (pointtype(refpt) == FREEFACETVERTEX) { - assert(0); // Report this case. - } - } else if (dir == ACROSSSEG) { - tsspivot1(searchtet, checkseg); - if (!b->quiet) { - printf("Two segments intersect.\n"); - forg1 = farsorg(sseg); - fdest1 = farsdest(sseg); - forg2 = farsorg(checkseg); - fdest2 = farsdest(checkseg); - printf(" 1st: (%d, %d), 2nd: (%d, %d).\n", pointmark(forg1), - pointmark(fdest1), pointmark(forg2), pointmark(fdest2)); - } - } else if (dir == ACROSSSUB) { - tspivot(searchtet, checksh); - if (!b->quiet) { - printf("A segment and a subface intersect.\n"); - forg1 = farsorg(sseg); - fdest1 = farsdest(sseg); - forg2 = sorg(checksh); - fdest2 = sdest(checksh); - fapex2 = sapex(checksh); - printf(" Seg: (%d, %d), Sub: (%d, %d, %d).\n", - pointmark(forg1), pointmark(fdest1), - pointmark(forg2), pointmark(fdest2), pointmark(fapex2)); - } - } else { - // Unknown cases. - assert(0); - } // Indicate it is an input problem. - terminatetetgen(3); + terminatetetgen(this, 3); } } } // while - } /////////////////////////////////////////////////////////////////////////////// // // -// scoutsubface() Look for a given subface in the tetrahedralization T. // +// 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. // @@ -16681,27 +15274,25 @@ enum tetgenmesh::interresult tetgenmesh::scoutsubface(face* searchsh, triface* searchtet) { triface spintet; - face checksh; point pa, pb, pc; enum interresult dir; + int t1ver; pa = sorg(*searchsh); pb = sdest(*searchsh); - if (b->verbose > 2) { - printf(" Scout subface (%d, %d, %d).\n", pointmark(pa), pointmark(pb), - pointmark(sapex(*searchsh))); - } // Get a tet whose origin is a. point2tetorg(pa, *searchtet); // Search the edge [a,b]. - dir = finddirection(searchtet, pb, 0); + dir = finddirection(searchtet, pb); if (dir == ACROSSVERT) { // Check validity of a PLC. if (dest(*searchtet) != pb) { - // A vertex lies on the search edge. Return it. + // 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. @@ -16711,8 +15302,7 @@ enum tetgenmesh::interresult while (1) { if (apex(spintet) == pc) { // Found a face matching to 'searchsh'! - tspivot(spintet, checksh); - if (checksh.sh == NULL) { + if (!issubface(spintet)) { // Insert 'searchsh'. tsbond(spintet, *searchsh); fsymself(spintet); @@ -16722,11 +15312,13 @@ enum tetgenmesh::interresult 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(5); + terminatetetgen(this, 5); // unifysubfaces(&checksh, searchsh); *searchtet = spintet; return COLLISIONFACE; @@ -16738,47 +15330,39 @@ enum tetgenmesh::interresult } // dir is either ACROSSEDGE or ACROSSFACE. - return dir; //ACROSSTET; + return dir; } /////////////////////////////////////////////////////////////////////////////// // // -// formmissingregion() Form the missing region of a missing subface. // +// 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 collection of missing subfaces connected through adjacent edges. // -// // -// The missing region R is returned in the array 'missingshs'. All subfaces // -// in R are oriented as 'missh'. The array 'missingshverts' returns all ver- // -// tices of R. All subfaces and vertices of R are marktested. // +// a connected region formed by a set of missing subfaces of a facet. // +// Comment: There should be no segment inside R. // // // -// 'adjtets' returns a list of tetrahedra adjacent to R. They are used to // -// search a crossing tetrahedron of R. // -// // -// Many ways are possible to form the missing region. The method used here // -// is to search missing edges in R. Starting from 'missh', its three edges // -// are checked. If one of the edges is missing, then the adjacent subface at // -// this edge is also missing. It is added to the array. By an incrementally // -// broad-first searching, we can find all subfaces of 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::formmissingregion(face* missh, arraypool* missingshs, - arraypool* missingshbds, - arraypool* missingshverts, - arraypool* adjtets) +void tetgenmesh::formregion(face* missh, arraypool* missingshs, + arraypool* missingshbds, arraypool* missingshverts) { - triface searchtet, *parytet; + triface searchtet, spintet; face neighsh, *parysh; + face neighseg, fakeseg; point pa, pb, *parypt; enum interresult dir; + int t1ver; int i, j; - if (b->verbose > 2) { - printf(" Form missing region from subface (%d, %d, %d)\n", - pointmark(sorg(*missh)), pointmark(sdest(*missh)), - pointmark(sapex(*missh))); - } smarktest(*missh); missingshs->newindex((void **) &parysh); *parysh = *missh; @@ -16789,43 +15373,22 @@ void tetgenmesh::formmissingregion(face* missh, arraypool* missingshs, for (j = 0; j < 3; j++) { pa = sorg(*missh); pb = sdest(*missh); - // Get a tet whose origin is a. point2tetorg(pa, searchtet); - // Search the edge [a,b]. - dir = finddirection(&searchtet, pb, 0); + dir = finddirection(&searchtet, pb); if (dir != ACROSSVERT) { // This edge is missing. Its neighbor is a missing subface. spivot(*missh, neighsh); - assert(neighsh.sh != NULL); if (!smarktested(neighsh)) { // Adjust the face orientation. - if (sorg(neighsh) != pb) { - sesymself(neighsh); - } - if (b->verbose > 3) { - printf(" Add a missing subface (%d, %d, %d)\n", - pointmark(pb), pointmark(pa), pointmark(sapex(neighsh))); - } + if (sorg(neighsh) != pb) sesymself(neighsh); smarktest(neighsh); missingshs->newindex((void **) &parysh); *parysh = neighsh; } } else { - if (dest(searchtet) == pb) { - // Remember an existing edge for searching the first crossing tet. - adjtets->newindex((void **) &parytet); - *parytet = searchtet; - // Found an existing edge, it must be a boundary edge of R. - if (b->verbose > 3) { - printf(" -- A boundary edge (%d, %d)\n", pointmark(pa), - pointmark(pb)); - } - missingshbds->newindex((void **) &parysh); - *parysh = *missh; // It is only queued once. - } else { - // The input PLC has problem. - //assert(0); - terminatetetgen(3); + if (dest(searchtet) != pb) { + // This might be a self-intersection problem. + terminatetetgen(this, 3); } } // Collect the vertices of R. @@ -16838,57 +15401,86 @@ void tetgenmesh::formmissingregion(face* missh, arraypool* missingshs, } // j } // i - if (b->verbose > 2) { - printf(" Region has: %ld subfaces, %ld vertices\n", - missingshs->objects, missingshverts->objects); - } + // 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 - if (missingshs->objects > maxregionsize) { - maxregionsize = missingshs->objects; - } // Unmarktest collected missing subfaces. for (i = 0; i < missingshs->objects; i++) { - missh = (face *) fastlookup(missingshs, i); - sunmarktest(*missh); + parysh = (face *) fastlookup(missingshs, i); + sunmarktest(*parysh); } - - // Comment: All vertices in R are pmarktested. } - - /////////////////////////////////////////////////////////////////////////////// // // // 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* adjtets, +int tetgenmesh::scoutcrossedge(triface& crosstet, arraypool* missingshbds, arraypool* missingshs) { - triface *searchtet, spintet; + triface searchtet, spintet; face *parysh; - face checkseg; + 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; - if (b->verbose > 2) { - printf(" Search a crossing edge.\n"); - } searchflag = 0; - for (j = 0; j < adjtets->objects && !searchflag; j++) { - searchtet = (triface *) fastlookup(adjtets, j); + 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; + spintet = searchtet; while (1) { pd = apex(spintet); pe = oppo(spintet); @@ -16902,7 +15494,7 @@ int tetgenmesh::scoutcrossedge(triface& crosstet, arraypool* adjtets, pa = sorg(*parysh); pb = sdest(*parysh); pc = sapex(*parysh); - interflag = tri_edge_test(pa, pb, pc, pd, pe, NULL, 1, types, poss); + 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. @@ -16910,14 +15502,13 @@ int tetgenmesh::scoutcrossedge(triface& crosstet, arraypool* adjtets, if ((dir == ACROSSFACE) || (dir == ACROSSEDGE)) { //pos = poss[0]; // Go to the crossing edge [d,e,#,#]. - eprev(spintet, crosstet); - esymself(crosstet); - enextself(crosstet); // [d,e,#,#]. + edestoppo(spintet, crosstet); // // [d,e,#,#]. // Check if it is a segment. - tsspivot1(crosstet, checkseg); - if (checkseg.sh != NULL) { - reportselfintersect(&checkseg, parysh); - terminatetetgen(3); + 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); @@ -16925,21 +15516,7 @@ int tetgenmesh::scoutcrossedge(triface& crosstet, arraypool* adjtets, if (ori < 0) { esymself(crosstet); } - if (b->verbose > 2) { - printf(" Found edge (%d, %d) intersect", pointmark(pd), - pointmark(pe)); - printf(" face (%d, %d, %d)\n", pointmark(pa), pointmark(pb), - pointmark(pc)); - } - // Save the corners of this subface. - plane_pa = pa; - plane_pb = pb; - plane_pc = pc; searchflag = 1; - } else { - // An improper intersection type. - // Maybe it is a PLC problem. - // At the moment, just ignore it. } } break; @@ -16951,11 +15528,10 @@ int tetgenmesh::scoutcrossedge(triface& crosstet, arraypool* adjtets, if (interflag > 0) break; // Go to the next tetrahedron. fnextself(spintet); - if (spintet.tet == searchtet->tet) break; + if (spintet.tet == searchtet.tet) break; } // while (1) } // j - adjtets->restart(); return searchflag; } @@ -16969,23 +15545,6 @@ int tetgenmesh::scoutcrossedge(triface& crosstet, arraypool* adjtets, // #] which intersects R in its interior, where the edge [d,e] intersects R, // // and d lies below R. // // // -// By knowing a crossing edge [d,e], all tetrahedra sharing at [d,e] must // -// cross R. They are added into the list 'crosstets'. From this set of tets, // -// new crossing edges (if there exist) can be detected. The key is how to // -// detect them correctly. The approach used here is described as follows: // -// Suppose [d,e,a,b] is a crossing tet, where [d,e] intersects R and d lies // -// below R. We look at the face [d,e,a]. If the apex a is not pmarktested, // -// i.e., it is not a vertex of R, then either edge [e,a] or [a,d] intersects // -// R. A simple way is to perform above/below test between [a] and R. But it // -// is not clear which subface of R is the best for this test. Also, this is // -// not safe when R is not strictly planar. A second way is to test if [e,a] // -// intersects one of the subfaces of R. If an intersection is found, then [e,// -// a] is a crossing edge. Otherwise, the edge [a,d] must be a crossing edge // -// of R. NOTE: This statement is correct if the input PLC is correct. We can // -// also detect the incorrectness of the input, e.g., if [a] only touches a // -// subface of R. The second approach is implemented here. It is slow, but is // -// more robust. // -// // // '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 // @@ -16994,13 +15553,6 @@ int tetgenmesh::scoutcrossedge(triface& crosstet, arraypool* adjtets, // in the top part of C, and so does 'botpoints'. Both 'toppoints' and // // 'botpoints' contain vertices of R. // // // -// NOTE: 'toppoints' may contain points which are not vertices of any top // -// faces, and so may 'botpoints'. Such points may belong to other facets and // -// need to be present after the recovery of this cavity (P1029.poly). // -// // -// A pair of boundary faces: 'firsttopface' and 'firstbotface', are saved. // -// They share the same edge in the boundary of the missing region. // -// // // Important: This routine assumes all vertices of the facet containing this // // subface are marked, i.e., pmarktested(p) returns true. // // // @@ -17011,27 +15563,22 @@ bool tetgenmesh::formcavity(triface* searchtet, arraypool* missingshs, arraypool* botfaces, arraypool* toppoints, arraypool* botpoints) { - arraypool *crossedges, *testededges; + arraypool *crossedges; triface spintet, neightet, *parytet; - face checksh, *parysh = NULL; - face checkseg; // *paryseg; + face *parysh = NULL; point pa, pd, pe, *parypt; enum interresult dir; - //REAL ori; - //REAL elen[3]; bool testflag, invalidflag; int types[2], poss[4]; + int t1ver; int i, j, k; // Temporarily re-use 'topfaces' for all crossing edges. crossedges = topfaces; - // Temporarily re-use 'botfaces' for all tested edges. - testededges = botfaces; // Only used by 'b->psc'. if (b->verbose > 2) { - printf(" Form the cavity of missing region.\n"); + printf(" Form the cavity of a missing region.\n"); } - missingsubfacecount += missingshs->objects; // Mark this edge to avoid testing it later. markedge(*searchtet); crossedges->newindex((void **) &parytet); @@ -17040,24 +15587,20 @@ bool tetgenmesh::formcavity(triface* searchtet, arraypool* missingshs, invalidflag = 0; // Collect all crossing tets. Each cross tet is saved in the standard - // form [d,e,#,#], where [d,e] is a corossing edge, d lies below R. + // form [d,e,#,#], where [d,e] is a crossing edge, d lies below R. // NEITHER d NOR e is a vertex of R (!pmarktested). - // NOTE: hull tets may be collected. See fig/dump-cavity-case2a(b).lua. - // Make sure that neither d nor e is dummypoint. 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); - assert(!pmarktested(pd)); // pd is not on R. if (!pinfected(pd)) { pinfect(pd); botpoints->newindex((void **) &parypt); *parypt = pd; } pe = dest(*searchtet); - assert(!pmarktested(pe)); // pe is not on R. if (!pinfected(pe)) { pinfect(pe); toppoints->newindex((void **) &parypt); @@ -17068,11 +15611,6 @@ bool tetgenmesh::formcavity(triface* searchtet, arraypool* missingshs, spintet = *searchtet; while (1) { if (!infected(spintet)) { - if (b->verbose > 3) { - printf(" Add a crossing tet (%d, %d, %d, %d)\n", - pointmark(org(spintet)), pointmark(dest(spintet)), - pointmark(apex(spintet)), pointmark(oppo(spintet))); - } infect(spintet); crosstets->newindex((void **) &parytet); *parytet = spintet; @@ -17088,10 +15626,10 @@ bool tetgenmesh::formcavity(triface* searchtet, arraypool* missingshs, // spintet is [d,e,a,#], where d lies below R, and e lies above R. pa = apex(spintet); if (pa != dummypoint) { - if (!pmarktested(pa) || b->psc) { - // There exists a crossing edge, either [e,a] or [a,d]. First check - // if the crossing edge has already be added. This is to check if - // a tetrahedron at this edge is marked. + 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) { @@ -17117,12 +15655,8 @@ bool tetgenmesh::formcavity(triface* searchtet, arraypool* missingshs, pe = dest(spintet); for (k = 0; k < missingshs->objects; k++) { parysh = (face *) fastlookup(missingshs, k); - plane_pa = sorg(*parysh); - plane_pb = sdest(*parysh); - plane_pc = sapex(*parysh); - // Test if this face intersects [e,a]. - if (tri_edge_test(plane_pa, plane_pb, plane_pc, pe, pa, - NULL, 1, types, poss)) { + 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]; @@ -17134,9 +15668,8 @@ bool tetgenmesh::formcavity(triface* searchtet, arraypool* missingshs, } break; } - // Test if this face intersects [a,d]. - if (tri_edge_test(plane_pa, plane_pb, plane_pc, pa, pd, - NULL, 1, types, poss)) { + 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]; @@ -17150,47 +15683,28 @@ bool tetgenmesh::formcavity(triface* searchtet, arraypool* missingshs, } } // k if (k < missingshs->objects) { - // Found a pair of triangle - edge interseciton. + // Found a pair of triangle - edge intersection. if (invalidflag) { - if (b->verbose > 2) { - printf(" A non-valid subface - edge intersection\n"); + if (!b->quiet) { + printf("Warning: A non-valid facet - edge intersection\n"); printf(" subface: (%d, %d, %d) edge: (%d, %d)\n", - pointmark(plane_pa), pointmark(plane_pb), - pointmark(plane_pc), pointmark(org(neightet)), + pointmark(sorg(*parysh)), pointmark(sdest(*parysh)), + pointmark(sapex(*parysh)), pointmark(org(neightet)), pointmark(dest(neightet))); } // It may be a PLC problem. - terminatetetgen(3); - } else if (b->psc) { - if (pmarktested(pa)) { - // The intersection is invalid. - if (b->verbose > 2) { - printf(" A non-valid subface - edge intersection\n"); - printf(" subface: (%d, %d, %d) edge: (%d, %d)\n", - pointmark(plane_pa), pointmark(plane_pb), - pointmark(plane_pc), pointmark(org(neightet)), - pointmark(dest(neightet))); - } - // Split the subface intersecting this edge. - recentsh = *parysh; - recenttet = neightet; // For point location. - invalidflag = 1; - break; - } // if (pmarktested(pa)) - } // if (b->psc) + 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. - tsspivot1(neightet, checkseg); - if (checkseg.sh != NULL) { + if (issubseg(neightet)) { // Invalid PLC! - reportselfintersect(&checkseg, parysh); - terminatetetgen(3); - } - if (b->verbose > 3) { - printf(" Add a crossing edge (%d, %d)\n", - pointmark(org(neightet)), pointmark(dest(neightet))); + //face checkseg; + //tsspivot1(neightet, checkseg); + //reportselfintersect(&checkseg, parysh); + terminatetetgen(this, 3); } // Mark this edge to avoid testing it again. markedge(neightet); @@ -17198,32 +15712,29 @@ bool tetgenmesh::formcavity(triface* searchtet, arraypool* missingshs, *parytet = neightet; } else { // No intersection is found. It may be a PLC problem. - //assert(b->psc); - // Mark this edge to avoid testing it again. - //markedge(neightet); - //testededges->newindex((void **) &parytet); - //*parytet = neightet; invalidflag = 1; // Split the subface intersecting [d,e]. for (k = 0; k < missingshs->objects; k++) { parysh = (face *) fastlookup(missingshs, k); - plane_pa = sorg(*parysh); - plane_pb = sdest(*parysh); - plane_pc = sapex(*parysh); // Test if this face intersects [e,a]. - if (tri_edge_test(plane_pa, plane_pb, plane_pc, pd, pe, - NULL, 1, types, poss)) { + if (tri_edge_test(sorg(*parysh),sdest(*parysh),sapex(*parysh), + pd, pe, NULL, 1, types, poss)) { break; } } // k - assert(k < missingshs->objects); + 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 (!pmarktested(pa) || b->psc) + } // if (pa != dummypoint) // Go to the next crossing tet. fnextself(spintet); if (spintet.tet == searchtet->tet) break; @@ -17238,7 +15749,6 @@ bool tetgenmesh::formcavity(triface* searchtet, arraypool* missingshs, printf(" Formed cavity: %ld (%ld) cross tets (edges).\n", crosstets->objects, crossedges->objects); } - crossingtetcount += crosstets->objects; // Unmark all marked edges. for (i = 0; i < crossedges->objects; i++) { @@ -17248,17 +15758,6 @@ bool tetgenmesh::formcavity(triface* searchtet, arraypool* missingshs, } crossedges->restart(); - if (b->psc) { - // Unmark all marked edges. - for (i = 0; i < testededges->objects; i++) { - searchtet = (triface *) fastlookup(testededges, i); - assert(edgemarked(*searchtet)); // SELF_CHECK - unmarkedge(*searchtet); - } - testededges->restart(); - } else { // only p->plc - assert(testededges->objects == 0l); - } if (invalidflag) { // Unmark all collected tets. @@ -17278,44 +15777,21 @@ bool tetgenmesh::formcavity(triface* searchtet, arraypool* missingshs, 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; } - // Find a pair of cavity boundary faces from the top and bottom sides of - // the facet each, and they share the same edge. Save them in the - // global variables: firsttopface, firstbotface. They will be used in - // fillcavity() for gluing top and bottom new tets. - for (i = 0; i < crosstets->objects; i++) { - searchtet = (triface *) fastlookup(crosstets, i); - // Crosstet is [d,e,a,b]. - enextesym(*searchtet, spintet); - eprevself(spintet); // spintet is [b,a,e,d] - fsym(spintet, neightet); // neightet is [a,b,e,#] - if (!infected(neightet)) { - // A top face. - firsttopface = neightet; - } else { - continue; // Go to the next cross tet. - } - eprevesym(*searchtet, spintet); - enextself(spintet); // spintet is [a,b,d,e] - fsym(spintet, neightet); // neightet is [b,a,d,#] - if (!infected(neightet)) { - // A bottom face. - firstbotface = neightet; - } else { - continue; - } - break; - } // i - assert(i < crosstets->objects); // SELF_CHECK // 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. + // 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. @@ -17325,16 +15801,14 @@ bool tetgenmesh::formcavity(triface* searchtet, arraypool* missingshs, for (i = 0; i < crosstets->objects; i++) { searchtet = (triface *) fastlookup(crosstets, i); // searchtet is [d,e,a,b]. - enextesym(*searchtet, spintet); - eprevself(spintet); // spintet is [b,a,e,d] + eorgoppo(*searchtet, spintet); fsym(spintet, neightet); // neightet is [a,b,e,#] if (!infected(neightet)) { // A top face. topfaces->newindex((void **) &parytet); *parytet = neightet; - } - eprevesym(*searchtet, spintet); - enextself(spintet); // spintet is [a,b,d,e] + } + edestoppo(*searchtet, spintet); fsym(spintet, neightet); // neightet is [b,a,d,#] if (!infected(neightet)) { // A bottom face. @@ -17399,19 +15873,17 @@ void tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, arraypool *cavshells, arraypool *newtets, arraypool *crosstets, arraypool *misfaces) { - triface searchtet, neightet, spintet, *parytet, *parytet1; - face checksh, tmpsh, *parysh; - face checkseg; + triface searchtet, neightet, *parytet, *parytet1; + face tmpsh, *parysh; point pa, pb, pc, pd, pt[3], *parypt; enum interresult dir; insertvertexflags ivf; - REAL ori; //, ang, len; + REAL ori; long baknum, bakhullsize; int bakchecksubsegflag, bakchecksubfaceflag; - //int iloc; + int t1ver; int i, j; - if (b->verbose > 2) { printf(" Delaunizing cavity: %ld points, %ld faces.\n", cavpoints->objects, cavfaces->objects); @@ -17425,31 +15897,38 @@ void tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, checksubsegflag = 0; checksubfaceflag = 0; b->verbose--; // Suppress informations for creating Delaunay tetra. - b->plc = 0; // Do not do unifypoint(); + b->plc = 0; // Do not check near vertices. + + ivf.bowywat = 1; // Use Bowyer-Watson algorithm. // Get four non-coplanar points (no dummypoint). - parytet = (triface *) fastlookup(cavfaces, 0); - pa = org(*parytet); - pb = dest(*parytet); - pc = apex(*parytet); + 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 = 1; i < cavfaces->objects; i++) { + 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. - // if (!pinfected(pt[j])) { - 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; + 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; @@ -17462,15 +15941,13 @@ void tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, // Incrementally insert the vertices (duplicated vertices are ignored). for (i = 0; i < cavpoints->objects; i++) { pt[0] = * (point *) fastlookup(cavpoints, i); - assert(pt[0] != dummypoint); // SELF_CHECK searchtet = recenttet; ivf.iloc = (int) OUTSIDE; - ivf.bowywat = 1; - insertvertex(pt[0], &searchtet, NULL, NULL, &ivf); + insertpoint(pt[0], &searchtet, NULL, NULL, &ivf); } if (b->verbose > 2) { - printf(" Identfying %ld boundary faces of the cavity.\n", + printf(" Identifying %ld boundary faces of the cavity.\n", cavfaces->objects); } @@ -17481,10 +15958,7 @@ void tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, parytet = (triface *) fastlookup(cavfaces, i); // Skip an interior face (due to the enlargement of the cavity). if (infected(*parytet)) continue; - // This face may contain dummypoint (See fig/dum-cavity-case2). - // If so, dummypoint must be its apex. - j = (parytet->ver & 3); // j is the face number. - parytet->ver = epivot[j]; // [4,5,2,11] + parytet->ver = epivot[parytet->ver]; pt[0] = org(*parytet); pt[1] = dest(*parytet); pt[2] = apex(*parytet); @@ -17495,38 +15969,21 @@ void tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, searchtet.tet = NULL; dir = scoutsubface(&tmpsh, &searchtet); if (dir == SHAREFACE) { - // Inserted. Make sure that tmpsh connects an interior tet of C. - stpivot(tmpsh, neightet); - // neightet and tmpsh refer to the same edge [pt[0], pt[1]]. - // If the origin of neightet is pt[1], it is inside. - if (org(neightet) != pt[1]) { - fsymself(neightet); - assert(org(neightet) == pt[1]); // SELF_CHECK - // Make sure that tmpsh is connected with an interior tet. - sesymself(tmpsh); - tsbond(neightet, tmpsh); - } - assert(dest(neightet) == pt[0]); // SELF_CHECK - } else if (dir == COLLISIONFACE) { - // This case is not possible anymore. 2010-02-01 - assert(0); - } else { - if (b->verbose > 2) { - printf(" bdry face (%d, %d, %d) -- %d is missing\n", - pointmark(pt[0]), pointmark(pt[1]), pointmark(pt[2]), i); - } + // 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; - continue; } - // 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; } // i if (misfaces->objects > 0) { @@ -17535,7 +15992,7 @@ void tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, misfaces->objects); } - // Removing all tempoaray subfaces. + // Removing all temporary subfaces. for (i = 0; i < cavshells->objects; i++) { parysh = (face *) fastlookup(cavshells, i); stpivot(*parysh, neightet); @@ -17566,11 +16023,7 @@ void tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, if (!pinfected(pd)) { searchtet = recenttet; ivf.iloc = (int) OUTSIDE; - ivf.bowywat = 1; - insertvertex(pd, &searchtet, NULL, NULL, &ivf); - if (b->verbose > 2) { - printf(" Add point %d into list.\n", pointmark(pd)); - } + insertpoint(pd, &searchtet, NULL, NULL, &ivf); pinfect(pd); cavpoints->newindex((void **) &parypt); *parypt = pd; @@ -17580,15 +16033,9 @@ void tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, esym(*parytet, neightet); fsymself(neightet); if (!infected(neightet)) { - if (b->verbose > 2) { - printf(" Add a cavface (%d, %d, %d).\n", - pointmark(org(neightet)), pointmark(dest(neightet)), - pointmark(apex(neightet))); - } cavfaces->newindex((void **) &parytet1); *parytet1 = neightet; - } else { - } + } enextself(*parytet); } // j } // if (!infected(parytet)) @@ -17614,8 +16061,8 @@ void tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, *parytet = recenttet; for (i = 0; i < newtets->objects; i++) { searchtet = * (triface *) fastlookup(newtets, i); - for (searchtet.ver = 0; searchtet.ver < 4; searchtet.ver++) { - fsym(searchtet, neightet); + for (j = 0; j < 4; j++) { + decode(searchtet.tet[j], neightet); if (!marktested(neightet)) { marktest(neightet); newtets->newindex((void **) &parytet); @@ -17627,9 +16074,6 @@ void tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, cavpoints->restart(); cavfaces->restart(); - if (cavshells->objects > maxcavsize) { - maxcavsize = cavshells->objects; - } if (crosstets->objects > baknum) { // The cavity has been enlarged. cavityexpcount++; @@ -17657,112 +16101,131 @@ void tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, /////////////////////////////////////////////////////////////////////////////// bool tetgenmesh::fillcavity(arraypool* topshells, arraypool* botshells, - arraypool* midfaces, arraypool* missingshs) + arraypool* midfaces, arraypool* missingshs, + arraypool* topnewtets, arraypool* botnewtets, + triface* crossedge) { arraypool *cavshells; - triface *parytet, bdrytet, toptet, bottet, midface; - triface neightet, spintet; - face checksh, *parysh; + triface bdrytet, neightet, *parytet; + triface searchtet, spintet; + face *parysh; face checkseg; - point pa, pb, pc, pf, pg; //, *pts; - int types[2], poss[4]; - //REAL elen[3]; //ori, len, n[3]; - bool mflag, bflag; - int i, j, k; + 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 (k = 0; k < 2; k++) { - cavshells = (k == 0 ? topshells : botshells); + 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. + // 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. + // Get the adjacent new tet inside the cavity. stpivot(*parysh, neightet); - assert(org(neightet) == pb); // SELF_CHECK - assert(dest(neightet) == pa); // SELF_CHECK - // Mark neightet as an interior tet of this cavity, 2009-04-24. - // Comment: We know neightet is an interior tet. - if (!infected(neightet)) { - infect(neightet); - } - assert(oppo(bdrytet) != NULL); // No faked tet. - // if (oppo(bdrytet) != NULL) { - // Bond the two tets. - bond(bdrytet, neightet); // Also cleared the pointer to tmpsh. - // } + // 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, encode(neightet)); - setpoint2tet(pb, encode(neightet)); - setpoint2tet(pc, encode(neightet)); - // Delete the temp subface. - // shellfacedealloc(subfacepool, parysh->sh); + setpoint2tet(pa, (tetrahedron) neightet.tet); + setpoint2tet(pb, (tetrahedron) neightet.tet); + setpoint2tet(pc, (tetrahedron) neightet.tet); } // i } // if (cavshells != NULL) - } // k - - mflag = true; // Initialize it. + } // j - if (midfaces != NULL) { + 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 - // The first pair of top and bottom tets share the same edge [a, b]. - // toptet = * (triface *) fastlookup(topfaces, 0); - if (infected(firsttopface)) { - // This is due to he enlargement of the cavity. Find the updated top - // boundary face at edge [a,b]. - // Comment: An uninfected tet at [a,b] should be found since [a,b] is a - // boundary edge of the missing region R. It should not be enclosed - // by the enlarged cavity. - pa = apex(firsttopface); // SELF_CHECK - while (1) { - fnextself(firsttopface); - if (!infected(firsttopface)) break; - assert(apex(firsttopface) != pa); // SELF_CHECK - } - } - toptet = firsttopface; - pa = apex(toptet); - fsymself(toptet); - // Search a subface from the top mesh. - while (1) { - esymself(toptet); // The next face in the same tet. - pc = apex(toptet); - assert(pc != pa); // We should not return to the starting point. - if (pmarktested(pc)) break; // [a,b,c] is a subface. - fsymself(toptet); // Go to the adjacent tet. - } - // Search the subface [a,b,c] in the bottom mesh. - // bottet = * (triface *) fastlookup(botfaces, 0); - if (infected(firstbotface)) { - pa = apex(firstbotface); // SELF_CHECK - while (1) { - fnextself(firstbotface); - if (!infected(firstbotface)) break; - assert(apex(firstbotface) != pa); // SELF_CHECK - } - } - bottet = firstbotface; - pa = apex(bottet); - fsymself(bottet); - while (1) { - esymself(bottet); // The next face in the same tet. - pf = apex(bottet); - assert(pf != pa); // We should not return to the starting point. - if (pf == pc) break; // Face matched. - if (pmarktested(pf)) { - mflag = false; break; // Not matched. - } - fsymself(bottet); - } if (mflag) { - // Connect the two tets together. + // Found a pair of matched faces in 'toptet' and 'bottet'. bond(toptet, bottet); // Both are interior tets. infect(toptet); @@ -17771,20 +16234,23 @@ bool tetgenmesh::fillcavity(arraypool* topshells, arraypool* botshells, 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); } - // Match pairs of subfaces (middle faces), connect top and bottom tets. + // 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); + midface = (triface *) fastlookup(midfaces, i); // The tet must be a new created tet (marktested). - assert(marktested(midface)); // SELF_CHECK - - // Check the neighbors at edges [b, c] and [c, a]. - for (j = 0; j < 2 && mflag; j++) { - enextself(midface); // [b, c] or [c, a]. - pg = apex(midface); - toptet = midface; + 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. @@ -17794,6 +16260,7 @@ bool tetgenmesh::fillcavity(arraypool* topshells, arraypool* botshells, break; // Find a subface. } if (pc == dummypoint) { + assert(0); // Check this case. break; // Find a subface. } // Go to the adjacent tet. @@ -17807,16 +16274,19 @@ bool tetgenmesh::fillcavity(arraypool* topshells, arraypool* botshells, if (!bflag) { // assert(marktested(toptet)); // SELF_CHECK if (!facemarked(toptet)) { - fsym(midface, bottet); + fsym(*midface, bottet); + spintet = bottet; while (1) { esymself(bottet); - pf = apex(bottet); - if (pf == pc) break; // Face matched. - if (pmarktested(pf)) { - mflag = false; break; // Not matched - } + 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. @@ -17828,79 +16298,185 @@ bool tetgenmesh::fillcavity(arraypool* topshells, arraypool* botshells, markface(toptet); midfaces->newindex((void **) &parytet); *parytet = toptet; - } else { - // The 'bottet' is not inside the cavity! - // This case can happen when the cavity was enlarged, and the - // 'toptet' is a co-facet (sub)face adjacent to the missing - // region, and it is a boundary face of the top cavity. - // So the toptet and bottet should be bonded already through - // a temp subface. See fig/dump-cavity-case18. Check it. - fsym(toptet, neightet); - assert(neightet.tet == bottet.tet); // SELF_CHECK - assert(neightet.ver == bottet.ver); // SELF_CHECK - // Do not add this face into 'midfaces'. } - } + } 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 (midfaces != NULL) - - if (mflag) { - if (midfaces != NULL) { + if (mflag) { if (b->verbose > 2) { printf(" Found %ld middle subfaces.\n", midfaces->objects); } - if (midfaces->objects > maxregionsize) { - maxregionsize = midfaces->objects; - } - // Unmark middle faces. + 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); - assert(facemarked(midface)); // SELF_CHECK - unmarkface(midface); - } - } - } else { - // Faces at top and bottom are not matched. There exists non-Delaunay - // subedges. See fig/dump-cavity-case5.lua. - pa = org(toptet); - pb = dest(toptet); - pc = apex(toptet); - pf = apex(bottet); - - pf = oppo(toptet); - pg = oppo(bottet); - // Find a subface in R which intersects the edge [f,g]. - for (i = 0; i < missingshs->objects; i++) { - parysh = (face *) fastlookup(missingshs, i); - pa = sorg(*parysh); - pb = sdest(*parysh); - pc = sapex(*parysh); - if (tri_edge_test(pa, pb, pc, pf, pg, NULL, 1, types, poss)) { - // Found a subface. - break; + 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); } - } - if (i < missingshs->objects) { - // Such subface exist. - recentsh = *parysh; + // 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 { - assert(0); // Debug this case. + 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); + } + } } - - // Set a tet for searching the new point. - recenttet = firsttopface; - } + midfaces->restart(); + } else { + mflag = true; + } // Delete the temp subfaces. - for (k = 0; k < 2; k++) { - cavshells = (k == 0 ? topshells : botshells); + 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); @@ -17913,9 +16489,6 @@ bool tetgenmesh::fillcavity(arraypool* topshells, arraypool* botshells, if (botshells != NULL) { botshells->restart(); } - if (midfaces != NULL) { - midfaces->restart(); - } return mflag; } @@ -17930,10 +16503,12 @@ 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 i, j, k; + int t1ver; + int i, j; if (b->verbose > 2) { printf(" Carve cavity: %ld old tets.\n", crosstets->objects); @@ -17949,38 +16524,44 @@ void tetgenmesh::carvecavity(arraypool *crosstets, arraypool *topnewtets, // Collect all subfaces and segments which attached to the old tets. for (i = 0; i < crosstets->objects; i++) { parytet = (triface *) fastlookup(crosstets, i); - assert(infected(*parytet)); // SELF_CHECK - for (parytet->ver = 0; parytet->ver < 4; parytet->ver++) { - tspivot(*parytet, checksh); - if (checksh.sh != NULL) { - if (!sinfected(checksh)) { - sinfect(checksh); - cavetetshlist->newindex((void **) &parysh); - *parysh = checksh; + 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 } - for (j = 0; j < 6; j++) { - parytet->ver = edge2ver[j]; - tsspivot1(*parytet, checkseg); - if (checkseg.sh != NULL) { - if (!sinfected(checkseg)) { - sinfect(checkseg); - cavetetseglist->newindex((void **) &paryseg); - *paryseg = checkseg; + 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++) { - checksh = * (face *) fastlookup(cavetetshlist, i); - suninfect(checksh); + parysh = (face *) fastlookup(cavetetshlist, i); + suninfect(*parysh); } // Uninfect collected segments. for (i = 0; i < cavetetseglist->objects; i++) { - checkseg = * (face *) fastlookup(cavetetseglist, i); - suninfect(checkseg); + paryseg = (face *) fastlookup(cavetetseglist, i); + suninfect(*paryseg); } // Connect subfaces to new tets. @@ -17996,11 +16577,6 @@ void tetgenmesh::carvecavity(arraypool *crosstets, arraypool *topnewtets, // Does this tet lie inside the cavity. if (infected(neightet)) { checksh = *parysh; - if (b->verbose > 2) { - printf(" Found an interior subface (%d, %d, %d)\n", - pointmark(sorg(checksh)), pointmark(sdest(checksh)), - pointmark(sapex(checksh))); - } stdissolve(checksh); caveencshlist->newindex((void **) &parysh); *parysh = checksh; @@ -18014,10 +16590,7 @@ void tetgenmesh::carvecavity(arraypool *crosstets, arraypool *topnewtets, tsbond(newtet, *parysh); } } // i - if (b->verbose > 2) { - printf(" %ld (%ld) cavity (interior) subfaces.\n", - cavetetshlist->objects, caveencshlist->objects); - } + for (i = 0; i < cavetetseglist->objects; i++) { checkseg = * (face *) fastlookup(cavetetseglist, i); @@ -18031,10 +16604,6 @@ void tetgenmesh::carvecavity(arraypool *crosstets, arraypool *topnewtets, } fnextself(spintet); if (spintet.tet == neightet.tet) { - if (b->verbose > 2) { - printf(" Found an interior seg (%d, %d)\n", - pointmark(sorg(checkseg)), pointmark(sdest(checkseg))); - } sstdissolve1(checkseg); caveencseglist->newindex((void **) &paryseg); *paryseg = checkseg; @@ -18052,10 +16621,7 @@ void tetgenmesh::carvecavity(arraypool *crosstets, arraypool *topnewtets, } } } // i - if (b->verbose > 2) { - printf(" %ld (%ld) cavity (interior) segments.\n", - cavetetseglist->objects, caveencseglist->objects); - } + cavetetshlist->restart(); cavetetseglist->restart(); @@ -18063,6 +16629,9 @@ void tetgenmesh::carvecavity(arraypool *crosstets, arraypool *topnewtets, // 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); } @@ -18070,8 +16639,8 @@ void tetgenmesh::carvecavity(arraypool *crosstets, arraypool *topnewtets, // Collect new tets in cavity. Some new tets have already been found // (and infected) in the fillcavity(). We first collect them. - for (k = 0; k < 2; k++) { - newtets = (k == 0 ? topnewtets : botnewtets); + 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); @@ -18081,20 +16650,17 @@ void tetgenmesh::carvecavity(arraypool *crosstets, arraypool *topnewtets, } } // i } - } // k + } // j // Now we collect all new tets in cavity. for (i = 0; i < crosstets->objects; i++) { parytet = (triface *) fastlookup(crosstets, i); - if (i == 0) { - recenttet = *parytet; // Remember a live handle. - } 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 + //assert((point) neightet.tet[7] != dummypoint); // SELF_CHECK infect(neightet); crosstets->newindex((void **) &pnewtet); *pnewtet = neightet; @@ -18103,9 +16669,12 @@ void tetgenmesh::carvecavity(arraypool *crosstets, arraypool *topnewtets, } // j } // i + parytet = (triface *) fastlookup(crosstets, 0); + recenttet = *parytet; // Remember a live handle. + // Delete outer new tets. - for (k = 0; k < 2; k++) { - newtets = (k == 0 ? topnewtets : botnewtets); + 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); @@ -18113,6 +16682,9 @@ void tetgenmesh::carvecavity(arraypool *crosstets, arraypool *topnewtets, // This is an interior tet. uninfect(*parytet); unmarktest(*parytet); + if (ishulltet(*parytet)) { + hullsize++; + } } else { // An outer tet. Delete it. tetrahedrondealloc(parytet->tet); @@ -18135,21 +16707,19 @@ void tetgenmesh::carvecavity(arraypool *crosstets, arraypool *topnewtets, /////////////////////////////////////////////////////////////////////////////// void tetgenmesh::restorecavity(arraypool *crosstets, arraypool *topnewtets, - arraypool *botnewtets) + arraypool *botnewtets, arraypool *missingshbds) { - triface *parytet, neightet; - face checksh; + 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 - if (i == 0) { - recenttet = *parytet; // Remember a live handle. - } parytet->ver = 0; for (parytet->ver = 0; parytet->ver < 4; parytet->ver++) { fsym(*parytet, neightet); @@ -18172,6 +16742,31 @@ void tetgenmesh::restorecavity(arraypool *crosstets, arraypool *topnewtets, 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); @@ -18201,7 +16796,8 @@ void tetgenmesh::restorecavity(arraypool *crosstets, arraypool *topnewtets, // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::flipcertify(triface *chkface, badface **pqueue) +void tetgenmesh::flipcertify(triface *chkface,badface **pqueue,point plane_pa, + point plane_pb, point plane_pc) { badface *parybf, *prevbf, *nextbf; triface neightet; @@ -18278,7 +16874,7 @@ void tetgenmesh::flipcertify(triface *chkface, badface **pqueue) for (i = 0; i < 5; i++) { if (pmarktest2ed(p[i])) { // A top point has a positive weight. - w[i] = orient3d(plane_pa, plane_pb, plane_pc, p[i]); + w[i] = orient3dfast(plane_pa, plane_pb, plane_pc, p[i]); if (w[i] < 0) w[i] = -w[i]; assert(w[i] != 0); } else { @@ -18294,12 +16890,7 @@ void tetgenmesh::flipcertify(triface *chkface, badface **pqueue) // p[1], p[0], p[2], p[3]. insph = insphere(p[1], p[0], p[2], p[3], p[4]); - - if (b->flipinsert_ori4dexact) { - ori4 = orient4dexact(p[1], p[0], p[2], p[3], p[4],w[1],w[0],w[2],w[3],w[4]); - } else { - ori4 = orient4d(p[1], p[0], p[2], p[3], p[4], w[1], w[0], w[2], w[3], w[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]); @@ -18332,7 +16923,7 @@ void tetgenmesh::flipcertify(triface *chkface, badface **pqueue) // 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. + //if (!b->flipinsert_random) { // Default use a priority queue. // Insert the item into priority queue. while (nextbf != NULL) { if (nextbf->key < parybf->key) { @@ -18342,7 +16933,7 @@ void tetgenmesh::flipcertify(triface *chkface, badface **pqueue) break; } } - } // if (!b->flipinsert_random) // -L1 + //} // if (!b->flipinsert_random) // Insert the new item between prev and next items. if (prevbf == NULL) { *pqueue = parybf; @@ -18374,19 +16965,20 @@ void tetgenmesh::flipinsertfacet(arraypool *crosstets, arraypool *toppoints, arraypool *botpoints, arraypool *midpoints) { arraypool *crossfaces, *bfacearray; - triface fliptets[5], baktets[2], fliptet, newface; + 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; @@ -18434,9 +17026,6 @@ void tetgenmesh::flipinsertfacet(arraypool *crosstets, arraypool *toppoints, if (b->verbose > 1) { printf(" Found %ld crossing faces.\n", crossfaces->objects); } - if (crossfaces->objects > maxcrossfacecount) { - maxcrossfacecount = crossfaces->objects; - } for (i = 0; i < crosstets->objects; i++) { parytet = (triface *) fastlookup(crosstets, i); @@ -18449,21 +17038,20 @@ void tetgenmesh::flipinsertfacet(arraypool *crosstets, arraypool *toppoints, for (i = 0; i < crossfaces->objects; i++) { parytet = (triface *) fastlookup(crossfaces, i); - flipcertify(parytet, &pqueue); + flipcertify(parytet, &pqueue, plane_pa, plane_pb, plane_pc); } crossfaces->restart(); // The list for temporarily storing unflipable faces. bfacearray = new arraypool(sizeof(triface), 4); - fliploop: fcount = 0; // Count the number of flips. // Flip insert the facet. while (pqueue != NULL) { - // Pop a face from the priotity queue. + // Pop a face from the priority queue. popbf = pqueue; bface = *popbf; @@ -18518,7 +17106,7 @@ void tetgenmesh::flipinsertfacet(arraypool *crosstets, arraypool *toppoints, fliptets[0] = fliptet; // abcd, d may be the new vertex. fliptets[1] = neightet; // bace. - flip23(fliptets, 1, 0, 0); + flip23(fliptets, 1, &fc); // Put the link faces into check list. for (i = 0; i < 3; i++) { eprevesym(fliptets[i], newface); @@ -18545,16 +17133,26 @@ void tetgenmesh::flipinsertfacet(arraypool *crosstets, arraypool *toppoints, 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, 0, 0); + flip32(fliptets, 1, &fc); // Put the link faces into check list. for (i = 0; i < 3; i++) { esym(fliptets[0], newface); @@ -18588,7 +17186,7 @@ void tetgenmesh::flipinsertfacet(arraypool *crosstets, arraypool *toppoints, 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, 0, 0); + 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. @@ -18607,7 +17205,7 @@ void tetgenmesh::flipinsertfacet(arraypool *crosstets, arraypool *toppoints, 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, 0, 0); + flip32(fliptets, 1, &fc); // Put the "outer" link faces into check list. // fliptets[0] = [d,e,f,a] // fliptets[1] = [e,d,f,b] @@ -18645,23 +17243,22 @@ void tetgenmesh::flipinsertfacet(arraypool *crosstets, arraypool *toppoints, pointmark(bface.fapex), pointmark(bface.foppo), pointmark(bface.noppo), bface.key); } - dbg_ignore_facecount++; } // 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); + flipcertify(parytet, &pqueue, plane_pa, plane_pb, plane_pc); } crossfaces->restart(); - if (!b->flipinsert_random) { + 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); + flipcertify(parytet, &pqueue, plane_pa, plane_pb, plane_pc); } } bfacearray->restart(); @@ -18683,22 +17280,10 @@ void tetgenmesh::flipinsertfacet(arraypool *crosstets, arraypool *toppoints, printf("!! No flip is found in %ld faces.\n", bfacearray->objects); assert(0); } - 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); - } - } - bfacearray->restart(); - goto fliploop; - } } // 'bfacearray' may be not empty (for what reason ??). - dbg_unflip_facecount += bfacearray->objects; + //dbg_unflip_facecount += bfacearray->objects; assert(flippool->items == 0l); delete bfacearray; @@ -18717,11 +17302,6 @@ void tetgenmesh::flipinsertfacet(arraypool *crosstets, arraypool *toppoints, f32count = flip32count - f32count; f44count = flip44count - f44count; totalfcount = f23count + f32count + f44count; - - if (totalfcount > maxflipsequence) { - maxflipsequence = totalfcount; - } - if (b->verbose > 2) { printf(" Total %ld flips. f23(%ld), f32(%ld), f44(%ld).\n", totalfcount, f23count, f32count, f44count); @@ -18734,156 +17314,85 @@ void tetgenmesh::flipinsertfacet(arraypool *crosstets, arraypool *toppoints, // // // '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: All subfaces in R are smarktested. // // // // 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, +bool tetgenmesh::fillregion(arraypool* missingshs, arraypool* missingshbds, arraypool* newshs) { badface *newflipface, *popface; - triface searchtet, spintet; + triface searchtet, spintet, neightet; face oldsh, newsh, opensh, *parysh; face casout, casin, neighsh, checksh; - face checkseg, fakeseg; - point pc, pd, pe, pf, ppt[2]; - enum interresult dir; - REAL n[3], len; // elen[3]; - bool insideflag; - int types[2], poss[4]; - int i, j, k; + face neighseg, checkseg; + point pc; + int success; + int t1ver; + int i, j; - if (b->verbose > 2) { - printf(" Fill region: %ld old subfaces (%ld).\n", missingshs->objects, - fillregioncount); - } - - // Search the first constrained face of R. It is found from the set of - // faces sharing at a boundary edge [a,b]. Such face must be found. - // The search takes the following two steps: - // - First, finds a candidate face [a,b,c] where c is also a vertex of R; - // Note that [a,b,c] may not be the right face to fill R. For instance, - // when R is concave at b. - // - Second, check if [a,b,c] can fill R. This can be checked if an - // adjacent tet of [a,b,c] intersects R. This is a tetrahedron-triangle - // intersection test. It can be reduced to two triangle-edge intersect - // tests, i.e., intersect the two faces not containing the edge [a,b] in - // this tet with all interior edges of R. - - // We start from the first boundary edge of R. - oldsh = * (face *) fastlookup(missingshbds, 0); - ppt[0] = sorg(oldsh); - ppt[1] = sdest(oldsh); - point2tetorg(ppt[0], searchtet); - dir = finddirection(&searchtet, ppt[1], 0); - assert(dir == ACROSSVERT); // SELF_CHECK - - insideflag = false; - - // Each face has two adjacent tets. - for (k = 0; k < 2; k++) { - if (b->verbose > 2) { - printf(" Search an interior face from edge (%d, %d).\n", - pointmark(ppt[0]), pointmark(ppt[1])); - } + + // 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)) { - // Found a candidate face. Check if it is inside R. - if (missingshs->objects > 2l) { - // pd = oppo(spintet); - // if (pd == dummypoint) { - // Calculate an above point for this subface. - facenormal(ppt[0], ppt[1], pc, n, 1, NULL); - len = sqrt(DOT(n, n)); - n[0] /= len; - n[1] /= len; - n[2] /= len; - len = DIST(ppt[0], ppt[1]); - len += DIST(ppt[1], pc); - len += DIST(pc, ppt[0]); - len /= 3.0; - dummypoint[0] = ppt[0][0] + len * n[0]; - dummypoint[1] = ppt[0][1] + len * n[1]; - dummypoint[2] = ppt[0][2] + len * n[2]; - pd = dummypoint; - // } - //if (pd != dummypoint) { - for (j = 0; j < 2 && !insideflag; j++) { - for (i = 1; i < missingshs->objects && !insideflag; i++) { - parysh = (face *) fastlookup(missingshs, i); - // Get an interior edge of R. - pe = sorg(*parysh); - pf = sdest(*parysh); - if (tri_edge_test(ppt[j],pc,pd,pe,pf,NULL,1,types,poss)) { - dir = (enum interresult) types[0]; - if (dir == ACROSSFACE) { - searchtet = spintet; - insideflag = true; - } else if (dir == ACROSSEDGE) { - searchtet = spintet; - insideflag = true; - } - } - } // i - } // j - // } - // if (pd == dummypoint) { - dummypoint[0] = 0; - dummypoint[1] = 0; - dummypoint[2] = 0; - // } - } else { - // It is a simple 2-to-2 flip. - searchtet = spintet; - insideflag = true; - } - } // if (pmarktested(pc)) - if (insideflag) break; + neightet = spintet; + j++; + } fnextself(spintet); if (spintet.tet == searchtet.tet) break; - } // while (1) - if (insideflag) break; - esymself(searchtet); - ppt[0] = org(searchtet); - ppt[1] = dest(searchtet); - } // k - - if (!insideflag) { - // Something strange is happening. - // Refine the missing region by adding a Steiner point. - recentsh = oldsh; - recenttet = searchtet; // For point location. + } + 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; } - // Create a new subface at the boundary edge. - if (b->verbose > 2) { - printf(" Create a new subface (%d, %d, %d)\n", pointmark(ppt[0]), - pointmark(ppt[1]), pointmark(pc)); - } makeshellface(subfaces, &newsh); - setsorg(newsh, ppt[0]); - setsdest(newsh, ppt[1]); - setsapex(newsh, pc); + 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. - tspivot(searchtet, checksh); // SELF_CHECK - assert(checksh.sh == NULL); // SELF_CHECK 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; @@ -18902,384 +17411,491 @@ bool tetgenmesh::fillregion(arraypool* missingshs, arraypool* missingshbds, sbond1(newsh, casout); sbond1(casin, newsh); } - if (checkseg.sh != NULL) { - ssbond(newsh, checkseg); + 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; } - // Add this new subface into list. - sinfect(newsh); - newshs->newindex((void **) &parysh); - *parysh = newsh; - // Push two "open" side of the new subface into stack. - for (i = 0; i < 2; i++) { - senextself(newsh); - newflipface = (badface *) flippool->alloc(); - newflipface->ss = newsh; - newflipface->nextitem = flipstack; - flipstack = newflipface; + + for (i = 0; i < 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; - // Every other boundary edge of R is identified as a segment. Insert a faked - // segments at the place if it is not a segment. - for (i = 1; i < missingshbds->objects; i++) { - parysh = (face *) fastlookup(missingshbds, i); - ppt[0] = sorg(*parysh); - ppt[1] = sdest(*parysh); - point2tetorg(ppt[0], searchtet); - dir = finddirection(&searchtet, ppt[1], 0); - assert(dir == ACROSSVERT); // SELF_CHECK - tsspivot1(searchtet, checkseg); - if (checkseg.sh == NULL) { - // Insert a fake segment at this tet. - if (b->verbose > 2) { - printf(" Insert a fake segment (%d, %d)\n", pointmark(ppt[0]), - pointmark(ppt[1])); - } - makeshellface(subsegs, &fakeseg); - setsorg(fakeseg, ppt[0]); - setsdest(fakeseg, ppt[1]); - 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; - } - checkseg = fakeseg; - } - // Let the segment hold the old subface. - checkseg.shver = 0; - sbond1(checkseg, *parysh); - // Remember it to free it later. - *parysh = checkseg; + for (i = 0; i < cavebdrylist->objects; i++) { + cavfaces->newindex((void **) &parytet); + *parytet = * (triface *) fastlookup(cavebdrylist, i); } - // Loop until 'flipstack' is empty. - while (flipstack != NULL) { + for (i = 0; i < caveoldtetlist->objects; i++) { + crosstets->newindex((void **) &parytet); + *parytet = * (triface *) fastlookup(caveoldtetlist, i); + } - // 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); + cavetetvertlist->restart(); + cavebdrylist->restart(); + caveoldtetlist->restart(); - // Process it if it is still open. - spivot(opensh, casout); - if (casout.sh == NULL) { - if (b->verbose > 2) { - printf(" Get an open side (%d, %d) - %d.\n", - pointmark(sorg(opensh)), pointmark(sdest(opensh)), - pointmark(sapex(opensh))); + // 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; } - // Search a neighbor to close this side. - stpivot(opensh, searchtet); - tsspivot1(searchtet, checkseg); - if (checkseg.sh == NULL) { - // No segment. It is inside R. Search for a new face to fill in R. - // Note that the face may not be found (see fig 2010-05-25-c). - spintet = searchtet; - fnextself(spintet); // Skip the current face. - while (1) { - pc = apex(spintet); - if (pmarktested(pc)) { - // Found a place for a new subface inside R -- Case (i). - tspivot(spintet, checksh); - if (checksh.sh == NULL) { - // Create a new subface. - if (b->verbose > 2) { - printf(" Create a new subface (%d, %d, %d)\n", - pointmark(org(spintet)), pointmark(dest(spintet)), - pointmark(pc)); - } - 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 { - // A new subface has already been created. - assert(sinfected(checksh)); // It must be in stack. - spivot(checksh, neighsh); // SELF_CHECK - assert(neighsh.sh == NULL); // Its side must be open. - if (b->verbose > 2) { - printf(" Connect to another open side (%d, %d, %d)\n", - pointmark(sorg(checksh)), pointmark(sdest(checksh)), - pointmark(sapex(checksh))); - } - sbond(opensh, checksh); // Simply connect them. - } - break; // -- Case (i) - } - fnextself(spintet); - if (spintet.tet == searchtet.tet) { - // Not find any face to fill in R at this side. - // TO DO: suggest a point to split the edge. - assert(0); - } - } // 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. - if (b->verbose > 2) { - printf(" Delete a fake segment (%d, %d)\n", - pointmark(sorg(checkseg)), pointmark(sdest(checkseg))); - } - spintet = searchtet; - while (1) { - tssdissolve1(spintet); - fnextself(spintet); - if (spintet.tet == searchtet.tet) break; - } - shellfacedealloc(subsegs, checkseg.sh); - } - if (b->verbose > 2) { - printf(" Connect to a boundary edge (%d, %d, %d)\n", - pointmark(sorg(oldsh)), pointmark(sdest(oldsh)), - pointmark(sapex(oldsh))); - } - sspivot(oldsh, checkseg); - 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); - } + } + + 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); } - sbond1(opensh, casout); - sbond1(casin, opensh); - } - if (checkseg.sh != NULL) { - ssbond(opensh, checkseg); } } + shellfacedealloc(subfaces, parysh->sh); + } + if (splitseg != NULL) { + // Delete the old segment in sC(p). + shellfacedealloc(subsegs, splitseg->sh); + } - } // if (casout.sh == NULL) - - } // while (flipstack != NULL) - - // Uninfect all new subfaces. - for (i = 0; i < newshs->objects; i++) { - parysh = (face *) fastlookup(newshs, i); - suninfect(*parysh); + // 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; + } } - if (b->verbose > 2) { - printf(" Created %ld new subfaces.\n", newshs->objects); + // 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; + } } - fillregioncount++; - return true; + 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() +void tetgenmesh::refineregion(face &splitsh, arraypool *cavpoints, + arraypool *cavfaces, arraypool *cavshells, + arraypool *newtets, arraypool *crosstets, + arraypool *misfaces) { - triface searchtet; - face splitsh; - face *paryseg, sseg; - point steinpt, pa, pb, pc; + triface searchtet, spintet; + face splitseg, *paryseg; + point steinpt, pa, pb, refpt; insertvertexflags ivf; - REAL auv[2], buv[2], newuv[2], t; - int fmark, fid, eid; - int loc; // iloc, sloc; - int s, i; - - // The mesh is a CDT. - assert(subsegstack->objects == 0l); // SELF_CHECK + enum interresult dir; + long baknum = points->items; + int t1ver; + int i; - // Create a new point. - makepoint(&steinpt, FREEFACETVERTEX); + if (b->verbose > 2) { + printf(" Refining region at edge (%d, %d, %d).\n", + pointmark(sorg(splitsh)), pointmark(sdest(splitsh)), + pointmark(sapex(splitsh))); + } - // The 'recentsh' saved an edge to be split. - splitsh = recentsh; // Add the Steiner point at the barycenter of the face. pa = sorg(splitsh); pb = sdest(splitsh); - pc = sapex(splitsh); - - if (b->psc) { - assert(in->facetmarkerlist != NULL); - fmark = shellmark(splitsh) - 1; - fid = in->facetmarkerlist[fmark]; - if (pointtype(pa) == RIDGEVERTEX) { - in->getvertexparamonface(in->geomhandle, pointmark(pa), fid, auv); - } else if (pointtype(pa) == FREESEGVERTEX) { - eid = pointgeomtag(pa); // The Edge containing this Steiner point. - t = pointgeomuv(pa, 0); // The Steiner point's parameter on Edge. - in->getedgesteinerparamonface(in->geomhandle, eid, t, fid, auv); - } else if (pointtype(pa) == FREEFACETVERTEX) { - auv[0] = pointgeomuv(pa, 0); - auv[1] = pointgeomuv(pa, 1); - } else { - assert(0); - } - if (pointtype(pb) == RIDGEVERTEX) { - in->getvertexparamonface(in->geomhandle, pointmark(pb), fid, buv); - } else if (pointtype(pb) == FREESEGVERTEX) { - eid = pointgeomtag(pb); // The Edge containing this Steiner point. - t = pointgeomuv(pb, 0); // The Steiner point's parameter on Edge. - in->getedgesteinerparamonface(in->geomhandle, eid, t, fid, buv); - } else if (pointtype(pb) == FREEFACETVERTEX) { - buv[0] = pointgeomuv(pb, 0); - buv[1] = pointgeomuv(pb, 1); - } else { - assert(0); - } - newuv[0] = 0.5 * (auv[0] + buv[0]); - newuv[1] = 0.5 * (auv[1] + buv[1]); - in->getsteineronface(in->geomhandle, fid, newuv, steinpt); - setpointgeomuv(steinpt, 0, newuv[0]); - setpointgeomuv(steinpt, 1, newuv[1]); - setpointgeomtag(steinpt, fid); - } else { - for (i = 0; i < 3; i++) { - steinpt[i] = (pa[i] + pb[i] + pc[i]) / 3.0; - } + // Create a new point. + makepoint(&steinpt, FREEFACETVERTEX); + for (i = 0; i < 3; i++) { + steinpt[i] = 0.5 * (pa[i] + pb[i]); } - // Start searching it from 'recentet'. - searchtet = recenttet; - // Now insert the point p. The flags are chosen as follows: - // - boywat = 2, the current T is a CDT, - // - lawson = 2, do flip after inserting p, some existing segments - // and subfaces may be flipped, they are queued and - // and will be recovered. - // - rejflag = 1, reject p if it encroaches upon at least one segment, - // queue encroached segments. - ivf.iloc = (int) OUTSIDE; - ivf.bowywat = 2; - ivf.lawson = 2; - ivf.rejflag = 1; - ivf.chkencflag = 0; - ivf.sloc = (int) ONFACE; - ivf.sbowywat = 2; - ivf.splitbdflag = 0; - ivf.validflag = 1; - ivf.respectbdflag = 0; - ivf.assignmeshsize = 0; - loc = insertvertex(steinpt, &searchtet, &splitsh, NULL, &ivf); - - assert((loc != OUTSIDE) && (loc != ONVERTEX)); - if (loc == NEARVERTEX) { - // The new point is either ON or VERY CLOSE to an existing point. - pa = point2ppt(steinpt); - printf(" !! Avoid to create a short edge (length = %g)\n", - distance(steinpt, pa)); - // Indicate it may be an input problem. - printf(" Short edge length bound is: %g. Tolerance is %g.\n", - b->minedgelength, b->epsilon); - terminatetetgen(4); - } - - if (loc == ENCSEGMENT) { - // Some segments are encroached and queued. - assert(encseglist->objects > 0l); - // Randomly pick one encroached segment to split. - s = randomnation(encseglist->objects); - paryseg = (face *) fastlookup(encseglist, s); - sseg = *paryseg; - // The new point p is the midpoint of this segment. - getsteinerptonsegment(&sseg, NULL, steinpt); - setpointtype(steinpt, FREESEGVERTEX); - encseglist->restart(); // Clear the queue. + 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; - // Start searching from an adjacent tetrahedron (containing the segment). - sstpivot1(sseg, searchtet); - spivot(sseg, splitsh); - // Insert the point p. The flags are chosen as follows: - // - boywat = 2, the current T is a CDT, - // - lawson = 2, do flip after inserting p, some existing segments - // and subfaces may be flipped, they are queued and - // and will be recovered. - // - rejflag = 0, always insert p, even it will cause some segments - // or subfaces missing, queue missing boundaries. - ivf.iloc = (int) ONEDGE; - ivf.bowywat = 2; - ivf.lawson = 2; - ivf.rejflag = 0; - ivf.chkencflag = 0; - ivf.sloc = (int) ONEDGE; - ivf.sbowywat = 2; - ivf.splitbdflag = 0; - ivf.validflag = 1; - ivf.respectbdflag = 0; - ivf.assignmeshsize = 0; - loc = insertvertex(steinpt, &searchtet, &splitsh, &sseg, &ivf); + point2tetorg(pa, searchtet); // Start location from it. + ivf.iloc = (int) OUTSIDE; - if (loc == NEARVERTEX) { - // The new point is either ON or VERY CLOSE to an existing point. - pa = point2ppt(steinpt); - printf(" !! Avoid to create a short edge (length = %g)\n", - distance(steinpt, pa)); - // Indicate it may be an input problem. - printf(" Short edge length bound is: %g. Tolerance is %g.\n", - b->minedgelength, b->epsilon); - terminatetetgen(4); + 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); } - - st_segref_count++; } else { st_facref_count++; + if (steinerleft > 0) steinerleft--; } - if (steinerleft > 0) steinerleft--; - // Do flip to recover Delaunayniess. - lawsonflip3d(steinpt, 2, 0, 0, 0); + 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; - // Some vertices may be queued, recover them. - if (subvertstack->objects > 0l) { - assert(0); //delaunizevertices(); - } + // 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 - // Some subsegments may be queued, recover them. - if (subsegstack->objects > 0l) { - delaunizesegments(); + if (b->verbose > 2) { + printf(" Added %ld Steiner points.\n", points->items - baknum); } } /////////////////////////////////////////////////////////////////////////////// // // -// constrainedfacets() Recover subfaces saved in 'subfacestack'. // +// constrainedfacets() Recover constrained facets in a CDT. // +// // +// All unrecovered subfaces are queued in 'subfacestack'. // // // /////////////////////////////////////////////////////////////////////////////// @@ -19290,16 +17906,14 @@ void tetgenmesh::constrainedfacets() arraypool *tg_topshells, *tg_botshells, *tg_facfaces; arraypool *tg_toppoints, *tg_botpoints; arraypool *tg_missingshs, *tg_missingshbds, *tg_missingshverts; - - triface searchtet, neightet; - face searchsh, neighsh, *parysh; - face checkseg, *paryseg; - point refpt, *parypt; + triface searchtet, neightet, crossedge; + face searchsh, *parysh, *parysh1; + face *paryseg; + point *parypt; enum interresult dir; - bool success; int facetcount; - //int bakhullsize; - int crossflag; + int success; + int t1ver; int i, j; // Initialize arrays. @@ -19317,245 +17931,184 @@ void tetgenmesh::constrainedfacets() 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); + encseglist = new arraypool(sizeof(face), 4); facetcount = 0; - // 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. + if (searchsh.sh[3] == NULL) continue; // It is dead. + if (isshtet(searchsh)) continue; // It is recovered. - stpivot(searchsh, neightet); - if (neightet.tet == NULL) { - // Find an unrecovered subface. - smarktest(searchsh); - tg_facfaces->newindex((void **) &parysh); - *parysh = searchsh; - // Collect all non-recovered subfaces of the same facet. - for (i = 0; i < tg_facfaces->objects; i++) { - searchsh = * (face *) fastlookup(tg_facfaces, i); - for (j = 0; j < 3; j++) { - sspivot(searchsh, checkseg); - if (checkseg.sh == NULL) { - spivot(searchsh, neighsh); - assert(neighsh.sh != NULL); // SELF_CHECK - if (!smarktested(neighsh)) { - // It may be already recovered. - stpivot(neighsh, neightet); - if (neightet.tet == NULL) { - smarktest(neighsh); - tg_facfaces->newindex((void **) &parysh); - *parysh = neighsh; - } + // 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(searchsh); - } // j - } // i - // Have found all facet subfaces (vertices). Uninfect them. - for (i = 0; i < tg_facfaces->objects; i++) { - parysh = (face *) fastlookup(tg_facfaces, i); - sunmarktest(*parysh); - } + } + 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(" Recover facet #%d: %ld subfaces.\n", facetcount + 1, - tg_facfaces->objects); - } - facetcount++; - - // Loop until 'tg_facfaces' is empty. - while (tg_facfaces->objects > 0l) { - // Get the last subface of this array. - tg_facfaces->objects--; - parysh = (face *) fastlookup(tg_facfaces, tg_facfaces->objects); - searchsh = *parysh; - - if (searchsh.sh[3] == NULL) continue; // Skip a dead subface. - - stpivot(searchsh, neightet); - if (neightet.tet != NULL) continue; // Not a missing subface. - - // Insert the subface. - searchtet.tet = NULL; - dir = scoutsubface(&searchsh, &searchtet); - if (dir == SHAREFACE) continue; // The subface is inserted. - if (dir == COLLISIONFACE) continue; // The subface is removed. - - // The subface is missing. Form the missing region. - // Re-use 'tg_crosstets' for 'adjtets'. - formmissingregion(&searchsh, tg_missingshs, tg_missingshbds, - tg_missingshverts, tg_crosstets); - - // Search for a crossing edge (tg_crosstets is cleared). - crossflag = scoutcrossedge(searchtet, tg_crosstets, tg_missingshs); - - if (crossflag == 1) { - // Recover subfaces by local retetrahedralization. - // Form a cavity of crossing tets. - if (formcavity(&searchtet, tg_missingshs, tg_crosstets, tg_topfaces, - tg_botfaces, tg_toppoints, tg_botpoints)) { - 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); - if (success) { - // Cavity is remeshed. Delete old tets and outer new tets. - carvecavity(tg_crosstets, tg_topnewtets, tg_botnewtets); - // Insert the missing region into cavity. - j = 0; // FOR DEBUG! Count the number of non-recovered faces. - for (i = 0; i < tg_missingshs->objects; i++) { - searchsh = * (face *) fastlookup(tg_missingshs, i); - searchtet.tet = NULL; - dir = scoutsubface(&searchsh, &searchtet); - assert(dir != COLLISIONFACE); // SELF_CHECK - if (dir != SHAREFACE) { - // A subface is missing. This is possible that the subface - // is not actually a constrained Delaunay face in T. - // Add this face at the end of the list, so it will be - // processed immediately. This is necessary because we - // have created some non-locally Delaunay face (by the - // remesh of the cavity). We have to insert the subfaces - // to make these face constrained Delaunay. - tg_facfaces->newindex((void **) &parysh); - *parysh = searchsh; - j++; // FOR DEBUG! - } - } // i - // Recover interior subfaces. - for (i = 0; i < caveencshlist->objects; i++) { - searchsh = * (face *) fastlookup(caveencshlist, i); - searchtet.tet = NULL; - dir = scoutsubface(&searchsh, &searchtet); - assert(dir != COLLISIONFACE); // SELF_CHECK - if (dir != SHAREFACE) { - // The subface is missing. This is possible that the subface - // is removed by the enlargement of the cavity. It has to - // be recovered. - // Add this face at the end of the list, so it will be - // processed immediately. We have to insert the subfaces - // to make these face constrained Delaunay. - tg_facfaces->newindex((void **) &parysh); - *parysh = searchsh; - j++; // FOR DEBUG! - } - } // i - // Recover interior segments. This should always be recovered. - for (i = 0; i < caveencseglist->objects; i++) { - paryseg = (face *) fastlookup(caveencseglist, i); - searchtet.tet = NULL; - refpt = NULL; - dir = scoutsegment(sorg(*paryseg),sdest(*paryseg),&searchtet, - &refpt, NULL); - assert(dir == SHAREEDGE); - // Insert this segment. - tsspivot1(searchtet, checkseg); // SELF_CHECK - if (checkseg.sh == NULL) { - // 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); - } - } // i - caveencshlist->restart(); - caveencseglist->restart(); - } else { - // Restore old tets and delete new tets. - restorecavity(tg_crosstets, tg_topnewtets, tg_botnewtets); - // Set a handle for searching subface. - //recentsh = searchsh; - } + 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 { - // Use the flip algorithm of Shewchuk to recover the subfaces. - flipinsertfacet(tg_crosstets, tg_toppoints, tg_botpoints, - tg_missingshverts); - // Check the missing subfaces again. - j = 0; // FOR DEBUG! Count the number of non-recovered faces. - for (i = 0; i < tg_missingshs->objects; i++) { - searchsh = * (face *) fastlookup(tg_missingshs, i); - searchtet.tet = NULL; - dir = scoutsubface(&searchsh, &searchtet); - assert(dir != COLLISIONFACE); // SELF_CHECK - if (dir != SHAREFACE) { - // A subface is missing. This is possible that the subface - // is not actually a constrained Delaunay face in T. - // Add this face at the end of the list, so it will be - // processed immediately. This is necessary because we - // have created some non-locally Delaunay face (by the - // remesh of the cavity). We have to insert the subfaces - // to make these face constrained Delaunay. - tg_facfaces->newindex((void **) &parysh); - *parysh = searchsh; - j++; // FOR DEBUG! - } - } // i - // Clear working lists. - tg_crosstets->restart(); - tg_topfaces->restart(); - tg_botfaces->restart(); - tg_toppoints->restart(); - tg_botpoints->restart(); - success = true; - } // if (b->flipinsert) - } else { - // Formcavity failed. - success = false; - } - } else { //if (crossflag == 0) { - // Recover subfaces by retriangulate the surface mesh. - // Re-use tg_topshells for newshs. - success = fillregion(tg_missingshs, tg_missingshbds, tg_topshells); - if (success) { - // Region is remeshed. Delete old subfaces (in tg_missingshs). - for (i = 0; i < tg_missingshs->objects; i++) { - parysh = (face *) fastlookup(tg_missingshs, i); - shellfacedealloc(subfaces, parysh->sh); + restorecavity(tg_crosstets, tg_topnewtets, tg_botnewtets, + tg_missingshbds); } - tg_topshells->restart(); } else { - // Search a handle for searching tetrahedron. - recenttet = searchtet; - } - } + // 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 - // 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) { + // 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); + } - if (!success) { - // The missing region can not be recovered. Refine it. - refineregion(); - // Clean the current list of facet subfaces. - //tg_facfaces->restart(); - } - } // while (tg_facfaces->objects > 0l) + // 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 (neightet.tet == NULL) - } // while (subfacstack->objects > 0l) + 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; @@ -19590,15 +18143,13 @@ void tetgenmesh::constraineddelaunay(clock_t& tv) // Statistics. long bakfillregioncount; long bakcavitycount, bakcavityexpcount; + long bakseg_ref_count; if (!b->quiet) { printf("Constrained Delaunay...\n"); } - //if (!b->psc) { - // Only identify acute vertex for PLC inputs. - markacutevertices(); - //} + makesegmentendpointsmap(); if (b->verbose) { printf(" Delaunizing segments.\n"); @@ -19606,37 +18157,25 @@ void tetgenmesh::constraineddelaunay(clock_t& tv) checksubsegflag = 1; - // Put all segments into the list. - if (0) { //if (b->order == 4) { // '-o4' option (for debug) - // In sequential order. - subsegs->traversalinit(); - for (i = 0; i < subsegs->items; i++) { - searchseg.sh = shellfacetraverse(subsegs); - //sinfect(searchseg); // Only save it once. - subsegstack->newindex((void **) &paryseg); - *paryseg = searchseg; - } - } else { - // 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; - } + // 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(" %ld Steiner points.\n", st_segref_count); + printf(" Inserted %ld Steiner points.\n", st_segref_count); } tv = clock(); @@ -19645,17 +18184,13 @@ void tetgenmesh::constraineddelaunay(clock_t& tv) printf(" Constraining facets.\n"); } - if (b->flipinsert) { - // Clear the counters. - flip23count = flip32count = flip44count = 0l; - } - // Subfaces will be introduced. checksubfaceflag = 1; bakfillregioncount = fillregioncount; bakcavitycount = cavitycount; bakcavityexpcount = cavityexpcount; + bakseg_ref_count = st_segref_count; // Randomly order the subfaces. subfaces->traversalinit(); @@ -19684,10 +18219,10 @@ void tetgenmesh::constraineddelaunay(clock_t& tv) } printf(".\n"); } - if (st_segref_count + st_facref_count > 0) { + 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, st_segref_count, - st_facref_count); + st_segref_count + st_facref_count - bakseg_ref_count, + st_segref_count - bakseg_ref_count, st_facref_count); } } } @@ -19711,10 +18246,6 @@ void tetgenmesh::constraineddelaunay(clock_t& tv) // the flip face, and [d,e] is the flip edge. NOTE: 'pc' may be 'dummypoint',// // other points must not be 'dummypoint'. // // // -// For avoiding mutually flipping, once a crossing face is flipped, it will // -// never be re-created again. Also, we never create a face or edge which is // -// intersecting the current recovering segment or subface. // -// // /////////////////////////////////////////////////////////////////////////////// int tetgenmesh::checkflipeligibility(int fliptype, point pa, point pb, @@ -19722,45 +18253,23 @@ int tetgenmesh::checkflipeligibility(int fliptype, point pa, point pb, int level, int edgepivot, flipconstraints* fc) { - int rejflag; - int i; - point tmppts[3]; - REAL normal[3], area, len; - REAL ori1, ori2; - REAL abovept[3]; - enum interresult dir; int types[2], poss[4]; int intflag; - - rejflag = 0; + 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]. - if (pc != dummypoint) { - // Do not flip if the newly created faces intersect this edge in - // their interiors. - tmppts[0] = pa; - tmppts[1] = pb; - tmppts[2] = pc; - if (0) { - // Make sure that the three new faces are not degenerate. - for (i = 0; i < 3 && !rejflag; i++) { - facenormal(pe, pd, tmppts[i], normal, 1, &len); - area = sqrt(DOT(normal, normal)); - if (area == 0) { - rejflag = 1; // A degenerate face. - } else { - if ((area / (len * len)) < b->epsilon) { - rejflag = 1; // A nearly degenerate face. - } - } - } // i - } - for (i = 0; i < 3 && !rejflag; i++) { + 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) { @@ -19775,12 +18284,7 @@ int tetgenmesh::checkflipeligibility(int fliptype, point pa, point pb, // Since [e,d] is the newly created edge. Reject this flip. rejflag = 1; } - } else { - if ((dir == ACROSSVERT) || (dir == TOUCHEDGE) || - (dir == TOUCHFACE)) { - assert(0); // Check this case. - } - } // dir + } } else if (intflag == 4) { // They may intersect at either a point or a line segment. dir = (enum interresult) types[0]; @@ -19790,177 +18294,32 @@ int tetgenmesh::checkflipeligibility(int fliptype, point pa, point pb, // Since [e,d] is the newly created edge. Reject this flip. rejflag = 1; } - } else { - if ((dir == ACROSSVERT) || (dir == TOUCHEDGE) || - (dir == TOUCHFACE)) { - assert(0); // Check this case. - } - } - } // if (intflag == 4) - } // i - } else { // pc == dummypoint - // Do not flip if the new hull edge [e,d] will intersect this edge - // in its interior. - // Comment: Here we actually need a 3D edge-edge test. - // We only do test if the edge in 'fc' is coplanar with the plane - // containing a,b,e,and d. - // Choose a better triangle [a,b,e] or [a,b,d]. - facenormal(pa, pb, pe, normal, 1, &len); - area = sqrt(DOT(normal, normal)); - facenormal(pa, pb, pd, normal, 1, &len); - len = sqrt(DOT(normal, normal)); // Re-use len as area. - if (area > len) { - // Choose [a,b,e] - ori1 = orient3d(pa, pb, pe, fc->seg[0]); - ori2 = orient3d(pa, pb, pe, fc->seg[1]); - } else { - // Choose [a,b,d] - ori1 = orient3d(pa, pb, pd, fc->seg[0]); - ori2 = orient3d(pa, pb, pd, fc->seg[1]); - } - if ((ori1 == 0) && (ori2 == 0)) { - calculateabovepoint4(pa, pb, pe, pd); - for (i = 0; i < 3; i++) { - abovept[i] = dummypoint[i]; - } - intflag = tri_edge_test(pe, pd, abovept, fc->seg[0], fc->seg[1], - NULL, 1, types, poss); - if (intflag == 2) { - dir = (enum interresult) types[0]; - assert(dir != ACROSSFACE); - 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 ((dir == ACROSSVERT) || (dir == TOUCHEDGE) || - (dir == TOUCHFACE)) { - assert(0); // Check this case. - } - } - } else if (intflag == 4) { - // [e,d,abovept] is coplanar with the constraining edge 'fc'. - // This is poissible if the edge in 'fc' is just the edge [e,d] - // (SHAREEDGE) or they share a common vertex (SHAREVEER) - dir = (enum interresult) types[0]; - if (dir == ACROSSEDGE) { - // This case can only happen if [e,d] is coplanar with 'fc'. - assert(0); // Not possible. - } else { - if ((dir == ACROSSVERT) || (dir == TOUCHEDGE) || - (dir == TOUCHFACE)) { - assert(0); // Check this case. - } } } - } - } // if (pc == dummypoint) + } // 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) { - if (0) { - // Make sure that [a,b,c] is a valid face. - facenormal(pa, pb, pc, normal, 1, &len); - area = sqrt(DOT(normal, normal)); - if (area == 0) { - rejflag = 1; // A degenerate face. - } else { - if ((area / (len * len)) < b->epsilon) { - rejflag = 1; // A nearly degenerate face. - } + // 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. } - } - if (!rejflag) { - // 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 (dir == ACROSSEDGE) { - // This case is possible since we allow a previous 2-to-3 flip - // even it will create a degenerate tet at edge [a,b]. - } else { - if ((dir == ACROSSVERT) || (dir == TOUCHEDGE) || - (dir == TOUCHFACE)) { - assert(0); // Check this case. - } - } - } 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. - // An example is found in case 'camila.poly', during the recovery - // of segment [151, 161] (at linklevel = 2). See: 2011-06-10-a. - 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 (!relflag) - } else { // pc == dummypoint - // The flip 3-to-2 will replace [e,d] with a new hull edge [a,b]. - // Only do flip if [a,b] does not intersect the edge of 'fc'. - // Comment: Here we acutually need a 3D edge-edge intersection test. - // We only do test if the edge in 'fc' is coplanar with the plane - // containing a,b,e, and d. - // Choose a better triangle [a,b,e] or [a,b,d]. - facenormal(pa, pb, pe, normal, 1, &len); - area = sqrt(DOT(normal, normal)); - facenormal(pa, pb, pd, normal, 1, &len); - len = sqrt(DOT(normal, normal)); // Re-use len as area. - if (area > len) { - // Choose [a,b,e] - ori1 = orient3d(pa, pb, pe, fc->seg[0]); - ori2 = orient3d(pa, pb, pe, fc->seg[1]); - } else { - // Choose [a,b,d] - ori1 = orient3d(pa, pb, pd, fc->seg[0]); - ori2 = orient3d(pa, pb, pd, fc->seg[1]); } - if ((ori1 == 0) && (ori2 == 0)) { - // The edge in 'fc' is coplanar with the plane containing [a,b,e,d]. - calculateabovepoint4(pa, pb, pe, pd); - for (i = 0; i < 3; i++) { - abovept[i] = dummypoint[i]; - } - intflag = tri_edge_test(pa, pb, abovept, fc->seg[0], fc->seg[1], - NULL, 1, types, poss); - if (intflag == 2) { - dir = (enum interresult) types[0]; - assert(dir != ACROSSFACE); - if (dir == ACROSSEDGE) { - assert(0); // Check this case. - rejflag = 1; // Do not flip. - } else { - if ((dir == ACROSSVERT) || (dir == TOUCHEDGE) || - (dir == TOUCHFACE)) { - assert(0); // Check this case. - } - } - } else if (intflag == 4) { - // The edge 'fc' is coplanar with [a,b,abovept]. - // This is poissible if the edge in 'fc' is just the edge [a,b] - // (SHAREEDGE) or they share a common vertex (SHAREVEER) - dir = (enum interresult) types[0]; - if (dir == ACROSSEDGE) { - // This case can only happen if [a,b] is coplanar with 'fc'. - assert(0); // Not possible. - } else { - if ((dir == ACROSSVERT) || (dir == TOUCHEDGE) || - (dir == TOUCHFACE)) { - assert(0); // Check this case. - } - } - } - } // if (ori1 == 0 && ori2 == 0) - } - } else { - assert(0); // An unknown flip type. + } // if (pc != dummypoint) } } // if (fc->seg[0] != NULL) @@ -19978,11 +18337,7 @@ int tetgenmesh::checkflipeligibility(int fliptype, point pa, point pb, rejflag = 1; } else if (dir == ACROSSEDGE) { rejflag = 1; - } else { - if ((dir == ACROSSVERT) || (dir == TOUCHEDGE) || (dir == TOUCHFACE)) { - assert(0); // Check this case. - } - } + } } else if (intflag == 4) { // The edge [e,d] is coplanar with the face. // There may be two intersections. @@ -20123,10 +18478,10 @@ int tetgenmesh::checkflipeligibility(int fliptype, point pa, point pb, // // // removeedgebyflips() Remove an edge by flips. // // // -// 'flipedge' is a non-convex or flat edge [a,b,#,#]. // +// '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, othereise, the // +// 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. // // // @@ -20135,77 +18490,39 @@ int tetgenmesh::checkflipeligibility(int fliptype, point pa, point pb, int tetgenmesh::removeedgebyflips(triface *flipedge, flipconstraints* fc) { triface *abtets, spintet; - face checkseg, *paryseg; - int counter; // DEBUG + int t1ver; int n, nn, i; - // Bakup valuses (for free spaces in flipnm_post()). - int bakunflip = fc->unflip; - int bakcollectnewtets = fc->collectnewtets; - - - if (b->verbose > 2) { - printf(" Removing edge (%d, %d)\n", pointmark(org(*flipedge)), - pointmark(dest(*flipedge))); - } - - //if (fc != NULL) { - fc->clearcounters(); - //} - if (checksubsegflag) { // Do not flip a segment. - tsspivot1(*flipedge, checkseg); - if (checkseg.sh != NULL) { - if (b->verbose > 2) { - printf(" Can't flip a segment (%d, %d).\n", - pointmark(sorg(checkseg)), pointmark(sdest(checkseg))); - } - //if (fc != NULL) { - fc->encsegcount++; - if (fc->collectencsegflag) { - if (!sinfected(checkseg)) { - // Queue this segment in list. - sinfect(checkseg); - caveencseglist->newindex((void **) &paryseg); - *paryseg = checkseg; - } + 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; - counter = 0; // Sum of star counters; spintet = *flipedge; - i = 0; while (1) { - counter += elemcounter(spintet); - i++; + n++; fnextself(spintet); if (spintet.tet == flipedge->tet) break; } - //assert(i >= 3); - if (i < 3) { - // It is only possible when the mesh contains inverted tetrahedra. - assert(checkinverttetflag); - // Since "return 2" means success, we return 0. - return 0; - } - assert(counter == 0); // SELF_CHECK - n = i; + assert(n >= 3); - flipstarcount++; - // Record the maximum star size. - if (n > maxflipstarsize) { - maxflipstarsize = n; - } if ((b->flipstarsize > 0) && (n > b->flipstarsize)) { - // The star size exceeds the given limit (-YY__). - skpflipstarcount++; + // The star size exceeds the limit. return 0; // Do not flip it. } @@ -20216,8 +18533,7 @@ int tetgenmesh::removeedgebyflips(triface *flipedge, flipconstraints* fc) i = 0; while (1) { abtets[i] = spintet; - //marktest(abtets[i]); // Marktest it (in Star(ab)). - setelemcounter(abtets[i], 1); + setelemcounter(abtets[i], 1); i++; fnextself(spintet); if (spintet.tet == flipedge->tet) break; @@ -20228,45 +18544,33 @@ int tetgenmesh::removeedgebyflips(triface *flipedge, flipconstraints* fc) nn = flipnm(abtets, n, 0, 0, fc); - if (nn == 2) { - // Edge is flipped. - if (b->verbose > 2) { - printf(" Edge is removed.\n"); - } - } else { + if (nn > 2) { // Edge is not flipped. Unmarktest the remaining tets in Star(ab). for (i = 0; i < nn; i++) { - //assert(marktested(abtets[i])); - //unmarktest(abtets[i]); - assert(elemcounter(abtets[i]) == 1); setelemcounter(abtets[i], 0); } - if (b->verbose > 2) { - printf(" Edge is not removed. n(%d), nn(%d).\n", n, nn); - } // 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; - fc->collectnewtets = 0; - flipnm_post(abtets, n, nn, 0, fc); - fc->unflip = bakunflip; - fc->collectnewtets = bakcollectnewtets; delete [] abtets; - return nn; //return nn == 2; + 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. // // // @@ -20274,45 +18578,25 @@ int tetgenmesh::removeedgebyflips(triface *flipedge, flipconstraints* fc) int tetgenmesh::removefacebyflips(triface *flipface, flipconstraints* fc) { - triface fliptets[3], flipedge; - face checksh; - point pa, pb, pc, pd, pe; - point pts[3]; - enum interresult dir; - int types[2], poss[4], pos; - REAL ori; - int reducflag, rejflag; - int i, j; - if (checksubfaceflag) { - tspivot(*flipface, checksh); - if (checksh.sh != NULL) { - if (b->verbose > 2) { - printf(" Can't flip a subface.\n"); - } + 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]); - - assert(!ishulltet(fliptets[0])); - assert(!ishulltet(fliptets[1])); - pa = org(fliptets[0]); pb = dest(fliptets[0]); pc = apex(fliptets[0]); pd = oppo(fliptets[0]); pe = oppo(fliptets[1]); - if (b->verbose > 2) { - printf(" Removing face (%d, %d, %d) -- %d, %d\n", pointmark(pa), - pointmark(pb), pointmark(pc), pointmark(pd), pointmark(pe)); - } - - reducflag = 0; - ori = orient3d(pa, pb, pd, pe); if (ori > 0) { ori = orient3d(pb, pc, pd, pe); @@ -20333,109 +18617,12 @@ int tetgenmesh::removefacebyflips(triface *flipface, flipconstraints* fc) if (reducflag) { // A 2-to-3 flip is found. - rejflag = 0; - if (fc != NULL) { - //rejflag = checkflipeligibility(1, pa, pb, pc, pd, pe, fc); - } - if (!rejflag) { - flip23(fliptets, 0, 0, 0); - if (b->verbose > 2) { - printf(" Face is removed by a 2-to-3 flip.\n"); - } - return 1; - } else { - if (b->verbose > 2) { - printf(" -- Reject a 2-to-3 flip at face (%d, %d, %d)\n", - pointmark(pa), pointmark(pb), pointmark(pc)); - } - if (fc != NULL) { - fc->rejf23count++; - } - } + flip23(fliptets, 0, fc); + return 1; } else { - if (0) { - // Try to flip one of the edges of this face. - pts[0] = org(flipedge); - pts[1] = dest(flipedge); - pts[2] = apex(flipedge); - // Start from the recorded locally non-convex edge 'flipedge'. - for (i = 0; i < 3; i++) { - if (removeedgebyflips(&flipedge, fc) == 2) { - if (b->verbose > 2) { - printf(" Face is removed by removing edge (%d, %d).\n", - pointmark(pts[i]), pointmark(pts[(i+1)%3])); - } - return 1; - } - // The 'flipedge' may be dead in above call. - point2tetorg(pts[i], flipedge); - finddirection(&flipedge, pts[(i+1)%3], 1); - if (dest(flipedge) != pts[(i+1)%3]) { - if (b->verbose > 2) { - printf(" Face is removed during removing edge (%d, %d).\n", - pointmark(pts[i]), pointmark(pts[(i+1)%3])); - } - return 1; - } - } // i - } else { - if (0) { //if (fc->seg[0] != NULL) { - // The face is intersecting a segment. - // Find the edge shared by three corssing faces. - // We assume that the 'flipface' is facing to 'fc->seg[0]'. It is the - // case when the function is called from 'recoveredgebyflips()'. - // DEBUG BEGIN - pa = org(*flipface); - pb = dest(*flipface); - pc = apex(*flipface); - ori = orient3d(pa, pb, pc, fc->seg[0]); - assert(ori < 0); - // DEBUG END - fsym(*flipface, flipedge); - pc = oppo(flipedge); - for (i = 0; i < 3; i++) { - pa = org(flipedge); - pb = dest(flipedge); - if (tri_edge_test(pa, pb, pc, fc->seg[0], fc->seg[1], NULL, 1, - types, poss)) { - dir = (enum interresult) types[0]; - if (dir == ACROSSFACE) { - break; // Found the crossing face. - } else if (dir == ACROSSEDGE) { - // Found an edge intersects the segment. - esymself(flipedge); - pos = poss[0]; - for (j = 0; j < pos; j++) { - eprevself(flipedge); - } - // Flip this edge. - break; - } else if (dir == SHAREVERT) { - // We have reached the endpoint of the segment. - assert(pc == fc->seg[1]); - // The face is not flippable. - return 0; - } else { - assert(0); // Not possible. - } - } - enextself(flipedge); - } - assert(i < 3); - } else { - if (b->verbose > 2) { - pa = org(flipedge); - pb = dest(flipedge); - } - } - // Try to flip the selected edge of this face. - if (removeedgebyflips(&flipedge, fc) == 2) { - if (b->verbose > 2) { - printf(" Face is removed by removing edge (%d, %d).\n", - pointmark(pa), pointmark(pb)); - } - return 1; - } + // Try to flip the selected edge of this face. + if (removeedgebyflips(&flipedge, fc) == 2) { + return 1; } } @@ -20450,7 +18637,7 @@ int tetgenmesh::removefacebyflips(triface *flipface, flipconstraints* fc) // 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 flipped. // +// 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 // @@ -20461,38 +18648,24 @@ int tetgenmesh::removefacebyflips(triface *flipface, flipconstraints* fc) int tetgenmesh::recoveredgebyflips(point startpt, point endpt, triface* searchtet, int fullsearch) { - triface neightet, spintet; // *abtets; - point pa, pb, pc, pd; - badface bakface; - enum interresult dir, dir1; flipconstraints fc; - int types[2], poss[4], pos = 0; - int success; - //int n, endi; - int i, j; //, k; - - if (b->verbose > 2) { - printf(" Recovering edge (%d, %d)\n", pointmark(startpt), - pointmark(endpt)); - } - + 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); - assert(org(*searchtet) == startpt); // SELF_CHECK - dir = finddirection(searchtet, endpt, 1); + dir = finddirection(searchtet, endpt); if (dir == ACROSSVERT) { if (dest(*searchtet) == endpt) { return 1; // Edge is recovered. } else { - // A PLC problem, or there is a Steiner point. - terminatetetgen(3); //assert(0); // Debug + terminatetetgen(this, 3); // // It may be a PLC problem. } } @@ -20511,175 +18684,178 @@ int tetgenmesh::recoveredgebyflips(point startpt, point endpt, continue; } } else { - terminatetetgen(3); //assert(0); // A PLC problem. + terminatetetgen(this, 3); // It may be a PLC problem. } // The edge is missing. if (fullsearch) { - - if (1) { - // Try to flip one of the faces/edges which intersects the edge. - success = 0; - - // Loop through the sequence of intersecting faces/edges from - // 'startpt' to 'endpt'. - point2tetorg(startpt, *searchtet); - assert(org(*searchtet) == startpt); // SELF_CHECK - dir = finddirection(searchtet, endpt, 1); - 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); + // 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)) { + if (tri_edge_test(pa,pb,pc,startpt,endpt,pd,1, types, poss)) { dir = (enum interresult) types[0]; pos = poss[0]; - break; + break; // for loop } 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 - } + } // 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); - } + // 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); - } + 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; + // 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); + // 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, 1); - 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) { + // 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. } - } // 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 destroed. - break; // Loop I-I } + } 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 (0) + } // while (1) // Loop I-I + + if (success) { + // One of intersecting faces/edges is flipped. + continue; + } } // if (fullsearch) @@ -20688,7 +18864,6 @@ int tetgenmesh::recoveredgebyflips(point startpt, point endpt, } // while (1) // Loop I - // The edge is not recovered. return 0; } @@ -20701,24 +18876,8 @@ int tetgenmesh::recoveredgebyflips(point startpt, point endpt, // 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]. // -// // -// These set of tets arises when we want to recover an edge from 'p0' to 'p_ // -// (n-1)', and the recoveredgenbyflips() routine fails. Note that the outer // -// faces of these tets defines a polyhedron P, and the set of tets gives the // -// ONLY tetrahedralization of P. If we replace the two boundary faces [a,b, // -// p0] and [a,b,p_(n-1)] by [p0,p_(n-1),a] and [p0,p_(n-1),b], and call the // -// new polyhedron P'. In this routine, we think P' is not tetrahedralizable // -// (since the routine recoveredgenbyflips() fails!! AND no flip is possible // -// on any of these edges: [a,p1], [b,p1], [a,p2], [b,p2], ..., [a,p_(n-2)], // -// and [b,p_(n-1)]). If n = 3, P' is just the famous Schoenhardt polyhedron. // -// For n > 3, we call P' the generalized Schoenhardt polyhedron, it includes // -// the Bagemihl's polyhedron as a special case. // -// // -// It is obvious that P is a Star-shaped polyhedron. The mid-point of [a,b] // -// is visible by all boundary faces of P, push it slightly inside P does not // -// change the visibilty. Indeed every interior point of [a,b] is visible by // -// the boundary faces of P. // -// // +// 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. // // // /////////////////////////////////////////////////////////////////////////////// @@ -20727,55 +18886,46 @@ int tetgenmesh::add_steinerpt_in_schoenhardtpoly(triface *abtets, int n, { triface worktet, *parytet; triface faketet1, faketet2; - point pa, pb, pc, pd; - point p1, p2, p3; - point steinerpt; + 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 loc; int it, i; - if (b->verbose > 2) { - printf(" Find a Steiner in Schoenhardt polyhedron (n=%d).\n", n); - } - pa = org(abtets[0]); - pb = dest(abtets[0]); 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++) { - eprev(abtets[i], worktet); - esymself(worktet); // [a,p_i,p_i+1]. + edestoppo(abtets[i], worktet); // [p_i,p_i+1,a] cavetetlist->newindex((void **) &parytet); *parytet = worktet; - enext(abtets[i], worktet); - esymself(worktet); // [p_i,b,p_i+1]. + 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 100 points in edge [c,d]. - for (it = 1; it < 100; it++) { + // Sample N points in edge [c,d]. + for (it = 1; it < N; it++) { for (i = 0; i < 3; i++) { - sampt[i] = pc[i] + (0.01 * (double) it) * vcd[i]; + sampt[i] = pc[i] + (stepi * (double) it) * vcd[i]; } for (i = 0; i < cavetetlist->objects; i++) { parytet = (triface *) fastlookup(cavetetlist, i); - p1 = org(*parytet); - p2 = dest(*parytet); - p3 = apex(*parytet); - ori = orient3d(p2, p1, p3, sampt); + ori = orient3d(dest(*parytet), org(*parytet), apex(*parytet), sampt); if (i == 0) { minvol = ori; } else { @@ -20794,26 +18944,22 @@ int tetgenmesh::add_steinerpt_in_schoenhardtpoly(triface *abtets, int n, } // it if (maxminvol <= 0) { - if (b->verbose > 2) { - printf(" Unable to find a initial point: maxminvol = %g\n", - maxminvol); - } cavetetlist->restart(); return 0; } for (i = 0; i < 3; i++) { - smtpt[i] = pc[i] + (0.01 * (double) maxidx) * vcd[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, pa, dummypoint); + setvertices(faketet1, pd, pc, org(abtets[0]), dummypoint); cavetetlist->newindex((void **) &parytet); *parytet = faketet1; maketetrahedron(&faketet2); - setvertices(faketet2, pc, pd, pb, dummypoint); + setvertices(faketet2, pc, pd, dest(abtets[0]), dummypoint); cavetetlist->newindex((void **) &parytet); *parytet = faketet2; @@ -20846,9 +18992,6 @@ int tetgenmesh::add_steinerpt_in_schoenhardtpoly(triface *abtets, int n, cavetetlist->restart(); if (!success) { - if (b->verbose > 2) { - printf(" Unable to relocate the initial point.\n"); - } return 0; } @@ -20865,36 +19008,205 @@ int tetgenmesh::add_steinerpt_in_schoenhardtpoly(triface *abtets, int n, } worktet = abtets[0]; // No need point location. ivf.iloc = (int) INSTAR; - ivf.bowywat = 0; // Do not use Bowyer-Watson algorithm. - ivf.lawson = 0; // Do not flip. - ivf.rejflag = 0; ivf.chkencflag = chkencflag; - ivf.sloc = 0; - ivf.sbowywat = 0; - ivf.splitbdflag = 0; - ivf.validflag = 0; - ivf.respectbdflag = 0; - ivf.assignmeshsize = 0; + 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). - loc = insertvertex(steinerpt, &worktet, NULL, NULL, &ivf); - - if (loc == (int) INSTAR) { + if (insertpoint(steinerpt, &worktet, NULL, NULL, &ivf)) { // The vertex has been inserted. - st_volref_count++; //st_inpoly_count++; + st_volref_count++; if (steinerleft > 0) steinerleft--; return 1; } else { - // The Steiner point is too close to an existing vertex. Reject it. + // Not inserted. + pointdealloc(steinerpt); + return 0; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// add_steinerpt_in_segment() Add a Steiner point inside a segment. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::add_steinerpt_in_segment(face* misseg, int searchlevel) +{ + triface searchtet; + face *paryseg, candseg; + point startpt, endpt, pc, pd; + flipconstraints fc; + enum interresult dir; + REAL P[3], Q[3], tp, tq; + REAL len, smlen = 0, split = 0, split_q = 0; + int success; + int i; + + startpt = sorg(*misseg); + endpt = sdest(*misseg); + + fc.seg[0] = startpt; + fc.seg[1] = endpt; + fc.checkflipeligibility = 1; + fc.collectencsegflag = 1; + + point2tetorg(startpt, searchtet); + dir = finddirection(&searchtet, endpt); + //assert(dir != ACROSSVERT); + + // Try to flip the first intersecting face/edge. + enextesymself(searchtet); // Go to the opposite face. + + int bak_fliplinklevel = b->fliplinklevel; + b->fliplinklevel = searchlevel; + + if (dir == ACROSSFACE) { + // A face is intersected with the segment. Try to flip it. + success = removefacebyflips(&searchtet, &fc); + assert(success == 0); + } else if (dir == ACROSSEDGE) { + // An edge is intersected with the segment. Try to flip it. + success = removeedgebyflips(&searchtet, &fc); + assert(success != 2); + } else { + terminatetetgen(this, 3); // It may be a PLC problem. + } + + split = 0; + for (i = 0; i < caveencseglist->objects; i++) { + paryseg = (face *) fastlookup(caveencseglist, i); + suninfect(*paryseg); + // Calculate the shortest edge between the two lines. + pc = sorg(*paryseg); + pd = sdest(*paryseg); + tp = tq = 0; + if (linelineint(startpt, endpt, pc, pd, P, Q, &tp, &tq)) { + // Does the shortest edge lie between the two segments? + // Round tp and tq. + if ((tp > 0) && (tq < 1)) { + if (tp < 0.5) { + if (tp < (b->epsilon * 1e+3)) tp = 0.0; + } else { + if ((1.0 - tp) < (b->epsilon * 1e+3)) tp = 1.0; + } + } + if ((tp <= 0) || (tp >= 1)) continue; + if ((tq > 0) && (tq < 1)) { + if (tq < 0.5) { + if (tq < (b->epsilon * 1e+3)) tq = 0.0; + } else { + if ((1.0 - tq) < (b->epsilon * 1e+3)) tq = 1.0; + } + } + if ((tq <= 0) || (tq >= 1)) continue; + // It is a valid shortest edge. Calculate its length. + len = distance(P, Q); + if (split == 0) { + smlen = len; + split = tp; + split_q = tq; + candseg = *paryseg; + } else { + if (len < smlen) { + smlen = len; + split = tp; + split_q = tq; + candseg = *paryseg; + } + } + } + } + + caveencseglist->restart(); + b->fliplinklevel = bak_fliplinklevel; + + if (split == 0) { + // Found no crossing segment. + return 0; + } + + face splitsh; + face splitseg; + point steinerpt, *parypt; + insertvertexflags ivf; + + if (b->addsteiner_algo == 1) { + // Split the segment at the closest point to a near segment. + makepoint(&steinerpt, FREESEGVERTEX); + for (i = 0; i < 3; i++) { + steinerpt[i] = startpt[i] + split * (endpt[i] - startpt[i]); + } + } else { // b->addsteiner_algo == 2 + for (i = 0; i < 3; i++) { + P[i] = startpt[i] + split * (endpt[i] - startpt[i]); + } + pc = sorg(candseg); + pd = sdest(candseg); + for (i = 0; i < 3; i++) { + Q[i] = pc[i] + split_q * (pd[i] - pc[i]); + } + makepoint(&steinerpt, FREEVOLVERTEX); + for (i = 0; i < 3; i++) { + steinerpt[i] = 0.5 * (P[i] + Q[i]); + } + } + + // We need to locate the point. Start searching from 'searchtet'. + if (split < 0.5) { + point2tetorg(startpt, searchtet); + } else { + point2tetorg(endpt, searchtet); + } + if (b->addsteiner_algo == 1) { + splitseg = *misseg; + spivot(*misseg, splitsh); + } else { + splitsh.sh = NULL; + splitseg.sh = NULL; + } + ivf.iloc = (int) OUTSIDE; + ivf.bowywat = 1; + ivf.lawson = 0; + ivf.rejflag = 0; + ivf.chkencflag = 0; + ivf.sloc = (int) ONEDGE; + ivf.sbowywat = 1; + ivf.splitbdflag = 0; + ivf.validflag = 1; + ivf.respectbdflag = 1; + ivf.assignmeshsize = b->metric; + + if (!insertpoint(steinerpt, &searchtet, &splitsh, &splitseg, &ivf)) { pointdealloc(steinerpt); return 0; } + + if (b->addsteiner_algo == 1) { + // Save this Steiner point (for removal). + // Re-use the array 'subvertstack'. + subvertstack->newindex((void **) &parypt); + *parypt = steinerpt; + st_segref_count++; + } else { // b->addsteiner_algo == 2 + // Queue the segment for recovery. + subsegstack->newindex((void **) &paryseg); + *paryseg = *misseg; + st_volref_count++; + } + if (steinerleft > 0) steinerleft--; + + return 1; } /////////////////////////////////////////////////////////////////////////////// // // -// addsteiner4recoversegment() Add a Steiner point for recoveing a seg. // +// addsteiner4recoversegment() Add a Steiner point for recovering a seg. // // // /////////////////////////////////////////////////////////////////////////////// @@ -20902,16 +19214,14 @@ int tetgenmesh::addsteiner4recoversegment(face* misseg, int splitsegflag) { triface *abtets, searchtet, spintet; face splitsh; - face checkseg; face *paryseg; point startpt, endpt; point pa, pb, pd, steinerpt, *parypt; enum interresult dir; insertvertexflags ivf; int types[2], poss[4]; - REAL ip[3], u; int n, endi, success; - int loc; + int t1ver; int i; startpt = sorg(*misseg); @@ -20923,12 +19233,7 @@ int tetgenmesh::addsteiner4recoversegment(face* misseg, int splitsegflag) // Try to recover the edge by adding Steiner points. point2tetorg(startpt, searchtet); - assert(org(searchtet) == startpt); // SELF_CHECK - dir = finddirection(&searchtet, endpt, 1); - assert(dir != ACROSSVERT); - - // Get the first intersecting face/edge. - assert(!ishulltet(searchtet)); + dir = finddirection(&searchtet, endpt); enextself(searchtet); //assert(apex(searchtet) == startpt); @@ -20936,13 +19241,8 @@ int tetgenmesh::addsteiner4recoversegment(face* misseg, int splitsegflag) // The segment is crossing at least 3 faces. Find the common edge of // the first 3 crossing faces. esymself(searchtet); - assert(oppo(searchtet) == startpt); fsym(searchtet, spintet); pd = oppo(spintet); - if (pd == endpt) { - // This should be possible. - assert(0); // Debug this case. - } for (i = 0; i < 3; i++) { pa = org(spintet); pb = dest(spintet); @@ -20958,8 +19258,9 @@ int tetgenmesh::addsteiner4recoversegment(face* misseg, int splitsegflag) } else { assert(dir == ACROSSEDGE); // PLC check. - tsspivot1(searchtet, checkseg); - if (checkseg.sh != NULL) { + if (issubseg(searchtet)) { + face checkseg; + tsspivot1(searchtet, checkseg); printf("Found two segments intersect each other.\n"); pa = farsorg(*misseg); pb = farsdest(*misseg); @@ -20969,7 +19270,7 @@ int tetgenmesh::addsteiner4recoversegment(face* misseg, int splitsegflag) pb = farsdest(checkseg); printf(" 2nd: [%d,%d] %d.\n", pointmark(pa), pointmark(pb), shellmark(checkseg)); - terminatetetgen(3); + terminatetetgen(this, 3); } } assert(apex(searchtet) == startpt); @@ -20996,8 +19297,6 @@ int tetgenmesh::addsteiner4recoversegment(face* misseg, int splitsegflag) abtets[i] = spintet; fnextself(spintet); } - assert(apex(abtets[0]) == startpt); - assert(apex(abtets[endi]) == endpt); success = 0; @@ -21030,10 +19329,10 @@ int tetgenmesh::addsteiner4recoversegment(face* misseg, int splitsegflag) // However, there will be invalid tets (either zero or negtive // volume). Otherwise, [c,d] should already be recovered by the // recoveredge() function. - assert(0); // DEBUG IT + terminatetetgen(this, 2); // Report a bug. } } else { - assert(0); // A PLC problem. + terminatetetgen(this, 10); // A PLC problem. } delete [] abtets; @@ -21054,98 +19353,21 @@ int tetgenmesh::addsteiner4recoversegment(face* misseg, int splitsegflag) printf(" Splitting segment (%d, %d)\n", pointmark(startpt), pointmark(endpt)); } + steinerpt = NULL; - if (endi == -1) { - // Let the missing segment be [a,b]. Let the edge [c,d] whose star contains - // a and intersects [a,b]. We choose the Steiner point at the intersection - // of the edge star of [c,d] and [a,b] (not a). - if (dir == ACROSSFACE) { - pa = org(searchtet); - pb = dest(searchtet); - - spintet = searchtet; - n = 0; endi = -1; - while (1) { - n++; // Count a tet in the star. - fnextself(spintet); - if (spintet.tet == searchtet.tet) break; - // Check if the segment leaves the edge star. - pd = apex(spintet); - assert(pd != endpt); - if (!tri_edge_test(pa, pb, pd, startpt, endpt, NULL, 1, types, poss)) { - if (endi == -1) endi = (n - 1); - } - } - assert(n >= 3); - assert(endi != -1); - - // 'abtets' is only for debug purpose. - abtets = new triface[endi]; - spintet = searchtet; - for (i = 0; i < endi; i++) { - abtets[i] = spintet; - fnextself(spintet); - } - searchtet = abtets[endi - 1]; - esymself(searchtet); // The exit face of [startpt, endpt]. - delete [] abtets; - } else { - assert(dir == ACROSSEDGE); - assert(apex(searchtet) == startpt); - esymself(searchtet); // The exit face of [startpt, endpt]. - //assert(oppo(searchtet) == startpt); - pa = org(searchtet); - pb = dest(searchtet); - } - - pd = apex(searchtet); - // Get the intersection type (ACROSSFACE or ACROSSEDGE). - if (tri_edge_test(pa, pb, pd, startpt, endpt, NULL, 1, types, poss)) { - dir = (enum interresult) types[0]; - assert((dir == ACROSSFACE) || (dir == ACROSSEDGE)); - } else { - assert(0); // not possible. + 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); + } - // Calculate the intersection of the face [a,b,d] and the segment. - planelineint(pa, pb, pd, startpt, endpt, ip, &u); - assert((u > 0) && (u < 1)); - - // Create a Steiner point. - makepoint(&steinerpt, FREESEGVERTEX); - for (i = 0; i < 3; i++) steinerpt[i] = ip[i]; - spivot(*misseg, splitsh); - if (dir == ACROSSFACE) { - ivf.iloc = (int) ONFACE; - } else { - ivf.iloc = (int) ONEDGE; - } - 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 = 0; - loc = insertvertex(steinerpt, &searchtet, &splitsh, misseg, &ivf); - - if (loc != ivf.iloc) { - if (loc == (int) NEARVERTEX) { - // The vertex is rejected. Too close to an existing vertex. - pointdealloc(steinerpt); - steinerpt = NULL; - } else { - assert(0); // Unknown case. - } - } - } else { // if (endi > 0) - steinerpt = NULL; - } if (steinerpt == NULL) { // Split the segment at its midpoint. @@ -21167,11 +19389,10 @@ int tetgenmesh::addsteiner4recoversegment(face* misseg, int splitsegflag) ivf.splitbdflag = 0; ivf.validflag = 1; ivf.respectbdflag = 1; - ivf.assignmeshsize = 0; - loc = insertvertex(steinerpt, &searchtet, &splitsh, misseg, &ivf); - - assert(loc != (int) ONVERTEX); - assert(loc != (int) NEARVERTEX); + ivf.assignmeshsize = b->metric; + if (!insertpoint(steinerpt, &searchtet, &splitsh, misseg, &ivf)) { + assert(0); + } } // if (endi > 0) // Save this Steiner point (for removal). @@ -21189,17 +19410,11 @@ int tetgenmesh::addsteiner4recoversegment(face* misseg, int splitsegflag) // // // recoversegments() Recover all segments. // // // -// All segments need to be recovered are in 'subsegstack' (Q). They will be // -// be recovered one by one. // -// // -// Each segment is first tried to be recovered by a sequence of flips which // -// removes faces intersecting this segment. However, it is not always possi- // -// ble to recover it by only this way. Then, Steiner points will be added to // -// help the recovery of it by flips. // +// All segments need to be recovered are in 'subsegstack'. // // // -// If 'steinerflag' is set, Steiner points will be added if a segment is not // -// able to recovered by flips. Otherwise, the segment is not recovered, and // -// it is returned in 'misseglist'. // +// 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. // // // /////////////////////////////////////////////////////////////////////////////// @@ -21207,11 +19422,12 @@ int tetgenmesh::recoversegments(arraypool *misseglist, int fullsearch, int steinerflag) { triface searchtet, spintet; - face sseg, checkseg, *paryseg; + face sseg, *paryseg; point startpt, endpt; int success; - - long bak_inpoly_count = st_volref_count; //st_inpoly_count; + 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", @@ -21260,8 +19476,6 @@ int tetgenmesh::recoversegments(arraypool *misseglist, int fullsearch, if (success) { // Segment is recovered. Insert it. - tsspivot1(searchtet, checkseg); // SELF_CHECK - assert(checkseg.sh == NULL); // Let the segment remember an adjacent tet. sstbond1(sseg, searchtet); // Bond the segment to all tets containing it. @@ -21300,6 +19514,10 @@ int tetgenmesh::recoversegments(arraypool *misseglist, int fullsearch, 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); + } } } @@ -21319,33 +19537,26 @@ int tetgenmesh::recoverfacebyflips(point pa, point pb, point pc, face *searchsh, triface* searchtet) { triface spintet, flipedge; - face checkseg; point pd, pe; enum interresult dir; flipconstraints fc; + int types[2], poss[4], intflag; int success, success1; + int t1ver; int i, j; - int intflag; - int types[2], poss[4]; - - if (b->verbose > 2) { - printf(" Recovering face (%d, %d, %d) by flips\n", pointmark(pa), - pointmark(pb), pointmark(pc)); - } - 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); - assert(org(*searchtet) == fc.fac[i]); // SELF_CHECK - dir = finddirection(searchtet, fc.fac[(i+1)%3], 1); + 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] @@ -21382,14 +19593,13 @@ int tetgenmesh::recoverfacebyflips(point pa, point pb, point pc, dir = (enum interresult) types[0]; if ((dir == ACROSSFACE) || (dir == ACROSSEDGE)) { // Go to the edge [d,e]. - eprev(spintet, flipedge); - esymself(flipedge); - enextself(flipedge); // [d,e,a,b]. + edestoppo(spintet, flipedge); // [d,e,a,b] if (searchsh != NULL) { // Check if [e,d] is a segment. - tsspivot1(flipedge, checkseg); - if (checkseg.sh != NULL) { - if (!b->quiet) { + 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); @@ -21397,16 +19607,16 @@ int tetgenmesh::recoverfacebyflips(point pa, point pb, point pc, pointmark(pe), shellmark(checkseg)); printf(" 2nd: [%d,%d,%d] %d\n", pointmark(pa), pointmark(pb), pointmark(pc), shellmark(*searchsh)); - } - terminatetetgen(3); - } + } + terminatetetgen(this, 3); + } } // Try to flip the edge [d,e]. success1 = (removeedgebyflips(&flipedge, &fc) == 2); } else { if (dir == TOUCHFACE) { point touchpt, *parypt; - if (poss[0] == 0) { + if (poss[1] == 0) { touchpt = pd; // pd is a coplanar vertex. } else { touchpt = pe; // pe is a coplanar vertex. @@ -21414,17 +19624,11 @@ int tetgenmesh::recoverfacebyflips(point pa, point pb, point pc, if (pointtype(touchpt) == FREEVOLVERTEX) { // A volume Steiner point was added in this subface. // Split this subface by this point. - if (b->verbose > 2) { - printf(" Shift volume Steiner point %d to facet.\n", - pointmark(touchpt)); - } face checksh, *parysh; int siloc = (int) ONFACE; int sbowat = 0; // Only split this subface. - - sinsertvertex(touchpt, searchsh, NULL, siloc, sbowat); - setpointtype(touchpt, FREEFACETVERTEX); + sinsertvertex(touchpt, searchsh, NULL, siloc, sbowat, 0); st_volref_count--; st_facref_count++; // Queue this vertex for removal. @@ -21438,12 +19642,6 @@ int tetgenmesh::recoverfacebyflips(point pa, point pb, point pc, spivot(*parysh, checksh); // The new subface [a, b, p]. // Do not recover a deleted new face (degenerated). if (checksh.sh[3] != NULL) { - if (b->verbose > 3) { - printf(" Queue new subface (%d, %d, %d).\n", - pointmark(sorg(checksh)), pointmark(sdest(checksh)), - pointmark(sapex(checksh))); - } - //sdissolve(checksh); // It has not been connected yet. subfacstack->newindex((void **) &parysh); *parysh = checksh; } @@ -21469,7 +19667,7 @@ int tetgenmesh::recoverfacebyflips(point pa, point pb, point pc, } else if (pointtype(touchpt) == FREEFACETVERTEX) { // Two facets self-intersect. } - terminatetetgen(3); + terminatetetgen(this, 3); } } else { assert(0); // Unknown cases. Debug. @@ -21502,13 +19700,13 @@ int tetgenmesh::recoversubfaces(arraypool *misshlist, int steinerflag) { triface searchtet, neightet, spintet; face searchsh, neighsh, neineish, *parysh; - face bdsegs[3], checkseg; + face bdsegs[3]; point startpt, endpt, apexpt, *parypt; point steinerpt; enum interresult dir; insertvertexflags ivf; int success; - int loc; + int t1ver; int i, j; if (b->verbose > 1) { @@ -21532,7 +19730,7 @@ int tetgenmesh::recoversubfaces(arraypool *misshlist, int steinerflag) if (b->verbose > 2) { - printf(" Recover subface (%d, %d, %d).\n", pointmark(sorg(searchsh)), + printf(" Recover subface (%d, %d, %d).\n",pointmark(sorg(searchsh)), pointmark(sdest(searchsh)), pointmark(sapex(searchsh))); } @@ -21552,14 +19750,13 @@ int tetgenmesh::recoversubfaces(arraypool *misshlist, int steinerflag) startpt = sorg(searchsh); endpt = sdest(searchsh); point2tetorg(startpt, searchtet); - assert(org(searchtet) == startpt); // SELF_CHECK - dir = finddirection(&searchtet, endpt, 1); + dir = finddirection(&searchtet, endpt); if (dir == ACROSSVERT) { if (dest(searchtet) == endpt) { success = 1; } else { //assert(0); // A PLC problem. - terminatetetgen(3); + terminatetetgen(this, 3); } } else { // The edge is missing. Try to recover it. @@ -21573,13 +19770,8 @@ int tetgenmesh::recoversubfaces(arraypool *misshlist, int steinerflag) } if (success) { // Insert a temporary segment to protect this edge. - if (b->verbose > 2) { - printf(" Insert a temp segment to protect edge [%d, %d].\n", - pointmark(startpt), pointmark(endpt)); - } makeshellface(subsegs, &(bdsegs[i])); setshvertices(bdsegs[i], startpt, endpt, NULL); - //setshellmark(bdsegs[i], -2); // It's a temporary segment. smarktest2(bdsegs[i]); // It's a temporary segment. // Insert this segment into surface mesh. ssbond(searchsh, bdsegs[i]); @@ -21588,8 +19780,6 @@ int tetgenmesh::recoversubfaces(arraypool *misshlist, int steinerflag) ssbond(neighsh, bdsegs[i]); } // Insert this segment into tetrahedralization. - tsspivot1(searchtet, checkseg); // SELF_CHECK - assert(checkseg.sh == NULL); sstbond1(bdsegs[i], searchtet); // Bond the segment to all tets containing it. spintet = searchtet; @@ -21601,11 +19791,7 @@ int tetgenmesh::recoversubfaces(arraypool *misshlist, int steinerflag) // 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])) { // if (shellmark(bdsegs[j]) == -2) { - if (b->verbose > 2) { - printf(" Remove a temp segment (%d, %d).\n", - pointmark(sorg(bdsegs[j])), pointmark(sdest(bdsegs[j]))); - } + if (smarktest2ed(bdsegs[j])) { spivot(bdsegs[j], neineish); assert(neineish.sh != NULL); //if (neineish.sh != NULL) { @@ -21617,7 +19803,7 @@ int tetgenmesh::recoversubfaces(arraypool *misshlist, int steinerflag) spivotself(neighsh); // SELF_CHECK assert(neighsh.sh == neineish.sh); } - //} + //} sstpivot1(bdsegs[j], searchtet); assert(searchtet.tet != NULL); //if (searchtet.tet != NULL) { @@ -21627,7 +19813,7 @@ int tetgenmesh::recoversubfaces(arraypool *misshlist, int steinerflag) fnextself(spintet); if (spintet.tet == searchtet.tet) break; } - //} + //} shellfacedealloc(subsegs, bdsegs[j].sh); } } // j @@ -21653,10 +19839,10 @@ int tetgenmesh::recoversubfaces(arraypool *misshlist, int steinerflag) ivf.splitbdflag = 0; ivf.validflag = 1; ivf.respectbdflag = 1; - ivf.assignmeshsize = 0; - loc = insertvertex(steinerpt, &searchtet, &searchsh, NULL, &ivf); - assert(loc != (int) OUTSIDE); - + 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); @@ -21681,11 +19867,7 @@ int tetgenmesh::recoversubfaces(arraypool *misshlist, int steinerflag) // Delete any temporary segment that has been created. for (j = 0; j < 3; j++) { - if (smarktest2ed(bdsegs[j])) { //if (shellmark(bdsegs[j]) == -2) { - if (b->verbose > 2) { - printf(" Remove a temp segment (%d, %d).\n", - pointmark(sorg(bdsegs[j])), pointmark(sdest(bdsegs[j]))); - } + if (smarktest2ed(bdsegs[j])) { spivot(bdsegs[j], neineish); assert(neineish.sh != NULL); //if (neineish.sh != NULL) { @@ -21697,7 +19879,7 @@ int tetgenmesh::recoversubfaces(arraypool *misshlist, int steinerflag) spivotself(neighsh); // SELF_CHECK assert(neighsh.sh == neineish.sh); } - //} + //} sstpivot1(bdsegs[j], neightet); assert(neightet.tet != NULL); //if (neightet.tet != NULL) { @@ -21707,7 +19889,7 @@ int tetgenmesh::recoversubfaces(arraypool *misshlist, int steinerflag) fnextself(spintet); if (spintet.tet == neightet.tet) break; } - //} + //} shellfacedealloc(subsegs, bdsegs[j].sh); } } // j @@ -21743,10 +19925,10 @@ int tetgenmesh::recoversubfaces(arraypool *misshlist, int steinerflag) ivf.splitbdflag = 0; ivf.validflag = 1; ivf.respectbdflag = 1; - ivf.assignmeshsize = 0; - loc = insertvertex(steinerpt, &searchtet, &searchsh, NULL, &ivf); - assert(loc != (int) OUTSIDE); - + 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); @@ -21762,11 +19944,6 @@ int tetgenmesh::recoversubfaces(arraypool *misshlist, int steinerflag) if (!success) { if (misshlist != NULL) { - if (b->verbose > 2) { - printf(" Subface (%d, %d, %d) is missing.\n", - pointmark(sorg(searchsh)), pointmark(sdest(searchsh)), - pointmark(sapex(searchsh))); - } // Save this subface. misshlist->newindex((void **) &parysh); *parysh = searchsh; @@ -21786,7 +19963,7 @@ int tetgenmesh::recoversubfaces(arraypool *misshlist, int steinerflag) // 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 oppsiting to 'searchpt'. // +// Every tet in 'tetlist' is at the face opposing to 'searchpt'. // // // // 'vertlist' returns the list of vertices in the star (exclude 'searchpt'). // // // @@ -21800,54 +19977,48 @@ int tetgenmesh::getvertexstar(int fullstar, point searchpt, arraypool* tetlist, { triface searchtet, neightet, *parytet; face checksh, *parysh; - //face checkseg; point pt, *parypt; int collectflag; + int t1ver; int i, j; - if (b->verbose > 2) { - printf(" Form the star of vertex %d.\n", pointmark(searchpt)); - } - point2tetorg(searchpt, searchtet); // Go to the opposite face (the link face) of the vertex. - enextself(searchtet); - esymself(searchtet); + 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. - for (i = 0; i < 3; i++) { - pt = org(searchtet); + 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; - enextself(searchtet); } } collectflag = 1; esym(searchtet, neightet); - tspivot(neightet, checksh); - if (checksh.sh != NULL) { + 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 (checksh.sh != NULL) + } if (!fullstar) { collectflag = 0; } } if (collectflag) { fsymself(neightet); // Goto the adj tet of this face. - assert(neightet.tet != NULL); esymself(neightet); // Goto the oppo face of this vertex. // assert(oppo(neightet) == searchpt); infect(neightet); // Collect this tet (link face). @@ -21867,15 +20038,14 @@ int tetgenmesh::getvertexstar(int fullstar, point searchpt, arraypool* tetlist, 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 neighors at the other two edges of this face. + // Check the neighbors at the other two edges of this face. for (j = 0; j < 2; j++) { collectflag = 1; enextself(searchtet); - //fnext(searchtet, neightet); esym(searchtet, neightet); - tspivot(neightet, checksh); - if (checksh.sh != NULL) { + if (issubface(neightet)) { if (shlist != NULL) { + tspivot(neightet, checksh); if (!sinfected(checksh)) { // Collect this subface (link edge). sinfected(checksh); @@ -21889,7 +20059,6 @@ int tetgenmesh::getvertexstar(int fullstar, point searchpt, arraypool* tetlist, } if (collectflag) { fsymself(neightet); - assert(neightet.tet != NULL); if (!infected(neightet)) { esymself(neightet); // Go to the face opposite to 'searchpt'. infect(neightet); @@ -21909,16 +20078,6 @@ int tetgenmesh::getvertexstar(int fullstar, point searchpt, arraypool* tetlist, } // j } // i - if (b->verbose > 2) { - printf(" Collected %ld tets", tetlist->objects); - if (vertlist != NULL) { - printf(", %ld vertices", vertlist->objects); - } - if (shlist != NULL) { - printf(", %ld subfaces", shlist->objects); - } - printf(".\n"); - } // Uninfect the list of tets and vertices. for (i = 0; i < tetlist->objects; i++) { @@ -21982,13 +20141,13 @@ int tetgenmesh::getedge(point e1, point e2, triface *tedge) // Search for the edge [e1, e2]. point2tetorg(e1, *tedge); - finddirection(tedge, e2, 1); + finddirection(tedge, e2); if (dest(*tedge) == e2) { return 1; } else { // Search for the edge [e2, e1]. point2tetorg(e2, *tedge); - finddirection(tedge, e1, 1); + finddirection(tedge, e1); if (dest(*tedge) == e1) { esymself(*tedge); return 1; @@ -21998,20 +20157,18 @@ int tetgenmesh::getedge(point e1, point e2, triface *tedge) // Go to the link face of e1. point2tetorg(e1, searchtet); - enextself(searchtet); - esymself(searchtet); + enextesymself(searchtet); //assert(oppo(searchtet) == e1); - assert(cavetetlist->objects == 0l); // It will re-use this list. + 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]. - enext(searchtet, *tedge); - esymself(*tedge); - eprevself(*tedge); // [e1,e2,#,#]. + eorgoppo(searchtet, *tedge); // [e1,e2,#,#]. return 1; } enextself(searchtet); @@ -22024,24 +20181,22 @@ int tetgenmesh::getedge(point e1, point e2, triface *tedge) pt = apex(neightet); if (pt == e2) { // Found. 'neightet' is [#,#,e2,e1]. - enext(neightet, *tedge); - esymself(*tedge); - eprevself(*tedge); // [e1,e2,#,#]. + eorgoppo(neightet, *tedge); // [e1,e2,#,#]. return 1; } // Continue searching in the link face of e1. infect(searchtet); - cavetetlist->newindex((void **) &parytet); + tetlist->newindex((void **) &parytet); *parytet = searchtet; infect(neightet); - cavetetlist->newindex((void **) &parytet); + tetlist->newindex((void **) &parytet); *parytet = neightet; done = 0; - for (i = 0; (i < cavetetlist->objects) && !done; i++) { - parytet = (triface *) fastlookup(cavetetlist, i); + for (i = 0; (i < tetlist->objects) && !done; i++) { + parytet = (triface *) fastlookup(tetlist, i); searchtet = *parytet; for (j = 0; (j < 2) && !done; j++) { enextself(searchtet); @@ -22051,13 +20206,11 @@ int tetgenmesh::getedge(point e1, point e2, triface *tedge) pt = apex(neightet); if (pt == e2) { // Found. 'neightet' is [#,#,e2,e1]. - enext(neightet, *tedge); - esymself(*tedge); - eprevself(*tedge); // [e1,e2,#,#]. + eorgoppo(neightet, *tedge); done = 1; } else { infect(neightet); - cavetetlist->newindex((void **) &parytet); + tetlist->newindex((void **) &parytet); *parytet = neightet; } } @@ -22065,11 +20218,11 @@ int tetgenmesh::getedge(point e1, point e2, triface *tedge) } // i // Uninfect the list of visited tets. - for (i = 0; i < cavetetlist->objects; i++) { - parytet = (triface *) fastlookup(cavetetlist, i); + for (i = 0; i < tetlist->objects; i++) { + parytet = (triface *) fastlookup(tetlist, i); uninfect(*parytet); } - cavetetlist->restart(); + tetlist->restart(); return done; } @@ -22085,7 +20238,6 @@ int tetgenmesh::getedge(point e1, point e2, triface *tedge) int tetgenmesh::reduceedgesatvertex(point startpt, arraypool* endptlist) { triface searchtet; - face checkseg; point *pendpt, *parypt; enum interresult dir; flipconstraints fc; @@ -22093,13 +20245,9 @@ int tetgenmesh::reduceedgesatvertex(point startpt, arraypool* endptlist) int count; int n, i, j; - if (b->verbose > 2) { - printf(" Initial edge degree = %ld.\n", endptlist->objects); - } - assert(endptlist->objects >= 4l); - // Reduce the number of edges. fc.remvert = startpt; + fc.checkflipeligibility = 1; while (1) { @@ -22121,13 +20269,12 @@ int tetgenmesh::reduceedgesatvertex(point startpt, arraypool* endptlist) } } else { point2tetorg(startpt, searchtet); - dir = finddirection(&searchtet, *pendpt, 1); + dir = finddirection(&searchtet, *pendpt); } if (dir == ACROSSVERT) { if (dest(searchtet) == *pendpt) { // Do not flip a segment. - tsspivot1(searchtet, checkseg); - if (checkseg.sh == NULL) { + if (!issubseg(searchtet)) { n = removeedgebyflips(&searchtet, &fc); if (n == 2) { reduceflag = 1; @@ -22158,10 +20305,6 @@ int tetgenmesh::reduceedgesatvertex(point startpt, arraypool* endptlist) } // while (1) - if (b->verbose > 2) { - printf(" Final edge degree = %ld.\n", endptlist->objects); - } - return (int) endptlist->objects; } @@ -22169,28 +20312,13 @@ int tetgenmesh::reduceedgesatvertex(point startpt, arraypool* endptlist) // // // removevertexbyflips() Remove a vertex by flips. // // // -// This routine attempts to remove the given vertex 'rempt' (p) from the cur-// -// rent tetrahedralization (T) by a sequence of elementary 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. // // // -// The original location of 'p' in the tetrahedralization without 'p' is ind-// -// icated by 'iloc'. 'searchtet' (t) is a tet in the current tetrahedralizat-// -// ion which contains 'p'. Depending on 'iloc', it means the followig: // -// - INTET: the origin of 't' is 'p'; // -// - ONFACE: the origin of 't' is 'p', the face of 't' was split by 'p'; // -// - ONEDGE: the origin of 't' is 'p', the edge of 't' was split by 'p'; // -// // -// If 'parentsh' (s) is given (not NULL), it indicates that 'p' is a Steiner // -// point on a facet, and 's' was a subface created by the insertion of 'p'. // -// 'iloc' must be either ONFACE or 'OEDGE'. The origin of 's' is 'p'. // -// // -// If 'parentseg' (seg) is given (not NULL), it indicated that 'p' is a // -// Steiner point on a segment, and 'seg' was a subsegment created by 'p'. // -// 'iloc' must be ONEDGE. The original of 'seg' is 'p'. // -// // // Unless T is a Delaunay tetrahedralization, there is no guarantee that 'p' // // can be successfully removed. // // // @@ -22202,11 +20330,13 @@ int tetgenmesh::removevertexbyflips(point steinerpt) triface searchtet, spintet, neightet; face parentsh, spinsh, checksh; face leftseg, rightseg, checkseg; - point lpt = NULL, rpt = NULL, apexpt, *parypt; + 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); @@ -22241,13 +20371,16 @@ int tetgenmesh::removevertexbyflips(point steinerpt) 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. @@ -22267,24 +20400,7 @@ int tetgenmesh::removevertexbyflips(point steinerpt) removeflag = 0; - if (valence < 3) { - assert(0); // Unknown cases. - } - - if (valence == 3) { - // Only three edges at this vertex. This is only possible when there are - // Inverted elements. - getvertexstar(1, steinerpt, cavetetlist, NULL, NULL); - if (cavetetlist->objects == 2) { - printf("to be continued..."); - assert(0); - } else { - assert(0); // Unknown cases. - } - cavetetlist->restart(); - loc = OUTSIDE; - removeflag = 1; - } else if (valence == 4) { + 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); @@ -22317,19 +20433,24 @@ int tetgenmesh::removevertexbyflips(point steinerpt) // 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! At first, this is - // due to the same reason as the case 'valence == 4'. Second, - // there are 4 vertices (including p, lpt, rpt) exactly coplanar. - // We can do a 6-to-2 flip to remove p and recover a face - // [lpt, rpt, c] = [a,b,c]. + // 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); - loc = ONFACE; - removeflag = 1; + // 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. @@ -22340,29 +20461,35 @@ int tetgenmesh::removevertexbyflips(point steinerpt) wrktets[2] = searchtet; eprevself(wrktets[2]); esymself(wrktets[2]); - // Get the one which has a subface (should be only 1). - n = -1; - valence = 0; // Re-use it as a counter. + // 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++) { - tspivot(wrktets[i], checksh); - if (checksh.sh != NULL) { - n = i; - valence++; + 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(valence == 1); - searchtet = wrktets[n]; - esymself(searchtet); - enextself(searchtet); + 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; - } else { // valence > 5. - - } // if (valence > 5) + } if (!removeflag) { if (vt == FREESEGVERTEX) { @@ -22399,7 +20526,7 @@ int tetgenmesh::removevertexbyflips(point steinerpt) if (!removeflag) { if (vt == FREESEGVERTEX) { - // Check if the edge [lpr, rpt] exists. + // 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. @@ -22407,7 +20534,7 @@ int tetgenmesh::removevertexbyflips(point steinerpt) // 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 surronding tets. + // Detach the subsegments from their surrounding tets. for (i = 0; i < 2; i++) { checkseg = (i == 0) ? leftseg : rightseg; sstpivot1(checkseg, neightet); @@ -22440,9 +20567,6 @@ int tetgenmesh::removevertexbyflips(point steinerpt) setpointtype(steinerpt, FREEVOLVERTEX); st_segref_count--; st_volref_count++; - // Save this Steiner points in (global) list. - suppsteinerptlist->newindex((void **) &parypt); - *parypt = steinerpt; return 1; } // if (!checksubfaceflag) } // if (getedge(...)) @@ -22450,10 +20574,6 @@ int tetgenmesh::removevertexbyflips(point steinerpt) } // if (!removeflag) if (!removeflag) { - if (b->verbose > 2) { - printf(" Unable to remove Steiner point %d val(%d).\n", - pointmark(steinerpt), valence); - } return 0; } @@ -22510,7 +20630,8 @@ int tetgenmesh::removevertexbyflips(point steinerpt) 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, 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 @@ -22528,6 +20649,32 @@ int tetgenmesh::removevertexbyflips(point steinerpt) 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). @@ -22535,18 +20682,12 @@ int tetgenmesh::removevertexbyflips(point steinerpt) // 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); - // DEBUG BEGIN - // fliptets[3] is [a,b,c,p], check it. - assert(org(fliptets[3]) == apex(fliptets[0])); // a - assert(dest(fliptets[3]) == apex(fliptets[1])); // b - assert(apex(fliptets[3]) == apex(fliptets[2])); // c - assert(oppo(fliptets[3]) == steinerpt); - // fliptets[4] is [b,a,c,e]. - // DEBUG END + //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, 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 @@ -22590,7 +20731,8 @@ int tetgenmesh::removevertexbyflips(point steinerpt) 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, 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]. @@ -22613,7 +20755,8 @@ int tetgenmesh::removevertexbyflips(point steinerpt) 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, 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 @@ -22638,7 +20781,8 @@ int tetgenmesh::removevertexbyflips(point steinerpt) 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, 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]; @@ -22664,7 +20808,7 @@ int tetgenmesh::removevertexbyflips(point steinerpt) // Insert the new segment. point2tetorg(lpt, searchtet); - finddirection(&searchtet, rpt, 1); + finddirection(&searchtet, rpt); assert(dest(searchtet) == rpt); sstbond1(rightseg, searchtet); spintet = searchtet; @@ -22714,51 +20858,42 @@ int tetgenmesh::removevertexbyflips(point steinerpt) } // if (vt == FREESEGVERTEX) // The point has been removed. - setpointtype(steinerpt, UNUSEDVERTEX); - unuverts++; - // 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 (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++; } - if (steinerleft > 0) steinerleft++; return 1; } /////////////////////////////////////////////////////////////////////////////// // // -// suppresssteinerpoint() Suppress a Steiner point. // -// // -// Remove a Steiner point 'p' from the segment it lies on. It is replaced by // -// a set of volume Steiner points in each sector at the segment. // -// // -// The list of volume Steiner points is returned in 'suppsteinerptlist'. // +// suppressbdrysteinerpoint() Suppress a boundary Steiner point // // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::suppressssteinerpoint(point steinerpt) +int tetgenmesh::suppressbdrysteinerpoint(point steinerpt) { - triface searchtet, neightet, spintet, *parytet; - triface newtet, newface; face parentsh, spinsh, *parysh; - face newsh, neighsh; - face leftseg, rightseg, checkseg, *splitseg; - point lpt = NULL, rpt = NULL, newpt, *parypt; - point pa, pb, pc; - verttype vt; - long bak_supp_steiners; - int slawson; - int i, j, k; + face leftseg, rightseg; + point lpt = NULL, rpt = NULL; + int i; - vt = pointtype(steinerpt); + verttype vt = pointtype(steinerpt); if (vt == FREESEGVERTEX) { sdecode(point2sh(steinerpt), leftseg); - assert(leftseg.sh != NULL); leftseg.shver = 0; if (sdest(leftseg) == steinerpt) { senext(leftseg, rightseg); @@ -22778,46 +20913,17 @@ int tetgenmesh::suppressssteinerpoint(point steinerpt) lpt = sorg(leftseg); rpt = sdest(rightseg); if (b->verbose > 2) { - printf(" Suppressing point %d from segment (%d, %d).\n", + printf(" Suppressing Steiner point %d in segment (%d, %d).\n", pointmark(steinerpt), pointmark(lpt), pointmark(rpt)); } - } else if (vt == FREEFACETVERTEX) { - if (b->verbose > 2) { - printf(" Suppressing point %d from facet.\n", - pointmark(steinerpt)); - } - //point2shorg(steinerpt, parentsh); - getvertexstar(0, steinerpt, cavetetlist, NULL, caveshlist); - parysh = (face *) fastlookup(caveshlist, 0); - parentsh = *parysh; - //assert(sapex(parentsh) == steinerpt); - senext2self(parentsh); - assert(sorg(parentsh) == steinerpt); - cavetetlist->restart(); - caveshlist->restart(); - } else { - // Do nothing. - return 0; - } - - if (vt == FREESEGVERTEX) { - // Check if this edge [lpt, rpt] already exists. - if (getedge(lpt, rpt, &searchtet)) { - tsspivot1(searchtet, checkseg); // SELF_CHECK - assert(checkseg.sh == NULL); - return 0; - } - } - - bak_supp_steiners = suppsteinerptlist->objects; - - if (vt == FREESEGVERTEX) { // 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; @@ -22827,6 +20933,198 @@ int tetgenmesh::suppressssteinerpoint(point steinerpt) 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); @@ -22843,13 +21141,6 @@ int tetgenmesh::suppressssteinerpoint(point steinerpt) fnextself(spintet); if (spintet.tet == neightet.tet) break; } - } else { // vt == FREEFACETVERTEX - // A facet Steiner point. There are exactly two sectors. - for (i = 0; i < 2; i++) { - cavesegshlist->newindex((void **) &parysh); - *parysh = parentsh; - sesymself(parentsh); - } } // Loop through all sectors bounded by facets at this segment. @@ -22862,27 +21153,25 @@ int tetgenmesh::suppressssteinerpoint(point steinerpt) // Get all tets in this sector. setpoint2tet(steinerpt, encode(neightet)); getvertexstar(0, steinerpt, cavetetlist, NULL, caveshlist); - assert(caveshlist->objects > 0); - // Create a new vertex 'np'. - makepoint(&newpt, FREEVOLVERTEX); - st_volref_count++; - // Init 'np' at the same location of 'p'. - for (j = 0; j < 3; j++) newpt[j] = steinerpt[j]; - // Within the tet, replace 'p' by 'np'. - for (j = 0; j < cavetetlist->objects; j++) { - parytet = (triface *) fastlookup(cavetetlist, j); - setoppo(*parytet, newpt); - } // j - // Save the new Steiner point in list. - suppsteinerptlist->newindex((void **) &parypt); - *parypt = newpt; + 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); + //assert(apex(neightet) == newpt); // Clear the connection at this face. dissolve(neightet); tsdissolve(neightet); @@ -22893,12 +21182,15 @@ int tetgenmesh::suppressssteinerpoint(point steinerpt) } // i cavesegshlist->restart(); - // Remove p from the segment. - slawson = 0; // Do not do flip afterword. 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; } @@ -22907,12 +21199,7 @@ int tetgenmesh::suppressssteinerpoint(point steinerpt) if (vt == FREESEGVERTEX) { // The original segment is returned in 'rightseg'. rightseg.shver = 0; - assert(sorg(rightseg) == lpt); - assert(sdest(rightseg) == rpt); } - // The set of new subfaces are found in 'caveshbdlist'. - assert(caveshbdlist->objects > 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. @@ -22932,6 +21219,8 @@ int tetgenmesh::suppressssteinerpoint(point steinerpt) sesymself(newsh); tsbond(neightet, newsh); } + // Temporarily increase the hullsize. + hullsize += (caveshbdlist->objects * 2l); if (vt == FREESEGVERTEX) { // Connecting new tets at the recovered segment. @@ -22939,30 +21228,20 @@ int tetgenmesh::suppressssteinerpoint(point steinerpt) assert(parentsh.sh != NULL); spinsh = parentsh; while (1) { - assert(sinfected(spinsh)); if (sorg(spinsh) != lpt) sesymself(spinsh); - assert(sorg(spinsh) == lpt); - assert(sdest(spinsh) == rpt); // Get the new tet at this subface. stpivot(spinsh, newtet); - assert(oppo(newtet) == dummypoint); tssbond1(newtet, rightseg); // Go to the other face at this segment. - esymself(newtet); - assert(org(newtet) == rpt); - assert(newtet.tet[newtet.ver & 3] == NULL); - // Get the adjacent tet at this segment. spivot(spinsh, neighsh); if (sorg(neighsh) != lpt) sesymself(neighsh); sesymself(neighsh); stpivot(neighsh, neightet); - assert(oppo(neightet) == dummypoint); tssbond1(neightet, rightseg); sstbond1(rightseg, neightet); - // Go to the other face at this segment. + // Connecting two adjacent tets at this segment. + esymself(newtet); esymself(neightet); - assert(org(neightet) == lpt); - assert(neightet.tet[neightet.ver & 3] == NULL); // Connect the two tets (at rightseg) together. bond(newtet, neightet); // Go to the next subface. @@ -22987,9 +21266,8 @@ int tetgenmesh::suppressssteinerpoint(point steinerpt) sspivot(newsh, checkseg); if (checkseg.sh != NULL) { // A segment. It must not be the recovered segment. - assert(checkseg.sh != rightseg.sh); tssbond1(newtet, checkseg); - //sstbond1(checkseg, newtet); + sstbond1(checkseg, newtet); } spivot(newsh, neighsh); if (neighsh.sh != NULL) { @@ -23032,10 +21310,23 @@ int tetgenmesh::suppressssteinerpoint(point steinerpt) neightet = searchtet; } pc = apex(newface); - if (pc == dummypoint) { - setapex(newface, apex(neightet)); + 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 { - assert(pc == apex(neightet)); + 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) @@ -23053,6 +21344,16 @@ int tetgenmesh::suppressssteinerpoint(point steinerpt) } 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++; @@ -23061,163 +21362,201 @@ int tetgenmesh::suppressssteinerpoint(point steinerpt) } else { // vt == FREEFACETVERTEX st_facref_count--; } - if (steinerleft > 0) steinerleft++; + if (steinerleft > 0) steinerleft++; // We've removed a Steiner points. - if (b->verbose > 2) { - printf(" Duplicated %ld Steiner points.\n", - suppsteinerptlist->objects - bak_supp_steiners); + + 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() { - triface *parytet; - point rempt, *parypt, *plastpt, *ppt; - optparameters opm; - REAL ori; - int bak_fliplinklevel; - int remcount, smtcount; - int count, nt; - int i, j; if (!b->quiet) { printf("Suppressing Steiner points ...\n"); } - bak_fliplinklevel = b->fliplinklevel; + point rempt, *parypt; + + int bak_fliplinklevel = b->fliplinklevel; b->fliplinklevel = 100000; // Unlimited flip level. - remcount = 0; + int suppcount = 0, remcount = 0; + int i; - if (b->nobisect_param > 1) { // -Y2 - // Try to remove all the Steiner points. + // 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 (removevertexbyflips(rempt)) { - remcount++; + if (pointtype(rempt) == FREEVOLVERTEX) { + if (removevertexbyflips(rempt)) { + remcount++; + } } } } + } + + if (remcount > 0) { if (b->verbose) { - if (remcount > 0) { - printf(" Removed %d Steiner points.\n", remcount); - } + printf(" Removed %d interior Steiner points.\n", remcount); } - subvertstack->restart(); } - remcount = smtcount = 0; + b->fliplinklevel = bak_fliplinklevel; - // Try to remove the suppressed Steiner points. - for (i = 0; i < suppsteinerptlist->objects; i++) { - // Get the Steiner point. - parypt = (point *) fastlookup(suppsteinerptlist, i); - rempt = *parypt; - if (pointtype(rempt) != UNUSEDVERTEX) { - assert((pointtype(rempt) == FREESEGVERTEX) || - (pointtype(rempt) == FREEFACETVERTEX) || - (pointtype(rempt) == FREEVOLVERTEX)); - if (removevertexbyflips(rempt)) { - // Move the last entry to fill the current one. - j = (int) (suppsteinerptlist->objects - 1); - plastpt = (point *) fastlookup(suppsteinerptlist, j); - *parypt = *plastpt; - suppsteinerptlist->objects--; - i--; - remcount++; - } - } else { - // The point has been removed. - // Move the last entry to fill the current one. - j = (int) (suppsteinerptlist->objects - 1); - plastpt = (point *) fastlookup(suppsteinerptlist, j); - *parypt = *plastpt; - suppsteinerptlist->objects--; - i--; - } - } // i + 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. - if (b->verbose) { - if (remcount > 0) { - printf(" Removed %d suppressed Steiner points.\n", remcount); - } - } + smtcount = 0; - if (suppsteinerptlist->objects == 0l) { - b->fliplinklevel = bak_fliplinklevel; - return remcount; - } + do { - // Point smooth options. - opm.max_min_volume = 1; - opm.numofsearchdirs = 20; - opm.searchstep = 0.001; + 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 - nt = 0; + smtcount += count; - while (1) { - // Try to smooth volume Steiner points. - count = 0; + if (count == 0) { + // No point has been smoothed. + break; + } - for (i = 0; i < suppsteinerptlist->objects; i++) { - parypt = (point *) fastlookup(suppsteinerptlist, 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 = orient3d(ppt[1], ppt[0], ppt[2], ppt[3]); - if (j == 0) { - opm.initval = ori; - } else { - if (opm.initval > ori) opm.initval = ori; - } + nt++; + if (nt > 2) { + break; // Already three iterations. } - if (smoothpoint(rempt, cavetetlist, 1, &opm)) { - count++; + } // 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; } - cavetetlist->restart(); } - } // i - - smtcount += count; - if (count == 0) { - // No point has been smoothed. break; - } + } while (1); // Additional loop for (ivcount > 0) - nt++; - if (nt > 2) { - break; // Already three iterations. + if (ivcount > 0) { + printf("BUG Report! The mesh contain inverted elements.\n"); } - } // while - - // The mesh should not contain inverted (or degenrrated) tets now. - checkinverttetflag = 0; - if (b->verbose) { - if (smtcount > 0) { - printf(" Smoothed %d Steiner points.\n", smtcount); + if (b->verbose) { + if (smtcount > 0) { + printf(" Smoothed %d Steiner points.\n", smtcount); + } } - } + } // -Y2 - b->fliplinklevel = bak_fliplinklevel; + subvertstack->restart(); - return smtcount; + return 1; } /////////////////////////////////////////////////////////////////////////////// @@ -23239,17 +21578,11 @@ void tetgenmesh::recoverboundary(clock_t& tv) // Counters. long bak_segref_count, bak_facref_count, bak_volref_count; - long bak_supp_count; if (!b->quiet) { printf("Recovering boundaries...\n"); } - if (b->verbose) { - printf(" Flip link level = %d\n", b->fliplinklevel); - } - - //markacutevertices(); if (b->verbose) { printf(" Recovering segments.\n"); @@ -23261,29 +21594,17 @@ void tetgenmesh::recoverboundary(clock_t& tv) misseglist = new arraypool(sizeof(face), 8); bdrysteinerptlist = new arraypool(sizeof(point), 8); - if (0) { //if (b->order == 4) { // '-o4' option (for debug) - // In sequential order. - subsegs->traversalinit(); - for (i = 0; i < subsegs->items; i++) { - searchseg.sh = shellfacetraverse(subsegs); - //sinfect(searchseg); // Only save it once. - subsegstack->newindex((void **) &paryseg); - *paryseg = searchseg; - } - } else { - // 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; - } + // 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. @@ -23293,8 +21614,8 @@ void tetgenmesh::recoverboundary(clock_t& tv) autofliplinklevel = 1; // Init value. } + // First, trying to recover segments by only doing flips. while (1) { - recoversegments(misseglist, 0, 0); if (misseglist->objects > 0) { @@ -23333,8 +21654,7 @@ void tetgenmesh::recoverboundary(clock_t& tv) } if (misseglist->objects > 0) { - // There are missing segments. Increase the fliplevel. - nit = 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++) { @@ -23343,17 +21663,13 @@ void tetgenmesh::recoverboundary(clock_t& tv) } misseglist->restart(); - // Recover the missing segments by doing more flips. recoversegments(misseglist, 1, 0); if (misseglist->objects < ms) { // The number of missing segments is reduced. continue; } else { - nit++; - if (nit >= 3) { - break; - } + break; } } if (b->verbose) { @@ -23363,8 +21679,8 @@ void tetgenmesh::recoverboundary(clock_t& tv) } if (misseglist->objects > 0) { - // There are missing segments. Add Steiner points in volume. - nit = 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++) { @@ -23373,17 +21689,13 @@ void tetgenmesh::recoverboundary(clock_t& tv) } misseglist->restart(); - // Recover the missing segments (with Steiner points). recoversegments(misseglist, 1, 1); if (misseglist->objects < ms) { // The number of missing segments is reduced. continue; } else { - nit++; - if (nit >= 3) { - break; - } + break; } } if (b->verbose) { @@ -23392,7 +21704,8 @@ void tetgenmesh::recoverboundary(clock_t& tv) } if (misseglist->objects > 0) { - // There are missing segments. Add Steiner points to split them. + // 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); @@ -23400,7 +21713,6 @@ void tetgenmesh::recoverboundary(clock_t& tv) } misseglist->restart(); - // Recover the missing segments (with Steiner points). recoversegments(misseglist, 1, 2); if (b->verbose) { @@ -23479,6 +21791,7 @@ void tetgenmesh::recoverboundary(clock_t& tv) while (1) { recoversubfaces(misshlist, 0); + if (misshlist->objects > 0) { if (b->fliplinklevel >= 0) { break; @@ -23553,32 +21866,18 @@ void tetgenmesh::recoverboundary(clock_t& tv) } - if ((bdrysteinerptlist->objects > 0) && (b->nobisect_param > 0)) { // -Y1 - bak_supp_count = 0; - b->fliplinklevel = 100000; // Unlimited flip levels. - do { - // Suppress boundary Steiner points. - for (i = 0; i < bdrysteinerptlist->objects; i++) { - parypt = (point *) fastlookup(bdrysteinerptlist, i); - rempt = *parypt; - suppressssteinerpoint(rempt); - bak_supp_count++; - } - bdrysteinerptlist->restart(); - // There may be subfaces need to be recover. - if (subfacstack->objects > 0l) { - recoversubfaces(NULL, 1); - } - } while (bdrysteinerptlist->objects > 0); + if (bdrysteinerptlist->objects > 0) { if (b->verbose) { - printf(" Suppressed %ld Steiner points from boundary.\n", - bak_supp_count); + printf(" %ld Steiner points remained in boundary.\n", + bdrysteinerptlist->objects); } - // The mesh contains inverted (or degenrrated) tets now. - checkinverttetflag = 1; } // if + // Accumulate the dynamic memory. + totalworkmemory += (misseglist->totalmemory + misshlist->totalmemory + + bdrysteinerptlist->totalmemory); + delete bdrysteinerptlist; delete misseglist; delete misshlist; @@ -23599,66 +21898,100 @@ void tetgenmesh::recoverboundary(clock_t& tv) // // /////////////////////////////////////////////////////////////////////////////// + void tetgenmesh::carveholes() { - arraypool *tetarray; - triface tetloop, neightet, hulltet, *parytet; - triface openface, casface; - triface *regiontets; - face checksh, casingout, casingin, *parysh; + arraypool *tetarray, *hullarray; + triface tetloop, neightet, *parytet, *parytet1; + triface *regiontets = NULL; + face checksh, *parysh; face checkseg; - point *ppt, pa, pb, pc, *parypt; - enum locateresult loc; - REAL volume; - long delsegcount, delvertcount, delsteinercount; - int regioncount; - int attrnum, attr, maxattr; - int remflag; - int i, j; - - tetrahedron ptr; + point ptloop, *parypt; + int t1ver; + int i, j, k; if (!b->quiet) { - printf("Removing exterior tetrahedra ...\n"); + 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); - regiontets = NULL; + hullarray = new arraypool(sizeof(triface), 10); - maxattr = 0; // Choose a small number here. - attrnum = in->numberoftetrahedronattributes; - - // Mark as infected any unprotected hull tets. + // 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 ((point) tetloop.tet[7] == dummypoint) { + if (ishulltet(tetloop)) { // Is this side protected by a subface? - tspivot(tetloop, checksh); - if (checksh.sh == NULL) { + if (!issubface(tetloop)) { + // Collect an unprotected hull tet and tet. infect(tetloop); - tetarray->newindex((void **) &parytet); + 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(); } - hullsize -= tetarray->objects; - 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); - loc = locate(&(in->holelist[i]), &neightet, 0, 1); // randflag = 1; - if (loc != OUTSIDE) { - infect(neightet); - tetarray->newindex((void **) &parytet); - *parytet = 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) { @@ -23666,10 +21999,10 @@ void tetgenmesh::carveholes() printf("lies outside the convex hull.\n"); } } - } - } + } // i + } // if (in->numberofholes > 0) - if (b->regionattrib && (in->numberofregions > 0)) { // If has -A option. + 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]; @@ -23678,12 +22011,8 @@ void tetgenmesh::carveholes() // Search a tet containing the i-th region point. neightet.tet = NULL; randomsample(&(in->regionlist[i]), &neightet); - loc = locate(&(in->regionlist[i]), &neightet, 0, 1); // randflag = 1; - if (loc != OUTSIDE) { + if (locate(&(in->regionlist[i]), &neightet) != OUTSIDE) { regiontets[i/5] = neightet; - if ((int) in->regionlist[i + 3] > maxattr) { - maxattr = (int) in->regionlist[i + 3]; - } } else { if (!b->quiet) { printf("Warning: The %d-th region point ", i/5+1); @@ -23694,58 +22023,51 @@ void tetgenmesh::carveholes() } } - // Find and infect all exterior tets (in concave place and in holes). + // Collect all exterior tets (in concave place and in holes). for (i = 0; i < tetarray->objects; i++) { parytet = (triface *) fastlookup(tetarray, i); - tetloop = *parytet; - for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) { - fsym(tetloop, neightet); - // Is this side protected by a subface? - tspivot(tetloop, checksh); - if (checksh.sh == NULL) { - // Not protected. Infect it if it is not a hull tet. - if ((point) neightet.tet[7] != dummypoint) { - if (!infected(neightet)) { + 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); - tetarray->newindex((void **) &parytet); - *parytet = 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 { - // Its adjacent tet is protected. - if ((point) neightet.tet[7] == dummypoint) { - // A hull tet. It is dead. - assert(!infected(neightet)); - infect(neightet); - tetarray->newindex((void **) &parytet); - *parytet = neightet; - // Both sides of this subface are exterior. - stdissolve(checksh); - // Queue this subface (to be deleted later). + // 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); // Only queue it once. + sinfect(checksh); subfacstack->newindex((void **) &parysh); *parysh = checksh; } - hullsize--; - } else { - if (!infected(neightet)) { - // The subface is still connect to a "live" tet - it survived. - // tsbond(neightet, checksh); - } else { - // Both sides of this subface are exterior. - stdissolve(checksh); - // Queue this subface (to be deleted later). - if (!sinfected(checksh)) { - sinfect(checksh); // Only queue it once. - 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. @@ -23760,233 +22082,283 @@ void tetgenmesh::carveholes() } } - // Remove all exterior tetrahedra (including infected hull tets). - for (i = 0; i < tetarray->objects; i++) { - parytet = (triface *) fastlookup(tetarray, i); - tetloop = *parytet; - for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) { - fsym(tetloop, neightet); - if (!infected(neightet)) { - // A "live" tet (may be a hull tet). Clear its adjacent tet. - neightet.tet[neightet.ver & 3] = 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; } - } - tetrahedrondealloc(parytet->tet); - } // i - - tetarray->restart(); // Re-use it for new hull tets. - - // Create new hull tets. - // Update point-to-tet map, segment-to-tet map, and subface-to-tet map. - tetrahedrons->traversalinit(); - tetloop.tet = tetrahedrontraverse(); - while (tetloop.tet != (tetrahedron *) NULL) { - for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) { - if (tetloop.tet[tetloop.ver] == NULL) { - tspivot(tetloop, checksh); - assert(checksh.sh != NULL); // SELF_CHECK - // Create a new hull tet. - 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 (i = 0; i < 3; i++) { - tsspivot1(tetloop, checkseg); - if (checkseg.sh != NULL) { - tssbond1(hulltet, checkseg); - sstbond1(checkseg, hulltet); - } - enextself(tetloop); - eprevself(hulltet); + 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; } - // Save this hull tet in list. - tetarray->newindex((void **) &parytet); - *parytet = hulltet; } } - // Update the point-to-tet map. - tetloop.ver = 0; - ptr = encode(tetloop); - ppt = (point *) tetloop.tet; - for (i = 4; i < 8; i++) { - setpoint2tet(ppt[i], ptr); - } - tetloop.tet = tetrahedrontraverse(); + ptloop = pointtraverse(); } - if (subfacstack->objects > 0) { - // Remove all subfaces which do not attach to any tetrahedron. - // Segments which are not attached to any subfaces and tets - // are deleted too. - delsegcount = 0; - 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; + 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) { - 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); + fnextself(neightet); + if (!infected(neightet)) break; } - if (checkseg.sh != NULL) { - // Make sure the segment does not connect to a dead one. - ssbond(casingout, checkseg); + 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))); } - } 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", + } + // 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++; } - 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); } - 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(); - } + subfacstack->restart(); + } // if (subfacstack->objects > 0l) - // Some vertices may be not belong to any tet. Mark them. - delvertcount = unuverts; - delsteinercount = 0l; - points->traversalinit(); - pa = pointtraverse(); - while (pa != NULL) { - if (pointtype(pa) != UNUSEDVERTEX) { - remflag = 0; - decode(point2tet(pa), neightet); - if ((neightet.tet == NULL) || (neightet.tet[4] == NULL)) { - remflag = 1; // It's a dead tet. - } else { - // Check if this tet contains pa. - ppt = (point *) &(neightet.tet[4]); - if (!((ppt[0] == pa) || (ppt[1] == pa) || - (ppt[2] == pa) || (ppt[3] == pa))) { - remflag = 1; // It's a wrong pointer. + 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 (remflag) { - // Found an exterior vertex. - if (pointmark(pa) > - (in->numberofpoints - (in->firstnumber ? 0 : 1))) { - if (pointtype(pa) == FREESEGVERTEX) { - st_segref_count--; - } else if (pointtype(pa) == FREEFACETVERTEX) { - st_facref_count--; + + 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 { - assert(pointtype(pa) == FREEVOLVERTEX); - st_volref_count--; //st_inpoly_count--; - } - delsteinercount++; // A Steiner point. - if (steinerleft > 0) steinerleft++; - } - setpointtype(pa, UNUSEDVERTEX); - unuverts++; - } else { - // This vertex survived. - if (b->nobisect && (b->nobisect_param > 1)) { // -Y2 - // Queue it if it is a Steiner point. - if ((pointtype(pa) == FREESEGVERTEX) || - (pointtype(pa) == FREEFACETVERTEX) || - (pointtype(pa) == FREEVOLVERTEX)) { - subvertstack->newindex((void **) &parypt); - *parypt = pa; + 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); } - pa = pointtraverse(); - } + tetarray->restart(); - 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); - } + 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)) - // Update the hull size. - hullsize += tetarray->objects; + 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(); - // Connect new hull tets. - for (i = 0; i < tetarray->objects; i++) { - parytet = (triface *) fastlookup(tetarray, i); - hulltet = *parytet; - for (j = 0; j < 3; j++) { - esym(hulltet, neightet); - if (neightet.tet[neightet.ver & 3] == NULL) { - tspivot(hulltet, checksh); - assert(checksh.sh != NULL); - // Get the next subface in the same face ring of checksh. It must - // exist, otherwise, checksh is either a dangling subface (which - // should be removed already), or it is not a hull face. - sfnext(checksh, casingout); - assert(casingout.sh != NULL); - // Go to the hull side. - sesymself(casingout); - stpivot(casingout, casface); - assert(ishulltet(casface)); - esymself(casface); - assert(casface.tet[casface.ver & 3] == NULL); - // Bond the two hull tets together. - bond(neightet, casface); + if (subfacstack->objects > 0l) { + for (i = 0; i < subfacstack->objects; i++) { + parysh = (face *) fastlookup(subfacstack, i); + suninfect(*parysh); } - enextself(hulltet); + subfacstack->restart(); } - } - // Set region attributes (when has -A and -AA options). - if (b->regionattrib) { + 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"); } - regioncount = 0; + 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) { @@ -23994,6 +22366,9 @@ void tetgenmesh::carveholes() 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]); @@ -24007,81 +22382,59 @@ void tetgenmesh::carveholes() if (b->varvolume) { // If has -a option. setvolumebound(tetloop.tet, volume); } - for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) { - fsym(tetloop, neightet); - // Is this side protected by a subface? - tspivot(tetloop, checksh); - if (checksh.sh == NULL) { - // Not protected. It must not be a hull tet. - // assert((point) neightet.tet[7] != dummypoint); - if ((point) neightet.tet[7] == dummypoint) { - assert(0); - } - if (!infected(neightet)) { + 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; } - } else { - // Protected. Set attribute for hull tet as well. - if ((point) neightet.tet[7] == dummypoint) { - setelemattribute(neightet.tet, attrnum, attr); - if (b->varvolume) { // If has -a option. - setvolumebound(neightet.tet, volume); - } - } } - } // ver + } // k } // j regioncount++; } // if (regiontets[i/5].tet != NULL) } // i } - if (b->regionattrib > 1) { // If has -AA option. - // 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 (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) { - fsym(tetloop, neightet); + // 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? - tspivot(tetloop, checksh); - if (checksh.sh == NULL) { - // Not protected. It must not be a hull tet. - assert((point) neightet.tet[7] != dummypoint); - if (!infected(neightet)) { - infect(neightet); - tetarray->newindex((void **) &parytet); - *parytet = neightet; - } - } else { - // Protected. Set attribute for hull tet as well. - if ((point) neightet.tet[7] == dummypoint) { - setelemattribute(neightet.tet, attrnum, attr); - } + if (!issubface(neightet)) { + infect(neightet); + tetarray->newindex((void **) &parytet); + *parytet = neightet; } - } // loc - } - attr++; // Increase the attribute. - regioncount++; - } // if (!infected(tetloop)) - tetloop.tet = tetrahedrontraverse(); + } + } // k + } // j + attr++; // Increase the attribute. + regioncount++; } - // Until here, every tet has a region attribute. + tetloop.tet = tetrahedrontraverse(); } + // Until here, every tet has a region attribute. // Uninfect processed tets. tetrahedrons->traversalinit(); @@ -24091,45 +22444,49 @@ void tetgenmesh::carveholes() tetloop.tet = tetrahedrontraverse(); } - // Mesh elements contain region attributes now. - in->numberoftetrahedronattributes++; - if (b->verbose) { - assert(regioncount > 0); + //assert(regioncount > 0); if (regioncount > 1) { printf(" Found %d subdomains.\n", regioncount); } else { - printf(" Found 1 domain.\n"); + printf(" Found %d domain.\n", regioncount); } } - } // if (b->regionattrib) - if (b->regionattrib && (in->numberofregions > 0)) { // If has -A option. + if (regiontets != NULL) { delete [] regiontets; } delete tetarray; + delete hullarray; - // The mesh is non-convex now. - nonconvex = 1; + 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) { - flippush(flipstack, &tetloop); - } + // 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(); + } - // Peel "slivers" off the hull. - lawsonflip3d(NULL, 4, 1, 0, 0); + flipconstraints fc; + fc.enqflag = 2; + long sliver_peel_count = lawsonflip3d(&fc); - if (b->verbose && (opt_sliver_peels > 0l)) { - printf(" Peeled %ld hull slivers.\n", opt_sliver_peels); - } + if (sliver_peel_count > 0l) { + if (b->verbose) { + printf(" Removed %ld hull slivers.\n", sliver_peel_count); + } + } + unflipqueue->restart(); + } // if (!b->convex) } /////////////////////////////////////////////////////////////////////////////// @@ -24153,20 +22510,33 @@ void tetgenmesh::reconstructmesh() REAL angtol, ang; int eextras, marker = 0; int bondflag; + int t1ver; int idx, i, j, k; if (!b->quiet) { printf("Reconstructing mesh ...\n"); } - // Default assume the mesh is non-convex. - nonconvex = 1; - // Create a map from indices to vertices. + 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; i++) { + //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; } @@ -24176,7 +22546,6 @@ void tetgenmesh::reconstructmesh() idx = i * in->numberofcorners; for (j = 0; j < 4; j++) { p[j] = idx2verlist[in->tetrahedronlist[idx++]]; - setpointtype(p[j], VOLVERTEX); // initial type. } // Check the orientation. ori = orient3d(p[0], p[1], p[2], p[3]); @@ -24224,7 +22593,6 @@ void tetgenmesh::reconstructmesh() p[2] = apex(tetloop); // c prevchktet = tetloop; do { - assert(checktet.ver < 4); // SELF_CHECK q[0] = org(checktet); // a' q[1] = dest(checktet); // b' q[2] = apex(checktet); // c' @@ -24410,51 +22778,54 @@ void tetgenmesh::reconstructmesh() } // 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)) { - bondflag = 1; // A hull face. - } else { - if (eextras > 0) { - if (elemattribute(tetloop.tet, eextras - 1) != - elemattribute(checktet.tet, eextras - 1)) { - bondflag = 1; // An interior interface. - } - } + // 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. } - 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); + } else { + if (eextras > 0) { + if (elemattribute(tetloop.tet, eextras - 1) != + elemattribute(checktet.tet, eextras - 1)) { + bondflag = 1; // Insert an interior interface. } - 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(); + } + } + 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(); @@ -24488,11 +22859,8 @@ void tetgenmesh::reconstructmesh() subloop.sh = shellfacetraverse(subfaces); } - //if (b->verbose) { - // printf(" Created %ld subfaces.\n", subfaces->items); - //} - // Segments will be introudced. + // 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++) { @@ -24554,90 +22922,187 @@ void tetgenmesh::reconstructmesh() } // 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. + // 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 { - // Check the dihedral angle formed by the two subfaces. - spivot(subloop, neighsh); - 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 (!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 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 (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); } - setshellmark(segloop, 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); - } - subloop.sh = shellfacetraverse(subfaces); - } + 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->verbose) { - // printf(" Created %ld segments.\n", 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; - //nonconvex = 1; delete [] idx2verlist; delete [] ver2tetarray; @@ -24659,33 +23124,37 @@ int tetgenmesh::scoutpoint(point searchpt, triface *searchtet, int randflag) { point pa, pb, pc, pd; enum locateresult loc = OUTSIDE; - REAL vol, ori1, ori2, ori3, ori4; - int iter; + REAL vol, ori1, ori2 = 0, ori3 = 0, ori4 = 0; + int t1ver; - if (searchtet->tet == NULL) { - *searchtet = recenttet; + + // Randomly select a good starting tet. + if (randflag) { + randomsample(searchpt, searchtet); + } else { + if (searchtet->tet == NULL) { + *searchtet = recenttet; + } } + loc = locate(searchpt, searchtet); - iter = 0; - while (1) { - // Randonmly select a good starting tet. - if (randflag) { - randomsample(searchpt, searchtet); + if (loc == OUTSIDE) { + if (b->convex) { // -c option + // The point lies outside of the convex hull. + return (int) loc; } - loc = locate(searchpt, searchtet, 0, 1); - if (loc == OUTSIDE) { - // Not found. This happens when the mesh is not convex. - if (!randflag) break; - iter++; - if (iter > 3) { - searchtet->tet = NULL; - break; - } - } else { - // Found the point. - break; + // 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); } - } // while (1) + } if (loc != OUTSIDE) { // Round the result of location. @@ -24693,11 +23162,11 @@ int tetgenmesh::scoutpoint(point searchpt, triface *searchtet, int randflag) pb = dest(*searchtet); pc = apex(*searchtet); pd = oppo(*searchtet); - vol = orient3d(pa, pb, pc, pd); - ori1 = orient3d(pa, pb, pc, searchpt); - ori2 = orient3d(pb, pa, pd, searchpt); - ori3 = orient3d(pc, pb, pd, searchpt); - ori4 = orient3d(pa, pc, pd, searchpt); + 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; @@ -24712,30 +23181,31 @@ int tetgenmesh::scoutpoint(point searchpt, triface *searchtet, int randflag) pc = apex(*searchtet); pd = oppo(*searchtet); - vol = orient3d(pa, pb, pc, pd); - assert(vol < 0); // vol != 0 - - ori1 = orient3d(pa, pb, pc, searchpt); - if (fabs(ori1 / vol) < b->epsilon) ori1 = 0; // Rounding. - if (ori1 <= 0) { - ori2 = orient3d(pb, pa, pd, searchpt); - if (fabs(ori2 / vol) < b->epsilon) ori2 = 0; - if (ori2 <= 0) { - ori3 = orient3d(pc, pb, pd, searchpt); - if (fabs(ori3 / vol) < b->epsilon) ori3 = 0; - if (ori3 <= 0) { - ori4 = orient3d(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 + 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 = bgm->tetrahedrontraverse(); + searchtet->tet = tetrahedrontraverse(); } // while (searchtet->tet != NULL) + nonregularcount++; // Re-use this counter. } if (searchtet->tet != NULL) { @@ -24824,13 +23294,9 @@ int tetgenmesh::scoutpoint(point searchpt, triface *searchtet, int randflag) // 'iloc' indicates the location of the point w.r.t. 'searchtet'. The size // // is obtained by linear interpolation on the vertices of the tet. // // // -// If 'posflag' is set, only do interpolation when all vertices have a posi- // -// tive value. // -// // /////////////////////////////////////////////////////////////////////////////// -REAL tetgenmesh::getpointmeshsize(point searchpt, triface *searchtet, int iloc, - int posflag) +REAL tetgenmesh::getpointmeshsize(point searchpt, triface *searchtet, int iloc) { point *pts, pa, pb, pc; REAL volume, vol[4], wei[4]; @@ -24842,15 +23308,15 @@ REAL tetgenmesh::getpointmeshsize(point searchpt, triface *searchtet, int iloc, if (iloc == (int) INTETRAHEDRON) { pts = (point *) &(searchtet->tet[4]); assert(pts[3] != dummypoint); - if (!posflag || - ((pts[0][pointmtrindex] > 0) && (pts[1][pointmtrindex] > 0) && - (pts[2][pointmtrindex] > 0) && (pts[3][pointmtrindex] > 0))) { + // 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 = orient3d(pts[0], pts[1], pts[2], pts[3]); - vol[0] = orient3d(searchpt, pts[1], pts[2], pts[3]); - vol[1] = orient3d(pts[0], searchpt, pts[2], pts[3]); - vol[2] = orient3d(pts[0], pts[1], searchpt, pts[3]); - vol[3] = orient3d(pts[0], pts[1], pts[2], searchpt); + 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]); @@ -24860,9 +23326,8 @@ REAL tetgenmesh::getpointmeshsize(point searchpt, triface *searchtet, int iloc, pa = org(*searchtet); pb = dest(*searchtet); pc = apex(*searchtet); - if (!posflag || - ((pa[pointmtrindex] > 0) && (pb[pointmtrindex] > 0) && - (pc[pointmtrindex] > 0))) { + 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); @@ -24874,7 +23339,7 @@ REAL tetgenmesh::getpointmeshsize(point searchpt, triface *searchtet, int iloc, } else if (iloc == (int) ONEDGE) { pa = org(*searchtet); pb = dest(*searchtet); - if (!posflag || ((pa[pointmtrindex] > 0) && (pb[pointmtrindex] > 0))) { + if ((pa[pointmtrindex] > 0) && (pb[pointmtrindex] > 0)) { volume = distance(pa, pb); vol[0] = distance(searchpt, pb); vol[1] = distance(pa, searchpt); @@ -24883,7 +23348,7 @@ REAL tetgenmesh::getpointmeshsize(point searchpt, triface *searchtet, int iloc, } } else if (iloc == (int) ONVERTEX) { pa = org(*searchtet); - if (!posflag || (pa[pointmtrindex] > 0)) { + if (pa[pointmtrindex] > 0) { size = pa[pointmtrindex]; } } @@ -24909,6 +23374,11 @@ void tetgenmesh::interpolatemeshsize() 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. @@ -24919,8 +23389,8 @@ void tetgenmesh::interpolatemeshsize() searchtet.tet = NULL; iloc = bgm->scoutpoint(ploop, &searchtet, 1); // randflag = 1 if (iloc != (int) OUTSIDE) { - // Interpolate the mesh size (posflag = 0) - ploop[pointmtrindex] = bgm->getpointmeshsize(ploop, &searchtet, iloc, 0); + // 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. @@ -24945,152 +23415,470 @@ void tetgenmesh::interpolatemeshsize() 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); } -} -/////////////////////////////////////////////////////////////////////////////// -// // -// insertconstrainedpoints() Insert a list of points into the mesh. // -// // -/////////////////////////////////////////////////////////////////////////////// + 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) { - triface searchtet, spintet; - face checksh, *splitsh; - face checkseg, *splitseg; - point newpt; - insertvertexflags ivf; - REAL *attr, x, y, z, w; - int randflag; - int count, index; - int loc; - int i, j; + point *insertarray, newpt; + REAL x, y, z, w; + int index, attribindex, mtrindex; + int arylen, i, j; if (!b->quiet) { printf("Inserting constrained points ...\n"); } - randflag = 1; // Randomly select start tet for point location. - count = 0; + insertarray = new point[addio->numberofpoints]; + arylen = 0; index = 0; + attribindex = 0; + mtrindex = 0; for (i = 0; i < addio->numberofpoints; i++) { - makepoint(&newpt, VOLVERTEX); - x = newpt[0] = addio->pointlist[index++]; - y = newpt[1] = addio->pointlist[index++]; - z = newpt[2] = addio->pointlist[index++]; + 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 weight. - w = addio->pointattributelist[addio->numberofpointattributes * i]; + // The first point attribute is its weight. + w = newpt[3]; } else { - // No given weight available. - w = 0; + // 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. } - } else { - newpt[3] = 0; } - // Read the add point attributes if current points have attributes. - if ((addio->numberofpointattributes > 0) && - (in->numberofpointattributes > 0)) { - attr = addio->pointattributelist + addio->numberofpointattributes * i; - for (j = 0; j < in->numberofpointattributes; j++) { - if (j < addio->numberofpointattributes) { - newpt[4 + j] = attr[j]; + 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(); } - // Read the point metric tensor. - //for (j = 0; j < in->numberofpointmtrs; j++) { - // pointloop[pointmtrindex + j] = in->pointmtrlist[mtrindex++]; - //} + if (b->verbose > 1) { + printf(" Coarsen %ld oversized points.\n", remptlist->objects); + } + } - // Find the location of the inserted point. - searchtet.tet = NULL; - ivf.iloc = scoutpoint(newpt, &searchtet, randflag); - if (ivf.iloc != (int) OUTSIDE) { - // Found the point. - // Initialize the insertion parameters. - if (b->psc) { - ivf.bowywat = 0; // Do not enlarge the initial cavity. - ivf.validflag = 0; // Do not validate the initial cavity. - } else { - ivf.bowywat = 3; // Use the "Bowyer-Watson" algorithm to form cavity. - ivf.validflag = 1; // Validate the B-W cavity. - } - ivf.lawson = 3; - ivf.rejflag = 0; - ivf.chkencflag = 0; - ivf.sloc = ivf.iloc; - ivf.sbowywat = ivf.bowywat; // Surface mesh options. - ivf.splitbdflag = 1; - ivf.respectbdflag = 1; - ivf.assignmeshsize = 1; - - splitsh = NULL; - splitseg = NULL; - - // Set the right point type. - if (ivf.iloc == (int) ONEDGE) { - tsspivot1(searchtet, checkseg); - if (checkseg.sh != NULL) { - setpointtype(newpt, RIDGEVERTEX); - spivot(checkseg, checksh); - splitsh = &checksh; - splitseg = &checkseg; - } else { - // Check if it is a subface edge. - spintet = searchtet; - while (1) { - tspivot(spintet, checksh); - if (checksh.sh != NULL) { - setpointtype(newpt, FACETVERTEX); - splitsh = &checksh; - break; - } - fnextself(spintet); - if (spintet.tet == searchtet.tet) break; - } - } - } else if (ivf.iloc == (int) ONFACE) { - tspivot(searchtet, checksh); - if (checksh.sh != NULL) { - setpointtype(newpt, FACETVERTEX); - splitsh = &checksh; + // 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); + } +} - // Insert the vertex. - loc = insertvertex(newpt, &searchtet, splitsh, splitseg, &ivf); +void tetgenmesh::meshcoarsening() +{ + arraypool *remptlist; - if (loc == ivf.iloc) { - // The point has been inserted. - lawsonflip3d(newpt, 4, 0, ivf.chkencflag, 0); - count++; + 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 { - if (!b->quiet) { - printf("Warning: Failed to insert point #%d. Ignored.\n", i); + ms = remptlist->objects; + if (nit > 0) { + nit--; } - pointdealloc(newpt); } + autofliplinklevel+=b->fliplinklevelinc; } else { - if (!b->quiet) { - printf("Warning: Can't locate add point #%d. Ignored.\n", i); - } - pointdealloc(newpt); + // All points are removed. + break; } - } // i + } // while (1) - if (b->verbose) { - printf(" Inserted %d of %d vertices.\n", count, addio->numberofpoints); + if (remptlist->objects > 0l) { + if (b->verbose) { + printf(" %ld points are not removed !\n", remptlist->objects); + } } + + b->fliplinklevel = bak_fliplinklevel; + delete remptlist; } //// //// @@ -25103,557 +23891,198 @@ void tetgenmesh::insertconstrainedpoints(tetgenio *addio) /////////////////////////////////////////////////////////////////////////////// // // -// marksharpsegments() Mark sharp segments. // -// // -// All segments are initialized as type NSHARP. // +// makefacetverticesmap() Create a map from facet to its vertices. // // // -// A segment is SHARP if there are two facets intersecting at it with an // -// internal dihedral angle (*) less than an angle \theta. // -// // -// A theoretical value of \theta is arccos(1/3) \approx 70.54 degree. It is // -// possible to relax it in practice. Here we choose \theta = 65 degree. // -// // -// The minimum dihedral angle between facets (minfacetdihed) is calulcated. // +// All facets will be indexed (starting from 0). The map is saved in two // +// global arrays: 'idx2facetlist' and 'facetverticeslist'. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::marksharpsegments() +void tetgenmesh::makefacetverticesmap() { - triface adjtet; - face startsh, spinsh, neighsh; - face segloop, nextseg, prevseg; - point eorg, edest; - REAL ang, smallang; - bool issharp; - int sharpcount; - - // For storing extremely small dihedral angle. - face *parysh, *parysh1; - REAL exsmallang; - int exsharpcount; + 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 > 0) { - printf(" Marking sharp segments.\n"); + if (b->verbose) { + printf(" Creating the facet vertices map.\n"); } - minfacetdihed = PI; - smallang = 65.0 * PI / 180.0; // 65 degree. - exsmallang = 15.0 * PI / 180.0; // 15 degree. - sharpcount = exsharpcount = 0; + facetvertexlist = new arraypool(sizeof(arraypool *), 10); + facetindex = totalvertices = 0; - // 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); - while (segloop.sh != (shellface *) NULL) { - segloop.shver = 0; - senext2(segloop, prevseg); - spivotself(prevseg); - if (prevseg.sh == NULL) { - // Operate on this seg s. - issharp = false; - spivot(segloop, startsh); - if (startsh.sh != NULL) { - // First check if two facets form an acute dihedral angle at s. - eorg = sorg(segloop); - edest = sdest(segloop); - spinsh = startsh; - while (1) { - if (sorg(spinsh) != eorg) sesymself(spinsh); - // Only do test when the spinsh is faceing inward. - stpivot(spinsh, adjtet); - if (adjtet.tet != NULL) { - if (!ishulltet(adjtet)) { - // Get the subface on the adjacent facet. - spivot(spinsh, neighsh); - // Do not calculate if it is self-bonded. - if ((neighsh.sh != NULL) && (neighsh.sh != spinsh.sh)) { - // Calculate the dihedral angle between the two subfaces. - ang = facedihedral(eorg, edest, sapex(spinsh), sapex(neighsh)); - // Only do check if a sharp angle has not been found. - if (!issharp) issharp = (ang < smallang); - // Remember the smallest facet dihedral angle. - minfacetdihed = minfacetdihed < ang ? minfacetdihed : ang; - if (ang < exsmallang) { - // It's an extremely small dihedral angle. - // Mark the two facets. - // To avoid too many Steiner points, do not refine them. - if (shelltype(spinsh) != SHARP) { - setshelltype(spinsh, SHARP); - cavesegshlist->newindex((void **) &parysh); - *parysh = spinsh; - } - if (shelltype(neighsh) != SHARP) { - setshelltype(neighsh, SHARP); - cavesegshlist->newindex((void **) &parysh); - *parysh = neighsh; - } - exsharpcount++; + 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; } } - } - } - // Go to the next facet. - spivotself(spinsh); - if (spinsh.sh == NULL) break; // A single subface case. - if (spinsh.sh == startsh.sh) break; - } - } // if (startsh.sh != NULL) - if (issharp) { - if (b->verbose > 2) { - printf(" Mark a sharp segment (%d, %d).\n", - pointmark(eorg), pointmark(edest)); - } - setshelltype(segloop, SHARP); - // The endpoint of this segment is acute. - if (pointtype(eorg) == RIDGEVERTEX) { - setpointtype(eorg, ACUTEVERTEX); - } else { - assert(pointtype(eorg) == ACUTEVERTEX); // SELF_CHECK - } - // Set the type for all subsegments at forwards. - edest = sdest(segloop); - senext(segloop, nextseg); - spivotself(nextseg); - while (nextseg.sh != NULL) { - setshelltype(nextseg, SHARP); - // Adjust the direction of nextseg. - 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); - } - // The endpoint of this segment is acute. - if (pointtype(edest) == RIDGEVERTEX) { - setpointtype(edest, ACUTEVERTEX); - } else { - assert(pointtype(edest) == ACUTEVERTEX); // SELF_CHECK - } - sharpcount++; - } // if (issharp) - } // if (prevseg.sh == NULL) - segloop.sh = shellfacetraverse(subsegs); - } - - // Mark all facets at extremely small dihedral angles. - if (cavesegshlist->objects > 0) { - for (i = 0; i < cavesegshlist->objects; i++) { - parysh = (face *) fastlookup(cavesegshlist, i); - caveshlist->newindex((void **) &parysh1); - *parysh1 = *parysh; - for (j = 0; j < caveshlist->objects; j++) { - parysh1 = (face *) fastlookup(caveshlist, j); - spinsh = *parysh1; - for (k = 0; k < 3; k++) { - sspivot(spinsh, nextseg); - if (nextseg.sh == NULL) { - spivot(spinsh, neighsh); - if (shelltype(neighsh) != SHARP) { - setshelltype(neighsh, SHARP); + sinfect(neighsh); caveshlist->newindex((void **) &parysh1); *parysh1 = neighsh; } } - senextself(spinsh); - } // k - } // j + 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(); - } // i - cavesegshlist->restart(); - } // if (cavesegshlist->objects > 0) + // 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) { - if (sharpcount > 0) { - printf(" Found %d (%d) sharp segments.\n", sharpcount, exsharpcount); + 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++; } - printf(" Minimum fac-fac angle = %g.\n", minfacetdihed / PI * 180.0); } + assert(k == totalvertices); + + // Free the lists. + for (i = 0; i < facetvertexlist->objects; i++) { + paryvertlist = (arraypool **) fastlookup(facetvertexlist, i); + vertlist = *paryvertlist; + delete vertlist; + } + delete facetvertexlist; } /////////////////////////////////////////////////////////////////////////////// // // -// decidefeaturepointsizes() Calculate sizes for all feature points. // -// // -// A feature point is either an acute vertex or a Steiner point on a sharp // -// segment. Each feature point p will be protected by a ball whose radius // -// is called its "feature size". // -// // -// NOTE: we should have already marked all features points in the two func- // -// tions: markacutevertices() and marksharpsegments(). Each feature point // -// has the type ACUTEVERTEX or FREESEGVERTEX. // -// // -// The feature size of a vertex is the minimum of the following sizes: // -// (0) the (approximated) local feature size (the distance to the second // -// nearest boundary) of the vertex; -// (1) the value specified in .mtr file (-m option); // -// (2) the cubic root of a fixed maximal volume constraint ('-a__'); // -// (3) the cubic root of a maximal volume constraint in a region ('-a'); // -// (4) the square root of a maximal area constraint in a .var file; // -// (5) a maximal length constraint in a .var file; // -// // -// If 'b->nobisect' ('-Y' option) is set, every input vertex has a feature // -// size. // -// // -// The feature size of a Steiner point is linearly interpolated from its adj-// -// acent vertices which belong to the "carrier" (the boundary of the lowrest // -// dimension) of this Steiner point. For example, a Steiner point on a seg- // -// ment gets its size from the two endpoints of the segment. // +// Check whether two segments, or a segment and a facet, or two facets are // +// adjacent to each other. // // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::decidefeaturepointsizes() +int tetgenmesh::segsegadjacent(face *seg1, face *seg2) { - arraypool *tetlist, *verlist; - triface starttet, *parytet; - face checksh, parentsh, shloop; - face checkseg, prevseg, nextseg, testseg; - point ploop, adjpt, e1, e2, *parypt; - REAL lfs_0, lfs_1, lfs_2; - REAL len, vol, maxlen = 0.0, varlen; - REAL ang, a, a1, a2, a3, prjpt[3], n[3]; - int featureflag, featurecount; - int i, j; + int segidx1 = getfacetindex(*seg1); + int segidx2 = getfacetindex(*seg2); - if (b->verbose > 0) { - printf(" Deciding feature-point sizes.\n"); - } + if (segidx1 == segidx2) return 0; - // Initialize working lists. - tetlist = cavetetlist; - verlist = cavetetvertlist; + point pa1 = segmentendpointslist[segidx1 * 2]; + point pb1 = segmentendpointslist[segidx1 * 2 + 1]; + point pa2 = segmentendpointslist[segidx2 * 2]; + point pb2 = segmentendpointslist[segidx2 * 2 + 1]; - if (b->fixedvolume) { - // A fixed volume constraint is imposed. This gives an upper bound of - // the maximal radius of the protect ball of a vertex. - maxlen = pow(6.0 * b->maxvolume, 1.0 / 3.0); + if ((pa1 == pa2) || (pa1 == pb2) || (pb1 == pa2) || (pb1 == pb2)) { + return 1; } + return 0; +} - // First only assign a size of p if p is not a Steiner point. The size of - // a Steiner point will be interpolated later from the endpoints of the - // segment on which it lies. - featurecount = 0; - points->traversalinit(); - ploop = pointtraverse(); - while (ploop != (point) NULL) { - // Check if it is a feature point. - featureflag = 0; - // Only calculate the size if it has a size zero. - // The point may already has a positive size (-m option). - if (ploop[pointmtrindex] == 0) { - if (pointtype(ploop) == ACUTEVERTEX) { - featureflag = 1; - } else { - if (b->nobisect) { // '-Y' option - if ((pointtype(ploop) == RIDGEVERTEX) || - (pointtype(ploop) == FACETVERTEX) || - (pointtype(ploop) == VOLVERTEX)) { - featureflag = 1; // It is an input vertex. - } - } - } - } - if (featureflag) { - // Form star(p). - getvertexstar(1, ploop, tetlist, verlist, NULL); - // Calculate lfs_0(p), i.e., the smallest distance from p to a vertex. - // We approximate it by taking the distance of p to its nearest - // vertex in Link(p). - lfs_0 = longest; - for (i = 0; i < verlist->objects; i++) { - parypt = (point *) fastlookup(verlist, i); - adjpt = * parypt; - if (adjpt == dummypoint) { - continue; // Skip a dummypoint. - } - if (pointtype(adjpt) == FREESEGVERTEX) { - // A Steiner point. Get the subsegment. - sdecode(point2sh(adjpt), checkseg); - assert(checkseg.sh != NULL); - checkseg.shver = 0; - if (sdest(checkseg) != adjpt) { - sesymself(checkseg); - } - assert(sdest(checkseg) == adjpt); - // It is possible that the original segment of 'adjpt' does not - // have 'ploop' as an endpoint. - if (sorg(checkseg) == ploop) { - // Find the other end point of the original segment. - nextseg = checkseg; - while (1) { - senext(nextseg, testseg); - spivotself(testseg); - if (testseg.sh == NULL) break; - // Go to the next subseg. - nextseg = testseg; - // Adjust the direction of the nextseg. - nextseg.shver = 0; - if (sorg(nextseg) != adjpt) { - sesymself(nextseg); - } - assert(sorg(nextseg) == adjpt); - adjpt = sdest(nextseg); - } - } - } else if (pointtype(adjpt) == FREEFACETVERTEX) { - // Ignore a Steiner point on facet. - continue; - } else if (pointtype(adjpt) == FREEVOLVERTEX) { - // Ignore a Steiner point in volume. - continue; - } - len = distance(ploop, adjpt); - if (lfs_0 > len) lfs_0 = len; - } // i - assert(lfs_0 < longest); // SELF_CHECK - ploop[pointmtrindex] = lfs_0; - // Calculate lfs_1(p), i.e., the smallest distance from p to a segment. - // We approximate it by restricting the segments in Link(p). - lfs_1 = lfs_0; - for (i = 0; i < tetlist->objects; i++) { - parytet = (triface *) fastlookup(tetlist, i); - for (j = 0; j < 3; j++) { - tsspivot1(*parytet, checkseg); - if (checkseg.sh != NULL) { - e1 = sorg(checkseg); - e2 = sdest(checkseg); - // Only do calculation if the projeciton of 'p' lies inside the - // segment [e1, e2]. - ang = interiorangle(ploop, e1, e2, NULL); - ang *= 2.0; - if (ang > PI) { - len = shortdistance(ploop, e1, e2); - if (lfs_1 > len) { - lfs_1 = len; - } - } - } - enextself(*parytet); - } // j - } // i - if (ploop[pointmtrindex] > lfs_1) { - ploop[pointmtrindex] = lfs_1; - } - // Calculate lfs_2(p), i.e., the smallest distance from p to a facet. - // We approximate it by restricting the facets in Link(p). - lfs_2 = lfs_0; - for (i = 0; i < tetlist->objects; i++) { - parytet = (triface *) fastlookup(tetlist, i); - tspivot(*parytet, checksh); - if (checksh.sh != NULL) { - adjpt = sorg(checksh); - e1 = sdest(checksh); - e2 = sapex(checksh); - // Only do calculation if the projeciton of 'p' lies inside the - // subface [adjpt, e1, e2]. - projpt2face(ploop, adjpt, e1, e2, prjpt); - facenormal(adjpt, e1, e2, n, 1, NULL); - a = sqrt(dot(n, n)); // area of [adjpt, e1, e2]. - if (a > 0) { - facenormal(adjpt, e1, prjpt, n, 1, NULL); - a1 = sqrt(dot(n, n)); - facenormal(e1, e2, prjpt, n, 1, NULL); - a2 = sqrt(dot(n, n)); - facenormal(e2, adjpt, prjpt, n, 1, NULL); - a3 = sqrt(dot(n, n)); - if ((fabs(a1 + a2 + a3 - a) / a) < b->epsilon) { - len = distance(ploop, prjpt); - if (lfs_2 > len) { - lfs_2 = len; - } - } - } else { - assert(0); // a degenerate triangle. - } // if (a > 0) - } - } - if (ploop[pointmtrindex] > lfs_2) { - ploop[pointmtrindex] = lfs_2; - } - if (b->fixedvolume) { - // A fixed volume constraint is imposed. Adjust H(p) <= maxlen. - if (ploop[pointmtrindex] > maxlen) { - ploop[pointmtrindex] = maxlen; - } - } - if (b->varvolume) { - // Variant volume constraints are imposed. Adjust H(p) <= varlen. - for (i = 0; i < tetlist->objects; i++) { - parytet = (triface *) fastlookup(tetlist, i); - starttet = *parytet; - vol = volumebound(starttet.tet); - if (vol > 0.0) { - varlen = pow(6 * vol, 1.0 / 3.0); - if (ploop[pointmtrindex] > varlen) { - ploop[pointmtrindex] = varlen; - } - } - } - } - // The size is calculated. - assert(ploop[pointmtrindex] > 0); // SELF_CHECK - // Clear working lists. - tetlist->restart(); - verlist->restart(); - featurecount++; - } // if (featureflag) - ploop = pointtraverse(); - } +int tetgenmesh::segfacetadjacent(face *subseg, face *subsh) +{ + int segidx = getfacetindex(*subseg); + point pa = segmentendpointslist[segidx * 2]; + point pb = segmentendpointslist[segidx * 2 + 1]; - if (b->verbose) { - printf(" %d feature points.\n", featurecount); - } + pinfect(pa); + pinfect(pb); - // Second only assign sizes for all Steiner points. A Steiner point p - // inserted on a sharp segment s is assigned a size by interpolating - // the sizes of the original endpoints of s. - featurecount = 0; - points->traversalinit(); - ploop = pointtraverse(); - while (ploop != (point) NULL) { - if (ploop[pointmtrindex] == 0.0) { - if (pointtype(ploop) == FREESEGVERTEX) { - // A Steiner point on segment. - featureflag = 0; - sdecode(point2sh(ploop), checkseg); - assert(checkseg.sh != NULL); - checkseg.shver = 0; - e1 = farsorg(checkseg); // The origin of this seg. - e2 = farsdest(checkseg); // The dest of this seg. - if (b->nobisect) { // '-Y' option. - assert(e1[pointmtrindex] > 0); // SELF_CHECK - assert(e2[pointmtrindex] > 0); // SELF_CHECK - featureflag = 1; - } else { - if ((e1[pointmtrindex] > 0) && (e2[pointmtrindex] > 0)) { - featureflag = 1; - } - } - if (featureflag) { - len = distance(e1, e2); - lfs_0 = distance(e1, ploop); // Re-use lfs_0. - ploop[pointmtrindex] = e1[pointmtrindex] - + (lfs_0 / len) * (e2[pointmtrindex] - e1[pointmtrindex]); - featurecount++; - } // if (featureflag) - } else if (pointtype(ploop) == FREEFACETVERTEX) { - if (b->nobisect) { // -Y option. - // Collect vertices in the Star(p) which are also in the facet - // containing p. - point2shorg(ploop, parentsh); - checksh = parentsh; - while (1) { - assert(sorg(checksh) == ploop); - adjpt = sdest(checksh); - // Collect this vertex. - verlist->newindex((void **) &parypt); - *parypt = adjpt; - // Go to the next subface at p. (counterclockwise) - senext2self(checksh); - spivotself(checksh); - assert(checksh.sh != NULL); - if (checksh.sh == parentsh.sh) break; - if (sorg(checksh) != ploop) sesymself(checksh); - } - assert(verlist->objects > 0); - // Using Shepard interpolation (p=1) to interpolate the size for 'p'. - // Re-use len, lfs_0, lfs_1, lfs_2; - lfs_1 = lfs_2 = 0; - for (i = 0; i < verlist->objects; i++) { - parypt = (point *) fastlookup(verlist, i); - adjpt = *parypt; - if (adjpt[pointmtrindex] > 0) { - len = distance(adjpt, ploop); - lfs_0 = 1.0 / len; - lfs_1 += lfs_0 * adjpt[pointmtrindex]; - lfs_2 += lfs_0; - } - } - assert(lfs_2 > 0); - ploop[pointmtrindex] = lfs_1 / lfs_2; - verlist->restart(); - featurecount++; - } // if (b->nobisect) - } else if (pointtype(ploop) == FREEVOLVERTEX) { - if (b->nobisect) { // -Y option. - getvertexstar(1, ploop, tetlist, verlist, NULL); - // Using Shepard interpolation to interpolate the size for 'p'. - // Re-use len, lfs_0, lfs_1, lfs_2; - lfs_1 = lfs_2 = 0; - for (i = 0; i < verlist->objects; i++) { - parypt = (point *) fastlookup(verlist, i); - adjpt = *parypt; - if (adjpt[pointmtrindex] > 0) { - len = distance(adjpt, ploop); - lfs_0 = 1.0 / len; - lfs_1 += lfs_0 * adjpt[pointmtrindex]; - lfs_2 += lfs_0; - } - } - assert(lfs_2 > 0); - ploop[pointmtrindex] = lfs_1 / lfs_2; - tetlist->restart(); - verlist->restart(); - featurecount++; - } // if (b->nobisect) - } - } // if (ploop[pointmtrindex] == 0.0) - ploop = pointtraverse(); + 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]); } - if (b->verbose && (featurecount > 0)) { - printf(" %d Steiner feature points.\n", featurecount); + for (i = idx2facetlist[fidx2]; i < idx2facetlist[fidx2+1]; i++) { + if (pinfected(facetverticeslist[i])) count++; } - if (checkconstraints) { - // A .var file exists. Adjust feature sizes. - if (in->facetconstraintlist) { - // Have facet area constrains. - subfaces->traversalinit(); - shloop.sh = shellfacetraverse(subfaces); - while (shloop.sh != (shellface *) NULL) { - varlen = areabound(shloop); - if (varlen > 0.0) { - // Check if the three corners are feature points. - varlen = sqrt(varlen); - for (j = 0; j < 3; j++) { - ploop = (point) shloop.sh[3 + j]; - if (ploop[pointmtrindex] > 0) { - if (ploop[pointmtrindex] > varlen) { - ploop[pointmtrindex] = varlen; - } - } - } // j - } - shloop.sh = shellfacetraverse(subfaces); - } - } - if (in->segmentconstraintlist) { - // Have facet area constrains. - subsegs->traversalinit(); - shloop.sh = shellfacetraverse(subsegs); - while (shloop.sh != (shellface *) NULL) { - varlen = areabound(shloop); - if (varlen > 0.0) { - // Check if the two endpoints are feature points. - for (j = 0; j < 2; j++) { - ploop = (point) shloop.sh[3 + j]; - if (ploop[pointmtrindex] > 0.0) { - if (ploop[pointmtrindex] > varlen) { - ploop[pointmtrindex] = varlen; - } - } - } // j - } - shloop.sh = shellfacetraverse(subsegs); - } - } - } // if (checkconstraints) + // Uninfect the vertices. + for (i = idx2facetlist[fidx1]; i < idx2facetlist[fidx1+1]; i++) { + puninfect(facetverticeslist[i]); + } + + return count > 0; } /////////////////////////////////////////////////////////////////////////////// @@ -25664,22 +24093,24 @@ void tetgenmesh::decidefeaturepointsizes() int tetgenmesh::checkseg4encroach(point pa, point pb, point checkpt) { - REAL ang; - REAL prjpt[3], u, v, t; - // Check if the point lies inside the diametrical sphere of this seg. - ang = interiorangle(checkpt, pa, pb, NULL); - ang *= 2.0; // Compare it to PI/2 (90 degree). + 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 (ang > PI) { + if (dot(v1, v2) < 0) { // Inside. - if (b->metric || b->nobisect) { // -m or -Y option. + if (b->metric) { // -m option. if ((pa[pointmtrindex] > 0) && (pb[pointmtrindex] > 0)) { - // In this case, we're sure that the projection of 'checkpt' lies - // inside the segment [a,b]. Check if 'checkpt' lies inside the - // protecting region of this seg. + // The projection of 'checkpt' lies inside the segment [a,b]. + REAL prjpt[3], u, v, t; projpt2edge(checkpt, pa, pb, prjpt); - // Get the mesh size at the location 'prjpt'. + // Interoplate the mesh size at the location 'prjpt'. u = distance(pa, pb); v = distance(pa, prjpt); t = v / u; @@ -25707,6 +24138,7 @@ int tetgenmesh::checkseg4encroach(point pa, point pb, point checkpt) // 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 // @@ -25716,21 +24148,11 @@ int tetgenmesh::checkseg4encroach(point pa, point pb, point checkpt) int tetgenmesh::checkseg4split(face *chkseg, point& encpt, int& qflag) { - triface searchtet, spintet; - point forg, fdest, eapex; - REAL ccent[3], len, r, d, diff; + REAL ccent[3], len, r; int i; - REAL ti, tj, t, midpt[3]; - REAL ang; - int eid; - - forg = sorg(*chkseg); - fdest = sdest(*chkseg); - - if (b->verbose > 2) { - printf(" Check segment (%d, %d)\n", pointmark(forg), pointmark(fdest)); - } + point forg = sorg(*chkseg); + point fdest = sdest(*chkseg); // Initialize the return values. encpt = NULL; @@ -25745,21 +24167,13 @@ int tetgenmesh::checkseg4split(face *chkseg, point& encpt, int& qflag) // First check its quality. if (checkconstraints && (areabound(*chkseg) > 0.0)) { if (len > areabound(*chkseg)) { - if (b->verbose > 2) { - printf(" has too large size, len = %g (> %g)\n", len, - areabound(*chkseg)); - } qflag = 1; return 1; } } - if (b->fixedvolume) { // if (b->varvolume || b->fixedvolume) { + if (b->fixedvolume) { if ((len * len * len) > b->maxvolume) { - if (b->verbose > 2) { - printf(" has too large size, len^3 = %g (> %g)\n", len*len*len, - b->maxvolume); - } qflag = 1; return 1; } @@ -25774,33 +24188,15 @@ int tetgenmesh::checkseg4split(face *chkseg, point& encpt, int& qflag) } } - if (b->psc) { - // Check if it satisfies the approximation requirement. - eid = shellmark(*chkseg); - if ((pointtype(forg) == ACUTEVERTEX)||(pointtype(forg) == RIDGEVERTEX)) { - ti = in->getvertexparamonedge(in->geomhandle, pointmark(forg), eid); - } else { - ti = pointgeomuv(forg, 0); - } - if ((pointtype(fdest) == ACUTEVERTEX)||(pointtype(fdest) == RIDGEVERTEX)) { - tj = in->getvertexparamonedge(in->geomhandle, pointmark(fdest), eid); - } else { - tj = pointgeomuv(fdest, 0); - } - t = 0.5 * (ti + tj); - in->getsteineronedge(in->geomhandle, eid, t, midpt); - ang = interiorangle(midpt, forg, fdest, NULL) / PI * 180.0; - if (ang < b->facet_ang_tol) { - // Refine this segment. - if (b->verbose > 2) { - printf(" has bad approx, ang = %g\n", ang); - } - qflag = 1; - return 1; - } - } // if (b->psc) // 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) { @@ -25811,8 +24207,21 @@ int tetgenmesh::checkseg4split(face *chkseg, point& encpt, int& qflag) if (fabs(diff) / r < b->epsilon) diff = 0.0; // Rounding. if (diff < 0) { // This segment is encroached by eapex. - encpt = eapex; - break; + 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); @@ -25820,9 +24229,6 @@ int tetgenmesh::checkseg4split(face *chkseg, point& encpt, int& qflag) } // while (1) if (encpt != NULL) { - if (b->verbose > 2) { - printf(" is encroached by %d\n", pointmark(encpt)); - } return 1; } @@ -25841,103 +24247,139 @@ int tetgenmesh::checkseg4split(face *chkseg, point& encpt, int& qflag) // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::splitsegment(face *splitseg, point encpt, int qflag, +int tetgenmesh::splitsegment(face *splitseg, point encpt, REAL rrp, + point encpt1, point encpt2, int qflag, int chkencflag) { - triface searchtet; - face searchsh; - point newpt, pa, pb; - insertvertexflags ivf; - REAL len; //, len1; - int loc; - //int i; - - pa = sorg(*splitseg); - pb = sdest(*splitseg); - len = distance(pa, pb); + point pa = sorg(*splitseg); + point pb = sdest(*splitseg); - if (b->verbose > 2) { - printf(" Split segment (%d, %d).\n", pointmark(pa), pointmark(pb)); - } - if (qflag == 0) { - if (shelltype(*splitseg) == SHARP) { - // Do not split it (due to a very small angle) even it is encroached. - // Avoid creating too many Steiner points. - return 0; + 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; + } + } } } - // Quickly check if we CAN split this segment. - if ((encpt == NULL) && (qflag == 0)) { - // Do not split this segment if the length is smaller than the mesh - // size at one of its endpoints. - if ((len < pa[pointmtrindex]) || (len < pb[pointmtrindex])) { - 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. - // Parameters are chosen as follows: - // - bowywat = 3, preserve subsegments and subfaces; - // - flipflag = 3, check star & link facets for flipping; - // - rejflag = 0, do insertion even if it encoraches upon - // other subsegments or subfaces. + // Split the segment by the Bowyer-Watson algorithm. sstpivot1(*splitseg, searchtet); ivf.iloc = (int) ONEDGE; - if (b->psc) { - ivf.bowywat = 0; // Do not enlarge the initial cavity. - ivf.validflag = 0; // Do not validate the initial cavity. - } else { - ivf.bowywat = 3; // Use the "Bowyer-Watson" algorithm to form cavity. - ivf.validflag = 1; // Validate the B-W cavity. - } - ivf.lawson = 3; - ivf.rejflag = 0; - if ((encpt == NULL) && (qflag == 0)) { - // Do not insert the point if it lies inside some protecting balls. - ivf.rejflag |= 4; + // 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 = ivf.iloc; - ivf.sbowywat = ivf.bowywat; // Surface mesh options. + ivf.sloc = (int) INSTAR; // ivf.iloc; + ivf.sbowywat = 3; // ivf.bowywat; // Surface mesh options. ivf.splitbdflag = 1; ivf.respectbdflag = 1; - ivf.assignmeshsize = 1; - loc = insertvertex(newpt, &searchtet, &searchsh, splitseg, &ivf); - - // The new vertex should not too close to an existing point. - if (loc == (int) NEARVERTEX) { - outnodes(0); - outsubfaces(0); - outsubsegments(0); - assert(0); - } else if (loc == ENCVERTEX) { - // The point lies in some protecting balls. Rejected. - pointdealloc(newpt); - } else if (loc == (int) BADELEMENT) { - // Failed to create a valid sub-cavity in surface mesh. - pointdealloc(newpt); - //prob_subseg_count++; - } else if (loc == (int) ONEDGE) { - // Flip not locally Delaunay link facets by the 'Lawson's algo'. - lawsonflip3d(newpt, 4, 0, chkencflag, 0); + 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 { - // The vertex was not inserted. For unknown reasons. - //pointdealloc(newpt); - assert(0); + // Point is not inserted. + pointdealloc(newpt); + return 0; } - - // Should not be here. - return 0; } /////////////////////////////////////////////////////////////////////////////// @@ -25948,7 +24390,7 @@ int tetgenmesh::splitsegment(face *splitseg, point encpt, int qflag, void tetgenmesh::repairencsegs(int chkencflag) { - badface *bface; + face *bface; point encpt = NULL; int qflag = 0; @@ -25956,20 +24398,25 @@ void tetgenmesh::repairencsegs(int chkencflag) // if an unlimited number of Steiner points is allowed. while ((badsubsegs->items > 0) && (steinerleft != 0)) { badsubsegs->traversalinit(); - bface = badfacetraverse(badsubsegs); + bface = (face *) badsubsegs->traverse(); while ((bface != NULL) && (steinerleft != 0)) { - // A queued segment may have been deleted (split). - if (bface->ss.sh[3] != NULL) { - // A queued segment may have been processed. - if (smarktest2ed(bface->ss)) { - sunmarktest2(bface->ss); - if (checkseg4split(&(bface->ss), encpt, qflag)) { - splitsegment(&(bface->ss), encpt, qflag, chkencflag); + // 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); } - badfacedealloc(badsubsegs, bface); // Remove this entry from list. - bface = badfacetraverse(badsubsegs); + bface = (face *) badsubsegs->traverse(); } } @@ -25982,19 +24429,37 @@ void tetgenmesh::repairencsegs(int chkencflag) assert(0); // Unknown case. } badsubsegs->traversalinit(); - bface = badfacetraverse(badsubsegs); + bface = (face *) badsubsegs->traverse(); while (bface != NULL) { - if (bface->ss.sh[3] != NULL) { - if (smarktest2ed(bface->ss)) { - sunmarktest2(bface->ss); + // Skip a deleleted element. + if (bface->shver >= 0) { + if ((bface->sh != NULL) && (bface->sh[3] != NULL)) { + if (smarktest2ed(*bface)) { + sunmarktest2(*bface); + } } } - bface = badfacetraverse(badsubsegs); + 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. // @@ -26005,8 +24470,6 @@ int tetgenmesh::checkfac4encroach(point pa, point pb, point pc, point checkpt, REAL* cent, REAL* r) { REAL rd, len; - REAL prjpt[3], n[3]; - REAL a, a1, a2, a3; circumsphere(pa, pb, pc, NULL, cent, &rd); assert(rd != 0); @@ -26015,10 +24478,12 @@ int tetgenmesh::checkfac4encroach(point pa, point pb, point pc, point checkpt, if (len < rd) { // The point lies inside the circumsphere of this face. - if (b->metric || b->nobisect) { // -m or -Y option. + 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); @@ -26040,10 +24505,6 @@ int tetgenmesh::checkfac4encroach(point pa, point pb, point pc, point checkpt, if (len < rd) { return 1; // Encroached. } - } else { - // The projection lies outside the face. - // In this case, 'p' must close to another face or a segment than - // to this one. We ignore this boundary face. } } else { return 1; // No protecting ball. Encroached. @@ -26074,14 +24535,10 @@ int tetgenmesh::checkfac4encroach(point pa, point pb, point pc, point checkpt, int tetgenmesh::checkfac4split(face *chkfac, point& encpt, int& qflag, REAL *cent) { - triface searchtet; - face checksh; // *parysh; - face checkseg; point pa, pb, pc; - REAL area, rd, len, sintheta; + REAL area, rd, len; REAL A[4][4], rhs[4], D; int indx[4]; - REAL elen[3]; int i; encpt = NULL; @@ -26091,11 +24548,6 @@ int tetgenmesh::checkfac4split(face *chkfac, point& encpt, int& qflag, pb = sdest(*chkfac); pc = sapex(*chkfac); - if (b->verbose > 2) { - printf(" Check subface (%d, %d, %d)\n", pointmark(pa), - pointmark(pb), pointmark(pc)); - } - // Compute the coefficient matrix A (3x3). A[0][0] = pb[0] - pa[0]; A[0][1] = pb[1] - pa[1]; @@ -26108,102 +24560,97 @@ int tetgenmesh::checkfac4split(face *chkfac, point& encpt, int& qflag, area = 0.5 * sqrt(dot(A[2], A[2])); // The area of [a,b,c]. // Compute the right hand side vector b (3x1). - elen[0] = dot(A[0], A[0]); // edge [a,b] - elen[1] = dot(A[1], A[1]); // edge [a,c] - rhs[0] = 0.5 * elen[0]; - rhs[1] = 0.5 * elen[1]; + 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)) { - 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 (b->verbose > 2) { - printf(" circent: (%g, %g, %g)\n", cent[0], cent[1], cent[2]); - printf(" cirradi: %g\n", rd); - } + // pivoting and backward and forward substitute. + if (!lu_decmp(A, 3, indx, &D, 0)) { + // A degenerate triangle. + assert(0); + } - // Check the quality (radius-edge ratio) of this subface. - // Re-use variables 'A', 'rhs', and 'D'. - A[2][0] = pb[0] - pc[0]; - A[2][1] = pb[1] - pc[1]; - A[2][2] = pb[2] - pc[2]; - elen[2] = dot(A[2], A[2]); // edge [b,c] - // Get the shortest edge length in 'D'. - D = elen[0]; // edge [a,b] - for (i = 1; i < 3; i++) { - if (D > elen[i]) D = elen[i]; + 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; } + } - - D = sqrt(D); - if (b->verbose > 2) { - printf(" shortest edge length = %g\n", D); + if (b->fixedvolume) { + if ((area * sqrt(area)) > b->maxvolume) { + qflag = 1; + return 1; } + } - rhs[3] = rd / D; // The radius-edge ratio. - - // Check if this subface is nearly degenerate. - sintheta = 1.0 / (2.0 * rhs[3]); - if (sintheta < sintheta_tol) { - // Do not split this subface. Save it in list. - if (b->verbose > 1) { - printf(" !! A degenerated subface, theta = %g (deg)\n", - asin(sintheta) / PI * 180.0); + 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; } - return 0; // Do not split a degenerated subface. } - - if (checkconstraints && (areabound(*chkfac) > 0.0)) { - // Check if the subface has too big area. - if (area > areabound(*chkfac)) { - if (b->verbose > 2) { - printf(" has too big area: %g (> %g)\n", area, - areabound(*chkfac)); - } + 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; - } + 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 (b->verbose > 2) { - printf(" is encroached by point %d\n", - pointmark(oppo(searchtet))); - } + // 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); - return 1; + } else { + if (len < smlen) { + smlen = len; + encpt = oppo(searchtet); + } } + //return 1; } - sesymself(*chkfac); } - } else { - assert(0); - } // if (!lu_decomp) + sesymself(*chkfac); + } - return 0; + return encpt != NULL; //return 0; } /////////////////////////////////////////////////////////////////////////////// @@ -26212,158 +24659,192 @@ int tetgenmesh::checkfac4split(face *chkfac, point& encpt, int& qflag, // // // 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- // -// ments. Instead, one of the encroached segments is split. It is possible // -// that none of the encorached segments can be split. // +// 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 encorached segment (= 1) or in- // -// side the facet (= 2). // +// (= 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, int qflag, - REAL *ccent, int chkencflag) +int tetgenmesh::splitsubface(face *splitfac, point encpt, point encpt1, + int qflag, REAL *ccent, int chkencflag) { - badface *bface; - triface searchtet; - face searchsh; - face checkseg, *paryseg; - point newpt, pa, pb, pc; - insertvertexflags ivf; - REAL rd; - int splitflag; - int loc; - int i; - + point pa = sorg(*splitfac); + point pb = sdest(*splitfac); + point pc = sapex(*splitfac); - pa = sorg(*splitfac); - pb = sdest(*splitfac); - pc = sapex(*splitfac); - if (b->verbose > 2) { - printf(" Split subface (%d, %d, %d).\n", pointmark(pa), pointmark(pb), - pointmark(pc)); - } - - // Quickly check if we CAN split this subface. - if (qflag == 0) { - // Do not split this subface if it forms a very small dihedral with - // another facet. Avoid creating too many Steiner points. - if (shelltype(*splitfac) == SHARP) { - return 0; - } - // Do not split this subface if the 'ccent' lies inside the protect balls - // of one of its vertices. - rd = distance(ccent, pa); - if ((rd <= pa[pointmtrindex]) || (rd <= pb[pointmtrindex]) || - (rd <= pc[pointmtrindex])) { - if (b->verbose > 2) { - printf(" Encroaching a protecting ball. Rejected.\n"); + 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 (0) { - } else { - // Split the subface at its circumcenter. - for (i = 0; i < 3; i++) newpt[i] = ccent[i]; - // 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)) { - // Insert this point. - } else { - pointdealloc(newpt); - return 0; - } + 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)); - // Split the subface by the "Bowyer-Watson" algorithm. - ivf.bowywat = 3; // Form B-W cavity. - ivf.lawson = 3; // Queue faces of the cavity for flipping. - ivf.rejflag = 1; // Reject it if it encroached upon any segment. - if (qflag == 0) { - ivf.rejflag |= 4; // Reject it if it encroached upon any vertex. + // 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 = ivf.iloc; - ivf.sbowywat = ivf.bowywat; + ivf.sloc = (int) INSTAR; // ivf.iloc; + ivf.sbowywat = 3; // ivf.bowywat; ivf.splitbdflag = 1; ivf.validflag = 1; ivf.respectbdflag = 1; - ivf.assignmeshsize = 1; + ivf.assignmeshsize = b->metric; ivf.refineflag = 2; ivf.refinesh = searchsh; + ivf.smlenflag = useinsertradius; // Update the insertion radius. - loc = insertvertex(newpt, &searchtet, &searchsh, NULL, &ivf); - if (loc == (int) ENCSEGMENT) { - // The new point encroaches upon some segments. - pointdealloc(newpt); - assert(encseglist->objects > 0); - // 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, qflag, chkencflag | 1)) { - splitflag = 1; // A point is inserted on a segment. - break; - } + 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(); } - 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[3] != NULL) { - if (!smarktest2ed(*splitfac)) { - bface = (badface *) badsubfacs->alloc(); - bface->ss = *splitfac; - smarktest2(bface->ss); // Only queue it once. - bface->forg = sorg(*splitfac); // An alive badface. + 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; } - return splitflag; - } else if (loc == (int) ENCVERTEX) { - // The point lies inside some protecting balls. Rejected. - pointdealloc(newpt); - } else if (loc == (int) ONVERTEX) { - pointdealloc(newpt); - } else if (loc == (int) NEARVERTEX) { - pointdealloc(newpt); - } else if (loc == (int) BADELEMENT) { - // Failed to create a valid sub-cavity in surface mesh. - pointdealloc(newpt); - } else if (loc == (int) ivf.iloc) { - // Flip not locally Delaunay link facets. - lawsonflip3d(newpt, 4, 0, chkencflag, 0); - st_facref_count++; - if (steinerleft > 0) steinerleft--; - return 1; // A point is inserted on facet. - } else { - // Unknown error. - assert(0); } - - // Should not be here. - return 0; } /////////////////////////////////////////////////////////////////////////////// @@ -26374,7 +24855,7 @@ int tetgenmesh::splitsubface(face *splitfac, point encpt, int qflag, void tetgenmesh::repairencfacs(int chkencflag) { - badface *bface; + face *bface; point encpt = NULL; int qflag = 0; REAL ccent[3]; @@ -26383,20 +24864,24 @@ void tetgenmesh::repairencfacs(int chkencflag) // if an unlimited number of Steiner points is allowed. while ((badsubfacs->items > 0) && (steinerleft != 0)) { badsubfacs->traversalinit(); - bface = badfacetraverse(badsubfacs); + bface = (face *) badsubfacs->traverse(); while ((bface != NULL) && (steinerleft != 0)) { - // A queued subface may have been deleted (split). - if (bface->ss.sh[3] != NULL) { - // A queued subface may have been processed. - if (smarktest2ed(bface->ss)) { - sunmarktest2(bface->ss); - if (checkfac4split(&(bface->ss), encpt, qflag, ccent)) { - splitsubface(&(bface->ss), encpt, qflag, ccent, chkencflag); + // 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. } - badfacedealloc(badsubfacs, bface); // Remove this entry from list. - bface = badfacetraverse(badsubfacs); + bface = (face *) badsubfacs->traverse(); } } @@ -26409,19 +24894,37 @@ void tetgenmesh::repairencfacs(int chkencflag) assert(0); // Unknown case. } badsubfacs->traversalinit(); - bface = badfacetraverse(badsubfacs); + bface = (face *) badsubfacs->traverse(); while (bface != NULL) { - if (bface->ss.sh[3] != NULL) { - if (smarktest2ed(bface->ss)) { - sunmarktest2(bface->ss); + // Skip a deleted element. + if (bface->shver >= 0) { + if ((bface->sh != NULL) && (bface->sh[3] != NULL)) { + if (smarktest2ed(*bface)) { + sunmarktest2(*bface); + } } } - bface = badfacetraverse(badsubfacs); + 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. // @@ -26434,11 +24937,18 @@ int tetgenmesh::checktet4split(triface *chktet, int &qflag, REAL *ccent) 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, rd; + 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]; @@ -26450,11 +24960,6 @@ int tetgenmesh::checktet4split(triface *chktet, int &qflag, REAL *ccent) pb = (point) chktet->tet[5]; pc = (point) chktet->tet[6]; - if (b->verbose > 2) { - printf(" Check tet (%d, %d, %d, %d)\n", pointmark(pa), - pointmark(pb), pointmark(pc), pointmark(pd)); - } - // 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]; @@ -26468,22 +24973,14 @@ int tetgenmesh::checktet4split(triface *chktet, int &qflag, REAL *ccent) if (!lu_decmp(A, 3, indx, &D, 0)) { // A degenerated tet (vol = 0). - if (b->verbose > 2) { - printf(" Min dihed = 0 (degree)\n"); - } - // Return its barycenter. - for (i = 0; i < 3; i++) { - ccent[i] = 0.25 * (pa[i] + pb[i] + pc[i] + pd[i]); - } - return 1; + // 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->verbose > 2) { - printf(" volume = %g.\n", vol); - } if (b->fixedvolume) { if (vol > b->maxvolume) { qflag = 1; @@ -26506,6 +25003,26 @@ int tetgenmesh::checktet4split(triface *chktet, int &qflag, REAL *ccent) } } + 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)) { @@ -26516,11 +25033,61 @@ int tetgenmesh::checktet4split(triface *chktet, int &qflag, REAL *ccent) lu_solve(A, 3, indx, rhs, 0); for (i = 0; i < 3; i++) ccent[i] = pd[i] + rhs[i]; return 1; - } else { - return 0; // Do not split this tet. } } + 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. @@ -26530,24 +25097,23 @@ int tetgenmesh::checktet4split(triface *chktet, int &qflag, REAL *ccent) lu_solve(A, 3, indx, rhs, 0); for (i = 0; i < 3; i++) ccent[i] = pd[i] + rhs[i]; rd = sqrt(dot(rhs, rhs)); - // 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; - } + 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); } - smlen = sqrt(smlen); D = rd / smlen; - if (b->verbose > 2) { - printf(" Ratio-edge ratio = %g, smlen = %g\n", D, smlen); - } if (D > b->minratio) { // A bad radius-edge ratio. return 1; @@ -26578,7 +25144,7 @@ int tetgenmesh::checktet4split(triface *chktet, int &qflag, REAL *ccent) 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 diehedral angle. + // Get the smallest dihedral angle. //maxcosd = mincosd = cosd[0]; maxcosd = cosd[0]; for (i = 1; i < 6; i++) { @@ -26586,9 +25152,6 @@ int tetgenmesh::checktet4split(triface *chktet, int &qflag, REAL *ccent) maxcosd = (cosd[i] > maxcosd ? cosd[i] : maxcosd); //mincosd = (cosd[i] < mincosd ? cosd[i] : maxcosd); } - if (b->verbose > 2) { - printf(" Min dihed = %g (degree)\n", acos(maxcosd) / PI * 180.0); - } if (maxcosd > cosmindihed) { // Calculate the circumcenter of this tet. // A bad dihedral angle. @@ -26604,26 +25167,6 @@ int tetgenmesh::checktet4split(triface *chktet, int &qflag, REAL *ccent) } } - 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; - } - } - } - } - return 0; } @@ -26636,54 +25179,33 @@ int tetgenmesh::checktet4split(triface *chktet, int &qflag, REAL *ccent) int tetgenmesh::splittetrahedron(triface* splittet, int qflag, REAL *ccent, int chkencflag) { - badface *bface; triface searchtet; - face checkseg, *paryseg; - point newpt, pa, *ppt = NULL; + face *paryseg; + point newpt; + badface *bface; insertvertexflags ivf; - REAL rd; int splitflag; - int loc; int i; - if (b->verbose > 2) { - ppt = (point *) &(splittet->tet[4]); - printf(" Split tet (%d, %d, %d, %d).\n", pointmark(ppt[0]), - pointmark(ppt[1]), pointmark(ppt[2]), pointmark(ppt[3])); - } - if (qflag == 0) { - // It is a bad quality tet (not due to mesh size). - // It can be split if 'ccent' does not encroach upon any prot. balls. - // Do a quick check if the 'ccent' lies inside the protect balls - // of one of the vertices of this tet. - ppt = (point *) &(splittet->tet[4]); - rd = distance(ccent, ppt[0]); - if ((rd <= ppt[0][pointmtrindex]) || (rd <= ppt[1][pointmtrindex]) || - (rd <= ppt[2][pointmtrindex]) || (rd <= ppt[3][pointmtrindex])) { - if (b->verbose > 2) { - printf(" Encroaching a protecting ball. Rejected.\n"); - } - return 0; - } - } + 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; - // Parameters are chosen as follows: - // - bowywat = 3, preserve subsegments and subfaces; - // - flipflag = 3, check star & link facets for flipping; - // - rejflag = 3, do not insert the point if it encroaches upon - // any segment or subface. - // - chkencflag = 4, (as input), only check tetrahedra. + // Use Bowyer-Watson algorithm. Preserve subsegments and subfaces; ivf.bowywat = 3; - ivf.lawson = 3; - ivf.rejflag = 3; - if (qflag == 0) { + 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; @@ -26691,106 +25213,82 @@ int tetgenmesh::splittetrahedron(triface* splittet, int qflag, REAL *ccent, ivf.splitbdflag = 0; // No use. ivf.validflag = 1; ivf.respectbdflag = 1; - ivf.assignmeshsize = 1; + ivf.assignmeshsize = b->metric; ivf.refineflag = 1; ivf.refinetet = *splittet; - loc = insertvertex(newpt, &searchtet, NULL, NULL, &ivf); - if (loc == (int) ENCSEGMENT) { - // There are encroached segments. - pointdealloc(newpt); - assert(encseglist->objects > 0); - splitflag = 0; - if (!b->nobisect) { // not -Y option - // Select an encroached segment and split it. - for (i = 0; i < encseglist->objects; i++) { - paryseg = (face *) fastlookup(encseglist, i); - if (splitsegment(paryseg, 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[4] != NULL) { - if (!marktest2ed(*splittet)) { - bface = (badface *) badtetrahedrons->alloc(); - bface->tt = *splittet; - marktest2(bface->tt); // Only queue it once. - bface->forg = org(*splittet); // An alive badface. - } - } + 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 splitflag; - } else if (loc == (int) ENCSUBFACE) { - // There are encroached subfaces. + return 1; + } else { + // Point is not inserted. pointdealloc(newpt); - assert(encshlist->objects > 0); - splitflag = 0; - if (!b->nobisect) { // not -Y option - // Select an encroached subface and split it. - for (i = 0; i < encshlist->objects; i++) { - bface = (badface *) fastlookup(encshlist, i); - if (splitsubface(&(bface->ss),NULL,qflag,bface->cent,chkencflag | 2)) { - splitflag = 1; // A point is inserted on a subface or a segment. - break; + // 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) - encshlist->restart(); - if (splitflag) { - assert(badsubsegs->items == 0l); // repairencsegs(chkencflag | 3); - // Some subfaces may need to be repaired. - repairencfacs(chkencflag | 2); - // Queue the tet if it is still alive. - if (splittet->tet[4] != NULL) { - if (!marktest2ed(*splittet)) { - bface = (badface *) badtetrahedrons->alloc(); - bface->tt = *splittet; - marktest2(bface->tt); // Only queue it once. - bface->forg = org(*splittet); // An alive badface. + } // 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 splitflag; - } else if (loc == (int) OUTSIDE) { - // There exists not boundary conforming segments/subfaces. - pointdealloc(newpt); - } else if (loc == (int) ONVERTEX) { - // Found a coincident vertex. It should be a Steiner point. - pa = org(searchtet); - assert(pointtype(pa) == FREEVOLVERTEX); - // Delete this new point. - pointdealloc(newpt); - } else if (loc == (int) NEARVERTEX) { - // The point lies very close to an existing point. - pa = point2ppt(newpt); - assert(pointtype(pa) == FREEVOLVERTEX); - // Delete this new point. - pointdealloc(newpt); - } else if (loc == (int) ENCVERTEX) { - // The new point encoraches upon some protecting balls. Rejected. - pointdealloc(newpt); - } else if (loc == (int) BADELEMENT) { - pointdealloc(newpt); - } else { - // Recover Delaunayness. - lawsonflip3d(newpt, 4, 0, chkencflag, 0); - // Vertex is inserted. - st_volref_count++; - if (steinerleft > 0) steinerleft--; - return 1; + return 0; } - - return 0; } /////////////////////////////////////////////////////////////////////////////// @@ -26801,28 +25299,33 @@ int tetgenmesh::splittetrahedron(triface* splittet, int qflag, REAL *ccent, void tetgenmesh::repairbadtets(int chkencflag) { - badface *bface; + 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 = badfacetraverse(badtetrahedrons); + bface = (triface *) badtetrahedrons->traverse(); while ((bface != NULL) && (steinerleft != 0)) { - // A queued tet may have been deleted. - if (!isdeadtet(bface->tt)) { - // A queued tet may have been processed. - if (marktest2ed(bface->tt)) { - unmarktest2(bface->tt); - if (checktet4split(&(bface->tt), qflag, ccent)) { - splittetrahedron(&(bface->tt), qflag, ccent, chkencflag); + // 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); } - badfacedealloc(badtetrahedrons, bface); - bface = badfacetraverse(badtetrahedrons); + bface = (triface *) badtetrahedrons->traverse(); } } @@ -26836,14 +25339,17 @@ void tetgenmesh::repairbadtets(int chkencflag) } // Unmark all queued tet. badtetrahedrons->traversalinit(); - bface = badfacetraverse(badtetrahedrons); + bface = (triface *) badtetrahedrons->traverse(); while (bface != NULL) { - if (!isdeadtet(bface->tt)) { - if (marktest2ed(bface->tt)) { - unmarktest2(bface->tt); + // Skip a deleted element. + if (bface->ver >= 0) { + if (!isdeadtet(*bface)) { + if (marktest2ed(*bface)) { + unmarktest2(*bface); + } } } - bface = badfacetraverse(badtetrahedrons); + bface = (triface *) badtetrahedrons->traverse(); } // Clear the pool. badtetrahedrons->restart(); @@ -26852,13 +25358,12 @@ void tetgenmesh::repairbadtets(int chkencflag) /////////////////////////////////////////////////////////////////////////////// // // -// enforcequality() Refine the mesh. // +// delaunayrefinement() Refine the mesh by Delaunay refinement. // // // /////////////////////////////////////////////////////////////////////////////// void tetgenmesh::delaunayrefinement() { - badface *bface; triface checktet; face checksh; face checkseg; @@ -26866,13 +25371,16 @@ void tetgenmesh::delaunayrefinement() 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(" Edge length limit = %g.\n", b->minedgelength); + 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#). @@ -26884,25 +25392,27 @@ void tetgenmesh::delaunayrefinement() } else { if (!b->quiet) { printf("\nWarning: "); - printf("The desired number of Steiner points (%d) is reached.\n\n", + printf("The desired number of Steiner points (%d) has reached.\n\n", b->steinerleft); } return; // No more Steiner points. } } - if (b->refine || b->nobisect) { // '-r' or '-Y' option. - markacutevertices(); + if (useinsertradius) { + if ((b->plc && b->nobisect) || b->refine) { // '-pY' or '-r' option. + makesegmentendpointsmap(); + } + makefacetverticesmap(); } - marksharpsegments(); - - decidefeaturepointsizes(); encseglist = new arraypool(sizeof(face), 8); encshlist = new arraypool(sizeof(badface), 8); - if (!b->nobisect) { // if no '-Y' option + + //if (!b->nobisect) { // if no '-Y' option + if (!b->nobisect || checkconstraints) { if (b->verbose) { printf(" Splitting encroached subsegments.\n"); } @@ -26911,17 +25421,14 @@ void tetgenmesh::delaunayrefinement() steinercount = points->items; // Initialize the pool of encroached subsegments. - badsubsegs = new memorypool(sizeof(badface), b->shellfaceperblock, - memorypool::POINTER, 0); + 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) { - bface = (badface *) badsubsegs->alloc(); - bface->ss = checkseg; - smarktest2(bface->ss); // Only queue it once. - bface->forg = sorg(checkseg); // An alive badface. + enqueuesubface(badsubsegs, &checkseg); checkseg.sh = shellfacetraverse(subsegs); } @@ -26932,7 +25439,6 @@ void tetgenmesh::delaunayrefinement() printf(" Added %ld Steiner points.\n", points->items - steinercount); } - if (b->reflevel > 1) { // '-D2' option if (b->verbose) { printf(" Splitting encroached subfaces.\n"); @@ -26944,17 +25450,14 @@ void tetgenmesh::delaunayrefinement() bak_facref_count = st_facref_count; // Initialize the pool of encroached subfaces. - badsubfacs = new memorypool(sizeof(badface), b->shellfaceperblock, - memorypool::POINTER, 0); + 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) { - bface = (badface *) badsubfacs->alloc(); - bface->ss = checksh; - smarktest2(bface->ss); // Only queue it once. - bface->forg = sorg(checksh); // An alive badface. + enqueuesubface(badsubfacs, &checksh); checksh.sh = shellfacetraverse(subfaces); } @@ -26966,7 +25469,6 @@ void tetgenmesh::delaunayrefinement() points->items-steinercount, st_segref_count-bak_segref_count, st_facref_count-bak_facref_count); } - } // if (b->reflevel > 1) } // if (!b->nobisect) @@ -26985,17 +25487,13 @@ void tetgenmesh::delaunayrefinement() cosmindihed = cos(b->mindihedral / 180.0 * PI); // Initialize the pool of bad quality tetrahedra. - badtetrahedrons = new memorypool(sizeof(badface), b->tetrahedraperblock, - memorypool::POINTER, 0); - + 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) { - bface = (badface *) badtetrahedrons->alloc(); - bface->tt = checktet; - marktest2(bface->tt); // Only queue it once. - bface->forg = org(checktet); // An alive badface. + enqueuetetrahedron(&checktet); checktet.tet = tetrahedrontraverse(); } @@ -27011,6 +25509,13 @@ void tetgenmesh::delaunayrefinement() } } // 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: "); @@ -27019,16 +25524,21 @@ void tetgenmesh::delaunayrefinement() } } + delete encseglist; delete encshlist; - if (!b->nobisect) { + //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; } } @@ -27041,6 +25551,267 @@ void tetgenmesh::delaunayrefinement() //// //// //// //// +/////////////////////////////////////////////////////////////////////////////// +// // +// 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. // @@ -27050,8 +25821,8 @@ void tetgenmesh::delaunayrefinement() void tetgenmesh::recoverdelaunay() { arraypool *flipqueue, *nextflipqueue, *swapqueue; - badface *bface, *parybface; triface tetloop, neightet, *parytet; + badface *bface, *parybface; point *ppt; flipconstraints fc; int i, j; @@ -27060,28 +25831,16 @@ void tetgenmesh::recoverdelaunay() printf("Recovering Delaunayness...\n"); } - if (b->verbose) { - printf(" max_flipstarsize = %d.\n", b->optmaxflipstarsize); - printf(" max_fliplinklevel = %d.\n", b->delmaxfliplevel); - } - - calc_tetprism_vol = 1; tetprism_vol_sum = 0.0; // Initialize it. - assert(flipstack == NULL); - assert(unflipqueue->objects == 0l); - // 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++) { - // Avoid queue a face twice. - fsym(tetloop, neightet); - if (!ishulltet(neightet)) { - if (!facemarked(neightet)) { - flippush(flipstack, &tetloop); - } + decode(tetloop.tet[tetloop.ver], neightet); + if (!facemarked(neightet)) { + flippush(flipstack, &tetloop); } } ppt = (point *) &(tetloop.tet[4]); @@ -27089,6 +25848,10 @@ void tetgenmesh::recoverdelaunay() 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); } @@ -27096,37 +25859,35 @@ void tetgenmesh::recoverdelaunay() if (b->verbose > 1) { printf(" Recover Delaunay [Lawson] : %ld\n", flippool->items); } - assert(unflipqueue->objects == 0l); // First only use the basic Lawson's flip. - lawsonflip3d(NULL, 4, 0, 0, 1); + fc.remove_ndelaunay_edge = 1; + fc.enqflag = 2; + + lawsonflip3d(&fc); if (b->verbose > 1) { - printf(" New obj = %.17g\n", tetprism_vol_sum); + printf(" obj (after Lawson) = %.17g\n", tetprism_vol_sum); } if (unflipqueue->objects == 0l) { - // The mesh is Delaunay. - return; + return; // The mesh is Delaunay. } - // Set the common options. - fc.remove_ndelaunay_edge = 1; fc.unflip = 1; // Unflip if the edge is not flipped. - fc.collectnewtets = 1; + fc.collectnewtets = 1; // new tets are returned in 'cavetetlist'. + fc.enqflag = 0; - autofliplinklevel = 1; // Init value. - b->fliplinklevel = -1; + autofliplinklevel = 1; // Init level. + b->fliplinklevel = -1; // No fixed level. // For efficiency reason, we limit the maximium size of the edge star. - // 'b->optmaxflipstarsize' is set by -OOOOO (5 Os), default is 10. int bakmaxflipstarsize = b->flipstarsize; - b->flipstarsize = b->optmaxflipstarsize; + 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; @@ -27134,60 +25895,61 @@ void tetgenmesh::recoverdelaunay() while (flipqueue->objects > 0l) { - 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)) { - // Remember the the objective value (volume of all tetprisms). - fc.bak_tetprism_vol = tetprism_vol_sum; - if (removeedgebyflips(&(bface->tt), &fc) == 2) { - if (b->verbose > 2) { - printf(" Decreased quantity: %.17g.\n", - fc.bak_tetprism_vol - tetprism_vol_sum); + 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 } - // 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. - fsym(*parytet, 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'. - lawsonflip3d(NULL, 4, 0, 0, 1); - } else { - // Unable to remove this edge. Save it. - nextflipqueue->newindex((void **) &parybface); + } // 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 - - flipqueue->restart(); - - // Swap the two flip queues. - swapqueue = flipqueue; - flipqueue = unflipqueue; - unflipqueue = swapqueue; - } // while (flipqueue->objects > 0l) + } + } // i if (b->verbose > 1) { - printf(" New obj = %.17g.\n", tetprism_vol_sum); + printf(" obj (after level %d) = %.17g.\n", autofliplinklevel, + tetprism_vol_sum); } + flipqueue->restart(); // Swap the two flip queues. swapqueue = flipqueue; @@ -27195,14 +25957,13 @@ void tetgenmesh::recoverdelaunay() nextflipqueue = swapqueue; if (flipqueue->objects > 0l) { - // 'b->delmaxfliplevel' is set by -OOOO, default is 1. + // 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) { @@ -27211,16 +25972,13 @@ void tetgenmesh::recoverdelaunay() } } - b->flipstarsize = bakmaxflipstarsize; - - delete nextflipqueue; - delete flipqueue; - - calc_tetprism_vol = 0; - if (b->verbose) { - printf(" Final obj = %.17g\n", tetprism_vol_sum); + printf(" Final obj = %.17g\n", tetprism_vol_sum); } + + b->flipstarsize = bakmaxflipstarsize; + delete flipqueue; + delete nextflipqueue; } /////////////////////////////////////////////////////////////////////////////// @@ -27233,11 +25991,7 @@ int tetgenmesh::gettetrahedron(point pa, point pb, point pc, point pd, triface *searchtet) { triface spintet; - - if (b->verbose > 2) { - printf(" Get tet [%d,%d,%d,%d].\n", pointmark(pa), pointmark(pb), - pointmark(pc), pointmark(pd)); - } + int t1ver; if (getedge(pa, pb, searchtet)) { spintet = *searchtet; @@ -27286,18 +26040,20 @@ long tetgenmesh::improvequalitybyflips() flipqueue = new arraypool(sizeof(badface), 10); nextflipqueue = new arraypool(sizeof(badface), 10); - // Flip edge options. - b->fliplinklevel = -1; - autofliplinklevel = 1; // Init value. + // Backup flip edge options. + int bakautofliplinklevel = autofliplinklevel; + int bakfliplinklevel = b->fliplinklevel; + int bakmaxflipstarsize = b->flipstarsize; - // For efficiency reason, we limit the maximium size of the edge star. - // 'b->optmaxflipstarsize' is set by -OOOOO (5 Os), default is 10. - int bakmaxflipstarsize = b->flipstarsize; - b->flipstarsize = b->optmaxflipstarsize; + // 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; @@ -27320,40 +26076,37 @@ long tetgenmesh::improvequalitybyflips() 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, - &maxdd, NULL); + &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. - if (b->verbose > 2) { - printf(" Found a large angle [%d,%d,%d,%d] (%g).\n", - pointmark(org(bface->tt)), pointmark(dest(bface->tt)), - pointmark(apex(bface->tt)), pointmark(oppo(bface->tt)), - acos(cosdd[i]) / PI * 180.0); - } fc.cosdihed_in = cosdd[i]; fc.cosdihed_out = 0.0; // 90 degree. n = removeedgebyflips(&(bface->tt), &fc); if (n == 2) { // Edge is flipped. - if (b->verbose > 2) { - printf(" Reduced a large angle to %g degree.\n", - acos(fc.cosdihed_out) / PI * 180.0); - } remflag = 1; if (fc.cosdihed_out < cosmaxdihed) { // Queue new bad tets for further improvements. @@ -27361,8 +26114,6 @@ long tetgenmesh::improvequalitybyflips() parytet = (triface *) fastlookup(cavetetlist, j); if (!isdeadtet(*parytet)) { ppt = (point *) & (parytet->tet[4]); - //if (!marktest2ed(*parytet)) { - assert(!marktest2ed(*parytet)); // SELF_CHECK // Do not test a hull tet. if (ppt[3] != dummypoint) { tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], ncosdd, @@ -27381,8 +26132,7 @@ long tetgenmesh::improvequalitybyflips() parybface->cent[n] = ncosdd[n]; } } - } // if (ppt[3] != dummypoint) { - //} + } // if (ppt[3] != dummypoint) } } // j } // if (fc.cosdihed_out < cosmaxdihed) @@ -27413,12 +26163,12 @@ long tetgenmesh::improvequalitybyflips() totalremcount += remcount; if (unflipqueue->objects > 0l) { - // 'b->optmaxfliplevel' is set by -OOO, default is 2. - if (autofliplinklevel >= b->optmaxfliplevel) { - // For efficiency reason, we do not search too far. + //if (autofliplinklevel >= b->optmaxfliplevel) { + if (autofliplinklevel >= b->optlevel) { break; } autofliplinklevel+=b->fliplinklevelinc; + //b->flipstarsize = 10 + (1 << (b->optlevel - 1)); } // Swap the two flip queues. @@ -27427,6 +26177,9 @@ long tetgenmesh::improvequalitybyflips() unflipqueue = swapqueue; } // while (flipqueues->objects > 0) + // Restore original flip edge options. + autofliplinklevel = bakautofliplinklevel; + b->fliplinklevel = bakfliplinklevel; b->flipstarsize = bakmaxflipstarsize; delete flipqueue; @@ -27451,8 +26204,8 @@ long tetgenmesh::improvequalitybyflips() // has two orientations, ccw or cw, with respect to 'p'. 'ccw' indicates // // the orientation is ccw (1) or not (0). // // // -// 'of' is a structure contains the parameters of the objective function. It // -// is needed by the evaluation of the function value. // +// '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. // // // @@ -27473,16 +26226,6 @@ int tetgenmesh::smoothpoint(point smtpt, arraypool *linkfacelist, int ccw, int numdirs, iter; int i, j, k; - if (b->verbose > 2) { - printf(" Smooth a point: %ld faces.\n", linkfacelist->objects); - if (opm->min_max_dihedangle) { - printf(" Init value = %g (degree).\n", - acos(opm->initval - 1.0) / PI * 180.0); - } else { - printf(" Init value = %g.\n", opm->initval); - } - } - // Decide the number of moving directions. numdirs = (int) linkfacelist->objects; if (numdirs > opm->numofsearchdirs) { @@ -27535,7 +26278,8 @@ int tetgenmesh::smoothpoint(point smtpt, arraypool *linkfacelist, int ccw, if (ori < 0.0) { // Calcuate the objective function value. if (opm->max_min_volume) { - val = -ori; + //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) { @@ -27548,8 +26292,10 @@ int tetgenmesh::smoothpoint(point smtpt, arraypool *linkfacelist, int ccw, } } 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 = -ori; + val = - orient3dfast(pa, pb, pc, nextpt); } else { // Discard this point. break; // j @@ -27569,7 +26315,6 @@ int tetgenmesh::smoothpoint(point smtpt, arraypool *linkfacelist, int ccw, } // j if (j == linkfacelist->objects) { // The function value has been improved. - assert(minval > opm->imprval); opm->imprval = minval; // Save the new location of the point. for (j = 0; j < 3; j++) bestpt[j] = nextpt[j]; @@ -27615,60 +26360,16 @@ int tetgenmesh::smoothpoint(point smtpt, arraypool *linkfacelist, int ccw, } // while (1) if (iter > 0) { - // The point has been smooothed. - opm->smthiter = iter; // Remember the number of iterations. - if (b->verbose > 2) { - printf(" Smoothed: %d iterations.\n", iter); - if (opm->min_max_dihedangle) { - printf(" Fina value = %g (degree).\n", - acos(opm->imprval - 1.0) / PI * 180.0); - } else { - printf(" Fina value = %g.\n", opm->imprval); - } - } + // 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]; - - if (opm->flipflag) { - // Push all affected faces into 'flipstack'. - triface starttet, neightet; - for (i = 0; i < linkfacelist->objects; i++) { - parytet = (triface *) fastlookup(linkfacelist, i); - starttet = *parytet; - for (starttet.ver = 0; starttet.ver < 4; starttet.ver++) { - fsym(starttet, neightet); - if (!infected(neightet)) { - flippush(flipstack, &starttet); - } - } - infect(*parytet); - } - for (i = 0; i < linkfacelist->objects; i++) { - parytet = (triface *) fastlookup(linkfacelist, i); - uninfect(*parytet); - } - } else if (opm->checkencflag) { - // Push all affected tets into pool. - badface *bface; - for (i = 0; i < linkfacelist->objects; i++) { - parytet = (triface *) fastlookup(linkfacelist, i); - if (!marktest2ed(*parytet)) { - marktest2(*parytet); // Only queue it once. - bface = (badface *) badtetrahedrons->alloc(); - bface->tt = *parytet; - bface->forg = org(bface->tt); - } - } - } - } else { - if (b->verbose > 2) { - printf(" Not smoothed.\n"); - } } return iter; } + /////////////////////////////////////////////////////////////////////////////// // // // improvequalitysmoothing() Improve mesh quality by smoothing. // @@ -27678,31 +26379,30 @@ int tetgenmesh::smoothpoint(point smtpt, arraypool *linkfacelist, int ccw, long tetgenmesh::improvequalitybysmoothing(optparameters *opm) { arraypool *flipqueue, *swapqueue; + triface *parytet; badface *bface, *parybface; point *ppt; long totalsmtcount, smtcount; int smtflag; - int iter, i, k; + int iter, i, j, k; //assert(unflipqueue->objects > 0l); flipqueue = new arraypool(sizeof(badface), 10); - totalsmtcount = 0l; - // Swap the two flip queues. swapqueue = flipqueue; flipqueue = unflipqueue; unflipqueue = swapqueue; + totalsmtcount = 0l; iter = 0; while (flipqueue->objects > 0l) { smtcount = 0l; - //while (flipqueue->objects > 0l) { if (b->verbose > 1) { - printf(" Improving mesh qualiy by smoothing [%d]#: %ld.\n", + printf(" Improving mesh quality by smoothing [%d]#: %ld.\n", iter, flipqueue->objects); } @@ -27710,6 +26410,7 @@ long tetgenmesh::improvequalitybysmoothing(optparameters *opm) 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. @@ -27719,10 +26420,7 @@ long tetgenmesh::improvequalitybysmoothing(optparameters *opm) if (bface->key < cossmtdihed) { // if (maxdd < cosslidihed) { // It is a sliver. Try to smooth its vertices. smtflag = 0; - //if (opm->min_max_dihedangle) { - opm->initval = bface->key + 1.0; - //opm->checkencflag = 4; // Queue affected tets. - //} + 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); @@ -27735,78 +26433,61 @@ long tetgenmesh::improvequalitybysmoothing(optparameters *opm) 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) { - // This tet is modifed. - smtcount++; - if ((opm->imprval - 1.0) < cossmtdihed) { - // Queue new slivers. - badtetrahedrons->traversalinit(); - bface = badfacetraverse(badtetrahedrons); - while (bface != NULL) { - assert(!isdeadtet(bface->tt)); - assert(marktest2ed(bface->tt)); - unmarktest2(bface->tt); - if (!marktested(bface->tt)) { - ppt = (point *) & (bface->tt.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(bface->tt); // It is in unflipqueue. - bface->forg = ppt[0]; - bface->fdest = ppt[1]; - bface->fapex = ppt[2]; - bface->foppo = ppt[3]; - bface->tt.ver = 11; - unflipqueue->newindex((void **) &parybface); - *parybface = *bface; - } - } - bface = badfacetraverse(badtetrahedrons); - } - } else { - // No new slivers. Only unmark the queued tets. - badtetrahedrons->traversalinit(); - bface = badfacetraverse(badtetrahedrons); - while (bface != NULL) { - assert(!isdeadtet(bface->tt)); - assert(marktest2ed(bface->tt)); - unmarktest2(bface->tt); - bface = badfacetraverse(badtetrahedrons); - } - } - badtetrahedrons->restart(); - } else { + if (!smtflag) { // Didn't smooth. Queue it again. - // Adjust the vertices for flipping. marktest(bface->tt); // It is in unflipqueue. - bface->forg = ppt[0]; - bface->fdest = ppt[1]; - bface->fapex = ppt[2]; - bface->foppo = ppt[3]; - bface->tt.ver = 11; unflipqueue->newindex((void **) &parybface); - *parybface = *bface; + 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 (maxdd < cosslidihed) } // if (!marktested(...)) - } // gettetrahedron(...) + } // if (gettetrahedron(...)) } // k flipqueue->restart(); - // } // while - // Unmark the tets in unflipqueue. for (i = 0; i < unflipqueue->objects; i++) { bface = (badface *) fastlookup(unflipqueue, i); - assert(!isdeadtet(bface->tt)); - assert(marktested(bface->tt)); unmarktest(bface->tt); } @@ -27846,35 +26527,36 @@ int tetgenmesh::splitsliver(triface *slitet, REAL cosd, int chkencflag) { triface *abtets; triface searchtet, spintet, *parytet; - face checkseg; point pa, pb, steinerpt; optparameters opm; insertvertexflags ivf; REAL smtpt[3], midpt[3]; int success; - int loc; + int t1ver; int n, i; - // 'slitet' is [c,d,a,b], where [c,d] has a big hihedral angle. + // 'slitet' is [c,d,a,b], where [c,d] has a big dihedral angle. // Go to the opposite edge [a,b]. - eprev(*slitet, searchtet); - esymself(searchtet); - enextself(searchtet); // [a,b,c,d]. + edestoppo(*slitet, searchtet); // [a,b,c,d]. // Do not split a segment. - tsspivot1(searchtet, checkseg); - if (checkseg.sh != NULL) { + 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]. @@ -27928,19 +26610,11 @@ int tetgenmesh::splitsliver(triface *slitet, REAL cosd, int chkencflag) cavetetlist->restart(); if (!success) { - if (b->verbose > 2) { - printf(" Unable to relocate the initial point.\n"); - } delete [] abtets; return 0; } - if (steinerleft == 0) { - // The desired number of Steiner points is reached. - return 0; - } - // Insert the Steiner point. makepoint(&steinerpt, FREEVOLVERTEX); for (i = 0; i < 3; i++) steinerpt[i] = smtpt[i]; @@ -27951,24 +26625,22 @@ int tetgenmesh::splitsliver(triface *slitet, REAL cosd, int chkencflag) 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.bowywat = 0; // Do not use Bowyer-Watson algorithm. - ivf.lawson = 0; // Do not flip. - ivf.rejflag = 0; ivf.chkencflag = chkencflag; - ivf.sloc = 0; - ivf.sbowywat = 0; - ivf.splitbdflag = 0; - ivf.validflag = 0; - ivf.respectbdflag = 0; - ivf.assignmeshsize = 0; + ivf.assignmeshsize = b->metric; - loc = insertvertex(steinerpt, &searchtet, NULL, NULL, &ivf); - if (loc == (int) INSTAR) { + if (insertpoint(steinerpt, &searchtet, NULL, NULL, &ivf)) { // The vertex has been inserted. - st_volref_count++; //st_inpoly_count++; + st_volref_count++; if (steinerleft > 0) steinerleft--; return 1; } else { @@ -27976,8 +26648,6 @@ int tetgenmesh::splitsliver(triface *slitet, REAL cosd, int chkencflag) pointdealloc(steinerpt); return 0; } - - delete [] abtets; } /////////////////////////////////////////////////////////////////////////////// @@ -27990,24 +26660,24 @@ long tetgenmesh::removeslivers(int chkencflag) { arraypool *flipqueue, *swapqueue; badface *bface, *parybface; + triface slitet, *parytet; point *ppt; - REAL *cosdd; + REAL cosdd[6], maxcosd; long totalsptcount, sptcount; - int iter, j, k; + int iter, i, j, k; //assert(unflipqueue->objects > 0l); flipqueue = new arraypool(sizeof(badface), 10); - totalsptcount = 0l; - // Swap the two flip queues. swapqueue = flipqueue; flipqueue = unflipqueue; unflipqueue = swapqueue; + totalsptcount = 0l; iter = 0; - while (flipqueue->objects > 0l) { + while ((flipqueue->objects > 0l) && (steinerleft != 0)) { sptcount = 0l; @@ -28016,74 +26686,63 @@ long tetgenmesh::removeslivers(int chkencflag) iter, flipqueue->objects); } - for (k = 0; k < flipqueue->objects; k++) { + 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 (!marktested(bface->tt)) { - // Here we simply re-compute the quality. Since other smoothing + 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. - cosdd = bface->cent; - for (j = 0; j < 6; j++) { - if (cosdd[j] < cosslidihed) { - // Found a large dihedral angle. - bface->tt.ver = edge2ver[j]; // Go to the edge. - if (b->verbose > 2) { - printf(" Found a bad tet [%d,%d,%d,%d] (%g).\n", - pointmark(org(bface->tt)), pointmark(dest(bface->tt)), - pointmark(apex(bface->tt)), pointmark(oppo(bface->tt)), - acos(cosdd[j]) / PI * 180.0); - } - if (splitsliver(&(bface->tt), cosdd[j], chkencflag)) { - sptcount++; - break; - } + } + 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(); - bface = badfacetraverse(badtetrahedrons); - while (bface != NULL) { - assert(!isdeadtet(bface->tt)); - assert(marktest2ed(bface->tt)); - unmarktest2(bface->tt); - ppt = (point *) & (bface->tt.tet[4]); - tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], bface->cent, - &(bface->key), NULL); - if (bface->key < cosslidihed) { - // A new sliver. Queue it. - //marktest(bface->tt); // It is in unflipqueue. - bface->forg = ppt[0]; - bface->fdest = ppt[1]; - bface->fapex = ppt[2]; - bface->foppo = ppt[3]; - bface->tt.ver = 11; - unflipqueue->newindex((void **) &parybface); - *parybface = *bface; + } + } // 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]; } - bface = badfacetraverse(badtetrahedrons); } - badtetrahedrons->restart(); - } else { - // Didn't split. Queue it again. - // Adjust the vertices for flipping. - //marktest(bface->tt); // It is in unflipqueue. - bface->forg = ppt[0]; - bface->fdest = ppt[1]; - bface->fapex = ppt[2]; - bface->foppo = ppt[3]; - bface->tt.ver = 11; - unflipqueue->newindex((void **) &parybface); - *parybface = *bface; - } // if (j == 6) - } // if (bface->key < cosslidihed) - // } // if (!marktested(bface->tt)) + 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 @@ -28121,11 +26780,12 @@ long tetgenmesh::removeslivers(int chkencflag) // // /////////////////////////////////////////////////////////////////////////////// -void tetgenmesh::optimizemesh(int optflag) +void tetgenmesh::optimizemesh() { badface *parybface; triface checktet; point *ppt; + int optpasses; optparameters opm; REAL ncosdd[6], maxdd; long totalremcount, remcount; @@ -28139,26 +26799,34 @@ void tetgenmesh::optimizemesh(int optflag) printf("Optimizing mesh...\n"); } - if (b->verbose > 1) { - printf(" min_max_dihedral = %g.\n", b->optmaxdihedral); - printf(" max_flipstarsize = %d.\n", b->optmaxflipstarsize); - printf(" max_fliplinklevel = %d.\n", b->optmaxfliplevel); - printf(" number of passes = %d.\n", b->optpasses); - } - totalsmtcount = totalsptcount = totalremcount = 0l; + optpasses = ((1 << b->optlevel) - 1); - if (b->verbose > 1) { - printf(" Removing large angles (> %g degree).\n", b->optmaxdihedral); + 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) { @@ -28181,23 +26849,23 @@ void tetgenmesh::optimizemesh(int optflag) totalremcount = improvequalitybyflips(); if ((unflipqueue->objects > 0l) && - ((b->optlevel & 2) || (b->optlevel & 4))) { // -O2 | -O4 - - badtetrahedrons = new memorypool(sizeof(badface), b->tetrahedraperblock, - memorypool::POINTER, 0); + ((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. + //opm.checkencflag = 4; // Queue affected tets after smoothing. chkencflag = 4; // Queue affected tets after splitting a sliver. iter = 0; - while (iter < b->optpasses) { + while (iter < optpasses) { smtcount = sptcount = remcount = 0l; - if (b->optlevel & 2) { + if (b->optscheme & 2) { smtcount += improvequalitybysmoothing(&opm); totalsmtcount += smtcount; if (smtcount > 0l) { @@ -28206,7 +26874,7 @@ void tetgenmesh::optimizemesh(int optflag) } } if (unflipqueue->objects > 0l) { - if (b->optlevel & 4) { + if (b->optscheme & 4) { sptcount += removeslivers(chkencflag); totalsptcount += sptcount; if (sptcount > 0l) { @@ -28228,7 +26896,7 @@ void tetgenmesh::optimizemesh(int optflag) delete badtetrahedrons; - } // // -O2 | -O4 + } if (unflipqueue->objects > 0l) { if (b->verbose > 1) { @@ -28239,13 +26907,13 @@ void tetgenmesh::optimizemesh(int optflag) if (b->verbose) { if (totalremcount > 0l) { - printf(" Removed %ld bad tets.\n", totalremcount); + printf(" Removed %ld edges.\n", totalremcount); } if (totalsmtcount > 0l) { printf(" Smoothed %ld points.\n", totalsmtcount); } if (totalsptcount > 0l) { - printf(" Split %ld bad tets.\n", totalsptcount); + printf(" Split %ld slivers.\n", totalsptcount); } } } @@ -28258,6 +26926,32 @@ void tetgenmesh::optimizemesh(int optflag) //// //// //// //// +/////////////////////////////////////////////////////////////////////////////// +// // +// 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. // @@ -28403,12 +27097,12 @@ int tetgenmesh::checkmesh(int topoflag) // // /////////////////////////////////////////////////////////////////////////////// -int tetgenmesh::checkshells(/*int sub2tet*/) +int tetgenmesh::checkshells() { triface neightet, symtet; face shloop, spinsh, nextsh; face checkseg; - point pa, pb; //, *ppt; + point pa, pb; int bakcount; int horrors, i; @@ -28436,10 +27130,10 @@ int tetgenmesh::checkshells(/*int sub2tet*/) 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", (unsigned long long) spinsh.sh, + 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", (unsigned long long) nextsh.sh); + printf(" Second: x%lx (DEAD)\n", (uintptr_t) nextsh.sh); horrors++; break; } @@ -28447,10 +27141,10 @@ int tetgenmesh::checkshells(/*int sub2tet*/) 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", (unsigned long long) spinsh.sh, + 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", (unsigned long long) nextsh.sh, + printf(" Scond: x%lx (%d, %d, %d).\n", (uintptr_t) nextsh.sh, pointmark(sorg(nextsh)), pointmark(sdest(nextsh)), pointmark(sapex(nextsh))); horrors++; @@ -28459,10 +27153,10 @@ int tetgenmesh::checkshells(/*int sub2tet*/) // 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", (unsigned long long) spinsh.sh, + 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", (unsigned long long) nextsh.sh, + printf(" Scond: x%lx (%d, %d, %d).\n", (uintptr_t) nextsh.sh, pointmark(sorg(nextsh)), pointmark(sdest(nextsh)), pointmark(sapex(nextsh))); horrors++; @@ -28476,19 +27170,19 @@ int tetgenmesh::checkshells(/*int sub2tet*/) 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", (unsigned long long) shloop.sh, + 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", (unsigned long long) checkseg.sh); + 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", (unsigned long long) shloop.sh, + 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", (unsigned long long) checkseg.sh, + printf(" Seg: x%lx (%d, %d).\n", (uintptr_t) checkseg.sh, pointmark(sorg(checkseg)), pointmark(sdest(checkseg))); horrors++; } @@ -28502,20 +27196,20 @@ int tetgenmesh::checkshells(/*int sub2tet*/) 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", (unsigned long long) shloop.sh, + 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", (unsigned long long) neightet.tet); + 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", (unsigned long long) shloop.sh, + 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", - (unsigned long long) neightet.tet, pointmark(org(neightet)), + (uintptr_t) neightet.tet, pointmark(org(neightet)), pointmark(dest(neightet)), pointmark(apex(neightet)), pointmark(oppo(neightet))); horrors++; @@ -28524,11 +27218,11 @@ int tetgenmesh::checkshells(/*int sub2tet*/) if (!((sorg(spinsh) == org(neightet)) && (sdest(spinsh) == dest(neightet)))) { printf(" !! !! Wrong tet-sub connection.\n"); - printf(" Sub: x%lx (%d, %d, %d).\n", (unsigned long long) spinsh.sh, + 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", - (unsigned long long) neightet.tet, pointmark(org(neightet)), + (uintptr_t) neightet.tet, pointmark(org(neightet)), pointmark(dest(neightet)), pointmark(apex(neightet)), pointmark(oppo(neightet))); horrors++; @@ -28539,11 +27233,11 @@ int tetgenmesh::checkshells(/*int sub2tet*/) if (!((sorg(spinsh) == org(symtet)) && (sdest(spinsh) == dest(symtet)))) { printf(" !! !! Wrong tet-sub connection.\n"); - printf(" Sub: x%lx (%d, %d, %d).\n", (unsigned long long) spinsh.sh, + 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", - (unsigned long long) symtet.tet, pointmark(org(symtet)), + (uintptr_t) symtet.tet, pointmark(org(symtet)), pointmark(dest(symtet)), pointmark(apex(symtet)), pointmark(oppo(symtet))); horrors++; @@ -28598,8 +27292,10 @@ int tetgenmesh::checksegments() face sseg, checkseg; point pa, pb; int miscount; + int t1ver; int horrors, i; + if (!b->quiet) { printf(" Checking tet->seg connections...\n"); } @@ -28623,9 +27319,9 @@ int tetgenmesh::checksegments() ((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", - (unsigned long long) tetloop.tet, pointmark(org(tetloop)), + (uintptr_t) tetloop.tet, pointmark(org(tetloop)), pointmark(dest(tetloop)), pointmark(apex(tetloop)), - pointmark(oppo(tetloop)), (unsigned long long) sseg.sh, + pointmark(oppo(tetloop)), (uintptr_t) sseg.sh, pointmark(pa), pointmark(pb)); horrors++; } else { @@ -28636,11 +27332,11 @@ int tetgenmesh::checksegments() if (checkseg.sh != sseg.sh) { printf(" !! Wrong tet->seg connection.\n"); printf(" Tet: x%lx (%d, %d, %d, %d) - ", - (unsigned long long) neightet.tet, pointmark(org(neightet)), + (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", (unsigned long long) checkseg.sh, + printf("Seg x%lx (%d, %d).\n", (uintptr_t) checkseg.sh, pointmark(sorg(checkseg)),pointmark(sdest(checkseg))); } else { printf("Seg: NULL.\n"); @@ -28660,9 +27356,9 @@ int tetgenmesh::checksegments() ((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", - (unsigned long long) neightet.tet, pointmark(org(neightet)), + (uintptr_t) neightet.tet, pointmark(org(neightet)), pointmark(dest(neightet)), pointmark(apex(neightet)), - pointmark(oppo(neightet)), (unsigned long long) sseg.sh, + pointmark(oppo(neightet)), (uintptr_t) sseg.sh, pointmark(pa), pointmark(pb)); horrors++; } @@ -28679,7 +27375,7 @@ int tetgenmesh::checksegments() printf(" !! A marked edge: (%d, %d, %d, %d) -- x%lx %d.\n", pointmark(org(neightet)), pointmark(dest(neightet)), pointmark(apex(neightet)), pointmark(oppo(neightet)), - (unsigned long long) neightet.tet, neightet.ver); + (uintptr_t) neightet.tet, neightet.ver); // Check if all tets at the edge are marked. spintet = neightet; while (1) { @@ -28688,7 +27384,7 @@ int tetgenmesh::checksegments() printf(" !! !! An unmarked edge (%d, %d, %d, %d) -- x%lx %d.\n", pointmark(org(spintet)), pointmark(dest(spintet)), pointmark(apex(spintet)), pointmark(oppo(spintet)), - (unsigned long long) spintet.tet, spintet.ver); + (uintptr_t) spintet.tet, spintet.ver); horrors++; } if (spintet.tet == neightet.tet) break; @@ -28721,7 +27417,7 @@ int tetgenmesh::checksegments() // sesymself(spinsh); // printf(" !! Wrong ori at subface (%d, %d, %d) -- x%lx %d\n", // pointmark(sorg(spinsh)), pointmark(sdest(spinsh)), - // pointmark(sapex(spinsh)), (unsigned long long) spinsh.sh, + // pointmark(sapex(spinsh)), (uintptr_t) spinsh.sh, // spinsh.shver); // horrors++; //} @@ -28734,7 +27430,7 @@ int tetgenmesh::checksegments() 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)), - (unsigned long long) spintet.tet, spintet.ver); + (uintptr_t) spintet.tet, spintet.ver); horrors++; } if (checkseg.sh != sseg.sh) { @@ -28753,7 +27449,7 @@ int tetgenmesh::checksegments() } else { printf(" !! Wrong seg-subface (%d, %d, %d) -- x%lx %d connect\n", pointmark(sorg(spinsh)), pointmark(sdest(spinsh)), - pointmark(sapex(spinsh)), (unsigned long long) spinsh.sh, + pointmark(sapex(spinsh)), (uintptr_t) spinsh.sh, spinsh.shver); horrors++; break; @@ -29034,6 +27730,7 @@ int tetgenmesh::checkconforming(int flag) REAL cent[3], radius, dist, diff, rd, len; bool enq; int encsubsegs, encsubfaces; + int t1ver; int i; REAL A[4][4], rhs[4], D; @@ -29189,10 +27886,10 @@ void tetgenmesh::qualitystatistics() REAL tetaspect, tetradius; REAL smalldiangle, bigdiangle; REAL smallfaangle, bigfaangle; - int radiustable[12]; - int aspecttable[16]; - int dihedangletable[18]; - int faceangletable[18]; + unsigned long radiustable[12]; + unsigned long aspecttable[16]; + unsigned long dihedangletable[18]; + unsigned long faceangletable[18]; int indx[4]; int radiusindex; int aspectindex; @@ -29220,10 +27917,10 @@ void tetgenmesh::qualitystatistics() aspectratiotable[8] = 25.0; aspectratiotable[9] = 50.0; aspectratiotable[10] = 100.0; aspectratiotable[11] = 0.0; - for (i = 0; i < 12; i++) radiustable[i] = 0; - for (i = 0; i < 12; i++) aspecttable[i] = 0; - for (i = 0; i < 18; i++) dihedangletable[i] = 0; - for (i = 0; i < 18; i++) faceangletable[i] = 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; @@ -29237,16 +27934,26 @@ void tetgenmesh::qualitystatistics() 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 = orient3d(p[1], p[0], p[2], p[3]) / 6.0; + 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]); @@ -29266,7 +27973,7 @@ void tetgenmesh::qualitystatistics() 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 lengthes. + // 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. @@ -29408,7 +28115,7 @@ void tetgenmesh::qualitystatistics() - // Calulate the largest and smallest face angles. + // 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. @@ -29495,16 +28202,16 @@ void tetgenmesh::qualitystatistics() smallestdiangle, sbuf); printf(" Aspect ratio histogram:\n"); - printf(" < %-6.6g : %8d | %6.6g - %-6.6g : %8d\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 : %8d | %6.6g - %-6.6g : %8d\n", + 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 : %8d | %6.6g - : %8d\n", + 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"); @@ -29513,7 +28220,7 @@ void tetgenmesh::qualitystatistics() printf(" Face angle histogram:\n"); for (i = 0; i < 9; i++) { - printf(" %3d - %3d degrees: %8d | %3d - %3d degrees: %8d\n", + 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]); } @@ -29525,20 +28232,20 @@ void tetgenmesh::qualitystatistics() printf(" Dihedral angle histogram:\n"); // Print the three two rows: - printf(" %3d - %2d degrees: %8d | %3d - %3d degrees: %8d\n", + printf(" %3d - %2d degrees: %8ld | %3d - %3d degrees: %8ld\n", 0, 5, dihedangletable[0], 80, 110, dihedangletable[9]); - printf(" %3d - %2d degrees: %8d | %3d - %3d degrees: %8d\n", + 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: %8d | %3d - %3d degrees: %8d\n", + 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: %8d | %3d - %3d degrees: %8d\n", + printf(" %3d - %2d degrees: %8ld | %3d - %3d degrees: %8ld\n", 60, 70, dihedangletable[7], 170, 175, dihedangletable[16]); - printf(" %3d - %2d degrees: %8d | %3d - %3d degrees: %8d\n", + 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", @@ -29550,6 +28257,96 @@ void tetgenmesh::qualitystatistics() } +/////////////////////////////////////////////////////////////////////////////// +// // +// 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. // @@ -29575,26 +28372,44 @@ void tetgenmesh::statistics() tetnumber = tetrahedrons->items - hullsize; facenumber = (tetnumber * 4l + hullsize) / 2l; - printf("\n Mesh points: %ld\n", points->items); + 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); - printf(" Mesh edges: %ld\n", meshedges); + 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 boundary faces: %ld\n", subfaces->items); - printf(" Mesh boundary edges: %ld\n", subsegs->items); - if (st_segref_count > 0l) { - printf(" Steiner points in boundary edges: %ld\n", st_segref_count); + 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 in boundary faces: %ld\n", st_facref_count); + printf(" Steiner points on facets: %ld\n", st_facref_count); } - if (st_volref_count > 0l) { - printf(" Steiner points in mesh domain: %ld\n", st_volref_count); + if (st_segref_count > 0l) { + printf(" Steiner points on segments: %ld\n", st_segref_count); } } else { printf(" Convex hull faces: %ld\n", hullsize); - printf(" Convex hull edges: %ld\n", meshhulledges); + 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"); @@ -29605,6 +28420,9 @@ void tetgenmesh::statistics() qualitystatistics(); } } + if (tetrahedrons->items > 0l) { + memorystatistics(); + } } } @@ -29636,7 +28454,7 @@ void tetgenmesh::jettisonnodes() int remcount; if (!b->quiet) { - printf("Jettisoning redundants points.\n"); + printf("Jettisoning redundant points.\n"); } points->traversalinit(); @@ -29647,7 +28465,7 @@ void tetgenmesh::jettisonnodes() jetflag = (pointtype(pointloop) == DUPLICATEDVERTEX) || (pointtype(pointloop) == UNUSEDVERTEX); if (jetflag) { - // It is a duplicated point, delete it. + // It is a duplicated or unused point, delete it. pointdealloc(pointloop); remcount++; } else { @@ -29658,32 +28476,121 @@ void tetgenmesh::jettisonnodes() // Re-index the point marker as well. in->pointmarkerlist[newidx] = in->pointmarkerlist[oldidx]; } - } - newidx++; - } - oldidx++; - //if (oldidx == in->numberofpoints) { - // // Update the numbe of input points (Because some were removed). - // in->numberofpoints -= remcount; - // // Remember this number for output original input nodes. - // jettisoninverts = remcount; - //} - pointloop = pointtraverse(); - } - if (b->verbose) { - printf(" %d duplicated vertices are removed.\n", dupverts); - printf(" %d unused vertices are removed.\n", unuverts); + } + 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(); } - dupverts = 0; - unuverts = 0; - - // 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; } - /////////////////////////////////////////////////////////////////////////////// // // // numberedges() Count the number of edges, save in "meshedges". // @@ -29691,28 +28598,18 @@ void tetgenmesh::jettisonnodes() // 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 firstindex, eindex; int ishulledge; + int t1ver; int i; - // Determine the first index (0 or 1). - firstindex = b->zeroindex ? 0 : in->firstnumber; - - // First indexing all tetrahedra. - tetrahedrons->traversalinit(); - eindex = firstindex; - worktet.tet = tetrahedrontraverse(); - while (worktet.tet != NULL) { - setelemindex(worktet.tet, eindex); - eindex++; - worktet.tet = tetrahedrontraverse(); - } - meshedges = meshhulledges = 0l; tetrahedrons->traversalinit(); @@ -29758,7 +28655,7 @@ void tetgenmesh::outnodes(tetgenio* out) char outnodefilename[FILENAMESIZE]; face parentsh; point pointloop; - int nextras, bmark, marker = 0; + int nextras, bmark, marker = 0, weightDT = 0; int coordindex, attribindex; int pointnumber, firstindex; int index, i; @@ -29776,14 +28673,18 @@ void tetgenmesh::outnodes(tetgenio* out) } } - nextras = in->numberofpointattributes; + 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(1); + terminatetetgen(this, 1); } // Number of points, number of dimensions, number of point attributes, // and number of boundary markers (zero or one). @@ -29793,14 +28694,14 @@ void tetgenmesh::outnodes(tetgenio* out) out->pointlist = new REAL[points->items * 3]; if (out->pointlist == (REAL *) NULL) { printf("Error: Out of memory.\n"); - terminatetetgen(1); + 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(1); + terminatetetgen(this, 1); } } // Allocate space for 'pointmarkerlist' if necessary; @@ -29808,14 +28709,14 @@ void tetgenmesh::outnodes(tetgenio* out) out->pointmarkerlist = new int[points->items]; if (out->pointmarkerlist == (int *) NULL) { printf("Error: Out of memory.\n"); - terminatetetgen(1); + terminatetetgen(this, 1); } } if (b->psc) { out->pointparamlist = new tetgenio::pointparam[points->items]; if (out->pointparamlist == NULL) { printf("Error: Out of memory.\n"); - terminatetetgen(1); + terminatetetgen(this, 1); } } out->numberofpoints = points->items; @@ -29860,7 +28761,13 @@ void tetgenmesh::outnodes(tetgenio* out) pointloop[0], pointloop[1], pointloop[2]); for (i = 0; i < nextras; i++) { // Write an attribute. - fprintf(outfile, " %.17g", pointloop[4 + i]); + 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. @@ -29892,7 +28799,13 @@ void tetgenmesh::outnodes(tetgenio* out) // Point attributes. for (i = 0; i < nextras; i++) { // Output an attribute. - out->pointattributelist[attribindex++] = pointloop[4 + i]; + 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. @@ -29958,7 +28871,7 @@ void tetgenmesh::outmetrics(tetgenio* out) outfile = fopen(outmtrfilename, "w"); if (outfile == (FILE *) NULL) { printf("File I/O Error: Cannot create file %s.\n", outmtrfilename); - terminatetetgen(3); + terminatetetgen(this, 3); } // Number of points, number of point metrices, // fprintf(outfile, "%ld %d\n", points->items, sizeoftensor + 3); @@ -29968,7 +28881,7 @@ void tetgenmesh::outmetrics(tetgenio* out) // out->pointmtrlist = new REAL[points->items * (sizeoftensor + 3)]; out->pointmtrlist = new REAL[points->items]; if (out->pointmtrlist == (REAL *) NULL) { - terminatetetgen(1); + terminatetetgen(this, 1); } out->numberofpointmtrs = 1; // (sizeoftensor + 3); mtrindex = 0; @@ -30012,7 +28925,6 @@ void tetgenmesh::outelements(tetgenio* out) FILE *outfile = NULL; char outelefilename[FILENAMESIZE]; tetrahedron* tptr; - triface worktet, spintet; point p1, p2, p3, p4; point *extralist; REAL *talist = NULL; @@ -30020,10 +28932,9 @@ void tetgenmesh::outelements(tetgenio* out) long ntets; int firstindex, shift; int pointindex, attribindex; - int highorderindex = 10; // The reserved pointer. + int highorderindex = 11; int elementnumber; int eextras; - int ishulledge; int i; if (out == (tetgenio *) NULL) { @@ -30042,12 +28953,12 @@ void tetgenmesh::outelements(tetgenio* out) // The number of tets excluding hull tets. ntets = tetrahedrons->items - hullsize; - eextras = in->numberoftetrahedronattributes; + 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(1); + 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); @@ -30056,14 +28967,14 @@ void tetgenmesh::outelements(tetgenio* out) out->tetrahedronlist = new int[ntets * (b->order == 1 ? 4 : 10)]; if (out->tetrahedronlist == (int *) NULL) { printf("Error: Out of memory.\n"); - terminatetetgen(1); + 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(1); + terminatetetgen(this, 1); } } out->numberoftetrahedra = ntets; @@ -30077,7 +28988,7 @@ void tetgenmesh::outelements(tetgenio* out) // Determine the first index (0 or 1). firstindex = b->zeroindex ? 0 : in->firstnumber; - shift = 0; // Default no shiftment. + shift = 0; // Default no shift. if ((in->firstnumber == 1) && (firstindex == 0)) { shift = 1; // Shift the output indices by 1. } @@ -30086,8 +28997,13 @@ void tetgenmesh::outelements(tetgenio* out) tptr = tetrahedrontraverse(); elementnumber = firstindex; // in->firstnumber; while (tptr != (tetrahedron *) NULL) { - p1 = (point) tptr[4]; - p2 = (point) tptr[5]; + 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) { @@ -30095,9 +29011,9 @@ void tetgenmesh::outelements(tetgenio* out) fprintf(outfile, "%5d %5d %5d %5d %5d", elementnumber, pointmark(p1) - shift, pointmark(p2) - shift, pointmark(p3) - shift, pointmark(p4) - shift); - if (0) { // if (b->order == 2) { + if (b->order == 2) { extralist = (point *) tptr[highorderindex]; - // Tetrahedron number, indices for four points plus six extra points. + // 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, @@ -30112,7 +29028,7 @@ void tetgenmesh::outelements(tetgenio* out) tlist[pointindex++] = pointmark(p2) - shift; tlist[pointindex++] = pointmark(p3) - shift; tlist[pointindex++] = pointmark(p4) - shift; - if (0) { // if (b->order == 2) { + if (b->order == 2) { extralist = (point *) tptr[highorderindex]; tlist[pointindex++] = pointmark(extralist[0]) - shift; tlist[pointindex++] = pointmark(extralist[1]) - shift; @@ -30125,44 +29041,12 @@ void tetgenmesh::outelements(tetgenio* out) talist[attribindex++] = elemattribute(tptr, i); } } - //if (b->neighout) { - // Remember the index of this element. - setelemindex(tptr, elementnumber); - //} + // Remember the index of this element (for counting edges). + setelemindex(tptr, elementnumber); tptr = tetrahedrontraverse(); elementnumber++; } - // Count the number of edges (# Voronoi faces). - meshedges = meshhulledges = 0l; - - tetrahedrons->traversalinit(); - tptr = tetrahedrontraverse(); - while (tptr != (tetrahedron *) NULL) { - // Count the number of Voronoi faces. Look at the six edges of this - // tet. Count an edge only if this tet's pointer is smaller than - // those of other non-hull tets which share this edge. - worktet.tet = tptr; - 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++; - } - } - tptr = tetrahedrontraverse(); - } if (out == (tetgenio *) NULL) { fprintf(outfile, "# Generated by %s\n", b->commandline); @@ -30174,11 +29058,6 @@ void tetgenmesh::outelements(tetgenio* out) // // // outfaces() Output all faces to a .face file or a tetgenio object. // // // -// The total number of faces f can be calculated as following: Let t be the // -// total number of tets. Since each tet has 4 faces, the number t * 4 counts // -// each interior face twice and each hull face once. So f = (t * 4 + h) / 2, // -// where h is the total number of hull faces (which is known). // -// // /////////////////////////////////////////////////////////////////////////////// void tetgenmesh::outfaces(tetgenio* out) @@ -30194,7 +29073,13 @@ void tetgenmesh::outfaces(tetgenio* out) int faceid, marker = 0; int firstindex, shift; int facenumber; - int index; + 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); @@ -30211,13 +29096,12 @@ void tetgenmesh::outfaces(tetgenio* out) ntets = tetrahedrons->items - hullsize; faces = (ntets * 4l + hullsize) / 2l; - //bmark = !b->nobound && in->facetmarkerlist; if (out == (tetgenio *) NULL) { outfile = fopen(facefilename, "w"); if (outfile == (FILE *) NULL) { printf("File I/O Error: Cannot create file %s.\n", facefilename); - terminatetetgen(1); + terminatetetgen(this, 1); } fprintf(outfile, "%ld %d\n", faces, !b->nobound); } else { @@ -30225,14 +29109,17 @@ void tetgenmesh::outfaces(tetgenio* out) out->trifacelist = new int[faces * 3]; if (out->trifacelist == (int *) NULL) { printf("Error: Out of memory.\n"); - terminatetetgen(1); + 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(1); + terminatetetgen(this, 1); } } if (b->neighout > 1) { @@ -30240,13 +29127,12 @@ void tetgenmesh::outfaces(tetgenio* out) out->adjtetlist = new int[faces * 2]; if (out->adjtetlist == (int *) NULL) { printf("Error: Out of memory.\n"); - terminatetetgen(1); + terminatetetgen(this, 1); } } out->numberoftrifaces = faces; elist = out->trifacelist; emlist = out->trifacemarkerlist; - index = 0; } // Determine the first index (0 or 1). @@ -30271,6 +29157,16 @@ void tetgenmesh::outfaces(tetgenio* out) 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) { @@ -30306,6 +29202,10 @@ void tetgenmesh::outfaces(tetgenio* out) 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); @@ -30319,6 +29219,11 @@ void tetgenmesh::outfaces(tetgenio* out) 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; } @@ -30375,7 +29280,7 @@ void tetgenmesh::outhullfaces(tetgenio* out) outfile = fopen(facefilename, "w"); if (outfile == (FILE *) NULL) { printf("File I/O Error: Cannot create file %s.\n", facefilename); - terminatetetgen(1); + terminatetetgen(this, 1); } fprintf(outfile, "%ld 0\n", hullsize); } else { @@ -30383,7 +29288,7 @@ void tetgenmesh::outhullfaces(tetgenio* out) out->trifacelist = new int[hullsize * 3]; if (out->trifacelist == (int *) NULL) { printf("Error: Out of memory.\n"); - terminatetetgen(1); + terminatetetgen(this, 1); } out->numberoftrifaces = hullsize; elist = out->trifacelist; @@ -30454,6 +29359,14 @@ void tetgenmesh::outsubfaces(tetgenio* out) 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"); @@ -30467,13 +29380,11 @@ void tetgenmesh::outsubfaces(tetgenio* out) } } - //bmark = !b->nobound && in->facetmarkerlist; - if (out == (tetgenio *) NULL) { outfile = fopen(facefilename, "w"); if (outfile == (FILE *) NULL) { printf("File I/O Error: Cannot create file %s.\n", facefilename); - terminatetetgen(3); + terminatetetgen(this, 3); } // Number of subfaces. fprintf(outfile, "%ld %d\n", subfaces->items, !b->nobound); @@ -30481,20 +29392,23 @@ void tetgenmesh::outsubfaces(tetgenio* out) // Allocate memory for 'trifacelist'. out->trifacelist = new int[subfaces->items * 3]; if (out->trifacelist == (int *) NULL) { - terminatetetgen(1); + 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(1); + terminatetetgen(this, 1); } } if (b->neighout > 1) { // '-nn' switch. out->adjtetlist = new int[subfaces->items * 2]; if (out->adjtetlist == (int *) NULL) { - terminatetetgen(1); + terminatetetgen(this, 1); } } out->numberoftrifaces = subfaces->items; @@ -30514,25 +29428,54 @@ void tetgenmesh::outsubfaces(tetgenio* out) 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) { - // 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. 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 (in->facetmarkerlist) { - faceid = shellmark(faceloop) - 1; - marker = in->facetmarkerlist[faceid]; + if (b->refine) { // -r option. + if (in->trifacemarkerlist) { + marker = shellmark(faceloop); + } else { + marker = 1; // Default marker for a subface is 1. + } } else { - marker = 1; // Default marker for a subface is 1. + 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) { @@ -30552,6 +29495,10 @@ void tetgenmesh::outsubfaces(tetgenio* out) 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); } @@ -30564,6 +29511,11 @@ void tetgenmesh::outsubfaces(tetgenio* out) 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; } @@ -30602,9 +29554,15 @@ void tetgenmesh::outedges(tetgenio* out) int ishulledge; int firstindex, shift; int edgenumber, marker; - int index, index1; + 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"); @@ -30618,11 +29576,25 @@ void tetgenmesh::outedges(tetgenio* out) } } + 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(1); + terminatetetgen(this, 1); } // Write the number of edges, boundary markers (0 or 1). fprintf(outfile, "%ld %d\n", meshedges, !b->nobound); @@ -30631,16 +29603,20 @@ void tetgenmesh::outedges(tetgenio* out) out->edgelist = new int[meshedges * 2]; if (out->edgelist == (int *) NULL) { printf("Error: Out of memory.\n"); - terminatetetgen(1); + 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; - index = 0; - index1 = 0; // if (!b->nobound) } // Determine the first index (0 or 1). @@ -30654,9 +29630,7 @@ void tetgenmesh::outedges(tetgenio* out) tetloop.tet = tetrahedrontraverse(); edgenumber = firstindex; // in->firstnumber; while (tetloop.tet != (tetrahedron *) NULL) { - // Count the number of Voronoi faces. Look at the six edges of this - // tet. Count an edge only if this tet's pointer is smaller than - // those of other non-hull tets which share this edge. + // Count the number of Voronoi faces. worktet.tet = tetloop.tet; for (i = 0; i < 6; i++) { worktet.ver = edge2ver[i]; @@ -30674,13 +29648,24 @@ void tetgenmesh::outedges(tetgenio* out) 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) { @@ -30704,6 +29689,13 @@ void tetgenmesh::outedges(tetgenio* out) 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"); } @@ -30737,6 +29729,18 @@ void tetgenmesh::outsubsegments(tetgenio* out) 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"); @@ -30754,19 +29758,25 @@ void tetgenmesh::outsubsegments(tetgenio* out) outfile = fopen(edgefilename, "w"); if (outfile == (FILE *) NULL) { printf("File I/O Error: Cannot create file %s.\n", edgefilename); - terminatetetgen(3); + terminatetetgen(this, 3); } // Number of subsegments. fprintf(outfile, "%ld 1\n", subsegs->items); } else { // Allocate memory for 'edgelist'. - out->edgelist = new int[subsegs->items * 2]; + out->edgelist = new int[subsegs->items * (b->order == 1 ? 2 : 3)]; if (out->edgelist == (int *) NULL) { - terminatetetgen(1); + 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(1); + terminatetetgen(this, 1); + } + if (b->neighout > 1) { + out->edgeadjtetlist = new int[subsegs->items]; } out->numberofedges = subsegs->items; elist = out->edgelist; @@ -30787,18 +29797,64 @@ void tetgenmesh::outsubsegments(tetgenio* out) 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 %d\n", edgenumber, - pointmark(torg) - shift, pointmark(tdest) - shift, marker); + 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); @@ -30847,7 +29903,7 @@ void tetgenmesh::outneighbors(tetgenio* out) outfile = fopen(neighborfilename, "w"); if (outfile == (FILE *) NULL) { printf("File I/O Error: Cannot create file %s.\n", neighborfilename); - terminatetetgen(1); + terminatetetgen(this, 1); } // Number of tetrahedra, four faces per tetrahedron. fprintf(outfile, "%ld %d\n", ntets, 4); @@ -30856,7 +29912,7 @@ void tetgenmesh::outneighbors(tetgenio* out) out->neighborlist = new int[ntets * 4]; if (out->neighborlist == (int *) NULL) { printf("Error: Out of memory.\n"); - terminatetetgen(1); + terminatetetgen(this, 1); } nlist = out->neighborlist; } @@ -30906,12 +29962,13 @@ void tetgenmesh::outneighbors(tetgenio* out) // 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 interal Delaunay edge. At a // +// 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. // // // /////////////////////////////////////////////////////////////////////////////// @@ -30921,7 +29978,7 @@ void tetgenmesh::outvoronoi(tetgenio* out) FILE *outfile = NULL; char outfilename[FILENAMESIZE]; tetgenio::voroedge *vedge = NULL; - tetgenio::vorofacet *vfacet; + tetgenio::vorofacet *vfacet = NULL; arraypool *tetlist, *ptlist; triface tetloop, worktet, spintet, firsttet; point pt[4], ploop, neipt; @@ -30934,6 +29991,8 @@ void tetgenmesh::outvoronoi(tetgenio* out) 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); @@ -30974,14 +30033,15 @@ void tetgenmesh::outvoronoi(tetgenio* out) // The number of Delaunay faces (Voronoi edges). faces = (4l * ntets + hullsize) / 2l; // The number of Delaunay edges (Voronoi faces). - // edges = points->items + faces - ntets - 1; - edges = meshedges; // Counted in outelements() or numberedges(); + 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(3); + terminatetetgen(this, 3); } // Number of voronoi points, 3 dim, no attributes, no marker. fprintf(outfile, "%ld 3 0 0\n", ntets); @@ -30990,7 +30050,7 @@ void tetgenmesh::outvoronoi(tetgenio* out) out->numberofvpoints = (int) ntets; out->vpointlist = new REAL[out->numberofvpoints * 3]; if (out->vpointlist == (REAL *) NULL) { - terminatetetgen(1); + terminatetetgen(this, 1); } } @@ -31004,7 +30064,12 @@ void tetgenmesh::outvoronoi(tetgenio* out) pt[i] = (point) tetloop.tet[4 + i]; setpoint2tet(pt[i], encode(tetloop)); } - circumsphere(pt[0], pt[1], pt[2], pt[3], ccent, NULL); + 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]); @@ -31041,7 +30106,7 @@ void tetgenmesh::outvoronoi(tetgenio* out) outfile = fopen(outfilename, "w"); if (outfile == (FILE *) NULL) { printf("File I/O Error: Cannot create file %s.\n", outfilename); - terminatetetgen(3); + terminatetetgen(this, 3); } // Number of Voronoi edges, no marker. fprintf(outfile, "%ld 0\n", faces); @@ -31143,7 +30208,7 @@ void tetgenmesh::outvoronoi(tetgenio* out) outfile = fopen(outfilename, "w"); if (outfile == (FILE *) NULL) { printf("File I/O Error: Cannot create file %s.\n", outfilename); - terminatetetgen(3); + terminatetetgen(this, 3); } // Number of Voronoi faces. fprintf(outfile, "%ld 0\n", edges); @@ -31151,7 +30216,7 @@ void tetgenmesh::outvoronoi(tetgenio* out) out->numberofvfacets = edges; out->vfacetlist = new tetgenio::vorofacet[out->numberofvfacets]; if (out->vfacetlist == (tetgenio::vorofacet *) NULL) { - terminatetetgen(1); + terminatetetgen(this, 1); } } @@ -31257,7 +30322,7 @@ void tetgenmesh::outvoronoi(tetgenio* out) outfile = fopen(outfilename, "w"); if (outfile == (FILE *) NULL) { printf("File I/O Error: Cannot create file %s.\n", outfilename); - terminatetetgen(3); + terminatetetgen(this, 3); } // Number of Voronoi cells. fprintf(outfile, "%ld\n", points->items - unuverts - dupverts); @@ -31265,7 +30330,7 @@ void tetgenmesh::outvoronoi(tetgenio* out) out->numberofvcells = points->items - unuverts - dupverts; out->vcelllist = new int*[out->numberofvcells]; if (out->vcelllist == (int **) NULL) { - terminatetetgen(1); + terminatetetgen(this, 1); } } @@ -31277,7 +30342,8 @@ void tetgenmesh::outvoronoi(tetgenio* out) vpointcount = 0; while (ploop != (point) NULL) { if ((pointtype(ploop) != UNUSEDVERTEX) && - (pointtype(ploop) != DUPLICATEDVERTEX)) { + (pointtype(ploop) != DUPLICATEDVERTEX) && + (pointtype(ploop) != NREGULARVERTEX)) { getvertexstar(1, ploop, tetlist, ptlist, NULL); // Mark all vertices. Check if it is a hull vertex. ishullvert = 0; @@ -31337,14 +30403,6 @@ void tetgenmesh::outvoronoi(tetgenio* out) if (out == (tetgenio *) NULL) { fprintf(outfile, "\n"); } - // DEBUG BEGIN - for (i = 0; i < ptlist->objects; i++) { - neipt = * (point *) fastlookup(ptlist, i); - if (neipt != dummypoint) { - assert(!pinfected(neipt)); - } - } - // DEBUG END tetlist->restart(); ptlist->restart(); vpointcount++; @@ -31583,13 +30641,18 @@ void tetgenmesh::outmesh2medit(char* mfilename) tetrahedrons->traversalinit(); tetptr = tetrahedrontraverse(); while (tetptr != (tetrahedron *) NULL) { - p1 = (point) tetptr[4]; - p2 = (point) tetptr[5]; + 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 (in->numberoftetrahedronattributes > 0) { + if (numelemattrib > 0) { fprintf(outfile, " %.17g", elemattribute(tetptr, 0)); } else { fprintf(outfile, " 0"); @@ -31639,13 +30702,18 @@ void tetgenmesh::outmesh2vtk(char* ofilename) { FILE *outfile; char vtkfilename[FILENAMESIZE]; - point pointloop; + 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; @@ -31693,15 +30761,16 @@ void tetgenmesh::outmesh2vtk(char* ofilename) tetrahedrons->traversalinit(); tptr = tetrahedrontraverse(); //elementnumber = firstindex; // in->firstnumber; - if (b->order == 2) { - printf(" Write VTK not implemented for order 2 elements \n"); - return; - } while (tptr != (tetrahedron *) NULL) { - point p1 = (point) tptr[4]; - point p2 = (point) tptr[5]; - point p3 = (point) tptr[6]; - point p4 = (point) tptr[7]; + 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; @@ -31717,6 +30786,20 @@ void tetgenmesh::outmesh2vtk(char* ofilename) } 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); } @@ -31756,184 +30839,170 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, tetgenio *addin, tetgenio *bgmin) { tetgenmesh m; - clock_t tv[17]; // Timing informations (defined in time.h) + 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 ((bgmin != NULL) && - ((bgmin->numberofpoints > 0) && (bgmin->pointmtrlist != NULL))) { + if (b->metric && bgmin && (bgmin->numberofpoints > 0)) { m.bgm = new tetgenmesh(); // Create an empty background mesh. m.bgm->b = b; m.bgm->in = bgmin; } -#ifdef INEXACT_GEOM_PRED - if (!b->quiet) { - printf("Using inexact geometric predicates.\n"); - } -#else - exactinit(); -#endif - 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) { + if (b->refine) { // -r m.reconstructmesh(); - } else { // b->plc - if (!b->diagnose) { - m.incrementaldelaunay(tv[16]); - } + } else { // -p + m.incrementaldelaunay(ts[0]); } tv[2] = clock(); if (!b->quiet) { if (b->refine) { - printf("Mesh reconstruction seconds: %g\n", - (tv[2] - tv[1]) / (REAL) CLOCKS_PER_SEC); + printf("Mesh reconstruction seconds: %g\n", ((REAL)(tv[2]-tv[1])) / cps); } else { - if (!b->diagnose) { - printf("Delaunay seconds: %g\n", - (tv[2] - tv[1]) / (REAL) CLOCKS_PER_SEC); - if (b->verbose) { - printf(" Point sorting seconds: %g\n", - (tv[16] - tv[1]) / (REAL) CLOCKS_PER_SEC); -#ifdef WITH_RUNTIME_COUNTERS - printf(" Point location seconds: %g\n", - m.t_ptloc / (REAL) CLOCKS_PER_SEC); - printf(" Point insertion seconds: %g\n", - m.t_ptinsert / (REAL) CLOCKS_PER_SEC); -#endif - } + 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) { + if (b->plc && !b->refine) { // -p m.meshsurface(); - } - tv[3] = clock(); + ts[0] = clock(); - if (!b->quiet) { - if (b->plc) { - printf("Surface mesh seconds: %g\n", - (tv[3] - tv[2]) / (REAL) CLOCKS_PER_SEC); + 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; } } - if (b->plc && b->diagnose) { // -d - m.detectinterfaces(); + tv[3] = clock(); + + if ((b->metric) && (m.bgm != NULL)) { // -m + m.bgm->initializepools(); + m.bgm->transfernodes(); + m.bgm->reconstructmesh(); - tv[4] = clock(); + ts[0] = clock(); if (!b->quiet) { - printf("Self-intersection seconds: %g\n", - (tv[4] - tv[3]) / (REAL) CLOCKS_PER_SEC); + printf("Background mesh reconstruct seconds: %g\n", + ((REAL)(ts[0] - tv[3])) / cps); } - // Only output when self-intersecting faces exist. - if (m.subfaces->items > 0l) { - m.outnodes(out); - m.outsubfaces(out); - } + if (b->metric) { // -m + m.interpolatemeshsize(); - return; - } + ts[1] = clock(); - if (b->plc) { - if (b->nobisect) { // with -Y option - m.recoverboundary(tv[15]); - } else { - m.constraineddelaunay(tv[15]); + if (!b->quiet) { + printf("Size interpolating seconds: %g\n",((REAL)(ts[1]-ts[0])) / cps); + } } } tv[4] = clock(); - if (!b->quiet) { - if (b->plc) { - printf("Boundary recovery seconds: %g\n", - (tv[4] - tv[3]) / (REAL) CLOCKS_PER_SEC); + 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", - (tv[15] - tv[3]) / (REAL) CLOCKS_PER_SEC); - printf(" Facet recovery seconds: %g\n", - (tv[4] - tv[15]) / (REAL) CLOCKS_PER_SEC); + printf(" Segment recovery seconds: %g\n",((REAL)(ts[0]-tv[4]))/ cps); + printf(" Facet recovery seconds: %g\n", ((REAL)(ts[1]-ts[0])) / cps); } } - } - if (b->plc && !b->convex) { m.carveholes(); - } - tv[5] = clock(); + ts[2] = clock(); - if (!b->quiet) { - if (b->plc && !b->convex) { - printf("Exterior tets removal seconds: %g\n", - (tv[5] - tv[4]) / (REAL) CLOCKS_PER_SEC); + if (!b->quiet) { + printf("Exterior tets removal seconds: %g\n",((REAL)(ts[2]-ts[1]))/cps); } - } - if (b->plc && b->nobisect) { - m.suppresssteinerpoints(); - } + if (b->nobisect) { // -Y + if (m.subvertstack->objects > 0l) { + m.suppresssteinerpoints(); - tv[6] = clock(); + ts[3] = clock(); - if (!b->quiet) { - if (b->plc && b->nobisect) { - printf("Steiner suppression seconds: %g\n", - (tv[6] - tv[5]) / (REAL) CLOCKS_PER_SEC); + if (!b->quiet) { + printf("Steiner suppression seconds: %g\n", + ((REAL)(ts[3]-ts[2]))/cps); + } + } } } - if (b->plc && b->nobisect) { - m.recoverdelaunay(); - } - - tv[7] = clock(); - - if (!b->quiet) { - if (b->plc && b->nobisect) { - printf("Delaunay recovery seconds: %g\n", - (tv[7] - tv[6]) / (REAL) CLOCKS_PER_SEC); - } - } + tv[5] = clock(); - if ((b->plc || b->refine) && (b->metric && (m.bgm != NULL))) { - m.bgm->initializepools(); - m.bgm->transfernodes(); - m.bgm->reconstructmesh(); + if (b->coarsen) { // -R + m.meshcoarsening(); } - tv[8] = clock(); + tv[6] = clock(); if (!b->quiet) { - if ((b->plc || b->refine) && (b->metric && (m.bgm != NULL))) { - printf("Background mesh reconstruct seconds: %g\n", - (tv[8] - tv[7]) / (REAL) CLOCKS_PER_SEC); + if (b->coarsen) { + printf("Mesh coarsening seconds: %g\n", ((REAL)(tv[6] - tv[5])) / cps); } } - if ((b->plc || b->refine) && (b->metric && (m.bgm != NULL))) { - m.interpolatemeshsize(); + if ((b->plc && b->nobisect) || b->coarsen) { + m.recoverdelaunay(); } - tv[9] = clock(); + tv[7] = clock(); if (!b->quiet) { - if ((b->plc || b->refine) && (b->metric && (m.bgm != NULL))) { - printf("Size interpolating seconds: %g\n", - (tv[9] - tv[8]) / (REAL) CLOCKS_PER_SEC); + if ((b->plc && b->nobisect) || b->coarsen) { + printf("Delaunay recovery seconds: %g\n", ((REAL)(tv[7] - tv[6]))/cps); } } @@ -31943,44 +31012,37 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, } } - tv[10] = clock(); + tv[8] = clock(); if (!b->quiet) { - if ((b->plc || b->refine) && b->insertaddpoints) { + if ((b->plc || b->refine) && b->insertaddpoints) { // -i if ((addin != NULL) && (addin->numberofpoints > 0)) { - printf("Constrained points seconds: %g\n", - (tv[10] - tv[9]) / (REAL) CLOCKS_PER_SEC); + printf("Constrained points seconds: %g\n", ((REAL)(tv[8]-tv[7]))/cps); } } } - - tv[11] = clock(); - - - if ((b->plc || b->refine) && b->quality) { - m.delaunayrefinement(); + if (b->quality) { + m.delaunayrefinement(); } - tv[12] = clock(); + tv[9] = clock(); if (!b->quiet) { - if ((b->plc || b->refine) && b->quality) { - printf("Refinement seconds: %g\n", - (tv[12] - tv[11]) / (REAL) CLOCKS_PER_SEC); + if (b->quality) { + printf("Refinement seconds: %g\n", ((REAL)(tv[9] - tv[8])) / cps); } } if ((b->plc || b->refine) && (b->optlevel > 0)) { - m.optimizemesh(1); + m.optimizemesh(); } - tv[13] = clock(); + tv[10] = clock(); if (!b->quiet) { if ((b->plc || b->refine) && (b->optlevel > 0)) { - printf("Optimization seconds: %g\n", - (tv[13] - tv[12]) / (REAL) CLOCKS_PER_SEC); + printf("Optimization seconds: %g\n", ((REAL)(tv[10] - tv[9])) / cps); } } @@ -31989,6 +31051,9 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, m.jettisonnodes(); } + if ((b->order == 2) && !b->convex) { + m.highorder(); + } if (!b->quiet) { printf("\n"); @@ -32007,11 +31072,10 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, m.outnodes(out); } - if (b->noelewritten == 1) { + if (b->noelewritten) { if (!b->quiet) { printf("NOT writing an .ele file.\n"); } - m.numberedges(); } else { if (m.tetrahedrons->items > 0l) { m.outelements(out); @@ -32041,11 +31105,17 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, } - if (b->edgesout) { - if (b->edgesout > 1) { - m.outedges(out); // -ee, output all mesh edges. + 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 { - m.outsubsegments(out); // -e, only output subsegments. + if (b->plc || b->refine) { + m.outsubsegments(out); // output subsegments. + } } } @@ -32078,13 +31148,11 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, } - tv[14] = clock(); + tv[11] = clock(); if (!b->quiet) { - printf("\nOutput seconds: %g\n", - (tv[14] - tv[13]) / (REAL) CLOCKS_PER_SEC); - printf("Total running seconds: %g\n", - (tv[14] - tv[0]) / (REAL) CLOCKS_PER_SEC); + 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) { @@ -32107,7 +31175,7 @@ void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out, /////////////////////////////////////////////////////////////////////////////// // // -// main() The entrance for running TetGen from command line. // +// main() The command line interface of TetGen. // // // /////////////////////////////////////////////////////////////////////////////// @@ -32117,7 +31185,7 @@ int main(int argc, char *argv[]) /////////////////////////////////////////////////////////////////////////////// // // -// tetrahedralize() The entrance for calling TetGen from another program. // +// tetrahedralize() The library interface of TetGen. // // // /////////////////////////////////////////////////////////////////////////////// @@ -32134,17 +31202,17 @@ void tetrahedralize(char *switches, tetgenio *in, tetgenio *out, tetgenio in, addin, bgmin; if (!b.parse_commandline(argc, argv)) { - terminatetetgen(10); + terminatetetgen(NULL, 10); } // Read input files. if (b.refine) { // -r if (!in.load_tetmesh(b.infilename, (int) b.object)) { - terminatetetgen(10); + terminatetetgen(NULL, 10); } } else { // -p if (!in.load_plc(b.infilename, (int) b.object)) { - terminatetetgen(10); + terminatetetgen(NULL, 10); } } if (b.insertaddpoints) { // -i @@ -32163,7 +31231,7 @@ void tetrahedralize(char *switches, tetgenio *in, tetgenio *out, #else // with TETLIBRARY if (!b.parse_commandline(switches)) { - terminatetetgen(10); + terminatetetgen(NULL, 10); } tetrahedralize(&b, in, out, addin, bgmin); diff --git a/contrib/Tetgen1.5/tetgen.h b/contrib/Tetgen1.5/tetgen.h index 41634d03f49ee9d805eb18e5511c672f8d23478a..3196e031f5318369c6842296cf58fa45a2af611b 100644 --- a/contrib/Tetgen1.5/tetgen.h +++ b/contrib/Tetgen1.5/tetgen.h @@ -2,21 +2,10 @@ // // // TetGen // // // -// A Quality Tetrahedral Mesh Generator and 3D Delaunay Triangulator // +// A Quality Tetrahedral Mesh Generator and A 3D Delaunay Triangulator // // // // Version 1.5 // -// February 21, 2012 // -// // -// PRE-RELEASE TEST CODE. // -// PLEASE DO NOT DISTRIBUTE !! // -// PLEASE HELP ME TO IMPROVE IT !! // -// // -// Copyright (C) 2002--2012 // -// Hang Si // -// Research Group: Numerical Mathematics and Scientific Computing // -// Weierstrass Institute for Applied Analysis and Stochastics (WIAS) // -// Mohrenstr. 39, 10117 Berlin, Germany // -// Hang.Si@wias-berlin.de // +// 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. // @@ -28,46 +17,51 @@ #ifndef tetgenH #define tetgenH -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <math.h> -#include <time.h> -#include <assert.h> - // 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 are -// inserted in places where I hope to catch bugs. +// 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 -// To insert lots of self-checks for internal errors, define the SELF_CHECK -// symbol. This will slow down the program a bit. +// 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 SELF_CHECK +#define REAL double // #define REAL float -// Default, TetGen uses the double precision for a real number. +// Maximum number of characters in a file name (including the null). -#define REAL double +#define FILENAMESIZE 1024 -// 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++ doesn't ship with this header file yet. We -// need to define them. -// Thanks to Steven G. Johnson (MIT) for the following code. +// Maximum number of chars in a line read from a file (including the null). -// Define the _MSC_VER symbol if you are using Microsoft Visual C++. +#define INPUTLINESIZE 2048 -// #define _MSC_VER +// TetGen only uses the C standard library. -// Define the _WIN64 symbol if you are running TetGen on Win64. +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include <time.h> +#include <assert.h> -// #define _WIN64 +// 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 @@ -81,31 +75,39 @@ # include <stdint.h> #endif -// 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 - /////////////////////////////////////////////////////////////////////////////// // // // tetgenio // // // -// A structure for transfering data into and out of TetGen. // +// 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). // // // -// It holds 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. // +// 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 must priorily free them by theirself // -// and set the pointers to NULLs. // +// "new" operator. Otherwise, the user is responsible to free them and all // +// pointers must be NULL before the call of the destructor. // // // /////////////////////////////////////////////////////////////////////////////// @@ -114,18 +116,18 @@ class tetgenio { public: // A "polygon" describes a simple polygon (no holes). It is not necessarily - // convex. Each polygon contains number of corners (points) and the same - // number of sides (edges). - // Note that the points of the polygon must be given in either counter- - // clockwise or clockwise order and they form a ring, so every two - // consective points forms an edge of the polygon. + // 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 facet. Each facet is a polygonal region possibly - // with holes, edges, and points in it. + // 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; @@ -157,19 +159,6 @@ public: int *elist; } vorofacet; - // The periodic boundary condition group data structure. A "pbcgroup" - // contains the definition of a pbc and the list of pbc point pairs. - // 'fmark1' and 'fmark2' are the facetmarkers of the two pbc facets f1 - // and f2, respectively. 'transmat' is the transformation matrix which - // maps a point in f1 into f2. An array of pbc point pairs are saved - // in 'pointpairlist'. The first point pair is at indices [0] and [1], - // followed by remaining pairs. Two integers per pair. - typedef struct { - int fmark1, fmark2; - REAL transmat[4][4]; - int numberofpointpairs; - int *pointpairlist; - } pbcgroup; // Additional parameters associated with an input (or mesh) vertex. // These informations are provided by CAD libraries. @@ -179,9 +168,6 @@ public: int type; // 0, 1, or 2. } pointparam; - // A callback function for mesh refinement. - typedef bool (* TetSizeFunc)(REAL*, REAL*, REAL*, REAL*, REAL*, REAL); - // Callback functions for meshing PSCs. typedef REAL (* GetVertexParamOnEdge)(void*, int, int); typedef void (* GetSteinerOnEdge)(void*, int, REAL, REAL*); @@ -189,6 +175,9 @@ public: 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; @@ -206,95 +195,108 @@ public: // 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. + // 'pointmarkerlist': An array of point markers; one integer per point. REAL *pointlist; REAL *pointattributelist; REAL *pointmtrlist; - int *pointmarkerlist; + int *pointmarkerlist; pointparam *pointparamlist; int numberofpoints; int numberofpointattributes; int numberofpointmtrs; - // `elementlist': An array of element (triangle or tetrahedron) corners. - // The first element's first corner is at index [0], followed by its - // other corners in counterclockwise order, followed by any other - // nodes if the element represents a nonlinear element. Each element - // occupies `numberofcorners' ints. - // `elementattributelist': An array of element attributes. Each - // element's attributes occupy `numberofelementattributes' REALs. - // `elementconstraintlist': An array of constraints, i.e. triangle's - // area or tetrahedron's volume; one REAL per element. Input only. - // `neighborlist': An array of element neighbors; 3 or 4 ints per - // element. Output only. - int *tetrahedronlist; + // '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 *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. + // '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. The first hole's x, y and z - // coordinates are at indices [0], [1] and [2], followed by the - // remaining holes. Three REALs per hole. + // '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 regional attributes and volume constraints. - // The first constraint's x, y and z coordinates are at indices [0], - // [1] and [2], followed by the regional attribute at index [3], foll- - // owed by the maximum volume at index [4]. Five REALs per constraint. - // Note that each regional attribute is used only if you select the `A' + // '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). + // 'a' switch (with no number following). REAL *regionlist; int numberofregions; - // `facetconstraintlist': An array of facet maximal area constraints. - // Two REALs per constraint. The first (at index [0]) is the facet - // marker (cast it to int), the second (at index [1]) is its maximum - // area bound. + // '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 max. length constraints. - // Three REALs per constraint. The first two (at indcies [0] and [1]) - // are the indices of the endpoints of the segment, the third (at index - // [2]) is its maximum length bound. + // '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; - // 'pbcgrouplist': An array of periodic boundary condition groups. - pbcgroup *pbcgrouplist; - int numberofpbcgroups; - - // `trifacelist': An array of triangular face endpoints. The first - // face's endpoints are at indices [0], [1] and [2], followed by the - // remaining faces. Three ints per face. - // `adjtetlist': An array of adjacent tetrahedra to the faces of - // trifacelist. Each face has at most two adjacent tets, the first - // face's adjacent tets are at [0], [1]. Two ints per face. A '-1' - // indicates outside (no adj. tet). This list is output when '-nn' - // switch is used. - // `trifacemarkerlist': An array of face markers; one int per face. + + // '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 *adjtetlist; 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. + // '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). @@ -312,9 +314,6 @@ public: int numberofvfacets; int numberofvcells; - // A callback function. - TetSizeFunc tetunsuitable; - // Variable (and callback functions) for meshing PSCs. void *geomhandle; GetVertexParamOnEdge getvertexparamonedge; @@ -323,6 +322,9 @@ public: 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*); @@ -370,8 +372,8 @@ public: // Initialize routine. void initialize() { - firstnumber = 0; // Default item index is numbered from Zero. - mesh_dim = 3; // Default mesh dimension is 3. + firstnumber = 0; + mesh_dim = 3; useindex = 1; pointlist = (REAL *) NULL; @@ -388,22 +390,25 @@ public: tetrahedronvolumelist = (REAL *) NULL; neighborlist = (int *) NULL; numberoftetrahedra = 0; - numberofcorners = 4; // Default is 4 nodes per element. + numberofcorners = 4; numberoftetrahedronattributes = 0; trifacelist = (int *) NULL; - adjtetlist = (int *) NULL; trifacemarkerlist = (int *) NULL; + o2facelist = (int *) NULL; + adjtetlist = (int *) NULL; numberoftrifaces = 0; - facetlist = (facet *) NULL; - facetmarkerlist = (int *) NULL; - numberoffacets = 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; @@ -415,8 +420,6 @@ public: segmentconstraintlist = (REAL *) NULL; numberofsegmentconstraints = 0; - pbcgrouplist = (pbcgroup *) NULL; - numberofpbcgroups = 0; vpointlist = (REAL *) NULL; vedgelist = (voroedge *) NULL; @@ -437,17 +440,12 @@ public: getsteineronface = NULL; } - // Free the memory allocated in 'tetgenio'. + // Free the memory allocated in 'tetgenio'. Note that it assumes that the + // memory was allocated by the "new" operator (C++). void deinitialize() { - facet *f; - polygon *p; - pbcgroup *pg; int i, j; - // Notince that this routine assumes that the memory was allocated by - // C++ memory allocation operator 'new'. - if (pointlist != (REAL *) NULL) { delete [] pointlist; } @@ -480,12 +478,15 @@ public: if (trifacelist != (int *) NULL) { delete [] trifacelist; } - if (adjtetlist != (int *) NULL) { - delete [] adjtetlist; - } if (trifacemarkerlist != (int *) NULL) { delete [] trifacemarkerlist; } + if (o2facelist != (int *) NULL) { + delete [] o2facelist; + } + if (adjtetlist != (int *) NULL) { + delete [] adjtetlist; + } if (edgelist != (int *) NULL) { delete [] edgelist; @@ -493,8 +494,16 @@ public: 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++) { @@ -524,15 +533,6 @@ public: if (segmentconstraintlist != (REAL *) NULL) { delete [] segmentconstraintlist; } - if (pbcgrouplist != (pbcgroup *) NULL) { - for (i = 0; i < numberofpbcgroups; i++) { - pg = &(pbcgrouplist[i]); - if (pg->pointpairlist != (int *) NULL) { - delete [] pg->pointpairlist; - } - } - delete [] pbcgrouplist; - } if (vpointlist != (REAL *) NULL) { delete [] vpointlist; } @@ -563,12 +563,16 @@ public: // // // tetgenbehavior // // // -// A structure to maintain the switches and parameters of TetGen. // +// A structure for maintaining the switches and parameters used by TetGen's // +// mesh data structure and algorithms. // // // -// parse_commandline() provides an simple interface to set the vaules of the // -// variables. It accepts the standard parameters (e.g., 'argc' and 'argv') // -// that pass to C/C++ main() function. Alternatively a string which contains // -// the command line options can be used as its parameter. // +// 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. // // // /////////////////////////////////////////////////////////////////////////////// @@ -576,93 +580,98 @@ class tetgenbehavior { public: - // Switches of TetGen. They are briefly described in the function syntax(). - // Plerase consult the user's manual for complete explanations. The last - // column indicates their initial values. - int plc; // '-p' switch, 0. - int psc; // '-s' switch, 0. - int quality; // '-q' switch, 0. - int refine; // '-r' switch, 0. - int metric; // '-m' switch, 0. - int nobisect; // '-Y' switch, 0. - int weighted; // '-w' switch, 0. - int varvolume; // '-a' switch without number, 0. - int fixedvolume; // '-a' switch with number, 0. - int incrflip; // '-l' switch, 0. - int flipinsert; // '-L' switch, 0. - int btree; // '-u' switch, 0. - int hilbertcurve; // '-U' switch, 0. - int insertaddpoints; // '-i' switch, 0. - int regionattrib; // '-A' switch, 0. - int conforming; // '-D' switch, 0. - int diagnose; // '-d' switch, 0. - int convex; // '-c' switch, 0. - int zeroindex; // '-z' switch, 0. - int facesout; // '-f' switch, 0. - int edgesout; // '-e' switch, 0. - int neighout; // '-n' switch, 0. - int voroout; // '-v',switch, 0. - int meditview; // '-g' switch, 0. - int vtkview; // '-K' switch, 0. - int nobound; // '-B' switch, 0. - int nonodewritten; // '-N' switch, 0. - int noelewritten; // '-E' switch, 0. - int nofacewritten; // '-F' switch, 0. - int noiterationnum; // '-I' switch, 0. - int nomerge; // '-M' switch, 0. - int nojettison; // '-J' switch, 0. - int docheck; // '-C' switch, 0. - int quiet; // '-Q' switch, 0. - int verbose; // '-V' switch, 0. - - // Parameters of TetGen. They are numbers specified after switches. The - // last colume indicates their initial values. - int vertexperblock; // after '-b', 4092. - int tetrahedraperblock; // after '-b', 8188. - int shellfaceperblock; // after '-b', 4092. - int nobisect_param; // after '-Y', 1. - int weighted_param; // after '-w', 0. - int flipinsert_random; // after '-L', 0. - int flipinsert_ori4dexact; // after '-L', 0. - int fliplinklevel; // after '-L', -1. - int flipstarsize; // after '-LL', -1. - int fliplinklevelinc; // after '-LLLL', 1. - int max_btreenode_size; // after '-u', 100. - int reflevel; // after '-D', 3. - int optlevel; // after '-O', 7. - int optpasses; // after '-OO', 3. - int optmaxfliplevel; // after '-OOO', 2. - int delmaxfliplevel; // after '-OOOO', 1. - int optmaxflipstarsize; // after '-OOOOO', 10. - int order; // after '-o', 1. - int steinerleft; // after '-S', 0. - REAL facet_ang_tol; // after '-p', 179.9. - REAL maxvolume; // after '-a', -1.0. - REAL minratio; // after '-q', 0.0. - REAL mindihedral; // after '-qq', 5.0. - REAL optmaxdihedral; // after '-o', 165.0. - REAL optminsmtdihed; // after '-oo', 175.0. - REAL optminslidihed; // after '-ooo', 179.0. - REAL epsilon; // after '-T', 1.0e-8. - REAL minedgelength; // The shortest length of an edge, after '-l', 0.0. - - // Variables used to save command line switches and in/out file names. + // 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 type of TetGen. They are recognized by the input file - // extensions. Currently the following objects are supported: + // 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); + // - 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 commandline switch + // 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; @@ -681,17 +690,19 @@ public: { plc = 0; psc = 0; - quality = 0; refine = 0; - metric = 0; + quality = 0; nobisect = 0; + coarsen = 0; + metric = 0; weighted = 0; - varvolume = 0; - fixedvolume = 0; + brio_hilbert = 1; incrflip = 0; flipinsert = 0; - btree = 0; - hilbertcurve = 0; + varvolume = 0; + fixedvolume = 0; + noexact = 0; + nostaticfilter = 0; insertaddpoints = 0; regionattrib = 0; conforming = 0; @@ -709,8 +720,10 @@ public: noelewritten = 0; nofacewritten = 0; noiterationnum = 0; - nomerge = 0; + nomergefacet = 0; + nomergevertex = 0; nojettison = 0; + reversetetori = 0; docheck = 0; quiet = 0; verbose = 0; @@ -718,31 +731,34 @@ public: vertexperblock = 4092; tetrahedraperblock = 8188; shellfaceperblock = 4092; - nobisect_param = 1; + nobisect_param = 2; + addsteiner_algo = 1; + coarsen_param = 0; weighted_param = 0; - flipinsert_random = 0; - flipinsert_ori4dexact = 0; fliplinklevel = -1; // No limit on linklevel. flipstarsize = -1; // No limit on flip star size. fliplinklevelinc = 1; - max_btreenode_size = 100; // Default use b-tree sorting. reflevel = 3; - optlevel = 7; // 1 & 2 & 4, // min_max_dihedral. - optpasses = 3; - optmaxfliplevel = 2; + optscheme = 7; // 1 & 2 & 4, // min_max_dihedral. + optlevel = 2; delmaxfliplevel = 1; - optmaxflipstarsize = 10; 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; + 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'; @@ -757,45 +773,42 @@ public: /////////////////////////////////////////////////////////////////////////////// // // -// Geometric predicates // -// // -// Return one of the values +1, 0, and -1 on basic geometric questions such // -// as the orientation of point sets, in-circle, and in-sphere tests. They // -// are basic units for implmenting geometric algorithms. TetGen uses two 3D // -// geometric predicates: the orientation and in-sphere tests. // -// // -// Orientation test: let a, b, c be a sequence of 3 non-collinear points in // -// R^3. They defines a unique hypeplane H. Let H+ and H- be the two spaces // -// separated by H, which are defined as follows (using the left-hand rule): // -// make a fist using your left hand in such a way that your fingers follow // -// the order of a, b and c, then your thumb is pointing to H+. Given any // -// point d in R^3, the orientation test returns +1 if d lies in H+, -1 if d // -// lies in H-, or 0 if d lies on H. // -// // -// In-sphere test: let a, b, c, d be 4 non-coplanar points in R^3. They // -// defines a unique circumsphere S. Given any point e in R^3, the in-sphere // -// test returns +1 if e lies inside S, or -1 if e lies outside S, or 0 if e // -// lies on S. // -// // -// The following routines use arbitrary precision floating-point arithmetic. // -// They are provided by J. R. Schewchuk in public domain (http://www.cs.cmu. // -// edu/~quake/robust.html). The source code are in "predicates.cxx". // +// 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. // // // /////////////////////////////////////////////////////////////////////////////// -REAL exactinit(); +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); -REAL orient4dexact(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe, - REAL ah, REAL bh, REAL ch, REAL dh, REAL eh); /////////////////////////////////////////////////////////////////////////////// // // // tetgenmesh // // // -// The object to generate tetrahedral meshes. // +// A structure for creating and updating tetrahedral meshes. // // // /////////////////////////////////////////////////////////////////////////////// @@ -803,64 +816,45 @@ class tetgenmesh { public: - // Labels that signify the type of a vertex. - enum verttype {UNUSEDVERTEX, DUPLICATEDVERTEX, RIDGEVERTEX, ACUTEVERTEX, - FACETVERTEX, VOLVERTEX, FREESEGVERTEX, FREEFACETVERTEX, - FREEVOLVERTEX, HIDDENVERTEX, DEADVERTEX}; - - // Labels that signify the type of a subsegment. - enum shestype {NSHARP, SHARP, FAKESH}; - - // 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 {OUTSIDE, INTETRAHEDRON, ONFACE, ONEDGE, ONVERTEX, INSTAR, - ENCVERTEX, ENCSEGMENT, ENCSUBFACE, NEARVERTEX,BADELEMENT}; - /////////////////////////////////////////////////////////////////////////////// // // // Mesh data structure // // // -// A tetrahedral mesh of a 3D domain is a 3D simplicial complex T whose und- // -// erlying space is homeomorphic to the domain. T contains a 2D subcomplex S // -// which is a triangular mesh of the boundary of the domain. S contains a 1D // -// subcomplex L which is a linear mesh of the boundary of the surface. Faces // -// and edges in S and L are respectivly called subfaces and segments to dis- // -// tinguish them from others in T. // -// // -// The data structure to represent a tetrahedral mesh stores the tetrahedra // -// and vertices of T. Each tetrahedron is a structure including informations // -// of its vertices and adjacencies. Each vertex carries its geometric coord- // -// inates. The faces and edges of T are implicitly represented by tetrahedra.// -// This representation has a clear separation between combinatoric and geom- // -// etric data of a tetrahedral mesh. // -// // -// A hull face of T is the face on the exterior domain boundary, i.e., it is // -// contained by only one tetrahedron in T. TetGen adds fictitious tetrahedra // -// (one-to-one) at the hull faces of T, 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 tetrahedra containing hull faces. // -// The extended set of tetrahedra with the infinite vertex is a tetrahedral- // -// ization of a compact 3-manifold without bounday. It has the property that // -// every face is shared by exactly two tetrahedra. // -// // -// The data structure stores explicitly the subfaces and segments (which are // -// in surface mesh S and the linear mesh L, respectively. Additional inform- // -// ations are stored in tetrahedra and subfaces to remember their relations. // +// 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 list of four subfaces (optional, for -p switch); - // - a list of six segments (optional, for -p switch); + // - 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; + // - 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. @@ -873,8 +867,7 @@ public: // - two adjoining tetrahedra; // - an area constraint (optional, for -q switch); // - an integer for boundary marker; - // - an integer for type: SHARPSEGMENT, NONSHARPSEGMENT, ...; - // - an integer for pbc group (optional, if in->pbcgrouplist exists); + // - an integer for type, flags, etc. typedef REAL **shellface; @@ -884,11 +877,11 @@ public: // - 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, or a bsp_tree node; + // - 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. + // - 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. @@ -897,25 +890,23 @@ public: /////////////////////////////////////////////////////////////////////////////// // // -// Ordered tetrahedra // +// Handles // // // -// The four vertices of a tetrahedron can be permuted in 24 different seque- // -// nces. We call each sequence resulted by an even permutation an "ordered // -// tetrahedron". There are total 12 ordered tetrahedra. They form a group // -// which is isomorphic to the alternating group of 4 elements. Geometrically,// -// if we direct the three edges within a face of a tetrahedron by the count- // -// erclockwise order viewed from the opposite vertex of this face (using ei- // -// ther right-hand or left-hand rule). There are total twelve directed edges // -// in the tetrahedron. Each of them corresponds to an ordered tetrahedron. // +// 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. // // // -// We represent an order tetrahedron by a pair (t, v), where t is a pointer // -// to the tetrahedron and v is a four-bit integer, in the range from 0 to 11,// -// identifying the ordered version of the tetrahedron. Assume the faces of // -// the tetrahedron is numbered from 0 to 3, and the edges in a face is numb- // -// ered from 0 to 2. The first two bits of v is used to identify the face of // -// the tetrahedron. The other two bits of v identify the edge in the face. // +// 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 (accodring to // +// 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: // @@ -927,19 +918,10 @@ public: // face 2 | 2 (2/3) 6 (2/1) 10 (2/0) // // face 3 | 3 (3/0) 7 (3/1) 11 (3/2) // // // -// Ordered triangles // -// // -// The three vertices of a triangle can be permuted in 6 different sequences // -// which form a group isomorphic to the symmetric group of 3 elements. Each // -// permutation of the vertices is called an ordered triangle. The first two // -// vertices of an ordered triangle defines an directed edge. There are total // -// six directed edge in the triangle. They can be divided into two groups, // -// which correspond the two orientations of the triangle, respectively. // -// // -// We represent an ordered triangle by a pair (s, v), where s is a pointer // -// to the triangle and v is a three-bit integer, in the range from 0 to 5, // -// identifying the directed edge of the triangle. Using the first bit of v // -// to identify the orientation, the other two bits of v identify the edge. // +// 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 // @@ -950,6 +932,9 @@ public: // 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 { @@ -974,12 +959,110 @@ public: } }; +/////////////////////////////////////////////////////////////////////////////// +// // +// 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 // // // -// A multiple usages structure. Despite of its name, a 'badface' can be used // -// to represent the following objects: // +// 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; // @@ -991,12 +1074,12 @@ public: class badface { public: triface tt; - face ss; + face ss; REAL key, cent[6]; // circumcenter or cos(dihedral angles) at 6 edges. point forg, fdest, fapex, foppo, noppo; - badface *previtem, *nextitem; + badface *nextitem; badface() : key(0), forg(0), fdest(0), fapex(0), foppo(0), noppo(0), - previtem(0), nextitem(0) {} + nextitem(0) {} }; /////////////////////////////////////////////////////////////////////////////// @@ -1011,28 +1094,33 @@ public: public: - int iloc, bowywat, lawson; - int rejflag, chkencflag; - int sloc, sbowywat; + 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() { - // All flags are initialized as 0. iloc = bowywat = lawson = 0; - rejflag = chkencflag = 0; - sloc = sbowywat = 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; } }; @@ -1049,11 +1137,9 @@ public: public: - 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. - //point remedge[2]; // A non-Delaunay edge to be removed. + // Elementary flip flags. + int enqflag; // (= flipflag) + int chkencflag; // Control flags int unflip; // Undo the performed flips. @@ -1062,37 +1148,22 @@ public: // Optimization flags. int remove_ndelaunay_edge; // Remove a non-Delaunay edge. - // remedge[0] and remedge[1] store the endpoints of a - // non-Delaunay edge to be removed. 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). - // Only perform a flip if new angles are less than it. REAL cosdihed_out; // The improved cosine of the dihedral angle. - // Counters. - int maxflippedlinklevelcount; // Maximal flipped link levels. - int misfliplinklevelcount; // Number of missed flip possibilities. - int chrismastreecount; // Number of Chrismas trees (unflippable case). - int convexhulledgecount; // Number of convex hull edges (unflippable case). - int encsegcount; // Number of hitted segments. - int rejf23count, rejf32count; // Number of rejections by checkflipeligi.. - - void clearcounters() { - maxflippedlinklevelcount = 0; - misfliplinklevelcount = 0; - chrismastreecount = 0; - convexhulledgecount = 0; - encsegcount = 0; - rejf23count = rejf32count = 0; - } + // 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() { - seg[0] = NULL; - fac[0] = NULL; - remvert = NULL; - //remedge[0] = NULL; + enqflag = 0; + chkencflag = 0; unflip = 0; collectnewtets = 0; @@ -1100,12 +1171,15 @@ public: 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; - clearcounters(); + checkflipeligibility = 0; + seg[0] = NULL; + fac[0] = NULL; + remvert = NULL; } }; @@ -1124,7 +1198,7 @@ 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 maxumum dihedral angle. + int min_max_dihedangle; // Minimize the maximum dihedral angle. // The initial and improved value. REAL initval, imprval; @@ -1134,11 +1208,6 @@ public: int maxiter; // Maximum smoothing iterations (disabled by -1). int smthiter; // Performed iterations. - int expstarflag; - int expstarcount; - - int flipflag; - int checkencflag; optparameters() { max_min_volume = 0; @@ -1152,138 +1221,39 @@ public: maxiter = -1; // Unlimited smoothing iterations. smthiter = 0; - expstarflag = 0; - expstarcount = 0; - - flipflag = 0; - checkencflag = 0; } }; /////////////////////////////////////////////////////////////////////////////// // // -// Arraypool // -// // -// A dynamic linear array. // -// (It is simply copied from Shewchuk's Starbase.c, which is provided as // -// part of Stellar, a program for improving tetrahedral meshes.) // -// // -// 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 // -// addesses 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 totoal memorypool in bytes. // -// // -/////////////////////////////////////////////////////////////////////////////// - - class arraypool { - - public: - - int objectbytes; - int objectsperblock; - int log2objectsperblock; - 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)->objectsperblock - 1)) * (pool)->objectbytes) - -/////////////////////////////////////////////////////////////////////////////// -// // -// Memorypool // -// // -// A type used to allocate memory. // -// (It is simply copied from Shewchuk's triangle.c.) // -// // -// 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. // -// // -// itemwordtype is set to POINTER or FLOATINGPOINT, and is used to suggest // -// what sort of word the record is primarily made up of. alignbytes // -// determines how new records should be aligned in memory. itembytes and // -// itemwords are the length of a record in bytes (after rounding up) and // -// words. itemsperblock is the number of items allocated at once in a // -// single block. items is the number of currently allocated items. // -// maxitems is the maximum number of items that have been allocated at // -// once; it is the current number of items plus the number of records kept // -// on deaditemstack. // +// Labels (enumeration declarations) used by TetGen. // // // /////////////////////////////////////////////////////////////////////////////// - class memorypool { - - public: - - // Labels that signify whether a record consists primarily of pointers - // or of floating-point words. Used for data alignment. - enum wordtype {POINTER, FLOATINGPOINT}; - - void **firstblock, **nowblock; - void *nextitem; - void *deaditemstack; - void **pathblock; - void *pathitem; - wordtype itemwordtype; - int alignbytes; - int itembytes, itemwords; - int itemsperblock; - long items, maxitems; - int unallocateditems; - int pathitemsleft; - - memorypool(); - memorypool(int, int, enum wordtype, int); - ~memorypool(); - - void poolinit(int, int, enum wordtype, int); - void restart(); - void *alloc(); - void dealloc(void*); - void traversalinit(); - void *traverse(); - }; + // 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}; /////////////////////////////////////////////////////////////////////////////// // // -// Class variables // +// Variables of TetGen // // // /////////////////////////////////////////////////////////////////////////////// // Pointer to the input data (a set of nodes, a PLC, or a mesh). - tetgenio *in; + tetgenio *in, *addin; // Pointer to the switches and parameters. tetgenbehavior *b; @@ -1291,9 +1261,8 @@ public: // Pointer to a background mesh (contains size specification map). tetgenmesh *bgm; - // Memorypools to store mesh elements: tetrahedra, subfaces, segments, - // and vertices. And memorypools for storing pointers which connect - // tetrahedra and subfaces and segments. + // 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; @@ -1302,17 +1271,8 @@ public: // A memorypool to store faces to be flipped. memorypool *flippool; - // A stack of faces to be flipped. - badface *flipstack; - // Two queues for handling unflippable edges. - arraypool *unflipqueue; //, *flipqueue; - - // Entry to find the binary tree nodes (-u option). - arraypool *btreenode_list; - // The maximum size of a btree node (number after -u option) is - int max_btreenode_size; // <= b->max_btreenode_size. - // The maximum btree depth (for bookkeeping). - int max_btree_depth; + arraypool *unflipqueue; + badface *flipstack; // Arrays used for point insertion (the Bowyer-Watson algorithm). arraypool *cavetetlist, *cavebdrylist, *caveoldtetlist; @@ -1322,48 +1282,38 @@ public: // Stacks used for CDT construction and boundary recovery. arraypool *subsegstack, *subfacstack, *subvertstack; - arraypool *suppsteinerptlist; - - // The infinite vertex. - point dummypoint; - // Two handles used for facet recovery in CDT. - triface firsttopface, firstbotface; + // Arrays of encroached segments and subfaces (for mesh refinement). + arraypool *encseglist, *encshlist; - // Three points define a plane (used in formcavity()). - point plane_pa, plane_pb, plane_pc; + // The map between facets to their vertices (for mesh refinement). + int *idx2facetlist; + point *facetverticeslist; - // Two arraies of encroached segments and subfaces (in mesh refinement). - arraypool *encseglist, *encshlist; + // The map between segments to their endpoints (for mesh refinement). + point *segmentendpointslist; - // Pointer to a recently visited tetrahedron, subface. + // 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; - // The increasement of link levels, default is 1. - int autofliplinklevel; + // Array (size = numberoftetrahedra * 6) for storing high-order nodes of + // tetrahedra (only used when -o2 switch is selected). + point *highordertable; - // The volume of tetrahedral-prisms (in 4D). - REAL tetprism_vol_sum; - int calc_tetprism_vol; - - // Other variables. - REAL xmax, xmin, ymax, ymin, zmax, zmin; // Bounding box of points. - REAL longest; // The longest possible edge length. - long hullsize; // Number of faces of convex hull. - long insegments; // Number of input segments. - long meshedges; // Number of output mesh edges. - long meshhulledges; // Number of hull mesh edges. - int steinerleft; // Number of Steiner points not yet used. + // 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 point2pbcptindex; // Index to find a pbc point to 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. @@ -1371,83 +1321,64 @@ public: 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 checkinverttetflag; // Are there inverted (degenerated) tets yet? - int checkpbcs; // Are there periodic boundary conditions? int checkconstraints; // Are there variant (node, seg, facet) constraints? int nonconvex; // Is current mesh non-convex? - int dupverts; // Are there duplicated vertices? - int unuverts; // Are there unused vertices? + 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; - REAL cosslidihed; // The cosine value of max dihedral of a sliver. + 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 sintheta_tol; // The tolerance for sin(small angle). + 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. - // Algorithm statistical counters. - long ptloc_count, ptloc_max_count; - long orient3dcount, inspherecount, insphere_sos_count; + // 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, flip22count; - long maxbowatcavsize, totalbowatcavsize, totaldeadtets; - long triedgcount, triedgcopcount; - long across_face_count, across_edge_count, across_max_count; - long fillregioncount, missingsubfacecount, crossingtetcount; - long cavitycount, cavityexpcount, maxcavsize, maxregionsize; - long maxcrossfacecount, maxflipsequence; - long dbg_ignore_facecount, dbg_unflip_facecount; - long ccent_relocate_count; - long opt_sliver_peels; - long r1count, r2count, r3count; - long maxfliplinklevel, maxflipstarsize; - long flipstarcount, sucflipstarcount, skpflipstarcount; - long st_segref_count, st_facref_count, st_volref_count; - - long rejrefinetetcount, rejrefineshcount; - -#ifdef WITH_RUNTIME_COUNTERS - clock_t t_ptloc, t_ptinsert; // Time counters for DT operations. -#endif + long flip23count, flip32count, flip44count, flip41count; + long flip31count, flip22count; + unsigned long totalworkmemory; // Total memory used by working arrays. + /////////////////////////////////////////////////////////////////////////////// // // // Mesh manipulation primitives // // // -// A serial of mesh operations such as topological maintenance, navigation, // -// local modification, etc., is accomplished through a set of mesh manipul- // -// ation primitives. These primitives are indeed very simple functions which // -// take one or two handles ('triface's and 'face's) as parameters, perform // -// basic operations such as "glue two tetrahedra at a face", "return the // -// origin of a tetrahedron", "return the subface adjoining at the face of a // -// tetrahedron", and so on. // -// // /////////////////////////////////////////////////////////////////////////////// // Fast lookup tables for mesh manipulation primitives. - static int mod12[36]; - static int mod6[18]; - static int edgepivot[12]; - static int orgpivot [12]; - static int destpivot[12]; - static int apexpivot[12]; - static int oppopivot[12]; - static int ver2edge[12]; - static int edge2ver[6]; + 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]; - static int sorgpivot [6]; - static int sdestpivot[6]; - static int sapexpivot[6]; - static int epivot[4]; + + void inittables(); // Primitives for tetrahedra. - inline void decode(tetrahedron ptr, triface& t); 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 fsym(triface& t1, triface& t2); - inline void fsymself(triface& t); inline void esym(triface& t1, triface& t2); inline void esymself(triface& t); inline void enext(triface& t1, triface& t2); @@ -1458,10 +1389,14 @@ public: 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 void fprev(triface& t1, triface& t2); - inline void fprevself(triface& t); inline point org (triface& t); inline point dest(triface& t); inline point apex(triface& t); @@ -1521,16 +1456,10 @@ public: inline void senextself(face& s); inline void senext2(face& s1, face& s2); inline void senext2self(face& s); - inline void sfnext(face& s1, face& s2); - inline void sfnextself(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 enum shestype shelltype(face& s); - inline void setshelltype(face& s, enum shestype value); - inline int shellpbcgroup(face& s); - inline void setshellpbcgroup(face& s, int value); inline void sinfect(face& s); inline void suninfect(face& s); inline bool sinfected(face& s); @@ -1543,6 +1472,8 @@ public: 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); @@ -1594,8 +1525,8 @@ public: inline void setpoint2ppt(point pt, point value); inline tetrahedron point2bgmtet(point pt); inline void setpoint2bgmtet(point pt, tetrahedron value); - inline point point2pbcpt(point pt); - inline void setpoint2pbcpt(point pt, point value); + inline void setpointinsradius(point pt, REAL value); + inline REAL getpointinsradius(point pt); // Advanced primitives. inline void point2tetorg(point pt, triface& t); @@ -1614,32 +1545,53 @@ public: tetrahedron *alltetrahedrontraverse(); void shellfacedealloc(memorypool*, shellface*); shellface *shellfacetraverse(memorypool*); - void badfacedealloc(memorypool*, badface*); - badface *badfacetraverse(memorypool*); void pointdealloc(point); point pointtraverse(); - void maketetrahedron(triface*); - void makeshellface(memorypool*, face*); - void makepoint(point*, enum verttype); void makeindex2pointmap(point*&); void makepoint2submap(memorypool*, int*&, face*&); + void maketetrahedron(triface*); + void makeshellface(memorypool*, face*); + void makepoint(point*, enum verttype); void initializepools(); /////////////////////////////////////////////////////////////////////////////// // // -// Geometric predicates and calculations // +// 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. // // // /////////////////////////////////////////////////////////////////////////////// - // Triangle-edge intersection test + // 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 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 + // 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); @@ -1649,13 +1601,12 @@ public: 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); - // Geometric predicates + // An embedded 2-dimensional geometric predicate (non-robust) REAL incircle3d(point pa, point pb, point pc, point pd); - REAL insphere_s(REAL*, REAL*, REAL*, REAL*, REAL*); - REAL orient4d_s(REAL*, REAL*, REAL*, REAL*, REAL*, - REAL, REAL, REAL, REAL, REAL); - // Geometric calculations + // 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); @@ -1668,51 +1619,134 @@ public: 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. // +// // /////////////////////////////////////////////////////////////////////////////// - void flippush(badface*&, triface*); - // The elementary flips. - void flip23(triface*, int, int, int); - void flip32(triface*, int, int, int); - void flip41(triface*, int, int, int); + 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); - // Incremental flips. - long lawsonflip3d(point, int flipflag, int, int, int flipedgeflag); - // Point insertion. - int insertvertex(point newpt, triface *searchtet, face *splitsh, face*, - insertvertexflags *ivf); + 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. - void btree_sort(point*, int, int, REAL, REAL, REAL, REAL, REAL, REAL, int); - void btree_insert(point insertpt); - void btree_search(point searchpt, triface* searchtet); - void ordervertices(point* vertexarray, int arraysize); + 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*, int, int); + 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); @@ -1724,14 +1758,11 @@ public: // // /////////////////////////////////////////////////////////////////////////////// - bool calculateabovepoint(arraypool*, point*, point*, point*); - void calculateabovepoint4(point, point, point, point); - 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 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); @@ -1746,53 +1777,116 @@ public: void meshsurface(); void interecursive(shellface** subfacearray, int arraysize, int axis, - REAL bxmin, REAL bxmax, REAL bymin, REAL bymax, - REAL bzmin, REAL bzmax, int* internum); + 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 markacutevertices(); + void makesegmentendpointsmap(); - void reportselfintersect(face *seg, face *shface); - - enum interresult finddirection(triface* searchtet, point endpt, int); + enum interresult finddirection(triface* searchtet, point endpt); enum interresult scoutsegment(point, point, triface*, point*, arraypool*); - void getsteinerptonsegment(face* seg, point refpt, point steinpt); + int getsteinerptonsegment(face* seg, point refpt, point steinpt); void delaunizesegments(); enum interresult scoutsubface(face* searchsh, triface* searchtet); - void formmissingregion(face* missh, arraypool* missingshs, - arraypool* missingshbds, arraypool* missingshverts, - arraypool *adjtets); - int scoutcrossedge(triface& crosstet, arraypool*, arraypool* missingshs); - bool formcavity(triface* searchtet, arraypool* missingshs, - arraypool* crosstets, arraypool* topfaces, - arraypool* botfaces, arraypool* toppoints, - arraypool* botpoints); - - // Facet recovery by local re-tetrahedralization [Si and Gaertner'05,'11]. - void delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, - arraypool *cavshells, arraypool *newtets, - arraypool *crosstets, arraypool *misfaces); - bool fillcavity(arraypool* topshells, arraypool* botshells, - arraypool* midfaces, arraypool* missingshs); - void carvecavity(arraypool *crosstets, arraypool *topnewtets, - arraypool *botnewtets); - void restorecavity(arraypool *crosstets, arraypool *topnewtets, - arraypool *botnewtets); - - // Facet recovery by flips [Shewchuk'03]. - void flipcertify(triface *chkface, badface **pqueue); - void flipinsertfacet(arraypool *crosstets, arraypool *toppoints, - arraypool *botpoints, arraypool *midpoints); + 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); - void refineregion(); + + 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(); @@ -1804,7 +1898,6 @@ public: // // /////////////////////////////////////////////////////////////////////////////// - // A call back function. int checkflipeligibility(int fliptype, point, point, point, point, point, int level, int edgepivot, flipconstraints* fc); @@ -1813,6 +1906,7 @@ public: 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); @@ -1824,7 +1918,7 @@ public: int reduceedgesatvertex(point startpt, arraypool* endptlist); int removevertexbyflips(point steinerpt); - int suppressssteinerpoint(point steinerpt); + int suppressbdrysteinerpoint(point steinerpt); int suppresssteinerpoints(); void recoverboundary(clock_t&); @@ -1840,33 +1934,70 @@ public: void reconstructmesh(); int scoutpoint(point, triface*, int randflag); - REAL getpointmeshsize(point, triface*, int iloc, int posflag); + 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 marksharpsegments(); - void decidefeaturepointsizes(); + 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, int qflag, int chkencflag); + 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 encpt, int qflag, REAL *ccent, - int chkencflag); + 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 chkencflag); + int splittetrahedron(triface* splittet,int qflag,REAL *ccent, int); void repairbadtets(int chkencflag); void delaunayrefinement(); @@ -1877,6 +2008,7 @@ public: // // /////////////////////////////////////////////////////////////////////////////// + long lawsonflip3d(flipconstraints *fc); void recoverdelaunay(); int gettetrahedron(point, point, point, point, triface *); @@ -1888,7 +2020,7 @@ public: int splitsliver(triface *, REAL, int); long removeslivers(int); - void optimizemesh(int optflag); + void optimizemesh(); /////////////////////////////////////////////////////////////////////////////// // // @@ -1905,7 +2037,9 @@ public: int checkconforming(int); // Mesh statistics. + void printfcomma(unsigned long n); void qualitystatistics(); + void memorystatistics(); void statistics(); /////////////////////////////////////////////////////////////////////////////// @@ -1932,6 +2066,7 @@ public: void outmesh2vtk(char*); + /////////////////////////////////////////////////////////////////////////////// // // // Constructor & destructor // @@ -1940,8 +2075,8 @@ public: tetgenmesh() { + in = addin = NULL; b = NULL; - in = NULL; bgm = NULL; tetrahedrons = subfaces = subsegs = points = NULL; @@ -1952,7 +2087,6 @@ public: dummypoint = NULL; flipstack = NULL; unflipqueue = NULL; - btreenode_list = NULL; cavetetlist = cavebdrylist = caveoldtetlist = NULL; cavetetshlist = cavetetseglist = cavetetvertlist = NULL; @@ -1960,103 +2094,82 @@ public: caveshlist = caveshbdlist = cavesegshlist = NULL; subsegstack = subfacstack = subvertstack = NULL; - suppsteinerptlist = NULL; encseglist = encshlist = NULL; + idx2facetlist = NULL; + facetverticeslist = NULL; + segmentendpointslist = NULL; - plane_pa = plane_pb = plane_pc = (point) NULL; + highordertable = NULL; - xmax = xmin = ymax = ymin = zmax = zmin = 0.0; - longest = 0.0; - hullsize = 0l; - insegments = 0l; - meshedges = meshhulledges = 0l; - steinerleft = -1; + numpointattrib = numelemattrib = 0; + sizeoftensor = 0; pointmtrindex = 0; pointparamindex = 0; pointmarkindex = 0; point2simindex = 0; - point2pbcptindex = 0; elemattribindex = 0; volumeboundindex = 0; shmarkindex = 0; areaboundindex = 0; checksubsegflag = 0; checksubfaceflag = 0; - checkinverttetflag = 0; - checkpbcs = 0; checkconstraints = 0; nonconvex = 0; - dupverts = 0; - unuverts = 0; + autofliplinklevel = 1; + useinsertradius = 0; samples = 0l; randomseed = 1l; minfaceang = minfacetdihed = PI; - sintheta_tol = sin(0.001 * PI / 180.0); - - autofliplinklevel = 1; - tetprism_vol_sum = 0.0; - calc_tetprism_vol = 0; + longest = 0.0; + xmax = xmin = ymax = ymin = zmax = zmin = 0.0; - ptloc_count = ptloc_max_count = 0l; - orient3dcount = 0l; - inspherecount = insphere_sos_count = 0l; - flip14count = flip26count = flipn2ncount = 0l; - flip23count = flip32count = flip44count = flip22count = 0l; - maxbowatcavsize = totalbowatcavsize = totaldeadtets = 0l; - triedgcount = triedgcopcount = 0l; - across_face_count = across_edge_count = across_max_count = 0l; - fillregioncount = missingsubfacecount = crossingtetcount = 0l; - cavitycount = cavityexpcount = 0l; - maxcavsize = maxregionsize = 0l; - maxcrossfacecount = maxflipsequence = 0l; - dbg_ignore_facecount = dbg_unflip_facecount = 0l; - ccent_relocate_count = 0l; - opt_sliver_peels = 0l; - r1count = r2count = r3count = 0l; + 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; - maxfliplinklevel = maxflipstarsize = 0l; - flipstarcount = sucflipstarcount = skpflipstarcount = 0l; - - rejrefinetetcount = rejrefineshcount = 0l; -#ifdef WITH_RUNTIME_COUNTERS - t_ptloc = t_ptinsert = (clock_t) 0; -#endif } // tetgenmesh() - ~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; - } - if (subsegs != (memorypool *) NULL) { delete subsegs; } - if (points != (memorypool *) NULL) { - delete points; - } + if (tet2segpool != NULL) { delete tet2segpool; - } - if (tet2subpool != NULL) { delete tet2subpool; } + if (flippool != NULL) { delete flippool; delete unflipqueue; } - if (dummypoint != (point) NULL) { - delete [] dummypoint; - } if (cavetetlist != NULL) { delete cavetetlist; @@ -2081,9 +2194,23 @@ public: delete subvertstack; } - if (suppsteinerptlist != NULL) { - delete suppsteinerptlist; + if (idx2facetlist != NULL) { + delete [] idx2facetlist; + delete [] facetverticeslist; + } + + if (segmentendpointslist != NULL) { + delete [] segmentendpointslist; + } + + if (highordertable != NULL) { + delete [] highordertable; } + } + + ~tetgenmesh() + { + freememory(); } // ~tetgenmesh() }; // End of class tetgenmesh. @@ -2099,7 +2226,7 @@ public: // 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 distruction function. // +// defines a mesh size function. // // // /////////////////////////////////////////////////////////////////////////////// @@ -2117,8 +2244,12 @@ void tetrahedralize(char *switches, tetgenio *in, tetgenio *out, // // /////////////////////////////////////////////////////////////////////////////// -inline void terminatetetgen(int x) +inline void terminatetetgen(tetgenmesh *m, int x) { + // Release the allocated memory. + if (m) { + m->freememory(); + } #ifdef TETLIBRARY throw x; #else @@ -2136,15 +2267,15 @@ inline void terminatetetgen(int x) printf("Hint: use -d option to detect all self-intersections.\n"); break; case 4: - printf("A very small input feature was size detected. Program stopped.\n"); + 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 clsoe input facets were detected. Program stopped.\n"); + printf("Two very close input facets were detected. Program stopped.\n"); printf("Hint: use -Y option to avoid adding Steiner points in boundary.\n"); break; case 10: - printf("An input error was detected Program stopped.\n"); + printf("An input error was detected. Program stopped.\n"); break; } // switch (x) exit(x); @@ -2153,24 +2284,12 @@ inline void terminatetetgen(int x) /////////////////////////////////////////////////////////////////////////////// // // -// Inline functions of mesh data structures // +// Primitives for tetrahedra // // // /////////////////////////////////////////////////////////////////////////////// -// -// Begin of primitives for tetrahedra -// - -// decode() converts a pointer to an ordered tetrahedron. 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); -} - -// encode() compress an ordered tetrahedron into a single pointer. It -// relies on the assumption that all tetrahedra are aligned to sixteen- +// 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) { @@ -2181,135 +2300,139 @@ inline tetgenmesh::tetrahedron tetgenmesh::encode2(tetrahedron* ptr, int ver) { return (tetrahedron) ((uintptr_t) (ptr) | (uintptr_t) (ver)); } -// bond() connects two adjacent tetrahedra together. t1 and t2 must refer -// to the same face and the same edge. Note that the edge directions of -// t1 and t2 are reversed. -// Since an edge of t1 can be bonded to any of the three edges of t2. We -// choose to bond the edge of t2 which is symmetric to the 0-th edge of -// t1, and vice versa. Now assume t1 is at i-th edge and t2 is at j-th -// edge, where i, j in {0, 1, 2}. The edge in t2 symmetric to 0-th edge -// of t1 is mod3[i + j]. -// Since the edge number is coded in the two higher bits of the version, -// both i, j are in {0, 4, 8}. The edge in t2 symmetric to 0-th edge -// of t1 is mod12[i + j]. +// decode() converts a pointer to a handle. The version is extracted from +// the four least significant bits of the pointer. -inline void tetgenmesh::bond(triface& t1, triface& t2) { - (t1).tet[(t1).ver & 3] = encode2((t2).tet, - ((t2).ver & 3) + mod12[((t1).ver & 12) + ((t2).ver & 12)]); - (t2).tet[(t2).ver & 3] = encode2((t1).tet, - ((t1).ver & 3) + mod12[((t1).ver & 12) + ((t2).ver & 12)]); +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); } -// dissolve() a bond (from one side). +// bond() connects two tetrahedra together. (t1,v1) and (t2,v2) must +// refer to the same face and the same edge. -inline void tetgenmesh::dissolve(triface& t) { - t.tet[t.ver & 3] = NULL; +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]); } -// fsym() finds the adjacent tetrahedron at the same face and the same edge. -inline void tetgenmesh::fsym(triface& t1, triface& t2) { - tetrahedron ptr = (t1).tet[(t1).ver & 3]; - int offset = 12 - ((t1).ver & 12); - decode(ptr, t2); - (t2).ver = mod12[(t2).ver + offset]; -} +// dissolve() a bond (from one side). -inline void tetgenmesh::fsymself(triface& t) { - tetrahedron ptr = (t).tet[(t).ver & 3]; - int offset = 12 - ((t).ver & 12); - decode(ptr, t); - (t).ver = mod12[(t).ver + offset]; +inline void tetgenmesh::dissolve(triface& t) { + t.tet[t.ver & 3] = NULL; } -// enext() finds the next edge (counterclockwise) on the same face. +// enext() finds the next edge (counterclockwise) in the same face. inline void tetgenmesh::enext(triface& t1, triface& t2) { - (t2).tet = (t1).tet; - (t2).ver = mod12[(t1).ver + 4]; + t2.tet = t1.tet; + t2.ver = enexttbl[t1.ver]; } inline void tetgenmesh::enextself(triface& t) { - (t).ver = mod12[(t).ver + 4]; + t.ver = enexttbl[t.ver]; } -// eprev() finds the next edge (clockwise) on the same face. +// eprev() finds the next edge (clockwise) in the same face. inline void tetgenmesh::eprev(triface& t1, triface& t2) { - (t2).tet = (t1).tet; - (t2).ver = mod12[(t1).ver + 8]; + t2.tet = t1.tet; + t2.ver = eprevtbl[t1.ver]; } inline void tetgenmesh::eprevself(triface& t) { - (t).ver = mod12[(t).ver + 8]; + t.ver = eprevtbl[t.ver]; } -// esym() finds the reversed edge. It is on the other face of the +// 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 = edgepivot[(t1).ver]; + (t2).ver = esymtbl[(t1).ver]; } inline void tetgenmesh::esymself(triface& t) { - (t).ver = edgepivot[(t).ver]; + (t).ver = esymtbl[(t).ver]; } -// enextesym() finds the reversed edge of the next edge. It is on the other -// face of the same tetrahedron. +// 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) { - enext(t1, t2); - esymself(t2); + t2.tet = t1.tet; + t2.ver = enextesymtbl[t1.ver]; } inline void tetgenmesh::enextesymself(triface& t) { - enextself(t); - esymself(t); + t.ver = enextesymtbl[t.ver]; } -// eprevesym() finds the reversed edge of the previous edge. It is on the -// other face of the same tetrahedron. +// eprevesym() finds the reversed edge of the previous edge. inline void tetgenmesh::eprevesym(triface& t1, triface& t2) { - eprev(t1, t2); - esymself(t2); + t2.tet = t1.tet; + t2.ver = eprevesymtbl[t1.ver]; } inline void tetgenmesh::eprevesymself(triface& t) { - eprevself(t); - esymself(t); + t.ver = eprevesymtbl[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 -// equivalent to the combination: fsym() * esym(). +// eorgoppo() Finds the opposite face of the origin of the current edge. +// Return the opposite edge of the current edge. -inline void tetgenmesh::fnext(triface& t1, triface& t2) { - esym(t1, t2); - fsymself(t2); +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]; } -inline void tetgenmesh::fnextself(triface& t) { - esymself(t); - fsymself(t); +// 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]; } -// fprev() finds the next face while rotating about an edge according to -// a left-hand rule. The face is in the adjacent tetrahedron. It is -// equivalent to the combination: esym() * fsym(). +// fsym() finds the adjacent tetrahedron at the same face and the same edge. -inline void tetgenmesh::fprev(triface& t1, triface& t2) { - fsym(t1, t2); - esymself(t2); +inline void tetgenmesh::fsym(triface& t1, triface& t2) { + decode((t1).tet[(t1).ver & 3], t2); + t2.ver = fsymtbl[t1.ver][t2.ver]; } -inline void tetgenmesh::fprevself(triface& t) { - fsymself(t); - esymself(t); + +#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. @@ -2408,17 +2531,12 @@ inline void tetgenmesh::uninfect(triface& t) { ((int *) (t.tet))[elemmarkerindex] &= ~1; } -// Test a tetrahedron for viral infection. - inline bool tetgenmesh::infected(triface& t) { return (((int *) (t.tet))[elemmarkerindex] & 1) != 0; } // marktest(), marktested(), unmarktest() -- primitives to flag or unflag a -// tetrahedron. The last second bit of the element marker is marked (1) -// or unmarked (0). -// One needs them in forming Bowyer-Watson cavity, to mark a tetrahedron if -// it has been checked (for Delaunay case) so later check can be avoided. +// tetrahedron. Use the second lowerest bit of the element marker. inline void tetgenmesh::marktest(triface& t) { ((int *) (t.tet))[elemmarkerindex] |= 2; @@ -2435,8 +2553,6 @@ inline bool tetgenmesh::marktested(triface& t) { // 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. -// One use of the face marker is in flip algorithm. Each queued face (check -// for locally Delaunay) is marked. inline void tetgenmesh::markface(triface& t) { ((int *) (t.tet))[elemmarkerindex] |= (4 << (t.ver & 3)); @@ -2453,7 +2569,7 @@ inline bool tetgenmesh::facemarked(triface& t) { // markedge(), unmarkedge(), edgemarked() -- primitives to flag or unflag an // edge of a tetrahedron. From the last 7th to 12th bits are used for // edge markers, e.g., the last 7th bit corresponds to the 0th edge, etc. -// Remark: The last 7th bit is marked by 2^6 = 64. +// 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]); @@ -2483,7 +2599,7 @@ inline bool tetgenmesh::marktest2ed(triface& t) { return (((int *) (t.tet))[elemmarkerindex] & (int) (4096)) != 0; } -// elemcounter(), setelemcounter() -- primitives to read or ser a (samll) +// 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]. @@ -2506,7 +2622,6 @@ inline void tetgenmesh::increaseelemcounter(triface& t) { inline void tetgenmesh::decreaseelemcounter(triface& t) { int c = elemcounter(t); - assert(c > 0); // Never get a negative counter. setelemcounter(t, c - 1); } @@ -2522,13 +2637,11 @@ inline bool tetgenmesh::isdeadtet(triface& t) { return ((t.tet == NULL) || (t.tet[4] == NULL)); } -// -// End of primitives for tetrahedra -// - -// -// Begin of primitives for subfaces/subsegments -// +/////////////////////////////////////////////////////////////////////////////// +// // +// Primitives for subfaces and subsegments // +// // +/////////////////////////////////////////////////////////////////////////////// // Each subface contains three pointers to its neighboring subfaces, with // edge versions. To save memory, both information are kept in a single @@ -2540,7 +2653,7 @@ inline bool tetgenmesh::isdeadtet(triface& t) { 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) 7); + s.sh = (shellface *) ((uintptr_t) (sptr) ^ (uintptr_t) (s.shver)); } inline tetgenmesh::shellface tetgenmesh::sencode(face& s) { @@ -2643,7 +2756,7 @@ inline void tetgenmesh::sesymself(face& s) s.shver ^= 1; } -// senext() finds the next edge (counterclockwise) in the same orientaion +// senext() finds the next edge (counterclockwise) in the same orientation // of this face. inline void tetgenmesh::senext(face& s1, face& s2) @@ -2668,73 +2781,6 @@ inline void tetgenmesh::senext2self(face& s) s.shver = snextpivot[snextpivot[s.shver]]; } -// sfnext() finds the next face (s2) in the same face ring of s1. -// s2 and s1 have the same edge orientation. -// s2 is found through the following determinations. -// If the edge of s1 is not a segment, then s2 = spivot(s1). -// Otherwise, suppose the segment's 0th version is [a,b]. -// To find the next face in the face ring we have two cases: -// (1) s1 is edge [a,b], then s2 = spivot(s1). -// (2) s1 is edge [b,a], then s1 = spivot(s2). -// In the case (2), we need to travese in the face ring of [a,b] to -// get s2. -// Comment: The correctness of this function is guaranteed by the -// surface mesh data structure, i.e., all subfaces at the face ring -// of [a,b] have the same edge orientation as [a,b]. - -inline void tetgenmesh::sfnext(face& s1, face& s2) -{ - face seg, s3; - - spivot(s1, s2); - - if (s2.sh != NULL) { - sspivot(s1, seg); - if (seg.sh != NULL) { - seg.shver = 0; - if (sorg(s1) != sorg(seg)) { - while (1) { - spivot(s2, s3); - if (s3.sh == s1.sh) break; - s2 = s3; - } - sesymself(s2); - } - } else { - if (sorg(s2) != sorg(s1)) { - sesymself(s2); - } - } - } -} - -inline void tetgenmesh::sfnextself(face& s) -{ - face seg, s2, s3; - - spivot(s, s2); - - if (s2.sh != NULL) { - sspivot(s, seg); - if (seg.sh != NULL) { - seg.shver = 0; - if (sorg(s) != sorg(seg)) { - while (1) { - spivot(s2, s3); - if (s3.sh == s.sh) break; - s2 = s3; - } - sesymself(s2); - } - } else { - if (sorg(s2) != sorg(s)) { - sesymself(s2); - } - } - } - - s = s2; -} // Check or set a subface's maximum area bound. @@ -2762,33 +2808,9 @@ inline void tetgenmesh::setshellmark(face& s, int value) } -// These two primitives set or read the type of the subface or subsegment. - -inline enum tetgenmesh::shestype tetgenmesh::shelltype(face& s) -{ - return (enum shestype) ((((int *) (s.sh))[shmarkindex + 1]) >> 8); -} - -inline void tetgenmesh::setshelltype(face& s, enum shestype value) -{ - ((int *) (s.sh))[shmarkindex + 1] = ((int) value << 8) + - ((((int *) ((s).sh))[shmarkindex + 1]) & 255); -} - -// These two primitives set or read the pbc group of the subface. - -inline int tetgenmesh::shellpbcgroup(face& s) -{ - return ((int *) (s.sh))[shmarkindex + 2]; -} - -inline void tetgenmesh::setshellpbcgroup(face& s, int value) -{ - ((int *) (s.sh))[shmarkindex + 2] = value; -} // sinfect(), sinfected(), suninfect() -- primitives to flag or unflag a -// subface. The last bit of ((int *) ((s).sh))[shmarkindex+1] is flaged. +// subface. The last bit of ((int *) ((s).sh))[shmarkindex+1] is flagged. inline void tetgenmesh::sinfect(face& s) { @@ -2810,8 +2832,7 @@ inline bool tetgenmesh::sinfected(face& s) } // smarktest(), smarktested(), sunmarktest() -- primitives to flag or unflag -// a subface. -// The last 2nd bit of ((int *) ((s).sh))[shmarkindex+1] is flaged. +// a subface. The last 2nd bit of the integer is flagged. inline void tetgenmesh::smarktest(face& s) { @@ -2831,8 +2852,7 @@ inline bool tetgenmesh::smarktested(face& s) } // smarktest2(), smarktest2ed(), sunmarktest2() -- primitives to flag or -// unflag a subface. -// The last 3rd bit of ((int *) ((s).sh))[shmarkindex+1] is flaged. +// unflag a subface. The last 3rd bit of the integer is flagged. inline void tetgenmesh::smarktest2(face& s) { @@ -2851,7 +2871,7 @@ 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 flaged. +// The last 4th bit of ((int *) ((s).sh))[shmarkindex+1] is flagged. inline void tetgenmesh::smarktest3(face& s) { @@ -2870,50 +2890,48 @@ inline bool tetgenmesh::smarktest3ed(face& s) return ((((int *) ((s).sh))[shmarkindex+1] & (int) 8) != 0); } -// -// End of primitives for subfaces/subsegments -// -// -// Begin of primitives for interacting between tetrahedra and subfaces -// +// 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) +inline void tetgenmesh::tsbond(triface& t, face& s) { - int soffset, toffset, ver; - if ((t).tet[9] == NULL) { // Allocate space for this tet. (t).tet[9] = (tetrahedron) tet2subpool->alloc(); - // NULL all fields in this space. + // Initialize. for (int i = 0; i < 4; i++) { ((shellface *) (t).tet[9])[i] = NULL; } } - - assert(org(t) == sorg(s)); // FOR DEBUG - - if (((s).shver & 1) == 0) { - // t and s have the same orientation. - soffset = mod6[6 - (((t).ver & 12) >> 1)]; // {0,2,4} - toffset = mod12[12 - (((s).shver & 6) << 1)]; // {0,4,8} - } else { - // t and s have revsered orientations. - soffset = (((t).ver & 12) >> 1); // {0,2,4} - toffset = (((s).shver & 6) << 1); // {0,4,8} - } - // Bond t <== s. - ver = ((s).shver & 1) + mod6[((s).shver & 6) + soffset]; - ((shellface *) (t).tet[9])[(t).ver & 3] = sencode2((s).sh, ver); + ((shellface *) (t).tet[9])[(t).ver & 3] = + sencode2((s).sh, tsbondtbl[t.ver][s.shver]); // Bond s <== t. - ver = ((t).ver & 3) + mod12[((t).ver & 12) + toffset]; - s.sh[9 + ((s).shver & 1)] = (shellface) encode2((t).tet, ver); + 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). @@ -2923,47 +2941,37 @@ inline void tetgenmesh::tsbond(triface& t, face& s) inline void tetgenmesh::tspivot(triface& t, face& s) { - int soffset; - if ((t).tet[9] == NULL) { (s).sh = NULL; return; } - // Get the attached subface s. sdecode(((shellface *) (t).tet[9])[(t).ver & 3], (s)); - - // Set the right edge in s. - if (((s).shver & 1) == 0) { - soffset = (((t).ver & 12) >> 1); // {0,2,4} - } else { - soffset = mod6[6 - (((t).ver & 12) >> 1)]; // {0,2,4} - } - (s).shver = ((s).shver & 1) + mod6[((s).shver & 6) + soffset]; + (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) { - int toffset; - decode((tetrahedron) s.sh[9 + (s.shver & 1)], t); - if ((t).tet == NULL) { return; } - - if (((s).shver & 1) == 0) { - toffset = (((s).shver & 6) << 1); // {0,4,8} - } else { - toffset = mod12[12 - (((s).shver & 6) << 1)]; // {0,4,8} - } - (t).ver = ((t).ver & 3) + mod12[((t).ver & 12) + toffset]; + (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) @@ -2981,13 +2989,11 @@ inline void tetgenmesh::stdissolve(face& s) (s).sh[10] = NULL; } -// -// End of primitives for interacting between tetrahedra and subfaces -// - -// -// Begin of primitives for interacting between subfaces and subsegs -// +/////////////////////////////////////////////////////////////////////////////// +// // +// Primitives for interacting between subfaces and segments // +// // +/////////////////////////////////////////////////////////////////////////////// // ssbond() bond a subface to a subsegment. @@ -3014,24 +3020,26 @@ inline void tetgenmesh::ssdissolve(face& s) inline void tetgenmesh::sspivot(face& s, face& edge) { - shellface sptr = (shellface) s.sh[6 + (s.shver >> 1)]; - sdecode(sptr, edge); + sdecode((shellface) s.sh[6 + (s.shver >> 1)], edge); } -// -// End of primitives for interacting between subfaces and subsegs -// +// Quickly check if the edge is a subsegment. + +#define isshsubseg(s) \ + ((s).sh[6 + ((s).shver >> 1)]) -// -// Begin of primitives for interacting between tet and subsegs. -// +/////////////////////////////////////////////////////////////////////////////// +// // +// 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(); - // NULL all fields in this space. + // Initialization. for (int i = 0; i < 6; i++) { ((shellface *) (t).tet[8])[i] = NULL; } @@ -3065,18 +3073,21 @@ inline void tetgenmesh::tsspivot1(triface& t, face& s) } } +// 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); } -// -// End of primitives for interacting between tet and subsegs. -// - -// -// Begin of primitives for points -// +/////////////////////////////////////////////////////////////////////////////// +// // +// Primitives for points // +// // +/////////////////////////////////////////////////////////////////////////////// inline int tetgenmesh::pointmark(point pt) { return ((int *) (pt))[pointmarkindex]; @@ -3119,7 +3130,7 @@ inline void tetgenmesh::setpointgeomuv(point pt, int i, REAL value) { } // pinfect(), puninfect(), pinfected() -- primitives to flag or unflag -// a point. The last bit of the integer '[pointindex+1]' is flaged. +// a point. The last bit of the integer '[pointindex+1]' is flagged. inline void tetgenmesh::pinfect(point pt) { ((int *) (pt))[pointmarkindex + 1] |= (int) 1; @@ -3133,8 +3144,8 @@ inline bool tetgenmesh::pinfected(point pt) { return (((int *) (pt))[pointmarkindex + 1] & (int) 1) != 0; } -// pmarktest(), punmarktest(), pmarktested() -- primitives to mark or unmark -// a point. +// pmarktest(), punmarktest(), pmarktested() -- more primitives to +// flag or unflag a point. inline void tetgenmesh::pmarktest(point pt) { ((int *) (pt))[pointmarkindex + 1] |= (int) 2; @@ -3148,8 +3159,6 @@ inline bool tetgenmesh::pmarktested(point pt) { return (((int *) (pt))[pointmarkindex + 1] & (int) 2) != 0; } -// pmarktest2(), ... - inline void tetgenmesh::pmarktest2(point pt) { ((int *) (pt))[pointmarkindex + 1] |= (int) 4; } @@ -3162,8 +3171,6 @@ inline bool tetgenmesh::pmarktest2ed(point pt) { return (((int *) (pt))[pointmarkindex + 1] & (int) 4) != 0; } -// pmarktest3(), ... - inline void tetgenmesh::pmarktest3(point pt) { ((int *) (pt))[pointmarkindex + 1] |= (int) 8; } @@ -3176,7 +3183,6 @@ 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. @@ -3213,14 +3219,16 @@ inline void tetgenmesh::setpoint2bgmtet(point pt, tetrahedron value) { ((tetrahedron *) (pt))[point2simindex + 3] = value; } -// These primitives set and read a pointer to its pbc point. -inline tetgenmesh::point tetgenmesh::point2pbcpt(point pt) { - return (point) ((tetrahedron *) (pt))[point2pbcptindex]; +// The primitives for saving and getting the insertion radius. +inline void tetgenmesh::setpointinsradius(point pt, REAL value) +{ + pt[pointmtrindex + sizeoftensor - 1] = value; } -inline void tetgenmesh::setpoint2pbcpt(point pt, point value) { - ((tetrahedron *) (pt))[point2pbcptindex] = (tetrahedron) value; +inline REAL tetgenmesh::getpointinsradius(point pt) +{ + return pt[pointmtrindex + sizeoftensor - 1]; } // point2tetorg() Get the tetrahedron whose origin is the point. @@ -3268,7 +3276,6 @@ inline tetgenmesh::point tetgenmesh::farsorg(face& s) spivotself(neighsh); if (neighsh.sh == NULL) break; if (sorg(neighsh) != sorg(travesh)) sesymself(neighsh); - assert(sorg(neighsh) == sorg(travesh)); // SELF_CHECK senext2(neighsh, travesh); } return sorg(travesh); @@ -3284,25 +3291,24 @@ inline tetgenmesh::point tetgenmesh::farsdest(face& s) spivotself(neighsh); if (neighsh.sh == NULL) break; if (sdest(neighsh) != sdest(travesh)) sesymself(neighsh); - assert(sdest(neighsh) == sdest(travesh)); // SELF_CHECK senext(neighsh, travesh); } return sdest(travesh); } -// -// End of primitives for points -// +/////////////////////////////////////////////////////////////////////////////// +// // +// 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]; @@ -3310,8 +3316,7 @@ inline void tetgenmesh::cross(REAL* v1, REAL* v2, REAL* n) n[2] = v1[0] * v2[1] - v2[0] * v1[1]; } -// distance() computs the Euclidean distance between two points. - +// 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]) + @@ -3319,24 +3324,11 @@ inline REAL tetgenmesh::distance(REAL* p1, REAL* p2) (p2[2] - p1[2]) * (p2[2] - p1[2])); } -// Linear algebra operators. - -#define NORM2(x, y, z) ((x) * (x) + (y) * (y) + (z) * (z)) - -#define DIST(p1, p2) \ - sqrt(NORM2((p2)[0] - (p1)[0], (p2)[1] - (p1)[1], (p2)[2] - (p1)[2])) - -#define DOT(v1, v2) \ - ((v1)[0] * (v2)[0] + (v1)[1] * (v2)[1] + (v1)[2] * (v2)[2]) - -#define CROSS(v1, v2, 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] - -#define SETVECTOR3(V, a0, a1, a2) (V)[0] = (a0); (V)[1] = (a1); (V)[2] = (a2) +inline REAL tetgenmesh::norm2(REAL x, REAL y, REAL z) +{ + return (x) * (x) + (y) * (y) + (z) * (z); +} -#define SWAP2(a0, a1, tmp) (tmp) = (a0); (a0) = (a1); (a1) = (tmp) #endif // #ifndef tetgenH