diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 844ef46f9..ecf75da72 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,6 +1,7 @@ cmake_minimum_required(VERSION 3.13) project(PrusaSlicer-native) +add_subdirectory(mesh_fix) add_subdirectory(build-utils) add_subdirectory(admesh) add_subdirectory(avrdude) diff --git a/src/mesh_fix/CMakeLists.txt b/src/mesh_fix/CMakeLists.txt new file mode 100644 index 000000000..073e912aa --- /dev/null +++ b/src/mesh_fix/CMakeLists.txt @@ -0,0 +1,36 @@ +################################################################################ + +set(SOURCES + src/TMesh/edge.cpp + src/TMesh/vertex.cpp + src/TMesh/io.cpp + src/TMesh/triangle.cpp + src/TMesh/tin.cpp + src/Algorithms/detectIntersections.cpp + src/Algorithms/holeFilling.cpp + src/Algorithms/marchIntersections.cpp + src/Algorithms/checkAndRepair.cpp + src/Kernel/heap.cpp + src/Kernel/matrix.cpp + src/Kernel/orientation.c + src/Kernel/list.cpp + src/Kernel/coordinates.cpp + src/Kernel/tmesh.cpp + src/Kernel/graph.cpp + src/Kernel/point.cpp + src/Kernel/jqsort.cpp + src/MeshFix/meshfix.cpp +) + +################################################################################ + +add_library(meshfix ${SOURCES}) + +target_include_directories(meshfix PUBLIC + include/TMesh + include/Kernel +) + +target_compile_options(meshfix PRIVATE -fpermissive) + +################################################################################ diff --git a/src/mesh_fix/gpl-3.0.txt b/src/mesh_fix/gpl-3.0.txt new file mode 100644 index 000000000..94a9ed024 --- /dev/null +++ b/src/mesh_fix/gpl-3.0.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. 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 +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. 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. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program 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, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU 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 Lesser General +Public License instead of this License. But first, please read +. diff --git a/src/mesh_fix/include/Kernel/basics.h b/src/mesh_fix/include/Kernel/basics.h new file mode 100644 index 000000000..406076e00 --- /dev/null +++ b/src/mesh_fix/include/Kernel/basics.h @@ -0,0 +1,157 @@ +/**************************************************************************** +* TMesh * +* * +* Consiglio Nazionale delle Ricerche * +* Istituto di Matematica Applicata e Tecnologie Informatiche * +* Sezione di Genova * +* IMATI-GE / CNR * +* * +* Authors: Marco Attene * +* Copyright(C) 2012: IMATI-GE / CNR * +* All rights reserved. * +* * +* This program is dual-licensed as follows: * +* * +* (1) You may use TMesh as 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 3 of the License, or * +* (at your option) any later version. * +* In this case the 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 (http://www.gnu.org/licenses/gpl.txt) * +* for more details. * +* * +* (2) You may use TMesh as part of a commercial software. In this case a * +* proper agreement must be reached with the Authors and with IMATI-GE/CNR * +* based on a proper licensing contract. * +* * +****************************************************************************/ + +#ifndef _BASICS_H +#define _BASICS_H + +#include +#include +#include +#include +#include +#include "coordinates.h" + +namespace T_MESH +{ + +#ifdef EXTENSIBLE_TMESH +#define TMESH_VIRTUAL virtual +#else +#define TMESH_VIRTUAL +#endif + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#define TMESH_VERSION "2.0" +#define TMESH_YEAR 2012 + +class TMesh +{ + public: + + static void (*display_message)(const char *, int); + static const char *app_name; + static const char *app_version; + static const char *app_year; + static const char *app_authors; + static const char *app_url; + static const char *app_maillist; + + static const char *filename; // This might be null. If not, it represents the file we are currently working with. + + static bool quiet; + + static void init(void (*)(const char *, int) = NULL); + + static void info(const char *, ...); + static void warning(const char *, ...); + static void error(const char *, ...); + static void begin_progress(); + static void report_progress(const char *, ...); + static void end_progress(); + + //! When called with a nonzero argument 'ts', launches a chronometer with a timeout of 'ts' seconds. + //! Later calls without arguments check the chronometer, and if it is over 'ts' the program exits. + static void exitOnTimeout(clock_t ts = 0); + + //! Appends a line to the file "tmesh.log" + static void addMessageToLogFile(const char *msg); + + //! Formats a message headed with date/time/filename, appends it to "tmesh.log", and exits with error + static void logToFileAndExit(const char *msg); + + //! When called without arguments prints the elapsed time from the latest reset. + static void printElapsedTime(bool reset = false); + + static void useRationals(bool u); + static bool isUsingRationals(); + static void useFiltering(bool u); + static bool isUsingFiltering(); + + //! Returns the status before the switch + static bool useRationals() { bool t = isUsingRationals(); useRationals(true); return t; } + + static void setFilename(const char *fname) { filename = fname; } +}; + +#define DISPMSG_ACTION_SETWIDGET 1 +#define DISPMSG_ACTION_PUTNEWLINE 2 +#define DISPMSG_ACTION_PUTPROGRESS 3 +#define DISPMSG_ACTION_PUTMESSAGE 4 +#define DISPMSG_ACTION_ERRORDIALOG 5 + +#ifndef _INC_WINDOWS +typedef unsigned char UBYTE; +typedef signed char BYTE; +typedef unsigned short UINT16; +typedef signed short INT16; +#endif + +#ifdef IS64BITPLATFORM +typedef long int j_voidint; +#else +typedef int j_voidint; +#endif + +#define UBYTE_MAX 255 + +#ifndef UINT16_MAX +#define UINT16_MAX 65535 +#endif + +#define FABS(a) (((a)<0)?(-(a)):(a)) +#define LOG2(a) (log(a)/log(2)) +#define PI2 (M_PI/2.0) +#ifndef MIN +#define MIN(a,b) (((a)<(b))?(a):(b)) +#endif +#ifndef MAX +#define MAX(a,b)(((a)>(b))?(a):(b)) +#endif + + +//////// Swaps two pointers. /////////////////////////////// + +inline void p_swap(void **a, void **b) {void *t = *a; *a = *b; *b = t;} + +///////////////////////////////////////////////////////////////////////////////////////////// + +class Data { +public: + virtual ~Data() = default; +}; + + +} //namespace T_MESH + +#endif //_BASICS_H + diff --git a/src/mesh_fix/include/Kernel/coordinates.h b/src/mesh_fix/include/Kernel/coordinates.h new file mode 100644 index 000000000..0d42228df --- /dev/null +++ b/src/mesh_fix/include/Kernel/coordinates.h @@ -0,0 +1,178 @@ +/**************************************************************************** +* TMesh * +* * +* Consiglio Nazionale delle Ricerche * +* Istituto di Matematica Applicata e Tecnologie Informatiche * +* Sezione di Genova * +* IMATI-GE / CNR * +* * +* Authors: Marco Attene * +* Copyright(C) 2012: IMATI-GE / CNR * +* All rights reserved. * +* * +* This program is dual-licensed as follows: * +* * +* (1) You may use TMesh as 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 3 of the License, or * +* (at your option) any later version. * +* In this case the 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 (http://www.gnu.org/licenses/gpl.txt) * +* for more details. * +* * +* (2) You may use TMesh as part of a commercial software. In this case a * +* proper agreement must be reached with the Authors and with IMATI-GE/CNR * +* based on a proper licensing contract. * +* * +****************************************************************************/ + +#ifndef _COORDINATES_H +#define _COORDINATES_H + +#include +#include +#include +#include + +#ifdef USE_HYBRID_KERNEL + +#ifdef USE_CGAL_LAZYNT +#include +#include +#include + +typedef CGAL::Lazy_exact_nt EXACT_NT; +#define EXACT_NT_TO_DOUBLE(x) (CGAL::to_double(x)) +#define EXACT_NT_DENOMINATOR(x) ((x)->exact().denominator()) +#define EXACT_NT_NUMERATOR(x) ((x)->exact().numerator()) + +#else +#include + +typedef mpq_class EXACT_NT; +#define EXACT_NT_TO_DOUBLE(x) ((x).get_d()) +#define EXACT_NT_DENOMINATOR(x) ((x)->get_den()) +#define EXACT_NT_NUMERATOR(x) ((x)->get_num()) + +#endif + +#endif + +#ifndef __APPLE__ +#include +#endif + +namespace T_MESH +{ + +#ifdef USE_HYBRID_KERNEL +class PM_Rational + { + private: + // Only one of the following two static members can be true at any time, but both of them can be false + // Thus, three configurations are possible: + // 1) APPROXIMATED: use_rationals = false, use_filtering = false + // 2) FILTERED: use_rationals = false, use_filtering = true + // 3) PRECISE: use_rationals = true, use_filtering = false + static bool use_rationals; // Kernel_mode uses rational numbers (precise) + static bool use_filtering; // Kernel_mode uses filtering + + public: + inline static bool isUsingRationals() { return use_rationals; } + inline static bool isUsingFiltering() { return use_filtering; } + static void useRationals(bool v) { use_rationals = v; use_filtering = !v; } + static void useFiltering(bool v) { use_filtering = v; if (v) use_rationals = false; } + + protected: + int64_t _val; // value. Might contain either a double or a pointer to mpq_class. + bool _whv; // Which value type is stored here. 1=rational, 0=double + + inline bool isOfRationalType() const { return _whv; } + inline bool isOfDoubleType() const { return !_whv; } + + inline static int64_t d2int64t(double a) { return *((int64_t *)((void *)(&a))); } + inline static double& int64t2d(const int64_t& a) { return *((double *)((void *)(&a))); } + + inline EXACT_NT& getVal() { return *((EXACT_NT *)_val); } + inline double& getDVal() { return int64t2d(_val); } + + inline const EXACT_NT& getVal() const { return *((EXACT_NT *)_val); } + inline const double& getDVal() const { return int64t2d(_val); } + + void switchToDouble(); + void switchToRational(); + + public: + PM_Rational() : _whv(0) {} // Undetermined double + PM_Rational(const EXACT_NT& a) { _whv = use_rationals; _val = (_whv) ? ((int64_t)new EXACT_NT(a)) : (d2int64t(EXACT_NT_TO_DOUBLE(a))); } + PM_Rational(float a) { _whv = use_rationals; _val = (_whv) ? ((int64_t)new EXACT_NT(a)) : (d2int64t(a)); } + PM_Rational(double a) { _whv = use_rationals; _val = (_whv) ? ((int64_t)new EXACT_NT(a)) : (d2int64t(a)); } + PM_Rational(int a) { _whv = use_rationals; _val = (_whv) ? ((int64_t)new EXACT_NT(a)) : (d2int64t(a)); } + + PM_Rational(const PM_Rational& a) { _whv = a._whv; _val = (_whv) ? ((int64_t)new EXACT_NT(a.getVal())) : (a._val); } + ~PM_Rational() { if (_whv) delete ((EXACT_NT *)_val); } + + inline EXACT_NT toRational() const { return (_whv) ? (getVal()) : (EXACT_NT(getDVal())); } + + // The following toSomething() functions may cause rounding. Use with caution ! + inline double toDouble() const { return ((_whv) ? (EXACT_NT_TO_DOUBLE(getVal())) : (getDVal())); } + inline int toInt() const { return int(toDouble()); } + inline float toFloat() const { return float(toDouble()); } + + void operator+=(const PM_Rational& a); + void operator-=(const PM_Rational& a); + void operator*=(const PM_Rational& a); + void operator/=(const PM_Rational& a); + PM_Rational operator+(const PM_Rational& a) const; + PM_Rational operator-(const PM_Rational& a) const; + PM_Rational operator*(const PM_Rational& a) const; + PM_Rational operator/(const PM_Rational& a) const; + bool operator==(const PM_Rational& a) const; + bool operator!=(const PM_Rational& a) const; + + PM_Rational& operator=(const PM_Rational& a); + void setFromRational(const EXACT_NT& a); + + bool operator<(const PM_Rational& a) const; + bool operator>(const PM_Rational& a) const; + inline bool operator<=(const PM_Rational& a) const { return (operator==(a) || operator<(a)); } + inline bool operator>=(const PM_Rational& a) const { return (operator==(a) || operator>(a)); } + + friend PM_Rational orient2D(const PM_Rational& px, const PM_Rational& py, const PM_Rational& qx, const PM_Rational& qy, const PM_Rational& rx, const PM_Rational& ry); + friend PM_Rational orient3D(const class Point *t, const class Point *a, const class Point *b, const class Point *c); +}; + +PM_Rational operator-(const PM_Rational& a); +PM_Rational ceil(const PM_Rational& a); +PM_Rational floor(const PM_Rational& a); +PM_Rational round(const PM_Rational& a); + +/**************** I/O operators ****************/ + +inline std::ostream & operator<<(std::ostream &o, const PM_Rational& c) { return o << c.toRational(); } +inline std::istream & operator>>(std::istream &i, PM_Rational& c) { EXACT_NT a; i >> a; c.setFromRational(a); return i; } + +#define TMESH_TO_DOUBLE(x) ((x).toDouble()) +#define TMESH_TO_FLOAT(x) ((x).toFloat()) +#define TMESH_TO_INT(x) ((x).toInt()) + +#else + +typedef double PM_Rational; + +#define TMESH_TO_DOUBLE(x) (x) +#define TMESH_TO_FLOAT(x) ((float)(x)) +#define TMESH_TO_INT(x) ((int)(x)) + +#endif + +typedef PM_Rational coord; + +#define TMESH_DETERMINANT3X3(a11, a12, a13, a21, a22, a23, a31, a32, a33) ((a11)*((a22)*(a33) - (a23)*(a32)) - (a12)*((a21)*(a33) - (a23)*(a31)) + (a13)*((a21)*(a32) - (a22)*(a31))) + +} //namespace T_MESH + +#endif //_COORDINATES_H + diff --git a/src/mesh_fix/include/Kernel/graph.h b/src/mesh_fix/include/Kernel/graph.h new file mode 100644 index 000000000..2fcd1f596 --- /dev/null +++ b/src/mesh_fix/include/Kernel/graph.h @@ -0,0 +1,145 @@ +/**************************************************************************** +* TMesh * +* * +* Consiglio Nazionale delle Ricerche * +* Istituto di Matematica Applicata e Tecnologie Informatiche * +* Sezione di Genova * +* IMATI-GE / CNR * +* * +* Authors: Marco Attene * +* Copyright(C) 2012: IMATI-GE / CNR * +* All rights reserved. * +* * +* This program is dual-licensed as follows: * +* * +* (1) You may use TMesh as 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 3 of the License, or * +* (at your option) any later version. * +* In this case the 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 (http://www.gnu.org/licenses/gpl.txt) * +* for more details. * +* * +* (2) You may use TMesh as part of a commercial software. In this case a * +* proper agreement must be reached with the Authors and with IMATI-GE/CNR * +* based on a proper licensing contract. * +* * +****************************************************************************/ + +#ifndef _GRAPH_H +#define _GRAPH_H + +#include "list.h" +#include "basics.h" + +namespace T_MESH +{ + +//! Base class type for nodes of non-oriented graphs + +class graphNode : public Data +{ + public: + + //! List of incident edges. + List edges; + + //! Generic 8-bit mask for marking purposes. + unsigned char mask; + + graphNode() {mask=0;} + TMESH_VIRTUAL ~graphNode() {} + + //! Returns TRUE if the node is isolated. O(1). + bool isIsolated() {return (edges.numels()==0);} + + //! Returns the edge connecting this with 'n'. NULL if not connected. O(degree). + class graphEdge *getEdge(graphNode *n); +}; + + +//! Base class type for edges of non-oriented graphs + +class graphEdge : public Data +{ + public: + + //! Edge's end-points + graphNode *n1, *n2; + + //! Generic info attached to the edge + void *info; + + //! Generic 8-bit mask for marking purposes. + unsigned char mask; + + //! Constructor. + graphEdge(graphNode *, graphNode *); + TMESH_VIRTUAL ~graphEdge() {} + + //! Returns the node oppsite to 'n'. O(1). + graphNode *oppositeNode(graphNode *n) {return (n1==n)?(n2):((n2==n)?(n1):(NULL));} + + //! Returns TRUE if this edge does not connect points. O(1). + bool isUnlinked() {return (n1==NULL);} + + //! Returns TRUE if 'n' is a node of this edge. O(1). + bool hasNode(graphNode *n) {return (n1==n || n2==n);} + + //! Makes this edge as 'unlinked' from the graph. O(1). + void makeUnlinked() {n1=NULL; n2=NULL;} + + //! Edge collapse. O(degree of neighbors). + + //! After one (or a series of) collapse, remember to call Graph::deleteUnlinkedElements() + //! to make the graph coherent with its node and edge lists. + void collapse(); + + //! Inverts the edge's orientation + void invert(); +}; + + +//! Base class type for non oriented graphs + +class Graph +{ + public: + + //! Nodes and edges of the graph. + List nodes, edges; + + ~Graph(); + + //! Adds an existing isolated node to the graph. O(1). + graphNode *addNode(graphNode *n) {nodes.appendHead(n); return n;} + + //! Creates a new edge out of a pair of nodes. O(degree of nodes). + + //! If the edges already exists, no new edge is created and the + //! existing one is returned. Otherwise the newly created edge is returned. + graphEdge *createEdge(graphNode *n1, graphNode *n2); + + //! Unlinks the edge and updates the graph connectivity accordingly. + void unlinkEdge(graphEdge *e); + + //! Removes and deletes the edge. Updates the graph connectivity accordingly. + void destroyEdge(graphEdge *e); + + //! Removes a node and updates the graph connectivity accordingly. + //! The unlinked node is returned. + graphNode *unlinkNode(graphNode *n); + + //! Eliminates isolated nodes and unlinked edges from the lists. O(N). + //! The eliminated elements are deleted too. + void deleteUnlinkedElements(); + + + bool isConnected(); +}; + +} //namespace T_MESH + +#endif // _GRAPH_H diff --git a/src/mesh_fix/include/Kernel/heap.h b/src/mesh_fix/include/Kernel/heap.h new file mode 100644 index 000000000..bfe7164e2 --- /dev/null +++ b/src/mesh_fix/include/Kernel/heap.h @@ -0,0 +1,94 @@ +/**************************************************************************** +* TMesh * +* * +* Consiglio Nazionale delle Ricerche * +* Istituto di Matematica Applicata e Tecnologie Informatiche * +* Sezione di Genova * +* IMATI-GE / CNR * +* * +* Authors: Marco Attene * +* Copyright(C) 2012: IMATI-GE / CNR * +* All rights reserved. * +* * +* This program is dual-licensed as follows: * +* * +* (1) You may use TMesh as 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 3 of the License, or * +* (at your option) any later version. * +* In this case the 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 (http://www.gnu.org/licenses/gpl.txt) * +* for more details. * +* * +* (2) You may use TMesh as part of a commercial software. In this case a * +* proper agreement must be reached with the Authors and with IMATI-GE/CNR * +* based on a proper licensing contract. * +* * +****************************************************************************/ + +#ifndef _HEAP_H +#define _HEAP_H + +#include "basics.h" + +namespace T_MESH +{ + +//! Heap base class type. + +//! abstractHeap is the base class for implementing heaps. +//! Each implementation (class extension) must define the method +//! compare to be used for sorting the heap. If the objects being +//! sorted are non-negative numbers, a special implementation may +//! use the field positions to record the index of each element +//! within the heap. This feature is useful when there is a need to +//! re-sort an element whose cost changes after its insertion into +//! the heap. The array 'positions' must be allocated by +//! the extended class constructor, and must be able to contain NMAX+1 +//! integer numbers, where NMAX is the maximum value that can be +//! assumed by an object. + + +class abstractHeap +{ + protected: + void **heap; //!< Heap data is stored here + int numels; //!< Current number of elements + int maxels; //!< Maximum number of elements + int *positions; //!< Optional pointer to an array of positions + + int upheap(int i); //!< Moves the i'th object up on the heap + int downheap(int i); //!< Moves the i'th object down on the heap + + //! Comparison of two heap elements + + //! This function must be implemented in the extended class. + //! The return value must be <0 if a0 if a>b or 0 if a=b. + + virtual int compare(const void *a, const void *b) = 0; + + public : + + abstractHeap(int n); //!< Creates a heap which can contain up to 'n' elements + virtual ~abstractHeap() = 0; //!< Default destructor + + //! Inserts 'e' into the heap + + //! Inserts an element 'e' into the heap in the correct position, according to the + //! method compare. If the insertion fails because the heap is full, -1 is + //! returned, otherwise the index position of the newly inserted element is + //! returned. + + int insert(void *e); + + int isEmpty() const {return (numels == 0);} //!< Returns TRUE if the heap is empty + void *getHead() const {return heap[1];} //!< Returns the first element of the heap + void *removeHead(); //!< Removes and returns the first element after rearranging the heap + void flush() {numels=0;} //!< Removes all the elements +}; + +} //namespace T_MESH + +#endif // _HEAP_H diff --git a/src/mesh_fix/include/Kernel/jqsort.h b/src/mesh_fix/include/Kernel/jqsort.h new file mode 100644 index 000000000..57b4cc046 --- /dev/null +++ b/src/mesh_fix/include/Kernel/jqsort.h @@ -0,0 +1,50 @@ +/**************************************************************************** +* TMesh * +* * +* Consiglio Nazionale delle Ricerche * +* Istituto di Matematica Applicata e Tecnologie Informatiche * +* Sezione di Genova * +* IMATI-GE / CNR * +* * +* Authors: Marco Attene * +* Copyright(C) 2012: IMATI-GE / CNR * +* All rights reserved. * +* * +* This program is dual-licensed as follows: * +* * +* (1) You may use TMesh as 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 3 of the License, or * +* (at your option) any later version. * +* In this case the 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 (http://www.gnu.org/licenses/gpl.txt) * +* for more details. * +* * +* (2) You may use TMesh as part of a commercial software. In this case a * +* proper agreement must be reached with the Authors and with IMATI-GE/CNR * +* based on a proper licensing contract. * +* * +****************************************************************************/ + +//! \file +//! \brief Declaration of a generic QuickSort function. +//! +//! The jqsort() function sorts an array with numels elements. +//! The v argument points to the start of the array of elements casted to void *. +//! The contents of the array are sorted in ascending order according to a +//! comparison function pointed to by comp, which is called with two +//! arguments that point to the objects being compared. +//! The comparison function must return an integer less than, equal to, or +//! greater than zero if the first argument is considered to be respectively +//! less than, equal to, or greater than the second. If two members +//! compare as equal, their order in the sorted array is undefined. +//! See the manpage of the standard library qsort() function for further information. + +namespace T_MESH +{ + +extern void jqsort(Data *v[], int numels, int (*comp)(const Data *, const Data *)); + +} //namespace T_MESH diff --git a/src/mesh_fix/include/Kernel/list.h b/src/mesh_fix/include/Kernel/list.h new file mode 100644 index 000000000..ba2c77d72 --- /dev/null +++ b/src/mesh_fix/include/Kernel/list.h @@ -0,0 +1,183 @@ +/**************************************************************************** +* TMesh * +* * +* Consiglio Nazionale delle Ricerche * +* Istituto di Matematica Applicata e Tecnologie Informatiche * +* Sezione di Genova * +* IMATI-GE / CNR * +* * +* Authors: Marco Attene * +* Copyright(C) 2012: IMATI-GE / CNR * +* All rights reserved. * +* * +* This program is dual-licensed as follows: * +* * +* (1) You may use TMesh as 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 3 of the License, or * +* (at your option) any later version. * +* In this case the 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 (http://www.gnu.org/licenses/gpl.txt) * +* for more details. * +* * +* (2) You may use TMesh as part of a commercial software. In this case a * +* proper agreement must be reached with the Authors and with IMATI-GE/CNR * +* based on a proper licensing contract. * +* * +****************************************************************************/ + +#ifndef _JLIST_H +#define _JLIST_H + +#include +#include "basics.h" + +namespace T_MESH +{ + +///////////////////////////////////////////////////////////////////////////////////////////// + +//! Generic node of a doubly likned list. + +class Node +{ + friend class List; // This is to make methods in 'List' able to modify n_prev and n_next + + public : + Data *data; //!< Actual data stored in the node + + //! Creates an isolated node storing 'd' + Node(const void *d) {data=(Data *)d; n_prev=n_next=NULL;} + + //! Creates a new node storing 'd' and links it to a previous node 'p' and to a next one 'n'. + Node(const Node *p, const Data *d, const Node *n); + ~Node(); //!< Standard destructor + + inline Node *prev() const {return n_prev;} //!< Returns the previous node in the list, possibly NULL + inline Node *next() const {return n_next;} //!< Returns the next node in the list, possibly NULL + + protected: + Node *n_prev,*n_next; //!< Previous and next node pointers +}; + + +///////////////////////////////////////////////////////////////////////////////////////////// + +//! Doubly linked list. + + +class List : public Data +{ + protected : + + Node *l_head; //!< First node pointer + Node *l_tail; //!< Last node pointer + int l_numels; //!< Number of elements in the list + + public : + + //! Creates an empty list + List() {l_head = l_tail = NULL; l_numels = 0;} + + //! Creates a list containing an element 'd' (singleton) + List(const Data *d) {l_head = l_tail = new Node(d); l_numels = 1;} + + //! Creates a list out of an array 'd' made of 'n' elements. + List(const Data **d, int n); + + //! Creates a duplicated list. + List(List& l) {l_head = l_tail = NULL; l_numels = 0; appendList(&l);} + + //! Creates a duplicated list. + List(List* l) {l_head = l_tail = NULL; l_numels = 0; appendList(l);} + + //! Destructor + ~List(); + + Node *head() const {return l_head;} //!< Gets the first node, NULL if empty. \n O(1). + Node *tail() const {return l_tail;} //!< Gets the last node, NULL if empty. \n O(1). + int numels() const {return l_numels;} //!< Gets the number of elements. \n O(1). + + void appendHead(const Data *d); //!< Appends a new node storing 'd' to the head. \n O(1). + void appendTail(const Data *d); //!< Appends a new node storing 'd' to the tail. \n O(1). + void insertAfter(Node *n, const Data *d); //! Inserts a new node storing 'd' right after 'n'. \n O(1). + + //! Deletes and removes the node containing 'd'. Returns its position, 0 if 'd' is not in the list. \n O(numels()). + int removeNode(const Data *d); + + //! Deletes and i'th node (starting from 0). Returns 0 if the list has less than i+1 nodes. \n O(numels()). + int removeNode(int i); + + //! Returns the node at position 'i' (starting from 0). Returns NULL if the list has less than i+1 nodes. \n O(numels()). + Node *getNode(int i) const; + + //! Deletes and removes the node 'n' from the list. \n O(1). + void removeCell(Node *n); + + //! Appends a list 'l' to the head by duplicating nodes in 'l'. \n O(l->numels()). + void appendList(const List *l); + + //! Appends a list 'l' to the tail by linking the first node of 'l' to the last one of this list. 'l' becomes empty. \n O(1). + void joinTailList(List *l); + + //! Moves node 'n' from this list to the end of 'l'. \n O(1). + void moveNodeTo(Node *n, List *l); + + Data *popHead(); //!< Deletes and removes the first node. Returns its data. \n O(1). + Data *popTail(); //!< Deletes and removes the last node. Returns its data. \n O(1). + + //! Deletes and removes the node 'n' from the list and frees data memory. \n O(1). + + // NOTE: The following is not true after refactoring, void* has been replaced with Data* + //! Warning. This method uses the free() function to to dispose the memory space + //! used by the data stored in the node. This means that such data should have + //! been allocated through malloc(), calloc() or realloc(), and not through the + //! 'new' operator. On some systems, however, the 'delete' operator simply calls 'free()' + //! right after the execution of the proper object destructor so, if the object + //! does not need to free internally allocated memory, it is safe to dispose the + //! memory trhough free() although the object was allocated by 'new'. This works + //! on Linux Fedora Core 2 distributions. + void freeCell(Node *n); + + //! Deletes and removes the node storing 'd' and frees the memory occupied by 'd' itself. \n O(numels()). + + //! Warning. Read the comment for the method 'freeCell()' + void freeNode(Data *d); + + //! Returns the node storing 'd'. NULL if not found. \n O(numels()). + Node *containsNode(const Data *d) const; + + //! Replaces old_n with new_n. The Node containing new_n is returned. \n O(numels()). + Node *replaceNode(const Data *old_n, const Data *new_n); + + //! Deletes and removes all the nodes and frees data memory. \n O(numels()). + + //! Warning. Read the comment for the method 'freeCell()' + void freeNodes(); + + void removeNodes(); //!< Deletes and removes all the nodes. \n O(numels()). + + Data **toArray() const; //!< Creates an array out of the list. \n O(numels()). + + //! Sorts the list using 'comp' as comparison function for two elements. \n O(numels()^2). + + //! This method uses the QuickSort algorithm for sorting, thus the complexity is N^2 in the + //! worst case, but it is actually much faster in the average case. If, however, there is + //! the need to have a guaranteed O(NlogN) complexity, it is possible to implement a heap + //! based on the 'abstractHeap' class. See the documentation of the standard 'qsort' library + //! function for details on the prototype of the comparison function 'comp'. + int sort(int (*comp)(const Data *, const Data *)); +}; + +//! Convenience macro to scan the nodes of a list. +#define FOREACHNODE(l, n) for ((n) = (l).head(); (n) != NULL; (n)=(n)->next()) + +//! Convenience macro to circulate around the nodes of a list 'l' starting from node 'm'. Must exit with break or return. +#define FOREACHNODECIRCULAR(l, m, n) for ((n) = (m); ; (n)=((n)!=(l).tail())?((n)->next()):((l).head())) + +} //namespace T_MESH + +#endif // _JLIST_H + diff --git a/src/mesh_fix/include/Kernel/matrix.h b/src/mesh_fix/include/Kernel/matrix.h new file mode 100644 index 000000000..3bd222399 --- /dev/null +++ b/src/mesh_fix/include/Kernel/matrix.h @@ -0,0 +1,301 @@ +/**************************************************************************** +* TMesh * +* * +* Consiglio Nazionale delle Ricerche * +* Istituto di Matematica Applicata e Tecnologie Informatiche * +* Sezione di Genova * +* IMATI-GE / CNR * +* * +* Authors: Marco Attene * +* Copyright(C) 2012: IMATI-GE / CNR * +* All rights reserved. * +* * +* This program is dual-licensed as follows: * +* * +* (1) You may use TMesh as 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 3 of the License, or * +* (at your option) any later version. * +* In this case the 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 (http://www.gnu.org/licenses/gpl.txt) * +* for more details. * +* * +* (2) You may use TMesh as part of a commercial software. In this case a * +* proper agreement must be reached with the Authors and with IMATI-GE/CNR * +* based on a proper licensing contract. * +* * +****************************************************************************/ + +#ifndef MATRIX_H +#define MATRIX_H + +#include +#include +#include "coordinates.h" +#include "list.h" + +namespace T_MESH +{ + +////////////////////////////////////////////////////////////////////////// +// +// Generic 3x3 matrix +// +////////////////////////////////////////////////////////////////////////// + +//! Generic 3x3 matrix. + +//! Elements are stored in a row-dominant order, thus +//! for example, M[4] is the first element of the second row. + +class Matrix3x3 +{ + public: + double M[9]; //!< Actual values of the matrix + + Matrix3x3() {M[0]=M[1]=M[2]=M[3]=M[4]=M[5]=M[6]=M[7]=M[8]=0.0;} //!< Contructs a null matrix + + //! Constructs a fully initialized matrix. + Matrix3x3(const double& a11, const double& a12, const double& a13, + const double& a21, const double& a22, const double& a23, + const double& a31, const double& a32, const double& a33); + + //! Constructs a 3x3 matrix as the product of Transpose(v1,v2,v3) and (w1,w2,w3). + Matrix3x3(const double& v1, const double& v2, const double& v3, + const double& w1, const double& w2, const double& w3); + + //! Constructs a 3x3 matrix as the product of Transpose(a,b,c) and (a,b,c). + Matrix3x3(const double& a, const double& b, const double& c); + + //! Returns TRUE if the matrix is symmetric + bool isSymmetric() const {return (M[2]==M[4] && M[3]==M[7] && M[6]==M[8]);} + + + //! Initializes all elements to 'd' + void operator=(const double& d) {M[0]=M[1]=M[2]=M[3]=M[4]=M[5]=M[6]=M[7]=M[8]=d;} + + void operator+=(const Matrix3x3&); //!< Sum another matrix + void operator-=(const Matrix3x3&); //!< Subtract another matrix + void operator*=(const double&); //!< Multiply by a scalar + void operator/=(const double& d) {operator *=(1.0/d);} //!< Divide by a scalar + Matrix3x3 operator+(const Matrix3x3&) const; //!< Returns the sum of this and another matrix + Matrix3x3 operator*(const double&) const; //!< Returns the product of this matrix with a scalar + Matrix3x3 operator*(const Matrix3x3&) const; //!< Returns the product of this and another matrix (rows by columns) + Matrix3x3 operator~() const; //!< Returns the transpose of this matrix + + //! Return the matrix transpose + Matrix3x3 transpose() const; + + //! Returns Transpose(a,b,c)*M*(a,b,c) + + //! Returns the (scalar) result of multiplying the matrix on + //! the left and on the right by the vector (a,b,c). + double lrMultiply(const double& a, const double& b, const double& c) const; + + //! Returns the (scalar) result of v*M*w + double lrMultiply(const double& v1, const double& v2, const double& v3, + const double& w1, const double& w2, const double& w3) const; +}; + + +////////////////////////////////////////////////////////////////////////// +// +// Symmetric 3x3 matrix +// +////////////////////////////////////////////////////////////////////////// + +//! Symmetric 3x3 matrix + +//! Compact storage: \n +//! M[0] M[1] M[3] \n +//! M[1] M[2] M[4] \n +//! M[3] M[4] M[5] \n + +class SymMatrix3x3 +{ + public: + double M[6]; //!< Actual values of the matrix + + SymMatrix3x3() {M[0]=M[1]=M[2]=M[3]=M[4]=M[5]=0.0;} //!< Constructs a null matrix. + + //! Constructs a fully initialized matrix. + SymMatrix3x3(const double&a11, const double&a12, const double&a22, + const double&a13, const double&a23, const double&a33); + + //! Constructs a symmetric matrix as the product of Transpose(a,b,c) and (a,b,c). + SymMatrix3x3(const double& a, const double& b, const double& c); + + //! Constructs a symmetric 3x3 matrix as a copy of an existing 3x3 matrix S. + + //! If 'S' is not symmetric, its upper triangular part is reflected to the lower. + + SymMatrix3x3(const Matrix3x3& S); + + bool operator==(const SymMatrix3x3& s) //!< True iff all entries are equal + {return (M[0]==s.M[0] && M[1]==s.M[1] && M[2]==s.M[2] && M[3]==s.M[3] && M[4]==s.M[4] && M[5]==s.M[5]);} + bool operator!=(const SymMatrix3x3& s) //!< True iff at least one different entry + {return (M[0]!=s.M[0] || M[1]!=s.M[1] || M[2]!=s.M[2] || M[3]!=s.M[3] || M[4]!=s.M[4] || M[5]!=s.M[5]);} + + void operator+=(const SymMatrix3x3&); //!< Sum another matrix + void operator-=(const SymMatrix3x3&); //!< Subtract another matrix + void operator*=(const double&); //!< Multiply by a scalar + void operator/=(const double& d) {operator *=(1.0/d);} //!< Divide by a scalar + SymMatrix3x3 operator+(const SymMatrix3x3&) const; //!< Returns the sum of this and another matrix + SymMatrix3x3 operator*(const double&) const; //!< Returns the product of this matrix with a scalar + + //! Initializes all elements to 'd' + void operator=(const double& d) {M[0]=M[1]=M[2]=M[3]=M[4]=M[5]=d;} + + //! Returns the determinant + double determinant() const {return (M[0]*M[2]*M[5])+(2.0*M[1]*M[3]*M[4])-(M[0]*M[4]*M[4])-(M[2]*M[3]*M[3])-(M[5]*M[1]*M[1]);} + + //! Returns TRUE iff the matrix is made of all zeroes. + bool isNull() const {return (M[0]==0 && M[1]==0 && M[2]==0 && M[3]==0 && M[4]==0 && M[5]==0);} + + //! Returns Transpose(a,b,c)*M*(a,b,c) + + //! Returns the (scalar) result of multiplying the matrix on + //! the left and on the right by the vector (a,b,c). + double lrMultiply(const double& a, const double& b, const double& c) const; + + + //! Returns the (scalar) result of v*M*w + double lrMultiply(const double& v1, const double& v2, const double& v3, + const double& w1, const double& w2, const double& w3) const; + + bool invert(); //!< Inverts this matrix. Returns FALSE if not invertible, TRUE otherwise + + //! Returns the matrix trace + double trace() const {return M[0]+M[2]+M[5];} + + //! Compute eigenvalues and eigenvectors of the matrix (Jacobi method). + + //! The calling function is responsible of verifying that the matrix + //! is diagonalizable. Also, eigen_vals and eigen_vecs must be allocated + //! prior to calling this method.\n + //! eigen_vals are sorted in ascending order (i.e., eigen_vals[0] is the smallest one).\n + //! eigen_vecs are sorted accordingly to the order of eigen_vals, that is, + //! (eigen_vecs[0], eigen_vecs[1], eigen_vecs[2]) is the eigenvector corresponding to + //! eigen_vals[0]. + void diagonalize(double eigen_vals[3], double eigen_vecs[9]) const; + + //! Compute the eigenvalues l1, l2 and l3. + + //! This method is much faster and precise than 'diagonalize', as it uses + //! an analytical direct method instead of an iterative approach. + void getEigenvalues(double *l1, double *l2, double *l3) const; + + //! Compute the eigenvector (a,b,c) corresponding to the minimum eigenvalue. + + //! This method is much faster and precise than 'diagonalize', as it uses + //! an analytical direct method instead of an iterative approach. + //! Returns the corresponding eigenvalue + double getMinEigenvector(double *a, double *b, double *c) const; + + //! This method is much faster and precise than 'diagonalize', as it uses + //! an analytical direct method instead of an iterative approach. + void getMaxEigenvector(double *a, double *b, double *c) const; + + //! Prints the contents of the matrix to the specified FILE id. + + //! If no 'id' is specifyed, results are printed to stdout. + + void print(FILE *id =stdout) const; +}; + + +////////////////////////////////////////////////////////////////////////// +// +// Symmetric 4x4 matrix +// +////////////////////////////////////////////////////////////////////////// + +//! Symmetric 4x4 matrix. + +//! Compact storage: \n +//! a2 ab ac ad \n +//! ab b2 bc bd \n +//! ac bc c2 cd \n +//! ad bd cd d2 \n + +class SymMatrix4x4 +{ + public: + coord a2,ab,ac,ad,b2,bc,bd,c2,cd,d2; //!< Actual matrix coeffs. + + SymMatrix4x4() {a2=ab=ac=ad=b2=bc=bd=c2=cd=d2=0;} //!< Constructs a null matrix + + //! Extend a 3x3 symmetric matrix to homogeneous coordinates (ad=bd=cd=0 and d2=1). + SymMatrix4x4(const SymMatrix3x3&); + + //! Quadric (a,b,c,d)*Transpose(a,b,c,d) + SymMatrix4x4(const coord& a, const coord& b, const coord& c, const coord& d); + + bool operator==(const SymMatrix4x4&); //!< True iff equal + bool operator!=(const SymMatrix4x4&); //!< True iff not equal + + void operator+=(const SymMatrix4x4&); //!< Sum another matrix + SymMatrix4x4 operator+(const SymMatrix4x4&) const; //!< Returns the sum of this and another matrix + SymMatrix4x4 operator*(const coord&) const; //!< Returns the product of this matrix by a scalar + + //! Adds the quadric (a,b,c,d)*Transpose(a,b,c,d) + void add(const coord& a, const coord& b, const coord& c, const coord& d); + + //! Returns Transpose(a,b,c,d)*M*(a,b,c,d) + + //! Returns the (scalar) result of multiplying the matrix on + //! the left and on the right by the vector (a,b,c,d). + coord lrMultiply(const coord& a, const coord& b, const coord& c, const coord& d) const; + + //! \brief Computes the vector (a,b,c) that minimizes the quantity lrMultiply(a,b,c,1). + //! Returns FALSE if such a vector is not unique. + bool getMinimizer(coord *a, coord *b, coord *c) const; + + bool invert(); //!< Inverts this matrix. Returns FALSE if not invertible, TRUE otherwise +}; + + +////////////////////////////////////////////////////////////////////////// +// +// Generic 4x4 matrix +// +////////////////////////////////////////////////////////////////////////// + +//! Generic 4x4 matrix. + +class Matrix4x4 +{ + public: + double matrix[4][4]; //!< Actual matrix coefficients + + //! Constructs an undefined matrix + Matrix4x4(); + + //! Constructs a diagonal matrix with 'd' values on the diagonal + Matrix4x4(const double& d); + + //! Constructs a fully initialized matrix (parameters are in row dominant order M[0][0], M[0][1], ...). + Matrix4x4( + const double&, const double&, const double&, const double&, + const double&, const double&, const double&, const double&, + const double&, const double&, const double&, const double&, + const double&, const double&, const double&, const double& + ); + + //! Rotation matrix from a quaternion + void setRotation(const double &, const double&, const double&, const double&); + + //! Translation matrix from a vector + void setTranslation(const double &, const double&, const double&); + + Matrix4x4 operator*(const Matrix4x4&) const; //!< Returns the product of this and another matrix (rows by columns) + + void transform(double *, double *, double *); //! Transform the vector by left-multiplication with the matrix +}; + +} //namespace T_MESH + +#endif // MATRIX_H + diff --git a/src/mesh_fix/include/Kernel/point.h b/src/mesh_fix/include/Kernel/point.h new file mode 100644 index 000000000..049b6b72d --- /dev/null +++ b/src/mesh_fix/include/Kernel/point.h @@ -0,0 +1,299 @@ +/**************************************************************************** +* TMesh * +* * +* Consiglio Nazionale delle Ricerche * +* Istituto di Matematica Applicata e Tecnologie Informatiche * +* Sezione di Genova * +* IMATI-GE / CNR * +* * +* Authors: Marco Attene * +* Copyright(C) 2012: IMATI-GE / CNR * +* All rights reserved. * +* * +* This program is dual-licensed as follows: * +* * +* (1) You may use TMesh as 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 3 of the License, or * +* (at your option) any later version. * +* In this case the 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 (http://www.gnu.org/licenses/gpl.txt) * +* for more details. * +* * +* (2) You may use TMesh as part of a commercial software. In this case a * +* proper agreement must be reached with the Authors and with IMATI-GE/CNR * +* based on a proper licensing contract. * +* * +****************************************************************************/ + +#ifndef _POINT_H +#define _POINT_H + +#include "basics.h" + +namespace T_MESH +{ + +//! Orientation predicates using filtering on doubles +extern "C" double orient2d(double *, double *, double *); +extern "C" double orient3d(double *, double *, double *, double *); + +//! Orientation predicates on PM_Rationals + +// orient2D: >0 =0 <0 if (p,q,r) are CCW, aligned, CW respectively +PM_Rational orient2D(const PM_Rational& px, const PM_Rational& py, const PM_Rational& qx, const PM_Rational& qy, const PM_Rational& rx, const PM_Rational& ry); + + +//! Geometric point definition + +//! This class represents a point in the Euclidean 3D space. It can be used +//! to represent 3D vectors originating at (0,0,0) and terminating at the +//! corresponding point. Several methods of this class are intended to +//! manipulate vectors rather than points; for example, a call of the +//! method normalize is an actual normalization if the object is a vector, +//! but it has to be intended as a projection on the unit sphere if the +//! object is intended to be a point. An object of type Point is a triplet +//! (x,y,z) of coordinates endowed with a pointer 'info' to possible additional +//! information. Each coordinate is a number of type 'coord' which, by +//! default, is a standard double. Operations on points include addition, +//! subtraction, cross and dot product, and many others. This class implements +//! several useful operations using vector arithmethic. For example, +//! the simple piece of code "A = B*C;" assignes to A the value of the dot +//! product of B and C. +//! Nearly zero or nearly flat angles are automatically snapped to +//! exactly zero and exactly flat angles if the difference is smaller +//! than the global variable _acos_tolerance. This is the very basic application +//! of our version of the epsilon geometry for robust computation. + + +class Point : public Data +{ + public : + coord x,y,z; //!< Coordinates + void *info; //!< Further information + + //! Creates a new point with coordinates (0,0,0). + Point() {x = y = z = 0; info = NULL;} + + //! Creates a new point with the same coordinates as 's'. The info field is not copied. + Point(const Point *s) {x = s->x; y = s->y; z = s->z; info = NULL;} + + //! Creates a new point with the same coordinates as 's'. The info field is not copied. + Point(const Point& s) {x = s.x; y = s.y; z = s.z; info = NULL;} + + //! Creates a new point with coordinates (a,b,c). + Point(const coord& a, const coord& b, const coord& c) {x = a; y = b; z = c; info = NULL;} + + //! Do not remove this. It makes the compiler produce a vtable for this object. + TMESH_VIRTUAL bool isPoint() const { return true; } + + //! Set the coordinates to (a,b,c). + void setValue(const coord& a, const coord& b, const coord& c) {x = a; y = b; z = c;} + + //! Set the coordinates as those of 'p' + void setValue(const Point& p) {x = p.x; y = p.y; z = p.z;} + + //! Set the coordinates as those of '*p' + void setValue(const Point *p) {x = p->x; y = p->y; z = p->z;} + + //! Returns the vector difference + Point operator-(const Point& p) const {return Point(x-p.x, y-p.y, z-p.z);} + + //! Returns the vector sum + Point operator+(const Point& p) const {return Point(x+p.x, y+p.y, z+p.z);} + + //! Sums another point + void operator+=(const Point& p) {x+=p.x; y+=p.y; z+=p.z;} + + //! Subtracts another point + void operator-=(const Point& p) {x-=p.x; y-=p.y; z-=p.z;} + + //! Returns the Cross Product + Point operator&(const Point& p) const {return Point(y*p.z-z*p.y, z*p.x-x*p.z, x*p.y-y*p.x);} + + //! Returns the Dot Product + coord operator*(const Point& p) const {return (x*p.x+y*p.y+z*p.z);} + + //! Returns the product with a scalar + Point operator*(const coord& d) const {return Point(x*d,y*d,z*d);} + + //! Multiplies by a scalar + void operator*=(const coord& m) { x *= m; y *= m; z *= m; } + + //! Divides by a scalar + void operator/=(const coord& m) { x /= m; y /= m; z /= m; } + + //! Returns the vector divided by the scalar + Point operator/(const coord& d) const { return Point(x / d, y / d, z / d); } + + //! TRUE iff coordinates are equal + bool operator==(const Point& p) const {return (x==p.x && y==p.y && z==p.z);} + + //! FALSE iff coordinates are equal + bool operator!=(const Point& p) const {return (x!=p.x || y!=p.y || z!=p.z);} + + //! TRUE iff this is lexycographically smaller than s + bool operator<(const Point& s) const; + + //! Returns the i'th coordinate + inline coord& at(unsigned char i) { return (i == 0) ? (x) : ((i == 1) ? (y) : (z)); } + + //! Returns the i'th coordinate + inline coord& operator[](unsigned char i) { return (i == 0) ? (x) : ((i == 1) ? (y) : (z)); } + + //! Returns the inverse vector + Point inverse() const {return Point(-x,-y,-z);} + + //! Inverts the vector + void invert() {x=-x; y=-y; z=-z;} + + //! TRUE if vector is (0,0,0) + bool isNull() const {return (x==0 && y==0 && z==0);} + + //! Squared distance from origin + coord squaredLength() const {return (x*x + y*y + z*z);} + + //! Squared distance from '*b' + coord squaredDistance(const Point *b) const { return (((*(this)) - (*b)).squaredLength()); } + + //! Returns the solution of the linear system Ax = d, where A is a 3x3 matrix whose rows are row1, row2 and row3, d = this + Point linearSystem(const Point& row1, const Point& row2, const Point& row3); + + + //! Projects the vector on the plane with normal 'n' passing through the origin. + void project(const Point *n); + + //! Returns the projection of the point on the straight line though 'a' and 'b'. + Point projection(const Point *a, const Point *b) const; + + //! Prints the coordinates of the point to a file handler. stdout is the default. + void printPoint(FILE *fp = stdout) const { fprintf(fp, "%f %f %f,\n", TMESH_TO_FLOAT(x), TMESH_TO_FLOAT(y), TMESH_TO_FLOAT(z)); } // Debug + + + //! Exact orientation test. + //! Return value is positive iff the tetrahedron (this,a,b,c) has a positive volume; + //! It is negative iff the tetrahedron (this,a,b,c) has a negative volume; + //! It is zero iff the tetrahedron (this,a,b,c) has a zero volume. + coord exactOrientation(const Point *a, const Point *b, const Point *c) const; + coord side3D(const Point *p1, const Point *p2, const Point *p3) const { return exactOrientation(p1, p2, p3); } + + + //! Exact misalignment test. Returns TRUE iff points are not aligned. + bool exactMisalignment(const Point *a, const Point *b) const; + bool notAligned(const Point *a, const Point *b) const { return exactMisalignment(a,b); } + + //! Exact planar side test. Returns TRUE iff 'this', Q, A and B are coplanar + //! and 'this' and Q are (properly) on the same side of A-B. + //! Warning! Coplanarity is not checked, result is undetermined if + //! 'this', Q, A and B are not coplanar. + bool exactSameSideOnPlane(const Point *Q, const Point *A, const Point *B) const; + + //! Itersection point between lines p-q and r-s. Return INFINITE_POINT if lineas are either parallel or degenerate. + static Point lineLineIntersection(const Point& p, const Point& q, const Point& r, const Point& s); + + //! Itersection point between line p-q and plane r-s-t. Return INFINITE_POINT for parallel/degenerate args. + static Point linePlaneIntersection(const Point& p, const Point& q, const Point& r, const Point& s, const Point& t); + + //! Squared area of the triangle p-q-r. + static coord squaredTriangleArea3D(const Point& p, const Point& q, const Point& r); + + //! true if 'p' is a point of the segment v1-v2 (endpoints excluded) + static bool pointInInnerSegment(const Point *p, const Point *v1, const Point *v2); + + //! true if 'p' is a point of the segment v1-v2 (endpoints included) + static bool pointInSegment(const Point *p, const Point *v1, const Point *v2); + + //! true if the coplanar point 'p' is in the inner area of v1-v2-v3. + //! Undetermined if points are not coplanar. + static bool pointInInnerTriangle(const Point *p, const Point *v1, const Point *v2, const Point *v3); + + //! true if the coplanar point 'p' is either in the inner area of v1-v2-v3 or on its border. + //! Undetermined if points are not coplanar. + static bool pointInTriangle(const Point *p, const Point *v1, const Point *v2, const Point *v3); + + //! true if (p1-p2) properly intersects (sp1-sp2) at any point (endpoints included). + //! Collinear overlapping segments are not considered to be properly intersecting. + static bool segmentsIntersect(const Point *p1, const Point *p2, const Point *sp1, const Point *sp2); + + //! true if the interior of (p1-p2) properly intersects the interior of (sp1-sp2). + //! Collinear overlapping segments are not considered to be properly intersecting. + static bool innerSegmentsCross(const Point& p1, const Point& p2, const Point& sp1, const Point& sp2); + + //! true if segment (s1-s2) intersects the triangle v1-v2-v3 (border included). + static bool segmentIntersectsTriangle(const Point *s1, const Point *s2, const Point *v1, const Point *v2, const Point *v3); + + //! true if segment (s1-s2) intersects the triangle v1-v2-v3 (border included). + //! Accelerated version - relative orientations are passed as parameters. + static bool segmentIntersectsTriangle(const Point *s1, const Point *s2, const Point *v1, const Point *v2, const Point *v3, const coord& o1, const coord& o2); + + + + + // FUNCTIONS BELOW THIS LINE MAY RETURN APPROXIMATED/NOT ROBUST RESULTS EVEN WHEN USING RATIONALS + + + + //! Distance from origin + double length() const { return sqrt(TMESH_TO_DOUBLE((x*x + y*y + z*z))); } + + //! Divides the vector by its length. If isNull() the application exits with an error. + void normalize(); + + //! Rotates the vector around 'axis' by 'ang' radians ccw. + void rotate(const Point& axis, const double& ang); + + //! Distance from 'b' + double distance(const Point& b) const {return (((*(this))-(b)).length());} + + //! Distance from '*b' + double distance(const Point *b) const { return (((*(this)) - (*b)).length()); } + + //! Distance from straight line through 'a' and 'b' + double distanceFromLine(const Point *a, const Point *b) const; + + //! Distance from straight line through 'a' and 'b'. *cc is set to the closest line point. + double distanceFromLine(const Point *a, const Point *b, Point *cc) const; + + double distanceFromEdge(const Point *a, const Point *b) const; //!< Distance from segment a-b + + //! Distance from segment a-b. *cc is set to the closest edge point. + double distanceFromEdge(const Point *a, const Point *b, Point *cc) const; + + //! Distance between the straight lines through (this) - l1_p2 and l2_p1 - l2_p2. + double distanceLineLine(const Point *l1_p2, const Point *l2_p1, const Point *l2_p2) const; + + //!< Angle between this vector and 'v' in radians. + double getAngle(const Point& v) const; + + //! Angle defined by in radians. + double getAngle(const Point& a, const Point& b) const { return (a - (*this)).getAngle(b - (*this)); } + + //! Angle defined by <*a, *this, *b> in radians. + double getAngle(const Point *a, const Point *b) const { return ((*a) - (*this)).getAngle((*b) - (*this)); } + + + //! Line-line closest point computation. + //! I SUSPECT THIS CAN BE MADE EXACT... + + //! Computes the closest points of the line passing through this and this2, + //! and the line passing through p1 and p2. The computed points are used to + //! initialize the coordinates of cpOnThis and cpOnOther. The method + //! returns 0 if the lines are parallel, 1 otherwise. + int closestPoints(const Point *this2, const Point *p1, const Point *p2, Point *cpOnThis, Point *cpOnOther) const; +}; + +//! Lexycographic comparison to be used with jqsort() or abstractHeap. +int xyzCompare(const Data *p1, const Data *p2); + +//! Static point with DBL_MAX coordinates. +extern const Point INFINITE_POINT; + +//! Checks whether a point is INFINITE_POINT. +#define IS_FINITE_POINT(p) ((p).x < DBL_MAX) + +} //namespace T_MESH + +#endif // _POINT_H + diff --git a/src/mesh_fix/include/Kernel/tmesh_kernel.h b/src/mesh_fix/include/Kernel/tmesh_kernel.h new file mode 100644 index 000000000..40ad5a552 --- /dev/null +++ b/src/mesh_fix/include/Kernel/tmesh_kernel.h @@ -0,0 +1,39 @@ +/**************************************************************************** +* TMesh * +* * +* Consiglio Nazionale delle Ricerche * +* Istituto di Matematica Applicata e Tecnologie Informatiche * +* Sezione di Genova * +* IMATI-GE / CNR * +* * +* Authors: Marco Attene * +* Copyright(C) 2012: IMATI-GE / CNR * +* All rights reserved. * +* * +* This program is dual-licensed as follows: * +* * +* (1) You may use TMesh as 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 3 of the License, or * +* (at your option) any later version. * +* In this case the 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 (http://www.gnu.org/licenses/gpl.txt) * +* for more details. * +* * +* (2) You may use TMesh as part of a commercial software. In this case a * +* proper agreement must be reached with the Authors and with IMATI-GE/CNR * +* based on a proper licensing contract. * +* * +****************************************************************************/ + +#ifndef _TMESH_KERNEL_H +#define _TMESH_KERNEL_H + +#include "basics.h" +#include "list.h" +#include "point.h" +#include "matrix.h" + +#endif //_TMESH_KERNEL_H diff --git a/src/mesh_fix/include/TMesh/detectIntersections.h b/src/mesh_fix/include/TMesh/detectIntersections.h new file mode 100644 index 000000000..9659890cf --- /dev/null +++ b/src/mesh_fix/include/TMesh/detectIntersections.h @@ -0,0 +1,59 @@ +/**************************************************************************** +* TMesh * +* * +* Consiglio Nazionale delle Ricerche * +* Istituto di Matematica Applicata e Tecnologie Informatiche * +* Sezione di Genova * +* IMATI-GE / CNR * +* * +* Authors: Marco Attene * +* Copyright(C) 2013: IMATI-GE / CNR * +* All rights reserved. * +* * +* This program is dual-licensed as follows: * +* * +* (1) You may use TMesh as 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 3 of the License, or * +* (at your option) any later version. * +* In this case the 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 (http://www.gnu.org/licenses/gpl.txt) * +* for more details. * +* * +* (2) You may use TMesh as part of a commercial software. In this case a * +* proper agreement must be reached with the Authors and with IMATI-GE/CNR * +* based on a proper licensing contract. * +* * +****************************************************************************/ + +#ifndef DETECT_INTERSECTIONS_H +#define DETECT_INTERSECTIONS_H + +#include "tin.h" + +namespace T_MESH +{ +#define DI_MAX_NUMBER_OF_CELLS 10000 +#define DI_EPSILON_POINT Point(1.0e-9, 1.0e-9, 1.0e-9) + +class di_cell : public Data +{ +public: + Point mp, Mp; + List triangles; + + di_cell() {} + di_cell(Basic_TMesh *tin, bool useAll = true); + + bool is_triangleBB_in_cell(Triangle *t) const; + + di_cell *fork(); + void selectIntersections(bool justproper = false); + bool doesNotIntersectForSure(); +}; + +} //namespace T_MESH + +#endif // DETECT_INTERSECTIONS_H diff --git a/src/mesh_fix/include/TMesh/edge.h b/src/mesh_fix/include/TMesh/edge.h new file mode 100644 index 000000000..dba8b6316 --- /dev/null +++ b/src/mesh_fix/include/TMesh/edge.h @@ -0,0 +1,252 @@ +/**************************************************************************** +* TMesh * +* * +* Consiglio Nazionale delle Ricerche * +* Istituto di Matematica Applicata e Tecnologie Informatiche * +* Sezione di Genova * +* IMATI-GE / CNR * +* * +* Authors: Marco Attene * +* Copyright(C) 2013: IMATI-GE / CNR * +* All rights reserved. * +* * +* This program is dual-licensed as follows: * +* * +* (1) You may use TMesh as 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 3 of the License, or * +* (at your option) any later version. * +* In this case the 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 (http://www.gnu.org/licenses/gpl.txt) * +* for more details. * +* * +* (2) You may use TMesh as part of a commercial software. In this case a * +* proper agreement must be reached with the Authors and with IMATI-GE/CNR * +* based on a proper licensing contract. * +* * +****************************************************************************/ + +#ifndef _EDGE_H +#define _EDGE_H + +#include "basics.h" +#include "vertex.h" + +namespace T_MESH +{ + +//! Edge of a Basic_TMesh. + +//! This class represents an edge of a triangulation. An Edge is the main +//! part of the Basic_TMesh data structure. Each edge has an orientation +//! (i.e. from v1 to v2) which forces the order in which incident triangles +//! (t1 and t2) are stored in the class. When looking the edge so that it +//! points "upwards", if the normal of t1 points towards the observer then +//! t1 must be on the left of the edge. The field mask is useful for +//! assigning up to 256 different states to the edge. + + +class Edge : public Data +{ + public : + + Vertex *v1,*v2; //!< End-points + class Triangle *t1,*t2; //!< Incident triangles + unsigned char mask; //!< bit-mask for marking purposes + void *info; //!< Further information + + + Edge(); //!< AMF_ADD 1.1-2 > + Edge(Vertex *s, Vertex *d); //!< Constructor + ~Edge(); //!< Destructor + + //! Returns true only if object is a basic Edge. All the reimplementations must return false. + TMESH_VIRTUAL bool isBaseType() const { return true; } + + //! TRUE iff edge is properly linked to a Basic_TMesh. + bool isLinked() const {return (v1 != NULL);} + + //! TRUE iff 'v' is an end-point of the edge. + bool hasVertex(const Vertex *v) const {return (v1==v || v2==v);} + + //! TRUE iff 't' is incident to the edge. + bool hasTriangle(const Triangle *t) const {return (t1==t || t2==t);} + + //! TRUE if both 'va' and 'vb' are vertices of the edge. + bool hasVertices(const Vertex *va, const Vertex *vb) const {return ((v1==va && v2==vb) || (v2==va && v1==vb));} + + //! Squared length of the edge. + coord squaredLength() const {return v1->squaredDistance(v2);} + + //! Convert to vector v2-v1. + Point toVector() const {return (*v2)-(*v1);} + + //! Return the edge's mid-point. + Point getMidPoint() const {return ((*v1)+(*v2))/2.0;} + + //! Invert the edge's orientation. + TMESH_VIRTUAL void invert() {p_swap((void **)(&v1), (void **)(&v2)); p_swap((void **)(&t1), (void **)(&t2));} //!< AMF_CHANGE 1.1-2> + + //! Triangle on the left of the edge when looking from 'v'. NULL if 'v' is not a vertex of the edge. + Triangle *leftTriangle(const Vertex *v) const {return ((v1 == v)?(t1):((v2 == v)?(t2):(NULL)));} + + //! Triangle on the right of the edge when looking from 'v'. NULL if 'v' is not a vertex of the edge. + Triangle *rightTriangle(const Vertex *v) const {return ((v1 == v)?(t2):((v2 == v)?(t1):(NULL)));} + + //! Vertex opposite to 'v'. NULL if 'v' is not a vertex of the edge. + Vertex *oppositeVertex(const Vertex *v) const {return ((v1 == v)?(v2):((v2 == v)?(v1):(NULL)));} + + //! Incident triangle opposite to 't'. NULL if 't' is not incident to the edge. + Triangle *oppositeTriangle(const Triangle *t) const {return ((t1 == t)?(t2):((t2 == t)?(t1):(NULL)));} + + //! Replace vertex 'a' with vertex 'b' in the edge and return TRUE. If 'a' is not a vertex of the edge return FALSE. + bool replaceVertex(const Vertex *a, Vertex *b) {if (v1==a) v1=b; else if (v2==a) v2=b; else return 0; return 1;} + + //! Replace incident triangle 'a' with 'b' and return TRUE. If 'a' is not incident to the edge return FALSE. + bool replaceTriangle(const Triangle *a, Triangle *b) {if (t1==a) t1=b; else if (t2==a) t2=b; else return 0; return 1;} + + //! Vertex shared with edge 'b'. NULL if this and 'b' do not share any vertex. + Vertex *commonVertex(const Edge *b) const {return ((v1 == b->v1 || v1 == b->v2)?(v1):((v2 == b->v1 || v2 == b->v2)?(v2):(NULL)));} + + //! TRUE iff edge is on the boundary (i.e., one of the two incident triangles is NULL). + bool isOnBoundary() const {return (t1 == NULL || t2 == NULL);} + + //! TRUE iff edge is isolated (i.e., both the two incident triangles are NULL). + bool isIsolated() const {return (t1 == NULL && t2 == NULL);} + + //! TRUE iff the two endpoints coincide exactly + bool isDegenerate() const {return ((*v1)==(*v2));} + + //! If the edge is on boundary return its only incident triangle. NULL otherwise. + Triangle *getBoundaryTriangle() const {return (t2 == NULL)?(t1):((t1 == NULL)?(t2):(NULL));} + + //! Print the coordinates of the end-ponts to the file handler pointed to by 'f' (stdout by default). + void printEdge(FILE *f =stdout) const {v1->printPoint(f); v2->printPoint(f);} + + //! Combinatorial edge-swap. + + //! Vertices of the edge are replaced with vertices of the two incident triangles which are opposite to this edge. + //! Connectivity information is updated properly. + //! If the edge is on boundary or if the edge after the swap already exists return FALSE and do not change anything. + //! Return TRUE on success. + //! If 'fast' is set, no topological check is performed. + TMESH_VIRTUAL bool swap(const bool fast=0); + + //! Edge collapse. + + //! This method collapses the edge and updates the connectivity of the + //! neighboring elements consistently. The edge will be contracted into its + //! first vertex v1 (or v2, for collapseOnV2()). + //! This method returns v1 (or v2) on success, NULL otherwise. + //! Failure occurs when the collapse would produce an invalid connectivity graph. + //! Caution! If the collapse succeeds the + //! edge, its incident triangles and the second vertex are unlinked, but + //! they are still present in the lists of the Basic_TMesh. + //! The calling function is responsible of removing them from the lists using + //! the method removeUnlinkedElements(). + Vertex *collapseOnV1(); + Vertex *collapseOnV2(); + + //! This method collapses the edge and updates the connectivity of the + //! neighboring elements consistently. The edge will be transformed into a + //! vertex with the coordinates of 'p'. + //! This method returns TRUE on success, FALSE otherwise. + //! Failure occurs when the collapse would produce an invalid connectivity graph. + //! Caution! If the collapse succeeds the + //! edge, its incident triangles and the second vertex are unlinked, but + //! they are still present in the lists of the Basic_TMesh. + //! The calling function is responsible of removing them from the lists using + //! the method removeUnlinkedElements(). + bool collapse(const Point& p); + + //! Edge collapse. + + //! This method collapses the edge and updates the connectivity of the + //! neighboring elements consistently. The edge will be transformed into a + //! vertex placed at the edge's mid-point. + //! This method returns TRUE on success, FALSE otherwise. + //! Failure occurs when the collapse would produce an invalid connectivity graph. + //! Caution! If the collapse succeeds the + //! edge, its incident triangles and the second vertex are unlinked, but + //! they are still present in the lists of the Basic_TMesh. + //! The calling function is responsible of removing them from the lists using + //! the method removeUnlinkedElements(). + bool collapse(); + + //! Merge with another boundary edge. + + //! If both this and 'e' are boundary edges, the edge 'e' is identified with + //! this one, and the connectivity of the neighboring elements is updated consistently. + //! This method returns TRUE on success, FALSE otherwise. + //! Failure occurs when the merge would produce an invalid connectivity graph (i.e., non orientable). + //! Caution! If the merge succeeds the edge 'e' and its two end-points + //! are unlinked, but they are still present in the lists of the + //! Basic_TMesh. It's responsibility of the calling function to remove + //! them from the lists using the method removeUnlinkedElements(). + bool merge(Edge *e); + + //! Stitching primitive. + + //! If there is a copy of this edge incident to one of the end-points, + //! identify it with this edge, and update the connectivity properly. + //! This method returns TRUE on success, FALSE otherwise. + //! Caution! If the stitch succeeds, the duplicated edge + //! is unlinked, but it is still present in the lists of the + //! Basic_TMesh. It's responsibility of the calling function to remove + //! it from the lists using the method removeEdges(). + bool stitch(); + + //! Returns TRUE if edge is not on boundary and its two incident triangles overlap + bool overlaps() const; + + //! true if this edge itersects 't' other than on common subsimplexes + bool intersects(const Triangle *t) const; + + //! Returns a positive value if dihedral angle is less than flat (convex), 0 if flat, + //! negative if more than flat (concave). Returns DBL_MAX if edge is on boundary. + coord getConvexity() const; + + // FUNCTIONS BELOW THIS LINE MAY RETURN APPROXIMATED/NOT ROBUST RESULTS EVEN WHEN USING RATIONALS + + + + //! Euclidean length of the edge. + double length() const { return v1->distance(v2); } + + //! Convert to normalized vector (v2-v1)/|v2-v1|. + Point toUnitVector() const; + + //! Return the normal at the edge as the average of the normals of the two incident triangles. + //! A null (0,0,0) vector is returned if the edge is on boundary. + Point getNormal() const; + + //! Return the minimum among the six angles of the two incident triangles.2PI if on boundary. + double delaunayMinAngle() const; + + //! Dihedral angle at the edge. + double dihedralAngle() const; + + //! Angle between the normals of the two incident triangles. + + //! Angle between the normals of the two incident triangles. If + //! the edge is on boundary or one or both the incident triangles are + //! degenerate, return -1. + double curvature() const; +}; + +//! Edge comparison based on length to be used with jqsort() or abstractHeap. +int edgeCompare(const Data *a, const Data *b); + +//! Lexycographic edge comparison to be used with jqsort() or abstractHeap. +int lexEdgeCompare(const Data *a, const Data *b); + +//! Vertex-based edge comparison for qsort. +//! Duplicated edges are contiguous in this sorting. +int vtxEdgeCompare(const Data *a, const Data *b); + +} //namespace T_MESH + +#endif //_EDGE_H + diff --git a/src/mesh_fix/include/TMesh/marchIntersections.h b/src/mesh_fix/include/TMesh/marchIntersections.h new file mode 100644 index 000000000..b1c822145 --- /dev/null +++ b/src/mesh_fix/include/TMesh/marchIntersections.h @@ -0,0 +1,133 @@ +/**************************************************************************** +* TMesh * +* * +* Consiglio Nazionale delle Ricerche * +* Istituto di Matematica Applicata e Tecnologie Informatiche * +* Sezione di Genova * +* IMATI-GE / CNR * +* * +* Authors: Marco Attene * +* Copyright(C) 2013: IMATI-GE / CNR * +* All rights reserved. * +* * +* This program is dual-licensed as follows: * +* * +* (1) You may use TMesh as 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 3 of the License, or * +* (at your option) any later version. * +* In this case the 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 (http://www.gnu.org/licenses/gpl.txt) * +* for more details. * +* * +* (2) You may use TMesh as part of a commercial software. In this case a * +* proper agreement must be reached with the Authors and with IMATI-GE/CNR * +* based on a proper licensing contract. * +* * +****************************************************************************/ + +#ifndef MARCHING_INTS_H +#define MARCHING_INTS_H + +#include "tmesh.h" + +namespace T_MESH +{ + +/////////////////////////////////////////////////////////////// +// +// A ray-mesh intersection. +// +/////////////////////////////////////////////////////////////// + +class mc_ints : public Data +{ + public: + + coord ic; // Distance from ray source + unsigned char sg; // 1 if ray enters mesh, 0 if it exits + ExtVertex *v; // Used to support polygonization + Triangle *source; // The triangle that generates this intersection + + mc_ints(coord a, unsigned char b, Triangle *s) { ic = a; sg = b; v = NULL; source = s; } + ~mc_ints() { if (v) delete(v); } + + static int compare(const Data *e1, const Data *e2); +}; + + +/////////////////////////////////////////////////////////////// +// +// A cubical cell to be polygonized +// +/////////////////////////////////////////////////////////////// + +class mc_cell : public Data +{ + public: + int x,y,z; // Coordinates (i.e. cell's position) + mc_ints *ints[12]; // Intersection at each voxel edge + + mc_cell(int a, int b, int c) {x=a; y=b; z=c;} + mc_cell(int a, int b, int c, mc_ints *m, int i) : x(a), y(b), z(c) + { + for (int n=0; n<12; n++) ints[n] = (n==i)?(m):(NULL); + } + + void polygonize(Basic_TMesh *tin); + static int compare(const void *e1, const void *e2); + + void merge(mc_cell *m); + +private: + unsigned char lookup() const; // The polygonization to be used + unsigned char lookdown(); // The other polygonization to be used + void purgeIntersections(); // TRUE if intersections are consistent in the cell +}; + + +/////////////////////////////////////////////////////////////// +// +// The marching intersections grid +// +/////////////////////////////////////////////////////////////// + +class mc_grid : public Data +{ + Point origin; // Origin for normalization + coord norm; // Normalization factor +protected: //! < AMF_CHANGE - since T_MESH 2.4-2 > + Basic_TMesh *tin; // Triangulation to remesh //! < AMF_CHANGE - since T_MESH 2.4-2 > + List *xy, *xz, *zy; // Axis-parallel rays //! < AMF_CHANGE - since T_MESH 2.4-2 > + int numrays; // Number of rays per axis //! < AMF_CHANGE - since T_MESH 2.4-2 > + +public: + mc_grid(Basic_TMesh *_tin, int n); + ~mc_grid() {delete [] xy; delete [] xz; delete [] zy;} + + TMESH_VIRTUAL mc_ints * newMcInts(coord a, unsigned char b, Triangle *s){ return new mc_ints(a,b,s); } //! < AMF_CHANGE - since T_MESH 2.4-2 > + + void remesh(bool simplify_result =false); + void simplify(); + + static bool segmentIntersectsTriangle(Point& ev1, Point& ev2, Triangle *t, Point& op); + +protected: //! < AMF_CHANGE - since T_MESH 2.4-2 > + TMESH_VIRTUAL void sample_triangle(Triangle *t); //! < AMF_CHANGE - since T_MESH 2.4-2 > + TMESH_VIRTUAL void createVertices(List *l, int i, int j, int k); //! < AMF_CHANGE - since T_MESH 2.4-2 > +private: + void sort(); + void purge(); + void createVertices(); + List *createCells(); + void trackOuterHull(); + + static inline coord oceil(const coord& d) { coord c; return ((c = ceil(d)) == d) ? (c + 1) : (c); } + void purgeList(List *l); +}; + +} //namespace T_MESH + +#endif // MARCHING_INTS_H diff --git a/src/mesh_fix/include/TMesh/tin.h b/src/mesh_fix/include/TMesh/tin.h new file mode 100644 index 000000000..2f55e3d02 --- /dev/null +++ b/src/mesh_fix/include/TMesh/tin.h @@ -0,0 +1,804 @@ +/**************************************************************************** +* TMesh * +* * +* Consiglio Nazionale delle Ricerche * +* Istituto di Matematica Applicata e Tecnologie Informatiche * +* Sezione di Genova * +* IMATI-GE / CNR * +* * +* Authors: Marco Attene * +* Copyright(C) 2013: IMATI-GE / CNR * +* All rights reserved. * +* * +* This program is dual-licensed as follows: * +* * +* (1) You may use TMesh as 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 3 of the License, or * +* (at your option) any later version. * +* In this case the 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 (http://www.gnu.org/licenses/gpl.txt) * +* for more details. * +* * +* (2) You may use TMesh as part of a commercial software. In this case a * +* proper agreement must be reached with the Authors and with IMATI-GE/CNR * +* based on a proper licensing contract. * +* * +****************************************************************************/ + +#ifndef _TIN_H +#define _TIN_H + +#include "tmesh.h" + +namespace T_MESH +{ + +//! Basic_TMesh + +//! This class represents a manifold and oriented triangle mesh. +//! Vertices, Edges and Triangles are stored in the Lists V, E and T +//! respectively. Methods boundaries(), handles() and shells() may +//! be used to retrieve the respective topological entities. +//! Navigation of the mesh is based on the topological relationships +//! stored in each Vertex, Edge and Triangle. +//! Some methods would require a global update only to maintain +//! consistent values of the protected fields n_boundaries, n_handles +//! and n_shells. On the other hand, the same methods would work in +//! constant time if these values would not need to be updated. +//! To keep complexity as low as possible, +//! we make use of the 'dirty bits' d_boundaries, d_handles and +//! d_shells to mark that the respective entities must be updated. +//! The access functions boundaries(), handles() and shells() +//! check the status of the respective dirty bit and do a global +//! update (i.e., eulerUpdate()) only if necessary. +//! The complexity of the methods is provided as a function of a +//! generic 'N', which is O(V.numels()) = O(E.numels()) = O(T.numels()). + +class Basic_TMesh +{ + protected: + + int n_boundaries; //!< Number of boundary loops + int n_handles; //!< Number of handles + int n_shells; //!< Number of connected components + + bool d_boundaries; //!< Dirty bit for n_boundaries + bool d_handles; //!< Dirty bit for n_handles + bool d_shells; //!< Dirty bit for n_shells + + public: + + List V; //!< Vertex set + List E; //!< Edge set + List T; //!< Triangle set + + void *info; //! Generic information attached to this mesh + + ///////////////////////////////////////////////////////////////////////////// + // + // Constructors/Destructor (Implemented in "MESH_STRUCTURE/io.cpp") + // + ///////////////////////////////////////////////////////////////////////////// + + //! Empty triangulation. Should be used only prior to a call to load(). + Basic_TMesh(); + + //! Pre-defined triangulation. Currently, only "triangle" and "tetrahedron" + //! are recognized. + Basic_TMesh(const char *); + void init(const char *); + + //! Clones an existing Trianglation. + Basic_TMesh(const Basic_TMesh *, const bool clone_info = false); + void init(const Basic_TMesh *, const bool clone_info = false); + + //! Clones an existing connected component. + + //! Creates a new Basic_TMesh out of a connected component of an existing + //! Basic_TMesh. 't' is a triangle of the connected component that must + //! be copied. If 'keep_ref' is TRUE, each element of the existing mesh + //! keeps a pointer to the corresponding new element in the 'info' field. + Basic_TMesh(const Triangle *t, const bool keep_ref = false); + void init(const Triangle *t, const bool keep_ref = false); + + //FIXED: + //! Destructor. Frees the memory allocated for all the mesh elements. + //! Warning! This method uses the freeNodes() method of the class List, + //! which is not guaranteed to work correctly on systems other than + //! Linux (see the documentation of List for details). + //! Assuming that the method works correctly, however, the calling + //! function is responsible of freeing the memory that was possibly + //! allocated for objects pointed to by the 'info' field of mesh + //! elements. Clearly, this must be done before calling the destructor. + ~Basic_TMesh(); + + //! Returns true only if object is a basic Basic_TMesh. All the reimplementations must return false. +// TMESH_VIRTUAL bool isBaseType() const { return true; } + + //! Get the number of boundary loops of the triangle mesh. O(1) or O(N). + int boundaries() { if (d_boundaries) eulerUpdate(); return n_boundaries; } + + //! Get the number of handles of the triangle mesh. O(1) or O(N). + int handles() { if (d_handles) eulerUpdate(); return n_handles; } + + //! Get the number of connected components of the triangle mesh. O(1) or O(N). + int shells() { if (d_shells) eulerUpdate(); return n_shells; } + + + ///////////////////////////////////////////////////////////////////////////// + // + // Input/Output methods (Implemented in "MESH_STRUCTURE/io.cpp") + // + ///////////////////////////////////////////////////////////////////////////// + + //! Initialize the triangle mesh from the file 'filename'. + + //! The file format is automatically deduced from the magic number + //! or the filename extension. If 'doupdate' is FALSE, the + //! global update for the topological entities is prevented. + //! Currently, the following file formats are supported: + //! Open Inventor (IV), VRML 1.0 and 2.0 (WRL), Object File Format (OFF), + //! IMATI Ver-Tri (VER, TRI), PLY, OBJ, STL. + //! A non-zero value is returned in case of error. Specifically, + //! IO_CANTOPEN means that the file couldn't be opened for reading. + //! IO_FORMAT means that the file format was not recognized by the loader. + //! IO_UNKNOWN represents all the other errors. + //! The calling function is responsible of verifying that the mesh is + //! empty before calling this method. + + int load(const char *filename, const bool update = 1); + int loadIV(const char *); //!< Loads IV + int loadVRML1(const char *); //!< Loads VRML 1.0 + int loadOFF(const char *); //!< Loads OFF + int loadEFF(const char *); //!< Loads EFF + int loadPLY(const char *); //!< Loads PLY + int loadVerTri(const char *); //!< Loads VER-TRI + int loadVRML2(const char *); //!< Loads VRML 2.0 + int loadOBJ(const char *); //!< Loads OBJ + int loadSTL(const char *); //!< Loads STL + + int cutAndStitch(); //!< Convert to manifold + Triangle * CreateIndexedTriangle(ExtVertex **, int, int, int); + TMESH_VIRTUAL Triangle * CreateTriangleFromVertices(ExtVertex *, ExtVertex *, ExtVertex *); + + //! This function approximates the vertex coordinates with the values + //! that can be represented in an ASCII file. + void coordBackApproximation(); + + protected: + void closeLoadingSession(FILE *, int, ExtVertex **, bool); + bool pinch(Edge *e, bool wcv); + //! If the 'e' is internal, creates a copy of the edge and associates e->t2 + //! to this copy. After this operation, 'e' and its copy are boundary edges + //! with the same vertices. If 'e' is already on boundary, nothing is done + //! and NULL is returned. Otherwise the newly created copy is returned. + TMESH_VIRTUAL Edge *duplicateEdge(Edge *e); + + public: + TMESH_VIRTUAL Vertex * newVertex(); + TMESH_VIRTUAL Vertex * newVertex(const coord &, const coord &, const coord &); + TMESH_VIRTUAL Vertex * newVertex(Point *); + TMESH_VIRTUAL Vertex * newVertex(Point &); + TMESH_VIRTUAL Vertex * newVertex(Vertex *); + TMESH_VIRTUAL Edge * newEdge(Vertex *, Vertex *); + TMESH_VIRTUAL Edge * newEdge(Edge *); + TMESH_VIRTUAL Triangle * newTriangle(); + TMESH_VIRTUAL Triangle * newTriangle(Edge *, Edge *, Edge *); + TMESH_VIRTUAL Basic_TMesh * newObject() const { return new Basic_TMesh(); } + TMESH_VIRTUAL Basic_TMesh * newObject(const Basic_TMesh *tm, const bool ci = false) const { return new Basic_TMesh(tm, ci); } + TMESH_VIRTUAL Basic_TMesh * newObject(const char *s) const { return new Basic_TMesh(s); } + + //! Save the triangle mesh to file 'filename'. + + //! The file format is deduced from the filename extension + //! (wrl = vrml 1.0), (iv = OpenInventor), (off = Object + //! file format), (ply = PLY format), (tri = IMATI Ver-Tri). + //! If 'back_approx' is set, vertex coordinates are approximated + //! to reflect the limited precision of floating point + //! representation in ASCII files. This should be used when + //! coherence is necessary between in-memory and saved data. + //! A non-zero return value is returned if errors occur. + + int save(const char *filename, bool back_approx = 0); + + int saveIV(const char *); //!< Saves IV + int saveOFF(const char *); //!< Saves OFF 1.0 + int saveEFF(const char *); //!< Saves EFF + int saveOBJ(const char *); //!< Saves OBJ + int saveSTL(const char *); //!< Saves STL + int savePLY(const char *, bool ascii = 1); //!< Saves PLY 1.0 (ascii or binary) + int saveVerTri(const char *); //!< Saves Ver-Tri + + //! Saves the triangle mesh to a VRML 1.0 file. + //! The value of 'mode' specifies whether to use additional + //! information attached to mesh elements in order to assign + //! them a proper color. + //! IO_CSAVE_OVERALL assigns a unique color for the entire mesh (default). + //! IO_CSAVE_PERFACE assigns a color to each triangle depending on the value + //! of its 'info' field. + //! IO_CSAVE_PERVERTEX assigns a color to each vertex depending on the value + //! of its 'info' field. + //! IO_CSAVE_PERFACE_INDEXED assigns one of five base colors to each triangle + //! depending on the value of its 'mask' field. + //! IO_CSAVE_PERVERTEX_INDEXED assigns one of five base colors to each vertex + //! depending on the value of its 'mask' field. + int saveVRML1(const char *, const int mode = 0); + + + //! Append another triangle mesh to the existing one. + + //! This method works exactly as the 'load()' method, except for the fact + //! that it does not assume that the mesh is empty. + int append(const char *filename, const bool doupdate = 1); + + + // Move all the elements of 't' to this mesh and delete 't' itself. + TMESH_VIRTUAL void moveMeshElements(Basic_TMesh *t, bool delInput = true); + + + ///////////////////////////////////////////////////////////////////////////// + // + // Primitive Construction (Implemented in "MESH_STRUCTURE/tin.cpp") + // + ///////////////////////////////////////////////////////////////////////////// + + //! Creates an Edge connecting two existing mesh vertices. + + //! Returns the newly created edge. If an edge connecting the two vertices + //! already exists in the mesh, then no new edge is created and the old one + //! is returned. + Edge *CreateEdge(Vertex *v1, Vertex *v2); + + + //! Creates an Edge connecting two existing mesh Extended vertices. + + //! Returns the newly created edge. If an edge connecting the two vertices + //! already exists in the mesh, then no new edge is created and the old one + //! is returned. + //! If 'check' is FALSE, the check for previously existing edges is skipped. + Edge *CreateEdge(ExtVertex *v1, ExtVertex *v2, const bool check = 1); + + + //! Creates a properly oriented Triangle bounded by three existing mesh edges. + + //! Returns the newly created Triangle. If e1, e2 and e3 + //! are not suitable for creating a properly oriented and + //! manifold triangle, the creation fails and NULL is returned. + TMESH_VIRTUAL Triangle * CreateTriangle(Edge *e1, Edge *e2, Edge *e3); + + + //! Creates an arbitrarily oriented Triangle bounded by three existing mesh edges. + + //! Returns the newly created Triangle. If either e1, e2 or e3 + //! has already two incident triangles, the creation fails and NULL is returned. + //! This method assumes that e1, e2 and e3 are incident to exactly three vertices. + TMESH_VIRTUAL Triangle * CreateUnorientedTriangle(Edge *, Edge *, Edge *); + + + //! Creates a newEdge 'e' and an oriented Triangle bounded by 'e', 'e1' and 'e2'. + + //! The newly created triangle is returned, unless 'e1' and 'e2' do not share a + //! vertex or they are not boundary edges. In this cases, NULL is returned. + TMESH_VIRTUAL Triangle *EulerEdgeTriangle(Edge *e1, Edge *e2); + + + //! Splits and edge at a given point and returns the newly created Vertex. + //! If the boolean parameter is set to true, the 'mask' fields of edges and + //! triangles are propagated to the new elements. + TMESH_VIRTUAL Vertex *splitEdge(Edge *, Point *, bool = 0); + + + //! Splits a triangle at a given point and returns the newly created Vertex. + //! If the boolean parameter is set to true, the 'mask' field of the + //! triangle is propagated to the new triangle. + TMESH_VIRTUAL Vertex *splitTriangle(Triangle *, Point *, bool = 0); + + //! Creates two new triangles connecting the boundary edges e1 and e2 + //! and returns their common edge. + //! If e1 and e2 share a vertex, then only one triangle is created and + //! e1 is returned. + //! Returns NULL if either e1 or e2 are not boundary edges. + TMESH_VIRTUAL Edge *bridgeBoundaries(Edge *e1, Edge *e2); + + ///////////////////////////////////////////////////////////////////////////// + // + // Primitive Destruction (Implemented in "MESH_STRUCTURE/tin.cpp") + // + ///////////////////////////////////////////////////////////////////////////// + + + //! Unlinks a triangle from the mesh. O(1). + + //! Resulting isolated vertices and edges are unlinked too. + //! If necessary, this method duplicates non-manifold vertices that can + //! occur due to the removal of the triangle. + //! The unlinked triangle, along with the other possible unlinked elements, + //! must be removed from the List T through removeUnlinkedElements(). + void unlinkTriangle(Triangle *); + + + //! Unlinks a triangle from the mesh. O(1). + + //! No check is performed on the resulting topology, which may be inconsistent. + //! The unlinked triangle, along with the other possible unlinked elements, + //! must be removed from the List T through removeUnlinkedElements(). + void unlinkTriangleNoManifold(Triangle *); + + + //! Removes a triangle from the mesh. O(N). + + //! This is equivalent to an unlinkTriangle(t) followed by a + //! removeUnlinkedElements(). + void removeTriangle(Triangle *t) { unlinkTriangle(t); removeUnlinkedElements(); } + + //! Removes all the unlinked triangles from List T. Returns the number of removed triangles. O(N). + int removeTriangles(); + + //! Removes all the unlinked edges from List E. Returns the number of removed edges. O(N). + int removeEdges(); + + //! Removes all the unlinked vertices from List V. Returns the number of removed vertices. O(N). + int removeVertices(); + + //! Removes all the unlinked elements from the lists. Returns the number of removed elements. O(N). + int removeUnlinkedElements() { return removeTriangles() + removeEdges() + removeVertices(); } + + //! Removes all the vertices that can be deleted without changing the geometric realization. O(N). + int removeRedundantVertices(); + + ///////////////////////////////////////////////////////////////////////////// + // + // Methods acting on selections (Implemented in "MESH_STRUCTURE/tin.cpp") + // + ///////////////////////////////////////////////////////////////////////////// + + //! Deselects all the triangles. O(N). + void deselectTriangles(); + + //! Removes all the selected triangles. O(N). + void removeSelectedTriangles(); + + //! Selects all the triangles having at least one boundary vertex. O(N). + //! Returns the number of selected triangles. + int selectBoundaryTriangles(); + + //! Enlarges the current selection of one triangle in width. O(N). + + //! Each triangle sharing at least one vertex with a currently selected + //! triangle becomes selected. + //! Returns the number of newly selected triangles. + int growSelection(); + + //! Shrinks the current selection of one triangle in width. O(N). + + //! Each triangle sharing at least one vertex with a currently unselected + //! triangle becomes unselected. + void shrinkSelection(); + + //! Inverts the selection status of all the triangles. O(N). + + //! If 't0' is not NULL, then only the connected component containing 't0' + //! is inverted. + void invertSelection(Triangle *t0 = NULL); + + //! If 't0' is selected, deselect everything but the selected triangles connected to 't0' + void reselectSelection(Triangle *t0); + + //! Creates a new Basic_TMesh out of an existing selection containing 't0'. O(output). + + //! If necessary, non-manifold vertices are properly duplicated. + //! If 'keep_ref' is set to TRUE, then elements of the original mesh point + //! (through their info field) to corresponding elements of the newly created copy. + + TMESH_VIRTUAL Basic_TMesh *createSubMeshFromSelection(Triangle *t0 = NULL, bool keep_ref = 0); + + //! Creates a new Basic_TMesh out of an existing triangle 't0'. O(output). + + TMESH_VIRTUAL Basic_TMesh *createSubMeshFromTriangle(Triangle *t0); + + //! Marks all the triangles within distance L from 'p' as selected. O(output). + + //! A triangle is considered to be within distance L from 'p' only if all + //! its three vertices are so. + //! Point 'p' is assumed to belong to triangle 't0', which is required to + //! limit the complexity to the size of the selected region. + //! Returns the number of selected triangles. + int selectSphericalRegion(Triangle *t0, const double L, const Point *p); + + + //! Marks all the triangles within distance L from 'p' as deselected. O(output). + + //! A triangle is considered to be within distance L from 'p' only if all + //! its three vertices are so. + //! Point 'p' is assumed to belong to triangle 't0', which is required to + //! limit the complexity to the size of the selected region. + //! Returns the number of deselected triangles. + int deselectSphericalRegion(Triangle *t0, const double L, const Point *p); + + + //! Deselects all the triangles farther than L from 'p'. O(N). + + //! A triangle is considered to be farther than L from 'p' if at least + //! one of its three vertices is so. + //! Point 'p' is assumed to belong to triangle 't0'. Passing 't0' avoids + //! the non robust and expensive computation of point-in-triangle. + void reselectSphericalRegion(Triangle *t0, const double L, const Point *p); + + //! Re-triangulates the currently selected region using a Delaunay-like approach. O(SlogS). + + //! A common plane is computed as the average of the planes of the triangles selected; + //! then, the vertices of the region are projected on the plane and edges are iteratively + //! swapped up to convergence (which is guaranteed on planar and simple domains). + //! Finally, the vertices are moved back to their original positions. This operation is + //! particularly useful to improve the quality of nearly flat regions. The selection must + //! be simple and its Gauss map must be entirely contained in a semi-sphere. + //! Returns TRUE on success, FALSE otherwise. + bool retriangulateSelectedRegion(); + + + //! TRUE iff the set of selected triangles in 'l' is simply connected. O(l->numels()). + bool isSelectionSimple(List *l); + + //! Unmarks all the elements but leaves the selection status of triangles as is. O(N). + void unmarkEverythingButSelections(); + + + //! Selects all the triangles of the connected component containing t0. O(N). + + //! If 'stop_on_sharp', expansion from 't0' brakes at tagged sharp edges. + //! Returns the number of selected triangles. + int selectConnectedComponent(Triangle *t0, bool stop_on_sharp = 0); + + + //! Deselects all the triangles of the connected component containing t0. O(N). + + //! If 'stop_on_sharp', expansion from 't0' brakes at tagged sharp edges. + //! Returns the number of deselected triangles. + int deselectConnectedComponent(Triangle *t0, bool stop_on_sharp = 0); + + + //! Append to the current mesh a copy of all the elements of 't'. + //! The newly created elements form a new selection. + TMESH_VIRTUAL void append(Basic_TMesh *t); + + //! This method removes one connected component from the mesh and creates + //! a separate new mesh out of it. The components to be removed is the one + //! containing the first triangle in the list T. + //! Possible selection flags are deleted by this method. + TMESH_VIRTUAL Basic_TMesh *split(); + + + ///////////////////////////////////////////////////////////////////////////// + // + // Region manipulation (Implemented in "MESH_STRUCTURE/tin.cpp") + // + ///////////////////////////////////////////////////////////////////////////// + + //! Make a list of triangles within distance L from 'p'. O(output). + + //! Starting from 't0', which is assumed to contain 'p', add a triangle at a + //! time to the list as long as all the vertices stay within distance L from 'p'. + List *getRegion(Triangle *t0, const double L, const Point *p); + + //! Removes triangles within distance L from 'p'. O(N). + + //! Starting from 't0', which is assumed to contain 'p', remove a triangle at a + //! time as long as all its vertices stay within distance L from 'p'. + void removeRegion(Triangle *t0, const double L, const Point *p); + + //! Get the vertex next to 'v' on the boundary of the region. O(1). + Vertex *nextVertexOnRegionBoundary(Vertex *v) const; + + //! Retrieve internal vertices of a region. O(l->numels()). + + //! This method returns a list containing an edge of the region's boundary + //! as its first element, and all the internal vertices as the remaining elements. + List *getRegionInternalVertices(List *l); + + //! Transform the vertices of the shell containing 't0' using the matrix m. O(S). + void transformShell(Triangle *t0, const Matrix4x4& m); + + //! Translate the mesh by a vector + void translate(const Point& t_vec); + + //! Return the center of mass of the mesh + Point getCenter() const; + + //! Remove all the triangles belonging to the shell containing 't0'. O(N). + void removeShell(Triangle *t0); + + + ///////////////////////////////////////////////////////////////////////////// + // + // Global Operations (Implemented in "MESH_STRUCTURE/tin.cpp") + // + ///////////////////////////////////////////////////////////////////////////// + + //! Tag sharp edges based on threshold angle. O(N). + + //! Tag as sharp all the edges in which the normals of the two incident + //! triangles form an angle greater than 't'. + void sharpEdgeTagging(const double t); + + //! Unmark all the elements. O(N). + void unmarkEverything(); + + //! Bounding box longest edge. 'b' and 't' are set as the longest diagonal end-points. O(N). + coord getBoundingBox(Point& b, Point& t) const; + + //! Bounding box longest diagonal. O(N). + double bboxLongestDiagonal() { Point a, b; getBoundingBox(a, b); return a.distance(b); } + + //! Approximate bounding ball radius. O(N). + double getBoundingBallRadius() const; + + //! Total area of the mesh. O(N). + double area() const; + + //! Total volume of the mesh assuming that boundaries() = 0. O(N). + double volume() const; + + //! Scale the mesh to make it fit within a cube [0,0,0]-[s,s,s]. O(N). + void normalize(const coord s = 1.0); + + //! Scale the mesh to make it fit within a cube [0,0,0]-[s,s,s] and snap coordinates on grid points O(N). + void quantize(const int s = 65536); + + //! Transform the mesh geometry using the transformation matrix m. O(N). + void transform(const Matrix4x4& m); + + //! Randomly move vertices along their normals. O(N). + //! Displacement is bounded by 'p'% of the bounding ball radius. + void addNormalNoise(const double p); + + //! Iteratively swaps edges to minimize the Delaunay minimum angle. O(N). + + //! Edges tagged as sharp are constrained not to swap. + //! On generically curved manifolds this process is not guaranteed to converge. + //! This method returns TRUE if convergence is reached, FALSE otherwise. + bool iterativeEdgeSwaps(); + + //! True if the mesh properly contains 'p' (exact when using rationals). + //! The mesh is assumed to be a well-defined polyhedron (i.e. no boundary, + //! no degenerate triangles, no self-intersections) and to have a correct orientation: + //! result is undetermined otherwise. + bool isInnerPoint(Point& p) const; + + //! Performs one step of Loop subdivision. If 'midpoint' is set, only the connectivity + //! is subdivided, whereas the surface shape is kept unchanged. + void loopSubdivision(bool midpoint =false); + + ///////////////////////////////////////////////////////////////////////////// + // + // Surface topology manipulation (Implemented in "MESH_STRUCTURE/tin.cpp") + // + ///////////////////////////////////////////////////////////////////////////// + + //! Invert all the triangle and edge orientations. O(N). + void flipNormals(); + + //! Invert the orientation of triangles and edges belonging to the shell containing 't0'. O(S). + void flipNormals(Triangle *t0); + + //! Return the triangle with the maximum 'z' coordinate in the shell containing 't0'. O(N). + + //! Useful for orienting meshes bounding solids. + Triangle *topTriangle(Triangle *t0); + + //! Updates the values of n_boundaries, n_handles and n_shells. O(N). + + //! The relative dirty bits are set to zero. + void eulerUpdate(); + + //! Duplicates edges and vertices to make the mesh homeomorphic to a disk. O(N). + void openToDisk(); + + + ///////////////////////////////////////////////////////////////////////////// + // + // Topological fixes (Implemented in "MESH_STRUCTURE/checkAndRepair.cpp") + // + ///////////////////////////////////////////////////////////////////////////// + + //! Removes all connected components but the one having most triangles. + //! Returns the number of components removed. + int removeSmallestComponents(); + + //! Checks that triangles are consistently oriented and, if they are not, + //! invert some of them to achieve an overall consistency. If the mesh is + //! not orientable cut it. Returns: 0 if mesh was already oriented; 1 if + //! the mesh could be oriented without cuts; >1 if cuts were necessary. + int forceNormalConsistence(); + + //! Same as above, but acts on a single connected component and uses one + //! specific triangle from which the orientation is propagated. + int forceNormalConsistence(Triangle *); + + //! Detect singular vertices and duplicte them. Return number of singular + //! vertices being duplicated. + int duplicateNonManifoldVertices(); + + //! Remove redundant triangles (i.e. having the same vertices as others) + //! and return their number. + int removeDuplicatedTriangles(); + + //! Check the mesh connectivity. If everything is fine NULL is returned, + //! otherwise an error string is returned. + const char *checkConnectivity(); + + //! If called in rebuildConnectivity(bool) fix the connectivity between + //! geometric elements + bool fixConnectivity(); //!< AMF_ADD 1.1> + + //! Considers triangles as purely geometric entities and recomputes their + //! connectivity based on vertex correspondence. + //! Returns false if mesh was not an oriented manifold. + TMESH_VIRTUAL bool rebuildConnectivity(bool = true); //!< AMF_CHANGE 1.1> + + //! Looks for topologically different edges having the same geometry + //! (i.e. coincident vertices) and unify them. Return their number. + int mergeCoincidentEdges(); + + + ///////////////////////////////////////////////////////////////////////////// + // + // Geometrical fixes (Implemented in "MESH_STRUCTURE/checkAndRepair.cpp") + // + ///////////////////////////////////////////////////////////////////////////// + + //! Looks for zero-area triangles and resolve them by splits and collapses. + //! If, for any reason, some degeneracies cannot be removed + //! a warning is issued and the degenerate triangles that could not + //! be resolved are selected. + //! The absolute value of the integer returned is the number of + //! collapses performed; the return value is negative if some + //! degenerate triangles could not be resolved. + int removeDegenerateTriangles(); + + //! Calls 'removeDegenerateTriangles()' and, if some degeneracies remain, + //! removes them and fills the resulting holes. Then tries again and, if + //! some degeneracies still remain, removes them and their neighbors and + //! fills the resulting holes, and so on, until the neighborhood growth + //! reaches max-iters. If even in this case some degeneracies remain, + //! returns false, otherwise returns true. + bool strongDegeneracyRemoval(int max_iters); + + //! Removes all the intersecting triangles and patches the resulting holes. + //! If the patches still produce intersections, iterates again on a larger + //! neighborhood. Tries up to max_iters times before giving up. Returns + //! true only if all the intersections could be removed. + bool strongIntersectionRemoval(int max_iters); + + //! Iteratively call strongDegeneracyRemoval and strongIntersectionRemoval + //! to produce an eventually clean mesh without degeneracies and intersections. + //! The two aforementioned methods are called up to max_iter times and + //! each of them is called using 'inner_loops' as a parameter. + //! Returns true only if the mesh could be completely cleaned. + bool meshclean(int max_iters = 10, int inner_loops = 3); + + //! Removes overlapping triangles and return their number. + int removeOverlappingTriangles(); + + //! Checks the mesh for degeneracies, concident vertices and overlaps. + //! If such a flaw is found returns its closest vertex. + Vertex *checkGeometry(); + + //! Selects all the triangles that unproperly intersect other parts of + //! the mesh and return their number. The parameter 'tris_per_cell' + //! determines the depth of the recursive space subdivision used to keep + //! the complexity under a resonable threchold. The default value is safe + //! in most cases. + //! if 'justproper' is true, coincident edges and vertices are not regarded + //! as intersections even if they are not common subsimplexes. + int selectIntersectingTriangles(UINT16 tris_per_cell = 50, bool justproper = false); + + + //! This is as coordBackApproximation() but it also checks for + //! intersections and, if any, it tries different approximations. + //! Returns true if no intersections remain. + bool safeCoordBackApproximation(); + + //! Removes all the connected components whose area is less than 'epsilon'. + //! Returns the number of components removed. + int removeSmallestComponents(double epsilon); + + + ///////////////////////////////////////////////////////////////////////////// + // + // Hole triangulation (Implemented in "MESH_STRUCTURE/holeFilling.cpp") + // + ///////////////////////////////////////////////////////////////////////////// + + //! Computes the barycenter of the boundary loop and connects it with + //! all the edges of the loop. Returns the number of triangles + //! created. + TMESH_VIRTUAL int StarTriangulateHole(Edge *); + + //! Creates a triangulation whose projection on the plane with normal 'nor' + //! is Delaunay. Returns the number of triangles created. + int TriangulateHole(Edge *, Point *nor); + + //! Creates a triangulation of the hole based on heuristics. + //! Returns the number of triangles created. + int TriangulateHole(Edge *); + + //! Creates a triangulation of the hole based on the assumption that it is flat. + //! Returns the number of triangles created. + // int TriangulateFlatHole(Edge *); + + //! Creates a triangulation and inserts the additional points in the List. + //! Returns the number of triangles created. + TMESH_VIRTUAL int TriangulateHole(Edge *, List *); + + //! Hole filling algorithm. Performs a triangulation based on heuristics and, + //! if 'refine' is set to true, adds inner vertices to reproduce the sampling + //! density of the surroundings. + void FillHole(Edge *, bool refine = true); + + //! Fills all the holes having at least 'nbe' boundary edges. If 'refine' + //! is true, adds inner vertices to reproduce the sampling density + //! of the surroundings. Returns number of holes patched. + //! If 'nbe' is 0 (default), all the holes are patched. + int fillSmallBoundaries(int nbe = 0, bool refine = true); + + //! Takes a selected region and inserts inner vertices to reproduce the + //! sampling density of the surroundings. If 't0' is not NULL, only the + //! selected region containing 't0' is refined. Returns the number of + //! vertices inserted. + TMESH_VIRTUAL int refineSelectedHolePatches(Triangle *t0 =NULL); + + //! Retriangulates the vertex neghborhood based on heuristics. + int retriangulateVT(Vertex *); + + //! Joins the two boundary vertices gv and gw through an edge. A pair of triangles is + //! added to properly change the topology of the mesh. + Edge *joinBoundaryLoops(Vertex *, Vertex *, bool = 0, bool = 1); + + // Debug and work-in-progress + + void printReport(); + + protected: + Vertex *watsonInsert(Point *, List *, int); + }; + +#define FOREACHTRIANGLE(Tt, n) for (n = T.head(), Tt = (n)?((Triangle *)n->data):NULL; n != NULL; n=n->next(), Tt = (n)?((Triangle *)n->data):NULL) +#define FOREACHEDGE(Tt, n) for (n = E.head(), Tt = (n)?((Edge *)n->data):NULL; n != NULL; n=n->next(), Tt = (n)?((Edge *)n->data):NULL) +#define FOREACHVERTEX(Tt, n) for (n = V.head(), Tt = (n)?((Vertex *)n->data):NULL; n != NULL; n=n->next(), Tt = (n)?((Vertex *)n->data):NULL) + +#define MARK_VISIT(a) ((a)->mask |= ((unsigned char)1)) +#define IS_VISITED(a) ((a)->mask & ((unsigned char)1)) +#define UNMARK_VISIT(a) ((a)->mask &= (~((unsigned char)1))) + +#define MARK_VISIT2(a) ((a)->mask |= ((unsigned char)2)) +#define IS_VISITED2(a) ((a)->mask & ((unsigned char)2)) +#define UNMARK_VISIT2(a) ((a)->mask &= (~((unsigned char)2))) + +#define MARK_BIT(a,b) ((a)->mask |= ((unsigned char)(1<mask & ((unsigned char)(1<mask &= (~((unsigned char)(1<commonVertex(e2);} //!< First vertex + Vertex *v2() const {return e2->commonVertex(e3);} //!< Second vertex + Vertex *v3() const {return e3->commonVertex(e1);} //!< Third vertex + + //! First adjacent triangle. NULL if boundary. + Triangle *t1() const {return e1->oppositeTriangle(this);} + + //! Second adjacent triangle. NULL if boundary. + Triangle *t2() const {return e2->oppositeTriangle(this);} + + //! Third adjacent triangle. NULL if boundary. + Triangle *t3() const {return e3->oppositeTriangle(this);} + + //! TRUE iff 'e' is an edge of the triangle. + bool hasEdge(const Edge *e) const {return (e==e1 || e==e2 || e==e3);} + + //! TRUE iff 'v' is a vertex of the triangle. + bool hasVertex(const Vertex *v) const {return (e1->hasVertex(v) || e2->hasVertex(v) || e3->hasVertex(v));} + + //! Triangle's edge opposite to 'v'. NULL if 'v' is not a vertex of the triangle. + Edge *oppositeEdge(const Vertex *v) const + {return ((!e1->hasVertex(v))?(e1):((!e2->hasVertex(v))?(e2):((!e3->hasVertex(v))?(e3):(NULL))));} + + //! Adjacent triangle opposite to 'v'. NULL if 'v' is not a vertex of the triangle. + Triangle *oppositeTriangle(const Vertex *v) const + {return ((!e1->hasVertex(v))?(t1()):((!e2->hasVertex(v))?(t2()):((!e3->hasVertex(v))?(t3()):(NULL))));} + + //! Triangle's vertex opposite to 'e'. NULL if 'e' is not an edge of the triangle. + Vertex *oppositeVertex(const Edge *e) const + {return (e==e1)?(v2()):((e==e2)?(v3()):((e==e3)?(v1()):(NULL)));} + + //! Triangle adjacent to the next edge of 'e'. NULL if 'e' is not an edge of the triangle. + Triangle *rightTriangle(const Edge *e) const + {return (e==e1)?(t2()):((e==e2)?(t3()):((e==e3)?(t1()):(NULL)));} + + //! Triangle adjacent to the previous edge of 'e'. NULL if 'e' is not an edge of the triangle. + Triangle *leftTriangle(const Edge *e) const + {return (e==e1)?(t3()):((e==e2)?(t1()):((e==e3)?(t2()):(NULL)));} + + //! Edge next to 'e' in the ordering or the triangle. NULL if 'e' is not an edge of the triangle. + Edge *nextEdge(const Edge *e) const {return ((e==e1)?(e2):((e==e2)?(e3):((e==e3)?(e1):(NULL))));} + + //! Edge preceeding 'e' in the ordering or the triangle. NULL if 'e' is not an edge of the triangle. + Edge *prevEdge(const Edge *e) const {return ((e==e1)?(e3):((e==e2)?(e1):((e==e3)?(e2):(NULL))));} + + //! Vertex next to 'v' in the ordering or the triangle. NULL if 'v' is not a vertex of the triangle. + Vertex *nextVertex(const Vertex *v) const + {return (!e1->hasVertex(v))?(v3()):((!e2->hasVertex(v))?(v1()):((!e3->hasVertex(v))?(v2()):(NULL)));} + + //! Vertex preceeding 'v' in the ordering or the triangle. NULL if 'v' is not a vertex of the triangle. + Vertex *prevVertex(const Vertex *v) const + {return (!e1->hasVertex(v))?(v1()):((!e2->hasVertex(v))?(v2()):((!e3->hasVertex(v))?(v3()):(NULL)));} + + //! Edge next to 'e' in the ordering or the triangle. NULL if 'e' is not an edge of the triangle. + Edge *nextEdge(const Vertex *v) const { return (v == v1()) ? (e2) : ((v == v2()) ? (e3) : ((v == v3()) ? (e1) : (NULL))); } + + + //! If this triangle shares an edge with 'b', then such an edge is returned. NULL otherwise. + Edge *commonEdge(const Triangle *b) const + {return ((e1 == b->e1 || e1 == b->e2 || e1 == b->e3)?(e1):\ + (((e2 == b->e1 || e2 == b->e2 || e2 == b->e3)?(e2):\ + (((e3 == b->e1 || e3 == b->e2 || e3 == b->e3)?(e3):(NULL))))));} + + //! If this triangle shares a vertex with 'b', then such a vertex is returned. NULL otherwise. + Vertex *commonVertex(const Triangle *b) const; + + //! Replace edge 'a' with edge 'b' in the triangle and return TRUE. If 'a' is not an edge of the triangle return FALSE. + bool replaceEdge(const Edge *a, Edge *b) + {if (e1==a) e1=b; else if (e2==a) e2=b; else if (e3==a) e3=b; else return 0; return 1;} + + //! TRUE if the oriantation is consistent with the one of 't' OR if this and 't' do not share any edge. + bool checkAdjNor(const Triangle *t) const; + + //! Return a vector orthogonal to the plane of the triangle. If triangle is degenerate return a null vector. + Point getVector() const; + + //! Return the triangle's barycenter. + Point getCenter() const; + + //! Return the center of the triangle's bounding sphere. + Point getCircleCenter() const; + + //! TRUE iff 'p' is inside the triangle's bounding sphere. + bool inSphere(const Point *p) const; + + //! Squared distance of 'p' from the plane of the triangle. Return -1 if triangle is degenerate. + coord squaredDistanceFromPoint(const Point *p) const; + + //! Squared distance of 'p' from the closest point of the triangle. Return -1 if triangle is degenerate. + //! If closest point is in the interior of the triangle, *closest_edge and *closest_vertex are set to NULL. + //! If the closest point is in the interior of an edge, *closest_edge is initialized with that edge. + //! If the closest point is a vertex, *closest_vertex is initialized with it. + coord pointTriangleSquaredDistance(const Point *p, Edge **closest_edge =NULL, Vertex **closest_vertex =NULL) const; + + //! Projection of 'p' on the plane of the triangle. Return INFINITE_POINT if triangle is degenerate. + Point project(const Point *p) const; + + //! Returns the longest edge of the triangle. + Edge *getLongestEdge() const; + + //! Degeneracy check using exact predicates. Return TRUE iff triangle has zero area. + bool isExactlyDegenerate() const; + + //! Returns the edge opposite to a 'cap' vertex + Edge *getCapEdge() const; + + //! Print the coordinates of the three vertices to the file handler pointed to by 'f' (stdout by default). + void printTriangle(FILE *f =stdout) const; + + //! true if this triangle itersects 't' other than on common subsimplexes + //! if 'justproper' is true, coincident edges and vertices are not regarded + //! as intersections even if they are not common subsimplexes. + bool intersects(const Triangle *t, bool justproper =false) const; + + // FUNCTIONS BELOW THIS LINE MAY RETURN APPROXIMATED/NOT ROBUST RESULTS EVEN WHEN USING RATIONALS + + + + //! Return a normal vector with direction (v1-v2) cross (v2-v3). If triangle is degenerate return a null vector. + Point getNormal() const; + + //! Area of the triangle (Heron's formula). + double area() const; + + //! Perimeter of the triangle. + double perimeter() const; + + //! Angle at vertex 'v'. Return -1 if 'v' is not a vertex of the triangle. + double getAngle(const Vertex *v) const; + + //! Angle between the normal vector of this and the one of 't'. Return -1 if one or both the triangles are degenerate. + double getDAngle(const Triangle *t) const; + + //! Distance of 'p' from the plane of the triangle. Return -1 if triangle is degenerate. + double distanceFromPoint(const Point *p) const; + + //! Distance of 'p' from the closest point of the triangle. Return -1 if triangle is degenerate. + //! If 'c' is not NULL, its coordinates are set to the ones of the closest point. + double pointTriangleDistance(const Point *p, Point *c = NULL) const; + + //! Return TRUE iff one of the adjacent triangles overlaps with this one + bool overlaps() const; +}; + +#define FOREACHTRIANGLEEDGE(t, e) for ((e) = (t)->e1; (e) != NULL; (e)=((e)==(t)->e3)?(NULL):((t)->nextEdge(e))) + +} //namespace T_MESH + +#endif // _TRIANGLE_H + diff --git a/src/mesh_fix/include/TMesh/vertex.h b/src/mesh_fix/include/TMesh/vertex.h new file mode 100644 index 000000000..e43b898b9 --- /dev/null +++ b/src/mesh_fix/include/TMesh/vertex.h @@ -0,0 +1,223 @@ +/**************************************************************************** +* TMesh * +* * +* Consiglio Nazionale delle Ricerche * +* Istituto di Matematica Applicata e Tecnologie Informatiche * +* Sezione di Genova * +* IMATI-GE / CNR * +* * +* Authors: Marco Attene * +* Copyright(C) 2013: IMATI-GE / CNR * +* All rights reserved. * +* * +* This program is dual-licensed as follows: * +* * +* (1) You may use TMesh as 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 3 of the License, or * +* (at your option) any later version. * +* In this case the 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 (http://www.gnu.org/licenses/gpl.txt) * +* for more details. * +* * +* (2) You may use TMesh as part of a commercial software. In this case a * +* proper agreement must be reached with the Authors and with IMATI-GE/CNR * +* based on a proper licensing contract. * +* * +****************************************************************************/ + +#ifndef _VERTEX_H +#define _VERTEX_H + +#include "basics.h" +#include "list.h" +#include "point.h" + +namespace T_MESH +{ + +//! Vertex of a Basic_TMesh + +//! This class represents a vertex of a manifold and oriented triangulation. +//! The base-class Point +//! describes geometrical and additional attributes of the vertex. The +//! field 'e0' is sufficient to retrieve all the neighboring elements in +//! optimal time, while the field 'mask' is useful for assigning up to 256 +//! different states to the vertex. + + +class Vertex : public Point +{ + public : + class Edge *e0; //!< One of the incident edges + unsigned char mask; //!< bit-mask for marking purposes + + //! Creates a new vertex with coordinates (0,0,0). + Vertex(); + + //! Creates a new vertex with the same coordinates (x,y,z). + Vertex(const coord& x, const coord& y, const coord& z); + + //! Creates a new vertex with the same coordinates as 'p'. The info field is not copied. + Vertex(const Point *p); + + //! Creates a new vertex with the same coordinates as 'p'. The info field is not copied. + Vertex(const Point& p); + ~Vertex(); //!< Destructor + + //! Returns true only if object is a basic Vertex. All the reimplementations must return false. + TMESH_VIRTUAL bool isBaseType() const { return true; } + + bool isLinked() const {return (e0!=0);} //!< TRUE iff vertex is not isolated + + //! List of adjacent vertices. + + //! Returns the list of vertices which are linked to this through an edge. + //! The list is counter-clockwise ordered. In the case of an internal ver- + //! tex the list starts from the opposite vertex of e0. If the vertex is on + //! the boundary, the list starts from the opposite vertex of the clock- + //! wise-most boundary edge. + List *VV() const; + + //! List of incident edges. + + //! Returns the list of edges incident at this vertex. The list is counter-clockwise + //! ordered. In the case of an internal vertex the list starts from 'e0' + //! If the vertex is on the boundary, the list starts from its clockwise-most + //! incident boundary edge. + List *VE() const; + + //! List of incident triangles. + + //! Returns the list of triangles incident at this. The list is counter- + //! clockwise ordered. In the case of an internal vertex the list starts + //! from the triangle on the left of e0, when looking from this. If the + //! vertex is on the boundary, the list starts from the clockwise-most + //! boundary triangle. + List *VT() const; + + //! Returns the edge connecting this vertex to 'v'. NULL if such an edge does not exist. + class Edge *getEdge(const Vertex *v) const; + int valence() const; //!< Returns the number of incident edges + int isOnBoundary() const; //!< TRUE iff vertex is on boundary + + //! Returns the edge following this vertex on the boundary. + + //! This edge is the counterclockwise-most incident edge. + //! Returns NULL if this vertex is not on the boundary. + Edge *nextBoundaryEdge() const; + + //! Returns the edge preceeding this vertex on the boundary. + + //! This edge is the clockwise-most incident edge. + //! Returns NULL if this vertex is not on the boundary. + Edge *prevBoundaryEdge() const; + + //! Returns the vertex following this one on the boundary. + + //! If the vertex is on the boundary, this is equivalent to nextBoundaryEdge()->oppositeVertex(v) + //! otherwise returns NULL. + Vertex *nextOnBoundary() const; + + //! Returns the vertex preceeding this one on the boundary. + + //! If the vertex is on the boundary, this is equivalent to prevBoundaryEdge()->oppositeVertex(v) + //! otherwise returns NULL. + Vertex *prevOnBoundary() const; + + //! TRUE iff vertex neighborhood is a flat disk. Always FALSE for boundary vertices. + bool isFlat() const; + + //! TRUE iff vertex neighborhood is made of either two flat halfdisks or one flat halfdisk on a rectilinear boundary. + //! When TRUE, the two edges may be initialized with the two non-flat incident edges. + //! If the two halfdisks are also coplanar, returns TRUE and e1 and e2 are set to NULL. + bool isDoubleFlat(Edge **e1, Edge **e2) const; + + //! Unlinks the vertex if it is either Flat() or DoubleFlat(). On success, the function returns TRUE, + //! the vertex neighborhood is retriangulated, and the geometric realization does not change. + //! If 'check_neighborhood' is 'false', incident triangles are assumed to be neither degenerate nor + //! overlapping. + //! Attention! The method unlinks the vertex but does not remove it from the Basic_TMesh list. The + //! calling function must clear the lists through Basic_TMesh::removeUnlinkedElements(). + bool removeIfRedundant(bool check_neighborhood = true); + + //! Normal at the vertex computed as the sum of incident triangle normals weighted on their incidence angle. + Point getNormal() const; + + //! Returns the angle between the two incident boundary edges. If the vertex is not on the boundary, returns -1. + double getBoundaryAngle() const; + + //! Discriminant Angle for triangulating 3D polygons. + + //! This method is useful when patching holes, and represents a heuristic + //! for choosing which vertex of the hole's boundary must be patched first. + //! Several cases are considered, including degenerate ones. Let 'v1' and 'v2' + //! be the two boundary vertices which are linked to this one through a boundary + //! edge. If 'v1' and 'v2' coincide, the method returns a negative number. + //! If 'v1' , 'this' , 'v2' form a flat angle, the method returns 3PI (270 + //! degrees). If the angle formed by 'v1' , 'this' , 'v2' is 0, the method + //! returns 0. If the vertex is not on the boundary, the method returns the + //! limit number DBL_MAX. In all the other cases the method returns the sum + //! of three angles A + D1 + D2, where A is the angle formed by v1 , this , + //! v2 , while D1 is the angle between the normal of the clockwise-most + //! incident boundary triangle and the normal of the triangle v1 , this , + //! v2; D2 is the analogous for the counterclockwise-most incident boundary + //! triangle. + double getAngleForTriangulation() const; + + //! Discriminant Angle for triangulating flat (or nearly flat) polygons. + + //! This method returns the angle between the two incident boundary edges + //! when projected onto the plane whose normal is 'n'. This angle + //! may be more than PI, because it represents the aperture of the non-tri- + //! angulated region around the vertex when projected on the plane. If the + //! vertex is not on the boundary, the method returns the limit value + //! DBL_MAX. + double getAngleOnAveragePlane(Point *n) const; + + double totalAngle() const; //!< Sum of incident angles. Returns -1 if on boundary. + + //! Excess angle. Returns DBL_MAX if on boundary. + double gaussianCurvature() const {double t=totalAngle(); return (t>=0)?(t):(DBL_MAX);} + + double totalDihedralAngle() const; //!< Sum of signed dihedral angles. Returns DBL_MAX if on boundary. + double voronoiArea() const; //!< A third of the total area of incident triangles. + + //! Zips the gap starting from here. + int zip(const bool =1); + +// Edge *inverseCollapse(Vertex *, Vertex *, Vertex *); + Edge *inverseCollapse(Vertex *, Edge *, Edge *, Edge *, Edge *, Edge *, class Triangle *, class Triangle *); +}; + +//! Scans the nodes 'n' of a list 'l' of vertices 'v'. +#define FOREACHVVVERTEX(l, v, n) for (n = l->head(), v = (n)?((Vertex *)n->data):NULL; n != NULL; n=n->next(), v = (n)?((Vertex *)n->data):NULL) + +//! Scans the nodes 'n' of a list 'l' of edges 'e'. +#define FOREACHVEEDGE(l, e, n) for (n = l->head(), e = (n)?((Edge *)n->data):NULL; n != NULL; n=n->next(), e = (n)?((Edge *)n->data):NULL) + +//! Scans the nodes 'n' of a list 'l' of triangles 't'. +#define FOREACHVTTRIANGLE(l, t, n) for (n = l->head(), t = (n)?((T_MESH::Triangle *)n->data):NULL; n != NULL; n=n->next(), t = (n)?((T_MESH::Triangle *)n->data):NULL) + + +//! Extended vertex for temporary use during connectivity creation. + +//! This class is used to allow the reconstruction of the connectivity +//! in linear time (average case) and to handle badly oriented input files. +//! It provides a complete VE relation. + +class ExtVertex +{ + public : + Vertex *v; + List VE; + + ExtVertex(Vertex *a) {v=a;} +}; + +} //namespace T_MESH + +#endif //_VERTEX_H + diff --git a/src/mesh_fix/src/Algorithms/checkAndRepair.cpp b/src/mesh_fix/src/Algorithms/checkAndRepair.cpp new file mode 100644 index 000000000..d524bb071 --- /dev/null +++ b/src/mesh_fix/src/Algorithms/checkAndRepair.cpp @@ -0,0 +1,1058 @@ +/**************************************************************************** +* TMesh * +* * +* Consiglio Nazionale delle Ricerche * +* Istituto di Matematica Applicata e Tecnologie Informatiche * +* Sezione di Genova * +* IMATI-GE / CNR * +* * +* Authors: Marco Attene * +* Copyright(C) 2013: IMATI-GE / CNR * +* All rights reserved. * +* * +* This program is dual-licensed as follows: * +* * +* (1) You may use TMesh as 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 3 of the License, or * +* (at your option) any later version. * +* In this case the 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 (http://www.gnu.org/licenses/gpl.txt) * +* for more details. * +* * +* (2) You may use TMesh as part of a commercial software. In this case a * +* proper agreement must be reached with the Authors and with IMATI-GE/CNR * +* based on a proper licensing contract. * +* * +****************************************************************************/ + +#include "tmesh.h" +#include "jqsort.h" +#include +#include + +namespace T_MESH +{ + +////////////// Checks the triangulation's connectivity ////////////// +// +// This method should be used when implementing new algorithms to +// check the consistency of the connectivity graph. Because an +// inconsistent graph is not assumed by all the other methods, such +// a flaw is considered critical and the program should terminate. +// If connectivity is ok, NULL is returned, otherwise a string +// describing the error is returned. +// +///////////////////////////////////////////////////////////////////// + +const char *Basic_TMesh::checkConnectivity() +{ + Vertex *v; + Edge *e,*e2; + Triangle *t; + Node *n,*m; + List *ve; + + FOREACHVERTEX(v, n) + { + if (v == NULL) return "checkConnectivity: detected NULL element in V list!"; + if (v->e0 == NULL) return "checkConnectivity: detected NULL e0 pointer for a vertex!"; + if (!v->e0->hasVertex(v)) return "checkConnectivity: detected wrong e0 pointer for a vertex!"; + } + + FOREACHEDGE(e, n) + { + if (e == NULL) return "checkConnectivity: detected NULL element in E list!"; + if (e->v1 == NULL || e->v2 == NULL) return "checkConnectivity: detected edge with one or two NULL end-points!"; + if (e->v1 == e->v2) return "checkConnectivity: detected edge with two coincident end-points!"; + if (e->t1 == NULL && e->t2 == NULL) return "checkConnectivity: detected edge with no incident triangles!"; + if (e->t1 != NULL) + { + if (!e->t1->hasEdge(e)) return "checkConnectivity: detected wrong t1 triangle at an edge"; + if (e->commonVertex(e->t1->nextEdge(e)) == e->v1) + return "checkConnectivity: Edge orientation does not match t1 normal"; + } + if (e->t2 != NULL) + { + if (!e->t2->hasEdge(e)) return "checkConnectivity: detected wrong t2 triangle at an edge"; + if (e->commonVertex(e->t2->nextEdge(e)) == e->v2) + return "checkConnectivity: Edge orientation does not match t2 normal"; + } + } + + FOREACHTRIANGLE(t, n) + { + if (t == NULL) return "checkConnectivity: detected NULL element in T list!"; + if (t->e1 == NULL || t->e2 == NULL || t->e3 == NULL) return "checkConnectivity: detected NULL as a triangle edge!"; + if (t->e1 == t->e2 || t->e1 == t->e3 || t->e2 == t->e3) return "checkConnectivity: detected triangle with two coincident edges!"; + if (t->v1() == NULL || t->v2() == NULL || t->v3() == NULL) return "checkConnectivity: triangle edges do not share vertices!"; + if (t->e1->t1 != t && t->e1->t2 != t) return "checkConnectivity: detected triangle with 1st edge not pointing to the triangle itself!"; + if (t->e2->t1 != t && t->e2->t2 != t) return "checkConnectivity: detected triangle with 2nd edge not pointing to the triangle itself!"; + if (t->e3->t1 != t && t->e3->t2 != t) return "checkConnectivity: detected triangle with 3rd edge not pointing to the triangle itself!"; + } + + FOREACHEDGE(e, n) + { + ve = e->v1->VE(); + FOREACHVEEDGE(ve, e2, m) + { + if (e2 != e && e2->oppositeVertex(e->v1) == e->v2) return "checkConnectivity: detected duplicate edge!"; + } + if (ve->containsNode(e) == NULL) return "checkConnectivity: detected non manifold vertex!"; + delete(ve); + ve = e->v2->VE(); + FOREACHVEEDGE(ve, e2, m) + { + if (e2 != e && e2->oppositeVertex(e->v2) == e->v1) return "checkConnectivity: detected duplicate edge!"; + } + if (ve->containsNode(e) == NULL) return "checkConnectivity: detected non manifold vertex!"; + delete(ve); + } + + return NULL; +} + + +////////////// Duplicate non-manifold vertices ///////////////////// +// +// If a vertex is topologically non-manifold, this data structure +// does not guarantee its functionality. Therefore, in order to use +// the same triangle mesh, this method allows to duplicate such +// vertices. Notice that the data-structure cannot code non-manifold +// edges. +// +//////////////////////////////////////////////////////////////////// + +int Basic_TMesh::duplicateNonManifoldVertices() +{ + Vertex *v; + Edge *e, *f; + Node *n, *m; + List *ve; + int dv = 0; + + FOREACHEDGE(e, n) + { + ve = e->v1->VE(); + if (ve->containsNode(e) == NULL) + { + v = newVertex(e->v1); //! + v->info = e->v1->info; //! < AMF_CHANGE 1.1-2 > + v->mask = 0; //! + V.appendHead(v); + + FOREACHVEEDGE(ve, f, m) f->replaceVertex(e->v1, v); + v->e0 = e->v1->e0; + e->v1->e0 = e; + dv++; + } + delete(ve); + } + FOREACHEDGE(e, n) + { + ve = e->v2->VE(); + if (ve->containsNode(e) == NULL) + { + v = newVertex(e->v2); //! + v->info = e->v2->info; //! < AMF_CHANGE 1.1-2 > + v->mask = 0; //! + V.appendHead(v); + + FOREACHVEEDGE(ve, f, m) f->replaceVertex(e->v2, v); + v->e0 = e->v2->e0; + e->v2->e0 = e; + dv++; + } + delete(ve); + } + + if (dv) d_boundaries = d_handles = d_shells = 1; + + return dv; +} + +////////////// Checks the triangulation geometry ////////////// +//// ////// +//// Looks for coincident vertices, degenerate triangles ////// +//// and overlapping triangles. ////// +//// If something is wrong returns the closest vertex. ////// +//// ////// +/////////////////////////////////////////////////////////////// + +Vertex *Basic_TMesh::checkGeometry() +{ + int i; + Vertex *ret = NULL; + double ang, minda = 0; + Triangle *t; + Edge *e; + Vertex **varr = (Vertex **)V.toArray(); + Edge **evarr; + Vertex *v1, *v2; + Node *n; + + if (varr == NULL) TMesh::warning("checkGeometry: Not enough memory. Can't check for coincident vertices.\n"); + else + { + jqsort((Data **)varr, V.numels(), xyzCompare); + for (i=0; i<(V.numels()-1); i++) + { + v1 = ((Vertex *)varr[i]); + v2 = ((Vertex *)varr[i+1]); + if ((*v1)==(*v2)) + { + ret = v1; + TMesh::warning("checkGeometry: detected coincident vertices.\n"); + if (v1->getEdge(v2)) + { + TMesh::warning(" and there is an edge connecting them!\n"); + free(varr); + return v1; + } + } + } + free(varr); + } + + evarr = (Edge **)E.toArray(); + if (evarr == NULL) TMesh::warning("checkGeometry: Not enough memory. Can't check for coincident edges.\n"); + else + { + jqsort((Data **)evarr, E.numels(), lexEdgeCompare); + for (i=0; i<(E.numels()-1); i++) + { + if (!lexEdgeCompare(evarr[i], evarr[i+1])) + { + ret = ((Edge *)evarr[i])->v1; + TMesh::warning("checkGeometry: detected coincident edges.\n"); + } + } + free(evarr); + } + + FOREACHTRIANGLE(t, n) + { + ang = t->getAngle(t->v1()); + if (ang == 0 || ang == M_PI) {TMesh::warning("checkGeometry: degenerate triangle detected.\n"); return t->v1();} + ang = t->getAngle(t->v2()); + if (ang == 0 || ang == M_PI) {TMesh::warning("checkGeometry: degenerate triangle detected.\n"); return t->v2();} + ang = t->getAngle(t->v3()); + if (ang == 0 || ang == M_PI) {TMesh::warning("checkGeometry: degenerate triangle detected.\n"); return t->v3();} + } + + ang = minda = 0; + FOREACHEDGE(e, n) + if (e->t1 != NULL && e->t2 != NULL && (ang = e->t1->getDAngle(e->t2)) == M_PI) + {TMesh::warning("checkGeometry: overlapping triangles detected.\n"); return e->v1;} + else minda = MAX(minda,ang); + TMesh::info("checkGeometry: minimum dihedral angle = %f (%f DEGs)\n", M_PI-minda, ((M_PI-minda)*360)/(2*M_PI)); + return ret; +} + + +///// Merges possible coincident edges ////////// + +int Basic_TMesh::mergeCoincidentEdges() +{ + V.sort(&xyzCompare); + Node *n; + Vertex *v, *pv = (Vertex *)V.head()->data; + + FOREACHVERTEX(v, n) UNMARK_BIT(v, 5); + + Edge *e; + FOREACHEDGE(e, n) if (e->isOnBoundary()) { MARK_BIT(e->v1, 5); MARK_BIT(e->v2, 5); } + + FOREACHVERTEX(v, n) + { + if ((*v) != (*pv) || !IS_BIT(v,5)) pv = v; + v->info = pv; + UNMARK_BIT(v, 5); + } + + // At this point any vertex points (through 'info') to its unique representative (possibly itself) + + FOREACHVERTEX(v, n) v->e0 = NULL; + + FOREACHEDGE(e, n) + { + if (e->v1->info != e->v1) e->v1 = (Vertex *)e->v1->info; + if (e->v2->info != e->v2) e->v2 = (Vertex *)e->v2->info; + e->v1->e0 = e->v2->e0 = e; + } + int rv = removeVertices(); + + // At this point the mesh should no longer have duplicated vertices, but may have duplicated edges + E.sort(&vtxEdgeCompare); + Edge *pe = (Edge *)E.head()->data; + FOREACHEDGE(e, n) + { + if (!e->isOnBoundary() || vtxEdgeCompare(e, pe)) pe = e; + e->info = pe; + } + FOREACHEDGE(e, n) if (e->info != e) + { + Triangle *t1 = e->getBoundaryTriangle(); + Edge *f = ((Edge *)e->info); + Triangle *t2 = f->getBoundaryTriangle(); + t1->replaceEdge(e, f); + ((f->t1 == NULL) ? (f->t1) : (f->t2)) = t1; + e->v1 = e->v2 = NULL; + f->v1->e0 = f->v2->e0 = f; + } + removeUnlinkedElements(); + + return 1; +} + +///// Fix geometric connectivity ////////// + +bool Basic_TMesh::fixConnectivity(){ //!< AMF_ADD 1.1> + bool retval = true; + int i; + + if ((i = removeVertices())) { retval = false; TMesh::warning("%d isolated vertices have been removed.\n", i); } + if (cutAndStitch()) { retval=false; TMesh::warning("Some cuts were necessary to cope with non manifold configuration.\n"); } + if (forceNormalConsistence()) { retval = false; TMesh::warning("Some triangles have been reversed to achieve orientation.\n"); } + if ((i=duplicateNonManifoldVertices())) { retval=false; TMesh::warning("%d non-manifold vertices have been duplicated.\n",i); } + if ((i=removeDuplicatedTriangles())) { retval=false; TMesh::warning("%d double-triangles have been removed.\n",i); } + + return retval; +} + +///// Recompute triangle connectivity ////////// + +bool Basic_TMesh::rebuildConnectivity(bool fixconnectivity) //!< AMF_CHANGE 1.1> +{ + if (V.numels() == 0) return false; + V.sort(&xyzCompare); + Node *n; + Vertex *v, *pv=(Vertex *)V.head()->data; + + FOREACHVERTEX(v,n) + { + if ((*v)!=(*pv)) pv=v; + v->info = pv; + } + + // At this point any vertex points (through 'info') to its unique representative (possibly itself) + + FOREACHVERTEX(v,n) v->e0=NULL; + + Edge *e; + FOREACHEDGE(e, n) + { + if (e->v1->info != e->v1) e->v1 = (Vertex *)e->v1->info; + if (e->v2->info != e->v2) e->v2 = (Vertex *)e->v2->info; + e->v1->e0 = e->v2->e0 = e; + } + int rv = removeVertices(); + + // At this point the mesh should no longer have duplicated vertices, but may have duplicated edges + + Triangle *t; + ExtVertex **var = new ExtVertex *[V.numels()]; + int i=0; + FOREACHVERTEX(v, n) { v->e0 = NULL; var[i] = new ExtVertex(v); v->info = (void *)(intptr_t)i; i++; } + int nt = T.numels(); + int *triangles = new int[nt*3]; + i = 0; FOREACHTRIANGLE(t, n) + { + triangles[i * 3] = (j_voidint)t->v1()->info; + triangles[i*3+1] = (j_voidint)t->v2()->info; + triangles[i*3+2] = (j_voidint)t->v3()->info; + i++; + } + T.freeNodes(); + E.freeNodes(); + int v1,v2,v3; + for (i = 0; iisOnBoundary() && e->t1->oppositeVertex(e) == e->t2->oppositeVertex(e)) + { + unlinkTriangle(e->t2); + i++; + } + removeUnlinkedElements(); + + if (i) d_boundaries = d_handles = d_shells = 1; + + return i; +} + + +//////// Split an edge at all the vertices belonging to its inner segment ///////// + +int multiSplitEdge(Basic_TMesh *tin, Edge *e) +{ + List splitVertices; + List triangles, tounmark; + MARK_BIT(e, 5); + if (e->t1 != NULL) { triangles.appendTail(e->t1); MARK_BIT(e->t1, 5); } + if (e->t2 != NULL) { triangles.appendTail(e->t2); MARK_BIT(e->t2, 5); } + + Triangle *t, *y; + Vertex *v; + + while ((t = (Triangle *)triangles.popHead()) != NULL) + { + tounmark.appendHead(t); + int num_front_edges = 0; + if (IS_BIT(t->e1, 5)) num_front_edges++; + if (IS_BIT(t->e2, 5)) num_front_edges++; + if (IS_BIT(t->e3, 5)) num_front_edges++; + if (num_front_edges == 3) continue; + if (num_front_edges == 1) + { + Edge *f = (IS_BIT(t->e1, 5)) ? (t->e1) : ((IS_BIT(t->e2, 5)) ? (t->e2) : (t->e3)); + v = t->oppositeVertex(f); + if (!v->exactMisalignment(e->v1, e->v2)) + { + if (!IS_BIT(v, 5) && Point::pointInInnerSegment(v, e->v1, e->v2)) { splitVertices.appendTail(v); MARK_BIT(v, 5); } + y = t->nextEdge(f)->oppositeTriangle(t); if (y != NULL && !IS_BIT(y, 5)) { triangles.appendTail(y); MARK_BIT(y, 5); } + y = t->prevEdge(f)->oppositeTriangle(t); if (y != NULL && !IS_BIT(y, 5)) { triangles.appendTail(y); MARK_BIT(y, 5); } + } + } + MARK_BIT(t->e1, 5); MARK_BIT(t->e2, 5); MARK_BIT(t->e3, 5); + } + + Node *n; + FOREACHVTTRIANGLE((&tounmark), t, n) { UNMARK_BIT(t, 5); UNMARK_BIT(t->e1, 5); UNMARK_BIT(t->e2, 5); UNMARK_BIT(t->e3, 5); } + FOREACHVVVERTEX((&splitVertices), v, n) UNMARK_BIT(v, 5); + + while (splitVertices.numels()) + { + coord ad, mind = DBL_MAX; + Vertex *gv; + FOREACHVVVERTEX((&splitVertices), v, n) if ((ad = v->squaredDistance(e->v2)) < mind) { gv = v; mind = ad; } + splitVertices.removeNode(gv); + tin->splitEdge(e, gv); + } + + return 1; +} + +//////// Split caps and collapse needles to eliminate degenerate triangles ///////// + +int Basic_TMesh::removeDegenerateTriangles() +{ + Node *n; + Triangle *t; + Edge *e, *e1, *e2, *e3, *e4; + Vertex *ov1, *ov2, *splitvs[2]; + int nov; + + List edges(E); + FOREACHVEEDGE((&edges), e, n) MARK_BIT(e, 5); + + while ((e = (Edge *)edges.popHead()) != NULL) // Split caps + { + UNMARK_BIT(e, 5); + nov = 0; + ov1 = (e->t1 != NULL) ? (e->t1->oppositeVertex(e)) : NULL; + ov2 = (e->t2 != NULL) ? (e->t2->oppositeVertex(e)) : NULL; + if (ov1 != NULL && Point::pointInInnerSegment(ov1, e->v1, e->v2)) splitvs[nov++] = ov1; + if (ov2 != NULL && Point::pointInInnerSegment(ov2, e->v1, e->v2)) splitvs[nov++] = ov2; + if (nov == 1) splitvs[1] = splitvs[0]; + if (nov > 1 && ov1->squaredDistance(e->v1) > ov2->squaredDistance(e->v1)) { splitvs[0] = ov2; splitvs[1] = ov1; } + + if (nov) + { + e1 = (e->t1 != NULL) ? (e->t1->nextEdge(e)) : NULL; + e2 = (e->t1 != NULL) ? (e->t1->prevEdge(e)) : NULL; + e3 = (e->t2 != NULL) ? (e->t2->nextEdge(e)) : NULL; + e4 = (e->t2 != NULL) ? (e->t2->prevEdge(e)) : NULL; + splitEdge(e, splitvs[1]); + if (nov > 1) splitEdge(e, splitvs[0]); + e = e1; if (e != NULL && !IS_BIT(e, 5)) { edges.appendTail(e); MARK_BIT(e, 5); } + e = e2; if (e != NULL && !IS_BIT(e, 5)) { edges.appendTail(e); MARK_BIT(e, 5); } + e = e3; if (e != NULL && !IS_BIT(e, 5)) { edges.appendTail(e); MARK_BIT(e, 5); } + e = e4; if (e != NULL && !IS_BIT(e, 5)) { edges.appendTail(e); MARK_BIT(e, 5); } + } + } + + int nc = 0; // Num of collapses to remove needles + + // Remove needles + FOREACHEDGE(e, n) if (e->isLinked() && ((*e->v1) == (*e->v2))) if (e->collapse()) nc++; + FOREACHEDGE(e, n) if (e->isLinked() && ((*e->v1) == (*e->v2))) + { + if (e->t1) unlinkTriangle(e->t1); + if (e->t2) unlinkTriangle(e->t2); + } + removeUnlinkedElements(); + + int degn = 0; + FOREACHTRIANGLE(t, n) if (t->isExactlyDegenerate()) degn++; + if (degn) + { + TMesh::warning("removeDegenerateTriangles() - This should not happen!\n"); + FOREACHTRIANGLE(t, n) if (t->isExactlyDegenerate()) MARK_VISIT(t); else UNMARK_VISIT(t); + } + + return (nc)*((degn) ? (-1) : (1)); +} + +//int Basic_TMesh::removeDegenerateTriangles() +//{ +// Node *n; +// Triangle *t; +// Edge *e; +// +// // Split caps +// E.sort(&edgeCompare); +// Vertex *ov1, *ov2; +// int nov; +// bool done; +// +// do +// { +// done = false; +// FOREACHEDGE(e, n) +// { +// nov = 0; +// if (e->t1 != NULL && Point::pointInInnerSegment((ov1 = e->t1->oppositeVertex(e)), e->v1, e->v2)) nov++; +// if (e->t2 != NULL && Point::pointInInnerSegment((ov2 = e->t2->oppositeVertex(e)), e->v1, e->v2)) nov += 2; +// if (nov == 3 && ov1->squaredDistance(e->v1) < ov2->squaredDistance(e->v1)) { splitEdge(e, ov2); splitEdge(e, ov1); } +// else if (nov == 3 && ov2->squaredDistance(e->v1) < ov1->squaredDistance(e->v1)) { splitEdge(e, ov1); splitEdge(e, ov2); } +// else if (nov >= 2) splitEdge(e, ov2); +// else if (nov == 1) splitEdge(e, ov1); +// if (nov) done = true; +// } +// } while (done); +// +// //FOREACHEDGE(e, n) multiSplitEdge(this, e); +// +// int nc = 0; // Num of collapses to remove needles +// +// // Remove needles +// FOREACHEDGE(e, n) if (e->isLinked() && ((*e->v1) == (*e->v2))) if (e->collapse()) nc++; +// +// FOREACHEDGE(e, n) if (e->isLinked() && ((*e->v1) == (*e->v2))) +// { +// if (e->t1) unlinkTriangle(e->t1); +// if (e->t2) unlinkTriangle(e->t2); +// } +// removeUnlinkedElements(); +// +// int degn = 0; +// FOREACHTRIANGLE(t, n) if (t->isExactlyDegenerate()) degn++; +// if (degn) +// { +// FOREACHTRIANGLE(t, n) if (t->isExactlyDegenerate()) MARK_VISIT(t); else UNMARK_VISIT(t); +// } +// +// return (nc)*((degn) ? (-1) : (1)); +//} + +//int Basic_TMesh::removeDegenerateTriangles() +//{ +// Edge *e; +// Node *n; +// int nc = 0; // Num of collapses to remove needles +// int ns = 0; // Num of swaps to remove internal caps +// int nr = 0; // Num of removals to remove boundary caps +// +// // Remove needles +// FOREACHEDGE(e, n) if (e->isLinked() && ((*e->v1) == (*e->v2))) if (e->collapse()) nc++; +// +// // Swap or remove caps +// Point p, w1, w2; +// FOREACHEDGE(e, n) if (e->isLinked() && ((*e->v1) != (*e->v2))) +// { +// if (e->t1 != NULL) +// { +// p.setValue(e->t1->oppositeVertex(e)); +// w1 = p - (*e->v1); +// w2 = p - (*e->v2); +// if (!w1.isNull() && !w2.isNull() && (w1*w2 < 0) && e->t1->isExactlyDegenerate()) +// { +// if (e->t2 != NULL) { if (e->swap()) { ns++; continue; } } +// else { unlinkTriangle(e->t1); nr++; continue; } +// } +// } +// if (e->t2 != NULL) +// { +// p.setValue(e->t2->oppositeVertex(e)); +// w1 = p - (*e->v1); +// w2 = p - (*e->v2); +// if (!w1.isNull() && !w2.isNull() && (w1*w2 < 0) && e->t2->isExactlyDegenerate()) +// { +// if (e->t1 != NULL) { if (e->swap()) { ns++; continue; } } +// else { unlinkTriangle(e->t2); nr++; continue; } +// } +// } +// } +// +// // Remove possible newly-introduced needles +// FOREACHEDGE(e, n) if (e->isLinked() && ((*e->v1) == (*e->v2))) if (e->collapse()) nc++; +// +// removeUnlinkedElements(); +// +// Triangle *t; +// int degn = 0; +// FOREACHTRIANGLE(t, n) if (t->isExactlyDegenerate()) degn++; +// if (degn) +// { +// FOREACHTRIANGLE(t, n) if (t->isExactlyDegenerate()) MARK_VISIT(t); else UNMARK_VISIT(t); +// } +// +// return (nc + ns + nr)*((degn) ? (-1) : (1)); +//} + +//int Basic_TMesh::removeDegenerateTriangles() +//{ +// Triangle *t; +// Node *n; +// Edge *e; +// int degn = 0, tcs = 0; +// +// if (TMesh::acos_tolerance > 0.0) +// { +// int collapses, swaps; +// List todo, *vt; +// +// do +// { +// collapses = swaps = 0; +// +// FOREACHTRIANGLE(t, n) if (t->isDegenerate()) +// {MARK_BIT(t, 5); todo.appendHead(t);} +// else UNMARK_BIT(t, 5); +// +// while (todo.numels()) +// { +// t = (Triangle *)todo.popHead(); +// UNMARK_BIT(t, 5); +// if (t->isLinked()) +// { +// if ((e = t->isCap()) != NULL) +// { +// if (e->isOnBoundary()) {unlinkTriangle(t); collapses++;} +// else if (e->swap()) +// { +// if (e->t1->overlaps() || e->t2->overlaps() || e->t1->isCap() || e->t2->isCap()) e->swap(1); +// else +// { +// swaps++; +// if (!IS_BIT(e->t1, 5)) { MARK_BIT(e->t1, 5); todo.appendTail(e->t1); } +// if (!IS_BIT(e->t2, 5)) { MARK_BIT(e->t2, 5); todo.appendTail(e->t2); } +// } +// } +// } +// else if ((e = t->isNeedle()) != NULL) +// { +// vt = e->v2->VT(); +// if (e->collapse()) +// { +// collapses++; +// FOREACHVTTRIANGLE(vt, t, n) +// if (t->isLinked() && !IS_BIT(t, 5)) { MARK_BIT(t, 5); todo.appendTail(t); } +// } +// else if (!e->isOnBoundary() && e->oppositeTriangle(t)->oppositeVertex(e)->valence() == 3) +// { +// if (e->oppositeTriangle(t)->nextEdge(e)->collapse()) +// { +// MARK_BIT(t, 5); todo.appendHead(t); +// t = e->oppositeTriangle(t); +// if (t && !IS_BIT(t, 5)) { MARK_BIT(t, 5); todo.appendTail(t); } +// collapses++; +// } +// } +// delete(vt); +// } +// } +// } +// +// if (collapses) removeUnlinkedElements(); +// tcs += collapses; tcs += swaps; +// } while (collapses+swaps); +// +// FOREACHTRIANGLE(t, n) if (t->isDegenerate()) degn++; +// +// if (degn) +// { +// FOREACHTRIANGLE(t, n) if (t->isDegenerate()) MARK_VISIT(t); else UNMARK_VISIT(t); +// } +// } +// else /// This uses exact arithmetics to deduce that triangles are degenerate +// { +// List triangles; +// const int MAX_ATTEMPTS = 10; +// +// FOREACHTRIANGLE(t, n) t->info=0; +// +// // BIT5 means that the triangle is in the list +// FOREACHTRIANGLE(t, n) +// { +// if (t->isExactlyDegenerate()) {triangles.appendTail(t); MARK_BIT(t, 5);} +// else UNMARK_BIT(t, 5); +// } +// +// while ((t=(Triangle *)triangles.popHead())!=NULL) +// { +// UNMARK_BIT(t, 5); +// if (t->isLinked()) +// { +// if (t->e1->isDegenerate()) {t->e1->collapse(); tcs++;} +// else if (t->e2->isDegenerate()) {t->e2->collapse(); tcs++;} +// else if (t->e3->isDegenerate()) {t->e3->collapse(); tcs++;} +// else if ((e=t->getLongestEdge())!=NULL) +// { +// if (e->swap()) +// { +// tcs++; +// t=e->t1; +// if (t->isExactlyDegenerate() && !IS_BIT(t, 5) && ((int)t->info < MAX_ATTEMPTS)) +// {triangles.appendTail(t); MARK_BIT(t, 5); t->info = (void *)(((int)t->info)+1);} +// t=e->t2; +// if (t->isExactlyDegenerate() && !IS_BIT(t, 5) && ((int)t->info < MAX_ATTEMPTS)) +// {triangles.appendTail(t); MARK_BIT(t, 5); t->info = (void *)(((int)t->info)+1);} +// } +// } +// } +// } +// +// removeUnlinkedElements(); +// +// FOREACHTRIANGLE(t, n) if (t->isExactlyDegenerate()) degn++; +// if (degn) +// { +// FOREACHTRIANGLE(t, n) if (t->isExactlyDegenerate()) MARK_VISIT(t); else UNMARK_VISIT(t); +// } +// } +// +// if (degn) tcs=-tcs; +// if (tcs<0) TMesh::info("removeDegenerateTriangles: %d degeneracies could not be removed and have been selected\n",degn); +// return tcs; +//} + + +bool Basic_TMesh::strongDegeneracyRemoval(int max_iters) +{ + int n, iter_count = 0; + bool qstatus = TMesh::quiet; + + TMesh::info("Removing degeneracies...\n"); + while ((++iter_count) <= max_iters && removeDegenerateTriangles()<0) + { + for (n=1; n max_iters) return false; + return true; +} + +//// If the mesh is made of more than one connected component //// +//// keep only the biggest one and remove all the others. //// + +int Basic_TMesh::removeSmallestComponents() +{ + Node *n,*m; + List todo; + List components; + List *component, *biggest = NULL; + Triangle *t, *t1, *t2, *t3; + int nt = 0, gnt = 0; + + if (T.numels() == 0) return 0; + + FOREACHTRIANGLE(t, n) UNMARK_BIT(t, 5); + + t = ((Triangle *)T.head()->data); + n = T.head(); + do + { + component = new List; + components.appendHead(component); + todo.appendHead(t); + while (todo.numels()) + { + t = (Triangle *)todo.head()->data; + todo.removeCell(todo.head()); + if (!IS_BIT(t, 5)) + { + t1 = t->t1(); + t2 = t->t2(); + t3 = t->t3(); + + if (t1 != NULL && !IS_BIT(t1, 5)) todo.appendHead(t1); + if (t2 != NULL && !IS_BIT(t2, 5)) todo.appendHead(t2); + if (t3 != NULL && !IS_BIT(t3, 5)) todo.appendHead(t3); + + MARK_BIT(t, 5); + component->appendTail(t); + } + } + todo.removeNodes(); + for (; n != NULL; n=n->next()) {t = ((Triangle *)n->data); if (!IS_BIT(t, 5)) break;} + } + while (n != NULL); + + int num_comps = components.numels(); + + FOREACHNODE(components, n) + if ((nt = ((List *)n->data)->numels()) > gnt) {gnt=nt; biggest = (List *)n->data;} + + FOREACHTRIANGLE(t, n) UNMARK_BIT(t, 5); + + nt = 0; + FOREACHNODE(components, n) + if (((List *)n->data) != biggest) + FOREACHVTTRIANGLE(((List *)n->data), t, m) + { + if (t->e1->v1 != NULL) t->e1->v1->e0 = NULL; + if (t->e1->v2 != NULL) t->e1->v2->e0 = NULL; + if (t->e2->v1 != NULL) t->e2->v1->e0 = NULL; + if (t->e2->v2 != NULL) t->e2->v2->e0 = NULL; + if (t->e3->v1 != NULL) t->e3->v1->e0 = NULL; + if (t->e3->v2 != NULL) t->e3->v2->e0 = NULL; + t->e1->v1 = t->e1->v2 = t->e2->v1 = t->e2->v2 = t->e3->v1 = t->e3->v2 = NULL; + t->e1 = t->e2 = t->e3 = NULL; + nt++; + } + + FOREACHNODE(components, n) delete((List *)n->data); + + if (nt) + { + d_boundaries = d_handles = d_shells = 1; + removeUnlinkedElements(); + return num_comps-1; + } + + return 0; +} + + +//// Remove components whose area is < eps_area + +int Basic_TMesh::removeSmallestComponents(double eps_area) +{ + Node *n; + List todo, component; + Triangle *t, *s; + int rem_comps=0; + double pa; + + if (T.numels() == 0) return 0; + + FOREACHTRIANGLE(t, n) UNMARK_BIT(t, 5); + t = ((Triangle *)T.head()->data); + n = T.head(); + do + { + todo.appendTail(t); MARK_BIT(t, 5); + pa = 0.0; + while ((t=(Triangle *)todo.popHead())!=NULL) + { + s = t->t1(); if (s != NULL && !IS_BIT(s, 5)) { todo.appendTail(s); MARK_BIT(s, 5); } + s = t->t2(); if (s != NULL && !IS_BIT(s, 5)) { todo.appendTail(s); MARK_BIT(s, 5); } + s = t->t3(); if (s != NULL && !IS_BIT(s, 5)) { todo.appendTail(s); MARK_BIT(s, 5); } + component.appendTail(t); + pa += t->area(); + } + if (pa < eps_area) { rem_comps++; while ((t = (Triangle *)component.popHead()) != NULL) unlinkTriangle(t); } + else component.removeNodes(); + + for (; n != NULL; n = n->next()) { t = ((Triangle *)n->data); if (!IS_BIT(t, 5)) break; } + } while (n != NULL); + + FOREACHTRIANGLE(t, n) UNMARK_BIT(t, 5); + + if (rem_comps) + { + d_boundaries = d_handles = d_shells = 1; + removeUnlinkedElements(); + } + + return rem_comps; +} + + +//// Traverses the triangulation and inverts normals in order //// +//// to make the adjacences consistent. //// +//// returns: +//// 0 = mesh was oriented, nothing done +//// 1 = some triangles were flipped to achieve orientation +//// >1 = mesh was not orientable. Cuts were necessary + +int Basic_TMesh::forceNormalConsistence() +{ + int ret = 0; + Node *n; + Triangle *t; + + FOREACHTRIANGLE(t, n) if (!IS_BIT(t, 5)) + ret |= forceNormalConsistence(t); + FOREACHTRIANGLE(t, n) UNMARK_BIT(t, 5); + return ret; +} + +int Basic_TMesh::forceNormalConsistence(Triangle *t0) +{ + Node *n; + Edge *e; + List todo, elist; + Triangle *t, *t1, *t2, *t3; + int tmp1, tmp2, r=0, wrn = 0, isclosed = 1; + + todo.appendHead(t0); + + while (todo.numels()) + { + t = (Triangle *)todo.head()->data; + todo.removeCell(todo.head()); + if (!IS_BIT(t, 5)) + { + t1 = t->t1(); t2 = t->t2(); t3 = t->t3(); + if (!IS_BIT(t->e1, 5)) { MARK_BIT(t->e1, 5); elist.appendHead(t->e1); } + if (!IS_BIT(t->e2, 5)) { MARK_BIT(t->e2, 5); elist.appendHead(t->e2); } + if (!IS_BIT(t->e3, 5)) { MARK_BIT(t->e3, 5); elist.appendHead(t->e3); } + + if (t1 != NULL && !IS_BIT(t1, 5)) { todo.appendHead(t1); if (!t->checkAdjNor(t1)) { t1->invert(); r = 1; } } + if (t2 != NULL && !IS_BIT(t2, 5)) { todo.appendHead(t2); if (!t->checkAdjNor(t2)) { t2->invert(); r = 1; } } + if (t3 != NULL && !IS_BIT(t3, 5)) { todo.appendHead(t3); if (!t->checkAdjNor(t3)) { t3->invert(); r = 1; } } + + MARK_BIT(t, 5); + } + } + + FOREACHVEEDGE((&(elist)), e, n) + { + UNMARK_BIT(e, 5); + if (isclosed && e->isOnBoundary()) isclosed = 0; + tmp1 = (e->t1 != NULL)?((e->commonVertex(e->t1->nextEdge(e)) == e->v1)?(-1):(1)):(0); + tmp2 = (e->t2 != NULL)?((e->commonVertex(e->t2->nextEdge(e)) == e->v2)?(-1):(1)):(0); + + if (tmp1*tmp2 < 0) + { + wrn++; + if (tmp1 == -1) p_swap((void **)(&(e->v1)), (void **)(&(e->v2))); + Edge *ne = newEdge(e->v2, e->v1); + E.appendHead(ne); + e->t2->replaceEdge(e, ne); + ne->t2 = e->t2; e->t2 = NULL; + } else if (tmp1 == -1 || tmp2 == -1) p_swap((void **)(&(e->v1)), (void **)(&(e->v2))); + } + + if (wrn) + { + d_boundaries = d_handles = d_shells = 1; + TMesh::warning("forceNormalConsistence: Basic_TMesh was not orientable. Cut performed.\n"); + } + + // Though useful in some easy cases, the flip below destroys the orientation + // when it is set on purpose (e.g. for internal cavities) + + //if (isclosed) + //{ + // t = topTriangle(t0); + // if (t->getNormal().z < 0) {flipNormals(t0); r=1;} + //} + + if (wrn) r |= 2; + + return r; +} + + +// If possible, swap edges to remove overlaps. When it is not +// enough, remove the overlapping triangles from the mesh. +// return the number of triangles that was necessary to remove. + +int Basic_TMesh::removeOverlappingTriangles() +{ + Node *n; + Edge *e; + List oved; + + FOREACHEDGE(e, n) if (e->overlaps()) oved.appendHead(e); + oved.sort(edgeCompare); + + for (n=oved.tail(); n!=NULL; n=n->prev()) + { + e = (Edge *)n->data; + if (e->overlaps() && e->swap()) + { + if (e->t1->isExactlyDegenerate() || e->t2->isExactlyDegenerate()) {e->swap(1); continue;} + if (e->t1->nextEdge(e)->overlaps()) { e->swap(1); continue; } + if (e->t1->prevEdge(e)->overlaps()) { e->swap(1); continue; } + if (e->t2->nextEdge(e)->overlaps()) { e->swap(1); continue; } + if (e->t2->prevEdge(e)->overlaps()) { e->swap(1); continue; } + } + } + + int nr = 0; + for (n=oved.tail(); n!=NULL; n=n->prev()) + { + e = (Edge *)n->data; + if (e->overlaps()) { unlinkTriangle(e->t1); unlinkTriangle(e->t2); nr++; } + } + if (nr) + { + removeUnlinkedElements(); + d_boundaries = d_handles = d_shells = 1; + } + + return nr*2; +} + + +bool Basic_TMesh::meshclean(int max_iters, int inner_loops) +{ + bool ni, nd; + Triangle *t; + Node *m; + + deselectTriangles(); + invertSelection(); + + for (int n=0; nisExactlyDegenerate()) ni=false; + if (ni) return true; + } + } + + return false; +} + +} //namespace T_MESH diff --git a/src/mesh_fix/src/Algorithms/detectIntersections.cpp b/src/mesh_fix/src/Algorithms/detectIntersections.cpp new file mode 100644 index 000000000..24444997f --- /dev/null +++ b/src/mesh_fix/src/Algorithms/detectIntersections.cpp @@ -0,0 +1,395 @@ +/**************************************************************************** +* TMesh * +* * +* Consiglio Nazionale delle Ricerche * +* Istituto di Matematica Applicata e Tecnologie Informatiche * +* Sezione di Genova * +* IMATI-GE / CNR * +* * +* Authors: Marco Attene * +* Copyright(C) 2013: IMATI-GE / CNR * +* All rights reserved. * +* * +* This program is dual-licensed as follows: * +* * +* (1) You may use TMesh as 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 3 of the License, or * +* (at your option) any later version. * +* In this case the 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 (http://www.gnu.org/licenses/gpl.txt) * +* for more details. * +* * +* (2) You may use TMesh as part of a commercial software. In this case a * +* proper agreement must be reached with the Authors and with IMATI-GE/CNR * +* based on a proper licensing contract. * +* * +****************************************************************************/ + +#include "detectIntersections.h" +#include +#include +#include "jqsort.h" + +namespace T_MESH +{ + di_cell::di_cell(Basic_TMesh *tin, bool useAll) + { + Node *n; + Vertex *v; + Triangle *t; + Mp.x = -DBL_MAX, mp.x = DBL_MAX; + Mp.y = -DBL_MAX, mp.y = DBL_MAX; + Mp.z = -DBL_MAX, mp.z = DBL_MAX; + FOREACHVVVERTEX((&(tin->V)), v, n) if (useAll || IS_BIT(v,5)) + { + if (v->x < mp.x) mp.x = v->x; + if (v->x > Mp.x) Mp.x = v->x; + if (v->y < mp.y) mp.y = v->y; + if (v->y > Mp.y) Mp.y = v->y; + if (v->z < mp.z) mp.z = v->z; + if (v->z > Mp.z) Mp.z = v->z; + } + + mp -= DI_EPSILON_POINT; + Mp += DI_EPSILON_POINT; + + FOREACHVTTRIANGLE((&(tin->T)), t, n) if (useAll || IS_VISITED(t)) triangles.appendTail(t); + } + + bool di_cell::is_triangleBB_in_cell(Triangle *t) const + { + Vertex *v1 = t->v1(), *v2 = t->v2(), *v3 = t->v3(); + coord mx = MIN(v1->x, MIN(v2->x, v3->x)); + coord Mx = MAX(v1->x, MAX(v2->x, v3->x)); + coord my = MIN(v1->y, MIN(v2->y, v3->y)); + coord My = MAX(v1->y, MAX(v2->y, v3->y)); + coord mz = MIN(v1->z, MIN(v2->z, v3->z)); + coord Mz = MAX(v1->z, MAX(v2->z, v3->z)); + + // Triangle BB is not entirely out of cell + if (!(MxMp.x || MyMp.y || MzMp.z)) return true; + else return false; + } + + di_cell *di_cell::fork() + { + Node *n; + Triangle *t; + Point e = Mp - mp; + di_cell *nc = new di_cell; + char which_coord = 2; + + if (e.x >= e.y && e.x >= e.z) which_coord = 0; + else if (e.y >= e.x && e.y >= e.z) which_coord = 1; + nc->mp = mp; nc->Mp = Mp; + nc->Mp[which_coord] -= (e[which_coord] / 2); mp[which_coord] = nc->Mp[which_coord]; + + n = triangles.head(); + while (n != NULL) + { + t = (Triangle *)n->data; + n = n->next(); + if (!is_triangleBB_in_cell(t)) + { + triangles.moveNodeTo((n != NULL) ? (n->prev()) : triangles.tail(), &(nc->triangles)); + } + else if (nc->is_triangleBB_in_cell(t)) nc->triangles.appendHead(t); + } + + return nc; + } + + + // Brute force all-with-all intersection test of the triangles in 'triangles'. + void di_cell::selectIntersections(bool justproper) + { + Triangle *t, *y; + Node *n, *m; + List *ts; + + for (n = triangles.head(); n != NULL; n = n->next()) + for (m = n->next(); m != NULL; m = m->next()) + { + t = (Triangle *)n->data; + y = (Triangle *)m->data; // For any pair (t,y) of triangles in the cell + // The same triangle pair can be in different cells. The following avoids redoing the check. + if (t->info == NULL || y->info == NULL || (((List *)t->info)->containsNode(y) == NULL)) + { + if (t->intersects(y, justproper)) + { + MARK_VISIT(t); MARK_VISIT(y); + ts = ((t->info != NULL) ? ((List *)t->info) : (new List)); ts->appendTail(y); t->info = ts; + ts = ((y->info != NULL) ? ((List *)y->info) : (new List)); ts->appendTail(t); y->info = ts; + } + } + } + } + + +///////////////////////////////////////////////////////////////////////// +// || +////////////////////// Select Intersections /////////////////////////// +// || +///////////////////////////////////////////////////////////////////////// + +int Basic_TMesh::selectIntersectingTriangles(UINT16 tris_per_cell, bool justproper) +{ + Triangle *t; + Vertex *v; + Node *n; + bool isSelection=0; + List *selT = new List, *selV = new List; + + TMesh::begin_progress(); + TMesh::report_progress(NULL); + + FOREACHTRIANGLE(t, n) if (IS_VISITED(t)) + { + isSelection=1; + selT->appendTail(t); + v=t->v1(); if (!IS_BIT(v,5)) {MARK_BIT(v,5); selV->appendTail(v);} + v=t->v2(); if (!IS_BIT(v,5)) {MARK_BIT(v,5); selV->appendTail(v);} + v=t->v3(); if (!IS_BIT(v,5)) {MARK_BIT(v,5); selV->appendTail(v);} + } + TMesh::report_progress(NULL); + + if (!isSelection) {delete(selT); delete(selV); selT=&T; selV=&V;} + + di_cell *c2, *c = new di_cell(this, !isSelection); + List cells, todo(c); + int i=0; + + while ((c = (di_cell *)todo.popHead()) != NULL) + { + if (i>DI_MAX_NUMBER_OF_CELLS || c->triangles.numels() <= tris_per_cell) cells.appendHead(c); + else + { + if (!(i % 1000)) TMesh::report_progress(NULL); + i++; + c2 = c->fork(); + todo.appendTail(c); + todo.appendTail(c2); + } + } + + // Deselect everything and select only intersecting triangles + deselectTriangles(); + FOREACHTRIANGLE(t, n) t->info = NULL; + i=0; FOREACHNODE(cells, n) + { + (((di_cell *)n->data)->selectIntersections(justproper)); + if (!(i % 100)) TMesh::report_progress("%d %% done ", ((i)* 100) / cells.numels()); + i++; + } + TMesh::end_progress(); + + // Dispose memory allocated for cells + FOREACHVTTRIANGLE(selT, t, n) { if (t->info!=NULL) delete((List *)t->info); t->info = NULL; } + while (cells.numels()) delete((di_cell *)cells.popHead()); + + // Count selected triangles for final report and delete stored normals + int its=0; + FOREACHVTTRIANGLE(selT, t, n) { if (IS_VISITED(t)) its++;} + + if (its) TMesh::info("%d intersecting triangles have been selected.\n",its); + else TMesh::info("No intersections detected.\n"); + + FOREACHVVVERTEX(selV, v, n) UNMARK_BIT(v,5); + if (isSelection) {delete(selT); delete(selV);} + + return its; +} + +void jitterIncrease(char *f) +{ + bool isnegative = (f[0] == '-'); + int l = strlen(f); + + if (isnegative) + { + for (int i = l - 1; i >= 1; i--) + if (f[i] == '0') f[i] = '9'; + else if (f[i] == '.') continue; + else { f[i]--; break; } + } else + { + for (int i = l - 1; i >= 0; i--) + if (f[i] == '9') f[i] = '0'; + else if (f[i] == '.') continue; + else { f[i]++; break; } + } +} + +void jitterDecrease(char *f) +{ + bool isnegative = (f[0] == '-'); + int l = strlen(f); + + if (isnegative) + { + for (int i = l - 1; i >= 1; i--) + if (f[i] == '9') f[i] = '0'; + else if (f[i] == '.') continue; + else { f[i]++; break; } + } else + { + for (int i = l - 1; i >= 0; i--) + if (f[i] == '0') f[i] = '9'; + else if (f[i] == '.') continue; + else { f[i]--; break; } + } +} + +void jitterCoordinate(coord& c, int j) +{ + char floatver[32]; + float x; + + sprintf(floatver, "%f", TMESH_TO_FLOAT(c)); + if (j > 0) jitterIncrease(floatver); + else if (j<0) jitterDecrease(floatver); + sscanf(floatver, "%f", &x); c = x; +} + +bool Basic_TMesh::safeCoordBackApproximation() +{ + Node *n; + Vertex *v; + + deselectTriangles(); + + FOREACHVERTEX(v, n) + { + jitterCoordinate(v->x, 0); + jitterCoordinate(v->y, 0); + jitterCoordinate(v->z, 0); + } + + Edge *e; + Vertex *ov1, *ov2; + + int pnos = 0, nos; + nos = 0; FOREACHEDGE(e, n) if (e->overlaps()) nos++; + + do + { + pnos = nos; + FOREACHEDGE(e, n) if (e->overlaps()) + { + ov1 = e->t1->oppositeVertex(e); + ov2 = e->t2->oppositeVertex(e); + v = (Point::squaredTriangleArea3D(e->v1, e->v2, ov1) < Point::squaredTriangleArea3D(e->v1, e->v2, ov2)) ? (ov1) : (ov2); + for (int a = -1; a <= 1; a++) for (int b = -1; b <= 1; b++) for (int c = -1; c <= 1; c++) + { + jitterCoordinate(v->x, a); jitterCoordinate(v->y, b); jitterCoordinate(v->z, c); + if (e->overlaps()) + { + jitterCoordinate(v->x, -a); jitterCoordinate(v->y, -b); jitterCoordinate(v->z, -c); + } else a = b = c = 2; + } + } + nos = 0; FOREACHEDGE(e, n) if (e->overlaps()) nos++; + } while (nos < pnos); + +// if (nos) TMesh::warning("%d overlaps could not be removed.\n", nos); + return (nos == 0); +} + + + + + + + + +bool remints_appendCubeToList(Triangle *t0, List& l) +{ + if (!IS_VISITED(t0) || IS_BIT(t0, 6)) return false; + + Triangle *t, *s; + Vertex *v; + List triList(t0); + MARK_BIT(t0,6); + coord minx = DBL_MAX, maxx = -DBL_MAX, miny = DBL_MAX, maxy = -DBL_MAX, minz = DBL_MAX, maxz = -DBL_MAX; + + while(triList.numels()) + { + t = (Triangle *)triList.popHead(); + v = t->v1(); + minx=MIN(minx,v->x); miny=MIN(miny,v->y); minz=MIN(minz,v->z); + maxx=MAX(maxx,v->x); maxy=MAX(maxy,v->y); maxz=MAX(maxz,v->z); + v = t->v2(); + minx=MIN(minx,v->x); miny=MIN(miny,v->y); minz=MIN(minz,v->z); + maxx=MAX(maxx,v->x); maxy=MAX(maxy,v->y); maxz=MAX(maxz,v->z); + v = t->v3(); + minx=MIN(minx,v->x); miny=MIN(miny,v->y); minz=MIN(minz,v->z); + maxx=MAX(maxx,v->x); maxy=MAX(maxy,v->y); maxz=MAX(maxz,v->z); + if ((s = t->t1()) != NULL && !IS_BIT(s, 6) && IS_VISITED(s)) {triList.appendHead(s); MARK_BIT(s,6);} + if ((s = t->t2()) != NULL && !IS_BIT(s, 6) && IS_VISITED(s)) {triList.appendHead(s); MARK_BIT(s,6);} + if ((s = t->t3()) != NULL && !IS_BIT(s, 6) && IS_VISITED(s)) {triList.appendHead(s); MARK_BIT(s,6);} + } + + l.appendTail(new Point(minx, miny, minz)); + l.appendTail(new Point(maxx, maxy, maxz)); + return true; +} + +bool remints_isVertexInCube(Vertex *v, List& loc) +{ + Node *n; + Point *p1, *p2; + FOREACHNODE(loc, n) + { + p1 = (Point *)n->data; n=n->next(); p2 = (Point *)n->data; + if (!(v->x < p1->x || v->y < p1->y || v->z < p1->z || + v->x > p2->x || v->y > p2->y || v->z > p2->z)) return true; + } + + return false; +} + +void remints_selectTrianglesInCubes(Basic_TMesh *tin) +{ + Triangle *t; + Vertex *v; + Node *n; + List loc; + FOREACHVTTRIANGLE((&(tin->T)), t, n) remints_appendCubeToList(t, loc); + FOREACHVVVERTEX((&(tin->V)), v, n) if (remints_isVertexInCube(v, loc)) MARK_BIT(v, 5); + FOREACHVTTRIANGLE((&(tin->T)), t, n) + { + UNMARK_BIT(t, 6); + if (IS_BIT(t->v1(), 5) || IS_BIT(t->v2(), 5) || IS_BIT(t->v3(), 5)) MARK_VISIT(t); + } + FOREACHVVVERTEX((&(tin->V)), v, n) UNMARK_BIT(v, 5); + loc.freeNodes(); +} + + +// returns true on success + +bool Basic_TMesh::strongIntersectionRemoval(int max_iters) +{ + int n, iter_count = 0; + bool qstatus = TMesh::quiet; + + TMesh::info("Removing self-intersections...\n"); + + while ((++iter_count) <= max_iters && selectIntersectingTriangles()) + { + for (n=1; n max_iters) return false; + return true; +} + +} //namespace T_MESH diff --git a/src/mesh_fix/src/Algorithms/holeFilling.cpp b/src/mesh_fix/src/Algorithms/holeFilling.cpp new file mode 100644 index 000000000..793d45c95 --- /dev/null +++ b/src/mesh_fix/src/Algorithms/holeFilling.cpp @@ -0,0 +1,797 @@ +/**************************************************************************** +* TMesh * +* * +* Consiglio Nazionale delle Ricerche * +* Istituto di Matematica Applicata e Tecnologie Informatiche * +* Sezione di Genova * +* IMATI-GE / CNR * +* * +* Authors: Marco Attene * +* Copyright(C) 2013: IMATI-GE / CNR * +* All rights reserved. * +* * +* This program is dual-licensed as follows: * +* * +* (1) You may use TMesh as 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 3 of the License, or * +* (at your option) any later version. * +* In this case the 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 (http://www.gnu.org/licenses/gpl.txt) * +* for more details. * +* * +* (2) You may use TMesh as part of a commercial software. In this case a * +* proper agreement must be reached with the Authors and with IMATI-GE/CNR * +* based on a proper licensing contract. * +* * +****************************************************************************/ + +#include "tin.h" +#include + +namespace T_MESH +{ + +////////////////////////////////////////////////////////////////// +// // +// T R I A N G U L A T I O N M E T H O D S // +// // +////////////////////////////////////////////////////////////////// + + +/////// Computes the center of mass of the hole and star-patches //////// + +int Basic_TMesh::StarTriangulateHole(Edge *e) +{ + if (!e->isOnBoundary()) return 0; + + List bvs; + Node *n; + Edge *e1, *e2, *e3; + Point np; + Vertex *v, *nv, *v1, *v2; + int nt=0; + + v = e->v1; + + do + { + bvs.appendHead(v); + v = v->nextOnBoundary(); + //MARK_BIT(v,5); // < AMF_ADD - since IMAT-STL 2.4-1 > + } while (v != e->v1); + + FOREACHVVVERTEX((&(bvs)), v, n) np = np+(*v); + + np = np/bvs.numels(); + nv = newVertex(&np); + V.appendHead(nv); + + v1 = ((Vertex *)bvs.head()->data); + Edge *ep = v1->e0; // AMF_ADD - since IMAT-STL 2.4-2 + e1 = CreateEdge(nv, v1); + v1->e0 = ep; // AMF_ADD - since IMAT-STL 2.4-2 + for (n=bvs.head()->next(); n!=NULL; n=n->next()) + { + v2 = ((Vertex *)n->data); + e2 = CreateEdge(nv, v2); + e3 = v1->getEdge(v2); + CreateTriangle(e1, e2, e3); + nt++; + v1 = v2; + e1 = e2; + } + v2 = ((Vertex *)bvs.head()->data); + e2 = nv->getEdge(v2); + e3 = v1->getEdge(v2); + CreateTriangle(e1, e2, e3); + nt++; + + return nt; +} + + +///// Patch holes using 2D Delaunay triangulation on the plane 'nor' ///// + +int Basic_TMesh::TriangulateHole(Edge *e, Point *nor) +{ + if (!e->isOnBoundary()) return 0; + + List bvs; + Node *n, *gn = NULL; + Edge *e1, *e2; + Vertex *v, *v1, *v2; + double ang, gang; + int nt = 0; + + v = e->v1; + do + { + bvs.appendHead(v); + v = v->nextOnBoundary(); + } while (v != e->v1); + + while (bvs.numels() > 2) + { + gang = DBL_MAX; + FOREACHVVVERTEX((&(bvs)), v, n) + if (!IS_BIT(v, 5) && v->e0 && (ang = v->getAngleOnAveragePlane(nor)) < gang) + {gang = ang; gn = n;} + if (gang == DBL_MAX) + { + TMesh::warning("TriangulateHole: Can't complete the triangulation.\n"); + FOREACHVVVERTEX((&(bvs)), v, n) UNMARK_BIT(v, 5); + return 0; + } + v = ((Vertex *)gn->data); + v1 = (Vertex *)((gn->next() != NULL)?(gn->next()):(bvs.head()))->data; + v2 = (Vertex *)((gn->prev() != NULL)?(gn->prev()):(bvs.tail()))->data; + e1 = v->getEdge(v1); + e2 = v->getEdge(v2); + if (!EulerEdgeTriangle(e1, e2)) MARK_BIT(v, 5); + else { bvs.removeCell(gn); UNMARK_BIT(v1, 5); UNMARK_BIT(v2, 5); nt++; } + } + + int i, skips; + do + { + skips = 0; + for (n=E.head(), i=2*nt*nt; inext(), i--) + { + e = ((Edge *)n->data); + ang = e->delaunayMinAngle(); + if (e->swap()) + {if (e->delaunayMinAngle() <= ang) e->swap(1); else skips++;} + } + if (i < 0) {TMesh::warning("Optimization is taking too long. I give up.\n"); break;} + } while (skips); + + return nt; +} + + +///// Triangulates a hole using the additional vertices in 'vl' ///// + +int Basic_TMesh::TriangulateHole(Edge *e, List *vl) +{ + if (!e->isOnBoundary()) return 0; + + List bvs, ovbs, nedg; + Node *n, *gn = NULL; + Edge *e1, *e2; + Vertex *v, *v1, *v2; + double ang, gang; + int nt = 0, neb; + + v = e->v1; + + do + { + bvs.appendHead(v); + v = v->nextOnBoundary(); + } while (v != e->v1); + ovbs.appendList(&bvs); + + while (bvs.numels() > 2) // While there are more than two boundary vertices + { + gang = DBL_MAX; + FOREACHVVVERTEX((&(bvs)), v, n) + if (!IS_BIT(v, 5) && v->e0 && (ang = v->getAngleForTriangulation()) < gang) + {gang = ang; gn = n;} + if (gang == DBL_MAX) + { + TMesh::warning("TriangulateHole: Can't complete the triangulation.\n"); + FOREACHVVVERTEX((&(bvs)), v, n) UNMARK_BIT(v, 5); + return 0; + } + v = ((Vertex *)gn->data); + v1 = (Vertex *)((gn->next() != NULL)?(gn->next()):(bvs.head()))->data; + v2 = (Vertex *)((gn->prev() != NULL)?(gn->prev()):(bvs.tail()))->data; + e1 = v->getEdge(v1); + e2 = v->getEdge(v2); + neb = E.numels(); + if (!EulerEdgeTriangle(e1, e2)) MARK_BIT(v, 5); + else + { + bvs.removeCell(gn); + UNMARK_BIT(v1, 5); + UNMARK_BIT(v2, 5); + nt++; + if (E.numels() > neb) nedg.appendHead(E.head()->data); + } + } + +// if (nt < 2) return nt; + + // Calcolo una normale per il buco come media dei nuovi triangoli + int i; + Point nor; + + for (i=0, n=T.head(); inext()) nor = nor+((Triangle *)n->data)->getNormal(); + if (nor.isNull()) + { + TMesh::warning("TriangulateHole: Unable to compute an average normal. Can't optimize.\n"); + return nt; + } + nor.normalize(); + + // Memorizzo da qualche parte la posizione originale dei vertici + // e Proietto il boundary sul piano con normale quella appena calcolata + //coord *ovps = (coord *)malloc((ovbs.numels())*3*sizeof(coord)); // AMF_CHANGE - since IMAT-STL 2.4-2 + coord *ovps = new coord[3*(ovbs.numels())]; // AMF_CHANGE - since IMAT-STL 2.4-2 + int j = 0; + FOREACHVVVERTEX((&(ovbs)), v, n) + { + ovps[j++] = v->x; + ovps[j++] = v->y; + ovps[j++] = v->z; + v->project(&nor); + } + + // Proietto i punti interni sul piano d'appoggio + Point *p; + //coord *ovpsi = (coord *)malloc((vl->numels())*3*sizeof(coord)); // AMF_CHANGE - since IMAT-STL 2.4-2 + coord *ovpsi = new coord[3*(vl->numels())]; // AMF_CHANGE - since IMAT-STL 2.4-2 + j = 0; + FOREACHNODE((*vl), n) + { + p = ((Point *)n->data); + ovpsi[j++] = p->x; ovpsi[j++] = p->y; ovpsi[j++] = p->z; + } + FOREACHNODE((*vl), n) + { + p = ((Point *)n->data); + p->project(&nor); + } + + // Ottimizzo secondo Delaunay vincolato al boundary la nuova regione + + int sw; + do + { + sw = 0; + FOREACHVEEDGE((&(nedg)), e1, n) + { + ang = e1->delaunayMinAngle(); + if (e1->swap()) + {if (e1->delaunayMinAngle() <= ang) e1->swap(1); else sw++;} + } + } while (sw); + + // Inserisco i punti interni + int ntt = T.numels()-nt; + List ivs; + + FOREACHNODE((*vl), n) + { + p = ((Point *)n->data); + ivs.appendTail(watsonInsert(p, &T, T.numels()-ntt)); + } + nt = (T.numels() - ntt); + + // Riporto i vertici interni al loro posto + j=0; FOREACHVVVERTEX((&(ivs)), v, n) + { + if (v != NULL) + { + v->x = ovpsi[j++]; v->y = ovpsi[j++]; v->z = ovpsi[j++]; + } else j+=3; + } + delete [] ovpsi; //free(ovpsi); // AMF_CHANGE - since IMAT-STL 2.4-2 + + // Riporto i vertici del boundary al loro posto + j=0; FOREACHVVVERTEX((&(ovbs)), v, n) + { + v->x = ovps[j++]; v->y = ovps[j++]; v->z = ovps[j++]; + } + delete [] ovps; //free(ovps); // AMF_CHANGE - since IMAT-STL 2.4-2 + + return nt; +} + + +////// Inserts the point 'p' in the Delaunay triangulation 'tR' with 'nt' triangles //// + +Vertex *Basic_TMesh::watsonInsert(Point *p, List *tR, int nt) +{ + Node *n, *m; + Edge *e; + Triangle *t; + List bdr, bdrs, todo, *ve; + Vertex *v1, *v2, *v3; + int i; + + for (i=0, n = T.head(); inext(), i++) + { + t = ((Triangle *)n->data); + if (t->e1 != NULL && t->inSphere(p)) + { + v1 = t->v1(); v2 = t->v2(); v3 = t->v3(); + if (!IS_BIT(v1, 5)) bdr.appendHead(v1); + if (!IS_BIT(v2, 5)) bdr.appendHead(v2); + if (!IS_BIT(v3, 5)) bdr.appendHead(v3); + MARK_BIT(v1, 5); MARK_BIT(v2, 5); MARK_BIT(v3, 5); + MARK_BIT(t, 6); + todo.appendHead(t); + } + } + if (bdr.numels() == 0) return NULL; + + FOREACHVVVERTEX((&(bdr)), v1, n) + { + ve = v1->VE(); + FOREACHVEEDGE(ve, e, m) if (!IS_BIT(e->t1, 6) || !IS_BIT(e->t2, 6)) v1->e0 = e; + delete(ve); + } + + while (todo.numels()) + { + t = ((Triangle *)todo.head()->data); + todo.removeCell(todo.head()); + unlinkTriangleNoManifold(t); + } + + Node *tmp; + for (i=0, n = T.head(); idata); + if (t->e1 == NULL) {tmp = n; n=n->next(); T.freeCell(tmp);} + else n=n->next(); + } + + for (n = bdr.head(); n!=NULL;) + { + v1 = ((Vertex *)n->data); + if (v1->e0 == NULL) {tmp = n; n=n->next(); bdr.removeCell(tmp);} + else n=n->next(); + } + + v1 = v2 = ((Vertex *)bdr.head()->data); + do + { + bdrs.appendHead(v1); + v1 = v1->nextOnBoundary(); + } while (v1 != v2); + + Vertex *v = newVertex(p->x, p->y, p->z); + V.appendHead(v); + + v1 = ((Vertex *)bdrs.head()->data); + v->e0 = e = newEdge(v, v1); + UNMARK_BIT(v1, 5); + E.appendHead(e); + + for (n = bdrs.head()->next(); n!=NULL; n=n->next()) + { + v1 = ((Vertex *)n->data); + UNMARK_BIT(v1, 5); + v2 = ((Vertex *)n->prev()->data); + e = newEdge(v, v1); + CreateTriangle(e, v1->getEdge(v2), (Edge *)E.head()->data); + E.appendHead(e); + } + EulerEdgeTriangle(v->e0, (Edge *)E.head()->data); + + return v; +} + + +//// Removes a vertex and retriangulates its VT //// + +int Basic_TMesh::retriangulateVT(Vertex *v) +{ + Point nor; + Edge *e, *e0 = v->e0->t1->oppositeEdge(v); + List *vt = v->VT(); + List oe; + Triangle *t; + Node *m, *n; + int i, nt; + + FOREACHVTTRIANGLE(vt, t, m) + { + e = t->oppositeEdge(v); + oe.appendTail(t->prevEdge(e)); + oe.appendTail(e); + oe.appendTail(t->nextEdge(e)); + nor = nor+t->getNormal(); + unlinkTriangle(t); + } + removeUnlinkedElements(); // AMF_CHANGE - since IMAT-STL 2.4-2 + nor.normalize(); + nt = TriangulateHole(e0, &nor); + + for (m=T.head(), i=0; inext()) + { + t = ((Triangle *)m->data); + if (t->overlaps() || t->isExactlyDegenerate()) break; + } + if (inext()) + unlinkTriangle(((Triangle *)m->data)); + n = oe.head(); + FOREACHVTTRIANGLE(vt, t, m) + { + t->e1 = ((Edge *)n->data); n=n->next(); + t->e2 = ((Edge *)n->data); n=n->next(); + t->e3 = ((Edge *)n->data); n=n->next(); + t->e1->v1 = v; t->e1->v2 = (t->e2->t1 == NULL)?(t->e2->v1):(t->e2->v2); + t->e3->v1 = v; t->e3->v2 = (t->e2->t1 == NULL)?(t->e2->v2):(t->e2->v1); + ((t->e2->t1 == NULL)?(t->e2->t1):(t->e2->t2)) = t; + t->e1->t1 = t; + t->e3->t2 = t; + } + v->e0 = ((Triangle *)vt->head()->data)->e1; + } + delete(vt); + + return 1; +} + + +////////// Generic method for patching holes. Heuristic. ///////////// +////////// Small angles are patched first, if possible. ///////////// + +int Basic_TMesh::TriangulateHole(Edge *e) +{ + if (!e->isOnBoundary()) return 0; + + List bvs; + Node *n, *gn = NULL; + Edge *e1, *e2; + Vertex *v, *v1, *v2; + double ang, gang; + int nt = 0; + Triangle *t; + v = e->v1; + + t = (e->t1!=NULL)?(e->t1):(e->t2); + if (t->nextEdge(e)->isOnBoundary() && t->prevEdge(e)->isOnBoundary()) return 0; + + do + { + bvs.appendHead(v); + v = v->nextOnBoundary(); + } while (v != e->v1); + + while (bvs.numels() > 2) + { + gang = DBL_MAX; + FOREACHVVVERTEX((&bvs), v, n) + if (!IS_BIT(v, 5) && v->e0 && (ang = v->getAngleForTriangulation()) < gang) + {gang = ang; gn = n;} + if (gang == DBL_MAX) + { + TMesh::warning("TriangulateHole: Can't complete the triangulation.\n"); + FOREACHVVVERTEX((&bvs), v, n) UNMARK_BIT(v, 5); + int i=0; FOREACHTRIANGLE(t, n) if (i++==nt) break; else unlinkTriangle(t); + removeUnlinkedElements(); + return 0; + } + v = ((Vertex *)gn->data); + v1 = (Vertex *)((gn->next() != NULL)?(gn->next()):(bvs.head()))->data; + v2 = (Vertex *)((gn->prev() != NULL)?(gn->prev()):(bvs.tail()))->data; + e1 = v->getEdge(v1); + e2 = v->getEdge(v2); + if ((t=EulerEdgeTriangle(e1,e2))==NULL) MARK_BIT(v, 5); + else { bvs.removeCell(gn); UNMARK_BIT(v1, 5); UNMARK_BIT(v2, 5); MARK_VISIT(t); nt++; } + } + + return nt; +} + + +// Fills the hole identified by 'e' and leaves the new triangle selected. +// 'refine' is for internal vertex insertion. + +void Basic_TMesh::FillHole(Edge *e, bool refine) +{ + int i, nt; + Node *n; + Triangle *t; + Vertex *v; + + deselectTriangles(); + FOREACHVERTEX(v, n) UNMARK_BIT(v, 5); + nt = TriangulateHole(e); + if (!nt) return; + + i=0; FOREACHTRIANGLE(t, n) if (i++==nt) break; else MARK_VISIT(t); + + if (refine) + refineSelectedHolePatches((Triangle *)T.head()->data); +} + +//// Triangulate Small Boundaries (with less than 'nbe' edges) ///// + +int Basic_TMesh::fillSmallBoundaries(int nbe, bool refine_patches) +{ + if (nbe == 0) nbe = E.numels(); + Vertex *v,*w; + Triangle *t; + Node *n; + int grd, is_selection=0, tbds = 0, pct = 100; + List bdrs; + + TMesh::begin_progress(); + TMesh::report_progress("0%% done "); + + FOREACHTRIANGLE(t, n) if (IS_VISITED(t)) {is_selection=1; break;} + + if (is_selection) { + FOREACHTRIANGLE(t, n) if (!IS_VISITED(t)) + { + MARK_BIT(t->v1(), 6); MARK_BIT(t->v2(), 6); MARK_BIT(t->v3(), 6); + } + } + else FOREACHVERTEX(v, n) UNMARK_BIT(v, 6); + + FOREACHVERTEX(v, n) + { + grd = 0; + if (!IS_BIT(v, 6) && v->isOnBoundary()) + { + tbds++; + w = v; + do + { + if (IS_BIT(w, 6)) grd=nbe+1; + MARK_BIT(w, 6); + grd++; + w = w->nextOnBoundary(); + } while (w != v); + if (grd <= nbe) bdrs.appendHead(w->nextBoundaryEdge()); + } + } + FOREACHVERTEX(v, n) { UNMARK_BIT(v, 5); UNMARK_BIT(v, 6); } + + deselectTriangles(); + + pct=0; FOREACHNODE(bdrs, n) + { + if (TriangulateHole((Edge *)n->data) && refine_patches) + { + t = (Triangle *)T.head()->data; + refineSelectedHolePatches(t); + } + TMesh::report_progress("%d%% done ",((++pct)*100)/bdrs.numels()); + } + + grd = bdrs.numels(); + + TMesh::end_progress(); + + return grd; +} + + +// Inserts new vertices in the current selection so as +// to reflect the density of the surrounding mesh. +// This method assumes that the selection has no internal vertices. + +int Basic_TMesh::refineSelectedHolePatches(Triangle *t0) +{ + Node *n, *m; + Triangle *t, *t1, *t2; + Edge *e, *f; + Vertex *v; + List *ve, toswap, reg, all_edges, interior_edges, boundary_edges, boundary_vertices, interior_vertices; + coord sigma, l, sv1, sv2, sv3, dv1, dv2, dv3; + int swaps, totits, nee, ntb, nnt=-1, pnnt, gits=0; + const double alpha = sqrt(2.0); + Point vc; + + if (t0 != NULL) + { + if (!IS_VISITED(t0)) TMesh::error("refineSelectedHolePatches: unexpected unselected t0 !"); + UNMARK_VISIT(t0); toswap.appendHead(t0); + while ((t=(Triangle *)toswap.popHead()) != NULL) + { + reg.appendHead(t); + t1=t->t1(); if (IS_VISITED(t1)) {UNMARK_VISIT(t1); toswap.appendHead(t1);} + t1=t->t2(); if (IS_VISITED(t1)) {UNMARK_VISIT(t1); toswap.appendHead(t1);} + t1=t->t3(); if (IS_VISITED(t1)) {UNMARK_VISIT(t1); toswap.appendHead(t1);} + } + FOREACHVTTRIANGLE((®), t, n) MARK_VISIT(t); + } + else FOREACHTRIANGLE(t, n) if (IS_VISITED(t)) reg.appendHead(t); + + printf("%d\n",reg.numels()); + + FOREACHVTTRIANGLE((®), t, n) + { + e = t->e1; if (!IS_BIT(e, 5)) {MARK_BIT(e, 5); all_edges.appendHead(e);} else UNMARK_BIT(e, 5); + e = t->e2; if (!IS_BIT(e, 5)) {MARK_BIT(e, 5); all_edges.appendHead(e);} else UNMARK_BIT(e, 5); + e = t->e3; if (!IS_BIT(e, 5)) {MARK_BIT(e, 5); all_edges.appendHead(e);} else UNMARK_BIT(e, 5); + } + + while (all_edges.numels()) + { + e = (Edge *)all_edges.popHead(); + if (IS_BIT(e, 5)) { boundary_edges.appendHead(e); UNMARK_BIT(e, 5); } + else { interior_edges.appendHead(e); MARK_BIT(e, 5); } + } + + FOREACHVEEDGE((&boundary_edges), e, n) + { + v = e->v1; if (!IS_BIT(v, 5)) { MARK_BIT(v, 5); boundary_vertices.appendHead(v);} + v = e->v2; if (!IS_BIT(v, 5)) { MARK_BIT(v, 5); boundary_vertices.appendHead(v);} + } + + FOREACHVVVERTEX((&boundary_vertices), v, n) UNMARK_BIT(v, 5); + + // Due to the above definitions, interior edges are BIT + + FOREACHVVVERTEX((&boundary_vertices), v, n) + { + ve = v->VE(); + sigma=0; nee=0; FOREACHVEEDGE(ve, e, m) if (!IS_BIT(e, 5)) {nee++; sigma += e->length();} + sigma /= nee; v->info = new coord(sigma); + delete(ve); + } + + FOREACHVEEDGE((&interior_edges), e, n) UNMARK_BIT(e, 5); + FOREACHVEEDGE((&boundary_edges), e, n) MARK_BIT(e, 6); + + do + { + pnnt=nnt; + nnt=0; + FOREACHVTTRIANGLE((®), t, n) + { + vc = t->getCenter(); + sv1 = (*(coord *)t->v1()->info); + sv2 = (*(coord *)t->v2()->info); + sv3 = (*(coord *)t->v3()->info); + sigma = (sv1+sv2+sv3)/3.0; + dv1 = alpha*(t->v1()->distance(&vc)); + dv2 = alpha*(t->v2()->distance(&vc)); + dv3 = alpha*(t->v3()->distance(&vc)); + if (dv1>sigma && dv1>sv1 && dv2>sigma && dv2>sv2 && dv3>sigma && dv3>sv3) + { + ntb = T.numels(); + v = splitTriangle(t,&vc,1); + nnt += (T.numels()-ntb); + if (T.numels() == ntb+2) + { + v->info = new coord(sigma); + interior_vertices.appendHead(v); + interior_edges.appendHead(v->e0); + interior_edges.appendHead(v->e0->leftTriangle(v)->prevEdge(v->e0)); + interior_edges.appendHead(v->e0->rightTriangle(v)->nextEdge(v->e0)); + t1 = ((Triangle *)T.head()->data); + t2 = ((Triangle *)T.head()->next()->data); + t1->mask = t2->mask = t->mask; + reg.appendHead(t1); reg.appendHead(t2); + } + } + } + + FOREACHVEEDGE((&interior_edges), e, n) {MARK_BIT(e, 5); toswap.appendHead(e);} + totits=0; swaps=1; + while (swaps && totits++ < 10) + { + swaps = 0; + while ((e=(Edge *)toswap.popHead())!=NULL) + { + UNMARK_BIT(e, 5); + l = e->squaredLength(); + if (e->swap()) + { + if (e->squaredLength() >= l*0.999999) e->swap(1); + else + { + swaps++; + toswap.appendTail(e); + f = e->t1->nextEdge(e); if (!IS_BIT(f, 5) && !IS_BIT(f, 6)) { MARK_BIT(f, 5); toswap.appendTail(f); } + f = e->t1->prevEdge(e); if (!IS_BIT(f, 5) && !IS_BIT(f, 6)) { MARK_BIT(f, 5); toswap.appendTail(f); } + f = e->t2->nextEdge(e); if (!IS_BIT(f, 5) && !IS_BIT(f, 6)) { MARK_BIT(f, 5); toswap.appendTail(f); } + f = e->t2->prevEdge(e); if (!IS_BIT(f, 5) && !IS_BIT(f, 6)) { MARK_BIT(f, 5); toswap.appendTail(f); } + } + } + } + } + + if (pnnt==nnt) gits++; + } while (nnt && gits<10); + + //FOREACHVEEDGE((&boundary_edges), e, n) UNMARK_BIT(e, 6); + FOREACHVVVERTEX((&boundary_vertices), v, n) { delete((coord *)v->info); v->info = NULL; MARK_BIT(v, 5);} + FOREACHVVVERTEX((&interior_vertices), v, n) { delete((coord *)v->info); v->info = NULL; MARK_BIT(v, 6);} + + if (gits>=10) {TMesh::warning("Fill holes: Refinement stage failed to converge. Breaking.\n"); return 1;} + + return 0; +} + +// Joins the two boundary vertices gv and gw through an edge. A pair of triangles is +// added to properly change the topology of the mesh. +// On success, the return value is the new edge connecting the two vertices. +// NULL is returned on failure. +// Failure occurs if gv and gw are not both on boundary. +// If 'justconnect' is false, the remaining hole is filled with new triangles, unless +// gv and gw are contiguous on the boundary loop (failure). +// If 'justconnect' is true, gv and gw must not belong to the same boundary loop (failure). +// If 'refine', the patching triangles are refined to reproduce neighboring density. + +Edge *Basic_TMesh::joinBoundaryLoops(Vertex *gv, Vertex *gw, bool justconnect, bool refine) +{ + Vertex *v, *gvn, *gwn; + Edge *e, *gve, *gwe; + Triangle *t; + Node *n; + double tl1 = 0.0, tl2 = 0.0, pl1, pl2; + + if (gv == NULL || gw == NULL || !gv->isOnBoundary() || !gw->isOnBoundary()) return NULL; + + FOREACHVERTEX(v, n) UNMARK_VISIT(v); + deselectTriangles(); + + v = gv; + if (!justconnect) + { + do { v = v->nextOnBoundary(); if (v == gw) return NULL; } while (v != gv); + } else + { + gvn = gv->nextOnBoundary(); gwn = gv->prevOnBoundary(); + if (gw == gvn || gw == gwn) return NULL; + if (gw == gvn->nextOnBoundary()) + { + t = EulerEdgeTriangle(gvn->prevBoundaryEdge(), gvn->nextBoundaryEdge()); MARK_VISIT(t); return t->oppositeEdge(gvn); + } + if (gw == gwn->prevOnBoundary()) + { + t = EulerEdgeTriangle(gwn->prevBoundaryEdge(), gwn->nextBoundaryEdge()); MARK_VISIT(t); return t->oppositeEdge(gwn); + } + } + + gve = gv->prevBoundaryEdge(); + gvn = gve->oppositeVertex(gv); + gwe = gw->nextBoundaryEdge(); + gwn = gwe->oppositeVertex(gw); + + Edge *je = CreateEdge(gv, gw); + Edge *je1 = CreateEdge(gv, gwn); + Edge *je2 = CreateEdge(gwn, gvn); + + t = CreateTriangle(je, gwe, je1); MARK_VISIT(t); + t = CreateTriangle(je1, je2, gve); MARK_VISIT(t); + + if (justconnect) return je; + + v = gv; do { e = v->nextBoundaryEdge(); v = e->oppositeVertex(v); tl1 += e->length(); } while (v != gv); + v = gw; do { e = v->nextBoundaryEdge(); v = e->oppositeVertex(v); tl2 += e->length(); } while (v != gw); + pl1 = tl1; pl2 = tl2; + + double c1, c2; + + e = je; + while (e->isOnBoundary()) + { + gv = (e->t2 != NULL) ? (e->v2) : (e->v1); gve = gv->nextBoundaryEdge(); + gw = (e->t1 != NULL) ? (e->v2) : (e->v1); gwe = gw->prevBoundaryEdge(); + c1 = fabs((pl1 - gve->length())*tl2 - pl2*tl1); + c2 = fabs((pl2 - gwe->length())*tl1 - pl1*tl2); + if (c1length(); + e = t->nextEdge(gve); + } else + { + t = EulerEdgeTriangle(gwe, e); MARK_VISIT(t); + pl2 -= gwe->length(); + e = t->prevEdge(gwe); + } + } + + if (refine) refineSelectedHolePatches(); + + return je; +} + +} //namespace T_MESH diff --git a/src/mesh_fix/src/Algorithms/marchIntersections.cpp b/src/mesh_fix/src/Algorithms/marchIntersections.cpp new file mode 100644 index 000000000..7d9a83351 --- /dev/null +++ b/src/mesh_fix/src/Algorithms/marchIntersections.cpp @@ -0,0 +1,1039 @@ +/**************************************************************************** +* TMesh * +* * +* Consiglio Nazionale delle Ricerche * +* Istituto di Matematica Applicata e Tecnologie Informatiche * +* Sezione di Genova * +* IMATI-GE / CNR * +* * +* Authors: Marco Attene * +* Copyright(C) 2013: IMATI-GE / CNR * +* All rights reserved. * +* * +* This program is dual-licensed as follows: * +* * +* (1) You may use TMesh as 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 3 of the License, or * +* (at your option) any later version. * +* In this case the 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 (http://www.gnu.org/licenses/gpl.txt) * +* for more details. * +* * +* (2) You may use TMesh as part of a commercial software. In this case a * +* proper agreement must be reached with the Authors and with IMATI-GE/CNR * +* based on a proper licensing contract. * +* * +****************************************************************************/ + +#include "marchIntersections.h" + +namespace T_MESH +{ + +int mc_ints::compare(const Data *e1, const Data *e2) +{ + mc_ints *a = (mc_ints *)e1; + mc_ints *b = (mc_ints *)e2; + coord& l1 = a->ic; + coord& l2 = b->ic; + if (l1 < l2) return -1; + if (l2 < l1) return 1; + + return 0; +} + +UBYTE mc_cell::lookup() const +{ + UBYTE l = 0; + int v[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }, m; + + if (ints[0]) if (ints[0]->sg) v[1] = 1; else v[0] = 1; + if (ints[1]) if (ints[1]->sg) v[2] = 1; else v[1] = 1; + if (ints[2]) if (ints[2]->sg) v[2] = 1; else v[3] = 1; + if (ints[3]) if (ints[3]->sg) v[3] = 1; else v[0] = 1; + + if (ints[4]) if (ints[4]->sg) v[5] = 1; else v[4] = 1; + if (ints[5]) if (ints[5]->sg) v[6] = 1; else v[5] = 1; + if (ints[6]) if (ints[6]->sg) v[6] = 1; else v[7] = 1; + if (ints[7]) if (ints[7]->sg) v[7] = 1; else v[4] = 1; + + if (ints[8]) if (ints[8]->sg) v[4] = 1; else v[0] = 1; + if (ints[9]) if (ints[9]->sg) v[5] = 1; else v[1] = 1; + if (ints[10]) if (ints[10]->sg) v[6] = 1; else v[2] = 1; + if (ints[11]) if (ints[11]->sg) v[7] = 1; else v[3] = 1; + + if (v[0] && !ints[0]) v[1] = 1; + if (v[0] && !ints[3]) v[3] = 1; + if (v[0] && !ints[8]) v[4] = 1; + + if (v[1] && !ints[0]) v[0] = 1; + if (v[1] && !ints[1]) v[2] = 1; + if (v[1] && !ints[9]) v[5] = 1; + + if (v[2] && !ints[1]) v[1] = 1; + if (v[2] && !ints[2]) v[3] = 1; + if (v[2] && !ints[10]) v[6] = 1; + + if (v[3] && !ints[2]) v[2] = 1; + if (v[3] && !ints[3]) v[0] = 1; + if (v[3] && !ints[11]) v[7] = 1; + + if (v[4] && !ints[4]) v[5] = 1; + if (v[4] && !ints[7]) v[7] = 1; + if (v[4] && !ints[8]) v[0] = 1; + + if (v[5] && !ints[4]) v[4] = 1; + if (v[5] && !ints[5]) v[6] = 1; + if (v[5] && !ints[9]) v[1] = 1; + + if (v[6] && !ints[5]) v[5] = 1; + if (v[6] && !ints[6]) v[7] = 1; + if (v[6] && !ints[10]) v[2] = 1; + + if (v[7] && !ints[6]) v[6] = 1; + if (v[7] && !ints[7]) v[4] = 1; + if (v[7] && !ints[11]) v[3] = 1; + + for (m = 0; m<8; m++) l |= (((UBYTE)((v[m]) ? (1) : (0))) << m); + + return l; +} + + +UBYTE mc_cell::lookdown() +{ + int v[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }, m; + UBYTE i; + const UBYTE c1[12] = { 0, 1, 3, 0, 4, 5, 7, 4, 0, 1, 2, 3 }; + const UBYTE c2[12] = { 1, 2, 2, 3, 5, 6, 6, 7, 4, 5, 6, 7 }; + const UBYTE e1[8] = { 0, 0, 2, 2, 4, 4, 6, 6 }; + const UBYTE e2[8] = { 3, 1, 1, 3, 7, 5, 5, 7 }; + const UBYTE e3[8] = { 8, 9, 10, 11, 8, 9, 10, 11 }; + + for (i = 0; i < 12; i++) if (ints[i]) + { + if (ints[i]->sg) v[c2[i]] = 1; else v[c1[i]] = 1; + } + + for (m = 0; m < 8; m++) if (v[m]) + { + if (!ints[e1[m]] && !v[c1[e1[m]]]) { v[c1[e1[m]]] = 1; if (c1[e1[m]] < m) { m = -1; continue; } } + if (!ints[e1[m]] && !v[c2[e1[m]]]) { v[c2[e1[m]]] = 1; if (c2[e1[m]] < m) { m = -1; continue; } } + if (!ints[e2[m]] && !v[c1[e2[m]]]) { v[c1[e2[m]]] = 1; if (c1[e2[m]] < m) { m = -1; continue; } } + if (!ints[e2[m]] && !v[c2[e2[m]]]) { v[c2[e2[m]]] = 1; if (c2[e2[m]] < m) { m = -1; continue; } } + if (!ints[e3[m]] && !v[c1[e3[m]]]) { v[c1[e3[m]]] = 1; if (c1[e3[m]] < m) { m = -1; continue; } } + if (!ints[e3[m]] && !v[c2[e3[m]]]) { v[c2[e3[m]]] = 1; if (c2[e3[m]] < m) { m = -1; continue; } } + } + + i = 0; + for (m = 0; m<8; m++) i |= (((UBYTE)((v[m]) ? (1) : (0))) << m); + + return i; +} + +int mc_cell::compare(const void *e1, const void *e2) +{ + mc_cell *a = (mc_cell *)e1; + mc_cell *b = (mc_cell *)e2; + int c; + + if ((c=(a->x - b->x)) < 0) return -1; + if (c > 0) return 1; + if ((c=(a->y - b->y)) < 0) return -1; + if (c > 0) return 1; + if ((c=(a->z - b->z)) < 0) return -1; + if (c > 0) return 1; + + return 0; +} + + + +void mc_cell::polygonize(Basic_TMesh *tin) +{ + ExtVertex *v[12]; + Edge *e1,*e2,*e3; + int i,t[3]; + unsigned char lu = lookdown(); + + static const char mc_triTable[256][20] = + { + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 8, 3, -1, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 8, 3, -1, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 9, 2, 10, -1, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 2, 8, 3, -1, 2, 10, 8, -1, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 11, 2, -1, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 9, 0, -1, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 11, 2, -1, 1, 9, 11, -1, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 3, 10, 1, -1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 10, 1, -1, 0, 8, 10, -1, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 3, 9, 0, -1, 3, 11, 9, -1, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 9, 8, 10, -1, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 4, 3, 0, -1, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 1, 9, -1, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 4, 1, 9, -1, 4, 7, 1, -1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 2, 10, -1, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 3, 4, 7, -1, 3, 0, 4, -1, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 9, 2, 10, -1, 9, 0, 2, -1, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 2, 10, 9, -1, 2, 9, 7, -1, 2, 7, 3, -1, 7, 9, 4, -1, -1, -1, -1, -1}, + { 8, 4, 7, -1, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 4, 7, -1, 11, 2, 4, -1, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 9, 0, 1, -1, 8, 4, 7, -1, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 4, 7, 11, -1, 9, 4, 11, -1, 9, 11, 2, -1, 9, 2, 1, -1, -1, -1, -1, -1}, + { 3, 10, 1, -1, 3, 11, 10, -1, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 11, 10, -1, 1, 4, 11, -1, 1, 0, 4, -1, 7, 11, 4, -1, -1, -1, -1, -1}, + { 4, 7, 8, -1, 9, 0, 11, -1, 9, 11, 10, -1, 11, 0, 3, -1, -1, -1, -1, -1}, + { 4, 7, 11, -1, 4, 11, 9, -1, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 9, 5, 4, -1, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 5, 4, -1, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 8, 5, 4, -1, 8, 3, 5, -1, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 2, 10, -1, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 3, 0, 8, -1, 1, 2, 10, -1, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 5, 2, 10, -1, 5, 4, 2, -1, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 2, 10, 5, -1, 3, 2, 5, -1, 3, 5, 4, -1, 3, 4, 8, -1, -1, -1, -1, -1}, + { 9, 5, 4, -1, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 11, 2, -1, 0, 8, 11, -1, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 5, 4, -1, 0, 1, 5, -1, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 2, 1, 5, -1, 2, 5, 8, -1, 2, 8, 11, -1, 4, 8, 5, -1, -1, -1, -1, -1}, + {10, 3, 11, -1, 10, 1, 3, -1, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 4, 9, 5, -1, 0, 8, 1, -1, 8, 10, 1, -1, 8, 11, 10, -1, -1, -1, -1, -1}, + { 5, 4, 0, -1, 5, 0, 11, -1, 5, 11, 10, -1, 11, 0, 3, -1, -1, -1, -1, -1}, + { 5, 4, 8, -1, 5, 8, 10, -1, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 9, 7, 8, -1, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 9, 3, 0, -1, 9, 5, 3, -1, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 7, 8, -1, 0, 1, 7, -1, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 5, 3, -1, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 9, 7, 8, -1, 9, 5, 7, -1, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {10, 1, 2, -1, 9, 5, 0, -1, 5, 3, 0, -1, 5, 7, 3, -1, -1, -1, -1, -1}, + { 8, 0, 2, -1, 8, 2, 5, -1, 8, 5, 7, -1, 10, 5, 2, -1, -1, -1, -1, -1}, + { 2, 10, 5, -1, 2, 5, 3, -1, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 7, 9, 5, -1, 7, 8, 9, -1, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 9, 5, 7, -1, 9, 7, 2, -1, 9, 2, 0, -1, 2, 7, 11, -1, -1, -1, -1, -1}, + { 2, 3, 11, -1, 0, 1, 8, -1, 1, 7, 8, -1, 1, 5, 7, -1, -1, -1, -1, -1}, + {11, 2, 1, -1, 11, 1, 7, -1, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 9, 5, 8, -1, 8, 5, 7, -1, 10, 1, 3, -1, 10, 3, 11, -1, -1, -1, -1, -1}, + { 5, 7, 0, -1, 5, 0, 9, -1, 7, 11, 0, -1, 1, 0, 10, -1, 11, 10, 0, -1}, + {11, 10, 0, -1, 11, 0, 3, -1, 10, 5, 0, -1, 8, 0, 7, -1, 5, 7, 0, -1}, + {11, 10, 5, -1, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 8, 3, -1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 9, 0, 1, -1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 8, 3, -1, 1, 9, 8, -1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 6, 5, -1, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 6, 5, -1, 1, 2, 6, -1, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 9, 6, 5, -1, 9, 0, 6, -1, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 5, 9, 8, -1, 5, 8, 2, -1, 5, 2, 6, -1, 3, 2, 8, -1, -1, -1, -1, -1}, + { 2, 3, 11, -1, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 0, 8, -1, 11, 2, 0, -1, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 1, 9, -1, 2, 3, 11, -1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 5, 10, 6, -1, 1, 9, 2, -1, 9, 11, 2, -1, 9, 8, 11, -1, -1, -1, -1, -1}, + { 6, 3, 11, -1, 6, 5, 3, -1, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 8, 11, -1, 0, 11, 5, -1, 0, 5, 1, -1, 5, 11, 6, -1, -1, -1, -1, -1}, + { 3, 11, 6, -1, 0, 3, 6, -1, 0, 6, 5, -1, 0, 5, 9, -1, -1, -1, -1, -1}, + { 6, 5, 9, -1, 6, 9, 11, -1, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 5, 10, 6, -1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 4, 3, 0, -1, 4, 7, 3, -1, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 9, 0, -1, 5, 10, 6, -1, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {10, 6, 5, -1, 1, 9, 7, -1, 1, 7, 3, -1, 7, 9, 4, -1, -1, -1, -1, -1}, + { 6, 1, 2, -1, 6, 5, 1, -1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 2, 5, -1, 5, 2, 6, -1, 3, 0, 4, -1, 3, 4, 7, -1, -1, -1, -1, -1}, + { 8, 4, 7, -1, 9, 0, 5, -1, 0, 6, 5, -1, 0, 2, 6, -1, -1, -1, -1, -1}, + { 7, 3, 9, -1, 7, 9, 4, -1, 3, 2, 9, -1, 5, 9, 6, -1, 2, 6, 9, -1}, + { 3, 11, 2, -1, 7, 8, 4, -1, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 5, 10, 6, -1, 4, 7, 2, -1, 4, 2, 0, -1, 2, 7, 11, -1, -1, -1, -1, -1}, + { 0, 1, 9, -1, 4, 7, 8, -1, 2, 3, 11, -1, 5, 10, 6, -1, -1, -1, -1, -1}, + { 9, 2, 1, -1, 9, 11, 2, -1, 9, 4, 11, -1, 7, 11, 4, -1, 5, 10, 6, -1}, + { 8, 4, 7, -1, 3, 11, 5, -1, 3, 5, 1, -1, 5, 11, 6, -1, -1, -1, -1, -1}, + { 5, 1, 11, -1, 5, 11, 6, -1, 1, 0, 11, -1, 7, 11, 4, -1, 0, 4, 11, -1}, + { 0, 5, 9, -1, 0, 6, 5, -1, 0, 3, 6, -1, 11, 6, 3, -1, 8, 4, 7, -1}, + { 6, 5, 9, -1, 6, 9, 11, -1, 4, 7, 9, -1, 7, 11, 9, -1, -1, -1, -1, -1}, + {10, 4, 9, -1, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 4, 10, 6, -1, 4, 9, 10, -1, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {10, 0, 1, -1, 10, 6, 0, -1, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 8, 3, 1, -1, 8, 1, 6, -1, 8, 6, 4, -1, 6, 1, 10, -1, -1, -1, -1, -1}, + { 1, 4, 9, -1, 1, 2, 4, -1, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 3, 0, 8, -1, 1, 2, 9, -1, 2, 4, 9, -1, 2, 6, 4, -1, -1, -1, -1, -1}, + { 0, 2, 4, -1, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 8, 3, 2, -1, 8, 2, 4, -1, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {10, 4, 9, -1, 10, 6, 4, -1, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 8, 2, -1, 2, 8, 11, -1, 4, 9, 10, -1, 4, 10, 6, -1, -1, -1, -1, -1}, + { 3, 11, 2, -1, 0, 1, 6, -1, 0, 6, 4, -1, 6, 1, 10, -1, -1, -1, -1, -1}, + { 6, 4, 1, -1, 6, 1, 10, -1, 4, 8, 1, -1, 2, 1, 11, -1, 8, 11, 1, -1}, + { 9, 6, 4, -1, 9, 3, 6, -1, 9, 1, 3, -1, 11, 6, 3, -1, -1, -1, -1, -1}, + { 8, 11, 1, -1, 8, 1, 0, -1, 11, 6, 1, -1, 9, 1, 4, -1, 6, 4, 1, -1}, + { 3, 11, 6, -1, 3, 6, 0, -1, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 6, 4, 8, -1, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 7, 10, 6, -1, 7, 8, 10, -1, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 7, 3, -1, 0, 10, 7, -1, 0, 9, 10, -1, 6, 7, 10, -1, -1, -1, -1, -1}, + {10, 6, 7, -1, 1, 10, 7, -1, 1, 7, 8, -1, 1, 8, 0, -1, -1, -1, -1, -1}, + {10, 6, 7, -1, 10, 7, 1, -1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 2, 6, -1, 1, 6, 8, -1, 1, 8, 9, -1, 8, 6, 7, -1, -1, -1, -1, -1}, + { 2, 6, 9, -1, 2, 9, 1, -1, 6, 7, 9, -1, 0, 9, 3, -1, 7, 3, 9, -1}, + { 7, 8, 0, -1, 7, 0, 6, -1, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 7, 3, 2, -1, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 2, 3, 11, -1, 10, 6, 8, -1, 10, 8, 9, -1, 8, 6, 7, -1, -1, -1, -1, -1}, + { 2, 0, 7, -1, 2, 7, 11, -1, 0, 9, 7, -1, 6, 7, 10, -1, 9, 10, 7, -1}, + { 1, 8, 0, -1, 1, 7, 8, -1, 1, 10, 7, -1, 6, 7, 10, -1, 2, 3, 11, -1}, + {11, 2, 1, -1, 11, 1, 7, -1, 10, 6, 1, -1, 6, 7, 1, -1, -1, -1, -1, -1}, + { 8, 9, 6, -1, 8, 6, 7, -1, 9, 1, 6, -1, 11, 6, 3, -1, 1, 3, 6, -1}, + { 0, 9, 1, -1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 7, 8, 0, -1, 7, 0, 6, -1, 3, 11, 0, -1, 11, 6, 0, -1, -1, -1, -1, -1}, + { 7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 3, 0, 8, -1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 1, 9, -1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 8, 1, 9, -1, 8, 3, 1, -1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {10, 1, 2, -1, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 2, 10, -1, 3, 0, 8, -1, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 2, 9, 0, -1, 2, 10, 9, -1, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 6, 11, 7, -1, 2, 10, 3, -1, 10, 8, 3, -1, 10, 9, 8, -1, -1, -1, -1, -1}, + { 7, 2, 3, -1, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 7, 0, 8, -1, 7, 6, 0, -1, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 2, 7, 6, -1, 2, 3, 7, -1, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 6, 2, -1, 1, 8, 6, -1, 1, 9, 8, -1, 8, 7, 6, -1, -1, -1, -1, -1}, + {10, 7, 6, -1, 10, 1, 7, -1, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {10, 7, 6, -1, 1, 7, 10, -1, 1, 8, 7, -1, 1, 0, 8, -1, -1, -1, -1, -1}, + { 0, 3, 7, -1, 0, 7, 10, -1, 0, 10, 9, -1, 6, 10, 7, -1, -1, -1, -1, -1}, + { 7, 6, 10, -1, 7, 10, 8, -1, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 6, 8, 4, -1, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 3, 6, 11, -1, 3, 0, 6, -1, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 8, 6, 11, -1, 8, 4, 6, -1, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 9, 4, 6, -1, 9, 6, 3, -1, 9, 3, 1, -1, 11, 3, 6, -1, -1, -1, -1, -1}, + { 6, 8, 4, -1, 6, 11, 8, -1, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 2, 10, -1, 3, 0, 11, -1, 0, 6, 11, -1, 0, 4, 6, -1, -1, -1, -1, -1}, + { 4, 11, 8, -1, 4, 6, 11, -1, 0, 2, 9, -1, 2, 10, 9, -1, -1, -1, -1, -1}, + {10, 9, 3, -1, 10, 3, 2, -1, 9, 4, 3, -1, 11, 3, 6, -1, 4, 6, 3, -1}, + { 8, 2, 3, -1, 8, 4, 2, -1, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 4, 2, -1, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 9, 0, -1, 2, 3, 4, -1, 2, 4, 6, -1, 4, 3, 8, -1, -1, -1, -1, -1}, + { 1, 9, 4, -1, 1, 4, 2, -1, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 8, 1, 3, -1, 8, 6, 1, -1, 8, 4, 6, -1, 6, 10, 1, -1, -1, -1, -1, -1}, + {10, 1, 0, -1, 10, 0, 6, -1, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 4, 6, 3, -1, 4, 3, 8, -1, 6, 10, 3, -1, 0, 3, 9, -1, 10, 9, 3, -1}, + {10, 9, 4, -1, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 4, 9, 5, -1, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 8, 3, -1, 4, 9, 5, -1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 5, 0, 1, -1, 5, 4, 0, -1, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 7, 6, -1, 8, 3, 4, -1, 3, 5, 4, -1, 3, 1, 5, -1, -1, -1, -1, -1}, + { 9, 5, 4, -1, 10, 1, 2, -1, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 6, 11, 7, -1, 1, 2, 10, -1, 0, 8, 3, -1, 4, 9, 5, -1, -1, -1, -1, -1}, + { 7, 6, 11, -1, 5, 4, 10, -1, 4, 2, 10, -1, 4, 0, 2, -1, -1, -1, -1, -1}, + { 3, 4, 8, -1, 3, 5, 4, -1, 3, 2, 5, -1, 10, 5, 2, -1, 11, 7, 6, -1}, + { 7, 2, 3, -1, 7, 6, 2, -1, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 9, 5, 4, -1, 0, 8, 6, -1, 0, 6, 2, -1, 6, 8, 7, -1, -1, -1, -1, -1}, + { 3, 6, 2, -1, 3, 7, 6, -1, 1, 5, 0, -1, 5, 4, 0, -1, -1, -1, -1, -1}, + { 6, 2, 8, -1, 6, 8, 7, -1, 2, 1, 8, -1, 4, 8, 5, -1, 1, 5, 8, -1}, + { 9, 5, 4, -1, 10, 1, 6, -1, 1, 7, 6, -1, 1, 3, 7, -1, -1, -1, -1, -1}, + { 1, 6, 10, -1, 1, 7, 6, -1, 1, 0, 7, -1, 8, 7, 0, -1, 9, 5, 4, -1}, + { 4, 0, 10, -1, 4, 10, 5, -1, 0, 3, 10, -1, 6, 10, 7, -1, 3, 7, 10, -1}, + { 7, 6, 10, -1, 7, 10, 8, -1, 5, 4, 10, -1, 4, 8, 10, -1, -1, -1, -1, -1}, + { 6, 9, 5, -1, 6, 11, 9, -1, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 3, 6, 11, -1, 0, 6, 3, -1, 0, 5, 6, -1, 0, 9, 5, -1, -1, -1, -1, -1}, + { 0, 11, 8, -1, 0, 5, 11, -1, 0, 1, 5, -1, 5, 6, 11, -1, -1, -1, -1, -1}, + { 6, 11, 3, -1, 6, 3, 5, -1, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 2, 10, -1, 9, 5, 11, -1, 9, 11, 8, -1, 11, 5, 6, -1, -1, -1, -1, -1}, + { 0, 11, 3, -1, 0, 6, 11, -1, 0, 9, 6, -1, 5, 6, 9, -1, 1, 2, 10, -1}, + {11, 8, 5, -1, 11, 5, 6, -1, 8, 0, 5, -1, 10, 5, 2, -1, 0, 2, 5, -1}, + { 6, 11, 3, -1, 6, 3, 5, -1, 2, 10, 3, -1, 10, 5, 3, -1, -1, -1, -1, -1}, + { 5, 8, 9, -1, 5, 2, 8, -1, 5, 6, 2, -1, 3, 8, 2, -1, -1, -1, -1, -1}, + { 9, 5, 6, -1, 9, 6, 0, -1, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 5, 8, -1, 1, 8, 0, -1, 5, 6, 8, -1, 3, 8, 2, -1, 6, 2, 8, -1}, + { 1, 5, 6, -1, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 3, 6, -1, 1, 6, 10, -1, 3, 8, 6, -1, 5, 6, 9, -1, 8, 9, 6, -1}, + {10, 1, 0, -1, 10, 0, 6, -1, 9, 5, 0, -1, 5, 6, 0, -1, -1, -1, -1, -1}, + { 0, 3, 8, -1, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 5, 10, -1, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 5, 10, -1, 11, 7, 5, -1, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 5, 11, 7, -1, 5, 10, 11, -1, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {10, 7, 5, -1, 10, 11, 7, -1, 9, 8, 1, -1, 8, 3, 1, -1, -1, -1, -1, -1}, + {11, 1, 2, -1, 11, 7, 1, -1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 8, 3, -1, 1, 2, 7, -1, 1, 7, 5, -1, 7, 2, 11, -1, -1, -1, -1, -1}, + { 9, 7, 5, -1, 9, 2, 7, -1, 9, 0, 2, -1, 2, 11, 7, -1, -1, -1, -1, -1}, + { 7, 5, 2, -1, 7, 2, 11, -1, 5, 9, 2, -1, 3, 2, 8, -1, 9, 8, 2, -1}, + { 2, 5, 10, -1, 2, 3, 5, -1, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 8, 2, 0, -1, 8, 5, 2, -1, 8, 7, 5, -1, 10, 2, 5, -1, -1, -1, -1, -1}, + { 9, 0, 1, -1, 5, 10, 3, -1, 5, 3, 7, -1, 3, 10, 2, -1, -1, -1, -1, -1}, + { 9, 8, 2, -1, 9, 2, 1, -1, 8, 7, 2, -1, 10, 2, 5, -1, 7, 5, 2, -1}, + { 1, 3, 5, -1, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 8, 7, -1, 0, 7, 1, -1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 9, 0, 3, -1, 9, 3, 5, -1, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 9, 8, 7, -1, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 5, 8, 4, -1, 5, 10, 8, -1, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 5, 0, 4, -1, 5, 11, 0, -1, 5, 10, 11, -1, 11, 3, 0, -1, -1, -1, -1, -1}, + { 0, 1, 9, -1, 8, 4, 10, -1, 8, 10, 11, -1, 10, 4, 5, -1, -1, -1, -1, -1}, + {10, 11, 4, -1, 10, 4, 5, -1, 11, 3, 4, -1, 9, 4, 1, -1, 3, 1, 4, -1}, + { 2, 5, 1, -1, 2, 8, 5, -1, 2, 11, 8, -1, 4, 5, 8, -1, -1, -1, -1, -1}, + { 0, 4, 11, -1, 0, 11, 3, -1, 4, 5, 11, -1, 2, 11, 1, -1, 5, 1, 11, -1}, + { 0, 2, 5, -1, 0, 5, 9, -1, 2, 11, 5, -1, 4, 5, 8, -1, 11, 8, 5, -1}, + { 9, 4, 5, -1, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 2, 5, 10, -1, 3, 5, 2, -1, 3, 4, 5, -1, 3, 8, 4, -1, -1, -1, -1, -1}, + { 5, 10, 2, -1, 5, 2, 4, -1, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 3, 10, 2, -1, 3, 5, 10, -1, 3, 8, 5, -1, 4, 5, 8, -1, 0, 1, 9, -1}, + { 5, 10, 2, -1, 5, 2, 4, -1, 1, 9, 2, -1, 9, 4, 2, -1, -1, -1, -1, -1}, + { 8, 4, 5, -1, 8, 5, 3, -1, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 4, 5, -1, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 8, 4, 5, -1, 8, 5, 3, -1, 9, 0, 5, -1, 0, 3, 5, -1, -1, -1, -1, -1}, + { 9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 4, 11, 7, -1, 4, 9, 11, -1, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 8, 3, -1, 4, 9, 7, -1, 9, 11, 7, -1, 9, 10, 11, -1, -1, -1, -1, -1}, + { 1, 10, 11, -1, 1, 11, 4, -1, 1, 4, 0, -1, 7, 4, 11, -1, -1, -1, -1, -1}, + { 3, 1, 4, -1, 3, 4, 8, -1, 1, 10, 4, -1, 7, 4, 11, -1, 10, 11, 4, -1}, + { 4, 11, 7, -1, 9, 11, 4, -1, 9, 2, 11, -1, 9, 1, 2, -1, -1, -1, -1, -1}, + { 9, 7, 4, -1, 9, 11, 7, -1, 9, 1, 11, -1, 2, 11, 1, -1, 0, 8, 3, -1}, + {11, 7, 4, -1, 11, 4, 2, -1, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 7, 4, -1, 11, 4, 2, -1, 8, 3, 4, -1, 3, 2, 4, -1, -1, -1, -1, -1}, + { 2, 9, 10, -1, 2, 7, 9, -1, 2, 3, 7, -1, 7, 4, 9, -1, -1, -1, -1, -1}, + { 9, 10, 7, -1, 9, 7, 4, -1, 10, 2, 7, -1, 8, 7, 0, -1, 2, 0, 7, -1}, + { 3, 7, 10, -1, 3, 10, 2, -1, 7, 4, 10, -1, 1, 10, 0, -1, 4, 0, 10, -1}, + { 1, 10, 2, -1, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 4, 9, 1, -1, 4, 1, 7, -1, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 4, 9, 1, -1, 4, 1, 7, -1, 0, 8, 1, -1, 8, 7, 1, -1, -1, -1, -1, -1}, + { 4, 0, 3, -1, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 9, 10, 8, -1, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 3, 0, 9, -1, 3, 9, 11, -1, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 1, 10, -1, 0, 10, 8, -1, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 3, 1, 10, -1, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 2, 11, -1, 1, 11, 9, -1, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 3, 0, 9, -1, 3, 9, 11, -1, 1, 2, 9, -1, 2, 11, 9, -1, -1, -1, -1, -1}, + { 0, 2, 11, -1, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 2, 3, 8, -1, 2, 8, 10, -1, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 9, 10, 2, -1, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 2, 3, 8, -1, 2, 8, 10, -1, 0, 1, 8, -1, 1, 10, 8, -1, -1, -1, -1, -1}, + { 1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 3, 8, -1, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1} + }; + + for (i=0; i<12; i++) v[i] = (ints[i])?(ints[i]->v):(NULL); + for (i=0; i<20; i+=4) + { + if (mc_triTable[lu][i] != -1) + { + t[0] = mc_triTable[lu][i]; + t[1] = mc_triTable[lu][i+1]; + t[2] = mc_triTable[lu][i+2]; + if (v[t[0]] && v[t[1]] && v[t[2]]) + { + e1 = tin->CreateEdge(v[t[0]], v[t[1]]); + e2 = tin->CreateEdge(v[t[1]], v[t[2]]); + e3 = tin->CreateEdge(v[t[2]], v[t[0]]); + if (tin->CreateTriangle(e1, e2, e3)==NULL) + { + TMesh::warning("mc_grid::polygonize: triangle failed.\n"); + } + } + else + { + TMesh::warning("mc_grid::polygonize: should not happen.\n"); + for (int i = 0; i < 12; i++) printf("%c ", (ints[i] == NULL) ? ('N') : ((ints[i]->sg) ? ('I') : ('O'))); + printf("\n"); + } + } + } +} + + +bool mc_grid::segmentIntersectsTriangle(Point& ev1, Point& ev2, Triangle *t, Point& op) +{ + coord d1, d2, o1, o2, o3; + Vertex *v1 = t->v1(), *v2 = t->v2(), *v3 = t->v3(); + + d1 = ev1.exactOrientation(v1,v2,v3); + d2 = ev2.exactOrientation(v1,v2,v3); + if ((d1==0 && d2==0) || (d1>0 && d2>0) || (d1<0 && d2<0)) return false; + o1 = ev1.exactOrientation(&ev2, v1, v2); + o2 = ev1.exactOrientation(&ev2, v2, v3); + if ((o1>0 && o2<0) || (o1<0 && o2>0)) return false; + o3 = ev1.exactOrientation(&ev2, v3, v1); + if ((o1>0 && o3<0) || (o1<0 && o3>0)) return false; + if ((o2>0 && o3<0) || (o2<0 && o3>0)) return false; + + //bool orat = coord::use_rationals; + //coord::use_rationals = true; + op = Point::linePlaneIntersection(ev1, ev2, v1, v2, v3); +// coord::use_rationals = orat; + + //d1 = FABS(d1); d2 = FABS(d2); + //op = ((ev1*d2)+(ev2*d1))/(d1+d2); + + return true; +} + +void mc_grid::sample_triangle(Triangle *t) +{ + Vertex *v1 = t->v1(), *v2 = t->v2(), *v3 = t->v3(); + coord minx = MIN(v1->x, MIN(v2->x, v3->x)); minx = ceil(minx); + coord Mx = MAX(v1->x, MAX(v2->x, v3->x)); Mx = floor(Mx); + coord miny = MIN(v1->y, MIN(v2->y, v3->y)); miny = ceil(miny); + coord My = MAX(v1->y, MAX(v2->y, v3->y)); My = floor(My); + coord minz = MIN(v1->z, MIN(v2->z, v3->z)); minz = ceil(minz); + coord Mz = MAX(v1->z, MAX(v2->z, v3->z)); Mz = floor(Mz); + int i, j; + Point p; + UBYTE sg; + Point normal(t->getVector()); + + p = (*v1)+Point(0,0,1); sg = (p.exactOrientation(v1, v2, v3) < 0)?(1):(0); + for (i = TMESH_TO_INT(minx); i <= TMESH_TO_INT(Mx); i++) + for (j = TMESH_TO_INT(miny); j <= TMESH_TO_INT(My); j++) + { + Point P1(i,j,minz-1), P2(i,j,Mz+1); + if (segmentIntersectsTriangle(P1, P2, t, p)) + {xy[i+numrays*j-1-numrays].appendTail(newMcInts(p.z, sg, t)); } + + } + + p = (*v1)+Point(0,1,0); sg = (p.exactOrientation(v1, v2, v3) < 0)?(1):(0); + for (i = TMESH_TO_INT(minx); i <= TMESH_TO_INT(Mx); i++) + for (j = TMESH_TO_INT(minz); j <= TMESH_TO_INT(Mz); j++) + { + Point P1(i,miny-1,j), P2(i,My+1,j); + if (segmentIntersectsTriangle(P1, P2, t, p)) + {xz[i+numrays*j-1-numrays].appendTail(newMcInts(p.y, sg, t)); } + } + + p = (*v1)+Point(1,0,0); sg = (p.exactOrientation(v1, v2, v3) < 0)?(1):(0); + for (i = TMESH_TO_INT(minz); i <= TMESH_TO_INT(Mz); i++) + for (j = TMESH_TO_INT(miny); j <= TMESH_TO_INT(My); j++) + { + Point P1(minx-1,j,i), P2(Mx+1,j,i); + if (segmentIntersectsTriangle(P1, P2, t, p)) + {zy[i+numrays*j-1-numrays].appendTail(newMcInts(p.x, sg, t)); } + } +} + +void mc_grid::sort() +{ + int i,j; + + for (i=0; inumels() < 2) return; + //for (n = l->head(); n != NULL; n = n->next()) if (((mc_ints *)n->data)->sg == 1) in++; else in--; + //if (in==0) + //{ + // for (n = l->head(); n != NULL; n = n->next()) + // { + // mc1 = (mc_ints *)n->data; + // if (mc1->sg == 1) { if (in != 0) mc1->ic = -1; in++; } else if (mc1->sg == 0) { if (in != 1) mc1->ic = -1; in--; } + // } + //} + //// Actual removal from list + //for (n = l->head(); n->next() != NULL;) + //if ((mc1 = (mc_ints *)n->data)->ic == -1) { n = n->next(); l->removeCell(n->prev()); delete mc1; } else n = n->next(); + //if (l->numels() && (mc1 = (mc_ints *)l->tail()->data)->ic == -1) { l->removeCell(l->tail()); delete mc1; } + + + for (int i = 0; i < numcells; i++) count[i] = 0; + + // For each cell + // Keep only first entering intersection and last existing one + + int ac, lc = -1; + if (l->numels() < 2) return; + for (n=l->head(); n != NULL; n=n->next()) + { + mc1 = (mc_ints *)n->data; + if (mc1->sg==1) // Only entering + { + if ((ac = TMESH_TO_INT(floor(mc1->ic))) == lc) mc1->ic = -1; + else lc = ac; + count[ac]++; + } + } + lc = numcells+1; + for (n=l->tail(); n != NULL; n=n->prev()) + { + mc1 = (mc_ints *)n->data; + if (mc1->sg==0) // Only exiting + { + if ((ac = TMESH_TO_INT(floor(mc1->ic))) == lc) mc1->ic = -1; + else lc = ac; + count[ac]--; + } + } + + // Actual removal from list + for (n = l->head(); n->next() != NULL;) + if ((mc1 = (mc_ints *)n->data)->ic == -1) { n = n->next(); l->removeCell(n->prev()); delete mc1; } + else n = n->next(); + if (l->numels() && (mc1 = (mc_ints *)l->tail()->data)->ic == -1) { l->removeCell(l->tail()); delete mc1; } + + // Remove discordant intersections + if (l->numels() < 2) return; + for (n = l->head(); n->next() != NULL; n = n->next()) + { + mc1 = (mc_ints *)n->data; + mc2 = (mc_ints *)n->next()->data; + int mc1c = TMESH_TO_INT(floor(mc1->ic)); + int mc2c = TMESH_TO_INT(floor(mc2->ic)); + if (mc1c == mc2c && mc1->sg != mc2->sg) + { + if ((count[mc1c] >= 0 && mc1->sg == 1) || (count[mc1c] <= 0 && mc1->sg == 0)) mc2->ic = -1; + if ((count[mc2c] >= 0 && mc2->sg == 1) || (count[mc2c] <= 0 && mc2->sg == 0)) mc1->ic = -1; + } + } + //for (n = l->head()->next(); n != l->tail(); n = n->next()) + //{ + // mc1 = (mc_ints *)n->data; + // mc1->ic = -1; + //} + + // Actual removal from list + for (n = l->head(); n->next() != NULL;) + if ((mc1 = (mc_ints *)n->data)->ic == -1) { n = n->next(); l->removeCell(n->prev()); delete mc1; } + else n = n->next(); + if (l->numels() && (mc1 = (mc_ints *)l->tail()->data)->ic == -1) { l->removeCell(l->tail()); delete mc1; } +} + +void mc_grid::purge() +{ + int i,j; + + for (i=0; idata; + trdc = mc->ic; + if (!k) v = tin->newVertex(i, j, trdc); + else if (!j) v = tin->newVertex(i, trdc, k); + else v = tin->newVertex(trdc, k, j); + mc->v = new ExtVertex(v); + tin->V.appendHead(v); + v->info = mc->source; + } +} + +void mc_grid::createVertices() +{ + int i,j; + + for (i=0; iints[i] != NULL) ints[i] = m->ints[i]; + m->x = -1; +} + +List *mc_grid::createCells() +{ + int i,j,k,numcells=numrays+1; + mc_ints *m; + Node *n; + List *ac = new List; + + for (i=0; idata); + k = TMESH_TO_INT(floor(m->ic)); + ac->appendTail(new mc_cell(i,j,k,m,5)); + ac->appendTail(new mc_cell(i,j+1,k,m,1)); + ac->appendTail(new mc_cell(i+1,j,k,m,7)); + ac->appendTail(new mc_cell(i+1,j+1,k,m,3)); + } + FOREACHNODE(xz[i+numrays*j], n) + { + m = ((mc_ints *)n->data); + k = TMESH_TO_INT(floor(m->ic)); + ac->appendTail(new mc_cell(i, k, j, m, 10)); + ac->appendTail(new mc_cell(i,k,j+1,m,9)); + ac->appendTail(new mc_cell(i+1,k,j,m,11)); + ac->appendTail(new mc_cell(i+1,k,j+1,m,8)); + } + FOREACHNODE(zy[i+numrays*j], n) + { + m = ((mc_ints *)n->data); + k = TMESH_TO_INT(floor(m->ic)); + ac->appendTail(new mc_cell(k, j, i, m, 6)); + ac->appendTail(new mc_cell(k,j+1,i,m,2)); + ac->appendTail(new mc_cell(k,j,i+1,m,4)); + ac->appendTail(new mc_cell(k,j+1,i+1,m,0)); + } + } + + ac->sort(mc_cell::compare); + mc_cell *mcc, *last = NULL; + //int x,y,z; + //x=y=z=-1; + + last = (mc_cell *)ac->head()->data; + for (n = ac->head()->next(); n != NULL; n = n->next()) + { + mcc = (mc_cell *)n->data; + if (mcc->x == last->x && mcc->y == last->y && mcc->z == last->z) last->merge(mcc); + else last = mcc; + } + + //FOREACHNODE((*ac), n) + //{ + // mcc = (mc_cell *)n->data; + // if (mcc->x == x && mcc->y == y && mcc->z == z) + // { + // if (last != NULL) + // { + // for (i=0; i<12; i++) if (mcc->ints[i] != NULL) last->ints[i] = mcc->ints[i]; + // } + // x = mcc->x; mcc->x = -1; + // } + // else {x = mcc->x; last = mcc;} + // y = mcc->y; z = mcc->z; + //} + + n = ac->head(); + do + { + mcc = (mc_cell *)n->data; + n = n->next(); + if (mcc->x == -1) { ac->removeCell((n != NULL) ? (n->prev()) : ac->tail()); delete (mcc); } + } while (n != NULL); + + return ac; +} + +void mc_grid::trackOuterHull() +{ + int i, j, numcells = numrays + 1; + mc_ints *m1, *m2; + Edge *e0; + Triangle *t0; + List *ac; + + for (i = 0; inumels()>1) + { + m1 = ((mc_ints *)ac->head()->data); + m2 = ((mc_ints *)ac->tail()->data); + e0 = m1->v->v->e0; t0 = (e0 && e0->t1) ? (e0->t1) : ((e0 && e0->t2) ? (e0->t2) : (NULL)); if (t0 && !IS_VISITED(t0)) tin->selectConnectedComponent(t0); + e0 = m2->v->v->e0; t0 = (e0 && e0->t1) ? (e0->t1) : ((e0 && e0->t2) ? (e0->t2) : (NULL)); if (t0 && !IS_VISITED(t0)) tin->selectConnectedComponent(t0); + } + ac = &xz[i + numrays*j]; + if (ac->numels()>1) + { + m1 = ((mc_ints *)ac->head()->data); + m2 = ((mc_ints *)ac->tail()->data); + e0 = m1->v->v->e0; t0 = (e0 && e0->t1) ? (e0->t1) : ((e0 && e0->t2) ? (e0->t2) : (NULL)); if (t0 && !IS_VISITED(t0)) tin->selectConnectedComponent(t0); + e0 = m2->v->v->e0; t0 = (e0 && e0->t1) ? (e0->t1) : ((e0 && e0->t2) ? (e0->t2) : (NULL)); if (t0 && !IS_VISITED(t0)) tin->selectConnectedComponent(t0); + } + ac = &zy[i + numrays*j]; + if (ac->numels()>1) + { + m1 = ((mc_ints *)ac->head()->data); + m2 = ((mc_ints *)ac->tail()->data); + e0 = m1->v->v->e0; t0 = (e0 && e0->t1) ? (e0->t1) : ((e0 && e0->t2) ? (e0->t2) : (NULL)); if (t0 && !IS_VISITED(t0)) tin->selectConnectedComponent(t0); + e0 = m2->v->v->e0; t0 = (e0 && e0->t1) ? (e0->t1) : ((e0 && e0->t2) ? (e0->t2) : (NULL)); if (t0 && !IS_VISITED(t0)) tin->selectConnectedComponent(t0); + } + } + tin->invertSelection(); + tin->removeSelectedTriangles(); +} + + +mc_grid::mc_grid(Basic_TMesh *_tin, int n) +{ + numrays=n; + xy=new List[n*n]; + xz=new List[n*n]; + zy=new List[n*n]; + tin = _tin; + + Point top; + tin->getBoundingBox(origin, top); + top-=origin; + norm = MAX(top.x,MAX(top.y,top.z)); + norm /= (n+1); + origin -= Point(norm/2, norm/2, norm/2); + norm = MAX(top.x,MAX(top.y,top.z))/numrays; +} + + +void mc_grid::remesh(bool simplify_result) +{ + Vertex *v; + Triangle *t; + Node *n; + Basic_TMesh ntin; + ntin.V.joinTailList(&(tin->V)); + ntin.E.joinTailList(&(tin->E)); + ntin.T.joinTailList(&(tin->T)); + + FOREACHVVVERTEX((&(ntin.V)), v, n) v->setValue(((*v)-origin)/norm); // Shift and normalize + + TMesh::begin_progress(); + int i=0; + FOREACHVTTRIANGLE((&ntin.T), t, n) + { + sample_triangle(t); t->info=NULL; + if (!((i++)%1000)) TMesh::report_progress("%d %% done ",(i*50)/ntin.T.numels()); + } + + sort(); // Sort the intersections + TMesh::report_progress("60 %% done "); + purge(); // Remove unused intersections + TMesh::report_progress("70 %% done "); + createVertices(); // Create the new samples + TMesh::report_progress("80 %% done "); + + // Compute the list of active cells + List *activeCells = createCells(); + TMesh::report_progress("90 %% done "); + mc_cell *c; + while ((c=(mc_cell *)activeCells->popHead())!=NULL) c->polygonize(tin); + TMesh::report_progress("95 %% done "); + + + tin->removeVertices(); + int count = tin->duplicateNonManifoldVertices(); + TMesh::info("Duplicated %d non-manifold vertices.\n",count); + + trackOuterHull(); + + if (simplify_result) FOREACHVVVERTEX((&ntin.V), v, n) v->setValue(((*v)*norm) + origin); // Shift and normalize + FOREACHVVVERTEX((&tin->V), v, n) v->setValue(((*v)*norm) + origin); // Shift and normalize + + tin->safeCoordBackApproximation(); + + if (simplify_result) simplify(); + TMesh::report_progress("99 %% done "); + + FOREACHVVVERTEX((&tin->V), v, n){ + + v->info = NULL; + } + FOREACHVTTRIANGLE((&ntin.T), t, n) if (t->info) { delete ((Point *)t->info); t->info = NULL; } + TMesh::end_progress(); +} + + + + + + + + + + + +bool mc_safeCollapse(Edge *e) +{ + Point orig(e->v2); + List *vt = e->v2->VT(); + Triangle *t; + Node *n; + Point nor; + + nor.setValue((Point *)e->v2->info); + e->v2->setValue(e->v1); + FOREACHVTTRIANGLE(vt, t, n) if (!t->hasEdge(e)) + { + if (((*(t->v3())) + nor).exactOrientation(t->v1(), t->v2(), t->v3()) <= 0) break; + if (t->nextVertex(e->v2)->info != e->v2->info) { if (((*(t->v3())) + (*(((Point *)t->nextVertex(e->v2)->info)))).exactOrientation(t->v1(), t->v2(), t->v3()) <= 0) break; } + if (t->prevVertex(e->v2)->info != e->v2->info) { if (((*(t->v3())) + (*(((Point *)t->prevVertex(e->v2)->info)))).exactOrientation(t->v1(), t->v2(), t->v3()) <= 0) break; } + } + delete vt; + e->v2->setValue(orig); + orig.setValue(e->v1); + return (n == NULL && e->collapse(orig)); +} + +//void mi_saveVRMLBorders(const char *filename, Basic_TMesh& tin) +//{ +// FILE *fp; +// int i; +// if ((fp = fopen(filename, "w")) == NULL) { TMesh::warning("Couldn't save graph!\n"); return; } +// +// fprintf(fp, "#VRML V1.0 ascii\nSeparator {\nMaterial { diffuseColor 1 0 0 }\nCoordinate3 {\npoint [\n"); +// Node *n; +// Edge *e; +// Vertex *v; +// i = 0; +// FOREACHVVVERTEX((&tin.V), v, n) +// { +// v->printPoint(fp); +// v->info = (void *)i; +// i++; +// } +// fprintf(fp, "]\n}\n"); +// +// fprintf(fp, "Material { diffuseColor 0.7 0.7 1 }\n"); +// fprintf(fp, "IndexedLineSet {\ncoordIndex [\n"); +// FOREACHVEEDGE((&tin.E), e, n) if (!IS_SHARPEDGE(e)) +// fprintf(fp, "%ld, %ld, -1,\n", (j_voidint)e->v1->info, (j_voidint)e->v2->info); +// fprintf(fp, "]\n}\n"); +// +// fprintf(fp, "Material { diffuseColor 1 0 0 }\n"); +// fprintf(fp, "IndexedLineSet {\ncoordIndex [\n"); +// FOREACHVEEDGE((&tin.E), e, n) if (IS_SHARPEDGE(e)) +// fprintf(fp, "%ld, %ld, -1,\n", (j_voidint)e->v1->info, (j_voidint)e->v2->info); +// fprintf(fp, "]\n}\n"); +// +// fprintf(fp, "Material { diffuseColor 0 1 0 }\nDrawStyle { pointSize 4 }\n"); +// +// fprintf(fp, "Separator {\nCoordinate3 {\npoint [\n"); +// FOREACHVVVERTEX((&tin.V), v, n) if (IS_VISITED2(v)) v->printPoint(fp); +// fprintf(fp, "]\n}\nPointSet {}\n}\n"); +// +// fprintf(fp, "}\n"); +// fclose(fp); +//} + +//bool mi_getCenterPosition(Triangle *t, Vertex **cp) +//{ +// Vertex *v1 = t->v1(), *v2 = t->v2(), *v3 = t->v3(); +// Triangle *ot1 = (Triangle *)(((Point *)v1->info)->info); +// Triangle *ot2 = (Triangle *)(((Point *)v2->info)->info); +// Triangle *ot3 = (Triangle *)(((Point *)v3->info)->info); +// +// Vertex *cv = ot1->v1(); +// if (!ot2->hasVertex(cv) || !ot3->hasVertex(cv)) cv = ot1->v2(); +// if (!ot2->hasVertex(cv) || !ot3->hasVertex(cv)) cv = ot1->v3(); +// if (!ot2->hasVertex(cv) || !ot3->hasVertex(cv)) return false; +// +// *cp = cv; +// return true; +//} + +//Point mi_getCenterPosition(Edge *e) +//{ +// Vertex *v1 = e->v1, *v2 = e->v2; +// Triangle *ot1 = (Triangle *)(((Point *)v1->info)->info); +// Triangle *ot2 = (Triangle *)(((Point *)v2->info)->info); +// if (ot1->commonEdge(ot2) == NULL) return e->getMidPoint(); +// +// Point nor1((Point *)v1->info), nor2((Point *)v2->info); +// if (nor1*nor2 < 0 || nor1.getAngle(nor2)<0.01) return e->getMidPoint(); +// +// Point nor3 = e->toVector()&nor1; +// Point d; +// d.x = (nor1*(*v1)); +// d.y = (nor2*(*v2)); +// d.z = (nor3*(*v2)); +// return d.linearSystem(nor1, nor2, nor3); +//} + +void mc_grid::simplify() +{ + Vertex *v; + Triangle *t; + Edge *e; + Node *n; + Point *nnor; + + + // This turns info-pointed triangles to info-pointed normals. + // In their turn, normals info-point their triangles. + FOREACHVVVERTEX((&tin->V), v, n) + { + t = ((Triangle *)v->info); + if (t->info != NULL) nnor = (Point *)t->info; + else t->info = nnor = new Point(t->getVector()); + v->info = nnor; + nnor->info = t; + } + + FOREACHVEEDGE((&tin->E), e, n) if (e->isOnBoundary() || e->v1->info != e->v2->info) { MARK_VISIT(e); MARK_VISIT(e->v1); MARK_VISIT(e->v2); } + FOREACHVTTRIANGLE((&tin->T), t, n) if (IS_VISITED(t->e1) && IS_VISITED(t->e2) && IS_VISITED(t->e3)) + { MARK_VISIT2(t->v1()); MARK_VISIT2(t->v2()); MARK_VISIT2(t->v3()); } + + List ies; + FOREACHVEEDGE((&tin->E), e, n) + if (!e->isOnBoundary() && e->v1->info == e->v2->info) ies.appendTail(e); + else if (e->isOnBoundary()) { MARK_VISIT2(e->v1); MARK_VISIT2(e->v2); } + Point nor; + int c; + do + { + c = 0; + FOREACHVEEDGE((&ies), e, n) if (e->isLinked() && !e->isOnBoundary() && e->v1->info == e->v2->info) + { + if (IS_VISITED2(e->v1) && IS_VISITED2(e->v2)) continue; + if (IS_VISITED(e->v1) && IS_VISITED(e->v2) && e->t1->oppositeVertex(e)->info == e->v1->info && e->t2->oppositeVertex(e)->info == e->v1->info) continue; + + if (IS_VISITED2(e->v2)) e->invert(); + else if (IS_VISITED(e->v2) && !IS_VISITED2(e->v1)) e->invert(); + if (mc_safeCollapse(e)) c++; + } + + //FOREACHVEEDGE((&ies), e, n) if (e->isLinked() && !(IS_VISITED(e->v1) && IS_VISITED(e->v2))) + //{ + // double a = e->delaunayMinAngle(); + // nor.setValue((Point *)e->v2->info); + // if (e->swap()) { if ((e->t1->getVector()*nor<0) || (e->t2->getVector()*nor<0) || e->delaunayMinAngle() <= a) e->swap(true); } + //} + } while (c); + + tin->removeUnlinkedElements(); + tin->deselectTriangles(); + FOREACHVEEDGE((&tin->E), e, n) e->mask = 0; + FOREACHVVVERTEX((&tin->V), v, n) v->mask = 0; +} + +} //namespace T_MESH diff --git a/src/mesh_fix/src/Algorithms/subdivision.cpp b/src/mesh_fix/src/Algorithms/subdivision.cpp new file mode 100644 index 000000000..5f7198f73 --- /dev/null +++ b/src/mesh_fix/src/Algorithms/subdivision.cpp @@ -0,0 +1,168 @@ +/**************************************************************************** +* TMesh * +* * +* Consiglio Nazionale delle Ricerche * +* Istituto di Matematica Applicata e Tecnologie Informatiche * +* Sezione di Genova * +* IMATI-GE / CNR * +* * +* Authors: Marco Attene * +* Copyright(C) 2012: IMATI-GE / CNR * +* All rights reserved. * +* * +* This program is dual-licensed as follows: * +* * +* (1) You may use TMesh as 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 3 of the License, or * +* (at your option) any later version. * +* In this case the 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 (http://www.gnu.org/licenses/gpl.txt) * +* for more details. * +* * +* (2) You may use TMesh as part of a commercial software. In this case a * +* proper agreement must be reached with the Authors and with IMATI-GE/CNR * +* based on a proper licensing contract. * +* * +****************************************************************************/ + +#include "tmesh.h" + +using namespace T_MESH; + +//////////////////// Loop's Subdivision Scheme ////////////////// + +class loopSplit +{ + public: + Edge *e; + Point p; + + loopSplit(Edge *_e, int md) + { + e = _e; + if (e->t1 == NULL || e->t2 == NULL || md) p = ((*e->v1)+(*e->v2))/2.0; + else + { + Vertex *ov1 = e->t1->oppositeVertex(e); + Vertex *ov2 = e->t2->oppositeVertex(e); + p = ((((*e->v1)+(*e->v2))*3.0)+((*ov1)+(*ov2)))/8.0; + } + } +}; + +double subsurfbeta_loop(int k) +{ + double beta = (cos((2.0*M_PI)/((double)k))/4.0)+(3.0/8.0); + return ((5.0/8.0)-(beta*beta))/((double)k); +} + + +void loopRelaxOriginal(Vertex *v) +{ + Node *n; + Edge *e; + List *ve; + Point np; + int k; + double beta; + + if (v->isOnBoundary()) + { + ve = v->VE(); + np = (*(((Edge *)ve->head()->data)->oppositeVertex(v))); + np += (*(((Edge *)ve->tail()->data)->oppositeVertex(v))); + np = (((*v)*6.0)+np)/8.0; + delete(ve); + } + else + { + ve = v->VE(); + k = ve->numels(); + beta = subsurfbeta_loop(k); + FOREACHVEEDGE(ve, e, n) np += (*(e->oppositeVertex(v))); + np = ((*v)*(1.0-k*beta))+(np*beta); + delete(ve); + } + + v->info = new Point(&np); +} + + +//// Performs one subdivision step using Loop's scheme //// +//// If 'md' is set the method does not alter the geometry //// + +void Basic_TMesh::loopSubdivision(bool midpoint) +{ + List nvs; + Edge *e, *e1, *e2; + Node *n; + Vertex *v; + Triangle *t; + loopSplit *ls; + int k, detected_sharp=0, is_selection=0; + if (!midpoint) FOREACHVERTEX(v, n) loopRelaxOriginal(v); + + FOREACHTRIANGLE(t, n) if (IS_VISITED(t)) {is_selection=1; break;} + if (is_selection) {FOREACHTRIANGLE(t, n) if (IS_VISITED(t)) {MARK_BIT(t->e1, 3); MARK_BIT(t->e2, 3); MARK_BIT(t->e3, 3);}} + else FOREACHEDGE(e, n) MARK_BIT(e, 3); + + FOREACHEDGE(e, n) if (IS_BIT(e, 3)) + { + MARK_BIT(e->v1, 3); MARK_BIT(e->v2, 3); + if (!midpoint && IS_SHARPEDGE(e)) detected_sharp = 1; + nvs.appendHead(new loopSplit(e, midpoint)); + } + + if (detected_sharp) + TMesh::warning("loopSubdivision: Crease-preservation is not supported.\n"); + + FOREACHNODE(nvs, n) + { + ls = ((loopSplit *)n->data); + k = ls->e->isOnBoundary(); + v = splitEdge(ls->e, &(ls->p)); + e1 = (Edge *)E.head()->data; + e2 = (Edge *)E.head()->next()->data; + MARK_VISIT2(v); MARK_VISIT2(e1); if (!k) MARK_VISIT2(e2); + + if (ls->e->t2) + { + if (IS_VISITED(ls->e->t2)) {t=(Triangle *)T.head()->data; MARK_VISIT(t);} + if (ls->e->t1 && IS_VISITED(ls->e->t1)) {t=(Triangle *)T.head()->next()->data; MARK_VISIT(t);} + } + else if (IS_VISITED(ls->e->t1)) {t=(Triangle *)T.head()->data; MARK_VISIT(t);} + + if (IS_SHARPEDGE(ls->e)) + { + if (k) TAG_SHARPEDGE(e2); + else TAG_SHARPEDGE((Edge *)E.head()->next()->next()->data); + } + } + + nvs.freeNodes(); + + FOREACHEDGE(e, n) + if (IS_VISITED2(e)) + { + UNMARK_VISIT2(e); + if ((IS_VISITED2(e->v1) && !IS_VISITED2(e->v2)) || + (!IS_VISITED2(e->v1) && IS_VISITED2(e->v2))) + if (e->swap(1) && (!IS_VISITED2(e->v1) || !IS_VISITED2(e->v2))) e->swap(1); + } + + FOREACHVERTEX(v, n) if (!IS_VISITED2(v) && !midpoint && IS_BIT(v, 3)) + { + v->setValue((Point *)v->info); + delete((Point *)v->info); + v->info = NULL; + } + FOREACHVERTEX(v, n) {UNMARK_VISIT2(v); UNMARK_BIT(v, 3);} + + if (detected_sharp) + TMesh::warning("loopSubdivision: Tagged sharp edges have been smoothed.\n"); + + FOREACHEDGE(e, n) UNMARK_BIT(e, 3); +} diff --git a/src/mesh_fix/src/Kernel/coordinates.cpp b/src/mesh_fix/src/Kernel/coordinates.cpp new file mode 100644 index 000000000..6c5ffd796 --- /dev/null +++ b/src/mesh_fix/src/Kernel/coordinates.cpp @@ -0,0 +1,241 @@ +/**************************************************************************** +* TMesh * +* * +* Consiglio Nazionale delle Ricerche * +* Istituto di Matematica Applicata e Tecnologie Informatiche * +* Sezione di Genova * +* IMATI-GE / CNR * +* * +* Authors: Marco Attene * +* Copyright(C) 2012: IMATI-GE / CNR * +* All rights reserved. * +* * +* This program is dual-licensed as follows: * +* * +* (1) You may use TMesh as 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 3 of the License, or * +* (at your option) any later version. * +* In this case the 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 (http://www.gnu.org/licenses/gpl.txt) * +* for more details. * +* * +* (2) You may use TMesh as part of a commercial software. In this case a * +* proper agreement must be reached with the Authors and with IMATI-GE/CNR * +* based on a proper licensing contract. * +* * +****************************************************************************/ + +#include "coordinates.h" +#include +#include + + +namespace T_MESH +{ +#ifndef NAN +#define NAN std::numeric_limits::quiet_NaN() +#endif + +#ifdef USE_HYBRID_KERNEL + +// Default behaviour = FILTERED KERNEL +bool PM_Rational::use_rationals = false; +bool PM_Rational::use_filtering = true; + +void PM_Rational::switchToDouble() +{ + if (_whv) + { + EXACT_NT * ov = (EXACT_NT *)_val; + double d = (double)((EXACT_NT_DENOMINATOR(ov) != 0) ? (EXACT_NT_TO_DOUBLE((*ov))) : (NAN)); + _val = d2int64t(d); + delete ov; + _whv = false; + } +} + +void PM_Rational::switchToRational() +{ + if (!_whv) + { + double od = int64t2d(_val); + if (od == NAN) _val = (int64_t)new EXACT_NT(0, 0); + else _val = (int64_t)new EXACT_NT(od); + _whv = true; + } +} + + void PM_Rational::operator+=(const PM_Rational& a) +{ + if (use_rationals) { switchToRational(); getVal() += a.toRational(); } + else { switchToDouble(); getDVal() += a.toDouble(); } +} + + void PM_Rational::operator-=(const PM_Rational& a) +{ + if (use_rationals) { switchToRational(); getVal() -= a.toRational(); } + else { switchToDouble(); getDVal() -= a.toDouble(); } +} + + void PM_Rational::operator*=(const PM_Rational& a) +{ + if (use_rationals) { switchToRational(); getVal() *= a.toRational(); } + else { switchToDouble(); getDVal() *= a.toDouble(); } +} + + void PM_Rational::operator/=(const PM_Rational& a) +{ + if (use_rationals) { switchToRational(); getVal() /= a.toRational(); } + else { switchToDouble(); getDVal() /= a.toDouble(); } +} + + PM_Rational PM_Rational::operator+(const PM_Rational& a) const +{ + if (use_rationals) return PM_Rational(toRational() + a.toRational()); + else return PM_Rational(toDouble() + a.toDouble()); +} + + PM_Rational PM_Rational::operator-(const PM_Rational& a) const +{ + if (use_rationals) return PM_Rational(toRational() - a.toRational()); + else return PM_Rational(toDouble() - a.toDouble()); +} + + PM_Rational PM_Rational::operator*(const PM_Rational& a) const +{ + if (use_rationals) return PM_Rational(toRational() * a.toRational()); + else return PM_Rational(toDouble() * a.toDouble()); +} + + PM_Rational PM_Rational::operator/(const PM_Rational& a) const +{ + if (use_rationals) return PM_Rational(toRational() / a.toRational()); + else return PM_Rational(toDouble() / a.toDouble()); +} + + bool PM_Rational::operator==(const PM_Rational& a) const +{ + if (_whv || a._whv /*use_rationals*/) return (toRational() == a.toRational()); + else return (toDouble() == a.toDouble()); + } + + bool PM_Rational::operator!=(const PM_Rational& a) const +{ + if (_whv || a._whv /*use_rationals*/) return (toRational() != a.toRational()); + else return (toDouble() != a.toDouble()); +} + + PM_Rational& PM_Rational::operator=(const PM_Rational& a) + { + if (_whv) delete ((EXACT_NT *)_val); + _whv = a._whv; _val = (_whv) ? ((int64_t)new EXACT_NT(a.getVal())) : (a._val); + return *this; + } + + void PM_Rational::setFromRational(const EXACT_NT& a) + { + if (_whv) delete ((EXACT_NT *)_val); + _whv = 1; _val = (int64_t)new EXACT_NT(a); + } + + bool PM_Rational::operator<(const PM_Rational& a) const + { + if (_whv || a._whv) return (toRational() < a.toRational()); + else return (toDouble() < a.toDouble()); + } + +bool PM_Rational::operator>(const PM_Rational& a) const +{ + if (_whv || a._whv) return (toRational() > a.toRational()); + else return (toDouble() > a.toDouble()); +} + +PM_Rational operator-(const PM_Rational& a) // This might be probably changed... do not understand why to switch.. +{ + if (PM_Rational::isUsingRationals()) return PM_Rational(-(a.toRational())); + else return PM_Rational(-(a.toDouble())); +} + +PM_Rational ceil(const PM_Rational& a) +{ + if (PM_Rational::isUsingRationals()) + { + mpz_t n, d, f; + mpz_init(n); mpz_init(d); mpz_init(f); +#ifdef USE_CGAL_LAZYNT + mpz_set(n, a.toRational().exact().numerator().mpz()); + mpz_set(d, a.toRational().exact().denominator().mpz()); + mpz_cdiv_q(f, n, d); + mpz_clear(n); mpz_clear(d); + return PM_Rational(EXACT_NT(CGAL::Gmpz(f))); +#else + mpz_set(n, a.toRational().get_num_mpz_t()); + mpz_set(d, a.toRational().get_den_mpz_t()); + mpz_cdiv_q(f, n, d); + mpz_clear(n); mpz_clear(d); + return PM_Rational(mpq_class(mpz_class(f))); +#endif + } + else + return PM_Rational(::ceil(a.toDouble())); +} + +PM_Rational floor(const PM_Rational& a) +{ + if (PM_Rational::isUsingRationals()) + { + mpz_t n, d, f; + mpz_init(n); mpz_init(d); mpz_init(f); +#ifdef USE_CGAL_LAZYNT + mpz_set(n, a.toRational().exact().numerator().mpz()); + mpz_set(d, a.toRational().exact().denominator().mpz()); + mpz_fdiv_q(f, n, d); + mpz_clear(n); mpz_clear(d); + return PM_Rational(EXACT_NT(CGAL::Gmpz(f))); +#else + mpz_set(n, a.toRational().get_num_mpz_t()); + mpz_set(d, a.toRational().get_den_mpz_t()); + mpz_fdiv_q(f, n, d); + mpz_clear(n); mpz_clear(d); + return PM_Rational(mpq_class(mpz_class(f))); +#endif + } else + return PM_Rational(::floor(a.toDouble())); +} + +PM_Rational round(const PM_Rational& a) +{ + if (PM_Rational::isUsingRationals()) + { + mpz_t n, d, f, c; + mpz_init(n); mpz_init(d); mpz_init(f); mpz_init(c); +#ifdef USE_CGAL_LAZYNT + mpz_set(n, a.toRational().exact().numerator().mpz()); + mpz_set(d, a.toRational().exact().denominator().mpz()); + mpz_fdiv_q(f, n, d); + mpz_cdiv_q(c, n, d); + mpz_clear(n); mpz_clear(d); + PM_Rational fr = PM_Rational(EXACT_NT(CGAL::Gmpz(f))); + PM_Rational cr = PM_Rational(EXACT_NT(CGAL::Gmpz(c))); + mpz_clear(f); mpz_clear(c); + return ((a - fr) < (cr - a)) ? (fr) : (cr); +#else + mpz_set(n, a.toRational().get_num_mpz_t()); + mpz_set(d, a.toRational().get_den_mpz_t()); + mpz_fdiv_q(f, n, d); + mpz_cdiv_q(c, n, d); + mpz_clear(n); mpz_clear(d); + PM_Rational fr = PM_Rational(mpq_class(mpz_class(f))); + PM_Rational cr = PM_Rational(mpq_class(mpz_class(c))); + mpz_clear(f); mpz_clear(c); + return ((a - fr) < (cr - a)) ? (fr) : (cr); +#endif + } else + return PM_Rational(::round(a.toDouble())); +} + +#endif +} //namespace T_MESH diff --git a/src/mesh_fix/src/Kernel/graph.cpp b/src/mesh_fix/src/Kernel/graph.cpp new file mode 100644 index 000000000..d8fc5b084 --- /dev/null +++ b/src/mesh_fix/src/Kernel/graph.cpp @@ -0,0 +1,219 @@ +/**************************************************************************** +* TMesh * +* * +* Consiglio Nazionale delle Ricerche * +* Istituto di Matematica Applicata e Tecnologie Informatiche * +* Sezione di Genova * +* IMATI-GE / CNR * +* * +* Authors: Marco Attene * +* Copyright(C) 2012: IMATI-GE / CNR * +* All rights reserved. * +* * +* This program is dual-licensed as follows: * +* * +* (1) You may use TMesh as 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 3 of the License, or * +* (at your option) any later version. * +* In this case the 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 (http://www.gnu.org/licenses/gpl.txt) * +* for more details. * +* * +* (2) You may use TMesh as part of a commercial software. In this case a * +* proper agreement must be reached with the Authors and with IMATI-GE/CNR * +* based on a proper licensing contract. * +* * +****************************************************************************/ + +#include "graph.h" + +namespace T_MESH +{ + +graphEdge *graphNode::getEdge(graphNode *gn) +{ + graphEdge *ge; + Node *n = edges.head(); + while (n!=NULL) + if ((ge=((graphEdge *)n->data))->oppositeNode(this)==gn) return ge; + else n=n->next(); + return NULL; +} + +graphEdge::graphEdge(graphNode *a, graphNode *b) +{ + n1=a; n2=b; + n1->edges.appendHead(this); + n2->edges.appendHead(this); +} + + +graphEdge *Graph::createEdge(graphNode *n1, graphNode *n2) +{ + Node *n; + FOREACHNODE(n1->edges, n) + if (((graphEdge *)n->data)->hasNode(n2)) + return (graphEdge *)n->data; + + edges.appendHead(new graphEdge(n1, n2)); + return (graphEdge *)edges.head()->data; +} + + +void graphEdge::collapse() +{ + Node *n; + graphEdge *e; + graphNode *nx; + + while ((e = (graphEdge *)n2->edges.popHead()) != NULL) + if (e != this) + { + ((e->n1 == n2)?(e->n1):(e->n2)) = n1; + n1->edges.appendHead(e); + } + + FOREACHNODE(n1->edges, n) + { + e = (graphEdge *)n->data; + if (!e->isUnlinked()) e->oppositeNode(n1)->mask = 0; + } + + n2->mask = 1; + FOREACHNODE(n1->edges, n) + { + e = (graphEdge *)n->data; + if (e != this) + { + nx = e->oppositeNode(n1); + if (nx->mask) {nx->edges.removeNode(e); e->makeUnlinked();} + nx->mask = 1; + } + } + + n = n1->edges.head(); + while (n != NULL) + { + e = (graphEdge *)n->data; + n = n->next(); + if (e->isUnlinked()) n1->edges.removeCell((n!=NULL)?(n->prev()):n1->edges.tail()); + } + + FOREACHNODE(n1->edges, n) + ((graphEdge *)n->data)->oppositeNode(n1)->mask = 0; + + n1->edges.removeNode(this); + + makeUnlinked(); +} + + +Graph::~Graph() +{ + graphNode *gn; + graphEdge *ge; + while ((gn=(graphNode *)nodes.popHead())!=NULL) delete gn; + while ((ge=(graphEdge *)edges.popHead())!=NULL) delete ge; +} + +void Graph::deleteUnlinkedElements() +{ + Node *n; + graphNode *gn; + graphEdge *ge; + + n = nodes.head(); + while (n != NULL) + { + gn = (graphNode *)n->data; + n = n->next(); + if (gn->isIsolated()) + { + nodes.removeCell((n!=NULL)?(n->prev()):nodes.tail()); + delete(gn); + } + } + + n = edges.head(); + while (n != NULL) + { + ge = (graphEdge *)n->data; + n = n->next(); + if (ge->isUnlinked()) + { + edges.removeCell((n!=NULL)?(n->prev()):edges.tail()); + delete(ge); + } + } +} + +void Graph::unlinkEdge(graphEdge *e) +{ + e->n1->edges.removeNode(e); + e->n2->edges.removeNode(e); + e->makeUnlinked(); +} + +void Graph::destroyEdge(graphEdge *e) +{ + unlinkEdge(e); + edges.removeNode(e); + delete e; +} + +graphNode *Graph::unlinkNode(graphNode *a) +{ + graphEdge *e; + while ((e = (graphEdge *)a->edges.popHead())!=NULL) unlinkEdge(e); + return a; +} + +void graphEdge::invert() +{ + graphNode *tmp = n1; + n1 = n2; + n2 = tmp; +} + +bool Graph::isConnected() +{ + if (nodes.numels() < 2) return true; + + unsigned char *nmask = new unsigned char[nodes.numels()]; + Node *n; + graphNode *p, *q; + int i; + + for (i=0, n=nodes.head(); n!=NULL; n=n->next(), i++) + { + p = (graphNode *)n->data; + nmask[i]=p->mask; + p->mask=0; + } + + p = (graphNode *)nodes.head()->data; + List todo(p); p->mask = 1; + while ((p = (graphNode *)todo.popHead())!=NULL) + { + for (n=p->edges.head(); n!=NULL; n=n->next()) + { + q = ((graphEdge *)n->data)->oppositeNode(p); + if (q->mask==0) {todo.appendTail(q); q->mask=1;} + } + } + + bool is_connected = true; + for (i=0, n=nodes.head(); n!=NULL; n=n->next(), i++) + { + p = (graphNode *)n->data; + if (p->mask==0) is_connected=false; + p->mask = nmask[i]; + } + + return is_connected; +} + +} //namespace T_MESH diff --git a/src/mesh_fix/src/Kernel/heap.cpp b/src/mesh_fix/src/Kernel/heap.cpp new file mode 100644 index 000000000..c488251a6 --- /dev/null +++ b/src/mesh_fix/src/Kernel/heap.cpp @@ -0,0 +1,122 @@ +/**************************************************************************** +* TMesh * +* * +* Consiglio Nazionale delle Ricerche * +* Istituto di Matematica Applicata e Tecnologie Informatiche * +* Sezione di Genova * +* IMATI-GE / CNR * +* * +* Authors: Marco Attene * +* Copyright(C) 2012: IMATI-GE / CNR * +* All rights reserved. * +* * +* This program is dual-licensed as follows: * +* * +* (1) You may use TMesh as 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 3 of the License, or * +* (at your option) any later version. * +* In this case the 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 (http://www.gnu.org/licenses/gpl.txt) * +* for more details. * +* * +* (2) You may use TMesh as part of a commercial software. In this case a * +* proper agreement must be reached with the Authors and with IMATI-GE/CNR * +* based on a proper licensing contract. * +* * +****************************************************************************/ + +#include +#include +#include "heap.h" + +namespace T_MESH +{ + +abstractHeap::abstractHeap(int size) +{ + heap = new void *[size+1]; + numels = 0; + maxels = size; + positions = NULL; +} + +abstractHeap::~abstractHeap() +{ + delete(heap); +} + +int abstractHeap::upheap(int k) +{ + if (k < 2) return k; + + void *t = heap[k]; + int fk = (k%2)?((k-1)/2):(k/2); + void *f = heap[fk]; + + if (compare(t, f) <= 0) + { + heap[k] = f; + heap[fk] = t; + if (positions != NULL) + { + positions[(j_voidint)f] = k; + positions[(j_voidint)t] = fk; + } + return upheap(fk); + } + return k; +} + +int abstractHeap::downheap(int k) +{ + int j; + + void *t = heap[k]; + int fk = (numels%2)?((numels-1)/2):(numels/2); + if (k > fk) return k; + + j = k+k; + if (j < numels && compare(heap[j], heap[j+1]) >= 0) j++; + void *f = heap[j]; + if (compare(t, f) >= 0) + { + heap[k] = f; + heap[j] = t; + if (positions != NULL) + { + positions[(j_voidint)f] = k; + positions[(j_voidint)t] = j; + } + return downheap(j); + } + + return k; +} + +int abstractHeap::insert(void *t) +{ + if (numels == maxels) return -1; + + heap[++numels] = t; + if (positions != NULL) positions[(j_voidint)t] = numels; + return upheap(numels); +} + +void *abstractHeap::removeHead() +{ + void *t = heap[1]; + if (positions != NULL) positions[(j_voidint)t] = 0; + heap[1] = heap[numels--]; + if (numels) + { + if (positions != NULL) positions[(j_voidint)heap[1]] = 1; + downheap(1); + } + + return t; +} + +} //namespace T_MESH diff --git a/src/mesh_fix/src/Kernel/jqsort.cpp b/src/mesh_fix/src/Kernel/jqsort.cpp new file mode 100644 index 000000000..554cbc9e5 --- /dev/null +++ b/src/mesh_fix/src/Kernel/jqsort.cpp @@ -0,0 +1,89 @@ +/**************************************************************************** +* TMesh * +* * +* Consiglio Nazionale delle Ricerche * +* Istituto di Matematica Applicata e Tecnologie Informatiche * +* Sezione di Genova * +* IMATI-GE / CNR * +* * +* Authors: Marco Attene * +* Copyright(C) 2012: IMATI-GE / CNR * +* All rights reserved. * +* * +* This program is dual-licensed as follows: * +* * +* (1) You may use TMesh as 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 3 of the License, or * +* (at your option) any later version. * +* In this case the 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 (http://www.gnu.org/licenses/gpl.txt) * +* for more details. * +* * +* (2) You may use TMesh as part of a commercial software. In this case a * +* proper agreement must be reached with the Authors and with IMATI-GE/CNR * +* based on a proper licensing contract. * +* * +****************************************************************************/ + +#define USE_STD_SORT + +#ifdef USE_STD_SORT +#include +#include +#endif + +#include "basics.h" + +namespace T_MESH +{ + +#ifndef USE_STD_SORT +inline void jswap(void *v[], int i, int j) +{ + void *temp = v[i]; + v[i] = v[j]; + v[j] = temp; +} + +void jqsort_prv(void *v[], int left, int right, int (*comp)(const void *, const void *)) +{ + register int i, last; + + if (left >= right) return; + jswap(v, left, (left+right)/2); + last = left; + for (i = left+1; i <= right; i++) + if ((*comp)(v[i], v[left]) < 0) jswap(v, ++last, i); + jswap(v, left, last); + jqsort_prv(v, left, last-1, comp); + jqsort_prv(v, last+1, right, comp); +} + +void jqsort(void *v[], int numels, int(*comp)(const void *, const void *)) +{ + jqsort_prv(v, 0, numels-1, comp); +} +#else + +class compobj +{ + int(*comp)(const Data *, const Data *); + +public: + compobj(int(*c)(const Data *, const Data *)) { comp = c; } + + bool operator()(Data *a, Data *b) { return (comp(a, b) < 0); } +}; + +void jqsort(Data *v[], int numels, int(*comp)(const Data *, const Data *)) +{ + compobj a(comp); + std::sort(v, v + numels, a); +} +#endif + +} //namespace T_MESH + diff --git a/src/mesh_fix/src/Kernel/list.cpp b/src/mesh_fix/src/Kernel/list.cpp new file mode 100644 index 000000000..d5a0e3764 --- /dev/null +++ b/src/mesh_fix/src/Kernel/list.cpp @@ -0,0 +1,301 @@ +/**************************************************************************** +* TMesh * +* * +* Consiglio Nazionale delle Ricerche * +* Istituto di Matematica Applicata e Tecnologie Informatiche * +* Sezione di Genova * +* IMATI-GE / CNR * +* * +* Authors: Marco Attene * +* Copyright(C) 2012: IMATI-GE / CNR * +* All rights reserved. * +* * +* This program is dual-licensed as follows: * +* * +* (1) You may use TMesh as 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 3 of the License, or * +* (at your option) any later version. * +* In this case the 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 (http://www.gnu.org/licenses/gpl.txt) * +* for more details. * +* * +* (2) You may use TMesh as part of a commercial software. In this case a * +* proper agreement must be reached with the Authors and with IMATI-GE/CNR * +* based on a proper licensing contract. * +* * +****************************************************************************/ + +#include +#include +#include "list.h" +#include "jqsort.h" + +namespace T_MESH +{ + +// Create a new node containing 'd', and link it to // +// 'p' on the left (prev) and to 'n' on the right (next). // + +Node::Node(const Node *p, const Data *d, const Node *n) +{ + data=(Data *)d; + if ((n_prev=(Node *)p) != NULL) n_prev->n_next = this; + if ((n_next=(Node *)n) != NULL) n_next->n_prev = this; +} + + +// Destroy and unlink the node + +Node::~Node() +{ + if (n_prev != NULL) n_prev->n_next = n_next; + if (n_next != NULL) n_next->n_prev = n_prev; +} + + +/////////// Constructor from list /////////////////// + +List::List(const Data **d, int n) +{ + l_head = l_tail = NULL; l_numels = 0; + for (int i=0; inext()); + if (b == l_tail) l_tail = nn; + l_numels++; +} + +////////////////// Appends a list ////////////////// + +void List::appendList(const List *l) +{ + Node *n = l->l_tail; + + while (n != NULL) + { + appendHead(n->data); + n=n->prev(); + } +} + +////////////////// Joins a list to the l_tail ////////////////// + +void List::joinTailList(List *l) +{ + if (l->l_numels == 0) return; + if (l_tail != NULL) + { + l_tail->n_next = l->l_head; l->l_head->n_prev = l_tail; l_tail = l->l_tail; + l_numels += l->l_numels; + } + else + { + l_head = l->l_head; l_tail = l->l_tail; l_numels = l->l_numels; + } + l->l_head = l->l_tail = NULL; l->l_numels = 0; +} + +//// Moves node 'n' from this list to the end of 'l'. \n O(1). +void List::moveNodeTo(Node *n, List *l) +{ + Node *pn = n->n_prev, *nn = n->n_next; + n->n_prev = l->l_tail; n->n_next = NULL; + + if (l->l_numels) l->l_tail->n_next = n; else l->l_head = n; + l->l_tail = n; + l->l_numels++; + + l_numels--; + if (pn != NULL) pn->n_next = nn; else l_head = nn; + if (nn != NULL) nn->n_prev = pn; else l_tail = pn; +} + +//// Removes the first node and returns the corresponding data ///// + +Data *List::popHead() +{ + Data *data = (l_head != NULL)?(l_head->data):(NULL); + if (l_head != NULL) removeCell(l_head); + return data; +} + +//// Removes the last node and returns the corresponding data ///// + +Data *List::popTail() +{ + Data *data = (l_tail != NULL)?(l_tail->data):(NULL); + if (l_tail != NULL) removeCell(l_tail); + return data; +} + +//////////////////// Removes an element ////////////////// + +int List::removeNode(const Data *d) +{ + Node *tmp = l_head; + int i=1; + + while (tmp != NULL) + if (tmp->data == d) + { + removeCell(tmp); + return i; + } + else {tmp=tmp->n_next; i++;} + + return 0; +} + + +//////////////////// Removes an element ////////////////// + +int List::removeNode(int i) +{ + Node *tmp = l_head; + + while (tmp!=NULL && i--) tmp=tmp->n_next; + if (tmp==NULL) return 0; + + removeCell(tmp); + return 1; +} + + +//////////////////// Gets a node ////////////////// + +Node *List::getNode(int i) const +{ + Node *tmp = l_head; + + while (tmp!=NULL && i--) tmp=tmp->n_next; + return tmp; +} + + +//////////////////// Removes a node ////////////////// + +void List::removeCell(Node *n) +{ + if (n==l_head) l_head = n->n_next; + if (n==l_tail) l_tail = n->n_prev; + delete(n); + l_numels--; +} + + +////////////////// Garbage collection ////////////// + +void List::freeCell(Node *n) +{ + delete(n->data); + removeCell(n); +} + +void List::freeNode(Data *d) +{ + delete(d); + removeNode(d); +} + +//////////////////// Belonging check ///////////////// + +Node *List::containsNode(const Data *d) const +{ + Node *tmp = l_head; + + while (tmp != NULL) + if (tmp->data == d) return tmp; + else tmp=tmp->n_next; + + return NULL; +} + +//////////////////// Replaces a node ///////////////// + +Node *List::replaceNode(const Data *od, const Data *nd) +{ + Node *tmp = containsNode(od); + if (tmp != NULL) { tmp->data = (Data *)nd; return tmp;} + appendTail(nd); + return l_tail; +} + +//////////////////////// Garbage collector ///////////////////// + +void List::freeNodes() +{ + while (l_head != NULL) freeCell(l_head); +} + +//////////////////////// Garbage collector ///////////////////// + +void List::removeNodes() +{ + while (l_head != NULL) removeCell(l_head); +} + + +///// Conversion to array /////// + +Data **List::toArray() const +{ + Node *n = l_head; + int i; + Data **array; + + if (l_numels == 0) return NULL; + array = (Data **)malloc(sizeof(Data *)*l_numels); + if (array == NULL) return NULL; + for (i=0; in_next) array[i] = n->data; + + return array; +} + +///// Sorts the list ///////// + +int List::sort(int (*comp)(const Data *, const Data *)) +{ + Data **array; + int ne = l_numels-1; + + if (l_numels < 2) return 0; + if ((array = toArray()) == NULL) return 1; + + jqsort(array, l_numels, comp); + removeNodes(); + for (; ne >= 0; ne--) appendHead(array[ne]); + free(array); + + return 0; +} + +} //namespace T_MESH diff --git a/src/mesh_fix/src/Kernel/matrix.cpp b/src/mesh_fix/src/Kernel/matrix.cpp new file mode 100644 index 000000000..0ff18e249 --- /dev/null +++ b/src/mesh_fix/src/Kernel/matrix.cpp @@ -0,0 +1,794 @@ +/**************************************************************************** +* TMesh * +* * +* Consiglio Nazionale delle Ricerche * +* Istituto di Matematica Applicata e Tecnologie Informatiche * +* Sezione di Genova * +* IMATI-GE / CNR * +* * +* Authors: Marco Attene * +* Copyright(C) 2012: IMATI-GE / CNR * +* All rights reserved. * +* * +* This program is dual-licensed as follows: * +* * +* (1) You may use TMesh as 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 3 of the License, or * +* (at your option) any later version. * +* In this case the 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 (http://www.gnu.org/licenses/gpl.txt) * +* for more details. * +* * +* (2) You may use TMesh as part of a commercial software. In this case a * +* proper agreement must be reached with the Authors and with IMATI-GE/CNR * +* based on a proper licensing contract. * +* * +****************************************************************************/ + +#include +#include "matrix.h" + +namespace T_MESH +{ + +#define FABS(a) (((a)<0)?(-(a)):((a))) + +////////////////////////////////////////////////////////////////////////// +// +// Generic 3x3 matrix +// +////////////////////////////////////////////////////////////////////////// + +// Plain constructor + +Matrix3x3::Matrix3x3(const double& a11, const double& a12, const double& a13, + const double& a21, const double& a22, const double& a23, + const double& a31, const double& a32, const double& a33) +{ + M[0] = a11; M[1] = a12; M[2] = a13; + M[3] = a21; M[4] = a22; M[5] = a23; + M[6] = a31; M[7] = a32; M[8] = a33; +} + + +// Matrix T(v1,v2,v3)*(w1,w2,w3). + +Matrix3x3::Matrix3x3(const double& v1, const double& v2, const double& v3, + const double& w1, const double& w2, const double& w3) +{ + M[0] = v1*w1; M[1] = v1*w2; M[2] = v1*w3; + M[3] = v2*w1; M[4] = v2*w2; M[5] = v2*w3; + M[6] = v3*w1; M[7] = v3*w2; M[8] = v3*w3; +} + + +// Symmetric matrix T(x,y,z)*(x,y,z) + +Matrix3x3::Matrix3x3(const double& x, const double& y, const double& z) +{ + M[0] = x*x; M[1] = x*y; M[2] = x*z; + M[3] = M[1]; M[4] = y*y; M[5] = y*z; + M[6] = M[2]; M[7] = M[5]; M[8] = z*z; +} + + +// Self-sum + +void Matrix3x3::operator+=(const Matrix3x3& s) +{ + M[0] += s.M[0]; M[1] += s.M[1]; M[2] += s.M[2]; + M[3] += s.M[3]; M[4] += s.M[4]; M[5] += s.M[5]; + M[6] += s.M[6]; M[7] += s.M[7]; M[8] += s.M[8]; +} + + +// Self-subtraction + +void Matrix3x3::operator-=(const Matrix3x3& s) +{ + M[0] -= s.M[0]; M[1] -= s.M[1]; M[2] -= s.M[2]; + M[3] -= s.M[3]; M[4] -= s.M[4]; M[5] -= s.M[5]; + M[6] -= s.M[6]; M[7] -= s.M[7]; M[8] -= s.M[8]; +} + + +// Self-multiplication + +void Matrix3x3::operator*=(const double& d) +{ + M[0] *= d; M[1] *= d; M[2] *= d; + M[3] *= d; M[4] *= d; M[5] *= d; + M[6] *= d; M[7] *= d; M[8] *= d; +} + + +// Sum + +Matrix3x3 Matrix3x3::operator+(const Matrix3x3& s) const +{ + return Matrix3x3(M[0]+s.M[0], M[1]+s.M[1], M[2]+s.M[2], + M[3]+s.M[3], M[4]+s.M[4], M[5]+s.M[5], + M[6]+s.M[6], M[7]+s.M[7], M[8]+s.M[8]); +} + + +// Scalar Multiplication + +Matrix3x3 Matrix3x3::operator*(const double& d) const +{ + return Matrix3x3(M[0]*d, M[1]*d, M[2]*d, + M[3]*d, M[4]*d, M[5]*d, + M[6]*d, M[7]*d, M[8]*d); +} + + +// Matrix multiplication + +Matrix3x3 Matrix3x3::operator*(const Matrix3x3& q) const +{ + return Matrix3x3( + M[0]*q.M[0]+M[1]*q.M[3]+M[2]*q.M[6], M[0]*q.M[1]+M[1]*q.M[4]+M[2]*q.M[7], M[0]*q.M[2]+M[1]*q.M[5]+M[2]*q.M[8], + M[3]*q.M[0]+M[4]*q.M[3]+M[5]*q.M[6], M[3]*q.M[1]+M[4]*q.M[4]+M[5]*q.M[7], M[3]*q.M[2]+M[4]*q.M[5]+M[5]*q.M[8], + M[6]*q.M[0]+M[7]*q.M[3]+M[8]*q.M[6], M[6]*q.M[1]+M[7]*q.M[4]+M[8]*q.M[7], M[6]*q.M[2]+M[7]*q.M[5]+M[8]*q.M[8] + ); +} + + +// Matrix transpose + +Matrix3x3 Matrix3x3::operator~() const +{ + return Matrix3x3(M[0],M[3],M[6],M[1],M[4],M[7],M[2],M[5],M[8]); +} + + +// Computes (x,y,z)*M*(x,y,z) + +double Matrix3x3::lrMultiply(const double& x, const double& y, const double& z) const +{ + return (x*(x*M[0] + y*M[3] + z*M[6]) + y*(x*M[1] + y*M[4] + z*M[7]) + z*(x*M[2] + y*M[5] + z*M[8])); +} + + +// Computes v*M*w +double Matrix3x3::lrMultiply(const double& v1, const double& v2, const double& v3, + const double& w1, const double& w2, const double& w3) const +{ + return (w1*(v1*M[0] + v2*M[3] + v3*M[6]) + w2*(v1*M[1] + v2*M[4] + v3*M[7]) + w3*(v1*M[2] + v2*M[5] + v3*M[8])); +} + +Matrix3x3 Matrix3x3::transpose() const +{ + return Matrix3x3(M[0], M[3], M[6], + M[1], M[4], M[7], + M[2], M[5], M[8]); +} + +////////////////////////////////////////////////////////////////////////// +// +// Symmetric 3x3 matrix +// +////////////////////////////////////////////////////////////////////////// + +// Plain constructor + +SymMatrix3x3::SymMatrix3x3(const double&a11, const double&a12, const double&a22, + const double&a13, const double&a23, const double&a33) +{ + M[0] = a11; M[1] = a12; M[3] = a13; + M[2] = a22; M[4] = a23; + M[5] = a33; +} + + +// Symmetric matrix T(x,y,z)*(x,y,z) + +SymMatrix3x3::SymMatrix3x3(const double& x, const double& y, const double& z) +{ + M[0] = x*x; M[1] = x*y; M[3] = x*z; + M[2] = y*y; M[4] = y*z; + M[5] = z*z; +} + + +// Constructor from generic matrix + +SymMatrix3x3::SymMatrix3x3(const Matrix3x3& q) +{ + M[0] = q.M[0]; M[1] = q.M[1]; M[3] = q.M[2]; + M[2] = q.M[4]; M[4] = q.M[5]; + M[5] = q.M[8]; +} + + +// Self-sum + +void SymMatrix3x3::operator+=(const SymMatrix3x3& s) +{ + M[0] += s.M[0]; M[1] += s.M[1]; M[2] += s.M[2]; + M[3] += s.M[3]; M[4] += s.M[4]; M[5] += s.M[5]; +} + + +// Self-subtraction + +void SymMatrix3x3::operator-=(const SymMatrix3x3& s) +{ + M[0] -= s.M[0]; M[1] -= s.M[1]; M[2] -= s.M[2]; + M[3] -= s.M[3]; M[4] -= s.M[4]; M[5] -= s.M[5]; +} + + +// Self-multiplication + +void SymMatrix3x3::operator*=(const double& d) +{ + M[0] *= d; M[1] *= d; M[2] *= d; + M[3] *= d; M[4] *= d; M[5] *= d; +} + + +// Sum + +SymMatrix3x3 SymMatrix3x3::operator+(const SymMatrix3x3& s) const +{ + return SymMatrix3x3(M[0]+s.M[0], M[1]+s.M[1], M[2]+s.M[2], + M[3]+s.M[3], M[4]+s.M[4], M[5]+s.M[5]); +} + + +// Multiplication + +SymMatrix3x3 SymMatrix3x3::operator*(const double& d) const +{ + return SymMatrix3x3(M[0]*d, M[1]*d, M[2]*d, M[3]*d, M[4]*d, M[5]*d); +} + + +// Computes (x,y,z)*M*(x,y,z) + +double SymMatrix3x3::lrMultiply(const double& x, const double& y, const double& z) const +{ + double a,b,c; + a = x*M[0] + y*M[1] + z*M[3]; + b = x*M[1] + y*M[2] + z*M[4]; + c = x*M[3] + y*M[4] + z*M[5]; + return (x*a + y*b + z*c); +} + + + +// Computes v*M*w +double SymMatrix3x3::lrMultiply(const double& v1, const double& v2, const double& v3, + const double& w1, const double& w2, const double& w3) const +{ + return (w1*(v1*M[0] + v2*M[1] + v3*M[3]) + w2*(v1*M[1] + v2*M[2] + v3*M[4]) + w3*(v1*M[3] + v2*M[4] + v3*M[5])); +} + +// Invert the matrix. If singular return FALSE + +bool SymMatrix3x3::invert() +{ + double det, pos, neg, t, out[6]; + + pos = neg = 0.0; + t = M[0]*M[2]*M[5]; ((t >= 0.0)?(pos):(neg)) += t; + t = M[1]*M[4]*M[3]; ((t >= 0.0)?(pos):(neg)) += t; + t = M[3]*M[1]*M[4]; ((t >= 0.0)?(pos):(neg)) += t; + t = -M[3]*M[2]*M[3]; ((t >= 0.0)?(pos):(neg)) += t; + t = -M[1]*M[1]*M[5]; ((t >= 0.0)?(pos):(neg)) += t; + t = -M[0]*M[4]*M[4]; ((t >= 0.0)?(pos):(neg)) += t; + det = pos+neg; + + t = det/(pos-neg); + if (FABS(t) >= 1.0e-15) + { + out[0] = (M[2] * M[5] - M[4] * M[4]) /det; + out[1] = -(M[1] * M[5] - M[4] * M[3]) /det; + out[2] = (M[0] * M[5] - M[3] * M[3]) /det; + out[3] = (M[1] * M[4] - M[2] * M[3]) /det; + out[4] = -(M[0] * M[4] - M[1] * M[3]) /det; + out[5] = (M[0] * M[2] - M[1] * M[1]) /det; + M[0] = out[0]; M[1] = out[1]; M[2] = out[2]; + M[3] = out[3]; M[4] = out[4]; M[5] = out[5]; + return 1; + } + + return 0; +} + + +// Compute eigenvalues and eigenvectors of the matrix (JACOBI method). +// The calling function is responsible of verifying that the matrix +// is diagonalizable. +// +// This routine was inspired from a software to estimate curvature +// tensors developed at INRIA. Visit the following link for details: +// http://www-sop.inria.fr/geometrica/team/Pierre.Alliez/demos/curvature/ +// +// This version has been slightly optimized. +// + +void SymMatrix3x3::diagonalize(double *eigen_val, double *eigen_vec) const +{ + static const double EPS = 0.00001; + static const double cos_pi_4 = 0.70710678; + static const int MAX_ITER = 100; + double a_P[6], v_P[9]; + double *a = a_P, *v = v_P; + double a_norm,a_normEPS,thr,thr_nn; + int nb_iter = 0; + int i,j,k,ij,jj,ik,l,m,lm,mq,lq,ll,mm,imv,im,iq,ilv,il; + int index_P[3]; + int *index = index_P; + double a_ij,a_lm,a_ll,a_mm,a_im,a_il,a_lm_2,v_ilv,v_imv,x; + double sinx,sinx_2,cosx,cosx_2,sincos,delta; + + a[0] = M[0]; a[1] = M[1]; a[2] = M[2]; + a[3] = M[3]; a[4] = M[4]; a[5] = M[5]; + a--; + + // Step 2 : Init diagonalization matrix as the unit matrix + + for (ij=0, i=0; i<3; i++) for (j=0; j<3; j++) v[ij++] = (i==j)?(1.0):(0.0); + v--; + + // Step 3 : compute the weight of the non diagonal terms + + a_norm = 0.0; + for (i=1, ij=1; i<=3; i++) for (j=1; j<=i; j++, ij++) if( i!=j ) {a_ij = a[ij]; a_norm += a_ij*a_ij;} + + if( a_norm != 0.0 ) + { + a_normEPS = a_norm*EPS; + thr = a_norm ; + + // Step 4 : rotations + while (thr > a_normEPS && nb_iter < MAX_ITER) + { + nb_iter++; + thr_nn = thr / 6; + + for (l=1; l<3; l++) + for (m=l+1; m<=3; m++) + { + // compute sinx and cosx + + lq = (l*l-l)/2; mq = (m*m-m)/2; + lm = l+mq; a_lm = a[lm]; + a_lm_2 = a_lm*a_lm; + + if( a_lm_2 < thr_nn ) continue; + + ll = l+lq; mm = m+mq; + a_ll = a[ll]; a_mm = a[mm]; + delta = a_ll - a_mm; + + if (delta==0.0) {sinx = -cos_pi_4; cosx = cos_pi_4;} + else {x = -atan( (a_lm+a_lm) / delta )/2.0; sinx=sin(x); cosx=cos(x);} + + sinx_2 = sinx*sinx; + cosx_2 = cosx*cosx; + sincos = sinx*cosx; + + // rotate L and M columns + + ilv = 3*(l-1); imv = 3*(m-1); + + for( i=1; i<=3;i++ ) + { + if( (i!=l) && (i!=m) ) + { + iq = (i*i-i)/2; + im = (i1.0e-12) {*L1 = *L2 = *L3 = a11; return;} // Evecs = (1,0,0), (0,1,0), (0,0,1) + + double l1, l2, l3; // Eigenvalues to be computed + + if (Q>=0) {double p=(b>0)?(pow(b/2, 1.0/3.0)):(0); l1=l2=((c2/3.0)+p); l3=((c2/3.0)-2.0*p);} + else + { + double t = atan2(sqrt(-Q), -b/2.0)/3.0, r = pow(((b*b)/4.0)-Q, 1.0/6.0); + double cos_t = cos(t), sin_t = sin(t); + const double sq3 = sqrt(3.0); + l1 = l2 = l3 = (c2/3.0); + l1 += (2*r*cos_t); + l2 -= r*(cos_t+sq3*sin_t); + l3 -= r*(cos_t-sq3*sin_t); + } + + if (l1<=l2 && l1<=l3) {*L1=l1; *L2=(l2=l2 && l1>=l3) {c0=u11; c1=u12; c2=u13; l=l1;} + else if (l2>=l1 && l2>=l3) {c0=u12; c1=u22; c2=u23; l=l2;} + else {c0=u13; c1=u23; c2=u33; l=l3;} + + l1 = sqrt(l); *x = c0/l1; *y = c1/l1; *z = c2/l1; + + return l; +} + + +// Computes the eigenvector corresp. to the maximum eigenvalue + +void SymMatrix3x3::getMaxEigenvector(double *x, double *y, double *z) const +{ + SymMatrix3x3(-M[0], -M[1], -M[2], -M[3], -M[4], -M[5]).getMinEigenvector(x, y, z); +} + +// Prints the matrix values + +void SymMatrix3x3::print(FILE *fp) const +{ + fprintf(fp,"%e %e %e\n",M[0],M[1],M[3]); + fprintf(fp,"%e %e %e\n",M[1],M[2],M[4]); + fprintf(fp,"%e %e %e\n",M[3],M[4],M[5]); +} + + +////////////////////////////////////////////////////////////////////////// +// +// Symmetric 4x4 matrix +// +////////////////////////////////////////////////////////////////////////// + +// Extend a 3x3 matrix + +SymMatrix4x4::SymMatrix4x4(const SymMatrix3x3& q) +{ + a2 = q.M[0]; ab = q.M[1]; ac = q.M[3]; ad = 0; + b2 = q.M[2]; bc = q.M[4]; bd = 0; + c2 = q.M[5]; cd = 0; d2 = 1; +} + + +// Build a quadric + +SymMatrix4x4::SymMatrix4x4(const coord& a, const coord& b, const coord& c, const coord& d) +{ + a2 = a*a; ab = a*b; ac = a*c; ad = a*d; + b2 = b*b; bc = b*c; bd = b*d; + c2 = c*c; cd = c*d; d2 = d*d; +} + +// True iff equal + +bool SymMatrix4x4::operator==(const SymMatrix4x4& q) +{ + return ( + a2 == q.a2 && ab == q.ab && ac == q.ac && ad == q.ad &&\ + b2 == q.b2 && bc == q.bc && bd == q.bd &&\ + c2 == q.c2 && cd == q.cd && d2 == q.d2); +} + +// True iff different + +bool SymMatrix4x4::operator!=(const SymMatrix4x4& q) +{ + return ( + a2 != q.a2 || ab != q.ab || ac != q.ac || ad != q.ad ||\ + b2 != q.b2 || bc != q.bc || bd != q.bd ||\ + c2 != q.c2 || cd != q.cd || d2 != q.d2); +} + +// Sum the matrix 'q' to the current matrix + +void SymMatrix4x4::operator+=(const SymMatrix4x4& q) +{ + a2 += q.a2; ab += q.ab; ac += q.ac; ad += q.ad; + b2 += q.b2; bc += q.bc; bd += q.bd; + c2 += q.c2; cd += q.cd; d2 += q.d2; +} + +// Returns the sum of the matrix with another matrix 'q' + +SymMatrix4x4 SymMatrix4x4::operator+(const SymMatrix4x4& q) const +{ + SymMatrix4x4 n; + + n.a2=a2+q.a2; n.ab=ab+q.ab; n.ac=ac+q.ac; n.ad=ad+q.ad; + n.b2=b2+q.b2; n.bc=bc+q.bc; n.bd=bd+q.bd; + n.c2=c2+q.c2; n.cd=cd+q.cd; n.d2=d2+q.d2; + + return n; +} + +// Returns the product of the matrix with a scalar + +SymMatrix4x4 SymMatrix4x4::operator*(const coord& d) const +{ + SymMatrix4x4 n; + + n.a2=a2*d; n.ab=ab*d; n.ac=ac*d; n.ad=ad*d; + n.b2=b2*d; n.bc=bc*d; n.bd=bd*d; + n.c2=c2*d; n.cd=cd*d; n.d2=d2*d; + + return n; +} + +void SymMatrix4x4::add(const coord& a, const coord& b, const coord& c, const coord& d) +{ + a2 += a*a; ab += a*b; ac += a*c; ad += a*d; + b2 += b*b; bc += b*c; bd += b*d; + c2 += c*c; cd += c*d; d2 += d*d; +} + + +// Computes (a,b,c,w)*M*(a,b,c,w) + +coord SymMatrix4x4::lrMultiply(const coord& x, const coord& y, const coord& z, const coord& w) const +{ + coord a,b,c,d; + a = x*a2 + y*ab + z*ac + w*ad; + b = x*ab + y*b2 + z*bc + w*bd; + c = x*ac + y*bc + z*c2 + w*cd; + d = x*ad + y*bd + z*cd + w*d2; + return (x*a + y*b + z*c + w*d); +} + +// Computes (a,b,c) s.t. (a,b,c,1)*M*(a,b,c,1) is minimized +// Returns FALSE if the minimizer is not unique + +bool SymMatrix4x4::getMinimizer(coord *a, coord *b, coord *c) const +{ + coord det, pos, neg, t; + + pos = neg = 0.0; + t = a2*b2*c2; ((t >= 0.0)?(pos):(neg)) += t; + t = ab*bc*ac; ((t >= 0.0)?(pos):(neg)) += t; + t = ac*ab*bc; ((t >= 0.0)?(pos):(neg)) += t; + t = -ac*b2*ac; ((t >= 0.0)?(pos):(neg)) += t; + t = -ab*ab*c2; ((t >= 0.0)?(pos):(neg)) += t; + t = -a2*bc*bc; ((t >= 0.0)?(pos):(neg)) += t; + det = pos+neg; + + if (pos == neg) return 0; + + t = det/(pos-neg); + if ( +#ifdef USE_HYBRID_KERNEL + (coord::isUsingRationals() && t!=0) || +#endif + (FABS(t) >= 1.0e-15)) + { + *a = -(ad*( (b2*c2 - bc*bc)) + bd*(-(ab*c2 - bc*ac)) + cd*( (ab*bc - b2*ac)))/det; + *b = -(ad*(-(ab*c2 - ac*bc)) + bd*( (a2*c2 - ac*ac)) + cd*(-(a2*bc - ab*ac)))/det; + *c = -(ad*( (ab*bc - ac*b2)) + bd*(-(a2*bc - ac*ab)) + cd*( (a2*b2 - ab*ab)))/det; + return 1; + } + + return 0; +} + + +// Invert a symmetric 4x4 matrix using L*D*L^T decomposition. +// The calling function is responsible of verifying that the matrix +// is positive definite. +// + +bool SymMatrix4x4::invert() +{ + if (a2 <= 0) return false; + + coord d00 = coord(1.0) / a2; + coord L10 = ab; + coord l10 = ab*d00; + coord L20 = ac; + coord l20 = ac*d00; + coord L30 = ad; + coord l30 = ad*d00; + coord d11 = b2-(L10*l10); + + if (d11 <= 0) return false; else d11 = coord(1.0)/d11; + + coord L21 = (bc-(L10*l20)); + coord l21 = L21*d11; + coord L31 = (bd-(L10*l30)); + coord l31 = L31*d11; + coord d22 = c2-(L20*l20)-(L21*l21); + + if (d22 <= 0) return false; else d22 = coord(1.0) / d22; + + coord L32 = (cd-(L20*l30)-(L21*l31)); + coord l32 = L32*d22; + coord d33 = d2-(L30*l30)-(L31*l31)-(L32*l32); + + if (d33 <= 0) return false; else d33 = coord(1.0) / d33; + + L20 = l10*l21-l20; + L31 = l21*l32-l31; + L30 = l32*l20-L31*l10-l30; + + a2 = d00+(-l10)*((-l10)*d11)+L20*(L20*d22)+L30*(L30*d33); + ab = ((-l10)*d11)+L20*((-l21)*d22)+L30*(L31*d33); + b2 = d11+(-l21)*((-l21)*d22)+L31*(L31*d33); + ac = (L20*d22)+L30*((-l32)*d33); + bc = ((-l21)*d22)+L31*((-l32)*d33); + c2 = d22+(-l32)*((-l32)*d33); + ad = (L30*d33); + bd = (L31*d33); + cd = ((-l32)*d33); + d2 = d33; + + return true; +} + + +////////////////////////////////////////////////////////////////////////// +// +// Generic 4x4 matrix +// +////////////////////////////////////////////////////////////////////////// + +Matrix4x4::Matrix4x4() {} + +Matrix4x4::Matrix4x4(const double& d) +{ + matrix[0][0] = matrix[1][1] = matrix[2][2] = matrix[3][3] = d; + matrix[1][0] = matrix[0][1] = matrix[1][2] = matrix[1][3] = 0; + matrix[2][0] = matrix[2][1] = matrix[0][2] = matrix[2][3] = 0; + matrix[3][0] = matrix[3][1] = matrix[3][2] = matrix[0][3] = 0; +} + +Matrix4x4::Matrix4x4(const double& a11, const double& a12, const double& a13, const double& a14, + const double& a21, const double& a22, const double& a23, const double& a24, + const double& a31, const double& a32, const double& a33, const double& a34, + const double& a41, const double& a42, const double& a43, const double& a44) +{ + matrix[0][0] = a11; matrix[0][1] = a12; matrix[0][2] = a13; matrix[0][3] = a14; + matrix[1][0] = a21; matrix[1][1] = a22; matrix[1][2] = a23; matrix[1][3] = a24; + matrix[2][0] = a31; matrix[2][1] = a32; matrix[2][2] = a33; matrix[2][3] = a34; + matrix[3][0] = a41; matrix[3][1] = a42; matrix[3][2] = a43; matrix[3][3] = a44; +} + +void Matrix4x4::setRotation(const double& rx, const double& ry, const double& rz, const double& rw) +{ + matrix[0][0] = rw*rw + rx*rx - ry*ry - rz*rz; + matrix[0][1] = 2*rx*ry + 2*rw*rz; + matrix[0][2] = 2*rx*rz - 2*rw*ry; + matrix[0][3] = 0.0; + + matrix[1][0] = 2*rx*ry-2*rw*rz; + matrix[1][1] = rw*rw - rx*rx + ry*ry - rz*rz; + matrix[1][2] = 2*ry*rz + 2*rw*rx; + matrix[1][3] = 0.0; + + matrix[2][0] = 2*rx*rz + 2*rw*ry; + matrix[2][1] = 2*ry*rz - 2*rw*rx; + matrix[2][2] = rw*rw - rx*rx - ry*ry + rz*rz; + matrix[2][3] = 0.0; + + matrix[3][0] = 0.0; + matrix[3][1] = 0.0; + matrix[3][2] = 0.0; + matrix[3][3] = rw*rw + rx*rx + ry*ry + rz*rz; +} + +void Matrix4x4::setTranslation(const double& x, const double& y, const double& z) +{ + matrix[0][0] = matrix[1][1] = matrix[2][2] = matrix[3][3] = 1; + matrix[1][0] = matrix[0][1] = matrix[1][2] = 0; + matrix[2][0] = matrix[2][1] = matrix[0][2] = 0; + matrix[3][0] = matrix[3][1] = matrix[3][2] = 0; + matrix[0][3] = x; + matrix[1][3] = y; + matrix[2][3] = z; +} + +Matrix4x4 Matrix4x4::operator*(const Matrix4x4& q) const +{ + int i, j; + Matrix4x4 m; + + for (i=0; i<4; i++) for (j=0; j<4; j++) + m.matrix[i][j] = matrix[i][0]*q.matrix[0][j] + matrix[i][1]*q.matrix[1][j] + matrix[i][2]*q.matrix[2][j] + matrix[i][3]*q.matrix[3][j]; + + return m; +} + +void Matrix4x4::transform(double *x, double *y, double *z) +{ + double w, a = *x, b = *y, c = *z; + + *x = matrix[0][0]*a + matrix[0][1]*b + matrix[0][2]*c + matrix[0][3]; + *y = matrix[1][0]*a + matrix[1][1]*b + matrix[1][2]*c + matrix[1][3]; + *z = matrix[2][0]*a + matrix[2][1]*b + matrix[2][2]*c + matrix[2][3]; + w = matrix[3][0]*a + matrix[3][1]*b + matrix[3][2]*c + matrix[3][3]; + + (*x) /= w; (*y) /= w; (*z) /= w; +} + +} //namespace T_MESH diff --git a/src/mesh_fix/src/Kernel/orientation.c b/src/mesh_fix/src/Kernel/orientation.c new file mode 100644 index 000000000..ab33dfd00 --- /dev/null +++ b/src/mesh_fix/src/Kernel/orientation.c @@ -0,0 +1,700 @@ +/**************************************************************************** +* TMesh * +* * +* Consiglio Nazionale delle Ricerche * +* Istituto di Matematica Applicata e Tecnologie Informatiche * +* Sezione di Genova * +* IMATI-GE / CNR * +* * +* Authors: Marco Attene * +* Copyright(C) 2012: IMATI-GE / CNR * +* All rights reserved. * +* * +* This program is dual-licensed as follows: * +* * +* (1) You may use TMesh as 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 3 of the License, or * +* (at your option) any later version. * +* In this case the 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 (http://www.gnu.org/licenses/gpl.txt) * +* for more details. * +* * +* (2) You may use TMesh as part of a commercial software. In this case a * +* proper agreement must be reached with the Authors and with IMATI-GE/CNR * +* based on a proper licensing contract. * +* * +****************************************************************************/ + +// This code is inspired on ideas first published in the following paper: +// Jonathan Richard Shewchuk. Adaptive Precision Floating-Point Arithmetic +// and Fast Robust Geometric Predicates, Discrete & Computational Geometry +// 18(3):305–363, October 1997. +// + +#include + +#ifdef SPECIFY_FP_PRECISION +#include +#endif + +/*****************************************************************************/ +/* */ +/* This section contains private macros and functions. */ +/* These are not part of the public interface. */ +/* */ +/*****************************************************************************/ + +#define FABS(a) (((a)>=0.0)?(a):(-(a))) +#define FTST(a,b,x,y) _bvr=x-a; y=b-_bvr +#define FTS(a,b,x,y) x=(double)(a+b); FTST(a,b,x,y) +#define TST(a,b,x,y) _bvr=(double)(x-a); _avr=x-_bvr; _brn=b-_bvr; _arn=a-_avr; y=_arn+_brn +#define TWS(a,b,x,y) x=(double)(a+b); TST(a,b,x,y) +#define TDT(a,b,x,y) _bvr=(double)(a-x); _avr=x+_bvr; _brn=_bvr-b; _arn=a-_avr; y=_arn+_brn +#define TWD(a,b,x,y) x=(double)(a-b); TDT(a,b,x,y) +#define SPLT(a,ahi,alo) c=(double)(_spl*a); abig=(double)(c-a); ahi=c-abig; alo=a-ahi +#define TPT(a,b,x,y) SPLT(a,ahi,alo); SPLT(b,bhi,blo); err1=x-(ahi*bhi); err2=err1-(alo*bhi); err3=err2-(ahi*blo); y=(alo*blo)-err3 +#define TWP(a,b,x,y) x=(double)(a*b); TPT(a,b,x,y) +#define TPP(a,b,bhi,blo,x,y) x=(double)(a*b); SPLT(a,ahi,alo); err1=x-(ahi*bhi); err2=err1-(alo*bhi); err3=err2-(ahi*blo); y=(alo*blo)-err3 +#define TOD(a1,a0,b,x2,x1,x0) TWD(a0,b,_i,x0); TWS(a1,_i,x2,x1) +#define TTD(a1,a0,b1,b0,x3,x2,x1,x0) TOD(a1,a0,b0,_j,_0,x0); TOD(_j,_0,b1,x3,x2,x1) +#define TOP(a1,a0,b,x3,x2,x1,x0) SPLT(b,bhi,blo); TPP(a0,b,bhi,blo,_i,x0); TPP(a1,b,bhi,blo,_j,_0); TWS(_i,_0,_k,x1); FTS(_j,_k,x3,x2) + +double _spl, _eps, _reb, _ccwebA, _ccwebB, _ccwebC, _o3ebA, _o3ebB, _o3ebC; +double _iccebA, _iccebB, _iccebC, _ispebA, _ispebB, _ispebC; + +int _fesze(int elen, double *e, int flen, double *f, double *h) +{ + double Q, Qnew, hh, _bvr, _avr, _brn, _arn, enow, fnow; + int eindex, findex, hindex; + + enow = e[0]; + fnow = f[0]; + eindex = findex = 0; + if ((fnow > enow) == (fnow > -enow)) { + Q = enow; + enow = e[++eindex]; + } else { + Q = fnow; + fnow = f[++findex]; + } + hindex = 0; + if ((eindex < elen) && (findex < flen)) { + if ((fnow > enow) == (fnow > -enow)) { + FTS(enow, Q, Qnew, hh); + enow = e[++eindex]; + } else { + FTS(fnow, Q, Qnew, hh); + fnow = f[++findex]; + } + Q = Qnew; + if (hh != 0.0) { + h[hindex++] = hh; + } + while ((eindex < elen) && (findex < flen)) { + if ((fnow > enow) == (fnow > -enow)) { + TWS(Q, enow, Qnew, hh); + enow = e[++eindex]; + } else { + TWS(Q, fnow, Qnew, hh); + fnow = f[++findex]; + } + Q = Qnew; + if (hh != 0.0) { + h[hindex++] = hh; + } + } + } + while (eindex < elen) { + TWS(Q, enow, Qnew, hh); + enow = e[++eindex]; + Q = Qnew; + if (hh != 0.0) { + h[hindex++] = hh; + } + } + while (findex < flen) { + TWS(Q, fnow, Qnew, hh); + fnow = f[++findex]; + Q = Qnew; + if (hh != 0.0) { + h[hindex++] = hh; + } + } + if ((Q != 0.0) || (hindex == 0)) { + h[hindex++] = Q; + } + return hindex; +} + +int _seze(int elen, double *e, double b, double *h) +{ + double Q, sum, hh, product1, product0, enow, _bvr, _avr, _brn, _arn, c; + double abig, ahi, alo, bhi, blo, err1, err2, err3; + int eindex, hindex; + + SPLT(b, bhi, blo); + TPP(e[0], b, bhi, blo, Q, hh); + hindex = 0; + if (hh != 0) { + h[hindex++] = hh; + } + for (eindex = 1; eindex < elen; eindex++) { + enow = e[eindex]; + TPP(enow, b, bhi, blo, product1, product0); + TWS(Q, product0, sum, hh); + if (hh != 0) { + h[hindex++] = hh; + } + FTS(product1, sum, Q, hh); + if (hh != 0) { + h[hindex++] = hh; + } + } + if ((Q != 0.0) || (hindex == 0)) { + h[hindex++] = Q; + } + return hindex; +} + +double _estm(int elen, double *e) +{ + int eindex; + double Q = e[0]; + for (eindex = 1; eindex < elen; eindex++) Q += e[eindex]; + return Q; +} + + +double _adaptive2dorientation(double *pa, double *pb, double *pc, double detsum) +{ + double acx, acy, bcx, bcy,acxtail, acytail, bcxtail, bcytail, detleft, detright; + double detlefttail, detrighttail, det, errbound, B[4], C1[8], C2[12], D[16]; + double B3, u[4], u3, s1, t1, s0, t0, _bvr, _avr, _brn, _arn, c; + double abig, ahi, alo, bhi, blo, err1, err2, err3, _i, _j, _0; + int C1length, C2length, Dlength; + + acx = (double) (pa[0] - pc[0]); + bcx = (double) (pb[0] - pc[0]); + acy = (double) (pa[1] - pc[1]); + bcy = (double) (pb[1] - pc[1]); + + TWP(acx, bcy, detleft, detlefttail); + TWP(acy, bcx, detright, detrighttail); + + TTD(detleft, detlefttail, detright, detrighttail, + B3, B[2], B[1], B[0]); + B[3] = B3; + + det = _estm(4, B); + errbound = _ccwebB * detsum; + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + TDT(pa[0], pc[0], acx, acxtail); + TDT(pb[0], pc[0], bcx, bcxtail); + TDT(pa[1], pc[1], acy, acytail); + TDT(pb[1], pc[1], bcy, bcytail); + + if ((acxtail == 0.0) && (acytail == 0.0) + && (bcxtail == 0.0) && (bcytail == 0.0)) { + return det; + } + + errbound = _ccwebC * detsum + _reb * FABS(det); + det += (acx * bcytail + bcy * acxtail) + - (acy * bcxtail + bcx * acytail); + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + TWP(acxtail, bcy, s1, s0); + TWP(acytail, bcx, t1, t0); + TTD(s1, s0, t1, t0, u3, u[2], u[1], u[0]); + u[3] = u3; + C1length = _fesze(4, B, 4, u, C1); + + TWP(acx, bcytail, s1, s0); + TWP(acy, bcxtail, t1, t0); + TTD(s1, s0, t1, t0, u3, u[2], u[1], u[0]); + u[3] = u3; + C2length = _fesze(C1length, C1, 4, u, C2); + + TWP(acxtail, bcytail, s1, s0); + TWP(acytail, bcxtail, t1, t0); + TTD(s1, s0, t1, t0, u3, u[2], u[1], u[0]); + u[3] = u3; + Dlength = _fesze(C2length, C2, 4, u, D); + + return(D[Dlength - 1]); +} + +double _adaptive3dorientation(double *pa, double *pb, double *pc, double *pd, double permanent) +{ + double adx, bdx, cdx, ady, bdy, cdy, adz, bdz, cdz, det, errbound; + double bdxcdy1, cdxbdy1, cdxady1, adxcdy1, adxbdy1, bdxady1; + double bdxcdy0, cdxbdy0, cdxady0, adxcdy0, adxbdy0, bdxady0, bc[4], ca[4], ab[4]; + double bc3, ca3, ab3, adet[8], bdet[8], cdet[8]; + double abdet[16], *finnow, *finother, *finswap, fin1[192], fin2[192]; + double adxtail, bdxtail, cdxtail, adytail, bdytail, cdytail, adztail, bdztail, cdztail; + double at_blarge, at_clarge, bt_clarge, bt_alarge, ct_alarge, ct_blarge; + double at_b[4], at_c[4], bt_c[4], bt_a[4], ct_a[4], ct_b[4]; + double bdxt_cdy1, cdxt_bdy1, cdxt_ady1, adxt_cdy1, adxt_bdy1, bdxt_ady1; + double bdxt_cdy0, cdxt_bdy0, cdxt_ady0, adxt_cdy0, adxt_bdy0, bdxt_ady0; + double bdyt_cdx1, cdyt_bdx1, cdyt_adx1, adyt_cdx1, adyt_bdx1, bdyt_adx1; + double bdyt_cdx0, cdyt_bdx0, cdyt_adx0, adyt_cdx0, adyt_bdx0, bdyt_adx0; + double bct[8], cat[8], abt[8], bdxt_cdyt1, cdxt_bdyt1, cdxt_adyt1; + double adxt_cdyt1, adxt_bdyt1, bdxt_adyt1, bdxt_cdyt0, cdxt_bdyt0, cdxt_adyt0; + double adxt_cdyt0, adxt_bdyt0, bdxt_adyt0, u[4], v[12], w[16], u3, negate; + double _bvr, _avr, _brn, _arn, c, abig, ahi, alo, bhi, blo; + double err1, err2, err3, _i, _j, _k, _0; + int at_blen, at_clen, bt_clen, bt_alen, ct_alen, ct_blen, finlength; + int vlength, wlength, alen, blen, clen, ablen, bctlen, catlen, abtlen; + + adx = (double) (pa[0] - pd[0]); + bdx = (double) (pb[0] - pd[0]); + cdx = (double) (pc[0] - pd[0]); + ady = (double) (pa[1] - pd[1]); + bdy = (double) (pb[1] - pd[1]); + cdy = (double) (pc[1] - pd[1]); + adz = (double) (pa[2] - pd[2]); + bdz = (double) (pb[2] - pd[2]); + cdz = (double) (pc[2] - pd[2]); + + TWP(bdx, cdy, bdxcdy1, bdxcdy0); + TWP(cdx, bdy, cdxbdy1, cdxbdy0); + TTD(bdxcdy1, bdxcdy0, cdxbdy1, cdxbdy0, bc3, bc[2], bc[1], bc[0]); + bc[3] = bc3; + alen = _seze(4, bc, adz, adet); + + TWP(cdx, ady, cdxady1, cdxady0); + TWP(adx, cdy, adxcdy1, adxcdy0); + TTD(cdxady1, cdxady0, adxcdy1, adxcdy0, ca3, ca[2], ca[1], ca[0]); + ca[3] = ca3; + blen = _seze(4, ca, bdz, bdet); + + TWP(adx, bdy, adxbdy1, adxbdy0); + TWP(bdx, ady, bdxady1, bdxady0); + TTD(adxbdy1, adxbdy0, bdxady1, bdxady0, ab3, ab[2], ab[1], ab[0]); + ab[3] = ab3; + clen = _seze(4, ab, cdz, cdet); + + ablen = _fesze(alen, adet, blen, bdet, abdet); + finlength = _fesze(ablen, abdet, clen, cdet, fin1); + + det = _estm(finlength, fin1); + errbound = _o3ebB * permanent; + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + TDT(pa[0], pd[0], adx, adxtail); + TDT(pb[0], pd[0], bdx, bdxtail); + TDT(pc[0], pd[0], cdx, cdxtail); + TDT(pa[1], pd[1], ady, adytail); + TDT(pb[1], pd[1], bdy, bdytail); + TDT(pc[1], pd[1], cdy, cdytail); + TDT(pa[2], pd[2], adz, adztail); + TDT(pb[2], pd[2], bdz, bdztail); + TDT(pc[2], pd[2], cdz, cdztail); + + if ((adxtail == 0.0) && (bdxtail == 0.0) && (cdxtail == 0.0) + && (adytail == 0.0) && (bdytail == 0.0) && (cdytail == 0.0) + && (adztail == 0.0) && (bdztail == 0.0) && (cdztail == 0.0)) { + return det; + } + + errbound = _o3ebC * permanent + _reb * FABS(det); + det += (adz * ((bdx * cdytail + cdy * bdxtail) + - (bdy * cdxtail + cdx * bdytail)) + + adztail * (bdx * cdy - bdy * cdx)) + + (bdz * ((cdx * adytail + ady * cdxtail) + - (cdy * adxtail + adx * cdytail)) + + bdztail * (cdx * ady - cdy * adx)) + + (cdz * ((adx * bdytail + bdy * adxtail) + - (ady * bdxtail + bdx * adytail)) + + cdztail * (adx * bdy - ady * bdx)); + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + finnow = fin1; + finother = fin2; + + if (adxtail == 0.0) { + if (adytail == 0.0) { + at_b[0] = 0.0; + at_blen = 1; + at_c[0] = 0.0; + at_clen = 1; + } else { + negate = -adytail; + TWP(negate, bdx, at_blarge, at_b[0]); + at_b[1] = at_blarge; + at_blen = 2; + TWP(adytail, cdx, at_clarge, at_c[0]); + at_c[1] = at_clarge; + at_clen = 2; + } + } else { + if (adytail == 0.0) { + TWP(adxtail, bdy, at_blarge, at_b[0]); + at_b[1] = at_blarge; + at_blen = 2; + negate = -adxtail; + TWP(negate, cdy, at_clarge, at_c[0]); + at_c[1] = at_clarge; + at_clen = 2; + } else { + TWP(adxtail, bdy, adxt_bdy1, adxt_bdy0); + TWP(adytail, bdx, adyt_bdx1, adyt_bdx0); + TTD(adxt_bdy1, adxt_bdy0, adyt_bdx1, adyt_bdx0, + at_blarge, at_b[2], at_b[1], at_b[0]); + at_b[3] = at_blarge; + at_blen = 4; + TWP(adytail, cdx, adyt_cdx1, adyt_cdx0); + TWP(adxtail, cdy, adxt_cdy1, adxt_cdy0); + TTD(adyt_cdx1, adyt_cdx0, adxt_cdy1, adxt_cdy0, + at_clarge, at_c[2], at_c[1], at_c[0]); + at_c[3] = at_clarge; + at_clen = 4; + } + } + if (bdxtail == 0.0) { + if (bdytail == 0.0) { + bt_c[0] = 0.0; + bt_clen = 1; + bt_a[0] = 0.0; + bt_alen = 1; + } else { + negate = -bdytail; + TWP(negate, cdx, bt_clarge, bt_c[0]); + bt_c[1] = bt_clarge; + bt_clen = 2; + TWP(bdytail, adx, bt_alarge, bt_a[0]); + bt_a[1] = bt_alarge; + bt_alen = 2; + } + } else { + if (bdytail == 0.0) { + TWP(bdxtail, cdy, bt_clarge, bt_c[0]); + bt_c[1] = bt_clarge; + bt_clen = 2; + negate = -bdxtail; + TWP(negate, ady, bt_alarge, bt_a[0]); + bt_a[1] = bt_alarge; + bt_alen = 2; + } else { + TWP(bdxtail, cdy, bdxt_cdy1, bdxt_cdy0); + TWP(bdytail, cdx, bdyt_cdx1, bdyt_cdx0); + TTD(bdxt_cdy1, bdxt_cdy0, bdyt_cdx1, bdyt_cdx0, + bt_clarge, bt_c[2], bt_c[1], bt_c[0]); + bt_c[3] = bt_clarge; + bt_clen = 4; + TWP(bdytail, adx, bdyt_adx1, bdyt_adx0); + TWP(bdxtail, ady, bdxt_ady1, bdxt_ady0); + TTD(bdyt_adx1, bdyt_adx0, bdxt_ady1, bdxt_ady0, + bt_alarge, bt_a[2], bt_a[1], bt_a[0]); + bt_a[3] = bt_alarge; + bt_alen = 4; + } + } + if (cdxtail == 0.0) { + if (cdytail == 0.0) { + ct_a[0] = 0.0; + ct_alen = 1; + ct_b[0] = 0.0; + ct_blen = 1; + } else { + negate = -cdytail; + TWP(negate, adx, ct_alarge, ct_a[0]); + ct_a[1] = ct_alarge; + ct_alen = 2; + TWP(cdytail, bdx, ct_blarge, ct_b[0]); + ct_b[1] = ct_blarge; + ct_blen = 2; + } + } else { + if (cdytail == 0.0) { + TWP(cdxtail, ady, ct_alarge, ct_a[0]); + ct_a[1] = ct_alarge; + ct_alen = 2; + negate = -cdxtail; + TWP(negate, bdy, ct_blarge, ct_b[0]); + ct_b[1] = ct_blarge; + ct_blen = 2; + } else { + TWP(cdxtail, ady, cdxt_ady1, cdxt_ady0); + TWP(cdytail, adx, cdyt_adx1, cdyt_adx0); + TTD(cdxt_ady1, cdxt_ady0, cdyt_adx1, cdyt_adx0, + ct_alarge, ct_a[2], ct_a[1], ct_a[0]); + ct_a[3] = ct_alarge; + ct_alen = 4; + TWP(cdytail, bdx, cdyt_bdx1, cdyt_bdx0); + TWP(cdxtail, bdy, cdxt_bdy1, cdxt_bdy0); + TTD(cdyt_bdx1, cdyt_bdx0, cdxt_bdy1, cdxt_bdy0, + ct_blarge, ct_b[2], ct_b[1], ct_b[0]); + ct_b[3] = ct_blarge; + ct_blen = 4; + } + } + + bctlen = _fesze(bt_clen, bt_c, ct_blen, ct_b, bct); + wlength = _seze(bctlen, bct, adz, w); + finlength = _fesze(finlength, finnow, wlength, w, + finother); + finswap = finnow; finnow = finother; finother = finswap; + + catlen = _fesze(ct_alen, ct_a, at_clen, at_c, cat); + wlength = _seze(catlen, cat, bdz, w); + finlength = _fesze(finlength, finnow, wlength, w, + finother); + finswap = finnow; finnow = finother; finother = finswap; + + abtlen = _fesze(at_blen, at_b, bt_alen, bt_a, abt); + wlength = _seze(abtlen, abt, cdz, w); + finlength = _fesze(finlength, finnow, wlength, w, + finother); + finswap = finnow; finnow = finother; finother = finswap; + + if (adztail != 0.0) { + vlength = _seze(4, bc, adztail, v); + finlength = _fesze(finlength, finnow, vlength, v, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (bdztail != 0.0) { + vlength = _seze(4, ca, bdztail, v); + finlength = _fesze(finlength, finnow, vlength, v, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (cdztail != 0.0) { + vlength = _seze(4, ab, cdztail, v); + finlength = _fesze(finlength, finnow, vlength, v, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + + if (adxtail != 0.0) { + if (bdytail != 0.0) { + TWP(adxtail, bdytail, adxt_bdyt1, adxt_bdyt0); + TOP(adxt_bdyt1, adxt_bdyt0, cdz, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = _fesze(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + if (cdztail != 0.0) { + TOP(adxt_bdyt1, adxt_bdyt0, cdztail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = _fesze(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + } + if (cdytail != 0.0) { + negate = -adxtail; + TWP(negate, cdytail, adxt_cdyt1, adxt_cdyt0); + TOP(adxt_cdyt1, adxt_cdyt0, bdz, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = _fesze(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + if (bdztail != 0.0) { + TOP(adxt_cdyt1, adxt_cdyt0, bdztail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = _fesze(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + } + } + if (bdxtail != 0.0) { + if (cdytail != 0.0) { + TWP(bdxtail, cdytail, bdxt_cdyt1, bdxt_cdyt0); + TOP(bdxt_cdyt1, bdxt_cdyt0, adz, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = _fesze(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + if (adztail != 0.0) { + TOP(bdxt_cdyt1, bdxt_cdyt0, adztail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = _fesze(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + } + if (adytail != 0.0) { + negate = -bdxtail; + TWP(negate, adytail, bdxt_adyt1, bdxt_adyt0); + TOP(bdxt_adyt1, bdxt_adyt0, cdz, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = _fesze(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + if (cdztail != 0.0) { + TOP(bdxt_adyt1, bdxt_adyt0, cdztail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = _fesze(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + } + } + if (cdxtail != 0.0) { + if (adytail != 0.0) { + TWP(cdxtail, adytail, cdxt_adyt1, cdxt_adyt0); + TOP(cdxt_adyt1, cdxt_adyt0, bdz, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = _fesze(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + if (bdztail != 0.0) { + TOP(cdxt_adyt1, cdxt_adyt0, bdztail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = _fesze(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + } + if (bdytail != 0.0) { + negate = -cdxtail; + TWP(negate, bdytail, cdxt_bdyt1, cdxt_bdyt0); + TOP(cdxt_bdyt1, cdxt_bdyt0, adz, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = _fesze(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + if (adztail != 0.0) { + TOP(cdxt_bdyt1, cdxt_bdyt0, adztail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = _fesze(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + } + } + + if (adztail != 0.0) { + wlength = _seze(bctlen, bct, adztail, w); + finlength = _fesze(finlength, finnow, wlength, w, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (bdztail != 0.0) { + wlength = _seze(catlen, cat, bdztail, w); + finlength = _fesze(finlength, finnow, wlength, w, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (cdztail != 0.0) { + wlength = _seze(abtlen, abt, cdztail, w); + finlength = _fesze(finlength, finnow, wlength, w, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + + return finnow[finlength - 1]; +} + + + + + +/*****************************************************************************/ +/* */ +/* PUBLIC FUNCTIONS */ +/* initPredicates() Sets the variables used for exact arithmetic. This */ +/* must be called once before using the other two functions. */ +/* orient2d() Computes the orientation of three 2D points. */ +/* orient3d() Computes the orientation of four 3D points. */ +/* */ +/*****************************************************************************/ + +void initPredicates() +{ + static char a_c=0; + double hf, ck, lc; + int e_o; + + if (a_c) return; else a_c = 1; + +#ifdef SPECIFY_FP_PRECISION + unsigned int old_cfp; + _controlfp_s(&old_cfp, _PC_53, MCW_PC); +#endif + + e_o = 1; + _eps = _spl = ck = 1.0; + hf = 0.5; + + do + { + lc=ck; + _eps *= hf; + if (e_o) _spl *= 2.0; + e_o = !e_o; + ck = 1.0 + _eps; + } while ((ck != 1.0) && (ck != lc)); + _spl += 1.0; + + _reb = (3.0 + 8.0 * _eps) * _eps; + _ccwebA = (3.0 + 16.0 * _eps) * _eps; + _ccwebB = (2.0 + 12.0 * _eps) * _eps; + _ccwebC = (9.0 + 64.0 * _eps) * _eps * _eps; + _o3ebA = (7.0 + 56.0 * _eps) * _eps; + _o3ebB = (3.0 + 28.0 * _eps) * _eps; + _o3ebC = (26.0 + 288.0 * _eps) * _eps * _eps; + _iccebA = (10.0 + 96.0 * _eps) * _eps; + _iccebB = (4.0 + 48.0 * _eps) * _eps; + _iccebC = (44.0 + 576.0 * _eps) * _eps * _eps; + _ispebA = (16.0 + 224.0 * _eps) * _eps; + _ispebB = (5.0 + 72.0 * _eps) * _eps; + _ispebC = (71.0 + 1408.0 * _eps) * _eps * _eps; + +#ifdef SPECIFY_FP_PRECISION + _controlfp_s(&old_cfp, _CW_DEFAULT, MCW_PC); +#endif +} + +double orient2d(double *pa, double *pb, double *pc) +{ + double dlf, drg, det, dsm, eb; + + dlf = (pa[0]-pc[0])*(pb[1]-pc[1]); + drg = (pa[1]-pc[1])*(pb[0]-pc[0]); + det = dlf - drg; + + if (dlf > 0.0) {if (drg <= 0.0) return det; else dsm = dlf + drg;} + else if (dlf < 0.0) {if (drg >= 0.0) return det; else dsm = -dlf - drg;} + else return det; + + eb = _ccwebA*dsm; + if ((det>=eb) || (-det>=eb)) return det; + + return _adaptive2dorientation(pa, pb, pc, dsm); +} + +double orient3d(double *pa, double *pb, double *pc, double *pd) +{ + double adx, bdx, cdx, ady, bdy, cdy, adz, bdz, cdz, pm, eb; + double bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady, det; + + 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]; + + bdxcdy = bdx*cdy; cdxbdy = cdx*bdy; + cdxady = cdx*ady; adxcdy = adx*cdy; + adxbdy = adx*bdy; bdxady = bdx*ady; + + det = adz*(bdxcdy-cdxbdy)+bdz*(cdxady-adxcdy)+cdz*(adxbdy-bdxady); + pm=(FABS(bdxcdy)+FABS(cdxbdy))*FABS(adz)+(FABS(cdxady)+FABS(adxcdy))*FABS(bdz)+(FABS(adxbdy)+FABS(bdxady))*FABS(cdz); + eb = _o3ebA*pm; + if ((det>eb) || (-det>eb)) return det; + return _adaptive3dorientation(pa, pb, pc, pd, pm); +} diff --git a/src/mesh_fix/src/Kernel/point.cpp b/src/mesh_fix/src/Kernel/point.cpp new file mode 100644 index 000000000..7d82b7f85 --- /dev/null +++ b/src/mesh_fix/src/Kernel/point.cpp @@ -0,0 +1,653 @@ +/**************************************************************************** +* TMesh * +* * +* Consiglio Nazionale delle Ricerche * +* Istituto di Matematica Applicata e Tecnologie Informatiche * +* Sezione di Genova * +* IMATI-GE / CNR * +* * +* Authors: Marco Attene * +* Copyright(C) 2012: IMATI-GE / CNR * +* All rights reserved. * +* * +* This program is dual-licensed as follows: * +* * +* (1) You may use TMesh as 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 3 of the License, or * +* (at your option) any later version. * +* In this case the 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 (http://www.gnu.org/licenses/gpl.txt) * +* for more details. * +* * +* (2) You may use TMesh as part of a commercial software. In this case a * +* proper agreement must be reached with the Authors and with IMATI-GE/CNR * +* based on a proper licensing contract. * +* * +****************************************************************************/ + +#include "point.h" +#include +#include +#include + +namespace T_MESH +{ + +const Point INFINITE_POINT(DBL_MAX, DBL_MAX, DBL_MAX); + +#ifdef USE_HYBRID_KERNEL + +PM_Rational orient2D(const PM_Rational& px, const PM_Rational& py, const PM_Rational& qx, const PM_Rational& qy, const PM_Rational& rx, const PM_Rational& ry) +{ + if (!PM_Rational::isUsingFiltering() && !PM_Rational::isUsingRationals()) + { + return ((px - rx)*(qy - ry) - (py - ry)*(qx - rx)); + } + else if (px.isOfDoubleType() && py.isOfDoubleType() && qx.isOfDoubleType() && qy.isOfDoubleType() && rx.isOfDoubleType() && ry.isOfDoubleType()) + { + double pqr[6]; + pqr[0] = px.getDVal(); pqr[1] = py.getDVal(); + pqr[2] = qx.getDVal(); pqr[3] = qy.getDVal(); + pqr[4] = rx.getDVal(); pqr[5] = ry.getDVal(); + return orient2d(pqr, pqr + 2, pqr + 4); + } + else if (PM_Rational::isUsingRationals()) + { + return ((px - rx)*(qy - ry) - (py - ry)*(qx - rx)); + } + else + { + PM_Rational::use_rationals = true; + PM_Rational O = ((px - rx)*(qy - ry) - (py - ry)*(qx - rx)); + PM_Rational::use_rationals = false; + return O; + } +} + +PM_Rational orient3D(const Point *t, const Point *a, const Point *b, const Point *c) +{ + if (!PM_Rational::isUsingFiltering() && !PM_Rational::isUsingRationals()) + { + return TMESH_DETERMINANT3X3(t->x - c->x, t->y - c->y, t->z - c->z, a->x - c->x, a->y - c->y, a->z - c->z, b->x - c->x, b->y - c->y, b->z - c->z); + } else if (a->x.isOfDoubleType() && a->y.isOfDoubleType() && a->z.isOfDoubleType() && + t->x.isOfDoubleType() && t->y.isOfDoubleType() && t->z.isOfDoubleType() && + b->x.isOfDoubleType() && b->y.isOfDoubleType() && b->z.isOfDoubleType() && + c->x.isOfDoubleType() && c->y.isOfDoubleType() && c->z.isOfDoubleType()) + { + double p1[3], p2[3], p3[3], p4[3]; + p1[0] = (t->x).getDVal(); p1[1] = (t->y).getDVal(); p1[2] = (t->z).getDVal(); + p2[0] = (a->x).getDVal(); p2[1] = (a->y).getDVal(); p2[2] = (a->z).getDVal(); + p3[0] = (b->x).getDVal(); p3[1] = (b->y).getDVal(); p3[2] = (b->z).getDVal(); + p4[0] = (c->x).getDVal(); p4[1] = (c->y).getDVal(); p4[2] = (c->z).getDVal(); + return orient3d(p1, p2, p3, p4); + } else if (PM_Rational::isUsingRationals()) + { + return TMESH_DETERMINANT3X3(t->x - c->x, t->y - c->y, t->z - c->z, a->x - c->x, a->y - c->y, a->z - c->z, b->x - c->x, b->y - c->y, b->z - c->z); + } else + { + PM_Rational::use_rationals = true; + PM_Rational O = TMESH_DETERMINANT3X3(t->x - c->x, t->y - c->y, t->z - c->z, a->x - c->x, a->y - c->y, a->z - c->z, b->x - c->x, b->y - c->y, b->z - c->z); + PM_Rational::use_rationals = false; + return O; + } +} + +#else + +PM_Rational orient2D(const PM_Rational& px, const PM_Rational& py, const PM_Rational& qx, const PM_Rational& qy, const PM_Rational& rx, const PM_Rational& ry) +{ +// return ((px - rx)*(qy - ry) - (py - ry)*(qx - rx)); + + double pqr[6]; + pqr[0] = TMESH_TO_DOUBLE(px); pqr[1] = TMESH_TO_DOUBLE(py); + pqr[2] = TMESH_TO_DOUBLE(qx); pqr[3] = TMESH_TO_DOUBLE(qy); + pqr[4] = TMESH_TO_DOUBLE(rx); pqr[5] = TMESH_TO_DOUBLE(ry); + return orient2d(pqr, pqr + 2, pqr + 4); +} + +PM_Rational orient3D(const Point *t, const Point *a, const Point *b, const Point *c) +{ +// return TMESH_DETERMINANT3X3(t->x - c->x, t->y - c->y, t->z - c->z, a->x - c->x, a->y - c->y, a->z - c->z, b->x - c->x, b->y - c->y, b->z - c->z); + + double p1[3], p2[3], p3[3], p4[3]; + p1[0] = TMESH_TO_DOUBLE(t->x); p1[1] = TMESH_TO_DOUBLE(t->y); p1[2] = TMESH_TO_DOUBLE(t->z); + p2[0] = TMESH_TO_DOUBLE(a->x); p2[1] = TMESH_TO_DOUBLE(a->y); p2[2] = TMESH_TO_DOUBLE(a->z); + p3[0] = TMESH_TO_DOUBLE(b->x); p3[1] = TMESH_TO_DOUBLE(b->y); p3[2] = TMESH_TO_DOUBLE(b->z); + p4[0] = TMESH_TO_DOUBLE(c->x); p4[1] = TMESH_TO_DOUBLE(c->y); p4[2] = TMESH_TO_DOUBLE(c->z); + return orient3d(p1, p2, p3, p4); +} + +#endif + +coord Point::exactOrientation(const Point *a, const Point *b, const Point *c) const +{ + return orient3D(this, a, b, c); +} + + +////////////// Alignment check ///////////// + +bool Point::exactMisalignment(const Point *A, const Point *B) const +{ + if (orient2D(x, y, A->x, A->y, B->x, B->y) != 0) return true; + if (orient2D(y, z, A->y, A->z, B->y, B->z) != 0) return true; + if (orient2D(z, x, A->z, A->x, B->z, B->x) != 0) return true; + + return false; +} + +bool Point::exactSameSideOnPlane(const Point *Q, const Point *A, const Point *B) const +{ + coord o1, o2; + int s1, s2; + + o1 = orient2D(x, y, A->x, A->y, B->x, B->y); + o2 = orient2D(Q->x, Q->y, A->x, A->y, B->x, B->y); + s1 = (o1>0) ? (1) : ((o1<0) ? (-1) : (0)); s2 = (o2>0) ? (1) : ((o2<0) ? (-1) : (0)); + if (s1 != s2) return false; + + o1 = orient2D(y, z, A->y, A->z, B->y, B->z); + o2 = orient2D(Q->y, Q->z, A->y, A->z, B->y, B->z); + s1 = (o1>0) ? (1) : ((o1<0) ? (-1) : (0)); s2 = (o2>0) ? (1) : ((o2<0) ? (-1) : (0)); + if (s1 != s2) return false; + + o1 = orient2D(z, x, A->z, A->x, B->z, B->x); + o2 = orient2D(Q->z, Q->x, A->z, A->x, B->z, B->x); + s1 = (o1>0) ? (1) : ((o1<0) ? (-1) : (0)); s2 = (o2>0) ? (1) : ((o2<0) ? (-1) : (0)); + if (s1 != s2) return false; + + return true; +} + +// Returns true if the coplanar point 'p' is in the inner area of 't'. +// Undetermined if p and t are not coplanar. +bool Point::pointInInnerTriangle(const Point *p, const Point *v1, const Point *v2, const Point *v3) +{ + //if (!p->exactSameSideOnPlane(v1, v2, v3)) return false; + //if (!p->exactSameSideOnPlane(v2, v3, v1)) return false; + //if (!p->exactSameSideOnPlane(v3, v1, v2)) return false; + //return true; + + // Less readable, but slightly more efficient (12 predicates instead of 18) + + coord o1, o2, oo2, oo4, oo6; + int s1, s2; + + o1 = orient2D(p->x, p->y, v2->x, v2->y, v3->x, v3->y); + o2 = oo2 = orient2D(v1->x, v1->y, v2->x, v2->y, v3->x, v3->y); + s1 = (o1>0) ? (1) : ((o1<0) ? (-1) : (0)); s2 = (o2>0) ? (1) : ((o2<0) ? (-1) : (0)); + if (s1 != s2) return false; + + o1 = orient2D(p->y, p->z, v2->y, v2->z, v3->y, v3->z); + o2 = oo4 = orient2D(v1->y, v1->z, v2->y, v2->z, v3->y, v3->z); + s1 = (o1>0) ? (1) : ((o1<0) ? (-1) : (0)); s2 = (o2>0) ? (1) : ((o2<0) ? (-1) : (0)); + if (s1 != s2) return false; + + o1 = orient2D(p->z, p->x, v2->z, v2->x, v3->z, v3->x); + o2 = oo6 = orient2D(v1->z, v1->x, v2->z, v2->x, v3->z, v3->x); + s1 = (o1>0) ? (1) : ((o1<0) ? (-1) : (0)); s2 = (o2>0) ? (1) : ((o2<0) ? (-1) : (0)); + if (s1 != s2) return false; + + o1 = orient2D(p->x, p->y, v3->x, v3->y, v1->x, v1->y); + o2 = oo2, + s1 = (o1>0) ? (1) : ((o1<0) ? (-1) : (0)); s2 = (o2>0) ? (1) : ((o2<0) ? (-1) : (0)); + if (s1 != s2) return false; + + o1 = orient2D(p->y, p->z, v3->y, v3->z, v1->y, v1->z); + o2 = oo4; + s1 = (o1>0) ? (1) : ((o1<0) ? (-1) : (0)); s2 = (o2>0) ? (1) : ((o2<0) ? (-1) : (0)); + if (s1 != s2) return false; + + o1 = orient2D(p->z, p->x, v3->z, v3->x, v1->z, v1->x); + o2 = oo6; + s1 = (o1>0) ? (1) : ((o1<0) ? (-1) : (0)); s2 = (o2>0) ? (1) : ((o2<0) ? (-1) : (0)); + if (s1 != s2) return false; + + o1 = orient2D(p->x, p->y, v1->x, v1->y, v2->x, v2->y); + o2 = oo2; + s1 = (o1>0) ? (1) : ((o1<0) ? (-1) : (0)); s2 = (o2>0) ? (1) : ((o2<0) ? (-1) : (0)); + if (s1 != s2) return false; + + o1 = orient2D(p->y, p->z, v1->y, v1->z, v2->y, v2->z); + o2 = oo4; + s1 = (o1>0) ? (1) : ((o1<0) ? (-1) : (0)); s2 = (o2>0) ? (1) : ((o2<0) ? (-1) : (0)); + if (s1 != s2) return false; + + o1 = orient2D(p->z, p->x, v1->z, v1->x, v2->z, v2->x); + o2 = oo6; + s1 = (o1>0) ? (1) : ((o1<0) ? (-1) : (0)); s2 = (o2>0) ? (1) : ((o2<0) ? (-1) : (0)); + if (s1 != s2) return false; + + return true; +} + +//////// Lexicographic Point comparison ////////// + +// This can be used with std::sort() +bool Point::operator<(const Point& s) const +{ + if (xs.x) return false; + if (ys.y) return false; + if (zx - ((Point *)b)->x)) < 0) return -1; + if (c > 0) return 1; + if ((c=(((Point *)a)->y - ((Point *)b)->y)) < 0) return -1; + if (c > 0) return 1; + if ((c=(((Point *)a)->z - ((Point *)b)->z)) < 0) return -1; + if (c > 0) return 1; + + return 0; +} + +//////////////// Normalization ///////////////////////// + +void Point::normalize() +{ + coord l = length(); + + if (l == 0) TMesh::error("normalize : Trying to normalize a null vector !\n"); + + x/=l; + y/=l; + z/=l; +} + + +//////////////////// Point rotation //////////////////// +/////////// 'ang' radians CCW around 'axis' //////////// + +void Point::rotate(const Point& a, const double& ang) +{ + double l, q[4], m[3][3]; + if ((l = a.length())==0.0) return; + l = sin(ang/2.0)/l; + + q[0] = TMESH_TO_DOUBLE(a.x)*l; + q[1] = TMESH_TO_DOUBLE(a.y)*l; + q[2] = TMESH_TO_DOUBLE(a.z)*l; + q[3] = cos(ang/2.0); + + m[0][0] = 1.0 - (q[1]*q[1] + q[2]*q[2])*2.0; + m[0][1] = (q[0] * q[1] + q[2] * q[3])*2.0; + m[0][2] = (q[2] * q[0] - q[1] * q[3])*2.0; + + m[1][0] = (q[0] * q[1] - q[2] * q[3])*2.0; + m[1][1] = 1.0 - (q[2] * q[2] + q[0] * q[0])*2.0; + m[1][2] = (q[1] * q[2] + q[0] * q[3])*2.0; + + m[2][0] = (q[2] * q[0] + q[1] * q[3])*2.0; + m[2][1] = (q[1] * q[2] - q[0] * q[3])*2.0; + m[2][2] = 1.0 - (q[1] * q[1] + q[0] * q[0])*2.0; + + q[0] = TMESH_TO_DOUBLE(x); q[1] = TMESH_TO_DOUBLE(y); q[2] = TMESH_TO_DOUBLE(z); + x = m[0][0]*q[0] + m[1][0]*q[1] + m[2][0]*q[2]; + y = m[0][1]*q[0] + m[1][1]*q[1] + m[2][1]*q[2]; + z = m[0][2]*q[0] + m[1][2]*q[1] + m[2][2]*q[2]; +} + + +///// Project the point on the plane whose normal is 'nor' ///// + +void Point::project(const Point *nor) +{ + Point pr = (*this)-((*nor)*((*this)*(*nor))); + x = pr.x; y = pr.y; z = pr.z; +} + + +/////////// Distance from the line passing through A and B //////// + +double Point::distanceFromLine(const Point *A, const Point *B) const +{ + Point BA = (*B)-(*A); + double lba = BA.length(); + + if (lba == 0.0) TMesh::error("distanceFromLine : Degenerate line passed !\n"); + + return ((((*this)-(*A))&BA).length())/(lba); +} + + +/////////////////// Distance from a line /////////////////////// +//// 'cc' is initialized as the point of the line whose //// +//// distance from 'this' is minimum. //// + +double Point::distanceFromLine(const Point *A, const Point *B, Point *cc) const +{ + Point AB = (*A)-(*B); + Point AP = (*A)-(*this); + Point BP = (*B)-(*this); + + if (AP.isNull()) + { + cc->x = A->x; cc->y = A->y; cc->z = A->z; + return 0.0; + } + else if (BP.isNull()) + { + cc->x = B->x; cc->y = B->y; cc->z = B->z; + return 0.0; + } + + coord t = (AB*AB); + if (t == 0.0) TMesh::error("distanceFromLine : Degenerate line passed !\n"); + else t = (AP*AB)/(-t); + cc->x = t*AB.x + A->x; + cc->y = t*AB.y + A->y; + cc->z = t*AB.z + A->z; + return distanceFromLine(A,B); +} + + +////////////// Projection on the line passing through A and B /////////// + +Point Point::projection(const Point *A, const Point *B) const +{ + Point BA = (*B)-(*A); + coord l = BA*BA; + if (l == 0.0) TMesh::error("projection : Degenerate line passed !\n"); + + return ((*A)+(BA*((BA*((*this)-(*A)))/(l)))); +} + + +////////////// Distance from a segment ///////////////// + +double Point::distanceFromEdge(const Point *A, const Point *B) const +{ + Point AP = (*A)-(*this); double apl = AP.length(); + Point BP = (*B) - (*this); double bpl = BP.length(); + + if (apl == 0 || bpl == 0.0) return 0.0; + + Point AB = (*A) - (*B); double abl = AP.length(); + Point BA = (*B)-(*A); + + if (abl*apl == 0.0 || abl*bpl == 0.0) return apl; + + if (AB.getAngle(AP) > PI2) return apl; + else if (BA.getAngle(BP) > PI2) return bpl; + + return distanceFromLine(A,B); +} + +/////////////////// Distance from a segment /////////////////////// +//// 'cc' is initialized as the point of the segment whose //// +//// distance from 'this' is minimum. //// + +double Point::distanceFromEdge(const Point *A, const Point *B, Point *cc) const +{ + Point AP = (*A)-(*this); double apl = AP.length(); + Point BP = (*B) - (*this); double bpl = BP.length(); + + if (apl == 0) {cc->setValue(A); return 0.0;} + if (bpl == 0) {cc->setValue(B); return 0.0;} + + Point AB = (*A)-(*B); coord abl = AP.length(); + Point BA = (*B)-(*A); + + if (abl*apl == 0.0 || abl*bpl == 0.0) {cc->setValue(A); return apl;} + + if (AB.getAngle(AP) > PI2) {cc->setValue(A); return apl;} + else if (BA.getAngle(BP) > PI2) {cc->setValue(B); return bpl;} + + coord t = (AB*AB); + if (t == 0.0) {cc->setValue(A); return apl;} + else t = (AP*AB)/(-t); + cc->x = t*AB.x + A->x; + cc->y = t*AB.y + A->y; + cc->z = t*AB.z + A->z; + return distanceFromLine(A,B); +} + +///////////////// Angle between two vectors /////////////// + +double Point::getAngle(const Point& p) const +{ + return atan2(((*this)&p).length(), TMESH_TO_DOUBLE(((*this)*p))); +} + + +/////////// Distance of two straight lines /////////////// + +double Point::distanceLineLine(const Point *A, const Point *A1, const Point *B1) const +{ + Point uu1 = ((*this)-(*A))&((*A1)-(*B1)); + coord nom = ((*A)-(*A1))*(uu1); + return FABS(TMESH_TO_DOUBLE(nom)) / (uu1.length()); +} + + +/////////// Solution of a linear system 3 x 3 ////////// +///// System Ax = d, where A = (a,b,c) rows, d = this ///// + +Point Point::linearSystem(const Point& a, const Point& b, const Point& c) +{ + Point ret; + coord det_A = TMESH_DETERMINANT3X3(a.x, a.y, a.z, b.x, b.y, b.z, c.x, c.y, c.z); + if (det_A == 0.0) return INFINITE_POINT; + ret.x = TMESH_DETERMINANT3X3(x, a.y, a.z, y, b.y, b.z, z, c.y, c.z); + ret.y = TMESH_DETERMINANT3X3(a.x, x, a.z, b.x, y, b.z, c.x, z, c.z); + ret.z = TMESH_DETERMINANT3X3(a.x, a.y, x, b.x, b.y, y, c.x, c.y, z); + + return (ret/det_A); +} + + + +//// Computes the closest points of the two lines 'this'-v1 and p1-p2 //// +//// Returns FALSE if the lines are parallel. //// + +int Point::closestPoints(const Point *v1, const Point *p1, const Point *p2, Point *ptOnThis, Point *ptOnLine2) const +{ + Point pos1 = *this; Point dir1 = (*v1)-pos1; + Point pos2 = *p1; Point dir2 = (*p2)-pos2; + coord d1l = dir1.length(), d2l = dir2.length(); + + if (d1l == 0.0 && d2l == 0.0) + {ptOnThis->setValue(this); ptOnLine2->setValue(p1); return 1;} + if (d1l*d2l == 0.0) + { + if (d1l <= d2l) + {ptOnThis->setValue(this); distanceFromLine(p1, p2, ptOnLine2); return 1;} + if (d2l <= d1l) + {ptOnLine2->setValue(p1); p1->distanceFromLine(this, v1, ptOnThis); return 1;} + } + + coord ang = dir1.getAngle(dir2); + if (ang == 0.0 || ang == M_PI) return 0; + + coord s, t, A, B, C, D, E, F, denom; + + denom = ((dir1*dir2)/(d1l*d2l)); + denom = denom*denom - 1; + + dir1.normalize(); + dir2.normalize(); + + A = E = dir1*dir2; + B = dir1*dir1; + C = (dir1*pos1) - (dir1*pos2); + D = dir2*dir2; + F = (dir2*pos1) - (dir2*pos2); + + s = ( C * D - A * F ) / denom; + t = ( C * E - B * F ) / denom; + *ptOnThis = pos1 + (dir1*s); + *ptOnLine2 = pos2 + (dir2*t); + +// Uncomment the following to compute the distance between segments +// if (s < 0 || s > ((*v1)-(*this)).length() || t < 0 || t > ((*p2)-(*p1)).length()) +// return 0; // The points does not belong to the edges + + return 1; +} + + + +// Returns the point of intersection between the two lines defined by (p,q) and (r,s) respectively +// Return INFINITE_POINT is lines are parallel or if p==q or r==s +Point Point::lineLineIntersection(const Point& p, const Point& q, const Point& r, const Point& s) +{ + Point da = q - p; + Point db = s - r; + Point dc = r - p; + Point dab = (da&db); + + if (dc*dab != 0.0) return INFINITE_POINT; + + coord k = (((dc&db)*dab) / (dab*dab)); + return p + (da*k); +} + +// Returns the point of intersection between the line for (p,q) and the plane for (r,s,t) +// Returns INFINITE_POINT in case of parallelism +Point Point::linePlaneIntersection(const Point& p, const Point& q, const Point& r, const Point& s, const Point& t) +{ + coord den = TMESH_DETERMINANT3X3(p.x - q.x, p.y - q.y, p.z - q.z, s.x - r.x, s.y - r.y, s.z - r.z, t.x - r.x, t.y - r.y, t.z - r.z); + if (den == 0) return INFINITE_POINT; + coord num = TMESH_DETERMINANT3X3(p.x - r.x, p.y - r.y, p.z - r.z, s.x - r.x, s.y - r.y, s.z - r.z, t.x - r.x, t.y - r.y, t.z - r.z); + coord gamma = num / den; + return p + ((q - p)*gamma); +} + +coord Point::squaredTriangleArea3D(const Point& p, const Point& q, const Point& r) +{ + Point pr = (p - r), qr = (q - r); + Point n = pr&qr; + return (n*n) / 4; +} + + +////////////////////////////////////////////////////////////////// +// +// Basic predicates of type 'pointIn' +// +////////////////////////////////////////////////////////////////// + +// Returns true if 'p' is a point of the segment v1-v2 (endpoints excluded) +bool Point::pointInInnerSegment(const Point *p, const Point *v1, const Point *v2) +{ + if (!p->exactMisalignment(v1, v2)) // Segment and point aligned + { + if (v1->x < v2->x && v1->x < p->x && p->x < v2->x) return true; + if (v1->y < v2->y && v1->y < p->y && p->y < v2->y) return true; + if (v1->z < v2->z && v1->z < p->z && p->z < v2->z) return true; + if (v1->x > v2->x && v1->x > p->x && p->x > v2->x) return true; + if (v1->y > v2->y && v1->y > p->y && p->y > v2->y) return true; + if (v1->z > v2->z && v1->z > p->z && p->z > v2->z) return true; + } + return false; +} + +// Returns true if 'p' is a point of the segment v1-v2 (endpoints included) +bool Point::pointInSegment(const Point *p, const Point *v1, const Point *v2) +{ + return ((*p) == (*(v1)) || (*p) == (*(v2)) || Point::pointInInnerSegment(p, v1, v2)); +} + + +// Returns true if the coplanar point 'p' is either in the inner area of +// 't' or on its border. Undetermined if p and t are not coplanar. +bool Point::pointInTriangle(const Point *p, const Point *v1, const Point *v2, const Point *v3) +{ + if (Point::pointInSegment(p, v1, v2)) return true; + else if (Point::pointInSegment(p, v2, v3)) return true; + else if (Point::pointInSegment(p, v3, v1)) return true; + else return Point::pointInInnerTriangle(p, v1, v2, v3); +} + + +////////////////////////////////////////////////////////////////// +// +// Basic predicates of type 'segmentIntersects' +// +////////////////////////////////////////////////////////////////// + +// true if (p1-p2) properly intersects (sp1-sp2) at any point (endpoints included). +// Collinear overlapping segments are not considered to be properly intersecting. +bool Point::segmentsIntersect(const Point *p1, const Point *p2, const Point *sp1, const Point *sp2) +{ + return (p1->exactOrientation(p2, sp1, sp2) == 0 && !p1->exactSameSideOnPlane(p2, sp1, sp2) && !sp1->exactSameSideOnPlane(sp2, p1, p2)); +} + +// Returns true if the interior of (p1-p2) properly intersects the interior of (sp1-sp2). +// Collinear overlapping segments are not considered to be properly intersecting. +bool Point::innerSegmentsCross(const Point& p1, const Point& p2, const Point& sp1, const Point& sp2) +{ + if (p1 == sp1 || p1 == sp2 || p2 == sp1 || p2 == sp2) return false; + return (p1.exactOrientation(&p2, &sp1, &sp2) == 0 && !p1.exactSameSideOnPlane(&p2, &sp1, &sp2) && !sp1.exactSameSideOnPlane(&sp2, &p1, &p2)); +} + +bool Point::segmentIntersectsTriangle(const Point *s1, const Point *s2, const Point *v1, const Point *v2, const Point *v3) +{ + coord o1, o2, o3; + + coord mx = MIN(s1->x, s2->x); + if (v1->x < mx && v2->x < mx && v3->x < mx) return false; + mx = MAX(s1->x, s2->x); + if (v1->x > mx && v2->x > mx && v3->x > mx) return false; + mx = MIN(s1->y, s2->y); + if (v1->y < mx && v2->y < mx && v3->y < mx) return false; + mx = MAX(s1->y, s2->y); + if (v1->y > mx && v2->y > mx && v3->y > mx) return false; + mx = MIN(s1->z, s2->z); + if (v1->z < mx && v2->z < mx && v3->z < mx) return false; + mx = MAX(s1->z, s2->z); + if (v1->z > mx && v2->z > mx && v3->z > mx) return false; + + o1 = s1->exactOrientation(v1, v2, v3); + o2 = s2->exactOrientation(v1, v2, v3); + if (o1 == 0 && o2 == 0) + { + if (!s1->exactSameSideOnPlane(s2, v1, v2) && !v1->exactSameSideOnPlane(v2, s1, s2)) return true; + if (!s1->exactSameSideOnPlane(s2, v2, v3) && !v2->exactSameSideOnPlane(v3, s1, s2)) return true; + if (!s1->exactSameSideOnPlane(s2, v3, v1) && !v3->exactSameSideOnPlane(v1, s1, s2)) return true; + if (Point::pointInInnerTriangle(s1, v1, v2, v3) && Point::pointInInnerTriangle(s2, v1, v2, v3)) return true; + return false; + } + + if ((o1>0 && o2>0) || (o1<0 && o2<0)) return false; // s1 and s2 are both above/below v1,v2,v3 + o1 = s1->exactOrientation(s2, v1, v2); + o2 = s1->exactOrientation(s2, v2, v3); + if ((o1>0 && o2<0) || (o1<0 && o2>0)) return false; + o3 = s1->exactOrientation(s2, v3, v1); + if ((o1>0 && o3<0) || (o1<0 && o3>0)) return false; + if ((o2>0 && o3<0) || (o2<0 && o3>0)) return false; + return true; +} + +bool Point::segmentIntersectsTriangle(const Point *s1, const Point *s2, const Point *v1, const Point *v2, const Point *v3, const coord& oo1, const coord& oo2) +{ + // In this case the fast reject by bounding box appears to be a disadvantage ... + if (oo1 == 0 && oo2 == 0) + { + if (!s1->exactSameSideOnPlane(s2, v1, v2) && !v1->exactSameSideOnPlane(v2, s1, s2)) return true; + if (!s1->exactSameSideOnPlane(s2, v2, v3) && !v2->exactSameSideOnPlane(v3, s1, s2)) return true; + if (!s1->exactSameSideOnPlane(s2, v3, v1) && !v3->exactSameSideOnPlane(v1, s1, s2)) return true; + if (Point::pointInInnerTriangle(s1, v1, v2, v3) && Point::pointInInnerTriangle(s2, v1, v2, v3)) return true; + return false; + } + + if ((oo1>0 && oo2>0) || (oo1<0 && oo2<0)) return false; // s1 and s2 are both above/below v1,v2,v3 + coord o1, o2, o3; + o1 = s1->exactOrientation(s2, v1, v2); + o2 = s1->exactOrientation(s2, v2, v3); + if ((o1>0 && o2<0) || (o1<0 && o2>0)) return false; + o3 = s1->exactOrientation(s2, v3, v1); + if ((o1>0 && o3<0) || (o1<0 && o3>0)) return false; + if ((o2>0 && o3<0) || (o2<0 && o3>0)) return false; + return true; +} + +} //namespace T_MESH diff --git a/src/mesh_fix/src/Kernel/tmesh.cpp b/src/mesh_fix/src/Kernel/tmesh.cpp new file mode 100644 index 000000000..97ab75a06 --- /dev/null +++ b/src/mesh_fix/src/Kernel/tmesh.cpp @@ -0,0 +1,259 @@ +/**************************************************************************** +* TMesh * +* * +* Consiglio Nazionale delle Ricerche * +* Istituto di Matematica Applicata e Tecnologie Informatiche * +* Sezione di Genova * +* IMATI-GE / CNR * +* * +* Authors: Marco Attene * +* Copyright(C) 2012: IMATI-GE / CNR * +* All rights reserved. * +* * +* This program is dual-licensed as follows: * +* * +* (1) You may use TMesh as 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 3 of the License, or * +* (at your option) any later version. * +* In this case the 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 (http://www.gnu.org/licenses/gpl.txt) * +* for more details. * +* * +* (2) You may use TMesh as part of a commercial software. In this case a * +* proper agreement must be reached with the Authors and with IMATI-GE/CNR * +* based on a proper licensing contract. * +* * +****************************************************************************/ + +#include "tmesh_kernel.h" +#include +#include +#include + +namespace T_MESH +{ + +extern "C" void initPredicates(); + +void (* TMesh::display_message)(const char*, int) = NULL; + +const char *TMesh::app_name = NULL; +const char *TMesh::app_version = NULL; +const char *TMesh::app_year = NULL; +const char *TMesh::app_authors = NULL; +const char *TMesh::app_url = NULL; +const char *TMesh::app_maillist = NULL; +const char *TMesh::filename = NULL; +bool TMesh::quiet = false; + +void TMesh::init(void (*dm)(const char *, int)) +{ + display_message = dm; + app_name = NULL; + app_version = NULL; + app_year = NULL; + app_authors = NULL; + app_url = NULL; + app_maillist = NULL; + filename = NULL; + quiet = false; + initPredicates(); +} + + +///////////// Prints a fatal error message and exits ///////////// + +void TMesh::error(const char *msg, ...) +{ + static char fmt[2048], fms[4096]; + va_list ap; + va_start(ap, msg); + strcpy(fmt,"\nERROR- "); + strcat(fmt,msg); + vsprintf(fms,fmt,ap); + + if (display_message != NULL) + display_message(fms, DISPMSG_ACTION_ERRORDIALOG); + else + { + fprintf(stderr,fms); + exit(-1); + } +} + +///////////// Prints a warning message ///////////// + +void TMesh::warning(const char *msg, ...) +{ + if (quiet) return; + static char fmt[2048], fms[4096]; + va_list ap; + va_start(ap, msg); + strcpy(fmt,"WARNING- "); + strcat(fmt,msg); + vsprintf(fms,fmt,ap); + + if (display_message != NULL) + display_message(fms, DISPMSG_ACTION_PUTMESSAGE); + else + fputs(fms, stderr); + + va_end(ap); +} + +///////////// Prints an information message ///////////// + +void TMesh::info(const char *msg, ...) +{ + if (quiet) return; + static char fmt[2048], fms[4096]; + va_list ap; + va_start(ap, msg); + strcpy(fmt,"INFO- "); + strcat(fmt,msg); + vsprintf(fms,fmt,ap); + + if (display_message != NULL) + display_message(fms, DISPMSG_ACTION_PUTMESSAGE); + else + printf(fms); + + va_end(ap); +} + +///////// Reports progress status for a process ////////// + +void TMesh::begin_progress() +{ + if (quiet) return; + if (display_message != NULL) + display_message("\n", DISPMSG_ACTION_PUTNEWLINE); + else + printf("\n"); +} + +void TMesh::report_progress(const char *msg, ...) +{ + if (quiet) return; + static char fmt[2048] = "\r"; + static char fms[4096]; + static char rotating_bar[5] = "-\\|/"; + static unsigned char wc=0; + + if (msg == NULL) + { + sprintf(fms,"%c",rotating_bar[wc++]); if (wc==4) wc=0; + strcpy(fmt+1,fms); + + if (display_message != NULL) + display_message(fmt, DISPMSG_ACTION_PUTPROGRESS); + else + { + printf("%s",fmt); + fflush(stdout); + } + } + else + { + va_list ap; + va_start(ap, msg); + strcpy(fmt+1,msg); + vsprintf(fms,fmt,ap); + + if (display_message != NULL) + display_message(fms, DISPMSG_ACTION_PUTPROGRESS); + else + { + printf("%s", fms); + fflush(stdout); + } + va_end(ap); + } +} + +void TMesh::end_progress() +{ + if (quiet) return; + if (display_message != NULL) + display_message("\n", DISPMSG_ACTION_PUTNEWLINE); + else + printf("\n"); +} + +void TMesh::useRationals(bool u) +{ +#ifdef USE_HYBRID_KERNEL + coord::useRationals(u); +#endif +} + +bool TMesh::isUsingRationals() +{ +#ifdef USE_HYBRID_KERNEL + return coord::isUsingRationals(); +#else + return false; +#endif +} + +void TMesh::useFiltering(bool u) +{ +#ifdef USE_HYBRID_KERNEL + coord::useFiltering(u); +#endif +} + +bool TMesh::isUsingFiltering() +{ +#ifdef USE_HYBRID_KERNEL + return coord::isUsingFiltering(); +#else + return false; +#endif +} + +void TMesh::addMessageToLogFile(const char *msg) +{ + FILE *fp = fopen("tmesh.log", "a"); + fprintf(fp, msg); + fclose(fp); +} + +// Get current date/time, format is YYYY-MM-DD.HH:mm:ss +char *currentDateTime() +{ + time_t now = time(0); + struct tm tstruct; + static char buf[80]; + tstruct = *localtime(&now); + strftime(buf, sizeof(buf), "%Y-%m-%d.%X", &tstruct); + + return buf; +} + +void TMesh::logToFileAndExit(const char *s) +{ + static char msg[2048]; + sprintf(msg, "%s\nFILE: %s\nRETURN VALUE: %s\n\n", currentDateTime(), (filename) ? (filename) : ("unknown"), s); + addMessageToLogFile(msg); + TMesh::error(msg); +} + +void TMesh::exitOnTimeout(clock_t ts) +{ + static clock_t beginning_time, timeout_secs; + if (ts != 0) { beginning_time = clock(); timeout_secs = ts; } + else if (((clock() - beginning_time) / 1000) > timeout_secs) logToFileAndExit("Timeout reached"); +} + +void TMesh::printElapsedTime(bool reset) +{ + static clock_t beginning_time; + if (reset) beginning_time = clock(); + else printf("\n\n********** PARTIAL ELAPSED: %d msecs\n\n", (clock() - beginning_time)); +} + +} //namespace T_MESH diff --git a/src/mesh_fix/src/MeshFix/meshfix.cpp b/src/mesh_fix/src/MeshFix/meshfix.cpp new file mode 100644 index 000000000..d05a7cbaa --- /dev/null +++ b/src/mesh_fix/src/MeshFix/meshfix.cpp @@ -0,0 +1,209 @@ +#include "tmesh.h" +#include +#include +#include + +using namespace T_MESH; + +double closestPair(List *bl1, List *bl2, Vertex **closest_on_bl1, Vertex **closest_on_bl2) +{ + Node *n, *m; + Vertex *v, *w; + double adist, mindist = DBL_MAX; + + FOREACHVVVERTEX(bl1, v, n) + FOREACHVVVERTEX(bl2, w, m) + if ((adist = w->squaredDistance(v))T)), t, n) t->info = NULL; + FOREACHVTTRIANGLE((&(tin->T)), t, n) if (t->info == NULL) + { + i++; + triList.appendHead(t); + t->info = (void *)(intptr_t)i; + + while (triList.numels()) + { + t = (Triangle *)triList.popHead(); + if ((s = t->t1()) != NULL && s->info == NULL) { triList.appendHead(s); s->info = (void *)(intptr_t)i; } + if ((s = t->t2()) != NULL && s->info == NULL) { triList.appendHead(s); s->info = (void *)(intptr_t)i; } + if ((s = t->t3()) != NULL && s->info == NULL) { triList.appendHead(s); s->info = (void *)(intptr_t)i; } + } + } + + if (i<2) + { + FOREACHVTTRIANGLE((&(tin->T)), t, n) t->info = NULL; + // JMesh::info("Mesh is a single component. Nothing done."); + return false; + } + + FOREACHVTTRIANGLE((&(tin->T)), t, n) + { + t->v1()->info = t->v2()->info = t->v3()->info = t->info; + } + + FOREACHVVVERTEX((&(tin->V)), v, n) if (!IS_VISITED2(v) && v->isOnBoundary()) + { + w = v; + one_loop = new List; + do + { + one_loop->appendHead(w); MARK_VISIT2(w); + w = w->nextOnBoundary(); + } while (w != v); + boundary_loops.appendHead(one_loop); + } + FOREACHVVVERTEX((&(tin->V)), v, n) UNMARK_VISIT2(v); + + bloops_array = (List **)boundary_loops.toArray(); + numloops = boundary_loops.numels(); + + int numtris = tin->T.numels(); + double adist, mindist = DBL_MAX; + + gv = NULL; + for (i = 0; ihead()->data)->info != ((Vertex *)bloops_array[j]->head()->data)->info) + { + adist = closestPair(bloops_array[i], bloops_array[j], &v, &w); + if (adistjoinBoundaryLoops(gv, gw, 1, 0); + + FOREACHVTTRIANGLE((&(tin->T)), t, n) t->info = NULL; + FOREACHVVVERTEX((&(tin->V)), v, n) v->info = NULL; + + free(bloops_array); + while ((one_loop = (List *)boundary_loops.popHead()) != NULL) delete one_loop; + + return (gv != NULL); +} + +void usage() +{ + printf("\nMeshFix V2.0 - by Marco Attene\n------\n"); + printf("Usage: MeshFix inmeshfile [outmeshfile] [-a] [-j] [-x]\n"); + printf(" Processes 'inmeshfile' and saves the result to 'outmeshfile'\n"); + printf(" If 'outmeshfile' is not specified 'inmeshfile_fixed.off' will be produced\n"); + printf(" Option '-a' = joins multiple open components before starting\n"); + printf(" Option '-j' = output files in STL format insted of OFF\n"); + printf(" Option '-x' exits if output file already exists.\n"); + printf(" Accepted input formats are OFF, PLY and STL.\n Other formats are supported only partially.\n"); + printf("\nIf MeshFix is used for research purposes, please cite the following paper:\n"); + printf("\n M. Attene.\n A lightweight approach to repairing digitized polygon meshes.\n The Visual Computer, 2010. (c) Springer.\n"); + printf("\nHIT ENTER TO EXIT.\n"); + getchar(); + exit(0); +} + +char *createFilename(const char *iname, const char *subext, char *oname, const char *newextension) +{ + static char tname[2048]; + strcpy(tname, iname); + for (int n = strlen(tname) - 1; n>0; n--) if (tname[n] == '.') { tname[n] = '\0'; break; } + sprintf(oname, "%s%s%s", tname, subext, newextension); + return oname; +} + + +int main(int argc, char *argv[]) +{ + TMesh::init(); // This is mandatory + TMesh::app_name = "MeshFix"; + TMesh::app_version = "2.0"; + TMesh::app_year = "2016"; + TMesh::app_authors = "Marco Attene"; + TMesh::app_maillist = "attene@ge.imati.cnr.it"; + + clock_t beginning = clock(); + + // Uncomment the following to prevent message reporting + // TMesh::quiet = true; + + Basic_TMesh tin; + bool stl_output = false; + bool skip_if_fixed = false; + bool join_multiple_components = false; + char infilename[2048], outfilename[2048], extension[] = ".off"; + + if (argc < 2) usage(); + + float par; + int i = 2; + if (argc > 2 && argv[2][0] == '-') i--; + + for (; i2 && argv[2][0] != '-') sprintf(outfilename, "%s", argv[2]); + else createFilename(infilename, "_fixed", outfilename, extension); + + if (skip_if_fixed && fopen(outfilename, "r")) TMesh::error("Output file already exists (-x option specified)."); + + // The loader automatically reconstructs a manifold triangle connectivity + if (tin.load(infilename) != 0) TMesh::error("Can't open file.\n"); + + if (join_multiple_components) + { + TMesh::info("\nJoining input components ...\n"); + TMesh::begin_progress(); + while (joinClosestComponents(&tin)) TMesh::report_progress("Num. components: %d ", tin.shells()); + TMesh::end_progress(); + tin.deselectTriangles(); + } + + // Keep only the largest component (i.e. with most triangles) + int sc = tin.removeSmallestComponents(); + if (sc) TMesh::warning("Removed %d small components\n",sc); + + // Fill holes + if (tin.boundaries()) + { + TMesh::warning("Patching holes\n"); + tin.fillSmallBoundaries(0, true); + } + + // Run geometry correction + if (!tin.boundaries()) TMesh::warning("Fixing degeneracies and intersections...\n"); + if (tin.boundaries() || !tin.meshclean()) TMesh::warning("MeshFix could not fix everything.\n", sc); + + + TMesh::info("Saving output mesh ...\n"); + tin.save(outfilename); + + printf("Elapsed time: %d ms\n", clock() - beginning); + + return 0; +} diff --git a/src/mesh_fix/src/TMesh/edge.cpp b/src/mesh_fix/src/TMesh/edge.cpp new file mode 100644 index 000000000..cee1ff3eb --- /dev/null +++ b/src/mesh_fix/src/TMesh/edge.cpp @@ -0,0 +1,427 @@ +/**************************************************************************** +* TMesh * +* * +* Consiglio Nazionale delle Ricerche * +* Istituto di Matematica Applicata e Tecnologie Informatiche * +* Sezione di Genova * +* IMATI-GE / CNR * +* * +* Authors: Marco Attene * +* Copyright(C) 2013: IMATI-GE / CNR * +* All rights reserved. * +* * +* This program is dual-licensed as follows: * +* * +* (1) You may use TMesh as 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 3 of the License, or * +* (at your option) any later version. * +* In this case the 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 (http://www.gnu.org/licenses/gpl.txt) * +* for more details. * +* * +* (2) You may use TMesh as part of a commercial software. In this case a * +* proper agreement must be reached with the Authors and with IMATI-GE/CNR * +* based on a proper licensing contract. * +* * +****************************************************************************/ + +#include "edge.h" +#include "triangle.h" + +namespace T_MESH +{ + +//////// Length-based edge comparison for qsort ////////// + +int edgeCompare(const Data *a, const Data *b) +{ + coord la = ((Edge *)a)->squaredLength(); + coord lb = ((Edge *)b)->squaredLength(); + + if (lalb) return 1; + + return 0; +} + + +//////// Lexycographic edge comparison for qsort ////////// + +int lexEdgeCompare(const Data *a, const Data *b) +{ + Vertex *va1 = ((Edge *)a)->v1; + Vertex *va2 = ((Edge *)a)->v2; + Vertex *vb1 = ((Edge *)b)->v1; + Vertex *vb2 = ((Edge *)b)->v2; + + if (xyzCompare(va1, va2) > 0) p_swap((void **)&va1, (void **)&va2); + if (xyzCompare(vb1, vb2) > 0) p_swap((void **)&vb1, (void **)&vb2); + + int ca = xyzCompare(va1, vb1); + + if (ca == 0) return xyzCompare(va2, vb2); + + return ca; +} + + +//////// Vertex-based edge comparison for qsort ////////// + +int vtxEdgeCompare(const Data *a, const Data *b) +{ + Vertex *va1 = ((Edge *)a)->v1; + Vertex *va2 = ((Edge *)a)->v2; + Vertex *vb1 = ((Edge *)b)->v1; + Vertex *vb2 = ((Edge *)b)->v2; + Vertex *tmp; + + if (va2vb1) return 1; + if (va2vb2) return 1; + return 0; +} + + +//////////////////////// Constructor /////////////////////// +//!< AMF_ADD 1.1-2 > +Edge::Edge(){ + mask = 0; + info = NULL; +} +Edge::Edge(Vertex *va, Vertex *vb) +{ + v1 = va; + v2 = vb; + t1 = t2 = NULL; + info = NULL; + mask = 0; +} + + +////////////////// Destructor ///////////////////////////// + +Edge::~Edge() +{ +} + + +////// Returns the unit vector for the edge direction ////// + +Point Edge::toUnitVector() const +{ + Point v = toVector(); + coord l = v.length(); + + if (l == 0) TMesh::error("Edge::toUnitVector : Degenerate Edge !\n"); + + return v/l; +} + + +/// Returns the edge normal. //// +/// It is the average of the incident triangle normals //// + +Point Edge::getNormal() const +{ + Point nor, n1, n2; + + if (t1 == NULL || t2 == NULL) return Point(0,0,0); + + n1 = t1->getNormal(); + n2 = t2->getNormal(); + nor = n1+n2; + if (nor.length() != 0.0) nor.normalize(); + return nor; +} + +////////////////////////// Edge swap //////////////////////// + +bool Edge::swap(const bool fast) +{ + if (!fast && (t1 == NULL || t2 == NULL || + t2->oppositeVertex(this)->getEdge(t1->oppositeVertex(this)) != NULL)) return 0; + + Edge *e1 = t1->nextEdge(this); + Edge *e3 = t2->nextEdge(this); + v1->e0 = e3; + v2->e0 = e1; + v1 = t2->oppositeVertex(this); + v2 = t1->oppositeVertex(this); + t1->replaceEdge(e1, e3); + t2->replaceEdge(e3, e1); + t1->invert(); + t2->invert(); + e1->replaceTriangle(t1, t2); + e3->replaceTriangle(t2, t1); + + return 1; +} + + +////////////////////////// Edge collapse //////////////////////// + +Vertex *Edge::collapseOnV1() +{ + Edge *e; + Node *n; + List *ve; + Vertex *tv; + + Edge *e1 = (t1 != NULL)?(t1->nextEdge(this)):(NULL); + Edge *e2 = (t1 != NULL)?(t1->prevEdge(this)):(NULL); + Edge *e3 = (t2 != NULL)?(t2->nextEdge(this)):(NULL); + Edge *e4 = (t2 != NULL)?(t2->prevEdge(this)):(NULL); + Vertex *v3 = (e1 != NULL)?(e1->oppositeVertex(v2)):(NULL); + Vertex *v4 = (e4 != NULL)?(e4->oppositeVertex(v2)):(NULL); + Triangle *ta1 = (e1 != NULL)?(e1->oppositeTriangle(t1)):(NULL); + Triangle *ta2 = (e2 != NULL)?(e2->oppositeTriangle(t1)):(NULL); + Triangle *ta3 = (e3 != NULL)?(e3->oppositeTriangle(t2)):(NULL); + Triangle *ta4 = (e4 != NULL)?(e4->oppositeTriangle(t2)):(NULL); + + if (v1->isOnBoundary() && v2->isOnBoundary()) + if (!(((ta1 || ta2) && !ta3 && !ta4) || ((ta3 || ta4) && !ta1 && !ta2))) return NULL; + + if (ta1 != NULL && ta2 != NULL && ta1->oppositeVertex(e1) == ta2->oppositeVertex(e2)) + return NULL; + if (ta3 != NULL && ta4 != NULL && ta3->oppositeVertex(e3) == ta4->oppositeVertex(e4)) + return NULL; + + if (ta1 == NULL && ta2 == NULL) v1->e0 = e3; + else v1->e0 = e2; + + if (v3 != NULL) v3->e0 = e2; + if (v4 != NULL) v4->e0 = e3; + + ve = v2->VE(); + FOREACHVEEDGE(ve, e, n) + { + tv = e->oppositeVertex(v2); + if (tv != v3 && tv != v4 && tv->getEdge(v1) != NULL) {delete(ve); return NULL;} + } + FOREACHVEEDGE(ve, e, n) if (e != this) e->replaceVertex(v2, v1); + delete(ve); + + if (e2 != NULL) e2->replaceTriangle(t1, ta1); + if (e3 != NULL) e3->replaceTriangle(t2, ta4); + + if (ta1 != NULL) ta1->replaceEdge(e1, e2); + if (ta4 != NULL) ta4->replaceEdge(e4, e3); + + v2->e0 = NULL; // v2 must be removed + if (e4 != NULL) e4->v1 = e4->v2 = NULL; // e4 must be removed + if (e1 != NULL) e1->v1 = e1->v2 = NULL; // e1 must be removed + if (t1 != NULL) t1->e1 = t1->e2 = t1->e3 = NULL; // t1 must be removed + if (t2 != NULL) t2->e1 = t2->e2 = t2->e3 = NULL; // t2 must be removed + + if (e2 != NULL && e2->t1 == NULL && e2->t2 == NULL) + { + v3->e0 = NULL; + e2->v1 = e2->v2 = NULL; + } + if (e3 != NULL && e3->t1 == NULL && e3->t2 == NULL) + { + v4->e0 = NULL; + e3->v1 = e3->v2 = NULL; + } + + v4 = v1; // This is the remaining vertex to be returned + + v2 = v1 = NULL; // this edge must be removed + + return v4; +} + +Vertex *Edge::collapseOnV2() +{ + invert(); + return collapseOnV1(); +} + +bool Edge::collapse(const Point& p) +{ + Vertex *r = collapseOnV1(); + if (r==NULL) return false; + else r->setValue(&p); // Average the collapse + + return true; +} + +bool Edge::collapse() +{ + return collapse(((*v1)+(*v2))/2); +} + + +///// Merge with another boundary edge ///// + +bool Edge::merge(Edge *e) +{ + if (t1 && t2) return 0; + if (e->t1 && e->t2) return 0; + Triangle *ot = (e->t1==NULL)?(e->t2):(e->t1); + if (ot == getBoundaryTriangle()) return 0; + if ((t1 && e->t1) || (t2 && e->t2)) e->invert(); + Vertex *ov1 = e->v1, *ov2 = e->v2; + List *ve1=NULL, *ve2=NULL; + Node *n; + Edge *f, *f2; + + if (ov1 != v1) + { + ve1 = ov1->VE(); + FOREACHVEEDGE(ve1, f, n) + { + f2 = f->oppositeVertex(ov1)->getEdge(v1); + if (f2 != NULL && (!f2->isOnBoundary() || !f->isOnBoundary())) + {delete(ve1); return 0;} + } + } + if (ov2 != v2) + { + ve2 = ov2->VE(); + FOREACHVEEDGE(ve2, f, n) + { + f2 = f->oppositeVertex(ov2)->getEdge(v2); + if (f2 != NULL && (!f2->isOnBoundary() || !f->isOnBoundary())) + {delete(ve1); delete(ve2); return 0;} + } + } + + if (ov1 != v1) + { + FOREACHVEEDGE(ve1, f, n) f->replaceVertex(ov1, v1); + delete(ve1); + ov1->e0 = NULL; + } + if (ov2 != v2) + { + FOREACHVEEDGE(ve2, f, n) f->replaceVertex(ov2, v2); + delete(ve2); + ov2->e0 = NULL; + } + ot->replaceEdge(e, this); + ((t1==NULL)?(t1):(t2)) = ot; + v1->e0 = v2->e0 = this; + e->v1 = e->v2 = NULL; + + return 1; +} + + +///// Angle between the normal of incident triangles ///// + +double Edge::curvature() const +{ + if (!t1 || !t2) return -1.0; + return t1->getDAngle(t2); +} + + +//// Dihedral angle + +double Edge::dihedralAngle() const +{ + if (!t1 || !t2) return -1.0; + Point nor1 = t1->getNormal(); + Point nor2 = t2->getNormal(); + if (nor1.isNull() || nor2.isNull()) return -1.0; + double c = nor1.getAngle(&nor2); + + Vertex *ov = t2->oppositeVertex(this); + if (((*ov)*nor1) - ((*v1)*nor1) < 0) return -(c - M_PI); + + return c+M_PI; +} + +//// Min Angle among those of the two incident triangles //// + +double Edge::delaunayMinAngle() const +{ + if (t1==NULL || t2==NULL) return 2*M_PI; + if (squaredLength()==0) return 0; + if (t1->nextEdge(this)->squaredLength() == 0) return 0; + if (t1->prevEdge(this)->squaredLength() == 0) return 0; + double a1 = t1->getAngle(v1); + double a2 = t1->getAngle(v2); + double a3 = t1->getAngle(t1->oppositeVertex(this)); + if (t2->nextEdge(this)->length()==0) return 0; + if (t2->prevEdge(this)->length()==0) return 0; + double a4 = t2->getAngle(v1); + double a5 = t2->getAngle(v2); + double a6 = t2->getAngle(t2->oppositeVertex(this)); + + if (a1+a4 >= M_PI || a2+a5 >= M_PI) return 3*M_PI; + return MIN(a1,(MIN(a2,(MIN(a3,(MIN(a4,(MIN(a5,a6))))))))); +} + + +// If edge is stitchable, merge it with its copy + +bool Edge::stitch() +{ + // This function seems to be insufficient to stitch in every case ! + if (!isOnBoundary()) return 0; + + Triangle *t, *t0 = (t1 != NULL) ? (t1) : (t2); + Vertex *v0; + Edge *e1; + + for (v0 = v1; v0 != NULL; v0 = ((v0 == v1) ? (v2) : (NULL))) + { + e1 = this; + t = t0; + while (t != NULL) + { + e1 = t->nextEdge(e1); if (!e1->hasVertex(v0)) e1 = t->nextEdge(e1); + t = e1->oppositeTriangle(t); + } + if (e1->oppositeVertex(v0) == oppositeVertex(v0)) + { + t = (e1->t1 != NULL) ? (e1->t1) : (e1->t2); + t->replaceEdge(e1, this); + v1->e0 = v2->e0 = this; + e1->v1 = e1->v2 = NULL; + replaceTriangle(NULL, t); + return 1; + } + } + + return 0; +} + +bool Edge::overlaps() const +{ + if (t1 == NULL || t2 == NULL) return false; + + Vertex *ov = t2->oppositeVertex(this); + if (ov->exactOrientation(t1->v1(), t1->v2(), t1->v3()) == 0 && ov->exactSameSideOnPlane(t1->oppositeVertex(this), v1, v2)) return true; + else return false; +} + +bool Edge::intersects(const Triangle *t) const +{ + if (t->hasEdge(this)) return false; + + Vertex *cv = (t->hasVertex(v1)) ? (v1) : ((t->hasVertex(v2)) ? (v2) : (NULL)); + if (cv) // If they share a vertex, intersection occurs if t's opposite edge intersect this edge + { + Edge *oe = t->oppositeEdge(cv); + if (Point::pointInTriangle(oppositeVertex(cv), cv, oe->v1, oe->v2)) return true; + else return (Point::segmentsIntersect(oe->v1, oe->v2, v1, v2)); + } + else return Point::segmentIntersectsTriangle(v1, v2, t->v1(), t->v2(), t->v3()); +} + +coord Edge::getConvexity() const +{ + if (t1 == NULL || t2 == NULL) return DBL_MAX; + else return (t1->oppositeVertex(this)->exactOrientation(t2->v3(), t2->v2(), t2->v1())); +} + +} //namespace T_MESH diff --git a/src/mesh_fix/src/TMesh/io.cpp b/src/mesh_fix/src/TMesh/io.cpp new file mode 100644 index 000000000..7efae0131 --- /dev/null +++ b/src/mesh_fix/src/TMesh/io.cpp @@ -0,0 +1,1623 @@ +/**************************************************************************** +* TMesh * +* * +* Consiglio Nazionale delle Ricerche * +* Istituto di Matematica Applicata e Tecnologie Informatiche * +* Sezione di Genova * +* IMATI-GE / CNR * +* * +* Authors: Marco Attene * +* Copyright(C) 2013: IMATI-GE / CNR * +* All rights reserved. * +* * +* This program is dual-licensed as follows: * +* * +* (1) You may use TMesh as 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 3 of the License, or * +* (at your option) any later version. * +* In this case the 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 (http://www.gnu.org/licenses/gpl.txt) * +* for more details. * +* * +* (2) You may use TMesh as part of a commercial software. In this case a * +* proper agreement must be reached with the Authors and with IMATI-GE/CNR * +* based on a proper licensing contract. * +* * +****************************************************************************/ + +#include "tmesh.h" +#include +#include +#include +#include +#include + +namespace T_MESH +{ + +#define VRML1_HEADER "#VRML V1.0 ascii" +#define VRML1_HSIZE 16 +#define VRML2_HEADER "#VRML V2.0 utf8" +#define VRML2_HSIZE 15 +#define OFF_HEADER "OFF" +#define OFF_HSIZE 3 +#define EFF_HEADER "EFF" +#define EFF_HSIZE 3 +#define PLY_HEADER "ply" +#define PLY_HSIZE 3 +#define IV_HEADER "#Inventor V2.1 ascii" +#define IV_HSIZE 20 + +#define PLY_FORMAT_ASCII 0 +#define PLY_FORMAT_BIN_L 1 +#define PLY_FORMAT_BIN_B 2 + +#define TVI1(a) (TMESH_TO_INT(((Triangle *)a->data)->v1()->x)) +#define TVI2(a) (TMESH_TO_INT(((Triangle *)a->data)->v2()->x)) +#define TVI3(a) (TMESH_TO_INT(((Triangle *)a->data)->v3()->x)) + +inline void PRINT_HEADING_COMMENT(FILE *f) +{ + if (TMesh::app_name != NULL) + { + fprintf(f, "# File created by %s",TMesh::app_name); + if (TMesh::app_version != NULL) + { + fprintf(f, " v%s",TMesh::app_version); + if (TMesh::app_year != NULL) fprintf(f, " (%s)",TMesh::app_year); + } + fprintf(f, "\n"); + if (TMesh::app_url != NULL) fprintf(f, "# %s\n",TMesh::app_url); + } + fprintf(f, "\n"); +} + +inline void PRINT_PLY_COMMENT(FILE *f) +{ + if (TMesh::app_name != NULL) + { + fprintf(f, "comment File created by %s",TMesh::app_name); + if (TMesh::app_version != NULL) + { + fprintf(f, " v%s",TMesh::app_version); + if (TMesh::app_year != NULL) fprintf(f, " (%s)",TMesh::app_year); + } + fprintf(f, "\n"); + if (TMesh::app_url != NULL) fprintf(f, "comment %s\n",TMesh::app_url); + } +} + +/// Returns TRUE if the two strings are equal in a case-insensitive sense ///// + +inline bool sameString(const char *a, const char *b) +{ + int i = 0; + while (a[i] != '\0' && b[i] != '\0') + { + if (tolower(a[i]) != tolower(b[i])) return false; + i++; + } + + return (a[i] == '\0' && b[i] == '\0'); +} + + + +// Swap endian-ness for four-byte elements + +inline void endian_swap_long(unsigned char *p) +{ + unsigned char b0,b1,b2,b3; + + b0 = *p; b1 = *(p+1); b2 = *(p+2); b3 = *(p+3); + *p = b3; *(p+1) = b2; *(p+2) = b1; *(p+3) = b0; +} + + +// Read one line (max 1024 chars) and exit if EOF + +char *readLineFromFile(FILE *in, bool exit_on_eof = 1) +{ +#define MAX_READLINE_CHARS 1024 + static char line[MAX_READLINE_CHARS]; + int i=0; + char c; + + while ((c = fgetc(in)) != '\n' && i<(MAX_READLINE_CHARS-1)) + if (c==EOF) + { + if (exit_on_eof) TMesh::error("\nUnexpected end of file!\n"); + else return NULL; + } + else if (c != '\r') line[i++] = c; + line[i] = '\0'; + + if (i==MAX_READLINE_CHARS-1) + TMesh::warning("readLineFromFile: Line is too long. Truncated !\n"); + + return line; +} + + +// Looks for a keyword 'kw' in an ASCII file referenced through 'fp'. +// The file pointer is set to the byte right after the first keyword matched. +// Return 1 on success (keyword match), 0 otherwise. + + +bool seek_keyword(FILE *fp, const char *kw) +{ + static char s[256]; + s[0]='\0'; + do fscanf(fp,"%255s",s); while (strcmp(s,kw) && !feof(fp)); + if (feof(fp)) return 0; + return 1; +} + + +inline void skipCommentAndBlankLines(FILE *fp) +{ + long pos0; + char *line, s[2]; + do {pos0 = ftell(fp); line = readLineFromFile(fp);} while (line[0] == '#' || line[0] == '\0' || !sscanf(line,"%1s",s)); + fseek(fp, pos0, SEEK_SET); +} + + +////////////////////// Dispatch the load /////////////////////////// + +int Basic_TMesh::load(const char *fname, const bool doupdate) +{ + FILE *fp; + char header[256]; + size_t as; + int err = IO_UNKNOWN; + + if ((fp = fopen(fname,"r")) == NULL) return IO_CANTOPEN; + as = fread(header, 1, 256, fp); + fclose(fp); + + if (as >= VRML1_HSIZE && !strncmp(header, VRML1_HEADER, VRML1_HSIZE)) err = loadVRML1(fname); + else if (as >= VRML2_HSIZE && !strncmp(header, VRML2_HEADER, VRML2_HSIZE)) err = loadVRML2(fname); + else if (as >= OFF_HSIZE && !strncmp(header, OFF_HEADER, OFF_HSIZE)) err = loadOFF(fname); + else if (as >= EFF_HSIZE && !strncmp(header, EFF_HEADER, EFF_HSIZE)) err = loadEFF(fname); + else if (as >= PLY_HSIZE && !strncmp(header, PLY_HEADER, PLY_HSIZE)) err = loadPLY(fname); + else if (as >= IV_HSIZE && !strncmp(header, IV_HEADER, IV_HSIZE)) err = loadIV(fname); + else if (sameString((char *)(fname+strlen(fname)-4), (char *)".obj")) err = loadOBJ(fname); + else if (sameString((char *)(fname+strlen(fname)-4), (char *)".tri")) err = loadVerTri(fname); + else if (sameString((char *)(fname+strlen(fname)-4), (char *)".stl")) err = loadSTL(fname); + + if (!err && doupdate) eulerUpdate(); + if (!err) TMesh::setFilename(fname); + + return err; +} + +int Basic_TMesh::append(const char *filename, const bool doupdate) +{ + if (!T.numels()) return load(filename, doupdate); + Basic_TMesh ntin; + int err = ntin.load(filename, 0); + + if (err) return err; + + V.joinTailList(&(ntin.V)); + E.joinTailList(&(ntin.E)); + T.joinTailList(&(ntin.T)); + if (doupdate) eulerUpdate(); + else d_boundaries = d_handles = d_shells = 1; + + return 0; +} + +int Basic_TMesh::save(const char *fname, bool back_approx) +{ + char nfname[4096]; + strcpy(nfname, fname); + + int rv; + size_t i=strlen(fname)-1; + while (i>0 && fname[i] != '.') i--; + + if (i==0) {strcat(nfname,".wrl"); i=strlen(fname);} + + if (sameString(nfname+i, ".wrl")) rv = saveVRML1(nfname); + else if (sameString(nfname+i, ".iv")) rv = saveIV(nfname); + else if (sameString(nfname + i, ".off")) rv = saveOFF(nfname); + else if (sameString(nfname + i, ".eff")) rv = saveEFF(nfname); + else if (sameString(nfname + i, ".ply")) rv = savePLY(nfname); + else if (sameString(nfname+i, ".obj")) rv = saveOBJ(nfname); + else if (sameString(nfname+i, ".stl")) rv = saveSTL(nfname); + else if (sameString(nfname+i, ".tri")) {nfname[i]='\0'; rv = saveVerTri(nfname);} + else + { + TMesh::warning("Unknown extension '%s'.\n",nfname+i); + TMesh::warning("I did not save anything.\n"); + TMesh::warning("Recognized extensions are:"); + TMesh::warning(".wrl (ASCII VRML 1.0)\n"); + TMesh::warning(".iv (Open Inventor 2.1)\n"); + TMesh::warning(".off (Object File Format)\n"); + TMesh::warning(".eff (Exact File Format)\n"); + TMesh::warning(".obj (Wavefront/Java3D)\n"); + TMesh::warning(".stl (Stereolithography)\n"); + TMesh::warning(".ply (Ascii PLY 1.0 Format)\n"); + TMesh::warning(".tri (IMATI ver-tri File Format)\n"); + return 0; + } + + if (!rv && back_approx) coordBackApproximation(); + + return rv; +} + + +// This part is common to all the loaders + +Triangle * Basic_TMesh::CreateTriangleFromVertices(ExtVertex *vari1, ExtVertex *vari2, ExtVertex *vari3) +{ + Edge *e1, *e2, *e3; + Triangle *t = NULL; + + e1 = CreateEdge(vari1,vari2); if (e1->t1 != NULL && e1->t2 != NULL) MARK_BIT(e1,5); + e2 = CreateEdge(vari2,vari3); if (e2->t1 != NULL && e2->t2 != NULL) MARK_BIT(e2,5); + e3 = CreateEdge(vari3,vari1); if (e3->t1 != NULL && e3->t2 != NULL) MARK_BIT(e3,5); + if (IS_BIT(e1,5)) {e1 = CreateEdge(vari1,vari2,0); MARK_BIT(e1,5);} + if (IS_BIT(e2,5)) {e2 = CreateEdge(vari2,vari3,0); MARK_BIT(e2,5);} + if (IS_BIT(e3,5)) {e3 = CreateEdge(vari3,vari1,0); MARK_BIT(e3,5);} + + if ((t=CreateUnorientedTriangle(e1,e2,e3)) == NULL) + { + if (e3->t1 == NULL && e3->t2 == NULL) + { + E.freeNode(e3); + vari3->VE.removeNode(e3); vari1->VE.removeNode(e3); + if (vari3->v->e0 == e3) vari3->v->e0 = NULL; + if (vari1->v->e0 == e3) vari1->v->e0 = NULL; + } + if (e2->t1 == NULL && e2->t2 == NULL) + { + E.freeNode(e2); + vari2->VE.removeNode(e2); vari3->VE.removeNode(e2); + if (vari2->v->e0 == e2) vari2->v->e0 = NULL; + if (vari3->v->e0 == e2) vari3->v->e0 = NULL; + } + if (e1->t1 == NULL && e1->t2 == NULL) + { + E.freeNode(e1); + vari1->VE.removeNode(e1); vari2->VE.removeNode(e1); + if (vari1->v->e0 == e1) vari1->v->e0 = NULL; + if (vari2->v->e0 == e1) vari2->v->e0 = NULL; + } + } + + return t; +} + + +// This part is common to all the loaders + +Triangle * Basic_TMesh::CreateIndexedTriangle(ExtVertex **var, int i1, int i2, int i3) +{ + return CreateTriangleFromVertices(var[i1], var[i2], var[i3]); +} + + +// This part is common to all the loaders + +void Basic_TMesh::closeLoadingSession(FILE *fp, int loaded_faces, ExtVertex **var, bool triangulate) +{ + int i, nv = V.numels(); + + fclose(fp); + + if (var != NULL) + { + for (i=0; ix)); sscanf(floatver, "%f", &x); v->x = x; + sprintf(floatver, "%f", TMESH_TO_FLOAT(v->y)); sscanf(floatver, "%f", &x); v->y = x; + sprintf(floatver, "%f", TMESH_TO_FLOAT(v->z)); sscanf(floatver, "%f", &x); v->z = x; + } +} + +////////////////////// Loads VRML 1.0 format /////////////////////////// + +int Basic_TMesh::loadVRML1(const char *fname) +{ + FILE *fp; + Node *n; + float x,y,z; + int i,i1,i2,i3,i4,nv=0,triangulate=0; + Vertex *v; + + coord cx; + + if ((fp = fopen(fname,"r")) == NULL) return IO_CANTOPEN; + + if (!seek_keyword(fp, "point")) {closeLoadingSession(fp, 0, NULL, 0); return IO_FORMAT;} + if (!seek_keyword(fp, "[")) {closeLoadingSession(fp, 0, NULL, 0); return IO_FORMAT;} + + while (fscanf(fp, "%f %f %f,", &x, &y, &z) == 3) { V.appendTail(newVertex(x, y, z)); cx = coord(x); /*printf("%f %f\n", x, cx.toFloat());*/ } + nv = V.numels(); + ExtVertex **var = (ExtVertex **)malloc(sizeof(ExtVertex *)*nv); + i = 0; FOREACHVERTEX(v, n) var[i++] = new ExtVertex(v); + + if (!seek_keyword(fp, "coordIndex")) {closeLoadingSession(fp, 0, var, 0); return IO_FORMAT;} + if (!seek_keyword(fp, "[")) {closeLoadingSession(fp, 0, var, 0); return IO_FORMAT;} + + i=0; TMesh::begin_progress(); + while (fscanf(fp,"%d, %d, %d,",&i1,&i2,&i3) == 3) + { + if (((i++)%1000) == 0) TMesh::report_progress("Loading ..%d%%",(i*100)/(nv*2)); + if (i1<0 || i2<0 || i3<0 || i1>(nv-1) || i2>(nv-1) || i3>(nv-1)) + TMesh::error("\nloadVRML1: Invalid indices %d %d %d!\n",i1,i2,i3); + do + { + if (i1 == i2 || i2 == i3 || i3 == i1) TMesh::warning("\nloadVRML1: Coincident indexes at face %d! Skipping.\n",i); + else if (!CreateIndexedTriangle(var, i1, i2, i3)) TMesh::warning("\nloadVRML1: This shouldn't happen!!! Skipping triangle.\n"); + if (fscanf(fp,"%d,",&i4) != 1) TMesh::error("loadVRML1: Unexpected end of file at face %d!\n",i); + i2=i3; i3=i4; + if (i4 != -1) triangulate=1; + } while (i4 != -1); + } + TMesh::end_progress(); + + closeLoadingSession(fp, i, var, (triangulate != 0)); + TMesh::setFilename(fname); + + return 0; +} + + +int Basic_TMesh::loadIV(const char *fname) +{ + return loadVRML1(fname); +} + + +////////////////////// Loads OFF format /////////////////////////// + +int Basic_TMesh::loadOFF(const char *fname) +{ + FILE *fp; + Node *n; + char s[256], *line; + float x,y,z; + int i,j,i1,i2,i3,i4,nv,nt,ne,triangulate=0; + Vertex *v; + + if ((fp = fopen(fname,"rb")) == NULL) return IO_CANTOPEN; + + fscanf(fp,"%255s",s); + if (strcmp(s,"OFF") || feof(fp)) return IO_FORMAT; + do {line = readLineFromFile(fp);} while (line[0] == '#' || line[0] == '\0' || !sscanf(line,"%256s",s)); + if (sscanf(line,"%d %d %d",&nv,&nt,&ne) < 3) return IO_FORMAT; + if (nv < 3) TMesh::error("\nloadOFF: Sorry. Can't load objects with less than 3 vertices.\n"); + if (nt < 1) TMesh::error("\nloadOFF: Sorry. Can't load objects with no faces.\n"); + + skipCommentAndBlankLines(fp); + + for (i = 0; i(nv-1) || i2>(nv-1) || i3>(nv-1)) TMesh::error("\nloadOFF: Invalid index at face %d!\n",i); + for (j=3; j<=i4; j++) + { + if (i1 == i2 || i2 == i3 || i3 == i1) TMesh::warning("\nloadOFF: Coincident indexes at triangle %d! Skipping.\n",i); + else if (!CreateIndexedTriangle(var, i1, i2, i3)) TMesh::warning("\nloadOFF: This shouldn't happen!!! Skipping triangle.\n"); + i2 = i3; + if (j> s; + if (is.eof() || is.fail() || strcmp(s, "EFF")) return IO_FORMAT; + is >> nv; if (is.eof() || is.fail()) return IO_FORMAT; + is >> nt; if (is.eof() || is.fail()) return IO_FORMAT; + if (nv < 3) TMesh::error("\nloadOFF: Sorry. Can't load objects with less than 3 vertices.\n"); + if (nt < 1) TMesh::error("\nloadOFF: Sorry. Can't load objects with no faces.\n"); + + TMesh::useRationals(true); + for (i = 0; i < nv; i++) + { + is >> x >> y >> z; + if (is.eof() || is.fail()) TMesh::error("\nloadEFF: Couldn't read coordinates for vertex # %d\n", i); + V.appendTail(newVertex(x, y, z)); + } + + ExtVertex **var = (ExtVertex **)malloc(sizeof(ExtVertex *)*nv); + i = 0; FOREACHVERTEX(v, n) var[i++] = new ExtVertex(v); + + TMesh::begin_progress(); + for (i = 0; i> i1 >> i2 >> i3; + if (is.eof() || is.fail()) TMesh::error("\nloadEFF: Couldn't read indexes for face # %d\n", i); + else + { + if ((i % 1000) == 0) TMesh::report_progress("Loading ..%d%%", (i * 100) / (nv * 2)); + if (i1<0 || i2<0 || i3<0 || i1>(nv - 1) || i2>(nv - 1) || i3>(nv - 1)) TMesh::error("\nloadEFF: Invalid index at face %d!\n", i); + if (i1 == i2 || i2 == i3 || i3 == i1) TMesh::warning("\nloadEFF: Coincident indexes at triangle %d! Skipping.\n", i); + else if (!CreateIndexedTriangle(var, i1, i2, i3)) TMesh::warning("\nloadEFF: This shouldn't happen!!! Skipping triangle.\n"); + } + } + + TMesh::end_progress(); + + is.close(); + + for (i = 0; i(nv-1) || i2>(nv-1) || i3>(nv-1)) TMesh::error("\nloadVRML2: Invalid index at face %d!\n",i); + do + { + if (i1 == i2 || i2 == i3 || i3 == i1) TMesh::warning("\nloadVRML2: Coincident indexes at triangle %d! Skipping.\n",i); + else if (!CreateIndexedTriangle(var, i1, i2, i3)) TMesh::warning("\nloadVRML2: This shouldn't happen!!! Skipping triangle.\n"); + if (fscanf(fp,"%d,",&i4) != 1) TMesh::error("loadVRML2: Unexpected end of file at triangle %d!\n",i); + i2=i3; i3=i4; + if (i4 != -1) triangulate=1; + } while (i4 != -1); + } + TMesh::end_progress(); + + closeLoadingSession(fp, i, var, (triangulate != 0)); + TMesh::setFilename(fname); + + return 0; +} + + +////////////////////// Loads Ver-Tri format //////////////////// + +int Basic_TMesh::loadVerTri(const char *fname) +{ + FILE *fpv, *fpt; + int numvers, numtris, i, i1, i2, i3, a1, a2, a3; + float x,y,z; + char vername[256], triname[256]; + Node *n; + Vertex *v; + + if (!sameString((char *)(fname+strlen(fname)-4), (char *)".tri")) return IO_UNKNOWN; + + strcpy(triname,fname); + strcpy(vername,fname); vername[strlen(vername)-4]='\0'; + strcat(vername,".ver"); + + if ((fpv = fopen(vername,"r")) == NULL) + { + fprintf(stderr,"Can't open '%s' for input !\n",vername); + return 1; + } + if ((fpt = fopen(triname,"r")) == NULL) + { + fclose(fpv); + fprintf(stderr,"Can't open '%s' for input !\n",triname); + return 1; + } + + if (!fscanf(fpv,"%d\n",&numvers) || numvers < 3) {fclose(fpv); fclose(fpt); return IO_FORMAT;} + if (!fscanf(fpt,"%d\n",&numtris) || numtris < 1) {fclose(fpv); fclose(fpt); return IO_FORMAT;} + + for (i=0; i (numvers) || i2 > (numvers) || i3 > (numvers)) TMesh::error("\nloadVerTri: Index out of bounds at triangle %d!\n",i); + else if (i1 == i2 || i2 == i3 || i3 == i1) TMesh::warning("\nloadVerTri: Coincident indexes at triangle %d! Skipping.\n",i); + else if (!CreateIndexedTriangle(var, i1-1, i2-1, i3-1)) TMesh::warning("\nloadVerTri: This shouldn't happen!!! Skipping triangle.\n"); + } + else TMesh::error("loadVerTri: Couldn't read %dth triangle !\n",i+1); + + TMesh::end_progress(); + + closeLoadingSession(fpt, T.numels(), var, 0); + TMesh::setFilename(fname); + + return 0; +} + + +////////////////////// Saves IV 2.1 format //////////////////// + +int Basic_TMesh::saveIV(const char *fname) +{ + FILE *fp; + int i; + char triname[256]; + Node *n; + coord *ocds; + Vertex *v; + + strcpy(triname,fname); + + if ((fp = fopen(triname,"w")) == NULL) + { + TMesh::warning("Can't open '%s' for output !\n",triname); + return 1; + } + + fprintf(fp,"#Inventor V2.1 ascii\n\n"); + PRINT_HEADING_COMMENT(fp); + fprintf(fp,"Separator {\n"); + fprintf(fp," Coordinate3 {\n point [\n"); + + FOREACHVERTEX(v, n) fprintf(fp, " %f %f %f,\n", TMESH_TO_FLOAT(v->x), TMESH_TO_FLOAT(v->y), TMESH_TO_FLOAT(v->z)); + + fprintf(fp," ]\n }\n"); + fprintf(fp," IndexedFaceSet {\n coordIndex [\n"); + + ocds = new coord[V.numels()]; + i=0; FOREACHVERTEX(v, n) ocds[i++] = v->x; + i=0; FOREACHVERTEX(v, n) v->x = i++; + + FOREACHNODE(T, n) fprintf(fp," %d, %d, %d, -1,\n",TVI1(n),TVI2(n),TVI3(n)); + + fprintf(fp," ]\n }\n"); + fprintf(fp,"}\n"); + + fclose(fp); + i=0; FOREACHVERTEX(v, n) v->x = ocds[i++]; + delete[] ocds; + + return 0; +} + + +////////////////////// Saves VRML 1.0 format //////////////////// + +int Basic_TMesh::saveVRML1(const char *fname, const int mode) +{ + FILE *fp; + int i; + unsigned int pkc; + char triname[256]; + Node *n; + Vertex *v; + Triangle *t; + coord *ocds; + + strcpy(triname,fname); + + if ((fp = fopen(triname,"w")) == NULL) + { + TMesh::warning("Can't open '%s' for output !\n",triname); + return 1; + } + + fprintf(fp,"#VRML V1.0 ascii\n\n"); + PRINT_HEADING_COMMENT(fp); + fprintf(fp,"Separator {\n"); + fprintf(fp," Coordinate3 {\n point [\n"); + + FOREACHVERTEX(v, n) fprintf(fp, " %f %f %f,\n", TMESH_TO_FLOAT(v->x), TMESH_TO_FLOAT(v->y), TMESH_TO_FLOAT(v->z)); + + fprintf(fp," ]\n }\n"); + + ocds = new coord[V.numels()]; + i=0; FOREACHVERTEX(v, n) {ocds[i] = v->x; v->x = i++;} + + switch (mode) + { + case IO_CSAVE_OVERALL: + fprintf(fp,"Material {\n diffuseColor 0.6 0.6 0.6\n}\n"); + break; + case IO_CSAVE_PERFACE: + fprintf(fp,"Material {\n diffuseColor [\n"); + FOREACHTRIANGLE(t, n) + { + pkc = (unsigned int)((j_voidint)t->info); + fprintf(fp," %f %f %f,\n",((pkc>>24)&0x000000ff)/255.0,((pkc>>16)&0x000000ff)/255.0,((pkc>>8)&0x000000ff)/255.0); + } + fprintf(fp," ]\n}\nMaterialBinding {\n value PER_FACE_INDEXED\n}\n"); + break; + case IO_CSAVE_PERVERTEX: + fprintf(fp,"Material {\n diffuseColor [\n"); + FOREACHVERTEX(v, n) + { + pkc = (unsigned int)((j_voidint)v->info); + fprintf(fp," %f %f %f,\n",((pkc>>24)&0x000000ff)/255.0,((pkc>>16)&0x000000ff)/255.0,((pkc>>8)&0x000000ff)/255.0); + } + fprintf(fp," ]\n}\nMaterialBinding {\n value PER_VERTEX_INDEXED\n}\n"); + break; + case IO_CSAVE_PERFACE_INDEXED: + fprintf(fp,"Material {\n diffuseColor [\n"); + fprintf(fp,"1.0 1.0 1.0,\n1.0 0.0 0.0,\n0.0 1.0 0.0,\n0.0 0.0 1.0,\n 0.8 0.8 0.0\n"); + fprintf(fp," ]\n}\nMaterialBinding {\n value PER_FACE_INDEXED\n}\n"); + break; + case IO_CSAVE_PERVERTEX_INDEXED: + fprintf(fp,"Material {\n diffuseColor [\n"); + fprintf(fp,"1.0 1.0 1.0,\n1.0 0.0 0.0,\n0.0 1.0 0.0,\n0.0 0.0 1.0,\n 0.8 0.8 0.0\n"); + fprintf(fp," ]\n}\nMaterialBinding {\n value PER_VERTEX_INDEXED\n}\n"); + break; + default: TMesh::error("Basic_TMesh::saveVRML1. Unknown mode %d\n",mode); + } + + fprintf(fp," IndexedFaceSet {\n coordIndex [\n"); + + FOREACHTRIANGLE(t, n) + fprintf(fp, " %d, %d, %d, -1,\n", TMESH_TO_INT(t->v1()->x), TMESH_TO_INT(t->v2()->x), TMESH_TO_INT(t->v3()->x)); + + fprintf(fp," ]\n"); + + if (mode != IO_CSAVE_OVERALL) + { + fprintf(fp," materialIndex [\n"); + switch (mode) + { + case IO_CSAVE_PERFACE_INDEXED: + FOREACHTRIANGLE(t, n) fprintf(fp," %d,\n",t->mask); + break; + case IO_CSAVE_PERVERTEX_INDEXED: + FOREACHTRIANGLE(t, n) fprintf(fp," %d, %d, %d, -1,\n",t->v1()->mask,t->v2()->mask,t->v3()->mask); + break; + case IO_CSAVE_PERFACE: + i=0; FOREACHTRIANGLE(t, n) fprintf(fp," %d,\n",i++); + break; + case IO_CSAVE_PERVERTEX: + FOREACHTRIANGLE(t, n) fprintf(fp, " %d, %d, %d, -1,\n", TMESH_TO_INT(t->v1()->x), TMESH_TO_INT(t->v2()->x), TMESH_TO_INT(t->v3()->x)); + break; + } + fprintf(fp," ]\n"); + } + + fprintf(fp," }\n}\n"); + + fclose(fp); + i=0; FOREACHVERTEX(v, n) v->x = ocds[i++]; + delete[] ocds; + + return 0; +} + + +////////////////////// Saves OFF format /////////////////////////// + +int Basic_TMesh::saveOFF(const char *fname) +{ + FILE *fp; + int i; + char triname[256]; + Node *n; + coord *ocds; + Vertex *v; + + strcpy(triname,fname); + + if ((fp = fopen(triname,"w")) == NULL) + { + TMesh::warning("Can't open '%s' for output !\n",triname); + return 1; + } + + fprintf(fp,"OFF\n"); + PRINT_HEADING_COMMENT(fp); + fprintf(fp,"%d %d 0\n",V.numels(),T.numels()); + + FOREACHVERTEX(v, n) fprintf(fp, "%f %f %f\n", TMESH_TO_FLOAT(v->x), TMESH_TO_FLOAT(v->y), TMESH_TO_FLOAT(v->z)); + + ocds = new coord[V.numels()]; + i=0; FOREACHVERTEX(v, n) ocds[i++] = v->x; + i=0; FOREACHVERTEX(v, n) v->x = i++; + + FOREACHNODE(T, n) fprintf(fp,"3 %d %d %d\n",TVI1(n),TVI2(n),TVI3(n)); + + fclose(fp); + i=0; FOREACHVERTEX(v, n) v->x = ocds[i++]; + delete[] ocds; + + return 0; +} + + +////////////////////// Saves Ver-Tri format //////////////////// + +//#define SAVE_INFO +int Basic_TMesh::saveVerTri(const char *fname) +{ +#ifdef SAVE_INFO + TMesh::warning("saveVerTri: Assuming that the vertex info field is allocated!\n"); + FILE *fpj; + char jkkname[256]; +#endif + FILE *fpv, *fpt; + int i, i1, i2, i3, a1, a2, a3; + char vername[256], triname[256]; + Node *n; + Vertex *v; + Triangle *t, *t1, *t2, *t3; + coord *ocds; + + strcpy(triname,fname); + strcpy(vername,fname); + strcat(triname,".tri"); + strcat(vername,".ver"); + +#ifdef SAVE_INFO + strcpy(jkkname,fname); + strcat(jkkname,".jkk"); +#endif + + if ((fpv = fopen(vername,"w")) == NULL) + { + fprintf(stderr,"Can't open '%s' for output !\n",vername); + return 1; + } + if ((fpt = fopen(triname,"w")) == NULL) + { + fclose(fpv); + fprintf(stderr,"Can't open '%s' for output !\n",triname); + return 1; + } +#ifdef SAVE_INFO + if ((fpj = fopen(jkkname,"w")) == NULL) + { + fclose(fpv); fclose(fpt); + fprintf(stderr,"Can't open '%s' for output !\n",jkkname); + return 1; + } +#endif + + fprintf(fpv,"%d\n",V.numels()); + FOREACHVERTEX(v, n) + { + fprintf(fpv, "%f %f %f\n", TMESH_TO_FLOAT(v->x), TMESH_TO_FLOAT(v->y), TMESH_TO_FLOAT(v->z)); + } + fclose(fpv); + +#ifdef SAVE_INFO + for (n=V.tail; n != NULL; n=n->prev) + { + v = ((Vertex *)n->data); + fprintf(fpj,"%f\n",(*((double *)(v->info)))); + } + fclose(fpj); +#endif + + ocds = new coord[V.numels()]; + i=0; FOREACHVERTEX(v, n) ocds[i++] = v->x; + i=0; FOREACHVERTEX(v, n) v->x = ++i; + i=0; FOREACHTRIANGLE(t, n) {i++; t->info = (void *)(intptr_t)i;} + + fprintf(fpt,"%d\n",T.numels()); + FOREACHTRIANGLE(t, n) + { + i1 = TMESH_TO_INT(t->v1()->x); i2 = TMESH_TO_INT(t->v2()->x); i3 = TMESH_TO_INT(t->v3()->x); + t1 = t->t1(); t2 = t->t2(); t3 = t->t3(); + a1 = (t1)?((long int)(t1->info)):(0); a2 = (t2)?((long int)(t2->info)):(0); a3 = (t3)?((long int)(t3->info)):(0); + fprintf(fpt,"%d %d %d %d %d %d\n",i1, i2, i3, a1, a2, a3); + } + fclose(fpt); + + i=0; FOREACHVERTEX(v, n) v->x = ocds[i++]; + delete[] ocds; + + return 0; +} + + +// Implements the cutting and stitching procedure to convert to manifold mesh // +// Assumes that singular edges to be cut and stitched are marked as BIT5. // + +bool Basic_TMesh::pinch(Edge *e1, bool with_common_vertex) +{ + List *ee = (List *)e1->info; + if (ee == NULL) return false; + Node *n = NULL; + Edge *e2=NULL; + List *ve; + + if (with_common_vertex) + { + e1->v1->e0 = e1; ve = e1->v1->VE(); + FOREACHVEEDGE(ve, e2, n) if (e2 != e1 && e2->isOnBoundary() && (*(e2->oppositeVertex(e1->v1))) == (*(e1->v2)) && e1->merge(e2)) break; + delete ve; + if (n == NULL) + { + e1->v2->e0 = e1; ve = e1->v2->VE(); + FOREACHVEEDGE(ve, e2, n) if (e2 != e1 && e2->isOnBoundary() && (*(e2->oppositeVertex(e1->v2))) == (*(e1->v1)) && e1->merge(e2)) break; + delete ve; + } + } + else //if (ee->numels()==2) + { + if (e1->t1 != NULL) + { + FOREACHVEEDGE(ee, e2, n) if (e2 != e1 && (((*(e2->v1)) == (*(e1->v1)) && e2->t2 != NULL) || ((*(e2->v1)) == (*(e1->v2)) && e2->t1 != NULL)) && e1->merge(e2)) break; + } + else + { + FOREACHVEEDGE(ee, e2, n) if (e2 != e1 && (((*(e2->v1)) == (*(e1->v1)) && e2->t1 != NULL) || ((*(e2->v1)) == (*(e1->v2)) && e2->t2 != NULL)) && e1->merge(e2)) break; + } + } + if (n == NULL) return false; + + ee->removeNode(e1); ee->removeNode(e2); e1->info = e2->info = NULL; + if (ee->numels() == 0) delete ee; + + Edge *e, *e_1 = NULL, *e_2 = NULL; + ve = e1->v1->VE(); + for (n = ve->head(); n != NULL; n = n->next()) if ((e = (Edge *)n->data)->info != NULL) { e_1 = e; break; } + for (n = ve->tail(); n != NULL; n = n->prev()) if ((e = (Edge *)n->data)->info != NULL) + { + if ((*(e->oppositeVertex(e1->v1))) != (*(e_1->oppositeVertex(e1->v1)))) e_1 = NULL; + break; + } + delete ve; + + ve = e1->v2->VE(); + for (n = ve->head(); n != NULL; n = n->next()) if ((e = (Edge *)n->data)->info != NULL) { e_2 = e; break; } + for (n = ve->tail(); n != NULL; n = n->prev()) if ((e = (Edge *)n->data)->info != NULL) + { + if ((*(e->oppositeVertex(e1->v2))) != (*(e_2->oppositeVertex(e1->v2)))) e_2 = NULL; + break; + } + delete ve; + + if (e_1 != NULL) pinch(e_1, true); + if (e_2 != NULL) pinch(e_2, true); + + return true; +} + +Edge *Basic_TMesh::duplicateEdge(Edge *e1) +{ + if (e1->t1 == NULL || e1->t2 == NULL) return NULL; + Edge *e2 = newEdge(e1); //e2->invert(); + E.appendHead(e2); + e1->t2->replaceEdge(e1, e2); + e2->t2 = e1->t2; e1->t2 = NULL; + return e2; +} + +int Basic_TMesh::cutAndStitch() +{ + Edge *e1, *e2; + Node *n; + List singular_edges; + + FOREACHEDGE(e1, n) if (IS_BIT(e1, 5) && ((e2 = duplicateEdge(e1)) != NULL)) MARK_BIT(e2, 5); + + FOREACHEDGE(e1, n) if (IS_BIT(e1, 5)) + { + singular_edges.appendHead(e1); + UNMARK_BIT(e1, 5); + } + + forceNormalConsistence(); + duplicateNonManifoldVertices(); + + singular_edges.sort(&lexEdgeCompare); + FOREACHEDGE(e1, n) e1->info = NULL; + e2 = NULL; + FOREACHVEEDGE((&singular_edges), e1, n) + { + if (e2 == NULL || lexEdgeCompare(e1, e2) != 0) { e1->info = new List(); e2 = e1; } + ((List *)e2->info)->appendTail(e1); + e1->info = e2->info; + } + // Now each edge is either 'regular' or has the info field pointing to a list of coincident boundary edges + + // First, pinch bounded chains of singular edges starting from one endpoint + FOREACHVEEDGE((&singular_edges), e1, n) if (e1->isLinked()) pinch(e1, true); + + // Then, pinch the remaining unbounded chains starting from any of the edges + FOREACHVEEDGE((&singular_edges), e1, n) if (e1->isLinked()) pinch(e1, false); + + removeUnlinkedElements(); + + d_boundaries = d_handles = d_shells = 1; + + return singular_edges.numels(); +} + + +//int Basic_TMesh::cutAndStitch() +//{ +// Edge *e1, *e2; +// Node *n; +// List cut; +// int i; +// +// FOREACHEDGE(e1, n) if (IS_BIT(e1, 5)) +// { +// if (e1->t1 != NULL && e1->t2 != NULL) +// { +// e2 = newEdge(e1); +// E.appendHead(e2); +// e1->t2->replaceEdge(e1, e2); +// e2->t2 = e1->t2; e1->t2 = NULL; +// } +// cut.appendHead(e1); +// UNMARK_BIT(e1, 5); +// } +// +// do +// { +// i = 0; +// FOREACHVEEDGE((&cut), e1, n) if (e1->v1 != NULL) i += e1->stitch(); +// } while (i); +// +// removeEdges(); +// +// d_boundaries = d_handles = d_shells = 1; +// +// return cut.numels(); +//} + + +////////////////////// PLY LOADER ////////////////////////////////////////////// + +int ply_parseElements(FILE *in, const char *elname) +{ + char c, keyword[64]; + int num; + // skip comments + if (!fscanf(in,"%64s ",keyword)) TMesh::error("Unexpected token or end of file!\n"); + while (!strcmp(keyword,"comment") || !strcmp(keyword,"obj_info")) + { + while ((c = fgetc(in)) != '\n') if (c==EOF) TMesh::error("\nUnexpected end of file!\n"); + if (!fscanf(in,"%64s ",keyword)) TMesh::error("Unexpected token or end of file!\n"); + } + if (strcmp(keyword,"element")) TMesh::error("element definition expected!\n"); + if (!fscanf(in,"%64s ",keyword)) TMesh::error("Unexpected token or end of file!\n"); + if (strcmp(keyword,elname)) TMesh::error("Sorry. Element type '%s' is not supported!\n",keyword); + if (!fscanf(in,"%d\n",&num)) TMesh::error("Unexpected token or end of file!\n"); + if (num <= 0) TMesh::error("Unexpected empty element list!\n"); + + return num; +} + +void ply_checkVertexProperties(FILE *in) +{ + char keyword[64], dtype[64], dval[64]; + if (fscanf(in,"%64s %64s %64s\n",keyword,dtype,dval) < 3) TMesh::error("Unexpected token or end of file!\n"); + if (strcmp(keyword,"property")) TMesh::error("property definition expected!\n"); + if (strcmp(dtype,"float") && strcmp(dtype,"float32")) TMesh::error("float property expected!\n"); + if (strcmp(dval,"x")) TMesh::error("'x' float property expected!\n"); + if (fscanf(in,"%64s %64s %64s\n",keyword,dtype,dval) < 3) TMesh::error("Unexpected token or end of file!\n"); + if (strcmp(keyword,"property")) TMesh::error("property definition expected!\n"); + if (strcmp(dtype,"float") && strcmp(dtype,"float32")) TMesh::error("float property expected!\n"); + if (strcmp(dval,"y")) TMesh::error("'y' float property expected!\n"); + if (fscanf(in,"%64s %64s %64s\n",keyword,dtype,dval) < 3) TMesh::error("Unexpected token or end of file!\n"); + if (strcmp(keyword,"property")) TMesh::error("property definition expected!\n"); + if (strcmp(dtype,"float") && strcmp(dtype,"float32")) TMesh::error("float property expected!\n"); + if (strcmp(dval,"z")) TMesh::error("'z' float property expected!\n"); +} + +int ply_getOverhead(FILE *in, int format, const char *element) +{ + char keyword[64], ptype[64], pname[64]; + int oh = 0; + long pos = ftell(in); + char *rline = readLineFromFile(in); + if (!sscanf(rline,"%64s ",keyword)) TMesh::error("Unexpected token or end of file!\n"); + while (!strcmp(keyword, "property")) + { + if (sscanf(rline,"%64s %64s %64s",keyword,ptype,pname) < 3) TMesh::error("Unexpected token or end of file!\n"); + if (!strcmp(element,"vertex") && !strcmp(pname,"x")) break; + else if (!strcmp(element,"face") && !strcmp(ptype,"list")) break; + pos = ftell(in); + if (!strcmp(ptype, "char") || !strcmp(ptype, "uchar")) oh += (format)?(1):1; + else if (!strcmp(ptype, "short") || !strcmp(ptype, "ushort")) oh += (format)?(2):1; + else if (!strcmp(ptype, "int") || !strcmp(ptype, "uint") || + !strcmp(ptype, "float") || !strcmp(ptype,"float32")) oh += (format)?(4):1; + else if (!strcmp(ptype, "double")) oh += (format)?(8):1; + else if (!strcmp(ptype, "list")) TMesh::error("list properties other than face indices are not supported!\n"); + else TMesh::error("Unrecognized property type!\n"); + if (!sscanf(readLineFromFile(in),"%64s ",keyword)) TMesh::error("Unexpected token or end of file!\n"); + } + fseek(in, pos, SEEK_SET); + + return oh; +} + +void ply_checkFaceProperties(FILE *in) +{ + char keyword[64], ltype[64], uctype[64], dtype[64], dval[64]; + if (fscanf(in,"%64s %64s %64s %64s %64s\n",keyword,ltype,uctype,dtype,dval) < 5) TMesh::error("Unexpected token or end of file!\n"); + if (strcmp(keyword,"property")) TMesh::error("property definition expected!\n"); + if (strcmp(ltype,"list")) TMesh::error("list property expected!\n"); + if (strcmp(uctype,"uchar") && strcmp(uctype,"uint8")) TMesh::error("uchar property expected!\n"); + if (strcmp(dtype,"int") && strcmp(dtype,"int32")) TMesh::error("int property expected!\n"); + if (strcmp(dval,"vertex_indices")) TMesh::error("vertex_indices property expected!\n"); +} + +void ply_readOverhead(FILE *in, int format, int oh) +{ + int i; + static char token[1024]; + if (format == PLY_FORMAT_ASCII) for (i=0; i(nv-1) || i2>(nv-1) || i3>(nv-1)) TMesh::error("\nloadPLY: Invalid index at face %d!\n",i); + for (j=3; j<=i4; j++) + { + if (i1 == i2 || i2 == i3 || i3 == i1) TMesh::warning("\nloadPLY: Coincident indexes at triangle %d! Skipping.\n",i); + else if (!CreateIndexedTriangle(var, i1, i2, i3)) TMesh::warning("\nloadPLY: This shouldn't happen!!! Skipping triangle.\n"); + i2 = i3; + if (jx), TMESH_TO_FLOAT(v->y), TMESH_TO_FLOAT(v->z)); + else FOREACHVERTEX(v, n) + { + fc[0] = TMESH_TO_FLOAT(v->x); fc[1] = TMESH_TO_FLOAT(v->y); fc[2] = TMESH_TO_FLOAT(v->z); + fwrite(fc, sizeof(float), 3, fp); + } + + ocds = new coord[V.numels()]; + i=0; FOREACHVERTEX(v, n) ocds[i++] = v->x; + i=0; FOREACHVERTEX(v, n) v->x = i++; + + if (ascii) FOREACHNODE(T, n) fprintf(fp,"3 %d %d %d\n",TVI1(n),TVI2(n),TVI3(n)); + else FOREACHNODE(T, n) + { + ii[0]=TVI1(n); ii[1]=TVI2(n); ii[2]=TVI3(n); + fwrite(&ii0, sizeof(unsigned char), 1, fp); + fwrite(ii, sizeof(int), 3, fp); + } + + fclose(fp); + i=0; FOREACHVERTEX(v, n) v->x = ocds[i++]; + delete[] ocds; + + return 0; +} + + +////////////////////// Loads OBJ format /////////////////////////// + +int Basic_TMesh::loadOBJ(const char *fname) +{ + FILE *fp; + Node *n; + char c, cmd[3] = ""; + float x,y,z; + bool face_section = 0; + int i=0,i1,i2,i3,nv=0,triangulate=0; + Vertex *v; + ExtVertex **var=NULL; + + if ((fp = fopen(fname,"r")) == NULL) return IO_CANTOPEN; + + TMesh::begin_progress(); + while (fscanf(fp, "%2s", cmd) && cmd[0] != '\0') + { + if (!strcmp(cmd,"v")) + { + if (face_section) TMesh::error("\nloadOBJ: Sorry. Couldn't manage disconnected vertex sections.\n"); + if (fscanf(fp, "%f %f %f", &x, &y, &z) == 3) V.appendTail(newVertex(x,y,z)); + else TMesh::error("\nloadOBJ: Couldn't read coordinates for vertex # %d\n",i); + } + else if (!strcmp(cmd,"f")) + { + if (!face_section) + { + nv = V.numels(); + var = (ExtVertex **)malloc(sizeof(ExtVertex *)*nv); + i=0; FOREACHVERTEX(v, n) var[i++] = new ExtVertex(v); + face_section = 1; + i=0; + } + + if (fscanf(fp,"%d %d %d",&i1,&i2,&i3) == 3) + { + if ((i%1000) == 0) TMesh::report_progress("Loading ..%d%%",(i*100)/(nv*2)); + if (i1<0 || i2<0 || i3<0) TMesh::error("\nloadOBJ: Sorry. Negative vertex references not supported.\n"); + if (i1<1 || i2<1 || i3<1 || i1>nv || i2>nv || i3>nv) TMesh::error("\nloadOBJ: Invalid index at face %d!\n",i); + do + { + if (i1 == i2 || i2 == i3 || i3 == i1) TMesh::warning("\nloadOBJ: Coincident indexes at triangle %d! Skipping.\n",i); + else if (!CreateIndexedTriangle(var, i1-1, i2-1, i3-1)) TMesh::warning("\nloadOBJ: This shouldn't happen!!! Skipping triangle.\n"); + i2 = i3; + while ((c=fgetc(fp)) != EOF && isspace(c) && c != '\n' && c != '\r'); + if (c==EOF) TMesh::error("\nloadOBJ: Unexpected end of file!\n"); + if (c != '\n' && c != '\r') + { + ungetc(c, fp); + if (fscanf(fp,"%d",&i3) != 1) TMesh::error("\nloadOBJ: Couldn't read indexes for face # %d\n",i); + else triangulate=1; + } + } while (c != '\n' && c != '\r'); + } + else TMesh::error("\nloadOBJ: Couldn't read indexes for face # %d\n",i); + i++; + } + else if (readLineFromFile(fp, 0) == NULL) break; + cmd[0]='\0'; + } + + TMesh::end_progress(); + + closeLoadingSession(fp, i, var, (triangulate!=0)); + TMesh::setFilename(fname); + + return 0; +} + +////////////////////// Saves OBJ format /////////////////////////// + +int Basic_TMesh::saveOBJ(const char *fname) +{ + FILE *fp; + int i; + char triname[256]; + Node *n; + coord *ocds; + Vertex *v; + + strcpy(triname,fname); + + if ((fp = fopen(triname,"w")) == NULL) + { + TMesh::warning("Can't open '%s' for output !\n",triname); + return 1; + } + + PRINT_HEADING_COMMENT(fp); + + FOREACHVERTEX(v, n) fprintf(fp, "v %f %f %f\n", TMESH_TO_FLOAT(v->x), TMESH_TO_FLOAT(v->y), TMESH_TO_FLOAT(v->z)); + + ocds = new coord[V.numels()]; + i=0; FOREACHVERTEX(v, n) ocds[i++] = v->x; + i=0; FOREACHVERTEX(v, n) v->x = i++; + + FOREACHNODE(T, n) fprintf(fp,"f %d %d %d\n",TVI1(n)+1,TVI2(n)+1,TVI3(n)+1); + + fclose(fp); + i=0; FOREACHVERTEX(v, n) v->x = ocds[i++]; + delete[] ocds; + + return 0; +} + + + +////////////////////// Loads STL format /////////////////////////// + +int Basic_TMesh::loadSTL(const char *fname) +{ + FILE *fp; + int nt=0, i=0; + char kw[64]="", kw2[64]="", *line, facet[50]; + float x,y,z; + bool binary=0; + Vertex *v, *v1=NULL, *v2=NULL, *v3=NULL; + Edge *e1, *e2, *e3; + Triangle *t; + Point nor; + + if ((fp = fopen(fname,"r")) == NULL) return IO_CANTOPEN; + + fscanf(fp,"%5s",kw); + if (strcmp(kw,"solid")) binary=1; + + if (!binary) // Might look ASCII just because it begins with 'solid'... + { + rewind(fp); + if ((line = readLineFromFile(fp, 0)) == NULL) binary = 1; + else if ((line = readLineFromFile(fp, 0)) == NULL) binary = 1; + else { + sscanf(line, "%64s", kw); + if (strcmp(kw, "facet")) binary = 1; + } + } + if (binary) fp = freopen(fname, "rb", fp); + + TMesh::begin_progress(); + + if (binary) + { + fseek(fp, 80, SEEK_SET); + fread(&nt, 4, 1, fp); + + for (i=0; igetNormal(); + fprintf(fp, " facet normal %f %f %f\n", TMESH_TO_FLOAT(nor.x), TMESH_TO_FLOAT(nor.y), TMESH_TO_FLOAT(nor.z)); + fprintf(fp, " outer loop\n"); + fprintf(fp, " vertex %f %f %f\n", TMESH_TO_FLOAT(t->v1()->x), TMESH_TO_FLOAT(t->v1()->y), TMESH_TO_FLOAT(t->v1()->z)); + fprintf(fp, " vertex %f %f %f\n", TMESH_TO_FLOAT(t->v2()->x), TMESH_TO_FLOAT(t->v2()->y), TMESH_TO_FLOAT(t->v2()->z)); + fprintf(fp, " vertex %f %f %f\n", TMESH_TO_FLOAT(t->v3()->x), TMESH_TO_FLOAT(t->v3()->y), TMESH_TO_FLOAT(t->v3()->z)); + fprintf(fp, " endloop\n"); + fprintf(fp, " endfacet\n"); + } + fprintf(fp, "endsolid T_MESH\n"); + + fclose(fp); + + return 0; +} + +////////////////////// Saves Extact File format (EFF) /////////////////////////// + +int Basic_TMesh::saveEFF(const char *fname) +{ + std::ofstream os; + os.open(fname); + if (!os.is_open()) + { + TMesh::warning("Can't open '%s' for output !\n", fname); + return 1; + } + + os << "EFF\n"; + os << V.numels() << " " << T.numels() << "\n"; + + Node *n; + Vertex *v; + FOREACHVERTEX(v, n) os << v->x << " " << v->y << " " << v->z << "\n"; + + coord *ocds = new coord[V.numels()]; + int i; + i = 0; FOREACHVERTEX(v, n) ocds[i++] = v->x; + i = 0; FOREACHVERTEX(v, n) v->x = i++; + + FOREACHNODE(T, n) os << TVI1(n) << " " << TVI2(n) << " " << TVI3(n) << "\n"; + + os.close(); + i = 0; FOREACHVERTEX(v, n) v->x = ocds[i++]; + delete[] ocds; + + return 0; +} + +} //namespace T_MESH diff --git a/src/mesh_fix/src/TMesh/tin.cpp b/src/mesh_fix/src/TMesh/tin.cpp new file mode 100644 index 000000000..3240e17d9 --- /dev/null +++ b/src/mesh_fix/src/TMesh/tin.cpp @@ -0,0 +1,2122 @@ +/**************************************************************************** +* TMesh * +* * +* Consiglio Nazionale delle Ricerche * +* Istituto di Matematica Applicata e Tecnologie Informatiche * +* Sezione di Genova * +* IMATI-GE / CNR * +* * +* Authors: Marco Attene * +* Copyright(C) 2013: IMATI-GE / CNR * +* All rights reserved. * +* * +* This program is dual-licensed as follows: * +* * +* (1) You may use TMesh as 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 3 of the License, or * +* (at your option) any later version. * +* In this case the 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 (http://www.gnu.org/licenses/gpl.txt) * +* for more details. * +* * +* (2) You may use TMesh as part of a commercial software. In this case a * +* proper agreement must be reached with the Authors and with IMATI-GE/CNR * +* based on a proper licensing contract. * +* * +****************************************************************************/ + +#include "tin.h" +#include +#include + +namespace T_MESH +{ + + Vertex * Basic_TMesh::newVertex(){ return new Vertex(); } //!< AMF_ADD 1.1> + Vertex * Basic_TMesh::newVertex(const coord &x, const coord &y, const coord &z){ return new Vertex(x, y, z); } //!< AMF_ADD 1.1> + Vertex * Basic_TMesh::newVertex(Point *p){ return new Vertex(p); } //!< AMF_ADD 1.1> + Vertex * Basic_TMesh::newVertex(Point &p){ return new Vertex(p); } //!< AMF_ADD 1.1> + Vertex * Basic_TMesh::newVertex(Vertex *v){ return new Vertex(v); } //!< AMF_ADD 1.1-2> + Edge * Basic_TMesh::newEdge(Vertex *s, Vertex *d){ return new Edge(s, d); } //!< AMF_ADD 1.1> + Edge * Basic_TMesh::newEdge(Edge *e){ return new Edge(e->v1,e->v2); } //!< AMF_ADD 1.1-2> + Triangle * Basic_TMesh::newTriangle(){ return new Triangle(); } //!< AMF_ADD 1.1> + Triangle * Basic_TMesh::newTriangle(Edge *a, Edge *b, Edge *c){ return new Triangle(a, b, c); } //!< AMF_ADD 1.1> + +////////////////////////////////////////////////////////////////// +// // +// C L A S S C O N S T R U C T O R S // +// // +////////////////////////////////////////////////////////////////// + + +///////////////////// Constructor (Empty) //////////////////// + +Basic_TMesh::Basic_TMesh() +{ + info=NULL; + n_boundaries = n_handles = n_shells = 0; + d_boundaries = d_handles = d_shells = 0; +} + + +//////////////////// Constructor (Pre-defined) /////////////// + +Basic_TMesh::Basic_TMesh(const char *tin_definition) { init(tin_definition); } + +void Basic_TMesh::init(const char *tin_definition) +{ + info=NULL; + if (!strcmp(tin_definition, "triangle")) + { + Vertex *v1 = newVertex(0,0,0); + Vertex *v2 = newVertex(2,0,0); + Vertex *v3 = newVertex(1,1,0); + Edge *e1 = newEdge(v1,v2); v1->e0 = e1; + Edge *e2 = newEdge(v2,v3); v2->e0 = e2; + Edge *e3 = newEdge(v3,v1); v3->e0 = e3; + Triangle *t1 = newTriangle(e1,e2,e3); + e1->t1 = t1; e1->t2 = NULL; + e2->t1 = t1; e2->t2 = NULL; + e3->t1 = t1; e3->t2 = NULL; + V.appendHead(v1); V.appendHead(v2); V.appendHead(v3); + T.appendHead(t1); + E.appendHead(e1); E.appendHead(e2); E.appendHead(e3); + + n_boundaries = 1; + n_handles = 0; + n_shells = 1; + d_boundaries = d_handles = d_shells = 0; + } + else if (!strcmp(tin_definition, "tetrahedron")) + { + Vertex *v1 = newVertex(-1,-1.4142136,0); + Vertex *v2 = newVertex(-1,1.4142136,0); + Vertex *v3 = newVertex(1,0,-1.4142136); + Vertex *v4 = newVertex(1,0,1.4142136); + Edge *e1 = newEdge(v1,v2); v1->e0 = e1; + Edge *e2 = newEdge(v2,v3); v2->e0 = e2; + Edge *e3 = newEdge(v3,v1); v3->e0 = e3; + Edge *e4 = newEdge(v1,v4); v4->e0 = e4; + Edge *e5 = newEdge(v2,v4); + Edge *e6 = newEdge(v3,v4); + Triangle *t1 = newTriangle(e1,e2,e3); + Triangle *t2 = newTriangle(e1,e4,e5); + Triangle *t3 = newTriangle(e2,e5,e6); + Triangle *t4 = newTriangle(e3,e6,e4); + e1->t1 = t1; e1->t2 = t2; + e2->t1 = t1; e2->t2 = t3; + e3->t1 = t1; e3->t2 = t4; + e4->t1 = t2; e4->t2 = t4; + e5->t1 = t3; e5->t2 = t2; + e6->t1 = t4; e6->t2 = t3; + V.appendHead(v1); V.appendHead(v2); V.appendHead(v3); V.appendHead(v4); + T.appendHead(t1); T.appendHead(t2); T.appendHead(t3); T.appendHead(t4); + E.appendHead(e1); E.appendHead(e2); E.appendHead(e3); + E.appendHead(e4); E.appendHead(e5); E.appendHead(e6); + + n_boundaries = 0; + n_handles = 0; + n_shells = 1; + d_boundaries = d_handles = d_shells = 0; + } + else if (!strcmp(tin_definition, "cube")) + { + const double crds[8][3] = {{0, 0, 0},{1, 0, 0},{1, 1, 0},{0, 1, 0},{0, 0, 1},{1, 0, 1},{1, 1, 1},{0, 1, 1}}; + const int tris[12][3] = {{3, 2, 1},{3, 1, 0},{4, 5, 6},{4, 6, 7},{7, 6, 2},{7, 2, 3},{0, 1, 5},{0, 5, 4},{1, 2, 6},{1, 6, 5},{3, 0, 4},{3, 4, 7}}; + ExtVertex *ev[8]; + for (int i=0; i<8; i++) + { + Vertex *v = newVertex(crds[i][0], crds[i][1], crds[i][2]); + ev[i] = new ExtVertex(v); + V.appendTail(v); + } + for (int i=0; i<12; i++) + CreateIndexedTriangle(ev, tris[i][0], tris[i][1], tris[i][2]); + for (int i=0; i<8; i++) delete ev[i]; + + n_boundaries = 1; + n_handles = 0; + n_shells = 1; + d_boundaries = d_handles = d_shells = 0; + } + else if (!strcmp(tin_definition, "cylinder")) + { + const double crds[8][2] = {{1,0},{0.7,0.7},{0,1},{-0.7,0.7},{-1,0},{-0.7,-0.7},{0,-1},{0.7,-0.7}}; + ExtVertex *ev[8]; + for (int i=0; i<16; i++) + { + Vertex *v = newVertex(crds[i%8][0], crds[i%8][1], (i<8)?(-1):(1)); + ev[i] = new ExtVertex(v); + V.appendTail(v); + } + + for (int i=0; i<8; i++) + { + CreateIndexedTriangle(ev, i, (i+1)%8, i+8); + CreateIndexedTriangle(ev, i+8, (i+1)%8, 8+(i+1)%8); + } + for (int i=0; i<6; i++) + { + CreateIndexedTriangle(ev, 0, i+2, i+1); + CreateIndexedTriangle(ev, 8, i+9, i+10); + } + for (int i=0; i<8; i++) delete ev[i]; + + n_boundaries = 1; + n_handles = 0; + n_shells = 1; + d_boundaries = d_handles = d_shells = 0; + } + else TMesh::error("Unknown triangulation type '%s'\n",tin_definition); +} + + +///////////////////// Cloning TIN /////////////////////////// + +Basic_TMesh::Basic_TMesh(const Basic_TMesh *tin, const bool clone_info) { init(tin, clone_info); } + +void Basic_TMesh::init(const Basic_TMesh *tin, const bool clone_info) +{ + info=NULL; + Node *n; + Vertex *v, *nv; + Edge *e, *ne; + Triangle *t, *nt; + + int i; + void **t_info = new void *[tin->T.numels()]; + i=0; FOREACHVTTRIANGLE((&(tin->T)), t, n) t_info[i++]=t->info; + void **e_info = new void *[tin->E.numels()]; + i=0; FOREACHVEEDGE((&(tin->E)), e, n) e_info[i++]=e->info; + void **v_info = new void *[tin->V.numels()]; + i=0; FOREACHVVVERTEX((&(tin->V)), v, n) v_info[i++]=v->info; + + FOREACHVVVERTEX((&(tin->V)), v, n) + { + nv = newVertex(v); V.appendTail(nv); v->info = nv; + } + + FOREACHVEEDGE((&(tin->E)), e, n) + {ne=newEdge((Vertex *)e->v1->info, (Vertex *)e->v2->info); E.appendTail(ne); e->info = ne;} + + FOREACHVTTRIANGLE((&(tin->T)), t, n) + {nt=newTriangle((Edge *)t->e1->info,(Edge *)t->e2->info,(Edge *)t->e3->info); T.appendTail(nt); t->info = nt;} + + FOREACHVVVERTEX((&(tin->V)), v, n) {((Vertex *)v->info)->e0 = (Edge *)v->e0->info; v->info = NULL;} + + FOREACHVEEDGE((&(tin->E)), e, n) + {((Edge *)e->info)->t1 = (e->t1)?((Triangle *)e->t1->info):(NULL); ((Edge *)e->info)->t2 = (e->t2)?((Triangle *)e->t2->info):(NULL); e->info = NULL;} + + i=0; FOREACHVTTRIANGLE((&(tin->T)), t, n) t->info=t_info[i++]; + i=0; FOREACHVEEDGE((&(tin->E)), e, n) e->info=e_info[i++]; + i=0; FOREACHVVVERTEX((&(tin->V)), v, n) v->info=v_info[i++]; + + if (clone_info) + { + i=0; FOREACHTRIANGLE(t, n) t->info=t_info[i++]; + i=0; FOREACHEDGE(e, n) e->info=e_info[i++]; + i=0; FOREACHVERTEX(v, n) v->info=v_info[i++]; + } + delete [] t_info; delete [] e_info; delete [] v_info; + + d_boundaries = d_handles = d_shells = 1; +} + + +//// Creates a new Basic_TMesh out of a connected component of an existing Basic_TMesh. +//// If 'keep_reference' is set to 'true', each element of the existing mesh keeps a +//// pointer to the corresponding new element in the 'info' field. + +Basic_TMesh::Basic_TMesh(const Triangle *t0, const bool keep_reference) { init(t0, keep_reference); } + +void Basic_TMesh::init(const Triangle *t0, const bool keep_reference) +{ + info=NULL; + List todo(t0), st, sv, se; + Node *n; + Triangle *t, *nt; + Vertex *v, *nv; + Edge *e, *ne; + + t=(Triangle *)t0; + MARK_VISIT2(t); + + while (todo.numels()) + { + t = (Triangle *)todo.popHead(); + st.appendHead(t); + nt=t->t1(); if (nt != NULL && !IS_VISITED2(nt)) {MARK_VISIT2(nt); todo.appendHead(nt);} + nt=t->t2(); if (nt != NULL && !IS_VISITED2(nt)) {MARK_VISIT2(nt); todo.appendHead(nt);} + nt=t->t3(); if (nt != NULL && !IS_VISITED2(nt)) {MARK_VISIT2(nt); todo.appendHead(nt);} + } + + FOREACHVTTRIANGLE((&st), t, n) + { + UNMARK_VISIT2(t); + e = t->e1; if (!IS_VISITED2(e)) {MARK_VISIT2(e); se.appendHead(e);} + e = t->e2; if (!IS_VISITED2(e)) {MARK_VISIT2(e); se.appendHead(e);} + e = t->e3; if (!IS_VISITED2(e)) {MARK_VISIT2(e); se.appendHead(e);} + v = t->v1(); if (!IS_VISITED2(v)) {MARK_VISIT2(v); sv.appendHead(v);} + v = t->v2(); if (!IS_VISITED2(v)) {MARK_VISIT2(v); sv.appendHead(v);} + v = t->v3(); if (!IS_VISITED2(v)) {MARK_VISIT2(v); sv.appendHead(v);} + } + + FOREACHVVVERTEX((&sv), v, n) + {UNMARK_VISIT2(v); nv=newVertex(v); V.appendTail(nv); v->info = nv;} + + FOREACHVEEDGE((&se), e, n) + {UNMARK_VISIT2(e); ne=newEdge((Vertex *)e->v1->info, (Vertex *)e->v2->info); E.appendTail(ne); e->info = ne;} + + FOREACHVTTRIANGLE((&st), t, n) + {nt=newTriangle((Edge *)t->e1->info,(Edge *)t->e2->info,(Edge *)t->e3->info); T.appendTail(nt); t->info = nt;} + + FOREACHVVVERTEX((&sv), v, n) ((Vertex *)v->info)->e0 = (Edge *)v->e0->info; + + FOREACHVEEDGE((&se), e, n) + {((Edge *)e->info)->t1 = (e->t1)?((Triangle *)e->t1->info):(NULL); ((Edge *)e->info)->t2 = (e->t2)?((Triangle *)e->t2->info):(NULL);} + + if (!keep_reference) + { + FOREACHVVVERTEX((&sv), v, n) v->info = NULL; + FOREACHVEEDGE((&se), e, n) e->info = NULL; + FOREACHVTTRIANGLE((&st), t, n) t->info = NULL; + } + + eulerUpdate(); +} + + +Basic_TMesh *Basic_TMesh::split() +{ + if (T.numels() == 0) return NULL; + deselectTriangles(); + Triangle *t = (Triangle *)T.head()->data; + selectConnectedComponent(t); + Basic_TMesh *stin = createSubMeshFromSelection(t); + removeSelectedTriangles(); + return stin; +} + +///////////////////// Destructor /////////////////////////// + +Basic_TMesh::~Basic_TMesh() +{ + T.freeNodes(); + V.freeNodes(); + E.freeNodes(); +} + + +////////////////////////////////////////////////////////////////// +// // +// P R I M I T I V E C O N S T R U C T I O N // +// // +////////////////////////////////////////////////////////////////// + + +//////////////////// Creates an edge //////////////////////////// + +Edge *Basic_TMesh::CreateEdge(Vertex *v1, Vertex *v2) +{ + Edge *e; + + if ((e = v1->getEdge(v2)) != NULL) return e; + + e = newEdge(v1,v2); + v1->e0 = e; + v2->e0 = e; + E.appendHead(e); + + return e; +} + + +//////////////////// Creates an edge //////////////////////////// + +Edge *Basic_TMesh::CreateEdge(ExtVertex *v1, ExtVertex *v2, const bool check) +{ + Edge *e; + Node *n; + + if (check) + FOREACHVEEDGE((&(v1->VE)), e, n) + if (e->oppositeVertex(v1->v) == v2->v) return e; + + e = newEdge(v1->v,v2->v); + if (v1->v->e0 == NULL) v1->v->e0 = e; + if (v2->v->e0 == NULL) v2->v->e0 = e; + v1->VE.appendHead(e); + v2->VE.appendHead(e); + E.appendHead(e); + + return e; +} + + +///////////////////// Creates a triangle ////////////////////////// + +Triangle *Basic_TMesh::CreateTriangle(Edge *e1, Edge *e2, Edge *e3) +{ + Triangle *tt, **at1, **at2, **at3; + + if (e1->commonVertex(e2) == e1->v2 && e1->t1 == NULL) at1 = &(e1->t1); + else if (e1->commonVertex(e2) == e1->v1 && e1->t2 == NULL) at1 = &(e1->t2); + else return NULL; + if (e2->commonVertex(e3) == e2->v2 && e2->t1 == NULL) at2 = &(e2->t1); + else if (e2->commonVertex(e3) == e2->v1 && e2->t2 == NULL) at2 = &(e2->t2); + else return NULL; + if (e3->commonVertex(e1) == e3->v2 && e3->t1 == NULL) at3 = &(e3->t1); + else if (e3->commonVertex(e1) == e3->v1 && e3->t2 == NULL) at3 = &(e3->t2); + else return NULL; + + tt = newTriangle(e1,e2,e3); + *at1 = *at2 = *at3 = tt; + T.appendHead(tt); + + MARK_VISIT(tt); // < AMF_ADD - since IMATI-STL 2.4-1 > + + d_boundaries = d_handles = d_shells = 1; + + return tt; +} + + +///////////////////// Creates an unoriented triangle ////////////////////////// + +Triangle *Basic_TMesh::CreateUnorientedTriangle(Edge *e1, Edge *e2, Edge *e3) +{ + Triangle *tt, **at1, **at2, **at3; + + if (e1->t1 == NULL) at1 = &(e1->t1); + else if (e1->t2 == NULL) at1 = &(e1->t2); + else return NULL; + if (e2->t1 == NULL) at2 = &(e2->t1); + else if (e2->t2 == NULL) at2 = &(e2->t2); + else return NULL; + if (e3->t1 == NULL) at3 = &(e3->t1); + else if (e3->t2 == NULL) at3 = &(e3->t2); + else return NULL; + + tt = newTriangle(e1,e2,e3); + *at1 = *at2 = *at3 = tt; + T.appendHead(tt); + + return tt; +} + + +////////////// Euler operatior: Create edge and triangle ////////////////////// + +Triangle *Basic_TMesh::EulerEdgeTriangle(Edge *e2, Edge *e3) +{ + Vertex *cv = e2->commonVertex(e3); + Triangle *adj = (e2->t1 == NULL)?(e2->t2):(e2->t1); + if (cv == NULL || !e2->isOnBoundary() || !e3->isOnBoundary()) return NULL; + + Edge *e1 = CreateEdge(e2->oppositeVertex(cv), e3->oppositeVertex(cv)); + if (adj->nextEdge(e2)->hasVertex(cv)) return CreateTriangle(e1,e3,e2); + return CreateTriangle(e1,e2,e3); +} + + +/////////// Creation of two triangles bridging two boundary edges ///////////// + +Edge *Basic_TMesh::bridgeBoundaries(Edge *gve, Edge *gwe) +{ + if (gve == gwe || !gve->isOnBoundary() || !gwe->isOnBoundary()) return NULL; + + Triangle *t; + Vertex *v = gve->commonVertex(gwe); + if (v != NULL) + { + t = EulerEdgeTriangle(gve, gwe); + return gve; + } + + Vertex *gv = (gve->t1) ? (gve->v1) : (gve->v2); + Vertex *gw = (gwe->t1) ? (gwe->v2) : (gwe->v1); + Vertex *gvn = gve->oppositeVertex(gv); + Vertex *gwn = gwe->oppositeVertex(gw); + + Edge *je = CreateEdge(gv, gw); + Edge *je2 = CreateEdge(gwn, gvn); + Edge *je1 = CreateEdge(gv, gwn); + + t = CreateTriangle(je, gwe, je1); + t = CreateTriangle(je1, je2, gve); + + return je1; +} + +////////////////////////////////////////////////////////////////// +// // +// P R I M I T I V E D E S T R U C T I O N // +// // +////////////////////////////////////////////////////////////////// + + +/////// Unlinks the triangle (elements are not removed from the lists) ////////// + +void Basic_TMesh::unlinkTriangle(Triangle *t) +{ + Vertex *v1 = t->v1(), *v2 = t->v2(), *v3 = t->v3(); + Edge *e1 = t->e1, *e2 = t->e2, *e3 = t->e3; + + int v1nm = (v1->isOnBoundary() && !e1->isOnBoundary() && !e2->isOnBoundary()); + int v2nm = (v2->isOnBoundary() && !e2->isOnBoundary() && !e3->isOnBoundary()); + int v3nm = (v3->isOnBoundary() && !e3->isOnBoundary() && !e1->isOnBoundary()); + + v1->e0 = ((e2->isOnBoundary())?(e1):(e2)); + v2->e0 = ((e3->isOnBoundary())?(e2):(e3)); + v3->e0 = ((e1->isOnBoundary())?(e3):(e1)); + + e1->replaceTriangle(t, NULL); + e2->replaceTriangle(t, NULL); + e3->replaceTriangle(t, NULL); + + if (e1->isIsolated() && e2->isIsolated()) v1->e0 = NULL; + if (e2->isIsolated() && e3->isIsolated()) v2->e0 = NULL; + if (e3->isIsolated() && e1->isIsolated()) v3->e0 = NULL; + if (e1->isIsolated()) e1->v1 = e1->v2 = NULL; + if (e2->isIsolated()) e2->v1 = e2->v2 = NULL; + if (e3->isIsolated()) e3->v1 = e3->v2 = NULL; + t->e1 = t->e2 = t->e3 = NULL; + + Vertex *nv; + Edge *e; + List *ve; + Node *n; + + if (v1nm) + { + nv = newVertex(v1->x, v1->y, v1->z); + nv->e0 = v1->e0; + ve = v1->VE(); + FOREACHVEEDGE(ve, e, n) e->replaceVertex(v1, nv); + delete(ve); + v1->e0 = e1; + V.appendHead(nv); + } + if (v2nm) + { + nv = newVertex(v2->x, v2->y, v2->z); + nv->e0 = v2->e0; + ve = v2->VE(); + FOREACHVEEDGE(ve, e, n) e->replaceVertex(v2, nv); + delete(ve); + v2->e0 = e2; + V.appendHead(nv); + } + if (v3nm) + { + nv = newVertex(v3->x, v3->y, v3->z); + nv->e0 = v3->e0; + ve = v3->VE(); + FOREACHVEEDGE(ve, e, n) e->replaceVertex(v3, nv); + delete(ve); + v3->e0 = e3; + V.appendHead(nv); + } +} + + +//// Unlinks the triangle (elements are not removed from the lists) //// +//// Differently from the above method, non-manifold vertices are not duplicated //// + +void Basic_TMesh::unlinkTriangleNoManifold(Triangle *t) +{ + Edge *e1 = t->e1, *e2 = t->e2, *e3 = t->e3; + + e1->replaceTriangle(t, NULL); + e2->replaceTriangle(t, NULL); + e3->replaceTriangle(t, NULL); + + if (e1->isIsolated()) e1->v1 = e1->v2 = NULL; + if (e2->isIsolated()) e2->v1 = e2->v2 = NULL; + if (e3->isIsolated()) e3->v1 = e3->v2 = NULL; + t->e1 = t->e2 = t->e3 = NULL; +} + + +///// Removes all the triangles with NULL edges ///// + +int Basic_TMesh::removeTriangles() +{ + Node *n; + Triangle *t; + int r = 0; + + n = T.head(); + while (n != NULL) + { + t = (Triangle *)n->data; + n = n->next(); + if (t->e1 == NULL || t->e2 == NULL || t->e3 == NULL) + { + r++; + T.removeCell((n!=NULL)?(n->prev()):T.tail()); + delete t; + } + } + + d_boundaries = d_handles = d_shells = 1; + + return r; +} + + +///// Removes all the edges with NULL vertices ///// + +int Basic_TMesh::removeEdges() +{ + Node *n; + Edge *e; + int r = 0; + + n = E.head(); + while (n != NULL) + { + e = (Edge *)n->data; + n = n->next(); + if (e->v1 == NULL || e->v2 == NULL) + { + r++; + E.removeCell((n!=NULL)?(n->prev()):E.tail()); + delete e; + } + } + + d_boundaries = d_handles = d_shells = 1; + + return r; +} + + +/////////// Removes all the vertices with e0 field = NULL //////////// + +int Basic_TMesh::removeVertices() +{ + Node *n; + Vertex *v; + int r = 0; + + n = V.head(); + while (n != NULL) + { + v = (Vertex *)n->data; + n = n->next(); + if (v->e0 == NULL) + { + r++; + V.removeCell((n!=NULL)?(n->prev()):V.tail()); + delete v; + } + } + + d_boundaries = d_handles = d_shells = 1; + + return r; +} + + +//// Removes all the vertices that can be deleted without changing the geometric realization. O(N). +int Basic_TMesh::removeRedundantVertices() +{ + Node *n; + Vertex *v; + int fv = 0; + FOREACHVERTEX(v, n) if (v->removeIfRedundant()) fv++; + removeUnlinkedElements(); + return fv; +} + +////////////////////////////////////////////////////////////////// +// // +// S E L E C T I O N M A N A G E M E N T // +// // +////////////////////////////////////////////////////////////////// + + +//////////////// Deselect all the triangles ///////////// + +void Basic_TMesh::deselectTriangles() +{ + Triangle *t; + Node *n; + FOREACHTRIANGLE(t, n) UNMARK_VISIT(t); +} + + +//////// Removes all the selected (IS_VISITED) triangles ///////////// + +void Basic_TMesh::removeSelectedTriangles() +{ + Node *n; + Triangle *t; + + FOREACHTRIANGLE(t, n) if (IS_VISITED(t)) unlinkTriangle(t); + removeUnlinkedElements(); +} + + +// Mark all the triangles having at least one boundary vertex as 'selected' + +int Basic_TMesh::selectBoundaryTriangles() +{ + Node *n; + Edge *e; + Vertex *v, *v1, *v2, *v3; + Triangle *t; + int ns=0; + + FOREACHEDGE(e, n) if (e->isOnBoundary()) {MARK_VISIT(e->v1); MARK_VISIT(e->v2);} + + FOREACHTRIANGLE(t, n) if (!IS_VISITED(t)) + { + v1 = t->v1(); v2 = t->v2(); v3 = t->v3(); + if (IS_VISITED(v1) || IS_VISITED(v2) || IS_VISITED(v3)) {MARK_VISIT(t); ns++;} + } + FOREACHVERTEX(v, n) UNMARK_VISIT(v); + + return ns; +} + + +// Grows the current selection (1 triangle width) + +int Basic_TMesh::growSelection() +{ + Node *n; + Vertex *v, *v1, *v2, *v3; + Triangle *t; + int ns=0; + + FOREACHTRIANGLE(t, n) if (IS_VISITED(t)) + { + v1 = t->v1(); v2 = t->v2(); v3 = t->v3(); + MARK_VISIT(v1); MARK_VISIT(v2); MARK_VISIT(v3); + } + FOREACHTRIANGLE(t, n) if (!IS_VISITED(t)) + { + v1 = t->v1(); v2 = t->v2(); v3 = t->v3(); + if (IS_VISITED(v1) || IS_VISITED(v2) || IS_VISITED(v3)) {MARK_VISIT(t); ns++;} + } + + FOREACHVERTEX(v, n) UNMARK_VISIT(v); + + return ns; +} + + +// Shrinks the current selection (1 triangle width) + +void Basic_TMesh::shrinkSelection() +{ + Node *n; + Vertex *v, *v1, *v2, *v3; + Triangle *t; + + FOREACHTRIANGLE(t, n) if (!IS_VISITED(t)) + { + v1 = t->v1(); v2 = t->v2(); v3 = t->v3(); + MARK_VISIT(v1); MARK_VISIT(v2); MARK_VISIT(v3); + } + FOREACHTRIANGLE(t, n) if (IS_VISITED(t)) + { + v1 = t->v1(); v2 = t->v2(); v3 = t->v3(); + if (IS_VISITED(v1) || IS_VISITED(v2) || IS_VISITED(v3)) UNMARK_VISIT(t); + } + + FOREACHVERTEX(v, n) UNMARK_VISIT(v); +} + + +// Toggles the selection status of the triangles + +void Basic_TMesh::invertSelection(Triangle *t0) +{ + Node *n; + Triangle *t; + + if (t0 != NULL) + { + List totoggle(t0); + Triangle *s; + bool unmark = IS_VISITED(t0); + if (unmark) UNMARK_VISIT(t0); else MARK_VISIT(t0); + while ((t = (Triangle *)totoggle.popHead()) != NULL) + { + if ((s = t->t1()) != NULL && ((IS_VISITED(s) && unmark) || (!IS_VISITED(s) && !unmark))) + {if (unmark) UNMARK_VISIT(s); else MARK_VISIT(s); totoggle.appendTail(s);} + if ((s = t->t2()) != NULL && ((IS_VISITED(s) && unmark) || (!IS_VISITED(s) && !unmark))) + {if (unmark) UNMARK_VISIT(s); else MARK_VISIT(s); totoggle.appendTail(s);} + if ((s = t->t3()) != NULL && ((IS_VISITED(s) && unmark) || (!IS_VISITED(s) && !unmark))) + {if (unmark) UNMARK_VISIT(s); else MARK_VISIT(s); totoggle.appendTail(s);} + } + } + else + { + FOREACHTRIANGLE(t, n) if (IS_VISITED(t)) UNMARK_VISIT(t); else MARK_VISIT(t); + } +} + + +void Basic_TMesh::reselectSelection(Triangle *t0) +{ + if (!IS_VISITED(t0)) return; + + Node *n; + Triangle *t, *s; + List triList(t0); + MARK_VISIT2(t0); + + while(triList.numels()) + { + t = (Triangle *)triList.popHead(); + if ((s = t->t1()) != NULL && !IS_VISITED2(s) && IS_VISITED(s)) {triList.appendHead(s); MARK_VISIT2(s);} + if ((s = t->t2()) != NULL && !IS_VISITED2(s) && IS_VISITED(s)) {triList.appendHead(s); MARK_VISIT2(s);} + if ((s = t->t3()) != NULL && !IS_VISITED2(s) && IS_VISITED(s)) {triList.appendHead(s); MARK_VISIT2(s);} + } + + FOREACHTRIANGLE(t, n) if (!IS_VISITED2(t)) UNMARK_VISIT(t); else UNMARK_VISIT2(t); +} + +// Creates a new mesh out of a selection. + +Basic_TMesh *Basic_TMesh::createSubMeshFromSelection(Triangle *t0, bool keep_ref) +{ + Triangle *t,*s, *nt; + Node *n; + + if (t0 != NULL && !IS_VISITED(t0)) return NULL; + + Basic_TMesh *tin = newObject(); + Vertex *v,*nv; + Edge *e, *ne; + List triList, sT, sE, sV; + + if (t0 != NULL) + { + triList.appendHead(t0); MARK_BIT(t0,5); + while (triList.numels()) + { + t = (Triangle *)triList.popHead(); + sT.appendHead(t); + if ((s = t->t1()) != NULL && !IS_BIT(s, 5) && IS_VISITED(s)) { triList.appendHead(s); MARK_BIT(s, 5); } + if ((s = t->t2()) != NULL && !IS_BIT(s, 5) && IS_VISITED(s)) { triList.appendHead(s); MARK_BIT(s, 5); } + if ((s = t->t3()) != NULL && !IS_BIT(s, 5) && IS_VISITED(s)) { triList.appendHead(s); MARK_BIT(s, 5); } + } + FOREACHVTTRIANGLE((&sT), t, n) + { + UNMARK_BIT(t->e1, 5); UNMARK_BIT(t->e2, 5); UNMARK_BIT(t->e3, 5); + UNMARK_BIT(t->v1(), 5); UNMARK_BIT(t->v2(), 5); UNMARK_BIT(t->v3(), 5); + } + FOREACHVTTRIANGLE((&sT), t, n) + { + if (!IS_BIT(t->e1, 5)) { sE.appendHead(t->e1); MARK_BIT(t->e1, 5); } + if (!IS_BIT(t->e2, 5)) { sE.appendHead(t->e2); MARK_BIT(t->e2, 5); } + if (!IS_BIT(t->e3, 5)) { sE.appendHead(t->e3); MARK_BIT(t->e3, 5); } + if ((v = t->v1()) && !IS_BIT(v, 5)) { sV.appendHead(v); MARK_BIT(v, 5); } + if ((v = t->v2()) && !IS_BIT(v, 5)) { sV.appendHead(v); MARK_BIT(v, 5); } + if ((v = t->v3()) && !IS_BIT(v, 5)) { sV.appendHead(v); MARK_BIT(v, 5); } + } + } + else + { + FOREACHEDGE(e, n) UNMARK_BIT(e, 5); + FOREACHVERTEX(v, n) UNMARK_BIT(v, 5); + FOREACHTRIANGLE(t, n) if (IS_VISITED(t)) + {sT.appendHead(t); MARK_BIT(t->e1, 5); MARK_BIT(t->e2, 5); MARK_BIT(t->e3, 5);} + FOREACHEDGE(e, n) if (IS_BIT(e,5)) + {sE.appendHead(e); MARK_BIT(e->v1, 5); MARK_BIT(e->v2, 5);} + FOREACHVERTEX(v, n) if (IS_BIT(v,5)) sV.appendHead(v); + } + + FOREACHVEEDGE((&sE), e, n) e->v1->e0 = e->v2->e0 = e; + + int i; + void **v_info = NULL, **e_info = NULL, **t_info = NULL; + if (!keep_ref) + { + v_info = new void *[sV.numels()]; + i=0; FOREACHVVVERTEX((&sV), v, n) v_info[i++] = v->info; + e_info = new void *[sE.numels()]; + i=0; FOREACHVEEDGE((&sE), e, n) e_info[i++] = e->info; + t_info = new void *[sT.numels()]; + i=0; FOREACHVTTRIANGLE((&sT), t, n) t_info[i++] = t->info; + } + + FOREACHVVVERTEX((&sV), v, n) + {nv=newVertex(v); tin->V.appendTail(nv); v->info = nv;} + + FOREACHVEEDGE((&sE), e, n) + {ne=newEdge((Vertex *)e->v1->info, (Vertex *)e->v2->info); tin->E.appendTail(ne); e->info = ne;} + + FOREACHVTTRIANGLE((&sT), t, n) + {nt=newTriangle((Edge *)t->e1->info,(Edge *)t->e2->info,(Edge *)t->e3->info); tin->T.appendTail(nt); t->info = nt; nt->info = t;} + + FOREACHVVVERTEX((&sV), v, n) ((Vertex *)v->info)->e0 = (Edge *)v->e0->info; + + FOREACHVEEDGE((&sE), e, n) + { + ((Edge *)e->info)->t1 = (e->t1 && IS_VISITED(e->t1))?((Triangle *)e->t1->info):(NULL); + ((Edge *)e->info)->t2 = (e->t2 && IS_VISITED(e->t2))?((Triangle *)e->t2->info):(NULL); + } + + i=0; if (!keep_ref) FOREACHVVVERTEX((&sV), v, n) v->info = v_info[i++]; + i=0; if (!keep_ref) FOREACHVEEDGE((&sE), e, n) e->info = e_info[i++]; + i=0; if (!keep_ref) FOREACHVTTRIANGLE((&sT), t, n) t->info = t_info[i++]; + + FOREACHVTTRIANGLE((&sT), t, n) UNMARK_BIT(t, 5); + FOREACHVEEDGE((&sE), e, n) UNMARK_BIT(e, 5); + FOREACHVVVERTEX((&sV), v, n) UNMARK_BIT(v, 5); + + if (!sT.numels()) {delete(tin); return NULL;} + + tin->duplicateNonManifoldVertices(); + tin->eulerUpdate(); + + return tin; +} + +Basic_TMesh *Basic_TMesh::createSubMeshFromTriangle(Triangle *t0) +{ + Basic_TMesh *ntin = newObject("triangle"); + + Vertex *v1 = (Vertex *)ntin->V.head()->data; + Vertex *v2 = (Vertex *)ntin->V.head()->next()->data; + Vertex *v3 = (Vertex *)ntin->V.head()->next()->next()->data; + v1->setValue(t0->v1()); + v2->setValue(t0->v3()); + v3->setValue(t0->v2()); + ((Triangle *)ntin->T.head()->data)->info = t0->info; + + return ntin; +} + +///// Marks all the triangles within distance L as selected ////// + +int Basic_TMesh::selectSphericalRegion(Triangle *t, const double L, const Point *center) +{ + List *reg = getRegion(t, L, center); + Node *n; + Triangle *s; + int nt=0; + + FOREACHVTTRIANGLE(reg, s, n) {MARK_VISIT(s); nt++;} + delete(reg); + + return nt; +} + + +///// Deselects all the triangles within distance L ////// + +int Basic_TMesh::deselectSphericalRegion(Triangle *t, const double L, const Point *center) +{ + List *reg = getRegion(t, L, center); + Node *n; + Triangle *s; + int nt=0; + + FOREACHVTTRIANGLE(reg, s, n) {UNMARK_VISIT(s); nt++;} + delete(reg); + + return nt; +} + + +///// Selects all the triangles within distance L which were already ////// +///// selected. Deselects the others. ////// + +void Basic_TMesh::reselectSphericalRegion(Triangle *t, const double L, const Point *center) +{ + List *reg = getRegion(t, L, center); + Node *n; + Triangle *s; + + FOREACHVTTRIANGLE(reg, s, n) MARK_VISIT2(s); + FOREACHTRIANGLE(s, n) if (IS_VISITED(s) && !IS_VISITED2(s)) UNMARK_VISIT(s); + FOREACHVTTRIANGLE(reg, s, n) UNMARK_VISIT2(s); + delete(reg); +} + + +//// Remove all the selected triangles and re-triangulate ///// + +bool Basic_TMesh::retriangulateSelectedRegion() +{ + List ttbr; + Node *n; + Triangle *u; + Point nor; + FOREACHTRIANGLE(u, n) if (IS_VISITED(u)) + {ttbr.appendHead(u); nor = nor+(u->getNormal()*u->area());} + + if (ttbr.numels() < 2) + { + TMesh::warning("retriangulateRegion: Nothing to retriangulate.\n"); + return 0; + } + + FOREACHVTTRIANGLE((&(ttbr)), u, n) + if (u->getNormal()*nor <= 0.0) + { + TMesh::warning("retriangulateRegion: Too complex geometry. Can't retriangulate.\n"); + return 0; + } + + if (!isSelectionSimple(&ttbr)) + { + TMesh::warning("retriangulateRegion: Non-simple region. Can't retriangulate.\n"); + return 0; + } + + List *ms = getRegionInternalVertices(&ttbr); + + FOREACHVTTRIANGLE((&(ttbr)), u, n) unlinkTriangle(u); + Edge *e = ((Edge *)ms->head()->data); + List *vl = ((List *)ms->head()->next()->data); + TriangulateHole(e, vl); + delete(vl); + delete(ms); + removeUnlinkedElements(); + + return 1; +} + + +////////// Check wether 's' represents a simple selection //////// + +bool Basic_TMesh::isSelectionSimple(List *s) +{ + if (!s->numels()) return 0; // Empty region is not simple + + Node *n; + Triangle *ta, *t = (Triangle *)s->head()->data; + List bdr, top(t); + MARK_VISIT2(t); + int nv=0; + + while (top.numels()) + { + t = (Triangle *)top.popHead(); + nv++; + ta=t->t1(); if (ta && IS_VISITED(ta) && !IS_VISITED2(ta)) {MARK_VISIT2(ta); top.appendHead(ta);} + else if (ta == NULL) break; else if (!IS_VISITED(ta)) bdr.appendHead(t->e1); + ta=t->t2(); if (ta && IS_VISITED(ta) && !IS_VISITED2(ta)) {MARK_VISIT2(ta); top.appendHead(ta);} + else if (ta == NULL) break; else if (!IS_VISITED(ta)) bdr.appendHead(t->e2); + ta=t->t3(); if (ta && IS_VISITED(ta) && !IS_VISITED2(ta)) {MARK_VISIT2(ta); top.appendHead(ta);} + else if (ta == NULL) break; else if (!IS_VISITED(ta)) bdr.appendHead(t->e3); + } + + FOREACHVTTRIANGLE(s, t, n) UNMARK_VISIT2(t); + if (top.numels()) return 0; // Mesh-boundary in selection + if (nv != s->numels()) return 0; // Disconnected selection + + Edge *e, *f, *ge=NULL, *e0; + List *ve; + FOREACHVEEDGE((&(bdr)), e, n) MARK_VISIT(e); + int nae; + + nv = 0; + e = e0 = (Edge *)bdr.head()->data; + Vertex *v = e->v1; + + do + { + nv++; + v = e->oppositeVertex(v); + ve = v->VE(); + nae=0; FOREACHVEEDGE(ve, f, n) if (f!=e && IS_VISITED(f)) {ge=f; nae++;} + delete(ve); + if (nae > 1) break; + e=ge; + } while (e != e0); + + FOREACHVEEDGE((&(bdr)), e, n) UNMARK_VISIT(e); + if (nv != bdr.numels()) return 0; // Non-simple selection + + return 1; +} + + +//// Unmarks all the elements of the triangulation //// +//// but leaves selected triangles marked. //// + +void Basic_TMesh::unmarkEverythingButSelections() +{ + Vertex *v; + Edge *e; + Triangle *t; + Node *n; + FOREACHVERTEX(v, n) v->mask = 0; + FOREACHEDGE(e, n) e->mask = 0; + FOREACHTRIANGLE(t, n) t->mask &= (unsigned char)1; +} + + +//// Selects all the triangles of the shell containing 't' //// + +int Basic_TMesh::selectConnectedComponent(Triangle *t0, bool sos) +{ + List todo; + Triangle *t, *t1, *t2, *t3; + int ns = 0; + + todo.appendHead(t0); + while (todo.numels()) + { + t = (Triangle *)todo.popHead(); + if (!IS_VISITED(t)) + { + t1 = t->t1(); t2 = t->t2(); t3 = t->t3(); + + if (t1 != NULL && !IS_VISITED(t1) && (!(sos && IS_SHARPEDGE(t->e1)))) todo.appendHead(t1); + if (t2 != NULL && !IS_VISITED(t2) && (!(sos && IS_SHARPEDGE(t->e2)))) todo.appendHead(t2); + if (t3 != NULL && !IS_VISITED(t3) && (!(sos && IS_SHARPEDGE(t->e3)))) todo.appendHead(t3); + + MARK_VISIT(t); ns++; + } + } + + return ns; +} + + +//// Deselects all the triangles of the shell containing 't' //// + +int Basic_TMesh::deselectConnectedComponent(Triangle *t0, bool sos) +{ + List todo; + Triangle *t, *t1, *t2, *t3; + int ns = 0; + + todo.appendHead(t0); + while (todo.numels()) + { + t = (Triangle *)todo.popHead(); + if (IS_VISITED(t)) + { + t1 = t->t1(); t2 = t->t2(); t3 = t->t3(); + + if (t1 != NULL && IS_VISITED(t1) && (!(sos && IS_SHARPEDGE(t->e1)))) todo.appendHead(t1); + if (t2 != NULL && IS_VISITED(t2) && (!(sos && IS_SHARPEDGE(t->e2)))) todo.appendHead(t2); + if (t3 != NULL && IS_VISITED(t3) && (!(sos && IS_SHARPEDGE(t->e3)))) todo.appendHead(t3); + + UNMARK_VISIT(t); ns++; + } + } + + return ns; +} + + +// Append to the current mesh a copy of all the elements of 'src'. +// The newly created elements form a new selection. + +void Basic_TMesh::append(Basic_TMesh *src) +{ + deselectTriangles(); + Basic_TMesh cb(src); + cb.invertSelection(); + V.joinTailList(&(cb.V)); + E.joinTailList(&(cb.E)); + T.joinTailList(&(cb.T)); + d_boundaries = d_handles = d_shells = 1; +} + + +// Move all the elements of 't' to this mesh and delete 't' itself. +void Basic_TMesh::moveMeshElements(Basic_TMesh *t, bool delInput) +{ + V.joinTailList(&(t->V)); + E.joinTailList(&(t->E)); + T.joinTailList(&(t->T)); + d_boundaries = d_handles = d_shells = 1; + if(delInput) delete t; +} + + +////////////////////////////////////////////////////////////////// +// // +// R E G I O N M A N I P U L A T I O N // +// // +////////////////////////////////////////////////////////////////// + +///// Make a list with all the triangles within distance L ////// + +List *Basic_TMesh::getRegion(Triangle *t, const double L, const Point *center) +{ + List triList, *toRemove = new List; + if (t->v1()->distance(center) > L) return toRemove; + if (t->v2()->distance(center) > L) return toRemove; + if (t->v3()->distance(center) > L) return toRemove; + + Triangle *s; + Node *n; + + triList.appendHead(t); + MARK_BIT(t,3); + + while(triList.numels() > 0) + { + t = (Triangle *)triList.head()->data; + triList.removeCell(triList.head()); + toRemove->appendHead(t); + + if ((s = t->t1()) != NULL && !IS_BIT(s,3) && s->oppositeVertex(t->e1)->distance(center) <= L) + {triList.appendHead(s); MARK_BIT(s,3);} + if ((s = t->t2()) != NULL && !IS_BIT(s,3) && s->oppositeVertex(t->e2)->distance(center) <= L) + {triList.appendHead(s); MARK_BIT(s,3);} + if ((s = t->t3()) != NULL && !IS_BIT(s,3) && s->oppositeVertex(t->e3)->distance(center) <= L) + {triList.appendHead(s); MARK_BIT(s,3);} + } + + FOREACHVTTRIANGLE(toRemove, s, n) UNMARK_BIT(s, 3); + + return toRemove; +} + + +///// Unlink all the triangles within distance L ////// + +void Basic_TMesh::removeRegion(Triangle *t, const double L, const Point *center) +{ + List triList, toRemove; + Node *n; + Triangle *s; + + triList.appendHead(t); + MARK_VISIT(t); + + while(triList.numels() > 0) + { + t = (Triangle *)triList.head()->data; + triList.removeCell(triList.head()); + toRemove.appendHead(t); + + if ((s = t->t1()) != NULL && !IS_VISITED(s) && s->oppositeVertex(t->e1)->distance(center) <= L) + {triList.appendHead(s); MARK_VISIT(s);} + if ((s = t->t2()) != NULL && !IS_VISITED(s) && s->oppositeVertex(t->e2)->distance(center) <= L) + {triList.appendHead(s); MARK_VISIT(s);} + if ((s = t->t3()) != NULL && !IS_VISITED(s) && s->oppositeVertex(t->e3)->distance(center) <= L) + {triList.appendHead(s); MARK_VISIT(s);} + } + + for (n = toRemove.tail(); n != NULL; n=n->prev()) + { + s = ((Triangle *)n->data); + unlinkTriangle(s); + } + + removeUnlinkedElements(); +} + + +//////// Next region's boundary vertex ///////////// + +Vertex *Basic_TMesh::nextVertexOnRegionBoundary(Vertex *sv) const +{ + Triangle *lt, *rt; + Edge *e; + List *ve = sv->VE(); + Node *n; + + FOREACHVEEDGE(ve, e, n) + { + lt = e->leftTriangle(sv); + rt = e->rightTriangle(sv); + if (lt != NULL && IS_VISITED(lt) && (rt == NULL || !IS_VISITED(rt))) + {delete(ve); return e->oppositeVertex(sv);} + } + delete(ve); + + return NULL; +} + + +//// This method returns a list containing an edge of the region's boundary //// +//// as its first element, and all the internal vertices as the remaining //// + +List *Basic_TMesh::getRegionInternalVertices(List *reg) +{ + List *iVertices = new List; + List *outList = new List; + Edge *bEdge = NULL; + Triangle *s, *t; + Node *n; + Vertex *v1, *v2, *v3; + + FOREACHVTTRIANGLE(reg, t, n) {MARK_VISIT(t); MARK_BIT(t, 3);} + + FOREACHVTTRIANGLE(reg, t, n) + { + if (IS_BIT(t,3)) + { + UNMARK_BIT(t,3); + if ((s = t->t1()) != NULL && !IS_VISITED(s)) {bEdge = t->e1; MARK_BIT(t->e1->v1, 3); MARK_BIT(t->e1->v2, 3);} + if ((s = t->t2()) != NULL && !IS_VISITED(s)) {bEdge = t->e2; MARK_BIT(t->e2->v1, 3); MARK_BIT(t->e2->v2, 3);} + if ((s = t->t3()) != NULL && !IS_VISITED(s)) {bEdge = t->e3; MARK_BIT(t->e3->v1, 3); MARK_BIT(t->e3->v2, 3);} + } + } + + FOREACHVTTRIANGLE(reg, s, n) + { + v1 = s->v1(); v2 = s->v2(); v3 = s->v3(); + if (!IS_BIT(v1, 3)) {iVertices->appendHead(v1); MARK_BIT(v1, 3);} + if (!IS_BIT(v2, 3)) {iVertices->appendHead(v2); MARK_BIT(v2, 3);} + if (!IS_BIT(v3, 3)) {iVertices->appendHead(v3); MARK_BIT(v3, 3);} + } + FOREACHVTTRIANGLE(reg, s, n) + { + v1 = s->v1(); v2 = s->v2(); v3 = s->v3(); + UNMARK_BIT(v1, 3); UNMARK_BIT(v2, 3); UNMARK_BIT(v3, 3); + } + + outList->appendHead(iVertices); + outList->appendHead(bEdge); + + return outList; +} + + +// Transforms only the shell indicated by 't0' + +void Basic_TMesh::transformShell(Triangle *t0, const Matrix4x4& m) +{ + List todo(t0), st, sv; + Triangle *t, *nt; + Vertex *v; + coord x, y, z, w; + + while (todo.numels()) + { + t = (Triangle *)todo.popHead(); + st.appendHead(t); + nt=t->t1(); if (nt != NULL && !IS_VISITED(nt)) {MARK_VISIT(nt); todo.appendHead(nt);} + nt=t->t2(); if (nt != NULL && !IS_VISITED(nt)) {MARK_VISIT(nt); todo.appendHead(nt);} + nt=t->t3(); if (nt != NULL && !IS_VISITED(nt)) {MARK_VISIT(nt); todo.appendHead(nt);} + } + + while (st.numels()) + { + t = (Triangle *)st.popHead(); + UNMARK_VISIT(t); + v = t->v1(); if (!IS_VISITED(v)) {MARK_VISIT(v); sv.appendHead(v);} + v = t->v2(); if (!IS_VISITED(v)) {MARK_VISIT(v); sv.appendHead(v);} + v = t->v3(); if (!IS_VISITED(v)) {MARK_VISIT(v); sv.appendHead(v);} + } + + while (sv.numels()) + { + v = (Vertex *)sv.popHead(); + UNMARK_VISIT(v); + x = ((*v)*Point(m.matrix[0][0],m.matrix[1][0],m.matrix[2][0]))+m.matrix[3][0]; + y = ((*v)*Point(m.matrix[0][1],m.matrix[1][1],m.matrix[2][1]))+m.matrix[3][1]; + z = ((*v)*Point(m.matrix[0][2],m.matrix[1][2],m.matrix[2][2]))+m.matrix[3][2]; + w = ((*v)*Point(m.matrix[0][3],m.matrix[1][3],m.matrix[2][3]))+m.matrix[3][3]; + v->x = x/w; v->y = y/w; v->z = z/w; + } +} + + +//// Removes all the triangles of the shell containing 't0' //// + +void Basic_TMesh::removeShell(Triangle *t0) +{ + List todo(t0); + Triangle *t, *t1, *t2, *t3; + + while (todo.numels()) + { + t = (Triangle *)todo.popHead(); + t1 = t->t1(); t2 = t->t2(); t3 = t->t3(); + + if (t1 != NULL && !IS_VISITED2(t1)) {MARK_VISIT2(t1); todo.appendHead(t1);} + if (t2 != NULL && !IS_VISITED2(t2)) {MARK_VISIT2(t2); todo.appendHead(t2);} + if (t3 != NULL && !IS_VISITED2(t3)) {MARK_VISIT2(t3); todo.appendHead(t3);} + + unlinkTriangle(t); + } + + removeUnlinkedElements(); +} + + +////////////////////////////////////////////////////////////////// +// // +// G L O B A L O P E R A T I O N S // +// // +////////////////////////////////////////////////////////////////// + + +/////// Tags as sharp all the edges exceeding the given curvature /////////// + +void Basic_TMesh::sharpEdgeTagging(const double ta) +{ + Node *n; + Edge *e; + FOREACHEDGE(e, n) + if (e->curvature() > ta) TAG_SHARPEDGE(e); + else UNTAG_SHARPEDGE(e); +} + + +//// Unmarks all the elements of the triangulation //// + +void Basic_TMesh::unmarkEverything() +{ + Vertex *v; + Edge *e; + Triangle *t; + Node *n; + FOREACHVERTEX(v, n) v->mask = 0; + FOREACHEDGE(e, n) e->mask = 0; + FOREACHTRIANGLE(t, n) t->mask = 0; +} + + +///// Compute the bounding box and return its max edge ///// + +coord Basic_TMesh::getBoundingBox(Point& mp, Point& Mp) const +{ + Vertex *v; Node *n; + Mp.x = -DBL_MAX, mp.x = DBL_MAX; + Mp.y = -DBL_MAX, mp.y = DBL_MAX; + Mp.z = -DBL_MAX, mp.z = DBL_MAX; + FOREACHVERTEX(v, n) + { + if (v->x < mp.x) mp.x = v->x; + if (v->x > Mp.x) Mp.x = v->x; + if (v->y < mp.y) mp.y = v->y; + if (v->y > Mp.y) Mp.y = v->y; + if (v->z < mp.z) mp.z = v->z; + if (v->z > Mp.z) Mp.z = v->z; + } + + return MAX(Mp.x-mp.x,MAX(Mp.y-mp.y,Mp.z-mp.z)); +} + + +///// Compute the approximate bounding ball radius ///// + +double Basic_TMesh::getBoundingBallRadius() const +{ + Vertex *v; Node *n; + Point tc, mp, Mp; + double tb, bsr = TMESH_TO_DOUBLE(getBoundingBox(mp, Mp)) / 2; + Point bsc = (Mp+mp)/2; + + FOREACHVERTEX(v, n) + if ((tb = ((*v)-bsc).length()) > bsr) + { + tc = ((*v)-bsc); tc.normalize(); + tb = ((tb-bsr)/2); + bsc = bsc+(tc*tb); + bsr += tb; + } + + return bsr; +} + + +////// Returns the surface area of the mesh ////// + +double Basic_TMesh::area() const +{ + Triangle *t; + Node *n; + double a=0.0; + FOREACHTRIANGLE(t, n) a += t->area(); + + return a; +} + + +////// Returns the volume of the mesh ////// + +double Basic_TMesh::volume() const +{ + Triangle *t; + Node *n; + double v=0.0; + FOREACHTRIANGLE(t, n) + v += TMESH_TO_DOUBLE((t->getCenter()*t->getNormal()))*t->area(); + + return v/3; +} + + +///// Places the mesh into the unit cube by translating and resizing ///// +///// so that all the coordinates are between 0 and mc (default =1). ///// + +void Basic_TMesh::normalize(coord mc) +{ + Vertex *v; + Node *n; + Point mp, Mp; + coord mel = getBoundingBox(mp, Mp) / mc; + FOREACHVERTEX(v, n) v->setValue(((*v) - mp) / mel); // Shift and normalize +} + +void Basic_TMesh::quantize(int nc) +{ + Vertex *v; + Node *n; + normalize(nc); + bool stat = TMesh::isUsingRationals(); + TMesh::useRationals(false); + FOREACHVERTEX(v, n) + { + v->x = coord(TMESH_TO_INT(v->x)); + v->y = coord(TMESH_TO_INT(v->y)); + v->z = coord(TMESH_TO_INT(v->z)); + } + TMesh::useRationals(stat); +} + + +/////// Transforms all the vertices using the 4x4 matrix 'm' /////////// + +void Basic_TMesh::transform(const Matrix4x4& m) +{ + Node *n; + Vertex *v; + coord x,y,z,w; + + FOREACHVERTEX(v, n) + { + x = ((*v)*Point(m.matrix[0][0],m.matrix[1][0],m.matrix[2][0]))+m.matrix[3][0]; + y = ((*v)*Point(m.matrix[0][1],m.matrix[1][1],m.matrix[2][1]))+m.matrix[3][1]; + z = ((*v)*Point(m.matrix[0][2],m.matrix[1][2],m.matrix[2][2]))+m.matrix[3][2]; + w = ((*v)*Point(m.matrix[0][3],m.matrix[1][3],m.matrix[2][3]))+m.matrix[3][3]; + v->x = x/w; v->y = y/w; v->z = z/w; + } +} + +// Translate the whole mesh by t_vec +void Basic_TMesh::translate(const Point& t_vec) +{ + Vertex *v; + Node *i; + FOREACHVERTEX(v, i) v->setValue((*v)+t_vec); +} + +// Return the center of mass of the mesh +Point Basic_TMesh::getCenter() const +{ + Point c; + Triangle *t; + Node *i; + coord av, tvol=0.0; + FOREACHTRIANGLE(t, i) + { + av = t->area(); + tvol += av; + c += (t->getCenter()*av); + } + + return c/tvol; +} + +// Add noise in the normal direction. Normal displacement is +// bounded by ns% of the bounding ball radius. + +void Basic_TMesh::addNormalNoise(double ns) +{ + Vertex *v; + Node *n; + Point np; + int i; + double noise; + coord *xyz = (coord *)malloc(sizeof(coord)*V.numels()*3); + ns *= (getBoundingBallRadius()/100.0); + + i=0; FOREACHVERTEX(v, n) + { + noise = ns*(((((double)rand()))-(((double)RAND_MAX)/2.0))/((double)RAND_MAX)); + np = (*v)+((v->getNormal())*noise); + xyz[i++]=np.x; xyz[i++]=np.y; xyz[i++]=np.z; + } + i=0; FOREACHVERTEX(v, n) {v->x=xyz[i++]; v->y=xyz[i++]; v->z=xyz[i++];} + + free(xyz); +} + + +// Iteratively swap edges to maximize the minimum angle. +// Checks and avoids normal inversion. + +bool Basic_TMesh::iterativeEdgeSwaps() +{ + Node *n; + Edge *e, *f; + double l; + int swaps=1, totits=1; + Point n1, n2, nor; + List toswap; + + bool selection=0; + Triangle *t; + FOREACHTRIANGLE(t, n) if (IS_VISITED(t)) {selection=1; break;} + + FOREACHEDGE(e, n) if (!IS_SHARPEDGE(e) && !e->isOnBoundary()) + { + MARK_VISIT(e); if ((!selection || (IS_VISITED(e->t1) && IS_VISITED(e->t2)))) toswap.appendTail(e); + } + + TMesh::begin_progress(); + while (swaps && totits++ < 10) + { + swaps = 0; for (n=toswap.head(); n!=NULL; ) + { + e = (Edge *)n->data; + if (n==toswap.tail()) {toswap.removeCell(toswap.tail()); n=NULL;} + else {n=n->next(); toswap.removeCell(n->prev());} + UNMARK_VISIT(e); + + n1 = e->t1->getNormal(); + n2 = e->t2->getNormal(); + nor = n1+n2; + l = e->delaunayMinAngle(); + if (e->swap()) + { + if (e->delaunayMinAngle() <= l*1.000001 || nor*e->t1->getNormal() <= 0 || nor*e->t2->getNormal() <= 0) e->swap(1); + else + { + swaps++; + f = e->t1->nextEdge(e); if (!IS_VISITED(f) && !IS_SHARPEDGE(f) && !f->isOnBoundary()) {MARK_VISIT(f); toswap.appendHead(f);} + f = e->t1->prevEdge(e); if (!IS_VISITED(f) && !IS_SHARPEDGE(f) && !f->isOnBoundary()) {MARK_VISIT(f); toswap.appendHead(f);} + f = e->t2->nextEdge(e); if (!IS_VISITED(f) && !IS_SHARPEDGE(f) && !f->isOnBoundary()) {MARK_VISIT(f); toswap.appendHead(f);} + f = e->t2->prevEdge(e); if (!IS_VISITED(f) && !IS_SHARPEDGE(f) && !f->isOnBoundary()) {MARK_VISIT(f); toswap.appendHead(f);} + } + } + + } + TMesh::report_progress("Swaps: %d ", swaps); + } + TMesh::end_progress(); + + FOREACHEDGE(e, n) UNMARK_VISIT(e); + + if (totits >= 10) + { + TMesh::warning("Optimization did not converge after 10 iterations! Stopping.\n"); + TMesh::warning("You may try to run the method again.\n"); + return 0; + } + + return 1; +} + + +////////////////////////////////////////////////////////////////// +// // +// T O P O L O G Y M A N I P U L A T I O N // +// // +////////////////////////////////////////////////////////////////// + + +////// Invertes all the triangle normals and edge orientations /////// + +void Basic_TMesh::flipNormals() +{ + Node *n; + Edge *e; + Triangle *t; + + FOREACHTRIANGLE(t, n) t->invert(); + FOREACHEDGE(e, n) p_swap((void **)(&(e->v1)), (void **)(&(e->v2))); //!< AMF_CHANGE 1.1-2 > +} + + +//// Marks all the triangles of the shell containing 't' //// + +void Basic_TMesh::flipNormals(Triangle *t0) +{ + List todo; + Triangle *t, *t1, *t2, *t3; + + todo.appendHead(t0); + while (todo.numels()) + { + t = (Triangle *)todo.popHead(); + if (!IS_BIT(t,6)) + { + t1 = t->t1(); t2 = t->t2(); t3 = t->t3(); + + if (t1 != NULL && !IS_BIT(t1,6)) todo.appendHead(t1); + if (t2 != NULL && !IS_BIT(t2,6)) todo.appendHead(t2); + if (t3 != NULL && !IS_BIT(t3,6)) todo.appendHead(t3); + + t->invert(); + if (!IS_BIT(t->e1,6)) p_swap((void **)(&(t->e1->v1)), (void **)(&(t->e1->v2))); + if (!IS_BIT(t->e2,6)) p_swap((void **)(&(t->e2->v1)), (void **)(&(t->e2->v2))); + if (!IS_BIT(t->e3,6)) p_swap((void **)(&(t->e3->v1)), (void **)(&(t->e3->v2))); + MARK_BIT(t->e1,6); MARK_BIT(t->e2,6); MARK_BIT(t->e3,6); + MARK_BIT(t,6); + } + } + + todo.appendHead(t0); + while (todo.numels()) + { + t = (Triangle *)todo.popHead(); + if (IS_BIT(t,6)) + { + t1 = t->t1(); t2 = t->t2(); t3 = t->t3(); + + if (t1 != NULL && IS_BIT(t1,6)) todo.appendHead(t1); + if (t2 != NULL && IS_BIT(t2,6)) todo.appendHead(t2); + if (t3 != NULL && IS_BIT(t3,6)) todo.appendHead(t3); + + UNMARK_BIT(t->e1,6); UNMARK_BIT(t->e2,6); UNMARK_BIT(t->e3,6); + UNMARK_BIT(t,6); + } + } +} + + +//// Returns the top triangle of the mesh (max. z) //// + +Triangle *Basic_TMesh::topTriangle(Triangle *t0) +{ + Node *n; + Vertex *v, *hv = NULL, *v1, *v2, *v3; + Edge *e, *fe = NULL; + coord az, Mz = -DBL_MAX; + Triangle *t, *t1, *t2, *t3; + List *ve, todo, tlist, elist, vlist; + + todo.appendHead(t0); MARK_BIT(t0,2); + + while (todo.numels()) + { + t = (Triangle *)todo.popHead(); tlist.appendHead(t); + t1 = t->t1(); t2 = t->t2(); t3 = t->t3(); + v1 = t->v1(); v2 = t->v2(); v3 = t->v3(); + if (!IS_VISITED(v1)) {MARK_VISIT(v1); vlist.appendHead(v1);} + if (!IS_VISITED(v2)) {MARK_VISIT(v2); vlist.appendHead(v2);} + if (!IS_VISITED(v3)) {MARK_VISIT(v3); vlist.appendHead(v3);} + + if (!IS_VISITED(t->e1)) {MARK_VISIT(t->e1); elist.appendHead(t->e1);} + if (!IS_VISITED(t->e2)) {MARK_VISIT(t->e2); elist.appendHead(t->e2);} + if (!IS_VISITED(t->e3)) {MARK_VISIT(t->e3); elist.appendHead(t->e3);} + + if (t1 != NULL && !IS_BIT(t1,2)) {MARK_BIT(t1,2); todo.appendHead(t1);} + if (t2 != NULL && !IS_BIT(t2,2)) {MARK_BIT(t2,2); todo.appendHead(t2);} + if (t3 != NULL && !IS_BIT(t3,2)) {MARK_BIT(t3,2); todo.appendHead(t3);} + } + + ve = new List; + + FOREACHVVVERTEX((&(vlist)), v, n) {UNMARK_VISIT(v); if ((az = v->z) > Mz) {Mz=az; hv = v;}} + Mz = DBL_MAX; + FOREACHVEEDGE((&(elist)), e, n) {UNMARK_VISIT(e); if (e->hasVertex(hv) && e->length() != 0) ve->appendHead(e);} + FOREACHVTTRIANGLE((&(tlist)), t, n) UNMARK_BIT(t, 2); + + FOREACHVEEDGE(ve, e, n) + if ((az = (hv->z - e->oppositeVertex(hv)->z)/e->length()) < Mz) {Mz=az; fe = e;} + delete(ve); + + if (fe == NULL) fe = hv->e0; + if (fe->t1 == NULL || fe->t2 == NULL) return NULL; + + return (FABS(fe->t1->getNormal().z) > FABS(fe->t2->getNormal().z))?(fe->t1):(fe->t2); +} + + +/////// Computes boundaries and handles /////////// + +void Basic_TMesh::eulerUpdate() +{ + Vertex *v, *w; + Edge *e; + Triangle *t, *s; + List triList; + Node *n; + n_boundaries = n_shells = n_handles = 0; + + FOREACHTRIANGLE(t, n) UNMARK_BIT(t, 5); + FOREACHVERTEX(v, n) UNMARK_BIT(v, 5); + + FOREACHTRIANGLE(t, n) if (!IS_BIT(t, 5)) + { + n_shells++; + triList.appendHead(t); + MARK_BIT(t, 5); + + while (triList.numels()) + { + t = (Triangle *)triList.popHead(); + if ((s = t->t1()) != NULL && !IS_BIT(s, 5)) { triList.appendHead(s); MARK_BIT(s, 5); } + if ((s = t->t2()) != NULL && !IS_BIT(s, 5)) { triList.appendHead(s); MARK_BIT(s, 5); } + if ((s = t->t3()) != NULL && !IS_BIT(s, 5)) { triList.appendHead(s); MARK_BIT(s, 5); } + } + } + FOREACHTRIANGLE(t, n) UNMARK_BIT(t, 5); + + bool hasBoundary = false; + + FOREACHEDGE(e, n) if (e->isOnBoundary()){ + hasBoundary = true; + MARK_BIT(e->v1, 5); + MARK_BIT(e->v2, 5); + } + + if (hasBoundary){ + FOREACHVERTEX(v, n) if (IS_BIT(v, 5)) + { + n_boundaries++; + for (w = v; IS_BIT(w, 5); w = w->nextOnBoundary()) UNMARK_BIT(w, 5); + } + } + + n_handles = (E.numels() - V.numels() - T.numels() + 2 * n_shells - n_boundaries) / 2; + d_boundaries = d_handles = d_shells = 0; +} + + +// Makes the mesh equivalent to a topological disk. +// Edges and vertices are duplicated when necessary. + +void Basic_TMesh::openToDisk() +{ + Triangle *t = (Triangle *)T.head()->data; + Triangle *s; + List triList, *ve; + Vertex *v, *w; + Edge *e, *ne; + Node *n; + triList.appendHead(t); + MARK_BIT(t,3); + + while(triList.numels()) + { + t = (Triangle *)triList.popHead(); + if ((s = t->t1()) != NULL && !IS_BIT(s,3)) + {triList.appendTail(s); MARK_BIT(s,3); MARK_BIT(t->e1,3);} + if ((s = t->t2()) != NULL && !IS_BIT(s,3)) + {triList.appendTail(s); MARK_BIT(s,3); MARK_BIT(t->e2,3);} + if ((s = t->t3()) != NULL && !IS_BIT(s,3)) + {triList.appendTail(s); MARK_BIT(s,3); MARK_BIT(t->e3,3);} + } + FOREACHTRIANGLE (t, n) UNMARK_BIT(t, 3); + + FOREACHVERTEX(v, n) v->info = new List; + + FOREACHEDGE(e, n) if (!IS_BIT(e, 3)) + { + ((List *)e->v1->info)->appendHead(e); + ((List *)e->v2->info)->appendHead(e); + } + + FOREACHVERTEX(v, n) if (((List *)v->info)->numels()==1) triList.appendHead(v); + if (!triList.numels()) TMesh::error("Basic_TMesh::openToDisk: Couldn't find a root.\n"); + + while(triList.numels()) + { + v = (Vertex *)triList.popHead(); + ve = ((List *)v->info); + if (ve->numels()) + { + e = (Edge *)(ve->head()->data); + MARK_BIT(e, 3); + ve->popHead(); + w = e->oppositeVertex(v); + ve = ((List *)w->info); + ve->removeNode(e); + if (ve->numels() == 1) triList.appendHead(w); + } + else + { + ve = v->VE(); + e = (Edge *)ve->head()->data; UNMARK_BIT(e, 3); ((List *)v->info)->appendHead(e); + e = (Edge *)ve->head()->next()->data; UNMARK_BIT(e, 3); ((List *)v->info)->appendHead(e); + delete(ve); + } + } + + FOREACHEDGE(e, n) if (!IS_BIT(e, 3) && !e->isOnBoundary()) + { + ne = newEdge(e->v1, e->v2); + ne->t1 = e->t1; e->t1 = NULL; E.appendHead(ne); + ne->t1->replaceEdge(e, ne); + } + + FOREACHEDGE(e, n) UNMARK_BIT(e, 3); + + FOREACHVERTEX(v, n) if (v->info) {delete(((List *)v->info)); v->info = NULL;} + duplicateNonManifoldVertices(); + d_boundaries = d_handles = d_shells = 1; +} + + +//// Splits the edge 'e' by the point 'p'. //// + +Vertex *Basic_TMesh::splitEdge(Edge *e, Point *p, bool copy_mask) +{ + if ((*p)==(*(e->v1))) return e->v1; + if ((*p)==(*(e->v2))) return e->v2; + Vertex *v3 = (e->t1 != NULL)?(e->t1->oppositeVertex(e)):(NULL); + Vertex *v4 = (e->t2 != NULL)?(e->t2->oppositeVertex(e)):(NULL); + Edge *be1 = (e->t1 != NULL)?(e->t1->nextEdge(e)):(NULL); + Edge *be4 = (e->t2 != NULL)?(e->t2->prevEdge(e)):(NULL); + Vertex *v = newVertex(p->x, p->y, p->z); + Edge *ne = newEdge(v, e->v2); + Edge *ne1 = (e->t1 != NULL)?(newEdge(v, v3)):(NULL); + Edge *ne2 = (e->t2 != NULL)?(newEdge(v, v4)):(NULL); + Triangle *nt1 = (e->t1 != NULL)?(newTriangle(ne1, ne,be1)):(NULL); + Triangle *nt2 = (e->t2 != NULL)?(newTriangle(ne, ne2,be4)):(NULL); + + ne->t1 = nt1; ne->t2 = nt2; + if (ne1 != NULL) {ne1->t1 = e->t1; ne1->t2 = nt1;} + if (ne2 != NULL) {ne2->t1 = nt2; ne2->t2 = e->t2;} + if (be1 != NULL) be1->replaceTriangle(e->t1, nt1); + if (be4 != NULL) be4->replaceTriangle(e->t2, nt2); + e->v2->e0 = (be1 != NULL)?(be1):(be4); + e->v2 = v; + v->e0 = e; + if (e->t1 != NULL) e->t1->replaceEdge(be1, ne1); + if (e->t2 != NULL) e->t2->replaceEdge(be4, ne2); + + if (copy_mask) + { + ne->mask = e->mask; + if (nt1 != NULL) nt1->mask = e->t1->mask; + if (nt2 != NULL) nt2->mask = e->t2->mask; + } + + V.appendHead(v); + E.appendHead(ne); + if (ne1 != NULL) E.appendHead(ne1); + if (ne2 != NULL) E.appendHead(ne2); + if (nt1 != NULL) T.appendHead(nt1); + if (nt2 != NULL) T.appendHead(nt2); + + return v; +} + + +//// Splits the trianlge 't' by the point 'p'. If 'corr' is TRUE //// +//// the method does not perform the split if this causes the //// +//// creation of a triangle with a vertex angle < MIN_ANG_TRI. //// + +Vertex *Basic_TMesh::splitTriangle(Triangle *t, Point *p, bool copy_mask) +{ + Vertex *v1 = t->v1(); + Vertex *v2 = t->v2(); + Vertex *v3 = t->v3(); + + Vertex *v = newVertex(p->x, p->y, p->z); + Edge *ne1 = newEdge(v, v1); + Edge *ne2 = newEdge(v, v2); + Edge *ne3 = newEdge(v, v3); + Triangle *nt1 = newTriangle(ne2, t->e3,ne3); + Triangle *nt2 = newTriangle(ne3, t->e1,ne1); + t->e3->replaceTriangle(t, nt1); + t->e1->replaceTriangle(t, nt2); + t->replaceEdge(t->e3, ne2); + t->replaceEdge(t->e1, ne1); + ne1->t1 = t; ne1->t2 = nt2; + ne2->t1 = nt1; ne2->t2 = t; + ne3->t1 = nt2; ne3->t2 = nt1; + v->e0 = ne1; + + V.appendHead(v); + E.appendHead(ne1); + E.appendHead(ne2); + E.appendHead(ne3); + T.appendHead(nt1); + T.appendHead(nt2); + + if (copy_mask) + { + nt1->mask = t->mask; + nt2->mask = t->mask; + } + + return v; +} + + +////////////////////////////////////////////////////////////////// +// // +// D E B U G AND WORK-IN-PROGRESS // +// // +////////////////////////////////////////////////////////////////// + + +/////// Prints general information /////////// + +void Basic_TMesh::printReport() +{ + eulerUpdate(); + + TMesh::info("*** Basic_TMesh Report ***\n"); + TMesh::info("V: %d\n",V.numels()); + TMesh::info("E: %d\n",E.numels()); + TMesh::info("T: %d\n",T.numels()); + + TMesh::info("Boundary: %d components.\n",boundaries()); + TMesh::info("Handles: %d.\n",handles()); + TMesh::info("Shells: %d.\n",shells()); +} + + +// Unless there are bugs, the following should be exact ... + +bool Basic_TMesh::isInnerPoint(Point& p) const +{ + if (T.numels() == 0) return false; // An ampty mesh does not enclose anything ... + + // We assume that the mesh is correctly oriented. + Node *n; + Vertex *v1, *v2, *v3; + Triangle *t; + + // Ray casting along positive X direction + Point p2(1, 0, 0); + p2 += p; + coord ad, min_distance = DBL_MAX; + Point ip; + Triangle *closest_triangle = NULL; + Edge *e, *closest_edge = NULL; + Vertex *closest_vertex = NULL; + + coord o1, o2, o3; + FOREACHTRIANGLE(t, n) + { + v1 = t->v1(); v2 = t->v2(); v3 = t->v3(); + if (((v1->y > p.y && v2->y > p.y) || (v1->y < p.y && v2->y < p.y)) && ((v1->y > p.y && v3->y > p.y) || (v1->y < p.y && v3->y < p.y))) + continue; + if (((v1->z > p.z && v2->z > p.z) || (v1->z < p.z && v2->z < p.z)) && ((v1->z > p.z && v3->z > p.z) || (v1->z < p.z && v3->z < p.z))) + continue; + o1 = orient2D(p.y, p.z, v1->y, v1->z, v2->y, v2->z); + o2 = orient2D(p.y, p.z, v2->y, v2->z, v3->y, v3->z); + o3 = orient2D(p.y, p.z, v3->y, v3->z, v1->y, v1->z); + + if (o1 == 0 && o2 == 0 && o3 == 0) continue; // Degenerate triangle. Skip. + else if (o1 == 0 && o2 == 0) + { + if ((ad = v2->x - p.x) == 0) return false; // Point is on surface + else if (ad > 0 && adx - p.x) == 0) return false; // Point is on surface + else if (ad > 0 && adx - p.x) == 0) return false; // Point is on surface + else if (ad > 0 && ade2) : ((o2 == 0) ? (t->e3) : (t->e1)); + if ((p.y < e->v1->y && p.y < e->v2->y) || (p.y > e->v1->y && p.y > e->v2->y) || + (p.z < e->v1->z && p.z < e->v2->z) || (p.z > e->v1->z && p.z > e->v2->z)) continue; + ip = Point::lineLineIntersection(p, p2, *e->v1, *e->v2); + ad = ip.x - p.x; + if (ad == 0) return false; // Point is on surface + else if (ad > 0 && ad 0 && o2 > 0 && o3 > 0) || (o1 < 0 && o2 < 0 && o3 < 0)) + { + ip = Point::linePlaneIntersection(p, p2, *v1, *v2, *v3); + ad = ip.x - p.x; + if (ad == 0) return false; // Point is on surface + else if (ad > 0 && adVE(); + coord ad, mind = DBL_MAX; + Point a = p - (*closest_vertex); + FOREACHVEEDGE(ve, e, n) + { + Vertex *ov1 = e->oppositeVertex(closest_vertex); + Point b = (*ov1) - (*closest_vertex); + ad = (((a&b)*(a&b)) / ((b*b)*(a*a))) - 1; + if (a*b < 0) ad = -ad; + if (ad < mind) { mind = ad; closest_edge = e; } + } + delete ve; + } + + // If cp is in the interior of an edge, select one of its incident triangles + if (closest_edge != NULL) + { + if (closest_edge->isOnBoundary()) return false; + Vertex *ov1 = closest_edge->t1->oppositeVertex(closest_edge); + Vertex *ov2 = closest_edge->t2->oppositeVertex(closest_edge); + coord o1 = p.exactOrientation(closest_edge->v1, closest_edge->v2, ov1); + coord o2 = ov2->exactOrientation(closest_edge->v1, closest_edge->v2, ov1); + closest_triangle = ((o1 >= 0 && o2 >= 0) || (o1 <= 0 && o2 <= 0)) ? (closest_edge->t2) : (closest_edge->t1); + } + + // If closest point is in the interior of a triangle, just check orientation + if (closest_triangle != NULL) + { + return (closest_triangle->getVector().x > 0); + } + + return false; + + //if (!singular_case) + //{ + // int parity = 0; + // Point p2(1, 0, 0); + // p2 += p; + // FOREACHTRIANGLE(t, n) + // { + // v1 = t->v1(); v2 = t->v2(); v3 = t->v3(); + // if ((IS_BIT(v1, 5) == IS_BIT(v2, 5) && IS_BIT(v1, 5) == IS_BIT(v3, 5))) continue; + // if ((IS_BIT(v1, 6) == IS_BIT(v2, 6) && IS_BIT(v1, 6) == IS_BIT(v3, 6))) continue; + // coord o1 = orient2D(p.y, p.z, v1->y, v1->z, v2->y, v2->z); + // coord o2 = orient2D(p.y, p.z, v2->y, v2->z, v3->y, v3->z); + // coord o3 = orient2D(p.y, p.z, v3->y, v3->z, v1->y, v1->z); + // if (o1 == 0 || o2 == 0 || o3 == 0) { singular_case = true; break; } + // if ((o1 > 0 && o2 > 0 && o3 > 0) || (o1 < 0 && o2 < 0 && o3 < 0)) + // { + // o1 = p.exactOrientation(v1, v2, v3); + // if (o1 == 0) return false; // Point is on triangle + // Point ip = Point::linePlaneIntersection(p, p2, v1, v2, v3); + // if (ip.x>p.x) + // { + // if (o1 < 0) parity++; else if (o1 > 0) parity--; + // } + // } + // } + // if (!singular_case) return (parity == 1); + //} + + // In case of singularity, revert to the following (slower) method + // Pick the closest triangle + //coord ad, min_d = DBL_MAX; + //Triangle *gt; + //FOREACHTRIANGLE(t, n) if ((ad = t->pointTriangleSquaredDistance(&p)) < min_d) { gt = t; min_d = ad; } + + //Edge *closest_edge = NULL; + //Vertex *closest_vertex = NULL; + //gt->pointTriangleSquaredDistance(&p, &closest_edge, &closest_vertex); + + //// If closest point is in the interior of gt, just check orientation + //if (closest_edge == NULL) return (p.exactOrientation(gt->v1(), gt->v2(), gt->v3())<0); + + //// If cp is in the interior of an edge, check edge convexity (e->getConvexity()) + //if (closest_vertex == NULL) return (closest_edge->getConvexity() < 0); + + //// If cp is a vertex, find the triangle in VT whose normal is mostly aligned with (cp-p) and check orientation + //Point normal = closest_vertex->getNormal(); // NOT ROBUST !!! This is an extremely particular case, but ... + //return (((*closest_vertex-p)*normal)>0); +} + +} //namespace T_MESH diff --git a/src/mesh_fix/src/TMesh/triangle.cpp b/src/mesh_fix/src/TMesh/triangle.cpp new file mode 100644 index 000000000..15fed33b7 --- /dev/null +++ b/src/mesh_fix/src/TMesh/triangle.cpp @@ -0,0 +1,410 @@ +/**************************************************************************** +* TMesh * +* * +* Consiglio Nazionale delle Ricerche * +* Istituto di Matematica Applicata e Tecnologie Informatiche * +* Sezione di Genova * +* IMATI-GE / CNR * +* * +* Authors: Marco Attene * +* Copyright(C) 2013: IMATI-GE / CNR * +* All rights reserved. * +* * +* This program is dual-licensed as follows: * +* * +* (1) You may use TMesh as 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 3 of the License, or * +* (at your option) any later version. * +* In this case the 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 (http://www.gnu.org/licenses/gpl.txt) * +* for more details. * +* * +* (2) You may use TMesh as part of a commercial software. In this case a * +* proper agreement must be reached with the Authors and with IMATI-GE/CNR * +* based on a proper licensing contract. * +* * +****************************************************************************/ + +#include "triangle.h" +#include + +namespace T_MESH +{ + +extern "C" double orient2d(double *, double *, double *); + +//////////////////// Constructor ////////////////////// + +//!< AMF_ADD 1.1> +Triangle::Triangle(){ + mask = 0; + info = NULL; +} + +Triangle::Triangle(Edge *a, Edge *b, Edge *c) +{ + e1 = a; + e2 = b; + e3 = c; + mask = 0; + info = NULL; +} + + +//////////////////// Normal vector ////////////////////// + +Point Triangle::getNormal() const +{ + Vertex *va = v1(), *vb = v2(), *vc = v3(); + Point vd = (((*va)-(*vb))&((*vb)-(*vc))); + coord l = vd.length(); + + if (l == 0) return Point(0,0,0); + + return vd/l; +} + + +////// Directional vector //////// + +Point Triangle::getVector() const +{ + Vertex *va = v1(), *vb = v2(), *vc = v3(); + return (((*va) - (*vb))&((*vb) - (*vc))); +} + + +/////////////////// Normal consistence check //////////////////// + +bool Triangle::checkAdjNor(const Triangle *t) const +{ + Edge *e = commonEdge(t); + if (e == NULL) return 1; + + Edge *ea = nextEdge(e); + Edge *eb = t->nextEdge(e); + if (ea->commonVertex(eb) == ea->commonVertex(e)) return 0; + + return 1; +} + + +//////////////////////// Triangle area ///////////////////////// + +double Triangle::area() const +{ + double a = e1->length(), b = e2->length(), c = e3->length(); + if (a==0.0 || b==0.0 || c==0.0) return 0.0; + double p = (a+b+c)/2.0; + p = p*(p-a)*(p-b)*(p-c); if (p<0) return 0.0; + return sqrt(p); +} + + +/////////////////////// Triangle perimeter ///////////////////// + +double Triangle::perimeter() const +{ + return e1->length()+e2->length()+e3->length(); +} + + +///////////// Barycenter /////////////////////// + +Point Triangle::getCenter() const +{ + Point va = *v1(), vb = *v2(), vc = *v3(); + return (va+vb+vc)/3.0; +} + + +///////////////////////// Circlecenter ///////////////////////// + +Point Triangle::getCircleCenter() const +{ + Point va = *v1(), vb = *v2(), vc = *v3(); + Point q1 = vb-va; + Point q2 = vc-va; + Point n = q2&q1; + Point m1 = e2->getMidPoint(); + Point m2 = e1->getMidPoint(); + + return Point(n*va,q1*m1,q2*m2).linearSystem(n,q1,q2); +} + + +/////// Check wether the point is inside the triangle's bounding ball ///// + +bool Triangle::inSphere(const Point *p) const +{ + Point c = getCircleCenter(); + coord rad = c.squaredDistance(e1->v1); + + return (p->squaredDistance(&c) < rad); +} + + +//////////////////// Angle at a vertex ///////////////////// + +double Triangle::getAngle(const Vertex *v) const +{ + Vertex *va = v1(), *vb = v2(), *vc = v3(); + if (v == va) return v->getAngle(vb, vc); + if (v == vb) return v->getAngle(va, vc); + if (v == vc) return v->getAngle(vb, va); + + return -1.0; +} + + +/////////// Angle between the two directional vectors ///////// + +double Triangle::getDAngle(const Triangle *t) const +{ + Point thisNormal = getVector(); + Point otherNormal = t->getVector(); + + if (thisNormal.isNull() || otherNormal.isNull()) return -1.0; + + return thisNormal.getAngle(otherNormal); +} + + +///////////// Distance from the plane of the triangle ////////////// + +double Triangle::distanceFromPoint(const Point *p) const +{ + return sqrt(TMESH_TO_DOUBLE(squaredDistanceFromPoint(p))); +} + +///////////// Squared distance from the plane of the triangle ////////////// + +coord Triangle::squaredDistanceFromPoint(const Point *p) const +{ + Point CA = e1->toVector()&e2->toVector(); + coord CA2 = CA*CA; + + if (CA2 == 0) return -1.0; + coord d = ((CA*(*p))-(CA*(*(e1->v1)))); + + return (d*d)/CA2; +} + + +///////////// Distance of point from the triangle ////////////// + +double Triangle::pointTriangleDistance(const Point *p, Point *cp) const +{ + return sqrt(TMESH_TO_DOUBLE(pointTriangleSquaredDistance(p))); +} + + +///////////// Distance of point from the triangle ////////////// + +coord Triangle::pointTriangleSquaredDistance(const Point *p, Edge **closest_edge, Vertex **closest_vertex) const +{ + Vertex *va = v1(), *vb = v2(), *vc = v3(); + Point n(((*va)-(*vb))&((*vb)-(*vc))); + if (n.x == 0 && n.y == 0 && n.z == 0) return -1.0; + + coord d1 = ((((*va)-(*vb))&((*vb)-(*p)))*n); + coord d2 = ((((*vb)-(*vc))&((*vc)-(*p)))*n); + coord d3 = ((((*vc)-(*va))&((*va)-(*p)))*n); + + if (d1 > 0 && d2 > 0 && d3 > 0) // Closest point in inner triangle + { + if (closest_edge != NULL) *closest_edge = NULL; + if (closest_vertex != NULL) *closest_vertex = NULL; + return squaredDistanceFromPoint(p); + } + + if (d2 < 0) { va = vb; vb = vc; if (closest_edge != NULL) *closest_edge = e3; } + else if (d3 < 0) { vb = va; va = vc; if (closest_edge != NULL) *closest_edge = e1; } + else if (closest_edge != NULL) *closest_edge = e2; + + Point i(p->projection(va,vb)); + Point p1(i-(*va)); Point p2(i-(*vb)); + + if (p1*p2 < 0) // Closest point on interior of one edge + { + return i.squaredDistance(p); + } + + d1=p1.squaredLength(); d2=p2.squaredLength(); + if (d1 < d2) { if (closest_vertex != NULL) *closest_vertex = va; return p->squaredDistance(va); } + else { if (closest_vertex != NULL) *closest_vertex = vb; return p->squaredDistance(vb); } +} + + +/////////// Projection of point 'p' on the plane of the triangle ///// + +Point Triangle::project(const Point *p) const +{ + Point n = getVector(); + if (n.isNull()) return INFINITE_POINT; + return Point::linePlaneIntersection(*p, (*p) + n, *(v1()), *(v2()), *(v3())); +} + +bool Triangle::isExactlyDegenerate() const +{ + return (!v1()->exactMisalignment((v2()), (v3()))); +} + +//// get longest edge ///// + +Edge *Triangle::getLongestEdge() const +{ + coord l1 = e1->squaredLength(); + coord l2 = e2->squaredLength(); + coord l3 = e3->squaredLength(); + if (l1>=l2 && l1>=l3) return e1; + if (l2>=l1 && l2>=l3) return e2; + return e3; +} + +Edge *Triangle::getCapEdge() const +{ + Edge *e; + e = e1; if (Point::pointInInnerSegment(oppositeVertex(e), e->v1, e->v2)) return e; + e = e2; if (Point::pointInInnerSegment(oppositeVertex(e), e->v1, e->v2)) return e; + e = e3; if (Point::pointInInnerSegment(oppositeVertex(e), e->v1, e->v2)) return e; + return NULL; +} + +///////////// Overlap check //////////////////// + +bool Triangle::overlaps() const +{ + return (e1->overlaps() || e2->overlaps() || e3->overlaps()); +} + +Vertex *Triangle::commonVertex(const Triangle *t2) const +{ + if (hasVertex(t2->v1())) return t2->v1(); + if (hasVertex(t2->v2())) return t2->v2(); + if (hasVertex(t2->v3())) return t2->v3(); + return NULL; +} + + +/// Debug + +void Triangle::printTriangle(FILE *fp) const +{ + v1()->printPoint(fp); + v2()->printPoint(fp); + v3()->printPoint(fp); +} + + +// This can be made more efficient, I guess... + +bool Triangle::intersects(const Triangle *t2, bool justproper) const +{ + Vertex *v11, *v12, *v13, *v21, *v22, *v23; + + if (justproper) + { + // This works for non-degenerate triangles. Not sure it will work for degeneracies too. + v11 = v1(); v12 = v2(); v13 = v3(); + v21 = t2->v1(); v22 = t2->v2(); v23 = t2->v3(); + Vertex *eq1 = ((*v11) == (*v21)) ? (v21) : (((*v11) == (*v22)) ? (v22) : (((*v11) == (*v23)) ? (v23) : (NULL))); + Vertex *eq2 = ((*v12) == (*v21)) ? (v21) : (((*v12) == (*v22)) ? (v22) : (((*v12) == (*v23)) ? (v23) : (NULL))); + Vertex *eq3 = ((*v13) == (*v21)) ? (v21) : (((*v13) == (*v22)) ? (v22) : (((*v13) == (*v23)) ? (v23) : (NULL))); + if (eq1 && eq2 && eq3) return false; // Triangles coincide + Edge *ce1 = NULL, *ce2 = NULL; + if (eq1 && eq2) { ce1 = e2; ce2 = (t2->e1->hasVertices(eq1, eq2)) ? (t2->e1) : ((t2->e2->hasVertices(eq1, eq2)) ? (t2->e2) : (t2->e3)); } + if (eq2 && eq3) { ce1 = e3; ce2 = (t2->e1->hasVertices(eq3, eq2)) ? (t2->e1) : ((t2->e2->hasVertices(eq3, eq2)) ? (t2->e2) : (t2->e3)); } + if (eq3 && eq1) { ce1 = e1; ce2 = (t2->e1->hasVertices(eq3, eq1)) ? (t2->e1) : ((t2->e2->hasVertices(eq3, eq1)) ? (t2->e2) : (t2->e3)); } + if (ce1) + { + Vertex *ov = t2->oppositeVertex(ce2); + return (ov->exactOrientation(v11, v12, v13) == 0 && ov->exactSameSideOnPlane(oppositeVertex(ce1), ce1->v1, ce1->v2)); + } + Vertex *cv1 = NULL, *cv2 = NULL; + if (eq1) { cv1 = v11; cv2 = eq1; } + if (eq2) { cv1 = v12; cv2 = eq2; } + if (eq3) { cv1 = v13; cv2 = eq3; } + if (cv1) // If they share a vertex, intersection occurs if the opposite edge intersect the triangle + { + Edge *ee1 = oppositeEdge(cv1), *ee2 = t2->oppositeEdge(cv2); + return (Point::segmentIntersectsTriangle(ee1->v1, ee1->v2, v21, v22, v23) || + Point::segmentIntersectsTriangle(ee2->v1, ee2->v2, v11, v12, v13)); + } + } + else + { + Edge *ce = commonEdge(t2); + if (ce) // If they share an edge, intersection occurs only if t1 and t2 overlap + { + Vertex *ov = t2->oppositeVertex(ce); + return (ov->exactOrientation(v1(), v2(), v3()) == 0 && ov->exactSameSideOnPlane(oppositeVertex(ce), ce->v1, ce->v2)); + } + + Vertex *cv = commonVertex(t2); + v11 = v1(); v12 = v2(); v13 = v3(); + v21 = t2->v1(); v22 = t2->v2(); v23 = t2->v3(); + if (cv) // If they share a vertex, intersection occurs if the opposite edge intersect the triangle + { + Edge *ee1 = oppositeEdge(cv), *ee2 = t2->oppositeEdge(cv); + return (Point::segmentIntersectsTriangle(ee1->v1, ee1->v2, v21, v22, v23) || + Point::segmentIntersectsTriangle(ee2->v1, ee2->v2, v11, v12, v13)); + } + } + + // Fast reject by bounding box + coord mx = MIN(v11->x, MIN(v13->x, v12->x)); + if (v21->x < mx && v22->x < mx && v23->x < mx) return false; + mx = MAX(v11->x, MAX(v13->x, v12->x)); + if (v21->x > mx && v22->x > mx && v23->x > mx) return false; + mx = MIN(v11->y, MIN(v13->y, v12->y)); + if (v21->y < mx && v22->y < mx && v23->y < mx) return false; + mx = MAX(v11->y, MAX(v13->y, v12->y)); + if (v21->y > mx && v22->y > mx && v23->y > mx) return false; + mx = MIN(v11->z, MIN(v13->z, v12->z)); + if (v21->z < mx && v22->z < mx && v23->z < mx) return false; + mx = MAX(v11->z, MAX(v13->z, v12->z)); + if (v21->z > mx && v22->z > mx && v23->z > mx) return false; + + // Calculate relative orientations + coord o11 = v11->exactOrientation(v21, v22, v23); + coord o12 = v12->exactOrientation(v21, v22, v23); + coord o13 = v13->exactOrientation(v21, v22, v23); + if ((o11>0 && o12>0 && o13>0) || (o11<0 && o12<0 && o13<0)) return false; // t1 above/below t2 + coord o21 = v21->exactOrientation(v11, v12, v13); + coord o22 = v22->exactOrientation(v11, v12, v13); + coord o23 = v23->exactOrientation(v11, v12, v13); + if ((o21>0 && o22>0 && o23>0) || (o21<0 && o22<0 && o23<0)) return false; // t2 above/below t1 + + if (o11 == 0 && o12 == 0 && o13 == 0) // t1 and t2 are coplanar + { + if (Point::innerSegmentsCross(v11, v12, v21, v22)) return true; + if (Point::innerSegmentsCross(v11, v12, v22, v23)) return true; + if (Point::innerSegmentsCross(v11, v12, v23, v21)) return true; + if (Point::innerSegmentsCross(v12, v13, v21, v22)) return true; + if (Point::innerSegmentsCross(v12, v13, v22, v23)) return true; + if (Point::innerSegmentsCross(v12, v13, v23, v21)) return true; + if (Point::innerSegmentsCross(v13, v11, v21, v22)) return true; + if (Point::innerSegmentsCross(v13, v11, v22, v23)) return true; + if (Point::innerSegmentsCross(v13, v11, v23, v21)) return true; + return ( + Point::pointInTriangle(v11, v21, v22, v23) || + Point::pointInTriangle(v12, v21, v22, v23) || + Point::pointInTriangle(v13, v21, v22, v23) || + Point::pointInTriangle(v21, v11, v12, v13) || + Point::pointInTriangle(v22, v11, v12, v13) || + Point::pointInTriangle(v23, v11, v12, v13)); + } + else return ( + Point::segmentIntersectsTriangle(v11, v12, v21, v22, v23, o11, o12) || + Point::segmentIntersectsTriangle(v12, v13, v21, v22, v23, o12, o13) || + Point::segmentIntersectsTriangle(v13, v11, v21, v22, v23, o13, o11) || + Point::segmentIntersectsTriangle(v21, v22, v11, v12, v13, o21, o22) || + Point::segmentIntersectsTriangle(v22, v23, v11, v12, v13, o22, o23) || + Point::segmentIntersectsTriangle(v23, v21, v11, v12, v13, o23, o21)); +} + +} //namespace T_MESH diff --git a/src/mesh_fix/src/TMesh/vertex.cpp b/src/mesh_fix/src/TMesh/vertex.cpp new file mode 100644 index 000000000..a06cebae8 --- /dev/null +++ b/src/mesh_fix/src/TMesh/vertex.cpp @@ -0,0 +1,682 @@ +/**************************************************************************** +* TMesh * +* * +* Consiglio Nazionale delle Ricerche * +* Istituto di Matematica Applicata e Tecnologie Informatiche * +* Sezione di Genova * +* IMATI-GE / CNR * +* * +* Authors: Marco Attene * +* Copyright(C) 2013: IMATI-GE / CNR * +* All rights reserved. * +* * +* This program is dual-licensed as follows: * +* * +* (1) You may use TMesh as 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 3 of the License, or * +* (at your option) any later version. * +* In this case the 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 (http://www.gnu.org/licenses/gpl.txt) * +* for more details. * +* * +* (2) You may use TMesh as part of a commercial software. In this case a * +* proper agreement must be reached with the Authors and with IMATI-GE/CNR * +* based on a proper licensing contract. * +* * +****************************************************************************/ + +#include "vertex.h" +#include "edge.h" +#include "triangle.h" +#include +#include + +namespace T_MESH +{ + +//////////////////// Constructors //////////////////////// + +Vertex::Vertex() : Point() +{ + e0 = NULL; + mask = 0; +} + + +Vertex::Vertex(const coord& a, const coord& b, const coord& c) : Point(a,b,c) +{ + e0 = NULL; + mask = 0; +} + + +Vertex::Vertex(const Point *p) : Point(p->x, p->y, p->z) +{ + e0 = NULL; + mask = 0; +} + + +Vertex::Vertex(const Point& p) : Point(p.x, p.y, p.z) +{ + e0 = NULL; + mask = 0; +} + +///////////////////// Destructor /////////////////////// + +Vertex::~Vertex() +{ +} + + +/////////////// VE relation //////////////////////////// + +List *Vertex::VE() const +{ + Triangle *t; + Edge *e; + Vertex *v; + List *ve = new List(); + + if (e0 == NULL) return ve; + + + + e = e0; + do + { + ve->appendTail(e); + v = e->oppositeVertex(this); + t = e->leftTriangle(this); + if (t == NULL) break; + e = t->oppositeEdge(v); + } while (e != e0); + + if (e == e0 && ve->numels() > 1) return ve; + + ve->popHead(); + e = e0; + + do + { + ve->appendHead(e); + v = e->oppositeVertex(this); + t = e->rightTriangle(this); + if (t == NULL) break; + e = t->oppositeEdge(v); + } while (e != e0); + + return ve; +} + +/////////////// VV relation //////////////////////////// + +List *Vertex::VV() const +{ + Triangle *t; + Edge *e; + Vertex *v; + List *vv = new List(); + + if (e0 == NULL) return vv; + + e = e0; + do + { + v = e->oppositeVertex(this); + vv->appendTail(v); + t = e->leftTriangle(this); + if (t == NULL) break; + e = t->oppositeEdge(v); + } while (e != e0); + + if (e == e0 && vv->numels() > 1) return vv; + + vv->popHead(); + e = e0; + do + { + v = e->oppositeVertex(this); + vv->appendHead(v); + t = e->rightTriangle(this); + if (t == NULL) break; + e = t->oppositeEdge(v); + } while (e != e0); + + return vv; +} + +/////////////// VT relation //////////////////////////// + +List *Vertex::VT() const +{ + Triangle *t; + Edge *e; + Vertex *v; + List *vt = new List(); + + if (e0 == NULL) return vt; + + e = e0; + do + { + v = e->oppositeVertex(this); + t = e->leftTriangle(this); + if (t == NULL) break; + vt->appendTail(t); + e = t->oppositeEdge(v); + } while (e != e0); + + if (e == e0 && vt->numels() > 1) return vt; + + e = e0; + do + { + v = e->oppositeVertex(this); + t = e->rightTriangle(this); + if (t == NULL) break; + vt->appendHead(t); + e = t->oppositeEdge(v); + } while (e != e0); + + return vt; +} + +/////////////// Returns the edge (this,v2) //////////////// + +Edge *Vertex::getEdge(const Vertex *v2) const +{ + List *ve = VE(); + Node *m; + Edge *e; + + FOREACHVEEDGE(ve, e, m) + if (e->oppositeVertex(this) == v2) {delete(ve); return e;} + + delete(ve); + return NULL; +} + + +///////////// Returns the vertex valence /////////////////////// + +int Vertex::valence() const +{ + List *ve = VE(); + int n = ve->numels(); + delete(ve); + return n; +} + +/////////////// Checks the boundary //////////////////////////// + +int Vertex::isOnBoundary() const +{ + Triangle *t; + Edge *e; + Vertex *v; + + if (e0 == NULL) return 0; + + e = e0; + do + { + v = e->oppositeVertex(this); + t = e->leftTriangle(this); + if (t == NULL) return 1; + e = t->oppositeEdge(v); + } while (e != e0); + + return 0; +} + + +/////////////// Next boundary edge //////////////////////////// + +Edge *Vertex::nextBoundaryEdge() const +{ + Triangle *t; + Edge *e; + Vertex *v; + + if (e0 == NULL) return NULL; + + e = e0; + do + { + v = e->oppositeVertex(this); + t = e->leftTriangle(this); + if (t == NULL) return e; + e = t->oppositeEdge(v); + } while (e != e0); + + return NULL; +} + + +/////////////// Next boundary vertex //////////////////////////// + +Vertex *Vertex::nextOnBoundary() const +{ + Edge *e = nextBoundaryEdge(); + if (e != NULL) return e->oppositeVertex(this); + + return NULL; +} + + +/////////////// Previous boundary edge //////////////////////////// + +Edge *Vertex::prevBoundaryEdge() const +{ + Triangle *t; + Edge *e; + Vertex *v; + + if (e0 == NULL) return NULL; + + e = e0; + do + { + v = e->oppositeVertex(this); + t = e->rightTriangle(this); + if (t == NULL) return e; + e = t->oppositeEdge(v); + } while (e != e0); + + return NULL; +} + + +/////////////// Previous boundary vertex //////////////////////////// + +Vertex *Vertex::prevOnBoundary() const +{ + Edge *e = prevBoundaryEdge(); + if (e != NULL) return e->oppositeVertex(this); + + return NULL; +} + +//// TRUE iff vertex neighborhood is a flat disk. Always FALSE for boundary vertices. +bool Vertex::isFlat() const +{ + List *ve = VE(); + Node *n; + Edge *e; + + FOREACHVEEDGE(ve, e, n) + if (e->getConvexity() != 0) { delete ve; return false; } + + delete ve; + return true; +} + +//// TRUE iff vertex neighborhood is made of either two flat halfdisks or one flat halfdisk on a rectilinear boundary. +bool Vertex::isDoubleFlat(Edge **e1, Edge **e2) const +{ + List *ve = VE(); + Node *n; + Edge *e; + int nne = 0; + *e1 = *e2 = NULL; + + FOREACHVEEDGE(ve, e, n) + if (e->getConvexity() != 0) + { + if (++nne > 2) { delete ve; return false; } + else if (nne == 1) *e1 = e; + else *e2 = e; + } + delete ve; + if (nne == 0) return true; // This means that vertex is flat + if (nne == 1) return false; // This should not be possible, but just in case... + return (!((*e1)->oppositeVertex(this)->exactMisalignment(this, (*e2)->oppositeVertex(this)))); +} + +//// Unlinks the vertex if it is either Flat() or DoubleFlat(). On success, the function returns TRUE, +//// the vertex neighborhood is retriangulated, and the geometric realization does not change. +bool Vertex::removeIfRedundant(bool check_neighborhood) +{ + Edge *e, *e1 = NULL, *e2 = NULL; + if (!isDoubleFlat(&e1, &e2)) return false; + + Node *n; + Vertex *vo1, *vo2; + List *ve; + + if (check_neighborhood) + { + ve = VT(); + Triangle *t; + FOREACHVTTRIANGLE(ve, t, n) if (t->isExactlyDegenerate()) { delete ve; return false; } + delete ve; + ve = VE(); + FOREACHVEEDGE(ve, e, n) if (e->overlaps()) { delete ve; return false; } + } else ve = VE(); + + if (e1 != NULL) ve->removeNode(e1); // means isDoubleFlat() + if (e2 != NULL) ve->removeNode(e2); // means isDoubleFlat() + if (e2 != NULL && *e1->oppositeVertex(this) == *e2->oppositeVertex(this)) { delete ve; return false; } + + while (1) + { + FOREACHVEEDGE(ve, e, n) + { + vo1 = e->t1->oppositeVertex(e); + vo2 = e->t2->oppositeVertex(e); + if (!e->v1->exactSameSideOnPlane(e->v2, vo1, vo2) && vo1->exactMisalignment(e->v1, vo2) && vo1->exactMisalignment(e->v2, vo2)) + { + if (!e->swap()) { delete ve; return false; } + break; + } + } + if (n != NULL) ve->removeCell(n); + else break; + } + + if (e1 != NULL) e = e1; // means isDoubleFlat() + else if (ve->numels() == 3) e = (Edge *)ve->head()->data; + else + { + FOREACHVEEDGE(ve, e, n) + if (!e->t1->oppositeVertex(e)->exactMisalignment(this, e->t2->oppositeVertex(e))) break; + if (n == NULL) { delete ve; return false; } // Should not happen... + else e = (Edge *)((n == ve->head()) ? (ve->tail()) : (n->prev()))->data; + } + delete ve; + + if (e->v1 == this) e->invert(); + if (e->collapseOnV1() == NULL) return false; // This is a very rare case. Should be treated, but does not hurt too much. + else return true; +} + +///// Vertex normal as weighted average of the incident triangle normals //// +///// The weight is the incidence angle. //// +///// Possible degenerate triangles are not taken into account. //// + +Point Vertex::getNormal() const +{ + List *vt = VT(); + Node *n; + Triangle *t; + coord pa; + Point tnor, ttn; + + FOREACHVTTRIANGLE(vt, t, n) + { + pa = t->getAngle(this); + ttn = t->getNormal(); + if (!ttn.isNull()) tnor = tnor+(ttn*pa); + } + delete(vt); + + if (tnor.isNull()) return Point(0,0,0); + + tnor.normalize(); + return tnor; +} + + +////////// Returns the angle between the two boundary edges //////// + +double Vertex::getBoundaryAngle() const +{ + Edge *e1 = prevBoundaryEdge(); + Edge *e2 = nextBoundaryEdge(); + if (e1 == NULL || e2 == NULL) return -1.0; + Vertex *v1 = e1->oppositeVertex(this); + Vertex *v2 = e2->oppositeVertex(this); + double ang = getAngle(v1, v2); + + return ang; +} + + +////////// Returns the discriminant for triangulation //////// + +double Vertex::getAngleForTriangulation() const +{ + Edge *e1 = prevBoundaryEdge(); + Edge *e2 = nextBoundaryEdge(); + if (e1 == NULL || e2 == NULL) return DBL_MAX; + Triangle *t1 = e1->getBoundaryTriangle(); + Triangle *t2 = e2->getBoundaryTriangle(); + Vertex *v1 = e1->oppositeVertex(this); + Vertex *v2 = e2->oppositeVertex(this); + if ((*v2)==(*v1)) return -2; + if (distance(v1)*distance(v2) == 0.0) return -1; + + double ang = getAngle(v1, v2); + if (ang == M_PI) return 3*M_PI; + if (ang == 0) return 0; + + Edge e3(v1,v2); + Triangle t(e1,e2,&e3); + double da1 = t.getDAngle(t1); + double da2 = t.getDAngle(t2); + + if (da1==M_PI && da2==M_PI) return (DBL_MAX/2.0); + if (da1==M_PI || da2==M_PI) return (DBL_MAX/4.0); + + return da1+da2+ang; +} + + +////////// Returns the AP discriminant for triangulation //////// + +double Vertex::getAngleOnAveragePlane(Point *nor) const +{ + Edge *e1 = prevBoundaryEdge(); + Edge *e2 = nextBoundaryEdge(); + if (e1 == NULL || e2 == NULL) return DBL_MAX; + Vertex *v1 = e1->oppositeVertex(this); + Vertex *v2 = e2->oppositeVertex(this); + Point p, p1, p2; + p1.setValue(v1); + p2.setValue(v2); + p.setValue(this); + p.project(nor); + p1.project(nor); + p2.project(nor); + if (p.distance(p1)*p.distance(p2) == 0.0) + { + TMesh::warning("getAngleOnAveragePlane: coincident projections\n"); + return 0.0; + } + double ang = p.getAngle(&p1, &p2); + if (nor->side3D(&p1, &p, &p2) < 0) ang = -(ang-(2*M_PI)); + + return ang; +} + + +///// mean curvature at the vertex: sum of signed dihedral angles //// + +double Vertex::totalDihedralAngle() const +{ + List *ve = VE(); + double mc = 0; + Edge *e; + Node *n; + + FOREACHVEEDGE(ve, e, n) + if (e->isOnBoundary()) {delete(ve); return DBL_MAX;} + else mc -= (e->dihedralAngle()-M_PI); + mc /= ve->numels(); + + delete(ve); + + return mc; +} + + +///// Sum of all the incident angles //// + +double Vertex::totalAngle() const +{ + List *ve = VE(); + double ta = 0.0; + Edge *e; + Node *n; + + FOREACHVEEDGE(ve, e, n) + if (e->isOnBoundary()) {delete(ve); return -1.0;} + else ta += e->leftTriangle(this)->getAngle(this); + + delete(ve); + + return ta; +} + + +/////// Voronoi area around the vertex /////////////////// + +double Vertex::voronoiArea() const +{ + List *vt = VT(); + Node *n; + Triangle *t; + double va = 0.0; + + FOREACHVTTRIANGLE(vt, t, n) va += t->area(); + delete(vt); + + return va/3.0; +} + + +// Closes the gap starting from this vertex +// If 'check_geom' is true, the zipping stops +// whether the coordinates of the vertices to +// be zipped are not equal. + +int Vertex::zip(const bool check_geom) +{ + Node *n; + Edge *e; + + List *ve = VE(); + Edge *be1 = (Edge *)ve->head()->data; + Edge *be2 = (Edge *)ve->tail()->data; + delete(ve); + if (!be1->isOnBoundary() || !be2->isOnBoundary()) return 0; + Vertex *ov1 = be1->oppositeVertex(this); + Vertex *ov2 = be2->oppositeVertex(this); + + if (check_geom && ((*ov1)!=(*ov2))) return 0; + + if (ov1 != ov2) + { + ve = ov2->VE(); + FOREACHVEEDGE(ve, e, n) e->replaceVertex(ov2, ov1); + delete(ve); + ov2->e0 = NULL; + } + + Triangle *t = (be2->t1!=NULL)?(be2->t1):(be2->t2); + t->replaceEdge(be2, be1); + be1->replaceTriangle(NULL, t); + be2->v1=be2->v2=NULL; + e0 = ov1->e0 = be1; + return 1+ov1->zip(check_geom); +} + + +/////// Progressive Mesh: Vertex split /////////////////// + +//Edge *Vertex::inverseCollapse(Vertex *v2, Vertex *v3, Vertex *v4) +//{ +// Edge *e, *e1, *e2=NULL, *e3=NULL, *e4; +// Triangle *t1, *t2, *ta1, *ta4; +// Node *n; +// Vertex *tmp; +// +// List *ve = VE(); +// FOREACHVEEDGE(ve, e, n) +// { +// tmp = e->oppositeVertex(this); +// if (tmp == v3) e2 = e; +// else if (tmp == v4) e3 = e; +// } +// +// if (!e2 || !e3) {delete(ve); return NULL;} +// +// ta1 = e2->rightTriangle(this); +// ta4 = e3->leftTriangle(this); +// +// FOREACHVEEDGE(ve, e, n) if (e == e3) break; +// FOREACHNODECIRCULAR((*ve), n, n) +// { +// e = ((Edge *)n->data); +// if (e == e2) break; +// else e->replaceVertex(this, v2); +// } +// delete(ve); +// +// e = new Edge(this, v2); +// e1 = new Edge(v2, v3); +// e4 = new Edge(v2, v4); +// t1 = newTriangle(e, e1, e2); +// t2 = newTriangle(e, e3, e4); +// +// e->t1 = t1; e->t2 = t2; +// e2->replaceTriangle(ta1, t1); +// e3->replaceTriangle(ta4, t2); +// if (ta1) ta1->replaceEdge(e2, e1); +// if (ta4) ta4->replaceEdge(e3, e4); +// e1->t1 = t1; e1->t2 = ta1; +// e4->t1 = ta4; e4->t2 = t2; +// v2->e0 = e0 = e; +// +//// Point p = (*this)-((*v2)-(*this)); +//// x = p.x; y = p.y; z = p.z; +// +// return e; +//} + + +/////// Progressive Mesh: Vertex split /////////////////// + +Edge *Vertex::inverseCollapse(Vertex *v2, Edge *e, Edge *e1, Edge *e2, Edge *e3, Edge *e4, Triangle *t1, Triangle *t2) +{ + Triangle *ta1, *ta4; + Node *n; + Edge *f; + + ta1 = e2->rightTriangle(this); + ta4 = e3->leftTriangle(this); + + List *ve = VE(); + FOREACHVEEDGE(ve, f, n) if (f == e3) break; + FOREACHNODECIRCULAR((*ve), n, n) + { + f = ((Edge *)n->data); + if (f == e2) break; + else f->replaceVertex(this, v2); + } + delete(ve); + + e->v1 = this; e->v2 = v2; + e1->v1 = v2; e1->v2 = e2->oppositeVertex(this); + e4->v1 = v2; e4->v2 = e3->oppositeVertex(this); + t1->e1 = e; t1->e2 = e1; t1->e3 = e2; + t2->e1 = e; t2->e2 = e3; t2->e3 = e4; + + e->t1 = t1; e->t2 = t2; + e2->replaceTriangle(ta1, t1); + e3->replaceTriangle(ta4, t2); + if (ta1) ta1->replaceEdge(e2, e1); + if (ta4) ta4->replaceEdge(e3, e4); + e1->t1 = t1; e1->t2 = ta1; + e4->t1 = ta4; e4->t2 = t2; + v2->e0 = e0 = e; + + return e; +} + +} //namespace T_MESH diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 81a7740ba..c73d16799 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -214,6 +214,8 @@ set(SLIC3R_GUI_SOURCES Utils/Http.hpp Utils/FixModelByWin10.cpp Utils/FixModelByWin10.hpp + Utils/FixModelByMeshFix.cpp + Utils/FixModelByMeshFix.hpp Utils/OctoPrint.cpp Utils/OctoPrint.hpp Utils/Duet.cpp @@ -260,7 +262,7 @@ add_library(libslic3r_gui STATIC ${SLIC3R_GUI_SOURCES}) encoding_check(libslic3r_gui) -target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui GLEW::GLEW OpenGL::GL hidapi libcurl ${wxWidgets_LIBRARIES}) +target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui GLEW::GLEW OpenGL::GL hidapi libcurl meshfix ${wxWidgets_LIBRARIES}) if (MSVC) target_link_libraries(libslic3r_gui Setupapi.lib) diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp index c3aaa2f4e..65906c1ae 100644 --- a/src/slic3r/GUI/GUI_Factories.cpp +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -694,8 +694,18 @@ wxMenuItem* MenuFactory::append_menu_item_fix_through_netfabb(wxMenu* menu) if (!is_windows10()) return nullptr; wxMenuItem* menu_item = append_menu_item(menu, wxID_ANY, _L("Fix through the Netfabb"), "", - [](wxCommandEvent&) { obj_list()->fix_through_netfabb(); }, "", menu, - []() {return plater()->can_fix_through_netfabb(); }, m_parent); + [](wxCommandEvent&) { obj_list()->repair_mesh(ObjectList::rmaNetfabb); }, "", menu, + []() {return plater()->can_repair_mesh(); }, m_parent); + + return menu_item; +} + + +wxMenuItem* MenuFactory::append_menu_item_fix_through_meshfix(wxMenu* menu) +{ + wxMenuItem* menu_item = append_menu_item(menu, wxID_ANY, _L("Fix through MeshFix (experimental)"), "", + [](wxCommandEvent&) { obj_list()->repair_mesh(ObjectList::rmaMeshfix); }, "", menu, + []() {return plater()->can_repair_mesh(); }, m_parent); return menu_item; } @@ -923,6 +933,7 @@ void MenuFactory::create_common_object_menu(wxMenu* menu) append_menu_item_scale_selection_to_fit_print_volume(menu); append_menu_item_fix_through_netfabb(menu); + append_menu_item_fix_through_meshfix(menu); append_menu_item_simplify(menu); append_menu_items_mirror(menu); } diff --git a/src/slic3r/GUI/GUI_Factories.hpp b/src/slic3r/GUI/GUI_Factories.hpp index 0c478a97b..26799c733 100644 --- a/src/slic3r/GUI/GUI_Factories.hpp +++ b/src/slic3r/GUI/GUI_Factories.hpp @@ -92,6 +92,7 @@ private: wxMenuItem* append_menu_item_printable(wxMenu* menu); void append_menu_items_osx(wxMenu* menu); wxMenuItem* append_menu_item_fix_through_netfabb(wxMenu* menu); + wxMenuItem* append_menu_item_fix_through_meshfix(wxMenu* menu); wxMenuItem* append_menu_item_simplify(wxMenu* menu); void append_menu_item_export_stl(wxMenu* menu); void append_menu_item_reload_from_disk(wxMenu* menu); diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index a02abc849..ff225fe9e 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -28,6 +28,7 @@ #include #include "slic3r/Utils/FixModelByWin10.hpp" +#include "slic3r/Utils/FixModelByMeshFix.hpp" #ifdef __WXMSW__ #include "wx/uiaction.h" @@ -916,7 +917,7 @@ void ObjectList::list_manipulation(const wxPoint& mouse_pos, bool evt_context_me { if (is_windows10() && m_objects_model->HasWarningIcon(item) && mouse_pos.x > 2 * wxGetApp().em_unit() && mouse_pos.x < 4 * wxGetApp().em_unit()) - fix_through_netfabb(); + repair_mesh(rmaNetfabb); else if (evt_context_menu) show_context_menu(evt_context_menu); // show context menu for "Name" column too } @@ -4031,7 +4032,7 @@ void ObjectList::rename_item() update_name_in_model(item); } -void ObjectList::fix_through_netfabb() +void ObjectList::repair_mesh(ObjectList::REPAIR_MESH_ALG alg) { // Do not fix anything when a gizmo is open. There might be issues with updates // and what is worse, the snapshot time would refer to the internal stack. @@ -4051,28 +4052,28 @@ void ObjectList::fix_through_netfabb() // clear selections from the non-broken models if any exists // and than fill names of models to repairing if (vol_idxs.empty()) { -#if !FIX_THROUGH_NETFABB_ALWAYS +#if !FIX_MESH_ALWAYS for (int i = int(obj_idxs.size())-1; i >= 0; --i) if (object(obj_idxs[i])->get_repaired_errors_count() == 0) obj_idxs.erase(obj_idxs.begin()+i); -#endif // FIX_THROUGH_NETFABB_ALWAYS +#endif // FIX_MESH_ALWAYS for (int obj_idx : obj_idxs) model_names.push_back(object(obj_idx)->name); } else { ModelObject* obj = object(obj_idxs.front()); -#if !FIX_THROUGH_NETFABB_ALWAYS +#if !FIX_MESH_ALWAYS for (int i = int(vol_idxs.size()) - 1; i >= 0; --i) if (obj->get_repaired_errors_count(vol_idxs[i]) == 0) vol_idxs.erase(vol_idxs.begin() + i); -#endif // FIX_THROUGH_NETFABB_ALWAYS +#endif // FIX_MESH_ALWAYS for (int vol_idx : vol_idxs) model_names.push_back(obj->volumes[vol_idx]->name); } auto plater = wxGetApp().plater(); - auto fix_and_update_progress = [this, plater, model_names](const int obj_idx, const int vol_idx, + auto fix_and_update_progress = [this, plater, model_names, alg](const int obj_idx, const int vol_idx, int model_idx, wxProgressDialog& progress_dlg, std::vector& succes_models, @@ -4091,8 +4092,19 @@ void ObjectList::fix_through_netfabb() plater->clear_before_change_mesh(obj_idx); std::string res; - if (!fix_model_by_win10_sdk_gui(*(object(obj_idx)), vol_idx, progress_dlg, msg, res)) - return false; + bool result = false; + switch (alg) { + case rmaNetfabb: + result = fix_model_by_win10_sdk_gui(*(object(obj_idx)), vol_idx, progress_dlg, msg, res); + break; + case rmaMeshfix: + result = fix_model_by_meshfix(*(object(obj_idx)), vol_idx, progress_dlg, msg, res); + break; + default: + break; + } + if (!result) return false; + wxGetApp().plater()->changed_mesh(obj_idx); plater->changed_mesh(obj_idx); @@ -4108,19 +4120,19 @@ void ObjectList::fix_through_netfabb() return true; }; - Plater::TakeSnapshot snapshot(plater, _L("Fix through NetFabb")); + Plater::TakeSnapshot snapshot(plater, _L("Repair model mesh")); // Open a progress dialog. - wxProgressDialog progress_dlg(_L("Fixing through NetFabb"), "", 100, find_toplevel_parent(plater), + wxProgressDialog progress_dlg(_L("Repair model mesh"), "", 100, find_toplevel_parent(plater), wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT); int model_idx{ 0 }; if (vol_idxs.empty()) { int vol_idx{ -1 }; for (int obj_idx : obj_idxs) { -#if !FIX_THROUGH_NETFABB_ALWAYS +#if !FIX_MESH_ALWAYS if (object(obj_idx)->get_repaired_errors_count(vol_idx) == 0) continue; -#endif // FIX_THROUGH_NETFABB_ALWAYS +#endif // FIX_MESH_ALWAYS if (!fix_and_update_progress(obj_idx, vol_idx, model_idx, progress_dlg, succes_models, failed_models)) break; model_idx++; @@ -4153,7 +4165,7 @@ void ObjectList::fix_through_netfabb() } if (msg.IsEmpty()) msg = _L("Repairing was canceled"); - plater->get_notification_manager()->push_notification(NotificationType::NetfabbFinished, NotificationManager::NotificationLevel::PrintInfoShortNotificationLevel, boost::nowide::narrow(msg)); + plater->get_notification_manager()->push_notification(NotificationType::RepairMeshFinished, NotificationManager::NotificationLevel::PrintInfoShortNotificationLevel, boost::nowide::narrow(msg)); } void ObjectList::simplify() diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index 51d69eaee..d9f1b7ea6 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -37,7 +37,7 @@ typedef std::pair t_layer_height_range; typedef std::map t_layer_config_ranges; // Manifold mesh may contain self-intersections, so we want to always allow fixing the mesh. -#define FIX_THROUGH_NETFABB_ALWAYS 1 +#define FIX_MESH_ALWAYS 1 namespace GUI { @@ -367,7 +367,8 @@ public: void instances_to_separated_objects(const int obj_idx); void split_instances(); void rename_item(); - void fix_through_netfabb(); + enum REPAIR_MESH_ALG{rmaNetfabb, rmaMeshfix}; + void repair_mesh(REPAIR_MESH_ALG alg); void simplify(); void update_item_error_icon(const int obj_idx, int vol_idx) const ; diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 18e58efb8..263419cbe 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -130,7 +130,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : if (m_fix_throught_netfab_bitmap->GetBitmap().GetRefData() == wxNullBitmap.GetRefData()) return; - wxGetApp().obj_list()->fix_through_netfabb(); + wxGetApp().obj_list()->repair_mesh(ObjectList::rmaNetfabb); update_warning_icon_state(wxGetApp().obj_list()->get_mesh_errors_info()); }); diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index 9265cb55e..e14dcb294 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -109,8 +109,8 @@ enum class NotificationType // Give user advice to simplify object with big amount of triangles // Contains ObjectID for closing when object is deleted SimplifySuggestion, - // information about netfabb is finished repairing model (blocking proccess) - NetfabbFinished, + // information about finished model repairing (blocking proccess) + RepairMeshFinished, // Short meesage to fill space between start and finish of export ExportOngoing, }; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 81c57bae0..df9eb2104 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1935,7 +1935,7 @@ struct Plater::priv bool can_split_to_volumes() const; bool can_arrange() const; bool can_layers_editing() const; - bool can_fix_through_netfabb() const; + bool can_repair_mesh() const; bool can_simplify() const; bool can_set_instance_to_object() const; bool can_mirror() const; @@ -4642,15 +4642,15 @@ bool Plater::priv::can_delete_all() const return !model.objects.empty(); } -bool Plater::priv::can_fix_through_netfabb() const +bool Plater::priv::can_repair_mesh() const { std::vector obj_idxs, vol_idxs; sidebar->obj_list()->get_selection_indexes(obj_idxs, vol_idxs); -#if FIX_THROUGH_NETFABB_ALWAYS +#if FIX_MESH_ALWAYS // Fixing always. return ! obj_idxs.empty() || ! vol_idxs.empty(); -#else // FIX_THROUGH_NETFABB_ALWAYS +#else // FIX_MESH_ALWAYS // Fixing only if the model is not manifold. if (vol_idxs.empty()) { for (auto obj_idx : obj_idxs) @@ -4664,7 +4664,7 @@ bool Plater::priv::can_fix_through_netfabb() const if (model.objects[obj_idx]->get_repaired_errors_count(vol_idx) > 0) return true; return false; -#endif // FIX_THROUGH_NETFABB_ALWAYS +#endif // FIX_MESH_ALWAYS } bool Plater::priv::can_simplify() const @@ -6848,7 +6848,7 @@ bool Plater::can_delete_all() const { return p->can_delete_all(); } bool Plater::can_increase_instances() const { return p->can_increase_instances(); } bool Plater::can_decrease_instances() const { return p->can_decrease_instances(); } bool Plater::can_set_instance_to_object() const { return p->can_set_instance_to_object(); } -bool Plater::can_fix_through_netfabb() const { return p->can_fix_through_netfabb(); } +bool Plater::can_repair_mesh() const { return p->can_repair_mesh(); } bool Plater::can_simplify() const { return p->can_simplify(); } bool Plater::can_split_to_objects() const { return p->can_split_to_objects(); } bool Plater::can_split_to_volumes() const { return p->can_split_to_volumes(); } diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index f59f138d4..a50fd66f9 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -309,7 +309,7 @@ public: bool can_increase_instances() const; bool can_decrease_instances() const; bool can_set_instance_to_object() const; - bool can_fix_through_netfabb() const; + bool can_repair_mesh() const; bool can_simplify() const; bool can_split_to_objects() const; bool can_split_to_volumes() const; diff --git a/src/slic3r/Utils/FixModelByMeshFix.cpp b/src/slic3r/Utils/FixModelByMeshFix.cpp new file mode 100644 index 000000000..806313208 --- /dev/null +++ b/src/slic3r/Utils/FixModelByMeshFix.cpp @@ -0,0 +1,395 @@ +#include "FixModelByMeshFix.hpp" +#include "libslic3r/TriangleMesh.hpp" +#include "libslic3r/Model.hpp" + +#include "tmesh.h" +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "libslic3r/Model.hpp" +#include "libslic3r/Print.hpp" +#include "libslic3r/PresetBundle.hpp" +#include "libslic3r/Format/3mf.hpp" +#include "../GUI/GUI.hpp" +#include "../GUI/I18N.hpp" +#include "../GUI/MsgDialog.hpp" + +#include +#include + +namespace Slic3r { + +class RepairCanceledException: public std::exception { +public: + const char* what() const throw () { + return "Model repair has been canceled"; + } +}; + +class RepairFailedException: public std::exception { +public: + const char* what() const throw () { + return "Model repair has failed"; + } +}; + +namespace detail { + +using namespace T_MESH; + +double closestPair(List *bl1, List *bl2, Vertex **closest_on_bl1, Vertex **closest_on_bl2) + { + Node *n, *m; + Vertex *v, *w; + double adist, mindist = DBL_MAX; + + FOREACHVVVERTEX(bl1, v, n) + FOREACHVVVERTEX(bl2, w, m) + if ((adist = w->squaredDistance(v)) < mindist) + { + mindist = adist; + *closest_on_bl1 = v; + *closest_on_bl2 = w; + } + + return mindist; +} + +bool joinClosestComponents(Basic_TMesh *tin) + { + Vertex *v, *w, *gv, *gw; + Triangle *t, *s; + Node *n; + List triList, boundary_loops, *one_loop; + List **bloops_array; + int i, j, numloops; + + // Mark triangles with connected component's unique ID + i = 0; + FOREACHVTTRIANGLE((&(tin->T)), t, n) t->info = NULL; + FOREACHVTTRIANGLE((&(tin->T)), t, n) if (t->info == NULL) + { + i++; + triList.appendHead(t); + t->info = (void *)(intptr_t)i; + + while (triList.numels()) + { + t = (Triangle *)triList.popHead(); + if ((s = t->t1()) != NULL && s->info == NULL) {triList.appendHead(s); s->info = (void *)(intptr_t)i;} + if ((s = t->t2()) != NULL && s->info == NULL) {triList.appendHead(s); s->info = (void *)(intptr_t)i;} + if ((s = t->t3()) != NULL && s->info == NULL) {triList.appendHead(s); s->info = (void *)(intptr_t)i;} + } + } + + if (i<2) + { + FOREACHVTTRIANGLE((&(tin->T)), t, n) t->info = NULL; + // JMesh::info("Mesh is a single component. Nothing done."); + return false; + } + + FOREACHVTTRIANGLE((&(tin->T)), t, n) + { + t->v1()->info = t->v2()->info = t->v3()->info = t->info; + } + + FOREACHVVVERTEX((&(tin->V)), v, n) if (!IS_VISITED2(v) && v->isOnBoundary()) + { + w = v; + one_loop = new List; + do + { + one_loop->appendHead(w); MARK_VISIT2(w); + w = w->nextOnBoundary(); + }while (w != v); + boundary_loops.appendHead(one_loop); + } + FOREACHVVVERTEX((&(tin->V)), v, n) UNMARK_VISIT2(v); + + bloops_array = (List **)boundary_loops.toArray(); + numloops = boundary_loops.numels(); + + double adist, + mindist = DBL_MAX; + + gv = NULL; + for (i = 0; i < numloops; i++) + for (j = 0; j < numloops; j++) + if (((Vertex*) bloops_array[i]->head()->data)->info != ((Vertex*) bloops_array[j]->head()->data)->info) + { + adist = closestPair(bloops_array[i], bloops_array[j], &v, &w); + if (adist < mindist) { + mindist = adist; + gv = v; + gw = w; + } + } + + if (gv != NULL) + tin->joinBoundaryLoops(gv, gw, 1, 0); + + FOREACHVTTRIANGLE((&(tin->T)), t, n) + t->info = NULL; + FOREACHVVVERTEX((&(tin->V)), v, n) + v->info = NULL; + + free(bloops_array); + while ((one_loop = (List*) boundary_loops.popHead()) != NULL) + delete one_loop; + + return (gv != NULL); +} + +class Basic_TMesh_Adapter: public Basic_TMesh { +public: + void load_indexed_triangle_set(const indexed_triangle_set &its) { + + for (const auto &vertex : its.vertices) { + this->V.appendTail(this->newVertex(vertex.x(), vertex.y(), vertex.z())); + } + + int nv = this->V.numels(); + Node *n = this->V.head(); + ExtVertex **tmp = (ExtVertex**) malloc(sizeof(ExtVertex*) * nv); + for (int index = 0; index < nv; ++index) { + tmp[index] = new ExtVertex(static_cast(n->data)); + n = n->next(); + } + + for (const auto &face : its.indices) { + if (face.x() == face.y() || face.y() == face.z() || face.z() == face.x()) { + continue; + } + this->CreateIndexedTriangle(tmp, face.x(), face.y(), face.z()); + } + + closeLoadingSession(this->T.numels(), tmp, false); + } + + indexed_triangle_set to_indexed_triangle_set() { + indexed_triangle_set out; + out.vertices.resize(this->V.numels()); + out.indices.resize(this->T.numels()); + + Node *n = this->V.head(); + for (int vertex_idx = 0; vertex_idx < this->V.numels(); ++vertex_idx) { + T_MESH::Vertex *v = static_cast(n->data); + out.vertices[vertex_idx] = Vec3f { float(v->x), float(v->y), float(v->z) }; + n = n->next(); + //store vertex index in the first coord, makes export of faces simple (inspired by saveOBJ method) + v->x = vertex_idx; + } + + n = this->T.head(); + for (int face_idx = 0; face_idx < this->T.numels(); ++face_idx) { + T_MESH::Triangle *t = static_cast(n->data); + out.indices[face_idx] = Vec3i { int(t->v1()->x), int(t->v2()->x), int(t->v3()->x) }; + n = n->next(); + } + + return out; + } + + bool meshclean_single_iteration(int inner_loops, const std::atomic &canceled) { + bool ni, nd; + Triangle *t; + Node *m; + + nd = strongDegeneracyRemoval(inner_loops); + if (canceled) + throw RepairCanceledException(); + deselectTriangles(); + invertSelection(); + ni = strongIntersectionRemoval(inner_loops); + if (canceled) + throw RepairCanceledException(); + if (ni && nd) { + FOREACHTRIANGLE(t, m) + if (t->isExactlyDegenerate()) + ni = false; + if (ni) + return true; + } + + return false; + } + +private: + + void closeLoadingSession(int loaded_faces, ExtVertex **tmp, bool triangulate) { + int i, nv = this->V.numels(); + if (tmp != NULL) + { + for (i = 0; i < nv; i++) + delete (tmp[i]); + free(tmp); + } + fixConnectivity(); + this->d_boundaries = this->d_handles = this->d_shells = 1; + } +}; + +} + +bool fix_model_by_meshfix(ModelObject &model_object, int volume_idx, wxProgressDialog &progress_dlg, + const wxString &msg_header, std::string &fix_result) { + std::mutex mtx; + std::condition_variable condition; + struct Progress { + std::string message; + int percent = 0; + bool updated = false; + } progress; + std::atomic canceled = false; + std::atomic finished = false; + + std::vector volumes; + if (volume_idx == -1) + volumes = model_object.volumes; + else + volumes.emplace_back(model_object.volumes[volume_idx]); + + // Executing the calculation in a background thread, so that the COM context could be created with its own threading model. + // (It seems like wxWidgets initialize the COM contex as single threaded and we need a multi-threaded context). + bool success = false; + size_t ivolume = 0; + auto on_progress = [&mtx, &condition, &ivolume, &volumes, &progress](const char *msg, unsigned prcnt) { + std::unique_lock lock(mtx); + progress.message = msg; + progress.percent = (int) floor((float(prcnt) + float(ivolume) * 100.f) / float(volumes.size())); + progress.updated = true; + condition.notify_all(); + }; + auto worker_thread = boost::thread( + [&model_object, &volumes, &ivolume, on_progress, &success, &canceled, &finished]() { + try { + std::vector meshes_repaired; + meshes_repaired.reserve(volumes.size()); + for (ModelVolume *mv : volumes) { + std::vector parts = its_split(mv->mesh().its); + for (size_t part_idx = 0; part_idx < parts.size(); ++part_idx) { + + detail::Basic_TMesh_Adapter tin { }; + on_progress(L("Loading source model"), 0); + if (canceled) + throw RepairCanceledException(); + tin.load_indexed_triangle_set(parts[part_idx]); + tin.boundaries(); + on_progress(L("Join closest components"), 10); + if (canceled) + throw RepairCanceledException(); + joinClosestComponents(&tin); + tin.deselectTriangles(); + tin.boundaries(); + // Keep only the largest component (i.e. with most triangles) + on_progress(L("Remove smallest components"), 20); + if (canceled) + throw RepairCanceledException(); + tin.removeSmallestComponents(); + + // Fill holes + on_progress(L("Check holes"), 30); + if (canceled) + throw RepairCanceledException(); + if (tin.boundaries()) { + on_progress(L("Patch small holes"), 40); + if (canceled) + throw RepairCanceledException(); + tin.fillSmallBoundaries(0, true); + } + + on_progress(L("Geometry check"), 50); + if (canceled) + throw RepairCanceledException(); + // Run geometry correction + if (!tin.boundaries()) { + int iteration = 0; + on_progress(L("Iterative geometry correction"), 55); + tin.deselectTriangles(); + tin.invertSelection(); + bool fixed = false; + while (iteration < 10 && !fixed) { //default constants taken from TMesh library + fixed = tin.meshclean_single_iteration(3, canceled); + on_progress(L("Fixing geometry"), std::min(95, 60 + iteration * 8)); // majority of objects should finish in 4 iterations + if (canceled) + throw RepairCanceledException(); + iteration++; + } + } + + if (tin.boundaries() || tin.T.numels() == 0) { + throw RepairFailedException(); + } + parts[part_idx] = tin.to_indexed_triangle_set(); + } + + for (size_t part_idx = 1; part_idx < parts.size(); ++part_idx) { + its_merge(parts[0], parts[part_idx]); + } + + meshes_repaired.emplace_back(std::move(parts[0])); + } + + for (size_t i = 0; i < volumes.size(); ++i) { + volumes[i]->set_mesh(std::move(meshes_repaired[i])); + volumes[i]->calculate_convex_hull(); + volumes[i]->set_new_unique_id(); + } + model_object.invalidate_bounding_box(); + --ivolume; + on_progress(L("Model repair finished"), 100); + success = true; + finished = true; + } catch (RepairCanceledException& /* ex */) { + canceled = true; + finished = true; + on_progress(L("Model repair canceled"), 100); + } catch (std::exception &ex) { + success = false; + finished = true; + on_progress(ex.what(), 100); + } + }); + + while (!finished) { + std::unique_lock lock(mtx); + condition.wait_for(lock, std::chrono::milliseconds(250), [&progress] { + return progress.updated; + }); + // decrease progress.percent value to avoid closing of the progress dialog + if (!progress_dlg.Update(progress.percent - 1, msg_header + _(progress.message))) + canceled = true; + else + progress_dlg.Fit(); + progress.updated = false; + } + + worker_thread.join(); + + if (canceled) { + // Nothing to show. + } else if (success) { + fix_result = ""; + } else { + fix_result = progress.message; + } + + return !canceled; +} + +} diff --git a/src/slic3r/Utils/FixModelByMeshFix.hpp b/src/slic3r/Utils/FixModelByMeshFix.hpp new file mode 100644 index 000000000..bde1530a9 --- /dev/null +++ b/src/slic3r/Utils/FixModelByMeshFix.hpp @@ -0,0 +1,20 @@ +#ifndef SRC_SLIC3R_UTILS_FIXMODELBYMESHFIX_HPP_ +#define SRC_SLIC3R_UTILS_FIXMODELBYMESHFIX_HPP_ + +#include +#include + +class wxProgressDialog; + +namespace Slic3r { + +class Model; +class ModelObject; +class Print; + +bool fix_model_by_meshfix(ModelObject &model_object, int volume_idx, wxProgressDialog &progress_dlg, + const wxString &msg_header, std::string &fix_result); + +} + +#endif /* SRC_SLIC3R_UTILS_FIXMODELBYMESHFIX_HPP_ */