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_ */