moab
|
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