surfaceFeatures.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration | Website: https://openfoam.org
5  \\ / A nd | Copyright (C) 2011-2024 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 \*---------------------------------------------------------------------------*/
25 
26 #include "surfaceFeatures.H"
27 #include "triSurface.H"
28 #include "indexedOctree.H"
29 #include "treeDataEdge.H"
30 #include "treeDataPoint.H"
31 #include "meshTools.H"
32 #include "linePointRef.H"
33 #include "OFstream.H"
34 #include "IFstream.H"
35 #include "unitConversion.H"
36 #include "EdgeMap.H"
37 
38 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
39 
40 namespace Foam
41 {
43 
44  const scalar surfaceFeatures::parallelTolerance = sin(degToRad(1.0));
45 }
46 
47 
48 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
49 
50 Foam::pointIndexHit Foam::surfaceFeatures::edgeNearest
51 (
52  const point& start,
53  const point& end,
54  const point& sample
55 )
56 {
57  pointHit eHit = linePointRef(start, end).nearestDist(sample);
58 
59  // Classification of position on edge.
60  label endPoint;
61 
62  if (eHit.hit())
63  {
64  // Nearest point is on edge itself.
65  // Note: might be at or very close to endpoint. We should use tolerance
66  // here.
67  endPoint = -1;
68  }
69  else
70  {
71  // Nearest point has to be one of the end points. Find out
72  // which one.
73  if
74  (
75  mag(eHit.rawPoint() - start)
76  < mag(eHit.rawPoint() - end)
77  )
78  {
79  endPoint = 0;
80  }
81  else
82  {
83  endPoint = 1;
84  }
85  }
86 
87  return pointIndexHit(eHit.hit(), eHit.rawPoint(), endPoint);
88 }
89 
90 
91 // Go from selected edges only to a value for every edge
93  const
94 {
95  List<edgeStatus> edgeStat(surf_.nEdges(), NONE);
96 
97  // Region edges first
98  for (label i = 0; i < externalStart_; i++)
99  {
100  edgeStat[featureEdges_[i]] = REGION;
101  }
102 
103  // External edges
104  for (label i = externalStart_; i < internalStart_; i++)
105  {
106  edgeStat[featureEdges_[i]] = EXTERNAL;
107  }
108 
109  // Internal edges
110  for (label i = internalStart_; i < featureEdges_.size(); i++)
111  {
112  edgeStat[featureEdges_[i]] = INTERNAL;
113  }
114 
115  return edgeStat;
116 }
117 
118 
119 // Set from value for every edge
121 (
122  const List<edgeStatus>& edgeStat,
123  const scalar includedAngle
124 )
125 {
126  // Count
127 
128  label nRegion = 0;
129  label nExternal = 0;
130  label nInternal = 0;
131 
132  forAll(edgeStat, edgeI)
133  {
134  if (edgeStat[edgeI] == REGION)
135  {
136  nRegion++;
137  }
138  else if (edgeStat[edgeI] == EXTERNAL)
139  {
140  nExternal++;
141  }
142  else if (edgeStat[edgeI] == INTERNAL)
143  {
144  nInternal++;
145  }
146  }
147 
148  externalStart_ = nRegion;
149  internalStart_ = externalStart_ + nExternal;
150 
151 
152  // Copy
153 
154  featureEdges_.setSize(internalStart_ + nInternal);
155 
156  label regionI = 0;
157  label externalI = externalStart_;
158  label internalI = internalStart_;
159 
160  forAll(edgeStat, edgeI)
161  {
162  if (edgeStat[edgeI] == REGION)
163  {
164  featureEdges_[regionI++] = edgeI;
165  }
166  else if (edgeStat[edgeI] == EXTERNAL)
167  {
168  featureEdges_[externalI++] = edgeI;
169  }
170  else if (edgeStat[edgeI] == INTERNAL)
171  {
172  featureEdges_[internalI++] = edgeI;
173  }
174  }
175 
176  const scalar minCos = Foam::cos(degToRad(180) - includedAngle);
177 
178  calcFeatPoints(edgeStat, minCos);
179 }
180 
181 
182 //construct feature points where more than 2 feature edges meet
183 void Foam::surfaceFeatures::calcFeatPoints
184 (
185  const List<edgeStatus>& edgeStat,
186  const scalar minCos
187 )
188 {
189  DynamicList<label> featurePoints(surf_.nPoints()/1000);
190 
191  const labelListList& pointEdges = surf_.pointEdges();
192  const edgeList& edges = surf_.edges();
193  const pointField& localPoints = surf_.localPoints();
194 
195  forAll(pointEdges, pointi)
196  {
197  const labelList& pEdges = pointEdges[pointi];
198 
199  label nFeatEdges = 0;
200 
201  forAll(pEdges, i)
202  {
203  if (edgeStat[pEdges[i]] != NONE)
204  {
205  nFeatEdges++;
206  }
207  }
208 
209  if (nFeatEdges > 2)
210  {
211  featurePoints.append(pointi);
212  }
213  else if (nFeatEdges == 2)
214  {
215  // Check the angle between the two edges
216  DynamicList<vector> edgeVecs(2);
217 
218  forAll(pEdges, i)
219  {
220  const label edgeI = pEdges[i];
221 
222  if (edgeStat[edgeI] != NONE)
223  {
224  edgeVecs.append(edges[edgeI].vec(localPoints));
225  edgeVecs.last() /= mag(edgeVecs.last());
226  }
227  }
228 
229  if (mag(edgeVecs[0] & edgeVecs[1]) < minCos)
230  {
231  featurePoints.append(pointi);
232  }
233  }
234  }
235 
236  featurePoints_.transfer(featurePoints);
237 }
238 
239 
240 void Foam::surfaceFeatures::classifyFeatureAngles
241 (
242  const labelListList& edgeFaces,
243  List<edgeStatus>& edgeStat,
244  const scalar minCos,
245  const bool geometricTestOnly
246 ) const
247 {
248  const vectorField& faceNormals = surf_.faceNormals();
249  const pointField& points = surf_.points();
250 
251  // Special case: minCos=1
252  bool selectAll = (mag(minCos-1.0) < small);
253 
254  forAll(edgeFaces, edgeI)
255  {
256  const labelList& eFaces = edgeFaces[edgeI];
257 
258  if (eFaces.size() != 2)
259  {
260  // Non-manifold. What to do here? Is region edge? external edge?
261  edgeStat[edgeI] = REGION;
262  }
263  else
264  {
265  label face0 = eFaces[0];
266  label face1 = eFaces[1];
267 
268  if
269  (
270  !geometricTestOnly
271  && surf_[face0].region() != surf_[face1].region()
272  )
273  {
274  edgeStat[edgeI] = REGION;
275  }
276  else if
277  (
278  selectAll
279  || ((faceNormals[face0] & faceNormals[face1]) < minCos)
280  )
281  {
282  // Check if convex or concave by looking at angle
283  // between face centres and normal
284  vector f0Tof1 =
285  surf_[face1].centre(points)
286  - surf_[face0].centre(points);
287 
288  if ((f0Tof1 & faceNormals[face0]) >= 0.0)
289  {
290  edgeStat[edgeI] = INTERNAL;
291  }
292  else
293  {
294  edgeStat[edgeI] = EXTERNAL;
295  }
296  }
297  }
298  }
299 }
300 
301 
302 // Returns next feature edge connected to pointi with correct value.
303 Foam::label Foam::surfaceFeatures::nextFeatEdge
304 (
305  const List<edgeStatus>& edgeStat,
306  const labelList& featVisited,
307  const label unsetVal,
308  const label prevEdgeI,
309  const label vertI
310 ) const
311 {
312  const labelList& pEdges = surf_.pointEdges()[vertI];
313 
314  label nextEdgeI = -1;
315 
316  forAll(pEdges, i)
317  {
318  label edgeI = pEdges[i];
319 
320  if
321  (
322  edgeI != prevEdgeI
323  && edgeStat[edgeI] != NONE
324  && featVisited[edgeI] == unsetVal
325  )
326  {
327  if (nextEdgeI == -1)
328  {
329  nextEdgeI = edgeI;
330  }
331  else
332  {
333  // More than one feature edge to choose from. End of segment.
334  return -1;
335  }
336  }
337  }
338 
339  return nextEdgeI;
340 }
341 
342 
343 // Finds connected feature edges by walking from prevEdgeI in direction of
344 // prevPointi. Marks feature edges visited in featVisited by assigning them
345 // the current feature line number. Returns cumulative length of edges walked.
346 // Works in one of two modes:
347 // - mark : step to edges with featVisited = -1.
348 // Mark edges visited with currentFeatI.
349 // - clear : step to edges with featVisited = currentFeatI
350 // Mark edges visited with -2 and erase from feature edges.
351 Foam::surfaceFeatures::labelScalar Foam::surfaceFeatures::walkSegment
352 (
353  const bool mark,
354  const List<edgeStatus>& edgeStat,
355  const label startEdgeI,
356  const label startPointi,
357  const label currentFeatI,
358  labelList& featVisited
359 )
360 {
361  label edgeI = startEdgeI;
362 
363  label vertI = startPointi;
364 
365  scalar visitedLength = 0.0;
366 
367  label nVisited = 0;
368 
369  if (findIndex(featurePoints_, startPointi) >= 0)
370  {
371  // Do not walk across feature points
372 
373  return labelScalar(nVisited, visitedLength);
374  }
375 
376  //
377  // Now we have:
378  // edgeI : first edge on this segment
379  // vertI : one of the endpoints of this segment
380  //
381  // Start walking, marking off edges (in featVisited)
382  // as we go along.
383  //
384 
385  label unsetVal;
386 
387  if (mark)
388  {
389  unsetVal = -1;
390  }
391  else
392  {
393  unsetVal = currentFeatI;
394  }
395 
396  do
397  {
398  // Step to next feature edge with value unsetVal
399  edgeI = nextFeatEdge(edgeStat, featVisited, unsetVal, edgeI, vertI);
400 
401  if (edgeI == -1 || edgeI == startEdgeI)
402  {
403  break;
404  }
405 
406  // Mark with current value. If in clean mode also remove feature edge.
407 
408  if (mark)
409  {
410  featVisited[edgeI] = currentFeatI;
411  }
412  else
413  {
414  featVisited[edgeI] = -2;
415  }
416 
417 
418  // Step to next vertex on edge
419  const edge& e = surf_.edges()[edgeI];
420 
421  vertI = e.otherVertex(vertI);
422 
423 
424  // Update cumulative length
425  visitedLength += e.mag(surf_.localPoints());
426 
427  nVisited++;
428 
429  if (nVisited > surf_.nEdges())
430  {
431  Warning<< "walkSegment : reached iteration limit in walking "
432  << "feature edges on surface from edge:" << startEdgeI
433  << " vertex:" << startPointi << nl
434  << "Returning with large length" << endl;
435 
436  return labelScalar(nVisited, great);
437  }
438  }
439  while (true);
440 
441  return labelScalar(nVisited, visitedLength);
442 }
443 
444 
445 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
446 
448 :
449  surf_(surf),
450  featurePoints_(0),
451  featureEdges_(0),
452  externalStart_(0),
453  internalStart_(0)
454 {}
455 
456 
457 // Construct from components.
459 (
460  const triSurface& surf,
461  const labelList& featurePoints,
462  const labelList& featureEdges,
463  const label externalStart,
464  const label internalStart
465 )
466 :
467  surf_(surf),
468  featurePoints_(featurePoints),
469  featureEdges_(featureEdges),
470  externalStart_(externalStart),
471  internalStart_(externalStart)
472 {}
473 
474 
475 // Construct from surface, angle and min length
477 (
478  const triSurface& surf,
479  const scalar includedAngle,
480  const scalar minLen,
481  const label minElems,
482  const bool geometricTestOnly
483 )
484 :
485  surf_(surf),
486  featurePoints_(0),
487  featureEdges_(0),
488  externalStart_(0),
489  internalStart_(0)
490 {
491  findFeatures(includedAngle, geometricTestOnly);
492 
493  if (minLen > 0 || minElems > 0)
494  {
495  trimFeatures(minLen, minElems, includedAngle);
496  }
497 }
498 
499 
501 (
502  const triSurface& surf,
503  const dictionary& featInfoDict
504 )
505 :
506  surf_(surf),
507  featurePoints_(featInfoDict.lookup("featurePoints")),
508  featureEdges_(featInfoDict.lookup("featureEdges")),
509  externalStart_(featInfoDict.lookup<label>("externalStart")),
510  internalStart_(featInfoDict.lookup<label>("internalStart"))
511 {}
512 
513 
515 (
516  const triSurface& surf,
517  const fileName& fName
518 )
519 :
520  surf_(surf),
521  featurePoints_(0),
522  featureEdges_(0),
523  externalStart_(0),
524  internalStart_(0)
525 {
526  IFstream str(fName);
527 
528  dictionary featInfoDict(str);
529 
530  featureEdges_ = labelList(featInfoDict.lookup("featureEdges"));
531  featurePoints_ = labelList(featInfoDict.lookup("featurePoints"));
532  externalStart_ = featInfoDict.lookup<label>("externalStart");
533  internalStart_ = featInfoDict.lookup<label>("internalStart");
534 }
535 
536 
538 (
539  const triSurface& surf,
540  const pointField& points,
541  const edgeList& edges,
542  const scalar mergeTol,
543  const bool geometricTestOnly
544 )
545 :
546  surf_(surf),
547  featurePoints_(0),
548  featureEdges_(0),
549  externalStart_(0),
550  internalStart_(0)
551 {
552  // Match edge mesh edges with the triSurface edges
553 
554  const labelListList& surfEdgeFaces = surf_.edgeFaces();
555  const edgeList& surfEdges = surf_.edges();
556 
557  scalar mergeTolSqr = sqr(mergeTol);
558 
559  EdgeMap<label> dynFeatEdges(2*edges.size());
560  DynamicList<labelList> dynFeatureEdgeFaces(edges.size());
561 
562  labelList edgeLabel;
563 
565  (
566  edges,
567  points,
568  mergeTolSqr,
569  edgeLabel // label of surface edge or -1
570  );
571 
572  label count = 0;
573  forAll(edgeLabel, sEdgeI)
574  {
575  const label sEdge = edgeLabel[sEdgeI];
576 
577  if (sEdge == -1)
578  {
579  continue;
580  }
581 
582  dynFeatEdges.insert(surfEdges[sEdge], count++);
583  dynFeatureEdgeFaces.append(surfEdgeFaces[sEdge]);
584  }
585 
586  // Find whether an edge is external or internal
587  List<edgeStatus> edgeStat(dynFeatEdges.size(), NONE);
588 
589  classifyFeatureAngles
590  (
591  dynFeatureEdgeFaces,
592  edgeStat,
593  great,
594  geometricTestOnly
595  );
596 
597  // Transfer the edge status to a list encompassing all edges in the surface
598  // so that calcFeatPoints can be used.
599  List<edgeStatus> allEdgeStat(surf_.nEdges(), NONE);
600 
601  forAll(allEdgeStat, eI)
602  {
603  EdgeMap<label>::const_iterator iter = dynFeatEdges.find(surfEdges[eI]);
604 
605  if (iter != dynFeatEdges.end())
606  {
607  allEdgeStat[eI] = edgeStat[iter()];
608  }
609  }
610 
611  edgeStat.clear();
612  dynFeatEdges.clear();
613 
614  setFromStatus(allEdgeStat, great);
615 }
616 
617 
619 :
620  surf_(sf.surface()),
621  featurePoints_(sf.featurePoints()),
622  featureEdges_(sf.featureEdges()),
623  externalStart_(sf.externalStart()),
624  internalStart_(sf.internalStart())
625 {}
626 
627 
628 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
629 
631 (
632  const bool regionEdges,
633  const bool externalEdges,
634  const bool internalEdges
635 ) const
636 {
637  DynamicList<label> selectedEdges;
638 
639  if (regionEdges)
640  {
641  selectedEdges.setCapacity(selectedEdges.size() + nRegionEdges());
642 
643  for (label i = 0; i < externalStart_; i++)
644  {
645  selectedEdges.append(featureEdges_[i]);
646  }
647  }
648 
649  if (externalEdges)
650  {
651  selectedEdges.setCapacity(selectedEdges.size() + nExternalEdges());
652 
653  for (label i = externalStart_; i < internalStart_; i++)
654  {
655  selectedEdges.append(featureEdges_[i]);
656  }
657  }
658 
659  if (internalEdges)
660  {
661  selectedEdges.setCapacity(selectedEdges.size() + nInternalEdges());
662 
663  for (label i = internalStart_; i < featureEdges_.size(); i++)
664  {
665  selectedEdges.append(featureEdges_[i]);
666  }
667  }
668 
669  return selectedEdges.shrink();
670 }
671 
672 
674 (
675  const scalar includedAngle,
676  const bool geometricTestOnly
677 )
678 {
679  scalar minCos = Foam::cos(degToRad(180) - includedAngle);
680 
681  // Per edge whether is feature edge.
682  List<edgeStatus> edgeStat(surf_.nEdges(), NONE);
683 
684  classifyFeatureAngles
685  (
686  surf_.edgeFaces(),
687  edgeStat,
688  minCos,
689  geometricTestOnly
690  );
691 
692  setFromStatus(edgeStat, includedAngle);
693 }
694 
695 
696 // Remove small strings of edges. First assigns string number to
697 // every edge and then works out the length of them.
699 (
700  const scalar minLen,
701  const label minElems,
702  const scalar includedAngle
703 )
704 {
705  // Convert feature edge list to status per edge.
706  List<edgeStatus> edgeStat(toStatus());
707 
708 
709  // Mark feature edges according to connected lines.
710  // -1: unassigned
711  // -2: part of too small a feature line
712  // >0: feature line number
713  labelList featLines(surf_.nEdges(), -1);
714 
715  // Current featureline number.
716  label featI = 0;
717 
718  // Current starting edge
719  label startEdgeI = 0;
720 
721  do
722  {
723  // Find unset featureline
724  for (; startEdgeI < edgeStat.size(); startEdgeI++)
725  {
726  if
727  (
728  edgeStat[startEdgeI] != NONE
729  && featLines[startEdgeI] == -1
730  )
731  {
732  // Found unassigned feature edge.
733  break;
734  }
735  }
736 
737  if (startEdgeI == edgeStat.size())
738  {
739  // No unset feature edge found. All feature edges have line number
740  // assigned.
741  break;
742  }
743 
744  featLines[startEdgeI] = featI;
745 
746  const edge& startEdge = surf_.edges()[startEdgeI];
747 
748  // Walk 'left' and 'right' from startEdge.
749  labelScalar leftPath =
750  walkSegment
751  (
752  true, // 'mark' mode
753  edgeStat,
754  startEdgeI, // edge, not yet assigned to a featureLine
755  startEdge[0], // start of edge
756  featI, // Mark value
757  featLines // Mark for all edges
758  );
759 
760  labelScalar rightPath =
761  walkSegment
762  (
763  true,
764  edgeStat,
765  startEdgeI,
766  startEdge[1], // end of edge
767  featI,
768  featLines
769  );
770 
771  if
772  (
773  (
774  leftPath.len_
775  + rightPath.len_
776  + startEdge.mag(surf_.localPoints())
777  < minLen
778  )
779  || (leftPath.n_ + rightPath.n_ + 1 < minElems)
780  )
781  {
782  // Rewalk same route (recognisable by featLines == featI)
783  // to reset featLines.
784 
785  featLines[startEdgeI] = -2;
786 
787  walkSegment
788  (
789  false, // 'clean' mode
790  edgeStat,
791  startEdgeI, // edge, not yet assigned to a featureLine
792  startEdge[0], // start of edge
793  featI, // Unset value
794  featLines // Mark for all edges
795  );
796 
797  walkSegment
798  (
799  false,
800  edgeStat,
801  startEdgeI,
802  startEdge[1], // end of edge
803  featI,
804  featLines
805  );
806  }
807  else
808  {
809  featI++;
810  }
811  }
812  while (true);
813 
814  // Unmark all feature lines that have featLines=-2
815  forAll(featureEdges_, i)
816  {
817  label edgeI = featureEdges_[i];
818 
819  if (featLines[edgeI] == -2)
820  {
821  edgeStat[edgeI] = NONE;
822  }
823  }
824 
825  // Convert back to edge labels
826  setFromStatus(edgeStat, includedAngle);
827 
828  return featLines;
829 }
830 
831 
833 {
834  dictionary featInfoDict;
835  featInfoDict.add("externalStart", externalStart_);
836  featInfoDict.add("internalStart", internalStart_);
837  featInfoDict.add("featureEdges", featureEdges_);
838  featInfoDict.add("featurePoints", featurePoints_);
839 
840  featInfoDict.write(writeFile);
841 }
842 
843 
844 void Foam::surfaceFeatures::write(const fileName& fName) const
845 {
846  OFstream str(fName);
847 
848  writeDict(str);
849 }
850 
851 
852 void Foam::surfaceFeatures::writeObj(const fileName& prefix) const
853 {
854  OFstream regionStr(prefix + "_regionEdges.obj");
855  Pout<< "Writing region edges to " << regionStr.name() << endl;
856 
857  label verti = 0;
858  for (label i = 0; i < externalStart_; i++)
859  {
860  const edge& e = surf_.edges()[featureEdges_[i]];
861 
862  meshTools::writeOBJ(regionStr, surf_.localPoints()[e[0]]); verti++;
863  meshTools::writeOBJ(regionStr, surf_.localPoints()[e[1]]); verti++;
864  regionStr << "l " << verti-1 << ' ' << verti << endl;
865  }
866 
867 
868  OFstream externalStr(prefix + "_externalEdges.obj");
869  Pout<< "Writing external edges to " << externalStr.name() << endl;
870 
871  verti = 0;
872  for (label i = externalStart_; i < internalStart_; i++)
873  {
874  const edge& e = surf_.edges()[featureEdges_[i]];
875 
876  meshTools::writeOBJ(externalStr, surf_.localPoints()[e[0]]); verti++;
877  meshTools::writeOBJ(externalStr, surf_.localPoints()[e[1]]); verti++;
878  externalStr << "l " << verti-1 << ' ' << verti << endl;
879  }
880 
881  OFstream internalStr(prefix + "_internalEdges.obj");
882  Pout<< "Writing internal edges to " << internalStr.name() << endl;
883 
884  verti = 0;
885  for (label i = internalStart_; i < featureEdges_.size(); i++)
886  {
887  const edge& e = surf_.edges()[featureEdges_[i]];
888 
889  meshTools::writeOBJ(internalStr, surf_.localPoints()[e[0]]); verti++;
890  meshTools::writeOBJ(internalStr, surf_.localPoints()[e[1]]); verti++;
891  internalStr << "l " << verti-1 << ' ' << verti << endl;
892  }
893 
894  OFstream pointStr(prefix + "_points.obj");
895  Pout<< "Writing feature points to " << pointStr.name() << endl;
896 
897  forAll(featurePoints_, i)
898  {
899  label pointi = featurePoints_[i];
900 
901  meshTools::writeOBJ(pointStr, surf_.localPoints()[pointi]);
902  }
903 }
904 
905 
906 // Get nearest vertex on patch for every point of surf in pointSet.
908 (
909  const labelList& pointLabels,
910  const pointField& samples,
911  const scalarField& maxDistSqr
912 ) const
913 {
914  // Build tree out of all samples.
915 
916  // Note: cannot be done one the fly - gcc4.4 compiler bug.
917  treeBoundBox bb(samples);
918 
920  (
921  treeDataPoint(samples), // all information needed to do checks
922  bb, // overall search domain
923  8, // maxLevel
924  10, // leafsize
925  3.0 // duplicity
926  );
927 
928  // From patch point to surface point
929  Map<label> nearest(2*pointLabels.size());
930 
931  const pointField& surfPoints = surf_.localPoints();
932 
933  forAll(pointLabels, i)
934  {
935  label surfPointi = pointLabels[i];
936 
937  const point& surfPt = surfPoints[surfPointi];
938 
939  pointIndexHit info = ppTree.findNearest
940  (
941  surfPt,
942  maxDistSqr[i]
943  );
944 
945  if (!info.hit())
946  {
948  << "Problem for point "
949  << surfPointi << " in tree " << ppTree.bb()
950  << abort(FatalError);
951  }
952 
953  label sampleI = info.index();
954 
955  if (magSqr(samples[sampleI] - surfPt) < maxDistSqr[sampleI])
956  {
957  nearest.insert(sampleI, surfPointi);
958  }
959  }
960 
961 
962  if (debug)
963  {
964  //
965  // Dump to obj file
966  //
967 
968  Pout<< endl
969  << "Dumping nearest surface feature points to nearestSamples.obj"
970  << endl
971  << "View this Lightwave-OBJ file with e.g. javaview" << endl
972  << endl;
973 
974  OFstream objStream("nearestSamples.obj");
975 
976  label vertI = 0;
977  forAllConstIter(Map<label>, nearest, iter)
978  {
979  meshTools::writeOBJ(objStream, samples[iter.key()]); vertI++;
980  meshTools::writeOBJ(objStream, surfPoints[iter()]); vertI++;
981  objStream<< "l " << vertI-1 << ' ' << vertI << endl;
982  }
983  }
984 
985  return nearest;
986 }
987 
988 
989 // Get nearest sample point for regularly sampled points along
990 // selected edges. Return map from sample to edge label
992 (
993  const labelList& selectedEdges,
994  const pointField& samples,
995  const scalarField& sampleDist,
996  const scalarField& maxDistSqr,
997  const scalar minSampleDist
998 ) const
999 {
1000  const pointField& surfPoints = surf_.localPoints();
1001  const edgeList& surfEdges = surf_.edges();
1002 
1003  scalar maxSearchSqr = max(maxDistSqr);
1004 
1005  // Note: cannot be done one the fly - gcc4.4 compiler bug.
1006  treeBoundBox bb(samples);
1007 
1009  (
1010  treeDataPoint(samples), // all information needed to do checks
1011  bb, // overall search domain
1012  8, // maxLevel
1013  10, // leafsize
1014  3.0 // duplicity
1015  );
1016 
1017  // From patch point to surface edge.
1018  Map<label> nearest(2*selectedEdges.size());
1019 
1020  forAll(selectedEdges, i)
1021  {
1022  label surfEdgeI = selectedEdges[i];
1023 
1024  const edge& e = surfEdges[surfEdgeI];
1025 
1026  if (debug && (i % 1000) == 0)
1027  {
1028  Pout<< "looking at surface feature edge " << surfEdgeI
1029  << " verts:" << e << " points:" << surfPoints[e[0]]
1030  << ' ' << surfPoints[e[1]] << endl;
1031  }
1032 
1033  // Normalised edge vector
1034  vector eVec = e.vec(surfPoints);
1035  scalar eMag = mag(eVec);
1036  eVec /= eMag;
1037 
1038 
1039  //
1040  // Sample along edge
1041  //
1042 
1043  bool exit = false;
1044 
1045  // Coordinate along edge (0 .. eMag)
1046  scalar s = 0.0;
1047 
1048  while (true)
1049  {
1050  point edgePoint(surfPoints[e.start()] + s*eVec);
1051 
1052  pointIndexHit info = ppTree.findNearest
1053  (
1054  edgePoint,
1055  maxSearchSqr
1056  );
1057 
1058  if (!info.hit())
1059  {
1060  // No point close enough to surface edge.
1061  break;
1062  }
1063 
1064  label sampleI = info.index();
1065 
1066  if (magSqr(info.hitPoint() - edgePoint) < maxDistSqr[sampleI])
1067  {
1068  nearest.insert(sampleI, surfEdgeI);
1069  }
1070 
1071  if (exit)
1072  {
1073  break;
1074  }
1075 
1076  // Step to next sample point using local distance.
1077  // Truncate to max 1/minSampleDist samples per feature edge.
1078  s += max(minSampleDist*eMag, sampleDist[sampleI]);
1079 
1080  if (s >= (1-minSampleDist)*eMag)
1081  {
1082  // Do one more sample, at edge endpoint
1083  s = eMag;
1084  exit = true;
1085  }
1086  }
1087  }
1088 
1089 
1090 
1091  if (debug)
1092  {
1093  // Dump to obj file
1094 
1095  Pout<< "Dumping nearest surface edges to nearestEdges.obj\n"
1096  << "View this Lightwave-OBJ file with e.g. javaview\n" << endl;
1097 
1098  OFstream objStream("nearestEdges.obj");
1099 
1100  label vertI = 0;
1101  forAllConstIter(Map<label>, nearest, iter)
1102  {
1103  const label sampleI = iter.key();
1104 
1105  meshTools::writeOBJ(objStream, samples[sampleI]); vertI++;
1106 
1107  const edge& e = surfEdges[iter()];
1108 
1109  point nearPt =
1110  e.line(surfPoints).nearestDist(samples[sampleI]).rawPoint();
1111 
1112  meshTools::writeOBJ(objStream, nearPt); vertI++;
1113 
1114  objStream<< "l " << vertI-1 << ' ' << vertI << endl;
1115  }
1116  }
1117 
1118  return nearest;
1119 }
1120 
1121 
1122 // Get nearest edge on patch for regularly sampled points along the feature
1123 // edges. Return map from patch edge to feature edge.
1124 //
1125 // Q: using point-based sampleDist and maxDist (distance to look around
1126 // each point). Should they be edge-based e.g. by averaging or max()?
1128 (
1129  const labelList& selectedEdges,
1130  const edgeList& sampleEdges,
1131  const labelList& selectedSampleEdges,
1132  const pointField& samplePoints,
1133  const scalarField& sampleDist,
1134  const scalarField& maxDistSqr,
1135  const scalar minSampleDist
1136 ) const
1137 {
1138  // Build tree out of selected sample edges.
1140  (
1141  treeDataEdge
1142  (
1143  false,
1144  sampleEdges,
1145  samplePoints,
1146  selectedSampleEdges
1147  ), // geometric info container for edges
1148  treeBoundBox(samplePoints), // overall search domain
1149  8, // maxLevel
1150  10, // leafsize
1151  3.0 // duplicity
1152  );
1153 
1154  const pointField& surfPoints = surf_.localPoints();
1155  const edgeList& surfEdges = surf_.edges();
1156 
1157  scalar maxSearchSqr = max(maxDistSqr);
1158 
1159  Map<pointIndexHit> nearest(2*sampleEdges.size());
1160 
1161  //
1162  // Loop over all selected edges. Sample at regular intervals. Find nearest
1163  // sampleEdges (using octree)
1164  //
1165 
1166  forAll(selectedEdges, i)
1167  {
1168  label surfEdgeI = selectedEdges[i];
1169 
1170  const edge& e = surfEdges[surfEdgeI];
1171 
1172  if (debug && (i % 1000) == 0)
1173  {
1174  Pout<< "looking at surface feature edge " << surfEdgeI
1175  << " verts:" << e << " points:" << surfPoints[e[0]]
1176  << ' ' << surfPoints[e[1]] << endl;
1177  }
1178 
1179  // Normalised edge vector
1180  vector eVec = e.vec(surfPoints);
1181  scalar eMag = mag(eVec);
1182  eVec /= eMag;
1183 
1184 
1185  //
1186  // Sample along edge
1187  //
1188 
1189  bool exit = false;
1190 
1191  // Coordinate along edge (0 .. eMag)
1192  scalar s = 0.0;
1193 
1194  while (true)
1195  {
1196  point edgePoint(surfPoints[e.start()] + s*eVec);
1197 
1198  pointIndexHit info = ppTree.findNearest
1199  (
1200  edgePoint,
1201  maxSearchSqr
1202  );
1203 
1204  if (!info.hit())
1205  {
1206  // No edge close enough to surface edge.
1207  break;
1208  }
1209 
1210  label index = info.index();
1211 
1212  label sampleEdgeI = ppTree.shapes().edgeLabels()[index];
1213 
1214  const edge& e = sampleEdges[sampleEdgeI];
1215 
1216  if (magSqr(info.hitPoint() - edgePoint) < maxDistSqr[e.start()])
1217  {
1218  nearest.insert
1219  (
1220  sampleEdgeI,
1221  pointIndexHit(true, edgePoint, surfEdgeI)
1222  );
1223  }
1224 
1225  if (exit)
1226  {
1227  break;
1228  }
1229 
1230  // Step to next sample point using local distance.
1231  // Truncate to max 1/minSampleDist samples per feature edge.
1232  // s += max(minSampleDist*eMag, sampleDist[e.start()]);
1233  s += 0.01*eMag;
1234 
1235  if (s >= (1-minSampleDist)*eMag)
1236  {
1237  // Do one more sample, at edge endpoint
1238  s = eMag;
1239  exit = true;
1240  }
1241  }
1242  }
1243 
1244 
1245  if (debug)
1246  {
1247  // Dump to obj file
1248 
1249  Pout<< "Dumping nearest surface feature edges to nearestEdges.obj\n"
1250  << "View this Lightwave-OBJ file with e.g. javaview\n" << endl;
1251 
1252  OFstream objStream("nearestEdges.obj");
1253 
1254  label vertI = 0;
1255  forAllConstIter(Map<pointIndexHit>, nearest, iter)
1256  {
1257  const label sampleEdgeI = iter.key();
1258 
1259  const edge& sampleEdge = sampleEdges[sampleEdgeI];
1260 
1261  // Write line from edgeMid to point on feature edge
1262  meshTools::writeOBJ(objStream, sampleEdge.centre(samplePoints));
1263  vertI++;
1264 
1265  meshTools::writeOBJ(objStream, iter().rawPoint());
1266  vertI++;
1267 
1268  objStream<< "l " << vertI-1 << ' ' << vertI << endl;
1269  }
1270  }
1271 
1272  return nearest;
1273 }
1274 
1275 
1276 // Get nearest surface edge for every sample. Return in form of
1277 // labelLists giving surfaceEdge label&intersectionpoint.
1279 (
1280  const labelList& selectedEdges,
1281  const pointField& samples,
1282  scalar searchSpanSqr, // Search span
1283  labelList& edgeLabel,
1284  labelList& edgeEndPoint,
1285  pointField& edgePoint
1286 ) const
1287 {
1288  edgeLabel.setSize(samples.size());
1289  edgeEndPoint.setSize(samples.size());
1290  edgePoint.setSize(samples.size());
1291 
1292  const pointField& localPoints = surf_.localPoints();
1293 
1294  treeBoundBox searchDomain(localPoints);
1295  searchDomain.inflate(0.1);
1296 
1298  (
1299  treeDataEdge
1300  (
1301  false,
1302  surf_.edges(),
1303  localPoints,
1304  selectedEdges
1305  ), // all information needed to do geometric checks
1306  searchDomain, // overall search domain
1307  8, // maxLevel
1308  10, // leafsize
1309  3.0 // duplicity
1310  );
1311 
1312  forAll(samples, i)
1313  {
1314  const point& sample = samples[i];
1315 
1316  pointIndexHit info = ppTree.findNearest
1317  (
1318  sample,
1319  searchSpanSqr
1320  );
1321 
1322  if (!info.hit())
1323  {
1324  edgeLabel[i] = -1;
1325  }
1326  else
1327  {
1328  edgeLabel[i] = selectedEdges[info.index()];
1329 
1330  // Need to recalculate to classify edgeEndPoint
1331  const edge& e = surf_.edges()[edgeLabel[i]];
1332 
1333  pointIndexHit pHit = edgeNearest
1334  (
1335  localPoints[e.start()],
1336  localPoints[e.end()],
1337  sample
1338  );
1339 
1340  edgePoint[i] = pHit.rawPoint();
1341  edgeEndPoint[i] = pHit.index();
1342  }
1343  }
1344 }
1345 
1346 
1347 // Get nearest point on nearest feature edge for every sample (is edge)
1349 (
1350  const labelList& selectedEdges,
1351 
1352  const edgeList& sampleEdges,
1353  const labelList& selectedSampleEdges,
1354  const pointField& samplePoints,
1355 
1356  const vector& searchSpan, // Search span
1357  labelList& edgeLabel, // label of surface edge or -1
1358  pointField& pointOnEdge, // point on above edge
1359  pointField& pointOnFeature // point on sample edge
1360 ) const
1361 {
1362  edgeLabel.setSize(selectedSampleEdges.size());
1363  pointOnEdge.setSize(selectedSampleEdges.size());
1364  pointOnFeature.setSize(selectedSampleEdges.size());
1365 
1366  treeBoundBox searchDomain(surf_.localPoints());
1367 
1369  (
1370  treeDataEdge
1371  (
1372  false,
1373  surf_.edges(),
1374  surf_.localPoints(),
1375  selectedEdges
1376  ), // all information needed to do geometric checks
1377  searchDomain, // overall search domain
1378  8, // maxLevel
1379  10, // leafsize
1380  3.0 // duplicity
1381  );
1382 
1383  forAll(selectedSampleEdges, i)
1384  {
1385  const edge& e = sampleEdges[selectedSampleEdges[i]];
1386 
1387  linePointRef edgeLine = e.line(samplePoints);
1388 
1389  point eMid(edgeLine.centre());
1390 
1391  treeBoundBox tightest(eMid - searchSpan, eMid + searchSpan);
1392 
1393  pointIndexHit info = ppTree.findNearest
1394  (
1395  edgeLine,
1396  tightest,
1397  pointOnEdge[i]
1398  );
1399 
1400  if (!info.hit())
1401  {
1402  edgeLabel[i] = -1;
1403  }
1404  else
1405  {
1406  edgeLabel[i] = selectedEdges[info.index()];
1407 
1408  pointOnFeature[i] = info.hitPoint();
1409  }
1410  }
1411 }
1412 
1413 
1415 (
1416  const edgeList& edges,
1417  const pointField& points,
1418  scalar searchSpanSqr, // Search span
1419  labelList& edgeLabel
1420 ) const
1421 {
1422  edgeLabel = labelList(surf_.nEdges(), -1);
1423 
1424  treeBoundBox searchDomain(points);
1425  searchDomain.inflate(0.1);
1426 
1428  (
1429  treeDataEdge
1430  (
1431  false,
1432  edges,
1433  points,
1434  identityMap(edges.size())
1435  ), // all information needed to do geometric checks
1436  searchDomain, // overall search domain
1437  8, // maxLevel
1438  10, // leafsize
1439  3.0 // duplicity
1440  );
1441 
1442  const edgeList& surfEdges = surf_.edges();
1443  const pointField& surfLocalPoints = surf_.localPoints();
1444 
1445  forAll(surfEdges, edgeI)
1446  {
1447  const edge& sample = surfEdges[edgeI];
1448 
1449  const point& startPoint = surfLocalPoints[sample.start()];
1450  const point& midPoint = sample.centre(surfLocalPoints);
1451 
1452  pointIndexHit infoMid = ppTree.findNearest
1453  (
1454  midPoint,
1455  searchSpanSqr
1456  );
1457 
1458  if (infoMid.hit())
1459  {
1460  const vector surfEdgeDir = midPoint - startPoint;
1461 
1462  const edge& featEdge = edges[infoMid.index()];
1463  const vector featEdgeDir = featEdge.vec(points);
1464 
1465  // Check that the edges are nearly parallel
1466  if (mag(surfEdgeDir ^ featEdgeDir) < parallelTolerance)
1467  {
1468  edgeLabel[edgeI] = edgeI;
1469  }
1470  }
1471  }
1472 }
1473 
1474 
1475 // * * * * * * * * * * * * * * * Member Operators * * * * * * * * * * * * * //
1476 
1478 {
1479  // Check for assignment to self
1480  if (this == &rhs)
1481  {
1483  << "Attempted assignment to self"
1484  << abort(FatalError);
1485  }
1486 
1487  if (&surf_ != &rhs.surface())
1488  {
1490  << "Operating on different surfaces"
1491  << abort(FatalError);
1492  }
1493 
1494  featurePoints_ = rhs.featurePoints();
1495  featureEdges_ = rhs.featureEdges();
1496  externalStart_ = rhs.externalStart();
1497  internalStart_ = rhs.internalStart();
1498 }
1499 
1500 
1501 // * * * * * * * * * * * * * * * Global Functions * * * * * * * * * * * * * //
1502 
1504 (
1505  const triSurface& surf,
1506  const boundBox& bb,
1507  const bool inside,
1509 )
1510 {
1511  forAll(edgeStat, edgei)
1512  {
1513  const point eMid = surf.edges()[edgei].centre(surf.localPoints());
1514 
1515  if (!inside ? bb.contains(eMid) : !bb.contains(eMid))
1516  {
1517  edgeStat[edgei] = surfaceFeatures::NONE;
1518  }
1519  }
1520 }
1521 
1522 
1524 (
1525  const triSurface& surf,
1526  const plane& cutPlane,
1528 )
1529 {
1530  const pointField& points = surf.points();
1531  const labelList& meshPoints = surf.meshPoints();
1532 
1533  forAll(edgeStat, edgei)
1534  {
1535  const edge& e = surf.edges()[edgei];
1536  const point& p0 = points[meshPoints[e.start()]];
1537  const point& p1 = points[meshPoints[e.end()]];
1538  const linePointRef line(p0, p1);
1539 
1540  // If edge does not intersect the plane, delete.
1541  const scalar intersect = cutPlane.lineIntersect(line);
1542  if (intersect < 0 || intersect > 1)
1543  {
1544  edgeStat[edgei] = surfaceFeatures::NONE;
1545  }
1546  }
1547 }
1548 
1549 
1551 (
1552  const triSurface& surf,
1553  const scalar tol,
1554  const scalar includedAngle,
1555  const label edgei
1556 )
1557 {
1558  const edge& e = surf.edges()[edgei];
1559  const labelList& eFaces = surf.edgeFaces()[edgei];
1560 
1561  // Bin according to normal
1562 
1563  DynamicList<Foam::vector> normals(2);
1564  DynamicList<labelList> bins(2);
1565 
1566  forAll(eFaces, eFacei)
1567  {
1568  const Foam::vector& n = surf.faceNormals()[eFaces[eFacei]];
1569 
1570  // Find the normal in normals
1571  label index = -1;
1572  forAll(normals, normalI)
1573  {
1574  if (mag(n&normals[normalI]) > (1-tol))
1575  {
1576  index = normalI;
1577  break;
1578  }
1579  }
1580 
1581  if (index != -1)
1582  {
1583  bins[index].append(eFacei);
1584  }
1585  else if (normals.size() >= 2)
1586  {
1587  // Would be third normal. Mark as feature.
1588  // Pout<< "** at edge:" << surf.localPoints()[e[0]]
1589  // << surf.localPoints()[e[1]]
1590  // << " have normals:" << normals
1591  // << " and " << n << endl;
1592  return surfaceFeatures::REGION;
1593  }
1594  else
1595  {
1596  normals.append(n);
1597  bins.append(labelList(1, eFacei));
1598  }
1599  }
1600 
1601  // Check resulting number of bins
1602  if (bins.size() == 1)
1603  {
1604  // Note: should check here whether they are two sets of faces
1605  // that are planar or indeed 4 faces al coming together at an edge.
1606  // Pout<< "** at edge:"
1607  // << surf.localPoints()[e[0]]
1608  // << surf.localPoints()[e[1]]
1609  // << " have single normal:" << normals[0]
1610  // << endl;
1611  return surfaceFeatures::NONE;
1612  }
1613  else
1614  {
1615  // Two bins. Check if normals make an angle
1616 
1617  // Pout<< "** at edge:"
1618  // << surf.localPoints()[e[0]]
1619  // << surf.localPoints()[e[1]] << nl
1620  // << " normals:" << normals << nl
1621  // << " bins :" << bins << nl
1622  // << endl;
1623 
1624  if (includedAngle >= 0)
1625  {
1626  scalar minCos = Foam::cos(degToRad(180) - includedAngle);
1627 
1628  forAll(eFaces, i)
1629  {
1630  const Foam::vector& ni = surf.faceNormals()[eFaces[i]];
1631  for (label j=i+1; j<eFaces.size(); j++)
1632  {
1633  const Foam::vector& nj = surf.faceNormals()[eFaces[j]];
1634  if (mag(ni & nj) < minCos)
1635  {
1636  // Pout<< "have sharp feature between normal:" << ni
1637  // << " and " << nj << endl;
1638 
1639  // Is feature. Keep as region or convert to
1640  // feature angle? For now keep as region.
1641  return surfaceFeatures::REGION;
1642  }
1643  }
1644  }
1645  }
1646 
1647 
1648  // So now we have two normals bins but need to make sure both
1649  // bins have the same regions in it.
1650 
1651  // 1. store + or - region number depending
1652  // on orientation of triangle in bins[0]
1653  const labelList& bin0 = bins[0];
1654  labelList regionAndNormal(bin0.size());
1655  forAll(bin0, i)
1656  {
1657  const labelledTri& t = surf.localFaces()[eFaces[bin0[i]]];
1658  int dir = t.edgeDirection(e);
1659 
1660  if (dir > 0)
1661  {
1662  regionAndNormal[i] = t.region()+1;
1663  }
1664  else if (dir == 0)
1665  {
1667  << exit(FatalError);
1668  }
1669  else
1670  {
1671  regionAndNormal[i] = -(t.region()+1);
1672  }
1673  }
1674 
1675  // 2. check against bin1
1676  const labelList& bin1 = bins[1];
1677  labelList regionAndNormal1(bin1.size());
1678  forAll(bin1, i)
1679  {
1680  const labelledTri& t = surf.localFaces()[eFaces[bin1[i]]];
1681  int dir = t.edgeDirection(e);
1682 
1683  label myRegionAndNormal;
1684  if (dir > 0)
1685  {
1686  myRegionAndNormal = t.region()+1;
1687  }
1688  else
1689  {
1690  myRegionAndNormal = -(t.region()+1);
1691  }
1692 
1693  regionAndNormal1[i] = myRegionAndNormal;
1694 
1695  label index = findIndex(regionAndNormal, -myRegionAndNormal);
1696  if (index == -1)
1697  {
1698  // Not found.
1699  // Pout<< "cannot find region " << myRegionAndNormal
1700  // << " in regions " << regionAndNormal << endl;
1701 
1702  return surfaceFeatures::REGION;
1703  }
1704  }
1705 
1706  return surfaceFeatures::NONE;
1707  }
1708 }
1709 
1710 
1712 (
1713  const triSurface& surf,
1714  const scalar tol,
1715  const scalar includedAngle,
1717 )
1718 {
1719  forAll(edgeStat, edgei)
1720  {
1721  const labelList& eFaces = surf.edgeFaces()[edgei];
1722 
1723  if
1724  (
1725  eFaces.size() > 2
1726  && edgeStat[edgei] == surfaceFeatures::REGION
1727  && (eFaces.size() % 2) == 0
1728  )
1729  {
1730  edgeStat[edgei] = checkNonManifoldEdge
1731  (
1732  surf,
1733  tol,
1734  includedAngle,
1735  edgei
1736  );
1737  }
1738  }
1739 }
1740 
1741 
1742 // ************************************************************************* //
label n
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:434
#define forAllConstIter(Container, container, iter)
Iterate across all elements in the container object of type.
Definition: UList.H:477
void setCapacity(const label)
Alter the size of the underlying storage.
Definition: DynamicListI.H:130
DynamicList< T, SizeInc, SizeMult, SizeDiv > & append(const T &)
Append an element at the end of the list.
Definition: DynamicListI.H:296
DynamicList< T, SizeInc, SizeMult, SizeDiv > & shrink()
Shrink the allocated space to the number of elements used.
Definition: DynamicListI.H:252
label size() const
Return number of elements in table.
Definition: HashTableI.H:65
bool insert(const Key &, const T &newElmt)
Insert a new hashedEntry.
Definition: HashTableI.H:80
iterator find(const Key &)
Find and return an iterator set at the hashedEntry.
Definition: HashTable.C:167
void clear()
Clear all entries from table.
Definition: HashTable.C:542
Input from file stream.
Definition: IFstream.H:85
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
Definition: List.H:91
void append(const T &)
Append an element at the end of the list.
Definition: ListI.H:178
void size(const label)
Override size to be inconsistent with allocated storage.
Definition: ListI.H:164
void clear()
Clear the list, i.e. set size to zero.
Definition: ListI.H:125
void setSize(const label)
Reset size of List.
Definition: List.C:281
A HashTable to objects of type <T> with a label key.
Definition: Map.H:52
Output to file stream.
Definition: OFstream.H:86
const fileName & name() const
Return the name of the stream.
Definition: OFstream.H:120
An Ostream is an abstract base class for all output systems (streams, files, token lists,...
Definition: Ostream.H:57
This class describes the interaction of (usually) a face and a point. It carries the info of a succes...
Definition: PointIndexHit.H:54
const Point & hitPoint() const
Return hit point.
const Point & rawPoint() const
Return point with no checking.
label index() const
Return index.
bool hit() const
Is there a hit.
label nEdges() const
Return number of edges in patch.
const edgeList & edges() const
Return list of edges, address into LOCAL point list.
const labelList & meshPoints() const
Return labelList of mesh points in patch. They are constructed.
const Field< PointType > & points() const
Return reference to global points.
const List< FaceType > & localFaces() const
Return patch faces addressing into local point list.
const labelListList & edgeFaces() const
Return edge-face addressing.
const Field< PointType > & localPoints() const
Return pointField of points in patch.
const Field< PointType > & faceNormals() const
Return face normals for patch.
const Vector< Cmpt > & centre(const Foam::List< Vector< Cmpt >> &) const
Return *this (used for point which is a typedef to Vector<scalar>.
Definition: VectorI.H:116
A bounding box defined in terms of the points at its extremities.
Definition: boundBox.H:59
void inflate(const scalar s)
Inflate box by factor*mag(span) in all dimensions.
Definition: boundBox.C:199
bool contains(const point &) const
Contains point? (inside or on edge)
Definition: boundBoxI.H:176
A list of keyword definitions, which are a keyword followed by any number of values (e....
Definition: dictionary.H:162
ITstream & lookup(const word &, bool recursive=false, bool patternMatch=true) const
Find and return an entry data stream.
Definition: dictionary.C:710
void write(Ostream &, const bool subDict=true) const
Write dictionary, normally with sub-dictionary formatting.
Definition: dictionaryIO.C:211
bool add(entry *, bool mergeEntry=false)
Add a new entry.
Definition: dictionary.C:1014
An edge is a list of two point labels. The functionality it provides supports the discretisation on a...
Definition: edge.H:61
scalar mag(const pointField &) const
Return scalar magnitude.
Definition: edgeI.H:181
vector vec(const pointField &) const
Return the vector (end - start)
Definition: edgeI.H:175
point centre(const pointField &) const
Return centre (centroid)
Definition: edgeI.H:169
label start() const
Return start vertex label.
Definition: edgeI.H:81
A class for handling file names.
Definition: fileName.H:82
Non-pointer based hierarchical recursive searching.
Definition: indexedOctree.H:72
const Type & shapes() const
Reference to shape.
const treeBoundBox & bb() const
Top bounding box.
Triangle with additional region number.
Definition: labelledTri.H:60
label region() const
Return region label.
Definition: labelledTriI.H:68
A line primitive.
Definition: line.H:71
Point centre() const
Return centre (centroid)
Definition: lineI.H:73
Mid-point interpolation (weighting factors = 0.5) scheme class.
Definition: midPoint.H:53
Geometric class that creates a 2D plane and can return the intersection point between a line and the ...
Definition: plane.H:61
scalar lineIntersect(const line< Point, PointRef > &l) const
Return the cutting point between the plane and.
Definition: plane.H:212
Holds feature edges/points of surface.
void findFeatures(const scalar includedAngle, const bool geometricTestOnly)
Find feature edges using provided included angle.
surfaceFeatures(const triSurface &)
Construct from surface.
labelList selectFeatureEdges(const bool regionEdges, const bool externalEdges, const bool internalEdges) const
Helper function: select a subset of featureEdges_.
const labelList & featureEdges() const
Return feature edge list.
void setFromStatus(const List< edgeStatus > &edgeStat, const scalar includedAngle)
Set from status per edge.
void write(const fileName &fName) const
Write as dictionary to file.
const labelList & featurePoints() const
Return feature point list.
void writeObj(const fileName &prefix) const
Write to separate OBJ files (region, external, internal edges,.
Map< pointIndexHit > nearestEdges(const labelList &selectedEdges, const edgeList &sampleEdges, const labelList &selectedSampleEdges, const pointField &samplePoints, const scalarField &sampleDist, const scalarField &maxDistSqr, const scalar minSampleDist=0.1) const
Like nearestSamples but now gets nearest point on.
List< edgeStatus > toStatus() const
From member feature edges to status per edge.
void writeDict(Ostream &) const
Write as dictionary.
void nearestSurfEdge(const labelList &selectedEdges, const pointField &samples, scalar searchSpanSqr, labelList &edgeLabel, labelList &edgeEndPoint, pointField &edgePoint) const
Find nearest surface edge (out of selectedEdges) for.
label internalStart() const
Start of internal edges.
label externalStart() const
Start of external edges.
const triSurface & surface() const
Map< label > nearestSamples(const labelList &selectedPoints, const pointField &samples, const scalarField &maxDistSqr) const
Find nearest sample for selected surface points.
void nearestFeatEdge(const edgeList &edges, const pointField &points, scalar searchSpanSqr, labelList &edgeLabel) const
Find nearest feature edge to each surface edge. Uses the.
void operator=(const surfaceFeatures &)
labelList trimFeatures(const scalar minLen, const label minElems, const scalar includedAngle)
Delete small sets of edges. Edges are stringed up and any.
Standard boundBox + extra functionality for use in octree.
Definition: treeBoundBox.H:90
Holds data for octree to work on an edges subset.
Definition: treeDataEdge.H:54
Holds (reference to) pointField. Encapsulation of data needed for octree searches....
Definition: treeDataPoint.H:60
int edgeDirection(const edge &) const
Return the edge direction on the face.
Definition: triFaceI.H:352
Triangulated surface description with patch information.
Definition: triSurface.H:69
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:334
volScalarField sf(fieldObject, mesh)
const pointField & points
gmvFile<< "tracers "<< particles.size()<< nl;forAllConstIter(Cloud< passiveParticle >, particles, iter){ gmvFile<< iter().position().x()<< " ";}gmvFile<< nl;forAllConstIter(Cloud< passiveParticle >, particles, iter){ gmvFile<< iter().position().y()<< " ";}gmvFile<< nl;forAllConstIter(Cloud< passiveParticle >, particles, iter){ gmvFile<< iter().position().z()<< " ";}gmvFile<< nl;forAll(lagrangianScalarNames, i){ word name=lagrangianScalarNames[i];IOField< scalar > s(IOobject(name, runTime.name(), cloud::prefix, mesh, IOobject::MUST_READ, IOobject::NO_WRITE))
void writeOBJ(Ostream &os, const point &pt)
Write obj representation of point.
Definition: meshTools.C:203
Namespace for OpenFOAM.
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
const doubleScalar e
Definition: doubleScalar.H:106
scalar degToRad(const scalar deg)
Convert degrees to radians.
List< label > labelList
A List of labels.
Definition: labelList.H:56
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
dimensionedSymmTensor sqr(const dimensionedVector &dv)
PointIndexHit< point > pointIndexHit
Definition: pointIndexHit.H:42
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:257
line< point, const point & > linePointRef
Line using referred points.
Definition: linePointRef.H:45
surfaceFeatures::edgeStatus checkNonManifoldEdge(const triSurface &surf, const scalar tol, const scalar includedAngle, const label edgei)
Divide into multiple normal bins.
void selectBox(const triSurface &surf, const boundBox &bb, const bool removeInside, List< surfaceFeatures::edgeStatus > &edgeStat)
Select edges inside or outside bounding box.
errorManip< error > abort(error &err)
Definition: errorManip.H:131
dimensionedScalar sin(const dimensionedScalar &ds)
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:42
vector point
Point is a vector.
Definition: point.H:41
Vector< scalar > vector
A scalar version of the templated Vector.
Definition: vector.H:49
List< labelList > labelListList
A List of labelList.
Definition: labelList.H:57
dimensioned< scalar > mag(const dimensioned< Type > &)
void selectManifoldEdges(const triSurface &surf, const scalar tol, const scalar includedAngle, List< surfaceFeatures::edgeStatus > &edgeStat)
Select manifold edges.
defineTypeNameAndDebug(combustionModel, 0)
layerAndWeight max(const layerAndWeight &a, const layerAndWeight &b)
PointHit< point > pointHit
Definition: pointHit.H:41
Field< vector > vectorField
Specialisation of Field<T> for vector.
prefixOSstream Pout(cout, "Pout")
Definition: IOstreams.H:53
label findIndex(const ListType &, typename ListType::const_reference, const label start=0)
Find first occurrence of given element and return index,.
void selectCutEdges(const triSurface &surf, const plane &cutPlane, List< surfaceFeatures::edgeStatus > &edgeStat)
Select edges that are intersected by the given plane.
error FatalError
label count(const ListType &l, typename ListType::const_reference x)
Count the number of occurrences of a value in a list.
labelList identityMap(const label len)
Create identity map (map[i] == i) of given length.
Definition: ListOps.C:104
static const char nl
Definition: Ostream.H:266
dimensioned< scalar > magSqr(const dimensioned< Type > &)
dimensionedScalar cos(const dimensionedScalar &ds)
messageStream Warning
labelList pointLabels(nPoints, -1)
static iteratorEnd end()
iteratorEnd set to beyond the end of any HashTable
Definition: HashTable.H:112
scalarField samples(nIntervals, 0)