pax_global_header 0000666 0000000 0000000 00000000064 14336667610 0014526 g ustar 00root root 0000000 0000000 52 comment=70ca24a9b4e6d8aa05e8572e768110dad9b4d47b
admesh-0.98.5/ 0000775 0000000 0000000 00000000000 14336667610 0013072 5 ustar 00root root 0000000 0000000 admesh-0.98.5/.gitignore 0000664 0000000 0000000 00000000451 14336667610 0015062 0 ustar 00root root 0000000 0000000 aclocal.m4
admesh
admesh-*/
ar-lib
autom4te.cache
config.guess
config.h
config.h.in
config.log
config.status
config.sub
configure
depcomp
.deps/
install-sh
*.la
.libs/
libtool
*.lo
ltmain.sh
Makefile
Makefile.in
missing
mkinstalldirs
m4/
*.o
*.pc
stamp-h1
*.tar.*
compat_reports
AUTHORS
ChangeLog
admesh-0.98.5/.travis.yml 0000664 0000000 0000000 00000001075 14336667610 0015206 0 ustar 00root root 0000000 0000000 language: c
matrix:
include:
- compiler: gcc
os: linux
env: EXTRA=--enable-werror
- compiler: clang
os: linux
- compiler: gcc
os: linux
arch: arm64
addons:
apt:
packages:
- libtool
- compiler: clang
os: osx
script:
- ./autogen.sh
- ./configure $EXTRA
- make
- cd .libs
- if [[ $TRAVIS_OS_NAME == linux ]]; then LD_LIBRARY_PATH=. && export LD_LIBRARY_PATH ; fi
- if [[ $TRAVIS_OS_NAME == osx ]]; then DYLD_LIBRARY_PATH=. && export DYLD_LIBRARY_PATH ; fi
- ./admesh ../block.stl
admesh-0.98.5/COMPILED.xml 0000664 0000000 0000000 00000000150 14336667610 0015004 0 ustar 00root root 0000000 0000000
dev
src/stl.h
.libs/libadmesh.so
admesh-0.98.5/COPYING 0000664 0000000 0000000 00000043254 14336667610 0014135 0 ustar 00root root 0000000 0000000 GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
Copyright (C)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
admesh-0.98.5/ChangeLog.old 0000664 0000000 0000000 00000003310 14336667610 0015416 0 ustar 00root root 0000000 0000000 Tue Aug 1 03:25:46 PDT 1995 Anthony Martin
* Fix it so that big endian systems write a little endian file
Also, write null characters after the label in stl_write_binary
Tue Aug 1 03:25:46 PDT 1995 Anthony Martin
* Change stl_fill_holes so that duplicate facets are not likely
to be generated. Should improve quality of filled holes.
Wed Aug 2 16:56:03 PDT 1995 Anthony Martin
* Add support for generating shared vertices
* Move fclose(stl->fp) to stl_open()
Tue Oct 31 13:56:25 PST 1995 Anthony Martin
* Don't use area any more to see whether or not the normal should
be calculated. Just go ahead and calculate the normal, and let the
code in stl_normalize vector() take care of the case of a normal
with zero area.
* Changes call to stl_check_normal_vector() in stl_add_facet() so
that it doesn't check the normal vector after it adds it. Just
caused accounting error for normals fixed.
Wed Nov 1 08:39:17 PST 1995 Anthony Martin
* Initialize normal vector to 0,0,0 when a new facet is added.
Sat Jan 20 23:43:01 PST 1996 Anthony Martin
* stlinit.c Initialize pointers to NULL. Check whether they have
been allocated before free()ing them. Duh.
Thu Jan 25 16:02:57 PST 1996 Anthony Martin
* shared.c Add fclose(fp); to stl_write_off().
* stl_io.c Add dxf write capability
* shared.c Add vrml write capability
Fri Jul 26 11:31:50 PDT 1996 Anthony Martin
* Update normals after rotation
* Add code to calculate the volume of the part
admesh-0.98.5/INSTALL 0000664 0000000 0000000 00000000726 14336667610 0014130 0 ustar 00root root 0000000 0000000 To install ADMesh, you will need a system with a c compiler.
Do the following:
1. Get the file admesh-x.xx.tar.gz
2. Extract the archive. i.e. type something like the following:
tar -zxvf admesh-x.xx.tar.gz
The source files will be extracted into a directory called admesh-x.xx
3. cd admesh-x.xx
4. type the following:
./configure
make
su -c 'make install'
That should do it. Standard options for configure script and make are provided.
admesh-0.98.5/Makefile.am 0000664 0000000 0000000 00000001773 14336667610 0015136 0 ustar 00root root 0000000 0000000 ACLOCAL_AMFLAGS = -I m4
AM_LDFLAGS = @DEAD_STRIP@
AM_LDFLAGS += -no-undefined
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libadmesh.pc
doc_DATA = \
admesh-doc.txt \
README.md \
COPYING \
AUTHORS \
ChangeLog \
ChangeLog.old
EXTRA_DIST = \
$(doc_DATA) \
INSTALL \
libadmesh.pc.in \
block.stl
CLEANFILES = libadmesh.pc
dist_man_MANS = admesh.1
bin_PROGRAMS = admesh
admesh_SOURCES = \
src/admesh.c
admesh_LDADD = \
libadmesh.la
# libadmesh libtool versioning
LIBADMESH_CURRENT=1
LIBADMESH_REVISION=0
LIBADMESH_AGE=0
pkginclude_HEADERS = src/stl.h
lib_LTLIBRARIES = libadmesh.la
libadmesh_la_SOURCES = \
src/connect.c \
src/normals.c \
src/shared.c \
src/stlinit.c \
src/stl_io.c \
src/util.c \
src/portable_endian.h
libadmesh_la_LDFLAGS = \
$(AM_LDFLAGS) \
-version-info $(LIBADMESH_CURRENT):$(LIBADMESH_REVISION):$(LIBADMESH_AGE)
distclean-local:
rm -rf *.cache *~
AUTHORS:
git log --format='%aN <%aE>' | sort -u > $@
ChangeLog:
./fortag.sh >$@
dist-hook: AUTHORS ChangeLog
admesh-0.98.5/README.md 0000664 0000000 0000000 00000011637 14336667610 0014361 0 ustar 00root root 0000000 0000000 About this release and repository:
----------------------------------
ADMesh was released as an application in 1995/96. Not much happened since then.
As the code of this project might bring use for others, this "fork" was created
to provide a shared library. Once done that, I've collected lots of bugfixes
from the world around us.
No further development will be done, but bugs will be resolved, if possible.
Don't patch this project downstream but use this code, so all can benefit from
the changes. Pull requests are welcome, but be sure to generate no warnings.
Grab the 0.98.5 tarball:
https://github.com/admesh/admesh/releases/download/v0.98.5/admesh-0.98.5.tar.gz
About ADMesh:
-------------
ADMesh is a program for processing triangulated solid meshes. Currently,
ADMesh only reads the STL file format that is used for rapid prototyping
applications, although it can write STL, VRML, OFF, and DXF files.
Additional information regarding the underlying algorithms of ADMesh
can be found in Anthony Martin's Masters Thesis available from here:
http://www.varlog.com/admesh-htm/ADMeshThesis.zip
Features:
---------
* Read and write binary and ASCII STL files
* Check STL files for flaws (i.e. unconnected facets, bad normals)
* Repair facets by connecting nearby facets that are within a given tolerance
* Fill holes in the mesh by adding facets.
* Repair normal directions (i.e. facets should be CCW)
* Repair normal values (i.e. should be perpendicular to facet with length=1)
* Remove degenerate facets (i.e. facets with 2 or more vertices equal)
* Translate in x, y, and z directions
* Rotate about the x, y, and z axes
* Mirror about the xy, yz, and xz planes
* Scale the part by a factor
* Merge 2 STL files into one
* Write an OFF file
* Write a VRML file
* Write a DXF file
* Calculate the volume of a part
ADMesh outputs the following statistics after processing:
````
================= Results produced by ADMesh version 0.98.5 =================
Input file : sphere.stl
File type : Binary STL file
Header : Processed by ADMesh version 0.98.5
============== Size ==============
Min X = -1.334557, Max X = 1.370952
Min Y = -1.377953, Max Y = 1.377230
Min Z = -1.373225, Max Z = 1.242838
========= Facet Status ========== Original ============ Final ====
Number of facets : 3656 3656
Facets with 1 disconnected edge : 18 0
Facets with 2 disconnected edges : 3 0
Facets with 3 disconnected edges : 0 0
Total disconnected facets : 21 0
=== Processing Statistics === ===== Other Statistics =====
Number of parts : 1 Volume : 10.889216
Degenerate facets : 0
Edges fixed : 24
Facets removed : 0
Facets added : 0
Facets reversed : 0
Backwards edges : 0
Normals fixed : 0
````
There are two different algorithms used for fixing unconnected facets. The
first algorithm finds an unconnected edge, and then checks nearby within a
given tolerance for another unconnected edge. It then fixes edges within
tolerance. Some meshes can be completely fixed just using this method. If
there are still unconnected facets after this "nearby check" has been done,
then a second algorithm is used. This algorithm just fills any holes in the
mesh by adding facets until all of the holes are filled. Using these two
algorithms, almost any imperfect STL file can be "fixed" 100% so that there
are 0 unconnected facets. Whether the resulting mesh is what you really
want is another question since there is no way for ADMesh to add information
that isn't there.
At this point ADMesh is only command-line driven and has no windowing
capabilities. This should make it extremely easy to port to any UNIX-like
system, and it shouldn't have any problems compiling on Windows NT, and some
people have had success compiling it under DOS or Windows using DJGPP.
ADMesh was developed on a 486/66 with 16Mb running the Linux operating system.
It has also been compiled and run on the following systems:
SunOS 4.1.3
IRIX 5.2
Please let me know about successes or failures with other systems.
On my Linux system with 16Mb of memory, I can easily process files that have
up to about 200,000 facets. Files larger than this can be processed, but
the system begins to slow down significantly due to swapping. A system with
more memory will be able to process greater numbers of facets more easily.
Following are some indications of process times:
* 40,000 facets: 10 seconds
* 80,000 facets: 20 seconds
* 160,000 facets: 50 seconds
* 320,000 facets: 13 minutes (heavy swapping occurred)
Note that those times were calculated around 1996.
ADMesh is free but copyrighted software. It is distributed under the terms
of the GNU General Public License (GPL). Details of the GPL are in the file
COPYING that comes with the ADMesh software package.
admesh-0.98.5/SYSTEM.xml 0000664 0000000 0000000 00000000203 14336667610 0014633 0 ustar 00root root 0000000 0000000
installed
/usr/include/admesh/stl.h
/usr/lib64/libadmesh.so
admesh-0.98.5/admesh-doc.txt 0000664 0000000 0000000 00000053560 14336667610 0015650 0 ustar 00root root 0000000 0000000 Note: This manual is slightly out of date. Specifically it doesn't
mention OFF, VRML, or DXF files. However, there isn't much to know about
these options. If you just type 'admesh --help' a list of options will be
printed.
This document describes the use of ADMesh version 0.93. ADMesh is a program
for processing triangulated solid meshes. Currently, ADMesh only reads the
STL file format that is used for rapid prototyping applications, although it
can write STL, VRML, OFF, and DXF files.
For information about compiling ADMesh, see the file INSTALL. The file
README contains a brief description of ADMesh along with its features and
capabilities.
ADMesh is Copyrighted software and is distributed under the terms of the GNU
General Public License. See the file COPYING for license information.
Invoking ADMesh
===============
ADMesh is executed as follows:
admesh [OPTION]... file
By default, ADMesh performs all of the mesh checking and repairing options
on the input file. This means that is checks exact, nearby,
remove-unconnected, fill-holes, normal-directions, and normal-values. The
file type (ASCII or binary) is automatically detected. The input file is
not modified unless it is specified by the --write option. If the following
command line was input:
admesh sphere.stl
The file sphere.stl would be opened and read, it would be checked and fixed
if necessary, and the results of processing would be printed out. The
results would not be saved.
The default value for tolerance is the length of the shortest edge of the
mesh. The default number of iterations is 2, and the default increment is
0.01% of the diameter of a sphere that encloses the entire mesh.
If any of the options --exact, --nearby, --remove-unconnected, --fill-holes,
--normal-directions, --reverse-all, --normal-values, or --no-check are
given, then no other checks besides that one will be done unless they are
specified or unless they are required by ADMesh before the specified check
can be done. For example the following command line:
admesh --remove-unconnected sphere.stl
would first do an exact check because it is required, and then the
unconnected facets would be removed. The results would be printed and no
other checks would be done.
Examples
========
To perform all checks except for nearby, the following command line would be
used:
admesh --exact --remove-unconnected --fill-holes \
--normal-directions --normal-values sphere.stl
Actually, since the exact check is required by ADMesh before
remove-unconnected, and remove-unconnected is required before --fill-holes,
the above command line could be shortened as follows with the same results:
admesh --fill-holes --normal-directions --normal-values sphere.stl
And again the same results could be achieved using the short options:
admesh -fudev sphere.stl
or
admesh -fdv sphere.stl
The following command lines do the same thing:
admesh sphere.stl
admesh -fundev sphere.stl
admesh -f -u -n -d -e -v sphere.stl
since the -fundev options are implied by default. To eliminate one of the
checks, just remove the letter of the check to eliminate from the "word"
'fundev'.
About
Option Summary
==============
ADMesh supports the following options, grouped by type.
*Mesh Transformation and Manipulation Options*
--x-rotate=angle Rotate CCW about x-axis by angle degrees
--y-rotate=angle Rotate CCW about y-axis by angle degrees
--z-rotate=angle Rotate CCW about z-axis by angle degrees
--xy-mirror Mirror about the xy plane
--yz-mirror Mirror about the yz plane
--xz-mirror Mirror about the xz plane
--scale=factor Scale the file by factor (multiply by factor)
--translate=x,y,z Translate the file to x, y, and z
--merge=name Merge file called name with input file
*Mesh Checking and Repairing Options*
-e, --exact Only check for perfectly matched edges
-n, --nearby Find and connect nearby facets. Correct bad facets
-t, --tolerance=tol Initial tolerance to use for nearby check = tol
-i, --iterations=i Number of iterations for nearby check = i
-m, --increment=inc Amount to increment tolerance after iteration=inc
-u, --remove-unconnected Remove facets that have 0 neighbors
-f, --fill-holes Add facets to fill holes
-d, --normal-directions Check and fix direction of normals(ie cw, ccw)
--reverse-all Reverse the directions of all facets and normals
-v, --normal-values Check and fix normal values
-c, --no-check Don't do any check on input file
*File Output Options*
-b, --write-binary-stl=name Output a binary STL file called name
-a, --write-ascii-stl=name Output an ascii STL file called name
*Miscellaneous Options*
--help Display this help and exit
--version Output version information and exit
Mesh Transformation and Manipulation Options
============================================
'--x-rotate=angle'
'--y-rotate=angle'
'--z-rotate=angle'
Rotate the entire mesh about the specified axis by the given number of
degrees. The rotation is counter-clockwise about the axis as seen by
looking along the positive axis towards the origin, assuming that the
coordinate system is as follows:
y^
|
|
|
+------->
/ x
/
z/
'--xy-mirror'
'--yz-mirror'
'--xz-mirror'
Mirror the mesh about the specified plane. Mirroring involves reversing
the sign of all of the coordinates in a particular axis. For example, to
mirror a mesh about the xy plane, the signs of all of the z coordinates
in the mesh are reversed.
'--scale=factor'
Scale the mesh by the given factor. This multiplies all of the
coordinates by the specified number. This option could be used to change
the "units" (there are no units explicitly specified in an STL file) of
the mesh. For example, to change a part from inches to millimeters, just
use the --scale=25.4 option.
'--translate=x,y,z'
Translate the mesh to the position x,y,z. This moves the minimum x, y,
and z values of the mesh to the specified position. For example, given a
mesh that has the following initial minimum and maximum coordinate values:
Min X = 4.000000, Max X = 5.000000
Min Y = 1.000000, Max Y = 3.000000
Min Z = -7.000000, Max Z = -2.000000
if the option --translate=1,2,3 is specified, the final values will be:
Min X = 1.000000, Max X = 2.000000
Min Y = 2.000000, Max Y = 4.000000
Min Z = 3.000000, Max Z = 8.000000
The translate option is often used to translate a mesh with arbitrary
minimum and maximum coordinates to 0,0,0. Usually, translation is also
required when merging two files.
'merge=name'
Merge the specified file with the input file. No translation is done, so
if, for example, a file was merged with itself, the resulting file would
end up with two meshes exactly the same, occupying exactly the same
space. So generally, translations need to be done to the files to be
merged so that when the two meshes are merged into one, the two resulting
parts are properly spaced. If you know the nature of the parts to be
merged, it is possible to "nest" one part inside the other. Note,
however, that no warnings will be given if one part intersects with the
other.
It is possible to place one part against another, with no space in
between, but you will still end up with two separately defined parts. If
such a mesh was made on a rapid-prototyping machine, the result would
depend on the nature of the machine. Machines that use a photopolymer
would produce a single solid part because the two parts would be "bonded"
during the build process. Machines that use a cutting process would
yield two or more parts.
A copy of a mesh can be made by using the --merge and --translate options
at the same time. For example, given a file called block.stl with the
following size:
Min X = 0.000000, Max X = 2.000000
Min Y = 0.000000, Max Y = 2.000000
Min Z = 0.000000, Max Z = 2.000000
to create a file called 2blocks.stl that contains two of the parts
separated by 1 unit in the x direction, the following command line would
be used:
admesh
--translate=3,0,0 --merge=block.stl --write-binary=2blocks.stl block.stl
This would yield a binary STL file called 2blocks.stl with the following
size:
Min X = 0.000000, Max X = 5.000000
Min Y = 0.000000, Max Y = 2.000000
Min Z = 0.000000, Max Z = 2.000000
Mesh Checking and Repairing Options
===================================
'-e', '--exact'
Check each facet of the mesh for its 3 neighbors. Since each facet is a
triangle, there should be exactly 3 neighboring facets for every facet in
the mesh. Since the mesh defines a solid, there should be no unconnected
edges in the mesh. When this option is specified, the 3 neighbors of
every facet are searched for and, if found, the neighbors are added to an
internal list that keeps track of the neighbors of each facet. A facet
is only considered a neighbor if two of its vertices EXACTLY match two of
the vertices of another facet. That means that there must be 0
difference between the x, y, and z coordinates of the two vertices of the
first facet and the two vertices of the second facet.
Degenerate facets (facets with two or more vertices equal to each other)
are removed during the exact check. No other changes are made to the
mesh. An exact check is always done before any of the other checking and
repairing options even if --exact isn't specified. There is one
exception to this rule; no exact check needs to be done before the
--normal-values option.
'-n', '--nearby'
'-t', '--tolerance=tol'
'-i', '--iterations=i'
'-m', '--increment=inc'
Checks each unconnected facet of the mesh for facets that are almost
connected but not quite. Due to round-off errors and other factors, it
is common for a mesh to have facets with neighbors that are very close
but don't match exactly. Often, this difference is only in the 8th
decimal place of the vertices, but these facets will not show up as
neighbors during the exact check. This option finds these nearby
neighbors and it changes their vertices so that they match exactly. The
exact check is alway done before the nearby check, so only facets that
remain unconnected after the exact check are candidates for the nearby
check.
The --tolerance=tol option is used to specify the distance that is
searched for the neighboring facet. By default, this value is set
automatically by ADMesh to be the length of the shortest edge of the
mesh. This value is used because it makes it unlikely for a facet that
shouldn't be a neighbor to be found and matched as a neighbor. If the
tolerance is too big, then some facets could end up connected that should
definitely not be connected. This could create a "mobius part" that is
not a valid solid. If this occurs, it can be seen by checking the value
of "Backwards edges" that is printed after processing. (The number of
backwards edges should be 0 for a valid solid.)
The --iterations=i and --increment=inc options are used together to
gradually connect nearby facets using progressively larger tolerances.
This helps to prevent incorrect connects but can also allow larger
tolerances to be used. The --iterations option gives the number of times
that facets are checked for nearby facets, each time using a larger
tolerance. The --increment=inc option gives the amount that the
tolerance is increased after each iteration. The number specified by
'inc' is added to the tolerance that was used in the previous iteration.
If all of the facets are connected, no further nearby checks will be
done.
'-f', '--fill-holes'
Fill holes in the mesh by adding facets. This is done after the exact
check and after nearby check (if any nearby check is done). If there are
still unconnected facets, then facets will be added to the mesh,
connecting the unconnected facets, until all of the holes have been
filled. This is guaranteed to completely completely fix all unconnected
facets. However, the resulting mesh may or may not be what the user
expects.
'-d', '--normal-directions'
Check and fix if necessary the directions of the facets. This only deals
with whether the vertices of all the facets are oriented clockwise or
counterclockwise, it doesn't check or modify the value of the normal
vector. Every facet should have its vertices defined in a
counterclockwise order when looked at from the outside of the part. This
option will orient all of the vertices so that they are all facing in the
same direction. However, it it possible that this option will make all
of the facets facet inwards instead of outwards. The algorithm tries to
get a clue of which direction is inside and outside by checking the value
of the normal vector so the chance is very good that the resulting mesh
will be correct. However, it doesn't explicitly check to find which
direction is inside and which is outside. In the future, I might write
code to explicitly check for the inside and the outside, but until then,
the current algorithm gets it right most of the time.
'--reverse-all'
Reverses the directions of all of the facets and normals. If the
--normal-directions option ended up making all of the facets facet
inwards instead of outwards, then this option can be used to reverse all
of the facets. It is up to the user to determine if the facets are
facing inwards and if they need reversing. In future versions of ADMesh,
this process may be automated. This option also fixes and updates the
normal vector for each facet.
'-v', '--normal-values'
Checks and fixes if necessary the normal vectors of every facet. The
normal vector will point outward for a counterclockwise facet. The
length of the normal vector will be 1.
'-c', '--no-check'
Don't do any checks or modifications to the input file. By default,
ADMesh performs all processes (exact, nearby, remove_unconnected,
fill-holes, normal-directions, and normals-values) on the input file. If
the --no-check option is specified, no checks or modifications will be
made on the input file. This could be used, for example, to translate an
ASCII STL file to a binary STL file, with no modifications made. A
command line such as the following might be used:
admesh
--no-check --write-binary-stl=newblock.stl --translate=0,0,0 block.stl
This would open the file block.stl, would translate it to 0,0,0 no checks
would be performed and a binary STL file of the translated mesh would be
written to newblock.stl.
'-b', '--write-binary-stl=name'
'-a,' '--write-ascii-stl=name'
Write a binary STL file with the name specified. The input file is not
modified by ADMesh so the only way to preserve any modifications that
have been made to the input file is to use one of the --write options. If
the user wants to modify (overwrite) the input file, then the input file
can also be specified for the --write option. For example, to convert an
input ASCII STL file called sphere.stl to a binary STL file, overwriting
the original file, and performing no checks, the following command line
would be used:
admesh --write-binary-stl=sphere.stl --no-check sphere.stl
'--help'
Display the possible command line options with a short description, and
then exit.
'--version'
Show the version information for ADMesh, and then exit.
ADMesh Output
=============
After ADMesh has processed a mesh, it prints out a page of information about
that mesh. The output looks like the following:
================= Results produced by ADMesh version 0.95 =================
Input file : sphere.stl
File type : Binary STL file
Header : Processed by ADMesh version 0.95
============== Size ==============
Min X = -1.334557, Max X = 1.370952
Min Y = -1.377953, Max Y = 1.377230
Min Z = -1.373225, Max Z = 1.242838
========= Facet Status ========== Original ============ Final ====
Number of facets : 3656 3656
Facets with 1 disconnected edge : 18 0
Facets with 2 disconnected edges : 3 0
Facets with 3 disconnected edges : 0 0
Total disconnected facets : 21 0
=== Processing Statistics === ===== Other Statistics =====
Number of parts : 1 Volume : 10.889216
Degenerate facets : 0
Edges fixed : 24
Facets removed : 0
Facets added : 0
Facets reversed : 0
Backwards edges : 0
Normals fixed : 0
Description of Output
====================
The following describes the output information line by line.
Input file : sphere.stl
The name of the file that was read.
File type : Binary STL file
The type of file. Currently, the only two possibilities are Binary STL
file and ASCII STL file. ADMesh automatically detect the type of input
file.
Header : Processed by ADMesh version 0.95
The first 80 characters of the STL file. The first 80 bytes of a binary
STL file or the first line of an ASCII STL file can contain some text.
Usually, the CAD system that has created that file, or the last program
to process that file puts its name in the header. ADMesh puts its own
string in the header when it saves the file.
============== Size ==============
Min X = -1.334557, Max X = 1.370952
Min Y = -1.377953, Max Y = 1.377230
Min Z = -1.373225, Max Z = 1.242838
This section gives the boundaries of the mesh. The mesh will fit just
inside a box of this size.
========= Facet Status ========== Original ============ Final ====
Number of facets : 3656 3656
Facets with 1 disconnected edge : 18 0
Facets with 2 disconnected edges : 3 0
Facets with 3 disconnected edges : 0 0
Total disconnected facets : 21 0
Information about the quality of the mesh before, and after processing by
ADMesh. The number of facets gives an idea about the complexity and
accuracy of the mesh. Disconnected facets will fall into 3 categories.
Some facets will have only one disconnected edge, some will have 2 edges
disconnected, and some will have all 3 edges disconnected. Of course,
for a valid solid mesh, there should be 0 disconnected facets.
=== Processing Statistics ===
Number of parts : 1
This is the total number of separate parts in the file. This can be a
very useful indication of whether your file is correct. Sometimes, the
user of the CAD system that creates the mesh just puts several pieces
together next to each other, and then outputs the mesh. This might not
cause any problems for a rapid prototyping system that uses a
photopolymer because all of the parts will be "glued" together anyway
during the build. However, a rapid prototyping machine that is based on
cutting will cut each one of the parts individually and the result will
be many parts that need to be glued together. The number of parts is
counted during --normal-directions, so if the --normal-directions check
is eliminated, then the number of parts will read 0.
Degenerate facets : 0
Number of degenerate facets in the input file. A degenerate facet is a
facet that has two or more vertices exactly the same. The resulting
facet is just a line (if two vertices are the same) or could even be a
point (if all 3 vertices are the same). These facets add no information
to the file and are removed by ADMesh during processing.
Edges fixed : 24
The total number of edges that were fixed by moving the vertices slightly
during the nearby check. This does not include facets that were added by
--fill-holes.
Facets removed : 0
The total number of facets removed. There are two cases where facets
might be removed. First, all degenerate facets in the input file are
removed. Second, if there are any completely unconnected facets (facets
with 3 disconnected edges) after the exact and nearby checks, then these
facets will be removed by --remove-unconnected.
Facets added : 0
Number of facets that have been added by ADMesh to the original mesh.
Facets are only added during --fill-holes. So this number represents the
number of facets that had to be added to fill all of the holes, if any,
in the original mesh.
Facets reversed : 0
The number of facets that were reversed during --normal-directions. This
only relates to the order of the vertices of the facet (CW or CCW), it
has nothing to do with the value of the normal vector.
Backwards edges : 0
The number of edges that are backwards. After ADMesh has finished all of
the checks and processing, it verifies the results. If the
normal-directions check has been done then the NUMBER OF BACKWARDS EDGES
SHOULD BE 0. If it is not, then a "mobius part" has been created which
is not a valid solid mesh. In this case the mesh can be processed again,
but a smaller tolerance on the nearby check should be used or no nearby
check should be done.
Normals fixed : 0
The number of normal vectors that have been fixed. During the
normal-values check, ADMesh calculates the value of every facet and
compares the result with the normal vector from the input file. If the
result is not within a fixed tolerance, then the normal is said to be
fixed. Actually, for consistency, every normal vector is rewritten with
the new calculated normal, even if the original normal was within
tolerance. However, the normals that were within tolerance are not
counted by normals fixed.
admesh-0.98.5/admesh.1 0000664 0000000 0000000 00000013470 14336667610 0014422 0 ustar 00root root 0000000 0000000 .TH ADMESH "1" 21/10/2013 "User Commands"
.SH NAME
ADMesh - a program for processing triangulated solid meshes
.SH SYNOPSIS
.B admesh
[\fIOPTION\fR]... \fIfile\fR
.SH DESCRIPTION
ADMesh is a program for processing triangulated solid meshes. Currently, ADMesh only reads the STL file format that is used for rapid prototyping applications, although it can write STL, VRML, OFF, and DXF files.
By default, ADMesh performs all of the mesh checking and repairing options
on the input file. This means that is checks exact, nearby,
remove-unconnected, fill-holes, normal-directions, and normal-values. The
file type (ASCII or binary) is automatically detected. The input file is
not modified unless it is specified by the \fB--write\fP option. If the following
command line was input:
.B admesh sphere.stl
The file sphere.stl would be opened and read, it would be checked and fixed
if necessary, and the results of processing would be printed out. The
results would not be saved.
The default value for tolerance is the length of the shortest edge of the
mesh. The default number of iterations is 2, and the default increment is
0.01% of the diameter of a sphere that encloses the entire mesh.
If any of the options \fB--exact\fP, \fB--nearby\fP, \fB--remove-unconnected\fP, \fB--fill-holes\fP,
\fB--normal-directions\fP, \fB--reverse-all\fP, \fB--normal-values\fP, or \fB--no-check\fP are
given, then no other checks besides that one will be done unless they are
specified or unless they are required by ADMesh before the specified check
can be done. For example the following command line:
.B admesh --remove-unconnected sphere.stl
would first do an exact check because it is required, and then the
unconnected facets would be removed. The results would be printed and no
other checks would be done.
.SH OPTIONS
.TP
\fB\-\-x\-rotate\fR=\fIangle\fR
Rotate CCW about x\-axis by angle degrees
.TP
\fB\-\-y\-rotate\fR=\fIangle\fR
Rotate CCW about y\-axis by angle degrees
.TP
\fB\-\-z\-rotate\fR=\fIangle\fR
Rotate CCW about z\-axis by angle degrees
.TP
\fB\-\-xy\-mirror\fR
Mirror about the xy plane
.TP
\fB\-\-yz\-mirror\fR
Mirror about the yz plane
.TP
\fB\-\-xz\-mirror\fR
Mirror about the xz plane
.TP
\fB\-\-scale\fR=\fIfactor\fR
Scale the file by factor (multiply by factor)
.TP
\fB\-\-translate\fR=\fIx\fR,y,z
Translate the file to x, y, and z
.TP
\fB\-\-merge\fR=\fIname\fR
Merge file called name with input file
.TP
\fB\-e\fR, \fB\-\-exact\fR
Only check for perfectly matched edges
.TP
\fB\-n\fR, \fB\-\-nearby\fR
Find and connect nearby facets. Correct bad facets
.TP
\fB\-t\fR, \fB\-\-tolerance\fR=\fItol\fR
Initial tolerance to use for nearby check = tol
.TP
\fB\-i\fR, \fB\-\-iterations\fR=\fIi\fR
Number of iterations for nearby check = i
.TP
\fB\-m\fR, \fB\-\-increment\fR=\fIinc\fR
Amount to increment tolerance after iteration=inc
.HP
\fB\-u\fR, \fB\-\-remove\-unconnected\fR Remove facets that have 0 neighbors
.TP
\fB\-f\fR, \fB\-\-fill\-holes\fR
Add facets to fill holes
.TP
\fB\-d\fR, \fB\-\-normal\-directions\fR
Check and fix direction of normals(ie cw, ccw)
.TP
\fB\-\-reverse\-all\fR
Reverse the directions of all facets and normals
.TP
\fB\-v\fR, \fB\-\-normal\-values\fR
Check and fix normal values
.TP
\fB\-c\fR, \fB\-\-no\-check\fR
Don't do any check on input file
.TP
\fB\-b\fR, \fB\-\-write\-binary\-stl\fR=\fIname\fR
Output a binary STL file called name
.TP
\fB\-a\fR, \fB\-\-write\-ascii\-stl\fR=\fIname\fR
Output an ascii STL file called name
.TP
\fB\-\-write\-off\fR=\fIname\fR
Output a Geomview OFF format file called name
.TP
\fB\-\-write\-dxf\fR=\fIname\fR
Output a DXF format file called name
.TP
\fB\-\-write\-vrml\fR=\fIname\fR
Output a VRML format file called name
.TP
\fB\-\-help\fR
Display this help and exit
.TP
\fB\-\-version\fR
Output version information and exit
.PP
The functions are executed in the same order as the options shown here.
So check here to find what happens if, for example, \fB\-\-translate\fR and \fB\-\-merge\fR
options are specified together. The order of the options specified on the
command line is not important.
.SH EXAMPLES
To perform all checks except for nearby, the following command line would be
used:
.B admesh --exact --remove-unconnected --fill-holes --normal-directions --normal-values sphere.stl
Actually, since the \fBexact\fP check is required by ADMesh before
\fBremove-unconnected\fP, and \fBremove-unconnected\fP is required before \fB--fill-holes\fP,
the above command line could be shortened as follows with the same results:
.B admesh --fill-holes --normal-directions --normal-values sphere.stl
And again the same results could be achieved using the short options:
.B admesh -fudev sphere.stl
or
.B admesh -fdv sphere.stl
The following command lines do the same thing:
.B admesh sphere.stl
.B admesh -fundev sphere.stl
.B admesh -f -u -n -d -e -v sphere.stl
since the \fB-fundev\fP options are implied by default. To eliminate one of the
checks, just remove the letter of the check to eliminate from the "word" fundev.
.SH SEE ALSO
For more information about the options and output read
.B admesh-doc.txt
- it is usually located in /usr/share/doc/admesh-x.xx dir.
.SH COPYRIGHT
Copyright (C) 1995, 1996 Anthony D. Martin
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
admesh-0.98.5/autogen.sh 0000775 0000000 0000000 00000000233 14336667610 0015071 0 ustar 00root root 0000000 0000000 #!/bin/sh
case `uname` in Darwin*) glibtoolize --copy ;;
*) libtoolize --force --copy ;; esac
aclocal -I m4
autoheader
automake -a -c --foreign
autoconf
admesh-0.98.5/block.stl 0000664 0000000 0000000 00000006565 14336667610 0014724 0 ustar 00root root 0000000 0000000 SOLID Untitled1
FACET NORMAL 0.00000000E+00 0.00000000E+00 1.00000000E+00
OUTER LOOP
VERTEX -1.96850394E+00 1.96850394E+00 1.96850394E+00
VERTEX -1.96850394E+00 -1.96850394E+00 1.96850394E+00
VERTEX 1.96850394E+00 -1.96850394E+00 1.96850394E+00
ENDLOOP
ENDFACET
FACET NORMAL 0.00000000E+00 -0.00000000E+00 1.00000000E+00
OUTER LOOP
VERTEX 1.96850394E+00 -1.96850394E+00 1.96850394E+00
VERTEX 1.96850394E+00 1.96850394E+00 1.96850394E+00
VERTEX -1.96850394E+00 1.96850394E+00 1.96850394E+00
ENDLOOP
ENDFACET
FACET NORMAL 0.00000000E+00 -0.00000000E+00 -1.00000000E+00
OUTER LOOP
VERTEX 1.96850394E+00 1.96850394E+00 -1.96850394E+00
VERTEX 1.96850394E+00 -1.96850394E+00 -1.96850394E+00
VERTEX -1.96850394E+00 -1.96850394E+00 -1.96850394E+00
ENDLOOP
ENDFACET
FACET NORMAL 0.00000000E+00 0.00000000E+00 -1.00000000E+00
OUTER LOOP
VERTEX -1.96850394E+00 -1.96850394E+00 -1.96850394E+00
VERTEX -1.96850394E+00 1.96850394E+00 -1.96850394E+00
VERTEX 1.96850394E+00 1.96850394E+00 -1.96850394E+00
ENDLOOP
ENDFACET
FACET NORMAL -1.00000000E+00 0.00000000E+00 0.00000000E+00
OUTER LOOP
VERTEX -1.96850394E+00 1.96850394E+00 -1.96850394E+00
VERTEX -1.96850394E+00 -1.96850394E+00 -1.96850394E+00
VERTEX -1.96850394E+00 -1.96850394E+00 1.96850394E+00
ENDLOOP
ENDFACET
FACET NORMAL -1.00000000E+00 0.00000000E+00 0.00000000E+00
OUTER LOOP
VERTEX -1.96850394E+00 -1.96850394E+00 1.96850394E+00
VERTEX -1.96850394E+00 1.96850394E+00 1.96850394E+00
VERTEX -1.96850394E+00 1.96850394E+00 -1.96850394E+00
ENDLOOP
ENDFACET
FACET NORMAL 1.00000000E+00 0.00000000E+00 0.00000000E+00
OUTER LOOP
VERTEX 1.96850394E+00 1.96850394E+00 1.96850394E+00
VERTEX 1.96850394E+00 -1.96850394E+00 1.96850394E+00
VERTEX 1.96850394E+00 -1.96850394E+00 -1.96850394E+00
ENDLOOP
ENDFACET
FACET NORMAL 1.00000000E+00 0.00000000E+00 0.00000000E+00
OUTER LOOP
VERTEX 1.96850394E+00 -1.96850394E+00 -1.96850394E+00
VERTEX 1.96850394E+00 1.96850394E+00 -1.96850394E+00
VERTEX 1.96850394E+00 1.96850394E+00 1.96850394E+00
ENDLOOP
ENDFACET
FACET NORMAL 0.00000000E+00 -1.00000000E+00 0.00000000E+00
OUTER LOOP
VERTEX -1.96850394E+00 -1.96850394E+00 1.96850394E+00
VERTEX -1.96850394E+00 -1.96850394E+00 -1.96850394E+00
VERTEX 1.96850394E+00 -1.96850394E+00 -1.96850394E+00
ENDLOOP
ENDFACET
FACET NORMAL 0.00000000E+00 -1.00000000E+00 0.00000000E+00
OUTER LOOP
VERTEX 1.96850394E+00 -1.96850394E+00 -1.96850394E+00
VERTEX 1.96850394E+00 -1.96850394E+00 1.96850394E+00
VERTEX -1.96850394E+00 -1.96850394E+00 1.96850394E+00
ENDLOOP
ENDFACET
FACET NORMAL 0.00000000E+00 1.00000000E+00 0.00000000E+00
OUTER LOOP
VERTEX -1.96850394E+00 1.96850394E+00 -1.96850394E+00
VERTEX -1.96850394E+00 1.96850394E+00 1.96850394E+00
VERTEX 1.96850394E+00 1.96850394E+00 1.96850394E+00
ENDLOOP
ENDFACET
FACET NORMAL 0.00000000E+00 1.00000000E+00 0.00000000E+00
OUTER LOOP
VERTEX 1.96850394E+00 1.96850394E+00 1.96850394E+00
VERTEX 1.96850394E+00 1.96850394E+00 -1.96850394E+00
VERTEX -1.96850394E+00 1.96850394E+00 -1.96850394E+00
ENDLOOP
ENDFACET
ENDSOLID Untitled1
admesh-0.98.5/configure.ac 0000664 0000000 0000000 00000004411 14336667610 0015360 0 ustar 00root root 0000000 0000000 dnl Process this file with autoconf to produce a configure script.
AC_PREREQ([2.65])
# ====================
# Version informations
# ====================
m4_define([admesh_version_major],[0])
m4_define([admesh_version_minor],[98])
m4_define([admesh_version_micro],[5])
m4_define([admesh_version_suffix],[])
m4_define([admesh_version],[admesh_version_major.admesh_version_minor.admesh_version_micro''admesh_version_suffix])
# =============
# Automake init
# =============
AC_INIT([admesh],[admesh_version])
AC_CONFIG_MACRO_DIR([m4])
AM_CONFIG_HEADER([config.h])
AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz subdir-objects])
AM_SILENT_RULES([yes])
LT_INIT([disable-static pic-only])
AC_LANG([C])
# ===========================
# Find required base packages
# ===========================
AC_PROG_CC
AC_PROG_INSTALL
AC_PROG_LIBTOOL
AC_PROG_SED
AC_PROG_MKDIR_P
# =======================
# Platform specific setup
# =======================
AC_CANONICAL_HOST
case $host_os in
darwin* )
DEAD_STRIP="-Wl,-dead_strip"
;;
*)
DEAD_STRIP="-Wl,--gc-sections -Wl,--as-needed"
;;
esac
AC_SUBST(DEAD_STRIP)
# ================
# Check for cflags
# ================
AC_ARG_ENABLE([werror],
[AS_HELP_STRING([--enable-werror], [Treat all warnings as errors, useful for development @<:@default=disabled@:>@])],
[enable_werror="$enableval"],
[enable_werror=no]
)
AS_IF([test x"$enable_werror" != "xno"], [
CFLAGS="$CFLAGS -Werror"
CXXFLAGS="$CXXFLAGS -Werror"
])
AS_IF([test x"$GCC" = xyes], [
# Be tough with warnings and produce less careless code
CFLAGS="$CFLAGS -Wall -Wextra -pedantic -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2"
CXXFLAGS="$CXXFLAGS -Wall -Wextra -Wshadow -pedantic -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2"
])
# =========
# Find libs
# =========
AC_CHECK_LIB(m, main)
# =====================
# Prepare all .in files
# =====================
AC_CONFIG_FILES([
Makefile
libadmesh.pc
])
AC_OUTPUT
# ==============================================
# Display final informations about configuration
# ==============================================
AC_MSG_NOTICE([
==============================================================================
Build configuration:
werror: ${enable_werror}
==============================================================================
])
admesh-0.98.5/fortag.sh 0000775 0000000 0000000 00000000571 14336667610 0014716 0 ustar 00root root 0000000 0000000 #!/bin/bash
lasttag=''
total=''
for tag in `git for-each-ref --sort=committerdate --format='%(refname)' refs/tags`; do
if [[ x$lasttag != x ]]; then
e="${tag#refs/tags/v}: "
e="$e`git --no-pager log -1 --format=%ai $tag`\n\n"
e="$e`git --no-pager log --pretty=\"format: * %s\" $lasttag..$tag`\n\n"
total="$e$total"
fi
lasttag=$tag
done
echo -en "$total"
admesh-0.98.5/libadmesh.pc.in 0000664 0000000 0000000 00000000343 14336667610 0015753 0 ustar 00root root 0000000 0000000 prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: libadmesh
Description: Library for woring with admesh
Version: @VERSION@
Libs: -L${libdir} -ladmesh
Libs.private:
Cflags: -I${includedir}
admesh-0.98.5/src/ 0000775 0000000 0000000 00000000000 14336667610 0013661 5 ustar 00root root 0000000 0000000 admesh-0.98.5/src/admesh.c 0000664 0000000 0000000 00000033164 14336667610 0015275 0 ustar 00root root 0000000 0000000 /* ADMesh -- process triangulated solid meshes
* Copyright (C) 1995, 1996 Anthony D. Martin
* Copyright (C) 2013, 2014 several contributors, see AUTHORS
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Questions, comments, suggestions, etc to
* https://github.com/admesh/admesh/issues
*/
#include
#include
#include
#include "stl.h"
#include "config.h"
static void usage(int status, char *program_name);
int
main(int argc, char **argv) {
stl_file stl_in;
float tolerance = 0;
float increment = 0;
float x_trans;
float y_trans;
float z_trans;
float scale_factor = 0;
float rotate_x_angle = 0;
float rotate_y_angle = 0;
float rotate_z_angle = 0;
int c;
char *program_name;
char *binary_name = NULL;
char *ascii_name = NULL;
char *merge_name = NULL;
char *off_name = NULL;
char *dxf_name = NULL;
char *vrml_name = NULL;
int fixall_flag = 1; /* Default behavior is to fix all. */
int exact_flag = 0; /* All checks turned off by default. */
int tolerance_flag = 0; /* Is tolerance specified on cmdline */
int nearby_flag = 0;
int remove_unconnected_flag = 0;
int fill_holes_flag = 0;
int normal_directions_flag = 0;
int normal_values_flag = 0;
int reverse_all_flag = 0;
int write_binary_stl_flag = 0;
int write_ascii_stl_flag = 0;
int generate_shared_vertices_flag = 0;
int write_off_flag = 0;
int write_dxf_flag = 0;
int write_vrml_flag = 0;
int translate_flag = 0;
int scale_flag = 0;
int rotate_x_flag = 0;
int rotate_y_flag = 0;
int rotate_z_flag = 0;
int mirror_xy_flag = 0;
int mirror_yz_flag = 0;
int mirror_xz_flag = 0;
int merge_flag = 0;
int help_flag = 0;
int version_flag = 0;
int iterations = 2; /* Default number of iterations. */
int increment_flag = 0;
char *input_file = NULL;
int ret = 0;
enum {rotate_x = 1000, rotate_y, rotate_z, merge, help, version,
mirror_xy, mirror_yz, mirror_xz, scale, translate, reverse_all,
off_file, dxf_file, vrml_file
};
struct option long_options[] = {
{"exact", no_argument, NULL, 'e'},
{"nearby", no_argument, NULL, 'n'},
{"tolerance", required_argument, NULL, 't'},
{"iterations", required_argument, NULL, 'i'},
{"increment", required_argument, NULL, 'm'},
{"remove-unconnected", no_argument, NULL, 'u'},
{"fill-holes", no_argument, NULL, 'f'},
{"normal-directions", no_argument, NULL, 'd'},
{"normal-values", no_argument, NULL, 'v'},
{"no-check", no_argument, NULL, 'c'},
{"reverse-all", no_argument, NULL, reverse_all},
{"write-binary-stl", required_argument, NULL, 'b'},
{"write-ascii-stl", required_argument, NULL, 'a'},
{"write-off", required_argument, NULL, off_file},
{"write-dxf", required_argument, NULL, dxf_file},
{"write-vrml", required_argument, NULL, vrml_file},
{"translate", required_argument, NULL, translate},
{"scale", required_argument, NULL, scale},
{"x-rotate", required_argument, NULL, rotate_x},
{"y-rotate", required_argument, NULL, rotate_y},
{"z-rotate", required_argument, NULL, rotate_z},
{"xy-mirror", no_argument, NULL, mirror_xy},
{"yz-mirror", no_argument, NULL, mirror_yz},
{"xz-mirror", no_argument, NULL, mirror_xz},
{"merge", required_argument, NULL, merge},
{"help", no_argument, NULL, help},
{"version", no_argument, NULL, version},
{NULL, 0, NULL, 0}
};
program_name = argv[0];
while((c = getopt_long(argc, argv, "et:i:m:nufdcvb:a:",
long_options, (int *) 0)) != EOF) {
switch(c) {
case 0: /* If *flag is not null */
break;
case 'e':
exact_flag = 1;
fixall_flag = 0;
break;
case 'n':
nearby_flag = 1;
fixall_flag = 0;
break;
case 't':
tolerance_flag = 1;
tolerance = atof(optarg);
break;
case 'i':
iterations = atoi(optarg);
break;
case 'm':
increment_flag = 1;
increment = atof(optarg);
break;
case 'u':
remove_unconnected_flag = 1;
fixall_flag = 0;
break;
case 'f':
fill_holes_flag = 1;
fixall_flag = 0;
break;
case 'd':
normal_directions_flag = 1;
fixall_flag = 0;
break;
case 'v':
normal_values_flag = 1;
fixall_flag = 0;
break;
case 'c':
fixall_flag = 0;
break;
case reverse_all:
reverse_all_flag = 1;
fixall_flag = 0;
break;
case 'b':
write_binary_stl_flag = 1;
binary_name = optarg; /* I'm not sure if this is safe. */
break;
case 'a':
write_ascii_stl_flag = 1;
ascii_name = optarg; /* I'm not sure if this is safe. */
break;
case off_file:
generate_shared_vertices_flag = 1;
write_off_flag = 1;
off_name = optarg;
break;
case vrml_file:
generate_shared_vertices_flag = 1;
write_vrml_flag = 1;
vrml_name = optarg;
break;
case dxf_file:
write_dxf_flag = 1;
dxf_name = optarg;
break;
case translate:
translate_flag = 1;
sscanf(optarg, "%f,%f,%f", &x_trans, &y_trans, &z_trans);
break;
case scale:
scale_flag = 1;
scale_factor = atof(optarg);
break;
case rotate_x:
rotate_x_flag = 1;
rotate_x_angle = atof(optarg);
break;
case rotate_y:
rotate_y_flag = 1;
rotate_y_angle = atof(optarg);
break;
case rotate_z:
rotate_z_flag = 1;
rotate_z_angle = atof(optarg);
break;
case mirror_xy:
mirror_xy_flag = 1;
break;
case mirror_yz:
mirror_yz_flag = 1;
break;
case mirror_xz:
mirror_xz_flag = 1;
break;
case merge:
merge_flag = 1;
merge_name = optarg;
break;
case help:
help_flag = 1;
break;
case version:
version_flag = 1;
break;
default:
usage(1, program_name);
return 1;
}
}
if(help_flag) {
usage(0, program_name);
return 0;
}
if(version_flag) {
printf("ADMesh - version " VERSION "\n");
return 0;
}
if(optind == argc) {
printf("No input file name given.\n");
usage(1, program_name);
return 1;
} else {
input_file = argv[optind];
}
printf("\
ADMesh version " VERSION ", Copyright (C) 1995, 1996 Anthony D. Martin\n\
ADMesh comes with NO WARRANTY. This is free software, and you are welcome to\n\
redistribute it under certain conditions. See the file COPYING for details.\n");
printf("Opening %s\n", input_file);
stl_open(&stl_in, input_file);
stl_exit_on_error(&stl_in);
if(rotate_x_flag) {
printf("Rotating about the x axis by %f degrees...\n", rotate_x_angle);
stl_rotate_x(&stl_in, rotate_x_angle);
}
if(rotate_y_flag) {
printf("Rotating about the y axis by %f degrees...\n", rotate_y_angle);
stl_rotate_y(&stl_in, rotate_y_angle);
}
if(rotate_z_flag) {
printf("Rotating about the z axis by %f degrees...\n", rotate_z_angle);
stl_rotate_z(&stl_in, rotate_z_angle);
}
if(mirror_xy_flag) {
printf("Mirroring about the xy plane...\n");
stl_mirror_xy(&stl_in);
}
if(mirror_yz_flag) {
printf("Mirroring about the yz plane...\n");
stl_mirror_yz(&stl_in);
}
if(mirror_xz_flag) {
printf("Mirroring about the xz plane...\n");
stl_mirror_xz(&stl_in);
}
if(scale_flag) {
printf("Scaling by factor %f...\n", scale_factor);
stl_scale(&stl_in, scale_factor);
}
if(translate_flag) {
printf("Translating to %f, %f, %f ...\n", x_trans, y_trans, z_trans);
stl_translate(&stl_in, x_trans, y_trans, z_trans);
}
if(merge_flag) {
printf("Merging %s with %s\n", input_file, merge_name);
/* Open the file and add the contents to stl_in: */
stl_open_merge(&stl_in, merge_name);
}
stl_repair(&stl_in,
fixall_flag,
exact_flag,
tolerance_flag,
tolerance,
increment_flag,
increment,
nearby_flag,
iterations,
remove_unconnected_flag,
fill_holes_flag,
normal_directions_flag,
normal_values_flag,
reverse_all_flag,
1);
if(generate_shared_vertices_flag) {
printf("Generating shared vertices...\n");
stl_generate_shared_vertices(&stl_in);
}
if(write_off_flag) {
printf("Writing OFF file %s\n", off_name);
stl_write_off(&stl_in, off_name);
if (stl_in.error) {
stl_clear_error(&stl_in);
ret = 1;
}
}
if(write_dxf_flag) {
printf("Writing DXF file %s\n", dxf_name);
stl_write_dxf(&stl_in, dxf_name, "Created by ADMesh version " VERSION);
if (stl_in.error) {
stl_clear_error(&stl_in);
ret = 1;
}
}
if(write_vrml_flag) {
printf("Writing VRML file %s\n", vrml_name);
stl_write_vrml(&stl_in, vrml_name);
if (stl_in.error) {
stl_clear_error(&stl_in);
ret = 1;
}
}
if(write_ascii_stl_flag) {
printf("Writing ascii file %s\n", ascii_name);
stl_write_ascii(&stl_in, ascii_name,
"Processed by ADMesh version " VERSION);
if (stl_in.error) {
stl_clear_error(&stl_in);
ret = 1;
}
}
if(write_binary_stl_flag) {
printf("Writing binary file %s\n", binary_name);
stl_write_binary(&stl_in, binary_name,
"Processed by ADMesh version " VERSION);
if (stl_in.error) {
stl_clear_error(&stl_in);
ret = 1;
}
}
stl_stats_out(&stl_in, stdout, input_file);
stl_close(&stl_in);
if (ret)
fprintf(stderr, "Some part of the procedure failed, see the above log for more information about what happened.\n");
return ret;
}
static void
usage(int status, char *program_name) {
if(status != 0) {
fprintf(stderr, "Try '%s --help' for more information.\n", program_name);
} else {
printf("\n");
printf("ADMesh version " VERSION "\n");
printf("Copyright (C) 1995, 1996 Anthony D. Martin\n");
printf("Usage: %s [OPTION]... file\n", program_name);
printf("\n");
printf(" --x-rotate=angle Rotate CCW about x-axis by angle degrees\n");
printf(" --y-rotate=angle Rotate CCW about y-axis by angle degrees\n");
printf(" --z-rotate=angle Rotate CCW about z-axis by angle degrees\n");
printf(" --xy-mirror Mirror about the xy plane\n");
printf(" --yz-mirror Mirror about the yz plane\n");
printf(" --xz-mirror Mirror about the xz plane\n");
printf(" --scale=factor Scale the file by factor (multiply by factor)\n");
printf(" --translate=x,y,z Translate the file to x, y, and z\n");
printf(" --merge=name Merge file called name with input file\n");
printf(" -e, --exact Only check for perfectly matched edges\n");
printf(" -n, --nearby Find and connect nearby facets. Correct bad facets\n");
printf(" -t, --tolerance=tol Initial tolerance to use for nearby check = tol\n");
printf(" -i, --iterations=i Number of iterations for nearby check = i\n");
printf(" -m, --increment=inc Amount to increment tolerance after iteration=inc\n");
printf(" -u, --remove-unconnected Remove facets that have 0 neighbors\n");
printf(" -f, --fill-holes Add facets to fill holes\n");
printf(" -d, --normal-directions Check and fix direction of normals(ie cw, ccw)\n");
printf(" --reverse-all Reverse the directions of all facets and normals\n");
printf(" -v, --normal-values Check and fix normal values\n");
printf(" -c, --no-check Don't do any check on input file\n");
printf(" -b, --write-binary-stl=name Output a binary STL file called name\n");
printf(" -a, --write-ascii-stl=name Output an ascii STL file called name\n");
printf(" --write-off=name Output a Geomview OFF format file called name\n");
printf(" --write-dxf=name Output a DXF format file called name\n");
printf(" --write-vrml=name Output a VRML format file called name\n");
printf(" --help Display this help and exit\n");
printf(" --version Output version information and exit\n");
printf("\n");
printf("The functions are executed in the same order as the options shown here.\n");
printf("So check here to find what happens if, for example, --translate and --merge\n");
printf("options are specified together. The order of the options specified on the\n");
printf("command line is not important.\n");
}
}
admesh-0.98.5/src/connect.c 0000664 0000000 0000000 00000075544 14336667610 0015475 0 ustar 00root root 0000000 0000000 /* ADMesh -- process triangulated solid meshes
* Copyright (C) 1995, 1996 Anthony D. Martin
* Copyright (C) 2013, 2014 several contributors, see AUTHORS
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Questions, comments, suggestions, etc to
* https://github.com/admesh/admesh/issues
*/
#include
#include
#include
#include
#include "stl.h"
static void stl_match_neighbors_exact(stl_file *stl,
stl_hash_edge *edge_a, stl_hash_edge *edge_b);
static void stl_match_neighbors_nearby(stl_file *stl,
stl_hash_edge *edge_a, stl_hash_edge *edge_b);
static void stl_record_neighbors(stl_file *stl,
stl_hash_edge *edge_a, stl_hash_edge *edge_b);
static void stl_initialize_facet_check_exact(stl_file *stl);
static void stl_initialize_facet_check_nearby(stl_file *stl);
static void stl_load_edge_exact(stl_file *stl, stl_hash_edge *edge,
stl_vertex *a, stl_vertex *b);
static int stl_load_edge_nearby(stl_file *stl, stl_hash_edge *edge,
stl_vertex *a, stl_vertex *b, float tolerance);
static void insert_hash_edge(stl_file *stl, stl_hash_edge edge,
void (*match_neighbors)(stl_file *stl,
stl_hash_edge *edge_a, stl_hash_edge *edge_b));
static int stl_get_hash_for_edge(int M, stl_hash_edge *edge);
static int stl_compare_function(stl_hash_edge *edge_a, stl_hash_edge *edge_b);
static void stl_free_edges(stl_file *stl);
static void stl_remove_facet(stl_file *stl, int facet_number);
static void stl_change_vertices(stl_file *stl, int facet_num, int vnot,
stl_vertex new_vertex);
static void stl_which_vertices_to_change(stl_file *stl, stl_hash_edge *edge_a,
stl_hash_edge *edge_b, int *facet1, int *vertex1,
int *facet2, int *vertex2,
stl_vertex *new_vertex1, stl_vertex *new_vertex2);
static void stl_remove_degenerate(stl_file *stl, int facet);
extern int stl_check_normal_vector(stl_file *stl,
int facet_num, int normal_fix_flag);
static void stl_update_connects_remove_1(stl_file *stl, int facet_num);
void
stl_check_facets_exact(stl_file *stl) {
/* This function builds the neighbors list. No modifications are made
* to any of the facets. The edges are said to match only if all six
* floats of the first edge matches all six floats of the second edge.
*/
stl_hash_edge edge;
stl_facet facet;
int i;
int j;
if (stl->error) return;
stl->stats.connected_edges = 0;
stl->stats.connected_facets_1_edge = 0;
stl->stats.connected_facets_2_edge = 0;
stl->stats.connected_facets_3_edge = 0;
stl_initialize_facet_check_exact(stl);
for(i = 0; i < stl->stats.number_of_facets; i++) {
facet = stl->facet_start[i];
/* If any two of the three vertices are found to be exactally the same, call them degenerate and remove the facet. */
if( !memcmp(&facet.vertex[0], &facet.vertex[1],
sizeof(stl_vertex))
|| !memcmp(&facet.vertex[1], &facet.vertex[2],
sizeof(stl_vertex))
|| !memcmp(&facet.vertex[0], &facet.vertex[2],
sizeof(stl_vertex))) {
stl->stats.degenerate_facets += 1;
stl_remove_facet(stl, i);
i--;
continue;
}
for(j = 0; j < 3; j++) {
edge.facet_number = i;
edge.which_edge = j;
stl_load_edge_exact(stl, &edge, &facet.vertex[j],
&facet.vertex[(j + 1) % 3]);
insert_hash_edge(stl, edge, stl_match_neighbors_exact);
}
}
stl_free_edges(stl);
}
static void
stl_load_edge_exact(stl_file *stl, stl_hash_edge *edge,
stl_vertex *a, stl_vertex *b) {
float diff_x;
float diff_y;
float diff_z;
float max_diff;
if (stl->error) return;
diff_x = ABS(a->x - b->x);
diff_y = ABS(a->y - b->y);
diff_z = ABS(a->z - b->z);
max_diff = STL_MAX(diff_x, diff_y);
max_diff = STL_MAX(diff_z, max_diff);
stl->stats.shortest_edge = STL_MIN(max_diff, stl->stats.shortest_edge);
if(diff_x == max_diff) {
if(a->x > b->x) {
memcpy(&edge->key[0], a, sizeof(stl_vertex));
memcpy(&edge->key[3], b, sizeof(stl_vertex));
} else {
memcpy(&edge->key[0], b, sizeof(stl_vertex));
memcpy(&edge->key[3], a, sizeof(stl_vertex));
edge->which_edge += 3; /* this edge is loaded backwards */
}
} else if(diff_y == max_diff) {
if(a->y > b->y) {
memcpy(&edge->key[0], a, sizeof(stl_vertex));
memcpy(&edge->key[3], b, sizeof(stl_vertex));
} else {
memcpy(&edge->key[0], b, sizeof(stl_vertex));
memcpy(&edge->key[3], a, sizeof(stl_vertex));
edge->which_edge += 3; /* this edge is loaded backwards */
}
} else {
if(a->z > b->z) {
memcpy(&edge->key[0], a, sizeof(stl_vertex));
memcpy(&edge->key[3], b, sizeof(stl_vertex));
} else {
memcpy(&edge->key[0], b, sizeof(stl_vertex));
memcpy(&edge->key[3], a, sizeof(stl_vertex));
edge->which_edge += 3; /* this edge is loaded backwards */
}
}
}
static void
stl_initialize_facet_check_exact(stl_file *stl) {
int i;
if (stl->error) return;
stl->stats.malloced = 0;
stl->stats.freed = 0;
stl->stats.collisions = 0;
stl->M = 81397;
for(i = 0; i < stl->stats.number_of_facets ; i++) {
/* initialize neighbors list to -1 to mark unconnected edges */
stl->neighbors_start[i].neighbor[0] = -1;
stl->neighbors_start[i].neighbor[1] = -1;
stl->neighbors_start[i].neighbor[2] = -1;
}
stl->heads = (stl_hash_edge**)calloc(stl->M, sizeof(*stl->heads));
if(stl->heads == NULL) perror("stl_initialize_facet_check_exact");
stl->tail = (stl_hash_edge*)malloc(sizeof(stl_hash_edge));
if(stl->tail == NULL) perror("stl_initialize_facet_check_exact");
stl->tail->next = stl->tail;
for(i = 0; i < stl->M; i++) {
stl->heads[i] = stl->tail;
}
}
static void
insert_hash_edge(stl_file *stl, stl_hash_edge edge,
void (*match_neighbors)(stl_file *stl,
stl_hash_edge *edge_a, stl_hash_edge *edge_b)) {
stl_hash_edge *link;
stl_hash_edge *new_edge;
stl_hash_edge *temp;
int chain_number;
if (stl->error) return;
chain_number = stl_get_hash_for_edge(stl->M, &edge);
link = stl->heads[chain_number];
if(link == stl->tail) {
/* This list doesn't have any edges currently in it. Add this one. */
new_edge = (stl_hash_edge*)malloc(sizeof(stl_hash_edge));
if(new_edge == NULL) perror("insert_hash_edge");
stl->stats.malloced++;
*new_edge = edge;
new_edge->next = stl->tail;
stl->heads[chain_number] = new_edge;
return;
} else if(!stl_compare_function(&edge, link)) {
/* This is a match. Record result in neighbors list. */
match_neighbors(stl, &edge, link);
/* Delete the matched edge from the list. */
stl->heads[chain_number] = link->next;
free(link);
stl->stats.freed++;
return;
} else {
/* Continue through the rest of the list */
for(;;) {
if(link->next == stl->tail) {
/* This is the last item in the list. Insert a new edge. */
new_edge = (stl_hash_edge*)malloc(sizeof(stl_hash_edge));
if(new_edge == NULL) perror("insert_hash_edge");
stl->stats.malloced++;
*new_edge = edge;
new_edge->next = stl->tail;
link->next = new_edge;
stl->stats.collisions++;
return;
} else if(!stl_compare_function(&edge, link->next)) {
/* This is a match. Record result in neighbors list. */
match_neighbors(stl, &edge, link->next);
/* Delete the matched edge from the list. */
temp = link->next;
link->next = link->next->next;
free(temp);
stl->stats.freed++;
return;
} else {
/* This is not a match. Go to the next link */
link = link->next;
stl->stats.collisions++;
}
}
}
}
static int
stl_get_hash_for_edge(int M, stl_hash_edge *edge) {
return ((edge->key[0] / 23 + edge->key[1] / 19 + edge->key[2] / 17
+ edge->key[3] /13 + edge->key[4] / 11 + edge->key[5] / 7 ) % M);
}
static int
stl_compare_function(stl_hash_edge *edge_a, stl_hash_edge *edge_b) {
if(edge_a->facet_number == edge_b->facet_number) {
return 1; /* Don't match edges of the same facet */
} else {
return memcmp(edge_a, edge_b, SIZEOF_EDGE_SORT);
}
}
void
stl_check_facets_nearby(stl_file *stl, float tolerance) {
stl_hash_edge edge[3];
stl_facet facet;
int i;
int j;
if (stl->error) return;
if( (stl->stats.connected_facets_1_edge == stl->stats.number_of_facets)
&& (stl->stats.connected_facets_2_edge == stl->stats.number_of_facets)
&& (stl->stats.connected_facets_3_edge == stl->stats.number_of_facets)) {
/* No need to check any further. All facets are connected */
return;
}
stl_initialize_facet_check_nearby(stl);
for(i = 0; i < stl->stats.number_of_facets; i++) {
facet = stl->facet_start[i];
for(j = 0; j < 3; j++) {
if(stl->neighbors_start[i].neighbor[j] == -1) {
edge[j].facet_number = i;
edge[j].which_edge = j;
if(stl_load_edge_nearby(stl, &edge[j], &facet.vertex[j],
&facet.vertex[(j + 1) % 3],
tolerance)) {
/* only insert edges that have different keys */
insert_hash_edge(stl, edge[j], stl_match_neighbors_nearby);
}
}
}
}
stl_free_edges(stl);
}
static int
stl_load_edge_nearby(stl_file *stl, stl_hash_edge *edge,
stl_vertex *a, stl_vertex *b, float tolerance) {
float diff_x;
float diff_y;
float diff_z;
float max_diff;
unsigned vertex1[3];
unsigned vertex2[3];
diff_x = ABS(a->x - b->x);
diff_y = ABS(a->y - b->y);
diff_z = ABS(a->z - b->z);
max_diff = STL_MAX(diff_x, diff_y);
max_diff = STL_MAX(diff_z, max_diff);
vertex1[0] = (unsigned)((a->x - stl->stats.min.x) / tolerance);
vertex1[1] = (unsigned)((a->y - stl->stats.min.y) / tolerance);
vertex1[2] = (unsigned)((a->z - stl->stats.min.z) / tolerance);
vertex2[0] = (unsigned)((b->x - stl->stats.min.x) / tolerance);
vertex2[1] = (unsigned)((b->y - stl->stats.min.y) / tolerance);
vertex2[2] = (unsigned)((b->z - stl->stats.min.z) / tolerance);
if( (vertex1[0] == vertex2[0])
&& (vertex1[1] == vertex2[1])
&& (vertex1[2] == vertex2[2])) {
/* Both vertices hash to the same value */
return 0;
}
if(diff_x == max_diff) {
if(a->x > b->x) {
memcpy(&edge->key[0], vertex1, sizeof(stl_vertex));
memcpy(&edge->key[3], vertex2, sizeof(stl_vertex));
} else {
memcpy(&edge->key[0], vertex2, sizeof(stl_vertex));
memcpy(&edge->key[3], vertex1, sizeof(stl_vertex));
edge->which_edge += 3; /* this edge is loaded backwards */
}
} else if(diff_y == max_diff) {
if(a->y > b->y) {
memcpy(&edge->key[0], vertex1, sizeof(stl_vertex));
memcpy(&edge->key[3], vertex2, sizeof(stl_vertex));
} else {
memcpy(&edge->key[0], vertex2, sizeof(stl_vertex));
memcpy(&edge->key[3], vertex1, sizeof(stl_vertex));
edge->which_edge += 3; /* this edge is loaded backwards */
}
} else {
if(a->z > b->z) {
memcpy(&edge->key[0], vertex1, sizeof(stl_vertex));
memcpy(&edge->key[3], vertex2, sizeof(stl_vertex));
} else {
memcpy(&edge->key[0], vertex2, sizeof(stl_vertex));
memcpy(&edge->key[3], vertex1, sizeof(stl_vertex));
edge->which_edge += 3; /* this edge is loaded backwards */
}
}
return 1;
}
static void
stl_free_edges(stl_file *stl) {
int i;
stl_hash_edge *temp;
if (stl->error) return;
if(stl->stats.malloced != stl->stats.freed) {
for(i = 0; i < stl->M; i++) {
for(temp = stl->heads[i]; stl->heads[i] != stl->tail;
temp = stl->heads[i]) {
stl->heads[i] = stl->heads[i]->next;
free(temp);
stl->stats.freed++;
}
}
}
free(stl->heads);
free(stl->tail);
}
static void
stl_initialize_facet_check_nearby(stl_file *stl) {
int i;
if (stl->error) return;
stl->stats.malloced = 0;
stl->stats.freed = 0;
stl->stats.collisions = 0;
/* tolerance = STL_MAX(stl->stats.shortest_edge, tolerance);*/
/* tolerance = STL_MAX((stl->stats.bounding_diameter / 500000.0), tolerance);*/
/* tolerance *= 0.5;*/
stl->M = 81397;
stl->heads = (stl_hash_edge**)calloc(stl->M, sizeof(*stl->heads));
if(stl->heads == NULL) perror("stl_initialize_facet_check_nearby");
stl->tail = (stl_hash_edge*)malloc(sizeof(stl_hash_edge));
if(stl->tail == NULL) perror("stl_initialize_facet_check_nearby");
stl->tail->next = stl->tail;
for(i = 0; i < stl->M; i++) {
stl->heads[i] = stl->tail;
}
}
static void
stl_record_neighbors(stl_file *stl,
stl_hash_edge *edge_a, stl_hash_edge *edge_b) {
int i;
int j;
if (stl->error) return;
/* Facet a's neighbor is facet b */
stl->neighbors_start[edge_a->facet_number].neighbor[edge_a->which_edge % 3] =
edge_b->facet_number; /* sets the .neighbor part */
stl->neighbors_start[edge_a->facet_number].
which_vertex_not[edge_a->which_edge % 3] =
(edge_b->which_edge + 2) % 3; /* sets the .which_vertex_not part */
/* Facet b's neighbor is facet a */
stl->neighbors_start[edge_b->facet_number].neighbor[edge_b->which_edge % 3] =
edge_a->facet_number; /* sets the .neighbor part */
stl->neighbors_start[edge_b->facet_number].
which_vertex_not[edge_b->which_edge % 3] =
(edge_a->which_edge + 2) % 3; /* sets the .which_vertex_not part */
if( ((edge_a->which_edge < 3) && (edge_b->which_edge < 3))
|| ((edge_a->which_edge > 2) && (edge_b->which_edge > 2))) {
/* these facets are oriented in opposite directions. */
/* their normals are probably messed up. */
stl->neighbors_start[edge_a->facet_number].
which_vertex_not[edge_a->which_edge % 3] += 3;
stl->neighbors_start[edge_b->facet_number].
which_vertex_not[edge_b->which_edge % 3] += 3;
}
/* Count successful connects */
/* Total connects */
stl->stats.connected_edges += 2;
/* Count individual connects */
i = ((stl->neighbors_start[edge_a->facet_number].neighbor[0] == -1) +
(stl->neighbors_start[edge_a->facet_number].neighbor[1] == -1) +
(stl->neighbors_start[edge_a->facet_number].neighbor[2] == -1));
j = ((stl->neighbors_start[edge_b->facet_number].neighbor[0] == -1) +
(stl->neighbors_start[edge_b->facet_number].neighbor[1] == -1) +
(stl->neighbors_start[edge_b->facet_number].neighbor[2] == -1));
if(i == 2) {
stl->stats.connected_facets_1_edge +=1;
} else if(i == 1) {
stl->stats.connected_facets_2_edge +=1;
} else {
stl->stats.connected_facets_3_edge +=1;
}
if(j == 2) {
stl->stats.connected_facets_1_edge +=1;
} else if(j == 1) {
stl->stats.connected_facets_2_edge +=1;
} else {
stl->stats.connected_facets_3_edge +=1;
}
}
static void
stl_match_neighbors_exact(stl_file *stl,
stl_hash_edge *edge_a, stl_hash_edge *edge_b) {
if (stl->error) return;
stl_record_neighbors(stl, edge_a, edge_b);
}
static void
stl_match_neighbors_nearby(stl_file *stl,
stl_hash_edge *edge_a, stl_hash_edge *edge_b) {
int facet1;
int facet2;
int vertex1;
int vertex2;
int vnot1;
int vnot2;
stl_vertex new_vertex1;
stl_vertex new_vertex2;
if (stl->error) return;
stl_record_neighbors(stl, edge_a, edge_b);
stl_which_vertices_to_change(stl, edge_a, edge_b, &facet1, &vertex1,
&facet2, &vertex2, &new_vertex1, &new_vertex2);
if(facet1 != -1) {
if(facet1 == edge_a->facet_number) {
vnot1 = (edge_a->which_edge + 2) % 3;
} else {
vnot1 = (edge_b->which_edge + 2) % 3;
}
if(((vnot1 + 2) % 3) == vertex1) {
vnot1 += 3;
}
stl_change_vertices(stl, facet1, vnot1, new_vertex1);
}
if(facet2 != -1) {
if(facet2 == edge_a->facet_number) {
vnot2 = (edge_a->which_edge + 2) % 3;
} else {
vnot2 = (edge_b->which_edge + 2) % 3;
}
if(((vnot2 + 2) % 3) == vertex2) {
vnot2 += 3;
}
stl_change_vertices(stl, facet2, vnot2, new_vertex2);
}
stl->stats.edges_fixed += 2;
}
static void
stl_change_vertices(stl_file *stl, int facet_num, int vnot,
stl_vertex new_vertex) {
int first_facet;
int direction;
int next_edge;
int pivot_vertex;
if (stl->error) return;
first_facet = facet_num;
direction = 0;
for(;;) {
if(vnot > 2) {
if(direction == 0) {
pivot_vertex = (vnot + 2) % 3;
next_edge = pivot_vertex;
direction = 1;
} else {
pivot_vertex = (vnot + 1) % 3;
next_edge = vnot % 3;
direction = 0;
}
} else {
if(direction == 0) {
pivot_vertex = (vnot + 1) % 3;
next_edge = vnot;
} else {
pivot_vertex = (vnot + 2) % 3;
next_edge = pivot_vertex;
}
}
stl->facet_start[facet_num].vertex[pivot_vertex] = new_vertex;
vnot = stl->neighbors_start[facet_num].which_vertex_not[next_edge];
facet_num = stl->neighbors_start[facet_num].neighbor[next_edge];
if(facet_num == -1) {
break;
}
if(facet_num == first_facet) {
/* back to the beginning */
printf("\
Back to the first facet changing vertices: probably a mobius part.\n\
Try using a smaller tolerance or don't do a nearby check\n");
return;
}
}
}
static void
stl_which_vertices_to_change(stl_file *stl, stl_hash_edge *edge_a,
stl_hash_edge *edge_b, int *facet1, int *vertex1,
int *facet2, int *vertex2,
stl_vertex *new_vertex1, stl_vertex *new_vertex2) {
int v1a; /* pair 1, facet a */
int v1b; /* pair 1, facet b */
int v2a; /* pair 2, facet a */
int v2b; /* pair 2, facet b */
/* Find first pair */
if(edge_a->which_edge < 3) {
v1a = edge_a->which_edge;
v2a = (edge_a->which_edge + 1) % 3;
} else {
v2a = edge_a->which_edge % 3;
v1a = (edge_a->which_edge + 1) % 3;
}
if(edge_b->which_edge < 3) {
v1b = edge_b->which_edge;
v2b = (edge_b->which_edge + 1) % 3;
} else {
v2b = edge_b->which_edge % 3;
v1b = (edge_b->which_edge + 1) % 3;
}
/* Of the first pair, which vertex, if any, should be changed */
if(!memcmp(&stl->facet_start[edge_a->facet_number].vertex[v1a],
&stl->facet_start[edge_b->facet_number].vertex[v1b],
sizeof(stl_vertex))) {
/* These facets are already equal. No need to change. */
*facet1 = -1;
} else {
if( (stl->neighbors_start[edge_a->facet_number].neighbor[v1a] == -1)
&& (stl->neighbors_start[edge_a->facet_number].
neighbor[(v1a + 2) % 3] == -1)) {
/* This vertex has no neighbors. This is a good one to change */
*facet1 = edge_a->facet_number;
*vertex1 = v1a;
*new_vertex1 = stl->facet_start[edge_b->facet_number].vertex[v1b];
} else {
*facet1 = edge_b->facet_number;
*vertex1 = v1b;
*new_vertex1 = stl->facet_start[edge_a->facet_number].vertex[v1a];
}
}
/* Of the second pair, which vertex, if any, should be changed */
if(!memcmp(&stl->facet_start[edge_a->facet_number].vertex[v2a],
&stl->facet_start[edge_b->facet_number].vertex[v2b],
sizeof(stl_vertex))) {
/* These facets are already equal. No need to change. */
*facet2 = -1;
} else {
if( (stl->neighbors_start[edge_a->facet_number].neighbor[v2a] == -1)
&& (stl->neighbors_start[edge_a->facet_number].
neighbor[(v2a + 2) % 3] == -1)) {
/* This vertex has no neighbors. This is a good one to change */
*facet2 = edge_a->facet_number;
*vertex2 = v2a;
*new_vertex2 = stl->facet_start[edge_b->facet_number].vertex[v2b];
} else {
*facet2 = edge_b->facet_number;
*vertex2 = v2b;
*new_vertex2 = stl->facet_start[edge_a->facet_number].vertex[v2a];
}
}
}
static void
stl_remove_facet(stl_file *stl, int facet_number) {
int neighbor[3];
int vnot[3];
int i;
int j;
if (stl->error) return;
stl->stats.facets_removed += 1;
/* Update list of connected edges */
j = ((stl->neighbors_start[facet_number].neighbor[0] == -1) +
(stl->neighbors_start[facet_number].neighbor[1] == -1) +
(stl->neighbors_start[facet_number].neighbor[2] == -1));
if(j == 2) {
stl->stats.connected_facets_1_edge -= 1;
} else if(j == 1) {
stl->stats.connected_facets_2_edge -= 1;
stl->stats.connected_facets_1_edge -= 1;
} else if(j == 0) {
stl->stats.connected_facets_3_edge -= 1;
stl->stats.connected_facets_2_edge -= 1;
stl->stats.connected_facets_1_edge -= 1;
}
stl->facet_start[facet_number] =
stl->facet_start[stl->stats.number_of_facets - 1];
/* I could reallocate at this point, but it is not really necessary. */
stl->neighbors_start[facet_number] =
stl->neighbors_start[stl->stats.number_of_facets - 1];
stl->stats.number_of_facets -= 1;
for(i = 0; i < 3; i++) {
neighbor[i] = stl->neighbors_start[facet_number].neighbor[i];
vnot[i] = stl->neighbors_start[facet_number].which_vertex_not[i];
}
for(i = 0; i < 3; i++) {
if(neighbor[i] != -1) {
if(stl->neighbors_start[neighbor[i]].neighbor[(vnot[i] + 1)% 3] !=
stl->stats.number_of_facets) {
printf("\
in stl_remove_facet: neighbor = %d numfacets = %d this is wrong\n",
stl->neighbors_start[neighbor[i]].neighbor[(vnot[i] + 1)% 3],
stl->stats.number_of_facets);
return;
}
stl->neighbors_start[neighbor[i]].neighbor[(vnot[i] + 1)% 3]
= facet_number;
}
}
}
void
stl_remove_unconnected_facets(stl_file *stl) {
/* A couple of things need to be done here. One is to remove any */
/* completely unconnected facets (0 edges connected) since these are */
/* useless and could be completely wrong. The second thing that needs to */
/* be done is to remove any degenerate facets that were created during */
/* stl_check_facets_nearby(). */
int i;
if (stl->error) return;
/* remove degenerate facets */
for(i = 0; i < stl->stats.number_of_facets; i++) {
if( !memcmp(&stl->facet_start[i].vertex[0],
&stl->facet_start[i].vertex[1], sizeof(stl_vertex))
|| !memcmp(&stl->facet_start[i].vertex[1],
&stl->facet_start[i].vertex[2], sizeof(stl_vertex))
|| !memcmp(&stl->facet_start[i].vertex[0],
&stl->facet_start[i].vertex[2], sizeof(stl_vertex))) {
stl_remove_degenerate(stl, i);
i--;
}
}
if(stl->stats.connected_facets_1_edge < stl->stats.number_of_facets) {
/* remove completely unconnected facets */
for(i = 0; i < stl->stats.number_of_facets; i++) {
if( (stl->neighbors_start[i].neighbor[0] == -1)
&& (stl->neighbors_start[i].neighbor[1] == -1)
&& (stl->neighbors_start[i].neighbor[2] == -1)) {
/* This facet is completely unconnected. Remove it. */
stl_remove_facet(stl, i);
i--;
}
}
}
}
static void
stl_remove_degenerate(stl_file *stl, int facet) {
int edge1;
int edge2;
int edge3;
int neighbor1;
int neighbor2;
int neighbor3;
int vnot1;
int vnot2;
int vnot3;
if (stl->error) return;
if( !memcmp(&stl->facet_start[facet].vertex[0],
&stl->facet_start[facet].vertex[1], sizeof(stl_vertex))
&& !memcmp(&stl->facet_start[facet].vertex[1],
&stl->facet_start[facet].vertex[2], sizeof(stl_vertex))) {
/* all 3 vertices are equal. Just remove the facet. I don't think*/
/* this is really possible, but just in case... */
printf("removing a facet in stl_remove_degenerate\n");
stl_remove_facet(stl, facet);
return;
}
if(!memcmp(&stl->facet_start[facet].vertex[0],
&stl->facet_start[facet].vertex[1], sizeof(stl_vertex))) {
edge1 = 1;
edge2 = 2;
edge3 = 0;
} else if(!memcmp(&stl->facet_start[facet].vertex[1],
&stl->facet_start[facet].vertex[2], sizeof(stl_vertex))) {
edge1 = 0;
edge2 = 2;
edge3 = 1;
} else if(!memcmp(&stl->facet_start[facet].vertex[2],
&stl->facet_start[facet].vertex[0], sizeof(stl_vertex))) {
edge1 = 0;
edge2 = 1;
edge3 = 2;
} else {
/* No degenerate. Function shouldn't have been called. */
return;
}
neighbor1 = stl->neighbors_start[facet].neighbor[edge1];
neighbor2 = stl->neighbors_start[facet].neighbor[edge2];
if(neighbor1 == -1) {
stl_update_connects_remove_1(stl, neighbor2);
}
if(neighbor2 == -1) {
stl_update_connects_remove_1(stl, neighbor1);
}
neighbor3 = stl->neighbors_start[facet].neighbor[edge3];
vnot1 = stl->neighbors_start[facet].which_vertex_not[edge1];
vnot2 = stl->neighbors_start[facet].which_vertex_not[edge2];
vnot3 = stl->neighbors_start[facet].which_vertex_not[edge3];
if(neighbor1 != -1){
stl->neighbors_start[neighbor1].neighbor[(vnot1 + 1) % 3] = neighbor2;
stl->neighbors_start[neighbor1].which_vertex_not[(vnot1 + 1) % 3] = vnot2;
}
if(neighbor2 != -1){
stl->neighbors_start[neighbor2].neighbor[(vnot2 + 1) % 3] = neighbor1;
stl->neighbors_start[neighbor2].which_vertex_not[(vnot2 + 1) % 3] = vnot1;
}
stl_remove_facet(stl, facet);
if(neighbor3 != -1) {
stl_update_connects_remove_1(stl, neighbor3);
stl->neighbors_start[neighbor3].neighbor[(vnot3 + 1) % 3] = -1;
}
}
void
stl_update_connects_remove_1(stl_file *stl, int facet_num) {
int j;
if (stl->error) return;
/* Update list of connected edges */
j = ((stl->neighbors_start[facet_num].neighbor[0] == -1) +
(stl->neighbors_start[facet_num].neighbor[1] == -1) +
(stl->neighbors_start[facet_num].neighbor[2] == -1));
if(j == 0) { /* Facet has 3 neighbors */
stl->stats.connected_facets_3_edge -= 1;
} else if(j == 1) { /* Facet has 2 neighbors */
stl->stats.connected_facets_2_edge -= 1;
} else if(j == 2) { /* Facet has 1 neighbor */
stl->stats.connected_facets_1_edge -= 1;
}
}
void
stl_fill_holes(stl_file *stl) {
stl_facet facet;
stl_facet new_facet;
int neighbors_initial[3];
stl_hash_edge edge;
int first_facet;
int direction;
int facet_num;
int vnot;
int next_edge;
int pivot_vertex;
int next_facet;
int i;
int j;
int k;
if (stl->error) return;
/* Insert all unconnected edges into hash list */
stl_initialize_facet_check_nearby(stl);
for(i = 0; i < stl->stats.number_of_facets; i++) {
facet = stl->facet_start[i];
for(j = 0; j < 3; j++) {
if(stl->neighbors_start[i].neighbor[j] != -1) continue;
edge.facet_number = i;
edge.which_edge = j;
stl_load_edge_exact(stl, &edge, &facet.vertex[j],
&facet.vertex[(j + 1) % 3]);
insert_hash_edge(stl, edge, stl_match_neighbors_exact);
}
}
for(i = 0; i < stl->stats.number_of_facets; i++) {
facet = stl->facet_start[i];
neighbors_initial[0] = stl->neighbors_start[i].neighbor[0];
neighbors_initial[1] = stl->neighbors_start[i].neighbor[1];
neighbors_initial[2] = stl->neighbors_start[i].neighbor[2];
first_facet = i;
for(j = 0; j < 3; j++) {
if(stl->neighbors_start[i].neighbor[j] != -1) continue;
new_facet.vertex[0] = facet.vertex[j];
new_facet.vertex[1] = facet.vertex[(j + 1) % 3];
if(neighbors_initial[(j + 2) % 3] == -1) {
direction = 1;
} else {
direction = 0;
}
facet_num = i;
vnot = (j + 2) % 3;
for(;;) {
if(vnot > 2) {
if(direction == 0) {
pivot_vertex = (vnot + 2) % 3;
next_edge = pivot_vertex;
direction = 1;
} else {
pivot_vertex = (vnot + 1) % 3;
next_edge = vnot % 3;
direction = 0;
}
} else {
if(direction == 0) {
pivot_vertex = (vnot + 1) % 3;
next_edge = vnot;
} else {
pivot_vertex = (vnot + 2) % 3;
next_edge = pivot_vertex;
}
}
next_facet = stl->neighbors_start[facet_num].neighbor[next_edge];
if(next_facet == -1) {
new_facet.vertex[2] = stl->facet_start[facet_num].
vertex[vnot % 3];
stl_add_facet(stl, &new_facet);
for(k = 0; k < 3; k++) {
edge.facet_number = stl->stats.number_of_facets - 1;
edge.which_edge = k;
stl_load_edge_exact(stl, &edge, &new_facet.vertex[k],
&new_facet.vertex[(k + 1) % 3]);
insert_hash_edge(stl, edge, stl_match_neighbors_exact);
}
break;
} else {
vnot = stl->neighbors_start[facet_num].
which_vertex_not[next_edge];
facet_num = next_facet;
}
if(facet_num == first_facet) {
/* back to the beginning */
printf("\
Back to the first facet filling holes: probably a mobius part.\n\
Try using a smaller tolerance or don't do a nearby check\n");
return;
}
}
}
}
}
void
stl_add_facet(stl_file *stl, stl_facet *new_facet) {
if (stl->error) return;
stl->stats.facets_added += 1;
if(stl->stats.facets_malloced < stl->stats.number_of_facets + 1) {
stl->facet_start = (stl_facet*)realloc(stl->facet_start,
(sizeof(stl_facet) * (stl->stats.facets_malloced + 256)));
if(stl->facet_start == NULL) perror("stl_add_facet");
stl->neighbors_start = (stl_neighbors*)realloc(stl->neighbors_start,
(sizeof(stl_neighbors) * (stl->stats.facets_malloced + 256)));
if(stl->neighbors_start == NULL) perror("stl_add_facet");
stl->stats.facets_malloced += 256;
}
stl->facet_start[stl->stats.number_of_facets] = *new_facet;
/* note that the normal vector is not set here, just initialized to 0 */
stl->facet_start[stl->stats.number_of_facets].normal.x = 0.0;
stl->facet_start[stl->stats.number_of_facets].normal.y = 0.0;
stl->facet_start[stl->stats.number_of_facets].normal.z = 0.0;
stl->neighbors_start[stl->stats.number_of_facets].neighbor[0] = -1;
stl->neighbors_start[stl->stats.number_of_facets].neighbor[1] = -1;
stl->neighbors_start[stl->stats.number_of_facets].neighbor[2] = -1;
stl->stats.number_of_facets += 1;
}
admesh-0.98.5/src/normals.c 0000664 0000000 0000000 00000025474 14336667610 0015514 0 ustar 00root root 0000000 0000000 /* ADMesh -- process triangulated solid meshes
* Copyright (C) 1995, 1996 Anthony D. Martin
* Copyright (C) 2013, 2014 several contributors, see AUTHORS
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Questions, comments, suggestions, etc to
* https://github.com/admesh/admesh/issues
*/
#include
#include
#include
#include
#include "stl.h"
static void stl_reverse_facet(stl_file *stl, int facet_num);
static void stl_reverse_vector(float v[]);
int stl_check_normal_vector(stl_file *stl, int facet_num, int normal_fix_flag);
static void
stl_reverse_facet(stl_file *stl, int facet_num) {
stl_vertex tmp_vertex;
/* int tmp_neighbor;*/
int neighbor[3];
int vnot[3];
stl->stats.facets_reversed += 1;
neighbor[0] = stl->neighbors_start[facet_num].neighbor[0];
neighbor[1] = stl->neighbors_start[facet_num].neighbor[1];
neighbor[2] = stl->neighbors_start[facet_num].neighbor[2];
vnot[0] = stl->neighbors_start[facet_num].which_vertex_not[0];
vnot[1] = stl->neighbors_start[facet_num].which_vertex_not[1];
vnot[2] = stl->neighbors_start[facet_num].which_vertex_not[2];
/* reverse the facet */
tmp_vertex = stl->facet_start[facet_num].vertex[0];
stl->facet_start[facet_num].vertex[0] =
stl->facet_start[facet_num].vertex[1];
stl->facet_start[facet_num].vertex[1] = tmp_vertex;
/* fix the vnots of the neighboring facets */
if(neighbor[0] != -1)
stl->neighbors_start[neighbor[0]].which_vertex_not[(vnot[0] + 1) % 3] =
(stl->neighbors_start[neighbor[0]].
which_vertex_not[(vnot[0] + 1) % 3] + 3) % 6;
if(neighbor[1] != -1)
stl->neighbors_start[neighbor[1]].which_vertex_not[(vnot[1] + 1) % 3] =
(stl->neighbors_start[neighbor[1]].
which_vertex_not[(vnot[1] + 1) % 3] + 4) % 6;
if(neighbor[2] != -1)
stl->neighbors_start[neighbor[2]].which_vertex_not[(vnot[2] + 1) % 3] =
(stl->neighbors_start[neighbor[2]].
which_vertex_not[(vnot[2] + 1) % 3] + 2) % 6;
/* swap the neighbors of the facet that is being reversed */
stl->neighbors_start[facet_num].neighbor[1] = neighbor[2];
stl->neighbors_start[facet_num].neighbor[2] = neighbor[1];
/* swap the vnots of the facet that is being reversed */
stl->neighbors_start[facet_num].which_vertex_not[1] = vnot[2];
stl->neighbors_start[facet_num].which_vertex_not[2] = vnot[1];
/* reverse the values of the vnots of the facet that is being reversed */
stl->neighbors_start[facet_num].which_vertex_not[0] =
(stl->neighbors_start[facet_num].which_vertex_not[0] + 3) % 6;
stl->neighbors_start[facet_num].which_vertex_not[1] =
(stl->neighbors_start[facet_num].which_vertex_not[1] + 3) % 6;
stl->neighbors_start[facet_num].which_vertex_not[2] =
(stl->neighbors_start[facet_num].which_vertex_not[2] + 3) % 6;
}
void
stl_fix_normal_directions(stl_file *stl) {
char *norm_sw;
/* int edge_num;*/
/* int vnot;*/
int checked = 0;
int facet_num;
/* int next_facet;*/
int i;
int j;
struct stl_normal {
int facet_num;
struct stl_normal *next;
};
struct stl_normal *head;
struct stl_normal *tail;
struct stl_normal *newn;
struct stl_normal *temp;
if (stl->error) return;
/* Initialize linked list. */
head = (struct stl_normal*)malloc(sizeof(struct stl_normal));
if(head == NULL) perror("stl_fix_normal_directions");
tail = (struct stl_normal*)malloc(sizeof(struct stl_normal));
if(tail == NULL) perror("stl_fix_normal_directions");
head->next = tail;
tail->next = tail;
/* Initialize list that keeps track of already fixed facets. */
norm_sw = (char*)calloc(stl->stats.number_of_facets, sizeof(char));
if(norm_sw == NULL) perror("stl_fix_normal_directions");
facet_num = 0;
/* If normal vector is not within tolerance and backwards:
Arbitrarily starts at face 0. If this one is wrong, we're screwed. Thankfully, the chances
of it being wrong randomly are low if most of the triangles are right: */
if(stl_check_normal_vector(stl, 0, 0) == 2)
stl_reverse_facet(stl, 0);
/* Say that we've fixed this facet: */
norm_sw[facet_num] = 1;
checked++;
for(;;) {
/* Add neighbors_to_list.
Add unconnected neighbors to the list:a */
for(j = 0; j < 3; j++) {
/* Reverse the neighboring facets if necessary. */
if(stl->neighbors_start[facet_num].which_vertex_not[j] > 2) {
/* If the facet has a neighbor that is -1, it means that edge isn't shared by another facet */
if(stl->neighbors_start[facet_num].neighbor[j] != -1) {
stl_reverse_facet
(stl, stl->neighbors_start[facet_num].neighbor[j]);
}
}
/* If this edge of the facet is connected: */
if(stl->neighbors_start[facet_num].neighbor[j] != -1 &&
stl->neighbors_start[facet_num].neighbor[j] < stl->stats.number_of_facets*(int)sizeof(char)) {
/* If we haven't fixed this facet yet, add it to the list: */
if(norm_sw[stl->neighbors_start[facet_num].neighbor[j]] != 1) {
/* Add node to beginning of list. */
newn = (struct stl_normal*)malloc(sizeof(struct stl_normal));
if(newn == NULL) perror("stl_fix_normal_directions");
newn->facet_num = stl->neighbors_start[facet_num].neighbor[j];
newn->next = head->next;
head->next = newn;
}
}
}
/* Get next facet to fix from top of list. */
if(head->next != tail) {
facet_num = head->next->facet_num;
if(norm_sw[facet_num] != 1) { /* If facet is in list mutiple times */
norm_sw[facet_num] = 1; /* Record this one as being fixed. */
checked++;
}
temp = head->next; /* Delete this facet from the list. */
head->next = head->next->next;
free(temp);
} else { /* if we ran out of facets to fix: */
/* All of the facets in this part have been fixed. */
stl->stats.number_of_parts += 1;
if(checked >= stl->stats.number_of_facets) {
/* All of the facets have been checked. Bail out. */
break;
} else {
/* There is another part here. Find it and continue. */
for(i = 0; i < stl->stats.number_of_facets; i++) {
if(norm_sw[i] == 0) {
/* This is the first facet of the next part. */
facet_num = i;
if(stl_check_normal_vector(stl, i, 0) == 2) {
stl_reverse_facet(stl, i);
}
norm_sw[facet_num] = 1;
checked++;
break;
}
}
}
}
}
free(head);
free(tail);
free(norm_sw);
}
int
stl_check_normal_vector(stl_file *stl, int facet_num, int normal_fix_flag) {
/* Returns 0 if the normal is within tolerance */
/* Returns 1 if the normal is not within tolerance, but direction is OK */
/* Returns 2 if the normal is not within tolerance and backwards */
/* Returns 4 if the status is unknown. */
float normal[3];
float test_norm[3];
stl_facet *facet;
facet = &stl->facet_start[facet_num];
stl_calculate_normal(normal, facet);
stl_normalize_vector(normal);
if( (ABS(normal[0] - facet->normal.x) < 0.001)
&& (ABS(normal[1] - facet->normal.y) < 0.001)
&& (ABS(normal[2] - facet->normal.z) < 0.001)) {
/* It is not really necessary to change the values here */
/* but just for consistency, I will. */
facet->normal.x = normal[0];
facet->normal.y = normal[1];
facet->normal.z = normal[2];
return 0;
}
test_norm[0] = facet->normal.x;
test_norm[1] = facet->normal.y;
test_norm[2] = facet->normal.z;
stl_normalize_vector(test_norm);
if( (ABS(normal[0] - test_norm[0]) < 0.001)
&& (ABS(normal[1] - test_norm[1]) < 0.001)
&& (ABS(normal[2] - test_norm[2]) < 0.001)) {
if(normal_fix_flag) {
facet->normal.x = normal[0];
facet->normal.y = normal[1];
facet->normal.z = normal[2];
stl->stats.normals_fixed += 1;
}
return 1;
}
stl_reverse_vector(test_norm);
if( (ABS(normal[0] - test_norm[0]) < 0.001)
&& (ABS(normal[1] - test_norm[1]) < 0.001)
&& (ABS(normal[2] - test_norm[2]) < 0.001)) {
/* Facet is backwards. */
if(normal_fix_flag) {
facet->normal.x = normal[0];
facet->normal.y = normal[1];
facet->normal.z = normal[2];
stl->stats.normals_fixed += 1;
}
return 2;
}
if(normal_fix_flag) {
facet->normal.x = normal[0];
facet->normal.y = normal[1];
facet->normal.z = normal[2];
stl->stats.normals_fixed += 1;
}
return 4;
}
static void
stl_reverse_vector(float v[]) {
v[0] *= -1;
v[1] *= -1;
v[2] *= -1;
}
void
stl_calculate_normal(float normal[], stl_facet *facet) {
float v1[3];
float v2[3];
v1[0] = facet->vertex[1].x - facet->vertex[0].x;
v1[1] = facet->vertex[1].y - facet->vertex[0].y;
v1[2] = facet->vertex[1].z - facet->vertex[0].z;
v2[0] = facet->vertex[2].x - facet->vertex[0].x;
v2[1] = facet->vertex[2].y - facet->vertex[0].y;
v2[2] = facet->vertex[2].z - facet->vertex[0].z;
normal[0] = (float)((double)v1[1] * (double)v2[2]) - ((double)v1[2] * (double)v2[1]);
normal[1] = (float)((double)v1[2] * (double)v2[0]) - ((double)v1[0] * (double)v2[2]);
normal[2] = (float)((double)v1[0] * (double)v2[1]) - ((double)v1[1] * (double)v2[0]);
}
void stl_normalize_vector(float v[]) {
double length;
double factor;
float min_normal_length;
length = sqrt((double)v[0] * (double)v[0] + (double)v[1] * (double)v[1] + (double)v[2] * (double)v[2]);
min_normal_length = 0.000000000001;
if(length < min_normal_length) {
v[0] = 0.0;
v[1] = 0.0;
v[2] = 0.0;
return;
}
factor = 1.0 / length;
v[0] *= factor;
v[1] *= factor;
v[2] *= factor;
}
void
stl_fix_normal_values(stl_file *stl) {
int i;
if (stl->error) return;
for(i = 0; i < stl->stats.number_of_facets; i++) {
stl_check_normal_vector(stl, i, 1);
}
}
void
stl_reverse_all_facets(stl_file *stl) {
int i;
float normal[3];
if (stl->error) return;
for(i = 0; i < stl->stats.number_of_facets; i++) {
stl_reverse_facet(stl, i);
stl_calculate_normal(normal, &stl->facet_start[i]);
stl_normalize_vector(normal);
stl->facet_start[i].normal.x = normal[0];
stl->facet_start[i].normal.y = normal[1];
stl->facet_start[i].normal.z = normal[2];
}
}
admesh-0.98.5/src/portable_endian.h 0000664 0000000 0000000 00000006003 14336667610 0017157 0 ustar 00root root 0000000 0000000 /* portable_endian.h
* from https://gist.github.com/panzi/6856583
* "License": Public Domain
* I, Mathias Panzenböck, place this file hereby into the public domain. Use it
* at your own risk for whatever you like. In case there are jurisdictions
* that don't support putting things in the public domain you can also consider
* it to be "dual licensed" under the BSD, MIT and Apache licenses, if you want
* to. This code is trivial anyway. Consider it an example on how to get the
* endian conversion functions on different platforms.
*/
#ifndef PORTABLE_ENDIAN_H__
#define PORTABLE_ENDIAN_H__
#if (defined(_WIN16) || defined(_WIN32) || defined(_WIN64)) && !defined(__WINDOWS__)
# define __WINDOWS__
#endif
#if defined(__linux__) || defined(__CYGWIN__)
# include
#elif defined(__APPLE__)
# include
# define htobe16(x) OSSwapHostToBigInt16(x)
# define htole16(x) OSSwapHostToLittleInt16(x)
# define be16toh(x) OSSwapBigToHostInt16(x)
# define le16toh(x) OSSwapLittleToHostInt16(x)
# define htobe32(x) OSSwapHostToBigInt32(x)
# define htole32(x) OSSwapHostToLittleInt32(x)
# define be32toh(x) OSSwapBigToHostInt32(x)
# define le32toh(x) OSSwapLittleToHostInt32(x)
# define htobe64(x) OSSwapHostToBigInt64(x)
# define htole64(x) OSSwapHostToLittleInt64(x)
# define be64toh(x) OSSwapBigToHostInt64(x)
# define le64toh(x) OSSwapLittleToHostInt64(x)
# define __BYTE_ORDER BYTE_ORDER
# define __BIG_ENDIAN BIG_ENDIAN
# define __LITTLE_ENDIAN LITTLE_ENDIAN
# define __PDP_ENDIAN PDP_ENDIAN
#elif defined(__OpenBSD__)
# include
#elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__)
# include
# define be16toh(x) betoh16(x)
# define le16toh(x) letoh16(x)
# define be32toh(x) betoh32(x)
# define le32toh(x) letoh32(x)
# define be64toh(x) betoh64(x)
# define le64toh(x) letoh64(x)
#elif defined(__WINDOWS__)
/* # include */
# include
# if BYTE_ORDER == LITTLE_ENDIAN
# define htobe16(x) htons(x)
# define htole16(x) (x)
# define be16toh(x) ntohs(x)
# define le16toh(x) (x)
# define htobe32(x) htonl(x)
# define htole32(x) (x)
# define be32toh(x) ntohl(x)
# define le32toh(x) (x)
# define htobe64(x) htonll(x)
# define htole64(x) (x)
# define be64toh(x) ntohll(x)
# define le64toh(x) (x)
# elif BYTE_ORDER == BIG_ENDIAN
/* that would be xbox 360 */
# define htobe16(x) (x)
# define htole16(x) __builtin_bswap16(x)
# define be16toh(x) (x)
# define le16toh(x) __builtin_bswap16(x)
# define htobe32(x) (x)
# define htole32(x) __builtin_bswap32(x)
# define be32toh(x) (x)
# define le32toh(x) __builtin_bswap32(x)
# define htobe64(x) (x)
# define htole64(x) __builtin_bswap64(x)
# define be64toh(x) (x)
# define le64toh(x) __builtin_bswap64(x)
# else
# error byte order not supported
# endif
# define __BYTE_ORDER BYTE_ORDER
# define __BIG_ENDIAN BIG_ENDIAN
# define __LITTLE_ENDIAN LITTLE_ENDIAN
# define __PDP_ENDIAN PDP_ENDIAN
#else
# error platform not supported
#endif
#endif
admesh-0.98.5/src/shared.c 0000664 0000000 0000000 00000017353 14336667610 0015304 0 ustar 00root root 0000000 0000000 /* ADMesh -- process triangulated solid meshes
* Copyright (C) 1995, 1996 Anthony D. Martin
* Copyright (C) 2013, 2014 several contributors, see AUTHORS
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Questions, comments, suggestions, etc to
* https://github.com/admesh/admesh/issues
*/
#include
#include
#include "stl.h"
void
stl_invalidate_shared_vertices(stl_file *stl) {
if (stl->error) return;
if (stl->v_indices != NULL) {
free(stl->v_indices);
stl->v_indices = NULL;
}
if (stl->v_shared != NULL) {
free(stl->v_shared);
stl->v_shared = NULL;
}
}
void
stl_generate_shared_vertices(stl_file *stl) {
int i;
int j;
int first_facet;
int direction;
int facet_num;
int vnot;
int next_edge;
int pivot_vertex;
int next_facet;
int reversed;
if (stl->error) return;
/* make sure this function is idempotent and does not leak memory */
stl_invalidate_shared_vertices(stl);
stl->v_indices = (v_indices_struct*)
calloc(stl->stats.number_of_facets, sizeof(v_indices_struct));
if(stl->v_indices == NULL) perror("stl_generate_shared_vertices");
stl->v_shared = (stl_vertex*)
calloc((stl->stats.number_of_facets / 2), sizeof(stl_vertex));
if(stl->v_shared == NULL) perror("stl_generate_shared_vertices");
stl->stats.shared_malloced = stl->stats.number_of_facets / 2;
stl->stats.shared_vertices = 0;
for(i = 0; i < stl->stats.number_of_facets; i++) {
stl->v_indices[i].vertex[0] = -1;
stl->v_indices[i].vertex[1] = -1;
stl->v_indices[i].vertex[2] = -1;
}
for(i = 0; i < stl->stats.number_of_facets; i++) {
first_facet = i;
for(j = 0; j < 3; j++) {
if(stl->v_indices[i].vertex[j] != -1) {
continue;
}
if(stl->stats.shared_vertices == stl->stats.shared_malloced) {
stl->stats.shared_malloced += 1024;
stl->v_shared = (stl_vertex*)realloc(stl->v_shared,
stl->stats.shared_malloced * sizeof(stl_vertex));
if(stl->v_shared == NULL) perror("stl_generate_shared_vertices");
}
stl->v_shared[stl->stats.shared_vertices] =
stl->facet_start[i].vertex[j];
direction = 0;
reversed = 0;
facet_num = i;
vnot = (j + 2) % 3;
for(;;) {
if(vnot > 2) {
if(direction == 0) {
pivot_vertex = (vnot + 2) % 3;
next_edge = pivot_vertex;
direction = 1;
} else {
pivot_vertex = (vnot + 1) % 3;
next_edge = vnot % 3;
direction = 0;
}
} else {
if(direction == 0) {
pivot_vertex = (vnot + 1) % 3;
next_edge = vnot;
} else {
pivot_vertex = (vnot + 2) % 3;
next_edge = pivot_vertex;
}
}
stl->v_indices[facet_num].vertex[pivot_vertex] =
stl->stats.shared_vertices;
next_facet = stl->neighbors_start[facet_num].neighbor[next_edge];
if(next_facet == -1) {
if(reversed) {
break;
} else {
direction = 1;
vnot = (j + 1) % 3;
reversed = 1;
facet_num = first_facet;
}
} else if(next_facet != first_facet) {
vnot = stl->neighbors_start[facet_num].
which_vertex_not[next_edge];
facet_num = next_facet;
} else {
break;
}
}
stl->stats.shared_vertices += 1;
}
}
}
void
stl_write_off(stl_file *stl, char *file) {
int i;
FILE *fp;
char *error_msg;
if (stl->error) return;
/* Open the file */
fp = fopen(file, "w");
if(fp == NULL) {
error_msg = (char*)
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
sprintf(error_msg, "stl_write_ascii: Couldn't open %s for writing",
file);
perror(error_msg);
free(error_msg);
stl->error = 1;
return;
}
fprintf(fp, "OFF\n");
fprintf(fp, "%d %d 0\n",
stl->stats.shared_vertices, stl->stats.number_of_facets);
for(i = 0; i < stl->stats.shared_vertices; i++) {
fprintf(fp, "\t%f %f %f\n",
stl->v_shared[i].x, stl->v_shared[i].y, stl->v_shared[i].z);
}
for(i = 0; i < stl->stats.number_of_facets; i++) {
fprintf(fp, "\t3 %d %d %d\n", stl->v_indices[i].vertex[0],
stl->v_indices[i].vertex[1], stl->v_indices[i].vertex[2]);
}
fclose(fp);
}
void
stl_write_vrml(stl_file *stl, char *file) {
int i;
FILE *fp;
char *error_msg;
if (stl->error) return;
/* Open the file */
fp = fopen(file, "w");
if(fp == NULL) {
error_msg = (char*)
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
sprintf(error_msg, "stl_write_ascii: Couldn't open %s for writing",
file);
perror(error_msg);
free(error_msg);
stl->error = 1;
return;
}
fprintf(fp, "#VRML V1.0 ascii\n\n");
fprintf(fp, "Separator {\n");
fprintf(fp, "\tDEF STLShape ShapeHints {\n");
fprintf(fp, "\t\tvertexOrdering COUNTERCLOCKWISE\n");
fprintf(fp, "\t\tfaceType CONVEX\n");
fprintf(fp, "\t\tshapeType SOLID\n");
fprintf(fp, "\t\tcreaseAngle 0.0\n");
fprintf(fp, "\t}\n");
fprintf(fp, "\tDEF STLModel Separator {\n");
fprintf(fp, "\t\tDEF STLColor Material {\n");
fprintf(fp, "\t\t\temissiveColor 0.700000 0.700000 0.000000\n");
fprintf(fp, "\t\t}\n");
fprintf(fp, "\t\tDEF STLVertices Coordinate3 {\n");
fprintf(fp, "\t\t\tpoint [\n");
for(i = 0; i < (stl->stats.shared_vertices - 1); i++) {
fprintf(fp, "\t\t\t\t%f %f %f,\n",
stl->v_shared[i].x, stl->v_shared[i].y, stl->v_shared[i].z);
}
fprintf(fp, "\t\t\t\t%f %f %f]\n",
stl->v_shared[i].x, stl->v_shared[i].y, stl->v_shared[i].z);
fprintf(fp, "\t\t}\n");
fprintf(fp, "\t\tDEF STLTriangles IndexedFaceSet {\n");
fprintf(fp, "\t\t\tcoordIndex [\n");
for(i = 0; i < (stl->stats.number_of_facets - 1); i++) {
fprintf(fp, "\t\t\t\t%d, %d, %d, -1,\n", stl->v_indices[i].vertex[0],
stl->v_indices[i].vertex[1], stl->v_indices[i].vertex[2]);
}
fprintf(fp, "\t\t\t\t%d, %d, %d, -1]\n", stl->v_indices[i].vertex[0],
stl->v_indices[i].vertex[1], stl->v_indices[i].vertex[2]);
fprintf(fp, "\t\t}\n");
fprintf(fp, "\t}\n");
fprintf(fp, "}\n");
fclose(fp);
}
void stl_write_obj (stl_file *stl, char *file) {
int i;
FILE* fp;
if (stl->error) return;
/* Open the file */
fp = fopen(file, "w");
if (fp == NULL) {
char* error_msg = (char*)malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
sprintf(error_msg, "stl_write_ascii: Couldn't open %s for writing", file);
perror(error_msg);
free(error_msg);
stl->error = 1;
return;
}
for (i = 0; i < stl->stats.shared_vertices; i++) {
fprintf(fp, "v %f %f %f\n", stl->v_shared[i].x, stl->v_shared[i].y, stl->v_shared[i].z);
}
for (i = 0; i < stl->stats.number_of_facets; i++) {
fprintf(fp, "f %d %d %d\n", stl->v_indices[i].vertex[0]+1, stl->v_indices[i].vertex[1]+1, stl->v_indices[i].vertex[2]+1);
}
fclose(fp);
}
admesh-0.98.5/src/stl.h 0000664 0000000 0000000 00000015436 14336667610 0014645 0 ustar 00root root 0000000 0000000 /* ADMesh -- process triangulated solid meshes
* Copyright (C) 1995, 1996 Anthony D. Martin
* Copyright (C) 2013, 2014 several contributors, see AUTHORS
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Questions, comments, suggestions, etc to
* https://github.com/admesh/admesh/issues
*/
#ifndef __admesh_stl__
#define __admesh_stl__
#include
#ifdef __cplusplus
extern "C" {
#endif
#define STL_MAX(A,B) ((A)>(B)? (A):(B))
#define STL_MIN(A,B) ((A)<(B)? (A):(B))
#define ABS(X) ((X) < 0 ? -(X) : (X))
#define LABEL_SIZE 80
#define NUM_FACET_SIZE 4
#define HEADER_SIZE 84
#define STL_MIN_FILE_SIZE 284
#define ASCII_LINES_PER_FACET 7
#define SIZEOF_EDGE_SORT 24
typedef struct {
float x;
float y;
float z;
} stl_vertex;
typedef struct {
float x;
float y;
float z;
} stl_normal;
typedef char stl_extra[2];
typedef struct {
stl_normal normal;
stl_vertex vertex[3];
stl_extra extra;
} stl_facet;
#define SIZEOF_STL_FACET 50
typedef enum {binary, ascii, inmemory} stl_type;
typedef struct {
stl_vertex p1;
stl_vertex p2;
int facet_number;
} stl_edge;
typedef struct stl_hash_edge {
unsigned key[6];
int facet_number;
int which_edge;
struct stl_hash_edge *next;
} stl_hash_edge;
typedef struct {
int neighbor[3];
char which_vertex_not[3];
} stl_neighbors;
typedef struct {
int vertex[3];
} v_indices_struct;
typedef struct {
char header[81];
stl_type type;
int number_of_facets;
stl_vertex max;
stl_vertex min;
stl_vertex size;
float bounding_diameter;
float shortest_edge;
float volume;
unsigned number_of_blocks;
int connected_edges;
int connected_facets_1_edge;
int connected_facets_2_edge;
int connected_facets_3_edge;
int facets_w_1_bad_edge;
int facets_w_2_bad_edge;
int facets_w_3_bad_edge;
int original_num_facets;
int edges_fixed;
int degenerate_facets;
int facets_removed;
int facets_added;
int facets_reversed;
int backwards_edges;
int normals_fixed;
int number_of_parts;
int malloced;
int freed;
int facets_malloced;
int collisions;
int shared_vertices;
int shared_malloced;
} stl_stats;
typedef struct {
FILE *fp;
stl_facet *facet_start;
stl_edge *edge_start;
stl_hash_edge **heads;
stl_hash_edge *tail;
int M;
stl_neighbors *neighbors_start;
v_indices_struct *v_indices;
stl_vertex *v_shared;
stl_stats stats;
char error;
} stl_file;
extern void stl_open(stl_file *stl, char *file);
extern void stl_close(stl_file *stl);
extern void stl_stats_out(stl_file *stl, FILE *file, char *input_file);
extern void stl_print_edges(stl_file *stl, FILE *file);
extern void stl_print_neighbors(stl_file *stl, char *file);
extern void stl_put_little_int(FILE *fp, int value_in);
extern void stl_put_little_float(FILE *fp, float value_in);
extern void stl_write_ascii(stl_file *stl, const char *file, const char *label);
extern void stl_write_binary(stl_file *stl, const char *file, const char *label);
extern void stl_write_binary_block(stl_file *stl, FILE *fp);
extern void stl_check_facets_exact(stl_file *stl);
extern void stl_check_facets_nearby(stl_file *stl, float tolerance);
extern void stl_remove_unconnected_facets(stl_file *stl);
extern void stl_write_vertex(stl_file *stl, int facet, int vertex);
extern void stl_write_facet(stl_file *stl, char *label, int facet);
extern void stl_write_edge(stl_file *stl, char *label, stl_hash_edge edge);
extern void stl_write_neighbor(stl_file *stl, int facet);
extern void stl_write_quad_object(stl_file *stl, char *file);
extern void stl_verify_neighbors(stl_file *stl);
extern void stl_fill_holes(stl_file *stl);
extern void stl_fix_normal_directions(stl_file *stl);
extern void stl_fix_normal_values(stl_file *stl);
extern void stl_reverse_all_facets(stl_file *stl);
extern void stl_translate(stl_file *stl, float x, float y, float z);
extern void stl_translate_relative(stl_file *stl, float x, float y, float z);
extern void stl_scale_versor(stl_file *stl, float versor[3]);
extern void stl_scale(stl_file *stl, float factor);
extern void stl_rotate_x(stl_file *stl, float angle);
extern void stl_rotate_y(stl_file *stl, float angle);
extern void stl_rotate_z(stl_file *stl, float angle);
extern void stl_mirror_xy(stl_file *stl);
extern void stl_mirror_yz(stl_file *stl);
extern void stl_mirror_xz(stl_file *stl);
extern void stl_open_merge(stl_file *stl, char *file);
extern void stl_invalidate_shared_vertices(stl_file *stl);
extern void stl_generate_shared_vertices(stl_file *stl);
extern void stl_write_obj(stl_file *stl, char *file);
extern void stl_write_off(stl_file *stl, char *file);
extern void stl_write_dxf(stl_file *stl, char *file, char *label);
extern void stl_write_vrml(stl_file *stl, char *file);
extern void stl_calculate_normal(float normal[], stl_facet *facet);
extern void stl_normalize_vector(float v[]);
extern void stl_calculate_volume(stl_file *stl);
extern void stl_repair(stl_file *stl, int fixall_flag, int exact_flag, int tolerance_flag, float tolerance, int increment_flag, float increment, int nearby_flag, int iterations, int remove_unconnected_flag, int fill_holes_flag, int normal_directions_flag, int normal_values_flag, int reverse_all_flag, int verbose_flag);
extern void stl_initialize(stl_file *stl);
extern void stl_count_facets(stl_file *stl, char *file);
extern void stl_allocate(stl_file *stl);
extern void stl_read(stl_file *stl, int first_facet, int first);
extern void stl_facet_stats(stl_file *stl, stl_facet facet, int first);
extern void stl_reallocate(stl_file *stl);
extern void stl_add_facet(stl_file *stl, stl_facet *new_facet);
extern void stl_get_size(stl_file *stl);
extern void stl_clear_error(stl_file *stl);
extern int stl_get_error(stl_file *stl);
extern void stl_exit_on_error(stl_file *stl);
#ifdef __cplusplus
}
#endif
#endif
admesh-0.98.5/src/stl_io.c 0000664 0000000 0000000 00000035552 14336667610 0015330 0 ustar 00root root 0000000 0000000 /* ADMesh -- process triangulated solid meshes
* Copyright (C) 1995, 1996 Anthony D. Martin
* Copyright (C) 2013, 2014 several contributors, see AUTHORS
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Questions, comments, suggestions, etc to
* https://github.com/admesh/admesh/issues
*/
#include
#include
#include "stl.h"
#include "config.h"
#if !defined(SEEK_SET)
#define SEEK_SET 0
#define SEEK_CUR 1
#define SEEK_END 2
#endif
void
stl_print_edges(stl_file *stl, FILE *file) {
int i;
int edges_allocated;
if (stl->error) return;
edges_allocated = stl->stats.number_of_facets * 3;
for(i = 0; i < edges_allocated; i++) {
fprintf(file, "%d, %f, %f, %f, %f, %f, %f\n",
stl->edge_start[i].facet_number,
stl->edge_start[i].p1.x, stl->edge_start[i].p1.y,
stl->edge_start[i].p1.z, stl->edge_start[i].p2.x,
stl->edge_start[i].p2.y, stl->edge_start[i].p2.z);
}
}
void
stl_stats_out(stl_file *stl, FILE *file, char *input_file) {
if (stl->error) return;
/* this is here for Slic3r, without our config.h
it won't use this part of the code anyway */
#ifndef VERSION
#define VERSION "unknown"
#endif
fprintf(file, "\n\
================= Results produced by ADMesh version " VERSION " ================\n");
fprintf(file, "\
Input file : %s\n", input_file);
if(stl->stats.type == binary) {
fprintf(file, "\
File type : Binary STL file\n");
} else {
fprintf(file, "\
File type : ASCII STL file\n");
}
fprintf(file, "\
Header : %s\n", stl->stats.header);
fprintf(file, "============== Size ==============\n");
fprintf(file, "Min X = % f, Max X = % f\n",
stl->stats.min.x, stl->stats.max.x);
fprintf(file, "Min Y = % f, Max Y = % f\n",
stl->stats.min.y, stl->stats.max.y);
fprintf(file, "Min Z = % f, Max Z = % f\n",
stl->stats.min.z, stl->stats.max.z);
fprintf(file, "\
========= Facet Status ========== Original ============ Final ====\n");
fprintf(file, "\
Number of facets : %5d %5d\n",
stl->stats.original_num_facets, stl->stats.number_of_facets);
fprintf(file, "\
Facets with 1 disconnected edge : %5d %5d\n",
stl->stats.facets_w_1_bad_edge, stl->stats.connected_facets_2_edge -
stl->stats.connected_facets_3_edge);
fprintf(file, "\
Facets with 2 disconnected edges : %5d %5d\n",
stl->stats.facets_w_2_bad_edge, stl->stats.connected_facets_1_edge -
stl->stats.connected_facets_2_edge);
fprintf(file, "\
Facets with 3 disconnected edges : %5d %5d\n",
stl->stats.facets_w_3_bad_edge, stl->stats.number_of_facets -
stl->stats.connected_facets_1_edge);
fprintf(file, "\
Total disconnected facets : %5d %5d\n",
stl->stats.facets_w_1_bad_edge + stl->stats.facets_w_2_bad_edge +
stl->stats.facets_w_3_bad_edge, stl->stats.number_of_facets -
stl->stats.connected_facets_3_edge);
fprintf(file,
"=== Processing Statistics === ===== Other Statistics =====\n");
fprintf(file, "\
Number of parts : %5d Volume : % f\n",
stl->stats.number_of_parts, stl->stats.volume);
fprintf(file, "\
Degenerate facets : %5d\n", stl->stats.degenerate_facets);
fprintf(file, "\
Edges fixed : %5d\n", stl->stats.edges_fixed);
fprintf(file, "\
Facets removed : %5d\n", stl->stats.facets_removed);
fprintf(file, "\
Facets added : %5d\n", stl->stats.facets_added);
fprintf(file, "\
Facets reversed : %5d\n", stl->stats.facets_reversed);
fprintf(file, "\
Backwards edges : %5d\n", stl->stats.backwards_edges);
fprintf(file, "\
Normals fixed : %5d\n", stl->stats.normals_fixed);
}
void
stl_write_ascii(stl_file *stl, const char *file, const char *label) {
int i;
FILE *fp;
char *error_msg;
if (stl->error) return;
/* Open the file */
fp = fopen(file, "w");
if(fp == NULL) {
error_msg = (char*)
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
sprintf(error_msg, "stl_write_ascii: Couldn't open %s for writing",
file);
perror(error_msg);
free(error_msg);
stl->error = 1;
return;
}
fprintf(fp, "solid %s\n", label);
for(i = 0; i < stl->stats.number_of_facets; i++) {
fprintf(fp, " facet normal % .8E % .8E % .8E\n",
stl->facet_start[i].normal.x, stl->facet_start[i].normal.y,
stl->facet_start[i].normal.z);
fprintf(fp, " outer loop\n");
fprintf(fp, " vertex % .8E % .8E % .8E\n",
stl->facet_start[i].vertex[0].x, stl->facet_start[i].vertex[0].y,
stl->facet_start[i].vertex[0].z);
fprintf(fp, " vertex % .8E % .8E % .8E\n",
stl->facet_start[i].vertex[1].x, stl->facet_start[i].vertex[1].y,
stl->facet_start[i].vertex[1].z);
fprintf(fp, " vertex % .8E % .8E % .8E\n",
stl->facet_start[i].vertex[2].x, stl->facet_start[i].vertex[2].y,
stl->facet_start[i].vertex[2].z);
fprintf(fp, " endloop\n");
fprintf(fp, " endfacet\n");
}
fprintf(fp, "endsolid %s\n", label);
fclose(fp);
}
void
stl_print_neighbors(stl_file *stl, char *file) {
int i;
FILE *fp;
char *error_msg;
if (stl->error) return;
/* Open the file */
fp = fopen(file, "w");
if(fp == NULL) {
error_msg = (char*)
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
sprintf(error_msg, "stl_print_neighbors: Couldn't open %s for writing",
file);
perror(error_msg);
free(error_msg);
stl->error = 1;
return;
}
for(i = 0; i < stl->stats.number_of_facets; i++) {
fprintf(fp, "%d, %d,%d, %d,%d, %d,%d\n",
i,
stl->neighbors_start[i].neighbor[0],
(int)stl->neighbors_start[i].which_vertex_not[0],
stl->neighbors_start[i].neighbor[1],
(int)stl->neighbors_start[i].which_vertex_not[1],
stl->neighbors_start[i].neighbor[2],
(int)stl->neighbors_start[i].which_vertex_not[2]);
}
fclose(fp);
}
void
stl_put_little_int(FILE *fp, int value_in) {
int new_value;
union {
int int_value;
char char_value[4];
} value;
value.int_value = value_in;
new_value = value.char_value[0] & 0xFF;
new_value |= (value.char_value[1] & 0xFF) << 0x08;
new_value |= (value.char_value[2] & 0xFF) << 0x10;
new_value |= (value.char_value[3] & 0xFF) << 0x18;
fwrite(&new_value, sizeof(int), 1, fp);
}
void
stl_put_little_float(FILE *fp, float value_in) {
int new_value;
union {
float float_value;
char char_value[4];
} value;
value.float_value = value_in;
new_value = value.char_value[0] & 0xFF;
new_value |= (value.char_value[1] & 0xFF) << 0x08;
new_value |= (value.char_value[2] & 0xFF) << 0x10;
new_value |= (value.char_value[3] & 0xFF) << 0x18;
fwrite(&new_value, sizeof(int), 1, fp);
}
void
stl_write_binary_block(stl_file *stl, FILE *fp)
{
int i;
for(i = 0; i < stl->stats.number_of_facets; i++)
{
stl_put_little_float(fp, stl->facet_start[i].normal.x);
stl_put_little_float(fp, stl->facet_start[i].normal.y);
stl_put_little_float(fp, stl->facet_start[i].normal.z);
stl_put_little_float(fp, stl->facet_start[i].vertex[0].x);
stl_put_little_float(fp, stl->facet_start[i].vertex[0].y);
stl_put_little_float(fp, stl->facet_start[i].vertex[0].z);
stl_put_little_float(fp, stl->facet_start[i].vertex[1].x);
stl_put_little_float(fp, stl->facet_start[i].vertex[1].y);
stl_put_little_float(fp, stl->facet_start[i].vertex[1].z);
stl_put_little_float(fp, stl->facet_start[i].vertex[2].x);
stl_put_little_float(fp, stl->facet_start[i].vertex[2].y);
stl_put_little_float(fp, stl->facet_start[i].vertex[2].z);
fputc(stl->facet_start[i].extra[0], fp);
fputc(stl->facet_start[i].extra[1], fp);
}
}
void
stl_write_binary(stl_file *stl, const char *file, const char *label) {
FILE *fp;
int i;
char *error_msg;
if (stl->error) return;
/* Open the file */
fp = fopen(file, "wb");
if(fp == NULL) {
error_msg = (char*)
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
sprintf(error_msg, "stl_write_binary: Couldn't open %s for writing",
file);
perror(error_msg);
free(error_msg);
stl->error = 1;
return;
}
fprintf(fp, "%s", label);
for(i = strlen(label); i < LABEL_SIZE; i++) putc(0, fp);
fseek(fp, LABEL_SIZE, SEEK_SET);
stl_put_little_int(fp, stl->stats.number_of_facets);
stl_write_binary_block(stl, fp);
fclose(fp);
}
void
stl_write_vertex(stl_file *stl, int facet, int vertex) {
if (stl->error) return;
printf(" vertex %d/%d % .8E % .8E % .8E\n", vertex, facet,
stl->facet_start[facet].vertex[vertex].x,
stl->facet_start[facet].vertex[vertex].y,
stl->facet_start[facet].vertex[vertex].z);
}
void
stl_write_facet(stl_file *stl, char *label, int facet) {
if (stl->error) return;
printf("facet (%d)/ %s\n", facet, label);
stl_write_vertex(stl, facet, 0);
stl_write_vertex(stl, facet, 1);
stl_write_vertex(stl, facet, 2);
}
void
stl_write_edge(stl_file *stl, char *label, stl_hash_edge edge) {
if (stl->error) return;
printf("edge (%d)/(%d) %s\n", edge.facet_number, edge.which_edge, label);
if(edge.which_edge < 3) {
stl_write_vertex(stl, edge.facet_number, edge.which_edge % 3);
stl_write_vertex(stl, edge.facet_number, (edge.which_edge + 1) % 3);
} else {
stl_write_vertex(stl, edge.facet_number, (edge.which_edge + 1) % 3);
stl_write_vertex(stl, edge.facet_number, edge.which_edge % 3);
}
}
void
stl_write_neighbor(stl_file *stl, int facet) {
if (stl->error) return;
printf("Neighbors %d: %d, %d, %d ; %d, %d, %d\n", facet,
stl->neighbors_start[facet].neighbor[0],
stl->neighbors_start[facet].neighbor[1],
stl->neighbors_start[facet].neighbor[2],
stl->neighbors_start[facet].which_vertex_not[0],
stl->neighbors_start[facet].which_vertex_not[1],
stl->neighbors_start[facet].which_vertex_not[2]);
}
void
stl_write_quad_object(stl_file *stl, char *file) {
FILE *fp;
int i;
int j;
char *error_msg;
stl_vertex connect_color;
stl_vertex uncon_1_color;
stl_vertex uncon_2_color;
stl_vertex uncon_3_color;
stl_vertex color;
if (stl->error) return;
/* Open the file */
fp = fopen(file, "w");
if(fp == NULL) {
error_msg = (char*)
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
sprintf(error_msg, "stl_write_quad_object: Couldn't open %s for writing",
file);
perror(error_msg);
free(error_msg);
stl->error = 1;
return;
}
connect_color.x = 0.0;
connect_color.y = 0.0;
connect_color.z = 1.0;
uncon_1_color.x = 0.0;
uncon_1_color.y = 1.0;
uncon_1_color.z = 0.0;
uncon_2_color.x = 1.0;
uncon_2_color.y = 1.0;
uncon_2_color.z = 1.0;
uncon_3_color.x = 1.0;
uncon_3_color.y = 0.0;
uncon_3_color.z = 0.0;
fprintf(fp, "CQUAD\n");
for(i = 0; i < stl->stats.number_of_facets; i++) {
j = ((stl->neighbors_start[i].neighbor[0] == -1) +
(stl->neighbors_start[i].neighbor[1] == -1) +
(stl->neighbors_start[i].neighbor[2] == -1));
if(j == 0) {
color = connect_color;
} else if(j == 1) {
color = uncon_1_color;
} else if(j == 2) {
color = uncon_2_color;
} else {
color = uncon_3_color;
}
fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n",
stl->facet_start[i].vertex[0].x,
stl->facet_start[i].vertex[0].y,
stl->facet_start[i].vertex[0].z, color.x, color.y, color.z);
fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n",
stl->facet_start[i].vertex[1].x,
stl->facet_start[i].vertex[1].y,
stl->facet_start[i].vertex[1].z, color.x, color.y, color.z);
fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n",
stl->facet_start[i].vertex[2].x,
stl->facet_start[i].vertex[2].y,
stl->facet_start[i].vertex[2].z, color.x, color.y, color.z);
fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n",
stl->facet_start[i].vertex[2].x,
stl->facet_start[i].vertex[2].y,
stl->facet_start[i].vertex[2].z, color.x, color.y, color.z);
}
fclose(fp);
}
void
stl_write_dxf(stl_file *stl, char *file, char *label) {
int i;
FILE *fp;
char *error_msg;
if (stl->error) return;
/* Open the file */
fp = fopen(file, "w");
if(fp == NULL) {
error_msg = (char*)
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
sprintf(error_msg, "stl_write_ascii: Couldn't open %s for writing",
file);
perror(error_msg);
free(error_msg);
stl->error = 1;
return;
}
fprintf(fp, "999\n%s\n", label);
fprintf(fp, "0\nSECTION\n2\nHEADER\n0\nENDSEC\n");
fprintf(fp, "0\nSECTION\n2\nTABLES\n0\nTABLE\n2\nLAYER\n70\n1\n\
0\nLAYER\n2\n0\n70\n0\n62\n7\n6\nCONTINUOUS\n0\nENDTAB\n0\nENDSEC\n");
fprintf(fp, "0\nSECTION\n2\nBLOCKS\n0\nENDSEC\n");
fprintf(fp, "0\nSECTION\n2\nENTITIES\n");
for(i = 0; i < stl->stats.number_of_facets; i++) {
fprintf(fp, "0\n3DFACE\n8\n0\n");
fprintf(fp, "10\n%f\n20\n%f\n30\n%f\n",
stl->facet_start[i].vertex[0].x, stl->facet_start[i].vertex[0].y,
stl->facet_start[i].vertex[0].z);
fprintf(fp, "11\n%f\n21\n%f\n31\n%f\n",
stl->facet_start[i].vertex[1].x, stl->facet_start[i].vertex[1].y,
stl->facet_start[i].vertex[1].z);
fprintf(fp, "12\n%f\n22\n%f\n32\n%f\n",
stl->facet_start[i].vertex[2].x, stl->facet_start[i].vertex[2].y,
stl->facet_start[i].vertex[2].z);
fprintf(fp, "13\n%f\n23\n%f\n33\n%f\n",
stl->facet_start[i].vertex[2].x, stl->facet_start[i].vertex[2].y,
stl->facet_start[i].vertex[2].z);
}
fprintf(fp, "0\nENDSEC\n0\nEOF\n");
fclose(fp);
}
void
stl_clear_error(stl_file *stl) {
stl->error = 0;
}
void
stl_exit_on_error(stl_file *stl) {
if (!stl->error) return;
stl->error = 0;
stl_close(stl);
exit(1);
}
int
stl_get_error(stl_file *stl) {
return stl->error;
}
admesh-0.98.5/src/stlinit.c 0000664 0000000 0000000 00000032740 14336667610 0015521 0 ustar 00root root 0000000 0000000 /* ADMesh -- process triangulated solid meshes
* Copyright (C) 1995, 1996 Anthony D. Martin
* Copyright (C) 2013, 2014 several contributors, see AUTHORS
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Questions, comments, suggestions, etc to
* https://github.com/admesh/admesh/issues
*/
#include
#include
#include
#include
#include
#include "portable_endian.h"
#include "stl.h"
#if !defined(SEEK_SET)
#define SEEK_SET 0
#define SEEK_CUR 1
#define SEEK_END 2
#endif
void
stl_open(stl_file *stl, char *file) {
stl_initialize(stl);
stl_count_facets(stl, file);
stl_allocate(stl);
stl_read(stl, 0, 1);
if (!stl->error) fclose(stl->fp);
}
void
stl_initialize(stl_file *stl) {
stl->error = 0;
stl->stats.backwards_edges = 0;
stl->stats.degenerate_facets = 0;
stl->stats.edges_fixed = 0;
stl->stats.facets_added = 0;
stl->stats.facets_removed = 0;
stl->stats.facets_reversed = 0;
stl->stats.normals_fixed = 0;
stl->stats.number_of_parts = 0;
stl->stats.original_num_facets = 0;
stl->stats.number_of_facets = 0;
stl->stats.facets_malloced = 0;
stl->stats.volume = -1.0;
stl->neighbors_start = NULL;
stl->facet_start = NULL;
stl->v_indices = NULL;
stl->v_shared = NULL;
}
void
stl_count_facets(stl_file *stl, char *file) {
long file_size;
uint32_t header_num_facets;
int num_facets;
int i, j;
size_t s;
unsigned char chtest[128];
int num_lines = 1;
char *error_msg;
if (stl->error) return;
/* Open the file in binary mode first */
stl->fp = fopen(file, "rb");
if(stl->fp == NULL) {
error_msg = (char*)
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
sprintf(error_msg, "stl_initialize: Couldn't open %s for reading",
file);
perror(error_msg);
free(error_msg);
stl->error = 1;
return;
}
/* Find size of file */
fseek(stl->fp, 0, SEEK_END);
file_size = ftell(stl->fp);
/* Check for binary or ASCII file */
fseek(stl->fp, HEADER_SIZE, SEEK_SET);
if (!fread(chtest, sizeof(chtest), 1, stl->fp)) {
perror("The input is an empty file");
stl->error = 1;
return;
}
stl->stats.type = ascii;
for(s = 0; s < sizeof(chtest); s++) {
if(chtest[s] > 127) {
stl->stats.type = binary;
break;
}
}
rewind(stl->fp);
/* Get the header and the number of facets in the .STL file */
/* If the .STL file is binary, then do the following */
if(stl->stats.type == binary) {
/* Test if the STL file has the right size */
if(((file_size - HEADER_SIZE) % SIZEOF_STL_FACET != 0)
|| (file_size < STL_MIN_FILE_SIZE)) {
fprintf(stderr, "The file %s has the wrong size.\n", file);
stl->error = 1;
return;
}
num_facets = (file_size - HEADER_SIZE) / SIZEOF_STL_FACET;
/* Read the header */
if (fread(stl->stats.header, LABEL_SIZE, 1, stl->fp) > 79) {
stl->stats.header[80] = '\0';
}
/* Read the int following the header. This should contain # of facets */
if((!fread(&header_num_facets, sizeof(uint32_t), 1, stl->fp)) || (uint32_t)num_facets != le32toh(header_num_facets)) {
fprintf(stderr,
"Warning: File size doesn't match number of facets in the header\n");
}
}
/* Otherwise, if the .STL file is ASCII, then do the following */
else {
/* Reopen the file in text mode (for getting correct newlines on Windows) */
if (freopen(file, "r", stl->fp) == NULL) {
perror("Could not reopen the file, something went wrong");
stl->error = 1;
return;
}
/* Find the number of facets */
j = 0;
for(i = 0; i < file_size ; i++) {
j++;
if(getc(stl->fp) == '\n') {
if(j > 4) { /* don't count short lines */
num_lines++;
}
j = 0;
}
}
rewind(stl->fp);
/* Get the header */
for(i = 0;
(i < 80) && (stl->stats.header[i] = getc(stl->fp)) != '\n'; i++);
stl->stats.header[i] = '\0'; /* Lose the '\n' */
stl->stats.header[80] = '\0';
num_facets = num_lines / ASCII_LINES_PER_FACET;
}
stl->stats.number_of_facets += num_facets;
stl->stats.original_num_facets = stl->stats.number_of_facets;
}
void
stl_allocate(stl_file *stl) {
if (stl->error) return;
/* Allocate memory for the entire .STL file */
stl->facet_start = (stl_facet*)calloc(stl->stats.number_of_facets,
sizeof(stl_facet));
if(stl->facet_start == NULL) perror("stl_initialize");
stl->stats.facets_malloced = stl->stats.number_of_facets;
/* Allocate memory for the neighbors list */
stl->neighbors_start = (stl_neighbors*)
calloc(stl->stats.number_of_facets, sizeof(stl_neighbors));
if(stl->neighbors_start == NULL) perror("stl_initialize");
}
void
stl_open_merge(stl_file *stl, char *file_to_merge) {
int num_facets_so_far;
stl_type origStlType;
FILE *origFp;
stl_file stl_to_merge;
if (stl->error) return;
/* Record how many facets we have so far from the first file. We will start putting
facets in the next position. Since we're 0-indexed, it'l be the same position. */
num_facets_so_far = stl->stats.number_of_facets;
/* Record the file type we started with: */
origStlType=stl->stats.type;
/* Record the file pointer too: */
origFp=stl->fp;
/* Initialize the sturucture with zero stats, header info and sizes: */
stl_initialize(&stl_to_merge);
stl_count_facets(&stl_to_merge, file_to_merge);
/* Copy what we need to into stl so that we can read the file_to_merge directly into it
using stl_read: Save the rest of the valuable info: */
stl->stats.type=stl_to_merge.stats.type;
stl->fp=stl_to_merge.fp;
/* Add the number of facets we already have in stl with what we we found in stl_to_merge but
haven't read yet. */
stl->stats.number_of_facets=num_facets_so_far+stl_to_merge.stats.number_of_facets;
/* Allocate enough room for stl->stats.number_of_facets facets and neighbors: */
stl_reallocate(stl);
/* Read the file to merge directly into stl, adding it to what we have already.
Start at num_facets_so_far, the index to the first unused facet. Also say
that this isn't our first time so we should augment stats like min and max
instead of erasing them. */
stl_read(stl, num_facets_so_far, 0);
/* Restore the stl information we overwrote (for stl_read) so that it still accurately
reflects the subject part: */
stl->stats.type=origStlType;
stl->fp=origFp;
}
extern void
stl_reallocate(stl_file *stl) {
if (stl->error) return;
/* Reallocate more memory for the .STL file(s) */
stl->facet_start = (stl_facet*)realloc(stl->facet_start, stl->stats.number_of_facets *
sizeof(stl_facet));
if(stl->facet_start == NULL) perror("stl_initialize");
stl->stats.facets_malloced = stl->stats.number_of_facets;
/* Reallocate more memory for the neighbors list */
stl->neighbors_start = (stl_neighbors*)
realloc(stl->neighbors_start, stl->stats.number_of_facets *
sizeof(stl_neighbors));
if(stl->facet_start == NULL) perror("stl_initialize");
}
/* Reads the contents of the file pointed to by stl->fp into the stl structure,
starting at facet first_facet. The second argument says if it's our first
time running this for the stl and therefore we should reset our max and min stats. */
void
stl_read(stl_file *stl, int first_facet, int first) {
stl_facet facet;
int i, j;
const int facet_float_length = 12;
float *facet_floats[12];
char facet_buffer[12 * sizeof(float)];
uint32_t endianswap_buffer; /* for byteswapping operations */
facet.extra[0] = 0;
facet.extra[1] = 0;
facet_floats[0] = &facet.normal.x;
facet_floats[1] = &facet.normal.y;
facet_floats[2] = &facet.normal.z;
facet_floats[3] = &facet.vertex[0].x;
facet_floats[4] = &facet.vertex[0].y;
facet_floats[5] = &facet.vertex[0].z;
facet_floats[6] = &facet.vertex[1].x;
facet_floats[7] = &facet.vertex[1].y;
facet_floats[8] = &facet.vertex[1].z;
facet_floats[9] = &facet.vertex[2].x;
facet_floats[10] = &facet.vertex[2].y;
facet_floats[11] = &facet.vertex[2].z;
if (stl->error) return;
if(stl->stats.type == binary) {
fseek(stl->fp, HEADER_SIZE, SEEK_SET);
} else {
rewind(stl->fp);
/* Skip the first line of the file */
while(getc(stl->fp) != '\n');
}
for(i = first_facet; i < stl->stats.number_of_facets; i++) {
if(stl->stats.type == binary)
/* Read a single facet from a binary .STL file */
{
if(fread(facet_buffer, sizeof(facet_buffer), 1, stl->fp)
+ fread(&facet.extra, sizeof(char), 2, stl->fp) != 3) {
perror("Cannot read facet");
stl->error = 1;
return;
}
for(j = 0; j < facet_float_length; j++) {
/* convert LE float to host byte order */
memcpy(&endianswap_buffer, facet_buffer + j * sizeof(float), 4);
endianswap_buffer = le32toh(endianswap_buffer);
memcpy(facet_floats[j], &endianswap_buffer, 4);
}
} else
/* Read a single facet from an ASCII .STL file */
{
if((fscanf(stl->fp, "%*s %*s %f %f %f\n", &facet.normal.x, &facet.normal.y, &facet.normal.z) + \
fscanf(stl->fp, "%*s %*s") + \
fscanf(stl->fp, "%*s %f %f %f\n", &facet.vertex[0].x, &facet.vertex[0].y, &facet.vertex[0].z) + \
fscanf(stl->fp, "%*s %f %f %f\n", &facet.vertex[1].x, &facet.vertex[1].y, &facet.vertex[1].z) + \
fscanf(stl->fp, "%*s %f %f %f\n", &facet.vertex[2].x, &facet.vertex[2].y, &facet.vertex[2].z) + \
fscanf(stl->fp, "%*s") + \
fscanf(stl->fp, "%*s")) != 12) {
perror("Something is syntactically very wrong with this ASCII STL!");
stl->error = 1;
return;
}
}
/* Write the facet into memory. */
stl->facet_start[i] = facet;
stl_facet_stats(stl, facet, first);
first = 0;
}
stl->stats.size.x = stl->stats.max.x - stl->stats.min.x;
stl->stats.size.y = stl->stats.max.y - stl->stats.min.y;
stl->stats.size.z = stl->stats.max.z - stl->stats.min.z;
stl->stats.bounding_diameter = sqrt(
stl->stats.size.x * stl->stats.size.x +
stl->stats.size.y * stl->stats.size.y +
stl->stats.size.z * stl->stats.size.z
);
}
void
stl_facet_stats(stl_file *stl, stl_facet facet, int first) {
float diff_x;
float diff_y;
float diff_z;
float max_diff;
if (stl->error) return;
/* while we are going through all of the facets, let's find the */
/* maximum and minimum values for x, y, and z */
/* Initialize the max and min values the first time through*/
if (first) {
stl->stats.max.x = facet.vertex[0].x;
stl->stats.min.x = facet.vertex[0].x;
stl->stats.max.y = facet.vertex[0].y;
stl->stats.min.y = facet.vertex[0].y;
stl->stats.max.z = facet.vertex[0].z;
stl->stats.min.z = facet.vertex[0].z;
diff_x = ABS(facet.vertex[0].x - facet.vertex[1].x);
diff_y = ABS(facet.vertex[0].y - facet.vertex[1].y);
diff_z = ABS(facet.vertex[0].z - facet.vertex[1].z);
max_diff = STL_MAX(diff_x, diff_y);
max_diff = STL_MAX(diff_z, max_diff);
stl->stats.shortest_edge = max_diff;
first = 0;
}
/* now find the max and min values */
stl->stats.max.x = STL_MAX(stl->stats.max.x, facet.vertex[0].x);
stl->stats.min.x = STL_MIN(stl->stats.min.x, facet.vertex[0].x);
stl->stats.max.y = STL_MAX(stl->stats.max.y, facet.vertex[0].y);
stl->stats.min.y = STL_MIN(stl->stats.min.y, facet.vertex[0].y);
stl->stats.max.z = STL_MAX(stl->stats.max.z, facet.vertex[0].z);
stl->stats.min.z = STL_MIN(stl->stats.min.z, facet.vertex[0].z);
stl->stats.max.x = STL_MAX(stl->stats.max.x, facet.vertex[1].x);
stl->stats.min.x = STL_MIN(stl->stats.min.x, facet.vertex[1].x);
stl->stats.max.y = STL_MAX(stl->stats.max.y, facet.vertex[1].y);
stl->stats.min.y = STL_MIN(stl->stats.min.y, facet.vertex[1].y);
stl->stats.max.z = STL_MAX(stl->stats.max.z, facet.vertex[1].z);
stl->stats.min.z = STL_MIN(stl->stats.min.z, facet.vertex[1].z);
stl->stats.max.x = STL_MAX(stl->stats.max.x, facet.vertex[2].x);
stl->stats.min.x = STL_MIN(stl->stats.min.x, facet.vertex[2].x);
stl->stats.max.y = STL_MAX(stl->stats.max.y, facet.vertex[2].y);
stl->stats.min.y = STL_MIN(stl->stats.min.y, facet.vertex[2].y);
stl->stats.max.z = STL_MAX(stl->stats.max.z, facet.vertex[2].z);
stl->stats.min.z = STL_MIN(stl->stats.min.z, facet.vertex[2].z);
}
void
stl_close(stl_file *stl) {
if (stl->error) return;
if(stl->neighbors_start != NULL)
free(stl->neighbors_start);
if(stl->facet_start != NULL)
free(stl->facet_start);
if(stl->v_indices != NULL)
free(stl->v_indices);
if(stl->v_shared != NULL)
free(stl->v_shared);
}
admesh-0.98.5/src/util.c 0000664 0000000 0000000 00000037237 14336667610 0015016 0 ustar 00root root 0000000 0000000 /* ADMesh -- process triangulated solid meshes
* Copyright (C) 1995, 1996 Anthony D. Martin
* Copyright (C) 2013, 2014 several contributors, see AUTHORS
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Questions, comments, suggestions, etc to
* https://github.com/admesh/admesh/issues
*/
#include
#include
#include
#include
#include "stl.h"
static void stl_rotate(float *x, float *y, float angle);
static float get_area(stl_facet *facet);
static float get_volume(stl_file *stl);
void
stl_verify_neighbors(stl_file *stl) {
int i;
int j;
stl_edge edge_a;
stl_edge edge_b;
int neighbor;
int vnot;
if (stl->error) return;
stl->stats.backwards_edges = 0;
for(i = 0; i < stl->stats.number_of_facets; i++) {
for(j = 0; j < 3; j++) {
edge_a.p1 = stl->facet_start[i].vertex[j];
edge_a.p2 = stl->facet_start[i].vertex[(j + 1) % 3];
neighbor = stl->neighbors_start[i].neighbor[j];
vnot = stl->neighbors_start[i].which_vertex_not[j];
if(neighbor == -1)
continue; /* this edge has no neighbor... Continue. */
if(vnot < 3) {
edge_b.p1 = stl->facet_start[neighbor].vertex[(vnot + 2) % 3];
edge_b.p2 = stl->facet_start[neighbor].vertex[(vnot + 1) % 3];
} else {
stl->stats.backwards_edges += 1;
edge_b.p1 = stl->facet_start[neighbor].vertex[(vnot + 1) % 3];
edge_b.p2 = stl->facet_start[neighbor].vertex[(vnot + 2) % 3];
}
if(memcmp(&edge_a, &edge_b, SIZEOF_EDGE_SORT) != 0) {
/* These edges should match but they don't. Print results. */
printf("edge %d of facet %d doesn't match edge %d of facet %d\n",
j, i, vnot + 1, neighbor);
stl_write_facet(stl, (char*)"first facet", i);
stl_write_facet(stl, (char*)"second facet", neighbor);
}
}
}
}
void
stl_translate(stl_file *stl, float x, float y, float z) {
int i;
int j;
if (stl->error) return;
for(i = 0; i < stl->stats.number_of_facets; i++) {
for(j = 0; j < 3; j++) {
stl->facet_start[i].vertex[j].x -= (stl->stats.min.x - x);
stl->facet_start[i].vertex[j].y -= (stl->stats.min.y - y);
stl->facet_start[i].vertex[j].z -= (stl->stats.min.z - z);
}
}
stl->stats.max.x -= (stl->stats.min.x - x);
stl->stats.max.y -= (stl->stats.min.y - y);
stl->stats.max.z -= (stl->stats.min.z - z);
stl->stats.min.x = x;
stl->stats.min.y = y;
stl->stats.min.z = z;
stl_invalidate_shared_vertices(stl);
}
/* Translates the stl by x,y,z, relatively from wherever it is currently */
void
stl_translate_relative(stl_file *stl, float x, float y, float z) {
int i;
int j;
if (stl->error) return;
for(i = 0; i < stl->stats.number_of_facets; i++) {
for(j = 0; j < 3; j++) {
stl->facet_start[i].vertex[j].x += x;
stl->facet_start[i].vertex[j].y += y;
stl->facet_start[i].vertex[j].z += z;
}
}
stl->stats.min.x += x;
stl->stats.min.y += y;
stl->stats.min.z += z;
stl->stats.max.x += x;
stl->stats.max.y += y;
stl->stats.max.z += z;
stl_invalidate_shared_vertices(stl);
}
void
stl_scale_versor(stl_file *stl, float versor[3]) {
int i;
int j;
if (stl->error) return;
/* scale extents */
stl->stats.min.x *= versor[0];
stl->stats.min.y *= versor[1];
stl->stats.min.z *= versor[2];
stl->stats.max.x *= versor[0];
stl->stats.max.y *= versor[1];
stl->stats.max.z *= versor[2];
/* scale size */
stl->stats.size.x *= versor[0];
stl->stats.size.y *= versor[1];
stl->stats.size.z *= versor[2];
/* scale volume */
if (stl->stats.volume > 0.0) {
stl->stats.volume *= (versor[0] * versor[1] * versor[2]);
}
for(i = 0; i < stl->stats.number_of_facets; i++) {
for(j = 0; j < 3; j++) {
stl->facet_start[i].vertex[j].x *= versor[0];
stl->facet_start[i].vertex[j].y *= versor[1];
stl->facet_start[i].vertex[j].z *= versor[2];
}
}
stl_invalidate_shared_vertices(stl);
}
void
stl_scale(stl_file *stl, float factor) {
float versor[3];
if (stl->error) return;
versor[0] = factor;
versor[1] = factor;
versor[2] = factor;
stl_scale_versor(stl, versor);
}
static void calculate_normals(stl_file *stl) {
long i;
float normal[3];
if (stl->error) return;
for(i = 0; i < stl->stats.number_of_facets; i++) {
stl_calculate_normal(normal, &stl->facet_start[i]);
stl_normalize_vector(normal);
stl->facet_start[i].normal.x = normal[0];
stl->facet_start[i].normal.y = normal[1];
stl->facet_start[i].normal.z = normal[2];
}
}
void
stl_rotate_x(stl_file *stl, float angle) {
int i;
int j;
if (stl->error) return;
for(i = 0; i < stl->stats.number_of_facets; i++) {
for(j = 0; j < 3; j++) {
stl_rotate(&stl->facet_start[i].vertex[j].y,
&stl->facet_start[i].vertex[j].z, angle);
}
}
stl_get_size(stl);
calculate_normals(stl);
}
void
stl_rotate_y(stl_file *stl, float angle) {
int i;
int j;
if (stl->error) return;
for(i = 0; i < stl->stats.number_of_facets; i++) {
for(j = 0; j < 3; j++) {
stl_rotate(&stl->facet_start[i].vertex[j].z,
&stl->facet_start[i].vertex[j].x, angle);
}
}
stl_get_size(stl);
calculate_normals(stl);
}
void
stl_rotate_z(stl_file *stl, float angle) {
int i;
int j;
if (stl->error) return;
for(i = 0; i < stl->stats.number_of_facets; i++) {
for(j = 0; j < 3; j++) {
stl_rotate(&stl->facet_start[i].vertex[j].x,
&stl->facet_start[i].vertex[j].y, angle);
}
}
stl_get_size(stl);
calculate_normals(stl);
}
static void
stl_rotate(float *x, float *y, float angle) {
double r;
double theta;
double radian_angle;
radian_angle = (angle / 180.0) * M_PI;
r = sqrt((*x **x) + (*y **y));
theta = atan2(*y, *x);
*x = r * cos(theta + radian_angle);
*y = r * sin(theta + radian_angle);
}
extern void
stl_get_size(stl_file *stl) {
int i;
int j;
if (stl->error) return;
if (stl->stats.number_of_facets == 0) return;
stl->stats.min.x = stl->facet_start[0].vertex[0].x;
stl->stats.min.y = stl->facet_start[0].vertex[0].y;
stl->stats.min.z = stl->facet_start[0].vertex[0].z;
stl->stats.max.x = stl->facet_start[0].vertex[0].x;
stl->stats.max.y = stl->facet_start[0].vertex[0].y;
stl->stats.max.z = stl->facet_start[0].vertex[0].z;
for(i = 0; i < stl->stats.number_of_facets; i++) {
for(j = 0; j < 3; j++) {
stl->stats.min.x = STL_MIN(stl->stats.min.x,
stl->facet_start[i].vertex[j].x);
stl->stats.min.y = STL_MIN(stl->stats.min.y,
stl->facet_start[i].vertex[j].y);
stl->stats.min.z = STL_MIN(stl->stats.min.z,
stl->facet_start[i].vertex[j].z);
stl->stats.max.x = STL_MAX(stl->stats.max.x,
stl->facet_start[i].vertex[j].x);
stl->stats.max.y = STL_MAX(stl->stats.max.y,
stl->facet_start[i].vertex[j].y);
stl->stats.max.z = STL_MAX(stl->stats.max.z,
stl->facet_start[i].vertex[j].z);
}
}
stl->stats.size.x = stl->stats.max.x - stl->stats.min.x;
stl->stats.size.y = stl->stats.max.y - stl->stats.min.y;
stl->stats.size.z = stl->stats.max.z - stl->stats.min.z;
stl->stats.bounding_diameter = sqrt(
stl->stats.size.x * stl->stats.size.x +
stl->stats.size.y * stl->stats.size.y +
stl->stats.size.z * stl->stats.size.z
);
}
void
stl_mirror_xy(stl_file *stl) {
int i;
int j;
float temp_size;
if (stl->error) return;
for(i = 0; i < stl->stats.number_of_facets; i++) {
for(j = 0; j < 3; j++) {
stl->facet_start[i].vertex[j].z *= -1.0;
}
}
temp_size = stl->stats.min.z;
stl->stats.min.z = stl->stats.max.z;
stl->stats.max.z = temp_size;
stl->stats.min.z *= -1.0;
stl->stats.max.z *= -1.0;
stl_reverse_all_facets(stl);
stl->stats.facets_reversed -= stl->stats.number_of_facets; /* for not altering stats */
}
void
stl_mirror_yz(stl_file *stl) {
int i;
int j;
float temp_size;
if (stl->error) return;
for(i = 0; i < stl->stats.number_of_facets; i++) {
for(j = 0; j < 3; j++) {
stl->facet_start[i].vertex[j].x *= -1.0;
}
}
temp_size = stl->stats.min.x;
stl->stats.min.x = stl->stats.max.x;
stl->stats.max.x = temp_size;
stl->stats.min.x *= -1.0;
stl->stats.max.x *= -1.0;
stl_reverse_all_facets(stl);
stl->stats.facets_reversed -= stl->stats.number_of_facets; /* for not altering stats */
}
void
stl_mirror_xz(stl_file *stl) {
int i;
int j;
float temp_size;
if (stl->error) return;
for(i = 0; i < stl->stats.number_of_facets; i++) {
for(j = 0; j < 3; j++) {
stl->facet_start[i].vertex[j].y *= -1.0;
}
}
temp_size = stl->stats.min.y;
stl->stats.min.y = stl->stats.max.y;
stl->stats.max.y = temp_size;
stl->stats.min.y *= -1.0;
stl->stats.max.y *= -1.0;
stl_reverse_all_facets(stl);
stl->stats.facets_reversed -= stl->stats.number_of_facets; /* for not altering stats */
}
static float get_volume(stl_file *stl) {
long i;
stl_vertex p0;
stl_vertex p;
stl_normal n;
float height;
float area;
float volume = 0.0;
if (stl->error) return 0;
/* Choose a point, any point as the reference */
p0.x = stl->facet_start[0].vertex[0].x;
p0.y = stl->facet_start[0].vertex[0].y;
p0.z = stl->facet_start[0].vertex[0].z;
for(i = 0; i < stl->stats.number_of_facets; i++) {
p.x = stl->facet_start[i].vertex[0].x - p0.x;
p.y = stl->facet_start[i].vertex[0].y - p0.y;
p.z = stl->facet_start[i].vertex[0].z - p0.z;
/* Do dot product to get distance from point to plane */
n = stl->facet_start[i].normal;
height = (n.x * p.x) + (n.y * p.y) + (n.z * p.z);
area = get_area(&stl->facet_start[i]);
volume += (area * height) / 3.0;
}
return volume;
}
void stl_calculate_volume(stl_file *stl) {
if (stl->error) return;
stl->stats.volume = get_volume(stl);
}
static float get_area(stl_facet *facet) {
double cross[3][3];
float sum[3];
float n[3];
float area;
int i;
/* cast to double before calculating cross product because large coordinates
can result in overflowing product
(bad area is responsible for bad volume and bad facets reversal) */
for(i = 0; i < 3; i++) {
cross[i][0]=(((double)facet->vertex[i].y * (double)facet->vertex[(i + 1) % 3].z) -
((double)facet->vertex[i].z * (double)facet->vertex[(i + 1) % 3].y));
cross[i][1]=(((double)facet->vertex[i].z * (double)facet->vertex[(i + 1) % 3].x) -
((double)facet->vertex[i].x * (double)facet->vertex[(i + 1) % 3].z));
cross[i][2]=(((double)facet->vertex[i].x * (double)facet->vertex[(i + 1) % 3].y) -
((double)facet->vertex[i].y * (double)facet->vertex[(i + 1) % 3].x));
}
sum[0] = cross[0][0] + cross[1][0] + cross[2][0];
sum[1] = cross[0][1] + cross[1][1] + cross[2][1];
sum[2] = cross[0][2] + cross[1][2] + cross[2][2];
/* This should already be done. But just in case, let's do it again */
stl_calculate_normal(n, facet);
stl_normalize_vector(n);
area = 0.5 * (n[0] * sum[0] + n[1] * sum[1] + n[2] * sum[2]);
return area;
}
void stl_repair(stl_file *stl,
int fixall_flag,
int exact_flag,
int tolerance_flag,
float tolerance,
int increment_flag,
float increment,
int nearby_flag,
int iterations,
int remove_unconnected_flag,
int fill_holes_flag,
int normal_directions_flag,
int normal_values_flag,
int reverse_all_flag,
int verbose_flag) {
int i;
int last_edges_fixed = 0;
if (stl->error) return;
if(exact_flag || fixall_flag || nearby_flag || remove_unconnected_flag
|| fill_holes_flag || normal_directions_flag) {
if (verbose_flag)
printf("Checking exact...\n");
exact_flag = 1;
stl_check_facets_exact(stl);
stl->stats.facets_w_1_bad_edge =
(stl->stats.connected_facets_2_edge -
stl->stats.connected_facets_3_edge);
stl->stats.facets_w_2_bad_edge =
(stl->stats.connected_facets_1_edge -
stl->stats.connected_facets_2_edge);
stl->stats.facets_w_3_bad_edge =
(stl->stats.number_of_facets -
stl->stats.connected_facets_1_edge);
}
if(nearby_flag || fixall_flag) {
if(!tolerance_flag) {
tolerance = stl->stats.shortest_edge;
}
if(!increment_flag) {
increment = stl->stats.bounding_diameter / 10000.0;
}
if(stl->stats.connected_facets_3_edge < stl->stats.number_of_facets) {
for(i = 0; i < iterations; i++) {
if(stl->stats.connected_facets_3_edge <
stl->stats.number_of_facets) {
if (verbose_flag)
printf("\
Checking nearby. Tolerance= %f Iteration=%d of %d...",
tolerance, i + 1, iterations);
stl_check_facets_nearby(stl, tolerance);
if (verbose_flag)
printf(" Fixed %d edges.\n",
stl->stats.edges_fixed - last_edges_fixed);
last_edges_fixed = stl->stats.edges_fixed;
tolerance += increment;
} else {
if (verbose_flag)
printf("\
All facets connected. No further nearby check necessary.\n");
break;
}
}
} else {
if (verbose_flag)
printf("All facets connected. No nearby check necessary.\n");
}
}
if(remove_unconnected_flag || fixall_flag || fill_holes_flag) {
if(stl->stats.connected_facets_3_edge < stl->stats.number_of_facets) {
if (verbose_flag)
printf("Removing unconnected facets...\n");
stl_remove_unconnected_facets(stl);
} else
if (verbose_flag)
printf("No unconnected need to be removed.\n");
}
if(fill_holes_flag || fixall_flag) {
if(stl->stats.connected_facets_3_edge < stl->stats.number_of_facets) {
if (verbose_flag)
printf("Filling holes...\n");
stl_fill_holes(stl);
} else
if (verbose_flag)
printf("No holes need to be filled.\n");
}
if(reverse_all_flag) {
if (verbose_flag)
printf("Reversing all facets...\n");
stl_reverse_all_facets(stl);
}
if(normal_directions_flag || fixall_flag) {
if (verbose_flag)
printf("Checking normal directions...\n");
stl_fix_normal_directions(stl);
}
if(normal_values_flag || fixall_flag) {
if (verbose_flag)
printf("Checking normal values...\n");
stl_fix_normal_values(stl);
}
/* Always calculate the volume. It shouldn't take too long */
if (verbose_flag)
printf("Calculating volume...\n");
stl_calculate_volume(stl);
if(fixall_flag) {
if(stl->stats.volume < 0.0) {
if (verbose_flag)
printf("Reversing all facets because volume is negative...\n");
stl_reverse_all_facets(stl);
stl->stats.volume = -stl->stats.volume;
}
}
if(exact_flag) {
if (verbose_flag)
printf("Verifying neighbors...\n");
stl_verify_neighbors(stl);
}
}