Mesh Oriented datABase  (version 5.4.1)
Array-based unstructured mesh datastructure
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,
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 /* export_dimension */ )
00078 {
00079     char header[81];
00080     Range triangles;
00081     ErrorCode rval;
00082 
00083     if( tag_list && num_tags )
00084     {
00085         MB_SET_ERR( MB_TYPE_OUT_OF_RANGE, "STL file does not support tag data" );
00086     }
00087 
00088     rval = make_header( header, qa_list );
00089     if( MB_SUCCESS != rval ) return rval;
00090 
00091     rval = get_triangles( ent_handles, num_sets, triangles );
00092     if( MB_SUCCESS != rval ) return rval;
00093 
00094     if( triangles.empty() )
00095     {
00096         MB_SET_ERR( MB_ENTITY_NOT_FOUND, "No triangles to write" );
00097     }
00098 
00099     bool is_ascii = false, is_binary = false;
00100     if( MB_SUCCESS == opts.get_null_option( "ASCII" ) ) is_ascii = true;
00101     if( MB_SUCCESS == opts.get_null_option( "BINARY" ) ) is_binary = true;
00102     if( is_ascii && is_binary )
00103     {
00104         MB_SET_ERR( MB_FAILURE, "Conflicting options: BINARY ASCII" );
00105     }
00106 
00107     bool big_endian = false, little_endian = false;
00108     if( MB_SUCCESS == opts.get_null_option( "BIG_ENDIAN" ) ) big_endian = true;
00109     if( MB_SUCCESS == opts.get_null_option( "LITTLE_ENDIAN" ) ) little_endian = true;
00110     if( big_endian && little_endian )
00111     {
00112         MB_SET_ERR( MB_FAILURE, "Conflicting options: BIG_ENDIAN LITTLE_ENDIAN" );
00113     }
00114     ByteOrder byte_order = big_endian ? STL_BIG_ENDIAN : little_endian ? STL_LITTLE_ENDIAN : STL_UNKNOWN_BYTE_ORDER;
00115 
00116     FILE* file = open_file( file_name, overwrite, is_binary );
00117     if( !file ) return MB_FILE_DOES_NOT_EXIST;
00118 
00119     if( is_binary )
00120         rval = binary_write_triangles( file, header, byte_order, triangles );
00121     else
00122     {
00123         // Get precision for node coordinates
00124         int precision;
00125         if( MB_SUCCESS != opts.get_int_option( "PRECISION", precision ) ) precision = DEFAULT_PRECISION;
00126 
00127         rval = ascii_write_triangles( file, header, triangles, precision );
00128     }
00129 
00130     fclose( file );
00131     return rval;
00132 }
00133 
00134 FILE* WriteSTL::open_file( const char* name, bool overwrite, bool binary )
00135 {
00136     // Open file with write access, and create it if it doesn't exist.
00137     int flags = O_WRONLY | O_CREAT;
00138     // Select behavior if the named file already exists. If
00139     // overwrite is true, truncate the file. If it is false,
00140     // make the call to open() fail.
00141     if( overwrite )
00142         flags |= O_TRUNC;
00143     else
00144         flags |= O_EXCL;
00145         // If platform defines a "binary" bit in the file access
00146         // flags (i.e. we're building on windows), then set it
00147         // if we're writing a binary file.
00148 #ifdef O_BINARY
00149     if( binary ) flags |= O_BINARY;
00150 #endif
00151 
00152     // Give everyone read and write, but not execute, permission.
00153     // These are not the final permissions for the file. Permissions
00154     // are removed according to the user's umask. All we want to
00155     // say here is that the executable bits should not be set because
00156     // this isn't an executable file. Everything else is a user
00157     // preference and should be left up to the umask.
00158     int creat_mode = _S_IREAD | _S_IWRITE;
00159 
00160     // Open the file.
00161     int fd = open( name, flags, creat_mode );
00162     if( fd < 0 )
00163     {
00164         MB_SET_ERR_RET_VAL( name << ": " << strerror( errno ), NULL );
00165     }
00166     FILE* result = fdopen( fd, binary ? "wb" : "w" );
00167     if( !result ) close( fd );
00168 
00169     return result;
00170 }
00171 
00172 ErrorCode WriteSTL::make_header( char header[81], const std::vector< std::string >& qa_list )
00173 {
00174     memset( header, 0, 81 );
00175 
00176     std::string result;
00177     for( std::vector< std::string >::const_iterator i = qa_list.begin(); i != qa_list.end(); ++i )
00178     {
00179         result += " ";
00180         result += *i;
00181     }
00182 
00183     size_t len = result.size();
00184     if( len > 80 ) len = 80;
00185     memcpy( header, result.c_str(), len );
00186 
00187     return MB_SUCCESS;
00188 }
00189 
00190 ErrorCode WriteSTL::get_triangles( const EntityHandle* set_array, int set_array_length, Range& triangles )
00191 {
00192     if( !set_array || 0 == set_array_length ) return mbImpl->get_entities_by_type( 0, MBTRI, triangles );
00193 
00194     const EntityHandle* iter = set_array;
00195     const EntityHandle* end  = iter + set_array_length;
00196     for( ; iter != end; ++iter )
00197     {
00198         Range r;
00199         ErrorCode rval = mbImpl->get_entities_by_type( *iter, MBTRI, r, true );
00200         if( MB_SUCCESS != rval ) return rval;
00201         triangles.merge( r );
00202     }
00203 
00204     return MB_SUCCESS;
00205 }
00206 
00207 ErrorCode WriteSTL::get_triangle_data( const double coords[9], float v1[3], float v2[3], float v3[3], float n[3] )
00208 {
00209     CartVect cv1, cv2, cv3, cn;
00210     ErrorCode rval = get_triangle_data( coords, cv1, cv2, cv3, cn );
00211     if( MB_SUCCESS != rval ) return rval;
00212 
00213     cv1.get( v1 );
00214     cv2.get( v2 );
00215     cv3.get( v3 );
00216     cn.get( n );
00217 
00218     return MB_SUCCESS;
00219 }
00220 
00221 ErrorCode WriteSTL::get_triangle_data( const double coords[9], CartVect& v1, CartVect& v2, CartVect& v3, CartVect& n )
00222 {
00223     v1 = coords;
00224     v2 = coords + 3;
00225     v3 = coords + 6;
00226 
00227     n = ( v2 - v1 ) * ( v3 - v1 );
00228 
00229     n.normalize();
00230 
00231     return MB_SUCCESS;
00232 }
00233 
00234 ErrorCode WriteSTL::ascii_write_triangles( FILE* file, const char header[81], const Range& triangles, int prec )
00235 {
00236     const char solid_name[] = "MOAB";
00237 
00238     char myheader[81] = "solid ";
00239     strcat( myheader, solid_name );
00240     strncat( myheader, header, 80 );
00241 
00242     if( EOF == fputs( myheader, file ) || EOF == fputs( "\n", file ) ) return MB_FILE_WRITE_ERROR;
00243 
00244     ErrorCode rval;
00245     double coords[9];
00246     CartVect v1, v2, v3, n;
00247     for( Range::const_iterator iter = triangles.begin(); iter != triangles.end(); ++iter )
00248     {
00249         const EntityHandle* conn;
00250         int num_vtx;
00251 
00252         rval = mbImpl->get_connectivity( *iter, conn, num_vtx );
00253         if( MB_SUCCESS != rval ) return rval;
00254         if( num_vtx != 3 ) return MB_FAILURE;
00255 
00256         rval = mbImpl->get_coords( conn, 3, coords );
00257         if( MB_SUCCESS != rval ) return rval;
00258 
00259         rval = get_triangle_data( coords, v1, v2, v3, n );
00260         if( MB_SUCCESS != rval ) return rval;
00261 
00262         fprintf( file, "facet normal %e %e %e\n", n[0], n[1], n[2] );
00263         fprintf( file, "outer loop\n" );
00264         fprintf( file, "vertex %.*e %.*e %.*e\n", prec, (float)v1[0], prec, (float)v1[1], prec, (float)v1[2] );
00265         fprintf( file, "vertex %.*e %.*e %.*e\n", prec, (float)v2[0], prec, (float)v2[1], prec, (float)v2[2] );
00266         fprintf( file, "vertex %.*e %.*e %.*e\n", prec, (float)v3[0], prec, (float)v3[1], prec, (float)v3[2] );
00267         fprintf( file, "endloop\n" );
00268         fprintf( file, "endfacet\n" );
00269     }
00270 
00271     fprintf( file, "endsolid %s\n", solid_name );
00272     return MB_SUCCESS;
00273 }
00274 
00275 struct BinTri
00276 {
00277     float normal[3];
00278     float vertex1[3];
00279     float vertex2[3];
00280     float vertex3[3];
00281     char pad[2];
00282 };
00283 
00284 ErrorCode WriteSTL::binary_write_triangles( FILE* file,
00285                                             const char header[81],
00286                                             ByteOrder byte_order,
00287                                             const Range& triangles )
00288 {
00289     ErrorCode rval;
00290     if( 1 != fwrite( header, 80, 1, file ) ) return MB_FILE_WRITE_ERROR;
00291 
00292     // Default to little endian if byte_order == UNKNOWN_BYTE_ORDER
00293     const bool want_big_endian = ( byte_order == STL_BIG_ENDIAN );
00294     const bool am_big_endian   = !SysUtil::little_endian();
00295     const bool swap_bytes      = ( want_big_endian == am_big_endian );
00296 
00297     if( triangles.size() > INT_MAX )  // Can't write that many triangles
00298         return MB_FAILURE;
00299 
00300     uint32_t count = (uint32_t)triangles.size();
00301     if( swap_bytes ) SysUtil::byteswap( &count, 1 );
00302     if( 1 != fwrite( &count, 4, 1, file ) ) return MB_FILE_WRITE_ERROR;
00303 
00304     double coords[9];
00305     BinTri tri;
00306     tri.pad[0] = tri.pad[1] = '\0';
00307     for( Range::const_iterator iter = triangles.begin(); iter != triangles.end(); ++iter )
00308     {
00309         const EntityHandle* conn;
00310         int num_vtx;
00311 
00312         rval = mbImpl->get_connectivity( *iter, conn, num_vtx );
00313         if( MB_SUCCESS != rval ) return rval;
00314         if( num_vtx != 3 ) return MB_FAILURE;
00315 
00316         rval = mbImpl->get_coords( conn, 3, coords );
00317         if( MB_SUCCESS != rval ) return rval;
00318 
00319         rval = get_triangle_data( coords, tri.vertex1, tri.vertex2, tri.vertex3, tri.normal );
00320         if( MB_SUCCESS != rval ) return rval;
00321 
00322         if( swap_bytes )
00323         {
00324             SysUtil::byteswap( tri.normal, 3 );
00325             SysUtil::byteswap( tri.vertex1, 3 );
00326             SysUtil::byteswap( tri.vertex2, 3 );
00327             SysUtil::byteswap( tri.vertex3, 3 );
00328         }
00329 
00330         if( 1 != fwrite( &tri, 50, 1, file ) ) return MB_FILE_WRITE_ERROR;
00331     }
00332 
00333     return MB_SUCCESS;
00334 }
00335 
00336 }  // namespace moab
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines