Loading [MathJax]/extensions/tex2jax.js
Mesh Oriented datABase  (version 5.5.1)
An array-based unstructured mesh library
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
ReadSTL.cpp
Go to the documentation of this file.
1 /** 2  * MOAB, a Mesh-Oriented datABase, is a software component for creating, 3  * storing and accessing finite element mesh data. 4  * 5  * Copyright 2004 Sandia Corporation. Under the terms of Contract 6  * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government 7  * retains certain rights in this software. 8  * 9  * This library is free software; you can redistribute it and/or 10  * modify it under the terms of the GNU Lesser General Public 11  * License as published by the Free Software Foundation; either 12  * version 2.1 of the License, or (at your option) any later version. 13  * 14  */ 15  16 /** 17  * \class ReadSTL 18  * \brief ASCII and Binary Stereo Lithography File readers. 19  * \author Jason Kraftcheck 20  */ 21  22 #include "ReadSTL.hpp" 23 #include "FileTokenizer.hpp" // For FileTokenizer 24 #include "Internals.hpp" 25 #include "moab/Interface.hpp" 26 #include "moab/ReadUtilIface.hpp" 27 #include "moab/Range.hpp" 28 #include "moab/FileOptions.hpp" 29 #include "SysUtil.hpp" 30  31 #include <cerrno> 32 #include <cstring> 33 #include <climits> 34 #include <cassert> 35 #include <map> 36  37 namespace moab 38 { 39  40 ReadSTL::ReadSTL( Interface* impl ) : mdbImpl( impl ) 41 { 42  mdbImpl->query_interface( readMeshIface ); 43 } 44  45 ReadSTL::~ReadSTL() 46 { 47  if( readMeshIface ) 48  { 49  mdbImpl->release_interface( readMeshIface ); 50  readMeshIface = NULL; 51  } 52 } 53  54 // Used to put points in an STL tree-based container 55 bool ReadSTL::Point::operator<( const ReadSTL::Point& other ) const 56 { 57  return 0 > memcmp( this, &other, sizeof( ReadSTL::Point ) ); 58 } 59  60 ErrorCode ReadSTL::read_tag_values( const char* /* file_name */, 61  const char* /* tag_name */, 62  const FileOptions& /* opts */, 63  std::vector< int >& /* tag_values_out */, 64  const SubsetList* /* subset_list */ ) 65 { 66  return MB_NOT_IMPLEMENTED; 67 } 68  69 // Generic load function for both ASCII and binary. Calls 70 // pure-virtual function implemented in subclasses to read 71 // the data from the file. 72 ErrorCode ReadSTL::load_file( const char* filename, 73  const EntityHandle* /* file_set */, 74  const FileOptions& opts, 75  const ReaderIface::SubsetList* subset_list, 76  const Tag* file_id_tag ) 77 { 78  if( subset_list ) 79  { 80  MB_SET_ERR( MB_UNSUPPORTED_OPERATION, "Reading subset of files not supported for STL" ); 81  } 82  83  ErrorCode result; 84  85  std::vector< ReadSTL::Triangle > triangles; 86  87  bool is_ascii = false, is_binary = false; 88  if( MB_SUCCESS == opts.get_null_option( "ASCII" ) ) is_ascii = true; 89  if( MB_SUCCESS == opts.get_null_option( "BINARY" ) ) is_binary = true; 90  if( is_ascii && is_binary ) 91  { 92  MB_SET_ERR( MB_FAILURE, "Conflicting options: BINARY ASCII" ); 93  } 94  95  bool big_endian = false, little_endian = false; 96  if( MB_SUCCESS == opts.get_null_option( "BIG_ENDIAN" ) ) big_endian = true; 97  if( MB_SUCCESS == opts.get_null_option( "LITTLE_ENDIAN" ) ) little_endian = true; 98  if( big_endian && little_endian ) 99  { 100  MB_SET_ERR( MB_FAILURE, "Conflicting options: BIG_ENDIAN LITTLE_ENDIAN" ); 101  } 102  ByteOrder byte_order = big_endian ? STL_BIG_ENDIAN : little_endian ? STL_LITTLE_ENDIAN : STL_UNKNOWN_BYTE_ORDER; 103  104  if( is_ascii ) 105  result = ascii_read_triangles( filename, triangles ); 106  else if( is_binary ) 107  result = binary_read_triangles( filename, byte_order, triangles ); 108  else 109  { 110  // Try ASCII first 111  result = ascii_read_triangles( filename, triangles ); 112  if( MB_SUCCESS != result ) 113  // ASCII failed, try binary 114  result = binary_read_triangles( filename, byte_order, triangles ); 115  } 116  if( MB_SUCCESS != result ) return result; 117  118  // Create a std::map from position->handle, and such 119  // that all positions are specified, and handles are zero. 120  std::map< Point, EntityHandle > vertex_map; 121  for( std::vector< Triangle >::iterator i = triangles.begin(); i != triangles.end(); ++i ) 122  { 123  vertex_map[i->points[0]] = 0; 124  vertex_map[i->points[1]] = 0; 125  vertex_map[i->points[2]] = 0; 126  } 127  128  // Create vertices 129  std::vector< double* > coord_arrays; 130  EntityHandle vtx_handle = 0; 131  result = readMeshIface->get_node_coords( 3, vertex_map.size(), MB_START_ID, vtx_handle, coord_arrays ); 132  if( MB_SUCCESS != result ) return result; 133  134  // Copy vertex coordinates into entity sequence coordinate arrays 135  // and copy handle into vertex_map. 136  double *x = coord_arrays[0], *y = coord_arrays[1], *z = coord_arrays[2]; 137  for( std::map< Point, EntityHandle >::iterator i = vertex_map.begin(); i != vertex_map.end(); ++i ) 138  { 139  i->second = vtx_handle; 140  ++vtx_handle; 141  *x = i->first.coords[0]; 142  ++x; 143  *y = i->first.coords[1]; 144  ++y; 145  *z = i->first.coords[2]; 146  ++z; 147  } 148  149  // Allocate triangles 150  EntityHandle elm_handle = 0; 151  EntityHandle* connectivity; 152  result = readMeshIface->get_element_connect( triangles.size(), 3, MBTRI, MB_START_ID, elm_handle, connectivity ); 153  if( MB_SUCCESS != result ) return result; 154  155  // Use vertex_map to recover triangle connectivity from 156  // vertex coordinates. 157  EntityHandle* conn_sav = connectivity; 158  for( std::vector< Triangle >::iterator i = triangles.begin(); i != triangles.end(); ++i ) 159  { 160  *connectivity = vertex_map[i->points[0]]; 161  ++connectivity; 162  *connectivity = vertex_map[i->points[1]]; 163  ++connectivity; 164  *connectivity = vertex_map[i->points[2]]; 165  ++connectivity; 166  } 167  168  // Notify MOAB of the new elements 169  result = readMeshIface->update_adjacencies( elm_handle, triangles.size(), 3, conn_sav ); 170  if( MB_SUCCESS != result ) return result; 171  172  if( file_id_tag ) 173  { 174  Range vertices( vtx_handle, vtx_handle + vertex_map.size() - 1 ); 175  Range elements( elm_handle, elm_handle + triangles.size() - 1 ); 176  readMeshIface->assign_ids( *file_id_tag, vertices ); 177  readMeshIface->assign_ids( *file_id_tag, elements ); 178  } 179  180  return MB_SUCCESS; 181 } 182  183 // Read ASCII file 184 ErrorCode ReadSTL::ascii_read_triangles( const char* name, std::vector< ReadSTL::Triangle >& tris ) 185 { 186  FILE* file = fopen( name, "r" ); 187  if( !file ) 188  { 189  return MB_FILE_DOES_NOT_EXIST; 190  } 191  192  char header[81]; 193  if( !fgets( header, sizeof( header ), file ) || // Read header line 194  strlen( header ) < 6 || // Must be at least 6 chars 195  header[strlen( header ) - 1] != '\n' || // Cannot exceed 80 chars 196  memcmp( header, "solid", 5 ) || // Must begin with "solid" 197  !isspace( header[5] ) ) 198  { // Followed by a whitespace char 199  fclose( file ); 200  return MB_FILE_WRITE_ERROR; 201  } 202  203  // Use tokenizer for remainder of parsing 204  FileTokenizer tokens( file, readMeshIface ); 205  206  Triangle tri; 207  float norm[3]; 208  209  // Read until end of file. If we reach "endsolid", read 210  // was successful. If EOF before "endsolid", return error. 211  for( ;; ) 212  { 213  // Check for either another facet or the end of the list. 214  const char* const expected[] = { "facet", "endsolid", 0 }; 215  switch( tokens.match_token( expected ) ) 216  { 217  case 1: 218  break; // Found another facet 219  case 2: 220  return MB_SUCCESS; // Found "endsolid" -- done 221  default: 222  return MB_FILE_WRITE_ERROR; // Found something else, or EOF 223  } 224  225  if( !tokens.match_token( "normal" ) || // Expect "normal" keyword 226  !tokens.get_floats( 3, norm ) || // Followed by normal vector 227  !tokens.match_token( "outer" ) || // Followed by "outer loop" 228  !tokens.match_token( "loop" ) ) 229  return MB_FILE_WRITE_ERROR; 230  231  // For each of three triangle vertices 232  for( int i = 0; i < 3; i++ ) 233  { 234  if( !tokens.match_token( "vertex" ) || !tokens.get_floats( 3, tri.points[i].coords ) ) 235  return MB_FILE_WRITE_ERROR; 236  } 237  238  if( !tokens.match_token( "endloop" ) || // Facet ends with "endloop" 239  !tokens.match_token( "endfacet" ) ) // and then "endfacet" 240  return MB_FILE_WRITE_ERROR; 241  242  tris.push_back( tri ); 243  } 244  245  fclose( file ); 246  return MB_SUCCESS; 247 } 248  249 // Header block from binary STL file (84 bytes long) 250 struct BinaryHeader 251 { 252  char comment[80]; // 80 byte comment string (null terminated?) 253  uint32_t count; // Number of triangles - 4 byte integer 254 }; 255  256 // Triangle spec from file (50 bytes) 257 struct BinaryTri 258 { 259  float normal[3]; // Normal as 3 4-byte little-endian IEEE floats 260  float coords[9]; // Vertex coords as 9 4-byte little-endian IEEE floats 261  char pad[2]; 262 }; 263  264 // Read a binary STL file 265 ErrorCode ReadSTL::binary_read_triangles( const char* name, 266  ReadSTL::ByteOrder byte_order, 267  std::vector< ReadSTL::Triangle >& tris ) 268 { 269  FILE* file = fopen( name, "rb" ); 270  if( !file ) 271  { 272  return MB_FILE_DOES_NOT_EXIST; 273  } 274  275  // Read header block 276  BinaryHeader header; 277  if( fread( &header, 84, 1, file ) != 1 ) 278  { 279  fclose( file ); 280  return MB_FILE_WRITE_ERROR; 281  } 282  283  // Allow user setting for byte order, default to little endian 284  const bool want_big_endian = ( byte_order == STL_BIG_ENDIAN ); 285  const bool am_big_endian = !SysUtil::little_endian(); 286  bool swap_bytes = ( want_big_endian == am_big_endian ); 287  288  // Compare the number of triangles to the length of the file. 289  // The file must contain an 80-byte description, a 4-byte 290  // triangle count and 50 bytes per triangle. 291  // 292  // The triangle count *may* allow us to determine the byte order 293  // of the file, if it is not an endian-symmetric value. 294  // 295  // We need to compare the expected size calculated from the triangle 296  // count with the file size anyway, as an invalid file or a byte- 297  // swapping issue could result in a very large (incorrect) value for 298  // num_tri, resulting in a SEGFAULT. 299  300  // Get expected number of triangles 301  if( swap_bytes ) SysUtil::byteswap( &header.count, 1 ); 302  unsigned long num_tri = header.count; 303  304  // Get the file length 305  long filesize = SysUtil::filesize( file ); 306  if( filesize >= 0 ) 307  { // -1 indicates could not determine file size (e.g. reading from FIFO) 308  // Check file size, but be careful of numeric overflow 309  if( ULONG_MAX / 50 - 84 < num_tri || // Next calc would have overflow 310  84 + 50 * num_tri != (unsigned long)filesize ) 311  { 312  // Unless the byte order was specified explicitly in the 313  // tag, try the opposite byte order. 314  uint32_t num_tri_tmp = header.count; 315  SysUtil::byteswap( &num_tri_tmp, 1 ); 316  unsigned long num_tri_swap = num_tri_tmp; 317  if( byte_order != STL_UNKNOWN_BYTE_ORDER || // If byte order was specified, fail now 318  ULONG_MAX / 50 - 84 < num_tri_swap || // Watch for overflow in next line 319  84 + 50 * num_tri_swap != (unsigned long)filesize ) 320  { 321  fclose( file ); 322  return MB_FILE_WRITE_ERROR; 323  } 324  swap_bytes = !swap_bytes; 325  num_tri = num_tri_swap; 326  } 327  } 328  329  // Allocate storage for triangles 330  tris.resize( num_tri ); 331  332  // Read each triangle 333  BinaryTri tri; // Binary block read from file 334  for( std::vector< Triangle >::iterator i = tris.begin(); i != tris.end(); ++i ) 335  { 336  if( fread( &tri, 50, 1, file ) != 1 ) 337  { 338  fclose( file ); 339  return MB_FILE_WRITE_ERROR; 340  } 341  342  if( swap_bytes ) SysUtil::byteswap( tri.coords, 9 ); 343  344  for( unsigned j = 0; j < 9; ++j ) 345  i->points[j / 3].coords[j % 3] = tri.coords[j]; 346  } 347  348  fclose( file ); 349  return MB_SUCCESS; 350 } 351  352 ReaderIface* ReadSTL::factory( Interface* iface ) 353 { 354  return new ReadSTL( iface ); 355 } 356  357 } // namespace moab