Branch data Line data Source code
1 : : /**
2 : : * MOAB, a Mesh-Oriented datABase, is a software component for creating,
3 : : * storing and accessing finite element mesh data.
4 : : *
5 : : * Copyright 2004 Sandia Corporation. Under the terms of Contract
6 : : * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government
7 : : * retains certain rights in this software.
8 : : *
9 : : * This library is free software; you can redistribute it and/or
10 : : * modify it under the terms of the GNU Lesser General Public
11 : : * License as published by the Free Software Foundation; either
12 : : * version 2.1 of the License, or (at your option) any later version.
13 : : *
14 : : */
15 : :
16 : : /**
17 : : * \class WriteSTL
18 : : * \brief ASCII and Binary Stereo Lithography File writers.
19 : : * \author Jason Kraftcheck
20 : : */
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 <stdio.h>
31 : : #include <sys/types.h>
32 : : #include <sys/stat.h>
33 : : #include <errno.h>
34 : : #include <math.h>
35 : : #include <fcntl.h>
36 : : #include <limits.h>
37 : :
38 : : namespace moab
39 : : {
40 : :
41 : : #if defined( _MSC_VER ) || defined( __MINGW32__ ) /* windows */
42 : : #include <io.h>
43 : : #ifndef __MINGW32__
44 : : typedef unsigned __int32 uint32_t;
45 : : #endif
46 : : #else /* posix */
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 : 0 : WriterIface* WriteSTL::factory( Interface* iface )
55 : : {
56 [ # # ]: 0 : return new WriteSTL( iface );
57 : : }
58 : :
59 : 0 : WriteSTL::WriteSTL( Interface* impl ) : mbImpl( impl )
60 : : {
61 [ # # ]: 0 : impl->query_interface( mWriteIface );
62 : 0 : }
63 : :
64 : 0 : WriteSTL::~WriteSTL()
65 : : {
66 : 0 : mbImpl->release_interface( mWriteIface );
67 [ # # ]: 0 : }
68 : :
69 : 0 : ErrorCode WriteSTL::write_file( const char* file_name, const bool overwrite, const FileOptions& opts,
70 : : const EntityHandle* ent_handles, const int num_sets,
71 : : const std::vector< std::string >& qa_list, const Tag* tag_list, int num_tags,
72 : : int /* export_dimension */ )
73 : : {
74 : : char header[81];
75 [ # # ]: 0 : Range triangles;
76 : : ErrorCode rval;
77 : :
78 [ # # ][ # # ]: 0 : if( tag_list && num_tags ) { MB_SET_ERR( MB_TYPE_OUT_OF_RANGE, "STL file does not support tag data" ); }
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
79 : :
80 [ # # ]: 0 : rval = make_header( header, qa_list );
81 [ # # ]: 0 : if( MB_SUCCESS != rval ) return rval;
82 : :
83 [ # # ]: 0 : rval = get_triangles( ent_handles, num_sets, triangles );
84 [ # # ]: 0 : if( MB_SUCCESS != rval ) return rval;
85 : :
86 [ # # ][ # # ]: 0 : if( triangles.empty() ) { MB_SET_ERR( MB_ENTITY_NOT_FOUND, "No triangles to write" ); }
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
87 : :
88 : 0 : bool is_ascii = false, is_binary = false;
89 [ # # ][ # # ]: 0 : if( MB_SUCCESS == opts.get_null_option( "ASCII" ) ) is_ascii = true;
90 [ # # ][ # # ]: 0 : if( MB_SUCCESS == opts.get_null_option( "BINARY" ) ) is_binary = true;
91 [ # # ][ # # ]: 0 : if( is_ascii && is_binary ) { MB_SET_ERR( MB_FAILURE, "Conflicting options: BINARY ASCII" ); }
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
92 : :
93 : 0 : bool big_endian = false, little_endian = false;
94 [ # # ][ # # ]: 0 : if( MB_SUCCESS == opts.get_null_option( "BIG_ENDIAN" ) ) big_endian = true;
95 [ # # ][ # # ]: 0 : if( MB_SUCCESS == opts.get_null_option( "LITTLE_ENDIAN" ) ) little_endian = true;
96 [ # # ][ # # ]: 0 : if( big_endian && little_endian ) { MB_SET_ERR( MB_FAILURE, "Conflicting options: BIG_ENDIAN LITTLE_ENDIAN" ); }
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
97 [ # # ][ # # ]: 0 : ByteOrder byte_order = big_endian ? STL_BIG_ENDIAN : little_endian ? STL_LITTLE_ENDIAN : STL_UNKNOWN_BYTE_ORDER;
98 : :
99 [ # # ]: 0 : FILE* file = open_file( file_name, overwrite, is_binary );
100 [ # # ]: 0 : if( !file ) return MB_FILE_DOES_NOT_EXIST;
101 : :
102 [ # # ]: 0 : if( is_binary )
103 [ # # ]: 0 : rval = binary_write_triangles( file, header, byte_order, triangles );
104 : : else
105 : : {
106 : : // Get precision for node coordinates
107 : : int precision;
108 [ # # ][ # # ]: 0 : if( MB_SUCCESS != opts.get_int_option( "PRECISION", precision ) ) precision = DEFAULT_PRECISION;
109 : :
110 [ # # ]: 0 : rval = ascii_write_triangles( file, header, triangles, precision );
111 : : }
112 : :
113 [ # # ]: 0 : fclose( file );
114 : 0 : return rval;
115 : : }
116 : :
117 : 0 : FILE* WriteSTL::open_file( const char* name, bool overwrite, bool binary )
118 : : {
119 : : // Open file with write access, and create it if it doesn't exist.
120 : 0 : int flags = O_WRONLY | O_CREAT;
121 : : // Select behavior if the named file already exists. If
122 : : // overwrite is true, truncate the file. If it is false,
123 : : // make the call to open() fail.
124 [ # # ]: 0 : if( overwrite )
125 : 0 : flags |= O_TRUNC;
126 : : else
127 : 0 : flags |= O_EXCL;
128 : : // If platform defines a "binary" bit in the file access
129 : : // flags (i.e. we're building on windows), then set it
130 : : // if we're writing a binary file.
131 : : #ifdef O_BINARY
132 : : if( binary ) flags |= O_BINARY;
133 : : #endif
134 : :
135 : : // Give everyone read and write, but not execute, permission.
136 : : // These are not the final permissions for the file. Permissions
137 : : // are removed according to the user's umask. All we want to
138 : : // say here is that the executable bits should not be set because
139 : : // this isn't an executable file. Everything else is a user
140 : : // preference and should be left up to the umask.
141 : 0 : int creat_mode = _S_IREAD | _S_IWRITE;
142 : :
143 : : // Open the file.
144 : 0 : int fd = open( name, flags, creat_mode );
145 [ # # ][ # # ]: 0 : if( fd < 0 ) { MB_SET_ERR_RET_VAL( name << ": " << strerror( errno ), NULL ); }
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
146 [ # # ]: 0 : FILE* result = fdopen( fd, binary ? "wb" : "w" );
147 [ # # ]: 0 : if( !result ) close( fd );
148 : :
149 : 0 : return result;
150 : : }
151 : :
152 : 0 : ErrorCode WriteSTL::make_header( char header[81], const std::vector< std::string >& qa_list )
153 : : {
154 : 0 : memset( header, 0, 81 );
155 : :
156 [ # # ]: 0 : std::string result;
157 [ # # ][ # # ]: 0 : for( std::vector< std::string >::const_iterator i = qa_list.begin(); i != qa_list.end(); ++i )
[ # # ]
158 : : {
159 [ # # ]: 0 : result += " ";
160 [ # # ][ # # ]: 0 : result += *i;
161 : : }
162 : :
163 : 0 : size_t len = result.size();
164 [ # # ]: 0 : if( len > 80 ) len = 80;
165 : 0 : memcpy( header, result.c_str(), len );
166 : :
167 : 0 : return MB_SUCCESS;
168 : : }
169 : :
170 : 0 : ErrorCode WriteSTL::get_triangles( const EntityHandle* set_array, int set_array_length, Range& triangles )
171 : : {
172 [ # # ][ # # ]: 0 : if( !set_array || 0 == set_array_length ) return mbImpl->get_entities_by_type( 0, MBTRI, triangles );
173 : :
174 : 0 : const EntityHandle* iter = set_array;
175 : 0 : const EntityHandle* end = iter + set_array_length;
176 [ # # ]: 0 : for( ; iter != end; ++iter )
177 : : {
178 [ # # ]: 0 : Range r;
179 [ # # ]: 0 : ErrorCode rval = mbImpl->get_entities_by_type( *iter, MBTRI, r, true );
180 [ # # ]: 0 : if( MB_SUCCESS != rval ) return rval;
181 [ # # ][ # # ]: 0 : triangles.merge( r );
182 : 0 : }
183 : :
184 : 0 : return MB_SUCCESS;
185 : : }
186 : :
187 : 0 : ErrorCode WriteSTL::get_triangle_data( const double coords[9], float v1[3], float v2[3], float v3[3], float n[3] )
188 : : {
189 [ # # ][ # # ]: 0 : CartVect cv1, cv2, cv3, cn;
[ # # ][ # # ]
190 [ # # ]: 0 : ErrorCode rval = get_triangle_data( coords, cv1, cv2, cv3, cn );
191 [ # # ]: 0 : if( MB_SUCCESS != rval ) return rval;
192 : :
193 [ # # ]: 0 : cv1.get( v1 );
194 [ # # ]: 0 : cv2.get( v2 );
195 [ # # ]: 0 : cv3.get( v3 );
196 [ # # ]: 0 : cn.get( n );
197 : :
198 : 0 : return MB_SUCCESS;
199 : : }
200 : :
201 : 0 : ErrorCode WriteSTL::get_triangle_data( const double coords[9], CartVect& v1, CartVect& v2, CartVect& v3, CartVect& n )
202 : : {
203 : 0 : v1 = coords;
204 : 0 : v2 = coords + 3;
205 : 0 : v3 = coords + 6;
206 : :
207 [ # # ][ # # ]: 0 : n = ( v2 - v1 ) * ( v3 - v1 );
208 : :
209 : 0 : n.normalize();
210 : :
211 : 0 : return MB_SUCCESS;
212 : : }
213 : :
214 : 0 : ErrorCode WriteSTL::ascii_write_triangles( FILE* file, const char header[81], const Range& triangles, int prec )
215 : : {
216 : 0 : const char solid_name[] = "MOAB";
217 : :
218 : 0 : char myheader[81] = "solid ";
219 : 0 : strcat( myheader, solid_name );
220 : 0 : strncat( myheader, header, 80 );
221 : :
222 [ # # ][ # # ]: 0 : if( EOF == fputs( myheader, file ) || EOF == fputs( "\n", file ) ) return MB_FILE_WRITE_ERROR;
[ # # ][ # # ]
[ # # ]
223 : :
224 : : ErrorCode rval;
225 : : double coords[9];
226 [ # # ][ # # ]: 0 : CartVect v1, v2, v3, n;
[ # # ][ # # ]
227 [ # # ][ # # ]: 0 : for( Range::const_iterator iter = triangles.begin(); iter != triangles.end(); ++iter )
[ # # ][ # # ]
[ # # ]
228 : : {
229 : : const EntityHandle* conn;
230 : : int num_vtx;
231 : :
232 [ # # ][ # # ]: 0 : rval = mbImpl->get_connectivity( *iter, conn, num_vtx );
233 [ # # ]: 0 : if( MB_SUCCESS != rval ) return rval;
234 [ # # ]: 0 : if( num_vtx != 3 ) return MB_FAILURE;
235 : :
236 [ # # ]: 0 : rval = mbImpl->get_coords( conn, 3, coords );
237 [ # # ]: 0 : if( MB_SUCCESS != rval ) return rval;
238 : :
239 [ # # ]: 0 : rval = get_triangle_data( coords, v1, v2, v3, n );
240 [ # # ]: 0 : if( MB_SUCCESS != rval ) return rval;
241 : :
242 [ # # ][ # # ]: 0 : fprintf( file, "facet normal %e %e %e\n", n[0], n[1], n[2] );
[ # # ][ # # ]
243 [ # # ]: 0 : fprintf( file, "outer loop\n" );
244 [ # # ][ # # ]: 0 : fprintf( file, "vertex %.*e %.*e %.*e\n", prec, (float)v1[0], prec, (float)v1[1], prec, (float)v1[2] );
[ # # ][ # # ]
245 [ # # ][ # # ]: 0 : fprintf( file, "vertex %.*e %.*e %.*e\n", prec, (float)v2[0], prec, (float)v2[1], prec, (float)v2[2] );
[ # # ][ # # ]
246 [ # # ][ # # ]: 0 : fprintf( file, "vertex %.*e %.*e %.*e\n", prec, (float)v3[0], prec, (float)v3[1], prec, (float)v3[2] );
[ # # ][ # # ]
247 [ # # ]: 0 : fprintf( file, "endloop\n" );
248 [ # # ]: 0 : fprintf( file, "endfacet\n" );
249 : : }
250 : :
251 [ # # ]: 0 : fprintf( file, "endsolid %s\n", solid_name );
252 : 0 : return MB_SUCCESS;
253 : : }
254 : :
255 : : struct BinTri
256 : : {
257 : : float normal[3];
258 : : float vertex1[3];
259 : : float vertex2[3];
260 : : float vertex3[3];
261 : : char pad[2];
262 : : };
263 : :
264 : 0 : ErrorCode WriteSTL::binary_write_triangles( FILE* file, const char header[81], ByteOrder byte_order,
265 : : const Range& triangles )
266 : : {
267 : : ErrorCode rval;
268 [ # # ][ # # ]: 0 : if( 1 != fwrite( header, 80, 1, file ) ) return MB_FILE_WRITE_ERROR;
269 : :
270 : : // Default to little endian if byte_order == UNKNOWN_BYTE_ORDER
271 : 0 : const bool want_big_endian = ( byte_order == STL_BIG_ENDIAN );
272 [ # # ]: 0 : const bool am_big_endian = !SysUtil::little_endian();
273 : 0 : const bool swap_bytes = ( want_big_endian == am_big_endian );
274 : :
275 [ # # ][ # # ]: 0 : if( triangles.size() > INT_MAX ) // Can't write that many triangles
276 : 0 : return MB_FAILURE;
277 : :
278 [ # # ]: 0 : uint32_t count = (uint32_t)triangles.size();
279 [ # # ][ # # ]: 0 : if( swap_bytes ) SysUtil::byteswap( &count, 1 );
280 [ # # ][ # # ]: 0 : if( 1 != fwrite( &count, 4, 1, file ) ) return MB_FILE_WRITE_ERROR;
281 : :
282 : : double coords[9];
283 : : BinTri tri;
284 : 0 : tri.pad[0] = tri.pad[1] = '\0';
285 [ # # ][ # # ]: 0 : for( Range::const_iterator iter = triangles.begin(); iter != triangles.end(); ++iter )
[ # # ][ # # ]
[ # # ]
286 : : {
287 : : const EntityHandle* conn;
288 : : int num_vtx;
289 : :
290 [ # # ][ # # ]: 0 : rval = mbImpl->get_connectivity( *iter, conn, num_vtx );
291 [ # # ]: 0 : if( MB_SUCCESS != rval ) return rval;
292 [ # # ]: 0 : if( num_vtx != 3 ) return MB_FAILURE;
293 : :
294 [ # # ]: 0 : rval = mbImpl->get_coords( conn, 3, coords );
295 [ # # ]: 0 : if( MB_SUCCESS != rval ) return rval;
296 : :
297 [ # # ]: 0 : rval = get_triangle_data( coords, tri.vertex1, tri.vertex2, tri.vertex3, tri.normal );
298 [ # # ]: 0 : if( MB_SUCCESS != rval ) return rval;
299 : :
300 [ # # ]: 0 : if( swap_bytes )
301 : : {
302 [ # # ]: 0 : SysUtil::byteswap( tri.normal, 3 );
303 [ # # ]: 0 : SysUtil::byteswap( tri.vertex1, 3 );
304 [ # # ]: 0 : SysUtil::byteswap( tri.vertex2, 3 );
305 [ # # ]: 0 : SysUtil::byteswap( tri.vertex3, 3 );
306 : : }
307 : :
308 [ # # ][ # # ]: 0 : if( 1 != fwrite( &tri, 50, 1, file ) ) return MB_FILE_WRITE_ERROR;
309 : : }
310 : :
311 : 0 : return MB_SUCCESS;
312 : : }
313 : :
314 [ + - ][ + - ]: 228 : } // namespace moab
|