Mesh Oriented datABase  (version 5.5.0)
An array-based unstructured mesh library
WriteSTL.cpp
Go to the documentation of this file.
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 <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__ ) /* 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 
55 {
56  return new WriteSTL( iface );
57 }
58 
59 WriteSTL::WriteSTL( Interface* impl ) : mbImpl( impl )
60 {
62 }
63 
65 {
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 /* export_dimension */ )
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  }
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  // Get precision for node coordinates
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  // Open file with write access, and create it if it doesn't exist.
137  int flags = O_WRONLY | O_CREAT;
138  // Select behavior if the named file already exists. If
139  // overwrite is true, truncate the file. If it is false,
140  // make the call to open() fail.
141  if( overwrite )
142  flags |= O_TRUNC;
143  else
144  flags |= O_EXCL;
145  // If platform defines a "binary" bit in the file access
146  // flags (i.e. we're building on windows), then set it
147  // if we're writing a binary file.
148 #ifdef O_BINARY
149  if( binary ) flags |= O_BINARY;
150 #endif
151 
152  // Give everyone read and write, but not execute, permission.
153  // These are not the final permissions for the file. Permissions
154  // are removed according to the user's umask. All we want to
155  // say here is that the executable bits should not be set because
156  // this isn't an executable file. Everything else is a user
157  // preference and should be left up to the umask.
158  int creat_mode = _S_IREAD | _S_IWRITE;
159 
160  // Open the file.
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 
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  // Default to little endian if byte_order == UNKNOWN_BYTE_ORDER
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 ) // Can't write that many triangles
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 } // namespace moab