MOAB: Mesh Oriented datABase
(version 5.4.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 */, 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