pax_global_header00006660000000000000000000000064121650362030014510gustar00rootroot0000000000000052 comment=ade9952f6cf6d0a9eed6784151c39c549c14fa21 admesh-0.96/000077500000000000000000000000001216503620300127075ustar00rootroot00000000000000admesh-0.96/ADMESH.DOC000066400000000000000000000535601216503620300142100ustar00rootroot00000000000000Note: 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.96 ================= Input file : sphere.stl File type : Binary STL file Header : Processed by ADMesh version 0.96 ============== 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.96 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.96/AUTHORS000066400000000000000000000002671216503620300137640ustar00rootroot00000000000000Anthony Martin , the full source code; 1995, 1996 Andy Doucette , bug-fixes; 2013 Anton Gladky , switch to cmake; 2013 admesh-0.96/CMakeLists.txt000066400000000000000000000016541216503620300154550ustar00rootroot00000000000000PROJECT(admesh C) CMAKE_MINIMUM_REQUIRED(VERSION 2.8) INCLUDE(GNUInstallDirs) #=========================================================== IF(NOT(CMAKE_C_FLAGS)) SET(CMAKE_BUILD_TYPE Release) SET(CMAKE_C_FLAGS "-Wall -g -O2 -fstack-protector --param=ssp-buffer-size=4 -Wformat -Werror=format-security -D_FORTIFY_SOURCE=2") ENDIF (NOT(CMAKE_C_FLAGS)) #=========================================================== FIND_LIBRARY(M_LIB m) #=========================================================== ADD_EXECUTABLE(admesh admesh.c connect.c stl_io.c stlinit.c util.c normals.c shared.c) TARGET_LINK_LIBRARIES(admesh ${M_LIB}) INSTALL(TARGETS admesh DESTINATION ${CMAKE_INSTALL_BINDIR}) #=========================================================== INSTALL(FILES ADMESH.DOC block.stl DESTINATION ${CMAKE_INSTALL_DOCDIR}) INSTALL(FILES admesh.1 DESTINATION ${CMAKE_INSTALL_MANDIR}) #=========================================================== admesh-0.96/COPYING000066400000000000000000000430761216503620300137540ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: 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) 19yy 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., 675 Mass Ave, Cambridge, MA 02139, 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) 19yy 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 Library General Public License instead of this License. admesh-0.96/ChangeLog000066400000000000000000000047031216503620300144650ustar00rootroot00000000000000Tue 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 Wed, 03 Jul 2013 16:57:55 +0200 Anton Gladky Andy Doucette (2): Fix merge-command. Fix translate-command. Anton Gladky (11): Add cmake-support. Remove Makefile and configure-files. Remove embedded getopt-files. Install block.stl. Update doc-files. Fix compilation warnings. Add version.h. Close file properly. Import patch from Debian. Set version 0.96 Generate and install manpage. Used help2man. Update version number in README and Doc admesh-0.96/INSTALL000066400000000000000000000013271216503620300137430ustar00rootroot00000000000000To install ADMesh, you will need a system with a c compiler and cmake. Do the following: 1. Get the file admesh-XXX.tar.gz 2. Extract the archive. i.e. type something like the following: tar -zxvf admesh-XXX.tar.gz The source files will be extracted into a directory 3. Create build directory: mkdir build cd build 4. Type the following: cmake -DCMAKE_INSTALL_PREFIX=/INSTALL_PATH/FOR/ADMESH ./../ADMESH_SRC_DIR make make install That should do it. You should have an executable file called admesh in: /INSTALL_PATH/FOR/ADMESH/bin/admesh To regenerate manpage for admesh, use help2man tool: help2man admessh --name="a program for processing triangulated solid meshes" > admesh.1 admesh-0.96/README000066400000000000000000000110601216503620300135650ustar00rootroot00000000000000 ADMesh version 0.96 ******************** 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. 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.96 ================= Input file : sphere.stl File type : Binary STL file Header : Processed by ADMesh version 0.96 ============== 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) There are a few features that are "missing" from ADMesh but that might be added in the future. These include: * Display capability * Slicing * Splitting a file into smaller files ADMesh is free but Copyrighted software. It is distributed under the terms of the GNU General Public License (GPL). It is NOT in the public domain and its source code cannot be used in commercial software. Details of the GPL are in the file COPYING that comes with the ADMesh software package. ADMesh can be obtained on Launchpad from: https://launchpad.net/admesh or from GitHub: https://github.com/gladk/admesh Send questions, comments, suggestions, etc. to me at the following address. Anthony D. Martin admesh-0.96/admesh.1000066400000000000000000000054441216503620300142410ustar00rootroot00000000000000.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.43.3. .TH ADMESH "1" "July 2013" "ADMesh - version 0.96" "User Commands" .SH NAME ADMesh \- a program for processing triangulated solid meshes .SH SYNOPSIS .B admesh [\fIOPTION\fR]... \fIfile\fR .SH DESCRIPTION ADMesh version 0.96 Copyright \(co 1995, 1996 Anthony D. Martin .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 "SEE ALSO" The full documentation for .B ADMesh is maintained as a Texinfo manual. If the .B info and .B ADMesh programs are properly installed at your site, the command .IP .B info ADMesh .PP should give you access to the complete manual. admesh-0.96/admesh.c000066400000000000000000000354751216503620300143320ustar00rootroot00000000000000/* ADMesh -- process triangulated solid meshes * 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Questions, comments, suggestions, etc to */ #include #include #include #include "stl.h" #include "version.h" static void usage(int status, char *program_name); int main(int argc, char **argv) { stl_file stl_in; int i; int last_edges_fixed = 0; 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; 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); break; } } if(help_flag) { usage(0, program_name); } if(version_flag) { printf("ADMesh - version %g\n",VERSION); exit(0); } if(optind == argc) { printf("No input file name given.\n"); usage(1, program_name); } else { input_file = argv[optind]; } printf("\ ADMesh version %g, 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",VERSION); printf("Opening %s\n", input_file); stl_open(&stl_in, input_file); 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); } if(exact_flag || fixall_flag || nearby_flag || remove_unconnected_flag || fill_holes_flag || normal_directions_flag) { printf("Checking exact...\n"); exact_flag = 1; stl_check_facets_exact(&stl_in); stl_in.stats.facets_w_1_bad_edge = (stl_in.stats.connected_facets_2_edge - stl_in.stats.connected_facets_3_edge); stl_in.stats.facets_w_2_bad_edge = (stl_in.stats.connected_facets_1_edge - stl_in.stats.connected_facets_2_edge); stl_in.stats.facets_w_3_bad_edge = (stl_in.stats.number_of_facets - stl_in.stats.connected_facets_1_edge); } if(nearby_flag || fixall_flag) { if(!tolerance_flag) { tolerance = stl_in.stats.shortest_edge; } if(!increment_flag) { increment = stl_in.stats.bounding_diameter / 10000.0; } if(stl_in.stats.connected_facets_3_edge < stl_in.stats.number_of_facets) { for(i = 0; i < iterations; i++) { if(stl_in.stats.connected_facets_3_edge < stl_in.stats.number_of_facets) { printf("\ Checking nearby. Tolerance= %f Iteration=%d of %d...", tolerance, i + 1, iterations); stl_check_facets_nearby(&stl_in, tolerance); printf(" Fixed %d edges.\n", stl_in.stats.edges_fixed - last_edges_fixed); last_edges_fixed = stl_in.stats.edges_fixed; tolerance += increment; } else { printf("\ All facets connected. No further nearby check necessary.\n"); break; } } } else { printf("All facets connected. No nearby check necessary.\n"); } } if(remove_unconnected_flag || fixall_flag || fill_holes_flag) { if(stl_in.stats.connected_facets_3_edge < stl_in.stats.number_of_facets) { printf("Removing unconnected facets...\n"); stl_remove_unconnected_facets(&stl_in); } else printf("No unconnected need to be removed.\n"); } if(fill_holes_flag || fixall_flag) { if(stl_in.stats.connected_facets_3_edge < stl_in.stats.number_of_facets) { printf("Filling holes...\n"); stl_fill_holes(&stl_in); } else printf("No holes need to be filled.\n"); } if(reverse_all_flag) { printf("Reversing all facets...\n"); stl_reverse_all_facets(&stl_in); } if(normal_directions_flag || fixall_flag) { printf("Checking normal directions...\n"); stl_fix_normal_directions(&stl_in); } if(normal_values_flag || fixall_flag) { printf("Checking normal values...\n"); stl_fix_normal_values(&stl_in); } /* Always calculate the volume. It shouldn't take too long */ printf("Calculating volume...\n"); stl_calculate_volume(&stl_in); if(exact_flag) { printf("Verifying neighbors...\n"); stl_verify_neighbors(&stl_in); } 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(write_dxf_flag) { printf("Writing DXF file %s\n", dxf_name); char *tmp_msg = NULL; sprintf(tmp_msg,"Created by ADMesh version %g",VERSION); stl_write_dxf(&stl_in, dxf_name, tmp_msg); } if(write_vrml_flag) { printf("Writing VRML file %s\n", vrml_name); stl_write_vrml(&stl_in, vrml_name); } if(write_ascii_stl_flag) { printf("Writing ascii file %s\n", ascii_name); char *tmp_msg = NULL; sprintf(tmp_msg,"Processed by ADMesh version %g",VERSION); stl_write_ascii(&stl_in, ascii_name, tmp_msg); } if(write_binary_stl_flag) { printf("Writing binary file %s\n", binary_name); char *tmp_msg = NULL; sprintf(tmp_msg,"Processed by ADMesh version %g",VERSION); stl_write_binary(&stl_in, binary_name, tmp_msg); } if(exact_flag) { stl_stats_out(&stl_in, stdout, input_file); } stl_close(&stl_in); exit(0); } 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\ ADMesh version %g\n\ Copyright (C) 1995, 1996 Anthony D. Martin\n\ Usage: %s [OPTION]... file\n", VERSION, program_name); printf("\n\ --x-rotate=angle Rotate CCW about x-axis by angle degrees\n\ --y-rotate=angle Rotate CCW about y-axis by angle degrees\n\ --z-rotate=angle Rotate CCW about z-axis by angle degrees\n\ --xy-mirror Mirror about the xy plane\n\ --yz-mirror Mirror about the yz plane\n\ --xz-mirror Mirror about the xz plane\n\ --scale=factor Scale the file by factor (multiply by factor)\n\ --translate=x,y,z Translate the file to x, y, and z\n\ --merge=name Merge file called name with input file\n\ -e, --exact Only check for perfectly matched edges\n\ -n, --nearby Find and connect nearby facets. Correct bad facets\n\ -t, --tolerance=tol Initial tolerance to use for nearby check = tol\n\ -i, --iterations=i Number of iterations for nearby check = i\n\ -m, --increment=inc Amount to increment tolerance after iteration=inc\n\ -u, --remove-unconnected Remove facets that have 0 neighbors\n\ -f, --fill-holes Add facets to fill holes\n\ -d, --normal-directions Check and fix direction of normals(ie cw, ccw)\n\ --reverse-all Reverse the directions of all facets and normals\n\ -v, --normal-values Check and fix normal values\n\ -c, --no-check Don't do any check on input file\n\ -b, --write-binary-stl=name Output a binary STL file called name\n\ -a, --write-ascii-stl=name Output an ascii STL file called name\n\ --write-off=name Output a Geomview OFF format file called name\n\ --write-dxf=name Output a DXF format file called name\n\ --write-vrml=name Output a VRML format file called name\n\ --help Display this help and exit\n\ --version Output version information and exit\n\ \n\ The functions are executed in the same order as the options shown here.\n\ So check here to find what happens if, for example, --translate and --merge\n\ options are specified together. The order of the options specified on the\n\ command line is not important.\n"); } exit(status); } admesh-0.96/block.stl000066400000000000000000000065651216503620300145410ustar00rootroot00000000000000SOLID 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.96/connect.c000066400000000000000000000732571216503620300145220ustar00rootroot00000000000000/* ADMesh -- process triangulated solid meshes * Copyright (C) 1995 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Questions, comments, suggestions, etc to */ #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); static void stl_add_facet(stl_file *stl, stl_facet *new_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; 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; 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; 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 = calloc(stl->M, sizeof(*stl->heads)); if(stl->heads == NULL) perror("stl_initialize_facet_check_exact"); stl->tail = 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; 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 = 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 = 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->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->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; 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 = calloc(stl->M, sizeof(*stl->heads)); if(stl->heads == NULL) perror("stl_initialize_facet_check_nearby"); stl->tail = 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; /* 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) { 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; 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; 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"); exit(1); break; } } } 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; 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); exit(1); } 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; /* 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( !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]; stl->neighbors_start[neighbor1].neighbor[(vnot1 + 1) % 3] = neighbor2; stl->neighbors_start[neighbor2].neighbor[(vnot2 + 1) % 3] = neighbor1; stl->neighbors_start[neighbor1].which_vertex_not[(vnot1 + 1) % 3] = vnot2; 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; /* 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; /* 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"); exit(1); break; } } } } } static void stl_add_facet(stl_file *stl, stl_facet *new_facet) { stl->stats.facets_added += 1; if(stl->stats.facets_malloced < stl->stats.number_of_facets + 1) { stl->facet_start = realloc(stl->facet_start, (sizeof(stl_facet) * (stl->stats.facets_malloced + 256))); if(stl->facet_start == NULL) perror("stl_add_facet"); stl->neighbors_start = 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.96/normals.c000066400000000000000000000271041216503620300145320ustar00rootroot00000000000000/* ADMesh -- process triangulated solid meshes * Copyright (C) 1995 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Questions, comments, suggestions, etc to */ #include #include #include #include #include "stl.h" static void stl_reverse_facet(stl_file *stl, int facet_num); /* static float stl_calculate_area(stl_facet *facet); */ 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; int checked_before = 0; struct stl_normal { int facet_num; struct stl_normal *next; }; struct stl_normal *head; struct stl_normal *tail; struct stl_normal *new; struct stl_normal *temp; /* Initialize linked list. */ head = malloc(sizeof(struct stl_normal)); if(head == NULL) perror("stl_fix_normal_directions"); tail = 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 = 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; /* edge_num = 0; vnot = stl->neighbors_start[0].which_vertex_not[0]; */ 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) { //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. */ new = malloc(sizeof(struct stl_normal)); if(new == NULL) perror("stl_fix_normal_directions"); new->facet_num = stl->neighbors_start[facet_num].neighbor[j]; new->next = head->next; head->next = new; } } } /* 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; /* There are (checked-checked_before) facets */ /* in part stl->stats.number_of_parts */ checked_before = checked; 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]); } /* static float stl_calculate_area(stl_facet *facet) { float cross[3][3]; float sum[3]; float normal[3]; float area; int i; for(i = 0; i < 3; i++) { cross[i][0] = ((facet->vertex[i].y * facet->vertex[(i + 1) % 3].z) - (facet->vertex[i].z * facet->vertex[(i + 1) % 3].y)); cross[i][1] = ((facet->vertex[i].z * facet->vertex[(i + 1) % 3].x) - (facet->vertex[i].x * facet->vertex[(i + 1) % 3].z)); cross[i][2] = ((facet->vertex[i].x * facet->vertex[(i + 1) % 3].y) - (facet->vertex[i].y * 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]; stl_calculate_normal(normal, facet); stl_normalize_vector(normal); area = 0.5 * (normal[0] * sum[0] + normal[1] * sum[1] + normal[2] * sum[2]); return ABS(area); } */ 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] = 1.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; 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]; 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.96/shared.c000066400000000000000000000142571216503620300143320ustar00rootroot00000000000000/* ADMesh -- process triangulated solid meshes * 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Questions, comments, suggestions, etc to */ #include #include #include "stl.h" 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; stl->v_indices = calloc(stl->stats.number_of_facets, sizeof(v_indices_struct)); if(stl->v_indices == NULL) perror("stl_generate_shared_vertices"); stl->v_shared = 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 = 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; /* Open the file */ fp = fopen(file, "w"); if(fp == NULL) { error_msg = 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); exit(1); } 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; /* Open the file */ fp = fopen(file, "w"); if(fp == NULL) { error_msg = 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); exit(1); } 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); } admesh-0.96/stl.h000066400000000000000000000123041216503620300136620ustar00rootroot00000000000000/* ADMesh -- process triangulated solid meshes * 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Questions, comments, suggestions, etc to */ #include #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} 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; }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_write_ascii(stl_file *stl, char *file, char *label); extern void stl_write_binary(stl_file *stl, char *file, char *label); 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_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_generate_shared_vertices(stl_file *stl); 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); admesh-0.96/stl_io.c000066400000000000000000000334121216503620300143470ustar00rootroot00000000000000/* ADMesh -- process triangulated solid meshes * 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Questions, comments, suggestions, etc to */ #include #include #include "stl.h" #include "version.h" #if !defined(SEEK_SET) #define SEEK_SET 0 #define SEEK_CUR 1 #define SEEK_END 2 #endif static void stl_put_little_int(FILE *fp, int value); static void stl_put_little_float(FILE *fp, float value_in); void stl_print_edges(stl_file *stl, FILE *file) { int i; int edges_allocated; 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) { fprintf(file, "\n\ ================= Results produced by ADMesh version %g ================\n",VERSION); 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, char *file, char *label) { int i; FILE *fp; char *error_msg; /* Open the file */ fp = fopen(file, "w"); if(fp == NULL) { error_msg = 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); exit(1); } 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; /* Open the file */ fp = fopen(file, "w"); if(fp == NULL) { error_msg = 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); exit(1); } 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); } static 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); } static 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(stl_file *stl, char *file, char *label) { FILE *fp; int i; char *error_msg; /* Open the file */ fp = fopen(file, "w"); if(fp == NULL) { error_msg = 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); exit(1); } 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); 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); } fclose(fp); } void stl_write_vertex(stl_file *stl, int facet, int vertex) { 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) { 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) { 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) { 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; /* Open the file */ fp = fopen(file, "w"); if(fp == NULL) { error_msg = 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); exit(1); } 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; /* Open the file */ fp = fopen(file, "w"); if(fp == NULL) { error_msg = 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); exit(1); } 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); } admesh-0.96/stlinit.c000066400000000000000000000311621216503620300145440ustar00rootroot00000000000000/* ADMesh -- process triangulated solid meshes * 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Questions, comments, suggestions, etc to */ #include #include #include #include #include "stl.h" #if !defined(SEEK_SET) #define SEEK_SET 0 #define SEEK_CUR 1 #define SEEK_END 2 #endif static void stl_initialize(stl_file *stl, char *file); static void stl_allocate(stl_file *stl); static void stl_read(stl_file *stl, int first_facet, int first); static void stl_reallocate(stl_file *stl); static int stl_get_little_int(FILE *fp); static float stl_get_little_float(FILE *fp); void stl_open(stl_file *stl, char *file) { stl_initialize(stl, file); stl_allocate(stl); stl_read(stl, 0, 1); fclose(stl->fp); } static int stl_get_little_int(FILE *fp) { int value; value = fgetc(fp) & 0xFF; value |= (fgetc(fp) & 0xFF) << 0x08; value |= (fgetc(fp) & 0xFF) << 0x10; value |= (fgetc(fp) & 0xFF) << 0x18; return(value); } static float stl_get_little_float(FILE *fp) { union { int int_value; float float_value; } value; value.int_value = fgetc(fp) & 0xFF; value.int_value |= (fgetc(fp) & 0xFF) << 0x08; value.int_value |= (fgetc(fp) & 0xFF) << 0x10; value.int_value |= (fgetc(fp) & 0xFF) << 0x18; return(value.float_value); } static void stl_initialize(stl_file *stl, char *file) { long file_size; int header_num_facets; int num_facets; int i, j; unsigned char chtest[128]; int num_lines = 1; char *error_msg; 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.volume = -1.0; stl->neighbors_start = NULL; stl->facet_start = NULL; stl->v_indices = NULL; stl->v_shared = NULL; /* Open the file */ stl->fp = fopen(file, "r"); if(stl->fp == NULL) { error_msg = 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); exit(1); } /* 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); size_t retSize = fread(chtest, sizeof(chtest), 1, stl->fp); stl->stats.type = ascii; for(i = 0; i < sizeof(chtest); i++) { if(chtest[i] > 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); exit(1); } num_facets = (file_size - HEADER_SIZE) / SIZEOF_STL_FACET; /* Read the header */ retSize = fread(stl->stats.header, LABEL_SIZE, 1, stl->fp); if (retSize>79) { stl->stats.header[80] = '\0'; } /* Read the int following the header. This should contain # of facets */ header_num_facets = stl_get_little_int(stl->fp); if(num_facets != 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 { /* 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; } static void stl_allocate(stl_file *stl) { /* Allocate memory for the entire .STL file */ stl->facet_start = 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 = calloc(stl->stats.number_of_facets, sizeof(stl_neighbors)); if(stl->facet_start == NULL) perror("stl_initialize"); } //This function reads file_to_merge and ADDs the contents of the file to the // already loaded and filled stl. void stl_open_merge(stl_file *stl, char *file_to_merge) { int num_facets_so_far; //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: stl_type origStlType=stl->stats.type; //Record the file pointer too: FILE *origFp=stl->fp; //Create an stl_file structure for the file to merge: stl_file stl_to_merge; //Initialize the sturucture with zero stats, header info and sizes: stl_initialize(&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; } static void stl_reallocate(stl_file *stl) { /* Reallocate more memory for the .STL file(s) */ stl->facet_start = 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 = 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. static void stl_read(stl_file *stl, int first_facet, int first) { stl_facet facet; int i; float diff_x; float diff_y; float diff_z; float max_diff; 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 */ { facet.normal.x = stl_get_little_float(stl->fp); facet.normal.y = stl_get_little_float(stl->fp); facet.normal.z = stl_get_little_float(stl->fp); facet.vertex[0].x = stl_get_little_float(stl->fp); facet.vertex[0].y = stl_get_little_float(stl->fp); facet.vertex[0].z = stl_get_little_float(stl->fp); facet.vertex[1].x = stl_get_little_float(stl->fp); facet.vertex[1].y = stl_get_little_float(stl->fp); facet.vertex[1].z = stl_get_little_float(stl->fp); facet.vertex[2].x = stl_get_little_float(stl->fp); facet.vertex[2].y = stl_get_little_float(stl->fp); facet.vertex[2].z = stl_get_little_float(stl->fp); facet.extra[0] = fgetc(stl->fp); facet.extra[1] = fgetc(stl->fp); } else /* Read a single facet from an ASCII .STL file */ { size_t retSize = fscanf(stl->fp, "%*s %*s %f %f %f\n", &facet.normal.x, &facet.normal.y, &facet.normal.z); retSize = fscanf(stl->fp, "%*s %*s"); retSize = fscanf(stl->fp, "%*s %f %f %f\n", &facet.vertex[0].x, &facet.vertex[0].y, &facet.vertex[0].z); retSize = fscanf(stl->fp, "%*s %f %f %f\n", &facet.vertex[1].x, &facet.vertex[1].y, &facet.vertex[1].z); retSize = fscanf(stl->fp, "%*s %f %f %f\n", &facet.vertex[2].x, &facet.vertex[2].y, &facet.vertex[2].z); retSize = fscanf(stl->fp, "%*s"); retSize = fscanf(stl->fp, "%*s"); } /* Write the facet into memory. */ stl->facet_start[i] = facet; /* 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); } 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_close(stl_file *stl) { 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.96/util.c000066400000000000000000000215661216503620300140420ustar00rootroot00000000000000/* ADMesh -- process triangulated solid meshes * 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Questions, comments, suggestions, etc to */ #include #include #include #include #include "stl.h" static void stl_rotate(float *x, float *y, float angle); static void stl_get_size(stl_file *stl); 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; 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, "first facet", i); stl_write_facet(stl, "second facet", neighbor); } } } } //Translates the stl by x,y,z, from wherever it is currently: void stl_translate(stl_file *stl, float x, float y, float z) { int i; int j; 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.max.x += x; stl->stats.max.y += y; stl->stats.max.z += z; stl->stats.min.x += x; stl->stats.min.y += y; stl->stats.min.z += z; } void stl_scale(stl_file *stl, float factor) { int i; int j; stl->stats.min.x *= factor; stl->stats.min.y *= factor; stl->stats.min.z *= factor; stl->stats.max.x *= factor; stl->stats.max.y *= factor; stl->stats.max.z *= factor; for(i = 0; i < stl->stats.number_of_facets; i++) { for(j = 0; j < 3; j++) { stl->facet_start[i].vertex[j].x *= factor; stl->facet_start[i].vertex[j].y *= factor; stl->facet_start[i].vertex[j].z *= factor; } } } static void calculate_normals(stl_file *stl) { long i; float normal[3]; 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; 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; 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; 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); } static void stl_get_size(stl_file *stl) { int i; int j; 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); } } } void stl_mirror_xy(stl_file *stl) { int i; int j; float temp_size; 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; } void stl_mirror_yz(stl_file *stl) { int i; int j; float temp_size; 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; } void stl_mirror_xz(stl_file *stl) { int i; int j; float temp_size; 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; } 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; /* 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) { stl->stats.volume = get_volume(stl); if(stl->stats.volume < 0.0){ stl_reverse_all_facets(stl); stl->stats.volume = -stl->stats.volume; } } static float get_area(stl_facet *facet) { float cross[3][3]; float sum[3]; float n[3]; float area; int i; for(i = 0; i < 3; i++){ cross[i][0]=((facet->vertex[i].y * facet->vertex[(i + 1) % 3].z) - (facet->vertex[i].z * facet->vertex[(i + 1) % 3].y)); cross[i][1]=((facet->vertex[i].z * facet->vertex[(i + 1) % 3].x) - (facet->vertex[i].x * facet->vertex[(i + 1) % 3].z)); cross[i][2]=((facet->vertex[i].x * facet->vertex[(i + 1) % 3].y) - (facet->vertex[i].y * 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; } admesh-0.96/version.h000066400000000000000000000000251216503620300145420ustar00rootroot00000000000000#define VERSION 0.96