ug4
stl_reader.h
Go to the documentation of this file.
1 /*
2  Copyright (c) 2018, Sebastian Reiter (s.b.reiter@gmail.com)
3  All rights reserved.
4 
5  Redistribution and use in source and binary forms, with or without
6  modification, are permitted provided that the following conditions are met:
7  * Redistributions of source code must retain the above copyright
8  notice, this list of conditions and the following disclaimer.
9  * Redistributions in binary form must reproduce the above copyright
10  notice, this list of conditions and the following disclaimer in the
11  documentation and/or other materials provided with the distribution.
12 
13  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
17  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24 
25 
125 #ifndef __H__STL_READER
126 #define __H__STL_READER
127 
128 #include <algorithm>
129 #include <exception>
130 #include <fstream>
131 #include <sstream>
132 #include <vector>
133 
134 #ifdef STL_READER_NO_EXCEPTIONS
135  #define STL_READER_THROW(msg) return false;
136  #define STL_READER_COND_THROW(cond, msg) if(cond) return false;
137 #else
139  #define STL_READER_THROW(msg) {std::stringstream ss; ss << msg; throw(std::runtime_error(ss.str()));}
140 
142  #define STL_READER_COND_THROW(cond, msg) if(cond){std::stringstream ss; ss << msg; throw(std::runtime_error(ss.str()));}
143 #endif
144 
145 
146 namespace stl_reader {
147 
149 
189 template <class TNumberContainer, class TIndexContainer>
190 bool ReadStlFile(const char* filename,
191  TNumberContainer& coordsOut,
192  TNumberContainer& normalsOut,
193  TIndexContainer& trisOut,
194  TIndexContainer& solidRangesOut);
195 
196 
198 
201 template <class TNumberContainer, class TIndexContainer>
202 bool ReadStlFile_ASCII(const char* filename,
203  TNumberContainer& coordsOut,
204  TNumberContainer& normalsOut,
205  TIndexContainer& trisOut,
206  TIndexContainer& solidRangesOut);
207 
209 
213 template <class TNumberContainer, class TIndexContainer>
214 bool ReadStlFile_BINARY(const char* filename,
215  TNumberContainer& coordsOut,
216  TNumberContainer& normalsOut,
217  TIndexContainer& trisOut,
218  TIndexContainer& solidRangesOut);
219 
221 
225 inline bool StlFileHasASCIIFormat(const char* filename);
226 
227 
229 template <class TNumber = float, class TIndex = unsigned int>
230 class StlMesh {
231 public:
234  {
235  solids.resize (2, 0);
236  }
237 
239  StlMesh (const char* filename)
240  {
241  read_file (filename);
242  }
243 
245  bool read_file (const char* filename)
246  {
247  bool res = false;
248 
249  #ifndef STL_READER_NO_EXCEPTIONS
250  try {
251  #endif
252 
253  res = ReadStlFile (filename, coords, normals, tris, solids);
254 
255  #ifndef STL_READER_NO_EXCEPTIONS
256  } catch (std::exception& e) {
257  #else
258  if (!res) {
259  #endif
260 
261  coords.clear ();
262  normals.clear ();
263  tris.clear ();
264  solids.clear ();
265  STL_READER_THROW (e.what());
266  }
267 
268  return res;
269  }
270 
272  size_t num_vrts () const
273  {
274  return coords.size() / 3;
275  }
276 
278  const TNumber* vrt_coords (const size_t vi) const
279  {
280  return &coords[vi * 3];
281  }
282 
284  size_t num_tris () const
285  {
286  return tris.size() / 3;
287  }
288 
290  const TIndex* tri_corner_inds (const size_t ti) const
291  {
292  return &tris [ti * 3];
293  }
294 
296  const TIndex tri_corner_ind (const size_t ti, const size_t ci) const
297  {
298  return tris [ti * 3 + ci];
299  }
300 
308  const TNumber* tri_corner_coords (const size_t ti, const size_t ci) const
309  {
310  return &coords[tri_corner_ind(ti, ci) * 3];
311  }
312 
314  const TNumber* tri_normal (const size_t ti) const
315  {
316  return &normals [ti * 3];
317  }
318 
320 
325  size_t num_solids () const
326  {
327  if(solids.empty ())
328  return 0;
329  return solids.size () - 1;
330  }
331 
333  TIndex solid_tris_begin (const size_t si) const
334  {
335  return solids [si];
336  }
337 
339  TIndex solid_tris_end (const size_t si) const
340  {
341  return solids [si + 1];
342  }
343 
345 
347  const TNumber* raw_coords () const
348  {
349  if(coords.empty())
350  return NULL;
351  return &coords[0];
352  }
353 
355 
357  const TNumber* raw_normals () const
358  {
359  if(normals.empty())
360  return NULL;
361  return &normals[0];
362  }
363 
365 
367  const TIndex* raw_tris () const
368  {
369  if(tris.empty())
370  return NULL;
371  return &tris[0];
372  }
373 
375 
377  const TIndex* raw_solids () const
378  {
379  if(solids.empty())
380  return NULL;
381  return &solids[0];
382  }
383 
384 private:
385  std::vector<TNumber> coords;
386  std::vector<TNumber> normals;
387  std::vector<TIndex> tris;
388  std::vector<TIndex> solids;
389 };
390 
391 
393 // IMPLEMENTATION
395 
396 
397 namespace stl_reader_impl {
398 
399  // a coordinate triple with an additional index. The index is required
400  // for RemoveDoubles, so that triangles can be reindexed properly.
401  template <typename number_t, typename index_t>
402  struct CoordWithIndex {
403  number_t data[3];
404  index_t index;
405 
406  bool operator == (const CoordWithIndex& c) const
407  {
408  return (c[0] == data[0]) && (c[1] == data[1]) && (c[2] == data[2]);
409  }
410 
411  bool operator != (const CoordWithIndex& c) const
412  {
413  return (c[0] != data[0]) || (c[1] != data[1]) || (c[2] != data[2]);
414  }
415 
416  bool operator < (const CoordWithIndex& c) const
417  {
418  return (data[0] < c[0])
419  || (data[0] == c[0] && data[1] < c[1])
420  || (data[0] == c[0] && data[1] == c[1] && data[2] < c[2]);
421  }
422 
423  inline number_t& operator [] (const size_t i) {return data[i];}
424  inline number_t operator [] (const size_t i) const {return data[i];}
425  };
426 
427  // sorts the array coordsWithIndexInOut and copies unique indices to coordsOut.
428  // Triangle-corners are re-indexed on the fly and degenerated triangles are removed.
429  template <class TNumberContainer, class TIndexContainer>
430  void RemoveDoubles (TNumberContainer& uniqueCoordsOut,
431  TIndexContainer& trisInOut,
432  std::vector <CoordWithIndex<
434  typename TIndexContainer::value_type> >
435  &coordsWithIndexInOut)
436  {
437  using namespace std;
438 
439  typedef typename TNumberContainer::value_type number_t;
440  typedef typename TIndexContainer::value_type index_t;
441 
442  sort (coordsWithIndexInOut.begin(), coordsWithIndexInOut.end());
443 
444  // first count unique indices
445  size_t numUnique = 1;
446  for(size_t i = 1; i < coordsWithIndexInOut.size(); ++i){
447  if(coordsWithIndexInOut[i] != coordsWithIndexInOut[i - 1])
448  ++numUnique;
449  }
450 
451  uniqueCoordsOut.resize (numUnique * 3);
452  vector<index_t> newIndex (coordsWithIndexInOut.size());
453 
454  // copy unique coordinates to 'uniqueCoordsOut' and create an index-map
455  // 'newIndex', which allows to re-index triangles later on.
456  size_t curInd = 0;
457  newIndex[0] = 0;
458  for(size_t i = 0; i < 3; ++i)
459  uniqueCoordsOut[i] = coordsWithIndexInOut[0][i];
460 
461  for(size_t i = 1; i < coordsWithIndexInOut.size(); ++i){
462  const CoordWithIndex <number_t, index_t> c = coordsWithIndexInOut[i];
463  if(c != coordsWithIndexInOut[i - 1]){
464  ++curInd;
465  for(size_t j = 0; j < 3; ++j)
466  uniqueCoordsOut[curInd * 3 + j] = coordsWithIndexInOut[i][j];
467  }
468 
469  newIndex[c.index] = curInd;
470  }
471 
472  // re-index triangles, so that they refer to 'uniqueCoordsOut'
473  // make sure to only add triangles which refer to three different indices
474  size_t numUniqueTriInds = 0;
475  for(size_t i = 0; i < trisInOut.size(); i+=3){
476  int ni[3];
477  for(int j = 0; j < 3; ++j)
478  ni[j] = newIndex[trisInOut[i+j]];
479 
480  if((ni[0] != ni[1]) && (ni[0] != ni[2]) && (ni[1] != ni[2])){
481  for(int j = 0; j < 3; ++j)
482  trisInOut[numUniqueTriInds + j] = ni[j];
483  numUniqueTriInds += 3;
484  }
485  }
486 
487  if(numUniqueTriInds < trisInOut.size())
488  trisInOut.resize (numUniqueTriInds);
489  }
490 }// end of namespace stl_reader_impl
491 
492 
493 template <class TNumberContainer, class TIndexContainer>
494 bool ReadStlFile(const char* filename,
495  TNumberContainer& coordsOut,
496  TNumberContainer& normalsOut,
497  TIndexContainer& trisOut,
498  TIndexContainer& solidRangesOut)
499 {
500  if(StlFileHasASCIIFormat(filename))
501  return ReadStlFile_ASCII(filename, coordsOut, normalsOut, trisOut, solidRangesOut);
502  else
503  return ReadStlFile_BINARY(filename, coordsOut, normalsOut, trisOut, solidRangesOut);
504 }
505 
506 
507 template <class TNumberContainer, class TIndexContainer>
508 bool ReadStlFile_ASCII(const char* filename,
509  TNumberContainer& coordsOut,
510  TNumberContainer& normalsOut,
511  TIndexContainer& trisOut,
512  TIndexContainer& solidRangesOut)
513 {
514  using namespace std;
515  using namespace stl_reader_impl;
516 
517  typedef typename TNumberContainer::value_type number_t;
518  typedef typename TIndexContainer::value_type index_t;
519 
520  coordsOut.clear();
521  normalsOut.clear();
522  trisOut.clear();
523  solidRangesOut.clear();
524 
525  ifstream in(filename);
526  STL_READER_COND_THROW(!in, "Couldn't open file " << filename);
527 
528  vector<CoordWithIndex <number_t, index_t> > coordsWithIndex;
529 
530  string buffer;
531  vector<string> tokens;
532  int lineCount = 1;
533  int maxNumTokens = 0;
534  size_t numFaceVrts = 0;
535 
536  while(!(in.eof() || in.fail()))
537  {
538  // read the line and tokenize.
539  // In order to reuse memory in between lines, 'tokens' won't be cleared.
540  // Instead we count the number of tokens using 'tokenCount'.
541  getline(in, buffer);
542 
543  istringstream line(buffer);
544  int tokenCount = 0;
545  while(!(line.eof() || line.fail())){
546  if(tokenCount >= maxNumTokens){
547  maxNumTokens = tokenCount + 1;
548  tokens.resize(maxNumTokens);
549  }
550  line >> tokens[tokenCount];
551  ++tokenCount;
552  }
553 
554  if(tokenCount > 0)
555  {
556  string& tok = tokens[0];
557  if(tok.compare("vertex") == 0){
558  if(tokenCount < 4){
559  STL_READER_THROW("ERROR while reading from " << filename <<
560  ": vertex not specified correctly in line " << lineCount);
561  }
562 
563  // read the position
564  CoordWithIndex <number_t, index_t> c;
565  for(size_t i = 0; i < 3; ++i)
566  c[i] = atof(tokens[i+1].c_str());
567  c.index = static_cast<index_t>(coordsWithIndex.size());
568  coordsWithIndex.push_back(c);
569  ++numFaceVrts;
570  }
571  else if(tok.compare("facet") == 0)
572  {
573  STL_READER_COND_THROW(tokenCount < 5,
574  "ERROR while reading from " << filename <<
575  ": triangle not specified correctly in line " << lineCount);
576 
577  STL_READER_COND_THROW(tokens[1].compare("normal") != 0,
578  "ERROR while reading from " << filename <<
579  ": Missing normal specifier in line " << lineCount);
580 
581  // read the normal
582  for(size_t i = 0; i < 3; ++i)
583  normalsOut.push_back (atof(tokens[i+2].c_str()));
584 
585  numFaceVrts = 0;
586  }
587  else if(tok.compare("outer") == 0){
588  STL_READER_COND_THROW ((tokenCount < 2) || (tokens[1].compare("loop") != 0),
589  "ERROR while reading from " << filename <<
590  ": expecting outer loop in line " << lineCount);
591  }
592  else if(tok.compare("endfacet") == 0){
593  STL_READER_COND_THROW(numFaceVrts != 3,
594  "ERROR while reading from " << filename <<
595  ": bad number of vertices specified for face in line " << lineCount);
596 
597  trisOut.push_back(coordsWithIndex.size() - 3);
598  trisOut.push_back(coordsWithIndex.size() - 2);
599  trisOut.push_back(coordsWithIndex.size() - 1);
600  }
601  else if(tok.compare("solid") == 0){
602  solidRangesOut.push_back(trisOut.size() / 3);
603  }
604  }
605  lineCount++;
606  }
607 
608  solidRangesOut.push_back(trisOut.size() / 3);
609 
610  RemoveDoubles (coordsOut, trisOut, coordsWithIndex);
611 
612  return true;
613 }
614 
615 
616 template <class TNumberContainer, class TIndexContainer>
617 bool ReadStlFile_BINARY(const char* filename,
618  TNumberContainer& coordsOut,
619  TNumberContainer& normalsOut,
620  TIndexContainer& trisOut,
621  TIndexContainer& solidRangesOut)
622 {
623  using namespace std;
624  using namespace stl_reader_impl;
625 
626  typedef typename TNumberContainer::value_type number_t;
627  typedef typename TIndexContainer::value_type index_t;
628 
629  coordsOut.clear();
630  normalsOut.clear();
631  trisOut.clear();
632  solidRangesOut.clear();
633 
634  ifstream in(filename, ios::binary);
635  STL_READER_COND_THROW(!in, "Couldnt open file " << filename);
636 
637  char stl_header[80];
638  in.read(stl_header, 80);
639  STL_READER_COND_THROW(!in, "Error while parsing binary stl header in file " << filename);
640 
641  unsigned int numTris = 0;
642  in.read((char*)&numTris, 4);
643  STL_READER_COND_THROW(!in, "Couldnt determine number of triangles in binary stl file " << filename);
644 
645  vector<CoordWithIndex <number_t, index_t> > coordsWithIndex;
646 
647  for(unsigned int tri = 0; tri < numTris; ++tri){
648  float d[12];
649  in.read((char*)d, 12 * 4);
650  STL_READER_COND_THROW(!in, "Error while parsing trianlge in binary stl file " << filename);
651 
652  for(int i = 0; i < 3; ++i)
653  normalsOut.push_back (d[i]);
654 
655  for(size_t ivrt = 1; ivrt < 4; ++ivrt){
656  CoordWithIndex <number_t, index_t> c;
657  for(size_t i = 0; i < 3; ++i)
658  c[i] = d[ivrt * 3 + i];
659  c.index = static_cast<index_t>(coordsWithIndex.size());
660  coordsWithIndex.push_back(c);
661  }
662 
663  trisOut.push_back(coordsWithIndex.size() - 3);
664  trisOut.push_back(coordsWithIndex.size() - 2);
665  trisOut.push_back(coordsWithIndex.size() - 1);
666 
667  char addData[2];
668  in.read(addData, 2);
669  STL_READER_COND_THROW(!in, "Error while parsing additional triangle data in binary stl file " << filename);
670  }
671 
672  solidRangesOut.push_back(0);
673  solidRangesOut.push_back(trisOut.size() / 3);
674 
675  RemoveDoubles (coordsOut, trisOut, coordsWithIndex);
676 
677  return true;
678 }
679 
680 
681 inline bool StlFileHasASCIIFormat(const char* filename)
682 {
683  using namespace std;
684  ifstream in(filename);
685  STL_READER_COND_THROW(!in, "Couldnt open file " << filename);
686 
687  string firstWord;
688  in >> firstWord;
689  transform(firstWord.begin(), firstWord.end(), firstWord.begin(), ::tolower);
690 
691  return firstWord.compare("solid") == 0;
692 }
693 
694 } // end of namespace stl_reader
695 
696 #endif //__H__STL_READER
convenience mesh class which makes accessing the stl data more easy
Definition: stl_reader.h:230
const TIndex * tri_corner_inds(const size_t ti) const
returns an array of 3 indices, one for each corner vertex of the triangle
Definition: stl_reader.h:290
const TIndex * raw_tris() const
returns a pointer to the triangle array, containing num_tris()*3 entries.
Definition: stl_reader.h:367
const TIndex * raw_solids() const
returns a pointer to the solids array, containing num_solids()+1 entries.
Definition: stl_reader.h:377
std::vector< TIndex > solids
Definition: stl_reader.h:388
const TNumber * tri_corner_coords(const size_t ti, const size_t ci) const
returns an array of 3 floating point values, one for each coordinate of the specified corner of the s...
Definition: stl_reader.h:308
const TNumber * raw_normals() const
returns a pointer to the normal array, containing num_tris()*3 entries.
Definition: stl_reader.h:357
size_t num_solids() const
returns the number of solids of the mesh
Definition: stl_reader.h:325
std::vector< TNumber > coords
Definition: stl_reader.h:385
size_t num_tris() const
returns the number of triangles in the mesh
Definition: stl_reader.h:284
std::vector< TNumber > normals
Definition: stl_reader.h:386
const TNumber * vrt_coords(const size_t vi) const
returns an array of 3 floating point values, one for each coordinate of the vertex
Definition: stl_reader.h:278
StlMesh(const char *filename)
initializes the mesh from the stl-file specified through filename
Definition: stl_reader.h:239
TIndex solid_tris_begin(const size_t si) const
returns the index of the first triangle in the given solid
Definition: stl_reader.h:333
const TIndex tri_corner_ind(const size_t ti, const size_t ci) const
returns the index of the corner with index 0<=ci<3 of triangle ti
Definition: stl_reader.h:296
const TNumber * raw_coords() const
returns a pointer to the coordinate array, containing num_vrts()*3 entries.
Definition: stl_reader.h:347
const TNumber * tri_normal(const size_t ti) const
returns an array of 3 floating point values defining the normal of a tri
Definition: stl_reader.h:314
bool read_file(const char *filename)
fills the mesh with the contents of the specified stl-file
Definition: stl_reader.h:245
StlMesh()
initializes an empty mesh
Definition: stl_reader.h:233
TIndex solid_tris_end(const size_t si) const
returns the index of the triangle behind the last triangle in the given solid
Definition: stl_reader.h:339
size_t num_vrts() const
returns the number of vertices in the mesh
Definition: stl_reader.h:272
std::vector< TIndex > tris
Definition: stl_reader.h:387
bool operator==(const MathVector< N, T > &v, const MathVector< N, T > &w)
Definition: math_vector.h:523
bool operator<(const MathVector< N, T > &v, const MathVector< N, T > &w)
Definition: math_vector.h:541
bool operator!=(const MathVector< N, T > &v, const MathVector< N, T > &w)
Definition: math_vector.h:552
Definition: smart_pointer.h:814
void RemoveDoubles(TNumberContainer &uniqueCoordsOut, TIndexContainer &trisInOut, std::vector< CoordWithIndex< typename TNumberContainer::value_type, typename TIndexContainer::value_type > > &coordsWithIndexInOut)
Definition: stl_reader.h:430
Definition: stl_reader.h:146
bool ReadStlFile_BINARY(const char *filename, TNumberContainer &coordsOut, TNumberContainer &normalsOut, TIndexContainer &trisOut, TIndexContainer &solidRangesOut)
Reads a binary stl file into several arrays.
Definition: stl_reader.h:617
bool ReadStlFile(const char *filename, TNumberContainer &coordsOut, TNumberContainer &normalsOut, TIndexContainer &trisOut, TIndexContainer &solidRangesOut)
Reads an ASCII or binary stl file into several arrays.
Definition: stl_reader.h:494
bool ReadStlFile_ASCII(const char *filename, TNumberContainer &coordsOut, TNumberContainer &normalsOut, TIndexContainer &trisOut, TIndexContainer &solidRangesOut)
Reads an ASCII stl file into several arrays.
Definition: stl_reader.h:508
bool StlFileHasASCIIFormat(const char *filename)
Determines whether a stl file has ASCII format.
Definition: stl_reader.h:681
T value_type
Definition: sparsematrix_interface.h:2
#define STL_READER_COND_THROW(cond, msg)
Throws an std::runtime_error with the given message, if the given condition evaluates to true.
Definition: stl_reader.h:142
#define STL_READER_THROW(msg)
Throws an std::runtime_error with the given message.
Definition: stl_reader.h:139
index_t index
Definition: stl_reader.h:404