moab
WriteSTL.cpp
Go to the documentation of this file.
00001 
00023 #include "WriteSTL.hpp"
00024 #include "moab/CN.hpp"
00025 #include "moab/Interface.hpp"
00026 #include "moab/Range.hpp"
00027 #include "moab/WriteUtilIface.hpp"
00028 #include "moab/FileOptions.hpp"
00029 #include "SysUtil.hpp"
00030 
00031 #include <stdio.h>
00032 #include <sys/types.h>
00033 #include <sys/stat.h>
00034 #include <errno.h>
00035 #include <math.h>
00036 #include <fcntl.h>
00037 #include <limits.h>
00038 
00039 namespace moab {
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   { return new WriteSTL( iface ); }
00056 
00057 WriteSTL::WriteSTL(Interface *impl) 
00058     : mbImpl(impl)
00059 {
00060   impl->query_interface( mWriteIface );
00061 }
00062 
00063 WriteSTL::~WriteSTL() 
00064 {
00065   mbImpl->release_interface(mWriteIface);
00066 }
00067 
00068 
00069 ErrorCode WriteSTL::write_file(const char *file_name, 
00070                                  const bool overwrite,
00071                                  const FileOptions& opts,
00072                                  const EntityHandle *ent_handles,
00073                                  const int num_sets,
00074                                  const std::vector<std::string>& qa_list, 
00075                                  const Tag* tag_list,
00076                                  int num_tags,
00077                                  int  )
00078 {
00079   char header[81];
00080   Range triangles;
00081   ErrorCode rval;
00082   
00083   if (tag_list && num_tags) {
00084     mWriteIface->report_error( "STL file does not support tag data\n" );
00085     return MB_TYPE_OUT_OF_RANGE;
00086   }
00087   
00088   rval = make_header( header, qa_list );
00089   if (MB_SUCCESS != rval)
00090     return rval;
00091   
00092   rval = get_triangles( ent_handles, num_sets, triangles );
00093   if (MB_SUCCESS != rval)
00094     return rval;
00095 
00096   if (triangles.empty()) {
00097     mWriteIface->report_error( "No triangles to write." );
00098     return MB_ENTITY_NOT_FOUND;
00099   }
00100  
00101   bool is_ascii = false, is_binary = false;
00102   if (MB_SUCCESS == opts.get_null_option( "ASCII" ))
00103     is_ascii = true;
00104   if (MB_SUCCESS == opts.get_null_option( "BINARY" ))
00105     is_binary = true;
00106   if (is_ascii && is_binary) {
00107     mWriteIface->report_error( "Conflicting options: BINARY ASCII\n" );
00108     return MB_FAILURE;
00109   }
00110   
00111   bool big_endian = false, little_endian = false;
00112   if (MB_SUCCESS == opts.get_null_option( "BIG_ENDIAN" ))
00113     big_endian = true;
00114   if (MB_SUCCESS == opts.get_null_option( "LITTLE_ENDIAN" ))
00115     little_endian = true;
00116   if (big_endian && little_endian) {
00117     mWriteIface->report_error( "Conflicting options: BIG_ENDIAN LITTLE_ENDIAN\n" );
00118     return MB_FAILURE;
00119   }
00120   ByteOrder byte_order = big_endian ? STL_BIG_ENDIAN : little_endian ? STL_LITTLE_ENDIAN : STL_UNKNOWN_BYTE_ORDER;
00121     
00122   FILE* file = open_file( file_name, overwrite, is_binary );
00123   if (!file)
00124     return MB_FILE_DOES_NOT_EXIST; 
00125   
00126   if (is_binary)
00127     rval = binary_write_triangles( file, header, byte_order, triangles );
00128   else {
00129       // Get precision for node coordinates
00130     int precision;
00131     if (MB_SUCCESS != opts.get_int_option( "PRECISION", precision ))
00132       precision = DEFAULT_PRECISION;
00133 
00134     rval = ascii_write_triangles( file, header, triangles, precision );
00135   }
00136   fclose( file );
00137   return rval;
00138 }
00139 
00140 
00141 FILE* WriteSTL::open_file( const char* name, bool overwrite, bool binary )
00142 {
00143     // Open file with write access, and create it if it doesn't exist.
00144   int flags = O_WRONLY|O_CREAT;
00145     // Select behavior if the named file already exists.  If 
00146     // overwrite is true, truncate the file.  If it is false,
00147     // make the call to open() fail.
00148   if (overwrite)
00149     flags |= O_TRUNC;
00150   else
00151     flags |= O_EXCL;
00152     // If platform defines a "binary" bit in the file access
00153     // flags (i.e. we're building on windows), then set it
00154     // if we're writing a binary file.
00155 #ifdef O_BINARY
00156   if (binary)
00157     flags |= O_BINARY;
00158 #endif
00159 
00160     // Give everyone read and write, but not execute, permision.
00161     // These are not the final permisions for the file.  Permissions
00162     // are removed according to the user's umask.  All we want to
00163     // say here is that the executable bits should not be set because
00164     // this isn't an executable file.  Everything else is a user
00165     // preference and should be left up to the umask.
00166   int creat_mode = _S_IREAD|_S_IWRITE;
00167 
00168     // Open the file.
00169   int fd = open( name, flags, creat_mode );
00170   if (fd < 0)
00171   {
00172     mWriteIface->report_error( "%s: %s\n", name, strerror(errno) );
00173     return 0;
00174   }
00175   FILE* result = fdopen( fd, binary ? "wb": "w" );
00176   if (!result)
00177     close( fd );
00178   
00179   return result;
00180 }
00181 
00182 ErrorCode WriteSTL::make_header( char header[81], 
00183                                    const std::vector<std::string>& qa_list )
00184 {
00185   memset( header, 0, 81 );
00186   
00187   std::string result;
00188   for (std::vector<std::string>::const_iterator i = qa_list.begin(); i != qa_list.end(); ++i)
00189   {
00190     result += " ";
00191     result += *i;
00192   }
00193   
00194   size_t len = result.size();
00195   if (len > 80)
00196     len = 80;
00197   memcpy( header, result.c_str(), len );
00198   
00199   return MB_SUCCESS;
00200 }
00201 
00202 ErrorCode WriteSTL::get_triangles( const EntityHandle* set_array,
00203                                      int set_array_length,
00204                                      Range& triangles )
00205 {
00206   if (!set_array || set_array_length == 0)
00207   {
00208     return mbImpl->get_entities_by_type( 0, MBTRI, triangles );
00209   }
00210   
00211   const EntityHandle* iter = set_array;
00212   const EntityHandle* end = iter + set_array_length;
00213   for (; iter != end; ++iter)
00214   {
00215     Range r;
00216     ErrorCode rval = mbImpl->get_entities_by_type( *iter, MBTRI, r, true );
00217     if (MB_SUCCESS != rval)
00218       return rval;
00219     triangles.merge( r );
00220   }
00221   
00222   return MB_SUCCESS;
00223 }
00224 
00225 ErrorCode WriteSTL::get_triangle_data( const double coords[9],
00226                                          float v1[3],
00227                                          float v2[3],
00228                                          float v3[3],
00229                                          float n[3] )
00230 {
00231   float e1[3], e2[3];
00232   v1[0] = (float)coords[0];
00233   v1[1] = (float)coords[1];
00234   v1[2] = (float)coords[2];
00235   v2[0] = (float)coords[3];
00236   v2[1] = (float)coords[4];
00237   v2[2] = (float)coords[5];
00238   v3[0] = (float)coords[6];
00239   v3[1] = (float)coords[7];
00240   v3[2] = (float)coords[8];
00241   e1[0] = v2[0] - v1[0];
00242   e1[1] = v2[1] - v1[1];
00243   e1[2] = v2[2] - v1[2];
00244   e2[0] = v3[0] - v1[0];
00245   e2[1] = v3[1] - v1[1];
00246   e2[2] = v3[2] - v1[2];
00247   n[0] = e1[1]*e2[2] - e1[2]*e2[1];
00248   n[1] = e1[2]*e2[0] - e1[0]*e2[2];
00249   n[2] = e1[0]*e2[1] - e1[1]*e2[0];
00250   float inv_len = 1.0f / (float)sqrt( n[0]*n[0] + n[1]*n[1] + n[2]*n[2] );
00251   n[0] *= inv_len;
00252   n[1] *= inv_len;
00253   n[2] *= inv_len;
00254   return MB_SUCCESS;
00255 }
00256 
00257 
00258 ErrorCode WriteSTL::ascii_write_triangles( FILE* file,
00259                                              const char header[81],
00260                                              const Range& triangles,
00261                                              int prec )
00262 {
00263   const char solid_name[] = "MOAB";
00264   
00265   char myheader[81] = "solid ";
00266   strcat( myheader, solid_name );
00267   strncat( myheader, header, 80 );
00268   
00269   if (EOF == fputs( myheader, file ) || EOF == fputs( "\n", file ))
00270     return MB_FILE_WRITE_ERROR;
00271   
00272   ErrorCode rval;
00273   double coords[9];
00274   float v1[3], v2[3], v3[3];
00275   float n[3];
00276   for (Range::const_iterator iter = triangles.begin();
00277        iter != triangles.end(); ++iter)
00278   {
00279     const EntityHandle* conn;
00280     int num_vtx;
00281     
00282     rval = mbImpl->get_connectivity( *iter, conn, num_vtx );
00283     if (MB_SUCCESS != rval)
00284       return rval;
00285     if (num_vtx != 3)
00286       return MB_FAILURE;
00287     
00288     rval = mbImpl->get_coords( conn, 3, coords );
00289     if (MB_SUCCESS != rval)
00290       return rval;
00291     
00292     rval = get_triangle_data( coords, v1, v2, v3, n );
00293     if (MB_SUCCESS != rval)
00294       return rval;
00295    
00296     fprintf( file,"facet normal %e %e %e\n", n[0], n[1], n[2] );
00297     fprintf( file,"outer loop\n" );
00298     fprintf( file,"vertex %.*e %.*e %.*e\n", prec, v1[0], prec, v1[1], prec, v1[2] );
00299     fprintf( file,"vertex %.*e %.*e %.*e\n", prec, v2[0], prec, v2[1], prec, v2[2] );
00300     fprintf( file,"vertex %.*e %.*e %.*e\n", prec, v3[0], prec, v3[1], prec, v3[2] );
00301     fprintf( file,"endloop\n" );
00302     fprintf( file,"endfacet\n" );
00303   }
00304   
00305   fprintf( file,"endsolid %s\n", solid_name );
00306   return MB_SUCCESS;
00307 }
00308 
00309 struct BinTri
00310 {
00311   float normal[3];
00312   float vertex1[3];
00313   float vertex2[3];
00314   float vertex3[3];
00315   char pad[2];
00316 };
00317 
00318 ErrorCode WriteSTL::binary_write_triangles( FILE* file,
00319                                              const char header[81],
00320                                              ByteOrder byte_order,
00321                                              const Range& triangles )
00322 {
00323   ErrorCode rval;
00324   if (fwrite( header, 80, 1, file ) != 1)
00325     return MB_FILE_WRITE_ERROR;
00326   
00327     // default to little endian if byte_order == UNKNOWN_BYTE_ORDER
00328   const bool want_big_endian = (byte_order == STL_BIG_ENDIAN);
00329   const bool am_big_endian = !SysUtil::little_endian();
00330   const bool swap_bytes = (want_big_endian == am_big_endian);
00331     
00332   if (triangles.size() > INT_MAX) // can't write that many triangles
00333     return MB_FAILURE;  
00334   
00335   uint32_t count = (uint32_t)triangles.size();
00336   if (swap_bytes)
00337     SysUtil::byteswap(&count, 1);
00338   if (fwrite( &count, 4, 1, file ) != 1)
00339     return MB_FILE_WRITE_ERROR;
00340 
00341   double coords[9];
00342   BinTri tri;
00343   tri.pad[0] = tri.pad[1] = '\0';
00344   for (Range::const_iterator iter = triangles.begin();
00345        iter != triangles.end(); ++iter)
00346   {
00347     const EntityHandle* conn;
00348     int num_vtx;
00349     
00350     rval = mbImpl->get_connectivity( *iter, conn, num_vtx );
00351     if (MB_SUCCESS != rval)
00352       return rval;
00353     if (num_vtx != 3)
00354       return MB_FAILURE;
00355     
00356     rval = mbImpl->get_coords( conn, 3, coords );
00357     if (MB_SUCCESS != rval)
00358       return rval;
00359     
00360     rval = get_triangle_data( coords, tri.vertex1, tri.vertex2, tri.vertex3, tri.normal );
00361     if (MB_SUCCESS != rval)
00362       return rval;
00363     
00364     if (swap_bytes)
00365     {
00366       SysUtil::byteswap( tri.normal, 3 );
00367       SysUtil::byteswap( tri.vertex1, 3 );
00368       SysUtil::byteswap( tri.vertex2, 3 );
00369       SysUtil::byteswap( tri.vertex3, 3 );
00370     }
00371    
00372     if (1 != fwrite( &tri, 50, 1, file ))
00373       return MB_FILE_WRITE_ERROR;
00374   }
00375   
00376   return MB_SUCCESS;
00377 }
00378 
00379 } // namespace moab
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines