Developer Documentation
STLReader.cc
1 /* ========================================================================= *
2  * *
3  * OpenMesh *
4  * Copyright (c) 2001-2015, RWTH-Aachen University *
5  * Department of Computer Graphics and Multimedia *
6  * All rights reserved. *
7  * www.openmesh.org *
8  * *
9  *---------------------------------------------------------------------------*
10  * This file is part of OpenMesh. *
11  *---------------------------------------------------------------------------*
12  * *
13  * Redistribution and use in source and binary forms, with or without *
14  * modification, are permitted provided that the following conditions *
15  * are met: *
16  * *
17  * 1. Redistributions of source code must retain the above copyright notice, *
18  * this list of conditions and the following disclaimer. *
19  * *
20  * 2. Redistributions in binary form must reproduce the above copyright *
21  * notice, this list of conditions and the following disclaimer in the *
22  * documentation and/or other materials provided with the distribution. *
23  * *
24  * 3. Neither the name of the copyright holder nor the names of its *
25  * contributors may be used to endorse or promote products derived from *
26  * this software without specific prior written permission. *
27  * *
28  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
29  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
30  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
31  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER *
32  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
33  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, *
34  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR *
35  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
36  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *
37  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *
38  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
39  * *
40  * ========================================================================= */
41 
42 /*===========================================================================*\
43  * *
44  * $Revision$ *
45  * $Date$ *
46  * *
47 \*===========================================================================*/
48 
49 
50 //== INCLUDES =================================================================
51 
52 
53 // STL
54 #include <map>
55 
56 #include <float.h>
57 #include <fstream>
58 #include <cstring>
59 
60 // OpenMesh
61 #include <OpenMesh/Core/IO/BinaryHelper.hh>
62 #include <OpenMesh/Core/IO/reader/STLReader.hh>
63 #include <OpenMesh/Core/IO/IOManager.hh>
64 
65 //comppare strings crossplatform ignorign case
66 #ifdef _WIN32
67  #define strnicmp _strnicmp
68 #else
69  #define strnicmp strncasecmp
70 #endif
71 
72 
73 //=== NAMESPACES ==============================================================
74 
75 
76 namespace OpenMesh {
77 namespace IO {
78 
79 
80 //=== INSTANCIATE =============================================================
81 
82 
83 // register the STLReader singleton with MeshReader
85 _STLReader_& STLReader() { return __STLReaderInstance; }
86 
87 
88 //=== IMPLEMENTATION ==========================================================
89 
90 
91 _STLReader_::
92 _STLReader_()
93  : eps_(FLT_MIN)
94 {
95  IOManager().register_module(this);
96 }
97 
98 
99 //-----------------------------------------------------------------------------
100 
101 
102 bool
104 read(const std::string& _filename, BaseImporter& _bi, Options& _opt)
105 {
106  bool result = false;
107 
108  STL_Type file_type = NONE;
109 
110  if ( check_extension( _filename, "stla" ) )
111  {
112  file_type = STLA;
113  }
114 
115  else if ( check_extension( _filename, "stlb" ) )
116  {
117  file_type = STLB;
118  }
119 
120  else if ( check_extension( _filename, "stl" ) )
121  {
122  file_type = check_stl_type(_filename);
123  }
124 
125 
126  switch (file_type)
127  {
128  case STLA:
129  {
130  result = read_stla(_filename, _bi, _opt);
131  _opt -= Options::Binary;
132  break;
133  }
134 
135  case STLB:
136  {
137  result = read_stlb(_filename, _bi, _opt);
138  _opt += Options::Binary;
139  break;
140  }
141 
142  default:
143  {
144  result = false;
145  break;
146  }
147  }
148 
149 
150  return result;
151 }
152 
153 bool
154 _STLReader_::read(std::istream& _is,
155  BaseImporter& _bi,
156  Options& _opt)
157 {
158 
159  bool result = false;
160 
161  if (_opt & Options::Binary)
162  result = read_stlb(_is, _bi, _opt);
163  else
164  result = read_stla(_is, _bi, _opt);
165 
166  return result;
167 }
168 
169 
170 //-----------------------------------------------------------------------------
171 
172 
173 #ifndef DOXY_IGNORE_THIS
174 
175 class CmpVec
176 {
177 public:
178 
179  CmpVec(float _eps=FLT_MIN) : eps_(_eps) {}
180 
181  bool operator()( const Vec3f& _v0, const Vec3f& _v1 ) const
182  {
183  if (fabs(_v0[0] - _v1[0]) <= eps_)
184  {
185  if (fabs(_v0[1] - _v1[1]) <= eps_)
186  {
187  return (_v0[2] < _v1[2] - eps_);
188  }
189  else return (_v0[1] < _v1[1] - eps_);
190  }
191  else return (_v0[0] < _v1[0] - eps_);
192  }
193 
194 private:
195  float eps_;
196 };
197 
198 #endif
199 
200 
201 //-----------------------------------------------------------------------------
202 
203 void trimStdString( std::string& _string) {
204  // Trim Both leading and trailing spaces
205 
206  size_t start = _string.find_first_not_of(" \t\r\n");
207  size_t end = _string.find_last_not_of(" \t\r\n");
208 
209  if(( std::string::npos == start ) || ( std::string::npos == end))
210  _string = "";
211  else
212  _string = _string.substr( start, end-start+1 );
213 }
214 
215 //-----------------------------------------------------------------------------
216 
217 bool
218 _STLReader_::
219 read_stla(const std::string& _filename, BaseImporter& _bi, Options& _opt) const
220 {
221  std::fstream in( _filename.c_str(), std::ios_base::in );
222 
223  if (!in)
224  {
225  omerr() << "[STLReader] : cannot not open file "
226  << _filename
227  << std::endl;
228  return false;
229  }
230 
231  bool res = read_stla(in, _bi, _opt);
232 
233  if (in)
234  in.close();
235 
236  return res;
237 }
238 
239 //-----------------------------------------------------------------------------
240 
241 bool
242 _STLReader_::
243 read_stla(std::istream& _in, BaseImporter& _bi, Options& _opt) const
244 {
245 
246  unsigned int i;
247  OpenMesh::Vec3f v;
248  OpenMesh::Vec3f n;
249  BaseImporter::VHandles vhandles;
250 
251  CmpVec comp(eps_);
252  std::map<Vec3f, VertexHandle, CmpVec> vMap(comp);
253  std::map<Vec3f, VertexHandle, CmpVec>::iterator vMapIt;
254 
255  std::string line;
256 
257  std::string garbage;
258  std::stringstream strstream;
259 
260  bool facet_normal(false);
261 
262  while( _in && !_in.eof() ) {
263 
264  // Get one line
265  std::getline(_in, line);
266  if ( _in.bad() ){
267  omerr() << " Warning! Could not read stream properly!\n";
268  return false;
269  }
270 
271  // Trim Both leading and trailing spaces
272  trimStdString(line);
273 
274  // Normal found?
275  if (line.find("facet normal") != std::string::npos) {
276  strstream.str(line);
277  strstream.clear();
278 
279  // facet
280  strstream >> garbage;
281 
282  // normal
283  strstream >> garbage;
284 
285  strstream >> n[0];
286  strstream >> n[1];
287  strstream >> n[2];
288 
289  facet_normal = true;
290  }
291 
292  // Detected a triangle
293  if ( (line.find("outer") != std::string::npos) || (line.find("OUTER") != std::string::npos ) ) {
294 
295  vhandles.clear();
296 
297  for (i=0; i<3; ++i) {
298  // Get one vertex
299  std::getline(_in, line);
300  trimStdString(line);
301 
302  strstream.str(line);
303  strstream.clear();
304 
305  strstream >> garbage;
306 
307  strstream >> v[0];
308  strstream >> v[1];
309  strstream >> v[2];
310 
311  // has vector been referenced before?
312  if ((vMapIt=vMap.find(v)) == vMap.end())
313  {
314  // No : add vertex and remember idx/vector mapping
315  VertexHandle handle = _bi.add_vertex(v);
316  vhandles.push_back(handle);
317  vMap[v] = handle;
318  }
319  else
320  // Yes : get index from map
321  vhandles.push_back(vMapIt->second);
322 
323  }
324 
325  // Add face only if it is not degenerated
326  if ((vhandles[0] != vhandles[1]) &&
327  (vhandles[0] != vhandles[2]) &&
328  (vhandles[1] != vhandles[2])) {
329 
330 
331  FaceHandle fh = _bi.add_face(vhandles);
332 
333  // set the normal if requested
334  // if a normal was requested but could not be found we unset the option
335  if (facet_normal) {
336  if (fh.is_valid() && _opt.face_has_normal())
337  _bi.set_normal(fh, n);
338  } else
339  _opt -= Options::FaceNormal;
340  }
341 
342  facet_normal = false;
343  }
344  }
345 
346  return true;
347 }
348 
349 //-----------------------------------------------------------------------------
350 
351 bool
352 _STLReader_::
353 read_stlb(const std::string& _filename, BaseImporter& _bi, Options& _opt) const
354 {
355  std::fstream in( _filename.c_str(), std::ios_base::in | std::ios_base::binary);
356 
357  if (!in)
358  {
359  omerr() << "[STLReader] : cannot not open file "
360  << _filename
361  << std::endl;
362  return false;
363  }
364 
365  bool res = read_stlb(in, _bi, _opt);
366 
367  if (in)
368  in.close();
369 
370  return res;
371 }
372 
373 //-----------------------------------------------------------------------------
374 
375 bool
376 _STLReader_::
377 read_stlb(std::istream& _in, BaseImporter& _bi, Options& _opt) const
378 {
379  char dummy[100];
380  bool swapFlag;
381  unsigned int i, nT;
382  OpenMesh::Vec3f v, n;
383  BaseImporter::VHandles vhandles;
384 
385  std::map<Vec3f, VertexHandle, CmpVec> vMap;
386  std::map<Vec3f, VertexHandle, CmpVec>::iterator vMapIt;
387 
388 
389  // check size of types
390  if ((sizeof(float) != 4) || (sizeof(int) != 4)) {
391  omerr() << "[STLReader] : wrong type size\n";
392  return false;
393  }
394 
395  // determine endian mode
396  union { unsigned int i; unsigned char c[4]; } endian_test;
397  endian_test.i = 1;
398  swapFlag = (endian_test.c[3] == 1);
399 
400  // read number of triangles
401  _in.read(dummy, 80);
402  nT = read_int(_in, swapFlag);
403 
404  // read triangles
405  while (nT)
406  {
407  vhandles.clear();
408 
409  // read triangle normal
410  n[0] = read_float(_in, swapFlag);
411  n[1] = read_float(_in, swapFlag);
412  n[2] = read_float(_in, swapFlag);
413 
414  // triangle's vertices
415  for (i=0; i<3; ++i)
416  {
417  v[0] = read_float(_in, swapFlag);
418  v[1] = read_float(_in, swapFlag);
419  v[2] = read_float(_in, swapFlag);
420 
421  // has vector been referenced before?
422  if ((vMapIt=vMap.find(v)) == vMap.end())
423  {
424  // No : add vertex and remember idx/vector mapping
425  VertexHandle handle = _bi.add_vertex(v);
426  vhandles.push_back(handle);
427  vMap[v] = handle;
428  }
429  else
430  // Yes : get index from map
431  vhandles.push_back(vMapIt->second);
432  }
433 
434 
435  // Add face only if it is not degenerated
436  if ((vhandles[0] != vhandles[1]) &&
437  (vhandles[0] != vhandles[2]) &&
438  (vhandles[1] != vhandles[2])) {
439  FaceHandle fh = _bi.add_face(vhandles);
440 
441  if (fh.is_valid() && _opt.face_has_normal())
442  _bi.set_normal(fh, n);
443  }
444 
445  _in.read(dummy, 2);
446  --nT;
447  }
448 
449  return true;
450 }
451 
452 //-----------------------------------------------------------------------------
453 
454 _STLReader_::STL_Type
455 _STLReader_::
456 check_stl_type(const std::string& _filename) const
457 {
458 
459  // open file
460  std::ifstream ifs (_filename.c_str(), std::ifstream::binary);
461  if(!ifs.good())
462  {
463  omerr() << "could not open file" << _filename << std::endl;
464  return NONE;
465  }
466 
467  //find first non whitespace character
468  std::string line = "";
469  std::size_t firstChar;
470  while(line.empty() && ifs.good())
471  {
472  std::getline(ifs,line);
473  firstChar = line.find_first_not_of("\t ");
474  }
475 
476  //check for ascii keyword solid
477  if(strnicmp("solid",&line[firstChar],5) == 0)
478  {
479  return STLA;
480  }
481  ifs.close();
482 
483  //if the file does not start with solid it is probably STLB
484  //check the file size to verify it.
485 
486  //open the file
487  FILE* in = fopen(_filename.c_str(), "rb");
488  if (!in) return NONE;
489 
490  // determine endian mode
491  union { unsigned int i; unsigned char c[4]; } endian_test;
492  endian_test.i = 1;
493  bool swapFlag = (endian_test.c[3] == 1);
494 
495 
496  // read number of triangles
497  char dummy[100];
498  fread(dummy, 1, 80, in);
499  size_t nT = read_int(in, swapFlag);
500 
501 
502  // compute file size from nT
503  size_t binary_size = 84 + nT*50;
504 
505  // get actual file size
506  size_t file_size(0);
507  rewind(in);
508  while (!feof(in))
509  file_size += fread(dummy, 1, 100, in);
510  fclose(in);
511 
512  // if sizes match -> it's STLB
513  return (binary_size == file_size ? STLB : NONE);
514 }
515 
516 
517 //=============================================================================
518 } // namespace IO
519 } // namespace OpenMesh
520 //=============================================================================
bool read(const std::string &_filename, BaseImporter &_bi, Options &_opt)
Definition: STLReader.cc:104
_STLReader_ __STLReaderInstance
Declare the single entity of the STL reader.
Definition: STLReader.cc:84
Set binary mode for r/w.
Definition: Options.hh:105
Has (r) / store (w) face normals.
Definition: Options.hh:113
float read_float(FILE *_in, bool _swap=false)
bool is_valid() const
The handle is valid iff the index is not equal to -1.
Definition: Handles.hh:77
size_t binary_size(const Mesh &_mesh, const std::string &_ext, Options _opt=Options::Default)
Get binary size of data.
Definition: MeshIO.hh:260
Set options for reader/writer modules.
Definition: Options.hh:95
Handle for a vertex entity.
Definition: Handles.hh:125
bool register_module(BaseReader *_bl)
Definition: IOManager.hh:222
int read_int(FILE *_in, bool _swap=false)
Handle for a face entity.
Definition: Handles.hh:146
_IOManager_ & IOManager()
Definition: IOManager.cc:77