/*---------------------------------------------------------------------------*\ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation \\/ M anipulation | Copyright (C) 2016 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 Checks 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" 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, 0); 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 ( const triSurface& surf, const labelList& faceZone, const word& fieldName, const fileName& surfFilePath, const fileName& surfFileNameBase ) { Info<< "Writing zoning to " << fileName ( surfFilePath / fieldName + '_' + surfFileNameBase + '.' + vtkSurfaceWriter::typeName ) << "..." << endl << endl; // Convert data scalarField scalarFaceZone(faceZone.size()); forAll(faceZone, i) { scalarFaceZone[i] = faceZone[i]; } faceList faces(surf.size()); forAll(surf, i) { faces[i] = surf[i]; } vtkSurfaceWriter().write ( surfFilePath, surfFileNameBase, meshedSurfRef ( surf.points(), faces ), fieldName, scalarFaceZone, false // face based data ); } 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; } } labelList pointMap; labelList faceMap; triSurface subSurf ( surf.subsetMesh ( includeMap, pointMap, faceMap ) ); 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; } } } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // int main(int argc, char *argv[]) { argList::noParallel(); argList::addArgument("surfaceFile"); 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", "verbose operation" ); 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 args(argc, argv); const fileName surfFileName = args[1]; const bool checkSelfIntersect = args.found("checkSelfIntersection"); const bool splitNonManifold = args.found("splitNonManifold"); const label outputThreshold = args.lookupOrDefault("outputThreshold", 10); Info<< "Reading surface from " << surfFileName << " ..." << nl << endl; // Read // ~~~~ triSurface surf(surfFileName); Info<< "Statistics:" << endl; surf.writeStats(Info); Info<< endl; // Determine path and extension fileName surfFileNameBase(surfFileName.name()); const word fileType = surfFileNameBase.ext(); // Strip extension surfFileNameBase = surfFileNameBase.lessExt(); // If extension was .gz strip original extension if (fileType == "gz") { surfFileNameBase = surfFileNameBase.lessExt(); } const fileName surfFilePath(surfFileName.path()); // write bounding box corners if (args.found("blockMesh")) { pointField cornerPts(boundBox(surf.points(), false).points()); Info<< "// blockMeshDict info" << nl << nl; Info<< "vertices\n(" << nl; forAll(cornerPts, pti) { Info<< " " << cornerPts[pti] << nl; } // number of divisions needs adjustment later Info<< ");\n" << nl << "blocks\n" << "(\n" << " hex (0 1 2 3 4 5 6 7) (10 10 10) simpleGrading (1 1 1)\n" << ");\n" << nl; Info<< "edges\n();" << nl << "patches\n();" << endl; Info<< nl << "// end blockMeshDict info" << nl << endl; } // Region sizes // ~~~~~~~~~~~~ { labelList regionSize(surf.patches().size(), 0); forAll(surf, facei) { label region = surf[facei].region(); if (region < 0 || region >= regionSize.size()) { WarningInFunction << "Triangle " << facei << " vertices " << surf[facei] << " has region " << region << " which is outside the range" << " of regions 0.." << surf.patches().size()-1 << endl; } else { regionSize[region]++; } } Info<< "Region\tSize" << nl << "------\t----" << nl; forAll(surf.patches(), patchi) { Info<< surf.patches()[patchi].name() << '\t' << regionSize[patchi] << nl; } Info<< nl << endl; } // Check triangles // ~~~~~~~~~~~~~~~ { DynamicList