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>
2019-06-10 19:45:38 +02:00
# include <boost/log/trivial.hpp>
2017-08-03 17:31:31 +02:00
# include <boost/nowide/cstdio.hpp>
2013-06-23 18:18:38 +02:00
# include "stl.h"
2021-05-24 12:20:55 +02:00
# include "libslic3r/LocalesUtils.hpp"
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 21:14:58 +02:00
its . indices . assign ( stl - > stats . number_of_facets , stl_triangle_vertex_indices ( - 1 , - 1 , - 1 ) ) ;
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 ) ;
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 ) ;
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 21:14:58 +02:00
if ( its . indices [ facet_idx ] [ 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 21:14:58 +02:00
its . indices [ facet_in_fan_idx ] [ 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 ;
// 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
{
2021-05-24 12:20:55 +02:00
Slic3r : : CNumericLocalesSetter locales_setter ;
2019-06-04 18:25:53 +02:00
/* Open the file */
FILE * fp = boost : : nowide : : fopen ( file , " w " ) ;
if ( fp = = nullptr ) {
2019-06-10 19:45:38 +02:00
BOOST_LOG_TRIVIAL ( error ) < < " stl_write_ascii: Couldn't open " < < file < < " for writing " ;
2019-06-10 17:17:36 +02:00
return false ;
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 )
2019-06-10 21:14:58 +02:00
fprintf ( fp , " \t 3 %d %d %d \n " , its . indices [ i ] [ 0 ] , its . indices [ i ] [ 1 ] , its . indices [ i ] [ 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
{
2021-05-24 12:20:55 +02:00
Slic3r : : CNumericLocalesSetter locales_setter ;
2019-06-04 18:25:53 +02:00
/* Open the file */
FILE * fp = boost : : nowide : : fopen ( file , " w " ) ;
if ( fp = = nullptr ) {
2019-06-10 19:45:38 +02:00
BOOST_LOG_TRIVIAL ( error ) < < " stl_write_vrml: Couldn't open " < < file < < " for writing " ;
2019-06-10 17:17:36 +02:00
return false ;
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 " ) ;
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 " ) ;
2019-06-10 18:30:54 +02:00
for ( size_t i = 0 ; i + 1 < its . indices . size ( ) ; + + i )
2019-06-10 21:14:58 +02:00
fprintf ( fp , " \t \t \t \t %d, %d, %d, -1, \n " , its . indices [ i ] [ 0 ] , its . indices [ i ] [ 1 ] , its . indices [ i ] [ 2 ] ) ;
fprintf ( fp , " \t \t \t \t %d, %d, %d, -1] \n " , its . indices [ i ] [ 0 ] , its . indices [ i ] [ 1 ] , its . indices [ i ] [ 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
{
2021-05-24 12:20:55 +02:00
Slic3r : : CNumericLocalesSetter locales_setter ;
2019-06-04 18:25:53 +02:00
FILE * fp = boost : : nowide : : fopen ( file , " w " ) ;
if ( fp = = nullptr ) {
2019-06-10 19:45:38 +02:00
BOOST_LOG_TRIVIAL ( error ) < < " stl_write_obj: Couldn't open " < < file < < " for writing " ;
2019-06-10 17:17:36 +02:00
return false ;
2019-06-04 18:25:53 +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 )
2019-06-10 21:14:58 +02:00
fprintf ( fp , " f %d %d %d \n " , its . indices [ i ] [ 0 ] + 1 , its . indices [ i ] [ 1 ] + 1 , its . indices [ i ] [ 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 ] ;
2019-06-10 22:43:42 +02:00
const int * vertices = its . indices . empty ( ) ? nullptr : its . indices [ facet_idx ] . data ( ) ;
2019-06-10 18:30:54 +02:00
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.
2019-06-10 21:14:58 +02:00
assert ( ( its . indices [ nbr_face ] [ ( nbr_vnot + 1 ) % 3 ] = = vertices [ ( nbr_idx + 1 ) % 3 ] & & its . indices [ nbr_face ] [ ( nbr_vnot + 2 ) % 3 ] = = vertices [ nbr_idx ] ) ) ;
2019-06-10 18:30:54 +02:00
} else {
// Faces facet_idx and nbr_face share two vertices accross the common edge. Faces are incorrectly oriented, one of them is flipped.
2019-06-10 21:14:58 +02:00
assert ( ( its . indices [ nbr_face ] [ ( nbr_vnot + 2 ) % 3 ] = = vertices [ ( nbr_idx + 1 ) % 3 ] & & its . indices [ nbr_face ] [ ( nbr_vnot + 1 ) % 3 ] = = vertices [ nbr_idx ] ) ) ;
2019-06-10 18:30:54 +02:00
}
}
}
}
}
# 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 ) ;
}