ENH: add alternative STL ASCII parsers
- In addition to the traditional Flex-based parser, added a Ragel-based parser and a handwritten one. Some representative timings for reading 5874387 points (1958129 tris): Flex Ragel Manual 5.2s 4.8s 6.7s total reading time 3.8s 3.4s 5.3s without point merging
This commit is contained in:
parent
a8da75d27e
commit
ea71484efa
@ -191,7 +191,12 @@ int main(int argc, char *argv[])
|
||||
Info<<"camel-case => " << (word("camel") & "case") << nl;
|
||||
for (const auto& s : { " text with \"spaces'", "08/15 value" })
|
||||
{
|
||||
Info<<"validated \"" << s << "\" => "
|
||||
// Character sequence
|
||||
|
||||
Info<<"validated 5 chars from \" => "
|
||||
<< word::validate(s, s+5, true) << nl;
|
||||
|
||||
Info<<"validated (via string convert) \"" << s << "\" => "
|
||||
<< word::validate(s, true) << nl;
|
||||
}
|
||||
Info<< nl;
|
||||
|
3
applications/test/surfaceReading/Make/files
Normal file
3
applications/test/surfaceReading/Make/files
Normal file
@ -0,0 +1,3 @@
|
||||
Test-surfaceReading.C
|
||||
|
||||
EXE = $(FOAM_APPBIN)/Test-surfaceReading
|
6
applications/test/surfaceReading/Make/options
Normal file
6
applications/test/surfaceReading/Make/options
Normal file
@ -0,0 +1,6 @@
|
||||
EXE_INC = \
|
||||
-I$(LIB_SRC)/fileFormats/lnInclude \
|
||||
-I$(LIB_SRC)/surfMesh/lnInclude
|
||||
|
||||
EXE_LIBS = \
|
||||
-lsurfMesh
|
145
applications/test/surfaceReading/Test-surfaceReading.C
Normal file
145
applications/test/surfaceReading/Test-surfaceReading.C
Normal file
@ -0,0 +1,145 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | Copyright (C) 2018 OpenCFD Ltd.
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM.
|
||||
|
||||
OpenFOAM is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Application
|
||||
Test-surfaceReading
|
||||
|
||||
Description
|
||||
Test basic surface format reading capabilities (and speeds)
|
||||
|
||||
Note
|
||||
The filename extensions are used to determine the file format type.
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#include "argList.H"
|
||||
#include "Time.H"
|
||||
#include "clockTime.H"
|
||||
#include "triSurface.H"
|
||||
#include "MeshedSurfaces.H"
|
||||
#include "UnsortedMeshedSurfaces.H"
|
||||
#include "STLReader.H"
|
||||
|
||||
using namespace Foam;
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
argList::addNote
|
||||
(
|
||||
"Test basic surface format reading capabilities (and speeds)"
|
||||
);
|
||||
|
||||
argList::noParallel();
|
||||
argList::addArgument("inputFile");
|
||||
|
||||
argList::addBoolOption
|
||||
(
|
||||
"triSurface",
|
||||
"Use triSurface for read"
|
||||
);
|
||||
argList::addBoolOption
|
||||
(
|
||||
"triFace",
|
||||
"Use triFace instead of face"
|
||||
);
|
||||
argList::addBoolOption
|
||||
(
|
||||
"unsorted",
|
||||
"Use UnsortedMeshedSurface instead of MeshedSurface, "
|
||||
"or unsorted output (with -triSurface option)"
|
||||
);
|
||||
|
||||
argList::addOption
|
||||
(
|
||||
"ext",
|
||||
"name",
|
||||
"Force alternative extension"
|
||||
);
|
||||
|
||||
argList::addOption
|
||||
(
|
||||
"stl-parser",
|
||||
"N",
|
||||
"ASCII parser type: 0=Flex, 1=Ragel, 2=Manual"
|
||||
);
|
||||
|
||||
#include "setRootCase.H"
|
||||
|
||||
const fileName importName = args[1];
|
||||
|
||||
word ext;
|
||||
if (!args.readIfPresent("ext", ext))
|
||||
{
|
||||
ext = importName.ext();
|
||||
if (ext == "gz")
|
||||
{
|
||||
ext = importName.lessExt().ext();
|
||||
}
|
||||
}
|
||||
|
||||
args.readIfPresent("stl-parser", fileFormats::STLReader::parserType);
|
||||
|
||||
clockTime timing;
|
||||
|
||||
if (args.found("triSurface"))
|
||||
{
|
||||
triSurface surf(importName, ext);
|
||||
|
||||
Info<< "Read surface:" << endl;
|
||||
surf.writeStats(Info);
|
||||
Info<< "Area : " << sum(surf.magSf()) << nl << endl;
|
||||
}
|
||||
else if (args.found("triFace"))
|
||||
{
|
||||
MeshedSurface<triFace> surf(importName, ext);
|
||||
|
||||
Info<< "Read surface:" << endl;
|
||||
surf.writeStats(Info);
|
||||
Info<< "Area : " << sum(surf.magSf()) << nl << endl;
|
||||
}
|
||||
else if (args.found("unsorted"))
|
||||
{
|
||||
UnsortedMeshedSurface<face> surf(importName, ext);
|
||||
|
||||
Info<< "Read surface:" << endl;
|
||||
surf.writeStats(Info);
|
||||
Info<< "Area : " << sum(surf.magSf()) << nl << endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
MeshedSurface<face> surf(importName, ext);
|
||||
|
||||
Info<< "Read surface:" << endl;
|
||||
surf.writeStats(Info);
|
||||
Info<< "Area : " << sum(surf.magSf()) << nl << endl;
|
||||
}
|
||||
|
||||
Info<< nl << "Reading took " << timing.elapsedTime() << "s" << nl
|
||||
<< "\nEnd\n" << endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ************************************************************************* //
|
@ -121,8 +121,12 @@ OptimisationSwitches
|
||||
|
||||
// Force dumping (at next timestep) upon signal (-1 to disable) and exit
|
||||
stopAtWriteNowSignal -1;
|
||||
|
||||
//- Choose STL ASCII parser: 0=Flex, 1=Ragel, 2=Manual
|
||||
fileFormats::stl 0;
|
||||
}
|
||||
|
||||
|
||||
/* Can specify fallback profiling settings
|
||||
profiling
|
||||
{
|
||||
|
@ -50,7 +50,7 @@ Foam::fileName Foam::fileName::validate
|
||||
out.resize(s.size());
|
||||
|
||||
char prev = 0;
|
||||
std::string::size_type count = 0;
|
||||
std::string::size_type len = 0;
|
||||
|
||||
// Largely as per stripInvalid
|
||||
for (auto iter = s.cbegin(); iter != s.cend(); ++iter)
|
||||
@ -66,17 +66,17 @@ Foam::fileName Foam::fileName::validate
|
||||
}
|
||||
|
||||
// Only track valid chars
|
||||
out[count++] = prev = c;
|
||||
out[len++] = prev = c;
|
||||
}
|
||||
}
|
||||
|
||||
if (doClean && prev == '/' && count > 1)
|
||||
if (doClean && prev == '/' && len > 1)
|
||||
{
|
||||
// Avoid trailing '/'
|
||||
--count;
|
||||
--len;
|
||||
}
|
||||
|
||||
out.resize(count);
|
||||
out.resize(len);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
@ -222,17 +222,18 @@ inline String Foam::string::validate(const std::string& str)
|
||||
String out;
|
||||
out.resize(str.size());
|
||||
|
||||
size_type count = 0;
|
||||
size_type len = 0;
|
||||
for (auto iter = str.cbegin(); iter != str.cend(); ++iter)
|
||||
{
|
||||
const char c = *iter;
|
||||
if (String::valid(c))
|
||||
{
|
||||
out[count++] = c;
|
||||
out[len] = c;
|
||||
++len;
|
||||
}
|
||||
}
|
||||
|
||||
out.resize(count);
|
||||
out.resize(len);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ Foam::word Foam::word::validate(const std::string& s, const bool prefix)
|
||||
word out;
|
||||
out.resize(s.size() + (prefix ? 1 : 0));
|
||||
|
||||
std::string::size_type count = 0;
|
||||
std::string::size_type len = 0;
|
||||
|
||||
// As per validate, but optionally detect if the first character
|
||||
// is a digit, which we'd like to avoid having since this will
|
||||
@ -52,17 +52,51 @@ Foam::word Foam::word::validate(const std::string& s, const bool prefix)
|
||||
|
||||
if (word::valid(c))
|
||||
{
|
||||
if (!count && prefix && isdigit(c))
|
||||
if (!len && prefix && isdigit(c))
|
||||
{
|
||||
// First valid character was a digit - prefix with '_'
|
||||
out[count++] = '_';
|
||||
out[len++] = '_';
|
||||
}
|
||||
|
||||
out[count++] = c;
|
||||
out[len++] = c;
|
||||
}
|
||||
}
|
||||
|
||||
out.resize(count);
|
||||
out.resize(len);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
Foam::word Foam::word::validate
|
||||
(
|
||||
const char* first,
|
||||
const char* last,
|
||||
const bool prefix
|
||||
)
|
||||
{
|
||||
std::string::size_type len = (last - first) + (prefix ? 1 : 0);
|
||||
|
||||
word out;
|
||||
out.resize(len);
|
||||
|
||||
for (len=0; first != last; ++first)
|
||||
{
|
||||
const char c = *first;
|
||||
|
||||
if (word::valid(c))
|
||||
{
|
||||
if (!len && prefix && isdigit(c))
|
||||
{
|
||||
// First valid character was a digit - prefix with '_'
|
||||
out[len++] = '_';
|
||||
}
|
||||
|
||||
out[len++] = c;
|
||||
}
|
||||
}
|
||||
|
||||
out.resize(len);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
@ -148,6 +148,16 @@ public:
|
||||
// that work nicely as dictionary keywords.
|
||||
static word validate(const std::string& s, const bool prefix=false);
|
||||
|
||||
//- Construct validated word (no invalid characters) from a sequence
|
||||
//- of characters in the range [first,last),
|
||||
// Optionally prefix any leading digit with '_'.
|
||||
static word validate
|
||||
(
|
||||
const char* first,
|
||||
const char* last,
|
||||
const bool prefix=false
|
||||
);
|
||||
|
||||
|
||||
// File-like Functions
|
||||
|
||||
|
@ -12,7 +12,9 @@ fire/FIRECore.C
|
||||
starcd/STARCDCore.C
|
||||
stl/STLCore.C
|
||||
stl/STLReader.C
|
||||
stl/STLReaderASCII.L
|
||||
stl/STLAsciiParseFlex.L
|
||||
stl/STLAsciiParseManual.C
|
||||
stl/STLAsciiParseRagel.C
|
||||
|
||||
vtk/core/foamVtkCore.C
|
||||
vtk/core/foamVtkPTraits.C
|
||||
|
153
src/fileFormats/stl/STLAsciiParse.H
Normal file
153
src/fileFormats/stl/STLAsciiParse.H
Normal file
@ -0,0 +1,153 @@
|
||||
/*--------------------------------*- C++ -*----------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | Copyright (C) 2018 OpenCFD Ltd.
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM.
|
||||
|
||||
OpenFOAM is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Class
|
||||
Foam::Detail::STLAsciiParse
|
||||
|
||||
Description
|
||||
Internal class used when parsing STL ASCII format
|
||||
|
||||
SourceFiles
|
||||
STLAsciiParse.C
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef STLAsciiParse_H
|
||||
#define STLAsciiParse_H
|
||||
|
||||
#include "DynamicList.H"
|
||||
#include "HashTable.H"
|
||||
#include "STLpoint.H"
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
namespace Foam
|
||||
{
|
||||
namespace Detail
|
||||
{
|
||||
|
||||
/*---------------------------------------------------------------------------*\
|
||||
Class Detail::STLAsciiParse Declaration
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
class STLAsciiParse
|
||||
{
|
||||
protected:
|
||||
|
||||
// Protected Data
|
||||
|
||||
bool sorted_;
|
||||
label groupId_; // The current solid group
|
||||
label lineNum_;
|
||||
|
||||
//- The number of local points on the current facet
|
||||
int nFacetPoints_;
|
||||
|
||||
//- Current vertex component when reading 'vertex'
|
||||
int nVertexCmpt_;
|
||||
|
||||
//- Scratch space for reading 'vertex'
|
||||
STLpoint currVertex_;
|
||||
|
||||
DynamicList<STLpoint> points_;
|
||||
DynamicList<label> facets_;
|
||||
DynamicList<word> names_;
|
||||
DynamicList<label> sizes_;
|
||||
HashTable<label> nameLookup_;
|
||||
|
||||
|
||||
// Protected Member Functions
|
||||
|
||||
//- Action when entering 'solid'
|
||||
inline void beginSolid(word solidName);
|
||||
|
||||
//- Action when entering 'facet'
|
||||
inline void beginFacet();
|
||||
|
||||
//- Reset vertex component to zero
|
||||
inline void resetVertex();
|
||||
|
||||
//- Add next vertex component. On each third call, adds the point.
|
||||
// \return true when point has been added (on the last component)
|
||||
inline bool addVertexComponent(float val);
|
||||
|
||||
//- Add next vertex component. On each third call, adds the point.
|
||||
// \return true when point has been added (on the last component)
|
||||
inline bool addVertexComponent(const char* text);
|
||||
|
||||
//- Action on 'endfacet'
|
||||
inline void endFacet();
|
||||
|
||||
|
||||
//- No copy construct
|
||||
STLAsciiParse(const STLAsciiParse&) = delete;
|
||||
|
||||
//- No copy assignment
|
||||
void operator=(const STLAsciiParse&) = delete;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
// Constructors
|
||||
|
||||
//- From input stream and the approximate number of vertices in the STL
|
||||
inline STLAsciiParse(const label approxNpoints);
|
||||
|
||||
|
||||
// Member Functions
|
||||
|
||||
//- Reset stored values
|
||||
inline void clear();
|
||||
|
||||
//- Do all the solid groups appear in order?
|
||||
inline bool sorted() const;
|
||||
|
||||
//- A list of unstitched triangle points
|
||||
inline DynamicList<STLpoint>& points();
|
||||
|
||||
//- A list of facet IDs (group IDs)
|
||||
//- corresponds to the number of triangles
|
||||
inline DynamicList<label>& facets();
|
||||
|
||||
//- Solid names in the order of their appearance.
|
||||
inline DynamicList<word>& names();
|
||||
|
||||
//- Solid sizes in the order of their appearance.
|
||||
inline DynamicList<label>& sizes();
|
||||
};
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
} // End namespace Detail
|
||||
} // End namespace Foam
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
#include "STLAsciiParseI.H"
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
#endif
|
||||
|
||||
// ************************************************************************* //
|
@ -3,7 +3,7 @@
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation
|
||||
\\/ M anipulation | Copyright (C) 2016-2017 OpenCFD Ltd.
|
||||
\\/ M anipulation | Copyright (C) 2016-2018 OpenCFD Ltd.
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM.
|
||||
@ -21,18 +21,21 @@ License
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Description
|
||||
Flex-based parsing of STL ASCII format
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
%option prefix="yySTL"
|
||||
%option yyclass="yySTLFlexLexer"
|
||||
|
||||
|
||||
%{
|
||||
|
||||
/* ------------------------------------------------------------------------ *\
|
||||
------ local definitions
|
||||
\* ------------------------------------------------------------------------ */
|
||||
|
||||
#include "STLAsciiParse.H"
|
||||
#include "STLReader.H"
|
||||
#include "OSspecific.H"
|
||||
|
||||
@ -40,6 +43,7 @@ License
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-register"
|
||||
|
||||
using namespace Foam;
|
||||
|
||||
// Dummy yyFlexLexer::yylex() to keep the linker happy. It is not called
|
||||
//! \cond dummy
|
||||
#if YY_FLEX_MAJOR_VERSION <= 2 && YY_FLEX_MINOR_VERSION <= 5 && YY_FLEX_SUBMINOR_VERSION < 34
|
||||
@ -67,111 +71,67 @@ int yySTLFlexLexer::yywrap()
|
||||
}
|
||||
//! \endcond
|
||||
|
||||
|
||||
//- A lexer for parsing STL ASCII files.
|
||||
// Returns DynamicList(s) of points and facets (zoneIds).
|
||||
// The facets are within a solid/endsolid grouping
|
||||
class STLASCIILexer
|
||||
class STLAsciiParseFlex
|
||||
:
|
||||
public Detail::STLAsciiParse,
|
||||
public yySTLFlexLexer
|
||||
{
|
||||
// Private data
|
||||
|
||||
bool sorted_;
|
||||
label groupID_; // current solid group
|
||||
label lineNo_;
|
||||
word startError_;
|
||||
|
||||
DynamicList<STLpoint> points_;
|
||||
DynamicList<label> facets_;
|
||||
DynamicList<word> names_;
|
||||
DynamicList<label> sizes_;
|
||||
HashTable<label> lookup_;
|
||||
word startError_;
|
||||
|
||||
public:
|
||||
|
||||
// Constructors
|
||||
|
||||
//- From input stream and the approximate number of vertices in the STL
|
||||
STLASCIILexer(istream* is, const label approxNpoints);
|
||||
//- From input stream and the approximate number of vertices in the STL
|
||||
STLAsciiParseFlex(istream* is, const label approxNpoints)
|
||||
:
|
||||
Detail::STLAsciiParse(approxNpoints),
|
||||
yySTLFlexLexer(is)
|
||||
{}
|
||||
|
||||
|
||||
// Member Functions
|
||||
|
||||
//- The lexer function itself
|
||||
int lex();
|
||||
//- The lexer function itself
|
||||
int lex();
|
||||
|
||||
// Access
|
||||
|
||||
//- Do all the solid groups appear in order?
|
||||
inline bool sorted() const
|
||||
{
|
||||
return sorted_;
|
||||
}
|
||||
|
||||
//- A list of unstitched triangle points
|
||||
inline DynamicList<STLpoint>& points()
|
||||
{
|
||||
return points_;
|
||||
}
|
||||
|
||||
//- A list of facet IDs (group IDs)
|
||||
// corresponds to the number of triangles
|
||||
inline DynamicList<label>& facets()
|
||||
{
|
||||
return facets_;
|
||||
}
|
||||
|
||||
//- Solid names in the order of their appearance.
|
||||
inline DynamicList<word>& names()
|
||||
{
|
||||
return names_;
|
||||
}
|
||||
|
||||
//- Solid sizes in the order of their appearance.
|
||||
inline DynamicList<label>& sizes()
|
||||
{
|
||||
return sizes_;
|
||||
}
|
||||
//- Execute lexer
|
||||
void execute()
|
||||
{
|
||||
while (lex()) {}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
STLASCIILexer::STLASCIILexer(istream* is, const label approxNpoints)
|
||||
:
|
||||
yySTLFlexLexer(is),
|
||||
sorted_(true),
|
||||
groupID_(-1),
|
||||
lineNo_(1),
|
||||
points_(approxNpoints),
|
||||
facets_(approxNpoints)
|
||||
{}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------ *\
|
||||
------ cppLexer::yylex()
|
||||
\* ------------------------------------------------------------------------ */
|
||||
|
||||
#undef YY_DECL
|
||||
#define YY_DECL int STLASCIILexer::lex()
|
||||
#define YY_DECL int STLAsciiParseFlex::lex()
|
||||
|
||||
%}
|
||||
|
||||
one_space [ \t\f\r]
|
||||
space {one_space}*
|
||||
some_space {one_space}+
|
||||
white [ \t\f\r]
|
||||
space {white}*
|
||||
some_space {white}+
|
||||
|
||||
alpha [_A-Za-z]
|
||||
digit [0-9]
|
||||
|
||||
integer {digit}+
|
||||
signedInteger [-+]?{integer}
|
||||
intNum [-+]?{digit}+
|
||||
|
||||
word ([[:alnum:]]|[[:punct:]])*
|
||||
string {word}({some_space}{word})*
|
||||
|
||||
exponent_part [eE][-+]?{digit}+
|
||||
fractional_constant [-+]?(({digit}*"."{digit}+)|({digit}+"."?))
|
||||
expon [Ee][-+]?{digit}+
|
||||
fract [-+]?(({digit}*"."{digit}+)|({digit}+"."?))
|
||||
|
||||
floatNum (({fractional_constant}{exponent_part}?)|({digit}+{exponent_part}))
|
||||
floatNum (({fract}{expon}?)|({digit}+{expon}))
|
||||
|
||||
x {floatNum}
|
||||
y {floatNum}
|
||||
@ -205,13 +165,6 @@ endsolid {space}("endsolid"|"ENDSOLID")({some_space}{word})*
|
||||
%%
|
||||
|
||||
%{
|
||||
// End of read character pointer returned by strtof
|
||||
// char* endPtr;
|
||||
|
||||
label cmpt = 0; // Component index when reading vertex
|
||||
STLpoint vertex;
|
||||
// STLpoint normal;
|
||||
|
||||
static const char* stateNames[7] =
|
||||
{
|
||||
"reading solid",
|
||||
@ -247,62 +200,13 @@ endsolid {space}("endsolid"|"ENDSOLID")({some_space}{word})*
|
||||
}
|
||||
|
||||
<readSolidName>{string} {
|
||||
const word solidName(word::validate(YYText()));
|
||||
|
||||
auto iter = lookup_.cfind(solidName);
|
||||
if (iter.found())
|
||||
{
|
||||
if (groupID_ != iter.object())
|
||||
{
|
||||
sorted_ = false; // Group appeared out of order
|
||||
groupID_ = iter.object();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
groupID_ = sizes_.size();
|
||||
if (lookup_.insert(solidName, groupID_))
|
||||
{
|
||||
names_.append(solidName);
|
||||
sizes_.append(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
FatalErrorInFunction<< "Duplicate solid-name: " << solidName
|
||||
<< exit(FatalError);
|
||||
}
|
||||
}
|
||||
beginSolid(word::validate(YYText()));
|
||||
BEGIN(INITIAL);
|
||||
}
|
||||
|
||||
<readSolidName>{space}\n {
|
||||
const word solidName("solid"); // Could also use solid0, solid1, ...
|
||||
|
||||
auto iter = lookup_.cfind(solidName);
|
||||
if (iter.found())
|
||||
{
|
||||
if (groupID_ != iter.object())
|
||||
{
|
||||
sorted_ = false; // Group appeared out of order
|
||||
groupID_ = iter.object();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
groupID_ = sizes_.size();
|
||||
if (lookup_.insert(solidName, groupID_))
|
||||
{
|
||||
names_.append(solidName);
|
||||
sizes_.append(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
FatalErrorInFunction<< "Duplicate solid-name: " << solidName
|
||||
<< exit(FatalError);
|
||||
}
|
||||
}
|
||||
|
||||
++lineNo_;
|
||||
beginSolid("solid"); // Could also use solid0, solid1, ...
|
||||
++lineNum_;
|
||||
BEGIN(INITIAL);
|
||||
}
|
||||
|
||||
@ -311,6 +215,7 @@ endsolid {space}("endsolid"|"ENDSOLID")({some_space}{word})*
|
||||
}
|
||||
|
||||
{facet} {
|
||||
beginFacet();
|
||||
BEGIN(readFacet);
|
||||
}
|
||||
|
||||
@ -337,24 +242,16 @@ endsolid {space}("endsolid"|"ENDSOLID")({some_space}{word})*
|
||||
BEGIN(readVertex);
|
||||
}
|
||||
|
||||
<readVertex>{space}{signedInteger}{space} {
|
||||
vertex[cmpt++] = atol(YYText());
|
||||
|
||||
if (cmpt == 3)
|
||||
<readVertex>{space}{intNum}{space} {
|
||||
if (addVertexComponent(float(::atol(YYText()))))
|
||||
{
|
||||
cmpt = 0;
|
||||
points_.append(vertex);
|
||||
BEGIN(readVertices);
|
||||
}
|
||||
}
|
||||
|
||||
<readVertex>{space}{floatNum}{space} {
|
||||
vertex[cmpt++] = atof(YYText());
|
||||
|
||||
if (cmpt == 3)
|
||||
if (addVertexComponent(::atof(YYText())))
|
||||
{
|
||||
cmpt = 0;
|
||||
points_.append(vertex);
|
||||
BEGIN(readVertices);
|
||||
}
|
||||
}
|
||||
@ -364,8 +261,7 @@ endsolid {space}("endsolid"|"ENDSOLID")({some_space}{word})*
|
||||
}
|
||||
|
||||
<readFacet>{endfacet} {
|
||||
facets_.append(groupID_);
|
||||
sizes_[groupID_]++;
|
||||
endFacet();
|
||||
BEGIN(INITIAL);
|
||||
}
|
||||
|
||||
@ -376,7 +272,7 @@ endsolid {space}("endsolid"|"ENDSOLID")({some_space}{word})*
|
||||
/* ---------------- Ignore remaining spaces and newlines ------------------ */
|
||||
|
||||
<*>{space} {}
|
||||
<*>\n { ++lineNo_; }
|
||||
<*>\n { ++lineNum_; }
|
||||
|
||||
|
||||
/* ------------------- Any other characters are errors -------------------- */
|
||||
@ -392,7 +288,7 @@ endsolid {space}("endsolid"|"ENDSOLID")({some_space}{word})*
|
||||
<stlError>.* {
|
||||
yy_pop_state();
|
||||
FatalErrorInFunction
|
||||
<< "while " << stateNames[YY_START] << " on line " << lineNo_ << nl
|
||||
<< "while " << stateNames[YY_START] << " on line " << lineNum_ << nl
|
||||
<< " expected " << stateExpects[YY_START]
|
||||
<< " but found '" << startError_.c_str() << YYText() << "'"
|
||||
<< exit(FatalError);
|
||||
@ -408,15 +304,13 @@ endsolid {space}("endsolid"|"ENDSOLID")({some_space}{word})*
|
||||
|
||||
|
||||
//
|
||||
// member function
|
||||
// Member Function
|
||||
//
|
||||
bool Foam::fileFormats::STLReader::readASCII
|
||||
bool Foam::fileFormats::STLReader::readAsciiFlex
|
||||
(
|
||||
const fileName& filename
|
||||
)
|
||||
{
|
||||
format_ = STLFormat::UNKNOWN;
|
||||
|
||||
IFstream is(filename);
|
||||
if (!is)
|
||||
{
|
||||
@ -425,20 +319,12 @@ bool Foam::fileFormats::STLReader::readASCII
|
||||
<< exit(FatalError);
|
||||
}
|
||||
|
||||
// Create the lexer with the approximate number of vertices in the STL
|
||||
// from the file size
|
||||
STLASCIILexer lexer(&(is.stdStream()), Foam::fileSize(filename)/400);
|
||||
while (lexer.lex() != 0) {}
|
||||
// Create with approx number of vertices in the STL (from file size)
|
||||
STLAsciiParseFlex lexer(&(is.stdStream()), Foam::fileSize(filename)/400);
|
||||
lexer.execute();
|
||||
|
||||
sorted_ = lexer.sorted();
|
||||
transfer(lexer);
|
||||
|
||||
// Transfer to normal lists
|
||||
points_.transfer(lexer.points());
|
||||
zoneIds_.transfer(lexer.facets());
|
||||
names_.transfer(lexer.names());
|
||||
sizes_.transfer(lexer.sizes());
|
||||
|
||||
format_ = STLFormat::ASCII;
|
||||
return true;
|
||||
}
|
||||
|
192
src/fileFormats/stl/STLAsciiParseI.H
Normal file
192
src/fileFormats/stl/STLAsciiParseI.H
Normal file
@ -0,0 +1,192 @@
|
||||
/*--------------------------------*- C++ -*----------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | Copyright (C) 2018 OpenCFD Ltd.
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM.
|
||||
|
||||
OpenFOAM is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
|
||||
|
||||
inline void Foam::Detail::STLAsciiParse::beginSolid(word solidName)
|
||||
{
|
||||
if (solidName.empty())
|
||||
{
|
||||
solidName = "solid"; // Could also use solid0, solid1, ...
|
||||
}
|
||||
|
||||
auto iter = nameLookup_.cfind(solidName);
|
||||
if (iter.found())
|
||||
{
|
||||
if (groupId_ != iter.object())
|
||||
{
|
||||
sorted_ = false; // Group appeared out of order
|
||||
groupId_ = iter.object();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
groupId_ = sizes_.size();
|
||||
if (nameLookup_.insert(solidName, groupId_))
|
||||
{
|
||||
names_.append(solidName);
|
||||
sizes_.append(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
FatalErrorInFunction<< "Duplicate solid-name: " << solidName
|
||||
<< exit(FatalError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inline void Foam::Detail::STLAsciiParse::beginFacet()
|
||||
{
|
||||
nFacetPoints_ = 0;
|
||||
nVertexCmpt_ = 0;
|
||||
}
|
||||
|
||||
|
||||
inline void Foam::Detail::STLAsciiParse::resetVertex()
|
||||
{
|
||||
nVertexCmpt_ = 0;
|
||||
}
|
||||
|
||||
|
||||
inline bool Foam::Detail::STLAsciiParse::addVertexComponent(float val)
|
||||
{
|
||||
currVertex_[nVertexCmpt_] = val;
|
||||
|
||||
if (++nVertexCmpt_ == 3)
|
||||
{
|
||||
points_.append(currVertex_);
|
||||
nVertexCmpt_ = 0;
|
||||
++nFacetPoints_;
|
||||
}
|
||||
|
||||
return !nVertexCmpt_;
|
||||
}
|
||||
|
||||
|
||||
inline bool Foam::Detail::STLAsciiParse::addVertexComponent(const char* text)
|
||||
{
|
||||
//-> safer, but slower: readFloat(text, currVertex_[nVertexCmpt_]);
|
||||
currVertex_[nVertexCmpt_] = ::atof(text);
|
||||
|
||||
if (++nVertexCmpt_ == 3)
|
||||
{
|
||||
points_.append(currVertex_);
|
||||
nVertexCmpt_ = 0;
|
||||
++nFacetPoints_;
|
||||
}
|
||||
|
||||
return !nVertexCmpt_;
|
||||
}
|
||||
|
||||
|
||||
inline void Foam::Detail::STLAsciiParse::endFacet()
|
||||
{
|
||||
if (nFacetPoints_ == 3)
|
||||
{
|
||||
facets_.append(groupId_);
|
||||
sizes_[groupId_]++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (nFacetPoints_ > 3)
|
||||
{
|
||||
nFacetPoints_ -= 3;
|
||||
}
|
||||
|
||||
if (nFacetPoints_)
|
||||
{
|
||||
points_.resize(points_.size() - nFacetPoints_);
|
||||
}
|
||||
}
|
||||
|
||||
nFacetPoints_ = 0;
|
||||
}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
|
||||
|
||||
inline Foam::Detail::STLAsciiParse::STLAsciiParse(const label approxNpoints)
|
||||
:
|
||||
sorted_(true),
|
||||
groupId_(-1),
|
||||
lineNum_(1),
|
||||
nFacetPoints_(0),
|
||||
nVertexCmpt_(0),
|
||||
points_(approxNpoints),
|
||||
facets_(approxNpoints/2)
|
||||
{}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
|
||||
|
||||
inline void Foam::Detail::STLAsciiParse::clear()
|
||||
{
|
||||
sorted_ = true;
|
||||
groupId_ = -1;
|
||||
lineNum_ = 0;
|
||||
|
||||
nFacetPoints_ = 0;
|
||||
nVertexCmpt_ = 0;
|
||||
|
||||
points_.clear();
|
||||
facets_.clear();
|
||||
names_.clear();
|
||||
sizes_.clear();
|
||||
nameLookup_.clear();
|
||||
}
|
||||
|
||||
|
||||
inline bool Foam::Detail::STLAsciiParse::sorted() const
|
||||
{
|
||||
return sorted_;
|
||||
}
|
||||
|
||||
|
||||
inline Foam::DynamicList<Foam::STLpoint>& Foam::Detail::STLAsciiParse::points()
|
||||
{
|
||||
return points_;
|
||||
}
|
||||
|
||||
|
||||
inline Foam::DynamicList<Foam::label>& Foam::Detail::STLAsciiParse::facets()
|
||||
{
|
||||
return facets_;
|
||||
}
|
||||
|
||||
|
||||
inline Foam::DynamicList<Foam::word>& Foam::Detail::STLAsciiParse::names()
|
||||
{
|
||||
return names_;
|
||||
}
|
||||
|
||||
|
||||
inline Foam::DynamicList<Foam::label>& Foam::Detail::STLAsciiParse::sizes()
|
||||
{
|
||||
return sizes_;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
423
src/fileFormats/stl/STLAsciiParseManual.C
Normal file
423
src/fileFormats/stl/STLAsciiParseManual.C
Normal file
@ -0,0 +1,423 @@
|
||||
/*--------------------------------*- C++ -*----------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | Copyright (C) 2018 OpenCFD Ltd.
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM.
|
||||
|
||||
OpenFOAM is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Description
|
||||
Hand-written parsing of STL ASCII format
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#include "STLAsciiParse.H"
|
||||
#include "STLReader.H"
|
||||
#include "OSspecific.H"
|
||||
#include "stringOps.H"
|
||||
|
||||
// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
|
||||
|
||||
static inline std::string perrorEOF(std::string expected)
|
||||
{
|
||||
return "Premature EOF while reading '" + expected + "'";
|
||||
}
|
||||
|
||||
|
||||
static inline std::string perrorParse(std::string expected, std::string found)
|
||||
{
|
||||
return "Parse error. Expecting '" + expected + "' found '" + found + "'";
|
||||
}
|
||||
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
namespace Foam
|
||||
{
|
||||
namespace Detail
|
||||
{
|
||||
|
||||
//- A lexer for parsing STL ASCII files.
|
||||
// Returns DynamicList(s) of points and facets (zoneIds).
|
||||
// The facets are within a solid/endsolid grouping
|
||||
class STLAsciiParseManual
|
||||
:
|
||||
public Detail::STLAsciiParse
|
||||
{
|
||||
enum scanState
|
||||
{
|
||||
scanSolid = 0,
|
||||
scanFacet,
|
||||
scanLoop,
|
||||
scanVerts,
|
||||
scanEndLoop,
|
||||
scanEndFacet,
|
||||
scanEndSolid
|
||||
};
|
||||
|
||||
scanState state_;
|
||||
|
||||
std::string errMsg_;
|
||||
|
||||
//- Like std:csub_match
|
||||
typedef std::pair<const char*, const char*> tokenType;
|
||||
|
||||
// Tokenized line
|
||||
DynamicList<tokenType, 16> tokens_;
|
||||
|
||||
//- Tokenize
|
||||
inline std::string::size_type tokenize(const char *p, const char *pe)
|
||||
{
|
||||
const char* start = p;
|
||||
tokens_.clear();
|
||||
|
||||
// Find not space
|
||||
while (p < pe && isspace(*p))
|
||||
{
|
||||
if (*p == '\n' && lineNum_)
|
||||
{
|
||||
++lineNum_;
|
||||
}
|
||||
++p;
|
||||
}
|
||||
|
||||
while (p != pe)
|
||||
{
|
||||
const char* beg = p;
|
||||
|
||||
// Find space
|
||||
while (p < pe && !isspace(*p))
|
||||
{
|
||||
++p;
|
||||
}
|
||||
tokens_.append(tokenType(beg, p));
|
||||
|
||||
// Find next
|
||||
while (p < pe && isspace(*p))
|
||||
{
|
||||
if (*p == '\n')
|
||||
{
|
||||
++lineNum_;
|
||||
return (p - start);
|
||||
}
|
||||
++p;
|
||||
}
|
||||
}
|
||||
|
||||
return (p - start);
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
|
||||
//- From input stream and the approximate number of vertices in the STL
|
||||
STLAsciiParseManual(const label approxNpoints)
|
||||
:
|
||||
Detail::STLAsciiParse(approxNpoints)
|
||||
{}
|
||||
|
||||
//- Execute parser
|
||||
void execute(std::istream& is);
|
||||
};
|
||||
|
||||
} // end of namespace Detail
|
||||
} // end of namespace Foam
|
||||
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
// Length of the input read buffer
|
||||
#define INBUFLEN 16384
|
||||
|
||||
void Foam::Detail::STLAsciiParseManual::execute(std::istream& is)
|
||||
{
|
||||
if (!is)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Buffering
|
||||
char inbuf[INBUFLEN];
|
||||
std::streamsize pending = 0;
|
||||
|
||||
lineNum_ = 0;
|
||||
|
||||
state_ = scanSolid;
|
||||
errMsg_.clear();
|
||||
|
||||
// Line-oriented processing loop
|
||||
while (is)
|
||||
{
|
||||
if (pending >= INBUFLEN)
|
||||
{
|
||||
// We overfilled the buffer while trying to scan a token...
|
||||
FatalErrorInFunction
|
||||
<< "buffer full while scanning near line " << lineNum_ << nl;
|
||||
break;
|
||||
}
|
||||
|
||||
char *data = inbuf + pending; // current data buffer
|
||||
const std::streamsize buflen = INBUFLEN - pending; // space in buffer
|
||||
|
||||
is.read(data, buflen);
|
||||
const std::streamsize gcount = is.gcount();
|
||||
|
||||
if (!gcount)
|
||||
{
|
||||
// EOF
|
||||
// If scanning for next "solid" this is a valid way to exit, but
|
||||
// an error if scanning for the initial "solid" or any other token
|
||||
|
||||
switch (state_)
|
||||
{
|
||||
case scanSolid:
|
||||
{
|
||||
if (!lineNum_) errMsg_ = perrorEOF("solid");
|
||||
break;
|
||||
}
|
||||
case scanFacet: { errMsg_ = perrorEOF("facet"); break; }
|
||||
case scanLoop: { errMsg_ = perrorEOF("outer loop"); break; }
|
||||
case scanVerts: { errMsg_ = perrorEOF("vertex"); break; }
|
||||
case scanEndLoop: { errMsg_ = perrorEOF("endloop"); break; }
|
||||
case scanEndFacet: { errMsg_ = perrorEOF("endfacet"); break; }
|
||||
case scanEndSolid: { errMsg_ = perrorEOF("endsolid"); break; }
|
||||
}
|
||||
|
||||
// Terminate the parsing loop
|
||||
break;
|
||||
}
|
||||
|
||||
// p,pe = Ragel parsing point and parsing end (default naming)
|
||||
// eof = Ragel EOF point (default naming)
|
||||
|
||||
char *p = inbuf;
|
||||
char *pe = data + gcount;
|
||||
|
||||
// Line-oriented: search backwards to find last newline
|
||||
{
|
||||
--pe;
|
||||
while (*pe != '\n' && pe >= inbuf)
|
||||
{
|
||||
--pe;
|
||||
}
|
||||
++pe;
|
||||
}
|
||||
|
||||
std::string cmd;
|
||||
do
|
||||
{
|
||||
// Tokenize
|
||||
const auto parsedLen = tokenize(p, pe);
|
||||
p += parsedLen;
|
||||
if (!parsedLen || tokens_.empty())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Ensure consistent case on the first token
|
||||
cmd.assign(tokens_[0].first, tokens_[0].second);
|
||||
stringOps::lower(cmd);
|
||||
|
||||
// Handle all expected parse states
|
||||
switch (state_)
|
||||
{
|
||||
case scanSolid:
|
||||
{
|
||||
if (cmd == "solid")
|
||||
{
|
||||
if (tokens_.empty())
|
||||
{
|
||||
beginSolid(word::null);
|
||||
}
|
||||
else
|
||||
{
|
||||
beginSolid
|
||||
(
|
||||
word::validate(tokens_[1].first, tokens_[1].second)
|
||||
);
|
||||
}
|
||||
|
||||
state_ = scanFacet; // Next state
|
||||
}
|
||||
else
|
||||
{
|
||||
errMsg_ = perrorParse("solid", cmd);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case scanFacet:
|
||||
{
|
||||
if (cmd == "color")
|
||||
{
|
||||
// Optional 'color' entry (after solid)
|
||||
// - continue looking for 'facet'
|
||||
continue;
|
||||
}
|
||||
else if (cmd == "facet")
|
||||
{
|
||||
beginFacet();
|
||||
state_ = scanLoop; // Next state
|
||||
}
|
||||
else if (cmd == "endsolid")
|
||||
{
|
||||
// Finished with 'endsolid' - find next solid
|
||||
state_ = scanSolid;
|
||||
}
|
||||
else
|
||||
{
|
||||
errMsg_ = perrorParse("facet", cmd);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case scanLoop:
|
||||
{
|
||||
if (cmd == "outer")
|
||||
{
|
||||
// More pedantic would with (tokens_[1] == "loop") too
|
||||
state_ = scanVerts; // Next state
|
||||
}
|
||||
else
|
||||
{
|
||||
errMsg_ = perrorParse("outer loop", cmd);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case scanVerts:
|
||||
{
|
||||
if (cmd == "vertex")
|
||||
{
|
||||
if (tokens_.size() > 3)
|
||||
{
|
||||
// Although tokens are not nul-terminated,
|
||||
// they are space delimited and thus good enough for atof()
|
||||
addVertexComponent(tokens_[1].first);
|
||||
addVertexComponent(tokens_[2].first);
|
||||
addVertexComponent(tokens_[3].first);
|
||||
}
|
||||
else
|
||||
{
|
||||
errMsg_ = "Error parsing vertex value";
|
||||
}
|
||||
}
|
||||
else if (cmd == "endloop")
|
||||
{
|
||||
state_ = scanEndFacet; // Next state
|
||||
}
|
||||
else
|
||||
{
|
||||
errMsg_ = perrorParse("vertex", cmd);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case scanEndLoop:
|
||||
{
|
||||
if (cmd == "endloop")
|
||||
{
|
||||
state_ = scanEndFacet; // Next state
|
||||
}
|
||||
else
|
||||
{
|
||||
errMsg_ = perrorParse("endloop", cmd);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case scanEndFacet:
|
||||
{
|
||||
if (cmd == "endfacet")
|
||||
{
|
||||
endFacet();
|
||||
state_ = scanFacet; // Next facet, or endsolid
|
||||
}
|
||||
else
|
||||
{
|
||||
errMsg_ = perrorParse("endfacet", cmd);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case scanEndSolid:
|
||||
{
|
||||
if (cmd == "endsolid")
|
||||
{
|
||||
state_ = scanSolid; // Start over again
|
||||
}
|
||||
else
|
||||
{
|
||||
errMsg_ = perrorParse("endsolid", cmd);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (errMsg_.empty());
|
||||
|
||||
// How much still in the bufffer?
|
||||
pending = data + gcount - pe;
|
||||
|
||||
if (pending)
|
||||
{
|
||||
memmove(inbuf, pe, pending);
|
||||
}
|
||||
|
||||
if (gcount < buflen)
|
||||
{
|
||||
break; // done
|
||||
}
|
||||
|
||||
if (!errMsg_.empty())
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!errMsg_.empty())
|
||||
{
|
||||
FatalErrorInFunction
|
||||
<< errMsg_ << nl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
//
|
||||
// Member Function
|
||||
//
|
||||
bool Foam::fileFormats::STLReader::readAsciiManual
|
||||
(
|
||||
const fileName& filename
|
||||
)
|
||||
{
|
||||
IFstream is(filename);
|
||||
if (!is)
|
||||
{
|
||||
FatalErrorInFunction
|
||||
<< "file " << filename << " not found"
|
||||
<< exit(FatalError);
|
||||
}
|
||||
|
||||
// Create with the approximate number of vertices in the STL from file size
|
||||
Detail::STLAsciiParseManual lexer(Foam::fileSize(filename)/400);
|
||||
lexer.execute(is.stdStream());
|
||||
|
||||
transfer(lexer);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
1920
src/fileFormats/stl/STLAsciiParseRagel.C
Normal file
1920
src/fileFormats/stl/STLAsciiParseRagel.C
Normal file
File diff suppressed because it is too large
Load Diff
310
src/fileFormats/stl/STLAsciiParseRagel.rl
Normal file
310
src/fileFormats/stl/STLAsciiParseRagel.rl
Normal file
@ -0,0 +1,310 @@
|
||||
/*--------------------------------*- C++ -*----------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | Copyright (C) 2018 OpenCFD Ltd.
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM.
|
||||
|
||||
OpenFOAM is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Description
|
||||
Ragel-based parsing of STL ASCII format.
|
||||
The goto-based finite state machine (FSM) is generated with
|
||||
|
||||
ragel -G2 -o STLAsciiParseRagel.C STLAsciiParseRagel.rl
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#include "STLAsciiParse.H"
|
||||
#include "STLReader.H"
|
||||
#include "OSspecific.H"
|
||||
|
||||
// https://en.wikipedia.org/wiki/STL_%28file_format%29#ASCII_STL
|
||||
//
|
||||
// Format
|
||||
//
|
||||
// solid [name]
|
||||
//
|
||||
// * where name is an optional string.
|
||||
// * The file continues with any number of triangles,
|
||||
// each represented as follows:
|
||||
//
|
||||
// [color ...]
|
||||
// facet normal ni nj nk
|
||||
// outer loop
|
||||
// vertex v1x v1y v1z
|
||||
// vertex v2x v2y v2z
|
||||
// vertex v3x v3y v3z
|
||||
// endloop
|
||||
// endfacet
|
||||
//
|
||||
// * where each n or v is a floating-point number.
|
||||
// * The file concludes with
|
||||
//
|
||||
// endsolid [name]
|
||||
|
||||
// We take some parsing shortcuts.
|
||||
// - Ignore 'color' lines
|
||||
// - Only look for initial 'facet '. Ignore 'normal ...'
|
||||
// - Ignore name for 'endsolid'
|
||||
//
|
||||
|
||||
// Ragel machine definition
|
||||
// Ragel variables (p, pe, eof, cs, top, stack, ts, te, act) defined later...
|
||||
//
|
||||
// Can use 'variable p xxx;' etc to change these names
|
||||
|
||||
// Define the machine actions
|
||||
%%{
|
||||
machine stlAscii;
|
||||
|
||||
action buffer { tok = p; /* Local token start */ }
|
||||
action nl { ++lineNum_; }
|
||||
|
||||
action bsolid { beginSolid(word::validate(tok, p)); }
|
||||
action bfacet { beginFacet(); }
|
||||
action efacet { endFacet(); }
|
||||
|
||||
action bvertex { resetVertex(); }
|
||||
action vertexCmpt
|
||||
{
|
||||
const char saveC = *p;
|
||||
*p = '\0'; // Make nul-terminated
|
||||
|
||||
addVertexComponent(tok);
|
||||
*p = saveC; // Restore previous character
|
||||
}
|
||||
|
||||
}%%
|
||||
|
||||
|
||||
%%{
|
||||
machine stlAscii;
|
||||
|
||||
white = [ \t\f\r]; # Horizontal whitespace
|
||||
nl = (white* '\n' %nl); # Newline
|
||||
dnl = ([^\n]* '\n' %nl); # Discard up to and including newline
|
||||
|
||||
decimal = ((digit* '.' digit+) | (digit+ '.'?)) ;
|
||||
number = [\-+]? (digit+ | decimal) ([Ee][\-+]? digit+)? ;
|
||||
|
||||
bfacet = space* ("facet"|"FACET") white %bfacet dnl;
|
||||
efacet = space* ("endfacet"|"ENDFACET") %efacet dnl;
|
||||
|
||||
solidName =
|
||||
('' >buffer %bsolid nl)
|
||||
| ((white+ [^\n]*) >buffer %bsolid nl);
|
||||
|
||||
bsolid =
|
||||
space* ("solid"|"SOLID") solidName ;
|
||||
|
||||
esolid = space* ("endsolid"|"ENDSOLID") dnl;
|
||||
|
||||
color = space* ("color"|"COLOR") dnl;
|
||||
|
||||
bloop = space* ("outer" white+ "loop")|("OUTER" white+ "LOOP") dnl;
|
||||
eloop = space* ("endloop"|"ENDLOOP") dnl;
|
||||
|
||||
vertex = space* ("vertex"|"VERTEX")
|
||||
((white+ (number > buffer %vertexCmpt)){3} nl);
|
||||
|
||||
main := space*
|
||||
(
|
||||
bsolid
|
||||
color?
|
||||
( bfacet bloop (vertex)* eloop efacet )*
|
||||
esolid
|
||||
)+ space*;
|
||||
|
||||
}%%
|
||||
|
||||
|
||||
//
|
||||
// FSM globals
|
||||
//
|
||||
|
||||
%% write data nofinal;
|
||||
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
namespace Foam
|
||||
{
|
||||
namespace Detail
|
||||
{
|
||||
|
||||
//- A lexer for parsing STL ASCII files.
|
||||
// Returns DynamicList(s) of points and facets (zoneIds).
|
||||
// The facets are within a solid/endsolid grouping
|
||||
class STLAsciiParseRagel
|
||||
:
|
||||
public Detail::STLAsciiParse
|
||||
{
|
||||
// Private Data
|
||||
word startError_;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
//- From input stream and the approximate number of vertices in the STL
|
||||
STLAsciiParseRagel(const label approxNpoints)
|
||||
:
|
||||
Detail::STLAsciiParse(approxNpoints)
|
||||
{}
|
||||
|
||||
//- Execute lexer
|
||||
void execute(std::istream& is);
|
||||
};
|
||||
|
||||
} // end of namespace Detail
|
||||
} // end of namespace Foam
|
||||
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
// Length of the input read buffer
|
||||
#define INBUFLEN 16384
|
||||
|
||||
void Foam::Detail::STLAsciiParseRagel::execute(std::istream& is)
|
||||
{
|
||||
if (!is)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// cs = code state
|
||||
int cs;
|
||||
|
||||
%%{write init;}%% /* ^^^ FSM initialization here ^^^ */;
|
||||
|
||||
// Local token start
|
||||
char *tok = nullptr;
|
||||
|
||||
// Buffering
|
||||
char inbuf[INBUFLEN];
|
||||
std::streamsize pending = 0;
|
||||
|
||||
// Line-oriented processing loop (as per Ragel pdf example)
|
||||
|
||||
while (is)
|
||||
{
|
||||
if (pending >= INBUFLEN)
|
||||
{
|
||||
// We overfilled the buffer while trying to scan a token...
|
||||
FatalErrorInFunction
|
||||
<< "buffer full while scanning near line " << lineNum_ << nl;
|
||||
break;
|
||||
}
|
||||
|
||||
char *data = inbuf + pending; // current data buffer
|
||||
const std::streamsize buflen = INBUFLEN - pending; // space in buffer
|
||||
|
||||
is.read(data, buflen);
|
||||
const std::streamsize gcount = is.gcount();
|
||||
if (!gcount)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// p,pe = Ragel parsing point, parsing end (default naming)
|
||||
// eof = Ragel EOF point (default naming)
|
||||
|
||||
char *p = inbuf;
|
||||
const char *pe = data + gcount;
|
||||
const char *eof = nullptr;
|
||||
if (!is)
|
||||
{
|
||||
eof = pe; // Tag 'pe' as being the EOF for the FSM as well
|
||||
}
|
||||
|
||||
// Line-oriented: search backwards to find last newline
|
||||
{
|
||||
--pe;
|
||||
while (*pe != '\n' && pe >= inbuf)
|
||||
{
|
||||
--pe;
|
||||
}
|
||||
++pe;
|
||||
}
|
||||
|
||||
%%{write exec;}%% /* ^^^ FSM execution here ^^^ */;
|
||||
|
||||
if (%%{write error;}%% == cs)
|
||||
{
|
||||
// FSM failed before finding a token
|
||||
FatalErrorInFunction
|
||||
<< "parse error while scanning near line " << lineNum_ << nl;
|
||||
|
||||
if (p)
|
||||
{
|
||||
std::string::size_type errLen = (pe - p);
|
||||
if (errLen > 80)
|
||||
{
|
||||
errLen = 80;
|
||||
}
|
||||
|
||||
FatalErrorInFunction
|
||||
<< "context: " << std::string(p, errLen) << nl;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// How much still in the bufffer?
|
||||
pending = data + gcount - pe;
|
||||
|
||||
if (pending)
|
||||
{
|
||||
memmove(inbuf, pe, pending);
|
||||
}
|
||||
|
||||
if (gcount < buflen)
|
||||
{
|
||||
break; // done
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
//
|
||||
// Member Function
|
||||
//
|
||||
bool Foam::fileFormats::STLReader::readAsciiRagel
|
||||
(
|
||||
const fileName& filename
|
||||
)
|
||||
{
|
||||
IFstream is(filename);
|
||||
if (!is)
|
||||
{
|
||||
FatalErrorInFunction
|
||||
<< "file " << filename << " not found"
|
||||
<< exit(FatalError);
|
||||
}
|
||||
|
||||
// Create with approx number of vertices in the STL (from file size)
|
||||
Detail::STLAsciiParseRagel lexer(Foam::fileSize(filename)/400);
|
||||
lexer.execute(is.stdStream());
|
||||
|
||||
transfer(lexer);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
@ -24,14 +24,60 @@ License
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#include "STLReader.H"
|
||||
#include "STLAsciiParse.H"
|
||||
#include "Map.H"
|
||||
#include "IFstream.H"
|
||||
#include "mergePoints.H"
|
||||
|
||||
#undef DEBUG_STLBINARY
|
||||
|
||||
/* * * * * * * * * * * * * * * Static Member Data * * * * * * * * * * * * * */
|
||||
|
||||
int Foam::fileFormats::STLReader::parserType
|
||||
(
|
||||
Foam::debug::optimisationSwitch("fileFormats::stl", 0)
|
||||
);
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
|
||||
|
||||
void Foam::fileFormats::STLReader::transfer
|
||||
(
|
||||
Detail::STLAsciiParse& parsed
|
||||
)
|
||||
{
|
||||
sorted_ = parsed.sorted();
|
||||
|
||||
points_.transfer(parsed.points());
|
||||
zoneIds_.transfer(parsed.facets());
|
||||
names_.transfer(parsed.names());
|
||||
sizes_.transfer(parsed.sizes());
|
||||
|
||||
format_ = STLFormat::ASCII;
|
||||
|
||||
parsed.clear();
|
||||
}
|
||||
|
||||
|
||||
bool Foam::fileFormats::STLReader::readASCII
|
||||
(
|
||||
const fileName& filename
|
||||
)
|
||||
{
|
||||
// No runtime selection of parser (only via optimisationSwitch)
|
||||
// this is something that is infrequently changed.
|
||||
if (parserType == 1)
|
||||
{
|
||||
return readAsciiRagel(filename);
|
||||
}
|
||||
else if (parserType == 2)
|
||||
{
|
||||
return readAsciiManual(filename);
|
||||
}
|
||||
return readAsciiFlex(filename);
|
||||
}
|
||||
|
||||
|
||||
bool Foam::fileFormats::STLReader::readBINARY
|
||||
(
|
||||
const fileName& filename
|
||||
@ -189,12 +235,6 @@ Foam::fileFormats::STLReader::STLReader
|
||||
}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
|
||||
|
||||
Foam::fileFormats::STLReader::~STLReader()
|
||||
{}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
|
||||
|
||||
void Foam::fileFormats::STLReader::clear()
|
||||
|
@ -45,6 +45,15 @@ SourceFiles
|
||||
|
||||
namespace Foam
|
||||
{
|
||||
|
||||
namespace Detail
|
||||
{
|
||||
|
||||
// Forward Declarations
|
||||
class STLAsciiParse;
|
||||
|
||||
}
|
||||
|
||||
namespace fileFormats
|
||||
{
|
||||
|
||||
@ -78,7 +87,19 @@ class STLReader
|
||||
|
||||
// Private Member Functions
|
||||
|
||||
//- Read ASCII
|
||||
//- Transfer parsed information to normal lists
|
||||
void transfer(Detail::STLAsciiParse& parsed);
|
||||
|
||||
//- Parse/read ASCII using Flex-based parser
|
||||
bool readAsciiFlex(const fileName& filename);
|
||||
|
||||
//- Parse/read ASCII using Ragel-based parser
|
||||
bool readAsciiRagel(const fileName& filename);
|
||||
|
||||
//- Parse/read ASCII using simple handwritten parser
|
||||
bool readAsciiManual(const fileName& filename);
|
||||
|
||||
//- Parse/read ASCII
|
||||
bool readASCII(const fileName& filename);
|
||||
|
||||
//- Read BINARY
|
||||
@ -88,15 +109,21 @@ class STLReader
|
||||
bool readFile(const fileName& filename, const STLFormat format);
|
||||
|
||||
|
||||
//- Disallow default bitwise copy construct
|
||||
//- No copy construct
|
||||
STLReader(const STLReader&) = delete;
|
||||
|
||||
//- Disallow default bitwise assignment
|
||||
//- No copy assignment
|
||||
void operator=(const STLReader&) = delete;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
// Static Data
|
||||
|
||||
//- ASCII parser types (0=Flex, 1=Ragel, 2=Manual)
|
||||
static int parserType;
|
||||
|
||||
|
||||
// Constructors
|
||||
|
||||
//- Read from file, filling in the information.
|
||||
@ -109,7 +136,7 @@ public:
|
||||
|
||||
|
||||
//- Destructor
|
||||
~STLReader();
|
||||
~STLReader() = default;
|
||||
|
||||
|
||||
// Member Functions
|
||||
|
@ -82,10 +82,10 @@ class STLsurfaceFormat
|
||||
);
|
||||
|
||||
|
||||
//- Disallow default bitwise copy construct
|
||||
//- No copy construct
|
||||
STLsurfaceFormat(const STLsurfaceFormat<Face>&) = delete;
|
||||
|
||||
//- Disallow default bitwise assignment
|
||||
//- No copy assignment
|
||||
void operator=(const STLsurfaceFormat<Face>&) = delete;
|
||||
|
||||
|
||||
|
@ -491,19 +491,30 @@ Foam::triSurface::triSurface
|
||||
}
|
||||
|
||||
|
||||
Foam::triSurface::triSurface(const fileName& name, const scalar scaleFactor)
|
||||
Foam::triSurface::triSurface
|
||||
(
|
||||
const fileName& name,
|
||||
const scalar scaleFactor
|
||||
)
|
||||
:
|
||||
triSurface(name, name.ext(), scaleFactor)
|
||||
{}
|
||||
|
||||
|
||||
Foam::triSurface::triSurface
|
||||
(
|
||||
const fileName& name,
|
||||
const word& ext,
|
||||
const scalar scaleFactor
|
||||
)
|
||||
:
|
||||
ParentType(List<Face>(), pointField()),
|
||||
patches_(),
|
||||
sortedEdgeFacesPtr_(nullptr),
|
||||
edgeOwnerPtr_(nullptr)
|
||||
{
|
||||
const word ext = name.ext();
|
||||
|
||||
read(name, ext);
|
||||
|
||||
scalePoints(scaleFactor);
|
||||
|
||||
setDefaultPatches();
|
||||
}
|
||||
|
||||
|
@ -285,6 +285,14 @@ public:
|
||||
// Optional (positive, non-zero) point scaling is possible.
|
||||
triSurface(const fileName& name, const scalar scaleFactor = -1);
|
||||
|
||||
//- Construct from file name (uses extension to determine type)
|
||||
triSurface
|
||||
(
|
||||
const fileName& name,
|
||||
const word& ext,
|
||||
const scalar scaleFactor = -1
|
||||
);
|
||||
|
||||
//- Construct from Istream
|
||||
triSurface(Istream& is);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user