KiCad PCB EDA Suite
Loading...
Searching...
No Matches
container_2d.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2015-2016 Mario Luzeiro <[email protected]>
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
24
25#include "container_2d.h"
26#include "../ray.h"
27#include <boost/range/algorithm/partition.hpp>
28#include <boost/range/algorithm/nth_element.hpp>
29#include <wx/debug.h>
30
31
36
37
39{
40 std::lock_guard<std::mutex> lock( m_lock );
41 m_bbox.Reset();
42
43 for( LIST_OBJECT2D::iterator ii = m_objects.begin(); ii != m_objects.end(); ++ii )
44 {
45 delete *ii;
46 }
47
48 m_objects.clear();
49}
50
51
56
57
62
63
65 CONST_LIST_OBJECT2D& aOutList ) const
66{
68}
69
70
71bool CONTAINER_2D::IntersectAny( const RAYSEG2D& aSegRay ) const
72{
74 return false;
75}
76
77
85
86
92
93
95{
96 for( std::list<BVH_CONTAINER_NODE_2D*>::iterator ii = m_elementsToDelete.begin();
97 ii != m_elementsToDelete.end();
98 ++ii )
99 {
100 delete *ii;
101 }
102
103 m_elementsToDelete.clear();
104 m_tree = nullptr;
105 m_isInitialized = false;
106}
107
108
113
114
115#define BVH_CONTAINER2D_MAX_OBJ_PER_LEAF 4
116
117
119{
120 if( m_isInitialized )
121 destroy();
122
123 m_isInitialized = true;
124
125 if( m_objects.empty() )
126 {
127 return;
128 }
129
131
132 m_elementsToDelete.push_back( m_tree );
133 m_tree->m_BBox = m_bbox;
134
135 for( LIST_OBJECT2D::const_iterator ii = m_objects.begin(); ii != m_objects.end(); ++ii )
136 {
137 m_tree->m_LeafList.push_back( static_cast<const OBJECT_2D*>( *ii ) );
138 }
139
141}
142
143
144// Based on a blog post by VADIM KRAVCENKO
145// http://www.vadimkravcenko.com/bvh-tree-building
146// Implements:
147
148// "Split in the middle of the longest Axis"
149// "Creates a binary tree with Top-Down approach.
150// Fastest BVH building, but least [speed] accuracy."
151static bool sortByCentroidX( const OBJECT_2D* a, const OBJECT_2D* b )
152{
153 return a->GetCentroid()[0] < b->GetCentroid()[0];
154}
155
156
157static bool sortByCentroidY( const OBJECT_2D* a, const OBJECT_2D* b )
158{
159 return a->GetCentroid()[0] < b->GetCentroid()[0];
160}
161
162
163static bool sortByCentroidZ( const OBJECT_2D* a, const OBJECT_2D* b )
164{
165 return a->GetCentroid()[0] < b->GetCentroid()[0];
166}
167
168
170{
171 wxASSERT( aNodeParent != nullptr );
172 wxASSERT( aNodeParent->m_BBox.IsInitialized() == true );
173 wxASSERT( aNodeParent->m_LeafList.size() > 0 );
174
175 if( aNodeParent->m_LeafList.size() > BVH_CONTAINER2D_MAX_OBJ_PER_LEAF )
176 {
177 // Create Leaf Nodes
180 m_elementsToDelete.push_back( leftNode );
181 m_elementsToDelete.push_back( rightNode );
182
183 leftNode->m_BBox.Reset();
184 rightNode->m_BBox.Reset();
185 leftNode->m_LeafList.clear();
186 rightNode->m_LeafList.clear();
187
188 // Decide which axis to split
189 const unsigned int axis_to_split = aNodeParent->m_BBox.MaxDimension();
190
191 // Divide the objects
192 switch( axis_to_split )
193 {
194 case 0: aNodeParent->m_LeafList.sort( sortByCentroidX ); break;
195 case 1: aNodeParent->m_LeafList.sort( sortByCentroidY ); break;
196 case 2: aNodeParent->m_LeafList.sort( sortByCentroidZ ); break;
197 }
198
199 unsigned int i = 0;
200
201 for( CONST_LIST_OBJECT2D::const_iterator ii = aNodeParent->m_LeafList.begin();
202 ii != aNodeParent->m_LeafList.end();
203 ++ii )
204 {
205 const OBJECT_2D* object = static_cast<const OBJECT_2D*>( *ii );
206
207 if( i < (aNodeParent->m_LeafList.size() / 2 ) )
208 {
209 leftNode->m_BBox.Union( object->GetBBox() );
210 leftNode->m_LeafList.push_back( object );
211 }
212 else
213 {
214 rightNode->m_BBox.Union( object->GetBBox() );
215 rightNode->m_LeafList.push_back( object );
216 }
217
218 i++;
219 }
220
221 wxASSERT( leftNode->m_LeafList.size() > 0 );
222 wxASSERT( rightNode->m_LeafList.size() > 0 );
223 wxASSERT( ( leftNode->m_LeafList.size() + rightNode->m_LeafList.size() ) ==
224 aNodeParent->m_LeafList.size() );
225
226 aNodeParent->m_Children[0] = leftNode;
227 aNodeParent->m_Children[1] = rightNode;
228 aNodeParent->m_LeafList.clear();
229
230 recursiveBuild_MIDDLE_SPLIT( leftNode );
231 recursiveBuild_MIDDLE_SPLIT( rightNode );
232
233 wxASSERT( aNodeParent->m_LeafList.size() == 0 );
234 }
235 else
236 {
237 // It is a Leaf
238 aNodeParent->m_Children[0] = nullptr;
239 aNodeParent->m_Children[1] = nullptr;
240 }
241
242 wxASSERT( aNodeParent != nullptr );
243 wxASSERT( aNodeParent->m_BBox.IsInitialized() == true );
244}
245
246
247bool BVH_CONTAINER_2D::IntersectAny( const RAYSEG2D& aSegRay ) const
248{
249 wxASSERT( m_isInitialized == true );
250
251 if( m_tree )
252 return recursiveIntersectAny( m_tree, aSegRay );
253
254 return false;
255}
256
257
259 const RAYSEG2D& aSegRay ) const
260{
261 wxASSERT( aNode != nullptr );
262
263 if( aNode->m_BBox.Inside( aSegRay.m_Start ) || aNode->m_BBox.Inside( aSegRay.m_End ) ||
264 aNode->m_BBox.Intersect( aSegRay ) )
265 {
266 if( !aNode->m_LeafList.empty() )
267 {
268 wxASSERT( aNode->m_Children[0] == nullptr );
269 wxASSERT( aNode->m_Children[1] == nullptr );
270
271 // Leaf
272 for( const OBJECT_2D* obj : aNode->m_LeafList )
273 {
274 if( obj->IsPointInside( aSegRay.m_Start ) ||
275 obj->IsPointInside( aSegRay.m_End ) ||
276 obj->Intersect( aSegRay, nullptr, nullptr ) )
277 return true;
278 }
279 }
280 else
281 {
282 wxASSERT( aNode->m_Children[0] != nullptr );
283 wxASSERT( aNode->m_Children[1] != nullptr );
284
285 // Node
286 if( recursiveIntersectAny( aNode->m_Children[0], aSegRay ) )
287 return true;
288
289 if( recursiveIntersectAny( aNode->m_Children[1], aSegRay ) )
290 return true;
291 }
292 }
293
294 return false;
295}
296
297
299 CONST_LIST_OBJECT2D& aOutList ) const
300{
301 wxASSERT( aBBox.IsInitialized() == true );
302 wxASSERT( m_isInitialized == true );
303
304 aOutList.clear();
305
306 if( m_tree )
307 recursiveGetListObjectsIntersects( m_tree, aBBox, aOutList );
308}
309
310
312 const BBOX_2D& aBBox,
313 CONST_LIST_OBJECT2D& aOutList ) const
314{
315 wxASSERT( aNode != nullptr );
316 wxASSERT( aBBox.IsInitialized() == true );
317
318 if( aNode->m_BBox.Intersects( aBBox ) )
319 {
320 if( !aNode->m_LeafList.empty() )
321 {
322 wxASSERT( aNode->m_Children[0] == nullptr );
323 wxASSERT( aNode->m_Children[1] == nullptr );
324
325 // Leaf
326 for( CONST_LIST_OBJECT2D::const_iterator ii = aNode->m_LeafList.begin();
327 ii != aNode->m_LeafList.end();
328 ++ii )
329 {
330 const OBJECT_2D* obj = static_cast<const OBJECT_2D*>( *ii );
331
332 if( obj->Intersects( aBBox ) )
333 aOutList.push_back( obj );
334 }
335 }
336 else
337 {
338 wxASSERT( aNode->m_Children[0] != nullptr );
339 wxASSERT( aNode->m_Children[1] != nullptr );
340
341 // Node
342 recursiveGetListObjectsIntersects( aNode->m_Children[0], aBBox, aOutList );
343 recursiveGetListObjectsIntersects( aNode->m_Children[1], aBBox, aOutList );
344 }
345 }
346}
void GetIntersectingObjects(const BBOX_2D &aBBox, CONST_LIST_OBJECT2D &aOutList) const override
Get a list of objects that intersects a bounding box.
void Clear() override
BVH_CONTAINER_NODE_2D * m_tree
void recursiveGetListObjectsIntersects(const BVH_CONTAINER_NODE_2D *aNode, const BBOX_2D &aBBox, CONST_LIST_OBJECT2D &aOutList) const
void recursiveBuild_MIDDLE_SPLIT(BVH_CONTAINER_NODE_2D *aNodeParent)
bool recursiveIntersectAny(const BVH_CONTAINER_NODE_2D *aNode, const RAYSEG2D &aSegRay) const
bool IntersectAny(const RAYSEG2D &aSegRay) const override
Intersect and check if a segment ray hits a object or is inside it.
std::list< BVH_CONTAINER_NODE_2D * > m_elementsToDelete
CONTAINER_2D_BASE(OBJECT_2D_TYPE aObjType)
virtual void Clear()
virtual ~CONTAINER_2D_BASE()
std::mutex m_lock
LIST_OBJECT2D m_objects
void GetIntersectingObjects(const BBOX_2D &aBBox, CONST_LIST_OBJECT2D &aOutList) const override
Get a list of objects that intersects a bounding box.
bool IntersectAny(const RAYSEG2D &aSegRay) const override
Intersect and check if a segment ray hits a object or is inside it.
virtual bool Intersects(const BBOX_2D &aBBox) const =0
a.Intersects(b) ⇔ !a.Disjoint(b) ⇔ !(a ∩ b = ∅)
const SFVEC2F & GetCentroid() const
Definition object_2d.h:101
const BBOX_2D & GetBBox() const
Definition object_2d.h:99
virtual bool Intersect(const RAYSEG2D &aSegRay, float *aOutT, SFVEC2F *aNormalOut) const =0
virtual bool IsPointInside(const SFVEC2F &aPoint) const =0
#define BVH_CONTAINER2D_MAX_OBJ_PER_LEAF
static bool sortByCentroidX(const OBJECT_2D *a, const OBJECT_2D *b)
static bool sortByCentroidY(const OBJECT_2D *a, const OBJECT_2D *b)
static bool sortByCentroidZ(const OBJECT_2D *a, const OBJECT_2D *b)
std::list< const OBJECT_2D * > CONST_LIST_OBJECT2D
OBJECT_2D_TYPE
Definition object_2d.h:41
Manage a bounding box defined by two SFVEC2F min max points.
Definition bbox_2d.h:38
bool Intersects(const BBOX_2D &aBBox) const
Test if a bounding box intersects this box.
Definition bbox_2d.cpp:207
unsigned int MaxDimension() const
Definition bbox_2d.cpp:127
void Union(const SFVEC2F &aPoint)
Recalculate the bounding box adding a point.
Definition bbox_2d.cpp:89
bool IsInitialized() const
Check if this bounding box is already initialized.
Definition bbox_2d.cpp:75
bool Inside(const SFVEC2F &aPoint) const
Check is a point is inside this bounding box.
Definition bbox_2d.cpp:219
void Reset()
Reset the bounding box to zero and uninitialize it.
Definition bbox_2d.cpp:82
bool Intersect(const RAY2D &aRay, float *t) const
Definition bbox_2d.cpp:246
CONST_LIST_OBJECT2D m_LeafList
Store the list of objects if that node is a Leaf.
BVH_CONTAINER_NODE_2D * m_Children[2]
SFVEC2F m_Start
Definition ray.h:103
SFVEC2F m_End
Definition ray.h:104