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-------------------------------------------------------------------------------
11License
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
42namespace Foam
43{
45}
46
47const 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
61void 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
70void 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.
263void 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.hitPoint() << " 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 (
333 )
334 {
335 const point& nearPt = surf1Pts[nearVert];
336
337 if
338 (
339 mag(pHit.hitPoint() - 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.hitPoint() << " 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 (
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 mag(pHit.hitPoint() - 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.hitPoint() << " 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 mag(pHit.hitPoint() - 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.hitPoint() << " 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.hitPoint()
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.hitPoint()
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.hitPoint()
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//
787void 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 (
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();
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 (mag(pHit.hitPoint() - 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
967void 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();
1177
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,
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();
1289
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 * * * * * * * * * * * * * //
1440
1442{
1443 return cutPoints_;
1444}
1445
1446
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
1474
1476{
1477 return surf1EdgeCuts_;
1478}
1479
1480
1482{
1483 return surf2EdgeCuts_;
1484}
1485
1486
1487void 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// ************************************************************************* //
A 1D vector of objects of type <T> that resizes itself as necessary to accept the new objects.
Definition: DynamicList.H:72
void append(const T &val)
Copy append an element to the end of this list.
Definition: DynamicListI.H:503
Enum is a wrapper around a list of names/values that represent particular enumeration (or int) values...
Definition: Enum.H:61
EnumType getOrDefault(const word &key, const dictionary &dict, const EnumType deflt, const bool failsafe=false) const
Definition: Enum.C:179
bool insert(const Key &key)
Insert a new entry, not overwriting existing entries.
Definition: HashSet.H:191
void clear()
Clear all entries from table.
Definition: HashTable.C:678
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
Definition: List.H:77
void transfer(List< T > &list)
Definition: List.C:447
void setSize(const label n)
Alias for resize()
Definition: List.H:218
void append(const T &val)
Append an element at the end of the list.
Definition: ListI.H:175
OFstream that keeps track of vertices.
Definition: OBJstream.H:61
virtual Ostream & write(const char c)
Write character.
Definition: OBJstream.C:78
Output to file stream, using an OSstream.
Definition: OFstream.H:57
This class describes the interaction of (usually) a face and a point. It carries the info of a succes...
Definition: PointIndexHit.H:66
label index() const noexcept
Return the hit index.
const point_type & hitPoint() const
Return hit point. Fatal if not hit.
label nEdges() const
Number of edges in patch.
label nPoints() const
Number of points supporting patch faces.
const labelListList & edgeFaces() const
Return edge-face addressing.
bool empty() const noexcept
True if the UList is empty (ie, size() is zero)
Definition: UListI.H:427
iterator end() noexcept
Return an iterator to end traversing the UList.
Definition: UListI.H:350
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:114
static constexpr direction size() noexcept
The number of elements in the VectorSpace = Ncmpts.
Definition: VectorSpace.H:176
A list of keyword definitions, which are a keyword followed by a number of values (eg,...
Definition: dictionary.H:126
bool readIfPresent(const word &keyword, T &val, enum keyType::option matchOpt=keyType::REGEX) const
Holder of intersections of edges of a surface with another surface. Optionally shuffles around points...
An edge is a list of two point labels. The functionality it provides supports the discretisation on a...
Definition: edge.H:66
static scalar setPlanarTol(const scalar t)
Set the planar tolerance, returning the previous value.
Definition: intersection.H:94
static scalar planarTol()
Return planar tolerance.
Definition: intersection.H:88
int count() const noexcept
Return the current reference count.
Definition: refCount.H:74
Basic surface-surface intersection description. Constructed from two surfaces it creates a descriptio...
const edgeList & cutEdges() const
The list of created edges.
void mergeEdges()
Merge duplicate edges.
const labelListList & surf2EdgeCuts() const
List of cut points on edges of surface2.
const labelListList & edgeCuts(const bool isFirstSurf) const
Access either surf1EdgeCuts (isFirstSurface = true) or.
intersectionType
Surface intersection types for classify, doCutEdges.
@ SELF_REGION
Self-intersection, region-wise only.
@ NONE
None = invalid (for input only)
void mergePoints(const scalar mergeDist)
Geometric merge points (points within mergeDist) prior to.
surfaceIntersection()
Construct null.
const labelListList & surf1EdgeCuts() const
List of cut points on edges of surface1.
const labelPairLookup & facePairToEdgeId() const
Lookup of pairs of faces to created edges.
const pointField & cutPoints() const
The list of cut points.
static const Enum< intersectionType > selfIntersectionNames
The user-selectable self-intersection enumeration names.
Helper class to search on triSurface.
const triSurface & surface() const
Return reference to the surface.
Triangulated surface description with patch information.
Definition: triSurface.H:79
labelledTri face_type
The face type (same as the underlying PrimitivePatch)
Definition: triSurface.H:209
@ POINT
Close to point.
Definition: triangle.H:96
@ EDGE
Close to edge.
Definition: triangle.H:97
A Vector of values with scalar precision, where scalar is float/double depending on the compilation f...
#define defineTypeNameAndDebug(Type, DebugSwitch)
Define the typeName and debug information.
Definition: className.H:121
A HashTable to objects of type <T> with a labelPair key. The hashing is based on labelPair (FixedList...
Geometric merging of points. See below.
#define WarningInFunction
Report a warning using Foam::Warning.
Namespace for OpenFOAM.
Pair< label > labelPair
A pair of labels.
Definition: Pair.H:57
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:47
void inplaceRenumber(const labelUList &oldToNew, IntListType &input)
Inplace renumber the values (not the indices) of a list.
List< label > labelList
A List of labels.
Definition: List.H:66
PointIndexHit< point > pointIndexHit
A PointIndexHit for 3D points.
Definition: pointIndexHit.H:46
label facePoint(const int facei, const block &block, const label i, const label j)
messageStream Info
Information stream (stdout output on master, null elsewhere)
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:44
vector point
Point is a vector.
Definition: point.H:43
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:372
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
void inplaceUniqueSort(ListType &input)
Inplace sorting and removal of duplicates.
HashSet< edge, Hash< edge > > edgeHashSet
A HashSet with edge for its key.
Definition: edgeHashes.H:51
List< edge > edgeList
A List of edges.
Definition: edgeList.H:63
label inplaceMergePoints(PointList &points, const scalar mergeTol, const bool verbose, labelList &pointToUnique)
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
srcOptions insert("case", fileName(rootDirSource/caseDirSource))
dictionary dict
volScalarField & e
Definition: createFields.H:11
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:333
#define forAllConstIters(container, iter)
Iterate across all elements of the container object with const access.
Definition: stdFoam.H:278