Line data Source code
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 :
26 : /** \file
27 : * \brief Provides functions to read **stl files** into user provided arrays
28 : *
29 : * The central function of this file is `ReadStlFile(...)`. It automatically recognizes
30 : * whether an *ASCII* or a *Binary* file is to be read. It identifies matching corner
31 : * coordinates of triangles with each other, so that the resulting coordinate
32 : * array does not contain the same coordinate-triple multiple times.
33 : *
34 : * The function operates on template container types. Those containers should
35 : * have similar interfaces as `std::vector` and operate on `float` or `double` types
36 : * (`TNumberContainer`) or on `int` or `size_t` types (`TIndexContainer`).
37 : *
38 : *
39 : * A conveniance class `StlMesh` is also provided, which makes accessing triangle
40 : * corners and corresponding corner coordinates much more easy. It still provides
41 : * raw access to the underlying data arrays.
42 : *
43 : *
44 : * ### Usage example 1 (using `StlMesh`):
45 : *
46 : * \code
47 : * try {
48 : * stl_reader::StlMesh <float, unsigned int> mesh ("geometry.stl");
49 : *
50 : * for(size_t itri = 0; itri < mesh.num_tris(); ++itri) {
51 : * std::cout << "coordinates of triangle " << itri << ": ";
52 : * for(size_t icorner = 0; icorner < 3; ++icorner) {
53 : * float* c = mesh.vrt_coords (mesh.tri_corner_ind (itri, icorner));
54 : * std::cout << "(" << c[0] << ", " << c[1] << ", " << c[2] << ") ";
55 : * }
56 : * std::cout << std::endl;
57 : *
58 : * float* n = mesh.tri_normal (itri);
59 : * std::cout << "normal of triangle " << itri << ": "
60 : * << "(" << n[0] << ", " << n[1] << ", " << n[2] << ")\n";
61 : * }
62 : * }
63 : * catch (std::exception& e) {
64 : * std::cout << e.what() << std::endl;
65 : * }
66 : * \endcode
67 : *
68 : *
69 : * ### Usage example 2 (using `StlMesh` and *solids*)
70 : *
71 : * \code
72 : * try {
73 : * stl_reader::StlMesh <float, unsigned int> mesh ("geometry.stl");
74 : *
75 : * for(size_t isolid = 0; isolid < mesh.num_solids(); ++isolid) {
76 : * std::cout << "solid " << isolid << std::endl;
77 : *
78 : * for(size_t itri = mesh.solid_tris_begin(isolid);
79 : * itri < mesh.solid_tris_end(isolid); ++itri)
80 : * {
81 : * const float* n = mesh.tri_normal (itri);
82 : * std::cout << "normal of triangle " << itri << ": "
83 : * << "(" << n[0] << ", " << n[1] << ", " << n[2] << ")\n";
84 : * }
85 : * }
86 : * }
87 : * catch (std::exception& e) {
88 : * std::cout << e.what() << std::endl;
89 : * }
90 : * \endcode
91 : *
92 : *
93 : * ### Usage example 3 (using raw data arrays)
94 : *
95 : * \code
96 : * std::vector<float> coords, normals;
97 : * std::vector<unsigned int> tris, solids;
98 : *
99 : * try {
100 : * stl_reader::ReadStlFile ("geometry.stl", coords, normals, tris, solids);
101 : * const size_t numTris = tris.size() / 3;
102 : * for(size_t itri = 0; itri < numTris; ++itri) {
103 : * std::cout << "coordinates of triangle " << itri << ": ";
104 : * for(size_t icorner = 0; icorner < 3; ++icorner) {
105 : * float* c = &coords[3 * tris [3 * itri + icorner]];
106 : * std::cout << "(" << c[0] << ", " << c[1] << ", " << c[2] << ") ";
107 : * }
108 : * std::cout << std::endl;
109 : *
110 : * float* n = &normals [3 * itri];
111 : * std::cout << "normal of triangle " << itri << ": "
112 : * << "(" << n[0] << ", " << n[1] << ", " << n[2] << ")\n";
113 : * }
114 : * }
115 : * catch (std::exception& e) {
116 : * std::cout << e.what() << std::endl;
117 : * }
118 : * \endcode
119 : *
120 : * If you do not want to use exceptions, you may define the macro
121 : * STL_READER_NO_EXCEPTIONS before including 'stl_reader.h'. In that case,
122 : * functions will return `false` if an error occurred.
123 : */
124 :
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
138 : /// Throws an std::runtime_error with the given message.
139 : #define STL_READER_THROW(msg) {std::stringstream ss; ss << msg; throw(std::runtime_error(ss.str()));}
140 :
141 : /// Throws an std::runtime_error with the given message, if the given condition evaluates to true.
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 :
148 : /// Reads an ASCII or binary stl file into several arrays
149 : /** Reads a stl file and writes its coordinates, normals and triangle-corner-indices
150 : * to the provided containers. It also fills a container solidRangesOut, which
151 : * provides the triangle ranges for individual solids.
152 : *
153 : * Double vertex entries are removed on the fly, so that triangle corners with
154 : * equal coordinates are represented by a single coordinate entry in coordsOut.
155 : *
156 : *
157 : * \param filename [in] The name of the file which shall be read
158 : *
159 : * \param coordsOut [out] Coordinates are written to this container. On termination,
160 : * it has size numVertices * 3. Each triple of entries forms a
161 : * 3d coordinate. The type TNumberContainer should have the same
162 : * interface as std::vector<float>.
163 : *
164 : * \param normalsOut [out] Face normals are written to this container. On termination,
165 : * it has size numFaces * 3. Each triple of entries forms a
166 : * 3d normal. The type TNumberContainer should have the same
167 : * interface as std::vector<float>.
168 : *
169 : * \param trisOut [out] Triangle corner indices are written to this container.
170 : * On termination, it has size numFaces * 3. Each triple of
171 : * entries defines a triangle. The type TIndexContainer should
172 : * have the same interface as std::vector<size_t>.
173 : * Multiply corner indices from trisOut by 3 to obtain the index
174 : * of the first coordinate of that corner in coordsOut.
175 : *
176 : * \param solidRangesOut [out] On termination, it holds the ranges of triangle indices
177 : * for each solid. It has the size numSolids + 1. Each entry
178 : * can be interpreted as a end/begin triangle index for the
179 : * previous/next solid. E.g., if there are 3 solids, the
180 : * returned array would look like this:
181 : * \code
182 : * {sol1Begin, sol1End/sol2Begin, sol2End/sol3Begin, sol3End}.
183 : * \endcode
184 : * The type TIndexContainer should have the same interface
185 : * as std::vector<size_t>.
186 : *
187 : * \returns true if the file was successfully read into the provided container.
188 : */
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 :
197 : /// Reads an ASCII stl file into several arrays
198 : /** \copydetails ReadStlFile
199 : * \sa ReadStlFile, ReadStlFile_ASCII
200 : */
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 :
208 : /// Reads a binary stl file into several arrays
209 : /** \copydetails ReadStlFile
210 : * \todo support systems with big endianess
211 : * \sa ReadStlFile, ReadStlFile_BINARY
212 : */
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 :
220 : /// Determines whether a stl file has ASCII format
221 : /** The underlying mechanism is simply checks whether the provided file starts
222 : * with the keyword solid. This should work for many stl files, but may
223 : * fail, of course.
224 : */
225 : inline bool StlFileHasASCIIFormat(const char* filename);
226 :
227 :
228 : /// convenience mesh class which makes accessing the stl data more easy
229 : template <class TNumber = float, class TIndex = unsigned int>
230 : class StlMesh {
231 : public:
232 : /// initializes an empty mesh
233 : StlMesh ()
234 : {
235 : solids.resize (2, 0);
236 : }
237 :
238 : /// initializes the mesh from the stl-file specified through filename
239 : StlMesh (const char* filename)
240 : {
241 : read_file (filename);
242 : }
243 :
244 : /// fills the mesh with the contents of the specified stl-file
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 :
271 : /// returns the number of vertices in the mesh
272 : size_t num_vrts () const
273 : {
274 : return coords.size() / 3;
275 : }
276 :
277 : /// returns an array of 3 floating point values, one for each coordinate of the vertex
278 : const TNumber* vrt_coords (const size_t vi) const
279 : {
280 : return &coords[vi * 3];
281 : }
282 :
283 : /// returns the number of triangles in the mesh
284 : size_t num_tris () const
285 : {
286 : return tris.size() / 3;
287 : }
288 :
289 : /// returns an array of 3 indices, one for each corner vertex of the triangle
290 : const TIndex* tri_corner_inds (const size_t ti) const
291 : {
292 : return &tris [ti * 3];
293 : }
294 :
295 : /// returns the index of the corner with index `0<=ci<3` of triangle ti
296 : const TIndex tri_corner_ind (const size_t ti, const size_t ci) const
297 : {
298 : return tris [ti * 3 + ci];
299 : }
300 :
301 : /** \brief returns an array of 3 floating point values, one for each
302 : * coordinate of the specified corner of the specified tri.
303 : * \note same result as calling on a `StlMesh mesh`:
304 : * \code
305 : * mesh.vrt_coords (mesh.tri_corner_ind (itri, icorner))
306 : * \endcode
307 : */
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 :
313 : /// returns an array of 3 floating point values defining the normal of a tri
314 : const TNumber* tri_normal (const size_t ti) const
315 : {
316 : return &normals [ti * 3];
317 : }
318 :
319 : /// returns the number of solids of the mesh
320 : /** solids can be seen as a partitioning of the triangles of a mesh.
321 : * By iterating consecutively from the index of the first triangle of a
322 : * solid `si` (using `solid_tris_begin(si)`) to the index of the last
323 : * triangle of a solid (using `solid_tris_end(...)-1`), one visits all
324 : * triangles of the solid `si`.*/
325 : size_t num_solids () const
326 : {
327 : if(solids.empty ())
328 : return 0;
329 : return solids.size () - 1;
330 : }
331 :
332 : /// returns the index of the first triangle in the given solid
333 : TIndex solid_tris_begin (const size_t si) const
334 : {
335 : return solids [si];
336 : }
337 :
338 : /// returns the index of the triangle behind the last triangle in the given solid
339 : TIndex solid_tris_end (const size_t si) const
340 : {
341 : return solids [si + 1];
342 : }
343 :
344 : /// returns a pointer to the coordinate array, containing `num_vrts()*3` entries.
345 : /** Storage layout: `x0,y0,z0,x1,y1,z1,...`
346 : * \returns pointer to a contiguous array of numbers, or `NULL` if no coords exist.*/
347 : const TNumber* raw_coords () const
348 : {
349 : if(coords.empty())
350 : return NULL;
351 : return &coords[0];
352 : }
353 :
354 : /// returns a pointer to the normal array, containing `num_tris()*3` entries.
355 : /** Storage layout: `nx0,ny0,nz0,nx1,ny1,nz1,...`
356 : * \returns pointer to a contiguous array of numbers, or `NULL` if no normals exist.*/
357 : const TNumber* raw_normals () const
358 : {
359 : if(normals.empty())
360 : return NULL;
361 : return &normals[0];
362 : }
363 :
364 : /// returns a pointer to the triangle array, containing `num_tris()*3` entries.
365 : /** Storage layout: `t0c0,t0c1,t0c2,t1c0,t1c1,t1c2,...`
366 : * \returns pointer to a contiguous array of indices, or `NULL` if no tris exist.*/
367 : const TIndex* raw_tris () const
368 : {
369 : if(tris.empty())
370 : return NULL;
371 : return &tris[0];
372 : }
373 :
374 : /// returns a pointer to the solids array, containing `num_solids()+1` entries.
375 : /** Storage layout: `s0begin, s0end/s1begin, s1end/s2begin, ..., sNend`
376 : * \returns pointer to a contiguous array of indices, or `NULL` if no solids exist.*/
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 :
392 : ////////////////////////////////////////////////////////////////////////////////
393 : // IMPLEMENTATION
394 : ////////////////////////////////////////////////////////////////////////////////
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 0 : return (c[0] != data[0]) || (c[1] != data[1]) || (c[2] != data[2]);
414 : }
415 :
416 : bool operator < (const CoordWithIndex& c) const
417 : {
418 0 : return (data[0] < c[0])
419 0 : || (data[0] == c[0] && data[1] < c[1])
420 0 : || (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 0 : 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 0 : 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 0 : sort (coordsWithIndexInOut.begin(), coordsWithIndexInOut.end());
443 :
444 : // first count unique indices
445 : size_t numUnique = 1;
446 0 : for(size_t i = 1; i < coordsWithIndexInOut.size(); ++i){
447 0 : if(coordsWithIndexInOut[i] != coordsWithIndexInOut[i - 1])
448 0 : ++numUnique;
449 : }
450 :
451 0 : uniqueCoordsOut.resize (numUnique * 3);
452 0 : 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 0 : newIndex[0] = 0;
458 0 : for(size_t i = 0; i < 3; ++i)
459 0 : uniqueCoordsOut[i] = coordsWithIndexInOut[0][i];
460 :
461 0 : for(size_t i = 1; i < coordsWithIndexInOut.size(); ++i){
462 0 : const CoordWithIndex <number_t, index_t> c = coordsWithIndexInOut[i];
463 0 : if(c != coordsWithIndexInOut[i - 1]){
464 0 : ++curInd;
465 0 : for(size_t j = 0; j < 3; ++j)
466 0 : uniqueCoordsOut[curInd * 3 + j] = coordsWithIndexInOut[i][j];
467 : }
468 :
469 0 : 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 0 : for(size_t i = 0; i < trisInOut.size(); i+=3){
476 : int ni[3];
477 0 : for(int j = 0; j < 3; ++j)
478 0 : ni[j] = newIndex[trisInOut[i+j]];
479 :
480 0 : if((ni[0] != ni[1]) && (ni[0] != ni[2]) && (ni[1] != ni[2])){
481 0 : for(int j = 0; j < 3; ++j)
482 0 : trisInOut[numUniqueTriInds + j] = ni[j];
483 0 : numUniqueTriInds += 3;
484 : }
485 : }
486 :
487 0 : if(numUniqueTriInds < trisInOut.size())
488 0 : trisInOut.resize (numUniqueTriInds);
489 0 : }
490 : }// end of namespace stl_reader_impl
491 :
492 :
493 : template <class TNumberContainer, class TIndexContainer>
494 0 : bool ReadStlFile(const char* filename,
495 : TNumberContainer& coordsOut,
496 : TNumberContainer& normalsOut,
497 : TIndexContainer& trisOut,
498 : TIndexContainer& solidRangesOut)
499 : {
500 0 : if(StlFileHasASCIIFormat(filename))
501 0 : return ReadStlFile_ASCII(filename, coordsOut, normalsOut, trisOut, solidRangesOut);
502 : else
503 0 : return ReadStlFile_BINARY(filename, coordsOut, normalsOut, trisOut, solidRangesOut);
504 : }
505 :
506 :
507 : template <class TNumberContainer, class TIndexContainer>
508 0 : 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 0 : ifstream in(filename);
526 0 : 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 0 : 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 0 : getline(in, buffer);
542 :
543 0 : istringstream line(buffer);
544 : int tokenCount = 0;
545 0 : while(!(line.eof() || line.fail())){
546 0 : if(tokenCount >= maxNumTokens){
547 0 : maxNumTokens = tokenCount + 1;
548 0 : tokens.resize(maxNumTokens);
549 : }
550 0 : line >> tokens[tokenCount];
551 0 : ++tokenCount;
552 : }
553 :
554 0 : if(tokenCount > 0)
555 : {
556 : string& tok = tokens[0];
557 0 : if(tok.compare("vertex") == 0){
558 0 : if(tokenCount < 4){
559 0 : 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 0 : for(size_t i = 0; i < 3; ++i)
566 0 : c[i] = atof(tokens[i+1].c_str());
567 0 : c.index = static_cast<index_t>(coordsWithIndex.size());
568 0 : coordsWithIndex.push_back(c);
569 0 : ++numFaceVrts;
570 : }
571 0 : else if(tok.compare("facet") == 0)
572 : {
573 0 : STL_READER_COND_THROW(tokenCount < 5,
574 : "ERROR while reading from " << filename <<
575 : ": triangle not specified correctly in line " << lineCount);
576 :
577 0 : 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 0 : for(size_t i = 0; i < 3; ++i)
583 0 : normalsOut.push_back (atof(tokens[i+2].c_str()));
584 :
585 : numFaceVrts = 0;
586 : }
587 0 : else if(tok.compare("outer") == 0){
588 0 : 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 0 : else if(tok.compare("endfacet") == 0){
593 0 : 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 0 : trisOut.push_back(coordsWithIndex.size() - 3);
598 0 : trisOut.push_back(coordsWithIndex.size() - 2);
599 0 : trisOut.push_back(coordsWithIndex.size() - 1);
600 : }
601 0 : else if(tok.compare("solid") == 0){
602 0 : solidRangesOut.push_back(trisOut.size() / 3);
603 : }
604 : }
605 0 : lineCount++;
606 : }
607 :
608 0 : solidRangesOut.push_back(trisOut.size() / 3);
609 :
610 0 : RemoveDoubles (coordsOut, trisOut, coordsWithIndex);
611 :
612 0 : return true;
613 0 : }
614 :
615 :
616 : template <class TNumberContainer, class TIndexContainer>
617 0 : 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 0 : ifstream in(filename, ios::binary);
635 0 : STL_READER_COND_THROW(!in, "Couldnt open file " << filename);
636 :
637 : char stl_header[80];
638 0 : in.read(stl_header, 80);
639 0 : STL_READER_COND_THROW(!in, "Error while parsing binary stl header in file " << filename);
640 :
641 0 : unsigned int numTris = 0;
642 0 : in.read((char*)&numTris, 4);
643 0 : 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 0 : for(unsigned int tri = 0; tri < numTris; ++tri){
648 : float d[12];
649 0 : in.read((char*)d, 12 * 4);
650 0 : STL_READER_COND_THROW(!in, "Error while parsing trianlge in binary stl file " << filename);
651 :
652 0 : for(int i = 0; i < 3; ++i)
653 0 : normalsOut.push_back (d[i]);
654 :
655 0 : for(size_t ivrt = 1; ivrt < 4; ++ivrt){
656 : CoordWithIndex <number_t, index_t> c;
657 0 : for(size_t i = 0; i < 3; ++i)
658 0 : c[i] = d[ivrt * 3 + i];
659 0 : c.index = static_cast<index_t>(coordsWithIndex.size());
660 0 : coordsWithIndex.push_back(c);
661 : }
662 :
663 0 : trisOut.push_back(coordsWithIndex.size() - 3);
664 0 : trisOut.push_back(coordsWithIndex.size() - 2);
665 0 : trisOut.push_back(coordsWithIndex.size() - 1);
666 :
667 : char addData[2];
668 0 : in.read(addData, 2);
669 0 : STL_READER_COND_THROW(!in, "Error while parsing additional triangle data in binary stl file " << filename);
670 : }
671 :
672 0 : solidRangesOut.push_back(0);
673 0 : solidRangesOut.push_back(trisOut.size() / 3);
674 :
675 0 : RemoveDoubles (coordsOut, trisOut, coordsWithIndex);
676 :
677 0 : return true;
678 0 : }
679 :
680 :
681 0 : inline bool StlFileHasASCIIFormat(const char* filename)
682 : {
683 : using namespace std;
684 0 : ifstream in(filename);
685 0 : STL_READER_COND_THROW(!in, "Couldnt open file " << filename);
686 :
687 : string firstWord;
688 0 : in >> firstWord;
689 : transform(firstWord.begin(), firstWord.end(), firstWord.begin(), ::tolower);
690 :
691 0 : return firstWord.compare("solid") == 0;
692 0 : }
693 :
694 : } // end of namespace stl_reader
695 :
696 : #endif //__H__STL_READER
|