surfaceIntersection.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 | www.openfoam.com
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8  Copyright (C) 2011-2016 OpenFOAM Foundation
9  Copyright (C) 2015-2022 OpenCFD Ltd.
10 -------------------------------------------------------------------------------
11 License
12  This file is part of OpenFOAM.
13 
14  OpenFOAM is free software: you can redistribute it and/or modify it
15  under the terms of the GNU General Public License as published by
16  the Free Software Foundation, either version 3 of the License, or
17  (at your option) any later version.
18 
19  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22  for more details.
23 
24  You should have received a copy of the GNU General Public License
25  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26 
27 \*---------------------------------------------------------------------------*/
28 
29 #include "surfaceIntersection.H"
30 #include "triSurfaceSearch.H"
31 #include "OBJstream.H"
32 #include "labelPairHashes.H"
33 #include "bitSet.H"
34 #include "triSurface.H"
35 #include "pointIndexHit.H"
36 #include "mergePoints.H"
37 #include "plane.H"
38 #include "edgeIntersections.H"
39 
40 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
41 
42 namespace Foam
43 {
44  defineTypeNameAndDebug(surfaceIntersection, 0);
45 }
46 
47 const Foam::Enum
48 <
50 >
52 ({
53  { intersectionType::SELF, "self" },
54  { intersectionType::SELF_REGION, "region" },
55  { intersectionType::NONE, "none" },
56 });
57 
58 
59 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
60 
61 void Foam::surfaceIntersection::setOptions(const dictionary& dict)
62 {
63  dict.readIfPresent("tolerance", tolerance_);
64  dict.readIfPresent("allowEdgeHits", allowEdgeHits_);
65  dict.readIfPresent("snap", snapToEnd_);
66  dict.readIfPresent("warnDegenerate", warnDegenerate_);
67 }
68 
69 
70 void Foam::surfaceIntersection::storeIntersection
71 (
72  const enum intersectionType cutFrom,
73  const labelList& facesA,
74  const label faceB,
75  const UList<point>& allCutPoints,
76  const label cutPointId,
77  DynamicList<edge>& allCutEdges
78 )
79 {
80  // Our lookup for two faces - populate with faceB (invariant)
81  // Normally always have face from the first surface as first element
82  labelPair twoFaces(faceB, faceB);
83 
84  forAll(facesA, facesAI)
85  {
86  const label faceA = facesA[facesAI];
87 
88  switch (cutFrom)
89  {
91  {
92  // faceA from 1st, faceB from 2nd
93  twoFaces.first() = faceA;
94  break;
95  }
97  {
98  // faceA from 2nd, faceB from 1st
99  twoFaces.second() = faceA;
100  break;
101  }
104  {
105  // Lookup should be commutativity - use sorted order
106  if (faceA < faceB)
107  {
108  twoFaces.first() = faceA;
109  twoFaces.second() = faceB;
110  }
111  else
112  {
113  twoFaces.first() = faceB;
114  twoFaces.second() = faceA;
115  }
116  break;
117  }
118 
120  return;
121  break;
122  }
123 
124 
125  // Get existing edge, or create a null edge (with -1)
126  edge& thisEdge = facePairToEdge_(twoFaces);
127  const label pointCount = thisEdge.count();
128 
129  if (pointCount == 0)
130  {
131  // First intersection of the faces - record it.
132  thisEdge.insert(cutPointId);
133 
134  if (debug & 4)
135  {
136  Pout<< "intersect faces " << twoFaces
137  << " point-1: " << cutPointId << " = "
138  << allCutPoints[cutPointId] << endl;
139  }
140  continue;
141  }
142  else if (pointCount == 2)
143  {
144  // This occurs for ugly surfaces with shards that result in multiple
145  // cuts very near a snapped end point.
146  if (debug & 4)
147  {
148  Pout<< "suppressed double intersection " << twoFaces
149  << thisEdge << endl;
150  }
151  continue;
152  }
153 
154  if (thisEdge.insert(cutPointId))
155  {
156  // Second intersection of the faces - this is an edge,
157  // with special treatment:
158  // - avoid duplicate points: addressed by the insert() above
159  // - avoid degenerate lengths
160  // - avoid duplicate edges - can occur with really dirty geometry
161 
162  if (edgeToId_.found(thisEdge))
163  {
164  // Already have this edgeId, but not for this intersection.
165  thisEdge.sort();
166  if (facePairToEdge_.insert(twoFaces, thisEdge))
167  {
168  if (debug & 4)
169  {
170  Pout<< "reuse edge - faces " << twoFaces << " edge#"
171  << edgeToId_[thisEdge] << " edge " << thisEdge
172  << " = " << thisEdge.line(allCutPoints)
173  << endl;
174  }
175  }
176  }
177  else if (thisEdge.mag(allCutPoints) < SMALL)
178  {
179  // Degenerate length
180  // - eg, end snapping was disabled or somehow failed.
181 
182  // Don't normally emit warnings, since these also arise for
183  // manifold connections. For example,
184  //
185  // e1| /e2
186  // | /
187  // |/
188  // ----.---- plane
189  //
190  // The plane is correctly pierced at the '.' by both edge-1
191  // and edge-2, which belong to the same originating face.
192 
193  // Filter/merge away the extraneous points later.
194  if (warnDegenerate_ > 0)
195  {
196  --warnDegenerate_;
198  << "Degenerate edge between faces " << twoFaces
199  << " on 1st/2nd surface with points "
200  << thisEdge.line(allCutPoints)
201  << endl;
202  }
203  else if (debug & 4)
204  {
205  Pout<< "degenerate edge face-pair " << twoFaces << " "
206  << thisEdge[0] << " point " << allCutPoints[thisEdge[0]]
207  << endl;
208  }
209 
210  // This is a failed edge - undo this second interaction
211  thisEdge.erase(cutPointId);
212  }
213  else
214  {
215  // This is a new edge.
216  const label edgeId = allCutEdges.size();
217 
218  if (facePairToEdgeId_.insert(twoFaces, edgeId))
219  {
220  // Record complete (line) intersection of two faces
221  thisEdge.sort();
222  edgeToId_.insert(thisEdge, edgeId);
223  allCutEdges.append(thisEdge);
224 
225  if (debug & 4)
226  {
227  Pout<< "create edge - faces " << twoFaces << " edge#"
228  << edgeId << " edge " << thisEdge
229  << " = " << thisEdge.line(allCutPoints)
230  << endl;
231  }
232  }
233  else
234  {
235  // Faces already had an intersection
236  // This should not fail, but for safety.
237  Info<<"WARN " << twoFaces
238  << " already intersected= " << thisEdge << endl;
239  thisEdge.erase(cutPointId);
240  }
241  }
242  }
243  else
244  {
245  // Duplicate point - usually zero-length edge from snapping
246  // - can discard this face/face interaction entirely
247  facePairToEdge_.erase(twoFaces);
248  }
249  }
250 }
251 
252 
253 // Classify cut of edge of surface1 with surface2:
254 // 1- point of edge hits point on surface2
255 // 2- edge pierces point on surface2
256 // 3- point of edge hits edge on surface2
257 // 4- edge pierces edge on surface2
258 // 5- point of edge hits face on surface2
259 // 6- edge pierces face on surface2
260 //
261 // Note that handling of 2 and 4 should be the same but with surface1 and
262 // surface2 reversed.
263 void Foam::surfaceIntersection::classifyHit
264 (
265  const triSurface& surf1,
266  const scalarField& surf1PointTol,
267  const triSurface& surf2,
268  const enum intersectionType cutFrom,
269  const label edgeI,
270  const pointIndexHit& pHit,
271 
272  DynamicList<point>& allCutPoints,
273  DynamicList<edge>& allCutEdges,
274  List<DynamicList<label>>& surfEdgeCuts
275 )
276 {
277  const edge& e1 = surf1.edges()[edgeI];
278 
279  const labelList& facesA = surf1.edgeFaces()[edgeI];
280 
281  // Label of face on surface2 edgeI intersected
282  label surf2Facei = pHit.index();
283 
284  // Classify point on surface2
285 
286  const triSurface::face_type& f2 = surf2.localFaces()[surf2Facei];
287  const pointField& surf1Pts = surf1.localPoints();
288  const pointField& surf2Pts = surf2.localPoints();
289 
290  label nearType, nearLabel;
291 
292  f2.nearestPointClassify(pHit.hitPoint(), surf2Pts, nearType, nearLabel);
293 
294  // Classify points on edge of surface1
295  const label edgeEnd =
296  classify
297  (
298  surf1PointTol[e1.start()],
299  surf1PointTol[e1.end()],
300  pHit.hitPoint(),
301  e1,
302  surf1Pts
303  );
304 
305  if (nearType == triPointRef::POINT)
306  {
307  if (edgeEnd >= 0)
308  {
309  // 1. Point hits point. Do nothing.
310  if (debug & 2)
311  {
312  Pout<< "hit-type[1] " << pHit.point() << " is surf1:"
313  << " end point of edge[" << edgeI << "] " << e1
314  << "==" << e1.line(surf1Pts)
315  << " surf2: vertex " << f2[nearLabel]
316  << " coord:" << surf2Pts[f2[nearLabel]]
317  << " - suppressed" << endl;
318  }
319  }
320  else
321  {
322  // 2. Edge hits point. Cut edge with new point.
323  label cutPointId = -1;
324  const label nearVert = f2[nearLabel];
325 
326  // For self-intersection, we have tolerances for each point
327  // (surf2 is actually surf1) so we shift the hit to coincide
328  // identically.
329  if
330  (
331  cutFrom == surfaceIntersection::SELF
333  )
334  {
335  const point& nearPt = surf1Pts[nearVert];
336 
337  if
338  (
339  pHit.hitPoint().dist(nearPt)
340  < surf1PointTol[nearVert]
341  )
342  {
343  cutPointId = allCutPoints.size();
344 
345  if (snapToEnd_)
346  {
347  if (snappedEnds_.insert(nearVert, cutPointId))
348  {
349  // Initial snap
350  allCutPoints.append(nearPt);
351  }
352  else
353  {
354  // Already snapped this point.
355  cutPointId = snappedEnds_[nearVert];
356  }
357  }
358  else
359  {
360  allCutPoints.append(nearPt);
361  }
362  }
363  }
364 
365  if (debug & 2)
366  {
367  Pout<< "hit-type[2] " << pHit.point() << " is surf1:"
368  << " from edge[" << edgeI << "] " << e1
369  << " surf2: vertex " << f2[nearLabel]
370  << " coord:" << surf2Pts[f2[nearLabel]]
371  << " - "
372  << (cutPointId >= 0 ? "snapped" : "stored") << endl;
373  }
374 
375  if (cutPointId == -1)
376  {
377  cutPointId = allCutPoints.size();
378  allCutPoints.append(pHit.hitPoint());
379  }
380  surfEdgeCuts[edgeI].append(cutPointId);
381 
382  const labelList& facesB = surf2.pointFaces()[f2[nearLabel]];
383  forAll(facesB, faceBI)
384  {
385  storeIntersection
386  (
387  cutFrom,
388  facesA,
389  facesB[faceBI],
390  allCutPoints,
391  cutPointId,
392  allCutEdges
393  );
394  }
395  }
396  }
397  else if (nearType == triPointRef::EDGE)
398  {
399  if (edgeEnd >= 0)
400  {
401  // 3. Point hits edge.
402  // Normally do nothing on this side since the reverse
403  // (edge hits point) is handled by 2.
404  // However, if the surfaces are separated by a minor gap,
405  // the end-point of a tolerance-extended edge can intersect another
406  // edge without itself being intersected by an edge.
407 
408  const label edge2I = getEdge(surf2, surf2Facei, nearLabel);
409  const edge& e2 = surf2.edges()[edge2I];
410  const label nearVert = e1[edgeEnd];
411 
412  label cutPointId = -1;
413 
414  // Storage treatment
415  // =0: nothing/ignore
416  // >0: store point/edge-cut. Attempt to create new edge.
417  // <0: store point/edge-cut only
418  int handling = (allowEdgeHits_ ? 1 : 0);
419  if
420  (
421  allowEdgeHits_
422  &&
423  (
424  cutFrom == surfaceIntersection::SELF
426  )
427  )
428  {
429  // The edge-edge intersection is hashed as an 'edge' to
430  // exploit the commutative lookup.
431  // Ie, only do the cut once
432  const edge intersect(edgeI, edge2I);
433 
434  if (e2.found(nearVert))
435  {
436  // Actually the same as #1 above, but missed due to
437  // tolerancing
438  handling = 0; // suppress
439  }
440  else if (edgeEdgeIntersection_.insert(intersect))
441  {
442  const point& nearPt = surf1Pts[nearVert];
443 
444  if
445  (
446  pHit.hitPoint().dist(nearPt)
447  < surf1PointTol[nearVert]
448  )
449  {
450  cutPointId = allCutPoints.size();
451 
452  if (snapToEnd_)
453  {
454  if (snappedEnds_.insert(nearVert, cutPointId))
455  {
456  // Initial snap
457  allCutPoints.append(nearPt);
458  }
459  else
460  {
461  // Already snapped this point.
462  cutPointId = snappedEnds_[nearVert];
463  handling = 2; // cached
464  }
465  }
466  else
467  {
468  allCutPoints.append(nearPt);
469  }
470  }
471  }
472  else
473  {
474  handling = 0; // ignore - already did this interaction
475  }
476  }
477 
478  if (debug & 2)
479  {
480  Pout<< "hit-type[3] " << pHit.point() << " is surf1:"
481  << " end point of edge[" << edgeI << "] " << e1
482  << "==" << e1.line(surf1Pts)
483  << " surf2: edge[" << edge2I << "] " << e2
484  << " coords:" << e2.line(surf2Pts)
485  << " - "
486  << (
487  handling > 1
488  ? "cached" : handling
489  ? "stored" : "suppressed"
490  ) << endl;
491  }
492 
493  if (handling)
494  {
495  if (cutPointId == -1)
496  {
497  cutPointId = allCutPoints.size();
498  allCutPoints.append(pHit.hitPoint());
499  }
500  surfEdgeCuts[edgeI].append(cutPointId);
501  }
502 
503  if (handling > 0)
504  {
505  const labelList& facesB = surf2.edgeFaces()[edge2I];
506  forAll(facesB, faceBI)
507  {
508  storeIntersection
509  (
510  cutFrom,
511  facesA,
512  facesB[faceBI],
513  allCutPoints,
514  cutPointId,
515  allCutEdges
516  );
517  }
518  }
519  }
520  else
521  {
522  // 4. Edge hits edge.
523 
524  // Cut edge with new point (creates duplicates when
525  // doing the surf2 with surf1 intersection but these
526  // are merged later on)
527 
528  // edge hits all faces on surf2 connected to the edge
529  //
530  // The edge-edge intersection is symmetric, store only once.
531  // - When intersecting two surfaces, note which edges are cut each
532  // time, but only create an edge from the first pass.
533  // - For self-intersection, it is slightly trickier if we don't
534  // want too many duplicate points.
535 
536  const label edge2I = getEdge(surf2, surf2Facei, nearLabel);
537  const edge& e2 = surf2.edges()[edge2I];
538  label cutPointId = -1;
539 
540  // Storage treatment
541  // =0: nothing/ignore
542  // >0: store point/edge-cut. Attempt to create new edge.
543  // <0: store point/edge-cut only
544  int handling = 0;
545  switch (cutFrom)
546  {
548  {
549  handling = 1;
550  break;
551  }
553  {
554  handling = -1;
555  break;
556  }
559  {
560  // The edge-edge intersection is hashed as an 'edge' to
561  // exploit the commutative lookup.
562  // Ie, only do the cut once
563  const edge intersect(edgeI, edge2I);
564 
565  if (edgeEdgeIntersection_.insert(intersect))
566  {
567  handling = 1;
568  forAll(e1, edgepti)
569  {
570  const label endId = e1[edgepti];
571  const point& nearPt = surf1Pts[endId];
572 
573  if
574  (
575  pHit.hitPoint().dist(nearPt)
576  < surf1PointTol[endId]
577  )
578  {
579  cutPointId = allCutPoints.size();
580 
581  if (snapToEnd_)
582  {
583  if (snappedEnds_.insert(endId, cutPointId))
584  {
585  // First time with this end-point
586  allCutPoints.append(nearPt);
587  }
588  else
589  {
590  // Already seen this end point
591  cutPointId = snappedEnds_[endId];
592  handling = 2; // cached
593  }
594  }
595  else
596  {
597  allCutPoints.append(nearPt);
598  }
599 
600  break;
601  }
602  }
603  }
604 
605  break;
606  }
607 
609  return;
610  break;
611  }
612 
613  if (debug & 2)
614  {
615  Pout<< "hit-type[4] " << pHit.point() << " is surf1:"
616  << " from edge[" << edgeI << "] " << e1
617  << "==" << e1.line(surf1Pts)
618  << " surf2: edge[" << edge2I << "] " << e2
619  << " coords:" << e2.line(surf2Pts)
620  << " - "
621  << (
622  handling < 0
623  ? "cut-point" : handling
624  ? "stored" : "suppressed"
625  )
626  << endl;
627  }
628 
629  if (handling)
630  {
631  if (cutPointId == -1)
632  {
633  cutPointId = allCutPoints.size();
634  allCutPoints.append(pHit.hitPoint());
635  }
636  surfEdgeCuts[edgeI].append(cutPointId);
637  }
638 
639  if (handling)
640  {
641  const vector eVec = e1.unitVec(surf1Pts);
642 
643  const labelList& facesB = surf2.edgeFaces()[edge2I];
644  forAll(facesB, faceBI)
645  {
646  // Intersecting edge should be non-coplanar with face
647  if
648  (
649  mag((surf2.faceNormals()[facesB[faceBI]] & eVec))
650  > 0.01
651  )
652  {
653  storeIntersection
654  (
655  cutFrom,
656  facesA,
657  facesB[faceBI],
658  allCutPoints,
659  cutPointId,
660  allCutEdges
661  );
662  }
663  }
664  }
665  }
666  }
667  else
668  {
669  if (edgeEnd >= 0)
670  {
671  // 5. Point hits face. Do what? Introduce
672  // point & triangulation in face?
673 
674  // Look exactly at what side (of surf2) edge is. Leave out ones on
675  // inside of surf2 (i.e. on opposite side of normal)
676 
677  // Vertex on/near surf2; vertex away from surf2
678  // otherVert on outside of surf2
679  const label nearVert = (edgeEnd == 0 ? e1.start() : e1.end());
680  const label otherVert = (edgeEnd == 0 ? e1.end() : e1.start());
681 
682  const point& nearPt = surf1Pts[nearVert];
683  const point& otherPt = surf1Pts[otherVert];
684 
685  const vector eVec = otherPt - nearPt;
686 
687  if ((surf2.faceNormals()[surf2Facei] & eVec) > 0)
688  {
689  // map to nearVert
690  // Reclassify as normal edge-face pierce (see below)
691  bool cached = false;
692 
693  label cutPointId = allCutPoints.size();
694  if (snapToEnd_)
695  {
696  if (snappedEnds_.insert(nearVert, cutPointId))
697  {
698  // First time with this end-point
699  allCutPoints.append(nearPt);
700  }
701  else
702  {
703  // Already seen this end point
704  cutPointId = snappedEnds_[nearVert];
705  cached = true;
706  }
707  }
708  else
709  {
710  allCutPoints.append(nearPt);
711  }
712 
713  surfEdgeCuts[edgeI].append(cutPointId);
714 
715  if (debug & 2)
716  {
717  Pout<< "hit-type[5] " << pHit.point()
718  << " shifted to " << nearPt
719  << " from edge[" << edgeI << "] " << e1
720  << "==" << e1.line(surf1Pts)
721  << " hits surf2 face[" << surf2Facei << "]"
722  << " - "
723  << (cached ? "cached" : "stored") << endl;
724  }
725 
726  // edge hits single face only
727  storeIntersection
728  (
729  cutFrom,
730  facesA,
731  surf2Facei,
732  allCutPoints,
733  cutPointId,
734  allCutEdges
735  );
736  }
737  else
738  {
739  if (debug & 2)
740  {
741  Pout<< "hit-type[5] " << pHit.point()
742  << " from edge[" << edgeI << "] " << e1
743  << " hits inside of surf2 face[" << surf2Facei << "]"
744  << " - discarded" << endl;
745  }
746  }
747  }
748  else
749  {
750  // 6. Edge pierces face. 'Normal' situation.
751  if (debug & 2)
752  {
753  Pout<< "hit-type[6] " << pHit.point()
754  << " from edge[" << edgeI << "] " << e1
755  << "==" << e1.line(surf1Pts)
756  << " hits surf2 face[" << surf2Facei << "]"
757  << " - stored" << endl;
758  }
759 
760  const label cutPointId = allCutPoints.size();
761  allCutPoints.append(pHit.hitPoint());
762  surfEdgeCuts[edgeI].append(cutPointId);
763 
764  // edge hits single face only
765  storeIntersection
766  (
767  cutFrom,
768  facesA,
769  surf2Facei,
770  allCutPoints,
771  cutPointId,
772  allCutEdges
773  );
774  }
775  }
776 }
777 
778 
779 // Cut all edges of surf1 with surf2. Sets
780 // - cutPoints : coordinates of cutPoints
781 // - cutEdges : newly created edges between cutPoints
782 // - facePairToVertex : hash from face1I and face2I to edge
783 // - facePairToEdgeId : hash from face1I and face2I to index in cutEdge
784 // - surfEdgeCuts : gives for each edge the cutPoints
785 // (in order from start to end)
786 //
787 void Foam::surfaceIntersection::doCutEdges
788 (
789  const triSurface& surf1,
790  const triSurfaceSearch& querySurf2,
791  const enum intersectionType cutFrom,
792 
793  DynamicList<point>& allCutPoints,
794  DynamicList<edge>& allCutEdges,
795  List<DynamicList<label>>& surfEdgeCuts
796 )
797 {
798  const scalar oldTol = intersection::setPlanarTol(tolerance_);
799 
800  const pointField& surf1Pts = surf1.localPoints();
801 
802  // Calculate local (to point) tolerance based on min edge length.
803  scalarField surf1PointTol(surf1Pts.size());
804 
805  forAll(surf1PointTol, pointi)
806  {
807  surf1PointTol[pointi] = tolerance_ * minEdgeLen(surf1, pointi);
808  }
809 
810  const indexedOctree<treeDataPrimitivePatch<triSurface>>& searchTree
811  = querySurf2.tree();
812 
813  if
814  (
815  cutFrom == surfaceIntersection::SELF
817  )
818  {
819  // An edge may intersect multiple faces
820  // - mask out faces that have already been hit before trying again
821  // - never intersect with faces attached to the edge itself
822  DynamicList<label> maskFaces(32);
823 
824  // Optionally prevent intersection within a single region.
825  // Like self-intersect, but only if regions are different
826  bitSet maskRegions(32);
827 
828  treeDataTriSurface::findAllIntersectOp
829  allIntersectOp(searchTree, maskFaces);
830 
831  forAll(surf1.edges(), edgeI)
832  {
833  const edge& e = surf1.edges()[edgeI];
834  const vector edgeVec = e.vec(surf1Pts);
835 
836  // Extend start/end by 1/2 tolerance - ensures cleaner cutting
837  const point ptStart =
838  surf1Pts[e.start()] - 0.5*surf1PointTol[e.start()]*edgeVec;
839  const point ptEnd =
840  surf1Pts[e.end()] + 0.5*surf1PointTol[e.end()]*edgeVec;
841 
842  maskRegions.clear();
843  if (cutFrom == surfaceIntersection::SELF_REGION)
844  {
845  for (auto& facei : surf1.edgeFaces()[edgeI])
846  {
847  maskRegions.set(surf1[facei].region());
848  }
849  }
850 
851  // Never intersect with faces attached directly to the edge itself,
852  // nor with faces attached to its end points. This mask contains
853  // some duplicates, but filtering them out is less efficient.
854  maskFaces = surf1.pointFaces()[e.start()];
855  maskFaces.append(surf1.pointFaces()[e.end()]);
856 
857  while (true)
858  {
859  pointIndexHit pHit = searchTree.findLine
860  (
861  ptStart,
862  ptEnd,
863  allIntersectOp
864  );
865 
866  if (!pHit.hit())
867  {
868  break;
869  }
870 
871  maskFaces.append(pHit.index());
872 
873  if (maskRegions.test(surf1[pHit.index()].region()))
874  {
875  continue;
876  }
877 
878  classifyHit
879  (
880  surf1,
881  surf1PointTol,
882  surf1,
883  cutFrom,
884  edgeI,
885  pHit,
886 
887  allCutPoints,
888  allCutEdges,
889  surfEdgeCuts
890  );
891  }
892  }
893  }
894  else
895  {
896  const triSurface& surf2 = querySurf2.surface();
897 
898  forAll(surf1.edges(), edgeI)
899  {
900  const edge& e = surf1.edges()[edgeI];
901  const vector edgeVec = e.vec(surf1Pts);
902 
903  const point tolVec = intersection::planarTol()*(edgeVec);
904  const scalar tolDim = mag(tolVec);
905 
906  // Extend start/end by 1/2 tolerance - ensures cleaner cutting
907  point ptStart =
908  surf1Pts[e.start()] - 0.5*surf1PointTol[e.start()]*edgeVec;
909  const point ptEnd =
910  surf1Pts[e.end()] + 0.5*surf1PointTol[e.end()]*edgeVec;
911 
912  bool doTrack = false;
913  do
914  {
915  pointIndexHit pHit = searchTree.findLine(ptStart, ptEnd);
916 
917  if (!pHit.hit())
918  {
919  break;
920  }
921 
922  classifyHit
923  (
924  surf1,
925  surf1PointTol,
926  surf2,
927  cutFrom,
928  edgeI,
929  pHit,
930 
931  allCutPoints,
932  allCutEdges,
933  surfEdgeCuts
934  );
935 
936  if (tolerance_ > 0)
937  {
938  if (pHit.hitPoint().dist(ptEnd) < tolDim)
939  {
940  // Near the end => done
941  doTrack = false;
942  }
943  else
944  {
945  // Continue tracking a bit further on
946  ptStart = pHit.hitPoint() + tolVec;
947  doTrack = true;
948  }
949  }
950  }
951  while (doTrack); // execute at least once
952  }
953  }
954  if (debug & 2)
955  {
956  Pout<< endl;
957  }
958 
959  // These temporaries are now unneeded:
960  edgeEdgeIntersection_.clear();
961  snappedEnds_.clear();
962 
964 }
965 
966 
967 void Foam::surfaceIntersection::joinDisconnected
968 (
969  DynamicList<edge>& allCutEdges
970 )
971 {
972  // This simple heuristic seems to work just as well (or better) than
973  // more complicated schemes
974  //
975  // For any face/face intersection that only appears once,
976  // consider which other faces/points are involved and connect between
977  // those points.
978  // Just do a simple connect-the-dots?
979 
980  Pair<Map<labelPairHashSet>> missedFacePoint;
981 
982  // Stage 1:
983  // - Extract "faceId -> (faceId, pointId)"
984  // for all face/face pairs that only have one interaction
985  forAllConstIters(facePairToEdge_, iter)
986  {
987  const labelPair& twoFaces = iter.key();
988  const edge& e = iter.val();
989 
990  if (e.count() == 1)
991  {
992  // minVertex = -1 (unused), maxVertex = pointId
993  const label pointId = e.maxVertex();
994 
995  missedFacePoint[0](twoFaces[0]).insert
996  (
997  labelPair(twoFaces[1], pointId)
998  );
999 
1000  missedFacePoint[1](twoFaces[1]).insert
1001  (
1002  labelPair(twoFaces[0], pointId)
1003  );
1004  }
1005  }
1006 
1007 
1008  // Stage 2:
1009  // - anything with two cross-interactions could cause a new edge:
1010 
1011  edgeHashSet newEdges;
1012  forAll(missedFacePoint, sidei)
1013  {
1014  const auto& mapping = missedFacePoint[sidei];
1015 
1016  forAllConstIters(mapping, iter)
1017  {
1018  const auto& connect = iter.val();
1019 
1020  if (connect.size() == 2)
1021  {
1022  // exactly two face/face cross-interactions
1023 
1024  edge e;
1025  for (const auto& facePoint : connect)
1026  {
1027  e.insert(facePoint.second());
1028  }
1029  e.sort();
1030 
1031  // Only consider edges with two unique ends,
1032  // and do not introduce duplicates
1033  if (e.count() == 2 && !edgeToId_.found(e))
1034  {
1035  newEdges.insert(e);
1036  }
1037  }
1038  }
1039  }
1040 
1041  label edgeId = allCutEdges.size();
1042  edgeList newEdgesLst = newEdges.sortedToc();
1043  for (const auto& e : newEdgesLst)
1044  {
1045  // Record complete (line) intersection of two faces
1046  allCutEdges.append(e);
1047  edgeToId_.insert(e, edgeId);
1048  ++edgeId;
1049  }
1050 }
1051 
1052 
1053 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
1054 
1056 :
1057  tolerance_(1e-3),
1058  allowEdgeHits_(true),
1059  snapToEnd_(true),
1060  warnDegenerate_(0),
1061  cutPoints_(0),
1062  cutEdges_(0),
1063  facePairToEdge_(0),
1064  facePairToEdgeId_(0),
1065  surf1EdgeCuts_(0),
1066  surf2EdgeCuts_(0)
1067 {}
1068 
1069 
1071 (
1072  const triSurfaceSearch& query1,
1073  const triSurfaceSearch& query2,
1074  const dictionary& dict
1075 )
1076 :
1077  tolerance_(1e-3),
1078  allowEdgeHits_(true),
1079  snapToEnd_(true),
1080  warnDegenerate_(0),
1081  cutPoints_(0),
1082  cutEdges_(0),
1083  facePairToEdge_(2*max(query1.surface().size(), query2.surface().size())),
1084  facePairToEdgeId_(2*max(query1.surface().size(), query2.surface().size())),
1085  surf1EdgeCuts_(0),
1086  surf2EdgeCuts_(0)
1087 {
1088  setOptions(dict);
1089 
1090  const triSurface& surf1 = query1.surface();
1091  const triSurface& surf2 = query2.surface();
1092 
1093  //
1094  // Cut all edges of surf1 with surf2.
1095  //
1096  if (debug)
1097  {
1098  Pout<< "Cutting surf1 edges" << endl;
1099  }
1100 
1101 
1102  DynamicList<edge> allCutEdges(surf1.nEdges()/20);
1103  DynamicList<point> allCutPoints(surf1.nPoints()/20);
1104 
1105 
1106  // From edge to cut index on surface1
1107  List<DynamicList<label>> edgeCuts1(query1.surface().nEdges());
1108 
1109  // 1st surf (for labelPair order)
1110  doCutEdges
1111  (
1112  surf1,
1113  query2,
1115  allCutPoints,
1116  allCutEdges,
1117  edgeCuts1
1118  );
1119  // Transfer to straight labelListList
1120  transfer(edgeCuts1, surf1EdgeCuts_);
1121 
1122 
1123  //
1124  // Cut all edges of surf2 with surf1.
1125  //
1126  if (debug)
1127  {
1128  Pout<< "Cutting surf2 edges" << endl;
1129  }
1130 
1131  // From edge to cut index
1132  List<DynamicList<label>> edgeCuts2(query2.surface().nEdges());
1133 
1134  // 2nd surf (for labelPair order)
1135  doCutEdges
1136  (
1137  surf2,
1138  query1,
1140  allCutPoints,
1141  allCutEdges,
1142  edgeCuts2
1143  );
1144 
1145  // join disconnected intersection points
1146  joinDisconnected(allCutEdges);
1147 
1148  // Transfer to straight label(List)List
1149  transfer(edgeCuts2, surf2EdgeCuts_);
1150  cutEdges_.transfer(allCutEdges);
1151  cutPoints_.transfer(allCutPoints);
1152 
1153  if (debug)
1154  {
1155  Pout<< "surfaceIntersection : Intersection generated:"
1156  << endl
1157  << " points:" << cutPoints_.size() << endl
1158  << " edges :" << cutEdges_.size() << endl;
1159 
1160  Pout<< "surfaceIntersection : Writing intersection to intEdges.obj"
1161  << endl;
1162 
1163  OBJstream("intEdges.obj").write(cutEdges_, cutPoints_);
1164 
1165  // Dump all cut edges to files
1166  Pout<< "Dumping cut edges of surface1 to surf1EdgeCuts.obj" << endl;
1167  OFstream edge1Stream("surf1EdgeCuts.obj");
1168  writeIntersectedEdges(surf1, surf1EdgeCuts_, edge1Stream);
1169 
1170  Pout<< "Dumping cut edges of surface2 to surf2EdgeCuts.obj" << endl;
1171  OFstream edge2Stream("surf2EdgeCuts.obj");
1172  writeIntersectedEdges(surf2, surf2EdgeCuts_, edge2Stream);
1173  }
1174 
1175  // Temporaries
1176  facePairToEdge_.clear();
1178  // Cleanup any duplicate cuts?
1179  // mergeEdges();
1180 }
1181 
1182 
1184 (
1185  const triSurfaceSearch& query1,
1186  const dictionary& dict
1187 )
1188 :
1189  tolerance_(1e-3),
1190  allowEdgeHits_(true),
1191  snapToEnd_(true),
1192  warnDegenerate_(0),
1193  cutPoints_(0),
1194  cutEdges_(0),
1195  facePairToEdge_(2*query1.surface().size()),
1196  facePairToEdgeId_(2*query1.surface().size()),
1197  surf1EdgeCuts_(0),
1198  surf2EdgeCuts_(0)
1199 {
1200  setOptions(dict);
1201 
1203  (
1204  "intersectionMethod",
1205  dict,
1206  intersectionType::SELF
1207  );
1208 
1209  if (cutFrom == intersectionType::NONE)
1210  {
1211  if (debug)
1212  {
1213  Pout<< "Skipping self-intersection (selected: none)" << endl;
1214  }
1215 
1216  // Temporaries
1217  facePairToEdge_.clear();
1218  facePairToEdgeId_.clear();
1219 
1220  return;
1221  }
1222 
1223  const triSurface& surf1 = query1.surface();
1224 
1225  //
1226  // Cut all edges of surf1 with surf1 itself.
1227  //
1228  if (debug)
1229  {
1230  Pout<< "Cutting surf1 edges" << endl;
1231  }
1232 
1233  DynamicList<edge> allCutEdges;
1234  DynamicList<point> allCutPoints;
1235 
1236  // From edge to cut index on surface1
1237  List<DynamicList<label>> edgeCuts1(query1.surface().nEdges());
1238 
1239  // self-intersection
1240  doCutEdges
1241  (
1242  surf1,
1243  query1,
1244  cutFrom,
1245  allCutPoints,
1246  allCutEdges,
1247  edgeCuts1
1248  );
1249 
1250  // join disconnected intersection points
1251  joinDisconnected(allCutEdges);
1252 
1253  // Transfer to straight label(List)List
1254  transfer(edgeCuts1, surf1EdgeCuts_);
1255  cutEdges_.transfer(allCutEdges);
1256  cutPoints_.transfer(allCutPoints);
1257 
1258  // Short-circuit
1259  if (cutPoints_.empty() && cutEdges_.empty())
1260  {
1261  if (debug)
1262  {
1263  Pout<< "Empty intersection" << endl;
1264  }
1265  return;
1266  }
1267 
1268  if (debug)
1269  {
1270  Pout<< "surfaceIntersection : Intersection generated and compressed:"
1271  << endl
1272  << " points:" << cutPoints_.size() << endl
1273  << " edges :" << cutEdges_.size() << endl;
1274 
1275 
1276  Pout<< "surfaceIntersection : Writing intersection to intEdges.obj"
1277  << endl;
1278 
1279  OBJstream("intEdges.obj").write(cutEdges_, cutPoints_);
1280 
1281  // Dump all cut edges to files
1282  Pout<< "Dumping cut edges of surface1 to surf1EdgeCuts.obj" << endl;
1283  OFstream edge1Stream("surf1EdgeCuts.obj");
1284  writeIntersectedEdges(surf1, surf1EdgeCuts_, edge1Stream);
1285  }
1286 
1287  // Temporaries
1288  facePairToEdge_.clear();
1290  // // Cleanup any duplicate cuts?
1291  // mergeEdges();
1292 }
1293 
1294 
1296 (
1297  const triSurface& surf1,
1298  const edgeIntersections& intersections1,
1299  const triSurface& surf2,
1300  const edgeIntersections& intersections2
1301 )
1302 :
1303  tolerance_(1e-3),
1304  allowEdgeHits_(true),
1305  snapToEnd_(true),
1306  warnDegenerate_(0),
1307  cutPoints_(0),
1308  cutEdges_(0),
1309  facePairToEdge_(2*max(surf1.size(), surf2.size())),
1310  facePairToEdgeId_(2*max(surf1.size(), surf2.size())),
1311  surf1EdgeCuts_(0),
1312  surf2EdgeCuts_(0)
1313 {
1314 
1315  // All intersection Pout (so for both surfaces)
1316  DynamicList<edge> allCutEdges((surf1.nEdges() + surf2.nEdges())/20);
1317  DynamicList<point> allCutPoints((surf1.nPoints() + surf2.nPoints())/20);
1318 
1319 
1320  // Cut all edges of surf1 with surf2
1321  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1322 
1323  if (debug)
1324  {
1325  Pout<< "Storing surf1 intersections" << endl;
1326  }
1327 
1328  {
1329  // From edge to cut index on surface1
1330  List<DynamicList<label>> edgeCuts1(surf1.nEdges());
1331 
1332  forAll(intersections1, edgeI)
1333  {
1334  const List<pointIndexHit>& intersections = intersections1[edgeI];
1335 
1336  forAll(intersections, i)
1337  {
1338  // edgeI intersects surf2. Store point.
1339  const pointIndexHit& pHit = intersections[i];
1340  const label cutPointId = allCutPoints.size();
1341 
1342  allCutPoints.append(pHit.hitPoint());
1343  edgeCuts1[edgeI].append(cutPointId);
1344 
1345  storeIntersection
1346  (
1348  surf1.edgeFaces()[edgeI],
1349  pHit.index(),
1350  allCutPoints,
1351  cutPointId,
1352  allCutEdges
1353  );
1354  }
1355  }
1356 
1357  // Transfer to straight labelListList
1358  transfer(edgeCuts1, surf1EdgeCuts_);
1359  }
1360 
1361 
1362  // Cut all edges of surf2 with surf1
1363  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1364 
1365  if (debug)
1366  {
1367  Pout<< "Storing surf2 intersections" << endl;
1368  }
1369 
1370  {
1371  // From edge to cut index on surface2
1372  List<DynamicList<label>> edgeCuts2(surf2.nEdges());
1373 
1374  forAll(intersections2, edgeI)
1375  {
1376  const List<pointIndexHit>& intersections = intersections2[edgeI];
1377 
1378  forAll(intersections, i)
1379  {
1380  // edgeI intersects surf1. Store point.
1381  const pointIndexHit& pHit = intersections[i];
1382  const label cutPointId = allCutPoints.size();
1383 
1384  allCutPoints.append(pHit.hitPoint());
1385  edgeCuts2[edgeI].append(cutPointId);
1386 
1387  storeIntersection
1388  (
1390  surf2.edgeFaces()[edgeI],
1391  pHit.index(),
1392  allCutPoints,
1393  cutPointId,
1394  allCutEdges
1395  );
1396  }
1397  }
1398 
1399  // Transfer to surf2EdgeCuts_ (straight labelListList)
1400  transfer(edgeCuts2, surf2EdgeCuts_);
1401  }
1402 
1403 
1404  // Transfer to straight label(List)List
1405  cutEdges_.transfer(allCutEdges);
1406  cutPoints_.transfer(allCutPoints);
1407 
1408 
1409  if (debug)
1410  {
1411  Pout<< "surfaceIntersection : Intersection generated:"
1412  << endl
1413  << " points:" << cutPoints_.size() << endl
1414  << " edges :" << cutEdges_.size() << endl;
1415 
1416  Pout<< "surfaceIntersection : Writing intersection to intEdges.obj"
1417  << endl;
1418 
1419  OBJstream("intEdges.obj").write(cutEdges_, cutPoints_);
1420 
1421  // Dump all cut edges to files
1422  Pout<< "Dumping cut edges of surface1 to surf1EdgeCuts.obj" << endl;
1423  OFstream edge1Stream("surf1EdgeCuts.obj");
1424  writeIntersectedEdges(surf1, surf1EdgeCuts_, edge1Stream);
1425 
1426  Pout<< "Dumping cut edges of surface2 to surf2EdgeCuts.obj" << endl;
1427  OFstream edge2Stream("surf2EdgeCuts.obj");
1428  writeIntersectedEdges(surf2, surf2EdgeCuts_, edge2Stream);
1429  }
1430 
1431  // Temporaries
1432  facePairToEdge_.clear();
1433 
1434  // // Cleanup any duplicate cuts?
1435  // mergeEdges();
1436 }
1437 
1438 
1439 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
1442 {
1443  return cutPoints_;
1444 }
1445 
1448 {
1449  return cutEdges_;
1450 }
1451 
1452 
1454 {
1455  return facePairToEdgeId_;
1456 }
1457 
1458 
1460 (
1461  const bool isFirstSurf
1462 ) const
1463 {
1464  if (isFirstSurf)
1465  {
1466  return surf1EdgeCuts_;
1467  }
1468  else
1469  {
1470  return surf2EdgeCuts_;
1471  }
1472 }
1473 
1476 {
1477  return surf1EdgeCuts_;
1478 }
1479 
1482 {
1483  return surf2EdgeCuts_;
1484 }
1485 
1486 
1487 void Foam::surfaceIntersection::mergePoints(const scalar mergeDist)
1488 {
1489  labelList pointMap;
1490 
1491  label nChanged = Foam::inplaceMergePoints
1492  (
1493  cutPoints_,
1494  mergeDist,
1495  false,
1496  pointMap
1497  );
1498 
1499  if (nChanged)
1500  {
1501  forAll(cutEdges_, edgei)
1502  {
1503  edge& e = cutEdges_[edgei];
1504 
1505  e[0] = pointMap[e[0]];
1506  e[1] = pointMap[e[1]];
1507  }
1508 
1509  forAll(surf1EdgeCuts_, edgei)
1510  {
1511  inplaceRenumber(pointMap, surf1EdgeCuts_[edgei]);
1512  inplaceUniqueSort(surf1EdgeCuts_[edgei]);
1513  }
1514  forAll(surf2EdgeCuts_, edgei)
1515  {
1516  inplaceRenumber(pointMap, surf2EdgeCuts_[edgei]);
1517  inplaceUniqueSort(surf2EdgeCuts_[edgei]);
1518  }
1519  }
1520 
1521  this->mergeEdges();
1522 }
1523 
1524 
1526 {
1527  edgeHashSet uniqEdges(2*cutEdges_.size());
1528 
1529  label nUniqEdges = 0;
1530  labelList edgeNumbering(cutEdges_.size(), -1);
1531 
1532  forAll(cutEdges_, edgeI)
1533  {
1534  const edge& e = cutEdges_[edgeI];
1535 
1536  // Remove degenerate and repeated edges
1537  // - reordering (e[0] < e[1]) is not really necessary
1538  if (e[0] != e[1] && uniqEdges.insert(e))
1539  {
1540  edgeNumbering[edgeI] = nUniqEdges;
1541  if (nUniqEdges != edgeI)
1542  {
1543  cutEdges_[nUniqEdges] = e;
1544  }
1545  cutEdges_[nUniqEdges].sort();
1546  ++nUniqEdges;
1547  }
1548  }
1549 
1550  // if (nUniqEdges < cutEdges_.size())
1551  // {
1552  // // Additional safety, in case the edge was replaced?
1553  // forAllIters(facePairToEdge_, iter)
1554  // {
1555  // iter.val() = edgeNumbering[iter.val()];
1556  // }
1557  // }
1558 
1559  cutEdges_.setSize(nUniqEdges); // truncate
1560 }
1561 
1562 
1563 // ************************************************************************* //
surfaceIntersection()
Construct null.
label nPoints() const
Number of points supporting patch faces.
dictionary dict
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:116
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
void transfer(List< T > &list)
Transfer the contents of the argument List into this list and annul the argument list.
Definition: List.C:326
A list of keyword definitions, which are a keyword followed by a number of values (eg...
Definition: dictionary.H:129
void append(const T &val)
Append an element at the end of the list.
Definition: List.H:517
List< edge > edgeList
List of edge.
Definition: edgeList.H:32
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:40
srcOptions insert("case", fileName(rootDirSource/caseDirSource))
bool empty() const noexcept
True if List is empty (ie, size() is zero)
Definition: UList.H:666
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:531
PointIndexHit< point > pointIndexHit
A PointIndexHit with a 3D point.
Definition: pointIndexHit.H:58
HashSet< edge, Hash< edge > > edgeHashSet
A HashSet with edge for its key. Hashing (and ==) on an edge is symmetric.
Definition: edgeHashes.H:48
const labelListList & surf2EdgeCuts() const
List of cut points on edges of surface2.
EnumType getOrDefault(const word &key, const dictionary &dict, const EnumType deflt, const bool warnOnly=false) const
Find the key in the dictionary and return the corresponding enumeration element based on its name...
Definition: Enum.C:173
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:421
Close to point.
Definition: triangle.H:240
static scalar setPlanarTol(const scalar t)
Set the planar tolerance, returning the previous value.
Definition: intersection.H:101
Helper class to search on triSurface.
static label getEdge(List< DynamicList< label >> &pe, DynamicList< edge > &es, const label pointi, const label nextPointi)
const edgeList & cutEdges() const
The list of created edges.
static scalar planarTol()
Return planar tolerance.
Definition: intersection.H:93
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:38
const dimensionedScalar e
Elementary charge.
Definition: createFields.H:11
Close to edge.
Definition: triangle.H:241
A 1D vector of objects of type <T> that resizes itself as necessary to accept the new objects...
Definition: DynamicList.H:51
An edge is a list of two vertex labels. This can correspond to a directed graph edge or an edge on a ...
Definition: edge.H:59
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
Self-intersection, region-wise only.
void clear()
Remove all entries from table.
Definition: HashTable.C:725
labelledTri face_type
The face type (same as the underlying PrimitivePatch)
Definition: triSurface.H:256
void mergeEdges()
Merge duplicate edges.
virtual bool write(const token &tok)=0
Write token to stream or otherwise handle it.
const labelListList & edgeFaces() const
Return edge-face addressing.
Vector< scalar > vector
Definition: vector.H:57
const labelListList & edgeCuts(const bool isFirstSurf) const
Access either surf1EdgeCuts (isFirstSurface = true) or.
void append(const T &val)
Copy append an element to the end of this list.
Definition: DynamicList.H:584
void inplaceUniqueSort(ListType &input)
Inplace sorting and removal of duplicates.
label inplaceMergePoints(PointList &points, const scalar mergeTol, const bool verbose, labelList &pointToUnique)
Inplace merge points, preserving the original point order. All points closer/equal mergeTol are to be...
A HashTable to objects of type <T> with a labelPair key. The hashing is based on labelPair (FixedList...
const wordList surface
Standard surface field types (scalar, vector, tensor, etc)
label nEdges() const
Number of edges in patch.
int debug
Static debugging option.
label facePoint(const int facei, const block &block, const label i, const label j)
Pair< label > labelPair
A pair of labels.
Definition: Pair.H:51
defineTypeNameAndDebug(combustionModel, 0)
Geometric merging of points. See below.
const pointField & cutPoints() const
The list of cut points.
const triSurface & surface() const
Return reference to the surface.
const labelPairLookup & facePairToEdgeId() const
Lookup of pairs of faces to created edges.
void inplaceRenumber(const labelUList &oldToNew, IntListType &input)
Inplace renumber the values (not the indices) of a list.
vector point
Point is a vector.
Definition: point.H:37
static constexpr direction size() noexcept
The number of elements in the VectorSpace = Ncmpts.
Definition: VectorSpace.H:206
const labelListList & surf1EdgeCuts() const
List of cut points on edges of surface1.
#define WarningInFunction
Report a warning using Foam::Warning.
Enum is a wrapper around a list of names/values that represent particular enumeration (or int) values...
Definition: error.H:64
void mergePoints(const scalar mergeDist)
Geometric merge points (points within mergeDist) prior to.
messageStream Info
Information stream (stdout output on master, null elsewhere)
intersectionType
Surface intersection types for classify, doCutEdges.
None = invalid (for input only)
List< label > labelList
A List of labels.
Definition: List.H:62
Triangulated surface description with patch information.
Definition: triSurface.H:71
Holder of intersections of edges of a surface with another surface. Optionally shuffles around points...
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
Namespace for OpenFOAM.
forAllConstIters(mixture.phases(), phase)
Definition: pEqn.H:28
static const Enum< intersectionType > selfIntersectionNames
The user-selectable self-intersection enumeration names.