searchableBox.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration |
5  \\ / A nd | www.openfoam.com
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8  Copyright (C) 2011-2016 OpenFOAM Foundation
9  Copyright (C) 2018-2022 OpenCFD Ltd.
10 -------------------------------------------------------------------------------
11 License
12  This file is part of OpenFOAM.
13 
14  OpenFOAM is free software: you can redistribute it and/or modify it
15  under the terms of the GNU General Public License as published by
16  the Free Software Foundation, either version 3 of the License, or
17  (at your option) any later version.
18 
19  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22  for more details.
23 
24  You should have received a copy of the GNU General Public License
25  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26 
27 \*---------------------------------------------------------------------------*/
28 
29 #include "searchableBox.H"
31 #include "SortableList.H"
32 
33 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
34 
35 namespace Foam
36 {
37  defineTypeNameAndDebug(searchableBox, 0);
39  (
40  searchableSurface,
41  searchableBox,
42  dict
43  );
45  (
46  searchableSurface,
47  searchableBox,
48  dict,
49  box
50  );
51 }
52 
53 
54 // * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
55 
56 namespace Foam
57 {
58 
59 // Read min/max or min/span
60 static void readBoxDim(const dictionary& dict, treeBoundBox& bb)
61 {
63 
64  dict.readEntry("min", bb.min(), keyType::LITERAL, readOpt);
65 
66  if (dict.readIfPresent("span", bb.max(), keyType::LITERAL))
67  {
68  bb.max() += bb.min();
70  }
71 
72  dict.readEntry("max", bb.max(), keyType::LITERAL, readOpt);
73 }
74 
75 } // End namespace Foam
76 
77 
78 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
79 
80 void Foam::searchableBox::projectOntoCoordPlane
81 (
82  const direction dir,
83  const point& planePt,
84  pointIndexHit& info
85 ) const
86 {
87  // Set point
88  info.point()[dir] = planePt[dir];
89 
90  // Set face
91  if (planePt[dir] == min()[dir])
92  {
93  info.setIndex(dir*2);
94  }
95  else if (planePt[dir] == max()[dir])
96  {
97  info.setIndex(dir*2+1);
98  }
99  else
100  {
102  << "Point on plane " << planePt
103  << " is not on coordinate " << min()[dir]
104  << " nor " << max()[dir] << nl
105  << abort(FatalError);
106  }
107 }
108 
109 
110 // Returns miss or hit with face (0..5) and region(always 0)
111 Foam::pointIndexHit Foam::searchableBox::findNearest
112 (
113  const point& bbMid,
114  const point& sample,
115  const scalar nearestDistSqr
116 ) const
117 {
118  // Point can be inside or outside. For every component direction can be
119  // left of min, right of max or inbetween.
120  // - outside points: project first one x plane (either min().x()
121  // or max().x()), then onto y plane and finally z. You should be left
122  // with intersection point
123  // - inside point: find nearest side (compare to mid point). Project onto
124  // that.
125 
126  // The face is set to the last projected face.
127 
128 
129  // Outside point projected onto cube. Assume faces 0..5.
130  pointIndexHit info(true, sample, -1);
131  bool outside = false;
132 
133  // (for internal points) per direction what nearest cube side is
134  point near;
135 
136  for (direction dir = 0; dir < vector::nComponents; ++dir)
137  {
138  if (info.point()[dir] < min()[dir])
139  {
140  projectOntoCoordPlane(dir, min(), info);
141  outside = true;
142  }
143  else if (info.point()[dir] > max()[dir])
144  {
145  projectOntoCoordPlane(dir, max(), info);
146  outside = true;
147  }
148  else if (info.point()[dir] > bbMid[dir])
149  {
150  near[dir] = max()[dir];
151  }
152  else
153  {
154  near[dir] = min()[dir];
155  }
156  }
157 
158 
159  // For outside points the info will be correct now. Handle inside points
160  // using the three near distances. Project onto the nearest plane.
161  if (!outside)
162  {
163  const vector dist(cmptMag(info.point() - near));
164 
165  direction projNorm(vector::Z);
166 
167  if (dist.x() < dist.y())
168  {
169  if (dist.x() < dist.z())
170  {
171  projNorm = vector::X;
172  }
173  }
174  else
175  {
176  if (dist.y() < dist.z())
177  {
178  projNorm = vector::Y;
179  }
180  }
181 
182  projectOntoCoordPlane(projNorm, near, info);
183  }
184 
185 
186  // Check if outside. Optimisation: could do some checks on distance already
187  // on components above
188  if (info.point().distSqr(sample) > nearestDistSqr)
189  {
190  info.setMiss();
191  info.setIndex(-1);
192  }
193 
194  return info;
195 }
196 
197 
198 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
199 
200 Foam::searchableBox::searchableBox
201 (
202  const IOobject& io,
203  const treeBoundBox& bb
204 )
205 :
207  treeBoundBox(bb)
208 {
209  if (!treeBoundBox::good())
210  {
212  << "Illegal bounding box specification : "
213  << static_cast<const treeBoundBox>(*this) << nl
214  << exit(FatalError);
215  }
216 
217  bounds() = static_cast<treeBoundBox>(*this);
218 }
219 
220 
221 Foam::searchableBox::searchableBox
222 (
223  const IOobject& io,
224  const dictionary& dict
225 )
226 :
227  searchableSurface(io),
228  treeBoundBox()
229 {
230  readBoxDim(dict, *this);
231 
232  if (!treeBoundBox::good())
233  {
235  << "Illegal bounding box specification : "
236  << static_cast<const treeBoundBox>(*this) << nl
237  << exit(FatalError);
238  }
240  bounds() = static_cast<treeBoundBox>(*this);
241 }
242 
243 
244 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
245 
247 {
248  if (regions_.empty())
249  {
250  regions_.resize(1);
251  regions_[0] = "region0";
252  }
253  return regions_;
254 }
255 
256 
258 {
259  auto tctrs = tmp<pointField>::New(6);
260  auto& ctrs = tctrs.ref();
261 
263  const faceList& fcs = treeBoundBox::faces;
264 
265  forAll(fcs, i)
266  {
267  ctrs[i] = fcs[i].centre(pts);
268  }
269 
270  return tctrs;
271 }
272 
273 
275 (
276  pointField& centres,
277  scalarField& radiusSqr
278 ) const
279 {
280  centres.setSize(size());
281  radiusSqr.setSize(size());
282  radiusSqr = Zero;
283 
285  const faceList& fcs = treeBoundBox::faces;
286 
287  forAll(fcs, i)
288  {
289  const face& f = fcs[i];
290 
291  centres[i] = f.centre(pts);
292  for (const label pointi : f)
293  {
294  const point& pt = pts[pointi];
295 
296  radiusSqr[i] = Foam::max(radiusSqr[i], centres[i].distSqr(pt));
297  }
298  }
299 
300  // Add a bit to make sure all points are tested inside
301  radiusSqr += Foam::sqr(SMALL);
302 }
303 
304 
306 {
307  return treeBoundBox::points();
308 }
309 
310 
311 Foam::pointIndexHit Foam::searchableBox::findNearest
312 (
313  const point& sample,
314  const scalar nearestDistSqr
315 ) const
316 {
317  return findNearest(centre(), sample, nearestDistSqr);
318 }
319 
320 
322 (
323  const point& sample,
324  const scalar nearestDistSqr
325 ) const
326 {
327  const point bbMid(centre());
328 
329  // Outside point projected onto cube. Assume faces 0..5.
330  pointIndexHit info(true, sample, -1);
331  bool outside = false;
332 
333  // (for internal points) per direction what nearest cube side is
334  point near;
335 
336  for (direction dir = 0; dir < vector::nComponents; ++dir)
337  {
338  if (info.point()[dir] < min()[dir])
339  {
340  projectOntoCoordPlane(dir, min(), info);
341  outside = true;
342  }
343  else if (info.point()[dir] > max()[dir])
344  {
345  projectOntoCoordPlane(dir, max(), info);
346  outside = true;
347  }
348  else if (info.point()[dir] > bbMid[dir])
349  {
350  near[dir] = max()[dir];
351  }
352  else
353  {
354  near[dir] = min()[dir];
355  }
356  }
357 
358 
359  // For outside points the info will be correct now. Handle inside points
360  // using the three near distances. Project onto the nearest two planes.
361  if (!outside)
362  {
363  // Get the per-component distance to nearest wall
364  vector dist(cmptMag(info.point() - near));
365 
366  SortableList<scalar> sortedDist(3);
367  sortedDist[0] = dist[0];
368  sortedDist[1] = dist[1];
369  sortedDist[2] = dist[2];
370  sortedDist.sort();
371 
372  // Project onto nearest
373  projectOntoCoordPlane(sortedDist.indices()[0], near, info);
374  // Project onto second nearest
375  projectOntoCoordPlane(sortedDist.indices()[1], near, info);
376  }
377 
378 
379  // Check if outside. Optimisation: could do some checks on distance already
380  // on components above
381  if (info.point().distSqr(sample) > nearestDistSqr)
382  {
383  info.setMiss();
384  info.setIndex(-1);
385  }
386 
387  return info;
388 }
389 
390 
391 Foam::pointIndexHit Foam::searchableBox::findNearest
392 (
393  const linePointRef& ln,
394  treeBoundBox& tightest,
395  point& linePoint
396 ) const
397 {
399  return pointIndexHit();
400 }
401 
402 
404 (
405  const point& start,
406  const point& end
407 ) const
408 {
409  pointIndexHit info(false, start, -1);
410 
411  bool foundInter;
412 
413  if (posBits(start) == 0)
414  {
415  if (posBits(end) == 0)
416  {
417  // Both start and end inside.
418  foundInter = false;
419  }
420  else
421  {
422  // end is outside. Clip to bounding box.
423  foundInter = intersects(end, start, info.point());
424  }
425  }
426  else
427  {
428  // start is outside. Clip to bounding box.
429  foundInter = intersects(start, end, info.point());
430  }
431 
432 
433  // Classify point
434  if (foundInter)
435  {
436  info.setHit();
437 
438  for (direction dir = 0; dir < vector::nComponents; ++dir)
439  {
440  if (info.point()[dir] == min()[dir])
441  {
442  info.setIndex(2*dir);
443  break;
444  }
445  else if (info.point()[dir] == max()[dir])
446  {
447  info.setIndex(2*dir+1);
448  break;
449  }
450  }
451 
452  if (info.index() == -1)
453  {
455  << "point " << info.point()
456  << " on segment " << start << end
457  << " should be on face of " << *this
458  << " but it isn't." << abort(FatalError);
459  }
460  }
461 
462  return info;
463 }
464 
465 
467 (
468  const point& start,
469  const point& end
470 ) const
471 {
472  return findLine(start, end);
473 }
474 
475 
476 void Foam::searchableBox::findNearest
477 (
478  const pointField& samples,
479  const scalarField& nearestDistSqr,
480  List<pointIndexHit>& info
481 ) const
482 {
483  info.setSize(samples.size());
484 
485  const point bbMid(centre());
486 
487  forAll(samples, i)
488  {
489  info[i] = findNearest(bbMid, samples[i], nearestDistSqr[i]);
490  }
491 }
492 
493 
495 (
496  const pointField& start,
497  const pointField& end,
498  List<pointIndexHit>& info
499 ) const
500 {
501  info.setSize(start.size());
502 
503  forAll(start, i)
504  {
505  info[i] = findLine(start[i], end[i]);
506  }
507 }
508 
509 
511 (
512  const pointField& start,
513  const pointField& end,
514  List<pointIndexHit>& info
515 ) const
516 {
517  info.setSize(start.size());
518 
519  forAll(start, i)
520  {
521  info[i] = findLineAny(start[i], end[i]);
522  }
523 }
524 
525 
527 (
528  const pointField& start,
529  const pointField& end,
530  List<List<pointIndexHit>>& info
531 ) const
532 {
533  info.setSize(start.size());
534 
535  // Work array
536  DynamicList<pointIndexHit> hits;
537 
538  // Tolerances:
539  // To find all intersections we add a small vector to the last intersection
540  // This is chosen such that
541  // - it is significant (SMALL is smallest representative relative tolerance;
542  // we need something bigger since we're doing calculations)
543  // - if the start-end vector is zero we still progress
544  const vectorField dirVec(end-start);
545  const scalarField magSqrDirVec(Foam::magSqr(dirVec));
546  const vectorField smallVec
547  (
548  ROOTSMALL*dirVec + vector::uniform(ROOTVSMALL)
549  );
550 
551  forAll(start, pointi)
552  {
553  // See if any intersection between pt and end
554  pointIndexHit inter = findLine(start[pointi], end[pointi]);
555 
556  if (inter.hit())
557  {
558  hits.clear();
559  hits.append(inter);
560 
561  point pt = inter.hitPoint() + smallVec[pointi];
562 
563  while (((pt-start[pointi])&dirVec[pointi]) <= magSqrDirVec[pointi])
564  {
565  // See if any intersection between pt and end
566  pointIndexHit inter = findLine(pt, end[pointi]);
567 
568  // Check for not hit or hit same face as before (can happen
569  // if vector along surface of face)
570  if
571  (
572  !inter.hit()
573  || (inter.index() == hits.last().index())
574  )
575  {
576  break;
577  }
578  hits.append(inter);
579 
580  pt = inter.hitPoint() + smallVec[pointi];
581  }
582 
583  info[pointi].transfer(hits);
584  }
585  else
586  {
587  info[pointi].clear();
588  }
589  }
590 }
591 
592 
594 (
595  const List<pointIndexHit>& info,
596  labelList& region
597 ) const
598 {
599  region.setSize(info.size());
600  region = 0;
601 }
602 
603 
605 (
606  const List<pointIndexHit>& info,
607  vectorField& normal
608 ) const
609 {
610  normal.setSize(info.size());
611  normal = Zero;
612 
613  forAll(info, i)
614  {
615  if (info[i].hit())
616  {
617  normal[i] = treeBoundBox::faceNormals[info[i].index()];
618  }
619  else
620  {
621  // Set to what?
622  }
623  }
624 }
625 
626 
628 (
629  const pointField& points,
630  List<volumeType>& volType
631 ) const
632 {
633  volType.resize_nocopy(points.size());
634 
635  forAll(points, pointi)
636  {
637  volType[pointi] =
638  (
642  );
643  }
644 }
645 
646 
647 // ************************************************************************* //
virtual const wordList & regions() const
Names of regions.
static void readBoxDim(const dictionary &dict, treeBoundBox &bb)
Definition: searchableBox.C:53
dictionary dict
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:116
uint8_t direction
Definition: direction.H:46
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:125
void resize(const label len)
Adjust allocated size of list.
Definition: ListI.H:160
error FatalError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL ERROR&#39; header text and sta...
A list of keyword definitions, which are a keyword followed by a number of values (eg...
Definition: dictionary.H:129
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:598
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:40
dimensionedSymmTensor sqr(const dimensionedVector &dv)
virtual tmp< pointField > coordinates() const
Get representative set of element coordinates.
pointIndexHit findLineAny(const point &start, const point &end) const
Find any intersection of line between start and end.
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:50
virtual void boundingSpheres(pointField &centres, scalarField &radiusSqr) const
Get bounding spheres (centre and radius squared), one per element.
virtual tmp< pointField > points() const
Get the points that define the surface.
scalarField samples(nIntervals, Zero)
PointIndexHit< point > pointIndexHit
A PointIndexHit with a 3D point.
Definition: pointIndexHit.H:58
static const FixedList< vector, 6 > faceNormals
The unit normal per face.
Definition: boundBox.H:129
This class describes the interaction of an object (often a face) and a point. It carries the info of ...
Definition: pointIndexHit.H:44
::Foam::direction nComponents(const expressions::valueTypeCode) noexcept
The number of components associated with given valueTypeCode.
Definition: exprTraits.C:40
Base class of (analytical or triangulated) surface. Encapsulates all the search routines. WIP.
const point & min() const noexcept
Minimum describing the bounding box.
Definition: boundBoxI.H:162
static const faceList faces
Face to point addressing, using octant corner points.
Definition: treeBoundBox.H:174
virtual const boundBox & bounds() const
Return const reference to boundBox.
Macros for easy insertion into run-time selection tables.
addNamedToRunTimeSelectionTable(topoSetCellSource, badQualityToCell, word, badQuality)
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:421
pointIndexHit findLine(const point &start, const point &end) const
Find nearest intersection of line between start and end.
virtual void getNormal(const List< pointIndexHit > &, vectorField &normal) const
From a set of points and indices get the normal.
bool readEntry(const word &keyword, T &val, enum keyType::option matchOpt=keyType::REGEX, IOobjectOption::readOption readOpt=IOobjectOption::MUST_READ) const
Find entry and assign to T val. FatalIOError if it is found and the number of tokens is incorrect...
List< face > faceList
List of faces.
Definition: faceListFwd.H:39
const point & max() const noexcept
Maximum describing the bounding box.
Definition: boundBoxI.H:168
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:38
void setSize(const label n)
Alias for resize()
Definition: List.H:316
line< point, const point & > linePointRef
A line using referred points.
Definition: line.H:66
const pointField & points
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
Reading is optional [identical to LAZY_READ].
static tmp< T > New(Args &&... args)
Construct tmp with forwarding arguments.
Definition: tmp.H:206
virtual void findLineAll(const pointField &start, const pointField &end, List< List< pointIndexHit >> &) const
Get all intersections in order from start to end.
Vector< scalar > vector
Definition: vector.H:57
A location inside the volume.
Definition: volumeType.H:65
String literal.
Definition: keyType.H:82
treeBoundBox()=default
Default construct: an inverted bounding box.
A location outside the volume.
Definition: volumeType.H:66
label min(const labelHashSet &set, label minValue=labelMax)
Find the min value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:26
errorManip< error > abort(error &err)
Definition: errorManip.H:139
pointIndexHit findNearestOnEdge(const point &sample, const scalar nearestDistSqr) const
Calculate nearest point on edge.
bool ln(const fileName &src, const fileName &dst)
Create a softlink. dst should not exist. Returns true if successful.
Definition: POSIX.C:1237
InfoProxy< IOobject > info() const noexcept
Return info proxy, for printing information to a stream.
Definition: IOobject.H:955
bool contains(const vector &dir, const point &) const
Contains point (inside or on edge) and moving in direction.
Definition: treeBoundBox.C:413
void cmptMag(FieldField< Field, Type > &cf, const FieldField< Field, Type > &f)
A Vector of values with scalar precision, where scalar is float/double depending on the compilation f...
bool readIfPresent(const word &keyword, T &val, enum keyType::option matchOpt=keyType::REGEX) const
Find an entry if present, and assign to T val. FatalIOError if it is found and the number of tokens i...
constexpr auto end(C &c) -> decltype(c.end())
Return iterator to the end of the container c.
Definition: stdFoam.H:201
defineTypeNameAndDebug(combustionModel, 0)
labelList f(nPoints)
vector point
Point is a vector.
Definition: point.H:37
virtual void getRegion(const List< pointIndexHit > &, labelList &region) const
From a set of points and indices get the region.
PtrList< volScalarField > & Y
bool good() const
Bounding box is non-inverted.
Definition: boundBoxI.H:156
tmp< pointField > points() const
Vertex coordinates. In octant coding.
Definition: treeBoundBox.C:87
Standard boundBox with extra functionality for use in octree.
Definition: treeBoundBox.H:90
Field< vector > vectorField
Specialisation of Field<T> for vector.
List< label > labelList
A List of labels.
Definition: List.H:62
virtual void getVolumeType(const pointField &points, List< volumeType > &volType) const
Determine type (inside/outside) for points.
IOobject io("surfaceFilmProperties", mesh.time().constant(), mesh, IOobject::READ_IF_PRESENT, IOobject::NO_WRITE, IOobject::NO_REGISTER)
A class for managing temporary objects.
Definition: HashPtrTable.H:50
#define NotImplemented
Issue a FatalErrorIn for a function not currently implemented.
Definition: error.H:686
Defines the attributes of an object for which implicit objectRegistry management is supported...
Definition: IOobject.H:172
dimensioned< typename typeOfMag< Type >::type > magSqr(const dimensioned< Type > &dt)
Namespace for OpenFOAM.
addToRunTimeSelectionTable(functionObject, pointHistory, dictionary)
const pointField & pts
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:127
readOption
Enumeration defining read preferences.