Loading...
Searching...
No Matches
STLAsciiParseManual.C
Go to the documentation of this file.
1/*--------------------------------*- C++ -*----------------------------------*\
2 ========= |
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4 \\ / O peration |
5 \\ / A nd | www.openfoam.com
6 \\/ M anipulation |
7-------------------------------------------------------------------------------
8 Copyright (C) 2018-2023 OpenCFD Ltd.
9-------------------------------------------------------------------------------
10License
11 This file is part of OpenFOAM.
12
13 OpenFOAM is free software: you can redistribute it and/or modify it
14 under the terms of the GNU General Public License as published by
15 the Free Software Foundation, either version 3 of the License, or
16 (at your option) any later version.
17
18 OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
19 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
20 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21 for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
25
26Description
27 Hand-written parsing of STL ASCII format
28
29\*---------------------------------------------------------------------------*/
30
32#include "STLReader.H"
33#include "stringOps.H"
34
35// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
36
37static inline std::string perrorEOF(std::string expected)
38{
39 return "Premature EOF while reading '" + expected + "'";
40}
41
42
43static inline std::string perrorParse(std::string expected, std::string found)
44{
45 return "Parse error. Expecting '" + expected + "' found '" + found + "'";
46}
47
48
49/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
50
51namespace Foam
52{
53namespace Detail
54{
55
56//- A lexer for parsing STL ASCII files.
57// Returns DynamicList(s) of points and facets (zoneIds).
58// The facets are within a solid/endsolid grouping
60:
62{
63 enum scanState
64 {
65 scanSolid = 0,
66 scanFacet,
67 scanLoop,
68 scanVerts,
69 scanEndLoop,
70 scanEndFacet,
71 scanEndSolid
72 };
73
74 scanState state_;
75
76 std::string errMsg_;
77
78 //- Like std:csub_match
79 typedef std::pair<const char*, const char*> tokenType;
80
81 // Tokenized line
83
84 //- Tokenize
85 inline std::string::size_type tokenize(const char *p, const char *pe)
86 {
87 const char* start = p;
88 tokens_.clear();
89
90 // Find not space
91 while (p < pe && isspace(*p))
92 {
93 if (*p == '\n' && lineNum_)
94 {
95 ++lineNum_;
96 }
97 ++p;
98 }
99
100 while (p != pe)
101 {
102 const char* beg = p;
103
104 // Find space
105 while (p < pe && !isspace(*p))
106 {
107 ++p;
108 }
109 tokens_.emplace_back(beg, p);
110
111 // Find next
112 while (p < pe && isspace(*p))
113 {
114 if (*p == '\n')
115 {
116 ++lineNum_;
117 return (p - start);
118 }
119 ++p;
120 }
121 }
122
123 return (p - start);
124 }
125
126
127public:
128
129 //- Construct with the estimated number of triangles in the STL
130 STLAsciiParseManual(const label nTrisEstimated)
131 :
132 Detail::STLAsciiParse(nTrisEstimated)
133 {}
134
135 //- Execute parser
136 void execute(std::istream& is);
137};
138
139} // End namespace Detail
140} // End namespace Foam
141
142
143/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
144
145// Length of the input read buffer
146#define INBUFLEN 16384
147
149{
150 if (!is)
152 return;
154
155 // Buffering
156 char inbuf[INBUFLEN];
157 std::streamsize pending = 0;
158
159 lineNum_ = 0;
160
161 state_ = scanSolid;
162 errMsg_.clear();
163
164 // Line-oriented processing loop
165 while (is)
166 {
167 if (pending >= INBUFLEN)
168 {
169 // We overfilled the buffer while trying to scan a token...
171 << "buffer full while scanning near line " << lineNum_ << nl;
172 break;
173 }
174
175 char *data = inbuf + pending; // current data buffer
176 const std::streamsize buflen = INBUFLEN - pending; // space in buffer
177
178 is.read(data, buflen);
179 const std::streamsize gcount = is.gcount();
180
181 if (gcount <= 0)
182 {
183 // EOF
184 // If scanning for next "solid" this is a valid way to exit, but
185 // an error if scanning for the initial "solid" or any other token
186
187 switch (state_)
188 {
189 case scanSolid:
190 {
191 if (!lineNum_) errMsg_ = perrorEOF("solid");
192 break;
193 }
194 case scanFacet: { errMsg_ = perrorEOF("facet"); break; }
195 case scanLoop: { errMsg_ = perrorEOF("outer loop"); break; }
196 case scanVerts: { errMsg_ = perrorEOF("vertex"); break; }
197 case scanEndLoop: { errMsg_ = perrorEOF("endloop"); break; }
198 case scanEndFacet: { errMsg_ = perrorEOF("endfacet"); break; }
199 case scanEndSolid: { errMsg_ = perrorEOF("endsolid"); break; }
200 }
201
202 // Terminate the parsing loop
203 break;
204 }
205
206 // p,pe = Ragel parsing point and parsing end (default naming)
207 // eof = Ragel EOF point (default naming)
208
209 char *p = inbuf;
210 char *pe = data + gcount;
211
212 // Line-oriented: search backwards to find last newline
213 {
214 --pe;
215 while (*pe != '\n' && pe >= inbuf)
216 {
217 --pe;
218 }
219 ++pe;
220 }
221
222 std::string cmd;
223 do
224 {
225 // Tokenize
226 const auto parsedLen = tokenize(p, pe);
227 p += parsedLen;
228 if (!parsedLen || tokens_.empty())
229 {
230 break;
231 }
232
233 // Ensure consistent case on the first token
234 cmd.assign(tokens_[0].first, tokens_[0].second);
235 stringOps::lower(cmd);
236
237 // Handle all expected parse states
238 switch (state_)
239 {
240 case scanSolid:
241 {
242 if (cmd == "solid")
243 {
244 if (tokens_.empty())
245 {
247 }
248 else
249 {
251 (
253 (
254 tokens_[1].first,
255 tokens_[1].second
256 )
257 );
258 }
259
260 state_ = scanFacet; // Next state
261 }
262 else
263 {
264 errMsg_ = perrorParse("solid", cmd);
265 }
266 break;
267 }
268 case scanFacet:
269 {
270 if (cmd == "color")
271 {
272 // Optional 'color' entry (after solid)
273 // - continue looking for 'facet'
274 continue;
275 }
276 else if (cmd == "facet")
277 {
278 beginFacet();
279 state_ = scanLoop; // Next state
280 }
281 else if (cmd == "endsolid")
282 {
283 // Finished with 'endsolid' - find next solid
284 state_ = scanSolid;
285 }
286 else
287 {
288 errMsg_ = perrorParse("facet", cmd);
289 }
290 break;
291 }
292 case scanLoop:
293 {
294 if (cmd == "outer")
295 {
296 // More pedantic would with (tokens_[1] == "loop") too
297 state_ = scanVerts; // Next state
298 }
299 else
300 {
301 errMsg_ = perrorParse("outer loop", cmd);
302 }
303 break;
304 }
305 case scanVerts:
306 {
307 if (cmd == "vertex")
308 {
309 if (tokens_.size() > 3)
310 {
311 // The tokens are space-delimited and thus okay
312 // for atof()
313 addVertexComponent(tokens_[1].first);
314 addVertexComponent(tokens_[2].first);
315 addVertexComponent(tokens_[3].first);
316 }
317 else
318 {
319 errMsg_ = "Error parsing vertex value";
320 }
321 }
322 else if (cmd == "endloop")
323 {
324 state_ = scanEndFacet; // Next state
325 }
326 else
327 {
328 errMsg_ = perrorParse("vertex", cmd);
329 }
330 break;
331 }
332 case scanEndLoop:
333 {
334 if (cmd == "endloop")
335 {
336 state_ = scanEndFacet; // Next state
337 }
338 else
339 {
340 errMsg_ = perrorParse("endloop", cmd);
341 }
342 break;
343 }
344 case scanEndFacet:
345 {
346 if (cmd == "endfacet")
347 {
348 endFacet();
349 state_ = scanFacet; // Next facet, or endsolid
350 }
351 else
352 {
353 errMsg_ = perrorParse("endfacet", cmd);
354 }
355 break;
356 }
357 case scanEndSolid:
358 {
359 if (cmd == "endsolid")
360 {
361 state_ = scanSolid; // Start over again
362 }
363 else
364 {
365 errMsg_ = perrorParse("endsolid", cmd);
366 }
367 break;
368 }
369 }
370 }
371 while (errMsg_.empty());
372
373 // How much still in the buffer?
374 pending = data + gcount - pe;
375
376 if (pending)
377 {
378 memmove(inbuf, pe, pending);
379 }
380
381 if (gcount < buflen)
382 {
383 break; // done
384 }
385
386 if (!errMsg_.empty())
387 {
388 break;
389 }
390 }
391
392 if (!errMsg_.empty())
393 {
395 << errMsg_ << nl;
396 }
397}
398
399
400// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
401
402bool Foam::fileFormats::STLReader::readAsciiManual
403(
404 const fileName& filename
405)
406{
407 IFstream is(filename);
408 if (!is)
409 {
411 << "file " << filename << " not found"
412 << exit(FatalError);
413 }
414
415 // Create with estimated number of triangles in the STL.
416 // 180 bytes / triangle. For simplicity, ignore compression
417
418 const auto fileLen = is.fileSize();
419
420 const label nTrisEstimated =
421 (
422 (fileLen > 0)
423 ? max(label(100), label(fileLen/180))
424 : label(100)
425 );
426
427
428 Detail::STLAsciiParseManual lexer(nTrisEstimated);
429 lexer.execute(is.stdStream());
430
431 transfer(lexer);
432
433 return true;
434}
435
436
437// ************************************************************************* //
#define INBUFLEN
static std::string perrorParse(std::string expected, std::string found)
static std::string perrorEOF(std::string expected)
bool found
A lexer for parsing STL ASCII files.
void execute(std::istream &is)
Execute parser.
STLAsciiParseManual(const label nTrisEstimated)
Construct with the estimated number of triangles in the STL.
Internal class used when parsing STL ASCII format.
void beginFacet()
Action when entering 'facet'.
void beginSolid(word solidName)
Action when entering 'solid'.
bool addVertexComponent(float val)
Add next vertex component. On each third call, adds the point.
void endFacet()
Action on 'endfacet'.
STLAsciiParse(const STLAsciiParse &)=delete
No copy construct.
A 1D vector of objects of type <T> that resizes itself as necessary to accept the new objects.
Definition DynamicList.H:68
Input from file stream as an ISstream, normally using std::ifstream for the actual input.
Definition IFstream.H:55
A class for handling file names.
Definition fileName.H:75
static word validate(const std::string &s, const bool prefix=false)
Construct validated word (no invalid characters).
Definition word.C:39
static const word null
An empty word.
Definition word.H:84
volScalarField & p
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition error.H:600
Implementation details for various OpenFOAM classes.
Definition zoneSubSet.C:30
string lower(const std::string &s)
Return string copy transformed with std::tolower on each character.
Namespace for OpenFOAM.
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition hashSets.C:40
constexpr bool isspace(char c) noexcept
Test for whitespace (C-locale only).
Definition char.H:77
error FatalError
Error stream (stdout output on all processes), with additional 'FOAM FATAL ERROR' header text and sta...
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition errorManip.H:125
constexpr char nl
The newline '\n' character (0x0a).
Definition Ostream.H:50