diff --git a/Common/CommandLine.cpp b/Common/CommandLine.cpp index 0b93ad25ad5a12820d030b6d9385649081b7a994..3730714ccebf80534a9d8da9f2500fbe91e9dbd3 100644 --- a/Common/CommandLine.cpp +++ b/Common/CommandLine.cpp @@ -1020,6 +1020,8 @@ void GetOptions(int argc, char *argv[], bool readConfigFiles, bool exitOnError) CTX::instance()->mesh.algo2d = ALGO_2D_BAMG; else if(!strncmp(argv[i], "del3d", 5) || !strncmp(argv[i], "gmsh3d", 6)) CTX::instance()->mesh.algo3d = ALGO_3D_DELAUNAY; + else if(!strncmp(argv[i], "hxt", 3) ) + CTX::instance()->mesh.algo3d = ALGO_3D_HXT; else if(!strncmp(argv[i], "front3d", 7) || !strncmp(argv[i], "netgen", 6)) CTX::instance()->mesh.algo3d = ALGO_3D_FRONTAL; else if(!strncmp(argv[i], "mmg3d", 5)) diff --git a/Common/GmshDefines.h b/Common/GmshDefines.h index bc2efb2ec3e943ffd0518bd637048acc9b0d15e6..2f784793b13f340886545d496161d75b475e4eed 100644 --- a/Common/GmshDefines.h +++ b/Common/GmshDefines.h @@ -247,6 +247,7 @@ #define ALGO_3D_FRONTAL_HEX 6 #define ALGO_3D_MMG3D 7 #define ALGO_3D_RTREE 9 +#define ALGO_3D_HXT 10 // Meshing methods #define MESH_NONE 0 diff --git a/Mesh/CMakeLists.txt b/Mesh/CMakeLists.txt index e1cbd98d062168a3e2b97ea6d140ed665fac8e46..17e8c936e87029b119d5fe4188c4e145c53ade96 100644 --- a/Mesh/CMakeLists.txt +++ b/Mesh/CMakeLists.txt @@ -15,6 +15,7 @@ set(SRC meshGFaceBamg.cpp meshGFaceBDS.cpp meshGFaceDelaunayInsertion.cpp meshGFaceLloyd.cpp meshGFaceOptimize.cpp meshGRegion.cpp + meshGRegionHxt.cpp meshDiscreteRegion.cpp meshGRegionDelaunayInsertion.cpp meshGRegionTransfinite.cpp meshGRegionExtruded.cpp meshGRegionCarveHole.cpp diff --git a/Mesh/meshGFace.cpp b/Mesh/meshGFace.cpp index 7dc4bb9ccb15f0404901206183269189ee417dd1..814abfd48ae97a579dbdd6566aa733d8be4a9ddf 100644 --- a/Mesh/meshGFace.cpp +++ b/Mesh/meshGFace.cpp @@ -1964,7 +1964,7 @@ static bool buildConsecutiveListOfVertices( return true; } -static bool meshGeneratorPeriodic(GFace *gf, bool debug = true) +static bool meshGeneratorPeriodic(GFace *gf, bool repairSelfIntersecting1dMesh, bool debug = true) { if(CTX::instance()->debugSurface > 0 && gf->tag() != CTX::instance()->debugSurface) { @@ -2382,6 +2382,9 @@ static bool meshGeneratorPeriodic(GFace *gf, bool debug = true) e->g = &CLASS_E; } + + // std::vector<EdgeToRecover> edgesNotRecovered; + for(unsigned int i = 0; i < edgeLoops_BDS.size(); i++) { std::vector<BDS_Point *> &edgeLoop_BDS = edgeLoops_BDS[i]; for(unsigned int j = 0; j < edgeLoop_BDS.size(); j++) { @@ -2389,6 +2392,9 @@ static bool meshGeneratorPeriodic(GFace *gf, bool debug = true) edgeLoop_BDS[j]->iD, edgeLoop_BDS[(j + 1) % edgeLoop_BDS.size()]->iD, _fatallyFailed); if(!e) { + // edgesNotRecovered.push_back(EdgeToRecover(edgeLoop_BDS[j]->iD, + // edgeLoop_BDS[(j + 1) % edgeLoop_BDS.size()]->iD)); + Msg::Error("Impossible to recover the edge %d %d", edgeLoop_BDS[j]->iD, edgeLoop_BDS[(j + 1) % edgeLoop_BDS.size()]->iD); gf->meshStatistics.status = GFace::FAILED; @@ -2847,7 +2853,7 @@ void meshGFace::operator()(GFace *gf, bool print) if(gf->getNativeType() != GEntity::GmshModel && (gf->periodic(0) || gf->periodic(1) || singularEdges)) { - if(!meshGeneratorPeriodic(gf, debugSurface >= 0 || debugSurface == -100)) + if(!meshGeneratorPeriodic(gf, repairSelfIntersecting1dMesh, debugSurface >= 0 || debugSurface == -100)) Msg::Error("Impossible to mesh periodic face %d", gf->tag()); } else { diff --git a/Mesh/meshGRegion.cpp b/Mesh/meshGRegion.cpp index 5a9eb534e93e5cae94beaf20dfabe7cbf39874ca..ac08173db32a7016f8e00b0088d49ada3d2ed1bd 100644 --- a/Mesh/meshGRegion.cpp +++ b/Mesh/meshGRegion.cpp @@ -8,6 +8,7 @@ #include "GmshConfig.h" #include "GmshMessage.h" #include "meshGRegion.h" +#include "meshGRegionHxt.h" #include "meshGFace.h" #include "meshGFaceOptimize.h" #include "boundaryLayersData.h" @@ -306,7 +307,12 @@ void MeshDelaunayVolume(std::vector<GRegion *> ®ions) // now do insertion of points - if(CTX::instance()->mesh.algo3d == ALGO_3D_MMG3D) { + if(CTX::instance()->mesh.algo3d == ALGO_3D_HXT) { + if (meshGRegionHxt (gr) != 0){ + Msg::Error ("HXT 3D mesh failed"); + } + } + else if(CTX::instance()->mesh.algo3d == ALGO_3D_MMG3D) { refineMeshMMG(gr); } else if(CTX::instance()->mesh.oldRefinement) { diff --git a/contrib/hxt/CMakeLists.txt b/contrib/hxt/CMakeLists.txt index 44868e22b8e49d13e028771a189176263d39ac50..91c518bcf397de49c06b6230d336f09cfe93ab1e 100644 --- a/contrib/hxt/CMakeLists.txt +++ b/contrib/hxt/CMakeLists.txt @@ -8,15 +8,32 @@ set(SRC hxt_context.c hxt_curvature.c hxt_edge.c + hxt_laplace.c hxt_linear_system.c hxt_linear_system_lu.c + hxt_linear_system_petsc.c + hxt_mesh.c hxt_message.c + hxt_non_linear_solver.c hxt_option.c - hxt_parametrization.c - hxt_mean_values.c + hxt_opt.c + hxt_sort.c hxt_tools.c - hxt_mesh.c + predicates.c + hxt_mesh3d.c + hxt_mesh3d_main.c + hxt_mesh_size.c + hxt_tetOpti.c + hxt_tetrahedra.c + hxt_tetRepair.c + hxt_tetUtils.c + hxt_tetFlag.c + hxt_tetPostpro.c + hxt_tet_aspect_ratio.c hxt_vertices.c + hxt_parametrization.c + hxt_mean_values.c + hxt_boundary_recovery.cxx ) file(GLOB_RECURSE HDR RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.h) diff --git a/contrib/hxt/CREDITS.txt b/contrib/hxt/CREDITS.txt new file mode 100644 index 0000000000000000000000000000000000000000..69efff767077185bbe4694740eb08246985b40e2 --- /dev/null +++ b/contrib/hxt/CREDITS.txt @@ -0,0 +1,10 @@ + Hxt copyright (C) 2017-2018 + + Universite catholique de Louvain + +The TetGen/BR code (tetgenBR.{cxx,h}) is copyright (c) 2016 Hang Si, +Weierstrass Institute for Applied Analysis and Stochatics. It is relicensed +under the terms of gmsh/LICENSE.txt for use in Gmsh thanks to a Software License +Agreement between Weierstrass Institute for Applied Analysis and Stochastics and +GMESH SPRL. + diff --git a/contrib/hxt/LICENSE.txt b/contrib/hxt/LICENSE.txt new file mode 100644 index 0000000000000000000000000000000000000000..bf09ce0b65a149590607aaf92998891dabd7c481 --- /dev/null +++ b/contrib/hxt/LICENSE.txt @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + 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 +this service 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. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +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 +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the 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 a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE 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. + + 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 +convey 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 General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/contrib/hxt/hxt_api.h b/contrib/hxt/hxt_api.h index ed7f6175f5c137ee7fc4f30677164e36458affb3..3d74b382b3171faec3c99166762311f20ca9ce4c 100644 --- a/contrib/hxt/hxt_api.h +++ b/contrib/hxt/hxt_api.h @@ -29,12 +29,17 @@ typedef enum HXT_STATUS_OUT_OF_MEMORY = -4, HXT_STATUS_FILE_CANNOT_BE_OPENED = -5, HXT_STATUS_POINTER_ERROR = -6, + HXT_STATUS_READ_ERROR = -7, + HXT_STATUS_WRITE_ERROR = -8, + HXT_STATUS_RANGE_ERROR = -9, + HXT_STATUS_FORMAT_ERROR = -10, // INTERNAL Errors (<= HXT_STATUS_INTERNAL) => HXT_CHECK does not give trace message but returns... should be catched internally ! HXT_STATUS_INTERNAL = -1024, - HXT_STATUS_SKIP = -1025, - HXT_STATUS_TRYAGAIN = -1026 + HXT_STATUS_CONFLICT = -1025, + HXT_STATUS_SKIP = -1026, + HXT_STATUS_TRYAGAIN = -1027 }HXTStatus; diff --git a/contrib/hxt/hxt_bbox.c b/contrib/hxt/hxt_bbox.c index a833fd784dcde1c1cc9e5dd332198f40c1cd25d4..87b078b403d38917c6a593e4c3a6f9b4e630109d 100644 --- a/contrib/hxt/hxt_bbox.c +++ b/contrib/hxt/hxt_bbox.c @@ -1,9 +1,9 @@ #include "hxt_bbox.h" // /* create a new bounding box */ -// HXTStatus hxtBboxCreate(hxtBbox** bboxP){ +// HXTStatus hxtBboxCreate(HXTBbox** bboxP){ // HXT_CHECK( -// hxtMalloc((void **) bboxP, sizeof(hxtBbox)) ); +// hxtMalloc((void **) bboxP, sizeof(HXTBbox)) ); // unsigned i; // for (i=0; i<3; i++) @@ -16,7 +16,7 @@ // } -// HXTStatus hxtBboxDelete(hxtBbox** bboxP){ +// HXTStatus hxtBboxDelete(HXTBbox** bboxP){ // HXT_CHECK( // hxtFree((void **) bboxP) ); @@ -25,7 +25,7 @@ /* update the bounding box with one new vertex */ -HXTStatus hxtBboxAddOne(hxtBbox* bbox, double* coord){ +HXTStatus hxtBboxAddOne(HXTBbox* bbox, double* coord){ unsigned i; for (i=0; i<3; i++) { @@ -41,7 +41,7 @@ HXTStatus hxtBboxAddOne(hxtBbox* bbox, double* coord){ /* update the bounding box with an array of n vertices at once (far quicker) */ -HXTStatus hxtBboxAdd(hxtBbox* bbox, double* coord, const uint32_t n){ +HXTStatus hxtBboxAdd(HXTBbox* bbox, double* coord, const uint32_t n){ #pragma omp parallel { @@ -51,19 +51,19 @@ HXTStatus hxtBboxAdd(hxtBbox* bbox, double* coord, const uint32_t n){ #pragma omp for nowait for (uint32_t i=0; i<n; i++) { - if(coord[4*i ]<min[0]) - min[0]=coord[4*i ]; - if(coord[4*i+1]<min[1]) - min[1]=coord[4*i+1]; - if(coord[4*i+2]<min[2]) - min[2]=coord[4*i+2]; - - if(coord[4*i ]>max[0]) - max[0]=coord[4*i ]; - if(coord[4*i+1]>max[1]) - max[1]=coord[4*i+1]; - if(coord[4*i+2]>max[2]) - max[2]=coord[4*i+2]; + if(coord[(size_t) 4*i ]<min[0]) + min[0]=coord[(size_t) 4*i ]; + if(coord[(size_t) 4*i+1]<min[1]) + min[1]=coord[(size_t) 4*i+1]; + if(coord[(size_t) 4*i+2]<min[2]) + min[2]=coord[(size_t) 4*i+2]; + + if(coord[(size_t) 4*i ]>max[0]) + max[0]=coord[(size_t) 4*i ]; + if(coord[(size_t) 4*i+1]>max[1]) + max[1]=coord[(size_t) 4*i+1]; + if(coord[(size_t) 4*i+2]>max[2]) + max[2]=coord[(size_t) 4*i+2]; } #pragma omp critical diff --git a/contrib/hxt/hxt_bbox.h b/contrib/hxt/hxt_bbox.h index 1159c69de43993cc35bcfa035807cab6a1bbeffb..6db445d5f09eace24f96323e05a2df9a2d4b33a8 100644 --- a/contrib/hxt/hxt_bbox.h +++ b/contrib/hxt/hxt_bbox.h @@ -12,15 +12,15 @@ extern "C" { typedef struct hxtBboxStruct{ double hxtDeclareAligned min[3]; double hxtDeclareAligned max[3]; -} hxtBbox; +} HXTBbox; // /* create a new bounding box */ -// HXTStatus hxtBboxCreate(hxtBbox** bboxP); +// HXTStatus hxtBboxCreate(HXTBbox** bboxP); // /* delete a bounding box */ -// HXTStatus hxtBboxDelete(hxtBbox** bboxP); +// HXTStatus hxtBboxDelete(HXTBbox** bboxP); -static inline void hxtBboxInit(hxtBbox* bbox){ +static inline void hxtBboxInit(HXTBbox* bbox){ bbox->min[0] = DBL_MAX; bbox->min[1] = DBL_MAX; bbox->min[2] = DBL_MAX; @@ -30,10 +30,10 @@ static inline void hxtBboxInit(hxtBbox* bbox){ } /* update the bounding box with one new vertex */ -HXTStatus hxtBboxAddOne(hxtBbox* bbox, double* coord); +HXTStatus hxtBboxAddOne(HXTBbox* bbox, double* coord); /* update the bounding box with an array of n vertices at once (far quicker) */ -HXTStatus hxtBboxAdd(hxtBbox* bbox, double* coord, uint32_t n); +HXTStatus hxtBboxAdd(HXTBbox* bbox, double* coord, uint32_t n); #ifdef __cplusplus } diff --git a/contrib/hxt/hxt_boundary_recovery.cxx b/contrib/hxt/hxt_boundary_recovery.cxx new file mode 100644 index 0000000000000000000000000000000000000000..52c7d0142250f81f49f456b871fdafceb94d7631 --- /dev/null +++ b/contrib/hxt/hxt_boundary_recovery.cxx @@ -0,0 +1,643 @@ +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <math.h> +#include <set> +#include <vector> +#include <time.h> +#if !defined(HAVE_NO_STDINT_H) +#include <stdint.h> +#elif defined(HAVE_NO_INTPTR_T) +typedef unsigned long intptr_t; +#endif +extern "C" { +#include "hxt_mesh.h" +#include "predicates.h" +#include "hxt_api.h" +#include "hxt_boundary_recovery.h" +} + +#define REAL double + +// dummy tetgenio class +class tetgenio{ +public: + int firstnumber; + int numberofpointattributes; + int numberoftetrahedronattributes; + int numberofsegmentconstraints; + REAL *segmentconstraintlist; + int numberoffacetconstraints; + REAL *facetconstraintlist; + int numberofpoints; + int *pointlist; + int *pointattributelist; + int numberofpointmakers; + int *pointmarkerlist; + int numberofpointmtrs; + int *pointmtrlist; + int numberofedges; + int *edgelist; + int *edgemarkerlist; + int numberofholes; + REAL *holelist; + int numberofregions; + REAL *regionlist; + int mesh_dim; + tetgenio() + { + firstnumber = 0; + numberofpointattributes = 0; + numberoftetrahedronattributes = 0; + numberofsegmentconstraints = 0; + segmentconstraintlist = 0; + numberoffacetconstraints = 0; + facetconstraintlist = 0; + numberofpoints = 0; + pointlist = 0; + pointattributelist = 0; + numberofpointmakers = 0; + pointmarkerlist = 0; + numberofpointmtrs = 0; + pointmtrlist = 0; + numberofedges = 0; + edgelist = 0; + edgemarkerlist = 0; + numberofholes = 0; + holelist = 0; + numberofregions = 0; + regionlist = 0; + mesh_dim = 0; + } +}; + +static double orient4d(double*, double *, double *, double *, double *, + double, double, double, double, double){ return 0.; } +#if !defined(TETLIBRARY) +#define TETLIBRARY +#endif +#include "tetgenBR.h" +#include "tetgenBR.cxx" + +// int _edges [12][2] = {{3,2},{3,0},{1,0},{1,2},{2,1},{0,2},{0,3},{2,0},{1,3},{2,3},{3,1},{0,1}}; + +// int computeTetGenVersion (const int iface1, uint32_t *v1, +// uint32_t *v2){ + +// uint32_t i1 = v1[_edges[iface1][0]]; +// uint32_t i2 = v1[_edges[iface1][1]]; +// for (int i=0;i<12;i++){ +// if (i2 == v2[_edges[i][0]] && +// i1 == v2[_edges[i][1]]) return i; +// } +// printf("oulalalala\n"); +// return -1; + +// } + +static inline int computeTetGenVersion2(uint32_t v1, uint32_t* v2Choices, const int iface2){ + int i; + for (i=0; i<3; i++) { + if(v1==v2Choices[i]){ + break; + } + } +#ifdef DEBUG + if(i==3) + HXT_WARNING("should never happen (file:%s line:%s)\n", __FILE__, __LINE__); +#endif + + return 4*i + iface2; +} + + +bool tetgenmesh::reconstructmesh(void *p){ + HXTMesh *mesh = (HXTMesh*) p; + in = new tetgenio(); + b = new tetgenbehavior(); + char opts[128]; + sprintf(opts, "YpeQT%g", 1.e-12); + b->parse_commandline(opts); + + initializepools(); + + // printf("we have %u vertices\n", mesh->vertices.num); + + { + point pointloop; + REAL x, y, z; + // Read the points. + for (uint32_t i = 0; i < mesh->vertices.num; i++) { + makepoint(&pointloop, UNUSEDVERTEX); + // Read the point coordinates. + x = pointloop[0] =mesh->vertices.coord[4*i ]; + y = pointloop[1] =mesh->vertices.coord[4*i+1]; + z = pointloop[2] =mesh->vertices.coord[4*i+2]; + // Determine the smallest and largest x, y and z coordinates. + if (i == 0) { + xmin = xmax = x; + ymin = ymax = y; + zmin = zmax = z; + } + else { + xmin = (x < xmin) ? x : xmin; + xmax = (x > xmax) ? x : xmax; + ymin = (y < ymin) ? y : ymin; + ymax = (y > ymax) ? y : ymax; + zmin = (z < zmin) ? z : zmin; + zmax = (z > zmax) ? z : zmax; + } + } + + // 'longest' is the largest possible edge length formed by input vertices. + x = xmax - xmin; + y = ymax - ymin; + z = zmax - zmin; + longest = sqrt(x * x + y * y + z * z); + if (longest == 0.0) { + return true; + } + + // Two identical points are distinguished by 'lengthlimit'. + if (minedgelength == 0.0) { + minedgelength = longest * b->epsilon; + } + } + + point *idx2verlist; + + // Create a map from indices to vertices. + // printf("we create a map from indices to vertices\n"); + makeindex2pointmap(idx2verlist); + // 'idx2verlist' has length 'in->numberofpoints + 1'. + if (in->firstnumber == 1) { + idx2verlist[0] = dummypoint; // Let 0th-entry be dummypoint. + } + { + tetrahedron *ver2tetarray; + //point *idx2verlist; + triface tetloop, checktet, prevchktet; + triface hulltet, face1, face2; + tetrahedron tptr; + point p[4], q[3]; + REAL ori; //, attrib, volume; + int bondflag; + int t1ver; + int idx, k; + + // Allocate an array that maps each vertex to its adjacent tets. + // printf("Allocate an array that maps each vertex to its adjacent tets\n"); + ver2tetarray = new tetrahedron[mesh->vertices.num + 1]; + for (unsigned int i = in->firstnumber; i < mesh->vertices.num + in->firstnumber; i++) { + setpointtype(idx2verlist[i], VOLVERTEX); // initial type. + ver2tetarray[i] = NULL; + } + + + // Create the tetrahedra and connect those that share a common face. + // printf("Connect %d tetrahedra\n", mesh->tetrahedra.num); + + const int perm[4] = {1,0,2,3}; + std::vector<triface> ts( mesh->tetrahedra.num ); + for (uint64_t i = 0; i < mesh->tetrahedra.num; i++) { + if (mesh->tetrahedra.node[4*i+3] == HXT_GHOST_VERTEX) + continue; + + maketetrahedron(&tetloop); // tetloop.ver = 11. + for (uint64_t j = 0; j < 4; j++) { + p[j] = idx2verlist[mesh->tetrahedra.node[j+4*i]]; + } + setvertices(tetloop, p[perm[0]], p[perm[1]], p[perm[2]], p[perm[3]]); + ts[i] = tetloop; + } + + for (uint64_t i = 0; i < mesh->tetrahedra.num; i++) { + if (mesh->tetrahedra.node[4*i+3] != HXT_GHOST_VERTEX){ + + for (int iface1=0; iface1<4; iface1++){ + uint64_t neigh = mesh->tetrahedra.neigh[4*i + perm[iface1]]; + // p[1] and p[0] have been exchanged + if(neigh!=HXT_NO_ADJACENT) { + uint64_t n = neigh >> 2; + int iface2 = perm[neigh&3]; + + if (mesh->tetrahedra.node[4*n+3] != HXT_GHOST_VERTEX){ + triface tf1 = ts[i]; + triface tf2 = ts[n]; + tf1.ver = iface1; + + // the face of the neighbor tetrahedra that is the same + uint32_t face2[3] = {mesh->tetrahedra.node[4*n+perm[(iface2+1)&3]], + mesh->tetrahedra.node[4*n+perm[((iface2&2)^3)]], + mesh->tetrahedra.node[4*n+perm[((iface2+3)&2)]]}; + + tf2.ver = computeTetGenVersion2 (mesh->tetrahedra.node[4*i+perm[(iface1+1)&3]], face2, iface2); + bond(tf1,tf2); + } + } + } + } + } + + // printf("Create hull tets, create the point-to-tet map, and clean up the temporary spaces used in each tet\n"); + // Create hull tets, create the point-to-tet map, and clean up the + // temporary spaces used in each tet. + hullsize = tetrahedrons->items; + tetrahedrons->traversalinit(); + tetloop.tet = tetrahedrontraverse(); + while (tetloop.tet != (tetrahedron *) NULL) { + tptr = encode(tetloop); + for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) { + if (tetloop.tet[tetloop.ver] == NULL) { + // Create a hull tet. + maketetrahedron(&hulltet); + p[0] = org(tetloop); + p[1] = dest(tetloop); + p[2] = apex(tetloop); + setvertices(hulltet, p[1], p[0], p[2], dummypoint); + bond(tetloop, hulltet); + // Try connecting this to others that share common hull edges. + for (int j = 0; j < 3; j++) { + fsym(hulltet, face2); + while (1) { + if (face2.tet == NULL) + break; + + esymself(face2); + + if (apex(face2) == dummypoint) + break; + + fsymself(face2); + } + if (face2.tet != NULL) { + // Found an adjacent hull tet. + assert(face2.tet[face2.ver & 3] == NULL); + esym(hulltet, face1); + bond(face1, face2); + } + enextself(hulltet); + } + //hullsize++; + } + + // Create the point-to-tet map. + setpoint2tet((point) (tetloop.tet[4 + tetloop.ver]), tptr); + + // Clean the temporary used space. + tetloop.tet[8 + tetloop.ver] = NULL; + } + tetloop.tet = tetrahedrontraverse(); + } + + hullsize = tetrahedrons->items - hullsize; + + delete [] ver2tetarray; + } + { + face newsh; + face newseg; + point p[4]; + int idx; + + for (uint64_t i=0;i<mesh->triangles.num;i++){ + for (uint64_t j = 0; j < 3; j++) { + p[j] = idx2verlist[mesh->triangles.node[3*i+j]]; + if (pointtype(p[j]) == VOLVERTEX) { + setpointtype(p[j], FACETVERTEX); + } + } + + // Create an initial triangulation. + makeshellface(subfaces, &newsh); + setshvertices(newsh, p[0], p[1], p[2]); + setshellmark(newsh, mesh->triangles.colors[i]); + recentsh = newsh; + + for (int j = 0; j < 3; j++) { + makeshellface(subsegs, &newseg); + setshvertices(newseg, sorg(newsh), sdest(newsh), NULL); + // Set the default segment marker '-1'. + setshellmark(newseg, -1); + ssbond(newsh, newseg); + senextself(newsh); + } + } // i + + unifysegments(); + + + face* shperverlist; + int* idx2shlist; + face searchsh, neighsh; + face segloop, checkseg; + point checkpt; + + // Construct a map from points to subfaces. + makepoint2submap(subfaces, idx2shlist, shperverlist); + + // Process the set of PSC edges. + // Remeber that all segments have default marker '-1'. + // int COUNTER = 0; + // printf("Add Edges\n"); + { + for (uint64_t i = 0; i< mesh->lines.num;i++) { + for (uint64_t j = 0; j < 2; j++) { + p[j] = idx2verlist[mesh->lines.node[2*i+j]]; + setpointtype(p[j], RIDGEVERTEX); + } + + if (p[0] == p[1]) { + // This is a potential problem in surface mesh. + continue; // Skip this edge. + } + + // Find a face contains the edge p[0], p[1]. + newseg.sh = NULL; + searchsh.sh = NULL; + idx = pointmark(p[0]) - in->firstnumber; + for (int j = idx2shlist[idx]; j < idx2shlist[idx + 1]; j++) { + checkpt = sdest(shperverlist[j]); + + if (checkpt == p[1]) { + searchsh = shperverlist[j]; + break; // Found. + } + else { + checkpt = sapex(shperverlist[j]); + if (checkpt == p[1]) { + senext2(shperverlist[j], searchsh); + sesymself(searchsh); + break; + } + } + } // j + + if (searchsh.sh != NULL) { + // Check if this edge is already a segment of the mesh. + sspivot(searchsh, checkseg); + if (checkseg.sh != NULL) { + // This segment already exist. + newseg = checkseg; + } + else { + // Create a new segment at this edge. + makeshellface(subsegs, &newseg); + setshvertices(newseg, p[0], p[1], NULL); + ssbond(searchsh, newseg); + spivot(searchsh, neighsh); + if (neighsh.sh != NULL) { + ssbond(neighsh, newseg); + } + } + } + else { + // It is a dangling segment (not belong to any facets). + // Check if segment [p[0],p[1]] 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] == p[0]) && (ppt[1] == p[1])) || + ((ppt[0] == p[1]) && (ppt[1] == p[0]))) { + // Found! + newseg = segloop; + break; + } + segloop.sh = shellfacetraverse(subsegs); + }*/ + if (newseg.sh == NULL) { + makeshellface(subsegs, &newseg); + setshvertices(newseg, p[0], p[1], NULL); + } + } + setshellmark(newseg, mesh->lines.colors[i]); + } // i + + delete [] shperverlist; + delete [] idx2shlist; + insegments = subsegs->items; + } + } + + delete [] idx2verlist; + clock_t t = clock(); + recoverboundary(t); + // printf("Carve Holes\n"); + // carveholes(); + if (subvertstack->objects > 0l) { + HXT_INFO("Suppressing Steiner points..."); + suppresssteinerpoints(); + } +#if 1 + HXT_INFO("Recover Delaunay"); + recoverdelaunay(); +#endif + // printf("Optimize final mesh\n"); + // optimizemesh(); + { + // printf("Write to HXT -->\n"); + // Write mesh into to HXT. + point p[4]; + std::set<int> /*l_faces, */l_edges; + + if (points->items > mesh->vertices.num) { + mesh->vertices.num = points->items; + if(mesh->vertices.num > mesh->vertices.size) { + // HXT_CHECK( hxtAlignedFree (&mesh->vertices.coord) ); + HXT_CHECK( hxtAlignedRealloc(&mesh->vertices.coord, + 4*mesh->vertices.num*sizeof( double )) ); + mesh->vertices.size = mesh->vertices.num; + } + + face parentseg, parentsh, spinsh; + point pointloop; + // Create newly added mesh vertices. + // The new vertices must be added at the end of the point list. + points->traversalinit(); + pointloop = pointtraverse(); + int reconstructingTriangularMeshIsRequired = 0; + while (pointloop != (point) NULL) { + if (issteinerpoint(pointloop)) { + // Check if this Steiner point locates on boundary. + if (pointtype(pointloop) == FREESEGVERTEX) { + HXT_WARNING("Steiner point inserted that splits an existing edge" + "(boundary mesh has changed," + "JF you should finish that thing and reconstruct edges as well)"); + reconstructingTriangularMeshIsRequired = 1; + sdecode(point2sh(pointloop), parentseg); + assert(parentseg.sh != NULL); + l_edges.insert(shellmark(parentseg)); + mesh->vertices.coord[4*pointmark(pointloop) ] = pointloop[0]; + mesh->vertices.coord[4*pointmark(pointloop)+1] = pointloop[1]; + mesh->vertices.coord[4*pointmark(pointloop)+2] = pointloop[2]; + } + else if (pointtype(pointloop) == FREEFACETVERTEX) { + // HXT_INFO("Steiner point inserted that splits an existing facet (boundary mesh has changed, JF you should code that thing)"); + reconstructingTriangularMeshIsRequired = 1; + sdecode(point2sh(pointloop), parentsh); + assert(parentsh.sh != NULL); + int ftag = shellmark(parentsh); + mesh->vertices.coord[4*pointmark(pointloop) ] = pointloop[0]; + mesh->vertices.coord[4*pointmark(pointloop)+1] = pointloop[1]; + mesh->vertices.coord[4*pointmark(pointloop)+2] = pointloop[2]; + } + else if (pointtype(pointloop) == FREEVOLVERTEX) { + // HXT_INFO("Steiner point inserted inside the volume of the mesh (not that much of a problem)"); + mesh->vertices.coord[4*pointmark(pointloop) ] = pointloop[0]; + mesh->vertices.coord[4*pointmark(pointloop)+1] = pointloop[1]; + mesh->vertices.coord[4*pointmark(pointloop)+2] = pointloop[2]; + } + } + else{ + mesh->vertices.coord[4*pointmark(pointloop) ] = pointloop[0]; + mesh->vertices.coord[4*pointmark(pointloop)+1] = pointloop[1]; + mesh->vertices.coord[4*pointmark(pointloop)+2] = pointloop[2]; + } + + pointloop = pointtraverse(); + } + if (reconstructingTriangularMeshIsRequired) { + // restore 2D mesh ... + HXT_CHECK( hxtAlignedFree(&(mesh->triangles.node))); + HXT_CHECK( hxtAlignedFree(&(mesh->triangles.colors))); + HXT_INFO("deleting %u triangles",mesh->triangles.num); + mesh->triangles.num = 0; // firstindex; // in->firstnumber; + { + face subloop; + subloop.shver = 0; + subfaces->traversalinit(); + subloop.sh = shellfacetraverse(subfaces); + while (subloop.sh != NULL) { + mesh->triangles.num ++ ; + subloop.sh = shellfacetraverse(subfaces); + } + } + HXT_INFO("creating %u triangles",mesh->triangles.num); + + { + face subloop; + HXT_CHECK( hxtAlignedMalloc(&mesh->triangles.node, + (mesh->triangles.num)*3*sizeof(uint32_t)) ); + if (mesh->triangles.node == NULL) + return HXT_ERROR(HXT_STATUS_OUT_OF_MEMORY); + HXT_CHECK( hxtAlignedMalloc(&mesh->triangles.colors, + (mesh->triangles.num)*sizeof(uint16_t)) ); + if (mesh->triangles.colors == NULL) + return HXT_ERROR(HXT_STATUS_OUT_OF_MEMORY); + mesh->triangles.size = mesh->triangles.num; + subloop.shver = 0; + subfaces->traversalinit(); + subloop.sh = shellfacetraverse(subfaces); + int iTriangle = 0; + while (subloop.sh != NULL) { + p[0] = sorg(subloop); + p[1] = sdest(subloop); + p[2] = sapex(subloop); + mesh->triangles.node [3*iTriangle+0] = pointmark(p[0]); + mesh->triangles.node [3*iTriangle+1] = pointmark(p[1]); + mesh->triangles.node [3*iTriangle+2] = pointmark(p[2]); + mesh->triangles.colors[iTriangle] = shellmark(subloop); + iTriangle++; + subloop.sh = shellfacetraverse(subfaces); + } + } + } + } + + int elementnumber = 1; // firstindex; // in->firstnumber; + { + // number tets + triface tetloop; + tetrahedrons->traversalinit(); + tetloop.tet = tetrahedrontraverse(); + while (tetloop.tet != (tetrahedron *) NULL) { + setelemindex(tetloop.tet, elementnumber); + tetloop.tet = tetrahedrontraverse(); + elementnumber++; + } + } + + { + // move data to HXT + triface tetloop; + tetrahedrons->traversalinit(); + tetloop.tet = tetrahedrontraverse(); + mesh->tetrahedra.num = elementnumber-1; + if(mesh->tetrahedra.num > mesh->tetrahedra.size) { + HXT_CHECK( hxtAlignedFree(&mesh->tetrahedra.node) ); + HXT_CHECK( hxtAlignedFree(&mesh->tetrahedra.neigh) ); + HXT_CHECK( hxtAlignedFree(&mesh->tetrahedra.colors) ); + HXT_CHECK( hxtAlignedFree(&mesh->tetrahedra.flag) ); + + HXT_CHECK( hxtAlignedMalloc(&mesh->tetrahedra.node, + (mesh->tetrahedra.num)*4*sizeof(uint32_t)) ); + HXT_CHECK( hxtAlignedMalloc(&mesh->tetrahedra.neigh, + (mesh->tetrahedra.num)*4*sizeof(uint64_t)) ); + HXT_CHECK( hxtAlignedMalloc(&mesh->tetrahedra.colors, + (mesh->tetrahedra.num)*sizeof(uint16_t)) ); + HXT_CHECK( hxtAlignedMalloc(&mesh->tetrahedra.flag, + (mesh->tetrahedra.num)*sizeof(uint16_t)) ); + + mesh->tetrahedra.size = mesh->tetrahedra.num; + } + + + int counter = 0; + while (tetloop.tet != (tetrahedron *) NULL) { + tetloop.ver = 11; + p[0] = org(tetloop); + p[1] = dest(tetloop); + p[2] = apex(tetloop); + p[3] = oppo(tetloop); + triface E, N[4]; + E = tetloop; + for (E.ver = 0; E.ver < 4; E.ver++) { + fsym(E, N[E.ver]); + } + int orderHXT[4] = {1,0,2,3}; + mesh->tetrahedra.colors[counter] = 0; + for (int k=0;k<4;k++){ + mesh->tetrahedra.node[4*counter+k] = pointmark(p[orderHXT[k]]); + if (mesh->tetrahedra.node[4*counter+k] >= mesh->vertices.num) + HXT_WARNING("ERROR : index %u out of range (%u)\n",mesh->tetrahedra.node[4*counter+k],mesh->vertices.num); + } + for (int i=0;i<4;i++){ + int ngh = elemindex(N[orderHXT[i]].tet); + if (ngh) { + // mesh->tetrahedra.neigh[4*counter+i] = 4*(elemindex(N[i].tet)-1)+i; + } + else{ + // mesh->tetrahedra.neigh[4*counter+i] = HXT_NO_ADJACENT; + } + } + // printf("%d --> %d %d %d %d\n", counter, pointmark(p[0]),pointmark(p[1]),pointmark(p[2]),pointmark(p[3])); + + counter++; + tetloop.tet = tetrahedrontraverse(); + } + } // mesh output + } + + delete in; + delete b; + return true; +} + +extern "C" { + HXTStatus hxt_boundary_recovery(HXTMesh *mesh) + { + bool ret = false; + try{ + tetgenmesh *m = new tetgenmesh(); + ret = m->reconstructmesh((void*)mesh); + delete m; + return HXT_STATUS_OK ; + } + catch (...){ + return HXT_ERROR_MSG(HXT_STATUS_FAILED, "failed to recover constrained lines/triangles") ; + } + } +} diff --git a/contrib/hxt/hxt_boundary_recovery.h b/contrib/hxt/hxt_boundary_recovery.h new file mode 100644 index 0000000000000000000000000000000000000000..b046b4a7a783f9a0cb5ff4b48291bbf9682895a3 --- /dev/null +++ b/contrib/hxt/hxt_boundary_recovery.h @@ -0,0 +1,4 @@ +#ifndef _HXT_BOUNDARY_RECOVERY_ +#define _HXT_BOUNDARY_RECOVERY_ +HXTStatus hxt_boundary_recovery(HXTMesh *mesh); +#endif diff --git a/contrib/hxt/hxt_laplace.h b/contrib/hxt/hxt_laplace.h index 7bed8b91ab9294f7301d931a72ffd589d0b3f109..b6f8a24c69387233c6a440395d867beffc021941 100644 --- a/contrib/hxt/hxt_laplace.h +++ b/contrib/hxt/hxt_laplace.h @@ -2,6 +2,7 @@ #define HEXTREME_LAPLACE_H #include "hxt_mesh.h" + HXTStatus hxtLaplace(HXTMesh *mesh); #endif diff --git a/contrib/hxt/hxt_linear_system_lu.c b/contrib/hxt/hxt_linear_system_lu.c index 6240723797f1fe9d52fbe8cbcf75893728bef833..ad21fef5def674041642667f06039df3b3d1f105 100644 --- a/contrib/hxt/hxt_linear_system_lu.c +++ b/contrib/hxt/hxt_linear_system_lu.c @@ -114,24 +114,34 @@ typedef hxtDeclareAligned double alignedDouble; // y[from:to] += a*x[from:to] // y and x must be 256-bit aligned // memory should be allocated in consequence -static void rowAxpy(double a, alignedDouble *__restrict__ x, alignedDouble *__restrict__ y, int from, int to) +static void rowAxpy(double a, double *__restrict__ px, double *__restrict__ py, int from, int to) { + // Can't use the aligned attribute on function arguments. + hxtDeclareAligned double * __restrict__ x = px; + hxtDeclareAligned double * __restrict__ y = py; + int i = from; - int pfrom = (from+3)&(~3); + int pfrom = (from+7)&(~7); if (pfrom > to) pfrom = to; for (; i < pfrom; ++i) y[i] += a*x[i]; for (; i+15 < to; i+=16) { - alignedDouble * __restrict__ X = x + i; - alignedDouble * __restrict__ Y = y + i; + hxtDeclareAligned double * __restrict__ X = x + i; + hxtDeclareAligned double * __restrict__ Y = y + i; for (int j = 0; j < 16; ++j){ Y[j] += a * X[j]; } } + for (; i+7 < to; i+=8) { + hxtDeclareAligned double * __restrict__ X = x + i; + hxtDeclareAligned double * __restrict__ Y = y + i; + for (int j = 0; j < 8; ++j) + Y[j] += a * X[j]; + } for (; i+3 < to; i+=4) { - alignedDouble * __restrict__ X = x + i; - alignedDouble * __restrict__ Y = y + i; + double * __restrict__ X = x + i; + double * __restrict__ Y = y + i; for (int j = 0; j < 4; ++j) Y[j] += a * X[j]; } @@ -144,35 +154,40 @@ static int imin(int a, int b) { return a < b ? a : b; } -static double rowReduction(alignedDouble *__restrict__ x, alignedDouble *__restrict__ y, int from, int to) +static double rowReduction(double *__restrict__ px, double *__restrict__ py, int from, int to) { - int i = from; double r = 0; - int pfrom = (from+3)&(~3); + hxtDeclareAligned double * __restrict__ x = px; + hxtDeclareAligned double * __restrict__ y = py; + + int i = from; + int pfrom = (from+7)&(~7); for (; i < imin(pfrom, to); ++i) r += x[i] * y[i]; - double R[4]; - for (; i+3 < to; i+=4) { - alignedDouble * __restrict__ X = x + i; - alignedDouble * __restrict__ Y = y + i; - for (int j = 0; j < 4; ++j) + double R[8]; + for (; i+7 < to; i+=8) { + hxtDeclareAligned double * __restrict__ X = x + i; + hxtDeclareAligned double * __restrict__ Y = y + i; + for (int j = 0; j < 8; ++j) R[j] = X[j]*Y[j]; - r += R[0]+R[1]+R[2]+R[3]; + r += R[0]+R[1]+R[2]+R[3]+R[4]+R[5]+R[6]+R[7]; } for (; i < to; ++i) r += x[i] * y[i]; return r; } -static void rowZero(alignedDouble *__restrict__ x, int from, int to) +static void rowZero(double *__restrict__ px, int from, int to) { + hxtDeclareAligned double * __restrict__ x = px; + int i = from; - int pfrom = (from+3)&(~3); + int pfrom = (from+7)&(~7); for (; i < imin(pfrom, to); ++i) x[i] = 0; - for (; i+3 < to; i+=4) { - alignedDouble * __restrict__ X = x + i; - for (int j = 0; j < 4; ++j) + for (; i+7 < to; i+=8) { + hxtDeclareAligned double * __restrict__ X = x + i; + for (int j = 0; j < 8; ++j) X[j] = 0; } for (; i < to; ++i) @@ -279,7 +294,7 @@ HXTStatus hxtLinearSystemLUCreate(HXTLinearSystemLU **pSystem, int nElements, in } free(nodeRowStart); free(nodeRowEnd); - system->M = malloc(sizeof(double)*totalSize); // FIXME Gmsh instead of _mm_malloc + system->M = _mm_malloc(sizeof(double)*totalSize, PADDING*8); system->rows = malloc(sizeof(double*)*system->n); for (int i = 0; i < totalSize; ++i) system->M[i] = 0; @@ -291,7 +306,7 @@ HXTStatus hxtLinearSystemLUCreate(HXTLinearSystemLU **pSystem, int nElements, in totalSize += system->rowEnd[i]-system->rowStart[i]+(paddedStart-start); system->rows[i] = system->M + paddedStart; } - system->x = malloc(sizeof(double)*system->n); // FIXME Gmsh instead of _mm_malloc + system->x = _mm_malloc(sizeof(double)*system->n, PADDING*8); return HXT_STATUS_OK; } @@ -384,7 +399,7 @@ HXTStatus hxtLinearSystemLUAddMatrixEntry(HXTLinearSystemLU *system, int node0, HXT_ERROR_MSG(HXT_STATUS_FAILED, "node %i or %i not in the domain", node0, node1); int row0 = system->nodeMap[node0]*system->nFields + field0; int col1 = system->nodeMap[node1]*system->nFields + field1; - + system->rows[row0][col1] += v; return HXT_STATUS_OK; } @@ -403,7 +418,7 @@ HXTStatus hxtLinearSystemLUSolve(HXTLinearSystemLU *system, double *rhs, double LUPDecompose(system); system->flaglu=1; } - + LUPSolve(system, rhs); for (int i = 0; i < system->nNodes; ++i){ int ii = system->nodeMap[i]; diff --git a/contrib/hxt/hxt_linear_system_petsc.c b/contrib/hxt/hxt_linear_system_petsc.c index d359fc642be4c9e99a2f392ca03c8dad4403b35c..03f2db4cce6c527605a1509752bd4f4c662e260f 100644 --- a/contrib/hxt/hxt_linear_system_petsc.c +++ b/contrib/hxt/hxt_linear_system_petsc.c @@ -229,16 +229,30 @@ HXTStatus hxtLinearSystemPETScAddRhsEntry(HXTLinearSystemPETSc *system, double * HXTStatus hxtLinearSystemPETScSolve(HXTLinearSystemPETSc *system, double *rhs, double *solution){ Vec b; + Vec x; HXT_PETSC_CHECK(VecCreateSeqWithArray(PETSC_COMM_SELF, 1, system->nDofs, rhs, &b)); + HXT_PETSC_CHECK(VecCreateSeqWithArray(PETSC_COMM_SELF, 1, system->nDofs, solution, &x)); + double normTest=0.0; + for (int iv = 0; iv < system->nDofs; ++iv){ + normTest+=solution[iv]*solution[iv]; + } + /* printf("IN PETSC norm vect sol : %g ; nofs : %i\n",normTest,system->nDofs); */ if(system->assemblyNeeded) { HXT_PETSC_CHECK(MatAssemblyBegin(system->a, MAT_FINAL_ASSEMBLY)); HXT_PETSC_CHECK(MatAssemblyEnd(system->a, MAT_FINAL_ASSEMBLY)); system->assemblyNeeded = 0; } HXT_PETSC_CHECK(KSPSetOperators(system->ksp, system->a, system->a)); - HXT_PETSC_CHECK(KSPSolve(system->ksp, b, system->x)); + /* HXT_PETSC_CHECK(KSPSolve(system->ksp, b, system->x)); */ + KSPSetInitialGuessNonzero(system->ksp,PETSC_TRUE); + PetscBool flag[1]; + KSPGetInitialGuessNonzero(system->ksp,flag); + /* printf("IN PETSC initialguessnonzero : %i\n",flag[0]); */ + HXT_PETSC_CHECK(KSPSolve(system->ksp, b, x)); HXT_PETSC_CHECK(VecDestroy(&b)); - HXT_CHECK(hxtLinearSystemPETScMapFromVec(system, system->x, solution)); + HXT_CHECK(hxtLinearSystemPETScMapFromVec(system, x, solution)); + HXT_PETSC_CHECK(VecDestroy(&x)); + /* HXT_CHECK(hxtLinearSystemPETScMapFromVec(system, system->x, solution)); */ return HXT_STATUS_OK; } diff --git a/contrib/hxt/hxt_mean_values.c b/contrib/hxt/hxt_mean_values.c index 53cde1182cda47c630e7832be7f6939d637a8ab4..8a662c13c6b45a01925ac26a4cd65b991b0152bc 100644 --- a/contrib/hxt/hxt_mean_values.c +++ b/contrib/hxt/hxt_mean_values.c @@ -155,12 +155,12 @@ HXTStatus hxtMeanValuesCompute(HXTMeanValues *meanValues){ // gather local edge index for(int it=0; it<2; it++){ if(tri[it]==(uint64_t)-1) - break; + break; for(int k=0; k<3; k++){ - if(edges->tri2edg[3*tri[it]+k]==ie){ - ik[it] = k; - break; - } + if(edges->tri2edg[3*tri[it]+k]==ie){ + ik[it] = k; + break; + } } } @@ -170,32 +170,32 @@ HXTStatus hxtMeanValuesCompute(HXTMeanValues *meanValues){ uint32_t v1 = edges->node[2*ie+(1-ij)]; if (flag[v0]==1){//boundary nodes/conditons - HXT_CHECK(hxtLinearSystemSetMatrixRowIdentity(sys,v0,0)); - HXT_CHECK(hxtLinearSystemSetRhsEntry(sys,Urhs, v0,0, uv[2*v0+0])); - HXT_CHECK(hxtLinearSystemSetRhsEntry(sys,Vrhs, v0,0, uv[2*v0+1])); + HXT_CHECK(hxtLinearSystemSetMatrixRowIdentity(sys,v0,0)); + HXT_CHECK(hxtLinearSystemSetRhsEntry(sys,Urhs, v0,0, uv[2*v0+0])); + HXT_CHECK(hxtLinearSystemSetRhsEntry(sys,Vrhs, v0,0, uv[2*v0+1])); } - else {// inner node - double e[3] = {mesh->vertices.coord[4*v1+0]-mesh->vertices.coord[4*v0+0],mesh->vertices.coord[4*v1+1]-mesh->vertices.coord[4*v0+1],mesh->vertices.coord[4*v1+2]-mesh->vertices.coord[4*v0+2]}; - double ne = sqrt(e[0]*e[0]+e[1]*e[1]+e[2]*e[2]); - - uint32_t vLeft = mesh->triangles.node[3*tri[0] + (ik[0]+2)%3]; - double a[3] = {mesh->vertices.coord[4*vLeft+0]-mesh->vertices.coord[4*v0+0],mesh->vertices.coord[4*vLeft+1]-mesh->vertices.coord[4*v0+1],mesh->vertices.coord[4*vLeft+2]-mesh->vertices.coord[4*v0+2]}; - double na = sqrt(a[0]*a[0]+a[1]*a[1]+a[2]*a[2]); - double thetaL = acos((a[0]*e[0]+a[1]*e[1]+a[2]*e[2])/(na*ne)); - - double thetaR=0.; - if(tri[1]==(uint64_t)-1) - thetaR=0.; - else{ - uint32_t vRight = mesh->triangles.node[3*tri[1] + (ik[1]+2)%3]; - double b[3] = {mesh->vertices.coord[4*vRight+0]-mesh->vertices.coord[4*v0+0],mesh->vertices.coord[4*vRight+1]-mesh->vertices.coord[4*v0+1],mesh->vertices.coord[4*vRight+2]-mesh->vertices.coord[4*v0+2]}; - double nb = sqrt(b[0]*b[0]+b[1]*b[1]+b[2]*b[2]); - thetaR = acos((b[0]*e[0]+b[1]*e[1]+b[2]*e[2])/(nb*ne)); - } - - double c = (tan(.5*thetaL)+tan(.5*thetaR))/ne; - HXT_CHECK(hxtLinearSystemAddMatrixEntry(sys, v0, 0, v1, 0, -c)); - HXT_CHECK(hxtLinearSystemAddMatrixEntry(sys, v0, 0, v0, 0, c)); + else {// inner node + double e[3] = {mesh->vertices.coord[4*v1+0]-mesh->vertices.coord[4*v0+0],mesh->vertices.coord[4*v1+1]-mesh->vertices.coord[4*v0+1],mesh->vertices.coord[4*v1+2]-mesh->vertices.coord[4*v0+2]}; + double ne = sqrt(e[0]*e[0]+e[1]*e[1]+e[2]*e[2]); + + uint32_t vLeft = mesh->triangles.node[3*tri[0] + (ik[0]+2)%3]; + double a[3] = {mesh->vertices.coord[4*vLeft+0]-mesh->vertices.coord[4*v0+0],mesh->vertices.coord[4*vLeft+1]-mesh->vertices.coord[4*v0+1],mesh->vertices.coord[4*vLeft+2]-mesh->vertices.coord[4*v0+2]}; + double na = sqrt(a[0]*a[0]+a[1]*a[1]+a[2]*a[2]); + double thetaL = acos((a[0]*e[0]+a[1]*e[1]+a[2]*e[2])/(na*ne)); + + double thetaR=0.; + if(tri[1]==(uint64_t)-1) + thetaR=0.; + else{ + uint32_t vRight = mesh->triangles.node[3*tri[1] + (ik[1]+2)%3]; + double b[3] = {mesh->vertices.coord[4*vRight+0]-mesh->vertices.coord[4*v0+0],mesh->vertices.coord[4*vRight+1]-mesh->vertices.coord[4*v0+1],mesh->vertices.coord[4*vRight+2]-mesh->vertices.coord[4*v0+2]}; + double nb = sqrt(b[0]*b[0]+b[1]*b[1]+b[2]*b[2]); + thetaR = acos((b[0]*e[0]+b[1]*e[1]+b[2]*e[2])/(nb*ne)); + } + + double c = (tan(.5*thetaL)+tan(.5*thetaR))/ne; + HXT_CHECK(hxtLinearSystemAddMatrixEntry(sys, v0, 0, v1, 0, -c)); + HXT_CHECK(hxtLinearSystemAddMatrixEntry(sys, v0, 0, v0, 0, c)); } } }// end for int ie @@ -224,7 +224,7 @@ HXTStatus hxtMeanValuesCompute(HXTMeanValues *meanValues){ HXT_CHECK(hxtLinearSystemAddMatrixEntry(sys, edges->node[2*edges_ll[j]+0], 0, edges->node[2*edges_ll[j]+1], 0, -tan(.5*(M_PI-theta)/2)/le )); HXT_CHECK(hxtLinearSystemAddMatrixEntry(sys, edges->node[2*edges_ll[j]+1], 0, edges->node[2*edges_ll[j]+1], 0, tan(.5*(M_PI-theta)/2)/le )); HXT_CHECK(hxtLinearSystemAddMatrixEntry(sys, edges->node[2*edges_ll[j]+1], 0, edges->node[2*edges_ll[j]+0], 0, -tan(.5*(M_PI-theta)/2)/le )); - + c[j] = tan(.5*(M_PI-theta)/2)/radius; d[j] = tan(.5*(theta))/radius; } @@ -243,7 +243,7 @@ HXTStatus hxtMeanValuesCompute(HXTMeanValues *meanValues){ } for(int j=0; j<n; j++){ - + uint32_t v = edges->node[2*edges_ll[j]+0]; HXT_CHECK(hxtLinearSystemAddMatrixEntry(sys, v, 0, v, 0, c[j])); @@ -281,22 +281,60 @@ HXTStatus hxtMeanValueAspectRatio(HXTMeanValues *meanValues, int *aspectRatio) { if(meanValues->aspectRatio<0){ + *aspectRatio = 1; + HXTMesh *mesh = meanValues->initialEdges->edg2mesh; + + double grad[3][2] = {{-1./2.,-sqrt(3)/6.},{1./2.,-sqrt(3)/6.},{0.,sqrt(3)/3.}}; + + uint64_t nTri = mesh->triangles.num; + double *uv = meanValues->uv; + for(uint64_t it=0; it<nTri; it++){ + + uint32_t *nodes = mesh->triangles.node + 3*it; + + + double jac[2][2] = {{0.,0.},{0.,0.}}; + for(int i=0; i<3; i++){ + + + jac[0][0] += uv[2*nodes[i]+0]*grad[i][0];// dx dxi + jac[0][1] += uv[2*nodes[i]+0]*grad[i][1];// dx deta + jac[1][0] += uv[2*nodes[i]+1]*grad[i][0];// dy dxi + jac[1][1] += uv[2*nodes[i]+1]*grad[i][1];// dy deta + } + + double det = jac[0][0]*jac[1][1]-jac[1][0]*jac[0][1]; + double frob = 0.; + for(int i =0; i<2; i++) + for(int j=0; j<2; j++) + frob += jac[i][j]*jac[i][j]; + + double quality = 2*det/frob; + if(quality<.1){ + printf("wrong aspect ratio !!!!!!! D-: \t %f\n",quality); + *aspectRatio = 0; + break; + } + + } + /* *aspectRatio = 1; uint32_t numEdges = meanValues->initialEdges->numEdges; double *uv = meanValues->uv; uint32_t *nodes = meanValues->initialEdges->node; for(uint32_t i=0; i<numEdges; i++){ - - double du = uv[2*nodes[2*i+1]+0] - uv[2*nodes[2*i+0]+0]; - double dv = uv[2*nodes[2*i+1]+1] - uv[2*nodes[2*i+0]+1]; - - if(sqrt(du*du+dv*dv) < 1e-4){ - *aspectRatio = 0; - break; - } - } + + double du = uv[2*nodes[2*i+1]+0] - uv[2*nodes[2*i+0]+0]; + double dv = uv[2*nodes[2*i+1]+1] - uv[2*nodes[2*i+0]+1]; + + if(sqrt(du*du+dv*dv) < 1e-4){ + *aspectRatio = 0; + break; + } + } + */ } else *aspectRatio = meanValues->aspectRatio; @@ -329,7 +367,7 @@ HXTStatus hxtMeanValuesGetData(HXTMeanValues *mv, uint64_t **global,uint32_t **g global_[ie] = mv->initialEdges->global[ie]; if(gn!=NULL) for(int kk=0; kk<3; kk++) - gn_[3*ie+kk] = mv->initialEdges->edg2mesh->triangles.node[3*ie+kk]; + gn_[3*ie+kk] = mv->initialEdges->edg2mesh->triangles.node[3*ie+kk]; } diff --git a/contrib/hxt/hxt_mesh.c b/contrib/hxt/hxt_mesh.c index 66f1e7bb09d5246d64f6ab67a32d144739a066f2..5c86fbb76af93004c364d7bbdb45b6ce4ce69394 100644 --- a/contrib/hxt/hxt_mesh.c +++ b/contrib/hxt/hxt_mesh.c @@ -1,11 +1,15 @@ #include "hxt_mesh.h" #include <float.h> +/* Compatible with HXT_ELT_TYPE enum exposed in header */ #define POINTID 15 #define LINEID 1 #define TRIID 2 -#define TETID 4 #define QUADID 3 +#define TETID 4 +#define HEXID 5 +#define PRIID 6 +#define PYRID 7 HXTStatus hxtMeshCreate ( HXTContext* ctx, HXTMesh** mesh) { HXT_CHECK( hxtMalloc (mesh, sizeof(HXTMesh)) ); @@ -16,29 +20,63 @@ HXTStatus hxtMeshCreate ( HXTContext* ctx, HXTMesh** mesh) { (*mesh)->vertices.coord = NULL; (*mesh)->vertices.num = 0; (*mesh)->vertices.size = 0; - (*mesh)->typeStat = NULL; // tetrahedra (*mesh)->tetrahedra.node = NULL; (*mesh)->tetrahedra.colors = NULL; + (*mesh)->tetrahedra.flag = NULL; (*mesh)->tetrahedra.neigh = NULL; + (*mesh)->tetrahedra.neighType = NULL; // (*mesh)->tetrahedra.subdet = NULL; (*mesh)->tetrahedra.num = 0; (*mesh)->tetrahedra.size = 0; + // hexahedra + (*mesh)->hexahedra.node = NULL; + (*mesh)->hexahedra.colors = NULL; + (*mesh)->hexahedra.flag = NULL; + (*mesh)->hexahedra.neigh = NULL; + (*mesh)->hexahedra.neighType = NULL; + (*mesh)->hexahedra.num = 0; + (*mesh)->hexahedra.size = 0; + + // prisms + (*mesh)->prisms.node = NULL; + (*mesh)->prisms.colors = NULL; + (*mesh)->prisms.flag = NULL; + (*mesh)->prisms.neigh = NULL; + (*mesh)->prisms.neighType = NULL; + (*mesh)->prisms.num = 0; + (*mesh)->prisms.size = 0; + + // pyramids + (*mesh)->pyramids.node = NULL; + (*mesh)->pyramids.colors = NULL; + (*mesh)->pyramids.flag = NULL; + (*mesh)->pyramids.neigh = NULL; + (*mesh)->pyramids.neighType = NULL; + (*mesh)->pyramids.num = 0; + (*mesh)->pyramids.size = 0; + // triangles (*mesh)->triangles.node = NULL; + (*mesh)->triangles.neigh = NULL; (*mesh)->triangles.colors = NULL; (*mesh)->triangles.num = 0; (*mesh)->triangles.size = 0; + // quads + (*mesh)->quads.node = NULL; + (*mesh)->quads.colors = NULL; + (*mesh)->quads.num = 0; + (*mesh)->quads.size = 0; + // lines (*mesh)->lines.node = NULL; (*mesh)->lines.colors = NULL; (*mesh)->lines.num = 0; (*mesh)->lines.size = 0; - // (*mesh)->numHexahedra = 0; return HXT_STATUS_OK; } @@ -50,19 +88,45 @@ HXTStatus hxtMeshDelete ( HXTMesh** mesh) { // tetrahedra HXT_CHECK( hxtAlignedFree(&(*mesh)->tetrahedra.colors) ); + HXT_CHECK( hxtAlignedFree(&(*mesh)->tetrahedra.flag) ); HXT_CHECK( hxtAlignedFree(&(*mesh)->tetrahedra.node) ); HXT_CHECK( hxtAlignedFree(&(*mesh)->tetrahedra.neigh) ); + HXT_CHECK( hxtAlignedFree(&(*mesh)->tetrahedra.neighType) ); // HXT_CHECK( hxtAlignedFree(&(*mesh)->tetrahedra.subdet) ); + + // hexahedra + HXT_CHECK( hxtAlignedFree(&(*mesh)->hexahedra.colors) ); + HXT_CHECK( hxtAlignedFree(&(*mesh)->hexahedra.flag) ); + HXT_CHECK( hxtAlignedFree(&(*mesh)->hexahedra.node) ); + HXT_CHECK( hxtAlignedFree(&(*mesh)->hexahedra.neigh) ); + HXT_CHECK( hxtAlignedFree(&(*mesh)->hexahedra.neighType) ); + + // prisms + HXT_CHECK( hxtAlignedFree(&(*mesh)->prisms.colors) ); + HXT_CHECK( hxtAlignedFree(&(*mesh)->prisms.flag) ); + HXT_CHECK( hxtAlignedFree(&(*mesh)->prisms.node) ); + HXT_CHECK( hxtAlignedFree(&(*mesh)->prisms.neigh) ); + HXT_CHECK( hxtAlignedFree(&(*mesh)->prisms.neighType) ); + + // pyramids + HXT_CHECK( hxtAlignedFree(&(*mesh)->pyramids.colors) ); + HXT_CHECK( hxtAlignedFree(&(*mesh)->pyramids.flag) ); + HXT_CHECK( hxtAlignedFree(&(*mesh)->pyramids.node) ); + HXT_CHECK( hxtAlignedFree(&(*mesh)->pyramids.neigh) ); + HXT_CHECK( hxtAlignedFree(&(*mesh)->pyramids.neighType) ); // triangles HXT_CHECK( hxtAlignedFree(&(*mesh)->triangles.node) ); HXT_CHECK( hxtAlignedFree(&(*mesh)->triangles.colors) ); + // quads + HXT_CHECK( hxtAlignedFree(&(*mesh)->quads.node) ); + HXT_CHECK( hxtAlignedFree(&(*mesh)->quads.colors) ); + // lines HXT_CHECK( hxtAlignedFree(&(*mesh)->lines.node) ); HXT_CHECK( hxtAlignedFree(&(*mesh)->lines.colors) ); - HXT_CHECK( hxtFree(&(*mesh)->typeStat) ); HXT_CHECK( hxtFree(mesh) ); return HXT_STATUS_OK; @@ -75,18 +139,20 @@ HXTStatus ReadNodesFromGmsh(FILE *fp, HXTMesh* m){ char buf[BUFSIZ]={""}; // scan for Nodes m->vertices.num = 0; - while( fgets(buf, BUFSIZ, fp )){ + while( fgets(buf, BUFSIZ, fp )!=NULL){ if(strstr(buf, "$Nodes")){ - fgets(buf, BUFSIZ, fp ); + if(fgets(buf, BUFSIZ, fp )==NULL) + return HXT_ERROR_MSG(HXT_STATUS_READ_ERROR, "Failed to read line"); m->vertices.num = atoi(buf); - HXT_CHECK( hxtAlignedMalloc(&m->vertices.coord, 4*m->vertices.num*sizeof( double )) ); + HXT_CHECK( hxtAlignedMalloc(&m->vertices.coord, sizeof( double )*4*m->vertices.num) ); if (m->vertices.coord == NULL)return HXT_ERROR(HXT_STATUS_OUT_OF_MEMORY); m->vertices.size = m->vertices.num; for(n=0;n<m->vertices.num;++n){ - fgets(buf, BUFSIZ, fp ); - sscanf(buf, "%*d %lf %lf %lf", &m->vertices.coord[4*n+0], &m->vertices.coord[4*n+1], &m->vertices.coord[4*n+2]); + if(fgets(buf, BUFSIZ, fp )==NULL) + return HXT_ERROR_MSG(HXT_STATUS_READ_ERROR, "Failed to read line"); + sscanf(buf, "%*d %lf %lf %lf", &m->vertices.coord[(size_t) 4*n+0], &m->vertices.coord[(size_t) 4*n+1], &m->vertices.coord[(size_t) 4*n+2]); } break; } @@ -107,23 +173,41 @@ HXTStatus ReadElementsFromGmsh(FILE *fp, HXTMesh* m){ m->lines.num = 0; m->triangles.num = 0; + m->quads.num = 0; m->tetrahedra.num = 0; + m->hexahedra.num = 0; + m->prisms.num = 0; + m->pyramids.num = 0; while(fgets(buf, BUFSIZ, fp )){ if(strstr(buf, "$Elements")){ - fgets(buf, BUFSIZ, fp ); + if(fgets(buf, BUFSIZ, fp )==NULL) + return HXT_ERROR_MSG(HXT_STATUS_READ_ERROR, "Failed to read line"); int tmpK = atoi(buf); for(k=0;k<tmpK;++k){ int etype = 0; - fgets(buf, BUFSIZ, fp ); + if(fgets(buf, BUFSIZ, fp )==NULL) + return HXT_ERROR_MSG(HXT_STATUS_READ_ERROR, "Failed to read line"); sscanf(buf, "%*d %d", &etype); if(etype==TETID){ // tets ++(m->tetrahedra.num); } + else if(etype==HEXID){ // hexahedra + ++(m->hexahedra.num); + } + else if(etype==PRIID){ // prisms + ++(m->prisms.num); + } + else if(etype==PYRID){ // pyramids + ++(m->pyramids.num); + } else if(etype==TRIID){ // triangles ++(m->triangles.num); } - else if(etype==LINEID){ // triangles + else if(etype==QUADID){ // quads + ++(m->quads.num); + } + else if(etype==LINEID){ // lines ++(m->lines.num); } else if(etype==POINTID){ // points @@ -145,30 +229,68 @@ HXTStatus ReadElementsFromGmsh(FILE *fp, HXTMesh* m){ if (m->tetrahedra.colors == NULL)return HXT_ERROR(HXT_STATUS_OUT_OF_MEMORY); m->tetrahedra.size = m->tetrahedra.num; } + if (m->hexahedra.num){ + HXT_CHECK( hxtAlignedMalloc(&m->hexahedra.node, (m->hexahedra.num)*8*sizeof(uint32_t)) ); + if (m->hexahedra.node == NULL)return HXT_ERROR(HXT_STATUS_OUT_OF_MEMORY); + HXT_CHECK( hxtAlignedMalloc(&m->hexahedra.colors, (m->hexahedra.num)*sizeof(uint16_t)) ); + if (m->hexahedra.colors == NULL)return HXT_ERROR(HXT_STATUS_OUT_OF_MEMORY); + m->hexahedra.size = m->hexahedra.num; + } + if (m->prisms.num){ + HXT_CHECK( hxtAlignedMalloc(&m->prisms.node, (m->prisms.num)*6*sizeof(uint32_t)) ); + if (m->prisms.node == NULL)return HXT_ERROR(HXT_STATUS_OUT_OF_MEMORY); + HXT_CHECK( hxtAlignedMalloc(&m->prisms.colors, (m->prisms.num)*sizeof(uint16_t)) ); + if (m->prisms.colors == NULL)return HXT_ERROR(HXT_STATUS_OUT_OF_MEMORY); + m->prisms.size = m->prisms.num; + } + if (m->pyramids.num){ + HXT_CHECK( hxtAlignedMalloc(&m->pyramids.node, (m->pyramids.num)*5*sizeof(uint32_t)) ); + if (m->pyramids.node == NULL)return HXT_ERROR(HXT_STATUS_OUT_OF_MEMORY); + HXT_CHECK( hxtAlignedMalloc(&m->pyramids.colors, (m->pyramids.num)*sizeof(uint16_t)) ); + if (m->pyramids.colors == NULL)return HXT_ERROR(HXT_STATUS_OUT_OF_MEMORY); + m->pyramids.size = m->pyramids.num; + } if (m->triangles.num){ HXT_CHECK( hxtAlignedMalloc(&m->triangles.node, (m->triangles.num)*3*sizeof(uint32_t)) ); if (m->triangles.node == NULL)return HXT_ERROR(HXT_STATUS_OUT_OF_MEMORY); HXT_CHECK( hxtAlignedMalloc(&m->triangles.colors, (m->triangles.num)*sizeof(uint16_t)) ); if (m->triangles.colors == NULL)return HXT_ERROR(HXT_STATUS_OUT_OF_MEMORY); + m->triangles.size = m->triangles.num; + } + if (m->quads.num){ + HXT_CHECK( hxtAlignedMalloc(&m->quads.node, (m->quads.num)*4*sizeof(uint32_t)) ); + if (m->quads.node == NULL)return HXT_ERROR(HXT_STATUS_OUT_OF_MEMORY); + HXT_CHECK( hxtAlignedMalloc(&m->quads.colors, (m->quads.num)*sizeof(uint16_t)) ); + if (m->quads.colors == NULL)return HXT_ERROR(HXT_STATUS_OUT_OF_MEMORY); + m->quads.size = m->quads.num; } if (m->lines.num){ HXT_CHECK( hxtAlignedMalloc(&m->lines.node, (m->lines.num)*2*sizeof(uint32_t)) ); if (m->lines.node == NULL)return HXT_ERROR(HXT_STATUS_OUT_OF_MEMORY); HXT_CHECK( hxtAlignedMalloc(&m->lines.colors, (m->lines.num)*sizeof(uint16_t)) ); if (m->lines.colors == NULL)return HXT_ERROR(HXT_STATUS_OUT_OF_MEMORY); + m->lines.size = m->lines.num; } while( fgets(buf, BUFSIZ, fp )){ if(strstr(buf, "$Elements")){ - fgets(buf, BUFSIZ, fp ); + if(fgets(buf, BUFSIZ, fp )==NULL) + return HXT_ERROR_MSG(HXT_STATUS_READ_ERROR, "Failed to read line"); int tmpK = atoi(buf); - m->tetrahedra.num=m->triangles.num=m->lines.num=0; + m->tetrahedra.num=0; + m->hexahedra.num=0; + m->prisms.num=0; + m->pyramids.num=0; + m->triangles.num=0; + m->quads.num=0; + m->lines.num=0; for(k=0;k<tmpK;++k){ int etype = 0, ntags; - fgets(buf, BUFSIZ, fp ); + if(fgets(buf, BUFSIZ, fp )==NULL) + return HXT_ERROR_MSG(HXT_STATUS_READ_ERROR, "Failed to read line"); sscanf(buf, "%*d %d %d", &etype, &ntags); if(etype==TETID){ // tets if(ntags==2){ // @@ -183,6 +305,52 @@ HXTStatus ReadElementsFromGmsh(FILE *fp, HXTMesh* m){ } ++m->tetrahedra.num; } + else if(etype==HEXID){ // hexahedra + if(ntags==2){ // + int a, b, c, d, e, f, g, h, color; + sscanf(buf, "%*d %*d %*d %*d %d %d %d %d %d %d %d %d %d", + &color,&a, &b, &c, &d, &e, &f, &g, &h); + m->hexahedra.node[8*m->hexahedra.num+0] = a-1; + m->hexahedra.node[8*m->hexahedra.num+1] = b-1; + m->hexahedra.node[8*m->hexahedra.num+2] = c-1; + m->hexahedra.node[8*m->hexahedra.num+3] = d-1; + m->hexahedra.node[8*m->hexahedra.num+4] = e-1; + m->hexahedra.node[8*m->hexahedra.num+5] = f-1; + m->hexahedra.node[8*m->hexahedra.num+6] = g-1; + m->hexahedra.node[8*m->hexahedra.num+7] = h-1; + m->hexahedra.colors[m->hexahedra.num] = color; + } + ++m->hexahedra.num; + } + else if(etype==PRIID){ // prisms + if(ntags==2){ // + int a, b, c, d, e, f, color; + sscanf(buf, "%*d %*d %*d %*d %d %d %d %d %d %d %d", + &color,&a, &b, &c, &d, &e, &f); + m->prisms.node[6*m->prisms.num+0] = a-1; + m->prisms.node[6*m->prisms.num+1] = b-1; + m->prisms.node[6*m->prisms.num+2] = c-1; + m->prisms.node[6*m->prisms.num+3] = d-1; + m->prisms.node[6*m->prisms.num+4] = e-1; + m->prisms.node[6*m->prisms.num+5] = f-1; + m->prisms.colors[m->prisms.num] = color; + } + ++m->prisms.num; + } + else if(etype==PYRID){ // pyramids + if(ntags==2){ // + int a, b, c, d, e, color; + sscanf(buf, "%*d %*d %*d %*d %d %d %d %d %d %d", + &color,&a, &b, &c, &d, &e); + m->pyramids.node[5*m->pyramids.num+0] = a-1; + m->pyramids.node[5*m->pyramids.num+1] = b-1; + m->pyramids.node[5*m->pyramids.num+2] = c-1; + m->pyramids.node[5*m->pyramids.num+3] = d-1; + m->pyramids.node[5*m->pyramids.num+4] = e-1; + m->pyramids.colors[m->pyramids.num] = color; + } + ++m->pyramids.num; + } else if(etype==TRIID){ // triangles if(ntags==2){ // int a, b, c, color; @@ -195,6 +363,19 @@ HXTStatus ReadElementsFromGmsh(FILE *fp, HXTMesh* m){ } ++m->triangles.num; } + else if(etype==QUADID){ // quads + if(ntags==2){ // + int a, b, c, d, color; + sscanf(buf, "%*d %*d %*d %*d %d %d %d %d %d", + &color,&a, &b, &c, &d); + m->quads.node[4*m->quads.num+0] = a-1; + m->quads.node[4*m->quads.num+1] = b-1; + m->quads.node[4*m->quads.num+2] = c-1; + m->quads.node[4*m->quads.num+3] = d-1; + m->quads.colors[m->quads.num] = color; + } + ++m->quads.num; + } else if(etype==POINTID){ // lines if(ntags==2){ // int a; @@ -219,24 +400,14 @@ HXTStatus ReadElementsFromGmsh(FILE *fp, HXTMesh* m){ } } - // LINEID 1 typeStat[1] = lines.num - // TRIID 2 typeStat[2] = triangles.num - // TETID 4 typeStat[4] = tetrahedra.num - // QUADID 3 typeStat[3] = quadangles.num - HXT_CHECK( hxtMalloc (&m->typeStat, 5*sizeof(uint64_t)) ); - if (m->typeStat == NULL)return HXT_ERROR(HXT_STATUS_OUT_OF_MEMORY); - m->typeStat[0] = 0; - m->typeStat[1] = m->lines.num; - m->typeStat[2] = m->triangles.num; - m->typeStat[4] = m->tetrahedra.num; - m->typeStat[3] = 0; - return HXT_STATUS_OK; } HXTStatus hxtMeshReadGmsh ( HXTMesh* m , const char *filename) { FILE *f = fopen (filename, "r"); - if (!f) return HXT_ERROR_MSG(HXT_STATUS_FILE_CANNOT_BE_OPENED, "mesh file '%s' cannot be opened",(filename==NULL)?"NULL":filename); + if (f==NULL) + return HXT_ERROR_MSG(HXT_STATUS_FILE_CANNOT_BE_OPENED, + "Cannot open mesh file \"%s\"",(filename==NULL)?"(null)":filename); HXT_CHECK( ReadNodesFromGmsh(f,m)); @@ -254,7 +425,8 @@ HXTStatus hxtMeshReadGmsh ( HXTMesh* m , const char *filename) { HXTStatus hxtMeshWriteGmsh ( HXTMesh* mesh , const char *filename) { FILE* file = fopen(filename,"w"); if(file==NULL) - HXT_ERROR_MSG(HXT_STATUS_FAILED, "Cannot open file %s",filename); + return HXT_ERROR_MSG(HXT_STATUS_FILE_CANNOT_BE_OPENED, + "Cannot open mesh file \"%s\"",(filename==NULL)?"(null)":filename); /* format for gmsh */ fprintf(file,"$MeshFormat\n" @@ -266,7 +438,7 @@ HXTStatus hxtMeshWriteGmsh ( HXTMesh* mesh , const char *filename) { { /* print the nodes */ uint32_t i; for (i=0; i<mesh->vertices.num; i++) - fprintf(file,"%u %.10E %.10E %.10E\n",i+1, mesh->vertices.coord[4*i+0], mesh->vertices.coord[4*i+1], mesh->vertices.coord[4*i+2]); + fprintf(file,"%u %.10E %.10E %.10E\n",i+1, mesh->vertices.coord[(size_t) 4*i+0], mesh->vertices.coord[(size_t) 4*i+1], mesh->vertices.coord[(size_t) 4*i+2]); } // count non-ghost vertex: @@ -285,36 +457,97 @@ HXTStatus hxtMeshWriteGmsh ( HXTMesh* mesh , const char *filename) { fprintf(file,"$EndNodes\n" "$Elements\n" - "%lu\n",index+mesh->triangles.num+mesh->lines.num); + "%lu\n", + index + + mesh->lines.num + + mesh->triangles.num + + mesh->quads.num + + mesh->hexahedra.num + + mesh->prisms.num + + mesh->pyramids.num + ); { /* print the elements */ index = 0; for (i=0; i<mesh->lines.num; i++){ uint16_t myColor = mesh->lines.colors ? mesh->lines.colors[i] : 0; - fprintf(file,"%llu %u 2 0 %u %u %u \n", ++index,LINEID, + fprintf(file,"%lu %u 2 0 %u %u %u\n", ++index,LINEID, myColor, mesh->lines.node[i*2]+1, mesh->lines.node[i*2 + 1]+1); } for (i=0; i<mesh->triangles.num; i++){ uint16_t myColor = mesh->triangles.colors ? mesh->triangles.colors[i] : 0; - fprintf(file,"%llu %u 2 0 %u %u %u %u \n", ++index,TRIID, + fprintf(file,"%lu %u 2 0 %u %u %u %u\n", ++index,TRIID, myColor, mesh->triangles.node[i*3]+1, mesh->triangles.node[i*3 + 1]+1, mesh->triangles.node[i*3 + 2]+1); } + for (i=0; i<mesh->quads.num; i++){ + uint16_t myColor = mesh->quads.colors ? mesh->quads.colors[i] : 0; + fprintf(file,"%lu %u 2 0 %u %u %u %u %u\n", ++index,QUADID, + myColor, + mesh->quads.node[i*4]+1, + mesh->quads.node[i*4 + 1]+1, + mesh->quads.node[i*4 + 2]+1, + mesh->quads.node[i*4 + 3]+1 + ); + } for (i=0; i<mesh->tetrahedra.num; i++){ if(mesh->tetrahedra.node[i*4 + 3]!=UINT32_MAX){ uint16_t myColor = mesh->tetrahedra.colors ? mesh->tetrahedra.colors[i] : 0; - if (myColor != UINT16_MAX) - fprintf(file,"%llu %u 2 0 %u %u %u %u %u\n", ++index,TETID, - myColor, - mesh->tetrahedra.node[i*4]+1, - mesh->tetrahedra.node[i*4 + 1]+1, - mesh->tetrahedra.node[i*4 + 2]+1, - mesh->tetrahedra.node[i*4 + 3]+1); + if (myColor != UINT16_MAX) + fprintf(file,"%lu %u 2 0 %u %u %u %u %u\n", ++index,TETID, + myColor, + mesh->tetrahedra.node[i*4]+1, + mesh->tetrahedra.node[i*4 + 1]+1, + mesh->tetrahedra.node[i*4 + 2]+1, + mesh->tetrahedra.node[i*4 + 3]+1); + } + } + for (i=0; i<mesh->hexahedra.num; i++){ + if(mesh->hexahedra.node[i*8 + 7]!=UINT32_MAX){ + uint16_t myColor = mesh->hexahedra.colors ? mesh->hexahedra.colors[i] : 0; + if (myColor != UINT16_MAX) + fprintf(file,"%lu %u 2 0 %u %u %u %u %u %u %u %u %u\n", ++index,HEXID, + myColor, + mesh->hexahedra.node[i*8]+1, + mesh->hexahedra.node[i*8 + 1]+1, + mesh->hexahedra.node[i*8 + 2]+1, + mesh->hexahedra.node[i*8 + 3]+1, + mesh->hexahedra.node[i*8 + 4]+1, + mesh->hexahedra.node[i*8 + 5]+1, + mesh->hexahedra.node[i*8 + 6]+1, + mesh->hexahedra.node[i*8 + 7]+1); + } + } + for (i=0; i<mesh->prisms.num; i++){ + if(mesh->prisms.node[i*6 + 5]!=UINT32_MAX){ + uint16_t myColor = mesh->prisms.colors ? mesh->prisms.colors[i] : 0; + if (myColor != UINT16_MAX) + fprintf(file,"%lu %u 2 0 %u %u %u %u %u %u %u\n", ++index,PRIID, + myColor, + mesh->prisms.node[i*6]+1, + mesh->prisms.node[i*6 + 1]+1, + mesh->prisms.node[i*6 + 2]+1, + mesh->prisms.node[i*6 + 3]+1, + mesh->prisms.node[i*6 + 4]+1, + mesh->prisms.node[i*6 + 5]+1); + } + } + for (i=0; i<mesh->pyramids.num; i++){ + if(mesh->pyramids.node[i*5 + 4]!=UINT32_MAX){ + uint16_t myColor = mesh->pyramids.colors ? mesh->pyramids.colors[i] : 0; + if (myColor != UINT16_MAX) + fprintf(file,"%lu %u 2 0 %u %u %u %u %u %u\n", ++index,PYRID, + myColor, + mesh->pyramids.node[i*5]+1, + mesh->pyramids.node[i*5 + 1]+1, + mesh->pyramids.node[i*5 + 2]+1, + mesh->pyramids.node[i*5 + 3]+1, + mesh->pyramids.node[i*5 + 4]+1); } } } diff --git a/contrib/hxt/hxt_mesh.h b/contrib/hxt/hxt_mesh.h index 6dfbc674f35525a828186127ea82a1b104670053..1ae907f3c9173841adc5982d201a7f3948c59c43 100644 --- a/contrib/hxt/hxt_mesh.h +++ b/contrib/hxt/hxt_mesh.h @@ -4,13 +4,22 @@ #include "hxt_tools.h" // to have SIMD_ALIGN and stdint.h - - #define HXT_GHOST_VERTEX UINT32_MAX #define HXT_DELETED_COLOR (UINT16_MAX-1) #define HXT_NO_ADJACENT UINT64_MAX +/* Element types, same as Gmsh */ +typedef enum { + HXT_NO_ELT = 0, + HXT_LINE = 1, + HXT_TRI = 2, + HXT_QUAD = 3, + HXT_TET = 4, + HXT_HEX = 5, + HXT_PRI = 6, + HXT_PYR = 7 +} HXT_ELT_TYPE; struct hxtMeshStruct { HXTContext* ctx; @@ -21,25 +30,68 @@ struct hxtMeshStruct { uint32_t size; double* coord; // 3 coordinates + 1 double per vertex! } vertices; - uint64_t* typeStat; // to delete // tetrahedra struct { uint32_t* node; // aligned (size = tetrahedra.size*4*sizeof(uint32_t)) uint64_t* neigh; // aligned (size = tetrahedra.size*4*sizeof(uint64_t)) + char* neighType; uint16_t* colors; + uint16_t* flag; uint64_t num; // number of tetrahedra uint64_t size; // reserved number of tetrahedra (size of the vector) } tetrahedra; + + // hexahedra + struct { + uint32_t* node; // aligned (size = hexahedra.size*8*sizeof(uint32_t)) + uint64_t* neigh; // aligned (size = hexahedra.size*6*sizeof(uint64_t)) + char* neighType; + uint16_t* colors; + uint16_t* flag; + uint64_t num; // number of tetrahedra + uint64_t size; // reserved number of hexahedra (size of the vector) + } hexahedra; + + // prisms + struct { + uint32_t* node; // aligned (size = prisms.size*6*sizeof(uint32_t)) + uint64_t* neigh; // aligned (size = prisms.size*5*sizeof(uint64_t)) + char* neighType; + uint16_t* colors; + uint16_t* flag; + uint64_t num; // number of tetrahedra + uint64_t size; // reserved number of prisms (size of the vector) + } prisms; + + // pyramids + struct { + uint32_t* node; // aligned (size = pyramids.size*5*sizeof(uint32_t)) + uint64_t* neigh; // aligned (size = pyramids.size*5*sizeof(uint64_t)) + char* neighType; + uint16_t* colors; + uint16_t* flag; + uint64_t num; // number of tetrahedra + uint64_t size; // reserved number of pyramids (size of the vector) + } pyramids; // triangles // TODO: consider writing a array of structure... struct { uint32_t* node; + uint64_t* neigh; uint16_t* colors; uint64_t num; uint64_t size; } triangles; + // quads + struct { + uint32_t* node; + uint16_t* colors; + uint64_t num; + uint64_t size; + } quads; + // lines // TODO: consider writing a array of structure... struct { uint32_t* node; diff --git a/contrib/hxt/hxt_mesh3d.c b/contrib/hxt/hxt_mesh3d.c index ebb6184f91dfbb3d7146dd88c6099fd6bd55becf..2d8a8c0f336c8d3a342eb6b2f339ec98ee50d369 100644 --- a/contrib/hxt/hxt_mesh3d.c +++ b/contrib/hxt/hxt_mesh3d.c @@ -1,167 +1,228 @@ -#include <math.h> -#include <time.h> -#include "hxt_bbox.h" -#include "hxt_tools.h" -#include "hxt_mesh_size.h" +// #include "hxt_mesh_size.h" #include "hxt_tetrahedra.h" -#include "hxt_vertices.h" +// #include "hxt_vertices.h" #include "hxt_mesh3d.h" #include "predicates.h" +#include "hxt_tetFlag.h" -#ifdef HXT_MICROSOFT -#define _CRT_RAND_S -#include <stdlib.h> -double drand48() { - double a; - rand_s(&a); - return a; -} -#endif +// #if defined(_MSC_VER) +// #define _CRT_RAND_S +// #include <stdlib.h> +// double drand48() { +// double a; +// rand_s(&a); +// return a; +// } +// #endif -static inline void sort3ints(uint32_t *i){ - uint32_t no1 = i[0]; - uint32_t no2 = i[1]; - uint32_t no3 = i[2]; - - if (no1>no2) { - i[1]=no1; - i[0]=no2; - } else { - i[1]=no2; - i[0]=no1; - } - if (i[1]>no3) { - i[2]=i[1]; - if(i[0]>no3){ - i[1]=i[0]; - i[0]=no3; - }else { - i[1]=no3; - } - }else i[2]=no3; - //printf("%u %u %u\n",i[0],i[1],i[2]); -} -static inline int facecmp(const void *p0, const void *p1) -{ - uint32_t *f0 = (uint32_t*)p0; - uint32_t *f1 = (uint32_t*)p1; - - if (f0[0] != f1[0]) return f0[0] - f1[0]; - if (f0[1] != f1[1]) return f0[1] - f1[1]; - return f0[2] - f1[2]; -} +HXTStatus hxtComputeMeshSizeFromTrianglesAndLines(HXTMesh* mesh, HXTDelaunayOptions* delOptions) { -HXTStatus hxtCreateFaceSearchStructure(HXTMesh* mesh, uint32_t **pfaces){ - uint32_t *tfaces; - - HXT_CHECK(hxtMalloc(&tfaces,3*mesh->triangles.num*sizeof(uint32_t))); - memcpy (tfaces, mesh->triangles.node, 3*mesh->triangles.num*sizeof(uint32_t)); + HXT_CHECK(hxtAlignedMalloc(&delOptions->nodalSizes,mesh->vertices.num*sizeof(double))); -#pragma omp parallel for - for (uint32_t i = 0; i<mesh->triangles.num; i++)sort3ints(&tfaces[3*i]); - - // CELESTIN FIXME : SHOULD USE YOUR PARALLEL SORT - qsort(tfaces,mesh->triangles.num,3*sizeof(uint32_t),facecmp); - *pfaces = tfaces; - return HXT_STATUS_OK; -} - -HXTStatus hxtComputeMeshSizeFromMesh (HXTMesh* mesh, double **psizes){ + #pragma omp parallel for + for (uint32_t i = 0; i<mesh->vertices.num; i++){ + delOptions->nodalSizes[i] = DBL_MAX; + } - double *sizes; - HXT_CHECK(hxtMalloc(&sizes,mesh->vertices.num*sizeof(double))); - -#pragma omp parallel for - for (uint32_t i = 0; i<mesh->tetrahedra.num; i++){ - for (uint32_t j = 0; j<4; j++){ - uint32_t n1 = mesh->tetrahedra.node[4*i+j]; - if (n1 != HXT_GHOST_VERTEX) - sizes[n1] = 1.e22; + // only do for triangles + // we do not take into account hereafter delOptions->nodalSizes = to DBL_MAX + // could be changed in another fashion + for (uint32_t i = 0; i<mesh->triangles.num; i++){ + for (uint32_t j = 0; j<3; j++){ + for (uint32_t k = j+1; k<3; k++){ + uint32_t n1 = mesh->triangles.node[3*i+j]; + uint32_t n2 = mesh->triangles.node[3*i+k]; + if (n1 != HXT_GHOST_VERTEX && n2 != HXT_GHOST_VERTEX){ + double *X1 = mesh->vertices.coord + (size_t) 4*n1; + double *X2 = mesh->vertices.coord + (size_t) 4*n2; + double l = sqrt ((X1[0]-X2[0])*(X1[0]-X2[0])+ + (X1[1]-X2[1])*(X1[1]-X2[1])+ + (X1[2]-X2[2])*(X1[2]-X2[2])); + if(l<delOptions->nodalSizes[n1]) delOptions->nodalSizes[n1] = l; + if(l<delOptions->nodalSizes[n2]) delOptions->nodalSizes[n2] = l; + } + } } } - -#pragma omp parallel for - for (uint32_t i = 0; i<mesh->tetrahedra.num; i++){ - for (uint32_t j = 0; j<4; j++){ - uint32_t n1 = mesh->tetrahedra.node[4*i+(j+0)%4]; - uint32_t n2 = mesh->tetrahedra.node[4*i+(j+1)%4]; + for (uint32_t i = 0; i<mesh->lines.num; i++){ + uint32_t n1 = mesh->lines.node[2*i+0]; + uint32_t n2 = mesh->lines.node[2*i+1]; if (n1 != HXT_GHOST_VERTEX && n2 != HXT_GHOST_VERTEX){ - double *X1 = mesh->vertices.coord + 4*n1; - double *X2 = mesh->vertices.coord + 4*n2; + double *X1 = mesh->vertices.coord + (size_t) 4*n1; + double *X2 = mesh->vertices.coord + (size_t) 4*n2; double l = sqrt ((X1[0]-X2[0])*(X1[0]-X2[0])+ (X1[1]-X2[1])*(X1[1]-X2[1])+ (X1[2]-X2[2])*(X1[2]-X2[2])); - sizes [n1] = l < sizes [n1] ? l : sizes [n1]; - sizes [n2] = l < sizes [n2] ? l : sizes [n2]; + if(l<delOptions->nodalSizes[n1]) delOptions->nodalSizes[n1] = l; + if(l<delOptions->nodalSizes[n2]) delOptions->nodalSizes[n2] = l; + } + } + return HXT_STATUS_OK; +} + +HXTStatus hxtComputeMeshSizeFromMesh (HXTMesh* mesh, HXTDelaunayOptions* delOptions) { + + HXT_CHECK(hxtAlignedMalloc(&delOptions->nodalSizes,mesh->vertices.num*sizeof(double))); + + #pragma omp parallel for + for (uint32_t i = 0; i<mesh->vertices.num; i++){ + delOptions->nodalSizes[i] = DBL_MAX; + } + + // only do for triangles + // we do not take into account hereafter delOptions->nodalSizes = to DBL_MAX + // could be changed in another fashion + for (uint32_t i = 0; i<mesh->tetrahedra.num; i++){ + for (uint32_t j = 0; j<4; j++){ + for (uint32_t k = j+1; k<4; k++){ + uint32_t n1 = mesh->tetrahedra.node[4*i+j]; + uint32_t n2 = mesh->tetrahedra.node[4*i+k]; + if (n1 != HXT_GHOST_VERTEX && n2 != HXT_GHOST_VERTEX){ + double *X1 = mesh->vertices.coord + (size_t) 4*n1; + double *X2 = mesh->vertices.coord + (size_t) 4*n2; + double l = sqrt ((X1[0]-X2[0])*(X1[0]-X2[0])+ + (X1[1]-X2[1])*(X1[1]-X2[1])+ + (X1[2]-X2[2])*(X1[2]-X2[2])); + if(l<delOptions->nodalSizes[n1]) delOptions->nodalSizes[n1] = l; + if(l<delOptions->nodalSizes[n2]) delOptions->nodalSizes[n2] = l; + } } } } - *psizes = sizes; return HXT_STATUS_OK; } -// TODO: use a sort on the index only and then put everything into order... -HXTStatus hxtEmptyMesh(HXTMesh* mesh){ + + + +HXTStatus hxtEmptyMesh(HXTMesh* mesh, HXTDelaunayOptions* delOptions){ // we assume that the input is a surface mesh if (mesh->tetrahedra.num) return HXT_ERROR_MSG(HXT_STATUS_FAILED, "The input mesh should only contain triangles"); if (mesh->triangles.num == 0) return HXT_ERROR_MSG(HXT_STATUS_FAILED, "The input mesh should contain triangles"); - hxtNodeInfo* nodeInfo; + double minDist2 = DBL_MAX; + #pragma omp parallel for reduction(min:minDist2) + for (uint64_t i=0; i<mesh->triangles.num; i++){ + uint32_t* node = mesh->triangles.node + 3*i; + for (int j=0; j<3; j++) { + double* n1 = mesh->vertices.coord + (size_t) 4*node[j]; + double* n2 = mesh->vertices.coord + (size_t) 4*node[(j+1)%3]; + + double dist2 = (n1[0]-n2[0])*(n1[0]-n2[0]) + + (n1[1]-n2[1])*(n1[1]-n2[1]) + + (n1[2]-n2[2])*(n1[2]-n2[2]); + + if(dist2<minDist2) + minDist2 = dist2; + } + } + double minSize = sqrt(minDist2); + + hxtNodeInfo* nodeInfo; HXT_CHECK( hxtAlignedMalloc(&nodeInfo, sizeof(hxtNodeInfo)*mesh->vertices.num) ); #pragma omp parallel for simd aligned(nodeInfo:SIMD_ALIGN) for (uint32_t i=0; i<mesh->vertices.num; i++) { nodeInfo[i].node = i; + nodeInfo[i].status = HXT_STATUS_TRYAGAIN; } - HXT_CHECK( hxtDelaunaySteadyVertices(mesh, NULL, nodeInfo, mesh->vertices.num) ); + delOptions->minSizeStart = 0.0; + delOptions->minSizeEnd = minSize; + HXT_CHECK( hxtDelaunaySteadyVertices(mesh, delOptions, nodeInfo, mesh->vertices.num) ); + delOptions->minSizeStart = minSize; + delOptions->minSizeEnd = minSize; + delOptions->numVerticesInMesh = mesh->vertices.num; + +#ifdef DEBUG + #pragma omp parallel for simd aligned(nodeInfo:SIMD_ALIGN) + for (uint32_t i=0; i<mesh->vertices.num; i++) { + if(nodeInfo[i].status!=HXT_STATUS_TRUE){ + HXT_WARNING("vertex %u of the empty mesh was not inserted\n", nodeInfo[i].node); + } + } +#endif HXT_CHECK( hxtAlignedFree(&nodeInfo) ); return HXT_STATUS_OK; - } -HXTStatus hxtVerifyBoundary(HXTMesh* mesh, uint32_t *missing) { - uint32_t ft [4][3] = {{0,1,2},{0,1,3},{0,2,3},{1,2,3}}; - - uint16_t *flags; - HXT_CHECK(hxtMalloc(&flags,mesh->triangles.num*sizeof(uint16_t))); -#pragma omp parallel for - for (uint32_t i = 0; i<mesh->triangles.num; i++)flags[i] = 0; - uint32_t *tfaces; - HXT_CHECK(hxtCreateFaceSearchStructure(mesh, &tfaces)); - -#pragma omp parallel for - for (uint64_t i=0; i<mesh->tetrahedra.num; i++){ - uint32_t *v = &mesh->tetrahedra.node[4*i]; - for (uint32_t j=0; j<4;j++){ - uint32_t facet[3] = {v[ft[j][0]],v[ft[j][1]],v[ft[j][2]]}; - sort3ints (facet); - uint32_t* found = (uint32_t*)bsearch (facet, tfaces, mesh->triangles.num, 3*sizeof(uint32_t), facecmp); - if (found) { - uint32_t facetId = (found - tfaces)/3; - flags[facetId] = 1; + +/*************************************************** + * Coloring the mesh * + ***************************************************/ +HXTStatus hxtColorMesh(HXTMesh* mesh, uint16_t *nbColors) { + uint64_t *stack; + HXT_CHECK(hxtMalloc(&stack,mesh->tetrahedra.num*sizeof(uint64_t))); + // now that tetrahedra are flaged, we can proceed to colorize the mesh + memset(mesh->tetrahedra.colors, 0, mesh->tetrahedra.size*sizeof(uint16_t)); + + uint16_t color = 1; + uint16_t colorOut = 0; + + while (1){ + uint64_t stackSize = 0; + uint64_t first = UINT64_MAX; + + for (uint64_t i=0; i<mesh->tetrahedra.num; i++) { + if(mesh->tetrahedra.colors[i]==0){ + first = i; + break; } } + + if(first==UINT64_MAX) + break; + + stack[stackSize++] = first; + mesh->tetrahedra.colors[first] = color; + + for (uint64_t i=0; i<stackSize; i++) { + uint64_t t = stack[i]; + + if (mesh->tetrahedra.node[4*t+3] == HXT_GHOST_VERTEX) + colorOut = color; + + for (unsigned j=0; j<4; j++) { + if(mesh->tetrahedra.neigh[4*t+j]!=HXT_NO_ADJACENT && isFacetConstrained(mesh, 4*t+j)==0){ // the facet is not a boundary facet + uint64_t neigh = mesh->tetrahedra.neigh[4*t+j]/4; + if(mesh->tetrahedra.colors[neigh]==0){ + stack[stackSize++] = neigh; + mesh->tetrahedra.colors[neigh] = color; + } + } + } + } + color++; + } + *nbColors = color-1; // -1 because we began at one AND the colorout is counted... + + HXT_CHECK( hxtFree(&stack) ); + + #pragma omp parallel for + for (int i=0;i<mesh->tetrahedra.num;i++){ + if (mesh->tetrahedra.colors[i] == colorOut){ + mesh->tetrahedra.colors[i] = UINT16_MAX; + } + else if(mesh->tetrahedra.colors[i] > colorOut){ + mesh->tetrahedra.colors[i]--; + } } - *missing=0; - for (uint32_t i = 0; i<mesh->triangles.num; i++)*missing += 1 - flags[i]; - HXT_CHECK(hxtFree(&tfaces)); - HXT_CHECK(hxtFree(&flags)); return HXT_STATUS_OK; } + // refine @@ -243,143 +304,139 @@ double hxtTetCircumcenter(double a[3], double b[3], double c[3], double d[3], } -/// Starts from an empty 3D mesh and color the different regions -/// assume that neighbors are computed -HXTStatus hxtColorMesh(HXTMesh* mesh, uint32_t *nbColors) { // TODO: why is nbColors unused ?? - // compute the face search structure - uint32_t *tfaces; - HXT_CHECK(hxtCreateFaceSearchStructure(mesh, &tfaces)); - - // TODO: check this out -#pragma omp parallel for - for (uint64_t i = 0; i<mesh->tetrahedra.num; i++) - mesh->tetrahedra.colors[i] = 0 ;//mesh->tetrahedra.node[4*i+3]!=HXT_GHOST_VERTEX ? 0 : UINT16_MAX; - - uint16_t color = 1; - uint16_t colorOut = 0; +HXTStatus hxtRefineTetrahedraOneStep(HXTMesh* mesh, HXTDelaunayOptions* delOptions, HXTMeshSize* sizeField, int *nbAdd, int iter) +{ + double *newVertices; + uint32_t *numCreated; + int maxThreads = omp_get_max_threads(); + HXT_CHECK( hxtAlignedMalloc(&newVertices, sizeof(double)*4*mesh->tetrahedra.num) ); + HXT_CHECK( hxtMalloc(&numCreated, maxThreads*sizeof(uint32_t)) ); - uint64_t *stack; - HXT_CHECK(hxtMalloc(&stack,mesh->tetrahedra.num*sizeof(uint64_t))); - while (1){ - uint64_t stackSize = 0; - uint64_t first; + + // TODO: creating multiple vertices per tetrahedron + uint32_t add = 0; + HXTStatus status = HXT_STATUS_OK; + #pragma omp parallel reduction(+:add) + { + int threadID = omp_get_thread_num(); + uint32_t localAdd = 0; - for ( first = 0; first<mesh->tetrahedra.num; first++)if (mesh->tetrahedra.colors[first] == 0) break; - if (first == mesh->tetrahedra.num) break; - stack[stackSize++] = first; - int count = 1; - while (stackSize){ - stackSize --; - uint64_t t = stack[stackSize]; - mesh->tetrahedra.colors[t] = color; - if (mesh->tetrahedra.node[4*t+3] == HXT_GHOST_VERTEX) - colorOut = color; - for (uint16_t i = 0; i< 4;i++){ - uint64_t neigh = mesh->tetrahedra.neigh[4*t+i]/4; - if (!mesh->tetrahedra.colors[neigh]){ - uint32_t* const Node = mesh->tetrahedra.node + 4*t; - uint32_t facet [3] = {Node[(i+1)%4], Node[(i+2)%4], Node[(i+3)%4]}; - sort3ints(facet); - uint32_t* found = (uint32_t*)bsearch (facet, tfaces, mesh->triangles.num, 3*sizeof(uint32_t), facecmp); - if (!found){ - stack[stackSize++] = neigh; - count ++; + #pragma omp for schedule(static) + for (uint64_t i=0; i<mesh->tetrahedra.num; i++) + { + newVertices[(size_t) 4*i+3] = -1.0; + if (mesh->tetrahedra.colors[i] != UINT16_MAX && isTetProcessed(mesh, i)==0){ + double *a = mesh->vertices.coord + (size_t) 4*mesh->tetrahedra.node[4*i+0]; + double *b = mesh->vertices.coord + (size_t) 4*mesh->tetrahedra.node[4*i+1]; + double *c = mesh->vertices.coord + (size_t) 4*mesh->tetrahedra.node[4*i+2]; + double *d = mesh->vertices.coord + (size_t) 4*mesh->tetrahedra.node[4*i+3]; + double circumcenter [3]; + double u,v,w; + hxtTetCircumcenter(a,b,c,d, circumcenter, &u, &v, &w); + double circumradius2 = (a[0]-circumcenter[0])*(a[0]-circumcenter[0])+ + (a[1]-circumcenter[1])*(a[1]-circumcenter[1])+ + (a[2]-circumcenter[2])*(a[2]-circumcenter[2]); + // all new edges will have a length equal to circumradius2 + double meshSize; + // HXTStatus status = hxtMeshSizeEvaluate ( sizeField, circumcenter, &meshSize); + + double SIZES[4]; + double AVG = 0; + int NN = 0; + for (int j=0;j<4;j++){ + SIZES[j] = delOptions->nodalSizes[mesh->tetrahedra.node[4*i+j]]; + if (SIZES[j] != DBL_MAX){ + NN++; + AVG += SIZES[j]; + } + } + if (NN != 4){ + AVG /= NN; + for (int j=0;j<4;j++){ + if (SIZES[j] == DBL_MAX){ + // delOptions->nodalSizes[mesh->tetrahedra.node[4*i+j]] = AVG; + SIZES[j] = AVG; + } } } + + meshSize = SIZES[0] * (1-u-v-w) + SIZES[1] * u + SIZES[2] * v + SIZES[3] * w; + + if (u > 0 && v > 0 && w > 0 && 1.-u-v-w > 0 && meshSize * meshSize * .49 < circumradius2) { + // printf("%llu %g\n",i,sqrt(circumradius2),meshSize); + newVertices[(size_t) 4*i ] = circumcenter[0]; + newVertices[(size_t) 4*i+1] = circumcenter[1]; + newVertices[(size_t) 4*i+2] = circumcenter[2]; + newVertices[(size_t) 4*i+3] = meshSize; + localAdd++; + } + + markTetAsProcessed(mesh, i); // we do not need to refine that tetrahedra anymore } } - color++; - } - for (int i=0;i<mesh->tetrahedra.num;i++)if (mesh->tetrahedra.colors[i] == colorOut) mesh->tetrahedra.colors[i] = UINT16_MAX; - HXT_CHECK(hxtFree(&stack)); - HXT_CHECK(hxtFree(&tfaces)); - return HXT_STATUS_OK; -} - -HXTStatus hxtRefineTetrahedraOneStep(HXTMesh* mesh, HXTMeshSize* sizeField, int *nbAdd, double **nodalSizes, int iter) { + numCreated[threadID] = localAdd; + + #pragma omp barrier + #pragma omp single + { + int nthreads = omp_get_num_threads(); + add = 0; + for (int i=0; i<nthreads; i++) { + uint32_t tsum = add + numCreated[i]; + numCreated[i] = add; + add = tsum; + } - double *newSizes; - double *newVertices; - HXT_CHECK(hxtMalloc(&newVertices, 4*mesh->tetrahedra.num*sizeof(double))); - HXT_CHECK(hxtMalloc(&newSizes,mesh->tetrahedra.num*sizeof(double))); -#pragma omp parallel for - for (uint64_t i=0; i<mesh->tetrahedra.num; i++) - newVertices[4*i+3] = 0.0; - - // TODO: not creating point stupidly - uint32_t add = 0; -#pragma omp parallel for reduction (+:add) - for (uint64_t i=0; i<mesh->tetrahedra.num; i++){ - uint16_t myColor = mesh->tetrahedra.colors[i]; - if (myColor != UINT16_MAX){ - double *a = mesh->vertices.coord + 4*mesh->tetrahedra.node[4*i+0]; - double *b = mesh->vertices.coord + 4*mesh->tetrahedra.node[4*i+1]; - double *c = mesh->vertices.coord + 4*mesh->tetrahedra.node[4*i+2]; - double *d = mesh->vertices.coord + 4*mesh->tetrahedra.node[4*i+3]; - double circumcenter [3]; - double u,v,w; - hxtTetCircumcenter(a,b,c,d, circumcenter, &u, &v, &w); - double circumradius = sqrt((a[0]-circumcenter[0])*(a[0]-circumcenter[0])+ - (a[1]-circumcenter[1])*(a[1]-circumcenter[1])+ - (a[2]-circumcenter[2])*(a[2]-circumcenter[2])); - // all new edges will have a length equal to circumradius - double meshSize; - // HXTStatus status = hxtMeshSizeEvaluate ( sizeField, circumcenter, &meshSize); - meshSize = - (*nodalSizes)[mesh->tetrahedra.node[4*i+0]] * (1-u-v-w) + - (*nodalSizes)[mesh->tetrahedra.node[4*i+1]] * u + - (*nodalSizes)[mesh->tetrahedra.node[4*i+2]] * v + - (*nodalSizes)[mesh->tetrahedra.node[4*i+3]] * w; - // printf("%g %g %g %g\n",u,v,w,meshSize); - // if mesh size is significantly smaller than circumradius - if (u > 0 && v > 0 && w > 0 && 1.-u-v-w > 0 && meshSize *.8 < circumradius) { - // printf("%llu %g\n",i,circumradius); - newVertices[4*i ] = circumcenter[0]; - newVertices[4*i+1] = circumcenter[1]; - newVertices[4*i+2] = circumcenter[2]; - newVertices[4*i+3] = 1.0; - newSizes[i] = meshSize; - add++; + if(mesh->vertices.num + add>mesh->vertices.size){ + status=hxtAlignedRealloc(&mesh->vertices.coord, sizeof(double)*4*(mesh->vertices.num + add)); + if(status==HXT_STATUS_OK){ + status=hxtAlignedRealloc(&delOptions->nodalSizes, (mesh->vertices.num + add)*sizeof(double)); + mesh->vertices.size = mesh->vertices.num + add; + } } } - } - if (mesh->vertices.num + add >= mesh->vertices.size){ - HXT_CHECK(hxtRealloc(&mesh->vertices.coord,4*(mesh->vertices.num + add)*sizeof(double))); - HXT_CHECK(hxtRealloc(nodalSizes,(mesh->vertices.num + add)*sizeof(double))); - mesh->vertices.size = mesh->vertices.num + add; + localAdd = numCreated[threadID] + mesh->vertices.num; + + if(status==HXT_STATUS_OK){ + #pragma omp for schedule(static) + for (uint64_t i=0; i<mesh->tetrahedra.num; i++){ + if ( newVertices [4*i+3]!=-1.0 ) { + mesh->vertices.coord[ (size_t) 4*localAdd ] = newVertices [(size_t) 4*i ]; + mesh->vertices.coord[ (size_t) 4*localAdd+1 ] = newVertices [(size_t) 4*i+1]; + mesh->vertices.coord[ (size_t) 4*localAdd+2 ] = newVertices [(size_t) 4*i+2]; + delOptions->nodalSizes[localAdd] = newVertices [4*i+3]; + localAdd++; + } + } + } } - add = 0; -// #pragma omp parallel for - for (uint64_t i=0; i<mesh->tetrahedra.num; i++){ - if ( newVertices [4*i+3]!=0.0 ) { - mesh->vertices.coord[ 4*mesh->vertices.num ] = newVertices [4*i ]; - mesh->vertices.coord[ 4*mesh->vertices.num+1 ] = newVertices [4*i+1]; - mesh->vertices.coord[ 4*mesh->vertices.num+2 ] = newVertices [4*i+2]; - (*nodalSizes) [ mesh->vertices.num ] = newSizes[i]; - mesh->vertices.num++; - add++; - } + if(status!=HXT_STATUS_OK){ + HXT_TRACE(status); + return status; } + + mesh->vertices.num += add; + + + HXT_CHECK( hxtFree(&numCreated) ); + HXT_CHECK(hxtAlignedFree(&newVertices)); - HXT_CHECK(hxtDelaunay(mesh, NULL)); + HXT_CHECK(hxtDelaunay(mesh, delOptions)); - HXT_CHECK(hxtFree(&newVertices)); - HXT_CHECK(hxtFree(&newSizes)); - *nbAdd = add; + *nbAdd = mesh->vertices.num - delOptions->numVerticesInMesh; + delOptions->numVerticesInMesh = mesh->vertices.num; return HXT_STATUS_OK; } -HXTStatus hxtRefineTetrahedra(HXTMesh* mesh, HXTMeshSize* sizeField, double **nodalSizes) { +HXTStatus hxtRefineTetrahedra(HXTMesh* mesh, HXTDelaunayOptions* delOptions, HXTMeshSize* sizeField) { int iter = 0; - while(iter++ < 20){ + while(iter++ < 40){ int nbAdd=0; - clock_t t1 = clock(); - HXT_CHECK(hxtRefineTetrahedraOneStep(mesh, sizeField, &nbAdd, nodalSizes, iter)); - HXT_INFO("ITERATION %3d -- %3f seconds\n",iter,(double)(clock()-t1)/CLOCKS_PER_SEC); + HXT_CHECK(hxtRefineTetrahedraOneStep(mesh, delOptions, sizeField, &nbAdd, iter)); // uint32_t nb; // HXT_CHECK(hxtColorMesh(mesh,&nb)); if (nbAdd == 0) break; diff --git a/contrib/hxt/hxt_mesh3d.h b/contrib/hxt/hxt_mesh3d.h index a795d091cb6d9a3e76450a3a3c5646945f343c0f..8b67844d75ad8faef1ffc73717da4529d1f37824 100644 --- a/contrib/hxt/hxt_mesh3d.h +++ b/contrib/hxt/hxt_mesh3d.h @@ -1,21 +1,20 @@ #ifndef _HXT_MESH_3D_ #define _HXT_MESH_3D_ -#include <hxt_tools.h> +#include "hxt_tetrahedra.h" /// Creates a structure that allows to look over triangular faces of the 2D mesh HXTStatus hxtCreateFaceSearchStructure(HXTMesh* mesh, uint32_t **pfaces); //// creates a mesh with all points of the surface mesh -HXTStatus hxtEmptyMesh(HXTMesh* mesh); +HXTStatus hxtEmptyMesh(HXTMesh* mesh, HXTDelaunayOptions* delOptions); /// Compute sizes at vertices of the mesh from existing edges of the tetrahera -HXTStatus hxtComputeMeshSizeFromMesh (HXTMesh* mesh, double **sizes); -/// Verify if all facets are present in a tetrahedrization, missing is the number of missing facets -HXTStatus hxtVerifyBoundary(HXTMesh* mesh, uint32_t *missing); +HXTStatus hxtComputeMeshSizeFromTrianglesAndLines (HXTMesh* mesh, HXTDelaunayOptions* delOptions); +HXTStatus hxtComputeMeshSizeFromMesh (HXTMesh* mesh, HXTDelaunayOptions* delOptions); +/// Gives a unique color to each enclosed volume +HXTStatus hxtColorMesh(HXTMesh* mesh, uint16_t *nbColors); /// Recover the boundary HXTStatus hxtRecoverBoundary(HXTMesh* mesh); -/// Starts from an empty 3D mesh and color the different regions -HXTStatus hxtColorMesh(HXTMesh* mesh, uint32_t *nbColors); /// Add points at tets circumcenter in order to fullfill a mesh size constraint -HXTStatus hxtRefineTetrahedra(HXTMesh* mesh, HXTMeshSize* meshsize, double **nodalSizes); +HXTStatus hxtRefineTetrahedra(HXTMesh* mesh, HXTDelaunayOptions* delOptions, HXTMeshSize* meshsize); #endif diff --git a/contrib/hxt/hxt_mesh3d_main.c b/contrib/hxt/hxt_mesh3d_main.c new file mode 100644 index 0000000000000000000000000000000000000000..bbd5e7c6b36f7a79c4a12fa708f4c234280e1971 --- /dev/null +++ b/contrib/hxt/hxt_mesh3d_main.c @@ -0,0 +1,192 @@ +#include "hxt_mesh3d.h" +#include "hxt_tetrahedra.h" +#include "hxt_tetRepair.h" +#include "hxt_tetUtils.h" +#include "hxt_tetFlag.h" +#include "hxt_tetOpti.h" + +HXTStatus hxtTetMesh3d(HXTMesh* mesh, + int nthreads, + int reproducible, + int verbosity, + int displayStat, + int refine, // refine if !=0 + int optimize,// optimize quality if !=0 + double qualityThreshold, + HXTStatus (*bnd_recovery)(HXTMesh* mesh)) { + double t[8]={0}; + t[0] = omp_get_wtime(); + + HXTBbox bbox; + hxtBboxInit(&bbox); + HXT_CHECK( hxtBboxAdd(&bbox, mesh->vertices.coord, mesh->vertices.num) ); + + HXTDelaunayOptions delOptions = {&bbox, NULL, 0.0, 0.0, 0, verbosity, reproducible, nthreads}; + uint32_t numVerticesConstrained = mesh->vertices.num; + + HXT_INFO_COND(verbosity>0, "Creating an empty mesh with %u vertices", numVerticesConstrained); + HXT_CHECK( hxtEmptyMesh(mesh, &delOptions) ); + + t[1] = omp_get_wtime(); + + uint64_t nbMissingFacets, nbMissingEdges=0; + uint16_t nbColors; + HXT_CHECK( hxtConstrainTriangles(mesh, &nbMissingFacets) ); + if(nbMissingFacets==0) // TODO: differentiating missing triangles and missing edges ?? + HXT_CHECK( hxtConstrainEdgesNotInTriangles(mesh, &nbMissingEdges) ); + + t[2] = omp_get_wtime(); + + if (nbMissingFacets != 0 || nbMissingEdges!=0){ + if(bnd_recovery==NULL) + return HXT_ERROR_MSG(HXT_STATUS_ERROR, + "there are missing features but no boundary recovery function is given"); + if(nbMissingFacets) + HXT_INFO("Recovering %lu missing facet(s)", nbMissingFacets); + else if(nbMissingEdges) + HXT_INFO("Recovering %lu missing edge(s)", nbMissingEdges); + HXT_CHECK(bnd_recovery(mesh)); + + if(delOptions.numVerticesInMesh < mesh->vertices.num) { + HXT_INFO("Steiner(s) point(s) were inserted"); + delOptions.numVerticesInMesh = mesh->vertices.num; + } + + t[3] = omp_get_wtime(); + + memset(mesh->tetrahedra.flag, 0, mesh->tetrahedra.num*sizeof(uint16_t)); + HXT_CHECK(hxtTetOrientNodes(mesh)); + HXT_CHECK(hxtTetAdjacencies(mesh)); + HXT_CHECK(hxtAddGhosts(mesh)); + // HXT_CHECK( hxtTetVerify(mesh) ); + + HXT_CHECK( hxtConstrainTriangles(mesh, &nbMissingFacets) ); + if(nbMissingFacets!=0) + return HXT_ERROR_MSG( HXT_STATUS_ERROR, + "%d boundary face%s still missing (after recovery step).", + nbMissingFacets, (nbMissingFacets>1)?"s are":" is" ); + + HXT_CHECK( hxtConstrainEdgesNotInTriangles(mesh, &nbMissingEdges) ); + if(nbMissingEdges!=0) + return HXT_ERROR_MSG( HXT_STATUS_ERROR, + "%d constrained edge%s still missing (after recovery step).", + nbMissingEdges, (nbMissingEdges>1)?"s are":" is" ); + } + + HXT_CHECK(hxtColorMesh(mesh, &nbColors)); + +#ifdef DEBUG + HXT_CHECK( hxtTetVerify(mesh) ); + + memset(mesh->tetrahedra.flag, 0, mesh->tetrahedra.num*sizeof(uint16_t)); + HXT_CHECK( hxtConstrainTriangles(mesh, &nbMissingFacets) ); + if(nbMissingFacets!=0) + return HXT_ERROR_MSG( HXT_STATUS_ERROR, + "%d boundary face%s still missing (after refine).", + nbMissingFacets, (nbMissingFacets>1)?"s are":" is" ); + + HXT_CHECK( hxtConstrainEdgesNotInTriangles(mesh, &nbMissingEdges) ); + if(nbMissingEdges!=0) + return HXT_ERROR_MSG( HXT_STATUS_ERROR, + "%d constrained edge%s still missing (after refine).", + nbMissingEdges, (nbMissingEdges>1)?"s are":" is" ); +#endif + + t[4] = omp_get_wtime(); + + if(refine){ + // HXT_CHECK(hxtComputeMeshSizeFromMesh(mesh, &delOptions)); + HXT_CHECK(hxtComputeMeshSizeFromTrianglesAndLines(mesh, &delOptions)); + + // // triangulate only one color + // if(color>=0) { + // #pragma omp parallel for simd + // for (uint64_t i=0; i<mesh->tetrahedra.num; i++) { + // if(mesh->tetrahedra.colors[i]!=theColor){ + // markTetAsProcessed(mesh,i) = 1; + // } + // } + // } + + + HXTMeshSize *meshSize = NULL; + // HXT_CHECK(hxtMeshSizeCreate (context,&meshSize)); + // HXT_CHECK(hxtMeshSizeCompute (meshSize, bbox.min, bbox.max, mySize, NULL)); + // printf("time from empty mesh to first insertion: %f second\n", omp_get_wtime() - time); + HXT_CHECK(hxtRefineTetrahedra(mesh, &delOptions, meshSize)); + // HXT_CHECK(hxtMeshSizeDelete (&meshSize)); + HXT_CHECK(hxtAlignedFree(&delOptions.nodalSizes)); + // #endif + } + + t[5] = omp_get_wtime(); + +#ifdef DEBUG + HXT_CHECK( hxtTetVerify(mesh) ); + + memset(mesh->tetrahedra.flag, 0, mesh->tetrahedra.num*sizeof(uint16_t)); + HXT_CHECK( hxtConstrainTriangles(mesh, &nbMissingFacets) ); + if(nbMissingFacets!=0) + return HXT_ERROR_MSG( HXT_STATUS_ERROR, + "%d boundary face%s still missing (after refine).", + nbMissingFacets, (nbMissingFacets>1)?"s are":" is" ); + + HXT_CHECK( hxtConstrainEdgesNotInTriangles(mesh, &nbMissingEdges) ); + if(nbMissingEdges!=0) + return HXT_ERROR_MSG( HXT_STATUS_ERROR, + "%d constrained edge%s still missing (after refine).", + nbMissingEdges, (nbMissingEdges>1)?"s are":" is" ); +#endif + + if(optimize) + HXT_CHECK( hxtOptimizeTetrahedra(mesh, &bbox, delOptions.minSizeEnd, qualityThreshold, numVerticesConstrained) ); + + t[6] = omp_get_wtime(); + + +#ifdef DEBUG + HXT_CHECK( hxtTetVerify(mesh) ); + + memset(mesh->tetrahedra.flag, 0, mesh->tetrahedra.num*sizeof(uint16_t)); + HXT_CHECK( hxtConstrainTriangles(mesh, &nbMissingFacets) ); + if(nbMissingFacets!=0) + return HXT_ERROR_MSG( HXT_STATUS_ERROR, + "%d boundary face%s still missing (after refine).", + nbMissingFacets, (nbMissingFacets>1)?"s are":" is" ); + + HXT_CHECK( hxtConstrainEdgesNotInTriangles(mesh, &nbMissingEdges) ); + if(nbMissingEdges!=0) + return HXT_ERROR_MSG( HXT_STATUS_ERROR, + "%d constrained edge%s still missing (after refine).", + nbMissingEdges, (nbMissingEdges>1)?"s are":" is" ); +#endif + + + if(displayStat){ + HXT_INFO("\n\t\tFinal tet. mesh contains %lu tetrahedra" + "\n\t\tand %u vertices", mesh->tetrahedra.num, mesh->vertices.num); + + HXT_INFO("tEmptyMesh \t = \t %8.3f", t[1]-t[0]); + HXT_INFO("tVerifyBnd \t = \t %8.3f", t[2]-t[1]); + if(t[3]){ + HXT_INFO("tBndRecovery\t = \t %8.3f", t[3]-t[2]); + HXT_INFO("tConvertMesh\t = \t %8.3f", t[4]-t[3]); + if(refine) + HXT_INFO("tRefine \t = \t %8.3f", t[5]-t[4]); + } + else{ + HXT_INFO("tBndRecovery\t = \t 0.000 (boundary not altered)"); + HXT_INFO("tConvertMesh\t = \t 0.000 (nothing to convert)"); + if(refine) + HXT_INFO("tRefine \t = \t %8.3f", t[5]-t[2]); + } + + if(!optimize) + HXT_INFO("tOptimize \t = \t 0.000 (mesh optimization disabled)"); + else + HXT_INFO("tOptimize \t = \t %8.3f", t[6]-t[5]); + } + + return HXT_STATUS_OK; +} + diff --git a/contrib/hxt/hxt_mesh3d_main.h b/contrib/hxt/hxt_mesh3d_main.h new file mode 100644 index 0000000000000000000000000000000000000000..af2b9ec27213f0fd795f1801d0c0090f61792dba --- /dev/null +++ b/contrib/hxt/hxt_mesh3d_main.h @@ -0,0 +1,16 @@ +#ifndef _HXT_MESH_3D_MAIN_ +#define _HXT_MESH_3D_MAIN_ + +#include "hxt_mesh.h" + +HXTStatus hxtTetMesh3d(HXTMesh* mesh, + int nthreads, + int reproducible, + int verbosity, + int displayStat, + int refine, + int optimize, + double qualityThreshold, + HXTStatus (*bnd_recovery)(HXTMesh* mesh)); + +#endif \ No newline at end of file diff --git a/contrib/hxt/hxt_message.c b/contrib/hxt/hxt_message.c index a22398430a85617557a68cfb1a1f47ab9c06ee40..bedd547d239570c239fb61cd29dff3d8a34b4d10 100644 --- a/contrib/hxt/hxt_message.c +++ b/contrib/hxt/hxt_message.c @@ -32,13 +32,25 @@ const char* hxtGetStatusString(HXTStatus status){ case HXT_STATUS_POINTER_ERROR: return "wrong pointer given"; break; + case HXT_STATUS_READ_ERROR: + return "read error"; + break; + case HXT_STATUS_WRITE_ERROR: + return "write error"; + break; + case HXT_STATUS_RANGE_ERROR: + return "number out of range"; + break; + case HXT_STATUS_FORMAT_ERROR: + return "wrong format"; + break; default: if(status<=HXT_STATUS_INTERNAL) return "internal error was not catched. This should not happen"; else if(status<0) return "unknow error"; else - return "positive return value"; + return "positive return value (no error)"; break; } } diff --git a/contrib/hxt/hxt_message.h b/contrib/hxt/hxt_message.h index 9bba6219dc21b2e63f8f007625a12b35d2d28941..50b0a6d412e0508e4b329ffa7cd315b57bd1e26d 100644 --- a/contrib/hxt/hxt_message.h +++ b/contrib/hxt/hxt_message.h @@ -8,10 +8,6 @@ #define STR(x) #x #define STRINGIFY(x) STR(x) -#ifdef _MSC_VER -#define __func__ __FUNCTION__ -#endif - #define HXT_INFO(...) hxtMessageInfo(__func__, __FILE__, STRINGIFY(__LINE__), ## __VA_ARGS__ ) #define HXT_INFO_COND(cond, ...) ((cond)?HXT_INFO(__VA_ARGS__):HXT_STATUS_OK) #define HXT_WARNING(...) hxtMessageWarning(__func__, __FILE__, STRINGIFY(__LINE__), ## __VA_ARGS__ ) @@ -38,11 +34,12 @@ #define HXT_CHECK(status) HXT_CHECK_MSG(status,NULL) /* use to check some expression inside of function, throw error if exp is not true */ -#ifdef DEBUG +#ifndef NDEBUG #define HXT_ASSERT_MSG(exp, ...) \ do { \ if (!(exp)) { \ - return HXT_ERROR_MSG(HXT_STATUS_ASSERTION_FAILED, ## __VA_ARGS__ ); \ + HXT_ERROR_MSG(HXT_STATUS_ASSERTION_FAILED, ## __VA_ARGS__ ); \ + abort(); \ } \ } while(0) #define HXT_ASSERT(exp) HXT_ASSERT_MSG(exp, "assertion (" #exp ") Failed") diff --git a/contrib/hxt/hxt_non_linear_solver.c b/contrib/hxt/hxt_non_linear_solver.c index 656127dca5a420fd3dcba4ead4eb739adb50f039..cfaac46f2870bbc661c6dc1876018fa0dd957235 100644 --- a/contrib/hxt/hxt_non_linear_solver.c +++ b/contrib/hxt/hxt_non_linear_solver.c @@ -3,8 +3,8 @@ HXTStatus hxtNewtonRaphson(HXTLinearSystem *nrSys, double *solution, int size, int maxiter, double tol, HXTNonLinearSolverCallbackF *fcb, HXTNonLinearSolverCallbackDF *dfcb, void *data) { double *delta, *rhs; - HXT_CHECK(hxtMalloc(&delta,size*sizeof(double))); - HXT_CHECK(hxtMalloc(&rhs,size*sizeof(double))); + HXT_CHECK(hxtAlignedMalloc(&delta,size*sizeof(double))); + HXT_CHECK(hxtAlignedMalloc(&rhs,size*sizeof(double))); for(int n=0; n<maxiter; n++){ HXT_CHECK(hxtLinearSystemZeroMatrix(nrSys)); HXT_CHECK(fcb(data,solution,nrSys,rhs)); @@ -20,8 +20,8 @@ HXTStatus hxtNewtonRaphson(HXTLinearSystem *nrSys, double *solution, int size, i if(nrNorm<tol) break; } - HXT_CHECK(hxtFree(&delta)); - HXT_CHECK(hxtFree(&rhs)); + HXT_CHECK(hxtAlignedFree(&delta)); + HXT_CHECK(hxtAlignedFree(&rhs)); return HXT_STATUS_OK; } diff --git a/contrib/hxt/hxt_opt.c b/contrib/hxt/hxt_opt.c new file mode 100644 index 0000000000000000000000000000000000000000..b0f27051610a3cf567f53db42af514d780ce787e --- /dev/null +++ b/contrib/hxt/hxt_opt.c @@ -0,0 +1,812 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <float.h> +#include <errno.h> +#include <limits.h> +#include "hxt_opt.h" + + +const HXTOptionArgumentConstraints HXT_POSITIVE_RANGE = { + 0, DBL_MAX, + 0, LLONG_MAX, + 0, ULLONG_MAX, + NULL, NULL, NULL +}; +const HXTOptionArgumentConstraints HXT_ALPHA_LOWERCASE_RANGE = { + -DBL_MAX, DBL_MAX, + LLONG_MIN, LLONG_MAX, + 0, ULLONG_MAX, + NULL, NULL, HXT_ALPHA_LOWERCASE_CHARACTERS +}; +const HXTOptionArgumentConstraints HXT_ALPHA_UPPERCASE_RANGE = { + -DBL_MAX, DBL_MAX, + LLONG_MIN, LLONG_MAX, + 0, ULLONG_MAX, + NULL, NULL, HXT_ALPHA_UPPERCASE_CHARACTERS +}; +const HXTOptionArgumentConstraints HXT_ALPHA_RANGE = { + -DBL_MAX, DBL_MAX, + LLONG_MIN, LLONG_MAX, + 0, ULLONG_MAX, + NULL, NULL, HXT_ALPHA_CHARACTERS +}; +const HXTOptionArgumentConstraints HXT_NUMERIC_RANGE = { + -DBL_MAX, DBL_MAX, + LLONG_MIN, LLONG_MAX, + 0, ULLONG_MAX, + NULL, NULL, HXT_NUMERIC_CHARACTERS +}; +const HXTOptionArgumentConstraints HXT_ALPHANUMERIC_LOWERCASE_RANGE = { + -DBL_MAX, DBL_MAX, + LLONG_MIN, LLONG_MAX, + 0, ULLONG_MAX, + NULL, NULL, HXT_ALPHANUMERIC_LOWERCASE_CHARACTERS +}; +const HXTOptionArgumentConstraints HXT_ALPHANUMERIC_UPPERCASE_RANGE = { + -DBL_MAX, DBL_MAX, + LLONG_MIN, LLONG_MAX, + 0, ULLONG_MAX, + NULL, NULL, HXT_ALPHANUMERIC_UPPERCASE_CHARACTERS +}; +const HXTOptionArgumentConstraints HXT_ALPHANUMERIC_RANGE = { + -DBL_MAX, DBL_MAX, + LLONG_MIN, LLONG_MAX, + 0, ULLONG_MAX, + NULL, NULL, HXT_ALPHANUMERIC_CHARACTERS +}; +const HXTOptionArgumentConstraints HXT_PRINTABLE_VISIBLE_RANGE = {// between space and ~ + -DBL_MAX, DBL_MAX, + LLONG_MIN, LLONG_MAX, + 0, ULLONG_MAX, + NULL, NULL, HXT_PRINTABLE_VISIBLE_CHARACTERS +}; +const HXTOptionArgumentConstraints HXT_PRINTABLE_RANGE = { + -DBL_MAX, DBL_MAX, + LLONG_MIN, LLONG_MAX, + 0, ULLONG_MAX, + NULL, NULL, HXT_PRINTABLE_CHARACTERS +}; +const HXTOptionArgumentConstraints HXT_0_1_RANGE = { + 0., 1., + 0, 1, + 0, 1, + NULL, NULL, NULL +}; +const HXTOptionArgumentConstraints HXT_0_2_RANGE = { + 0., 2., + 0, 2, + 0, 2, + NULL, NULL, NULL +}; +const HXTOptionArgumentConstraints HXT_0_3_RANGE = { + 0., 3., + 0, 3, + 0, 3, + NULL, NULL, NULL +}; +const HXTOptionArgumentConstraints HXT_0_4_RANGE = { + 0., 4., + 0, 4, + 0, 4, + NULL, NULL, NULL +}; +const HXTOptionArgumentConstraints HXT_0_5_RANGE = { + 0., 5., + 0, 5, + 0, 5, + NULL, NULL, NULL +}; +const HXTOptionArgumentConstraints HXT_0_10_RANGE = { + 0., 10., + 0, 10, + 0, 10, + NULL, NULL, NULL +}; +const HXTOptionArgumentConstraints HXT_0_20_RANGE = { + 0., 20., + 0, 20, + 0, 20, + NULL, NULL, NULL +}; +const HXTOptionArgumentConstraints HXT_0_50_RANGE = { + 0., 50., + 0, 50, + 0, 50, + NULL, NULL, NULL +}; +const HXTOptionArgumentConstraints HXT_0_100_RANGE = { + 0., 100., + 0, 100, + 0, 100, + NULL, NULL, NULL +}; +const HXTOptionArgumentConstraints HXT_0_1000_RANGE = { + 0., 1000., + 0, 1000, + 0, 1000, + NULL, NULL, NULL +}; + + +typedef struct HXTOptionStruct { + char shortName; + const char* longName; + const char* description; + const HXTOptionArgumentConstraints* constraints; + HXTOptionArgumentType valueType; + void* valuePtr; +} HXTOption; + +static HXTOption helpOption = {'h', "help", "Display this help message", NULL, HXT_FLAG, NULL}; +static HXTOption* optionList = NULL; +static int optionListLength = 0; +static int optionListSize = 0; + + +#define MAX(a,b) (((a)>(b))?(a):(b)) + + +static HXTStatus optionListReserve(int n) +{ + if(optionList==NULL) { + optionListSize = MAX(16,n); + HXT_CHECK( hxtMalloc(&optionList, optionListSize*sizeof(HXTOption)) ); + optionList[0] = helpOption; + optionListLength = 1; + } + else if(optionListLength + n > optionListSize) { + optionListSize = MAX(2*optionListSize, optionListLength + n); + HXT_CHECK( hxtRealloc(&optionList, optionListSize) ); + } + return HXT_STATUS_OK; +} + + +HXTStatus hxtAddOption(char shortName, + const char* longName, + const char* description, + HXTOptionArgumentType valueType, + const HXTOptionArgumentConstraints* constraints, + void* valuePtr) +{ + HXT_CHECK( optionListReserve(1) ); + +#ifndef NDEBUG + if(valuePtr==NULL && (shortName!='\0' || longName!=NULL || description!=NULL)) + return HXT_ERROR_MSG(HXT_STATUS_ERROR, "Adding a non-empty option with a NULL value pointer makes no sense"); + for (int i=0; i<optionListLength; i++) { + if(shortName!='\0' && shortName==optionList[i].shortName) + return HXT_ERROR_MSG(HXT_STATUS_ERROR, "-%c is already the short name of another option", shortName); + } + if(longName!=NULL) { + if(longName[0]=='\0') + return HXT_ERROR_MSG(HXT_STATUS_ERROR, "Cannot use empty string as a long option name. Use NULL pointer if you do not want a long option name."); + if(longName[0]=='-') + return HXT_ERROR_MSG(HXT_STATUS_ERROR, "long option \"%s\" cannot begin with a '-'", longName); + for (int i=0; longName[i]!='\0'; i++) { + if(longName[i]<=' ') + return HXT_ERROR_MSG(HXT_STATUS_ERROR, "long option \"%s\" should only contain printable characters", longName); + } + for (int i=0; i<optionListLength; i++) { + if(optionList[i].longName!=NULL && strcmp(longName, optionList[i].longName)==0) + return HXT_ERROR_MSG(HXT_STATUS_ERROR, "--%s is already the long name of another option", longName); + } + } +#endif + + if(shortName=='\0' && (longName==NULL || longName[0]=='\0') && + (valueType==HXT_FLAG || valueType==HXT_NO_FLAG) ) { + return HXT_ERROR_MSG(HXT_STATUS_ERROR, "A flag must have an option name. Therefore, it can not be a trailing option"); + } + + optionList[optionListLength].shortName = shortName; + optionList[optionListLength].longName = longName; + optionList[optionListLength].description = description; + optionList[optionListLength].valueType = valueType; + optionList[optionListLength].constraints = constraints; + optionList[optionListLength].valuePtr = valuePtr; + optionListLength++; + + return HXT_STATUS_OK; +} + + +/********************************************************************* +* search a long option inside the list * +*********************************************************************/ +static int searchLongOption(const char* string) +{ + for (int i=0; i<optionListLength; i++) { + if(optionList[i].longName!=NULL && + strcmp(optionList[i].longName, string)==0) { + return i; + } + } + + return -1; +} + + +/********************************************************************* +* search a short option inside the list * +*********************************************************************/ +static int searchShortOption(char c) +{ + for (int i=0; i<optionListLength; i++) { + if(c==optionList[i].shortName) { + return i; + } + } + return -1; +} + + +int fileExists(const char *fname) +{ + FILE *file; + if ((file = fopen(fname, "r"))) + { + fclose(file); + return 1; + } + return 0; +} + + +static const char* getArgTypeName(HXTOptionArgumentType t){ + switch(t) { + case HXT_FLAG: + case HXT_NO_FLAG: + return "(none)"; + case HXT_DOUBLE: + return "a double"; + case HXT_FLOAT: + return "a float"; + case HXT_INT: + return "an integer "; + case HXT_I64: + return "a 64-bit integer"; + case HXT_I32: + return "a 32-bit integer"; + case HXT_I16: + return "a 16-bit integer"; + case HXT_UNSIGNED: + return "an unsigned integer "; + case HXT_U64: + return "a 64-bit unsigned integer"; + case HXT_U32: + return "a 32-bit unsigned integer"; + case HXT_U16: + return "a 16-bit unsigned integer"; + default: + return "a string"; + } +} + + +/********************************************************************* +* scan the argument and verify its validity * +*********************************************************************/ +static HXTStatus doOption(HXTOption* opt, + const char* arg, + const char* optName) +{ + if(opt->valueType==HXT_FLAG) { + *(int*) opt->valuePtr = 1; + return HXT_STATUS_OK; + } + if(opt->valueType==HXT_NO_FLAG) { + *(int*) opt->valuePtr = 0; + return HXT_STATUS_OK; + } + + char* endptr = NULL; + double r = 0; + unsigned long long u = 0; + long long int i = 0; + const char* s = NULL; + + int isDouble = 0, isInteger = 0, isUnsigned = 0; + + if(opt->valueType==HXT_DOUBLE || opt->valueType==HXT_FLOAT) { + isDouble = 1; + r = strtod(arg, &endptr); + } + else if(opt->valueType==HXT_INT || + opt->valueType==HXT_I64 || + opt->valueType==HXT_I32 || + opt->valueType==HXT_I16) { + isInteger = 1; + i = strtoll(arg, &endptr, 10); + } + else if(opt->valueType==HXT_UNSIGNED || + opt->valueType==HXT_U64 || + opt->valueType==HXT_U32 || + opt->valueType==HXT_U16) { + isUnsigned = 1; + u = strtoull(arg, &endptr, 10); + } + else { + s = arg; + } + + // verify the validity + if(errno == ERANGE){ +range_error: + return HXT_ERROR_MSG(HXT_STATUS_RANGE_ERROR, + "cannot convert argument \"%s\" of option \"%s\" to %s (range overflow)", + arg, optName, getArgTypeName(opt->valueType)); + } + else if (arg == endptr) + { + return HXT_ERROR_MSG(HXT_STATUS_FORMAT_ERROR, + "cannot convert argument \"%s\" of option \"%s\" to %s (no digit)", + arg, optName, getArgTypeName(opt->valueType)); + } + else if(endptr!=NULL && *endptr!='\0') { + return HXT_ERROR_MSG(HXT_STATUS_FORMAT_ERROR, + "cannot convert argument \"%s\" of option \"%s\" to %s (trailing unvalid characters)", + arg, optName, getArgTypeName(opt->valueType)); + } + + // verify the conversion is possible + switch(opt->valueType) { + // floating point values: + case HXT_DOUBLE: + *(double*) opt->valuePtr = r; + break; + case HXT_FLOAT: + if((float)r!=r) goto range_error; + *(float*) opt->valuePtr = r; + break; + // integer values + case HXT_INT: + if((int)i!=i) goto range_error; + *(int*) opt->valuePtr = i; + break; + case HXT_I64: + if((int64_t)i!=i) goto range_error; + *(int64_t*) opt->valuePtr = i; + break; + case HXT_I32: + if((int32_t)i!=i) goto range_error; + *(int32_t*) opt->valuePtr = i; + break; + case HXT_I16: + if((int16_t)i!=i) goto range_error; + *(int16_t*) opt->valuePtr = i; + break; + // unsigned integer values + case HXT_UNSIGNED: + if((unsigned)u!=u) goto range_error; + *(unsigned*) opt->valuePtr = u; + break; + case HXT_U64: + if((uint64_t)u!=u) goto range_error; + *(uint64_t*) opt->valuePtr = u; + break; + case HXT_U32: + if((uint32_t)u!=u) goto range_error; + *(uint32_t*) opt->valuePtr = u; + break; + case HXT_U16: + if((uint16_t)u!=u) goto range_error; + *(uint16_t*) opt->valuePtr = u; + break; + // string values + case HXT_EXISTING_FILENAME: + if(!fileExists(s)) + return HXT_ERROR_MSG(HXT_STATUS_FILE_CANNOT_BE_OPENED, + "file \"%s\" does not exist", s); + *(const char**) opt->valuePtr = s; + break; + case HXT_NEW_FILENAME: + if(fileExists(s)) + return HXT_ERROR_MSG(HXT_STATUS_FILE_CANNOT_BE_OPENED, + "file \"%s\" already exists", s); + *(const char**) opt->valuePtr = s; + break; + case HXT_ASK_TO_ERASE_FILENAME: + if(fileExists(s)) { + char memory[64]; + HXT_INFO("file \"%s\" already exists\n do you want to overwrite it ? y/N", s); + char* string = fgets(memory, 64, stdin); + if(string==NULL || (string[0]!='y' && string[0]!='Y') ) + return HXT_ERROR_MSG(HXT_STATUS_FILE_CANNOT_BE_OPENED, "aborting"); + } + /* fall through */ + + default: + *(const char**) opt->valuePtr = s; + break; + } + + if(opt->constraints==NULL) + return HXT_STATUS_OK; + + + if(isDouble){ + if(r < opt->constraints->doubleMin || r > opt->constraints->doubleMax) + return HXT_ERROR_MSG(HXT_STATUS_RANGE_ERROR, + "cannot convert argument \"%s\" of option \"%s\" " + "to %s between %g and %g (range overflow)", + arg, optName, getArgTypeName(opt->valueType), + opt->constraints->doubleMin, opt->constraints->doubleMax); + } + else if(isInteger){ + if(i < opt->constraints->intMin || i > opt->constraints->intMax) + return HXT_ERROR_MSG(HXT_STATUS_RANGE_ERROR, + "cannot convert argument \"%s\" of option \"%s\" " + "to %s between %lld and %lld (range overflow)", + arg, optName, getArgTypeName(opt->valueType), + opt->constraints->intMin, opt->constraints->intMax); + } + else if(isUnsigned){ + if(u < opt->constraints->unsignedMin || u > opt->constraints->unsignedMax) + return HXT_ERROR_MSG(HXT_STATUS_RANGE_ERROR, + "cannot convert argument \"%s\" of option \"%s\" " + "to %s between %llu and %llu (range overflow)", + arg, optName, getArgTypeName(opt->valueType), + opt->constraints->unsignedMin, opt->constraints->unsignedMax); + } + else { + if(opt->constraints->stringPrefix!=NULL) { + const char* prefix = opt->constraints->stringPrefix; + size_t lenpre = strlen(prefix), lens = strlen(s); + if(lens < lenpre || strncmp(prefix, s, lenpre) != 0) + return HXT_ERROR_MSG(HXT_STATUS_ERROR, "argument \"%s\" of option \"%s\" is not prefixed by \"%s\"", prefix); + } + if(opt->constraints->stringSuffix!=NULL) { + const char* suffix = opt->constraints->stringSuffix; + size_t lensuf = strlen(suffix), lens = strlen(s); + if(lens < lensuf || strcmp(suffix, s + lens - lensuf) != 0) + return HXT_ERROR_MSG(HXT_STATUS_ERROR, "argument \"%s\" of option \"%s\" is not suffixed by \"%s\"", suffix); + } + if(opt->constraints->stringCharAllowed!=NULL) { + const char* allowed = opt->constraints->stringCharAllowed; + for (int j=0; s[j]!='\0'; j++) { + int found = 0; + int special = 0; + if(opt->constraints==&HXT_ALPHA_LOWERCASE_RANGE + || opt->constraints==&HXT_ALPHA_RANGE + || opt->constraints==&HXT_ALPHANUMERIC_LOWERCASE_RANGE + || opt->constraints==&HXT_ALPHANUMERIC_RANGE) { + special = 1; + if(s[j]>='a' && s[j]<='z') found = 1; + } + if(opt->constraints==&HXT_ALPHA_UPPERCASE_RANGE + || opt->constraints==&HXT_ALPHA_RANGE + || opt->constraints==&HXT_ALPHANUMERIC_UPPERCASE_RANGE + || opt->constraints==&HXT_ALPHANUMERIC_RANGE) { + special = 1; + if(s[j]>='A' && s[j]<='Z') found = 1; + } + if(opt->constraints==&HXT_NUMERIC_RANGE + || opt->constraints==&HXT_ALPHANUMERIC_LOWERCASE_RANGE + || opt->constraints==&HXT_ALPHANUMERIC_UPPERCASE_RANGE + || opt->constraints==&HXT_ALPHANUMERIC_RANGE) { + special = 1; + if(s[j]>='0' && s[j]<='9') found = 1; + } + if(opt->constraints==&HXT_PRINTABLE_VISIBLE_RANGE + || opt->constraints==&HXT_PRINTABLE_RANGE) { + special = 1; + if(s[j]>' ' && s[j]<='~') found = 1; + } + if(opt->constraints==&HXT_PRINTABLE_RANGE) { + special = 1; + if(s[j]==' ' || s[j]=='\t' || s[j]=='\n') found = 1; + } + if(special!=1) { + for (int k=0; allowed[k]!='\0'; k++) { + if(s[j]==allowed[k]){ + found = 1; + break; + } + } + } + + if(found==0) + return HXT_ERROR_MSG(HXT_STATUS_FORMAT_ERROR, "argument \"%s\" of option \"%s\" contain the unvalid character %c", s[j]); + } + } + } + + return HXT_STATUS_OK; +} + + +static int getNextTrailingOption(int n) { + for (int i=n+1; i<optionListLength; i++) { + if(optionList[i].shortName=='\0' && optionList[i].longName==NULL) { + return i; + } + } + return -1; +} + + +HXTStatus hxtParseOptions(const int argc, const char* argv[]) +{ + int dashdash = 0; + int trailing = getNextTrailingOption(0); + for (int i=1; i<argc; i++) { + const char *arg = NULL; + HXTOption* opt = NULL; + + if(dashdash || argv[i][0]!='-' || (argv[i][0]=='-' && argv[i][1]=='\0')){ + if(trailing==-1 || optionList[trailing].valuePtr==NULL) + return HXT_ERROR_MSG(HXT_STATUS_FORMAT_ERROR, + "Additional argument \"%s\" does not correspond to any option", + argv[i]); + else + HXT_CHECK( doOption(&optionList[trailing], argv[i], optionList[trailing].description) ); + + trailing = getNextTrailingOption(trailing); + } + else if(argv[i][1]=='-') { /* long opt */ + if(argv[i][2]=='\0') /* -- terminate argument parsing, + everything from now on is a trailing option */ { + dashdash = 1; + continue; + } + + char* equalSign = strchr(argv[i]+2,'='); + + if(equalSign!=NULL){ + *equalSign = '\0'; + arg = equalSign + 1; + } + + int num = searchLongOption(argv[i]+2); + if(num<0){ + return HXT_ERROR_MSG(HXT_STATUS_FORMAT_ERROR, + "option \"%s\" not found", argv[i]); + } + opt = &optionList[num]; + + if(equalSign!=NULL){ + *equalSign = '='; + if(opt->valueType==HXT_FLAG || opt->valueType==HXT_NO_FLAG){ + return HXT_ERROR_MSG(HXT_STATUS_FORMAT_ERROR, + "option \"%s\" takes no argument", argv[i]); + } + } + else{ + if(num==0) { // it's the help option + return HXT_STATUS_INTERNAL; + } + if(opt->valueType!=HXT_FLAG && opt->valueType!=HXT_NO_FLAG) { + if(argc<=i+1) { + return HXT_ERROR_MSG(HXT_STATUS_FORMAT_ERROR, + "options \"%s\" requires an argument", argv[i]); + } + else { + arg = argv[i+1]; + i++; + } + } + } + + HXT_CHECK( doOption(opt, arg, argv[i]) ); + } + else{ /* short option */ + int cond = argv[i][1]; + for(int j=1; cond; j++){ + int num = searchShortOption(argv[i][j]); + if(num<0){ + return HXT_ERROR_MSG(HXT_STATUS_FORMAT_ERROR, + "option \'%c\' in \"%s\" not found", argv[i][j], argv[i]); + } + + char optName[] = "- "; + optName[1] = argv[i][j]; + cond = argv[i][j+1]; + + if(num==0){ + return HXT_STATUS_INTERNAL; + } + + opt = &optionList[num]; + + if(opt->valueType!=HXT_FLAG && opt->valueType!=HXT_NO_FLAG){ + if(cond){ + arg = argv[i] + j+1; + cond = 0; // stop the loop + } + else if(argc<=i+1) { + return HXT_ERROR_MSG(HXT_STATUS_FORMAT_ERROR, + "options \'%c\' in \"%s\" requires an argument", argv[i][j], argv[i]); + } + else { + arg = argv[i+1]; + i++; + } + } + + HXT_CHECK( doOption(opt, arg, optName) ); + } + } + } + + if(trailing!=-1) { + if(getNextTrailingOption(trailing)!=-1) + return HXT_ERROR_MSG(HXT_STATUS_FORMAT_ERROR, "No \"%s\" given", optionList[trailing].description); + } + + HXT_CHECK( hxtFree(&optionList) ); + + return HXT_STATUS_OK; +} + + +#define MY_SPRINTF(...) *offset+=snprintf(text+ *offset, 16384- *offset, ## __VA_ARGS__ ) + +static const char* getHelpValueName(HXTOption* opt) { + switch(opt->valueType) { + case HXT_DOUBLE: + case HXT_FLOAT: + return "VAL"; + case HXT_INT: + case HXT_I64: + case HXT_I32: + case HXT_I16: + case HXT_UNSIGNED: + case HXT_U64: + case HXT_U32: + case HXT_U16: + return "NUM"; + case HXT_STRING: + return "TEXT"; + case HXT_EXISTING_FILENAME: + case HXT_NEW_FILENAME: + case HXT_ASK_TO_ERASE_FILENAME: + return "FILENAME"; + default: + return "(none)"; + } +} + + +static void printOptionLine(HXTOption* opt, char text[16384], int* offset) +{ + const int char_max = 30; + int oldOffset = *offset; + MY_SPRINTF(" "); + + if(opt->shortName!='\0'){ + if(opt->longName!=NULL){ + MY_SPRINTF("-%c, ", opt->shortName); + } + else{ + MY_SPRINTF("-%c", opt->shortName); + } + } + + if(opt->longName!=NULL){ + if(opt->valueType==HXT_FLAG || opt->valueType==HXT_NO_FLAG) + MY_SPRINTF("--%s", opt->longName); + else + MY_SPRINTF("--%s=%s", opt->longName, getHelpValueName(opt)); + + + } + + int char_count = *offset - oldOffset; + if(char_count<char_max) + MY_SPRINTF("%*c", char_max - char_count, ' '); + else + MY_SPRINTF("\n%*c", char_max, ' '); + + // description + { + int i,k; + for (i=0,k=0; opt->description[i+1]!='\0'; i++, k++) { + if(opt->description[i]=='\n'){ + if(opt->description[i+1]=='\n' || opt->description[i+1]=='\0') + MY_SPRINTF("%.*s\n", k, opt->description+i-k); + else + MY_SPRINTF("%.*s\n%*c", k, opt->description+i-k, char_max+2, ' '); + k=-1; + } + } + MY_SPRINTF("%s", opt->description+i-k); + } + + // default argument + if(opt->valueType!=HXT_FLAG && opt->valueType!=HXT_NO_FLAG) + { + double r = 0; + unsigned long long u = 0; + long long int i = 0; + MY_SPRINTF("\n%*cdefault: %s=", char_max+1, ' ', getHelpValueName(opt)); + switch(opt->valueType) { + case HXT_DOUBLE: + MY_SPRINTF("%g", *(double*) opt->valuePtr); + break; + case HXT_FLOAT: + r = *(float*) opt->valuePtr; + MY_SPRINTF("%g", r); + break; + case HXT_INT: + MY_SPRINTF("%d", *(int*) opt->valuePtr); + break; + case HXT_I64: + i = *(int64_t*) opt->valuePtr; + MY_SPRINTF("%lld", i); + break; + case HXT_I32: + i = *(int32_t*) opt->valuePtr; + MY_SPRINTF("%lld", i); + break; + case HXT_I16: + i = *(int16_t*) opt->valuePtr; + MY_SPRINTF("%lld", i); + break; + case HXT_UNSIGNED: + MY_SPRINTF("%u", *(unsigned*) opt->valuePtr); + break; + case HXT_U64: + u = *(uint64_t*) opt->valuePtr; + MY_SPRINTF("%llu", u); + break; + case HXT_U32: + u = *(uint32_t*) opt->valuePtr; + MY_SPRINTF("%llu", u); + break; + case HXT_U16: + u = *(uint16_t*) opt->valuePtr; + MY_SPRINTF("%llu", u); + break; + case HXT_STRING: + case HXT_EXISTING_FILENAME: + case HXT_NEW_FILENAME: + case HXT_ASK_TO_ERASE_FILENAME: + if(opt->valuePtr!=NULL) + MY_SPRINTF("%s", *(const char**) opt->valuePtr); + break; + default: + break; + } + } + + MY_SPRINTF("\n"); +} + + +HXTStatus hxtGetOptionHelp(char text[16384], + const char* programName, + const char* programDescription, + const char* additionalInfo) +{ + int offsetval = 0; + int* offset = &offsetval; + if(programName!=NULL) { + MY_SPRINTF("%s [OPTION] ...", programName); + int n = getNextTrailingOption(0); + while(n!=-1 && optionList[n].valuePtr!=NULL) { + int n2 = getNextTrailingOption(n); + if(optionList[n].shortName=='\0' && optionList[n].longName==NULL && optionList[n].description!=NULL) { + if(n2==-1) + MY_SPRINTF(" [%s]", optionList[n].description); + else + MY_SPRINTF(" %s", optionList[n].description); + } + n=n2; + } + } + + if(programDescription!=NULL) + MY_SPRINTF("\n\n%s", programDescription); + MY_SPRINTF("\n\n"); + + for (int i=0; i<optionListLength; i++) { + if(optionList[i].shortName!='\0' || optionList[i].longName!=NULL) + printOptionLine(&optionList[i], text, offset); + } + + if(additionalInfo) + MY_SPRINTF("%s\n", additionalInfo); + return HXT_STATUS_OK; +} \ No newline at end of file diff --git a/contrib/hxt/hxt_opt.h b/contrib/hxt/hxt_opt.h new file mode 100644 index 0000000000000000000000000000000000000000..c2ad5b8b5c49e15d8d049a1815b5392fc523796a --- /dev/null +++ b/contrib/hxt/hxt_opt.h @@ -0,0 +1,170 @@ +#ifndef __HXT_OPT_H__ +#define __HXT_OPT_H__ + +#include "hxt_tools.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/**************************************************************************************** + * argument type: the type of the argument that must be given to the option + * or HXT[_NO]_FLAG to indicate that the option doesn't have an argument + ***************************************************************************************/ +typedef enum { +// flags + // no argument must be given, the value must be an integer set to 0 (it is set to 1 when option is called) + HXT_FLAG, + // no argument must be given, the value must be an integer set to 1 (it is set to 0 when option is called) + HXT_NO_FLAG, + +// floating point values: + HXT_DOUBLE, + HXT_FLOAT, + +// integer values + HXT_INT, + HXT_I64, + HXT_I32, + HXT_I16, + +// unsigned integer values + HXT_UNSIGNED, + HXT_U64, + HXT_U32, + HXT_U16, + +// strings values + HXT_STRING, + HXT_EXISTING_FILENAME, + HXT_NEW_FILENAME, + HXT_ASK_TO_ERASE_FILENAME +} HXTOptionArgumentType; + +/************************************************************************************* + * Constraints on the accepted range of the value given in argument * + ************************************************************************************/ +typedef struct { + // acceptable range for floating-point values + double doubleMin; + double doubleMax; + + // acceptable range for integer values + long long int intMin; + long long int intMax; + + // acceptable range for unsigned integer values + unsigned long long unsignedMin; + unsigned long long unsignedMax; + + // required prefix and suffix to the string (NULL pointer are omitted) + const char* stringPrefix; + const char* stringSuffix; + + // character allowed, required or forbidden (NULL pointer are omitted) + const char* stringCharAllowed; +} HXTOptionArgumentConstraints; + +#define HXT_ALPHA_LOWERCASE_CHARACTERS "abcdefghijklmnopqrstuvwxyz" +#define HXT_ALPHA_UPPERCASE_CHARACTERS "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +#define HXT_ALPHA_CHARACTERS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" +#define HXT_NUMERIC_CHARACTERS "0123456789" +#define HXT_ALPHANUMERIC_LOWERCASE_CHARACTERS "abcdefghijklmnopqrstuvwxyz0123456789" +#define HXT_ALPHANUMERIC_UPPERCASE_CHARACTERS "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" +#define HXT_ALPHANUMERIC_CHARACTERS \ + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" +#define HXT_PRINTABLE_VISIBLE_CHARACTERS \ + "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" +#define HXT_PRINTABLE_CHARACTERS \ + "\t\n !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" + +// built-in hxtOptionArg +extern const HXTOptionArgumentConstraints HXT_POSITIVE_RANGE; + +extern const HXTOptionArgumentConstraints HXT_ALPHA_LOWERCASE_RANGE; +extern const HXTOptionArgumentConstraints HXT_ALPHA_UPPERCASE_RANGE; +extern const HXTOptionArgumentConstraints HXT_ALPHA_RANGE; +extern const HXTOptionArgumentConstraints HXT_NUMERIC_RANGE; +extern const HXTOptionArgumentConstraints HXT_ALPHANUMERIC_LOWERCASE_RANGE; +extern const HXTOptionArgumentConstraints HXT_ALPHANUMERIC_UPPERCASE_RANGE; +extern const HXTOptionArgumentConstraints HXT_ALPHANUMERIC_RANGE; +extern const HXTOptionArgumentConstraints HXT_PRINTABLE_VISIBLE_RANGE; +extern const HXTOptionArgumentConstraints HXT_PRINTABLE_RANGE; + +extern const HXTOptionArgumentConstraints HXT_0_1_RANGE; +extern const HXTOptionArgumentConstraints HXT_0_2_RANGE; +extern const HXTOptionArgumentConstraints HXT_0_3_RANGE; +extern const HXTOptionArgumentConstraints HXT_0_4_RANGE; +extern const HXTOptionArgumentConstraints HXT_0_5_RANGE; +extern const HXTOptionArgumentConstraints HXT_0_10_RANGE; +extern const HXTOptionArgumentConstraints HXT_0_20_RANGE; +extern const HXTOptionArgumentConstraints HXT_0_50_RANGE; +extern const HXTOptionArgumentConstraints HXT_0_100_RANGE; +extern const HXTOptionArgumentConstraints HXT_0_1000_RANGE; + + +HXTStatus hxtAddOption(char short_name, + const char* longName, + const char* description, + HXTOptionArgumentType valueType, + const HXTOptionArgumentConstraints* constraints, + void* valuePtr); + +/* Trailing options are options without an option name. + * the order in which they are added is important ! + * by default, the last trailing option is optional + * you can add an empty trailing option (a trailing option with valuePtr==NULL) at the end to make the last trailing option required + * example: + * + * const char* input = NULL; + * HXT_CHECK(hxtAddTrailingOption("INPUT_MESH", HXT_EXISTING_FILENAME, NULL, &input)); + * + * // without the next line, the user would be allowed not giving an input mesh file in the command line + * + * HXT_CHECK(hxtAddTrailingOption(NULL, 0, NULL, NULL)); + * + * // this empty trailing option option ensure that an input mesh file is required +*/ +static inline HXTStatus hxtAddTrailingOption( + const char* argumentName, // name to use in the usage line ./program [Options] trailingArg1 trailingArg2 ... + HXTOptionArgumentType valueType, + const HXTOptionArgumentConstraints* constraints, + void* valuePtr) +{ + HXT_CHECK( hxtAddOption('\0', NULL, argumentName, valueType, constraints, valuePtr) ); + return HXT_STATUS_OK; +} + +// use the HXT_PARSE_COMMAND_LINE macro instead +HXTStatus hxtGetOptionHelp(char message[16384], + const char* programName, + const char* programDescription, + const char* additionalInfo); + +// use the HXT_PARSE_COMMAND_LINE macro instead +HXTStatus hxtParseOptions(const int argc, const char* argv[]); + +// This macro should be placed in the main, after all options are added ! +#define HXT_PARSE_COMMAND_LINE(argc, argv, programName, programDescription, additionalInfo) \ +do{ \ + char help[16384]; \ + HXTStatus status = hxtGetOptionHelp(help, programName, programDescription, additionalInfo); \ + if(status==HXT_STATUS_OK) \ + status = hxtParseOptions(argc, argv); \ + if(status!=HXT_STATUS_OK && status!=HXT_STATUS_INTERNAL) { \ + HXT_TRACE(status); \ + puts("\n ==> Help on Error\n\n ---\n"); \ + } \ + if(status!=HXT_STATUS_OK) { \ + printf("%s", help); \ + return status!=HXT_STATUS_INTERNAL?status:0; \ + } \ +} while(0); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/contrib/hxt/hxt_option.c b/contrib/hxt/hxt_option.c index d750738c4d7125432a049ba562cd41b4a7b59afb..500ce1d87aa5cf4d4a3092094f7c434d8b44480d 100644 --- a/contrib/hxt/hxt_option.c +++ b/contrib/hxt/hxt_option.c @@ -1,244 +1,503 @@ -#include "hxt_option.h" -#include "hxt_message.h" -#include "hxt_tools.h" +#include <stdlib.h> +#include <stdio.h> #include <string.h> +#include <errno.h> +#include "hxt_option.h" + +static HXTOpt help_option = {"h", "help", "Display this help message"}; -typedef struct { - void *data; - hxtOptionType type; - char *description; - char *name; - HXTOptionCallback *cb; - void *cbdata; -} hxtOption; - -struct HXTOptionListStruct { - size_t noptions; - hxtOption *options; - size_t narguments; - hxtOption *arguments; - int lastArgumentIsRepeated; - char *description; +/********************************************************************* +* program structure * +*********************************************************************/ +struct HXTOptProgramStruct { + const char* usage_line; + const char* start_description; + const char* end_description; + HXTOpt** opts; + size_t opt_length; + size_t opt_capacity; }; -HXTStatus hxtOptionListCreate(HXTOptionList **list, const char *description) + +/********************************************************************* +* Create a program * +*********************************************************************/ +HXTStatus hxtOptProgCreate(HXTOptProgram** program_ptr, + const char* usage_line, + const char* start_description, + const char* end_description) { - HXT_CHECK(hxtMalloc(list, sizeof(HXTOptionList))); - (*list)->noptions = 0; - (*list)->options = NULL; - (*list)->narguments = 0; - (*list)->arguments = NULL; - (*list)->description = strdup(description); - (*list)->lastArgumentIsRepeated = 0; - return HXT_STATUS_OK; + HXT_ASSERT(program_ptr!=NULL); + + HXTOptProgram* program; + HXT_CHECK( hxtMalloc(&program, sizeof(HXTOptProgram)) ); + *program_ptr = program; + + program->usage_line = usage_line; + program->start_description = start_description; + program->end_description = end_description; + program->opts = NULL; + program->opt_length = 0; + program->opt_capacity = 0; + + hxtOptProgAddOption(program, &help_option); + + return HXT_STATUS_OK; } -HXTStatus hxtOptionListDelete(HXTOptionList **list) + +/********************************************************************* +* add an option to a program * +*********************************************************************/ +HXTStatus hxtOptProgAddOption(HXTOptProgram* program, HXTOpt* opt) { - for (size_t i = 0; i < (*list)->noptions; ++i) { - free((*list)->options[i].name); - free((*list)->options[i].description); - } - for (size_t i = 0; i < (*list)->narguments; ++i) { - free((*list)->arguments[i].name); - free((*list)->arguments[i].description); - } - free((*list)->description); - if((*list)->options) - HXT_CHECK(hxtFree(&(*list)->options)); - if((*list)->arguments) - HXT_CHECK(hxtFree(&(*list)->arguments)); - HXT_CHECK(hxtFree(list)); - return HXT_STATUS_OK; + HXT_ASSERT( program!=NULL ); + HXT_ASSERT( opt!=NULL ); + + if(program->opt_length >= program->opt_capacity){ + size_t newSize = program->opt_length?program->opt_length*2:16; + HXT_CHECK( hxtRealloc(&program->opts, + sizeof(HXTOpt*) * newSize ) ); + program->opt_capacity = newSize; + } + + program->opts[program->opt_length++] = opt; + + return HXT_STATUS_OK; +} + + +/********************************************************************* +* delete a program * +*********************************************************************/ +HXTStatus hxtOptProgDelete(HXTOptProgram* program){ + HXT_ASSERT( program!=NULL ); + HXT_CHECK( hxtFree(&program->opts) ); + HXT_CHECK( hxtFree(&program) ); + + return HXT_STATUS_OK; } -HXTStatus hxtOptionListAdd(HXTOptionList *list, const char *name, const char *description, hxtOptionType otype, HXTOptionFlag oflag, void *data) + +/********************************************************************* +* search a long option inside the lists of a program * +*********************************************************************/ +static int searchLongOption(HXTOptProgram* program, + const char* string) { - return hxtOptionListAddCallback(list, name, description, otype, oflag, data, NULL, NULL); + for (int i=0; i<program->opt_length; i++) { + if(program->opts[i]->longs!=NULL && + strcmp(program->opts[i]->longs, string)==0) { + return i; + } + } + + return -1; } -HXTStatus hxtOptionListAddCallback(HXTOptionList *list, const char *name, const char *description, hxtOptionType otype, HXTOptionFlag oflag, void *data, HXTOptionCallback *cb, void *cbdata) + +/********************************************************************* +* search a short option inside the lists of a program * +*********************************************************************/ +static int searchShortOption(HXTOptProgram* program, + char c) { - hxtOption *opt; - if (oflag & HXT_OPTION_ARGUMENT) { - if(list->lastArgumentIsRepeated) - return HXT_ERROR_MSG(HXT_STATUS_FAILED, "hxt options : cannot add an argument after a repeated command line argument (trying to add '%s' after '%s').", name, list->arguments[list->narguments-1].name); - list->narguments++; - HXT_CHECK(hxtRealloc(&list->arguments, list->narguments*sizeof(hxtOption))); - opt = &list->arguments[list->narguments-1]; - if (oflag & HXT_OPTION_REPEATED_ARGUMENT) { - if (!cb) - return HXT_ERROR_MSG(HXT_STATUS_FAILED, "hxt options : a repeated argument without callback does not make sense"); - list->lastArgumentIsRepeated = 1; - } - } - else { - list->noptions++; - HXT_CHECK(hxtRealloc(&list->options, list->noptions*sizeof(hxtOption))); - opt = &list->options[list->noptions-1]; - } - opt->name = strdup(name); - opt->description = strdup(description); - opt->type = otype; - opt->data = data; - opt->cb = cb; - opt->cbdata = cbdata; - return HXT_STATUS_OK; + for (int i=0; i<program->opt_length; i++) { + if(program->opts[i]->shorts!=NULL && + strchr(program->opts[i]->shorts, c)!=NULL) { + return i; + } + } + + return 0; } -static HXTStatus hxtOptionParse(hxtOption *option, const char *val) { - switch(option->type) { - case HXT_OPTION_INT : - if(sscanf(val, "%d", (int*)option->data) == 1) - return HXT_STATUS_OK; - break; - case HXT_OPTION_DOUBLE : - if(sscanf(val, "%le", (double*)option->data) == 1) - return HXT_STATUS_OK; - break; - case HXT_OPTION_STRING : - { - char *sdata = *(char**)option->data; - if (sdata) - hxtFree(&sdata); - HXT_CHECK(hxtMalloc(&sdata, sizeof(char)*(strlen(val)+1))); - strcpy(sdata, val); - *(char**)option->data = sdata; - return HXT_STATUS_OK; - } - break; - default : - return HXT_ERROR_MSG(HXT_STATUS_FAILED, "unknown option type for option %s", option->name); - }; - return HXT_ERROR_MSG(HXT_STATUS_FAILED, "cannot parse option \"%s\" with value \"s\"", option->name, val); +static inline const char* getArgTypeName(ARG_TYPE t){ + switch(t) { + case ARG_INT64 : + return "a 64-bit integer"; + case ARG_INT32 : + return "a 32-bit integer"; + case ARG_UINT32 : + return "a 32-bit unsigned integer"; + case ARG_DOUBLE : + return "a float"; + case ARG_FLOAT : + return "a double"; + case ARG_POSITIVE_FLOAT : + return "a positive float"; + case ARG_POSITIVE_DOUBLE : + return "a positive double"; + case ARG_STRING : + return "a string"; + default: + if(t>0) + return "an integer"; + else + return "a string"; + } } -static HXTStatus hxtOptionRunCallback(hxtOption *option) { - if (!option->cb) - return HXT_STATUS_OK; - return option->cb(option->data, option->cbdata); + +/********************************************************************* +* scan parameter and execute callback function for user options * +*********************************************************************/ +static HXTStatus doOption(HXTOpt* opt, + const char* arg, + void* state, + char* optName) +{ + char* endptr = NULL; + + int64_t integer = INT64_MIN; + double real = NAN; + const char* string = "\a"; + + + if(opt->argRequirement&1 || + (opt->argRequirement==ARG_OPTIONAL && arg!=NULL)) { + if(opt->argType>-4) + integer = strtol(arg, &endptr, 0); + else if(opt->argType>-8) + real = strtod(arg, &endptr); + else + string = arg; + + + if(errno == ERANGE){ + return HXT_ERROR_MSG(HXT_STATUS_RANGE_ERROR, + "cannot convert argument \"%s\" of option \"%s\" to %s (range overflow)", + arg, optName, getArgTypeName(opt->argType)); + } + else if (arg == endptr) + { + return HXT_ERROR_MSG(HXT_STATUS_FORMAT_ERROR, + "cannot convert argument \"%s\" of option \"%s\" to %s (no digit)", + arg, optName, getArgTypeName(opt->argType)); + } + else if(endptr!=NULL && *endptr!='\0') { + return HXT_ERROR_MSG(HXT_STATUS_FORMAT_ERROR, + "cannot convert argument \"%s\" of option \"%s\" to %s (trailing unvalid characters)", + arg, optName, getArgTypeName(opt->argType)); + } + + switch(opt->argType){ + case ARG_POSITIVE_INT64 : + if(integer<0) + return HXT_ERROR_MSG(HXT_STATUS_RANGE_ERROR, + "cannot convert argument \"%s\" of option \"%s\" to %s (value was negative)", + arg, optName, getArgTypeName(opt->argType)); + break; + case ARG_INT32: + { + int32_t i32 = integer; + if(i32!=integer) + return HXT_ERROR_MSG(HXT_STATUS_RANGE_ERROR, + "cannot convert argument \"%s\" of option \"%s\" to %s ((int32_t)%ld!=%ld)", + arg, optName, getArgTypeName(opt->argType), integer, integer); + } + break; + case ARG_UINT32: + { + uint32_t u32 = integer; + if(u32!=integer) + return HXT_ERROR_MSG(HXT_STATUS_RANGE_ERROR, + "cannot convert argument \"%s\" of option \"%s\" to %s ((uint32_t)%ld!=%ld)", + arg, optName, getArgTypeName(opt->argType), integer, integer); + } + break; + case ARG_POSITIVE_DOUBLE: + if(real<0.0) + return HXT_ERROR_MSG(HXT_STATUS_RANGE_ERROR, + "cannot convert argument \"%s\" of option \"%s\" to %s (value was negative)", + arg, optName, getArgTypeName(opt->argType)); + break; + case ARG_POSITIVE_FLOAT: + if(real<0.0) + return HXT_ERROR_MSG(HXT_STATUS_RANGE_ERROR, + "cannot convert argument \"%s\" of option \"%s\" to %s (value was negative)", + arg, optName, getArgTypeName(opt->argType)); + case ARG_FLOAT: + { + float f32 = real; + if(f32!=real) + return HXT_ERROR_MSG(HXT_STATUS_RANGE_ERROR, + "cannot convert argument \"%s\" of option \"%s\" to %s ((float)%f!=%f)", + arg, optName, getArgTypeName(opt->argType), real, real); + } + break; + default: + if(opt->argType>0 && ( integer > opt->argType || integer<0 ) ) + return HXT_ERROR_MSG(HXT_STATUS_RANGE_ERROR, + "cannot convert argument \"%s\" of option \"%s\" to an integer between 0 and %d", + arg, optName, opt->argType); + break; + + } + } + + if(opt->argType>-4) + opt->integer = integer; + else if(opt->argType>-8) + opt->real = real; + else + opt->string = string; + + if(opt->cb!=NULL){ + HXTStatus err = opt->cb(opt, state); + if(err<0){ + return HXT_ERROR_MSG(HXT_STATUS_ERROR, + "argument \"%s\" of option \"%s\" triggered an error in callback", + arg, optName); + } + } + + return HXT_STATUS_OK; } -static HXTStatus hxtOptionPrint(hxtOption *option) { - switch(option->type) { - case HXT_OPTION_INT : - printf("%i", *(int*)option->data); - return HXT_STATUS_OK; - case HXT_OPTION_DOUBLE : - printf("%g", *(double*)option->data); - return HXT_STATUS_OK; - case HXT_OPTION_STRING : - printf("\"%s\"", *(char**)option->data); - return HXT_STATUS_OK; - case HXT_OPTION_FLAG : - printf("%s", (*(int*)option->data)?"on":"off"); - return HXT_STATUS_OK; - default : - return HXT_ERROR_MSG(HXT_STATUS_FAILED, "unknown option type for option %s", option->name); - }; + +HXTStatus hxtOptCalledWithoutArg(HXTOpt* opt){ + if(opt->argType>-4) // integer type + return (opt->integer==INT64_MIN); + else if(opt->argType>-8) + return (opt->real==NAN); + else + return (opt->string[0]=='\a'); } -static HXTStatus hxtOptionListParseArgvCheck(HXTOptionList *options, int argc, char **argv, const char *description) + +/********************************************************************* +* Function parsing argv * +*********************************************************************/ +HXTStatus hxtOptProgParse(HXTOptProgram* program, + const int argc, + char* argv[], + void* state, + int* optind) { - int p=1; - int iarg=0; - while (p < argc) { - if (strlen(argv[p]) > 2 && strncmp(argv[p],"--",2) == 0) { - size_t i; - const char *optname; - if (strlen(argv[p]) > 5 && strncmp("--no-",argv[p],5) == 0) { - optname = argv[p]+5; - for (i = 0; i < options->noptions; ++i) { - hxtOption *opt = &options->options[i]; - if(!strcmp(optname, opt->name) && opt->type == HXT_OPTION_FLAG) { - *(int*)opt->data = 0; - HXT_CHECK(hxtOptionRunCallback(opt)); - break; - } - } - } - else { - optname = argv[p]+2; - for (i = 0; i < options->noptions; ++i) { - hxtOption *opt = &options->options[i]; - if(!strcmp(optname, opt->name)) { - if (opt->type == HXT_OPTION_FLAG) - *(int*)opt->data = 1; - else{ - HXT_CHECK(hxtOptionParse(opt, argv[p+1])); - HXT_CHECK(hxtOptionRunCallback(opt)); - p++; - } - break; - } - } - } - if (i == options->noptions) - return HXT_ERROR_MSG(HXT_STATUS_FAILED, "unknown command line option \"%s\"", optname); - } - else { - if ((size_t)iarg == options->narguments && options->lastArgumentIsRepeated) - iarg--; - if ((size_t)iarg >= options->narguments) - return HXT_ERROR_MSG(HXT_STATUS_FAILED, "too many command line arguments (\"%s\")", argv[p]); - hxtOption *opt = &options->arguments[iarg]; - HXT_CHECK(hxtOptionParse(opt, argv[p])); - HXT_CHECK(hxtOptionRunCallback(opt)); - iarg++; - } - p++; - } - if ((size_t)iarg != options->narguments) - return HXT_ERROR_MSG(HXT_STATUS_FAILED, "missing command line arguments (\"%s\")", options->arguments[iarg].name); - return HXT_STATUS_OK; + HXT_ASSERT(program!=NULL); + HXT_ASSERT(optind!=NULL); + + int numarg = argc; + *optind = 1; + for (int i=1; i<numarg; i++) { + char *arg = NULL; + HXTOpt* opt = NULL; + + if(argv[i][0]!='-' || (argv[i][0]=='-' && argv[i][1]=='\0')){ + /* not opt, shift everything before this string */ + arg = argv[i]; + for (int j=i; j<argc-1; j++) { + argv[j] = argv[j+1]; + } + argv[argc-1] = arg; + numarg--; + i--; + } + else if(argv[i][1]=='-') { /* long opt */ + if(argv[i][2]=='\0') /* -- terminate argument parsing */ + return HXT_STATUS_OK; + + char* equalSign = strchr(argv[i]+2,'='); + + if(equalSign!=NULL){ + *equalSign = '\0'; + arg = equalSign + 1; + } + + int num = searchLongOption(program, argv[i]+2); + if(num<0){ + return HXT_ERROR_MSG(HXT_STATUS_FORMAT_ERROR, + "option \"%s\" not found", argv[i]); + } + opt = program->opts[num]; + + if(equalSign!=NULL){ + *equalSign = '='; + if(opt->argRequirement==ARG_NONE){ + return HXT_ERROR_MSG(HXT_STATUS_FORMAT_ERROR, + "option \"%s\" takes no argument", argv[i]); + } + } + else{ + if(num==0) { // it's the help option + *optind = -1; + return HXT_STATUS_OK; + } + if(opt->argRequirement&1 && numarg<=i+1){ + return HXT_ERROR_MSG(HXT_STATUS_FORMAT_ERROR, + "options \"%s\" requires an argument", argv[i]); + } + else if(numarg>i+1 && (opt->argRequirement&1 || argv[i+1][0]!='-')) { + arg = argv[i+1]; + i++; + } + } + + HXT_CHECK( doOption(opt, arg, state, argv[i]) ); + } + else{ /* short option */ + int cond = argv[i][1]; + for(int j=1; cond; j++){ + int num = searchShortOption(program, argv[i][j]); + if(num<0){ + return HXT_ERROR_MSG(HXT_STATUS_FORMAT_ERROR, + "option \'%c\' in \"%s\" not found", argv[i][j], argv[i]); + } + + char optName[] = "- "; + optName[1] = argv[i][j]; + cond = argv[i][j+1]; + + if(num==0){ + *optind = -1; + return HXT_STATUS_OK; + } + + opt = program->opts[num]; + + if(opt->argRequirement!=ARG_NONE){ + if(cond){ + arg = argv[i] + j+1; + cond = 0; // stop the loop + } + else if(opt->argRequirement&1 && numarg<=i+1){ + return HXT_ERROR_MSG(HXT_STATUS_FORMAT_ERROR, + "options \'%c\' in \"%s\" requires an argument", argv[i][j], argv[i]); + } + else if(numarg>i+1 && (opt->argRequirement==ARG_REQUIRED || argv[i+1][0]!='-')) { + arg = argv[i+1]; + i++; + } + } + + HXT_CHECK( doOption(opt, arg, state, optName) ); + } + } + *optind = i+1; + } + + for (int i=0; i<program->opt_length; i++) { + HXTOpt* opt = program->opts[i]; + if(opt->argRequirement==ARG_NON_NULL_REQUIRED && + ((opt->argType>-4 && opt->integer==0) || + (opt->argType>-8 && opt->real==0.0) || + (opt->argType<=-8 && opt->string==NULL) + ) + ) { + if(opt->longs!=NULL) + return HXT_ERROR_MSG(HXT_STATUS_ERROR, + "mandatory option \"--%s\" must have a non-null argument", + opt->longs); + else + return HXT_ERROR_MSG(HXT_STATUS_ERROR, + "mandatory option \'-%c\' must have a non-null argument", + opt->shorts[0]); + } + } + + return HXT_STATUS_OK; } -HXTStatus hxtOptionListParseArgv(HXTOptionList *options, int argc, char **argv, const char *description) + +#define MY_SPRINTF(...) *offset+=snprintf(text+ *offset, 16384- *offset, ## __VA_ARGS__ ) + +static void printOptionLine(HXTOpt* opt, char text[16384], int* offset) { - HXTStatus err = hxtOptionListParseArgvCheck(options, argc, argv, description); - if (err){ - printf("\n"); - HXT_CHECK(hxtOptionListPrintUsage(options, argv[0])); - printf("\n"); - return HXT_STATUS_FAILED; - } - return HXT_STATUS_OK; + int char_max = 30; + int oldOffset = *offset; + MY_SPRINTF(" "); + + int long_exist = opt->longs!=NULL && opt->longs[0]!='\0'; + + // short params + if(opt->shorts!=NULL && opt->shorts[0]!='\0'){ + int len = strlen(opt->shorts); + + for (int i=0; i<len-1; i++){ + MY_SPRINTF("-%c, ", opt->shorts[i]); + } + + if(long_exist){ + MY_SPRINTF("-%c, ", opt->shorts[len-1]); + } + else{ + MY_SPRINTF("-%c", opt->shorts[len-1]); + } + } + + if(long_exist){ + if(opt->argRequirement==ARG_NONE) + MY_SPRINTF("--%s", opt->longs); + else if(opt->argRequirement==ARG_OPTIONAL) + MY_SPRINTF("--%s[=%s]", opt->longs, opt->argName); + else + MY_SPRINTF("--%s=%s", opt->longs, opt->argName); + + + } + + int char_count = *offset - oldOffset; + if(char_count<char_max) + MY_SPRINTF("%*c", char_max - char_count, ' '); + else + MY_SPRINTF("\n%*c", char_max, ' '); + + // description + { + int i,k; + for (i=0,k=0; opt->description[i+1]!='\0'; i++, k++) { + if(opt->description[i]=='\n'){ + if(opt->description[i+1]=='\n' || opt->description[i+1]=='\0') + MY_SPRINTF("%.*s\n", k, opt->description+i-k); + else + MY_SPRINTF("%.*s\n%*c", k, opt->description+i-k, char_max+2, ' '); + k=-1; + } + } + MY_SPRINTF("%s", opt->description+i-k); + } + + // default argument + if(opt->argRequirement!=ARG_NONE && + (opt->argType!=ARG_STRING || opt->string!=NULL)) + { + MY_SPRINTF("\n%*cdefault: %s=", char_max+1, ' ', opt->argName); + if(opt->argType>-4) // integer type + MY_SPRINTF("%ld",opt->integer); + else if(opt->argType>-8) + MY_SPRINTF("%g",opt->real); + else + MY_SPRINTF("%s",opt->string); + } + + MY_SPRINTF("\n"); } -HXTStatus hxtOptionListPrintUsage(const HXTOptionList *list, const char *exename) + + +HXTStatus hxtOptProgGetHelp(HXTOptProgram* program, char text[16384]) { - printf("Usage : %s", exename); - if (list->noptions != 0) - printf(" [options]"); - for (size_t i = 0; i < list->narguments; ++i) { - printf(" %s", list->arguments[i].name); - } - if (list->lastArgumentIsRepeated) - printf(" [%s2 ...]", list->arguments[list->narguments-1].name); - printf("\n"); - printf("%s\n", list->description); - char opstr[256]; - const char* hxtOptionTypeName[] = {"double", "int", "string"}; - if (list->narguments != 0) { - printf("\nmandatory arguments :\n"); - for(size_t i = 0; i < list->narguments; ++i) { - hxtOption *opt = &list->arguments[i]; - sprintf(opstr, "%s(%s)", opt->name, hxtOptionTypeName[opt->type]); - printf(" %-20s %s\n", opstr, opt->description); - } - } - if (list->noptions != 0) { - printf("\noptions :\n"); - for(size_t i = 0; i < list->noptions; ++i) { - hxtOption *opt = &list->options[i]; - if (opt->type == HXT_OPTION_FLAG) - sprintf(opstr, "[no-]%s", opt->name); - else - sprintf(opstr, "%s %s", opt->name, hxtOptionTypeName[opt->type]); - printf(" --%-20s %s [",opstr, opt->description); - HXT_CHECK(hxtOptionPrint(opt)); - printf("]\n"); - } - } - return HXT_STATUS_OK; -} + int offsetval = 0; + int* offset = &offsetval; + if(program->usage_line) + MY_SPRINTF("%s\n\n", program->usage_line); + + if(program->start_description) + MY_SPRINTF("%s\n\n", program->start_description); + + int oldOffset = *offset; + MY_SPRINTF("Application Options:\n"); + int char_printed = *offset - oldOffset; + + MY_SPRINTF("%.*s\n", char_printed>64?63:char_printed-1, + "---------------------------------------------------------------"); + + for (int i=0; i<program->opt_length; i++) { + printOptionLine(program->opts[i], text, offset); + } + + if(program->end_description) + MY_SPRINTF("%s\n", program->end_description); + return HXT_STATUS_OK; +} \ No newline at end of file diff --git a/contrib/hxt/hxt_option.h b/contrib/hxt/hxt_option.h index d373c130b79b911e0c3a25d02e70214321a1ae19..29dab9f9728c0c2d95e9d3ef525b34afe98e47ff 100644 --- a/contrib/hxt/hxt_option.h +++ b/contrib/hxt/hxt_option.h @@ -1,64 +1,181 @@ -#ifndef HXT_OPTION_H -#define HXT_OPTION_H +#ifndef __HXT_OPT_H__ +#define __HXT_OPT_H__ -#include "hxt_api.h" -#include "hxt_message.h" +#include "hxt_tools.h" +#ifdef __cplusplus +extern "C" { +#endif -/* - * usage example: - * - -#include "hxt_option.h" +typedef struct HXTOptListStruct HXTOptList; +typedef struct HXTOptProgramStruct HXTOptProgram; -HXTStatus opencb(void *val, void *data) { - //open_file(*(char**)val); - return HXT_STATUS_OK; -} +/************** new option parsing method ****************** + + ## the program is detailed into a HXTOptProgram. + HXTOptProgram* program; + HXT_CHECK( hxtOptProgCreate(&program, + "Usage: mon_programme [options]", ## a usage line for Help + "mon_programme is a programme that doesn't do much", ## a description of the program + "Example:\n mon_programme --coucou=David" ## Examples, contact information etc. + ) ); + + ## then, we will add each possible options to this program + ## each options is detailed into a HXTOpt: + HXTOpt myOpt = { + "cC", ## short option names (each character is a short name => '-c' and '-C' refer to this option) + "coucou", ## long option name (a unique long name => '--coucou' refers to this option) + + "Prints coucou c to the screen\n" ## a description of this option + "Not usefull at all but funny", ## can span multiple lines (no comma on the previous line) + + coucouCallBack, ## a callback function called everytime the program is called + ## the prototype of 'coucouCallBack' must be: + ## HXTStatus coucouCallBack(HXTOpt* opt, void* state); + ## the option given will be 'myOpt'. + ## the state is a pointer that we give to the parsing function + + "YOUR_NAME", ## the name of the argument to show in Help + ## here Help will show: + ## -c, -C, --coucou=YOUR_NAME Prints coucou YOUR_NAME to the screen + + ARG_REQUIRED, ## one of ARG_NONE, ARG_REQUIRED, ARG_OPTIONAL and ARG_NON_NULL_REQUIRED + ## specify if an argument is required (see ARG_REQUIREMENT section) + + ARG_STRING, ## the type of the argument as described below (ARG_TYPE section) + + {.string="Joe"} ## a default value for the argument (uneeded here as the argument is required...) + }; + + ##then we add the option to the program: + HXT_CHECK( hxtOptProgAddOption(program, myOpt) ); + + ## Once all options are added, we can parse the argument. + ## The function will automatically return the status and print Help on errors + + void* state = NULL; ## this pointer will be given to each callback functions + int optind=0; ## this integer will contain the number of string processed in argv + HXT_OPT_MAIN_PARSE(program, argc, argv, NULL, &optind ); + + ## you can now use the argument provided by the user safely + printf(" Coucou %s !!!", myOpt.string); + + ## and delete the program structure + HXT_CHECK( hxtOptProgDelete(program) ); -int main(int argc, char **argv) { - - char *opts = NULL; - char *opti = NULL; - double optd = 3.; - int optb = 0; - - HXTOptionList *list; - HXT_CHECK(hxtOptionListCreate(&list, "Solve the laplace equation.")); - HXT_CHECK(hxtOptionListAddCallback(list, "filename", "file to open", HXT_OPTION_STRING, HXT_OPTION_REPEATED_ARGUMENT, &opti, opencb, NULL)); - HXT_CHECK(hxtOptionListAdd(list, "optd", "description of double option", HXT_OPTION_DOUBLE, 0, &optd)); - HXT_CHECK(hxtOptionListAdd(list, "opts", "description of string option", HXT_OPTION_STRING, 0, &opts)); - HXT_CHECK(hxtOptionListAdd(list, "optb", "description of flag option", HXT_OPTION_FLAG, 0, &optb)); - HXT_CHECK(hxtOptionListParseArgv(list, argc, argv, "laplace")); - hxtOptionListPrintUsage(list, argv[0]); - HXT_CHECK(hxtOptionListDelete(&list)); - ... - } */ -#ifdef __cplusplus -extern "C" { -#endif -typedef struct HXTOptionListStruct HXTOptionList; +/**************************************************************************************** + * ARG_REQUIREMENT : specify if an argument is required to follow the option + ***************************************************************************************/ +typedef enum { + ARG_NONE=0, /* no argument, the union value is incremented at each call */ + ARG_REQUIRED=1, /* argument required, the union value is the value of the argument */ + ARG_OPTIONAL=2, /* optional argument, the union value is: + - the value of the argument if there is an argument + - INT64_MIN for integers type + - NAN for floating point type if there was no argument + - "\a" for string types + */ + ARG_NON_NULL_REQUIRED=3 /* argument is required and the option should be non-null + at end of parsing */ +} ARG_REQUIREMENT; + -typedef enum {HXT_OPTION_DEFAULT=0, HXT_OPTION_ARGUMENT=1, HXT_OPTION_REPEATED_ARGUMENT=2} HXTOptionFlag; -typedef enum {HXT_OPTION_DOUBLE, HXT_OPTION_INT, HXT_OPTION_STRING, HXT_OPTION_FLAG} hxtOptionType; +/**************************************************************************************** + * ARG_TYPE: the accepted values of the possible argument + * (use whatever you want if argRequirement==ARG_NONE) + ***************************************************************************************/ +typedef enum { + // a positive value N means an integer between 0 and N included + ARG_POSITIVE_INT32=INT32_MAX, -typedef HXTStatus HXTOptionCallback(void *valuep, void *data); + /* Integer value types (stored in opt.integer) */ + ARG_INT64=0, // an integer, whatever fits in a int64_t + ARG_POSITIVE_INT64=-1, + ARG_INT32=-2, // the value is checked to fit in a int32_t + ARG_UINT32=-3,// the value is checked to fit in a uint32_t -HXTStatus hxtOptionListCreate(HXTOptionList **list, const char *description); -HXTStatus hxtOptionListDelete(HXTOptionList **list); + /* Floating value types (stored in opt.real) */ + ARG_DOUBLE=-4,// a double value, whatever fits in a double + ARG_FLOAT=-5, // the value is checked to fit in a float + ARG_POSITIVE_FLOAT=-6, // the value is checked to fit in a float and be positive + ARG_POSITIVE_DOUBLE=-7,// the value is checked to fit in a double and be positive -HXTStatus hxtOptionListAdd(HXTOptionList *list, const char *name, const char *description, hxtOptionType otype, HXTOptionFlag oflag, void *data); -HXTStatus hxtOptionListAddCallback(HXTOptionList *list, const char *name, const char *description, hxtOptionType otype, HXTOptionFlag oflag, void *data, HXTOptionCallback *cb, void *cbdata); + /* String value type (stored in opt.string) */ + ARG_STRING=-8 +} ARG_TYPE; -HXTStatus hxtOptionListParseArgv(HXTOptionList *options, int argc, char **argv, const char *description); -HXTStatus hxtOptionListPrintUsage(const HXTOptionList *options, const char *exename); + + +typedef struct HXTOptStruct{ + const char* const shorts; + const char* const longs; + const char* const description; + HXTStatus (*const cb)(struct HXTOptStruct* arg, void* state); + const char* const argName; + const ARG_REQUIREMENT argRequirement; + const int argType; + + int64_t integer; + double real; + const char* string; +}HXTOpt; + + + + + + +HXTStatus hxtOptProgCreate(HXTOptProgram** program_ptr, + const char* usage_line, + const char* start_description, + const char* end_description); + + +HXTStatus hxtOptProgAddOption(HXTOptProgram* program, + HXTOpt* opt); + + +HXTStatus hxtOptProgParse(HXTOptProgram* program, + const int argc, char* argv[], + void* state, + int* optind); + + +#define HXT_OPT_MAIN_PARSE(program, argc, argv, state, optind_ptr) \ + do { \ + char help[16384]; \ + hxtOptProgGetHelp(program, help); \ + HXTStatus _status = hxtOptProgParse(program, argc, argv, NULL, optind_ptr ); \ + if(_status!=HXT_STATUS_OK){ \ + HXT_TRACE(_status); \ + puts("\n ==> Help on Error\n\n ---\n\n"); \ + *optind_ptr=-1; \ + } \ + if(*optind_ptr==-1) { \ + printf("%s", help); \ + HXT_CHECK( hxtOptProgDelete(program) ); \ + return _status; \ + } \ + }while(0) + +HXTStatus hxtOptCalledWithoutArg(HXTOpt* opt); + +HXTStatus hxtOptProgGetHelp(HXTOptProgram* program, char text[16384]); + +HXTStatus hxtOptProgDelete(HXTOptProgram* program); + + +static inline HXTStatus hxtOptProgAddOptionArray(HXTOptProgram* program, + HXTOpt* opt, int n) { + for (int i=0; i<n; i++) { HXT_CHECK( hxtOptProgAddOption(program, &opt[i]) ); } + return HXT_STATUS_OK; +} #ifdef __cplusplus } #endif - #endif diff --git a/contrib/hxt/hxt_parametrization.c b/contrib/hxt/hxt_parametrization.c index fe2864e1cdfabfe43c7b4859fa2cffcb8fc0c6b7..23f1486e74d7b6d7a879898a8d486ce6fa4f059c 100644 --- a/contrib/hxt/hxt_parametrization.c +++ b/contrib/hxt/hxt_parametrization.c @@ -167,7 +167,7 @@ static HXTStatus hxtLongestEdgeBisection(HXTEdges *edges,int nrefinements) break; } } - do{ + do{ HXT_CHECK(hxtLongestEdge(e,ti,&ei,ej)); tj = e->edg2tri[2*ei+0]==ti ? e->edg2tri[2*ei+1] : e->edg2tri[2*ei+0]; HXT_CHECK(hxtLongestEdge(e,tj,&ej,ei)); @@ -275,7 +275,7 @@ static HXTStatus hxtLongestEdgeBisection(HXTEdges *edges,int nrefinements) t_[1] = ttj; } - }//end for + }//end for e->numEdges +=3; if(e->numEdges > maxEdg){ maxEdg *= 2; @@ -285,7 +285,7 @@ static HXTStatus hxtLongestEdgeBisection(HXTEdges *edges,int nrefinements) } e->node[2*ei+1] = m->vertices.num-1; e->edg2tri[2*ei+0] = ti; - e->edg2tri[2*ei+1] = ttj; + e->edg2tri[2*ei+1] = ttj; e->node[2*e0+0] = m->vertices.num-1; e->node[2*e0+1] = nj; e->edg2tri[2*e0+0] = tti; @@ -346,12 +346,12 @@ static HXTStatus hxtPartitioning(HXTEdges *edges,int *part, int nPartitions,HXTV uint64_t ce = 0; for(uint64_t ie=0; ie<mesh->triangles.num; ie++){ if(part[ie]==p){ - for(int jv=0; jv<3; jv++){ - uint32_t current = mesh->triangles.node[3*ie+jv]; - if(flagVertices[current] == (uint32_t)-1) - flagVertices[current] = cv++; - } - ce++; + for(int jv=0; jv<3; jv++){ + uint32_t current = mesh->triangles.node[3*ie+jv]; + if(flagVertices[current] == (uint32_t)-1) + flagVertices[current] = cv++; + } + ce++; } } /* @@ -365,12 +365,12 @@ static HXTStatus hxtPartitioning(HXTEdges *edges,int *part, int nPartitions,HXTV uint32_t current = flagVertices[iv]; if(current==(uint32_t)-1); else{ - msh->vertices.coord[4*current+0] = mesh->vertices.coord[4*iv+0]; - msh->vertices.coord[4*current+1] = mesh->vertices.coord[4*iv+1]; - msh->vertices.coord[4*current+2] = mesh->vertices.coord[4*iv+2]; - msh->vertices.coord[4*current+3] = mesh->vertices.coord[4*iv+3]; + msh->vertices.coord[4*current+0] = mesh->vertices.coord[4*iv+0]; + msh->vertices.coord[4*current+1] = mesh->vertices.coord[4*iv+1]; + msh->vertices.coord[4*current+2] = mesh->vertices.coord[4*iv+2]; + msh->vertices.coord[4*current+3] = mesh->vertices.coord[4*iv+3]; } - } + } msh->triangles.num = ce; //< @@ -382,25 +382,18 @@ static HXTStatus hxtPartitioning(HXTEdges *edges,int *part, int nPartitions,HXTV ce = 0; for(uint64_t ie=0; ie<mesh->triangles.num; ie++){ if(part[ie]==p){ - global[ce] = edges->global[ie]; - msh->triangles.colors[ce] = mesh->triangles.colors[ie]; - for(int jv=0; jv<3; jv++){ - uint32_t current = flagVertices[mesh->triangles.node[3*ie+jv]]; - msh->triangles.node[3*ce+jv] = current; - } - ce++; + global[ce] = edges->global[ie]; + msh->triangles.colors[ce] = mesh->triangles.colors[ie]; + for(int jv=0; jv<3; jv++){ + uint32_t current = flagVertices[mesh->triangles.node[3*ie+jv]]; + msh->triangles.node[3*ce+jv] = current; + } + ce++; } } - /* - char name[16]; - sprintf(name,"havealook%d.msh",p); - hxtMeshWriteGmsh(msh,name); - */ HXTEdges* edg = NULL; HXT_CHECK(hxtEdgesCreate(msh,&edg)); - //< edg->global = global; - //> HXT_CHECK(hxtVectorPushBack(partitions,edg)); } @@ -439,63 +432,59 @@ static HXTStatus hxtCheckConnectivity(HXTEdges *e,HXTVector *partitions) uint64_t current = queue[iq]; uint32_t *ed = &e->tri2edg[3*current]; for(int j=0; j<3; j++){ - if (e->edg2tri[2*ed[j]+1] == (uint64_t) -1) - continue; - uint64_t next = e->edg2tri[2*ed[j]] == current ? e->edg2tri[2*ed[j]+1] : e->edg2tri[2*ed[j]]; - if (flag[next] == -1){ - queue[last]= next; - last++; - idx[iq+1]++; - flag[next] = (int) count; - } + if (e->edg2tri[2*ed[j]+1] == (uint64_t) -1) + continue; + uint64_t next = e->edg2tri[2*ed[j]] == current ? e->edg2tri[2*ed[j]+1] : e->edg2tri[2*ed[j]]; + if (flag[next] == -1){ + queue[last]= next; + last++; + idx[iq+1]++; + flag[next] = (int) count; + } } } count++; goOn = 0; for(uint64_t i=0; i<m->triangles.num; i++) if (flag[i]== -1){ - goOn=1; - iq=last; - queue[last] = i; - last++; - flag[i] = count; - break; + goOn=1; + iq=last; + queue[last] = i; + last++; + flag[i] = count; + break; } } - //if(partitions!=NULL) - printf("HXT_INFO \t There is/are %d connected part(s).\n",count); + printf("HXT_INFO \t There is/are %d connected part(s).\n",count); for(uint64_t i=0; i<m->triangles.num; i++){ uint32_t *refVert = &m->triangles.node[3*queue[i]]; - //printf("REFERENCE triangle %lu (%u %u %u)\n",queue[i],refVert[0],refVert[1],refVert[2]); for(uint64_t j=idx[i]+flag[queue[i]]; j<idx[i+1]+flag[queue[i]]; j++){ int cond = 0; uint32_t *checkVert = &m->triangles.node[3*queue[j]]; - //printf("\t check tri %lu (%u %u %u)\n",queue[j],checkVert[0],checkVert[1],checkVert[2]); for(int ii=0; ii<3; ii++){ - for(int jj=0; jj<3; jj++){ - //printf("%u->%u vs %u<-%u\n",refVert[ii],refVert[(ii+1)%3],checkVert[(jj+1)%3],checkVert[jj]); - if(refVert[ii]==checkVert[(jj+1)%3] && refVert[(ii+1)%3]==checkVert[jj]){ - cond = 1; - break; - } - } - if(cond==1) - break; + for(int jj=0; jj<3; jj++){ + if(refVert[ii]==checkVert[(jj+1)%3] && refVert[(ii+1)%3]==checkVert[jj]){ + cond = 1; + break; + } + } + if(cond==1) + break; }//end for ii - if(cond==0){ - printf("Unconsistent orientation \t %lu (%u %u %u) ; (%u %u)-(%u %u)-(%u %u) [%d] ~ %lu (%u %u %u) ; (%u %u)-(%u %u)-(%u %u) [%d].\n",queue[i],refVert[0],refVert[1],refVert[2],e->node[2*e->tri2edg[3*queue[i]+0]+0],e->node[2*e->tri2edg[3*queue[i]+0]+1],e->node[2*e->tri2edg[3*queue[i]+1]+0],e->node[2*e->tri2edg[3*queue[i]+1]+1],e->node[2*e->tri2edg[3*queue[i]+2]+0],e->node[2*e->tri2edg[3*queue[i]+2]+1],flag[queue[i]],queue[j],checkVert[0],checkVert[1],checkVert[2],e->node[2*e->tri2edg[3*queue[j]+0]+0],e->node[2*e->tri2edg[3*queue[j]+0]+1],e->node[2*e->tri2edg[3*queue[j]+1]+0],e->node[2*e->tri2edg[3*queue[j]+1]+1],e->node[2*e->tri2edg[3*queue[j]+2]+0],e->node[2*e->tri2edg[3*queue[j]+2]+1],flag[queue[j]]); - uint64_t temp = m->triangles.node[3*queue[j]+0]; - m->triangles.node[3*queue[j]+0] = m->triangles.node[3*queue[j]+1]; - m->triangles.node[3*queue[j]+1] = temp; - uint32_t te = e->tri2edg[3*queue[j]+1]; - e->tri2edg[3*queue[j]+1] = e->tri2edg[3*queue[j]+2]; - e->tri2edg[3*queue[j]+2] = te; - - printf("Consistent (?) orientation \t %lu (%u %u %u) ; (%u %u)-(%u %u)-(%u %u) [%d] ~ %lu (%u %u %u) ; (%u %u)-(%u %u)-(%u %u) [%d].\n",queue[i],refVert[0],refVert[1],refVert[2],e->node[2*e->tri2edg[3*queue[i]+0]+0],e->node[2*e->tri2edg[3*queue[i]+0]+1],e->node[2*e->tri2edg[3*queue[i]+1]+0],e->node[2*e->tri2edg[3*queue[i]+1]+1],e->node[2*e->tri2edg[3*queue[i]+2]+0],e->node[2*e->tri2edg[3*queue[i]+2]+1],flag[queue[i]],queue[j],checkVert[0],checkVert[1],checkVert[2],e->node[2*e->tri2edg[3*queue[j]+0]+0],e->node[2*e->tri2edg[3*queue[j]+0]+1],e->node[2*e->tri2edg[3*queue[j]+1]+0],e->node[2*e->tri2edg[3*queue[j]+1]+1],e->node[2*e->tri2edg[3*queue[j]+2]+0],e->node[2*e->tri2edg[3*queue[j]+2]+1],flag[queue[j]]); - + if(cond==0){ + printf("Unconsistent orientation \t %lu (%u %u %u) ; (%u %u)-(%u %u)-(%u %u) [%d] ~ %lu (%u %u %u) ; (%u %u)-(%u %u)-(%u %u) [%d].\n",queue[i],refVert[0],refVert[1],refVert[2],e->node[2*e->tri2edg[3*queue[i]+0]+0],e->node[2*e->tri2edg[3*queue[i]+0]+1],e->node[2*e->tri2edg[3*queue[i]+1]+0],e->node[2*e->tri2edg[3*queue[i]+1]+1],e->node[2*e->tri2edg[3*queue[i]+2]+0],e->node[2*e->tri2edg[3*queue[i]+2]+1],flag[queue[i]],queue[j],checkVert[0],checkVert[1],checkVert[2],e->node[2*e->tri2edg[3*queue[j]+0]+0],e->node[2*e->tri2edg[3*queue[j]+0]+1],e->node[2*e->tri2edg[3*queue[j]+1]+0],e->node[2*e->tri2edg[3*queue[j]+1]+1],e->node[2*e->tri2edg[3*queue[j]+2]+0],e->node[2*e->tri2edg[3*queue[j]+2]+1],flag[queue[j]]); + uint64_t temp = m->triangles.node[3*queue[j]+0]; + m->triangles.node[3*queue[j]+0] = m->triangles.node[3*queue[j]+1]; + m->triangles.node[3*queue[j]+1] = temp; + uint32_t te = e->tri2edg[3*queue[j]+1]; + e->tri2edg[3*queue[j]+1] = e->tri2edg[3*queue[j]+2]; + e->tri2edg[3*queue[j]+2] = te; + + printf("Consistent (?) orientation \t %lu (%u %u %u) ; (%u %u)-(%u %u)-(%u %u) [%d] ~ %lu (%u %u %u) ; (%u %u)-(%u %u)-(%u %u) [%d].\n",queue[i],refVert[0],refVert[1],refVert[2],e->node[2*e->tri2edg[3*queue[i]+0]+0],e->node[2*e->tri2edg[3*queue[i]+0]+1],e->node[2*e->tri2edg[3*queue[i]+1]+0],e->node[2*e->tri2edg[3*queue[i]+1]+1],e->node[2*e->tri2edg[3*queue[i]+2]+0],e->node[2*e->tri2edg[3*queue[i]+2]+1],flag[queue[i]],queue[j],checkVert[0],checkVert[1],checkVert[2],e->node[2*e->tri2edg[3*queue[j]+0]+0],e->node[2*e->tri2edg[3*queue[j]+0]+1],e->node[2*e->tri2edg[3*queue[j]+1]+0],e->node[2*e->tri2edg[3*queue[j]+1]+1],e->node[2*e->tri2edg[3*queue[j]+2]+0],e->node[2*e->tri2edg[3*queue[j]+2]+1],flag[queue[j]]); + }//end if cond==0 }//end for j }//end for i @@ -537,11 +526,11 @@ static HXTStatus hxtSplitEdges(HXTEdges *edges,int nPartitions,HXTVector *partit for(int j=0; j<3; j++){ uint64_t *local = &edg2tri[2*current[j]]; if(local[1]==(uint64_t)-1) - continue; - else{ - nbh[idx[i]+temp] = i == (int) local[0] ? (int) local[1] : (int) local[0]; - weights[idx[i]+temp] = (int) (hxtEdgesLength(edges,current[j])+1); - temp++; + continue; + else{ + nbh[idx[i]+temp] = i == (int) local[0] ? (int) local[1] : (int) local[0]; + weights[idx[i]+temp] = (int) (hxtEdgesLength(edges,current[j])+1); + temp++; } } @@ -663,10 +652,7 @@ HXTStatus hxtParametrizationCreate(HXTMesh *mesh, int nrefinements, HXTParametri HXTMeanValues *param; HXTEdges *current = (HXTEdges *)(toparam->ptr[i]); HXT_CHECK(hxtMeanValuesCreate(current,¶m)); - //printf("creation (%d/%d): ok\n",i,toparam->last); HXT_CHECK(hxtMeanValuesCompute(param)); - //printf("%d \t ntottri = %ld\n",i,param0->edges->edg2mesh->triangles.num); - //printf("computation: ok\n"); int ar; HXT_CHECK(hxtMeanValueAspectRatio(param,&ar)); @@ -678,20 +664,10 @@ HXTStatus hxtParametrizationCreate(HXTMesh *mesh, int nrefinements, HXTParametri hxtVectorFree(&toparam); param0->n = atlas->last+1; - //printf("n=%d\n",param0->n); HXT_CHECK(hxtMalloc(¶m0->maps,param0->n*sizeof(HXTMeanValues*))); - for(int i=0; i<param0->n; i++){ + for(int i=0; i<param0->n; i++) param0->maps[i] = (HXTMeanValues *) atlas->ptr[i]; - /* - char str0[64]; - sprintf(str0, "param_%d.msh",i); - HXT_CHECK(hxtMeanValuesWrite(param0->maps[i],str0)); - char str1[64]; - sprintf(str1, "paramMesh_%d.msh",i); - HXT_CHECK(hxtMeanValuesWriteParamMesh(param0->maps[i],str1)); - printf("writing (%d/%d): ok\n",i,param0->n-1); - */ - } + HXT_CHECK(hxtVectorFree(&atlas)); return HXT_STATUS_OK; } @@ -749,8 +725,8 @@ HXTStatus hxtParametrizationCompute(HXTParametrization *parametrization, int **_ for(int ie=0; ie<ne; ie++){ for(int kk=0; kk<3; kk++){ - int gvn = (int) parametrization->edges->edg2mesh->triangles.node[3*global[ie]+kk]; - nodes[nNodes[c]+gn[3*ie+kk]] = gvn; + int gvn = (int) parametrization->edges->edg2mesh->triangles.node[3*global[ie]+kk]; + nodes[nNodes[c]+gn[3*ie+kk]] = gvn; } } diff --git a/contrib/hxt/hxt_parametrization_old.c b/contrib/hxt/hxt_parametrization_old.c deleted file mode 100644 index 40eaf64714783d226b735e09c0f4cf9e180c8aad..0000000000000000000000000000000000000000 --- a/contrib/hxt/hxt_parametrization_old.c +++ /dev/null @@ -1,412 +0,0 @@ -#include "hxt_parametrization.h" -#include "hxt_mean_values.h" -#include "hxt_edge.h" -#include "metis.h" - - -/* - -// # O U T P U T - -int* colors; //gives colors of elements: colors[i] = color of i-th element -int* nNodes; //gives lower index of int* nodes for a color: nNodes[i] = first node index of i-th color -int* nodes; //gives the global index of initial 'mesh': nodes[i] = if i=nNodes[c]+k, gives the global index of k-th node of c-th color -double* uv; //give the uv param coordinates of a local node: uv[2*i+p] = u/v component of the global k-node of the c-th color - - */ - - - - -struct HXTVectorStruct{ - void **ptr; - size_t size; - int last; - int length; -}; - - -static HXTStatus hxtVectorInit(HXTVector **vecptr){ - - - HXT_CHECK(hxtMalloc(vecptr,sizeof(HXTVector))); - - HXTVector *new = *vecptr; - - new->ptr = NULL; - new->last = -1; - new->length = 0; - - return HXT_STATUS_OK; -} - - -static HXTStatus hxtVectorFree(HXTVector **vecptr){ - - HXTVector *vector = *vecptr; - - HXT_CHECK(hxtFree(&vector->ptr)); - - HXT_CHECK(hxtFree(vecptr)); - - return HXT_STATUS_OK; - -} - -static HXTStatus hxtVectorPushBack(HXTVector *vector, void *object){ - - int i = vector->last; - int length = vector->length; - - if(i+1 >= length){ - if(length==0) - length=1; - HXT_CHECK(hxtRealloc(&vector->ptr,2*(length)*sizeof(void *))); - vector->length = 2*length; - } - vector->ptr[i+1] = object; - vector->last = i+1; - - return HXT_STATUS_OK; -} - - -/*---------------------------------------*/ - - - -struct HXTParametrizationStruct{ - HXTEdges *edges; - int n; - HXTMeanValues **maps; -}; - -static HXTStatus hxtSplitEdges(HXTEdges *edges,int nPartitions,HXTVector *partitions) -{ - - HXTMesh *mesh = edges->edg2mesh; - - uint32_t *tri2edg = edges->tri2edg; - uint64_t *edg2tri = edges->edg2tri; - - HXTBoundaries *boundaries; - HXT_CHECK(hxtEdgesSetBoundaries(edges, &boundaries)); - int nbedg; - HXT_CHECK(hxtBoundariesGetNumberOfBorderEdges(boundaries,&nbedg)); - - int nVertex = (int) mesh->triangles.num; - int nEdge = (int) (edges->numEdges) - nbedg; - - int *idx=NULL; - HXT_CHECK(hxtMalloc(&idx,(nVertex+1)*sizeof(int))); - int *nbh=NULL; - HXT_CHECK(hxtMalloc(&nbh, 2*nEdge*sizeof(int))); - - idx[0] = 0; - for(int i=0; i<nVertex; i++){// triangle by triangle - uint32_t *current = &tri2edg[3*i]; - int temp = 0; - for(int j=0; j<3; j++){ - uint64_t *local = &edg2tri[2*current[j]]; - if(local[1]==-1){}//printf("ele %d (s1)\n",i+1);} - else{ - nbh[idx[i]+temp] = i == (int) local[0] ? (int) local[1] : (int) local[0]; - temp++; - } - } - - idx[i+1] = idx[i] + temp; - } - - - // int options[5]; - // options[0] = 1; - // options[1] = 1; - // options[2] = 1; - // options[3] = 1; - // options[4] = 0; - int wgtflag = 0; - int numflag = 0; - - // only for k-way - idx_t options[METIS_NOPTIONS]; - METIS_SetDefaultOptions(options); - options[METIS_OPTION_NCUTS] = nPartitions; - options[METIS_OPTION_MINCONN] = 1; - options[METIS_OPTION_CONTIG] = 1; - - int edgeCut; - int *part; - HXT_CHECK(hxtMalloc(&part,nVertex*sizeof(int))); - //int zero = 0; - int one=1; - //METIS_PartGraphRecursive(&nVertex,&one,idx,nbh,NULL,NULL,NULL,&nPartitions,NULL,NULL,NULL,&edgeCut,part); - // METIS_PartGraphKway(&nVertex,idx,nbh,NULL,NULL,&wgtflag, &numflag,&nPartitions,options,&edgeCut,part); - // METIS_PartGraphKway(&nVertex,&one,idx,nbh,NULL,NULL,weights,&nPartitions,NULL,NULL,options,&edgeCut,part); - METIS_PartGraphKway(&nVertex,&one,idx,nbh,NULL,NULL,NULL,&nPartitions,NULL,NULL,options,&edgeCut,part); - // METIS_PartGraphKway(&nVertex,&one,idx,nbh,NULL,NULL,NULL,&nPartitions,NULL,NULL,options,&edgeCut,part); - - HXT_CHECK(hxtFree(&nbh)); - HXT_CHECK(hxtFree(&idx)); - - - uint32_t *flagVertices; - HXT_CHECK(hxtMalloc(&flagVertices,mesh->vertices.num*sizeof(uint32_t))); - for(int p = 0; p<nPartitions; p++){ - HXTMesh *msh=NULL; - HXT_CHECK(hxtMeshCreate(mesh->ctx,&msh)); - - - for(uint32_t iv=0; iv<mesh->vertices.num; iv++) - flagVertices[iv] = -1; - - uint32_t cv = 0; - uint64_t ce = 0; - for(uint64_t ie=0; ie<mesh->triangles.num; ie++){ - if(part[ie]==p){ - for(int jv=0; jv<3; jv++){ - uint32_t current = mesh->triangles.node[3*ie+jv]; - if(flagVertices[current] == -1) - flagVertices[current] = cv++; - } - ce++; - } - } - /* - printf("------------stat \t part %d---------------\n",p); - printf("\t number of vertices:%u\n",cv); - printf("\t number of elements:%lu\n",ce); - */ - msh->vertices.num = cv; - HXT_CHECK(hxtAlignedMalloc(&msh->vertices.coord,4*cv*sizeof(double))); - for(uint32_t iv=0; iv<mesh->vertices.num; iv++){ - uint32_t current = flagVertices[iv]; - if(current==-1); - else{ - msh->vertices.coord[4*current+0] = mesh->vertices.coord[4*iv+0]; - msh->vertices.coord[4*current+1] = mesh->vertices.coord[4*iv+1]; - msh->vertices.coord[4*current+2] = mesh->vertices.coord[4*iv+2]; - msh->vertices.coord[4*current+3] = mesh->vertices.coord[4*iv+3]; - } - } - - msh->triangles.num = ce; - //< - uint64_t *global; - HXT_CHECK(hxtMalloc(&global,ce*sizeof(uint64_t))); - //> - HXT_CHECK( hxtAlignedMalloc(&msh->triangles.node,ce*3*sizeof(uint32_t)) ); - HXT_CHECK( hxtAlignedMalloc(&msh->triangles.colors,ce*sizeof(uint16_t)) ); - ce = 0; - for(uint64_t ie=0; ie<mesh->triangles.num; ie++){ - if(part[ie]==p){ - global[ce] = edges->global[ie]; - msh->triangles.colors[ce] = mesh->triangles.colors[ie]; - for(int jv=0; jv<3; jv++){ - uint32_t current = flagVertices[mesh->triangles.node[3*ie+jv]]; - msh->triangles.node[3*ce+jv] = current; - } - ce++; - } - } - HXTEdges* edg = NULL; - HXT_CHECK(hxtEdgesCreate(msh,&edg)); - //< - edg->global = global; - //> - HXT_CHECK(hxtVectorPushBack(partitions,edg)); - } - return HXT_STATUS_OK; -} - -static HXTStatus hxtCuttingProcess(HXTEdges *edges, int ar, int bool, HXTVector *toparam) -{ - - HXTBoundaries *boundaries; - HXT_CHECK(hxtEdgesSetBoundaries(edges, &boundaries)); - int nll; - HXT_CHECK(hxtBoundariesGetNumberOfLineLoops(boundaries,&nll)); - int g = (edges->numEdges - edges->edg2mesh->vertices.num - edges->edg2mesh->triangles.num + 2 - nll)/2; - - HXTVector *toSplit; - HXT_CHECK(hxtVectorInit(&toSplit)); - if(ar==0 || g!=0 || nll == 0) - HXT_CHECK(hxtVectorPushBack(toSplit,edges)); - else - HXT_CHECK(hxtVectorPushBack(toparam,edges)); - for(int i=0; i<toSplit->last+1; i++){ - HXTVector *split=NULL; - HXT_CHECK(hxtVectorInit(&split)); - HXTEdges *beingSplitted = (HXTEdges*) toSplit->ptr[i]; - HXT_CHECK(hxtSplitEdges(beingSplitted,2,split)); - if(bool==1){ - HXT_CHECK(hxtMeshDelete(&beingSplitted->edg2mesh)); - HXT_CHECK(hxtEdgesDelete(&beingSplitted)); - } - else - bool=1; - for(int j=0; j<split->last+1; j++){ - HXTEdges* piece = (HXTEdges*) split->ptr[j]; - HXT_CHECK(hxtEdgesSetBoundaries(piece, &boundaries)); - HXT_CHECK(hxtBoundariesGetNumberOfLineLoops(boundaries,&nll)); - g = (piece->numEdges - piece->edg2mesh->vertices.num - piece->edg2mesh->triangles.num + 2 - nll)/2; - if(g!=0 || nll==0){ - HXT_CHECK(hxtVectorPushBack(toSplit,piece)); - } - else - HXT_CHECK(hxtVectorPushBack(toparam,piece)); - } - HXT_CHECK(hxtVectorFree(&split)); - } - HXT_CHECK(hxtVectorFree(&toSplit)); - - // free boundaries - - return HXT_STATUS_OK; -} - -HXTStatus hxtParametrizationCreate(HXTMesh *mesh, HXTParametrization **parametrization) -{ - HXTParametrization* param; - HXT_CHECK(hxtMalloc(¶m,sizeof(HXTParametrization))); - *parametrization = param; - - HXTMesh *initialMesh=NULL; - HXT_CHECK(hxtMeshCreate(mesh->ctx,&initialMesh)); - *initialMesh = *mesh; - HXT_CHECK(hxtEdgesCreate(initialMesh, ¶m->edges)); - - HXTEdges *edges=NULL; - HXT_CHECK(hxtEdgesCreate(initialMesh, &edges)); - uint64_t *global; - HXT_CHECK(hxtMalloc(&global,edges->edg2mesh->triangles.num*sizeof(uint64_t))); - for(uint64_t i=0; i<edges->edg2mesh->triangles.num; i++) - global[i]= i; - edges->global = global; - param->edges->global = global; - HXTVector *toparam; - HXT_CHECK(hxtVectorInit(&toparam)); - HXT_CHECK(hxtCuttingProcess(edges,1,0,toparam)); - - HXTVector *atlas; - HXT_CHECK(hxtVectorInit(&atlas)); - for(int i=0; i<toparam->last+1; i++){ - HXTMeanValues *param; - HXTEdges *current = (HXTEdges *)(toparam->ptr[i]); - HXT_CHECK(hxtMeanValuesCreate(current,¶m)); - HXT_CHECK(hxtMeanValuesCompute(param)); - int ar; - HXT_CHECK(hxtMeanValueAspectRatio(param,&ar)); - - if (ar==0){ - //printf("wrong aspect ratio!\n"); - HXT_CHECK(hxtCuttingProcess(current,ar,1,toparam)); - //printf("splitting process: ok\n"); - } - else - HXT_CHECK(hxtVectorPushBack(atlas,param)); - } - hxtVectorFree(&toparam); - - - - param->n = atlas->last+1; - //printf("n=%d\n",param->n); - HXT_CHECK(hxtMalloc(¶m->maps,param->n*sizeof(HXTMeanValues*))); - for(int i=0; i<param->n; i++){ - param->maps[i] = (HXTMeanValues *) atlas->ptr[i]; - // char str0[64]; - // sprintf(str0, "param_%d.msh",i); - // HXT_CHECK(hxtMeanValuesWrite(param->maps[i],str0)); - // char str1[64]; - // sprintf(str1, "paramMesh_%d.msh",i); - // HXT_CHECK(hxtMeanValuesWriteParamMesh(param->maps[i],str1)); - //printf("writing (%d/%d): ok\n",i,param->n-1); - } - HXT_CHECK(hxtVectorFree(&atlas)); - return HXT_STATUS_OK; -} - - - -HXTStatus hxtParametrizationDelete(HXTParametrization **parametrization) -{ - - return HXT_STATUS_OK; -} - -HXTStatus hxtParametrizationCompute(HXTParametrization *parametrization, int **_colors_, int **_nNodes_, int **_nodes_, double **_uv_, int *nc) -{ - - int *colors=NULL, *nNodes=NULL, *nodes=NULL; - double *uv=NULL; - - int Ncolors = parametrization->n; - *nc=Ncolors; - int totalNtriangles = (int) parametrization->edges->edg2mesh->triangles.num; - //int totalNvertices = parametrization->edges->edg2mesh->vertices.num; - - HXT_CHECK(hxtMalloc(&colors,totalNtriangles*sizeof(int))); - HXT_CHECK(hxtMalloc(&nNodes,(Ncolors+1)*sizeof(int))); - nNodes[0] = 0; - for(int c=0; c<Ncolors; c++){ - - uint64_t *global=NULL; - int nv, ne; - HXT_CHECK(hxtMeanValuesGetData(parametrization->maps[c],&global, NULL, NULL, &nv,&ne)); - nNodes[c+1] = nNodes[c]+nv; - - for(int ie=0; ie<ne; ie++) - colors[global[ie]] = c; - - HXT_CHECK(hxtFree(&global)); - - } - - - HXT_CHECK(hxtMalloc(&uv,2*nNodes[Ncolors]*sizeof(double)));// - HXT_CHECK(hxtMalloc(&nodes,nNodes[Ncolors]*sizeof(int))); - for(int it=0; it<nNodes[Ncolors]; it++) - nodes[it] = -1; - - for(int c=0; c<Ncolors; c++){ - - uint64_t *global=NULL; - uint32_t *gn=NULL; - double *uvc=NULL; - int nv, ne; - HXT_CHECK(hxtMeanValuesGetData(parametrization->maps[c],&global, &gn, &uvc, &nv,&ne)); - for(int iv=0; iv<2*nv; iv++) - uv[2*nNodes[c]+iv] = uvc[iv]; - - for(int ie=0; ie<ne; ie++){ - for(int kk=0; kk<3; kk++){ - //printf("c=%d, ie=%d, global[ie]=%lu, kk=%d \t %lu\n",c,ie,global[ie],kk,3*parametrization->edges->edg2mesh->triangles.num); - int gvn = (int) parametrization->edges->edg2mesh->triangles.node[3*global[ie]+kk]; - nodes[nNodes[c]+gn[3*ie+kk]] = gvn; - } - } - - //printf("maybe not\n"); - HXT_CHECK(hxtFree(&global)); - //printf("-----------0\n"); - HXT_CHECK(hxtFree(&gn)); - //printf("-----------1\n"); - HXT_CHECK(hxtFree(&uvc)); - //printf("-----------2\n"); - } - - - *_colors_=colors; - *_nNodes_=nNodes; - *_nodes_=nodes; - *_uv_=uv; - - return HXT_STATUS_OK; -} - -HXTStatus hxtParametrizationWrite(HXTParametrization *parametrization, const char *filename) -{ - - return HXT_STATUS_OK; -} diff --git a/contrib/hxt/hxt_parametrization_old.h b/contrib/hxt/hxt_parametrization_old.h deleted file mode 100644 index ad29d05c509da8f3741581017e37abb6cc90e555..0000000000000000000000000000000000000000 --- a/contrib/hxt/hxt_parametrization_old.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef HEXTREME_PARAMETRIZATION_H -#define HEXTREME_PARAMETRIZATION_H - -#include "hxt_tools.h" -#include "hxt_mesh.h" -#include "hxt_edge.h" - -typedef struct HXTVectorStruct HXTVector; -typedef struct HXTParametrizationStruct HXTParametrization; - - -HXTStatus hxtParametrizationCreate(HXTMesh *mesh, HXTParametrization **parametrization); -HXTStatus hxtParametrizationDelete(HXTParametrization **parametrization); -HXTStatus hxtParametrizationCompute(HXTParametrization *parametrization, int **colors, int **nNodes, int **nodes, double **uv, int*nc); -HXTStatus hxtParametrizationWrite(HXTParametrization *parametrization, const char *filename); - - -#endif diff --git a/contrib/hxt/hxt_sort.c b/contrib/hxt/hxt_sort.c new file mode 100644 index 0000000000000000000000000000000000000000..74ba36c4bda40d2f8448b4c125571245dd6d6310 --- /dev/null +++ b/contrib/hxt/hxt_sort.c @@ -0,0 +1,65 @@ +#include "hxt_sort.h" + +/*************************************** + * for 1 value * + ***************************************/ +static inline uint64_t group1_get(uint64_t* val, const void* userData){ + return *val; +} + +HXTStatus group1_sort(uint64_t* val, const uint64_t n, const uint64_t max){ + PARALLEL_HYBRID64(uint64_t, val, n, max, group1_get, NULL); + return HXT_STATUS_OK; +} + +/*************************************** + * for 2 values * + ***************************************/ +static inline uint64_t group2_get_v0(HXTGroup2* pair, const void* userData){ + return pair->v[0]; +} + +HXTStatus group2_sort_v0(HXTGroup2* pair, const uint64_t n, const uint64_t max){ + PARALLEL_HYBRID64(HXTGroup2, pair, n, max, group2_get_v0, NULL); + return HXT_STATUS_OK; +} + +static inline uint64_t group2_get_v1(HXTGroup2* pair, const void* userData){ + return pair->v[1]; +} + +HXTStatus group2_sort_v1(HXTGroup2* pair, const uint64_t n, const uint64_t max){ + PARALLEL_HYBRID64(HXTGroup2, pair, n, max, group2_get_v1, NULL); + return HXT_STATUS_OK; +} + + +/*************************************** + * for 3 values * + ***************************************/ +static inline uint64_t group3_get_v0(HXTGroup3* triplet, const void* userData){ + return triplet->v[0]; +} + +HXTStatus group3_sort_v0(HXTGroup3* triplet, const uint64_t n, const uint64_t max){ + PARALLEL_HYBRID64(HXTGroup3, triplet, n, max, group3_get_v0, NULL); + return HXT_STATUS_OK; +} + +static inline uint64_t group3_get_v1(HXTGroup3* triplet, const void* userData){ + return triplet->v[1]; +} + +HXTStatus group3_sort_v1(HXTGroup3* triplet, const uint64_t n, const uint64_t max){ + PARALLEL_HYBRID64(HXTGroup3, triplet, n, max, group3_get_v1, NULL); + return HXT_STATUS_OK; +} + +static inline uint64_t group3_get_v2(HXTGroup3* triplet, const void* userData){ + return triplet->v[2]; +} + +HXTStatus group3_sort_v2(HXTGroup3* triplet, const uint64_t n, const uint64_t max){ + PARALLEL_HYBRID64(HXTGroup3, triplet, n, max, group3_get_v2, NULL); + return HXT_STATUS_OK; +} \ No newline at end of file diff --git a/contrib/hxt/hxt_sort.h b/contrib/hxt/hxt_sort.h index a30fe6195b3805b212b93297a6038244872ab9f0..ae409d33474cbf9729e52d5be5291a114228b7db 100644 --- a/contrib/hxt/hxt_sort.h +++ b/contrib/hxt/hxt_sort.h @@ -22,6 +22,24 @@ Author: Célestin Marot (celestin.marot@uclouvain.be) */ #include "hxt_tools.h" +// sorting function already defined +typedef struct{ + uint64_t v[2]; +}HXTGroup2; + +typedef struct{ + uint64_t v[3]; +}HXTGroup3; + +HXTStatus group1_sort(uint64_t* val, const uint64_t n, const uint64_t max); + +HXTStatus group2_sort_v0(HXTGroup2* pair, const uint64_t n, const uint64_t max); +HXTStatus group2_sort_v1(HXTGroup2* pair, const uint64_t n, const uint64_t max); + +HXTStatus group3_sort_v0(HXTGroup3* triplet, const uint64_t n, const uint64_t max); +HXTStatus group3_sort_v1(HXTGroup3* triplet, const uint64_t n, const uint64_t max); +HXTStatus group3_sort_v2(HXTGroup3* triplet, const uint64_t n, const uint64_t max); + /* convert from other types to uint32_t CONSERVING ORDER FOR ALL VALUES ! */ diff --git a/contrib/hxt/hxt_tetAdjacencies.c b/contrib/hxt/hxt_tetAdjacencies.c deleted file mode 100644 index fcf8dd3181b4e29b63587af30de8dd6721e7b457..0000000000000000000000000000000000000000 --- a/contrib/hxt/hxt_tetAdjacencies.c +++ /dev/null @@ -1,169 +0,0 @@ -#include "hxt_mesh.h" -#include "hxt_vertices.h" -#include "predicates.h" -#include "hxt_sort.h" - -#define SWAP(x,y) do{uint32_t tmp=x; x=y; y=tmp;}while(0) - -/********************************************** - (re-)compute adjacency of the tetrahedral mesh - **********************************************/ -typedef struct{ - uint32_t node[3]; - uint64_t face; -} triangleInfo; - -typedef struct{ - uint64_t node; - uint64_t face; -}triangleInfoFast; - -static inline uint32_t triangleKey0(triangleInfo* t, const void* userData){ - return t->node[0]; -} - -static inline uint32_t triangleKey1(triangleInfo* t, const void* userData){ - return t->node[1]; -} - -static inline uint32_t triangleKey2(triangleInfo* t, const void* userData){ - return t->node[2]; -} - -static inline uint64_t triangleKeyFast(triangleInfoFast* t, const void* userData){ - return t->node; -} - -static HXTStatus triangleSort(triangleInfo* triangles, uint32_t n, - uint32_t (*triangleKey)(triangleInfo*, const void*), - uint32_t max){ - HXTSORT32_UNIFORM(triangleInfo, triangles, n, max, triangleKey, NULL); - return HXT_STATUS_OK; -} - - -HXTStatus hxtComputeAdjacencies(HXTMesh* mesh){ - const uint64_t nTet = mesh->tetrahedra.num; - const uint64_t n = mesh->vertices.num+1; - - // make sure it was allocated - HXT_CHECK( hxtAlignedFree(&mesh->tetrahedra.neigh) ); - HXT_CHECK( hxtAlignedMalloc(&mesh->tetrahedra.neigh, mesh->tetrahedra.size*4*sizeof(uint64_t)) ); - - #pragma omp parallel for - for (uint64_t i=0; i<mesh->tetrahedra.num*4; i++) { - mesh->tetrahedra.neigh[i] = HXT_NO_ADJACENT; - } - - // first step, create all triangles... - triangleInfo* triangles; - triangleInfoFast* trianglesFast; // used when n^3 can fit in 64 bits - - if(n > 2642245)// n^3 cannot be on 64 bits - HXT_CHECK( hxtAlignedMalloc(&triangles, nTet*4*sizeof(triangleInfo)) ); - else - HXT_CHECK( hxtAlignedMalloc(&trianglesFast, nTet*4*sizeof(triangleInfoFast)) ); - - int hxtDeclareAligned indices[4][4] = {{1,2,3,0},{0,2,3,0},{0,1,3,0},{0,1,2,0}}; // 4th is just a padding - - // fill the triangle structure - #pragma omp parallel for - for (uint64_t i=0; i<nTet; i++) { - uint32_t* node = mesh->tetrahedra.node + 4*i; - int hxtDeclareAligned order[4]; - - // sort a and b - int cmp1 = (node[0] <= node[1]); - int cmp2 = (node[2] <= node[3]); - order[0] = !cmp1; - order[1] = cmp1; - order[2] = 2 + !cmp2; - order[3] = 2 + cmp2; - - // after this, order[0] is at the right place - if(node[order[2]] < node[order[0]]){ - SWAP(order[0], order[2]); - } - - // after this, order[3] is at the right place - if(node[order[3]] < node[order[1]]){ - SWAP(order[3], order[1]); - } - - // remains order[1] and order[2] - if(node[order[2]] < node[order[1]]){ - SWAP(order[1], order[2]); - } - - // on some architecture, this is a simple SIMD operation - uint32_t hxtDeclareAligned nodeOrdered[4]; - for (int j=0; j<4; j++) { - nodeOrdered[j] = node[order[j]]; - } - - if(nodeOrdered[3]==HXT_GHOST_VERTEX) - nodeOrdered[3] = mesh->vertices.num; - - if(n > 2642245){ - for (int j=0; j<4; j++) { - triangles[i*4+j].node[0] = nodeOrdered[indices[j][0]]; - triangles[i*4+j].node[1] = nodeOrdered[indices[j][1]]; - triangles[i*4+j].node[2] = nodeOrdered[indices[j][2]]; - triangles[i*4+j].face = i*4 + order[j]; - } - } - else{ - for (int j=0; j<4; j++) { - trianglesFast[i*4+j].node = nodeOrdered[indices[j][0]]*n*n - + nodeOrdered[indices[j][1]]*n - + nodeOrdered[indices[j][2]]; - trianglesFast[i*4+j].face = i*4 + order[j]; - } - } - - - } - - // sort the triangles... - if(n > 2642245) - { - HXT_CHECK( triangleSort(triangles, nTet*4, triangleKey2, mesh->vertices.num) ); - HXT_CHECK( triangleSort(triangles, nTet*4, triangleKey1, mesh->vertices.num) ); - HXT_CHECK( triangleSort(triangles, nTet*4, triangleKey0, mesh->vertices.num) ); - - // now that triangles are sorted, when two are the same, we - #pragma omp parallel for - for (uint64_t i=0; i<nTet*4-1; i++) { - if(triangles[i].node[0]==triangles[i+1].node[0] && - triangles[i].node[1]==triangles[i+1].node[1] && - triangles[i].node[2]==triangles[i+1].node[2]) - { - mesh->tetrahedra.neigh[triangles[i].face] = triangles[i+1].face; - mesh->tetrahedra.neigh[triangles[i+1].face] = triangles[i].face; - // i++; // can be done but break SIMD - } - } - - HXT_CHECK( hxtAlignedFree(&triangles) ); - } - else{ - HXTSORT64_UNIFORM(triangleInfoFast, trianglesFast, nTet*4, n*n*n-1, triangleKeyFast, NULL); - - #pragma omp parallel for - for (uint64_t i=0; i<nTet*4-1; i++) { - if(trianglesFast[i].node==trianglesFast[i+1].node) - { - mesh->tetrahedra.neigh[trianglesFast[i].face] = trianglesFast[i+1].face; - mesh->tetrahedra.neigh[trianglesFast[i+1].face] = trianglesFast[i].face; - // i++; // can be done but break SIMD - } - } - - HXT_CHECK( hxtAlignedFree(&trianglesFast) ); - } - - - - - return HXT_STATUS_OK; -} \ No newline at end of file diff --git a/contrib/hxt/hxt_tetAdjacencies.h b/contrib/hxt/hxt_tetAdjacencies.h deleted file mode 100644 index c21bd52e4ee1116be939a7f85d01516882433d58..0000000000000000000000000000000000000000 --- a/contrib/hxt/hxt_tetAdjacencies.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef _HXT_TETADJACENCIES_ -#define _HXT_TETADJACENCIES_ - -#ifdef __cplusplus -extern "C" { -#endif - -#include "hxt_mesh.h" - -/* (re-)compute the adjacency of a mesh */ -HXTStatus hxtComputeAdjacencies(HXTMesh* mesh); - - -#ifdef __cplusplus -} -#endif - -#endif - diff --git a/contrib/hxt/hxt_tetFlag.c b/contrib/hxt/hxt_tetFlag.c new file mode 100644 index 0000000000000000000000000000000000000000..bf068fd7677023574aabaa6f9c8183725d5456df --- /dev/null +++ b/contrib/hxt/hxt_tetFlag.c @@ -0,0 +1,645 @@ +#include "hxt_sort.h" +#include "hxt_tetFlag.h" + + +static inline void sort3ints(uint32_t i[3]){ + if(i[0]>i[1]){ + uint32_t tmp = i[0]; i[0] = i[1]; i[1] = tmp; + } + + if(i[1]>i[2]){ + uint32_t tmp = i[1]; i[1] = i[2]; i[2] = tmp; + + if(i[0]>i[1]){ + uint32_t tmp = i[0]; i[0] = i[1]; i[1] = tmp; + } + } +} +// just a function such that: +// if hashedCompare(a,b)==true +// then hashedCompare(b,a)==false +// +// BUT hashedCompare(a,b)==true and hashedCompare(b,c)==true +// doesn't imply that hashedCompare(a,c)==true +// and inversely if you replace true with false (no transitivity) +static inline uint64_t hashedCompare(uint64_t a, uint64_t b) { + return ((a^b)&1)^(a<b); +} + + +static inline uint64_t hash64(uint64_t x) { + x = (x ^ (x >> 30)) * UINT64_C(0xbf58476d1ce4e5b9); + x = (x ^ (x >> 27)) * UINT64_C(0x94d049bb133111eb); + x = x ^ (x >> 31); + return x; +} + + +// just a function such that: +// if transitiveHashedCmp(a,b)==true +// then transitiveHashedCmp(b,a)==false +// +// AND transitiveHashedCmp(a,b)==true and transitiveHashedCmp(b,c)==true +// imply that transitiveHashedCmp(a,c)==true +// and inversely if you replace true with false (the relation is transitive) +static inline uint64_t transitiveHashedCmp(uint64_t a, uint64_t b) { + return hash64(a) < hash64(b); +} + + +static HXTStatus hxtEdgesNotInTriangles(HXTMesh* mesh, uint32_t* lines, uint64_t* numLines, uint64_t* threadCount, const uint64_t n) { + HXTGroup2* edgeKey = NULL; + + uint64_t numEdgesTotal = mesh->triangles.num*3+mesh->lines.num; + HXT_CHECK( hxtAlignedMalloc(&edgeKey, numEdgesTotal*sizeof(HXTGroup2)) ); + + #pragma omp parallel + { + #pragma omp for nowait + for (uint64_t i=0; i<mesh->lines.num; i++) { + if(mesh->lines.node[2*i]<mesh->lines.node[2*i+1]) { + edgeKey[i].v[0] = mesh->lines.node[2*i]*n + mesh->lines.node[2*i+1]; + edgeKey[i].v[1] = 0; + } + else if(mesh->lines.node[2*i]<mesh->lines.node[2*i+1]){ + edgeKey[i].v[0] = mesh->lines.node[2*i+1]*n + mesh->lines.node[2*i]; + edgeKey[i].v[1] = 0; + } + else { + edgeKey[i].v[0] = mesh->lines.node[2*i]*n + mesh->lines.node[2*i]; // the line begins and ends at the same point... + edgeKey[i].v[1] = 1; + } + } + + #pragma omp for nowait + for (uint64_t i=0; i<mesh->triangles.num; i++) { + uint32_t v[3] = {mesh->triangles.node[3*i+0], + mesh->triangles.node[3*i+1], + mesh->triangles.node[3*i+2]}; + + sort3ints(v); + + edgeKey[mesh->lines.num+3*i].v[0] = v[0]*n + v[1]; + edgeKey[mesh->lines.num+3*i].v[1] = 1; + edgeKey[mesh->lines.num+3*i+1].v[0] = v[0]*n + v[2]; + edgeKey[mesh->lines.num+3*i+1].v[1] = 1; + edgeKey[mesh->lines.num+3*i+2].v[0] = v[1]*n + v[2]; + edgeKey[mesh->lines.num+3*i+2].v[1] = 1; + } + } + + group2_sort_v0(edgeKey, numEdgesTotal, n*(n-1)-1); + + uint64_t total = 0; + + // TODO: filter all edge (the sort is stable... maybe put triangles before lines :p) + #pragma omp parallel + { + int threadID = omp_get_thread_num(); + uint64_t localNum = 0; + + #pragma omp for schedule(static) + for (uint64_t i=0; i<numEdgesTotal; i++) { + if(edgeKey[i].v[1]==0) { + if(i==numEdgesTotal-1 || edgeKey[i].v[0] != edgeKey[i+1].v[0]) + localNum++; + else + edgeKey[i].v[1]=1; + } + } + + threadCount[threadID] = localNum; + + #pragma omp barrier + #pragma omp single + { + int nthreads = omp_get_num_threads(); + for (int i=0; i<nthreads; i++) { + uint32_t tsum = total + threadCount[i]; + threadCount[i] = total; + total = tsum; + } + } + + localNum = threadCount[threadID]; + if(total) { + #pragma omp for schedule(static) + for (uint64_t i=0; i<numEdgesTotal; i++) { + if(edgeKey[i].v[1]==0) { + lines[2*localNum] = edgeKey[i].v[0]%n; + lines[2*localNum+1] = edgeKey[i].v[0]/n; + localNum++; + } + } + } + } + + *numLines = total; + + HXT_CHECK( hxtAlignedFree(&edgeKey) ); + + return HXT_STATUS_OK; +} + + +/******************************************************************************** + * report the number of missing edges or set tet.flag for constrained edges * + ********************************************************************************/ +// every tetrahedra must have a neighbor HXT_NO_ADJACENT is NOT permitted !!! +HXTStatus hxtConstrainEdgesNotInTriangles(HXTMesh* mesh, uint64_t* missing) { + const int nodeArray[4][4] = {{-1, 2, 3, 1}, + { 3,-1, 0, 2}, + { 1, 3,-1, 0}, + { 2, 0, 1,-1}}; + + const int facetToNumber[4][4] = {{-1, 0, 1, 2}, + { 0,-1, 3, 4}, + { 1, 3,-1, 5}, + { 2, 4, 5,-1}}; + + + const int numberToFacetMin[] = { 0, 0, 0, 1, 1, 2}; + const int numberToFacetMax[] = { 1, 2, 3, 2, 3, 3}; + + const uint64_t n = mesh->vertices.num; + uint64_t* numEdges; + uint32_t* lines; + uint64_t numLines; + int maxThreads = omp_get_max_threads(); + HXT_CHECK( hxtMalloc(&numEdges, maxThreads*sizeof(uint64_t)) ); + HXT_CHECK( hxtAlignedMalloc(&lines, mesh->lines.num*2*sizeof(uint32_t)) ); + + // we don't wont to constrain edge that are already in a triangle + HXT_CHECK( hxtEdgesNotInTriangles(mesh, lines, &numLines, numEdges, n) ); + + if(numLines==0) { + HXT_CHECK( hxtAlignedFree(&lines) ); + HXT_CHECK( hxtFree(&numEdges) ); + return HXT_STATUS_OK; + } + + HXTGroup2* edgeKey = NULL; + uint64_t numEdgesTotal = 0; + HXTStatus status = HXT_STATUS_OK; + + #pragma omp parallel + { + const int threadID = omp_get_thread_num(); + uint64_t localNum = 0; + + #pragma omp for schedule(static) + for (uint64_t i=0; i<mesh->tetrahedra.num; i++) { // for each tetrahedra + for (int j=0; j<4; j++) { + for (int k=j+1; k<4; k++) { + int in_facet = j; + int out_facet = k; + + uint32_t p0 = mesh->tetrahedra.node[4*i + nodeArray[j][k]]; + uint32_t p1 = mesh->tetrahedra.node[4*i + nodeArray[k][j]]; + + if(p0==HXT_GHOST_VERTEX || p1==HXT_GHOST_VERTEX) + continue; + + int truth = 1; + + uint64_t curTet = i; + do + { + uint32_t newV = mesh->tetrahedra.node[4*curTet + in_facet]; + + // go into the neighbor through out_facet + uint64_t neigh = mesh->tetrahedra.neigh[4*curTet + out_facet]; + curTet = neigh/4; + in_facet = neigh%4; + + if(transitiveHashedCmp(curTet, i)) { + truth=0; + break; + } + + uint32_t* nodes = mesh->tetrahedra.node + 4*curTet; + for (out_facet=0; out_facet<3; out_facet++) + if(nodes[out_facet]==newV) + break; + + } while (curTet!=i); + + if(truth){ + constrainEdge(mesh, i, j, k); + localNum++; + } + } + } + } + + numEdges[threadID] = localNum; + + #pragma omp barrier + #pragma omp single + { + int nthreads = omp_get_num_threads(); + numEdgesTotal = numLines; + for (int i=0; i<nthreads; i++) { + // printf("%lu\n", numEdges[i]); + uint32_t tsum = numEdgesTotal + numEdges[i]; + numEdges[i] = numEdgesTotal; + numEdgesTotal = tsum; + } + +#ifndef NDEBUG + if(numEdgesTotal>2*mesh->tetrahedra.num+numLines){ + HXT_ERROR_MSG(HXT_STATUS_ERROR, "you should never go here.."); + exit(EXIT_FAILURE); + } +#endif + + status = hxtAlignedMalloc(&edgeKey, numEdgesTotal*sizeof(HXTGroup2)); + } + + if(status==HXT_STATUS_OK) { + // copy the edges from mesh->lines in the edgeKey struct array + #pragma omp for + for (uint64_t i=0; i<numLines; i++) { + uint32_t p0 = lines[2*i+0]; + uint32_t p1 = lines[2*i+1]; + + if(p0<p1) { + edgeKey[i].v[0] = p0*n*2 + p1*2 + 0; + } + else { + edgeKey[i].v[0] = p1*n*2 + p0*2 + 0; + } + + edgeKey[i].v[1] = HXT_NO_ADJACENT; // this lines does not come from any tetrahedra + } + + localNum = numEdges[threadID]; + #pragma omp for schedule(static) + for (uint64_t i=0; i<mesh->tetrahedra.num; i++) { + for (int j=0; j<4; j++) { + for (int k=j+1; k<4; k++) { + if(isEdgeConstrained(mesh, i, j, k)){ + uint32_t p0 = mesh->tetrahedra.node[4*i + nodeArray[j][k]]; + uint32_t p1 = mesh->tetrahedra.node[4*i + nodeArray[k][j]]; + + if(p0==HXT_GHOST_VERTEX || p1==HXT_GHOST_VERTEX) + { + HXT_ERROR_MSG(HXT_STATUS_ERROR, "There were contrained edges before this function was called."); + exit(EXIT_FAILURE); + } + + if(p0<p1){ + edgeKey[localNum].v[0] = p0*n*2 + p1*2 + 1; + } + else { + edgeKey[localNum].v[0] = p1*n*2 + p0*2 + 1; + } + edgeKey[localNum].v[1] = 6*i+facetToNumber[j][k]; + + localNum++; + + unconstrainEdge(mesh, i, j, k); + } + } + } + } + } + } + + HXT_CHECK( hxtAlignedFree(&lines) ); + + HXT_CHECK(status); + + group2_sort_v0(edgeKey, numEdgesTotal, (n-1)*n*2-1); + + #pragma omp parallel + { + const int threadID = omp_get_thread_num(); + numEdges[threadID] = 0; + + #pragma omp for + for (uint64_t i=0; i<numEdgesTotal; i++) { + if(edgeKey[i].v[0]%2==0 && (i==numEdgesTotal-1 || edgeKey[i].v[0]/2!=edgeKey[i+1].v[0]/2)) + numEdges[threadID]++; // the edge is missing + } + + #pragma omp barrier + #pragma omp single + { + int nthreads = omp_get_num_threads(); + *missing = 0; + for (int i=0; i<nthreads; i++) { + *missing+=numEdges[i]; + } + } + } + + HXT_CHECK( hxtFree(&numEdges) ); + + if(*missing){ + HXT_CHECK( hxtAlignedFree(&edgeKey) ); + return HXT_STATUS_OK; + } + + char* edgeFlag; + HXT_CHECK( hxtAlignedMalloc(&edgeFlag, 6*mesh->tetrahedra.num*sizeof(char)) ); + memset(edgeFlag, 0, 6*mesh->tetrahedra.num*sizeof(char)); + + #pragma omp parallel for + for (uint64_t i=1; i<numEdgesTotal; i++) { + if(edgeKey[i-1].v[0]%2==0) { + + // turn around the edge to set edgeFlag of all tetrahedra to 1... + uint64_t firstTet = edgeKey[i].v[1]/6; + uint64_t curTet = firstTet; + int edgeNumber = edgeKey[i].v[1]%6; + int in_facet = numberToFacetMin[edgeNumber]; + int out_facet = numberToFacetMax[edgeNumber]; + + do + { + edgeFlag[6*curTet + facetToNumber[in_facet][out_facet]] = 1; + + uint32_t newV = mesh->tetrahedra.node[4*curTet + in_facet]; + + // go into the neighbor through out_facet + uint64_t neigh = mesh->tetrahedra.neigh[4*curTet + out_facet]; + curTet = neigh/4; + in_facet = neigh%4; + uint32_t* nodes = mesh->tetrahedra.node + 4*curTet; + for (out_facet=0; out_facet<3; out_facet++) + if(nodes[out_facet]==newV) + break; + + } while (curTet!=firstTet); + } + } + + HXT_CHECK( hxtAlignedFree(&edgeKey) ); + + #pragma omp parallel for + for (uint64_t i=0; i<mesh->tetrahedra.num; i++) { + for (int j=0; j<6; j++) { + if(edgeFlag[6*i+j]) + constrainEdge(mesh, i, numberToFacetMin[j], numberToFacetMax[j]); + } + } + + + HXT_CHECK( hxtAlignedFree(&edgeFlag) ); + + + return HXT_STATUS_OK; +} + + +/******************************************************************************** + * report the number of missing triangles or set flag for constrained triangle * + ********************************************************************************/ +// every tetrahedra must have a neighbor HXT_NO_ADJACENT is NOT permitted !!! +HXTStatus hxtConstrainTriangles(HXTMesh* mesh, uint64_t* missing) { + HXTGroup2 *triKey = NULL; + HXTGroup3* pairKey = NULL; + + if(mesh->triangles.num==0) { + *missing = 0; + return HXT_STATUS_OK; + } + + uint64_t *numTriangles; + int maxThreads = omp_get_max_threads(); + HXT_CHECK( hxtMalloc(&numTriangles, maxThreads*sizeof(uint64_t)) ); + + const uint64_t n = mesh->vertices.num; + uint64_t numTrianglesTotal; + + HXTStatus status = HXT_STATUS_OK; + +#ifndef NDEBUG + uint64_t nGhosts = 0; + + #pragma omp parallel for reduction(+:nGhosts) + for (uint64_t i=0; i<mesh->tetrahedra.num; i++) { + if(mesh->tetrahedra.node[4*i+3]==HXT_GHOST_VERTEX) + nGhosts++; + } + + if(n <= 2097152){ + HXT_CHECK( hxtAlignedMalloc(&triKey, (2*mesh->tetrahedra.num-3*nGhosts/2+mesh->triangles.num)*sizeof(HXTGroup2)) ); + } + else{ + HXT_CHECK( hxtAlignedMalloc(&pairKey, (2*mesh->tetrahedra.num-3*nGhosts/2+mesh->triangles.num)*sizeof(HXTGroup3)) ); + } +#endif + + // create a list of triangles in the mesh: + #pragma omp parallel + { + const int threadID = omp_get_thread_num(); + uint64_t localNum = 0; + + #pragma omp for schedule(static) + for (uint64_t i=0; i<mesh->tetrahedra.num; i++) { + if(mesh->tetrahedra.node[4*i+3]!=HXT_GHOST_VERTEX) { + for (int j=0; j<4; j++) { + uint64_t neigh = mesh->tetrahedra.neigh[4*i+j]/4; + if(hashedCompare(i, neigh) || + mesh->tetrahedra.node[neigh*4+3]==HXT_GHOST_VERTEX) + localNum++; + } + } + } + + numTriangles[threadID] = localNum; + #pragma omp barrier + #pragma omp single + { + int nthreads = omp_get_num_threads(); + numTrianglesTotal = mesh->triangles.num; + for (int i=0; i<nthreads; i++) { + uint32_t tsum = numTrianglesTotal + numTriangles[i]; + numTriangles[i] = numTrianglesTotal; + numTrianglesTotal = tsum; + } + +#ifndef NDEBUG + if(numTrianglesTotal!=2*mesh->tetrahedra.num-3*nGhosts/2+mesh->triangles.num){ + HXT_ERROR_MSG(HXT_STATUS_ERROR, "you should never go here... (%lu!=2*%lu+3*%lu/2",numTrianglesTotal-mesh->triangles.num, + mesh->tetrahedra.num, nGhosts); + exit(EXIT_FAILURE); + } +#else + if(n <= 2097152){ + status = hxtAlignedMalloc(&triKey, numTrianglesTotal*sizeof(HXTGroup2)); + } + else{ + status = hxtAlignedMalloc(&pairKey, numTrianglesTotal*sizeof(HXTGroup3)); + } +#endif + } + + if(status==HXT_STATUS_OK) { + // copy the triangles from mesh->triangles in the triKey struct array + if(n <= 2097152){ + #pragma omp for + for (uint64_t i=0; i<mesh->triangles.num; i++) { + uint32_t v[3] = {mesh->triangles.node[3*i+0], + mesh->triangles.node[3*i+1], + mesh->triangles.node[3*i+2]}; + sort3ints(v); + triKey[i].v[1] = HXT_NO_ADJACENT; // this triangle does not come from any tetrahedra + triKey[i].v[0] = v[0]*(n-1)*n*2 + v[1]*n*2 + v[2]*2 + 0; // the lowest bit of triangles from mesh->triangles is unset + } + } + else{ + #pragma omp for + for (uint64_t i=0; i<mesh->triangles.num; i++) { + uint32_t v[3] = {mesh->triangles.node[3*i+0], + mesh->triangles.node[3*i+1], + mesh->triangles.node[3*i+2]}; + sort3ints(v); + pairKey[i].v[2] = HXT_NO_ADJACENT; // this triangle does not come from any tetrahedra + pairKey[i].v[1] = v[0]*(n-1) + v[1]; + pairKey[i].v[0] = v[2]*2+0; + } + } + + // add the triangle from the tetrahedral mesh to the triKey struct array + localNum = numTriangles[threadID]; + if(n <= 2097152){ + #pragma omp for schedule(static) + for (uint64_t i=0; i<mesh->tetrahedra.num; i++) { + if(mesh->tetrahedra.node[4*i+3]!=HXT_GHOST_VERTEX){ + for (int j=0; j<4; j++) { + uint64_t neigh = mesh->tetrahedra.neigh[4*i+j]/4; + if(hashedCompare(i, neigh) || + mesh->tetrahedra.node[neigh*4+3]==HXT_GHOST_VERTEX) { + uint32_t v[3] = {mesh->tetrahedra.node[4*i+((j+1)&3)], + mesh->tetrahedra.node[4*i+((j+2)&3)], + mesh->tetrahedra.node[4*i+((j+3)&3)]}; + sort3ints(v); + triKey[localNum].v[1] = 4*i+j; + triKey[localNum].v[0] = v[0]*(n-1)*n*2 + v[1]*n*2 + v[2]*2 + 1; // max: 2(n-3)(n-1)n + 2(n-2)n + 2(n-1) + 1 + // = 2(n-2)(n-1)n-1 + localNum++; + } + } + } + } + } + else { + #pragma omp for schedule(static) + for (uint64_t i=0; i<mesh->tetrahedra.num; i++) { + if(mesh->tetrahedra.node[4*i+3]!=HXT_GHOST_VERTEX){ + for (int j=0; j<4; j++) { + uint64_t neigh = mesh->tetrahedra.neigh[4*i+j]/4; + if(hashedCompare(i, neigh) || + mesh->tetrahedra.node[neigh*4+3]==HXT_GHOST_VERTEX) { + uint32_t v[3] = {mesh->tetrahedra.node[4*i+((j+1)&3)], + mesh->tetrahedra.node[4*i+((j+2)&3)], + mesh->tetrahedra.node[4*i+((j+3)&3)]}; + sort3ints(v); + pairKey[localNum].v[2] = 4*i+j; + pairKey[localNum].v[1] = v[0]*(n-1) + v[1]; // max: (n-3)(n-1) + (n-2) = (n-2)(n-1) - 1 + pairKey[localNum].v[0] = v[2]*2+1; // max: (n-1)2+1 = 2n-1 + + localNum++; + } + } + } + } + } + } + } + + HXT_CHECK(status); + + if(n <= 2097152){ + // sort triKey + group2_sort_v0(triKey, numTrianglesTotal, 2*(n-2)*(n-1)*n-1); + } + else{ + group3_sort_v0(pairKey, numTrianglesTotal, 2*n-1); + group3_sort_v1(pairKey, numTrianglesTotal, (n-2)*(n-1)-1); + } + + #pragma omp parallel + { + const int threadID = omp_get_thread_num(); + numTriangles[threadID] = 0; + + if(n <= 2097152){ + #pragma omp for + for (uint64_t i=0; i<numTrianglesTotal; i++) { + if(triKey[i].v[0]%2==0 && (i==numTrianglesTotal-1 || triKey[i].v[0]/2!=triKey[i+1].v[0]/2)) + numTriangles[threadID]++; // the triangle is missing + } + } + else{ + #pragma omp for + for (uint64_t i=0; i<numTrianglesTotal; i++) { + if(pairKey[i].v[0]%2==0 && (i==numTrianglesTotal-1 || pairKey[i].v[0]/2!=pairKey[i+1].v[0]/2 || pairKey[i].v[1]!=pairKey[i+1].v[1])) + numTriangles[threadID]++; + } + } + + #pragma omp barrier + #pragma omp single + { + int nthreads = omp_get_num_threads(); + *missing = 0; + for (int i=0; i<nthreads; i++) { + *missing+=numTriangles[i]; + } + } + } + + HXT_CHECK( hxtFree(&numTriangles) ); + + if(*missing){ + HXT_CHECK( hxtAlignedFree(&triKey) ); + HXT_CHECK( hxtAlignedFree(&pairKey) ); + return HXT_STATUS_OK; + } + + char* faceFlag; + HXT_CHECK( hxtAlignedMalloc(&faceFlag, 4*mesh->tetrahedra.num*sizeof(char)) ); + memset(faceFlag, 0, 4*mesh->tetrahedra.num*sizeof(char)); + + if(n <= 2097152){ + #pragma omp parallel for + for (uint64_t i=1; i<numTrianglesTotal; i++) { + if(triKey[i-1].v[0]%2==0 ) { + faceFlag[triKey[i].v[1]] = 1; + if(mesh->tetrahedra.neigh[triKey[i].v[1]]!=HXT_NO_ADJACENT) + faceFlag[mesh->tetrahedra.neigh[triKey[i].v[1]]] = 1; + } + } + } + else{ + #pragma omp parallel for + for (uint64_t i=1; i<numTrianglesTotal; i++) { + if(pairKey[i-1].v[0]%2==0) { + faceFlag[pairKey[i].v[2]] = 1; + if(mesh->tetrahedra.neigh[pairKey[i].v[2]]!=HXT_NO_ADJACENT) + faceFlag[mesh->tetrahedra.neigh[pairKey[i].v[2]]] = 1; + } + } + } + + HXT_CHECK( hxtAlignedFree(&triKey) ); + HXT_CHECK( hxtAlignedFree(&pairKey) ); + + + #pragma omp parallel for + for (uint64_t i=0; i<mesh->tetrahedra.num; i++) { + for (int j=0; j<4; j++) { + if(faceFlag[4*i+j]) + constrainFacet(mesh, 4*i+j); + } + } + + HXT_CHECK( hxtAlignedFree(&faceFlag) ); + + return HXT_STATUS_OK; +} \ No newline at end of file diff --git a/contrib/hxt/hxt_tetFlag.h b/contrib/hxt/hxt_tetFlag.h new file mode 100644 index 0000000000000000000000000000000000000000..829df847ae5f629e9c6a531311fcc377f12b4fbe --- /dev/null +++ b/contrib/hxt/hxt_tetFlag.h @@ -0,0 +1,109 @@ +#ifndef _HXT_TETFLAG_ +#define _HXT_TETFLAG_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "hxt_mesh.h" + +// Verify if all facets are present in a tetrahedrization, missing is the number of missing facets +// it then set tetrahedra.flag to account for the contrains on the facets +HXTStatus hxtConstrainTriangles(HXTMesh* mesh, uint64_t* missing); +HXTStatus hxtConstrainEdgesNotInTriangles(HXTMesh* mesh, uint64_t* missing); + + +/* flag is a 16-bit number + * + * 0 facet 0 is contrained + * 1 edge between facet 0 and facet 1 is contrained + * 2 edge betwwen facet 0 and facet 2 is contrained + * 3 edge betwwen facet 0 and facet 3 is contrained + * 4 facet 1 is contrained + * 5 + * 6 edge betwwen facet 1 and facet 2 is contrained + * 7 edge betwwen facet 1 and facet 3 is contrained + * 8 facet 2 is contrained + * 9 + * 10 + * 11 edge between facet 2 and facet 3 is contrained + * 12 facet 3 is contrained + * 13 + * 14 the tetrahedron has already been processed (a vertex was already inserted inside it and if failed) + * 15 the tetrahedron is deleted + */ + + +static inline uint16_t isAnyEdgeConstrained(HXTMesh* mesh, uint64_t tet) { + return mesh->tetrahedra.flag[tet] & UINT16_C(0x8CE); +} + +static inline uint16_t isAnyEdgeOfFacetConstrained(HXTMesh* mesh, uint64_t facet) { + static uint16_t mask[4] = {0xE, 0xC2, 0x844, 0x888}; + return mesh->tetrahedra.flag[facet/4] & mask[facet%4]; +} + +// static inline uint16_t isAnyThingConstrained(HXTMesh* mesh, uint64_t tet) { +// return mesh->tetrahedra.flag[tet] & 0x19DF; +// } + +static inline uint16_t isAnyFacetConstrained(HXTMesh* mesh, uint64_t tet) { + return mesh->tetrahedra.flag[tet] & UINT16_C(0x1111); +} + +static inline uint16_t isEdgeConstrained(HXTMesh* mesh, uint64_t tet, unsigned facetmin, unsigned facetmax) { + return mesh->tetrahedra.flag[tet] & (1U<<(facetmin*4+facetmax)); +} + +static inline uint16_t isEdgeConstrainedSafe(HXTMesh* mesh, uint64_t tet, unsigned facet1, unsigned facet2) { + return mesh->tetrahedra.flag[tet] & (1U<<(facet1<facet2?facet1*4+facet2:facet2*4+facet1)); +} + +static inline void constrainEdge(HXTMesh* mesh, uint64_t tet, unsigned facetmin, unsigned facetmax) { + mesh->tetrahedra.flag[tet] |= (1U<<(facetmin*4+facetmax)); +} + +static inline void unconstrainEdge(HXTMesh* mesh, uint64_t tet, unsigned facetmin, unsigned facetmax) { + mesh->tetrahedra.flag[tet] &= ~(1U<<(facetmin*4+facetmax)); +} + +// here facet = 4*tet + facett +static inline uint16_t isFacetConstrained(HXTMesh* mesh, uint64_t facet) { + return mesh->tetrahedra.flag[facet/4] & (1U<<(facet%4*4)); +} + +static inline void constrainFacet(HXTMesh* mesh, uint64_t facet) { + mesh->tetrahedra.flag[facet/4] |= (1U<<(facet%4*4)); +} + +static inline uint16_t isTetDeleted(HXTMesh* mesh, uint64_t tet) { + return mesh->tetrahedra.flag[tet] & (1U<<15); +} + +static inline void markTetAsDeleted(HXTMesh* mesh, uint64_t tet) { + mesh->tetrahedra.flag[tet] |= (1U<<15); +} + +static inline void unmarkTetAsDeleted(HXTMesh* mesh, uint64_t tet) { + mesh->tetrahedra.flag[tet] &= ~(1U<<15); +} + +static inline uint16_t isTetProcessed(HXTMesh* mesh, uint64_t tet) { + return mesh->tetrahedra.flag[tet] & (1U<<14); +} + +static inline void markTetAsProcessed(HXTMesh* mesh, uint64_t tet) { + mesh->tetrahedra.flag[tet] |= (1U<<14); +} + + +static inline void unmarkTetAsProcessed(HXTMesh* mesh, uint64_t tet) { + mesh->tetrahedra.flag[tet] &= ~(1U<<14); +} + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/contrib/hxt/hxt_tetOpti.c b/contrib/hxt/hxt_tetOpti.c new file mode 100644 index 0000000000000000000000000000000000000000..d5714451ead9d40c3536137152f3920538a761c2 --- /dev/null +++ b/contrib/hxt/hxt_tetOpti.c @@ -0,0 +1,1595 @@ +#include "hxt_tet_aspect_ratio.h" +#include "hxt_mesh.h" +#include "hxt_tetUtils.h" +#include "predicates.h" +#include "hxt_tetRepair.h" +#include "hxt_tetFlag.h" +#include "hxt_vertices.h" +#include "hxt_tools.h" +#include "hxt_sort.h" +#include <float.h> + +#define NV -128 // not a valid entry +#define HXT_MAX_CAVITY_SIZE 7 // because no cavity is has more than 7 tetrahedra while swapping +#define DELETED_BUFFER_SIZE 8192 +#define SMALLEST_ROUND 1024 + +/* usefull macros */ +#define MAX(x,y) ((x)>(y) ? (x) : (y)) +#define MIN(x,y) ((x)<(y) ? (x) : (y)) +#define SWAP(x,y) do{int tmp=x; x=y; y=tmp;}while(0) + + +/* +An oriented edge {up,down} is described by its 'in' and 'out' facets: + v_up + |\`-_ + | \ `-_ + | \ `-_ out_facet + | \ `-_ | + | \ up `-_ / + | \ facet `-_ <--' +our | \ `-_ +edge | in \ `-_ +-----> | facet \v_in___________`>v_out + | / _-' + | / _-' + | / down _-' + | / facet _-' + | / _-' + | / _-' + | / _-' + | / _-' + |/-' + v_down + + _-' + / + \ we scan tetrahedra in a counterclockwise order when viewed from up + `-__-> + + + We keep the numbering of facets and the orientation of tetrahedra as before so: + + mesh->tetrahedra.node[4*t + in_facet] = v_out + mesh->tetrahedra.node[4*t + out_facet] = v_in + mesh->tetrahedra.node[4*t + down_facet] = v_up + mesh->tetrahedra.node[4*t + up_facet] = v_down + + +There are 12 possible oriented edges (in_facet!=out_facet) +There are also 12 possibilities for a valid tetrahedra, each can be uniquely defined by an oriented edge... + +in_f=0 out_f=1 up_f=2 down_f=3 in_f=0 out_f=2 up_f=3 down_f=1 in_f=0 out_f=3 up_f=1 down_f=2 + v_3 v_1 v_2 + |\`-_ |\`-_ |\`-_ + | \ `-_ | \ `-_ | \ `-_ + | \ `-_ | \ `-_ | \ `-_ + | \ `-_ | \ `-_ | \ `-_ + | \ `-_ | \ `-_ | \ `-_ + | \v_1_______`>v_0 | \v_2_______`>v_0 | \v_3_______`>v_0 + | / _-' | / _-' | / _-' + | / _-' | / _-' | / _-' + | / _-' | / _-' | / _-' + | / _-' | / _-' | / _-' + | / _-' | / _-' | / _-' + |/-' |/-' |/-' + v_2 v_3 v_1 + + +in_f=1 out_f=2 up_f=0 down_f=3 in_f=1 out_f=0 up_f=3 down_f=2 in_f=1 out_f=3 up_f=2 down_f=0 + v_3 v_2 v_0 + |\`-_ |\`-_ |\`-_ + | \ `-_ | \ `-_ | \ `-_ + | \ `-_ | \ `-_ | \ `-_ + | \ `-_ | \ `-_ | \ `-_ + | \ `-_ | \ `-_ | \ `-_ + | \v_2_______`>v_1 | \v_0_______`>v_1 | \v_3_______`>v_1 + | / _-' | / _-' | / _-' + | / _-' | / _-' | / _-' + | / _-' | / _-' | / _-' + | / _-' | / _-' | / _-' + | / _-' | / _-' | / _-' + |/-' |/-' |/-' + v_0 v_3 v_2 + + +in_f=2 out_f=3 up_f=0 down_f=1 in_f=2 out_f=0 up_f=1 down_f=3 in_f=2 out_f=1 up_f=3 down_f=0 + v_1 v_3 v_0 + |\`-_ |\`-_ |\`-_ + | \ `-_ | \ `-_ | \ `-_ + | \ `-_ | \ `-_ | \ `-_ + | \ `-_ | \ `-_ | \ `-_ + | \ `-_ | \ `-_ | \ `-_ + | \v_3_______`>v_2 | \v_0_______`>v_2 | \v_1_______`>v_2 + | / _-' | / _-' | / _-' + | / _-' | / _-' | / _-' + | / _-' | / _-' | / _-' + | / _-' | / _-' | / _-' + | / _-' | / _-' | / _-' + |/-' |/-' |/-' + v_0 v_1 v_3 + + +in_f=3 out_f=1 up_f=0 down_f=2 in_f=3 out_f=0 up_f=2 down_f=1 in_f=3 out_f=2 up_f=1 down_f=0 + v_2 v_1 v_0 + |\`-_ |\`-_ |\`-_ + | \ `-_ | \ `-_ | \ `-_ + | \ `-_ | \ `-_ | \ `-_ + | \ `-_ | \ `-_ | \ `-_ + | \ `-_ | \ `-_ | \ `-_ + | \v_1_______`>v_3 | \v_0_______`>v_3 | \v_2_______`>v_3 + | / _-' | / _-' | / _-' + | / _-' | / _-' | / _-' + | / _-' | / _-' | / _-' + | / _-' | / _-' | / _-' + | / _-' | / _-' | / _-' + |/-' |/-' |/-' + v_0 v_2 v_1 +*/ + +const int _UP_FACET[4][4] = {{NV, 2, 3, 1}, + { 3,NV, 0, 2}, + { 1, 3,NV, 0}, + { 2, 0, 1,NV}}; + +#define UP_FACET(in_facet, out_facet) _UP_FACET[in_facet][out_facet] +#define DOWN_FACET(in_facet, out_facet) _UP_FACET[out_facet][in_facet] + +#define UP_VERTEX(in_facet, out_facet) DOWN_FACET(in_facet, out_facet) +#define DOWN_VERTEX(in_facet, out_facet) UP_FACET(in_facet, out_facet) +#define IN_VERTEX(in_facet, out_facet) (out_facet) +#define OUT_VERTEX(in_facet, out_facet) (in_facet) + +/* that's everything we need to handle edges ! + + NOW, we need patterns of triangulation around edges to make swaps + */ + +typedef struct { + const unsigned char (*triangles)[3]; /* triangles array */ + const uint64_t *triangle_in_triangul; /* in which triangulation is the triangles (bit array) */ + const unsigned char (*triangulations)[5]; /* triangulation array */ + const signed char (*triangul_neigh)[20]; /* array to find adjacencies back */ + const int num_triangles; /* number of different triangles */ + const int num_triangulations ; /* number of different triangulations */ + // const int num_triangles_per_triangulation; /* simply the number of nodes +2... */ +} SwapPattern ; + + +const unsigned char triangles3[][3] = { {0,1,2} }; +const uint64_t triangle_in_triangul3[] = { 1}; +const unsigned char triangulations3[][5] = {{0}}; +const signed char triangul_neigh3[][20] = { + {-2,-3,-1,NV,NV,NV,NV,NV,NV,NV,NV,NV,NV,NV,NV,NV,NV,NV,NV,NV} +}; + + +const unsigned char triangles4[][3] = { + {0,1,2}, {2,3,0}, {1,2,3}, {3,0,1} +}; +const uint64_t triangle_in_triangul4[] = { 1, 1, 2, 2}; +const unsigned char triangulations4[][5] = { + {0,1}, {2,3} +}; +const signed char triangul_neigh4[][20] = { + {-2,5,-1,NV,-4,1,-3,NV,NV,NV,NV,NV,NV,NV,NV,NV,NV,NV,NV,NV}, + {-3,5,-2,NV,-1,1,-4,NV,NV,NV,NV,NV,NV,NV,NV,NV,NV,NV,NV,NV} +}; + + + +const unsigned char triangles5[][3] = { + {0,1,2}, {0,2,3}, {0,3,4}, {0,1,4}, {1,3,4}, + {1,2,3}, {2,3,4}, {0,2,4}, {0,1,3}, {1,2,4} +}; +const uint64_t triangle_in_triangul5[] = {5, 1, 9, 18, 2, 10, 20, 4, 8, 16}; +const unsigned char triangulations5[][5] = { {0,1,2}, {3,4,5}, {0,6,7}, {2,5,8}, {3,6,9} }; +const signed char triangul_neigh5[][20] = { + {-2,6,-1,NV,-3,10,1,NV,-4,-5,5,NV,NV,NV,NV,NV,NV,NV,NV,NV}, + {5,-5,-1,NV,-4,0,9,NV,-3,6,-2,NV,NV,NV,NV,NV,NV,NV,NV,NV}, + {-2,10,-1,NV,-4,8,-3,NV,5,-5,1,NV,NV,NV,NV,NV,NV,NV,NV,NV}, + {-4,-5,9,NV,-3,8,-2,NV,5,2,-1,NV,NV,NV,NV,NV,NV,NV,NV,NV}, + {9,-5,-1,NV,-4,8,-3,NV,5,0,-2,NV,NV,NV,NV,NV,NV,NV,NV,NV} +}; + + +const unsigned char triangles6[][3] = { + {0,1,2}, {0,2,3}, {0,3,4}, {0,4,5}, {0,2,5}, {2,4,5}, {2,3,4}, {0,3,5}, + {3,4,5}, {0,2,4}, {2,3,5}, {1,2,3}, {0,1,3}, {0,1,5}, {1,4,5}, {1,3,4}, + {0,1,4}, {1,3,5}, {1,2,4}, {1,2,5} +}; +const uint64_t triangle_in_triangul6[] = { + 31, 5, 33, 2345, 18, 4098, 7178, 132, 8852, 8, 8208, + 992, 160, 13888, 1088, 320, 2304, 512, 3072, 12288}; +const unsigned char triangulations6[][5] = { + {0,1,2,3}, {0,4,5,6}, {0,1,7,8}, {0,3,6,9}, {0,4,8,10}, + {2,3,11,12}, {11,13,14,15}, {7,8,11,12}, {3,11,15,16}, + {8,11,13,17}, {6,13,14,18}, {3,6,16,18}, {5,6,13,19}, + {8,10,13,19} +}; +const signed char triangul_neigh6[][20] = { + {-2,6,-1,NV,-3,10,1,NV,-4,14,5,NV,-5,-6,9,NV,NV,NV,NV,NV}, + {-2,6,-1,NV,9,-6,1,NV,-5,4,13,NV,-4,10,-3,NV,NV,NV,NV,NV}, + {-2,6,-1,NV,-3,10,1,NV,13,-6,5,NV,-5,8,-4,NV,NV,NV,NV,NV}, + {-2,14,-1,NV,-5,-6,13,NV,-4,12,-3,NV,9,6,1,NV,NV,NV,NV,NV}, + {-2,6,-1,NV,13,-6,1,NV,-5,12,-4,NV,9,4,-3,NV,NV,NV,NV,NV}, + {-4,6,13,NV,-5,-6,1,NV,-3,12,-2,NV,9,2,-1,NV,NV,NV,NV,NV}, + {-3,14,-2,NV,9,-6,-1,NV,-5,4,13,NV,-4,10,1,NV,NV,NV,NV,NV}, + {5,-6,13,NV,-5,0,-4,NV,-3,12,-2,NV,9,2,-1,NV,NV,NV,NV,NV}, + {-5,-6,13,NV,-3,10,-2,NV,-4,12,5,NV,9,2,-1,NV,NV,NV,NV,NV}, + {-5,12,-4,NV,-3,14,-2,NV,13,-6,-1,NV,1,8,5,NV,NV,NV,NV,NV}, + {-4,12,-3,NV,9,-6,-1,NV,-5,4,13,NV,1,10,-2,NV,NV,NV,NV,NV}, + {-5,-6,9,NV,-4,12,-3,NV,13,2,-1,NV,5,8,-2,NV,NV,NV,NV,NV}, + {-5,12,5,NV,-4,2,-3,NV,13,-6,-1,NV,1,8,-2,NV,NV,NV,NV,NV}, + {-5,4,-4,NV,1,12,-3,NV,13,-6,-1,NV,5,8,-2,NV,NV,NV,NV,NV} +}; + + +const unsigned char triangles7[][3] = { + {0,1,2}, {0,2,3}, {0,3,4}, {0,4,5}, {0,5,6}, {0,3,6}, {3,5,6}, {3,4,5}, {0,4,6}, + {4,5,6}, {0,3,5}, {3,4,6}, {0,2,4}, {2,3,4}, {0,2,6}, {2,5,6}, {2,4,5}, {0,2,5}, + {2,4,6}, {2,3,5}, {2,3,6}, {0,1,3}, {1,2,3}, {0,1,4}, {1,3,4}, {0,1,6}, {1,5,6}, + {1,4,5}, {0,1,5}, {1,4,6}, {1,3,5}, {1,3,6}, {1,2,4}, {1,2,5}, {1,2,6} +}; +const uint64_t triangle_in_triangul7[] = { + 16383, 31, 81925, 268976161, 294512118057, 294930, 1099578773506, 2061701913610, + 1075904644, 2273256481428, 131080, 2199157743632, 160, 137170519008, 13888, 584115553344, + 60129542464, 2304, 68719477248, 962072677376, 3298534895616, 507904, 268419072, 1344798720, + 16252928, 4102458179584, 146583584768, 2689597440, 294243008512, 4303355904, 50331648, 201326592, + 8321499136, 438086664192, 3951369912320 + }; +const unsigned char triangulations7[][5] = { + {0,1,2,3,4}, {0,1,5,6,7}, {0,1,2,8,9}, {0,1,4,7,10}, {0,1,5,9,11}, {0,3,4,12,13}, + {0,13,14,15,16}, {0,8,9,12,13}, {0,4,13,16,17}, {0,9,13,14,18}, {0,7,14,15,19}, + {0,4,7,17,19}, {0,6,7,14,20}, {0,9,11,14,20}, {2,3,4,21,22}, {5,6,7,21,22}, + {2,8,9,21,22}, {4,7,10,21,22}, {5,9,11,21,22}, {3,4,22,23,24}, {22,24,25,26,27}, + {8,9,22,23,24}, {4,22,24,27,28}, {9,22,24,25,29}, {7,22,25,26,30}, {4,7,22,28,30}, + {6,7,22,25,31}, {9,11,22,25,31}, {3,4,13,23,32}, {13,25,26,27,32}, {8,9,13,23,32}, + {4,13,27,28,32}, {9,13,25,29,32}, {13,16,25,26,33}, {4,13,16,28,33}, + {13,15,16,25,34}, {9,13,18,25,34}, {7,19,25,26,33}, {4,7,19,28,33}, + {7,15,19,25,34}, {6,7,20,25,34}, {9,11,20,25,34} +}; +const signed char triangul_neigh7[][20] = { + {-2,6,-1,NV,-3,10,1,NV,-4,14,5,NV,-5,18,9,NV,-6,-7,13,NV}, + {-2,6,-1,NV,-3,10,1,NV,13,-7,5,NV,-6,8,17,NV,-5,14,-4,NV}, + {-2,6,-1,NV,-3,10,1,NV,-4,14,5,NV,17,-7,9,NV,-6,12,-5,NV}, + {-2,6,-1,NV,-3,18,1,NV,-6,-7,17,NV,-5,16,-4,NV,13,10,5,NV}, + {-2,6,-1,NV,-3,10,1,NV,17,-7,5,NV,-6,16,-5,NV,13,8,-4,NV}, + {-2,14,-1,NV,-5,10,13,NV,-6,-7,5,NV,17,6,1,NV,-4,12,-3,NV}, + {-2,10,-1,NV,-4,18,-3,NV,13,-7,1,NV,-6,8,17,NV,-5,14,5,NV}, + {-2,14,-1,NV,9,-7,13,NV,-6,4,-5,NV,17,6,1,NV,-4,12,-3,NV}, + {-2,18,-1,NV,-6,-7,17,NV,-4,14,-3,NV,-5,16,9,NV,13,6,1,NV}, + {-2,14,-1,NV,-6,16,-5,NV,-4,18,-3,NV,17,-7,1,NV,5,12,9,NV}, + {-2,10,-1,NV,-5,16,-4,NV,13,-7,1,NV,-6,8,17,NV,5,14,-3,NV}, + {-2,14,-1,NV,-6,-7,13,NV,-5,16,-4,NV,17,6,1,NV,9,12,-3,NV}, + {-2,14,-1,NV,-6,16,9,NV,-5,6,-4,NV,17,-7,1,NV,5,12,-3,NV}, + {-2,14,-1,NV,-6,8,-5,NV,5,16,-4,NV,17,-7,1,NV,9,12,-3,NV}, + {-4,6,13,NV,-5,10,1,NV,-6,-7,5,NV,17,2,-1,NV,-3,12,-2,NV}, + {5,-7,13,NV,-6,0,9,NV,-5,6,-4,NV,17,2,-1,NV,-3,12,-2,NV}, + {-4,6,13,NV,9,-7,1,NV,-6,4,-5,NV,17,2,-1,NV,-3,12,-2,NV}, + {-6,-7,9,NV,-5,8,-4,NV,5,2,13,NV,17,10,-1,NV,-3,12,-2,NV}, + {9,-7,13,NV,-6,8,-5,NV,5,0,-4,NV,17,2,-1,NV,-3,12,-2,NV}, + {-5,6,13,NV,-6,-7,1,NV,-3,18,-2,NV,17,2,-1,NV,-4,12,9,NV}, + {-3,6,-2,NV,-4,18,1,NV,13,-7,-1,NV,-6,8,17,NV,-5,14,5,NV}, + {5,-7,13,NV,-6,0,-5,NV,-3,18,-2,NV,17,2,-1,NV,-4,12,9,NV}, + {-6,-7,17,NV,-3,10,-2,NV,-4,14,5,NV,-5,16,9,NV,13,2,-1,NV}, + {-6,16,-5,NV,-3,10,-2,NV,-4,18,5,NV,17,-7,-1,NV,1,12,9,NV}, + {-5,16,-4,NV,-3,18,-2,NV,13,-7,-1,NV,-6,8,17,NV,1,14,5,NV}, + {-6,-7,13,NV,-5,16,-4,NV,-3,18,-2,NV,17,2,-1,NV,5,12,9,NV}, + {-6,16,5,NV,-5,2,-4,NV,-3,18,-2,NV,17,-7,-1,NV,1,12,9,NV}, + {-6,4,-5,NV,1,16,-4,NV,-3,18,-2,NV,17,-7,-1,NV,5,12,9,NV}, + {-5,6,13,NV,-6,-7,1,NV,-4,16,-3,NV,17,2,-1,NV,9,12,-2,NV}, + {-4,16,-3,NV,9,-7,-1,NV,-6,4,13,NV,-5,10,17,NV,1,14,-2,NV}, + {5,-7,13,NV,-6,0,-5,NV,-4,16,-3,NV,17,2,-1,NV,9,12,-2,NV}, + {-6,-7,13,NV,-4,16,-3,NV,-5,12,17,NV,9,2,-1,NV,5,10,-2,NV}, + {-6,12,-5,NV,-4,16,-3,NV,13,-7,-1,NV,1,8,17,NV,5,14,-2,NV}, + {-4,6,-3,NV,-5,16,1,NV,13,-7,-1,NV,-6,8,17,NV,5,14,-2,NV}, + {-6,-7,13,NV,-4,10,-3,NV,-5,16,5,NV,17,2,-1,NV,9,12,-2,NV}, + {-4,10,-3,NV,-6,16,9,NV,-5,6,1,NV,17,-7,-1,NV,5,12,-2,NV}, + {-6,8,-5,NV,-4,10,-3,NV,1,16,5,NV,17,-7,-1,NV,9,12,-2,NV}, + {-5,4,-4,NV,1,16,-3,NV,13,-7,-1,NV,-6,8,17,NV,5,14,-2,NV}, + {-6,-7,13,NV,-5,8,-4,NV,5,16,-3,NV,17,2,-1,NV,9,12,-2,NV}, + {-5,8,-4,NV,-6,16,9,NV,1,6,-3,NV,17,-7,-1,NV,5,12,-2,NV}, + {-6,8,5,NV,-5,2,-4,NV,1,16,-3,NV,17,-7,-1,NV,9,12,-2,NV}, + {-6,4,-5,NV,1,8,-4,NV,5,16,-3,NV,17,-7,-1,NV,9,12,-2,NV} +}; + + +SwapPattern patterns[8] = { + {},{},{}, + { + // pattern with 3 points around edge | 3 tetra in, 2 tetra out + .triangles = triangles3, + .num_triangles = 1, + .triangulations = triangulations3, + .num_triangulations = 1, + .triangul_neigh = triangul_neigh3, + .triangle_in_triangul = triangle_in_triangul3 + }, + + { + // pattern with 4 points around edge | 4 tetra in, 4 tetra out + .triangles = triangles4, + .num_triangles = 4, + .triangulations = triangulations4, + .num_triangulations = 2, + .triangul_neigh = triangul_neigh4, + .triangle_in_triangul = triangle_in_triangul4 + }, + + { + // pattern with 5 points around edge | 5 tetra in, 6 tetra out + .triangles = triangles5, + .num_triangles = 10, + .triangulations = triangulations5, + .num_triangulations = 5, + .triangul_neigh = triangul_neigh5, + .triangle_in_triangul = triangle_in_triangul5 + }, + + { + // pattern with 6 points around edge + .triangles = triangles6, + .num_triangles = 20, + .triangulations = triangulations6, + .num_triangulations = 14, + .triangul_neigh = triangul_neigh6, + .triangle_in_triangul = triangle_in_triangul6 + }, + + { + // pattern with 7 points around edge + .triangles = triangles7, + .num_triangles = 35, + .triangulations = triangulations7, + .num_triangulations = 42, // see Catalan numbers ;) + .triangul_neigh = triangul_neigh7, + .triangle_in_triangul = triangle_in_triangul7 + } +}; + + +/**************** that's it for the DATAs ****************/ + +typedef struct { + struct { + HXTGroup2* tetID_dist; + uint64_t num; + uint64_t size; + } badTets; + + struct { + double* values; + double threshold; + } quality2; +} ThreadShared; + +typedef struct +{ + struct { + uint64_t neigh_up [HXT_MAX_CAVITY_SIZE]; + uint64_t neigh_down[HXT_MAX_CAVITY_SIZE]; + unsigned char flag [HXT_MAX_CAVITY_SIZE]; + uint32_t annulus [HXT_MAX_CAVITY_SIZE]; + uint32_t v_up; + uint32_t v_down; + uint32_t num; + } cavity; + + struct { + uint64_t* tetID; + uint64_t num; + uint64_t size; + } deleted; + + struct { + uint64_t startDist; + uint64_t endDist; + uint32_t first; + uint32_t length; + } partition; +} ThreadLocal; + + + +/* + Compute the squared aspect ratio of a tet. quickly return 0.0 if the tet is inverted +*/ +static inline double tetQuality2 (HXTMesh *mesh, uint32_t p0, uint32_t p1, uint32_t p2, uint32_t p3) +{ + return hxtTetAspectFastRatio (&mesh->vertices.coord[4*p0], + &mesh->vertices.coord[4*p1], + &mesh->vertices.coord[4*p2], + &mesh->vertices.coord[4*p3]); +} + +/*********************************************** + re-allocation functions + ***********************************************/ +static HXTStatus synchronizeReallocation(HXTMesh* mesh, ThreadShared* shared, volatile int* toCopy, volatile int* copy){ + // threads cant be doing something while the realloc portion happen + #pragma omp barrier + + // this unable us to have the same value of toCopy for everyone, as we are sure nothing happens to those variables here + if(toCopy!=copy){ + *copy = *toCopy; + } + + HXTStatus status = HXT_STATUS_OK; + // make reallocations in a critical section + #pragma omp single + { + if(mesh->tetrahedra.num > mesh->tetrahedra.size){ + status = hxtAlignedRealloc(&shared->quality2.values, sizeof(double)*mesh->tetrahedra.num); + if(status==HXT_STATUS_OK) status = hxtTetrahedraReserve(mesh, mesh->tetrahedra.num); + } + } // implicit barrier here + + if(status!=HXT_STATUS_OK) + HXT_TRACE(status); + + return status; +} + +static inline HXTStatus reserveNewDeleted(ThreadLocal* local, uint64_t num){ + num += local->deleted.num; + if(num > local->deleted.size){ + HXT_CHECK( hxtAlignedRealloc(&local->deleted.tetID, 2*num*sizeof(uint64_t)) ); + local->deleted.size = 2*num; + } + + return HXT_STATUS_OK; +} + + +static inline HXTStatus createNewDeleted(HXTMesh* mesh, ThreadShared* shared, ThreadLocal* local) { + uint64_t needed = DELETED_BUFFER_SIZE-local->deleted.num; + + uint64_t ntet; + + // TODO: when multi-threading, don't forget atomic operation and critical sections + #pragma omp atomic capture + { ntet = mesh->tetrahedra.num; mesh->tetrahedra.num+=needed;} + + HXT_CHECK( synchronizeReallocation(mesh, shared, NULL, NULL) ); + HXT_CHECK( reserveNewDeleted(local, needed) ); + + #pragma omp simd + for (uint64_t i=0; i<needed; i++){ + local->deleted.tetID[local->deleted.num+i] = ntet+i; + shared->quality2.values[ntet+i] = DBL_MAX; + // markTetAsDeleted(mesh, ntet+i); + // printf("adding tet %lu to deleted[%lu]\n", ntet+i, local->deleted.num+i); + } + + local->deleted.num = DELETED_BUFFER_SIZE; + return HXT_STATUS_OK; +} + +// suppose that the right capacity for quality2 values is already allocated +static HXTStatus threadShared_update(HXTMesh* mesh, ThreadShared* shared) { + int maxThreads = omp_get_max_threads(); + HXTStatus status = HXT_STATUS_OK; + + uint64_t* badTetsCount; + HXT_CHECK( hxtMalloc(&badTetsCount, maxThreads*sizeof(uint64_t)) ); + + #pragma omp parallel + { + int threadID = omp_get_thread_num(); + badTetsCount[threadID] = 0; + + #pragma omp for schedule(static) + for (int i=0; i<mesh->tetrahedra.num; i++) { + if(mesh->tetrahedra.colors[i]!=UINT16_MAX && shared->quality2.values[i]<shared->quality2.threshold) + badTetsCount[threadID]++; + } + + #pragma omp barrier + #pragma omp single + { + int nthreads = omp_get_num_threads(); + shared->badTets.num = 0; + for (int i=0; i<nthreads; i++) { + uint64_t tsum = badTetsCount[i] + shared->badTets.num; + badTetsCount[i] = shared->badTets.num; + shared->badTets.num = tsum; + } + + if(shared->badTets.num > shared->badTets.size){ + status = hxtAlignedRealloc(&shared->badTets.tetID_dist, sizeof(HXTGroup2)*shared->badTets.num); + shared->badTets.size = shared->badTets.num; + } + } + + if(status==HXT_STATUS_OK){ + #pragma omp for schedule(static) + for (uint64_t i=0; i<mesh->tetrahedra.num; i++) { + if(mesh->tetrahedra.colors[i]!=UINT16_MAX && shared->quality2.values[i]<shared->quality2.threshold){ + uint64_t badTetsID = badTetsCount[threadID]++; + shared->badTets.tetID_dist[badTetsID].v[1] = i; + } + } + } + } + HXT_CHECK( hxtFree(&badTetsCount) ); + + return status; +} + + +static HXTStatus threadShared_create(HXTMesh *mesh, double qualityThreshold, ThreadShared** shared) { + ThreadShared* newShared; + HXT_CHECK( hxtMalloc(&newShared, sizeof(ThreadShared))); + + if(qualityThreshold<0.0) + return HXT_ERROR_MSG(HXT_STATUS_ERROR, "quality threshold must be positive"); + + newShared->badTets.num = 0; + newShared->badTets.size = 0; + newShared->quality2.threshold = qualityThreshold*qualityThreshold/24.; + HXT_CHECK( hxtAlignedMalloc(&newShared->quality2.values, sizeof(double)*mesh->tetrahedra.size) ); + newShared->badTets.tetID_dist = NULL; + + #pragma omp parallel for + for (uint64_t i=0; i<mesh->tetrahedra.num; i++) { + uint32_t* nodes = mesh->tetrahedra.node + 4*i; + if(nodes[3]==HXT_GHOST_VERTEX) + newShared->quality2.values[i] = DBL_MAX; // exterior tetrahedra have maximum quality + else + newShared->quality2.values[i] = tetQuality2(mesh, nodes[0], nodes[1], nodes[2], nodes[3]); + } + + *shared = newShared; + return HXT_STATUS_OK; +} + + + +static HXTStatus threadShared_destroy(ThreadShared** shared) { + HXT_CHECK( hxtAlignedFree(&(*shared)->quality2.values) ); + HXT_CHECK( hxtAlignedFree(&(*shared)->badTets.tetID_dist) ); + HXT_CHECK( hxtFree(shared) ); + return HXT_STATUS_OK; +} + + +static inline int vertexOutOfPartition(HXTVertex* vertices, uint32_t v, uint64_t rel, uint64_t startDist) { + return vertices[v].padding.hilbertDist - startDist >= rel; +} + + +static HXTStatus threadLocals_create(ThreadLocal** locals_ptr, int nthreads) { + ThreadLocal* newLocal; + HXT_CHECK( hxtMalloc(&newLocal, nthreads*sizeof(ThreadLocal))); + + for (int threadID=0; threadID<nthreads; threadID++) { + newLocal[threadID].deleted.num = 0; + newLocal[threadID].deleted.size = DELETED_BUFFER_SIZE; + HXT_CHECK( hxtAlignedMalloc(&newLocal[threadID].deleted.tetID, + sizeof(uint64_t)*DELETED_BUFFER_SIZE) ); + } + + *locals_ptr = newLocal; + return HXT_STATUS_OK; +} + +static HXTStatus threadLocals_update(HXTMesh* mesh, HXTBbox* bbox, double minSize, + ThreadShared* shared, ThreadLocal* locals, + int nthreads, uint32_t* seed, int changePartitions, uint64_t* nConflict_ptr, uint32_t* nbits) { + + if(nthreads>1) { + HXTVertex* vertices = (HXTVertex*) mesh->vertices.coord; + HXTGroup2* badTets = shared->badTets.tetID_dist; + + double indexShift_percent = (double) hxtReproducibleLCG(seed)/RAND_MAX; + + if(changePartitions) { + double hxtDeclareAligned bboxShift[3]; + + bboxShift[0] = (double) hxtReproducibleLCG(seed)/RAND_MAX; + bboxShift[1] = (double) hxtReproducibleLCG(seed)/RAND_MAX; + bboxShift[2] = (double) hxtReproducibleLCG(seed)/RAND_MAX; + + *nbits = hxtAdvancedHilbertBits(bbox, minSize, minSize, + mesh->vertices.num, + mesh->vertices.num, + mesh->vertices.num, + shared->badTets.num, + nthreads); + + HXT_CHECK( hxtVerticesHilbertDist(bbox, vertices, mesh->vertices.num, nbits, bboxShift) ); + } + + #pragma omp parallel for simd aligned(badTets:SIMD_ALIGN) + for (uint64_t i=0; i<shared->badTets.num; i++) { + uint32_t firstNode = mesh->tetrahedra.node[4*badTets[i].v[1]]; + badTets[i].v[0] = vertices[firstNode].padding.hilbertDist; + } + + // sort the bad tetrahedrons following their first node hilbert dist + HXT_CHECK( group2_sort_v0(badTets, shared->badTets.num, (UINT64_C(1)<<(*nbits)) - 1) ); + + const uint64_t step = shared->badTets.num/nthreads; + uint64_t indexShift = MIN(step-1,(uint64_t) indexShift_percent*step); + uint64_t nConflict = 0; + + #pragma omp parallel num_threads(nthreads) reduction(+:nConflict) + { + const int threadID = omp_get_thread_num(); + + uint64_t first = step*threadID + indexShift; + uint64_t startDist = badTets[first].v[0]; + + uint64_t up = 1; + while(first+up<shared->badTets.num && startDist==badTets[first + up].v[0]) + up++; + + first = first+up==shared->badTets.num?0:first+up; + if(first > 0) + startDist = (badTets[first].v[0] + + badTets[first - 1].v[0] + 1)/2; + else + startDist = badTets[shared->badTets.num-1].v[0] + + (badTets[first].v[0] - badTets[shared->badTets.num - 1].v[0])/2; + + locals[threadID].partition.first = first; + locals[threadID].partition.startDist = startDist; + + #pragma omp barrier + + uint64_t firstNext = locals[(threadID+1)%nthreads].partition.first; + uint64_t endDist = locals[(threadID+1)%nthreads].partition.startDist; + locals[threadID].partition.length = (firstNext + shared->badTets.num - first)%shared->badTets.num; + locals[threadID].partition.endDist = endDist; + + uint64_t rel = startDist - endDist; + + // dismiss tetrahedron that are in our list but not in our partition + for (uint64_t i=0; i<locals[threadID].partition.length; i++) { + uint64_t index = (locals[threadID].partition.first + i)%shared->badTets.num; + uint64_t curTet = shared->badTets.tetID_dist[index].v[1]; + + uint32_t* nodes = mesh->tetrahedra.node + 4*curTet; + + if(vertexOutOfPartition(vertices, nodes[0], rel, startDist) + + vertexOutOfPartition(vertices, nodes[1], rel, startDist) + + vertexOutOfPartition(vertices, nodes[2], rel, startDist) + + vertexOutOfPartition(vertices, nodes[3], rel, startDist) > 1) { + shared->badTets.tetID_dist[index].v[1] = HXT_NO_ADJACENT; + nConflict++; + } + } + } + + *nConflict_ptr+=nConflict; + } + else { + locals[0].partition.startDist = 0; + locals[0].partition.endDist = UINT64_MAX; + locals[0].partition.first = 0; + locals[0].partition.length = shared->badTets.num; + } + + return HXT_STATUS_OK; +} + + +static HXTStatus threadLocals_destroy(ThreadLocal** local, int nthreads) { + for (int threadID=0; threadID<nthreads; threadID++) { + HXT_CHECK( hxtAlignedFree(&(*local)[threadID].deleted.tetID) ); + } + HXT_CHECK( hxtFree(local) ); + return HXT_STATUS_OK; +} + + +static HXTStatus flip2_3(HXTMesh* mesh, ThreadShared* shared, ThreadLocal* local, + const uint64_t tet_0, uint16_t color, unsigned out_facet_0) +{ + if(isFacetConstrained(mesh, 4*tet_0 + out_facet_0) || mesh->tetrahedra.neigh[4*tet_0 + out_facet_0]==HXT_NO_ADJACENT) + return HXT_STATUS_INTERNAL; + + uint64_t neigh = mesh->tetrahedra.neigh[4*tet_0 + out_facet_0]; + + + uint64_t tet_1 = neigh/4; + + HXT_ASSERT(tet_1<mesh->tetrahedra.num); + HXT_ASSERT(tet_1!=tet_0); + HXT_ASSERT(mesh->tetrahedra.neigh[neigh]==4*tet_0 + out_facet_0); + unsigned in_facet_1 = neigh%4; + + { // check for conflict with other threads'partition + const uint64_t startDist = local->partition.startDist; + const uint64_t rel = local->partition.endDist - startDist; + HXTVertex* vertices = (HXTVertex*) mesh->vertices.coord; + if(vertexOutOfPartition(vertices, mesh->tetrahedra.node[4*tet_1 + 0], rel, startDist) + + vertexOutOfPartition(vertices, mesh->tetrahedra.node[4*tet_1 + 1], rel, startDist) + + vertexOutOfPartition(vertices, mesh->tetrahedra.node[4*tet_1 + 2], rel, startDist) + + vertexOutOfPartition(vertices, mesh->tetrahedra.node[4*tet_1 + 3], rel, startDist) > 1) + return HXT_STATUS_CONFLICT; + } + + double worst_qual = shared->quality2.values[tet_0]; + + if(shared->quality2.values[tet_1]<worst_qual) + worst_qual = shared->quality2.values[tet_1]; + + local->cavity.v_up = mesh->tetrahedra.node[4*tet_0 + out_facet_0]; + local->cavity.v_down = mesh->tetrahedra.node[4*tet_1 + in_facet_1]; + + // choose a reference facet in the tet_0 + unsigned in_facet_0 = (out_facet_0+1)%4; + + // adding vertices of the annulus: + local->cavity.annulus[0] = mesh->tetrahedra.node[4*tet_0 + UP_VERTEX(in_facet_0, out_facet_0)]; + local->cavity.annulus[1] = mesh->tetrahedra.node[4*tet_0 + DOWN_VERTEX(in_facet_0, out_facet_0)]; + local->cavity.annulus[2] = mesh->tetrahedra.node[4*tet_0 + OUT_VERTEX(in_facet_0, out_facet_0)]; + + local->cavity.neigh_up[0] = mesh->tetrahedra.neigh[4*tet_0 + in_facet_0]; + local->cavity.neigh_up[1] = mesh->tetrahedra.neigh[4*tet_0 + DOWN_FACET(in_facet_0, out_facet_0)]; + local->cavity.neigh_up[2] = mesh->tetrahedra.neigh[4*tet_0 + UP_FACET(in_facet_0, out_facet_0)]; + + uint32_t v = local->cavity.annulus[2]; + + // find one of the vertex in the tet_1 + uint32_t* nodes = mesh->tetrahedra.node + 4*tet_1; + + unsigned out_facet_1; + for (out_facet_1=0; out_facet_1<4; out_facet_1++) + if(nodes[out_facet_1]==v) + break; + + // HXT_ASSERT(out_facet_1!=4); + // HXT_ASSERT(out_facet_1!=in_facet_1); + // HXT_ASSERT((isEdgeConstrainedSafe(mesh, tet_0, in_facet_0, out_facet_0)!=0)==(isEdgeConstrainedSafe(mesh, tet_1, in_facet_1, out_facet_1)!=0)); + // HXT_ASSERT((isEdgeConstrainedSafe(mesh, tet_0, in_facet_0, out_facet_0)!=0)==(isEdgeConstrainedSafe(mesh, tet_1, in_facet_1, out_facet_1)!=0)); + // HXT_ASSERT((isEdgeConstrainedSafe(mesh, tet_0, DOWN_FACET(in_facet_0, out_facet_0), out_facet_0)!=0) + // ==(isEdgeConstrainedSafe(mesh, tet_1, DOWN_FACET(in_facet_1, out_facet_1), in_facet_1)!=0)); + // HXT_ASSERT((isEdgeConstrainedSafe(mesh, tet_0, UP_FACET(in_facet_0, out_facet_0), out_facet_0)!=0) + // ==(isEdgeConstrainedSafe(mesh, tet_1, UP_FACET(in_facet_1, out_facet_1), in_facet_1)!=0)); + // HXT_ASSERT((isFacetConstrained(mesh, 4*tet_0 + out_facet_0)!=0)==(isFacetConstrained(mesh, 4*tet_1 + in_facet_1)!=0)); + // if(mesh->tetrahedra.neigh[neigh]!=4*tet_0+out_facet_0) + // return HXT_ERROR_MSG(HXT_STATUS_ERROR, "mesh->tetrahedra.neigh[%lu]==%lu instead of %lu",neigh,mesh->tetrahedra.neigh[neigh],4*tet_0+out_facet_0); + + local->cavity.neigh_down[0] = mesh->tetrahedra.neigh[4*tet_1 + out_facet_1]; + local->cavity.neigh_down[1] = mesh->tetrahedra.neigh[4*tet_1 + DOWN_FACET(in_facet_1, out_facet_1)]; + local->cavity.neigh_down[2] = mesh->tetrahedra.neigh[4*tet_1 + UP_FACET(in_facet_1, out_facet_1)]; + + local->cavity.flag[0] = ((isFacetConstrained(mesh, 4*tet_0 + in_facet_0)!=0)<<12) + + ((isFacetConstrained(mesh, 4*tet_1 + out_facet_1)!=0)<<8) + + ((isEdgeConstrainedSafe(mesh, tet_1, out_facet_1, DOWN_FACET(in_facet_1, out_facet_1))!=0)<<2) + + ((isEdgeConstrainedSafe(mesh, tet_0, in_facet_0, DOWN_FACET(in_facet_0, out_facet_0))!=0)<<3) + + ((isEdgeConstrainedSafe(mesh, tet_1, out_facet_1, UP_FACET(in_facet_1, out_facet_1))!=0)<<6) + + ((isEdgeConstrainedSafe(mesh, tet_0, in_facet_0, UP_FACET(in_facet_0, out_facet_0))!=0)<<7) + + ((isEdgeConstrainedSafe(mesh, tet_0, in_facet_0, out_facet_0)!=0)<<11); + + local->cavity.flag[1] = ((isFacetConstrained(mesh, 4*tet_0 + DOWN_FACET(in_facet_0, out_facet_0))!=0)<<12) + + ((isFacetConstrained(mesh, 4*tet_1 + DOWN_FACET(in_facet_1, out_facet_1))!=0)<<8) + + ((isEdgeConstrainedSafe(mesh, tet_1, DOWN_FACET(in_facet_1, out_facet_1), UP_FACET(in_facet_1, out_facet_1))!=0)<<2) + + ((isEdgeConstrainedSafe(mesh, tet_0, DOWN_FACET(in_facet_0, out_facet_0), UP_FACET(in_facet_0, out_facet_0))!=0)<<3) + + ((isEdgeConstrainedSafe(mesh, tet_1, DOWN_FACET(in_facet_1, out_facet_1), out_facet_1)!=0)<<6) + + ((isEdgeConstrainedSafe(mesh, tet_0, DOWN_FACET(in_facet_0, out_facet_0), in_facet_0)!=0)<<7) + + ((isEdgeConstrainedSafe(mesh, tet_0, DOWN_FACET(in_facet_0, out_facet_0), out_facet_0)!=0)<<11); + + local->cavity.flag[2] = ((isFacetConstrained(mesh, 4*tet_0 + UP_FACET(in_facet_0, out_facet_0))!=0)<<12) + + ((isFacetConstrained(mesh, 4*tet_1 + UP_FACET(in_facet_1, out_facet_1))!=0)<<8) + + ((isEdgeConstrainedSafe(mesh, tet_1, UP_FACET(in_facet_1, out_facet_1), out_facet_1)!=0)<<2) + + ((isEdgeConstrainedSafe(mesh, tet_0, UP_FACET(in_facet_0, out_facet_0), in_facet_0)!=0)<<3) + + ((isEdgeConstrainedSafe(mesh, tet_1, UP_FACET(in_facet_1, out_facet_1), DOWN_FACET(in_facet_1, out_facet_1))!=0)<<6) + + ((isEdgeConstrainedSafe(mesh, tet_0, UP_FACET(in_facet_0, out_facet_0), DOWN_FACET(in_facet_0, out_facet_0))!=0)<<7) + + ((isEdgeConstrainedSafe(mesh, tet_0, UP_FACET(in_facet_0, out_facet_0), out_facet_0)!=0)<<11); + + // now we have everything... we just need to test if the quality of tetrahedra would be good + double qual[3]; + for (int i=0; i<3; i++) { + qual[i] = tetQuality2(mesh, local->cavity.annulus[i], local->cavity.annulus[(i+1)%3], local->cavity.v_up, local->cavity.v_down); + if(qual[i]<worst_qual){ + return HXT_STATUS_INTERNAL; + } + } + + if(local->deleted.num<1) + HXT_CHECK( createNewDeleted(mesh, shared, local) ); + + local->deleted.num--; + uint64_t newTet[3] = {tet_0, tet_1, local->deleted.tetID[local->deleted.num]}; + + for (unsigned i=0; i<3; i++) { + uint64_t curTet = newTet[i]; + mesh->tetrahedra.node[4*curTet + 0] = local->cavity.annulus[i]; + mesh->tetrahedra.node[4*curTet + 1] = local->cavity.annulus[(i+1)%3]; + mesh->tetrahedra.node[4*curTet + 2] = local->cavity.v_up; + mesh->tetrahedra.node[4*curTet + 3] = local->cavity.v_down; + + mesh->tetrahedra.neigh[4*curTet + 0] = 4*newTet[(i+1)%3] + 1; + // mesh->tetrahedra.neigh[4*curTet + 1] = 4*newTet[(i+4)%3] + 0; + mesh->tetrahedra.neigh[4*newTet[(i+1)%3] + 1] = 4*curTet + 0; + + mesh->tetrahedra.neigh[4*curTet + 2] = local->cavity.neigh_down[i]; + if( local->cavity.neigh_down[i]!=HXT_NO_ADJACENT) + mesh->tetrahedra.neigh[local->cavity.neigh_down[i]] = 4*curTet + 2; + + mesh->tetrahedra.neigh[4*curTet + 3] = local->cavity.neigh_up[i]; + if( local->cavity.neigh_up[i]!=HXT_NO_ADJACENT) + mesh->tetrahedra.neigh[local->cavity.neigh_up[i]] = 4*curTet + 3; + + mesh->tetrahedra.colors[curTet] = color; + + // TODO: verify flags are well done + mesh->tetrahedra.flag[curTet] = local->cavity.flag[i]; + shared->quality2.values[curTet] = qual[i]; + } + + return HXT_STATUS_OK; + // return HXT_STATUS_INTERNAL; +} + + +static inline HXTStatus buildEdgeCavity(HXTMesh* mesh, ThreadLocal* local, + const uint64_t badTet, uint16_t color, + int in_facet, int out_facet) +{ + const uint64_t startDist = local->partition.startDist; + const uint64_t rel = local->partition.endDist - startDist; + int edgeOut = 0; + HXTVertex* vertices = (HXTVertex*) mesh->vertices.coord; + + uint64_t curTet = badTet; + local->cavity.num = 0; + + if(isEdgeConstrainedSafe(mesh, badTet, in_facet, out_facet)) + return HXT_STATUS_INTERNAL; + + local->cavity.v_up = mesh->tetrahedra.node[4*badTet + UP_VERTEX(in_facet, out_facet)]; + edgeOut += vertexOutOfPartition(vertices, local->cavity.v_up, rel, startDist); + local->cavity.v_down = mesh->tetrahedra.node[4*badTet + DOWN_VERTEX(in_facet, out_facet)]; + edgeOut += vertexOutOfPartition(vertices, local->cavity.v_down, rel, startDist); + + HXT_CHECK( reserveNewDeleted(local, 7) ); + + do{ + // add the current tetrahedra + local->deleted.tetID[local->deleted.num + local->cavity.num] = curTet; + + // add the neighbor up and down + local->cavity.neigh_up[local->cavity.num] = mesh->tetrahedra.neigh[4*curTet + UP_FACET(in_facet, out_facet)]; + local->cavity.neigh_down[local->cavity.num] = mesh->tetrahedra.neigh[4*curTet + DOWN_FACET(in_facet, out_facet)]; + + // TODO: just store one flag for up and down. the one of the default tetrahedron + local->cavity.flag[local->cavity.num] = (isFacetConstrained(mesh, 4*curTet + UP_FACET(in_facet, out_facet))!=0) + + ((isEdgeConstrainedSafe(mesh, curTet, UP_FACET(in_facet, out_facet), out_facet)!=0)<<1) + + ((isEdgeConstrainedSafe(mesh, curTet, UP_FACET(in_facet, out_facet), DOWN_FACET(in_facet, out_facet))!=0)<<2) + + ((isEdgeConstrainedSafe(mesh, curTet, UP_FACET(in_facet, out_facet), in_facet)!=0)<<3) + + ((isFacetConstrained(mesh, 4*curTet + DOWN_FACET(in_facet, out_facet))!=0)<<4) + + ((isEdgeConstrainedSafe(mesh, curTet, DOWN_FACET(in_facet, out_facet), out_facet)!=0)<<5) + + ((isEdgeConstrainedSafe(mesh, curTet, DOWN_FACET(in_facet, out_facet), in_facet)!=0)<<6) + + ((isEdgeConstrainedSafe(mesh, curTet, UP_FACET(in_facet, out_facet), DOWN_FACET(in_facet, out_facet))!=0)<<7); + + // add the annulus vertex + uint32_t oldV = mesh->tetrahedra.node[4*curTet + IN_VERTEX(in_facet, out_facet)]; + uint32_t newV = mesh->tetrahedra.node[4*curTet + OUT_VERTEX(in_facet, out_facet)]; + + local->cavity.annulus[local->cavity.num] = oldV; + local->cavity.num++; + + // go into the neighbor through out_facet + uint64_t neigh = mesh->tetrahedra.neigh[4*curTet + out_facet]; + if(neigh == HXT_NO_ADJACENT + || (isFacetConstrained(mesh, neigh)!=0) + || local->cavity.num>=HXT_MAX_CAVITY_SIZE) { + return HXT_STATUS_INTERNAL; + } + + if(vertexOutOfPartition(vertices, oldV, rel, startDist) + + vertexOutOfPartition(vertices, newV, rel, startDist) + edgeOut > 1) { + return HXT_STATUS_CONFLICT; + } + + curTet = neigh/4; + in_facet = neigh%4; + + uint32_t* nodes = mesh->tetrahedra.node + 4*curTet; + + for (out_facet=0; out_facet<3; out_facet++) + if(nodes[out_facet]==newV) + break; + + }while(curTet!=badTet); + + return HXT_STATUS_OK; +} + + +static HXTStatus edgeSwap(HXTMesh *mesh, ThreadShared* shared, ThreadLocal* local, + const uint64_t badTet, const uint16_t color, + int in_facet, int out_facet) +{ + HXT_CHECK( buildEdgeCavity(mesh, local, badTet, color, in_facet, out_facet) ); + + // find worst quality2 tet of the cavity + double worst = DBL_MAX; + for (uint64_t i=0; i<local->cavity.num; i++) { + uint64_t tet = local->deleted.tetID[local->deleted.num+i]; + double qual = shared->quality2.values[tet]; + if(qual<worst) + worst = qual; + } + + const SwapPattern* patt = &patterns[local->cavity.num]; + const int num_triangle_per_triangul = local->cavity.num-2; + uint32_t* annulus = local->cavity.annulus; + + // calculate qualities of all possible tetrahedra + // TODO: optimize the fact that a facet is common in qual_up and qual_down + double hxtDeclareAligned qual_up[35]; + double hxtDeclareAligned qual_down[35]; + uint64_t mask = 0; + for (int i=0; i<patt->num_triangles; i++) { + uint32_t p0 = annulus[patt->triangles[i][0]]; + uint32_t p1 = annulus[patt->triangles[i][1]]; + uint32_t p2 = annulus[patt->triangles[i][2]]; + + // printf("%u %u %u\n", p0, p1, p2); + + qual_up[i] = tetQuality2(mesh, p0, p1, local->cavity.v_up, p2); + qual_down[i] = tetQuality2(mesh, p0, p1, p2, local->cavity.v_down); + + if(qual_up[i]<=worst || qual_down[i]<=worst) + mask |= patt->triangle_in_triangul[i]; + } + + // find the best triangulation + int best_triangulation = -1; + double best_worst = 0; + for (int i=0; i<patt->num_triangulations; i++) { + if((mask & (UINT64_C(1)<<i))==0) { + double cur_worst = DBL_MAX; + // this mean that no triangle in the triangulation + // is worst than the current worst tetrahedron + for (int j=0; j<num_triangle_per_triangul; j++) { + double q_u = qual_up[patt->triangulations[i][j]]; + double q_d = qual_down[patt->triangulations[i][j]]; + if(q_u<best_worst || q_d<best_worst){ + cur_worst = 0; + break; + } + if(q_u<cur_worst) + cur_worst = q_u; + if(q_d<cur_worst) + cur_worst = q_d; + } + + if(cur_worst > best_worst){ + best_worst = cur_worst; + best_triangulation = i; + } + } + } + + // if no triangulation are better, return + if(best_triangulation==-1) { + return HXT_STATUS_INTERNAL; + } + + + // mark new deleted tet as deleted + for (uint64_t i=0; i<local->cavity.num; i++) { + shared->quality2.values[local->deleted.tetID[local->deleted.num+i]] = DBL_MAX; // deleted tets have good quality2 + // markTetAsDeleted(mesh, local->deleted.tetID[local->deleted.num+i]); + } + local->deleted.num += local->cavity.num; + + // reserve enough tetrahedra + if(2*num_triangle_per_triangul > local->deleted.num){ // tetrahedra are created... + HXT_CHECK(createNewDeleted(mesh, shared, local) ); + } + + uint64_t start = local->deleted.num - 2*num_triangle_per_triangul; + + // make the swap + for (int i=0; i<num_triangle_per_triangul; i++) { + uint32_t tri = patt->triangulations[best_triangulation][i]; + uint32_t p0 = annulus[patt->triangles[tri][0]]; + uint32_t p1 = annulus[patt->triangles[tri][1]]; + uint32_t p2 = annulus[patt->triangles[tri][2]]; + + // neighbor information. if positive, the neighbor is a new tetrahedra + int n0 = patt->triangul_neigh[best_triangulation][4*i]; + int n1 = patt->triangul_neigh[best_triangulation][4*i+1]; + int n2 = patt->triangul_neigh[best_triangulation][4*i+2]; + + const uint64_t newTet_up = local->deleted.tetID[start + i*2]; + const uint64_t newTet_down = local->deleted.tetID[start + i*2 + 1]; + + // create the upper tet. + { + uint32_t* nodes = mesh->tetrahedra.node + 4*newTet_up; + uint64_t* neigh = mesh->tetrahedra.neigh + 4*newTet_up; + + mesh->tetrahedra.colors[newTet_up] = color; + mesh->tetrahedra.flag[newTet_up] = 0; + shared->quality2.values[newTet_up] = qual_up[tri]; + + nodes[0] = p0; + nodes[1] = p1; + nodes[2] = local->cavity.v_up; + nodes[3] = p2; + + if(n0>=0){ + neigh[0] = 4*local->deleted.tetID[start + (n0/4)*2] + (n0%4) + (n0%4)/2; + } + else { + neigh[0] = local->cavity.neigh_up[-n0-1]; + + // (down=2, in=3, out=1, up=0) + mesh->tetrahedra.flag[newTet_up] |= local->cavity.flag[-n0-1] & 0xF; + + if(neigh[0]!=HXT_NO_ADJACENT) + mesh->tetrahedra.neigh[neigh[0]] = 4*newTet_up + 0; + } + if(n1>=0){ + neigh[1] = 4*local->deleted.tetID[start + (n1/4)*2] + (n1%4) + (n1%4)/2; + } + else { + neigh[1] = local->cavity.neigh_up[-n1-1]; + + // (down=2, in=0, out=3, up=1) + mesh->tetrahedra.flag[newTet_up] |= (local->cavity.flag[-n1-1]&5)<<4 | // face (bit 0) is the up_facet => 1*4 (bit 4) + (local->cavity.flag[-n1-1]&2)<<6 | // first edge (bit 1) was between up_facet and out_facet => 1-3 (bit 7) + // (local->cavity.flag[-n1-1]&4)<<4 | // second edge (bit 2) was between up_facet and down_facet => 1-2 (bit 6) + (local->cavity.flag[-n1-1]&8)>>2; // third edge (bit 3) was between up_facet and in_facet => 0-1 (bit 1) + + if(neigh[1]!=HXT_NO_ADJACENT) + mesh->tetrahedra.neigh[neigh[1]] = 4*newTet_up + 1; + } + neigh[2] = newTet_down*4 + 3; + if(n2>=0){ + neigh[3] = 4*local->deleted.tetID[start + (n2/4)*2] + (n2%4) + (n2%4)/2; + } + else { + neigh[3] = local->cavity.neigh_up[-n2-1]; + + // (down=2, in=1, out=0, up=3) + mesh->tetrahedra.flag[newTet_up] |= (local->cavity.flag[-n2-1]&1)<<12 | // face (bit 0) is the up_facet => 3*4 (bit 12) + (local->cavity.flag[-n2-1]&2)<<2 | // first edge (bit 1) was between up_facet and out_facet => 0-3 (bit 3) + (local->cavity.flag[-n2-1]&4)<<9 | // second edge (bit 2) was between up_facet and down_facet => 2-3 (bit 11) + (local->cavity.flag[-n2-1]&8)<<4; // third edge (bit 3) was between up_facet and in_facet => 1-3 (bit 7) + + if(neigh[3]!=HXT_NO_ADJACENT) + mesh->tetrahedra.neigh[neigh[3]] = 4*newTet_up + 3; + } + } + + // create the down tet. + { + uint32_t* nodes = mesh->tetrahedra.node + 4*newTet_down; + uint64_t* neigh = mesh->tetrahedra.neigh + 4*newTet_down; + + mesh->tetrahedra.colors[newTet_down] = color; + mesh->tetrahedra.flag[newTet_down] = 0; + shared->quality2.values[newTet_down] = qual_down[tri]; + + nodes[0] = p0; + nodes[1] = p1; + nodes[2] = p2; + nodes[3] = local->cavity.v_down; + + if(n0>=0){ + neigh[0] = 4*local->deleted.tetID[start + (n0/4)*2 +1] + (n0%4); + } + else { + neigh[0] = local->cavity.neigh_down[-n0-1]; + + // (down=0, in=2, out=1, up=3) + mesh->tetrahedra.flag[newTet_down] |= (local->cavity.flag[-n0-1] & 0xF0)>>4; + + if(neigh[0]!=HXT_NO_ADJACENT) + mesh->tetrahedra.neigh[neigh[0]] = 4*newTet_down + 0; + } + if(n1>=0){ + neigh[1] = 4*local->deleted.tetID[start + (n1/4)*2 +1] + (n1%4); + } + else { + neigh[1] = local->cavity.neigh_down[-n1-1]; + + // (down=1, in=0, out=2, up=3) + mesh->tetrahedra.flag[newTet_down] |= (local->cavity.flag[-n1-1]&0x90) | // face (bit 4) is the down_facet => 1*4 (bit 4) + (local->cavity.flag[-n1-1]&32)<<1 | // first edge (bit 5) was between down_facet and out_facet => 1-2 (bit 6) + (local->cavity.flag[-n1-1]&64)>>5; // second edge (bit 6) was between down_facet and in_facet => 0-1 (bit 1) + //(local->cavity.flag[-n1-1]&128); // third edge (bit 7) was between down_facet and up_facet => 1-3 (bit 7) + + if(neigh[1]!=HXT_NO_ADJACENT) + mesh->tetrahedra.neigh[neigh[1]] = 4*newTet_down + 1; + } + if(n2>=0){ + neigh[2] = 4*local->deleted.tetID[start + (n2/4)*2 +1] + (n2%4); + } + else { + neigh[2] = local->cavity.neigh_down[-n2-1]; + + // (down=2, in=1, out=0, up=3) + mesh->tetrahedra.flag[newTet_down] |= (local->cavity.flag[-n2-1]&0x90)<<4 | // face (bit 4) is the down_facet => 2*4 (bit 8) + (local->cavity.flag[-n2-1]&32)>>3 | // first edge (bit 5) was between down_facet and out_facet => 0-2 (bit 2) + (local->cavity.flag[-n2-1]&64); // second edge (bit 6) was between down_facet and in_facet => 1-2 (bit 6) + //(local->cavity.flag[-n2-1]&128)<<4; // third edge (bit 7) was between down_facet and up_facet => 2-3 (bit 11) + + if(neigh[2]!=HXT_NO_ADJACENT) + mesh->tetrahedra.neigh[neigh[2]] = 4*newTet_down + 2; + } + neigh[3] = newTet_up*4 + 2; + } + } + + local->deleted.num = start; + + return HXT_STATUS_OK; +} + + +static inline HXTStatus buildVertexCavity(HXTMesh* mesh, ThreadLocal* local, + uint64_t curFace, + const uint32_t numVerticesConstrained) { + const uint64_t startDist = local->partition.startDist; + const uint64_t rel = local->partition.endDist - startDist; + const uint32_t vertex = mesh->tetrahedra.node[curFace]; + HXTVertex* vertices = (HXTVertex*) mesh->vertices.coord; + + // the vertex we are moving should be in the partition or we don't even try... + if(vertex < numVerticesConstrained || vertexOutOfPartition(vertices, vertex, rel, startDist)) + return HXT_STATUS_INTERNAL; + + HXT_CHECK( reserveNewDeleted(local, 4) ); + local->deleted.tetID[local->deleted.num++] = curFace; + markTetAsDeleted(mesh, curFace/4); + + for (uint64_t start=local->deleted.num-1; start<local->deleted.num; start++) { + HXT_CHECK( reserveNewDeleted(local, 3) ); + + uint64_t curFace = local->deleted.tetID[start]; + uint64_t curTet = curFace/4; + unsigned face = curFace%4; + + // all the cases where this function will fail. + for (int j=0; j<3; j++) { + uint64_t f = (face+j+1)%4; + uint64_t neigh = mesh->tetrahedra.neigh[4*curTet + f]; + uint64_t neighTet = neigh/4; + unsigned neighF = neigh%4; + + if(neigh==HXT_NO_ADJACENT) + return HXT_STATUS_INTERNAL; + + if(isTetDeleted(mesh, neighTet)) + continue; + + if(isFacetConstrained(mesh, 4*curTet+f)) + return HXT_STATUS_INTERNAL; + + for(int k=0; k<4; k++){ + if(k!=f && isEdgeConstrainedSafe(mesh, curTet, f, k)) + return HXT_STATUS_INTERNAL; + } + + // uint32_t a = mesh->tetrahedra.node[(face+j)%4]; + uint32_t b = mesh->tetrahedra.node[4*curTet + (face+j+2)%4]; + uint32_t c = mesh->tetrahedra.node[4*curTet + (face+j+3)%4]; + uint32_t d = mesh->tetrahedra.node[neigh]; + if(vertexOutOfPartition(vertices, b, rel, startDist) + + vertexOutOfPartition(vertices, c, rel, startDist) + + vertexOutOfPartition(vertices, d, rel, startDist) > 1) + return HXT_STATUS_CONFLICT; + + // we can add the face without any fears now, but we have to find where is the vertex + if(mesh->tetrahedra.node[4*neighTet + (neighF+1)%4]==vertex) + local->deleted.tetID[local->deleted.num++] = 4*neighTet + (neighF+1)%4; + else if(mesh->tetrahedra.node[4*neighTet + (neighF+2)%4]==vertex) + local->deleted.tetID[local->deleted.num++] = 4*neighTet + (neighF+2)%4; + else + local->deleted.tetID[local->deleted.num++] = 4*neighTet + (neighF+3)%4; + + markTetAsDeleted(mesh, neighTet); + } + } + + return HXT_STATUS_OK; +} + +static double compute_worst_quality(HXTMesh* mesh, ThreadLocal* local, uint64_t prevNumDeleted) { + double worst = DBL_MAX; + + // compute worst quality + for (uint64_t i=prevNumDeleted; i<local->deleted.num; i++) { + uint64_t tet = local->deleted.tetID[i]/4; + double qual = tetQuality2(mesh, + mesh->tetrahedra.node[4*tet + 0], + mesh->tetrahedra.node[4*tet + 1], + mesh->tetrahedra.node[4*tet + 2], + mesh->tetrahedra.node[4*tet + 3]); + if(qual < worst) + worst = qual; + } + + return worst; +} + + +static inline void interpolate_coord(const double start[3], const double end[3], double coord[3], double alpha) { + for (int i=0; i<3; i++) { + coord[i] = alpha*(start[i])+(1-alpha)*end[i]; + } +} + + +static HXTStatus golden_section_search(HXTMesh* mesh, + ThreadShared* shared, + ThreadLocal* local, + uint64_t prevNumDeleted, + double start[3], + double end[3], + uint32_t vertex) +{ + double* coord = mesh->vertices.coord + 4*vertex; + double tol = 0.00001; + const double invPhi = 0.6180339887498949; + double a = 1; + double b = 0; + double c = invPhi; + double d = 1-invPhi; + + interpolate_coord(start, end, coord, a); + double fa = compute_worst_quality(mesh, local, prevNumDeleted); + + interpolate_coord(start, end, coord, b); + double fb = compute_worst_quality(mesh, local, prevNumDeleted); + + interpolate_coord(start, end, coord, c); + double fc = compute_worst_quality(mesh, local, prevNumDeleted); + + interpolate_coord(start, end, coord, d); + double fd = compute_worst_quality(mesh, local, prevNumDeleted); + + + // double fstart = fa, fend = fb; + // int iter = 0; + + while(fc>fa && fc>fb && fd>fa && fd>fb && (fabs(fa-fb)>tol || fa==0.0 || fb==0.0)) { + if(fc>=fd) { // maximum is between a & d, (b <-- d and d <-- c, compute new position of c) + fb = fd; + b = d; + fd = fc; + d = c; + c = a + invPhi*(b-a); // (1-invPhi)*a+invPhi*b; + interpolate_coord(start, end, coord, c); + fc = compute_worst_quality(mesh, local, prevNumDeleted); + } + else { // maximum is between c & b, (a <-- c and c <-- d, compute new position of d) + fa = fc; + a = c; + fc = fd; + c = d; + d = b + invPhi*(a-b); // invPhi*a+(1-invPhi)*b; + interpolate_coord(start, end, coord, d); + fd = compute_worst_quality(mesh, local, prevNumDeleted); + } + + // iter++; + } + + // printf("fa:%f fb:%f fc:%f fd:%f fstart:%f fend:%f iter:%d\n", fa, fb, fc, fd, fstart, fend, iter); + + // best of fa fb + if(fb > fa) { + // printf("%f\n", fb); + a = b; + interpolate_coord(start, end, coord, b); + } + else { + // printf("%f\n", fa); + interpolate_coord(start, end, coord, a); + } + + if(a<0.999) { + // compute new qualities + for (uint64_t i=prevNumDeleted; i<local->deleted.num; i++) { + uint64_t tet = local->deleted.tetID[i]/4; + shared->quality2.values[tet] = tetQuality2(mesh, + mesh->tetrahedra.node[4*tet + 0], + mesh->tetrahedra.node[4*tet + 1], + mesh->tetrahedra.node[4*tet + 2], + mesh->tetrahedra.node[4*tet + 3]); + } + } + else if(a!=1.0) { + interpolate_coord(start, end, coord, 1.0); + } + + return HXT_STATUS_OK; +} + +// curFace is the face opposite to the vertex we are going to move +static HXTStatus smoothing(HXTMesh *mesh, + ThreadShared* shared, + ThreadLocal* local, + const uint64_t curFace, + const uint32_t numVerticesConstrained) +{ + uint64_t prevNumDeleted = local->deleted.num; + HXTStatus status = buildVertexCavity(mesh, local, curFace, numVerticesConstrained); + if(status!=HXT_STATUS_OK && status!=HXT_STATUS_INTERNAL && status!=HXT_STATUS_CONFLICT){ + HXT_TRACE(status); + return status; + } + + for (uint64_t i=prevNumDeleted; i<local->deleted.num; i++) { + unmarkTetAsDeleted(mesh, local->deleted.tetID[i]/4); + } + + if(status==HXT_STATUS_INTERNAL || status==HXT_STATUS_CONFLICT){ + local->deleted.num = prevNumDeleted; + return status; + } + + const uint64_t numTetInCavity = local->deleted.num - prevNumDeleted; + + // move the point + uint32_t vertex = mesh->tetrahedra.node[curFace]; + double oldCoord[3]; + double centerCoord[3] = {0}; + for (int j=0; j<3; j++) { + oldCoord[j] = mesh->vertices.coord[4*vertex+j]; + } + + for (uint64_t i=prevNumDeleted; i<local->deleted.num; i++) { + uint64_t tet = local->deleted.tetID[i]/4; + unsigned face = local->deleted.tetID[i]%4; + double* a = mesh->vertices.coord + 4*mesh->tetrahedra.node[4*tet + (face+1)%4]; + double* b = mesh->vertices.coord + 4*mesh->tetrahedra.node[4*tet + (face+2)%4]; + double* c = mesh->vertices.coord + 4*mesh->tetrahedra.node[4*tet + (face+3)%4]; + for (int j=0; j<3; j++) { + centerCoord[j] += (a[j]+b[j]+c[j])/3.0; + } + } + + for (int j=0; j<3; j++) { + centerCoord[j] /= (double) numTetInCavity ; + } + + HXT_CHECK( golden_section_search(mesh, shared, local, prevNumDeleted, oldCoord, centerCoord, vertex) ); + + local->deleted.num = prevNumDeleted; + + if(shared->quality2.values[curFace/4] < shared->quality2.threshold) + return HXT_STATUS_INTERNAL; + + return HXT_STATUS_OK; +} + + +HXTStatus hxtOptimizeTetrahedra(HXTMesh *mesh, + HXTBbox* bbox, + double minSize, + double qualityThreshold, + uint32_t numVerticesConstrained){ + ThreadLocal* locals = NULL; + ThreadShared* shared = NULL; + volatile HXTStatus globalStatus = HXT_STATUS_OK; + const int maxThreads = omp_get_max_threads(); + uint32_t seed = 1; + uint32_t nbits = 0; + int changePartitions = 1; + + HXT_CHECK( threadShared_create(mesh, qualityThreshold, &shared) ); + HXT_CHECK( threadLocals_create(&locals, maxThreads) ); + + int nthreads = maxThreads; + uint64_t nSwaps = UINT64_MAX; + uint64_t nConflict = 0; + uint64_t nAttempt = 1; + + do { + int threadFinished = 0; + + HXT_CHECK( threadShared_update(mesh, shared) ); + if(shared->badTets.num==0) + break; + + // TODO: we still need to update heuristics + if(nSwaps<10 || 100.*nConflict/nAttempt > 45.0) { + nthreads=(nthreads+1)/2; + changePartitions = 1; + } + else if(100.*nConflict/nAttempt > 8.0){ + changePartitions=1; + } + + nSwaps = 0; + nConflict = 0; + + HXT_CHECK( threadLocals_update(mesh, bbox, minSize, shared, locals, nthreads, &seed, changePartitions, &nConflict, &nbits) ); + + nAttempt = nConflict; + changePartitions = 0; + HXT_INFO("%lu bad tetrahedra being optimized on %d threads", shared->badTets.num, nthreads); + + #pragma omp parallel num_threads(nthreads) reduction(+:nSwaps,nConflict,nAttempt) + { + const int threadID = omp_get_thread_num(); + ThreadLocal* local = locals + threadID; + + for (uint64_t i=0; i<local->partition.length && globalStatus==HXT_STATUS_OK; i++) { + // eliminate the bad tetrahedra with swaps + uint64_t index = (local->partition.first + i)%shared->badTets.num; + uint64_t curTet = shared->badTets.tetID_dist[index].v[1]; + + if(curTet==HXT_NO_ADJACENT || shared->quality2.values[curTet]>= shared->quality2.threshold) + continue; + + uint16_t color = mesh->tetrahedra.colors[curTet]; + uint64_t* neighs = mesh->tetrahedra.neigh + 4*curTet; + + /*** sort the neighbor by their qualities **/ + + double hxtDeclareAligned qual[4]; + for (int i=0; i<4; i++) { + if(neighs[i]==HXT_NO_ADJACENT || + mesh->tetrahedra.colors[neighs[i]/4]!=color) { + qual[i]=DBL_MAX; + } + else + qual[i] = shared->quality2.values[neighs[i]/4]; + } + + int hxtDeclareAligned order[4] = {0,1,2,3}; + + // sort first pair + if(qual[order[1]] < qual[order[0]]) + SWAP(order[1], order[0]); + + // sort second pair + if(qual[order[3]] < qual[order[2]]) + SWAP(order[3], order[2]); + + // after this, order[0] is at the right place + if(qual[order[2]] < qual[order[0]]) + SWAP(order[2], order[0]); + + // after this, order[3] is at the right place + if(qual[order[3]] < qual[order[1]]) + SWAP(order[3], order[1]); + + // remains order[1] and order[2] + if(qual[order[2]] < qual[order[1]]) + SWAP(order[2], order[1]); + + HXTStatus status=HXT_STATUS_OK; + status = smoothing(mesh, shared, local, 4*curTet+order[3], numVerticesConstrained); + nConflict+=status==HXT_STATUS_CONFLICT; nAttempt++; + if(status<=HXT_STATUS_INTERNAL) { + status = smoothing(mesh, shared, local, 4*curTet+order[2], numVerticesConstrained); + nConflict+=status==HXT_STATUS_CONFLICT; nAttempt++; + if(status<=HXT_STATUS_INTERNAL) { + status = smoothing(mesh, shared, local, 4*curTet+order[1], numVerticesConstrained); + nConflict+=status==HXT_STATUS_CONFLICT; nAttempt++; + if(status<=HXT_STATUS_INTERNAL) { + status = smoothing(mesh, shared, local, 4*curTet+order[0], numVerticesConstrained); + nConflict+=status==HXT_STATUS_CONFLICT; nAttempt++; + } + } + } + + + /*** make a swap whenever it is possible and it is an improvement ***/ + if(status<=HXT_STATUS_INTERNAL && qual[order[1]]!=DBL_MAX) { + status = edgeSwap(mesh, shared, local, curTet, color, order[0], order[1]); + nConflict+=status==HXT_STATUS_CONFLICT; nAttempt++; + if(status<=HXT_STATUS_INTERNAL && qual[order[2]]!=DBL_MAX) { + status = edgeSwap(mesh, shared, local, curTet, color, order[0], order[2]); + nConflict+=status==HXT_STATUS_CONFLICT; nAttempt++; + if(status<=HXT_STATUS_INTERNAL) { + status = edgeSwap(mesh, shared, local, curTet, color, order[1], order[2]); + nConflict+=status==HXT_STATUS_CONFLICT; nAttempt++; + } + if(status<=HXT_STATUS_INTERNAL && qual[order[3]]!=DBL_MAX) { + status = edgeSwap(mesh, shared, local, curTet, color, order[0], order[3]); + nConflict+=status==HXT_STATUS_CONFLICT; nAttempt++; + if(status<=HXT_STATUS_INTERNAL) { + status = edgeSwap(mesh, shared, local, curTet, color, order[1], order[3]); + nConflict+=status==HXT_STATUS_CONFLICT; nAttempt++; + } + if(status<=HXT_STATUS_INTERNAL) { + status = edgeSwap(mesh, shared, local, curTet, color, order[2], order[3]); + nConflict+=status==HXT_STATUS_CONFLICT; nAttempt++; + } + } + } + } + + // if(status<=HXT_STATUS_INTERNAL && qual[order[0]]!=DBL_MAX) { + // status = flip2_3(mesh, shared, local, curTet, color, order[0]); + // if(status<=HXT_STATUS_INTERNAL && qual[order[1]]!=DBL_MAX) { + // status = flip2_3(mesh, shared, local, curTet, color, order[1]); + // if(status<=HXT_STATUS_INTERNAL && qual[order[2]]!=DBL_MAX) { + // status = flip2_3(mesh, shared, local, curTet, color, order[2]); + // if(status<=HXT_STATUS_INTERNAL && qual[order[3]]!=DBL_MAX) { + // status = flip2_3(mesh, shared, local, curTet, color, order[3]); + // } + // } + // } + // } + + if(status<=HXT_STATUS_INTERNAL) // the cavity cannot be built + { + // HXT_WARNING("bad tetrahedron %lu cannot be improved", curTet); + // status=HXT_STATUS_OK; + continue; + } + else if(status!=HXT_STATUS_OK) { + #pragma omp atomic write + globalStatus = status; + break; + } + + nSwaps++; + } + + #pragma omp atomic update + threadFinished++; + + int val = 0; + do{ + // threads are waiting here for a reallocation + HXTStatus status = synchronizeReallocation(mesh, shared, &threadFinished, &val); + + if(status!=HXT_STATUS_OK) { + #pragma omp atomic write + globalStatus = status; + } + }while(val<nthreads); + + } + + if(globalStatus!=HXT_STATUS_OK){ + HXT_TRACE(globalStatus); + return globalStatus; + } + + HXT_INFO("%lu mesh optimizations done (%.2f%%)", nSwaps, nSwaps*100.0/shared->badTets.num); + + } while(nConflict!=0 || nSwaps!=0); + + for (int threadID=0; threadID<maxThreads; threadID++) { + for (int i=0; i<locals[threadID].deleted.num; i++) { + uint64_t delTet = locals[threadID].deleted.tetID[i]; + markTetAsDeleted(mesh, delTet); + for (int j=0; j<4; j++) + mesh->tetrahedra.neigh[4*delTet+j] = HXT_NO_ADJACENT; + } + } + + HXT_CHECK( hxtRemoveDeleted(mesh) ); + + double mean = 0; + // #pragma omp parallel for reduction(+:mean) + for (uint64_t i=0; i<mesh->tetrahedra.num; i++) { + if(shared->quality2.values[i]!=DBL_MAX && mesh->tetrahedra.colors[i]!=UINT16_MAX) + mean += sqrt(shared->quality2.values[i]*24.0)/mesh->tetrahedra.num; + } + + double min = DBL_MAX; + // #pragma omp parallel for reduction(min:min) + for (uint64_t i=0; i<mesh->tetrahedra.num; i++) { + if(shared->quality2.values[i]!=DBL_MAX && mesh->tetrahedra.colors[i]!=UINT16_MAX) + min = fmin(sqrt(shared->quality2.values[i]*24.0), min); + } + + printf("mean quality: %f | min quality: %f\n", mean, min); + + HXT_CHECK( threadLocals_destroy(&locals, maxThreads) ); + HXT_CHECK( threadShared_destroy(&shared) ); + + return HXT_STATUS_OK; +} + diff --git a/contrib/hxt/hxt_tetOpti.h b/contrib/hxt/hxt_tetOpti.h new file mode 100644 index 0000000000000000000000000000000000000000..0afd8121a090de670173669eb38c0bcc866e97f7 --- /dev/null +++ b/contrib/hxt/hxt_tetOpti.h @@ -0,0 +1,6 @@ +#ifndef HXT_MESH_H_ +#define HXT_MESH_H_ +#include "hxt_api.h" +#include "hxt_mesh.h" +HXTStatus hxtOptimizeTetrahedra(HXTMesh *mesh, HXTBbox* bbox, double minSize, double qualityThreshold, uint32_t numVerticesConstrained); +#endif diff --git a/contrib/hxt/hxt_tetPostpro.c b/contrib/hxt/hxt_tetPostpro.c new file mode 100644 index 0000000000000000000000000000000000000000..ad2537193c66f77cf309ed8a5622b5cd6698acb6 --- /dev/null +++ b/contrib/hxt/hxt_tetPostpro.c @@ -0,0 +1,183 @@ +#include "predicates.h" +#include "hxt_tetrahedra.h" +#include "hxt_tetPostpro.h" +#include "hxt_tetFlag.h" +#include "hxt_tetUtils.h" +#include "hxt_vertices.h" + + +static void verticesPlaneOrient(HXTMesh* mesh, double* __restrict__ p0, double* __restrict__ p1, double* __restrict__ p2){ + #pragma omp parallel for + for (uint32_t i=0; i<mesh->vertices.num; i++) { + mesh->vertices.coord[4*i+3] = orient3d(p0, p1, p2, mesh->vertices.coord + 4*i); + } +} + +/** keep only tetrahedra whose intersection with a plane is not empty. +The plane is defined by 3 points. */ +HXTStatus hxtTetPlaneIntersection(HXTMesh* mesh, double* p0, double* p1, double* p2){ + verticesPlaneOrient(mesh, p0, p1, p2); + + #pragma omp parallel for + for (uint64_t i=0; i<mesh->tetrahedra.num; i++) { + if(mesh->tetrahedra.node[4*i+3] == HXT_GHOST_VERTEX) { + markTetAsDeleted(mesh, i); + } + else { + double firstOrient = mesh->vertices.coord[4*mesh->tetrahedra.node[4*i]+3]; + for (int j=1; j<4; j++) { + double secondOrient = mesh->vertices.coord[4*mesh->tetrahedra.node[4*i+j]+3]; + if(firstOrient*secondOrient<=0.0){ + firstOrient = 0.0; + break; + } + } + + if(firstOrient!=0.0) + markTetAsDeleted(mesh, i); + } + } + + HXT_CHECK( hxtRemoveDeleted(mesh) ); + + return HXT_STATUS_OK; +} + +/** keep only tetrahedra with at least two vertices on the positive side of the plane {p0,p1,p2}. +(p is on the positive side <=> orient3d(p0,p1,p2, p) > 0) +When seen from the negative side, p0,p1,p2 is counter-clockwise */ +HXTStatus hxtTetPlaneOrient(HXTMesh* mesh, double* p0, double* p1, double* p2){ + verticesPlaneOrient(mesh, p0, p1, p2); + + #pragma omp parallel for + for (uint64_t i=0; i<mesh->tetrahedra.num; i++) { + if(mesh->tetrahedra.node[4*i+3] == HXT_GHOST_VERTEX) { + markTetAsDeleted(mesh, i); + } + else { + for (int j=0; j<4; j++) { + if(mesh->vertices.coord[4*mesh->tetrahedra.node[4*i+j]+3]<0.0){ + markTetAsDeleted(mesh, i); + break; + } + } + } + } + + HXT_CHECK( hxtRemoveDeleted(mesh) ); + + return HXT_STATUS_OK; +} + + +/** keep only vertices that are either in the surface mesh or in the tetrahedra mesh */ +HXTStatus hxtFilterVertices(HXTMesh* mesh, double* nodalSizes){ + HXTVertex* vertices = (HXTVertex*) mesh->vertices.coord; + + #pragma omp parallel for + for (uint32_t i=0; i<mesh->vertices.num; i++) { + vertices[i].padding.status = HXT_STATUS_FALSE; + } + + if(mesh->tetrahedra.node!=NULL){ + #pragma omp parallel for + for (uint32_t i=0; i<mesh->tetrahedra.num; i++) { + vertices[mesh->tetrahedra.node[4*i+0]].padding.status = HXT_STATUS_TRUE; + vertices[mesh->tetrahedra.node[4*i+1]].padding.status = HXT_STATUS_TRUE; + vertices[mesh->tetrahedra.node[4*i+2]].padding.status = HXT_STATUS_TRUE; + vertices[mesh->tetrahedra.node[4*i+3]].padding.status = HXT_STATUS_TRUE; + } + } + + if(mesh->triangles.node!=NULL){ + #pragma omp parallel for + for (uint32_t i=0; i<mesh->triangles.num; i++) { + vertices[mesh->triangles.node[3*i+0]].padding.status = HXT_STATUS_TRUE; + vertices[mesh->triangles.node[3*i+1]].padding.status = HXT_STATUS_TRUE; + vertices[mesh->triangles.node[3*i+2]].padding.status = HXT_STATUS_TRUE; + } + } + + if(mesh->lines.node!=NULL){ + #pragma omp parallel for + for (uint32_t i=0; i<mesh->lines.num; i++) { + vertices[mesh->lines.node[2*i+0]].padding.status = HXT_STATUS_TRUE; + vertices[mesh->lines.node[2*i+1]].padding.status = HXT_STATUS_TRUE; + } + } + + /* remove deleted vertices and change tetrahedra.node triangles.node and lines.nodes accordingly */ + uint32_t* numInserted; + HXT_CHECK( hxtAlignedMalloc(&numInserted, omp_get_max_threads()*sizeof(uint32_t)) ); + const uint32_t n = mesh->vertices.num; + + // when a vertex was skipped, nodeInfo[i].status = HXT_STATUS_FALSE + #pragma omp parallel + { + uint32_t start = 0; + int threadID = omp_get_thread_num(); + numInserted[threadID] = 0; + + #pragma omp for schedule(static) + for (uint32_t i=0; i<n; i++) { + if(vertices[i].padding.status==HXT_STATUS_TRUE) + numInserted[threadID]++; + }// implicit barrier here + + for (int i=0; i<threadID; i++) { + start+=numInserted[i]; + } + + // 3rd: compute where each vertices will be + #pragma omp for schedule(static) + for (uint32_t i=0; i<n; i++) { + uint32_t oldStart = start; + + if(vertices[i].padding.status==HXT_STATUS_TRUE) + start++; + + // index and status are at the same location (it's a union) we cannot put this above the "if" ! + vertices[i].padding.index = oldStart; + } + + // 4th: update tetrahedra.node accordingly + if(mesh->tetrahedra.node!=NULL){ + #pragma omp for + for (uint64_t i=0; i<4*mesh->tetrahedra.num; i++) { + uint32_t index = mesh->tetrahedra.node[i]; + if(index!=HXT_GHOST_VERTEX) + mesh->tetrahedra.node[i] = vertices[index].padding.index; + } + } + + if(mesh->triangles.node!=NULL){ + #pragma omp for + for (uint64_t i=0; i<3*mesh->triangles.num; i++) { + uint32_t index = mesh->triangles.node[i]; + mesh->triangles.node[i] = vertices[index].padding.index; + } + } + + if(mesh->lines.node!=NULL){ + #pragma omp for + for (uint64_t i=0; i<2*mesh->lines.num; i++) { + uint32_t index = mesh->lines.node[i]; + mesh->lines.node[i] = vertices[index].padding.index; + } + } + } + + HXT_CHECK( hxtAlignedFree(&numInserted) ); + + // 5th: put vertices at the right indices + for (uint32_t i=0; i<mesh->vertices.num; i++) { + if(nodalSizes!=NULL){ + nodalSizes[vertices[i].padding.index] = nodalSizes[i]; + } + vertices[vertices[i].padding.index] = vertices[i]; + } + + mesh->vertices.num = vertices[mesh->vertices.num-1].padding.index + 1; + + return HXT_STATUS_OK; +} diff --git a/contrib/hxt/hxt_tetPostpro.h b/contrib/hxt/hxt_tetPostpro.h new file mode 100644 index 0000000000000000000000000000000000000000..62ebd5cc35389993dc19c03f7ee169a6b2d4762a --- /dev/null +++ b/contrib/hxt/hxt_tetPostpro.h @@ -0,0 +1,32 @@ +#ifndef _HXT_TETPOSTPRO_ +#define _HXT_TETPOSTPRO_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "hxt_mesh.h" + +/** +* \file hxt_tetPospro.h postprocessing to keep only some part of a tetrahedra mesh +* \author Célestin Marot +*/ + +/** keep only tetrahedra whose intersection with a plane is not empty. +The plane is defined by 3 points. */ +HXTStatus hxtTetPlaneIntersection(HXTMesh* mesh, double* p0, double* p1, double* p2); + +/** keep only tetrahedra with at least two vertices on the positive side of the plane {p0,p1,p2}. +(p is on the positive side <=> orient3d(p0,p1,p2, p) > 0) +When seen from the negative side, p0,p1,p2 is counter-clockwise */ +HXTStatus hxtTetPlaneOrient(HXTMesh* mesh, double* p0, double* p1, double* p2); + +/** keep only vertices that are either in the surface mesh or in the tetrahedra mesh */ +HXTStatus hxtFilterVertices(HXTMesh* mesh, double* nodalSizes); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/contrib/hxt/hxt_tetRepair.c b/contrib/hxt/hxt_tetRepair.c new file mode 100644 index 0000000000000000000000000000000000000000..4ba131f23e3404e3aa60d9347b4b3d2a7daa8189 --- /dev/null +++ b/contrib/hxt/hxt_tetRepair.c @@ -0,0 +1,399 @@ +#include "hxt_mesh.h" +#include "hxt_vertices.h" +#include "predicates.h" +#include "hxt_sort.h" +#include "hxt_tetRepair.h" +#include "hxt_tetFlag.h" + +/** +* \file hxt_tetRepair.c see header hxt_tetRepair.h +* \author Célestin Marot +*/ + +#define SWAP(x,y) do{int tmp=x; x=y; y=tmp;}while(0) + +/********************************************** + (re-)compute adjacency of the tetrahedral mesh + **********************************************/ +HXTStatus hxtTetAdjacencies(HXTMesh* mesh){ + const uint64_t nTet = mesh->tetrahedra.num; + const uint64_t n = mesh->vertices.num+1; + + // make sure it was allocated + HXT_CHECK( hxtAlignedFree(&mesh->tetrahedra.neigh) ); + HXT_CHECK( hxtAlignedMalloc(&mesh->tetrahedra.neigh, mesh->tetrahedra.size*4*sizeof(uint64_t)) ); + + #pragma omp parallel for + for (uint64_t i=0; i<mesh->tetrahedra.num*4; i++) { + mesh->tetrahedra.neigh[i] = HXT_NO_ADJACENT; + } + + // first step, create all triangles... + HXTGroup3* triplet; + HXTGroup2* pair; // used when n^3 can fit in 64 bits + + if(n > 2642245)// n^3 cannot be on 64 bits + HXT_CHECK( hxtAlignedMalloc(&triplet, nTet*4*sizeof(HXTGroup3)) ); + else + HXT_CHECK( hxtAlignedMalloc(&pair, nTet*4*sizeof(HXTGroup2)) ); + + int hxtDeclareAligned indices[4][4] = {{1,2,3,0},{0,2,3,0},{0,1,3,0},{0,1,2,0}}; // 4th is just a padding + + // fill the triangle structure + #pragma omp parallel for + for (uint64_t i=0; i<nTet; i++) { + uint32_t* node = mesh->tetrahedra.node + 4*i; + int hxtDeclareAligned order[4]; + + // sort a and b + int cmp1 = (node[0] <= node[1]); + int cmp2 = (node[2] <= node[3]); + order[0] = !cmp1; + order[1] = cmp1; + order[2] = 2 + !cmp2; + order[3] = 2 + cmp2; + + // after this, order[0] is at the right place + if(node[order[2]] < node[order[0]]){ + SWAP(order[0], order[2]); + } + + // after this, order[3] is at the right place + if(node[order[3]] < node[order[1]]){ + SWAP(order[3], order[1]); + } + + // remains order[1] and order[2] + if(node[order[2]] < node[order[1]]){ + SWAP(order[1], order[2]); + } + + // on some architecture, this is a simple SIMD operation + uint32_t hxtDeclareAligned nodeOrdered[4]; + for (int j=0; j<4; j++) { + nodeOrdered[j] = node[order[j]]; + } + + if(nodeOrdered[3]==HXT_GHOST_VERTEX) + nodeOrdered[3] = mesh->vertices.num; + + if(n > 2642245){ + for (int j=0; j<4; j++) { + triplet[i*4+j].v[0] = nodeOrdered[indices[j][0]]*n + + nodeOrdered[indices[j][1]]; + triplet[i*4+j].v[1] = nodeOrdered[indices[j][2]]; + triplet[i*4+j].v[2] = i*4 + order[j]; + } + } + else{ + for (int j=0; j<4; j++) { + pair[i*4+j].v[0] = nodeOrdered[indices[j][0]]*n*n + + nodeOrdered[indices[j][1]]*n + + nodeOrdered[indices[j][2]]; + pair[i*4+j].v[1] = i*4 + order[j]; + } + } + + + } + + // sort the triangles... + if(n > 2642245) + { + HXT_CHECK( group3_sort_v1(triplet, nTet*4, n-1) ); + HXT_CHECK( group3_sort_v0(triplet, nTet*4, n*n-1) ); + + // now that triangles are sorted, when two are the same, they are neighbors + #pragma omp parallel for + for (uint64_t i=0; i<nTet*4-1; i++) { + if(triplet[i].v[0]==triplet[i+1].v[0] && + triplet[i].v[1]==triplet[i+1].v[1]) + { + mesh->tetrahedra.neigh[triplet[i].v[2]] = triplet[i+1].v[2]; + mesh->tetrahedra.neigh[triplet[i+1].v[2]] = triplet[i].v[2]; + // i++; // can be done but break SIMD + } + } + + HXT_CHECK( hxtAlignedFree(&triplet) ); + } + else{ + HXT_CHECK( group2_sort_v0(pair, nTet*4, n*n*n-1) ); + + #pragma omp parallel for + for (uint64_t i=0; i<nTet*4-1; i++) { + if(pair[i].v[0]==pair[i+1].v[0]) + { + mesh->tetrahedra.neigh[pair[i].v[1]] = pair[i+1].v[1]; + mesh->tetrahedra.neigh[pair[i+1].v[1]] = pair[i].v[1]; + // i++; // can be done but break SIMD + } + } + + HXT_CHECK( hxtAlignedFree(&pair) ); + } + + return HXT_STATUS_OK; +} + + + +HXTStatus hxtTetReorder(HXTMesh* mesh) +{ + const uint64_t nTet = mesh->tetrahedra.num; + const uint64_t n = mesh->vertices.num; + uint64_t max = n*n*n-1; + + HXTGroup3* triplet; + HXTGroup2* pair; // used when n^3 can fit in 64 bits + + if(n > 2642245){// n^3 cannot be on 64 bits + HXT_CHECK( hxtAlignedMalloc(&triplet, nTet*4*sizeof(HXTGroup3)) ); + max = n*n-1; + } + + HXT_CHECK( hxtAlignedMalloc(&pair, nTet*4*sizeof(HXTGroup2)) ); + + if(n > 2642245){ + #pragma omp parallel for + for (uint64_t i=0; i<nTet; i++) { + triplet[i].v[0] = mesh->tetrahedra.node[4*i]*n + + mesh->tetrahedra.node[4*i+1]; + triplet[i].v[1] = mesh->tetrahedra.node[4*i+2]; + triplet[i].v[2] = i; + } + } + else{ + #pragma omp parallel for + for (uint64_t i=0; i<nTet; i++) { + pair[i].v[0] = mesh->tetrahedra.node[4*i]*n*n + + mesh->tetrahedra.node[4*i+1]*n + + mesh->tetrahedra.node[4*i+2]; + pair[i].v[1] = i; + } + } + + if(n > 2642245) + { + HXT_CHECK( group3_sort_v1(triplet, nTet, n-1) ); + + #pragma omp parallel for + for (uint64_t i=0; i<nTet; i++) { + pair[i].v[0] = triplet[i].v[0]; + pair[i].v[1] = triplet[i].v[2]; + } + + HXT_CHECK( hxtAlignedFree(&triplet) ); + } + + + HXT_CHECK( group2_sort_v0(pair, nTet, max) ); + + // we do not care about key anymore, put inverse index + #pragma omp parallel for + for (uint64_t i=0; i<nTet; i++) { + pair[pair[i].v[1]].v[0] = i; + } + + uint64_t* newNeigh; + HXT_CHECK( hxtAlignedMalloc(&newNeigh, mesh->tetrahedra.size*4*sizeof(uint64_t))); + + #pragma omp parallel for + for (uint64_t i=0; i<nTet; i++) { + uint64_t index = pair[i].v[1]; + for (unsigned j=0; j<4; j++){ + uint64_t oldNeigh = mesh->tetrahedra.neigh[4*index+j]; + if(oldNeigh==HXT_NO_ADJACENT) + newNeigh[i*4+j] = HXT_NO_ADJACENT; + else + newNeigh[i*4+j] = pair[oldNeigh/4].v[0]*4 + oldNeigh%4; + } + } + + HXT_CHECK( hxtAlignedFree(&mesh->tetrahedra.neigh) ); + mesh->tetrahedra.neigh = newNeigh; + + uint32_t* newNode; + HXT_CHECK( hxtAlignedMalloc(&newNode, mesh->tetrahedra.size*4*sizeof(uint32_t))); + + #pragma omp parallel for + for (uint64_t i=0; i<nTet; i++) { + uint64_t index = pair[i].v[1]; + uint32_t* oldNode = mesh->tetrahedra.node + 4*index; + #pragma omp simd aligned(newNode:SIMD_ALIGN) aligned(oldNode:16) + for (unsigned j=0; j<4; j++) + newNode[i*4+j] = oldNode[j]; + } + + HXT_CHECK( hxtAlignedFree(&mesh->tetrahedra.node) ); + mesh->tetrahedra.node = newNode; + + uint16_t* newColor; + HXT_CHECK( hxtAlignedMalloc(&newColor, mesh->tetrahedra.size*sizeof(uint16_t))); + + #pragma omp parallel for + for (uint64_t i=0; i<nTet; i++) { + uint64_t index = pair[i].v[1]; + newColor[i] = mesh->tetrahedra.colors[index]; + } + + HXT_CHECK( hxtAlignedFree(&mesh->tetrahedra.colors) ); + mesh->tetrahedra.colors = newColor; + + uint16_t* newFlag; + HXT_CHECK( hxtAlignedMalloc(&newFlag, mesh->tetrahedra.size*sizeof(uint16_t))); + + #pragma omp parallel for + for (uint64_t i=0; i<nTet; i++) { + uint64_t index = pair[i].v[1]; + newFlag[i] = mesh->tetrahedra.flag[index]; + } + + HXT_CHECK( hxtAlignedFree(&mesh->tetrahedra.flag) ); + mesh->tetrahedra.flag = newFlag; + + HXT_CHECK( hxtAlignedFree(&pair) ); + + return HXT_STATUS_OK; +} + + +// assume there is no ghost tetrahedron, this is to repair an imported mesh +HXTStatus hxtTetOrientNodes(HXTMesh* mesh) +{ + const uint64_t nTet = mesh->tetrahedra.num; + const double* coord = mesh->vertices.coord; + + #pragma omp parallel for + for (uint64_t i=0; i<nTet; i++) { + uint32_t* node = mesh->tetrahedra.node + 4*i; + + if(orient3d(coord+4*node[0], coord+4*node[1], coord+4*node[2], coord+4*node[3])<0){ + uint32_t tmp = node[0]; + node[0] = node[1]; + node[1] = tmp; + } + } + + return HXT_STATUS_OK; +} + + + +HXTStatus hxtTetVerify(HXTMesh* mesh) +{ + volatile int errorOccured = 0; + HXTVertex* vertices = (HXTVertex*) mesh->vertices.coord; + + #pragma omp parallel for + for (uint64_t i=0; i<mesh->tetrahedra.num; i++) + { + + // if(errorOccured) + // continue; + + uint32_t* Node = mesh->tetrahedra.node + i*4; + uint64_t* Neigh = mesh->tetrahedra.neigh + i*4; + + if(isTetDeleted(mesh, i)){ + // HXT_WARNING("deleted tetrahedra remain in the mesh"); + continue; + } + + // check for good placement of ghost vertex + if(Node[0]==HXT_GHOST_VERTEX || Node[1]==HXT_GHOST_VERTEX || Node[2]==HXT_GHOST_VERTEX){ + HXT_ERROR_MSG(HXT_STATUS_ERROR, "ghost vertex at wrong place in tet. %lu", i); + errorOccured=1; + continue; + } + + double* a = vertices[Node[0]].coord; + double* b = vertices[Node[1]].coord; + double* c = vertices[Node[2]].coord; + + // check for the orientation + // if(Node[3]==0){ + // HXT_WARNING("ghost tet. %lu remains in the array (did you clean the mesh?)",i*4); + // } + // else + if(Node[3]!=HXT_GHOST_VERTEX && orient3d(a,b,c,vertices[Node[3]].coord)<=0.0){ + HXT_ERROR_MSG(HXT_STATUS_ERROR, "orientation of tet %lu is wrong",i); + errorOccured=1; + continue; + } + + // check the neighbors + for (unsigned j=0; j<4; j++) + { + uint64_t neigh = Neigh[j]; + + if(neigh==HXT_NO_ADJACENT){ + continue; + } + + if(neigh>=mesh->tetrahedra.num*4) { + HXT_ERROR_MSG(HXT_STATUS_ERROR, "%uth neighbor of tet %lu does not exist", j, i); + errorOccured=1; + continue; + } + + // uint64_t* NeighNeigh = mesh->tetrahedra.neigh + neigh; + uint32_t* NeighNode = mesh->tetrahedra.node + neigh/4*4; + + if(mesh->tetrahedra.neigh[neigh]!=i*4+j){ + HXT_ERROR_MSG(HXT_STATUS_ERROR, "tet %lu (%lu/4) is not the neighbor of its %uth neighbor %lu (%lu/4)", i, i*4,j, neigh/4, neigh); + errorOccured=1; + continue; + } + + uint32_t V[3] = { Node[((j+1)&3)], Node[((j&2)^3)], Node[((j+3)&2)]}; + unsigned l; + + for (l=0; l<3; l++) + { + if(NeighNode[(((neigh&3)+1)&3)]==V[l] && NeighNode[(((neigh&3)&2)^3)]==V[(l+1)%3] && NeighNode[(((neigh&3)+3)&2)]==V[(l+2)%3]) + break; + } + + if(l!=3){ + HXT_ERROR_MSG(HXT_STATUS_ERROR, "neighbor %u of tet. %lu is intersecting it (common face has the same orientation)",j,i); + errorOccured=1; + continue; + } + + for (l=0; l<3; l++) + { + if(NeighNode[(((neigh&3)+1)&3)]==V[l] && NeighNode[(((neigh&3)&2)^3)]==V[(l+2)%3] && NeighNode[(((neigh&3)+3)&2)]==V[(l+1)%3]) + break; + } + + if(l==3){ + HXT_ERROR_MSG(HXT_STATUS_ERROR, "neighbor %u of tet. %lu doesn't contain 3 common vertices",j,i); + errorOccured=1; + continue; + } + + + if((isFacetConstrained(mesh, i*4+j)!=0) ^ (isFacetConstrained(mesh, neigh)!=0)) { + HXT_ERROR_MSG(HXT_STATUS_ERROR, "constraint is not consistent on both side of facet 4*%lu+%u",i,j); + errorOccured=1; + continue; + } + + // only for delaunay triangulation... + // if(Node[3]!=HXT_GHOST_VERTEX && NeighNode[neigh&3]!=HXT_GHOST_VERTEX && insphere(vertices[Node[0]].coord, + // vertices[Node[1]].coord, + // vertices[Node[2]].coord, + // vertices[Node[3]].coord, + // vertices[NeighNode[neigh&3]].coord)>0.0){ + // HXT_ERROR_MSG(HXT_STATUS_ERROR, "neighbor %u of tet %lu has it's non-common node in the sphere (insphere(%u %u %u %u %u)>0)",j,i*4, Node[0], Node[1], Node[2], Node[3], NeighNode[neigh&3]); + // errorOccured=1; + // continue; + // } + } + } + + if(errorOccured) + return HXT_STATUS_ERROR; + return HXT_STATUS_OK; +} diff --git a/contrib/hxt/hxt_tetRepair.h b/contrib/hxt/hxt_tetRepair.h new file mode 100644 index 0000000000000000000000000000000000000000..8f87bfbc4f2b02f0652443b3b4b4ac8533069e7e --- /dev/null +++ b/contrib/hxt/hxt_tetRepair.h @@ -0,0 +1,34 @@ +#ifndef _HXT_TETREPAIR_ +#define _HXT_TETREPAIR_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "hxt_mesh.h" + +/** +* \file hxt_tetRepair.h Repair a tetrahedra mesh +* \author Célestin Marot +*/ + +/** (re-)compute the adjacency of a mesh */ +HXTStatus hxtTetAdjacencies(HXTMesh* mesh); + +/** orient tetrahedra correctly + \warning The mesh cannot contain any ghost vertex */ +HXTStatus hxtTetOrientNodes(HXTMesh* mesh); + +/** verify the consistency of a tetrahedral mesh */ +HXTStatus hxtTetVerify(HXTMesh* mesh); + +/** reorder tetrahedra in a reproducible manner */ +HXTStatus hxtTetReorder(HXTMesh* mesh); + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/contrib/hxt/hxt_tetUtils.c b/contrib/hxt/hxt_tetUtils.c new file mode 100644 index 0000000000000000000000000000000000000000..ef618ab7892761f06b17cacea0bb5599514bc4e9 --- /dev/null +++ b/contrib/hxt/hxt_tetUtils.c @@ -0,0 +1,194 @@ +#include "hxt_tetUtils.h" +#include "hxt_tetFlag.h" +#include "hxt_sort.h" + +#define MAX(x,y) ((x)>(y) ? (x) : (y)) + +/*********************************** + * remove deleted tetrahedra of mesh + ***********************************/ +// TODO: a parallel technique +HXTStatus hxtRemoveDeleted(HXTMesh* mesh) +{ + #pragma omp parallel for + for (uint64_t i=0; i<mesh->tetrahedra.num; i++) { + if(isTetDeleted(mesh, i)){ + for (unsigned j=0; j<4; j++) { + uint64_t neigh = mesh->tetrahedra.neigh[4*i+j]; // neighbor of the deleted tet + if(neigh!=HXT_NO_ADJACENT) // the deleted tet had a neighbor pointing to him... + mesh->tetrahedra.neigh[neigh] = HXT_NO_ADJACENT; + } + } + } + + uint64_t right = mesh->tetrahedra.num-1; + uint64_t left = 0; + + while(1) { + while(left < right && isTetDeleted(mesh, right)) right--; + while(left < right && isTetDeleted(mesh, left)==0) left++; + + if(left >= right) + break; + + mesh->tetrahedra.colors[left] = mesh->tetrahedra.colors[right]; + mesh->tetrahedra.flag[left] = mesh->tetrahedra.flag[right]; + + // swap the two tetrahedra + for (unsigned j=0; j<4; j++) { + uint64_t neighR = mesh->tetrahedra.neigh[4*right+j]; // neighbor of the tet that will be moved (not deleted) + if(neighR!=HXT_NO_ADJACENT) + mesh->tetrahedra.neigh[neighR] = 4*left+j; + + mesh->tetrahedra.node[4*left+j] = mesh->tetrahedra.node[4*right+j]; + mesh->tetrahedra.neigh[4*left+j] = neighR; + } + + left++; right--; + } + + if(left==right && isTetDeleted(mesh, left)==0) left++; + + mesh->tetrahedra.num = left; + + return HXT_STATUS_OK; +} + + +/*************************************** + * remove ghost tetrahedra from the mesh + * see header for more information + ***************************************/ +HXTStatus hxtRemoveGhosts(HXTMesh* mesh){ + #pragma omp parallel for + for (uint64_t i=0; i<mesh->tetrahedra.num; i++) { + if(mesh->tetrahedra.node[4*i+3]==HXT_GHOST_VERTEX){ + markTetAsDeleted(mesh, i); + } + } + + HXT_CHECK( hxtRemoveDeleted(mesh) ); + + return HXT_STATUS_OK; +} + + + +/********************************** + * add ghost tetrahedra to the mesh + * see header for more information + **********************************/ +HXTStatus hxtAddGhosts(HXTMesh* mesh){ + int maxThreads = omp_get_max_threads(); + + uint64_t* hullCount; + HXTGroup2* edges; + uint64_t totalHullCount = 0; + HXT_CHECK( hxtMalloc(&hullCount, maxThreads*sizeof(uint64_t)) ); + + HXTStatus status = HXT_STATUS_OK; + + #pragma omp parallel + { + int threadID = omp_get_thread_num(); + hullCount[threadID] = 0; + + // count the number of convex hull faces + #pragma omp for schedule(static) + for (uint64_t i=0; i<mesh->tetrahedra.num; i++) { + for (unsigned j=0; j<4; j++) { + if(mesh->tetrahedra.neigh[4*i+j]==HXT_NO_ADJACENT) + hullCount[threadID]++; + } + } + + // exclusive scan + allocation + #pragma omp barrier + #pragma omp single + { + int nthreads = omp_get_num_threads(); + + for (int i=0; i<nthreads; i++) { + uint64_t tsum = hullCount[i] + totalHullCount; + hullCount[i] = totalHullCount; + totalHullCount = tsum; + } + + status = hxtTetrahedraReserve(mesh, totalHullCount+mesh->tetrahedra.num); + if(status!=HXT_STATUS_OK) + HXT_TRACE(status); + else { + status = hxtAlignedMalloc(&edges, 3*totalHullCount*sizeof(HXTGroup2)); + if(status!=HXT_STATUS_OK) + HXT_TRACE(status); + } + } + + + if(status== HXT_STATUS_OK){ + // create the Ghost tet. + #pragma omp for schedule(static) + for (uint64_t i=0; i<mesh->tetrahedra.num; i++) { + for (unsigned j=0; j<4; j++) { + if(mesh->tetrahedra.neigh[4*i+j]==HXT_NO_ADJACENT){ + uint64_t newGhost = hullCount[threadID] + mesh->tetrahedra.num; + + mesh->tetrahedra.neigh[4*i+j] = 4*newGhost+3; + mesh->tetrahedra.neigh[4*newGhost+3] = 4*i+j; + + mesh->tetrahedra.colors[newGhost] = UINT16_MAX; + mesh->tetrahedra.flag[newGhost] = 0; + if(isFacetConstrained(mesh, 4*i+j)) + constrainFacet(mesh, 4*newGhost+3); + + uint32_t v0, v1, v2; + + v0 = mesh->tetrahedra.node[4*i+((j+1)&3)]; + v1 = mesh->tetrahedra.node[4*i+((j+3)&2)]; + v2 = mesh->tetrahedra.node[4*i+((j&2)^3)]; + + mesh->tetrahedra.node[4*newGhost+0] = v0; + mesh->tetrahedra.node[4*newGhost+1] = v1; + mesh->tetrahedra.node[4*newGhost+2] = v2; + mesh->tetrahedra.node[4*newGhost+3] = HXT_GHOST_VERTEX; + + uint64_t index = 3*hullCount[threadID]; + + edges[index+0].v[0] = (v0<v1)?(uint64_t) v0*mesh->vertices.num+v1 : (uint64_t) v1*mesh->vertices.num+v0; + edges[index+0].v[1] = 4*newGhost+2; + edges[index+1].v[0] = (v0<v2)?(uint64_t) v0*mesh->vertices.num+v2 : (uint64_t) v2*mesh->vertices.num+v0; + edges[index+1].v[1] = 4*newGhost+1; + edges[index+2].v[0] = (v1<v2)?(uint64_t) v1*mesh->vertices.num+v2 : (uint64_t) v2*mesh->vertices.num+v1; + edges[index+2].v[1] = 4*newGhost+0; + + hullCount[threadID]++; + } + } + } + } + } + + if(status!=HXT_STATUS_OK){ + return status; + } + + mesh->tetrahedra.num+=totalHullCount; + + // now we have to find the adjacencies between ghosts + const uint64_t max = (uint64_t) mesh->vertices.num*mesh->vertices.num; + const uint64_t n = totalHullCount*3; + HXT_CHECK( group2_sort_v0(edges, n, max) ); + + // connect adjacencies + #pragma omp parallel for + for (uint64_t i=0; i<n; i+=2) { + mesh->tetrahedra.neigh[edges[i].v[1]] = edges[i+1].v[1]; + mesh->tetrahedra.neigh[edges[i+1].v[1]] = edges[i].v[1]; + } + + // first make a list with all edges + HXT_CHECK( hxtAlignedFree(&edges) ); + HXT_CHECK( hxtFree(&hullCount) ); + + return HXT_STATUS_OK; +} \ No newline at end of file diff --git a/contrib/hxt/hxt_tetUtils.h b/contrib/hxt/hxt_tetUtils.h new file mode 100644 index 0000000000000000000000000000000000000000..b8d382e1bb1f151c3e2e1f34fd5c7134248c1c33 --- /dev/null +++ b/contrib/hxt/hxt_tetUtils.h @@ -0,0 +1,61 @@ +#ifndef _HXT_TETUTILS_ +#define _HXT_TETUTILS_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "hxt_mesh.h" + +/** +* \file tetUtils.h utility for the tetrahedral mesh +* \author Célestin Marot +*/ + +/** + * Reserve memory space for tetrahedra assuming ntet tetrahedra will be added to the triangulation + * it is efficient to give ntet = ~8 times the number of vertices to be added + * as in average, the number of tetrahedra is ~6.5x greater than the number of vertices + */ +static inline HXTStatus hxtTetrahedraReserve(HXTMesh* mesh, uint64_t totalTet){ + if(totalTet > mesh->tetrahedra.size){ + HXT_CHECK( hxtAlignedRealloc(&mesh->tetrahedra.flag, totalTet*sizeof(uint16_t)) ); + HXT_CHECK( hxtAlignedRealloc(&mesh->tetrahedra.colors, totalTet*sizeof(uint16_t)) ); + HXT_CHECK( hxtAlignedRealloc(&mesh->tetrahedra.node, totalTet*4*sizeof(uint32_t)) ); + HXT_CHECK( hxtAlignedRealloc(&mesh->tetrahedra.neigh, totalTet*4*sizeof(uint64_t)) ); + mesh->tetrahedra.size = totalTet; + } + return HXT_STATUS_OK; +} + +static inline HXTStatus hxtTetrahedraDoubleSize(HXTMesh* mesh) { + return hxtTetrahedraReserve(mesh, 2*mesh->tetrahedra.num); +} + + +/** + * Remove tetrahedra 't' whose flag indicates they are deleted + * it updates the adjacency of the neighbors of 't' referencing 't' + * to HXT_NO_ADJACENT. + * You must set the adjacency of 't' to HXT_NO_ADJACENT if you don't want this behavior + */ +HXTStatus hxtRemoveDeleted(HXTMesh* mesh); + +/** Removes ghost tetrahedra */ +HXTStatus hxtRemoveGhosts(HXTMesh* mesh); + +/** Adds ghost tetrahedra to adjoin tet. whose neighbors are HXT_NO_ADJACENT.\n + * THIS FUNCTION SUPPOSE 2 THINGS: + * - there are no ghost tetrahedra + * - face with 1 tetrahedra (neigh[face]==HXT_NO_ADJACENT) are all on the convex hull + * + * this function will not work properly in any other cases... + */ +HXTStatus hxtAddGhosts(HXTMesh* mesh); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/contrib/hxt/hxt_tet_aspect_ratio.c b/contrib/hxt/hxt_tet_aspect_ratio.c new file mode 100644 index 0000000000000000000000000000000000000000..fc1a9011d3ba960231f901d818666a07092315af --- /dev/null +++ b/contrib/hxt/hxt_tet_aspect_ratio.c @@ -0,0 +1,192 @@ +#include <math.h> +#include "predicates.h" + +void hxt_cross_prod(double a[3], double b[3], double c[3]) +{ + c[2] = a[0] * b[1] - a[1] * b[0]; + c[1] = -a[0] * b[2] + a[2] * b[0]; + c[0] = a[1] * b[2] - a[2] * b[1]; +} + +double hxt_distance2(double p0[3], double p1[3]){ + double a = p0[0]-p1[0]; + double b = p0[1]-p1[1]; + double c = p0[2]-p1[2]; + return a*a+b*b+c*c; +} + +double hxt_triangle_area(double p0[3], double p1[3], double p2[3]) +{ + double a[3], b[3], c[3]; + + a[0] = p2[0] - p1[0]; + a[1] = p2[1] - p1[1]; + a[2] = p2[2] - p1[2]; + + b[0] = p0[0] - p1[0]; + b[1] = p0[1] - p1[1]; + b[2] = p0[2] - p1[2]; + + hxt_cross_prod(a, b, c); + return 0.5 * sqrt(c[0] * c[0] + c[1] * c[1] + c[2] * c[2]); +} + +double hxtTetAspectRatio (double p0[3], double p1[3], double p2[3], double p3[3]) { + double volume = orient3d (p0,p1,p2,p3) / 6.0; + + double s1 = fabs(hxt_triangle_area(p0, p1, p2)); + double s2 = fabs(hxt_triangle_area(p0, p2, p3)); + double s3 = fabs(hxt_triangle_area(p0, p1, p3)); + double s4 = fabs(hxt_triangle_area(p1, p2, p3)); + + double rhoin = 3. * volume / (s1 + s2 + s3 + s4); + double l = hxt_distance2 (p0,p1); + double l2 = hxt_distance2 (p0,p2); + double l3 = hxt_distance2 (p0,p3); + double l4 = hxt_distance2 (p1,p2); + double l5 = hxt_distance2 (p1,p3); + double l6 = hxt_distance2 (p2,p3); + + l = l2 > l ? l2:l; + l = l3 > l ? l3:l; + l = l4 > l ? l4:l; + l = l5 > l ? l5:l; + l = l6 > l ? l6:l; + + return sqrt(24./l) * rhoin ; +} + + + +//!\ this function does not return the aspect ratio 'r'. It returns 'r^2/24' +double hxtTetAspectFastRatio (double a[3], double b[3], double c[3], double d[3]) { + double ab[3], ac[3], ad[3], bc[3], cd[3], db[3]; + for (int i=0; i<3; i++) { + ab[i] = b[i] - a[i]; // AB + ac[i] = c[i] - a[i]; // AC + ad[i] = d[i] - a[i]; // AD + } + + double adxac0 = ad[1]*ac[2] - ad[2]*ac[1]; + double abxad0 = ab[1]*ad[2] - ab[2]*ad[1]; + double acxab0 = ac[1]*ab[2] - ac[2]*ab[1]; + double volume6 = ab[0]*adxac0 + ac[0]*abxad0 + ad[0]*acxab0; + + // abort as early as possible + if(volume6<=0.0) + return 0.0; + + double adxac1 = ad[2]*ac[0] - ad[0]*ac[2]; + double adxac2 = ad[0]*ac[1] - ad[1]*ac[0]; + + double abxad1 = ab[2]*ad[0] - ab[0]*ad[2]; + double abxad2 = ab[0]*ad[1] - ab[1]*ad[0]; + + double acxab1 = ac[2]*ab[0] - ac[0]*ab[2]; + double acxab2 = ac[0]*ab[1] - ac[1]*ab[0]; + + for (int i=0; i<3; i++) { + db[i] = b[i] - d[i]; // DB = B-D = AB-AD + bc[i] = c[i] - b[i]; // BC = C-B = AC-AB + cd[i] = d[i] - c[i]; // CD = D-c = AD-AC + } + + double cdxbc0 = cd[1]*bc[2] - cd[2]*bc[1]; // = adxac0+acxab0+abxad0; + double cdxbc1 = cd[2]*bc[0] - cd[0]*bc[2]; // = adxac1+acxab1+abxad1; + double cdxbc2 = cd[0]*bc[1] - cd[1]*bc[0]; // = adxac2+acxab2+abxad2; + + double areaSum = sqrt(adxac0*adxac0 + adxac1*adxac1 + adxac2*adxac2) + + sqrt(abxad0*abxad0 + abxad1*abxad1 + abxad2*abxad2) + + sqrt(acxab0*acxab0 + acxab1*acxab1 + acxab2*acxab2) + + sqrt(cdxbc0*cdxbc0 + cdxbc1*cdxbc1 + cdxbc2*cdxbc2); + + double l = ab[0]*ab[0] + ab[1]*ab[1] + ab[2]*ab[2]; // |AB|² + double l2 = ac[0]*ac[0] + ac[1]*ac[1] + ac[2]*ac[2]; // |AC|² + double l3 = ad[0]*ad[0] + ad[1]*ad[1] + ad[2]*ad[2]; // |AD|² + double l4 = bc[0]*bc[0] + bc[1]*bc[1] + bc[2]*bc[2]; // |BC|² + double l5 = cd[0]*cd[0] + cd[1]*cd[1] + cd[2]*cd[2]; // |CD|² + double l6 = db[0]*db[0] + db[1]*db[1] + db[2]*db[2]; // |DB|² + + if(l2>l) l=l2; + if(l3>l) l=l3; + if(l4>l) l=l4; + if(l5>l) l=l5; + if(l6>l) l=l6; + + return volume6*volume6/(l*areaSum*areaSum); +} + + +// for a pair of tetrahedra: (abcd) and (abec) +// void hxtTetAspectFastRatioPair (double a[3], double b[3], double c[3], double d[3], double e[3], double* quality1, double quality2) { +// double ab[3], ac[3], ad[3], bc[3], cd[3], db[3]; +// for (int i=0; i<3; i++) { +// ab[i] = b[i] - a[i]; // AB +// ac[i] = c[i] - a[i]; // AC +// ad[i] = d[i] - a[i]; // AD +// ae[i] = e[i] - a[i]; // AE +// } + +// double adxac0 = ad[1]*ac[2] - ad[2]*ac[1]; +// double abxad0 = ab[1]*ad[2] - ab[2]*ad[1]; +// double acxab0 = ac[1]*ab[2] - ac[2]*ab[1]; +// double acxae0 = ac[1]*ae[2] - ac[2]*ae[1]; +// double aexab0 = ae[1]*ab[2] - ae[2]*ab[1]; +// double volume1 = ab[0]*adxac0 + ac[0]*abxad0 + ad[0]*acxab0; +// double volume2 = ab[0]*acxae0 - ae[0]*acxab0 + ac[0]*aexab0; + +// // abort as early as possible +// if(volume1<=0.0 || volume2<=0.0){ +// *quality1 = 0.0; +// *quality2 = 0.0; +// return; +// } + +// double adxac1 = ad[2]*ac[0] - ad[0]*ac[2]; +// double adxac2 = ad[0]*ac[1] - ad[1]*ac[0]; + +// double abxad1 = ab[2]*ad[0] - ab[0]*ad[2]; +// double abxad2 = ab[0]*ad[1] - ab[1]*ad[0]; + +// double acxab1 = ac[2]*ab[0] - ac[0]*ab[2]; +// double acxab2 = ac[0]*ab[1] - ac[1]*ab[0]; + +// double acxae1 = ac[2]*ae[0] - ac[0]*ae[2]; +// double acxae2 = ac[0]*ae[1] - ac[1]*ae[0]; + +// double aexab1 = ae[2]*ab[0] - ae[0]*ab[2]; +// double aexab2 = ae[0]*ab[1] - ae[1]*ab[0]; + +// for (int i=0; i<3; i++) { +// db[i] = b[i] - d[i]; // DB = B-D = AB-AD +// bc[i] = c[i] - b[i]; // BC = C-B = AC-AB +// cd[i] = d[i] - c[i]; // CD = D-c = AD-AC + +// } + +// double cdxbc0 = cd[1]*bc[2] - cd[2]*bc[1]; // = adxac0+acxab0+abxad0; +// double cdxbc1 = cd[2]*bc[0] - cd[0]*bc[2]; // = adxac1+acxab1+abxad1; +// double cdxbc2 = cd[0]*bc[1] - cd[1]*bc[0]; // = adxac2+acxab2+abxad2; + +// double commonArea = sqrt(acxab0*acxab0 + acxab1*acxab1 + acxab2*acxab2); + +// double areaSum1 = commonArea +// + sqrt(adxac0*adxac0 + adxac1*adxac1 + adxac2*adxac2) +// + sqrt(abxad0*abxad0 + abxad1*abxad1 + abxad2*abxad2) +// + sqrt(cdxbc0*cdxbc0 + cdxbc1*cdxbc1 + cdxbc2*cdxbc2); + +// double l = ab[0]*ab[0] + ab[1]*ab[1] + ab[2]*ab[2]; // |AB|² +// double l2 = ac[0]*ac[0] + ac[1]*ac[1] + ac[2]*ac[2]; // |AC|² +// double l3 = ad[0]*ad[0] + ad[1]*ad[1] + ad[2]*ad[2]; // |AD|² +// double l4 = bc[0]*bc[0] + bc[1]*bc[1] + bc[2]*bc[2]; // |BC|² +// double l5 = cd[0]*cd[0] + cd[1]*cd[1] + cd[2]*cd[2]; // |CD|² +// double l6 = db[0]*db[0] + db[1]*db[1] + db[2]*db[2]; // |DB|² + +// if(l2>l) l=l2; +// if(l3>l) l=l3; +// if(l4>l) l=l4; +// if(l5>l) l=l5; +// if(l6>l) l=l6; + +// *quality1 = volume1*volume1/(l*areaSum1*areaSum1); +// } \ No newline at end of file diff --git a/contrib/hxt/hxt_tet_aspect_ratio.h b/contrib/hxt/hxt_tet_aspect_ratio.h new file mode 100644 index 0000000000000000000000000000000000000000..0ecc2bd5f74fab9a547954bef5af5ba83a02bd85 --- /dev/null +++ b/contrib/hxt/hxt_tet_aspect_ratio.h @@ -0,0 +1,9 @@ +#ifndef _HXT_TET_ASPECT_RATIO_ +#define _HXT_TET_ASPECT_RATIO_ + +double hxtTetAspectRatio (double p0[3], double p1[3], double p2[3], double p3[3]); + +//!\ this function does not return the aspect ratio 'r'. It returns 'r^2/24' +double hxtTetAspectFastRatio (double p0[3], double p1[3], double p2[3], double p3[3]); + +#endif diff --git a/contrib/hxt/hxt_tetrahedra.c b/contrib/hxt/hxt_tetrahedra.c index 68d1801a3f93e1ad6fe776edf0ed8ce896bf2a4a..d90368f3d88d33dbd98269c59744777da9698614 100644 --- a/contrib/hxt/hxt_tetrahedra.c +++ b/contrib/hxt/hxt_tetrahedra.c @@ -1,16 +1,19 @@ #include "hxt_tetrahedra.h" #include "predicates.h" +#include "hxt_tetRepair.h" +#include "hxt_tetUtils.h" +#include "hxt_tetFlag.h" #include "hxt_sort.h" +/** +* \file hxt_tetrahedra.c see header hxt_tetrahedra.h. +* \author Célestin Marot +*/ /* compile-time parameters */ #define SMALLEST_ROUND 2048 -#define ROUND_RATIO 7 -#define THREAD_RATIO 8 #define DELETED_BUFFER_SIZE 8182 -// #define HXT_DELAUNAY_MAX_MEMORY /* use per thread bufffer instead of a global one (it's faster if it fits into RAM) */ // #define HXT_DELAUNAY_LOW_MEMORY /* doesn't use any buffer (a lot slower, except if you are at the limit of filling the RAM) */ -// (if both are defined, MAX_MEMORY takes over MIN_MEMORY) /* usefull macros */ #define ABS(x) ((x) >= 0 ? (x) : -(x)) @@ -26,102 +29,54 @@ } \ }while(0) -/* usefull mask */ -static const uint64_t NEIGH_H = UINT64_C(0xFFFFFFFFFFFFFFFC); -typedef struct TetLocalStruct { - uint64_t hxtDeclareAligned Map[1024]; - - struct{ - uint32_t hxtDeclareAligned node[4]; - uint64_t otherside; // the tet on the other side of the boundar - } *bnd; - uint64_t numBnd; - uint64_t sizeBnd; +typedef struct{ + uint32_t hxtDeclareAligned node[3]; + uint16_t flag; + uint64_t neigh; // the tet on the other side of the boundar +} cavityBnd_t; - uint64_t* deleted; - uint64_t numDeleted; - uint64_t sizeDeleted; - - uint32_t* vertices; - uint32_t numVertices; - uint32_t sizeVertices; +typedef struct { +#ifndef HXT_DELAUNAY_LOW_MEMORY + uint64_t hxtDeclareAligned Map[1024]; +#endif - uint64_t startDist; - uint64_t endDist; - uint32_t localStart; + struct { + cavityBnd_t* bnd; + uint64_t num; + uint64_t size; + } ball; + + struct { + uint64_t* tetID; + uint64_t num; + uint64_t size; + } deleted; + + struct { + uint64_t startDist; + uint64_t endDist; + uint32_t first; + } partition; } TetLocal; -static inline HXTStatus walking2Cavity(HXTMesh* mesh, TetLocal* local, uint64_t* curTet, const uint32_t vta); -static inline HXTStatus diggingACavity(HXTMesh* mesh, TetLocal* local, uint64_t curTet, const uint32_t vta, const uint16_t color); -static inline HXTStatus fillingACavity(HXTMesh* mesh, TetLocal* local, uint32_t* verticesID, uint64_t* curTet, const uint16_t color); -static inline HXTStatus filterCavity (TetLocal* local, HXTMesh *mesh, const double *nodalSizes); -// static HXTStatus hxtTetrahedraVerifyInternal(HXTMesh* mesh, TetLocal* Locals, int nthreads); - - - -HXTStatus hxtTetrahedraReserve(HXTMesh* mesh, uint64_t ntet){ - ntet+=mesh->tetrahedra.num; - if(ntet > mesh->tetrahedra.size){ - HXT_CHECK( hxtAlignedRealloc(&mesh->tetrahedra.colors, ntet*sizeof(uint16_t)) ); - HXT_CHECK( hxtAlignedRealloc(&mesh->tetrahedra.node, ntet*4*sizeof(uint32_t)) ); - HXT_CHECK( hxtAlignedRealloc(&mesh->tetrahedra.neigh, ntet*4*sizeof(uint64_t)) ); - mesh->tetrahedra.size = ntet; - } - return HXT_STATUS_OK; -} - - /*********************************** - * remove deleted tetrahedra of mesh + * create the initial tetrahedron + * surrounded by 4 ghost tetrahedra ***********************************/ -//TODO: maybe do this in parallel (like in a sort pass) -HXTStatus hxtRemoveDeleted(HXTMesh* mesh){ - - uint64_t right = mesh->tetrahedra.num-1; - uint64_t left = 0; - - while(1) { - while(left < right && mesh->tetrahedra.colors[right]==HXT_DELETED_COLOR) right--; - while(left < right && mesh->tetrahedra.colors[left]!=HXT_DELETED_COLOR) left++; - - if(left >= right) - break; - - mesh->tetrahedra.colors[left] = mesh->tetrahedra.colors[right]; - - // swap the two tetrahedra - for (unsigned j=0; j<4; j++) { - - uint64_t neigh = mesh->tetrahedra.neigh[4*right+j]; - if(neigh!=HXT_NO_ADJACENT) - mesh->tetrahedra.neigh[neigh] = 4*left+j; - - mesh->tetrahedra.node[4*left+j] = mesh->tetrahedra.node[4*right+j]; - mesh->tetrahedra.neigh[4*left+j] = neigh; - } - - left++; right--; - } - - if(left==right && mesh->tetrahedra.colors[left]!=HXT_DELETED_COLOR) left++; - - mesh->tetrahedra.num = left; - - return HXT_STATUS_OK; -} - - -static inline HXTStatus hxtTetrahedraInit(HXTMesh* mesh, hxtNodeInfo* nodeInfo, uint32_t nToInsert){ +static inline HXTStatus hxtTetrahedraInit(HXTMesh* mesh, hxtNodeInfo* nodeInfo, uint32_t nToInsert, int verbosity){ if(nToInsert < 4){ return HXT_ERROR_MSG(HXT_STATUS_ERROR, "cannot mesh less than four vertices"); } if(mesh->tetrahedra.size < 5){ - HXT_INFO("Initialization reserved %lu Tet.", 10UL*nToInsert); - hxtTetrahedraReserve(mesh, 10UL*nToInsert); + uint32_t maxSizeEstim = MAX(omp_get_max_threads()*DELETED_BUFFER_SIZE+8UL*nToInsert, 10UL*nToInsert); + HXT_CHECK( hxtTetrahedraReserve(mesh, maxSizeEstim) ); + HXT_INFO_COND(verbosity>1, "Initialization reserved %lu Tet.", mesh->tetrahedra.size); } + HXTVertex* vertices = (HXTVertex*) mesh->vertices.coord; + // find non-coplanar vertices double orientation = 0.0; @@ -134,10 +89,10 @@ static inline HXTStatus hxtTetrahedraInit(HXTMesh* mesh, hxtNodeInfo* nodeInfo, { for (l=k+1; orientation==0.0 && l<nToInsert; l++) { - orientation = orient3d(mesh->vertices.coord + 4*nodeInfo[i].node, - mesh->vertices.coord + 4*nodeInfo[j].node, - mesh->vertices.coord + 4*nodeInfo[k].node, - mesh->vertices.coord + 4*nodeInfo[l].node); + orientation = orient3d(vertices[nodeInfo[i].node].coord, + vertices[nodeInfo[j].node].coord, + vertices[nodeInfo[k].node].coord, + vertices[nodeInfo[l].node].coord); } } } @@ -151,24 +106,28 @@ static inline HXTStatus hxtTetrahedraInit(HXTMesh* mesh, hxtNodeInfo* nodeInfo, // swap 0<->i 1<->j 2<->k 3<->l { - uint32_t tmp = nodeInfo[i].node; - nodeInfo[i].node = nodeInfo[0].node; - nodeInfo[0].node = tmp; + hxtNodeInfo tmp = nodeInfo[i]; + nodeInfo[i] = nodeInfo[0]; + nodeInfo[0] = tmp; + nodeInfo[0].status = HXT_STATUS_TRUE; i = 0; - tmp = nodeInfo[j].node; - nodeInfo[j].node = nodeInfo[1].node; - nodeInfo[1].node = tmp; + tmp = nodeInfo[j]; + nodeInfo[j] = nodeInfo[1]; + nodeInfo[1] = tmp; + nodeInfo[1].status = HXT_STATUS_TRUE; j = 1; - tmp = nodeInfo[k].node; - nodeInfo[k].node = nodeInfo[2].node; - nodeInfo[2].node = tmp; + tmp = nodeInfo[k]; + nodeInfo[k] = nodeInfo[2]; + nodeInfo[2] = tmp; + nodeInfo[2].status = HXT_STATUS_TRUE; k = 2; - tmp = nodeInfo[l].node; - nodeInfo[l].node = nodeInfo[3].node; - nodeInfo[3].node = tmp; + tmp = nodeInfo[l]; + nodeInfo[l] = nodeInfo[3]; + nodeInfo[3] = tmp; + nodeInfo[3].status = HXT_STATUS_TRUE; l = 3; } @@ -179,128 +138,111 @@ static inline HXTStatus hxtTetrahedraInit(HXTMesh* mesh, hxtNodeInfo* nodeInfo, j = tmp; } - mesh->tetrahedra.neigh[ 0] = 19; mesh->tetrahedra.node[ 0] = nodeInfo[l].node; - mesh->tetrahedra.neigh[ 1] = 15; mesh->tetrahedra.node[ 1] = nodeInfo[k].node; - mesh->tetrahedra.neigh[ 2] = 11; mesh->tetrahedra.node[ 2] = nodeInfo[j].node; - mesh->tetrahedra.neigh[ 3] = 7; mesh->tetrahedra.node[ 3] = nodeInfo[i].node; + mesh->tetrahedra.neigh[ 0] = 19; mesh->tetrahedra.node[ 0] = nodeInfo[l].node; + mesh->tetrahedra.neigh[ 1] = 15; mesh->tetrahedra.node[ 1] = nodeInfo[k].node; + mesh->tetrahedra.neigh[ 2] = 11; mesh->tetrahedra.node[ 2] = nodeInfo[j].node; + mesh->tetrahedra.neigh[ 3] = 7; mesh->tetrahedra.node[ 3] = nodeInfo[i].node; - mesh->tetrahedra.neigh[ 4] = 18; mesh->tetrahedra.node[ 4] = nodeInfo[l].node; - mesh->tetrahedra.neigh[ 5] = 10; mesh->tetrahedra.node[ 5] = nodeInfo[j].node; - mesh->tetrahedra.neigh[ 6] = 13; mesh->tetrahedra.node[ 6] = nodeInfo[k].node; - mesh->tetrahedra.neigh[ 7] = 3; mesh->tetrahedra.node[ 7] = HXT_GHOST_VERTEX; + mesh->tetrahedra.neigh[ 4] = 18; mesh->tetrahedra.node[ 4] = nodeInfo[l].node; + mesh->tetrahedra.neigh[ 5] = 10; mesh->tetrahedra.node[ 5] = nodeInfo[j].node; + mesh->tetrahedra.neigh[ 6] = 13; mesh->tetrahedra.node[ 6] = nodeInfo[k].node; + mesh->tetrahedra.neigh[ 7] = 3; mesh->tetrahedra.node[ 7] = HXT_GHOST_VERTEX; - mesh->tetrahedra.neigh[8 ] = 17; mesh->tetrahedra.node[ 8] = nodeInfo[l].node; - mesh->tetrahedra.neigh[9 ] = 14; mesh->tetrahedra.node[ 9] = nodeInfo[k].node; - mesh->tetrahedra.neigh[10] = 5; mesh->tetrahedra.node[10] = nodeInfo[i].node; - mesh->tetrahedra.neigh[11] = 2; mesh->tetrahedra.node[11] = HXT_GHOST_VERTEX; + mesh->tetrahedra.neigh[8 ] = 17; mesh->tetrahedra.node[ 8] = nodeInfo[l].node; + mesh->tetrahedra.neigh[9 ] = 14; mesh->tetrahedra.node[ 9] = nodeInfo[k].node; + mesh->tetrahedra.neigh[10] = 5; mesh->tetrahedra.node[10] = nodeInfo[i].node; + mesh->tetrahedra.neigh[11] = 2; mesh->tetrahedra.node[11] = HXT_GHOST_VERTEX; - mesh->tetrahedra.neigh[12] = 16; mesh->tetrahedra.node[12] = nodeInfo[l].node; - mesh->tetrahedra.neigh[13] = 6; mesh->tetrahedra.node[13] = nodeInfo[i].node; - mesh->tetrahedra.neigh[14] = 9; mesh->tetrahedra.node[14] = nodeInfo[j].node; - mesh->tetrahedra.neigh[15] = 1; mesh->tetrahedra.node[15] = HXT_GHOST_VERTEX; + mesh->tetrahedra.neigh[12] = 16; mesh->tetrahedra.node[12] = nodeInfo[l].node; + mesh->tetrahedra.neigh[13] = 6; mesh->tetrahedra.node[13] = nodeInfo[i].node; + mesh->tetrahedra.neigh[14] = 9; mesh->tetrahedra.node[14] = nodeInfo[j].node; + mesh->tetrahedra.neigh[15] = 1; mesh->tetrahedra.node[15] = HXT_GHOST_VERTEX; - mesh->tetrahedra.neigh[16] = 12; mesh->tetrahedra.node[16] = nodeInfo[k].node; - mesh->tetrahedra.neigh[17] = 8; mesh->tetrahedra.node[17] = nodeInfo[j].node; - mesh->tetrahedra.neigh[18] = 4; mesh->tetrahedra.node[18] = nodeInfo[i].node; - mesh->tetrahedra.neigh[19] = 0; mesh->tetrahedra.node[19] = HXT_GHOST_VERTEX; + mesh->tetrahedra.neigh[16] = 12; mesh->tetrahedra.node[16] = nodeInfo[k].node; + mesh->tetrahedra.neigh[17] = 8; mesh->tetrahedra.node[17] = nodeInfo[j].node; + mesh->tetrahedra.neigh[18] = 4; mesh->tetrahedra.node[18] = nodeInfo[i].node; + mesh->tetrahedra.neigh[19] = 0; mesh->tetrahedra.node[19] = HXT_GHOST_VERTEX; mesh->tetrahedra.num = 5; - for (uint64_t i=0; i<5 ;i++) mesh->tetrahedra.colors[i] = UINT16_MAX; + for (uint64_t i=0; i<5 ;i++){ + mesh->tetrahedra.colors[i] = UINT16_MAX; + mesh->tetrahedra.flag[i] = 0; + } return HXT_STATUS_OK; } - -// fill the passes array and the number of threads to beginwith -// return the number of passes -static unsigned computePasses(uint32_t passes[12], uint32_t nInserted, uint32_t nToInsert, - int* nthreads) +/*********************************** + * fill the passes array which tells + * the size of each BRIO round. + * return the number of BRIO passes + ***********************************/ +static unsigned computePasses(uint32_t passes[12], uint32_t nInserted, uint32_t nToInsert) { - unsigned npasses; - *nthreads = 1; + unsigned npasses=0; + passes[0] = nToInsert; - passes[0] = 0; - if(nInserted*ROUND_RATIO < SMALLEST_ROUND) - passes[1] = SMALLEST_ROUND; - else{ - passes[1] = nInserted*ROUND_RATIO; - - // we already begin with a good number of points in the mesh - // => compute how much threads we can launch - uint32_t tmp = nInserted/SMALLEST_ROUND; - while(tmp>0){ - tmp/=ROUND_RATIO; - *nthreads*=THREAD_RATIO; + for (unsigned i=0; i<10; i++) { + if(passes[i] < SMALLEST_ROUND || passes[i]/8 < nInserted){ + passes[i+1] = 0; + npasses = i+1; + break; } + passes[i+1] = passes[i]/7.5; } - if(nToInsert < passes[1]){ - passes[1] = nToInsert; - npasses = 1; - } - else{ - npasses = 2; - for (uint32_t i=ROUND_RATIO*MAX(passes[1],SMALLEST_ROUND); i<nToInsert; i*=ROUND_RATIO) - passes[npasses++] = i; - - passes[npasses] = nToInsert; + for(unsigned i=0; i<=npasses/2; i++){ + uint32_t tmp = passes[i]; + passes[i] = passes[npasses-i]; + passes[npasses-i] = tmp; } return npasses; } - - +/****************************************** + * initialisation of the TetLocal structure + ******************************************/ static inline HXTStatus localInit(TetLocal* local){ - local->sizeBnd = 1020; // accounting for the offset in aligned malloc, to avoid additional memory page - local->numBnd = 0; - local->bnd = NULL; - local->sizeDeleted = DELETED_BUFFER_SIZE; - local->numDeleted = 0; - local->deleted = NULL; - local->sizeVertices = 1014; - local->numVertices = 0; - local->vertices = NULL; - - HXT_CHECK( hxtAlignedMalloc(&local->bnd, local->sizeBnd*sizeof(local->bnd[0])) ); - HXT_CHECK( hxtAlignedMalloc(&local->deleted, local->sizeDeleted*sizeof(uint64_t)) ); - HXT_CHECK( hxtAlignedMalloc(&local->vertices, local->sizeVertices*sizeof(uint32_t)) ); + local->ball.size = 1020; // accounting for the offset in aligned malloc, to avoid additional memory page + local->ball.num = 0; + local->ball.bnd = NULL; + local->deleted.size = DELETED_BUFFER_SIZE; + local->deleted.num = 0; + local->deleted.tetID = NULL; + + HXT_CHECK( hxtAlignedMalloc(&local->ball.bnd, local->ball.size*sizeof(cavityBnd_t)) ); + HXT_CHECK( hxtAlignedMalloc(&local->deleted.tetID, local->deleted.size*sizeof(uint64_t)) ); return HXT_STATUS_OK; } - +/*********************************************** + re-allocation functions + ***********************************************/ static HXTStatus synchronizeReallocation(HXTMesh* mesh, volatile int* toCopy, volatile int* copy){ // threads cant be doing something while the realloc portion happen #pragma omp barrier - // this unable us to have the same value of toCopy fore everyone, as we are sure nothing happens to those variables here + // this unable us to have the same value of toCopy for everyone, as we are sure nothing happens to those variables here if(toCopy!=copy){ *copy = *toCopy; } - HXTStatus fail = HXT_STATUS_OK; + HXTStatus status = HXT_STATUS_OK; // make reallocations in a critical section #pragma omp single { - uint64_t nTet = mesh->tetrahedra.num; - uint64_t tetSize = mesh->tetrahedra.size; - if(nTet > tetSize){ - fail = hxtAlignedRealloc(&mesh->tetrahedra.neigh, nTet*8*sizeof(uint64_t)); - if(fail>=0) fail = hxtAlignedRealloc(&mesh->tetrahedra.node, nTet*8*sizeof(uint32_t)); - if(fail>=0) fail = hxtAlignedRealloc(&mesh->tetrahedra.colors, nTet*2*sizeof(uint16_t)); - mesh->tetrahedra.size = 2*nTet; - HXT_INFO("reallocation happened\n"); + if(mesh->tetrahedra.num > mesh->tetrahedra.size){ + status = hxtTetrahedraDoubleSize(mesh); } } // implicit barrier here - if(fail<0){ - HXT_TRACE(fail); - return fail; - } + if(status!=HXT_STATUS_OK) + HXT_TRACE(status); - return HXT_STATUS_OK; + return status; } @@ -314,1390 +256,1617 @@ static inline HXTStatus reserveNewTet(HXTMesh* mesh){ } static inline HXTStatus reserveNewDeleted(TetLocal* local, uint64_t num){ - if(num > local->sizeDeleted){ - HXT_CHECK( hxtAlignedRealloc(&local->deleted, 2*num*sizeof(uint64_t)) ); - local->sizeDeleted = 2*num; + num += local->deleted.num; + if(num > local->deleted.size){ + HXT_CHECK( hxtAlignedRealloc(&local->deleted.tetID, 2*num*sizeof(uint64_t)) ); + local->deleted.size = 2*num; } return HXT_STATUS_OK; } static inline HXTStatus reserveNewBnd(TetLocal* local, uint64_t num){ - if(num > local->sizeBnd){ - HXT_CHECK( hxtAlignedRealloc(&local->bnd, 2*num*sizeof(local->bnd[0])) ); - local->sizeBnd = 2*num; + num += local->ball.num; + if(num > local->ball.size){ + HXT_CHECK( hxtAlignedRealloc(&local->ball.bnd, 2*num*sizeof(cavityBnd_t)) ); + local->ball.size = 2*num; } return HXT_STATUS_OK; } +/***********************************************/ - -static inline HXTStatus reserveNewVertices(TetLocal* local, uint32_t num){ - if(num > local->sizeVertices){ - HXT_CHECK( hxtAlignedRealloc(&local->vertices, 2*num*sizeof(uint32_t)) ); - local->sizeVertices = 2*num; - } - - return HXT_STATUS_OK; -} - - - -static inline HXTStatus checkTetrahedron(hxtVertex* vertices, TetLocal* local, const uint32_t* nodes){ - uint64_t rel = local->endDist - local->startDist; - - if(rel==UINT64_MAX) // we are working with one thread only... +/************************************ + * check if a tetrahedra is entirely + * in the calling thread's partition + ***********************************/ +static inline HXTStatus checkTetrahedron(HXTVertex* vertices, TetLocal* local, const uint32_t* nodes){ + /* Actually, one vertex (not more) could be in another partition without creating a conflict. + However, all threads would have to have a verticesID array => a lot of memory space wasted. + Instead, we only allow the ghost vertex to be in another partition, it is handle differently in + computeAdjacenciesFast function */ + uint64_t rel = local->partition.endDist - local->partition.startDist; + + if(local->partition.endDist==UINT64_MAX) // if we are working with one thread only return HXT_STATUS_OK; - // unsigned wrap around is defined by the standard, so we improve the speed - uint64_t d0 = vertices[nodes[0]].padding.hilbertDist - local->startDist; - uint64_t d1 = vertices[nodes[1]].padding.hilbertDist - local->startDist; - uint64_t d2 = vertices[nodes[2]].padding.hilbertDist - local->startDist; - uint64_t d3 = (nodes[3]==HXT_GHOST_VERTEX?0:vertices[nodes[3]].padding.hilbertDist) - local->startDist; - -#if defined( HXT_DELAUNAY_MAX_MEMORY ) || defined( HXT_DELAUNAY_LOW_MEMORY ) - if( (d0>=rel) + (d1>=rel) + (d2>=rel) + (d3>=rel) > 1) - return HXT_STATUS_INTERNAL; -#else + // unsigned wrap around is defined by the standard + uint64_t d0 = vertices[nodes[0]].padding.hilbertDist - local->partition.startDist; + uint64_t d1 = vertices[nodes[1]].padding.hilbertDist - local->partition.startDist; + uint64_t d2 = vertices[nodes[2]].padding.hilbertDist - local->partition.startDist; + uint64_t d3 = nodes[3]==HXT_GHOST_VERTEX ? d2: (vertices[nodes[3]].padding.hilbertDist - local->partition.startDist); + if((d0>=rel) || (d1>=rel) || (d2>=rel) || (d3>=rel)) return HXT_STATUS_INTERNAL; -#endif return HXT_STATUS_OK; } -#define HXT_SQR(X) (X)*(X) +static inline HXTStatus pointIsTooClose(const double* __restrict__ p1, const double* __restrict__ p2, double nodalSize){ + double d2 = (p1[0]-p2[0])*(p1[0]-p2[0]) + + (p1[1]-p2[1])*(p1[1]-p2[1]) + + (p1[2]-p2[2])*(p1[2]-p2[2]); + if (d2 < 0.8*0.8*nodalSize*nodalSize){ + return HXT_STATUS_INTERNAL; + } + + return HXT_STATUS_OK; +} -static inline HXTStatus filterCavity (TetLocal* local, HXTMesh *mesh, const double *nodalSizes) +/* if one edge of the cavity is shorter than the nodalSize, return HXT_STATUS_INTERNAL */ +static inline HXTStatus filterCavity (TetLocal* local, HXTMesh *mesh, const double *nodalSizes, const uint32_t vta) { - uint32_t vta = local->bnd[0].node[0]; double *vtaCoord = mesh->vertices.coord + 4*vta; double vtaNodalSize = nodalSizes[vta]; - for (uint64_t i = 0 ; i< local->numBnd ; i++) { - for (uint64_t j=1;j<4;j++) { - uint32_t nodej = local->bnd[i].node[j]; + for (uint64_t i = 0 ; i< local->ball.num ; i++) { + for (unsigned j=0;j<3;j++) { + uint32_t nodej = local->ball.bnd[i].node[j]; if (j!=3 || nodej != HXT_GHOST_VERTEX){ double *Xj = mesh->vertices.coord + 4*nodej; - double d2 = HXT_SQR (vtaCoord[0]-Xj[0]) - + HXT_SQR (vtaCoord[1]-Xj[1]) - + HXT_SQR (vtaCoord[2]-Xj[2]); - double minSizeAllowed = 0.8 * 0.5 * ( vtaNodalSize + nodalSizes[nodej]); - if (d2 < HXT_SQR(minSizeAllowed)){ - return HXT_STATUS_INTERNAL; - } + HXT_CHECK( pointIsTooClose(vtaCoord, Xj, 0.5*( vtaNodalSize + nodalSizes[nodej])) ); } } } return HXT_STATUS_OK; } +static inline HXTStatus filterTet(HXTMesh* mesh, const double *nodalSizes, const uint64_t curTet, const uint32_t vta){ + HXTVertex* vertices = (HXTVertex*) mesh->vertices.coord; -static inline void restoreDeleted(HXTMesh* mesh, TetLocal* local, const uint64_t prevDeleted, const uint16_t color){ - for (uint64_t i=prevDeleted; i<local->numDeleted; i++) - mesh->tetrahedra.colors[local->deleted[i]/4] = color; + double *vtaCoord = vertices[vta].coord; + double vtaNodalSize = nodalSizes[vta]; - local->numDeleted = prevDeleted; + for (unsigned j=0; j<4; j++) { + uint32_t nodej = mesh->tetrahedra.node[4*curTet+j]; + + if (j!=3 || nodej != HXT_GHOST_VERTEX){ + double* Xj = vertices[nodej].coord; + double otherNodalSize = nodalSizes[nodej]; + if(otherNodalSize==DBL_MAX){ + otherNodalSize = vtaNodalSize; + } + HXT_CHECK( pointIsTooClose(vtaCoord, Xj, 0.5*( vtaNodalSize + otherNodalSize)) ); + } + } + return HXT_STATUS_OK; } -static inline HXTStatus doTheWork(HXTMesh* mesh, - uint32_t* verticesID, - TetLocal* local, - const double* nodalSizes, - uint64_t* curTet, - const uint32_t vta){ - const uint64_t prevDeleted = local->numDeleted; +/* restore the structure as it was before the failed insertion attempt */ +static inline void restoreDeleted(HXTMesh* mesh, TetLocal* local, const uint64_t prevDeleted, const uint16_t color){ + for (uint64_t i=prevDeleted; i<local->deleted.num; i++) + unmarkTetAsDeleted(mesh, local->deleted.tetID[i]); - HXT_CHECK( walking2Cavity(mesh, local, curTet, vta) ); + local->deleted.num = prevDeleted; +} - const uint16_t color = mesh->tetrahedra.colors[*curTet/4]; - HXTStatus status = diggingACavity(mesh, local, *curTet, vta, color); +/*********************************** + * insphere predicate & perturbation + ***********************************/ +// see Perturbations and Vertex Removal in a 3D Delaunay Triangulation, O. Devillers & M. Teillaud +static double symbolicPerturbation (uint32_t indices[5] , const double* __restrict__ i, + const double* __restrict__ j, + const double* __restrict__ k, + const double* __restrict__ l, + const double* __restrict__ m){ + double const* pt[5] = {i,j,k,l,m}; - if(status==HXT_STATUS_INTERNAL){ - restoreDeleted(mesh, local, prevDeleted, color); - return HXT_STATUS_TRYAGAIN; - } - else{ - HXT_CHECK(status); - } + // 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. + int swaps = 0; // Record the total number of swaps. + int n = 5; + int count; + do { + count = 0; + n = n - 1; + for (int iter = 0; iter < n; iter++) { + if (indices[iter] > indices[iter+1]) { - // printf("deleted: %lu, bnd:%lu\n", local->numDeleted, local->numBnd); - if(nodalSizes!=NULL && filterCavity(local, mesh, nodalSizes)==HXT_STATUS_INTERNAL){ - restoreDeleted(mesh, local, prevDeleted, color); - return HXT_STATUS_FALSE; + const double *swappt = pt[iter]; + pt[iter] = pt[iter+1]; + pt[iter+1] = swappt; + + uint32_t sw = indices [iter]; + indices[iter] = indices[iter+1]; + indices[iter+1] = sw; + count++; + } + } + swaps += count; + } while (count > 0); // Continue if some points are swapped. + + double 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; } + + double oriB = -orient3d(pt[0], pt[2], pt[3], pt[4]); + if (oriB == 0.0) HXT_WARNING("Symbolic perturbation failed (2 superposed vertices ?)"); + // Flip the sign if there are odd number of swaps. + if ((swaps % 2) != 0) oriB = -oriB; + return oriB; +} - if(local->numBnd > local->numDeleted){ - uint64_t needed = MAX(DELETED_BUFFER_SIZE-local->numDeleted, local->numBnd - local->numDeleted); - uint64_t ntet; +/* wrapper around the insphere predicate that handles + the ghost vertex and symbolic perturbation if needed */ +double tetInsphere(HXTMesh* mesh, const uint64_t curTet, const uint32_t vta){ + HXTVertex* vertices = (HXTVertex*) mesh->vertices.coord; + uint32_t* Node = mesh->tetrahedra.node + curTet; - #pragma omp atomic capture - { ntet = mesh->tetrahedra.num; mesh->tetrahedra.num+=needed;} + const double* __restrict__ a = vertices[Node[0]].coord; + const double* __restrict__ b = vertices[Node[1]].coord; + const double* __restrict__ c = vertices[Node[2]].coord; + const double* __restrict__ e = vertices[vta].coord; - reserveNewTet(mesh); - reserveNewDeleted(local, local->numDeleted+needed); + if(Node[3]==HXT_GHOST_VERTEX){ + double det = orient3d(a,b,c,e); - #pragma omp simd - for (uint64_t i=0; i<needed; i++){ - local->deleted[local->numDeleted+i] = 4*(ntet+i); - mesh->tetrahedra.colors[ntet+i] = HXT_DELETED_COLOR; + if(det!=0.0){ + return det; } - local->numDeleted+=needed; + // we never go here, except when point are aligned on boundary + // HXT_INFO("insphere using opposite vertex"); + uint32_t oppositeNode = mesh->tetrahedra.node[mesh->tetrahedra.neigh[curTet+3]]; + double* const __restrict__ oppositeVertex = vertices[oppositeNode].coord; + det = insphere(a,b,c,oppositeVertex,e); + + if (det == 0.0) { + uint32_t nn[5] = {Node[0],Node[1],Node[2],oppositeNode,vta}; + // HXT_INFO("symbolic perturbation on boundary"); + det = symbolicPerturbation (nn, a,b,c,oppositeVertex,e); + + } + return -det; } - HXT_CHECK( fillingACavity(mesh, local, verticesID, curTet, color) ); + double* const __restrict__ d = vertices[Node[3]].coord; - return HXT_STATUS_TRUE; + double det = insphere(a,b,c,d,e); + if (det == 0.0) { + uint32_t nn[5] = {Node[0],Node[1],Node[2],Node[3],vta}; + // HXT_INFO("symbolic perturbation"); + det = symbolicPerturbation (nn, a,b,c,d,e); + } + return det; } +/*********************************** + * walk to cavity + ***********************************/ +static HXTStatus walking2Cavity(HXTMesh* mesh, TetLocal* local, uint64_t* __restrict__ curTet, const uint32_t vta){ + uint64_t nextTet = *curTet; + uint32_t seed = 1; + HXTVertex* vertices = (HXTVertex*) mesh->vertices.coord; + /* if nextTet is a ghost triangle, go to the neighbor that is not a ghost triangle */ + if(mesh->tetrahedra.node[4*nextTet+3]==HXT_GHOST_VERTEX) + nextTet = mesh->tetrahedra.neigh[4*nextTet+3]/4; -static HXTStatus hxtTetrahedraCompute(HXTMesh* mesh, - HXTDelaunayOptions* options, - hxtNodeInfo* nodeInfo, - const uint64_t nToInsert, - int noReordering) -{ + double* const vtaCoord = vertices[vta].coord; + unsigned enteringFace = 4; - double time = omp_get_wtime(); +#ifndef NDEBUG + uint64_t TotalCount = 0; +#endif + - uint64_t numTetrahedra = mesh->tetrahedra.num; - uint32_t totalNumSkipped = 0; + while(1){ + const uint32_t* __restrict__ curNode = mesh->tetrahedra.node + 4*nextTet; + const uint64_t* __restrict__ curNeigh = mesh->tetrahedra.neigh + 4*nextTet; - // third, divide indices in different passes - const int maxThreads = omp_get_max_threads(); - int threadToBeginWith; - uint32_t passes[12]; + #ifndef NDEBUG + if(curNode[3]==HXT_GHOST_VERTEX){ + return HXT_ERROR_MSG(HXT_STATUS_FAILED, "walked outside of the domain"); + } + #endif - unsigned npasses = computePasses(passes, options->numVerticesInMesh, nToInsert, &threadToBeginWith); - threadToBeginWith = MIN(threadToBeginWith, maxThreads); + unsigned neigh = 4; + unsigned outside = 0; + uint32_t randomU = hxtReproducibleLCG(&seed); + for (unsigned i=0; i<4; i++) + { + uint32_t index = (i+randomU)%4; + if (index!=enteringFace) { + // we walk where the volume is minimum + const double* __restrict__ a = vertices[curNode[(index+1)&3]].coord; + const double* __restrict__ b = vertices[curNode[(index&2)^3]].coord; + const double* __restrict__ c = vertices[curNode[(index+3)&2]].coord; + + if (orient3d(a,b,c, vtaCoord) < 0.0){ + if(curNeigh[index]==HXT_NO_ADJACENT) { // the point is outside the triangulation + return HXT_ERROR_MSG(HXT_STATUS_ERROR, + "vertex {%f %f %f} outside the triangulation and no ghost tetrahedra", + vtaCoord[0], vtaCoord[1], vtaCoord[2]); + } - // that ugly cast because people want an array of double into the mesh structure - hxtVertex* vertices = (hxtVertex*) mesh->vertices.coord; - + uint64_t tet = curNeigh[index]/4; + const uint32_t* __restrict__ neighNodes = mesh->tetrahedra.node + tet*4; + if(checkTetrahedron(vertices, local, neighNodes)==HXT_STATUS_OK){ + if(neighNodes[3]==HXT_GHOST_VERTEX){ + *curTet = tet; + return HXT_STATUS_OK; + } + neigh=index; + break; + } + outside = 1; + } + } + } - /****************************************************** - shuffle (and optimize cache locality) - ******************************************************/ - if(noReordering){ - // shuffle nodeInfo - HXT_CHECK( hxtNodeInfoShuffle(nodeInfo, nToInsert) ); - } - else{ - hxtVertex* verticesToInsert = vertices + mesh->vertices.num - nToInsert; - // shuffle the vertices to insert, then sort each pass except the first according to the hilbert curve... - HXT_CHECK( hxtVerticesShuffle(verticesToInsert, nToInsert) ); + if(neigh==4){ + const double* __restrict__ a = vertices[curNode[0]].coord; + const double* __restrict__ b = vertices[curNode[1]].coord; + const double* __restrict__ c = vertices[curNode[2]].coord; + const double* __restrict__ d = vertices[curNode[3]].coord; + if(outside || + (orient3d(a,b,c,vtaCoord)<=0.0) + + (orient3d(a,b,vtaCoord,d)<=0.0) + + (orient3d(a,vtaCoord,c,d)<=0.0) + + (orient3d(vtaCoord,b,c,d)<=0.0)>2){ + return HXT_STATUS_TRYAGAIN; + } + *curTet = nextTet; + return HXT_STATUS_OK; + } - uint32_t nbits = 0; - HXT_CHECK( hxtVerticesHilbertDist(options->bbox, verticesToInsert, nToInsert, &nbits, NULL) ); + // printf("nextTet %u %g %u %u\n",nextTet,Min, count, neigh); + nextTet = curNeigh[neigh]/4; + enteringFace = curNeigh[neigh]&3; - for (unsigned i=1; i<npasses; i++) { - HXT_CHECK( hxtVerticesSort(verticesToInsert+passes[i], passes[i+1]-passes[i], nbits) ); + #ifndef NDEBUG + if(TotalCount>mesh->tetrahedra.num){ + return HXT_ERROR_MSG(HXT_STATUS_FAILED, "infinite walk to find the cavity"); } + // printf("%lu\n",TotalCount); + TotalCount++; + #endif } - - /****************************************************** - Initializations and allocations - ******************************************************/ - if(mesh->tetrahedra.num<5){ - HXT_INFO_COND(options->verbosity>0, - "Initializing mesh"); - HXT_CHECK( hxtTetrahedraInit(mesh, nodeInfo, nToInsert) ); - options->numVerticesInMesh = 4; // not counting the ghost vertex - passes[0] = 4; - } +} - uint32_t* verticesID; -#if defined( HXT_DELAUNAY_MAX_MEMORY ) - HXT_CHECK( hxtAlignedMalloc(&verticesID, maxThreads*(mesh->vertices.num+SIMD_ALIGN/4)*sizeof(uint32_t)) ); -#elif defined( HXT_DELAUNAY_LOW_MEMORY ) - verticesID = NULL; // we do not need it -#else - HXT_CHECK( hxtAlignedMalloc(&verticesID, (mesh->vertices.num+1)*sizeof(uint32_t)) ); -#endif +/*********************************** + * digging cavity + ***********************************/ - TetLocal* Locals; - HXT_CHECK( hxtMalloc(&Locals, maxThreads*sizeof(TetLocal)) ); - // HXT_CHECK( hxtMalloc()) +/* pushing cavity boundary information to local->ball */ +static inline void bndPush( TetLocal* local, uint16_t flag, + const uint32_t node1, const uint32_t node2, + const uint32_t node3, const uint64_t neigh){ + uint64_t n = local->ball.num; + local->ball.bnd[n].node[0] = node1; + local->ball.bnd[n].node[1] = node2; + local->ball.bnd[n].node[2] = node3; + local->ball.bnd[n].flag = flag; + local->ball.bnd[n].neigh = neigh; + local->ball.num++; +} - for (int i=0; i<maxThreads; i++) - localInit(&Locals[i]); +/* delete a tetrahedra being part of the cavity */ +static inline HXTStatus deletedPush(HXTMesh* mesh, TetLocal* local, const uint64_t neigh){ + // check if 3 points of the new tetrahedra are owned by this thread + HXT_CHECK( checkTetrahedron((HXTVertex*) mesh->vertices.coord, local, mesh->tetrahedra.node + neigh*4) ); + local->deleted.tetID[local->deleted.num++] = neigh; + markTetAsDeleted(mesh, neigh); + return HXT_STATUS_OK; +} - HXT_INFO_COND(options->verbosity>0, - "insertion of %10u vertices (%3d threads )\t- mesh.nvert: %-10u", - passes[npasses] - passes[0], maxThreads, options->numVerticesInMesh); +/* check if the cavity is star shaped + This isn't usefull for pure Delaunay but when we constrain cavity with colors, + it is usefull */ +static HXTStatus isStarShaped(TetLocal* local, HXTMesh* mesh, const uint32_t vta, uint64_t* blindFaceIndex) +{ + HXTVertex* vertices = (HXTVertex*) mesh->vertices.coord; + double *vtaCoord = vertices[vta].coord; - for (uint32_t p=0; p<npasses; p++) - { + for (uint64_t i=0; i<local->ball.num; i++) { + if(local->ball.bnd[i].node[2]==HXT_GHOST_VERTEX){ - double percent = 200; + } + else{ + double* b = vertices[local->ball.bnd[i].node[0]].coord; + double* c = vertices[local->ball.bnd[i].node[1]].coord; + double* d = vertices[local->ball.bnd[i].node[2]].coord; + if(orient3d(vtaCoord, b, c, d)<=0.0){ + *blindFaceIndex = i; + return HXT_STATUS_INTERNAL; + } + } + } + return HXT_STATUS_OK; +} - int nthreads = threadToBeginWith; - threadToBeginWith=MIN(threadToBeginWith*THREAD_RATIO, maxThreads); - const uint32_t initialPassLength = passes[p+1] - passes[p]; - for(uint32_t n=0; passes[p+1]-passes[p]; n++) - { - const uint32_t passStart = passes[p]; - const uint32_t passEnd = passes[p+1]; - const uint32_t passLength = passEnd - passStart; - - /****************************************************** - choosing number of threads - ******************************************************/ - - - if(percent<140/nthreads || passLength<SMALLEST_ROUND){ - nthreads=1; - } - else if(percent<20){ - nthreads/=2; - } - else if(passLength < nthreads*SMALLEST_ROUND) - nthreads/=2; +static HXTStatus undeleteTetrahedron(TetLocal* local, HXTMesh* mesh, const uint32_t vta, uint64_t tetToUndelete) { + // the tetrahedra should not be deleted anymore + for (uint64_t i=local->deleted.num; ; i--) { + if(local->deleted.tetID[i-1]==tetToUndelete) { + local->deleted.num--; + local->deleted.tetID[i-1] = local->deleted.tetID[local->deleted.num]; + break; + } +#ifdef DEBUG + if(i==1) + return HXT_ERROR_MSG(HXT_STATUS_ERROR, "could not find the tetrahedra in the deleted array"); +#endif + } + unmarkTetAsDeleted(mesh, tetToUndelete); + + uint64_t bndFaces[4] = {HXT_NO_ADJACENT, HXT_NO_ADJACENT, HXT_NO_ADJACENT, HXT_NO_ADJACENT}; + int nbndFace = 0; + + // we should update the boundary (that's the difficult part...) + // first remove all the boundary faces that come from the tetrahedron we just remove from the cavity + for (uint64_t i=local->ball.num; nbndFace<4 && i>0; i--) { + if(mesh->tetrahedra.neigh[local->ball.bnd[i-1].neigh]/4==tetToUndelete) { + bndFaces[nbndFace++] = local->ball.bnd[i-1].neigh; + local->ball.num--; + local->ball.bnd[i-1] = local->ball.bnd[local->ball.num]; + } + } + // we must replace them by all the other faces of the tetrahedron we just removed + const uint64_t* __restrict__ curNeigh = mesh->tetrahedra.neigh + tetToUndelete*4; + const uint32_t* __restrict__ curNode = mesh->tetrahedra.node + tetToUndelete*4; - /****************************************************** - Sorting vertices - ******************************************************/ +#ifdef DEBUG + int nbndFace2 = (isTetDeleted(mesh, curNeigh[0]/4)==0) + (isTetDeleted(mesh, curNeigh[1]/4)==0) + (isTetDeleted(mesh, curNeigh[2]/4)==0) + (isTetDeleted(mesh, curNeigh[3]/4)==0); + if(nbndFace!=nbndFace2) + return HXT_ERROR_MSG(HXT_STATUS_ERROR, "found %d non-deleted tet adjacent to the tet we unremove but there should be %d %lu %lu %lu %lu", nbndFace, nbndFace2, bndFaces[0], bndFaces[1], bndFaces[2], bndFaces[3]); +#endif - double hxtDeclareAligned bboxShift[4]={0.5,0.5,0.5,0}; + HXT_CHECK( reserveNewBnd(local, 3) ); + if(curNeigh[0]!=bndFaces[0] && curNeigh[0]!=bndFaces[1] && curNeigh[0]!=bndFaces[2] && curNeigh[0]!=bndFaces[3]) + bndPush(local, (mesh->tetrahedra.flag[tetToUndelete] & 0x9) | + (isEdgeConstrained(mesh, tetToUndelete, 0 , 2)>>1) | + (isEdgeConstrained(mesh, tetToUndelete, 0 , 1)<<1), curNode[2], curNode[1], curNode[3], 4*tetToUndelete+0); - if(percent<100 && nthreads>1) - { - bboxShift[0] = (double) rand()/RAND_MAX; - bboxShift[1] = (double) rand()/RAND_MAX; - bboxShift[2] = (double) rand()/RAND_MAX; - bboxShift[3] = (double) rand()/RAND_MAX; // this is not a bbox deformation, it's an index shift - } + if(curNeigh[1]!=bndFaces[0] && curNeigh[1]!=bndFaces[1] && curNeigh[1]!=bndFaces[2] && curNeigh[1]!=bndFaces[3]) + bndPush(local, ((mesh->tetrahedra.flag[tetToUndelete] & 0xD0)>>4) | + isEdgeConstrained(mesh, tetToUndelete, 0 , 1), curNode[0], curNode[2], curNode[3], 4*tetToUndelete+1); - uint32_t nbits = hxtAdvancedHilbertBits(initialPassLength, passLength, nthreads, maxThreads); - if(noReordering){ - HXT_CHECK( hxtVerticesHilbertDist(options->bbox, vertices, mesh->vertices.num, &nbits, bboxShift) ); - } - else{ - HXT_CHECK( hxtVerticesHilbertDist(options->bbox, vertices, mesh->vertices.num - nToInsert + passEnd, &nbits, bboxShift) ); - } - + if(curNeigh[2]!=bndFaces[0] && curNeigh[2]!=bndFaces[1] && curNeigh[2]!=bndFaces[2] && curNeigh[2]!=bndFaces[3]) + bndPush(local, ((mesh->tetrahedra.flag[tetToUndelete] & 0x900)>>8) | + (isEdgeConstrained(mesh, tetToUndelete, 1 , 2)>>5) | + isEdgeConstrained(mesh, tetToUndelete, 0 , 2), curNode[1], curNode[0], curNode[3], 4*tetToUndelete+2); - #pragma omp parallel for simd aligned(nodeInfo:SIMD_ALIGN) - for (uint32_t i=passStart; i<passEnd; i++) { - nodeInfo[i].hilbertDist = vertices[nodeInfo[i].node].padding.hilbertDist; - } + if(curNeigh[3]!=bndFaces[0] && curNeigh[3]!=bndFaces[1] && curNeigh[3]!=bndFaces[2] && curNeigh[3]!=bndFaces[3]) + bndPush(local, (isFacetConstrained(mesh, 4*tetToUndelete+3)>>12) | + (isEdgeConstrained(mesh, tetToUndelete, 0 , 3)>>2) | + (isEdgeConstrained(mesh, tetToUndelete, 1 , 3)>>5) | + (isEdgeConstrained(mesh, tetToUndelete, 2 , 3)>>8), curNode[0], curNode[1], curNode[2], 4*tetToUndelete+3); - if(n!=0){ - HXT_CHECK( hxtNodeInfoSort(nodeInfo + passStart, passLength, nbits) ); - } + return HXT_STATUS_OK; +} - const uint32_t rem = passLength%nthreads; - const uint32_t step = passLength/nthreads; - uint32_t indexShift = MIN(step-1,(uint32_t) bboxShift[3]*step); +static HXTStatus reshapeCavityIfNeeded(TetLocal* local, HXTMesh* mesh, const uint32_t vta, uint64_t prevDeleted) { + // we will remove the tetrahedra adjacent to the face that does not see the point, progressively, until the cavity is star shaped... + uint64_t blindFace = 0; + while(isStarShaped(local, mesh, vta, &blindFace)==HXT_STATUS_INTERNAL) + { + // printf("deleting %lu cavity:%lu ball:%lu\n",mesh->tetrahedra.neigh[local->ball.bnd[blindFace].neigh]/4, local->deleted.num-prevDeleted, local->ball.num ); + HXT_CHECK( undeleteTetrahedron(local, mesh, vta, mesh->tetrahedra.neigh[local->ball.bnd[blindFace].neigh]/4) ); + } + return HXT_STATUS_OK; +} - int threadFinished = 0; - #pragma omp parallel num_threads(nthreads) - { - uint64_t curTet = 0; // we always begin with the first tet. (index 0) - const int threadID = omp_get_thread_num(); - nthreads = omp_get_num_threads(); +static HXTStatus respectEdgeConstraint(TetLocal* local, HXTMesh* mesh, const uint32_t vta, const uint16_t color, const uint64_t prevDeleted) { + // HXT_WARNING("a constrained edge was inside the cavity, recovering it"); - uint32_t localStart; - uint32_t localN; - int foundTet = 0; + // all the tetrahedron have the same color 'color', we will use that color to flag them + for (uint64_t i=prevDeleted; i<local->deleted.num; i++) { + uint64_t delTet = local->deleted.tetID[i]; + mesh->tetrahedra.colors[delTet] = 0; + } - if(nthreads>1){ - // if(threadID<nthreads){ + for (uint64_t i=prevDeleted; i<local->deleted.num; i++) { + uint64_t delTet = local->deleted.tetID[i]; + int exist = 1; + for (int j=0; exist && j<4; j++) { + for (int k=j+1; exist && k<4; k++) { + if(isEdgeConstrained(mesh, delTet, j, k) && (mesh->tetrahedra.colors[delTet] & (1U<<(j*4+k)))==0) { + // const int _UP_FACET[4][4] = {{-1, 2, 3, 1}, + // { 3,-1, 0, 2}, + // { 1, 3,-1, 0}, + // { 2, 0, 1,-1}}; + + // HXT_WARNING("found constrained edge %u-%u in tetrahedron %lu (%d %d) inside the cavity\n",mesh->tetrahedra.node[4*delTet+ _UP_FACET[j][k]], mesh->tetrahedra.node[4*delTet+ _UP_FACET[k][j]], delTet, j, k); + // turn around the edge to check if a facet is on the boundary of the cavity. If yes, the edge is processed... if not, we delete one tet. + int in_facet = j; + int out_facet = k; + + int edgeIsSafe = 0; + uint64_t curTet = delTet; + // first turn + do + { + uint32_t newV = mesh->tetrahedra.node[4*curTet + in_facet]; - /****************************************************** - Making partitions - ******************************************************/ + // go into the neighbor through out_facet + uint64_t neigh = mesh->tetrahedra.neigh[4*curTet + out_facet]; + curTet = neigh/4; + in_facet = neigh%4; - // printf("bbox: %f %f %f shift = %u/%u\n", bboxShift[0], bboxShift[1], bboxShift[2], indexShift%step, step); - localStart = step*threadID + ((threadID>=rem)?rem:threadID) + indexShift; - uint64_t dist = nodeInfo[passStart + localStart].hilbertDist; - - uint32_t up = 1; - while(localStart+up<passLength && dist==nodeInfo[passStart + localStart + up].hilbertDist) - up++; + uint32_t* nodes = mesh->tetrahedra.node + 4*curTet; + for (out_facet=0; out_facet<3; out_facet++) + if(nodes[out_facet]==newV) + break; - uint32_t down = 0; - while(localStart>down && dist==nodeInfo[passStart + localStart-down-1].hilbertDist) - down--; + if(isTetDeleted(mesh, curTet)!=0) { + // mark that the edge as been treate + #ifdef DEBUG + if((mesh->tetrahedra.colors[curTet] & (1U<<(in_facet<out_facet?in_facet*4+out_facet:out_facet*4+in_facet)))!=0) + return HXT_ERROR_MSG(HXT_STATUS_ERROR, "the flag says that the tet has already been processed for this edge..."); + #endif + mesh->tetrahedra.colors[curTet] |= (1U<<(in_facet*4+out_facet)) | (1U<<(out_facet*4+in_facet)); + } + else { + edgeIsSafe=1; + } - if(up>down && threadID!=0 && - nodeInfo[passStart - + step*(threadID-1) + (((threadID-1)>=rem)?rem:(threadID-1)) - + indexShift].hilbertDist - ==dist) - localStart -=down; - else - localStart += up; - - Locals[threadID].localStart = localStart; - // } + // printf("edge %u-%u, tet %lu (%d %d) deleted:%u\n", mesh->tetrahedra.node[4*curTet+ _UP_FACET[in_facet][out_facet]], mesh->tetrahedra.node[4*curTet+ _UP_FACET[out_facet][in_facet]], curTet, in_facet, out_facet, isTetDeleted(mesh, curTet)!=0); - #pragma omp barrier + } while (curTet!=delTet); - // if(threadID<nthreads){ - uint32_t localEnd = Locals[(threadID+1)%nthreads].localStart; - // if localStart = localEnd, the length is 0 - localN = (localEnd + passLength - localStart)%passLength; + if(!edgeIsSafe) { // we must find a tetrahedron on the opposite side of vta and delete it. + in_facet = j; + out_facet = k; + curTet = delTet; - Locals[threadID].startDist = nodeInfo[passStart + localStart].hilbertDist; - Locals[threadID].endDist = nodeInfo[passStart + localEnd].hilbertDist; + uint64_t tetContainingVta = local->deleted.tetID[prevDeleted]; + uint64_t tetToUndelete = HXT_NO_ADJACENT; + double distMax = 0.0; + double* vtaCoord = mesh->vertices.coord + 4*vta; - /****************************************************** - find starting tetrahedron - ******************************************************/ + #ifdef DEBUG + double* a = mesh->vertices.coord + 4*mesh->tetrahedra.node[4*tetContainingVta]; + double* b = mesh->vertices.coord + 4*mesh->tetrahedra.node[4*tetContainingVta+1]; + double* c = mesh->vertices.coord + 4*mesh->tetrahedra.node[4*tetContainingVta+2]; + double* d = mesh->vertices.coord + 4*mesh->tetrahedra.node[4*tetContainingVta+3]; - for (uint64_t i=0; i<mesh->tetrahedra.num; i++) - { - curTet = i*4; - if(mesh->tetrahedra.colors[i]!=HXT_DELETED_COLOR && - checkTetrahedron(vertices, &Locals[threadID], mesh->tetrahedra.node + curTet )==HXT_STATUS_OK) - { - foundTet = 1; - break; + if(orient3d(vtaCoord,b,c,d)<0.0 || orient3d(a,vtaCoord,c,d)<0.0 || orient3d(a,b,vtaCoord,d)<0.0 || orient3d(a,b,c,vtaCoord)<0.0) { + return HXT_ERROR_MSG(HXT_STATUS_ERROR, "an edge part of a ghost tetrahedron is constrained"); } - } - // } - - #pragma omp barrier - } - else - // if(threadID==0) - { + #endif - /****************************************************** - single-thread partition and starting tetrahedron - ******************************************************/ - localStart = 0; - localN = passLength; - Locals[0].startDist = 0; - Locals[0].endDist = UINT64_MAX; - - for (uint64_t i=0; i<mesh->tetrahedra.num; i++) - { - if(mesh->tetrahedra.colors[i]!=HXT_DELETED_COLOR){ - curTet = i*4; - foundTet = 1; - break; - } - } - } + do + { + uint32_t newV = mesh->tetrahedra.node[4*curTet + in_facet]; - if (foundTet == 0) { - HXT_INFO_COND(options->verbosity>1, - "thread %d did not find any tetrahedron to begin with", threadID); - } + // go into the neighbor through out_facet + uint64_t neigh = mesh->tetrahedra.neigh[4*curTet + out_facet]; + curTet = neigh/4; + in_facet = neigh%4; + uint32_t* nodes = mesh->tetrahedra.node + 4*curTet; + for (out_facet=0; out_facet<3; out_facet++) + if(nodes[out_facet]==newV) + break; - // if(threadID<nthreads){ - if(foundTet!=0){ + double* coord1 = mesh->vertices.coord + newV; + double* coord2 = mesh->vertices.coord + nodes[in_facet]; - /****************************************************** - vertices insertion - ******************************************************/ - for (uint32_t i=0; i<localN; i++) - { - uint32_t passIndex = (localStart+i)%passLength; - uint32_t vta = nodeInfo[passStart + passIndex].node; - #ifdef HXT_DELAUNAY_MAX_MEMORY - HXTStatus status = doTheWork(mesh, - verticesID+((threadID*(mesh->vertices.num+SIMD_ALIGN/4)) & ~(SIMD_ALIGN/4 - 1)), - &Locals[threadID], - options->nodalSizes, - &curTet, - vta); - #else - HXTStatus status = doTheWork(mesh, verticesID, &Locals[threadID], options->nodalSizes, &curTet, vta); - #endif - - switch(status){ - case HXT_STATUS_TRYAGAIN: - // ; - if(nthreads==1){ - double* vtaCoord = vertices[vta].coord; - HXT_WARNING("skipping supposedly duplicate vertex (%f %f %f)", vtaCoord[0], vtaCoord[1], vtaCoord[2]); - nodeInfo[passStart + passIndex].status = HXT_STATUS_FALSE; - break; + if(curTet!=tetContainingVta) { + double dist = 0.0; + for (int l=0; l<3; l++) { + double meanCoord = (coord1[l]+coord2[l])*0.5; + double diff = meanCoord-vtaCoord[l]; + dist += diff*diff; } - case HXT_STATUS_FALSE: - case HXT_STATUS_TRUE: - nodeInfo[passStart + passIndex].status = status; - break; - default: // error other than HXT_STATUS_TRYAGAIN cause the program to return - nodeInfo[passStart + passIndex].status = HXT_STATUS_TRYAGAIN; - HXT_OMP_CHECK( status ); - break; - } - } - } - // } - - #pragma omp atomic update - threadFinished++; - int val = 0; - do{ - // threads are waiting here for a reallocation - synchronizeReallocation(mesh, &threadFinished, &val); - }while(val<nthreads); - // }while(val<maxThreads); + if(dist>distMax) { + dist = distMax; + tetToUndelete = curTet; + } + } + } while (curTet!=delTet); - // HXT_INFO("thread %d terminated", threadID, threadFinished, nthreads); - } + if(tetToUndelete==delTet) + exist = 0; - /****************************************************** - vertices that have to be tried again are put at the end - ******************************************************/ - // everything above i+shift is HXT_STATUS_TRYAGAIN - uint32_t shift = 0; - unsigned numSkipped = 0; - for (uint32_t i=passEnd; i>passStart;) - { - i--; - if(nodeInfo[i].status!=HXT_STATUS_TRYAGAIN){ - if(nodeInfo[i].status==HXT_STATUS_FALSE) - numSkipped++; - shift++; - } - else if(shift!=0) { - hxtNodeInfo tmp = nodeInfo[i]; - nodeInfo[i] = nodeInfo[i+shift]; - nodeInfo[i+shift] = tmp; + // printf("undeleting tetrahedron %lu\n", tetToUndelete); + mesh->tetrahedra.colors[tetToUndelete] = color; + HXT_CHECK( undeleteTetrahedron(local, mesh, vta, tetToUndelete) ); + } } } - - options->numVerticesInMesh += shift - numSkipped; - - percent = (shift-numSkipped)*100.0/(passes[p+1]-passes[p]-numSkipped); - totalNumSkipped += numSkipped; - - HXT_INFO_COND(options->verbosity>1, - "%3d thrd |%10u/%-10u-> %*.1f%-*c\t- mesh.nvert: %-10u", - nthreads, shift, passLength, MIN(8,n/2)+5, percent, 8-MIN(8,n/2),'%', options->numVerticesInMesh); - - passes[p] += shift; } } - /****************************************************** - Cleaning - ******************************************************/ - - HXT_CHECK( hxtRemoveDeleted(mesh) ); - - for (int i=0; i<maxThreads; i++){ - HXT_CHECK( hxtAlignedFree(&Locals[i].deleted) ); - HXT_CHECK( hxtAlignedFree(&Locals[i].bnd) ); - HXT_CHECK( hxtAlignedFree(&Locals[i].vertices) ); + for (uint64_t i=prevDeleted; i<local->deleted.num; i++) { + uint64_t delTet = local->deleted.tetID[i]; + mesh->tetrahedra.colors[delTet] = color; } - HXT_CHECK( hxtAlignedFree(&verticesID) ); - HXT_CHECK( hxtFree(&Locals) ); - - /*************************************************************** - if reordering allowed, remove vertices we could not insert - ***************************************************************/ - if(!noReordering && totalNumSkipped!=0){ - /* remove deleted vertices and change tetrahedra.node accordingly */ + return HXT_STATUS_OK; +} - uint32_t* numInserted; - HXT_CHECK( hxtAlignedMalloc(&numInserted, maxThreads*sizeof(uint32_t)) ); - uint32_t firstShifted = mesh->vertices.num - nToInsert; - uint32_t n = nToInsert; +/* this function does a Breadth-first search of the tetrahedra in the cavity + * it add those to local->deleted + * it also maintain a local->bnd array with all the information concerning the boundary of the cavity + */ +static inline HXTStatus diggingACavity(HXTMesh* mesh, TetLocal* local, uint64_t curTet, const uint32_t vta, int* edgeConstraint){ + // add tetrahedra to cavity + local->deleted.tetID[local->deleted.num++] = curTet; + markTetAsDeleted(mesh, curTet); + local->ball.num = 0; - // when a vertex was skipped, nodeInfo[i].status = HXT_STATUS_FALSE - #pragma omp parallel - { - // uint32_t localFirstSkipped = UINT32_MAX; + - // 1st: mark vertices with their corresponding status - #pragma omp for schedule(static) - for (uint32_t i=0; i<nToInsert; i++) { - uint32_t index = nodeInfo[i].node; - HXTStatus status = nodeInfo[i].status; - vertices[index].padding.status = status; - // if(status!=HXT_STATUS_TRUE && localFirstSkipped>i) - // localFirstSkipped = i; - } + for(uint64_t start=local->deleted.num-1; start < local->deleted.num; start++){ + uint64_t curTet = local->deleted.tetID[start]; + const uint64_t* __restrict__ curNeigh = mesh->tetrahedra.neigh + 4*curTet; + const uint32_t* __restrict__ curNode = mesh->tetrahedra.node + 4*curTet; - #pragma omp barrier - #pragma omp single - { - uint32_t i = 0; - while (vertices[firstShifted+i].padding.status==HXT_STATUS_TRUE) i++; + *edgeConstraint += isAnyEdgeConstrained(mesh, curTet)!=0; - firstShifted += i+1; - n -= i+1; - } + /* here we allocate enough space for the boundary (local->bnd), the cavity (local->deleted) and the vertices (local->vertices) */ + HXT_CHECK( reserveNewDeleted(local, 4) ); + HXT_CHECK( reserveNewBnd(local, 4) ); - uint32_t start = 0; - int threadID = omp_get_thread_num(); - numInserted[threadID] = 0; + // we unrolled the loop for speed (also because indices are not trivial, we would need a 4X4 array) - #pragma omp for schedule(static) - for (uint32_t i=0; i<n; i++) { - if(vertices[firstShifted+i].padding.status==HXT_STATUS_TRUE) - numInserted[threadID]++; + /* and here we push stuff to local->bnd or local->deleted, always keeping ghost tet at last place */ + uint64_t neigh = curNeigh[0]/4; + if(curNeigh[0]!=HXT_NO_ADJACENT && isTetDeleted(mesh, neigh)==0){ + if(isFacetConstrained(mesh, 4*curTet+0) || + tetInsphere(mesh, neigh*4, vta)<=0.0){ + bndPush(local, mesh->tetrahedra.flag[curTet] & 0xF, + curNode[1], curNode[2], curNode[3], curNeigh[0]); } - - for (int i=0; i<threadID; i++) { - start+=numInserted[i]; + else{ + HXT_CHECK( deletedPush(mesh, local, neigh) ); } - start += firstShifted-1; - - // 3rd: compute where each vertices will be - #pragma omp for schedule(static) - for (uint32_t i=0; i<n; i++) { - uint32_t oldStart = start; - - if(vertices[firstShifted+i].padding.status==HXT_STATUS_TRUE) - start++; + } - vertices[firstShifted+i].padding.index = oldStart; + neigh = curNeigh[1]/4; + if(curNeigh[1]!=HXT_NO_ADJACENT && isTetDeleted(mesh, neigh)==0){ + if(isFacetConstrained(mesh, 4*curTet+1) || + tetInsphere(mesh, neigh*4, vta)<=0.0){ + bndPush(local, (isFacetConstrained(mesh, 4*curTet+1)>>4) | + (isEdgeConstrained(mesh, curTet, 1 , 2)>>5) | + (isEdgeConstrained(mesh, curTet, 0 , 1)<<1) | + (isEdgeConstrained(mesh, curTet, 1 , 3)>>4), + curNode[2], curNode[0], curNode[3], curNeigh[1]); } - - // 4th: update tetrahedra.node accordingly - #pragma omp for - for (uint64_t i=0; i<4*mesh->tetrahedra.num; i++) { - uint32_t index = mesh->tetrahedra.node[i]; - if(index>=firstShifted && index!=HXT_GHOST_VERTEX) - mesh->tetrahedra.node[i] = vertices[index].padding.index; + else{ + HXT_CHECK( deletedPush(mesh, local, neigh) ); } } -HXT_ASSERT(totalNumSkipped==mesh->vertices.num - vertices[mesh->vertices.num-1].padding.index - 1); - - HXT_CHECK( hxtAlignedFree(&numInserted) ); - - // 5th: put vertices at the right indices - for (uint32_t i=firstShifted; i<mesh->vertices.num; i++) { - vertices[vertices[i].padding.index] = vertices[i]; + neigh = curNeigh[2]/4; + if(curNeigh[2]!=HXT_NO_ADJACENT && isTetDeleted(mesh, neigh)==0){ + if(isFacetConstrained(mesh, 4*curTet+2)|| + tetInsphere(mesh, neigh*4, vta)<=0.0){ + bndPush(local, (isFacetConstrained(mesh, 4*curTet+2)>>8) | + (isEdgeConstrained(mesh, curTet, 0 , 2)>>1) | + (isEdgeConstrained(mesh, curTet, 1 , 2)>>4) | + (isEdgeConstrained(mesh, curTet, 2 , 3)>>8), + curNode[0], curNode[1], curNode[3], curNeigh[2]); + } + else{ + HXT_CHECK( deletedPush(mesh, local, neigh) ); + } } - if(options->verbosity>1) - HXT_INFO("%u vertices removed (vertices not inserted in the mesh are removed when using hxtDelaunay)", totalNumSkipped); - else if(options->verbosity>0) - HXT_INFO("%u vertices removed", totalNumSkipped); - - - mesh->vertices.num = mesh->vertices.num - totalNumSkipped; - } - - if(options->verbosity>0){ - time = omp_get_wtime() - time; - long long int tetCreated = (long long int) mesh->tetrahedra.num - (long long int) numTetrahedra; - HXT_INFO("took %f second for %lld tetrahedra (%.3f Mtet./s)", - time, tetCreated , tetCreated/time/1000000.0); + neigh = curNeigh[3]/4; + if(curNeigh[3]!=HXT_NO_ADJACENT && isTetDeleted(mesh, neigh)==0){ + if(isFacetConstrained(mesh, 4*curTet+3) || + tetInsphere(mesh, neigh*4, vta)<=0.0){ + + bndPush(local, (isFacetConstrained(mesh, 4*curTet+3)>>12) | + (isEdgeConstrained(mesh, curTet, 1 , 3)>>6) | + (isEdgeConstrained(mesh, curTet, 0 , 3)>>1) | + (isEdgeConstrained(mesh, curTet, 2 , 3)>>8), + // there are 2 valid order for nodes: 1,0,2,3 and 0,2,1,3 + curNode[1], curNode[0], curNode[2], curNeigh[3]); + } + else{ + HXT_CHECK( deletedPush(mesh, local, neigh) ); + } + } } return HXT_STATUS_OK; } +/************************************************************** + * compute adjacencies with a matrix O(1) insertion and search + **************************************************************/ +#ifndef HXT_DELAUNAY_LOW_MEMORY +static inline HXTStatus computeAdjacenciesFast(HXTMesh* mesh, TetLocal* local, uint32_t* __restrict__ verticesID, const uint64_t blength){ + cavityBnd_t* __restrict__ bnd = local->ball.bnd; -static HXTStatus DelaunayOptionsInit(HXTMesh* mesh, - HXTDelaunayOptions* userOptions, - HXTDelaunayOptions* options, - hxtBbox* bbox){ -HXT_ASSERT(mesh!=NULL); +#ifndef NDEBUG + int ghost_is_there = 0; +#endif - if(userOptions!=NULL){ - options->bbox = userOptions->bbox; - options->nodalSizes = userOptions->nodalSizes; - options->verbosity = userOptions->verbosity; - options->numVerticesInMesh = userOptions->numVerticesInMesh; - } - else{ - // default parameters - options->bbox = NULL; - options->nodalSizes = NULL; - options->verbosity = 0; +HXT_ASSERT(((size_t) bnd)%SIMD_ALIGN==0); +HXT_ASSERT(((size_t) verticesID)%SIMD_ALIGN==0); - // count the number of vertices in the mesh - #pragma omp parallel for - for (uint32_t i=0; i<mesh->vertices.num; i++) { - mesh->vertices.coord[4*i+3] = 0.0; + #pragma omp simd aligned(verticesID,bnd:SIMD_ALIGN) + for (uint32_t i=0; i<blength; i++){ + verticesID[bnd[i].node[0]] = UINT32_MAX; + verticesID[bnd[i].node[1]] = UINT32_MAX; + if(bnd[i].node[2]!=HXT_GHOST_VERTEX){ + verticesID[bnd[i].node[2]] = UINT32_MAX; } + } - #pragma omp parallel for - for (uint64_t i=0; i<4*mesh->tetrahedra.num; i++) { - mesh->vertices.coord[4*mesh->tetrahedra.node[i]+3] = 1.0; + uint32_t npts = 1; + for (uint32_t i=0; i<blength; i++) + { + if(verticesID[bnd[i].node[0]]>npts){ + verticesID[bnd[i].node[0]] = npts++; + } + bnd[i].node[0] = verticesID[bnd[i].node[0]]; + if(verticesID[bnd[i].node[1]]>npts){ + verticesID[bnd[i].node[1]] = npts++; } + bnd[i].node[1] = verticesID[bnd[i].node[1]]; - uint32_t numVerticesInMesh = 0; - #pragma omp parallel for reduction(+:numVerticesInMesh) - for (uint32_t i=0; i<mesh->vertices.num; i++) { - numVerticesInMesh += mesh->vertices.coord[4*i+3]; + if(bnd[i].node[2]==HXT_GHOST_VERTEX){ + bnd[i].node[2] = 0; +#ifndef NDEBUG + ghost_is_there = 1; +#endif + } + else{ + if(verticesID[bnd[i].node[2]]>npts){ + verticesID[bnd[i].node[2]] = npts++; + } + bnd[i].node[2] = verticesID[bnd[i].node[2]]; } - options->numVerticesInMesh = numVerticesInMesh; } -HXT_ASSERT(options->numVerticesInMesh <= mesh->vertices.num); + HXT_ASSERT_MSG((npts-3+ghost_is_there)*2==blength, "Failed to compute adjacencies (f) %u (%u ghost) vertices and %u cavity boundaries", npts-1+ghost_is_there, ghost_is_there, blength); // symbol undefined - if(options->bbox==NULL){ - options->bbox = bbox; - hxtBboxInit(bbox); - HXT_CHECK( hxtBboxAdd(bbox, mesh->vertices.coord, mesh->vertices.num) ); + #pragma omp simd aligned(verticesID:SIMD_ALIGN) + for (uint32_t i=0; i<blength; i++) + { + local->Map[bnd[i].node[0]*32 + bnd[i].node[1]] = bnd[i].neigh + 3; + local->Map[bnd[i].node[1]*32 + bnd[i].node[2]] = bnd[i].neigh + 1; + local->Map[bnd[i].node[2]*32 + bnd[i].node[0]] = bnd[i].neigh + 2; } - // for the predicates to work - exactinit(options->bbox->max[0]-options->bbox->min[0], - options->bbox->max[1]-options->bbox->min[1], - options->bbox->max[2]-options->bbox->min[2]); + #pragma omp simd aligned(verticesID:SIMD_ALIGN) + for (uint32_t i=0; i<blength; i++) + { + mesh->tetrahedra.neigh[bnd[i].neigh + 1] = local->Map[bnd[i].node[2]*32 + bnd[i].node[1]]; + mesh->tetrahedra.neigh[bnd[i].neigh + 2] = local->Map[bnd[i].node[0]*32 + bnd[i].node[2]]; + mesh->tetrahedra.neigh[bnd[i].neigh + 3] = local->Map[bnd[i].node[1]*32 + bnd[i].node[0]]; + } return HXT_STATUS_OK; } +#endif +/************************************************************** + * compute adjacencies with a matrix O(n) insertion and search + **************************************************************/ +static inline HXTStatus computeAdjacenciesSlow(HXTMesh* mesh, TetLocal* local, const uint64_t start, const uint64_t blength){ -HXTStatus hxtDelaunay(HXTMesh* mesh, HXTDelaunayOptions* userOptions){ - HXTDelaunayOptions options; - hxtBbox bbox; - HXT_CHECK( DelaunayOptionsInit(mesh, userOptions, &options, &bbox) ); + uint64_t tlength = 0; + const uint64_t middle = blength*3/2; // 3N - const uint64_t nToInsert = mesh->vertices.num - options.numVerticesInMesh; + // N+2 point on the surface of the cavity + // 2N triangle on the surface of the cavity, x3 (4*0.5+1) data = 6N+9 uint64_t + // => enough place for the 3N edge x2 data = 6N uint64_t + uint64_t* Tmp = (uint64_t*) local->ball.bnd; + const unsigned index[4] = {2,3,1,2}; - hxtNodeInfo* nodeInfo; - HXT_CHECK( hxtAlignedMalloc(&nodeInfo, nToInsert*sizeof(hxtNodeInfo)) ); - - // we fill nodeInfo with the indices of each vertices to insert... - #pragma omp parallel for simd - for (uint32_t i=0; i<nToInsert; i++) { - nodeInfo[i].node = options.numVerticesInMesh + i; - // nodeInfo[i].status = HXT_STATUS_TRYAGAIN; - } + for (uint64_t i=0; i<blength; i++) + { + uint64_t curTet = local->deleted.tetID[start+ i]; + const uint32_t* __restrict__ Node = mesh->tetrahedra.node + 4*curTet; - hxtTetrahedraCompute(mesh, &options, nodeInfo, nToInsert, 0); + // pointer to the position of Node[0] in the Tmp array + for (unsigned j=0; j<3; j++) + { + // define the edge by the minimum vertex and the other + uint64_t key = ((uint64_t) Node[index[j]]<<32) + Node[index[j+1]]; - HXT_CHECK( hxtAlignedFree(&nodeInfo) ); + // linear searching/pushing into Tmp + uint64_t k; + for (k=0; k<tlength; k++) // this is the only nested loop... the one that cost it all + { + __assume_aligned(Tmp, SIMD_ALIGN); + if(Tmp[k]==key) + break; + } + + uint64_t curFace = 4*curTet+j+1; + // we did not found it + if(k==tlength){ + Tmp[tlength] = (key>>32) + (key<<32); + Tmp[middle + tlength] = curFace; + tlength++; + } + else{// we found the neighbour ! + uint64_t pairValue = Tmp[middle+k]; + mesh->tetrahedra.neigh[curFace] = pairValue; + mesh->tetrahedra.neigh[pairValue] = curFace; + tlength--; + if(k<tlength){// put the last entry in the one we just discovered + Tmp[k] = Tmp[tlength]; + Tmp[middle+k] = Tmp[middle + tlength]; + } + } + } + } + HXT_ASSERT_MSG(tlength==0, "Failed to compute adjacencies (s)"); // verify that all neighbor were found return HXT_STATUS_OK; } +/**************************************** + * filling back the cavity (DelaunayBall) + ****************************************/ +static inline HXTStatus fillingACavity(HXTMesh* mesh, TetLocal* local, uint32_t* __restrict__ verticesID, uint64_t* __restrict__ curTet, const uint32_t vta, const uint16_t color){ + uint64_t clength = local->deleted.num; + uint64_t blength = local->ball.num; -HXTStatus hxtDelaunaySteadyVertices(HXTMesh* mesh, HXTDelaunayOptions* userOptions, hxtNodeInfo* nodeInfo, uint64_t nToInsert){ - HXT_ASSERT(nodeInfo!=NULL); + uint64_t start = clength - blength; - HXTDelaunayOptions options; - hxtBbox bbox; - HXT_CHECK( DelaunayOptionsInit(mesh, userOptions, &options, &bbox) ); + // #pragma vector aligned + #pragma omp simd + for (uint64_t i=0; i<blength; i++) + { -HXT_ASSERT(options.numVerticesInMesh+nToInsert <= mesh->vertices.num); + __assume_aligned(local->deleted.tetID, SIMD_ALIGN); + __assume_aligned(local->ball.bnd, SIMD_ALIGN); + __assume_aligned(mesh->tetrahedra.colors, SIMD_ALIGN); + __assume_aligned(mesh->tetrahedra.flag, SIMD_ALIGN); + __assume_aligned(mesh->tetrahedra.node, SIMD_ALIGN); + __assume_aligned(mesh->tetrahedra.neigh, SIMD_ALIGN); + + const uint64_t newTet = local->deleted.tetID[i + start]; + uint32_t* __restrict__ Node = mesh->tetrahedra.node + 4*newTet; + mesh->tetrahedra.colors[newTet] = color; + mesh->tetrahedra.flag[newTet] = 0; + + /* we need to always put the ghost vertex at the fourth slot*/ + Node[0] = vta; + Node[1] = local->ball.bnd[i].node[0]; + Node[2] = local->ball.bnd[i].node[1]; + Node[3] = local->ball.bnd[i].node[2]; + + const uint64_t neigh = local->ball.bnd[i].neigh; + mesh->tetrahedra.neigh[4*newTet] = neigh; + + mesh->tetrahedra.flag[newTet] = local->ball.bnd[i].flag; + + // update neighbor's neighbor + mesh->tetrahedra.neigh[neigh] = 4*newTet; + + // we recycle neigh to contain newTet (used in computeAdjacencies) + local->ball.bnd[i].neigh = 4*newTet; + } +#ifndef HXT_DELAUNAY_LOW_MEMORY + if(blength<=58){ // N+2<=31 => N<=29 => 2N<=58 + #ifndef NDEBUG + HXT_CHECK( computeAdjacenciesFast(mesh, local, verticesID, blength) ); + #else + computeAdjacenciesFast(mesh, local, verticesID, blength); + #endif + } + else +#endif + { + #ifndef NDEBUG + HXT_CHECK(computeAdjacenciesSlow(mesh, local, start, blength) ); + #else + computeAdjacenciesSlow(mesh, local, start, blength); + #endif + } + + + + *curTet = local->deleted.tetID[start]; + local->deleted.num = start; - hxtTetrahedraCompute(mesh, &options, nodeInfo, nToInsert, 1); return HXT_STATUS_OK; } +/************************************************************* + * insert a single point + ************************************************************/ +static inline HXTStatus insertion(HXTMesh* mesh, + uint32_t* verticesID, + TetLocal* local, + const double* nodalSizes, + uint64_t* curTet, + const uint32_t vta, + int perfectlyDelaunay){ + const uint64_t prevDeleted = local->deleted.num; -/*********************************** - * insphere predicate & perturbation - ***********************************/ -// see Perturbations and Vertex Removal in a 3D Delaunay Triangulation, O. Devillers & M. Teillaud -static double symbolicPerturbation (uint32_t indices[5] , const double* __restrict__ i, - const double* __restrict__ j, - const double* __restrict__ k, - const double* __restrict__ l, - const double* __restrict__ m){ - - double const* pt[5] = {i,j,k,l,m}; + HXT_CHECK( walking2Cavity(mesh, local, curTet, vta) ); - // 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. - int swaps = 0; // Record the total number of swaps. - int n = 5; - int count; - do { - count = 0; - n = n - 1; - for (int iter = 0; iter < n; iter++) { - if (indices[iter] > indices[iter+1]) { + if(nodalSizes!=NULL && filterTet(mesh, nodalSizes, *curTet, vta)){ + return HXT_STATUS_FALSE; + } - const double *swappt = pt[iter]; - pt[iter] = pt[iter+1]; - pt[iter+1] = swappt; + const uint16_t color = mesh->tetrahedra.colors[*curTet]; + int edgeConstraint = 0; + HXTStatus status = diggingACavity(mesh, local, *curTet, vta, &edgeConstraint); - uint32_t sw = indices [iter]; - indices[iter] = indices[iter+1]; - indices[iter+1] = sw; - count++; - } - } - swaps += count; - } while (count > 0); // Continue if some points are swapped. - - double 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; + if(status==HXT_STATUS_INTERNAL){ + restoreDeleted(mesh, local, prevDeleted, color); + return HXT_STATUS_TRYAGAIN; + } + else{ + HXT_CHECK(status); } - - double oriB = -orient3d(pt[0], pt[2], pt[3], pt[4]); - if (oriB == 0.0) HXT_WARNING("Symbolic perturbation failed (2 superposed vertices ?)"); - // Flip the sign if there are odd number of swaps. - if ((swaps % 2) != 0) oriB = -oriB; - return oriB; -} + if(edgeConstraint) { + HXT_CHECK( respectEdgeConstraint(local, mesh, vta, color, prevDeleted) ); + } + // uint64_t face = 0; + // if(!perfectlyDelaunay && isStarShaped(local, mesh, vta, &face)==HXT_STATUS_INTERNAL) { + // restoreDeleted(mesh, local, prevDeleted, color); + // return HXT_STATUS_FALSE; + // } -// there could be multiple stuff to accelerate the predicate. first see if orient3D to 2 face is negative -// orient3D + orient3D with perpendicular at the vertex -// see if the distance is more than what the radius could be... visually, you can know -double tetInsphere(HXTMesh* mesh, const uint64_t curTet, const uint32_t vta){ - uint32_t* Node = mesh->tetrahedra.node + curTet; + // reshape the cavity if it is not star shaped + if(!perfectlyDelaunay) + HXT_CHECK( reshapeCavityIfNeeded(local, mesh, vta, prevDeleted) ); - const double* __restrict__ a = mesh->vertices.coord + 4*Node[0]; - const double* __restrict__ b = mesh->vertices.coord + 4*Node[1]; - const double* __restrict__ c = mesh->vertices.coord + 4*Node[2]; - const double* __restrict__ e = mesh->vertices.coord + 4*vta; + if(nodalSizes!=NULL && filterCavity(local, mesh, nodalSizes, vta)) { + restoreDeleted(mesh, local, prevDeleted, color); + return HXT_STATUS_FALSE; + } - if(Node[3]==HXT_GHOST_VERTEX){ - double det = orient3d(a,b,c,e); - if(det!=0.0){ - return det; - } + if(local->ball.num > local->deleted.num){ + uint64_t needed = MAX(DELETED_BUFFER_SIZE,local->ball.num)-local->deleted.num; - // we never go here, except when point are aligned on boundary - // HXT_INFO("insphere using opposite vertex"); - uint32_t oppositeNode = mesh->tetrahedra.node[mesh->tetrahedra.neigh[curTet+3]]; - double* const __restrict__ oppositeVertex = mesh->vertices.coord + 4*oppositeNode; - det = insphere(a,b,c,oppositeVertex,e); + uint64_t ntet; - if (det == 0.0) { - uint32_t nn[5] = {Node[0],Node[1],Node[2],oppositeNode,vta}; - // HXT_INFO("symbolic perturbation on boundary"); - det = symbolicPerturbation (nn, a,b,c,oppositeVertex,e); - + #pragma omp atomic capture + { ntet = mesh->tetrahedra.num; mesh->tetrahedra.num+=needed;} + + reserveNewTet(mesh); + reserveNewDeleted(local, needed); + + #pragma omp simd + for (uint64_t i=0; i<needed; i++){ + local->deleted.tetID[local->deleted.num+i] = ntet+i; + mesh->tetrahedra.flag[ntet+i] = 0; + markTetAsDeleted(mesh, ntet+i); } - return -det; + + local->deleted.num+=needed; } - double* const __restrict__ d = mesh->vertices.coord + 4*Node[3]; + HXT_CHECK( fillingACavity(mesh, local, verticesID, curTet, vta, color) ); - double det = insphere(a,b,c,d,e); - if (det == 0.0) { - uint32_t nn[5] = {Node[0],Node[1],Node[2],Node[3],vta}; - // HXT_INFO("symbolic perturbation"); - det = symbolicPerturbation (nn, a,b,c,d,e); - } - return det; + return HXT_STATUS_TRUE; } +/************************************************************* + * Delaunay triangulation of a set of points + ************************************************************/ +static HXTStatus parallelDelaunay3D(HXTMesh* mesh, + HXTDelaunayOptions* options, + hxtNodeInfo* nodeInfo, + const uint32_t nToInsert, + int noReordering) +{ + uint32_t totalNumSkipped = 0; + uint32_t seed = 1; + // third, divide indices in different passes + const int maxThreads = options->delaunayThreads; + const int perfectlyDelaunay = mesh->tetrahedra.num<=5; + uint32_t passes[12]; + unsigned npasses = computePasses(passes, options->numVerticesInMesh, nToInsert); + // that ugly cast because people want an array of double into the mesh structure + HXTVertex* vertices = (HXTVertex*) mesh->vertices.coord; + -/*********************************** - * walk to cavity - ***********************************/ + /****************************************************** + shuffle (and optimize cache locality) + ******************************************************/ + if(noReordering){ + // shuffle nodeInfo + HXT_CHECK( hxtNodeInfoShuffle(nodeInfo, nToInsert) ); + } + else { + HXT_INFO_COND(options->verbosity>1, "Reordering vertices from %u to %u", mesh->vertices.num - nToInsert, mesh->vertices.num); + HXTVertex* verticesToInsert = vertices + mesh->vertices.num - nToInsert; -static inline HXTStatus walking2Cavity(HXTMesh* mesh, TetLocal* local, uint64_t* __restrict__ curTet, const uint32_t vta){ - uint64_t nextTet = *curTet; + if(options->nodalSizes==NULL){ + // shuffle the vertices to insert, then sort each pass except the first according to the hilbert curve... + HXT_CHECK( hxtVerticesShuffle(verticesToInsert, nToInsert) ); + } + else{ + HXT_CHECK( hxtNodeInfoShuffle(nodeInfo, nToInsert) ); + } - /* if nextTet is a ghost triangle, go to the neighbor that is not a ghost triangle */ - if(mesh->tetrahedra.node[nextTet+3]==HXT_GHOST_VERTEX) - nextTet = mesh->tetrahedra.neigh[nextTet+3]&NEIGH_H; + uint32_t nbits = hxtAdvancedHilbertBits(options->bbox, options->minSizeStart, options->minSizeEnd, + options->numVerticesInMesh, + options->numVerticesInMesh + nToInsert, + options->numVerticesInMesh + nToInsert/4, + nToInsert/2, + maxThreads); + + HXT_CHECK( hxtVerticesHilbertDist(options->bbox, verticesToInsert, nToInsert, &nbits, NULL) ); - double* const vtaCoord = mesh->vertices.coord + 4*vta; - unsigned enteringFace = 4; + if(options->nodalSizes==NULL){ + for (unsigned i=options->numVerticesInMesh < SMALLEST_ROUND; i<npasses; i++) { + HXT_CHECK( hxtVerticesSort(verticesToInsert+passes[i], passes[i+1]-passes[i], nbits) ); + } + } + else{ + #pragma omp parallel for + for (int i=0; i<nToInsert; i++) { + nodeInfo[i].hilbertDist = verticesToInsert[i].padding.hilbertDist; + } -#ifdef DEBUG - uint64_t TotalCount = 0; -#endif - + for (unsigned i=options->numVerticesInMesh < SMALLEST_ROUND; i<npasses; i++) { + HXT_CHECK( hxtNodeInfoSort(nodeInfo+passes[i], passes[i+1]-passes[i], nbits) ); + } - while(1){ - const uint32_t* __restrict__ curNode = mesh->tetrahedra.node + nextTet; - const uint64_t* __restrict__ curNeigh = mesh->tetrahedra.neigh + nextTet; + const uint32_t nodalMin = mesh->vertices.num - nToInsert; + double* sizesToInsert = options->nodalSizes + nodalMin; - if (tetInsphere(mesh, nextTet, vta) > 0){ - *curTet = nextTet; - return HXT_STATUS_OK; + size_t vertSize = nToInsert*sizeof(HXTVertex); + size_t sizeSize = nToInsert*sizeof(double); + HXTVertex* vertCopy; + double* sizeCopy; + HXT_CHECK( hxtAlignedMalloc(&vertCopy, vertSize) ); + HXT_CHECK( hxtAlignedMalloc(&sizeCopy, sizeSize) ); + + #pragma omp parallel for + for (uint32_t i=0; i<nToInsert; i++) { + vertCopy[i] = verticesToInsert[nodeInfo[i].node-nodalMin]; + sizeCopy[i] = sizesToInsert[nodeInfo[i].node-nodalMin]; + nodeInfo[i].node = nodalMin + i; + } + + memcpy(verticesToInsert, vertCopy, vertSize); + memcpy(sizesToInsert, sizeCopy, sizeSize); + + HXT_CHECK( hxtAlignedFree(&vertCopy) ); + HXT_CHECK( hxtAlignedFree(&sizeCopy) ); } - #ifdef DEBUG - else if(curNode[3]==HXT_GHOST_VERTEX){ - return HXT_ERROR_MSG(HXT_STATUS_FAILED, "walked outside of the domain"); + } + + /****************************************************** + Initializations and allocations + ******************************************************/ + if(mesh->tetrahedra.num<5){ + HXT_INFO_COND(options->verbosity>0, + "Initialization of tet. mesh"); + HXT_CHECK( hxtTetrahedraInit(mesh, nodeInfo, nToInsert, options->verbosity) ); + options->numVerticesInMesh = 4; // not counting the ghost vertex + passes[0] = 4; + } + + + uint32_t* verticesID; +#ifdef HXT_DELAUNAY_LOW_MEMORY + verticesID = NULL; // we do not need it +#else + HXT_CHECK( hxtAlignedMalloc(&verticesID, mesh->vertices.num*sizeof(uint32_t)) ); +#endif + + TetLocal* Locals; + HXT_CHECK( hxtMalloc(&Locals, maxThreads*sizeof(TetLocal)) ); + // HXT_CHECK( hxtMalloc()) + + for (int i=0; i<maxThreads; i++) + localInit(&Locals[i]); + + + HXT_INFO_COND(options->verbosity>0, + "Delaunay of %10u vertices on %3d threads\t- mesh.nvert: %-10u", + passes[npasses] - passes[0], maxThreads, options->numVerticesInMesh); + + for (uint32_t p=0; p<npasses; p++) + { + + double percent = 200; + int nthreads = 1; + uint32_t tmp = (passes[p+1]-passes[p])/SMALLEST_ROUND; + while(tmp>0 && nthreads<maxThreads){ + tmp = tmp/2; + nthreads*=2; } - #endif + nthreads = MIN(nthreads, maxThreads); + // const uint32_t initialPassLength = passes[p+1] - passes[p]; - unsigned i; - unsigned neigh=4; - double Min = 0.0; - for (i=0; i<4; i++) + for(uint32_t n=0; passes[p+1]-passes[p]; n++) { - // we walk where the volume is minimum - const double* __restrict__ a = mesh->vertices.coord +4*curNode[(i+1)&3]; - const double* __restrict__ b = mesh->vertices.coord +4*curNode[(i&2)^3]; - const double* __restrict__ c = mesh->vertices.coord +4*curNode[(i+3)&2]; + const uint32_t passStart = passes[p]; + const uint32_t passEnd = passes[p+1]; + const uint32_t passLength = passEnd - passStart; + + /****************************************************** + choosing number of threads + ******************************************************/ + if(percent<140/nthreads || passLength<SMALLEST_ROUND){ + nthreads=1; + } + else if(percent<20){ + nthreads=(nthreads+1)/2; + } + else if(passLength < nthreads*SMALLEST_ROUND) + nthreads=(nthreads+1)/2; + + + /****************************************************** + Sorting vertices + ******************************************************/ + double hxtDeclareAligned bboxShift[4]={0.5,0.5,0.5,0}; + + if(percent<100 && nthreads>1) + { + bboxShift[0] = (double) hxtReproducibleLCG(&seed)/RAND_MAX; + bboxShift[1] = (double) hxtReproducibleLCG(&seed)/RAND_MAX; + bboxShift[2] = (double) hxtReproducibleLCG(&seed)/RAND_MAX; + bboxShift[3] = (double) hxtReproducibleLCG(&seed)/RAND_MAX; // this is not a bbox deformation, it's an index shift + } + + uint32_t nbits = hxtAdvancedHilbertBits(options->bbox, options->minSizeStart, options->minSizeEnd, + options->numVerticesInMesh - passStart, + options->numVerticesInMesh - passStart + nToInsert, + options->numVerticesInMesh, + passLength, + nthreads); + if(noReordering){ + HXT_CHECK( hxtVerticesHilbertDist(options->bbox, vertices, mesh->vertices.num, &nbits, bboxShift) ); + } + else{ + // TODO: do not recompute hilber dist of the whole mesh on 1 thread + HXT_CHECK( hxtVerticesHilbertDist(options->bbox, vertices, mesh->vertices.num - nToInsert + passEnd, &nbits, bboxShift) ); + } - if (i!=enteringFace) { - double val = orient3d(a,b,c, vtaCoord); - const uint32_t* __restrict__ neighNodes = mesh->tetrahedra.node + (curNeigh[i]&NEIGH_H); - - if (val < Min && checkTetrahedron((hxtVertex*) mesh->vertices.coord, local, neighNodes)==HXT_STATUS_OK){ - neigh = i; - Min = val; - // break; + + #pragma omp parallel for simd aligned(nodeInfo:SIMD_ALIGN) + for (uint32_t i=passStart; i<passEnd; i++) { + nodeInfo[i].hilbertDist = vertices[nodeInfo[i].node].padding.hilbertDist; + } + + if(p!=0 || n!=0 || nthreads>1 || options->numVerticesInMesh >= SMALLEST_ROUND){ + HXT_CHECK( hxtNodeInfoSort(nodeInfo + passStart, passLength, nbits) ); + } + + const uint32_t step = passLength/nthreads; + + uint32_t indexShift = MIN(step-1,(uint32_t) bboxShift[3]*step); + + int threadFinished = 0; + + #pragma omp parallel num_threads(nthreads) + { + #ifdef _MSC_VER + #pragma omp single + nthreads = omp_get_num_threads(); + #endif + + uint64_t curTet = 0; // we always begin with the first tet. (index 0) + const int threadID = omp_get_thread_num(); + + uint32_t localStart; + uint32_t localN; + int foundTet = 0; + + if(nthreads>1){ + // if(threadID<nthreads){ + + /****************************************************** + Making partitions + ******************************************************/ + localStart = step*threadID + indexShift; + uint64_t dist = nodeInfo[passStart + localStart].hilbertDist; + + uint32_t up = 1; + while(localStart+up<passLength && dist==nodeInfo[passStart + localStart + up].hilbertDist) + up++; + + localStart = localStart+up==passLength?0:localStart+up; + if(localStart > 0) + Locals[threadID].partition.startDist = (nodeInfo[passStart + localStart].hilbertDist + + nodeInfo[passStart + localStart - 1].hilbertDist + 1)/2; + else + Locals[threadID].partition.startDist = nodeInfo[passStart + passLength-1].hilbertDist + (nodeInfo[passStart + localStart].hilbertDist - nodeInfo[passStart + passLength - 1].hilbertDist)/2; + Locals[threadID].partition.first = localStart; + // } + + #pragma omp barrier + + // if(threadID<nthreads){ + uint32_t localEnd = Locals[(threadID+1)%nthreads].partition.first; + localN = (localEnd + passLength - localStart)%passLength; + + Locals[threadID].partition.endDist = Locals[(threadID+1)%nthreads].partition.startDist; + + // printf("%d) first dist: %lu, last dist: %lu startDist: %lu endDist: %lu\n", threadID, nodeInfo[passStart + localStart].hilbertDist, nodeInfo[(passStart + localStart + localN-1)%passLength].hilbertDist, Locals[threadID].partition.startDist, Locals[threadID].partition.endDist); + + + /****************************************************** + find starting tetrahedron + ******************************************************/ + + for (uint64_t i=0; i<mesh->tetrahedra.num; i++) + { + curTet = i; + if(isTetDeleted(mesh, i)==0 && + checkTetrahedron(vertices, &Locals[threadID], mesh->tetrahedra.node + curTet*4 )==HXT_STATUS_OK) + { + foundTet = 1; + break; + } + } + + if(options->reproducible){ + Locals[threadID].partition.startDist = 0; + Locals[threadID].partition.endDist = UINT64_MAX; + + // walk in total liberty toward the first point + HXTStatus status = walking2Cavity(mesh, &Locals[threadID], &curTet, nodeInfo[passStart + (localStart+localN/2)%passLength].node); + + if(status!=HXT_STATUS_OK && status!=HXT_STATUS_TRYAGAIN){ + HXT_OMP_CHECK( status ); + } + + Locals[threadID].partition.startDist = nodeInfo[passStart + localStart].hilbertDist; + Locals[threadID].partition.endDist = nodeInfo[passStart + localEnd].hilbertDist; + + if(checkTetrahedron(vertices, &Locals[threadID], mesh->tetrahedra.node + curTet*4 )!=HXT_STATUS_OK){ + foundTet = 0; + // check the neighbors + for (unsigned i=0; i<4; i++) { + uint64_t tet = mesh->tetrahedra.neigh[4*curTet+i]/4; + if(checkTetrahedron(vertices, &Locals[threadID], mesh->tetrahedra.node + tet*4 )==HXT_STATUS_OK){ + foundTet = 1; + curTet = tet; + break; + } + } + } + } + + // } + + #pragma omp barrier } + else + // if(threadID==0) + { + + /****************************************************** + single-thread partition and starting tetrahedron + ******************************************************/ + localStart = 0; + localN = passLength; + Locals[0].partition.startDist = 0; + Locals[0].partition.endDist = UINT64_MAX; + + for (uint64_t i=0; i<mesh->tetrahedra.num; i++) + { + if(isTetDeleted(mesh, i)==0){ + curTet = i; + foundTet = 1; + break; + } + } + } + + if (foundTet == 0) { + HXT_INFO_COND(options->verbosity>1, + "thread %d did not find any tetrahedron to begin with", threadID); + } + + // filtering vertices on the Moore curve + if(options->nodalSizes!=NULL) + { + double* p1 = NULL; + double p1Size = 0; + + for (uint32_t i=0; i<localN; i++) + { + uint32_t passIndex = (localStart+i)%passLength; + uint32_t lastNode = nodeInfo[passStart + passIndex].node; + if(nodeInfo[passStart + passIndex].status==HXT_STATUS_TRYAGAIN){ + double* p2 = vertices[lastNode].coord; + double p2Size = options->nodalSizes[lastNode]; + if(p1!=NULL && pointIsTooClose(p1, p2, 0.5*(p1Size+p2Size))!=HXT_STATUS_OK){ + nodeInfo[passStart + passIndex].status=HXT_STATUS_FALSE; + } + else{ + p1 = p2; + p1Size = p2Size; + } + } + } + } + + + // if(threadID<nthreads){ + if(foundTet!=0){ + + /****************************************************** + vertices insertion + ******************************************************/ + for (uint32_t i=0; i<localN; i++) + { + uint32_t passIndex = (localStart+i)%passLength; + uint32_t vta = nodeInfo[passStart + passIndex].node; + if(nodeInfo[passStart + passIndex].status==HXT_STATUS_TRYAGAIN){ + HXTStatus status = insertion(mesh, verticesID, &Locals[threadID], options->nodalSizes, &curTet, vta, perfectlyDelaunay); + + switch(status){ + case HXT_STATUS_TRYAGAIN: + // ; + if(nthreads==1){ + double* vtaCoord = vertices[vta].coord; + HXT_WARNING("skipping supposedly duplicate vertex (%f %f %f)", vtaCoord[0], vtaCoord[1], vtaCoord[2]); + nodeInfo[passStart + passIndex].status = HXT_STATUS_FALSE; + break; + } + case HXT_STATUS_FALSE: + case HXT_STATUS_TRUE: + nodeInfo[passStart + passIndex].status = status; + break; + default: // error other than HXT_STATUS_TRYAGAIN cause the program to return + nodeInfo[passStart + passIndex].status = HXT_STATUS_TRYAGAIN; + HXT_OMP_CHECK( status ); + break; + } + } + else{ + nodeInfo[passStart + passIndex].status = HXT_STATUS_FALSE; + } + } + } + // } + + #pragma omp atomic update + threadFinished++; + + int val = 0; + do{ + // threads are waiting here for a reallocation + HXT_OMP_CHECK( synchronizeReallocation(mesh, &threadFinished, &val) ); + }while(val<nthreads); + // }while(val<maxThreads); } - } - if(neigh==4){ - // if(tetInsphere(mesh, nextTet, vta) > 0){ - // *curTet = nextTet; - // return HXT_STATUS_OK; - // } - return HXT_STATUS_TRYAGAIN; + /****************************************************** + vertices that have to be tried again are put at the end + ******************************************************/ + // everything above i+shift is HXT_STATUS_TRYAGAIN + uint32_t shift = 0; + unsigned numSkipped = 0; + for (uint32_t i=passEnd; i>passStart;) + { + i--; + if(nodeInfo[i].status!=HXT_STATUS_TRYAGAIN){ + if(nodeInfo[i].status==HXT_STATUS_FALSE) + numSkipped++; + shift++; + } + else if(shift!=0) { + hxtNodeInfo tmp = nodeInfo[i]; + nodeInfo[i] = nodeInfo[i+shift]; + nodeInfo[i+shift] = tmp; + } + } + + options->numVerticesInMesh += shift - numSkipped; + + percent = (shift-numSkipped)*100.0/MAX(1,passLength-numSkipped); + totalNumSkipped += numSkipped; + + HXT_INFO_COND(options->verbosity>1, + "%3d thrd |%10u/%-10u-> %*.1f%-*c\t- mesh.nvert: %-10u", + nthreads, shift-numSkipped, passLength-numSkipped, MIN(8,n/2)+5, percent, 8-MIN(8,n/2),'%', options->numVerticesInMesh); + + passes[p] += shift; } + } - // printf("nextTet %u %g %u %u\n",nextTet,Min, count, neigh); - nextTet = curNeigh[neigh]&NEIGH_H; - enteringFace = curNeigh[neigh]&3; - - #ifdef DEBUG - if(TotalCount>mesh->tetrahedra.num){ - return HXT_ERROR_MSG(HXT_STATUS_FAILED, "infinite walk to find the cavity"); + /****************************************************** + Cleaning + ******************************************************/ + #pragma omp parallel num_threads(maxThreads) + { + const int threadID = omp_get_thread_num(); + for (uint64_t i=0; i<Locals[threadID].deleted.num; i++) { + for (int j=0; j<4; j++) { + mesh->tetrahedra.neigh[4*Locals[threadID].deleted.tetID[i]+j] = HXT_NO_ADJACENT; + } } - // printf("%lu\n",TotalCount); - TotalCount++; - #endif } -} + HXT_CHECK( hxtRemoveDeleted(mesh) ); + for (int i=0; i<maxThreads; i++){ + HXT_CHECK( hxtAlignedFree(&Locals[i].deleted.tetID) ); + HXT_CHECK( hxtAlignedFree(&Locals[i].ball.bnd) ); + } + HXT_CHECK( hxtAlignedFree(&verticesID) ); + HXT_CHECK( hxtFree(&Locals) ); -/*********************************** - * digging cavity - ***********************************/ + /*************************************************************** + if reordering allowed, remove vertices we could not insert + ***************************************************************/ + if(!noReordering && totalNumSkipped!=0){ + /* remove deleted vertices and change tetrahedra.node accordingly */ -/* pushing boundary information to local->bnd */ -static inline void bndPush( TetLocal* local, const uint32_t vta, - const uint32_t node1, const uint32_t node2, - const uint32_t node3, const uint64_t otherside){ - uint64_t n = local->numBnd; - local->bnd[n].node[0] = vta; - local->bnd[n].node[1] = node1; - local->bnd[n].node[2] = node2; - local->bnd[n].node[3] = node3; - local->bnd[n].otherside = otherside; - local->numBnd++; -} + uint32_t* numInserted; + HXT_CHECK( hxtAlignedMalloc(&numInserted, omp_get_max_threads()*sizeof(uint32_t)) ); -static inline HXTStatus deletedPush(HXTMesh* mesh, TetLocal* local, const uint64_t neigh){ - // check if 3 points of the new tetrahedra are owned by this thread - HXT_CHECK( checkTetrahedron((hxtVertex*) mesh->vertices.coord, local, mesh->tetrahedra.node + (neigh&NEIGH_H)) ); + uint32_t firstShifted = mesh->vertices.num - nToInsert; + uint32_t n = nToInsert; - local->deleted[local->numDeleted++] = neigh&NEIGH_H; - local->vertices[local->numVertices++] = mesh->tetrahedra.node[neigh]; + // when a vertex was skipped, nodeInfo[i].status = HXT_STATUS_FALSE + #pragma omp parallel + { + // 1st: mark vertices with their corresponding status + #pragma omp for schedule(static) + for (uint32_t i=0; i<nToInsert; i++) { + uint32_t index = nodeInfo[i].node; + HXTStatus status = nodeInfo[i].status; + vertices[index].padding.status = status; + }// implicit barrier here - return HXT_STATUS_OK; -} + #pragma omp single + { + uint32_t i = 0; + while (vertices[firstShifted+i].padding.status==HXT_STATUS_TRUE) i++; + firstShifted += i+1; + n -= i+1; + }// implicit barrier here -//TODO: when applying colors, we need to verify that the cavity is convex... everytime !! -/* this function does a Breadth-first search of the tetrahedra in the cavity - * it add those to local->deleted - * it also maintain a local->bnd array with all the information concerning the boundary of the cavity - */ -static inline HXTStatus diggingACavity(HXTMesh* mesh, TetLocal* local, uint64_t curTet, const uint32_t vta, const uint16_t color){ - // add triangle to cavity - uint64_t start; - uint16_t* __restrict__ allColors = mesh->tetrahedra.colors; - - local->deleted[local->numDeleted++] = curTet; - allColors[curTet>>2]=HXT_DELETED_COLOR; - - // add the vertices to the list - local->vertices[0] = mesh->tetrahedra.node[curTet + 0]; - local->vertices[1] = mesh->tetrahedra.node[curTet + 1]; - local->vertices[2] = mesh->tetrahedra.node[curTet + 2]; - local->vertices[3] = mesh->tetrahedra.node[curTet + 3]; - local->numVertices = 4; - local->numBnd = 0; + uint32_t start = 0; + int threadID = omp_get_thread_num(); + numInserted[threadID] = 0; - + #pragma omp for schedule(static) + for (uint32_t i=0; i<n; i++) { + if(vertices[firstShifted+i].padding.status==HXT_STATUS_TRUE) + numInserted[threadID]++; + }// implicit barrier here - for(start=local->numDeleted-1; start < local->numDeleted; start++){ - uint64_t curTet = local->deleted[start]; - const uint64_t* __restrict__ curNeigh = mesh->tetrahedra.neigh + curTet; - const uint32_t* __restrict__ curNode = mesh->tetrahedra.node + curTet; - + for (int i=0; i<threadID; i++) { + start+=numInserted[i]; + } + start += firstShifted-1; - /* here we allocate enough space for the boundary (local->bnd), the cavity (local->deleted) and the vertices (local->vertices) */ - HXT_CHECK( reserveNewDeleted(local, local->numDeleted+4) ); - HXT_CHECK( reserveNewBnd(local, local->numBnd+4) ); - HXT_CHECK( reserveNewVertices(local, local->numVertices+4) ); + // 3rd: compute where each vertices will be + #pragma omp for schedule(static) + for (uint32_t i=0; i<n; i++) { + uint32_t oldStart = start; - // we unrolled the loop for speed (also because indices are not trivial, we would need a 4X4 array) + if(vertices[firstShifted+i].padding.status==HXT_STATUS_TRUE) + start++; - /* and here we push stuff to local->bnd or local->deleted, always keeping ghost tet at last place */ - uint64_t neigh = curNeigh[0]>>2; - if(allColors[neigh]!=HXT_DELETED_COLOR){ - if( - // allColors[neigh] != color || - tetInsphere(mesh, neigh*4, vta)<=0.0){ - bndPush(local, vta, curNode[1], curNode[2], curNode[3], curNeigh[0]); - } - else{ - HXT_CHECK( deletedPush(mesh, local, curNeigh[0]) ); - allColors[neigh] = HXT_DELETED_COLOR; + // index and status are at the same location (it's a union) we cannot put this above the "if" ! + vertices[firstShifted+i].padding.index = oldStart; } - } - neigh = curNeigh[1]>>2; - if(allColors[neigh]!=HXT_DELETED_COLOR){ - if( - // allColors[neigh] != color || - tetInsphere(mesh, neigh*4, vta)<=0.0){ - bndPush(local, vta, curNode[2], curNode[0], curNode[3], curNeigh[1]); - } - else{ - HXT_CHECK( deletedPush(mesh, local, curNeigh[1]) ); - allColors[neigh] = HXT_DELETED_COLOR; + // 4th: update tetrahedra.node accordingly + #pragma omp for + for (uint64_t i=0; i<4*mesh->tetrahedra.num; i++) { + uint32_t index = mesh->tetrahedra.node[i]; + if(index>=firstShifted && index!=HXT_GHOST_VERTEX) + mesh->tetrahedra.node[i] = vertices[index].padding.index; } } - neigh = curNeigh[2]>>2; - if(allColors[neigh]!=HXT_DELETED_COLOR){ - if( - // allColors[neigh] != color || - tetInsphere(mesh, neigh*4, vta)<=0.0){ - bndPush(local, vta, curNode[0], curNode[1], curNode[3], curNeigh[2]); - } - else{ - HXT_CHECK( deletedPush(mesh, local, curNeigh[2]) ); - allColors[neigh] = HXT_DELETED_COLOR; - } - } + HXT_CHECK( hxtAlignedFree(&numInserted) ); - neigh = curNeigh[3]>>2; - if(allColors[neigh]!=HXT_DELETED_COLOR){ - if( - // allColors[neigh] != color || - tetInsphere(mesh, neigh*4, vta)<=0.0){ - // curNode[0] is the biggest, the smallest can be curNode[1] or curNode[2] - // if(curNode[1]<curNode[2]) - // bndPush(local, vta, curNode[0], curNode[2], curNode[1], curNeigh[3]); - // else - bndPush(local, vta, curNode[1], curNode[0], curNode[2], curNeigh[3]); - } - else{ - HXT_CHECK( deletedPush(mesh, local, curNeigh[3]) ); - allColors[neigh] = HXT_DELETED_COLOR; + // 5th: put vertices at the right indices + for (uint32_t i=firstShifted; i<mesh->vertices.num; i++) { + if(options->nodalSizes!=NULL){ + options->nodalSizes[vertices[i].padding.index] = options->nodalSizes[i]; } + vertices[vertices[i].padding.index] = vertices[i]; } - } - - return HXT_STATUS_OK; -} - - -/*********************************** - * filling back the cavity - ***********************************/ -// N+2 point on the surface of the cavity -// 2N triangle on the surface of the cavity, x3 (4*0.5+1) data = 6N+9 uint64_t -// => enough place for the 3N edge x2 data = 6N uint64_t -static inline HXTStatus computeAdjacenciesFast(HXTMesh* mesh, TetLocal* local, uint32_t* __restrict__ verticesID, const uint64_t start, const uint64_t blength){ - uint32_t* __restrict__ localVertices = local->vertices; -HXT_ASSERT(((size_t) verticesID)%SIMD_ALIGN==0); - - uint32_t i; - #pragma omp simd aligned(verticesID,localVertices:SIMD_ALIGN) - for (i=0; i<local->numVertices; i++){ - verticesID[localVertices[i]+1] = UINT32_MAX; - } + if(options->verbosity>1) + HXT_INFO("%u vertices removed (vertices not inserted in the mesh are removed when using hxtDelaunay)\n", totalNumSkipped); - uint32_t npts = 0; - for (i=0; i<local->numVertices; i++) - { - if(verticesID[localVertices[i]+1]>npts){ - // printf("vertice %u (located at %u in verticesID) has got number %u\n",localVertices[i],localVertices[i]+1, npts); - verticesID[localVertices[i]+1] = npts++; - } + mesh->vertices.num = mesh->vertices.num - totalNumSkipped; } - HXT_ASSERT_MSG((npts-2)*2==blength, "Failed to compute adjacencies (f)"); - - #pragma omp simd aligned(verticesID:SIMD_ALIGN) - for (i=0; i<blength; i++) - { - local->Map[verticesID[local->bnd[i].node[1]+1]*32 + verticesID[local->bnd[i].node[2]+1]] = local->bnd[i].otherside + 3; - local->Map[verticesID[local->bnd[i].node[2]+1]*32 + verticesID[local->bnd[i].node[3]+1]] = local->bnd[i].otherside + 1; - local->Map[verticesID[local->bnd[i].node[3]+1]*32 + verticesID[local->bnd[i].node[1]+1]] = local->bnd[i].otherside + 2; - } + HXT_INFO_COND(options->verbosity>0, "Delaunay done !%10u skipped", totalNumSkipped); + HXT_INFO_COND(options->verbosity>1, "mem. allocated:%5.2fGB - mesh.ntet: %-12lu - mesh.nvert: %-10lu", + ((50 + 2*(mesh->tetrahedra.flag!=NULL)) * mesh->tetrahedra.size + + (32 + 8*(options->nodalSizes!=NULL)) * mesh->vertices.size)/(1024.*1024.*1024.), + mesh->tetrahedra.num, mesh->vertices.num); - #pragma omp simd aligned(verticesID:SIMD_ALIGN) - for (i=0; i<blength; i++) - { - mesh->tetrahedra.neigh[local->bnd[i].otherside + 1] = local->Map[verticesID[local->bnd[i].node[3]+1]*32 + verticesID[local->bnd[i].node[2]+1]]; - mesh->tetrahedra.neigh[local->bnd[i].otherside + 2] = local->Map[verticesID[local->bnd[i].node[1]+1]*32 + verticesID[local->bnd[i].node[3]+1]]; - mesh->tetrahedra.neigh[local->bnd[i].otherside + 3] = local->Map[verticesID[local->bnd[i].node[2]+1]*32 + verticesID[local->bnd[i].node[1]+1]]; + if(options->reproducible && maxThreads!=1){ + HXT_INFO_COND(options->verbosity>1, "Reordering tetrahedra (reproducible==true)\n", mesh->vertices.num - nToInsert, mesh->vertices.num); + HXT_CHECK( hxtTetReorder(mesh) ); } return HXT_STATUS_OK; } -static inline HXTStatus computeAdjacenciesSlow(HXTMesh* mesh, TetLocal* local, const uint64_t start, const uint64_t blength){ - - uint64_t tlength = 0; - const uint64_t middle = blength*3/2; // 3N - - uint64_t* Tmp = (uint64_t*) local->bnd; // we know there is enough space... - const unsigned index[4] = {2,3,1,2}; +/***************************************** + * complete the HXTDelaunayOptions struct + * when there are missing fields. + ****************************************/ +static HXTStatus DelaunayOptionsInit(HXTMesh* mesh, + HXTDelaunayOptions* userOptions, + HXTDelaunayOptions* options, + HXTBbox* bbox){ +HXT_ASSERT(mesh!=NULL); - uint64_t i; - for (i=0; i<blength; i++) - { - uint64_t curTet = local->deleted[start+ i]; - const uint32_t* __restrict__ Node = mesh->tetrahedra.node + curTet; + if(userOptions!=NULL){ + options->bbox = userOptions->bbox; + options->nodalSizes = userOptions->nodalSizes; + options->verbosity = userOptions->verbosity; + options->minSizeStart = MAX(0.0, userOptions->minSizeStart); + options->minSizeEnd = MAX(options->minSizeStart, userOptions->minSizeEnd); + options->numVerticesInMesh = userOptions->numVerticesInMesh; + options->delaunayThreads = userOptions->delaunayThreads; + options->reproducible = userOptions->reproducible; + } + else{ + HXTVertex* vertices = (HXTVertex*) mesh->vertices.coord; - // printf("===Node: %u %u %u %u\n",Node[0],Node[1],Node[2],Node[3]); + // default parameters + options->bbox = NULL; + options->nodalSizes = NULL; + options->minSizeStart = 0.0; + options->minSizeEnd = 0.0; + options->verbosity = 1; + options->delaunayThreads = 0; + options->reproducible = 0; - // pointer to the position of Node[0] in the Tmp array - uint64_t j; - for (j=0; j<3; j++) - { - // define the edge by the minimum vertex and the other - uint64_t key = ((uint64_t) Node[index[j]]<<32) + Node[index[j+1]]; - curTet++; + // count the number of vertices in the mesh + #pragma omp parallel for + for (uint32_t i=0; i<mesh->vertices.num; i++) { + vertices[i].padding.index = 0; + } - // linear searching/pushing into Tmp - uint64_t k; - for (k=0; k<tlength; k++) // this is the only nested loop... the one that cost it all - { - __assume_aligned(Tmp, SIMD_ALIGN); - if(Tmp[k]==key) - break; - } + #pragma omp parallel for + for (uint64_t i=0; i<mesh->tetrahedra.num; i++) { + vertices[mesh->tetrahedra.node[4*i+0]].padding.index = 1; + vertices[mesh->tetrahedra.node[4*i+1]].padding.index = 1; + vertices[mesh->tetrahedra.node[4*i+2]].padding.index = 1; + if(mesh->tetrahedra.node[4*i+3]!=HXT_GHOST_VERTEX) + vertices[mesh->tetrahedra.node[4*i+3]].padding.index = 1; + } - // we did not found it - if(k==tlength){ - // printf("pushing key %lu=(%u<<32) + %u (tet %lu, %lu = %lu)\n",key,Node[index[j]],Node[node[j+1]], curTet&NEIGH_H, j+1, curTet); - Tmp[tlength] = (key>>32) + (key<<32); - Tmp[middle + tlength] = curTet; - tlength++; - } - else{// we found the neighbour ! - uint64_t pairValue = Tmp[middle+k]; - // printf("found key %lu (tet %lu, %lu = %lu) neigh:%lu\n",key, curTet&NEIGH_H, j+1, curTet, pairValue); - mesh->tetrahedra.neigh[curTet] = pairValue; - mesh->tetrahedra.neigh[pairValue] = curTet; - tlength--; - if(k<tlength){// put the last entry in the one we just discovered - Tmp[k] = Tmp[tlength]; - Tmp[middle+k] = Tmp[middle + tlength]; - } - } + uint32_t numVerticesInMesh = 0; + #pragma omp parallel for reduction(+:numVerticesInMesh) + for (uint32_t i=0; i<mesh->vertices.num; i++) { + numVerticesInMesh += vertices[i].padding.index; } + + options->numVerticesInMesh = numVerticesInMesh; } - HXT_ASSERT_MSG(tlength==0, "Failed to compute adjacencies (s)"); // verify that all neighbor were found - return HXT_STATUS_OK; -} +HXT_ASSERT(options->numVerticesInMesh <= mesh->vertices.num); -static inline HXTStatus fillingACavity(HXTMesh* mesh, TetLocal* local, uint32_t* __restrict__ verticesID, uint64_t* __restrict__ curTet, const uint16_t color){ - uint64_t clength = local->numDeleted; - uint64_t blength = local->numBnd; + if(options->bbox==NULL){ + options->bbox = bbox; + hxtBboxInit(bbox); + HXT_CHECK( hxtBboxAdd(bbox, mesh->vertices.coord, mesh->vertices.num) ); + } - uint64_t start = clength - blength; + if(options->delaunayThreads==0) + options->delaunayThreads = omp_get_max_threads(); + else if(options->delaunayThreads<0) + options->delaunayThreads = omp_get_num_procs(); + + if(options->delaunayThreads>omp_get_thread_limit()) + options->delaunayThreads = omp_get_thread_limit(); - // #pragma vector aligned - #pragma omp simd - for (uint64_t i=0; i<blength; i++) - { + // for the predicates to work + exactinit(options->bbox->max[0]-options->bbox->min[0], + options->bbox->max[1]-options->bbox->min[1], + options->bbox->max[2]-options->bbox->min[2]); - __assume_aligned(local->deleted, SIMD_ALIGN); - __assume_aligned(local->bnd, SIMD_ALIGN); - __assume_aligned(mesh->tetrahedra.colors, SIMD_ALIGN); - __assume_aligned(mesh->tetrahedra.node, SIMD_ALIGN); - __assume_aligned(mesh->tetrahedra.neigh, SIMD_ALIGN); + return HXT_STATUS_OK; +} - const uint64_t newTet = local->deleted[i + start]; - uint32_t* __restrict__ Node = mesh->tetrahedra.node + newTet; - mesh->tetrahedra.colors[newTet>>2] = color; - /* we need to always put the ghost vertex at the fourth slot*/ - Node[0] = local->bnd[i].node[0]; - Node[1] = local->bnd[i].node[1]; - Node[2] = local->bnd[i].node[2]; - Node[3] = local->bnd[i].node[3]; +/***************************************** + * parallel Delaunay + * see header for a complete description + ****************************************/ +HXTStatus hxtDelaunay(HXTMesh* mesh, HXTDelaunayOptions* userOptions){ + HXTDelaunayOptions options; + HXTBbox bbox; + HXT_CHECK( DelaunayOptionsInit(mesh, userOptions, &options, &bbox) ); - const uint64_t otherside = local->bnd[i].otherside; - mesh->tetrahedra.neigh[newTet] = otherside; - // update neighbor's neighbor - mesh->tetrahedra.neigh[otherside] = newTet; + const uint32_t nToInsert = mesh->vertices.num - options.numVerticesInMesh; - // we recycle otherside to contain newTet (used in computeAdjacencies) - local->bnd[i].otherside = newTet; - } -#ifndef HXT_DELAUNAY_LOW_MEMORY - if(blength<=60){ // N+2<=32 => N<=30 => 2N<=60 - #ifdef DEBUG - HXT_CHECK( computeAdjacenciesFast(mesh, local, verticesID, start, blength) ); - #else - computeAdjacenciesFast(mesh, local, verticesID, start, blength); - #endif - } - else -#endif - { - #ifdef DEBUG - HXT_CHECK(computeAdjacenciesSlow(mesh, local, start, blength) ); - #else - computeAdjacenciesSlow(mesh, local, start, blength); - #endif + if(options.reproducible && nToInsert<2048) // not worth launching threads and having to reorder tets after... + options.delaunayThreads = 1; + + hxtNodeInfo* nodeInfo; + HXT_CHECK( hxtAlignedMalloc(&nodeInfo, nToInsert*sizeof(hxtNodeInfo)) ); + + // we fill nodeInfo with the indices of each vertices to insert... + #pragma omp parallel for simd + for (uint32_t i=0; i<nToInsert; i++) { + nodeInfo[i].node = options.numVerticesInMesh + i; + nodeInfo[i].status = HXT_STATUS_TRYAGAIN; // necessary for when foundTet = 0; } - *curTet = local->deleted[start]; - local->numDeleted = start; + HXT_CHECK( parallelDelaunay3D(mesh, &options, nodeInfo, nToInsert, 0) ); + + HXT_CHECK( hxtAlignedFree(&nodeInfo) ); return HXT_STATUS_OK; } -/************************************************************************** - * VERIFYING A MESH * - **************************************************************************/ -// static HXTStatus hxtTetrahedraVerifyInternal(HXTMesh* mesh, TetLocal* Locals, int nthreads) -// { -// unsigned numErrors = 0; -// if(mesh->tetrahedra.num > mesh->tetrahedra.size){ -// HXT_WARNING("number of tetrahedra exceed the size of the array"); -// numErrors++; -// } -// uint64_t i; -// // #pragma omp parallel for -// for (i=0; i<mesh->tetrahedra.num; i++) -// { - -// uint32_t* Node = mesh->tetrahedra.node + i*4; -// uint64_t* Neigh = mesh->tetrahedra.neigh + i*4; - - -// int deleted = 0; -// for (int threadID=0; threadID<nthreads; threadID++) -// { -// int thisDeleted = 0; -// for (uint64_t j=0; j<Locals[threadID].numDeleted; j++) -// { -// if(i*4==Locals[threadID].deleted[j]){ -// thisDeleted++; -// } -// } - -// if(thisDeleted>0){ -// if(thisDeleted>1){ -// HXT_WARNING("tet. %lu is deleted multiple time in thread %d", i*4, threadID); -// numErrors++; -// } -// deleted++; -// } -// } - -// if(deleted>0){ -// if(deleted>1){ -// HXT_WARNING("tet. %lu is deleted in mutliple threads",i*4); -// numErrors++; -// } - -// int flag = mesh->tetrahedra.colors[i] == HXT_DELETED_COLOR; -// // verify that the flag is set according to the deleted stuff -// if(flag!=deleted){ -// HXT_WARNING("tet. %lu 's flag say it is %sdeleted but it is %sin deleted list", i*4, (flag)?"":"not ", (deleted)?"":"not "); -// numErrors++; -// } - -// continue; -// } - -// if(Node[0]>mesh->vertices.num || Node[1]>mesh->vertices.num || Node[2]>mesh->vertices.num -// || (Node[3]!=HXT_GHOST_VERTEX && Node[3]>mesh->vertices.num)){ -// HXT_WARNING("ghost vertex at wrong place in tet. %lu (%u %u %u %u)", i*4, Node[0], Node[1], Node[2], Node[3]); -// numErrors++; -// } - - -// double* a = mesh->vertices.coord + 4*Node[0]; -// double* b = mesh->vertices.coord + 4*Node[1]; -// double* c = mesh->vertices.coord + 4*Node[2]; -// double* d; -// if(Node[3]!=HXT_GHOST_VERTEX) -// d = mesh->vertices.coord + 4*Node[3]; - -// if(Node[3]!=HXT_GHOST_VERTEX && orient3d(a,b,c,d)<0.0){ -// HXT_WARNING("orientation of tet %lu is wrong",i*4); -// numErrors++; -// } - -// // check the neighbors -// unsigned j; -// for (j=0; j<4; j++) -// { -// uint64_t neigh = Neigh[j]; - -// // verify that neigh is not deleted -// if(mesh->tetrahedra.colors[neigh/4] == HXT_DELETED_COLOR){ -// HXT_WARNING("tet %lu has got a deleted tet as its %uth neighbor", i*4, j); -// numErrors++; -// } - -// // uint64_t* NeighNeigh = mesh->tetrahedra.neigh + neigh; -// uint32_t* NeighNode = mesh->tetrahedra.node + (neigh&NEIGH_H); - -// if(mesh->tetrahedra.neigh[neigh]!=i*4+j){ -// HXT_WARNING("tet %lu is not the neighbor of its %uth neighbor", i*4,j); -// numErrors++; -// } - -// uint32_t V[3] = { Node[((j+1)&3)], Node[((j&2)^3)], Node[((j+3)&2)]}; -// unsigned l; -// for (l=0; l<3; l++) -// { -// if(NeighNode[(((neigh&3)+1)&3)]==V[l] && NeighNode[(((neigh&3)&2)^3)]==V[(l+2)%3] && NeighNode[(((neigh&3)+3)&2)]==V[(l+1)%3]) -// break; -// } - -// if(l==3){ -// HXT_WARNING("neighbor %u of tet. %lu doesn't contain 3 common vertices",j,i*4); -// numErrors++; -// } - -// if(Node[j]==NeighNode[neigh&3]){ -// HXT_WARNING("neighbor %u of tet. %lu contain the same non-common vertex... (WTF?)"); -// numErrors++; -// } - -// if(Node[3]!=HXT_GHOST_VERTEX && NeighNode[neigh&3]!=HXT_GHOST_VERTEX && insphere(mesh->vertices.coord + 4*Node[0], -// mesh->vertices.coord + 4*Node[1], -// mesh->vertices.coord + 4*Node[2], -// mesh->vertices.coord + 4*Node[3], -// mesh->vertices.coord + 4*NeighNode[neigh&3]].coord)>0.0){ -// HXT_WARNING("neighbor %u (%lu) of tet %lu has it's non-common node in the sphere (insphere(%u %u %u %u %u)=%.12g>0)",j, neigh, i*4, Node[0], Node[1], Node[2], Node[3], NeighNode[neigh&3],insphere(mesh->vertices.coord + 4*Node[0], -// mesh->vertices.coord + 4*Node[1], -// mesh->vertices.coord + 4*Node[2], -// mesh->vertices.coord + 4*Node[3], -// mesh->vertices.coord + 4*NeighNode[neigh&3])); -// numErrors++; -// } -// } -// } - -// if(numErrors){ -// return HXT_ERROR_MSG(HXT_STATUS_ERROR, "hxtTetrahedraVerifyInternal detected %d errors", numErrors); -// } - -// return HXT_STATUS_OK; -// } - -// HXTStatus hxtTetrahedraVerify(HXTMesh* mesh, TetLocal* local, int nthreads) -HXTStatus hxtTetrahedraVerify(HXTMesh* mesh) -{ - volatile int errorOccured = 0; - - #pragma omp parallel for - for (uint64_t i=0; i<mesh->tetrahedra.num; i++) - { - - if(errorOccured) - continue; - - uint32_t* Node = mesh->tetrahedra.node + i*4; - uint64_t* Neigh = mesh->tetrahedra.neigh + i*4; - - if(mesh->tetrahedra.colors[i] == HXT_DELETED_COLOR) - continue; - - // check for good placement of ghost vertex - if(Node[0]==HXT_GHOST_VERTEX || Node[1]==HXT_GHOST_VERTEX || Node[2]==HXT_GHOST_VERTEX){ - HXT_ERROR_MSG(HXT_STATUS_ERROR, "ghost vertex at wrong place in tet. %lu", i*4); - errorOccured=1; - continue; - } - - double* a = mesh->vertices.coord + 4*Node[0]; - double* b = mesh->vertices.coord + 4*Node[1]; - double* c = mesh->vertices.coord + 4*Node[2]; - - // check for the orientation - // if(Node[3]==0){ - // HXT_WARNING("ghost tet. %lu remains in the array (did you clean the mesh?)",i*4); - // } - // else - if(Node[3]!=HXT_GHOST_VERTEX && orient3d(a,b,c,mesh->vertices.coord + 4*Node[3])<0.0){ - HXT_ERROR_MSG(HXT_STATUS_ERROR, "orientation of tet %lu is wrong",i*4); - errorOccured=1; - continue; - } +/************************************************ + * parallel Delaunay without moving the vertices + * see header for a complete description + ***********************************************/ +HXTStatus hxtDelaunaySteadyVertices(HXTMesh* mesh, HXTDelaunayOptions* userOptions, hxtNodeInfo* nodeInfo, uint64_t nToInsert){ +HXT_ASSERT(nodeInfo!=NULL); - // check the neighbors - unsigned j; - for (j=0; j<4; j++) - { - uint64_t neigh = Neigh[j]; - // uint64_t* NeighNeigh = mesh->tetrahedra.neigh + neigh; - uint32_t* NeighNode = mesh->tetrahedra.node + (neigh&NEIGH_H); - - if(mesh->tetrahedra.neigh[neigh]!=i*4+j){ - HXT_ERROR_MSG(HXT_STATUS_ERROR, "tet %lu is not the neighbor of its %uth neighbor", i*4,j); - errorOccured=1; - continue; - } + HXTDelaunayOptions options; + HXTBbox bbox; + HXT_CHECK( DelaunayOptionsInit(mesh, userOptions, &options, &bbox) ); - uint32_t V[3] = { Node[((j+1)&3)], Node[((j&2)^3)], Node[((j+3)&2)]}; - unsigned l; - for (l=0; l<3; l++) - { - if(NeighNode[(((neigh&3)+1)&3)]==V[l] && NeighNode[(((neigh&3)&2)^3)]==V[(l+2)%3] && NeighNode[(((neigh&3)+3)&2)]==V[(l+1)%3]) - break; - } + if(options.reproducible && nToInsert<2048) // not worth launching threads and having to reorder tets after... + options.delaunayThreads = 1; - if(l==3){ - HXT_ERROR_MSG(HXT_STATUS_ERROR, "neighbor %u of tet. %lu doesn't contain 3 common vertices",j,i*4); - errorOccured=1; - continue; - } +HXT_ASSERT(options.numVerticesInMesh+nToInsert <= mesh->vertices.num); - if(Node[3]!=HXT_GHOST_VERTEX && NeighNode[neigh&3]!=HXT_GHOST_VERTEX && insphere(mesh->vertices.coord + 4*Node[0], - mesh->vertices.coord + 4*Node[1], - mesh->vertices.coord + 4*Node[2], - mesh->vertices.coord + 4*Node[3], - mesh->vertices.coord + 4*NeighNode[neigh&3])>0.0){ - HXT_ERROR_MSG(HXT_STATUS_ERROR, "neighbor %u of tet %lu has it's non-common node in the sphere (insphere(%u %u %u %u %u)>0)",j,i*4, Node[0], Node[1], Node[2], Node[3], NeighNode[neigh&3]); - errorOccured=1; - continue; - } - } - } + HXT_CHECK( parallelDelaunay3D(mesh, &options, nodeInfo, nToInsert, 1) ); - if(errorOccured) - return HXT_STATUS_ERROR; return HXT_STATUS_OK; } + diff --git a/contrib/hxt/hxt_tetrahedra.h b/contrib/hxt/hxt_tetrahedra.h index c4769f625c6837b4d667c304a824d32a565ffd74..5511dd5b9359c3d760330006935156493e4e888c 100644 --- a/contrib/hxt/hxt_tetrahedra.h +++ b/contrib/hxt/hxt_tetrahedra.h @@ -8,68 +8,103 @@ extern "C" { #include "hxt_mesh.h" #include "hxt_vertices.h" -/* reserve place for tetrahedra assuming ntet tetrahedra will be added to the triangulation - * it is efficient to give ntet = ~10 times the number of vertices to be added - * as in average, the number of tetrahedra is ~6.5x greater than the number of vertices +/** +* \file tetrahedra.h Delaunay tetrahedrization +* \author Célestin Marot +*/ + +/** + * \struct HXTDelaunayOptions + * + * Options for the Delaunay functions hxtDelaunay() and hxtDelaunaySteadyVertices() + * + * */ -HXTStatus hxtTetrahedraReserve(HXTMesh* mesh, uint64_t ntet); - -/* verify the consistency of a tetrahedral mesh */ -HXTStatus hxtTetrahedraVerify(HXTMesh* mesh); - - - -typedef struct HXTDelaunayOptions_struct{ - hxtBbox* bbox; // if NULL, the bbox is recomputed internally - // else it mush contain the bounding box of all vertices - - double* nodalSizes; // if NULL, doesn't restrict nodalSize - // else contains the minimum mesh size at each vertex - // if the insertion of a vertex create an edge smaller than - // the average nodalSize of its endpoints, the vertex is not inserted - - uint32_t numVerticesInMesh; // the number of vertices in the mesh - - int verbosity; // verbosity=0: don't print information - // verbosity=1: print basic information on each pass - // verbosity=2: print everything +typedef struct { + HXTBbox* bbox; /**< The bounding box for all vertices. + * - if bbox==NULL, the bbox is recomputed internally; + * - if bbox!=NULL, bbox must contain all vertices */ + + double* nodalSizes; /**< + * - if nodalSize==NULL, doesn't restrict nodalSize; + * - if nodalSize!=NULL, nodalSize contains the minimum + * mesh size at each vertex.\n + * If the insertion of a vertex create an edge smaller than + * the average nodalSize of its endpoints, the vertex is + * not inserted + * \warning a segmentation fault will occur if a vertex + * doesn't have a corresponding mesh size */ + + double minSizeStart; /**< estimate of the minimum mesh size at the moment of the call. + * 0 if the mesh is empty or if the distribution is uniform + * (the mesh size is then guessed with the number of point) */ + double minSizeEnd; /**< estimate of the minimum mesh size when all points are inserted in the Delaunay. + * 0 if the distribution is uniform + * (the mesh size is then guessed with the number of point) */ + + uint32_t numVerticesInMesh; /**< The number of vertices in the mesh */ + + int verbosity; /**< + * - if verbosity<=0: don't print information. + * - if verbosity==1: print basic information on each pass + * - if verbosity>=2: print everything */ + + int reproducible; /**< If reproducible!=0, the Delaunay use a reproducible tetrahedra order + * in order to be totally deterministic. + * \warning this takes time ! + * It requires a total reordering of tetrahedra at the end to get a reproducible order\n + * except if `delaunayThreads==1 || (delaunayThreads==0 && omp_get_max_threads()==1)`\n + * in which case it is reproducible anyway */ + + int delaunayThreads; /**< number of threads for the delaunay insertion + * - if delaunayThreads==0, it will use omp_get_max_threads() + * - if delaunayThreads<0, it will uses omp_get_num_procs() */ } HXTDelaunayOptions; -/* insert only vertices of index nodeInfo[i].nodes (does not change ordering of vertices !) +/** + * \brief Delaunay of a set of vertices that does not modify their order + * \details This perform the insertion of the vertices whose indices are + * given in nodeInfo (in the \ref hxtNodeInfo.node wtructure member)\n + * This function does not change the order of vertices in the mesh.\n + * \ref hxtNodeInfo.status will be modified by the function to tell + * if the vertex was successfully inserted or not. + * - nodeInfo[i].status==HXT_STATUS_TRUE if the vertex was successfully inserted. + * - nodeInfo[i].status==HXT_STATUS_FALSE if the vertex was not inserted. + * - nodeInfo[i].status==HXT_STATUS_TRYAGAIN if an error occured before the vertex could be inserted + * + * \warning + * - the order of nodeInfo will change + * - hxtNodeInfo[i].hilbertDist will change + * - mesh->tetrahedra.* will change + * - mesh->vertices.coord[4*i+3] will change * - * this function modifies - * mesh->tetrahedra.* - * mesh->vertices[*].dist - * hxtNodeInfo ordering will change - * hxtNodeInfo[*].index will change - * hxtNodeInfo[*].status = HXT_STATUS_TRUE if the vertex was successfully inserted - * hxtNodeInfo[*].status = HXT_STATUS_FALSE if the vertex was not inserted - (insertion would break the nodalSize or the vertex is already in the mesh) - * hxtNodeInfo[*].status = HXT_STATUS_TRYAGAIN if an error occured before the vertex could be inserted + * \param mesh: a valid Delaunay mesh + * \param options: options to give to the Delaunay algorithm \ref HXTDelaunayOptions + * \param[in, out] nodeInfo: the indices of the vertices to insert in the tetrahedral mesh. + * \param nToInset: the number of element in nodeInfo, hence the number of vertices to insert. */ HXTStatus hxtDelaunaySteadyVertices(HXTMesh* mesh, HXTDelaunayOptions* options, hxtNodeInfo* nodeInfo, uint64_t nToInsert); -/* insert vertices from numVerticesInMesh to mesh->vertices.num +/** + * \brief Delaunay of a set of vertices + * \details This perform the insertion of the vertices + * from numVerticesInMesh to mesh->vertices.num\n * - * this function modifies - * mesh->tetrahedra.* - * mesh->vertices[*].dist - * mesh->vertices can be reordered - * vertices that could not be inserted are deleted from mesh->vertices ! - * (their insertion would break the nodalSize or the vertex is a duplicate) + * \warning + * - the order of mesh->vertices will change + * - hxtNodeInfo[i].hilbertDist will change + * - mesh->tetrahedra.* will change + * - mesh->vertices.coord[4*i+3] will change + * - vertices that could not be inserted are deleted from mesh->vertices ! * - * DO NOT USE THIS FUNCTION IF OTHER STUFF DEPENDS ON THE VERTICES TO INSERT - * - * However, this version is a little bit faster... + * \param mesh: a valid Delaunay mesh + * \param options: options to give to the Delaunay algorithm \ref HXTDelaunayOptions */ HXTStatus hxtDelaunay(HXTMesh* mesh, HXTDelaunayOptions* options); -/* remove tetrahedra whose color are HXT_DELETED_COLOR */ -HXTStatus hxtRemoveDeleted(HXTMesh* mesh); - #ifdef __cplusplus } #endif diff --git a/contrib/hxt/hxt_tools.h b/contrib/hxt/hxt_tools.h index 11421d57af31c4855666b84a7db4778a074b10b8..f9fcab01d11697ac56198b5295aea839c8ea2c22 100644 --- a/contrib/hxt/hxt_tools.h +++ b/contrib/hxt/hxt_tools.h @@ -14,13 +14,7 @@ extern "C" { /* define SIMD ALIGNMENT */ #ifndef SIMD_ALIGN -#ifdef __AVX512F__ #define SIMD_ALIGN 64 -#elif defined(__AVX2__) -#define SIMD_ALIGN 32 -#else -#define SIMD_ALIGN 16 /* we align to 16 anyway even if no sse */ -#endif #endif // declare alignement of pointer allocated on the stack or in a struct @@ -52,7 +46,7 @@ ___ | int arrayLength = ...; | int *array; | HXT_CHECK( hxtMalloc(&array, sizeof(int)*arrayLength) ); -| +| | array[0] = ...; | [...] | array[arrayLength-1] = ...; @@ -104,15 +98,6 @@ static inline HXTStatus hxtRealloc(void* ptrToPtr, size_t size) } -// FIXME Gmsh: aligned routines do not seem to work on 32 bit machines -#include <stdint.h> -#if UINTPTR_MAX == 0xffffffff -static inline HXTStatus hxtGetAlignedBlockSize(void* ptrToPtr, size_t* size){ *size = 0; return HXT_STATUS_OK; } -static inline HXTStatus hxtAlignedMalloc(void* ptrToPtr, size_t size){ return hxtMalloc(ptrToPtr, size); } -static inline HXTStatus hxtAlignedFree(void* ptrToPtr){ return hxtFree(ptrToPtr); } -static inline HXTStatus hxtAlignedRealloc(void* ptrToPtr, size_t size){ return hxtRealloc(ptrToPtr, size); } -#else - /********************************************************* * Hextreme aligned malloc implementation *********************************************************/ @@ -152,7 +137,7 @@ static inline HXTStatus hxtAlignedMalloc(void* ptrToPtr, size_t size) p2 = (char**)(((size_t)(pstart) + startOffset) & ~(SIMD_ALIGN - 1)); // only keep bits ge SIMD_ALIGN p2[-1] = pstart; p2[-2] = pstart + size; - p2[-3] = pstart + (size ^ 0xBF58476D1CE4E5B9ULL); // makes a verification possible + p2[-3] = (char*) ((size_t) pstart + (size ^ 0xBF58476D1CE4E5B9ULL)); // makes a verification possible *(void**)ptrToPtr = p2; return HXT_STATUS_OK; @@ -187,15 +172,15 @@ static inline HXTStatus hxtAlignedRealloc(void* ptrToPtr, size_t size) HXT_CHECK(hxtAlignedFree(ptrToPtr)); return HXT_STATUS_OK; } - + size_t old_size; HXT_CHECK( hxtGetAlignedBlockSize(ptrToPtr, &old_size) ); if(size>old_size || size+4096<old_size){ // we do not shrink block to gain less than 4096 bytes void* newptr = NULL; HXT_CHECK( hxtAlignedMalloc(&newptr, size)); - - memcpy(newptr, *(void**)ptrToPtr, size>old_size?old_size:size); + if(old_size) + memcpy(newptr, *(void**)ptrToPtr, size>old_size?old_size:size); HXT_CHECK( hxtAlignedFree(ptrToPtr) ); @@ -205,7 +190,22 @@ static inline HXTStatus hxtAlignedRealloc(void* ptrToPtr, size_t size) return HXT_STATUS_OK; } -#endif // FIXME Gmsh +/********************************************************* + A way to call rand with a seed to get a reproducible + result. + For example, we do not call srand() each time we + call a reproducible Delaunay, else if someone was calling + rand(); Delaunay(); rand(); ... + he would always get the same result. We use + hxtReproducibleRand() instead + + !!!! 1st seed must absolutely be 1 !!!! +**********************************************************/ +static inline uint32_t hxtReproducibleLCG(uint32_t *seed) +{ + *seed = 69621*(*seed)%2147483647; + return *seed; +} /********************************************************* * Matrix operations diff --git a/contrib/hxt/hxt_vertices.c b/contrib/hxt/hxt_vertices.c index c3b482dc41cb3fe6d8e37975e3885e9cf72b55b6..5e1a15770c824339d67a198efa43c45e3932662d 100644 --- a/contrib/hxt/hxt_vertices.c +++ b/contrib/hxt/hxt_vertices.c @@ -3,6 +3,11 @@ #include "hxt_vertices.h" #include "hxt_sort.h" +/** +* \file hxt_vertices.c see header hxt_vertices.h. +* \author Célestin Marot +*/ + /* create a nextbefore macro * for a strictly positive value, round to previous double value */ #if (defined (__STD_VERSION__) && (__STD_VERSION__ >= 199901L)) // nextafter only from C99 @@ -33,12 +38,10 @@ * * multiple of 11: 0 11 22 33 44 55 66 * from this, we get that it's better to use 0 9 21 33 42 54 63 bit - * corresponding to a nbr of iteration of 0 3 7 11 14 18 21 - * - * note that 2 */ + * corresponding to a nbr of iteration of 0 3 7 11 14 18 21 */ uint32_t hxtAdvisedHilbertBits(const uint32_t n) { - uint32_t nlog2 = u32_log2(n*n*n)/2; // if we have n points, we want approximately n^1.5 possible hilbert coordinates + uint32_t nlog2 = u32_log2(n)*3/2; // if we have n points, we want approximately n^1.5 possible hilbert coordinates if(n < HXT_SORT_SEQUENTIAL_LIMIT){ return (nlog2+7)/8*8/3*3; @@ -48,20 +51,39 @@ uint32_t hxtAdvisedHilbertBits(const uint32_t n) } } -#define ALPHA_FACTOR // we want a minimum of 16 blocks per partitions^2 // we also want to have at least 16 blocks per vertex to sort -uint32_t hxtAdvancedHilbertBits(const uint32_t nInitial, const uint32_t nToSort, const uint32_t nPartitions, const uint32_t maxPartitions){ - uint32_t nlog2 = u32_log2((nToSort+nInitial/64)*nPartitions*SIMD_ALIGN/64*maxPartitions/128); +uint32_t hxtAdvancedHilbertBits(HXTBbox* bbox, double sizeStart, double sizeEnd, uint32_t numStart, uint32_t numEnd, uint32_t numInMesh, uint32_t numToSort, uint32_t nthreads){ + uint32_t logNthreads = u32_log2(nthreads+1); + double k1 = 0.065*(logNthreads+2); // how much the number of iteration depends on the mesh size + const double k2 = 0.38; // how much the number of iteration depends on the number of point to sort + uint32_t num = numInMesh + numToSort/2; + + if(sizeEnd==0.0){ // we have to guess the mesh size + // k1 *= (2./3.); // minimum mesh size in uniform distribution + k1 *= (1./3.); // grid-like + } + else{ + double meanBBoxSize = ((bbox->max[0] - bbox->min[0]) + + (bbox->max[1] - bbox->min[1]) + + (bbox->max[2] - bbox->min[2]))/3.0; - if(nToSort < HXT_SORT_SEQUENTIAL_LIMIT){ - return ((nlog2+7)/8*8)/3*3; - } - else{ - return MIN(63,((nlog2+10)/11*11)/3*3); - } - return nlog2; + double k1End = (double) u64_log2((uint64_t) (meanBBoxSize/sizeEnd))/u32_log2(numEnd); + + if(sizeStart==0.0){ + k1 *= k1End; + } + else{ + double alpha = (double) (num - numStart)/(numEnd-numStart + .1); + double k1Start = (double) u64_log2((uint64_t) (meanBBoxSize/sizeStart))/u32_log2(numStart); + + k1 *= (1-alpha)*k1Start + alpha*k1End; + } + } + + num = u32_log2(num)*k1 + u32_log2(numToSort)*k2; + return MIN(21, num)*3; } /*================================= compute the hilberts distance =========================================== @@ -69,29 +91,30 @@ uint32_t hxtAdvancedHilbertBits(const uint32_t nInitial, const uint32_t nToSort, * This part is for computing the hilbert coordinates of the vertices (X,Y,Z) * shift[i] must be between 0 and 1 */ -HXTStatus hxtVerticesHilbertDist(hxtBbox* bbox, hxtVertex* vertices, const uint32_t n, uint32_t* nbits, const double* shift) +HXTStatus hxtVerticesHilbertDist(HXTBbox* bbox, HXTVertex* vertices, const uint32_t n, uint32_t* nbits, const double* shift) { HXT_ASSERT(vertices!=NULL); HXT_ASSERT(bbox!=NULL); +HXT_ASSERT(nbits!=NULL); HXT_ASSERT_MSG(bbox->min[0]<bbox->max[0] || bbox->min[1]<bbox->max[1] || bbox->min[2]<bbox->max[2],"wrong bounding box"); - - uint32_t level; - if(nbits==NULL || *nbits==0){ - *nbits = hxtAdvisedHilbertBits(n); - level = (*nbits + 2)/3; - *nbits = level*3; + + if(*nbits>63){ + *nbits = 63; } - else if(*nbits>63){ - level = 21; - *nbits = level*3; + else if(*nbits==0) { + #pragma omp parallel for simd + for (uint32_t i=0; i<n; i++) + vertices[i].padding.hilbertDist = 0; + + return HXT_STATUS_OK; } else{ - level = (*nbits + 2)/3; - *nbits = level*3; + *nbits = (*nbits+2)/3*3; } + const uint32_t level = *nbits/3; const double defaultShift[3] = {0.5,0.5,0.5}; if(shift==NULL) @@ -99,6 +122,14 @@ HXT_ASSERT_MSG(bbox->min[0]<bbox->max[0] || /* this was a beautifull check of the parameters... */ + // if(level==0){ + // #pragma omp parallel for + // for (uint32_t i=0; i<n; i++) + // vertices[i].padding.hilbertDist = 0; + + // return HXT_STATUS_OK; + // } + double hxtDeclareAligned div1[3]; double hxtDeclareAligned div2[3]; double hxtDeclareAligned mean[3]; @@ -120,8 +151,6 @@ HXT_ASSERT_MSG(bbox->min[0]<bbox->max[0] || } - - // const uint32_t invGCTable[8] = {0,1,3,2,7,6,4,5}; #pragma omp parallel for simd @@ -246,30 +275,30 @@ HXT_ASSERT_MSG(bbox->min[0]<bbox->max[0] || } -static inline uint64_t getVertexDist64(hxtVertex* const __restrict__ v, const void* user_data) +static inline uint64_t getVertexDist64(HXTVertex* const __restrict__ v, const void* user_data) { return v->padding.hilbertDist; } -static HXTStatus hxtVerticesSort64(hxtVertex* const __restrict__ vertices, const uint32_t n, const uint64_t distMax) +static HXTStatus hxtVerticesSort64(HXTVertex* const __restrict__ vertices, const uint32_t n, const uint64_t distMax) { - HXTSORT64_UNIFORM(hxtVertex, vertices, n, distMax, getVertexDist64, NULL); + HXTSORT64_UNIFORM(HXTVertex, vertices, n, distMax, getVertexDist64, NULL); return HXT_STATUS_OK; } -static inline uint32_t getVertexDist32(hxtVertex* const __restrict__ v, const void* user_data) +static inline uint32_t getVertexDist32(HXTVertex* const __restrict__ v, const void* user_data) { return v->padding.hilbertDist; } // TODO: test if it's really worth it to have a 32 bit version -static HXTStatus hxtVerticesSort32(hxtVertex* const __restrict__ vertices, const uint32_t n, const uint32_t distMax){ - HXTSORT32_UNIFORM(hxtVertex, vertices, n, distMax, getVertexDist32, NULL); +static HXTStatus hxtVerticesSort32(HXTVertex* const __restrict__ vertices, const uint32_t n, const uint32_t distMax){ + HXTSORT32_UNIFORM(HXTVertex, vertices, n, distMax, getVertexDist32, NULL); return HXT_STATUS_OK; } -HXTStatus hxtVerticesSort(hxtVertex* const __restrict__ vertices, const uint32_t n, uint32_t nbits) +HXTStatus hxtVerticesSort(HXTVertex* const __restrict__ vertices, const uint32_t n, uint32_t nbits) { HXT_ASSERT(vertices!=NULL); if(nbits>64){ @@ -347,7 +376,7 @@ static inline uint32_t fastHash(uint32_t x) { } /* for the non-static function, use a 22 bit key and a sort with two pass so we don't need to copy */ -HXTStatus hxtVerticesShuffle(hxtVertex* const __restrict__ vertices, const uint32_t n){ +HXTStatus hxtVerticesShuffle(HXTVertex* const __restrict__ vertices, const uint32_t n){ #pragma omp parallel for simd for (uint32_t i=0; i<n; i++){ vertices[i].padding.hilbertDist = fastHash(i); diff --git a/contrib/hxt/hxt_vertices.h b/contrib/hxt/hxt_vertices.h index 9fff40894e73a93562563d993c2698dcb183fd6b..2550e8034838290c128a21f333075cb113887a8c 100644 --- a/contrib/hxt/hxt_vertices.h +++ b/contrib/hxt/hxt_vertices.h @@ -4,42 +4,107 @@ #include "hxt_mesh.h" #include "hxt_bbox.h" -typedef struct hxtVertexStruct{ +/** +* \file hxt_vertices.h +* Compute Hilbert/Moore indices of an array of vertices and sort them. +* \author Célestin Marot +*/ + +/** + * \struct HXTVertex + * \brief Contain coordinates and a padding that can be used as a temporary value. + * + * We can cast mesh->vertices.coord to a HXTVertex + * in order to use the 4th coordinate as a temporary value + * of different types. It is used for sorting and for the Delaunay partitions. + */ +typedef struct { double coord[3]; union { uint64_t hilbertDist; uint32_t index; HXTStatus status; } padding; -} hxtVertex; +} HXTVertex; -typedef struct hxtNodeInfoStruct{ + +/** + * \struct hxtNodeInfo + * \brief Simple structure with a vertex index, a Hilbert/Moore coordinate and a status + * + * Simple structure with a vertex index, a Hilbert/Moore coordinate and a status + * For example, when vertices cannot be moved, we sort that structure instead. + */ +typedef struct { uint64_t hilbertDist; uint32_t node; HXTStatus status; // is the vertex inserted ? true, false or try_again } hxtNodeInfo; + +/** + * \brief Evaluate the number of bits for the Hilbert/Moore indices + * \param n: number of points + * \return number of bits of Hilbert/Moore indices for a good enough resolution of points coordinates + * + * \details This function is aimed to work with Delaunay insertion. + * Given a number of points from a supposedly almost uniform distribution, + * this function guess what is the level of the Hilbert/Moore curve + * needed such that the Delaunay insertion performs well. + */ uint32_t hxtAdvisedHilbertBits(const uint32_t n); -uint32_t hxtAdvancedHilbertBits(const uint32_t nInitial, const uint32_t nToSort, const uint32_t nPartitions, const uint32_t maxPartitions); -/* compute the hilbert distance of each vertex in vertex.dist - * @in bbox: the bounding box countaining the min and max values - * @in vertices: an array of vertices corresponding to the bbox - * @in n: the number of vertices - * @in nbits: hint on the number of bits on which the hilbert distance must be computed - * use 0 if you want the function to find a good number of bits for you - * @out nbits: the real number of bits on which the hilbert distance was computed +/** + * Same as hxtAdvisedHilbertBits() but with much more information + * \param bbox: the bounding box of the mesh + * \param sizeStart: estimated mesh size at a previous time Tstart + * \param sizeEnd: estimated mesh size in the future time Tend + * \param numStart: number of vertices in the mesh at Tstart + * \param numStart: number of vertices in the mesh at Tend + * \param numInMesh: number of vertices in the mesh now + * \param numToSort: number of point to insert/sort at this insertion step */ -HXTStatus hxtVerticesHilbertDist(hxtBbox* bbox, hxtVertex* vertices, const uint32_t n, uint32_t* nbits, const double shift[3]); +uint32_t hxtAdvancedHilbertBits(HXTBbox* bbox, double sizeStart, double sizeEnd, uint32_t numStart, uint32_t numEnd, uint32_t numInMesh, uint32_t numToSort, uint32_t nthreads); +/** + * \brief Compute the Hilbert/Moore index of each vertex in `vertices` + * \param bbox: a bounding box that contain all the given vertices + * \param vertices: the vertices coordinates + * \param n: the number of vertices + * \param[in,out] nbits: Sugested number of bits of the resulting Hilbert/Moore indices. The real number of bits used is returned. + * \param shift[3]: a value between 0 and 1 that commands how the Hilbert/Moore curve is deformed in each dimension. + * {0.5, 0.5, 0.5} is the underformed Hilbert/Moore curve. + * + * \details Compute the Hilbert/Moore index of each vertex in its \ref HXTVertex.padding.hilbertDist structure member. + * The size of the Hilbert/Moore index in bits can be suggested. Use nbits=0 if you don't want to suggest anything. + * If the size of the resulting Hilbert/Moore indices differ, the value of nbits is set to the real size in bits. + * A deformation of the Hilbert/Moore curve can be done with the shift parameter + */ +HXTStatus hxtVerticesHilbertDist(HXTBbox* bbox, HXTVertex* vertices, const uint32_t n, uint32_t* nbits, const double shift[3]); -/* sort all vertex using their dist value using a parallel radix sort with multiple pass - * for the sort to be done properly, nbits>=(nbr. of bits in vertices[i].dist) for all i<n + +/** + * Sort all the vertices following their \ref HXTVertex.padding.hilbertDist structure member. + * + * \param vertices: pointer to an array of vertices + * \param n: number of vertices in the array + * \param nbits: the maximum number of bits set in the \ref HXTVertex.padding.hilbertDist structure member. + */ +HXTStatus hxtVerticesSort(HXTVertex* const vertices, const uint32_t n, uint32_t nbits); + +/** + * Same as hxtVerticesSort(), but sort \ref hxtNodeInfo following their hilbertDist structure member. */ -HXTStatus hxtVerticesSort(hxtVertex* const vertices, const uint32_t n, uint32_t nbits); HXTStatus hxtNodeInfoSort(hxtNodeInfo* const array, const uint32_t n, uint32_t nbits); -HXTStatus hxtVerticesShuffle(hxtVertex* const vertices, const uint32_t n); +/** + * Shuffle vertices given in an array of \ref HXTVertex in a pseudo-random fashion (always the same random sequence). + */ +HXTStatus hxtVerticesShuffle(HXTVertex* const vertices, const uint32_t n); + +/** + * Shuffle vertices given in an array of \ref hxtNodeInfo in a pseudo-random fashion (always the same random sequence). + */ HXTStatus hxtNodeInfoShuffle(hxtNodeInfo* const array, const uint32_t n); #endif diff --git a/contrib/hxt/mesh3d_main.c b/contrib/hxt/mesh3d_main.c deleted file mode 100644 index b6dcd86bea7af0ba91d9724c5d0a7eaeddb1e404..0000000000000000000000000000000000000000 --- a/contrib/hxt/mesh3d_main.c +++ /dev/null @@ -1,62 +0,0 @@ -#include <math.h> -#include "hxt_message.h" -#include "hxt_api.h" -#include "hxt_mesh3d.h" -#include "hxt_tetrahedra.h" -#include "hxt_mesh.h" -#include "hxt_bbox.h" - -HXTStatus mySize (double x[3], void* data, double* s){ - *s = .1; - return HXT_STATUS_OK; -} - -int main(int argc, char **argv) { - if (argc != 3) - return HXT_ERROR_MSG(HXT_STATUS_FAILED,"usage: mesh3d input.msh output.msh"); - - HXTContext *context; - hxtContextCreate(&context); - HXTMesh *mesh; - HXT_CHECK(hxtMeshCreate(context, &mesh)); - HXT_CHECK(hxtMeshReadGmsh(mesh, argv[1])); - - // we must first update the bbox - hxtBbox bbox; - hxtBboxInit(&bbox); - HXT_CHECK( hxtBboxAdd(&bbox, mesh->vertices.coord, mesh->vertices.num) ); - - // impossible to put 0 !!! - for (uint32_t i = 0;i< mesh->vertices.num;i++){ - mesh->vertices.coord[4*i ] += 1.e-8*drand48(); - mesh->vertices.coord[4*i+1] += 1.e-8*drand48(); - mesh->vertices.coord[4*i+2] += 1.e-8*drand48(); - } - - printf("creating an empty mesh with %u vertices\n",mesh->vertices.num); - HXT_CHECK(hxtEmptyMesh(mesh)); - puts("verifying consistency of the mesh"); - HXT_CHECK( hxtTetrahedraVerify(mesh) ); - - uint32_t nbMissing; - printf("verifying the boundary\n"); - HXT_CHECK(hxtVerifyBoundary(mesh,&nbMissing)); - printf("%7d missing faces\n",nbMissing); - uint32_t nbColors; - if (nbMissing == 0){ - double *sizes; - HXT_CHECK(hxtComputeMeshSizeFromMesh (mesh, &sizes)); - HXT_CHECK(hxtColorMesh(mesh,&nbColors)); - HXTMeshSize *meshSize; - HXT_CHECK(hxtMeshSizeCreate (context,&meshSize)); - HXT_CHECK(hxtMeshSizeCompute (meshSize, bbox.min, bbox.max, mySize, NULL)); - HXT_CHECK(hxtRefineTetrahedra(mesh, meshSize, &sizes)); - HXT_CHECK(hxtMeshSizeDelete (&meshSize)); - HXT_CHECK(hxtFree(&sizes)); - puts("verifying consistency of the mesh"); - HXT_CHECK( hxtTetrahedraVerify(mesh) ); - } - - HXT_CHECK(hxtMeshWriteGmsh(mesh, argv[2])); - return HXT_STATUS_OK; -} diff --git a/contrib/hxt/parametrization_main.c b/contrib/hxt/parametrization_main.c deleted file mode 100644 index 540aea32b190e7c699b65590f758ac9071428d9b..0000000000000000000000000000000000000000 --- a/contrib/hxt/parametrization_main.c +++ /dev/null @@ -1,87 +0,0 @@ -#include "hxt_option.h" // to be used -#include "hxt_parametrization.h" -#include "hxt_linear_system.h" - -int main(int argc, char **argv) { - - if (argc < 2) - return HXT_ERROR_MSG(HXT_STATUS_FAILED,"usage: parametrization input.msh"); - - HXT_CHECK(hxtInitializeLinearSystems(&argc, &argv)); - HXTContext *context; - HXT_CHECK(hxtContextCreate(&context)); - HXTMesh *mesh; - HXT_CHECK(hxtMeshCreate(context, &mesh)); - HXT_CHECK(hxtMeshReadGmsh(mesh, argv[1])); - - printf("%f\n",mesh->vertices[0].coord[1]); - - HXTParametrization *param; - HXT_CHECK(hxtParametrizationCreate(mesh,¶m)); - - int *colors=NULL, *nNodes=NULL, *nodes=NULL, nc; - double *uv=NULL; - HXT_CHECK(hxtParametrizationCompute(param,&colors,&nNodes,&nodes,&uv,&nc)); - - - ///////// - - FILE* file = fopen("checkmyjob.msh","w"); - fprintf(file,"$MeshFormat\n" - "2.2 0 %u\n" - "$EndMeshFormat\n" - "$Nodes\n" - "%u\n",(unsigned) sizeof(double), mesh->numVertices); - - - { /* print the nodes */ - uint32_t i; - for (i=0; i<mesh->numVertices; i++) - fprintf(file,"%u %f %f %f\n",i+1, mesh->vertices[i].coord[0], mesh->vertices[i].coord[1], mesh->vertices[i].coord[2]); - } - - fprintf(file,"$EndNodes\n" - "$Elements\n" - "%lu\n",mesh->triangles.num); - - for (uint64_t i=0; i<mesh->triangles.num; i++){ - fprintf(file,"%lu 2 2 0 %d %u %u %u \n",i, - colors[i], - mesh->triangles.node[i*3]+1, - mesh->triangles.node[i*3 + 1]+1, - mesh->triangles.node[i*3 + 2]+1); - } - - - fputs("$EndElements\n",file); - fclose(file); - - for(int c=0; c<nc; c++){ - - char uname[32], vname[32]; - sprintf(uname,"nodedata_u_%d.msh",c); - sprintf(vname,"nodedata_v_%d.msh",c); - - FILE *uf = fopen(uname,"w"); - FILE *vf = fopen(vname,"w"); - - fprintf(uf,"$MeshFormat\n2.2 0 8\n$EndMeshFormat\n$NodeData\n1\n\"u\"\n1\n0\n3\n0\n1\n%u\n",nNodes[c+1]-nNodes[c]); - fprintf(vf,"$MeshFormat\n2.2 0 8\n$EndMeshFormat\n$NodeData\n1\n\"v\"\n1\n0\n3\n0\n1\n%u\n",nNodes[c+1]-nNodes[c]); - - - - for(int i=nNodes[c]; i<nNodes[c+1]; i++){ - fprintf(uf,"%u %f\n",nodes[i]+1,uv[2*i+0]); - fprintf(vf,"%u %f\n",nodes[i]+1,uv[2*i+1]); - } - - - fprintf(uf,"$EndNodeData"); - fprintf(vf,"$EndNodeData"); - - fclose(uf); - fclose(vf); - } - - -} diff --git a/contrib/hxt/predicates.c b/contrib/hxt/predicates.c index b015cd6bb60f985d21bbfb190e332815140c0459..727a32c3cbd86a7ed9ffa5f37bd74a16ab1341d7 100644 --- a/contrib/hxt/predicates.c +++ b/contrib/hxt/predicates.c @@ -331,7 +331,9 @@ REAL splitter; static REAL epsilon; /* = 2^(-p). Used to estimate roundoff errors. */ /* A set of coefficients used to calculate maximum roundoff errors. */ static REAL resulterrbound; +static REAL ccwerrboundA, ccwerrboundB, ccwerrboundC; static REAL o3derrboundB, o3derrboundC; +static REAL iccerrboundA, iccerrboundB, iccerrboundC; static REAL isperrboundB, isperrboundC; // Static filters for orient3d() and insphere(). @@ -409,14 +411,19 @@ void exactinit(REAL maxx, REAL maxy, REAL maxz) /* Error bounds for orientation and incircle tests. */ resulterrbound = (3.0 + 8.0 * epsilon) * epsilon; + ccwerrboundA = (3.0 + 16.0 * epsilon) * epsilon; + ccwerrboundB = (2.0 + 12.0 * epsilon) * epsilon; + ccwerrboundC = (9.0 + 64.0 * epsilon) * epsilon * epsilon; o3derrboundA = (7.0 + 56.0 * epsilon) * epsilon; o3derrboundB = (3.0 + 28.0 * epsilon) * epsilon; o3derrboundC = (26.0 + 288.0 * epsilon) * epsilon * epsilon; + iccerrboundA = (10.0 + 96.0 * epsilon) * epsilon; + iccerrboundB = (4.0 + 48.0 * epsilon) * epsilon; + iccerrboundC = (44.0 + 576.0 * epsilon) * epsilon * epsilon; isperrboundA = (16.0 + 224.0 * epsilon) * epsilon; isperrboundB = (5.0 + 72.0 * epsilon) * epsilon; isperrboundC = (71.0 + 1408.0 * epsilon) * epsilon * epsilon; - // Calculate the two static filters for orient3d() and insphere() tests. // Added by H. Si, 2012-08-23. @@ -624,31 +631,35 @@ int fast_expansion_sum_zeroelim(const int elen, const REAL *e, const int flen, c eindex = findex = 0; if ((fnow > enow) == (fnow > -enow)) { Q = enow; - enow = e[++eindex]; + ++eindex; } else { Q = fnow; - fnow = f[++findex]; + ++findex; } hindex = 0; if ((eindex < elen) && (findex < flen)) { + enow = e[eindex]; + fnow = f[findex]; if ((fnow > enow) == (fnow > -enow)) { Fast_Two_Sum(enow, Q, Qnew, hh); - enow = e[++eindex]; + ++eindex; } else { Fast_Two_Sum(fnow, Q, Qnew, hh); - fnow = f[++findex]; + ++findex; } Q = Qnew; if (hh != 0.0) { h[hindex++] = hh; } while ((eindex < elen) && (findex < flen)) { + enow = e[eindex]; + fnow = f[findex]; if ((fnow > enow) == (fnow > -enow)) { Two_Sum(Q, enow, Qnew, hh); - enow = e[++eindex]; + ++eindex; } else { Two_Sum(Q, fnow, Qnew, hh); - fnow = f[++findex]; + ++findex; } Q = Qnew; if (hh != 0.0) { @@ -657,16 +668,18 @@ int fast_expansion_sum_zeroelim(const int elen, const REAL *e, const int flen, c } } while (eindex < elen) { + enow = e[eindex]; Two_Sum(Q, enow, Qnew, hh); - enow = e[++eindex]; + ++eindex; Q = Qnew; if (hh != 0.0) { h[hindex++] = hh; } } while (findex < flen) { + fnow = f[findex]; Two_Sum(Q, fnow, Qnew, hh); - fnow = f[++findex]; + ++findex; Q = Qnew; if (hh != 0.0) { h[hindex++] = hh; @@ -798,6 +811,155 @@ static REAL estimate(const int elen, const REAL *e) return Q; } +/*****************************************************************************/ +/* */ +/* orient2dfast() Approximate 2D orientation test. Nonrobust. */ +/* orient2dexact() Exact 2D orientation test. Robust. */ +/* orient2dslow() Another exact 2D orientation test. Robust. */ +/* orient2d() Adaptive exact 2D orientation test. Robust. */ +/* */ +/* Return a positive value if the points pa, pb, and pc occur */ +/* in counterclockwise order; a negative value if they occur */ +/* in clockwise order; and zero if they are collinear. The */ +/* result is also a rough approximation of twice the signed */ +/* area of the triangle defined by the three points. */ +/* */ +/* Only the first and last routine should be used; the middle two are for */ +/* timings. */ +/* */ +/* The last three use exact arithmetic to ensure a correct answer. The */ +/* result returned is the determinant of a matrix. In orient2d() only, */ +/* this determinant is computed adaptively, in the sense that exact */ +/* arithmetic is used only to the degree it is needed to ensure that the */ +/* returned value has the correct sign. Hence, orient2d() is usually quite */ +/* fast, but will run more slowly when the input points are collinear or */ +/* nearly so. */ +/* */ +/*****************************************************************************/ + +REAL orient2dfast(const REAL* __restrict__ pa, const REAL* __restrict__ pb, const REAL* __restrict__ pc) +{ + REAL acx, bcx, acy, bcy; + + acx = pa[0] - pc[0]; + bcx = pb[0] - pc[0]; + acy = pa[1] - pc[1]; + bcy = pb[1] - pc[1]; + return acx * bcy - acy * bcx; +} + +REAL orient2dadapt(const REAL* __restrict__ pa, const REAL* __restrict__ pb, const REAL* __restrict__ pc, const REAL detsum) +{ + INEXACT REAL acx, acy, bcx, bcy; + REAL acxtail, acytail, bcxtail, bcytail; + INEXACT REAL detleft, detright; + REAL detlefttail, detrighttail; + REAL det, errbound; + REAL B[4], C1[8], C2[12], D[16]; + INEXACT REAL B3; + int C1length, C2length, Dlength; + REAL u[4]; + INEXACT REAL u3; + INEXACT REAL s1, t1; + REAL s0, t0; + + INEXACT REAL bvirt; + REAL avirt, bround, around; + INEXACT REAL c; + INEXACT REAL abig; + REAL ahi, alo, bhi, blo; + REAL err1, err2, err3; + INEXACT REAL _i, _j; + REAL _0; + + acx = (REAL) (pa[0] - pc[0]); + bcx = (REAL) (pb[0] - pc[0]); + acy = (REAL) (pa[1] - pc[1]); + bcy = (REAL) (pb[1] - pc[1]); + + Two_Product(acx, bcy, detleft, detlefttail); + Two_Product(acy, bcx, detright, detrighttail); + + Two_Two_Diff(detleft, detlefttail, detright, detrighttail, + B3, B[2], B[1], B[0]); + B[3] = B3; + + det = estimate(4, B); + errbound = ccwerrboundB * detsum; + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + Two_Diff_Tail(pa[0], pc[0], acx, acxtail); + Two_Diff_Tail(pb[0], pc[0], bcx, bcxtail); + Two_Diff_Tail(pa[1], pc[1], acy, acytail); + Two_Diff_Tail(pb[1], pc[1], bcy, bcytail); + + if ((acxtail == 0.0) && (acytail == 0.0) + && (bcxtail == 0.0) && (bcytail == 0.0)) { + return det; + } + + errbound = ccwerrboundC * detsum + resulterrbound * Absolute(det); + det += (acx * bcytail + bcy * acxtail) + - (acy * bcxtail + bcx * acytail); + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + Two_Product(acxtail, bcy, s1, s0); + Two_Product(acytail, bcx, t1, t0); + Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]); + u[3] = u3; + C1length = fast_expansion_sum_zeroelim(4, B, 4, u, C1); + + Two_Product(acx, bcytail, s1, s0); + Two_Product(acy, bcxtail, t1, t0); + Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]); + u[3] = u3; + C2length = fast_expansion_sum_zeroelim(C1length, C1, 4, u, C2); + + Two_Product(acxtail, bcytail, s1, s0); + Two_Product(acytail, bcxtail, t1, t0); + Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]); + u[3] = u3; + Dlength = fast_expansion_sum_zeroelim(C2length, C2, 4, u, D); + + return(D[Dlength - 1]); +} + +REAL orient2d(const REAL* __restrict__ pa, const REAL* __restrict__ pb, const REAL* __restrict__ pc) +{ + REAL detleft, detright, det; + REAL detsum, errbound; + + detleft = (pa[0] - pc[0]) * (pb[1] - pc[1]); + detright = (pa[1] - pc[1]) * (pb[0] - pc[0]); + det = detleft - detright; + + if (detleft > 0.0) { + if (detright <= 0.0) { + return det; + } else { + detsum = detleft + detright; + } + } else if (detleft < 0.0) { + if (detright >= 0.0) { + return det; + } else { + detsum = -detleft - detright; + } + } else { + return det; + } + + errbound = ccwerrboundA * detsum; + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + return orient2dadapt(pa, pb, pc, detsum); +} /*****************************************************************************/ /* */ @@ -1291,6 +1453,668 @@ REAL orient3d(const REAL* const __restrict__ pa, const REAL* const __restrict__ return orient3dadapt(pa, pb, pc, pd, permanent); } +/*****************************************************************************/ +/* */ +/* incirclefast() Approximate 2D incircle test. Nonrobust. */ +/* incircleexact() Exact 2D incircle test. Robust. */ +/* incircleslow() Another exact 2D incircle test. Robust. */ +/* incircle() Adaptive exact 2D incircle test. Robust. */ +/* */ +/* Return a positive value if the point pd lies inside the */ +/* circle passing through pa, pb, and pc; a negative value if */ +/* it lies outside; and zero if the four points are cocircular.*/ +/* The points pa, pb, and pc must be in counterclockwise */ +/* order, or the sign of the result will be reversed. */ +/* */ +/* Only the first and last routine should be used; the middle two are for */ +/* timings. */ +/* */ +/* The last three use exact arithmetic to ensure a correct answer. The */ +/* result returned is the determinant of a matrix. In incircle() only, */ +/* this determinant is computed adaptively, in the sense that exact */ +/* arithmetic is used only to the degree it is needed to ensure that the */ +/* returned value has the correct sign. Hence, incircle() is usually quite */ +/* fast, but will run more slowly when the input points are cocircular or */ +/* nearly so. */ +/* */ +/*****************************************************************************/ + +REAL incirclefast(const REAL* __restrict__ pa, const REAL* __restrict__ pb, const REAL* __restrict__ pc, const REAL* __restrict__ pd) +{ + REAL adx, ady, bdx, bdy, cdx, cdy; + REAL abdet, bcdet, cadet; + REAL alift, blift, clift; + + adx = pa[0] - pd[0]; + ady = pa[1] - pd[1]; + bdx = pb[0] - pd[0]; + bdy = pb[1] - pd[1]; + cdx = pc[0] - pd[0]; + cdy = pc[1] - pd[1]; + + abdet = adx * bdy - bdx * ady; + bcdet = bdx * cdy - cdx * bdy; + cadet = cdx * ady - adx * cdy; + alift = adx * adx + ady * ady; + blift = bdx * bdx + bdy * bdy; + clift = cdx * cdx + cdy * cdy; + + return alift * bcdet + blift * cadet + clift * abdet; +} + +REAL incircleadapt(const REAL* __restrict__ pa, const REAL* __restrict__ pb, const REAL* __restrict__ pc, const REAL* __restrict__ pd, const REAL permanent) +{ + INEXACT REAL adx, bdx, cdx, ady, bdy, cdy; + REAL det, errbound; + + INEXACT REAL bdxcdy1, cdxbdy1, cdxady1, adxcdy1, adxbdy1, bdxady1; + REAL bdxcdy0, cdxbdy0, cdxady0, adxcdy0, adxbdy0, bdxady0; + REAL bc[4], ca[4], ab[4]; + INEXACT REAL bc3, ca3, ab3; + REAL axbc[8], axxbc[16], aybc[8], ayybc[16], adet[32]; + int axbclen, axxbclen, aybclen, ayybclen, alen; + REAL bxca[8], bxxca[16], byca[8], byyca[16], bdet[32]; + int bxcalen, bxxcalen, bycalen, byycalen, blen; + REAL cxab[8], cxxab[16], cyab[8], cyyab[16], cdet[32]; + int cxablen, cxxablen, cyablen, cyyablen, clen; + REAL abdet[64]; + int ablen; + REAL fin1[1152], fin2[1152]; + REAL *finnow, *finother, *finswap; + int finlength; + + REAL adxtail, bdxtail, cdxtail, adytail, bdytail, cdytail; + INEXACT REAL adxadx1, adyady1, bdxbdx1, bdybdy1, cdxcdx1, cdycdy1; + REAL adxadx0, adyady0, bdxbdx0, bdybdy0, cdxcdx0, cdycdy0; + REAL aa[4], bb[4], cc[4]; + INEXACT REAL aa3, bb3, cc3; + INEXACT REAL ti1, tj1; + REAL ti0, tj0; + REAL u[4], v[4]; + INEXACT REAL u3, v3; + REAL temp8[8], temp16a[16], temp16b[16], temp16c[16]; + REAL temp32a[32], temp32b[32], temp48[48], temp64[64]; + int temp8len, temp16alen, temp16blen, temp16clen; + int temp32alen, temp32blen, temp48len, temp64len; + REAL axtbb[8], axtcc[8], aytbb[8], aytcc[8]; + int axtbblen, axtcclen, aytbblen, aytcclen; + REAL bxtaa[8], bxtcc[8], bytaa[8], bytcc[8]; + int bxtaalen, bxtcclen, bytaalen, bytcclen; + REAL cxtaa[8], cxtbb[8], cytaa[8], cytbb[8]; + int cxtaalen, cxtbblen, cytaalen, cytbblen; + REAL axtbc[8], aytbc[8], bxtca[8], bytca[8], cxtab[8], cytab[8]; + int axtbclen, aytbclen, bxtcalen, bytcalen, cxtablen, cytablen; + REAL axtbct[16], aytbct[16], bxtcat[16], bytcat[16], cxtabt[16], cytabt[16]; + int axtbctlen, aytbctlen, bxtcatlen, bytcatlen, cxtabtlen, cytabtlen; + REAL axtbctt[8], aytbctt[8], bxtcatt[8]; + REAL bytcatt[8], cxtabtt[8], cytabtt[8]; + int axtbcttlen, aytbcttlen, bxtcattlen, bytcattlen, cxtabttlen, cytabttlen; + REAL abt[8], bct[8], cat[8]; + int abtlen, bctlen, catlen; + REAL abtt[4], bctt[4], catt[4]; + int abttlen, bcttlen, cattlen; + INEXACT REAL abtt3, bctt3, catt3; + REAL negate; + + INEXACT REAL bvirt; + REAL avirt, bround, around; + INEXACT REAL c; + INEXACT REAL abig; + REAL ahi, alo, bhi, blo; + REAL err1, err2, err3; + INEXACT REAL _i, _j; + REAL _0; + + // Avoid compiler warnings. H. Si, 2012-02-16. + axtbclen = aytbclen = bxtcalen = bytcalen = cxtablen = cytablen = 0; + + adx = (REAL) (pa[0] - pd[0]); + bdx = (REAL) (pb[0] - pd[0]); + cdx = (REAL) (pc[0] - pd[0]); + ady = (REAL) (pa[1] - pd[1]); + bdy = (REAL) (pb[1] - pd[1]); + cdy = (REAL) (pc[1] - pd[1]); + + Two_Product(bdx, cdy, bdxcdy1, bdxcdy0); + Two_Product(cdx, bdy, cdxbdy1, cdxbdy0); + Two_Two_Diff(bdxcdy1, bdxcdy0, cdxbdy1, cdxbdy0, bc3, bc[2], bc[1], bc[0]); + bc[3] = bc3; + axbclen = scale_expansion_zeroelim(4, bc, adx, axbc); + axxbclen = scale_expansion_zeroelim(axbclen, axbc, adx, axxbc); + aybclen = scale_expansion_zeroelim(4, bc, ady, aybc); + ayybclen = scale_expansion_zeroelim(aybclen, aybc, ady, ayybc); + alen = fast_expansion_sum_zeroelim(axxbclen, axxbc, ayybclen, ayybc, adet); + + Two_Product(cdx, ady, cdxady1, cdxady0); + Two_Product(adx, cdy, adxcdy1, adxcdy0); + Two_Two_Diff(cdxady1, cdxady0, adxcdy1, adxcdy0, ca3, ca[2], ca[1], ca[0]); + ca[3] = ca3; + bxcalen = scale_expansion_zeroelim(4, ca, bdx, bxca); + bxxcalen = scale_expansion_zeroelim(bxcalen, bxca, bdx, bxxca); + bycalen = scale_expansion_zeroelim(4, ca, bdy, byca); + byycalen = scale_expansion_zeroelim(bycalen, byca, bdy, byyca); + blen = fast_expansion_sum_zeroelim(bxxcalen, bxxca, byycalen, byyca, bdet); + + Two_Product(adx, bdy, adxbdy1, adxbdy0); + Two_Product(bdx, ady, bdxady1, bdxady0); + Two_Two_Diff(adxbdy1, adxbdy0, bdxady1, bdxady0, ab3, ab[2], ab[1], ab[0]); + ab[3] = ab3; + cxablen = scale_expansion_zeroelim(4, ab, cdx, cxab); + cxxablen = scale_expansion_zeroelim(cxablen, cxab, cdx, cxxab); + cyablen = scale_expansion_zeroelim(4, ab, cdy, cyab); + cyyablen = scale_expansion_zeroelim(cyablen, cyab, cdy, cyyab); + clen = fast_expansion_sum_zeroelim(cxxablen, cxxab, cyyablen, cyyab, cdet); + + ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet); + finlength = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, fin1); + + det = estimate(finlength, fin1); + errbound = iccerrboundB * permanent; + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + Two_Diff_Tail(pa[0], pd[0], adx, adxtail); + Two_Diff_Tail(pa[1], pd[1], ady, adytail); + Two_Diff_Tail(pb[0], pd[0], bdx, bdxtail); + Two_Diff_Tail(pb[1], pd[1], bdy, bdytail); + Two_Diff_Tail(pc[0], pd[0], cdx, cdxtail); + Two_Diff_Tail(pc[1], pd[1], cdy, cdytail); + if ((adxtail == 0.0) && (bdxtail == 0.0) && (cdxtail == 0.0) + && (adytail == 0.0) && (bdytail == 0.0) && (cdytail == 0.0)) { + return det; + } + + errbound = iccerrboundC * permanent + resulterrbound * Absolute(det); + det += ((adx * adx + ady * ady) * ((bdx * cdytail + cdy * bdxtail) + - (bdy * cdxtail + cdx * bdytail)) + + 2.0 * (adx * adxtail + ady * adytail) * (bdx * cdy - bdy * cdx)) + + ((bdx * bdx + bdy * bdy) * ((cdx * adytail + ady * cdxtail) + - (cdy * adxtail + adx * cdytail)) + + 2.0 * (bdx * bdxtail + bdy * bdytail) * (cdx * ady - cdy * adx)) + + ((cdx * cdx + cdy * cdy) * ((adx * bdytail + bdy * adxtail) + - (ady * bdxtail + bdx * adytail)) + + 2.0 * (cdx * cdxtail + cdy * cdytail) * (adx * bdy - ady * bdx)); + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + finnow = fin1; + finother = fin2; + + if ((bdxtail != 0.0) || (bdytail != 0.0) + || (cdxtail != 0.0) || (cdytail != 0.0)) { + Square(adx, adxadx1, adxadx0); + Square(ady, adyady1, adyady0); + Two_Two_Sum(adxadx1, adxadx0, adyady1, adyady0, aa3, aa[2], aa[1], aa[0]); + aa[3] = aa3; + } + if ((cdxtail != 0.0) || (cdytail != 0.0) + || (adxtail != 0.0) || (adytail != 0.0)) { + Square(bdx, bdxbdx1, bdxbdx0); + Square(bdy, bdybdy1, bdybdy0); + Two_Two_Sum(bdxbdx1, bdxbdx0, bdybdy1, bdybdy0, bb3, bb[2], bb[1], bb[0]); + bb[3] = bb3; + } + if ((adxtail != 0.0) || (adytail != 0.0) + || (bdxtail != 0.0) || (bdytail != 0.0)) { + Square(cdx, cdxcdx1, cdxcdx0); + Square(cdy, cdycdy1, cdycdy0); + Two_Two_Sum(cdxcdx1, cdxcdx0, cdycdy1, cdycdy0, cc3, cc[2], cc[1], cc[0]); + cc[3] = cc3; + } + + if (adxtail != 0.0) { + axtbclen = scale_expansion_zeroelim(4, bc, adxtail, axtbc); + temp16alen = scale_expansion_zeroelim(axtbclen, axtbc, 2.0 * adx, + temp16a); + + axtcclen = scale_expansion_zeroelim(4, cc, adxtail, axtcc); + temp16blen = scale_expansion_zeroelim(axtcclen, axtcc, bdy, temp16b); + + axtbblen = scale_expansion_zeroelim(4, bb, adxtail, axtbb); + temp16clen = scale_expansion_zeroelim(axtbblen, axtbb, -cdy, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (adytail != 0.0) { + aytbclen = scale_expansion_zeroelim(4, bc, adytail, aytbc); + temp16alen = scale_expansion_zeroelim(aytbclen, aytbc, 2.0 * ady, + temp16a); + + aytbblen = scale_expansion_zeroelim(4, bb, adytail, aytbb); + temp16blen = scale_expansion_zeroelim(aytbblen, aytbb, cdx, temp16b); + + aytcclen = scale_expansion_zeroelim(4, cc, adytail, aytcc); + temp16clen = scale_expansion_zeroelim(aytcclen, aytcc, -bdx, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (bdxtail != 0.0) { + bxtcalen = scale_expansion_zeroelim(4, ca, bdxtail, bxtca); + temp16alen = scale_expansion_zeroelim(bxtcalen, bxtca, 2.0 * bdx, + temp16a); + + bxtaalen = scale_expansion_zeroelim(4, aa, bdxtail, bxtaa); + temp16blen = scale_expansion_zeroelim(bxtaalen, bxtaa, cdy, temp16b); + + bxtcclen = scale_expansion_zeroelim(4, cc, bdxtail, bxtcc); + temp16clen = scale_expansion_zeroelim(bxtcclen, bxtcc, -ady, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (bdytail != 0.0) { + bytcalen = scale_expansion_zeroelim(4, ca, bdytail, bytca); + temp16alen = scale_expansion_zeroelim(bytcalen, bytca, 2.0 * bdy, + temp16a); + + bytcclen = scale_expansion_zeroelim(4, cc, bdytail, bytcc); + temp16blen = scale_expansion_zeroelim(bytcclen, bytcc, adx, temp16b); + + bytaalen = scale_expansion_zeroelim(4, aa, bdytail, bytaa); + temp16clen = scale_expansion_zeroelim(bytaalen, bytaa, -cdx, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (cdxtail != 0.0) { + cxtablen = scale_expansion_zeroelim(4, ab, cdxtail, cxtab); + temp16alen = scale_expansion_zeroelim(cxtablen, cxtab, 2.0 * cdx, + temp16a); + + cxtbblen = scale_expansion_zeroelim(4, bb, cdxtail, cxtbb); + temp16blen = scale_expansion_zeroelim(cxtbblen, cxtbb, ady, temp16b); + + cxtaalen = scale_expansion_zeroelim(4, aa, cdxtail, cxtaa); + temp16clen = scale_expansion_zeroelim(cxtaalen, cxtaa, -bdy, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (cdytail != 0.0) { + cytablen = scale_expansion_zeroelim(4, ab, cdytail, cytab); + temp16alen = scale_expansion_zeroelim(cytablen, cytab, 2.0 * cdy, + temp16a); + + cytaalen = scale_expansion_zeroelim(4, aa, cdytail, cytaa); + temp16blen = scale_expansion_zeroelim(cytaalen, cytaa, bdx, temp16b); + + cytbblen = scale_expansion_zeroelim(4, bb, cdytail, cytbb); + temp16clen = scale_expansion_zeroelim(cytbblen, cytbb, -adx, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + + if ((adxtail != 0.0) || (adytail != 0.0)) { + if ((bdxtail != 0.0) || (bdytail != 0.0) + || (cdxtail != 0.0) || (cdytail != 0.0)) { + Two_Product(bdxtail, cdy, ti1, ti0); + Two_Product(bdx, cdytail, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]); + u[3] = u3; + negate = -bdy; + Two_Product(cdxtail, negate, ti1, ti0); + negate = -bdytail; + Two_Product(cdx, negate, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]); + v[3] = v3; + bctlen = fast_expansion_sum_zeroelim(4, u, 4, v, bct); + + Two_Product(bdxtail, cdytail, ti1, ti0); + Two_Product(cdxtail, bdytail, tj1, tj0); + Two_Two_Diff(ti1, ti0, tj1, tj0, bctt3, bctt[2], bctt[1], bctt[0]); + bctt[3] = bctt3; + bcttlen = 4; + } else { + bct[0] = 0.0; + bctlen = 1; + bctt[0] = 0.0; + bcttlen = 1; + } + + if (adxtail != 0.0) { + temp16alen = scale_expansion_zeroelim(axtbclen, axtbc, adxtail, temp16a); + axtbctlen = scale_expansion_zeroelim(bctlen, bct, adxtail, axtbct); + temp32alen = scale_expansion_zeroelim(axtbctlen, axtbct, 2.0 * adx, + temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + if (bdytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, cc, adxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, bdytail, + temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, + temp16a, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (cdytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, bb, -adxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, cdytail, + temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, + temp16a, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + + temp32alen = scale_expansion_zeroelim(axtbctlen, axtbct, adxtail, + temp32a); + axtbcttlen = scale_expansion_zeroelim(bcttlen, bctt, adxtail, axtbctt); + temp16alen = scale_expansion_zeroelim(axtbcttlen, axtbctt, 2.0 * adx, + temp16a); + temp16blen = scale_expansion_zeroelim(axtbcttlen, axtbctt, adxtail, + temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, + temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, + temp64, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (adytail != 0.0) { + temp16alen = scale_expansion_zeroelim(aytbclen, aytbc, adytail, temp16a); + aytbctlen = scale_expansion_zeroelim(bctlen, bct, adytail, aytbct); + temp32alen = scale_expansion_zeroelim(aytbctlen, aytbct, 2.0 * ady, + temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + + + temp32alen = scale_expansion_zeroelim(aytbctlen, aytbct, adytail, + temp32a); + aytbcttlen = scale_expansion_zeroelim(bcttlen, bctt, adytail, aytbctt); + temp16alen = scale_expansion_zeroelim(aytbcttlen, aytbctt, 2.0 * ady, + temp16a); + temp16blen = scale_expansion_zeroelim(aytbcttlen, aytbctt, adytail, + temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, + temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, + temp64, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + } + if ((bdxtail != 0.0) || (bdytail != 0.0)) { + if ((cdxtail != 0.0) || (cdytail != 0.0) + || (adxtail != 0.0) || (adytail != 0.0)) { + Two_Product(cdxtail, ady, ti1, ti0); + Two_Product(cdx, adytail, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]); + u[3] = u3; + negate = -cdy; + Two_Product(adxtail, negate, ti1, ti0); + negate = -cdytail; + Two_Product(adx, negate, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]); + v[3] = v3; + catlen = fast_expansion_sum_zeroelim(4, u, 4, v, cat); + + Two_Product(cdxtail, adytail, ti1, ti0); + Two_Product(adxtail, cdytail, tj1, tj0); + Two_Two_Diff(ti1, ti0, tj1, tj0, catt3, catt[2], catt[1], catt[0]); + catt[3] = catt3; + cattlen = 4; + } else { + cat[0] = 0.0; + catlen = 1; + catt[0] = 0.0; + cattlen = 1; + } + + if (bdxtail != 0.0) { + temp16alen = scale_expansion_zeroelim(bxtcalen, bxtca, bdxtail, temp16a); + bxtcatlen = scale_expansion_zeroelim(catlen, cat, bdxtail, bxtcat); + temp32alen = scale_expansion_zeroelim(bxtcatlen, bxtcat, 2.0 * bdx, + temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + if (cdytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, aa, bdxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, cdytail, + temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, + temp16a, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (adytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, cc, -bdxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, adytail, + temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, + temp16a, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + + temp32alen = scale_expansion_zeroelim(bxtcatlen, bxtcat, bdxtail, + temp32a); + bxtcattlen = scale_expansion_zeroelim(cattlen, catt, bdxtail, bxtcatt); + temp16alen = scale_expansion_zeroelim(bxtcattlen, bxtcatt, 2.0 * bdx, + temp16a); + temp16blen = scale_expansion_zeroelim(bxtcattlen, bxtcatt, bdxtail, + temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, + temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, + temp64, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (bdytail != 0.0) { + temp16alen = scale_expansion_zeroelim(bytcalen, bytca, bdytail, temp16a); + bytcatlen = scale_expansion_zeroelim(catlen, cat, bdytail, bytcat); + temp32alen = scale_expansion_zeroelim(bytcatlen, bytcat, 2.0 * bdy, + temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + + + temp32alen = scale_expansion_zeroelim(bytcatlen, bytcat, bdytail, + temp32a); + bytcattlen = scale_expansion_zeroelim(cattlen, catt, bdytail, bytcatt); + temp16alen = scale_expansion_zeroelim(bytcattlen, bytcatt, 2.0 * bdy, + temp16a); + temp16blen = scale_expansion_zeroelim(bytcattlen, bytcatt, bdytail, + temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, + temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, + temp64, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + } + if ((cdxtail != 0.0) || (cdytail != 0.0)) { + if ((adxtail != 0.0) || (adytail != 0.0) + || (bdxtail != 0.0) || (bdytail != 0.0)) { + Two_Product(adxtail, bdy, ti1, ti0); + Two_Product(adx, bdytail, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]); + u[3] = u3; + negate = -ady; + Two_Product(bdxtail, negate, ti1, ti0); + negate = -adytail; + Two_Product(bdx, negate, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]); + v[3] = v3; + abtlen = fast_expansion_sum_zeroelim(4, u, 4, v, abt); + + Two_Product(adxtail, bdytail, ti1, ti0); + Two_Product(bdxtail, adytail, tj1, tj0); + Two_Two_Diff(ti1, ti0, tj1, tj0, abtt3, abtt[2], abtt[1], abtt[0]); + abtt[3] = abtt3; + abttlen = 4; + } else { + abt[0] = 0.0; + abtlen = 1; + abtt[0] = 0.0; + abttlen = 1; + } + + if (cdxtail != 0.0) { + temp16alen = scale_expansion_zeroelim(cxtablen, cxtab, cdxtail, temp16a); + cxtabtlen = scale_expansion_zeroelim(abtlen, abt, cdxtail, cxtabt); + temp32alen = scale_expansion_zeroelim(cxtabtlen, cxtabt, 2.0 * cdx, + temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + if (adytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, bb, cdxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, adytail, + temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, + temp16a, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (bdytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, aa, -cdxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, bdytail, + temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, + temp16a, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + + temp32alen = scale_expansion_zeroelim(cxtabtlen, cxtabt, cdxtail, + temp32a); + cxtabttlen = scale_expansion_zeroelim(abttlen, abtt, cdxtail, cxtabtt); + temp16alen = scale_expansion_zeroelim(cxtabttlen, cxtabtt, 2.0 * cdx, + temp16a); + temp16blen = scale_expansion_zeroelim(cxtabttlen, cxtabtt, cdxtail, + temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, + temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, + temp64, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (cdytail != 0.0) { + temp16alen = scale_expansion_zeroelim(cytablen, cytab, cdytail, temp16a); + cytabtlen = scale_expansion_zeroelim(abtlen, abt, cdytail, cytabt); + temp32alen = scale_expansion_zeroelim(cytabtlen, cytabt, 2.0 * cdy, + temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + + + temp32alen = scale_expansion_zeroelim(cytabtlen, cytabt, cdytail, + temp32a); + cytabttlen = scale_expansion_zeroelim(abttlen, abtt, cdytail, cytabtt); + temp16alen = scale_expansion_zeroelim(cytabttlen, cytabtt, 2.0 * cdy, + temp16a); + temp16blen = scale_expansion_zeroelim(cytabttlen, cytabtt, cdytail, + temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, + temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, + temp64, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + } + + return finnow[finlength - 1]; +} + +REAL incircle(const REAL* __restrict__ pa, const REAL* __restrict__ pb, const REAL* __restrict__ pc, const REAL* __restrict__ pd) +{ + REAL adx, bdx, cdx, ady, bdy, cdy; + REAL bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady; + REAL alift, blift, clift; + REAL det; + REAL permanent, errbound; + + adx = pa[0] - pd[0]; + bdx = pb[0] - pd[0]; + cdx = pc[0] - pd[0]; + ady = pa[1] - pd[1]; + bdy = pb[1] - pd[1]; + cdy = pc[1] - pd[1]; + + bdxcdy = bdx * cdy; + cdxbdy = cdx * bdy; + alift = adx * adx + ady * ady; + + cdxady = cdx * ady; + adxcdy = adx * cdy; + blift = bdx * bdx + bdy * bdy; + + adxbdy = adx * bdy; + bdxady = bdx * ady; + clift = cdx * cdx + cdy * cdy; + + det = alift * (bdxcdy - cdxbdy) + + blift * (cdxady - adxcdy) + + clift * (adxbdy - bdxady); + + permanent = (Absolute(bdxcdy) + Absolute(cdxbdy)) * alift + + (Absolute(cdxady) + Absolute(adxcdy)) * blift + + (Absolute(adxbdy) + Absolute(bdxady)) * clift; + errbound = iccerrboundA * permanent; + if ((det > errbound) || (-det > errbound)) { + return det; + } + + return incircleadapt(pa, pb, pc, pd, permanent); +} /*****************************************************************************/ /* */ diff --git a/contrib/hxt/predicates.h b/contrib/hxt/predicates.h index 46c92554dacfeb41fc773986e52c52a3980f9765..35d171f1a57f8499db4e0e3fe3eba11c4253f209 100644 --- a/contrib/hxt/predicates.h +++ b/contrib/hxt/predicates.h @@ -31,6 +31,17 @@ double orient3d( const double* const __restrict__ pc, const double* const __restrict__ pd); +double incircle( + const double* const __restrict__ pa, + const double* const __restrict__ pb, + const double* const __restrict__ pc, + const double* const __restrict__ pd); + +double orient2d( + const double* const __restrict__ pa, + const double* const __restrict__ pb, + const double* const __restrict__ pc); + int grow_expansion( int elen, const double *e, double b, double *h); diff --git a/contrib/hxt/tetgenBR.cxx b/contrib/hxt/tetgenBR.cxx new file mode 100644 index 0000000000000000000000000000000000000000..e8eecc4aa6854d011de2a585ea5a2f3c37dbb432 --- /dev/null +++ b/contrib/hxt/tetgenBR.cxx @@ -0,0 +1,17343 @@ +/////////////////////////////////////////////////////////////////////////////// +// // +// TetGen // +// // +// A Quality Tetrahedral Mesh Generator and A 3D Delaunay Triangulator // +// // +// Version 1.5 // +// May 31, 2014 // +// // +// Copyright (C) 2002--2016 // +// // +// TetGen is freely available through the website: http://www.tetgen.org. // +// It may be copied, modified, and redistributed for non-commercial use. // +// Please consult the file LICENSE for the detailed copyright notices. // +// // +/////////////////////////////////////////////////////////////////////////////// + +// The following code of this file was automatically generated. Do not edit! +// TetGenBR -- The Boundary Recovery code of TetGen. + +/////////////////////////////////////////////////////////////////////////////// +// // +// parse_commandline() Read the command line, identify switches, and set // +// up options and file names. // +// // +// 'argc' and 'argv' are the same parameters passed to the function main() // +// of a C/C++ program. They together represent the command line user invoked // +// from an environment in which TetGen is running. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenbehavior::parse_commandline(int argc, char **argv) +{ + int startindex; + int increment; + int meshnumber; + int i, j, k; + char workstring[1024]; + + // First determine the input style of the switches. + if (argc == 0) { + startindex = 0; // Switches are given without a dash. + argc = 1; // For running the following for-loop once. + commandline[0] = '\0'; + } else { + startindex = 1; + strcpy(commandline, argv[0]); + strcat(commandline, " "); + } + + for (i = startindex; i < argc; i++) { + // Remember the command line for output. + strcat(commandline, argv[i]); + strcat(commandline, " "); + if (startindex == 1) { + // Is this string a filename? + if (argv[i][0] != '-') { + strncpy(infilename, argv[i], 1024 - 1); + infilename[1024 - 1] = '\0'; + continue; + } + } + // Parse the individual switch from the string. + for (j = startindex; argv[i][j] != '\0'; j++) { + if (argv[i][j] == 'p') { + plc = 1; + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + facet_separate_ang_tol = (REAL) strtod(workstring, (char **) NULL); + } + if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { + j++; + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.') || (argv[i][j + 1] == 'e') || + (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + facet_overlap_ang_tol = (REAL) strtod(workstring, (char **) NULL); + } + } + if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { + j++; + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + facet_small_ang_tol = (REAL) strtod(workstring, (char **) NULL); + } + } + } else if (argv[i][j] == 's') { + psc = 1; + } else if (argv[i][j] == 'Y') { + nobisect = 1; + if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) { + nobisect_nomerge = (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')) { + supsteiner_level = (argv[i][j + 1] - '0'); + j++; + } + } + if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { + j++; + if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) { + addsteiner_algo = (argv[i][j + 1] - '0'); + j++; + } + } + } else if (argv[i][j] == 'r') { + refine = 1; + } else if (argv[i][j] == 'q') { + quality = 1; + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + minratio = (REAL) strtod(workstring, (char **) NULL); + } + if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { + j++; + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + mindihedral = (REAL) strtod(workstring, (char **) NULL); + } + } + } else if (argv[i][j] == 'R') { + coarsen = 1; + if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) { + coarsen_param = (argv[i][j + 1] - '0'); + j++; + } + if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { + j++; + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + coarsen_percent = (REAL) strtod(workstring, (char **) NULL); + } + } + } else if (argv[i][j] == 'w') { + weighted = 1; + if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) { + weighted_param = (argv[i][j + 1] - '0'); + j++; + } + } else if (argv[i][j] == 'b') { + // -b(brio_threshold/brio_ratio/hilbert_limit/hilbert_order) + brio_hilbert = 1; + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + brio_threshold = (int) strtol(workstring, (char **) &workstring, 0); + } + if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { + j++; + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + brio_ratio = (REAL) strtod(workstring, (char **) NULL); + } + } + if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { + j++; + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.') || (argv[i][j + 1] == '-')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.') || (argv[i][j + 1] == '-')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + hilbert_limit = (int) strtol(workstring, (char **) &workstring, 0); + } + } + if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { + j++; + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.') || (argv[i][j + 1] == '-')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.') || (argv[i][j + 1] == '-')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + hilbert_order = (REAL) strtod(workstring, (char **) NULL); + } + } + if (brio_threshold == 0) { // -b0 + brio_hilbert = 0; // Turn off BRIO-Hilbert sorting. + } + if (brio_ratio >= 1.0) { // -b/1 + no_sort = 1; + brio_hilbert = 0; // Turn off BRIO-Hilbert sorting. + } + } else if (argv[i][j] == 'l') { + incrflip = 1; + } else if (argv[i][j] == 'L') { + flipinsert = 1; + } else if (argv[i][j] == 'm') { + metric = 1; + } else if (argv[i][j] == 'a') { + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + fixedvolume = 1; + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.') || (argv[i][j + 1] == 'e') || + (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + maxvolume = (REAL) strtod(workstring, (char **) NULL); + } else { + varvolume = 1; + } + } else if (argv[i][j] == 'A') { + regionattrib = 1; + } else if (argv[i][j] == 'D') { + cdtrefine = 1; + if ((argv[i][j + 1] >= '1') && (argv[i][j + 1] <= '3')) { + reflevel = (argv[i][j + 1] - '1') + 1; + j++; + } + } else if (argv[i][j] == 'i') { + insertaddpoints = 1; + } else if (argv[i][j] == 'd') { + diagnose = 1; + } else if (argv[i][j] == 'c') { + convex = 1; + } else if (argv[i][j] == 'M') { + nomergefacet = 1; + nomergevertex = 1; + if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '1')) { + nomergefacet = (argv[i][j + 1] - '0'); + j++; + } + if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { + j++; + if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '1')) { + nomergevertex = (argv[i][j + 1] - '0'); + j++; + } + } + } else if (argv[i][j] == 'X') { + if (argv[i][j + 1] == '1') { + nostaticfilter = 1; + j++; + } else { + noexact = 1; + } + } else if (argv[i][j] == 'z') { + if (argv[i][j + 1] == '1') { // -z1 + reversetetori = 1; + j++; + } else { + zeroindex = 1; // -z + } + } else if (argv[i][j] == 'f') { + facesout++; + } else if (argv[i][j] == 'e') { + edgesout++; + } else if (argv[i][j] == 'n') { + neighout++; + } else if (argv[i][j] == 'v') { + voroout = 1; + } else if (argv[i][j] == 'g') { + meditview = 1; + } else if (argv[i][j] == 'k') { + vtkview = 1; + } else if (argv[i][j] == 'J') { + nojettison = 1; + } else if (argv[i][j] == 'B') { + nobound = 1; + } else if (argv[i][j] == 'N') { + nonodewritten = 1; + } else if (argv[i][j] == 'E') { + noelewritten = 1; + } else if (argv[i][j] == 'F') { + nofacewritten = 1; + } else if (argv[i][j] == 'I') { + noiterationnum = 1; + } else if (argv[i][j] == 'S') { + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.') || (argv[i][j + 1] == 'e') || + (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + steinerleft = (int) strtol(workstring, (char **) NULL, 0); + } + } else if (argv[i][j] == 'o') { + if (argv[i][j + 1] == '2') { + order = 2; + j++; + } + if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { + j++; + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + optmaxdihedral = (REAL) strtod(workstring, (char **) NULL); + } + } + } else if (argv[i][j] == 'O') { + if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) { + optlevel = (argv[i][j + 1] - '0'); + j++; + } + if ((argv[i][j + 1] == '/') || (argv[i][j + 1] == ',')) { + j++; + if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '7')) { + optscheme = (argv[i][j + 1] - '0'); + j++; + } + } + } else if (argv[i][j] == 'T') { + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.') || (argv[i][j + 1] == 'e') || + (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + epsilon = (REAL) strtod(workstring, (char **) NULL); + } + } else if (argv[i][j] == 'C') { + docheck++; + } else if (argv[i][j] == 'Q') { + quiet = 1; + } else if (argv[i][j] == 'V') { + verbose++; + } else if (argv[i][j] == 'x') { + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.') || (argv[i][j + 1] == 'e') || + (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + tetrahedraperblock = (int) strtol(workstring, (char **) NULL, 0); + if (tetrahedraperblock > 8188) { + vertexperblock = tetrahedraperblock / 2; + shellfaceperblock = vertexperblock / 2; + } else { + tetrahedraperblock = 8188; + } + } + } else if ((argv[i][j] == 'h') || (argv[i][j] == 'H') || + (argv[i][j] == '?')) { + } else { + printf("Warning: Unknown switch -%c.\n", argv[i][j]); + } + } + } + + if (startindex == 0) { + // Set a temporary filename for debugging output. + strcpy(infilename, "tetgen-tmpfile"); + } else { + if (infilename[0] == '\0') { + } + // Recognize the object from file extension if it is available. + if (!strcmp(&infilename[strlen(infilename) - 5], ".node")) { + infilename[strlen(infilename) - 5] = '\0'; + object = NODES; + } else if (!strcmp(&infilename[strlen(infilename) - 5], ".poly")) { + infilename[strlen(infilename) - 5] = '\0'; + object = POLY; + plc = 1; + } else if (!strcmp(&infilename[strlen(infilename) - 6], ".smesh")) { + infilename[strlen(infilename) - 6] = '\0'; + object = POLY; + plc = 1; + } else if (!strcmp(&infilename[strlen(infilename) - 4], ".off")) { + infilename[strlen(infilename) - 4] = '\0'; + object = OFF; + plc = 1; + } else if (!strcmp(&infilename[strlen(infilename) - 4], ".ply")) { + infilename[strlen(infilename) - 4] = '\0'; + object = PLY; + plc = 1; + } else if (!strcmp(&infilename[strlen(infilename) - 4], ".stl")) { + infilename[strlen(infilename) - 4] = '\0'; + object = STL; + plc = 1; + } else if (!strcmp(&infilename[strlen(infilename) - 5], ".mesh")) { + infilename[strlen(infilename) - 5] = '\0'; + object = MEDIT; + if (!refine) plc = 1; + } else if (!strcmp(&infilename[strlen(infilename) - 4], ".vtk")) { + infilename[strlen(infilename) - 4] = '\0'; + object = VTK; + plc = 1; + } else if (!strcmp(&infilename[strlen(infilename) - 4], ".ele")) { + infilename[strlen(infilename) - 4] = '\0'; + object = MESH; + refine = 1; + } + } + + if (nobisect && (!plc && !refine)) { // -Y + plc = 1; // Default -p option. + } + if (quality && (!plc && !refine)) { // -q + plc = 1; // Default -p option. + } + if (diagnose && !plc) { // -d + plc = 1; + } + if (refine && !quality) { // -r only + // Reconstruct a mesh, no mesh optimization. + optlevel = 0; + } + if (insertaddpoints && (optlevel == 0)) { // with -i option + optlevel = 2; + } + if (coarsen && (optlevel == 0)) { // with -R option + optlevel = 2; + } + + // Detect improper combinations of switches. + if ((refine || plc) && weighted) { + printf("Error: Switches -w cannot use together with -p or -r.\n"); + return false; + } + + if (convex) { // -c + if (plc && !regionattrib) { + // -A (region attribute) is needed for marking exterior tets (-1). + regionattrib = 1; + } + } + + // Note: -A must not used together with -r option. + // Be careful not to add an extra attribute to each element unless the + // input supports it (PLC in, but not refining a preexisting mesh). + if (refine || !plc) { + regionattrib = 0; + } + // Be careful not to allocate space for element area constraints that + // will never be assigned any value (other than the default -1.0). + if (!refine && !plc) { + varvolume = 0; + } + // If '-a' or '-aa' is in use, enable '-q' option too. + if (fixedvolume || varvolume) { + if (quality == 0) { + quality = 1; + if (!plc && !refine) { + plc = 1; // enable -p. + } + } + } + // No user-specified dihedral angle bound. Use default ones. + if (!quality) { + if (optmaxdihedral < 179.0) { + if (nobisect) { // with -Y option + optmaxdihedral = 179.0; + } else { // -p only + optmaxdihedral = 179.999; + } + } + if (optminsmtdihed < 179.999) { + optminsmtdihed = 179.999; + } + if (optminslidihed < 179.999) { + optminslidihed = 179.999; + } + } + + increment = 0; + strcpy(workstring, infilename); + j = 1; + while (workstring[j] != '\0') { + if ((workstring[j] == '.') && (workstring[j + 1] != '\0')) { + increment = j + 1; + } + j++; + } + meshnumber = 0; + if (increment > 0) { + j = increment; + do { + if ((workstring[j] >= '0') && (workstring[j] <= '9')) { + meshnumber = meshnumber * 10 + (int) (workstring[j] - '0'); + } else { + increment = 0; + } + j++; + } while (workstring[j] != '\0'); + } + if (noiterationnum) { + strcpy(outfilename, infilename); + } else if (increment == 0) { + strcpy(outfilename, infilename); + strcat(outfilename, ".1"); + } else { + workstring[increment] = '%'; + workstring[increment + 1] = 'd'; + workstring[increment + 2] = '\0'; + sprintf(outfilename, workstring, meshnumber + 1); + } + // Additional input file name has the end ".a". + strcpy(addinfilename, infilename); + strcat(addinfilename, ".a"); + // Background filename has the form "*.b.ele", "*.b.node", ... + strcpy(bgmeshfilename, infilename); + strcat(bgmeshfilename, ".b"); + + return true; +} + +//// //// +//// //// +//// behavior_cxx ///////////////////////////////////////////////////////////// + +//// mempool_cxx ////////////////////////////////////////////////////////////// +//// //// +//// //// + +// Initialize fast lookup tables for mesh maniplulation primitives. + +int tetgenmesh::bondtbl[12][12] = {{0,},}; +int tetgenmesh::enexttbl[12] = {0,}; +int tetgenmesh::eprevtbl[12] = {0,}; +int tetgenmesh::enextesymtbl[12] = {0,}; +int tetgenmesh::eprevesymtbl[12] = {0,}; +int tetgenmesh::eorgoppotbl[12] = {0,}; +int tetgenmesh::edestoppotbl[12] = {0,}; +int tetgenmesh::fsymtbl[12][12] = {{0,},}; +int tetgenmesh::facepivot1[12] = {0,}; +int tetgenmesh::facepivot2[12][12] = {{0,},}; +int tetgenmesh::tsbondtbl[12][6] = {{0,},}; +int tetgenmesh::stbondtbl[12][6] = {{0,},}; +int tetgenmesh::tspivottbl[12][6] = {{0,},}; +int tetgenmesh::stpivottbl[12][6] = {{0,},}; + +// Table 'esymtbl' takes an directed edge (version) as input, returns the +// inversed edge (version) of it. + +int tetgenmesh::esymtbl[12] = {9, 6, 11, 4, 3, 7, 1, 5, 10, 0, 8, 2}; + +// The following four tables give the 12 permutations of the set {0,1,2,3}. +// An offset 4 is added to each element for a direct access of the points +// in the tetrahedron data structure. + +int tetgenmesh:: orgpivot[12] = {7, 7, 5, 5, 6, 4, 4, 6, 5, 6, 7, 4}; +int tetgenmesh::destpivot[12] = {6, 4, 4, 6, 5, 6, 7, 4, 7, 7, 5, 5}; +int tetgenmesh::apexpivot[12] = {5, 6, 7, 4, 7, 7, 5, 5, 6, 4, 4, 6}; +int tetgenmesh::oppopivot[12] = {4, 5, 6, 7, 4, 5, 6, 7, 4, 5, 6, 7}; + +// The twelve versions correspond to six undirected edges. The following two +// tables map a version to an undirected edge and vice versa. + +int tetgenmesh::ver2edge[12] = {0, 1, 2, 3, 3, 5, 1, 5, 4, 0, 4, 2}; +int tetgenmesh::edge2ver[ 6] = {0, 1, 2, 3, 8, 5}; + +// Edge versions whose apex or opposite may be dummypoint. + +int tetgenmesh::epivot[12] = {4, 5, 2, 11, 4, 5, 2, 11, 4, 5, 2, 11}; + + +// Table 'snextpivot' takes an edge version as input, returns the next edge +// version in the same edge ring. + +int tetgenmesh::snextpivot[6] = {2, 5, 4, 1, 0, 3}; + +// The following three tables give the 6 permutations of the set {0,1,2}. +// An offset 3 is added to each element for a direct access of the points +// in the triangle data structure. + +int tetgenmesh::sorgpivot [6] = {3, 4, 4, 5, 5, 3}; +int tetgenmesh::sdestpivot[6] = {4, 3, 5, 4, 3, 5}; +int tetgenmesh::sapexpivot[6] = {5, 5, 3, 3, 4, 4}; + +/////////////////////////////////////////////////////////////////////////////// +// // +// inittable() Initialize the look-up tables. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::inittables() +{ + int soffset, toffset; + int i, j; + + + // i = t1.ver; j = t2.ver; + for (i = 0; i < 12; i++) { + for (j = 0; j < 12; j++) { + bondtbl[i][j] = (j & 3) + (((i & 12) + (j & 12)) % 12); + } + } + + + // i = t1.ver; j = t2.ver + for (i = 0; i < 12; i++) { + for (j = 0; j < 12; j++) { + fsymtbl[i][j] = (j + 12 - (i & 12)) % 12; + } + } + + + for (i = 0; i < 12; i++) { + facepivot1[i] = (esymtbl[i] & 3); + } + + for (i = 0; i < 12; i++) { + for (j = 0; j < 12; j++) { + facepivot2[i][j] = fsymtbl[esymtbl[i]][j]; + } + } + + for (i = 0; i < 12; i++) { + enexttbl[i] = (i + 4) % 12; + eprevtbl[i] = (i + 8) % 12; + } + + for (i = 0; i < 12; i++) { + enextesymtbl[i] = esymtbl[enexttbl[i]]; + eprevesymtbl[i] = esymtbl[eprevtbl[i]]; + } + + for (i = 0; i < 12; i++) { + eorgoppotbl [i] = eprevtbl[esymtbl[enexttbl[i]]]; + edestoppotbl[i] = enexttbl[esymtbl[eprevtbl[i]]]; + } + + // i = t.ver, j = s.shver + for (i = 0; i < 12; i++) { + for (j = 0; j < 6; j++) { + if ((j & 1) == 0) { + soffset = (6 - ((i & 12) >> 1)) % 6; + toffset = (12 - ((j & 6) << 1)) % 12; + } else { + soffset = (i & 12) >> 1; + toffset = (j & 6) << 1; + } + tsbondtbl[i][j] = (j & 1) + (((j & 6) + soffset) % 6); + stbondtbl[i][j] = (i & 3) + (((i & 12) + toffset) % 12); + } + } + + + // i = t.ver, j = s.shver + for (i = 0; i < 12; i++) { + for (j = 0; j < 6; j++) { + if ((j & 1) == 0) { + soffset = (i & 12) >> 1; + toffset = (j & 6) << 1; + } else { + soffset = (6 - ((i & 12) >> 1)) % 6; + toffset = (12 - ((j & 6) << 1)) % 12; + } + tspivottbl[i][j] = (j & 1) + (((j & 6) + soffset) % 6); + stpivottbl[i][j] = (i & 3) + (((i & 12) + toffset) % 12); + } + } +} + + +/////////////////////////////////////////////////////////////////////////////// +// // +// restart() Deallocate all objects in this pool. // +// // +// The pool returns to a fresh state, like after it was initialized, except // +// that no memory is freed to the operating system. Rather, the previously // +// allocated blocks are ready to be used. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::arraypool::restart() +{ + objects = 0l; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// poolinit() Initialize an arraypool for allocation of objects. // +// // +// Before the pool may be used, it must be initialized by this procedure. // +// After initialization, memory can be allocated and freed in this pool. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::arraypool::poolinit(int sizeofobject, int log2objperblk) +{ + // Each object must be at least one byte long. + objectbytes = sizeofobject > 1 ? sizeofobject : 1; + + log2objectsperblock = log2objperblk; + // Compute the number of objects in each block. + objectsperblock = ((int) 1) << log2objectsperblock; + objectsperblockmark = objectsperblock - 1; + + // No memory has been allocated. + totalmemory = 0l; + // The top array has not been allocated yet. + toparray = (char **) NULL; + toparraylen = 0; + + // Ready all indices to be allocated. + restart(); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// arraypool() The constructor and destructor. // +// // +/////////////////////////////////////////////////////////////////////////////// + +tetgenmesh::arraypool::arraypool(int sizeofobject, int log2objperblk) +{ + poolinit(sizeofobject, log2objperblk); +} + +tetgenmesh::arraypool::~arraypool() +{ + int i; + + // Has anything been allocated at all? + if (toparray != (char **) NULL) { + // Walk through the top array. + for (i = 0; i < toparraylen; i++) { + // Check every pointer; NULLs may be scattered randomly. + if (toparray[i] != (char *) NULL) { + // Free an allocated block. + free((void *) toparray[i]); + } + } + // Free the top array. + free((void *) toparray); + } + + // The top array is no longer allocated. + toparray = (char **) NULL; + toparraylen = 0; + objects = 0; + totalmemory = 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// getblock() Return (and perhaps create) the block containing the object // +// with a given index. // +// // +// This function takes care of allocating or resizing the top array if nece- // +// ssary, and of allocating the block if it hasn't yet been allocated. // +// // +// Return a pointer to the beginning of the block (NOT the object). // +// // +/////////////////////////////////////////////////////////////////////////////// + +char* tetgenmesh::arraypool::getblock(int objectindex) +{ + char **newarray; + char *block; + int newsize; + int topindex; + int i; + + // Compute the index in the top array (upper bits). + topindex = objectindex >> log2objectsperblock; + // Does the top array need to be allocated or resized? + if (toparray == (char **) NULL) { + // Allocate the top array big enough to hold 'topindex', and NULL out + // its contents. + newsize = topindex + 128; + toparray = (char **) malloc((size_t) (newsize * sizeof(char *))); + toparraylen = newsize; + for (i = 0; i < newsize; i++) { + toparray[i] = (char *) NULL; + } + // Account for the memory. + totalmemory = newsize * (uintptr_t) sizeof(char *); + } else if (topindex >= toparraylen) { + // Resize the top array, making sure it holds 'topindex'. + newsize = 3 * toparraylen; + if (topindex >= newsize) { + newsize = topindex + 128; + } + // Allocate the new array, copy the contents, NULL out the rest, and + // free the old array. + newarray = (char **) malloc((size_t) (newsize * sizeof(char *))); + for (i = 0; i < toparraylen; i++) { + newarray[i] = toparray[i]; + } + for (i = toparraylen; i < newsize; i++) { + newarray[i] = (char *) NULL; + } + free(toparray); + // Account for the memory. + totalmemory += (newsize - toparraylen) * sizeof(char *); + toparray = newarray; + toparraylen = newsize; + } + + // Find the block, or learn that it hasn't been allocated yet. + block = toparray[topindex]; + if (block == (char *) NULL) { + // Allocate a block at this index. + block = (char *) malloc((size_t) (objectsperblock * objectbytes)); + toparray[topindex] = block; + // Account for the memory. + totalmemory += objectsperblock * objectbytes; + } + + // Return a pointer to the block. + return block; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// lookup() Return the pointer to the object with a given index, or NULL // +// if the object's block doesn't exist yet. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void* tetgenmesh::arraypool::lookup(int objectindex) +{ + char *block; + int topindex; + + // Has the top array been allocated yet? + if (toparray == (char **) NULL) { + return (void *) NULL; + } + + // Compute the index in the top array (upper bits). + topindex = objectindex >> log2objectsperblock; + // Does the top index fit in the top array? + if (topindex >= toparraylen) { + return (void *) NULL; + } + + // Find the block, or learn that it hasn't been allocated yet. + block = toparray[topindex]; + if (block == (char *) NULL) { + return (void *) NULL; + } + + // Compute a pointer to the object with the given index. Note that + // 'objectsperblock' is a power of two, so the & operation is a bit mask + // that preserves the lower bits. + return (void *)(block + (objectindex & (objectsperblock - 1)) * objectbytes); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// newindex() Allocate space for a fresh object from the pool. // +// // +// 'newptr' returns a pointer to the new object (it must not be a NULL). // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::arraypool::newindex(void **newptr) +{ + // Allocate an object at index 'firstvirgin'. + int newindex = objects; + *newptr = (void *) (getblock(objects) + + (objects & (objectsperblock - 1)) * objectbytes); + objects++; + + return newindex; +} + + +/////////////////////////////////////////////////////////////////////////////// +// // +// memorypool() The constructors of memorypool. // +// // +/////////////////////////////////////////////////////////////////////////////// + +tetgenmesh::memorypool::memorypool() +{ + firstblock = nowblock = (void **) NULL; + nextitem = (void *) NULL; + deaditemstack = (void *) NULL; + pathblock = (void **) NULL; + pathitem = (void *) NULL; + alignbytes = 0; + itembytes = itemwords = 0; + itemsperblock = 0; + items = maxitems = 0l; + unallocateditems = 0; + pathitemsleft = 0; +} + +tetgenmesh::memorypool::memorypool(int bytecount, int itemcount, int wsize, + int alignment) +{ + poolinit(bytecount, itemcount, wsize, alignment); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// ~memorypool() Free to the operating system all memory taken by a pool. // +// // +/////////////////////////////////////////////////////////////////////////////// + +tetgenmesh::memorypool::~memorypool() +{ + while (firstblock != (void **) NULL) { + nowblock = (void **) *(firstblock); + free(firstblock); + firstblock = nowblock; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// poolinit() Initialize a pool of memory for allocation of items. // +// // +// A `pool' is created whose records have size at least `bytecount'. Items // +// will be allocated in `itemcount'-item blocks. Each item is assumed to be // +// a collection of words, and either pointers or floating-point values are // +// assumed to be the "primary" word type. (The "primary" word type is used // +// to determine alignment of items.) If `alignment' isn't zero, all items // +// will be `alignment'-byte aligned in memory. `alignment' must be either a // +// multiple or a factor of the primary word size; powers of two are safe. // +// `alignment' is normally used to create a few unused bits at the bottom of // +// each item's pointer, in which information may be stored. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::memorypool::poolinit(int bytecount,int itemcount,int wordsize, + int alignment) +{ + // Find the proper alignment, which must be at least as large as: + // - The parameter `alignment'. + // - The primary word type, to avoid unaligned accesses. + // - sizeof(void *), so the stack of dead items can be maintained + // without unaligned accesses. + if (alignment > wordsize) { + alignbytes = alignment; + } else { + alignbytes = wordsize; + } + if ((int) sizeof(void *) > alignbytes) { + alignbytes = (int) sizeof(void *); + } + itemwords = ((bytecount + alignbytes - 1) / alignbytes) + * (alignbytes / wordsize); + itembytes = itemwords * wordsize; + itemsperblock = itemcount; + + // Allocate a block of items. Space for `itemsperblock' items and one + // pointer (to point to the next block) are allocated, as well as space + // to ensure alignment of the items. + firstblock = (void **) malloc(itemsperblock * itembytes + sizeof(void *) + + alignbytes); + if (firstblock == (void **) NULL) { + terminatetetgen(NULL, 1); + } + // Set the next block pointer to NULL. + *(firstblock) = (void *) NULL; + restart(); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// restart() Deallocate all items in this pool. // +// // +// The pool is returned to its starting state, except that no memory is // +// freed to the operating system. Rather, the previously allocated blocks // +// are ready to be reused. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::memorypool::restart() +{ + uintptr_t alignptr; + + items = 0; + maxitems = 0; + + // Set the currently active block. + nowblock = firstblock; + // Find the first item in the pool. Increment by the size of (void *). + alignptr = (uintptr_t) (nowblock + 1); + // Align the item on an `alignbytes'-byte boundary. + nextitem = (void *) + (alignptr + (uintptr_t) alignbytes - + (alignptr % (uintptr_t) alignbytes)); + // There are lots of unallocated items left in this block. + unallocateditems = itemsperblock; + // The stack of deallocated items is empty. + deaditemstack = (void *) NULL; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// alloc() Allocate space for an item. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void* tetgenmesh::memorypool::alloc() +{ + void *newitem; + void **newblock; + uintptr_t alignptr; + + // First check the linked list of dead items. If the list is not + // empty, allocate an item from the list rather than a fresh one. + if (deaditemstack != (void *) NULL) { + newitem = deaditemstack; // Take first item in list. + deaditemstack = * (void **) deaditemstack; + } else { + // Check if there are any free items left in the current block. + if (unallocateditems == 0) { + // Check if another block must be allocated. + if (*nowblock == (void *) NULL) { + // Allocate a new block of items, pointed to by the previous block. + newblock = (void **) malloc(itemsperblock * itembytes + sizeof(void *) + + alignbytes); + if (newblock == (void **) NULL) { + terminatetetgen(NULL, 1); + } + *nowblock = (void *) newblock; + // The next block pointer is NULL. + *newblock = (void *) NULL; + } + // Move to the new block. + nowblock = (void **) *nowblock; + // Find the first item in the block. + // Increment by the size of (void *). + alignptr = (uintptr_t) (nowblock + 1); + // Align the item on an `alignbytes'-byte boundary. + nextitem = (void *) + (alignptr + (uintptr_t) alignbytes - + (alignptr % (uintptr_t) alignbytes)); + // There are lots of unallocated items left in this block. + unallocateditems = itemsperblock; + } + // Allocate a new item. + newitem = nextitem; + // Advance `nextitem' pointer to next free item in block. + nextitem = (void *) ((uintptr_t) nextitem + itembytes); + unallocateditems--; + maxitems++; + } + items++; + return newitem; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// dealloc() Deallocate space for an item. // +// // +// The deallocated space is stored in a queue for later reuse. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::memorypool::dealloc(void *dyingitem) +{ + // Push freshly killed item onto stack. + *((void **) dyingitem) = deaditemstack; + deaditemstack = dyingitem; + items--; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// traversalinit() Prepare to traverse the entire list of items. // +// // +// This routine is used in conjunction with traverse(). // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::memorypool::traversalinit() +{ + uintptr_t alignptr; + + // Begin the traversal in the first block. + pathblock = firstblock; + // Find the first item in the block. Increment by the size of (void *). + alignptr = (uintptr_t) (pathblock + 1); + // Align with item on an `alignbytes'-byte boundary. + pathitem = (void *) + (alignptr + (uintptr_t) alignbytes - + (alignptr % (uintptr_t) alignbytes)); + // Set the number of items left in the current block. + pathitemsleft = itemsperblock; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// traverse() Find the next item in the list. // +// // +// This routine is used in conjunction with traversalinit(). Be forewarned // +// that this routine successively returns all items in the list, including // +// deallocated ones on the deaditemqueue. It's up to you to figure out which // +// ones are actually dead. It can usually be done more space-efficiently by // +// a routine that knows something about the structure of the item. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void* tetgenmesh::memorypool::traverse() +{ + void *newitem; + uintptr_t alignptr; + + // Stop upon exhausting the list of items. + if (pathitem == nextitem) { + return (void *) NULL; + } + // Check whether any untraversed items remain in the current block. + if (pathitemsleft == 0) { + // Find the next block. + pathblock = (void **) *pathblock; + // Find the first item in the block. Increment by the size of (void *). + alignptr = (uintptr_t) (pathblock + 1); + // Align with item on an `alignbytes'-byte boundary. + pathitem = (void *) + (alignptr + (uintptr_t) alignbytes - + (alignptr % (uintptr_t) alignbytes)); + // Set the number of items left in the current block. + pathitemsleft = itemsperblock; + } + newitem = pathitem; + // Find the next item in the block. + pathitem = (void *) ((uintptr_t) pathitem + itembytes); + pathitemsleft--; + return newitem; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// makeindex2pointmap() Create a map from index to vertices. // +// // +// 'idx2verlist' returns the created map. Traverse all vertices, a pointer // +// to each vertex is set into the array. The pointer to the first vertex is // +// saved in 'idx2verlist[in->firstnumber]'. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::makeindex2pointmap(point*& idx2verlist) +{ + point pointloop; + int idx; + + if (b->verbose > 1) { + printf(" Constructing mapping from indices to points.\n"); + } + + idx2verlist = new point[points->items + 1]; + + points->traversalinit(); + pointloop = pointtraverse(); + idx = in->firstnumber; + while (pointloop != (point) NULL) { + idx2verlist[idx++] = pointloop; + pointloop = pointtraverse(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// makesubfacemap() Create a map from vertex to subfaces incident at it. // +// // +// The map is returned in two arrays 'idx2faclist' and 'facperverlist'. All // +// subfaces incident at i-th vertex (i is counted from 0) are found in the // +// array facperverlist[j], where idx2faclist[i] <= j < idx2faclist[i + 1]. // +// Each entry in facperverlist[j] is a subface whose origin is the vertex. // +// // +// NOTE: These two arrays will be created inside this routine, don't forget // +// to free them after using. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::makepoint2submap(memorypool* pool, int*& idx2faclist, + face*& facperverlist) +{ + face shloop; + int i, j, k; + + if (b->verbose > 1) { + printf(" Making a map from points to subfaces.\n"); + } + + // Initialize 'idx2faclist'. + idx2faclist = new int[points->items + 1]; + for (i = 0; i < points->items + 1; i++) idx2faclist[i] = 0; + + // Loop all subfaces, counter the number of subfaces incident at a vertex. + pool->traversalinit(); + shloop.sh = shellfacetraverse(pool); + while (shloop.sh != (shellface *) NULL) { + // Increment the number of incident subfaces for each vertex. + j = pointmark((point) shloop.sh[3]) - in->firstnumber; + idx2faclist[j]++; + j = pointmark((point) shloop.sh[4]) - in->firstnumber; + idx2faclist[j]++; + // Skip the third corner if it is a segment. + if (shloop.sh[5] != NULL) { + j = pointmark((point) shloop.sh[5]) - in->firstnumber; + idx2faclist[j]++; + } + shloop.sh = shellfacetraverse(pool); + } + + // Calculate the total length of array 'facperverlist'. + j = idx2faclist[0]; + idx2faclist[0] = 0; // Array starts from 0 element. + for (i = 0; i < points->items; i++) { + k = idx2faclist[i + 1]; + idx2faclist[i + 1] = idx2faclist[i] + j; + j = k; + } + + // The total length is in the last unit of idx2faclist. + facperverlist = new face[idx2faclist[i]]; + + // Loop all subfaces again, remember the subfaces at each vertex. + pool->traversalinit(); + shloop.sh = shellfacetraverse(pool); + while (shloop.sh != (shellface *) NULL) { + j = pointmark((point) shloop.sh[3]) - in->firstnumber; + shloop.shver = 0; // save the origin. + facperverlist[idx2faclist[j]] = shloop; + idx2faclist[j]++; + // Is it a subface or a subsegment? + if (shloop.sh[5] != NULL) { + j = pointmark((point) shloop.sh[4]) - in->firstnumber; + shloop.shver = 2; // save the origin. + facperverlist[idx2faclist[j]] = shloop; + idx2faclist[j]++; + j = pointmark((point) shloop.sh[5]) - in->firstnumber; + shloop.shver = 4; // save the origin. + facperverlist[idx2faclist[j]] = shloop; + idx2faclist[j]++; + } else { + j = pointmark((point) shloop.sh[4]) - in->firstnumber; + shloop.shver = 1; // save the origin. + facperverlist[idx2faclist[j]] = shloop; + idx2faclist[j]++; + } + shloop.sh = shellfacetraverse(pool); + } + + // Contents in 'idx2faclist' are shifted, now shift them back. + for (i = points->items - 1; i >= 0; i--) { + idx2faclist[i + 1] = idx2faclist[i]; + } + idx2faclist[0] = 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// tetrahedrondealloc() Deallocate space for a tet., marking it dead. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::tetrahedrondealloc(tetrahedron *dyingtetrahedron) +{ + // Set tetrahedron's vertices to NULL. This makes it possible to detect + // dead tetrahedra when traversing the list of all tetrahedra. + dyingtetrahedron[4] = (tetrahedron) NULL; + + // Dealloc the space to subfaces/subsegments. + if (dyingtetrahedron[8] != NULL) { + tet2segpool->dealloc((shellface *) dyingtetrahedron[8]); + } + if (dyingtetrahedron[9] != NULL) { + tet2subpool->dealloc((shellface *) dyingtetrahedron[9]); + } + + tetrahedrons->dealloc((void *) dyingtetrahedron); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// tetrahedrontraverse() Traverse the tetrahedra, skipping dead ones. // +// // +/////////////////////////////////////////////////////////////////////////////// + +tetgenmesh::tetrahedron* tetgenmesh::tetrahedrontraverse() +{ + tetrahedron *newtetrahedron; + + do { + newtetrahedron = (tetrahedron *) tetrahedrons->traverse(); + if (newtetrahedron == (tetrahedron *) NULL) { + return (tetrahedron *) NULL; + } + } while ((newtetrahedron[4] == (tetrahedron) NULL) || + ((point) newtetrahedron[7] == dummypoint)); + return newtetrahedron; +} + +tetgenmesh::tetrahedron* tetgenmesh::alltetrahedrontraverse() +{ + tetrahedron *newtetrahedron; + + do { + newtetrahedron = (tetrahedron *) tetrahedrons->traverse(); + if (newtetrahedron == (tetrahedron *) NULL) { + return (tetrahedron *) NULL; + } + } while (newtetrahedron[4] == (tetrahedron) NULL); // Skip dead ones. + return newtetrahedron; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// shellfacedealloc() Deallocate space for a shellface, marking it dead. // +// Used both for dealloc a subface and subsegment. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::shellfacedealloc(memorypool *pool, shellface *dyingsh) +{ + // Set shellface's vertices to NULL. This makes it possible to detect dead + // shellfaces when traversing the list of all shellfaces. + dyingsh[3] = (shellface) NULL; + pool->dealloc((void *) dyingsh); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// shellfacetraverse() Traverse the subfaces, skipping dead ones. Used // +// for both subfaces and subsegments pool traverse. // +// // +/////////////////////////////////////////////////////////////////////////////// + +tetgenmesh::shellface* tetgenmesh::shellfacetraverse(memorypool *pool) +{ + shellface *newshellface; + + do { + newshellface = (shellface *) pool->traverse(); + if (newshellface == (shellface *) NULL) { + return (shellface *) NULL; + } + } while (newshellface[3] == (shellface) NULL); // Skip dead ones. + return newshellface; +} + + +/////////////////////////////////////////////////////////////////////////////// +// // +// pointdealloc() Deallocate space for a point, marking it dead. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::pointdealloc(point dyingpoint) +{ + // Mark the point as dead. This makes it possible to detect dead points + // when traversing the list of all points. + setpointtype(dyingpoint, DEADVERTEX); + points->dealloc((void *) dyingpoint); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// pointtraverse() Traverse the points, skipping dead ones. // +// // +/////////////////////////////////////////////////////////////////////////////// + +tetgenmesh::point tetgenmesh::pointtraverse() +{ + point newpoint; + + do { + newpoint = (point) points->traverse(); + if (newpoint == (point) NULL) { + return (point) NULL; + } + } while (pointtype(newpoint) == DEADVERTEX); // Skip dead ones. + return newpoint; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// maketetrahedron() Create a new tetrahedron. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::maketetrahedron(triface *newtet) +{ + newtet->tet = (tetrahedron *) tetrahedrons->alloc(); + + // Initialize the four adjoining tetrahedra to be "outer space". + newtet->tet[0] = NULL; + newtet->tet[1] = NULL; + newtet->tet[2] = NULL; + newtet->tet[3] = NULL; + // Four NULL vertices. + newtet->tet[4] = NULL; + newtet->tet[5] = NULL; + newtet->tet[6] = NULL; + newtet->tet[7] = NULL; + // No attached segments and subfaces yet. + newtet->tet[8] = NULL; + newtet->tet[9] = NULL; + // Initialize the marker (clear all flags). + setelemmarker(newtet->tet, 0); + for (int i = 0; i < numelemattrib; i++) { + setelemattribute(newtet->tet, i, 0.0); + } + if (b->varvolume) { + setvolumebound(newtet->tet, -1.0); + } + + // Initialize the version to be Zero. + newtet->ver = 11; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// makeshellface() Create a new shellface with version zero. Used for // +// both subfaces and subsegments. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::makeshellface(memorypool *pool, face *newface) +{ + newface->sh = (shellface *) pool->alloc(); + + // No adjointing subfaces. + newface->sh[0] = NULL; + newface->sh[1] = NULL; + newface->sh[2] = NULL; + // Three NULL vertices. + newface->sh[3] = NULL; + newface->sh[4] = NULL; + newface->sh[5] = NULL; + // No adjoining subsegments. + newface->sh[6] = NULL; + newface->sh[7] = NULL; + newface->sh[8] = NULL; + // No adjoining tetrahedra. + newface->sh[9] = NULL; + newface->sh[10] = NULL; + if (checkconstraints) { + // Initialize the maximum area bound. + setareabound(*newface, 0.0); + } + // Set the boundary marker to zero. + setshellmark(*newface, 0); + // Clear the infection and marktest bits. + ((int *) (newface->sh))[shmarkindex + 1] = 0; + if (useinsertradius) { + setfacetindex(*newface, 0); + } + + newface->shver = 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// makepoint() Create a new point. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::makepoint(point* pnewpoint, enum verttype vtype) +{ + int i; + + *pnewpoint = (point) points->alloc(); + + // Initialize the point attributes. + for (i = 0; i < numpointattrib; i++) { + (*pnewpoint)[3 + i] = 0.0; + } + // Initialize the metric tensor. + for (i = 0; i < sizeoftensor; i++) { + (*pnewpoint)[pointmtrindex + i] = 0.0; + } + setpoint2tet(*pnewpoint, NULL); + setpoint2ppt(*pnewpoint, NULL); + if (b->plc || b->refine) { + // Initialize the point-to-simplex field. + setpoint2sh(*pnewpoint, NULL); + if (b->metric && (bgm != NULL)) { + setpoint2bgmtet(*pnewpoint, NULL); + } + } + // Initialize the point marker (starting from in->firstnumber). + setpointmark(*pnewpoint, (int) (points->items) - (!in->firstnumber)); + // Clear all flags. + ((int *) (*pnewpoint))[pointmarkindex + 1] = 0; + // Initialize (set) the point type. + setpointtype(*pnewpoint, vtype); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// initializepools() Calculate the sizes of the point, tetrahedron, and // +// subface. Initialize their memory pools. // +// // +// This routine also computes the indices 'pointmarkindex', 'point2simindex',// +// 'point2pbcptindex', 'elemattribindex', and 'volumeboundindex'. They are // +// used to find values within each point and tetrahedron, respectively. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::initializepools() +{ + int pointsize = 0, elesize = 0, shsize = 0; + int i; + + if (b->verbose) { + printf(" Initializing memorypools.\n"); + printf(" tetrahedron per block: %d.\n", b->tetrahedraperblock); + } + + inittables(); + + // There are three input point lists available, which are in, addin, + // and bgm->in. These point lists may have different number of + // attributes. Decide the maximum number. + numpointattrib = in->numberofpointattributes; + if (bgm != NULL) { + if (bgm->in->numberofpointattributes > numpointattrib) { + numpointattrib = bgm->in->numberofpointattributes; + } + } + if (addin != NULL) { + if (addin->numberofpointattributes > numpointattrib) { + numpointattrib = addin->numberofpointattributes; + } + } + if (b->weighted || b->flipinsert) { // -w or -L. + // The internal number of point attribute needs to be at least 1 + // (for storing point weights). + if (numpointattrib == 0) { + numpointattrib = 1; + } + } + + // Default varconstraint = 0; + if (in->segmentconstraintlist || in->facetconstraintlist) { + checkconstraints = 1; + } + if (b->plc || b->refine) { + // Save the insertion radius for Steiner points if boundaries + // are allowed be split. + if (!b->nobisect || checkconstraints) { + useinsertradius = 1; + } + } + + // The index within each point at which its metric tensor is found. + // Each vertex has three coordinates. + if (b->psc) { + // '-s' option (PSC), the u,v coordinates are provided. + pointmtrindex = 5 + numpointattrib; + // The index within each point at which its u, v coordinates are found. + // Comment: They are saved after the list of point attributes. + pointparamindex = pointmtrindex - 2; + } else { + pointmtrindex = 3 + numpointattrib; + } + // For '-m' option. A tensor field is provided (*.mtr or *.b.mtr file). + if (b->metric) { + // Decide the size (1, 3, or 6) of the metric tensor. + if (bgm != (tetgenmesh *) NULL) { + // A background mesh is allocated. It may not exist though. + sizeoftensor = (bgm->in != (tetgenio *) NULL) ? + bgm->in->numberofpointmtrs : in->numberofpointmtrs; + } else { + // No given background mesh - Itself is a background mesh. + sizeoftensor = in->numberofpointmtrs; + } + // Make sure sizeoftensor is at least 1. + sizeoftensor = (sizeoftensor > 0) ? sizeoftensor : 1; + } else { + // For '-q' option. Make sure to have space for saving a scalar value. + sizeoftensor = b->quality ? 1 : 0; + } + if (useinsertradius) { + // Increase a space (REAL) for saving point insertion radius, it is + // saved directly after the metric. + sizeoftensor++; + } + pointinsradiusindex = pointmtrindex + sizeoftensor - 1; + // The index within each point at which an element pointer is found, where + // the index is measured in pointers. Ensure the index is aligned to a + // sizeof(tetrahedron)-byte address. + point2simindex = ((pointmtrindex + sizeoftensor) * sizeof(REAL) + + sizeof(tetrahedron) - 1) / sizeof(tetrahedron); + if (b->plc || b->refine || b->voroout) { + // Increase the point size by three pointers, which are: + // - a pointer to a tet, read by point2tet(); + // - a pointer to a parent point, read by point2ppt()). + // - a pointer to a subface or segment, read by point2sh(); + if (b->metric && (bgm != (tetgenmesh *) NULL)) { + // Increase one pointer into the background mesh, point2bgmtet(). + pointsize = (point2simindex + 4) * sizeof(tetrahedron); + } else { + pointsize = (point2simindex + 3) * sizeof(tetrahedron); + } + } else { + // Increase the point size by two pointer, which are: + // - a pointer to a tet, read by point2tet(); + // - a pointer to a parent point, read by point2ppt()). -- Used by btree. + pointsize = (point2simindex + 2) * sizeof(tetrahedron); + } + // The index within each point at which the boundary marker is found, + // Ensure the point marker is aligned to a sizeof(int)-byte address. + pointmarkindex = (pointsize + sizeof(int) - 1) / sizeof(int); + // Now point size is the ints (indicated by pointmarkindex) plus: + // - an integer for boundary marker; + // - an integer for vertex type; + // - an integer for geometry tag (optional, -s option). + pointsize = (pointmarkindex + 2 + (b->psc ? 1 : 0)) * sizeof(tetrahedron); + + // Initialize the pool of vertices. + points = new memorypool(pointsize, b->vertexperblock, sizeof(REAL), 0); + + if (b->verbose) { + printf(" Size of a point: %d bytes.\n", points->itembytes); + } + + // Initialize the infinite vertex. + dummypoint = (point) new char[pointsize]; + // Initialize all fields of this point. + dummypoint[0] = 0.0; + dummypoint[1] = 0.0; + dummypoint[2] = 0.0; + for (i = 0; i < numpointattrib; i++) { + dummypoint[3 + i] = 0.0; + } + // Initialize the metric tensor. + for (i = 0; i < sizeoftensor; i++) { + dummypoint[pointmtrindex + i] = 0.0; + } + setpoint2tet(dummypoint, NULL); + setpoint2ppt(dummypoint, NULL); + if (b->plc || b->psc || b->refine) { + // Initialize the point-to-simplex field. + setpoint2sh(dummypoint, NULL); + if (b->metric && (bgm != NULL)) { + setpoint2bgmtet(dummypoint, NULL); + } + } + // Initialize the point marker (starting from in->firstnumber). + setpointmark(dummypoint, -1); // The unique marker for dummypoint. + // Clear all flags. + ((int *) (dummypoint))[pointmarkindex + 1] = 0; + // Initialize (set) the point type. + setpointtype(dummypoint, UNUSEDVERTEX); // Does not matter. + + // The number of bytes occupied by a tetrahedron is varying by the user- + // specified options. The contents of the first 12 pointers are listed + // in the following table: + // [0] |__ neighbor at f0 __| + // [1] |__ neighbor at f1 __| + // [2] |__ neighbor at f2 __| + // [3] |__ neighbor at f3 __| + // [4] |_____ vertex p0 ____| + // [5] |_____ vertex p1 ____| + // [6] |_____ vertex p2 ____| + // [7] |_____ vertex p3 ____| + // [8] |__ segments array __| (used by -p) + // [9] |__ subfaces array __| (used by -p) + // [10] |_____ reserved _____| + // [11] |___ elem marker ____| (used as an integer) + + elesize = 12 * sizeof(tetrahedron); + + // The index to find the element markers. An integer containing varies + // flags and element counter. + if (!(sizeof(int) <= sizeof(tetrahedron)) || + ((sizeof(tetrahedron) % sizeof(int)))) { + terminatetetgen(this, 2); + } + elemmarkerindex = (elesize - sizeof(tetrahedron)) / sizeof(int); + + // The actual number of element attributes. Note that if the + // `b->regionattrib' flag is set, an additional attribute will be added. + numelemattrib = in->numberoftetrahedronattributes + (b->regionattrib > 0); + + // The index within each element at which its attributes are found, where + // the index is measured in REALs. + elemattribindex = (elesize + sizeof(REAL) - 1) / sizeof(REAL); + // The index within each element at which the maximum volume bound is + // found, where the index is measured in REALs. + volumeboundindex = elemattribindex + numelemattrib; + // If element attributes or an constraint are needed, increase the number + // of bytes occupied by an element. + if (b->varvolume) { + elesize = (volumeboundindex + 1) * sizeof(REAL); + } else if (numelemattrib > 0) { + elesize = volumeboundindex * sizeof(REAL); + } + + + // Having determined the memory size of an element, initialize the pool. + tetrahedrons = new memorypool(elesize, b->tetrahedraperblock, sizeof(void *), + 16); + + if (b->verbose) { + printf(" Size of a tetrahedron: %d (%d) bytes.\n", elesize, + tetrahedrons->itembytes); + } + + if (b->plc || b->refine) { // if (b->useshelles) { + // The number of bytes occupied by a subface. The list of pointers + // stored in a subface are: three to other subfaces, three to corners, + // three to subsegments, two to tetrahedra. + shsize = 11 * sizeof(shellface); + // The index within each subface at which the maximum area bound is + // found, where the index is measured in REALs. + areaboundindex = (shsize + sizeof(REAL) - 1) / sizeof(REAL); + // If -q switch is in use, increase the number of bytes occupied by + // a subface for saving maximum area bound. + if (checkconstraints) { + shsize = (areaboundindex + 1) * sizeof(REAL); + } else { + shsize = areaboundindex * sizeof(REAL); + } + // The index within subface at which the facet marker is found. Ensure + // the marker is aligned to a sizeof(int)-byte address. + shmarkindex = (shsize + sizeof(int) - 1) / sizeof(int); + // Increase the number of bytes by two or three integers, one for facet + // marker, one for shellface type and flags, and optionally one + // for storing facet index (for mesh refinement). + shsize = (shmarkindex + 2 + useinsertradius) * sizeof(shellface); + + // Initialize the pool of subfaces. Each subface record is eight-byte + // aligned so it has room to store an edge version (from 0 to 5) in + // the least three bits. + subfaces = new memorypool(shsize, b->shellfaceperblock, sizeof(void *), 8); + + if (b->verbose) { + printf(" Size of a shellface: %d (%d) bytes.\n", shsize, + subfaces->itembytes); + } + + // Initialize the pool of subsegments. The subsegment's record is same + // with subface. + subsegs = new memorypool(shsize, b->shellfaceperblock, sizeof(void *), 8); + + // Initialize the pool for tet-subseg connections. + tet2segpool = new memorypool(6 * sizeof(shellface), b->shellfaceperblock, + sizeof(void *), 0); + // Initialize the pool for tet-subface connections. + tet2subpool = new memorypool(4 * sizeof(shellface), b->shellfaceperblock, + sizeof(void *), 0); + + // Initialize arraypools for segment & facet recovery. + subsegstack = new arraypool(sizeof(face), 10); + subfacstack = new arraypool(sizeof(face), 10); + subvertstack = new arraypool(sizeof(point), 8); + + // Initialize arraypools for surface point insertion/deletion. + caveshlist = new arraypool(sizeof(face), 8); + caveshbdlist = new arraypool(sizeof(face), 8); + cavesegshlist = new arraypool(sizeof(face), 4); + + cavetetshlist = new arraypool(sizeof(face), 8); + cavetetseglist = new arraypool(sizeof(face), 8); + caveencshlist = new arraypool(sizeof(face), 8); + caveencseglist = new arraypool(sizeof(face), 8); + } + + // Initialize the pools for flips. + flippool = new memorypool(sizeof(badface), 1024, sizeof(void *), 0); + unflipqueue = new arraypool(sizeof(badface), 10); + + // Initialize the arraypools for point insertion. + cavetetlist = new arraypool(sizeof(triface), 10); + cavebdrylist = new arraypool(sizeof(triface), 10); + caveoldtetlist = new arraypool(sizeof(triface), 10); + cavetetvertlist = new arraypool(sizeof(point), 10); +} + +//// //// +//// //// +//// mempool_cxx ////////////////////////////////////////////////////////////// + +//// geom_cxx ///////////////////////////////////////////////////////////////// +//// //// +//// //// + +// PI is the ratio of a circle's circumference to its diameter. +REAL tetgenmesh::PI = 3.14159265358979323846264338327950288419716939937510582; + +/////////////////////////////////////////////////////////////////////////////// +// // +// insphere_s() Insphere test with symbolic perturbation. // +// // +// Given four points pa, pb, pc, and pd, test if the point pe lies inside or // +// outside the circumscribed sphere of the four points. // +// // +// Here we assume that the 3d orientation of the point sequence {pa, pb, pc, // +// pd} is positive (NOT zero), i.e., pd lies above the plane passing through // +// points pa, pb, and pc. Otherwise, the returned sign is flipped. // +// // +// Return a positive value (> 0) if pe lies inside, a negative value (< 0) // +// if pe lies outside the sphere, the returned value will not be zero. // +// // +/////////////////////////////////////////////////////////////////////////////// + +REAL tetgenmesh::insphere_s(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe) +{ + REAL sign; + + sign = insphere(pa, pb, pc, pd, pe); + if (sign != 0.0) { + return sign; + } + + // Symbolic perturbation. + point pt[5], swappt; + REAL oriA, oriB; + int swaps, count; + int n, i; + + pt[0] = pa; + pt[1] = pb; + pt[2] = pc; + pt[3] = pd; + pt[4] = pe; + + // Sort the five points such that their indices are in the increasing + // order. An optimized bubble sort algorithm is used, i.e., it has + // the worst case O(n^2) runtime, but it is usually much faster. + swaps = 0; // Record the total number of swaps. + n = 5; + do { + count = 0; + n = n - 1; + for (i = 0; i < n; i++) { + if (pointmark(pt[i]) > pointmark(pt[i+1])) { + swappt = pt[i]; pt[i] = pt[i+1]; pt[i+1] = swappt; + count++; + } + } + swaps += count; + } while (count > 0); // Continue if some points are swapped. + + oriA = orient3d(pt[1], pt[2], pt[3], pt[4]); + if (oriA != 0.0) { + // Flip the sign if there are odd number of swaps. + if ((swaps % 2) != 0) oriA = -oriA; + return oriA; + } + + oriB = -orient3d(pt[0], pt[2], pt[3], pt[4]); + if (oriB == 0.0) { + terminatetetgen(this, 2); + } + // Flip the sign if there are odd number of swaps. + if ((swaps % 2) != 0) oriB = -oriB; + return oriB; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// orient4d_s() 4d orientation test with symbolic perturbation. // +// // +// Given four lifted points pa', pb', pc', and pd' in R^4,test if the lifted // +// point pe' in R^4 lies below or above the hyperplane passing through the // +// four points pa', pb', pc', and pd'. // +// // +// Here we assume that the 3d orientation of the point sequence {pa, pb, pc, // +// pd} is positive (NOT zero), i.e., pd lies above the plane passing through // +// the points pa, pb, and pc. Otherwise, the returned sign is flipped. // +// // +// Return a positive value (> 0) if pe' lies below, a negative value (< 0) // +// if pe' lies above the hyperplane, the returned value should not be zero. // +// // +/////////////////////////////////////////////////////////////////////////////// + +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]); + if (oriB == 0.0) { + terminatetetgen(this, 2); + } + // Flip the sign if there are odd number of swaps. + if ((swaps % 2) != 0) oriB = -oriB; + return oriB; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// tri_edge_test() Triangle-edge intersection test. // +// // +// This routine takes a triangle T (with vertices A, B, C) and an edge E (P, // +// Q) in 3D, and tests if they intersect each other. // +// // +// If the point 'R' is not NULL, it lies strictly above the plane defined by // +// A, B, C. It is used in test when T and E are coplanar. // +// // +// If T and E intersect each other, they may intersect in different ways. If // +// 'level' > 0, their intersection type will be reported 'types' and 'pos'. // +// // +// The return value indicates one of the following cases: // +// - 0, T and E are disjoint. // +// - 1, T and E intersect each other. // +// - 2, T and E are not coplanar. They intersect at a single point. // +// - 4, T and E are coplanar. They intersect at a single point or a line // +// segment (if types[1] != DISJOINT). // +// // +/////////////////////////////////////////////////////////////////////////////// + +#define SETVECTOR3(V, a0, a1, a2) (V)[0] = (a0); (V)[1] = (a1); (V)[2] = (a2) + +#define SWAP2(a0, a1, tmp) (tmp) = (a0); (a0) = (a1); (a1) = (tmp) + +int tetgenmesh::tri_edge_2d(point A, point B, point C, point P, point Q, + point R, int level, int *types, int *pos) +{ + point U[3], V[3]; // The permuted vectors of points. + int pu[3], pv[3]; // The original positions of points. + REAL abovept[3]; + REAL sA, sB, sC; + REAL s1, s2, s3, s4; + int z1; + + if (R == NULL) { + // Calculate a lift point. + if (1) { + REAL n[3], len; + // Calculate a lift point, saved in dummypoint. + facenormal(A, B, C, n, 1, NULL); + len = sqrt(dot(n, n)); + if (len != 0) { + n[0] /= len; + n[1] /= len; + n[2] /= len; + len = distance(A, B); + len += distance(B, C); + len += distance(C, A); + len /= 3.0; + R = abovept; //dummypoint; + R[0] = A[0] + len * n[0]; + R[1] = A[1] + len * n[1]; + R[2] = A[2] + len * n[2]; + } else { + // The triangle [A,B,C] is (nearly) degenerate, i.e., it is (close) + // to a line. We need a line-line intersection test. + // !!! A non-save return value.!!! + return 0; // DISJOINT + } + } + } + + // Test A's, B's, and C's orientations wrt plane PQR. + sA = orient3d(P, Q, R, A); + sB = orient3d(P, Q, R, B); + sC = orient3d(P, Q, R, C); + + + if (sA < 0) { + if (sB < 0) { + if (sC < 0) { // (---). + return 0; + } else { + if (sC > 0) { // (--+). + // All points are in the right positions. + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 0, 1, 2); + z1 = 0; + } else { // (--0). + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 0, 1, 2); + z1 = 1; + } + } + } else { + if (sB > 0) { + if (sC < 0) { // (-+-). + SETVECTOR3(U, C, A, B); // PT = ST + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 2, 0, 1); + SETVECTOR3(pv, 0, 1, 2); + z1 = 0; + } else { + if (sC > 0) { // (-++). + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 1, 2, 0); + SETVECTOR3(pv, 1, 0, 2); + z1 = 0; + } else { // (-+0). + SETVECTOR3(U, C, A, B); // PT = ST + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 2, 0, 1); + SETVECTOR3(pv, 0, 1, 2); + z1 = 2; + } + } + } else { + if (sC < 0) { // (-0-). + SETVECTOR3(U, C, A, B); // PT = ST + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 2, 0, 1); + SETVECTOR3(pv, 0, 1, 2); + z1 = 1; + } else { + if (sC > 0) { // (-0+). + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 1, 2, 0); + SETVECTOR3(pv, 1, 0, 2); + z1 = 2; + } else { // (-00). + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 1, 2, 0); + SETVECTOR3(pv, 1, 0, 2); + z1 = 3; + } + } + } + } + } else { + if (sA > 0) { + if (sB < 0) { + if (sC < 0) { // (+--). + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 1, 2, 0); + SETVECTOR3(pv, 0, 1, 2); + z1 = 0; + } else { + if (sC > 0) { // (+-+). + SETVECTOR3(U, C, A, B); // PT = ST + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 2, 0, 1); + SETVECTOR3(pv, 1, 0, 2); + z1 = 0; + } else { // (+-0). + SETVECTOR3(U, C, A, B); // PT = ST + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 2, 0, 1); + SETVECTOR3(pv, 1, 0, 2); + z1 = 2; + } + } + } else { + if (sB > 0) { + if (sC < 0) { // (++-). + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 1, 0, 2); + z1 = 0; + } else { + if (sC > 0) { // (+++). + return 0; + } else { // (++0). + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 1, 0, 2); + z1 = 1; + } + } + } else { // (+0#) + if (sC < 0) { // (+0-). + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 1, 2, 0); + SETVECTOR3(pv, 0, 1, 2); + z1 = 2; + } else { + if (sC > 0) { // (+0+). + SETVECTOR3(U, C, A, B); // PT = ST + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 2, 0, 1); + SETVECTOR3(pv, 1, 0, 2); + z1 = 1; + } else { // (+00). + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 1, 2, 0); + SETVECTOR3(pv, 0, 1, 2); + z1 = 3; + } + } + } + } + } else { + if (sB < 0) { + if (sC < 0) { // (0--). + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 1, 2, 0); + SETVECTOR3(pv, 0, 1, 2); + z1 = 1; + } else { + if (sC > 0) { // (0-+). + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 0, 1, 2); + z1 = 2; + } else { // (0-0). + SETVECTOR3(U, C, A, B); // PT = ST + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 2, 0, 1); + SETVECTOR3(pv, 1, 0, 2); + z1 = 3; + } + } + } else { + if (sB > 0) { + if (sC < 0) { // (0+-). + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 1, 0, 2); + z1 = 2; + } else { + if (sC > 0) { // (0++). + SETVECTOR3(U, B, C, A); // PT = ST x ST + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 1, 2, 0); + SETVECTOR3(pv, 1, 0, 2); + z1 = 1; + } else { // (0+0). + SETVECTOR3(U, C, A, B); // PT = ST + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 2, 0, 1); + SETVECTOR3(pv, 0, 1, 2); + z1 = 3; + } + } + } else { // (00#) + if (sC < 0) { // (00-). + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(V, Q, P, R); // PL = SL + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 1, 0, 2); + z1 = 3; + } else { + if (sC > 0) { // (00+). + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 0, 1, 2); + z1 = 3; + } else { // (000) + // Not possible unless ABC is degenerate. + // Avoiding compiler warnings. + SETVECTOR3(U, A, B, C); // I3 + SETVECTOR3(V, P, Q, R); // I2 + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 0, 1, 2); + z1 = 4; + } + } + } + } + } + } + + s1 = orient3d(U[0], U[2], R, V[1]); // A, C, R, Q + s2 = orient3d(U[1], U[2], R, V[0]); // B, C, R, P + + if (s1 > 0) { + return 0; + } + if (s2 < 0) { + return 0; + } + + if (level == 0) { + return 1; // They are intersected. + } + + + if (z1 == 1) { + if (s1 == 0) { // (0###) + // C = Q. + types[0] = (int) SHAREVERT; + pos[0] = pu[2]; // C + pos[1] = pv[1]; // Q + types[1] = (int) DISJOINT; + } else { + if (s2 == 0) { // (#0##) + // C = P. + types[0] = (int) SHAREVERT; + pos[0] = pu[2]; // C + pos[1] = pv[0]; // P + types[1] = (int) DISJOINT; + } else { // (-+##) + // C in [P, Q]. + types[0] = (int) ACROSSVERT; + pos[0] = pu[2]; // C + pos[1] = pv[0]; // [P, Q] + types[1] = (int) DISJOINT; + } + } + return 4; + } + + s3 = orient3d(U[0], U[2], R, V[0]); // A, C, R, P + s4 = orient3d(U[1], U[2], R, V[1]); // B, C, R, Q + + if (z1 == 0) { // (tritri-03) + if (s1 < 0) { + if (s3 > 0) { + if (s4 > 0) { + // [P, Q] overlaps [k, l] (-+++). + types[0] = (int) ACROSSEDGE; + pos[0] = pu[2]; // [C, A] + pos[1] = pv[0]; // [P, Q] + types[1] = (int) TOUCHFACE; + pos[2] = 3; // [A, B, C] + pos[3] = pv[1]; // Q + } else { + if (s4 == 0) { + // Q = l, [P, Q] contains [k, l] (-++0). + types[0] = (int) ACROSSEDGE; + pos[0] = pu[2]; // [C, A] + pos[1] = pv[0]; // [P, Q] + types[1] = (int) TOUCHEDGE; + pos[2] = pu[1]; // [B, C] + pos[3] = pv[1]; // Q + } else { // s4 < 0 + // [P, Q] contains [k, l] (-++-). + types[0] = (int) ACROSSEDGE; + pos[0] = pu[2]; // [C, A] + pos[1] = pv[0]; // [P, Q] + types[1] = (int) ACROSSEDGE; + pos[2] = pu[1]; // [B, C] + pos[3] = pv[0]; // [P, Q] + } + } + } else { + if (s3 == 0) { + if (s4 > 0) { + // P = k, [P, Q] in [k, l] (-+0+). + types[0] = (int) TOUCHEDGE; + pos[0] = pu[2]; // [C, A] + pos[1] = pv[0]; // P + types[1] = (int) TOUCHFACE; + pos[2] = 3; // [A, B, C] + pos[3] = pv[1]; // Q + } else { + if (s4 == 0) { + // [P, Q] = [k, l] (-+00). + types[0] = (int) TOUCHEDGE; + pos[0] = pu[2]; // [C, A] + pos[1] = pv[0]; // P + types[1] = (int) TOUCHEDGE; + pos[2] = pu[1]; // [B, C] + pos[3] = pv[1]; // Q + } else { + // P = k, [P, Q] contains [k, l] (-+0-). + types[0] = (int) TOUCHEDGE; + pos[0] = pu[2]; // [C, A] + pos[1] = pv[0]; // P + types[1] = (int) ACROSSEDGE; + pos[2] = pu[1]; // [B, C] + pos[3] = pv[0]; // [P, Q] + } + } + } else { // s3 < 0 + if (s2 > 0) { + if (s4 > 0) { + // [P, Q] in [k, l] (-+-+). + types[0] = (int) TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pv[0]; // P + types[1] = (int) TOUCHFACE; + pos[2] = 3; // [A, B, C] + pos[3] = pv[1]; // Q + } else { + if (s4 == 0) { + // Q = l, [P, Q] in [k, l] (-+-0). + types[0] = (int) TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pv[0]; // P + types[1] = (int) TOUCHEDGE; + pos[2] = pu[1]; // [B, C] + pos[3] = pv[1]; // Q + } else { // s4 < 0 + // [P, Q] overlaps [k, l] (-+--). + types[0] = (int) TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pv[0]; // P + types[1] = (int) ACROSSEDGE; + pos[2] = pu[1]; // [B, C] + pos[3] = pv[0]; // [P, Q] + } + } + } else { // s2 == 0 + // P = l (#0##). + types[0] = (int) TOUCHEDGE; + pos[0] = pu[1]; // [B, C] + pos[1] = pv[0]; // P + types[1] = (int) DISJOINT; + } + } + } + } else { // s1 == 0 + // Q = k (0####) + types[0] = (int) TOUCHEDGE; + pos[0] = pu[2]; // [C, A] + pos[1] = pv[1]; // Q + types[1] = (int) DISJOINT; + } + } else if (z1 == 2) { // (tritri-23) + if (s1 < 0) { + if (s3 > 0) { + if (s4 > 0) { + // [P, Q] overlaps [A, l] (-+++). + types[0] = (int) ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = pv[0]; // [P, Q] + types[1] = (int) TOUCHFACE; + pos[2] = 3; // [A, B, C] + pos[3] = pv[1]; // Q + } else { + if (s4 == 0) { + // Q = l, [P, Q] contains [A, l] (-++0). + types[0] = (int) ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = pv[0]; // [P, Q] + types[1] = (int) TOUCHEDGE; + pos[2] = pu[1]; // [B, C] + pos[3] = pv[1]; // Q + } else { // s4 < 0 + // [P, Q] contains [A, l] (-++-). + types[0] = (int) ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = pv[0]; // [P, Q] + types[1] = (int) ACROSSEDGE; + pos[2] = pu[1]; // [B, C] + pos[3] = pv[0]; // [P, Q] + } + } + } else { + if (s3 == 0) { + if (s4 > 0) { + // P = A, [P, Q] in [A, l] (-+0+). + types[0] = (int) SHAREVERT; + pos[0] = pu[0]; // A + pos[1] = pv[0]; // P + types[1] = (int) TOUCHFACE; + pos[2] = 3; // [A, B, C] + pos[3] = pv[1]; // Q + } else { + if (s4 == 0) { + // [P, Q] = [A, l] (-+00). + types[0] = (int) SHAREVERT; + pos[0] = pu[0]; // A + pos[1] = pv[0]; // P + types[1] = (int) TOUCHEDGE; + pos[2] = pu[1]; // [B, C] + pos[3] = pv[1]; // Q + } else { // s4 < 0 + // Q = l, [P, Q] in [A, l] (-+0-). + types[0] = (int) SHAREVERT; + pos[0] = pu[0]; // A + pos[1] = pv[0]; // P + types[1] = (int) ACROSSEDGE; + pos[2] = pu[1]; // [B, C] + pos[3] = pv[0]; // [P, Q] + } + } + } else { // s3 < 0 + if (s2 > 0) { + if (s4 > 0) { + // [P, Q] in [A, l] (-+-+). + types[0] = (int) TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pv[0]; // P + types[0] = (int) TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pv[1]; // Q + } else { + if (s4 == 0) { + // Q = l, [P, Q] in [A, l] (-+-0). + types[0] = (int) TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pv[0]; // P + types[0] = (int) TOUCHEDGE; + pos[0] = pu[1]; // [B, C] + pos[1] = pv[1]; // Q + } else { // s4 < 0 + // [P, Q] overlaps [A, l] (-+--). + types[0] = (int) TOUCHFACE; + pos[0] = 3; // [A, B, C] + pos[1] = pv[0]; // P + types[0] = (int) ACROSSEDGE; + pos[0] = pu[1]; // [B, C] + pos[1] = pv[0]; // [P, Q] + } + } + } else { // s2 == 0 + // P = l (#0##). + types[0] = (int) TOUCHEDGE; + pos[0] = pu[1]; // [B, C] + pos[1] = pv[0]; // P + types[1] = (int) DISJOINT; + } + } + } + } else { // s1 == 0 + // Q = A (0###). + types[0] = (int) SHAREVERT; + pos[0] = pu[0]; // A + pos[1] = pv[1]; // Q + types[1] = (int) DISJOINT; + } + } else if (z1 == 3) { // (tritri-33) + if (s1 < 0) { + if (s3 > 0) { + if (s4 > 0) { + // [P, Q] overlaps [A, B] (-+++). + types[0] = (int) ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = pv[0]; // [P, Q] + types[1] = (int) TOUCHEDGE; + pos[2] = pu[0]; // [A, B] + pos[3] = pv[1]; // Q + } else { + if (s4 == 0) { + // Q = B, [P, Q] contains [A, B] (-++0). + types[0] = (int) ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = pv[0]; // [P, Q] + types[1] = (int) SHAREVERT; + pos[2] = pu[1]; // B + pos[3] = pv[1]; // Q + } else { // s4 < 0 + // [P, Q] contains [A, B] (-++-). + types[0] = (int) ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = pv[0]; // [P, Q] + types[1] = (int) ACROSSVERT; + pos[2] = pu[1]; // B + pos[3] = pv[0]; // [P, Q] + } + } + } else { + if (s3 == 0) { + if (s4 > 0) { + // P = A, [P, Q] in [A, B] (-+0+). + types[0] = (int) SHAREVERT; + pos[0] = pu[0]; // A + pos[1] = pv[0]; // P + types[1] = (int) TOUCHEDGE; + pos[2] = pu[0]; // [A, B] + pos[3] = pv[1]; // Q + } else { + if (s4 == 0) { + // [P, Q] = [A, B] (-+00). + types[0] = (int) SHAREEDGE; + pos[0] = pu[0]; // [A, B] + pos[1] = pv[0]; // [P, Q] + types[1] = (int) DISJOINT; + } else { // s4 < 0 + // P= A, [P, Q] in [A, B] (-+0-). + types[0] = (int) SHAREVERT; + pos[0] = pu[0]; // A + pos[1] = pv[0]; // P + types[1] = (int) ACROSSVERT; + pos[2] = pu[1]; // B + pos[3] = pv[0]; // [P, Q] + } + } + } else { // s3 < 0 + if (s2 > 0) { + if (s4 > 0) { + // [P, Q] in [A, B] (-+-+). + types[0] = (int) TOUCHEDGE; + pos[0] = pu[0]; // [A, B] + pos[1] = pv[0]; // P + types[1] = (int) TOUCHEDGE; + pos[2] = pu[0]; // [A, B] + pos[3] = pv[1]; // Q + } else { + if (s4 == 0) { + // Q = B, [P, Q] in [A, B] (-+-0). + types[0] = (int) TOUCHEDGE; + pos[0] = pu[0]; // [A, B] + pos[1] = pv[0]; // P + types[1] = (int) SHAREVERT; + pos[2] = pu[1]; // B + pos[3] = pv[1]; // Q + } else { // s4 < 0 + // [P, Q] overlaps [A, B] (-+--). + types[0] = (int) TOUCHEDGE; + pos[0] = pu[0]; // [A, B] + pos[1] = pv[0]; // P + types[1] = (int) ACROSSVERT; + pos[2] = pu[1]; // B + pos[3] = pv[0]; // [P, Q] + } + } + } else { // s2 == 0 + // P = B (#0##). + types[0] = (int) SHAREVERT; + pos[0] = pu[1]; // B + pos[1] = pv[0]; // P + types[1] = (int) DISJOINT; + } + } + } + } else { // s1 == 0 + // Q = A (0###). + types[0] = (int) SHAREVERT; + pos[0] = pu[0]; // A + pos[1] = pv[1]; // Q + types[1] = (int) DISJOINT; + } + } + + return 4; +} + +int tetgenmesh::tri_edge_tail(point A,point B,point C,point P,point Q,point R, + REAL sP,REAL sQ,int level,int *types,int *pos) +{ + point U[3], V[3]; //, Ptmp; + int pu[3], pv[3]; //, itmp; + REAL s1, s2, s3; + int z1; + + + if (sP < 0) { + if (sQ < 0) { // (--) disjoint + return 0; + } else { + if (sQ > 0) { // (-+) + SETVECTOR3(U, A, B, C); + SETVECTOR3(V, P, Q, R); + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 0, 1, 2); + z1 = 0; + } else { // (-0) + SETVECTOR3(U, A, B, C); + SETVECTOR3(V, P, Q, R); + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 0, 1, 2); + z1 = 1; + } + } + } else { + if (sP > 0) { // (+-) + if (sQ < 0) { + SETVECTOR3(U, A, B, C); + SETVECTOR3(V, Q, P, R); // P and Q are flipped. + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 1, 0, 2); + z1 = 0; + } else { + if (sQ > 0) { // (++) disjoint + return 0; + } else { // (+0) + SETVECTOR3(U, B, A, C); // A and B are flipped. + SETVECTOR3(V, P, Q, R); + SETVECTOR3(pu, 1, 0, 2); + SETVECTOR3(pv, 0, 1, 2); + z1 = 1; + } + } + } else { // sP == 0 + if (sQ < 0) { // (0-) + SETVECTOR3(U, A, B, C); + SETVECTOR3(V, Q, P, R); // P and Q are flipped. + SETVECTOR3(pu, 0, 1, 2); + SETVECTOR3(pv, 1, 0, 2); + z1 = 1; + } else { + if (sQ > 0) { // (0+) + SETVECTOR3(U, B, A, C); // A and B are flipped. + SETVECTOR3(V, Q, P, R); // P and Q are flipped. + SETVECTOR3(pu, 1, 0, 2); + SETVECTOR3(pv, 1, 0, 2); + z1 = 1; + } else { // (00) + // A, B, C, P, and Q are coplanar. + z1 = 2; + } + } + } + } + + if (z1 == 2) { + // The triangle and the edge are coplanar. + return tri_edge_2d(A, B, C, P, Q, R, level, types, pos); + } + + s1 = orient3d(U[0], U[1], V[0], V[1]); + if (s1 < 0) { + return 0; + } + + s2 = orient3d(U[1], U[2], V[0], V[1]); + if (s2 < 0) { + return 0; + } + + s3 = orient3d(U[2], U[0], V[0], V[1]); + if (s3 < 0) { + return 0; + } + + if (level == 0) { + return 1; // The are intersected. + } + + types[1] = (int) DISJOINT; // No second intersection point. + + if (z1 == 0) { + if (s1 > 0) { + if (s2 > 0) { + if (s3 > 0) { // (+++) + // [P, Q] passes interior of [A, B, C]. + types[0] = (int) ACROSSFACE; + pos[0] = 3; // interior of [A, B, C] + pos[1] = 0; // [P, Q] + } else { // s3 == 0 (++0) + // [P, Q] intersects [C, A]. + types[0] = (int) ACROSSEDGE; + pos[0] = pu[2]; // [C, A] + pos[1] = 0; // [P, Q] + } + } else { // s2 == 0 + if (s3 > 0) { // (+0+) + // [P, Q] intersects [B, C]. + types[0] = (int) ACROSSEDGE; + pos[0] = pu[1]; // [B, C] + pos[1] = 0; // [P, Q] + } else { // s3 == 0 (+00) + // [P, Q] passes C. + types[0] = (int) ACROSSVERT; + pos[0] = pu[2]; // C + pos[1] = 0; // [P, Q] + } + } + } else { // s1 == 0 + if (s2 > 0) { + if (s3 > 0) { // (0++) + // [P, Q] intersects [A, B]. + types[0] = (int) ACROSSEDGE; + pos[0] = pu[0]; // [A, B] + pos[1] = 0; // [P, Q] + } else { // s3 == 0 (0+0) + // [P, Q] passes A. + types[0] = (int) ACROSSVERT; + pos[0] = pu[0]; // A + pos[1] = 0; // [P, Q] + } + } else { // s2 == 0 + if (s3 > 0) { // (00+) + // [P, Q] passes B. + types[0] = (int) ACROSSVERT; + pos[0] = pu[1]; // B + pos[1] = 0; // [P, Q] + } + } + } + } else { // z1 == 1 + if (s1 > 0) { + if (s2 > 0) { + if (s3 > 0) { // (+++) + // Q lies in [A, B, C]. + types[0] = (int) TOUCHFACE; + pos[0] = 0; // [A, B, C] + pos[1] = pv[1]; // Q + } else { // s3 == 0 (++0) + // Q lies on [C, A]. + types[0] = (int) TOUCHEDGE; + pos[0] = pu[2]; // [C, A] + pos[1] = pv[1]; // Q + } + } else { // s2 == 0 + if (s3 > 0) { // (+0+) + // Q lies on [B, C]. + types[0] = (int) TOUCHEDGE; + pos[0] = pu[1]; // [B, C] + pos[1] = pv[1]; // Q + } else { // s3 == 0 (+00) + // Q = C. + types[0] = (int) SHAREVERT; + pos[0] = pu[2]; // C + pos[1] = pv[1]; // Q + } + } + } else { // s1 == 0 + if (s2 > 0) { + if (s3 > 0) { // (0++) + // Q lies on [A, B]. + types[0] = (int) TOUCHEDGE; + pos[0] = pu[0]; // [A, B] + pos[1] = pv[1]; // Q + } else { // s3 == 0 (0+0) + // Q = A. + types[0] = (int) SHAREVERT; + pos[0] = pu[0]; // A + pos[1] = pv[1]; // Q + } + } else { // s2 == 0 + if (s3 > 0) { // (00+) + // Q = B. + types[0] = (int) SHAREVERT; + pos[0] = pu[1]; // B + pos[1] = pv[1]; // Q + } + } + } + } + + // T and E intersect in a single point. + return 2; +} + +int tetgenmesh::tri_edge_test(point A, point B, point C, point P, point Q, + point R, int level, int *types, int *pos) +{ + REAL sP, sQ; + + // Test the locations of P and Q with respect to ABC. + sP = orient3d(A, B, C, P); + sQ = orient3d(A, B, C, Q); + + return tri_edge_tail(A, B, C, P, Q, R, sP, sQ, level, types, pos); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// tri_tri_inter() Test whether two triangle (abc) and (opq) are // +// intersecting or not. // +// // +// Return 0 if they are disjoint. Otherwise, return 1. 'type' returns one of // +// the four cases: SHAREVERTEX, SHAREEDGE, SHAREFACE, and INTERSECT. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::tri_edge_inter_tail(REAL* A, REAL* B, REAL* C, REAL* P, + REAL* Q, REAL s_p, REAL s_q) +{ + int types[2], pos[4]; + int ni; // =0, 2, 4 + + ni = tri_edge_tail(A, B, C, P, Q, NULL, s_p, s_q, 1, types, pos); + + if (ni > 0) { + if (ni == 2) { + // Get the intersection type. + if (types[0] == (int) SHAREVERT) { + return (int) SHAREVERT; + } else { + return (int) INTERSECT; + } + } else if (ni == 4) { + // There may be two intersections. + if (types[0] == (int) SHAREVERT) { + if (types[1] == (int) DISJOINT) { + return (int) SHAREVERT; + } else { + return (int) INTERSECT; + } + } else { + if (types[0] == (int) SHAREEDGE) { + return (int) SHAREEDGE; + } else { + return (int) INTERSECT; + } + } + } + } + + return (int) DISJOINT; +} + +int tetgenmesh::tri_tri_inter(REAL* A,REAL* B,REAL* C,REAL* O,REAL* P,REAL* Q) +{ + REAL s_o, s_p, s_q; + REAL s_a, s_b, s_c; + + s_o = orient3d(A, B, C, O); + s_p = orient3d(A, B, C, P); + s_q = orient3d(A, B, C, Q); + if ((s_o * s_p > 0.0) && (s_o * s_q > 0.0)) { + // o, p, q are all in the same halfspace of ABC. + return 0; // DISJOINT; + } + + s_a = orient3d(O, P, Q, A); + s_b = orient3d(O, P, Q, B); + s_c = orient3d(O, P, Q, C); + if ((s_a * s_b > 0.0) && (s_a * s_c > 0.0)) { + // a, b, c are all in the same halfspace of OPQ. + return 0; // DISJOINT; + } + + int abcop, abcpq, abcqo; + int shareedge = 0; + + abcop = tri_edge_inter_tail(A, B, C, O, P, s_o, s_p); + if (abcop == (int) INTERSECT) { + return (int) INTERSECT; + } else if (abcop == (int) SHAREEDGE) { + shareedge++; + } + abcpq = tri_edge_inter_tail(A, B, C, P, Q, s_p, s_q); + if (abcpq == (int) INTERSECT) { + return (int) INTERSECT; + } else if (abcpq == (int) SHAREEDGE) { + shareedge++; + } + abcqo = tri_edge_inter_tail(A, B, C, Q, O, s_q, s_o); + if (abcqo == (int) INTERSECT) { + return (int) INTERSECT; + } else if (abcqo == (int) SHAREEDGE) { + shareedge++; + } + if (shareedge == 3) { + // opq are coincident with abc. + return (int) SHAREFACE; + } + + // Continue to detect whether opq and abc are intersecting or not. + int opqab, opqbc, opqca; + + opqab = tri_edge_inter_tail(O, P, Q, A, B, s_a, s_b); + if (opqab == (int) INTERSECT) { + return (int) INTERSECT; + } + opqbc = tri_edge_inter_tail(O, P, Q, B, C, s_b, s_c); + if (opqbc == (int) INTERSECT) { + return (int) INTERSECT; + } + opqca = tri_edge_inter_tail(O, P, Q, C, A, s_c, s_a); + if (opqca == (int) INTERSECT) { + return (int) INTERSECT; + } + + // At this point, two triangles are not intersecting and not coincident. + // They may be share an edge, or share a vertex, or disjoint. + if (abcop == (int) SHAREEDGE) { + // op is coincident with an edge of abc. + return (int) SHAREEDGE; + } + if (abcpq == (int) SHAREEDGE) { + // pq is coincident with an edge of abc. + return (int) SHAREEDGE; + } + if (abcqo == (int) SHAREEDGE) { + // qo is coincident with an edge of abc. + return (int) SHAREEDGE; + } + + // They may share a vertex or disjoint. + if (abcop == (int) SHAREVERT) { + return (int) SHAREVERT; + } + if (abcpq == (int) SHAREVERT) { + // q is the coincident vertex. + return (int) SHAREVERT; + } + + // They are disjoint. + return (int) DISJOINT; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// lu_decmp() Compute the LU decomposition of a matrix. // +// // +// Compute the LU decomposition of a (non-singular) square matrix A using // +// partial pivoting and implicit row exchanges. The result is: // +// A = P * L * U, // +// where P is a permutation matrix, L is unit lower triangular, and U is // +// upper triangular. The factored form of A is used in combination with // +// 'lu_solve()' to solve linear equations: Ax = b, or invert a matrix. // +// // +// The inputs are a square matrix 'lu[N..n+N-1][N..n+N-1]', it's size is 'n'.// +// On output, 'lu' is replaced by the LU decomposition of a rowwise permuta- // +// tion of itself, 'ps[N..n+N-1]' is an output vector that records the row // +// permutation effected by the partial pivoting, effectively, 'ps' array // +// tells the user what the permutation matrix P is; 'd' is output as +1/-1 // +// depending on whether the number of row interchanges was even or odd, // +// respectively. // +// // +// Return true if the LU decomposition is successfully computed, otherwise, // +// return false in case that A is a singular matrix. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenmesh::lu_decmp(REAL lu[4][4], int n, int* ps, REAL* d, int N) +{ + REAL scales[4]; + REAL pivot, biggest, mult, tempf; + int pivotindex = 0; + int i, j, k; + + *d = 1.0; // No row interchanges yet. + + for (i = N; i < n + N; i++) { // For each row. + // Find the largest element in each row for row equilibration + biggest = 0.0; + for (j = N; j < n + N; j++) + if (biggest < (tempf = fabs(lu[i][j]))) + biggest = tempf; + if (biggest != 0.0) + scales[i] = 1.0 / biggest; + else { + scales[i] = 0.0; + return false; // Zero row: singular matrix. + } + ps[i] = i; // Initialize pivot sequence. + } + + for (k = N; k < n + N - 1; k++) { // For each column. + // Find the largest element in each column to pivot around. + biggest = 0.0; + for (i = k; i < n + N; i++) { + if (biggest < (tempf = fabs(lu[ps[i]][k]) * scales[ps[i]])) { + biggest = tempf; + pivotindex = i; + } + } + if (biggest == 0.0) { + return false; // Zero column: singular matrix. + } + if (pivotindex != k) { // Update pivot sequence. + j = ps[k]; + ps[k] = ps[pivotindex]; + ps[pivotindex] = j; + *d = -(*d); // ...and change the parity of d. + } + + // Pivot, eliminating an extra variable each time + pivot = lu[ps[k]][k]; + for (i = k + 1; i < n + N; i++) { + lu[ps[i]][k] = mult = lu[ps[i]][k] / pivot; + if (mult != 0.0) { + for (j = k + 1; j < n + N; j++) + lu[ps[i]][j] -= mult * lu[ps[k]][j]; + } + } + } + + // (lu[ps[n + N - 1]][n + N - 1] == 0.0) ==> A is singular. + return lu[ps[n + N - 1]][n + N - 1] != 0.0; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// lu_solve() Solves the linear equation: Ax = b, after the matrix A // +// has been decomposed into the lower and upper triangular // +// matrices L and U, where A = LU. // +// // +// 'lu[N..n+N-1][N..n+N-1]' is input, not as the matrix 'A' but rather as // +// its LU decomposition, computed by the routine 'lu_decmp'; 'ps[N..n+N-1]' // +// is input as the permutation vector returned by 'lu_decmp'; 'b[N..n+N-1]' // +// is input as the right-hand side vector, and returns with the solution // +// vector. 'lu', 'n', and 'ps' are not modified by this routine and can be // +// left in place for successive calls with different right-hand sides 'b'. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::lu_solve(REAL lu[4][4], int n, int* ps, REAL* b, int N) +{ + int i, j; + REAL X[4], dot; + + for (i = N; i < n + N; i++) X[i] = 0.0; + + // Vector reduction using U triangular matrix. + for (i = N; i < n + N; i++) { + dot = 0.0; + for (j = N; j < i + N; j++) + dot += lu[ps[i]][j] * X[j]; + X[i] = b[ps[i]] - dot; + } + + // Back substitution, in L triangular matrix. + for (i = n + N - 1; i >= N; i--) { + dot = 0.0; + for (j = i + 1; j < n + N; j++) + dot += lu[ps[i]][j] * X[j]; + X[i] = (X[i] - dot) / lu[ps[i]][i]; + } + + for (i = N; i < n + N; i++) b[i] = X[i]; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// incircle3d() 3D in-circle test. // +// // +// Return a negative value if pd is inside the circumcircle of the triangle // +// pa, pb, and pc. // +// // +// IMPORTANT: It assumes that [a,b] is the common edge, i.e., the two input // +// triangles are [a,b,c] and [b,a,d]. // +// // +/////////////////////////////////////////////////////////////////////////////// + +REAL tetgenmesh::incircle3d(point pa, point pb, point pc, point pd) +{ + REAL area2[2], n1[3], n2[3], c[3]; + REAL sign, r, d; + + // Calculate the areas of the two triangles [a, b, c] and [b, a, d]. + facenormal(pa, pb, pc, n1, 1, NULL); + area2[0] = dot(n1, n1); + facenormal(pb, pa, pd, n2, 1, NULL); + area2[1] = dot(n2, n2); + + if (area2[0] > area2[1]) { + // Choose [a, b, c] as the base triangle. + circumsphere(pa, pb, pc, NULL, c, &r); + d = distance(c, pd); + } else { + // Choose [b, a, d] as the base triangle. + if (area2[1] > 0) { + circumsphere(pb, pa, pd, NULL, c, &r); + d = distance(c, pc); + } else { + // The four points are collinear. This case only happens on the boundary. + return 0; // Return "not inside". + } + } + + sign = d - r; + if (fabs(sign) / r < b->epsilon) { + sign = 0; + } + + return sign; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// facenormal() Calculate the normal of the face. // +// // +// The normal of the face abc can be calculated by the cross product of 2 of // +// its 3 edge vectors. A better choice of two edge vectors will reduce the // +// numerical error during the calculation. Burdakov proved that the optimal // +// basis problem is equivalent to the minimum spanning tree problem with the // +// edge length be the functional, see Burdakov, "A greedy algorithm for the // +// optimal basis problem", BIT 37:3 (1997), 591-599. If 'pivot' > 0, the two // +// short edges in abc are chosen for the calculation. // +// // +// If 'lav' is not NULL and if 'pivot' is set, the average edge length of // +// the edges of the face [a,b,c] is returned. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::facenormal(point pa, point pb, point pc, REAL *n, int pivot, + REAL* lav) +{ + REAL v1[3], v2[3], v3[3], *pv1, *pv2; + REAL L1, L2, L3; + + v1[0] = pb[0] - pa[0]; // edge vector v1: a->b + v1[1] = pb[1] - pa[1]; + v1[2] = pb[2] - pa[2]; + v2[0] = pa[0] - pc[0]; // edge vector v2: c->a + v2[1] = pa[1] - pc[1]; + v2[2] = pa[2] - pc[2]; + + // Default, normal is calculated by: v1 x (-v2) (see Fig. fnormal). + if (pivot > 0) { + // Choose edge vectors by Burdakov's algorithm. + v3[0] = pc[0] - pb[0]; // edge vector v3: b->c + v3[1] = pc[1] - pb[1]; + v3[2] = pc[2] - pb[2]; + L1 = dot(v1, v1); + L2 = dot(v2, v2); + L3 = dot(v3, v3); + // Sort the three edge lengths. + if (L1 < L2) { + if (L2 < L3) { + pv1 = v1; pv2 = v2; // n = v1 x (-v2). + } else { + pv1 = v3; pv2 = v1; // n = v3 x (-v1). + } + } else { + if (L1 < L3) { + pv1 = v1; pv2 = v2; // n = v1 x (-v2). + } else { + pv1 = v2; pv2 = v3; // n = v2 x (-v3). + } + } + if (lav) { + // return the average edge length. + *lav = (sqrt(L1) + sqrt(L2) + sqrt(L3)) / 3.0; + } + } else { + pv1 = v1; pv2 = v2; // n = v1 x (-v2). + } + + // Calculate the face normal. + cross(pv1, pv2, n); + // Inverse the direction; + n[0] = -n[0]; + n[1] = -n[1]; + n[2] = -n[2]; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// shortdistance() Returns the shortest distance from point p to a line // +// defined by two points e1 and e2. // +// // +// First compute the projection length l_p of the vector v1 = p - e1 along // +// the vector v2 = e2 - e1. Then Pythagoras' Theorem is used to compute the // +// shortest distance. // +// // +// This routine allows that p is collinear with the line. In this case, the // +// return value is zero. The two points e1 and e2 should not be identical. // +// // +/////////////////////////////////////////////////////////////////////////////// + +REAL tetgenmesh::shortdistance(REAL* p, REAL* e1, REAL* e2) +{ + REAL v1[3], v2[3]; + REAL len, l_p; + + v1[0] = e2[0] - e1[0]; + v1[1] = e2[1] - e1[1]; + v1[2] = e2[2] - e1[2]; + v2[0] = p[0] - e1[0]; + v2[1] = p[1] - e1[1]; + v2[2] = p[2] - e1[2]; + + len = sqrt(dot(v1, v1)); + + v1[0] /= len; + v1[1] /= len; + v1[2] /= len; + l_p = dot(v1, v2); + + return sqrt(dot(v2, v2) - l_p * l_p); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// triarea() Return the area of a triangle. // +// // +/////////////////////////////////////////////////////////////////////////////// + +REAL tetgenmesh::triarea(REAL* pa, REAL* pb, REAL* pc) +{ + REAL A[4][4]; + + // Compute the coefficient matrix A (3x3). + A[0][0] = pb[0] - pa[0]; + A[0][1] = pb[1] - pa[1]; + A[0][2] = pb[2] - pa[2]; // vector V1 (pa->pb) + A[1][0] = pc[0] - pa[0]; + A[1][1] = pc[1] - pa[1]; + A[1][2] = pc[2] - pa[2]; // vector V2 (pa->pc) + + cross(A[0], A[1], A[2]); // vector V3 (V1 X V2) + + return 0.5 * sqrt(dot(A[2], A[2])); // The area of [a,b,c]. +} + +REAL tetgenmesh::orient3dfast(REAL *pa, REAL *pb, REAL *pc, REAL *pd) +{ + REAL adx, bdx, cdx; + REAL ady, bdy, cdy; + REAL adz, bdz, cdz; + + adx = pa[0] - pd[0]; + bdx = pb[0] - pd[0]; + cdx = pc[0] - pd[0]; + ady = pa[1] - pd[1]; + bdy = pb[1] - pd[1]; + cdy = pc[1] - pd[1]; + adz = pa[2] - pd[2]; + bdz = pb[2] - pd[2]; + cdz = pc[2] - pd[2]; + + return adx * (bdy * cdz - bdz * cdy) + + bdx * (cdy * adz - cdz * ady) + + cdx * (ady * bdz - adz * bdy); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// interiorangle() Return the interior angle (0 - 2 * PI) between vectors // +// o->p1 and o->p2. // +// // +// 'n' is the normal of the plane containing face (o, p1, p2). The interior // +// angle is the total angle rotating from o->p1 around n to o->p2. Exchange // +// the position of p1 and p2 will get the complement angle of the other one. // +// i.e., interiorangle(o, p1, p2) = 2 * PI - interiorangle(o, p2, p1). Set // +// 'n' be NULL if you only want the interior angle between 0 - PI. // +// // +/////////////////////////////////////////////////////////////////////////////// + +REAL tetgenmesh::interiorangle(REAL* o, REAL* p1, REAL* p2, REAL* n) +{ + REAL v1[3], v2[3], np[3]; + REAL theta, costheta, lenlen; + REAL ori, len1, len2; + + // Get the interior angle (0 - PI) between o->p1, and o->p2. + v1[0] = p1[0] - o[0]; + v1[1] = p1[1] - o[1]; + v1[2] = p1[2] - o[2]; + v2[0] = p2[0] - o[0]; + v2[1] = p2[1] - o[1]; + v2[2] = p2[2] - o[2]; + len1 = sqrt(dot(v1, v1)); + len2 = sqrt(dot(v2, v2)); + lenlen = len1 * len2; + + costheta = dot(v1, v2) / lenlen; + if (costheta > 1.0) { + costheta = 1.0; // Roundoff. + } else if (costheta < -1.0) { + costheta = -1.0; // Roundoff. + } + theta = acos(costheta); + if (n != NULL) { + // Get a point above the face (o, p1, p2); + np[0] = o[0] + n[0]; + np[1] = o[1] + n[1]; + np[2] = o[2] + n[2]; + // Adjust theta (0 - 2 * PI). + ori = orient3d(p1, o, np, p2); + if (ori > 0.0) { + theta = 2 * PI - theta; + } + } + + return theta; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// projpt2edge() Return the projection point from a point to an edge. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::projpt2edge(REAL* p, REAL* e1, REAL* e2, REAL* prj) +{ + REAL v1[3], v2[3]; + REAL len, l_p; + + v1[0] = e2[0] - e1[0]; + v1[1] = e2[1] - e1[1]; + v1[2] = e2[2] - e1[2]; + v2[0] = p[0] - e1[0]; + v2[1] = p[1] - e1[1]; + v2[2] = p[2] - e1[2]; + + len = sqrt(dot(v1, v1)); + v1[0] /= len; + v1[1] /= len; + v1[2] /= len; + l_p = dot(v1, v2); + + prj[0] = e1[0] + l_p * v1[0]; + prj[1] = e1[1] + l_p * v1[1]; + prj[2] = e1[2] + l_p * v1[2]; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// projpt2face() Return the projection point from a point to a face. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::projpt2face(REAL* p, REAL* f1, REAL* f2, REAL* f3, REAL* prj) +{ + REAL fnormal[3], v1[3]; + REAL len, dist; + + // Get the unit face normal. + facenormal(f1, f2, f3, fnormal, 1, NULL); + len = sqrt(fnormal[0]*fnormal[0] + fnormal[1]*fnormal[1] + + fnormal[2]*fnormal[2]); + fnormal[0] /= len; + fnormal[1] /= len; + fnormal[2] /= len; + // Get the vector v1 = |p - f1|. + v1[0] = p[0] - f1[0]; + v1[1] = p[1] - f1[1]; + v1[2] = p[2] - f1[2]; + // Get the project distance. + dist = dot(fnormal, v1); + + // Get the project point. + prj[0] = p[0] - dist * fnormal[0]; + prj[1] = p[1] - dist * fnormal[1]; + prj[2] = p[2] - dist * fnormal[2]; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// tetalldihedral() Get all (six) dihedral angles of a tet. // +// // +// If 'cosdd' is not NULL, it returns the cosines of the 6 dihedral angles, // +// the edge indices are given in the global array 'edge2ver'. If 'cosmaxd' // +// (or 'cosmind') is not NULL, it returns the cosine of the maximal (or // +// minimal) dihedral angle. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenmesh::tetalldihedral(point pa, point pb, point pc, point pd, + REAL* cosdd, REAL* cosmaxd, REAL* cosmind) +{ + REAL N[4][3], vol, cosd, len; + int f1 = 0, f2 = 0, i, j; + + vol = 0; // Check if the tet is valid or not. + + // Get four normals of faces of the tet. + tetallnormal(pa, pb, pc, pd, N, &vol); + + if (vol > 0) { + // Normalize the normals. + for (i = 0; i < 4; i++) { + len = sqrt(dot(N[i], N[i])); + if (len != 0.0) { + for (j = 0; j < 3; j++) N[i][j] /= len; + } else { + // There are degeneracies, such as duplicated vertices. + vol = 0; //assert(0); + } + } + } + + if (vol <= 0) { // if (vol == 0.0) { + // A degenerated tet or an inverted tet. + facenormal(pc, pb, pd, N[0], 1, NULL); + facenormal(pa, pc, pd, N[1], 1, NULL); + facenormal(pb, pa, pd, N[2], 1, NULL); + facenormal(pa, pb, pc, N[3], 1, NULL); + // Normalize the normals. + for (i = 0; i < 4; i++) { + len = sqrt(dot(N[i], N[i])); + if (len != 0.0) { + for (j = 0; j < 3; j++) N[i][j] /= len; + } else { + // There are degeneracies, such as duplicated vertices. + break; // Not a valid normal. + } + } + if (i < 4) { + // Do not calculate dihedral angles. + // Set all angles be 0 degree. There will be no quality optimization for + // this tet! Use volume optimization to correct it. + if (cosdd != NULL) { + for (i = 0; i < 6; i++) { + cosdd[i] = -1.0; // 180 degree. + } + } + // This tet has zero volume. + if (cosmaxd != NULL) { + *cosmaxd = -1.0; // 180 degree. + } + if (cosmind != NULL) { + *cosmind = -1.0; // 180 degree. + } + return false; + } + } + + // Calculate the cosine of the dihedral angles of the edges. + for (i = 0; i < 6; i++) { + switch (i) { + case 0: f1 = 0; f2 = 1; break; // [c,d]. + case 1: f1 = 1; f2 = 2; break; // [a,d]. + case 2: f1 = 2; f2 = 3; break; // [a,b]. + case 3: f1 = 0; f2 = 3; break; // [b,c]. + case 4: f1 = 2; f2 = 0; break; // [b,d]. + case 5: f1 = 1; f2 = 3; break; // [a,c]. + } + cosd = -dot(N[f1], N[f2]); + if (cosd < -1.0) cosd = -1.0; // Rounding. + if (cosd > 1.0) cosd = 1.0; // Rounding. + if (cosdd) cosdd[i] = cosd; + if (cosmaxd || cosmind) { + if (i == 0) { + if (cosmaxd) *cosmaxd = cosd; + if (cosmind) *cosmind = cosd; + } else { + if (cosmaxd) *cosmaxd = cosd < *cosmaxd ? cosd : *cosmaxd; + if (cosmind) *cosmind = cosd > *cosmind ? cosd : *cosmind; + } + } + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// tetallnormal() Get the in-normals of the four faces of a given tet. // +// // +// Let tet be abcd. N[4][3] returns the four normals, which are: N[0] cbd, // +// N[1] acd, N[2] bad, N[3] abc (exactly corresponding to the face indices // +// of the mesh data structure). These normals are unnormalized. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::tetallnormal(point pa, point pb, point pc, point pd, + REAL N[4][3], REAL* volume) +{ + REAL A[4][4], rhs[4], D; + int indx[4]; + int i, j; + + // get the entries of A[3][3]. + for (i = 0; i < 3; i++) A[0][i] = pa[i] - pd[i]; // d->a vec + for (i = 0; i < 3; i++) A[1][i] = pb[i] - pd[i]; // d->b vec + for (i = 0; i < 3; i++) A[2][i] = pc[i] - pd[i]; // d->c vec + + // Compute the inverse of matrix A, to get 3 normals of the 4 faces. + if (lu_decmp(A, 3, indx, &D, 0)) { // Decompose the matrix just once. + if (volume != NULL) { + // Get the volume of the tet. + *volume = fabs((A[indx[0]][0] * A[indx[1]][1] * A[indx[2]][2])) / 6.0; + } + for (j = 0; j < 3; j++) { + for (i = 0; i < 3; i++) rhs[i] = 0.0; + rhs[j] = 1.0; // Positive means the inside direction + lu_solve(A, 3, indx, rhs, 0); + for (i = 0; i < 3; i++) N[j][i] = rhs[i]; + } + // Get the fourth normal by summing up the first three. + for (i = 0; i < 3; i++) N[3][i] = - N[0][i] - N[1][i] - N[2][i]; + } else { + // The tet is degenerated. + if (volume != NULL) { + *volume = 0; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// tetaspectratio() Calculate the aspect ratio of the tetrahedron. // +// // +// The aspect ratio of a tet is L/h, where L is the longest edge length, and // +// h is the shortest height of the tet. // +// // +/////////////////////////////////////////////////////////////////////////////// + +REAL tetgenmesh::tetaspectratio(point pa, point pb, point pc, point pd) +{ + REAL V[6][3], edgelength[6], longlen; + REAL vda[3], vdb[3], vdc[3]; + REAL N[4][3], A[4][4], rhs[4], D; + REAL H[4], volume, minheightinv; + int indx[4]; + int i, j; + + // Set the edge vectors: V[0], ..., V[5] + for (i = 0; i < 3; i++) V[0][i] = pa[i] - pd[i]; + for (i = 0; i < 3; i++) V[1][i] = pb[i] - pd[i]; + for (i = 0; i < 3; i++) V[2][i] = pc[i] - pd[i]; + for (i = 0; i < 3; i++) V[3][i] = pb[i] - pa[i]; + for (i = 0; i < 3; i++) V[4][i] = pc[i] - pb[i]; + for (i = 0; i < 3; i++) V[5][i] = pa[i] - pc[i]; + + // 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. + longlen = edgelength[0]; + for (i = 1; i < 6; i++) { + longlen = edgelength[i] > longlen ? edgelength[i] : longlen; + } + + // Set the matrix A = [vda, vdb, vdc]^T. + for (i = 0; i < 3; i++) A[0][i] = vda[i] = pa[i] - pd[i]; + for (i = 0; i < 3; i++) A[1][i] = vdb[i] = pb[i] - pd[i]; + for (i = 0; i < 3; i++) A[2][i] = vdc[i] = pc[i] - pd[i]; + // Lu-decompose the matrix A. + lu_decmp(A, 3, indx, &D, 0); + // Get the volume of abcd. + volume = (A[indx[0]][0] * A[indx[1]][1] * A[indx[2]][2]) / 6.0; + // Check if it is zero. + if (volume == 0.0) return 1.0e+200; // A degenerate tet. + + // Compute the 4 face normals (N[0], ..., N[3]). + for (j = 0; j < 3; j++) { + for (i = 0; i < 3; i++) rhs[i] = 0.0; + rhs[j] = 1.0; // Positive means the inside direction + lu_solve(A, 3, indx, rhs, 0); + for (i = 0; i < 3; i++) N[j][i] = rhs[i]; + } + // Get the fourth normal by summing up the first three. + for (i = 0; i < 3; i++) N[3][i] = - N[0][i] - N[1][i] - N[2][i]; + // Normalized the normals. + for (i = 0; i < 4; i++) { + // H[i] is the inverse of the height of its corresponding face. + H[i] = sqrt(dot(N[i], N[i])); + // if (H[i] > 0.0) { + // for (j = 0; j < 3; j++) N[i][j] /= H[i]; + // } + } + // Get the radius of the inscribed sphere. + // insradius = 1.0 / (H[0] + H[1] + H[2] + H[3]); + // Get the biggest H[i] (corresponding to the smallest height). + minheightinv = H[0]; + for (i = 1; i < 4; i++) { + if (H[i] > minheightinv) minheightinv = H[i]; + } + + return sqrt(longlen) * minheightinv; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// circumsphere() Calculate the smallest circumsphere (center and radius) // +// of the given three or four points. // +// // +// The circumsphere of four points (a tetrahedron) is unique if they are not // +// degenerate. If 'pd = NULL', the smallest circumsphere of three points is // +// the diametral sphere of the triangle if they are not degenerate. // +// // +// Return TRUE if the input points are not degenerate and the circumcenter // +// and circumradius are returned in 'cent' and 'radius' respectively if they // +// are not NULLs. Otherwise, return FALSE, the four points are co-planar. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenmesh::circumsphere(REAL* pa, REAL* pb, REAL* pc, REAL* pd, + REAL* cent, REAL* radius) +{ + REAL A[4][4], rhs[4], D; + int indx[4]; + + // Compute the coefficient matrix A (3x3). + A[0][0] = pb[0] - pa[0]; + A[0][1] = pb[1] - pa[1]; + A[0][2] = pb[2] - pa[2]; + A[1][0] = pc[0] - pa[0]; + A[1][1] = pc[1] - pa[1]; + A[1][2] = pc[2] - pa[2]; + if (pd != NULL) { + A[2][0] = pd[0] - pa[0]; + A[2][1] = pd[1] - pa[1]; + A[2][2] = pd[2] - pa[2]; + } else { + cross(A[0], A[1], A[2]); + } + + // Compute the right hand side vector b (3x1). + rhs[0] = 0.5 * dot(A[0], A[0]); + rhs[1] = 0.5 * dot(A[1], A[1]); + if (pd != NULL) { + rhs[2] = 0.5 * dot(A[2], A[2]); + } else { + rhs[2] = 0.0; + } + + // Solve the 3 by 3 equations use LU decomposition with partial pivoting + // and backward and forward substitute.. + if (!lu_decmp(A, 3, indx, &D, 0)) { + if (radius != (REAL *) NULL) *radius = 0.0; + return false; + } + lu_solve(A, 3, indx, rhs, 0); + if (cent != (REAL *) NULL) { + cent[0] = pa[0] + rhs[0]; + cent[1] = pa[1] + rhs[1]; + cent[2] = pa[2] + rhs[2]; + } + if (radius != (REAL *) NULL) { + *radius = sqrt(rhs[0] * rhs[0] + rhs[1] * rhs[1] + rhs[2] * rhs[2]); + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// orthosphere() Calulcate the orthosphere of four weighted points. // +// // +// A weighted point (p, P^2) can be interpreted as a sphere centered at the // +// point 'p' with a radius 'P'. The 'height' of 'p' is pheight = p[0]^2 + // +// p[1]^2 + p[2]^2 - P^2. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenmesh::orthosphere(REAL* pa, REAL* pb, REAL* pc, REAL* pd, + REAL aheight, REAL bheight, REAL cheight, + REAL dheight, REAL* orthocent, REAL* radius) +{ + REAL A[4][4], rhs[4], D; + int indx[4]; + + // Set the coefficient matrix A (4 x 4). + A[0][0] = 1.0; A[0][1] = pa[0]; A[0][2] = pa[1]; A[0][3] = pa[2]; + A[1][0] = 1.0; A[1][1] = pb[0]; A[1][2] = pb[1]; A[1][3] = pb[2]; + A[2][0] = 1.0; A[2][1] = pc[0]; A[2][2] = pc[1]; A[2][3] = pc[2]; + A[3][0] = 1.0; A[3][1] = pd[0]; A[3][2] = pd[1]; A[3][3] = pd[2]; + + // Set the right hand side vector (4 x 1). + rhs[0] = 0.5 * aheight; + rhs[1] = 0.5 * bheight; + rhs[2] = 0.5 * cheight; + rhs[3] = 0.5 * dheight; + + // Solve the 4 by 4 equations use LU decomposition with partial pivoting + // and backward and forward substitute.. + if (!lu_decmp(A, 4, indx, &D, 0)) { + if (radius != (REAL *) NULL) *radius = 0.0; + return false; + } + lu_solve(A, 4, indx, rhs, 0); + + if (orthocent != (REAL *) NULL) { + orthocent[0] = rhs[1]; + orthocent[1] = rhs[2]; + orthocent[2] = rhs[3]; + } + if (radius != (REAL *) NULL) { + // rhs[0] = - rheight / 2; + // rheight = - 2 * rhs[0]; + // = r[0]^2 + r[1]^2 + r[2]^2 - radius^2 + // radius^2 = r[0]^2 + r[1]^2 + r[2]^2 -rheight + // = r[0]^2 + r[1]^2 + r[2]^2 + 2 * rhs[0] + *radius = sqrt(rhs[1] * rhs[1] + rhs[2] * rhs[2] + rhs[3] * rhs[3] + + 2.0 * rhs[0]); + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// planelineint() Calculate the intersection of a line and a plane. // +// // +// The equation of a plane (points P are on the plane with normal N and P3 // +// on the plane) can be written as: N dot (P - P3) = 0. The equation of the // +// line (points P on the line passing through P1 and P2) can be written as: // +// P = P1 + u (P2 - P1). The intersection of these two occurs when: // +// N dot (P1 + u (P2 - P1)) = N dot P3. // +// Solving for u gives: // +// N dot (P3 - P1) // +// u = ------------------. // +// N dot (P2 - P1) // +// If the denominator is 0 then N (the normal to the plane) is perpendicular // +// to the line. Thus the line is either parallel to the plane and there are // +// no solutions or the line is on the plane in which case there are an infi- // +// nite number of solutions. // +// // +// The plane is given by three points pa, pb, and pc, e1 and e2 defines the // +// line. If u is non-zero, The intersection point (if exists) returns in ip. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::planelineint(REAL* pa, REAL* pb, REAL* pc, REAL* e1, REAL* e2, + REAL* ip, REAL* u) +{ + REAL n[3], det, det1; + + // Calculate N. + facenormal(pa, pb, pc, n, 1, NULL); + // Calculate N dot (e2 - e1). + det = n[0] * (e2[0] - e1[0]) + n[1] * (e2[1] - e1[1]) + + n[2] * (e2[2] - e1[2]); + if (det != 0.0) { + // Calculate N dot (pa - e1) + det1 = n[0] * (pa[0] - e1[0]) + n[1] * (pa[1] - e1[1]) + + n[2] * (pa[2] - e1[2]); + *u = det1 / det; + ip[0] = e1[0] + *u * (e2[0] - e1[0]); + ip[1] = e1[1] + *u * (e2[1] - e1[1]); + ip[2] = e1[2] + *u * (e2[2] - e1[2]); + } else { + *u = 0.0; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// linelineint() Calculate the intersection(s) of two line segments. // +// // +// Calculate the line segment [P, Q] that is the shortest route between two // +// lines from A to B and C to D. Calculate also the values of tp and tq // +// where: P = A + tp (B - A), and Q = C + tq (D - C). // +// // +// Return 1 if the line segment exists. Otherwise, return 0. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::linelineint(REAL* A, REAL* B, REAL* C, REAL* D, REAL* P, + REAL* Q, REAL* tp, REAL* tq) +{ + REAL vab[3], vcd[3], vca[3]; + REAL vab_vab, vcd_vcd, vab_vcd; + REAL vca_vab, vca_vcd; + REAL det, eps; + int i; + + for (i = 0; i < 3; i++) { + vab[i] = B[i] - A[i]; + vcd[i] = D[i] - C[i]; + vca[i] = A[i] - C[i]; + } + + vab_vab = dot(vab, vab); + vcd_vcd = dot(vcd, vcd); + vab_vcd = dot(vab, vcd); + + det = vab_vab * vcd_vcd - vab_vcd * vab_vcd; + // Round the result. + eps = det / (fabs(vab_vab * vcd_vcd) + fabs(vab_vcd * vab_vcd)); + if (eps < b->epsilon) { + return 0; + } + + vca_vab = dot(vca, vab); + vca_vcd = dot(vca, vcd); + + *tp = (vcd_vcd * (- vca_vab) + vab_vcd * vca_vcd) / det; + *tq = (vab_vcd * (- vca_vab) + vab_vab * vca_vcd) / det; + + for (i = 0; i < 3; i++) P[i] = A[i] + (*tp) * vab[i]; + for (i = 0; i < 3; i++) Q[i] = C[i] + (*tq) * vcd[i]; + + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// tetprismvol() Calculate the volume of a tetrahedral prism in 4D. // +// // +// A tetrahedral prism is a convex uniform polychoron (four dimensional poly-// +// tope). It has 6 polyhedral cells: 2 tetrahedra connected by 4 triangular // +// prisms. It has 14 faces: 8 triangular and 6 square. It has 16 edges and 8 // +// vertices. (Wikipedia). // +// // +// Let 'p0', ..., 'p3' be four affinely independent points in R^3. They form // +// the lower tetrahedral facet of the prism. The top tetrahedral facet is // +// formed by four vertices, 'p4', ..., 'p7' in R^4, which is obtained by // +// lifting each vertex of the lower facet into R^4 by a weight (height). A // +// canonical choice of the weights is the square of Euclidean norm of of the // +// points (vectors). // +// // +// // +// The return value is (4!) 24 times of the volume of the tetrahedral prism. // +// // +/////////////////////////////////////////////////////////////////////////////// + +REAL tetgenmesh::tetprismvol(REAL* p0, REAL* p1, REAL* p2, REAL* p3) +{ + REAL *p4, *p5, *p6, *p7; + REAL w4, w5, w6, w7; + REAL vol[4]; + + p4 = p0; + p5 = p1; + p6 = p2; + p7 = p3; + + // TO DO: these weights can be pre-calculated! + w4 = dot(p0, p0); + w5 = dot(p1, p1); + w6 = dot(p2, p2); + w7 = dot(p3, p3); + + // Calculate the volume of the tet-prism. + vol[0] = orient4d(p5, p6, p4, p3, p7, w5, w6, w4, 0, w7); + vol[1] = orient4d(p3, p6, p2, p0, p1, 0, w6, 0, 0, 0); + vol[2] = orient4d(p4, p6, p3, p0, p1, w4, w6, 0, 0, 0); + vol[3] = orient4d(p6, p5, p4, p3, p1, w6, w5, w4, 0, 0); + + return fabs(vol[0]) + fabs(vol[1]) + fabs(vol[2]) + fabs(vol[3]); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// calculateabovepoint() Calculate a point above a facet in 'dummypoint'. // +// // +/////////////////////////////////////////////////////////////////////////////// + +bool tetgenmesh::calculateabovepoint(arraypool *facpoints, point *ppa, + point *ppb, point *ppc) +{ + point *ppt, pa, pb, pc; + REAL v1[3], v2[3], n[3]; + REAL lab, len, A, area; + REAL x, y, z; + int i; + + ppt = (point *) fastlookup(facpoints, 0); + pa = *ppt; // a is the first point. + pb = pc = NULL; // Avoid compiler warnings. + + // Get a point b s.t. the length of [a, b] is maximal. + lab = 0; + for (i = 1; i < facpoints->objects; i++) { + ppt = (point *) fastlookup(facpoints, i); + x = (*ppt)[0] - pa[0]; + y = (*ppt)[1] - pa[1]; + z = (*ppt)[2] - pa[2]; + len = x * x + y * y + z * z; + if (len > lab) { + lab = len; + pb = *ppt; + } + } + lab = sqrt(lab); + if (lab == 0) { + if (!b->quiet) { + printf("Warning: All points of a facet are coincident with %d.\n", + pointmark(pa)); + } + return false; + } + + // Get a point c s.t. the area of [a, b, c] is maximal. + v1[0] = pb[0] - pa[0]; + v1[1] = pb[1] - pa[1]; + v1[2] = pb[2] - pa[2]; + A = 0; + for (i = 1; i < facpoints->objects; i++) { + ppt = (point *) fastlookup(facpoints, i); + v2[0] = (*ppt)[0] - pa[0]; + v2[1] = (*ppt)[1] - pa[1]; + v2[2] = (*ppt)[2] - pa[2]; + cross(v1, v2, n); + area = dot(n, n); + if (area > A) { + A = area; + pc = *ppt; + } + } + if (A == 0) { + // All points are collinear. No above point. + if (!b->quiet) { + printf("Warning: All points of a facet are collinaer with [%d, %d].\n", + pointmark(pa), pointmark(pb)); + } + return false; + } + + // Calculate an above point of this facet. + facenormal(pa, pb, pc, n, 1, NULL); + len = sqrt(dot(n, n)); + n[0] /= len; + n[1] /= len; + n[2] /= len; + lab /= 2.0; // Half the maximal length. + dummypoint[0] = pa[0] + lab * n[0]; + dummypoint[1] = pa[1] + lab * n[1]; + dummypoint[2] = pa[2] + lab * n[2]; + + if (ppa != NULL) { + // Return the three points. + *ppa = pa; + *ppb = pb; + *ppc = pc; + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// Calculate an above point. It lies above the plane containing the subface // +// [a,b,c], and save it in dummypoint. Moreover, the vector pa->dummypoint // +// is the normal of the plane. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::calculateabovepoint4(point pa, point pb, point pc, point pd) +{ + REAL n1[3], n2[3], *norm; + REAL len, len1, len2; + + // Select a base. + facenormal(pa, pb, pc, n1, 1, NULL); + len1 = sqrt(dot(n1, n1)); + facenormal(pa, pb, pd, n2, 1, NULL); + len2 = sqrt(dot(n2, n2)); + if (len1 > len2) { + norm = n1; + len = len1; + } else { + norm = n2; + len = len2; + } + 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]; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// report_overlapping_facets() Report two overlapping facets. // +// // +// Two subfaces, f1 [a, b, c] and f2 [a, b, d], share the same edge [a, b]. // +// 'dihedang' is the dihedral angle between these two facets. It must less // +// than the variable 'b->facet_overlap_angle_tol'. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::report_overlapping_facets(face *f1, face *f2, REAL dihedang) +{ + point pa, pb, pc, pd; + + pa = sorg(*f1); + pb = sdest(*f1); + pc = sapex(*f1); + pd = sapex(*f2); + + if (pc != pd) { + printf("Found two %s self-intersecting facets.\n", + dihedang > 0 ? "nearly" : "exactly"); + printf(" 1st: [%d, %d, %d] #%d\n", + pointmark(pa), pointmark(pb), pointmark(pc), shellmark(*f1)); + printf(" 2nd: [%d, %d, %d] #%d\n", + pointmark(pa), pointmark(pb), pointmark(pd), shellmark(*f2)); + if (dihedang > 0) { + printf("The dihedral angle between them is %g degree.\n", + dihedang / PI * 180.0); + printf("Hint: You may use -p/# to decrease the dihedral angle"); + printf(" tolerance %g (degree).\n", b->facet_overlap_ang_tol); + } + } else { + if (shellmark(*f1) != shellmark(*f2)) { + // Two identical faces from two different facet. + printf("Found two overlapping facets.\n"); + } else { + printf("Found two duplicated facets.\n"); + } + printf(" 1st: [%d, %d, %d] #%d\n", + pointmark(pa), pointmark(pb), pointmark(pc), shellmark(*f1)); + printf(" 2nd: [%d, %d, %d] #%d\n", + pointmark(pa), pointmark(pb), pointmark(pd), shellmark(*f2)); + } + + // Return the information + sevent.e_type = 6; + sevent.f_marker1 = shellmark(*f1); + sevent.f_vertices1[0] = pointmark(pa); + sevent.f_vertices1[1] = pointmark(pb); + sevent.f_vertices1[2] = pointmark(pc); + sevent.f_marker2 = shellmark(*f2); + sevent.f_vertices2[0] = pointmark(pa); + sevent.f_vertices2[1] = pointmark(pb); + sevent.f_vertices2[2] = pointmark(pd); + + terminatetetgen(this, 3); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// report_selfint_edge() Report a self-intersection at an edge. // +// // +// The edge 'e1'->'e2' and the tetrahedron 'itet' intersect. 'dir' indicates // +// that the edge intersects the tet at its origin vertex (ACROSSVERTEX), or // +// its current face (ACROSSFACE), or its current edge (ACROSSEDGE). // +// If 'iedge' is not NULL, it is either a segment or a subface that contains // +// the edge 'e1'->'e2'. It is used to report the geometry entity. // +// // +// Since it is a self-intersection, the vertex, edge or face of 'itet' that // +// is intersecting with this edge must be an input vertex, a segment, or a // +// subface, respectively. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::report_selfint_edge(point e1, point e2, face *iedge, + triface* itet, enum interresult dir) +{ + point forg = NULL, fdest = NULL, fapex = NULL; + int etype = 0, geomtag = 0, facemark = 0; + + if (iedge != NULL) { + if (iedge->sh[5] != NULL) { + etype = 2; // A subface + forg = e1; + fdest = e2; + fapex = sapex(*iedge); + facemark = shellmark(*iedge); + } else { + etype = 1; // A segment + forg = farsorg(*iedge); + fdest = farsdest(*iedge); + // Get a facet containing this segment. + face parentsh; + spivot(*iedge, parentsh); + if (parentsh.sh != NULL) { + facemark = shellmark(parentsh); + } + } + geomtag = shellmark(*iedge); + } + + if (dir == SHAREEDGE) { + // Two edges (segments) are coincide. + face colseg; + tsspivot1(*itet, colseg); + if (etype == 1) { + if (colseg.sh != iedge->sh) { + face parentsh; + spivot(colseg, parentsh); + printf("PLC Error: Two segments are overlapping.\n"); + printf(" Segment 1: [%d, %d] #%d (%d)\n", pointmark(sorg(colseg)), + pointmark(sdest(colseg)), shellmark(colseg), + parentsh.sh ? shellmark(parentsh) : 0); + printf(" Segment 2: [%d, %d] #%d (%d)\n", pointmark(forg), + pointmark(fdest), geomtag, facemark); + sevent.e_type = 4; + sevent.f_marker1 = (parentsh.sh ? shellmark(parentsh) : 0); + sevent.s_marker1 = shellmark(colseg); + sevent.f_vertices1[0] = pointmark( sorg(colseg)); + sevent.f_vertices1[1] = pointmark(sdest(colseg)); + sevent.f_vertices1[2] = 0; + sevent.f_marker2 = facemark; + sevent.s_marker2 = geomtag; + sevent.f_vertices2[0] = pointmark(forg); + sevent.f_vertices2[1] = pointmark(fdest); + sevent.f_vertices2[2] = 0; + } else { + // Two identical segments. Why report it? + terminatetetgen(this, 2); + } + } else if (etype == 2) { + printf("PLC Error: A segment lies in a facet.\n"); + printf(" Segment: [%d, %d] #%d\n", pointmark(sorg(colseg)), + pointmark(sdest(colseg)), shellmark(colseg)); + printf(" Facet: [%d,%d,%d] #%d\n", pointmark(forg), + pointmark(fdest), pointmark(fapex), geomtag); + sevent.e_type = 5; + sevent.f_marker1 = 0; + sevent.s_marker1 = shellmark(colseg); + sevent.f_vertices1[0] = pointmark( sorg(colseg)); + sevent.f_vertices1[1] = pointmark(sdest(colseg)); + sevent.f_vertices1[2] = 0; + sevent.f_marker2 = geomtag; + sevent.s_marker2 = 0; + sevent.f_vertices2[0] = pointmark(forg); + sevent.f_vertices2[1] = pointmark(fdest); + sevent.f_vertices2[2] = pointmark(fapex); + } + } else if (dir == SHAREFACE) { + // Two triangles (subfaces) are coincide. + face colface; + tspivot(*itet, colface); + if (etype == 2) { + if (colface.sh != iedge->sh) { + printf("PLC Error: Two facets are overlapping.\n"); + printf(" Facet 1: [%d,%d,%d] #%d\n", pointmark(forg), + pointmark(fdest), pointmark(fapex), geomtag); + printf(" Facet 2: [%d,%d,%d] #%d\n", pointmark(sorg(colface)), + pointmark(sdest(colface)), pointmark(sapex(colface)), + shellmark(colface)); + sevent.e_type = 6; + sevent.f_marker1 = geomtag; + sevent.s_marker1 = 0; + sevent.f_vertices1[0] = pointmark(forg); + sevent.f_vertices1[1] = pointmark(fdest); + sevent.f_vertices1[2] = pointmark(fapex); + sevent.f_marker2 = shellmark(colface); + sevent.s_marker2 = 0; + sevent.f_vertices2[0] = pointmark(sorg(colface)); + sevent.f_vertices2[1] = pointmark(sdest(colface)); + sevent.f_vertices2[2] = pointmark(sapex(colface)); + } else { + // Two identical subfaces. Why report it? + terminatetetgen(this, 2); + } + } else { + terminatetetgen(this, 2); + } + } else if (dir == ACROSSVERT) { + point pp = dest(*itet); + if ((pointtype(pp) == RIDGEVERTEX) || (pointtype(pp) == FACETVERTEX) + || (pointtype(pp) == VOLVERTEX)) { + if (etype == 1) { + printf("PLC Error: A vertex lies in a segment.\n"); + printf(" Vertex: [%d] (%g,%g,%g).\n",pointmark(pp),pp[0],pp[1],pp[2]); + printf(" Segment: [%d, %d] #%d (%d)\n", pointmark(forg), + pointmark(fdest), geomtag, facemark); + sevent.e_type = 7; + sevent.f_marker1 = 0; + sevent.s_marker1 = 0; + sevent.f_vertices1[0] = pointmark(pp); + sevent.f_vertices1[1] = 0; + sevent.f_vertices1[2] = 0; + sevent.f_marker2 = facemark; + sevent.s_marker2 = geomtag; + sevent.f_vertices2[0] = pointmark(forg); + sevent.f_vertices2[1] = pointmark(fdest); + sevent.f_vertices2[2] = 0; + sevent.int_point[0] = pp[0]; + sevent.int_point[1] = pp[1]; + sevent.int_point[2] = pp[2]; + } else if (etype == 2) { + printf("PLC Error: A vertex lies in a facet.\n"); + printf(" Vertex: [%d] (%g,%g,%g).\n",pointmark(pp),pp[0],pp[1],pp[2]); + printf(" Facet: [%d,%d,%d] #%d\n", pointmark(forg), pointmark(fdest), + pointmark(fapex), geomtag); + sevent.e_type = 8; + sevent.f_marker1 = 0; + sevent.s_marker1 = 0; + sevent.f_vertices1[0] = pointmark(pp); + sevent.f_vertices1[1] = 0; + sevent.f_vertices1[2] = 0; + sevent.f_marker2 = geomtag; + sevent.s_marker2 = 0; + sevent.f_vertices2[0] = pointmark(forg); + sevent.f_vertices2[1] = pointmark(fdest); + sevent.f_vertices2[2] = pointmark(fapex); + sevent.int_point[0] = pp[0]; + sevent.int_point[1] = pp[1]; + sevent.int_point[2] = pp[2]; + } + } else if (pointtype(pp) == FREESEGVERTEX) { + face parentseg, parentsh; + sdecode(point2sh(pp), parentseg); + spivot(parentseg, parentsh); + if (parentseg.sh != NULL) { + point p1 = farsorg(parentseg); + point p2 = farsdest(parentseg); + if (etype == 1) { + printf("PLC Error: Two segments intersect at point (%g,%g,%g).\n", + pp[0], pp[1], pp[2]); + printf(" Segment 1: [%d, %d], #%d (%d)\n", pointmark(forg), + pointmark(fdest), geomtag, facemark); + printf(" Segment 2: [%d, %d], #%d (%d)\n", pointmark(p1), + pointmark(p2), shellmark(parentseg), + parentsh.sh ? shellmark(parentsh) : 0); + sevent.e_type = 1; + sevent.f_marker1 = facemark; + sevent.s_marker1 = geomtag; + sevent.f_vertices1[0] = pointmark(forg); + sevent.f_vertices1[1] = pointmark(fdest); + sevent.f_vertices1[2] = 0; + sevent.f_marker2 = (parentsh.sh ? shellmark(parentsh) : 0); + sevent.s_marker2 = shellmark(parentseg); + sevent.f_vertices2[0] = pointmark(p1); + sevent.f_vertices2[1] = pointmark(p2); + sevent.f_vertices2[2] = 0; + sevent.int_point[0] = pp[0]; + sevent.int_point[1] = pp[1]; + sevent.int_point[2] = pp[2]; + } else if (etype == 2) { + printf("PLC Error: A segment and a facet intersect at point"); + printf(" (%g,%g,%g).\n", pp[0], pp[1], pp[2]); + printf(" Segment: [%d, %d], #%d (%d)\n", pointmark(p1), + pointmark(p2), shellmark(parentseg), + parentsh.sh ? shellmark(parentsh) : 0); + printf(" Facet: [%d,%d,%d] #%d\n", pointmark(forg), + pointmark(fdest), pointmark(fapex), geomtag); + sevent.e_type = 2; + sevent.f_marker1 = (parentsh.sh ? shellmark(parentsh) : 0); + sevent.s_marker1 = shellmark(parentseg); + sevent.f_vertices1[0] = pointmark(p1); + sevent.f_vertices1[1] = pointmark(p2); + sevent.f_vertices1[2] = 0; + sevent.f_marker2 = geomtag; + sevent.s_marker2 = 0; + sevent.f_vertices2[0] = pointmark(forg); + sevent.f_vertices2[1] = pointmark(fdest); + sevent.f_vertices2[2] = pointmark(fapex); + sevent.int_point[0] = pp[0]; + sevent.int_point[1] = pp[1]; + sevent.int_point[2] = pp[2]; + } + } else { + terminatetetgen(this, 2); // Report a bug. + } + } else if (pointtype(pp) == FREEFACETVERTEX) { + face parentsh; + sdecode(point2sh(pp), parentsh); + if (parentsh.sh != NULL) { + point p1 = sorg(parentsh); + point p2 = sdest(parentsh); + point p3 = sapex(parentsh); + if (etype == 1) { + printf("PLC Error: A segment and a facet intersect at point"); + printf(" (%g,%g,%g).\n", pp[0], pp[1], pp[2]); + printf(" Segment : [%d, %d], #%d (%d)\n", pointmark(forg), + pointmark(fdest), geomtag, facemark); + printf(" Facet : [%d, %d, %d] #%d.\n", pointmark(p1), + pointmark(p2), pointmark(p3), shellmark(parentsh)); + sevent.e_type = 2; + sevent.f_marker1 = facemark; + sevent.s_marker1 = geomtag; + sevent.f_vertices1[0] = pointmark(forg); + sevent.f_vertices1[1] = pointmark(fdest); + sevent.f_vertices1[2] = 0; + sevent.f_marker2 = shellmark(parentsh); + sevent.s_marker2 = 0; + sevent.f_vertices2[0] = pointmark(p1); + sevent.f_vertices2[1] = pointmark(p2); + sevent.f_vertices2[2] = pointmark(p3); + sevent.int_point[0] = pp[0]; + sevent.int_point[1] = pp[1]; + sevent.int_point[2] = pp[2]; + } else if (etype == 2) { + printf("PLC Error: Two facets intersect at point (%g,%g,%g).\n", + pp[0], pp[1], pp[2]); + printf(" Facet 1: [%d, %d, %d] #%d.\n", pointmark(forg), + pointmark(fdest), pointmark(fapex), geomtag); + printf(" Facet 2: [%d, %d, %d] #%d.\n", pointmark(p1), + pointmark(p2), pointmark(p3), shellmark(parentsh)); + sevent.e_type = 3; + sevent.f_marker1 = geomtag; + sevent.s_marker1 = 0; + sevent.f_vertices1[0] = pointmark(forg); + sevent.f_vertices1[1] = pointmark(fdest); + sevent.f_vertices1[2] = pointmark(fapex); + sevent.f_marker2 = shellmark(parentsh); + sevent.s_marker2 = 0; + sevent.f_vertices2[0] = pointmark(p1); + sevent.f_vertices2[1] = pointmark(p2); + sevent.f_vertices2[2] = pointmark(p3); + sevent.int_point[0] = pp[0]; + sevent.int_point[1] = pp[1]; + sevent.int_point[2] = pp[2]; + } + } else { + terminatetetgen(this, 2); // Report a bug. + } + } else if (pointtype(pp) == FREEVOLVERTEX) { + // This is not a PLC error. + // We should shift the vertex. + // not down yet. + terminatetetgen(this, 2); // Report a bug. + } else { + terminatetetgen(this, 2); // Report a bug. + } + terminatetetgen(this, 3); + } else if (dir == ACROSSEDGE) { + if (issubseg(*itet)) { + face checkseg; + tsspivot1(*itet, checkseg); + face parentsh; + spivot(checkseg, parentsh); + // Calulcate the intersecting point. + point p1 = sorg(checkseg); + point p2 = sdest(checkseg); + REAL P[3], Q[3], tp = 0, tq = 0; + linelineint(e1, e2, p1, p2, P, Q, &tp, &tq); + if (etype == 1) { + printf("PLC Error: Two segments intersect at point (%g,%g,%g).\n", + P[0], P[1], P[2]); + printf(" Segment 1: [%d, %d] #%d (%d)\n", pointmark(forg), + pointmark(fdest), geomtag, facemark); + printf(" Segment 2: [%d, %d] #%d (%d)\n", pointmark(p1), + pointmark(p2), shellmark(checkseg), + parentsh.sh ? shellmark(parentsh) : 0); + sevent.e_type = 1; + sevent.f_marker1 = facemark; + sevent.s_marker1 = geomtag; + sevent.f_vertices1[0] = pointmark(forg); + sevent.f_vertices1[1] = pointmark(fdest); + sevent.f_vertices1[2] = 0; + sevent.f_marker2 = (parentsh.sh ? shellmark(parentsh) : 0); + sevent.s_marker2 = shellmark(checkseg); + sevent.f_vertices2[0] = pointmark(p1); + sevent.f_vertices2[1] = pointmark(p2); + sevent.f_vertices2[2] = 0; + sevent.int_point[0] = P[0]; + sevent.int_point[1] = P[1]; + sevent.int_point[2] = P[2]; + } else if (etype == 2) { + printf("PLC Error: A segment and a facet intersect at point"); + printf(" (%g,%g,%g).\n", P[0], P[1], P[2]); + printf(" Segment: [%d, %d] #%d (%d)\n", pointmark(p1), + pointmark(p2), shellmark(checkseg), + parentsh.sh ? shellmark(parentsh) : 0); + printf(" Facet: [%d, %d, %d] #%d.\n", pointmark(forg), + pointmark(fdest), pointmark(fapex), geomtag); + sevent.e_type = 2; + sevent.f_marker1 = (parentsh.sh ? shellmark(parentsh) : 0); + sevent.s_marker1 = shellmark(checkseg); + sevent.f_vertices1[0] = pointmark(p1); + sevent.f_vertices1[1] = pointmark(p2); + sevent.f_vertices1[2] = 0; + sevent.f_marker2 = geomtag; + sevent.s_marker2 = 0; + sevent.f_vertices2[0] = pointmark(forg); + sevent.f_vertices2[1] = pointmark(fdest); + sevent.f_vertices2[2] = pointmark(fapex); + sevent.int_point[0] = P[0]; + sevent.int_point[1] = P[1]; + sevent.int_point[2] = P[2]; + } + terminatetetgen(this, 3); + } + } else if (dir == ACROSSFACE) { + if (issubface(*itet)) { + face checksh; + tspivot(*itet, checksh); + point p1 = sorg(checksh); + point p2 = sdest(checksh); + point p3 = sapex(checksh); + REAL ip[3], u = 0; + planelineint(p1, p2, p3, e1, e2, ip, &u); + if (etype == 1) { + printf("PLC Error: A segment and a facet intersect at point"); + printf(" (%g,%g,%g).\n", ip[0], ip[1], ip[2]); + printf(" Segment: [%d, %d] #%d (%d)\n", pointmark(forg), + pointmark(fdest), geomtag, facemark); + printf(" Facet: [%d, %d, %d] #%d.\n", pointmark(p1), + pointmark(p2), pointmark(p3), shellmark(checksh)); + sevent.e_type = 2; + sevent.f_marker1 = facemark; + sevent.s_marker1 = geomtag; + sevent.f_vertices1[0] = pointmark(forg); + sevent.f_vertices1[1] = pointmark(fdest); + sevent.f_vertices1[2] = 0; + sevent.f_marker2 = shellmark(checksh); + sevent.s_marker2 = 0; + sevent.f_vertices2[0] = pointmark(p1); + sevent.f_vertices2[1] = pointmark(p2); + sevent.f_vertices2[2] = pointmark(p3); + sevent.int_point[0] = ip[0]; + sevent.int_point[1] = ip[1]; + sevent.int_point[2] = ip[2]; + } else if (etype == 2) { + printf("PLC Error: Two facets intersect at point (%g,%g,%g).\n", + ip[0], ip[1], ip[2]); + printf(" Facet 1: [%d, %d, %d] #%d.\n", pointmark(forg), + pointmark(fdest), pointmark(fapex), geomtag); + printf(" Facet 2: [%d, %d, %d] #%d.\n", pointmark(p1), + pointmark(p2), pointmark(p3), shellmark(checksh)); + sevent.e_type = 3; + sevent.f_marker1 = geomtag; + sevent.s_marker1 = 0; + sevent.f_vertices1[0] = pointmark(forg); + sevent.f_vertices1[1] = pointmark(fdest); + sevent.f_vertices1[2] = pointmark(fapex); + sevent.f_marker2 = shellmark(checksh); + sevent.s_marker2 = 0; + sevent.f_vertices2[0] = pointmark(p1); + sevent.f_vertices2[1] = pointmark(p2); + sevent.f_vertices2[2] = pointmark(p3); + sevent.int_point[0] = ip[0]; + sevent.int_point[1] = ip[1]; + sevent.int_point[2] = ip[2]; + } + terminatetetgen(this, 3); + } + } else { + // An unknown 'dir'. + terminatetetgen(this, 2); + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// report_selfint_face() Report a self-intersection at a facet. // +// // +// The triangle with vertices 'p1', 'p2', and 'p3' intersects with the edge // +// of the tetrahedra 'iedge'. The intersection type is reported by 'intflag',// +// 'types', and 'poss'. // +// // +// This routine ASSUMES (1) the triangle (p1,p2,p3) must belong to a facet, // +// 'sface' is a subface of the same facet; and (2) 'iedge' must be either a // +// segment or an edge of another facet. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::report_selfint_face(point p1, point p2, point p3, face* sface, + triface* iedge, int intflag, int* types, int* poss) +{ + face iface; + point e1 = NULL, e2 = NULL, e3 = NULL; + int etype = 0, geomtag = 0, facemark = 0; + + geomtag = shellmark(*sface); + + if (issubface(*iedge)) { + tspivot(*iedge, iface); + e1 = sorg(iface); + e2 = sdest(iface); + e3 = sapex(iface); + etype = 2; + facemark = geomtag; + } else if (issubseg(*iedge)) { + tsspivot1(*iedge, iface); + e1 = farsorg(iface); + e2 = farsdest(iface); + etype = 1; + face parentsh; + spivot(iface, parentsh); + facemark = shellmark(parentsh); + } else { + terminatetetgen(this, 2); + } + + if (intflag == 2) { + // The triangle and the edge intersect only at one point. + REAL ip[3], u = 0; + planelineint(p1, p2, p3, e1, e2, ip, &u); + if ((types[0] == (int) ACROSSFACE) || + (types[0] == (int) ACROSSEDGE)) { + // The triangle and the edge intersect in their interiors. + if (etype == 1) { + printf("PLC Error: A segment and a facet intersect at point"); + printf(" (%g,%g,%g).\n", ip[0], ip[1], ip[2]); + printf(" Segment: [%d,%d] #%d (%d)\n", pointmark(e1), pointmark(e2), + shellmark(iface), facemark); + printf(" Facet: [%d,%d,%d] #%d\n", pointmark(p1), + pointmark(p2), pointmark(p3), geomtag); + sevent.e_type = 2; + sevent.f_marker1 = facemark; + sevent.s_marker1 = shellmark(iface); + sevent.f_vertices1[0] = pointmark(e1); + sevent.f_vertices1[1] = pointmark(e2); + sevent.f_vertices1[2] = 0; + sevent.f_marker2 = geomtag; + sevent.s_marker2 = 0; + sevent.f_vertices2[0] = pointmark(p1); + sevent.f_vertices2[1] = pointmark(p2); + sevent.f_vertices2[2] = pointmark(p3); + sevent.int_point[0] = ip[0]; + sevent.int_point[1] = ip[1]; + sevent.int_point[2] = ip[2]; + } else { + printf("PLC Error: Two facets intersect at point"); + printf(" (%g,%g,%g).\n", ip[0], ip[1], ip[2]); + printf(" Facet 1: [%d,%d,%d] #%d\n", pointmark(e1), pointmark(e2), + pointmark(sorg(iface)), shellmark(iface)); + printf(" Facet 2: [%d,%d,%d] #%d\n", pointmark(p1), + pointmark(p2), pointmark(p3), geomtag); + sevent.e_type = 3; + sevent.f_marker1 = shellmark(iface); + sevent.s_marker1 = 0; + sevent.f_vertices1[0] = pointmark(e1); + sevent.f_vertices1[1] = pointmark(e2); + sevent.f_vertices1[2] = pointmark(sorg(iface)); + sevent.f_marker2 = geomtag; + sevent.s_marker2 = 0; + sevent.f_vertices2[0] = pointmark(p1); + sevent.f_vertices2[1] = pointmark(p2); + sevent.f_vertices2[2] = pointmark(p3); + sevent.int_point[0] = ip[0]; + sevent.int_point[1] = ip[1]; + sevent.int_point[2] = ip[2]; + } + } else if (types[0] == (int) ACROSSVERT) { + // A vertex of the triangle and the edge intersect. + point crosspt = NULL; + if (poss[0] == 0) { + crosspt = p1; + } else if (poss[0] == 1) { + crosspt = p2; + } else if (poss[0] == 2) { + crosspt = p3; + } else { + terminatetetgen(this, 2); + } + if (!issteinerpoint(crosspt)) { + if (etype == 1) { + printf("PLC Error: A vertex and a segment intersect at (%g,%g,%g)\n", + crosspt[0], crosspt[1], crosspt[2]); + printf(" Vertex: #%d\n", pointmark(crosspt)); + printf(" Segment: [%d,%d] #%d (%d)\n", pointmark(e1), pointmark(e2), + shellmark(iface), facemark); + sevent.e_type = 7; + sevent.f_marker1 = 0; + sevent.s_marker1 = 0; + sevent.f_vertices1[0] = pointmark(crosspt); + sevent.f_vertices1[1] = 0; + sevent.f_vertices1[2] = 0; + sevent.f_marker2 = facemark; + sevent.s_marker2 = shellmark(iface); + sevent.f_vertices2[0] = pointmark(e1); + sevent.f_vertices2[1] = pointmark(e2); + sevent.f_vertices2[2] = 0; + sevent.int_point[0] = crosspt[0]; + sevent.int_point[1] = crosspt[1]; + sevent.int_point[2] = crosspt[2]; + } else { + printf("PLC Error: A vertex and a facet intersect at (%g,%g,%g)\n", + crosspt[0], crosspt[1], crosspt[2]); + printf(" Vertex: #%d\n", pointmark(crosspt)); + printf(" Facet: [%d,%d,%d] #%d\n", pointmark(p1), + pointmark(p2), pointmark(p3), geomtag); + sevent.e_type = 8; + sevent.f_marker1 = 0; + sevent.s_marker1 = 0; + sevent.f_vertices1[0] = pointmark(crosspt); + sevent.f_vertices1[1] = 0; + sevent.f_vertices1[2] = 0; + sevent.f_marker2 = geomtag; + sevent.s_marker2 = 0; + sevent.f_vertices2[0] = pointmark(p1); + sevent.f_vertices2[1] = pointmark(p2); + sevent.f_vertices2[2] = pointmark(p3); + sevent.int_point[0] = crosspt[0]; + sevent.int_point[1] = crosspt[1]; + sevent.int_point[2] = crosspt[2]; + } + } else { + // It is a Steiner point. To be processed. + terminatetetgen(this, 2); + } + } else if ((types[0] == (int) TOUCHFACE) || + (types[0] == (int) TOUCHEDGE)) { + // The triangle and a vertex of the edge intersect. + point touchpt = NULL; + if (poss[1] == 0) { + touchpt = org(*iedge); + } else if (poss[1] == 1) { + touchpt = dest(*iedge); + } else { + terminatetetgen(this, 2); + } + if (!issteinerpoint(touchpt)) { + printf("PLC Error: A vertex and a facet intersect at (%g,%g,%g)\n", + touchpt[0], touchpt[1], touchpt[2]); + printf(" Vertex: #%d\n", pointmark(touchpt)); + printf(" Facet: [%d,%d,%d] #%d\n", pointmark(p1), + pointmark(p2), pointmark(p3), geomtag); + sevent.e_type = 8; + sevent.f_marker1 = 0; + sevent.s_marker1 = 0; + sevent.f_vertices1[0] = pointmark(touchpt); + sevent.f_vertices1[1] = 0; + sevent.f_vertices1[2] = 0; + sevent.f_marker2 = geomtag; + sevent.s_marker2 = 0; + sevent.f_vertices2[0] = pointmark(p1); + sevent.f_vertices2[1] = pointmark(p2); + sevent.f_vertices2[2] = pointmark(p3); + sevent.int_point[0] = touchpt[0]; + sevent.int_point[1] = touchpt[1]; + sevent.int_point[2] = touchpt[2]; + } else { + // It is a Steiner point. To be processed. + terminatetetgen(this, 2); + } + } else if (types[0] == (int) SHAREVERT) { + terminatetetgen(this, 2); + } else { + terminatetetgen(this, 2); + } + } else if (intflag == 4) { + if (types[0] == (int) SHAREFACE) { + printf("PLC Error: Two facets are overlapping.\n"); + printf(" Facet 1: [%d,%d,%d] #%d\n", pointmark(e1), + pointmark(e2), pointmark(e3), facemark); + printf(" Facet 2: [%d,%d,%d] #%d\n", pointmark(p1), + pointmark(p2), pointmark(p3), geomtag); + sevent.e_type = 6; + sevent.f_marker1 = facemark; + sevent.s_marker1 = 0; + sevent.f_vertices1[0] = pointmark(e1); + sevent.f_vertices1[1] = pointmark(e2); + sevent.f_vertices1[2] = pointmark(e3); + sevent.f_marker2 = geomtag; + sevent.s_marker2 = 0; + sevent.f_vertices2[0] = pointmark(p1); + sevent.f_vertices2[1] = pointmark(p2); + sevent.f_vertices2[2] = pointmark(p3); + } else { + terminatetetgen(this, 2); + } + } else { + terminatetetgen(this, 2); + } + + terminatetetgen(this, 3); + return 0; +} + +//// //// +//// //// +//// geom_cxx ///////////////////////////////////////////////////////////////// + +//// flip_cxx ///////////////////////////////////////////////////////////////// +//// //// +//// //// + +/////////////////////////////////////////////////////////////////////////////// +// // +// flip23() Perform a 2-to-3 flip (face-to-edge flip). // +// // +// 'fliptets' is an array of three tets (handles), where the [0] and [1] are // +// [a,b,c,d] and [b,a,c,e]. The three new tets: [e,d,a,b], [e,d,b,c], and // +// [e,d,c,a] are returned in [0], [1], and [2] of 'fliptets'. As a result, // +// The face [a,b,c] is removed, and the edge [d,e] is created. // +// // +// If 'hullflag' > 0, hull tets may be involved in this flip, i.e., one of // +// the five vertices may be 'dummypoint'. There are two canonical cases: // +// (1) d is 'dummypoint', then all three new tets are hull tets. If e is // +// 'dummypoint', we reconfigure e to d, i.e., turn it up-side down. // +// (2) c is 'dummypoint', then two new tets: [e,d,b,c] and [e,d,c,a], are // +// hull tets. If a or b is 'dummypoint', we reconfigure it to c, i.e., // +// rotate the three input tets counterclockwisely (right-hand rule) // +// until a or b is in c's position. // +// // +// If 'fc->enqflag' is set, convex hull faces will be queued for flipping. // +// In particular, if 'fc->enqflag' is 1, it is called by incrementalflip() // +// after the insertion of a new point. It is assumed that 'd' is the new // +// point. IN this case, only link faces of 'd' are queued. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::flip23(triface* fliptets, int hullflag, flipconstraints *fc) +{ + triface topcastets[3], botcastets[3]; + triface newface, casface; + point pa, pb, pc, pd, pe; + REAL attrib, volume; + int dummyflag = 0; // range = {-1, 0, 1, 2}. + int i; + + if (hullflag > 0) { + // Check if e is dummypoint. + if (oppo(fliptets[1]) == dummypoint) { + // Swap the two old tets. + newface = fliptets[0]; + fliptets[0] = fliptets[1]; + fliptets[1] = newface; + dummyflag = -1; // d is dummypoint. + } else { + // Check if either a or b is dummypoint. + if (org(fliptets[0]) == dummypoint) { + dummyflag = 1; // a is dummypoint. + enextself(fliptets[0]); + eprevself(fliptets[1]); + } else if (dest(fliptets[0]) == dummypoint) { + dummyflag = 2; // b is dummypoint. + eprevself(fliptets[0]); + enextself(fliptets[1]); + } else { + dummyflag = 0; // either c or d may be dummypoint. + } + } + } + + pa = org(fliptets[0]); + pb = dest(fliptets[0]); + pc = apex(fliptets[0]); + pd = oppo(fliptets[0]); + pe = oppo(fliptets[1]); + + flip23count++; + + // Get the outer boundary faces. + for (i = 0; i < 3; i++) { + fnext(fliptets[0], topcastets[i]); + enextself(fliptets[0]); + } + for (i = 0; i < 3; i++) { + fnext(fliptets[1], botcastets[i]); + eprevself(fliptets[1]); + } + + // Re-use fliptets[0] and fliptets[1]. + fliptets[0].ver = 11; + fliptets[1].ver = 11; + setelemmarker(fliptets[0].tet, 0); // Clear all flags. + setelemmarker(fliptets[1].tet, 0); + // NOTE: the element attributes and volume constraint remain unchanged. + if (checksubsegflag) { + // Dealloc the space to subsegments. + if (fliptets[0].tet[8] != NULL) { + tet2segpool->dealloc((shellface *) fliptets[0].tet[8]); + fliptets[0].tet[8] = NULL; + } + if (fliptets[1].tet[8] != NULL) { + tet2segpool->dealloc((shellface *) fliptets[1].tet[8]); + fliptets[1].tet[8] = NULL; + } + } + if (checksubfaceflag) { + // Dealloc the space to subfaces. + if (fliptets[0].tet[9] != NULL) { + tet2subpool->dealloc((shellface *) fliptets[0].tet[9]); + fliptets[0].tet[9] = NULL; + } + if (fliptets[1].tet[9] != NULL) { + tet2subpool->dealloc((shellface *) fliptets[1].tet[9]); + fliptets[1].tet[9] = NULL; + } + } + // Create a new tet. + maketetrahedron(&(fliptets[2])); + // The new tet have the same attributes from the old tet. + for (i = 0; i < numelemattrib; i++) { + attrib = elemattribute(fliptets[0].tet, i); + setelemattribute(fliptets[2].tet, i, attrib); + } + if (b->varvolume) { + volume = volumebound(fliptets[0].tet); + setvolumebound(fliptets[2].tet, volume); + } + + if (hullflag > 0) { + // Check if d is dummytet. + if (pd != dummypoint) { + setvertices(fliptets[0], pe, pd, pa, pb); // [e,d,a,b] * + setvertices(fliptets[1], pe, pd, pb, pc); // [e,d,b,c] * + // Check if c is dummypoint. + if (pc != dummypoint) { + setvertices(fliptets[2], pe, pd, pc, pa); // [e,d,c,a] * + } else { + setvertices(fliptets[2], pd, pe, pa, pc); // [d,e,a,c] + esymself(fliptets[2]); // [e,d,c,a] * + } + // The hullsize does not change. + } else { + // d is dummypoint. + setvertices(fliptets[0], pa, pb, pe, pd); // [a,b,e,d] + setvertices(fliptets[1], pb, pc, pe, pd); // [b,c,e,d] + setvertices(fliptets[2], pc, pa, pe, pd); // [c,a,e,d] + // Adjust the faces to [e,d,a,b], [e,d,b,c], [e,d,c,a] * + for (i = 0; i < 3; i++) { + eprevesymself(fliptets[i]); + enextself(fliptets[i]); + } + // We deleted one hull tet, and created three hull tets. + hullsize += 2; + } + } else { + setvertices(fliptets[0], pe, pd, pa, pb); // [e,d,a,b] * + setvertices(fliptets[1], pe, pd, pb, pc); // [e,d,b,c] * + setvertices(fliptets[2], pe, pd, pc, pa); // [e,d,c,a] * + } + + if (fc->remove_ndelaunay_edge) { // calc_tetprism_vol + REAL volneg[2], volpos[3], vol_diff; + if (pd != dummypoint) { + if (pc != dummypoint) { + volpos[0] = tetprismvol(pe, pd, pa, pb); + volpos[1] = tetprismvol(pe, pd, pb, pc); + volpos[2] = tetprismvol(pe, pd, pc, pa); + volneg[0] = tetprismvol(pa, pb, pc, pd); + volneg[1] = tetprismvol(pb, pa, pc, pe); + } else { // pc == dummypoint + volpos[0] = tetprismvol(pe, pd, pa, pb); + volpos[1] = 0.; + volpos[2] = 0.; + volneg[0] = 0.; + volneg[1] = 0.; + } + } else { // pd == dummypoint. + volpos[0] = 0.; + volpos[1] = 0.; + volpos[2] = 0.; + volneg[0] = 0.; + volneg[1] = tetprismvol(pb, pa, pc, pe); + } + vol_diff = volpos[0] + volpos[1] + volpos[2] - volneg[0] - volneg[1]; + fc->tetprism_vol_sum += vol_diff; // Update the total sum. + } + + // Bond three new tets together. + for (i = 0; i < 3; i++) { + esym(fliptets[i], newface); + bond(newface, fliptets[(i + 1) % 3]); + } + // Bond to top outer boundary faces (at [a,b,c,d]). + for (i = 0; i < 3; i++) { + eorgoppo(fliptets[i], newface); // At edges [b,a], [c,b], [a,c]. + bond(newface, topcastets[i]); + } + // Bond bottom outer boundary faces (at [b,a,c,e]). + for (i = 0; i < 3; i++) { + edestoppo(fliptets[i], newface); // At edges [a,b], [b,c], [c,a]. + bond(newface, botcastets[i]); + } + + if (checksubsegflag) { + // Bond subsegments if there are. + // Each new tet has 5 edges to be checked (except the edge [e,d]). + face checkseg; + // The middle three: [a,b], [b,c], [c,a]. + for (i = 0; i < 3; i++) { + if (issubseg(topcastets[i])) { + tsspivot1(topcastets[i], checkseg); + eorgoppo(fliptets[i], newface); + tssbond1(newface, checkseg); + sstbond1(checkseg, newface); + if (fc->chkencflag & 1) { + enqueuesubface(badsubsegs, &checkseg); + } + } + } + // The top three: [d,a], [d,b], [d,c]. Two tets per edge. + for (i = 0; i < 3; i++) { + eprev(topcastets[i], casface); + if (issubseg(casface)) { + tsspivot1(casface, checkseg); + enext(fliptets[i], newface); + tssbond1(newface, checkseg); + sstbond1(checkseg, newface); + esym(fliptets[(i + 2) % 3], newface); + eprevself(newface); + tssbond1(newface, checkseg); + sstbond1(checkseg, newface); + if (fc->chkencflag & 1) { + enqueuesubface(badsubsegs, &checkseg); + } + } + } + // The bot three: [a,e], [b,e], [c,e]. Two tets per edge. + for (i = 0; i < 3; i++) { + enext(botcastets[i], casface); + if (issubseg(casface)) { + tsspivot1(casface, checkseg); + eprev(fliptets[i], newface); + tssbond1(newface, checkseg); + sstbond1(checkseg, newface); + esym(fliptets[(i + 2) % 3], newface); + enextself(newface); + tssbond1(newface, checkseg); + sstbond1(checkseg, newface); + if (fc->chkencflag & 1) { + enqueuesubface(badsubsegs, &checkseg); + } + } + } + } // if (checksubsegflag) + + if (checksubfaceflag) { + // Bond 6 subfaces if there are. + face checksh; + for (i = 0; i < 3; i++) { + if (issubface(topcastets[i])) { + tspivot(topcastets[i], checksh); + eorgoppo(fliptets[i], newface); + sesymself(checksh); + tsbond(newface, checksh); + if (fc->chkencflag & 2) { + enqueuesubface(badsubfacs, &checksh); + } + } + } + for (i = 0; i < 3; i++) { + if (issubface(botcastets[i])) { + tspivot(botcastets[i], checksh); + edestoppo(fliptets[i], newface); + sesymself(checksh); + tsbond(newface, checksh); + if (fc->chkencflag & 2) { + enqueuesubface(badsubfacs, &checksh); + } + } + } + } // if (checksubfaceflag) + + if (fc->chkencflag & 4) { + // Put three new tets into check list. + for (i = 0; i < 3; i++) { + enqueuetetrahedron(&(fliptets[i])); + } + } + + // Update the point-to-tet map. + setpoint2tet(pa, (tetrahedron) fliptets[0].tet); + setpoint2tet(pb, (tetrahedron) fliptets[0].tet); + setpoint2tet(pc, (tetrahedron) fliptets[1].tet); + setpoint2tet(pd, (tetrahedron) fliptets[0].tet); + setpoint2tet(pe, (tetrahedron) fliptets[0].tet); + + if (hullflag > 0) { + if (dummyflag != 0) { + // Restore the original position of the points (for flipnm()). + if (dummyflag == -1) { + // Reverse the edge. + for (i = 0; i < 3; i++) { + esymself(fliptets[i]); + } + // Swap the last two new tets. + newface = fliptets[1]; + fliptets[1] = fliptets[2]; + fliptets[2] = newface; + } else { + // either a or b were swapped. + if (dummyflag == 1) { + // a is dummypoint. + newface = fliptets[0]; + fliptets[0] = fliptets[2]; + fliptets[2] = fliptets[1]; + fliptets[1] = newface; + } else { // dummyflag == 2 + // b is dummypoint. + newface = fliptets[0]; + fliptets[0] = fliptets[1]; + fliptets[1] = fliptets[2]; + fliptets[2] = newface; + } + } + } + } + + if (fc->enqflag > 0) { + // Queue faces which may be locally non-Delaunay. + for (i = 0; i < 3; i++) { + eprevesym(fliptets[i], newface); + flippush(flipstack, &newface); + } + if (fc->enqflag > 1) { + for (i = 0; i < 3; i++) { + enextesym(fliptets[i], newface); + flippush(flipstack, &newface); + } + } + } + + recenttet = fliptets[0]; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// flip32() Perform a 3-to-2 flip (edge-to-face flip). // +// // +// 'fliptets' is an array of three tets (handles), which are [e,d,a,b], // +// [e,d,b,c], and [e,d,c,a]. The two new tets: [a,b,c,d] and [b,a,c,e] are // +// returned in [0] and [1] of 'fliptets'. As a result, the edge [e,d] is // +// replaced by the face [a,b,c]. // +// // +// If 'hullflag' > 0, hull tets may be involved in this flip, i.e., one of // +// the five vertices may be 'dummypoint'. There are two canonical cases: // +// (1) d is 'dummypoint', then [a,b,c,d] is hull tet. If e is 'dummypoint',// +// we reconfigure e to d, i.e., turnover it. // +// (2) c is 'dummypoint' then both [a,b,c,d] and [b,a,c,e] are hull tets. // +// If a or b is 'dummypoint', we reconfigure it to c, i.e., rotate the // +// three old tets counterclockwisely (right-hand rule) until a or b // +// is in c's position. // +// // +// If 'fc->enqflag' is set, convex hull faces will be queued for flipping. // +// In particular, if 'fc->enqflag' is 1, it is called by incrementalflip() // +// after the insertion of a new point. It is assumed that 'a' is the new // +// point. In this case, only link faces of 'a' are queued. // +// // +// If 'checksubfaceflag' is on (global variable), and assume [e,d] is not a // +// segment. There may be two (interior) subfaces sharing at [e,d], which are // +// [e,d,p] and [e,d,q], where the pair (p,q) may be either (a,b), or (b,c), // +// or (c,a) In such case, a 2-to-2 flip is performed on these two subfaces // +// and two new subfaces [p,q,e] and [p,q,d] are created. They are inserted // +// back into the tetrahedralization. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::flip32(triface* fliptets, int hullflag, flipconstraints *fc) +{ + triface topcastets[3], botcastets[3]; + triface newface, casface; + face flipshs[3]; + face checkseg; + point pa, pb, pc, pd, pe; + REAL attrib, volume; + int dummyflag = 0; // Rangle = {-1, 0, 1, 2} + int spivot = -1, scount = 0; // for flip22() + int t1ver; + int i, j; + + if (hullflag > 0) { + // Check if e is 'dummypoint'. + if (org(fliptets[0]) == dummypoint) { + // Reverse the edge. + for (i = 0; i < 3; i++) { + esymself(fliptets[i]); + } + // Swap the last two tets. + newface = fliptets[1]; + fliptets[1] = fliptets[2]; + fliptets[2] = newface; + dummyflag = -1; // e is dummypoint. + } else { + // Check if a or b is the 'dummypoint'. + if (apex(fliptets[0]) == dummypoint) { + dummyflag = 1; // a is dummypoint. + newface = fliptets[0]; + fliptets[0] = fliptets[1]; + fliptets[1] = fliptets[2]; + fliptets[2] = newface; + } else if (apex(fliptets[1]) == dummypoint) { + dummyflag = 2; // b is dummypoint. + newface = fliptets[0]; + fliptets[0] = fliptets[2]; + fliptets[2] = fliptets[1]; + fliptets[1] = newface; + } else { + dummyflag = 0; // either c or d may be dummypoint. + } + } + } + + pa = apex(fliptets[0]); + pb = apex(fliptets[1]); + pc = apex(fliptets[2]); + pd = dest(fliptets[0]); + pe = org(fliptets[0]); + + flip32count++; + + // Get the outer boundary faces. + for (i = 0; i < 3; i++) { + eorgoppo(fliptets[i], casface); + fsym(casface, topcastets[i]); + } + for (i = 0; i < 3; i++) { + edestoppo(fliptets[i], casface); + fsym(casface, botcastets[i]); + } + + if (checksubfaceflag) { + // Check if there are interior subfaces at the edge [e,d]. + for (i = 0; i < 3; i++) { + tspivot(fliptets[i], flipshs[i]); + if (flipshs[i].sh != NULL) { + // Found an interior subface. + stdissolve(flipshs[i]); // Disconnect the sub-tet bond. + scount++; + } else { + spivot = i; + } + } + } + + // Re-use fliptets[0] and fliptets[1]. + fliptets[0].ver = 11; + fliptets[1].ver = 11; + setelemmarker(fliptets[0].tet, 0); // Clear all flags. + setelemmarker(fliptets[1].tet, 0); + if (checksubsegflag) { + // Dealloc the space to subsegments. + if (fliptets[0].tet[8] != NULL) { + tet2segpool->dealloc((shellface *) fliptets[0].tet[8]); + fliptets[0].tet[8] = NULL; + } + if (fliptets[1].tet[8] != NULL) { + tet2segpool->dealloc((shellface *) fliptets[1].tet[8]); + fliptets[1].tet[8] = NULL; + } + } + if (checksubfaceflag) { + // Dealloc the space to subfaces. + if (fliptets[0].tet[9] != NULL) { + tet2subpool->dealloc((shellface *) fliptets[0].tet[9]); + fliptets[0].tet[9] = NULL; + } + if (fliptets[1].tet[9] != NULL) { + tet2subpool->dealloc((shellface *) fliptets[1].tet[9]); + fliptets[1].tet[9] = NULL; + } + } + if (checksubfaceflag) { + if (scount > 0) { + // The element attributes and volume constraint must be set correctly. + // There are two subfaces involved in this flip. The three tets are + // separated into two different regions, one may be exterior. The + // first region has two tets, and the second region has only one. + // The two created tets must be in the same region as the first region. + // The element attributes and volume constraint must be set correctly. + //assert(spivot != -1); + // The tet fliptets[spivot] is in the first region. + for (j = 0; j < 2; j++) { + for (i = 0; i < numelemattrib; i++) { + attrib = elemattribute(fliptets[spivot].tet, i); + setelemattribute(fliptets[j].tet, i, attrib); + } + if (b->varvolume) { + volume = volumebound(fliptets[spivot].tet); + setvolumebound(fliptets[j].tet, volume); + } + } + } + } + // Delete an old tet. + tetrahedrondealloc(fliptets[2].tet); + + if (hullflag > 0) { + // Check if c is dummypointc. + if (pc != dummypoint) { + // Check if d is dummypoint. + if (pd != dummypoint) { + // No hull tet is involved. + } else { + // We deleted three hull tets, and created one hull tet. + hullsize -= 2; + } + setvertices(fliptets[0], pa, pb, pc, pd); + setvertices(fliptets[1], pb, pa, pc, pe); + } else { + // c is dummypoint. The two new tets are hull tets. + setvertices(fliptets[0], pb, pa, pd, pc); + setvertices(fliptets[1], pa, pb, pe, pc); + // Adjust badc -> abcd. + esymself(fliptets[0]); + // Adjust abec -> bace. + esymself(fliptets[1]); + // The hullsize does not change. + } + } else { + setvertices(fliptets[0], pa, pb, pc, pd); + setvertices(fliptets[1], pb, pa, pc, pe); + } + + if (fc->remove_ndelaunay_edge) { // calc_tetprism_vol + REAL volneg[3], volpos[2], vol_diff; + if (pc != dummypoint) { + if (pd != dummypoint) { + volneg[0] = tetprismvol(pe, pd, pa, pb); + volneg[1] = tetprismvol(pe, pd, pb, pc); + volneg[2] = tetprismvol(pe, pd, pc, pa); + volpos[0] = tetprismvol(pa, pb, pc, pd); + volpos[1] = tetprismvol(pb, pa, pc, pe); + } else { // pd == dummypoint + volneg[0] = 0.; + volneg[1] = 0.; + volneg[2] = 0.; + volpos[0] = 0.; + volpos[1] = tetprismvol(pb, pa, pc, pe); + } + } else { // pc == dummypoint. + volneg[0] = tetprismvol(pe, pd, pa, pb); + volneg[1] = 0.; + volneg[2] = 0.; + volpos[0] = 0.; + volpos[1] = 0.; + } + vol_diff = volpos[0] + volpos[1] - volneg[0] - volneg[1] - volneg[2]; + fc->tetprism_vol_sum += vol_diff; // Update the total sum. + } + + // Bond abcd <==> bace. + bond(fliptets[0], fliptets[1]); + // Bond new faces to top outer boundary faces (at abcd). + for (i = 0; i < 3; i++) { + esym(fliptets[0], newface); + bond(newface, topcastets[i]); + enextself(fliptets[0]); + } + // Bond new faces to bottom outer boundary faces (at bace). + for (i = 0; i < 3; i++) { + esym(fliptets[1], newface); + bond(newface, botcastets[i]); + eprevself(fliptets[1]); + } + + if (checksubsegflag) { + // Bond 9 segments to new (flipped) tets. + for (i = 0; i < 3; i++) { // edges a->b, b->c, c->a. + if (issubseg(topcastets[i])) { + tsspivot1(topcastets[i], checkseg); + tssbond1(fliptets[0], checkseg); + sstbond1(checkseg, fliptets[0]); + tssbond1(fliptets[1], checkseg); + sstbond1(checkseg, fliptets[1]); + if (fc->chkencflag & 1) { + enqueuesubface(badsubsegs, &checkseg); + } + } + enextself(fliptets[0]); + eprevself(fliptets[1]); + } + // The three top edges. + for (i = 0; i < 3; i++) { // edges b->d, c->d, a->d. + esym(fliptets[0], newface); + eprevself(newface); + enext(topcastets[i], casface); + if (issubseg(casface)) { + tsspivot1(casface, checkseg); + tssbond1(newface, checkseg); + sstbond1(checkseg, newface); + if (fc->chkencflag & 1) { + enqueuesubface(badsubsegs, &checkseg); + } + } + enextself(fliptets[0]); + } + // The three bot edges. + for (i = 0; i < 3; i++) { // edges b<-e, c<-e, a<-e. + esym(fliptets[1], newface); + enextself(newface); + eprev(botcastets[i], casface); + if (issubseg(casface)) { + tsspivot1(casface, checkseg); + tssbond1(newface, checkseg); + sstbond1(checkseg, newface); + if (fc->chkencflag & 1) { + enqueuesubface(badsubsegs, &checkseg); + } + } + eprevself(fliptets[1]); + } + } // if (checksubsegflag) + + if (checksubfaceflag) { + face checksh; + // Bond the top three casing subfaces. + for (i = 0; i < 3; i++) { // At edges [b,a], [c,b], [a,c] + if (issubface(topcastets[i])) { + tspivot(topcastets[i], checksh); + esym(fliptets[0], newface); + sesymself(checksh); + tsbond(newface, checksh); + if (fc->chkencflag & 2) { + enqueuesubface(badsubfacs, &checksh); + } + } + enextself(fliptets[0]); + } + // Bond the bottom three casing subfaces. + for (i = 0; i < 3; i++) { // At edges [a,b], [b,c], [c,a] + if (issubface(botcastets[i])) { + tspivot(botcastets[i], checksh); + esym(fliptets[1], newface); + sesymself(checksh); + tsbond(newface, checksh); + if (fc->chkencflag & 2) { + enqueuesubface(badsubfacs, &checksh); + } + } + eprevself(fliptets[1]); + } + + if (scount > 0) { + face flipfaces[2]; + // Perform a 2-to-2 flip in subfaces. + flipfaces[0] = flipshs[(spivot + 1) % 3]; + flipfaces[1] = flipshs[(spivot + 2) % 3]; + sesymself(flipfaces[1]); + flip22(flipfaces, 0, fc->chkencflag); + // Connect the flipped subfaces to flipped tets. + // First go to the corresponding flipping edge. + // Re-use top- and botcastets[0]. + topcastets[0] = fliptets[0]; + botcastets[0] = fliptets[1]; + for (i = 0; i < ((spivot + 1) % 3); i++) { + enextself(topcastets[0]); + eprevself(botcastets[0]); + } + // Connect the top subface to the top tets. + esymself(topcastets[0]); + sesymself(flipfaces[0]); + // Check if there already exists a subface. + tspivot(topcastets[0], checksh); + if (checksh.sh == NULL) { + tsbond(topcastets[0], flipfaces[0]); + fsymself(topcastets[0]); + sesymself(flipfaces[0]); + tsbond(topcastets[0], flipfaces[0]); + } else { + // An invalid 2-to-2 flip. Report a bug. + terminatetetgen(this, 2); + } + // Connect the bot subface to the bottom tets. + esymself(botcastets[0]); + sesymself(flipfaces[1]); + // Check if there already exists a subface. + tspivot(botcastets[0], checksh); + if (checksh.sh == NULL) { + tsbond(botcastets[0], flipfaces[1]); + fsymself(botcastets[0]); + sesymself(flipfaces[1]); + tsbond(botcastets[0], flipfaces[1]); + } else { + // An invalid 2-to-2 flip. Report a bug. + terminatetetgen(this, 2); + } + } // if (scount > 0) + } // if (checksubfaceflag) + + if (fc->chkencflag & 4) { + // Put two new tets into check list. + for (i = 0; i < 2; i++) { + enqueuetetrahedron(&(fliptets[i])); + } + } + + setpoint2tet(pa, (tetrahedron) fliptets[0].tet); + setpoint2tet(pb, (tetrahedron) fliptets[0].tet); + setpoint2tet(pc, (tetrahedron) fliptets[0].tet); + setpoint2tet(pd, (tetrahedron) fliptets[0].tet); + setpoint2tet(pe, (tetrahedron) fliptets[1].tet); + + if (hullflag > 0) { + if (dummyflag != 0) { + // Restore the original position of the points (for flipnm()). + if (dummyflag == -1) { + // e were dummypoint. Swap the two new tets. + newface = fliptets[0]; + fliptets[0] = fliptets[1]; + fliptets[1] = newface; + } else { + // a or b was dummypoint. + if (dummyflag == 1) { + eprevself(fliptets[0]); + enextself(fliptets[1]); + } else { // dummyflag == 2 + enextself(fliptets[0]); + eprevself(fliptets[1]); + } + } + } + } + + if (fc->enqflag > 0) { + // Queue faces which may be locally non-Delaunay. + // pa = org(fliptets[0]); // 'a' may be a new vertex. + enextesym(fliptets[0], newface); + flippush(flipstack, &newface); + eprevesym(fliptets[1], newface); + flippush(flipstack, &newface); + if (fc->enqflag > 1) { + //pb = dest(fliptets[0]); + eprevesym(fliptets[0], newface); + flippush(flipstack, &newface); + enextesym(fliptets[1], newface); + flippush(flipstack, &newface); + //pc = apex(fliptets[0]); + esym(fliptets[0], newface); + flippush(flipstack, &newface); + esym(fliptets[1], newface); + flippush(flipstack, &newface); + } + } + + recenttet = fliptets[0]; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// flip41() Perform a 4-to-1 flip (Remove a vertex). // +// // +// 'fliptets' is an array of four tetrahedra in the star of the removing // +// vertex 'p'. Let the four vertices in the star of p be a, b, c, and d. The // +// four tets in 'fliptets' are: [p,d,a,b], [p,d,b,c], [p,d,c,a], and [a,b,c, // +// p]. On return, 'fliptets[0]' is the new tet [a,b,c,d]. // +// // +// If 'hullflag' is set (> 0), one of the five vertices may be 'dummypoint'. // +// The 'hullsize' may be changed. Note that p may be dummypoint. In this // +// case, four hull tets are replaced by one real tet. // +// // +// If 'checksubface' flag is set (>0), it is possible that there are three // +// interior subfaces connecting at p. If so, a 3-to-1 flip is performed to // +// to remove p from the surface triangulation. // +// // +// If it is called by the routine incrementalflip(), we assume that d is the // +// newly inserted vertex. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::flip41(triface* fliptets, int hullflag, flipconstraints *fc) +{ + triface topcastets[3], botcastet; + triface newface, neightet; + face flipshs[4]; + point pa, pb, pc, pd, pp; + int dummyflag = 0; // in {0, 1, 2, 3, 4} + int spivot = -1, scount = 0; + int t1ver; + int i; + + pa = org(fliptets[3]); + pb = dest(fliptets[3]); + pc = apex(fliptets[3]); + pd = dest(fliptets[0]); + pp = org(fliptets[0]); // The removing vertex. + + flip41count++; + + // Get the outer boundary faces. + for (i = 0; i < 3; i++) { + enext(fliptets[i], topcastets[i]); + fnextself(topcastets[i]); // [d,a,b,#], [d,b,c,#], [d,c,a,#] + enextself(topcastets[i]); // [a,b,d,#], [b,c,d,#], [c,a,d,#] + } + fsym(fliptets[3], botcastet); // [b,a,c,#] + + if (checksubfaceflag) { + // Check if there are three subfaces at 'p'. + // Re-use 'newface'. + for (i = 0; i < 3; i++) { + fnext(fliptets[3], newface); // [a,b,p,d],[b,c,p,d],[c,a,p,d]. + tspivot(newface, flipshs[i]); + if (flipshs[i].sh != NULL) { + spivot = i; // Remember this subface. + scount++; + } + enextself(fliptets[3]); + } + if (scount > 0) { + // There are three subfaces connecting at p. + if (scount < 3) { + // The new subface is one of {[a,b,d], [b,c,d], [c,a,d]}. + // Go to the tet containing the three subfaces. + fsym(topcastets[spivot], neightet); + // Get the three subfaces connecting at p. + for (i = 0; i < 3; i++) { + esym(neightet, newface); + tspivot(newface, flipshs[i]); + eprevself(neightet); + } + } else { + spivot = 3; // The new subface is [a,b,c]. + } + } + } // if (checksubfaceflag) + + + // Re-use fliptets[0] for [a,b,c,d]. + fliptets[0].ver = 11; + setelemmarker(fliptets[0].tet, 0); // Clean all flags. + // NOTE: the element attributes and volume constraint remain unchanged. + if (checksubsegflag) { + // Dealloc the space to subsegments. + if (fliptets[0].tet[8] != NULL) { + tet2segpool->dealloc((shellface *) fliptets[0].tet[8]); + fliptets[0].tet[8] = NULL; + } + } + if (checksubfaceflag) { + // Dealloc the space to subfaces. + if (fliptets[0].tet[9] != NULL) { + tet2subpool->dealloc((shellface *) fliptets[0].tet[9]); + fliptets[0].tet[9] = NULL; + } + } + // Delete the other three tets. + for (i = 1; i < 4; i++) { + tetrahedrondealloc(fliptets[i].tet); + } + + if (pp != dummypoint) { + // Mark the point pp as unused. + setpointtype(pp, UNUSEDVERTEX); + unuverts++; + } + + // Create the new tet [a,b,c,d]. + if (hullflag > 0) { + // One of the five vertices may be 'dummypoint'. + if (pa == dummypoint) { + // pa is dummypoint. + setvertices(fliptets[0], pc, pb, pd, pa); + esymself(fliptets[0]); // [b,c,a,d] + eprevself(fliptets[0]); // [a,b,c,d] + dummyflag = 1; + } else if (pb == dummypoint) { + setvertices(fliptets[0], pa, pc, pd, pb); + esymself(fliptets[0]); // [c,a,b,d] + enextself(fliptets[0]); // [a,b,c,d] + dummyflag = 2; + } else if (pc == dummypoint) { + setvertices(fliptets[0], pb, pa, pd, pc); + esymself(fliptets[0]); // [a,b,c,d] + dummyflag = 3; + } else if (pd == dummypoint) { + setvertices(fliptets[0], pa, pb, pc, pd); + dummyflag = 4; + } else { + setvertices(fliptets[0], pa, pb, pc, pd); + if (pp == dummypoint) { + dummyflag = -1; + } else { + dummyflag = 0; + } + } + if (dummyflag > 0) { + // We deleted 3 hull tets, and create 1 hull tet. + hullsize -= 2; + } else if (dummyflag < 0) { + // We deleted 4 hull tets. + hullsize -= 4; + // meshedges does not change. + } + } else { + setvertices(fliptets[0], pa, pb, pc, pd); + } + + if (fc->remove_ndelaunay_edge) { // calc_tetprism_vol + REAL volneg[4], volpos[1], vol_diff; + if (dummyflag > 0) { + if (pa == dummypoint) { + volneg[0] = 0.; + volneg[1] = tetprismvol(pp, pd, pb, pc); + volneg[2] = 0.; + volneg[3] = 0.; + } else if (pb == dummypoint) { + volneg[0] = 0.; + volneg[1] = 0.; + volneg[2] = tetprismvol(pp, pd, pc, pa); + volneg[3] = 0.; + } else if (pc == dummypoint) { + volneg[0] = tetprismvol(pp, pd, pa, pb); + volneg[1] = 0.; + volneg[2] = 0.; + volneg[3] = 0.; + } else { // pd == dummypoint + volneg[0] = 0.; + volneg[1] = 0.; + volneg[2] = 0.; + volneg[3] = tetprismvol(pa, pb, pc, pp); + } + volpos[0] = 0.; + } else if (dummyflag < 0) { + volneg[0] = 0.; + volneg[1] = 0.; + volneg[2] = 0.; + volneg[3] = 0.; + volpos[0] = tetprismvol(pa, pb, pc, pd); + } else { + volneg[0] = tetprismvol(pp, pd, pa, pb); + volneg[1] = tetprismvol(pp, pd, pb, pc); + volneg[2] = tetprismvol(pp, pd, pc, pa); + volneg[3] = tetprismvol(pa, pb, pc, pp); + volpos[0] = tetprismvol(pa, pb, pc, pd); + } + vol_diff = volpos[0] - volneg[0] - volneg[1] - volneg[2] - volneg[3]; + fc->tetprism_vol_sum += vol_diff; // Update the total sum. + } + + // Bond the new tet to adjacent tets. + for (i = 0; i < 3; i++) { + esym(fliptets[0], newface); // At faces [b,a,d], [c,b,d], [a,c,d]. + bond(newface, topcastets[i]); + enextself(fliptets[0]); + } + bond(fliptets[0], botcastet); + + if (checksubsegflag) { + face checkseg; + // Bond 6 segments (at edges of [a,b,c,d]) if there there are. + for (i = 0; i < 3; i++) { + eprev(topcastets[i], newface); // At edges [d,a],[d,b],[d,c]. + if (issubseg(newface)) { + tsspivot1(newface, checkseg); + esym(fliptets[0], newface); + enextself(newface); // At edges [a,d], [b,d], [c,d]. + tssbond1(newface, checkseg); + sstbond1(checkseg, newface); + if (fc->chkencflag & 1) { + enqueuesubface(badsubsegs, &checkseg); + } + } + enextself(fliptets[0]); + } + for (i = 0; i < 3; i++) { + if (issubseg(topcastets[i])) { + tsspivot1(topcastets[i], checkseg); // At edges [a,b],[b,c],[c,a]. + tssbond1(fliptets[0], checkseg); + sstbond1(checkseg, fliptets[0]); + if (fc->chkencflag & 1) { + enqueuesubface(badsubsegs, &checkseg); + } + } + enextself(fliptets[0]); + } + } + + if (checksubfaceflag) { + face checksh; + // Bond 4 subfaces (at faces of [a,b,c,d]) if there are. + for (i = 0; i < 3; i++) { + if (issubface(topcastets[i])) { + tspivot(topcastets[i], checksh); // At faces [a,b,d],[b,c,d],[c,a,d] + esym(fliptets[0], newface); // At faces [b,a,d],[c,b,d],[a,c,d] + sesymself(checksh); + tsbond(newface, checksh); + if (fc->chkencflag & 2) { + enqueuesubface(badsubfacs, &checksh); + } + } + enextself(fliptets[0]); + } + if (issubface(botcastet)) { + tspivot(botcastet, checksh); // At face [b,a,c] + sesymself(checksh); + tsbond(fliptets[0], checksh); + if (fc->chkencflag & 2) { + enqueuesubface(badsubfacs, &checksh); + } + } + + if (spivot >= 0) { + // Perform a 3-to-1 flip in surface triangulation. + // Depending on the value of 'spivot', the three subfaces are: + // - 0: [a,b,p], [b,d,p], [d,a,p] + // - 1: [b,c,p], [c,d,p], [d,b,p] + // - 2: [c,a,p], [a,d,p], [d,c,p] + // - 3: [a,b,p], [b,c,p], [c,a,p] + // Adjust the three subfaces such that their origins are p, i.e., + // - 3: [p,a,b], [p,b,c], [p,c,a]. (Required by the flip31()). + for (i = 0; i < 3; i++) { + senext2self(flipshs[i]); + } + flip31(flipshs, 0); + // Delete the three old subfaces. + for (i = 0; i < 3; i++) { + shellfacedealloc(subfaces, flipshs[i].sh); + } + if (spivot < 3) { + // // Bond the new subface to the new tet [a,b,c,d]. + tsbond(topcastets[spivot], flipshs[3]); + fsym(topcastets[spivot], newface); + sesym(flipshs[3], checksh); + tsbond(newface, checksh); + } else { + // Bound the new subface [a,b,c] to the new tet [a,b,c,d]. + tsbond(fliptets[0], flipshs[3]); + fsym(fliptets[0], newface); + sesym(flipshs[3], checksh); + tsbond(newface, checksh); + } + } // if (spivot > 0) + } // if (checksubfaceflag) + + if (fc->chkencflag & 4) { + enqueuetetrahedron(&(fliptets[0])); + } + + // Update the point-to-tet map. + setpoint2tet(pa, (tetrahedron) fliptets[0].tet); + setpoint2tet(pb, (tetrahedron) fliptets[0].tet); + setpoint2tet(pc, (tetrahedron) fliptets[0].tet); + setpoint2tet(pd, (tetrahedron) fliptets[0].tet); + + if (fc->enqflag > 0) { + // Queue faces which may be locally non-Delaunay. + flippush(flipstack, &(fliptets[0])); // [a,b,c] (opposite to new point). + if (fc->enqflag > 1) { + for (i = 0; i < 3; i++) { + esym(fliptets[0], newface); + flippush(flipstack, &newface); + enextself(fliptets[0]); + } + } + } + + recenttet = fliptets[0]; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// flipnm() Flip an edge through a sequence of elementary flips. // +// // +// 'abtets' is an array of 'n' tets in the star of edge [a,b].These tets are // +// ordered in a counterclockwise cycle with respect to the vector a->b, i.e.,// +// use the right-hand rule. // +// // +// 'level' (>= 0) indicates the current link level. If 'level > 0', we are // +// flipping a link edge of an edge [a',b'], and 'abedgepivot' indicates // +// which link edge, i.e., [c',b'] or [a',c'], is [a,b] These two parameters // +// allow us to determine the new tets after a 3-to-2 flip, i.e., tets that // +// do not inside the reduced star of edge [a',b']. // +// // +// If the flag 'fc->unflip' is set, this routine un-does the flips performed // +// in flipnm([a,b]) so that the mesh is returned to its original state // +// before doing the flipnm([a,b]) operation. // +// // +// The return value is an integer nn, where nn <= n. If nn is 2, then the // +// edge is flipped. The first and the second tets in 'abtets' are new tets. // +// Otherwise, nn > 2, the edge is not flipped, and nn is the number of tets // +// in the current star of [a,b]. // +// // +// ASSUMPTIONS: // +// - Neither a nor b is 'dummypoint'. // +// - [a,b] must not be a segment. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::flipnm(triface* abtets, int n, int level, int abedgepivot, + flipconstraints* fc) +{ + triface fliptets[3], spintet, flipedge; + triface *tmpabtets, *parytet; + point pa, pb, pc, pd, pe, pf; + REAL ori; + int hullflag, hulledgeflag; + int reducflag, rejflag; + int reflexlinkedgecount; + int edgepivot; + int n1, nn; + int t1ver; + int i, j; + + pa = org(abtets[0]); + pb = dest(abtets[0]); + + if (n > 3) { + // Try to reduce the size of the Star(ab) by flipping a face in it. + reflexlinkedgecount = 0; + + for (i = 0; i < n; i++) { + // Let the face of 'abtets[i]' be [a,b,c]. + if (checksubfaceflag) { + if (issubface(abtets[i])) { + continue; // Skip a subface. + } + } + // Do not flip this face if it is involved in two Stars. + if ((elemcounter(abtets[i]) > 1) || + (elemcounter(abtets[(i - 1 + n) % n]) > 1)) { + continue; + } + + pc = apex(abtets[i]); + pd = apex(abtets[(i + 1) % n]); + pe = apex(abtets[(i - 1 + n) % n]); + if ((pd == dummypoint) || (pe == dummypoint)) { + continue; // [a,b,c] is a hull face. + } + + + // Decide whether [a,b,c] is flippable or not. + reducflag = 0; + + hullflag = (pc == dummypoint); // pc may be dummypoint. + hulledgeflag = 0; + if (hullflag == 0) { + ori = orient3d(pb, pc, pd, pe); // Is [b,c] locally convex? + if (ori > 0) { + ori = orient3d(pc, pa, pd, pe); // Is [c,a] locally convex? + if (ori > 0) { + // Test if [a,b] is locally convex OR flat. + ori = orient3d(pa, pb, pd, pe); + if (ori > 0) { + // Found a 2-to-3 flip: [a,b,c] => [e,d] + reducflag = 1; + } else if (ori == 0) { + // [a,b] is flat. + if (n == 4) { + // The "flat" tet can be removed immediately by a 3-to-2 flip. + reducflag = 1; + // Check if [e,d] is a hull edge. + pf = apex(abtets[(i + 2) % n]); + hulledgeflag = (pf == dummypoint); + } + } + } + } + if (!reducflag) { + reflexlinkedgecount++; + } + } else { + // 'c' is dummypoint. + if (n == 4) { + // Let the vertex opposite to 'c' is 'f'. + // A 4-to-4 flip is possible if the two tets [d,e,f,a] and [e,d,f,b] + // are valid tets. + // Note: When the mesh is not convex, it is possible that [a,b] is + // locally non-convex (at hull faces [a,b,e] and [b,a,d]). + // In this case, an edge flip [a,b] to [e,d] is still possible. + pf = apex(abtets[(i + 2) % n]); + ori = orient3d(pd, pe, pf, pa); + if (ori < 0) { + ori = orient3d(pe, pd, pf, pb); + if (ori < 0) { + // Found a 4-to-4 flip: [a,b] => [e,d] + reducflag = 1; + ori = 0; // Signal as a 4-to-4 flip (like a co-planar case). + hulledgeflag = 1; // [e,d] is a hull edge. + } + } + } + } // if (hullflag) + + if (reducflag) { + if (nonconvex && hulledgeflag) { + // We will create a hull edge [e,d]. Make sure it does not exist. + if (getedge(pe, pd, &spintet)) { + // The 2-to-3 flip is not a topological valid flip. + reducflag = 0; + } + } + } + + if (reducflag) { + // [a,b,c] could be removed by a 2-to-3 flip. + rejflag = 0; + if (fc->checkflipeligibility) { + // Check if the flip can be performed. + rejflag = checkflipeligibility(1, pa, pb, pc, pd, pe, level, + abedgepivot, fc); + } + if (!rejflag) { + // Do flip: [a,b,c] => [e,d]. + fliptets[0] = abtets[i]; + fsym(fliptets[0], fliptets[1]); // abtets[i-1]. + flip23(fliptets, hullflag, fc); + + // Shrink the array 'abtets', maintain the original order. + // Two tets 'abtets[i-1] ([a,b,e,c])' and 'abtets[i] ([a,b,c,d])' + // are flipped, i.e., they do not in Star(ab) anymore. + // 'fliptets[0]' ([e,d,a,b]) is in Star(ab), it is saved in + // 'abtets[i-1]' (adjust it to be [a,b,e,d]), see below: + // + // before after + // [0] |___________| [0] |___________| + // ... |___________| ... |___________| + // [i-1] |_[a,b,e,c]_| [i-1] |_[a,b,e,d]_| + // [i] |_[a,b,c,d]_| --> [i] |_[a,b,d,#]_| + // [i+1] |_[a,b,d,#]_| [i+1] |_[a,b,#,*]_| + // ... |___________| ... |___________| + // [n-2] |___________| [n-2] |___________| + // [n-1] |___________| [n-1] |_[i]_2-t-3_| + // + edestoppoself(fliptets[0]); // [a,b,e,d] + // Increase the counter of this new tet (it is in Star(ab)). + increaseelemcounter(fliptets[0]); + abtets[(i - 1 + n) % n] = fliptets[0]; + for (j = i; j < n - 1; j++) { + abtets[j] = abtets[j + 1]; // Upshift + } + // The last entry 'abtets[n-1]' is empty. It is used in two ways: + // (i) it remembers the vertex 'c' (in 'abtets[n-1].tet'), and + // (ii) it remembers the position [i] where this flip took place. + // These information let us to either undo this flip or recover + // the original edge link (for collecting new created tets). + abtets[n - 1].tet = (tetrahedron *) pc; + abtets[n - 1].ver = 0; // Clear it. + // 'abtets[n - 1].ver' is in range [0,11] -- only uses 4 bits. + // Use the 5th bit in 'abtets[n - 1].ver' to signal a 2-to-3 flip. + abtets[n - 1].ver |= (1 << 4); + // The poisition [i] of this flip is saved above the 7th bit. + abtets[n - 1].ver |= (i << 6); + + if (fc->collectnewtets) { + // Push the two new tets [e,d,b,c] and [e,d,c,a] into a stack. + // Re-use the global array 'cavetetlist'. + for (j = 1; j < 3; j++) { + cavetetlist->newindex((void **) &parytet); + *parytet = fliptets[j]; // fliptets[1], fliptets[2]. + } + } + + // Star(ab) is reduced. Try to flip the edge [a,b]. + nn = flipnm(abtets, n - 1, level, abedgepivot, fc); + + if (nn == 2) { + // The edge has been flipped. + return nn; + } else { // if (nn > 2) + // The edge is not flipped. + if (fc->unflip || (ori == 0)) { + // Undo the previous 2-to-3 flip, i.e., do a 3-to-2 flip to + // transform [e,d] => [a,b,c]. + // 'ori == 0' means that the previous flip created a degenerated + // tet. It must be removed. + // Remember that 'abtets[i-1]' is [a,b,e,d]. We can use it to + // find another two tets [e,d,b,c] and [e,d,c,a]. + fliptets[0] = abtets[(i-1 + (n-1)) % (n-1)]; // [a,b,e,d] + edestoppoself(fliptets[0]); // [e,d,a,b] + fnext(fliptets[0], fliptets[1]); // [1] is [e,d,b,c] + fnext(fliptets[1], fliptets[2]); // [2] is [e,d,c,a] + // Restore the two original tets in Star(ab). + flip32(fliptets, hullflag, fc); + // Marktest the two restored tets in Star(ab). + for (j = 0; j < 2; j++) { + increaseelemcounter(fliptets[j]); + } + // Expand the array 'abtets', maintain the original order. + for (j = n - 2; j>= i; j--) { + abtets[j + 1] = abtets[j]; // Downshift + } + // Insert the two new tets 'fliptets[0]' [a,b,c,d] and + // 'fliptets[1]' [b,a,c,e] into the (i-1)-th and i-th entries, + // respectively. + esym(fliptets[1], abtets[(i - 1 + n) % n]); // [a,b,e,c] + abtets[i] = fliptets[0]; // [a,b,c,d] + nn++; + if (fc->collectnewtets) { + // Pop two (flipped) tets from the stack. + cavetetlist->objects -= 2; + } + } // if (unflip || (ori == 0)) + } // if (nn > 2) + + if (!fc->unflip) { + // The flips are not reversed. The current Star(ab) can not be + // further reduced. Return its current size (# of tets). + return nn; + } + // unflip is set. + // Continue the search for flips. + } + } // if (reducflag) + } // i + + // The Star(ab) is not reduced. + if (reflexlinkedgecount > 0) { + // There are reflex edges in the Link(ab). + if (((b->fliplinklevel < 0) && (level < autofliplinklevel)) || + ((b->fliplinklevel >= 0) && (level < b->fliplinklevel))) { + // Try to reduce the Star(ab) by flipping a reflex edge in Link(ab). + for (i = 0; i < n; i++) { + // Do not flip this face [a,b,c] if there are two Stars involved. + if ((elemcounter(abtets[i]) > 1) || + (elemcounter(abtets[(i - 1 + n) % n]) > 1)) { + continue; + } + pc = apex(abtets[i]); + if (pc == dummypoint) { + continue; // [a,b] is a hull edge. + } + pd = apex(abtets[(i + 1) % n]); + pe = apex(abtets[(i - 1 + n) % n]); + if ((pd == dummypoint) || (pe == dummypoint)) { + continue; // [a,b,c] is a hull face. + } + + + edgepivot = 0; // No edge is selected yet. + + // Test if [b,c] is locally convex or flat. + ori = orient3d(pb, pc, pd, pe); + if (ori <= 0) { + // Select the edge [c,b]. + enext(abtets[i], flipedge); // [b,c,a,d] + edgepivot = 1; + } + if (!edgepivot) { + // Test if [c,a] is locally convex or flat. + ori = orient3d(pc, pa, pd, pe); + if (ori <= 0) { + // Select the edge [a,c]. + eprev(abtets[i], flipedge); // [c,a,b,d]. + edgepivot = 2; + } + } + + if (!edgepivot) continue; + + // An edge is selected. + if (checksubsegflag) { + // Do not flip it if it is a segment. + if (issubseg(flipedge)) { + if (fc->collectencsegflag) { + face checkseg, *paryseg; + tsspivot1(flipedge, checkseg); + if (!sinfected(checkseg)) { + // Queue this segment in list. + sinfect(checkseg); + caveencseglist->newindex((void **) &paryseg); + *paryseg = checkseg; + } + } + continue; + } + } + + // Try to flip the selected edge ([c,b] or [a,c]). + esymself(flipedge); + // Count the number of tets at the edge. + n1 = 0; + j = 0; // Sum of the star counters. + spintet = flipedge; + while (1) { + n1++; + j += (elemcounter(spintet)); + fnextself(spintet); + if (spintet.tet == flipedge.tet) break; + } + if (n1 < 3) { + // This is only possible when the mesh contains inverted + // elements. Reprot a bug. + terminatetetgen(this, 2); + } + if (j > 2) { + // The Star(flipedge) overlaps other Stars. + continue; // Do not flip this edge. + } + + if ((b->flipstarsize > 0) && (n1 > b->flipstarsize)) { + // The star size exceeds the given limit. + continue; // Do not flip it. + } + + // Allocate spaces for Star(flipedge). + tmpabtets = new triface[n1]; + // Form the Star(flipedge). + j = 0; + spintet = flipedge; + while (1) { + tmpabtets[j] = spintet; + // Increase the star counter of this tet. + increaseelemcounter(tmpabtets[j]); + j++; + fnextself(spintet); + if (spintet.tet == flipedge.tet) break; + } + + // Try to flip the selected edge away. + nn = flipnm(tmpabtets, n1, level + 1, edgepivot, fc); + + if (nn == 2) { + // The edge is flipped. Star(ab) is reduced. + // Shrink the array 'abtets', maintain the original order. + if (edgepivot == 1) { + // 'tmpabtets[0]' is [d,a,e,b] => contains [a,b]. + spintet = tmpabtets[0]; // [d,a,e,b] + enextself(spintet); + esymself(spintet); + enextself(spintet); // [a,b,e,d] + } else { + // 'tmpabtets[1]' is [b,d,e,a] => contains [a,b]. + spintet = tmpabtets[1]; // [b,d,e,a] + eprevself(spintet); + esymself(spintet); + eprevself(spintet); // [a,b,e,d] + } // edgepivot == 2 + increaseelemcounter(spintet); // It is in Star(ab). + // Put the new tet at [i-1]-th entry. + abtets[(i - 1 + n) % n] = spintet; + for (j = i; j < n - 1; j++) { + abtets[j] = abtets[j + 1]; // Upshift + } + // Remember the flips in the last entry of the array 'abtets'. + // They can be used to recover the flipped edge. + abtets[n - 1].tet = (tetrahedron *) tmpabtets; // The star(fedge). + abtets[n - 1].ver = 0; // Clear it. + // Use the 1st and 2nd bit to save 'edgepivot' (1 or 2). + abtets[n - 1].ver |= edgepivot; + // Use the 6th bit to signal this n1-to-m1 flip. + abtets[n - 1].ver |= (1 << 5); + // The poisition [i] of this flip is saved from 7th to 19th bit. + abtets[n - 1].ver |= (i << 6); + // The size of the star 'n1' is saved from 20th bit. + abtets[n - 1].ver |= (n1 << 19); + + // Remember the flipped link vertex 'c'. It can be used to recover + // the original edge link of [a,b], and to collect new tets. + tmpabtets[0].tet = (tetrahedron *) pc; + tmpabtets[0].ver = (1 << 5); // Flag it as a vertex handle. + + // Continue to flip the edge [a,b]. + nn = flipnm(abtets, n - 1, level, abedgepivot, fc); + + if (nn == 2) { + // The edge has been flipped. + return nn; + } else { // if (nn > 2) { + // The edge is not flipped. + if (fc->unflip) { + // Recover the flipped edge ([c,b] or [a,c]). + // The sequence of flips are saved in 'tmpabtets'. + // abtets[(i-1) % (n-1)] is [a,b,e,d], i.e., the tet created by + // the flipping of edge [c,b] or [a,c].It must still exist in + // Star(ab). It is the start tet to recover the flipped edge. + if (edgepivot == 1) { + // The flip edge is [c,b]. + tmpabtets[0] = abtets[((i-1)+(n-1))%(n-1)]; // [a,b,e,d] + eprevself(tmpabtets[0]); + esymself(tmpabtets[0]); + eprevself(tmpabtets[0]); // [d,a,e,b] + fsym(tmpabtets[0], tmpabtets[1]); // [a,d,e,c] + } else { + // The flip edge is [a,c]. + tmpabtets[1] = abtets[((i-1)+(n-1))%(n-1)]; // [a,b,e,d] + enextself(tmpabtets[1]); + esymself(tmpabtets[1]); + enextself(tmpabtets[1]); // [b,d,e,a] + fsym(tmpabtets[1], tmpabtets[0]); // [d,b,e,c] + } // if (edgepivot == 2) + + // Recover the flipped edge ([c,b] or [a,c]). + flipnm_post(tmpabtets, n1, 2, edgepivot, fc); + + // Insert the two recovered tets into Star(ab). + for (j = n - 2; j >= i; j--) { + abtets[j + 1] = abtets[j]; // Downshift + } + if (edgepivot == 1) { + // tmpabtets[0] is [c,b,d,a] ==> contains [a,b] + // tmpabtets[1] is [c,b,a,e] ==> contains [a,b] + // tmpabtets[2] is [c,b,e,d] + fliptets[0] = tmpabtets[1]; + enextself(fliptets[0]); + esymself(fliptets[0]); // [a,b,e,c] + fliptets[1] = tmpabtets[0]; + esymself(fliptets[1]); + eprevself(fliptets[1]); // [a,b,c,d] + } else { + // tmpabtets[0] is [a,c,d,b] ==> contains [a,b] + // tmpabtets[1] is [a,c,b,e] ==> contains [a,b] + // tmpabtets[2] is [a,c,e,d] + fliptets[0] = tmpabtets[1]; + eprevself(fliptets[0]); + esymself(fliptets[0]); // [a,b,e,c] + fliptets[1] = tmpabtets[0]; + esymself(fliptets[1]); + enextself(fliptets[1]); // [a,b,c,d] + } // edgepivot == 2 + for (j = 0; j < 2; j++) { + increaseelemcounter(fliptets[j]); + } + // Insert the two recovered tets into Star(ab). + abtets[(i - 1 + n) % n] = fliptets[0]; + abtets[i] = fliptets[1]; + nn++; + // Release the allocated spaces. + delete [] tmpabtets; + } // if (unflip) + } // if (nn > 2) + + if (!fc->unflip) { + // The flips are not reversed. The current Star(ab) can not be + // further reduced. Return its size (# of tets). + return nn; + } + // unflip is set. + // Continue the search for flips. + } else { + // The selected edge is not flipped. + if (!fc->unflip) { + // Release the memory used in this attempted flip. + flipnm_post(tmpabtets, n1, nn, edgepivot, fc); + } + // Decrease the star counters of tets in Star(flipedge). + for (j = 0; j < nn; j++) { + decreaseelemcounter(tmpabtets[j]); + } + // Release the allocated spaces. + delete [] tmpabtets; + } + } // i + } // if (level...) + } // if (reflexlinkedgecount > 0) + } else { + // Check if a 3-to-2 flip is possible. + // Let the three apexes be c, d,and e. Hull tets may be involved. If so, + // we rearrange them such that the vertex e is dummypoint. + hullflag = 0; + + if (apex(abtets[0]) == dummypoint) { + pc = apex(abtets[1]); + pd = apex(abtets[2]); + pe = apex(abtets[0]); + hullflag = 1; + } else if (apex(abtets[1]) == dummypoint) { + pc = apex(abtets[2]); + pd = apex(abtets[0]); + pe = apex(abtets[1]); + hullflag = 2; + } else { + pc = apex(abtets[0]); + pd = apex(abtets[1]); + pe = apex(abtets[2]); + hullflag = (pe == dummypoint) ? 3 : 0; + } + + reducflag = 0; + rejflag = 0; + + + if (hullflag == 0) { + // Make sure that no inverted tet will be created, i.e. the new tets + // [d,c,e,a] and [c,d,e,b] must be valid tets. + ori = orient3d(pd, pc, pe, pa); + if (ori < 0) { + ori = orient3d(pc, pd, pe, pb); + if (ori < 0) { + reducflag = 1; + } + } + } else { + // [a,b] is a hull edge. + // Note: This can happen when it is in the middle of a 4-to-4 flip. + // Note: [a,b] may even be a non-convex hull edge. + if (!nonconvex) { + // The mesh is convex, only do flip if it is a coplanar hull edge. + ori = orient3d(pa, pb, pc, pd); + if (ori == 0) { + reducflag = 1; + } + } else { // nonconvex + reducflag = 1; + } + if (reducflag == 1) { + // [a,b], [a,b,c] and [a,b,d] are on the convex hull. + // Make sure that no inverted tet will be created. + point searchpt = NULL, chkpt; + REAL bigvol = 0.0, ori1, ori2; + // Search an interior vertex which is an apex of edge [c,d]. + // In principle, it can be arbitrary interior vertex. To avoid + // numerical issue, we choose the vertex which belongs to a tet + // 't' at edge [c,d] and 't' has the biggest volume. + fliptets[0] = abtets[hullflag % 3]; // [a,b,c,d]. + eorgoppoself(fliptets[0]); // [d,c,b,a] + spintet = fliptets[0]; + while (1) { + fnextself(spintet); + chkpt = oppo(spintet); + if (chkpt == pb) break; + if ((chkpt != dummypoint) && (apex(spintet) != dummypoint)) { + ori = -orient3d(pd, pc, apex(spintet), chkpt); + if (ori > bigvol) { + bigvol = ori; + searchpt = chkpt; + } + } + } + if (searchpt != NULL) { + // Now valid the configuration. + ori1 = orient3d(pd, pc, searchpt, pa); + ori2 = orient3d(pd, pc, searchpt, pb); + if (ori1 * ori2 >= 0.0) { + reducflag = 0; // Not valid. + } else { + ori1 = orient3d(pa, pb, searchpt, pc); + ori2 = orient3d(pa, pb, searchpt, pd); + if (ori1 * ori2 >= 0.0) { + reducflag = 0; // Not valid. + } + } + } else { + // No valid searchpt is found. + reducflag = 0; // Do not flip it. + } + } // if (reducflag == 1) + } // if (hullflag == 1) + + if (reducflag) { + // A 3-to-2 flip is possible. + if (checksubfaceflag) { + // This edge (must not be a segment) can be flipped ONLY IF it belongs + // to either 0 or 2 subfaces. In the latter case, a 2-to-2 flip in + // the surface mesh will be automatically performed within the + // 3-to-2 flip. + nn = 0; + edgepivot = -1; // Re-use it. + for (j = 0; j < 3; j++) { + if (issubface(abtets[j])) { + nn++; // Found a subface. + } else { + edgepivot = j; + } + } + if (nn == 1) { + // Found only 1 subface containing this edge. This can happen in + // the boundary recovery phase. The neighbor subface is not yet + // recovered. This edge should not be flipped at this moment. + rejflag = 1; + } else if (nn == 2) { + // Found two subfaces. A 2-to-2 flip is possible. Validate it. + // Below we check if the two faces [p,q,a] and [p,q,b] are subfaces. + eorgoppo(abtets[(edgepivot + 1) % 3], spintet); // [q,p,b,a] + if (issubface(spintet)) { + rejflag = 1; // Conflict to a 2-to-2 flip. + } else { + esymself(spintet); + if (issubface(spintet)) { + rejflag = 1; // Conflict to a 2-to-2 flip. + } + } + } else if (nn == 3) { + // Report a bug. + terminatetetgen(this, 2); + } + } + if (!rejflag && fc->checkflipeligibility) { + // Here we must exchange 'a' and 'b'. Since in the check... function, + // we assume the following point sequence, 'a,b,c,d,e', where + // the face [a,b,c] will be flipped and the edge [e,d] will be + // created. The two new tets are [a,b,c,d] and [b,a,c,e]. + rejflag = checkflipeligibility(2, pc, pd, pe, pb, pa, level, + abedgepivot, fc); + } + if (!rejflag) { + // Do flip: [a,b] => [c,d,e] + flip32(abtets, hullflag, fc); + if (fc->remove_ndelaunay_edge) { + if (level == 0) { + // It is the desired removing edge. Check if we have improved + // the objective function. + if ((fc->tetprism_vol_sum >= 0.0) || + (fabs(fc->tetprism_vol_sum) < fc->bak_tetprism_vol)) { + // No improvement! flip back: [c,d,e] => [a,b]. + flip23(abtets, hullflag, fc); + // Increase the element counter -- They are in cavity. + for (j = 0; j < 3; j++) { + increaseelemcounter(abtets[j]); + } + return 3; + } + } // if (level == 0) + } + if (fc->collectnewtets) { + // Collect new tets. + if (level == 0) { + // Push the two new tets into stack. + for (j = 0; j < 2; j++) { + cavetetlist->newindex((void **) &parytet); + *parytet = abtets[j]; + } + } else { + // Only one of the new tets is collected. The other one is inside + // the reduced edge star. 'abedgepivot' is either '1' or '2'. + cavetetlist->newindex((void **) &parytet); + if (abedgepivot == 1) { // [c,b] + *parytet = abtets[1]; + } else { + *parytet = abtets[0]; + } + } + } // if (fc->collectnewtets) + return 2; + } + } // if (reducflag) + } // if (n == 3) + + // The current (reduced) Star size. + return n; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// flipnm_post() Post process a n-to-m flip. // +// // +// IMPORTANT: This routine only works when there is no other flip operation // +// is done after flipnm([a,b]) which attempts to remove an edge [a,b]. // +// // +// 'abtets' is an array of 'n' (>= 3) tets which are in the original star of // +// [a,b] before flipnm([a,b]). 'nn' (< n) is the value returned by flipnm. // +// If 'nn == 2', the edge [a,b] has been flipped. 'abtets[0]' and 'abtets[1]'// +// are [c,d,e,b] and [d,c,e,a], i.e., a 2-to-3 flip can recover the edge [a, // +// b] and its initial Star([a,b]). If 'nn >= 3' edge [a,b] still exists in // +// current mesh and 'nn' is the current number of tets in Star([a,b]). // +// // +// Each 'abtets[i]', where nn <= i < n, saves either a 2-to-3 flip or a // +// flipnm([p1,p2]) operation ([p1,p2] != [a,b]) which created the tet // +// 'abtets[t-1]', where '0 <= t <= i'. These information can be used to // +// undo the flips performed in flipnm([a,b]) or to collect new tets created // +// by the flipnm([a,b]) operation. // +// // +// Default, this routine only walks through the flips and frees the spaces // +// allocated during the flipnm([a,b]) operation. // +// // +// If the flag 'fc->unflip' is set, this routine un-does the flips performed // +// in flipnm([a,b]) so that the mesh is returned to its original state // +// before doing the flipnm([a,b]) operation. // +// // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::flipnm_post(triface* abtets, int n, int nn, int abedgepivot, + flipconstraints* fc) +{ + triface fliptets[3], flipface; + triface *tmpabtets; + int fliptype; + int edgepivot; + int t, n1; + int i, j; + + + if (nn == 2) { + // The edge [a,b] has been flipped. + // 'abtets[0]' is [c,d,e,b] or [#,#,#,b]. + // 'abtets[1]' is [d,c,e,a] or [#,#,#,a]. + if (fc->unflip) { + // Do a 2-to-3 flip to recover the edge [a,b]. There may be hull tets. + flip23(abtets, 1, fc); + if (fc->collectnewtets) { + // Pop up new (flipped) tets from the stack. + if (abedgepivot == 0) { + // Two new tets were collected. + cavetetlist->objects -= 2; + } else { + // Only one of the two new tets was collected. + cavetetlist->objects -= 1; + } + } + } + // The initial size of Star(ab) is 3. + nn++; + } + + // Walk through the performed flips. + for (i = nn; i < n; i++) { + // At the beginning of each step 'i', the size of the Star([a,b]) is 'i'. + // At the end of this step, the size of the Star([a,b]) is 'i+1'. + // The sizes of the Link([a,b]) are the same. + fliptype = ((abtets[i].ver >> 4) & 3); // 0, 1, or 2. + if (fliptype == 1) { + // It was a 2-to-3 flip: [a,b,c]->[e,d]. + t = (abtets[i].ver >> 6); + if (fc->unflip) { + if (b->verbose > 2) { + printf(" Recover a 2-to-3 flip at f[%d].\n", t); + } + // 'abtets[(t-1)%i]' is the tet [a,b,e,d] in current Star(ab), i.e., + // it is created by a 2-to-3 flip [a,b,c] => [e,d]. + fliptets[0] = abtets[((t - 1) + i) % i]; // [a,b,e,d] + eprevself(fliptets[0]); + esymself(fliptets[0]); + enextself(fliptets[0]); // [e,d,a,b] + fnext(fliptets[0], fliptets[1]); // [e,d,b,c] + fnext(fliptets[1], fliptets[2]); // [e,d,c,a] + // Do a 3-to-2 flip: [e,d] => [a,b,c]. + // NOTE: hull tets may be invloved. + flip32(fliptets, 1, fc); + // Expand the array 'abtets', maintain the original order. + // The new array length is (i+1). + for (j = i - 1; j >= t; j--) { + abtets[j + 1] = abtets[j]; // Downshift + } + // The tet abtets[(t-1)%i] is deleted. Insert the two new tets + // 'fliptets[0]' [a,b,c,d] and 'fliptets[1]' [b,a,c,e] into + // the (t-1)-th and t-th entries, respectively. + esym(fliptets[1], abtets[((t-1) + (i+1)) % (i+1)]); // [a,b,e,c] + abtets[t] = fliptets[0]; // [a,b,c,d] + if (fc->collectnewtets) { + // Pop up two (flipped) tets from the stack. + cavetetlist->objects -= 2; + } + } + } else if (fliptype == 2) { + tmpabtets = (triface *) (abtets[i].tet); + n1 = ((abtets[i].ver >> 19) & 8191); // \sum_{i=0^12}{2^i} = 8191 + edgepivot = (abtets[i].ver & 3); + t = ((abtets[i].ver >> 6) & 8191); + if (fc->unflip) { + if (b->verbose > 2) { + printf(" Recover a %d-to-m flip at e[%d] of f[%d].\n", n1, + edgepivot, t); + } + // Recover the flipped edge ([c,b] or [a,c]). + // abtets[(t - 1 + i) % i] is [a,b,e,d], i.e., the tet created by + // the flipping of edge [c,b] or [a,c]. It must still exist in + // Star(ab). Use it to recover the flipped edge. + if (edgepivot == 1) { + // The flip edge is [c,b]. + tmpabtets[0] = abtets[(t - 1 + i) % i]; // [a,b,e,d] + eprevself(tmpabtets[0]); + esymself(tmpabtets[0]); + eprevself(tmpabtets[0]); // [d,a,e,b] + fsym(tmpabtets[0], tmpabtets[1]); // [a,d,e,c] + } else { + // The flip edge is [a,c]. + tmpabtets[1] = abtets[(t - 1 + i) % i]; // [a,b,e,d] + enextself(tmpabtets[1]); + esymself(tmpabtets[1]); + enextself(tmpabtets[1]); // [b,d,e,a] + fsym(tmpabtets[1], tmpabtets[0]); // [d,b,e,c] + } // if (edgepivot == 2) + + // Do a n1-to-m1 flip to recover the flipped edge ([c,b] or [a,c]). + flipnm_post(tmpabtets, n1, 2, edgepivot, fc); + + // Insert the two recovered tets into the original Star(ab). + for (j = i - 1; j >= t; j--) { + abtets[j + 1] = abtets[j]; // Downshift + } + if (edgepivot == 1) { + // tmpabtets[0] is [c,b,d,a] ==> contains [a,b] + // tmpabtets[1] is [c,b,a,e] ==> contains [a,b] + // tmpabtets[2] is [c,b,e,d] + fliptets[0] = tmpabtets[1]; + enextself(fliptets[0]); + esymself(fliptets[0]); // [a,b,e,c] + fliptets[1] = tmpabtets[0]; + esymself(fliptets[1]); + eprevself(fliptets[1]); // [a,b,c,d] + } else { + // tmpabtets[0] is [a,c,d,b] ==> contains [a,b] + // tmpabtets[1] is [a,c,b,e] ==> contains [a,b] + // tmpabtets[2] is [a,c,e,d] + fliptets[0] = tmpabtets[1]; + eprevself(fliptets[0]); + esymself(fliptets[0]); // [a,b,e,c] + fliptets[1] = tmpabtets[0]; + esymself(fliptets[1]); + enextself(fliptets[1]); // [a,b,c,d] + } // edgepivot == 2 + // Insert the two recovered tets into Star(ab). + abtets[((t-1) + (i+1)) % (i+1)] = fliptets[0]; + abtets[t] = fliptets[1]; + } + else { + // Only free the spaces. + flipnm_post(tmpabtets, n1, 2, edgepivot, fc); + } // if (!unflip) + if (b->verbose > 2) { + printf(" Release %d spaces at f[%d].\n", n1, i); + } + delete [] tmpabtets; + } + } // i + + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// insertpoint() Insert a point into current tetrahedralization. // +// // +// The Bowyer-Watson (B-W) algorithm is used to add a new point p into the // +// tetrahedralization T. It first finds a "cavity", denoted as C, in T, C // +// consists of tetrahedra in T that "conflict" with p. If T is a Delaunay // +// tetrahedralization, then all boundary faces (triangles) of C are visible // +// by p, i.e.,C is star-shaped. We can insert p into T by first deleting all // +// tetrahedra in C, then creating new tetrahedra formed by boundary faces of // +// C and p. If T is not a DT, then C may be not star-shaped. It must be // +// modified so that it becomes star-shaped. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::insertpoint(point insertpt, triface *searchtet, face *splitsh, + face *splitseg, insertvertexflags *ivf) +{ + arraypool *swaplist; + triface *cavetet, spintet, neightet, neineitet, *parytet; + triface oldtet, newtet, newneitet; + face checksh, neighsh, *parysh; + face checkseg, *paryseg; + point *pts, pa, pb, pc, *parypt; + enum locateresult loc = OUTSIDE; + REAL sign, ori; + REAL attrib, volume; + bool enqflag; + int t1ver; + int i, j, k, s; + + if (b->verbose > 2) { + printf(" Insert point %d\n", pointmark(insertpt)); + } + + // Locate the point. + if (searchtet->tet != NULL) { + loc = (enum locateresult) ivf->iloc; + } + + if (loc == OUTSIDE) { + if (searchtet->tet == NULL) { + if (!b->weighted) { + randomsample(insertpt, searchtet); + } else { + // Weighted DT. There may exist dangling vertex. + *searchtet = recenttet; + } + } + // Locate the point. + loc = locate(insertpt, searchtet); + } + + ivf->iloc = (int) loc; // The return value. + + if (b->weighted) { + if (loc != OUTSIDE) { + // Check if this vertex is regular. + pts = (point *) searchtet->tet; + sign = orient4d_s(pts[4], pts[5], pts[6], pts[7], insertpt, + pts[4][3], pts[5][3], pts[6][3], pts[7][3], + insertpt[3]); + if (sign > 0) { + // This new vertex lies above the lower hull. Do not insert it. + ivf->iloc = (int) NONREGULAR; + return 0; + } + } + } + + // Create the initial cavity C(p) which contains all tetrahedra that + // intersect p. It may include 1, 2, or n tetrahedra. + // If p lies on a segment or subface, also create the initial sub-cavity + // sC(p) which contains all subfaces (and segment) which intersect p. + + if (loc == OUTSIDE) { + flip14count++; + // The current hull will be enlarged. + // Add four adjacent boundary tets into list. + for (i = 0; i < 4; i++) { + decode(searchtet->tet[i], neightet); + neightet.ver = epivot[neightet.ver]; + cavebdrylist->newindex((void **) &parytet); + *parytet = neightet; + } + infect(*searchtet); + caveoldtetlist->newindex((void **) &parytet); + *parytet = *searchtet; + } else if (loc == INTETRAHEDRON) { + flip14count++; + // Add four adjacent boundary tets into list. + for (i = 0; i < 4; i++) { + decode(searchtet->tet[i], neightet); + neightet.ver = epivot[neightet.ver]; + cavebdrylist->newindex((void **) &parytet); + *parytet = neightet; + } + infect(*searchtet); + caveoldtetlist->newindex((void **) &parytet); + *parytet = *searchtet; + } else if (loc == ONFACE) { + flip26count++; + // Add six adjacent boundary tets into list. + j = (searchtet->ver & 3); // The current face number. + for (i = 1; i < 4; i++) { + decode(searchtet->tet[(j + i) % 4], neightet); + neightet.ver = epivot[neightet.ver]; + cavebdrylist->newindex((void **) &parytet); + *parytet = neightet; + } + decode(searchtet->tet[j], spintet); + j = (spintet.ver & 3); // The current face number. + for (i = 1; i < 4; i++) { + decode(spintet.tet[(j + i) % 4], neightet); + neightet.ver = epivot[neightet.ver]; + cavebdrylist->newindex((void **) &parytet); + *parytet = neightet; + } + infect(spintet); + caveoldtetlist->newindex((void **) &parytet); + *parytet = spintet; + infect(*searchtet); + caveoldtetlist->newindex((void **) &parytet); + *parytet = *searchtet; + + if (ivf->splitbdflag) { + if ((splitsh != NULL) && (splitsh->sh != NULL)) { + // Create the initial sub-cavity sC(p). + smarktest(*splitsh); + caveshlist->newindex((void **) &parysh); + *parysh = *splitsh; + } + } // if (splitbdflag) + } else if (loc == ONEDGE) { + flipn2ncount++; + // Add all adjacent boundary tets into list. + spintet = *searchtet; + while (1) { + eorgoppo(spintet, neightet); + decode(neightet.tet[neightet.ver & 3], neightet); + neightet.ver = epivot[neightet.ver]; + cavebdrylist->newindex((void **) &parytet); + *parytet = neightet; + edestoppo(spintet, neightet); + decode(neightet.tet[neightet.ver & 3], neightet); + neightet.ver = epivot[neightet.ver]; + cavebdrylist->newindex((void **) &parytet); + *parytet = neightet; + infect(spintet); + caveoldtetlist->newindex((void **) &parytet); + *parytet = spintet; + fnextself(spintet); + if (spintet.tet == searchtet->tet) break; + } // while (1) + + if (ivf->splitbdflag) { + // Create the initial sub-cavity sC(p). + if ((splitseg != NULL) && (splitseg->sh != NULL)) { + smarktest(*splitseg); + splitseg->shver = 0; + spivot(*splitseg, *splitsh); + } + if (splitsh != NULL) { + if (splitsh->sh != NULL) { + // Collect all subfaces share at this edge. + pa = sorg(*splitsh); + neighsh = *splitsh; + while (1) { + // Adjust the origin of its edge to be 'pa'. + if (sorg(neighsh) != pa) { + sesymself(neighsh); + } + // Add this face into list (in B-W cavity). + smarktest(neighsh); + caveshlist->newindex((void **) &parysh); + *parysh = neighsh; + // Add this face into face-at-splitedge list. + cavesegshlist->newindex((void **) &parysh); + *parysh = neighsh; + // Go to the next face at the edge. + spivotself(neighsh); + // Stop if all faces at the edge have been visited. + if (neighsh.sh == splitsh->sh) break; + if (neighsh.sh == NULL) break; + } // while (1) + } // if (not a dangling segment) + } + } // if (splitbdflag) + } else if (loc == INSTAR) { + // We assume that all tets in the star are given in 'caveoldtetlist', + // and they are all infected. + // Collect the boundary faces of the star. + for (i = 0; i < caveoldtetlist->objects; i++) { + cavetet = (triface *) fastlookup(caveoldtetlist, i); + // Check its 4 neighbor tets. + for (j = 0; j < 4; j++) { + decode(cavetet->tet[j], neightet); + if (!infected(neightet)) { + // It's a boundary face. + neightet.ver = epivot[neightet.ver]; + cavebdrylist->newindex((void **) &parytet); + *parytet = neightet; + } + } + } + } else if (loc == ONVERTEX) { + // The point already exist. Do nothing and return. + return 0; + } + + + if (ivf->bowywat) { + // Update the cavity C(p) using the Bowyer-Watson algorithm. + swaplist = cavetetlist; + cavetetlist = cavebdrylist; + cavebdrylist = swaplist; + for (i = 0; i < cavetetlist->objects; i++) { + // 'cavetet' is an adjacent tet at outside of the cavity. + cavetet = (triface *) fastlookup(cavetetlist, i); + // The tet may be tested and included in the (enlarged) cavity. + if (!infected(*cavetet)) { + // Check for two possible cases for this tet: + // (1) It is a cavity tet, or + // (2) it is a cavity boundary face. + enqflag = false; + if (!marktested(*cavetet)) { + // Do Delaunay (in-sphere) test. + pts = (point *) cavetet->tet; + if (pts[7] != dummypoint) { + // A volume tet. Operate on it. + if (b->weighted) { + sign = orient4d_s(pts[4], pts[5], pts[6], pts[7], insertpt, + pts[4][3], pts[5][3], pts[6][3], pts[7][3], + insertpt[3]); + } else { + sign = insphere_s(pts[4], pts[5], pts[6], pts[7], insertpt); + } + enqflag = (sign < 0.0); + } else { + if (!nonconvex) { + // Test if this hull face is visible by the new point. + ori = orient3d(pts[4], pts[5], pts[6], insertpt); + if (ori < 0) { + // A visible hull face. + // Include it in the cavity. The convex hull will be enlarged. + enqflag = true; + } else if (ori == 0.0) { + // A coplanar hull face. We need to test if this hull face is + // Delaunay or not. We test if the adjacent tet (not faked) + // of this hull face is Delaunay or not. + decode(cavetet->tet[3], neineitet); + if (!infected(neineitet)) { + if (!marktested(neineitet)) { + // Do Delaunay test on this tet. + pts = (point *) neineitet.tet; + if (b->weighted) { + sign = orient4d_s(pts[4],pts[5],pts[6],pts[7], insertpt, + pts[4][3], pts[5][3], pts[6][3], + pts[7][3], insertpt[3]); + } else { + sign = insphere_s(pts[4],pts[5],pts[6],pts[7], insertpt); + } + enqflag = (sign < 0.0); + } + } else { + // The adjacent tet is non-Delaunay. The hull face is non- + // Delaunay as well. Include it in the cavity. + enqflag = true; + } // if (!infected(neineitet)) + } // if (ori == 0.0) + } else { + // A hull face (must be a subface). + // We FIRST include it in the initial cavity if the adjacent tet + // (not faked) of this hull face is not Delaunay wrt p. + // Whether it belongs to the final cavity will be determined + // during the validation process. 'validflag'. + decode(cavetet->tet[3], neineitet); + if (!infected(neineitet)) { + if (!marktested(neineitet)) { + // Do Delaunay test on this tet. + pts = (point *) neineitet.tet; + if (b->weighted) { + sign = orient4d_s(pts[4],pts[5],pts[6],pts[7], insertpt, + pts[4][3], pts[5][3], pts[6][3], + pts[7][3], insertpt[3]); + } else { + sign = insphere_s(pts[4],pts[5],pts[6],pts[7], insertpt); + } + enqflag = (sign < 0.0); + } + } else { + // The adjacent tet is non-Delaunay. The hull face is non- + // Delaunay as well. Include it in the cavity. + enqflag = true; + } // if (infected(neineitet)) + } // if (nonconvex) + } // if (pts[7] != dummypoint) + marktest(*cavetet); // Only test it once. + } // if (!marktested(*cavetet)) + + if (enqflag) { + // Found a tet in the cavity. Put other three faces in check list. + k = (cavetet->ver & 3); // The current face number + for (j = 1; j < 4; j++) { + decode(cavetet->tet[(j + k) % 4], neightet); + cavetetlist->newindex((void **) &parytet); + *parytet = neightet; + } + infect(*cavetet); + caveoldtetlist->newindex((void **) &parytet); + *parytet = *cavetet; + } else { + // Found a boundary face of the cavity. + cavetet->ver = epivot[cavetet->ver]; + cavebdrylist->newindex((void **) &parytet); + *parytet = *cavetet; + } + } // if (!infected(*cavetet)) + } // i + + cavetetlist->restart(); // Clear the working list. + } // if (ivf->bowywat) + + if (checksubsegflag) { + // Collect all segments of C(p). + shellface *ssptr; + for (i = 0; i < caveoldtetlist->objects; i++) { + cavetet = (triface *) fastlookup(caveoldtetlist, i); + if ((ssptr = (shellface*) cavetet->tet[8]) != NULL) { + for (j = 0; j < 6; j++) { + if (ssptr[j]) { + sdecode(ssptr[j], checkseg); + if (!sinfected(checkseg)) { + sinfect(checkseg); + cavetetseglist->newindex((void **) &paryseg); + *paryseg = checkseg; + } + } + } // j + } + } // i + // Uninfect collected segments. + for (i = 0; i < cavetetseglist->objects; i++) { + paryseg = (face *) fastlookup(cavetetseglist, i); + suninfect(*paryseg); + } + + } // if (checksubsegflag) + + if (checksubfaceflag) { + // Collect all subfaces of C(p). + shellface *sptr; + for (i = 0; i < caveoldtetlist->objects; i++) { + cavetet = (triface *) fastlookup(caveoldtetlist, i); + if ((sptr = (shellface*) cavetet->tet[9]) != NULL) { + for (j = 0; j < 4; j++) { + if (sptr[j]) { + sdecode(sptr[j], checksh); + if (!sinfected(checksh)) { + sinfect(checksh); + cavetetshlist->newindex((void **) &parysh); + *parysh = checksh; + } + } + } // j + } + } // i + // Uninfect collected subfaces. + for (i = 0; i < cavetetshlist->objects; i++) { + parysh = (face *) fastlookup(cavetetshlist, i); + suninfect(*parysh); + } + + } // if (checksubfaceflag) + + if ((ivf->iloc == (int) OUTSIDE) && ivf->refineflag) { + // The vertex lies outside of the domain. And it does not encroach + // upon any boundary segment or subface. Do not insert it. + insertpoint_abort(splitseg, ivf); + return 0; + } + + if (ivf->splitbdflag) { + // The new point locates in surface mesh. Update the sC(p). + // We have already 'smarktested' the subfaces which directly intersect + // with p in 'caveshlist'. From them, we 'smarktest' their neighboring + // subfaces which are included in C(p). Do not across a segment. + for (i = 0; i < caveshlist->objects; i++) { + parysh = (face *) fastlookup(caveshlist, i); + checksh = *parysh; + for (j = 0; j < 3; j++) { + if (!isshsubseg(checksh)) { + spivot(checksh, neighsh); + if (!smarktested(neighsh)) { + stpivot(neighsh, neightet); + if (infected(neightet)) { + fsymself(neightet); + if (infected(neightet)) { + // This subface is inside C(p). + // Check if its diametrical circumsphere encloses 'p'. + // The purpose of this check is to avoid forming invalid + // subcavity in surface mesh. + sign = incircle3d(sorg(neighsh), sdest(neighsh), + sapex(neighsh), insertpt); + if (sign < 0) { + smarktest(neighsh); + caveshlist->newindex((void **) &parysh); + *parysh = neighsh; + } + } + } + } + } + senextself(checksh); + } // j + } // i + } // if (ivf->splitbdflag) + + if (ivf->validflag) { + // Validate C(p) and update it if it is not star-shaped. + int cutcount = 0; + + if (ivf->respectbdflag) { + // The initial cavity may include subfaces which are not on the facets + // being splitting. Find them and make them as boundary of C(p). + // Comment: We have already 'smarktested' the subfaces in sC(p). They + // are completely inside C(p). + for (i = 0; i < cavetetshlist->objects; i++) { + parysh = (face *) fastlookup(cavetetshlist, i); + stpivot(*parysh, neightet); + if (infected(neightet)) { + fsymself(neightet); + if (infected(neightet)) { + // Found a subface inside C(p). + if (!smarktested(*parysh)) { + // It is possible that this face is a boundary subface. + // Check if it is a hull face. + //assert(apex(neightet) != dummypoint); + if (oppo(neightet) != dummypoint) { + fsymself(neightet); + } + if (oppo(neightet) != dummypoint) { + ori = orient3d(org(neightet), dest(neightet), apex(neightet), + insertpt); + if (ori < 0) { + // A visible face, get its neighbor face. + fsymself(neightet); + ori = -ori; // It must be invisible by p. + } + } else { + // A hull tet. It needs to be cut. + ori = 1; + } + // Cut this tet if it is either invisible by or coplanar with p. + if (ori >= 0) { + uninfect(neightet); + unmarktest(neightet); + cutcount++; + neightet.ver = epivot[neightet.ver]; + cavebdrylist->newindex((void **) &parytet); + *parytet = neightet; + // Add three new faces to find new boundaries. + for (j = 0; j < 3; j++) { + esym(neightet, neineitet); + neineitet.ver = epivot[neineitet.ver]; + cavebdrylist->newindex((void **) &parytet); + *parytet = neineitet; + enextself(neightet); + } + } // if (ori >= 0) + } + } + } + } // i + + // The initial cavity may include segments in its interior. We need to + // Update the cavity so that these segments are on the boundary of + // the cavity. + for (i = 0; i < cavetetseglist->objects; i++) { + paryseg = (face *) fastlookup(cavetetseglist, i); + // Check this segment if it is not a splitting segment. + if (!smarktested(*paryseg)) { + sstpivot1(*paryseg, neightet); + spintet = neightet; + while (1) { + if (!infected(spintet)) break; + fnextself(spintet); + if (spintet.tet == neightet.tet) break; + } + if (infected(spintet)) { + // Find an adjacent tet at this segment such that both faces + // at this segment are not visible by p. + pa = org(neightet); + pb = dest(neightet); + spintet = neightet; + j = 0; + while (1) { + // Check if this face is visible by p. + pc = apex(spintet); + if (pc != dummypoint) { + ori = orient3d(pa, pb, pc, insertpt); + if (ori >= 0) { + // Not visible. Check another face in this tet. + esym(spintet, neineitet); + pc = apex(neineitet); + if (pc != dummypoint) { + ori = orient3d(pb, pa, pc, insertpt); + if (ori >= 0) { + // Not visible. Found this face. + j = 1; // Flag that it is found. + break; + } + } + } + } + fnextself(spintet); + if (spintet.tet == neightet.tet) break; + } + if (j == 0) { + // Not found such a face. + terminatetetgen(this, 2); + } + neightet = spintet; + if (b->verbose > 3) { + printf(" Cut tet (%d, %d, %d, %d)\n", + pointmark(org(neightet)), pointmark(dest(neightet)), + pointmark(apex(neightet)), pointmark(oppo(neightet))); + } + uninfect(neightet); + unmarktest(neightet); + cutcount++; + neightet.ver = epivot[neightet.ver]; + cavebdrylist->newindex((void **) &parytet); + *parytet = neightet; + // Add three new faces to find new boundaries. + for (j = 0; j < 3; j++) { + esym(neightet, neineitet); + neineitet.ver = epivot[neineitet.ver]; + cavebdrylist->newindex((void **) &parytet); + *parytet = neineitet; + enextself(neightet); + } + } + } + } // i + } // if (ivf->respectbdflag) + + // Update the cavity by removing invisible faces until it is star-shaped. + for (i = 0; i < cavebdrylist->objects; i++) { + cavetet = (triface *) fastlookup(cavebdrylist, i); + // 'cavetet' is an exterior tet adjacent to the cavity. + // Check if its neighbor is inside C(p). + fsym(*cavetet, neightet); + if (infected(neightet)) { + if (apex(*cavetet) != dummypoint) { + // It is a cavity boundary face. Check its visibility. + if (oppo(neightet) != dummypoint) { + // Check if this face is visible by the new point. + if (issubface(neightet)) { + // We should only create a new tet that has a reasonable volume. + // Re-use 'volume' and 'attrib'. + pa = org(*cavetet); + pb = dest(*cavetet); + pc = apex(*cavetet); + volume = orient3dfast(pa, pb, pc, insertpt); + attrib = distance(pa, pb) * distance(pb, pc) * distance(pc, pa); + if ((fabs(volume) / attrib) < b->epsilon) { + ori = 0.0; + } else { + ori = orient3d(pa, pb, pc, insertpt); + } + } else { + ori = orient3d(org(*cavetet), dest(*cavetet), apex(*cavetet), + insertpt); + } + enqflag = (ori > 0); + // Comment: if ori == 0 (coplanar case), we also cut the tet. + } else { + // It is a hull face. And its adjacent tet (at inside of the + // domain) has been cut from the cavity. Cut it as well. + //assert(nonconvex); + enqflag = false; + } + } else { + enqflag = true; // A hull edge. + } + if (enqflag) { + // This face is valid, save it. + cavetetlist->newindex((void **) &parytet); + *parytet = *cavetet; + } else { + uninfect(neightet); + unmarktest(neightet); + cutcount++; + // Add three new faces to find new boundaries. + for (j = 0; j < 3; j++) { + esym(neightet, neineitet); + neineitet.ver = epivot[neineitet.ver]; + cavebdrylist->newindex((void **) &parytet); + *parytet = neineitet; + enextself(neightet); + } + // 'cavetet' is not on the cavity boundary anymore. + unmarktest(*cavetet); + } + } else { + // 'cavetet' is not on the cavity boundary anymore. + unmarktest(*cavetet); + } + } // i + + if (cutcount > 0) { + // The cavity has been updated. + // Update the cavity boundary faces. + cavebdrylist->restart(); + for (i = 0; i < cavetetlist->objects; i++) { + cavetet = (triface *) fastlookup(cavetetlist, i); + // 'cavetet' was an exterior tet adjacent to the cavity. + fsym(*cavetet, neightet); + if (infected(neightet)) { + // It is a cavity boundary face. + cavebdrylist->newindex((void **) &parytet); + *parytet = *cavetet; + } else { + // Not a cavity boundary face. + unmarktest(*cavetet); + } + } + + // Update the list of old tets. + cavetetlist->restart(); + for (i = 0; i < caveoldtetlist->objects; i++) { + cavetet = (triface *) fastlookup(caveoldtetlist, i); + if (infected(*cavetet)) { + cavetetlist->newindex((void **) &parytet); + *parytet = *cavetet; + } + } + // Swap 'cavetetlist' and 'caveoldtetlist'. + swaplist = caveoldtetlist; + caveoldtetlist = cavetetlist; + cavetetlist = swaplist; + + // The cavity should contain at least one tet. + if (caveoldtetlist->objects == 0l) { + insertpoint_abort(splitseg, ivf); + ivf->iloc = (int) BADELEMENT; + return 0; + } + + if (ivf->splitbdflag) { + int cutshcount = 0; + // Update the sub-cavity sC(p). + for (i = 0; i < caveshlist->objects; i++) { + parysh = (face *) fastlookup(caveshlist, i); + if (smarktested(*parysh)) { + enqflag = false; + stpivot(*parysh, neightet); + if (infected(neightet)) { + fsymself(neightet); + if (infected(neightet)) { + enqflag = true; + } + } + if (!enqflag) { + sunmarktest(*parysh); + // Use the last entry of this array to fill this entry. + j = caveshlist->objects - 1; + checksh = * (face *) fastlookup(caveshlist, j); + *parysh = checksh; + cutshcount++; + caveshlist->objects--; // The list is shrinked. + i--; + } + } + } + + if (cutshcount > 0) { + i = 0; // Count the number of invalid subfaces/segments. + // Valid the updated sub-cavity sC(p). + if (loc == ONFACE) { + if ((splitsh != NULL) && (splitsh->sh != NULL)) { + // The to-be split subface should be in sC(p). + if (!smarktested(*splitsh)) i++; + } + } else if (loc == ONEDGE) { + if ((splitseg != NULL) && (splitseg->sh != NULL)) { + // The to-be split segment should be in sC(p). + if (!smarktested(*splitseg)) i++; + } + if ((splitsh != NULL) && (splitsh->sh != NULL)) { + // All subfaces at this edge should be in sC(p). + pa = sorg(*splitsh); + neighsh = *splitsh; + while (1) { + // Adjust the origin of its edge to be 'pa'. + if (sorg(neighsh) != pa) { + sesymself(neighsh); + } + // Add this face into list (in B-W cavity). + if (!smarktested(neighsh)) i++; + // Go to the next face at the edge. + spivotself(neighsh); + // Stop if all faces at the edge have been visited. + if (neighsh.sh == splitsh->sh) break; + if (neighsh.sh == NULL) break; + } // while (1) + } + } + + if (i > 0) { + // The updated sC(p) is invalid. Do not insert this vertex. + insertpoint_abort(splitseg, ivf); + ivf->iloc = (int) BADELEMENT; + return 0; + } + } // if (cutshcount > 0) + } // if (ivf->splitbdflag) + } // if (cutcount > 0) + + } // if (ivf->validflag) + + if (ivf->refineflag) { + // The new point is inserted by Delaunay refinement, i.e., it is the + // circumcenter of a tetrahedron, or a subface, or a segment. + // Do not insert this point if the tetrahedron, or subface, or segment + // is not inside the final cavity. + if (((ivf->refineflag == 1) && !infected(ivf->refinetet)) || + ((ivf->refineflag == 2) && !smarktested(ivf->refinesh))) { + insertpoint_abort(splitseg, ivf); + ivf->iloc = (int) BADELEMENT; + return 0; + } + } // if (ivf->refineflag) + + if (b->plc && (loc != INSTAR)) { + // Reject the new point if it lies too close to an existing point (b->plc), + // or it lies inside a protecting ball of near vertex (ivf->rejflag & 4). + // Collect the list of vertices of the initial cavity. + if (loc == OUTSIDE) { + pts = (point *) &(searchtet->tet[4]); + for (i = 0; i < 3; i++) { + cavetetvertlist->newindex((void **) &parypt); + *parypt = pts[i]; + } + } else if (loc == INTETRAHEDRON) { + pts = (point *) &(searchtet->tet[4]); + for (i = 0; i < 4; i++) { + cavetetvertlist->newindex((void **) &parypt); + *parypt = pts[i]; + } + } else if (loc == ONFACE) { + pts = (point *) &(searchtet->tet[4]); + for (i = 0; i < 3; i++) { + cavetetvertlist->newindex((void **) &parypt); + *parypt = pts[i]; + } + if (pts[3] != dummypoint) { + cavetetvertlist->newindex((void **) &parypt); + *parypt = pts[3]; + } + fsym(*searchtet, spintet); + if (oppo(spintet) != dummypoint) { + cavetetvertlist->newindex((void **) &parypt); + *parypt = oppo(spintet); + } + } else if (loc == ONEDGE) { + spintet = *searchtet; + cavetetvertlist->newindex((void **) &parypt); + *parypt = org(spintet); + cavetetvertlist->newindex((void **) &parypt); + *parypt = dest(spintet); + while (1) { + if (apex(spintet) != dummypoint) { + cavetetvertlist->newindex((void **) &parypt); + *parypt = apex(spintet); + } + fnextself(spintet); + if (spintet.tet == searchtet->tet) break; + } + } + + int rejptflag = (ivf->rejflag & 4); + REAL rd; + pts = NULL; + + for (i = 0; i < cavetetvertlist->objects; i++) { + parypt = (point *) fastlookup(cavetetvertlist, i); + rd = distance(*parypt, insertpt); + // Is the point very close to an existing point? + if (rd < minedgelength) { + pts = parypt; + loc = NEARVERTEX; + break; + } + if (rejptflag) { + // Is the point encroaches upon an existing point? + if (rd < (0.5 * (*parypt)[pointmtrindex])) { + pts = parypt; + loc = ENCVERTEX; + break; + } + } + } + cavetetvertlist->restart(); // Clear the work list. + + if (pts != NULL) { + // The point is either too close to an existing vertex (NEARVERTEX) + // or encroaches upon (inside the protecting ball) of that vertex. + if (loc == NEARVERTEX) { + if (!issteinerpoint(insertpt) && b->nomergevertex) { // -M0/1 option. + // 'insertpt' is an input vertex. + // In this case, we still insert this vertex. Issue a warning. + 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, minedgelength); + printf(" You may try a smaller tolerance (-T) (current is %g)\n", + b->epsilon); + printf(" to avoid this warning.\n"); + } + } else { + point2tetorg(*pts, *searchtet); + insertpoint_abort(splitseg, ivf); + ivf->iloc = (int) loc; + return 0; + } + } else { // loc == ENCVERTEX + // The point lies inside the protection ball. + point2tetorg(*pts, *searchtet); + insertpoint_abort(splitseg, ivf); + ivf->iloc = (int) loc; + return 0; + } + } + } // if (b->plc && (loc != INSTAR)) + + if (b->weighted || ivf->cdtflag || ivf->smlenflag + ) { + // There may be other vertices inside C(p). We need to find them. + // Collect all vertices of C(p). + for (i = 0; i < caveoldtetlist->objects; i++) { + cavetet = (triface *) fastlookup(caveoldtetlist, i); + //assert(infected(*cavetet)); + pts = (point *) &(cavetet->tet[4]); + for (j = 0; j < 4; j++) { + if (pts[j] != dummypoint) { + if (!pinfected(pts[j])) { + pinfect(pts[j]); + cavetetvertlist->newindex((void **) &parypt); + *parypt = pts[j]; + } + } + } // j + } // i + // Uninfect all collected (cavity) vertices. + for (i = 0; i < cavetetvertlist->objects; i++) { + parypt = (point *) fastlookup(cavetetvertlist, i); + puninfect(*parypt); + } + if (ivf->smlenflag) { + REAL len; + // Get the length of the shortest edge connecting to 'newpt'. + parypt = (point *) fastlookup(cavetetvertlist, 0); + ivf->smlen = distance(*parypt, insertpt); + ivf->parentpt = *parypt; + for (i = 1; i < cavetetvertlist->objects; i++) { + parypt = (point *) fastlookup(cavetetvertlist, i); + len = distance(*parypt, insertpt); + if (len < ivf->smlen) { + ivf->smlen = len; + ivf->parentpt = *parypt; + } + } + } + } + + + if (ivf->cdtflag) { + // Unmark tets. + for (i = 0; i < caveoldtetlist->objects; i++) { + cavetet = (triface *) fastlookup(caveoldtetlist, i); + unmarktest(*cavetet); + } + for (i = 0; i < cavebdrylist->objects; i++) { + cavetet = (triface *) fastlookup(cavebdrylist, i); + unmarktest(*cavetet); + } + // Clean up arrays which are not needed. + cavetetlist->restart(); + if (checksubsegflag) { + cavetetseglist->restart(); + } + if (checksubfaceflag) { + cavetetshlist->restart(); + } + return 1; + } + + // Before re-mesh C(p). Process the segments and subfaces which are on the + // boundary of C(p). Make sure that each such segment or subface is + // connecting to a tet outside C(p). So we can re-connect them to the + // new tets inside the C(p) later. + + if (checksubsegflag) { + for (i = 0; i < cavetetseglist->objects; i++) { + paryseg = (face *) fastlookup(cavetetseglist, i); + // Operate on it if it is not the splitting segment, i.e., in sC(p). + if (!smarktested(*paryseg)) { + // Check if the segment is inside the cavity. + // 'j' counts the num of adjacent tets of this seg. + // 'k' counts the num of adjacent tets which are 'sinfected'. + j = k = 0; + sstpivot1(*paryseg, neightet); + spintet = neightet; + while (1) { + j++; + if (!infected(spintet)) { + neineitet = spintet; // An outer tet. Remember it. + } else { + k++; // An in tet. + } + fnextself(spintet); + if (spintet.tet == neightet.tet) break; + } + // assert(j > 0); + if (k == 0) { + // The segment is not connect to C(p) anymore. Remove it by + // Replacing it by the last entry of this list. + s = cavetetseglist->objects - 1; + checkseg = * (face *) fastlookup(cavetetseglist, s); + *paryseg = checkseg; + cavetetseglist->objects--; + i--; + } else if (k < j) { + // The segment is on the boundary of C(p). + sstbond1(*paryseg, neineitet); + } else { // k == j + // The segment is inside C(p). + if (!ivf->splitbdflag) { + checkseg = *paryseg; + sinfect(checkseg); // Flag it as an interior segment. + caveencseglist->newindex((void **) &paryseg); + *paryseg = checkseg; + } else { + //assert(0); // Not possible. + terminatetetgen(this, 2); + } + } + } else { + // assert(smarktested(*paryseg)); + // Flag it as an interior segment. Do not queue it, since it will + // be deleted after the segment splitting. + sinfect(*paryseg); + } + } // i + } // if (checksubsegflag) + + if (checksubfaceflag) { + for (i = 0; i < cavetetshlist->objects; i++) { + parysh = (face *) fastlookup(cavetetshlist, i); + // Operate on it if it is not inside the sub-cavity sC(p). + if (!smarktested(*parysh)) { + // Check if this subface is inside the cavity. + k = 0; + for (j = 0; j < 2; j++) { + stpivot(*parysh, neightet); + if (!infected(neightet)) { + checksh = *parysh; // Remember this side. + } else { + k++; + } + sesymself(*parysh); + } + if (k == 0) { + // The subface is not connected to C(p). Remove it. + s = cavetetshlist->objects - 1; + checksh = * (face *) fastlookup(cavetetshlist, s); + *parysh = checksh; + cavetetshlist->objects--; + i--; + } else if (k == 1) { + // This side is the outer boundary of C(p). + *parysh = checksh; + } else { // k == 2 + if (!ivf->splitbdflag) { + checksh = *parysh; + sinfect(checksh); // Flag it. + caveencshlist->newindex((void **) &parysh); + *parysh = checksh; + } else { + //assert(0); // Not possible. + terminatetetgen(this, 2); + } + } + } else { + // assert(smarktested(*parysh)); + // Flag it as an interior subface. Do not queue it. It will be + // deleted after the facet point insertion. + sinfect(*parysh); + } + } // i + } // if (checksubfaceflag) + + // Create new tetrahedra to fill the cavity. + + for (i = 0; i < cavebdrylist->objects; i++) { + cavetet = (triface *) fastlookup(cavebdrylist, i); + neightet = *cavetet; + unmarktest(neightet); // Unmark it. + // Get the oldtet (inside the cavity). + fsym(neightet, oldtet); + if (apex(neightet) != dummypoint) { + // Create a new tet in the cavity. + maketetrahedron(&newtet); + setorg(newtet, dest(neightet)); + setdest(newtet, org(neightet)); + setapex(newtet, apex(neightet)); + setoppo(newtet, insertpt); + } else { + // Create a new hull tet. + hullsize++; + maketetrahedron(&newtet); + setorg(newtet, org(neightet)); + setdest(newtet, dest(neightet)); + setapex(newtet, insertpt); + setoppo(newtet, dummypoint); // It must opposite to face 3. + // Adjust back to the cavity bounday face. + esymself(newtet); + } + // The new tet inherits attribtes from the old tet. + for (j = 0; j < numelemattrib; j++) { + attrib = elemattribute(oldtet.tet, j); + setelemattribute(newtet.tet, j, attrib); + } + if (b->varvolume) { + volume = volumebound(oldtet.tet); + setvolumebound(newtet.tet, volume); + } + // Connect newtet <==> neightet, this also disconnect the old bond. + bond(newtet, neightet); + // oldtet still connects to neightet. + *cavetet = oldtet; // *cavetet = newtet; + } // i + + // Set a handle for speeding point location. + recenttet = newtet; + //setpoint2tet(insertpt, encode(newtet)); + setpoint2tet(insertpt, (tetrahedron) (newtet.tet)); + + // Re-use this list to save new interior cavity faces. + cavetetlist->restart(); + + // Connect adjacent new tetrahedra together. + for (i = 0; i < cavebdrylist->objects; i++) { + cavetet = (triface *) fastlookup(cavebdrylist, i); + // cavtet is an oldtet, get the newtet at this face. + oldtet = *cavetet; + fsym(oldtet, neightet); + fsym(neightet, newtet); + // Comment: oldtet and newtet must be at the same directed edge. + // Connect the three other faces of this newtet. + for (j = 0; j < 3; j++) { + esym(newtet, neightet); // Go to the face. + if (neightet.tet[neightet.ver & 3] == NULL) { + // Find the adjacent face of this newtet. + spintet = oldtet; + while (1) { + fnextself(spintet); + if (!infected(spintet)) break; + } + fsym(spintet, newneitet); + esymself(newneitet); + bond(neightet, newneitet); + if (ivf->lawson > 1) { + cavetetlist->newindex((void **) &parytet); + *parytet = neightet; + } + } + //setpoint2tet(org(newtet), encode(newtet)); + setpoint2tet(org(newtet), (tetrahedron) (newtet.tet)); + enextself(newtet); + enextself(oldtet); + } + *cavetet = newtet; // Save the new tet. + } // i + + if (checksubfaceflag) { + // Connect subfaces on the boundary of the cavity to the new tets. + for (i = 0; i < cavetetshlist->objects; i++) { + parysh = (face *) fastlookup(cavetetshlist, i); + // Connect it if it is not a missing subface. + if (!sinfected(*parysh)) { + stpivot(*parysh, neightet); + fsym(neightet, spintet); + sesymself(*parysh); + tsbond(spintet, *parysh); + } + } + } + + if (checksubsegflag) { + // Connect segments on the boundary of the cavity to the new tets. + for (i = 0; i < cavetetseglist->objects; i++) { + paryseg = (face *) fastlookup(cavetetseglist, i); + // Connect it if it is not a missing segment. + if (!sinfected(*paryseg)) { + sstpivot1(*paryseg, neightet); + spintet = neightet; + while (1) { + tssbond1(spintet, *paryseg); + fnextself(spintet); + if (spintet.tet == neightet.tet) break; + } + } + } + } + + if (((splitsh != NULL) && (splitsh->sh != NULL)) || + ((splitseg != NULL) && (splitseg->sh != NULL))) { + // Split a subface or a segment. + sinsertvertex(insertpt, splitsh, splitseg, ivf->sloc, ivf->sbowywat, 0); + } + + if (checksubfaceflag) { + if (ivf->splitbdflag) { + // Recover new subfaces in C(p). + for (i = 0; i < caveshbdlist->objects; i++) { + // Get an old subface at edge [a, b]. + parysh = (face *) fastlookup(caveshbdlist, i); + spivot(*parysh, checksh); // The new subface [a, b, p]. + // Do not recover a deleted new face (degenerated). + if (checksh.sh[3] != NULL) { + // Note that the old subface still connects to adjacent old tets + // of C(p), which still connect to the tets outside C(p). + stpivot(*parysh, neightet); + // Find the adjacent tet containing the edge [a,b] outside C(p). + spintet = neightet; + while (1) { + fnextself(spintet); + if (!infected(spintet)) break; + } + // The adjacent tet connects to a new tet in C(p). + fsym(spintet, neightet); + // Find the tet containing the face [a, b, p]. + spintet = neightet; + while (1) { + fnextself(spintet); + if (apex(spintet) == insertpt) break; + } + // Adjust the edge direction in spintet and checksh. + if (sorg(checksh) != org(spintet)) { + sesymself(checksh); + } + // Connect the subface to two adjacent tets. + tsbond(spintet, checksh); + fsymself(spintet); + sesymself(checksh); + tsbond(spintet, checksh); + } // if (checksh.sh[3] != NULL) + } + } else { + // The Boundary recovery phase. + // Put all new subfaces into stack for recovery. + for (i = 0; i < caveshbdlist->objects; i++) { + // Get an old subface at edge [a, b]. + parysh = (face *) fastlookup(caveshbdlist, i); + spivot(*parysh, checksh); // The new subface [a, b, p]. + // Do not recover a deleted new face (degenerated). + if (checksh.sh[3] != NULL) { + subfacstack->newindex((void **) &parysh); + *parysh = checksh; + } + } + // Put all interior subfaces into stack for recovery. + for (i = 0; i < caveencshlist->objects; i++) { + parysh = (face *) fastlookup(caveencshlist, i); + // Some subfaces inside C(p) might be split in sinsertvertex(). + // Only queue those faces which are not split. + if (!smarktested(*parysh)) { + checksh = *parysh; + suninfect(checksh); + stdissolve(checksh); // Detach connections to old tets. + subfacstack->newindex((void **) &parysh); + *parysh = checksh; + } + } + } + } // if (checksubfaceflag) + + if (checksubsegflag) { + if (ivf->splitbdflag) { + if (splitseg != NULL) { + // Recover the two new subsegments in C(p). + for (i = 0; i < cavesegshlist->objects; i++) { + paryseg = (face *) fastlookup(cavesegshlist, i); + // Insert this subsegment into C(p). + checkseg = *paryseg; + // Get the adjacent new subface. + checkseg.shver = 0; + spivot(checkseg, checksh); + if (checksh.sh != NULL) { + // Get the adjacent new tetrahedron. + stpivot(checksh, neightet); + } else { + // It's a dangling segment. + point2tetorg(sorg(checkseg), neightet); + finddirection(&neightet, sdest(checkseg)); + } + sstbond1(checkseg, neightet); + spintet = neightet; + while (1) { + tssbond1(spintet, checkseg); + fnextself(spintet); + if (spintet.tet == neightet.tet) break; + } + } + } // if (splitseg != NULL) + } else { + // The Boundary Recovery Phase. + // Queue missing segments in C(p) for recovery. + if (splitseg != NULL) { + // Queue two new subsegments in C(p) for recovery. + for (i = 0; i < cavesegshlist->objects; i++) { + paryseg = (face *) fastlookup(cavesegshlist, i); + checkseg = *paryseg; + //sstdissolve1(checkseg); // It has not been connected yet. + s = randomnation(subsegstack->objects + 1); + subsegstack->newindex((void **) &paryseg); + *paryseg = * (face *) fastlookup(subsegstack, s); + paryseg = (face *) fastlookup(subsegstack, s); + *paryseg = checkseg; + } + } // if (splitseg != NULL) + for (i = 0; i < caveencseglist->objects; i++) { + paryseg = (face *) fastlookup(caveencseglist, i); + if (!smarktested(*paryseg)) { // It may be split. + checkseg = *paryseg; + suninfect(checkseg); + sstdissolve1(checkseg); // Detach connections to old tets. + s = randomnation(subsegstack->objects + 1); + subsegstack->newindex((void **) &paryseg); + *paryseg = * (face *) fastlookup(subsegstack, s); + paryseg = (face *) fastlookup(subsegstack, s); + *paryseg = checkseg; + } + } + } + } // if (checksubsegflag) + + if (b->weighted + ) { + // Some vertices may be completed inside the cavity. They must be + // detected and added to recovering list. + // Since every "live" vertex must contain a pointer to a non-dead + // tetrahedron, we can check for each vertex this pointer. + for (i = 0; i < cavetetvertlist->objects; i++) { + pts = (point *) fastlookup(cavetetvertlist, i); + decode(point2tet(*pts), *searchtet); + if (infected(*searchtet)) { + if (b->weighted) { + if (b->verbose > 1) { + printf(" Point #%d is non-regular after the insertion of #%d.\n", + pointmark(*pts), pointmark(insertpt)); + } + setpointtype(*pts, NREGULARVERTEX); + nonregularcount++; + } + } + } + } + + if (ivf->chkencflag & 1) { + // Queue all segment outside C(p). + for (i = 0; i < cavetetseglist->objects; i++) { + paryseg = (face *) fastlookup(cavetetseglist, i); + // Skip if it is the split segment. + if (!sinfected(*paryseg)) { + enqueuesubface(badsubsegs, paryseg); + } + } + if (splitseg != NULL) { + // Queue the two new subsegments inside C(p). + for (i = 0; i < cavesegshlist->objects; i++) { + paryseg = (face *) fastlookup(cavesegshlist, i); + enqueuesubface(badsubsegs, paryseg); + } + } + } // if (chkencflag & 1) + + if (ivf->chkencflag & 2) { + // Queue all subfaces outside C(p). + for (i = 0; i < cavetetshlist->objects; i++) { + parysh = (face *) fastlookup(cavetetshlist, i); + // Skip if it is a split subface. + if (!sinfected(*parysh)) { + enqueuesubface(badsubfacs, parysh); + } + } + // Queue all new subfaces inside C(p). + for (i = 0; i < caveshbdlist->objects; i++) { + // Get an old subface at edge [a, b]. + parysh = (face *) fastlookup(caveshbdlist, i); + spivot(*parysh, checksh); // checksh is a new subface [a, b, p]. + // Do not recover a deleted new face (degenerated). + if (checksh.sh[3] != NULL) { + enqueuesubface(badsubfacs, &checksh); + } + } + } // if (chkencflag & 2) + + if (ivf->chkencflag & 4) { + // Queue all new tetrahedra in C(p). + for (i = 0; i < cavebdrylist->objects; i++) { + cavetet = (triface *) fastlookup(cavebdrylist, i); + enqueuetetrahedron(cavetet); + } + } + + // C(p) is re-meshed successfully. + + // Delete the old tets in C(p). + for (i = 0; i < caveoldtetlist->objects; i++) { + searchtet = (triface *) fastlookup(caveoldtetlist, i); + if (ishulltet(*searchtet)) { + hullsize--; + } + tetrahedrondealloc(searchtet->tet); + } + + if (((splitsh != NULL) && (splitsh->sh != NULL)) || + ((splitseg != NULL) && (splitseg->sh != NULL))) { + // Delete the old subfaces in sC(p). + for (i = 0; i < caveshlist->objects; i++) { + parysh = (face *) fastlookup(caveshlist, i); + if (checksubfaceflag) {//if (bowywat == 2) { + // It is possible that this subface still connects to adjacent + // tets which are not in C(p). If so, clear connections in the + // adjacent tets at this subface. + stpivot(*parysh, neightet); + if (neightet.tet != NULL) { + if (neightet.tet[4] != NULL) { + // Found an adjacent tet. It must be not in C(p). + tsdissolve(neightet); + fsymself(neightet); + tsdissolve(neightet); + } + } + } + shellfacedealloc(subfaces, parysh->sh); + } + if ((splitseg != NULL) && (splitseg->sh != NULL)) { + // Delete the old segment in sC(p). + shellfacedealloc(subsegs, splitseg->sh); + } + } + + if (ivf->lawson) { + for (i = 0; i < cavebdrylist->objects; i++) { + searchtet = (triface *) fastlookup(cavebdrylist, i); + flippush(flipstack, searchtet); + } + if (ivf->lawson > 1) { + for (i = 0; i < cavetetlist->objects; i++) { + searchtet = (triface *) fastlookup(cavetetlist, i); + flippush(flipstack, searchtet); + } + } + } + + + // Clean the working lists. + + caveoldtetlist->restart(); + cavebdrylist->restart(); + cavetetlist->restart(); + + if (checksubsegflag) { + cavetetseglist->restart(); + caveencseglist->restart(); + } + + if (checksubfaceflag) { + cavetetshlist->restart(); + caveencshlist->restart(); + } + + if (b->weighted || ivf->smlenflag + ) { + cavetetvertlist->restart(); + } + + if (((splitsh != NULL) && (splitsh->sh != NULL)) || + ((splitseg != NULL) && (splitseg->sh != NULL))) { + caveshlist->restart(); + caveshbdlist->restart(); + cavesegshlist->restart(); + } + + return 1; // Point is inserted. +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// insertpoint_abort() Abort the insertion of a new vertex. // +// // +// The cavity will be restored. All working lists are cleared. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::insertpoint_abort(face *splitseg, insertvertexflags *ivf) +{ + triface *cavetet; + face *parysh; + int i; + + for (i = 0; i < caveoldtetlist->objects; i++) { + cavetet = (triface *) fastlookup(caveoldtetlist, i); + uninfect(*cavetet); + unmarktest(*cavetet); + } + for (i = 0; i < cavebdrylist->objects; i++) { + cavetet = (triface *) fastlookup(cavebdrylist, i); + unmarktest(*cavetet); + } + cavetetlist->restart(); + cavebdrylist->restart(); + caveoldtetlist->restart(); + cavetetseglist->restart(); + cavetetshlist->restart(); + if (ivf->splitbdflag) { + if ((splitseg != NULL) && (splitseg->sh != NULL)) { + sunmarktest(*splitseg); + } + for (i = 0; i < caveshlist->objects; i++) { + parysh = (face *) fastlookup(caveshlist, i); + sunmarktest(*parysh); + } + caveshlist->restart(); + cavesegshlist->restart(); + } +} + +//// //// +//// //// +//// flip_cxx ///////////////////////////////////////////////////////////////// + +//// delaunay_cxx ///////////////////////////////////////////////////////////// +//// //// +//// //// + + +/////////////////////////////////////////////////////////////////////////////// +// // +// hilbert_init() Initialize the Gray code permutation table. // +// // +// The table 'transgc' has 8 x 3 x 8 entries. It contains all possible Gray // +// code sequences traveled by the 1st order Hilbert curve in 3 dimensions. // +// The first column is the Gray code of the entry point of the curve, and // +// the second column is the direction (0, 1, or 2, 0 means the x-axis) where // +// the exit point of curve lies. // +// // +// The table 'tsb1mod3' contains the numbers of trailing set '1' bits of the // +// indices from 0 to 7, modulo by '3'. The code for generating this table is // +// from: http://graphics.stanford.edu/~seander/bithacks.html. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::hilbert_init(int n) +{ + int gc[8], N, mask, travel_bit; + int e, d, f, k, g; + int v, c; + int i; + + N = (n == 2) ? 4 : 8; + mask = (n == 2) ? 3 : 7; + + // Generate the Gray code sequence. + for (i = 0; i < N; i++) { + gc[i] = i ^ (i >> 1); + } + + for (e = 0; e < N; e++) { + for (d = 0; d < n; d++) { + // Calculate the end point (f). + f = e ^ (1 << d); // Toggle the d-th bit of 'e'. + // travel_bit = 2**p, the bit we want to travel. + travel_bit = e ^ f; + for (i = 0; i < N; i++) { + // // Rotate gc[i] left by (p + 1) % n bits. + k = gc[i] * (travel_bit * 2); + g = ((k | (k / N)) & mask); + // Calculate the permuted Gray code by xor with the start point (e). + transgc[e][d][i] = (g ^ e); + } + } // d + } // e + + // Count the consecutive '1' bits (trailing) on the right. + tsb1mod3[0] = 0; + for (i = 1; i < N; i++) { + v = ~i; // Count the 0s. + v = (v ^ (v - 1)) >> 1; // Set v's trailing 0s to 1s and zero rest + for (c = 0; v; c++) { + v >>= 1; + } + tsb1mod3[i] = c % n; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// hilbert_sort3() Sort points using the 3d Hilbert curve. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::hilbert_split(point* vertexarray,int arraysize,int gc0,int gc1, + REAL bxmin, REAL bxmax, REAL bymin, REAL bymax, + REAL bzmin, REAL bzmax) +{ + point swapvert; + int axis, d; + REAL split; + int i, j; + + + // Find the current splitting axis. 'axis' is a value 0, or 1, or 2, which + // correspoding to x-, or y- or z-axis. + axis = (gc0 ^ gc1) >> 1; + + // Calulate the split position along the axis. + if (axis == 0) { + split = 0.5 * (bxmin + bxmax); + } else if (axis == 1) { + split = 0.5 * (bymin + bymax); + } else { // == 2 + split = 0.5 * (bzmin + bzmax); + } + + // Find the direction (+1 or -1) of the axis. If 'd' is +1, the direction + // of the axis is to the positive of the axis, otherwise, it is -1. + d = ((gc0 & (1<<axis)) == 0) ? 1 : -1; + + + // Partition the vertices into left- and right-arrays such that left points + // have Hilbert indices lower than the right points. + i = 0; + j = arraysize - 1; + + // Partition the vertices into left- and right-arrays. + if (d > 0) { + do { + for (; i < arraysize; i++) { + if (vertexarray[i][axis] >= split) break; + } + for (; j >= 0; j--) { + if (vertexarray[j][axis] < split) break; + } + // Is the partition finished? + if (i == (j + 1)) break; + // Swap i-th and j-th vertices. + swapvert = vertexarray[i]; + vertexarray[i] = vertexarray[j]; + vertexarray[j] = swapvert; + // Continue patitioning the array; + } while (true); + } else { + do { + for (; i < arraysize; i++) { + if (vertexarray[i][axis] <= split) break; + } + for (; j >= 0; j--) { + if (vertexarray[j][axis] > split) break; + } + // Is the partition finished? + if (i == (j + 1)) break; + // Swap i-th and j-th vertices. + swapvert = vertexarray[i]; + vertexarray[i] = vertexarray[j]; + vertexarray[j] = swapvert; + // Continue patitioning the array; + } while (true); + } + + return i; +} + +void tetgenmesh::hilbert_sort3(point* vertexarray, int arraysize, int e, int d, + REAL bxmin, REAL bxmax, REAL bymin, REAL bymax, + REAL bzmin, REAL bzmax, int depth) +{ + REAL x1, x2, y1, y2, z1, z2; + int p[9], w, e_w, d_w, k, ei, di; + int n = 3, mask = 7; + + p[0] = 0; + p[8] = arraysize; + + // Sort the points according to the 1st order Hilbert curve in 3d. + p[4] = hilbert_split(vertexarray, p[8], transgc[e][d][3], transgc[e][d][4], + bxmin, bxmax, bymin, bymax, bzmin, bzmax); + p[2] = hilbert_split(vertexarray, p[4], transgc[e][d][1], transgc[e][d][2], + bxmin, bxmax, bymin, bymax, bzmin, bzmax); + p[1] = hilbert_split(vertexarray, p[2], transgc[e][d][0], transgc[e][d][1], + bxmin, bxmax, bymin, bymax, bzmin, bzmax); + p[3] = hilbert_split(&(vertexarray[p[2]]), p[4] - p[2], + transgc[e][d][2], transgc[e][d][3], + bxmin, bxmax, bymin, bymax, bzmin, bzmax) + p[2]; + p[6] = hilbert_split(&(vertexarray[p[4]]), p[8] - p[4], + transgc[e][d][5], transgc[e][d][6], + bxmin, bxmax, bymin, bymax, bzmin, bzmax) + p[4]; + p[5] = hilbert_split(&(vertexarray[p[4]]), p[6] - p[4], + transgc[e][d][4], transgc[e][d][5], + bxmin, bxmax, bymin, bymax, bzmin, bzmax) + p[4]; + p[7] = hilbert_split(&(vertexarray[p[6]]), p[8] - p[6], + transgc[e][d][6], transgc[e][d][7], + bxmin, bxmax, bymin, bymax, bzmin, bzmax) + p[6]; + + if (b->hilbert_order > 0) { + // A maximum order is prescribed. + if ((depth + 1) == b->hilbert_order) { + // The maximum prescribed order is reached. + return; + } + } + + // Recursively sort the points in sub-boxes. + for (w = 0; w < 8; w++) { + // w is the local Hilbert index (NOT Gray code). + // Sort into the sub-box either there are more than 2 points in it, or + // the prescribed order of the curve is not reached yet. + //if ((p[w+1] - p[w] > b->hilbert_limit) || (b->hilbert_order > 0)) { + if ((p[w+1] - p[w]) > b->hilbert_limit) { + // Calculcate the start point (ei) of the curve in this sub-box. + // update e = e ^ (e(w) left_rotate (d+1)). + if (w == 0) { + e_w = 0; + } else { + // calculate e(w) = gc(2 * floor((w - 1) / 2)). + k = 2 * ((w - 1) / 2); + e_w = k ^ (k >> 1); // = gc(k). + } + k = e_w; + e_w = ((k << (d+1)) & mask) | ((k >> (n-d-1)) & mask); + ei = e ^ e_w; + // Calulcate the direction (di) of the curve in this sub-box. + // update d = (d + d(w) + 1) % n + if (w == 0) { + d_w = 0; + } else { + d_w = ((w % 2) == 0) ? tsb1mod3[w - 1] : tsb1mod3[w]; + } + di = (d + d_w + 1) % n; + // Calculate the bounding box of the sub-box. + if (transgc[e][d][w] & 1) { // x-axis + x1 = 0.5 * (bxmin + bxmax); + x2 = bxmax; + } else { + x1 = bxmin; + x2 = 0.5 * (bxmin + bxmax); + } + if (transgc[e][d][w] & 2) { // y-axis + y1 = 0.5 * (bymin + bymax); + y2 = bymax; + } else { + y1 = bymin; + y2 = 0.5 * (bymin + bymax); + } + if (transgc[e][d][w] & 4) { // z-axis + z1 = 0.5 * (bzmin + bzmax); + z2 = bzmax; + } else { + z1 = bzmin; + z2 = 0.5 * (bzmin + bzmax); + } + hilbert_sort3(&(vertexarray[p[w]]), p[w+1] - p[w], ei, di, + x1, x2, y1, y2, z1, z2, depth+1); + } // if (p[w+1] - p[w] > 1) + } // w +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// brio_multiscale_sort() Sort the points using BRIO and Hilbert curve. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::brio_multiscale_sort(point* vertexarray, int arraysize, + int threshold, REAL ratio, int *depth) +{ + int middle; + + middle = 0; + if (arraysize >= threshold) { + (*depth)++; + middle = arraysize * ratio; + brio_multiscale_sort(vertexarray, middle, threshold, ratio, depth); + } + // Sort the right-array (rnd-th round) using the Hilbert curve. + hilbert_sort3(&(vertexarray[middle]), arraysize - middle, 0, 0, // e, d + xmin, xmax, ymin, ymax, zmin, zmax, 0); // depth. +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// randomnation() Generate a random number between 0 and 'choices' - 1. // +// // +/////////////////////////////////////////////////////////////////////////////// + +unsigned long tetgenmesh::randomnation(unsigned int choices) +{ + unsigned long newrandom; + + if (choices >= 714025l) { + newrandom = (randomseed * 1366l + 150889l) % 714025l; + randomseed = (newrandom * 1366l + 150889l) % 714025l; + newrandom = newrandom * (choices / 714025l) + randomseed; + if (newrandom >= choices) { + return newrandom - choices; + } else { + return newrandom; + } + } else { + randomseed = (randomseed * 1366l + 150889l) % 714025l; + return randomseed % choices; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// randomsample() Randomly sample the tetrahedra for point loation. // +// // +// Searching begins from one of handles: the input 'searchtet', a recently // +// encountered tetrahedron 'recenttet', or from one chosen from a random // +// sample. The choice is made by determining which one's origin is closest // +// to the point we are searching for. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::randomsample(point searchpt,triface *searchtet) +{ + tetrahedron *firsttet, *tetptr; + point torg; + void **sampleblock; + uintptr_t alignptr; + long sampleblocks, samplesperblock, samplenum; + long tetblocks, i, j; + REAL searchdist, dist; + + if (b->verbose > 2) { + printf(" Random sampling tetrahedra for searching point %d.\n", + pointmark(searchpt)); + } + + if (!nonconvex) { + if (searchtet->tet == NULL) { + // A null tet. Choose the recenttet as the starting tet. + *searchtet = recenttet; + } + + // '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'. + searchdist = longest; + } + + // Select "good" candidate using k random samples, taking the closest one. + // The number of random samples taken is proportional to the fourth root + // of the number of tetrahedra in the mesh. + while (samples * samples * samples * samples < tetrahedrons->items) { + samples++; + } + // Find how much blocks in current tet pool. + tetblocks = (tetrahedrons->maxitems + b->tetrahedraperblock - 1) + / b->tetrahedraperblock; + // Find the average samples per block. Each block at least have 1 sample. + samplesperblock = 1 + (samples / tetblocks); + sampleblocks = samples / samplesperblock; + sampleblock = tetrahedrons->firstblock; + for (i = 0; i < sampleblocks; i++) { + alignptr = (uintptr_t) (sampleblock + 1); + firsttet = (tetrahedron *) + (alignptr + (uintptr_t) tetrahedrons->alignbytes + - (alignptr % (uintptr_t) tetrahedrons->alignbytes)); + for (j = 0; j < samplesperblock; j++) { + if (i == tetblocks - 1) { + // This is the last block. + samplenum = randomnation((int) + (tetrahedrons->maxitems - (i * b->tetrahedraperblock))); + } else { + samplenum = randomnation(b->tetrahedraperblock); + } + tetptr = (tetrahedron *) + (firsttet + (samplenum * tetrahedrons->itemwords)); + torg = (point) tetptr[4]; + if (torg != (point) NULL) { + dist = (searchpt[0] - torg[0]) * (searchpt[0] - torg[0]) + + (searchpt[1] - torg[1]) * (searchpt[1] - torg[1]) + + (searchpt[2] - torg[2]) * (searchpt[2] - torg[2]); + if (dist < searchdist) { + searchtet->tet = tetptr; + searchtet->ver = 11; // torg = org(t); + searchdist = dist; + } + } else { + // A dead tet. Re-sample it. + if (i != tetblocks - 1) j--; + } + } + sampleblock = (void **) *sampleblock; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// locate() Find a tetrahedron containing a given point. // +// // +// Begins its search from 'searchtet', assume there is a line segment L from // +// a vertex of 'searchtet' to the query point 'searchpt', and simply walk // +// towards 'searchpt' by traversing all faces intersected by L. // +// // +// On completion, 'searchtet' is a tetrahedron that contains 'searchpt'. The // +// returned value indicates one of the following cases: // +// - ONVERTEX, the search point lies on the origin of 'searchtet'. // +// - ONEDGE, the search point lies on an edge of 'searchtet'. // +// - ONFACE, the search point lies on a face of 'searchtet'. // +// - INTET, the search point lies in the interior of 'searchtet'. // +// - OUTSIDE, the search point lies outside the mesh. 'searchtet' is a // +// hull face which is visible by the search point. // +// // +// WARNING: This routine is designed for convex triangulations, and will not // +// generally work after the holes and concavities have been carved. // +// // +/////////////////////////////////////////////////////////////////////////////// + +enum tetgenmesh::locateresult + tetgenmesh::locate(point searchpt, triface* searchtet, int chkencflag) +{ + point torg, tdest, tapex, toppo; + enum {ORGMOVE, DESTMOVE, APEXMOVE} nextmove; + REAL ori, oriorg, oridest, oriapex; + enum locateresult loc = OUTSIDE; + int t1ver; + int s; + + if (searchtet->tet == NULL) { + // A null tet. Choose the recenttet as the starting tet. + searchtet->tet = recenttet.tet; + } + + // Check if we are in the outside of the convex hull. + if (ishulltet(*searchtet)) { + // Get its adjacent tet (inside the hull). + searchtet->ver = 3; + fsymself(*searchtet); + } + + // Let searchtet be the face such that 'searchpt' lies above to it. + for (searchtet->ver = 0; searchtet->ver < 4; searchtet->ver++) { + torg = org(*searchtet); + tdest = dest(*searchtet); + tapex = apex(*searchtet); + ori = orient3d(torg, tdest, tapex, searchpt); + if (ori < 0.0) break; + } + if (searchtet->ver == 4) { + terminatetetgen(this, 2); + } + + // Walk through tetrahedra to locate the point. + while (true) { + + toppo = oppo(*searchtet); + + // Check if the vertex is we seek. + if (toppo == searchpt) { + // Adjust the origin of searchtet to be searchpt. + esymself(*searchtet); + eprevself(*searchtet); + loc = ONVERTEX; // return ONVERTEX; + break; + } + + // We enter from one of serarchtet's faces, which face do we exit? + oriorg = orient3d(tdest, tapex, toppo, searchpt); + oridest = orient3d(tapex, torg, toppo, searchpt); + oriapex = orient3d(torg, tdest, toppo, searchpt); + + // Now decide which face to move. It is possible there are more than one + // faces are viable moves. If so, randomly choose one. + if (oriorg < 0) { + if (oridest < 0) { + if (oriapex < 0) { + // All three faces are possible. + s = randomnation(3); // 's' is in {0,1,2}. + if (s == 0) { + nextmove = ORGMOVE; + } else if (s == 1) { + nextmove = DESTMOVE; + } else { + nextmove = APEXMOVE; + } + } else { + // Two faces, opposite to origin and destination, are viable. + //s = randomnation(2); // 's' is in {0,1}. + if (randomnation(2)) { + nextmove = ORGMOVE; + } else { + nextmove = DESTMOVE; + } + } + } else { + if (oriapex < 0) { + // Two faces, opposite to origin and apex, are viable. + //s = randomnation(2); // 's' is in {0,1}. + if (randomnation(2)) { + nextmove = ORGMOVE; + } else { + nextmove = APEXMOVE; + } + } else { + // Only the face opposite to origin is viable. + nextmove = ORGMOVE; + } + } + } else { + if (oridest < 0) { + if (oriapex < 0) { + // Two faces, opposite to destination and apex, are viable. + //s = randomnation(2); // 's' is in {0,1}. + if (randomnation(2)) { + nextmove = DESTMOVE; + } else { + nextmove = APEXMOVE; + } + } else { + // Only the face opposite to destination is viable. + nextmove = DESTMOVE; + } + } else { + if (oriapex < 0) { + // Only the face opposite to apex is viable. + nextmove = APEXMOVE; + } else { + // The point we seek must be on the boundary of or inside this + // tetrahedron. Check for boundary cases. + if (oriorg == 0) { + // Go to the face opposite to origin. + enextesymself(*searchtet); + if (oridest == 0) { + eprevself(*searchtet); // edge oppo->apex + if (oriapex == 0) { + // oppo is duplicated with p. + loc = ONVERTEX; // return ONVERTEX; + break; + } + loc = ONEDGE; // return ONEDGE; + break; + } + if (oriapex == 0) { + enextself(*searchtet); // edge dest->oppo + loc = ONEDGE; // return ONEDGE; + break; + } + loc = ONFACE; // return ONFACE; + break; + } + if (oridest == 0) { + // Go to the face opposite to destination. + eprevesymself(*searchtet); + if (oriapex == 0) { + eprevself(*searchtet); // edge oppo->org + loc = ONEDGE; // return ONEDGE; + break; + } + loc = ONFACE; // return ONFACE; + break; + } + if (oriapex == 0) { + // Go to the face opposite to apex + esymself(*searchtet); + loc = ONFACE; // return ONFACE; + break; + } + loc = INTETRAHEDRON; // return INTETRAHEDRON; + break; + } + } + } + + // Move to the selected face. + if (nextmove == ORGMOVE) { + enextesymself(*searchtet); + } else if (nextmove == DESTMOVE) { + eprevesymself(*searchtet); + } else { + esymself(*searchtet); + } + if (chkencflag) { + // Check if we are walking across a subface. + if (issubface(*searchtet)) { + loc = ENCSUBFACE; + break; + } + } + // Move to the adjacent tetrahedron (maybe a hull tetrahedron). + fsymself(*searchtet); + if (oppo(*searchtet) == dummypoint) { + loc = OUTSIDE; // return OUTSIDE; + break; + } + + // Retreat the three vertices of the base face. + torg = org(*searchtet); + tdest = dest(*searchtet); + tapex = apex(*searchtet); + + } // while (true) + + return loc; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// flippush() Push a face (possibly will be flipped) into flipstack. // +// // +// The face is marked. The flag is used to check the validity of the face on // +// its popup. Some other flips may change it already. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::flippush(badface*& fstack, triface* flipface) +{ + if (!facemarked(*flipface)) { + badface *newflipface = (badface *) flippool->alloc(); + newflipface->tt = *flipface; + markface(newflipface->tt); + // Push this face into stack. + newflipface->nextitem = fstack; + fstack = newflipface; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// incrementalflip() Incrementally flipping to construct DT. // +// // +// Faces need to be checked for flipping are already queued in 'flipstack'. // +// Return the total number of performed flips. // +// // +// Comment: This routine should be only used in the incremental Delaunay // +// construction. In other cases, lawsonflip3d() should be used. // +// // +// If the new point lies outside of the convex hull ('hullflag' is set). The // +// incremental flip algorithm still works as usual. However, we must ensure // +// that every flip (2-to-3 or 3-to-2) does not create a duplicated (existing)// +// edge or face. Otherwise, the underlying space of the triangulation becomes// +// non-manifold and it is not possible to flip further. // +// Thanks to Joerg Rambau and Frank Lutz for helping in this issue. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::incrementalflip(point newpt, int hullflag, flipconstraints *fc) +{ + badface *popface; + triface fliptets[5], *parytet; + point *pts, *parypt, pe; + REAL sign, ori; + int flipcount = 0; + int t1ver; + int i; + + if (b->verbose > 2) { + printf(" Lawson flip (%ld faces).\n", flippool->items); + } + + if (hullflag) { + // 'newpt' lies in the outside of the convex hull. + // Mark all hull vertices which are connecting to it. + popface = flipstack; + while (popface != NULL) { + pts = (point *) popface->tt.tet; + for (i = 4; i < 8; i++) { + if ((pts[i] != newpt) && (pts[i] != dummypoint)) { + if (!pinfected(pts[i])) { + pinfect(pts[i]); + cavetetvertlist->newindex((void **) &parypt); + *parypt = pts[i]; + } + } + } + popface = popface->nextitem; + } + } + + // Loop until the queue is empty. + while (flipstack != NULL) { + + // Pop a face from the stack. + popface = flipstack; + fliptets[0] = popface->tt; + flipstack = flipstack->nextitem; // The next top item in stack. + flippool->dealloc((void *) popface); + + // Skip it if it is a dead tet (destroyed by previous flips). + if (isdeadtet(fliptets[0])) continue; + // Skip it if it is not the same tet as we saved. + if (!facemarked(fliptets[0])) continue; + + unmarkface(fliptets[0]); + + if ((point) fliptets[0].tet[7] == dummypoint) { + // It must be a hull edge. + fliptets[0].ver = epivot[fliptets[0].ver]; + // A hull edge. The current convex hull may be enlarged. + fsym(fliptets[0], fliptets[1]); + pts = (point *) fliptets[1].tet; + ori = orient3d(pts[4], pts[5], pts[6], newpt); + if (ori < 0) { + // Visible. The convex hull will be enlarged. + // Decide which flip (2-to-3, 3-to-2, or 4-to-1) to use. + // Check if the tet [a,c,e,d] or [c,b,e,d] exists. + enext(fliptets[1], fliptets[2]); + eprev(fliptets[1], fliptets[3]); + fnextself(fliptets[2]); // [a,c,e,*] + fnextself(fliptets[3]); // [c,b,e,*] + if (oppo(fliptets[2]) == newpt) { + if (oppo(fliptets[3]) == newpt) { + // Both tets exist! A 4-to-1 flip is found. + terminatetetgen(this, 2); // Report a bug. + } else { + esym(fliptets[2], fliptets[0]); + fnext(fliptets[0], fliptets[1]); + fnext(fliptets[1], fliptets[2]); + // Perform a 3-to-2 flip. Replace edge [c,a] by face [d,e,b]. + // This corresponds to my standard labels, where edge [e,d] is + // repalced by face [a,b,c], and a is the new vertex. + // [0] [c,a,d,e] (d = newpt) + // [1] [c,a,e,b] (c = dummypoint) + // [2] [c,a,b,d] + flip32(fliptets, 1, fc); + } + } else { + if (oppo(fliptets[3]) == newpt) { + fnext(fliptets[3], fliptets[0]); + fnext(fliptets[0], fliptets[1]); + fnext(fliptets[1], fliptets[2]); + // Perform a 3-to-2 flip. Replace edge [c,b] by face [d,a,e]. + // [0] [c,b,d,a] (d = newpt) + // [1] [c,b,a,e] (c = dummypoint) + // [2] [c,b,e,d] + flip32(fliptets, 1, fc); + } else { + if (hullflag) { + // Reject this flip if pe is already marked. + pe = oppo(fliptets[1]); + if (!pinfected(pe)) { + pinfect(pe); + cavetetvertlist->newindex((void **) &parypt); + *parypt = pe; + // Perform a 2-to-3 flip. + flip23(fliptets, 1, fc); + } else { + // Reject this flip. + flipcount--; + } + } else { + // Perform a 2-to-3 flip. Replace face [a,b,c] by edge [e,d]. + // [0] [a,b,c,d], d = newpt. + // [1] [b,a,c,e], c = dummypoint. + flip23(fliptets, 1, fc); + } + } + } + flipcount++; + } + continue; + } // if (dummypoint) + + fsym(fliptets[0], fliptets[1]); + if ((point) fliptets[1].tet[7] == dummypoint) { + // A hull face is locally Delaunay. + continue; + } + // Check if the adjacent tet has already been tested. + if (marktested(fliptets[1])) { + // It has been tested and it is Delaunay. + continue; + } + + // Test whether the face is locally Delaunay or not. + pts = (point *) fliptets[1].tet; + if (b->weighted) { + sign = orient4d_s(pts[4], pts[5], pts[6], pts[7], newpt, + pts[4][3], pts[5][3], pts[6][3], pts[7][3], + newpt[3]); + } else { + sign = insphere_s(pts[4], pts[5], pts[6], pts[7], newpt); + } + + + if (sign < 0) { + point pd = newpt; + point pe = oppo(fliptets[1]); + // Check the convexity of its three edges. Stop checking either a + // locally non-convex edge (ori < 0) or a flat edge (ori = 0) is + // encountered, and 'fliptet' represents that edge. + for (i = 0; i < 3; i++) { + ori = orient3d(org(fliptets[0]), dest(fliptets[0]), pd, pe); + if (ori <= 0) break; + enextself(fliptets[0]); + } + if (ori > 0) { + // A 2-to-3 flip is found. + // [0] [a,b,c,d], + // [1] [b,a,c,e]. no dummypoint. + flip23(fliptets, 0, fc); + flipcount++; + } else { // ori <= 0 + // The edge ('fliptets[0]' = [a',b',c',d]) is non-convex or flat, + // where the edge [a',b'] is one of [a,b], [b,c], and [c,a]. + // Check if there are three or four tets sharing at this edge. + esymself(fliptets[0]); // [b,a,d,c] + for (i = 0; i < 3; i++) { + fnext(fliptets[i], fliptets[i+1]); + } + if (fliptets[3].tet == fliptets[0].tet) { + // A 3-to-2 flip is found. (No hull tet.) + flip32(fliptets, 0, fc); + flipcount++; + } else { + // There are more than 3 tets at this edge. + fnext(fliptets[3], fliptets[4]); + if (fliptets[4].tet == fliptets[0].tet) { + if (ori == 0) { + // A 4-to-4 flip is found. (Two hull tets may be involved.) + // Current tets in 'fliptets': + // [0] [b,a,d,c] (d may be newpt) + // [1] [b,a,c,e] + // [2] [b,a,e,f] (f may be dummypoint) + // [3] [b,a,f,d] + esymself(fliptets[0]); // [a,b,c,d] + // A 2-to-3 flip replaces face [a,b,c] by edge [e,d]. + // This creates a degenerate tet [e,d,a,b] (tmpfliptets[0]). + // It will be removed by the followed 3-to-2 flip. + flip23(fliptets, 0, fc); // No hull tet. + fnext(fliptets[3], fliptets[1]); + fnext(fliptets[1], fliptets[2]); + // Current tets in 'fliptets': + // [0] [...] + // [1] [b,a,d,e] (degenerated, d may be new point). + // [2] [b,a,e,f] (f may be dummypoint) + // [3] [b,a,f,d] + // A 3-to-2 flip replaces edge [b,a] by face [d,e,f]. + // Hull tets may be involved (f may be dummypoint). + flip32(&(fliptets[1]), (apex(fliptets[3]) == dummypoint), fc); + flipcount++; + } + } + } + } // ori + } else { + // The adjacent tet is Delaunay. Mark it to avoid testing it again. + marktest(fliptets[1]); + // Save it for unmarking it later. + cavebdrylist->newindex((void **) &parytet); + *parytet = fliptets[1]; + } + + } // while (flipstack) + + // Unmark saved tetrahedra. + for (i = 0; i < cavebdrylist->objects; i++) { + parytet = (triface *) fastlookup(cavebdrylist, i); + unmarktest(*parytet); + } + cavebdrylist->restart(); + + if (hullflag) { + // Unmark infected vertices. + for (i = 0; i < cavetetvertlist->objects; i++) { + parypt = (point *) fastlookup(cavetetvertlist, i); + puninfect(*parypt); + } + cavetetvertlist->restart(); + } + + + return flipcount; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// initialdelaunay() Create an initial Delaunay tetrahedralization. // +// // +// The tetrahedralization contains only one tetrahedron abcd, and four hull // +// tetrahedra. The points pa, pb, pc, and pd must be linearly independent. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::initialdelaunay(point pa, point pb, point pc, point pd) +{ + triface firsttet, tetopa, tetopb, tetopc, tetopd; + triface worktet, worktet1; + + if (b->verbose > 2) { + printf(" Create init tet (%d, %d, %d, %d)\n", pointmark(pa), + pointmark(pb), pointmark(pc), pointmark(pd)); + } + + // Create the first tetrahedron. + maketetrahedron(&firsttet); + setvertices(firsttet, pa, pb, pc, pd); + // Create four hull tetrahedra. + maketetrahedron(&tetopa); + setvertices(tetopa, pb, pc, pd, dummypoint); + maketetrahedron(&tetopb); + setvertices(tetopb, pc, pa, pd, dummypoint); + maketetrahedron(&tetopc); + setvertices(tetopc, pa, pb, pd, dummypoint); + maketetrahedron(&tetopd); + setvertices(tetopd, pb, pa, pc, dummypoint); + hullsize += 4; + + // Connect hull tetrahedra to firsttet (at four faces of firsttet). + bond(firsttet, tetopd); + esym(firsttet, worktet); + bond(worktet, tetopc); // ab + enextesym(firsttet, worktet); + bond(worktet, tetopa); // bc + eprevesym(firsttet, worktet); + bond(worktet, tetopb); // ca + + // Connect hull tetrahedra together (at six edges of firsttet). + esym(tetopc, worktet); + esym(tetopd, worktet1); + bond(worktet, worktet1); // ab + esym(tetopa, worktet); + eprevesym(tetopd, worktet1); + bond(worktet, worktet1); // bc + esym(tetopb, worktet); + enextesym(tetopd, worktet1); + bond(worktet, worktet1); // ca + eprevesym(tetopc, worktet); + enextesym(tetopb, worktet1); + bond(worktet, worktet1); // da + eprevesym(tetopa, worktet); + enextesym(tetopc, worktet1); + bond(worktet, worktet1); // db + eprevesym(tetopb, worktet); + enextesym(tetopa, worktet1); + bond(worktet, worktet1); // dc + + // Set the vertex type. + if (pointtype(pa) == UNUSEDVERTEX) { + setpointtype(pa, VOLVERTEX); + } + if (pointtype(pb) == UNUSEDVERTEX) { + setpointtype(pb, VOLVERTEX); + } + if (pointtype(pc) == UNUSEDVERTEX) { + setpointtype(pc, VOLVERTEX); + } + if (pointtype(pd) == UNUSEDVERTEX) { + setpointtype(pd, VOLVERTEX); + } + + setpoint2tet(pa, encode(firsttet)); + setpoint2tet(pb, encode(firsttet)); + setpoint2tet(pc, encode(firsttet)); + setpoint2tet(pd, encode(firsttet)); + + // Remember the first tetrahedron. + recenttet = firsttet; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// incrementaldelaunay() Create a Delaunay tetrahedralization by // +// the incremental approach. // +// // +/////////////////////////////////////////////////////////////////////////////// + + +void tetgenmesh::incrementaldelaunay(clock_t& tv) +{ + triface searchtet; + point *permutarray, swapvertex; + REAL v1[3], v2[3], n[3]; + REAL bboxsize, bboxsize2, bboxsize3, ori; + int randindex; + int ngroup = 0; + int i, j; + + if (!b->quiet) { + printf("Delaunizing vertices...\n"); + } + + // Form a random permuation (uniformly at random) of the set of vertices. + permutarray = new point[in->numberofpoints]; + points->traversalinit(); + + if (b->no_sort) { + if (b->verbose) { + printf(" Using the input order.\n"); + } + for (i = 0; i < in->numberofpoints; i++) { + permutarray[i] = (point) points->traverse(); + } + } else { + if (b->verbose) { + printf(" Permuting vertices.\n"); + } + srand(in->numberofpoints); + for (i = 0; i < in->numberofpoints; i++) { + randindex = rand() % (i + 1); // randomnation(i + 1); + permutarray[i] = permutarray[randindex]; + permutarray[randindex] = (point) points->traverse(); + } + if (b->brio_hilbert) { // -b option + if (b->verbose) { + printf(" Sorting vertices.\n"); + } + hilbert_init(in->mesh_dim); + brio_multiscale_sort(permutarray, in->numberofpoints, b->brio_threshold, + b->brio_ratio, &ngroup); + } + } + + tv = clock(); // Remember the time for sorting points. + + // Calculate the diagonal size of its bounding box. + bboxsize = sqrt(norm2(xmax - xmin, ymax - ymin, zmax - zmin)); + bboxsize2 = bboxsize * bboxsize; + bboxsize3 = bboxsize2 * bboxsize; + + // Make sure the second vertex is not identical with the first one. + i = 1; + while ((distance(permutarray[0],permutarray[i])/bboxsize)<b->epsilon) { + i++; + if (i == in->numberofpoints - 1) { + printf("Exception: All vertices are (nearly) identical (Tol = %g).\n", + b->epsilon); + terminatetetgen(this, 10); + } + } + if (i > 1) { + // Swap to move the non-identical vertex from index i to index 1. + swapvertex = permutarray[i]; + permutarray[i] = permutarray[1]; + permutarray[1] = swapvertex; + } + + // Make sure the third vertex is not collinear with the first two. + // Acknowledgement: Thanks Jan Pomplun for his correction by using + // epsilon^2 and epsilon^3 (instead of epsilon). 2013-08-15. + i = 2; + for (j = 0; j < 3; j++) { + v1[j] = permutarray[1][j] - permutarray[0][j]; + v2[j] = permutarray[i][j] - permutarray[0][j]; + } + cross(v1, v2, n); + while ((sqrt(norm2(n[0], n[1], n[2])) / bboxsize2) < + (b->epsilon * b->epsilon)) { + i++; + if (i == in->numberofpoints - 1) { + printf("Exception: All vertices are (nearly) collinear (Tol = %g).\n", + b->epsilon); + terminatetetgen(this, 10); + } + for (j = 0; j < 3; j++) { + v2[j] = permutarray[i][j] - permutarray[0][j]; + } + cross(v1, v2, n); + } + if (i > 2) { + // Swap to move the non-identical vertex from index i to index 1. + swapvertex = permutarray[i]; + permutarray[i] = permutarray[2]; + permutarray[2] = swapvertex; + } + + // Make sure the fourth vertex is not coplanar with the first three. + i = 3; + ori = orient3dfast(permutarray[0], permutarray[1], permutarray[2], + permutarray[i]); + while ((fabs(ori) / bboxsize3) < (b->epsilon * b->epsilon * b->epsilon)) { + i++; + if (i == in->numberofpoints) { + printf("Exception: All vertices are coplanar (Tol = %g).\n", + b->epsilon); + terminatetetgen(this, 10); + } + ori = orient3dfast(permutarray[0], permutarray[1], permutarray[2], + permutarray[i]); + } + if (i > 3) { + // Swap to move the non-identical vertex from index i to index 1. + swapvertex = permutarray[i]; + permutarray[i] = permutarray[3]; + permutarray[3] = swapvertex; + } + + // Orient the first four vertices in permutarray so that they follow the + // right-hand rule. + if (ori > 0.0) { + // Swap the first two vertices. + swapvertex = permutarray[0]; + permutarray[0] = permutarray[1]; + permutarray[1] = swapvertex; + } + + // Create the initial Delaunay tetrahedralization. + initialdelaunay(permutarray[0], permutarray[1], permutarray[2], + permutarray[3]); + + if (b->verbose) { + printf(" Incrementally inserting vertices.\n"); + } + insertvertexflags ivf; + flipconstraints fc; + + // Choose algorithm: Bowyer-Watson (default) or Incremental Flip + if (b->incrflip) { + ivf.bowywat = 0; + ivf.lawson = 1; + fc.enqflag = 1; + } else { + ivf.bowywat = 1; + ivf.lawson = 0; + } + + + for (i = 4; i < in->numberofpoints; i++) { + if (pointtype(permutarray[i]) == UNUSEDVERTEX) { + setpointtype(permutarray[i], VOLVERTEX); + } + if (b->brio_hilbert || b->no_sort) { // -b or -b/1 + // Start the last updated tet. + searchtet.tet = recenttet.tet; + } else { // -b0 + // Randomly choose the starting tet for point location. + searchtet.tet = NULL; + } + ivf.iloc = (int) OUTSIDE; + // Insert the vertex. + if (insertpoint(permutarray[i], &searchtet, NULL, NULL, &ivf)) { + if (flipstack != NULL) { + // Perform flip to recover Delaunayness. + incrementalflip(permutarray[i], (ivf.iloc == (int) OUTSIDE), &fc); + } + } else { + if (ivf.iloc == (int) ONVERTEX) { + // The point already exists. Mark it and do nothing on it. + swapvertex = org(searchtet); + 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 = org(searchtet); + 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], 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. + setpoint2ppt(permutarray[i], swapvertex); + setpointtype(permutarray[i], DUPLICATEDVERTEX); + dupverts++; + } else if (ivf.iloc == (int) NONREGULAR) { + // The point is non-regular. Skipped. + if (b->verbose) { + printf(" Point #%d is non-regular, skipped.\n", + pointmark(permutarray[i])); + } + setpointtype(permutarray[i], NREGULARVERTEX); + nonregularcount++; + } + } + } + + + + delete [] permutarray; +} + +//// //// +//// //// +//// delaunay_cxx ///////////////////////////////////////////////////////////// + +//// surface_cxx ////////////////////////////////////////////////////////////// +//// //// +//// //// + +/////////////////////////////////////////////////////////////////////////////// +// // +// flipshpush() Push a facet edge into flip stack. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::flipshpush(face* flipedge) +{ + badface *newflipface; + + newflipface = (badface *) flippool->alloc(); + newflipface->ss = *flipedge; + newflipface->forg = sorg(*flipedge); + newflipface->fdest = sdest(*flipedge); + newflipface->nextitem = flipstack; + flipstack = newflipface; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// flip22() Perform a 2-to-2 flip in surface mesh. // +// // +// 'flipfaces' is an array of two subfaces. On input, they are [a,b,c] and // +// [b,a,d]. On output, they are [c,d,b] and [d,c,a]. As a result, edge [a,b] // +// is replaced by edge [c,d]. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::flip22(face* flipfaces, int flipflag, int chkencflag) +{ + face bdedges[4], outfaces[4], infaces[4]; + face bdsegs[4]; + face checkface; + point pa, pb, pc, pd; + int i; + + pa = sorg(flipfaces[0]); + pb = sdest(flipfaces[0]); + pc = sapex(flipfaces[0]); + pd = sapex(flipfaces[1]); + + if (sorg(flipfaces[1]) != pb) { + sesymself(flipfaces[1]); + } + + flip22count++; + + // Collect the four boundary edges. + senext(flipfaces[0], bdedges[0]); + senext2(flipfaces[0], bdedges[1]); + senext(flipfaces[1], bdedges[2]); + senext2(flipfaces[1], bdedges[3]); + + // Collect outer boundary faces. + for (i = 0; i < 4; i++) { + spivot(bdedges[i], outfaces[i]); + infaces[i] = outfaces[i]; + sspivot(bdedges[i], bdsegs[i]); + if (outfaces[i].sh != NULL) { + if (isshsubseg(bdedges[i])) { + spivot(infaces[i], checkface); + while (checkface.sh != bdedges[i].sh) { + infaces[i] = checkface; + spivot(infaces[i], checkface); + } + } + } + } + + // The flags set in these two subfaces do not change. + // Shellmark does not change. + // area constraint does not change. + + // Transform [a,b,c] -> [c,d,b]. + setshvertices(flipfaces[0], pc, pd, pb); + // Transform [b,a,d] -> [d,c,a]. + setshvertices(flipfaces[1], pd, pc, pa); + + // Update the point-to-subface map. + if (pointtype(pa) == FREEFACETVERTEX) { + setpoint2sh(pa, sencode(flipfaces[1])); + } + if (pointtype(pb) == FREEFACETVERTEX) { + setpoint2sh(pb, sencode(flipfaces[0])); + } + if (pointtype(pc) == FREEFACETVERTEX) { + setpoint2sh(pc, sencode(flipfaces[0])); + } + if (pointtype(pd) == FREEFACETVERTEX) { + setpoint2sh(pd, sencode(flipfaces[0])); + } + + // Reconnect boundary edges to outer boundary faces. + for (i = 0; i < 4; i++) { + if (outfaces[(3 + i) % 4].sh != NULL) { + // Make sure that the subface has the ori as the segment. + if (bdsegs[(3 + i) % 4].sh != NULL) { + bdsegs[(3 + i) % 4].shver = 0; + if (sorg(bdedges[i]) != sorg(bdsegs[(3 + i) % 4])) { + sesymself(bdedges[i]); + } + } + sbond1(bdedges[i], outfaces[(3 + i) % 4]); + sbond1(infaces[(3 + i) % 4], bdedges[i]); + } else { + sdissolve(bdedges[i]); + } + if (bdsegs[(3 + i) % 4].sh != NULL) { + ssbond(bdedges[i], bdsegs[(3 + i) % 4]); + if (chkencflag & 1) { + // Queue this segment for encroaching check. + enqueuesubface(badsubsegs, &(bdsegs[(3 + i) % 4])); + } + } else { + ssdissolve(bdedges[i]); + } + } + + if (chkencflag & 2) { + // Queue the flipped subfaces for quality/encroaching checks. + for (i = 0; i < 2; i++) { + enqueuesubface(badsubfacs, &(flipfaces[i])); + } + } + + recentsh = flipfaces[0]; + + if (flipflag) { + // Put the boundary edges into flip stack. + for (i = 0; i < 4; i++) { + flipshpush(&(bdedges[i])); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// flip31() Remove a vertex by transforming 3-to-1 subfaces. // +// // +// 'flipfaces' is an array of subfaces. Its length is at least 4. On input, // +// the first three faces are: [p,a,b], [p,b,c], and [p,c,a]. This routine // +// replaces them by one face [a,b,c], it is returned in flipfaces[3]. // +// // +// NOTE: The three old subfaces are not deleted within this routine. They // +// still hold pointers to their adjacent subfaces. These informations are // +// needed by the routine 'sremovevertex()' for recovering a segment. // +// The caller of this routine must delete the old subfaces after their uses. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::flip31(face* flipfaces, int flipflag) +{ + face bdedges[3], outfaces[3], infaces[3]; + face bdsegs[3]; + face checkface; + point pa, pb, pc; + int i; + + pa = sdest(flipfaces[0]); + pb = sdest(flipfaces[1]); + pc = sdest(flipfaces[2]); + + flip31count++; + + // Collect all infos at the three boundary edges. + for (i = 0; i < 3; i++) { + senext(flipfaces[i], bdedges[i]); + spivot(bdedges[i], outfaces[i]); + infaces[i] = outfaces[i]; + sspivot(bdedges[i], bdsegs[i]); + if (outfaces[i].sh != NULL) { + if (isshsubseg(bdedges[i])) { + spivot(infaces[i], checkface); + while (checkface.sh != bdedges[i].sh) { + infaces[i] = checkface; + spivot(infaces[i], checkface); + } + } + } + } // i + + // Create a new subface. + makeshellface(subfaces, &(flipfaces[3])); + setshvertices(flipfaces[3], pa, pb,pc); + setshellmark(flipfaces[3], shellmark(flipfaces[0])); + if (checkconstraints) { + //area = areabound(flipfaces[0]); + setareabound(flipfaces[3], areabound(flipfaces[0])); + } + if (useinsertradius) { + setfacetindex(flipfaces[3], getfacetindex(flipfaces[0])); + } + + // Update the point-to-subface map. + if (pointtype(pa) == FREEFACETVERTEX) { + setpoint2sh(pa, sencode(flipfaces[3])); + } + if (pointtype(pb) == FREEFACETVERTEX) { + setpoint2sh(pb, sencode(flipfaces[3])); + } + if (pointtype(pc) == FREEFACETVERTEX) { + setpoint2sh(pc, sencode(flipfaces[3])); + } + + // Update the three new boundary edges. + bdedges[0] = flipfaces[3]; // [a,b] + senext(flipfaces[3], bdedges[1]); // [b,c] + senext2(flipfaces[3], bdedges[2]); // [c,a] + + // Reconnect boundary edges to outer boundary faces. + for (i = 0; i < 3; i++) { + if (outfaces[i].sh != NULL) { + // Make sure that the subface has the ori as the segment. + if (bdsegs[i].sh != NULL) { + bdsegs[i].shver = 0; + if (sorg(bdedges[i]) != sorg(bdsegs[i])) { + sesymself(bdedges[i]); + } + } + sbond1(bdedges[i], outfaces[i]); + sbond1(infaces[i], bdedges[i]); + } + if (bdsegs[i].sh != NULL) { + ssbond(bdedges[i], bdsegs[i]); + } + } + + recentsh = flipfaces[3]; + + if (flipflag) { + // Put the boundary edges into flip stack. + for (i = 0; i < 3; i++) { + flipshpush(&(bdedges[i])); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// lawsonflip() Flip non-locally Delaunay edges. // +// // +/////////////////////////////////////////////////////////////////////////////// + +long tetgenmesh::lawsonflip() +{ + badface *popface; + face flipfaces[2]; + point pa, pb, pc, pd; + REAL sign; + long flipcount = 0; + + if (b->verbose > 2) { + printf(" Lawson flip %ld edges.\n", flippool->items); + } + + while (flipstack != (badface *) NULL) { + + // Pop an edge from the stack. + popface = flipstack; + flipfaces[0] = popface->ss; + pa = popface->forg; + pb = popface->fdest; + flipstack = popface->nextitem; // The next top item in stack. + flippool->dealloc((void *) popface); + + // Skip it if it is dead. + if (flipfaces[0].sh[3] == NULL) continue; + // Skip it if it is not the same edge as we saved. + if ((sorg(flipfaces[0]) != pa) || (sdest(flipfaces[0]) != pb)) continue; + // Skip it if it is a subsegment. + if (isshsubseg(flipfaces[0])) continue; + + // Get the adjacent face. + spivot(flipfaces[0], flipfaces[1]); + if (flipfaces[1].sh == NULL) continue; // Skip a hull edge. + pc = sapex(flipfaces[0]); + pd = sapex(flipfaces[1]); + + sign = incircle3d(pa, pb, pc, pd); + + if (sign < 0) { + // It is non-locally Delaunay. Flip it. + flip22(flipfaces, 1, 0); + flipcount++; + } + } + + if (b->verbose > 2) { + printf(" Performed %ld flips.\n", flipcount); + } + + return flipcount; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// sinsertvertex() Insert a vertex into a triangulation of a facet. // +// // +// This function uses three global arrays: 'caveshlist', 'caveshbdlist', and // +// 'caveshseglist'. On return, 'caveshlist' contains old subfaces in C(p), // +// 'caveshbdlist' contains new subfaces in C(p). If the new point lies on a // +// segment, 'cavesegshlist' returns the two new subsegments. // +// // +// 'iloc' suggests the location of the point. If it is OUTSIDE, this routine // +// will first locate the point. It starts searching from 'searchsh' or 'rec- // +// entsh' if 'searchsh' is NULL. // +// // +// If 'bowywat' is set (1), the Bowyer-Watson algorithm is used to insert // +// the vertex. Otherwise, only insert the vertex in the initial cavity. // +// // +// If 'iloc' is 'INSTAR', this means the cavity of this vertex was already // +// provided in the list 'caveshlist'. // +// // +// If 'splitseg' is not NULL, the new vertex lies on the segment and it will // +// be split. 'iloc' must be either 'ONEDGE' or 'INSTAR'. // +// // +// 'rflag' (rounding) is a parameter passed to slocate() function. If it is // +// set, after the location of the point is found, either ONEDGE or ONFACE, // +// round the result using an epsilon. // +// // +// NOTE: the old subfaces in C(p) are not deleted. They're needed in case we // +// want to remove the new point immediately. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::sinsertvertex(point insertpt, face *searchsh, face *splitseg, + int iloc, int bowywat, int rflag) +{ + face cavesh, neighsh, *parysh; + face newsh, casout, casin; + face checkseg; + point pa, pb; + enum locateresult loc = OUTSIDE; + REAL sign, ori; + int i, j; + + if (b->verbose > 2) { + printf(" Insert facet point %d.\n", pointmark(insertpt)); + } + + if (bowywat == 3) { + loc = INSTAR; + } + + if ((splitseg != NULL) && (splitseg->sh != NULL)) { + // A segment is going to be split, no point location. + spivot(*splitseg, *searchsh); + if (loc != INSTAR) loc = ONEDGE; + } else { + if (loc != INSTAR) loc = (enum locateresult) iloc; + if (loc == OUTSIDE) { + // Do point location in surface mesh. + if (searchsh->sh == NULL) { + *searchsh = recentsh; + } + // Search the vertex. An above point must be provided ('aflag' = 1). + loc = slocate(insertpt, searchsh, 1, 1, rflag); + } + } + + + // Form the initial sC(p). + if (loc == ONFACE) { + // Add the face into list (in B-W cavity). + smarktest(*searchsh); + caveshlist->newindex((void **) &parysh); + *parysh = *searchsh; + } else if (loc == ONEDGE) { + if ((splitseg != NULL) && (splitseg->sh != NULL)) { + splitseg->shver = 0; + pa = sorg(*splitseg); + } else { + pa = sorg(*searchsh); + } + if (searchsh->sh != NULL) { + // Collect all subfaces share at this edge. + neighsh = *searchsh; + while (1) { + // Adjust the origin of its edge to be 'pa'. + if (sorg(neighsh) != pa) sesymself(neighsh); + // Add this face into list (in B-W cavity). + smarktest(neighsh); + caveshlist->newindex((void **) &parysh); + *parysh = neighsh; + // Add this face into face-at-splitedge list. + cavesegshlist->newindex((void **) &parysh); + *parysh = neighsh; + // Go to the next face at the edge. + spivotself(neighsh); + // Stop if all faces at the edge have been visited. + if (neighsh.sh == searchsh->sh) break; + if (neighsh.sh == NULL) break; + } + } // If (not a non-dangling segment). + } else if (loc == ONVERTEX) { + return (int) loc; + } else if (loc == OUTSIDE) { + // Comment: This should only happen during the surface meshing step. + // Enlarge the convex hull of the triangulation by including p. + // An above point of the facet is set in 'dummypoint' to replace + // orient2d tests by orient3d tests. + // Imagine that the current edge a->b (in 'searchsh') is horizontal in a + // plane, and a->b is directed from left to right, p lies above a->b. + // Find the right-most edge of the triangulation which is visible by p. + neighsh = *searchsh; + while (1) { + senext2self(neighsh); + spivot(neighsh, casout); + if (casout.sh == NULL) { + // A convex hull edge. Is it visible by p. + ori = orient3d(sorg(neighsh), sdest(neighsh), dummypoint, insertpt); + if (ori < 0) { + *searchsh = neighsh; // Visible, update 'searchsh'. + } else { + break; // 'searchsh' is the right-most visible edge. + } + } else { + if (sorg(casout) != sdest(neighsh)) sesymself(casout); + neighsh = casout; + } + } + // Create new triangles for all visible edges of p (from right to left). + casin.sh = NULL; // No adjacent face at right. + pa = sorg(*searchsh); + pb = sdest(*searchsh); + while (1) { + // Create a new subface on top of the (visible) edge. + makeshellface(subfaces, &newsh); + setshvertices(newsh, pb, pa, insertpt); + setshellmark(newsh, shellmark(*searchsh)); + if (checkconstraints) { + //area = areabound(*searchsh); + setareabound(newsh, areabound(*searchsh)); + } + if (useinsertradius) { + setfacetindex(newsh, getfacetindex(*searchsh)); + } + // Connect the new subface to the bottom subfaces. + sbond1(newsh, *searchsh); + sbond1(*searchsh, newsh); + // Connect the new subface to its right-adjacent subface. + if (casin.sh != NULL) { + senext(newsh, casout); + sbond1(casout, casin); + sbond1(casin, casout); + } + // The left-adjacent subface has not been created yet. + senext2(newsh, casin); + // Add the new face into list (inside the B-W cavity). + smarktest(newsh); + caveshlist->newindex((void **) &parysh); + *parysh = newsh; + // Move to the convex hull edge at the left of 'searchsh'. + neighsh = *searchsh; + while (1) { + senextself(neighsh); + spivot(neighsh, casout); + if (casout.sh == NULL) { + *searchsh = neighsh; + break; + } + if (sorg(casout) != sdest(neighsh)) sesymself(casout); + neighsh = casout; + } + // A convex hull edge. Is it visible by p. + pa = sorg(*searchsh); + pb = sdest(*searchsh); + ori = orient3d(pa, pb, dummypoint, insertpt); + // Finish the process if p is not visible by the hull edge. + if (ori >= 0) break; + } + } else if (loc == INSTAR) { + // Under this case, the sub-cavity sC(p) has already been formed in + // insertvertex(). + } + + // Form the Bowyer-Watson cavity sC(p). + for (i = 0; i < caveshlist->objects; i++) { + cavesh = * (face *) fastlookup(caveshlist, i); + for (j = 0; j < 3; j++) { + if (!isshsubseg(cavesh)) { + spivot(cavesh, neighsh); + if (neighsh.sh != NULL) { + // The adjacent face exists. + if (!smarktested(neighsh)) { + if (bowywat) { + if (loc == INSTAR) { // if (bowywat > 2) { + // It must be a boundary edge. + sign = 1; + } else { + // Check if this subface is connected to adjacent tet(s). + if (!isshtet(neighsh)) { + // Check if the subface is non-Delaunay wrt. the new pt. + sign = incircle3d(sorg(neighsh), sdest(neighsh), + sapex(neighsh), insertpt); + } else { + // It is connected to an adjacent tet. A boundary edge. + sign = 1; + } + } + if (sign < 0) { + // Add the adjacent face in list (in B-W cavity). + smarktest(neighsh); + caveshlist->newindex((void **) &parysh); + *parysh = neighsh; + } + } else { + sign = 1; // A boundary edge. + } + } else { + sign = -1; // Not a boundary edge. + } + } else { + // No adjacent face. It is a hull edge. + if (loc == OUTSIDE) { + // It is a boundary edge if it does not contain p. + if ((sorg(cavesh) == insertpt) || (sdest(cavesh) == insertpt)) { + sign = -1; // Not a boundary edge. + } else { + sign = 1; // A boundary edge. + } + } else { + sign = 1; // A boundary edge. + } + } + } else { + // Do not across a segment. It is a boundary edge. + sign = 1; + } + if (sign >= 0) { + // Add a boundary edge. + caveshbdlist->newindex((void **) &parysh); + *parysh = cavesh; + } + senextself(cavesh); + } // j + } // i + + + // Creating new subfaces. + for (i = 0; i < caveshbdlist->objects; i++) { + parysh = (face *) fastlookup(caveshbdlist, i); + sspivot(*parysh, checkseg); + if ((parysh->shver & 01) != 0) sesymself(*parysh); + pa = sorg(*parysh); + pb = sdest(*parysh); + // Create a new subface. + makeshellface(subfaces, &newsh); + setshvertices(newsh, pa, pb, insertpt); + setshellmark(newsh, shellmark(*parysh)); + if (checkconstraints) { + //area = areabound(*parysh); + setareabound(newsh, areabound(*parysh)); + } + if (useinsertradius) { + setfacetindex(newsh, getfacetindex(*parysh)); + } + // Update the point-to-subface map. + if (pointtype(pa) == FREEFACETVERTEX) { + setpoint2sh(pa, sencode(newsh)); + } + if (pointtype(pb) == FREEFACETVERTEX) { + setpoint2sh(pb, sencode(newsh)); + } + // Connect newsh to outer subfaces. + spivot(*parysh, casout); + if (casout.sh != NULL) { + casin = casout; + if (checkseg.sh != NULL) { + // Make sure that newsh has the right ori at this segment. + checkseg.shver = 0; + if (sorg(newsh) != sorg(checkseg)) { + sesymself(newsh); + sesymself(*parysh); // This side should also be inverse. + } + spivot(casin, neighsh); + while (neighsh.sh != parysh->sh) { + casin = neighsh; + spivot(casin, neighsh); + } + } + sbond1(newsh, casout); + sbond1(casin, newsh); + } + if (checkseg.sh != NULL) { + ssbond(newsh, checkseg); + } + // Connect oldsh <== newsh (for connecting adjacent new subfaces). + // *parysh and newsh point to the same edge and the same ori. + sbond1(*parysh, newsh); + } + + if (newsh.sh != NULL) { + // Set a handle for searching. + recentsh = newsh; + } + + // Update the point-to-subface map. + if (pointtype(insertpt) == FREEFACETVERTEX) { + setpoint2sh(insertpt, sencode(newsh)); + } + + // Connect adjacent new subfaces together. + for (i = 0; i < caveshbdlist->objects; i++) { + // Get an old subface at edge [a, b]. + parysh = (face *) fastlookup(caveshbdlist, i); + spivot(*parysh, newsh); // The new subface [a, b, p]. + senextself(newsh); // At edge [b, p]. + spivot(newsh, neighsh); + if (neighsh.sh == NULL) { + // Find the adjacent new subface at edge [b, p]. + pb = sdest(*parysh); + neighsh = *parysh; + while (1) { + senextself(neighsh); + spivotself(neighsh); + if (neighsh.sh == NULL) break; + if (!smarktested(neighsh)) break; + if (sdest(neighsh) != pb) sesymself(neighsh); + } + if (neighsh.sh != NULL) { + // Now 'neighsh' is a new subface at edge [b, #]. + if (sorg(neighsh) != pb) sesymself(neighsh); + senext2self(neighsh); // Go to the open edge [p, b]. + sbond(newsh, neighsh); + } + } + spivot(*parysh, newsh); // The new subface [a, b, p]. + senext2self(newsh); // At edge [p, a]. + spivot(newsh, neighsh); + if (neighsh.sh == NULL) { + // Find the adjacent new subface at edge [p, a]. + pa = sorg(*parysh); + neighsh = *parysh; + while (1) { + senext2self(neighsh); + spivotself(neighsh); + if (neighsh.sh == NULL) break; + if (!smarktested(neighsh)) break; + if (sorg(neighsh) != pa) sesymself(neighsh); + } + if (neighsh.sh != NULL) { + // Now 'neighsh' is a new subface at edge [#, a]. + if (sdest(neighsh) != pa) sesymself(neighsh); + senextself(neighsh); // Go to the open edge [a, p]. + sbond(newsh, neighsh); + } + } + } + + if ((loc == ONEDGE) || ((splitseg != NULL) && (splitseg->sh != NULL)) + || (cavesegshlist->objects > 0l)) { + // An edge is being split. We distinguish two cases: + // (1) the edge is not on the boundary of the cavity; + // (2) the edge is on the boundary of the cavity. + // In case (2), the edge is either a segment or a hull edge. There are + // degenerated new faces in the cavity. They must be removed. + face aseg, bseg, aoutseg, boutseg; + + for (i = 0; i < cavesegshlist->objects; i++) { + // Get the saved old subface. + parysh = (face *) fastlookup(cavesegshlist, i); + // Get a possible new degenerated subface. + spivot(*parysh, cavesh); + if (sapex(cavesh) == insertpt) { + // Found a degenerated new subface, i.e., case (2). + if (cavesegshlist->objects > 1) { + // There are more than one subface share at this edge. + j = (i + 1) % (int) cavesegshlist->objects; + parysh = (face *) fastlookup(cavesegshlist, j); + spivot(*parysh, neighsh); + // Adjust cavesh and neighsh both at edge a->b, and has p as apex. + if (sorg(neighsh) != sorg(cavesh)) { + sesymself(neighsh); + } + // Connect adjacent faces at two other edges of cavesh and neighsh. + // As a result, the two degenerated new faces are squeezed from the + // new triangulation of the cavity. Note that the squeezed faces + // still hold the adjacent informations which will be used in + // re-connecting subsegments (if they exist). + for (j = 0; j < 2; j++) { + senextself(cavesh); + senextself(neighsh); + spivot(cavesh, newsh); + spivot(neighsh, casout); + sbond1(newsh, casout); // newsh <- casout. + } + } else { + // There is only one subface containing this edge [a,b]. Squeeze the + // degenerated new face [a,b,c] by disconnecting it from its two + // adjacent subfaces at edges [b,c] and [c,a]. Note that the face + // [a,b,c] still hold the connection to them. + for (j = 0; j < 2; j++) { + senextself(cavesh); + spivot(cavesh, newsh); + sdissolve(newsh); + } + } + //recentsh = newsh; + // Update the point-to-subface map. + if (pointtype(insertpt) == FREEFACETVERTEX) { + setpoint2sh(insertpt, sencode(newsh)); + } + } + } + + if ((splitseg != NULL) && (splitseg->sh != NULL)) { + if (loc != INSTAR) { // if (bowywat < 3) { + smarktest(*splitseg); // Mark it as being processed. + } + + aseg = *splitseg; + pa = sorg(*splitseg); + pb = sdest(*splitseg); + + // Insert the new point p. + makeshellface(subsegs, &aseg); + makeshellface(subsegs, &bseg); + + setshvertices(aseg, pa, insertpt, NULL); + setshvertices(bseg, insertpt, pb, NULL); + setshellmark(aseg, shellmark(*splitseg)); + setshellmark(bseg, shellmark(*splitseg)); + if (checkconstraints) { + setareabound(aseg, areabound(*splitseg)); + setareabound(bseg, areabound(*splitseg)); + } + if (useinsertradius) { + setfacetindex(aseg, getfacetindex(*splitseg)); + setfacetindex(bseg, getfacetindex(*splitseg)); + } + + // Connect [#, a]<->[a, p]. + senext2(*splitseg, boutseg); // Temporarily use boutseg. + spivotself(boutseg); + if (boutseg.sh != NULL) { + senext2(aseg, aoutseg); + sbond(boutseg, aoutseg); + } + // Connect [p, b]<->[b, #]. + senext(*splitseg, aoutseg); + spivotself(aoutseg); + if (aoutseg.sh != NULL) { + senext(bseg, boutseg); + sbond(boutseg, aoutseg); + } + // Connect [a, p] <-> [p, b]. + senext(aseg, aoutseg); + senext2(bseg, boutseg); + sbond(aoutseg, boutseg); + + // Connect subsegs [a, p] and [p, b] to adjacent new subfaces. + // Although the degenerated new faces have been squeezed. They still + // hold the connections to the actual new faces. + for (i = 0; i < cavesegshlist->objects; i++) { + parysh = (face *) fastlookup(cavesegshlist, i); + spivot(*parysh, neighsh); + // neighsh is a degenerated new face. + if (sorg(neighsh) != pa) { + sesymself(neighsh); + } + senext2(neighsh, newsh); + spivotself(newsh); // The edge [p, a] in newsh + ssbond(newsh, aseg); + senext(neighsh, newsh); + spivotself(newsh); // The edge [b, p] in newsh + ssbond(newsh, bseg); + } + + + // Let the point remember the segment it lies on. + if (pointtype(insertpt) == FREESEGVERTEX) { + setpoint2sh(insertpt, sencode(aseg)); + } + // Update the point-to-seg map. + if (pointtype(pa) == FREESEGVERTEX) { + setpoint2sh(pa, sencode(aseg)); + } + if (pointtype(pb) == FREESEGVERTEX) { + setpoint2sh(pb, sencode(bseg)); + } + } // if ((splitseg != NULL) && (splitseg->sh != NULL)) + + // Delete all degenerated new faces. + for (i = 0; i < cavesegshlist->objects; i++) { + parysh = (face *) fastlookup(cavesegshlist, i); + spivotself(*parysh); + if (sapex(*parysh) == insertpt) { + shellfacedealloc(subfaces, parysh->sh); + } + } + cavesegshlist->restart(); + + if ((splitseg != NULL) && (splitseg->sh != NULL)) { + // Return the two new subsegments (for further process). + // Re-use 'cavesegshlist'. + cavesegshlist->newindex((void **) &parysh); + *parysh = aseg; + cavesegshlist->newindex((void **) &parysh); + *parysh = bseg; + } + } // if (loc == ONEDGE) + + + return (int) loc; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// sremovevertex() Remove a vertex from the surface mesh. // +// // +// 'delpt' (p) is the vertex to be removed. If 'parentseg' is not NULL, p is // +// a segment vertex, and the origin of 'parentseg' is p. Otherwise, p is a // +// facet vertex, and the origin of 'parentsh' is p. // +// // +// Within each facet, we first use a sequence of 2-to-2 flips to flip any // +// edge at p, finally use a 3-to-1 flip to remove p. // +// // +// All new created subfaces are returned in the global array 'caveshbdlist'. // +// The new segment (when p is on segment) is returned in 'parentseg'. // +// // +// If 'lawson' > 0, the Lawson flip algorithm is used to recover Delaunay- // +// ness after p is removed. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::sremovevertex(point delpt, face* parentsh, face* parentseg, + int lawson) +{ + face flipfaces[4], spinsh, *parysh; + point pa, pb, pc, pd; + REAL ori1, ori2; + int it, i, j; + + if (parentseg != NULL) { + // 'delpt' (p) should be a Steiner point inserted in a segment [a,b], + // where 'parentseg' should be [p,b]. Find the segment [a,p]. + face startsh, neighsh, nextsh; + face abseg, prevseg, checkseg; + face adjseg1, adjseg2; + face fakesh; + senext2(*parentseg, prevseg); + spivotself(prevseg); + prevseg.shver = 0; + // Restore the original segment [a,b]. + pa = sorg(prevseg); + pb = sdest(*parentseg); + if (b->verbose > 2) { + printf(" Remove vertex %d from segment [%d, %d].\n", + pointmark(delpt), pointmark(pa), pointmark(pb)); + } + makeshellface(subsegs, &abseg); + setshvertices(abseg, pa, pb, NULL); + setshellmark(abseg, shellmark(*parentseg)); + if (checkconstraints) { + setareabound(abseg, areabound(*parentseg)); + } + if (useinsertradius) { + setfacetindex(abseg, getfacetindex(*parentseg)); + } + // Connect [#, a]<->[a, b]. + senext2(prevseg, adjseg1); + spivotself(adjseg1); + if (adjseg1.sh != NULL) { + adjseg1.shver = 0; + senextself(adjseg1); + senext2(abseg, adjseg2); + sbond(adjseg1, adjseg2); + } + // Connect [a, b]<->[b, #]. + senext(*parentseg, adjseg1); + spivotself(adjseg1); + if (adjseg1.sh != NULL) { + adjseg1.shver = 0; + senext2self(adjseg1); + senext(abseg, adjseg2); + sbond(adjseg1, adjseg2); + } + // Update the point-to-segment map. + setpoint2sh(pa, sencode(abseg)); + setpoint2sh(pb, sencode(abseg)); + + // Get the faces in face ring at segment [p, b]. + // Re-use array 'caveshlist'. + spivot(*parentseg, *parentsh); + if (parentsh->sh != NULL) { + spinsh = *parentsh; + while (1) { + // Save this face in list. + caveshlist->newindex((void **) &parysh); + *parysh = spinsh; + // Go to the next face in the ring. + spivotself(spinsh); + if (spinsh.sh == NULL) { + break; // It is possible there is only one facet. + } + if (spinsh.sh == parentsh->sh) break; + } + } + + // Create the face ring of the new segment [a,b]. Each face in the ring + // is [a,b,p] (degenerated!). It will be removed (automatically). + for (i = 0; i < caveshlist->objects; i++) { + parysh = (face *) fastlookup(caveshlist, i); + startsh = *parysh; + if (sorg(startsh) != delpt) { + sesymself(startsh); + } + // startsh is [p, b, #1], find the subface [a, p, #2]. + neighsh = startsh; + while (1) { + senext2self(neighsh); + sspivot(neighsh, checkseg); + if (checkseg.sh != NULL) { + // It must be the segment [a, p]. + break; + } + spivotself(neighsh); + if (sorg(neighsh) != delpt) sesymself(neighsh); + } + // Now neighsh is [a, p, #2]. + if (neighsh.sh != startsh.sh) { + // Detach the two subsegments [a,p] and [p,b] from subfaces. + ssdissolve(startsh); + ssdissolve(neighsh); + // Create a degenerated subface [a,b,p]. It is used to: (1) hold the + // new segment [a,b]; (2) connect to the two adjacent subfaces + // [p,b,#] and [a,p,#]. + makeshellface(subfaces, &fakesh); + setshvertices(fakesh, pa, pb, delpt); + setshellmark(fakesh, shellmark(startsh)); + // Connect fakesh to the segment [a,b]. + ssbond(fakesh, abseg); + // Connect fakesh to adjacent subfaces: [p,b,#1] and [a,p,#2]. + senext(fakesh, nextsh); + sbond(nextsh, startsh); + senext2(fakesh, nextsh); + sbond(nextsh, neighsh); + smarktest(fakesh); // Mark it as faked. + } else { + // Special case. There exists already a degenerated face [a,b,p]! + // There is no need to create a faked subface here. + senext2self(neighsh); // [a,b,p] + // Since we will re-connect the face ring using the faked subfaces. + // We put the adjacent face of [a,b,p] to the list. + spivot(neighsh, startsh); // The original adjacent subface. + if (sorg(startsh) != pa) sesymself(startsh); + sdissolve(startsh); + // Connect fakesh to the segment [a,b]. + ssbond(startsh, abseg); + fakesh = startsh; // Do not mark it! + // Delete the degenerated subface. + shellfacedealloc(subfaces, neighsh.sh); + } + // Save the fakesh in list (for re-creating the face ring). + cavesegshlist->newindex((void **) &parysh); + *parysh = fakesh; + } // i + caveshlist->restart(); + + // Re-create the face ring. + if (cavesegshlist->objects > 1) { + for (i = 0; i < cavesegshlist->objects; i++) { + parysh = (face *) fastlookup(cavesegshlist, i); + fakesh = *parysh; + // Get the next face in the ring. + j = (i + 1) % cavesegshlist->objects; + parysh = (face *) fastlookup(cavesegshlist, j); + nextsh = *parysh; + sbond1(fakesh, nextsh); + } + } + + // Delete the two subsegments containing p. + shellfacedealloc(subsegs, parentseg->sh); + shellfacedealloc(subsegs, prevseg.sh); + // Return the new segment. + *parentseg = abseg; + } else { + // p is inside the surface. + if (b->verbose > 2) { + printf(" Remove vertex %d from surface.\n", pointmark(delpt)); + } + // Let 'delpt' be its apex. + senextself(*parentsh); + // For unifying the code, we add parentsh to list. + cavesegshlist->newindex((void **) &parysh); + *parysh = *parentsh; + } + + // Remove the point (p). + + for (it = 0; it < cavesegshlist->objects; it++) { + parentsh = (face *) fastlookup(cavesegshlist, it); // [a,b,p] + senextself(*parentsh); // [b,p,a]. + spivotself(*parentsh); + if (sorg(*parentsh) != delpt) sesymself(*parentsh); + // now parentsh is [p,b,#]. + if (sorg(*parentsh) != delpt) { + // The vertex has already been removed in above special case. + continue; + } + + while (1) { + // Initialize the flip edge list. Re-use 'caveshlist'. + spinsh = *parentsh; // [p, b, #] + while (1) { + caveshlist->newindex((void **) &parysh); + *parysh = spinsh; + senext2self(spinsh); + spivotself(spinsh); + if (spinsh.sh == parentsh->sh) break; + if (sorg(spinsh) != delpt) sesymself(spinsh); + } // while (1) + + if (caveshlist->objects == 3) { + // Delete the point by a 3-to-1 flip. + for (i = 0; i < 3; i++) { + parysh = (face *) fastlookup(caveshlist, i); + flipfaces[i] = *parysh; + } + flip31(flipfaces, lawson); + for (i = 0; i < 3; i++) { + shellfacedealloc(subfaces, flipfaces[i].sh); + } + caveshlist->restart(); + // Save the new subface. + caveshbdlist->newindex((void **) &parysh); + *parysh = flipfaces[3]; + // The vertex is removed. + break; + } + + // Search an edge to flip. + for (i = 0; i < caveshlist->objects; i++) { + parysh = (face *) fastlookup(caveshlist, i); + flipfaces[0] = *parysh; + spivot(flipfaces[0], flipfaces[1]); + if (sorg(flipfaces[0]) != sdest(flipfaces[1])) + sesymself(flipfaces[1]); + // Skip this edge if it belongs to a faked subface. + if (!smarktested(flipfaces[0]) && !smarktested(flipfaces[1])) { + pa = sorg(flipfaces[0]); + pb = sdest(flipfaces[0]); + pc = sapex(flipfaces[0]); + pd = sapex(flipfaces[1]); + calculateabovepoint4(pa, pb, pc, pd); + // Check if a 2-to-2 flip is possible. + ori1 = orient3d(pc, pd, dummypoint, pa); + ori2 = orient3d(pc, pd, dummypoint, pb); + if (ori1 * ori2 < 0) { + // A 2-to-2 flip is found. + flip22(flipfaces, lawson, 0); + // The i-th edge is flipped. The i-th and (i-1)-th subfaces are + // changed. The 'flipfaces[1]' contains p as its apex. + senext2(flipfaces[1], *parentsh); + // Save the new subface. + caveshbdlist->newindex((void **) &parysh); + *parysh = flipfaces[0]; + break; + } + } // + } // i + + if (i == caveshlist->objects) { + // Do a flip22 and a flip31 to remove p. + parysh = (face *) fastlookup(caveshlist, 0); + flipfaces[0] = *parysh; + spivot(flipfaces[0], flipfaces[1]); + if (sorg(flipfaces[0]) != sdest(flipfaces[1])) { + sesymself(flipfaces[1]); + } + flip22(flipfaces, lawson, 0); + senext2(flipfaces[1], *parentsh); + // Save the new subface. + caveshbdlist->newindex((void **) &parysh); + *parysh = flipfaces[0]; + } + + // The edge list at p are changed. + caveshlist->restart(); + } // while (1) + + } // it + + cavesegshlist->restart(); + + if (b->verbose > 2) { + printf(" Created %ld new subfaces.\n", caveshbdlist->objects); + } + + + if (lawson) { + lawsonflip(); + } + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// slocate() Locate a point in a surface triangulation. // +// // +// Staring the search from 'searchsh'(it should not be NULL). Perform a line // +// walk search for a subface containing the point (p). // +// // +// If 'aflag' is set, the 'dummypoint' is pre-calculated so that it lies // +// above the 'searchsh' in its current orientation. The test if c is CCW to // +// the line a->b can be done by the test if c is below the oriented plane // +// a->b->dummypoint. // +// // +// If 'cflag' is not TRUE, the triangulation may not be convex. Stop search // +// when a segment is met and return OUTSIDE. // +// // +// If 'rflag' (rounding) is set, after the location of the point is found, // +// either ONEDGE or ONFACE, round the result using an epsilon. // +// // +// The returned value indicates the following cases: // +// - ONVERTEX, p is the origin of 'searchsh'. // +// - ONEDGE, p lies on the edge of 'searchsh'. // +// - ONFACE, p lies in the interior of 'searchsh'. // +// - OUTSIDE, p lies outside of the triangulation, p is on the left-hand // +// side of the edge 'searchsh'(s), i.e., org(s), dest(s), p are CW. // +// // +/////////////////////////////////////////////////////////////////////////////// + +enum tetgenmesh::locateresult tetgenmesh::slocate(point searchpt, + face* searchsh, int aflag, int cflag, int rflag) +{ + face neighsh; + point pa, pb, pc; + enum locateresult loc; + enum {MOVE_BC, MOVE_CA} nextmove; + REAL ori, ori_bc, ori_ca; + int i; + + pa = sorg(*searchsh); + pb = sdest(*searchsh); + pc = sapex(*searchsh); + + if (!aflag) { + // No above point is given. Calculate an above point for this facet. + calculateabovepoint4(pa, pb, pc, searchpt); + } + + // 'dummypoint' is given. Make sure it is above [a,b,c] + ori = orient3d(pa, pb, pc, dummypoint); + if (ori > 0) { + sesymself(*searchsh); // Reverse the face orientation. + } else if (ori == 0.0) { + // This case should not happen theoretically. But... + return UNKNOWN; + } + + // Find an edge of the face s.t. p lies on its right-hand side (CCW). + for (i = 0; i < 3; i++) { + pa = sorg(*searchsh); + pb = sdest(*searchsh); + ori = orient3d(pa, pb, dummypoint, searchpt); + if (ori > 0) break; + senextself(*searchsh); + } + if (i == 3) { + return UNKNOWN; + } + + pc = sapex(*searchsh); + + if (pc == searchpt) { + senext2self(*searchsh); + return ONVERTEX; + } + + while (1) { + + ori_bc = orient3d(pb, pc, dummypoint, searchpt); + ori_ca = orient3d(pc, pa, dummypoint, searchpt); + + if (ori_bc < 0) { + if (ori_ca < 0) { // (--) + // Any of the edges is a viable move. + if (randomnation(2)) { + nextmove = MOVE_CA; + } else { + nextmove = MOVE_BC; + } + } else { // (-#) + // Edge [b, c] is viable. + nextmove = MOVE_BC; + } + } else { + if (ori_ca < 0) { // (#-) + // Edge [c, a] is viable. + nextmove = MOVE_CA; + } else { + if (ori_bc > 0) { + if (ori_ca > 0) { // (++) + loc = ONFACE; // Inside [a, b, c]. + break; + } else { // (+0) + senext2self(*searchsh); // On edge [c, a]. + loc = ONEDGE; + break; + } + } else { // ori_bc == 0 + if (ori_ca > 0) { // (0+) + senextself(*searchsh); // On edge [b, c]. + loc = ONEDGE; + break; + } else { // (00) + // p is coincident with vertex c. + senext2self(*searchsh); + return ONVERTEX; + } + } + } + } + + // Move to the next face. + if (nextmove == MOVE_BC) { + senextself(*searchsh); + } else { + senext2self(*searchsh); + } + if (!cflag) { + // NON-convex case. Check if we will cross a boundary. + if (isshsubseg(*searchsh)) { + return ENCSEGMENT; + } + } + spivot(*searchsh, neighsh); + if (neighsh.sh == NULL) { + return OUTSIDE; // A hull edge. + } + // Adjust the edge orientation. + if (sorg(neighsh) != sdest(*searchsh)) { + sesymself(neighsh); + } + + // Update the newly discovered face and its endpoints. + *searchsh = neighsh; + pa = sorg(*searchsh); + pb = sdest(*searchsh); + pc = sapex(*searchsh); + + if (pc == searchpt) { + senext2self(*searchsh); + return ONVERTEX; + } + + } // while (1) + + // assert(loc == ONFACE || loc == ONEDGE); + + + if (rflag) { + // Round the locate result before return. + REAL n[3], area_abc, area_abp, area_bcp, area_cap; + + pa = sorg(*searchsh); + pb = sdest(*searchsh); + pc = sapex(*searchsh); + + facenormal(pa, pb, pc, n, 1, NULL); + area_abc = sqrt(dot(n, n)); + + facenormal(pb, pc, searchpt, n, 1, NULL); + area_bcp = sqrt(dot(n, n)); + if ((area_bcp / area_abc) < b->epsilon) { + area_bcp = 0; // Rounding. + } + + facenormal(pc, pa, searchpt, n, 1, NULL); + area_cap = sqrt(dot(n, n)); + if ((area_cap / area_abc) < b->epsilon) { + area_cap = 0; // Rounding + } + + if ((loc == ONFACE) || (loc == OUTSIDE)) { + facenormal(pa, pb, searchpt, n, 1, NULL); + area_abp = sqrt(dot(n, n)); + if ((area_abp / area_abc) < b->epsilon) { + area_abp = 0; // Rounding + } + } else { // loc == ONEDGE + area_abp = 0; + } + + if (area_abp == 0) { + if (area_bcp == 0) { + senextself(*searchsh); + loc = ONVERTEX; // p is close to b. + } else { + if (area_cap == 0) { + loc = ONVERTEX; // p is close to a. + } else { + loc = ONEDGE; // p is on edge [a,b]. + } + } + } else if (area_bcp == 0) { + if (area_cap == 0) { + senext2self(*searchsh); + loc = ONVERTEX; // p is close to c. + } else { + senextself(*searchsh); + loc = ONEDGE; // p is on edge [b,c]. + } + } else if (area_cap == 0) { + senext2self(*searchsh); + loc = ONEDGE; // p is on edge [c,a]. + } else { + loc = ONFACE; // p is on face [a,b,c]. + } + } // if (rflag) + + return loc; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// sscoutsegment() Look for a segment in the surface triangulation. // +// // +// The segment is given by the origin of 'searchsh' and 'endpt'. // +// // +// If an edge in T is found matching this segment, the segment is "locked" // +// in T at the edge. Otherwise, flip the first edge in T that the segment // +// crosses. Continue the search from the flipped face. // +// // +// This routine uses 'orisent3d' to determine the search direction. It uses // +// 'dummypoint' as the 'lifted point' in 3d, and it assumes that it (dummy- // +// point) lies above the 'searchsh' (w.r.t the Right-hand rule). // +// // +/////////////////////////////////////////////////////////////////////////////// + +enum tetgenmesh::interresult tetgenmesh::sscoutsegment(face *searchsh, + point endpt, int insertsegflag, int reporterrorflag, int chkencflag) +{ + face flipshs[2], neighsh; + point startpt, pa, pb, pc, pd; + enum interresult dir; + enum {MOVE_AB, MOVE_CA} nextmove; + REAL ori_ab, ori_ca, len; + + // The origin of 'searchsh' is fixed. + startpt = sorg(*searchsh); + nextmove = MOVE_AB; // Avoid compiler warning. + + if (b->verbose > 2) { + printf(" Scout segment (%d, %d).\n", pointmark(startpt), + pointmark(endpt)); + } + len = distance(startpt, endpt); + + // Search an edge in 'searchsh' on the path of this segment. + while (1) { + + pb = sdest(*searchsh); + if (pb == endpt) { + dir = SHAREEDGE; // Found! + break; + } + + pc = sapex(*searchsh); + if (pc == endpt) { + senext2self(*searchsh); + sesymself(*searchsh); + dir = SHAREEDGE; // Found! + break; + } + + + // Round the results. + if ((sqrt(triarea(startpt, pb, endpt)) / len) < b->epsilon) { + ori_ab = 0.0; + } else { + ori_ab = orient3d(startpt, pb, dummypoint, endpt); + } + if ((sqrt(triarea(pc, startpt, endpt)) / len) < b->epsilon) { + ori_ca = 0.0; + } else { + ori_ca = orient3d(pc, startpt, dummypoint, endpt); + } + + if (ori_ab < 0) { + if (ori_ca < 0) { // (--) + // Both sides are viable moves. + if (randomnation(2)) { + nextmove = MOVE_CA; + } else { + nextmove = MOVE_AB; + } + } else { // (-#) + nextmove = MOVE_AB; + } + } else { + if (ori_ca < 0) { // (#-) + nextmove = MOVE_CA; + } else { + if (ori_ab > 0) { + if (ori_ca > 0) { // (++) + // The segment intersects with edge [b, c]. + dir = ACROSSEDGE; + break; + } else { // (+0) + // The segment collinear with edge [c, a]. + senext2self(*searchsh); + sesymself(*searchsh); + dir = ACROSSVERT; + break; + } + } else { + if (ori_ca > 0) { // (0+) + // The segment is collinear with edge [a, b]. + dir = ACROSSVERT; + break; + } else { // (00) + // startpt == endpt. Not possible. + terminatetetgen(this, 2); + } + } + } + } + + // Move 'searchsh' to the next face, keep the origin unchanged. + if (nextmove == MOVE_AB) { + if (chkencflag) { + // Do not cross boundary. + if (isshsubseg(*searchsh)) { + return ACROSSEDGE; // ACROSS_SEG + } + } + spivot(*searchsh, neighsh); + if (neighsh.sh != NULL) { + if (sorg(neighsh) != pb) sesymself(neighsh); + 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); + if (chkencflag) { + // Do not cross boundary. + if (isshsubseg(neighsh)) { + *searchsh = neighsh; + return ACROSSEDGE; // ACROSS_SEG + } + } + spivotself(neighsh); + if (sdest(neighsh) != pc) sesymself(neighsh); + *searchsh = neighsh; + } + } else { // MOVE_CA + senext2(*searchsh, neighsh); + if (chkencflag) { + // Do not cross boundary. + if (isshsubseg(neighsh)) { + *searchsh = neighsh; + return ACROSSEDGE; // ACROSS_SEG + } + } + spivotself(neighsh); + if (neighsh.sh != NULL) { + if (sdest(neighsh) != pc) sesymself(neighsh); + *searchsh = neighsh; + } else { + // The same reason as above. + // Try the next side, i.e., (startpt->pb). + if (chkencflag) { + // Do not cross boundary. + if (isshsubseg(*searchsh)) { + return ACROSSEDGE; // ACROSS_SEG + } + } + spivot(*searchsh, neighsh); + if (sorg(neighsh) != pb) sesymself(neighsh); + senext(neighsh, *searchsh); + } + } + } // while + + if (dir == SHAREEDGE) { + if (insertsegflag) { + // Insert the segment into the triangulation. + face newseg; + makeshellface(subsegs, &newseg); + setshvertices(newseg, startpt, endpt, NULL); + // Set the default segment marker. + setshellmark(newseg, -1); + ssbond(*searchsh, newseg); + spivot(*searchsh, neighsh); + if (neighsh.sh != NULL) { + ssbond(neighsh, newseg); + } + } + return dir; + } + + if (dir == ACROSSVERT) { + // A point is found collinear with this segment. + if (reporterrorflag) { + point pp = sdest(*searchsh); + printf("PLC Error: A vertex lies in a segment in facet #%d.\n", + shellmark(*searchsh)); + printf(" Vertex: [%d] (%g,%g,%g).\n",pointmark(pp),pp[0],pp[1],pp[2]); + printf(" Segment: [%d, %d]\n", pointmark(startpt), pointmark(endpt)); + } + return dir; + } + + if (dir == ACROSSEDGE) { + // Edge [b, c] intersects with the segment. + senext(*searchsh, flipshs[0]); + if (isshsubseg(flipshs[0])) { + if (reporterrorflag) { + REAL P[3], Q[3], tp = 0, tq = 0; + linelineint(startpt, endpt, pb, pc, P, Q, &tp, &tq); + printf("PLC Error: Two segments intersect at point (%g,%g,%g),", + P[0], P[1], P[2]); + printf(" in facet #%d.\n", shellmark(*searchsh)); + printf(" Segment 1: [%d, %d]\n", pointmark(pb), pointmark(pc)); + printf(" Segment 2: [%d, %d]\n", pointmark(startpt),pointmark(endpt)); + } + return dir; // ACROSS_SEG + } + // Flip edge [b, c], queue unflipped edges (for Delaunay checks). + spivot(flipshs[0], flipshs[1]); + if (sorg(flipshs[1]) != sdest(flipshs[0])) sesymself(flipshs[1]); + flip22(flipshs, 1, 0); + // The flip may create an inverted triangle, check it. + pa = sapex(flipshs[1]); + pb = sapex(flipshs[0]); + pc = sorg(flipshs[0]); + pd = sdest(flipshs[0]); + // Check if pa and pb are on the different sides of [pc, pd]. + // Re-use ori_ab, ori_ca for the tests. + ori_ab = orient3d(pc, pd, dummypoint, pb); + ori_ca = orient3d(pd, pc, dummypoint, pa); + if (ori_ab <= 0) { + flipshpush(&(flipshs[0])); + } else if (ori_ca <= 0) { + flipshpush(&(flipshs[1])); + } + // Set 'searchsh' s.t. its origin is 'startpt'. + *searchsh = flipshs[0]; + } + + return sscoutsegment(searchsh, endpt, insertsegflag, reporterrorflag, + chkencflag); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// scarveholes() Remove triangles not in the facet. // +// // +// This routine re-uses the two global arrays: caveshlist and caveshbdlist. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::scarveholes(int holes, REAL* holelist) +{ + face *parysh, searchsh, neighsh; + enum locateresult loc; + int i, j; + + // Get all triangles. Infect unprotected convex hull triangles. + smarktest(recentsh); + caveshlist->newindex((void **) &parysh); + *parysh = recentsh; + for (i = 0; i < caveshlist->objects; i++) { + parysh = (face *) fastlookup(caveshlist, i); + searchsh = *parysh; + searchsh.shver = 0; + for (j = 0; j < 3; j++) { + spivot(searchsh, neighsh); + // Is this side on the convex hull? + if (neighsh.sh != NULL) { + if (!smarktested(neighsh)) { + smarktest(neighsh); + caveshlist->newindex((void **) &parysh); + *parysh = neighsh; + } + } else { + // A hull side. Check if it is protected by a segment. + if (!isshsubseg(searchsh)) { + // Not protected. Save this face. + if (!sinfected(searchsh)) { + sinfect(searchsh); + caveshbdlist->newindex((void **) &parysh); + *parysh = searchsh; + } + } + } + senextself(searchsh); + } + } + + // Infect the triangles in the holes. + for (i = 0; i < 3 * holes; i += 3) { + searchsh = recentsh; + loc = slocate(&(holelist[i]), &searchsh, 1, 1, 0); + if (loc != OUTSIDE) { + sinfect(searchsh); + caveshbdlist->newindex((void **) &parysh); + *parysh = searchsh; + } + } + + // Find and infect all exterior triangles. + for (i = 0; i < caveshbdlist->objects; i++) { + parysh = (face *) fastlookup(caveshbdlist, i); + searchsh = *parysh; + searchsh.shver = 0; + for (j = 0; j < 3; j++) { + spivot(searchsh, neighsh); + if (neighsh.sh != NULL) { + if (!isshsubseg(searchsh)) { + if (!sinfected(neighsh)) { + sinfect(neighsh); + caveshbdlist->newindex((void **) &parysh); + *parysh = neighsh; + } + } else { + sdissolve(neighsh); // Disconnect a protected face. + } + } + senextself(searchsh); + } + } + + // Delete exterior triangles, unmark interior triangles. + for (i = 0; i < caveshlist->objects; i++) { + parysh = (face *) fastlookup(caveshlist, i); + if (sinfected(*parysh)) { + shellfacedealloc(subfaces, parysh->sh); + } else { + sunmarktest(*parysh); + } + } + + caveshlist->restart(); + caveshbdlist->restart(); +} + + +/////////////////////////////////////////////////////////////////////////////// +// // +// unifysegments() Remove redundant segments and create face links. // +// // +// After this routine, although segments are unique, but some of them may be // +// removed later by mergefacet(). All vertices still have type FACETVERTEX. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::unifysegments() +{ + badface *facelink = NULL, *newlinkitem, *f1, *f2; + face *facperverlist, sface; + face subsegloop, testseg; + point torg, tdest; + REAL ori1, ori2, ori3; + REAL n1[3], n2[3]; + REAL cosang, ang, ang_tol; + int *idx2faclist; + int idx, k, m; + + if (b->verbose > 1) { + printf(" Unifying segments.\n"); + } + // The limit dihedral angle that two facets are not overlapping. + ang_tol = b->facet_overlap_ang_tol / 180.0 * PI; + if (ang_tol < 0.0) ang_tol = 0.0; + + // Create a mapping from vertices to subfaces. + makepoint2submap(subfaces, idx2faclist, facperverlist); + + + subsegloop.shver = 0; + subsegs->traversalinit(); + subsegloop.sh = shellfacetraverse(subsegs); + while (subsegloop.sh != (shellface *) NULL) { + torg = sorg(subsegloop); + tdest = sdest(subsegloop); + + idx = pointmark(torg) - in->firstnumber; + // Loop through the set of subfaces containing 'torg'. Get all the + // subfaces containing the edge (torg, tdest). Save and order them + // in 'sfacelist', the ordering is defined by the right-hand rule + // with thumb points from torg to tdest. + for (k = idx2faclist[idx]; k < idx2faclist[idx + 1]; k++) { + sface = facperverlist[k]; + // The face may be deleted if it is a duplicated face. + if (sface.sh[3] == NULL) continue; + // Search the edge torg->tdest. + if (sdest(sface) != tdest) { + senext2self(sface); + sesymself(sface); + } + if (sdest(sface) != tdest) continue; + + // Save the face f in facelink. + if (flippool->items >= 2) { + f1 = facelink; + for (m = 0; m < flippool->items - 1; m++) { + f2 = f1->nextitem; + ori1 = orient3d(torg, tdest, sapex(f1->ss), sapex(f2->ss)); + ori2 = orient3d(torg, tdest, sapex(f1->ss), sapex(sface)); + if (ori1 > 0) { + // apex(f2) is below f1. + if (ori2 > 0) { + // apex(f) is below f1 (see Fig.1). + ori3 = orient3d(torg, tdest, sapex(f2->ss), sapex(sface)); + if (ori3 > 0) { + // apex(f) is below f2, insert it. + break; + } else if (ori3 < 0) { + // apex(f) is above f2, continue. + } else { // ori3 == 0; + // f is coplanar and codirection with f2. + report_overlapping_facets(&(f2->ss), &sface); + break; + } + } else if (ori2 < 0) { + // apex(f) is above f1 below f2, inset it (see Fig. 2). + break; + } else { // ori2 == 0; + // apex(f) is coplanar with f1 (see Fig. 5). + ori3 = orient3d(torg, tdest, sapex(f2->ss), sapex(sface)); + if (ori3 > 0) { + // apex(f) is below f2, insert it. + break; + } else { + // f is coplanar and codirection with f1. + report_overlapping_facets(&(f1->ss), &sface); + break; + } + } + } else if (ori1 < 0) { + // apex(f2) is above f1. + if (ori2 > 0) { + // apex(f) is below f1, continue (see Fig. 3). + } else if (ori2 < 0) { + // apex(f) is above f1 (see Fig.4). + ori3 = orient3d(torg, tdest, sapex(f2->ss), sapex(sface)); + if (ori3 > 0) { + // apex(f) is below f2, insert it. + break; + } else if (ori3 < 0) { + // apex(f) is above f2, continue. + } else { // ori3 == 0; + // f is coplanar and codirection with f2. + report_overlapping_facets(&(f2->ss), &sface); + break; + } + } else { // ori2 == 0; + // f is coplanar and with f1 (see Fig. 6). + ori3 = orient3d(torg, tdest, sapex(f2->ss), sapex(sface)); + if (ori3 > 0) { + // f is also codirection with f1. + report_overlapping_facets(&(f1->ss), &sface); + break; + } else { + // f is above f2, continue. + } + } + } else { // ori1 == 0; + // apex(f2) is coplanar with f1. By assumption, f1 is not + // coplanar and codirection with f2. + if (ori2 > 0) { + // apex(f) is below f1, continue (see Fig. 7). + } else if (ori2 < 0) { + // apex(f) is above f1, insert it (see Fig. 7). + break; + } else { // ori2 == 0. + // apex(f) is coplanar with f1 (see Fig. 8). + // f is either codirection with f1 or is codirection with f2. + facenormal(torg, tdest, sapex(f1->ss), n1, 1, NULL); + facenormal(torg, tdest, sapex(sface), n2, 1, NULL); + if (dot(n1, n2) > 0) { + report_overlapping_facets(&(f1->ss), &sface); + } else { + report_overlapping_facets(&(f2->ss), &sface); + } + break; + } + } + // Go to the next item; + f1 = f2; + } // for (m = 0; ...) + if (sface.sh[3] != NULL) { + // Insert sface between f1 and f2. + newlinkitem = (badface *) flippool->alloc(); + newlinkitem->ss = sface; + newlinkitem->nextitem = f1->nextitem; + f1->nextitem = newlinkitem; + } + } else if (flippool->items == 1) { + f1 = facelink; + // Make sure that f is not coplanar and codirection with f1. + ori1 = orient3d(torg, tdest, sapex(f1->ss), sapex(sface)); + if (ori1 == 0) { + // f is coplanar with f1 (see Fig. 8). + facenormal(torg, tdest, sapex(f1->ss), n1, 1, NULL); + facenormal(torg, tdest, sapex(sface), n2, 1, NULL); + if (dot(n1, n2) > 0) { + // The two faces are codirectional as well. + report_overlapping_facets(&(f1->ss), &sface); + } + } + // Add this face to link if it is not deleted. + if (sface.sh[3] != NULL) { + // Add this face into link. + newlinkitem = (badface *) flippool->alloc(); + newlinkitem->ss = sface; + newlinkitem->nextitem = NULL; + f1->nextitem = newlinkitem; + } + } else { + // The first face. + newlinkitem = (badface *) flippool->alloc(); + newlinkitem->ss = sface; + newlinkitem->nextitem = NULL; + facelink = newlinkitem; + } + } // for (k = idx2faclist[idx]; ...) + + + // Set the connection between this segment and faces containing it, + // at the same time, remove redundant segments. + f1 = facelink; + for (k = 0; k < flippool->items; k++) { + sspivot(f1->ss, testseg); + // If 'testseg' is not 'subsegloop' and is not dead, it is redundant. + if ((testseg.sh != subsegloop.sh) && (testseg.sh[3] != NULL)) { + shellfacedealloc(subsegs, testseg.sh); + } + // Bonds the subface and the segment together. + ssbond(f1->ss, subsegloop); + f1 = f1->nextitem; + } + + // Create the face ring at the segment. + if (flippool->items > 1) { + f1 = facelink; + for (k = 1; k <= flippool->items; k++) { + k < flippool->items ? f2 = f1->nextitem : f2 = facelink; + // Calculate the dihedral angle between the two facet. + facenormal(torg, tdest, sapex(f1->ss), n1, 1, NULL); + facenormal(torg, tdest, sapex(f2->ss), n2, 1, NULL); + cosang = dot(n1, n2) / (sqrt(dot(n1, n1)) * sqrt(dot(n2, n2))); + // Rounding. + if (cosang > 1.0) cosang = 1.0; + else if (cosang < -1.0) cosang = -1.0; + ang = acos(cosang); + if (ang < ang_tol) { + // Two facets are treated as overlapping each other. + report_overlapping_facets(&(f1->ss), &(f2->ss), ang); + } else { + // Record the smallest input dihedral angle. + if (ang < minfacetdihed) { + minfacetdihed = ang; + } + sbond1(f1->ss, f2->ss); + } + f1 = f2; + } + } + + flippool->restart(); + + // Are there length constraints? + if (b->quality && (in->segmentconstraintlist != (REAL *) NULL)) { + int e1, e2; + REAL len; + for (k = 0; k < in->numberofsegmentconstraints; k++) { + e1 = (int) in->segmentconstraintlist[k * 3]; + e2 = (int) in->segmentconstraintlist[k * 3 + 1]; + if (((pointmark(torg) == e1) && (pointmark(tdest) == e2)) || + ((pointmark(torg) == e2) && (pointmark(tdest) == e1))) { + len = in->segmentconstraintlist[k * 3 + 2]; + setareabound(subsegloop, len); + break; + } + } + } + + subsegloop.sh = shellfacetraverse(subsegs); + } + + delete [] idx2faclist; + delete [] facperverlist; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// identifyinputedges() Identify input edges. // +// // +// A set of input edges is provided in the 'in->edgelist'. We find these // +// edges in the surface mesh and make them segments of the mesh. // +// // +// It is possible that an input edge is not in any facet, i.e.,it is a float-// +// segment inside the volume. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::identifyinputedges(point *idx2verlist) +{ + face* shperverlist; + int* idx2shlist; + face searchsh, neighsh; + face segloop, checkseg, newseg; + point checkpt, pa = NULL, pb = NULL; + int *endpts; + int edgemarker; + int idx, i, j; + + int e1, e2; + REAL len; + + if (!b->quiet) { + printf("Inserting edges ...\n"); + } + + // Construct a map from points to subfaces. + makepoint2submap(subfaces, idx2shlist, shperverlist); + + // Process the set of input edges. + for (i = 0; i < in->numberofedges; i++) { + endpts = &(in->edgelist[(i << 1)]); + if (endpts[0] == endpts[1]) { + if (!b->quiet) { + printf("Warning: Edge #%d is degenerated. Skipped.\n", i); + } + continue; // Skip a degenerated edge. + } + // Recall that all existing segments have a default marker '-1'. + // We assign all identified segments a default marker '-2'. + edgemarker = in->edgemarkerlist ? in->edgemarkerlist[i] : -2; + + // Find a face contains the edge. + newseg.sh = NULL; + searchsh.sh = NULL; + idx = endpts[0] - in->firstnumber; + for (j = idx2shlist[idx]; j < idx2shlist[idx + 1]; j++) { + checkpt = sdest(shperverlist[j]); + if (pointmark(checkpt) == endpts[1]) { + searchsh = shperverlist[j]; + break; // Found. + } else { + checkpt = sapex(shperverlist[j]); + if (pointmark(checkpt) == endpts[1]) { + senext2(shperverlist[j], searchsh); + sesymself(searchsh); + break; + } + } + } // j + + if (searchsh.sh != NULL) { + // Check if this edge is already a segment of the mesh. + sspivot(searchsh, checkseg); + if (checkseg.sh != NULL) { + // This segment already exist. + newseg = checkseg; + } else { + // Create a new segment at this edge. + pa = sorg(searchsh); + pb = sdest(searchsh); + makeshellface(subsegs, &newseg); + setshvertices(newseg, pa, pb, NULL); + ssbond(searchsh, newseg); + spivot(searchsh, neighsh); + if (neighsh.sh != NULL) { + ssbond(neighsh, newseg); + } + } + } else { + // It is a dangling segment (not belong to any facets). + // Get the two endpoints of this segment. + pa = idx2verlist[endpts[0]]; + pb = idx2verlist[endpts[1]]; + if (pa == pb) { + if (!b->quiet) { + printf("Warning: Edge #%d is degenerated. Skipped.\n", i); + } + continue; + } + // Check if segment [a,b] already exists. + // TODO: Change the brute-force search. Slow! + point *ppt; + 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); + } + } + + setshellmark(newseg, edgemarker); + + if (b->quality && (in->segmentconstraintlist != (REAL *) NULL)) { + for (i = 0; i < in->numberofsegmentconstraints; i++) { + e1 = (int) in->segmentconstraintlist[i * 3]; + e2 = (int) in->segmentconstraintlist[i * 3 + 1]; + if (((pointmark(pa) == e1) && (pointmark(pb) == e2)) || + ((pointmark(pa) == e2) && (pointmark(pb) == e1))) { + len = in->segmentconstraintlist[i * 3 + 2]; + setareabound(newseg, len); + break; + } + } + } + } // i + + delete [] shperverlist; + delete [] idx2shlist; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// mergefacets() Merge adjacent facets. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::mergefacets() +{ + face parentsh, neighsh, neineish; + face segloop; + point pa, pb, pc, pd; + REAL n1[3], n2[3]; + REAL cosang, cosang_tol; + + + // Allocate an array to save calcaulated dihedral angles at segments. + arraypool *dihedangarray = new arraypool(sizeof(double), 10); + REAL *paryang = NULL; + + // First, remove coplanar segments. + // The dihedral angle bound for two different facets. + cosang_tol = cos(b->facet_separate_ang_tol / 180.0 * PI); + + subsegs->traversalinit(); + segloop.sh = shellfacetraverse(subsegs); + while (segloop.sh != (shellface *) NULL) { + // Only remove a segment if it has a marker '-1'. + if (shellmark(segloop) != -1) { + segloop.sh = shellfacetraverse(subsegs); + continue; + } + spivot(segloop, parentsh); + if (parentsh.sh != NULL) { + spivot(parentsh, neighsh); + if (neighsh.sh != NULL) { + spivot(neighsh, neineish); + if (neineish.sh == parentsh.sh) { + // Exactly two subfaces at this segment. + // Only merge them if they have the same boundary marker. + if (shellmark(parentsh) == shellmark(neighsh)) { + pa = sorg(segloop); + pb = sdest(segloop); + pc = sapex(parentsh); + pd = sapex(neighsh); + // Calculate the dihedral angle at the segment [a,b]. + facenormal(pa, pb, pc, n1, 1, NULL); + facenormal(pa, pb, pd, n2, 1, NULL); + cosang = dot(n1, n2) / (sqrt(dot(n1, n1)) * sqrt(dot(n2, n2))); + if (cosang < cosang_tol) { + ssdissolve(parentsh); + ssdissolve(neighsh); + shellfacedealloc(subsegs, segloop.sh); + // Add the edge to flip stack. + flipshpush(&parentsh); + } else { + // Save 'cosang' to avoid re-calculate it. + // Re-use the pointer at the first segment. + dihedangarray->newindex((void **) &paryang); + *paryang = cosang; + segloop.sh[6] = (shellface) paryang; + } + } + } // if (neineish.sh == parentsh.sh) + } + } + segloop.sh = shellfacetraverse(subsegs); + } + + // Second, remove ridge segments at small angles. + // The dihedral angle bound for two different facets. + cosang_tol = cos(b->facet_small_ang_tol / 180.0 * PI); + REAL cosang_sep_tol = cos((b->facet_separate_ang_tol - 5.0) / 180.0 * PI); + face shloop; + face seg1, seg2; + REAL cosang1, cosang2; + int i, j; + + subfaces->traversalinit(); + shloop.sh = shellfacetraverse(subfaces); + while (shloop.sh != (shellface *) NULL) { + for (i = 0; i < 3; i++) { + if (isshsubseg(shloop)) { + senext(shloop, neighsh); + if (isshsubseg(neighsh)) { + // Found two segments sharing at one vertex. + // Check if they form a small angle. + pa = sorg(shloop); + pb = sdest(shloop); + pc = sapex(shloop); + for (j = 0; j < 3; j++) n1[j] = pa[j] - pb[j]; + for (j = 0; j < 3; j++) n2[j] = pc[j] - pb[j]; + cosang = dot(n1, n2) / (sqrt(dot(n1, n1)) * sqrt(dot(n2, n2))); + if (cosang > cosang_tol) { + // Found a small angle. + segloop.sh = NULL; + sspivot(shloop, seg1); + sspivot(neighsh, seg2); + if (seg1.sh[6] != NULL) { + paryang = (REAL *) (seg1.sh[6]); + cosang1 = *paryang; + } else { + cosang1 = 1.0; // 0 degree; + } + if (seg2.sh[6] != NULL) { + paryang = (REAL *) (seg2.sh[6]); + cosang2 = *paryang; + } else { + cosang2 = 1.0; // 0 degree; + } + if (cosang1 < cosang_sep_tol) { + if (cosang2 < cosang_sep_tol) { + if (cosang1 < cosang2) { + segloop = seg1; + } else { + segloop = seg2; + } + } else { + segloop = seg1; + } + } else { + if (cosang2 < cosang_sep_tol) { + segloop = seg2; + } + } + if (segloop.sh != NULL) { + // Remove this segment. + segloop.shver = 0; + spivot(segloop, parentsh); + spivot(parentsh, neighsh); + ssdissolve(parentsh); + ssdissolve(neighsh); + shellfacedealloc(subsegs, segloop.sh); + // Add the edge to flip stack. + flipshpush(&parentsh); + break; + } + } + } // if (isshsubseg) + } // if (isshsubseg) + senextself(shloop); + } + shloop.sh = shellfacetraverse(subfaces); + } + + delete dihedangarray; + + if (flipstack != NULL) { + lawsonflip(); // Recover Delaunayness. + } +} + + +/////////////////////////////////////////////////////////////////////////////// +// // +// finddirection() Find the tet on the path from one point to another. // +// // +// The path starts from 'searchtet''s origin and ends at 'endpt'. On finish, // +// 'searchtet' contains a tet on the path, its origin does not change. // +// // +// The return value indicates one of the following cases (let 'searchtet' be // +// abcd, a is the origin of the path): // +// - ACROSSVERT, edge ab is collinear with the path; // +// - ACROSSEDGE, edge bc intersects with the path; // +// - ACROSSFACE, face bcd intersects with the path. // +// // +// WARNING: This routine is designed for convex triangulations, and will not // +// generally work after the holes and concavities have been carved. // +// // +/////////////////////////////////////////////////////////////////////////////// + +enum tetgenmesh::interresult + tetgenmesh::finddirection(triface* searchtet, point endpt) +{ + triface neightet; + point pa, pb, pc, pd; + enum {HMOVE, RMOVE, LMOVE} nextmove; + REAL hori, rori, lori; + int t1ver; + int s; + + // The origin is fixed. + pa = org(*searchtet); + if ((point) searchtet->tet[7] == dummypoint) { + // A hull tet. Choose the neighbor of its base face. + decode(searchtet->tet[3], *searchtet); + // Reset the origin to be pa. + if ((point) searchtet->tet[4] == pa) { + searchtet->ver = 11; + } else if ((point) searchtet->tet[5] == pa) { + searchtet->ver = 3; + } else if ((point) searchtet->tet[6] == pa) { + searchtet->ver = 7; + } else { + searchtet->ver = 0; + } + } + + pb = dest(*searchtet); + // Check whether the destination or apex is 'endpt'. + if (pb == endpt) { + // pa->pb is the search edge. + return ACROSSVERT; + } + + pc = apex(*searchtet); + if (pc == endpt) { + // pa->pc is the search edge. + eprevesymself(*searchtet); + return ACROSSVERT; + } + + // Walk through tets around pa until the right one is found. + while (1) { + + pd = oppo(*searchtet); + // Check whether the opposite vertex is 'endpt'. + if (pd == endpt) { + // pa->pd is the search edge. + esymself(*searchtet); + enextself(*searchtet); + return ACROSSVERT; + } + // Check if we have entered outside of the domain. + if (pd == dummypoint) { + // This is possible when the mesh is non-convex. + if (nonconvex) { + return ACROSSFACE; // return ACROSSSUB; // Hit a bounday. + } else { + terminatetetgen(this, 2); + } + } + + // Now assume that the base face abc coincides with the horizon plane, + // and d lies above the horizon. The search point 'endpt' may lie + // above or below the horizon. We test the orientations of 'endpt' + // with respect to three planes: abc (horizon), bad (right plane), + // and acd (left plane). + hori = orient3d(pa, pb, pc, endpt); + rori = orient3d(pb, pa, pd, endpt); + lori = orient3d(pa, pc, pd, endpt); + + // Now decide the tet to move. It is possible there are more than one + // tets are viable moves. Is so, randomly choose one. + if (hori > 0) { + if (rori > 0) { + if (lori > 0) { + // Any of the three neighbors is a viable move. + s = randomnation(3); + if (s == 0) { + nextmove = HMOVE; + } else if (s == 1) { + nextmove = RMOVE; + } else { + nextmove = LMOVE; + } + } else { + // Two tets, below horizon and below right, are viable. + if (randomnation(2)) { + nextmove = HMOVE; + } else { + nextmove = RMOVE; + } + } + } else { + if (lori > 0) { + // Two tets, below horizon and below left, are viable. + if (randomnation(2)) { + nextmove = HMOVE; + } else { + nextmove = LMOVE; + } + } else { + // The tet below horizon is chosen. + nextmove = HMOVE; + } + } + } else { + if (rori > 0) { + if (lori > 0) { + // Two tets, below right and below left, are viable. + if (randomnation(2)) { + nextmove = RMOVE; + } else { + nextmove = LMOVE; + } + } else { + // The tet below right is chosen. + nextmove = RMOVE; + } + } else { + if (lori > 0) { + // The tet below left is chosen. + nextmove = LMOVE; + } else { + // 'endpt' lies either on the plane(s) or across face bcd. + if (hori == 0) { + if (rori == 0) { + // pa->'endpt' is COLLINEAR with pa->pb. + return ACROSSVERT; + } + if (lori == 0) { + // pa->'endpt' is COLLINEAR with pa->pc. + eprevesymself(*searchtet); // [a,c,d] + return ACROSSVERT; + } + // pa->'endpt' crosses the edge pb->pc. + return ACROSSEDGE; + } + if (rori == 0) { + if (lori == 0) { + // pa->'endpt' is COLLINEAR with pa->pd. + esymself(*searchtet); // face bad. + enextself(*searchtet); // face [a,d,b] + return ACROSSVERT; + } + // pa->'endpt' crosses the edge pb->pd. + esymself(*searchtet); // face bad. + enextself(*searchtet); // face adb + return ACROSSEDGE; + } + if (lori == 0) { + // pa->'endpt' crosses the edge pc->pd. + eprevesymself(*searchtet); // [a,c,d] + return ACROSSEDGE; + } + // pa->'endpt' crosses the face bcd. + return ACROSSFACE; + } + } + } + + // Move to the next tet, fix pa as its origin. + if (nextmove == RMOVE) { + fnextself(*searchtet); + } else if (nextmove == LMOVE) { + eprevself(*searchtet); + fnextself(*searchtet); + enextself(*searchtet); + } else { // HMOVE + fsymself(*searchtet); + enextself(*searchtet); + } + pb = dest(*searchtet); + pc = apex(*searchtet); + + } // while (1) + +} + + +//// steiner_cxx ////////////////////////////////////////////////////////////// +//// //// +//// //// + +/////////////////////////////////////////////////////////////////////////////// +// // +// checkflipeligibility() A call back function for boundary recovery. // +// // +// 'fliptype' indicates which elementary flip will be performed: 1 : 2-to-3, // +// and 2 : 3-to-2, respectively. // +// // +// 'pa, ..., pe' are the vertices involved in this flip, where [a,b,c] is // +// the flip face, and [d,e] is the flip edge. NOTE: 'pc' may be 'dummypoint',// +// other points must not be 'dummypoint'. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::checkflipeligibility(int fliptype, point pa, point pb, + point pc, point pd, point pe, + int level, int edgepivot, + flipconstraints* fc) +{ + point tmppts[3]; + enum interresult dir; + int types[2], poss[4]; + int intflag; + int rejflag = 0; + int i; + + if (fc->seg[0] != NULL) { + // A constraining edge is given (e.g., for edge recovery). + if (fliptype == 1) { + // A 2-to-3 flip: [a,b,c] => [e,d,a], [e,d,b], [e,d,c]. + tmppts[0] = pa; + tmppts[1] = pb; + tmppts[2] = pc; + for (i = 0; i < 3 && !rejflag; i++) { + if (tmppts[i] != dummypoint) { + // Test if the face [e,d,#] intersects the edge. + intflag = tri_edge_test(pe, pd, tmppts[i], fc->seg[0], fc->seg[1], + NULL, 1, types, poss); + if (intflag == 2) { + // They intersect at a single point. + dir = (enum interresult) types[0]; + if (dir == ACROSSFACE) { + // The interior of [e,d,#] intersect the segment. + rejflag = 1; + } else if (dir == ACROSSEDGE) { + if (poss[0] == 0) { + // The interior of [e,d] intersect the segment. + // Since [e,d] is the newly created edge. Reject this flip. + rejflag = 1; + } + } + } else if (intflag == 4) { + // They may intersect at either a point or a line segment. + dir = (enum interresult) types[0]; + if (dir == ACROSSEDGE) { + if (poss[0] == 0) { + // The interior of [e,d] intersect the segment. + // Since [e,d] is the newly created edge. Reject this flip. + rejflag = 1; + } + } + } + } // if (tmppts[0] != dummypoint) + } // i + } else if (fliptype == 2) { + // A 3-to-2 flip: [e,d,a], [e,d,b], [e,d,c] => [a,b,c] + if (pc != dummypoint) { + // Check if the new face [a,b,c] intersect the edge in its interior. + intflag = tri_edge_test(pa, pb, pc, fc->seg[0], fc->seg[1], NULL, + 1, types, poss); + if (intflag == 2) { + // They intersect at a single point. + dir = (enum interresult) types[0]; + if (dir == ACROSSFACE) { + // The interior of [a,b,c] intersect the segment. + rejflag = 1; // Do not flip. + } + } else if (intflag == 4) { + // [a,b,c] is coplanar with the edge. + dir = (enum interresult) types[0]; + if (dir == ACROSSEDGE) { + // The boundary of [a,b,c] intersect the segment. + rejflag = 1; // Do not flip. + } + } + } // if (pc != dummypoint) + } + } // if (fc->seg[0] != NULL) + + if ((fc->fac[0] != NULL) && !rejflag) { + // A constraining face is given (e.g., for face recovery). + if (fliptype == 1) { + // A 2-to-3 flip. + // Test if the new edge [e,d] intersects the face. + intflag = tri_edge_test(fc->fac[0], fc->fac[1], fc->fac[2], pe, pd, + NULL, 1, types, poss); + if (intflag == 2) { + // They intersect at a single point. + dir = (enum interresult) types[0]; + if (dir == ACROSSFACE) { + rejflag = 1; + } else if (dir == ACROSSEDGE) { + rejflag = 1; + } + } else if (intflag == 4) { + // The edge [e,d] is coplanar with the face. + // There may be two intersections. + for (i = 0; i < 2 && !rejflag; i++) { + dir = (enum interresult) types[i]; + if (dir == ACROSSFACE) { + rejflag = 1; + } else if (dir == ACROSSEDGE) { + rejflag = 1; + } + } + } + } // if (fliptype == 1) + } // if (fc->fac[0] != NULL) + + if ((fc->remvert != NULL) && !rejflag) { + // The vertex is going to be removed. Do not create a new edge which + // contains this vertex. + if (fliptype == 1) { + // A 2-to-3 flip. + if ((pd == fc->remvert) || (pe == fc->remvert)) { + rejflag = 1; + } + } + } + + if (fc->remove_large_angle && !rejflag) { + // Remove a large dihedral angle. Do not create a new small angle. + REAL cosmaxd = 0, diff; + if (fliptype == 1) { + // We assume that neither 'a' nor 'b' is dummypoint. + // A 2-to-3 flip: [a,b,c] => [e,d,a], [e,d,b], [e,d,c]. + // The new tet [e,d,a,b] will be flipped later. Only two new tets: + // [e,d,b,c] and [e,d,c,a] need to be checked. + if ((pc != dummypoint) && (pe != dummypoint) && (pd != dummypoint)) { + // Get the largest dihedral angle of [e,d,b,c]. + tetalldihedral(pe, pd, pb, pc, NULL, &cosmaxd, NULL); + diff = cosmaxd - fc->cosdihed_in; + if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0; // Rounding. + if (diff <= 0) { //if (cosmaxd <= fc->cosdihed_in) { + rejflag = 1; + } else { + // Record the largest new angle. + if (cosmaxd < fc->cosdihed_out) { + fc->cosdihed_out = cosmaxd; + } + // Get the largest dihedral angle of [e,d,c,a]. + tetalldihedral(pe, pd, pc, pa, NULL, &cosmaxd, NULL); + diff = cosmaxd - fc->cosdihed_in; + if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0; // Rounding. + if (diff <= 0) { //if (cosmaxd <= fc->cosdihed_in) { + rejflag = 1; + } else { + // Record the largest new angle. + if (cosmaxd < fc->cosdihed_out) { + fc->cosdihed_out = cosmaxd; + } + } + } + } // if (pc != dummypoint && ...) + } else if (fliptype == 2) { + // A 3-to-2 flip: [e,d,a], [e,d,b], [e,d,c] => [a,b,c] + // We assume that neither 'e' nor 'd' is dummypoint. + if (level == 0) { + // Both new tets [a,b,c,d] and [b,a,c,e] are new tets. + if ((pa != dummypoint) && (pb != dummypoint) && (pc != dummypoint)) { + // Get the largest dihedral angle of [a,b,c,d]. + tetalldihedral(pa, pb, pc, pd, NULL, &cosmaxd, NULL); + diff = cosmaxd - fc->cosdihed_in; + if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0; // Rounding + if (diff <= 0) { //if (cosmaxd <= fc->cosdihed_in) { + rejflag = 1; + } else { + // Record the largest new angle. + if (cosmaxd < fc->cosdihed_out) { + fc->cosdihed_out = cosmaxd; + } + // Get the largest dihedral angle of [b,a,c,e]. + tetalldihedral(pb, pa, pc, pe, NULL, &cosmaxd, NULL); + diff = cosmaxd - fc->cosdihed_in; + if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0;// Rounding + if (diff <= 0) { //if (cosmaxd <= fc->cosdihed_in) { + rejflag = 1; + } else { + // Record the largest new angle. + if (cosmaxd < fc->cosdihed_out) { + fc->cosdihed_out = cosmaxd; + } + } + } + } + } else { // level > 0 + if (edgepivot == 1) { + // The new tet [a,b,c,d] will be flipped. Only check [b,a,c,e]. + if ((pa != dummypoint) && (pb != dummypoint) && (pc != dummypoint)) { + // Get the largest dihedral angle of [b,a,c,e]. + tetalldihedral(pb, pa, pc, pe, NULL, &cosmaxd, NULL); + diff = cosmaxd - fc->cosdihed_in; + if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0;// Rounding + if (diff <= 0) { //if (cosmaxd <= fc->cosdihed_in) { + rejflag = 1; + } else { + // Record the largest new angle. + if (cosmaxd < fc->cosdihed_out) { + fc->cosdihed_out = cosmaxd; + } + } + } + } else { + // The new tet [b,a,c,e] will be flipped. Only check [a,b,c,d]. + if ((pa != dummypoint) && (pb != dummypoint) && (pc != dummypoint)) { + // Get the largest dihedral angle of [b,a,c,e]. + tetalldihedral(pa, pb, pc, pd, NULL, &cosmaxd, NULL); + diff = cosmaxd - fc->cosdihed_in; + if (fabs(diff/fc->cosdihed_in) < b->epsilon) diff = 0.0;// Rounding + if (diff <= 0) { //if (cosmaxd <= fc->cosdihed_in) { + rejflag = 1; + } else { + // Record the largest new angle. + if (cosmaxd < fc->cosdihed_out) { + fc->cosdihed_out = cosmaxd; + } + } + } + } // edgepivot + } // level + } + } + + return rejflag; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// removeedgebyflips() Attempt to remove an edge by flips. // +// // +// 'flipedge' is a non-convex or flat edge [a,b,#,#] to be removed. // +// // +// The return value is a positive integer, it indicates whether the edge is // +// removed or not. A value "2" means the edge is removed, otherwise, the // +// edge is not removed and the value (must >= 3) is the current number of // +// tets in the edge star. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::removeedgebyflips(triface *flipedge, flipconstraints* fc) +{ + triface *abtets, spintet; + int t1ver; + int n, nn, i; + + + if (checksubsegflag) { + // Do not flip a segment. + if (issubseg(*flipedge)) { + if (fc->collectencsegflag) { + face checkseg, *paryseg; + tsspivot1(*flipedge, checkseg); + if (!sinfected(checkseg)) { + // Queue this segment in list. + sinfect(checkseg); + caveencseglist->newindex((void **) &paryseg); + *paryseg = checkseg; + } + } + return 0; + } + } + + // Count the number of tets at edge [a,b]. + n = 0; + spintet = *flipedge; + while (1) { + n++; + fnextself(spintet); + if (spintet.tet == flipedge->tet) break; + } + if (n < 3) { + // It is only possible when the mesh contains inverted tetrahedra. + terminatetetgen(this, 2); // Report a bug + } + + if ((b->flipstarsize > 0) && (n > b->flipstarsize)) { + // The star size exceeds the limit. + return 0; // Do not flip it. + } + + // Allocate spaces. + abtets = new triface[n]; + // Collect the tets at edge [a,b]. + spintet = *flipedge; + i = 0; + while (1) { + abtets[i] = spintet; + setelemcounter(abtets[i], 1); + i++; + fnextself(spintet); + if (spintet.tet == flipedge->tet) break; + } + + + // Try to flip the edge (level = 0, edgepivot = 0). + nn = flipnm(abtets, n, 0, 0, fc); + + + if (nn > 2) { + // Edge is not flipped. Unmarktest the remaining tets in Star(ab). + for (i = 0; i < nn; i++) { + setelemcounter(abtets[i], 0); + } + // Restore the input edge (needed by Lawson's flip). + *flipedge = abtets[0]; + } + + // Release the temporary allocated spaces. + // NOTE: fc->unflip must be 0. + int bakunflip = fc->unflip; + fc->unflip = 0; + flipnm_post(abtets, n, nn, 0, fc); + fc->unflip = bakunflip; + + delete [] abtets; + + return nn; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// removefacebyflips() Remove a face by flips. // +// // +// Return 1 if the face is removed. Otherwise, return 0. // +// // +// ASSUMPTIONS: // +// - 'flipface' must not be a subface. // +// - 'flipface' must not be a hull face. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::removefacebyflips(triface *flipface, flipconstraints* fc) +{ + triface fliptets[3], flipedge; + point pa, pb, pc, pd, pe; + REAL ori; + int reducflag = 0; + + fliptets[0] = *flipface; + fsym(*flipface, fliptets[1]); + pa = org(fliptets[0]); + pb = dest(fliptets[0]); + pc = apex(fliptets[0]); + pd = oppo(fliptets[0]); + pe = oppo(fliptets[1]); + + ori = orient3d(pa, pb, pd, pe); + if (ori > 0) { + ori = orient3d(pb, pc, pd, pe); + if (ori > 0) { + ori = orient3d(pc, pa, pd, pe); + if (ori > 0) { + // Found a 2-to-3 flip. + reducflag = 1; + } else { + eprev(*flipface, flipedge); // [c,a] + } + } else { + enext(*flipface, flipedge); // [b,c] + } + } else { + flipedge = *flipface; // [a,b] + } + + if (reducflag) { + // A 2-to-3 flip is found. + flip23(fliptets, 0, fc); + return 1; + } else { + // Try to flip the selected edge of this face. + if (removeedgebyflips(&flipedge, fc) == 2) { + return 1; + } + } + + // Face is not removed. + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// recoveredge() Recover an edge in current tetrahedralization. // +// // +// If the edge is recovered, 'searchtet' returns a tet containing the edge. // +// // +// This edge may intersect a set of faces and edges in the mesh. All these // +// faces or edges are needed to be removed. // +// // +// If the parameter 'fullsearch' is set, it tries to flip any face or edge // +// that intersects the recovering edge. Otherwise, only the face or edge // +// which is visible by 'startpt' is tried. // +// // +// The parameter 'sedge' is used to report self-intersection. If it is not // +// a NULL, it is EITHER a segment OR a subface that contains this edge. // +// // +// Note that this routine assumes that the tetrahedralization is convex. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::recoveredgebyflips(point startpt, point endpt, face *sedge, + triface* searchtet, int fullsearch) +{ + flipconstraints fc; + enum interresult dir; + + fc.seg[0] = startpt; + fc.seg[1] = endpt; + fc.checkflipeligibility = 1; + + // The mainloop of the edge reocvery. + while (1) { // Loop I + + // Search the edge from 'startpt'. + point2tetorg(startpt, *searchtet); + dir = finddirection(searchtet, endpt); + if (dir == ACROSSVERT) { + if (dest(*searchtet) == endpt) { + return 1; // Edge is recovered. + } else { + if (sedge) { + return report_selfint_edge(startpt, endpt, sedge, searchtet, dir); + } else { + return 0; + } + } + } + + // The edge is missing. + + // Try to remove the first intersecting face/edge. + enextesymself(*searchtet); // Go to the opposite face. + if (dir == ACROSSFACE) { + if (checksubfaceflag) { + if (issubface(*searchtet)) { + if (sedge) { + return report_selfint_edge(startpt, endpt, sedge, searchtet, dir); + } else { + return 0; // Cannot flip a subface. + } + } + } + // Try to flip a crossing face. + if (removefacebyflips(searchtet, &fc)) { + continue; + } + } else if (dir == ACROSSEDGE) { + if (checksubsegflag) { + if (issubseg(*searchtet)) { + if (sedge) { + return report_selfint_edge(startpt, endpt, sedge, searchtet, dir); + } else { + return 0; // Cannot flip a segment. + } + } + } + // Try to flip an intersecting edge. + if (removeedgebyflips(searchtet, &fc) == 2) { + continue; + } + } + + // The edge is missing. + + if (fullsearch) { + // Try to flip one of the faces/edges which intersects the edge. + triface neightet, spintet; + point pa, pb, pc, pd; + badface bakface; + enum interresult dir1; + int types[2], poss[4], pos = 0; + int success = 0; + int t1ver; + int i, j; + + // Loop through the sequence of intersecting faces/edges from + // 'startpt' to 'endpt'. + point2tetorg(startpt, *searchtet); + dir = finddirection(searchtet, endpt); + + // 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. + if (dir == DISJOINT) { + terminatetetgen(this, 2); + } + } else if (dir == ACROSSEDGE) { + while (1) { // Loop I-I-I + // Check the two opposite faces (of the edge) in 'searchtet'. + for (i = 0; i < 2; i++) { + 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 + } else { + terminatetetgen(this, 2); // Report a bug + } + + // 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 { + terminatetetgen(this, 2); // Report a bug + } + } + + // The next to be flipped face/edge. + *searchtet = neightet; + + // Bakup this face (tetrahedron). + bakface.forg = org(*searchtet); + bakface.fdest = dest(*searchtet); + bakface.fapex = apex(*searchtet); + bakface.foppo = oppo(*searchtet); + + // Try to flip this intersecting face/edge. + if (dir == ACROSSFACE) { + if (checksubfaceflag) { + if (issubface(*searchtet)) { + if (sedge) { + return report_selfint_edge(startpt,endpt,sedge,searchtet,dir); + } else { + return 0; // Cannot flip a subface. + } + } + } + if (removefacebyflips(searchtet, &fc)) { + success = 1; + break; // Loop I-I + } + } else if (dir == ACROSSEDGE) { + if (checksubsegflag) { + if (issubseg(*searchtet)) { + if (sedge) { + return report_selfint_edge(startpt,endpt,sedge,searchtet,dir); + } else { + return 0; // Cannot flip a segment. + } + } + } + if (removeedgebyflips(searchtet, &fc) == 2) { + success = 1; + break; // Loop I-I + } + } else if (dir == ACROSSVERT) { + if (sedge) { + //return report_selfint_edge(startpt, endpt, sedge, searchtet, dir); + terminatetetgen(this, 2); + } else { + return 0; + } + } else { + terminatetetgen(this, 2); + } + + // 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) { + if (dest(*searchtet) == bakface.fdest) { + spintet = *searchtet; + while (1) { + if (apex(spintet) == bakface.fapex) { + // Found the face. + *searchtet = spintet; + break; + } + fnextself(spintet); + if (spintet.tet == searchtet->tet) { + searchtet->tet = NULL; + break; // Not find. + } + } // while (1) + if (searchtet->tet != NULL) { + if (oppo(*searchtet) != bakface.foppo) { + fsymself(*searchtet); + if (oppo(*searchtet) != bakface.foppo) { + // The original (intersecting) tet has been flipped. + searchtet->tet = NULL; + break; // Not find. + } + } + } + } else { + searchtet->tet = NULL; // Not find. + } + } else { + searchtet->tet = NULL; // Not find. + } + if (searchtet->tet == NULL) { + success = 0; // This face/edge has been destroyed. + break; // Loop I-I + } + } + } // while (1) // Loop I-I + + if (success) { + // One of intersecting faces/edges is flipped. + continue; + } + + } // if (fullsearch) + + // The edge is missing. + break; // Loop I + + } // while (1) // Loop I + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// add_steinerpt_in_schoenhardtpoly() Insert a Steiner point in a Schoen- // +// hardt polyhedron. // +// // +// 'abtets' is an array of n tets which all share at the edge [a,b]. Let the // +// tets are [a,b,p0,p1], [a,b,p1,p2], ..., [a,b,p_(n-2),p_(n-1)]. Moreover, // +// the edge [p0,p_(n-1)] intersects all of the tets in 'abtets'. A special // +// case is that the edge [p0,p_(n-1)] is coplanar with the edge [a,b]. // +// Such set of tets arises when we want to recover an edge from 'p0' to 'p_ // +// (n-1)', and the number of tets at [a,b] can not be reduced by any flip. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::add_steinerpt_in_schoenhardtpoly(triface *abtets, int n, + int chkencflag) +{ + triface worktet, *parytet; + triface faketet1, faketet2; + point pc, pd, steinerpt; + insertvertexflags ivf; + optparameters opm; + REAL vcd[3], sampt[3], smtpt[3]; + REAL maxminvol = 0.0, minvol = 0.0, ori; + int success, maxidx = 0; + int it, i; + + + pc = apex(abtets[0]); // pc = p0 + pd = oppo(abtets[n-1]); // pd = p_(n-1) + + + // Find an optimial point in edge [c,d]. It is visible by all outer faces + // of 'abtets', and it maxmizes the min volume. + + // initialize the list of 2n boundary faces. + for (i = 0; i < n; i++) { + edestoppo(abtets[i], worktet); // [p_i,p_i+1,a] + cavetetlist->newindex((void **) &parytet); + *parytet = worktet; + eorgoppo(abtets[i], worktet); // [p_i+1,p_i,b] + cavetetlist->newindex((void **) &parytet); + *parytet = worktet; + } + + int N = 100; + REAL stepi = 0.01; + + // Search the point along the edge [c,d]. + for (i = 0; i < 3; i++) vcd[i] = pd[i] - pc[i]; + + // Sample N points in edge [c,d]. + for (it = 1; it < N; it++) { + for (i = 0; i < 3; i++) { + sampt[i] = pc[i] + (stepi * (double) it) * vcd[i]; + } + for (i = 0; i < cavetetlist->objects; i++) { + parytet = (triface *) fastlookup(cavetetlist, i); + ori = orient3d(dest(*parytet), org(*parytet), apex(*parytet), sampt); + if (i == 0) { + minvol = ori; + } else { + if (minvol > ori) minvol = ori; + } + } // i + if (it == 1) { + maxminvol = minvol; + maxidx = it; + } else { + if (maxminvol < minvol) { + maxminvol = minvol; + maxidx = it; + } + } + } // it + + if (maxminvol <= 0) { + cavetetlist->restart(); + return 0; + } + + for (i = 0; i < 3; i++) { + smtpt[i] = pc[i] + (stepi * (double) maxidx) * vcd[i]; + } + + // Create two faked tets to hold the two non-existing boundary faces: + // [d,c,a] and [c,d,b]. + maketetrahedron(&faketet1); + setvertices(faketet1, pd, pc, org(abtets[0]), dummypoint); + cavetetlist->newindex((void **) &parytet); + *parytet = faketet1; + maketetrahedron(&faketet2); + setvertices(faketet2, pc, pd, dest(abtets[0]), dummypoint); + cavetetlist->newindex((void **) &parytet); + *parytet = faketet2; + + // Point smooth options. + opm.max_min_volume = 1; + opm.numofsearchdirs = 20; + opm.searchstep = 0.001; + opm.maxiter = 100; // Limit the maximum iterations. + opm.initval = 0.0; // Initial volume is zero. + + // Try to relocate the point into the inside of the polyhedron. + success = smoothpoint(smtpt, cavetetlist, 1, &opm); + + if (success) { + while (opm.smthiter == 100) { + // It was relocated and the prescribed maximum iteration reached. + // Try to increase the search stepsize. + opm.searchstep *= 10.0; + //opm.maxiter = 100; // Limit the maximum iterations. + opm.initval = opm.imprval; + opm.smthiter = 0; // Init. + smoothpoint(smtpt, cavetetlist, 1, &opm); + } + } // if (success) + + // Delete the two faked tets. + tetrahedrondealloc(faketet1.tet); + tetrahedrondealloc(faketet2.tet); + + cavetetlist->restart(); + + if (!success) { + return 0; + } + + + // Insert the Steiner point. + makepoint(&steinerpt, FREEVOLVERTEX); + for (i = 0; i < 3; i++) steinerpt[i] = smtpt[i]; + + // Insert the created Steiner point. + for (i = 0; i < n; i++) { + infect(abtets[i]); + caveoldtetlist->newindex((void **) &parytet); + *parytet = abtets[i]; + } + worktet = abtets[0]; // No need point location. + ivf.iloc = (int) INSTAR; + ivf.chkencflag = chkencflag; + ivf.assignmeshsize = b->metric; + if (ivf.assignmeshsize) { + // Search the tet containing 'steinerpt' for size interpolation. + locate(steinerpt, &(abtets[0])); + worktet = abtets[0]; + } + + // Insert the new point into the tetrahedralization T. + // Note that T is convex (nonconvex = 0). + if (insertpoint(steinerpt, &worktet, NULL, NULL, &ivf)) { + // The vertex has been inserted. + st_volref_count++; + if (steinerleft > 0) steinerleft--; + return 1; + } else { + // Not inserted. + pointdealloc(steinerpt); + return 0; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// add_steinerpt_in_segment() Add a Steiner point inside a segment. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::add_steinerpt_in_segment(face* misseg, int searchlevel) +{ + triface searchtet; + face *paryseg, candseg; + point startpt, endpt, pc, pd; + flipconstraints fc; + enum interresult dir; + REAL P[3], Q[3], tp, tq; + REAL len, smlen = 0, split = 0, split_q = 0; + int success=0; + 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); + // 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); + } else if (dir == ACROSSEDGE) { + // An edge is intersected with the segment. Try to flip it. + success = removeedgebyflips(&searchtet, &fc); + } + + 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--; + if (!success){ + // error + } + + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// addsteiner4recoversegment() Add a Steiner point for recovering a seg. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::addsteiner4recoversegment(face* misseg, int splitsegflag) +{ + triface *abtets, searchtet, spintet; + face splitsh; + face *paryseg; + point startpt, endpt; + point pa, pb, pd, steinerpt, *parypt; + enum interresult dir; + insertvertexflags ivf; + int types[2], poss[4]; + int n, endi, success; + int t1ver; + int i; + + startpt = sorg(*misseg); + if (pointtype(startpt) == FREESEGVERTEX) { + sesymself(*misseg); + startpt = sorg(*misseg); + } + endpt = sdest(*misseg); + + // Try to recover the edge by adding Steiner points. + point2tetorg(startpt, searchtet); + dir = finddirection(&searchtet, endpt); + enextself(searchtet); + + if (dir == ACROSSFACE) { + // The segment is crossing at least 3 faces. Find the common edge of + // the first 3 crossing faces. + esymself(searchtet); + fsym(searchtet, spintet); + pd = oppo(spintet); + for (i = 0; i < 3; i++) { + pa = org(spintet); + pb = dest(spintet); + if (tri_edge_test(pa, pb, pd, startpt, endpt, NULL, 1, types, poss)) { + break; // Found the edge. + } + enextself(spintet); + eprevself(searchtet); + } + esymself(searchtet); + } + + spintet = searchtet; + n = 0; endi = -1; + while (1) { + // Check if the endpt appears in the star. + if (apex(spintet) == endpt) { + endi = n; // Remember the position of endpt. + } + n++; // Count a tet in the star. + fnextself(spintet); + if (spintet.tet == searchtet.tet) break; + } + + if (endi > 0) { + // endpt is also in the edge star + // Get all tets in the edge star. + abtets = new triface[n]; + spintet = searchtet; + for (i = 0; i < n; i++) { + abtets[i] = spintet; + fnextself(spintet); + } + + success = 0; + + if (dir == ACROSSFACE) { + // Find a Steiner points inside the polyhedron. + if (add_steinerpt_in_schoenhardtpoly(abtets, endi, 0)) { + success = 1; + } + } else if (dir == ACROSSEDGE) { + // PLC check. + if (issubseg(searchtet)) { + terminatetetgen(this, 2); + } + if (n > 4) { + // In this case, 'abtets' is separated by the plane (containing the + // two intersecting edges) into two parts, P1 and P2, where P1 + // consists of 'endi' tets: abtets[0], abtets[1], ..., + // abtets[endi-1], and P2 consists of 'n - endi' tets: + // abtets[endi], abtets[endi+1], abtets[n-1]. + if (endi > 2) { // P1 + // There are at least 3 tets in the first part. + if (add_steinerpt_in_schoenhardtpoly(abtets, endi, 0)) { + success++; + } + } + if ((n - endi) > 2) { // P2 + // There are at least 3 tets in the first part. + if (add_steinerpt_in_schoenhardtpoly(&(abtets[endi]), n - endi, 0)) { + success++; + } + } + } else { + // In this case, a 4-to-4 flip should be re-cover the edge [c,d]. + // However, there will be invalid tets (either zero or negtive + // volume). Otherwise, [c,d] should already be recovered by the + // recoveredge() function. + terminatetetgen(this, 2); + } + } else { + terminatetetgen(this, 2); + } + + delete [] abtets; + + if (success) { + // Add the missing segment back to the recovering list. + subsegstack->newindex((void **) &paryseg); + *paryseg = *misseg; + return 1; + } + } // if (endi > 0) + + if (!splitsegflag) { + return 0; + } + + if (b->verbose > 2) { + printf(" Splitting segment (%d, %d)\n", pointmark(startpt), + pointmark(endpt)); + } + steinerpt = NULL; + + if (b->addsteiner_algo > 0) { // -Y/1 or -Y/2 + if (add_steinerpt_in_segment(misseg, 3)) { + return 1; + } + sesymself(*misseg); + if (add_steinerpt_in_segment(misseg, 3)) { + return 1; + } + sesymself(*misseg); + } + + + + + if (steinerpt == NULL) { + // Split the segment at its midpoint. + makepoint(&steinerpt, FREESEGVERTEX); + for (i = 0; i < 3; i++) { + steinerpt[i] = 0.5 * (startpt[i] + endpt[i]); + } + + // We need to locate the point. + spivot(*misseg, splitsh); + ivf.iloc = (int) OUTSIDE; + ivf.bowywat = 1; + ivf.lawson = 0; + ivf.rejflag = 0; + ivf.chkencflag = 0; + ivf.sloc = (int) ONEDGE; + ivf.sbowywat = 1; + ivf.splitbdflag = 0; + ivf.validflag = 1; + ivf.respectbdflag = 1; + ivf.assignmeshsize = b->metric; + if (!insertpoint(steinerpt, &searchtet, &splitsh, misseg, &ivf)) { + terminatetetgen(this, 2); + } + } // if (endi > 0) + + // Save this Steiner point (for removal). + // Re-use the array 'subvertstack'. + subvertstack->newindex((void **) &parypt); + *parypt = steinerpt; + + st_segref_count++; + if (steinerleft > 0) steinerleft--; + + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// recoversegments() Recover all segments. // +// // +// All segments need to be recovered are in 'subsegstack'. // +// // +// This routine first tries to recover each segment by only using flips. If // +// no flip is possible, and the flag 'steinerflag' is set, it then tries to // +// insert Steiner points near or in the segment. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::recoversegments(arraypool *misseglist, int fullsearch, + int steinerflag) +{ + triface searchtet, spintet; + face sseg, *paryseg; + point startpt, endpt; + int success; + int t1ver; + + long bak_inpoly_count = st_volref_count; + long bak_segref_count = st_segref_count; + + if (b->verbose > 1) { + printf(" Recover segments [%s level = %2d] #: %ld.\n", + (b->fliplinklevel > 0) ? "fixed" : "auto", + (b->fliplinklevel > 0) ? b->fliplinklevel : autofliplinklevel, + subsegstack->objects); + } + + // Loop until 'subsegstack' is empty. + while (subsegstack->objects > 0l) { + // seglist is used as a stack. + subsegstack->objects--; + paryseg = (face *) fastlookup(subsegstack, subsegstack->objects); + sseg = *paryseg; + + // Check if this segment has been recovered. + sstpivot1(sseg, searchtet); + if (searchtet.tet != NULL) { + continue; // Not a missing segment. + } + + startpt = sorg(sseg); + endpt = sdest(sseg); + + if (b->verbose > 2) { + printf(" Recover segment (%d, %d).\n", pointmark(startpt), + pointmark(endpt)); + } + + success = 0; + + if (recoveredgebyflips(startpt, endpt, &sseg, &searchtet, 0)) { + success = 1; + } else { + // Try to recover it from the other direction. + if (recoveredgebyflips(endpt, startpt, &sseg, &searchtet, 0)) { + success = 1; + } + } + + if (!success && fullsearch) { + if (recoveredgebyflips(startpt, endpt, &sseg, &searchtet, fullsearch)) { + success = 1; + } + } + + if (success) { + // Segment is recovered. Insert it. + // Let the segment remember an adjacent tet. + sstbond1(sseg, searchtet); + // Bond the segment to all tets containing it. + spintet = searchtet; + do { + tssbond1(spintet, sseg); + fnextself(spintet); + } while (spintet.tet != searchtet.tet); + } else { + if (steinerflag > 0) { + // Try to recover the segment but do not split it. + if (addsteiner4recoversegment(&sseg, 0)) { + success = 1; + } + if (!success && (steinerflag > 1)) { + // Split the segment. + addsteiner4recoversegment(&sseg, 1); + success = 1; + } + } + if (!success) { + if (misseglist != NULL) { + // Save this segment. + misseglist->newindex((void **) &paryseg); + *paryseg = sseg; + } + } + } + + } // while (subsegstack->objects > 0l) + + if (steinerflag) { + if (b->verbose > 1) { + // Report the number of added Steiner points. + if (st_volref_count > bak_inpoly_count) { + printf(" Add %ld Steiner points in volume.\n", + st_volref_count - bak_inpoly_count); + } + if (st_segref_count > bak_segref_count) { + printf(" Add %ld Steiner points in segments.\n", + st_segref_count - bak_segref_count); + } + } + } + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// recoverfacebyflips() Recover a face by flips. // +// // +// 'pa', 'pb', and 'pc' are the three vertices of this face. This routine // +// tries to recover it in the tetrahedral mesh. It is assumed that the three // +// edges, i.e., pa->pb, pb->pc, and pc->pa all exist. // +// // +// If the face is recovered, it is returned by 'searchtet'. // +// // +// If 'searchsh' is not NULL, it is a subface to be recovered. Its vertices // +// must be pa, pb, and pc. It is mainly used to check self-intersections. // +// Another use of this subface is to split it when a Steiner point is found // +// inside this subface. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::recoverfacebyflips(point pa, point pb, point pc, + face *searchsh, triface* searchtet) +{ + triface spintet, flipedge; + point pd, pe; + flipconstraints fc; + int types[2], poss[4], intflag; + int success; + int t1ver; + int i, j; + + + fc.fac[0] = pa; + fc.fac[1] = pb; + fc.fac[2] = pc; + fc.checkflipeligibility = 1; + success = 0; + + for (i = 0; i < 3 && !success; i++) { + while (1) { + // Get a tet containing the edge [a,b]. + point2tetorg(fc.fac[i], *searchtet); + finddirection(searchtet, fc.fac[(i+1)%3]); + // Search the face [a,b,c] + spintet = *searchtet; + while (1) { + if (apex(spintet) == fc.fac[(i+2)%3]) { + // Found the face. + *searchtet = spintet; + // Return the face [a,b,c]. + for (j = i; j > 0; j--) { + eprevself(*searchtet); + } + success = 1; + break; + } + fnextself(spintet); + if (spintet.tet == searchtet->tet) break; + } // while (1) + if (success) break; + // The face is missing. Try to recover it. + flipedge.tet = NULL; + // Find a crossing edge of this face. + spintet = *searchtet; + while (1) { + pd = apex(spintet); + pe = oppo(spintet); + if ((pd != dummypoint) && (pe != dummypoint)) { + // Check if [d,e] intersects [a,b,c] + intflag = tri_edge_test(pa, pb, pc, pd, pe, NULL, 1, types, poss); + if (intflag > 0) { + // By the assumption that all edges of the face exist, they can + // only intersect at a single point. + if (intflag == 2) { + // Go to the edge [d,e]. + edestoppo(spintet, flipedge); // [d,e,a,b] + if (searchsh != NULL) { + // Check the intersection type. + if ((types[0] == (int) ACROSSFACE) || + (types[0] == (int) ACROSSEDGE)) { + // Check if [e,d] is a segment. + if (issubseg(flipedge)) { + return report_selfint_face(pa, pb, pc, searchsh, &flipedge, + intflag, types, poss); + } else { + // Check if [e,d] is an edge of a subface. + triface chkface = flipedge; + while (1) { + if (issubface(chkface)) break; + fsymself(chkface); + if (chkface.tet == flipedge.tet) break; + } + if (issubface(chkface)) { + // Two subfaces are intersecting. + return report_selfint_face(pa, pb, pc,searchsh,&chkface, + intflag, types, poss); + } + } + } else if (types[0] == TOUCHFACE) { + // This is possible when a Steiner point was added on it. + point touchpt, *parypt; + if (poss[1] == 0) { + touchpt = pd; // pd is a coplanar vertex. + } else { + touchpt = pe; // pe is a coplanar vertex. + } + if (pointtype(touchpt) == FREEVOLVERTEX) { + // A volume Steiner point was added in this subface. + // Split this subface by this point. + face checksh, *parysh; + int siloc = (int) ONFACE; + int sbowat = 0; // Only split this subface. A 1-to-3 flip. + setpointtype(touchpt, FREEFACETVERTEX); + sinsertvertex(touchpt, searchsh, NULL, siloc, sbowat, 0); + st_volref_count--; + st_facref_count++; + // Queue this vertex for removal. + subvertstack->newindex((void **) &parypt); + *parypt = touchpt; + // Queue new subfaces for recovery. + // Put all new subfaces into stack for recovery. + for (i = 0; i < caveshbdlist->objects; i++) { + // Get an old subface at edge [a, b]. + parysh = (face *) fastlookup(caveshbdlist, i); + spivot(*parysh, checksh); // The new subface [a, b, p]. + // Do not recover a deleted new face (degenerated). + if (checksh.sh[3] != NULL) { + subfacstack->newindex((void **) &parysh); + *parysh = checksh; + } + } + // Delete the old subfaces in sC(p). + for (i = 0; i < caveshlist->objects; i++) { + parysh = (face *) fastlookup(caveshlist, i); + shellfacedealloc(subfaces, parysh->sh); + } + // Clear working lists. + caveshlist->restart(); + caveshbdlist->restart(); + cavesegshlist->restart(); + // We can return this function. + searchsh->sh = NULL; // It has been split. + return 1; + } else { + // Other cases may be due to a bug or a PLC error. + return report_selfint_face(pa, pb, pc, searchsh, &flipedge, + intflag, types, poss); + } + } else { + // The other intersection types: ACROSSVERT, TOUCHEDGE, + // SHAREVERTEX should not be possible or due to a PLC error. + return report_selfint_face(pa, pb, pc, searchsh, &flipedge, + intflag, types, poss); + } + } // if (searchsh != NULL) + } else { // intflag == 4. Coplanar case. + terminatetetgen(this, 2); + } + break; + } // if (intflag > 0) + } + fnextself(spintet); + if (spintet.tet == searchtet->tet) { + terminatetetgen(this, 2); + } + } // while (1) + // Try to flip the edge [d,e]. + if (removeedgebyflips(&flipedge, &fc) == 2) { + // A crossing edge is removed. + continue; + } + break; + } // while (1) + } // i + + return success; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// recoversubfaces() Recover all subfaces. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::recoversubfaces(arraypool *misshlist, int steinerflag) +{ + triface searchtet, neightet, spintet; + face searchsh, neighsh, neineish, *parysh; + face bdsegs[3]; + point startpt, endpt, apexpt, *parypt; + point steinerpt; + insertvertexflags ivf; + int success; + int t1ver; + int i, j; + + if (b->verbose > 1) { + printf(" Recover subfaces [%s level = %2d] #: %ld.\n", + (b->fliplinklevel > 0) ? "fixed" : "auto", + (b->fliplinklevel > 0) ? b->fliplinklevel : autofliplinklevel, + subfacstack->objects); + } + + // Loop until 'subfacstack' is empty. + while (subfacstack->objects > 0l) { + + subfacstack->objects--; + parysh = (face *) fastlookup(subfacstack, subfacstack->objects); + searchsh = *parysh; + + if (searchsh.sh[3] == NULL) continue; // Skip a dead subface. + + stpivot(searchsh, neightet); + if (neightet.tet != NULL) continue; // Skip a recovered subface. + + + if (b->verbose > 2) { + printf(" Recover subface (%d, %d, %d).\n",pointmark(sorg(searchsh)), + pointmark(sdest(searchsh)), pointmark(sapex(searchsh))); + } + + // The three edges of the face need to be existed first. + for (i = 0; i < 3; i++) { + sspivot(searchsh, bdsegs[i]); + if (bdsegs[i].sh != NULL) { + // The segment must exist. + sstpivot1(bdsegs[i], searchtet); + if (searchtet.tet == NULL) { + terminatetetgen(this, 2); + } + } else { + // This edge is not a segment (due to a Steiner point). + // Check whether it exists or not. + success = 0; + startpt = sorg(searchsh); + endpt = sdest(searchsh); + point2tetorg(startpt, searchtet); + finddirection(&searchtet, endpt); + if (dest(searchtet) == endpt) { + success = 1; + } else { + // The edge is missing. Try to recover it. + if (recoveredgebyflips(startpt, endpt, &searchsh, &searchtet, 0)) { + success = 1; + } else { + if (recoveredgebyflips(endpt, startpt, &searchsh, &searchtet, 0)) { + success = 1; + } + } + } + if (success) { + // Insert a temporary segment to protect this edge. + makeshellface(subsegs, &(bdsegs[i])); + setshvertices(bdsegs[i], startpt, endpt, NULL); + smarktest2(bdsegs[i]); // It's a temporary segment. + // Insert this segment into surface mesh. + ssbond(searchsh, bdsegs[i]); + spivot(searchsh, neighsh); + if (neighsh.sh != NULL) { + ssbond(neighsh, bdsegs[i]); + } + // Insert this segment into tetrahedralization. + sstbond1(bdsegs[i], searchtet); + // Bond the segment to all tets containing it. + spintet = searchtet; + do { + tssbond1(spintet, bdsegs[i]); + fnextself(spintet); + } while (spintet.tet != searchtet.tet); + } else { + // An edge of this subface is missing. Can't recover this subface. + // Delete any temporary segment that has been created. + for (j = (i - 1); j >= 0; j--) { + if (smarktest2ed(bdsegs[j])) { + spivot(bdsegs[j], neineish); + ssdissolve(neineish); + spivot(neineish, neighsh); + if (neighsh.sh != NULL) { + ssdissolve(neighsh); + } + sstpivot1(bdsegs[j], searchtet); + spintet = searchtet; + while (1) { + tssdissolve1(spintet); + fnextself(spintet); + if (spintet.tet == searchtet.tet) break; + } + shellfacedealloc(subsegs, bdsegs[j].sh); + } + } // j + if (steinerflag) { + // Add a Steiner point at the midpoint of this edge. + if (b->verbose > 2) { + printf(" Add a Steiner point in subedge (%d, %d).\n", + pointmark(startpt), pointmark(endpt)); + } + makepoint(&steinerpt, FREEFACETVERTEX); + for (j = 0; j < 3; j++) { + steinerpt[j] = 0.5 * (startpt[j] + endpt[j]); + } + + point2tetorg(startpt, searchtet); // Start from 'searchtet'. + ivf.iloc = (int) OUTSIDE; // Need point location. + ivf.bowywat = 1; + ivf.lawson = 0; + ivf.rejflag = 0; + ivf.chkencflag = 0; + ivf.sloc = (int) ONEDGE; + ivf.sbowywat = 1; // Allow flips in facet. + ivf.splitbdflag = 0; + ivf.validflag = 1; + ivf.respectbdflag = 1; + ivf.assignmeshsize = b->metric; + if (!insertpoint(steinerpt, &searchtet, &searchsh, NULL, &ivf)) { + terminatetetgen(this, 2); + } + // Save this Steiner point (for removal). + // Re-use the array 'subvertstack'. + subvertstack->newindex((void **) &parypt); + *parypt = steinerpt; + + st_facref_count++; + if (steinerleft > 0) steinerleft--; + } // if (steinerflag) + break; + } + } + senextself(searchsh); + } // i + + if (i == 3) { + // Recover the subface. + startpt = sorg(searchsh); + endpt = sdest(searchsh); + apexpt = sapex(searchsh); + + success = recoverfacebyflips(startpt,endpt,apexpt,&searchsh,&searchtet); + + // Delete any temporary segment that has been created. + for (j = 0; j < 3; j++) { + if (smarktest2ed(bdsegs[j])) { + spivot(bdsegs[j], neineish); + ssdissolve(neineish); + spivot(neineish, neighsh); + if (neighsh.sh != NULL) { + ssdissolve(neighsh); + } + sstpivot1(bdsegs[j], neightet); + spintet = neightet; + while (1) { + tssdissolve1(spintet); + fnextself(spintet); + if (spintet.tet == neightet.tet) break; + } + shellfacedealloc(subsegs, bdsegs[j].sh); + } + } // j + + if (success) { + if (searchsh.sh != NULL) { + // Face is recovered. Insert it. + tsbond(searchtet, searchsh); + fsymself(searchtet); + sesymself(searchsh); + tsbond(searchtet, searchsh); + } + } else { + if (steinerflag) { + // Add a Steiner point at the barycenter of this subface. + if (b->verbose > 2) { + printf(" Add a Steiner point in subface (%d, %d, %d).\n", + pointmark(startpt), pointmark(endpt), pointmark(apexpt)); + } + makepoint(&steinerpt, FREEFACETVERTEX); + for (j = 0; j < 3; j++) { + steinerpt[j] = (startpt[j] + endpt[j] + apexpt[j]) / 3.0; + } + + point2tetorg(startpt, searchtet); // Start from 'searchtet'. + ivf.iloc = (int) OUTSIDE; // Need point location. + ivf.bowywat = 1; + ivf.lawson = 0; + ivf.rejflag = 0; + ivf.chkencflag = 0; + ivf.sloc = (int) ONFACE; + ivf.sbowywat = 1; // Allow flips in facet. + ivf.splitbdflag = 0; + ivf.validflag = 1; + ivf.respectbdflag = 1; + ivf.assignmeshsize = b->metric; + if (!insertpoint(steinerpt, &searchtet, &searchsh, NULL, &ivf)) { + terminatetetgen(this, 2); + } + // Save this Steiner point (for removal). + // Re-use the array 'subvertstack'. + subvertstack->newindex((void **) &parypt); + *parypt = steinerpt; + + st_facref_count++; + if (steinerleft > 0) steinerleft--; + } // if (steinerflag) + } + } else { + success = 0; + } + + if (!success) { + if (misshlist != NULL) { + // Save this subface. + misshlist->newindex((void **) &parysh); + *parysh = searchsh; + } + } + + } // while (subfacstack->objects > 0l) + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// getvertexstar() Return the star of a vertex. // +// // +// If the flag 'fullstar' is set, return the complete star of this vertex. // +// Otherwise, only a part of the star which is bounded by facets is returned.// +// // +// 'tetlist' returns the list of tets in the star of the vertex 'searchpt'. // +// Every tet in 'tetlist' is at the face opposing to 'searchpt'. // +// // +// 'vertlist' returns the list of vertices in the star (exclude 'searchpt'). // +// // +// 'shlist' returns the list of subfaces in the star. Each subface must face // +// to the interior of this star. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::getvertexstar(int fullstar, point searchpt, arraypool* tetlist, + arraypool* vertlist, arraypool* shlist) +{ + triface searchtet, neightet, *parytet; + face checksh, *parysh; + point pt, *parypt; + int collectflag; + int t1ver; + int i, j; + + point2tetorg(searchpt, searchtet); + + // Go to the opposite face (the link face) of the vertex. + enextesymself(searchtet); + //assert(oppo(searchtet) == searchpt); + infect(searchtet); // Collect this tet (link face). + tetlist->newindex((void **) &parytet); + *parytet = searchtet; + if (vertlist != NULL) { + // Collect three (link) vertices. + j = (searchtet.ver & 3); // The current vertex index. + for (i = 1; i < 4; i++) { + pt = (point) searchtet.tet[4 + ((j + i) % 4)]; + pinfect(pt); + vertlist->newindex((void **) &parypt); + *parypt = pt; + } + } + + collectflag = 1; + esym(searchtet, neightet); + if (issubface(neightet)) { + if (shlist != NULL) { + tspivot(neightet, checksh); + if (!sinfected(checksh)) { + // Collect this subface (link edge). + sinfected(checksh); + shlist->newindex((void **) &parysh); + *parysh = checksh; + } + } + if (!fullstar) { + collectflag = 0; + } + } + if (collectflag) { + fsymself(neightet); // Goto the adj tet of this face. + esymself(neightet); // Goto the oppo face of this vertex. + // assert(oppo(neightet) == searchpt); + infect(neightet); // Collect this tet (link face). + tetlist->newindex((void **) &parytet); + *parytet = neightet; + if (vertlist != NULL) { + // Collect its apex. + pt = apex(neightet); + pinfect(pt); + vertlist->newindex((void **) &parypt); + *parypt = pt; + } + } // if (collectflag) + + // Continue to collect all tets in the star. + for (i = 0; i < tetlist->objects; i++) { + searchtet = * (triface *) fastlookup(tetlist, i); + // Note that 'searchtet' is a face opposite to 'searchpt', and the neighbor + // tet at the current edge is already collected. + // Check the neighbors at the other two edges of this face. + for (j = 0; j < 2; j++) { + collectflag = 1; + enextself(searchtet); + esym(searchtet, neightet); + if (issubface(neightet)) { + if (shlist != NULL) { + tspivot(neightet, checksh); + if (!sinfected(checksh)) { + // Collect this subface (link edge). + sinfected(checksh); + shlist->newindex((void **) &parysh); + *parysh = checksh; + } + } + if (!fullstar) { + collectflag = 0; + } + } + if (collectflag) { + fsymself(neightet); + if (!infected(neightet)) { + esymself(neightet); // Go to the face opposite to 'searchpt'. + infect(neightet); + tetlist->newindex((void **) &parytet); + *parytet = neightet; + if (vertlist != NULL) { + // Check if a vertex is collected. + pt = apex(neightet); + if (!pinfected(pt)) { + pinfect(pt); + vertlist->newindex((void **) &parypt); + *parypt = pt; + } + } + } // if (!infected(neightet)) + } // if (collectflag) + } // j + } // i + + + // Uninfect the list of tets and vertices. + for (i = 0; i < tetlist->objects; i++) { + parytet = (triface *) fastlookup(tetlist, i); + uninfect(*parytet); + } + + if (vertlist != NULL) { + for (i = 0; i < vertlist->objects; i++) { + parypt = (point *) fastlookup(vertlist, i); + puninfect(*parypt); + } + } + + if (shlist != NULL) { + for (i = 0; i < shlist->objects; i++) { + parysh = (face *) fastlookup(shlist, i); + suninfect(*parysh); + } + } + + return (int) tetlist->objects; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// getedge() Get a tetrahedron having the two endpoints. // +// // +// The method here is to search the second vertex in the link faces of the // +// first vertex. The global array 'cavetetlist' is re-used for searching. // +// // +// This function is used for the case when the mesh is non-convex. Otherwise,// +// the function finddirection() should be faster than this. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::getedge(point e1, point e2, triface *tedge) +{ + triface searchtet, neightet, *parytet; + point pt; + int done; + int i, j; + + if (b->verbose > 2) { + printf(" Get edge from %d to %d.\n", pointmark(e1), pointmark(e2)); + } + + // Quickly check if 'tedge' is just this edge. + if (!isdeadtet(*tedge)) { + if (org(*tedge) == e1) { + if (dest(*tedge) == e2) { + return 1; + } + } else if (org(*tedge) == e2) { + if (dest(*tedge) == e1) { + esymself(*tedge); + return 1; + } + } + } + + // Search for the edge [e1, e2]. + point2tetorg(e1, *tedge); + finddirection(tedge, e2); + if (dest(*tedge) == e2) { + return 1; + } else { + // Search for the edge [e2, e1]. + point2tetorg(e2, *tedge); + finddirection(tedge, e1); + if (dest(*tedge) == e1) { + esymself(*tedge); + return 1; + } + } + + + // Go to the link face of e1. + point2tetorg(e1, searchtet); + enextesymself(searchtet); + arraypool *tetlist = cavebdrylist; + + // Search e2. + for (i = 0; i < 3; i++) { + pt = apex(searchtet); + if (pt == e2) { + // Found. 'searchtet' is [#,#,e2,e1]. + eorgoppo(searchtet, *tedge); // [e1,e2,#,#]. + return 1; + } + enextself(searchtet); + } + + // Get the adjacent link face at 'searchtet'. + fnext(searchtet, neightet); + esymself(neightet); + // assert(oppo(neightet) == e1); + pt = apex(neightet); + if (pt == e2) { + // Found. 'neightet' is [#,#,e2,e1]. + eorgoppo(neightet, *tedge); // [e1,e2,#,#]. + return 1; + } + + // Continue searching in the link face of e1. + infect(searchtet); + tetlist->newindex((void **) &parytet); + *parytet = searchtet; + infect(neightet); + tetlist->newindex((void **) &parytet); + *parytet = neightet; + + done = 0; + + for (i = 0; (i < tetlist->objects) && !done; i++) { + parytet = (triface *) fastlookup(tetlist, i); + searchtet = *parytet; + for (j = 0; (j < 2) && !done; j++) { + enextself(searchtet); + fnext(searchtet, neightet); + if (!infected(neightet)) { + esymself(neightet); + pt = apex(neightet); + if (pt == e2) { + // Found. 'neightet' is [#,#,e2,e1]. + eorgoppo(neightet, *tedge); + done = 1; + } else { + infect(neightet); + tetlist->newindex((void **) &parytet); + *parytet = neightet; + } + } + } // j + } // i + + // Uninfect the list of visited tets. + for (i = 0; i < tetlist->objects; i++) { + parytet = (triface *) fastlookup(tetlist, i); + uninfect(*parytet); + } + tetlist->restart(); + + return done; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// reduceedgesatvertex() Reduce the number of edges at a given vertex. // +// // +// 'endptlist' contains the endpoints of edges connecting at the vertex. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::reduceedgesatvertex(point startpt, arraypool* endptlist) +{ + triface searchtet; + point *pendpt, *parypt; + enum interresult dir; + flipconstraints fc; + int reduceflag; + int count; + int n, i, j; + + + fc.remvert = startpt; + fc.checkflipeligibility = 1; + + while (1) { + + count = 0; + + for (i = 0; i < endptlist->objects; i++) { + pendpt = (point *) fastlookup(endptlist, i); + if (*pendpt == dummypoint) { + continue; // Do not reduce a virtual edge. + } + reduceflag = 0; + // Find the edge. + if (nonconvex) { + if (getedge(startpt, *pendpt, &searchtet)) { + dir = ACROSSVERT; + } else { + // The edge does not exist (was flipped). + dir = INTERSECT; + } + } else { + point2tetorg(startpt, searchtet); + dir = finddirection(&searchtet, *pendpt); + } + if (dir == ACROSSVERT) { + if (dest(searchtet) == *pendpt) { + // Do not flip a segment. + if (!issubseg(searchtet)) { + n = removeedgebyflips(&searchtet, &fc); + if (n == 2) { + reduceflag = 1; + } + } + } + } else { + // The edge has been flipped. + reduceflag = 1; + } + if (reduceflag) { + count++; + // Move the last vertex into this slot. + j = endptlist->objects - 1; + parypt = (point *) fastlookup(endptlist, j); + *pendpt = *parypt; + endptlist->objects--; + i--; + } + } // i + + if (count == 0) { + // No edge is reduced. + break; + } + + } // while (1) + + return (int) endptlist->objects; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// removevertexbyflips() Remove a vertex by flips. // +// // +// This routine attempts to remove the given vertex 'rempt' (p) from the // +// tetrahedralization (T) by a sequence of flips. // +// // +// The algorithm used here is a simple edge reduce method. Suppose there are // +// n edges connected at p. We try to reduce the number of edges by flipping // +// any edge (not a segment) that is connecting at p. // +// // +// Unless T is a Delaunay tetrahedralization, there is no guarantee that 'p' // +// can be successfully removed. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::removevertexbyflips(point steinerpt) +{ + triface *fliptets = NULL, wrktets[4]; + triface searchtet, spintet, neightet; + face parentsh, spinsh, checksh; + face leftseg, rightseg, checkseg; + point lpt = NULL, rpt = NULL, apexpt; //, *parypt; + flipconstraints fc; + enum verttype vt; + enum locateresult loc; + int valence, removeflag; + int slawson; + int t1ver; + int n, i; + + vt = pointtype(steinerpt); + + if (vt == FREESEGVERTEX) { + sdecode(point2sh(steinerpt), leftseg); + leftseg.shver = 0; + if (sdest(leftseg) == steinerpt) { + senext(leftseg, rightseg); + spivotself(rightseg); + rightseg.shver = 0; + } else { + rightseg = leftseg; + senext2(rightseg, leftseg); + spivotself(leftseg); + leftseg.shver = 0; + } + lpt = sorg(leftseg); + rpt = sdest(rightseg); + if (b->verbose > 2) { + printf(" Removing Steiner point %d in segment (%d, %d).\n", + pointmark(steinerpt), pointmark(lpt), pointmark(rpt)); + + } + } else if (vt == FREEFACETVERTEX) { + if (b->verbose > 2) { + printf(" Removing Steiner point %d in facet.\n", + pointmark(steinerpt)); + } + } else if (vt == FREEVOLVERTEX) { + if (b->verbose > 2) { + printf(" Removing Steiner point %d in volume.\n", + pointmark(steinerpt)); + } + } else if (vt == VOLVERTEX) { + if (b->verbose > 2) { + printf(" Removing a point %d in volume.\n", + pointmark(steinerpt)); + } + } else { + // It is not a Steiner point. + return 0; + } + + // Try to reduce the number of edges at 'p' by flips. + getvertexstar(1, steinerpt, cavetetlist, cavetetvertlist, NULL); + cavetetlist->restart(); // This list may be re-used. + if (cavetetvertlist->objects > 3l) { + valence = reduceedgesatvertex(steinerpt, cavetetvertlist); + } else { + valence = cavetetvertlist->objects; + } + cavetetvertlist->restart(); + + removeflag = 0; + + if (valence == 4) { + // Only 4 vertices (4 tets) left! 'p' is inside the convex hull of the 4 + // vertices. This case is due to that 'p' is not exactly on the segment. + point2tetorg(steinerpt, searchtet); + loc = INTETRAHEDRON; + removeflag = 1; + } else if (valence == 5) { + // There are 5 edges. + if (vt == FREESEGVERTEX) { + sstpivot1(leftseg, searchtet); + if (org(searchtet) != steinerpt) { + esymself(searchtet); + } + i = 0; // Count the numbe of tet at the edge [p,lpt]. + neightet.tet = NULL; // Init the face. + spintet = searchtet; + while (1) { + i++; + if (apex(spintet) == rpt) { + // Remember the face containing the edge [lpt, rpt]. + neightet = spintet; + } + fnextself(spintet); + if (spintet.tet == searchtet.tet) break; + } + if (i == 3) { + // This case has been checked below. + } else if (i == 4) { + // There are 4 tets sharing at [p,lpt]. There must be 4 tets sharing + // at [p,rpt]. There must be a face [p, lpt, rpt]. + if (apex(neightet) == rpt) { + // The edge (segment) has been already recovered! + // Check if a 6-to-2 flip is possible (to remove 'p'). + // Let 'searchtet' be [p,d,a,b] + esym(neightet, searchtet); + enextself(searchtet); + // Check if there are exactly three tets at edge [p,d]. + wrktets[0] = searchtet; // [p,d,a,b] + for (i = 0; i < 2; i++) { + fnext(wrktets[i], wrktets[i+1]); // [p,d,b,c], [p,d,c,a] + } + if (apex(wrktets[0]) == oppo(wrktets[2])) { + loc = ONFACE; + removeflag = 1; + } + } + } + } else if (vt == FREEFACETVERTEX) { + // It is possible to do a 6-to-2 flip to remove the vertex. + point2tetorg(steinerpt, searchtet); + // Get the three faces of 'searchtet' which share at p. + // All faces has p as origin. + wrktets[0] = searchtet; + wrktets[1] = searchtet; + esymself(wrktets[1]); + enextself(wrktets[1]); + wrktets[2] = searchtet; + eprevself(wrktets[2]); + esymself(wrktets[2]); + // All internal edges of the six tets have valance either 3 or 4. + // Get one edge which has valance 3. + searchtet.tet = NULL; + for (i = 0; i < 3; i++) { + spintet = wrktets[i]; + valence = 0; + while (1) { + valence++; + fnextself(spintet); + if (spintet.tet == wrktets[i].tet) break; + } + if (valence == 3) { + // Found the edge. + searchtet = wrktets[i]; + break; + } + } + // Note, we do not detach the three subfaces at p. + // They will be removed within a 4-to-1 flip. + loc = ONFACE; + removeflag = 1; + } + //removeflag = 1; + } + + if (!removeflag) { + if (vt == FREESEGVERTEX) { + // Check is it possible to recover the edge [lpt,rpt]. + // The condition to check is: Whether each tet containing 'leftseg' is + // adjacent to a tet containing 'rightseg'. + sstpivot1(leftseg, searchtet); + if (org(searchtet) != steinerpt) { + esymself(searchtet); + } + spintet = searchtet; + while (1) { + // Go to the bottom face of this tet. + eprev(spintet, neightet); + esymself(neightet); // [steinerpt, p1, p2, lpt] + // Get the adjacent tet. + fsymself(neightet); // [p1, steinerpt, p2, rpt] + if (oppo(neightet) != rpt) { + // Found a non-matching adjacent tet. + break; + } + fnextself(spintet); + if (spintet.tet == searchtet.tet) { + // 'searchtet' is [p,d,p1,p2]. + loc = ONEDGE; + removeflag = 1; + break; + } + } + } // if (vt == FREESEGVERTEX) + } + + if (!removeflag) { + if (vt == FREESEGVERTEX) { + // Check if the edge [lpt, rpt] exists. + if (getedge(lpt, rpt, &searchtet)) { + // We have recovered this edge. Shift the vertex into the volume. + // We can recover this edge if the subfaces are not recovered yet. + if (!checksubfaceflag) { + // Remove the vertex from the surface mesh. + // This will re-create the segment [lpt, rpt] and re-triangulate + // all the facets at the segment. + // Detach the subsegments from their surrounding tets. + for (i = 0; i < 2; i++) { + checkseg = (i == 0) ? leftseg : rightseg; + sstpivot1(checkseg, neightet); + spintet = neightet; + while (1) { + tssdissolve1(spintet); + fnextself(spintet); + if (spintet.tet == neightet.tet) break; + } + sstdissolve1(checkseg); + } // i + slawson = 1; // Do lawson flip after removal. + spivot(rightseg, parentsh); // 'rightseg' has p as its origin. + sremovevertex(steinerpt, &parentsh, &rightseg, slawson); + // Clear the list for new subfaces. + caveshbdlist->restart(); + // Insert the new segment. + sstbond1(rightseg, searchtet); + spintet = searchtet; + while (1) { + tssbond1(spintet, rightseg); + fnextself(spintet); + if (spintet.tet == searchtet.tet) break; + } + // The Steiner point has been shifted into the volume. + setpointtype(steinerpt, FREEVOLVERTEX); + st_segref_count--; + st_volref_count++; + return 1; + } // if (!checksubfaceflag) + } // if (getedge(...)) + } // if (vt == FREESEGVERTEX) + } // if (!removeflag) + + if (!removeflag) { + return 0; + } + + if (vt == FREESEGVERTEX) { + // Detach the subsegments from their surronding tets. + for (i = 0; i < 2; i++) { + checkseg = (i == 0) ? leftseg : rightseg; + sstpivot1(checkseg, neightet); + spintet = neightet; + while (1) { + tssdissolve1(spintet); + fnextself(spintet); + if (spintet.tet == neightet.tet) break; + } + sstdissolve1(checkseg); + } // i + if (checksubfaceflag) { + // Detach the subfaces at the subsegments from their attached tets. + for (i = 0; i < 2; i++) { + checkseg = (i == 0) ? leftseg : rightseg; + spivot(checkseg, parentsh); + if (parentsh.sh != NULL) { + spinsh = parentsh; + while (1) { + stpivot(spinsh, neightet); + if (neightet.tet != NULL) { + tsdissolve(neightet); + } + sesymself(spinsh); + stpivot(spinsh, neightet); + if (neightet.tet != NULL) { + tsdissolve(neightet); + } + stdissolve(spinsh); + spivotself(spinsh); // Go to the next subface. + if (spinsh.sh == parentsh.sh) break; + } + } + } // i + } // if (checksubfaceflag) + } + + if (loc == INTETRAHEDRON) { + // Collect the four tets containing 'p'. + fliptets = new triface[4]; + fliptets[0] = searchtet; // [p,d,a,b] + for (i = 0; i < 2; i++) { + fnext(fliptets[i], fliptets[i+1]); // [p,d,b,c], [p,d,c,a] + } + eprev(fliptets[0], fliptets[3]); + fnextself(fliptets[3]); // it is [a,p,b,c] + eprevself(fliptets[3]); + esymself(fliptets[3]); // [a,b,c,p]. + // Remove p by a 4-to-1 flip. + //flip41(fliptets, 1, 0, 0); + flip41(fliptets, 1, &fc); + //recenttet = fliptets[0]; + } else if (loc == ONFACE) { + // Let the original two tets be [a,b,c,d] and [b,a,c,e]. And p is in + // face [a,b,c]. Let 'searchtet' be the tet [p,d,a,b]. + // Collect the six tets containing 'p'. + fliptets = new triface[6]; + fliptets[0] = searchtet; // [p,d,a,b] + for (i = 0; i < 2; i++) { + fnext(fliptets[i], fliptets[i+1]); // [p,d,b,c], [p,d,c,a] + } + eprev(fliptets[0], fliptets[3]); + fnextself(fliptets[3]); // [a,p,b,e] + esymself(fliptets[3]); // [p,a,e,b] + eprevself(fliptets[3]); // [e,p,a,b] + for (i = 3; i < 5; i++) { + fnext(fliptets[i], fliptets[i+1]); // [e,p,b,c], [e,p,c,a] + } + if (vt == FREEFACETVERTEX) { + // We need to determine the location of three subfaces at p. + valence = 0; // Re-use it. + // Check if subfaces are all located in the lower three tets. + // i.e., [e,p,a,b], [e,p,b,c], and [e,p,c,a]. + for (i = 3; i < 6; i++) { + if (issubface(fliptets[i])) valence++; + } + if (valence > 0) { + // We must do 3-to-2 flip in the upper part. We simply re-arrange + // the six tets. + for (i = 0; i < 3; i++) { + esym(fliptets[i+3], wrktets[i]); + esym(fliptets[i], fliptets[i+3]); + fliptets[i] = wrktets[i]; + } + // Swap the last two pairs, i.e., [1]<->[[2], and [4]<->[5] + wrktets[1] = fliptets[1]; + fliptets[1] = fliptets[2]; + fliptets[2] = wrktets[1]; + wrktets[1] = fliptets[4]; + fliptets[4] = fliptets[5]; + fliptets[5] = wrktets[1]; + } + } + // Remove p by a 6-to-2 flip, which is a combination of two flips: + // a 3-to-2 (deletes the edge [e,p]), and + // a 4-to-1 (deletes the vertex p). + // First do a 3-to-2 flip on [e,p,a,b],[e,p,b,c],[e,p,c,a]. It creates + // two new tets: [a,b,c,p] and [b,a,c,e]. The new tet [a,b,c,p] is + // degenerate (has zero volume). It will be deleted in the followed + // 4-to-1 flip. + //flip32(&(fliptets[3]), 1, 0, 0); + flip32(&(fliptets[3]), 1, &fc); + // Second do a 4-to-1 flip on [p,d,a,b],[p,d,b,c],[p,d,c,a],[a,b,c,p]. + // This creates a new tet [a,b,c,d]. + //flip41(fliptets, 1, 0, 0); + flip41(fliptets, 1, &fc); + //recenttet = fliptets[0]; + } else if (loc == ONEDGE) { + // Let the original edge be [e,d] and p is in [e,d]. Assume there are n + // tets sharing at edge [e,d] originally. We number the link vertices + // of [e,d]: p_0, p_1, ..., p_n-1. 'searchtet' is [p,d,p_0,p_1]. + // Count the number of tets at edge [e,p] and [p,d] (this is n). + n = 0; + spintet = searchtet; + while (1) { + n++; + fnextself(spintet); + if (spintet.tet == searchtet.tet) break; + } + // Collect the 2n tets containing 'p'. + fliptets = new triface[2 * n]; + fliptets[0] = searchtet; // [p,b,p_0,p_1] + for (i = 0; i < (n - 1); i++) { + fnext(fliptets[i], fliptets[i+1]); // [p,d,p_i,p_i+1]. + } + eprev(fliptets[0], fliptets[n]); + fnextself(fliptets[n]); // [p_0,p,p_1,e] + esymself(fliptets[n]); // [p,p_0,e,p_1] + eprevself(fliptets[n]); // [e,p,p_0,p_1] + for (i = n; i < (2 * n - 1); i++) { + fnext(fliptets[i], fliptets[i+1]); // [e,p,p_i,p_i+1]. + } + // Remove p by a 2n-to-n flip, it is a sequence of n flips: + // - Do a 2-to-3 flip on + // [p_0,p_1,p,d] and + // [p,p_1,p_0,e]. + // This produces: + // [e,d,p_0,p_1], + // [e,d,p_1,p] (degenerated), and + // [e,d,p,p_0] (degenerated). + wrktets[0] = fliptets[0]; // [p,d,p_0,p_1] + eprevself(wrktets[0]); // [p_0,p,d,p_1] + esymself(wrktets[0]); // [p,p_0,p_1,d] + enextself(wrktets[0]); // [p_0,p_1,p,d] [0] + wrktets[1] = fliptets[n]; // [e,p,p_0,p_1] + enextself(wrktets[1]); // [p,p_0,e,p_1] + esymself(wrktets[1]); // [p_0,p,p_1,e] + eprevself(wrktets[1]); // [p_1,p_0,p,e] [1] + //flip23(wrktets, 1, 0, 0); + flip23(wrktets, 1, &fc); + // Save the new tet [e,d,p,p_0] (degenerated). + fliptets[n] = wrktets[2]; + // Save the new tet [e,d,p_0,p_1]. + fliptets[0] = wrktets[0]; + // - Repeat from i = 1 to n-2: (n - 2) flips + // - Do a 3-to-2 flip on + // [p,p_i,d,e], + // [p,p_i,e,p_i+1], and + // [p,p_i,p_i+1,d]. + // This produces: + // [d,e,p_i+1,p_i], and + // [e,d,p_i+1,p] (degenerated). + for (i = 1; i < (n - 1); i++) { + wrktets[0] = wrktets[1]; // [e,d,p_i,p] (degenerated). + enextself(wrktets[0]); // [d,p_i,e,p] (...) + esymself(wrktets[0]); // [p_i,d,p,e] (...) + eprevself(wrktets[0]); // [p,p_i,d,e] (degenerated) [0]. + wrktets[1] = fliptets[n+i]; // [e,p,p_i,p_i+1] + enextself(wrktets[1]); // [p,p_i,e,p_i+1] [1] + wrktets[2] = fliptets[i]; // [p,d,p_i,p_i+1] + eprevself(wrktets[2]); // [p_i,p,d,p_i+1] + esymself(wrktets[2]); // [p,p_i,p_i+1,d] [2] + //flip32(wrktets, 1, 0, 0); + flip32(wrktets, 1, &fc); + // Save the new tet [e,d,p_i,p_i+1]. // FOR DEBUG ONLY + fliptets[i] = wrktets[0]; // [d,e,p_i+1,p_i] // FOR DEBUG ONLY + esymself(fliptets[i]); // [e,d,p_i,p_i+1] // FOR DEBUG ONLY + } + // - Do a 4-to-1 flip on + // [p,p_0,e,d], [d,e,p_0,p], + // [p,p_0,d,p_n-1], [e,p_n-1,p_0,p], + // [p,p_0,p_n-1,e], [p_0,p_n-1,d,p], and + // [e,d,p_n-1,p]. + // This produces + // [e,d,p_n-1,p_0] and + // deletes p. + wrktets[3] = wrktets[1]; // [e,d,p_n-1,p] (degenerated) [3] + wrktets[0] = fliptets[n]; // [e,d,p,p_0] (degenerated) + eprevself(wrktets[0]); // [p,e,d,p_0] (...) + esymself(wrktets[0]); // [e,p,p_0,d] (...) + enextself(wrktets[0]); // [p,p_0,e,d] (degenerated) [0] + wrktets[1] = fliptets[n-1]; // [p,d,p_n-1,p_0] + esymself(wrktets[1]); // [d,p,p_0,p_n-1] + enextself(wrktets[1]); // [p,p_0,d,p_n-1] [1] + wrktets[2] = fliptets[2*n-1]; // [e,p,p_n-1,p_0] + enextself(wrktets[2]); // [p_p_n-1,e,p_0] + esymself(wrktets[2]); // [p_n-1,p,p_0,e] + enextself(wrktets[2]); // [p,p_0,p_n-1,e] [2] + //flip41(wrktets, 1, 0, 0); + flip41(wrktets, 1, &fc); + // Save the new tet [e,d,p_n-1,p_0] // FOR DEBUG ONLY + fliptets[n-1] = wrktets[0]; // [e,d,p_n-1,p_0] // FOR DEBUG ONLY + //recenttet = fliptets[0]; + } + + delete [] fliptets; + + if (vt == FREESEGVERTEX) { + // Remove the vertex from the surface mesh. + // This will re-create the segment [lpt, rpt] and re-triangulate + // all the facets at the segment. + // Only do lawson flip when subfaces are not recovery yet. + slawson = (checksubfaceflag ? 0 : 1); + spivot(rightseg, parentsh); // 'rightseg' has p as its origin. + sremovevertex(steinerpt, &parentsh, &rightseg, slawson); + + // The original segment is returned in 'rightseg'. + rightseg.shver = 0; + // Insert the new segment. + point2tetorg(lpt, searchtet); + finddirection(&searchtet, rpt); + sstbond1(rightseg, searchtet); + spintet = searchtet; + while (1) { + tssbond1(spintet, rightseg); + fnextself(spintet); + if (spintet.tet == searchtet.tet) break; + } + + if (checksubfaceflag) { + // Insert subfaces at segment [lpt,rpt] into the tetrahedralization. + spivot(rightseg, parentsh); + if (parentsh.sh != NULL) { + spinsh = parentsh; + while (1) { + if (sorg(spinsh) != lpt) { + sesymself(spinsh); + } + apexpt = sapex(spinsh); + // Find the adjacent tet of [lpt,rpt,apexpt]; + spintet = searchtet; + while (1) { + if (apex(spintet) == apexpt) { + tsbond(spintet, spinsh); + sesymself(spinsh); // Get to another side of this face. + fsym(spintet, neightet); + tsbond(neightet, spinsh); + sesymself(spinsh); // Get back to the original side. + break; + } + fnextself(spintet); + } + spivotself(spinsh); + if (spinsh.sh == parentsh.sh) break; + } + } + } // if (checksubfaceflag) + + // Clear the set of new subfaces. + caveshbdlist->restart(); + } // if (vt == FREESEGVERTEX) + + // The point has been removed. + if (pointtype(steinerpt) != UNUSEDVERTEX) { + setpointtype(steinerpt, UNUSEDVERTEX); + unuverts++; + } + if (vt != VOLVERTEX) { + // Update the correspinding counters. + if (vt == FREESEGVERTEX) { + st_segref_count--; + } else if (vt == FREEFACETVERTEX) { + st_facref_count--; + } else if (vt == FREEVOLVERTEX) { + st_volref_count--; + } + if (steinerleft > 0) steinerleft++; + } + + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// suppressbdrysteinerpoint() Suppress a boundary Steiner point // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::suppressbdrysteinerpoint(point steinerpt) +{ + face parentsh, spinsh, *parysh; + face leftseg, rightseg; + point lpt = NULL, rpt = NULL; + int i; + + verttype vt = pointtype(steinerpt); + + if (vt == FREESEGVERTEX) { + sdecode(point2sh(steinerpt), leftseg); + leftseg.shver = 0; + if (sdest(leftseg) == steinerpt) { + senext(leftseg, rightseg); + spivotself(rightseg); + rightseg.shver = 0; + } else { + rightseg = leftseg; + senext2(rightseg, leftseg); + spivotself(leftseg); + leftseg.shver = 0; + } + lpt = sorg(leftseg); + rpt = sdest(rightseg); + if (b->verbose > 2) { + printf(" Suppressing Steiner point %d in segment (%d, %d).\n", + pointmark(steinerpt), pointmark(lpt), pointmark(rpt)); + } + // Get all subfaces at the left segment [lpt, steinerpt]. + spivot(leftseg, parentsh); + if (parentsh.sh != NULL) { + // It is not a dangling segment. + spinsh = parentsh; + while (1) { + cavesegshlist->newindex((void **) &parysh); + *parysh = spinsh; + // Orient the face consistently. + if (sorg(*parysh)!= sorg(parentsh)) sesymself(*parysh); + spivotself(spinsh); + if (spinsh.sh == NULL) break; + if (spinsh.sh == parentsh.sh) break; + } + } + if (cavesegshlist->objects < 2) { + // It is a single segment. Not handle it yet. + cavesegshlist->restart(); + return 0; + } + } else if (vt == FREEFACETVERTEX) { + if (b->verbose > 2) { + printf(" Suppressing Steiner point %d from facet.\n", + pointmark(steinerpt)); + } + sdecode(point2sh(steinerpt), parentsh); + // A facet Steiner point. There are exactly two sectors. + for (i = 0; i < 2; i++) { + cavesegshlist->newindex((void **) &parysh); + *parysh = parentsh; + sesymself(parentsh); + } + } else { + return 0; + } + + triface searchtet, neightet, *parytet; + point pa, pb, pc, pd; + REAL v1[3], v2[3], len, u; + + REAL startpt[3] = {0,}, samplept[3] = {0,}, candpt[3] = {0,}; + REAL ori, minvol, smallvol; + int samplesize; + int it, j, k; + + int n = (int) cavesegshlist->objects; + point *newsteiners = new point[n]; + for (i = 0; i < n; i++) newsteiners[i] = NULL; + + // Search for each sector an interior vertex. + for (i = 0; i < cavesegshlist->objects; i++) { + parysh = (face *) fastlookup(cavesegshlist, i); + stpivot(*parysh, searchtet); + // Skip it if it is outside. + if (ishulltet(searchtet)) continue; + // Get the "half-ball". Tets in 'cavetetlist' all contain 'steinerpt' as + // opposite. Subfaces in 'caveshlist' all contain 'steinerpt' as apex. + // Moreover, subfaces are oriented towards the interior of the ball. + setpoint2tet(steinerpt, encode(searchtet)); + getvertexstar(0, steinerpt, cavetetlist, NULL, caveshlist); + // Calculate the searching vector. + pa = sorg(*parysh); + pb = sdest(*parysh); + pc = sapex(*parysh); + facenormal(pa, pb, pc, v1, 1, NULL); + len = sqrt(dot(v1, v1)); + v1[0] /= len; + v1[1] /= len; + v1[2] /= len; + if (vt == FREESEGVERTEX) { + parysh = (face *) fastlookup(cavesegshlist, (i + 1) % n); + pd = sapex(*parysh); + facenormal(pb, pa, pd, v2, 1, NULL); + len = sqrt(dot(v2, v2)); + 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); + break; + } + } + } + } // j + // Close the ball by adding the subfaces. + for (j = 0; j < caveshlist->objects; j++) { + parysh = (face *) fastlookup(caveshlist, j); + stpivot(*parysh, neightet); + cavetetlist->newindex((void **) &parytet); + *parytet = neightet; + } + // Search a best point inside the segment [startpt, steinerpt]. + it = 0; + samplesize = 100; + v1[0] = steinerpt[0] - startpt[0]; + v1[1] = steinerpt[1] - startpt[1]; + v1[2] = steinerpt[2] - startpt[2]; + minvol = -1.0; + while (it < 3) { + for (j = 1; j < samplesize - 1; j++) { + samplept[0] = startpt[0] + ((REAL) j / (REAL) samplesize) * v1[0]; + samplept[1] = startpt[1] + ((REAL) j / (REAL) samplesize) * v1[1]; + samplept[2] = startpt[2] + ((REAL) j / (REAL) samplesize) * v1[2]; + // Find the minimum volume for 'samplept'. + smallvol = -1; + for (k = 0; k < cavetetlist->objects; k++) { + parytet = (triface *) fastlookup(cavetetlist, k); + pa = org(*parytet); + pb = dest(*parytet); + pc = apex(*parytet); + ori = orient3d(pb, pa, pc, samplept); + if (ori <= 0) { + break; // An invalid tet. + } + if (smallvol == -1) { + smallvol = ori; + } else { + if (ori < smallvol) smallvol = ori; + } + } // k + if (k == cavetetlist->objects) { + // Found a valid point. Remember it. + if (minvol == -1.0) { + candpt[0] = samplept[0]; + candpt[1] = samplept[1]; + candpt[2] = samplept[2]; + minvol = smallvol; + } else { + if (minvol < smallvol) { + // It is a better location. Remember it. + candpt[0] = samplept[0]; + candpt[1] = samplept[1]; + candpt[2] = samplept[2]; + minvol = smallvol; + } else { + // No improvement of smallest volume. + // Since we are searching along the line [startpt, steinerpy], + // The smallest volume can only be decreased later. + break; + } + } + } + } // j + if (minvol > 0) break; + samplesize *= 10; + it++; + } // while (it < 3) + if (minvol == -1.0) { + // Failed to find a valid point. + cavetetlist->restart(); + caveshlist->restart(); + break; + } + // Create a new Steiner point inside this section. + makepoint(&(newsteiners[i]), FREEVOLVERTEX); + newsteiners[i][0] = candpt[0]; + newsteiners[i][1] = candpt[1]; + newsteiners[i][2] = candpt[2]; + cavetetlist->restart(); + caveshlist->restart(); + } // i + + if (i < cavesegshlist->objects) { + // Failed to suppress the vertex. + for (; i > 0; i--) { + if (newsteiners[i - 1] != NULL) { + pointdealloc(newsteiners[i - 1]); + } + } + delete [] newsteiners; + cavesegshlist->restart(); + return 0; + } + + // Remove p from the segment or the facet. + triface newtet, newface, spintet; + face newsh, neighsh; + face *splitseg, checkseg; + int slawson = 0; // Do not do flip afterword. + int t1ver; + + if (vt == FREESEGVERTEX) { + // Detach 'leftseg' and 'rightseg' from their adjacent tets. + // These two subsegments will be deleted. + sstpivot1(leftseg, neightet); + spintet = neightet; + while (1) { + tssdissolve1(spintet); + fnextself(spintet); + if (spintet.tet == neightet.tet) break; + } + sstpivot1(rightseg, neightet); + spintet = neightet; + while (1) { + tssdissolve1(spintet); + fnextself(spintet); + if (spintet.tet == neightet.tet) break; + } + } + + // Loop through all sectors bounded by facets at this segment. + // Within each sector, create a new Steiner point 'np', and replace 'p' + // by 'np' for all tets in this sector. + for (i = 0; i < cavesegshlist->objects; i++) { + parysh = (face *) fastlookup(cavesegshlist, i); + // 'parysh' is the face [lpt, steinerpt, #]. + stpivot(*parysh, neightet); + // Get all tets in this sector. + setpoint2tet(steinerpt, encode(neightet)); + getvertexstar(0, steinerpt, cavetetlist, NULL, caveshlist); + if (!ishulltet(neightet)) { + // Within each tet in the ball, replace 'p' by 'np'. + for (j = 0; j < cavetetlist->objects; j++) { + parytet = (triface *) fastlookup(cavetetlist, j); + setoppo(*parytet, newsteiners[i]); + } // j + // Point to a parent tet. + parytet = (triface *) fastlookup(cavetetlist, 0); + setpoint2tet(newsteiners[i], (tetrahedron) (parytet->tet)); + st_volref_count++; + if (steinerleft > 0) steinerleft--; + } + // Disconnect the set of boundary faces. They're temporarily open faces. + // They will be connected to the new tets after 'p' is removed. + for (j = 0; j < caveshlist->objects; j++) { + // Get a boundary face. + parysh = (face *) fastlookup(caveshlist, j); + stpivot(*parysh, neightet); + //assert(apex(neightet) == newpt); + // Clear the connection at this face. + dissolve(neightet); + tsdissolve(neightet); + } + // Clear the working lists. + cavetetlist->restart(); + caveshlist->restart(); + } // i + cavesegshlist->restart(); + + if (vt == FREESEGVERTEX) { + spivot(rightseg, parentsh); // 'rightseg' has p as its origin. + splitseg = &rightseg; + } else { + if (sdest(parentsh) == steinerpt) { + senextself(parentsh); + } else if (sapex(parentsh) == steinerpt) { + senext2self(parentsh); + } + splitseg = NULL; + } + sremovevertex(steinerpt, &parentsh, splitseg, slawson); + + if (vt == FREESEGVERTEX) { + // The original segment is returned in 'rightseg'. + rightseg.shver = 0; + } + + // For each new subface, create two new tets at each side of it. + // Both of the two new tets have its opposite be dummypoint. + for (i = 0; i < caveshbdlist->objects; i++) { + parysh = (face *) fastlookup(caveshbdlist, i); + sinfect(*parysh); // Mark it for connecting new tets. + newsh = *parysh; + pa = sorg(newsh); + pb = sdest(newsh); + pc = sapex(newsh); + maketetrahedron(&newtet); + maketetrahedron(&neightet); + setvertices(newtet, pa, pb, pc, dummypoint); + setvertices(neightet, pb, pa, pc, dummypoint); + bond(newtet, neightet); + tsbond(newtet, newsh); + sesymself(newsh); + tsbond(neightet, newsh); + } + // Temporarily increase the hullsize. + hullsize += (caveshbdlist->objects * 2l); + + if (vt == FREESEGVERTEX) { + // Connecting new tets at the recovered segment. + spivot(rightseg, parentsh); + spinsh = parentsh; + while (1) { + if (sorg(spinsh) != lpt) sesymself(spinsh); + // Get the new tet at this subface. + stpivot(spinsh, newtet); + tssbond1(newtet, rightseg); + // Go to the other face at this segment. + spivot(spinsh, neighsh); + if (sorg(neighsh) != lpt) sesymself(neighsh); + sesymself(neighsh); + stpivot(neighsh, neightet); + tssbond1(neightet, rightseg); + sstbond1(rightseg, neightet); + // Connecting two adjacent tets at this segment. + esymself(newtet); + esymself(neightet); + // Connect the two tets (at rightseg) together. + bond(newtet, neightet); + // Go to the next subface. + spivotself(spinsh); + if (spinsh.sh == parentsh.sh) break; + } + } + + // Connecting new tets at new subfaces together. + for (i = 0; i < caveshbdlist->objects; i++) { + parysh = (face *) fastlookup(caveshbdlist, i); + newsh = *parysh; + //assert(sinfected(newsh)); + // Each new subface contains two new tets. + for (k = 0; k < 2; k++) { + stpivot(newsh, newtet); + for (j = 0; j < 3; j++) { + // Check if this side is open. + esym(newtet, newface); + if (newface.tet[newface.ver & 3] == NULL) { + // An open face. Connect it to its adjacent tet. + sspivot(newsh, checkseg); + if (checkseg.sh != NULL) { + // A segment. It must not be the recovered segment. + tssbond1(newtet, checkseg); + sstbond1(checkseg, newtet); + } + spivot(newsh, neighsh); + if (neighsh.sh != NULL) { + // The adjacent subface exists. It's not a dangling segment. + if (sorg(neighsh) != sdest(newsh)) sesymself(neighsh); + stpivot(neighsh, neightet); + if (sinfected(neighsh)) { + esymself(neightet); + } else { + // Search for an open face at this edge. + spintet = neightet; + while (1) { + esym(spintet, searchtet); + fsym(searchtet, spintet); + if (spintet.tet == NULL) break; + } + // Found an open face at 'searchtet'. + neightet = searchtet; + } + } else { + // The edge (at 'newsh') is a dangling segment. + // Get an adjacent tet at this segment. + sstpivot1(checkseg, neightet); + if (org(neightet) != sdest(newsh)) esymself(neightet); + // Search for an open face at this edge. + spintet = neightet; + while (1) { + esym(spintet, searchtet); + fsym(searchtet, spintet); + if (spintet.tet == NULL) break; + } + // Found an open face at 'searchtet'. + neightet = searchtet; + } + pc = apex(newface); + if (apex(neightet) == steinerpt) { + // Exterior case. The 'neightet' is a hull tet which contain + // 'steinerpt'. It will be deleted after 'steinerpt' is removed. + caveoldtetlist->newindex((void **) &parytet); + *parytet = neightet; + // Connect newface to the adjacent hull tet of 'neightet', which + // has the same edge as 'newface', and does not has 'steinerpt'. + fnextself(neightet); + } else { + if (pc == dummypoint) { + if (apex(neightet) != dummypoint) { + setapex(newface, apex(neightet)); + // A hull tet has turned into an interior tet. + hullsize--; // Must update the hullsize. + } + } + } + bond(newface, neightet); + } // if (newface.tet[newface.ver & 3] == NULL) + enextself(newtet); + senextself(newsh); + } // j + sesymself(newsh); + } // k + } // i + + // Unmark all new subfaces. + for (i = 0; i < caveshbdlist->objects; i++) { + parysh = (face *) fastlookup(caveshbdlist, i); + suninfect(*parysh); + } + caveshbdlist->restart(); + + if (caveoldtetlist->objects > 0l) { + // Delete hull tets which contain 'steinerpt'. + for (i = 0; i < caveoldtetlist->objects; i++) { + parytet = (triface *) fastlookup(caveoldtetlist, i); + tetrahedrondealloc(parytet->tet); + } + // Must update the hullsize. + hullsize -= caveoldtetlist->objects; + caveoldtetlist->restart(); + } + + setpointtype(steinerpt, UNUSEDVERTEX); + unuverts++; + if (vt == FREESEGVERTEX) { + st_segref_count--; + } else { // vt == FREEFACETVERTEX + st_facref_count--; + } + if (steinerleft > 0) steinerleft++; // We've removed a Steiner points. + + + point *parypt; + int steinercount = 0; + + int bak_fliplinklevel = b->fliplinklevel; + b->fliplinklevel = 100000; // Unlimited flip level. + + // Try to remove newly added Steiner points. + for (i = 0; i < n; i++) { + if (newsteiners[i] != NULL) { + if (!removevertexbyflips(newsteiners[i])) { + if (b->supsteiner_level > 0) { // Not -Y/0 + // Save it in subvertstack for removal. + subvertstack->newindex((void **) &parypt); + *parypt = newsteiners[i]; + } + steinercount++; + } + } + } + + b->fliplinklevel = bak_fliplinklevel; + + if (steinercount > 0) { + if (b->verbose > 2) { + printf(" Added %d interior Steiner points.\n", steinercount); + } + } + + delete [] newsteiners; + + return 1; +} + + +/////////////////////////////////////////////////////////////////////////////// +// // +// suppresssteinerpoints() Suppress Steiner points. // +// // +// All Steiner points have been saved in 'subvertstack' in the routines // +// carveholes() and suppresssteinerpoint(). // +// Each Steiner point is either removed or shifted into the interior. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::suppresssteinerpoints() +{ + + if (!b->quiet) { + printf("Suppressing Steiner points ...\n"); + } + + point rempt, *parypt; + + int bak_fliplinklevel = b->fliplinklevel; + b->fliplinklevel = 100000; // Unlimited flip level. + int suppcount = 0, remcount = 0; + int i; + + // Try to suppress boundary Steiner points. + for (i = 0; i < subvertstack->objects; i++) { + parypt = (point *) fastlookup(subvertstack, i); + rempt = *parypt; + if (pointtype(rempt) != UNUSEDVERTEX) { + if ((pointtype(rempt) == FREESEGVERTEX) || + (pointtype(rempt) == FREEFACETVERTEX)) { + if (suppressbdrysteinerpoint(rempt)) { + suppcount++; + } + } + } + } // i + + if (suppcount > 0) { + if (b->verbose) { + printf(" Suppressed %d boundary Steiner points.\n", suppcount); + } + } + + if (b->supsteiner_level > 0) { // -Y/1 + for (i = 0; i < subvertstack->objects; i++) { + parypt = (point *) fastlookup(subvertstack, i); + rempt = *parypt; + if (pointtype(rempt) != UNUSEDVERTEX) { + if (pointtype(rempt) == FREEVOLVERTEX) { + if (removevertexbyflips(rempt)) { + remcount++; + } + } + } + } + } + + if (remcount > 0) { + if (b->verbose) { + printf(" Removed %d interior Steiner points.\n", remcount); + } + } + + b->fliplinklevel = bak_fliplinklevel; + + if (b->supsteiner_level > 1) { // -Y/2 + // Smooth interior Steiner points. + optparameters opm; + triface *parytet; + point *ppt; + REAL ori; + int smtcount, count, ivcount; + int nt, j; + + // Point smooth options. + opm.max_min_volume = 1; + opm.numofsearchdirs = 20; + opm.searchstep = 0.001; + opm.maxiter = 30; // Limit the maximum iterations. + + smtcount = 0; + + do { + + nt = 0; + + while (1) { + count = 0; + ivcount = 0; // Clear the inverted count. + + for (i = 0; i < subvertstack->objects; i++) { + parypt = (point *) fastlookup(subvertstack, i); + rempt = *parypt; + if (pointtype(rempt) == FREEVOLVERTEX) { + getvertexstar(1, rempt, cavetetlist, NULL, NULL); + // Calculate the initial smallest volume (maybe zero or negative). + for (j = 0; j < cavetetlist->objects; j++) { + parytet = (triface *) fastlookup(cavetetlist, j); + ppt = (point *) &(parytet->tet[4]); + ori = orient3dfast(ppt[1], ppt[0], ppt[2], ppt[3]); + if (j == 0) { + opm.initval = ori; + } else { + if (opm.initval > ori) opm.initval = ori; + } + } + if (smoothpoint(rempt, cavetetlist, 1, &opm)) { + count++; + } + if (opm.imprval <= 0.0) { + ivcount++; // The mesh contains inverted elements. + } + cavetetlist->restart(); + } + } // i + + smtcount += count; + + if (count == 0) { + // No point has been smoothed. + break; + } + + nt++; + if (nt > 2) { + break; // Already three iterations. + } + } // while + + if (ivcount > 0) { + // There are inverted elements! + if (opm.maxiter > 0) { + // Set unlimited smoothing steps. Try again. + opm.numofsearchdirs = 30; + opm.searchstep = 0.0001; + opm.maxiter = -1; + continue; + } + } + + break; + } while (1); // Additional loop for (ivcount > 0) + + if (ivcount > 0) { + printf("BUG Report! The mesh contain inverted elements.\n"); + } + + if (b->verbose) { + if (smtcount > 0) { + printf(" Smoothed %d Steiner points.\n", smtcount); + } + } + } // -Y2 + + subvertstack->restart(); + + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// recoverboundary() Recover segments and facets. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::recoverboundary(clock_t& tv) +{ + arraypool *misseglist, *misshlist; + arraypool *bdrysteinerptlist; + face searchsh, *parysh; + face searchseg, *paryseg; + point rempt, *parypt; + long ms; // The number of missing segments/subfaces. + int nit; // The number of iterations. + int s, i; + + // Counters. + long bak_segref_count, bak_facref_count, bak_volref_count; + + if (!b->quiet) { + printf("Recovering boundaries...\n"); + } + + + if (b->verbose) { + printf(" Recovering segments.\n"); + } + + // Segments will be introduced. + checksubsegflag = 1; + + misseglist = new arraypool(sizeof(face), 8); + bdrysteinerptlist = new arraypool(sizeof(point), 8); + + // In random order. + subsegs->traversalinit(); + for (i = 0; i < subsegs->items; i++) { + s = randomnation(i + 1); + // Move the s-th seg to the i-th. + subsegstack->newindex((void **) &paryseg); + *paryseg = * (face *) fastlookup(subsegstack, s); + // Put i-th seg to be the s-th. + searchseg.sh = shellfacetraverse(subsegs); + paryseg = (face *) fastlookup(subsegstack, s); + *paryseg = searchseg; + } + + // The init number of missing segments. + ms = subsegs->items; + nit = 0; + if (b->fliplinklevel < 0) { + autofliplinklevel = 1; // Init value. + } + + // First, trying to recover segments by only doing flips. + while (1) { + recoversegments(misseglist, 0, 0); + + if (misseglist->objects > 0) { + if (b->fliplinklevel >= 0) { + break; + } else { + if (misseglist->objects >= ms) { + nit++; + if (nit >= 3) { + //break; + // Do the last round with unbounded flip link level. + b->fliplinklevel = 100000; + } + } else { + ms = misseglist->objects; + if (nit > 0) { + nit--; + } + } + for (i = 0; i < misseglist->objects; i++) { + subsegstack->newindex((void **) &paryseg); + *paryseg = * (face *) fastlookup(misseglist, i); + } + misseglist->restart(); + autofliplinklevel+=b->fliplinklevelinc; + } + } else { + // All segments are recovered. + break; + } + } // while (1) + + if (b->verbose) { + printf(" %ld (%ld) segments are recovered (missing).\n", + subsegs->items - misseglist->objects, misseglist->objects); + } + + if (misseglist->objects > 0) { + // Second, trying to recover segments by doing more flips (fullsearch). + while (misseglist->objects > 0) { + ms = misseglist->objects; + for (i = 0; i < misseglist->objects; i++) { + subsegstack->newindex((void **) &paryseg); + *paryseg = * (face *) fastlookup(misseglist, i); + } + misseglist->restart(); + + recoversegments(misseglist, 1, 0); + + if (misseglist->objects < ms) { + // The number of missing segments is reduced. + continue; + } else { + break; + } + } + if (b->verbose) { + printf(" %ld (%ld) segments are recovered (missing).\n", + subsegs->items - misseglist->objects, misseglist->objects); + } + } + + if (misseglist->objects > 0) { + // Third, trying to recover segments by doing more flips (fullsearch) + // and adding Steiner points in the volume. + while (misseglist->objects > 0) { + ms = misseglist->objects; + for (i = 0; i < misseglist->objects; i++) { + subsegstack->newindex((void **) &paryseg); + *paryseg = * (face *) fastlookup(misseglist, i); + } + misseglist->restart(); + + recoversegments(misseglist, 1, 1); + + if (misseglist->objects < ms) { + // The number of missing segments is reduced. + continue; + } else { + break; + } + } + if (b->verbose) { + printf(" Added %ld Steiner points in volume.\n", st_volref_count); + } + } + + if (misseglist->objects > 0) { + // Last, trying to recover segments by doing more flips (fullsearch), + // and adding Steiner points in the volume, and splitting segments. + long bak_inpoly_count = st_volref_count; //st_inpoly_count; + for (i = 0; i < misseglist->objects; i++) { + subsegstack->newindex((void **) &paryseg); + *paryseg = * (face *) fastlookup(misseglist, i); + } + misseglist->restart(); + + recoversegments(misseglist, 1, 2); + + if (b->verbose) { + printf(" Added %ld Steiner points in segments.\n", st_segref_count); + if (st_volref_count > bak_inpoly_count) { + printf(" Added another %ld Steiner points in volume.\n", + st_volref_count - bak_inpoly_count); + } + } + } + + + if (st_segref_count > 0) { + // Try to remove the Steiner points added in segments. + bak_segref_count = st_segref_count; + bak_volref_count = st_volref_count; + for (i = 0; i < subvertstack->objects; i++) { + // Get the Steiner point. + parypt = (point *) fastlookup(subvertstack, i); + rempt = *parypt; + if (!removevertexbyflips(rempt)) { + // Save it in list. + bdrysteinerptlist->newindex((void **) &parypt); + *parypt = rempt; + } + } + if (b->verbose) { + if (st_segref_count < bak_segref_count) { + if (bak_volref_count < st_volref_count) { + printf(" Suppressed %ld Steiner points in segments.\n", + st_volref_count - bak_volref_count); + } + if ((st_segref_count + (st_volref_count - bak_volref_count)) < + bak_segref_count) { + printf(" Removed %ld Steiner points in segments.\n", + bak_segref_count - + (st_segref_count + (st_volref_count - bak_volref_count))); + } + } + } + subvertstack->restart(); + } + + + tv = clock(); + + if (b->verbose) { + printf(" Recovering facets.\n"); + } + + // Subfaces will be introduced. + checksubfaceflag = 1; + + misshlist = new arraypool(sizeof(face), 8); + + // Randomly order the subfaces. + subfaces->traversalinit(); + for (i = 0; i < subfaces->items; i++) { + s = randomnation(i + 1); + // Move the s-th subface to the i-th. + subfacstack->newindex((void **) &parysh); + *parysh = * (face *) fastlookup(subfacstack, s); + // Put i-th subface to be the s-th. + searchsh.sh = shellfacetraverse(subfaces); + parysh = (face *) fastlookup(subfacstack, s); + *parysh = searchsh; + } + + ms = subfaces->items; + nit = 0; + b->fliplinklevel = -1; // Init. + if (b->fliplinklevel < 0) { + autofliplinklevel = 1; // Init value. + } + + while (1) { + recoversubfaces(misshlist, 0); + + if (misshlist->objects > 0) { + if (b->fliplinklevel >= 0) { + break; + } else { + if (misshlist->objects >= ms) { + nit++; + if (nit >= 3) { + //break; + // Do the last round with unbounded flip link level. + b->fliplinklevel = 100000; + } + } else { + ms = misshlist->objects; + if (nit > 0) { + nit--; + } + } + for (i = 0; i < misshlist->objects; i++) { + subfacstack->newindex((void **) &parysh); + *parysh = * (face *) fastlookup(misshlist, i); + } + misshlist->restart(); + autofliplinklevel+=b->fliplinklevelinc; + } + } else { + // All subfaces are recovered. + break; + } + } // while (1) + + if (b->verbose) { + printf(" %ld (%ld) subfaces are recovered (missing).\n", + subfaces->items - misshlist->objects, misshlist->objects); + } + + if (misshlist->objects > 0) { + // There are missing subfaces. Add Steiner points. + for (i = 0; i < misshlist->objects; i++) { + subfacstack->newindex((void **) &parysh); + *parysh = * (face *) fastlookup(misshlist, i); + } + misshlist->restart(); + + recoversubfaces(NULL, 1); + + if (b->verbose) { + printf(" Added %ld Steiner points in facets.\n", st_facref_count); + } + } + + + if (st_facref_count > 0) { + // Try to remove the Steiner points added in facets. + bak_facref_count = st_facref_count; + for (i = 0; i < subvertstack->objects; i++) { + // Get the Steiner point. + parypt = (point *) fastlookup(subvertstack, i); + rempt = *parypt; + if (!removevertexbyflips(*parypt)) { + // Save it in list. + bdrysteinerptlist->newindex((void **) &parypt); + *parypt = rempt; + } + } + if (b->verbose) { + if (st_facref_count < bak_facref_count) { + printf(" Removed %ld Steiner points in facets.\n", + bak_facref_count - st_facref_count); + } + } + subvertstack->restart(); + } + + + if (bdrysteinerptlist->objects > 0) { + if (b->verbose) { + printf(" %ld Steiner points remained in boundary.\n", + bdrysteinerptlist->objects); + } + } // if + + + // Accumulate the dynamic memory. + totalworkmemory += (misseglist->totalmemory + misshlist->totalmemory + + bdrysteinerptlist->totalmemory); + + delete bdrysteinerptlist; + delete misseglist; + delete misshlist; +} + +//// //// +//// //// +//// steiner_cxx ////////////////////////////////////////////////////////////// + + +//// reconstruct_cxx ////////////////////////////////////////////////////////// +//// //// +//// //// + +/////////////////////////////////////////////////////////////////////////////// +// // +// carveholes() Remove tetrahedra not in the mesh domain. // +// // +/////////////////////////////////////////////////////////////////////////////// + + +void tetgenmesh::carveholes() +{ + arraypool *tetarray, *hullarray; + triface tetloop, neightet, *parytet, *parytet1; + triface *regiontets = NULL; + face checksh, *parysh; + face checkseg; + point ptloop, *parypt; + int t1ver; + int i, j, k; + + if (!b->quiet) { + if (b->convex) { + printf("Marking exterior tetrahedra ...\n"); + } else { + printf("Removing exterior tetrahedra ...\n"); + } + } + + // Initialize the pool of exterior tets. + tetarray = new arraypool(sizeof(triface), 10); + hullarray = new arraypool(sizeof(triface), 10); + + // Collect unprotected tets and hull tets. + tetrahedrons->traversalinit(); + tetloop.ver = 11; // The face opposite to dummypoint. + tetloop.tet = alltetrahedrontraverse(); + while (tetloop.tet != (tetrahedron *) NULL) { + if (ishulltet(tetloop)) { + // Is this side protected by a subface? + if (!issubface(tetloop)) { + // Collect an unprotected hull tet and tet. + infect(tetloop); + hullarray->newindex((void **) &parytet); + *parytet = tetloop; + // tetloop's face number is 11 & 3 = 3. + decode(tetloop.tet[3], neightet); + if (!infected(neightet)) { + infect(neightet); + tetarray->newindex((void **) &parytet); + *parytet = neightet; + } + } + } + tetloop.tet = alltetrahedrontraverse(); + } + + if (in->numberofholes > 0) { + // Mark as infected any tets inside volume holes. + for (i = 0; i < 3 * in->numberofholes; i += 3) { + // Search a tet containing the i-th hole point. + neightet.tet = NULL; + randomsample(&(in->holelist[i]), &neightet); + if (locate(&(in->holelist[i]), &neightet) != OUTSIDE) { + // The tet 'neightet' contain this point. + if (!infected(neightet)) { + infect(neightet); + tetarray->newindex((void **) &parytet); + *parytet = neightet; + // Add its adjacent tet if it is not protected. + if (!issubface(neightet)) { + decode(neightet.tet[neightet.ver & 3], tetloop); + if (!infected(tetloop)) { + infect(tetloop); + if (ishulltet(tetloop)) { + hullarray->newindex((void **) &parytet); + } else { + tetarray->newindex((void **) &parytet); + } + *parytet = tetloop; + } + } + else { + // It is protected. Check if its adjacent tet is a hull tet. + decode(neightet.tet[neightet.ver & 3], tetloop); + if (ishulltet(tetloop)) { + // It is hull tet, add it into the list. Moreover, the subface + // is dead, i.e., both sides are in exterior. + if (!infected(tetloop)) { + infect(tetloop); + hullarray->newindex((void **) &parytet); + *parytet = tetloop; + } + } + if (infected(tetloop)) { + // Both sides of this subface are in exterior. + tspivot(neightet, checksh); + sinfect(checksh); // Only queue it once. + subfacstack->newindex((void **) &parysh); + *parysh = checksh; + } + } + } // if (!infected(neightet)) + } else { + // A hole point locates outside of the convex hull. + if (!b->quiet) { + printf("Warning: The %d-th hole point ", i/3 + 1); + printf("lies outside the convex hull.\n"); + } + } + } // i + } // if (in->numberofholes > 0) + + if (b->regionattrib && (in->numberofregions > 0)) { // -A option. + // Record the tetrahedra that contains the region points for assigning + // region attributes after the holes have been carved. + regiontets = new triface[in->numberofregions]; + // Mark as marktested any tetrahedra inside volume regions. + for (i = 0; i < 5 * in->numberofregions; i += 5) { + // Search a tet containing the i-th region point. + neightet.tet = NULL; + randomsample(&(in->regionlist[i]), &neightet); + if (locate(&(in->regionlist[i]), &neightet) != OUTSIDE) { + regiontets[i/5] = neightet; + } else { + if (!b->quiet) { + printf("Warning: The %d-th region point ", i/5+1); + printf("lies outside the convex hull.\n"); + } + regiontets[i/5].tet = NULL; + } + } + } + + // Collect all exterior tets (in concave place and in holes). + for (i = 0; i < tetarray->objects; i++) { + parytet = (triface *) fastlookup(tetarray, i); + j = (parytet->ver & 3); // j is the current face number. + // Check the other three adjacent tets. + for (k = 1; k < 4; k++) { + decode(parytet->tet[(j + k) % 4], neightet); + // neightet may be a hull tet. + if (!infected(neightet)) { + // Is neightet protected by a subface. + if (!issubface(neightet)) { + // Not proected. Collect it. (It must not be a hull tet). + infect(neightet); + tetarray->newindex((void **) &parytet1); + *parytet1 = neightet; + } else { + // Protected. Check if it is a hull tet. + if (ishulltet(neightet)) { + // A hull tet. Collect it. + infect(neightet); + hullarray->newindex((void **) &parytet1); + *parytet1 = neightet; + // Both sides of this subface are exterior. + tspivot(neightet, checksh); + // Queue this subface (to be deleted later). + sinfect(checksh); // Only queue it once. + subfacstack->newindex((void **) &parysh); + *parysh = checksh; + } + } + } else { + // Both sides of this face are in exterior. + // If there is a subface. It should be collected. + if (issubface(neightet)) { + tspivot(neightet, checksh); + if (!sinfected(checksh)) { + sinfect(checksh); + subfacstack->newindex((void **) &parysh); + *parysh = checksh; + } + } + } + } // j, k + } // i + + if (b->regionattrib && (in->numberofregions > 0)) { + // Re-check saved region tets to see if they lie outside. + for (i = 0; i < in->numberofregions; i++) { + if (infected(regiontets[i])) { + if (b->verbose) { + printf("Warning: The %d-th region point ", i+1); + printf("lies in the exterior of the domain.\n"); + } + regiontets[i].tet = NULL; + } + } + } + + // Collect vertices which point to infected tets. These vertices + // may get deleted after the removal of exterior tets. + // If -Y1 option is used, collect all Steiner points for removal. + // The lists 'cavetetvertlist' and 'subvertstack' are re-used. + points->traversalinit(); + ptloop = pointtraverse(); + while (ptloop != NULL) { + if ((pointtype(ptloop) != UNUSEDVERTEX) && + (pointtype(ptloop) != DUPLICATEDVERTEX)) { + decode(point2tet(ptloop), neightet); + if (infected(neightet)) { + cavetetvertlist->newindex((void **) &parypt); + *parypt = ptloop; + } + if (b->nobisect && (b->supsteiner_level > 0)) { // -Y/1 + // Queue it if it is a Steiner point. + if (pointmark(ptloop) > + (in->numberofpoints - (in->firstnumber ? 0 : 1))) { + subvertstack->newindex((void **) &parypt); + *parypt = ptloop; + } + } + } + ptloop = pointtraverse(); + } + + if (!b->convex && (tetarray->objects > 0l)) { // No -c option. + // Remove exterior tets. Hull tets are updated. + arraypool *newhullfacearray; + triface hulltet, casface; + face segloop, *paryseg; + point pa, pb, pc; + long delsegcount = 0l; + + // Collect segments which point to infected tets. Some segments + // may get deleted after the removal of exterior tets. + subsegs->traversalinit(); + segloop.sh = shellfacetraverse(subsegs); + while (segloop.sh != NULL) { + sstpivot1(segloop, neightet); + if (infected(neightet)) { + subsegstack->newindex((void **) &paryseg); + *paryseg = segloop; + } + segloop.sh = shellfacetraverse(subsegs); + } + + newhullfacearray = new arraypool(sizeof(triface), 10); + + // Create and save new hull tets. + for (i = 0; i < tetarray->objects; i++) { + parytet = (triface *) fastlookup(tetarray, i); + for (j = 0; j < 4; j++) { + decode(parytet->tet[j], tetloop); + if (!infected(tetloop)) { + // Found a new hull face (must be a subface). + tspivot(tetloop, checksh); + maketetrahedron(&hulltet); + pa = org(tetloop); + pb = dest(tetloop); + pc = apex(tetloop); + setvertices(hulltet, pb, pa, pc, dummypoint); + bond(tetloop, hulltet); + // Update the subface-to-tet map. + sesymself(checksh); + tsbond(hulltet, checksh); + // Update the segment-to-tet map. + for (k = 0; k < 3; k++) { + if (issubseg(tetloop)) { + tsspivot1(tetloop, checkseg); + tssbond1(hulltet, checkseg); + sstbond1(checkseg, hulltet); + } + enextself(tetloop); + eprevself(hulltet); + } + // Update the point-to-tet map. + setpoint2tet(pa, (tetrahedron) tetloop.tet); + setpoint2tet(pb, (tetrahedron) tetloop.tet); + setpoint2tet(pc, (tetrahedron) tetloop.tet); + // Save the exterior tet at this hull face. It still holds pointer + // to the adjacent interior tet. Use it to connect new hull tets. + newhullfacearray->newindex((void **) &parytet1); + parytet1->tet = parytet->tet; + parytet1->ver = j; + } // if (!infected(tetloop)) + } // j + } // i + + // Connect new hull tets. + for (i = 0; i < newhullfacearray->objects; i++) { + parytet = (triface *) fastlookup(newhullfacearray, i); + fsym(*parytet, neightet); + // Get the new hull tet. + fsym(neightet, hulltet); + for (j = 0; j < 3; j++) { + esym(hulltet, casface); + if (casface.tet[casface.ver & 3] == NULL) { + // Since the boundary of the domain may not be a manifold, we + // find the adjacent hull face by traversing the tets in the + // exterior (which are all infected tets). + neightet = *parytet; + while (1) { + fnextself(neightet); + if (!infected(neightet)) break; + } + if (!ishulltet(neightet)) { + // An interior tet. Get the new hull tet. + fsymself(neightet); + esymself(neightet); + } + // Bond them together. + bond(casface, neightet); + } + enextself(hulltet); + enextself(*parytet); + } // j + } // i + + if (subfacstack->objects > 0l) { + // Remove all subfaces which do not attach to any tetrahedron. + // Segments which are not attached to any subfaces and tets + // are deleted too. + face casingout, casingin; + + for (i = 0; i < subfacstack->objects; i++) { + parysh = (face *) fastlookup(subfacstack, i); + if (i == 0) { + if (b->verbose) { + printf("Warning: Removed an exterior face (%d, %d, %d) #%d\n", + pointmark(sorg(*parysh)), pointmark(sdest(*parysh)), + pointmark(sapex(*parysh)), shellmark(*parysh)); + } + } + // Dissolve this subface from face links. + for (j = 0; j < 3; j++) { + spivot(*parysh, casingout); + sspivot(*parysh, checkseg); + if (casingout.sh != NULL) { + casingin = casingout; + while (1) { + spivot(casingin, checksh); + if (checksh.sh == parysh->sh) break; + casingin = checksh; + } + if (casingin.sh != casingout.sh) { + // Update the link: ... -> casingin -> casingout ->... + sbond1(casingin, casingout); + } else { + // Only one subface at this edge is left. + sdissolve(casingout); + } + if (checkseg.sh != NULL) { + // Make sure the segment does not connect to a dead one. + ssbond(casingout, checkseg); + } + } else { + if (checkseg.sh != NULL) { + //if (checkseg.sh[3] != NULL) { + if (delsegcount == 0) { + if (b->verbose) { + printf("Warning: Removed an exterior segment (%d, %d) #%d\n", + pointmark(sorg(checkseg)), pointmark(sdest(checkseg)), + shellmark(checkseg)); + } + } + shellfacedealloc(subsegs, checkseg.sh); + delsegcount++; + } + } + senextself(*parysh); + } // j + // Delete this subface. + shellfacedealloc(subfaces, parysh->sh); + } // i + if (b->verbose) { + printf(" Deleted %ld subfaces.\n", subfacstack->objects); + } + subfacstack->restart(); + } // if (subfacstack->objects > 0l) + + if (subsegstack->objects > 0l) { + for (i = 0; i < subsegstack->objects; i++) { + paryseg = (face *) fastlookup(subsegstack, i); + if (paryseg->sh && (paryseg->sh[3] != NULL)) { + sstpivot1(*paryseg, neightet); + if (infected(neightet)) { + if (b->verbose) { + printf("Warning: Removed an exterior segment (%d, %d) #%d\n", + pointmark(sorg(*paryseg)), pointmark(sdest(*paryseg)), + shellmark(*paryseg)); + } + shellfacedealloc(subsegs, paryseg->sh); + delsegcount++; + } + } + } + subsegstack->restart(); + } // if (subsegstack->objects > 0l) + + if (delsegcount > 0) { + if (b->verbose) { + printf(" Deleted %ld segments.\n", delsegcount); + } + } + + if (cavetetvertlist->objects > 0l) { + // Some vertices may lie in exterior. Marke them as UNUSEDVERTEX. + long delvertcount = unuverts; + 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 { + st_volref_count--; + } + delsteinercount++; + if (steinerleft > 0) steinerleft++; + } + setpointtype(*parypt, UNUSEDVERTEX); + unuverts++; + } + } + + if (b->verbose) { + if (unuverts > delvertcount) { + if (delsteinercount > 0l) { + if (unuverts > (delvertcount + delsteinercount)) { + printf(" Removed %ld exterior input vertices.\n", + unuverts - delvertcount - delsteinercount); + } + printf(" Removed %ld exterior Steiner vertices.\n", + delsteinercount); + } else { + printf(" Removed %ld exterior input vertices.\n", + unuverts - delvertcount); + } + } + } + cavetetvertlist->restart(); + // Comment: 'subvertstack' will be cleaned in routine + // suppresssteinerpoints(). + } // if (cavetetvertlist->objects > 0l) + + // Update the hull size. + hullsize += (newhullfacearray->objects - hullarray->objects); + + // Delete all exterior tets and old hull tets. + for (i = 0; i < tetarray->objects; i++) { + parytet = (triface *) fastlookup(tetarray, i); + tetrahedrondealloc(parytet->tet); + } + tetarray->restart(); + + for (i = 0; i < hullarray->objects; i++) { + parytet = (triface *) fastlookup(hullarray, i); + tetrahedrondealloc(parytet->tet); + } + hullarray->restart(); + + delete newhullfacearray; + } // if (!b->convex && (tetarray->objects > 0l)) + + if (b->convex && (tetarray->objects > 0l)) { // With -c option + // In this case, all exterior tets get a region marker '-1'. + int attrnum = numelemattrib - 1; + + for (i = 0; i < tetarray->objects; i++) { + parytet = (triface *) fastlookup(tetarray, i); + setelemattribute(parytet->tet, attrnum, -1); + } + tetarray->restart(); + + for (i = 0; i < hullarray->objects; i++) { + parytet = (triface *) fastlookup(hullarray, i); + uninfect(*parytet); + } + hullarray->restart(); + + if (subfacstack->objects > 0l) { + for (i = 0; i < subfacstack->objects; i++) { + parysh = (face *) fastlookup(subfacstack, i); + suninfect(*parysh); + } + subfacstack->restart(); + } + + if (cavetetvertlist->objects > 0l) { + cavetetvertlist->restart(); + } + } // if (b->convex && (tetarray->objects > 0l)) + + if (b->regionattrib) { // With -A option. + if (!b->quiet) { + printf("Spreading region attributes.\n"); + } + REAL volume; + int attr, maxattr = 0; // Choose a small number here. + int attrnum = numelemattrib - 1; + // Comment: The element region marker is at the end of the list of + // the element attributes. + int regioncount = 0; + + // If has user-defined region attributes. + if (in->numberofregions > 0) { + // Spread region attributes. + for (i = 0; i < 5 * in->numberofregions; i += 5) { + if (regiontets[i/5].tet != NULL) { + attr = (int) in->regionlist[i + 3]; + if (attr > maxattr) { + maxattr = attr; + } + volume = in->regionlist[i + 4]; + tetarray->restart(); // Re-use this array. + infect(regiontets[i/5]); + tetarray->newindex((void **) &parytet); + *parytet = regiontets[i/5]; + // Collect and set attrs for all tets of this region. + for (j = 0; j < tetarray->objects; j++) { + parytet = (triface *) fastlookup(tetarray, j); + tetloop = *parytet; + setelemattribute(tetloop.tet, attrnum, attr); + if (b->varvolume) { // If has -a option. + setvolumebound(tetloop.tet, volume); + } + for (k = 0; k < 4; k++) { + decode(tetloop.tet[k], neightet); + // Is the adjacent already checked? + if (!infected(neightet)) { + // Is this side protected by a subface? + if (!issubface(neightet)) { + infect(neightet); + tetarray->newindex((void **) &parytet); + *parytet = neightet; + } + } + } // k + } // j + regioncount++; + } // if (regiontets[i/5].tet != NULL) + } // i + } + + // Set attributes for all tetrahedra. + attr = maxattr + 1; + tetrahedrons->traversalinit(); + tetloop.tet = tetrahedrontraverse(); + while (tetloop.tet != (tetrahedron *) NULL) { + if (!infected(tetloop)) { + // An unmarked region. + tetarray->restart(); // Re-use this array. + infect(tetloop); + tetarray->newindex((void **) &parytet); + *parytet = tetloop; + // Find and mark all tets. + for (j = 0; j < tetarray->objects; j++) { + parytet = (triface *) fastlookup(tetarray, j); + tetloop = *parytet; + setelemattribute(tetloop.tet, attrnum, attr); + for (k = 0; k < 4; k++) { + decode(tetloop.tet[k], neightet); + // Is the adjacent tet already checked? + if (!infected(neightet)) { + // Is this side protected by a subface? + if (!issubface(neightet)) { + infect(neightet); + tetarray->newindex((void **) &parytet); + *parytet = neightet; + } + } + } // k + } // j + attr++; // Increase the attribute. + regioncount++; + } + tetloop.tet = tetrahedrontraverse(); + } + // Until here, every tet has a region attribute. + + // Uninfect processed tets. + tetrahedrons->traversalinit(); + tetloop.tet = tetrahedrontraverse(); + while (tetloop.tet != (tetrahedron *) NULL) { + uninfect(tetloop); + tetloop.tet = tetrahedrontraverse(); + } + + if (b->verbose) { + //assert(regioncount > 0); + if (regioncount > 1) { + printf(" Found %d subdomains.\n", regioncount); + } else { + printf(" Found %d domain.\n", regioncount); + } + } + } // if (b->regionattrib) + + if (regiontets != NULL) { + delete [] regiontets; + } + delete tetarray; + delete hullarray; + + if (!b->convex) { // No -c option + // The mesh is non-convex now. + nonconvex = 1; + + // Push all hull tets into 'flipstack'. + tetrahedrons->traversalinit(); + tetloop.ver = 11; // The face opposite to dummypoint. + tetloop.tet = alltetrahedrontraverse(); + while (tetloop.tet != (tetrahedron *) NULL) { + if ((point) tetloop.tet[7] == dummypoint) { + fsym(tetloop, neightet); + flippush(flipstack, &neightet); + } + tetloop.tet = alltetrahedrontraverse(); + } + + flipconstraints fc; + fc.enqflag = 2; + long sliver_peel_count = lawsonflip3d(&fc); + + if (sliver_peel_count > 0l) { + if (b->verbose) { + printf(" Removed %ld hull slivers.\n", sliver_peel_count); + } + } + unflipqueue->restart(); + } // if (!b->convex) +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// 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; + } +} + +//// optimize_cxx ///////////////////////////////////////////////////////////// +//// //// +//// //// + +/////////////////////////////////////////////////////////////////////////////// +// // +// lawsonflip3d() A three-dimensional Lawson's algorithm. // +// // +/////////////////////////////////////////////////////////////////////////////// + +long tetgenmesh::lawsonflip3d(flipconstraints *fc) +{ + triface fliptets[5], neightet, hulltet; + face checksh, casingout; + badface *popface, *bface; + point pd, pe, *pts; + REAL sign, ori; + REAL vol, len3; + 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]); + + // Use the length of the edge [d,e] as a reference to determine + // a nearly degenerated new tet. + len3 = distance(pd, pe); + len3 = (len3 * len3 * len3); + + // Check the convexity of its three edges. Stop checking either a + // locally non-convex edge (ori < 0) or a flat edge (ori = 0) is + // encountered, and 'fliptet' represents that edge. + for (i = 0; i < 3; i++) { + ori = orient3d(org(fliptets[0]), dest(fliptets[0]), pd, pe); + if (ori > 0) { + // Avoid creating a nearly degenerated new tet at boundary. + // Re-use fliptets[2], fliptets[3]; + esym(fliptets[0], fliptets[2]); + esym(fliptets[1], fliptets[3]); + if (issubface(fliptets[2]) || issubface(fliptets[3])) { + vol = orient3dfast(org(fliptets[0]), dest(fliptets[0]), pd, pe); + if ((fabs(vol) / len3) < b->epsilon) { + ori = 0.0; // Do rounding. + } + } + } // Rounding check + if (ori <= 0) break; + enextself(fliptets[0]); + eprevself(fliptets[1]); + } + + 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; + + // Return if no unflippable faces left. + if (unflipqueue->objects == 0l) break; + // Return if no flip has been performed. + if (flipcount == 0l) break; + + // Try to flip the unflippable faces. + for (i = 0; i < unflipqueue->objects; i++) { + bface = (badface *) fastlookup(unflipqueue, i); + if (!isdeadtet(bface->tt) && + (org(bface->tt) == bface->forg) && + (dest(bface->tt) == bface->fdest) && + (apex(bface->tt) == bface->fapex)) { + flippush(flipstack, &(bface->tt)); + } + } + unflipqueue->restart(); + + } // while (1) + + if (b->verbose > 2) { + if (totalcount > 0) { + printf(" Performed %ld flips.\n", totalcount); + } + if (sliver_peels > 0) { + printf(" Removed %ld hull slivers.\n", sliver_peels); + } + if (unflipqueue->objects > 0l) { + printf(" %ld unflippable edges remained.\n", unflipqueue->objects); + } + } + + return totalcount + sliver_peels; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// recoverdelaunay() Recovery the locally Delaunay property. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::recoverdelaunay() +{ + arraypool *flipqueue, *nextflipqueue, *swapqueue; + triface tetloop, neightet, *parytet; + badface *bface, *parybface; + point *ppt; + flipconstraints fc; + int i, j; + + if (!b->quiet) { + printf("Recovering Delaunayness...\n"); + } + + tetprism_vol_sum = 0.0; // Initialize it. + + // Put all interior faces of the mesh into 'flipstack'. + tetrahedrons->traversalinit(); + tetloop.tet = tetrahedrontraverse(); + while (tetloop.tet != NULL) { + for (tetloop.ver = 0; tetloop.ver < 4; tetloop.ver++) { + decode(tetloop.tet[tetloop.ver], neightet); + if (!facemarked(neightet)) { + flippush(flipstack, &tetloop); + } + } + ppt = (point *) &(tetloop.tet[4]); + tetprism_vol_sum += tetprismvol(ppt[0], ppt[1], ppt[2], ppt[3]); + tetloop.tet = tetrahedrontraverse(); + } + + // Calulate a relatively lower bound for small improvement. + // Used to avoid rounding error in volume calculation. + fc.bak_tetprism_vol = tetprism_vol_sum * b->epsilon * 1e-3; + + if (b->verbose) { + printf(" Initial obj = %.17g\n", tetprism_vol_sum); + } + + if (b->verbose > 1) { + printf(" Recover Delaunay [Lawson] : %ld\n", flippool->items); + } + + // First only use the basic Lawson's flip. + fc.remove_ndelaunay_edge = 1; + fc.enqflag = 2; + + lawsonflip3d(&fc); + + if (b->verbose > 1) { + printf(" obj (after Lawson) = %.17g\n", tetprism_vol_sum); + } + + if (unflipqueue->objects == 0l) { + return; // The mesh is Delaunay. + } + + fc.unflip = 1; // Unflip if the edge is not flipped. + fc.collectnewtets = 1; // new tets are returned in 'cavetetlist'. + fc.enqflag = 0; + + autofliplinklevel = 1; // Init level. + b->fliplinklevel = -1; // No fixed level. + + // For efficiency reason, we limit the maximium size of the edge star. + int bakmaxflipstarsize = b->flipstarsize; + b->flipstarsize = 10; // default + + flipqueue = new arraypool(sizeof(badface), 10); + nextflipqueue = new arraypool(sizeof(badface), 10); + + // Swap the two flip queues. + swapqueue = flipqueue; + flipqueue = unflipqueue; + unflipqueue = swapqueue; + + while (flipqueue->objects > 0l) { + + if (b->verbose > 1) { + printf(" Recover Delaunay [level = %2d] #: %ld.\n", + autofliplinklevel, flipqueue->objects); + } + + for (i = 0; i < flipqueue->objects; i++) { + bface = (badface *) fastlookup(flipqueue, i); + if (getedge(bface->forg, bface->fdest, &bface->tt)) { + if (removeedgebyflips(&(bface->tt), &fc) == 2) { + tetprism_vol_sum += fc.tetprism_vol_sum; + fc.tetprism_vol_sum = 0.0; // Clear it. + // Queue new faces for flips. + for (j = 0; j < cavetetlist->objects; j++) { + parytet = (triface *) fastlookup(cavetetlist, j); + // A queued new tet may be dead. + if (!isdeadtet(*parytet)) { + for (parytet->ver = 0; parytet->ver < 4; parytet->ver++) { + // Avoid queue a face twice. + decode(parytet->tet[parytet->ver], neightet); + if (!facemarked(neightet)) { + flippush(flipstack, parytet); + } + } // parytet->ver + } + } // j + cavetetlist->restart(); + // Remove locally non-Delaunay faces. New non-Delaunay edges + // may be found. They are saved in 'unflipqueue'. + fc.enqflag = 2; + lawsonflip3d(&fc); + fc.enqflag = 0; + // There may be unflipable faces. Add them in flipqueue. + for (j = 0; j < unflipqueue->objects; j++) { + bface = (badface *) fastlookup(unflipqueue, j); + flipqueue->newindex((void **) &parybface); + *parybface = *bface; + } + unflipqueue->restart(); + } else { + // Unable to remove this edge. Save it. + nextflipqueue->newindex((void **) &parybface); + *parybface = *bface; + // Normally, it should be zero. + //assert(fc.tetprism_vol_sum == 0.0); + // However, due to rounding errors, a tiny value may appear. + fc.tetprism_vol_sum = 0.0; + } + } + } // i + + if (b->verbose > 1) { + printf(" obj (after level %d) = %.17g.\n", autofliplinklevel, + tetprism_vol_sum); + } + flipqueue->restart(); + + // Swap the two flip queues. + swapqueue = flipqueue; + flipqueue = nextflipqueue; + nextflipqueue = swapqueue; + + if (flipqueue->objects > 0l) { + // default 'b->delmaxfliplevel' is 1. + if (autofliplinklevel >= b->delmaxfliplevel) { + // For efficiency reason, we do not search too far. + break; + } + autofliplinklevel+=b->fliplinklevelinc; + } + } // while (flipqueue->objects > 0l) + + if (flipqueue->objects > 0l) { + if (b->verbose > 1) { + printf(" %ld non-Delaunay edges remained.\n", flipqueue->objects); + } + } + + if (b->verbose) { + printf(" Final obj = %.17g\n", tetprism_vol_sum); + } + + b->flipstarsize = bakmaxflipstarsize; + delete flipqueue; + delete nextflipqueue; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// gettetrahedron() Get a tetrahedron which have the given vertices. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::gettetrahedron(point pa, point pb, point pc, point pd, + triface *searchtet) +{ + triface spintet; + int t1ver; + + if (getedge(pa, pb, searchtet)) { + spintet = *searchtet; + while (1) { + if (apex(spintet) == pc) { + *searchtet = spintet; + break; + } + fnextself(spintet); + if (spintet.tet == searchtet->tet) break; + } + if (apex(*searchtet) == pc) { + if (oppo(*searchtet) == pd) { + return 1; + } else { + fsymself(*searchtet); + if (oppo(*searchtet) == pd) { + return 1; + } + } + } + } + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// improvequalitybyflips() Improve the mesh quality by flips. // +// // +/////////////////////////////////////////////////////////////////////////////// + +long tetgenmesh::improvequalitybyflips() +{ + arraypool *flipqueue, *nextflipqueue, *swapqueue; + badface *bface, *parybface; + triface *parytet; + point *ppt; + flipconstraints fc; + REAL *cosdd, ncosdd[6], maxdd; + long totalremcount, remcount; + int remflag; + int n, i, j, k; + + //assert(unflipqueue->objects > 0l); + flipqueue = new arraypool(sizeof(badface), 10); + nextflipqueue = new arraypool(sizeof(badface), 10); + + // Backup flip edge options. + int bakautofliplinklevel = autofliplinklevel; + int bakfliplinklevel = b->fliplinklevel; + int bakmaxflipstarsize = b->flipstarsize; + + // Set flip edge options. + autofliplinklevel = 1; + b->fliplinklevel = -1; + b->flipstarsize = 10; // b->optmaxflipstarsize; + + fc.remove_large_angle = 1; + fc.unflip = 1; + fc.collectnewtets = 1; + fc.checkflipeligibility = 1; + + totalremcount = 0l; + + // Swap the two flip queues. + swapqueue = flipqueue; + flipqueue = unflipqueue; + unflipqueue = swapqueue; + + while (flipqueue->objects > 0l) { + + remcount = 0l; + + while (flipqueue->objects > 0l) { + if (b->verbose > 1) { + printf(" Improving mesh qualiy by flips [%d]#: %ld.\n", + autofliplinklevel, flipqueue->objects); + } + + for (k = 0; k < flipqueue->objects; k++) { + bface = (badface *) fastlookup(flipqueue, k); + if (gettetrahedron(bface->forg, bface->fdest, bface->fapex, + bface->foppo, &bface->tt)) { + //assert(!ishulltet(bface->tt)); + // There are bad dihedral angles in this tet. + if (bface->tt.ver != 11) { + // The dihedral angles are permuted. + // Here we simply re-compute them. Slow!!. + ppt = (point *) & (bface->tt.tet[4]); + tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], bface->cent, + &bface->key, NULL); + bface->forg = ppt[0]; + bface->fdest = ppt[1]; + bface->fapex = ppt[2]; + bface->foppo = ppt[3]; + bface->tt.ver = 11; + } + if (bface->key == 0) { + // Re-comput the quality values. Due to smoothing operations. + ppt = (point *) & (bface->tt.tet[4]); + tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], bface->cent, + &bface->key, NULL); + } + cosdd = bface->cent; + remflag = 0; + for (i = 0; (i < 6) && !remflag; i++) { + if (cosdd[i] < cosmaxdihed) { + // Found a large dihedral angle. + bface->tt.ver = edge2ver[i]; // Go to the edge. + fc.cosdihed_in = cosdd[i]; + fc.cosdihed_out = 0.0; // 90 degree. + n = removeedgebyflips(&(bface->tt), &fc); + if (n == 2) { + // Edge is flipped. + remflag = 1; + if (fc.cosdihed_out < cosmaxdihed) { + // Queue new bad tets for further improvements. + for (j = 0; j < cavetetlist->objects; j++) { + parytet = (triface *) fastlookup(cavetetlist, j); + if (!isdeadtet(*parytet)) { + ppt = (point *) & (parytet->tet[4]); + // Do not test a hull tet. + if (ppt[3] != dummypoint) { + tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], ncosdd, + &maxdd, NULL); + if (maxdd < cosmaxdihed) { + // There are bad dihedral angles in this tet. + nextflipqueue->newindex((void **) &parybface); + parybface->tt.tet = parytet->tet; + parybface->tt.ver = 11; + parybface->forg = ppt[0]; + parybface->fdest = ppt[1]; + parybface->fapex = ppt[2]; + parybface->foppo = ppt[3]; + parybface->key = maxdd; + for (n = 0; n < 6; n++) { + parybface->cent[n] = ncosdd[n]; + } + } + } // if (ppt[3] != dummypoint) + } + } // j + } // if (fc.cosdihed_out < cosmaxdihed) + cavetetlist->restart(); + remcount++; + } + } + } // i + if (!remflag) { + // An unremoved bad tet. Queue it again. + unflipqueue->newindex((void **) &parybface); + *parybface = *bface; + } + } // if (gettetrahedron(...)) + } // k + + flipqueue->restart(); + + // Swap the two flip queues. + swapqueue = flipqueue; + flipqueue = nextflipqueue; + nextflipqueue = swapqueue; + } // while (flipqueues->objects > 0) + + if (b->verbose > 1) { + printf(" Removed %ld bad tets.\n", remcount); + } + totalremcount += remcount; + + if (unflipqueue->objects > 0l) { + //if (autofliplinklevel >= b->optmaxfliplevel) { + if (autofliplinklevel >= b->optlevel) { + break; + } + autofliplinklevel+=b->fliplinklevelinc; + //b->flipstarsize = 10 + (1 << (b->optlevel - 1)); + } + + // Swap the two flip queues. + swapqueue = flipqueue; + flipqueue = unflipqueue; + unflipqueue = swapqueue; + } // while (flipqueues->objects > 0) + + // Restore original flip edge options. + autofliplinklevel = bakautofliplinklevel; + b->fliplinklevel = bakfliplinklevel; + b->flipstarsize = bakmaxflipstarsize; + + delete flipqueue; + delete nextflipqueue; + + return totalremcount; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// smoothpoint() Moving a vertex to improve the mesh quality. // +// // +// 'smtpt' (p) is a point to be smoothed. Generally, it is a Steiner point. // +// It may be not a vertex of the mesh. // +// // +// This routine tries to move 'p' inside its star until a selected objective // +// function over all tetrahedra in the star is improved. The function may be // +// the some quality measures, i.e., aspect ratio, maximum dihedral angel, or // +// simply the volume of the tetrahedra. // +// // +// 'linkfacelist' contains the list of link faces of 'p'. Since a link face // +// has two orientations, ccw or cw, with respect to 'p'. 'ccw' indicates // +// the orientation is ccw (1) or not (0). // +// // +// 'opm' is a structure contains the parameters of the objective function. // +// It is needed by the evaluation of the function value. // +// // +// The return value indicates weather the point is smoothed or not. // +// // +// ASSUMPTION: This routine assumes that all link faces are true faces, i.e, // +// no face has 'dummypoint' as its vertex. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::smoothpoint(point smtpt, arraypool *linkfacelist, int ccw, + optparameters *opm) +{ + triface *parytet, *parytet1, swaptet; + point pa, pb, pc; + REAL fcent[3], startpt[3], nextpt[3], bestpt[3]; + REAL oldval, minval = 0.0, val; + REAL maxcosd; // oldang, newang; + REAL ori, diff; + int numdirs, iter; + int i, j, k; + + // Decide the number of moving directions. + numdirs = (int) linkfacelist->objects; + if (numdirs > opm->numofsearchdirs) { + numdirs = opm->numofsearchdirs; // Maximum search directions. + } + + // Set the initial value. + opm->imprval = opm->initval; + iter = 0; + + for (i = 0; i < 3; i++) { + bestpt[i] = startpt[i] = smtpt[i]; + } + + // Iterate until the obj function is not improved. + while (1) { + + // Find the best next location. + oldval = opm->imprval; + + for (i = 0; i < numdirs; i++) { + // Randomly pick a link face (0 <= k <= objects - i - 1). + k = (int) randomnation(linkfacelist->objects - i); + parytet = (triface *) fastlookup(linkfacelist, k); + // Calculate a new position from 'p' to the center of this face. + pa = org(*parytet); + pb = dest(*parytet); + pc = apex(*parytet); + for (j = 0; j < 3; j++) { + fcent[j] = (pa[j] + pb[j] + pc[j]) / 3.0; + } + for (j = 0; j < 3; j++) { + nextpt[j] = startpt[j] + opm->searchstep * (fcent[j] - startpt[j]); + } + // Calculate the largest minimum function value for the new location. + for (j = 0; j < linkfacelist->objects; j++) { + parytet = (triface *) fastlookup(linkfacelist, j); + if (ccw) { + pa = org(*parytet); + pb = dest(*parytet); + } else { + pb = org(*parytet); + pa = dest(*parytet); + } + pc = apex(*parytet); + ori = orient3d(pa, pb, pc, nextpt); + if (ori < 0.0) { + // Calcuate the objective function value. + if (opm->max_min_volume) { + //val = -ori; + val = - orient3dfast(pa, pb, pc, nextpt); + } else if (opm->min_max_aspectratio) { + val = 1.0 / tetaspectratio(pa, pb, pc, nextpt); + } else if (opm->min_max_dihedangle) { + tetalldihedral(pa, pb, pc, nextpt, NULL, &maxcosd, NULL); + if (maxcosd < -1) maxcosd = -1.0; // Rounding. + val = maxcosd + 1.0; // Make it be positive. + } else { + // Unknown objective function. + val = 0.0; + } + } else { // ori >= 0.0; + // An invalid new tet. + // This may happen if the mesh contains inverted elements. + if (opm->max_min_volume) { + //val = -ori; + val = - orient3dfast(pa, pb, pc, nextpt); + } else { + // Discard this point. + break; // j + } + } // if (ori >= 0.0) + // Stop looping when the object value is not improved. + if (val <= opm->imprval) { + break; // j + } else { + // Remember the smallest improved value. + if (j == 0) { + minval = val; + } else { + minval = (val < minval) ? val : minval; + } + } + } // j + if (j == linkfacelist->objects) { + // The function value has been improved. + opm->imprval = minval; + // Save the new location of the point. + for (j = 0; j < 3; j++) bestpt[j] = nextpt[j]; + } + // Swap k-th and (object-i-1)-th entries. + j = linkfacelist->objects - i - 1; + parytet = (triface *) fastlookup(linkfacelist, k); + parytet1 = (triface *) fastlookup(linkfacelist, j); + swaptet = *parytet1; + *parytet1 = *parytet; + *parytet = swaptet; + } // i + + diff = opm->imprval - oldval; + if (diff > 0.0) { + // Is the function value improved effectively? + if (opm->max_min_volume) { + //if ((diff / oldval) < b->epsilon) diff = 0.0; + } else if (opm->min_max_aspectratio) { + if ((diff / oldval) < 1e-3) diff = 0.0; + } else if (opm->min_max_dihedangle) { + //oldang = acos(oldval - 1.0); + //newang = acos(opm->imprval - 1.0); + //if ((oldang - newang) < 0.00174) diff = 0.0; // about 0.1 degree. + } else { + // Unknown objective function. + terminatetetgen(this, 2); + } + } + + if (diff > 0.0) { + // Yes, move p to the new location and continue. + for (j = 0; j < 3; j++) startpt[j] = bestpt[j]; + iter++; + if ((opm->maxiter > 0) && (iter >= opm->maxiter)) { + // Maximum smoothing iterations reached. + break; + } + } else { + break; + } + + } // while (1) + + if (iter > 0) { + // The point has been smoothed. + opm->smthiter = iter; // Remember the number of iterations. + // The point has been smoothed. Update it to its new position. + for (i = 0; i < 3; i++) smtpt[i] = startpt[i]; + } + + return iter; +} + + +/////////////////////////////////////////////////////////////////////////////// +// // +// improvequalitysmoothing() Improve mesh quality by smoothing. // +// // +/////////////////////////////////////////////////////////////////////////////// + +long tetgenmesh::improvequalitybysmoothing(optparameters *opm) +{ + arraypool *flipqueue, *swapqueue; + triface *parytet; + badface *bface, *parybface; + point *ppt; + long totalsmtcount, smtcount; + int smtflag; + int iter, i, j, k; + + //assert(unflipqueue->objects > 0l); + flipqueue = new arraypool(sizeof(badface), 10); + + // Swap the two flip queues. + swapqueue = flipqueue; + flipqueue = unflipqueue; + unflipqueue = swapqueue; + + totalsmtcount = 0l; + iter = 0; + + while (flipqueue->objects > 0l) { + + smtcount = 0l; + + if (b->verbose > 1) { + printf(" Improving mesh quality by smoothing [%d]#: %ld.\n", + iter, flipqueue->objects); + } + + for (k = 0; k < flipqueue->objects; k++) { + bface = (badface *) fastlookup(flipqueue, k); + if (gettetrahedron(bface->forg, bface->fdest, bface->fapex, + bface->foppo, &bface->tt)) { + // Operate on it if it is not in 'unflipqueue'. + if (!marktested(bface->tt)) { + // Here we simply re-compute the quality. Since other smoothing + // operation may have moved the vertices of this tet. + ppt = (point *) & (bface->tt.tet[4]); + tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], bface->cent, + &bface->key, NULL); + if (bface->key < cossmtdihed) { // if (maxdd < cosslidihed) { + // It is a sliver. Try to smooth its vertices. + smtflag = 0; + opm->initval = bface->key + 1.0; + for (i = 0; (i < 4) && !smtflag; i++) { + if (pointtype(ppt[i]) == FREEVOLVERTEX) { + getvertexstar(1, ppt[i], cavetetlist, NULL, NULL); + opm->searchstep = 0.001; // Search step size + smtflag = smoothpoint(ppt[i], cavetetlist, 1, opm); + if (smtflag) { + while (opm->smthiter == opm->maxiter) { + opm->searchstep *= 10.0; // Increase the step size. + opm->initval = opm->imprval; + opm->smthiter = 0; // reset + smoothpoint(ppt[i], cavetetlist, 1, opm); + } + // This tet is modifed. + smtcount++; + if ((opm->imprval - 1.0) < cossmtdihed) { + // There are slivers in new tets. Queue them. + for (j = 0; j < cavetetlist->objects; j++) { + parytet = (triface *) fastlookup(cavetetlist, j); + // Operate it if it is not in 'unflipqueue'. + if (!marktested(*parytet)) { + // Evaluate its quality. + // Re-use ppt, bface->key, bface->cent. + ppt = (point *) & (parytet->tet[4]); + tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], + bface->cent, &bface->key, NULL); + if (bface->key < cossmtdihed) { + // A new sliver. Queue it. + marktest(*parytet); // It is in unflipqueue. + unflipqueue->newindex((void **) &parybface); + parybface->tt = *parytet; + parybface->forg = ppt[0]; + parybface->fdest = ppt[1]; + parybface->fapex = ppt[2]; + parybface->foppo = ppt[3]; + parybface->tt.ver = 11; + parybface->key = 0.0; + } + } + } // j + } // if ((opm->imprval - 1.0) < cossmtdihed) + } // if (smtflag) + cavetetlist->restart(); + } // if (pointtype(ppt[i]) == FREEVOLVERTEX) + } // i + if (!smtflag) { + // Didn't smooth. Queue it again. + marktest(bface->tt); // It is in unflipqueue. + unflipqueue->newindex((void **) &parybface); + parybface->tt = bface->tt; + parybface->forg = ppt[0]; + parybface->fdest = ppt[1]; + parybface->fapex = ppt[2]; + parybface->foppo = ppt[3]; + parybface->tt.ver = 11; + parybface->key = 0.0; + } + } // if (maxdd < cosslidihed) + } // if (!marktested(...)) + } // if (gettetrahedron(...)) + } // k + + flipqueue->restart(); + + // Unmark the tets in unflipqueue. + for (i = 0; i < unflipqueue->objects; i++) { + bface = (badface *) fastlookup(unflipqueue, i); + unmarktest(bface->tt); + } + + if (b->verbose > 1) { + printf(" Smooth %ld points.\n", smtcount); + } + totalsmtcount += smtcount; + + if (smtcount == 0l) { + // No point has been smoothed. + break; + } else { + iter++; + if (iter == 2) { //if (iter >= b->optpasses) { + break; + } + } + + // Swap the two flip queues. + swapqueue = flipqueue; + flipqueue = unflipqueue; + unflipqueue = swapqueue; + } // while + + delete flipqueue; + + return totalsmtcount; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// splitsliver() Split a sliver. // +// // +/////////////////////////////////////////////////////////////////////////////// + +int tetgenmesh::splitsliver(triface *slitet, REAL cosd, int chkencflag) +{ + triface *abtets; + triface searchtet, spintet, *parytet; + point pa, pb, steinerpt; + optparameters opm; + insertvertexflags ivf; + REAL smtpt[3], midpt[3]; + int success; + int t1ver; + int n, i; + + // 'slitet' is [c,d,a,b], where [c,d] has a big dihedral angle. + // Go to the opposite edge [a,b]. + edestoppo(*slitet, searchtet); // [a,b,c,d]. + + // Do not split a segment. + if (issubseg(searchtet)) { + return 0; + } + + // Count the number of tets shared at [a,b]. + // Do not split it if it is a hull edge. + spintet = searchtet; + n = 0; + while (1) { + if (ishulltet(spintet)) break; + n++; + fnextself(spintet); + if (spintet.tet == searchtet.tet) break; + } + if (ishulltet(spintet)) { + return 0; // It is a hull edge. + } + + // Get all tets at edge [a,b]. + abtets = new triface[n]; + spintet = searchtet; + for (i = 0; i < n; i++) { + abtets[i] = spintet; + fnextself(spintet); + } + + // Initialize the list of 2n boundary faces. + for (i = 0; i < n; i++) { + eprev(abtets[i], searchtet); + esymself(searchtet); // [a,p_i,p_i+1]. + cavetetlist->newindex((void **) &parytet); + *parytet = searchtet; + enext(abtets[i], searchtet); + esymself(searchtet); // [p_i,b,p_i+1]. + cavetetlist->newindex((void **) &parytet); + *parytet = searchtet; + } + + // Init the Steiner point at the midpoint of edge [a,b]. + pa = org(abtets[0]); + pb = dest(abtets[0]); + for (i = 0; i < 3; i++) { + smtpt[i] = midpt[i] = 0.5 * (pa[i] + pb[i]); + } + + // Point smooth options. + opm.min_max_dihedangle = 1; + opm.initval = cosd + 1.0; // Initial volume is zero. + opm.numofsearchdirs = 20; + opm.searchstep = 0.001; + opm.maxiter = 100; // Limit the maximum iterations. + + success = smoothpoint(smtpt, cavetetlist, 1, &opm); + + if (success) { + while (opm.smthiter == opm.maxiter) { + // It was relocated and the prescribed maximum iteration reached. + // Try to increase the search stepsize. + opm.searchstep *= 10.0; + //opm.maxiter = 100; // Limit the maximum iterations. + opm.initval = opm.imprval; + opm.smthiter = 0; // Init. + smoothpoint(smtpt, cavetetlist, 1, &opm); + } + } // if (success) + + cavetetlist->restart(); + + if (!success) { + delete [] abtets; + return 0; + } + + + // Insert the Steiner point. + makepoint(&steinerpt, FREEVOLVERTEX); + for (i = 0; i < 3; i++) steinerpt[i] = smtpt[i]; + + // Insert the created Steiner point. + for (i = 0; i < n; i++) { + infect(abtets[i]); + caveoldtetlist->newindex((void **) &parytet); + *parytet = abtets[i]; + } + + searchtet = abtets[0]; // No need point location. + if (b->metric) { + locate(steinerpt, &searchtet); // For size interpolation. + } + + delete [] abtets; + + ivf.iloc = (int) INSTAR; + ivf.chkencflag = chkencflag; + ivf.assignmeshsize = b->metric; + + + if (insertpoint(steinerpt, &searchtet, NULL, NULL, &ivf)) { + // The vertex has been inserted. + st_volref_count++; + if (steinerleft > 0) steinerleft--; + return 1; + } else { + // The Steiner point is too close to an existing vertex. Reject it. + pointdealloc(steinerpt); + return 0; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// removeslivers() Remove slivers by adding Steiner points. // +// // +/////////////////////////////////////////////////////////////////////////////// + +long tetgenmesh::removeslivers(int chkencflag) +{ + arraypool *flipqueue, *swapqueue; + badface *bface, *parybface; + triface slitet, *parytet; + point *ppt; + REAL cosdd[6], maxcosd; + long totalsptcount, sptcount; + int iter, i, j, k; + + //assert(unflipqueue->objects > 0l); + flipqueue = new arraypool(sizeof(badface), 10); + + // Swap the two flip queues. + swapqueue = flipqueue; + flipqueue = unflipqueue; + unflipqueue = swapqueue; + + totalsptcount = 0l; + iter = 0; + + while ((flipqueue->objects > 0l) && (steinerleft != 0)) { + + sptcount = 0l; + + if (b->verbose > 1) { + printf(" Splitting bad quality tets [%d]#: %ld.\n", + iter, flipqueue->objects); + } + + for (k = 0; (k < flipqueue->objects) && (steinerleft != 0); k++) { + bface = (badface *) fastlookup(flipqueue, k); + if (gettetrahedron(bface->forg, bface->fdest, bface->fapex, + bface->foppo, &bface->tt)) { + if ((bface->key == 0) || (bface->tt.ver != 11)) { + // Here we need to re-compute the quality. Since other smoothing + // operation may have moved the vertices of this tet. + ppt = (point *) & (bface->tt.tet[4]); + tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], bface->cent, + &bface->key, NULL); + } + if (bface->key < cosslidihed) { + // It is a sliver. Try to split it. + slitet.tet = bface->tt.tet; + //cosdd = bface->cent; + for (j = 0; j < 6; j++) { + if (bface->cent[j] < cosslidihed) { + // Found a large dihedral angle. + slitet.ver = edge2ver[j]; // Go to the edge. + if (splitsliver(&slitet, bface->cent[j], chkencflag)) { + sptcount++; + break; + } + } + } // j + if (j < 6) { + // A sliver is split. Queue new slivers. + badtetrahedrons->traversalinit(); + parytet = (triface *) badtetrahedrons->traverse(); + while (parytet != NULL) { + unmarktest2(*parytet); + ppt = (point *) & (parytet->tet[4]); + tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], cosdd, + &maxcosd, NULL); + if (maxcosd < cosslidihed) { + // A new sliver. Queue it. + unflipqueue->newindex((void **) &parybface); + parybface->forg = ppt[0]; + parybface->fdest = ppt[1]; + parybface->fapex = ppt[2]; + parybface->foppo = ppt[3]; + parybface->tt.tet = parytet->tet; + parybface->tt.ver = 11; + parybface->key = maxcosd; + for (i = 0; i < 6; i++) { + parybface->cent[i] = cosdd[i]; + } + } + parytet = (triface *) badtetrahedrons->traverse(); + } + badtetrahedrons->restart(); + } else { + // Didn't split. Queue it again. + unflipqueue->newindex((void **) &parybface); + *parybface = *bface; + } // if (j == 6) + } // if (bface->key < cosslidihed) + } // if (gettetrahedron(...)) + } // k + + flipqueue->restart(); + + if (b->verbose > 1) { + printf(" Split %ld tets.\n", sptcount); + } + totalsptcount += sptcount; + + if (sptcount == 0l) { + // No point has been smoothed. + break; + } else { + iter++; + if (iter == 2) { //if (iter >= b->optpasses) { + break; + } + } + + // Swap the two flip queues. + swapqueue = flipqueue; + flipqueue = unflipqueue; + unflipqueue = swapqueue; + } // while + + delete flipqueue; + + return totalsptcount; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// optimizemesh() Optimize mesh for specified objective functions. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::optimizemesh() +{ + badface *parybface; + triface checktet; + point *ppt; + int optpasses; + optparameters opm; + REAL ncosdd[6], maxdd; + long totalremcount, remcount; + long totalsmtcount, smtcount; + long totalsptcount, sptcount; + int chkencflag; + int iter; + int n; + + if (!b->quiet) { + printf("Optimizing mesh...\n"); + } + + optpasses = ((1 << b->optlevel) - 1); + + if (b->verbose) { + printf(" Optimization level = %d.\n", b->optlevel); + printf(" Optimization scheme = %d.\n", b->optscheme); + printf(" Number of iteration = %d.\n", optpasses); + printf(" Min_Max dihed angle = %g.\n", b->optmaxdihedral); + } + + totalsmtcount = totalsptcount = totalremcount = 0l; + + cosmaxdihed = cos(b->optmaxdihedral / 180.0 * PI); + cossmtdihed = cos(b->optminsmtdihed / 180.0 * PI); + cosslidihed = cos(b->optminslidihed / 180.0 * PI); + + int attrnum = numelemattrib - 1; + + // Put all bad tetrahedra into array. + tetrahedrons->traversalinit(); + checktet.tet = tetrahedrontraverse(); + while (checktet.tet != NULL) { + if (b->convex) { // -c + // Skip this tet if it lies in the exterior. + if (elemattribute(checktet.tet, attrnum) == -1.0) { + checktet.tet = tetrahedrontraverse(); + continue; + } + } + ppt = (point *) & (checktet.tet[4]); + tetalldihedral(ppt[0], ppt[1], ppt[2], ppt[3], ncosdd, &maxdd, NULL); + if (maxdd < cosmaxdihed) { + // There are bad dihedral angles in this tet. + unflipqueue->newindex((void **) &parybface); + parybface->tt.tet = checktet.tet; + parybface->tt.ver = 11; + parybface->forg = ppt[0]; + parybface->fdest = ppt[1]; + parybface->fapex = ppt[2]; + parybface->foppo = ppt[3]; + parybface->key = maxdd; + for (n = 0; n < 6; n++) { + parybface->cent[n] = ncosdd[n]; + } + } + checktet.tet = tetrahedrontraverse(); + } + + totalremcount = improvequalitybyflips(); + + if ((unflipqueue->objects > 0l) && + ((b->optscheme & 2) || (b->optscheme & 4))) { + // The pool is only used by removeslivers(). + badtetrahedrons = new memorypool(sizeof(triface), b->tetrahedraperblock, + sizeof(void *), 0); + + // Smoothing options. + opm.min_max_dihedangle = 1; + opm.numofsearchdirs = 10; + // opm.searchstep = 0.001; + opm.maxiter = 30; // Limit the maximum iterations. + //opm.checkencflag = 4; // Queue affected tets after smoothing. + chkencflag = 4; // Queue affected tets after splitting a sliver. + iter = 0; + + while (iter < optpasses) { + smtcount = sptcount = remcount = 0l; + if (b->optscheme & 2) { + smtcount += improvequalitybysmoothing(&opm); + totalsmtcount += smtcount; + if (smtcount > 0l) { + remcount = improvequalitybyflips(); + totalremcount += remcount; + } + } + if (unflipqueue->objects > 0l) { + if (b->optscheme & 4) { + sptcount += removeslivers(chkencflag); + totalsptcount += sptcount; + if (sptcount > 0l) { + remcount = improvequalitybyflips(); + totalremcount += remcount; + } + } + } + if (unflipqueue->objects > 0l) { + if (remcount > 0l) { + iter++; + } else { + break; + } + } else { + break; + } + } // while (iter) + + delete badtetrahedrons; + badtetrahedrons = NULL; + } + + if (unflipqueue->objects > 0l) { + if (b->verbose > 1) { + printf(" %ld bad tets remained.\n", unflipqueue->objects); + } + unflipqueue->restart(); + } + + if (b->verbose) { + if (totalremcount > 0l) { + printf(" Removed %ld edges.\n", totalremcount); + } + if (totalsmtcount > 0l) { + printf(" Smoothed %ld points.\n", totalsmtcount); + } + if (totalsptcount > 0l) { + printf(" Split %ld slivers.\n", totalsptcount); + } + } +} + +//// //// +//// //// +//// optimize_cxx ///////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +// // +// jettisonnodes() Jettison unused or duplicated vertices. // +// // +// Unused points are those input points which are outside the mesh domain or // +// have no connection (isolated) to the mesh. Duplicated points exist for // +// example if the input PLC is read from a .stl mesh file (marked during the // +// Delaunay tetrahedralization step. This routine remove these points from // +// points list. All existing points are reindexed. // +// // +/////////////////////////////////////////////////////////////////////////////// + +void tetgenmesh::jettisonnodes() +{ + point pointloop; + bool jetflag; + int oldidx, newidx; + int remcount; + + if (!b->quiet) { + printf("Jettisoning redundant points.\n"); + } + + points->traversalinit(); + pointloop = pointtraverse(); + oldidx = newidx = 0; // in->firstnumber; + remcount = 0; + while (pointloop != (point) NULL) { + jetflag = (pointtype(pointloop) == DUPLICATEDVERTEX) || + (pointtype(pointloop) == UNUSEDVERTEX); + if (jetflag) { + // It is a duplicated or unused point, delete it. + pointdealloc(pointloop); + remcount++; + } else { + // Re-index it. + setpointmark(pointloop, newidx + in->firstnumber); + if (in->pointmarkerlist != (int *) NULL) { + if (oldidx < in->numberofpoints) { + // Re-index the point marker as well. + in->pointmarkerlist[newidx] = in->pointmarkerlist[oldidx]; + } + } + newidx++; + } + oldidx++; + pointloop = pointtraverse(); + } + if (b->verbose) { + printf(" %ld duplicated vertices are removed.\n", dupverts); + printf(" %ld unused vertices are removed.\n", unuverts); + } + dupverts = 0l; + unuverts = 0l; + + // The following line ensures that dead items in the pool of nodes cannot + // be allocated for the new created nodes. This ensures that the input + // nodes will occur earlier in the output files, and have lower indices. + points->deaditemstack = (void *) NULL; +} + + diff --git a/contrib/hxt/tetgenBR.h b/contrib/hxt/tetgenBR.h new file mode 100644 index 0000000000000000000000000000000000000000..b88347e4de2a7511134076e3cfb697685446e1a3 --- /dev/null +++ b/contrib/hxt/tetgenBR.h @@ -0,0 +1,2589 @@ +/////////////////////////////////////////////////////////////////////////////// +// // +// TetGen // +// // +// A Quality Tetrahedral Mesh Generator and A 3D Delaunay Triangulator // +// // +// Version 1.5 // +// May 31, 2014 // +// // +// Copyright (C) 2002--2016 // +// // +// TetGen is freely available through the website: http://www.tetgen.org. // +// It may be copied, modified, and redistributed for non-commercial use. // +// Please consult the file LICENSE for the detailed copyright notices. // +// // +/////////////////////////////////////////////////////////////////////////////// + +// The following code of this file was automatically generated. Do not edit! +// TetGenBR -- The Boundary Recovery code of TetGen. + +#ifndef tetgenBRH +#define tetgenBRH + +/////////////////////////////////////////////////////////////////////////////// +// // +// tetgenbehavior // +// // +// A structure for maintaining the switches and parameters used by TetGen's // +// mesh data structure and algorithms. // +// // +// All switches and parameters are initialized with default values. They can // +// be set by the command line arguments (a list of strings) of TetGen. // +// // +// NOTE: Some of the switches are incompatible. While some may depend on // +// other switches. The routine parse_commandline() sets the switches from // +// the command line (a list of strings) and checks the consistency of the // +// applied switches. // +// // +/////////////////////////////////////////////////////////////////////////////// + +class tetgenbehavior { + +public: + + // Switches of TetGen. + int plc; // '-p', 0. + int psc; // '-s', 0. + int refine; // '-r', 0. + int quality; // '-q', 0. + int nobisect; // '-Y', 0. + int coarsen; // '-R', 0. + int weighted; // '-w', 0. + int brio_hilbert; // '-b', 1. + int incrflip; // '-l', 0. + int flipinsert; // '-L', 0. + int metric; // '-m', 0. + int varvolume; // '-a', 0. + int fixedvolume; // '-a', 0. + int regionattrib; // '-A', 0. + int cdtrefine; // '-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 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_nomerge; // '-Y', 1. + int supsteiner_level; // '-Y/', 2. + int addsteiner_algo; // '-Y//', 1. + int coarsen_param; // '-R', 0. + int weighted_param; // '-w', 0. + int fliplinklevel; // -1. + int flipstarsize; // -1. + int fliplinklevelinc; // 1. + int reflevel; // '-D', 3. + int optlevel; // '-O', 2. + int optscheme; // '-O', 7. + int delmaxfliplevel; // 1. + int order; // '-o', 1. + int reversetetori; // '-o/', 0. + 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_separate_ang_tol; // '-p', 179.9. + REAL facet_overlap_ang_tol; // '-p/', 0.1. + REAL facet_small_ang_tol; // '-p//', 15.0. + 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 coarsen_percent; // -R1/#, 1.0. + + // Strings of command line arguments and input/output file names. + char commandline[1024]; + char infilename[1024]; + char outfilename[1024]; + char addinfilename[1024]; + char bgmeshfilename[1024]; + + // The input object of TetGen. They are recognized by either the input + // file extensions or by the specified options. + // Currently the following objects are supported: + // - NODES, a list of nodes (.node); + // - POLY, a piecewise linear complex (.poly or .smesh); + // - OFF, a polyhedron (.off, Geomview's file format); + // - PLY, a polyhedron (.ply, file format from gatech, only ASCII); + // - STL, a surface mesh (.stl, stereolithography format); + // - MEDIT, a surface mesh (.mesh, Medit's file format); + // - MESH, a tetrahedral mesh (.ele). + // If no extension is available, the imposed command line switch + // (-p or -r) implies the object. + enum objecttype {NODES, POLY, OFF, PLY, STL, MEDIT, VTK, MESH} object; + + + void syntax(); + void usage(); + + // Command line parse routine. + bool parse_commandline(int argc, char **argv); + bool parse_commandline(char *switches) { + return parse_commandline(0, &switches); + } + + // Initialize all variables. + tetgenbehavior() + { + plc = 0; + psc = 0; + refine = 0; + quality = 0; + nobisect = 0; + coarsen = 0; + metric = 0; + weighted = 0; + brio_hilbert = 1; + incrflip = 0; + flipinsert = 0; + varvolume = 0; + fixedvolume = 0; + noexact = 0; + nostaticfilter = 0; + insertaddpoints = 0; + regionattrib = 0; + cdtrefine = 0; + diagnose = 0; + convex = 0; + zeroindex = 0; + facesout = 0; + edgesout = 0; + neighout = 0; + voroout = 0; + meditview = 0; + vtkview = 0; + nobound = 0; + nonodewritten = 0; + noelewritten = 0; + nofacewritten = 0; + noiterationnum = 0; + nomergefacet = 0; + nomergevertex = 0; + nojettison = 0; + docheck = 0; + quiet = 0; + verbose = 0; + + vertexperblock = 4092; + tetrahedraperblock = 8188; + shellfaceperblock = 4092; + nobisect_nomerge = 1; + supsteiner_level = 2; + addsteiner_algo = 1; + coarsen_param = 0; + weighted_param = 0; + fliplinklevel = -1; + flipstarsize = -1; + fliplinklevelinc = 1; + reflevel = 3; + optscheme = 7; + optlevel = 2; + delmaxfliplevel = 1; + order = 1; + reversetetori = 0; + steinerleft = -1; + no_sort = 0; + hilbert_order = 52; //-1; + hilbert_limit = 8; + brio_threshold = 64; + brio_ratio = 0.125; + facet_separate_ang_tol = 179.9; + facet_overlap_ang_tol = 0.01; + facet_small_ang_tol = 15.0; + maxvolume = -1.0; + minratio = 2.0; + mindihedral = 0.0; + optmaxdihedral = 165.00; + optminsmtdihed = 179.00; + optminslidihed = 179.00; + epsilon = 1.0e-8; + coarsen_percent = 1.0; + object = NODES; + + commandline[0] = '\0'; + infilename[0] = '\0'; + outfilename[0] = '\0'; + addinfilename[0] = '\0'; + bgmeshfilename[0] = '\0'; + + } + +}; // class tetgenbehavior + + +/////////////////////////////////////////////////////////////////////////////// +// // +// tetgenmesh // +// // +// A structure for creating and updating tetrahedral meshes. // +// // +/////////////////////////////////////////////////////////////////////////////// + +class tetgenmesh { + +public: + +/////////////////////////////////////////////////////////////////////////////// +// // +// Mesh data structure // +// // +// A tetrahedral mesh T of a 3D piecewise linear complex (PLC) X is a 3D // +// simplicial complex whose underlying space is equal to the space of X. T // +// contains a 2D subcomplex S which is a triangular mesh of the boundary of // +// X. S contains a 1D subcomplex L which is a linear mesh of the boundary of // +// S. Faces and edges in S and L are respectively called subfaces and segme- // +// nts to distinguish them from others in T. // +// // +// TetGen stores the tetrahedra and vertices of T. The basic structure of a // +// tetrahedron contains pointers to its vertices and adjacent tetrahedra. A // +// vertex stores its x-, y-, and z-coordinates, and a pointer to a tetrahed- // +// ron containing it. Both tetrahedra and vertices may contain user data. // +// // +// Each face of T belongs to either two tetrahedra or one tetrahedron. In // +// the latter case, the face is an exterior boundary face of T. TetGen adds // +// fictitious tetrahedra (one-to-one) at such faces, and connects them to an // +// "infinite vertex" (which has no geometric coordinates). One can imagine // +// such a vertex lies in 4D space and is visible by all exterior boundary // +// faces. The extended set of tetrahedra (including the infinite vertex) is // +// a tetrahedralization of a 3-pseudomanifold without boundary. It has the // +// property that every face is shared by exactly two tetrahedra. // +// // +// The current version of TetGen stores explicitly the subfaces and segments // +// (which are in surface mesh S and the linear mesh L), respectively. Extra // +// pointers are allocated in tetrahedra and subfaces to point each others. // +// // +/////////////////////////////////////////////////////////////////////////////// + + // The tetrahedron data structure. It includes the following fields: + // - a list of four adjoining tetrahedra; + // - a list of four vertices; + // - a pointer to a list of four subfaces (optional, for -p switch); + // - a pointer to a list of six segments (optional, for -p switch); + // - a list of user-defined floating-point attributes (optional); + // - a volume constraint (optional, for -a switch); + // - an integer of element marker (and flags); + // The structure of a tetrahedron is an array of pointers. Its actual size + // (the length of the array) is determined at runtime. + + typedef REAL **tetrahedron; + + // The subface data structure. It includes the following fields: + // - a list of three adjoining subfaces; + // - a list of three vertices; + // - a list of three adjoining segments; + // - two adjoining tetrahedra; + // - an area constraint (optional, for -q switch); + // - an integer for boundary marker; + // - an integer for type, flags, etc. + + typedef REAL **shellface; + + // The point data structure. It includes the following fields: + // - x, y and z coordinates; + // - a list of user-defined point attributes (optional); + // - u, v coordinates (optional, for -s switch); + // - a metric tensor (optional, for -q or -m switch); + // - a pointer to an adjacent tetrahedron; + // - a pointer to a parent (or a duplicate) point; + // - a pointer to an adjacent subface or segment (optional, -p switch); + // - a pointer to a tet in background mesh (optional, for -m switch); + // - an integer for boundary marker (point index); + // - an integer for point type (and flags). + // - an integer for geometry tag (optional, for -s switch). + // The structure of a point is an array of REALs. Its acutal size is + // determined at the runtime. + + typedef REAL *point; + +/////////////////////////////////////////////////////////////////////////////// +// // +// Handles // +// // +// Navigation and manipulation in a tetrahedralization are accomplished by // +// operating on structures referred as ``handles". A handle is a pair (t,v), // +// where t is a pointer to a tetrahedron, and v is a 4-bit integer, in the // +// range from 0 to 11. v is called the ``version'' of a tetrahedron, it rep- // +// resents a directed edge of a specific face of the tetrahedron. // +// // +// There are 12 even permutations of the four vertices, each of them corres- // +// ponds to a directed edge (a version) of the tetrahedron. The 12 versions // +// can be grouped into 4 distinct ``edge rings'' in 4 ``oriented faces'' of // +// this tetrahedron. One can encode each version (a directed edge) into a // +// 4-bit integer such that the two upper bits encode the index (from 0 to 2) // +// of this edge in the edge ring, and the two lower bits encode the index ( // +// from 0 to 3) of the oriented face which contains this edge. // +// // +// The four vertices of a tetrahedron are indexed from 0 to 3 (according to // +// their storage in the data structure). Give each face the same index as // +// the node opposite it in the tetrahedron. Denote the edge connecting face // +// i to face j as i/j. We number the twelve versions as follows: // +// // +// | edge 0 edge 1 edge 2 // +// --------|-------------------------------- // +// face 0 | 0 (0/1) 4 (0/3) 8 (0/2) // +// face 1 | 1 (1/2) 5 (1/3) 9 (1/0) // +// face 2 | 2 (2/3) 6 (2/1) 10 (2/0) // +// face 3 | 3 (3/0) 7 (3/1) 11 (3/2) // +// // +// Similarly, navigation and manipulation in a (boundary) triangulation are // +// done by using handles of triangles. Each handle is a pair (s, v), where s // +// is a pointer to a triangle, and v is a version in the range from 0 to 5. // +// Each version corresponds to a directed edge of this triangle. // +// // +// Number the three vertices of a triangle from 0 to 2 (according to their // +// storage in the data structure). Give each edge the same index as the node // +// opposite it in the triangle. The six versions of a triangle are: // +// // +// | edge 0 edge 1 edge 2 // +// ---------------|-------------------------- // +// ccw orieation | 0 2 4 // +// cw orieation | 1 3 5 // +// // +// In the following, a 'triface' is a handle of tetrahedron, and a 'face' is // +// a handle of a triangle. // +// // +/////////////////////////////////////////////////////////////////////////////// + + class triface { + public: + tetrahedron *tet; + int ver; // Range from 0 to 11. + triface() : tet(0), ver(0) {} + triface& operator=(const triface& t) { + tet = t.tet; ver = t.ver; + return *this; + } + }; + + class face { + public: + shellface *sh; + int shver; // Range from 0 to 5. + face() : sh(0), shver(0) {} + face& operator=(const face& s) { + sh = s.sh; shver = s.shver; + return *this; + } + }; + +/////////////////////////////////////////////////////////////////////////////// +// // +// Arraypool // +// // +// A dynamic linear array. (It is written by J. Shewchuk) // +// // +// Each arraypool contains an array of pointers to a number of blocks. Each // +// block contains the same fixed number of objects. Each index of the array // +// addresses a particular object in the pool. The most significant bits add- // +// ress the index of the block containing the object. The less significant // +// bits address this object within the block. // +// // +// 'objectbytes' is the size of one object in blocks; 'log2objectsperblock' // +// is the base-2 logarithm of 'objectsperblock'; 'objects' counts the number // +// of allocated objects; 'totalmemory' is the total memory in bytes. // +// // +/////////////////////////////////////////////////////////////////////////////// + + class arraypool { + + public: + + int objectbytes; + int objectsperblock; + int log2objectsperblock; + int objectsperblockmark; + int toparraylen; + char **toparray; + long objects; + unsigned long totalmemory; + + void restart(); + void poolinit(int sizeofobject, int log2objperblk); + char* getblock(int objectindex); + void* lookup(int objectindex); + int newindex(void **newptr); + + arraypool(int sizeofobject, int log2objperblk); + ~arraypool(); + }; + +// fastlookup() -- A fast, unsafe operation. Return the pointer to the object +// with a given index. Note: The object's block must have been allocated, +// i.e., by the function newindex(). + +#define fastlookup(pool, index) \ + (void *) ((pool)->toparray[(index) >> (pool)->log2objectsperblock] + \ + ((index) & (pool)->objectsperblockmark) * (pool)->objectbytes) + +/////////////////////////////////////////////////////////////////////////////// +// // +// Memorypool // +// // +// A structure for memory allocation. (It is written by J. Shewchuk) // +// // +// firstblock is the first block of items. nowblock is the block from which // +// items are currently being allocated. nextitem points to the next slab // +// of free memory for an item. deaditemstack is the head of a linked list // +// (stack) of deallocated items that can be recycled. unallocateditems is // +// the number of items that remain to be allocated from nowblock. // +// // +// Traversal is the process of walking through the entire list of items, and // +// is separate from allocation. Note that a traversal will visit items on // +// the "deaditemstack" stack as well as live items. pathblock points to // +// the block currently being traversed. pathitem points to the next item // +// to be traversed. pathitemsleft is the number of items that remain to // +// be traversed in pathblock. // +// // +/////////////////////////////////////////////////////////////////////////////// + + class memorypool { + + public: + + void **firstblock, **nowblock; + void *nextitem; + void *deaditemstack; + void **pathblock; + void *pathitem; + int alignbytes; + int itembytes, itemwords; + int itemsperblock; + long items, maxitems; + int unallocateditems; + int pathitemsleft; + + memorypool(); + memorypool(int, int, int, int); + ~memorypool(); + + void poolinit(int, int, int, int); + void restart(); + void *alloc(); + void dealloc(void*); + void traversalinit(); + void *traverse(); + }; + +/////////////////////////////////////////////////////////////////////////////// +// // +// badface // +// // +// Despite of its name, a 'badface' can be used to represent one of the // +// following objects: // +// - a face of a tetrahedron which is (possibly) non-Delaunay; // +// - an encroached subsegment or subface; // +// - a bad-quality tetrahedron, i.e, has too large radius-edge ratio; // +// - a sliver, i.e., has good radius-edge ratio but nearly zero volume; // +// - a recently flipped face (saved for undoing the flip later). // +// // +/////////////////////////////////////////////////////////////////////////////// + + class badface { + public: + triface tt; + face ss; + REAL key, cent[6]; // circumcenter or cos(dihedral angles) at 6 edges. + point forg, fdest, fapex, foppo, noppo; + badface *nextitem; + badface() : key(0), forg(0), fdest(0), fapex(0), foppo(0), noppo(0), + nextitem(0) {} + }; + +/////////////////////////////////////////////////////////////////////////////// +// // +// insertvertexflags // +// // +// A collection of flags that pass to the routine insertvertex(). // +// // +/////////////////////////////////////////////////////////////////////////////// + + class insertvertexflags { + + public: + + int iloc; // input/output. + int bowywat, lawson; + int splitbdflag, validflag, respectbdflag; + int rejflag, chkencflag, cdtflag; + int assignmeshsize; + int sloc, sbowywat; + + // Used by Delaunay refinement. + int refineflag; // 0, 1, 2, 3 + triface refinetet; + face refinesh; + int smlenflag; // for useinsertradius. + REAL smlen; // for useinsertradius. + point parentpt; + + insertvertexflags() { + iloc = bowywat = lawson = 0; + splitbdflag = validflag = respectbdflag = 0; + rejflag = chkencflag = cdtflag = 0; + assignmeshsize = 0; + sloc = sbowywat = 0; + + refineflag = 0; + refinetet.tet = NULL; + refinesh.sh = NULL; + smlenflag = 0; + smlen = 0.0; + } + }; + +/////////////////////////////////////////////////////////////////////////////// +// // +// flipconstraints // +// // +// A structure of a collection of data (options and parameters) which pass // +// to the edge flip function flipnm(). // +// // +/////////////////////////////////////////////////////////////////////////////// + + class flipconstraints { + + public: + + // Elementary flip flags. + int enqflag; // (= flipflag) + int chkencflag; + + // Control flags + int unflip; // Undo the performed flips. + int collectnewtets; // Collect the new tets created by flips. + int collectencsegflag; + + // Optimization flags. + int remove_ndelaunay_edge; // Remove a non-Delaunay edge. + REAL bak_tetprism_vol; // The value to be minimized. + REAL tetprism_vol_sum; + int remove_large_angle; // Remove a large dihedral angle at edge. + REAL cosdihed_in; // The input cosine of the dihedral angle (> 0). + REAL cosdihed_out; // The improved cosine of the dihedral angle. + + // Boundary recovery flags. + int checkflipeligibility; + point seg[2]; // A constraining edge to be recovered. + point fac[3]; // A constraining face to be recovered. + point remvert; // A vertex to be removed. + + + flipconstraints() { + enqflag = 0; + chkencflag = 0; + + unflip = 0; + collectnewtets = 0; + collectencsegflag = 0; + + remove_ndelaunay_edge = 0; + bak_tetprism_vol = 0.0; + tetprism_vol_sum = 0.0; + remove_large_angle = 0; + cosdihed_in = 0.0; + cosdihed_out = 0.0; + + checkflipeligibility = 0; + seg[0] = NULL; + fac[0] = NULL; + remvert = NULL; + } + }; + +/////////////////////////////////////////////////////////////////////////////// +// // +// optparameters // +// // +// Optimization options and parameters. // +// // +/////////////////////////////////////////////////////////////////////////////// + + class optparameters { + + public: + + // The one of goals of optimization. + int max_min_volume; // Maximize the minimum volume. + int min_max_aspectratio; // Minimize the maximum aspect ratio. + int min_max_dihedangle; // Minimize the maximum dihedral angle. + + // The initial and improved value. + REAL initval, imprval; + + int numofsearchdirs; + REAL searchstep; + int maxiter; // Maximum smoothing iterations (disabled by -1). + int smthiter; // Performed iterations. + + + optparameters() { + max_min_volume = 0; + min_max_aspectratio = 0; + min_max_dihedangle = 0; + + initval = imprval = 0.0; + + numofsearchdirs = 10; + searchstep = 0.01; + maxiter = -1; // Unlimited smoothing iterations. + smthiter = 0; + + } + }; + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Labels (enumeration declarations) used by TetGen. // +// // +/////////////////////////////////////////////////////////////////////////////// + + // Labels that signify the type of a vertex. + enum verttype {UNUSEDVERTEX, DUPLICATEDVERTEX, RIDGEVERTEX, ACUTEVERTEX, + FACETVERTEX, VOLVERTEX, FREESEGVERTEX, FREEFACETVERTEX, + FREEVOLVERTEX, NREGULARVERTEX, DEADVERTEX}; + + // Labels that signify the result of triangle-triangle intersection test. + enum interresult {DISJOINT, INTERSECT, SHAREVERT, SHAREEDGE, SHAREFACE, + TOUCHEDGE, TOUCHFACE, ACROSSVERT, ACROSSEDGE, ACROSSFACE}; + + // Labels that signify the result of point location. + enum locateresult {UNKNOWN, OUTSIDE, INTETRAHEDRON, ONFACE, ONEDGE, ONVERTEX, + ENCVERTEX, ENCSEGMENT, ENCSUBFACE, NEARVERTEX, NONREGULAR, + INSTAR, BADELEMENT}; + +/////////////////////////////////////////////////////////////////////////////// +// // +// Variables of TetGen // +// // +/////////////////////////////////////////////////////////////////////////////// + + // Pointer to the input data (a set of nodes, a PLC, or a mesh). + tetgenio *in, *addin; + + // Pointer to the switches and parameters. + tetgenbehavior *b; + + // Pointer to a background mesh (contains size specification map). + tetgenmesh *bgm; + + // Memorypools to store mesh elements (points, tetrahedra, subfaces, and + // segments) and extra pointers between tetrahedra, subfaces, and segments. + memorypool *tetrahedrons, *subfaces, *subsegs, *points; + memorypool *tet2subpool, *tet2segpool; + + // Memorypools to store bad-quality (or encroached) elements. + memorypool *badtetrahedrons, *badsubfacs, *badsubsegs; + + // A memorypool to store faces to be flipped. + memorypool *flippool; + arraypool *unflipqueue; + badface *flipstack; + + // Arrays used for point insertion (the Bowyer-Watson algorithm). + arraypool *cavetetlist, *cavebdrylist, *caveoldtetlist; + arraypool *cavetetshlist, *cavetetseglist, *cavetetvertlist; + arraypool *caveencshlist, *caveencseglist; + arraypool *caveshlist, *caveshbdlist, *cavesegshlist; + + // Stacks used for CDT construction and boundary recovery. + arraypool *subsegstack, *subfacstack, *subvertstack; + + // Arrays of encroached segments and subfaces (for mesh refinement). + arraypool *encseglist, *encshlist; + + // The map between facets to their vertices (for mesh refinement). + int *idx2facetlist; + point *facetverticeslist; + + // The map between segments to their endpoints (for mesh refinement). + point *segmentendpointslist; + + // The infinite vertex. + point dummypoint; + // The recently visited tetrahedron, subface. + triface recenttet; + face recentsh; + + // PI is the ratio of a circle's circumference to its diameter. + static REAL PI; + + // Array (size = numberoftetrahedra * 6) for storing high-order nodes of + // tetrahedra (only used when -o2 switch is selected). + point *highordertable; + + // Various variables. + int numpointattrib; // Number of point attributes. + int numelemattrib; // Number of tetrahedron attributes. + int sizeoftensor; // Number of REALs per metric tensor. + int pointmtrindex; // Index to find the metric tensor of a point. + int pointparamindex; // Index to find the u,v coordinates of a point. + int point2simindex; // Index to find a simplex adjacent to a point. + int pointmarkindex; // Index to find boundary marker of a point. + int pointinsradiusindex; // Index to find the insertion radius of a point. + int elemattribindex; // Index to find attributes of a tetrahedron. + int volumeboundindex; // Index to find volume bound of a tetrahedron. + int elemmarkerindex; // Index to find marker of a tetrahedron. + int shmarkindex; // Index to find boundary marker of a subface. + int areaboundindex; // Index to find area bound of a subface. + int checksubsegflag; // Are there segments in the tetrahedralization yet? + int checksubfaceflag; // Are there subfaces in the tetrahedralization yet? + int checkconstraints; // Are there variant (node, seg, facet) constraints? + int nonconvex; // Is current mesh non-convex? + int autofliplinklevel; // The increase of link levels, default is 1. + int useinsertradius; // Save the insertion radius for Steiner points. + long samples; // Number of random samples for point location. + unsigned long randomseed; // Current random number seed. + REAL cosmaxdihed, cosmindihed; // The cosine values of max/min dihedral. + REAL cossmtdihed; // The cosine value of a bad dihedral to be smoothed. + REAL cosslidihed; // The cosine value of the max dihedral of a sliver. + REAL minfaceang, minfacetdihed; // The minimum input (dihedral) angles. + REAL tetprism_vol_sum; // The total volume of tetrahedral-prisms (in 4D). + REAL longest; // The longest possible edge length. + REAL minedgelength; // = longest * b->epsion. + REAL xmax, xmin, ymax, ymin, zmax, zmin; // Bounding box of points. + + // Counters. + long insegments; // Number of input segments. + long hullsize; // Number of exterior boundary faces. + long meshedges; // Number of mesh edges. + long meshhulledges; // Number of boundary mesh edges. + long steinerleft; // Number of Steiner points not yet used. + long dupverts; // Are there duplicated vertices? + long unuverts; // Are there unused vertices? + long nonregularcount; // Are there non-regular vertices? + long st_segref_count, st_facref_count, st_volref_count; // Steiner points. + long fillregioncount, cavitycount, cavityexpcount; + long flip14count, flip26count, flipn2ncount; + long flip23count, flip32count, flip44count, flip41count; + long flip31count, flip22count; + unsigned long totalworkmemory; // Total memory used by working arrays. + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Mesh manipulation primitives // +// // +/////////////////////////////////////////////////////////////////////////////// + + // Fast lookup tables for mesh manipulation primitives. + static int bondtbl[12][12], fsymtbl[12][12]; + static int esymtbl[12], enexttbl[12], eprevtbl[12]; + static int enextesymtbl[12], eprevesymtbl[12]; + static int eorgoppotbl[12], edestoppotbl[12]; + static int facepivot1[12], facepivot2[12][12]; + static int orgpivot[12], destpivot[12], apexpivot[12], oppopivot[12]; + static int tsbondtbl[12][6], stbondtbl[12][6]; + static int tspivottbl[12][6], stpivottbl[12][6]; + static int ver2edge[12], edge2ver[6], epivot[12]; + static int sorgpivot [6], sdestpivot[6], sapexpivot[6]; + static int snextpivot[6]; + + void inittables(); + + // Primitives for tetrahedra. + inline tetrahedron encode(triface& t); + inline tetrahedron encode2(tetrahedron* ptr, int ver); + inline void decode(tetrahedron ptr, triface& t); + inline void bond(triface& t1, triface& t2); + inline void dissolve(triface& t); + inline void esym(triface& t1, triface& t2); + inline void esymself(triface& t); + inline void enext(triface& t1, triface& t2); + inline void enextself(triface& t); + inline void eprev(triface& t1, triface& t2); + inline void eprevself(triface& t); + inline void enextesym(triface& t1, triface& t2); + inline void enextesymself(triface& t); + inline void eprevesym(triface& t1, triface& t2); + inline void eprevesymself(triface& t); + inline void eorgoppo(triface& t1, triface& t2); + inline void eorgoppoself(triface& t); + inline void edestoppo(triface& t1, triface& t2); + inline void edestoppoself(triface& t); + inline void fsym(triface& t1, triface& t2); + inline void fsymself(triface& t); + inline void fnext(triface& t1, triface& t2); + inline void fnextself(triface& t); + inline point org (triface& t); + inline point dest(triface& t); + inline point apex(triface& t); + inline point oppo(triface& t); + inline void setorg (triface& t, point p); + inline void setdest(triface& t, point p); + inline void setapex(triface& t, point p); + inline void setoppo(triface& t, point p); + inline REAL elemattribute(tetrahedron* ptr, int attnum); + inline void setelemattribute(tetrahedron* ptr, int attnum, REAL value); + inline REAL volumebound(tetrahedron* ptr); + inline void setvolumebound(tetrahedron* ptr, REAL value); + inline int elemindex(tetrahedron* ptr); + inline void setelemindex(tetrahedron* ptr, int value); + inline int elemmarker(tetrahedron* ptr); + inline void setelemmarker(tetrahedron* ptr, int value); + inline void infect(triface& t); + inline void uninfect(triface& t); + inline bool infected(triface& t); + inline void marktest(triface& t); + inline void unmarktest(triface& t); + inline bool marktested(triface& t); + inline void markface(triface& t); + inline void unmarkface(triface& t); + inline bool facemarked(triface& t); + inline void markedge(triface& t); + inline void unmarkedge(triface& t); + inline bool edgemarked(triface& t); + inline void marktest2(triface& t); + inline void unmarktest2(triface& t); + inline bool marktest2ed(triface& t); + inline int elemcounter(triface& t); + inline void setelemcounter(triface& t, int value); + inline void increaseelemcounter(triface& t); + inline void decreaseelemcounter(triface& t); + inline bool ishulltet(triface& t); + inline bool isdeadtet(triface& t); + + // Primitives for subfaces and subsegments. + inline void sdecode(shellface sptr, face& s); + inline shellface sencode(face& s); + inline shellface sencode2(shellface *sh, int shver); + inline void spivot(face& s1, face& s2); + inline void spivotself(face& s); + inline void sbond(face& s1, face& s2); + inline void sbond1(face& s1, face& s2); + inline void sdissolve(face& s); + inline point sorg(face& s); + inline point sdest(face& s); + inline point sapex(face& s); + inline void setsorg(face& s, point pointptr); + inline void setsdest(face& s, point pointptr); + inline void setsapex(face& s, point pointptr); + inline void sesym(face& s1, face& s2); + inline void sesymself(face& s); + inline void senext(face& s1, face& s2); + inline void senextself(face& s); + inline void senext2(face& s1, face& s2); + inline void senext2self(face& s); + inline REAL areabound(face& s); + inline void setareabound(face& s, REAL value); + inline int shellmark(face& s); + inline void setshellmark(face& s, int value); + inline void sinfect(face& s); + inline void suninfect(face& s); + inline bool sinfected(face& s); + inline void smarktest(face& s); + inline void sunmarktest(face& s); + inline bool smarktested(face& s); + inline void smarktest2(face& s); + inline void sunmarktest2(face& s); + inline bool smarktest2ed(face& s); + inline void smarktest3(face& s); + inline void sunmarktest3(face& s); + inline bool smarktest3ed(face& s); + inline void setfacetindex(face& f, int value); + inline int getfacetindex(face& f); + + // Primitives for interacting tetrahedra and subfaces. + inline void tsbond(triface& t, face& s); + inline void tsdissolve(triface& t); + inline void stdissolve(face& s); + inline void tspivot(triface& t, face& s); + inline void stpivot(face& s, triface& t); + + // Primitives for interacting tetrahedra and segments. + inline void tssbond1(triface& t, face& seg); + inline void sstbond1(face& s, triface& t); + inline void tssdissolve1(triface& t); + inline void sstdissolve1(face& s); + inline void tsspivot1(triface& t, face& s); + inline void sstpivot1(face& s, triface& t); + + // Primitives for interacting subfaces and segments. + inline void ssbond(face& s, face& edge); + inline void ssbond1(face& s, face& edge); + inline void ssdissolve(face& s); + inline void sspivot(face& s, face& edge); + + // Primitives for points. + inline int pointmark(point pt); + inline void setpointmark(point pt, int value); + inline enum verttype pointtype(point pt); + inline void setpointtype(point pt, enum verttype value); + inline int pointgeomtag(point pt); + inline void setpointgeomtag(point pt, int value); + inline REAL pointgeomuv(point pt, int i); + inline void setpointgeomuv(point pt, int i, REAL value); + inline void pinfect(point pt); + inline void puninfect(point pt); + inline bool pinfected(point pt); + inline void pmarktest(point pt); + inline void punmarktest(point pt); + inline bool pmarktested(point pt); + inline void pmarktest2(point pt); + inline void punmarktest2(point pt); + inline bool pmarktest2ed(point pt); + inline void pmarktest3(point pt); + inline void punmarktest3(point pt); + inline bool pmarktest3ed(point pt); + inline tetrahedron point2tet(point pt); + inline void setpoint2tet(point pt, tetrahedron value); + inline shellface point2sh(point pt); + inline void setpoint2sh(point pt, shellface value); + inline point point2ppt(point pt); + inline void setpoint2ppt(point pt, point value); + inline tetrahedron point2bgmtet(point pt); + inline void setpoint2bgmtet(point pt, tetrahedron value); + inline void setpointinsradius(point pt, REAL value); + inline REAL getpointinsradius(point pt); + inline bool issteinerpoint(point pt); + + // Advanced primitives. + inline void point2tetorg(point pt, triface& t); + inline void point2shorg(point pa, face& s); + inline point farsorg(face& seg); + inline point farsdest(face& seg); + +/////////////////////////////////////////////////////////////////////////////// +// // +// Memory managment // +// // +/////////////////////////////////////////////////////////////////////////////// + + void tetrahedrondealloc(tetrahedron*); + tetrahedron *tetrahedrontraverse(); + tetrahedron *alltetrahedrontraverse(); + void shellfacedealloc(memorypool*, shellface*); + shellface *shellfacetraverse(memorypool*); + void pointdealloc(point); + point pointtraverse(); + + void makeindex2pointmap(point*&); + void makepoint2submap(memorypool*, int*&, face*&); + void maketetrahedron(triface*); + void makeshellface(memorypool*, face*); + void makepoint(point*, enum verttype); + + void initializepools(); + +/////////////////////////////////////////////////////////////////////////////// +// // +// Advanced geometric predicates and calculations // +// // +// TetGen uses a simplified symbolic perturbation scheme from Edelsbrunner, // +// et al [*]. Hence the point-in-sphere test never returns a zero. The idea // +// is to perturb the weights of vertices in the fourth dimension. TetGen // +// uses the indices of the vertices decide the amount of perturbation. It is // +// implemented in the routine insphere_s(). +// // +// The routine tri_edge_test() determines whether or not a triangle and an // +// edge intersect in 3D. If they intersect, their intersection type is also // +// reported. This test is a combination of n 3D orientation tests (n is bet- // +// ween 3 and 9). It uses the robust orient3d() test to make the branch dec- // +// isions. The routine tri_tri_test() determines whether or not two triang- // +// les intersect in 3D. It also uses the robust orient3d() test. // +// // +// There are a number of routines to calculate geometrical quantities, e.g., // +// circumcenters, angles, dihedral angles, face normals, face areas, etc. // +// They are so far done by the default floating-point arithmetics which are // +// non-robust. They should be improved in the future. // +// // +/////////////////////////////////////////////////////////////////////////////// + + // Symbolic perturbations (robust) + REAL insphere_s(REAL*, REAL*, REAL*, REAL*, REAL*); + REAL orient4d_s(REAL*, REAL*, REAL*, REAL*, REAL*, + REAL, REAL, REAL, REAL, REAL); + + // Triangle-edge intersection test (robust) + int tri_edge_2d(point, point, point, point, point, point, int, int*, int*); + int tri_edge_tail(point, point, point, point, point, point, REAL, REAL, int, + int*, int*); + int tri_edge_test(point, point, point, point, point, point, int, int*, int*); + + // Triangle-triangle intersection test (robust) + int tri_edge_inter_tail(point, point, point, point, point, REAL, REAL); + int tri_tri_inter(point, point, point, point, point, point); + + // Linear algebra functions + inline REAL dot(REAL* v1, REAL* v2); + inline void cross(REAL* v1, REAL* v2, REAL* n); + bool lu_decmp(REAL lu[4][4], int n, int* ps, REAL* d, int N); + void lu_solve(REAL lu[4][4], int n, int* ps, REAL* b, int N); + + // An embedded 2-dimensional geometric predicate (non-robust) + REAL incircle3d(point pa, point pb, point pc, point pd); + + // Geometric calculations (non-robust) + REAL orient3dfast(REAL *pa, REAL *pb, REAL *pc, REAL *pd); + inline REAL norm2(REAL x, REAL y, REAL z); + inline REAL distance(REAL* p1, REAL* p2); + void facenormal(point pa, point pb, point pc, REAL *n, int pivot, REAL *lav); + REAL shortdistance(REAL* p, REAL* e1, REAL* e2); + REAL triarea(REAL* pa, REAL* pb, REAL* pc); + REAL interiorangle(REAL* o, REAL* p1, REAL* p2, REAL* n); + void projpt2edge(REAL* p, REAL* e1, REAL* e2, REAL* prj); + void projpt2face(REAL* p, REAL* f1, REAL* f2, REAL* f3, REAL* prj); + bool tetalldihedral(point, point, point, point, REAL*, REAL*, REAL*); + void tetallnormal(point, point, point, point, REAL N[4][3], REAL* volume); + REAL tetaspectratio(point, point, point, point); + bool circumsphere(REAL*, REAL*, REAL*, REAL*, REAL* cent, REAL* radius); + bool orthosphere(REAL*,REAL*,REAL*,REAL*,REAL,REAL,REAL,REAL,REAL*,REAL*); + void planelineint(REAL*, REAL*, REAL*, REAL*, REAL*, REAL*, REAL*); + int linelineint(REAL*, REAL*, REAL*, REAL*, REAL*, REAL*, REAL*, REAL*); + REAL tetprismvol(REAL* pa, REAL* pb, REAL* pc, REAL* pd); + bool calculateabovepoint(arraypool*, point*, point*, point*); + void calculateabovepoint4(point, point, point, point); + + // PLC error reports. + void report_overlapping_facets(face*, face*, REAL dihedang = 0.0); + int report_selfint_edge(point, point, face* sedge, triface* searchtet, + enum interresult); + int report_selfint_face(point, point, point, face* sface, triface* iedge, + int intflag, int* types, int* poss); + +/////////////////////////////////////////////////////////////////////////////// +// // +// Local mesh transformations // +// // +// A local transformation replaces a small set of tetrahedra with another // +// set of tetrahedra which fills the same space and the same boundaries. // +// In 3D, the most simplest local transformations are the elementary flips // +// performed within the convex hull of five vertices: 2-to-3, 3-to-2, 1-to-4,// +// and 4-to-1 flips, where the numbers indicate the number of tetrahedra // +// before and after each flip. The 1-to-4 and 4-to-1 flip involve inserting // +// or deleting a vertex, respectively. // +// There are complex local transformations which can be decomposed as a // +// combination of elementary flips. For example,a 4-to-4 flip which replaces // +// two coplanar edges can be regarded by a 2-to-3 flip and a 3-to-2 flip. // +// Note that the first 2-to-3 flip will temporarily create a degenerate tet- // +// rahedron which is removed immediately by the followed 3-to-2 flip. More // +// generally, a n-to-m flip, where n > 3, m = (n - 2) * 2, which removes an // +// edge can be done by first performing a sequence of (n - 3) 2-to-3 flips // +// followed by a 3-to-2 flip. // +// // +// The routines flip23(), flip32(), and flip41() perform the three element- // +// ray flips. The flip14() is available inside the routine insertpoint(). // +// // +// The routines flipnm() and flipnm_post() implement a generalized edge flip // +// algorithm which uses a combination of elementary flips. // +// // +// The routine insertpoint() implements a variant of Bowyer-Watson's cavity // +// algorithm to insert a vertex. It works for arbitrary tetrahedralization, // +// either Delaunay, or constrained Delaunay, or non-Delaunay. // +// // +/////////////////////////////////////////////////////////////////////////////// + + // The elementary flips. + void flip23(triface*, int, flipconstraints* fc); + void flip32(triface*, int, flipconstraints* fc); + void flip41(triface*, int, flipconstraints* fc); + + // A generalized edge flip. + int flipnm(triface*, int n, int level, int, flipconstraints* fc); + int flipnm_post(triface*, int n, int nn, int, flipconstraints* fc); + + // Point insertion. + int insertpoint(point, triface*, face*, face*, insertvertexflags*); + void insertpoint_abort(face*, insertvertexflags*); + +/////////////////////////////////////////////////////////////////////////////// +// // +// Delaunay tetrahedralization // +// // +// The routine incrementaldelaunay() implemented two incremental algorithms // +// for constructing Delaunay tetrahedralizations (DTs): the Bowyer-Watson // +// (B-W) algorithm and the incremental flip algorithm of Edelsbrunner and // +// Shah, "Incremental topological flipping works for regular triangulation," // +// Algorithmica, 15:233-241, 1996. // +// // +// The routine incrementalflip() implements the flip algorithm of [Edelsbru- // +// nner and Shah, 1996]. It flips a queue of locally non-Delaunay faces (in // +// an arbitrary order). The success is guaranteed when the Delaunay tetrah- // +// edralization is constructed incrementally by adding one vertex at a time. // +// // +// The routine locate() finds a tetrahedron contains a new point in current // +// DT. It uses a simple stochastic walk algorithm: starting from an arbitr- // +// ary tetrahedron in DT, it finds the destination by visit one tetrahedron // +// at a time, randomly chooses a tetrahedron if there are more than one // +// choices. This algorithm terminates due to Edelsbrunner's acyclic theorem. // +// Choose a good starting tetrahedron is crucial to the speed of the walk. // +// TetGen originally uses the "jump-and-walk" algorithm of Muecke, E.P., // +// Saias, I., and Zhu, B. "Fast Randomized Point Location Without Preproces- // +// sing." In Proceedings of the 12th ACM Symposium on Computational Geometry,// +// 274-283, 1996. It first randomly samples several tetrahedra in the DT // +// and then choosing the closet one to start walking. // +// The above algorithm slows download dramatically as the number of points // +// grows -- reported in Amenta, N., Choi, S. and Rote, G., "Incremental // +// construction con {BRIO}," In Proceedings of 19th ACM Symposium on // +// Computational Geometry, 211-219, 2003. On the other hand, Liu and // +// Snoeyink showed that the point location can be made in constant time if // +// the points are pre-sorted so that the nearby points in space have nearby // +// indices, then adding the points in this order. They sorted the points // +// along the 3D Hilbert curve. // +// // +// The routine hilbert_sort3() sorts a set of 3D points along the 3D Hilbert // +// curve. It recursively splits a point set according to the Hilbert indices // +// mapped to the subboxes of the bounding box of the point set. // +// The Hilbert indices is calculated by Butz's algorithm in 1971. A nice // +// exposition of this algorithm can be found in the paper of Hamilton, C., // +// "Compact Hilbert Indices", Technical Report CS-2006-07, Computer Science, // +// Dalhousie University, 2006 (the Section 2). My implementation also refer- // +// enced Steven Witham's implementation of "Hilbert walk" (hopefully, it is // +// still available at: http://www.tiac.net/~sw/2008/10/Hilbert/). // +// // +// TetGen sorts the points using the method in the paper of Boissonnat,J.-D.,// +// Devillers, O. and Hornus, S. "Incremental Construction of the Delaunay // +// Triangulation and the Delaunay Graph in Medium Dimension," In Proceedings // +// of the 25th ACM Symposium on Computational Geometry, 2009. // +// It first randomly sorts the points into subgroups using the Biased Rand-// +// omized Insertion Ordering (BRIO) of Amenta et al 2003, then sorts the // +// points in each subgroup along the 3D Hilbert curve. Inserting points in // +// this order ensures a randomized "sprinkling" of the points over the // +// domain, while sorting of each subset ensures locality. // +// // +/////////////////////////////////////////////////////////////////////////////// + + void transfernodes(); + + // Point sorting. + int transgc[8][3][8], tsb1mod3[8]; + void hilbert_init(int n); + int hilbert_split(point* vertexarray, int arraysize, int gc0, int gc1, + REAL, REAL, REAL, REAL, REAL, REAL); + void hilbert_sort3(point* vertexarray, int arraysize, int e, int d, + REAL, REAL, REAL, REAL, REAL, REAL, int depth); + void brio_multiscale_sort(point*,int,int threshold,REAL ratio,int* depth); + + // Point location. + unsigned long randomnation(unsigned int choices); + void randomsample(point searchpt, triface *searchtet); + enum locateresult locate(point searchpt, triface *searchtet, + int chkencflag = 0); + + // Incremental flips. + void flippush(badface*&, triface*); + int incrementalflip(point newpt, int, flipconstraints *fc); + + // Incremental Delaunay construction. + void initialdelaunay(point pa, point pb, point pc, point pd); + void incrementaldelaunay(clock_t&); + +/////////////////////////////////////////////////////////////////////////////// +// // +// Surface triangulation // +// // +/////////////////////////////////////////////////////////////////////////////// + + void flipshpush(face*); + void flip22(face*, int, int); + void flip31(face*, int); + long lawsonflip(); + int sinsertvertex(point newpt, face*, face*, int iloc, int bowywat, int); + int sremovevertex(point delpt, face*, face*, int lawson); + + enum locateresult slocate(point, face*, int, int, int); + enum interresult sscoutsegment(face*, point, int, int, int); + void scarveholes(int, REAL*); + + void unifysegments(); + void identifyinputedges(point*); + void mergefacets(); + + enum interresult finddirection(triface* searchtet, point endpt); + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Constrained tetrahedralizations. // +// // +/////////////////////////////////////////////////////////////////////////////// + + int checkflipeligibility(int fliptype, point, point, point, point, point, + int level, int edgepivot, flipconstraints* fc); + + int removeedgebyflips(triface*, flipconstraints*); + int removefacebyflips(triface*, flipconstraints*); + + int recoveredgebyflips(point, point, face*, triface*, int fullsearch); + int add_steinerpt_in_schoenhardtpoly(triface*, int, int chkencflag); + int add_steinerpt_in_segment(face*, int searchlevel); + int addsteiner4recoversegment(face*, int); + int recoversegments(arraypool*, int fullsearch, int steinerflag); + + int recoverfacebyflips(point, point, point, face*, triface*); + int recoversubfaces(arraypool*, int steinerflag); + + int getvertexstar(int, point searchpt, arraypool*, arraypool*, arraypool*); + int getedge(point, point, triface*); + int reduceedgesatvertex(point startpt, arraypool* endptlist); + int removevertexbyflips(point steinerpt); + + int suppressbdrysteinerpoint(point steinerpt); + int suppresssteinerpoints(); + + void recoverboundary(clock_t&); + +/////////////////////////////////////////////////////////////////////////////// +// // +// Mesh reconstruction // +// // +/////////////////////////////////////////////////////////////////////////////// + + void carveholes(); + + // Comment: These three functions are implemented directly in: + // gmsh_wrk/Mesh/meshGRegionBoundaryRecovery.cpp + bool reconstructmesh(void *); + void outsurfacemesh(const char* mfilename); + void outmesh2medit(const char* mfilename); + + void enqueuesubface(memorypool*, face*); + void enqueuetetrahedron(triface*); + +/////////////////////////////////////////////////////////////////////////////// +// // +// Mesh optimization // +// // +/////////////////////////////////////////////////////////////////////////////// + + long lawsonflip3d(flipconstraints *fc); + void recoverdelaunay(); + + int gettetrahedron(point, point, point, point, triface *); + long improvequalitybyflips(); + + int smoothpoint(point smtpt, arraypool*, int ccw, optparameters *opm); + long improvequalitybysmoothing(optparameters *opm); + + int splitsliver(triface *, REAL, int); + long removeslivers(int); + + void optimizemesh(); + + void jettisonnodes(); + +/////////////////////////////////////////////////////////////////////////////// +// // +// Constructor & destructor // +// // +/////////////////////////////////////////////////////////////////////////////// + + void initializetetgenmesh() + { + in = addin = NULL; + b = NULL; + bgm = NULL; + + tetrahedrons = subfaces = subsegs = points = NULL; + badtetrahedrons = badsubfacs = badsubsegs = NULL; + tet2segpool = tet2subpool = NULL; + flippool = NULL; + + dummypoint = NULL; + flipstack = NULL; + unflipqueue = NULL; + + cavetetlist = cavebdrylist = caveoldtetlist = NULL; + cavetetshlist = cavetetseglist = cavetetvertlist = NULL; + caveencshlist = caveencseglist = NULL; + caveshlist = caveshbdlist = cavesegshlist = NULL; + + subsegstack = subfacstack = subvertstack = NULL; + encseglist = encshlist = NULL; + idx2facetlist = NULL; + facetverticeslist = NULL; + segmentendpointslist = NULL; + + highordertable = NULL; + + numpointattrib = numelemattrib = 0; + sizeoftensor = 0; + pointmtrindex = 0; + pointparamindex = 0; + pointmarkindex = 0; + point2simindex = 0; + pointinsradiusindex = 0; + elemattribindex = 0; + volumeboundindex = 0; + shmarkindex = 0; + areaboundindex = 0; + checksubsegflag = 0; + checksubfaceflag = 0; + checkconstraints = 0; + nonconvex = 0; + autofliplinklevel = 1; + useinsertradius = 0; + samples = 0l; + randomseed = 1l; + minfaceang = minfacetdihed = PI; + tetprism_vol_sum = 0.0; + longest = minedgelength = 0.0; + xmax = xmin = ymax = ymin = zmax = zmin = 0.0; + + insegments = 0l; + hullsize = 0l; + meshedges = meshhulledges = 0l; + steinerleft = -1; + dupverts = 0l; + unuverts = 0l; + nonregularcount = 0l; + st_segref_count = st_facref_count = st_volref_count = 0l; + fillregioncount = cavitycount = cavityexpcount = 0l; + flip14count = flip26count = flipn2ncount = 0l; + flip23count = flip32count = flip44count = flip41count = 0l; + flip22count = flip31count = 0l; + totalworkmemory = 0l; + + + } // tetgenmesh() + + void freememory() + { + if (bgm != NULL) { + delete bgm; + } + + if (points != (memorypool *) NULL) { + delete points; + delete [] dummypoint; + } + if (tetrahedrons != (memorypool *) NULL) { + delete tetrahedrons; + } + if (subfaces != (memorypool *) NULL) { + delete subfaces; + delete subsegs; + } + if (tet2segpool != NULL) { + delete tet2segpool; + delete tet2subpool; + } + + if (badtetrahedrons) { + delete badtetrahedrons; + } + if (badsubfacs) { + delete badsubfacs; + } + if (badsubsegs) { + delete badsubsegs; + } + if (encseglist) { + delete encseglist; + } + if (encshlist) { + delete encshlist; + } + + if (flippool != NULL) { + delete flippool; + delete unflipqueue; + } + + if (cavetetlist != NULL) { + delete cavetetlist; + delete cavebdrylist; + delete caveoldtetlist; + delete cavetetvertlist; + } + + if (caveshlist != NULL) { + delete caveshlist; + delete caveshbdlist; + delete cavesegshlist; + delete cavetetshlist; + delete cavetetseglist; + delete caveencshlist; + delete caveencseglist; + } + + if (subsegstack != NULL) { + delete subsegstack; + delete subfacstack; + delete subvertstack; + } + + if (idx2facetlist != NULL) { + delete [] idx2facetlist; + delete [] facetverticeslist; + } + + if (segmentendpointslist != NULL) { + delete [] segmentendpointslist; + } + + if (highordertable != NULL) { + delete [] highordertable; + } + + initializetetgenmesh(); + } + + tetgenmesh() + { + initializetetgenmesh(); + } + + ~tetgenmesh() + { + freememory(); + } // ~tetgenmesh() + +}; // End of class tetgenmesh. + + +/////////////////////////////////////////////////////////////////////////////// +// // +// terminatetetgen() Terminate TetGen with a given exit code. // +// // +/////////////////////////////////////////////////////////////////////////////// + +// selfint_event, a structure to report self-intersections. +// +// - e_type, report the type of self-intersections, +// it may be one of: +// 0, reserved. +// 1, two edges intersect, +// 2, an edge and a triangle intersect, +// 3, two triangles intersect, +// 4, two edges are overlapping, +// 5, an edge and a triangle are overlapping, +// 6, two triangles are overlapping, +// 7, a vertex lies in an edge, +// 8, a vertex lies in a facet, + +class selfint_event { +public: + int e_type; + int f_marker1; // Tag of the 1st facet. + int s_marker1; // Tag of the 1st segment. + int f_vertices1[3]; + int f_marker2; // Tag of the 2nd facet. + int s_marker2; // Tag of the 2nd segment. + int f_vertices2[3]; + REAL int_point[3]; + selfint_event() { + e_type = 0; + f_marker1 = f_marker2 = 0; + s_marker1 = s_marker2 = 0; + } +}; + +static selfint_event sevent; + +inline void terminatetetgen(tetgenmesh *m, int x) +{ +#ifdef TETLIBRARY + throw x; +#else + switch (x) { + case 1: // Out of memory. + printf("Error: Out of memory.\n"); + break; + case 2: // Encounter an internal error. + printf("Please report this bug to Hang.Si@wias-berlin.de. Include\n"); + printf(" the message above, your input data set, and the exact\n"); + printf(" command line you used to run this program, thank you.\n"); + break; + case 3: + printf("A self-intersection was detected. Program stopped.\n"); + printf("Hint: use -d option to detect all self-intersections.\n"); + break; + case 4: + printf("A very small input feature size was detected. Program stopped.\n"); + if (m) { + printf("Hint: use -T option to set a smaller tolerance. Current is %g\n", + m->b->epsilon); + } + break; + case 5: + printf("Two very close input facets were detected. Program stopped.\n"); + printf("Hint: use -Y option to avoid adding Steiner points in boundary.\n"); + break; + case 10: + printf("An input error was detected. Program stopped.\n"); + break; + } // switch (x) + exit(x); +#endif // #ifdef TETLIBRARY +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// Primitives for tetrahedra // +// // +/////////////////////////////////////////////////////////////////////////////// + +// encode() compress a handle into a single pointer. It relies on the +// assumption that all addresses of tetrahedra are aligned to sixteen- +// byte boundaries, so that the last four significant bits are zero. + +inline tetgenmesh::tetrahedron tetgenmesh::encode(triface& t) { + return (tetrahedron) ((uintptr_t) (t).tet | (uintptr_t) (t).ver); +} + +inline tetgenmesh::tetrahedron tetgenmesh::encode2(tetrahedron* ptr, int ver) { + return (tetrahedron) ((uintptr_t) (ptr) | (uintptr_t) (ver)); +} + +// decode() converts a pointer to a handle. The version is extracted from +// the four least significant bits of the pointer. + +inline void tetgenmesh::decode(tetrahedron ptr, triface& t) { + (t).ver = (int) ((uintptr_t) (ptr) & (uintptr_t) 15); + (t).tet = (tetrahedron *) ((uintptr_t) (ptr) ^ (uintptr_t) (t).ver); +} + +// bond() connects two tetrahedra together. (t1,v1) and (t2,v2) must +// refer to the same face and the same edge. + +inline void tetgenmesh::bond(triface& t1, triface& t2) { + // printf("%d %d %d\n",t1.ver,t2.ver,bondtbl[t1.ver][t2.ver]); + t1.tet[t1.ver & 3] = encode2(t2.tet, bondtbl[t1.ver][t2.ver]); + t2.tet[t2.ver & 3] = encode2(t1.tet, bondtbl[t2.ver][t1.ver]); +} + + +// dissolve() a bond (from one side). + +inline void tetgenmesh::dissolve(triface& t) { + t.tet[t.ver & 3] = NULL; +} + +// enext() finds the next edge (counterclockwise) in the same face. + +inline void tetgenmesh::enext(triface& t1, triface& t2) { + t2.tet = t1.tet; + t2.ver = enexttbl[t1.ver]; +} + +inline void tetgenmesh::enextself(triface& t) { + t.ver = enexttbl[t.ver]; +} + +// eprev() finds the next edge (clockwise) in the same face. + +inline void tetgenmesh::eprev(triface& t1, triface& t2) { + t2.tet = t1.tet; + t2.ver = eprevtbl[t1.ver]; +} + +inline void tetgenmesh::eprevself(triface& t) { + t.ver = eprevtbl[t.ver]; +} + +// esym() finds the reversed edge. It is in the other face of the +// same tetrahedron. + +inline void tetgenmesh::esym(triface& t1, triface& t2) { + (t2).tet = (t1).tet; + (t2).ver = esymtbl[(t1).ver]; +} + +inline void tetgenmesh::esymself(triface& t) { + (t).ver = esymtbl[(t).ver]; +} + +// enextesym() finds the reversed edge of the next edge. It is in the other +// face of the same tetrahedron. It is the combination esym() * enext(). + +inline void tetgenmesh::enextesym(triface& t1, triface& t2) { + t2.tet = t1.tet; + t2.ver = enextesymtbl[t1.ver]; +} + +inline void tetgenmesh::enextesymself(triface& t) { + t.ver = enextesymtbl[t.ver]; +} + +// eprevesym() finds the reversed edge of the previous edge. + +inline void tetgenmesh::eprevesym(triface& t1, triface& t2) { + t2.tet = t1.tet; + t2.ver = eprevesymtbl[t1.ver]; +} + +inline void tetgenmesh::eprevesymself(triface& t) { + t.ver = eprevesymtbl[t.ver]; +} + +// eorgoppo() Finds the opposite face of the origin of the current edge. +// Return the opposite edge of the current edge. + +inline void tetgenmesh::eorgoppo(triface& t1, triface& t2) { + t2.tet = t1.tet; + t2.ver = eorgoppotbl[t1.ver]; +} + +inline void tetgenmesh::eorgoppoself(triface& t) { + t.ver = eorgoppotbl[t.ver]; +} + +// edestoppo() Finds the opposite face of the destination of the current +// edge. Return the opposite edge of the current edge. + +inline void tetgenmesh::edestoppo(triface& t1, triface& t2) { + t2.tet = t1.tet; + t2.ver = edestoppotbl[t1.ver]; +} + +inline void tetgenmesh::edestoppoself(triface& t) { + t.ver = edestoppotbl[t.ver]; +} + +// fsym() finds the adjacent tetrahedron at the same face and the same edge. + +inline void tetgenmesh::fsym(triface& t1, triface& t2) { + decode((t1).tet[(t1).ver & 3], t2); + t2.ver = fsymtbl[t1.ver][t2.ver]; +} + + +#define fsymself(t) \ + t1ver = (t).ver; \ + decode((t).tet[(t).ver & 3], (t));\ + (t).ver = fsymtbl[t1ver][(t).ver] + +// fnext() finds the next face while rotating about an edge according to +// a right-hand rule. The face is in the adjacent tetrahedron. It is +// the combination: fsym() * esym(). + +inline void tetgenmesh::fnext(triface& t1, triface& t2) { + decode(t1.tet[facepivot1[t1.ver]], t2); + t2.ver = facepivot2[t1.ver][t2.ver]; +} + + +#define fnextself(t) \ + t1ver = (t).ver; \ + decode((t).tet[facepivot1[(t).ver]], (t)); \ + (t).ver = facepivot2[t1ver][(t).ver] + + +// The following primtives get or set the origin, destination, face apex, +// or face opposite of an ordered tetrahedron. + +inline tetgenmesh::point tetgenmesh::org(triface& t) { + return (point) (t).tet[orgpivot[(t).ver]]; +} + +inline tetgenmesh::point tetgenmesh:: dest(triface& t) { + return (point) (t).tet[destpivot[(t).ver]]; +} + +inline tetgenmesh::point tetgenmesh:: apex(triface& t) { + return (point) (t).tet[apexpivot[(t).ver]]; +} + +inline tetgenmesh::point tetgenmesh:: oppo(triface& t) { + return (point) (t).tet[oppopivot[(t).ver]]; +} + +inline void tetgenmesh:: setorg(triface& t, point p) { + (t).tet[orgpivot[(t).ver]] = (tetrahedron) (p); +} + +inline void tetgenmesh:: setdest(triface& t, point p) { + (t).tet[destpivot[(t).ver]] = (tetrahedron) (p); +} + +inline void tetgenmesh:: setapex(triface& t, point p) { + (t).tet[apexpivot[(t).ver]] = (tetrahedron) (p); +} + +inline void tetgenmesh:: setoppo(triface& t, point p) { + (t).tet[oppopivot[(t).ver]] = (tetrahedron) (p); +} + +#define setvertices(t, torg, tdest, tapex, toppo) \ + (t).tet[orgpivot[(t).ver]] = (tetrahedron) (torg);\ + (t).tet[destpivot[(t).ver]] = (tetrahedron) (tdest); \ + (t).tet[apexpivot[(t).ver]] = (tetrahedron) (tapex); \ + (t).tet[oppopivot[(t).ver]] = (tetrahedron) (toppo) + +// Check or set a tetrahedron's attributes. + +inline REAL tetgenmesh::elemattribute(tetrahedron* ptr, int attnum) { + return ((REAL *) (ptr))[elemattribindex + attnum]; +} + +inline void tetgenmesh::setelemattribute(tetrahedron* ptr, int attnum, + REAL value) { + ((REAL *) (ptr))[elemattribindex + attnum] = value; +} + +// Check or set a tetrahedron's maximum volume bound. + +inline REAL tetgenmesh::volumebound(tetrahedron* ptr) { + return ((REAL *) (ptr))[volumeboundindex]; +} + +inline void tetgenmesh::setvolumebound(tetrahedron* ptr, REAL value) { + ((REAL *) (ptr))[volumeboundindex] = value; +} + +// Get or set a tetrahedron's index (only used for output). +// These two routines use the reserved slot ptr[10]. + +inline int tetgenmesh::elemindex(tetrahedron* ptr) { + int *iptr = (int *) &(ptr[10]); + return iptr[0]; +} + +inline void tetgenmesh::setelemindex(tetrahedron* ptr, int value) { + int *iptr = (int *) &(ptr[10]); + iptr[0] = value; +} + +// Get or set a tetrahedron's marker. +// Set 'value = 0' cleans all the face/edge flags. + +inline int tetgenmesh::elemmarker(tetrahedron* ptr) { + return ((int *) (ptr))[elemmarkerindex]; +} + +inline void tetgenmesh::setelemmarker(tetrahedron* ptr, int value) { + ((int *) (ptr))[elemmarkerindex] = value; +} + +// infect(), infected(), uninfect() -- primitives to flag or unflag a +// tetrahedron. The last bit of the element marker is flagged (1) +// or unflagged (0). + +inline void tetgenmesh::infect(triface& t) { + ((int *) (t.tet))[elemmarkerindex] |= 1; +} + +inline void tetgenmesh::uninfect(triface& t) { + ((int *) (t.tet))[elemmarkerindex] &= ~1; +} + +inline bool tetgenmesh::infected(triface& t) { + return (((int *) (t.tet))[elemmarkerindex] & 1) != 0; +} + +// marktest(), marktested(), unmarktest() -- primitives to flag or unflag a +// tetrahedron. Use the second lowerest bit of the element marker. + +inline void tetgenmesh::marktest(triface& t) { + ((int *) (t.tet))[elemmarkerindex] |= 2; +} + +inline void tetgenmesh::unmarktest(triface& t) { + ((int *) (t.tet))[elemmarkerindex] &= ~2; +} + +inline bool tetgenmesh::marktested(triface& t) { + return (((int *) (t.tet))[elemmarkerindex] & 2) != 0; +} + +// markface(), unmarkface(), facemarked() -- primitives to flag or unflag a +// face of a tetrahedron. From the last 3rd to 6th bits are used for +// face markers, e.g., the last third bit corresponds to loc = 0. + +inline void tetgenmesh::markface(triface& t) { + ((int *) (t.tet))[elemmarkerindex] |= (4 << (t.ver & 3)); +} + +inline void tetgenmesh::unmarkface(triface& t) { + ((int *) (t.tet))[elemmarkerindex] &= ~(4 << (t.ver & 3)); +} + +inline bool tetgenmesh::facemarked(triface& t) { + return (((int *) (t.tet))[elemmarkerindex] & (4 << (t.ver & 3))) != 0; +} + +// markedge(), unmarkedge(), edgemarked() -- primitives to flag or unflag an +// edge of a tetrahedron. From the last 7th to 12th bits are used for +// edge markers, e.g., the last 7th bit corresponds to the 0th edge, etc. +// Remark: The last 7th bit is marked by 2^6 = 64. + +inline void tetgenmesh::markedge(triface& t) { + ((int *) (t.tet))[elemmarkerindex] |= (int) (64 << ver2edge[(t).ver]); +} + +inline void tetgenmesh::unmarkedge(triface& t) { + ((int *) (t.tet))[elemmarkerindex] &= ~(int) (64 << ver2edge[(t).ver]); +} + +inline bool tetgenmesh::edgemarked(triface& t) { + return (((int *) (t.tet))[elemmarkerindex] & + (int) (64 << ver2edge[(t).ver])) != 0; +} + +// marktest2(), unmarktest2(), marktest2ed() -- primitives to flag and unflag +// a tetrahedron. The 13th bit (2^12 = 4096) is used for this flag. + +inline void tetgenmesh::marktest2(triface& t) { + ((int *) (t.tet))[elemmarkerindex] |= (int) (4096); +} + +inline void tetgenmesh::unmarktest2(triface& t) { + ((int *) (t.tet))[elemmarkerindex] &= ~(int) (4096); +} + +inline bool tetgenmesh::marktest2ed(triface& t) { + return (((int *) (t.tet))[elemmarkerindex] & (int) (4096)) != 0; +} + +// elemcounter(), setelemcounter() -- primitives to read or ser a (small) +// integer counter in this tet. It is saved from the 16th bit. On 32 bit +// system, the range of the counter is [0, 2^15 = 32768]. + +inline int tetgenmesh::elemcounter(triface& t) { + return (((int *) (t.tet))[elemmarkerindex]) >> 16; +} + +inline void tetgenmesh::setelemcounter(triface& t, int value) { + int c = ((int *) (t.tet))[elemmarkerindex]; + // Clear the old counter while keep the other flags. + c &= 65535; // sum_{i=0^15} 2^i + c |= (value << 16); + ((int *) (t.tet))[elemmarkerindex] = c; +} + +inline void tetgenmesh::increaseelemcounter(triface& t) { + int c = elemcounter(t); + setelemcounter(t, c + 1); +} + +inline void tetgenmesh::decreaseelemcounter(triface& t) { + int c = elemcounter(t); + setelemcounter(t, c - 1); +} + +// ishulltet() tests if t is a hull tetrahedron. + +inline bool tetgenmesh::ishulltet(triface& t) { + return (point) (t).tet[7] == dummypoint; +} + +// isdeadtet() tests if t is a tetrahedron is dead. + +inline bool tetgenmesh::isdeadtet(triface& t) { + return ((t.tet == NULL) || (t.tet[4] == NULL)); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// Primitives for subfaces and subsegments // +// // +/////////////////////////////////////////////////////////////////////////////// + +// Each subface contains three pointers to its neighboring subfaces, with +// edge versions. To save memory, both information are kept in a single +// pointer. To make this possible, all subfaces are aligned to eight-byte +// boundaries, so that the last three bits of each pointer are zeros. An +// edge version (in the range 0 to 5) is compressed into the last three +// bits of each pointer by 'sencode()'. 'sdecode()' decodes a pointer, +// extracting an edge version and a pointer to the beginning of a subface. + +inline void tetgenmesh::sdecode(shellface sptr, face& s) { + s.shver = (int) ((uintptr_t) (sptr) & (uintptr_t) 7); + s.sh = (shellface *) ((uintptr_t) (sptr) ^ (uintptr_t) (s.shver)); +} + +inline tetgenmesh::shellface tetgenmesh::sencode(face& s) { + return (shellface) ((uintptr_t) s.sh | (uintptr_t) s.shver); +} + +inline tetgenmesh::shellface tetgenmesh::sencode2(shellface *sh, int shver) { + return (shellface) ((uintptr_t) sh | (uintptr_t) shver); +} + +// sbond() bonds two subfaces (s1) and (s2) together. s1 and s2 must refer +// to the same edge. No requirement is needed on their orientations. + +inline void tetgenmesh::sbond(face& s1, face& s2) +{ + s1.sh[s1.shver >> 1] = sencode(s2); + s2.sh[s2.shver >> 1] = sencode(s1); +} + +// sbond1() bonds s1 <== s2, i.e., after bonding, s1 is pointing to s2, +// but s2 is not pointing to s1. s1 and s2 must refer to the same edge. +// No requirement is needed on their orientations. + +inline void tetgenmesh::sbond1(face& s1, face& s2) +{ + s1.sh[s1.shver >> 1] = sencode(s2); +} + +// Dissolve a subface bond (from one side). Note that the other subface +// will still think it's connected to this subface. + +inline void tetgenmesh::sdissolve(face& s) +{ + s.sh[s.shver >> 1] = NULL; +} + +// spivot() finds the adjacent subface (s2) for a given subface (s1). +// s1 and s2 share at the same edge. + +inline void tetgenmesh::spivot(face& s1, face& s2) +{ + shellface sptr = s1.sh[s1.shver >> 1]; + sdecode(sptr, s2); +} + +inline void tetgenmesh::spivotself(face& s) +{ + shellface sptr = s.sh[s.shver >> 1]; + sdecode(sptr, s); +} + +// These primitives determine or set the origin, destination, or apex +// of a subface with respect to the edge version. + +inline tetgenmesh::point tetgenmesh::sorg(face& s) +{ + return (point) s.sh[sorgpivot[s.shver]]; +} + +inline tetgenmesh::point tetgenmesh::sdest(face& s) +{ + return (point) s.sh[sdestpivot[s.shver]]; +} + +inline tetgenmesh::point tetgenmesh::sapex(face& s) +{ + return (point) s.sh[sapexpivot[s.shver]]; +} + +inline void tetgenmesh::setsorg(face& s, point pointptr) +{ + s.sh[sorgpivot[s.shver]] = (shellface) pointptr; +} + +inline void tetgenmesh::setsdest(face& s, point pointptr) +{ + s.sh[sdestpivot[s.shver]] = (shellface) pointptr; +} + +inline void tetgenmesh::setsapex(face& s, point pointptr) +{ + s.sh[sapexpivot[s.shver]] = (shellface) pointptr; +} + +#define setshvertices(s, pa, pb, pc)\ + setsorg(s, pa);\ + setsdest(s, pb);\ + setsapex(s, pc) + +// sesym() reserves the direction of the lead edge. + +inline void tetgenmesh::sesym(face& s1, face& s2) +{ + s2.sh = s1.sh; + s2.shver = (s1.shver ^ 1); // Inverse the last bit. +} + +inline void tetgenmesh::sesymself(face& s) +{ + s.shver ^= 1; +} + +// senext() finds the next edge (counterclockwise) in the same orientation +// of this face. + +inline void tetgenmesh::senext(face& s1, face& s2) +{ + s2.sh = s1.sh; + s2.shver = snextpivot[s1.shver]; +} + +inline void tetgenmesh::senextself(face& s) +{ + s.shver = snextpivot[s.shver]; +} + +inline void tetgenmesh::senext2(face& s1, face& s2) +{ + s2.sh = s1.sh; + s2.shver = snextpivot[snextpivot[s1.shver]]; +} + +inline void tetgenmesh::senext2self(face& s) +{ + s.shver = snextpivot[snextpivot[s.shver]]; +} + + +// Check or set a subface's maximum area bound. + +inline REAL tetgenmesh::areabound(face& s) +{ + return ((REAL *) (s.sh))[areaboundindex]; +} + +inline void tetgenmesh::setareabound(face& s, REAL value) +{ + ((REAL *) (s.sh))[areaboundindex] = value; +} + +// These two primitives read or set a shell marker. Shell markers are used +// to hold user boundary information. + +inline int tetgenmesh::shellmark(face& s) +{ + return ((int *) (s.sh))[shmarkindex]; +} + +inline void tetgenmesh::setshellmark(face& s, int value) +{ + ((int *) (s.sh))[shmarkindex] = value; +} + + + +// sinfect(), sinfected(), suninfect() -- primitives to flag or unflag a +// subface. The last bit of ((int *) ((s).sh))[shmarkindex+1] is flagged. + +inline void tetgenmesh::sinfect(face& s) +{ + ((int *) ((s).sh))[shmarkindex+1] = + (((int *) ((s).sh))[shmarkindex+1] | (int) 1); +} + +inline void tetgenmesh::suninfect(face& s) +{ + ((int *) ((s).sh))[shmarkindex+1] = + (((int *) ((s).sh))[shmarkindex+1] & ~(int) 1); +} + +// Test a subface for viral infection. + +inline bool tetgenmesh::sinfected(face& s) +{ + return (((int *) ((s).sh))[shmarkindex+1] & (int) 1) != 0; +} + +// smarktest(), smarktested(), sunmarktest() -- primitives to flag or unflag +// a subface. The last 2nd bit of the integer is flagged. + +inline void tetgenmesh::smarktest(face& s) +{ + ((int *) ((s).sh))[shmarkindex+1] = + (((int *)((s).sh))[shmarkindex+1] | (int) 2); +} + +inline void tetgenmesh::sunmarktest(face& s) +{ + ((int *) ((s).sh))[shmarkindex+1] = + (((int *)((s).sh))[shmarkindex+1] & ~(int)2); +} + +inline bool tetgenmesh::smarktested(face& s) +{ + return ((((int *) ((s).sh))[shmarkindex+1] & (int) 2) != 0); +} + +// smarktest2(), smarktest2ed(), sunmarktest2() -- primitives to flag or +// unflag a subface. The last 3rd bit of the integer is flagged. + +inline void tetgenmesh::smarktest2(face& s) +{ + ((int *) ((s).sh))[shmarkindex+1] = + (((int *)((s).sh))[shmarkindex+1] | (int) 4); +} + +inline void tetgenmesh::sunmarktest2(face& s) +{ + ((int *) ((s).sh))[shmarkindex+1] = + (((int *)((s).sh))[shmarkindex+1] & ~(int)4); +} + +inline bool tetgenmesh::smarktest2ed(face& s) +{ + return ((((int *) ((s).sh))[shmarkindex+1] & (int) 4) != 0); +} + +// The last 4th bit of ((int *) ((s).sh))[shmarkindex+1] is flagged. + +inline void tetgenmesh::smarktest3(face& s) +{ + ((int *) ((s).sh))[shmarkindex+1] = + (((int *)((s).sh))[shmarkindex+1] | (int) 8); +} + +inline void tetgenmesh::sunmarktest3(face& s) +{ + ((int *) ((s).sh))[shmarkindex+1] = + (((int *)((s).sh))[shmarkindex+1] & ~(int)8); +} + +inline bool tetgenmesh::smarktest3ed(face& s) +{ + return ((((int *) ((s).sh))[shmarkindex+1] & (int) 8) != 0); +} + + +// Each facet has a unique index (automatically indexed). Starting from '0'. +// We save this index in the same field of the shell type. + +inline void tetgenmesh::setfacetindex(face& s, int value) +{ + ((int *) (s.sh))[shmarkindex + 2] = value; +} + +inline int tetgenmesh::getfacetindex(face& s) +{ + return ((int *) (s.sh))[shmarkindex + 2]; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// Primitives for interacting between tetrahedra and subfaces // +// // +/////////////////////////////////////////////////////////////////////////////// + +// tsbond() bond a tetrahedron (t) and a subface (s) together. +// Note that t and s must be the same face and the same edge. Moreover, +// t and s have the same orientation. +// Since the edge number in t and in s can be any number in {0,1,2}. We bond +// the edge in s which corresponds to t's 0th edge, and vice versa. + +inline void tetgenmesh::tsbond(triface& t, face& s) +{ + if ((t).tet[9] == NULL) { + // Allocate space for this tet. + (t).tet[9] = (tetrahedron) tet2subpool->alloc(); + // Initialize. + for (int i = 0; i < 4; i++) { + ((shellface *) (t).tet[9])[i] = NULL; + } + } + // Bond t <== s. + ((shellface *) (t).tet[9])[(t).ver & 3] = + sencode2((s).sh, tsbondtbl[t.ver][s.shver]); + // Bond s <== t. + s.sh[9 + ((s).shver & 1)] = + (shellface) encode2((t).tet, stbondtbl[t.ver][s.shver]); +} + +// tspivot() finds a subface (s) abutting on the given tetrahdera (t). +// Return s.sh = NULL if there is no subface at t. Otherwise, return +// the subface s, and s and t must be at the same edge wth the same +// orientation. + +inline void tetgenmesh::tspivot(triface& t, face& s) +{ + if ((t).tet[9] == NULL) { + (s).sh = NULL; + return; + } + // Get the attached subface s. + sdecode(((shellface *) (t).tet[9])[(t).ver & 3], (s)); + (s).shver = tspivottbl[t.ver][s.shver]; +} + +// Quickly check if the handle (t, v) is a subface. +#define issubface(t) \ + ((t).tet[9] && ((t).tet[9])[(t).ver & 3]) + +// stpivot() finds a tetrahedron (t) abutting a given subface (s). +// Return the t (if it exists) with the same edge and the same +// orientation of s. + +inline void tetgenmesh::stpivot(face& s, triface& t) +{ + decode((tetrahedron) s.sh[9 + (s.shver & 1)], t); + if ((t).tet == NULL) { + return; + } + (t).ver = stpivottbl[t.ver][s.shver]; +} + +// Quickly check if this subface is attached to a tetrahedron. + +#define isshtet(s) \ + ((s).sh[9 + ((s).shver & 1)]) + +// tsdissolve() dissolve a bond (from the tetrahedron side). + +inline void tetgenmesh::tsdissolve(triface& t) +{ + if ((t).tet[9] != NULL) { + ((shellface *) (t).tet[9])[(t).ver & 3] = NULL; + } +} + +// stdissolve() dissolve a bond (from the subface side). + +inline void tetgenmesh::stdissolve(face& s) +{ + (s).sh[9] = NULL; + (s).sh[10] = NULL; +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// Primitives for interacting between subfaces and segments // +// // +/////////////////////////////////////////////////////////////////////////////// + +// ssbond() bond a subface to a subsegment. + +inline void tetgenmesh::ssbond(face& s, face& edge) +{ + s.sh[6 + (s.shver >> 1)] = sencode(edge); + edge.sh[0] = sencode(s); +} + +inline void tetgenmesh::ssbond1(face& s, face& edge) +{ + s.sh[6 + (s.shver >> 1)] = sencode(edge); + //edge.sh[0] = sencode(s); +} + +// ssdisolve() dissolve a bond (from the subface side) + +inline void tetgenmesh::ssdissolve(face& s) +{ + s.sh[6 + (s.shver >> 1)] = NULL; +} + +// sspivot() finds a subsegment abutting a subface. + +inline void tetgenmesh::sspivot(face& s, face& edge) +{ + sdecode((shellface) s.sh[6 + (s.shver >> 1)], edge); +} + +// Quickly check if the edge is a subsegment. + +#define isshsubseg(s) \ + ((s).sh[6 + ((s).shver >> 1)]) + +/////////////////////////////////////////////////////////////////////////////// +// // +// Primitives for interacting between tetrahedra and segments // +// // +/////////////////////////////////////////////////////////////////////////////// + +inline void tetgenmesh::tssbond1(triface& t, face& s) +{ + if ((t).tet[8] == NULL) { + // Allocate space for this tet. + (t).tet[8] = (tetrahedron) tet2segpool->alloc(); + // Initialization. + for (int i = 0; i < 6; i++) { + ((shellface *) (t).tet[8])[i] = NULL; + } + } + ((shellface *) (t).tet[8])[ver2edge[(t).ver]] = sencode((s)); +} + +inline void tetgenmesh::sstbond1(face& s, triface& t) +{ + ((tetrahedron *) (s).sh)[9] = encode(t); +} + +inline void tetgenmesh::tssdissolve1(triface& t) +{ + if ((t).tet[8] != NULL) { + ((shellface *) (t).tet[8])[ver2edge[(t).ver]] = NULL; + } +} + +inline void tetgenmesh::sstdissolve1(face& s) +{ + ((tetrahedron *) (s).sh)[9] = NULL; +} + +inline void tetgenmesh::tsspivot1(triface& t, face& s) +{ + if ((t).tet[8] != NULL) { + sdecode(((shellface *) (t).tet[8])[ver2edge[(t).ver]], s); + } else { + (s).sh = NULL; + } +} + +// Quickly check whether 't' is a segment or not. + +#define issubseg(t) \ + ((t).tet[8] && ((t).tet[8])[ver2edge[(t).ver]]) + +inline void tetgenmesh::sstpivot1(face& s, triface& t) +{ + decode((tetrahedron) s.sh[9], t); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// Primitives for points // +// // +/////////////////////////////////////////////////////////////////////////////// + +inline int tetgenmesh::pointmark(point pt) { + return ((int *) (pt))[pointmarkindex]; +} + +inline void tetgenmesh::setpointmark(point pt, int value) { + ((int *) (pt))[pointmarkindex] = value; +} + + +// These two primitives set and read the type of the point. + +inline enum tetgenmesh::verttype tetgenmesh::pointtype(point pt) { + return (enum verttype) (((int *) (pt))[pointmarkindex + 1] >> (int) 8); +} + +inline void tetgenmesh::setpointtype(point pt, enum verttype value) { + ((int *) (pt))[pointmarkindex + 1] = + ((int) value << 8) + (((int *) (pt))[pointmarkindex + 1] & (int) 255); +} + +// Read and set the geometry tag of the point (used by -s option). + +inline int tetgenmesh::pointgeomtag(point pt) { + return ((int *) (pt))[pointmarkindex + 2]; +} + +inline void tetgenmesh::setpointgeomtag(point pt, int value) { + ((int *) (pt))[pointmarkindex + 2] = value; +} + +// Read and set the u,v coordinates of the point (used by -s option). + +inline REAL tetgenmesh::pointgeomuv(point pt, int i) { + return pt[pointparamindex + i]; +} + +inline void tetgenmesh::setpointgeomuv(point pt, int i, REAL value) { + pt[pointparamindex + i] = value; +} + +// pinfect(), puninfect(), pinfected() -- primitives to flag or unflag +// a point. The last bit of the integer '[pointindex+1]' is flagged. + +inline void tetgenmesh::pinfect(point pt) { + ((int *) (pt))[pointmarkindex + 1] |= (int) 1; +} + +inline void tetgenmesh::puninfect(point pt) { + ((int *) (pt))[pointmarkindex + 1] &= ~(int) 1; +} + +inline bool tetgenmesh::pinfected(point pt) { + return (((int *) (pt))[pointmarkindex + 1] & (int) 1) != 0; +} + +// pmarktest(), punmarktest(), pmarktested() -- more primitives to +// flag or unflag a point. + +inline void tetgenmesh::pmarktest(point pt) { + ((int *) (pt))[pointmarkindex + 1] |= (int) 2; +} + +inline void tetgenmesh::punmarktest(point pt) { + ((int *) (pt))[pointmarkindex + 1] &= ~(int) 2; +} + +inline bool tetgenmesh::pmarktested(point pt) { + return (((int *) (pt))[pointmarkindex + 1] & (int) 2) != 0; +} + +inline void tetgenmesh::pmarktest2(point pt) { + ((int *) (pt))[pointmarkindex + 1] |= (int) 4; +} + +inline void tetgenmesh::punmarktest2(point pt) { + ((int *) (pt))[pointmarkindex + 1] &= ~(int) 4; +} + +inline bool tetgenmesh::pmarktest2ed(point pt) { + return (((int *) (pt))[pointmarkindex + 1] & (int) 4) != 0; +} + +inline void tetgenmesh::pmarktest3(point pt) { + ((int *) (pt))[pointmarkindex + 1] |= (int) 8; +} + +inline void tetgenmesh::punmarktest3(point pt) { + ((int *) (pt))[pointmarkindex + 1] &= ~(int) 8; +} + +inline bool tetgenmesh::pmarktest3ed(point pt) { + return (((int *) (pt))[pointmarkindex + 1] & (int) 8) != 0; +} + +// These following primitives set and read a pointer to a tetrahedron +// a subface/subsegment, a point, or a tet of background mesh. + +inline tetgenmesh::tetrahedron tetgenmesh::point2tet(point pt) { + return ((tetrahedron *) (pt))[point2simindex]; +} + +inline void tetgenmesh::setpoint2tet(point pt, tetrahedron value) { + ((tetrahedron *) (pt))[point2simindex] = value; +} + +inline tetgenmesh::point tetgenmesh::point2ppt(point pt) { + return (point) ((tetrahedron *) (pt))[point2simindex + 1]; +} + +inline void tetgenmesh::setpoint2ppt(point pt, point value) { + ((tetrahedron *) (pt))[point2simindex + 1] = (tetrahedron) value; +} + +inline tetgenmesh::shellface tetgenmesh::point2sh(point pt) { + return (shellface) ((tetrahedron *) (pt))[point2simindex + 2]; +} + +inline void tetgenmesh::setpoint2sh(point pt, shellface value) { + ((tetrahedron *) (pt))[point2simindex + 2] = (tetrahedron) value; +} + + +inline tetgenmesh::tetrahedron tetgenmesh::point2bgmtet(point pt) { + return ((tetrahedron *) (pt))[point2simindex + 3]; +} + +inline void tetgenmesh::setpoint2bgmtet(point pt, tetrahedron value) { + ((tetrahedron *) (pt))[point2simindex + 3] = value; +} + + +// The primitives for saving and getting the insertion radius. +inline void tetgenmesh::setpointinsradius(point pt, REAL value) +{ + pt[pointinsradiusindex] = value; +} + +inline REAL tetgenmesh::getpointinsradius(point pt) +{ + return pt[pointinsradiusindex]; +} + +inline bool tetgenmesh::issteinerpoint(point pt) { + return (pointtype(pt) == FREESEGVERTEX) || (pointtype(pt) == FREEFACETVERTEX) + || (pointtype(pt) == FREEVOLVERTEX); +} + +// point2tetorg() Get the tetrahedron whose origin is the point. + +inline void tetgenmesh::point2tetorg(point pa, triface& searchtet) +{ + decode(point2tet(pa), searchtet); + if ((point) searchtet.tet[4] == pa) { + searchtet.ver = 11; + } else if ((point) searchtet.tet[5] == pa) { + searchtet.ver = 3; + } else if ((point) searchtet.tet[6] == pa) { + searchtet.ver = 7; + } else { + searchtet.ver = 0; + } +} + +// point2shorg() Get the subface/segment whose origin is the point. + +inline void tetgenmesh::point2shorg(point pa, face& searchsh) +{ + sdecode(point2sh(pa), searchsh); + if ((point) searchsh.sh[3] == pa) { + searchsh.shver = 0; + } else if ((point) searchsh.sh[4] == pa) { + searchsh.shver = (searchsh.sh[5] != NULL ? 2 : 1); + } else { + searchsh.shver = 4; + } +} + +// farsorg() Return the origin of the subsegment. +// farsdest() Return the destination of the subsegment. + +inline tetgenmesh::point tetgenmesh::farsorg(face& s) +{ + face travesh, neighsh; + + travesh = s; + while (1) { + senext2(travesh, neighsh); + spivotself(neighsh); + if (neighsh.sh == NULL) break; + if (sorg(neighsh) != sorg(travesh)) sesymself(neighsh); + senext2(neighsh, travesh); + } + return sorg(travesh); +} + +inline tetgenmesh::point tetgenmesh::farsdest(face& s) +{ + face travesh, neighsh; + + travesh = s; + while (1) { + senext(travesh, neighsh); + spivotself(neighsh); + if (neighsh.sh == NULL) break; + if (sdest(neighsh) != sdest(travesh)) sesymself(neighsh); + senext(neighsh, travesh); + } + return sdest(travesh); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// Linear algebra operators. // +// // +/////////////////////////////////////////////////////////////////////////////// + +// dot() returns the dot product: v1 dot v2. +inline REAL tetgenmesh::dot(REAL* v1, REAL* v2) +{ + return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; +} + +// cross() computes the cross product: n = v1 cross v2. +inline void tetgenmesh::cross(REAL* v1, REAL* v2, REAL* n) +{ + n[0] = v1[1] * v2[2] - v2[1] * v1[2]; + n[1] = -(v1[0] * v2[2] - v2[0] * v1[2]); + n[2] = v1[0] * v2[1] - v2[0] * v1[1]; +} + +// distance() computes the Euclidean distance between two points. +inline REAL tetgenmesh::distance(REAL* p1, REAL* p2) +{ + return sqrt((p2[0] - p1[0]) * (p2[0] - p1[0]) + + (p2[1] - p1[1]) * (p2[1] - p1[1]) + + (p2[2] - p1[2]) * (p2[2] - p1[2])); +} + +inline REAL tetgenmesh::norm2(REAL x, REAL y, REAL z) +{ + return (x) * (x) + (y) * (y) + (z) * (z); +} + +#endif // tetgenBRH