84 scalar dist = nBins/(
max -
min);
98 else if (val >=
max - SMALL)
104 index = label((val -
min)*dist);
106 if ((index < 0) || (index >= nBins))
109 <<
"value " << val <<
" at index " << i
110 <<
" outside range " <<
min <<
" .. " <<
max <<
endl;
135 const word& fieldName,
137 const word& surfFileStem
148 (surfFilePath / surfFileStem),
163 const label nFaceZones,
166 const word& surfFileStem
177 includeMap[facei] =
true;
186 / surfFileStem +
"_" +
name(
zone) +
".obj"
189 Info<<
"writing part " <<
zone <<
" size " << subSurf.
size()
190 <<
" to " << subName <<
endl;
192 subSurf.write(subName);
204 for (
const label edgei : markedEdges)
206 edgeSet.insert(edges[edgei]);
211 if (edgeSet.found(edges[edgei]))
213 markedEdges.insert(edgei);
226 forAll(isMarkedEdge, edgei)
228 if (isMarkedEdge[edgei])
236 forAll(isMarkedEdge, edgei)
238 if (isMarkedEdge[edgei])
240 edgeSet.insert(edges[edgei]);
246 if (edgeSet.found(edges[edgei]))
248 isMarkedEdge[edgei] =
true;
265 for (label edgei : edgeSet)
268 edge compactEdge(-1, -1);
271 label& compacti = pointToCompact[
e[ep]];
274 compacti = compactPoints.size();
278 compactEdge[ep] = compacti;
280 compactEdges.append(compactEdge);
283 edgeMesh eMesh(std::move(compactPoints), std::move(compactEdges));
284 eMesh.write(setName);
290int main(
int argc,
char *argv[])
294 "Check geometric and topological quality of a surface"
302 "checkSelfIntersection",
303 "Also check for self-intersection"
308 "Split surface along non-manifold edges "
309 "(default split is fully disconnected)"
315 "Write vertices/blocks for blockMeshDict"
321 "Upper limit on the number of files written. "
322 "Default is 10, using 0 suppresses file writing."
328 "Reconstruct and write problem triangles/edges in selected format"
335 const bool checkSelfIntersect =
args.found(
"checkSelfIntersection");
336 const bool splitNonManifold =
args.found(
"splitNonManifold");
337 const label outputThreshold =
338 args.getOrDefault<label>(
"outputThreshold", 10);
339 const word surfaceFormat =
args.getOrDefault<
word>(
"writeSets",
"");
340 const bool writeSets = !surfaceFormat.empty();
360 Info<<
"Reading surface from "
361 <<
args.relativePath(surfName) <<
" ..." <<
nl <<
endl;
375 const fileName surfFilePath(surfName.path());
376 word surfFileStem(surfName.stem());
379 if (surfName.has_ext(
"gz"))
386 if (
args.found(
"blockMesh"))
390 Info<<
"// blockMeshDict info" <<
nl <<
nl;
392 Info<<
"vertices\n(" <<
nl;
395 Info<<
" " << cornerPts[pti] <<
nl;
402 <<
" hex (0 1 2 3 4 5 6 7) (10 10 10) simpleGrading (1 1 1)\n"
406 <<
"patches\n();" <<
endl;
420 label region = surf[facei].region();
422 if (region < 0 || region >= regionSize.size())
425 <<
"Triangle " << facei <<
" vertices " << surf[facei]
426 <<
" has region " << region <<
" which is outside the range"
432 regionSize[region]++;
436 Info<<
"Index\tRegion\tSize" <<
nl
437 <<
"-----\t------\t----" <<
nl;
440 Info<< patchi <<
'\t'
441 << surf.
patches()[patchi].name() <<
'\t'
442 << regionSize[patchi] <<
nl;
459 illegalFaces.append(facei);
463 if (illegalFaces.size())
465 Info<<
"Surface has " << illegalFaces.size()
466 <<
" illegal triangles." <<
endl;
478 subSurf.triFaceFaces(faces);
484 (surfFilePath / surfFileStem),
496 Info<<
"Wrote illegal triangles to "
499 else if (outputThreshold > 0)
505 if (i >= outputThreshold)
507 Info<<
"Suppressing further warning messages."
508 <<
" Use -outputThreshold to increase number"
509 <<
" of warnings." <<
endl;
515 Info<<
"Dumping conflicting face labels to " << str.name()
517 <<
"Paste this into the input for surfaceSubset" <<
endl;
523 Info<<
"Surface has no illegal triangles." <<
endl;
539 if (
f[0] ==
f[1] ||
f[0] ==
f[2] ||
f[1] ==
f[2])
556 labelList binCount = countBins(0, 1, 20, triQ);
558 Info<<
"Triangle quality (equilateral=1, collapsed=0):"
565 scalar dist = (1.0 - 0.0)/20.0;
569 Info<<
" " <<
min <<
" .. " <<
min+dist <<
" : "
570 << 1.0/surf.
size() * binCount[bini]
578 const label minIndex = minMaxIds.
first();
579 const label maxIndex = minMaxIds.
second();
581 Info<<
" min " << triQ[minIndex] <<
" for triangle " << minIndex
583 <<
" max " << triQ[maxIndex] <<
" for triangle " << maxIndex
588 if (triQ[minIndex] < SMALL)
591 <<
"Minimum triangle quality is "
592 << triQ[minIndex] <<
". This might give problems in"
593 <<
" self-intersection testing later on." <<
endl;
607 (surfFilePath / surfFileStem),
615 Info<<
"Wrote triangle-quality to "
618 else if (outputThreshold > 0)
624 if (triQ[facei] < 1
e-11)
626 problemFaces.append(facei);
630 if (!problemFaces.empty())
634 Info<<
"Dumping bad quality faces to " << str.name() <<
endl
635 <<
"Paste this into the input for surfaceSubset" <<
nl
655 edgeMag[edgei] = edges[edgei].mag(localPoints);
660 const label minEdgei = minMaxIds.
first();
661 const label maxEdgei = minMaxIds.
second();
663 const edge& minE = edges[minEdgei];
664 const edge& maxE = edges[maxEdgei];
668 <<
" min " << edgeMag[minEdgei] <<
" for edge " << minEdgei
669 <<
" points " << localPoints[minE[0]] << localPoints[minE[1]]
671 <<
" max " << edgeMag[maxEdgei] <<
" for edge " << maxEdgei
672 <<
" points " << localPoints[maxE[0]] << localPoints[maxE[1]]
686 scalar smallDim = 1
e-6 * bb.mag();
688 Info<<
"Checking for points less than 1e-6 of bounding box ("
689 << bb.span() <<
" metre) apart."
697 for (label i = 1; i < sortedMag.size(); i++)
699 label pti = sortedMag.indices()[i];
701 label prevPti = sortedMag.indices()[i-1];
703 if (
mag(localPoints[pti] - localPoints[prevPti]) < smallDim)
712 const edge&
e = edges[pEdges[i]];
714 if (
e[0] == prevPti ||
e[1] == prevPti)
723 if (nClose < outputThreshold)
727 Info<<
" close unconnected points "
728 << pti <<
' ' << localPoints[pti]
729 <<
" and " << prevPti <<
' '
730 << localPoints[prevPti]
732 <<
mag(localPoints[pti] - localPoints[prevPti])
737 Info<<
" small edge between points "
738 << pti <<
' ' << localPoints[pti]
739 <<
" and " << prevPti <<
' '
740 << localPoints[prevPti]
742 <<
mag(localPoints[pti] - localPoints[prevPti])
746 else if (nClose == outputThreshold)
748 Info<<
"Suppressing further warning messages."
749 <<
" Use -outputThreshold to increase number"
750 <<
" of warnings." <<
endl;
757 Info<<
"Found " << nClose <<
" nearby points." <<
nl
774 const labelList& myFaces = edgeFaces[edgei];
776 if (myFaces.
size() == 1)
778 problemFaces.append(myFaces[0]);
779 openEdges.append(edgei);
785 const labelList& myFaces = edgeFaces[edgei];
787 if (myFaces.
size() > 2)
791 problemFaces.append(myFaces[myFacei]);
793 multipleEdges.
append(edgei);
796 problemFaces.shrink();
798 if (openEdges.size() || multipleEdges.size())
800 Info<<
"Surface is not closed since not all edges ("
801 << edgeFaces.
size() <<
") connected to "
802 <<
"two faces:" <<
endl
803 <<
" connected to one face : " << openEdges.size() <<
endl
804 <<
" connected to >2 faces : " << multipleEdges.size() <<
endl;
806 Info<<
"Conflicting face labels:" << problemFaces.size() <<
endl;
808 if (!edgeFormat.empty() && openEdges.size())
816 Info<<
"Writing open edges to "
820 if (!edgeFormat.empty() && multipleEdges.size())
828 Info<<
"Writing multiply connected edges to "
830 writeEdgeSet(
outputName, surf, multipleEdges);
835 Info<<
"Surface is closed. All edges connected to two faces." <<
endl;
846 if (splitNonManifold)
850 if (edgeFaces[edgei].size() > 2)
852 borderEdge[edgei] =
true;
855 syncEdges(surf, borderEdge);
861 Info<<
"Number of unconnected parts : " << numZones <<
endl;
863 if (numZones > 1 && outputThreshold > 0)
865 Info<<
"Splitting surface into parts ..." <<
endl <<
endl;
882 if (numZones > outputThreshold)
884 Info<<
"Limiting number of files to " << outputThreshold
915 syncEdges(surf, borderEdge);
924 <<
"Number of zones (connected area with consistent normal) : "
925 << numNormalZones <<
endl;
927 if (numNormalZones > 1)
929 Info<<
"More than one normal orientation." <<
endl;
931 if (outputThreshold > 0)
948 if (numNormalZones > outputThreshold)
950 Info<<
"Limiting number of files to " << outputThreshold
956 Foam::min(outputThreshold, numNormalZones),
959 surfFileStem +
"_normal"
970 if (checkSelfIntersect)
972 Info<<
"Checking self-intersection." <<
endl;
979 if (outputThreshold > 0)
993 treeDataTriSurface::findSelfIntersectOp exclOp(
tree, edgei);
1003 intStreamPtr().write(hitInfo.point());
1009 if (hitInfo2.hit() && hitInfo.index() != hitInfo2.index())
1015 intStreamPtr().write(hitInfo2.point());
1063 Info<<
"Surface is not self-intersecting" <<
endl;
1067 Info<<
"Surface is self-intersecting at " << nInt
1068 <<
" locations." <<
endl;
1072 Info<<
"Writing intersection points to "
1073 << intStreamPtr().name() <<
endl;
vtk::lineWriter writer(edgeCentres, edgeList::null(), fileName(aMesh.time().globalPath()/(vtkBaseFileName+"-edgesCentres")))
Dynamically sized Field. Similar to DynamicList, but inheriting from a Field instead of a List.
A 1D vector of objects of type <T> that resizes itself as necessary to accept the new objects.
label size() const noexcept
The number of elements in table.
void append(const T &val)
Append an element at the end of the list.
An OFstream that keeps track of vertices and provides convenience output methods for OBJ files.
Output to file stream as an OSstream, normally using std::ofstream for the actual output.
Generic output stream using a standard (STL) stream.
const T & first() const noexcept
Access the first element.
const T & second() const noexcept
Access the second element.
label nEdges() const
Number of edges in patch.
label nPoints() const
Number of points supporting patch faces.
const labelListList & pointEdges() const
Return point-edge addressing.
const edgeList & edges() const
Return list of edges, address into LOCAL point list.
const labelList & meshPoints() const
Return labelList of mesh points in patch.
const Field< point_type > & localPoints() const
Return pointField of points in patch.
const Field< point_type > & points() const noexcept
Return reference to global points.
const labelListList & edgeFaces() const
Return edge-face addressing.
A list that is sorted upon construction or when explicitly requested with the sort() method.
A List with indirect addressing. Like IndirectList but does not store addressing.
void size(const label n)
Older name for setAddressableSize.
Extract command arguments and options from the supplied argc and argv parameters.
static void addVerboseOption(const string &usage="", bool advanced=false)
Enable a 'verbose' bool option, with usage information.
static void addArgument(const string &argName, const string &usage="")
Append a (mandatory) argument to validArgs.
static void addBoolOption(const word &optName, const string &usage="", bool advanced=false)
Add a bool option to validOptions with usage information.
static void noParallel()
Remove the parallel options.
static void addOption(const word &optName, const string ¶m="", const string &usage="", bool advanced=false)
Add an option to validOptions with usage information.
static void addNote(const string ¬e)
Add extra notes for the usage information.
Pointer management similar to std::unique_ptr, with some additional methods and type checking.
void reset(T *p=nullptr) noexcept
Delete managed object and set to new given pointer.
A bounding box defined in terms of min/max extrema points.
tmp< pointField > points() const
Corner points in an order corresponding to a 'hex' cell.
Mesh data needed to do the Finite Area discretisation.
An edge is a list of two vertex labels. This can correspond to a directed graph edge or an edge on a ...
A subset of mesh faces organised as a primitive patch.
A class for handling file names.
Non-pointer based hierarchical recursive searching.
A triFace with additional (region) index.
Base class for surface writers.
static autoPtr< surfaceWriter > New(const word &writeType)
Select construct a surfaceWriter.
A surfaceWriter for VTK legacy (.vtk) or XML (.vtp) format.
Helper class to search on triSurface.
Triangulated surface description with patch information.
label markZones(const boolList &borderEdge, labelList &faceZone) const
(size and) fills faceZone with zone of face. Zone is area
void triFaceFaces(List< face > &plainFaceList) const
Create a list of faces from the triFaces.
triSurface subsetMesh(const UList< bool > &include, labelList &pointMap, labelList &faceMap) const
Return a new surface subsetted on the selected faces.
const geometricSurfacePatchList & patches() const noexcept
void writeStats(Ostream &os) const
Write some statistics.
A class for handling words, derived from Foam::string.
bool remove_ext()
Remove extension, return true if string changed.
A class representing the concept of 0 (zero) that can be used to avoid manipulating objects known to ...
Base class for mesh zones.
word outputName("finiteArea-edges.obj")
OBJstream os(runTime.globalPath()/outputName)
#define WarningInFunction
Report a warning using Foam::Warning.
List< edge > edgeList
List of edge.
Pair< label > labelPair
A pair of labels.
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
List< labelList > labelListList
List of labelList.
HashSet< edge, Hash< edge > > edgeHashSet
A HashSet with edge for its key. Hashing (and ==) on an edge is symmetric.
List< label > labelList
A List of labels.
HashSet< label, Hash< label > > labelHashSet
A HashSet of labels, uses label hasher.
messageStream Info
Information stream (stdout output on master, null elsewhere).
List< face > faceList
List of faces.
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
labelPair findMinMax(const ListType &input, label start=0)
Linear search for the index of the min/max element, similar to std::minmax_element but for lists and ...
Ostream & endl(Ostream &os)
Add newline and flush stream.
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
label min(const labelHashSet &set, label minValue=labelMax)
Find the min value in labelHashSet, optionally limited by second argument.
Field< label > labelField
Specialisation of Field<T> for label.
vector point
Point is a vector.
List< bool > boolList
A List of bools.
static constexpr const zero Zero
Global zero (0).
triangle< point, const point & > triPointRef
A triangle using referred points.
vectorField pointField
pointField is a vectorField.
PointIndexHit< point > pointIndexHit
A PointIndexHit with a 3D point.
UList< label > labelUList
A UList of labels.
constexpr char nl
The newline '\n' character (0x0a).
Tree tree(triangles.begin(), triangles.end())
Foam::argList args(argc, argv)
#define forAll(list, i)
Loop across all elements in list.