moab
moab::ReadSTL Class Reference

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

#include <ReadSTL.hpp>

Inheritance diagram for moab::ReadSTL:
moab::ReaderIface

List of all members.

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)
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.
 ReadSTL (Interface *impl=NULL)
 Constructor.
virtual ~ReadSTL ()
 Destructor.

Static Public Member Functions

static ReaderIfacefactory (Interface *)
 factory method for STL reader

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

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 64 of file ReadSTL.hpp.


Member Enumeration Documentation

Enumerator:
STL_BIG_ENDIAN 
STL_LITTLE_ENDIAN 
STL_UNKNOWN_BYTE_ORDER 

Definition at line 103 of file ReadSTL.hpp.


Constructor & Destructor Documentation

ReadSTL::ReadSTL ( Interface impl = NULL)

Constructor.

Definition at line 40 of file ReadSTL.cpp.

ReadSTL::~ReadSTL ( ) [virtual]

Destructor.

Definition at line 46 of file ReadSTL.cpp.


Member Function Documentation

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

Definition at line 195 of file ReadSTL.cpp.

{
  FILE* file = fopen( name, "r" );
  if (!file)
  {
    readMeshIface->report_error( "%s: %s\n", name, strerror(errno) );
    return MB_FILE_DOES_NOT_EXIST;
  }
  
  char header[82];
  if (!fgets( header, sizeof(header), file ) ||  // read header line
      strlen( header ) < 6                   ||  // must be at least 6 chars
      header[strlen(header) - 1] != '\n'     ||  // cannot exceed 80 chars
      memcmp( header, "solid", 5 )           ||  // must begin with "solid"
      !isspace( header[5] ) )                    // followed by a whitespace char
  {
    readMeshIface->report_error( "%s: %s\n", name, strerror(errno) );
    fclose( file );
    return MB_FILE_WRITE_ERROR;
  }
  
    // Use tokenizer for remainder of parsing
  FileTokenizer tokens( file, readMeshIface );
  
  Triangle tri;
  float norm[3];
  
    // Read until end of file.  If we reach "endsolid", read
    // was successful.  If EOF before "endsolid", return error.
  for (;;)
  {
      // Check for either another facet or the end of the list.
    const char* const expected[] = { "facet", "endsolid", 0 };
    switch( tokens.match_token( expected ) )
    {
      case 1:  break;                        // found another facet
      case 2:  return MB_SUCCESS;            // found "endsolid" -- done
      default: return MB_FILE_WRITE_ERROR;   // found something else, or EOF
    }
    
    if (!tokens.match_token( "normal" ) ||    // expect "normal" keyword
        !tokens.get_floats( 3, norm )   ||    // followed by normal vector
        !tokens.match_token( "outer" )  ||    // followed by "outer loop"
        !tokens.match_token( "loop" )   )
      return MB_FILE_WRITE_ERROR;
    
      // for each of three triangle vertices
    for (int i = 0; i < 3; i++)
    {
      if (!tokens.match_token("vertex") ||
          !tokens.get_floats( 3, tri.points[i].coords ))
        return MB_FILE_WRITE_ERROR;
    }
    
    if (!tokens.match_token( "endloop" ) ||   // facet ends with "endloop"
        !tokens.match_token( "endfacet" ) )   // and then "endfacet"
      return MB_FILE_WRITE_ERROR;
    
    tris.push_back( tri );
  }
}
ErrorCode ReadSTL::binary_read_triangles ( const char *  file_name,
ByteOrder  byte_order,
std::vector< Triangle > &  tris_out 
) [protected]

Definition at line 273 of file ReadSTL.cpp.

{
  FILE* file = fopen( name, "rb" );
  if (!file)
  {
    readMeshIface->report_error( "%s: %s\n", name, strerror(errno) );
    return MB_FILE_DOES_NOT_EXIST;
  }
  
    // Read header block
  BinaryHeader header;
  if (fread( &header, 84, 1, file ) != 1)
  {
    fclose ( file );
    readMeshIface->report_error( "%s: %s\n", name, strerror(errno) );
    return MB_FILE_WRITE_ERROR;
  }
  
    // Allow user setting for byte order, default to little endian
  const bool want_big_endian = (byte_order == STL_BIG_ENDIAN);
  const bool am_big_endian = !SysUtil::little_endian();
  bool swap_bytes = (want_big_endian == am_big_endian);
  
    // Compare the number of triangles to the length of the file.  
    // The file must contain an 80-byte description, a 4-byte 
    // triangle count and 50 bytes per triangle.
    //
    // The triangle count *may* allow us to determine the byte order
    // of the file, if it is not an endian-symetric value.  
    //
    // We need to compare the expected size calculated from the triangle
    // count with the file size anyway, as an invalid file or a byte-
    // swapping issue could result in a very large (incorrect) value for
    // num_tri, resulting in a SEGFAULT. 
  
    // Get expected number of triangles
  if (swap_bytes)
    SysUtil::byteswap( &header.count, 1 );
  unsigned long num_tri = header.count;
  
    // Get the file length
  long filesize = SysUtil::filesize( file );
  if (filesize >= 0) // -1 indicates could not determine file size (e.g. reading from FIFO)
  {
      // Check file size, but be careful of numeric overflow
    if (ULONG_MAX / 50 - 84 < num_tri ||   // next calc would have oveflow
        84 + 50 * num_tri != (unsigned long)filesize)
    {
        // Unless the byte order was specified explicitly in the 
        // tag, try the opposite byte order.
      uint32_t num_tri_tmp = header.count;
      SysUtil::byteswap( &num_tri_tmp, 1 );
      unsigned long num_tri_swap = num_tri_tmp;
      if (byte_order != STL_UNKNOWN_BYTE_ORDER || // If byte order was specified, fail now
          ULONG_MAX / 50 - 84 < num_tri_swap  || // watch for overflow in next line
          84 + 50 * num_tri_swap != (unsigned long)filesize)
      {
        fclose( file );
        readMeshIface->report_error( "%s: not a binary STL file\n", name );
        return MB_FILE_DOES_NOT_EXIST;
      }
      swap_bytes = !swap_bytes;
      num_tri = num_tri_swap;
    }
  }
    
    // Allocate storage for triangles
  tris.resize( num_tri );
  
    // Read each triangle
  BinaryTri tri;  // binary block read from file
  for (std::vector<Triangle>::iterator i = tris.begin(); i != tris.end(); ++i)
  {
    if (fread( &tri, 50, 1, file ) != 1)
    {
      fclose( file );
      readMeshIface->report_error( "%s: %s\n", name, strerror(errno) );
      return MB_FILE_WRITE_ERROR;
    }
    
    if (swap_bytes)
      SysUtil::byteswap( tri.coords, 9 );
    
    for (unsigned j = 0; j < 9; ++j)
      i->points[j/3].coords[j%3] = tri.coords[j];
  }
  
  fclose( file );
  return MB_SUCCESS;
}
ReaderIface * ReadSTL::factory ( Interface iface) [static]

factory method for STL reader

Definition at line 367 of file ReadSTL.cpp.

  { return new ReadSTL(iface); }
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 74 of file ReadSTL.cpp.

{
  if (subset_list) {
    readMeshIface->report_error( "Reading subset of files not supported for STL." );
    return MB_UNSUPPORTED_OPERATION;
  }

  ErrorCode result;

  std::vector<ReadSTL::Triangle> triangles;
 
  bool is_ascii = false, is_binary = false;
  if (MB_SUCCESS == opts.get_null_option( "ASCII" ))
    is_ascii = true;
  if (MB_SUCCESS == opts.get_null_option( "BINARY" ))
    is_binary = true;
  if (is_ascii && is_binary) {
    readMeshIface->report_error( "Conflicting options: BINARY ASCII\n" );
    return MB_FAILURE;
  }
  
  bool big_endian = false, little_endian = false;
  if (MB_SUCCESS == opts.get_null_option( "BIG_ENDIAN" ))
    big_endian = true;
  if (MB_SUCCESS == opts.get_null_option( "LITTLE_ENDIAN" ))
    little_endian = true;
  if (big_endian && little_endian) {
    readMeshIface->report_error( "Conflicting options: BIG_ENDIAN LITTLE_ENDIAN\n" );
    return MB_FAILURE;
  }
  ByteOrder byte_order =    big_endian ? STL_BIG_ENDIAN 
                       : little_endian ? STL_LITTLE_ENDIAN 
                       :                 STL_UNKNOWN_BYTE_ORDER;
 
  if (is_ascii) 
    result = ascii_read_triangles( filename, triangles );
  else if (is_binary)
    result = binary_read_triangles( filename, byte_order, triangles );
  else {
      // try ASCII first
    result = ascii_read_triangles( filename, triangles );
    if (MB_SUCCESS != result) 
        // ASCII failed, try binary
      result = binary_read_triangles( filename, byte_order, triangles );
  }
  if (MB_SUCCESS != result)
    return result;

    // Create a std::map from position->handle, and such
    // that all positions are specified, and handles are zero.
  std::map<Point,EntityHandle> vertex_map;
  for (std::vector<Triangle>::iterator i = triangles.begin(); i != triangles.end(); ++i)
  {
    vertex_map[i->points[0]] = 0;
    vertex_map[i->points[1]] = 0;
    vertex_map[i->points[2]] = 0;
  }
  
    // Create vertices 
  std::vector<double*> coord_arrays;
  EntityHandle vtx_handle = 0;
  result = readMeshIface->get_node_coords( 3, vertex_map.size(), MB_START_ID,
                                           vtx_handle, coord_arrays );
  if (MB_SUCCESS != result)
    return result;
    
    // Copy vertex coordinates into entity sequence coordinate arrays
    // and copy handle into vertex_map.
  double *x = coord_arrays[0], *y = coord_arrays[1], *z = coord_arrays[2];
  for (std::map<Point,EntityHandle>::iterator i = vertex_map.begin();
       i != vertex_map.end(); ++i)
  {
    i->second = vtx_handle; ++vtx_handle;
    *x = i->first.coords[0]; ++x;
    *y = i->first.coords[1]; ++y;
    *z = i->first.coords[2]; ++z;
  }
  
    // Allocate triangles
  EntityHandle elm_handle = 0;
  EntityHandle* connectivity;
  result = readMeshIface->get_element_connect( triangles.size(),
                                             3,
                                             MBTRI,
                                             MB_START_ID,
                                             elm_handle,
                                             connectivity );
  if (MB_SUCCESS != result)
    return result;
  
    // Use vertex_map to reconver triangle connectivity from
    // vertex coordinates.
  EntityHandle *conn_sav = connectivity;
  for (std::vector<Triangle>::iterator i = triangles.begin(); i != triangles.end(); ++i)
  {
    *connectivity = vertex_map[i->points[0]]; ++connectivity;
    *connectivity = vertex_map[i->points[1]]; ++connectivity;
    *connectivity = vertex_map[i->points[2]]; ++connectivity;
  }
  
    // notify MOAB of the new elements
  result = readMeshIface->update_adjacencies(elm_handle, triangles.size(), 
                                             3, conn_sav);
  if (MB_SUCCESS != result) return result;

  if (file_id_tag) {
    Range vertices(vtx_handle, vtx_handle+vertex_map.size()-1);
    Range elements(elm_handle, elm_handle+triangles.size()-1);
    readMeshIface->assign_ids( *file_id_tag, vertices );
    readMeshIface->assign_ids( *file_id_tag, elements );
  }

  return MB_SUCCESS;
}
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 62 of file ReadSTL.cpp.

{
  return MB_NOT_IMPLEMENTED;
}

Member Data Documentation

interface instance

Definition at line 119 of file ReadSTL.hpp.

Definition at line 116 of file ReadSTL.hpp.


The documentation for this class was generated from the following files:
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines