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