1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright (C) 1991-2008 OpenCFD Ltd.
7 -------------------------------------------------------------------------------
9 This file is part of OpenFOAM.
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 the
13 Free Software Foundation; either version 2 of the License, or (at your
14 option) any later version.
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
21 You should have received a copy of the GNU General Public License
22 along with OpenFOAM; if not, write to the Free Software Foundation,
23 Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 \*---------------------------------------------------------------------------*/
27 #include "faceTriangulation.H"
31 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
33 const Foam::scalar Foam::faceTriangulation::edgeRelTol = 1E-6;
36 //- Edge to the right of face vertex i
37 Foam::label Foam::faceTriangulation::right(const label, label i)
43 //- Edge to the left of face vertex i
44 Foam::label Foam::faceTriangulation::left(const label size, label i)
46 return i == 0 ? size - 1 : (i - 1);
50 // Calculate (normalized) edge vectors.
51 // edges[i] gives edge between point i+1 and i.
52 Foam::tmp<Foam::vectorField> Foam::faceTriangulation::calcEdges
55 const pointField& points
58 tmp<vectorField> tedges(new vectorField(f.size()));
59 vectorField& edges = tedges();
63 label ni = f.fcIndex(i);
65 point thisPt = points[f[i]];
66 point nextPt = points[f[ni]];
68 vector vec(nextPt - thisPt);
69 vec /= mag(vec) + VSMALL;
78 // Calculates half angle components of angle from e0 to e1
79 void Foam::faceTriangulation::calcHalfAngle
88 // truncate cos to +-1 to prevent negative numbers
89 scalar cos = max(-1, min(1, e0 & e1));
91 scalar sin = (e0 ^ e1) & normal;
95 // 3rd or 4th quadrant
96 cosHalfAngle = - Foam::sqrt(0.5*(1 + cos));
97 sinHalfAngle = Foam::sqrt(0.5*(1 - cos));
101 // 1st or 2nd quadrant
102 cosHalfAngle = Foam::sqrt(0.5*(1 + cos));
103 sinHalfAngle = Foam::sqrt(0.5*(1 - cos));
108 // Calculate intersection point between edge p1-p2 and ray (in 2D).
109 // Return true and intersection point if intersection between p1 and p2.
110 Foam::pointHit Foam::faceTriangulation::rayEdgeIntersect
112 const vector& normal,
113 const point& rayOrigin,
114 const vector& rayDir,
120 // Start off from miss
123 // Construct plane normal to rayDir and intersect
124 const vector y = normal ^ rayDir;
126 posOnEdge = plane(rayOrigin, y).normalIntersect(p1, (p2-p1));
128 // Check intersection to left of p1 or right of p2
129 if ((posOnEdge < 0) || (posOnEdge > 1))
135 // Check intersection behind rayOrigin
136 point intersectPt = p1 + posOnEdge * (p2 - p1);
138 if (((intersectPt - rayOrigin) & rayDir) < 0)
146 result.setPoint(intersectPt);
147 result.setDistance(mag(intersectPt - rayOrigin));
154 // Return true if triangle given its three points (anticlockwise ordered)
156 bool Foam::faceTriangulation::triangleContainsPoint
165 scalar area01Pt = triPointRef(p0, p1, pt).normal() & n;
166 scalar area12Pt = triPointRef(p1, p2, pt).normal() & n;
167 scalar area20Pt = triPointRef(p2, p0, pt).normal() & n;
169 if ((area01Pt > 0) && (area12Pt > 0) && (area20Pt > 0))
173 else if ((area01Pt < 0) && (area12Pt < 0) && (area20Pt < 0))
175 FatalErrorIn("triangleContainsPoint") << abort(FatalError);
185 // Starting from startIndex find diagonal. Return in index1, index2.
186 // Index1 always startIndex except when convex polygon
187 void Foam::faceTriangulation::findDiagonal
189 const pointField& points,
191 const vectorField& edges,
192 const vector& normal,
193 const label startIndex,
198 const point& startPt = points[f[startIndex]];
200 // Calculate angle at startIndex
201 const vector& rightE = edges[right(f.size(), startIndex)];
202 const vector leftE = -edges[left(f.size(), startIndex)];
204 // Construct ray which bisects angle
205 scalar cosHalfAngle = GREAT;
206 scalar sinHalfAngle = GREAT;
207 calcHalfAngle(normal, rightE, leftE, cosHalfAngle, sinHalfAngle);
211 + sinHalfAngle*(normal ^ rightE)
213 // rayDir should be normalized already but is not due to rounding errors
215 rayDir /= mag(rayDir) + VSMALL;
219 // Check all edges (apart from rightE and leftE) for nearest intersection
222 label faceVertI = f.fcIndex(startIndex);
224 pointHit minInter(false, vector::zero, GREAT, true);
226 scalar minPosOnEdge = GREAT;
228 for(label i = 0; i < f.size() - 2; i++)
237 points[f[faceVertI]],
238 points[f[f.fcIndex(faceVertI)]],
242 if (inter.hit() && inter.distance() < minInter.distance())
245 minIndex = faceVertI;
246 minPosOnEdge = posOnEdge;
249 faceVertI = f.fcIndex(faceVertI);
255 //WarningIn("faceTriangulation::findDiagonal")
256 // << "Could not find intersection starting from " << f[startIndex]
257 // << " for face " << f << endl;
264 const label leftIndex = minIndex;
265 const label rightIndex = f.fcIndex(minIndex);
267 // Now ray intersects edge from leftIndex to rightIndex.
268 // Check for intersection being one of the edge points. Make sure never
269 // to return two consecutive points.
271 if (mag(minPosOnEdge) < edgeRelTol && f.fcIndex(startIndex) != leftIndex)
280 mag(minPosOnEdge - 1) < edgeRelTol
281 && f.fcIndex(rightIndex) != startIndex
290 // Select visible vertex that minimizes
291 // angle to bisection. Visibility checking by checking if inside triangle
292 // formed by startIndex, leftIndex, rightIndex
294 const point& leftPt = points[f[leftIndex]];
295 const point& rightPt = points[f[rightIndex]];
298 scalar maxCos = -GREAT;
300 // all vertices except for startIndex and ones to left and right of it.
301 faceVertI = f.fcIndex(f.fcIndex(startIndex));
302 for(label i = 0; i < f.size() - 3; i++)
304 const point& pt = points[f[faceVertI]];
308 (faceVertI == leftIndex)
309 || (faceVertI == rightIndex)
310 || (triangleContainsPoint(normal, startPt, leftPt, rightPt, pt))
313 // pt inside triangle (so perhaps visible)
314 // Select based on minimal angle (so guaranteed visible).
315 vector edgePt0 = pt - startPt;
316 edgePt0 /= mag(edgePt0);
318 scalar cos = rayDir & edgePt0;
322 minIndex = faceVertI;
325 faceVertI = f.fcIndex(faceVertI);
330 // no vertex found. Return startIndex and one of the intersected edge
334 if (f.fcIndex(startIndex) != leftIndex)
351 // Find label of vertex to start splitting from. Is:
352 // 1] flattest concave angle
353 // 2] flattest convex angle if no concave angles.
354 Foam::label Foam::faceTriangulation::findStart
357 const vectorField& edges,
361 const label size = f.size();
363 scalar minCos = GREAT;
368 const vector& rightEdge = edges[right(size, fp)];
369 const vector leftEdge = -edges[left(size, fp)];
371 if (((rightEdge ^ leftEdge) & normal) < SMALL)
373 scalar cos = rightEdge & leftEdge;
384 // No concave angle found. Get flattest convex angle
389 const vector& rightEdge = edges[right(size, fp)];
390 const vector leftEdge = -edges[left(size, fp)];
392 scalar cos = rightEdge & leftEdge;
405 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
407 // Split face f into triangles. Handles all simple (convex & concave)
409 bool Foam::faceTriangulation::split
412 const pointField& points,
414 const vector& normal,
418 const label size = f.size();
424 "split(const bool, const pointField&, const face&"
425 ", const vector&, label&)"
426 ) << "Illegal face:" << f
427 << " with points " << IndirectList<point>(points, f)()
434 // Triangle. Just copy.
435 triFace& tri = operator[](triI++);
444 // General case. Start splitting for -flattest concave angle
445 // -or flattest convex angle if no concave angles.
447 tmp<vectorField> tedges(calcEdges(f, points));
448 const vectorField& edges = tedges();
450 label startIndex = findStart(f, edges, normal);
452 // Find diagonal to split face across
456 for (label iter = 0; iter < f.size(); iter++)
469 if (index1 != -1 && index2 != -1)
471 // Found correct diagonal
475 // Try splitting from next startingIndex.
476 startIndex = f.fcIndex(startIndex);
479 if (index1 == -1 || index2 == -1)
483 // Do naive triangulation. Find smallest angle to start
484 // triangulating from.
486 scalar maxCos = -GREAT;
490 const vector& rightEdge = edges[right(size, fp)];
491 const vector leftEdge = -edges[left(size, fp)];
493 scalar cos = rightEdge & leftEdge;
503 "split(const bool, const pointField&, const face&"
504 ", const vector&, label&)"
505 ) << "Cannot find valid diagonal on face " << f
506 << " with points " << IndirectList<point>(points, f)() << nl
507 << "Returning naive triangulation starting from "
508 << f[maxIndex] << " which might not be correct for a"
509 << " concave or warped face" << endl;
512 label fp = f.fcIndex(maxIndex);
514 for (label i = 0; i < size-2; i++)
516 label nextFp = f.fcIndex(fp);
518 triFace& tri = operator[](triI++);
519 tri[0] = f[maxIndex];
532 "split(const bool, const pointField&, const face&"
533 ", const vector&, label&)"
534 ) << "Cannot find valid diagonal on face " << f
535 << " with points " << IndirectList<point>(points, f)() << nl
536 << "Returning empty triFaceList" << endl;
543 // Split into two subshapes.
544 // face1: index1 to index2
545 // face2: index2 to index1
547 // Get sizes of the two subshapes
551 diff = index2 - index1;
556 diff = index2 + size - index1;
559 label nPoints1 = diff + 1;
560 label nPoints2 = size - diff + 1;
562 if (nPoints1 == size || nPoints2 == size)
566 "split(const bool, const pointField&, const face&"
567 ", const vector&, label&)"
568 ) << "Illegal split of face:" << f
569 << " with points " << IndirectList<point>(points, f)()
570 << " at indices " << index1 << " and " << index2
571 << abort(FatalError);
575 // Collect face1 points
576 face face1(nPoints1);
578 label faceVertI = index1;
579 for (int i = 0; i < nPoints1; i++)
581 face1[i] = f[faceVertI];
582 faceVertI = f.fcIndex(faceVertI);
585 // Collect face2 points
586 face face2(nPoints2);
589 for (int i = 0; i < nPoints2; i++)
591 face2[i] = f[faceVertI];
592 faceVertI = f.fcIndex(faceVertI);
595 // Decompose the split faces
596 //Pout<< "Split face:" << f << " into " << face1 << " and " << face2
598 //string oldPrefix(Pout.prefix());
599 //Pout.prefix() = " " + oldPrefix;
602 split(fallBack, points, face1, normal, triI)
603 && split(fallBack, points, face2, normal, triI);
605 //Pout.prefix() = oldPrefix;
612 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
615 Foam::faceTriangulation::faceTriangulation()
621 // Construct from components
622 Foam::faceTriangulation::faceTriangulation
624 const pointField& points,
629 triFaceList(f.size()-2)
631 vector avgNormal = f.normal(points);
632 avgNormal /= mag(avgNormal) + VSMALL;
636 bool valid = split(fallBack, points, f, avgNormal, triI);
645 // Construct from components
646 Foam::faceTriangulation::faceTriangulation
648 const pointField& points,
654 triFaceList(f.size()-2)
658 bool valid = split(fallBack, points, f, n, triI);
667 // Construct from Istream
668 Foam::faceTriangulation::faceTriangulation(Istream& is)
674 // ************************************************************************* //