1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | foam-extend: Open Source CFD
5 \\ / A nd | For copyright notice see file Copyright
7 -------------------------------------------------------------------------------
9 This file is part of foam-extend.
11 foam-extend 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 3 of the License, or (at your
14 option) any later version.
16 foam-extend is distributed in the hope that it will be useful, but
17 WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with foam-extend. 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?
39 bool validTri(const bool verbose, const triSurface& surf, const label faceI)
41 // Simple check on indices ok.
43 const labelledTri& f = surf[faceI];
47 (f[0] < 0) || (f[0] >= surf.points().size())
48 || (f[1] < 0) || (f[1] >= surf.points().size())
49 || (f[2] < 0) || (f[2] >= surf.points().size())
52 WarningIn("validTri(const triSurface&, const label)")
53 << "triangle " << faceI << " vertices " << f
54 << " uses point indices outside point range 0.."
55 << surf.points().size()-1 << endl;
60 if ((f[0] == f[1]) || (f[0] == f[2]) || (f[1] == f[2]))
62 WarningIn("validTri(const triSurface&, const label)")
63 << "triangle " << faceI
64 << " uses non-unique vertices " << f
65 << " coords:" << f.points(surf.points())
70 // Duplicate triangle check
72 const labelList& fFaces = surf.faceFaces()[faceI];
74 // Check if faceNeighbours use same points as this face.
75 // Note: discards normal information - sides of baffle are merged.
78 label nbrFaceI = fFaces[i];
80 if (nbrFaceI <= faceI)
82 // lower numbered faces already checked
86 const labelledTri& nbrF = surf[nbrFaceI];
90 ((f[0] == nbrF[0]) || (f[0] == nbrF[1]) || (f[0] == nbrF[2]))
91 && ((f[1] == nbrF[0]) || (f[1] == nbrF[1]) || (f[1] == nbrF[2]))
92 && ((f[2] == nbrF[0]) || (f[2] == nbrF[1]) || (f[2] == nbrF[2]))
95 WarningIn("validTri(const triSurface&, const label)")
96 << "triangle " << faceI << " vertices " << f
97 << " has the same vertices as triangle " << nbrFaceI
98 << " vertices " << nbrF
99 << " coords:" << f.points(surf.points())
114 const scalarField& vals
117 scalar dist = nBins/(max - min);
119 labelList binCount(nBins, 0);
123 scalar val = vals[i];
127 if (Foam::mag(val - min) < SMALL)
131 else if (val >= max - SMALL)
137 index = label((val - min)*dist);
139 if ((index < 0) || (index >= nBins))
143 "countBins(const scalar, const scalar, const label"
144 ", const scalarField&)"
145 ) << "value " << val << " at index " << i
146 << " outside range " << min << " .. " << max << endl;
165 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
168 int main(int argc, char *argv[])
170 argList::noParallel();
172 argList::validArgs.clear();
173 argList::validArgs.append("surface file");
174 argList::validOptions.insert("checkSelfIntersection", "");
175 argList::validOptions.insert("verbose", "");
176 argList args(argc, argv);
178 bool checkSelfIntersection = args.optionFound("checkSelfIntersection");
179 bool verbose = args.optionFound("verbose");
181 fileName surfFileName(args.additionalArgs()[0]);
182 Pout<< "Reading surface from " << surfFileName << " ..." << nl << endl;
188 triSurface surf(surfFileName);
189 const pointField& surfPoints = surf.points();
192 Pout<< "Statistics:" << endl;
193 surf.writeStats(Pout);
201 labelList regionSize(surf.patches().size(), 0);
202 scalarField regionSumArea(surf.patches().size(), 0);
206 label region = surf[faceI].region();
208 if (region < 0 || region >= regionSize.size())
210 WarningIn(args.executable())
211 << "Triangle " << faceI << " vertices " << surf[faceI]
212 << " has region " << region
213 << " which is outside the range"
214 << " of regions 0.." << surf.patches().size() - 1
219 regionSize[region]++;
220 regionSumArea[region] += surf[faceI].mag(surfPoints);
224 Pout<< "Region\tSize\tArea" << nl
225 << "------\t----\t----" << nl;
226 forAll(surf.patches(), patchI)
228 Pout<< surf.patches()[patchI].name() << tab
229 << regionSize[patchI] << tab << regionSumArea[patchI] << nl;
239 DynamicList<label> illegalFaces(surf.size()/100 + 1);
243 if (!validTri(verbose, surf, faceI))
245 illegalFaces.append(faceI);
249 if (illegalFaces.size())
251 Pout<< "Surface has " << illegalFaces.size()
252 << " illegal triangles." << endl;
254 OFstream str("illegalFaces");
255 Pout<< "Dumping conflicting face labels to " << str.name() << endl
256 << "Paste this into the input for surfaceSubset" << endl;
261 Pout<< "Surface has no illegal triangles." << endl;
272 scalarField triQ(surf.size(), 0);
275 const labelledTri& f = surf[faceI];
277 if (f[0] == f[1] || f[0] == f[2] || f[1] == f[2])
279 //WarningIn(args.executable())
280 // << "Illegal triangle " << faceI << " vertices " << f
281 // << " coords " << f.points(surf.points()) << endl;
292 vector ba(tri.b() - tri.a());
293 ba /= mag(ba) + VSMALL;
295 vector ca(tri.c() - tri.a());
296 ca /= mag(ca) + VSMALL;
298 if (mag(ba&ca) > 1-1E-3)
304 triQ[faceI] = triPointRef
314 labelList binCount = countBins(0, 1, 20, triQ);
316 Pout<< "Triangle quality (equilateral=1, collapsed=0):"
323 scalar dist = (1.0 - 0.0)/20.0;
325 forAll(binCount, binI)
327 Pout<< " " << min << " .. " << min+dist << " : "
328 << 1.0/surf.size() * binCount[binI]
334 label minIndex = findMin(triQ);
335 label maxIndex = findMax(triQ);
337 Pout<< " min " << triQ[minIndex] << " for triangle " << minIndex
339 << " max " << triQ[maxIndex] << " for triangle " << maxIndex
344 if (triQ[minIndex] < SMALL)
346 WarningIn(args.executable()) << "Minimum triangle quality is "
347 << triQ[minIndex] << ". This might give problems in"
348 << " self-intersection testing later on." << endl;
351 // Dump for subsetting
353 DynamicList<label> problemFaces(surf.size()/100+1);
357 if (triQ[faceI] < 1E-11)
359 problemFaces.append(faceI);
362 OFstream str("badFaces");
364 Pout<< "Dumping bad quality faces to " << str.name() << endl
365 << "Paste this into the input for surfaceSubset" << nl
377 const edgeList& edges = surf.edges();
378 const pointField& localPoints = surf.localPoints();
380 scalarField edgeMag(edges.size());
384 edgeMag[edgeI] = edges[edgeI].mag(localPoints);
387 label minEdgeI = findMin(edgeMag);
388 label maxEdgeI = findMax(edgeMag);
390 const edge& minE = edges[minEdgeI];
391 const edge& maxE = edges[maxEdgeI];
394 Pout<< "Edges:" << nl
395 << " min " << edgeMag[minEdgeI] << " for edge " << minEdgeI
396 << " points " << localPoints[minE[0]] << localPoints[minE[1]]
398 << " max " << edgeMag[maxEdgeI] << " for edge " << maxEdgeI
399 << " points " << localPoints[maxE[0]] << localPoints[maxE[1]]
409 const edgeList& edges = surf.edges();
410 const pointField& localPoints = surf.localPoints();
412 const boundBox bb(localPoints);
413 scalar smallDim = 1E-6 * bb.mag();
415 Pout<< "Checking for points less than 1E-6 of bounding box ("
416 << bb.span() << " meter) apart."
420 SortableList<scalar> sortedMag(mag(localPoints));
424 for (label i = 1; i < sortedMag.size(); i++)
426 label ptI = sortedMag.indices()[i];
428 label prevPtI = sortedMag.indices()[i-1];
430 if (mag(localPoints[ptI] - localPoints[prevPtI]) < smallDim)
432 // Check if neighbours.
433 const labelList& pEdges = surf.pointEdges()[ptI];
439 const edge& e = edges[pEdges[i]];
441 if (e[0] == prevPtI || e[1] == prevPtI)
443 // point1 and point0 are connected through edge.
454 Pout<< " close unconnected points "
455 << ptI << ' ' << localPoints[ptI]
456 << " and " << prevPtI << ' '
457 << localPoints[prevPtI]
459 << mag(localPoints[ptI] - localPoints[prevPtI])
464 Pout<< " small edge between points "
465 << ptI << ' ' << localPoints[ptI]
466 << " and " << prevPtI << ' '
467 << localPoints[prevPtI]
469 << mag(localPoints[ptI] - localPoints[prevPtI])
475 Pout<< "Found " << nClose << " nearby points." << nl
484 DynamicList<label> problemFaces(surf.size()/100 + 1);
486 const labelListList& eFaces = surf.edgeFaces();
488 label nSingleEdges = 0;
489 forAll(eFaces, edgeI)
491 const labelList& myFaces = eFaces[edgeI];
493 if (myFaces.size() == 1)
495 problemFaces.append(myFaces[0]);
501 label nMultEdges = 0;
502 forAll(eFaces, edgeI)
504 const labelList& myFaces = eFaces[edgeI];
506 if (myFaces.size() > 2)
508 forAll(myFaces, myFaceI)
510 problemFaces.append(myFaces[myFaceI]);
516 problemFaces.shrink();
518 if ((nSingleEdges != 0) || (nMultEdges != 0))
520 Pout<< "Surface is not closed since not all edges connected to "
521 << "two faces:" << endl
522 << " connected to one face : " << nSingleEdges << endl
523 << " connected to >2 faces : " << nMultEdges << endl;
525 Pout<< "Conflicting face labels:" << problemFaces.size() << endl;
527 OFstream str("problemFaces");
529 Pout<< "Dumping conflicting face labels to " << str.name() << endl
530 << "Paste this into the input for surfaceSubset" << endl;
536 Pout<< "Surface is closed. All edges connected to two faces." << endl;
542 // Check singly connected domain
543 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
546 label numZones = surf.markZones(boolList(surf.nEdges(), false), faceZone);
548 Pout<< "Number of unconnected parts : " << numZones << endl;
552 Pout<< "Splitting surface into parts ..." << endl << endl;
554 fileName surfFileNameBase(surfFileName.name());
556 for(label zone = 0; zone < numZones; zone++)
558 boolList includeMap(surf.size(), false);
560 forAll(faceZone, faceI)
562 if (faceZone[faceI] == zone)
564 includeMap[faceI] = true;
583 surfFileNameBase.lessExt()
589 Pout<< "writing part " << zone << " size " << subSurf.size()
590 << " to " << subFileName << endl;
592 subSurf.write(subFileName);
603 labelHashSet borderEdge(surf.size()/1000);
604 PatchTools::checkOrientation(surf, false, &borderEdge);
607 // Colour all faces into zones using borderEdge
609 labelList normalZone;
610 label numNormalZones = PatchTools::markZones(surf, borderEdge, normalZone);
613 << "Number of zones (connected area with consistent normal) : "
614 << numNormalZones << endl;
616 if (numNormalZones > 1)
618 Pout<< "More than one normal orientation." << endl;
624 // Check self-intersection
625 // ~~~~~~~~~~~~~~~~~~~~~~~
627 if (checkSelfIntersection)
629 Pout<< "Checking self-intersection." << endl;
631 triSurfaceSearch querySurf(surf);
632 surfaceIntersection inter(querySurf);
634 if (inter.cutEdges().empty() && inter.cutPoints().empty())
636 Pout<< "Surface is not self-intersecting" << endl;
640 Pout<< "Surface is self-intersecting" << endl;
641 Pout<< "Writing edges of intersection to selfInter.obj" << endl;
643 OFstream intStream("selfInter.obj");
644 forAll(inter.cutPoints(), cutPointI)
646 const point& pt = inter.cutPoints()[cutPointI];
648 intStream << "v " << pt.x() << ' ' << pt.y() << ' ' << pt.z()
651 forAll(inter.cutEdges(), cutEdgeI)
653 const edge& e = inter.cutEdges()[cutEdgeI];
655 intStream << "l " << e.start()+1 << ' ' << e.end()+1 << endl;
662 Pout<< "End\n" << endl;
668 // ************************************************************************* //