surfaceFeatureExtract.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration |
5  \\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8 License
9  This file is part of OpenFOAM.
10 
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.
15 
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
19  for more details.
20 
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/>.
23 
24 Application
25  surfaceFeatureExtract
26 
27 Description
28  Extracts and writes surface features to file. All but the basic feature
29  extraction is WIP.
30 
31  Curvature calculation is an implementation of the algorithm from:
32 
33  "Estimating Curvatures and their Derivatives on Triangle Meshes"
34  by S. Rusinkiewicz
35 
36 \*---------------------------------------------------------------------------*/
37 
38 #include "argList.H"
39 #include "Time.H"
40 #include "triSurface.H"
41 #include "surfaceFeatures.H"
42 #include "featureEdgeMesh.H"
44 #include "treeBoundBox.H"
45 #include "meshTools.H"
46 #include "OFstream.H"
47 #include "triSurfaceMesh.H"
48 #include "vtkSurfaceWriter.H"
49 #include "triSurfaceFields.H"
50 #include "indexedOctree.H"
51 #include "treeDataEdge.H"
52 #include "unitConversion.H"
53 #include "plane.H"
54 #include "tensor2D.H"
55 #include "symmTensor2D.H"
56 #include "point.H"
57 #include "triadField.H"
58 #include "transform.H"
59 
60 using namespace Foam;
61 
62 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
63 
64 scalar calcVertexNormalWeight
65 (
66  const triFace& f,
67  const label pI,
68  const vector& fN,
69  const pointField& points
70 )
71 {
72  label index = findIndex(f, pI);
73 
74  if (index == -1)
75  {
77  << "Point not in face" << abort(FatalError);
78  }
79 
80  const vector e1 = points[f[index]] - points[f[f.fcIndex(index)]];
81  const vector e2 = points[f[index]] - points[f[f.rcIndex(index)]];
82 
83  return mag(fN)/(magSqr(e1)*magSqr(e2) + VSMALL);
84 }
85 
86 
87 point randomPointInPlane(const plane& p)
88 {
89  // Perturb base point
90  const point& refPt = p.refPoint();
91 
92  // ax + by + cz + d = 0
93  const FixedList<scalar, 4>& planeCoeffs = p.planeCoeffs();
94 
95  const scalar perturbX = refPt.x() + 1e-3;
96  const scalar perturbY = refPt.y() + 1e-3;
97  const scalar perturbZ = refPt.z() + 1e-3;
98 
99  if (mag(planeCoeffs[2]) < SMALL)
100  {
101  if (mag(planeCoeffs[1]) < SMALL)
102  {
103  const scalar x =
104  -1.0
105  *(
106  planeCoeffs[3]
107  + planeCoeffs[1]*perturbY
108  + planeCoeffs[2]*perturbZ
109  )/planeCoeffs[0];
110 
111  return point
112  (
113  x,
114  perturbY,
115  perturbZ
116  );
117  }
118 
119  const scalar y =
120  -1.0
121  *(
122  planeCoeffs[3]
123  + planeCoeffs[0]*perturbX
124  + planeCoeffs[2]*perturbZ
125  )/planeCoeffs[1];
126 
127  return point
128  (
129  perturbX,
130  y,
131  perturbZ
132  );
133  }
134  else
135  {
136  const scalar z =
137  -1.0
138  *(
139  planeCoeffs[3]
140  + planeCoeffs[0]*perturbX
141  + planeCoeffs[1]*perturbY
142  )/planeCoeffs[2];
143 
144  return point
145  (
146  perturbX,
147  perturbY,
148  z
149  );
150  }
151 }
152 
153 
154 triadField calcVertexCoordSys
155 (
156  const triSurface& surf,
157  const vectorField& pointNormals
158 )
159 {
160  const pointField& points = surf.points();
161  const Map<label>& meshPointMap = surf.meshPointMap();
162 
163  triadField pointCoordSys(points.size());
164 
165  forAll(points, pI)
166  {
167  const point& pt = points[pI];
168  const vector& normal = pointNormals[meshPointMap[pI]];
169 
170  if (mag(normal) < SMALL)
171  {
172  pointCoordSys[meshPointMap[pI]] = triad::unset;
173  continue;
174  }
175 
176  plane p(pt, normal);
177 
178  // Pick random point in plane
179  vector dir1 = pt - randomPointInPlane(p);
180  dir1 /= mag(dir1);
181 
182  vector dir2 = dir1 ^ normal;
183  dir2 /= mag(dir2);
184 
185  pointCoordSys[meshPointMap[pI]] = triad(dir1, dir2, normal);
186  }
187 
188  return pointCoordSys;
189 }
190 
191 
192 vectorField calcVertexNormals(const triSurface& surf)
193 {
194  // Weighted average of normals of faces attached to the vertex
195  // Weight = fA / (mag(e0)^2 * mag(e1)^2);
196 
197  Info<< "Calculating vertex normals" << endl;
198 
199  vectorField pointNormals(surf.nPoints(), Zero);
200 
201  const pointField& points = surf.points();
202  const labelListList& pointFaces = surf.pointFaces();
203  const labelList& meshPoints = surf.meshPoints();
204 
205  forAll(pointFaces, pI)
206  {
207  const labelList& pFaces = pointFaces[pI];
208 
209  forAll(pFaces, fI)
210  {
211  const label facei = pFaces[fI];
212  const triFace& f = surf[facei];
213 
214  vector fN = f.normal(points);
215 
216  scalar weight = calcVertexNormalWeight
217  (
218  f,
219  meshPoints[pI],
220  fN,
221  points
222  );
223 
224  pointNormals[pI] += weight*fN;
225  }
226 
227  pointNormals[pI] /= mag(pointNormals[pI]) + VSMALL;
228  }
229 
230  return pointNormals;
231 }
232 
233 
234 triSurfacePointScalarField calcCurvature
235 (
236  const word& name,
237  const Time& runTime,
238  const triSurface& surf,
239  const vectorField& pointNormals,
240  const triadField& pointCoordSys
241 )
242 {
243  Info<< "Calculating face curvature" << endl;
244 
245  const pointField& points = surf.points();
246  const labelList& meshPoints = surf.meshPoints();
247  const Map<label>& meshPointMap = surf.meshPointMap();
248 
249  triSurfacePointScalarField curvaturePointField
250  (
251  IOobject
252  (
253  name + ".curvature",
254  runTime.constant(),
255  "triSurface",
256  runTime,
259  ),
260  surf,
261  dimLength,
262  scalarField(points.size(), 0.0)
263  );
264 
265  List<symmTensor2D> pointFundamentalTensors
266  (
267  points.size(),
269  );
270 
271  scalarList accumulatedWeights(points.size(), 0.0);
272 
273  forAll(surf, fI)
274  {
275  const triFace& f = surf[fI];
276  const edgeList fEdges = f.edges();
277 
278  // Calculate the edge vectors and the normal differences
279  vectorField edgeVectors(f.size(), Zero);
280  vectorField normalDifferences(f.size(), Zero);
281 
282  forAll(fEdges, feI)
283  {
284  const edge& e = fEdges[feI];
285 
286  edgeVectors[feI] = e.vec(points);
287  normalDifferences[feI] =
288  pointNormals[meshPointMap[e[0]]]
289  - pointNormals[meshPointMap[e[1]]];
290  }
291 
292  // Set up a local coordinate system for the face
293  const vector& e0 = edgeVectors[0];
294  const vector eN = f.normal(points);
295  const vector e1 = (e0 ^ eN);
296 
297  if (magSqr(eN) < ROOTVSMALL)
298  {
299  continue;
300  }
301 
302  triad faceCoordSys(e0, e1, eN);
303  faceCoordSys.normalize();
304 
305  // Construct the matrix to solve
307  scalarDiagonalMatrix Z(3, 0);
308 
309  // Least Squares
310  for (label i = 0; i < 3; ++i)
311  {
312  scalar x = edgeVectors[i] & faceCoordSys[0];
313  scalar y = edgeVectors[i] & faceCoordSys[1];
314 
315  T(0, 0) += sqr(x);
316  T(1, 0) += x*y;
317  T(1, 1) += sqr(x) + sqr(y);
318  T(2, 1) += x*y;
319  T(2, 2) += sqr(y);
320 
321  scalar dndx = normalDifferences[i] & faceCoordSys[0];
322  scalar dndy = normalDifferences[i] & faceCoordSys[1];
323 
324  Z[0] += dndx*x;
325  Z[1] += dndx*y + dndy*x;
326  Z[2] += dndy*y;
327  }
328 
329  // Perform Cholesky decomposition and back substitution.
330  // Decomposed matrix is in T and solution is in Z.
331  LUsolve(T, Z);
332  symmTensor2D secondFundamentalTensor(Z[0], Z[1], Z[2]);
333 
334  // Loop over the face points adding the contribution of the face
335  // curvature to the points.
336  forAll(f, fpI)
337  {
338  const label patchPointIndex = meshPointMap[f[fpI]];
339 
340  const triad& ptCoordSys = pointCoordSys[patchPointIndex];
341 
342  if (!ptCoordSys.set())
343  {
344  continue;
345  }
346 
347  // Rotate faceCoordSys to ptCoordSys
348  tensor rotTensor = rotationTensor(ptCoordSys[2], faceCoordSys[2]);
349  triad rotatedFaceCoordSys = rotTensor & tensor(faceCoordSys);
350 
351  // Project the face curvature onto the point plane
352 
353  vector2D cmp1
354  (
355  ptCoordSys[0] & rotatedFaceCoordSys[0],
356  ptCoordSys[0] & rotatedFaceCoordSys[1]
357  );
358  vector2D cmp2
359  (
360  ptCoordSys[1] & rotatedFaceCoordSys[0],
361  ptCoordSys[1] & rotatedFaceCoordSys[1]
362  );
363 
364  tensor2D projTensor
365  (
366  cmp1,
367  cmp2
368  );
369 
370  symmTensor2D projectedFundamentalTensor
371  (
372  projTensor.x() & (secondFundamentalTensor & projTensor.x()),
373  projTensor.x() & (secondFundamentalTensor & projTensor.y()),
374  projTensor.y() & (secondFundamentalTensor & projTensor.y())
375  );
376 
377  // Calculate weight
378  // TODO: Voronoi area weighting
379  scalar weight = calcVertexNormalWeight
380  (
381  f,
382  meshPoints[patchPointIndex],
383  f.normal(points),
384  points
385  );
386 
387  // Sum contribution of face to this point
388  pointFundamentalTensors[patchPointIndex] +=
389  weight*projectedFundamentalTensor;
390 
391  accumulatedWeights[patchPointIndex] += weight;
392  }
393 
394  if (false)
395  {
396  Info<< "Points = " << points[f[0]] << " "
397  << points[f[1]] << " "
398  << points[f[2]] << endl;
399  Info<< "edgeVecs = " << edgeVectors[0] << " "
400  << edgeVectors[1] << " "
401  << edgeVectors[2] << endl;
402  Info<< "normDiff = " << normalDifferences[0] << " "
403  << normalDifferences[1] << " "
404  << normalDifferences[2] << endl;
405  Info<< "faceCoordSys = " << faceCoordSys << endl;
406  Info<< "T = " << T << endl;
407  Info<< "Z = " << Z << endl;
408  }
409  }
410 
411  forAll(curvaturePointField, pI)
412  {
413  pointFundamentalTensors[pI] /= (accumulatedWeights[pI] + SMALL);
414 
415  vector2D principalCurvatures = eigenValues(pointFundamentalTensors[pI]);
416 
417  //scalar curvature =
418  // (principalCurvatures[0] + principalCurvatures[1])/2;
419  scalar curvature = max
420  (
421  mag(principalCurvatures[0]),
422  mag(principalCurvatures[1])
423  );
424  //scalar curvature = principalCurvatures[0]*principalCurvatures[1];
425 
426  curvaturePointField[meshPoints[pI]] = curvature;
427  }
428 
429  return curvaturePointField;
430 }
431 
432 
433 bool edgesConnected(const edge& e1, const edge& e2)
434 {
435  if
436  (
437  e1.start() == e2.start()
438  || e1.start() == e2.end()
439  || e1.end() == e2.start()
440  || e1.end() == e2.end()
441  )
442  {
443  return true;
444  }
445 
446  return false;
447 }
448 
449 
450 scalar calcProximityOfFeaturePoints
451 (
452  const List<pointIndexHit>& hitList,
453  const scalar defaultCellSize
454 )
455 {
456  scalar minDist = defaultCellSize;
457 
458  for
459  (
460  label hI1 = 0;
461  hI1 < hitList.size() - 1;
462  ++hI1
463  )
464  {
465  const pointIndexHit& pHit1 = hitList[hI1];
466 
467  if (pHit1.hit())
468  {
469  for
470  (
471  label hI2 = hI1 + 1;
472  hI2 < hitList.size();
473  ++hI2
474  )
475  {
476  const pointIndexHit& pHit2 = hitList[hI2];
477 
478  if (pHit2.hit())
479  {
480  scalar curDist = mag(pHit1.hitPoint() - pHit2.hitPoint());
481 
482  minDist = min(curDist, minDist);
483  }
484  }
485  }
486  }
487 
488  return minDist;
489 }
490 
491 
492 scalar calcProximityOfFeatureEdges
493 (
494  const extendedFeatureEdgeMesh& efem,
495  const List<pointIndexHit>& hitList,
496  const scalar defaultCellSize
497 )
498 {
499  scalar minDist = defaultCellSize;
500 
501  for
502  (
503  label hI1 = 0;
504  hI1 < hitList.size() - 1;
505  ++hI1
506  )
507  {
508  const pointIndexHit& pHit1 = hitList[hI1];
509 
510  if (pHit1.hit())
511  {
512  const edge& e1 = efem.edges()[pHit1.index()];
513 
514  for
515  (
516  label hI2 = hI1 + 1;
517  hI2 < hitList.size();
518  ++hI2
519  )
520  {
521  const pointIndexHit& pHit2 = hitList[hI2];
522 
523  if (pHit2.hit())
524  {
525  const edge& e2 = efem.edges()[pHit2.index()];
526 
527  // Don't refine if the edges are connected to each other
528  if (!edgesConnected(e1, e2))
529  {
530  scalar curDist =
531  mag(pHit1.hitPoint() - pHit2.hitPoint());
532 
533  minDist = min(curDist, minDist);
534  }
535  }
536  }
537  }
538  }
539 
540  return minDist;
541 }
542 
543 
544 void dumpBox(const treeBoundBox& bb, const fileName& fName)
545 {
546  OFstream str(fName);
547 
548  Info<< "Dumping bounding box " << bb << " as lines to obj file "
549  << str.name() << endl;
550 
551 
552  pointField boxPoints(bb.points());
553 
554  forAll(boxPoints, i)
555  {
556  meshTools::writeOBJ(str, boxPoints[i]);
557  }
558 
560  {
561  const edge& e = treeBoundBox::edges[i];
562 
563  str<< "l " << e[0]+1 << ' ' << e[1]+1 << nl;
564  }
565 }
566 
567 
568 // Deletes all edges inside/outside bounding box from set.
569 void deleteBox
570 (
571  const triSurface& surf,
572  const treeBoundBox& bb,
573  const bool removeInside,
575 )
576 {
577  forAll(edgeStat, edgeI)
578  {
579  const point eMid = surf.edges()[edgeI].centre(surf.localPoints());
580 
581  if (removeInside ? bb.contains(eMid) : !bb.contains(eMid))
582  {
583  edgeStat[edgeI] = surfaceFeatures::NONE;
584  }
585  }
586 }
587 
588 
589 bool onLine(const point& p, const linePointRef& line)
590 {
591  const point& a = line.start();
592  const point& b = line.end();
593 
594  if
595  (
596  ( p.x() < min(a.x(), b.x()) || p.x() > max(a.x(), b.x()) )
597  || ( p.y() < min(a.y(), b.y()) || p.y() > max(a.y(), b.y()) )
598  || ( p.z() < min(a.z(), b.z()) || p.z() > max(a.z(), b.z()) )
599  )
600  {
601  return false;
602  }
603 
604  return true;
605 }
606 
607 
608 // Deletes all edges inside/outside bounding box from set.
609 void deleteEdges
610 (
611  const triSurface& surf,
612  const plane& cutPlane,
614 )
615 {
616  const pointField& points = surf.points();
617  const labelList& meshPoints = surf.meshPoints();
618 
619  forAll(edgeStat, edgeI)
620  {
621  const edge& e = surf.edges()[edgeI];
622  const point& p0 = points[meshPoints[e.start()]];
623  const point& p1 = points[meshPoints[e.end()]];
624  const linePointRef line(p0, p1);
625 
626  // If edge does not intersect the plane, delete.
627  scalar intersect = cutPlane.lineIntersect(line);
628 
629  point featPoint = intersect * (p1 - p0) + p0;
630 
631  if (!onLine(featPoint, line))
632  {
633  edgeStat[edgeI] = surfaceFeatures::NONE;
634  }
635  }
636 }
637 
638 
639 void drawHitProblem
640 (
641  label fI,
642  const triSurface& surf,
643  const pointField& start,
644  const pointField& faceCentres,
645  const pointField& end,
646  const List<pointIndexHit>& hitInfo
647 )
648 {
649  Info<< nl << "# findLineAll did not hit its own face."
650  << nl << "# fI " << fI
651  << nl << "# start " << start[fI]
652  << nl << "# f centre " << faceCentres[fI]
653  << nl << "# end " << end[fI]
654  << nl << "# hitInfo " << hitInfo
655  << endl;
656 
657  meshTools::writeOBJ(Info, start[fI]);
658  meshTools::writeOBJ(Info, faceCentres[fI]);
659  meshTools::writeOBJ(Info, end[fI]);
660 
661  Info<< "l 1 2 3" << endl;
662 
663  meshTools::writeOBJ(Info, surf.points()[surf[fI][0]]);
664  meshTools::writeOBJ(Info, surf.points()[surf[fI][1]]);
665  meshTools::writeOBJ(Info, surf.points()[surf[fI][2]]);
666 
667  Info<< "f 4 5 6" << endl;
668 
669  forAll(hitInfo, hI)
670  {
671  label hFI = hitInfo[hI].index();
672 
673  meshTools::writeOBJ(Info, surf.points()[surf[hFI][0]]);
674  meshTools::writeOBJ(Info, surf.points()[surf[hFI][1]]);
675  meshTools::writeOBJ(Info, surf.points()[surf[hFI][2]]);
676 
677  Info<< "f "
678  << 3*hI + 7 << " "
679  << 3*hI + 8 << " "
680  << 3*hI + 9
681  << endl;
682  }
683 }
684 
685 
686 // Unmark non-manifold edges if individual triangles are not features
687 void unmarkBaffles
688 (
689  const triSurface& surf,
690  const scalar includedAngle,
692 )
693 {
694  scalar minCos = Foam::cos(degToRad(180.0 - includedAngle));
695 
696  const labelListList& edgeFaces = surf.edgeFaces();
697 
698  forAll(edgeFaces, edgeI)
699  {
700  const labelList& eFaces = edgeFaces[edgeI];
701 
702  if (eFaces.size() > 2)
703  {
704  label i0 = eFaces[0];
705  //const labelledTri& f0 = surf[i0];
706  const Foam::vector& n0 = surf.faceNormals()[i0];
707 
708  //Pout<< "edge:" << edgeI << " n0:" << n0 << endl;
709 
710  bool same = true;
711 
712  for (label i = 1; i < eFaces.size(); i++)
713  {
714  //const labelledTri& f = surf[i];
715  const Foam::vector& n = surf.faceNormals()[eFaces[i]];
716 
717  //Pout<< " mag(n&n0): " << mag(n&n0) << endl;
718 
719  if (mag(n&n0) < minCos)
720  {
721  same = false;
722  break;
723  }
724  }
725 
726  if (same)
727  {
728  edgeStat[edgeI] = surfaceFeatures::NONE;
729  }
730  }
731  }
732 }
733 
734 
735 //- Divide into multiple normal bins
736 // - return REGION if != 2 normals
737 // - return REGION if 2 normals that make feature angle
738 // - otherwise return NONE and set normals,bins
739 surfaceFeatures::edgeStatus checkFlatRegionEdge
740 (
741  const triSurface& surf,
742  const scalar tol,
743  const scalar includedAngle,
744  const label edgeI
745 )
746 {
747  const edge& e = surf.edges()[edgeI];
748  const labelList& eFaces = surf.edgeFaces()[edgeI];
749 
750  // Bin according to normal
751 
752  DynamicList<Foam::vector> normals(2);
753  DynamicList<labelList> bins(2);
754 
755  forAll(eFaces, eFacei)
756  {
757  const Foam::vector& n = surf.faceNormals()[eFaces[eFacei]];
758 
759  // Find the normal in normals
760  label index = -1;
761  forAll(normals, normalI)
762  {
763  if (mag(n&normals[normalI]) > (1-tol))
764  {
765  index = normalI;
766  break;
767  }
768  }
769 
770  if (index != -1)
771  {
772  bins[index].append(eFacei);
773  }
774  else if (normals.size() >= 2)
775  {
776  // Would be third normal. Mark as feature.
777  //Pout<< "** at edge:" << surf.localPoints()[e[0]]
778  // << surf.localPoints()[e[1]]
779  // << " have normals:" << normals
780  // << " and " << n << endl;
782  }
783  else
784  {
785  normals.append(n);
786  bins.append(labelList(1, eFacei));
787  }
788  }
789 
790 
791  // Check resulting number of bins
792  if (bins.size() == 1)
793  {
794  // Note: should check here whether they are two sets of faces
795  // that are planar or indeed 4 faces al coming together at an edge.
796  //Pout<< "** at edge:"
797  // << surf.localPoints()[e[0]]
798  // << surf.localPoints()[e[1]]
799  // << " have single normal:" << normals[0]
800  // << endl;
801  return surfaceFeatures::NONE;
802  }
803  else
804  {
805  // Two bins. Check if normals make an angle
806 
807  //Pout<< "** at edge:"
808  // << surf.localPoints()[e[0]]
809  // << surf.localPoints()[e[1]] << nl
810  // << " normals:" << normals << nl
811  // << " bins :" << bins << nl
812  // << endl;
813 
814  if (includedAngle >= 0)
815  {
816  scalar minCos = Foam::cos(degToRad(180.0 - includedAngle));
817 
818  forAll(eFaces, i)
819  {
820  const Foam::vector& ni = surf.faceNormals()[eFaces[i]];
821  for (label j=i+1; j<eFaces.size(); j++)
822  {
823  const Foam::vector& nj = surf.faceNormals()[eFaces[j]];
824  if (mag(ni & nj) < minCos)
825  {
826  //Pout<< "have sharp feature between normal:" << ni
827  // << " and " << nj << endl;
828 
829  // Is feature. Keep as region or convert to
830  // feature angle? For now keep as region.
832  }
833  }
834  }
835  }
836 
837 
838  // So now we have two normals bins but need to make sure both
839  // bins have the same regions in it.
840 
841  // 1. store + or - region number depending
842  // on orientation of triangle in bins[0]
843  const labelList& bin0 = bins[0];
844  labelList regionAndNormal(bin0.size());
845  forAll(bin0, i)
846  {
847  const labelledTri& t = surf.localFaces()[eFaces[bin0[i]]];
848  int dir = t.edgeDirection(e);
849 
850  if (dir > 0)
851  {
852  regionAndNormal[i] = t.region()+1;
853  }
854  else if (dir == 0)
855  {
857  << exit(FatalError);
858  }
859  else
860  {
861  regionAndNormal[i] = -(t.region()+1);
862  }
863  }
864 
865  // 2. check against bin1
866  const labelList& bin1 = bins[1];
867  labelList regionAndNormal1(bin1.size());
868  forAll(bin1, i)
869  {
870  const labelledTri& t = surf.localFaces()[eFaces[bin1[i]]];
871  int dir = t.edgeDirection(e);
872 
873  label myRegionAndNormal;
874  if (dir > 0)
875  {
876  myRegionAndNormal = t.region()+1;
877  }
878  else
879  {
880  myRegionAndNormal = -(t.region()+1);
881  }
882 
883  regionAndNormal1[i] = myRegionAndNormal;
884 
885  label index = findIndex(regionAndNormal, -myRegionAndNormal);
886  if (index == -1)
887  {
888  // Not found.
889  //Pout<< "cannot find region " << myRegionAndNormal
890  // << " in regions " << regionAndNormal << endl;
891 
893  }
894  }
895 
896  // Passed all checks, two normal bins with the same contents.
897  //Pout<< "regionAndNormal:" << regionAndNormal << endl;
898  //Pout<< "myRegionAndNormal:" << regionAndNormal1 << endl;
899 
900  return surfaceFeatures::NONE;
901  }
902 }
903 
904 
905 void writeStats(const extendedFeatureEdgeMesh& fem, Ostream& os)
906 {
907  os << " points : " << fem.points().size() << nl
908  << " of which" << nl
909  << " convex : "
910  << fem.concaveStart() << nl
911  << " concave : "
912  << (fem.mixedStart()-fem.concaveStart()) << nl
913  << " mixed : "
914  << (fem.nonFeatureStart()-fem.mixedStart()) << nl
915  << " non-feature : "
916  << (fem.points().size()-fem.nonFeatureStart()) << nl
917  << " edges : " << fem.edges().size() << nl
918  << " of which" << nl
919  << " external edges : "
920  << fem.internalStart() << nl
921  << " internal edges : "
922  << (fem.flatStart()- fem.internalStart()) << nl
923  << " flat edges : "
924  << (fem.openStart()- fem.flatStart()) << nl
925  << " open edges : "
926  << (fem.multipleStart()- fem.openStart()) << nl
927  << " multiply connected : "
928  << (fem.edges().size()- fem.multipleStart()) << endl;
929 }
930 
931 
932 
933 int main(int argc, char *argv[])
934 {
936  (
937  "extract and write surface features to file"
938  );
940 
941  #include "addDictOption.H"
942 
943  #include "setRootCase.H"
944  #include "createTime.H"
945 
946  const word dictName("surfaceFeatureExtractDict");
948 
949  Info<< "Reading " << dictName << nl << endl;
950 
951  const IOdictionary dict(dictIO);
952 
954  {
955  if (!iter().isDict())
956  {
957  continue;
958  }
959 
960  const dictionary& surfaceDict = iter().dict();
961 
962  if (!surfaceDict.found("extractionMethod"))
963  {
964  continue;
965  }
966 
967  const word extractionMethod = surfaceDict.lookup("extractionMethod");
968 
969  const fileName surfFileName = iter().keyword();
970  const fileName sFeatFileName = surfFileName.lessExt().name();
971 
972  Info<< "Surface : " << surfFileName << nl << endl;
973 
974  const Switch writeVTK =
975  surfaceDict.lookupOrDefault<Switch>("writeVTK", "off");
976  const Switch writeObj =
977  surfaceDict.lookupOrDefault<Switch>("writeObj", "off");
978 
979  const Switch curvature =
980  surfaceDict.lookupOrDefault<Switch>("curvature", "off");
981  const Switch featureProximity =
982  surfaceDict.lookupOrDefault<Switch>("featureProximity", "off");
983  const Switch closeness =
984  surfaceDict.lookupOrDefault<Switch>("closeness", "off");
985 
986 
987  Info<< nl << "Feature line extraction is only valid on closed manifold "
988  << "surfaces." << endl;
989 
990  // Read
991  // ~~~~
992 
993  triSurface surf(runTime.constantPath()/"triSurface"/surfFileName);
994 
995  Info<< "Statistics:" << endl;
996  surf.writeStats(Info);
997  Info<< endl;
998 
999  faceList faces(surf.size());
1000 
1001  forAll(surf, fI)
1002  {
1003  faces[fI] = surf[fI].triFaceFace();
1004  }
1005 
1006 
1007  // Either construct features from surface & featureAngle or read set.
1008  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1009 
1011 
1012  scalar includedAngle = 0.0;
1013 
1014  if (extractionMethod == "extractFromFile")
1015  {
1016  const dictionary& extractFromFileDict =
1017  surfaceDict.subDict("extractFromFileCoeffs");
1018 
1019  const fileName featureEdgeFile =
1020  extractFromFileDict.lookup("featureEdgeFile");
1021 
1022  const Switch geometricTestOnly =
1023  extractFromFileDict.lookupOrDefault<Switch>
1024  (
1025  "geometricTestOnly",
1026  "no"
1027  );
1028 
1029  edgeMesh eMesh(featureEdgeFile);
1030 
1031  // Sometimes duplicate edges are present. Remove them.
1032  eMesh.mergeEdges();
1033 
1034  Info<< nl << "Reading existing feature edges from file "
1035  << featureEdgeFile << nl
1036  << "Selecting edges purely based on geometric tests: "
1037  << geometricTestOnly.asText() << endl;
1038 
1039  set.set
1040  (
1041  new surfaceFeatures
1042  (
1043  surf,
1044  eMesh.points(),
1045  eMesh.edges(),
1046  1e-6,
1047  geometricTestOnly
1048  )
1049  );
1050  }
1051  else if (extractionMethod == "extractFromSurface")
1052  {
1053  const dictionary& extractFromSurfaceDict =
1054  surfaceDict.subDict("extractFromSurfaceCoeffs");
1055 
1056  includedAngle =
1057  readScalar(extractFromSurfaceDict.lookup("includedAngle"));
1058 
1059  const Switch geometricTestOnly =
1060  extractFromSurfaceDict.lookupOrDefault<Switch>
1061  (
1062  "geometricTestOnly",
1063  "no"
1064  );
1065 
1066  Info<< nl
1067  << "Constructing feature set from included angle "
1068  << includedAngle << nl
1069  << "Selecting edges purely based on geometric tests: "
1070  << geometricTestOnly.asText() << endl;
1071 
1072  set.set
1073  (
1074  new surfaceFeatures
1075  (
1076  surf,
1077  includedAngle,
1078  0,
1079  0,
1080  geometricTestOnly
1081  )
1082  );
1083  }
1084  else
1085  {
1087  << "No initial feature set. Provide either one"
1088  << " of extractFromFile (to read existing set)" << nl
1089  << " or extractFromSurface (to construct new set from angle)"
1090  << exit(FatalError);
1091  }
1092 
1093 
1094  // Trim set
1095  // ~~~~~~~~
1096 
1097  if (surfaceDict.isDict("trimFeatures"))
1098  {
1099  dictionary trimDict = surfaceDict.subDict("trimFeatures");
1100 
1101  scalar minLen =
1102  trimDict.lookupOrAddDefault<scalar>("minLen", -GREAT);
1103 
1104  label minElem = trimDict.lookupOrAddDefault<label>("minElem", 0);
1105 
1106  // Trim away small groups of features
1107  if (minElem > 0 || minLen > 0)
1108  {
1109  Info<< "Removing features of length < "
1110  << minLen << endl;
1111  Info<< "Removing features with number of edges < "
1112  << minElem << endl;
1113 
1114  set().trimFeatures(minLen, minElem, includedAngle);
1115  }
1116  }
1117 
1118 
1119  // Subset
1120  // ~~~~~~
1121 
1122  // Convert to marked edges, points
1123  List<surfaceFeatures::edgeStatus> edgeStat(set().toStatus());
1124 
1125  if (surfaceDict.isDict("subsetFeatures"))
1126  {
1127  const dictionary& subsetDict = surfaceDict.subDict
1128  (
1129  "subsetFeatures"
1130  );
1131 
1132  if (subsetDict.found("insideBox"))
1133  {
1134  treeBoundBox bb(subsetDict.lookup("insideBox")());
1135 
1136  Info<< "Removing all edges outside bb " << bb << endl;
1137  dumpBox(bb, "subsetBox.obj");
1138 
1139  deleteBox(surf, bb, false, edgeStat);
1140  }
1141  else if (subsetDict.found("outsideBox"))
1142  {
1143  treeBoundBox bb(subsetDict.lookup("outsideBox")());
1144 
1145  Info<< "Removing all edges inside bb " << bb << endl;
1146  dumpBox(bb, "deleteBox.obj");
1147 
1148  deleteBox(surf, bb, true, edgeStat);
1149  }
1150 
1151  const Switch nonManifoldEdges =
1152  subsetDict.lookupOrDefault<Switch>("nonManifoldEdges", "yes");
1153 
1154  if (!nonManifoldEdges)
1155  {
1156  Info<< "Removing all non-manifold edges"
1157  << " (edges with > 2 connected faces) unless they"
1158  << " cross multiple regions" << endl;
1159 
1160  forAll(edgeStat, edgeI)
1161  {
1162  const labelList& eFaces = surf.edgeFaces()[edgeI];
1163 
1164  if
1165  (
1166  eFaces.size() > 2
1167  && edgeStat[edgeI] == surfaceFeatures::REGION
1168  && (eFaces.size() % 2) == 0
1169  )
1170  {
1171  edgeStat[edgeI] = checkFlatRegionEdge
1172  (
1173  surf,
1174  1e-5, //tol,
1175  includedAngle,
1176  edgeI
1177  );
1178  }
1179  }
1180  }
1181 
1182  const Switch openEdges =
1183  subsetDict.lookupOrDefault<Switch>("openEdges", "yes");
1184 
1185  if (!openEdges)
1186  {
1187  Info<< "Removing all open edges"
1188  << " (edges with 1 connected face)" << endl;
1189 
1190  forAll(edgeStat, edgeI)
1191  {
1192  if (surf.edgeFaces()[edgeI].size() == 1)
1193  {
1194  edgeStat[edgeI] = surfaceFeatures::NONE;
1195  }
1196  }
1197  }
1198 
1199  if (subsetDict.found("plane"))
1200  {
1201  plane cutPlane(subsetDict.lookup("plane")());
1202 
1203  deleteEdges(surf, cutPlane, edgeStat);
1204 
1205  Info<< "Only edges that intersect the plane with normal "
1206  << cutPlane.normal()
1207  << " and base point " << cutPlane.refPoint()
1208  << " will be included as feature edges."<< endl;
1209  }
1210  }
1211 
1212 
1213  surfaceFeatures newSet(surf);
1214  newSet.setFromStatus(edgeStat, includedAngle);
1215 
1216  Info<< nl
1217  << "Initial feature set:" << nl
1218  << " feature points : " << newSet.featurePoints().size() << nl
1219  << " feature edges : " << newSet.featureEdges().size() << nl
1220  << " of which" << nl
1221  << " region edges : " << newSet.nRegionEdges() << nl
1222  << " external edges : " << newSet.nExternalEdges() << nl
1223  << " internal edges : " << newSet.nInternalEdges() << nl
1224  << endl;
1225 
1226  //if (writeObj)
1227  //{
1228  // newSet.writeObj("final");
1229  //}
1230 
1231  boolList surfBaffleRegions(surf.patches().size(), false);
1232 
1233  wordList surfBaffleNames;
1234  surfaceDict.readIfPresent("baffles", surfBaffleNames);
1235 
1236  forAll(surf.patches(), pI)
1237  {
1238  const word& name = surf.patches()[pI].name();
1239 
1240  if (findIndex(surfBaffleNames, name) != -1)
1241  {
1242  Info<< "Adding baffle region " << name << endl;
1243  surfBaffleRegions[pI] = true;
1244  }
1245  }
1246 
1247  // Extracting and writing a extendedFeatureEdgeMesh
1249  (
1250  newSet,
1251  runTime,
1252  sFeatFileName + ".extendedFeatureEdgeMesh",
1253  surfBaffleRegions
1254  );
1255 
1256 
1257  if (surfaceDict.isDict("addFeatures"))
1258  {
1259  const word addFeName = surfaceDict.subDict("addFeatures")["name"];
1260  Info<< "Adding (without merging) features from " << addFeName
1261  << nl << endl;
1262 
1263  extendedFeatureEdgeMesh addFeMesh
1264  (
1265  IOobject
1266  (
1267  addFeName,
1268  runTime.time().constant(),
1269  "extendedFeatureEdgeMesh",
1270  runTime.time(),
1273  )
1274  );
1275  Info<< "Read " << addFeMesh.name() << nl;
1276  writeStats(addFeMesh, Info);
1277 
1278  feMesh.add(addFeMesh);
1279  }
1280 
1281 
1282  Info<< nl
1283  << "Final feature set:" << nl;
1284  writeStats(feMesh, Info);
1285 
1286  Info<< nl << "Writing extendedFeatureEdgeMesh to "
1287  << feMesh.objectPath() << endl;
1288 
1289  mkDir(feMesh.path());
1290 
1291  if (writeObj)
1292  {
1293  feMesh.writeObj(feMesh.path()/surfFileName.lessExt().name());
1294  }
1295 
1296  feMesh.write();
1297 
1298  // Write a featureEdgeMesh for backwards compatibility
1299  featureEdgeMesh bfeMesh
1300  (
1301  IOobject
1302  (
1303  surfFileName.lessExt().name() + ".eMesh", // name
1304  runTime.constant(), // instance
1305  "triSurface",
1306  runTime, // registry
1309  false
1310  ),
1311  feMesh.points(),
1312  feMesh.edges()
1313  );
1314 
1315  Info<< nl << "Writing featureEdgeMesh to "
1316  << bfeMesh.objectPath() << endl;
1317 
1318  bfeMesh.regIOobject::write();
1319 
1320  // Find close features
1321 
1322  // // Dummy trim operation to mark features
1323  // labelList featureEdgeIndexing = newSet.trimFeatures(-GREAT, 0);
1324 
1325  // scalarField surfacePtFeatureIndex(surf.points().size(), -1);
1326 
1327  // forAll(newSet.featureEdges(), eI)
1328  // {
1329  // const edge& e = surf.edges()[newSet.featureEdges()[eI]];
1330 
1331  // surfacePtFeatureIndex[surf.meshPoints()[e.start()]] =
1332  // featureEdgeIndexing[newSet.featureEdges()[eI]];
1333 
1334  // surfacePtFeatureIndex[surf.meshPoints()[e.end()]] =
1335  // featureEdgeIndexing[newSet.featureEdges()[eI]];
1336  // }
1337 
1338  // if (writeVTK)
1339  // {
1340  // vtkSurfaceWriter().write
1341  // (
1342  // runTime.constant()/"triSurface", // outputDir
1343  // sFeatFileName, // surfaceName
1344  // surf.points(),
1345  // faces,
1346  // "surfacePtFeatureIndex", // fieldName
1347  // surfacePtFeatureIndex,
1348  // true, // isNodeValues
1349  // true // verbose
1350  // );
1351  // }
1352 
1353  // Random rndGen(343267);
1354 
1355  // treeBoundBox surfBB
1356  // (
1357  // treeBoundBox(searchSurf.bounds()).extend(rndGen, 1e-4)
1358  // );
1359 
1360  // surfBB.min() -= Foam::point(ROOTVSMALL, ROOTVSMALL, ROOTVSMALL);
1361  // surfBB.max() += Foam::point(ROOTVSMALL, ROOTVSMALL, ROOTVSMALL);
1362 
1363  // indexedOctree<treeDataEdge> ftEdTree
1364  // (
1365  // treeDataEdge
1366  // (
1367  // false,
1368  // surf.edges(),
1369  // surf.localPoints(),
1370  // newSet.featureEdges()
1371  // ),
1372  // surfBB,
1373  // 8, // maxLevel
1374  // 10, // leafsize
1375  // 3.0 // duplicity
1376  // );
1377 
1378  // labelList nearPoints = ftEdTree.findBox
1379  // (
1380  // treeBoundBox
1381  // (
1382  // sPt - featureSearchSpan*Foam::vector::one,
1383  // sPt + featureSearchSpan*Foam::vector::one
1384  // )
1385  // );
1386 
1387  if (closeness)
1388  {
1389  Info<< nl << "Extracting internal and external closeness of "
1390  << "surface." << endl;
1391 
1392 
1393  triSurfaceMesh searchSurf
1394  (
1395  IOobject
1396  (
1397  sFeatFileName + ".closeness",
1398  runTime.constant(),
1399  "triSurface",
1400  runTime,
1403  ),
1404  surf
1405  );
1406 
1407 
1408  // Internal and external closeness
1409 
1410  // Prepare start and end points for intersection tests
1411 
1412  const vectorField& normals = searchSurf.faceNormals();
1413 
1414  scalar span = searchSurf.bounds().mag();
1415 
1416  scalar externalAngleTolerance = 10;
1417  scalar externalToleranceCosAngle =
1418  Foam::cos
1419  (
1420  degToRad(180 - externalAngleTolerance)
1421  );
1422 
1423  scalar internalAngleTolerance = 45;
1424  scalar internalToleranceCosAngle =
1425  Foam::cos
1426  (
1427  degToRad(180 - internalAngleTolerance)
1428  );
1429 
1430  Info<< "externalToleranceCosAngle: " << externalToleranceCosAngle
1431  << nl
1432  << "internalToleranceCosAngle: " << internalToleranceCosAngle
1433  << endl;
1434 
1435  // Info<< "span " << span << endl;
1436 
1437  pointField start(searchSurf.faceCentres() - span*normals);
1438  pointField end(searchSurf.faceCentres() + span*normals);
1439  const pointField& faceCentres = searchSurf.faceCentres();
1440 
1441  List<List<pointIndexHit>> allHitInfo;
1442 
1443  // Find all intersections (in order)
1444  searchSurf.findLineAll(start, end, allHitInfo);
1445 
1446  scalarField internalCloseness(start.size(), GREAT);
1447  scalarField externalCloseness(start.size(), GREAT);
1448 
1449  forAll(allHitInfo, fI)
1450  {
1451  const List<pointIndexHit>& hitInfo = allHitInfo[fI];
1452 
1453  if (hitInfo.size() < 1)
1454  {
1455  drawHitProblem(fI, surf, start, faceCentres, end, hitInfo);
1456 
1457  // FatalErrorIn(args.executable())
1458  // << "findLineAll did not hit its own face."
1459  // << exit(FatalError);
1460  }
1461  else if (hitInfo.size() == 1)
1462  {
1463  if (!hitInfo[0].hit())
1464  {
1465  // FatalErrorIn(args.executable())
1466  // << "findLineAll did not hit any face."
1467  // << exit(FatalError);
1468  }
1469  else if (hitInfo[0].index() != fI)
1470  {
1471  drawHitProblem
1472  (
1473  fI,
1474  surf,
1475  start,
1476  faceCentres,
1477  end,
1478  hitInfo
1479  );
1480 
1481  // FatalErrorIn(args.executable())
1482  // << "findLineAll did not hit its own face."
1483  // << exit(FatalError);
1484  }
1485  }
1486  else
1487  {
1488  label ownHitI = -1;
1489 
1490  forAll(hitInfo, hI)
1491  {
1492  // Find the hit on the triangle that launched the ray
1493 
1494  if (hitInfo[hI].index() == fI)
1495  {
1496  ownHitI = hI;
1497 
1498  break;
1499  }
1500  }
1501 
1502  if (ownHitI < 0)
1503  {
1504  drawHitProblem
1505  (
1506  fI,
1507  surf,
1508  start,
1509  faceCentres,
1510  end,
1511  hitInfo
1512  );
1513 
1514  // FatalErrorIn(args.executable())
1515  // << "findLineAll did not hit its own face."
1516  // << exit(FatalError);
1517  }
1518  else if (ownHitI == 0)
1519  {
1520  // There are no internal hits, the first hit is the
1521  // closest external hit
1522 
1523  if
1524  (
1525  (
1526  normals[fI]
1527  & normals[hitInfo[ownHitI + 1].index()]
1528  )
1529  < externalToleranceCosAngle
1530  )
1531  {
1532  externalCloseness[fI] =
1533  mag
1534  (
1535  faceCentres[fI]
1536  - hitInfo[ownHitI + 1].hitPoint()
1537  );
1538  }
1539  }
1540  else if (ownHitI == hitInfo.size() - 1)
1541  {
1542  // There are no external hits, the last but one hit is
1543  // the closest internal hit
1544 
1545  if
1546  (
1547  (
1548  normals[fI]
1549  & normals[hitInfo[ownHitI - 1].index()]
1550  )
1551  < internalToleranceCosAngle
1552  )
1553  {
1554  internalCloseness[fI] =
1555  mag
1556  (
1557  faceCentres[fI]
1558  - hitInfo[ownHitI - 1].hitPoint()
1559  );
1560  }
1561  }
1562  else
1563  {
1564  if
1565  (
1566  (
1567  normals[fI]
1568  & normals[hitInfo[ownHitI + 1].index()]
1569  )
1570  < externalToleranceCosAngle
1571  )
1572  {
1573  externalCloseness[fI] =
1574  mag
1575  (
1576  faceCentres[fI]
1577  - hitInfo[ownHitI + 1].hitPoint()
1578  );
1579  }
1580 
1581  if
1582  (
1583  (
1584  normals[fI]
1585  & normals[hitInfo[ownHitI - 1].index()]
1586  )
1587  < internalToleranceCosAngle
1588  )
1589  {
1590  internalCloseness[fI] =
1591  mag
1592  (
1593  faceCentres[fI]
1594  - hitInfo[ownHitI - 1].hitPoint()
1595  );
1596  }
1597  }
1598  }
1599  }
1600 
1601  triSurfaceScalarField internalClosenessField
1602  (
1603  IOobject
1604  (
1605  sFeatFileName + ".internalCloseness",
1606  runTime.constant(),
1607  "triSurface",
1608  runTime,
1611  ),
1612  surf,
1613  dimLength,
1614  internalCloseness
1615  );
1616 
1617  internalClosenessField.write();
1618 
1619  triSurfaceScalarField externalClosenessField
1620  (
1621  IOobject
1622  (
1623  sFeatFileName + ".externalCloseness",
1624  runTime.constant(),
1625  "triSurface",
1626  runTime,
1629  ),
1630  surf,
1631  dimLength,
1632  externalCloseness
1633  );
1634 
1635  externalClosenessField.write();
1636 
1637  if (writeVTK)
1638  {
1640  (
1641  runTime.constantPath()/"triSurface",// outputDir
1642  sFeatFileName, // surfaceName
1643  surf.points(),
1644  faces,
1645  "internalCloseness", // fieldName
1646  internalCloseness,
1647  false, // isNodeValues
1648  true // verbose
1649  );
1650 
1652  (
1653  runTime.constantPath()/"triSurface",// outputDir
1654  sFeatFileName, // surfaceName
1655  surf.points(),
1656  faces,
1657  "externalCloseness", // fieldName
1658  externalCloseness,
1659  false, // isNodeValues
1660  true // verbose
1661  );
1662  }
1663  }
1664 
1665 
1666  if (curvature)
1667  {
1668  Info<< nl << "Extracting curvature of surface at the points."
1669  << endl;
1670 
1671  vectorField pointNormals = calcVertexNormals(surf);
1672  triadField pointCoordSys = calcVertexCoordSys(surf, pointNormals);
1673 
1674  triSurfacePointScalarField k = calcCurvature
1675  (
1676  sFeatFileName,
1677  runTime,
1678  surf,
1679  pointNormals,
1680  pointCoordSys
1681  );
1682 
1683  k.write();
1684 
1685  if (writeVTK)
1686  {
1688  (
1689  runTime.constantPath()/"triSurface",// outputDir
1690  sFeatFileName, // surfaceName
1691  surf.points(),
1692  faces,
1693  "curvature", // fieldName
1694  k,
1695  true, // isNodeValues
1696  true // verbose
1697  );
1698  }
1699  }
1700 
1701 
1702  if (featureProximity)
1703  {
1704  Info<< nl << "Extracting proximity of close feature points and "
1705  << "edges to the surface" << endl;
1706 
1707  const scalar searchDistance =
1708  readScalar(surfaceDict.lookup("maxFeatureProximity"));
1709 
1710  scalarField featureProximity(surf.size(), searchDistance);
1711 
1712  forAll(surf, fI)
1713  {
1714  const triPointRef& tri = surf[fI].tri(surf.points());
1715  const point& triCentre = tri.circumCentre();
1716 
1717  const scalar radiusSqr = min
1718  (
1719  sqr(4*tri.circumRadius()),
1720  sqr(searchDistance)
1721  );
1722 
1723  List<pointIndexHit> hitList;
1724 
1725  feMesh.allNearestFeatureEdges(triCentre, radiusSqr, hitList);
1726 
1727  featureProximity[fI] =
1728  calcProximityOfFeatureEdges
1729  (
1730  feMesh,
1731  hitList,
1732  featureProximity[fI]
1733  );
1734 
1735  feMesh.allNearestFeaturePoints(triCentre, radiusSqr, hitList);
1736 
1737  featureProximity[fI] =
1738  calcProximityOfFeaturePoints
1739  (
1740  hitList,
1741  featureProximity[fI]
1742  );
1743  }
1744 
1745  triSurfaceScalarField featureProximityField
1746  (
1747  IOobject
1748  (
1749  sFeatFileName + ".featureProximity",
1750  runTime.constant(),
1751  "triSurface",
1752  runTime,
1755  ),
1756  surf,
1757  dimLength,
1758  featureProximity
1759  );
1760 
1761  featureProximityField.write();
1762 
1763  if (writeVTK)
1764  {
1766  (
1767  runTime.constantPath()/"triSurface",// outputDir
1768  sFeatFileName, // surfaceName
1769  surf.points(),
1770  faces,
1771  "featureProximity", // fieldName
1772  featureProximity,
1773  false, // isNodeValues
1774  true // verbose
1775  );
1776  }
1777  }
1778 
1779  Info<< endl;
1780  }
1781 
1782  Info<< "ExecutionTime = " << runTime.elapsedCpuTime() << " s"
1783  << " ClockTime = " << runTime.elapsedClockTime() << " s"
1784  << nl << endl;
1785 
1786  Info<< "End\n" << endl;
1787 
1788  return 0;
1789 }
1790 
1791 
1792 // ************************************************************************* //
label concaveStart() const
Return the index of the start of the concave feature points.
label end() const
Return end vertex label.
Definition: edgeI.H:92
const Time & time() const
Return time.
label nPoints() const
Return number of points supporting patch faces.
const Cmpt & z() const
Definition: VectorI.H:87
dictionary dict
label nonFeatureStart() const
Return the index of the start of the non-feature points.
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:428
A triangle primitive used to calculate face normals and swept volumes.
Definition: triangle.H:57
intWM_LABEL_SIZE_t label
A label is an int32_t or int64_t as specified by the pre-processor macro WM_LABEL_SIZE.
Definition: label.H:59
A line primitive.
Definition: line.H:56
A class for handling file names.
Definition: fileName.H:69
label region() const
Return region label.
Definition: labelledTriI.H:68
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
const Cmpt & x() const
Definition: VectorI.H:75
const labelList & meshPoints() const
Return labelList of mesh points in patch. They are constructed.
A 1D vector of objects of type <T> with a fixed size <Size>.
Definition: FixedList.H:53
error FatalError
A list of keyword definitions, which are a keyword followed by any number of values (e...
Definition: dictionary.H:137
dimensioned< Type > max(const dimensioned< Type > &, const dimensioned< Type > &)
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:319
T lookupOrAddDefault(const word &, const T &, bool recursive=false, bool patternMatch=true)
Find and return a T, if not found return the given.
const point & refPoint() const
Return or return plane base point.
Definition: plane.C:246
Fields for triSurface.
bool hit() const
Is there a hit.
dimensionedSymmTensor sqr(const dimensionedVector &dv)
Unit conversion functions.
void writeStats(Ostream &) const
Write some statistics.
Definition: triSurface.C:1067
const List< Face > & localFaces() const
Return patch faces addressing into local point list.
Output to file stream.
Definition: OFstream.H:81
void size(const label)
Override size to be inconsistent with allocated storage.
Definition: ListI.H:76
const dictionary & subDict(const word &) const
Find and return a sub-dictionary.
Definition: dictionary.C:633
bool isDict(const word &) const
Check if entry is a sub-dictionary.
Definition: dictionary.C:602
dimensionedVector eigenValues(const dimensionedTensor &dt)
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:253
fileName constantPath() const
Return constant path.
Definition: TimePaths.H:146
const Field< PointType > & points() const
Return reference to global points.
A simple wrapper around bool so that it can be read as a word: true/false, on/off, yes/no, y/n, t/f, or none.
Definition: Switch.H:60
const Map< label > & meshPointMap() const
Mesh point map. Given the global point index find its.
static void noParallel()
Remove the parallel options.
Definition: argList.C:146
This class describes the interaction of (usually) a face and a point. It carries the info of a succes...
Definition: PointIndexHit.H:53
PointRef end() const
Return second vertex.
Definition: lineI.H:66
void writeOBJ(Ostream &os, const point &pt)
Write obj representation of point.
Definition: meshTools.C:203
label k
Boltzmann constant.
Geometric class that creates a 2D plane and can return the intersection point between a line and the ...
Definition: plane.H:60
tensor rotationTensor(const vector &n1, const vector &n2)
Rotational transformation tensor from vector n1 to n2.
Definition: transform.H:47
Class to control time during OpenFOAM simulations that is also the top-level objectRegistry.
Definition: Time.H:68
Templated 2D tensor derived from VectorSpace adding construction from 4 components, element access using xx(), xy(), yx() and yy() member functions and the iner-product (dot-product) and outer-product of two Vector2Ds (tensor-product) operators.
Definition: Tensor2D.H:56
const vector & normal() const
Return plane normal.
Definition: plane.C:240
scalar degToRad(const scalar deg)
Conversion from degrees to radians.
virtual void write(const fileName &outputDir, const fileName &surfaceName, const pointField &points, const faceList &faces, const bool verbose=false) const
Write single surface geometry to file.
IOdictionary is derived from dictionary and IOobject to give the dictionary automatic IO functionalit...
Definition: IOdictionary.H:53
vector normal(const pointField &) const
Vector normal; magnitude is equal to area of face.
Definition: triFaceI.H:181
IOoject and searching on triSurface.
const pointField & points() const
Return points.
Definition: edgeMeshI.H:39
scalar y
word name() const
Return file name (part beyond last /)
Definition: fileName.C:179
dimensionedScalar cos(const dimensionedScalar &ds)
const pointField & points
A 1D vector of objects of type <T> that resizes itself as necessary to accept the new objects...
Definition: DynamicList.H:56
static const triad unset
Definition: triad.H:99
An edge is a list of two point labels. The functionality it provides supports the discretisation on a...
Definition: edge.H:58
label multipleStart() const
Return the index of the start of the multiply-connected feature.
A class for handling words, derived from string.
Definition: word.H:59
const geometricSurfacePatchList & patches() const
Definition: triSurface.H:314
bool readIfPresent(const word &, T &, bool recursive=false, bool patternMatch=true) const
Find an entry if present, and assign to T.
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
A triangular face using a FixedList of labels corresponding to mesh vertices.
Definition: triFace.H:68
3D tensor transformation operations.
void append(const T &)
Append an element at the end of the list.
Definition: ListI.H:97
edgeList edges() const
Return edges in face point ordering,.
Definition: triFaceI.H:318
const edgeList & edges() const
Return list of edges, address into LOCAL point list.
bool set(const direction d) const
Is the vector in the direction d set.
Definition: triadI.H:61
Info<< "Finished reading KIVA file"<< endl;cellShapeList cellShapes(nPoints);labelList cellZoning(nPoints,-1);const cellModel &hex=*(cellModeller::lookup("hex"));labelList hexLabels(8);label activeCells=0;labelList pointMap(nPoints);forAll(pointMap, i){pointMap[i]=i;}for(label i=0;i< nPoints;i++){if(f[i] > 0.0){hexLabels[0]=i;hexLabels[1]=i1tab[i];hexLabels[2]=i3tab[i1tab[i]];hexLabels[3]=i3tab[i];hexLabels[4]=i8tab[i];hexLabels[5]=i1tab[i8tab[i]];hexLabels[6]=i3tab[i1tab[i8tab[i]]];hexLabels[7]=i3tab[i8tab[i]];cellShapes[activeCells]=cellShape(hex, hexLabels);edgeList edges=cellShapes[activeCells].edges();forAll(edges, ei){if(edges[ei].mag(points)< SMALL){label start=pointMap[edges[ei].start()];while(start!=pointMap[start]){start=pointMap[start];}label end=pointMap[edges[ei].end()];while(end!=pointMap[end]){end=pointMap[end];}label minLabel=min(start, end);pointMap[start]=pointMap[end]=minLabel;}}cellZoning[activeCells]=idreg[i];activeCells++;}}cellShapes.setSize(activeCells);cellZoning.setSize(activeCells);forAll(cellShapes, celli){cellShape &cs=cellShapes[celli];forAll(cs, i){cs[i]=pointMap[cs[i]];}cs.collapse();}label bcIDs[11]={-1, 0, 2, 4,-1, 5,-1, 6, 7, 8, 9};const label nBCs=12;const word *kivaPatchTypes[nBCs]={&wallPolyPatch::typeName,&wallPolyPatch::typeName,&wallPolyPatch::typeName,&wallPolyPatch::typeName,&symmetryPolyPatch::typeName,&wedgePolyPatch::typeName,&polyPatch::typeName,&polyPatch::typeName,&polyPatch::typeName,&polyPatch::typeName,&symmetryPolyPatch::typeName,&oldCyclicPolyPatch::typeName};enum patchTypeNames{PISTON, VALVE, LINER, CYLINDERHEAD, AXIS, WEDGE, INFLOW, OUTFLOW, PRESIN, PRESOUT, SYMMETRYPLANE, CYCLIC};const char *kivaPatchNames[nBCs]={"piston","valve","liner","cylinderHead","axis","wedge","inflow","outflow","presin","presout","symmetryPlane","cyclic"};List< SLList< face > > pFaces[nBCs]
Definition: readKivaGrid.H:235
const Cmpt & y() const
Definition: VectorI.H:81
static const edgeList edges
Edge to point addressing.
Definition: treeBoundBox.H:169
const word & constant() const
Return constant name.
Definition: TimePaths.H:124
bool found(const word &, bool recursive=false, bool patternMatch=true) const
Search dictionary for given keyword.
Definition: dictionary.C:306
List< label > labelList
A List of labels.
Definition: labelList.H:56
static const zero Zero
Definition: zero.H:91
Triangle with additional region number.
Definition: labelledTri.H:57
scalar lineIntersect(const line< Point, PointRef > &l) const
Return the cutting point between the plane and.
Definition: plane.H:180
bool readScalar(const char *buf, doubleScalar &s)
Read whole of buf as a scalar. Return true if succesful.
Definition: doubleScalar.H:63
label fcIndex(const label i) const
Return the forward circular index, i.e. the next index.
Definition: FixedListI.H:115
forAllConstIter(PtrDictionary< phaseModel >, mixture.phases(), phase)
Definition: pEqn.H:29
errorManip< error > abort(error &err)
Definition: errorManip.H:131
label size() const
Return the number of elements in the FixedList.
Definition: FixedListI.H:402
dimensioned< scalar > magSqr(const dimensioned< Type > &)
An Ostream is an abstract base class for all output systems (streams, files, token lists...
Definition: Ostream.H:53
label start() const
Return start vertex label.
Definition: edgeI.H:81
static const char nl
Definition: Ostream.H:262
PointRef start() const
Return first vertex.
Definition: lineI.H:60
Points connected by edges.
Definition: edgeMesh.H:69
label openStart() const
Return the index of the start of the open feature edges.
fileName lessExt() const
Return file name without extension (part before last .)
Definition: fileName.C:268
label findIndex(const ListType &, typename ListType::const_reference, const label start=0)
Find first occurence of given element and return index,.
void T(FieldField< Field, Type > &f1, const FieldField< Field, Type > &f2)
dimensioned< Type > min(const dimensioned< Type > &, const dimensioned< Type > &)
bool mkDir(const fileName &, mode_t=0777)
Make a directory and return an error if it could not be created.
Definition: POSIX.C:295
time_t elapsedClockTime() const
Returns wall-clock time from clock instantiation.
Definition: clock.C:122
IOobject dictIO(dictName, runTime.constant(), mesh, IOobject::MUST_READ_IF_MODIFIED, IOobject::NO_WRITE)
bool contains(const vector &dir, const point &) const
Contains point (inside or on edge) and moving in direction.
Definition: treeBoundBox.C:393
label internalStart() const
Return the index of the start of the internal feature edges.
A templated 2D square symmetric matrix of objects of <T>, where the n x n matrix dimension is known a...
Representation of a 3D Cartesian coordinate system as a Vector of vectors.
Definition: triad.H:65
Point circumCentre() const
Return circum-centre.
Definition: triangleI.H:110
const char * asText() const
Return a text representation of the Switch.
Definition: Switch.C:123
const Field< PointType > & faceNormals() const
Return face normals for patch.
vector point
Point is a vector.
Definition: point.H:41
int edgeDirection(const edge &) const
Return the edge direction on the face.
Definition: triFaceI.H:345
A surfaceWriter for VTK legacy format.
const dimensionSet dimLength(0, 1, 0, 0, 0, 0, 0)
Definition: dimensionSets.H:50
word dictName("noiseDict")
tmp< pointField > points() const
Vertex coordinates. In octant coding.
Definition: treeBoundBox.C:165
label flatStart() const
Return the index of the start of the flat feature edges.
FixedList< scalar, 4 > planeCoeffs() const
Return coefficients for the.
Definition: plane.C:252
label index() const
Return index.
const edgeList & edges() const
Return edges.
Definition: edgeMeshI.H:45
const Point & hitPoint() const
Return hit point.
Templated 2D symmetric tensor derived from VectorSpace adding construction from 4 components...
Definition: SymmTensor2D.H:53
vector vec(const pointField &) const
Return the vector (end - start)
Definition: edgeI.H:157
virtual bool write() const
Write using setting from DB.
Standard boundBox + extra functionality for use in octree.
Definition: treeBoundBox.H:87
messageStream Info
dimensioned< scalar > mag(const dimensioned< Type > &)
const labelListList & edgeFaces() const
Return edge-face addressing.
An auto-pointer similar to the STL auto_ptr but with automatic casting to a reference to the type and...
Definition: PtrList.H:53
label mixedStart() const
Return the index of the start of the mixed type feature points.
static const SymmTensor2D< Cmpt > zero
Definition: VectorSpace.H:110
const labelListList & pointFaces() const
Return point-face addressing.
scalar circumRadius() const
Return circum-radius.
Definition: triangleI.H:137
static void addNote(const string &)
Add extra notes for the usage information.
Definition: argList.C:124
volScalarField & p
Triangulated surface description with patch information.
Definition: triSurface.H:65
IOobject defines the attributes of an object for which implicit objectRegistry management is supporte...
Definition: IOobject.H:91
T lookupOrDefault(const word &, const T &, bool recursive=false, bool patternMatch=true) const
Find and return a T,.
Tensor< scalar > tensor
Tensor of scalars.
Definition: tensor.H:51
const Field< PointType > & localPoints() const
Return pointField of points in patch.
Holds feature edges/points of surface.
double elapsedCpuTime() const
Return CPU time (in seconds) from the start.
Definition: cpuTime.C:67
Namespace for OpenFOAM.
void LUsolve(scalarSquareMatrix &matrix, List< Type > &source)
Solve the matrix using LU decomposition with pivoting.
ITstream & lookup(const word &, bool recursive=false, bool patternMatch=true) const
Find and return an entry data stream.
Definition: dictionary.C:451