00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054 #include <OpenFOAM/triangle.H>
00055 #include <triSurface/triSurface.H>
00056 #include <meshTools/triSurfaceTools.H>
00057 #include <meshTools/triSurfaceSearch.H>
00058 #include <OpenFOAM/argList.H>
00059 #include <OpenFOAM/OFstream.H>
00060 #include <meshTools/surfaceIntersection.H>
00061 #include <OpenFOAM/SortableList.H>
00062 #include <OpenFOAM/PatchTools.H>
00063
00064 using namespace Foam;
00065
00066
00067 bool validTri(const bool verbose, const triSurface& surf, const label faceI)
00068 {
00069
00070
00071 const labelledTri& f = surf[faceI];
00072
00073 if
00074 (
00075 (f[0] < 0) || (f[0] >= surf.points().size())
00076 || (f[1] < 0) || (f[1] >= surf.points().size())
00077 || (f[2] < 0) || (f[2] >= surf.points().size())
00078 )
00079 {
00080 WarningIn("validTri(const triSurface&, const label)")
00081 << "triangle " << faceI << " vertices " << f
00082 << " uses point indices outside point range 0.."
00083 << surf.points().size()-1 << endl;
00084
00085 return false;
00086 }
00087
00088 if ((f[0] == f[1]) || (f[0] == f[2]) || (f[1] == f[2]))
00089 {
00090 WarningIn("validTri(const triSurface&, const label)")
00091 << "triangle " << faceI
00092 << " uses non-unique vertices " << f
00093 << " coords:" << f.points(surf.points())
00094 << endl;
00095 return false;
00096 }
00097
00098
00099
00100 const labelList& fFaces = surf.faceFaces()[faceI];
00101
00102
00103
00104 forAll(fFaces, i)
00105 {
00106 label nbrFaceI = fFaces[i];
00107
00108 if (nbrFaceI <= faceI)
00109 {
00110
00111 continue;
00112 }
00113
00114 const labelledTri& nbrF = surf[nbrFaceI];
00115
00116 if
00117 (
00118 ((f[0] == nbrF[0]) || (f[0] == nbrF[1]) || (f[0] == nbrF[2]))
00119 && ((f[1] == nbrF[0]) || (f[1] == nbrF[1]) || (f[1] == nbrF[2]))
00120 && ((f[2] == nbrF[0]) || (f[2] == nbrF[1]) || (f[2] == nbrF[2]))
00121 )
00122 {
00123 WarningIn("validTri(const triSurface&, const label)")
00124 << "triangle " << faceI << " vertices " << f
00125 << " has the same vertices as triangle " << nbrFaceI
00126 << " vertices " << nbrF
00127 << " coords:" << f.points(surf.points())
00128 << endl;
00129
00130 return false;
00131 }
00132 }
00133 return true;
00134 }
00135
00136
00137 labelList countBins
00138 (
00139 const scalar min,
00140 const scalar max,
00141 const label nBins,
00142 const scalarField& vals
00143 )
00144 {
00145 scalar dist = nBins/(max - min);
00146
00147 labelList binCount(nBins, 0);
00148
00149 forAll(vals, i)
00150 {
00151 scalar val = vals[i];
00152
00153 label index = -1;
00154
00155 if (Foam::mag(val - min) < SMALL)
00156 {
00157 index = 0;
00158 }
00159 else if (val >= max - SMALL)
00160 {
00161 index = nBins - 1;
00162 }
00163 else
00164 {
00165 index = label((val - min)*dist);
00166
00167 if ((index < 0) || (index >= nBins))
00168 {
00169 WarningIn
00170 (
00171 "countBins(const scalar, const scalar, const label"
00172 ", const scalarField&)"
00173 ) << "value " << val << " at index " << i
00174 << " outside range " << min << " .. " << max << endl;
00175
00176 if (index < 0)
00177 {
00178 index = 0;
00179 }
00180 else
00181 {
00182 index = nBins - 1;
00183 }
00184 }
00185 }
00186 binCount[index]++;
00187 }
00188
00189 return binCount;
00190 }
00191
00192
00193
00194
00195
00196 int main(int argc, char *argv[])
00197 {
00198 argList::noParallel();
00199
00200 argList::validArgs.clear();
00201 argList::validArgs.append("surface file");
00202 argList::validOptions.insert("checkSelfIntersection", "");
00203 argList::validOptions.insert("verbose", "");
00204 argList args(argc, argv);
00205
00206 bool checkSelfIntersection = args.optionFound("checkSelfIntersection");
00207 bool verbose = args.optionFound("verbose");
00208
00209 fileName surfFileName(args.additionalArgs()[0]);
00210 Pout<< "Reading surface from " << surfFileName << " ..." << nl << endl;
00211
00212
00213
00214
00215
00216 triSurface surf(surfFileName);
00217
00218
00219 Pout<< "Statistics:" << endl;
00220 surf.writeStats(Pout);
00221 Pout<< endl;
00222
00223
00224
00225
00226
00227 {
00228 labelList regionSize(surf.patches().size(), 0);
00229
00230 forAll(surf, faceI)
00231 {
00232 label region = surf[faceI].region();
00233
00234 if (region < 0 || region >= regionSize.size())
00235 {
00236 WarningIn(args.executable())
00237 << "Triangle " << faceI << " vertices " << surf[faceI]
00238 << " has region " << region << " which is outside the range"
00239 << " of regions 0.." << surf.patches().size()-1
00240 << endl;
00241 }
00242 else
00243 {
00244 regionSize[region]++;
00245 }
00246 }
00247
00248 Pout<< "Region\tSize" << nl
00249 << "------\t----" << nl;
00250 forAll(surf.patches(), patchI)
00251 {
00252 Pout<< surf.patches()[patchI].name() << '\t'
00253 << regionSize[patchI] << nl;
00254 }
00255 Pout<< nl << endl;
00256 }
00257
00258
00259
00260
00261
00262 {
00263 DynamicList<label> illegalFaces(surf.size()/100 + 1);
00264
00265 forAll(surf, faceI)
00266 {
00267 if (!validTri(verbose, surf, faceI))
00268 {
00269 illegalFaces.append(faceI);
00270 }
00271 }
00272
00273 if (illegalFaces.size())
00274 {
00275 Pout<< "Surface has " << illegalFaces.size()
00276 << " illegal triangles." << endl;
00277
00278 OFstream str("illegalFaces");
00279 Pout<< "Dumping conflicting face labels to " << str.name() << endl
00280 << "Paste this into the input for surfaceSubset" << endl;
00281 str << illegalFaces;
00282 }
00283 else
00284 {
00285 Pout<< "Surface has no illegal triangles." << endl;
00286 }
00287 Pout<< endl;
00288 }
00289
00290
00291
00292
00293
00294
00295 {
00296 scalarField triQ(surf.size(), 0);
00297 forAll(surf, faceI)
00298 {
00299 const labelledTri& f = surf[faceI];
00300
00301 if (f[0] == f[1] || f[0] == f[2] || f[1] == f[2])
00302 {
00303
00304
00305
00306 }
00307 else
00308 {
00309 triPointRef tri
00310 (
00311 surf.points()[f[0]],
00312 surf.points()[f[1]],
00313 surf.points()[f[2]]
00314 );
00315
00316 vector ba(tri.b() - tri.a());
00317 ba /= mag(ba) + VSMALL;
00318
00319 vector ca(tri.c() - tri.a());
00320 ca /= mag(ca) + VSMALL;
00321
00322 if (mag(ba&ca) > 1-1E-3)
00323 {
00324 triQ[faceI] = SMALL;
00325 }
00326 else
00327 {
00328 triQ[faceI] = triPointRef
00329 (
00330 surf.points()[f[0]],
00331 surf.points()[f[1]],
00332 surf.points()[f[2]]
00333 ).quality();
00334 }
00335 }
00336 }
00337
00338 labelList binCount = countBins(0, 1, 20, triQ);
00339
00340 Pout<< "Triangle quality (equilateral=1, collapsed=0):"
00341 << endl;
00342
00343
00344 OSstream& os = Pout;
00345 os.width(4);
00346
00347 scalar dist = (1.0 - 0.0)/20.0;
00348 scalar min = 0;
00349 forAll(binCount, binI)
00350 {
00351 Pout<< " " << min << " .. " << min+dist << " : "
00352 << 1.0/surf.size() * binCount[binI]
00353 << endl;
00354 min += dist;
00355 }
00356 Pout<< endl;
00357
00358 label minIndex = findMin(triQ);
00359 label maxIndex = findMax(triQ);
00360
00361 Pout<< " min " << triQ[minIndex] << " for triangle " << minIndex
00362 << nl
00363 << " max " << triQ[maxIndex] << " for triangle " << maxIndex
00364 << nl
00365 << endl;
00366
00367
00368 if (triQ[minIndex] < SMALL)
00369 {
00370 WarningIn(args.executable()) << "Minimum triangle quality is "
00371 << triQ[minIndex] << ". This might give problems in"
00372 << " self-intersection testing later on." << endl;
00373 }
00374
00375
00376 {
00377 DynamicList<label> problemFaces(surf.size()/100+1);
00378
00379 forAll(triQ, faceI)
00380 {
00381 if (triQ[faceI] < 1E-11)
00382 {
00383 problemFaces.append(faceI);
00384 }
00385 }
00386 OFstream str("badFaces");
00387
00388 Pout<< "Dumping bad quality faces to " << str.name() << endl
00389 << "Paste this into the input for surfaceSubset" << nl
00390 << nl << endl;
00391
00392 str << problemFaces;
00393 }
00394 }
00395
00396
00397
00398
00399
00400 {
00401 const edgeList& edges = surf.edges();
00402 const pointField& localPoints = surf.localPoints();
00403
00404 scalarField edgeMag(edges.size());
00405
00406 forAll(edges, edgeI)
00407 {
00408 edgeMag[edgeI] = edges[edgeI].mag(localPoints);
00409 }
00410
00411 label minEdgeI = findMin(edgeMag);
00412 label maxEdgeI = findMax(edgeMag);
00413
00414 const edge& minE = edges[minEdgeI];
00415 const edge& maxE = edges[maxEdgeI];
00416
00417
00418 Pout<< "Edges:" << nl
00419 << " min " << edgeMag[minEdgeI] << " for edge " << minEdgeI
00420 << " points " << localPoints[minE[0]] << localPoints[minE[1]]
00421 << nl
00422 << " max " << edgeMag[maxEdgeI] << " for edge " << maxEdgeI
00423 << " points " << localPoints[maxE[0]] << localPoints[maxE[1]]
00424 << nl
00425 << endl;
00426 }
00427
00428
00429
00430
00431
00432 {
00433 const edgeList& edges = surf.edges();
00434 const pointField& localPoints = surf.localPoints();
00435
00436 const boundBox bb(localPoints);
00437 scalar smallDim = 1E-6 * bb.mag();
00438
00439 Pout<< "Checking for points less than 1E-6 of bounding box ("
00440 << bb.span() << " meter) apart."
00441 << endl;
00442
00443
00444 SortableList<scalar> sortedMag(mag(localPoints));
00445
00446 label nClose = 0;
00447
00448 for (label i = 1; i < sortedMag.size(); i++)
00449 {
00450 label ptI = sortedMag.indices()[i];
00451
00452 label prevPtI = sortedMag.indices()[i-1];
00453
00454 if (mag(localPoints[ptI] - localPoints[prevPtI]) < smallDim)
00455 {
00456
00457 const labelList& pEdges = surf.pointEdges()[ptI];
00458
00459 label edgeI = -1;
00460
00461 forAll(pEdges, i)
00462 {
00463 const edge& e = edges[pEdges[i]];
00464
00465 if (e[0] == prevPtI || e[1] == prevPtI)
00466 {
00467
00468 edgeI = pEdges[i];
00469
00470 break;
00471 }
00472 }
00473
00474 nClose++;
00475
00476 if (edgeI == -1)
00477 {
00478 Pout<< " close unconnected points "
00479 << ptI << ' ' << localPoints[ptI]
00480 << " and " << prevPtI << ' '
00481 << localPoints[prevPtI]
00482 << " distance:"
00483 << mag(localPoints[ptI] - localPoints[prevPtI])
00484 << endl;
00485 }
00486 else
00487 {
00488 Pout<< " small edge between points "
00489 << ptI << ' ' << localPoints[ptI]
00490 << " and " << prevPtI << ' '
00491 << localPoints[prevPtI]
00492 << " distance:"
00493 << mag(localPoints[ptI] - localPoints[prevPtI])
00494 << endl;
00495 }
00496 }
00497 }
00498
00499 Pout<< "Found " << nClose << " nearby points." << nl
00500 << endl;
00501 }
00502
00503
00504
00505
00506
00507
00508 DynamicList<label> problemFaces(surf.size()/100 + 1);
00509
00510 const labelListList& eFaces = surf.edgeFaces();
00511
00512 label nSingleEdges = 0;
00513 forAll(eFaces, edgeI)
00514 {
00515 const labelList& myFaces = eFaces[edgeI];
00516
00517 if (myFaces.size() == 1)
00518 {
00519 problemFaces.append(myFaces[0]);
00520
00521 nSingleEdges++;
00522 }
00523 }
00524
00525 label nMultEdges = 0;
00526 forAll(eFaces, edgeI)
00527 {
00528 const labelList& myFaces = eFaces[edgeI];
00529
00530 if (myFaces.size() > 2)
00531 {
00532 forAll(myFaces, myFaceI)
00533 {
00534 problemFaces.append(myFaces[myFaceI]);
00535 }
00536
00537 nMultEdges++;
00538 }
00539 }
00540 problemFaces.shrink();
00541
00542 if ((nSingleEdges != 0) || (nMultEdges != 0))
00543 {
00544 Pout<< "Surface is not closed since not all edges connected to "
00545 << "two faces:" << endl
00546 << " connected to one face : " << nSingleEdges << endl
00547 << " connected to >2 faces : " << nMultEdges << endl;
00548
00549 Pout<< "Conflicting face labels:" << problemFaces.size() << endl;
00550
00551 OFstream str("problemFaces");
00552
00553 Pout<< "Dumping conflicting face labels to " << str.name() << endl
00554 << "Paste this into the input for surfaceSubset" << endl;
00555
00556 str << problemFaces;
00557 }
00558 else
00559 {
00560 Pout<< "Surface is closed. All edges connected to two faces." << endl;
00561 }
00562 Pout<< endl;
00563
00564
00565
00566
00567
00568
00569 labelList faceZone;
00570 label numZones = surf.markZones(boolList(surf.nEdges(), false), faceZone);
00571
00572 Pout<< "Number of unconnected parts : " << numZones << endl;
00573
00574 if (numZones > 1)
00575 {
00576 Pout<< "Splitting surface into parts ..." << endl << endl;
00577
00578 fileName surfFileNameBase(surfFileName.name());
00579
00580 for(label zone = 0; zone < numZones; zone++)
00581 {
00582 boolList includeMap(surf.size(), false);
00583
00584 forAll(faceZone, faceI)
00585 {
00586 if (faceZone[faceI] == zone)
00587 {
00588 includeMap[faceI] = true;
00589 }
00590 }
00591
00592 labelList pointMap;
00593 labelList faceMap;
00594
00595 triSurface subSurf
00596 (
00597 surf.subsetMesh
00598 (
00599 includeMap,
00600 pointMap,
00601 faceMap
00602 )
00603 );
00604
00605 fileName subFileName
00606 (
00607 surfFileNameBase.lessExt()
00608 + "_"
00609 + name(zone)
00610 + ".ftr"
00611 );
00612
00613 Pout<< "writing part " << zone << " size " << subSurf.size()
00614 << " to " << subFileName << endl;
00615
00616 subSurf.write(subFileName);
00617 }
00618
00619 return 0;
00620 }
00621
00622
00623
00624
00625
00626
00627 labelHashSet borderEdge(surf.size()/1000);
00628 PatchTools::checkOrientation(surf, false, &borderEdge);
00629
00630
00631
00632
00633 labelList normalZone;
00634 label numNormalZones = PatchTools::markZones(surf, borderEdge, normalZone);
00635
00636 Pout<< endl
00637 << "Number of zones (connected area with consistent normal) : "
00638 << numNormalZones << endl;
00639
00640 if (numNormalZones > 1)
00641 {
00642 Pout<< "More than one normal orientation." << endl;
00643 }
00644 Pout<< endl;
00645
00646
00647
00648
00649
00650
00651 if (checkSelfIntersection)
00652 {
00653 Pout<< "Checking self-intersection." << endl;
00654
00655 triSurfaceSearch querySurf(surf);
00656 surfaceIntersection inter(querySurf);
00657
00658 if (inter.cutEdges().empty() && inter.cutPoints().empty())
00659 {
00660 Pout<< "Surface is not self-intersecting" << endl;
00661 }
00662 else
00663 {
00664 Pout<< "Surface is self-intersecting" << endl;
00665 Pout<< "Writing edges of intersection to selfInter.obj" << endl;
00666
00667 OFstream intStream("selfInter.obj");
00668 forAll(inter.cutPoints(), cutPointI)
00669 {
00670 const point& pt = inter.cutPoints()[cutPointI];
00671
00672 intStream << "v " << pt.x() << ' ' << pt.y() << ' ' << pt.z()
00673 << endl;
00674 }
00675 forAll(inter.cutEdges(), cutEdgeI)
00676 {
00677 const edge& e = inter.cutEdges()[cutEdgeI];
00678
00679 intStream << "l " << e.start()+1 << ' ' << e.end()+1 << endl;
00680 }
00681 }
00682 Pout<< endl;
00683 }
00684
00685
00686 Pout<< "End\n" << endl;
00687
00688 return 0;
00689 }
00690
00691
00692