Revert "Merge branch 'pm_stable_meshfix' into dev_250"

This reverts commit 71a8ead282, reversing
changes made to a1143ab26e.
This commit is contained in:
Lukas Matena 2022-06-17 16:13:18 +02:00
parent d59b11568e
commit 9aa104d080
52 changed files with 34 additions and 16936 deletions

View File

@ -1,7 +1,6 @@
cmake_minimum_required(VERSION 3.13)
project(PrusaSlicer-native)
add_subdirectory(mesh_fix)
add_subdirectory(build-utils)
add_subdirectory(admesh)
add_subdirectory(avrdude)

View File

@ -1,34 +0,0 @@
################################################################################
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
)
################################################################################

View File

@ -1,114 +0,0 @@
This distribution of MeshFix-V2.1 library is only meant for interfacing MeshFix-V2.1 with Slic3rPE
(https://github.com/prusa3d/PrusaSlicer).
The MeshFix-V2.1 source file was acquired from https://github.com/MarcoAttene/MeshFix-V2.1 at revision
e9399efd6954f6f38e19852226241839a4e1cd53.
We did various changes and fixes to the original library:
- removed void pointers, substituted with pointers to Data struct (with virtual destructor)
- all structs stored previously in void ptr now inherit from the added Data struct
- created double and int wrapper which inherits from Data
- fixed detected unefined behaviours
- fixed detected buffer over/underflows
Many thanks to Marco Attene and other contributors.
Pavel Mikus (pavel.mikus@prusa3d.cz)
6.6.2022
See original contents of the README file below.
======================================================================================
======================================================================================
======================================================================================
----------------------------
MeshFIX - Version 2.1
----------------------------
by Marco Attene
Consiglio Nazionale delle Ricerche
Istituto di Matematica Applicata e Tecnologie Informatiche
Sezione di Genova
IMATI-GE / CNR
This software takes as input a polygon mesh and produces a copy of the input where all the occurrences of a specific set of "defects" are corrected. MeshFix has been designed to correct typical flaws present in RAW DIGITIZED mesh models, thus it might fail or produce coarse results if run on other sorts of input meshes (e.g. tessellated CAD models). When the software fails, it appends a textual description to the file "meshfix.log".
The input is assumed to represent a single CLOSED SOLID OBJECT, thus the output will be a SINGLE WATERTIGHT TRIANGLE MESH bounding a polyhedron. All the singularities, self-intersections and degenerate elements are removed from the input, while regions of the surface without defects are left unmodified. Accepted input file formats are:
STL (http://www.sdsc.edu/tmf/Stl-specs/stl.html)
OFF (http://shape.cs.princeton.edu/benchmark/documentation/off_format.html)
PLY (http://www.cs.unc.edu/~geom/Powerplant/Ply.doc)
and partially:
IV 2.1, VRML 1.0, VRML 2.0, OBJ.
-----------------------------
ALGORITHM AND CITATION POLICY
-----------------------------
To better understand how the algorithm works, please refer to the following paper:
M. Attene.
A lightweight approach to repairing digitized polygon meshes.
The Visual Computer, 2010. (c) Springer. DOI: 10.1007/s00371-010-0416-3
This software is based on ideas published therein. If you use MeshFix for research purposes you should cite the above paper in your published results. MeshFix cannot be used for commercial purposes without a proper licensing contract.
----------
PARAMETERS
----------
The user may want to force the software to join the connected components [-a] whose boundary loops are closer to each other. Otherwise, only the largest component is kept while the others are considered as "noise". By default, the output file is in OFF format. The user may change this to STL [-j]. If the software is run as part of a script that processes all the files in a directory, the parameter [-x] may be useful to avoid to re-run the repairing of a mesh if its result already exists as a file.
-------------------
COMMAND LINE SYNTAX
-------------------
Run MeshFix without parameters to get the synopsis.
---------------------
OTHER LAUNCHING MODES
---------------------
A mesh can also be fixed my simply dragging its icon on MeshFix's icon. In this case, only default parameters can be used.
MeshFix does not require any interaction, thus it can be inserted into a script to automatically repair all the models of a given repository (e.g. a folder). If some failure cases occur, they will be logged to "meshfix.log" and thus can be easily located and possibly processed is a second stage through interactive software tools such as ReMESH (http://remesh.sourceforge.net).
---------------------
SOURCE CODE
---------------------
From version 2.0 MeshFix is self-contained, that is, it does not depend on any other software.
To compile the source code on Windows you need Microsoft Visual C++ 2013 or newer.
Just click on vc12/MeshFix_All.sln and hit F7.
The executable will be saved in bin/.
Both 32bit and 64bit versions can be produced.
The source code is standard ANSI C++ and should be portable.
To compile on other configurations you may use CMake (thanks to Jeremie Dumas for having created the CMakeLists!).
---------
Copyright
---------
MeshFix is
Copyright(C) 2010: IMATI-GE / CNR
All rights reserved.
This program is dual-licensed as follows:
(1) You may use MeshFix 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 MeshFix 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.

View File

@ -1,674 +0,0 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU 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.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 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 <http://www.gnu.org/licenses/>.
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:
<program> Copyright (C) <year> <name of author>
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
<http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

View File

@ -1,302 +0,0 @@
/****************************************************************************
* 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 <stdio.h>
#include <math.h>
#include <time.h>
#include <limits.h>
#include <float.h>
#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
#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;}
/////////////////////////////////////////////////////////////////////////////////////////////
template<typename T, typename Child>
class Primitive {
protected:
T value;
public:
// we must type cast to child to so
// a += 3 += 5 ... and etc.. work the same way
// as on primitives
Child &childRef(){
return *((Child*)this);
}
// you can overload to give a default value if you want
Primitive(){}
explicit Primitive(T v):value(v){}
T get(){
return value;
}
#define OP(op) Child &operator op(Child const &v){\
value op v.value; \
return childRef(); \
}\
Child &operator op(T const &v){\
value op v; \
return childRef(); \
}
// all with equals
OP(+=)
OP(-=)
OP(*=)
OP(/=)
OP(<<=)
OP(>>=)
OP(|=)
OP(^=)
OP(&=)
OP(%=)
#undef OP
#define OP(p) Child operator p(Child const &v){\
Child other = childRef();\
other p ## = v;\
return other;\
}\
Child operator p(T const &v){\
Child other = childRef();\
other p ## = v;\
return other;\
}
OP(+)
OP(-)
OP(*)
OP(/)
OP(<<)
OP(>>)
OP(|)
OP(^)
OP(&)
OP(%)
#undef OP
#define OP(p) bool operator p(Child const &v){\
return value p v.value;\
}\
bool operator p(T const &v){\
return value p v;\
}
OP(&&)
OP(||)
OP(<)
OP(<=)
OP(>)
OP(>=)
OP(==)
OP(!=)
#undef OP
Child operator +(){return Child(value);}
Child operator -(){return Child(-value);}
Child &operator ++(){++value; return childRef();}
Child operator ++(int){
Child ret(value);
++value;
return childRef();
}
Child operator --(int){
Child ret(value);
--value;
return childRef();
}
bool operator!(){return !value;}
Child operator~(){return Child(~value);}
};
class Data {
public:
virtual ~Data() = default;
};
class intWrapper: public Data {
private:
int val;
public:
intWrapper(int val = 0) :
val(val) {
}
operator int &() {
return val;
}
int* operator &() {
return &val;
}
operator int() const {
return val;
}
operator int*() {
return &val;
}
};
class doubleWrapper: public Data, public Primitive<double, doubleWrapper> {
public:
doubleWrapper(double val = 0) {
this->value = val;
}
operator double &() {
return value;
}
double* operator &() {
return &value;
}
operator double() const {
return value;
}
operator double*() {
return &value;
}
};
inline int to_int(Data *d) {
return static_cast<intWrapper*>(d)->operator int();
}
} //namespace T_MESH
#endif //_BASICS_H

View File

@ -1,178 +0,0 @@
/****************************************************************************
* 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 <stdio.h>
#include <math.h>
#include <limits.h>
#include <float.h>
#ifdef USE_HYBRID_KERNEL
#ifdef USE_CGAL_LAZYNT
#include <CGAL/Gmpq.h>
#include <CGAL/Lazy_exact_nt.h>
#include <CGAL/number_utils.h>
typedef CGAL::Lazy_exact_nt<CGAL::Gmpq> 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 <mpirxx.h>
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 <cstdint>
#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

View File

@ -1,145 +0,0 @@
/****************************************************************************
* 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

View File

@ -1,94 +0,0 @@
/****************************************************************************
* 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:
Data **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 a<b, >0 if a>b or 0 if a=b.
virtual int compare(const Data *a, const Data *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(Data *e);
int isEmpty() const {return (numels == 0);} //!< Returns TRUE if the heap is empty
Data *getHead() const {return heap[1];} //!< Returns the first element of the heap
Data *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

View File

@ -1,50 +0,0 @@
/****************************************************************************
* 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

View File

@ -1,183 +0,0 @@
/****************************************************************************
* 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 <stdio.h>
#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

View File

@ -1,301 +0,0 @@
/****************************************************************************
* 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 <stdio.h>
#include <float.h>
#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

View File

@ -1,299 +0,0 @@
/****************************************************************************
* 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
Data *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 <a, *this, b> 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

View File

@ -1,39 +0,0 @@
/****************************************************************************
* 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

View File

@ -1,59 +0,0 @@
/****************************************************************************
* 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

View File

@ -1,252 +0,0 @@
/****************************************************************************
* 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
Data *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

View File

@ -1,133 +0,0 @@
/****************************************************************************
* 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 Data *e1, const Data *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

View File

@ -1,804 +0,0 @@
/****************************************************************************
* 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<<b)))
#define IS_BIT(a,b) ((a)->mask & ((unsigned char)(1<<b)))
#define UNMARK_BIT(a,b) ((a)->mask &= (~((unsigned char)(1<<b))))
#define TAG_SHARPEDGE(a) (MARK_BIT((a),7))
#define IS_SHARPEDGE(a) (IS_BIT((a),7))
#define UNTAG_SHARPEDGE(a) (UNMARK_BIT((a),7))
// Errors from loading
#define IO_CANTOPEN 10
#define IO_FORMAT 20
#define IO_UNKNOWN 30
#define IO_CSAVE_OVERALL 0
#define IO_CSAVE_PERFACE 1
#define IO_CSAVE_PERVERTEX 2
#define IO_CSAVE_PERFACE_INDEXED 3
#define IO_CSAVE_PERVERTEX_INDEXED 4
} //namespace T_MESH
#endif //_TIN_H

View File

@ -1,40 +0,0 @@
/****************************************************************************
* 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_H
#define _TMESH_H
#include "tmesh_kernel.h"
#include "vertex.h"
#include "edge.h"
#include "triangle.h"
#include "tin.h"
#endif //_TMESH_H

View File

@ -1,218 +0,0 @@
/****************************************************************************
* 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 _TRIANGLE_H
#define _TRIANGLE_H
#include "tmesh.h"
namespace T_MESH
{
//! Triangle of a Basic_TMesh.
//! This class represents a triangle of a triangulation. Each Triangle has
//! an orientation (clockwise or counter-clockwise) due to the order in
//! which its edges are stored in the class. When looking at the triangle
//! so that (e1, e2, e3) are sorted counter-clockwise, the normal at the
//! triangle points towards the observer. The field mask is useful for
//! assigning up to 256 different states to the edge.
class Triangle : public Data
{
public :
Edge *e1, *e2, *e3; //!< Edges of the triangle
Data* info; //!< Further information
unsigned char mask; //!< bit-mask for marking purposes
Triangle();
Triangle(Edge *, Edge *, Edge *); //!< Constructor
//! Returns true only if object is a basic Triangle. All the reimplementations must return false.
TMESH_VIRTUAL bool isBaseType() const { return true; }
bool isLinked() const {return (e1!=NULL);} //!< TRUE if properly linked
//! Inverts the orientation of the triangle
TMESH_VIRTUAL void invert() { p_swap((void **)(&e2), (void **)(&e3)); }
Vertex *v1() const {return e1->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

View File

@ -1,223 +0,0 @@
/****************************************************************************
* 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

File diff suppressed because it is too large Load Diff

View File

@ -1,395 +0,0 @@
/****************************************************************************
* 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 <string.h>
#include <stdlib.h>
#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 (!(Mx<mp.x || mx>Mp.x || My<mp.y || my>Mp.y || Mz<mp.z || mz>Mp.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<iter_count; n++) growSelection();
removeSelectedTriangles();
removeSmallestComponents();
TMesh::quiet = true; fillSmallBoundaries(E.numels(), false); TMesh::quiet = qstatus;
coordBackApproximation();
remints_selectTrianglesInCubes(this);
}
if (iter_count > max_iters) return false;
return true;
}
} //namespace T_MESH

View File

@ -1,797 +0,0 @@
/****************************************************************************
* 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 <stdlib.h>
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; i<nt; n=n->next(), 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(); i<nt; i++, n=n->next()) 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(); i<nt; n=n->next(), 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(); i<nt; i++)
{
t = ((Triangle *)n->data);
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; i<nt; i++, m=m->next())
{
t = ((Triangle *)m->data);
if (t->overlaps() || t->isExactlyDegenerate()) break;
}
if (i<nt)
{
TMesh::warning("Re-triangulation failed. Restoring..\n");
for (m=T.head(), i=0; i<nt; i++, m=m->next())
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;
doubleWrapper 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((&reg), t, n) MARK_VISIT(t);
}
else FOREACHTRIANGLE(t, n) if (IS_VISITED(t)) reg.appendHead(t);
printf("%d\n",reg.numels());
FOREACHVTTRIANGLE((&reg), 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 /= double(nee); v->info = new doubleWrapper(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((&reg), t, n)
{
vc = t->getCenter();
sv1 = (*(doubleWrapper *)t->v1()->info);
sv2 = (*(doubleWrapper *)t->v2()->info);
sv3 = (*(doubleWrapper *)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 doubleWrapper(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((Data *)v->info); v->info = NULL; MARK_BIT(v, 5);}
FOREACHVVVERTEX((&interior_vertices), v, n) { delete((Data *)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 (c1<c2)
{
t = EulerEdgeTriangle(e, gve); MARK_VISIT(t);
pl1 -= gve->length();
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

File diff suppressed because it is too large Load Diff

View File

@ -1,168 +0,0 @@
/****************************************************************************
* 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);
}

View File

@ -1,241 +0,0 @@
/****************************************************************************
* 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 <limits>
#include <cmath>
namespace T_MESH
{
#ifndef NAN
#define NAN std::numeric_limits<double>::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

View File

@ -1,219 +0,0 @@
/****************************************************************************
* 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

View File

@ -1,122 +0,0 @@
/****************************************************************************
* 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 <stdio.h>
#include <stdlib.h>
#include "heap.h"
namespace T_MESH
{
abstractHeap::abstractHeap(int size)
{
heap = new Data *[size+1];
numels = 0;
maxels = size;
positions = NULL;
}
abstractHeap::~abstractHeap()
{
delete(heap);
}
int abstractHeap::upheap(int k)
{
if (k < 2) return k;
Data *t = heap[k];
int fk = (k%2)?((k-1)/2):(k/2);
Data *f = heap[fk];
if (compare(t, f) <= 0)
{
heap[k] = f;
heap[fk] = t;
if (positions != NULL)
{
positions[to_int(f)] = k;
positions[to_int(t)] = fk;
}
return upheap(fk);
}
return k;
}
int abstractHeap::downheap(int k)
{
int j;
Data *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++;
Data *f = heap[j];
if (compare(t, f) >= 0)
{
heap[k] = f;
heap[j] = t;
if (positions != NULL)
{
positions[to_int(f)] = k;
positions[to_int(t)] = j;
}
return downheap(j);
}
return k;
}
int abstractHeap::insert(Data *t)
{
if (numels == maxels) return -1;
heap[++numels] = t;
if (positions != NULL) positions[to_int(t)] = numels;
return upheap(numels);
}
Data *abstractHeap::removeHead()
{
Data *t = heap[1];
if (positions != NULL) positions[to_int(t)] = 0;
heap[1] = heap[numels--];
if (numels)
{
if (positions != NULL) positions[to_int(heap[1])] = 1;
downheap(1);
}
return t;
}
} //namespace T_MESH

View File

@ -1,89 +0,0 @@
/****************************************************************************
* 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 <vector>
#include <algorithm>
#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

View File

@ -1,301 +0,0 @@
/****************************************************************************
* 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 <stdio.h>
#include <stdlib.h>
#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; i<n; i++) appendTail(d[i]);
}
///////////////////////// Destructor //////////////////////////
List::~List()
{
while (l_head != NULL) removeCell(l_head);
}
////////////////// Append an element //////////////////
void List::appendHead(const Data *d)
{
l_head = new Node(NULL, d, l_head);
if (l_tail == NULL) l_tail = l_head;
l_numels++;
}
void List::appendTail(const Data *d)
{
l_tail = new Node(l_tail, d, NULL);
if (l_head == NULL) l_head = l_tail;
l_numels++;
}
void List::insertAfter(Node *b, const Data *d)
{
Node *nn = new Node(b, d, b->next());
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; i<l_numels; i++, n=n->n_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

View File

@ -1,794 +0,0 @@
/****************************************************************************
* 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 <math.h>
#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 = (i<m)?(i+mq):(m+iq);
a_im = a[im];
il = (i<l)?(i+lq):(l+iq);
a_il = a[il];
a[il] = a_il*cosx - a_im*sinx;
a[im] = a_il*sinx + a_im*cosx;
}
ilv++; imv++;
v_ilv = v[ilv]; v_imv = v[imv];
v[ilv] = cosx*v_ilv - sinx*v_imv;
v[imv] = sinx*v_ilv + cosx*v_imv;
}
x = a_lm*sincos; x+=x;
a[ll] = a_ll*cosx_2 + a_mm*sinx_2 - x;
a[mm] = a_ll*sinx_2 + a_mm*cosx_2 + x;
a[lm] = 0.0;
thr = FABS( thr - a_lm_2 );
}
}
}
// Step 5: index conversion and copy eigen values
// back from Fortran to C++
a++;
for( i=0; i<3; i++ ) { k = i + (i*(i+1))/2; eigen_val[i] = a[k];}
// Step 6: sort the eigen values and eigen vectors
for( i=0; i<3; i++ ) index[i] = i;
for( i=0; i<(3-1); i++ ) {
x = eigen_val[i];
k = i;
for( j=i+1; j<3; j++ ) if( x < eigen_val[j] ) { k = j; x = eigen_val[j];}
eigen_val[k] = eigen_val[i];
eigen_val[i] = x;
jj = index[k];
index[k] = index[i];
index[i] = jj;
}
// Step 7: save the eigen vectors
v++; // back from Fortran to to C++
ij = 0;
for( k=0; k<3; k++ ) for(ik = index[k]*3, i=0; i<3; i++ ) eigen_vec[ij++] = v[ik++];
}
// Computes the eigenvalues of the matrix
void SymMatrix3x3::getEigenvalues(double *L1, double *L2, double *L3) const
{
double a11 = M[0], a12 = M[1], a22 = M[2], a13 = M[3], a23 = M[4], a33 = M[5];
double c0 = (a11*a22*a33)+(2.0*a12*a13*a23)-(a11*a23*a23)-(a22*a13*a13)-(a33*a12*a12); // Mat. Determinant
double c1 = a11*a22-a12*a12+a11*a33-a13*a13+a22*a33-a23*a23;
double c2 = a11+a22+a33;
double a = (3*c1-c2*c2)/3.0;
double b = (9*c1*c2-2*c2*c2*c2-27*c0)/27.0;
double Q = ((b*b)/4.0)+((a*a*a)/27.0);
if (Q>1.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<l3)?(l2):(l3); *L3=(l2<l3)?(l3):(l2);}
else if (l2<=l1 && l2<=l3) {*L1=l2; *L2=(l1<l3)?(l1):(l3); *L3=(l1<l3)?(l3):(l1);}
else {*L1=l3; *L2=(l1<l2)?(l1):(l2); *L3=(l1<l2)?(l2):(l1);}
}
// Computes the eigenvector corresp. to the minimum eigenvalue
double SymMatrix3x3::getMinEigenvector(double *x, double *y, double *z) const
{
double a11 = M[0], a12 = M[1], a22 = M[2], a13 = M[3], a23 = M[4], a33 = M[5];
double l, l1, l2, l3, c0, c1, c2;
getEigenvalues(&l, &l2, &l3);
if (l==l3 && l==l2) {*x = 1; *y = *z = 0; return l;}
a11-=l; a22-=l; a33-=l;
double u11 = a22*a33-a23*a23, u12 = a13*a23-a12*a33, u13 = a12*a23-a13*a22;
double u22 = a11*a33-a13*a13, u23 = a12*a13-a23*a11, u33 = a11*a22-a12*a12;
l1 = u11*u11+u12*u12+u13*u13;
l2 = u12*u12+u22*u22+u23*u23;
l3 = u13*u13+u23*u23+u33*u33;
if (l1>=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

View File

@ -1,700 +0,0 @@
/****************************************************************************
* 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 <math.h>
#ifdef SPECIFY_FP_PRECISION
#include <float.h>
#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[5], C1[8], C2[12], D[16];
double B3, u[5], 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);
}

View File

@ -1,653 +0,0 @@
/****************************************************************************
* 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 <stdlib.h>
#include <limits.h>
#include <errno.h>
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 (x<s.x) return true; else if (x>s.x) return false;
if (y<s.y) return true; else if (y>s.y) return false;
if (z<s.z) return true; else return false;
}
// This can be used with jqsort
int xyzCompare(const Data *a, const Data *b)
{
coord c;
if ((c=(((Point *)a)->x - ((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

View File

@ -1,259 +0,0 @@
/****************************************************************************
* 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 <stdlib.h>
#include <string.h>
#include <stdarg.h>
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", int((clock() - beginning_time)));
}
} //namespace T_MESH

View File

@ -1,209 +0,0 @@
#include "tmesh.h"
#include <stdlib.h>
#include <string.h>
#include <time.h>
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 = new intWrapper(i);
while (triList.numels())
{
t = (Triangle *)triList.popHead();
if ((s = t->t1()) != NULL && s->info == NULL) { triList.appendHead(s); s->info = new intWrapper(i); }
if ((s = t->t2()) != NULL && s->info == NULL) { triList.appendHead(s); s->info = new intWrapper(i); }
if ((s = t->t3()) != NULL && s->info == NULL) { triList.appendHead(s); s->info = new intWrapper(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; 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);
}
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 (; i<argc; i++)
{
if (i<argc-1) par = (float)atof(argv[i+1]); else par = 0;
if (!strcmp(argv[i], "-x")) skip_if_fixed = true;
else if (!strcmp(argv[i], "-a")) join_multiple_components = true;
else if (!strcmp(argv[i], "-j")) stl_output = true;
else if (argv[i][0] == '-') TMesh::warning("%s - Unknown operation.\n", argv[i]);
if (par) i++;
}
sprintf(infilename, "%s", argv[1]);
if (stl_output) strcpy(extension, ".stl");
if (argc>2 && 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;
}

View File

@ -1,427 +0,0 @@
/****************************************************************************
* 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 (la<lb) return -1;
if (la>lb) 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 (va2<va1) { tmp = va1; va1 = va2; va2 = tmp; }
if (vb2<vb1) { tmp = vb1; vb1 = vb2; vb2 = tmp; }
if (va1<vb1) return -1;
if (va1>vb1) return 1;
if (va2<vb2) return -1;
if (va2>vb2) 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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,410 +0,0 @@
/****************************************************************************
* 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 <stdlib.h>
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

View File

@ -1,682 +0,0 @@
/****************************************************************************
* 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 <stdlib.h>
#include <errno.h>
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

View File

@ -214,8 +214,6 @@ 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
@ -262,7 +260,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 meshfix ${wxWidgets_LIBRARIES})
target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui GLEW::GLEW OpenGL::GL hidapi libcurl ${wxWidgets_LIBRARIES})
if (MSVC)
target_link_libraries(libslic3r_gui Setupapi.lib)

View File

@ -694,20 +694,8 @@ 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()->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)
{
if (is_windows10())
return nullptr;
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);
[](wxCommandEvent&) { obj_list()->fix_through_netfabb(); }, "", menu,
[]() {return plater()->can_fix_through_netfabb(); }, m_parent);
return menu_item;
}
@ -935,7 +923,6 @@ 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);
}

View File

@ -92,7 +92,6 @@ 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);

View File

@ -28,7 +28,6 @@
#include <wx/numformatter.h>
#include "slic3r/Utils/FixModelByWin10.hpp"
#include "slic3r/Utils/FixModelByMeshFix.hpp"
#ifdef __WXMSW__
#include "wx/uiaction.h"
@ -917,7 +916,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())
repair_mesh(rmaNetfabb);
fix_through_netfabb();
else if (evt_context_menu)
show_context_menu(evt_context_menu); // show context menu for "Name" column too
}
@ -4032,7 +4031,7 @@ void ObjectList::rename_item()
update_name_in_model(item);
}
void ObjectList::repair_mesh(ObjectList::REPAIR_MESH_ALG alg)
void ObjectList::fix_through_netfabb()
{
// 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.
@ -4052,28 +4051,28 @@ void ObjectList::repair_mesh(ObjectList::REPAIR_MESH_ALG alg)
// clear selections from the non-broken models if any exists
// and than fill names of models to repairing
if (vol_idxs.empty()) {
#if !FIX_MESH_ALWAYS
#if !FIX_THROUGH_NETFABB_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_MESH_ALWAYS
#endif // FIX_THROUGH_NETFABB_ALWAYS
for (int obj_idx : obj_idxs)
model_names.push_back(object(obj_idx)->name);
}
else {
ModelObject* obj = object(obj_idxs.front());
#if !FIX_MESH_ALWAYS
#if !FIX_THROUGH_NETFABB_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_MESH_ALWAYS
#endif // FIX_THROUGH_NETFABB_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, alg](const int obj_idx, const int vol_idx,
auto fix_and_update_progress = [this, plater, model_names](const int obj_idx, const int vol_idx,
int model_idx,
wxProgressDialog& progress_dlg,
std::vector<std::string>& succes_models,
@ -4092,19 +4091,8 @@ void ObjectList::repair_mesh(ObjectList::REPAIR_MESH_ALG alg)
plater->clear_before_change_mesh(obj_idx);
std::string res;
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;
if (!fix_model_by_win10_sdk_gui(*(object(obj_idx)), vol_idx, progress_dlg, msg, res))
return false;
wxGetApp().plater()->changed_mesh(obj_idx);
plater->changed_mesh(obj_idx);
@ -4120,19 +4108,19 @@ void ObjectList::repair_mesh(ObjectList::REPAIR_MESH_ALG alg)
return true;
};
Plater::TakeSnapshot snapshot(plater, _L("Repair model mesh"));
Plater::TakeSnapshot snapshot(plater, _L("Fix through NetFabb"));
// Open a progress dialog.
wxProgressDialog progress_dlg(_L("Repair model mesh"), "", 100, find_toplevel_parent(plater),
wxProgressDialog progress_dlg(_L("Fixing through NetFabb"), "", 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_MESH_ALWAYS
#if !FIX_THROUGH_NETFABB_ALWAYS
if (object(obj_idx)->get_repaired_errors_count(vol_idx) == 0)
continue;
#endif // FIX_MESH_ALWAYS
#endif // FIX_THROUGH_NETFABB_ALWAYS
if (!fix_and_update_progress(obj_idx, vol_idx, model_idx, progress_dlg, succes_models, failed_models))
break;
model_idx++;
@ -4165,7 +4153,7 @@ void ObjectList::repair_mesh(ObjectList::REPAIR_MESH_ALG alg)
}
if (msg.IsEmpty())
msg = _L("Repairing was canceled");
plater->get_notification_manager()->push_notification(NotificationType::RepairMeshFinished, NotificationManager::NotificationLevel::PrintInfoShortNotificationLevel, boost::nowide::narrow(msg));
plater->get_notification_manager()->push_notification(NotificationType::NetfabbFinished, NotificationManager::NotificationLevel::PrintInfoShortNotificationLevel, boost::nowide::narrow(msg));
}
void ObjectList::simplify()

View File

@ -37,7 +37,7 @@ typedef std::pair<coordf_t, coordf_t> t_layer_height_range;
typedef std::map<t_layer_height_range, ModelConfig> t_layer_config_ranges;
// Manifold mesh may contain self-intersections, so we want to always allow fixing the mesh.
#define FIX_MESH_ALWAYS 1
#define FIX_THROUGH_NETFABB_ALWAYS 1
namespace GUI {
@ -367,8 +367,7 @@ public:
void instances_to_separated_objects(const int obj_idx);
void split_instances();
void rename_item();
enum REPAIR_MESH_ALG{rmaNetfabb, rmaMeshfix};
void repair_mesh(REPAIR_MESH_ALG alg);
void fix_through_netfabb();
void simplify();
void update_item_error_icon(const int obj_idx, int vol_idx) const ;

View File

@ -130,7 +130,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
if (m_fix_throught_netfab_bitmap->GetBitmap().GetRefData() == wxNullBitmap.GetRefData())
return;
wxGetApp().obj_list()->repair_mesh(ObjectList::rmaNetfabb);
wxGetApp().obj_list()->fix_through_netfabb();
update_warning_icon_state(wxGetApp().obj_list()->get_mesh_errors_info());
});

View File

@ -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 finished model repairing (blocking proccess)
RepairMeshFinished,
// information about netfabb is finished repairing model (blocking proccess)
NetfabbFinished,
// Short meesage to fill space between start and finish of export
ExportOngoing,
};

View File

@ -1935,7 +1935,7 @@ struct Plater::priv
bool can_split_to_volumes() const;
bool can_arrange() const;
bool can_layers_editing() const;
bool can_repair_mesh() const;
bool can_fix_through_netfabb() 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_repair_mesh() const
bool Plater::priv::can_fix_through_netfabb() const
{
std::vector<int> obj_idxs, vol_idxs;
sidebar->obj_list()->get_selection_indexes(obj_idxs, vol_idxs);
#if FIX_MESH_ALWAYS
#if FIX_THROUGH_NETFABB_ALWAYS
// Fixing always.
return ! obj_idxs.empty() || ! vol_idxs.empty();
#else // FIX_MESH_ALWAYS
#else // FIX_THROUGH_NETFABB_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_repair_mesh() const
if (model.objects[obj_idx]->get_repaired_errors_count(vol_idx) > 0)
return true;
return false;
#endif // FIX_MESH_ALWAYS
#endif // FIX_THROUGH_NETFABB_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_repair_mesh() const { return p->can_repair_mesh(); }
bool Plater::can_fix_through_netfabb() const { return p->can_fix_through_netfabb(); }
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(); }

View File

@ -309,7 +309,7 @@ public:
bool can_increase_instances() const;
bool can_decrease_instances() const;
bool can_set_instance_to_object() const;
bool can_repair_mesh() const;
bool can_fix_through_netfabb() const;
bool can_simplify() const;
bool can_split_to_objects() const;
bool can_split_to_volumes() const;

View File

@ -1,403 +0,0 @@
#include "tmesh.h"
#include "FixModelByMeshFix.hpp"
#include "libslic3r/TriangleMesh.hpp"
#include "libslic3r/Model.hpp"
#include <string.h>
#include <atomic>
#include <chrono>
#include <cstdint>
#include <condition_variable>
#include <exception>
#include <string>
#include <thread>
#include <boost/filesystem.hpp>
#include <boost/nowide/convert.hpp>
#include <boost/nowide/cstdio.hpp>
#include <boost/thread.hpp>
#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 <wx/msgdlg.h>
#include <wx/progdlg.h>
namespace Slic3r {
class RepairCanceledException: public std::exception {
public:
const char* what() const throw () {
return "Model repair has been cancelled";
}
};
class RepairFailedException: public std::exception {
public:
const char* what() const throw () {
return "Model repair has failed";
}
};
namespace detail {
using namespace T_MESH;
static int ensure_tmesh_initialization = (TMesh::init(), 0);
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 = new intWrapper(i);
while (triList.numels())
{
t = (Triangle *)triList.popHead();
if ((s = t->t1()) != NULL && s->info == NULL) {triList.appendHead(s); s->info = new intWrapper(i);}
if ((s = t->t2()) != NULL && s->info == NULL) {triList.appendHead(s); s->info = new intWrapper(i);}
if ((s = t->t3()) != NULL && s->info == NULL) {triList.appendHead(s); s->info = new intWrapper(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<Vertex*>(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<T_MESH::Vertex*>(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<T_MESH::Triangle*>(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<bool> &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<bool> canceled = false;
std::atomic<bool> finished = false;
std::vector<ModelVolume*> 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<std::mutex> 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<TriangleMesh> meshes_repaired;
meshes_repaired.reserve(volumes.size());
for (ModelVolume *mv : volumes) {
std::vector<indexed_triangle_set> parts = its_split(mv->mesh().its);
unsigned percent_per_part = 95 / parts.size();
for (size_t part_idx = 0; part_idx < parts.size(); ++part_idx) {
unsigned progress_part_base = part_idx * percent_per_part;
detail::Basic_TMesh_Adapter tin { };
on_progress(L("Loading source model"), progress_part_base);
if (canceled)
throw RepairCanceledException();
tin.load_indexed_triangle_set(parts[part_idx]);
tin.boundaries();
on_progress(L("Join closest components"), unsigned(progress_part_base + 0.1 * percent_per_part));
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"),
unsigned(progress_part_base + 0.2 * percent_per_part));
if (canceled)
throw RepairCanceledException();
tin.removeSmallestComponents();
// Fill holes
on_progress(L("Check holes"), unsigned(progress_part_base + 0.3 * percent_per_part));
if (canceled)
throw RepairCanceledException();
if (tin.boundaries()) {
on_progress(L("Patch small holes"),
unsigned(progress_part_base + 0.4 * percent_per_part));
if (canceled)
throw RepairCanceledException();
tin.fillSmallBoundaries(0, true);
}
on_progress(L("Geometry check"), unsigned(progress_part_base + 0.5 * percent_per_part));
if (canceled)
throw RepairCanceledException();
// Run geometry correction
if (!tin.boundaries()) {
int iteration = 0;
on_progress(L("Start iterative correction"),
unsigned(progress_part_base + 0.55 * percent_per_part));
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"),
progress_part_base
+ percent_per_part * std::min(0.9, 0.6 + iteration * 0.08)); // majority of objects will 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<std::mutex> 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;
}
}

View File

@ -1,20 +0,0 @@
#ifndef SRC_SLIC3R_UTILS_FIXMODELBYMESHFIX_HPP_
#define SRC_SLIC3R_UTILS_FIXMODELBYMESHFIX_HPP_
#include <string>
#include <wx/string.h>
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_ */

View File

@ -326,8 +326,9 @@ public:
// fix_result containes a message if fixing failed
bool fix_model_by_win10_sdk_gui(ModelObject &model_object, int volume_idx, wxProgressDialog& progress_dialog, const wxString& msg_header, std::string& fix_result)
{
std::mutex mtx;
std::condition_variable condition;
std::mutex mutex;
std::condition_variable condition;
std::unique_lock<std::mutex> lock(mutex);
struct Progress {
std::string message;
int percent = 0;
@ -346,8 +347,8 @@ bool fix_model_by_win10_sdk_gui(ModelObject &model_object, int volume_idx, wxPro
// (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<std::mutex> lock(mtx);
auto on_progress = [&mutex, &condition, &ivolume, &volumes, &progress](const char *msg, unsigned prcnt) {
std::lock_guard<std::mutex> lk(mutex);
progress.message = msg;
progress.percent = (int)floor((float(prcnt) + float(ivolume) * 100.f) / float(volumes.size()));
progress.updated = true;
@ -423,7 +424,6 @@ bool fix_model_by_win10_sdk_gui(ModelObject &model_object, int volume_idx, wxPro
}
});
while (! finished) {
std::unique_lock<std::mutex> 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_dialog.Update(progress.percent-1, msg_header + _(progress.message)))