1 // Copyright (c) 2015 GeometryFactory
2 //
3 // This file is part of CGAL (www.cgal.org);
4 //
5 // $URL: https://github.com/CGAL/cgal/blob/v5.3/Stream_support/include/CGAL/IO/STL/STL_reader.h $
6 // $Id: STL_reader.h fb6f703 2021-05-04T14:07:49+02:00 Sébastien Loriot
7 // SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial
8 //
9 // Author(s)     : Andreas Fabri,
10 //                 Mael Rouxel-Labbé
11 
12 #ifndef CGAL_IO_STL_STL_READER_H
13 #define CGAL_IO_STL_STL_READER_H
14 
15 #include <CGAL/IO/io.h>
16 #include <CGAL/IO/helpers.h>
17 
18 #include <boost/cstdint.hpp>
19 #include <boost/range/value_type.hpp>
20 
21 #include <cctype>
22 #include <iostream>
23 #include <map>
24 #include <string>
25 #include <vector>
26 
27 namespace CGAL {
28 namespace IO {
29 namespace internal {
30 
31 template <class PointRange, class TriangleRange, typename IndexMap>
32 bool read_ASCII_facet(std::istream& is,
33                       PointRange& points,
34                       TriangleRange& facets,
35                       int& index,
36                       IndexMap& index_map,
37                       const bool verbose = false)
38 {
39   typedef typename boost::range_value<PointRange>::type         Point;
40   typedef typename boost::range_value<TriangleRange>::type      Triangle;
41 
42   // Here, we have already read the word 'facet' and are looking to read till 'endfacet'
43 
44   std::string s;
45   std::string vertex("vertex"), endfacet("endfacet");
46 
47   int count = 0;
48   double x,y,z;
49   Point p;
50   Triangle ijk;
51   CGAL::internal::resize(ijk, 3);
52 
53   while(is >> s)
54   {
55     if(s == endfacet)
56     {
57       if(count != 3)
58       {
59         if(verbose)
60           std::cerr << "Error: only triangulated surfaces are supported" << std::endl;
61 
62         return false;
63       }
64 
65       facets.push_back(ijk);
66       return true;
67     }
68     else if(s == vertex)
69     {
70       if(count >= 3)
71       {
72         if(verbose)
73           std::cerr << "Error: only triangulated surfaces are supported" << std::endl;
74 
75         return false;
76       }
77 
78       if(!(is >> iformat(x) >> iformat(y) >> iformat(z)))
79       {
80         if(verbose)
81           std::cerr << "Error while reading point coordinates (premature end of file)" << std::endl;
82 
83         return false;
84       }
85       else
86       {
87         fill_point(x, y, z, 1 /*w*/, p);
88         typename std::map<Point, int>::iterator iti = index_map.insert(std::make_pair(p, -1)).first;
89 
90         if(iti->second == -1)
91         {
92           ijk[count] = index;
93           iti->second = index++;
94           points.push_back(p);
95         }
96         else
97         {
98           ijk[count] = iti->second;
99         }
100       }
101 
102       ++count;
103     }
104   }
105 
106   if(verbose)
107     std::cerr << "Error while reading facet (premature end of file)" << std::endl;
108 
109   return false;
110 }
111 
112 template <class PointRange, class TriangleRange>
113 bool parse_ASCII_STL(std::istream& is,
114                      PointRange& points,
115                      TriangleRange& facets,
116                      const bool verbose = false)
117 {
118   typedef typename boost::range_value<PointRange>::type           Point;
119   bool solid_found = false;
120   if(verbose)
121     std::cout << "Parsing ASCII file..." << std::endl;
122 
123   if(!is.good())
124     return false;
125 
126   // Here, we have already read the word 'solid'
127 
128   int index = 0;
129   std::map<Point, int> index_map;
130 
131   std::string s, facet("facet"), endsolid("endsolid"), solid("solid");
132   bool in_solid(false);
133   while(is >> s)
134   {
135     if(s == solid)
136     {
137       if(in_solid)
138         break;
139 
140       in_solid = true;
141     }
142     if(s == facet)
143     {
144       if(!read_ASCII_facet(is, points, facets, index, index_map, verbose))
145         return false;
146     }
147     else if(s == endsolid)
148     {
149       in_solid = false;
150       solid_found = true;
151     }
152   }
153 
154   if(in_solid)
155   {
156     if(verbose)
157       std::cerr << "Error while parsing ASCII file" << std::endl;
158 
159     return false;
160   }
161 
162   return solid_found && !in_solid;
163 }
164 
165 template <class PointRange, class TriangleRange>
166 bool parse_binary_STL(std::istream& is,
167                       PointRange& points,
168                       TriangleRange& facets,
169                       const bool verbose = false)
170 {
171   typedef typename boost::range_value<PointRange>::type         Point;
172   typedef typename boost::range_value<TriangleRange>::type      Triangle;
173 
174   if(verbose)
175     std::cout << "Parsing binary file..." << std::endl;
176 
177   // Start from the beginning again to simplify things
178   is.clear();
179   is.seekg(0, std::ios::beg);
180 
181   if(!is.good())
182     return false;
183 
184   // Discard the first 80 chars (unused header)
185   int pos = 0;
186   char c;
187 
188   if(verbose)
189     std::cout << "header: ";
190 
191   while(pos < 80)
192   {
193     is.read(reinterpret_cast<char*>(&c), sizeof(c));
194     if(!is.good())
195       break;
196 
197     if(verbose)
198       std::cout << c;
199 
200     ++pos;
201   }
202 
203   if(verbose)
204     std::cout << std::endl;
205 
206   if(pos != 80)
207     return true; // empty file
208 
209   int index = 0;
210   std::map<Point, int> index_map;
211 
212   boost::uint32_t N32;
213   if(!(is.read(reinterpret_cast<char*>(&N32), sizeof(N32))))
214   {
215     if(verbose)
216       std::cerr << "Error while reading number of facets" << std::endl;
217 
218     return false;
219   }
220 
221   unsigned int N = N32;
222   if(verbose)
223     std::cout << N << " facets to read" << std::endl;
224 
225   for(unsigned int i=0; i<N; ++i)
226   {
227     float normal[3];
228     if(!(is.read(reinterpret_cast<char*>(&normal[0]), sizeof(normal[0]))) ||
229        !(is.read(reinterpret_cast<char*>(&normal[1]), sizeof(normal[1]))) ||
230        !(is.read(reinterpret_cast<char*>(&normal[2]), sizeof(normal[2]))))
231     {
232       if(verbose)
233         std::cerr << "Error while reading normal coordinates (premature end of file)" << std::endl;
234 
235       return false;
236     }
237 
238     Triangle ijk;
239     CGAL::internal::resize(ijk, 3);
240 
241     for(int j=0; j<3; ++j)
242     {
243       float x,y,z;
244       if(!(is.read(reinterpret_cast<char*>(&x), sizeof(x))) ||
245          !(is.read(reinterpret_cast<char*>(&y), sizeof(y))) ||
246          !(is.read(reinterpret_cast<char*>(&z), sizeof(z))))
247       {
248         if(verbose)
249           std::cerr << "Error while reading vertex coordinates (premature end of file)" << std::endl;
250 
251         return false;
252       }
253 
254       Point p;
255       fill_point(x, y, z, 1 /*w*/, p);
256 
257       typename std::map<Point, int>::iterator iti = index_map.insert(std::make_pair(p, -1)).first;
258 
259       if(iti->second == -1)
260       {
261         ijk[j] = index;
262         iti->second = index++;
263         points.push_back(p);
264       }
265       else
266       {
267         ijk[j] = iti->second;
268       }
269     }
270 
271     facets.push_back(ijk);
272 
273     // Read so-called attribute byte count and ignore it
274     char c;
275     if(!(is.read(reinterpret_cast<char*>(&c), sizeof(c))) ||
276        !(is.read(reinterpret_cast<char*>(&c), sizeof(c))))
277     {
278       if(verbose)
279         std::cerr << "Error while reading attribute byte count (premature end of file)" << std::endl;
280 
281       return false;
282     }
283   }
284 
285   return !is.fail();
286 }
287 
288 } // namespace internal
289 } // namespace IO
290 } // namespace CGAL
291 
292 #endif // CGAL_IO_STL_STL_READER_H
293