MOAB: Mesh Oriented datABase
(version 5.4.1)
|
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