Go to the documentation of this file.
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,
148 (surfFilePath / surfFileNameBase),
156 Info<<
"Wrote zoning to " << outputName <<
nl <<
endl;
163 const label nFaceZones,
171 boolList includeMap(surf.size(),
false);
177 includeMap[facei] =
true;
186 / surfFileNameBase +
"_" +
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);
290 int 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)"
314 "Additional verbosity"
319 "Write vertices/blocks for blockMeshDict"
325 "Upper limit on the number of files written. "
326 "Default is 10, using 0 suppresses file writing."
332 "Reconstruct and write problem triangles/edges in selected format"
339 const bool checkSelfIntersect =
args.
found(
"checkSelfIntersection");
340 const bool splitNonManifold =
args.
found(
"splitNonManifold");
341 const label outputThreshold =
344 const bool writeSets = !surfaceFormat.empty();
364 Info<<
"Reading surface from " << surfFileName <<
" ..." <<
nl <<
endl;
379 const word fileType = surfFileNameBase.
ext();
381 surfFileNameBase = surfFileNameBase.
lessExt();
383 if (fileType ==
"gz")
385 surfFileNameBase = surfFileNameBase.
lessExt();
395 Info<<
"// blockMeshDict info" <<
nl <<
nl;
397 Info<<
"vertices\n(" <<
nl;
400 Info<<
" " << cornerPts[pti] <<
nl;
407 <<
" hex (0 1 2 3 4 5 6 7) (10 10 10) simpleGrading (1 1 1)\n"
411 <<
"patches\n();" <<
endl;
425 label region = surf[facei].region();
427 if (region < 0 || region >= regionSize.size())
430 <<
"Triangle " << facei <<
" vertices " << surf[facei]
431 <<
" has region " << region <<
" which is outside the range"
432 <<
" of regions 0.." << surf.
patches().size()-1
437 regionSize[region]++;
441 Info<<
"Region\tSize" <<
nl
442 <<
"------\t----" <<
nl;
446 << regionSize[patchi] <<
nl;
463 illegalFaces.append(facei);
467 if (illegalFaces.size())
469 Info<<
"Surface has " << illegalFaces.size()
470 <<
" illegal triangles." <<
endl;
472 if (surfWriter.
valid())
474 boolList isIllegalFace(surf.size(),
false);
482 subSurf.triFaceFaces(faces);
488 (surfFilePath / surfFileNameBase),
500 Info<<
"Wrote illegal triangles to "
501 << outputName <<
nl <<
endl;
503 else if (outputThreshold > 0)
509 if (i >= outputThreshold)
511 Info<<
"Suppressing further warning messages."
512 <<
" Use -outputThreshold to increase number"
513 <<
" of warnings." <<
endl;
519 Info<<
"Dumping conflicting face labels to " << str.name()
521 <<
"Paste this into the input for surfaceSubset" <<
endl;
527 Info<<
"Surface has no illegal triangles." <<
endl;
543 if (
f[0] ==
f[1] ||
f[0] ==
f[2] ||
f[1] ==
f[2])
560 labelList binCount = countBins(0, 1, 20, triQ);
562 Info<<
"Triangle quality (equilateral=1, collapsed=0):"
569 scalar dist = (1.0 - 0.0)/20.0;
573 Info<<
" " <<
min <<
" .. " <<
min+dist <<
" : "
574 << 1.0/surf.size() * binCount[bini]
582 const label minIndex = minMaxIds.first();
583 const label maxIndex = minMaxIds.
second();
585 Info<<
" min " << triQ[minIndex] <<
" for triangle " << minIndex
587 <<
" max " << triQ[maxIndex] <<
" for triangle " << maxIndex
592 if (triQ[minIndex] < SMALL)
595 <<
"Minimum triangle quality is "
596 << triQ[minIndex] <<
". This might give problems in"
597 <<
" self-intersection testing later on." <<
endl;
601 if (surfWriter.
valid())
611 (surfFilePath / surfFileNameBase),
619 Info<<
"Wrote triangle-quality to "
620 << outputName <<
nl <<
endl;
622 else if (outputThreshold > 0)
628 if (triQ[facei] < 1
e-11)
630 problemFaces.append(facei);
634 if (!problemFaces.empty())
638 Info<<
"Dumping bad quality faces to " << str.name() <<
endl
639 <<
"Paste this into the input for surfaceSubset" <<
nl
659 edgeMag[edgei] = edges[edgei].mag(localPoints);
664 const label minEdgei = minMaxIds.first();
665 const label maxEdgei = minMaxIds.
second();
667 const edge& minE = edges[minEdgei];
668 const edge& maxE = edges[maxEdgei];
672 <<
" min " << edgeMag[minEdgei] <<
" for edge " << minEdgei
673 <<
" points " << localPoints[minE[0]] << localPoints[minE[1]]
675 <<
" max " << edgeMag[maxEdgei] <<
" for edge " << maxEdgei
676 <<
" points " << localPoints[maxE[0]] << localPoints[maxE[1]]
690 scalar smallDim = 1
e-6 * bb.mag();
692 Info<<
"Checking for points less than 1e-6 of bounding box ("
693 << bb.span() <<
" metre) apart."
701 for (label i = 1; i < sortedMag.size(); i++)
703 label pti = sortedMag.indices()[i];
705 label prevPti = sortedMag.indices()[i-1];
707 if (
mag(localPoints[pti] - localPoints[prevPti]) < smallDim)
716 const edge&
e = edges[pEdges[i]];
718 if (
e[0] == prevPti ||
e[1] == prevPti)
727 if (nClose < outputThreshold)
731 Info<<
" close unconnected points "
732 << pti <<
' ' << localPoints[pti]
733 <<
" and " << prevPti <<
' '
734 << localPoints[prevPti]
736 <<
mag(localPoints[pti] - localPoints[prevPti])
741 Info<<
" small edge between points "
742 << pti <<
' ' << localPoints[pti]
743 <<
" and " << prevPti <<
' '
744 << localPoints[prevPti]
746 <<
mag(localPoints[pti] - localPoints[prevPti])
750 else if (nClose == outputThreshold)
752 Info<<
"Suppressing further warning messages."
753 <<
" Use -outputThreshold to increase number"
754 <<
" of warnings." <<
endl;
761 Info<<
"Found " << nClose <<
" nearby points." <<
nl
778 const labelList& myFaces = edgeFaces[edgei];
780 if (myFaces.size() == 1)
782 problemFaces.
append(myFaces[0]);
783 openEdges.append(edgei);
789 const labelList& myFaces = edgeFaces[edgei];
791 if (myFaces.size() > 2)
795 problemFaces.append(myFaces[myFacei]);
797 multipleEdges.append(edgei);
800 problemFaces.shrink();
802 if (openEdges.size() || multipleEdges.size())
804 Info<<
"Surface is not closed since not all edges ("
805 << edgeFaces.size() <<
") connected to "
806 <<
"two faces:" <<
endl
807 <<
" connected to one face : " << openEdges.size() <<
endl
808 <<
" connected to >2 faces : " << multipleEdges.size() <<
endl;
810 Info<<
"Conflicting face labels:" << problemFaces.size() <<
endl;
812 if (!edgeFormat.empty() && openEdges.size())
820 Info<<
"Writing open edges to " << openName <<
" ..." <<
endl;
821 writeEdgeSet(openName, surf, openEdges);
823 if (!edgeFormat.empty() && multipleEdges.size())
831 Info<<
"Writing multiply connected edges to "
832 << multName <<
" ..." <<
endl;
833 writeEdgeSet(multName, surf, multipleEdges);
838 Info<<
"Surface is closed. All edges connected to two faces." <<
endl;
849 if (splitNonManifold)
853 if (edgeFaces[edgei].size() > 2)
855 borderEdge[edgei] =
true;
858 syncEdges(surf, borderEdge);
864 Info<<
"Number of unconnected parts : " << numZones <<
endl;
866 if (numZones > 1 && outputThreshold > 0)
868 Info<<
"Splitting surface into parts ..." <<
endl <<
endl;
870 if (!surfWriter.
valid())
885 if (numZones > outputThreshold)
887 Info<<
"Limiting number of files to " << outputThreshold
893 min(outputThreshold, numZones),
918 syncEdges(surf, borderEdge);
927 <<
"Number of zones (connected area with consistent normal) : "
928 << numNormalZones <<
endl;
930 if (numNormalZones > 1)
932 Info<<
"More than one normal orientation." <<
endl;
934 if (outputThreshold > 0)
936 if (!surfWriter.
valid())
951 if (numNormalZones > outputThreshold)
953 Info<<
"Limiting number of files to " << outputThreshold
959 min(outputThreshold, numNormalZones),
962 surfFileNameBase +
"_normal"
973 if (checkSelfIntersect)
975 Info<<
"Checking self-intersection." <<
endl;
982 if (outputThreshold > 0)
1004 if (intStreamPtr.
valid())
1006 intStreamPtr().write(hitInfo.hitPoint());
1012 if (hitInfo2.hit() && hitInfo.index() != hitInfo2.index())
1016 if (intStreamPtr.
valid())
1018 intStreamPtr().write(hitInfo2.hitPoint());
1066 Info<<
"Surface is not self-intersecting" <<
endl;
1070 Info<<
"Surface is self-intersecting at " << nInt
1071 <<
" locations." <<
endl;
1073 if (intStreamPtr.
valid())
1075 Info<<
"Writing intersection points to "
1076 << intStreamPtr().name() <<
endl;
void writeStats(Ostream &os) const
Write some statistics.
const T & second() const noexcept
Return second element, which is also the last element.
triSurface subsetMesh(const UList< bool > &include, labelList &pointMap, labelList &faceMap) const
Return a new surface subsetted on the selected faces.
label size() const noexcept
The number of elements in table.
void reset(T *p=nullptr) noexcept
Delete managed object and set to new given pointer.
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
const labelListList & edgeFaces() const
Return edge-face addressing.
A class for handling words, derived from Foam::string.
virtual fileName write()=0
Write separate surface geometry to file.
Base class for surface writers.
A class for handling file names.
OFstream that keeps track of vertices.
const edgeList & edges() const
Return list of edges, address into LOCAL point list.
T getOrDefault(const word &optName, const T &deflt) const
Get a value from the named option if present, or return default.
static std::string path(const std::string &str)
Return directory path name (part before last /)
static constexpr const zero Zero
Global zero (0)
A 1D vector of objects of type <T> that resizes itself as necessary to accept the new objects.
void triFaceFaces(List< face > &plainFaceList) const
Create a list of faces from the triFaces.
label nEdges() const
Return number of edges in patch.
static std::string name(const std::string &str)
Return basename (part beyond last /), including its extension.
virtual int width() const
Get width of output field.
An edge is a list of two point labels. The functionality it provides supports the discretisation on a...
Base class for mesh zones.
virtual void write(const coordSet &, const wordList &, const List< const Field< Type > * > &, Ostream &) const =0
General entry point for writing.
static void addNote(const string ¬e)
Add extra notes for the usage information.
Extract command arguments and options from the supplied argc and argv parameters.
void append(const T &val)
Append an element at the end of the list.
bool valid() const noexcept
True if the managed pointer is non-null.
Ostream & endl(Ostream &os)
Add newline and flush stream.
fileName lessExt() const
Return file name without extension (part before last .)
pointIndexHit findLineAny(const point &start, const point &end) const
Find any intersection of line between start and end.
const geometricSurfacePatchList & patches() const
Helper class to search on triSurface.
label min(const labelHashSet &set, label minValue=labelMax)
Find the min value in labelHashSet, optionally limited by second argument.
#define forAll(list, i)
Loop across all elements in list.
static void addArgument(const string &argName, const string &usage="")
Append a (mandatory) argument to validArgs.
labelPair findMinMax(const ListType &input, label start=0)
This class describes the interaction of (usually) a face and a point. It carries the info of a succes...
A surfaceWriter for VTK legacy (.vtk) or XML (.vtp) format.
Field< label > labelField
Specialisation of Field<T> for label.
Triangulated surface description with patch information.
A subset of mesh faces organised as a primitive patch.
messageStream Info
Information stream (uses stdout - output is on the master only)
word name(const complex &c)
Return string representation of complex.
DynamicList< T, SizeMin > & append(const T &val)
Append an element to the end of this list.
const labelListList & pointEdges() const
Return point-edge addressing.
Non-pointer based hierarchical recursive searching.
Generic output stream using a standard (STL) stream.
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
label nPoints() const
Return number of points supporting patch faces.
const Field< point_type > & points() const
Return reference to global points.
A list that is sorted upon construction or when explicitly requested with the sort() method.
constexpr auto end(C &c) -> decltype(c.end())
Return iterator to the end of the container c.
Base class for graphics format writing. Entry points are.
virtual bool write(const token &tok)=0
Write token to stream or otherwise handle it.
word ext() const
Return file name extension (part after last .)
const Field< point_type > & localPoints() const
Return pointField of points in patch.
Output to file stream, using an OSstream.
static void addBoolOption(const word &optName, const string &usage="", bool advanced=false)
Add a bool option to validOptions with usage information.
Pointer management similar to std::unique_ptr, with some additional methods and type checking.
label markZones(const boolList &borderEdge, labelList &faceZone) const
(size and) fills faceZone with zone of face. Zone is area
static autoPtr< surfaceWriter > New(const word &writeType)
Return a reference to the selected surfaceWriter.
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
const dimensionedScalar e
Elementary charge.
bool insert(const Key &key)
Insert a new entry, not overwriting existing entries.
Triangle with additional region number.
A bounding box defined in terms of min/max extrema points.
void size(const label n) noexcept
Override size to be inconsistent with allocated storage.
A List with indirect addressing.
static void noParallel()
Remove the parallel options.
const labelList & meshPoints() const
Return labelList of mesh points in patch.
static void addOption(const word &optName, const string ¶m="", const string &usage="", bool advanced=false)
Add an option to validOptions with usage information.
Foam::argList args(argc, argv)
Mesh data needed to do the Finite Area discretisation.
#define WarningInFunction
Report a warning using Foam::Warning.
virtual void open(const fileName &outputPath)
Open for output on specified path, using existing surface.
triangle< point, const point & > triPointRef
bool found(const word &optName) const
Return true if the named option is found.