1
15
16
21
22 #include "WriteSTL.hpp"
23 #include "moab/CN.hpp"
24 #include "moab/Interface.hpp"
25 #include "moab/Range.hpp"
26 #include "moab/WriteUtilIface.hpp"
27 #include "moab/FileOptions.hpp"
28 #include "SysUtil.hpp"
29
30 #include <cstdio>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <cerrno>
34 #include <cmath>
35 #include <fcntl.h>
36 #include <climits>
37
38 namespace moab
39 {
40
41 #if defined( _MSC_VER ) || defined( __MINGW32__ )
42 #include <io.h>
43 #ifndef __MINGW32__
44 typedef unsigned __int32 uint32_t;
45 #endif
46 #else
47 #include <unistd.h>
48 #define _S_IREAD ( S_IRUSR | S_IRGRP | S_IROTH )
49 #define _S_IWRITE ( S_IWUSR | S_IWGRP | S_IWOTH )
50 #endif
51
52 const int DEFAULT_PRECISION = 6;
53
54 WriterIface* WriteSTL::factory( Interface* iface )
55 {
56 return new WriteSTL( iface );
57 }
58
59 WriteSTL::WriteSTL( Interface* impl ) : mbImpl( impl )
60 {
61 impl->query_interface( mWriteIface );
62 }
63
64 WriteSTL::~WriteSTL()
65 {
66 mbImpl->release_interface( mWriteIface );
67 }
68
69 ErrorCode WriteSTL::write_file( const char* file_name,
70 const bool overwrite,
71 const FileOptions& opts,
72 const EntityHandle* ent_handles,
73 const int num_sets,
74 const std::vector< std::string >& qa_list,
75 const Tag* tag_list,
76 int num_tags,
77 int )
78 {
79 char header[81];
80 Range triangles;
81 ErrorCode rval;
82
83 if( tag_list && num_tags )
84 {
85 MB_SET_ERR( MB_TYPE_OUT_OF_RANGE, "STL file does not support tag data" );
86 }
87
88 rval = make_header( header, qa_list );
89 if( MB_SUCCESS != rval ) return rval;
90
91 rval = get_triangles( ent_handles, num_sets, triangles );
92 if( MB_SUCCESS != rval ) return rval;
93
94 if( triangles.empty() )
95 {
96 MB_SET_ERR( MB_ENTITY_NOT_FOUND, "No triangles to write" );
97 }
98
99 bool is_ascii = false, is_binary = false;
100 if( MB_SUCCESS == opts.get_null_option( "ASCII" ) ) is_ascii = true;
101 if( MB_SUCCESS == opts.get_null_option( "BINARY" ) ) is_binary = true;
102 if( is_ascii && is_binary )
103 {
104 MB_SET_ERR( MB_FAILURE, "Conflicting options: BINARY ASCII" );
105 }
106
107 bool big_endian = false, little_endian = false;
108 if( MB_SUCCESS == opts.get_null_option( "BIG_ENDIAN" ) ) big_endian = true;
109 if( MB_SUCCESS == opts.get_null_option( "LITTLE_ENDIAN" ) ) little_endian = true;
110 if( big_endian && little_endian )
111 {
112 MB_SET_ERR( MB_FAILURE, "Conflicting options: BIG_ENDIAN LITTLE_ENDIAN" );
113 }
114 ByteOrder byte_order = big_endian ? STL_BIG_ENDIAN : little_endian ? STL_LITTLE_ENDIAN : STL_UNKNOWN_BYTE_ORDER;
115
116 FILE* file = open_file( file_name, overwrite, is_binary );
117 if( !file ) return MB_FILE_DOES_NOT_EXIST;
118
119 if( is_binary )
120 rval = binary_write_triangles( file, header, byte_order, triangles );
121 else
122 {
123
124 int precision;
125 if( MB_SUCCESS != opts.get_int_option( "PRECISION", precision ) ) precision = DEFAULT_PRECISION;
126
127 rval = ascii_write_triangles( file, header, triangles, precision );
128 }
129
130 fclose( file );
131 return rval;
132 }
133
134 FILE* WriteSTL::open_file( const char* name, bool overwrite, bool binary )
135 {
136
137 int flags = O_WRONLY | O_CREAT;
138
139
140
141 if( overwrite )
142 flags |= O_TRUNC;
143 else
144 flags |= O_EXCL;
145
146
147
148 #ifdef O_BINARY
149 if( binary ) flags |= O_BINARY;
150 #endif
151
152
153
154
155
156
157
158 int creat_mode = _S_IREAD | _S_IWRITE;
159
160
161 int fd = open( name, flags, creat_mode );
162 if( fd < 0 )
163 {
164 MB_SET_ERR_RET_VAL( name << ": " << strerror( errno ), NULL );
165 }
166 FILE* result = fdopen( fd, binary ? "wb" : "w" );
167 if( !result ) close( fd );
168
169 return result;
170 }
171
172 ErrorCode WriteSTL::make_header( char header[81], const std::vector< std::string >& qa_list )
173 {
174 memset( header, 0, 81 );
175
176 std::string result;
177 for( std::vector< std::string >::const_iterator i = qa_list.begin(); i != qa_list.end(); ++i )
178 {
179 result += " ";
180 result += *i;
181 }
182
183 size_t len = result.size();
184 if( len > 80 ) len = 80;
185 memcpy( header, result.c_str(), len );
186
187 return MB_SUCCESS;
188 }
189
190 ErrorCode WriteSTL::get_triangles( const EntityHandle* set_array, int set_array_length, Range& triangles )
191 {
192 if( !set_array || 0 == set_array_length ) return mbImpl->get_entities_by_type( 0, MBTRI, triangles );
193
194 const EntityHandle* iter = set_array;
195 const EntityHandle* end = iter + set_array_length;
196 for( ; iter != end; ++iter )
197 {
198 Range r;
199 ErrorCode rval = mbImpl->get_entities_by_type( *iter, MBTRI, r, true );
200 if( MB_SUCCESS != rval ) return rval;
201 triangles.merge( r );
202 }
203
204 return MB_SUCCESS;
205 }
206
207 ErrorCode WriteSTL::get_triangle_data( const double coords[9], float v1[3], float v2[3], float v3[3], float n[3] )
208 {
209 CartVect cv1, cv2, cv3, cn;
210 ErrorCode rval = get_triangle_data( coords, cv1, cv2, cv3, cn );
211 if( MB_SUCCESS != rval ) return rval;
212
213 cv1.get( v1 );
214 cv2.get( v2 );
215 cv3.get( v3 );
216 cn.get( n );
217
218 return MB_SUCCESS;
219 }
220
221 ErrorCode WriteSTL::get_triangle_data( const double coords[9], CartVect& v1, CartVect& v2, CartVect& v3, CartVect& n )
222 {
223 v1 = coords;
224 v2 = coords + 3;
225 v3 = coords + 6;
226
227 n = ( v2 - v1 ) * ( v3 - v1 );
228
229 n.normalize();
230
231 return MB_SUCCESS;
232 }
233
234 ErrorCode WriteSTL::ascii_write_triangles( FILE* file, const char header[81], const Range& triangles, int prec )
235 {
236 const char solid_name[] = "MOAB";
237
238 char myheader[81] = "solid ";
239 strcat( myheader, solid_name );
240 strncat( myheader, header, 80 );
241
242 if( EOF == fputs( myheader, file ) || EOF == fputs( "\n", file ) ) return MB_FILE_WRITE_ERROR;
243
244 ErrorCode rval;
245 double coords[9];
246 CartVect v1, v2, v3, n;
247 for( Range::const_iterator iter = triangles.begin(); iter != triangles.end(); ++iter )
248 {
249 const EntityHandle* conn;
250 int num_vtx;
251
252 rval = mbImpl->get_connectivity( *iter, conn, num_vtx );
253 if( MB_SUCCESS != rval ) return rval;
254 if( num_vtx != 3 ) return MB_FAILURE;
255
256 rval = mbImpl->get_coords( conn, 3, coords );
257 if( MB_SUCCESS != rval ) return rval;
258
259 rval = get_triangle_data( coords, v1, v2, v3, n );
260 if( MB_SUCCESS != rval ) return rval;
261
262 fprintf( file, "facet normal %e %e %e\n", n[0], n[1], n[2] );
263 fprintf( file, "outer loop\n" );
264 fprintf( file, "vertex %.*e %.*e %.*e\n", prec, (float)v1[0], prec, (float)v1[1], prec, (float)v1[2] );
265 fprintf( file, "vertex %.*e %.*e %.*e\n", prec, (float)v2[0], prec, (float)v2[1], prec, (float)v2[2] );
266 fprintf( file, "vertex %.*e %.*e %.*e\n", prec, (float)v3[0], prec, (float)v3[1], prec, (float)v3[2] );
267 fprintf( file, "endloop\n" );
268 fprintf( file, "endfacet\n" );
269 }
270
271 fprintf( file, "endsolid %s\n", solid_name );
272 return MB_SUCCESS;
273 }
274
275 struct BinTri
276 {
277 float normal[3];
278 float vertex1[3];
279 float vertex2[3];
280 float vertex3[3];
281 char pad[2];
282 };
283
284 ErrorCode WriteSTL::binary_write_triangles( FILE* file,
285 const char header[81],
286 ByteOrder byte_order,
287 const Range& triangles )
288 {
289 ErrorCode rval;
290 if( 1 != fwrite( header, 80, 1, file ) ) return MB_FILE_WRITE_ERROR;
291
292
293 const bool want_big_endian = ( byte_order == STL_BIG_ENDIAN );
294 const bool am_big_endian = !SysUtil::little_endian();
295 const bool swap_bytes = ( want_big_endian == am_big_endian );
296
297 if( triangles.size() > INT_MAX )
298 return MB_FAILURE;
299
300 uint32_t count = (uint32_t)triangles.size();
301 if( swap_bytes ) SysUtil::byteswap( &count, 1 );
302 if( 1 != fwrite( &count, 4, 1, file ) ) return MB_FILE_WRITE_ERROR;
303
304 double coords[9];
305 BinTri tri;
306 tri.pad[0] = tri.pad[1] = '\0';
307 for( Range::const_iterator iter = triangles.begin(); iter != triangles.end(); ++iter )
308 {
309 const EntityHandle* conn;
310 int num_vtx;
311
312 rval = mbImpl->get_connectivity( *iter, conn, num_vtx );
313 if( MB_SUCCESS != rval ) return rval;
314 if( num_vtx != 3 ) return MB_FAILURE;
315
316 rval = mbImpl->get_coords( conn, 3, coords );
317 if( MB_SUCCESS != rval ) return rval;
318
319 rval = get_triangle_data( coords, tri.vertex1, tri.vertex2, tri.vertex3, tri.normal );
320 if( MB_SUCCESS != rval ) return rval;
321
322 if( swap_bytes )
323 {
324 SysUtil::byteswap( tri.normal, 3 );
325 SysUtil::byteswap( tri.vertex1, 3 );
326 SysUtil::byteswap( tri.vertex2, 3 );
327 SysUtil::byteswap( tri.vertex3, 3 );
328 }
329
330 if( 1 != fwrite( &tri, 50, 1, file ) ) return MB_FILE_WRITE_ERROR;
331 }
332
333 return MB_SUCCESS;
334 }
335
336 }