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-------------------------------------------------------------------------------
11License
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
34template<class Type>
35Foam::scalar Foam::AABBTree<Type>::tolerance_ = 1e-4;
36
37// * * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * //
38
39template<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 {
51 meshTools::writeOBJ(os, pt);
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
78template<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
125template<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
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
321template<class Type>
323:
324 maxLevel_(0),
325 minLeafSize_(0),
326 boundBoxes_(),
327 addressing_()
328{}
329
330
331template<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
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
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
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
441template<class Type>
443{
444 return boundBoxes_;
445}
446
447
448template<class Type>
450{
451 return addressing_;
452}
453
454
455template<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
470template<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
487template<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
514template<class Type>
515Foam::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// ************************************************************************* //
Templated tree of axis-aligned bounding boxes (AABB)
Definition: AABBTree.H:73
label minLeafSize_
Minimum points per leaf.
Definition: AABBTree.H:86
AABBTree()
Null constructor.
Definition: AABBTree.C:322
const List< labelList > & addressing() const
Return the contents addressing.
Definition: AABBTree.C:449
label maxLevel_
Maximum tree level.
Definition: AABBTree.H:83
bool pointInside(const point &pt) const
Determine whether a point is inside the bounding boxes.
Definition: AABBTree.C:456
void writeOBJ(const bool writeLinesOnly, const treeBoundBox &bb, label &vertI, Ostream &os) const
Write OBJ file of bounding box.
Definition: AABBTree.C:41
List< labelList > addressing_
Leaf addressing.
Definition: AABBTree.H:92
const List< treeBoundBox > & boundBoxes() const
Return the bounding boxes making up the tree.
Definition: AABBTree.C:442
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
List< treeBoundBox > boundBoxes_
Bounding boxes making up the tree.
Definition: AABBTree.H:89
A 1D vector of objects of type <T> that resizes itself as necessary to accept the new objects.
Definition: DynamicList.H:72
DynamicList< T, SizeMin > & shrink()
Shrink the allocated space to the number of elements used.
Definition: DynamicListI.H:434
void append(const T &val)
Copy append an element to the end of this list.
Definition: DynamicListI.H:503
streamFormat format() const noexcept
Get the current stream format.
virtual bool check(const char *operation) const
Check IOstream status for given operation.
Definition: IOstream.C:58
An Istream is an abstract base class for all input systems (streams, files, token lists etc)....
Definition: Istream.H:64
virtual bool beginRawRead()=0
Start of low-level raw binary read.
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
Definition: List.H:77
void transfer(List< T > &list)
Definition: List.C:447
void append(const T &val)
Append an element at the end of the list.
Definition: ListI.H:175
virtual Ostream & write(const char c)
Write character.
Definition: OBJstream.C:78
An Ostream is an abstract base class for all output systems (streams, files, token lists,...
Definition: Ostream.H:62
int overlaps
Flag to control which overlap calculations are performed.
Definition: PDRparams.H:97
An ordered pair of two objects of type <T> with first() and second() elements.
Definition: Pair.H:69
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:94
bool empty() const noexcept
True if the UList is empty (ie, size() is zero)
Definition: UListI.H:427
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:114
A bitSet stores bits (elements with only two states) in packed internal format and supports a variety...
Definition: bitSet.H:66
unsigned int count(const bool on=true) const
Count number of bits set.
Definition: bitSetI.H:500
void set(const bitSet &bitset)
Set specified bits from another bitset.
Definition: bitSetI.H:590
A bounding box defined in terms of min/max extrema points.
Definition: boundBox.H:64
const point & min() const
Minimum describing the bounding box.
Definition: boundBoxI.H:91
void add(const boundBox &bb)
Extend to include the second box.
Definition: boundBoxI.H:191
void inflate(const scalar s)
Inflate box by factor*mag(span) in all dimensions.
Definition: boundBox.C:175
vector span() const
The bounding box span (from minimum to maximum)
Definition: boundBoxI.H:127
An edge is a list of two point labels. The functionality it provides supports the discretisation on a...
Definition: edge.H:66
A face is a list of labels corresponding to mesh vertices.
Definition: face.H:75
@ SPACE
Space [isspace].
Definition: token.H:125
Standard boundBox with extra functionality for use in octree.
Definition: treeBoundBox.H:89
static const edgeList edges
Edge to point addressing.
Definition: treeBoundBox.H:154
static const faceList faces
Face to point addressing.
Definition: treeBoundBox.H:151
tmp< pointField > points() const
Vertex coordinates. In octant coding.
Definition: treeBoundBox.C:92
OBJstream os(runTime.globalPath()/outputName)
const pointField & points
#define FUNCTION_NAME
Pair< label > labelPair
A pair of labels.
Definition: Pair.H:57
labelList identity(const label len, label start=0)
Return an identity map of the given length with (map[i] == i)
Definition: labelList.C:38
void component(FieldField< Field, typename FieldField< Field, Type >::cmptType > &sf, const FieldField< Field, Type > &f, const direction d)
messageStream Info
Information stream (stdout output on master, null elsewhere)
Ostream & operator<<(Ostream &, const boundaryPatch &p)
Write boundaryPatch as dictionary entries (without surrounding braces)
Definition: boundaryPatch.C:83
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:372
Istream & operator>>(Istream &, directionInfo &)
label readRawLabel(Istream &is)
Read raw label from binary stream.
Definition: label.C:46
void sort(UList< T > &list)
Sort the list.
Definition: UList.C:342
uint8_t direction
Definition: direction.H:56
void divide(FieldField< Field, Type > &f, const FieldField< Field, Type > &f1, const FieldField< Field, scalar > &f2)
constexpr char nl
The newline '\n' character (0x0a)
Definition: Ostream.H:53
labelList f(nPoints)
volScalarField & e
Definition: createFields.H:11
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:333