Commit ea71484e authored by Mark Olesen's avatar Mark Olesen
Browse files

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
parent a8da75d2
......@@ -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;
......
Test-surfaceReading.C
EXE = $(FOAM_APPBIN)/Test-surfaceReading
EXE_INC = \
-I$(LIB_SRC)/fileFormats/lnInclude \
-I$(LIB_SRC)/surfMesh/lnInclude
EXE_LIBS = \
-lsurfMesh
/*---------------------------------------------------------------------------*\
========= |
\\ / 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
......
/*--------------------------------*- 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,83 +71,40 @@ 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();
// 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_;
}
};
//- The lexer function itself
int lex();
STLASCIILexer::STLASCIILexer(istream* is, const label approxNpoints)
:
yySTLFlexLexer(is),
sorted_(true),
groupID_(-1),
lineNo_(1),
points_(approxNpoints),
facets_(approxNpoints)
{}