/*---------------------------------------------------------------------------*\ ========= | \\ / 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