MOAB: Mesh Oriented datABase  (version 5.3.0)
WriteSTL.cpp
Go to the documentation of this file.
00001 /**
00002  * MOAB, a Mesh-Oriented datABase, is a software component for creating,
00003  * storing and accessing finite element mesh data.
00004  *
00005  * Copyright 2004 Sandia Corporation.  Under the terms of Contract
00006  * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government
00007  * retains certain rights in this software.
00008  *
00009  * This library is free software; you can redistribute it and/or
00010  * modify it under the terms of the GNU Lesser General Public
00011  * License as published by the Free Software Foundation; either
00012  * version 2.1 of the License, or (at your option) any later version.
00013  *
00014  */
00015 
00016 /**
00017  * \class WriteSTL
00018  * \brief ASCII and Binary Stereo Lithography File writers.
00019  * \author Jason Kraftcheck
00020  */
00021 
00022 #include "WriteSTL.hpp"
00023 #include "moab/CN.hpp"
00024 #include "moab/Interface.hpp"
00025 #include "moab/Range.hpp"
00026 #include "moab/WriteUtilIface.hpp"
00027 #include "moab/FileOptions.hpp"
00028 #include "SysUtil.hpp"
00029 
00030 #include <cstdio>
00031 #include <sys/types.h>
00032 #include <sys/stat.h>
00033 #include <cerrno>
00034 #include <cmath>
00035 #include <fcntl.h>
00036 #include <climits>
00037 
00038 namespace moab
00039 {
00040 
00041 #if defined( _MSC_VER ) || defined( __MINGW32__ ) /* windows */
00042 #include <io.h>
00043 #ifndef __MINGW32__
00044 typedef unsigned __int32 uint32_t;
00045 #endif
00046 #else /* posix */
00047 #include <unistd.h>
00048 #define _S_IREAD  ( S_IRUSR | S_IRGRP | S_IROTH )
00049 #define _S_IWRITE ( S_IWUSR | S_IWGRP | S_IWOTH )
00050 #endif
00051 
00052 const int DEFAULT_PRECISION = 6;
00053 
00054 WriterIface* WriteSTL::factory( Interface* iface )
00055 {
00056     return new WriteSTL( iface );
00057 }
00058 
00059 WriteSTL::WriteSTL( Interface* impl ) : mbImpl( impl )
00060 {
00061     impl->query_interface( mWriteIface );
00062 }
00063 
00064 WriteSTL::~WriteSTL()
00065 {
00066     mbImpl->release_interface( mWriteIface );
00067 }
00068 
00069 ErrorCode WriteSTL::write_file( const char* file_name, const bool overwrite, const FileOptions& opts,
00070                                 const EntityHandle* ent_handles, const int num_sets,
00071                                 const std::vector< std::string >& qa_list, const Tag* tag_list, int num_tags,
00072                                 int /* export_dimension */ )
00073 {
00074     char header[81];
00075     Range triangles;
00076     ErrorCode rval;
00077 
00078     if( tag_list && num_tags ) { MB_SET_ERR( MB_TYPE_OUT_OF_RANGE, "STL file does not support tag data" ); }
00079 
00080     rval = make_header( header, qa_list );
00081     if( MB_SUCCESS != rval ) return rval;
00082 
00083     rval = get_triangles( ent_handles, num_sets, triangles );
00084     if( MB_SUCCESS != rval ) return rval;
00085 
00086     if( triangles.empty() ) { MB_SET_ERR( MB_ENTITY_NOT_FOUND, "No triangles to write" ); }
00087 
00088     bool is_ascii = false, is_binary = false;
00089     if( MB_SUCCESS == opts.get_null_option( "ASCII" ) ) is_ascii = true;
00090     if( MB_SUCCESS == opts.get_null_option( "BINARY" ) ) is_binary = true;
00091     if( is_ascii && is_binary ) { MB_SET_ERR( MB_FAILURE, "Conflicting options: BINARY ASCII" ); }
00092 
00093     bool big_endian = false, little_endian = false;
00094     if( MB_SUCCESS == opts.get_null_option( "BIG_ENDIAN" ) ) big_endian = true;
00095     if( MB_SUCCESS == opts.get_null_option( "LITTLE_ENDIAN" ) ) little_endian = true;
00096     if( big_endian && little_endian ) { MB_SET_ERR( MB_FAILURE, "Conflicting options: BIG_ENDIAN LITTLE_ENDIAN" ); }
00097     ByteOrder byte_order = big_endian ? STL_BIG_ENDIAN : little_endian ? STL_LITTLE_ENDIAN : STL_UNKNOWN_BYTE_ORDER;
00098 
00099     FILE* file = open_file( file_name, overwrite, is_binary );
00100     if( !file ) return MB_FILE_DOES_NOT_EXIST;
00101 
00102     if( is_binary )
00103         rval = binary_write_triangles( file, header, byte_order, triangles );
00104     else
00105     {
00106         // Get precision for node coordinates
00107         int precision;
00108         if( MB_SUCCESS != opts.get_int_option( "PRECISION", precision ) ) precision = DEFAULT_PRECISION;
00109 
00110         rval = ascii_write_triangles( file, header, triangles, precision );
00111     }
00112 
00113     fclose( file );
00114     return rval;
00115 }
00116 
00117 FILE* WriteSTL::open_file( const char* name, bool overwrite, bool binary )
00118 {
00119     // Open file with write access, and create it if it doesn't exist.
00120     int flags = O_WRONLY | O_CREAT;
00121     // Select behavior if the named file already exists. If
00122     // overwrite is true, truncate the file. If it is false,
00123     // make the call to open() fail.
00124     if( overwrite )
00125         flags |= O_TRUNC;
00126     else
00127         flags |= O_EXCL;
00128         // If platform defines a "binary" bit in the file access
00129         // flags (i.e. we're building on windows), then set it
00130         // if we're writing a binary file.
00131 #ifdef O_BINARY
00132     if( binary ) flags |= O_BINARY;
00133 #endif
00134 
00135     // Give everyone read and write, but not execute, permission.
00136     // These are not the final permissions for the file. Permissions
00137     // are removed according to the user's umask. All we want to
00138     // say here is that the executable bits should not be set because
00139     // this isn't an executable file. Everything else is a user
00140     // preference and should be left up to the umask.
00141     int creat_mode = _S_IREAD | _S_IWRITE;
00142 
00143     // Open the file.
00144     int fd = open( name, flags, creat_mode );
00145     if( fd < 0 ) { MB_SET_ERR_RET_VAL( name << ": " << strerror( errno ), NULL ); }
00146     FILE* result = fdopen( fd, binary ? "wb" : "w" );
00147     if( !result ) close( fd );
00148 
00149     return result;
00150 }
00151 
00152 ErrorCode WriteSTL::make_header( char header[81], const std::vector< std::string >& qa_list )
00153 {
00154     memset( header, 0, 81 );
00155 
00156     std::string result;
00157     for( std::vector< std::string >::const_iterator i = qa_list.begin(); i != qa_list.end(); ++i )
00158     {
00159         result += " ";
00160         result += *i;
00161     }
00162 
00163     size_t len = result.size();
00164     if( len > 80 ) len = 80;
00165     memcpy( header, result.c_str(), len );
00166 
00167     return MB_SUCCESS;
00168 }
00169 
00170 ErrorCode WriteSTL::get_triangles( const EntityHandle* set_array, int set_array_length, Range& triangles )
00171 {
00172     if( !set_array || 0 == set_array_length ) return mbImpl->get_entities_by_type( 0, MBTRI, triangles );
00173 
00174     const EntityHandle* iter = set_array;
00175     const EntityHandle* end  = iter + set_array_length;
00176     for( ; iter != end; ++iter )
00177     {
00178         Range r;
00179         ErrorCode rval = mbImpl->get_entities_by_type( *iter, MBTRI, r, true );
00180         if( MB_SUCCESS != rval ) return rval;
00181         triangles.merge( r );
00182     }
00183 
00184     return MB_SUCCESS;
00185 }
00186 
00187 ErrorCode WriteSTL::get_triangle_data( const double coords[9], float v1[3], float v2[3], float v3[3], float n[3] )
00188 {
00189     CartVect cv1, cv2, cv3, cn;
00190     ErrorCode rval = get_triangle_data( coords, cv1, cv2, cv3, cn );
00191     if( MB_SUCCESS != rval ) return rval;
00192 
00193     cv1.get( v1 );
00194     cv2.get( v2 );
00195     cv3.get( v3 );
00196     cn.get( n );
00197 
00198     return MB_SUCCESS;
00199 }
00200 
00201 ErrorCode WriteSTL::get_triangle_data( const double coords[9], CartVect& v1, CartVect& v2, CartVect& v3, CartVect& n )
00202 {
00203     v1 = coords;
00204     v2 = coords + 3;
00205     v3 = coords + 6;
00206 
00207     n = ( v2 - v1 ) * ( v3 - v1 );
00208 
00209     n.normalize();
00210 
00211     return MB_SUCCESS;
00212 }
00213 
00214 ErrorCode WriteSTL::ascii_write_triangles( FILE* file, const char header[81], const Range& triangles, int prec )
00215 {
00216     const char solid_name[] = "MOAB";
00217 
00218     char myheader[81] = "solid ";
00219     strcat( myheader, solid_name );
00220     strncat( myheader, header, 80 );
00221 
00222     if( EOF == fputs( myheader, file ) || EOF == fputs( "\n", file ) ) return MB_FILE_WRITE_ERROR;
00223 
00224     ErrorCode rval;
00225     double coords[9];
00226     CartVect v1, v2, v3, n;
00227     for( Range::const_iterator iter = triangles.begin(); iter != triangles.end(); ++iter )
00228     {
00229         const EntityHandle* conn;
00230         int num_vtx;
00231 
00232         rval = mbImpl->get_connectivity( *iter, conn, num_vtx );
00233         if( MB_SUCCESS != rval ) return rval;
00234         if( num_vtx != 3 ) return MB_FAILURE;
00235 
00236         rval = mbImpl->get_coords( conn, 3, coords );
00237         if( MB_SUCCESS != rval ) return rval;
00238 
00239         rval = get_triangle_data( coords, v1, v2, v3, n );
00240         if( MB_SUCCESS != rval ) return rval;
00241 
00242         fprintf( file, "facet normal %e %e %e\n", n[0], n[1], n[2] );
00243         fprintf( file, "outer loop\n" );
00244         fprintf( file, "vertex %.*e %.*e %.*e\n", prec, (float)v1[0], prec, (float)v1[1], prec, (float)v1[2] );
00245         fprintf( file, "vertex %.*e %.*e %.*e\n", prec, (float)v2[0], prec, (float)v2[1], prec, (float)v2[2] );
00246         fprintf( file, "vertex %.*e %.*e %.*e\n", prec, (float)v3[0], prec, (float)v3[1], prec, (float)v3[2] );
00247         fprintf( file, "endloop\n" );
00248         fprintf( file, "endfacet\n" );
00249     }
00250 
00251     fprintf( file, "endsolid %s\n", solid_name );
00252     return MB_SUCCESS;
00253 }
00254 
00255 struct BinTri
00256 {
00257     float normal[3];
00258     float vertex1[3];
00259     float vertex2[3];
00260     float vertex3[3];
00261     char pad[2];
00262 };
00263 
00264 ErrorCode WriteSTL::binary_write_triangles( FILE* file, const char header[81], ByteOrder byte_order,
00265                                             const Range& triangles )
00266 {
00267     ErrorCode rval;
00268     if( 1 != fwrite( header, 80, 1, file ) ) return MB_FILE_WRITE_ERROR;
00269 
00270     // Default to little endian if byte_order == UNKNOWN_BYTE_ORDER
00271     const bool want_big_endian = ( byte_order == STL_BIG_ENDIAN );
00272     const bool am_big_endian   = !SysUtil::little_endian();
00273     const bool swap_bytes      = ( want_big_endian == am_big_endian );
00274 
00275     if( triangles.size() > INT_MAX )  // Can't write that many triangles
00276         return MB_FAILURE;
00277 
00278     uint32_t count = (uint32_t)triangles.size();
00279     if( swap_bytes ) SysUtil::byteswap( &count, 1 );
00280     if( 1 != fwrite( &count, 4, 1, file ) ) return MB_FILE_WRITE_ERROR;
00281 
00282     double coords[9];
00283     BinTri tri;
00284     tri.pad[0] = tri.pad[1] = '\0';
00285     for( Range::const_iterator iter = triangles.begin(); iter != triangles.end(); ++iter )
00286     {
00287         const EntityHandle* conn;
00288         int num_vtx;
00289 
00290         rval = mbImpl->get_connectivity( *iter, conn, num_vtx );
00291         if( MB_SUCCESS != rval ) return rval;
00292         if( num_vtx != 3 ) return MB_FAILURE;
00293 
00294         rval = mbImpl->get_coords( conn, 3, coords );
00295         if( MB_SUCCESS != rval ) return rval;
00296 
00297         rval = get_triangle_data( coords, tri.vertex1, tri.vertex2, tri.vertex3, tri.normal );
00298         if( MB_SUCCESS != rval ) return rval;
00299 
00300         if( swap_bytes )
00301         {
00302             SysUtil::byteswap( tri.normal, 3 );
00303             SysUtil::byteswap( tri.vertex1, 3 );
00304             SysUtil::byteswap( tri.vertex2, 3 );
00305             SysUtil::byteswap( tri.vertex3, 3 );
00306         }
00307 
00308         if( 1 != fwrite( &tri, 50, 1, file ) ) return MB_FILE_WRITE_ERROR;
00309     }
00310 
00311     return MB_SUCCESS;
00312 }
00313 
00314 }  // namespace moab
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines