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 
45 //== INCLUDES =================================================================
46 
47 
48 // STL
49 #include <map>
50 
51 #include <float.h>
52 #include <fstream>
53 #include <cstring>
54 
55 // OpenMesh
56 #include <OpenMesh/Core/IO/BinaryHelper.hh>
57 #include <OpenMesh/Core/IO/reader/STLReader.hh>
58 #include <OpenMesh/Core/IO/IOManager.hh>
59 
60 //comppare strings crossplatform ignorign case
61 #ifdef _WIN32
62  #define strnicmp _strnicmp
63 #else
64  #define strnicmp strncasecmp
65 #endif
66 
67 
68 //=== NAMESPACES ==============================================================
69 
70 
71 namespace OpenMesh {
72 namespace IO {
73 
74 
75 //=== INSTANCIATE =============================================================
76 
77 
78 // register the STLReader singleton with MeshReader
80 _STLReader_& STLReader() { return __STLReaderInstance; }
81 
82 
83 //=== IMPLEMENTATION ==========================================================
84 
85 
86 _STLReader_::
87 _STLReader_()
88  : eps_(FLT_MIN)
89 {
90  IOManager().register_module(this);
91 }
92 
93 
94 //-----------------------------------------------------------------------------
95 
96 
97 bool
99 read(const std::string& _filename, BaseImporter& _bi, Options& _opt)
100 {
101  bool result = false;
102 
103  STL_Type file_type = NONE;
104 
105  if ( check_extension( _filename, "stla" ) )
106  {
107  file_type = STLA;
108  }
109 
110  else if ( check_extension( _filename, "stlb" ) )
111  {
112  file_type = STLB;
113  }
114 
115  else if ( check_extension( _filename, "stl" ) )
116  {
117  file_type = check_stl_type(_filename);
118  }
119 
120 
121  switch (file_type)
122  {
123  case STLA:
124  {
125  result = read_stla(_filename, _bi, _opt);
126  _opt -= Options::Binary;
127  break;
128  }
129 
130  case STLB:
131  {
132  result = read_stlb(_filename, _bi, _opt);
133  _opt += Options::Binary;
134  break;
135  }
136 
137  default:
138  {
139  result = false;
140  break;
141  }
142  }
143 
144 
145  return result;
146 }
147 
148 bool
149 _STLReader_::read(std::istream& _is,
150  BaseImporter& _bi,
151  Options& _opt)
152 {
153 
154  bool result = false;
155 
156  if (_opt & Options::Binary)
157  result = read_stlb(_is, _bi, _opt);
158  else
159  result = read_stla(_is, _bi, _opt);
160 
161  return result;
162 }
163 
164 
165 //-----------------------------------------------------------------------------
166 
167 
168 #ifndef DOXY_IGNORE_THIS
169 
170 class CmpVec
171 {
172 public:
173 
174  explicit CmpVec(float _eps=FLT_MIN) : eps_(_eps) {}
175 
176  bool operator()( const Vec3f& _v0, const Vec3f& _v1 ) const
177  {
178  if (fabs(_v0[0] - _v1[0]) <= eps_)
179  {
180  if (fabs(_v0[1] - _v1[1]) <= eps_)
181  {
182  return (_v0[2] < _v1[2] - eps_);
183  }
184  else return (_v0[1] < _v1[1] - eps_);
185  }
186  else return (_v0[0] < _v1[0] - eps_);
187  }
188 
189 private:
190  float eps_;
191 };
192 
193 #endif
194 
195 
196 //-----------------------------------------------------------------------------
197 
198 void trimStdString( std::string& _string) {
199  // Trim Both leading and trailing spaces
200 
201  size_t start = _string.find_first_not_of(" \t\r\n");
202  size_t end = _string.find_last_not_of(" \t\r\n");
203 
204  if(( std::string::npos == start ) || ( std::string::npos == end))
205  _string = "";
206  else
207  _string = _string.substr( start, end-start+1 );
208 }
209 
210 //-----------------------------------------------------------------------------
211 
212 bool
213 _STLReader_::
214 read_stla(const std::string& _filename, BaseImporter& _bi, Options& _opt) const
215 {
216  std::fstream in( _filename.c_str(), std::ios_base::in );
217 
218  if (!in)
219  {
220  omerr() << "[STLReader] : cannot not open file "
221  << _filename
222  << std::endl;
223  return false;
224  }
225 
226  bool res = read_stla(in, _bi, _opt);
227 
228  if (in)
229  in.close();
230 
231  return res;
232 }
233 
234 //-----------------------------------------------------------------------------
235 
236 bool
237 _STLReader_::
238 read_stla(std::istream& _in, BaseImporter& _bi, Options& _opt) const
239 {
240 
241  unsigned int i;
242  OpenMesh::Vec3f v;
243  OpenMesh::Vec3f n;
244  BaseImporter::VHandles vhandles;
245 
246  CmpVec comp(eps_);
247  std::map<Vec3f, VertexHandle, CmpVec> vMap(comp);
248  std::map<Vec3f, VertexHandle, CmpVec>::iterator vMapIt;
249 
250  std::string line;
251 
252  std::string garbage;
253  std::stringstream strstream;
254 
255  bool facet_normal(false);
256 
257  while( _in && !_in.eof() ) {
258 
259  // Get one line
260  std::getline(_in, line);
261  if ( _in.bad() ){
262  omerr() << " Warning! Could not read stream properly!\n";
263  return false;
264  }
265 
266  // Trim Both leading and trailing spaces
267  trimStdString(line);
268 
269  // Normal found?
270  if (line.find("facet normal") != std::string::npos) {
271  strstream.str(line);
272  strstream.clear();
273 
274  // facet
275  strstream >> garbage;
276 
277  // normal
278  strstream >> garbage;
279 
280  strstream >> n[0];
281  strstream >> n[1];
282  strstream >> n[2];
283 
284  facet_normal = true;
285  }
286 
287  // Detected a triangle
288  if ( (line.find("outer") != std::string::npos) || (line.find("OUTER") != std::string::npos ) ) {
289 
290  vhandles.clear();
291 
292  for (i=0; i<3; ++i) {
293  // Get one vertex
294  std::getline(_in, line);
295  trimStdString(line);
296 
297  strstream.str(line);
298  strstream.clear();
299 
300  strstream >> garbage;
301 
302  strstream >> v[0];
303  strstream >> v[1];
304  strstream >> v[2];
305 
306  // has vector been referenced before?
307  if ((vMapIt=vMap.find(v)) == vMap.end())
308  {
309  // No : add vertex and remember idx/vector mapping
310  VertexHandle handle = _bi.add_vertex(v);
311  vhandles.push_back(handle);
312  vMap[v] = handle;
313  }
314  else
315  // Yes : get index from map
316  vhandles.push_back(vMapIt->second);
317 
318  }
319 
320  // Add face only if it is not degenerated
321  if ((vhandles[0] != vhandles[1]) &&
322  (vhandles[0] != vhandles[2]) &&
323  (vhandles[1] != vhandles[2])) {
324 
325 
326  FaceHandle fh = _bi.add_face(vhandles);
327 
328  // set the normal if requested
329  // if a normal was requested but could not be found we unset the option
330  if (facet_normal) {
331  if (fh.is_valid() && _opt.face_has_normal())
332  _bi.set_normal(fh, n);
333  } else
334  _opt -= Options::FaceNormal;
335  }
336 
337  facet_normal = false;
338  }
339  }
340 
341  return true;
342 }
343 
344 //-----------------------------------------------------------------------------
345 
346 bool
347 _STLReader_::
348 read_stlb(const std::string& _filename, BaseImporter& _bi, Options& _opt) const
349 {
350  std::fstream in( _filename.c_str(), std::ios_base::in | std::ios_base::binary);
351 
352  if (!in)
353  {
354  omerr() << "[STLReader] : cannot not open file "
355  << _filename
356  << std::endl;
357  return false;
358  }
359 
360  bool res = read_stlb(in, _bi, _opt);
361 
362  if (in)
363  in.close();
364 
365  return res;
366 }
367 
368 //-----------------------------------------------------------------------------
369 
370 bool
371 _STLReader_::
372 read_stlb(std::istream& _in, BaseImporter& _bi, Options& _opt) const
373 {
374  char dummy[100];
375  bool swapFlag;
376  unsigned int i, nT;
377  OpenMesh::Vec3f v, n;
378  BaseImporter::VHandles vhandles;
379 
380  std::map<Vec3f, VertexHandle, CmpVec> vMap;
381  std::map<Vec3f, VertexHandle, CmpVec>::iterator vMapIt;
382 
383 
384  // check size of types
385  if ((sizeof(float) != 4) || (sizeof(int) != 4)) {
386  omerr() << "[STLReader] : wrong type size\n";
387  return false;
388  }
389 
390  // determine endian mode
391  union { unsigned int i; unsigned char c[4]; } endian_test;
392  endian_test.i = 1;
393  swapFlag = (endian_test.c[3] == 1);
394 
395  // read number of triangles
396  _in.read(dummy, 80);
397  nT = read_int(_in, swapFlag);
398 
399  // read triangles
400  while (nT)
401  {
402  vhandles.clear();
403 
404  // read triangle normal
405  n[0] = read_float(_in, swapFlag);
406  n[1] = read_float(_in, swapFlag);
407  n[2] = read_float(_in, swapFlag);
408 
409  // triangle's vertices
410  for (i=0; i<3; ++i)
411  {
412  v[0] = read_float(_in, swapFlag);
413  v[1] = read_float(_in, swapFlag);
414  v[2] = read_float(_in, swapFlag);
415 
416  // has vector been referenced before?
417  if ((vMapIt=vMap.find(v)) == vMap.end())
418  {
419  // No : add vertex and remember idx/vector mapping
420  VertexHandle handle = _bi.add_vertex(v);
421  vhandles.push_back(handle);
422  vMap[v] = handle;
423  }
424  else
425  // Yes : get index from map
426  vhandles.push_back(vMapIt->second);
427  }
428 
429 
430  // Add face only if it is not degenerated
431  if ((vhandles[0] != vhandles[1]) &&
432  (vhandles[0] != vhandles[2]) &&
433  (vhandles[1] != vhandles[2])) {
434  FaceHandle fh = _bi.add_face(vhandles);
435 
436  if (fh.is_valid() && _opt.face_has_normal())
437  _bi.set_normal(fh, n);
438  }
439 
440  _in.read(dummy, 2);
441  --nT;
442  }
443 
444  return true;
445 }
446 
447 //-----------------------------------------------------------------------------
448 
449 _STLReader_::STL_Type
450 _STLReader_::
451 check_stl_type(const std::string& _filename) const
452 {
453 
454  // open file
455  std::ifstream ifs (_filename.c_str(), std::ifstream::binary);
456  if(!ifs.good())
457  {
458  omerr() << "could not open file" << _filename << std::endl;
459  return NONE;
460  }
461 
462  //find first non whitespace character
463  std::string line = "";
464  std::size_t firstChar;
465  while(line.empty() && ifs.good())
466  {
467  std::getline(ifs,line);
468  firstChar = line.find_first_not_of("\t ");
469  }
470 
471  //check for ascii keyword solid
472  if(strnicmp("solid",&line[firstChar],5) == 0)
473  {
474  return STLA;
475  }
476  ifs.close();
477 
478  //if the file does not start with solid it is probably STLB
479  //check the file size to verify it.
480 
481  //open the file
482  FILE* in = fopen(_filename.c_str(), "rb");
483  if (!in) return NONE;
484 
485  // determine endian mode
486  union { unsigned int i; unsigned char c[4]; } endian_test;
487  endian_test.i = 1;
488  bool swapFlag = (endian_test.c[3] == 1);
489 
490 
491  // read number of triangles
492  char dummy[100];
493  fread(dummy, 1, 80, in);
494  size_t nT = read_int(in, swapFlag);
495 
496 
497  // compute file size from nT
498  size_t binary_size = 84 + nT*50;
499 
500  // get actual file size
501  size_t file_size(0);
502  rewind(in);
503  while (!feof(in))
504  file_size += fread(dummy, 1, 100, in);
505  fclose(in);
506 
507  // if sizes match -> it's STLB
508  return (binary_size == file_size ? STLB : NONE);
509 }
510 
511 
512 //=============================================================================
513 } // namespace IO
514 } // namespace OpenMesh
515 //=============================================================================
Handle for a face entity.
Definition: Handles.hh:141
size_t binary_size(const Mesh &_mesh, const std::string &_ext, Options _opt=Options::Default)
Get binary size of data.
Definition: MeshIO.hh:268
Handle for a vertex entity.
Definition: Handles.hh:120
bool is_valid() const
The handle is valid iff the index is not negative.
Definition: Handles.hh:72
bool register_module(BaseReader *_bl)
Definition: IOManager.hh:217
float read_float(FILE *_in, bool _swap=false)
Set options for reader/writer modules.
Definition: Options.hh:90
_IOManager_ & IOManager()
Definition: IOManager.cc:72
Has (r) / store (w) face normals.
Definition: Options.hh:108
bool read(const std::string &_filename, BaseImporter &_bi, Options &_opt) override
Definition: STLReader.cc:99
int read_int(FILE *_in, bool _swap=false)
Set binary mode for r/w.
Definition: Options.hh:100
_STLReader_ __STLReaderInstance
Declare the single entity of the STL reader.
Definition: STLReader.cc:79