1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright (C) 1991-2009 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 the
13 Free Software Foundation; either version 2 of the License, or (at your
14 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, write to the Free Software Foundation,
23 Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
29 Performs various checks on surface.
33 - surfaceCheck [OPTIONS] \<Foam surface file\>
35 @param \<Foam surface file\> \n
36 @todo Detailed description of argument.
38 @param -noSelfIntersection \n
39 Check for self-intersection.
41 @param -case \<dir\>\n
48 Display Doxygen API documentation page for this application.
51 Display Doxygen source documentation page for this application.
53 \*---------------------------------------------------------------------------*/
55 #include <OpenFOAM/triangle.H>
56 #include <triSurface/triSurface.H>
57 #include <meshTools/triSurfaceTools.H>
58 #include <meshTools/triSurfaceSearch.H>
59 #include <OpenFOAM/argList.H>
60 #include <OpenFOAM/OFstream.H>
61 #include <meshTools/surfaceIntersection.H>
62 #include <OpenFOAM/SortableList.H>
63 #include <OpenFOAM/PatchTools.H>
67 // Does face use valid vertices?
68 bool validTri(const bool verbose, const triSurface& surf, const label faceI)
70 // Simple check on indices ok.
72 const labelledTri& f = surf[faceI];
76 (f[0] < 0) || (f[0] >= surf.points().size())
77 || (f[1] < 0) || (f[1] >= surf.points().size())
78 || (f[2] < 0) || (f[2] >= surf.points().size())
81 WarningIn("validTri(const triSurface&, const label)")
82 << "triangle " << faceI << " vertices " << f
83 << " uses point indices outside point range 0.."
84 << surf.points().size()-1 << endl;
89 if ((f[0] == f[1]) || (f[0] == f[2]) || (f[1] == f[2]))
91 WarningIn("validTri(const triSurface&, const label)")
92 << "triangle " << faceI
93 << " uses non-unique vertices " << f
94 << " coords:" << f.points(surf.points())
99 // duplicate triangle check
101 const labelList& fFaces = surf.faceFaces()[faceI];
103 // Check if faceNeighbours use same points as this face.
104 // Note: discards normal information - sides of baffle are merged.
107 label nbrFaceI = fFaces[i];
109 if (nbrFaceI <= faceI)
111 // lower numbered faces already checked
115 const labelledTri& nbrF = surf[nbrFaceI];
119 ((f[0] == nbrF[0]) || (f[0] == nbrF[1]) || (f[0] == nbrF[2]))
120 && ((f[1] == nbrF[0]) || (f[1] == nbrF[1]) || (f[1] == nbrF[2]))
121 && ((f[2] == nbrF[0]) || (f[2] == nbrF[1]) || (f[2] == nbrF[2]))
124 WarningIn("validTri(const triSurface&, const label)")
125 << "triangle " << faceI << " vertices " << f
126 << " has the same vertices as triangle " << nbrFaceI
127 << " vertices " << nbrF
128 << " coords:" << f.points(surf.points())
143 const scalarField& vals
146 scalar dist = nBins/(max - min);
148 labelList binCount(nBins, 0);
152 scalar val = vals[i];
156 if (Foam::mag(val - min) < SMALL)
160 else if (val >= max - SMALL)
166 index = label((val - min)*dist);
168 if ((index < 0) || (index >= nBins))
172 "countBins(const scalar, const scalar, const label"
173 ", const scalarField&)"
174 ) << "value " << val << " at index " << i
175 << " outside range " << min << " .. " << max << endl;
194 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
197 int main(int argc, char *argv[])
199 argList::noParallel();
201 argList::validArgs.clear();
202 argList::validArgs.append("surface file");
203 argList::validOptions.insert("checkSelfIntersection", "");
204 argList::validOptions.insert("verbose", "");
205 argList args(argc, argv);
207 bool checkSelfIntersection = args.optionFound("checkSelfIntersection");
208 bool verbose = args.optionFound("verbose");
210 fileName surfFileName(args.additionalArgs()[0]);
211 Pout<< "Reading surface from " << surfFileName << " ..." << nl << endl;
217 triSurface surf(surfFileName);
220 Pout<< "Statistics:" << endl;
221 surf.writeStats(Pout);
229 labelList regionSize(surf.patches().size(), 0);
233 label region = surf[faceI].region();
235 if (region < 0 || region >= regionSize.size())
237 WarningIn(args.executable())
238 << "Triangle " << faceI << " vertices " << surf[faceI]
239 << " has region " << region << " which is outside the range"
240 << " of regions 0.." << surf.patches().size()-1
245 regionSize[region]++;
249 Pout<< "Region\tSize" << nl
250 << "------\t----" << nl;
251 forAll(surf.patches(), patchI)
253 Pout<< surf.patches()[patchI].name() << '\t'
254 << regionSize[patchI] << nl;
264 DynamicList<label> illegalFaces(surf.size()/100 + 1);
268 if (!validTri(verbose, surf, faceI))
270 illegalFaces.append(faceI);
274 if (illegalFaces.size())
276 Pout<< "Surface has " << illegalFaces.size()
277 << " illegal triangles." << endl;
279 OFstream str("illegalFaces");
280 Pout<< "Dumping conflicting face labels to " << str.name() << endl
281 << "Paste this into the input for surfaceSubset" << endl;
286 Pout<< "Surface has no illegal triangles." << endl;
297 scalarField triQ(surf.size(), 0);
300 const labelledTri& f = surf[faceI];
302 if (f[0] == f[1] || f[0] == f[2] || f[1] == f[2])
304 //WarningIn(args.executable())
305 // << "Illegal triangle " << faceI << " vertices " << f
306 // << " coords " << f.points(surf.points()) << endl;
317 vector ba(tri.b() - tri.a());
318 ba /= mag(ba) + VSMALL;
320 vector ca(tri.c() - tri.a());
321 ca /= mag(ca) + VSMALL;
323 if (mag(ba&ca) > 1-1E-3)
329 triQ[faceI] = triPointRef
339 labelList binCount = countBins(0, 1, 20, triQ);
341 Pout<< "Triangle quality (equilateral=1, collapsed=0):"
348 scalar dist = (1.0 - 0.0)/20.0;
350 forAll(binCount, binI)
352 Pout<< " " << min << " .. " << min+dist << " : "
353 << 1.0/surf.size() * binCount[binI]
359 label minIndex = findMin(triQ);
360 label maxIndex = findMax(triQ);
362 Pout<< " min " << triQ[minIndex] << " for triangle " << minIndex
364 << " max " << triQ[maxIndex] << " for triangle " << maxIndex
369 if (triQ[minIndex] < SMALL)
371 WarningIn(args.executable()) << "Minimum triangle quality is "
372 << triQ[minIndex] << ". This might give problems in"
373 << " self-intersection testing later on." << endl;
376 // Dump for subsetting
378 DynamicList<label> problemFaces(surf.size()/100+1);
382 if (triQ[faceI] < 1E-11)
384 problemFaces.append(faceI);
387 OFstream str("badFaces");
389 Pout<< "Dumping bad quality faces to " << str.name() << endl
390 << "Paste this into the input for surfaceSubset" << nl
402 const edgeList& edges = surf.edges();
403 const pointField& localPoints = surf.localPoints();
405 scalarField edgeMag(edges.size());
409 edgeMag[edgeI] = edges[edgeI].mag(localPoints);
412 label minEdgeI = findMin(edgeMag);
413 label maxEdgeI = findMax(edgeMag);
415 const edge& minE = edges[minEdgeI];
416 const edge& maxE = edges[maxEdgeI];
419 Pout<< "Edges:" << nl
420 << " min " << edgeMag[minEdgeI] << " for edge " << minEdgeI
421 << " points " << localPoints[minE[0]] << localPoints[minE[1]]
423 << " max " << edgeMag[maxEdgeI] << " for edge " << maxEdgeI
424 << " points " << localPoints[maxE[0]] << localPoints[maxE[1]]
434 const edgeList& edges = surf.edges();
435 const pointField& localPoints = surf.localPoints();
437 const boundBox bb(localPoints);
438 scalar smallDim = 1E-6 * bb.mag();
440 Pout<< "Checking for points less than 1E-6 of bounding box ("
441 << bb.span() << " meter) apart."
445 SortableList<scalar> sortedMag(mag(localPoints));
449 for (label i = 1; i < sortedMag.size(); i++)
451 label ptI = sortedMag.indices()[i];
453 label prevPtI = sortedMag.indices()[i-1];
455 if (mag(localPoints[ptI] - localPoints[prevPtI]) < smallDim)
457 // Check if neighbours.
458 const labelList& pEdges = surf.pointEdges()[ptI];
464 const edge& e = edges[pEdges[i]];
466 if (e[0] == prevPtI || e[1] == prevPtI)
468 // point1 and point0 are connected through edge.
479 Pout<< " close unconnected points "
480 << ptI << ' ' << localPoints[ptI]
481 << " and " << prevPtI << ' '
482 << localPoints[prevPtI]
484 << mag(localPoints[ptI] - localPoints[prevPtI])
489 Pout<< " small edge between points "
490 << ptI << ' ' << localPoints[ptI]
491 << " and " << prevPtI << ' '
492 << localPoints[prevPtI]
494 << mag(localPoints[ptI] - localPoints[prevPtI])
500 Pout<< "Found " << nClose << " nearby points." << nl
509 DynamicList<label> problemFaces(surf.size()/100 + 1);
511 const labelListList& eFaces = surf.edgeFaces();
513 label nSingleEdges = 0;
514 forAll(eFaces, edgeI)
516 const labelList& myFaces = eFaces[edgeI];
518 if (myFaces.size() == 1)
520 problemFaces.append(myFaces[0]);
526 label nMultEdges = 0;
527 forAll(eFaces, edgeI)
529 const labelList& myFaces = eFaces[edgeI];
531 if (myFaces.size() > 2)
533 forAll(myFaces, myFaceI)
535 problemFaces.append(myFaces[myFaceI]);
541 problemFaces.shrink();
543 if ((nSingleEdges != 0) || (nMultEdges != 0))
545 Pout<< "Surface is not closed since not all edges connected to "
546 << "two faces:" << endl
547 << " connected to one face : " << nSingleEdges << endl
548 << " connected to >2 faces : " << nMultEdges << endl;
550 Pout<< "Conflicting face labels:" << problemFaces.size() << endl;
552 OFstream str("problemFaces");
554 Pout<< "Dumping conflicting face labels to " << str.name() << endl
555 << "Paste this into the input for surfaceSubset" << endl;
561 Pout<< "Surface is closed. All edges connected to two faces." << endl;
567 // Check singly connected domain
568 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
571 label numZones = surf.markZones(boolList(surf.nEdges(), false), faceZone);
573 Pout<< "Number of unconnected parts : " << numZones << endl;
577 Pout<< "Splitting surface into parts ..." << endl << endl;
579 fileName surfFileNameBase(surfFileName.name());
581 for(label zone = 0; zone < numZones; zone++)
583 boolList includeMap(surf.size(), false);
585 forAll(faceZone, faceI)
587 if (faceZone[faceI] == zone)
589 includeMap[faceI] = true;
608 surfFileNameBase.lessExt()
614 Pout<< "writing part " << zone << " size " << subSurf.size()
615 << " to " << subFileName << endl;
617 subSurf.write(subFileName);
628 labelHashSet borderEdge(surf.size()/1000);
629 PatchTools::checkOrientation(surf, false, &borderEdge);
632 // Colour all faces into zones using borderEdge
634 labelList normalZone;
635 label numNormalZones = PatchTools::markZones(surf, borderEdge, normalZone);
638 << "Number of zones (connected area with consistent normal) : "
639 << numNormalZones << endl;
641 if (numNormalZones > 1)
643 Pout<< "More than one normal orientation." << endl;
649 // Check self-intersection
650 // ~~~~~~~~~~~~~~~~~~~~~~~
652 if (checkSelfIntersection)
654 Pout<< "Checking self-intersection." << endl;
656 triSurfaceSearch querySurf(surf);
657 surfaceIntersection inter(querySurf);
659 if (inter.cutEdges().empty() && inter.cutPoints().empty())
661 Pout<< "Surface is not self-intersecting" << endl;
665 Pout<< "Surface is self-intersecting" << endl;
666 Pout<< "Writing edges of intersection to selfInter.obj" << endl;
668 OFstream intStream("selfInter.obj");
669 forAll(inter.cutPoints(), cutPointI)
671 const point& pt = inter.cutPoints()[cutPointI];
673 intStream << "v " << pt.x() << ' ' << pt.y() << ' ' << pt.z()
676 forAll(inter.cutEdges(), cutEdgeI)
678 const edge& e = inter.cutEdges()[cutEdgeI];
680 intStream << "l " << e.start()+1 << ' ' << e.end()+1 << endl;
687 Pout<< "End\n" << endl;
693 // ************************ vim: set sw=4 sts=4 et: ************************ //