2013-06-23 18:18:38 +02:00
/* ADMesh -- process triangulated solid meshes
2014-09-23 14:34:37 +02:00
* Copyright (C) 1995, 1996 Anthony D. Martin <amartin@engr.csulb.edu>
* Copyright (C) 2013, 2014 several contributors, see AUTHORS
2013-06-23 18:18:38 +02:00
*
* 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
2014-09-23 14:34:37 +02:00
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
2013-06-23 18:18:38 +02:00
* 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.
2014-09-23 14:34:37 +02:00
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
2013-06-23 18:18:38 +02:00
*
2014-09-23 14:34:37 +02:00
* Questions, comments, suggestions, etc to
* https://github.com/admesh/admesh/issues
2013-06-23 18:18:38 +02:00
*/
#include <stdlib.h>
2014-09-23 14:34:37 +02:00
#include <string.h>
2013-06-23 18:18:38 +02:00
2019-06-04 18:25:53 +02:00
#include <vector>
2017-08-03 17:31:31 +02:00
#include <boost/nowide/cstdio.hpp>
2013-06-23 18:18:38 +02:00
#include "stl.h"
2019-06-10 18:30:54 +02:00
void stl_generate_shared_vertices ( stl_file * stl , indexed_triangle_set & its )
2019-06-04 18:25:53 +02:00
{
// 3 indices to vertex per face
2019-06-10 18:30:54 +02:00
its . indices . assign ( stl -> stats . number_of_facets , v_indices_struct ());
2019-06-04 18:25:53 +02:00
// Shared vertices (3D coordinates)
2019-06-10 18:30:54 +02:00
its . vertices . clear ();
its . vertices . reserve ( stl -> stats . number_of_facets / 2 );
2014-09-23 14:34:37 +02:00
2019-06-04 18:25:53 +02:00
// A degenerate mesh may contain loops: Traversing a fan will end up in an endless loop
// while never reaching the starting face. To avoid these endless loops, traversed faces at each fan traversal
// are marked with a unique fan_traversal_stamp.
unsigned int fan_traversal_stamp = 0 ;
std :: vector < unsigned int > fan_traversal_facet_visited ( stl -> stats . number_of_facets , 0 );
2013-06-23 18:18:38 +02:00
2019-06-04 18:25:53 +02:00
for ( uint32_t facet_idx = 0 ; facet_idx < stl -> stats . number_of_facets ; ++ facet_idx ) {
for ( int j = 0 ; j < 3 ; ++ j ) {
2019-06-10 18:30:54 +02:00
if ( its . indices [ facet_idx ]. vertex [ j ] != - 1 )
2019-06-04 18:25:53 +02:00
// Shared vertex was already assigned.
continue ;
// Create a new shared vertex.
2019-06-10 18:30:54 +02:00
its . vertices . emplace_back ( stl -> facet_start [ facet_idx ]. vertex [ j ]);
2019-06-04 18:25:53 +02:00
// Traverse the fan around the j-th vertex of the i-th face, assign the newly created shared vertex index to all the neighboring triangles in the triangle fan.
int facet_in_fan_idx = facet_idx ;
bool edge_direction = false ;
bool traversal_reversed = false ;
int vnot = ( j + 2 ) % 3 ;
// Increase the
++ fan_traversal_stamp ;
for (;;) {
// Next edge on facet_in_fan_idx to be traversed. The edge is indexed by its starting vertex index.
int next_edge = 0 ;
// Vertex index in facet_in_fan_idx, which is being pivoted around, and which is being assigned a new shared vertex.
int pivot_vertex = 0 ;
if ( vnot > 2 ) {
// The edge of facet_in_fan_idx opposite to vnot is equally oriented, therefore
// the neighboring facet is flipped.
if ( ! edge_direction ) {
pivot_vertex = ( vnot + 2 ) % 3 ;
next_edge = pivot_vertex ;
} else {
pivot_vertex = ( vnot + 1 ) % 3 ;
next_edge = vnot % 3 ;
}
edge_direction = ! edge_direction ;
} else {
// The neighboring facet is correctly oriented.
if ( ! edge_direction ) {
pivot_vertex = ( vnot + 1 ) % 3 ;
next_edge = vnot ;
} else {
pivot_vertex = ( vnot + 2 ) % 3 ;
next_edge = pivot_vertex ;
}
}
2019-06-10 18:30:54 +02:00
its . indices [ facet_in_fan_idx ]. vertex [ pivot_vertex ] = its . vertices . size () - 1 ;
2019-06-04 18:25:53 +02:00
fan_traversal_facet_visited [ facet_in_fan_idx ] = fan_traversal_stamp ;
2013-06-23 18:18:38 +02:00
2019-06-04 18:25:53 +02:00
// next_edge is an index of the starting vertex of the edge, not an index of the opposite vertex to the edge!
int next_facet = stl -> neighbors_start [ facet_in_fan_idx ]. neighbor [ next_edge ];
if ( next_facet == - 1 ) {
// No neighbor going in the current direction.
if ( traversal_reversed ) {
// Went to one limit, then turned back and reached the other limit. Quit the fan traversal.
break ;
} else {
// Reached the first limit. Now try to reverse and traverse up to the other limit.
edge_direction = true ;
vnot = ( j + 1 ) % 3 ;
traversal_reversed = true ;
facet_in_fan_idx = facet_idx ;
}
} else if ( next_facet == facet_idx ) {
// Traversed a closed fan all around.
// assert(! traversal_reversed);
break ;
} else if ( next_facet >= ( int ) stl -> stats . number_of_facets ) {
// The mesh is not valid!
// assert(false);
break ;
} else if ( fan_traversal_facet_visited [ next_facet ] == fan_traversal_stamp ) {
// Traversed a closed fan all around, but did not reach the starting face.
// This indicates an invalid geometry (non-manifold).
//assert(false);
break ;
} else {
// Continue traversal.
// next_edge is an index of the starting vertex of the edge, not an index of the opposite vertex to the edge!
vnot = stl -> neighbors_start [ facet_in_fan_idx ]. which_vertex_not [ next_edge ];
facet_in_fan_idx = next_facet ;
}
}
}
}
2013-06-23 18:18:38 +02:00
}
2019-06-10 18:30:54 +02:00
bool its_write_off ( const indexed_triangle_set & its , const char * file )
2019-06-04 18:25:53 +02:00
{
/* Open the file */
FILE * fp = boost :: nowide :: fopen ( file , "w" );
if ( fp == nullptr ) {
char * error_msg = ( char * ) malloc ( 81 + strlen ( file )); /* Allow 80 chars+file size for message */
sprintf ( error_msg , "stl_write_ascii: Couldn't open %s for writing" , file );
perror ( error_msg );
free ( error_msg );
2019-06-10 17:17:36 +02:00
return false ;
2019-06-04 18:25:53 +02:00
}
2014-09-23 14:34:37 +02:00
2019-06-04 18:25:53 +02:00
fprintf ( fp , "OFF \n " );
2019-06-10 18:30:54 +02:00
fprintf ( fp , "%d %d 0 \n " , ( int ) its . vertices . size (), ( int ) its . indices . size ());
for ( int i = 0 ; i < its . vertices . size (); ++ i )
fprintf ( fp , " \t %f %f %f \n " , its . vertices [ i ]( 0 ), its . vertices [ i ]( 1 ), its . vertices [ i ]( 2 ));
for ( uint32_t i = 0 ; i < its . indices . size (); ++ i )
fprintf ( fp , " \t 3 %d %d %d \n " , its . indices [ i ]. vertex [ 0 ], its . indices [ i ]. vertex [ 1 ], its . indices [ i ]. vertex [ 2 ]);
2019-06-04 18:25:53 +02:00
fclose ( fp );
2019-06-10 17:17:36 +02:00
return true ;
2013-06-23 18:18:38 +02:00
}
2019-06-10 18:30:54 +02:00
bool its_write_vrml ( const indexed_triangle_set & its , const char * file )
2019-06-04 18:25:53 +02:00
{
/* Open the file */
FILE * fp = boost :: nowide :: fopen ( file , "w" );
if ( fp == nullptr ) {
char * error_msg = ( char * ) malloc ( 81 + strlen ( file )); /* Allow 80 chars+file size for message */
sprintf ( error_msg , "stl_write_ascii: Couldn't open %s for writing" , file );
perror ( error_msg );
free ( error_msg );
2019-06-10 17:17:36 +02:00
return false ;
2019-06-04 18:25:53 +02:00
}
2014-09-23 14:34:37 +02:00
2019-06-04 18:25:53 +02:00
fprintf ( fp , "#VRML V1.0 ascii \n\n " );
fprintf ( fp , "Separator { \n " );
fprintf ( fp , " \t DEF STLShape ShapeHints { \n " );
fprintf ( fp , " \t\t vertexOrdering COUNTERCLOCKWISE \n " );
fprintf ( fp , " \t\t faceType CONVEX \n " );
fprintf ( fp , " \t\t shapeType SOLID \n " );
fprintf ( fp , " \t\t creaseAngle 0.0 \n " );
fprintf ( fp , " \t } \n " );
fprintf ( fp , " \t DEF STLModel Separator { \n " );
fprintf ( fp , " \t\t DEF STLColor Material { \n " );
fprintf ( fp , " \t\t\t emissiveColor 0.700000 0.700000 0.000000 \n " );
fprintf ( fp , " \t\t } \n " );
fprintf ( fp , " \t\t DEF STLVertices Coordinate3 { \n " );
fprintf ( fp , " \t\t\t point [ \n " );
2014-09-23 14:34:37 +02:00
2019-06-04 18:25:53 +02:00
int i = 0 ;
2019-06-10 18:30:54 +02:00
for (; i + 1 < its . vertices . size (); ++ i )
fprintf ( fp , " \t\t\t\t %f %f %f, \n " , its . vertices [ i ]( 0 ), its . vertices [ i ]( 1 ), its . vertices [ i ]( 2 ));
fprintf ( fp , " \t\t\t\t %f %f %f] \n " , its . vertices [ i ]( 0 ), its . vertices [ i ]( 1 ), its . vertices [ i ]( 2 ));
2019-06-04 18:25:53 +02:00
fprintf ( fp , " \t\t } \n " );
fprintf ( fp , " \t\t DEF STLTriangles IndexedFaceSet { \n " );
fprintf ( fp , " \t\t\t coordIndex [ \n " );
2013-06-23 18:18:38 +02:00
2019-06-10 18:30:54 +02:00
for ( size_t i = 0 ; i + 1 < its . indices . size (); ++ i )
fprintf ( fp , " \t\t\t\t %d, %d, %d, -1, \n " , its . indices [ i ]. vertex [ 0 ], its . indices [ i ]. vertex [ 1 ], its . indices [ i ]. vertex [ 2 ]);
fprintf ( fp , " \t\t\t\t %d, %d, %d, -1] \n " , its . indices [ i ]. vertex [ 0 ], its . indices [ i ]. vertex [ 1 ], its . indices [ i ]. vertex [ 2 ]);
2019-06-04 18:25:53 +02:00
fprintf ( fp , " \t\t } \n " );
fprintf ( fp , " \t } \n " );
fprintf ( fp , "} \n " );
fclose ( fp );
2019-06-10 17:17:36 +02:00
return true ;
2013-06-23 18:18:38 +02:00
}
2013-06-23 21:11:46 +02:00
2019-06-10 18:30:54 +02:00
bool its_write_obj ( const indexed_triangle_set & its , const char * file )
2019-06-04 18:25:53 +02:00
{
2014-09-23 14:34:37 +02:00
2019-06-04 18:25:53 +02:00
FILE * fp = boost :: nowide :: fopen ( file , "w" );
if ( fp == nullptr ) {
char * error_msg = ( char * ) malloc ( 81 + strlen ( file )); /* Allow 80 chars+file size for message */
sprintf ( error_msg , "stl_write_ascii: Couldn't open %s for writing" , file );
perror ( error_msg );
free ( error_msg );
2019-06-10 17:17:36 +02:00
return false ;
2019-06-04 18:25:53 +02:00
}
2014-09-23 14:34:37 +02:00
2019-06-10 18:30:54 +02:00
for ( size_t i = 0 ; i < its . vertices . size (); ++ i )
fprintf ( fp , "v %f %f %f \n " , its . vertices [ i ]( 0 ), its . vertices [ i ]( 1 ), its . vertices [ i ]( 2 ));
for ( size_t i = 0 ; i < its . indices . size (); ++ i )
fprintf ( fp , "f %d %d %d \n " , its . indices [ i ]. vertex [ 0 ] + 1 , its . indices [ i ]. vertex [ 1 ] + 1 , its . indices [ i ]. vertex [ 2 ] + 1 );
2019-06-04 18:25:53 +02:00
fclose ( fp );
2019-06-10 17:17:36 +02:00
return true ;
2013-06-23 21:11:46 +02:00
}
2019-06-10 18:30:54 +02:00
// Check validity of the mesh, assert on error.
bool stl_validate ( const stl_file * stl , const indexed_triangle_set & its )
{
assert ( ! stl -> facet_start . empty ());
assert ( stl -> facet_start . size () == stl -> stats . number_of_facets );
assert ( stl -> neighbors_start . size () == stl -> stats . number_of_facets );
assert ( stl -> facet_start . size () == stl -> neighbors_start . size ());
assert ( ! stl -> neighbors_start . empty ());
assert (( its . indices . empty ()) == ( its . vertices . empty ()));
assert ( stl -> stats . number_of_facets > 0 );
assert ( its . vertices . empty () || its . indices . size () == stl -> stats . number_of_facets );
#ifdef _DEBUG
// Verify validity of neighborship data.
for ( int facet_idx = 0 ; facet_idx < ( int ) stl -> stats . number_of_facets ; ++ facet_idx ) {
const stl_neighbors & nbr = stl -> neighbors_start [ facet_idx ];
const int * vertices = ( its . indices . empty ()) ? nullptr : its . indices [ facet_idx ]. vertex ;
for ( int nbr_idx = 0 ; nbr_idx < 3 ; ++ nbr_idx ) {
int nbr_face = stl -> neighbors_start [ facet_idx ]. neighbor [ nbr_idx ];
assert ( nbr_face < ( int ) stl -> stats . number_of_facets );
if ( nbr_face != - 1 ) {
int nbr_vnot = nbr . which_vertex_not [ nbr_idx ];
assert ( nbr_vnot >= 0 && nbr_vnot < 6 );
// Neighbor of the neighbor is the original face.
assert ( stl -> neighbors_start [ nbr_face ]. neighbor [( nbr_vnot + 1 ) % 3 ] == facet_idx );
int vnot_back = stl -> neighbors_start [ nbr_face ]. which_vertex_not [( nbr_vnot + 1 ) % 3 ];
assert ( vnot_back >= 0 && vnot_back < 6 );
assert (( nbr_vnot < 3 ) == ( vnot_back < 3 ));
assert ( vnot_back % 3 == ( nbr_idx + 2 ) % 3 );
if ( vertices != nullptr ) {
// Has shared vertices.
if ( nbr_vnot < 3 ) {
// Faces facet_idx and nbr_face share two vertices accross the common edge. Faces are correctly oriented.
assert (( its . indices [ nbr_face ]. vertex [( nbr_vnot + 1 ) % 3 ] == vertices [( nbr_idx + 1 ) % 3 ] && its . indices [ nbr_face ]. vertex [( nbr_vnot + 2 ) % 3 ] == vertices [ nbr_idx ]));
} else {
// Faces facet_idx and nbr_face share two vertices accross the common edge. Faces are incorrectly oriented, one of them is flipped.
assert (( its . indices [ nbr_face ]. vertex [( nbr_vnot + 2 ) % 3 ] == vertices [( nbr_idx + 1 ) % 3 ] && its . indices [ nbr_face ]. vertex [( nbr_vnot + 1 ) % 3 ] == vertices [ nbr_idx ]));
}
}
}
}
}
#endif /* _DEBUG */
return true ;
}
// Check validity of the mesh, assert on error.
bool stl_validate ( const stl_file * stl )
{
indexed_triangle_set its ;
return stl_validate ( stl , its );
}