1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright (C) 1991-2010 OpenCFD Ltd.
7 -------------------------------------------------------------------------------
9 This file is part of OpenFOAM.
11 OpenFOAM is free software: you can redistribute it and/or modify it
12 under the terms of the GNU General Public License as published by
13 the Free Software Foundation, either version 3 of the License, or
14 (at your option) any later version.
16 OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21 You should have received a copy of the GNU General Public License
22 along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
28 Performs various checks on surface.
32 - surfaceCheck [OPTIONS] \<Foam surface file\>
34 @param \<Foam surface file\> \n
35 @todo Detailed description of argument.
37 @param -noSelfIntersection \n
38 Check for self-intersection.
40 @param -case \<dir\>\n
47 Display Doxygen API documentation page for this application.
50 Display Doxygen source documentation page for this application.
52 \*---------------------------------------------------------------------------*/
54 #include <OpenFOAM/triangle.H>
55 #include <triSurface/triSurface.H>
56 #include <meshTools/triSurfaceTools.H>
57 #include <meshTools/triSurfaceSearch.H>
58 #include <OpenFOAM/argList.H>
59 #include <OpenFOAM/OFstream.H>
60 #include <meshTools/surfaceIntersection.H>
61 #include <OpenFOAM/SortableList.H>
62 #include <OpenFOAM/PatchTools.H>
66 // Does face use valid vertices?
67 bool validTri(const bool verbose, const triSurface& surf, const label faceI)
69 // Simple check on indices ok.
71 const labelledTri& f = surf[faceI];
75 (f[0] < 0) || (f[0] >= surf.points().size())
76 || (f[1] < 0) || (f[1] >= surf.points().size())
77 || (f[2] < 0) || (f[2] >= surf.points().size())
80 WarningIn("validTri(const triSurface&, const label)")
81 << "triangle " << faceI << " vertices " << f
82 << " uses point indices outside point range 0.."
83 << surf.points().size()-1 << endl;
88 if ((f[0] == f[1]) || (f[0] == f[2]) || (f[1] == f[2]))
90 WarningIn("validTri(const triSurface&, const label)")
91 << "triangle " << faceI
92 << " uses non-unique vertices " << f
93 << " coords:" << f.points(surf.points())
98 // duplicate triangle check
100 const labelList& fFaces = surf.faceFaces()[faceI];
102 // Check if faceNeighbours use same points as this face.
103 // Note: discards normal information - sides of baffle are merged.
106 label nbrFaceI = fFaces[i];
108 if (nbrFaceI <= faceI)
110 // lower numbered faces already checked
114 const labelledTri& nbrF = surf[nbrFaceI];
118 ((f[0] == nbrF[0]) || (f[0] == nbrF[1]) || (f[0] == nbrF[2]))
119 && ((f[1] == nbrF[0]) || (f[1] == nbrF[1]) || (f[1] == nbrF[2]))
120 && ((f[2] == nbrF[0]) || (f[2] == nbrF[1]) || (f[2] == nbrF[2]))
123 WarningIn("validTri(const triSurface&, const label)")
124 << "triangle " << faceI << " vertices " << f
125 << " has the same vertices as triangle " << nbrFaceI
126 << " vertices " << nbrF
127 << " coords:" << f.points(surf.points())
142 const scalarField& vals
145 scalar dist = nBins/(max - min);
147 labelList binCount(nBins, 0);
151 scalar val = vals[i];
155 if (Foam::mag(val - min) < SMALL)
159 else if (val >= max - SMALL)
165 index = label((val - min)*dist);
167 if ((index < 0) || (index >= nBins))
171 "countBins(const scalar, const scalar, const label"
172 ", const scalarField&)"
173 ) << "value " << val << " at index " << i
174 << " outside range " << min << " .. " << max << endl;
193 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
196 int main(int argc, char *argv[])
198 argList::noParallel();
200 argList::validArgs.clear();
201 argList::validArgs.append("surface file");
202 argList::validOptions.insert("checkSelfIntersection", "");
203 argList::validOptions.insert("verbose", "");
204 argList args(argc, argv);
206 bool checkSelfIntersection = args.optionFound("checkSelfIntersection");
207 bool verbose = args.optionFound("verbose");
209 fileName surfFileName(args.additionalArgs()[0]);
210 Pout<< "Reading surface from " << surfFileName << " ..." << nl << endl;
216 triSurface surf(surfFileName);
219 Pout<< "Statistics:" << endl;
220 surf.writeStats(Pout);
228 labelList regionSize(surf.patches().size(), 0);
232 label region = surf[faceI].region();
234 if (region < 0 || region >= regionSize.size())
236 WarningIn(args.executable())
237 << "Triangle " << faceI << " vertices " << surf[faceI]
238 << " has region " << region << " which is outside the range"
239 << " of regions 0.." << surf.patches().size()-1
244 regionSize[region]++;
248 Pout<< "Region\tSize" << nl
249 << "------\t----" << nl;
250 forAll(surf.patches(), patchI)
252 Pout<< surf.patches()[patchI].name() << '\t'
253 << regionSize[patchI] << nl;
263 DynamicList<label> illegalFaces(surf.size()/100 + 1);
267 if (!validTri(verbose, surf, faceI))
269 illegalFaces.append(faceI);
273 if (illegalFaces.size())
275 Pout<< "Surface has " << illegalFaces.size()
276 << " illegal triangles." << endl;
278 OFstream str("illegalFaces");
279 Pout<< "Dumping conflicting face labels to " << str.name() << endl
280 << "Paste this into the input for surfaceSubset" << endl;
285 Pout<< "Surface has no illegal triangles." << endl;
296 scalarField triQ(surf.size(), 0);
299 const labelledTri& f = surf[faceI];
301 if (f[0] == f[1] || f[0] == f[2] || f[1] == f[2])
303 //WarningIn(args.executable())
304 // << "Illegal triangle " << faceI << " vertices " << f
305 // << " coords " << f.points(surf.points()) << endl;
316 vector ba(tri.b() - tri.a());
317 ba /= mag(ba) + VSMALL;
319 vector ca(tri.c() - tri.a());
320 ca /= mag(ca) + VSMALL;
322 if (mag(ba&ca) > 1-1E-3)
328 triQ[faceI] = triPointRef
338 labelList binCount = countBins(0, 1, 20, triQ);
340 Pout<< "Triangle quality (equilateral=1, collapsed=0):"
347 scalar dist = (1.0 - 0.0)/20.0;
349 forAll(binCount, binI)
351 Pout<< " " << min << " .. " << min+dist << " : "
352 << 1.0/surf.size() * binCount[binI]
358 label minIndex = findMin(triQ);
359 label maxIndex = findMax(triQ);
361 Pout<< " min " << triQ[minIndex] << " for triangle " << minIndex
363 << " max " << triQ[maxIndex] << " for triangle " << maxIndex
368 if (triQ[minIndex] < SMALL)
370 WarningIn(args.executable()) << "Minimum triangle quality is "
371 << triQ[minIndex] << ". This might give problems in"
372 << " self-intersection testing later on." << endl;
375 // Dump for subsetting
377 DynamicList<label> problemFaces(surf.size()/100+1);
381 if (triQ[faceI] < 1E-11)
383 problemFaces.append(faceI);
386 OFstream str("badFaces");
388 Pout<< "Dumping bad quality faces to " << str.name() << endl
389 << "Paste this into the input for surfaceSubset" << nl
401 const edgeList& edges = surf.edges();
402 const pointField& localPoints = surf.localPoints();
404 scalarField edgeMag(edges.size());
408 edgeMag[edgeI] = edges[edgeI].mag(localPoints);
411 label minEdgeI = findMin(edgeMag);
412 label maxEdgeI = findMax(edgeMag);
414 const edge& minE = edges[minEdgeI];
415 const edge& maxE = edges[maxEdgeI];
418 Pout<< "Edges:" << nl
419 << " min " << edgeMag[minEdgeI] << " for edge " << minEdgeI
420 << " points " << localPoints[minE[0]] << localPoints[minE[1]]
422 << " max " << edgeMag[maxEdgeI] << " for edge " << maxEdgeI
423 << " points " << localPoints[maxE[0]] << localPoints[maxE[1]]
433 const edgeList& edges = surf.edges();
434 const pointField& localPoints = surf.localPoints();
436 const boundBox bb(localPoints);
437 scalar smallDim = 1E-6 * bb.mag();
439 Pout<< "Checking for points less than 1E-6 of bounding box ("
440 << bb.span() << " meter) apart."
444 SortableList<scalar> sortedMag(mag(localPoints));
448 for (label i = 1; i < sortedMag.size(); i++)
450 label ptI = sortedMag.indices()[i];
452 label prevPtI = sortedMag.indices()[i-1];
454 if (mag(localPoints[ptI] - localPoints[prevPtI]) < smallDim)
456 // Check if neighbours.
457 const labelList& pEdges = surf.pointEdges()[ptI];
463 const edge& e = edges[pEdges[i]];
465 if (e[0] == prevPtI || e[1] == prevPtI)
467 // point1 and point0 are connected through edge.
478 Pout<< " close unconnected points "
479 << ptI << ' ' << localPoints[ptI]
480 << " and " << prevPtI << ' '
481 << localPoints[prevPtI]
483 << mag(localPoints[ptI] - localPoints[prevPtI])
488 Pout<< " small edge between points "
489 << ptI << ' ' << localPoints[ptI]
490 << " and " << prevPtI << ' '
491 << localPoints[prevPtI]
493 << mag(localPoints[ptI] - localPoints[prevPtI])
499 Pout<< "Found " << nClose << " nearby points." << nl
508 DynamicList<label> problemFaces(surf.size()/100 + 1);
510 const labelListList& eFaces = surf.edgeFaces();
512 label nSingleEdges = 0;
513 forAll(eFaces, edgeI)
515 const labelList& myFaces = eFaces[edgeI];
517 if (myFaces.size() == 1)
519 problemFaces.append(myFaces[0]);
525 label nMultEdges = 0;
526 forAll(eFaces, edgeI)
528 const labelList& myFaces = eFaces[edgeI];
530 if (myFaces.size() > 2)
532 forAll(myFaces, myFaceI)
534 problemFaces.append(myFaces[myFaceI]);
540 problemFaces.shrink();
542 if ((nSingleEdges != 0) || (nMultEdges != 0))
544 Pout<< "Surface is not closed since not all edges connected to "
545 << "two faces:" << endl
546 << " connected to one face : " << nSingleEdges << endl
547 << " connected to >2 faces : " << nMultEdges << endl;
549 Pout<< "Conflicting face labels:" << problemFaces.size() << endl;
551 OFstream str("problemFaces");
553 Pout<< "Dumping conflicting face labels to " << str.name() << endl
554 << "Paste this into the input for surfaceSubset" << endl;
560 Pout<< "Surface is closed. All edges connected to two faces." << endl;
566 // Check singly connected domain
567 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
570 label numZones = surf.markZones(boolList(surf.nEdges(), false), faceZone);
572 Pout<< "Number of unconnected parts : " << numZones << endl;
576 Pout<< "Splitting surface into parts ..." << endl << endl;
578 fileName surfFileNameBase(surfFileName.name());
580 for(label zone = 0; zone < numZones; zone++)
582 boolList includeMap(surf.size(), false);
584 forAll(faceZone, faceI)
586 if (faceZone[faceI] == zone)
588 includeMap[faceI] = true;
607 surfFileNameBase.lessExt()
613 Pout<< "writing part " << zone << " size " << subSurf.size()
614 << " to " << subFileName << endl;
616 subSurf.write(subFileName);
627 labelHashSet borderEdge(surf.size()/1000);
628 PatchTools::checkOrientation(surf, false, &borderEdge);
631 // Colour all faces into zones using borderEdge
633 labelList normalZone;
634 label numNormalZones = PatchTools::markZones(surf, borderEdge, normalZone);
637 << "Number of zones (connected area with consistent normal) : "
638 << numNormalZones << endl;
640 if (numNormalZones > 1)
642 Pout<< "More than one normal orientation." << endl;
648 // Check self-intersection
649 // ~~~~~~~~~~~~~~~~~~~~~~~
651 if (checkSelfIntersection)
653 Pout<< "Checking self-intersection." << endl;
655 triSurfaceSearch querySurf(surf);
656 surfaceIntersection inter(querySurf);
658 if (inter.cutEdges().empty() && inter.cutPoints().empty())
660 Pout<< "Surface is not self-intersecting" << endl;
664 Pout<< "Surface is self-intersecting" << endl;
665 Pout<< "Writing edges of intersection to selfInter.obj" << endl;
667 OFstream intStream("selfInter.obj");
668 forAll(inter.cutPoints(), cutPointI)
670 const point& pt = inter.cutPoints()[cutPointI];
672 intStream << "v " << pt.x() << ' ' << pt.y() << ' ' << pt.z()
675 forAll(inter.cutEdges(), cutEdgeI)
677 const edge& e = inter.cutEdges()[cutEdgeI];
679 intStream << "l " << e.start()+1 << ' ' << e.end()+1 << endl;
686 Pout<< "End\n" << endl;
692 // ************************ vim: set sw=4 sts=4 et: ************************ //