1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright (C) 2011 OpenFOAM Foundation
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/>.
24 \*---------------------------------------------------------------------------*/
27 #include "triSurface.H"
28 #include "triSurfaceTools.H"
29 #include "triSurfaceSearch.H"
32 #include "surfaceIntersection.H"
33 #include "SortableList.H"
34 #include "PatchTools.H"
38 // Does face use valid vertices?
42 const triSurface& surf,
46 // Simple check on indices ok.
48 const labelledTri& f = surf[faceI];
52 if (f[fp] < 0 || f[fp] >= surf.points().size())
54 WarningIn("validTri(const triSurface&, const label)")
55 << "triangle " << faceI << " vertices " << f
56 << " uses point indices outside point range 0.."
57 << surf.points().size()-1 << endl;
62 if ((f[0] == f[1]) || (f[0] == f[2]) || (f[1] == f[2]))
64 WarningIn("validTri(const triSurface&, const label)")
65 << "triangle " << faceI
66 << " uses non-unique vertices " << f
67 << " coords:" << f.points(surf.points())
72 // duplicate triangle check
74 const labelList& fFaces = surf.faceFaces()[faceI];
76 // Check if faceNeighbours use same points as this face.
77 // Note: discards normal information - sides of baffle are merged.
80 label nbrFaceI = fFaces[i];
82 if (nbrFaceI <= faceI)
84 // lower numbered faces already checked
88 const labelledTri& nbrF = surf[nbrFaceI];
92 ((f[0] == nbrF[0]) || (f[0] == nbrF[1]) || (f[0] == nbrF[2]))
93 && ((f[1] == nbrF[0]) || (f[1] == nbrF[1]) || (f[1] == nbrF[2]))
94 && ((f[2] == nbrF[0]) || (f[2] == nbrF[1]) || (f[2] == nbrF[2]))
97 WarningIn("validTri(const triSurface&, const label)")
98 << "triangle " << faceI << " vertices " << f
99 << " has the same vertices as triangle " << nbrFaceI
100 << " vertices " << nbrF
101 << " coords:" << f.points(surf.points())
116 const scalarField& vals
119 scalar dist = nBins/(max - min);
121 labelList binCount(nBins, 0);
125 scalar val = vals[i];
129 if (Foam::mag(val - min) < SMALL)
133 else if (val >= max - SMALL)
139 index = label((val - min)*dist);
141 if ((index < 0) || (index >= nBins))
145 "countBins(const scalar, const scalar, const label"
146 ", const scalarField&)"
147 ) << "value " << val << " at index " << i
148 << " outside range " << min << " .. " << max << endl;
167 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
170 int main(int argc, char *argv[])
172 argList::noParallel();
173 argList::validArgs.append("surfaceFile");
174 argList::addBoolOption
176 "checkSelfIntersection",
177 "also check for self-intersection"
179 argList::addBoolOption
184 argList::addBoolOption
187 "write vertices/blocks for blockMeshDict"
190 argList args(argc, argv);
192 const fileName surfFileName = args[1];
193 const bool checkSelfIntersect = args.optionFound("checkSelfIntersection");
194 const bool verbose = args.optionFound("verbose");
196 Info<< "Reading surface from " << surfFileName << " ..." << nl << endl;
202 triSurface surf(surfFileName);
205 Info<< "Statistics:" << endl;
206 surf.writeStats(Info);
209 // write bounding box corners
210 if (args.optionFound("blockMesh"))
212 pointField cornerPts(boundBox(surf.points()).points());
214 Info<<"// blockMeshDict info" << nl
215 <<"vertices\n(" << nl;
216 forAll(cornerPts, ptI)
218 Info << " " << cornerPts[ptI] << nl;
221 // number of divisions needs adjustment later
225 <<" hex (0 1 2 3 4 5 6 7) (10 10 10) simpleGrading (1 1 1)\n"
228 Info<<"edges\n();" << nl
229 <<"patches\n();" << endl;
237 labelList regionSize(surf.patches().size(), 0);
241 label region = surf[faceI].region();
243 if (region < 0 || region >= regionSize.size())
245 WarningIn(args.executable())
246 << "Triangle " << faceI << " vertices " << surf[faceI]
247 << " has region " << region << " which is outside the range"
248 << " of regions 0.." << surf.patches().size()-1
253 regionSize[region]++;
257 Info<< "Region\tSize" << nl
258 << "------\t----" << nl;
259 forAll(surf.patches(), patchI)
261 Info<< surf.patches()[patchI].name() << '\t'
262 << regionSize[patchI] << nl;
272 DynamicList<label> illegalFaces(surf.size()/100 + 1);
276 if (!validTri(verbose, surf, faceI))
278 illegalFaces.append(faceI);
282 if (illegalFaces.size())
284 Info<< "Surface has " << illegalFaces.size()
285 << " illegal triangles." << endl;
287 OFstream str("illegalFaces");
288 Info<< "Dumping conflicting face labels to " << str.name() << endl
289 << "Paste this into the input for surfaceSubset" << endl;
294 Info<< "Surface has no illegal triangles." << endl;
305 scalarField triQ(surf.size(), 0);
308 const labelledTri& f = surf[faceI];
310 if (f[0] == f[1] || f[0] == f[2] || f[1] == f[2])
312 //WarningIn(args.executable())
313 // << "Illegal triangle " << faceI << " vertices " << f
314 // << " coords " << f.points(surf.points()) << endl;
325 vector ba(tri.b() - tri.a());
326 ba /= mag(ba) + VSMALL;
328 vector ca(tri.c() - tri.a());
329 ca /= mag(ca) + VSMALL;
331 if (mag(ba&ca) > 1-1E-3)
337 triQ[faceI] = triPointRef
347 labelList binCount = countBins(0, 1, 20, triQ);
349 Info<< "Triangle quality (equilateral=1, collapsed=0):"
356 scalar dist = (1.0 - 0.0)/20.0;
358 forAll(binCount, binI)
360 Info<< " " << min << " .. " << min+dist << " : "
361 << 1.0/surf.size() * binCount[binI]
367 label minIndex = findMin(triQ);
368 label maxIndex = findMax(triQ);
370 Info<< " min " << triQ[minIndex] << " for triangle " << minIndex
372 << " max " << triQ[maxIndex] << " for triangle " << maxIndex
377 if (triQ[minIndex] < SMALL)
379 WarningIn(args.executable()) << "Minimum triangle quality is "
380 << triQ[minIndex] << ". This might give problems in"
381 << " self-intersection testing later on." << endl;
384 // Dump for subsetting
386 DynamicList<label> problemFaces(surf.size()/100+1);
390 if (triQ[faceI] < 1E-11)
392 problemFaces.append(faceI);
396 if (!problemFaces.empty())
398 OFstream str("badFaces");
400 Info<< "Dumping bad quality faces to " << str.name() << endl
401 << "Paste this into the input for surfaceSubset" << nl
414 const edgeList& edges = surf.edges();
415 const pointField& localPoints = surf.localPoints();
417 scalarField edgeMag(edges.size());
421 edgeMag[edgeI] = edges[edgeI].mag(localPoints);
424 label minEdgeI = findMin(edgeMag);
425 label maxEdgeI = findMax(edgeMag);
427 const edge& minE = edges[minEdgeI];
428 const edge& maxE = edges[maxEdgeI];
431 Info<< "Edges:" << nl
432 << " min " << edgeMag[minEdgeI] << " for edge " << minEdgeI
433 << " points " << localPoints[minE[0]] << localPoints[minE[1]]
435 << " max " << edgeMag[maxEdgeI] << " for edge " << maxEdgeI
436 << " points " << localPoints[maxE[0]] << localPoints[maxE[1]]
446 const edgeList& edges = surf.edges();
447 const pointField& localPoints = surf.localPoints();
449 const boundBox bb(localPoints);
450 scalar smallDim = 1E-6 * bb.mag();
452 Info<< "Checking for points less than 1E-6 of bounding box ("
453 << bb.span() << " meter) apart."
457 SortableList<scalar> sortedMag(mag(localPoints));
461 for (label i = 1; i < sortedMag.size(); i++)
463 label ptI = sortedMag.indices()[i];
465 label prevPtI = sortedMag.indices()[i-1];
467 if (mag(localPoints[ptI] - localPoints[prevPtI]) < smallDim)
469 // Check if neighbours.
470 const labelList& pEdges = surf.pointEdges()[ptI];
476 const edge& e = edges[pEdges[i]];
478 if (e[0] == prevPtI || e[1] == prevPtI)
480 // point1 and point0 are connected through edge.
491 Info<< " close unconnected points "
492 << ptI << ' ' << localPoints[ptI]
493 << " and " << prevPtI << ' '
494 << localPoints[prevPtI]
496 << mag(localPoints[ptI] - localPoints[prevPtI])
501 Info<< " small edge between points "
502 << ptI << ' ' << localPoints[ptI]
503 << " and " << prevPtI << ' '
504 << localPoints[prevPtI]
506 << mag(localPoints[ptI] - localPoints[prevPtI])
512 Info<< "Found " << nClose << " nearby points." << nl
521 DynamicList<label> problemFaces(surf.size()/100 + 1);
523 const labelListList& eFaces = surf.edgeFaces();
525 label nSingleEdges = 0;
526 forAll(eFaces, edgeI)
528 const labelList& myFaces = eFaces[edgeI];
530 if (myFaces.size() == 1)
532 problemFaces.append(myFaces[0]);
538 label nMultEdges = 0;
539 forAll(eFaces, edgeI)
541 const labelList& myFaces = eFaces[edgeI];
543 if (myFaces.size() > 2)
545 forAll(myFaces, myFaceI)
547 problemFaces.append(myFaces[myFaceI]);
553 problemFaces.shrink();
555 if ((nSingleEdges != 0) || (nMultEdges != 0))
557 Info<< "Surface is not closed since not all edges connected to "
558 << "two faces:" << endl
559 << " connected to one face : " << nSingleEdges << endl
560 << " connected to >2 faces : " << nMultEdges << endl;
562 Info<< "Conflicting face labels:" << problemFaces.size() << endl;
564 OFstream str("problemFaces");
566 Info<< "Dumping conflicting face labels to " << str.name() << endl
567 << "Paste this into the input for surfaceSubset" << endl;
573 Info<< "Surface is closed. All edges connected to two faces." << endl;
579 // Check singly connected domain
580 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
583 label numZones = surf.markZones(boolList(surf.nEdges(), false), faceZone);
585 Info<< "Number of unconnected parts : " << numZones << endl;
589 Info<< "Splitting surface into parts ..." << endl << endl;
591 fileName surfFileNameBase(surfFileName.name());
593 for (label zone = 0; zone < numZones; zone++)
595 boolList includeMap(surf.size(), false);
597 forAll(faceZone, faceI)
599 if (faceZone[faceI] == zone)
601 includeMap[faceI] = true;
620 surfFileNameBase.lessExt()
626 Info<< "writing part " << zone << " size " << subSurf.size()
627 << " to " << subFileName << endl;
629 subSurf.write(subFileName);
640 labelHashSet borderEdge(surf.size()/1000);
641 PatchTools::checkOrientation(surf, false, &borderEdge);
644 // Colour all faces into zones using borderEdge
646 labelList normalZone;
647 label numNormalZones = PatchTools::markZones(surf, borderEdge, normalZone);
650 << "Number of zones (connected area with consistent normal) : "
651 << numNormalZones << endl;
653 if (numNormalZones > 1)
655 Info<< "More than one normal orientation." << endl;
661 // Check self-intersection
662 // ~~~~~~~~~~~~~~~~~~~~~~~
664 if (checkSelfIntersect)
666 Info<< "Checking self-intersection." << endl;
668 triSurfaceSearch querySurf(surf);
669 surfaceIntersection inter(querySurf);
671 if (inter.cutEdges().empty() && inter.cutPoints().empty())
673 Info<< "Surface is not self-intersecting" << endl;
677 Info<< "Surface is self-intersecting" << endl;
678 Info<< "Writing edges of intersection to selfInter.obj" << endl;
680 OFstream intStream("selfInter.obj");
681 forAll(inter.cutPoints(), cutPointI)
683 const point& pt = inter.cutPoints()[cutPointI];
685 intStream << "v " << pt.x() << ' ' << pt.y() << ' ' << pt.z()
688 forAll(inter.cutEdges(), cutEdgeI)
690 const edge& e = inter.cutEdges()[cutEdgeI];
692 intStream << "l " << e.start()+1 << ' ' << e.end()+1 << endl;
699 Info<< "\nEnd\n" << endl;
705 // ************************************************************************* //