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-04 18:25:53 +02:00
void stl_invalidate_shared_vertices ( stl_file * stl )
{
2019-06-04 22:06:42 +02:00
stl -> v_indices . clear ();
stl -> v_shared . clear ();
2013-08-05 10:48:38 +02:00
}
2013-06-23 18:18:38 +02:00
2019-06-04 18:25:53 +02:00
void stl_generate_shared_vertices ( stl_file * stl )
{
// 3 indices to vertex per face
2019-06-04 22:06:42 +02:00
stl -> v_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 17:17:36 +02:00
stl -> v_shared . clear ();
2019-06-10 11:04:09 +02:00
stl -> v_shared . 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 ) {
if ( stl -> v_indices [ facet_idx ]. vertex [ j ] != - 1 )
// Shared vertex was already assigned.
continue ;
// Create a new shared vertex.
2019-06-04 22:06:42 +02:00
stl -> v_shared . 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 17:36:15 +02:00
stl -> v_indices [ facet_in_fan_idx ]. vertex [ pivot_vertex ] = stl -> v_shared . 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 17:17:36 +02:00
bool stl_write_off ( stl_file * stl , 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 17:36:15 +02:00
fprintf ( fp , "%d %d 0 \n " , stl -> v_shared . size (), stl -> stats . number_of_facets );
for ( int i = 0 ; i < stl -> v_shared . size (); ++ i )
2019-06-04 18:25:53 +02:00
fprintf ( fp , " \t %f %f %f \n " , stl -> v_shared [ i ]( 0 ), stl -> v_shared [ i ]( 1 ), stl -> v_shared [ i ]( 2 ));
for ( uint32_t i = 0 ; i < stl -> stats . number_of_facets ; ++ i )
fprintf ( fp , " \t 3 %d %d %d \n " , stl -> v_indices [ i ]. vertex [ 0 ], stl -> v_indices [ i ]. vertex [ 1 ], stl -> v_indices [ i ]. vertex [ 2 ]);
fclose ( fp );
2019-06-10 17:17:36 +02:00
return true ;
2013-06-23 18:18:38 +02:00
}
2019-06-10 17:17:36 +02:00
bool stl_write_vrml ( stl_file * stl , 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 17:36:15 +02:00
for (; i + 1 < stl -> v_shared . size (); ++ i )
2019-06-04 18:25:53 +02:00
fprintf ( fp , " \t\t\t\t %f %f %f, \n " , stl -> v_shared [ i ]( 0 ), stl -> v_shared [ i ]( 1 ), stl -> v_shared [ i ]( 2 ));
fprintf ( fp , " \t\t\t\t %f %f %f] \n " , stl -> v_shared [ i ]( 0 ), stl -> v_shared [ i ]( 1 ), stl -> v_shared [ i ]( 2 ));
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-04 18:25:53 +02:00
for ( int i = 0 ; i + 1 < ( int ) stl -> stats . number_of_facets ; ++ 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 );
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 17:17:36 +02:00
bool stl_write_obj ( stl_file * stl , 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 17:36:15 +02:00
for ( size_t i = 0 ; i < stl -> v_shared . size (); ++ i )
2019-06-04 18:25:53 +02:00
fprintf ( fp , "v %f %f %f \n " , stl -> v_shared [ i ]( 0 ), stl -> v_shared [ i ]( 1 ), stl -> v_shared [ i ]( 2 ));
for ( uint32_t i = 0 ; i < stl -> stats . number_of_facets ; ++ i )
fprintf ( fp , "f %d %d %d \n " , stl -> v_indices [ i ]. vertex [ 0 ] + 1 , stl -> v_indices [ i ]. vertex [ 1 ] + 1 , stl -> v_indices [ i ]. vertex [ 2 ] + 1 );
fclose ( fp );
2019-06-10 17:17:36 +02:00
return true ;
2013-06-23 21:11:46 +02:00
}