1
15
16
21
22 #include "ReadSTL.hpp"
23 #include "FileTokenizer.hpp"
24 #include "Internals.hpp"
25 #include "moab/Interface.hpp"
26 #include "moab/ReadUtilIface.hpp"
27 #include "moab/Range.hpp"
28 #include "moab/FileOptions.hpp"
29 #include "SysUtil.hpp"
30
31 #include <cerrno>
32 #include <cstring>
33 #include <climits>
34 #include <cassert>
35 #include <map>
36
37 namespace moab
38 {
39
40 ReadSTL::ReadSTL( Interface* impl ) : mdbImpl( impl )
41 {
42 mdbImpl->query_interface( readMeshIface );
43 }
44
45 ReadSTL::~ReadSTL()
46 {
47 if( readMeshIface )
48 {
49 mdbImpl->release_interface( readMeshIface );
50 readMeshIface = NULL;
51 }
52 }
53
54
55 bool ReadSTL::Point::operator<( const ReadSTL::Point& other ) const
56 {
57 return 0 > memcmp( this, &other, sizeof( ReadSTL::Point ) );
58 }
59
60 ErrorCode ReadSTL::read_tag_values( const char* ,
61 const char* ,
62 const FileOptions& ,
63 std::vector< int >& ,
64 const SubsetList* )
65 {
66 return MB_NOT_IMPLEMENTED;
67 }
68
69
70
71
72 ErrorCode ReadSTL::load_file( const char* filename,
73 const EntityHandle* ,
74 const FileOptions& opts,
75 const ReaderIface::SubsetList* subset_list,
76 const Tag* file_id_tag )
77 {
78 if( subset_list )
79 {
80 MB_SET_ERR( MB_UNSUPPORTED_OPERATION, "Reading subset of files not supported for STL" );
81 }
82
83 ErrorCode result;
84
85 std::vector< ReadSTL::Triangle > triangles;
86
87 bool is_ascii = false, is_binary = false;
88 if( MB_SUCCESS == opts.get_null_option( "ASCII" ) ) is_ascii = true;
89 if( MB_SUCCESS == opts.get_null_option( "BINARY" ) ) is_binary = true;
90 if( is_ascii && is_binary )
91 {
92 MB_SET_ERR( MB_FAILURE, "Conflicting options: BINARY ASCII" );
93 }
94
95 bool big_endian = false, little_endian = false;
96 if( MB_SUCCESS == opts.get_null_option( "BIG_ENDIAN" ) ) big_endian = true;
97 if( MB_SUCCESS == opts.get_null_option( "LITTLE_ENDIAN" ) ) little_endian = true;
98 if( big_endian && little_endian )
99 {
100 MB_SET_ERR( MB_FAILURE, "Conflicting options: BIG_ENDIAN LITTLE_ENDIAN" );
101 }
102 ByteOrder byte_order = big_endian ? STL_BIG_ENDIAN : little_endian ? STL_LITTLE_ENDIAN : STL_UNKNOWN_BYTE_ORDER;
103
104 if( is_ascii )
105 result = ascii_read_triangles( filename, triangles );
106 else if( is_binary )
107 result = binary_read_triangles( filename, byte_order, triangles );
108 else
109 {
110
111 result = ascii_read_triangles( filename, triangles );
112 if( MB_SUCCESS != result )
113
114 result = binary_read_triangles( filename, byte_order, triangles );
115 }
116 if( MB_SUCCESS != result ) return result;
117
118
119
120 std::map< Point, EntityHandle > vertex_map;
121 for( std::vector< Triangle >::iterator i = triangles.begin(); i != triangles.end(); ++i )
122 {
123 vertex_map[i->points[0]] = 0;
124 vertex_map[i->points[1]] = 0;
125 vertex_map[i->points[2]] = 0;
126 }
127
128
129 std::vector< double* > coord_arrays;
130 EntityHandle vtx_handle = 0;
131 result = readMeshIface->get_node_coords( 3, vertex_map.size(), MB_START_ID, vtx_handle, coord_arrays );
132 if( MB_SUCCESS != result ) return result;
133
134
135
136 double *x = coord_arrays[0], *y = coord_arrays[1], *z = coord_arrays[2];
137 for( std::map< Point, EntityHandle >::iterator i = vertex_map.begin(); i != vertex_map.end(); ++i )
138 {
139 i->second = vtx_handle;
140 ++vtx_handle;
141 *x = i->first.coords[0];
142 ++x;
143 *y = i->first.coords[1];
144 ++y;
145 *z = i->first.coords[2];
146 ++z;
147 }
148
149
150 EntityHandle elm_handle = 0;
151 EntityHandle* connectivity;
152 result = readMeshIface->get_element_connect( triangles.size(), 3, MBTRI, MB_START_ID, elm_handle, connectivity );
153 if( MB_SUCCESS != result ) return result;
154
155
156
157 EntityHandle* conn_sav = connectivity;
158 for( std::vector< Triangle >::iterator i = triangles.begin(); i != triangles.end(); ++i )
159 {
160 *connectivity = vertex_map[i->points[0]];
161 ++connectivity;
162 *connectivity = vertex_map[i->points[1]];
163 ++connectivity;
164 *connectivity = vertex_map[i->points[2]];
165 ++connectivity;
166 }
167
168
169 result = readMeshIface->update_adjacencies( elm_handle, triangles.size(), 3, conn_sav );
170 if( MB_SUCCESS != result ) return result;
171
172 if( file_id_tag )
173 {
174 Range vertices( vtx_handle, vtx_handle + vertex_map.size() - 1 );
175 Range elements( elm_handle, elm_handle + triangles.size() - 1 );
176 readMeshIface->assign_ids( *file_id_tag, vertices );
177 readMeshIface->assign_ids( *file_id_tag, elements );
178 }
179
180 return MB_SUCCESS;
181 }
182
183
184 ErrorCode ReadSTL::ascii_read_triangles( const char* name, std::vector< ReadSTL::Triangle >& tris )
185 {
186 FILE* file = fopen( name, "r" );
187 if( !file )
188 {
189 return MB_FILE_DOES_NOT_EXIST;
190 }
191
192 char header[81];
193 if( !fgets( header, sizeof( header ), file ) ||
194 strlen( header ) < 6 ||
195 header[strlen( header ) - 1] != '\n' ||
196 memcmp( header, "solid", 5 ) ||
197 !isspace( header[5] ) )
198 {
199 fclose( file );
200 return MB_FILE_WRITE_ERROR;
201 }
202
203
204 FileTokenizer tokens( file, readMeshIface );
205
206 Triangle tri;
207 float norm[3];
208
209
210
211 for( ;; )
212 {
213
214 const char* const expected[] = { "facet", "endsolid", 0 };
215 switch( tokens.match_token( expected ) )
216 {
217 case 1:
218 break;
219 case 2:
220 return MB_SUCCESS;
221 default:
222 return MB_FILE_WRITE_ERROR;
223 }
224
225 if( !tokens.match_token( "normal" ) ||
226 !tokens.get_floats( 3, norm ) ||
227 !tokens.match_token( "outer" ) ||
228 !tokens.match_token( "loop" ) )
229 return MB_FILE_WRITE_ERROR;
230
231
232 for( int i = 0; i < 3; i++ )
233 {
234 if( !tokens.match_token( "vertex" ) || !tokens.get_floats( 3, tri.points[i].coords ) )
235 return MB_FILE_WRITE_ERROR;
236 }
237
238 if( !tokens.match_token( "endloop" ) ||
239 !tokens.match_token( "endfacet" ) )
240 return MB_FILE_WRITE_ERROR;
241
242 tris.push_back( tri );
243 }
244
245 fclose( file );
246 return MB_SUCCESS;
247 }
248
249
250 struct BinaryHeader
251 {
252 char comment[80];
253 uint32_t count;
254 };
255
256
257 struct BinaryTri
258 {
259 float normal[3];
260 float coords[9];
261 char pad[2];
262 };
263
264
265 ErrorCode ReadSTL::binary_read_triangles( const char* name,
266 ReadSTL::ByteOrder byte_order,
267 std::vector< ReadSTL::Triangle >& tris )
268 {
269 FILE* file = fopen( name, "rb" );
270 if( !file )
271 {
272 return MB_FILE_DOES_NOT_EXIST;
273 }
274
275
276 BinaryHeader header;
277 if( fread( &header, 84, 1, file ) != 1 )
278 {
279 fclose( file );
280 return MB_FILE_WRITE_ERROR;
281 }
282
283
284 const bool want_big_endian = ( byte_order == STL_BIG_ENDIAN );
285 const bool am_big_endian = !SysUtil::little_endian();
286 bool swap_bytes = ( want_big_endian == am_big_endian );
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301 if( swap_bytes ) SysUtil::byteswap( &header.count, 1 );
302 unsigned long num_tri = header.count;
303
304
305 long filesize = SysUtil::filesize( file );
306 if( filesize >= 0 )
307 {
308
309 if( ULONG_MAX / 50 - 84 < num_tri ||
310 84 + 50 * num_tri != (unsigned long)filesize )
311 {
312
313
314 uint32_t num_tri_tmp = header.count;
315 SysUtil::byteswap( &num_tri_tmp, 1 );
316 unsigned long num_tri_swap = num_tri_tmp;
317 if( byte_order != STL_UNKNOWN_BYTE_ORDER ||
318 ULONG_MAX / 50 - 84 < num_tri_swap ||
319 84 + 50 * num_tri_swap != (unsigned long)filesize )
320 {
321 fclose( file );
322 return MB_FILE_WRITE_ERROR;
323 }
324 swap_bytes = !swap_bytes;
325 num_tri = num_tri_swap;
326 }
327 }
328
329
330 tris.resize( num_tri );
331
332
333 BinaryTri tri;
334 for( std::vector< Triangle >::iterator i = tris.begin(); i != tris.end(); ++i )
335 {
336 if( fread( &tri, 50, 1, file ) != 1 )
337 {
338 fclose( file );
339 return MB_FILE_WRITE_ERROR;
340 }
341
342 if( swap_bytes ) SysUtil::byteswap( tri.coords, 9 );
343
344 for( unsigned j = 0; j < 9; ++j )
345 i->points[j / 3].coords[j % 3] = tri.coords[j];
346 }
347
348 fclose( file );
349 return MB_SUCCESS;
350 }
351
352 ReaderIface* ReadSTL::factory( Interface* iface )
353 {
354 return new ReadSTL( iface );
355 }
356
357 }