Loading...
Searching...
No Matches
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-------------------------------------------------------------------------------
10License
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
33const Foam::scalar Foam::faceTriangulation::edgeRelTol = 1e-6;
34
35
36// Edge to the right of face vertex i
37Foam::label Foam::faceTriangulation::right(const label, label i)
38{
39 return i;
40}
41
42
43// Edge to the left of face vertex i
44Foam::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.
52Foam::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
76void 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.
107Foam::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
152bool 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
181void 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.
348Foam::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.
403bool 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 * * * * * * * * * * * * * * //
599:
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// ************************************************************************* //
scalar y
label n
An Istream is an abstract base class for all input systems (streams, files, token lists etc)....
Definition Istream.H:60
A List with indirect addressing. Like IndirectList but does not store addressing.
void size(const label n)
Older name for setAddressableSize.
Definition UList.H:118
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:99
faceTriangulation()
Construct null.
A face is a list of labels corresponding to mesh vertices.
Definition face.H:71
Geometric class that creates a 3D plane and can return the intersection point between a line and the ...
Definition plane.H:91
scalar normalIntersect(const point &pnt0, const vector &dir) const
Return cut coefficient for plane and line defined by origin and direction.
Definition plane.C:293
A class for managing temporary objects.
Definition tmp.H:75
static tmp< T > New(Args &&... args)
Construct tmp with forwarding arguments.
Definition tmp.H:215
A triangular face using a FixedList of labels corresponding to mesh vertices.
Definition triFace.H:68
A Vector of values with scalar precision, where scalar is float/double depending on the compilation f...
const volScalarField & p0
Definition EEqn.H:36
static bool split(const std::string &line, std::string &key, std::string &val)
Definition cpuInfo.C:32
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition error.H:600
const pointField & points
#define WarningInFunction
Report a warning using Foam::Warning.
PointHit< point > pointHit
A PointHit with a 3D point.
Definition pointHit.H:51
dimensionedScalar sin(const dimensionedScalar &ds)
dimensionSet clamp(const dimensionSet &a, const dimensionSet &range)
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition Ostream.H:519
dimensionedScalar sqrt(const dimensionedScalar &ds)
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
scalar diff(const triad &A, const triad &B)
Return a quantity of the difference between two triads.
Definition triad.C:373
errorManip< error > abort(error &err)
Definition errorManip.H:139
Field< vector > vectorField
Specialisation of Field<T> for vector.
vector point
Point is a vector.
Definition point.H:37
static constexpr const zero Zero
Global zero (0).
Definition zero.H:127
error FatalError
Error stream (stdout output on all processes), with additional 'FOAM FATAL ERROR' header text and sta...
List< triFace > triFaceList
List of triFace.
Definition triFaceList.H:31
vectorField pointField
pointField is a vectorField.
Vector< scalar > vector
Definition vector.H:57
dimensionedScalar cos(const dimensionedScalar &ds)
constexpr char nl
The newline '\n' character (0x0a).
Definition Ostream.H:50
labelList f(nPoints)
volScalarField & e
#define forAll(list, i)
Loop across all elements in list.
Definition stdFoam.H:299