1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright (C) 1991-2009 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 "searchableCylinder.H"
28 #include "addToRunTimeSelectionTable.H"
30 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
35 defineTypeNameAndDebug(searchableCylinder, 0);
36 addToRunTimeSelectionTable(searchableSurface, searchableCylinder, dict);
41 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
43 Foam::pointField Foam::searchableCylinder::coordinates() const
45 pointField ctrs(1, 0.5*(point1_ + point2_));
51 Foam::pointIndexHit Foam::searchableCylinder::findNearest
54 const scalar nearestDistSqr
57 pointIndexHit info(false, sample, -1);
59 vector v(sample - point1_);
61 // Decompose sample-point1 into normal and parallel component
62 scalar parallel = (v & unitDir_);
64 // Remove the parallel component and normalise
65 v -= parallel*unitDir_;
68 if (magV < ROOTVSMALL)
79 // nearest is at point1 end cap. Clip to radius.
80 info.setPoint(point1_ + min(magV, radius_)*v);
82 else if (parallel >= magDir_)
84 // nearest is at point2 end cap. Clip to radius.
85 info.setPoint(point2_ + min(magV, radius_)*v);
89 // inbetween endcaps. Might either be nearer endcaps or cylinder wall
91 // distance to endpoint: parallel or parallel-magDir
92 // distance to cylinder wall: magV-radius_
94 // Nearest cylinder point
95 point cylPt = sample + (radius_-magV)*v;
97 if (parallel < 0.5*magDir_)
99 // Project onto p1 endcap
100 point end1Pt = point1_ + min(magV, radius_)*v;
102 if (magSqr(sample-cylPt) < magSqr(sample-end1Pt))
104 info.setPoint(cylPt);
108 info.setPoint(end1Pt);
113 // Project onto p2 endcap
114 point end2Pt = point2_ + min(magV, radius_)*v;
116 if (magSqr(sample-cylPt) < magSqr(sample-end2Pt))
118 info.setPoint(cylPt);
122 info.setPoint(end2Pt);
127 if (magSqr(sample - info.rawPoint()) < nearestDistSqr)
137 Foam::scalar Foam::searchableCylinder::radius2(const point& pt) const
139 const vector x = (pt-point1_) ^ unitDir_;
144 // From http://www.gamedev.net/community/forums/topic.asp?topic_id=467789 -
145 // intersection of cylinder with ray
146 void Foam::searchableCylinder::findLineAll
157 vector point1Start(start-point1_);
158 vector point2Start(start-point2_);
159 vector point1End(end-point1_);
161 // Quick rejection of complete vector outside endcaps
162 scalar s1 = point1Start&unitDir_;
163 scalar s2 = point1End&unitDir_;
165 if ((s1 < 0 && s2 < 0) || (s1 > magDir_ && s2 > magDir_))
170 // Line as P = start+t*V where V is unit vector and t=[0..mag(end-start)]
172 scalar magV = mag(V);
173 if (magV < ROOTVSMALL)
180 // We now get the nearest intersections to start. This can either be
181 // the intersection with the end plane or with the cylinder side.
183 // Get the two points (expressed in t) on the end planes. This is to
184 // clip any cylinder intersection against.
188 // Maintain the two intersections with the endcaps
189 scalar tNear = VGREAT;
190 scalar tFar = VGREAT;
193 scalar s = (V&unitDir_);
197 tPoint2 = -(point2Start&unitDir_)/s;
198 if (tPoint2 < tPoint1)
200 Swap(tPoint1, tPoint2);
202 if (tPoint1 > magV || tPoint2 < 0)
207 // See if the points on the endcaps are actually inside the cylinder
208 if (tPoint1 >= 0 && tPoint1 <= magV)
210 if (radius2(start+tPoint1*V) <= sqr(radius_))
215 if (tPoint2 >= 0 && tPoint2 <= magV)
217 if (radius2(start+tPoint2*V) <= sqr(radius_))
219 // Check if already have a near hit from point1
233 // Vector perpendicular to cylinder. Check for outside already done
234 // above so just set tpoint to allow all.
241 const vector x = point1Start ^ unitDir_;
242 const vector y = V ^ unitDir_;
243 const scalar d = sqr(radius_);
245 // Second order equation of the form a*t^2 + b*t + c
246 const scalar a = (y&y);
247 const scalar b = 2*(x&y);
248 const scalar c = (x&x)-d;
250 const scalar disc = b*b-4*a*c;
260 else if (disc < ROOTVSMALL)
263 if (mag(a) > ROOTVSMALL)
267 //Pout<< "single solution t:" << t1
268 // << " for start:" << start << " end:" << end
269 // << " c:" << c << endl;
271 if (t1 >= 0 && t1 <= magV && t1 >= tPoint1 && t1 <= tPoint2)
273 // valid. Insert sorted.
291 // Aligned with axis. Check if outside radius
292 //Pout<< "small discriminant:" << disc
293 // << " for start:" << start << " end:" << end
294 // << " magV:" << magV
295 // << " c:" << c << endl;
304 if (mag(a) > ROOTVSMALL)
306 scalar sqrtDisc = sqrt(disc);
308 t1 = (-b - sqrtDisc)/(2*a);
309 t2 = (-b + sqrtDisc)/(2*a);
315 if (t1 >= 0 && t1 <= magV && t1 >= tPoint1 && t1 <= tPoint2)
317 // valid. Insert sorted.
328 if (t2 >= 0 && t2 <= magV && t2 >= tPoint1 && t2 <= tPoint2)
330 // valid. Insert sorted.
341 //Pout<< "two solutions t1:" << t1 << " t2:" << t2
342 // << " for start:" << start << " end:" << end
343 // << " magV:" << magV
344 // << " c:" << c << endl;
348 // Aligned with axis. Check if outside radius
349 //Pout<< "large discriminant:" << disc
350 // << " small a:" << a
351 // << " for start:" << start << " end:" << end
352 // << " magV:" << magV
353 // << " c:" << c << endl;
362 if (tNear >= 0 && tNear <= magV)
364 near.setPoint(start+tNear*V);
370 far.setPoint(start+tFar*V);
375 else if (tFar >= 0 && tFar <= magV)
377 near.setPoint(start+tFar*V);
384 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
386 Foam::searchableCylinder::searchableCylinder
394 searchableSurface(io),
397 magDir_(mag(point2_-point1_)),
398 unitDir_((point2_-point1_)/magDir_),
403 Foam::searchableCylinder::searchableCylinder
406 const dictionary& dict
409 searchableSurface(io),
410 point1_(dict.lookup("point1")),
411 point2_(dict.lookup("point2")),
412 magDir_(mag(point2_-point1_)),
413 unitDir_((point2_-point1_)/magDir_),
414 radius_(readScalar(dict.lookup("radius")))
418 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
420 Foam::searchableCylinder::~searchableCylinder()
424 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
426 const Foam::wordList& Foam::searchableCylinder::regions() const
428 if (regions_.empty())
431 regions_[0] = "region0";
437 void Foam::searchableCylinder::findNearest
439 const pointField& samples,
440 const scalarField& nearestDistSqr,
441 List<pointIndexHit>& info
444 info.setSize(samples.size());
448 info[i] = findNearest(samples[i], nearestDistSqr[i]);
453 void Foam::searchableCylinder::findLine
455 const pointField& start,
456 const pointField& end,
457 List<pointIndexHit>& info
460 info.setSize(start.size());
464 // Pick nearest intersection. If none intersected take second one.
466 findLineAll(start[i], end[i], info[i], b);
467 if (!info[i].hit() && b.hit())
475 void Foam::searchableCylinder::findLineAny
477 const pointField& start,
478 const pointField& end,
479 List<pointIndexHit>& info
482 info.setSize(start.size());
486 // Discard far intersection
488 findLineAll(start[i], end[i], info[i], b);
489 if (!info[i].hit() && b.hit())
497 void Foam::searchableCylinder::findLineAll
499 const pointField& start,
500 const pointField& end,
501 List<List<pointIndexHit> >& info
504 info.setSize(start.size());
508 pointIndexHit near, far;
509 findLineAll(start[i], end[i], near, far);
541 void Foam::searchableCylinder::getRegion
543 const List<pointIndexHit>& info,
547 region.setSize(info.size());
552 void Foam::searchableCylinder::getNormal
554 const List<pointIndexHit>& info,
558 normal.setSize(info.size());
559 normal = vector::zero;
565 vector v(info[i].hitPoint() - point1_);
567 // Decompose sample-point1 into normal and parallel component
568 scalar parallel = v & unitDir_;
572 normal[i] = -unitDir_;
574 else if (parallel > magDir_)
576 normal[i] = -unitDir_;
580 // Remove the parallel component
581 v -= parallel*unitDir_;
582 normal[i] = v/mag(v);
589 void Foam::searchableCylinder::getVolumeType
591 const pointField& points,
592 List<volumeType>& volType
595 volType.setSize(points.size());
598 forAll(points, pointI)
600 const point& pt = points[pointI];
602 vector v(pt - point1_);
604 // Decompose sample-point1 into normal and parallel component
605 scalar parallel = v & unitDir_;
609 // left of point1 endcap
610 volType[pointI] = OUTSIDE;
612 else if (parallel > magDir_)
614 // right of point2 endcap
615 volType[pointI] = OUTSIDE;
619 // Remove the parallel component
620 v -= parallel*unitDir_;
622 if (mag(v) > radius_)
624 volType[pointI] = OUTSIDE;
628 volType[pointI] = INSIDE;
635 // ************************************************************************* //