1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright (C) 2011 OpenFOAM Foundation
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
13 the Free Software Foundation, either version 3 of the License, or
14 (at your 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, see <http://www.gnu.org/licenses/>.
24 \*---------------------------------------------------------------------------*/
26 #include "searchableCylinder.H"
27 #include "addToRunTimeSelectionTable.H"
29 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
34 defineTypeNameAndDebug(searchableCylinder, 0);
35 addToRunTimeSelectionTable(searchableSurface, searchableCylinder, dict);
40 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
42 Foam::pointField Foam::searchableCylinder::coordinates() const
44 pointField ctrs(1, 0.5*(point1_ + point2_));
50 Foam::pointIndexHit Foam::searchableCylinder::findNearest
53 const scalar nearestDistSqr
56 pointIndexHit info(false, sample, -1);
58 vector v(sample - point1_);
60 // Decompose sample-point1 into normal and parallel component
61 scalar parallel = (v & unitDir_);
63 // Remove the parallel component and normalise
64 v -= parallel*unitDir_;
67 if (magV < ROOTVSMALL)
78 // nearest is at point1 end cap. Clip to radius.
79 info.setPoint(point1_ + min(magV, radius_)*v);
81 else if (parallel >= magDir_)
83 // nearest is at point2 end cap. Clip to radius.
84 info.setPoint(point2_ + min(magV, radius_)*v);
88 // inbetween endcaps. Might either be nearer endcaps or cylinder wall
90 // distance to endpoint: parallel or parallel-magDir
91 // distance to cylinder wall: magV-radius_
93 // Nearest cylinder point
95 if (magV < ROOTVSMALL)
97 // Point exactly on centre line. Take any point on wall.
98 vector e1 = point(1,0,0) ^ unitDir_;
99 scalar magE1 = mag(e1);
102 e1 = point(0,1,0) ^ unitDir_;
106 cylPt = sample + radius_*e1;
110 cylPt = sample + (radius_-magV)*v;
113 if (parallel < 0.5*magDir_)
115 // Project onto p1 endcap
116 point end1Pt = point1_ + min(magV, radius_)*v;
118 if (magSqr(sample-cylPt) < magSqr(sample-end1Pt))
120 info.setPoint(cylPt);
124 info.setPoint(end1Pt);
129 // Project onto p2 endcap
130 point end2Pt = point2_ + min(magV, radius_)*v;
132 if (magSqr(sample-cylPt) < magSqr(sample-end2Pt))
134 info.setPoint(cylPt);
138 info.setPoint(end2Pt);
143 if (magSqr(sample - info.rawPoint()) < nearestDistSqr)
153 Foam::scalar Foam::searchableCylinder::radius2(const point& pt) const
155 const vector x = (pt-point1_) ^ unitDir_;
160 // From http://www.gamedev.net/community/forums/topic.asp?topic_id=467789 -
161 // intersection of cylinder with ray
162 void Foam::searchableCylinder::findLineAll
173 vector point1Start(start-point1_);
174 vector point2Start(start-point2_);
175 vector point1End(end-point1_);
177 // Quick rejection of complete vector outside endcaps
178 scalar s1 = point1Start&unitDir_;
179 scalar s2 = point1End&unitDir_;
181 if ((s1 < 0 && s2 < 0) || (s1 > magDir_ && s2 > magDir_))
186 // Line as P = start+t*V where V is unit vector and t=[0..mag(end-start)]
188 scalar magV = mag(V);
189 if (magV < ROOTVSMALL)
196 // We now get the nearest intersections to start. This can either be
197 // the intersection with the end plane or with the cylinder side.
199 // Get the two points (expressed in t) on the end planes. This is to
200 // clip any cylinder intersection against.
204 // Maintain the two intersections with the endcaps
205 scalar tNear = VGREAT;
206 scalar tFar = VGREAT;
209 scalar s = (V&unitDir_);
213 tPoint2 = -(point2Start&unitDir_)/s;
214 if (tPoint2 < tPoint1)
216 Swap(tPoint1, tPoint2);
218 if (tPoint1 > magV || tPoint2 < 0)
223 // See if the points on the endcaps are actually inside the cylinder
224 if (tPoint1 >= 0 && tPoint1 <= magV)
226 if (radius2(start+tPoint1*V) <= sqr(radius_))
231 if (tPoint2 >= 0 && tPoint2 <= magV)
233 if (radius2(start+tPoint2*V) <= sqr(radius_))
235 // Check if already have a near hit from point1
249 // Vector perpendicular to cylinder. Check for outside already done
250 // above so just set tpoint to allow all.
257 const vector x = point1Start ^ unitDir_;
258 const vector y = V ^ unitDir_;
259 const scalar d = sqr(radius_);
261 // Second order equation of the form a*t^2 + b*t + c
262 const scalar a = (y&y);
263 const scalar b = 2*(x&y);
264 const scalar c = (x&x)-d;
266 const scalar disc = b*b-4*a*c;
276 else if (disc < ROOTVSMALL)
279 if (mag(a) > ROOTVSMALL)
283 //Pout<< "single solution t:" << t1
284 // << " for start:" << start << " end:" << end
285 // << " c:" << c << endl;
287 if (t1 >= 0 && t1 <= magV && t1 >= tPoint1 && t1 <= tPoint2)
289 // valid. Insert sorted.
307 // Aligned with axis. Check if outside radius
308 //Pout<< "small discriminant:" << disc
309 // << " for start:" << start << " end:" << end
310 // << " magV:" << magV
311 // << " c:" << c << endl;
320 if (mag(a) > ROOTVSMALL)
322 scalar sqrtDisc = sqrt(disc);
324 t1 = (-b - sqrtDisc)/(2*a);
325 t2 = (-b + sqrtDisc)/(2*a);
331 if (t1 >= 0 && t1 <= magV && t1 >= tPoint1 && t1 <= tPoint2)
333 // valid. Insert sorted.
344 if (t2 >= 0 && t2 <= magV && t2 >= tPoint1 && t2 <= tPoint2)
346 // valid. Insert sorted.
357 //Pout<< "two solutions t1:" << t1 << " t2:" << t2
358 // << " for start:" << start << " end:" << end
359 // << " magV:" << magV
360 // << " c:" << c << endl;
364 // Aligned with axis. Check if outside radius
365 //Pout<< "large discriminant:" << disc
366 // << " small a:" << a
367 // << " for start:" << start << " end:" << end
368 // << " magV:" << magV
369 // << " c:" << c << endl;
378 if (tNear >= 0 && tNear <= magV)
380 near.setPoint(start+tNear*V);
386 far.setPoint(start+tFar*V);
391 else if (tFar >= 0 && tFar <= magV)
393 near.setPoint(start+tFar*V);
400 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
402 Foam::searchableCylinder::searchableCylinder
410 searchableSurface(io),
413 magDir_(mag(point2_-point1_)),
414 unitDir_((point2_-point1_)/magDir_),
419 Foam::searchableCylinder::searchableCylinder
422 const dictionary& dict
425 searchableSurface(io),
426 point1_(dict.lookup("point1")),
427 point2_(dict.lookup("point2")),
428 magDir_(mag(point2_-point1_)),
429 unitDir_((point2_-point1_)/magDir_),
430 radius_(readScalar(dict.lookup("radius")))
434 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
436 Foam::searchableCylinder::~searchableCylinder()
440 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
442 const Foam::wordList& Foam::searchableCylinder::regions() const
444 if (regions_.empty())
447 regions_[0] = "region0";
453 void Foam::searchableCylinder::findNearest
455 const pointField& samples,
456 const scalarField& nearestDistSqr,
457 List<pointIndexHit>& info
460 info.setSize(samples.size());
464 info[i] = findNearest(samples[i], nearestDistSqr[i]);
469 void Foam::searchableCylinder::findLine
471 const pointField& start,
472 const pointField& end,
473 List<pointIndexHit>& info
476 info.setSize(start.size());
480 // Pick nearest intersection. If none intersected take second one.
482 findLineAll(start[i], end[i], info[i], b);
483 if (!info[i].hit() && b.hit())
491 void Foam::searchableCylinder::findLineAny
493 const pointField& start,
494 const pointField& end,
495 List<pointIndexHit>& info
498 info.setSize(start.size());
502 // Discard far intersection
504 findLineAll(start[i], end[i], info[i], b);
505 if (!info[i].hit() && b.hit())
513 void Foam::searchableCylinder::findLineAll
515 const pointField& start,
516 const pointField& end,
517 List<List<pointIndexHit> >& info
520 info.setSize(start.size());
524 pointIndexHit near, far;
525 findLineAll(start[i], end[i], near, far);
557 void Foam::searchableCylinder::getRegion
559 const List<pointIndexHit>& info,
563 region.setSize(info.size());
568 void Foam::searchableCylinder::getNormal
570 const List<pointIndexHit>& info,
574 normal.setSize(info.size());
575 normal = vector::zero;
581 vector v(info[i].hitPoint() - point1_);
583 // Decompose sample-point1 into normal and parallel component
584 scalar parallel = v & unitDir_;
588 normal[i] = -unitDir_;
590 else if (parallel > magDir_)
592 normal[i] = -unitDir_;
596 // Remove the parallel component
597 v -= parallel*unitDir_;
598 normal[i] = v/mag(v);
605 void Foam::searchableCylinder::getVolumeType
607 const pointField& points,
608 List<volumeType>& volType
611 volType.setSize(points.size());
614 forAll(points, pointI)
616 const point& pt = points[pointI];
618 vector v(pt - point1_);
620 // Decompose sample-point1 into normal and parallel component
621 scalar parallel = v & unitDir_;
625 // left of point1 endcap
626 volType[pointI] = OUTSIDE;
628 else if (parallel > magDir_)
630 // right of point2 endcap
631 volType[pointI] = OUTSIDE;
635 // Remove the parallel component
636 v -= parallel*unitDir_;
638 if (mag(v) > radius_)
640 volType[pointI] = OUTSIDE;
644 volType[pointI] = INSIDE;
651 // ************************************************************************* //