MOAB: Mesh Oriented datABase  (version 5.2.1)
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)
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
Interface * mdbImpl
 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 63 of file ReadSTL.hpp.


Member Enumeration Documentation

Enumerator:
STL_BIG_ENDIAN 
STL_LITTLE_ENDIAN 
STL_UNKNOWN_BYTE_ORDER 

Definition at line 97 of file ReadSTL.hpp.


Constructor & Destructor Documentation

ReadSTL::ReadSTL ( Interface *  impl = NULL)

Constructor.

Definition at line 40 of file ReadSTL.cpp.

References mdbImpl, and readMeshIface.

Referenced by factory().

                                  : mdbImpl( impl )
{
    mdbImpl->query_interface( readMeshIface );
}
ReadSTL::~ReadSTL ( ) [virtual]

Destructor.

Definition at line 45 of file ReadSTL.cpp.

References mdbImpl, and readMeshIface.

{
    if( readMeshIface )
    {
        mdbImpl->release_interface( readMeshIface );
        readMeshIface = NULL;
    }
}

Member Function Documentation

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

Definition at line 170 of file ReadSTL.cpp.

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, readMeshIface, and tokens.

Referenced by load_file().

{
    FILE* file = fopen( name, "r" );
    if( !file ) { return MB_FILE_DOES_NOT_EXIST; }

    char header[81];
    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
        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 );
    }

    fclose( file );
    return MB_SUCCESS;
}
ErrorCode ReadSTL::binary_read_triangles ( const char *  file_name,
ByteOrder  byte_order,
std::vector< Triangle > &  tris_out 
) [protected]

Definition at line 248 of file ReadSTL.cpp.

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().

{
    FILE* file = fopen( name, "rb" );
    if( !file ) { return MB_FILE_DOES_NOT_EXIST; }

    // Read header block
    BinaryHeader header;
    if( fread( &header, 84, 1, file ) != 1 )
    {
        fclose( file );
        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-symmetric 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 overflow
            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 );
                return MB_FILE_WRITE_ERROR;
            }
            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 );
            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 331 of file ReadSTL.cpp.

References ReadSTL().

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

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

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

{
    if( subset_list ) { MB_SET_ERR( MB_UNSUPPORTED_OPERATION, "Reading subset of files not supported for STL" ); }

    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 ) { MB_SET_ERR( MB_FAILURE, "Conflicting options: BINARY ASCII" ); }

    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 ) { MB_SET_ERR( MB_FAILURE, "Conflicting options: BIG_ENDIAN LITTLE_ENDIAN" ); }
    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 recover 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 60 of file ReadSTL.cpp.

References MB_NOT_IMPLEMENTED.

{
    return MB_NOT_IMPLEMENTED;
}

Member Data Documentation

Interface* moab::ReadSTL::mdbImpl [protected]

interface instance

Definition at line 114 of file ReadSTL.hpp.

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

Definition at line 111 of file ReadSTL.hpp.

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

List of all members.


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