triIntersect.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) 2021-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 "triIntersect.H"
27 #include "boundBox.H"
28 #include "cubicEqn.H"
29 #include "unitConversion.H"
30 #include "OFstream.H"
31 #include "tensor2D.H"
32 #include "vtkWritePolyData.H"
33 
34 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
35 
36 namespace Foam
37 {
38 namespace triIntersect
39 {
40 
41 
42 //- The maximum dot product between a source point normal and a target plane
43 // considered to be a valid, forward projection
44 const scalar maxDot = - cos(degToRad(80));
45 
46 
47 //- Divide two numbers and protect the result from overflowing
48 scalar protectedDivide(const scalar a, const scalar b)
49 {
50  return mag(a)/vGreat < mag(b) ? a/b : sign(a)*sign(b)*vGreat;
51 }
52 
53 
54 //- Divide two numbers, protect the result from overflowing, and clip the
55 // result between 0 and 1
56 scalar protectedDivideAndClip01(const scalar a, const scalar b)
57 {
58  const scalar bStar = max(mag(a), mag(b));
59 
60  return bStar == 0 ? 0 : max(a*sign(b), 0)/bStar;
61 }
62 
63 
64 //- Print 3x3 FixedListLists on one line
65 template <class Type>
67 {
68  os << token::BEGIN_LIST;
69  forAll(l, i)
70  {
71  if (i) os << token::SPACE;
72  os << l[i];
73  }
74  os << token::END_LIST;
75  return os;
76 }
77 
78 
79 //- Clip the given vector between values of 0 and 1, and also clip one minus
80 // its component sum. Clipping is applied to groups of components. It is done
81 // by moving the value linearly towards the value where all components in the
82 // group, and one minus their sum, share the same value.
84 {
85  vector y(x);
86 
87  for (label group = 0; group < groups[findMax(groups)] + 1; ++ group)
88  {
89  label n = 1;
90  forAll(x, i)
91  {
92  if (groups[i] == group)
93  {
94  n ++;
95  }
96  }
97 
98  if (n == 1)
99  {
100  continue;
101  }
102  else if (n == 2)
103  {
104  forAll(x, i)
105  {
106  if (groups[i] == group)
107  {
108  y[i] = min(max(x[i], 0), 1);
109  }
110  }
111  }
112  else
113  {
114  scalar xn = 1;
115  forAll(x, i)
116  {
117  if (groups[i] == group)
118  {
119  xn -= x[i];
120  }
121  }
122 
123  scalar phi = 0;
124  forAll(x, i)
125  {
126  if (groups[i] == group)
127  {
128  if (x[i] < 0)
129  {
130  phi = max(phi, n*x[i]/(n*x[i] - 1));
131  }
132  }
133  }
134  if (xn < 0)
135  {
136  phi = max(phi, n*xn/(n*xn - 1));
137  }
138 
139  forAll(x, i)
140  {
141  if (groups[i] == group)
142  {
143  y[i] = min(max((1 - phi)*x[i] + phi/n, 0), 1);
144  }
145  }
146  }
147  }
148 
149  return y;
150 }
151 
152 
153 //- Solve a projection equation given a value of the t variable
155 (
156  const vector& C,
157  const vector& Ct,
158  const vector& Cu,
159  const vector& Cv,
160  const vector& Ctu,
161  const vector& Ctv,
162  const FixedList<label, 3> groups,
163  const scalar t
164 )
165 {
166  // Solve a least squares problem for u and v
167  const vector CCtt = C + Ct*t;
168  const vector CuCtut = Cu + Ctu*t;
169  const vector CvCtvt = Cv + Ctv*t;
170 
171  const tensor2D A
172  (
173  CuCtut & CuCtut, CuCtut & CvCtvt,
174  CvCtvt & CuCtut, CvCtvt & CvCtvt
175  );
176  const vector2D B
177  (
178  - CuCtut & CCtt,
179  - CvCtvt & CCtt
180  );
181 
182  const scalar detA = det(A);
183  const vector2D detAuv = cof(A) & B;
184 
185  const vector tuv
186  (
187  t,
188  protectedDivide(detAuv.x(), detA),
189  protectedDivide(detAuv.y(), detA)
190  );
191 
192  // Apply group clipping
193  return clipped01(tuv, groups);
194 }
195 
196 
197 //- Solve a projection equation
199 (
200  const vector& C,
201  const vector& Ct,
202  const vector& Cu,
203  const vector& Cv,
204  const vector& Ctu,
205  const vector& Ctv,
206  const FixedList<label, 3> groups
207 )
208 {
209  // Solve the cubic projection equation for t
210  const Roots<3> tRoots =
211  cubicEqn
212  (
213  (Ct ^ Ctu) & Ctv,
214  ((C ^ Ctu) & Ctv) + ((Ct ^ Cu) & Ctv) + ((Ct ^ Ctu) & Cv),
215  ((C ^ Cu) & Ctv) + ((C ^ Ctu) & Cv) + ((Ct ^ Cu) & Cv),
216  (C ^ Cu) & Cv
217  ).roots();
218 
219  // Solve the remaining problem for u and v
220  label nTuvs = 0;
222  forAll(tRoots, tRooti)
223  {
224  if (tRoots.type(tRooti) != rootType::real) continue;
225 
226  if (mag(tRoots[tRooti]) > great) continue;
227 
228  const vector tuv =
230  (
231  C,
232  Ct,
233  Cu,
234  Cv,
235  Ctu,
236  Ctv,
237  {-1, -1, -1},
238  tRoots[tRooti]
239  );
240 
241  if (cmptMax(cmptMag(tuv)) > rootVGreat) continue;
242 
243  tuvs[nTuvs ++] = tuv;
244  }
245 
246  // Apply clipping
247  FixedList<scalar, 3> tuvClippage(NaN);
248  for (label i = 0; i < nTuvs; ++ i)
249  {
250  const vector tuvOld = tuvs[i];
251  tuvs[i] = clipped01(tuvs[i], groups);
252  tuvClippage[i] = cmptSum(cmptMag(tuvs[i] - tuvOld));
253  }
254 
255  // Sort so the least clipped roots come first
256  for (label i = 0; i < nTuvs - 1; ++ i)
257  {
258  for (label j = 0; j < nTuvs - 1; ++ j)
259  {
260  if (tuvClippage[j] > tuvClippage[j + 1])
261  {
262  Swap(tuvs[j], tuvs[j + 1]);
263  Swap(tuvClippage[j], tuvClippage[j + 1]);
264  }
265  }
266  }
267 
268  // Analyse each clipped solution value versus estimated error. If the
269  // value is small relative to the error, then return success.
270  for (label i = 0; i < nTuvs; ++ i)
271  {
272  const scalar t = tuvs[i].x(), u = tuvs[i].y(), v = tuvs[i].z();
273 
274  const scalar magSqrF =
275  magSqr(C + Ct*t + Cu*u + Cv*v + Ctu*t*u + Ctv*t*v);
276  const scalar magSqrErrF =
277  magSqr
278  (
279  1*cmptMag(C)
280  + 2*cmptMag(Ct)*mag(t)
281  + 2*cmptMag(Cu)*mag(u)
282  + 2*cmptMag(Cv)*mag(v)
283  + 3*cmptMag(Ctu)*mag(t)*mag(u)
284  + 3*cmptMag(Ctv)*mag(t)*mag(v)
285  )*small;
286 
287  if (magSqrF < magSqrErrF)
288  {
289  return Tuple2<bool, vector>(true, tuvs[i]);
290  }
291  }
292 
293  // No suitable roots were found. Return failure.
294  return Tuple2<bool, vector>(false, vector::uniform(NaN));
295 }
296 
297 
298 //- Calculate the non-dimensional offsets of the source points from the target
299 // edges. These values are considered indicative only. The calculation is not
300 // as reliable as that for the target point offsets from the source edges (see
301 // below). The target point offsets should take precedence where possible.
303 (
304  const FixedList<point, 3>& srcPs,
305  const FixedList<vector, 3>& srcNs,
306  const FixedList<bool, 3>& srcOwns,
307  const FixedList<point, 3>& tgtPs,
308  const FixedList<bool, 3>& tgtOwns
309 )
310 {
312 
313  const triPointRef tgtTri(tgtPs[0], tgtPs[1], tgtPs[2]);
314  const vector tgtN = tgtTri.normal();
315 
316  // Check whether the intersections occur in a forward direction
317  FixedList<bool, 3> srcFwd;
318  forAll(srcPs, srcPi)
319  {
320  srcFwd[srcPi] = (srcNs[srcPi] & tgtN) < maxDot;
321  }
322 
323  // For all forward projecting source points, determine the offset from the
324  // target edges
325  forAll(srcPs, srcPi)
326  {
327  if (srcFwd[srcPi])
328  {
329  forAll(tgtPs, tgtEi)
330  {
331  const label tgtPi0 = tgtEi, tgtPi1 = (tgtEi + 1) % 3;
332  result[srcPi][tgtEi] =
334  (
335  srcPs[srcPi],
336  srcNs[srcPi],
337  {tgtPs[tgtPi0], tgtPs[tgtPi1]},
338  tgtOwns[tgtEi]
339  );
340  }
341  }
342  }
343 
344  // For all backward projecting source points, initialise to outside
345  // everything
346  forAll(srcPs, srcPi)
347  {
348  if (!srcFwd[srcPi])
349  {
350  result[srcPi] = {-vGreat, -vGreat, -vGreat};
351  }
352  }
353 
354  // For source edges with one forward projecting and one backward
355  // projecting point, compute the point and normal where the projection
356  // direction changes and use this to determine the offset of the backward
357  // projecting point
358  forAll(srcPs, srcEi)
359  {
360  const label srcPi0 = srcEi, srcPi1 = (srcEi + 1) % 3;
361 
362  if (srcFwd[srcPi0] != srcFwd[srcPi1])
363  {
364  // Get the source edge parameter and normal at the asymptote where
365  // the normal switches sign relative to the target
366  const scalar srcT =
368  (
369  maxDot - (srcNs[srcPi0] & tgtN),
370  (srcNs[srcPi1] - srcNs[srcPi0]) & tgtN
371  );
372  const point srcP = (1 - srcT)*srcPs[srcPi0] + srcT*srcPs[srcPi1];
373  const vector srcN = (1 - srcT)*srcNs[srcPi0] + srcT*srcNs[srcPi1];
374 
375  forAll(tgtPs, tgtEi)
376  {
377  const label tgtPi0 = tgtEi, tgtPi1 = (tgtEi + 1) % 3;
378  result[srcFwd[srcPi0] ? srcPi1 : srcPi0][tgtEi] =
380  (
381  srcP,
382  srcN,
383  {tgtPs[tgtPi0], tgtPs[tgtPi1]},
384  tgtOwns[tgtEi]
385  );
386  }
387  }
388  }
389 
390  return result;
391 }
392 
393 
394 //- Calculate the non-dimensional offsets the target points from the source
395 // edges. These values are considered definitive, and should take precedence
396 // over the source point target edge offsets.
398 (
399  const FixedList<point, 3>& srcPs,
400  const FixedList<vector, 3>& srcNs,
401  const FixedList<bool, 3>& srcOwns,
402  const FixedList<point, 3>& tgtPs,
403  const FixedList<bool, 3>& tgtOwns
404 )
405 {
407 
408  // For all target points, determine the offset from each source edge
409  forAll(tgtPs, tgtPi)
410  {
411  forAll(srcPs, srcEi)
412  {
413  const label srcPi0 = srcEi, srcPi1 = (srcEi + 1) % 3;
414  result[tgtPi][srcEi] =
416  (
417  {srcPs[srcPi0], srcPs[srcPi1]},
418  {srcNs[srcPi0], srcNs[srcPi1]},
419  tgtPs[tgtPi],
420  srcOwns[srcEi]
421  );
422  }
423  }
424 
425  return result;
426 }
427 
428 
429 //- Construct point-inside/outside-edge topology from a set of point-edge
430 // offsets. Uses the sign of the offsets.
432 (
433  const FixedList<FixedList<scalar, 3>, 3>& thisOtherEdgeOffset
434 )
435 {
437 
438  // Determine the edge association from the sign of the offset
439  forAll(thisOtherEdgeOffset, thisPi)
440  {
441  forAll(thisOtherEdgeOffset[thisPi], otherEi)
442  {
443  result[thisPi][otherEi] =
444  thisOtherEdgeOffset[thisPi][otherEi] > 0 ? +1 : -1;
445  }
446  }
447 
448  return result;
449 }
450 
451 
452 //- Construct point-inside/outside-triangle topology from a set of
453 // point-inside/outside-edge topology
455 (
457 )
458 {
459  FixedList<label, 3> result;
460 
461  // Combine edge associations to get triangle associations
462  forAll(thisInOtherEdge, thisPi)
463  {
464  result[thisPi] = count(thisInOtherEdge[thisPi], 1) == 3 ? +1 : -1;
465  }
466 
467  return result;
468 }
469 
470 
471 //- Construct target-point-inside/outside-source-triangle topology from a set
472 // of target-point-inside/outside-source-edge topology, and some additional
473 // geometric information to handle cases where the source normal direction
474 // is reversed relative to the target triangle
476 (
477  const FixedList<point, 3>& srcPs,
478  const FixedList<vector, 3>& srcNs,
479  const FixedList<point, 3>& tgtPs,
480  const FixedList<FixedList<label, 3>, 3>& tgtInSrcEdge
481 )
482 {
483  const triPointRef tgtTri(tgtPs[0], tgtPs[1], tgtPs[2]);
484  const vector tgtN = tgtTri.normal();
485 
486  // Combine edge associations to get triangle associations
487  FixedList<label, 3> result = thisInOtherTri(tgtInSrcEdge);
488 
489  // Filter to only include forward intersections
490  forAll(tgtInSrcEdge, tgtPi)
491  {
492  if (result[tgtPi] == 1)
493  {
494  const barycentric2D srcTs =
495  srcTriTgtPointIntersection(srcPs, srcNs, tgtPs[tgtPi]);
496  const vector srcN = srcTriInterpolate(srcTs, srcNs);
497  const bool tgtFwd = (srcN & tgtN) < maxDot;
498  result[tgtPi] = tgtFwd ? +1 : -1;
499  }
500  }
501 
502  return result;
503 }
504 
505 
506 //- Override results of the srcInTgt/tgtInSrc calculations with explicit
507 // connections between points on either side
509 (
510  const FixedList<label, 3>& thisOtherPis,
513 )
514 {
515  forAll(thisOtherPis, thisPi)
516  {
517  const label otherPi = thisOtherPis[thisPi];
518 
519  if (otherPi != -1)
520  {
521  const label otherEi0 = (otherPi + 2) % 3, otherEi1 = otherPi;
522 
523  thisInOtherTri[thisPi] = 0;
524 
525  thisInOtherEdge[thisPi][otherEi0] = 0;
526  thisInOtherEdge[thisPi][otherEi1] = 0;
527  }
528  }
529 }
530 
531 
532 //- Calculate whether the points of the given source triangle project inside or
533 // outside the opposing target triangle and its edges
535 (
536  const FixedList<point, 3>& srcPs,
537  const FixedList<vector, 3>& srcNs,
538  const FixedList<bool, 3>& srcOwns,
539  const FixedList<label, 3>& srcTgtPis,
540  const FixedList<point, 3>& tgtPs,
541  const FixedList<bool, 3>& tgtOwns,
542  FixedList<FixedList<label, 3>, 3>& srcInTgtEdge,
543  FixedList<label, 3>& srcInTgtTri
544 )
545 {
547  triIntersect::srcTgtEdgeOffset(srcPs, srcNs, srcOwns, tgtPs, tgtOwns);
548 
549  srcInTgtEdge = thisInOtherEdge(srcTgtEdgeOffset);
550 
551  srcInTgtTri = thisInOtherTri(srcInTgtEdge);
552 
553  thisIsOther(srcTgtPis, srcInTgtEdge, srcInTgtTri);
554 }
555 
556 
557 //- Calculate whether the points of the given target triangle project inside or
558 // outside the opposing source triangle and its edges
560 (
561  const FixedList<point, 3>& srcPs,
562  const FixedList<vector, 3>& srcNs,
563  const FixedList<bool, 3>& srcOwns,
564  const FixedList<point, 3>& tgtPs,
565  const FixedList<bool, 3>& tgtOwns,
566  const FixedList<label, 3>& tgtSrcPis,
567  FixedList<FixedList<label, 3>, 3>& tgtInSrcEdge,
569 )
570 {
572  triIntersect::tgtSrcEdgeOffset(srcPs, srcNs, srcOwns, tgtPs, tgtOwns);
573 
574  tgtInSrcEdge = thisInOtherEdge(tgtSrcEdgeOffset);
575 
576  tgtInSrcTri = triIntersect::tgtInSrcTri(srcPs, srcNs, tgtPs, tgtInSrcEdge);
577 
578  thisIsOther(tgtSrcPis, tgtInSrcEdge, tgtInSrcTri);
579 }
580 
581 
582 //- Order intersection locations into a polygon
584 (
585  const UList<location>& locations,
586  bool isSrcEdge,
587  const label i0,
588  label& nVisited,
589  boolList& visited,
591 )
592 {
593  // Mark this location as visited
594  order[nVisited ++] = i0;
595  visited[i0] = true;
596 
597  // Get the index of the edge attached to this point
598  const location& l0 = locations[i0];
599  const label ei0 =
600  isSrcEdge
601  ? (l0.isSrcPoint() ? l0.srcPointi() : l0.srcEdgei())
602  : (l0.isTgtPoint() ? (l0.tgtPointi() + 2) % 3 : l0.tgtEdgei());
603 
604  // Terminate if connected back to the first location
605  {
606  const label i1 = order.first();
607 
608  const location& l1 = locations[i1];
609 
610  if
611  (
612  i0 != order.first()
613  && !(l1.isSrcNotTgtPoint() && !isSrcEdge)
614  && !(l1.isTgtNotSrcPoint() && isSrcEdge)
615  )
616  {
617  const location& l1 = locations[i1];
618  const label ei1 =
619  isSrcEdge
620  ? (l1.isSrcPoint() ? (l1.srcPointi() + 2) % 3 : l1.srcEdgei())
621  : (l1.isTgtPoint() ? l1.tgtPointi() : l1.tgtEdgei());
622 
623  if (ei0 == ei1)
624  {
625  return true;
626  }
627  }
628  }
629 
630  // Search for the next connected location and recurse if found
631  forAll(locations, i1)
632  {
633  if (!visited[i1])
634  {
635  const location& l1 = locations[i1];
636 
637  if
638  (
639  !(l1.isSrcNotTgtPoint() && !isSrcEdge)
640  && !(l1.isTgtNotSrcPoint() && isSrcEdge)
641  )
642  {
643  const label ei1 =
644  isSrcEdge
645  ? (l1.isSrcPoint() ? (l1.srcPointi() + 2) % 3 : l1.srcEdgei())
646  : (l1.isTgtPoint() ? l1.tgtPointi() : l1.tgtEdgei());
647 
648  if (ei0 == ei1)
649  {
650  auto branch = [&](const bool isSrcEdge)
651  {
652  return orderLocations
653  (
654  locations,
655  isSrcEdge,
656  i1,
657  nVisited,
658  visited,
659  order
660  );
661  };
662 
663  if
664  (
665  (
666  !l1.isSrcAndTgtPoint()
667  && branch(l1.isIntersection() != isSrcEdge)
668  )
669  || (
670  l1.isSrcAndTgtPoint()
671  && (branch(true) || branch(false))
672  )
673  )
674  {
675  return true;
676  }
677  }
678  }
679  }
680  }
681 
682  // This branch failed to find a connected location. Un-visit this location.
683  order[-- nVisited] = -1;
684  visited[i0] = false;
685 
686  return false;
687 }
688 
689 
690 //- Construct the intersection topology
692 (
693  const FixedList<label, 3>& tgtSrcPis,
694  const FixedList<FixedList<label, 3>, 3>& srcInTgtEdge,
695  const FixedList<FixedList<label, 3>, 3>& tgtInSrcEdge,
696  const FixedList<label, 3>& srcInTgtTri,
698  DynamicList<location>& pointLocations
699 )
700 {
701  // Step 1: Process trivial rejection cases
702 
703  // If the entire target triangle is outside or on the same source edge
704  // then there can be no intersection.
705  forAll(srcInTgtEdge, srcEi)
706  {
707  bool outside = true;
708  forAll(tgtInSrcEdge, tgtPi)
709  {
710  if (tgtInSrcEdge[tgtPi][srcEi] == 1)
711  {
712  outside = false;
713  break;
714  }
715  }
716  if (outside)
717  {
718  return true;
719  }
720  }
721 
722  // If all source points are outside all target edges this indicates
723  // that the triangles are oppositely oriented, in which case there can
724  // also be no intersection.
725  if (count(srcInTgtEdge, {-1, -1, -1}) == 3)
726  {
727  return true;
728  }
729 
730  // Step 2: Define point addition/checking functions
731 
732  // Add crossing point locations, inserting source points as necessary
733  auto addPointLocations = [&pointLocations]
734  (
735  const location l1,
736  const location l2 = location(),
737  const bool add = true
738  )
739  {
740  if (!pointLocations.empty())
741  {
742  const location l0 = pointLocations.last();
743 
744  if (l0.isIntersection() || l0.isSrcAndTgtPoint())
745  {
746  const label srcEi0 =
747  l0.isIntersection()
748  ? l0.srcEdgei()
749  : (l0.srcPointi() + 2) % 3;
750  const label tgtEi0 =
751  l0.isIntersection()
752  ? l0.tgtEdgei()
753  : l0.tgtPointi();
754 
755  const label srcEi1 =
756  l1.isIntersection()
757  ? l1.srcEdgei()
758  : l1.srcPointi();
759  const label tgtEi1 =
760  l1.isIntersection()
761  ? l1.tgtEdgei()
762  : (l1.tgtPointi() + 2) % 3;
763 
764  if
765  (
766  (l0.isIntersection() && l1.isIntersection())
767  || tgtEi0 != tgtEi1
768  )
769  {
770  for
771  (
772  label srcEj = srcEi0;
773  srcEj != srcEi1;
774  srcEj = (srcEj + 2) % 3
775  )
776  {
777  pointLocations.append(location::srcPoint(srcEj));
778  }
779  }
780  }
781  }
782 
783  if (add)
784  {
785  pointLocations.append(l1);
786 
787  if (!l2.isNull())
788  {
789  pointLocations.append(l2);
790  }
791  }
792  };
793 
794  // One target point is within the source triangle and one is not
795  auto inTriToOut = [&addPointLocations,&srcInTgtEdge]
796  (
797  const label tgtEi,
798  const label tgtOutSrcEi1,
799  const label tgtOutSrcPi1,
800  const bool reverse
801  )
802  {
803  const label srcEi =
804  tgtOutSrcEi1 != -1
805  ? tgtOutSrcEi1
806  : srcInTgtEdge[tgtOutSrcPi1][tgtEi] == 1
807  ? (tgtOutSrcPi1 + 2*reverse) % 3
808  : (tgtOutSrcPi1 + 2*!reverse) % 3;
809 
810  addPointLocations(location::intersection(srcEi, tgtEi));
811  };
812 
813  // One target point is a source point and the other is outside a source edge
814  auto isPointToOutEdge = [&addPointLocations,&srcInTgtEdge]
815  (
816  const label tgtEi,
817  const label tgtIsSrcPi0,
818  const label tgtOutSrcEi1,
819  const bool reverse
820  )
821  {
822  const label srcEi0Next = (tgtIsSrcPi0 + 2*reverse) % 3;
823  const label srcEi0Opp = (tgtIsSrcPi0 + 1) % 3;
824 
825  if
826  (
827  srcInTgtEdge[(tgtIsSrcPi0 + 1) % 3][tgtEi] == -1
828  && srcInTgtEdge[(tgtIsSrcPi0 + 2) % 3][tgtEi] == -1
829  )
830  {
831  return false;
832  }
833 
834  if (tgtOutSrcEi1 == srcEi0Next)
835  {
836  return false;
837  }
838 
839  if (tgtOutSrcEi1 == srcEi0Opp)
840  {
841  addPointLocations(location::intersection(srcEi0Opp, tgtEi));
842  }
843 
844  return true;
845  };
846 
847  // One target point is a source point and the other is outside a source
848  // corner
849  auto isPointToOutCorner = []
850  (
851  const label tgtEi,
852  const label tgtIsSrcPi0,
853  const label tgtOutSrcPi1,
854  const bool reverse
855  )
856  {
857  return tgtOutSrcPi1 != (tgtIsSrcPi0 + 1 + reverse) % 3;
858  };
859 
860  // Both target points are outside source edges
861  auto outEdgeToOutEdge = [&addPointLocations,&srcInTgtEdge]
862  (
863  const label tgtEi,
864  const label tgtOutSrcEi0,
865  const label tgtOutSrcEi1
866  )
867  {
868  const label srcPi = (5 - tgtOutSrcEi0 - tgtOutSrcEi1) % 3;
869 
870  if
871  (
872  (tgtOutSrcEi0 != (tgtOutSrcEi1 + 1) % 3)
873  && (srcInTgtEdge[srcPi][tgtEi] != 1)
874  )
875  {
876  return false;
877  }
878 
879  if
880  (
881  (tgtOutSrcEi0 == (tgtOutSrcEi1 + 1) % 3)
882  != (srcInTgtEdge[srcPi][tgtEi] == 1)
883  )
884  {
885  addPointLocations
886  (
887  location::intersection(tgtOutSrcEi0, tgtEi),
888  location::intersection(tgtOutSrcEi1, tgtEi)
889  );
890  }
891 
892  return true;
893  };
894 
895  // One target point is outside a source edge and the other is outside a
896  // source corner
897  auto outEdgeToOutCorner = [&addPointLocations,&srcInTgtEdge]
898  (
899  const label tgtEi,
900  const label tgtOutSrcEi0,
901  const label tgtOutSrcPi1,
902  const bool reverse
903  )
904  {
905  if (tgtOutSrcEi0 == tgtOutSrcPi1)
906  {
907  return !reverse;
908  }
909 
910  if ((tgtOutSrcEi0 + 1) % 3 == tgtOutSrcPi1)
911  {
912  return reverse;
913  }
914 
915  const label srcPi1Prev = (tgtOutSrcPi1 + 1 + !reverse) % 3;
916 
917  if (srcInTgtEdge[srcPi1Prev][tgtEi] == -1)
918  {
919  return false;
920  }
921 
922  const label srcPi1Next = (tgtOutSrcPi1 + 1 + reverse) % 3;
923 
924  if (srcInTgtEdge[srcPi1Next][tgtEi] == 1)
925  {
926  return true;
927  }
928 
929  location l1 = location::intersection(tgtOutSrcEi0, tgtEi);
930 
931  const label srcEi =
932  srcInTgtEdge[tgtOutSrcPi1][tgtEi] == 1
933  ? (tgtOutSrcPi1 + 2*reverse) % 3
934  : (tgtOutSrcPi1 + 2*!reverse) % 3;
935 
936  location l2 = location::intersection(srcEi, tgtEi);
937 
938  if (reverse)
939  {
940  Swap(l1, l2);
941  }
942 
943  addPointLocations(l1, l2);
944 
945  return true;
946  };
947 
948  // Both target points are outside source corners
949  auto outCornerToOutCorner = []
950  (
951  const label tgtEi,
952  const label tgtOutSrcPi0,
953  const label tgtOutSrcPi1
954  )
955  {
956  return tgtOutSrcPi0 != (tgtOutSrcPi1 + 2) % 3;
957  };
958 
959  // Step 3: Walk around the target edges to form the intersection polygon
960  for (label tgtEi = 0; tgtEi < 3; tgtEi ++)
961  {
962  const label tgtPi0 = tgtEi, tgtPi1 = (tgtEi + 1) % 3;
963 
964  const bool tgtInSrcTri0 = tgtInSrcTri[tgtPi0] == 1;
965  const bool tgtInSrcTri1 = tgtInSrcTri[tgtPi1] == 1;
966 
967  const label tgtIsSrcPi0 =
968  tgtInSrcTri[tgtPi0] == 0 ? tgtSrcPis[tgtPi0] : -1;
969  const label tgtIsSrcPi1 =
970  tgtInSrcTri[tgtPi1] == 0 ? tgtSrcPis[tgtPi1] : -1;
971 
972  const label tgtOutSrcEi0 =
973  count(tgtInSrcEdge[tgtPi0], -1) == 1
974  ? findIndex(tgtInSrcEdge[tgtPi0], -1)
975  : -1;
976  const label tgtOutSrcEi1 =
977  count(tgtInSrcEdge[tgtPi1], -1) == 1
978  ? findIndex(tgtInSrcEdge[tgtPi1], -1)
979  : -1;
980 
981  const label tgtOutSrcPi0 =
982  count(tgtInSrcEdge[tgtPi0], -1) == 2
983  ? (findIndex(tgtInSrcEdge[tgtPi0], 1) + 2) % 3
984  : -1;
985  const label tgtOutSrcPi1 =
986  count(tgtInSrcEdge[tgtPi1], -1) == 2
987  ? (findIndex(tgtInSrcEdge[tgtPi1], 1) + 2) % 3
988  : -1;
989 
990  // Add the first point if it within or part of the source triangle
991  if (tgtInSrcTri0)
992  {
993  pointLocations.append(location::tgtPoint(tgtPi0));
994  }
995  if (tgtIsSrcPi0 != -1)
996  {
997  addPointLocations(location::srcTgtPoint(tgtIsSrcPi0, tgtPi0));
998  }
999 
1000  // Add crossings
1001  if
1002  (
1003  (tgtInSrcTri0 && tgtInSrcTri1)
1004  || (tgtOutSrcEi0 != -1 && tgtOutSrcEi0 == tgtOutSrcEi1)
1005  || (tgtOutSrcPi0 != -1 && tgtOutSrcPi0 == tgtOutSrcPi1)
1006  )
1007  {
1008  // Both target points are in the same source quadrant. There is
1009  // nothing to check or to add.
1010  }
1011  else if
1012  (
1013  (tgtInSrcTri0 && tgtIsSrcPi1 != -1)
1014  || (tgtIsSrcPi0 != -1 && tgtInSrcTri1)
1015  )
1016  {
1017  // One target point is within the source triangle and one is a
1018  // source point. There is nothing to check or to add.
1019  }
1020  else if (tgtInSrcTri0 && (tgtOutSrcEi1 != -1 || tgtOutSrcPi1 != -1))
1021  {
1022  // The first target point is within the source triangle and the
1023  // second is outside
1024  inTriToOut(tgtEi, tgtOutSrcEi1, tgtOutSrcPi1, 0);
1025  }
1026  else if ((tgtOutSrcEi0 != -1 || tgtOutSrcPi0 != -1) && tgtInSrcTri1)
1027  {
1028  // (reverse of previous clause)
1029  inTriToOut(tgtEi, tgtOutSrcEi0, tgtOutSrcPi0, 1);
1030  }
1031  else if (tgtIsSrcPi0 != -1 && tgtIsSrcPi1 != -1)
1032  {
1033  // Both target points are source points. Check the ordering is
1034  // compatible with an intersection.
1035  if (tgtIsSrcPi0 != (tgtIsSrcPi1 + 1) % 3)
1036  {
1037  pointLocations.clear();
1038  return false;
1039  }
1040  }
1041  else if (tgtIsSrcPi0 != -1 && tgtOutSrcEi1 != -1)
1042  {
1043  // The first target point is a source point and the second is
1044  // outside a source edge
1045  if (!isPointToOutEdge(tgtEi, tgtIsSrcPi0, tgtOutSrcEi1, 0))
1046  {
1047  pointLocations.clear();
1048  return false;
1049  }
1050  }
1051  else if (tgtOutSrcEi0 != -1 && tgtIsSrcPi1 != -1)
1052  {
1053  // (reverse of previous clause)
1054  if (!isPointToOutEdge(tgtEi, tgtIsSrcPi1, tgtOutSrcEi0, 1))
1055  {
1056  pointLocations.clear();
1057  return false;
1058  }
1059  }
1060  else if (tgtIsSrcPi0 != -1 && tgtOutSrcPi1 != -1)
1061  {
1062  // The first target point is a source point and the second is
1063  // outside a source corner
1064  if (!isPointToOutCorner(tgtEi, tgtIsSrcPi0, tgtOutSrcPi1, 0))
1065  {
1066  pointLocations.clear();
1067  return false;
1068  }
1069  }
1070  else if (tgtOutSrcPi0 != -1 && tgtIsSrcPi1 != -1)
1071  {
1072  // (reverse of previous clause)
1073  if (!isPointToOutCorner(tgtEi, tgtIsSrcPi1, tgtOutSrcPi0, 1))
1074  {
1075  pointLocations.clear();
1076  return false;
1077  }
1078  }
1079  else if (tgtOutSrcEi0 != -1 && tgtOutSrcEi1 != -1)
1080  {
1081  // Both target points are outside source edges
1082  if (!outEdgeToOutEdge(tgtEi, tgtOutSrcEi0, tgtOutSrcEi1))
1083  {
1084  pointLocations.clear();
1085  return false;
1086  }
1087  }
1088  else if (tgtOutSrcEi0 != -1 && tgtOutSrcPi1 != -1)
1089  {
1090  // The first target point is outside a source edge and the
1091  // second is outside a source corner
1092  if (!outEdgeToOutCorner(tgtEi, tgtOutSrcEi0, tgtOutSrcPi1, 0))
1093  {
1094  pointLocations.clear();
1095  return false;
1096  }
1097  }
1098  else if (tgtOutSrcPi0 != -1 && tgtOutSrcEi1 != -1)
1099  {
1100  // (reverse of previous clause)
1101  if (!outEdgeToOutCorner(tgtEi, tgtOutSrcEi1, tgtOutSrcPi0, 1))
1102  {
1103  pointLocations.clear();
1104  return false;
1105  }
1106  }
1107  else if (tgtOutSrcPi0 != -1 && tgtOutSrcPi1 != -1)
1108  {
1109  // Both target points are outside source corners
1110  if (!outCornerToOutCorner(tgtEi, tgtOutSrcPi0, tgtOutSrcPi1))
1111  {
1112  pointLocations.clear();
1113  return false;
1114  }
1115  }
1116  else
1117  {
1118  // A target point is outside all source edges. The projection
1119  // has collapsed.
1120  pointLocations.clear();
1121  return false;
1122  }
1123  }
1124 
1125  // Step 4: Complete the polygon by adding any remaining source points that
1126  // were not traversed during the walk of the target edges
1127  if (!pointLocations.empty())
1128  {
1129  const location& l = pointLocations.first();
1130 
1131  if (l.isIntersection() || l.isSrcAndTgtPoint())
1132  {
1133  addPointLocations(l, location(), false);
1134  }
1135  }
1136  else
1137  {
1138  forAllReverse(srcInTgtEdge, srcPi)
1139  {
1140  pointLocations.append(location::srcPoint(srcPi));
1141  }
1142  }
1143 
1144  // Step 5: The above walk was done around the target triangle, but the
1145  // result should be ordered in the direction of the source triangle, so the
1146  // list of locations must be reversed
1147  inplaceReverseList(pointLocations);
1148 
1149  return true;
1150 }
1151 
1152 
1153 //- Construct the intersection geometry
1155 (
1156  const FixedList<point, 3>& srcPs,
1157  const FixedList<vector, 3>& srcNs,
1158  const FixedList<point, 3>& tgtPs,
1159  DynamicList<point>& srcPoints,
1160  DynamicList<vector>& srcPointNormals,
1161  DynamicList<point>& tgtPoints,
1162  const DynamicList<location>& pointLocations
1163 )
1164 {
1165  srcPoints.resize(pointLocations.size());
1166  srcPointNormals.resize(pointLocations.size());
1167  tgtPoints.resize(pointLocations.size());
1168 
1169  forAll(pointLocations, pointi)
1170  {
1171  const location& l = pointLocations[pointi];
1172 
1173  if (l.isSrcAndTgtPoint())
1174  {
1175  const point& srcP = srcPs[l.srcPointi()];
1176  const vector& srcN = srcNs[l.srcPointi()];
1177  const point& tgtP = tgtPs[l.tgtPointi()];
1178 
1179  srcPoints[pointi] = srcP;
1180  srcPointNormals[pointi] = srcN;
1181  tgtPoints[pointi] = tgtP;
1182  }
1183  else if (l.isSrcPoint())
1184  {
1185  const point& srcP = srcPs[l.srcPointi()];
1186  const vector& srcN = srcNs[l.srcPointi()];
1187  barycentric2D tgtTs =
1188  srcPointTgtTriIntersection(srcP, srcN, tgtPs);
1189 
1190  // Force inside the target triangle
1191  if (cmptMin(tgtTs) < 0)
1192  {
1193  const direction iMin = findMin(tgtTs);
1194  const direction iMax = findMax(tgtTs);
1195  const direction iMid = 3 - iMin - iMax;
1196 
1197  if (tgtTs[iMid] < 0)
1198  {
1199  tgtTs[iMin] = 0;
1200  tgtTs[iMax] = 1;
1201  tgtTs[iMid] = 0;
1202  }
1203  else
1204  {
1205  const scalar t = tgtTs[iMax] + tgtTs[iMid];
1206  tgtTs[iMin] = 0;
1207  tgtTs[iMax] /= t;
1208  tgtTs[iMid] /= t;
1209  }
1210  }
1211 
1212  srcPoints[pointi] = srcP;
1213  srcPointNormals[pointi] = srcN;
1214  tgtPoints[pointi] = tgtTriInterpolate(tgtTs, tgtPs);
1215  }
1216  else if (l.isTgtPoint())
1217  {
1218  const point& tgtP = tgtPs[l.tgtPointi()];
1219  const barycentric2D srcTs =
1220  srcTriTgtPointIntersection(srcPs, srcNs, tgtP);
1221 
1222  srcPoints[pointi] = srcTriInterpolate(srcTs, srcPs);
1223  srcPointNormals[pointi] = srcTriInterpolate(srcTs, srcNs);
1224  tgtPoints[pointi] = tgtP;
1225  }
1226  else // if (l.isIntersection())
1227  {
1228  const label srcPi0 = l.srcEdgei(), srcPi1 = (srcPi0 + 1) % 3;
1229  const label tgtPi0 = l.tgtEdgei(), tgtPi1 = (tgtPi0 + 1) % 3;
1230 
1231  const Pair<scalar> ts =
1233  (
1234  {srcPs[srcPi0], srcPs[srcPi1]},
1235  {srcNs[srcPi0], srcNs[srcPi1]},
1236  {tgtPs[tgtPi0], tgtPs[tgtPi1]}
1237  );
1238  const scalar srcT = ts.first(), tgtT = ts.second();
1239 
1240  srcPoints[pointi] =
1241  (1 - srcT)*srcPs[srcPi0] + srcT*srcPs[srcPi1];
1242  srcPointNormals[pointi] =
1243  (1 - srcT)*srcNs[srcPi0] + srcT*srcNs[srcPi1];
1244  tgtPoints[pointi] =
1245  (1 - tgtT)*tgtPs[tgtPi0] + tgtT*tgtPs[tgtPi1];
1246  }
1247  }
1248 }
1249 
1250 
1251 } // End namespace triIntersect
1252 } // End namespace Foam
1253 
1254 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
1255 
1257 (
1258  const word& name,
1259  const FixedList<point, 3>& srcPs,
1260  const FixedList<vector, 3>& srcNs,
1261  const label nEdge,
1262  const label nNormal,
1263  const scalar lNormal
1264 )
1265 {
1266  scalar lengthScale = 0;
1267  for (label i = 0; i < 3; ++ i)
1268  {
1269  lengthScale = max(lengthScale, mag(srcPs[i] - srcPs[(i + 1) % 3]));
1270  }
1271 
1272  const label nu = nEdge, nv = nNormal;
1273  const scalar u0 = 0, u1 = 1;
1274  const scalar v0 = -lNormal/2*lengthScale, v1 = lNormal/2*lengthScale;
1275 
1276  pointField ps(3*(nu + 1)*(nv + 1));
1277  for (label i = 0; i < 3; ++ i)
1278  {
1279  const point& p0 = srcPs[i], p1 = srcPs[(i + 1) % 3];
1280  const vector& n0 = srcNs[i], n1 = srcNs[(i + 1) % 3];
1281 
1282  for (label iu = 0; iu <= nu; ++ iu)
1283  {
1284  const scalar u = u0 + (u1 - u0)*scalar(iu)/nu;
1285  for (label iv = 0; iv <= nv; ++ iv)
1286  {
1287  const scalar v = v0 + (v1 - v0)*scalar(iv)/nv;
1288  const vector x = p0 + (p1 - p0)*u + (n0 + (n1 - n0)*u)*v;
1289  ps[i*(nu + 1)*(nv + 1) + iu*(nv + 1) + iv] = x;
1290  }
1291  }
1292  }
1293 
1294  faceList fs(3*nu*nv);
1295  for (label i = 0; i < 3; ++ i)
1296  {
1297  for (label iu = 0; iu < nu; ++ iu)
1298  {
1299  for (label iv = 0; iv < nv; ++ iv)
1300  {
1301  fs[i*nu*nv + iu*nv + iv] =
1302  face
1303  ({
1304  i*(nu + 1)*(nv + 1) + (nv + 1)*iu + iv,
1305  i*(nu + 1)*(nv + 1) + (nv + 1)*iu + iv + 1,
1306  i*(nu + 1)*(nv + 1) + (nv + 1)*(iu + 1) + iv + 1,
1307  i*(nu + 1)*(nv + 1) + (nv + 1)*(iu + 1) + iv
1308  });
1309  }
1310  }
1311  }
1312 
1313  Info<< indent << "Writing face to " << name + ".vtk" << endl;
1315  (
1316  name + ".vtk",
1317  name,
1318  false,
1319  ps,
1320  labelList(),
1321  labelListList(),
1322  fs
1323  );
1324 }
1325 
1326 
1328 (
1329  const Pair<point>& srcPs,
1330  const Pair<vector>& srcNs,
1331  const point& tgtP
1332 )
1333 {
1334  const tensor A(srcPs[1] - srcPs[0], srcNs[0], srcNs[1]);
1335  const scalar detA = det(A);
1336 
1337  const tensor T(A.y()^A.z(), A.z()^A.x(), A.x()^A.y());
1338 
1339  const vector detAY = T & (tgtP - srcPs[0]);
1340  const scalar Yx = protectedDivideAndClip01(detAY.x(), detA);
1341 
1342  const scalar offset = Yx*detAY.y() - (1 - Yx)*detAY.z();
1343 
1344  return offset == 0 ? - vSmall : offset;
1345 }
1346 
1347 
1349 (
1350  const Pair<point>& srcPs,
1351  const Pair<vector>& srcNs,
1352  const point& tgtP,
1353  const bool srcDirection
1354 )
1355 {
1356  if (srcDirection)
1357  {
1358  return srcEdgeTgtPointOffset(srcPs, srcNs, tgtP);
1359  }
1360  else
1361  {
1362  return - srcEdgeTgtPointOffset(reverse(srcPs), reverse(srcNs), tgtP);
1363  }
1364 }
1365 
1366 
1368 (
1369  const point& srcP,
1370  const vector& srcN,
1371  const Pair<point>& tgtPs
1372 )
1373 {
1374  const tensor A(srcN, tgtPs[1] - tgtPs[0], srcN^(tgtPs[1] - tgtPs[0]));
1375  const scalar detA = det(A);
1376 
1377  const tensor T(A.y()^A.z(), A.z()^A.x(), A.x()^A.y());
1378 
1379  const vector detAY = T & (tgtPs[0] - srcP);
1380 
1381  const scalar offset = protectedDivide(detAY.z(), detA);
1382 
1383  return offset == 0 ? - vSmall : offset;
1384 }
1385 
1386 
1388 (
1389  const point& srcP,
1390  const vector& srcN,
1391  const Pair<point>& tgtPs,
1392  const bool tgtDirection
1393 )
1394 {
1395  if (tgtDirection)
1396  {
1397  return srcPointTgtEdgeOffset(srcP, srcN, tgtPs);
1398  }
1399  else
1400  {
1401  return - srcPointTgtEdgeOffset(srcP, srcN, reverse(tgtPs));
1402  }
1403 }
1404 
1405 
1407 (
1408  const Pair<point>& srcPs,
1409  const Pair<vector>& srcNs,
1410  const Pair<point>& tgtPs
1411 )
1412 {
1413  scalar srcT, tgtT;
1414 
1415  #ifndef BISECT
1418  (
1419  srcPs[0] - tgtPs[0],
1420  srcPs[1] - srcPs[0],
1421  tgtPs[0] - tgtPs[1],
1422  srcNs[0],
1423  Zero,
1424  srcNs[1] - srcNs[0],
1425  {0, 1, -1}
1426  );
1427  if (solution.first())
1428  {
1429  // If the analytical solution succeeds, then use the result
1430  srcT = solution.second().x();
1431  tgtT = solution.second().y();
1432  }
1433  else
1434  #endif
1435  {
1436  // If the analytical solution fails, then solve by bisection
1437 
1438  // !!! This method, whilst elegant, isn't sufficiently robust. The
1439  // srcPointTgtEdgeOffset calculation is not as reliable as
1440  // srcEdgeTgtPointOffset. So, we can't bisect the source edge to get
1441  // srcT and then reuse the solveProjectionGivenT stuff. We need to
1442  // bisect the target edge to get tgtT, and then have a specific process
1443  // for calculating srcT from tgtT.
1444  /*
1445  scalar srcT0 = 0, srcT1 = 1;
1446 
1447  const scalar o0 = srcPointTgtEdgeOffset(srcPs[0], srcNs[0], tgtPs);
1448  const scalar o1 = srcPointTgtEdgeOffset(srcPs[1], srcNs[1], tgtPs);
1449 
1450  const scalar s = o0 > o1 ? +1 : -1;
1451 
1452  for (label i = 0; i < ceil(std::log2(1/small)); ++ i)
1453  {
1454  const scalar srcT = (srcT0 + srcT1)/2;
1455  const vector srcP = (1 - srcT)*srcPs[0] + srcT*srcPs[1];
1456  const vector srcN = (1 - srcT)*srcNs[0] + srcT*srcNs[1];
1457 
1458  const scalar o = s*srcPointTgtEdgeOffset(srcP, srcN, tgtPs);
1459 
1460  if (o > 0)
1461  {
1462  srcT0 = srcT;
1463  }
1464  else
1465  {
1466  srcT1 = srcT;
1467  }
1468  }
1469 
1470  srcT = (srcT0 + srcT1)/2;
1471  tgtT =
1472  solveProjectionGivenT
1473  (
1474  srcPs[0] - tgtPs[0],
1475  srcPs[1] - srcPs[0],
1476  tgtPs[0] - tgtPs[1],
1477  srcNs[0],
1478  Zero,
1479  srcNs[1] - srcNs[0],
1480  {0, 1, -1},
1481  srcT
1482  ).y();
1483  */
1484 
1485  // !!! This method appears robust
1486  scalar tgtT0 = 0, tgtT1 = 1;
1487 
1488  const scalar o0 = srcEdgeTgtPointOffset(srcPs, srcNs, tgtPs[0]);
1489  const scalar o1 = srcEdgeTgtPointOffset(srcPs, srcNs, tgtPs[1]);
1490 
1491  const scalar s = o0 > o1 ? +1 : -1;
1492 
1493  for (label i = 0; i < ceil(std::log2(1/small)); ++ i)
1494  {
1495  const scalar tgtT = (tgtT0 + tgtT1)/2;
1496  const vector tgtP = (1 - tgtT)*tgtPs[0] + tgtT*tgtPs[1];
1497 
1498  const scalar o = s*srcEdgeTgtPointOffset(srcPs, srcNs, tgtP);
1499 
1500  if (o > 0)
1501  {
1502  tgtT0 = tgtT;
1503  }
1504  else
1505  {
1506  tgtT1 = tgtT;
1507  }
1508  }
1509 
1510  tgtT = (tgtT0 + tgtT1)/2;
1511 
1512  // Solve for the corresponding source edge coordinate
1513  const vector srcDP = srcPs[1] - srcPs[0];
1514  const vector tgtP = (1 - tgtT)*tgtPs[0] + tgtT*tgtPs[1];
1515 
1516  const tensor A(srcDP, srcNs[0], srcNs[1]);
1517  const scalar detA = det(A);
1518 
1519  const vector Tx = A.y()^A.z();
1520 
1521  const scalar magDetAYx = sign(detA)*(Tx & (tgtP - srcPs[0]));
1522 
1523  const scalar srcTStar = protectedDivideAndClip01(magDetAYx, mag(detA));
1524 
1525  const vector srcN = (1 - srcTStar)*srcNs[0] + srcTStar*srcNs[1];
1526 
1527  const vector srcDPPerpN = srcDP - (srcDP & srcN)*srcN;
1528 
1529  srcT =
1531  (
1532  (tgtP - srcPs[0]) & srcDPPerpN,
1533  srcDP & srcDPPerpN
1534  );
1535  }
1536 
1537  /*
1538  // Check that the points match
1539  {
1540  const point srcP = (1 - srcT)*srcPs[0] + srcT*srcPs[1];
1541  const point srcN = (1 - srcT)*srcNs[0] + srcT*srcNs[1];
1542  const point tgtP = (1 - tgtT)*tgtPs[0] + tgtT*tgtPs[1];
1543  const scalar srcU = ((tgtP - srcP) & srcN)/magSqr(srcN);
1544  const point srcQ = srcP + srcU*srcN;
1545  Info<< "srcT=" << srcT << ", tgtT=" << tgtT
1546  << ", err=" << magSqr(srcQ - tgtP) << endl;
1547  }
1548  */
1549 
1550  return Pair<scalar>(srcT, tgtT);
1551 }
1552 
1553 
1555 (
1556  const FixedList<point, 3>& srcPs,
1557  const FixedList<vector, 3>& srcNs,
1558  const point& tgtP
1559 )
1560 {
1561  auto srcPN = []
1562  (
1563  const FixedList<vector, 3>& srcPNs,
1564  const vector2D& srcTs
1565  )
1566  {
1567  const scalar srcT0 = 1 - srcTs.x() - srcTs.y();
1568  return srcT0*srcPNs[0] + srcTs.x()*srcPNs[1] + srcTs.y()*srcPNs[2];
1569  };
1570 
1571  auto srcEdgePNs = [srcPN]
1572  (
1573  const FixedList<vector, 3>& srcPNs,
1574  const vector2D& srcTs0,
1575  const vector2D& srcTs1
1576  )
1577  {
1578  return Pair<vector>(srcPN(srcPNs, srcTs0), srcPN(srcPNs, srcTs1));
1579  };
1580 
1581  auto offset = [&](const vector2D& srcTs0, const vector2D& srcTs1)
1582  {
1583  return srcEdgeTgtPointOffset
1584  (
1585  srcEdgePNs(srcPs, srcTs0, srcTs1),
1586  srcEdgePNs(srcNs, srcTs0, srcTs1),
1587  tgtP
1588  );
1589  };
1590 
1591  const scalar oA = offset(vector2D(0, 0), vector2D(1, 0));
1592  const scalar oB = offset(vector2D(1, 0), vector2D(0, 1));
1593  const scalar oC = offset(vector2D(0, 1), vector2D(0, 0));
1594  const FixedList<scalar, 3> offsets({oA, oB, oC});
1595 
1596  // If inside the triangle (or outside if the triangle is inverted) ...
1597  if (offsets[findMin(offsets)] >= 0 || offsets[findMax(offsets)] <= 0)
1598  {
1599  scalar srcT, srcU;
1600 
1601  #ifndef BISECT
1604  (
1605  srcPs[0] - tgtP,
1606  srcNs[0],
1607  srcPs[1] - srcPs[0],
1608  srcPs[2] - srcPs[0],
1609  srcNs[1] - srcNs[0],
1610  srcNs[2] - srcNs[0],
1611  {-1, 0, 0}
1612  );
1613  if (solution.first())
1614  {
1615  // If the analytical solution succeeds, then use the result
1616  srcT = solution.second().y();
1617  srcU = solution.second().z();
1618  }
1619  else
1620  #endif
1621  {
1622  // If the analytical solution fails, then solve by bisection
1623  const scalar sign = offsets[findMin(offsets)] > 0 ? +1 : -1;
1624 
1625  vector2D srcTsA(0, 0), srcTsB(1, 0), srcTsC(0, 1);
1626 
1627  for (label i = 0; i < ceil(std::log2(1/small)); ++ i)
1628  {
1629  const vector2D srcTsAB = (srcTsA + srcTsB)/2;
1630  const vector2D srcTsBC = (srcTsB + srcTsC)/2;
1631  const vector2D srcTsCA = (srcTsC + srcTsA)/2;
1632 
1633  const scalar oA = sign*offset(srcTsCA, srcTsAB);
1634  const scalar oB = sign*offset(srcTsAB, srcTsBC);
1635  const scalar oC = sign*offset(srcTsBC, srcTsCA);
1636  const FixedList<scalar, 3> offsets({oA, oB, oC});
1637 
1638  const label offsetMini = findMin(offsets);
1639  if (offsets[offsetMini] > 0)
1640  {
1641  srcTsA = srcTsAB;
1642  srcTsB = srcTsBC;
1643  srcTsC = srcTsCA;
1644  }
1645  else if (offsetMini == 0)
1646  {
1647  srcTsC = srcTsCA;
1648  srcTsB = srcTsAB;
1649  }
1650  else if (offsetMini == 1)
1651  {
1652  srcTsA = srcTsAB;
1653  srcTsC = srcTsBC;
1654  }
1655  else if (offsetMini == 2)
1656  {
1657  srcTsB = srcTsBC;
1658  srcTsA = srcTsCA;
1659  }
1660  }
1661 
1662  srcT = (srcTsA[0] + srcTsB[0] + srcTsC[0])/3;
1663  srcU = (srcTsA[1] + srcTsB[1] + srcTsC[1])/3;
1664  }
1665 
1666  return barycentric2D(1 - srcT - srcU, srcT, srcU);
1667  }
1668 
1669  // If outside an edge ...
1670  forAll(srcPs, srcEi)
1671  {
1672  const label srcEi0 = (srcEi + 2) % 3, srcEi1 = (srcEi + 1) % 3;
1673 
1674  if
1675  (
1676  offsets[srcEi] <= 0
1677  && offsets[srcEi0] >= 0
1678  && offsets[srcEi1] >= 0
1679  )
1680  {
1681  const label srcPi0 = srcEi, srcPi1 = (srcEi + 1) % 3;
1682  const label srcPiOpp = (srcEi + 2) % 3;
1683 
1684  scalar srcT, srcU;
1685 
1686  #ifndef BISECT
1689  (
1690  srcPs[srcPi0] - tgtP,
1691  srcPs[srcPi1] - srcPs[srcPi0],
1692  srcPs[srcPi0] - srcPs[srcPiOpp],
1693  srcNs[srcPi0],
1694  srcPs[srcPi1] - srcPs[srcPi0],
1695  srcNs[srcPi1] - srcNs[srcPi0],
1696  {0, -1, -1}
1697  );
1698  if (solution.first())
1699  {
1700  // If the analytical solution succeeds, then use the result
1701  srcT = solution.second().x();
1702  srcU = solution.second().y();
1703  }
1704  else
1705  #endif
1706  {
1707  // If the analytical solution fails, then solve by bisection
1708  const vector2D srcTsOpp(srcPiOpp == 1, srcPiOpp == 2);
1709  const vector2D srcTs0(srcPi0 == 1, srcPi0 == 2);
1710  const vector2D srcTs1(srcPi1 == 1, srcPi1 == 2);
1711 
1712  scalar srcT0 = 0, srcT1 = 1;
1713 
1714  for (label i = 0; i < ceil(std::log2(1/small)); ++ i)
1715  {
1716  const scalar srcT = (srcT0 + srcT1)/2;
1717  const vector2D srcTs01(srcTs0*(1 - srcT) + srcTs1*srcT);
1718 
1719  const scalar o = offset(srcTsOpp, srcTs01);
1720 
1721  if (o > 0)
1722  {
1723  srcT0 = srcT;
1724  }
1725  else
1726  {
1727  srcT1 = srcT;
1728  }
1729  }
1730 
1731  srcT = (srcT0 + srcT1)/2;
1732  srcU =
1734  (
1735  srcPs[srcPi0] - tgtP,
1736  srcPs[srcPi1] - srcPs[srcPi0],
1737  srcPs[srcPi0] - srcPs[srcPiOpp],
1738  srcNs[srcPi0],
1739  srcPs[srcPi1] - srcPs[srcPi0],
1740  srcNs[srcPi1] - srcNs[srcPi0],
1741  {0, -1, -1},
1742  srcT
1743  ).y();
1744  }
1745 
1746  // Convert to the triangle's coordinate system
1747  barycentric2D y;
1748  y[srcPiOpp] = - srcU;
1749  y[srcPi0] = (1 + srcU)*(1 - srcT);
1750  y[srcPi1] = (1 + srcU)*srcT;
1751  return y;
1752  }
1753  }
1754 
1755  // If outside a corner ...
1756  forAll(srcPs, srcPi)
1757  {
1758  const label srcEiOpp = (srcPi + 1) % 3;
1759  const label srcEi0 = (srcPi + 2) % 3, srcEi1 = srcPi;
1760 
1761  if
1762  (
1763  offsets[srcEiOpp] >= 0
1764  && offsets[srcEi0] <= 0
1765  && offsets[srcEi1] <= 0
1766  )
1767  {
1768  // Solve for the intersection coordinates directly
1769  const label srcPi0 = (srcPi + 2) % 3, srcPi1 = (srcPi + 1) % 3;
1770 
1771  const tensor A
1772  (
1773  srcPs[srcPi] - srcPs[srcPi0],
1774  srcPs[srcPi] - srcPs[srcPi1],
1775  srcNs[srcPi]
1776  );
1777  const vector T0(A.y()^A.z()), T1(A.z()^A.x());
1778  const scalar detA = A.x() & T0;
1779 
1780  const scalar srcT =
1781  protectedDivide(T0 & (tgtP - srcPs[srcPi]), detA);
1782  const scalar srcU =
1783  protectedDivide(T1 & (tgtP - srcPs[srcPi]), detA);
1784 
1785  // Convert to the triangle's coordinate system
1786  barycentric2D y;
1787  y[srcPi0] = - srcT;
1788  y[srcPi1] = - srcU;
1789  y[srcPi] = 1 + srcT + srcU;
1790  return y;
1791  }
1792  }
1793 
1794  // Above logic means we should never reach here
1796  << "Point " << tgtP << " could not be classified within triangle "
1797  << srcPs << " with projection normals " << srcNs << exit(FatalError);
1798  return barycentric2D::uniform(NaN);
1799 }
1800 
1801 
1803 (
1804  const point& srcP,
1805  const vector& srcN,
1806  const FixedList<point, 3>& tgtPs
1807 )
1808 {
1809  const tensor A(tgtPs[1] - tgtPs[0], tgtPs[2] - tgtPs[0], - srcN);
1810  const scalar detA = det(A);
1811 
1812  const vector T0(A.y()^A.z()), T1(A.z()^A.x());
1813  const tensor T(- T0 - T1, T0, T1);
1814 
1815  const vector detAY = (T & (srcP - tgtPs[0])) + vector(detA, 0, 0);
1816  const scalar maxMagDetAY = mag(detAY[findMax(cmptMag(detAY))]);
1817 
1818  // Usual case. The source normal is not parallel to the target triangle.
1819  // The intersection is a single unambiguous point.
1820  if (maxMagDetAY/vGreat < mag(detA))
1821  {
1822  const vector y = detAY/detA;
1823  return barycentric2D(y.x(), y.y(), y.z());
1824  }
1825 
1826  // Degenerate case. The source normal is parallel to, and the source point
1827  // is out of the plane of, the target triangle. Really, there is no
1828  // intersection, but for the purposes of this function we can say there is
1829  // an intersection and it is arbitrarily far away.
1830  if (maxMagDetAY > 0)
1831  {
1832  const vector y = detAY/maxMagDetAY*vGreat;
1833  return barycentric2D(y.x(), y.y(), y.z());
1834  }
1835 
1836  const tensor2D A2
1837  (
1838  A.x() & A.x(), A.x() & A.y(),
1839  A.y() & A.x(), A.y() & A.y()
1840  );
1841  const scalar detA2(det(A2));
1842 
1843  const tensor2D T2(cof(A2));
1844 
1845  const vector2D detAY2 =
1846  T2 & vector2D(A.x() & (srcP - tgtPs[0]), A.y() & (srcP - tgtPs[0]));
1847  const scalar maxMagDetAY2 = mag(detAY[findMax(cmptMag(detAY2))]);
1848 
1849  // Very degenerate case. The source normal is parallel to, and the source
1850  // point is on the plane of, the target triangle. The intersection is a
1851  // line. Choose the point on the line corresponding to the source point.
1852  if (maxMagDetAY2/vGreat < mag(detA2))
1853  {
1854  const vector2D y2 = detAY2/detA2;
1855  return barycentric2D(1 - cmptSum(y2), y2.x(), y2.y());
1856  }
1857 
1858  // Most degenerate case. The target triangle has collapsed to a line.
1859  // Choose an arbitrary point a long way away. It's possible there's more we
1860  // could do here, but a need has yet to present itself.
1861  return barycentric2D(-vGreat, vGreat, vGreat);
1862 }
1863 
1864 
1866 (
1867  const FixedList<point, 3>& srcPs,
1868  const FixedList<vector, 3>& srcNs,
1869  const FixedList<bool, 3>& srcOwns,
1870  const FixedList<label, 3>& srcTgtPis,
1871  const FixedList<point, 3>& tgtPs,
1872  const FixedList<bool, 3>& tgtOwns,
1873  const FixedList<label, 3>& tgtSrcPis,
1874  DynamicList<point>& srcPoints,
1875  DynamicList<vector>& srcPointNormals,
1876  DynamicList<point>& tgtPoints,
1877  DynamicList<location>& pointLocations,
1878  const bool debug,
1879  const word& writePrefix
1880 )
1881 {
1882  const bool write = writePrefix != word::null;
1883 
1884  if (debug || write)
1885  {
1886  Info<< indent << "Intersecting triangles" << incrIndent << endl;
1887  }
1888 
1889  if (write)
1890  {
1891  writePolygon(writePrefix + "_srcTri", srcPs);
1892  writePolygon(writePrefix + "_tgtTri", tgtPs);
1893  writeTriProjection(writePrefix + "_srcPrj", srcPs, srcNs);
1894  }
1895 
1896  // Determine what source points lie within target edges and vice-versa
1897  FixedList<FixedList<label, 3>, 3> srcInTgtEdge, tgtInSrcEdge;
1898  FixedList<label, 3> srcInTgtTri, tgtInSrcTri;
1899  srcInTgt
1900  (
1901  srcPs, srcNs, srcOwns, srcTgtPis,
1902  tgtPs, tgtOwns,
1903  srcInTgtEdge,
1904  srcInTgtTri
1905  );
1906  tgtInSrc
1907  (
1908  srcPs, srcNs, srcOwns,
1909  tgtPs, tgtOwns, tgtSrcPis,
1910  tgtInSrcEdge,
1911  tgtInSrcTri
1912  );
1913 
1914  if (debug)
1915  {
1916  if (count(srcTgtPis, -1) != 3)
1917  {
1918  Info<< indent << "srcTgtPis=" << srcTgtPis << endl;
1919  }
1920  Info<< indent << "srcInTgtTri=" << srcInTgtTri << endl
1921  << indent << "srcInTgtEdge=" << srcInTgtEdge << endl;
1922  if (count(tgtSrcPis, -1) != 3)
1923  {
1924  Info<< indent << "tgtSrcPis=" << tgtSrcPis << endl;
1925  }
1926  Info<< indent << "tgtInSrcTri=" << tgtInSrcTri << endl
1927  << indent << "tgtInSrcEdge=" << tgtInSrcEdge << endl;
1928  }
1929 
1930  // Generate the locations
1932  (
1933  tgtSrcPis,
1934  srcInTgtEdge,
1935  tgtInSrcEdge,
1936  srcInTgtTri,
1937  tgtInSrcTri,
1938  pointLocations
1939  );
1940 
1941  // Generate the geometry
1943  (
1944  srcPs,
1945  srcNs,
1946  tgtPs,
1947  srcPoints,
1948  srcPointNormals,
1949  tgtPoints,
1950  pointLocations
1951  );
1952 
1953  if (write)
1954  {
1955  writePolygon(writePrefix + "_srcIctFace", srcPoints);
1956  writePolygon(writePrefix + "_tgtIctFace", tgtPoints);
1957  }
1958 
1959  if (debug || write)
1960  {
1961  Info<< decrIndent;
1962  }
1963 }
1964 
1965 // ************************************************************************* //
static const Foam::dimensionedScalar A("A", Foam::dimPressure, 611.21)
static const Foam::dimensionedScalar B("B", Foam::dimless, 18.678)
scalar Cv(const scalar p, const scalar T) const
Definition: HtoEthermo.H:2
scalar y
label n
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:434
#define forAllReverse(list, i)
Reverse loop across all elements in list.
Definition: UList.H:446
Templated 2D Barycentric derived from VectorSpace. Has 3 components, one of which is redundant.
Definition: Barycentric2D.H:53
Graphite solid properties.
Definition: C.H:51
A 1D vector of objects of type <T> that resizes itself as necessary to accept the new objects.
Definition: DynamicList.H:78
void resize(const label)
Alter the addressed list size.
Definition: DynamicListI.H:216
DynamicList< T, SizeInc, SizeMult, SizeDiv > & append(const T &)
Append an element at the end of the list.
Definition: DynamicListI.H:296
void clear()
Clear the addressed list, i.e. set the size to zero.
Definition: DynamicListI.H:236
A 1D vector of objects of type <T> with a fixed size <Size>.
Definition: FixedList.H:78
void size(const label)
Override size to be inconsistent with allocated storage.
Definition: ListI.H:164
An Ostream is an abstract base class for all output systems (streams, files, token lists,...
Definition: Ostream.H:57
const Type & second() const
Return second.
Definition: Pair.H:110
const Type & first() const
Return first.
Definition: Pair.H:98
Templated storage for the roots of polynomial equations, plus flags to indicate the nature of the roo...
Definition: Roots.H:67
void type(const direction i, const rootType t)
Set the type of the i-th root.
Definition: RootsI.H:120
Templated 2D tensor derived from VectorSpace adding construction from 4 components,...
Definition: Tensor2D.H:59
A 2-tuple for storing two objects of different types.
Definition: Tuple2.H:66
T * first()
Return the first entry.
Definition: UILList.H:109
A 1D vector of objects of type <T>, where the size of the vector is known and can be used for subscri...
Definition: UList.H:74
T & first()
Return the first element of the list.
Definition: UListI.H:114
bool empty() const
Return true if the UList is empty (ie, size() is zero)
Definition: UListI.H:325
T & last()
Return the last element of the list.
Definition: UListI.H:128
const Cmpt & y() const
Definition: Vector2DI.H:74
const Cmpt & x() const
Definition: Vector2DI.H:68
static Form uniform(const Cmpt &s)
Return a VectorSpace with all elements = s.
Definition: VectorSpaceI.H:168
const Cmpt & z() const
Definition: VectorI.H:87
const Cmpt & y() const
Definition: VectorI.H:81
const Cmpt & x() const
Definition: VectorI.H:75
Cubic equation of the form a*x^3 + b*x^2 + c*x + d = 0.
Definition: cubicEqn.H:52
Roots< 3 > roots() const
Get the roots.
Definition: cubicEqn.C:32
A face is a list of labels corresponding to mesh vertices.
Definition: face.H:76
Selector class for relaxation factors, solver type and solution.
Definition: solution.H:51
@ BEGIN_LIST
Definition: token.H:109
@ END_LIST
Definition: token.H:110
bool isTgtNotSrcPoint() const
Return whether the location is a target point and not a source.
static location intersection(const label srcEi, const label tgtEi)
Construct an intersection location between a source and a.
bool isTgtPoint() const
Return whether the location is a target point.
label tgtEdgei() const
Return the target edge index.
static location srcTgtPoint(const label srcPi, const label tgtPi)
Construct a source and target point location.
bool isSrcNotTgtPoint() const
Return whether the location is a source point and not a target.
static location tgtPoint(const label tgtPi)
Construct a target point location.
label srcPointi() const
Return the source point index.
label tgtPointi() const
Return the target point index.
bool isSrcAndTgtPoint() const
Return whether the location is a source point and a target point.
bool isIntersection() const
Return whether the location is an intersection.
bool isSrcPoint() const
Return whether the location is a source point.
label srcEdgei() const
Return the source edge index.
static location srcPoint(const label srcPi)
Construct a source point location.
A triangle primitive used to calculate face areas and swept volumes.
Definition: triangle.H:80
vector normal() const
Return unit normal.
Definition: triangleI.H:110
A class for handling words, derived from string.
Definition: word.H:62
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:334
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))
volScalarField & b
Definition: createFields.H:25
const char *const group
Group name for atomic constants.
barycentric2D srcPointTgtTriIntersection(const point &srcP, const vector &srcN, const FixedList< point, 3 > &tgtPs)
Calculate the intersection of a projected source point with a target.
scalar srcPointTgtEdgeOffset(const point &srcP, const vector &srcN, const Pair< point > &tgtPs)
Calculate the signed offset of a projected source point in relation to a.
void tgtInSrc(const FixedList< point, 3 > &srcPs, const FixedList< vector, 3 > &srcNs, const FixedList< bool, 3 > &srcOwns, const FixedList< point, 3 > &tgtPs, const FixedList< bool, 3 > &tgtOwns, const FixedList< label, 3 > &tgtSrcPis, FixedList< FixedList< label, 3 >, 3 > &tgtInSrcEdge, FixedList< label, 3 > &tgtInSrcTri)
Calculate whether the points of the given target triangle project inside or.
Definition: triIntersect.C:560
void generateGeometry(const FixedList< point, 3 > &srcPs, const FixedList< vector, 3 > &srcNs, const FixedList< point, 3 > &tgtPs, DynamicList< point > &srcPoints, DynamicList< vector > &srcPointNormals, DynamicList< point > &tgtPoints, const DynamicList< location > &pointLocations)
Construct the intersection geometry.
barycentric2D srcTriTgtPointIntersection(const FixedList< point, 3 > &srcPs, const FixedList< vector, 3 > &srcNs, const point &tgtP)
Calculate the intersection of a target point with a source triangle's.
Tuple2< bool, vector > solveProjection(const vector &C, const vector &Ct, const vector &Cu, const vector &Cv, const vector &Ctu, const vector &Ctv, const FixedList< label, 3 > groups)
Solve a projection equation.
Definition: triIntersect.C:199
void writeTriProjection(const word &name, const FixedList< point, 3 > &srcTriPs, const FixedList< vector, 3 > &srcTriNs, const label nEdge=20, const label nNormal=20, const scalar lNormal=0.5)
Write a VTK file of a triangle projection.
FixedList< FixedList< scalar, 3 >, 3 > tgtSrcEdgeOffset(const FixedList< point, 3 > &srcPs, const FixedList< vector, 3 > &srcNs, const FixedList< bool, 3 > &srcOwns, const FixedList< point, 3 > &tgtPs, const FixedList< bool, 3 > &tgtOwns)
Calculate the non-dimensional offsets the target points from the source.
Definition: triIntersect.C:398
scalar protectedDivideAndClip01(const scalar a, const scalar b)
Divide two numbers, protect the result from overflowing, and clip the.
Definition: triIntersect.C:56
void srcInTgt(const FixedList< point, 3 > &srcPs, const FixedList< vector, 3 > &srcNs, const FixedList< bool, 3 > &srcOwns, const FixedList< label, 3 > &srcTgtPis, const FixedList< point, 3 > &tgtPs, const FixedList< bool, 3 > &tgtOwns, FixedList< FixedList< label, 3 >, 3 > &srcInTgtEdge, FixedList< label, 3 > &srcInTgtTri)
Calculate whether the points of the given source triangle project inside or.
Definition: triIntersect.C:535
void thisIsOther(const FixedList< label, 3 > &thisOtherPis, FixedList< FixedList< label, 3 >, 3 > &thisInOtherEdge, FixedList< label, 3 > &thisInOtherTri)
Override results of the srcInTgt/tgtInSrc calculations with explicit.
Definition: triIntersect.C:509
FixedList< label, 3 > tgtInSrcTri(const FixedList< point, 3 > &srcPs, const FixedList< vector, 3 > &srcNs, const FixedList< point, 3 > &tgtPs, const FixedList< FixedList< label, 3 >, 3 > &tgtInSrcEdge)
Construct target-point-inside/outside-source-triangle topology from a set.
Definition: triIntersect.C:476
FixedList< FixedList< label, 3 >, 3 > thisInOtherEdge(const FixedList< FixedList< scalar, 3 >, 3 > &thisOtherEdgeOffset)
Construct point-inside/outside-edge topology from a set of point-edge.
Definition: triIntersect.C:432
Ostream & operator<<(Ostream &os, const FixedList< FixedList< Type, 3 >, 3 > &l)
Print 3x3 FixedListLists on one line.
Definition: triIntersect.C:66
bool generateLocations(const FixedList< label, 3 > &tgtSrcPis, const FixedList< FixedList< label, 3 >, 3 > &srcInTgtEdge, const FixedList< FixedList< label, 3 >, 3 > &tgtInSrcEdge, const FixedList< label, 3 > &srcInTgtTri, const FixedList< label, 3 > &tgtInSrcTri, DynamicList< location > &pointLocations)
Construct the intersection topology.
Definition: triIntersect.C:692
vector clipped01(const vector x, const FixedList< label, 3 > groups)
Clip the given vector between values of 0 and 1, and also clip one minus.
Definition: triIntersect.C:83
const scalar maxDot
The maximum dot product between a source point normal and a target plane.
Definition: triIntersect.C:44
FixedList< label, 3 > thisInOtherTri(const FixedList< FixedList< label, 3 >, 3 > &thisInOtherEdge)
Construct point-inside/outside-triangle topology from a set of.
Definition: triIntersect.C:455
scalar srcEdgeTgtPointOffset(const Pair< point > &srcPs, const Pair< vector > &srcNs, const point &tgtP)
Calculate the signed offset of a target point in relation to a projected.
vector solveProjectionGivenT(const vector &C, const vector &Ct, const vector &Cu, const vector &Cv, const vector &Ctu, const vector &Ctv, const FixedList< label, 3 > groups, const scalar t)
Solve a projection equation given a value of the t variable.
Definition: triIntersect.C:155
Type tgtTriInterpolate(const barycentric2D &y, const FixedList< Type, 3 > &tgtPsis)
Use the coordinates obtained from srcPointTgtTriIntersection to interpolate.
void writePolygon(const word &name, const PointField &ps)
Write a VTK file of a polygon.
scalar protectedDivide(const scalar a, const scalar b)
Divide two numbers and protect the result from overflowing.
Definition: triIntersect.C:48
FixedList< FixedList< scalar, 3 >, 3 > srcTgtEdgeOffset(const FixedList< point, 3 > &srcPs, const FixedList< vector, 3 > &srcNs, const FixedList< bool, 3 > &srcOwns, const FixedList< point, 3 > &tgtPs, const FixedList< bool, 3 > &tgtOwns)
Calculate the non-dimensional offsets of the source points from the target.
Definition: triIntersect.C:303
bool orderLocations(const UList< location > &locations, bool isSrcEdge, const label i0, label &nVisited, boolList &visited, labelList &order)
Order intersection locations into a polygon.
Definition: triIntersect.C:584
Pair< scalar > srcEdgeTgtEdgeIntersection(const Pair< point > &srcPs, const Pair< vector > &srcNs, const Pair< point > &tgtPs)
Calculate the intersection of a target edge with a source edge's.
Type srcTriInterpolate(const barycentric2D &y, const FixedList< Type, 3 > &tgtPsis)
Use the coordinates obtained from srcTriTgtPointIntersection to interpolate.
void intersectTris(const FixedList< point, 3 > &srcPs, const FixedList< vector, 3 > &srcNs, const FixedList< bool, 3 > &srcOwns, const FixedList< label, 3 > &srcTgtPis, const FixedList< point, 3 > &tgtPs, const FixedList< bool, 3 > &tgtOwns, const FixedList< label, 3 > &tgtSrcPis, DynamicList< point > &srcPoints, DynamicList< vector > &srcPointNormals, DynamicList< point > &tgtPoints, DynamicList< location > &pointLocations, const bool debug, const word &writePrefix=word::null)
Construct the intersection of a source triangle's projected volume and a.
void write(std::ostream &os, const bool binary, List< floatScalar > &fField)
Write floats ascii or binary.
Namespace for OpenFOAM.
Vector2D< scalar > vector2D
vector2D obtained from generic Vector2D
Definition: vector2D.H:49
dimensionedScalar det(const dimensionedSphericalTensor &dt)
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
static const zero Zero
Definition: zero.H:97
scalar degToRad(const scalar deg)
Convert degrees to radians.
Ostream & decrIndent(Ostream &os)
Decrement the indent level.
Definition: Ostream.H:241
List< label > labelList
A List of labels.
Definition: labelList.H:56
label log2(label i)
Return the log base 2 by successive bit-shifting of the given label.
Definition: label.H:79
dimensionedScalar sign(const dimensionedScalar &ds)
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
label findMax(const ListType &, const label start=0)
Find index of max element (and larger than given element).
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:257
word name(const bool)
Return a word representation of a bool.
Definition: boolIO.C:39
void inplaceReverseList(ListType &list)
Inplace reversal of a list using Swap.
int order(const scalar s)
void cmptMin(FieldField< Field, typename FieldField< Field, Type >::cmptType > &cf, const FieldField< Field, Type > &f)
messageStream Info
Barycentric2D< scalar > barycentric2D
A scalar version of the templated Barycentric2D.
Definition: barycentric2D.H:45
Ostream & incrIndent(Ostream &os)
Increment the indent level.
Definition: Ostream.H:234
void reverse(UList< T > &, const label n)
Definition: UListI.H:334
layerAndWeight min(const layerAndWeight &a, const layerAndWeight &b)
dimensionedSymmTensor cof(const dimensionedSymmTensor &dt)
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
void add(FieldField< Field1, typename typeOfSum< Type1, Type2 >::type > &f, const FieldField< Field1, Type1 > &f1, const FieldField< Field2, Type2 > &f2)
dimensioned< scalar > mag(const dimensioned< Type > &)
layerAndWeight max(const layerAndWeight &a, const layerAndWeight &b)
dimensionSet cmptMag(const dimensionSet &)
Definition: dimensionSet.C:296
void cmptMax(FieldField< Field, typename FieldField< Field, Type >::cmptType > &cf, const FieldField< Field, Type > &f)
label findMin(const ListType &, const label start=0)
Find index of min element (and less than given element).
label findIndex(const ListType &, typename ListType::const_reference, const label start=0)
Find first occurrence of given element and return index,.
error FatalError
void Swap(T &a, T &b)
Definition: Swap.H:43
label count(const ListType &l, typename ListType::const_reference x)
Count the number of occurrences of a value in a list.
Cmpt cmptSum(const VectorSpace< Form, Cmpt, Ncmpts > &vs)
Definition: VectorSpaceI.H:507
void offset(label &lst, const label o)
Ostream & indent(Ostream &os)
Indent stream.
Definition: Ostream.H:227
void T(FieldField< Field, Type > &f1, const FieldField< Field, Type > &f2)
dimensioned< scalar > magSqr(const dimensioned< Type > &)
dimensionedScalar cos(const dimensionedScalar &ds)
uint8_t direction
Definition: direction.H:45
scalar u0
scalar T0
Definition: createFields.H:22
Functions with which to perform an intersection of a pair of triangles; the source and target....