MOAB: Mesh Oriented datABase
(version 5.4.1)
|
ASCII and Binary Stereo Lithography File readers. More...
#include <ReadSTL.hpp>
Classes | |
struct | Point |
struct | Triangle |
Public Types | |
enum | ByteOrder { STL_BIG_ENDIAN, STL_LITTLE_ENDIAN, STL_UNKNOWN_BYTE_ORDER } |
Public Member Functions | |
ErrorCode | load_file (const char *file_name, const EntityHandle *file_set, const FileOptions &opts, const SubsetList *subset_list=0, const Tag *file_id_tag=0) |
ErrorCode | read_tag_values (const char *file_name, const char *tag_name, const FileOptions &opts, std::vector< int > &tag_values_out, const SubsetList *subset_list=0) |
Read tag values from a file. | |
ReadSTL (Interface *impl=NULL) | |
Constructor. | |
virtual | ~ReadSTL () |
Destructor. | |
Static Public Member Functions | |
static ReaderIface * | factory (Interface *) |
factory method for STL reader | |
Protected Member Functions | |
ErrorCode | ascii_read_triangles (const char *file_name, std::vector< Triangle > &tris_out) |
ErrorCode | binary_read_triangles (const char *file_name, ByteOrder byte_order, std::vector< Triangle > &tris_out) |
Protected Attributes | |
ReadUtilIface * | readMeshIface |
Interface * | mdbImpl |
interface instance |
ASCII and Binary Stereo Lithography File readers.
STL files contain no connectivity infomration. Each triangle is specified as by three sets of single-precision coordinate triples. This reader does not use ANY tolerance when comparing vertex locations to recover connectivity. The points must be EXACTLY equal (including the sign on zero values.) If the file was written by an application which represented connectivity explicitly, there is no reason for the vertex coordinates to be anything other than exactly equal.
For binary STL files, the defacto standard is that they be written with a little-endian byte order. The reader will attempt to determine the byte order automatically, and if it is ambiguous, will default to little-endian. The byte ordering may be forced by by creating an integer tag named "__STL_BYTE_ORDER" and setting a global/mesh value for the tag as 1 for big-endian or 0 for little-endian.
For binary files, this reader relies on the file size to determine the validity of the file and may use it in guessing the byte order. This should not be an issue, as the file size can be determined exactly from the number of triangles for a valid file. However, if for some reason the file is readable even though it is invalid (e.g. it is some hybrid file with STL data in the beginning and some app- specific data appended to the end of the file) the check on the file size can be disabled by giving the reader a something other than a regular file to read from. For example, on Unix-like systems, have the reader read from a FIFO instead of a file: mkfifo /tmp/fifo.stlb cat my_binary_file.stlb > /tmp/fifo.stlb and instruct the MOAB-based application to read from /tmp/fifo.stlb
Definition at line 63 of file ReadSTL.hpp.
Definition at line 103 of file ReadSTL.hpp.
ReadSTL::ReadSTL | ( | Interface * | impl = NULL | ) |
Constructor.
Definition at line 40 of file ReadSTL.cpp.
References mdbImpl, moab::Interface::query_interface(), and readMeshIface.
Referenced by factory().
: mdbImpl( impl ) { mdbImpl->query_interface( readMeshIface ); }
ReadSTL::~ReadSTL | ( | ) | [virtual] |
Destructor.
Definition at line 45 of file ReadSTL.cpp.
References mdbImpl, readMeshIface, and moab::Interface::release_interface().
{ if( readMeshIface ) { mdbImpl->release_interface( readMeshIface ); readMeshIface = NULL; } }
ErrorCode ReadSTL::ascii_read_triangles | ( | const char * | file_name, |
std::vector< Triangle > & | tris_out | ||
) | [protected] |
Definition at line 184 of file ReadSTL.cpp.
References moab::ReadSTL::Point::coords, moab::FileTokenizer::get_floats(), moab::FileTokenizer::match_token(), MB_FILE_DOES_NOT_EXIST, MB_FILE_WRITE_ERROR, MB_SUCCESS, moab::ReadSTL::Triangle::points, and readMeshIface.
Referenced by load_file().
{ FILE* file = fopen( name, "r" ); if( !file ) { return MB_FILE_DOES_NOT_EXIST; } char header[81]; if( !fgets( header, sizeof( header ), file ) || // Read header line strlen( header ) < 6 || // Must be at least 6 chars header[strlen( header ) - 1] != '\n' || // Cannot exceed 80 chars memcmp( header, "solid", 5 ) || // Must begin with "solid" !isspace( header[5] ) ) { // Followed by a whitespace char fclose( file ); return MB_FILE_WRITE_ERROR; } // Use tokenizer for remainder of parsing FileTokenizer tokens( file, readMeshIface ); Triangle tri; float norm[3]; // Read until end of file. If we reach "endsolid", read // was successful. If EOF before "endsolid", return error. for( ;; ) { // Check for either another facet or the end of the list. const char* const expected[] = { "facet", "endsolid", 0 }; switch( tokens.match_token( expected ) ) { case 1: break; // Found another facet case 2: return MB_SUCCESS; // Found "endsolid" -- done default: return MB_FILE_WRITE_ERROR; // Found something else, or EOF } if( !tokens.match_token( "normal" ) || // Expect "normal" keyword !tokens.get_floats( 3, norm ) || // Followed by normal vector !tokens.match_token( "outer" ) || // Followed by "outer loop" !tokens.match_token( "loop" ) ) return MB_FILE_WRITE_ERROR; // For each of three triangle vertices for( int i = 0; i < 3; i++ ) { if( !tokens.match_token( "vertex" ) || !tokens.get_floats( 3, tri.points[i].coords ) ) return MB_FILE_WRITE_ERROR; } if( !tokens.match_token( "endloop" ) || // Facet ends with "endloop" !tokens.match_token( "endfacet" ) ) // and then "endfacet" return MB_FILE_WRITE_ERROR; tris.push_back( tri ); } fclose( file ); return MB_SUCCESS; }
ErrorCode ReadSTL::binary_read_triangles | ( | const char * | file_name, |
ByteOrder | byte_order, | ||
std::vector< Triangle > & | tris_out | ||
) | [protected] |
Definition at line 265 of file ReadSTL.cpp.
References moab::SysUtil::byteswap(), moab::BinaryTri::coords, moab::BinaryHeader::count, moab::SysUtil::filesize(), moab::SysUtil::little_endian(), MB_FILE_DOES_NOT_EXIST, MB_FILE_WRITE_ERROR, MB_SUCCESS, STL_BIG_ENDIAN, STL_UNKNOWN_BYTE_ORDER, and moab::SysUtil::swap_bytes().
Referenced by load_file().
{ FILE* file = fopen( name, "rb" ); if( !file ) { return MB_FILE_DOES_NOT_EXIST; } // Read header block BinaryHeader header; if( fread( &header, 84, 1, file ) != 1 ) { fclose( file ); return MB_FILE_WRITE_ERROR; } // Allow user setting for byte order, default to little endian const bool want_big_endian = ( byte_order == STL_BIG_ENDIAN ); const bool am_big_endian = !SysUtil::little_endian(); bool swap_bytes = ( want_big_endian == am_big_endian ); // Compare the number of triangles to the length of the file. // The file must contain an 80-byte description, a 4-byte // triangle count and 50 bytes per triangle. // // The triangle count *may* allow us to determine the byte order // of the file, if it is not an endian-symmetric value. // // We need to compare the expected size calculated from the triangle // count with the file size anyway, as an invalid file or a byte- // swapping issue could result in a very large (incorrect) value for // num_tri, resulting in a SEGFAULT. // Get expected number of triangles if( swap_bytes ) SysUtil::byteswap( &header.count, 1 ); unsigned long num_tri = header.count; // Get the file length long filesize = SysUtil::filesize( file ); if( filesize >= 0 ) { // -1 indicates could not determine file size (e.g. reading from FIFO) // Check file size, but be careful of numeric overflow if( ULONG_MAX / 50 - 84 < num_tri || // Next calc would have overflow 84 + 50 * num_tri != (unsigned long)filesize ) { // Unless the byte order was specified explicitly in the // tag, try the opposite byte order. uint32_t num_tri_tmp = header.count; SysUtil::byteswap( &num_tri_tmp, 1 ); unsigned long num_tri_swap = num_tri_tmp; if( byte_order != STL_UNKNOWN_BYTE_ORDER || // If byte order was specified, fail now ULONG_MAX / 50 - 84 < num_tri_swap || // Watch for overflow in next line 84 + 50 * num_tri_swap != (unsigned long)filesize ) { fclose( file ); return MB_FILE_WRITE_ERROR; } swap_bytes = !swap_bytes; num_tri = num_tri_swap; } } // Allocate storage for triangles tris.resize( num_tri ); // Read each triangle BinaryTri tri; // Binary block read from file for( std::vector< Triangle >::iterator i = tris.begin(); i != tris.end(); ++i ) { if( fread( &tri, 50, 1, file ) != 1 ) { fclose( file ); return MB_FILE_WRITE_ERROR; } if( swap_bytes ) SysUtil::byteswap( tri.coords, 9 ); for( unsigned j = 0; j < 9; ++j ) i->points[j / 3].coords[j % 3] = tri.coords[j]; } fclose( file ); return MB_SUCCESS; }
ReaderIface * ReadSTL::factory | ( | Interface * | iface | ) | [static] |
factory method for STL reader
Definition at line 352 of file ReadSTL.cpp.
References ReadSTL().
Referenced by moab::ReaderWriterSet::ReaderWriterSet().
ErrorCode ReadSTL::load_file | ( | const char * | file_name, |
const EntityHandle * | file_set, | ||
const FileOptions & | opts, | ||
const SubsetList * | subset_list = 0 , |
||
const Tag * | file_id_tag = 0 |
||
) | [virtual] |
Generic file loading code for both binary and ASCII readers. Calls reader-specific read_triangles function to do actual I/O.
Implements moab::ReaderIface.
Definition at line 72 of file ReadSTL.cpp.
References ascii_read_triangles(), moab::ReadUtilIface::assign_ids(), moab::SysUtil::big_endian(), binary_read_triangles(), ErrorCode, moab::ReadUtilIface::get_element_connect(), moab::ReadUtilIface::get_node_coords(), moab::FileOptions::get_null_option(), moab::SysUtil::little_endian(), MB_SET_ERR, MB_START_ID, MB_SUCCESS, MB_UNSUPPORTED_OPERATION, MBTRI, readMeshIface, STL_BIG_ENDIAN, STL_LITTLE_ENDIAN, STL_UNKNOWN_BYTE_ORDER, and moab::ReadUtilIface::update_adjacencies().
{ if( subset_list ) { MB_SET_ERR( MB_UNSUPPORTED_OPERATION, "Reading subset of files not supported for STL" ); } ErrorCode result; std::vector< ReadSTL::Triangle > triangles; bool is_ascii = false, is_binary = false; if( MB_SUCCESS == opts.get_null_option( "ASCII" ) ) is_ascii = true; if( MB_SUCCESS == opts.get_null_option( "BINARY" ) ) is_binary = true; if( is_ascii && is_binary ) { MB_SET_ERR( MB_FAILURE, "Conflicting options: BINARY ASCII" ); } bool big_endian = false, little_endian = false; if( MB_SUCCESS == opts.get_null_option( "BIG_ENDIAN" ) ) big_endian = true; if( MB_SUCCESS == opts.get_null_option( "LITTLE_ENDIAN" ) ) little_endian = true; if( big_endian && little_endian ) { MB_SET_ERR( MB_FAILURE, "Conflicting options: BIG_ENDIAN LITTLE_ENDIAN" ); } ByteOrder byte_order = big_endian ? STL_BIG_ENDIAN : little_endian ? STL_LITTLE_ENDIAN : STL_UNKNOWN_BYTE_ORDER; if( is_ascii ) result = ascii_read_triangles( filename, triangles ); else if( is_binary ) result = binary_read_triangles( filename, byte_order, triangles ); else { // Try ASCII first result = ascii_read_triangles( filename, triangles ); if( MB_SUCCESS != result ) // ASCII failed, try binary result = binary_read_triangles( filename, byte_order, triangles ); } if( MB_SUCCESS != result ) return result; // Create a std::map from position->handle, and such // that all positions are specified, and handles are zero. std::map< Point, EntityHandle > vertex_map; for( std::vector< Triangle >::iterator i = triangles.begin(); i != triangles.end(); ++i ) { vertex_map[i->points[0]] = 0; vertex_map[i->points[1]] = 0; vertex_map[i->points[2]] = 0; } // Create vertices std::vector< double* > coord_arrays; EntityHandle vtx_handle = 0; result = readMeshIface->get_node_coords( 3, vertex_map.size(), MB_START_ID, vtx_handle, coord_arrays ); if( MB_SUCCESS != result ) return result; // Copy vertex coordinates into entity sequence coordinate arrays // and copy handle into vertex_map. double *x = coord_arrays[0], *y = coord_arrays[1], *z = coord_arrays[2]; for( std::map< Point, EntityHandle >::iterator i = vertex_map.begin(); i != vertex_map.end(); ++i ) { i->second = vtx_handle; ++vtx_handle; *x = i->first.coords[0]; ++x; *y = i->first.coords[1]; ++y; *z = i->first.coords[2]; ++z; } // Allocate triangles EntityHandle elm_handle = 0; EntityHandle* connectivity; result = readMeshIface->get_element_connect( triangles.size(), 3, MBTRI, MB_START_ID, elm_handle, connectivity ); if( MB_SUCCESS != result ) return result; // Use vertex_map to recover triangle connectivity from // vertex coordinates. EntityHandle* conn_sav = connectivity; for( std::vector< Triangle >::iterator i = triangles.begin(); i != triangles.end(); ++i ) { *connectivity = vertex_map[i->points[0]]; ++connectivity; *connectivity = vertex_map[i->points[1]]; ++connectivity; *connectivity = vertex_map[i->points[2]]; ++connectivity; } // Notify MOAB of the new elements result = readMeshIface->update_adjacencies( elm_handle, triangles.size(), 3, conn_sav ); if( MB_SUCCESS != result ) return result; if( file_id_tag ) { Range vertices( vtx_handle, vtx_handle + vertex_map.size() - 1 ); Range elements( elm_handle, elm_handle + triangles.size() - 1 ); readMeshIface->assign_ids( *file_id_tag, vertices ); readMeshIface->assign_ids( *file_id_tag, elements ); } return MB_SUCCESS; }
ErrorCode ReadSTL::read_tag_values | ( | const char * | file_name, |
const char * | tag_name, | ||
const FileOptions & | opts, | ||
std::vector< int > & | tag_values_out, | ||
const SubsetList * | subset_list = 0 |
||
) | [virtual] |
Read tag values from a file.
Read the list if all integer tag values from the file for a tag that is a single integer value per entity.
file_name | The file to read. |
tag_name | The tag for which to read values |
tag_values_out | Output: The list of tag values. |
subset_list | An array of tag name and value sets specifying the subset of the file to read. If multiple tags are specified, the sets that match all tags (intersection) should be read. |
subset_list_length | The length of the 'subset_list' array. |
Implements moab::ReaderIface.
Definition at line 60 of file ReadSTL.cpp.
References MB_NOT_IMPLEMENTED.
{ return MB_NOT_IMPLEMENTED; }
Interface* moab::ReadSTL::mdbImpl [protected] |
interface instance
Definition at line 120 of file ReadSTL.hpp.
Referenced by ReadSTL(), and ~ReadSTL().
ReadUtilIface* moab::ReadSTL::readMeshIface [protected] |
Definition at line 117 of file ReadSTL.hpp.
Referenced by ascii_read_triangles(), load_file(), ReadSTL(), and ~ReadSTL().