MOAB: Mesh Oriented datABase
(version 5.3.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, 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