Mesh Oriented datABase  (version 5.5.1)
An array-based unstructured mesh library
moab::ReadSTL Class Reference

ASCII and Binary Stereo Lithography File readers. More...

#include <ReadSTL.hpp>

+ Inheritance diagram for moab::ReadSTL:
+ Collaboration diagram for moab::ReadSTL:

Classes

struct  Point
 
struct  Triangle
 

Public Types

enum  ByteOrder { STL_BIG_ENDIAN , STL_LITTLE_ENDIAN , STL_UNKNOWN_BYTE_ORDER }
 

Public Member Functions

ErrorCode load_file (const char *file_name, const EntityHandle *file_set, const FileOptions &opts, const SubsetList *subset_list=0, const Tag *file_id_tag=0)
 Generic file loading code for both binary and ASCII readers. Calls reader-specific read_triangles function to do actual I/O. More...
 
ErrorCode read_tag_values (const char *file_name, const char *tag_name, const FileOptions &opts, std::vector< int > &tag_values_out, const SubsetList *subset_list=0)
 Read tag values from a file. More...
 
 ReadSTL (Interface *impl=NULL)
 Constructor. More...
 
virtual ~ReadSTL ()
 Destructor. More...
 
- Public Member Functions inherited from moab::ReaderIface
virtual ~ReaderIface ()
 

Static Public Member Functions

static ReaderIfacefactory (Interface *)
 factory method for STL reader More...
 

Protected Member Functions

ErrorCode ascii_read_triangles (const char *file_name, std::vector< Triangle > &tris_out)
 
ErrorCode binary_read_triangles (const char *file_name, ByteOrder byte_order, std::vector< Triangle > &tris_out)
 

Protected Attributes

ReadUtilIfacereadMeshIface
 
InterfacemdbImpl
 interface instance More...
 

Detailed Description

ASCII and Binary Stereo Lithography File readers.

Author
Jason Kraftcheck

STL files contain no connectivity infomration. Each triangle is specified as by three sets of single-precision coordinate triples. This reader does not use ANY tolerance when comparing vertex locations to recover connectivity. The points must be EXACTLY equal (including the sign on zero values.) If the file was written by an application which represented connectivity explicitly, there is no reason for the vertex coordinates to be anything other than exactly equal.

For binary STL files, the defacto standard is that they be written with a little-endian byte order. The reader will attempt to determine the byte order automatically, and if it is ambiguous, will default to little-endian. The byte ordering may be forced by by creating an integer tag named "__STL_BYTE_ORDER" and setting a global/mesh value for the tag as 1 for big-endian or 0 for little-endian.

For binary files, this reader relies on the file size to determine the validity of the file and may use it in guessing the byte order. This should not be an issue, as the file size can be determined exactly from the number of triangles for a valid file. However, if for some reason the file is readable even though it is invalid (e.g. it is some hybrid file with STL data in the beginning and some app- specific data appended to the end of the file) the check on the file size can be disabled by giving the reader a something other than a regular file to read from. For example, on Unix-like systems, have the reader read from a FIFO instead of a file: mkfifo /tmp/fifo.stlb cat my_binary_file.stlb > /tmp/fifo.stlb and instruct the MOAB-based application to read from /tmp/fifo.stlb

Definition at line 63 of file ReadSTL.hpp.

Member Enumeration Documentation

◆ ByteOrder

Enumerator
STL_BIG_ENDIAN 
STL_LITTLE_ENDIAN 
STL_UNKNOWN_BYTE_ORDER 

Definition at line 103 of file ReadSTL.hpp.

104  {
108  };

Constructor & Destructor Documentation

◆ ReadSTL()

ReadSTL::ReadSTL ( Interface impl = NULL)

Constructor.

Definition at line 40 of file ReadSTL.cpp.

40  : mdbImpl( impl )
41 {
43 }

References mdbImpl, moab::Interface::query_interface(), and readMeshIface.

Referenced by factory().

◆ ~ReadSTL()

ReadSTL::~ReadSTL ( )
virtual

Destructor.

Definition at line 45 of file ReadSTL.cpp.

46 {
47  if( readMeshIface )
48  {
50  readMeshIface = NULL;
51  }
52 }

References mdbImpl, readMeshIface, and moab::Interface::release_interface().

Member Function Documentation

◆ ascii_read_triangles()

ErrorCode ReadSTL::ascii_read_triangles ( const char *  file_name,
std::vector< Triangle > &  tris_out 
)
protected

Definition at line 184 of file ReadSTL.cpp.

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 }

References moab::ReadSTL::Point::coords, moab::FileTokenizer::get_floats(), moab::FileTokenizer::match_token(), MB_FILE_DOES_NOT_EXIST, MB_FILE_WRITE_ERROR, MB_SUCCESS, moab::ReadSTL::Triangle::points, and readMeshIface.

Referenced by load_file().

◆ binary_read_triangles()

ErrorCode ReadSTL::binary_read_triangles ( const char *  file_name,
ReadSTL::ByteOrder  byte_order,
std::vector< Triangle > &  tris_out 
)
protected

Definition at line 265 of file ReadSTL.cpp.

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  }
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 }

References moab::SysUtil::byteswap(), moab::BinaryTri::coords, moab::BinaryHeader::count, moab::SysUtil::filesize(), moab::SysUtil::little_endian(), MB_FILE_DOES_NOT_EXIST, MB_FILE_WRITE_ERROR, MB_SUCCESS, STL_BIG_ENDIAN, STL_UNKNOWN_BYTE_ORDER, and moab::SysUtil::swap_bytes().

Referenced by load_file().

◆ factory()

ReaderIface * ReadSTL::factory ( Interface iface)
static

factory method for STL reader

Definition at line 352 of file ReadSTL.cpp.

353 {
354  return new ReadSTL( iface );
355 }

References iface, and ReadSTL().

Referenced by moab::ReaderWriterSet::ReaderWriterSet().

◆ load_file()

ErrorCode ReadSTL::load_file ( const char *  file_name,
const EntityHandle file_set,
const FileOptions opts,
const SubsetList subset_list = 0,
const Tag file_id_tag = 0 
)
virtual

Generic file loading code for both binary and ASCII readers. Calls reader-specific read_triangles function to do actual I/O.

Implements moab::ReaderIface.

Definition at line 72 of file ReadSTL.cpp.

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  }
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 }

References ascii_read_triangles(), moab::ReadUtilIface::assign_ids(), moab::SysUtil::big_endian(), binary_read_triangles(), ErrorCode, moab::ReadUtilIface::get_element_connect(), moab::ReadUtilIface::get_node_coords(), moab::FileOptions::get_null_option(), moab::SysUtil::little_endian(), MB_SET_ERR, MB_START_ID, MB_SUCCESS, MB_UNSUPPORTED_OPERATION, MBTRI, readMeshIface, STL_BIG_ENDIAN, STL_LITTLE_ENDIAN, STL_UNKNOWN_BYTE_ORDER, and moab::ReadUtilIface::update_adjacencies().

◆ read_tag_values()

ErrorCode ReadSTL::read_tag_values ( const char *  file_name,
const char *  tag_name,
const FileOptions opts,
std::vector< int > &  tag_values_out,
const SubsetList subset_list = 0 
)
virtual

Read tag values from a file.

Read the list if all integer tag values from the file for a tag that is a single integer value per entity.

Parameters
file_nameThe file to read.
tag_nameThe tag for which to read values
tag_values_outOutput: The list of tag values.
subset_listAn array of tag name and value sets specifying the subset of the file to read. If multiple tags are specified, the sets that match all tags (intersection) should be read.
subset_list_lengthThe length of the 'subset_list' array.

Implements moab::ReaderIface.

Definition at line 60 of file ReadSTL.cpp.

65 {
66  return MB_NOT_IMPLEMENTED;
67 }

References MB_NOT_IMPLEMENTED.

Member Data Documentation

◆ mdbImpl

Interface* moab::ReadSTL::mdbImpl
protected

interface instance

Definition at line 120 of file ReadSTL.hpp.

Referenced by ReadSTL(), and ~ReadSTL().

◆ readMeshIface

ReadUtilIface* moab::ReadSTL::readMeshIface
protected

Definition at line 117 of file ReadSTL.hpp.

Referenced by ascii_read_triangles(), load_file(), ReadSTL(), and ~ReadSTL().


The documentation for this class was generated from the following files: