Loading [MathJax]/extensions/tex2jax.js
Mesh Oriented datABase  (version 5.5.1)
An array-based unstructured mesh library
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
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  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 /* 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  } 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  // 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  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  // 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