/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2016-2021 OpenCFD Ltd.
-------------------------------------------------------------------------------
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 .
Application
surfaceCheck
Group
grpSurfaceUtilities
Description
Check geometric and topological quality of a surface.
Usage
\b surfaceCheck [OPTION] surfaceFile
Options:
- \par -checkSelfIntersection
Check for self-intersection.
- \par -splitNonManifold
Split surface along non-manifold edges.
- \par -verbose
Extra verbosity.
- \par -blockMesh
Write vertices/blocks for tight-fitting 1 cell blockMeshDict.
- \par -outputThreshold \
Upper limit on the number of files written.
Prevent surfaces with many disconnected parts from writing many files.
The default is 10. A value of 0 suppresses file writing.
\*---------------------------------------------------------------------------*/
#include "triangle.H"
#include "edgeHashes.H"
#include "triSurface.H"
#include "triSurfaceSearch.H"
#include "triSurfaceTools.H"
#include "argList.H"
#include "OFstream.H"
#include "OBJstream.H"
#include "SortableList.H"
#include "PatchTools.H"
#include "vtkSurfaceWriter.H"
#include "functionObject.H"
#include "DynamicField.H"
#include "edgeMesh.H"
using namespace Foam;
labelList countBins
(
const scalar min,
const scalar max,
const label nBins,
const scalarField& vals
)
{
scalar dist = nBins/(max - min);
labelList binCount(nBins, Zero);
forAll(vals, i)
{
scalar val = vals[i];
label index = -1;
if (Foam::mag(val - min) < SMALL)
{
index = 0;
}
else if (val >= max - SMALL)
{
index = nBins - 1;
}
else
{
index = label((val - min)*dist);
if ((index < 0) || (index >= nBins))
{
WarningInFunction
<< "value " << val << " at index " << i
<< " outside range " << min << " .. " << max << endl;
if (index < 0)
{
index = 0;
}
else
{
index = nBins - 1;
}
}
}
binCount[index]++;
}
return binCount;
}
void writeZoning
(
surfaceWriter& writer,
const triSurface& surf,
const labelList& faceZone,
const word& fieldName,
const fileName& surfFilePath,
const fileName& surfFileNameBase
)
{
// Transcribe faces
faceList faces;
surf.triFaceFaces(faces);
writer.open
(
surf.points(),
faces,
(surfFilePath / surfFileNameBase),
false // serial - already merged
);
fileName outputName = writer.write(fieldName, labelField(faceZone));
writer.clear();
Info<< "Wrote zoning to " << outputName << nl << endl;
}
void writeParts
(
const triSurface& surf,
const label nFaceZones,
const labelList& faceZone,
const fileName& surfFilePath,
const fileName& surfFileNameBase
)
{
for (label zone = 0; zone < nFaceZones; zone++)
{
boolList includeMap(surf.size(), false);
forAll(faceZone, facei)
{
if (faceZone[facei] == zone)
{
includeMap[facei] = true;
}
}
triSurface subSurf(surf.subsetMesh(includeMap));
fileName subName
(
surfFilePath
/ surfFileNameBase + "_" + name(zone) + ".obj"
);
Info<< "writing part " << zone << " size " << subSurf.size()
<< " to " << subName << endl;
subSurf.write(subName);
}
}
void syncEdges(const triSurface& p, labelHashSet& markedEdges)
{
// See comment below about having duplicate edges
const edgeList& edges = p.edges();
edgeHashSet edgeSet(2*markedEdges.size());
for (const label edgei : markedEdges)
{
edgeSet.insert(edges[edgei]);
}
forAll(edges, edgei)
{
if (edgeSet.found(edges[edgei]))
{
markedEdges.insert(edgei);
}
}
}
void syncEdges(const triSurface& p, boolList& isMarkedEdge)
{
// See comment below about having duplicate edges
const edgeList& edges = p.edges();
label n = 0;
forAll(isMarkedEdge, edgei)
{
if (isMarkedEdge[edgei])
{
n++;
}
}
edgeHashSet edgeSet(2*n);
forAll(isMarkedEdge, edgei)
{
if (isMarkedEdge[edgei])
{
edgeSet.insert(edges[edgei]);
}
}
forAll(edges, edgei)
{
if (edgeSet.found(edges[edgei]))
{
isMarkedEdge[edgei] = true;
}
}
}
void writeEdgeSet
(
const word& setName,
const triSurface& surf,
const labelUList& edgeSet
)
{
// Get compact edge mesh
labelList pointToCompact(surf.nPoints(), -1);
DynamicField compactPoints(edgeSet.size());
DynamicList compactEdges(edgeSet.size());
for (label edgei : edgeSet)
{
const edge& e = surf.edges()[edgei];
edge compactEdge(-1, -1);
forAll(e, ep)
{
label& compacti = pointToCompact[e[ep]];
if (compacti == -1)
{
compacti = compactPoints.size();
label pointi = surf.meshPoints()[e[ep]];
compactPoints.append(surf.points()[pointi]);
}
compactEdge[ep] = compacti;
}
compactEdges.append(compactEdge);
}
edgeMesh eMesh(std::move(compactPoints), std::move(compactEdges));
eMesh.write(setName);
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
int main(int argc, char *argv[])
{
argList::addNote
(
"Check geometric and topological quality of a surface"
);
argList::noParallel();
argList::addArgument("input", "The input surface file");
argList::addBoolOption
(
"checkSelfIntersection",
"Also check for self-intersection"
);
argList::addBoolOption
(
"splitNonManifold",
"Split surface along non-manifold edges "
"(default split is fully disconnected)"
);
argList::addBoolOption
(
"verbose",
"Additional verbosity"
);
argList::addBoolOption
(
"blockMesh",
"Write vertices/blocks for blockMeshDict"
);
argList::addOption
(
"outputThreshold",
"number",
"Upper limit on the number of files written. "
"Default is 10, using 0 suppresses file writing."
);
argList::addOption
(
"writeSets",
"surfaceFormat",
"Reconstruct and write problem triangles/edges in selected format"
);
argList args(argc, argv);
const auto surfFileName = args.get(1);
const bool checkSelfIntersect = args.found("checkSelfIntersection");
const bool splitNonManifold = args.found("splitNonManifold");
const label outputThreshold =
args.getOrDefault