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
79 _STLReader_  __STLReaderInstance;
STLReader()80 _STLReader_&  STLReader() { return __STLReaderInstance; }
81 
82 
83 //=== IMPLEMENTATION ==========================================================
84 
85 
86 _STLReader_::
_STLReader_()87 _STLReader_()
88   : eps_(FLT_MIN)
89 {
90   IOManager().register_module(this);
91 }
92 
93 
94 //-----------------------------------------------------------------------------
95 
96 
97 bool
98 _STLReader_::
read(const std::string & _filename,BaseImporter & _bi,Options & _opt)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
read(std::istream & _is,BaseImporter & _bi,Options & _opt)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 
CmpVec(float _eps=FLT_MIN)174   explicit CmpVec(float _eps=FLT_MIN) : eps_(_eps) {}
175 
operator ()(const Vec3f & _v0,const Vec3f & _v1) const176   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 
trimStdString(std::string & _string)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_::
read_stla(const std::string & _filename,BaseImporter & _bi,Options & _opt) const214 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_::
read_stla(std::istream & _in,BaseImporter & _bi,Options & _opt) const238 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_::
read_stlb(const std::string & _filename,BaseImporter & _bi,Options & _opt) const348 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_::
read_stlb(std::istream & _in,BaseImporter & _bi,Options & _opt) const372 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_::
check_stl_type(const std::string & _filename) const451 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 //=============================================================================
516