MOAB: Mesh Oriented datABase  (version 5.4.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 */,
00061                                     const char* /* tag_name */,
00062                                     const FileOptions& /* opts */,
00063                                     std::vector< int >& /* tag_values_out */,
00064                                     const SubsetList* /* subset_list */ )
00065 {
00066     return MB_NOT_IMPLEMENTED;
00067 }
00068 
00069 // Generic load function for both ASCII and binary. Calls
00070 // pure-virtual function implemented in subclasses to read
00071 // the data from the file.
00072 ErrorCode ReadSTL::load_file( const char* filename,
00073                               const EntityHandle* /* file_set */,
00074                               const FileOptions& opts,
00075                               const ReaderIface::SubsetList* subset_list,
00076                               const Tag* file_id_tag )
00077 {
00078     if( subset_list )
00079     {
00080         MB_SET_ERR( MB_UNSUPPORTED_OPERATION, "Reading subset of files not supported for STL" );
00081     }
00082 
00083     ErrorCode result;
00084 
00085     std::vector< ReadSTL::Triangle > triangles;
00086 
00087     bool is_ascii = false, is_binary = false;
00088     if( MB_SUCCESS == opts.get_null_option( "ASCII" ) ) is_ascii = true;
00089     if( MB_SUCCESS == opts.get_null_option( "BINARY" ) ) is_binary = true;
00090     if( is_ascii && is_binary )
00091     {
00092         MB_SET_ERR( MB_FAILURE, "Conflicting options: BINARY ASCII" );
00093     }
00094 
00095     bool big_endian = false, little_endian = false;
00096     if( MB_SUCCESS == opts.get_null_option( "BIG_ENDIAN" ) ) big_endian = true;
00097     if( MB_SUCCESS == opts.get_null_option( "LITTLE_ENDIAN" ) ) little_endian = true;
00098     if( big_endian && little_endian )
00099     {
00100         MB_SET_ERR( MB_FAILURE, "Conflicting options: BIG_ENDIAN LITTLE_ENDIAN" );
00101     }
00102     ByteOrder byte_order = big_endian ? STL_BIG_ENDIAN : little_endian ? STL_LITTLE_ENDIAN : STL_UNKNOWN_BYTE_ORDER;
00103 
00104     if( is_ascii )
00105         result = ascii_read_triangles( filename, triangles );
00106     else if( is_binary )
00107         result = binary_read_triangles( filename, byte_order, triangles );
00108     else
00109     {
00110         // Try ASCII first
00111         result = ascii_read_triangles( filename, triangles );
00112         if( MB_SUCCESS != result )
00113             // ASCII failed, try binary
00114             result = binary_read_triangles( filename, byte_order, triangles );
00115     }
00116     if( MB_SUCCESS != result ) return result;
00117 
00118     // Create a std::map from position->handle, and such
00119     // that all positions are specified, and handles are zero.
00120     std::map< Point, EntityHandle > vertex_map;
00121     for( std::vector< Triangle >::iterator i = triangles.begin(); i != triangles.end(); ++i )
00122     {
00123         vertex_map[i->points[0]] = 0;
00124         vertex_map[i->points[1]] = 0;
00125         vertex_map[i->points[2]] = 0;
00126     }
00127 
00128     // Create vertices
00129     std::vector< double* > coord_arrays;
00130     EntityHandle vtx_handle = 0;
00131     result = readMeshIface->get_node_coords( 3, vertex_map.size(), MB_START_ID, vtx_handle, coord_arrays );
00132     if( MB_SUCCESS != result ) return result;
00133 
00134     // Copy vertex coordinates into entity sequence coordinate arrays
00135     // and copy handle into vertex_map.
00136     double *x = coord_arrays[0], *y = coord_arrays[1], *z = coord_arrays[2];
00137     for( std::map< Point, EntityHandle >::iterator i = vertex_map.begin(); i != vertex_map.end(); ++i )
00138     {
00139         i->second = vtx_handle;
00140         ++vtx_handle;
00141         *x = i->first.coords[0];
00142         ++x;
00143         *y = i->first.coords[1];
00144         ++y;
00145         *z = i->first.coords[2];
00146         ++z;
00147     }
00148 
00149     // Allocate triangles
00150     EntityHandle elm_handle = 0;
00151     EntityHandle* connectivity;
00152     result = readMeshIface->get_element_connect( triangles.size(), 3, MBTRI, MB_START_ID, elm_handle, connectivity );
00153     if( MB_SUCCESS != result ) return result;
00154 
00155     // Use vertex_map to recover triangle connectivity from
00156     // vertex coordinates.
00157     EntityHandle* conn_sav = connectivity;
00158     for( std::vector< Triangle >::iterator i = triangles.begin(); i != triangles.end(); ++i )
00159     {
00160         *connectivity = vertex_map[i->points[0]];
00161         ++connectivity;
00162         *connectivity = vertex_map[i->points[1]];
00163         ++connectivity;
00164         *connectivity = vertex_map[i->points[2]];
00165         ++connectivity;
00166     }
00167 
00168     // Notify MOAB of the new elements
00169     result = readMeshIface->update_adjacencies( elm_handle, triangles.size(), 3, conn_sav );
00170     if( MB_SUCCESS != result ) return result;
00171 
00172     if( file_id_tag )
00173     {
00174         Range vertices( vtx_handle, vtx_handle + vertex_map.size() - 1 );
00175         Range elements( elm_handle, elm_handle + triangles.size() - 1 );
00176         readMeshIface->assign_ids( *file_id_tag, vertices );
00177         readMeshIface->assign_ids( *file_id_tag, elements );
00178     }
00179 
00180     return MB_SUCCESS;
00181 }
00182 
00183 // Read ASCII file
00184 ErrorCode ReadSTL::ascii_read_triangles( const char* name, std::vector< ReadSTL::Triangle >& tris )
00185 {
00186     FILE* file = fopen( name, "r" );
00187     if( !file )
00188     {
00189         return MB_FILE_DOES_NOT_EXIST;
00190     }
00191 
00192     char header[81];
00193     if( !fgets( header, sizeof( header ), file ) ||  // Read header line
00194         strlen( header ) < 6 ||                      // Must be at least 6 chars
00195         header[strlen( header ) - 1] != '\n' ||      // Cannot exceed 80 chars
00196         memcmp( header, "solid", 5 ) ||              // Must begin with "solid"
00197         !isspace( header[5] ) )
00198     {  // Followed by a whitespace char
00199         fclose( file );
00200         return MB_FILE_WRITE_ERROR;
00201     }
00202 
00203     // Use tokenizer for remainder of parsing
00204     FileTokenizer tokens( file, readMeshIface );
00205 
00206     Triangle tri;
00207     float norm[3];
00208 
00209     // Read until end of file. If we reach "endsolid", read
00210     // was successful. If EOF before "endsolid", return error.
00211     for( ;; )
00212     {
00213         // Check for either another facet or the end of the list.
00214         const char* const expected[] = { "facet", "endsolid", 0 };
00215         switch( tokens.match_token( expected ) )
00216         {
00217             case 1:
00218                 break;  // Found another facet
00219             case 2:
00220                 return MB_SUCCESS;  // Found "endsolid" -- done
00221             default:
00222                 return MB_FILE_WRITE_ERROR;  // Found something else, or EOF
00223         }
00224 
00225         if( !tokens.match_token( "normal" ) ||  // Expect "normal" keyword
00226             !tokens.get_floats( 3, norm ) ||    // Followed by normal vector
00227             !tokens.match_token( "outer" ) ||   // Followed by "outer loop"
00228             !tokens.match_token( "loop" ) )
00229             return MB_FILE_WRITE_ERROR;
00230 
00231         // For each of three triangle vertices
00232         for( int i = 0; i < 3; i++ )
00233         {
00234             if( !tokens.match_token( "vertex" ) || !tokens.get_floats( 3, tri.points[i].coords ) )
00235                 return MB_FILE_WRITE_ERROR;
00236         }
00237 
00238         if( !tokens.match_token( "endloop" ) ||  // Facet ends with "endloop"
00239             !tokens.match_token( "endfacet" ) )  // and then "endfacet"
00240             return MB_FILE_WRITE_ERROR;
00241 
00242         tris.push_back( tri );
00243     }
00244 
00245     fclose( file );
00246     return MB_SUCCESS;
00247 }
00248 
00249 // Header block from binary STL file (84 bytes long)
00250 struct BinaryHeader
00251 {
00252     char comment[80];  // 80 byte comment string (null terminated?)
00253     uint32_t count;    // Number of triangles - 4 byte integer
00254 };
00255 
00256 // Triangle spec from file (50 bytes)
00257 struct BinaryTri
00258 {
00259     float normal[3];  // Normal as 3 4-byte little-endian IEEE floats
00260     float coords[9];  // Vertex coords as 9 4-byte little-endian IEEE floats
00261     char pad[2];
00262 };
00263 
00264 // Read a binary STL file
00265 ErrorCode ReadSTL::binary_read_triangles( const char* name,
00266                                           ReadSTL::ByteOrder byte_order,
00267                                           std::vector< ReadSTL::Triangle >& tris )
00268 {
00269     FILE* file = fopen( name, "rb" );
00270     if( !file )
00271     {
00272         return MB_FILE_DOES_NOT_EXIST;
00273     }
00274 
00275     // Read header block
00276     BinaryHeader header;
00277     if( fread( &header, 84, 1, file ) != 1 )
00278     {
00279         fclose( file );
00280         return MB_FILE_WRITE_ERROR;
00281     }
00282 
00283     // Allow user setting for byte order, default to little endian
00284     const bool want_big_endian = ( byte_order == STL_BIG_ENDIAN );
00285     const bool am_big_endian   = !SysUtil::little_endian();
00286     bool swap_bytes            = ( want_big_endian == am_big_endian );
00287 
00288     // Compare the number of triangles to the length of the file.
00289     // The file must contain an 80-byte description, a 4-byte
00290     // triangle count and 50 bytes per triangle.
00291     //
00292     // The triangle count *may* allow us to determine the byte order
00293     // of the file, if it is not an endian-symmetric value.
00294     //
00295     // We need to compare the expected size calculated from the triangle
00296     // count with the file size anyway, as an invalid file or a byte-
00297     // swapping issue could result in a very large (incorrect) value for
00298     // num_tri, resulting in a SEGFAULT.
00299 
00300     // Get expected number of triangles
00301     if( swap_bytes ) SysUtil::byteswap( &header.count, 1 );
00302     unsigned long num_tri = header.count;
00303 
00304     // Get the file length
00305     long filesize = SysUtil::filesize( file );
00306     if( filesize >= 0 )
00307     {  // -1 indicates could not determine file size (e.g. reading from FIFO)
00308         // Check file size, but be careful of numeric overflow
00309         if( ULONG_MAX / 50 - 84 < num_tri ||  // Next calc would have overflow
00310             84 + 50 * num_tri != (unsigned long)filesize )
00311         {
00312             // Unless the byte order was specified explicitly in the
00313             // tag, try the opposite byte order.
00314             uint32_t num_tri_tmp = header.count;
00315             SysUtil::byteswap( &num_tri_tmp, 1 );
00316             unsigned long num_tri_swap = num_tri_tmp;
00317             if( byte_order != STL_UNKNOWN_BYTE_ORDER ||  // If byte order was specified, fail now
00318                 ULONG_MAX / 50 - 84 < num_tri_swap ||    // Watch for overflow in next line
00319                 84 + 50 * num_tri_swap != (unsigned long)filesize )
00320             {
00321                 fclose( file );
00322                 return MB_FILE_WRITE_ERROR;
00323             }
00324             swap_bytes = !swap_bytes;
00325             num_tri    = num_tri_swap;
00326         }
00327     }
00328 
00329     // Allocate storage for triangles
00330     tris.resize( num_tri );
00331 
00332     // Read each triangle
00333     BinaryTri tri;  // Binary block read from file
00334     for( std::vector< Triangle >::iterator i = tris.begin(); i != tris.end(); ++i )
00335     {
00336         if( fread( &tri, 50, 1, file ) != 1 )
00337         {
00338             fclose( file );
00339             return MB_FILE_WRITE_ERROR;
00340         }
00341 
00342         if( swap_bytes ) SysUtil::byteswap( tri.coords, 9 );
00343 
00344         for( unsigned j = 0; j < 9; ++j )
00345             i->points[j / 3].coords[j % 3] = tri.coords[j];
00346     }
00347 
00348     fclose( file );
00349     return MB_SUCCESS;
00350 }
00351 
00352 ReaderIface* ReadSTL::factory( Interface* iface )
00353 {
00354     return new ReadSTL( iface );
00355 }
00356 
00357 }  // namespace moab
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines