treeBoundBox.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration | Website: https://openfoam.org
5  \\ / A nd | Copyright (C) 2011-2026 OpenFOAM Foundation
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8 License
9  This file is part of OpenFOAM.
10 
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.
15 
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
19  for more details.
20 
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/>.
23 
24 \*---------------------------------------------------------------------------*/
25 
26 #include "treeBoundBox.H"
27 #include "ListOps.H"
28 #include "OFstream.H"
29 
30 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
31 
33 (
34  vector::rootMin/2,
35  vector::rootMax/2
36 );
37 
38 
40 (
41  vector::rootMax/2,
42  vector::rootMin/2
43 );
44 
45 
46 const Foam::label facesArray[6][4] =
47 {
48  {0, 4, 6, 2}, // left
49  {1, 3, 7, 5}, // right
50  {0, 1, 5, 4}, // bottom
51  {2, 6, 7, 3}, // top
52  {0, 2, 3, 1}, // back
53  {4, 5, 7, 6} // front
54 };
55 
56 
58 (
59  initListList<face, label, 6, 4>(facesArray)
60 );
61 
62 
63 const Foam::label edgesArray[12][2] =
64 {
65  {0, 1}, // 0
66  {1, 3},
67  {2, 3}, // 2
68  {0, 2},
69  {4, 5}, // 4
70  {5, 7},
71  {6, 7}, // 6
72  {4, 6},
73  {0, 4}, // 8
74  {1, 5},
75  {3, 7}, // 10
76  {2, 6}
77 };
78 
79 
81 (
82  calcEdges(edgesArray)
83 );
84 
85 
87 (
88  calcFaceNormals()
89 );
90 
91 
92 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
93 
94 Foam::edgeList Foam::treeBoundBox::calcEdges(const label edgesArray[12][2])
95 {
96  edgeList edges(12);
97  forAll(edges, edgeI)
98  {
99  edges[edgeI][0] = edgesArray[edgeI][0];
100  edges[edgeI][1] = edgesArray[edgeI][1];
101  }
102  return edges;
103 }
104 
105 
106 Foam::FixedList<Foam::vector, 6> Foam::treeBoundBox::calcFaceNormals()
107 {
108  FixedList<vector, 6> normals;
109  normals[faceId::left] = vector(-1, 0, 0);
110  normals[faceId::right] = vector( 1, 0, 0);
111  normals[faceId::bottom] = vector( 0, -1, 0);
112  normals[faceId::top] = vector( 0, 1, 0);
113  normals[faceId::back] = vector( 0, 0, -1);
114  normals[faceId::front] = vector( 0, 0, 1);
115  return normals;
116 }
117 
118 
119 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
120 
122 :
123  boundBox(points, false)
124 {
125  if (points.empty())
126  {
128  << "cannot find bounding box for zero-sized pointField, "
129  << "returning zero" << endl;
130 
131  return;
132  }
133 }
134 
135 
137 (
138  const UList<point>& points,
139  const labelUList& indices
140 )
141 :
142  boundBox(points, indices, false)
143 {
144  if (points.empty() || indices.empty())
145  {
147  << "cannot find bounding box for zero-sized pointField, "
148  << "returning zero" << endl;
149 
150  return;
151  }
152 }
153 
154 
155 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
156 
158 {
160  pointField& points = tPts.ref();
161 
162  forAll(points, octant)
163  {
164  points[octant] = corner(octant);
165  }
166 
167  return tPts;
168 }
169 
170 
172 {
173  return subBbox(midpoint(), octant);
174 }
175 
176 
178 (
179  const point& mid,
180  const direction octant
181 ) const
182 {
183  if (octant > 7)
184  {
186  << "octant should be [0..7]"
187  << abort(FatalError);
188  }
189 
190  // start with a copy of this bounding box and adjust limits accordingly
191  treeBoundBox subBb(*this);
192  point& bbMin = subBb.min();
193  point& bbMax = subBb.max();
194 
195  if (octant & octantBit::rightHalf)
196  {
197  bbMin.x() = mid.x(); // mid -> max
198  }
199  else
200  {
201  bbMax.x() = mid.x(); // min -> mid
202  }
203 
204  if (octant & octantBit::topHalf)
205  {
206  bbMin.y() = mid.y(); // mid -> max
207  }
208  else
209  {
210  bbMax.y() = mid.y(); // min -> mid
211  }
212 
213  if (octant & octantBit::frontHalf)
214  {
215  bbMin.z() = mid.z(); // mid -> max
216  }
217  else
218  {
219  bbMax.z() = mid.z(); // min -> mid
220  }
221 
222  return subBb;
223 }
224 
225 
227 (
228  const point& overallStart,
229  const vector& overallVec,
230  const point& start,
231  const point& end,
232  point& pt,
233  direction& ptOnFaces
234 ) const
235 {
236  // Sutherlands algorithm:
237  // loop
238  // - start = intersection of line with one of the planes bounding
239  // the bounding box
240  // - stop if start inside bb (return true)
241  // - stop if start and end in same 'half' (e.g. both above bb)
242  // (return false)
243  //
244  // Uses posBits to efficiently determine 'half' in which start and end
245  // point are.
246  //
247  // Note:
248  // - sets coordinate to exact position: e.g. pt.x() = min().x()
249  // since plane intersect routine might have truncation error.
250  // This makes sure that posBits tests 'inside'
251 
252  const direction endBits = posBits(end);
253  pt = start;
254 
255  // Allow maximum of 3 clips.
256  for (label i = 0; i < 4; ++i)
257  {
258  direction ptBits = posBits(pt);
259 
260  if (ptBits == 0)
261  {
262  // pt inside bb
263  ptOnFaces = faceBits(pt);
264  return true;
265  }
266 
267  if ((ptBits & endBits) != 0)
268  {
269  // pt and end in same block outside of bb
270  ptOnFaces = faceBits(pt);
271  return false;
272  }
273 
274  if (ptBits & faceBit::left)
275  {
276  // Intersect with plane V=min, n=-1,0,0
277  if (Foam::mag(overallVec.x()) > vSmall)
278  {
279  scalar s = (min().x() - overallStart.x())/overallVec.x();
280  pt.x() = min().x();
281  pt.y() = overallStart.y() + overallVec.y()*s;
282  pt.z() = overallStart.z() + overallVec.z()*s;
283  }
284  else
285  {
286  // Vector not in x-direction. But still intersecting bb planes.
287  // So must be close - just snap to bb.
288  pt.x() = min().x();
289  }
290  }
291  else if (ptBits & faceBit::right)
292  {
293  // Intersect with plane V=max, n=1,0,0
294  if (Foam::mag(overallVec.x()) > vSmall)
295  {
296  scalar s = (max().x() - overallStart.x())/overallVec.x();
297  pt.x() = max().x();
298  pt.y() = overallStart.y() + overallVec.y()*s;
299  pt.z() = overallStart.z() + overallVec.z()*s;
300  }
301  else
302  {
303  pt.x() = max().x();
304  }
305  }
306  else if (ptBits & faceBit::bottom)
307  {
308  // Intersect with plane V=min, n=0,-1,0
309  if (Foam::mag(overallVec.y()) > vSmall)
310  {
311  scalar s = (min().y() - overallStart.y())/overallVec.y();
312  pt.x() = overallStart.x() + overallVec.x()*s;
313  pt.y() = min().y();
314  pt.z() = overallStart.z() + overallVec.z()*s;
315  }
316  else
317  {
318  pt.x() = min().y();
319  }
320  }
321  else if (ptBits & faceBit::top)
322  {
323  // Intersect with plane V=max, n=0,1,0
324  if (Foam::mag(overallVec.y()) > vSmall)
325  {
326  scalar s = (max().y() - overallStart.y())/overallVec.y();
327  pt.x() = overallStart.x() + overallVec.x()*s;
328  pt.y() = max().y();
329  pt.z() = overallStart.z() + overallVec.z()*s;
330  }
331  else
332  {
333  pt.y() = max().y();
334  }
335  }
336  else if (ptBits & faceBit::back)
337  {
338  // Intersect with plane V=min, n=0,0,-1
339  if (Foam::mag(overallVec.z()) > vSmall)
340  {
341  scalar s = (min().z() - overallStart.z())/overallVec.z();
342  pt.x() = overallStart.x() + overallVec.x()*s;
343  pt.y() = overallStart.y() + overallVec.y()*s;
344  pt.z() = min().z();
345  }
346  else
347  {
348  pt.z() = min().z();
349  }
350  }
351  else if (ptBits & faceBit::front)
352  {
353  // Intersect with plane V=max, n=0,0,1
354  if (Foam::mag(overallVec.z()) > vSmall)
355  {
356  scalar s = (max().z() - overallStart.z())/overallVec.z();
357  pt.x() = overallStart.x() + overallVec.x()*s;
358  pt.y() = overallStart.y() + overallVec.y()*s;
359  pt.z() = max().z();
360  }
361  else
362  {
363  pt.z() = max().z();
364  }
365  }
366  }
367 
368  // Can end up here if the end point is on the edge of the boundBox
369  return true;
370 }
371 
372 
374 (
375  const point& start,
376  const point& end,
377  point& pt
378 ) const
379 {
380  direction ptBits;
381  return intersects(start, end-start, start, end, pt, ptBits);
382 }
383 
384 
385 bool Foam::treeBoundBox::contains(const vector& dir, const point& pt) const
386 {
387  // Compare all components against min and max of bb
388 
389  for (direction cmpt=0; cmpt<3; cmpt++)
390  {
391  if (pt[cmpt] < min()[cmpt])
392  {
393  return false;
394  }
395  else if (pt[cmpt] == min()[cmpt])
396  {
397  // On edge. Outside if direction points outwards.
398  if (dir[cmpt] < 0)
399  {
400  return false;
401  }
402  }
403 
404  if (pt[cmpt] > max()[cmpt])
405  {
406  return false;
407  }
408  else if (pt[cmpt] == max()[cmpt])
409  {
410  // On edge. Outside if direction points outwards.
411  if (dir[cmpt] > 0)
412  {
413  return false;
414  }
415  }
416  }
417 
418  // All components inside bb
419  return true;
420 }
421 
422 
424 {
425  direction faceBits = 0;
426  if (pt.x() == min().x())
427  {
428  faceBits |= faceBit::left;
429  }
430  else if (pt.x() == max().x())
431  {
432  faceBits |= faceBit::right;
433  }
434 
435  if (pt.y() == min().y())
436  {
437  faceBits |= faceBit::bottom;
438  }
439  else if (pt.y() == max().y())
440  {
441  faceBits |= faceBit::top;
442  }
443 
444  if (pt.z() == min().z())
445  {
446  faceBits |= faceBit::back;
447  }
448  else if (pt.z() == max().z())
449  {
450  faceBits |= faceBit::front;
451  }
452  return faceBits;
453 }
454 
455 
457 {
458  direction posBits = 0;
459 
460  if (pt.x() < min().x())
461  {
462  posBits |= faceBit::left;
463  }
464  else if (pt.x() > max().x())
465  {
466  posBits |= faceBit::right;
467  }
468 
469  if (pt.y() < min().y())
470  {
471  posBits |= faceBit::bottom;
472  }
473  else if (pt.y() > max().y())
474  {
475  posBits |= faceBit::top;
476  }
477 
478  if (pt.z() < min().z())
479  {
480  posBits |= faceBit::back;
481  }
482  else if (pt.z() > max().z())
483  {
484  posBits |= faceBit::front;
485  }
486  return posBits;
487 }
488 
489 
491 (
492  const point& pt,
493  point& nearest,
494  point& furthest
495 ) const
496 {
497  scalar nearX, nearY, nearZ;
498  scalar farX, farY, farZ;
499 
500  if (Foam::mag(min().x() - pt.x()) < Foam::mag(max().x() - pt.x()))
501  {
502  nearX = min().x();
503  farX = max().x();
504  }
505  else
506  {
507  nearX = max().x();
508  farX = min().x();
509  }
510 
511  if (Foam::mag(min().y() - pt.y()) < Foam::mag(max().y() - pt.y()))
512  {
513  nearY = min().y();
514  farY = max().y();
515  }
516  else
517  {
518  nearY = max().y();
519  farY = min().y();
520  }
521 
522  if (Foam::mag(min().z() - pt.z()) < Foam::mag(max().z() - pt.z()))
523  {
524  nearZ = min().z();
525  farZ = max().z();
526  }
527  else
528  {
529  nearZ = max().z();
530  farZ = min().z();
531  }
532 
533  nearest = point(nearX, nearY, nearZ);
534  furthest = point(farX, farY, farZ);
535 }
536 
537 
538 Foam::scalar Foam::treeBoundBox::maxDist(const point& pt) const
539 {
540  point near, far;
541  calcExtremities(pt, near, far);
542 
543  return Foam::mag(far - pt);
544 }
545 
546 
548 (
549  const point& pt,
550  const treeBoundBox& other
551 ) const
552 {
553  //
554  // Distance point <-> nearest and furthest away vertex of this
555  //
556 
557  point nearThis, farThis;
558 
559  // get nearest and furthest away vertex
560  calcExtremities(pt, nearThis, farThis);
561 
562  const scalar minDistThis =
563  sqr(nearThis.x() - pt.x())
564  + sqr(nearThis.y() - pt.y())
565  + sqr(nearThis.z() - pt.z());
566  const scalar maxDistThis =
567  sqr(farThis.x() - pt.x())
568  + sqr(farThis.y() - pt.y())
569  + sqr(farThis.z() - pt.z());
570 
571  //
572  // Distance point <-> other
573  //
574 
575  point nearOther, farOther;
576 
577  // get nearest and furthest away vertex
578  other.calcExtremities(pt, nearOther, farOther);
579 
580  const scalar minDistOther =
581  sqr(nearOther.x() - pt.x())
582  + sqr(nearOther.y() - pt.y())
583  + sqr(nearOther.z() - pt.z());
584  const scalar maxDistOther =
585  sqr(farOther.x() - pt.x())
586  + sqr(farOther.y() - pt.y())
587  + sqr(farOther.z() - pt.z());
588 
589  //
590  // Categorise
591  //
592  if (maxDistThis < minDistOther)
593  {
594  // All vertices of this are nearer to point than any vertex of other
595  return -1;
596  }
597  else if (minDistThis > maxDistOther)
598  {
599  // All vertices of this are further from point than any vertex of other
600  return 1;
601  }
602  else
603  {
604  // Mixed bag
605  return 0;
606  }
607 }
608 
609 
610 void Foam::treeBoundBox::writeOBJ(const fileName& fName) const
611 {
612  OFstream str(fName);
613 
614  Info<< "Dumping bounding box " << *this << " as lines to obj file "
615  << str.name() << endl;
616 
617  pointField bbPoints(points());
618 
619  forAll(bbPoints, i)
620  {
621  const point& pt = bbPoints[i];
622 
623  str<< "v " << pt.x() << ' ' << pt.y() << ' ' << pt.z() << endl;
624  }
625 
627  {
628  const edge& e = treeBoundBox::edges[i];
629 
630  str<< "l " << e[0]+1 << ' ' << e[1]+1 << nl;
631  }
632 }
633 
634 
635 // * * * * * * * * * * * * * * * Friend Operators * * * * * * * * * * * * * //
636 
638 {
639  return operator==
640  (
641  static_cast<const boundBox&>(a),
642  static_cast<const boundBox&>(b)
643  );
644 }
645 
646 
648 {
649  return !(a == b);
650 }
651 
652 
653 // * * * * * * * * * * * * * * * Ostream Operator * * * * * * * * * * * * * //
654 
656 {
657  return os << static_cast<const boundBox&>(bb);
658 }
659 
660 
662 {
663  return is >> static_cast<boundBox&>(bb);
664 }
665 
666 
667 // ************************************************************************* //
scalar y
Various functions to operate on Lists.
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:449
A 1D vector of objects of type <T> with a fixed size <Size>.
Definition: FixedList.H:78
An Istream is an abstract base class for all input systems (streams, files, token lists etc)....
Definition: Istream.H:60
Output to file stream.
Definition: OFstream.H:87
const fileName & name() const
Return the name of the stream.
Definition: OFstream.H:121
An Ostream is an abstract base class for all output systems (streams, files, token lists,...
Definition: Ostream.H:57
A 1D vector of objects of type <T>, where the size of the vector is known and can be used for subscri...
Definition: UList.H:74
bool empty() const
Return true if the UList is empty (ie, size() is zero)
Definition: UListI.H:325
const Cmpt & z() const
Definition: VectorI.H:87
const Cmpt & y() const
Definition: VectorI.H:81
const Cmpt & x() const
Definition: VectorI.H:75
A bounding box defined in terms of the points at its extremities.
Definition: boundBox.H:60
const point & min() const
Minimum point defining the bounding box.
Definition: boundBoxI.H:88
const point & max() const
Maximum point defining the bounding box.
Definition: boundBoxI.H:94
An edge is a list of two point labels. The functionality it provides supports the discretisation on a...
Definition: edge.H:61
A class for handling file names.
Definition: fileName.H:82
A class for managing temporary objects.
Definition: tmp.H:55
T & ref() const
Return non-const reference or generate a fatal error.
Definition: tmpI.H:197
Standard boundBox + extra functionality for use in octree.
Definition: treeBoundBox.H:90
void writeOBJ(const fileName &fName) const
Definition: treeBoundBox.C:610
void calcExtremities(const point &pt, point &nearest, point &furthest) const
Calculate nearest and furthest (to point) vertex coords of.
Definition: treeBoundBox.C:491
scalar maxDist(const point &) const
Returns distance point to furthest away corner.
Definition: treeBoundBox.C:538
label distanceCmp(const point &, const treeBoundBox &other) const
Compare distance to point with other bounding box.
Definition: treeBoundBox.C:548
direction faceBits(const point &) const
Code position of point on bounding box faces.
Definition: treeBoundBox.C:423
treeBoundBox subBbox(const direction) const
Sub box given by octant number. Midpoint calculated.
Definition: treeBoundBox.C:171
static const edgeList edges
Edge to point addressing.
Definition: treeBoundBox.H:175
bool contains(const point &) const
Contains point or other bounding box?
Definition: boundBoxI.H:204
bool intersects(const point &overallStart, const vector &overallVec, const point &start, const point &end, point &pt, direction &ptBits) const
Intersects segment; set point to intersection position and face,.
Definition: treeBoundBox.C:227
static const treeBoundBox invertedBox
As per boundBox::invertedBox, but with great instead of vGreat.
Definition: treeBoundBox.H:105
static const treeBoundBox greatBox
As per boundBox::greatBox, but with great instead of vGreat.
Definition: treeBoundBox.H:102
static const FixedList< vector, 6 > faceNormals
Per face the unit normal.
Definition: treeBoundBox.H:178
static const faceList faces
Face to point addressing.
Definition: treeBoundBox.H:172
direction posBits(const point &) const
Position of point relative to bounding box.
Definition: treeBoundBox.C:456
treeBoundBox()
Construct null setting points to zero.
Definition: treeBoundBoxI.H:31
tmp< pointField > points() const
Vertex coordinates. In octant coding.
Definition: treeBoundBox.C:157
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:334
const pointField & points
gmvFile<< "tracers "<< particles.size()<< nl;forAllConstIter(lagrangian::Cloud< passiveParticle >, particles, iter){ gmvFile<< iter().position().x()<< " ";}gmvFile<< nl;forAllConstIter(lagrangian::Cloud< passiveParticle >, particles, iter){ gmvFile<< iter().position().y()<< " ";}gmvFile<< nl;forAllConstIter(lagrangian::Cloud< passiveParticle >, particles, iter){ gmvFile<< iter().position().z()<< " ";}gmvFile<< nl;forAll(lagrangianScalarNames, i){ word name=lagrangianScalarNames[i];IOField< scalar > s(IOobject(name, runTime.name(), lagrangian::cloud::prefix, mesh, IOobject::MUST_READ, IOobject::NO_WRITE))
volScalarField & b
Definition: createFields.H:27
#define WarningInFunction
Report a warning using Foam::Warning.
bool operator!=(const particle &, const particle &)
Definition: particle.C:445
const doubleScalar e
Definition: doubleScalar.H:106
Istream & operator>>(Istream &, pointEdgeDist &)
Definition: pointEdgeDist.C:41
intWM_LABEL_SIZE_t label
A label is an int32_t or int64_t as specified by the pre-processor macro WM_LABEL_SIZE.
Definition: label.H:59
tmp< fvMatrix< Type > > operator==(const fvMatrix< Type > &, const fvMatrix< Type > &)
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:288
errorManip< error > abort(error &err)
Definition: errorManip.H:131
messageStream Info
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:42
vector point
Point is a vector.
Definition: point.H:41
tmp< DimensionedField< typename outerProduct< Type, Type >::type, GeoMesh, Field >> sqr(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
Vector< scalar > vector
A scalar version of the templated Vector.
Definition: vector.H:49
dimensioned< Type > min(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
tmp< DimensionedField< scalar, GeoMesh, Field > > mag(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
Ostream & operator<<(Ostream &os, const fvConstraints &constraints)
error FatalError
List< edge > edgeList
Definition: edgeList.H:38
static const char nl
Definition: Ostream.H:297
uint8_t direction
Definition: direction.H:45
dimensioned< Type > max(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
const Foam::label edgesArray[12][2]
Definition: treeBoundBox.C:63
const Foam::label facesArray[6][4]
Definition: treeBoundBox.C:46