Mesh Oriented datABase  (version 5.6.0)
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 }

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 262 of file ReadSTL.cpp.

265 {
266  FILE* file = fopen( name, "rb" );
267  if( !file )
268  {
269  return MB_FILE_DOES_NOT_EXIST;
270  }
271 
272  // Read header block
273  BinaryHeader header;
274  if( fread( &header, 84, 1, file ) != 1 )
275  {
276  fclose( file );
277  return MB_FILE_WRITE_ERROR;
278  }
279 
280  // Allow user setting for byte order, default to little endian
281  const bool want_big_endian = ( byte_order == STL_BIG_ENDIAN );
282  const bool am_big_endian = !SysUtil::little_endian();
283  bool swap_bytes = ( want_big_endian == am_big_endian );
284 
285  // Compare the number of triangles to the length of the file.
286  // The file must contain an 80-byte description, a 4-byte
287  // triangle count and 50 bytes per triangle.
288  //
289  // The triangle count *may* allow us to determine the byte order
290  // of the file, if it is not an endian-symmetric value.
291  //
292  // We need to compare the expected size calculated from the triangle
293  // count with the file size anyway, as an invalid file or a byte-
294  // swapping issue could result in a very large (incorrect) value for
295  // num_tri, resulting in a SEGFAULT.
296 
297  // Get expected number of triangles
298  if( swap_bytes ) SysUtil::byteswap( &header.count, 1 );
299  unsigned long num_tri = header.count;
300 
301  // Get the file length
302  long filesize = SysUtil::filesize( file );
303  if( filesize >= 0 )
304  { // -1 indicates could not determine file size (e.g. reading from FIFO)
305  // Check file size, but be careful of numeric overflow
306  if( ULONG_MAX / 50 - 84 < num_tri || // Next calc would have overflow
307  84 + 50 * num_tri != (unsigned long)filesize )
308  {
309  // Unless the byte order was specified explicitly in the
310  // tag, try the opposite byte order.
311  uint32_t num_tri_tmp = header.count;
312  SysUtil::byteswap( &num_tri_tmp, 1 );
313  unsigned long num_tri_swap = num_tri_tmp;
314  if( byte_order != STL_UNKNOWN_BYTE_ORDER || // If byte order was specified, fail now
315  ULONG_MAX / 50 - 84 < num_tri_swap || // Watch for overflow in next line
316  84 + 50 * num_tri_swap != (unsigned long)filesize )
317  {
318  fclose( file );
319  return MB_FILE_WRITE_ERROR;
320  }
322  num_tri = num_tri_swap;
323  }
324  }
325 
326  // Allocate storage for triangles
327  tris.resize( num_tri );
328 
329  // Read each triangle
330  BinaryTri tri; // Binary block read from file
331  for( std::vector< Triangle >::iterator i = tris.begin(); i != tris.end(); ++i )
332  {
333  if( fread( &tri, 50, 1, file ) != 1 )
334  {
335  fclose( file );
336  return MB_FILE_WRITE_ERROR;
337  }
338 
339  if( swap_bytes ) SysUtil::byteswap( tri.coords, 9 );
340 
341  for( unsigned j = 0; j < 9; ++j )
342  i->points[j / 3].coords[j % 3] = tri.coords[j];
343  }
344 
345  fclose( file );
346  return MB_SUCCESS;
347 }

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 349 of file ReadSTL.cpp.

350 {
351  return new ReadSTL( iface );
352 }

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: