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-2022 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 "mathematicalConstants.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>
66 Ostream& operator<<(Ostream& os, const FixedList<FixedList<Type, 3>, 3>& l)
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 // it's 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  const vector tuv =
228  (
229  C,
230  Ct,
231  Cu,
232  Cv,
233  Ctu,
234  Ctv,
235  {-1, -1, -1},
236  tRoots[tRooti]
237  );
238 
239  if (cmptMax(cmptMag(tuv)) > rootVGreat) continue;
240 
241  tuvs[nTuvs ++] = tuv;
242  }
243 
244  // Apply clipping
245  FixedList<scalar, 3> tuvClippage(NaN);
246  for (label i = 0; i < nTuvs; ++ i)
247  {
248  const vector tuvOld = tuvs[i];
249  tuvs[i] = clipped01(tuvs[i], groups);
250  tuvClippage[i] = cmptSum(cmptMag(tuvs[i] - tuvOld));
251  }
252 
253  // Sort so the least clipped roots come first
254  for (label i = 0; i < nTuvs - 1; ++ i)
255  {
256  for (label j = 0; j < nTuvs - 1; ++ j)
257  {
258  if (tuvClippage[j] > tuvClippage[j + 1])
259  {
260  Swap(tuvs[j], tuvs[j + 1]);
261  Swap(tuvClippage[j], tuvClippage[j + 1]);
262  }
263  }
264  }
265 
266  // Analyse each clipped solution value versus estimated error. If the
267  // value is small relative to the error, then return success.
268  for (label i = 0; i < nTuvs; ++ i)
269  {
270  const scalar t = tuvs[i].x(), u = tuvs[i].y(), v = tuvs[i].z();
271 
272  const scalar magSqrF =
273  magSqr(C + Ct*t + Cu*u + Cv*v + Ctu*t*u + Ctv*t*v);
274  const scalar magSqrErrF =
275  magSqr
276  (
277  1*cmptMag(C)
278  + 2*cmptMag(Ct)*mag(t)
279  + 2*cmptMag(Cu)*mag(u)
280  + 2*cmptMag(Cv)*mag(v)
281  + 3*cmptMag(Ctu)*mag(t)*mag(u)
282  + 3*cmptMag(Ctv)*mag(t)*mag(v)
283  )*small;
284 
285  if (magSqrF < magSqrErrF)
286  {
287  return Tuple2<bool, vector>(true, tuvs[i]);
288  }
289  }
290 
291  // No suitable roots were found. Return failure.
292  return Tuple2<bool, vector>(false, vector::uniform(NaN));
293 }
294 
295 
296 //- Calculate whether the points of the given source triangle project inside or
297 // outside the opposing target triangle
298 void srcInTgt
299 (
300  const FixedList<point, 3>& srcPs,
301  const FixedList<vector, 3>& srcNs,
302  const FixedList<bool, 3>& srcOwns,
303  const FixedList<point, 3>& tgtPs,
304  const FixedList<bool, 3>& tgtOwns,
305  FixedList<FixedList<label, 3>, 3>& srcInTgtEdge,
306  FixedList<label, 3>& srcInTgtTri
307 )
308 {
309  const triPointRef tgtTri(tgtPs[0], tgtPs[1], tgtPs[2]);
310  const vector tgtN = tgtTri.normal();
311 
312  // Check whether the intersections occur in a forward direction
313  FixedList<bool, 3> srcFwd;
314  forAll(srcPs, srcPi)
315  {
316  srcFwd[srcPi] = (srcNs[srcPi] & tgtN) < maxDot;
317  }
318 
319  // For all forward projecting source points, determine which side of
320  // each target edge they project to
321  forAll(srcPs, srcPi)
322  {
323  if (srcFwd[srcPi])
324  {
325  forAll(tgtPs, tgtEi)
326  {
327  const label tgtPi0 = tgtEi, tgtPi1 = (tgtEi + 1) % 3;
328  const scalar o =
330  (
331  srcPs[srcPi],
332  srcNs[srcPi],
333  {tgtPs[tgtPi0], tgtPs[tgtPi1]},
334  tgtOwns[tgtEi]
335  );
336  srcInTgtEdge[srcPi][tgtEi] = o > 0 ? +1 : -1;
337  }
338  }
339  }
340 
341  // For all backward projecting source points, initialise to outside
342  // everything
343  forAll(srcPs, srcPi)
344  {
345  if (!srcFwd[srcPi])
346  {
347  srcInTgtEdge[srcPi] = {-1, -1, -1};
348  }
349  }
350 
351  /*
352  // For all backward projecting source points, initialise to values of the
353  // connected forward pointing points
354  forAll(srcPs, srcPi)
355  {
356  if (!srcFwd[srcPi])
357  {
358  const label srcPi0 = (srcPi + 2) % 3, srcPi1 = (srcPi + 1) % 3;
359 
360  forAll(tgtPs, tgtEi)
361  {
362  srcInTgtEdge[srcPi][tgtEi] =
363  (srcFwd[srcPi0] || srcFwd[srcPi1])
364  && (!srcFwd[srcPi0] || srcInTgtEdge[srcPi0][tgtEi])
365  && (!srcFwd[srcPi1] || srcInTgtEdge[srcPi1][tgtEi])
366  ? +1
367  : -1;
368  };
369  }
370  }
371  */
372 
373  // For source edges with one forward projecting and one backward
374  // projecting point, compute the point and normal where the projection
375  // direction changes and use this to determine the offset of the backward
376  // projecting point
377  forAll(srcPs, srcEi)
378  {
379  const label srcPi0 = srcEi, srcPi1 = (srcEi + 1) % 3;
380 
381  if (srcFwd[srcPi0] != srcFwd[srcPi1])
382  {
383  // Get the source edge parameter and normal at the asymptote where
384  // the normal switches sign relative to the target
385  const scalar srcT =
387  (
388  maxDot - (srcNs[srcPi0] & tgtN),
389  (srcNs[srcPi1] - srcNs[srcPi0]) & tgtN
390  );
391  const point srcP = (1 - srcT)*srcPs[srcPi0] + srcT*srcPs[srcPi1];
392  const vector srcN = (1 - srcT)*srcNs[srcPi0] + srcT*srcNs[srcPi1];
393 
394  forAll(tgtPs, tgtEi)
395  {
396  const label tgtPi0 = tgtEi, tgtPi1 = (tgtEi + 1) % 3;
397  const scalar o =
399  (
400  srcP,
401  srcN,
402  {tgtPs[tgtPi0], tgtPs[tgtPi1]},
403  tgtOwns[tgtEi]
404  );
405  srcInTgtEdge[srcFwd[srcPi0] ? srcPi1 : srcPi0][tgtEi] =
406  o > 0 ? +1 : -1;
407  }
408  }
409  }
410 
411  // Combine edge associations to get triangle associations
412  forAll(srcPs, srcPi)
413  {
414  srcInTgtTri[srcPi] = count(srcInTgtEdge[srcPi], 1) == 3 ? +1 : -1;
415  }
416 }
417 
418 
419 //- Calculate whether the points of the given target triangle project inside or
420 // outside the opposing source triangle
421 void tgtInSrc
422 (
423  const FixedList<point, 3>& srcPs,
424  const FixedList<vector, 3>& srcNs,
425  const FixedList<bool, 3>& srcOwns,
426  const FixedList<point, 3>& tgtPs,
427  const FixedList<bool, 3>& tgtOwns,
428  FixedList<FixedList<label, 3>, 3>& tgtInSrcEdge,
429  FixedList<label, 3>& tgtInSrcTri
430 )
431 {
432  const triPointRef tgtTri(tgtPs[0], tgtPs[1], tgtPs[2]);
433  const vector tgtN = tgtTri.normal();
434 
435  // For all target points, determine which side of each source edge
436  // surface they are on
437  forAll(tgtPs, tgtPi)
438  {
439  forAll(srcPs, srcEi)
440  {
441  const label srcPi0 = srcEi, srcPi1 = (srcEi + 1) % 3;
442  const scalar o =
444  (
445  {srcPs[srcPi0], srcPs[srcPi1]},
446  {srcNs[srcPi0], srcNs[srcPi1]},
447  tgtPs[tgtPi],
448  srcOwns[srcEi]
449  );
450  tgtInSrcEdge[tgtPi][srcEi] = o > 0 ? +1 : -1;
451  }
452  }
453 
454  // Combine edge associations to get triangle associations
455  forAll(tgtPs, tgtPi)
456  {
457  tgtInSrcTri[tgtPi] = count(tgtInSrcEdge[tgtPi], 1) == 3 ? +1 : -1;
458  }
459 
460  // Check whether the intersections occur in a forward direction
461  forAll(tgtPs, tgtPi)
462  {
463  if (tgtInSrcTri[tgtPi] == 1)
464  {
465  const barycentric2D srcTs =
466  srcTriTgtPointIntersection(srcPs, srcNs, tgtPs[tgtPi]);
467  const vector srcN = srcTriInterpolate(srcTs, srcNs);
468  const bool tgtFwd = (srcN & tgtN) < maxDot;
469  tgtInSrcTri[tgtPi] = tgtFwd ? +1 : -1;
470  }
471  }
472 }
473 
474 
475 //- Override results of the srcInTgt/tgtInSrc calculations with explicit
476 // connections between points on either side
477 void thisIsOther
478 (
479  const FixedList<label, 3>& thisOtherPis,
480  FixedList<FixedList<label, 3>, 3>& thisInOtherEdge,
481  FixedList<label, 3>& thisInOtherTri
482 )
483 {
484  forAll(thisOtherPis, thisPi)
485  {
486  const label otherPi = thisOtherPis[thisPi];
487 
488  if (otherPi != -1)
489  {
490  const label otherEi0 = (otherPi + 2) % 3, otherEi1 = otherPi;
491 
492  thisInOtherTri[thisPi] = 0;
493 
494  thisInOtherEdge[thisPi][otherEi0] = 0;
495  thisInOtherEdge[thisPi][otherEi1] = 0;
496  }
497  }
498 }
499 
500 
501 //- Order intersection locations into a polygon
502 bool orderLocations
503 (
504  const UList<location>& locations,
505  bool isSrcEdge,
506  const label i0,
507  label& nVisited,
508  boolList& visited,
509  labelList& order
510 )
511 {
512  // Mark this location as visited
513  order[nVisited ++] = i0;
514  visited[i0] = true;
515 
516  // Get the index of the edge attached to this point
517  const location& l0 = locations[i0];
518  const label ei0 =
519  isSrcEdge
520  ? (l0.isSrcPoint() ? l0.srcPointi() : l0.srcEdgei())
521  : (l0.isTgtPoint() ? (l0.tgtPointi() + 2) % 3 : l0.tgtEdgei());
522 
523  // Terminate if connected back to the first location
524  {
525  const label i1 = order.first();
526 
527  const location& l1 = locations[i1];
528 
529  if
530  (
531  i0 != order.first()
532  && !(l1.isSrcNotTgtPoint() && !isSrcEdge)
533  && !(l1.isTgtNotSrcPoint() && isSrcEdge)
534  )
535  {
536  const location& l1 = locations[i1];
537  const label ei1 =
538  isSrcEdge
539  ? (l1.isSrcPoint() ? (l1.srcPointi() + 2) % 3 : l1.srcEdgei())
540  : (l1.isTgtPoint() ? l1.tgtPointi() : l1.tgtEdgei());
541 
542  if (ei0 == ei1)
543  {
544  return true;
545  }
546  }
547  }
548 
549  // Search for the next connected location and recurse if found
550  forAll(locations, i1)
551  {
552  if (!visited[i1])
553  {
554  const location& l1 = locations[i1];
555 
556  if
557  (
558  !(l1.isSrcNotTgtPoint() && !isSrcEdge)
559  && !(l1.isTgtNotSrcPoint() && isSrcEdge)
560  )
561  {
562  const label ei1 =
563  isSrcEdge
564  ? (l1.isSrcPoint() ? (l1.srcPointi() + 2) % 3 : l1.srcEdgei())
565  : (l1.isTgtPoint() ? l1.tgtPointi() : l1.tgtEdgei());
566 
567  if (ei0 == ei1)
568  {
569  auto branch = [&](const bool isSrcEdge)
570  {
571  return orderLocations
572  (
573  locations,
574  isSrcEdge,
575  i1,
576  nVisited,
577  visited,
578  order
579  );
580  };
581 
582  if
583  (
584  (
585  !l1.isSrcAndTgtPoint()
586  && branch(l1.isIntersection() != isSrcEdge)
587  )
588  || (
589  l1.isSrcAndTgtPoint()
590  && (branch(true) || branch(false))
591  )
592  )
593  {
594  return true;
595  }
596  }
597  }
598  }
599  }
600 
601  // This branch failed to find a connected location. Un-visit this location.
602  order[-- nVisited] = -1;
603  visited[i0] = false;
604 
605  return false;
606 }
607 
608 
610 (
611  const FixedList<point, 3>& srcPs,
612  const FixedList<vector, 3>& srcNs,
613  const FixedList<point, 3>& tgtPs,
614  DynamicList<point>& srcPoints,
615  DynamicList<vector>& srcPointNormals,
616  DynamicList<point>& tgtPoints,
617  const DynamicList<location>& pointLocations
618 )
619 {
620  srcPoints.resize(pointLocations.size());
621  srcPointNormals.resize(pointLocations.size());
622  tgtPoints.resize(pointLocations.size());
623 
624  forAll(pointLocations, pointi)
625  {
626  const location& l = pointLocations[pointi];
627 
628  if (l.isSrcAndTgtPoint())
629  {
630  const point& srcP = srcPs[l.srcPointi()];
631  const vector& srcN = srcNs[l.srcPointi()];
632  const point& tgtP = tgtPs[l.tgtPointi()];
633 
634  srcPoints[pointi] = srcP;
635  srcPointNormals[pointi] = srcN;
636  tgtPoints[pointi] = tgtP;
637  }
638  else if (l.isSrcPoint())
639  {
640  const point& srcP = srcPs[l.srcPointi()];
641  const vector& srcN = srcNs[l.srcPointi()];
642  const barycentric2D tgtTs =
643  srcPointTgtTriIntersection(srcP, srcN, tgtPs);
644 
645  srcPoints[pointi] = srcP;
646  srcPointNormals[pointi] = srcN;
647  tgtPoints[pointi] = tgtTriInterpolate(tgtTs, tgtPs);
648  }
649  else if (l.isTgtPoint())
650  {
651  const point& tgtP = tgtPs[l.tgtPointi()];
652  const barycentric2D srcTs =
653  srcTriTgtPointIntersection(srcPs, srcNs, tgtP);
654 
655  srcPoints[pointi] = srcTriInterpolate(srcTs, srcPs);
656  srcPointNormals[pointi] = srcTriInterpolate(srcTs, srcNs);
657  tgtPoints[pointi] = tgtP;
658  }
659  else // if (l.isIntersection())
660  {
661  const label srcPi0 = l.srcEdgei(), srcPi1 = (srcPi0 + 1) % 3;
662  const label tgtPi0 = l.tgtEdgei(), tgtPi1 = (tgtPi0 + 1) % 3;
663 
664  const Pair<scalar> ts =
666  (
667  {srcPs[srcPi0], srcPs[srcPi1]},
668  {srcNs[srcPi0], srcNs[srcPi1]},
669  {tgtPs[tgtPi0], tgtPs[tgtPi1]}
670  );
671  const scalar srcT = ts.first(), tgtT = ts.second();
672 
673  srcPoints[pointi] =
674  (1 - srcT)*srcPs[srcPi0] + srcT*srcPs[srcPi1];
675  srcPointNormals[pointi] =
676  (1 - srcT)*srcNs[srcPi0] + srcT*srcNs[srcPi1];
677  tgtPoints[pointi] =
678  (1 - tgtT)*tgtPs[tgtPi0] + tgtT*tgtPs[tgtPi1];
679  }
680  }
681 }
682 
683 
684 } // End namespace triIntersect
685 } // End namespace Foam
686 
687 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
688 
690 (
691  const word& name,
692  const FixedList<point, 3>& srcPs,
693  const FixedList<vector, 3>& srcNs,
694  const label nEdge,
695  const label nNormal,
696  const scalar lNormal
697 )
698 {
699  scalar lengthScale = 0;
700  for (label i = 0; i < 3; ++ i)
701  {
702  lengthScale = max(lengthScale, mag(srcPs[i] - srcPs[(i + 1) % 3]));
703  }
704 
705  const label nu = nEdge, nv = nNormal;
706  const scalar u0 = 0, u1 = 1;
707  const scalar v0 = -lNormal/2*lengthScale, v1 = lNormal/2*lengthScale;
708 
709  pointField ps(3*(nu + 1)*(nv + 1));
710  for (label i = 0; i < 3; ++ i)
711  {
712  const point& p0 = srcPs[i], p1 = srcPs[(i + 1) % 3];
713  const vector& n0 = srcNs[i], n1 = srcNs[(i + 1) % 3];
714 
715  for (label iu = 0; iu <= nu; ++ iu)
716  {
717  const scalar u = u0 + (u1 - u0)*scalar(iu)/nu;
718  for (label iv = 0; iv <= nv; ++ iv)
719  {
720  const scalar v = v0 + (v1 - v0)*scalar(iv)/nv;
721  const vector x = p0 + (p1 - p0)*u + (n0 + (n1 - n0)*u)*v;
722  ps[i*(nu + 1)*(nv + 1) + iu*(nv + 1) + iv] = x;
723  }
724  }
725  }
726 
727  faceList fs(3*nu*nv);
728  for (label i = 0; i < 3; ++ i)
729  {
730  for (label iu = 0; iu < nu; ++ iu)
731  {
732  for (label iv = 0; iv < nv; ++ iv)
733  {
734  fs[i*nu*nv + iu*nv + iv] =
735  face
736  ({
737  i*(nu + 1)*(nv + 1) + (nv + 1)*iu + iv,
738  i*(nu + 1)*(nv + 1) + (nv + 1)*iu + iv + 1,
739  i*(nu + 1)*(nv + 1) + (nv + 1)*(iu + 1) + iv + 1,
740  i*(nu + 1)*(nv + 1) + (nv + 1)*(iu + 1) + iv
741  });
742  }
743  }
744  }
745 
746  Info<< indent << "Writing face to " << name + ".vtk" << endl;
748  (
749  name + ".vtk",
750  name,
751  false,
752  ps,
753  labelList(),
754  labelListList(),
755  fs
756  );
757 }
758 
759 
761 (
762  const Pair<point>& srcPs,
763  const Pair<vector>& srcNs,
764  const point& tgtP
765 )
766 {
767  const tensor A(srcPs[1] - srcPs[0], srcNs[0], srcNs[1]);
768  const scalar detA = det(A);
769 
770  const tensor T(A.y()^A.z(), A.z()^A.x(), A.x()^A.y());
771 
772  const vector detAY = T & (tgtP - srcPs[0]);
773  const scalar Yx = protectedDivideAndClip01(detAY.x(), detA);
774 
775  const scalar offset = Yx*detAY.y() - (1 - Yx)*detAY.z();
776 
777  return offset == 0 ? - vSmall : offset;
778 }
779 
780 
782 (
783  const Pair<point>& srcPs,
784  const Pair<vector>& srcNs,
785  const point& tgtP,
786  const bool srcDirection
787 )
788 {
789  if (srcDirection)
790  {
791  return srcEdgeTgtPointOffset(srcPs, srcNs, tgtP);
792  }
793  else
794  {
795  return - srcEdgeTgtPointOffset(reverse(srcPs), reverse(srcNs), tgtP);
796  }
797 }
798 
799 
801 (
802  const point& srcP,
803  const vector& srcN,
804  const Pair<point>& tgtPs
805 )
806 {
807  const tensor A(srcN, tgtPs[1] - tgtPs[0], srcN^(tgtPs[1] - tgtPs[0]));
808  const scalar detA = det(A);
809 
810  const tensor T(A.y()^A.z(), A.z()^A.x(), A.x()^A.y());
811 
812  const vector detAY = T & (tgtPs[0] - srcP);
813 
814  const scalar offset = protectedDivide(detAY.z(), detA);
815 
816  return offset == 0 ? - vSmall : offset;
817 }
818 
819 
821 (
822  const point& srcP,
823  const vector& srcN,
824  const Pair<point>& tgtPs,
825  const bool tgtDirection
826 )
827 {
828  if (tgtDirection)
829  {
830  return srcPointTgtEdgeOffset(srcP, srcN, tgtPs);
831  }
832  else
833  {
834  return - srcPointTgtEdgeOffset(srcP, srcN, reverse(tgtPs));
835  }
836 }
837 
838 
840 (
841  const Pair<point>& srcPs,
842  const Pair<vector>& srcNs,
843  const Pair<point>& tgtPs
844 )
845 {
846  scalar srcT, tgtT;
847 
848  #ifndef BISECT
851  (
852  srcPs[0] - tgtPs[0],
853  srcPs[1] - srcPs[0],
854  tgtPs[0] - tgtPs[1],
855  srcNs[0],
856  Zero,
857  srcNs[1] - srcNs[0],
858  {0, 1, -1}
859  );
860  if (solution.first())
861  {
862  // If the analytical solution succeeds, then use the result
863  srcT = solution.second().x();
864  tgtT = solution.second().y();
865  }
866  else
867  #endif
868  {
869  // If the analytical solution fails, then solve by bisection
870 
871  // !!! This method, whilst elegant, isn't sufficiently robust. The
872  // srcPointTgtEdgeOffset calculation is not as reliable as
873  // srcEdgeTgtPointOffset. So, we can't bisect the source edge to get
874  // srcT and then reuse the solveProjectionGivenT stuff. We need to
875  // bisect the target edge to get tgtT, and then have a specific process
876  // for calculating srcT from tgtT.
877  /*
878  scalar srcT0 = 0, srcT1 = 1;
879 
880  const scalar o0 = srcPointTgtEdgeOffset(srcPs[0], srcNs[0], tgtPs);
881  const scalar o1 = srcPointTgtEdgeOffset(srcPs[1], srcNs[1], tgtPs);
882 
883  const scalar s = o0 > o1 ? +1 : -1;
884 
885  for (label i = 0; i < ceil(std::log2(1/small)); ++ i)
886  {
887  const scalar srcT = (srcT0 + srcT1)/2;
888  const vector srcP = (1 - srcT)*srcPs[0] + srcT*srcPs[1];
889  const vector srcN = (1 - srcT)*srcNs[0] + srcT*srcNs[1];
890 
891  const scalar o = s*srcPointTgtEdgeOffset(srcP, srcN, tgtPs);
892 
893  if (o > 0)
894  {
895  srcT0 = srcT;
896  }
897  else
898  {
899  srcT1 = srcT;
900  }
901  }
902 
903  srcT = (srcT0 + srcT1)/2;
904  tgtT =
905  solveProjectionGivenT
906  (
907  srcPs[0] - tgtPs[0],
908  srcPs[1] - srcPs[0],
909  tgtPs[0] - tgtPs[1],
910  srcNs[0],
911  Zero,
912  srcNs[1] - srcNs[0],
913  {0, 1, -1},
914  srcT
915  ).y();
916  */
917 
918  // !!! This method appears robust
919  scalar tgtT0 = 0, tgtT1 = 1;
920 
921  const scalar o0 = srcEdgeTgtPointOffset(srcPs, srcNs, tgtPs[0]);
922  const scalar o1 = srcEdgeTgtPointOffset(srcPs, srcNs, tgtPs[1]);
923 
924  const scalar s = o0 > o1 ? +1 : -1;
925 
926  for (label i = 0; i < ceil(std::log2(1/small)); ++ i)
927  {
928  const scalar tgtT = (tgtT0 + tgtT1)/2;
929  const vector tgtP = (1 - tgtT)*tgtPs[0] + tgtT*tgtPs[1];
930 
931  const scalar o = s*srcEdgeTgtPointOffset(srcPs, srcNs, tgtP);
932 
933  if (o > 0)
934  {
935  tgtT0 = tgtT;
936  }
937  else
938  {
939  tgtT1 = tgtT;
940  }
941  }
942 
943  tgtT = (tgtT0 + tgtT1)/2;
944 
945  // Solve for the corresponding source edge coordinate
946  const vector srcDP = srcPs[1] - srcPs[0];
947  const vector tgtP = (1 - tgtT)*tgtPs[0] + tgtT*tgtPs[1];
948 
949  const tensor A(srcDP, srcNs[0], srcNs[1]);
950  const scalar detA = det(A);
951 
952  const vector Tx = A.y()^A.z();
953 
954  const scalar magDetAYx = sign(detA)*(Tx & (tgtP - srcPs[0]));
955 
956  const scalar srcTStar = protectedDivideAndClip01(magDetAYx, mag(detA));
957 
958  const vector srcN = (1 - srcTStar)*srcNs[0] + srcTStar*srcNs[1];
959 
960  const vector srcDPPerpN = srcDP - (srcDP & srcN)*srcN;
961 
962  srcT =
964  (
965  (tgtP - srcPs[0]) & srcDPPerpN,
966  srcDP & srcDPPerpN
967  );
968  }
969 
970  /*
971  // Check that the points match
972  {
973  const point srcP = (1 - srcT)*srcPs[0] + srcT*srcPs[1];
974  const point srcN = (1 - srcT)*srcNs[0] + srcT*srcNs[1];
975  const point tgtP = (1 - tgtT)*tgtPs[0] + tgtT*tgtPs[1];
976  const scalar srcU = ((tgtP - srcP) & srcN)/magSqr(srcN);
977  const point srcQ = srcP + srcU*srcN;
978  Info<< "srcT=" << srcT << ", tgtT=" << tgtT
979  << ", err=" << magSqr(srcQ - tgtP) << endl;
980  }
981  */
982 
983  return Pair<scalar>(srcT, tgtT);
984 }
985 
986 
988 (
989  const FixedList<point, 3>& srcPs,
990  const FixedList<vector, 3>& srcNs,
991  const point& tgtP
992 )
993 {
994  auto srcPN = []
995  (
996  const FixedList<vector, 3>& srcPNs,
997  const vector2D& srcTs
998  )
999  {
1000  const scalar srcT0 = 1 - srcTs.x() - srcTs.y();
1001  return srcT0*srcPNs[0] + srcTs.x()*srcPNs[1] + srcTs.y()*srcPNs[2];
1002  };
1003 
1004  auto srcEdgePNs = [srcPN]
1005  (
1006  const FixedList<vector, 3>& srcPNs,
1007  const vector2D& srcTs0,
1008  const vector2D& srcTs1
1009  )
1010  {
1011  return Pair<vector>(srcPN(srcPNs, srcTs0), srcPN(srcPNs, srcTs1));
1012  };
1013 
1014  auto offset = [&](const vector2D& srcTs0, const vector2D& srcTs1)
1015  {
1016  return srcEdgeTgtPointOffset
1017  (
1018  srcEdgePNs(srcPs, srcTs0, srcTs1),
1019  srcEdgePNs(srcNs, srcTs0, srcTs1),
1020  tgtP
1021  );
1022  };
1023 
1024  const scalar oA = offset(vector2D(0, 0), vector2D(1, 0));
1025  const scalar oB = offset(vector2D(1, 0), vector2D(0, 1));
1026  const scalar oC = offset(vector2D(0, 1), vector2D(0, 0));
1027  const FixedList<scalar, 3> offsets({oA, oB, oC});
1028 
1029  // If inside the triangle (or outside if the triangle is inverted) ...
1030  if (offsets[findMin(offsets)] >= 0 || offsets[findMax(offsets)] <= 0)
1031  {
1032  scalar srcT, srcU;
1033 
1034  #ifndef BISECT
1037  (
1038  srcPs[0] - tgtP,
1039  srcNs[0],
1040  srcPs[1] - srcPs[0],
1041  srcPs[2] - srcPs[0],
1042  srcNs[1] - srcNs[0],
1043  srcNs[2] - srcNs[0],
1044  {-1, 0, 0}
1045  );
1046  if (solution.first())
1047  {
1048  // If the analytical solution succeeds, then use the result
1049  srcT = solution.second().y();
1050  srcU = solution.second().z();
1051  }
1052  else
1053  #endif
1054  {
1055  // If the analytical solution fails, then solve by bisection
1056  const scalar sign = offsets[findMin(offsets)] > 0 ? +1 : -1;
1057 
1058  vector2D srcTsA(0, 0), srcTsB(1, 0), srcTsC(0, 1);
1059 
1060  for (label i = 0; i < ceil(std::log2(1/small)); ++ i)
1061  {
1062  const vector2D srcTsAB = (srcTsA + srcTsB)/2;
1063  const vector2D srcTsBC = (srcTsB + srcTsC)/2;
1064  const vector2D srcTsCA = (srcTsC + srcTsA)/2;
1065 
1066  const scalar oA = sign*offset(srcTsCA, srcTsAB);
1067  const scalar oB = sign*offset(srcTsAB, srcTsBC);
1068  const scalar oC = sign*offset(srcTsBC, srcTsCA);
1069  const FixedList<scalar, 3> offsets({oA, oB, oC});
1070 
1071  const label offsetMini = findMin(offsets);
1072  if (offsets[offsetMini] > 0)
1073  {
1074  srcTsA = srcTsAB;
1075  srcTsB = srcTsBC;
1076  srcTsC = srcTsCA;
1077  }
1078  else if (offsetMini == 0)
1079  {
1080  srcTsC = srcTsCA;
1081  srcTsB = srcTsAB;
1082  }
1083  else if (offsetMini == 1)
1084  {
1085  srcTsA = srcTsAB;
1086  srcTsC = srcTsBC;
1087  }
1088  else if (offsetMini == 2)
1089  {
1090  srcTsB = srcTsBC;
1091  srcTsA = srcTsCA;
1092  }
1093  }
1094 
1095  srcT = (srcTsA[0] + srcTsB[0] + srcTsC[0])/3;
1096  srcU = (srcTsA[1] + srcTsB[1] + srcTsC[1])/3;
1097  }
1098 
1099  return barycentric2D(1 - srcT - srcU, srcT, srcU);
1100  }
1101 
1102  // If outside an edge ...
1103  forAll(srcPs, srcEi)
1104  {
1105  const label srcEi0 = (srcEi + 2) % 3, srcEi1 = (srcEi + 1) % 3;
1106 
1107  if
1108  (
1109  offsets[srcEi] <= 0
1110  && offsets[srcEi0] >= 0
1111  && offsets[srcEi1] >= 0
1112  )
1113  {
1114  const label srcPi0 = srcEi, srcPi1 = (srcEi + 1) % 3;
1115  const label srcPiOpp = (srcEi + 2) % 3;
1116 
1117  scalar srcT, srcU;
1118 
1119  #ifndef BISECT
1122  (
1123  srcPs[srcPi0] - tgtP,
1124  srcPs[srcPi1] - srcPs[srcPi0],
1125  srcPs[srcPi0] - srcPs[srcPiOpp],
1126  srcNs[srcPi0],
1127  srcPs[srcPi1] - srcPs[srcPi0],
1128  srcNs[srcPi1] - srcNs[srcPi0],
1129  {0, -1, -1}
1130  );
1131  if (solution.first())
1132  {
1133  // If the analytical solution succeeds, then use the result
1134  srcT = solution.second().x();
1135  srcU = solution.second().y();
1136  }
1137  else
1138  #endif
1139  {
1140  // If the analytical solution fails, then solve by bisection
1141  const vector2D srcTsOpp(srcPiOpp == 1, srcPiOpp == 2);
1142  const vector2D srcTs0(srcPi0 == 1, srcPi0 == 2);
1143  const vector2D srcTs1(srcPi1 == 1, srcPi1 == 2);
1144 
1145  scalar srcT0 = 0, srcT1 = 1;
1146 
1147  for (label i = 0; i < ceil(std::log2(1/small)); ++ i)
1148  {
1149  const scalar srcT = (srcT0 + srcT1)/2;
1150  const vector2D srcTs01(srcTs0*(1 - srcT) + srcTs1*srcT);
1151 
1152  const scalar o = offset(srcTsOpp, srcTs01);
1153 
1154  if (o > 0)
1155  {
1156  srcT0 = srcT;
1157  }
1158  else
1159  {
1160  srcT1 = srcT;
1161  }
1162  }
1163 
1164  srcT = (srcT0 + srcT1)/2;
1165  srcU =
1167  (
1168  srcPs[srcPi0] - tgtP,
1169  srcPs[srcPi1] - srcPs[srcPi0],
1170  srcPs[srcPi0] - srcPs[srcPiOpp],
1171  srcNs[srcPi0],
1172  srcPs[srcPi1] - srcPs[srcPi0],
1173  srcNs[srcPi1] - srcNs[srcPi0],
1174  {0, -1, -1},
1175  srcT
1176  ).y();
1177  }
1178 
1179  // Convert to the triangle's coordinate system
1180  barycentric2D y;
1181  y[srcPiOpp] = - srcU;
1182  y[srcPi0] = (1 + srcU)*(1 - srcT);
1183  y[srcPi1] = (1 + srcU)*srcT;
1184  return y;
1185  }
1186  }
1187 
1188  // If outside a corner ...
1189  forAll(srcPs, srcPi)
1190  {
1191  const label srcEiOpp = (srcPi + 1) % 3;
1192  const label srcEi0 = (srcPi + 2) % 3, srcEi1 = srcPi;
1193 
1194  if
1195  (
1196  offsets[srcEiOpp] >= 0
1197  && offsets[srcEi0] <= 0
1198  && offsets[srcEi1] <= 0
1199  )
1200  {
1201  // Solve for the intersection coordinates directly
1202  const label srcPi0 = (srcPi + 2) % 3, srcPi1 = (srcPi + 1) % 3;
1203 
1204  const tensor A
1205  (
1206  srcPs[srcPi] - srcPs[srcPi0],
1207  srcPs[srcPi] - srcPs[srcPi1],
1208  srcNs[srcPi]
1209  );
1210  const vector T0(A.y()^A.z()), T1(A.z()^A.x());
1211  const scalar detA = A.x() & T0;
1212 
1213  const scalar srcT =
1214  protectedDivide(T0 & (tgtP - srcPs[srcPi]), detA);
1215  const scalar srcU =
1216  protectedDivide(T1 & (tgtP - srcPs[srcPi]), detA);
1217 
1218  // Convert to the triangle's coordinate system
1219  barycentric2D y;
1220  y[srcPi0] = - srcT;
1221  y[srcPi1] = - srcU;
1222  y[srcPi] = 1 + srcT + srcU;
1223  return y;
1224  }
1225  }
1226 
1227  // Above logic means we should never reach here
1229  << "Point " << tgtP << " could not be classified within triangle "
1230  << srcPs << " with projection normals " << srcNs << exit(FatalError);
1231  return barycentric2D::uniform(NaN);
1232 }
1233 
1234 
1237  const point& srcP,
1238  const vector& srcN,
1239  const FixedList<point, 3>& tgtPs
1240 )
1241 {
1242  const tensor A(tgtPs[1] - tgtPs[0], tgtPs[2] - tgtPs[0], - srcN);
1243  const scalar detA = det(A);
1244 
1245  const vector T0(A.y()^A.z()), T1(A.z()^A.x());
1246  const tensor T(- T0 - T1, T0, T1);
1247 
1248  const vector detAY = (T & (srcP - tgtPs[0])) + vector(detA, 0, 0);
1249 
1250  const scalar maxMagDetAY = mag(detAY[findMax(cmptMag(detAY))]);
1251 
1252  const vector y =
1253  maxMagDetAY/vGreat < mag(detA)
1254  ? detAY/detA
1255  : detAY/maxMagDetAY*vGreat;
1256 
1257  return barycentric2D(y.x(), y.y(), y.z());
1258 }
1259 
1260 
1263  const FixedList<point, 3>& srcPs,
1264  const FixedList<vector, 3>& srcNs,
1265  const FixedList<bool, 3>& srcOwns,
1266  const FixedList<label, 3>& srcTgtPis,
1267  const FixedList<point, 3>& tgtPs,
1268  const FixedList<bool, 3>& tgtOwns,
1269  const FixedList<label, 3>& tgtSrcPis,
1270  DynamicList<point>& srcPoints,
1271  DynamicList<vector>& srcPointNormals,
1272  DynamicList<point>& tgtPoints,
1273  DynamicList<location>& pointLocations,
1274  const bool debug,
1275  const word& writePrefix
1276 )
1277 {
1278  const bool write = writePrefix != word::null;
1279 
1280  if (debug || write)
1281  {
1282  Info<< indent << "Intersecting triangles" << incrIndent << endl;
1283  }
1284 
1285  if (write)
1286  {
1287  writePolygon(writePrefix + "_srcTri", srcPs);
1288  writePolygon(writePrefix + "_tgtTri", tgtPs);
1289  writeTriProjection(writePrefix + "_srcPrj", srcPs, srcNs);
1290  }
1291 
1292  // Step 1: Figure out what source points lie within which target triangles
1293  // and edges and vice-versa
1294  FixedList<FixedList<label, 3>, 3> srcInTgtEdge, tgtInSrcEdge;
1295  FixedList<label, 3> srcInTgtTri, tgtInSrcTri;
1296  srcInTgt(srcPs, srcNs, srcOwns, tgtPs, tgtOwns, srcInTgtEdge, srcInTgtTri);
1297  thisIsOther(srcTgtPis, srcInTgtEdge, srcInTgtTri);
1298  tgtInSrc(srcPs, srcNs, srcOwns, tgtPs, tgtOwns, tgtInSrcEdge, tgtInSrcTri);
1299  thisIsOther(tgtSrcPis, tgtInSrcEdge, tgtInSrcTri);
1300 
1301  if (debug)
1302  {
1303  if (count(srcTgtPis, -1) != 3)
1304  {
1305  Info<< indent << "srcTgtPis=" << srcTgtPis << endl;
1306  }
1307  Info<< indent << "srcInTgtTri=" << srcInTgtTri << endl
1308  << indent << "srcInTgtEdge=" << srcInTgtEdge << endl;
1309  if (count(tgtSrcPis, -1) != 3)
1310  {
1311  Info<< indent << "tgtSrcPis=" << tgtSrcPis << endl;
1312  }
1313  Info<< indent << "tgtInSrcTri=" << tgtInSrcTri << endl
1314  << indent << "tgtInSrcEdge=" << tgtInSrcEdge << endl;
1315  }
1316 
1317  // Step 2: Process trivial rejection cases
1318  bool noIntersection = false;
1319  {
1320  // If the entire target triangle is outside or on the same source edge
1321  // then there can be no intersection
1322  forAll(srcPs, srcEi)
1323  {
1324  bool outside = true;
1325  forAll(tgtPs, tgtPi)
1326  {
1327  if (tgtInSrcEdge[tgtPi][srcEi] == 1)
1328  {
1329  outside = false;
1330  break;
1331  }
1332  }
1333  if (outside)
1334  {
1335  noIntersection = true;
1336  break;
1337  }
1338  }
1339 
1340  // If all source points are outside all target edges this indicates
1341  // that the triangles are oppositely oriented, in which case there can
1342  // also be no intersection
1343  if (count(srcInTgtEdge, {-1, -1, -1}) == 3)
1344  {
1345  noIntersection = true;
1346  }
1347  }
1348 
1349  // Step 3: Walk around the target edges to form the intersection polygon
1350  if (!noIntersection)
1351  {
1352  // Step 3.1: Define functions
1353 
1354  // Add crossing point locations, inserting source points as necessary
1355  auto addPointLocations = [&pointLocations]
1356  (
1357  const location l1,
1358  const location l2 = location(),
1359  const bool add = true
1360  )
1361  {
1362  if (!pointLocations.empty())
1363  {
1364  const location l0 = pointLocations.last();
1365 
1366  if (l0.isIntersection() || l0.isSrcAndTgtPoint())
1367  {
1368  const label srcEi0 =
1369  l0.isIntersection()
1370  ? l0.srcEdgei()
1371  : (l0.srcPointi() + 2) % 3;
1372  const label tgtEi0 =
1373  l0.isIntersection()
1374  ? l0.tgtEdgei()
1375  : l0.tgtPointi();
1376 
1377  const label srcEi1 =
1378  l1.isIntersection()
1379  ? l1.srcEdgei()
1380  : l1.srcPointi();
1381  const label tgtEi1 =
1382  l1.isIntersection()
1383  ? l1.tgtEdgei()
1384  : (l1.tgtPointi() + 2) % 3;
1385 
1386  if
1387  (
1388  (l0.isIntersection() && l1.isIntersection())
1389  || tgtEi0 != tgtEi1
1390  )
1391  {
1392  for
1393  (
1394  label srcEj = srcEi0;
1395  srcEj != srcEi1;
1396  srcEj = (srcEj + 2) % 3
1397  )
1398  {
1399  pointLocations.append(location::srcPoint(srcEj));
1400  }
1401  }
1402  }
1403  }
1404 
1405  if (!add) return;
1406 
1407  pointLocations.append(l1);
1408 
1409  if (!l2.isNull())
1410  {
1411  pointLocations.append(l2);
1412  }
1413  };
1414 
1415  // One target point is within the source triangle and one is not
1416  auto inTriToOut = [&addPointLocations,&srcInTgtEdge]
1417  (
1418  const label tgtEi,
1419  const label tgtOutSrcEi1,
1420  const label tgtOutSrcPi1,
1421  const bool reverse
1422  )
1423  {
1424  const label srcEi =
1425  tgtOutSrcEi1 != -1
1426  ? tgtOutSrcEi1
1427  : srcInTgtEdge[tgtOutSrcPi1][tgtEi] == 1
1428  ? (tgtOutSrcPi1 + 2*reverse) % 3
1429  : (tgtOutSrcPi1 + 2*!reverse) % 3;
1430 
1431  addPointLocations(location::intersection(srcEi, tgtEi));
1432 
1433  return true;
1434  };
1435 
1436  // One target point is a source point and the other is outside a source
1437  // edge
1438  auto isPointToOutEdge = [&addPointLocations,&srcInTgtEdge]
1439  (
1440  const label tgtEi,
1441  const label tgtIsSrcPi0,
1442  const label tgtOutSrcEi1,
1443  const bool reverse
1444  )
1445  {
1446  const label srcEi0Next = (tgtIsSrcPi0 + 2*reverse) % 3;
1447  const label srcEi0Opp = (tgtIsSrcPi0 + 1) % 3;
1448 
1449  if
1450  (
1451  srcInTgtEdge[(tgtIsSrcPi0 + 1) % 3][tgtEi] == -1
1452  && srcInTgtEdge[(tgtIsSrcPi0 + 2) % 3][tgtEi] == -1
1453  )
1454  {
1455  return false;
1456  }
1457 
1458  if (tgtOutSrcEi1 == srcEi0Next)
1459  {
1460  return false;
1461  }
1462 
1463  if (tgtOutSrcEi1 == srcEi0Opp)
1464  {
1465  addPointLocations(location::intersection(srcEi0Opp, tgtEi));
1466  }
1467 
1468  return true;
1469  };
1470 
1471  // One target point is a source point and the other is outside a source
1472  // corner
1473  auto isPointToOutCorner = []
1474  (
1475  const label tgtEi,
1476  const label tgtIsSrcPi0,
1477  const label tgtOutSrcPi1,
1478  const bool reverse
1479  )
1480  {
1481  return tgtOutSrcPi1 != (tgtIsSrcPi0 + 1 + reverse) % 3;
1482  };
1483 
1484  // Both target points are outside source edges
1485  auto outEdgeToOutEdge = [&addPointLocations,&srcInTgtEdge]
1486  (
1487  const label tgtEi,
1488  const label tgtOutSrcEi0,
1489  const label tgtOutSrcEi1
1490  )
1491  {
1492  const label srcPi = (5 - tgtOutSrcEi0 - tgtOutSrcEi1) % 3;
1493 
1494  if
1495  (
1496  (tgtOutSrcEi0 != (tgtOutSrcEi1 + 1) % 3)
1497  && (srcInTgtEdge[srcPi][tgtEi] != 1)
1498  )
1499  {
1500  return false;
1501  }
1502 
1503  if
1504  (
1505  (tgtOutSrcEi0 == (tgtOutSrcEi1 + 1) % 3)
1506  != (srcInTgtEdge[srcPi][tgtEi] == 1)
1507  )
1508  {
1509  addPointLocations
1510  (
1511  location::intersection(tgtOutSrcEi0, tgtEi),
1512  location::intersection(tgtOutSrcEi1, tgtEi)
1513  );
1514  }
1515 
1516  return true;
1517  };
1518 
1519  // One target point is outside a source edge and the other is outside a
1520  // source corner
1521  auto outEdgeToOutCorner = [&addPointLocations,&srcInTgtEdge]
1522  (
1523  const label tgtEi,
1524  const label tgtOutSrcEi0,
1525  const label tgtOutSrcPi1,
1526  const bool reverse
1527  )
1528  {
1529  if (tgtOutSrcEi0 != (tgtOutSrcPi1 + 1) % 3)
1530  {
1531  return true;
1532  }
1533 
1534  const label srcPi1Prev = (tgtOutSrcPi1 + 1 + !reverse) % 3;
1535 
1536  if (srcInTgtEdge[srcPi1Prev][tgtEi] == -1)
1537  {
1538  return false;
1539  }
1540 
1541  const label srcPi1Next = (tgtOutSrcPi1 + 1 + reverse) % 3;
1542 
1543  if (srcInTgtEdge[srcPi1Next][tgtEi] == 1)
1544  {
1545  return true;
1546  }
1547 
1548  location l1 = location::intersection(tgtOutSrcEi0, tgtEi);
1549 
1550  const label srcEi =
1551  srcInTgtEdge[tgtOutSrcPi1][tgtEi] == 1
1552  ? (tgtOutSrcPi1 + 2*reverse) % 3
1553  : (tgtOutSrcPi1 + 2*!reverse) % 3;
1554 
1555  location l2 = location::intersection(srcEi, tgtEi);
1556 
1557  if (reverse)
1558  {
1559  Swap(l1, l2);
1560  }
1561 
1562  addPointLocations(l1, l2);
1563 
1564  return true;
1565  };
1566 
1567  // Both target points are outside source corners
1568  auto outCornerToOutCorner = []
1569  (
1570  const label tgtEi,
1571  const label tgtOutSrcPi0,
1572  const label tgtOutSrcPi1
1573  )
1574  {
1575  return tgtOutSrcPi0 != (tgtOutSrcPi1 + 2) % 3;
1576  };
1577 
1578  // Step 3.2: Consider each target edge in turn
1579  forAll(tgtPs, tgtEi)
1580  {
1581  const label tgtPi0 = tgtEi, tgtPi1 = (tgtEi + 1) % 3;
1582 
1583  const bool tgtInSrcTri0 = tgtInSrcTri[tgtPi0] == 1;
1584  const bool tgtInSrcTri1 = tgtInSrcTri[tgtPi1] == 1;
1585 
1586  const label tgtIsSrcPi0 =
1587  tgtInSrcTri[tgtPi0] == 0 ? tgtSrcPis[tgtPi0] : -1;
1588  const label tgtIsSrcPi1 =
1589  tgtInSrcTri[tgtPi1] == 0 ? tgtSrcPis[tgtPi1] : -1;
1590 
1591  const label tgtOutSrcEi0 =
1592  count(tgtInSrcEdge[tgtPi0], -1) == 1
1593  ? findIndex(tgtInSrcEdge[tgtPi0], -1)
1594  : -1;
1595  const label tgtOutSrcEi1 =
1596  count(tgtInSrcEdge[tgtPi1], -1) == 1
1597  ? findIndex(tgtInSrcEdge[tgtPi1], -1)
1598  : -1;
1599 
1600  const label tgtOutSrcPi0 =
1601  count(tgtInSrcEdge[tgtPi0], -1) == 2
1602  ? (findIndex(tgtInSrcEdge[tgtPi0], 1) + 2) % 3
1603  : -1;
1604  const label tgtOutSrcPi1 =
1605  count(tgtInSrcEdge[tgtPi1], -1) == 2
1606  ? (findIndex(tgtInSrcEdge[tgtPi1], 1) + 2) % 3
1607  : -1;
1608 
1609  // Add the first point if it within or part of the source triangle
1610  if (tgtInSrcTri0)
1611  {
1612  pointLocations.append(location::tgtPoint(tgtPi0));
1613  }
1614  if (tgtIsSrcPi0 != -1)
1615  {
1616  addPointLocations(location::srcTgtPoint(tgtIsSrcPi0, tgtPi0));
1617  }
1618 
1619  // Add crossings
1620  if
1621  (
1622  (tgtInSrcTri0 && tgtInSrcTri1)
1623  || (tgtOutSrcEi0 != -1 && tgtOutSrcEi0 == tgtOutSrcEi1)
1624  || (tgtOutSrcPi0 != -1 && tgtOutSrcPi0 == tgtOutSrcPi1)
1625  )
1626  {
1627  // Both target points are in the same source quadrant. There is
1628  // nothing to check or to add.
1629  }
1630  else if
1631  (
1632  (tgtInSrcTri0 && tgtIsSrcPi1 != -1)
1633  || (tgtIsSrcPi0 != -1 && tgtInSrcTri1)
1634  )
1635  {
1636  // One target point is within the source triangle and one is a
1637  // source point. There is nothing to check or to add.
1638  }
1639  else if (tgtInSrcTri0 && (tgtOutSrcEi1 != -1 || tgtOutSrcPi1 != -1))
1640  {
1641  // The first target point is within the source triangle and the
1642  // second is outside
1643  if (!inTriToOut(tgtEi, tgtOutSrcEi1, tgtOutSrcPi1, 0))
1644  {
1645  pointLocations.clear();
1646  break;
1647  }
1648  }
1649  else if ((tgtOutSrcEi0 != -1 || tgtOutSrcPi0 != -1) && tgtInSrcTri1)
1650  {
1651  // (reverse of previous clause)
1652  if (!inTriToOut(tgtEi, tgtOutSrcEi0, tgtOutSrcPi0, 1))
1653  {
1654  pointLocations.clear();
1655  break;
1656  }
1657  }
1658  else if (tgtIsSrcPi0 != -1 && tgtIsSrcPi1 != -1)
1659  {
1660  // Both target points are source points. Check the ordering is
1661  // compatible with an intersection.
1662  if (tgtIsSrcPi0 != (tgtIsSrcPi1 + 1) % 3)
1663  {
1664  pointLocations.clear();
1665  break;
1666  }
1667  }
1668  else if (tgtIsSrcPi0 != -1 && tgtOutSrcEi1 != -1)
1669  {
1670  // The first target point is a source point and the second is
1671  // outside a source edge
1672  if (!isPointToOutEdge(tgtEi, tgtIsSrcPi0, tgtOutSrcEi1, 0))
1673  {
1674  pointLocations.clear();
1675  break;
1676  }
1677  }
1678  else if (tgtOutSrcEi0 != -1 && tgtIsSrcPi1 != -1)
1679  {
1680  // (reverse of previous clause)
1681  if (!isPointToOutEdge(tgtEi, tgtIsSrcPi1, tgtOutSrcEi0, 1))
1682  {
1683  pointLocations.clear();
1684  break;
1685  }
1686  }
1687  else if (tgtIsSrcPi0 != -1 && tgtOutSrcPi1 != -1)
1688  {
1689  // The first target point is a source point and the second is
1690  // outside a source corner
1691  if (!isPointToOutCorner(tgtEi, tgtIsSrcPi0, tgtOutSrcPi1, 0))
1692  {
1693  pointLocations.clear();
1694  break;
1695  }
1696  }
1697  else if (tgtOutSrcPi0 != -1 && tgtIsSrcPi1 != -1)
1698  {
1699  // (reverse of previous clause)
1700  if (!isPointToOutCorner(tgtEi, tgtIsSrcPi1, tgtOutSrcPi0, 1))
1701  {
1702  pointLocations.clear();
1703  break;
1704  }
1705  }
1706  else if (tgtOutSrcEi0 != -1 && tgtOutSrcEi1 != -1)
1707  {
1708  // Both target points are outside source edges
1709  if (!outEdgeToOutEdge(tgtEi, tgtOutSrcEi0, tgtOutSrcEi1))
1710  {
1711  pointLocations.clear();
1712  break;
1713  }
1714  }
1715  else if (tgtOutSrcEi0 != -1 && tgtOutSrcPi1 != -1)
1716  {
1717  // The first target point is outside a source edge and the
1718  // second is outside a source corner
1719  if (!outEdgeToOutCorner(tgtEi, tgtOutSrcEi0, tgtOutSrcPi1, 0))
1720  {
1721  pointLocations.clear();
1722  break;
1723  }
1724  }
1725  else if (tgtOutSrcPi0 != -1 && tgtOutSrcEi1 != -1)
1726  {
1727  // (reverse of previous clause)
1728  if (!outEdgeToOutCorner(tgtEi, tgtOutSrcEi1, tgtOutSrcPi0, 1))
1729  {
1730  pointLocations.clear();
1731  break;
1732  }
1733  }
1734  else if (tgtOutSrcPi0 != -1 && tgtOutSrcPi1 != -1)
1735  {
1736  // Both target points are outside source corners
1737  if (!outCornerToOutCorner(tgtEi, tgtOutSrcPi0, tgtOutSrcPi1))
1738  {
1739  pointLocations.clear();
1740  break;
1741  }
1742  }
1743  else
1744  {
1745  // A target point is outside all source edges. The projection
1746  // has collapsed.
1747  pointLocations.clear();
1748  break;
1749  }
1750  }
1751 
1752  // Step 3.3: Close the polygon
1753  if (!pointLocations.empty())
1754  {
1755  const location& l = pointLocations.first();
1756 
1757  if (l.isIntersection() || l.isSrcAndTgtPoint())
1758  {
1759  addPointLocations(l, location(), false);
1760  }
1761  }
1762  }
1763 
1764  // Step 4: The above walk was done around the target triangle, but the
1765  // result should be ordered in the direction of the source triangle, so the
1766  // list of locations must be reversed
1767  inplaceReverseList(pointLocations);
1768 
1769  // Step 5: Handle the special case of all source points within the target
1770  // triangle. Target edges do not form a part of this intersection, so
1771  // pointLocations is empty. We need to add the entire source triangle to
1772  // the intersection.
1773  if (pointLocations.empty() && count(srcInTgtTri, 1) == 3)
1774  {
1775  forAll(srcPs, srcPi)
1776  {
1777  pointLocations.append(location::srcPoint(srcPi));
1778  }
1779  }
1780 
1781  if (debug)
1782  {
1783  Info<< indent << "pointLocations=" << pointLocations << endl;
1784  }
1785 
1786  // Step 6: Generate the geometry
1788  (
1789  srcPs,
1790  srcNs,
1791  tgtPs,
1792  srcPoints,
1793  srcPointNormals,
1794  tgtPoints,
1795  pointLocations
1796  );
1797 
1798  if (write)
1799  {
1800  writePolygon(writePrefix + "_srcIctFace", srcPoints);
1801  writePolygon(writePrefix + "_tgtIctFace", tgtPoints);
1802  }
1803 
1804  if (debug || write)
1805  {
1806  Info<< decrIndent;
1807  }
1808 }
1809 
1810 // ************************************************************************* //
void generateGeometryForLocations(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)
Definition: triIntersect.C:610
const char *const group
Group name for atomic constants.
dimensionedScalar sign(const dimensionedScalar &ds)
List< labelList > labelListList
A List of labelList.
Definition: labelList.H:57
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&#39;s.
Definition: triIntersect.C:988
void cmptMax(FieldField< Field, typename FieldField< Field, Type >::cmptType > &cf, const FieldField< Field, Type > &f)
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&#39;s.
Definition: triIntersect.C:840
bool isTgtNotSrcPoint() const
Return whether the location is a target point and not a source.
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:434
bool empty() const
Return true if the UList is empty (ie, size() is zero)
Definition: UListI.H:325
A triangle primitive used to calculate face areas and swept volumes.
Definition: triangle.H:57
void srcInTgt(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, 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:299
layerAndWeight max(const layerAndWeight &a, const layerAndWeight &b)
Ostream & indent(Ostream &os)
Indent stream.
Definition: Ostream.H:221
bool isIntersection() const
Return whether the location is an intersection.
Barycentric2D< scalar > barycentric2D
A scalar version of the templated Barycentric2D.
Definition: barycentric2D.H:45
void resize(const label)
Alter the addressed list size.
Definition: DynamicListI.H:216
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
A face is a list of labels corresponding to mesh vertices.
Definition: face.H:75
error FatalError
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:306
label log2(label i)
Return the log base 2 by successive bit-shifting of the given label.
Definition: label.H:79
A 2-tuple for storing two objects of different types.
Definition: HashTable.H:65
const Type1 & first() const
Return first.
Definition: Tuple2.H:116
void size(const label)
Override size to be inconsistent with allocated storage.
Definition: ListI.H:164
Vector< Cmpt > x() const
Definition: TensorI.H:289
Vector2D< scalar > vector2D
vector2D obtained from generic Vector2D
Definition: vector2D.H:49
bool isSrcPoint() const
Return whether the location is a source point.
Cmpt cmptSum(const VectorSpace< Form, Cmpt, Ncmpts > &vs)
Definition: VectorSpaceI.H:500
const dimensionedScalar b
Wien displacement law constant: default SI units: [m K].
Definition: createFields.H:27
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:251
label count(const ListType &l, typename ListType::const_reference x)
Count the number of occurrences of a value in a list.
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.
Definition: triIntersect.C:761
static location tgtPoint(const label tgtPi)
Construct a target point location.
label findMin(const ListType &, const label start=0)
Find index of min element (and less than given element).
Vector< Cmpt > y() const
Definition: TensorI.H:296
const Cmpt & z() const
Definition: VectorI.H:87
Templated storage for the roots of polynomial equations, plus flags to indicate the nature of the roo...
Definition: Roots.H:64
Vector< scalar > vector
A scalar version of the templated Vector.
Definition: vector.H:49
static location srcPoint(const label srcPi)
Construct a source point location.
bool isSrcNotTgtPoint() const
Return whether the location is a source point and not a target.
dimensionedScalar det(const dimensionedSphericalTensor &dt)
static location srcTgtPoint(const label srcPi, const label tgtPi)
Construct a source and target point location.
bool isTgtPoint() const
Return whether the location is a target point.
T & first()
Return the first element of the list.
Definition: UListI.H:114
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 Cmpt & x() const
Definition: Vector2DI.H:68
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
Templated 2D tensor derived from VectorSpace adding construction from 4 components, element access using xx(), xy(), yx() and yy() member functions and the iner-product (dot-product) and outer-product of two Vector2Ds (tensor-product) operators.
Definition: Tensor2D.H:56
scalar degToRad(const scalar deg)
Conversion from degrees to radians.
const Cmpt & y() const
Definition: VectorI.H:81
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&#39;s projected volume and a.
label tgtPointi() const
Return the target point index.
Templated 2D Barycentric derived from VectorSpace. Has 3 components, one of which is redundant...
Definition: Barycentric2D.H:50
Vector< Cmpt > z() const
Definition: TensorI.H:303
void inplaceReverseList(ListType &list)
Inplace reversal of a list using Swap.
label srcPointi() const
Return the source point index.
scalar y
Cubic equation of the form a*x^3 + b*x^2 + c*x + d = 0.
Definition: cubicEqn.H:49
scalar u0
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.timeName(), cloud::prefix, mesh, IOobject::MUST_READ, IOobject::NO_WRITE))
dimensionedScalar cos(const dimensionedScalar &ds)
void Swap(T &a, T &b)
Definition: Swap.H:43
const Cmpt & y() const
Definition: Vector2DI.H:74
A 1D vector of objects of type <T> that resizes itself as necessary to accept the new objects...
Definition: DynamicList.H:56
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:478
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, 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:422
A class for handling words, derived from string.
Definition: word.H:59
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.
Definition: triIntersect.C:690
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.
Definition: triIntersect.C:801
dimensionSet cmptMag(const dimensionSet &)
Definition: dimensionSet.C:275
DynamicList< T, SizeInc, SizeMult, SizeDiv > & append(const T &)
Append an element at the end of the list.
Definition: DynamicListI.H:296
static const word null
An empty word.
Definition: word.H:77
void write(std::ostream &os, const bool binary, List< floatScalar > &fField)
Write floats ascii or binary.
layerAndWeight min(const layerAndWeight &a, const layerAndWeight &b)
phi
Definition: correctPhi.H:3
List< label > labelList
A List of labels.
Definition: labelList.H:56
static const zero Zero
Definition: zero.H:97
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
A 1D vector of objects of type <T>, where the size of the vector is known and can be used for subscri...
Definition: HashTable.H:60
const Cmpt & x() const
Definition: VectorI.H:75
vector normal() const
Return unit normal.
Definition: triangleI.H:110
scalar protectedDivideAndClip01(const scalar a, const scalar b)
Divide two numbers, protect the result from overflowing, and clip the.
Definition: triIntersect.C:56
Type tgtTriInterpolate(const barycentric2D &y, const FixedList< Type, 3 > &tgtPsis)
Use the coordinates obtained from srcPointTgtTriIntersection to interpolate.
label findMax(const ListType &, const label start=0)
Find index of max element (and larger than given element).
Templated 3D Vector derived from VectorSpace adding construction from 3 components, element access using x(), y() and z() member functions and the inner-product (dot-product) and cross product operators.
Definition: Vector.H:57
dimensioned< scalar > magSqr(const dimensioned< Type > &)
An Ostream is an abstract base class for all output systems (streams, files, token lists...
Definition: Ostream.H:54
void add(FieldField< Field1, typename typeOfSum< Type1, Type2 >::type > &f, const FieldField< Field1, Type1 > &f1, const FieldField< Field2, Type2 > &f2)
void reverse(UList< T > &, const label n)
Definition: UListI.H:334
Ostream & decrIndent(Ostream &os)
Decrement the indent level.
Definition: Ostream.H:235
void write(const fileName &file, const word &title, const bool binary, const PointField &points, const VertexList &vertices, const LineList &lines, const FaceList &faces, const wordList &fieldNames, const boolList &fieldIsPointValues, const UPtrList< const Field< label >> &fieldLabelValues #define FieldTypeValuesConstArg(Type, nullArg))
Write VTK polygonal data to a file. Takes a PtrList of fields of labels and.
label findIndex(const ListType &, typename ListType::const_reference, const label start=0)
Find first occurrence of given element and return index,.
void T(FieldField< Field, Type > &f1, const FieldField< Field, Type > &f2)
const Type & second() const
Return second.
Definition: Pair.H:110
Roots< 3 > roots() const
Get the roots.
Definition: cubicEqn.C:32
bool isSrcAndTgtPoint() const
Return whether the location is a source point and a target point.
scalar protectedDivide(const scalar a, const scalar b)
Divide two numbers and protect the result from overflowing.
Definition: triIntersect.C:48
Type srcTriInterpolate(const barycentric2D &y, const FixedList< Type, 3 > &tgtPsis)
Use the coordinates obtained from srcTriTgtPointIntersection to interpolate.
barycentric2D srcPointTgtTriIntersection(const point &srcP, const vector &srcN, const FixedList< point, 3 > &tgtPs)
Calculate the intersection of a projected source point with a target.
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:503
static location intersection(const label srcEi, const label tgtEi)
Construct an intersection location between a source and a.
void writePolygon(const word &name, const PointField &ps)
Write a VTK file of a polygon.
messageStream Info
static Vector< scalar > uniform(const scalar &s)
Return a VectorSpace with all elements = s.
Definition: VectorSpaceI.H:168
const Type2 & second() const
Return second.
Definition: Tuple2.H:128
dimensioned< scalar > mag(const dimensioned< Type > &)
label n
Selector class for relaxation factors, solver type and solution.
Definition: solution.H:48
Functions with which to perform an intersection of a pair of triangles; the source and target...
T & last()
Return the last element of the list.
Definition: UListI.H:128
void clear()
Clear the addressed list, i.e. set the size to zero.
Definition: DynamicListI.H:236
Ostream & incrIndent(Ostream &os)
Increment the indent level.
Definition: Ostream.H:228
const Type & first() const
Return first.
Definition: Pair.H:98
dimensionedSymmTensor cof(const dimensionedSymmTensor &dt)
label tgtEdgei() const
Return the target edge index.
label srcEdgei() const
Return the source edge index.
Namespace for OpenFOAM.
void type(const direction i, const rootType t)
Set the type of the i-th root.
Definition: RootsI.H:120
scalar T0
Definition: createFields.H:22