MOAB: Mesh Oriented datABase  (version 5.3.0)
ReadSTL.cpp
Go to the documentation of this file.
00001 /**
00002  * MOAB, a Mesh-Oriented datABase, is a software component for creating,
00003  * storing and accessing finite element mesh data.
00004  *
00005  * Copyright 2004 Sandia Corporation.  Under the terms of Contract
00006  * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government
00007  * retains certain rights in this software.
00008  *
00009  * This library is free software; you can redistribute it and/or
00010  * modify it under the terms of the GNU Lesser General Public
00011  * License as published by the Free Software Foundation; either
00012  * version 2.1 of the License, or (at your option) any later version.
00013  *
00014  */
00015 
00016 /**
00017  * \class ReadSTL
00018  * \brief ASCII and Binary Stereo Lithography File readers.
00019  * \author Jason Kraftcheck
00020  */
00021 
00022 #include "ReadSTL.hpp"
00023 #include "FileTokenizer.hpp"  // For FileTokenizer
00024 #include "Internals.hpp"
00025 #include "moab/Interface.hpp"
00026 #include "moab/ReadUtilIface.hpp"
00027 #include "moab/Range.hpp"
00028 #include "moab/FileOptions.hpp"
00029 #include "SysUtil.hpp"
00030 
00031 #include <cerrno>
00032 #include <cstring>
00033 #include <climits>
00034 #include <cassert>
00035 #include <map>
00036 
00037 namespace moab
00038 {
00039 
00040 ReadSTL::ReadSTL( Interface* impl ) : mdbImpl( impl )
00041 {
00042     mdbImpl->query_interface( readMeshIface );
00043 }
00044 
00045 ReadSTL::~ReadSTL()
00046 {
00047     if( readMeshIface )
00048     {
00049         mdbImpl->release_interface( readMeshIface );
00050         readMeshIface = NULL;
00051     }
00052 }
00053 
00054 // Used to put points in an STL tree-based container
00055 bool ReadSTL::Point::operator<( const ReadSTL::Point& other ) const
00056 {
00057     return 0 > memcmp( this, &other, sizeof( ReadSTL::Point ) );
00058 }
00059 
00060 ErrorCode ReadSTL::read_tag_values( const char* /* file_name */, const char* /* tag_name */,
00061                                     const FileOptions& /* opts */, std::vector< int >& /* tag_values_out */,
00062                                     const SubsetList* /* subset_list */ )
00063 {
00064     return MB_NOT_IMPLEMENTED;
00065 }
00066 
00067 // Generic load function for both ASCII and binary. Calls
00068 // pure-virtual function implemented in subclasses to read
00069 // the data from the file.
00070 ErrorCode ReadSTL::load_file( const char* filename, const EntityHandle* /* file_set */, const FileOptions& opts,
00071                               const ReaderIface::SubsetList* subset_list, const Tag* file_id_tag )
00072 {
00073     if( subset_list ) { MB_SET_ERR( MB_UNSUPPORTED_OPERATION, "Reading subset of files not supported for STL" ); }
00074 
00075     ErrorCode result;
00076 
00077     std::vector< ReadSTL::Triangle > triangles;
00078 
00079     bool is_ascii = false, is_binary = false;
00080     if( MB_SUCCESS == opts.get_null_option( "ASCII" ) ) is_ascii = true;
00081     if( MB_SUCCESS == opts.get_null_option( "BINARY" ) ) is_binary = true;
00082     if( is_ascii && is_binary ) { MB_SET_ERR( MB_FAILURE, "Conflicting options: BINARY ASCII" ); }
00083 
00084     bool big_endian = false, little_endian = false;
00085     if( MB_SUCCESS == opts.get_null_option( "BIG_ENDIAN" ) ) big_endian = true;
00086     if( MB_SUCCESS == opts.get_null_option( "LITTLE_ENDIAN" ) ) little_endian = true;
00087     if( big_endian && little_endian ) { MB_SET_ERR( MB_FAILURE, "Conflicting options: BIG_ENDIAN LITTLE_ENDIAN" ); }
00088     ByteOrder byte_order = big_endian ? STL_BIG_ENDIAN : little_endian ? STL_LITTLE_ENDIAN : STL_UNKNOWN_BYTE_ORDER;
00089 
00090     if( is_ascii )
00091         result = ascii_read_triangles( filename, triangles );
00092     else if( is_binary )
00093         result = binary_read_triangles( filename, byte_order, triangles );
00094     else
00095     {
00096         // Try ASCII first
00097         result = ascii_read_triangles( filename, triangles );
00098         if( MB_SUCCESS != result )
00099             // ASCII failed, try binary
00100             result = binary_read_triangles( filename, byte_order, triangles );
00101     }
00102     if( MB_SUCCESS != result ) return result;
00103 
00104     // Create a std::map from position->handle, and such
00105     // that all positions are specified, and handles are zero.
00106     std::map< Point, EntityHandle > vertex_map;
00107     for( std::vector< Triangle >::iterator i = triangles.begin(); i != triangles.end(); ++i )
00108     {
00109         vertex_map[i->points[0]] = 0;
00110         vertex_map[i->points[1]] = 0;
00111         vertex_map[i->points[2]] = 0;
00112     }
00113 
00114     // Create vertices
00115     std::vector< double* > coord_arrays;
00116     EntityHandle vtx_handle = 0;
00117     result = readMeshIface->get_node_coords( 3, vertex_map.size(), MB_START_ID, vtx_handle, coord_arrays );
00118     if( MB_SUCCESS != result ) return result;
00119 
00120     // Copy vertex coordinates into entity sequence coordinate arrays
00121     // and copy handle into vertex_map.
00122     double *x = coord_arrays[0], *y = coord_arrays[1], *z = coord_arrays[2];
00123     for( std::map< Point, EntityHandle >::iterator i = vertex_map.begin(); i != vertex_map.end(); ++i )
00124     {
00125         i->second = vtx_handle;
00126         ++vtx_handle;
00127         *x = i->first.coords[0];
00128         ++x;
00129         *y = i->first.coords[1];
00130         ++y;
00131         *z = i->first.coords[2];
00132         ++z;
00133     }
00134 
00135     // Allocate triangles
00136     EntityHandle elm_handle = 0;
00137     EntityHandle* connectivity;
00138     result = readMeshIface->get_element_connect( triangles.size(), 3, MBTRI, MB_START_ID, elm_handle, connectivity );
00139     if( MB_SUCCESS != result ) return result;
00140 
00141     // Use vertex_map to recover triangle connectivity from
00142     // vertex coordinates.
00143     EntityHandle* conn_sav = connectivity;
00144     for( std::vector< Triangle >::iterator i = triangles.begin(); i != triangles.end(); ++i )
00145     {
00146         *connectivity = vertex_map[i->points[0]];
00147         ++connectivity;
00148         *connectivity = vertex_map[i->points[1]];
00149         ++connectivity;
00150         *connectivity = vertex_map[i->points[2]];
00151         ++connectivity;
00152     }
00153 
00154     // Notify MOAB of the new elements
00155     result = readMeshIface->update_adjacencies( elm_handle, triangles.size(), 3, conn_sav );
00156     if( MB_SUCCESS != result ) return result;
00157 
00158     if( file_id_tag )
00159     {
00160         Range vertices( vtx_handle, vtx_handle + vertex_map.size() - 1 );
00161         Range elements( elm_handle, elm_handle + triangles.size() - 1 );
00162         readMeshIface->assign_ids( *file_id_tag, vertices );
00163         readMeshIface->assign_ids( *file_id_tag, elements );
00164     }
00165 
00166     return MB_SUCCESS;
00167 }
00168 
00169 // Read ASCII file
00170 ErrorCode ReadSTL::ascii_read_triangles( const char* name, std::vector< ReadSTL::Triangle >& tris )
00171 {
00172     FILE* file = fopen( name, "r" );
00173     if( !file ) { return MB_FILE_DOES_NOT_EXIST; }
00174 
00175     char header[81];
00176     if( !fgets( header, sizeof( header ), file ) ||  // Read header line
00177         strlen( header ) < 6 ||                      // Must be at least 6 chars
00178         header[strlen( header ) - 1] != '\n' ||      // Cannot exceed 80 chars
00179         memcmp( header, "solid", 5 ) ||              // Must begin with "solid"
00180         !isspace( header[5] ) )
00181     {  // Followed by a whitespace char
00182         fclose( file );
00183         return MB_FILE_WRITE_ERROR;
00184     }
00185 
00186     // Use tokenizer for remainder of parsing
00187     FileTokenizer tokens( file, readMeshIface );
00188 
00189     Triangle tri;
00190     float norm[3];
00191 
00192     // Read until end of file. If we reach "endsolid", read
00193     // was successful. If EOF before "endsolid", return error.
00194     for( ;; )
00195     {
00196         // Check for either another facet or the end of the list.
00197         const char* const expected[] = { "facet", "endsolid", 0 };
00198         switch( tokens.match_token( expected ) )
00199         {
00200             case 1:
00201                 break;  // Found another facet
00202             case 2:
00203                 return MB_SUCCESS;  // Found "endsolid" -- done
00204             default:
00205                 return MB_FILE_WRITE_ERROR;  // Found something else, or EOF
00206         }
00207 
00208         if( !tokens.match_token( "normal" ) ||  // Expect "normal" keyword
00209             !tokens.get_floats( 3, norm ) ||    // Followed by normal vector
00210             !tokens.match_token( "outer" ) ||   // Followed by "outer loop"
00211             !tokens.match_token( "loop" ) )
00212             return MB_FILE_WRITE_ERROR;
00213 
00214         // For each of three triangle vertices
00215         for( int i = 0; i < 3; i++ )
00216         {
00217             if( !tokens.match_token( "vertex" ) || !tokens.get_floats( 3, tri.points[i].coords ) )
00218                 return MB_FILE_WRITE_ERROR;
00219         }
00220 
00221         if( !tokens.match_token( "endloop" ) ||  // Facet ends with "endloop"
00222             !tokens.match_token( "endfacet" ) )  // and then "endfacet"
00223             return MB_FILE_WRITE_ERROR;
00224 
00225         tris.push_back( tri );
00226     }
00227 
00228     fclose( file );
00229     return MB_SUCCESS;
00230 }
00231 
00232 // Header block from binary STL file (84 bytes long)
00233 struct BinaryHeader
00234 {
00235     char comment[80];  // 80 byte comment string (null terminated?)
00236     uint32_t count;    // Number of triangles - 4 byte integer
00237 };
00238 
00239 // Triangle spec from file (50 bytes)
00240 struct BinaryTri
00241 {
00242     float normal[3];  // Normal as 3 4-byte little-endian IEEE floats
00243     float coords[9];  // Vertex coords as 9 4-byte little-endian IEEE floats
00244     char pad[2];
00245 };
00246 
00247 // Read a binary STL file
00248 ErrorCode ReadSTL::binary_read_triangles( const char* name, ReadSTL::ByteOrder byte_order,
00249                                           std::vector< ReadSTL::Triangle >& tris )
00250 {
00251     FILE* file = fopen( name, "rb" );
00252     if( !file ) { return MB_FILE_DOES_NOT_EXIST; }
00253 
00254     // Read header block
00255     BinaryHeader header;
00256     if( fread( &header, 84, 1, file ) != 1 )
00257     {
00258         fclose( file );
00259         return MB_FILE_WRITE_ERROR;
00260     }
00261 
00262     // Allow user setting for byte order, default to little endian
00263     const bool want_big_endian = ( byte_order == STL_BIG_ENDIAN );
00264     const bool am_big_endian   = !SysUtil::little_endian();
00265     bool swap_bytes            = ( want_big_endian == am_big_endian );
00266 
00267     // Compare the number of triangles to the length of the file.
00268     // The file must contain an 80-byte description, a 4-byte
00269     // triangle count and 50 bytes per triangle.
00270     //
00271     // The triangle count *may* allow us to determine the byte order
00272     // of the file, if it is not an endian-symmetric value.
00273     //
00274     // We need to compare the expected size calculated from the triangle
00275     // count with the file size anyway, as an invalid file or a byte-
00276     // swapping issue could result in a very large (incorrect) value for
00277     // num_tri, resulting in a SEGFAULT.
00278 
00279     // Get expected number of triangles
00280     if( swap_bytes ) SysUtil::byteswap( &header.count, 1 );
00281     unsigned long num_tri = header.count;
00282 
00283     // Get the file length
00284     long filesize = SysUtil::filesize( file );
00285     if( filesize >= 0 )
00286     {  // -1 indicates could not determine file size (e.g. reading from FIFO)
00287         // Check file size, but be careful of numeric overflow
00288         if( ULONG_MAX / 50 - 84 < num_tri ||  // Next calc would have overflow
00289             84 + 50 * num_tri != (unsigned long)filesize )
00290         {
00291             // Unless the byte order was specified explicitly in the
00292             // tag, try the opposite byte order.
00293             uint32_t num_tri_tmp = header.count;
00294             SysUtil::byteswap( &num_tri_tmp, 1 );
00295             unsigned long num_tri_swap = num_tri_tmp;
00296             if( byte_order != STL_UNKNOWN_BYTE_ORDER ||  // If byte order was specified, fail now
00297                 ULONG_MAX / 50 - 84 < num_tri_swap ||    // Watch for overflow in next line
00298                 84 + 50 * num_tri_swap != (unsigned long)filesize )
00299             {
00300                 fclose( file );
00301                 return MB_FILE_WRITE_ERROR;
00302             }
00303             swap_bytes = !swap_bytes;
00304             num_tri    = num_tri_swap;
00305         }
00306     }
00307 
00308     // Allocate storage for triangles
00309     tris.resize( num_tri );
00310 
00311     // Read each triangle
00312     BinaryTri tri;  // Binary block read from file
00313     for( std::vector< Triangle >::iterator i = tris.begin(); i != tris.end(); ++i )
00314     {
00315         if( fread( &tri, 50, 1, file ) != 1 )
00316         {
00317             fclose( file );
00318             return MB_FILE_WRITE_ERROR;
00319         }
00320 
00321         if( swap_bytes ) SysUtil::byteswap( tri.coords, 9 );
00322 
00323         for( unsigned j = 0; j < 9; ++j )
00324             i->points[j / 3].coords[j % 3] = tri.coords[j];
00325     }
00326 
00327     fclose( file );
00328     return MB_SUCCESS;
00329 }
00330 
00331 ReaderIface* ReadSTL::factory( Interface* iface )
00332 {
00333     return new ReadSTL( iface );
00334 }
00335 
00336 }  // namespace moab
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines