AABBTree.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) 2015 OpenFOAM Foundation
9  Copyright (C) 2016-2021 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 "AABBTree.H"
30 #include "meshTools.H"
31 #include "bitSet.H"
32 //#include "OFstream.H"
33 
34 template<class Type>
35 Foam::scalar Foam::AABBTree<Type>::tolerance_ = 1e-4;
36 
37 // * * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * //
38 
39 template<class Type>
41 (
42  const bool writeLinesOnly,
43  const treeBoundBox& bb,
44  label& vertI,
45  Ostream& os
46 ) const
47 {
48  const pointField pts(bb.points());
49  for (const point& pt : pts)
50  {
52  }
53 
54  if (writeLinesOnly)
55  {
56  for (const edge& e : bb.edges)
57  {
58  os << "l " << e[0] + vertI + 1 << ' ' << e[1] + vertI + 1 << nl;
59  }
60  }
61  else
62  {
63  for (const face& f : bb.faces)
64  {
65  os << 'f';
66  for (const label fpi : f)
67  {
68  os << ' ' << fpi + vertI + 1;
69  }
70  os << nl;
71  }
72  }
73 
74  vertI += pts.size();
75 }
76 
77 
78 template<class Type>
80 (
81  const bool leavesOnly,
82  const bool writeLinesOnly,
83  const treeBoundBox& bb,
84  const label nodeI,
85  const List<Pair<treeBoundBox>>& bbs,
86  const List<Pair<label>>& nodes,
87  label& vertI,
88  Ostream& os
89 ) const
90 {
91  if (!leavesOnly || nodeI < 0)
92  {
93  writeOBJ(writeLinesOnly, bb, vertI, os);
94  }
95 
96  // recurse to find leaves
97  if (nodeI >= 0)
98  {
99  writeOBJ
100  (
101  leavesOnly,
102  writeLinesOnly,
103  bbs[nodeI].first(),
104  nodes[nodeI].first(),
105  bbs,
106  nodes,
107  vertI,
108  os
109  );
110  writeOBJ
111  (
112  leavesOnly,
113  writeLinesOnly,
114  bbs[nodeI].second(),
115  nodes[nodeI].second(),
116  bbs,
117  nodes,
118  vertI,
119  os
120  );
121  }
122 }
123 
124 
125 template<class Type>
127 (
128  const bool equalBinSize,
129  const label level,
130  const List<Type>& objects,
131  const pointField& points,
132  const DynamicList<label>& objectIDs,
133  const treeBoundBox& bb,
134  const label nodeI,
135 
137  DynamicList<labelPair>& nodes,
138  DynamicList<labelList>& addressing
139 ) const
140 {
141  const vector span = bb.span();
142 
143  // Determine which direction to divide the box
144 
145  direction maxDir = 0;
146  scalar maxSpan = span[maxDir];
147  for (label dirI = 1; dirI < 3; ++dirI)
148  {
149  if (span[dirI] > maxSpan)
150  {
151  maxSpan = span[dirI];
152  maxDir = dirI;
153  }
154  }
155 
156 
157  scalar divide;
158 
159  if (equalBinSize)
160  {
161  // Pick up points used by this set of objects
162 
163  bitSet isUsedPoint(points.size());
165 
166  for (const label objI : objectIDs)
167  {
168  const Type& obj = objects[objI];
169 
170  for (const label pointI : obj)
171  {
172  if (isUsedPoint.set(pointI))
173  {
174  component.append(points[pointI][maxDir]);
175  }
176  }
177  }
178 
179  // Determine the median
180 
182 
183  divide = component[component.size()/2];
184  }
185  else
186  {
187  // Geometric middle
188  divide = bb.min()[maxDir] + 0.5*maxSpan;
189  }
190 
191 
192  scalar divMin = divide + tolerance_*maxSpan;
193  scalar divMax = divide - tolerance_*maxSpan;
194 
195 
196  // Assign the objects to min or max bin
197 
198  DynamicList<label> minBinObjectIDs(objectIDs.size());
199  treeBoundBox minBb(boundBox::invertedBox);
200 
201  DynamicList<label> maxBinObjectIDs(objectIDs.size());
202  treeBoundBox maxBb(boundBox::invertedBox);
203 
204  for (const label objI : objectIDs)
205  {
206  const Type& obj = objects[objI];
207 
208  bool intoMin = false;
209  bool intoMax = false;
210 
211  for (const label pointI : obj)
212  {
213  const point& pt = points[pointI];
214  if (pt[maxDir] < divMin)
215  {
216  intoMin = true;
217  }
218  if (pt[maxDir] > divMax)
219  {
220  intoMax = true;
221  }
222  }
223 
224  // Note: object is inserted into both min/max child boxes (duplicated)
225  // if it crosses the bin boundaries
226  if (intoMin)
227  {
228  minBinObjectIDs.append(objI);
229  minBb.add(points, obj);
230  }
231  if (intoMax)
232  {
233  maxBinObjectIDs.append(objI);
234  maxBb.add(points, obj);
235  }
236  }
237 
238  // Inflate box in case geometry reduces to 2-D
239  if (minBinObjectIDs.size())
240  {
241  minBb.inflate(0.01);
242  }
243  if (maxBinObjectIDs.size())
244  {
245  maxBb.inflate(0.01);
246  }
247 
248  minBinObjectIDs.shrink();
249  maxBinObjectIDs.shrink();
250 
251 
252  label minI;
253  if (minBinObjectIDs.size() > minLeafSize_ && level < maxLevel_)
254  {
255  // New leaf
256  minI = nodes.size();
257  nodes.append(labelPair(-1, -1));
258  }
259  else
260  {
261  // Update existing leaf
262  minI = -addressing.size() - 1;
263  addressing.append(minBinObjectIDs);
264  }
265 
266  label maxI;
267  if (maxBinObjectIDs.size() > minLeafSize_ && level < maxLevel_)
268  {
269  // New leaf
270  maxI = nodes.size();
271  nodes.append(labelPair(-1, -1));
272  }
273  else
274  {
275  // Update existing leaf
276  maxI = -addressing.size() - 1;
277  addressing.append(maxBinObjectIDs);
278  }
279 
280  nodes(nodeI) = labelPair(minI, maxI);
281  bbs(nodeI) = Pair<treeBoundBox>(minBb, maxBb);
282 
283  // Recurse
284  if (minI >= 0)
285  {
286  createBoxes
287  (
288  equalBinSize,
289  level + 1,
290  objects,
291  points,
292  minBinObjectIDs,
293  minBb,
294  minI,
295  bbs,
296  nodes,
297  addressing
298  );
299  }
300  if (maxI >= 0)
301  {
302  createBoxes
303  (
304  equalBinSize,
305  level + 1,
306  objects,
307  points,
308  maxBinObjectIDs,
309  maxBb,
310  maxI,
311  bbs,
312  nodes,
313  addressing
314  );
315  }
316 }
317 
318 
319 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
320 
321 template<class Type>
323 :
324  maxLevel_(0),
325  minLeafSize_(0),
326  boundBoxes_(),
327  addressing_()
328 {}
329 
330 
331 template<class Type>
333 (
334  const UList<Type>& objects,
335  const pointField& points,
336  const bool equalBinSize,
337  const label maxLevel,
338  const label minLeafSize
339 )
340 :
341  maxLevel_(maxLevel),
342  minLeafSize_(minLeafSize),
343  boundBoxes_(),
344  addressing_()
345 {
346  if (objects.empty())
347  {
348  return;
349  }
350 
351 
352  DynamicList<Pair<treeBoundBox>> bbs(maxLevel);
353  DynamicList<labelPair> nodes(maxLevel);
354  DynamicList<labelList> addr(maxLevel);
355 
356  nodes.append(labelPair(-1, -1));
357  treeBoundBox topBb(points);
358  topBb.inflate(0.01);
359 
360  DynamicList<label> objectIDs(identity(objects.size()));
361 
362  createBoxes
363  (
364  equalBinSize,
365  0, // starting at top level
366  objects,
367  points,
368  objectIDs,
369  topBb,
370  0, // starting node
371 
372  bbs,
373  nodes,
374  addr
375  );
376 
377 
378  //{
379  // OFstream os("tree.obj");
380  // label vertI = 0;
381  // writeOBJ
382  // (
383  // true, // leavesOnly
384  // false, // writeLinesOnly
385  //
386  // topBb,
387  // 0,
388  // bbs,
389  // nodes,
390  // vertI,
391  // os
392  // );
393  //}
394 
395 
396  // transfer flattened tree to persistent storage
397  DynamicList<treeBoundBox> boundBoxes(2*bbs.size());
398  DynamicList<labelList> addressing(2*addr.size());
399 
400  forAll(nodes, nodeI)
401  {
402  if (nodes[nodeI].first() < 0)
403  {
404  boundBoxes.append(bbs[nodeI].first());
405  addressing.append(addr[-(nodes[nodeI].first() + 1)]);
406  }
407  if (nodes[nodeI].second() < 0)
408  {
409  boundBoxes.append(bbs[nodeI].second());
410  addressing.append(addr[-(nodes[nodeI].second() + 1)]);
411  }
412  }
413 
414  boundBoxes_.transfer(boundBoxes);
415  addressing_.transfer(addressing);
416 
417 
418  if (0)
419  {
420  bitSet checked(objects.size());
421  for (const auto& box : addressing_)
422  {
423  for (const auto& id : box)
424  {
425  checked.set(id);
426  }
427  }
428 
429  const label unsetSize = checked.count(false);
430 
431  if (unsetSize)
432  {
433  Info<< "*** Problem: IDs not set: " << unsetSize << endl;
434  }
435  }
436 }
437 
438 
439 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
440 
441 template<class Type>
443 {
444  return boundBoxes_;
445 }
446 
447 
448 template<class Type>
450 {
451  return addressing_;
452 }
453 
454 
455 template<class Type>
457 {
458  for (const treeBoundBox& bb : boundBoxes_)
459  {
460  if (bb.contains(pt))
461  {
462  return true;
463  }
464  }
465 
466  return false;
467 }
468 
469 
470 template<class Type>
472 {
473  for (const treeBoundBox& bb : boundBoxes_)
474  {
475  if (bb.overlaps(bbIn))
476  {
477  return true;
478  }
479  }
480 
481  return false;
482 }
483 
484 
485 // * * * * * * * * * * * * * * IOstream Operators * * * * * * * * * * * * * //
486 
487 template<class Type>
489 {
490  if (os.format() == IOstream::ASCII)
491  {
492  os << tree.maxLevel_ << token::SPACE
493  << tree.minLeafSize_ << token::SPACE
494  << tree.boundBoxes_ << token::SPACE
495  << tree.addressing_ << token::SPACE;
496  }
497  else
498  {
499  os.write
500  (
501  reinterpret_cast<const char*>(&tree.maxLevel_),
502  sizeof(tree.maxLevel_)
503  + sizeof(tree.minLeafSize_)
504  );
505  os << tree.boundBoxes_
506  << tree.addressing_;
507  }
508 
510  return os;
511 }
512 
513 
514 template<class Type>
515 Foam::Istream& Foam::operator>>(Istream& is, AABBTree<Type>& tree)
516 {
517  if (is.format() == IOstream::ASCII)
518  {
519  is >> tree.maxLevel_
520  >> tree.minLeafSize_;
521  }
522  else
523  {
524  is.beginRawRead();
525 
526  readRawLabel(is, &tree.maxLevel_);
527  readRawLabel(is, &tree.minLeafSize_);
528 
529  is.endRawRead();
530  }
531 
532  is >> tree.boundBoxes_
533  >> tree.addressing_;
534 
535  is.check(FUNCTION_NAME);
536  return is;
537 }
538 
539 
540 // ************************************************************************* //
meshTools.H
Foam::treeBoundBox::overlaps
bool overlaps(const boundBox &bb) const
Overlaps other bounding box?
Definition: boundBoxI.H:221
Foam::component
void component(FieldField< Field, typename FieldField< Field, Type >::cmptType > &sf, const FieldField< Field, Type > &f, const direction d)
Definition: FieldFieldFunctions.C:44
Foam::bitSet
A bitSet stores bits (elements with only two states) in packed internal format and supports a variety...
Definition: bitSet.H:63
Foam::treeBoundBox::points
tmp< pointField > points() const
Vertex coordinates. In octant coding.
Definition: treeBoundBox.C:92
Foam::DynamicList< label >
Foam::meshTools::writeOBJ
void writeOBJ(Ostream &os, const point &pt)
Write obj representation of a point.
Definition: meshTools.C:203
Foam::AABBTree::pointInside
bool pointInside(const point &pt) const
Determine whether a point is inside the bounding boxes.
Definition: AABBTree.C:456
Foam::boundBox::inflate
void inflate(const scalar s)
Inflate box by factor*mag(span) in all dimensions.
Definition: boundBox.C:175
Foam::treeBoundBox
Standard boundBox with extra functionality for use in octree.
Definition: treeBoundBox.H:86
Foam::edge
An edge is a list of two point labels. The functionality it provides supports the discretisation on a...
Definition: edge.H:63
Foam::AABBTree::overlaps
bool overlaps(const boundBox &bbIn) const
Definition: AABBTree.C:471
Foam::IOstreamOption::format
streamFormat format() const noexcept
Get the current stream format.
Definition: IOstreamOption.H:286
Foam::AABBTree::boundBoxes
const List< treeBoundBox > & boundBoxes() const
Return the bounding boxes making up the tree.
Definition: AABBTree.C:442
Foam::bitSet::set
void set(const bitSet &bitset)
Set specified bits from another bitset.
Definition: bitSetI.H:574
Foam::AABBTree::boundBoxes_
List< treeBoundBox > boundBoxes_
Bounding boxes making up the tree.
Definition: AABBTree.H:89
Foam::operator>>
Istream & operator>>(Istream &, directionInfo &)
Definition: directionInfo.C:230
Foam::endl
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:369
Foam::treeBoundBox::edges
static const edgeList edges
Edge to point addressing.
Definition: treeBoundBox.H:154
Foam::AABBTree::writeOBJ
void writeOBJ(const bool writeLinesOnly, const treeBoundBox &bb, label &vertI, Ostream &os) const
Write OBJ file of bounding box.
Definition: AABBTree.C:41
Foam::OBJstream::write
virtual Ostream & write(const char c)
Write character.
Definition: OBJstream.C:78
Foam::DynamicList::shrink
DynamicList< T, SizeMin > & shrink()
Shrink the allocated space to the number of elements used.
Definition: DynamicListI.H:434
bitSet.H
Foam::boundBox::min
const point & min() const
Minimum describing the bounding box.
Definition: boundBoxI.H:91
forAll
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:296
Foam::divide
void divide(FieldField< Field, Type > &f, const FieldField< Field, Type > &f1, const FieldField< Field, scalar > &f2)
Foam::labelPair
Pair< label > labelPair
A pair of labels.
Definition: Pair.H:54
Foam::boundBox::span
vector span() const
The bounding box span (from minimum to maximum)
Definition: boundBoxI.H:127
Foam::operator<<
Ostream & operator<<(Ostream &, const boundaryPatch &p)
Write boundaryPatch as dictionary entries (without surrounding braces)
Definition: boundaryPatch.C:83
Foam::Field< vector >
Foam::Istream
An Istream is an abstract base class for all input systems (streams, files, token lists etc)....
Definition: Istream.H:61
Foam::Info
messageStream Info
Information stream (stdout output on master, null elsewhere)
Foam::treeBoundBox::faces
static const faceList faces
Face to point addressing.
Definition: treeBoundBox.H:151
Foam::DynamicList::append
DynamicList< T, SizeMin > & append(const T &val)
Append an element to the end of this list.
Definition: DynamicListI.H:511
Foam::sort
void sort(UList< T > &a)
Definition: UList.C:261
AABBTree.H
Foam::AABBTree::AABBTree
AABBTree()
Null constructor.
Definition: AABBTree.C:322
Foam::UList::empty
bool empty() const noexcept
True if the UList is empty (ie, size() is zero)
Definition: UListI.H:427
Foam::IOstream::check
virtual bool check(const char *operation) const
Check IOstream status for given operation.
Definition: IOstream.C:58
Foam::AABBTree::maxLevel_
label maxLevel_
Maximum tree level.
Definition: AABBTree.H:83
os
OBJstream os(runTime.globalPath()/outputName)
Foam::IOstreamOption::ASCII
"ascii" (normal default)
Definition: IOstreamOption.H:72
Foam::Istream::beginRawRead
virtual bool beginRawRead()=0
Start of low-level raw binary read.
Foam::treeBoundBox::contains
bool contains(const vector &dir, const point &) const
Contains point (inside or on edge) and moving in direction.
Definition: treeBoundBox.C:320
Foam::nl
constexpr char nl
Definition: Ostream.H:404
Foam::Pair
An ordered pair of two objects of type <T> with first() and second() elements.
Definition: Pair.H:54
Foam::AABBTree::addressing
const List< labelList > & addressing() const
Return the contents addressing.
Definition: AABBTree.C:449
f
labelList f(nPoints)
Foam::Vector< scalar >
Foam::AABBTree
Templated tree of axis-aligned bounding boxes (AABB)
Definition: AABBTree.H:59
Foam::List
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
Definition: BitOps.H:63
Foam::AABBTree::createBoxes
void createBoxes(const bool equalBinSize, const label level, const List< Type > &objects, const pointField &points, const DynamicList< label > &objectIDs, const treeBoundBox &bb, const label nodeI, DynamicList< Pair< treeBoundBox >> &bbs, DynamicList< labelPair > &nodes, DynamicList< labelList > &addressing) const
Create the bounding boxes by interrogating points.
Definition: AABBTree.C:127
Foam::token::SPACE
Space [isspace].
Definition: token.H:125
Foam::UList< Type >
points
const pointField & points
Definition: gmvOutputHeader.H:1
Foam::constant::electromagnetic::e
const dimensionedScalar e
Elementary charge.
Definition: createFields.H:11
Foam::identity
labelList identity(const label len, label start=0)
Create identity map of the given length with (map[i] == i)
Definition: labelList.C:38
Foam::direction
uint8_t direction
Definition: direction.H:52
FUNCTION_NAME
#define FUNCTION_NAME
Definition: messageStream.H:295
Foam::boundBox
A bounding box defined in terms of min/max extrema points.
Definition: boundBox.H:63
Foam::AABBTree::minLeafSize_
label minLeafSize_
Minimum points per leaf.
Definition: AABBTree.H:86
Foam::face
A face is a list of labels corresponding to mesh vertices.
Definition: face.H:72
Foam::readRawLabel
label readRawLabel(Istream &is)
Read raw label from binary stream.
Definition: label.C:46
Foam::UList::size
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:114
Foam::Ostream
An Ostream is an abstract base class for all output systems (streams, files, token lists,...
Definition: Ostream.H:56
Foam::AABBTree::addressing_
List< labelList > addressing_
Leaf addressing.
Definition: AABBTree.H:92