/*---------------------------------------------------------------------------*\ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 1991-2009 OpenCFD Ltd. \\/ M anipulation | ------------------------------------------------------------------------------- License This file is part of OpenFOAM. OpenFOAM is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. OpenFOAM is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenFOAM. If not, see . \*---------------------------------------------------------------------------*/ #include "treeDataTriSurface.H" #include "triSurfaceTools.H" #include "triangleFuncs.H" #include "indexedOctree.H" // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // defineTypeNameAndDebug(Foam::treeDataTriSurface, 0); // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // // Fast distance to triangle calculation. From // "Distance Between Point and Trangle in 3D" // David Eberly, Magic Software Inc. Aug. 2003. // Works on function Q giving distance to point and tries to minimize this. Foam::scalar Foam::treeDataTriSurface::nearestCoords ( const point& base, const point& E0, const point& E1, const scalar a, const scalar b, const scalar c, const point& P, scalar& s, scalar& t ) { // distance vector const vector D(base - P); // Precalculate distance factors. const scalar d = E0 & D; const scalar e = E1 & D; // Do classification const scalar det = a*c - b*b; s = b*e - c*d; t = b*d - a*e; if (s+t < det) { if (s < 0) { if (t < 0) { //region 4 if (e > 0) { //min on edge t = 0 t = 0; s = (d >= 0 ? 0 : (-d >= a ? 1 : -d/a)); } else { //min on edge s=0 s = 0; t = (e >= 0 ? 0 : (-e >= c ? 1 : -e/c)); } } else { //region 3. Min on edge s = 0 s = 0; t = (e >= 0 ? 0 : (-e >= c ? 1 : -e/c)); } } else if (t < 0) { //region 5 t = 0; s = (d >= 0 ? 0 : (-d >= a ? 1 : -d/a)); } else { //region 0 const scalar invDet = 1/det; s *= invDet; t *= invDet; } } else { if (s < 0) { //region 2 const scalar tmp0 = b + d; const scalar tmp1 = c + e; if (tmp1 > tmp0) { //min on edge s+t=1 const scalar numer = tmp1 - tmp0; const scalar denom = a-2*b+c; s = (numer >= denom ? 1 : numer/denom); t = 1 - s; } else { //min on edge s=0 s = 0; t = (tmp1 <= 0 ? 1 : (e >= 0 ? 0 : - e/c)); } } else if (t < 0) { //region 6 const scalar tmp0 = b + d; const scalar tmp1 = c + e; if (tmp1 > tmp0) { //min on edge s+t=1 const scalar numer = tmp1 - tmp0; const scalar denom = a-2*b+c; s = (numer >= denom ? 1 : numer/denom); t = 1 - s; } else { //min on edge t=0 t = 0; s = (tmp1 <= 0 ? 1 : (d >= 0 ? 0 : - d/a)); } } else { //region 1 const scalar numer = c+e-(b+d); if (numer <= 0) { s = 0; } else { const scalar denom = a-2*b+c; s = (numer >= denom ? 1 : numer/denom); } } t = 1 - s; } // Calculate distance. // Note: abs should not be needed but truncation error causes problems // with points very close to one of the triangle vertices. // (seen up to -9e-15). Alternatively add some small value. const scalar f = D & D; return Foam::mag(a*s*s + 2*b*s*t + c*t*t + 2*d*s + 2*e*t + f); } // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // // Construct from components Foam::treeDataTriSurface::treeDataTriSurface(const triSurface& surface) : surface_(surface) {} // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // Foam::pointField Foam::treeDataTriSurface::points() const { const pointField& points = surface_.points(); pointField centres(surface_.size()); forAll(surface_, triI) { centres[triI] = surface_[triI].centre(points); } return centres; } //- Get type of sample (inside/outside/mixed) w.r.t. surface. Foam::label Foam::treeDataTriSurface::getVolumeType ( const indexedOctree& tree, const point& sample ) const { // Find nearest point const treeBoundBox& treeBb = tree.bb(); pointIndexHit pHit = tree.findNearest ( sample, max ( Foam::sqr(GREAT), Foam::magSqr(treeBb.span()) ) ); if (!pHit.hit()) { FatalErrorIn("treeDataTriSurface::getVolumeType(..)") << "treeBb:" << treeBb << " sample:" << sample << " pHit:" << pHit << abort(FatalError); } triSurfaceTools::sideType t = triSurfaceTools::surfaceSide ( surface_, sample, pHit.index(), pHit.hitPoint(), indexedOctree::perturbTol() ); if (t == triSurfaceTools::UNKNOWN) { return indexedOctree::UNKNOWN; } else if (t == triSurfaceTools::INSIDE) { return indexedOctree::INSIDE; } else if (t == triSurfaceTools::OUTSIDE) { return indexedOctree::OUTSIDE; } else { FatalErrorIn("treeDataTriSurface::getVolumeType(..)") << "problem" << abort(FatalError); return indexedOctree::UNKNOWN; } } // Check if any point on triangle is inside cubeBb. bool Foam::treeDataTriSurface::overlaps ( const label index, const treeBoundBox& cubeBb ) const { const pointField& points = surface_.points(); const labelledTri& f = surface_[index]; // Triangle points const point& p0 = points[f[0]]; const point& p1 = points[f[1]]; const point& p2 = points[f[2]]; treeBoundBox triBb(p0, p0); triBb.min() = min(triBb.min(), p1); triBb.min() = min(triBb.min(), p2); triBb.max() = max(triBb.max(), p1); triBb.max() = max(triBb.max(), p2); //- For testing: robust one //return cubeBb.overlaps(triBb); //- Exact test of triangle intersecting bb // Quick rejection. If whole bounding box of tri is outside cubeBb then // there will be no intersection. if (!cubeBb.overlaps(triBb)) { return false; } // Check if one or more triangle point inside if (cubeBb.contains(p0) || cubeBb.contains(p1) || cubeBb.contains(p2)) { // One or more points inside return true; } // Now we have the difficult case: all points are outside but connecting // edges might go through cube. Use fast intersection of bounding box. //return triangleFuncs::intersectBbExact(p0, p1, p2, cubeBb); return triangleFuncs::intersectBb(p0, p1, p2, cubeBb); } // Calculate nearest point to sample. Updates (if any) nearestDistSqr, minIndex, // nearestPoint. void Foam::treeDataTriSurface::findNearest ( const labelList& indices, const point& sample, scalar& nearestDistSqr, label& minIndex, point& nearestPoint ) const { const pointField& points = surface_.points(); forAll(indices, i) { label index = indices[i]; const labelledTri& f = surface_[index]; // Triangle points const point& p0 = points[f[0]]; const point& p1 = points[f[1]]; const point& p2 = points[f[2]]; ////- Possible optimization: do quick rejection of triangle if bounding //// sphere does not intersect triangle bounding box. From simplistic //// test was not found to speed up things. // //// Triangle bounding box. //point triBbMin = min(p0, min(p1, p2)); //point triBbMax = max(p0, max(p1, p2)); // //if //( // indexedOctree::intersects // ( // triBbMin, // triBbMax, // nearestDistSqr, // sample // ) //) { // Get spanning vectors of triangle vector base(p1); vector E0(p0 - p1); vector E1(p2 - p1); scalar a(E0& E0); scalar b(E0& E1); scalar c(E1& E1); // Get nearest point in s,t coordinates (s is along E0, t is along // E1) scalar s; scalar t; scalar distSqr = nearestCoords ( base, E0, E1, a, b, c, sample, s, t ); if (distSqr < nearestDistSqr) { nearestDistSqr = distSqr; minIndex = index; nearestPoint = base + s*E0 + t*E1; } } } } // Calculate nearest point to line. Updates (if any) nearestDistSqr, minIndex, // nearestPoint. void Foam::treeDataTriSurface::findNearest ( const labelList& indices, const linePointRef& ln, treeBoundBox& tightest, label& minIndex, point& linePoint, point& nearestPoint ) const { notImplemented ( "treeDataTriSurface::findNearest(const labelList&" ", const linePointRef&, treeBoundBox&, label&, point&, point&) const" ); } bool Foam::treeDataTriSurface::intersects ( const label index, const point& start, const point& end, point& intersectionPoint ) const { const pointField& points = surface_.points(); const labelledTri& f = surface_[index]; // Do quick rejection test treeBoundBox triBb(points[f[0]], points[f[0]]); triBb.min() = min(triBb.min(), points[f[1]]); triBb.max() = max(triBb.max(), points[f[1]]); triBb.min() = min(triBb.min(), points[f[2]]); triBb.max() = max(triBb.max(), points[f[2]]); const direction startBits(triBb.posBits(start)); const direction endBits(triBb.posBits(end)); if ((startBits & endBits) != 0) { // start and end in same block outside of triBb. return false; } const triPointRef tri(points[f[0]], points[f[1]], points[f[2]]); const vector dir(end - start); // Use relative tolerance (from octree) to determine intersection. pointHit inter = tri.intersection ( start, dir, intersection::HALF_RAY, indexedOctree::perturbTol() ); if (inter.hit() && inter.distance() <= 1) { // Note: no extra test on whether intersection is in front of us // since using half_ray. intersectionPoint = inter.hitPoint(); return true; } else { return false; } //- Using exact intersections //pointHit info = f.tri(points).intersectionExact(start, end); // //if (info.hit()) //{ // intersectionPoint = info.hitPoint(); //} //return info.hit(); } // ************************************************************************* //