MOAB: Mesh Oriented datABase
(version 5.2.1)
|
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