faceTriangulation.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration |
5  \\ / A nd | www.openfoam.com
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8  Copyright (C) 2011-2016 OpenFOAM Foundation
9 -------------------------------------------------------------------------------
10 License
11  This file is part of OpenFOAM.
12 
13  OpenFOAM is free software: you can redistribute it and/or modify it
14  under the terms of the GNU General Public License as published by
15  the Free Software Foundation, either version 3 of the License, or
16  (at your option) any later version.
17 
18  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
19  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
20  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21  for more details.
22 
23  You should have received a copy of the GNU General Public License
24  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
25 
26 \*---------------------------------------------------------------------------*/
27 
28 #include "faceTriangulation.H"
29 #include "plane.H"
30 
31 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
32 
33 const Foam::scalar Foam::faceTriangulation::edgeRelTol = 1e-6;
34 
35 
36 // Edge to the right of face vertex i
37 Foam::label Foam::faceTriangulation::right(const label, label i)
38 {
39  return i;
40 }
41 
42 
43 // Edge to the left of face vertex i
44 Foam::label Foam::faceTriangulation::left(const label size, label i)
45 {
46  return i ? i-1 : size-1;
47 }
48 
49 
50 // Calculate (normalized) edge vectors.
51 // edges[i] gives edge between point i+1 and i.
52 Foam::tmp<Foam::vectorField> Foam::faceTriangulation::calcEdges
53 (
54  const face& f,
55  const pointField& points
56 )
57 {
58  auto tedges = tmp<vectorField>::New(f.size());
59  auto& edges = tedges.ref();
60 
61  forAll(f, i)
62  {
63  point thisPt = points[f[i]];
64  point nextPt = points[f[f.fcIndex(i)]];
65 
66  vector vec(nextPt - thisPt);
67 
68  edges[i] = vec.normalise();
69  }
70 
71  return tedges;
72 }
73 
74 
75 // Calculates half angle components of angle from e0 to e1
76 void Foam::faceTriangulation::calcHalfAngle
77 (
78  const vector& normal,
79  const vector& e0,
80  const vector& e1,
81  scalar& cosHalfAngle,
82  scalar& sinHalfAngle
83 )
84 {
85  // truncate cos to +-1 to prevent negative numbers
86  scalar cos = clamp((e0 & e1), -1, 1);
87 
88  scalar sin = (e0 ^ e1) & normal;
89 
90  if (sin < -ROOTVSMALL)
91  {
92  // 3rd or 4th quadrant
93  cosHalfAngle = - Foam::sqrt(0.5*(1 + cos));
94  sinHalfAngle = Foam::sqrt(0.5*(1 - cos));
95  }
96  else
97  {
98  // 1st or 2nd quadrant
99  cosHalfAngle = Foam::sqrt(0.5*(1 + cos));
100  sinHalfAngle = Foam::sqrt(0.5*(1 - cos));
101  }
102 }
103 
104 
105 // Calculate intersection point between edge p1-p2 and ray (in 2D).
106 // Return true and intersection point if intersection between p1 and p2.
107 Foam::pointHit Foam::faceTriangulation::rayEdgeIntersect
108 (
109  const vector& normal,
110  const point& rayOrigin,
111  const vector& rayDir,
112  const point& p1,
113  const point& p2,
114  scalar& posOnEdge
115 )
116 {
117  // Start off from miss
118  pointHit result(p1);
119 
120  // Construct plane normal to rayDir and intersect
121  const vector y = normal ^ rayDir;
122 
123  posOnEdge = plane(rayOrigin, y).normalIntersect(p1, (p2-p1));
124 
125  // Check intersection to left of p1 or right of p2
126  if ((posOnEdge < 0) || (posOnEdge > 1))
127  {
128  // Miss
129  }
130  else
131  {
132  // Check intersection behind rayOrigin
133  point intersectPt = p1 + posOnEdge * (p2 - p1);
134 
135  if (((intersectPt - rayOrigin) & rayDir) < 0)
136  {
137  // Miss
138  }
139  else
140  {
141  // Hit
142  result.hitPoint(intersectPt);
143  result.setDistance(mag(intersectPt - rayOrigin));
144  }
145  }
146  return result;
147 }
148 
149 
150 // Return true if triangle given its three points (anticlockwise ordered)
151 // contains point
152 bool Foam::faceTriangulation::triangleContainsPoint
153 (
154  const vector& n,
155  const point& p0,
156  const point& p1,
157  const point& p2,
158  const point& pt
159 )
160 {
161  const scalar area01Pt = triPointRef::areaNormal(p0, p1, pt) & n;
162  const scalar area12Pt = triPointRef::areaNormal(p1, p2, pt) & n;
163  const scalar area20Pt = triPointRef::areaNormal(p2, p0, pt) & n;
164 
165  if ((area01Pt > 0) && (area12Pt > 0) && (area20Pt > 0))
166  {
167  return true;
168  }
169  else if ((area01Pt < 0) && (area12Pt < 0) && (area20Pt < 0))
170  {
172  return false;
173  }
174 
175  return false;
176 }
177 
178 
179 // Starting from startIndex find diagonal. Return in index1, index2.
180 // Index1 always startIndex except when convex polygon
181 void Foam::faceTriangulation::findDiagonal
182 (
183  const pointField& points,
184  const face& f,
185  const vectorField& edges,
186  const vector& normal,
187  const label startIndex,
188  label& index1,
189  label& index2
190 )
191 {
192  const point& startPt = points[f[startIndex]];
193 
194  // Calculate angle at startIndex
195  const vector& rightE = edges[right(f.size(), startIndex)];
196  const vector leftE = -edges[left(f.size(), startIndex)];
197 
198  // Construct ray which bisects angle
199  scalar cosHalfAngle = GREAT;
200  scalar sinHalfAngle = GREAT;
201  calcHalfAngle(normal, rightE, leftE, cosHalfAngle, sinHalfAngle);
202  vector rayDir
203  (
204  cosHalfAngle*rightE
205  + sinHalfAngle*(normal ^ rightE)
206  );
207  // rayDir should be normalized already but is not due to rounding errors
208  // so normalize.
209  rayDir.normalise();
210 
211 
212  //
213  // Check all edges (apart from rightE and leftE) for nearest intersection
214  //
215 
216  label faceVertI = f.fcIndex(startIndex);
217 
218  pointHit minInter(false, Zero, GREAT, true);
219  label minIndex = -1;
220  scalar minPosOnEdge = GREAT;
221 
222  for (label i = 0; i < f.size() - 2; i++)
223  {
224  scalar posOnEdge;
225  pointHit inter =
226  rayEdgeIntersect
227  (
228  normal,
229  startPt,
230  rayDir,
231  points[f[faceVertI]],
232  points[f[f.fcIndex(faceVertI)]],
233  posOnEdge
234  );
235 
236  if (inter.hit() && inter.distance() < minInter.distance())
237  {
238  minInter = inter;
239  minIndex = faceVertI;
240  minPosOnEdge = posOnEdge;
241  }
242 
243  faceVertI = f.fcIndex(faceVertI);
244  }
245 
246 
247  if (minIndex == -1)
248  {
249  //WarningInFunction
250  // << "Could not find intersection starting from " << f[startIndex]
251  // << " for face " << f << endl;
252 
253  index1 = -1;
254  index2 = -1;
255  return;
256  }
257 
258  const label leftIndex = minIndex;
259  const label rightIndex = f.fcIndex(minIndex);
260 
261  // Now ray intersects edge from leftIndex to rightIndex.
262  // Check for intersection being one of the edge points. Make sure never
263  // to return two consecutive points.
264 
265  if (mag(minPosOnEdge) < edgeRelTol && f.fcIndex(startIndex) != leftIndex)
266  {
267  index1 = startIndex;
268  index2 = leftIndex;
269 
270  return;
271  }
272  if
273  (
274  mag(minPosOnEdge - 1) < edgeRelTol
275  && f.fcIndex(rightIndex) != startIndex
276  )
277  {
278  index1 = startIndex;
279  index2 = rightIndex;
280 
281  return;
282  }
283 
284  // Select visible vertex that minimizes
285  // angle to bisection. Visibility checking by checking if inside triangle
286  // formed by startIndex, leftIndex, rightIndex
287 
288  const point& leftPt = points[f[leftIndex]];
289  const point& rightPt = points[f[rightIndex]];
290 
291  minIndex = -1;
292  scalar maxCos = -GREAT;
293 
294  // all vertices except for startIndex and ones to left and right of it.
295  faceVertI = f.fcIndex(f.fcIndex(startIndex));
296  for (label i = 0; i < f.size() - 3; i++)
297  {
298  const point& pt = points[f[faceVertI]];
299 
300  if
301  (
302  (faceVertI == leftIndex)
303  || (faceVertI == rightIndex)
304  || (triangleContainsPoint(normal, startPt, leftPt, rightPt, pt))
305  )
306  {
307  // pt inside triangle (so perhaps visible)
308  // Select based on minimal angle (so guaranteed visible).
309  vector edgePt0 = (pt - startPt);
310  edgePt0.normalise();
311 
312  scalar cos = rayDir & edgePt0;
313  if (cos > maxCos)
314  {
315  maxCos = cos;
316  minIndex = faceVertI;
317  }
318  }
319  faceVertI = f.fcIndex(faceVertI);
320  }
321 
322  if (minIndex == -1)
323  {
324  // no vertex found. Return startIndex and one of the intersected edge
325  // endpoints.
326  index1 = startIndex;
327 
328  if (f.fcIndex(startIndex) != leftIndex)
329  {
330  index2 = leftIndex;
331  }
332  else
333  {
334  index2 = rightIndex;
335  }
336 
337  return;
338  }
339 
340  index1 = startIndex;
341  index2 = minIndex;
342 }
343 
344 
345 // Find label of vertex to start splitting from. Is:
346 // 1] flattest concave angle
347 // 2] flattest convex angle if no concave angles.
348 Foam::label Foam::faceTriangulation::findStart
349 (
350  const face& f,
351  const vectorField& edges,
352  const vector& normal
353 )
354 {
355  const label size = f.size();
356 
357  scalar minCos = GREAT;
358  label minIndex = -1;
359 
360  forAll(f, fp)
361  {
362  const vector& rightEdge = edges[right(size, fp)];
363  const vector leftEdge = -edges[left(size, fp)];
364 
365  if (((rightEdge ^ leftEdge) & normal) < ROOTVSMALL)
366  {
367  scalar cos = rightEdge & leftEdge;
368  if (cos < minCos)
369  {
370  minCos = cos;
371  minIndex = fp;
372  }
373  }
374  }
375 
376  if (minIndex == -1)
377  {
378  // No concave angle found. Get flattest convex angle
379  minCos = GREAT;
380 
381  forAll(f, fp)
382  {
383  const vector& rightEdge = edges[right(size, fp)];
384  const vector leftEdge = -edges[left(size, fp)];
385 
386  scalar cos = rightEdge & leftEdge;
387  if (cos < minCos)
388  {
389  minCos = cos;
390  minIndex = fp;
391  }
392  }
393  }
394 
395  return minIndex;
396 }
397 
398 
399 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
400 
401 // Split face f into triangles. Handles all simple (convex & concave)
402 // polygons.
403 bool Foam::faceTriangulation::split
404 (
405  const bool fallBack,
406  const pointField& points,
407  const face& f,
408  const vector& normal,
409  label& triI
410 )
411 {
412  const label size = f.size();
413 
414  if (size <= 2)
415  {
417  << "Illegal face:" << f
418  << " with points " << UIndirectList<point>(points, f)
419  << endl;
420 
421  return false;
422  }
423  else if (size == 3)
424  {
425  // Triangle. Just copy.
426  triFace& tri = operator[](triI++);
427  tri[0] = f[0];
428  tri[1] = f[1];
429  tri[2] = f[2];
430 
431  return true;
432  }
433  else
434  {
435  // General case. Start splitting for -flattest concave angle
436  // -or flattest convex angle if no concave angles.
437 
438  tmp<vectorField> tedges(calcEdges(f, points));
439  const vectorField& edges = tedges();
440 
441  label startIndex = findStart(f, edges, normal);
442 
443  // Find diagonal to split face across
444  label index1 = -1;
445  label index2 = -1;
446 
447  forAll(f, iter)
448  {
449  findDiagonal
450  (
451  points,
452  f,
453  edges,
454  normal,
455  startIndex,
456  index1,
457  index2
458  );
459 
460  if (index1 != -1 && index2 != -1)
461  {
462  // Found correct diagonal
463  break;
464  }
465 
466  // Try splitting from next startingIndex.
467  startIndex = f.fcIndex(startIndex);
468  }
469 
470  if (index1 == -1 || index2 == -1)
471  {
472  if (fallBack)
473  {
474  // Do naive triangulation. Find smallest angle to start
475  // triangulating from.
476  label maxIndex = -1;
477  scalar maxCos = -GREAT;
478 
479  forAll(f, fp)
480  {
481  const vector& rightEdge = edges[right(size, fp)];
482  const vector leftEdge = -edges[left(size, fp)];
483 
484  scalar cos = rightEdge & leftEdge;
485  if (cos > maxCos)
486  {
487  maxCos = cos;
488  maxIndex = fp;
489  }
490  }
491 
493  << "Cannot find valid diagonal on face " << f
494  << " with points " << UIndirectList<point>(points, f)
495  << nl
496  << "Returning naive triangulation starting from "
497  << f[maxIndex] << " which might not be correct for a"
498  << " concave or warped face" << endl;
499 
500 
501  label fp = f.fcIndex(maxIndex);
502 
503  for (label i = 0; i < size-2; i++)
504  {
505  label nextFp = f.fcIndex(fp);
506 
507  triFace& tri = operator[](triI++);
508  tri[0] = f[maxIndex];
509  tri[1] = f[fp];
510  tri[2] = f[nextFp];
511 
512  fp = nextFp;
513  }
514 
515  return true;
516  }
517  else
518  {
520  << "Cannot find valid diagonal on face " << f
521  << " with points " << UIndirectList<point>(points, f)
522  << nl
523  << "Returning empty triFaceList" << endl;
524 
525  return false;
526  }
527  }
528 
529 
530  // Split into two subshapes.
531  // face1: index1 to index2
532  // face2: index2 to index1
533 
534  // Get sizes of the two subshapes
535  label diff = 0;
536  if (index2 > index1)
537  {
538  diff = index2 - index1;
539  }
540  else
541  {
542  // folded round
543  diff = index2 + size - index1;
544  }
545 
546  label nPoints1 = diff + 1;
547  label nPoints2 = size - diff + 1;
548 
549  if (nPoints1 == size || nPoints2 == size)
550  {
552  << "Illegal split of face:" << f
553  << " with points " << UIndirectList<point>(points, f)
554  << " at indices " << index1 << " and " << index2
555  << abort(FatalError);
556  }
557 
558 
559  // Collect face1 points
560  face face1(nPoints1);
561 
562  label faceVertI = index1;
563  for (int i = 0; i < nPoints1; i++)
564  {
565  face1[i] = f[faceVertI];
566  faceVertI = f.fcIndex(faceVertI);
567  }
568 
569  // Collect face2 points
570  face face2(nPoints2);
571 
572  faceVertI = index2;
573  for (int i = 0; i < nPoints2; i++)
574  {
575  face2[i] = f[faceVertI];
576  faceVertI = f.fcIndex(faceVertI);
577  }
578 
579  // Decompose the split faces
580  //Pout<< "Split face:" << f << " into " << face1 << " and " << face2
581  // << endl;
582  //string oldPrefix(Pout.prefix());
583  //Pout.prefix() = " " + oldPrefix;
584 
585  bool splitOk =
586  split(fallBack, points, face1, normal, triI)
587  && split(fallBack, points, face2, normal, triI);
588 
589  //Pout.prefix() = oldPrefix;
590 
591  return splitOk;
592  }
593 }
594 
595 
596 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
597 
599 :
600  triFaceList()
601 {}
602 
603 
605 (
606  const pointField& points,
607  const face& f,
608  const bool fallBack
609 )
610 :
611  triFaceList(f.size()-2)
612 {
613  const vector avgNormal = f.unitNormal(points);
614 
615  label triI = 0;
616 
617  bool valid = split(fallBack, points, f, avgNormal, triI);
618 
619  if (!valid)
620  {
621  clear();
622  }
623 }
624 
625 
627 (
628  const pointField& points,
629  const face& f,
630  const vector& n,
631  const bool fallBack
632 )
633 :
634  triFaceList(f.size()-2)
635 {
636  label triI = 0;
637 
638  bool valid = split(fallBack, points, f, n, triI);
639 
640  if (!valid)
641  {
642  clear();
643  }
644 }
645 
646 
648 :
649  triFaceList(is)
650 {}
651 
652 
653 // ************************************************************************* //
List< triFace > triFaceList
List of triFace.
Definition: triFaceList.H:31
scalar diff(const triad &A, const triad &B)
Return a quantity of the difference between two triads.
Definition: triad.C:373
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:116
A face is a list of labels corresponding to mesh vertices.
Definition: face.H:68
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
error FatalError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL ERROR&#39; header text and sta...
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:598
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
Definition: BitOps.H:56
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:50
dimensionedScalar sqrt(const dimensionedScalar &ds)
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:531
Vector< Cmpt > & normalise(const scalar tol=ROOTVSMALL)
Inplace normalise the vector by its magnitude.
Definition: VectorI.H:114
faceTriangulation()
Construct null.
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:421
label fcIndex(const label i) const noexcept
The forward circular index. The next index in the list which returns to the first at the end of the l...
Definition: UListI.H:97
scalar y
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:38
const dimensionedScalar e
Elementary charge.
Definition: createFields.H:11
dimensionedScalar cos(const dimensionedScalar &ds)
const pointField & points
void clear()
Clear the list, i.e. set size to zero.
Definition: ListI.H:137
face triFace(3)
static tmp< T > New(Args &&... args)
Construct tmp with forwarding arguments.
Definition: tmp.H:206
Vector< scalar > vector
Definition: vector.H:57
Describes the interaction of a object and a (templated) point. It carries the info of a successful hi...
Definition: pointHit.H:43
errorManip< error > abort(error &err)
Definition: errorManip.H:139
dimensionedScalar sin(const dimensionedScalar &ds)
A Vector of values with scalar precision, where scalar is float/double depending on the compilation f...
labelList f(nPoints)
static bool split(const std::string &line, std::string &key, std::string &val)
Definition: cpuInfo.C:32
vector point
Point is a vector.
Definition: point.H:37
#define WarningInFunction
Report a warning using Foam::Warning.
label n
Field< vector > vectorField
Specialisation of Field<T> for vector.
A class for managing temporary objects.
Definition: HashPtrTable.H:50
const volScalarField & p0
Definition: EEqn.H:36
PointHit< point > pointHit
A PointHit with a 3D point.
Definition: pointHit.H:43
dimensionSet clamp(const dimensionSet &a, const dimensionSet &range)
Definition: dimensionSet.C:271
vector areaNormal() const
The area normal - with magnitude equal to area of triangle.
Definition: triangleI.H:151
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:127