Loading [MathJax]/extensions/tex2jax.js
ug4
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
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
146namespace stl_reader {
147
149
189template <class TNumberContainer, class TIndexContainer>
190bool ReadStlFile(const char* filename,
191 TNumberContainer& coordsOut,
192 TNumberContainer& normalsOut,
193 TIndexContainer& trisOut,
194 TIndexContainer& solidRangesOut);
195
196
198
201template <class TNumberContainer, class TIndexContainer>
202bool ReadStlFile_ASCII(const char* filename,
203 TNumberContainer& coordsOut,
204 TNumberContainer& normalsOut,
205 TIndexContainer& trisOut,
206 TIndexContainer& solidRangesOut);
207
209
213template <class TNumberContainer, class TIndexContainer>
214bool ReadStlFile_BINARY(const char* filename,
215 TNumberContainer& coordsOut,
216 TNumberContainer& normalsOut,
217 TIndexContainer& trisOut,
218 TIndexContainer& solidRangesOut);
219
221
225inline bool StlFileHasASCIIFormat(const char* filename);
226
227
229template <class TNumber = float, class TIndex = unsigned int>
230class StlMesh {
231public:
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
384private:
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
397namespace 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>
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<
433 typename TNumberContainer::value_type,
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
493template <class TNumberContainer, class TIndexContainer>
494bool 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
507template <class TNumberContainer, class TIndexContainer>
508bool 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
616template <class TNumberContainer, class TIndexContainer>
617bool 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
681inline 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 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
std::vector< TIndex > solids
Definition stl_reader.h:388
size_t num_solids() const
returns the number of solids of the mesh
Definition stl_reader.h:325
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_coords() const
returns a pointer to the coordinate array, containing num_vrts()*3 entries.
Definition stl_reader.h:347
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 * raw_normals() const
returns a pointer to the normal array, containing num_tris()*3 entries.
Definition stl_reader.h:357
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 TIndex * raw_tris() const
returns a pointer to the triangle array, containing num_tris()*3 entries.
Definition stl_reader.h:367
bool read_file(const char *filename)
fills the mesh with the contents of the specified stl-file
Definition stl_reader.h:245
const TIndex * raw_solids() const
returns a pointer to the solids array, containing num_solids()+1 entries.
Definition stl_reader.h:377
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
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 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
Definition smart_pointer.h:814
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
#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