FreeFOAM The Cross-Platform CFD Toolkit
Hosted by SourceForge:
Get FreeFOAM at SourceForge.net.
            Fast, secure and Free Open Source software downloads

triangleI.H

Go to the documentation of this file.
00001 /*---------------------------------------------------------------------------*\
00002   =========                 |
00003   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
00004    \\    /   O peration     |
00005     \\  /    A nd           | Copyright (C) 1991-2010 OpenCFD Ltd.
00006      \\/     M anipulation  |
00007 -------------------------------------------------------------------------------
00008 License
00009     This file is part of OpenFOAM.
00010 
00011     OpenFOAM is free software: you can redistribute it and/or modify it
00012     under the terms of the GNU General Public License as published by
00013     the Free Software Foundation, either version 3 of the License, or
00014     (at your option) any later version.
00015 
00016     OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
00017     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00018     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
00019     for more details.
00020 
00021     You should have received a copy of the GNU General Public License
00022     along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
00023 
00024 \*---------------------------------------------------------------------------*/
00025 
00026 #include <OpenFOAM/IOstreams.H>
00027 #include <OpenFOAM/pointHit.H>
00028 #include <OpenFOAM/mathematicalConstants.H>
00029 
00030 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
00031 
00032 namespace Foam
00033 {
00034 
00035 // * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
00036 
00037 template<class Point, class PointRef>
00038 pointHit triangle<Point, PointRef>::nearestPoint
00039 (
00040     const Point& baseVertex,
00041     const vector& E0,
00042     const vector& E1,
00043     const point& P
00044 )
00045 {
00046     // Distance vector
00047     const vector D(baseVertex - P);
00048 
00049     // Some geometrical factors
00050     const scalar a = E0 & E0;
00051     const scalar b = E0 & E1;
00052     const scalar c = E1 & E1;
00053 
00054     // Precalculate distance factors
00055     const scalar d = E0 & D;
00056     const scalar e = E1 & D;
00057     const scalar f = D & D;
00058 
00059     // Do classification
00060     const scalar det = a*c - b*b;
00061     scalar s = b*e - c*d;
00062     scalar t = b*d - a*e;
00063 
00064     bool inside = false;
00065 
00066     if (s+t < det)
00067     {
00068         if (s < 0)
00069         {
00070             if (t < 0)
00071             {
00072                 // Region 4
00073                 if (e > 0)
00074                 {
00075                     // min on edge t = 0
00076                     t = 0;
00077                     s = (d >= 0 ? 0 : (-d >= a ? 1 : -d/a));
00078                 }
00079                 else
00080                 {
00081                     // min on edge s=0
00082                     s = 0;
00083                     t = (e >= 0 ? 0 : (-e >= c ? 1 : -e/c));
00084                 }
00085             }
00086             else
00087             {
00088                 // Region 3. Min on edge s = 0
00089                 s = 0;
00090                 t = (e >= 0 ? 0 : (-e >= c ? 1 : -e/c));
00091             }
00092         }
00093         else if (t < 0)
00094         {
00095             // Region 5
00096             t = 0;
00097             s = (d >= 0 ? 0 : (-d >= a ? 1 : -d/a));
00098         }
00099         else
00100         {
00101             // Region 0
00102             const scalar invDet = 1/det;
00103             s *= invDet;
00104             t *= invDet;
00105 
00106             inside = true;
00107         }
00108     }
00109     else
00110     {
00111         if (s < 0)
00112         {
00113             // Region 2
00114             const scalar tmp0 = b + d;
00115             const scalar tmp1 = c + e;
00116             if (tmp1 > tmp0)
00117             {
00118                 // min on edge s+t=1
00119                 const scalar numer = tmp1 - tmp0;
00120                 const scalar denom = a-2*b+c;
00121                 s = (numer >= denom ? 1 : numer/denom);
00122                 t = 1 - s;
00123             }
00124             else
00125             {
00126                 // min on edge s=0
00127                 s = 0;
00128                 t = (tmp1 <= 0 ? 1 : (e >= 0 ? 0 : - e/c));
00129             }
00130         }
00131         else if (t < 0)
00132         {
00133             // Region 6
00134             const scalar tmp0 = b + d;
00135             const scalar tmp1 = c + e;
00136             if (tmp1 > tmp0)
00137             {
00138                 // min on edge s+t=1
00139                 const scalar numer = tmp1 - tmp0;
00140                 const scalar denom = a-2*b+c;
00141                 s = (numer >= denom ? 1 : numer/denom);
00142                 t = 1 - s;
00143             }
00144             else
00145             {
00146                 // min on edge t=0
00147                 t = 0;
00148                 s = (tmp1 <= 0 ? 1 : (d >= 0 ? 0 : - d/a));
00149             }
00150         }
00151         else
00152         {
00153             // Region 1
00154             const scalar numer = c+e-(b+d);
00155             if (numer <= 0)
00156             {
00157                 s = 0;
00158             }
00159             else
00160             {
00161                 const scalar denom = a-2*b+c;
00162                 s = (numer >= denom ? 1 : numer/denom);
00163             }
00164         }
00165 
00166         t = 1 - s;
00167     }
00168 
00169     // Calculate distance.
00170     // Note: Foam::mag used since truncation error causes negative distances
00171     // with points very close to one of the triangle vertices.
00172     // (Up to -2.77556e-14 seen). Could use +SMALL but that not large enough.
00173 
00174     return pointHit
00175     (
00176         inside,
00177         baseVertex + s*E0 + t*E1,
00178         Foam::sqrt
00179         (
00180             Foam::mag(a*s*s + 2*b*s*t + c*t*t + 2*d*s + 2*e*t + f)
00181         ),
00182         !inside
00183     );
00184 }
00185 
00186 
00187 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
00188 
00189 template<class Point, class PointRef>
00190 inline triangle<Point, PointRef>::triangle
00191 (
00192     const Point& a,
00193     const Point& b,
00194     const Point& c
00195 )
00196 :
00197     a_(a),
00198     b_(b),
00199     c_(c)
00200 {}
00201 
00202 
00203 template<class Point, class PointRef>
00204 inline triangle<Point, PointRef>::triangle(Istream& is)
00205 {
00206     // Read beginning of triangle point pair
00207     is.readBegin("triangle");
00208 
00209     is >> a_ >> b_ >> c_;
00210 
00211     // Read end of triangle point pair
00212     is.readEnd("triangle");
00213 
00214     // Check state of Istream
00215     is.check("triangle::triangle(Istream& is)");
00216 }
00217 
00218 
00219 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
00220 
00221 template<class Point, class PointRef>
00222 inline const Point& triangle<Point, PointRef>::a() const
00223 {
00224     return a_;
00225 }
00226 
00227 template<class Point, class PointRef>
00228 inline const Point& triangle<Point, PointRef>::b() const
00229 {
00230     return b_;
00231 }
00232 
00233 template<class Point, class PointRef>
00234 inline const Point& triangle<Point, PointRef>::c() const
00235 {
00236     return c_;
00237 }
00238 
00239 
00240 template<class Point, class PointRef>
00241 inline Point triangle<Point, PointRef>::centre() const
00242 {
00243     return (1.0/3.0)*(a_ + b_ + c_);
00244 }
00245 
00246 
00247 template<class Point, class PointRef>
00248 inline scalar triangle<Point, PointRef>::mag() const
00249 {
00250     return ::Foam::mag(normal());
00251 }
00252 
00253 
00254 template<class Point, class PointRef>
00255 inline vector triangle<Point, PointRef>::normal() const
00256 {
00257     return 0.5*((b_ - a_)^(c_ - a_));
00258 }
00259 
00260 
00261 template<class Point, class PointRef>
00262 inline vector triangle<Point, PointRef>::circumCentre() const
00263 {
00264     scalar d1 = (c_ - a_)&(b_ - a_);
00265     scalar d2 = -(c_ - b_)&(b_ - a_);
00266     scalar d3 = (c_ - a_)&(c_ - b_);
00267 
00268     scalar c1 = d2*d3;
00269     scalar c2 = d3*d1;
00270     scalar c3 = d1*d2;
00271 
00272     scalar c = c1 + c2 + c3;
00273 
00274     return
00275     (
00276         ((c2 + c3)*a_ + (c3 + c1)*b_ + (c1 + c2)*c_)/(2*c)
00277     );
00278 }
00279 
00280 
00281 template<class Point, class PointRef>
00282 inline scalar triangle<Point, PointRef>::circumRadius() const
00283 {
00284     scalar d1 = (c_ - a_) & (b_ - a_);
00285     scalar d2 = - (c_ - b_) & (b_ - a_);
00286     scalar d3 = (c_ - a_) & (c_ - b_);
00287 
00288     scalar denom = d2*d3 + d3*d1 + d1*d2;
00289 
00290     if (Foam::mag(denom) < VSMALL)
00291     {
00292         return GREAT;
00293     }
00294     else
00295     {
00296         scalar a = (d1 + d2)*(d2 + d3)*(d3 + d1) / denom;
00297 
00298         return 0.5*Foam::sqrt(min(GREAT, max(0, a)));
00299     }
00300 }
00301 
00302 
00303 template<class Point, class PointRef>
00304 inline scalar triangle<Point, PointRef>::quality() const
00305 {
00306     return
00307         mag()
00308       / (
00309             mathematicalConstant::pi
00310           * Foam::sqr(circumRadius())
00311           * 0.413497
00312           + VSMALL
00313         );
00314 }
00315 
00316 
00317 template<class Point, class PointRef>
00318 inline scalar triangle<Point, PointRef>::sweptVol(const triangle& t) const
00319 {
00320     return (1.0/12.0)*
00321     (
00322         ((t.a_ - a_) & ((b_ - a_)^(c_ - a_)))
00323       + ((t.b_ - b_) & ((c_ - b_)^(t.a_ - b_)))
00324       + ((c_ - t.c_) & ((t.b_ - t.c_)^(t.a_ - t.c_)))
00325 
00326       + ((t.a_ - a_) & ((b_ - a_)^(c_ - a_)))
00327       + ((b_ - t.b_) & ((t.a_ - t.b_)^(t.c_ - t.b_)))
00328       + ((c_ - t.c_) & ((b_ - t.c_)^(t.a_ - t.c_)))
00329     );
00330 }
00331 
00332 
00333 template<class Point, class PointRef>
00334 inline pointHit triangle<Point, PointRef>::ray
00335 (
00336     const point& p,
00337     const vector& q,
00338     const intersection::algorithm alg,
00339     const intersection::direction dir
00340 ) const
00341 {
00342     // Express triangle in terms of baseVertex (point a_) and
00343     // two edge vectors
00344     vector E0 = b_ - a_;
00345     vector E1 = c_ - a_;
00346 
00347     // Initialize intersection to miss.
00348     pointHit inter(p);
00349 
00350     vector n(0.5*(E0 ^ E1));
00351     scalar area = Foam::mag(n);
00352 
00353     if (area < VSMALL)
00354     {
00355         // Ineligible miss.
00356         inter.setMiss(false);
00357 
00358         // The miss point is the nearest point on the triangle. Take any one.
00359         inter.setPoint(a_);
00360 
00361         // The distance to the miss is the distance between the
00362         // original point and plane of intersection. No normal so use
00363         // distance from p to a_
00364         inter.setDistance(Foam::mag(a_ - p));
00365 
00366         return inter;
00367     }
00368 
00369     vector q1 = q/Foam::mag(q);
00370 
00371     if (dir == intersection::CONTACT_SPHERE)
00372     {
00373         n /= area;
00374 
00375         return ray(p, q1 - n, alg, intersection::VECTOR);
00376     }
00377 
00378     // Intersection point with triangle plane
00379     point pInter;
00380     // Is intersection point inside triangle
00381     bool hit;
00382     {
00383         // Reuse the fast ray intersection routine below in FULL_RAY
00384         // mode since the original intersection routine has rounding problems.
00385         pointHit fastInter = intersection(p, q1, intersection::FULL_RAY);
00386         hit = fastInter.hit();
00387 
00388         if (hit)
00389         {
00390             pInter = fastInter.rawPoint();
00391         }
00392         else
00393         {
00394             // Calculate intersection of ray with triangle plane
00395             vector v = a_ - p;
00396             pInter = p + (q1&v)*q1;
00397         }
00398     }
00399 
00400     // Distance to intersection point
00401     scalar dist = q1 & (pInter - p);
00402 
00403     const scalar planarPointTol =
00404         Foam::min
00405         (
00406             Foam::min
00407             (
00408                 Foam::mag(E0),
00409                 Foam::mag(E1)
00410             ),
00411             Foam::mag(c_ - b_)
00412         )*intersection::planarTol();
00413 
00414     bool eligible =
00415         alg == intersection::FULL_RAY
00416      || (alg == intersection::HALF_RAY && dist > -planarPointTol)
00417      || (
00418             alg == intersection::VISIBLE
00419          && ((q1 & normal()) < -VSMALL)
00420         );
00421 
00422     if (hit && eligible)
00423     {
00424         // Hit. Set distance to intersection.
00425         inter.setHit();
00426         inter.setPoint(pInter);
00427         inter.setDistance(dist);
00428     }
00429     else
00430     {
00431         // Miss or ineligible hit.
00432         inter.setMiss(eligible);
00433 
00434         // The miss point is the nearest point on the triangle
00435         inter.setPoint(nearestPoint(a_, E0, E1, p).rawPoint());
00436 
00437         // The distance to the miss is the distance between the
00438         // original point and plane of intersection
00439         inter.setDistance(Foam::mag(pInter - p));
00440     }
00441 
00442 
00443     return inter;
00444 }
00445 
00446 
00447 // From "Fast, Minimum Storage Ray/Triangle Intersection"
00448 // Moeller/Trumbore.
00449 template<class Point, class PointRef>
00450 inline pointHit triangle<Point, PointRef>::intersection
00451 (
00452     const point& orig,
00453     const vector& dir,
00454     const intersection::algorithm alg,
00455     const scalar tol
00456 ) const
00457 {
00458     const vector edge1 = b_ - a_;
00459     const vector edge2 = c_ - a_;
00460 
00461     // begin calculating determinant - also used to calculate U parameter
00462     const vector pVec = dir ^ edge2;
00463 
00464     // if determinant is near zero, ray lies in plane of triangle
00465     const scalar det = edge1 & pVec;
00466 
00467     // Initialise to miss
00468     pointHit intersection(false, vector::zero, GREAT, false);
00469 
00470     if (alg == intersection::VISIBLE)
00471     {
00472         // Culling branch
00473         if (det < ROOTVSMALL)
00474         {
00475             // Ray on wrong side of triangle. Return miss
00476             return intersection;
00477         }
00478     }
00479     else if (alg == intersection::HALF_RAY || alg == intersection::FULL_RAY)
00480     {
00481         // Non-culling branch
00482         if (det > -ROOTVSMALL && det < ROOTVSMALL)
00483         {
00484             // Ray parallel to triangle. Return miss
00485             return intersection;
00486         }
00487     }
00488 
00489     const scalar inv_det = 1.0 / det;
00490 
00491     /* calculate distance from a_ to ray origin */
00492     const vector tVec = orig-a_;
00493 
00494     /* calculate U parameter and test bounds */
00495     const scalar u = (tVec & pVec)*inv_det;
00496 
00497     if (u < -tol || u > 1.0+tol)
00498     {
00499         // return miss
00500         return intersection;
00501     }
00502 
00503     /* prepare to test V parameter */
00504     const vector qVec = tVec ^ edge1;
00505 
00506     /* calculate V parameter and test bounds */
00507     const scalar v = (dir & qVec) * inv_det;
00508 
00509     if (v < -tol || u + v > 1.0+tol)
00510     {
00511         // return miss
00512         return intersection;
00513     }
00514 
00515     /* calculate t, scale parameters, ray intersects triangle */
00516     const scalar t = (edge2 & qVec) * inv_det;
00517 
00518     if (alg == intersection::HALF_RAY && t < -tol)
00519     {
00520         // Wrong side of orig. Return miss
00521         return intersection;
00522     }
00523 
00524     intersection.setHit();
00525     intersection.setPoint(a_ + u*edge1 + v*edge2);
00526     intersection.setDistance(t);
00527 
00528     return intersection;
00529 }
00530 
00531 
00532 template<class Point, class PointRef>
00533 inline pointHit triangle<Point, PointRef>::nearestPoint
00534 (
00535     const point& p
00536 ) const
00537 {
00538     // Express triangle in terms of baseVertex (point a_) and
00539     // two edge vectors
00540     vector E0 = b_ - a_;
00541     vector E1 = c_ - a_;
00542 
00543     return nearestPoint(a_, E0, E1, p);
00544 }
00545 
00546 
00547 template<class Point, class PointRef>
00548 inline bool triangle<Point, PointRef>::classify
00549 (
00550     const point& p,
00551     const scalar tol,
00552     label& nearType,
00553     label& nearLabel
00554 ) const
00555 {
00556     const vector E0 = b_ - a_;
00557     const vector E1 = c_ - a_;
00558     const vector n = 0.5*(E0 ^ E1);
00559 
00560     // Get largest component of normal
00561     scalar magX = Foam::mag(n.x());
00562     scalar magY = Foam::mag(n.y());
00563     scalar magZ = Foam::mag(n.z());
00564 
00565     label i0 = -1;
00566     if ((magX >= magY) && (magX >= magZ))
00567     {
00568         i0 = 0;
00569     }
00570     else if ((magY >= magX) && (magY >= magZ))
00571     {
00572         i0 = 1;
00573     }
00574     else
00575     {
00576         i0 = 2;
00577     }
00578 
00579     // Get other components
00580     label i1 = (i0 + 1) % 3;
00581     label i2 = (i1 + 1) % 3;
00582 
00583 
00584     scalar u1 = E0[i1];
00585     scalar v1 = E0[i2];
00586 
00587     scalar u2 = E1[i1];
00588     scalar v2 = E1[i2];
00589 
00590     scalar det = v2*u1 - u2*v1;
00591 
00592     scalar u0 = p[i1] - a_[i1];
00593     scalar v0 = p[i2] - a_[i2];
00594 
00595     scalar alpha = 0;
00596     scalar beta = 0;
00597 
00598     bool hit = false;
00599 
00600     if (Foam::mag(u1) < ROOTVSMALL)
00601     {
00602         beta = u0/u2;
00603 
00604         alpha = (v0 - beta*v2)/v1;
00605 
00606         hit = ((alpha >= 0) && ((alpha + beta) <= 1));
00607     }
00608     else
00609     {
00610         beta = (v0*u1 - u0*v1)/det;
00611 
00612         alpha = (u0 - beta*u2)/u1;
00613 
00614         hit = ((alpha >= 0) && ((alpha + beta) <= 1));
00615     }
00616 
00617     //
00618     // Now alpha, beta are the coordinates in the triangle local coordinate
00619     // system E0, E1
00620     //
00621 
00622     //Pout<< "alpha:" << alpha << endl;
00623     //Pout<< "beta:" << beta << endl;
00624     //Pout<< "hit:" << hit << endl;
00625     //Pout<< "tol:" << tol << endl;
00626 
00627     if (hit)
00628     {
00629         // alpha,beta might get negative due to precision errors
00630         alpha = max(0.0, min(1.0, alpha));
00631         beta = max(0.0, min(1.0, beta));
00632     }
00633 
00634     nearType = NONE;
00635     nearLabel = -1;
00636 
00637     if (Foam::mag(alpha+beta-1) <= tol)
00638     {
00639         // On edge between vert 1-2 (=edge 1)
00640 
00641         if (Foam::mag(alpha) <= tol)
00642         {
00643             nearType = POINT;
00644             nearLabel = 2;
00645         }
00646         else if (Foam::mag(beta) <= tol)
00647         {
00648             nearType = POINT;
00649             nearLabel = 1;
00650         }
00651         else if ((alpha >= 0) && (alpha <= 1) && (beta >= 0) && (beta <= 1))
00652         {
00653             nearType = EDGE;
00654             nearLabel = 1;
00655         }
00656     }
00657     else if (Foam::mag(alpha) <= tol)
00658     {
00659         // On edge between vert 2-0 (=edge 2)
00660 
00661         if (Foam::mag(beta) <= tol)
00662         {
00663             nearType = POINT;
00664             nearLabel = 0;
00665         }
00666         else if (Foam::mag(beta-1) <= tol)
00667         {
00668             nearType = POINT;
00669             nearLabel = 2;
00670         }
00671         else if ((beta >= 0) && (beta <= 1))
00672         {
00673             nearType = EDGE;
00674             nearLabel = 2;
00675         }
00676     }
00677     else if (Foam::mag(beta) <= tol)
00678     {
00679         // On edge between vert 0-1 (= edge 0)
00680 
00681         if (Foam::mag(alpha) <= tol)
00682         {
00683             nearType = POINT;
00684             nearLabel = 0;
00685         }
00686         else if (Foam::mag(alpha-1) <= tol)
00687         {
00688             nearType = POINT;
00689             nearLabel = 1;
00690         }
00691         else if ((alpha >= 0) && (alpha <= 1))
00692         {
00693             nearType = EDGE;
00694             nearLabel = 0;
00695         }
00696     }
00697 
00698     return hit;
00699 }
00700 
00701 
00702 
00703 
00704 
00705 // * * * * * * * * * * * * * * * Ostream Operator  * * * * * * * * * * * * * //
00706 
00707 template<class point, class pointRef>
00708 inline Istream& operator>>(Istream& is, triangle<point, pointRef>& t)
00709 {
00710     // Read beginning of triangle point pair
00711     is.readBegin("triangle");
00712 
00713     is >> t.a_ >> t.b_ >> t.c_;
00714 
00715     // Read end of triangle point pair
00716     is.readEnd("triangle");
00717 
00718     // Check state of Ostream
00719     is.check("Istream& operator>>(Istream&, triangle&)");
00720 
00721     return is;
00722 }
00723 
00724 
00725 template<class Point, class PointRef>
00726 inline Ostream& operator<<(Ostream& os, const triangle<Point, PointRef>& t)
00727 {
00728     os  << nl
00729         << token::BEGIN_LIST
00730         << t.a_ << token::SPACE << t.b_ << token::SPACE << t.c_
00731         << token::END_LIST;
00732 
00733     return os;
00734 }
00735 
00736 
00737 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
00738 
00739 } // End namespace Foam
00740 
00741 // ************************ vim: set sw=4 sts=4 et: ************************ //
00742 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines