Adjust connectivity to use RTree

Connectivity searches were slow for large, complex boards.  This was
partly due to the need to search all lists for possible connections
between anchors.

We remove this requirement by creating an RTree based on items' bounding
boxes similar to the PNS router.  Then we get both the colliding items
and search forward/backward to see if there are connections between the
anchors and the other item.

Because we use RTree, we no longer need the maintenance overhead of multiple
item lists and an anchor list in the connectivity class.

Fixes: lp:1701791
* https://bugs.launchpad.net/kicad/+bug/1701791

Fixes: lp:1769408
* https://bugs.launchpad.net/kicad/+bug/1769408

Fixes: lp:1761989
* https://bugs.launchpad.net/kicad/+bug/1761989
This commit is contained in:
Seth Hillbrand 2018-05-20 17:06:01 -07:00
parent 6f65f835f4
commit 63a952d239
5 changed files with 340 additions and 400 deletions

View File

@ -2727,9 +2727,9 @@ void BOARD::ReplaceNetlist( NETLIST& aNetlist, bool aDeleteSinglePadNets,
{
std::vector<unsigned int> padCount( connAlgo->NetCount() );
for( const auto cnItem : connAlgo->PadList() )
for( const auto cnItem : connAlgo->ItemList() )
{
if( !cnItem->Valid() )
if( !cnItem->Valid() || cnItem->Parent()->Type() != PCB_PAD_T )
continue;
int net = cnItem->Parent()->GetNetCode();

View File

@ -24,6 +24,7 @@
#include <connectivity_algo.h>
#include <widgets/progress_reporter.h>
#include <geometry/geometry_utils.h>
#include <thread>
#include <mutex>
@ -168,25 +169,25 @@ bool CN_CONNECTIVITY_ALGO::Remove( BOARD_ITEM* aItem )
m_itemMap.erase( static_cast<BOARD_CONNECTED_ITEM*>( pad ) );
}
m_padList.SetDirty( true );
m_itemList.SetDirty( true );
break;
case PCB_PAD_T:
m_itemMap[ static_cast<BOARD_CONNECTED_ITEM*>( aItem ) ].MarkItemsAsInvalid();
m_itemMap.erase( static_cast<BOARD_CONNECTED_ITEM*>( aItem ) );
m_padList.SetDirty( true );
m_itemList.SetDirty( true );
break;
case PCB_TRACE_T:
m_itemMap[ static_cast<BOARD_CONNECTED_ITEM*>( aItem ) ].MarkItemsAsInvalid();
m_itemMap.erase( static_cast<BOARD_CONNECTED_ITEM*>( aItem ) );
m_trackList.SetDirty( true );
m_itemList.SetDirty( true );
break;
case PCB_VIA_T:
m_itemMap[ static_cast<BOARD_CONNECTED_ITEM*>( aItem ) ].MarkItemsAsInvalid();
m_itemMap.erase( static_cast<BOARD_CONNECTED_ITEM*>( aItem ) );
m_viaList.SetDirty( true );
m_itemList.SetDirty( true );
break;
case PCB_ZONE_AREA_T:
@ -202,6 +203,10 @@ bool CN_CONNECTIVITY_ALGO::Remove( BOARD_ITEM* aItem )
return false;
}
// Once we delete an item, it may connect between lists, so mark both as potentially invalid
m_itemList.SetHasInvalid( true );
m_zoneList.SetHasInvalid( true );
return true;
}
@ -243,7 +248,7 @@ bool CN_CONNECTIVITY_ALGO::Add( BOARD_ITEM* aItem )
if( m_itemMap.find( pad ) != m_itemMap.end() )
return false;
add( m_padList, pad );
add( m_itemList, pad );
}
break;
@ -252,7 +257,7 @@ bool CN_CONNECTIVITY_ALGO::Add( BOARD_ITEM* aItem )
if( m_itemMap.find ( static_cast<D_PAD*>( aItem ) ) != m_itemMap.end() )
return false;
add( m_padList, static_cast<D_PAD*>( aItem ) );
add( m_itemList, static_cast<D_PAD*>( aItem ) );
break;
@ -261,7 +266,7 @@ bool CN_CONNECTIVITY_ALGO::Add( BOARD_ITEM* aItem )
if( m_itemMap.find( static_cast<TRACK*>( aItem ) ) != m_itemMap.end() )
return false;
add( m_trackList, static_cast<TRACK*>( aItem ) );
add( m_itemList, static_cast<TRACK*>( aItem ) );
break;
}
@ -270,7 +275,7 @@ bool CN_CONNECTIVITY_ALGO::Add( BOARD_ITEM* aItem )
if( m_itemMap.find( static_cast<VIA*>( aItem ) ) != m_itemMap.end() )
return false;
add( m_viaList, static_cast<VIA*>( aItem ) );
add( m_itemList, static_cast<VIA*>( aItem ) );
break;
@ -302,143 +307,6 @@ void CN_CONNECTIVITY_ALGO::searchConnections( bool aIncludeZones )
{
std::mutex cnListLock;
int totalDirtyCount = 0;
if( m_lastSearchWithZones != aIncludeZones )
{
m_padList.MarkAllAsDirty();
m_viaList.MarkAllAsDirty();
m_trackList.MarkAllAsDirty();
m_zoneList.MarkAllAsDirty();
}
m_lastSearchWithZones = aIncludeZones;
auto checkForConnection = [ &cnListLock ] ( const CN_ANCHOR_PTR point, CN_ITEM* aRefItem, int aMaxDist = 0 )
{
const auto parent = aRefItem->Parent();
assert( point->Item() );
assert( point->Item()->Parent() );
assert( aRefItem->Parent() );
if( !point->Item()->Valid() )
return;
if( !aRefItem->Valid() )
return;
if( parent == point->Item()->Parent() )
return;
if( !( parent->GetLayerSet() &
point->Item()->Parent()->GetLayerSet() ).any() )
return;
switch( parent->Type() )
{
case PCB_PAD_T:
case PCB_VIA_T:
if( parent->HitTest( wxPoint( point->Pos().x, point->Pos().y ) ) )
{
std::lock_guard<std::mutex> lock( cnListLock );
CN_ITEM::Connect( aRefItem, point->Item() );
}
break;
case PCB_TRACE_T:
{
const auto track = static_cast<TRACK*> ( parent );
const VECTOR2I d_start( VECTOR2I( track->GetStart() ) - point->Pos() );
const VECTOR2I d_end( VECTOR2I( track->GetEnd() ) - point->Pos() );
if( d_start.EuclideanNorm() < aMaxDist
|| d_end.EuclideanNorm() < aMaxDist )
{
std::lock_guard<std::mutex> lock( cnListLock );
CN_ITEM::Connect( aRefItem, point->Item() );
}
break;
}
case PCB_ZONE_T:
case PCB_ZONE_AREA_T:
{
const auto zone = static_cast<ZONE_CONTAINER*> ( parent );
auto zoneItem = static_cast<CN_ZONE*> ( aRefItem );
if( point->Item()->Net() != parent->GetNetCode() )
return;
if( !( zone->GetLayerSet() &
point->Item()->Parent()->GetLayerSet() ).any() )
return;
if( zoneItem->ContainsAnchor( point ) )
{
std::lock_guard<std::mutex> lock( cnListLock );
CN_ITEM::Connect( zoneItem, point->Item() );
}
break;
}
default :
assert( false );
}
};
auto checkInterZoneConnection = [ &cnListLock ] ( CN_ZONE* testedZone, CN_ZONE* aRefZone )
{
const auto parentZone = static_cast<const ZONE_CONTAINER*>( aRefZone->Parent() );
if( testedZone->Parent()->Type () != PCB_ZONE_AREA_T )
return;
if( testedZone == aRefZone )
return;
if( testedZone->Parent() == aRefZone->Parent() )
return;
if( testedZone->Net() != parentZone->GetNetCode() )
return; // we only test zones belonging to the same net
if( !( testedZone->Parent()->GetLayerSet() & parentZone->GetLayerSet() ).any() )
return; // and on same layer
const auto& outline = parentZone->GetFilledPolysList().COutline( aRefZone->SubpolyIndex() );
for( int i = 0; i < outline.PointCount(); i++ )
{
if( testedZone->ContainsPoint( outline.CPoint( i ) ) )
{
std::lock_guard<std::mutex> lock( cnListLock );
CN_ITEM::Connect( aRefZone, testedZone );
return;
}
}
const auto testedZoneParent = static_cast<const ZONE_CONTAINER*>( testedZone->Parent() );
const auto& outline2 = testedZoneParent->GetFilledPolysList().COutline( testedZone->SubpolyIndex() );
for( int i = 0; i < outline2.PointCount(); i++ )
{
if( aRefZone->ContainsPoint( outline2.CPoint( i ) ) )
{
std::lock_guard<std::mutex> lock( cnListLock );
CN_ITEM::Connect( aRefZone, testedZone );
return;
}
}
};
#ifdef CONNECTIVITY_DEBUG
printf("Search start\n");
#endif
@ -449,16 +317,12 @@ void CN_CONNECTIVITY_ALGO::searchConnections( bool aIncludeZones )
std::vector<CN_ITEM*> garbage;
garbage.reserve( 1024 );
m_padList.RemoveInvalidItems( garbage );
m_viaList.RemoveInvalidItems( garbage );
m_trackList.RemoveInvalidItems( garbage );
m_itemList.RemoveInvalidItems( garbage );
m_zoneList.RemoveInvalidItems( garbage );
for( auto item : garbage )
delete item;
//auto all = allItemsInBoard();
#ifdef CONNECTIVITY_DEBUG
for( auto item : m_padList )
if( all.find( item->Parent() ) == all.end() ) { printf("Failing pad : %p\n", item->Parent() ); assert ( false ); }
@ -477,49 +341,20 @@ void CN_CONNECTIVITY_ALGO::searchConnections( bool aIncludeZones )
garbage_collection.Show();
PROF_COUNTER search_cnt( "search-connections" );
PROF_COUNTER search_basic( "search-basic" );
PROF_COUNTER search_pads( "search-pads" );
#endif
if( m_padList.IsDirty() || m_trackList.IsDirty() || m_viaList.IsDirty() )
if( m_itemList.IsDirty() )
{
totalDirtyCount++;
for( auto padItem : m_padList )
for( auto item : m_itemList )
{
auto pad = static_cast<D_PAD*> ( padItem->Parent() );
auto searchPads = std::bind( checkForConnection, _1, padItem );
if( item->Dirty() )
{
CN_VISITOR visitor( item, &cnListLock );
m_itemList.FindNearby( item, visitor );
m_padList.FindNearby( pad->ShapePos(), pad->GetBoundingRadius(), searchPads, pad->GetLayerSet() );
m_trackList.FindNearby( pad->ShapePos(), pad->GetBoundingRadius(), searchPads, pad->GetLayerSet() );
m_viaList.FindNearby( pad->ShapePos(), pad->GetBoundingRadius(), searchPads, pad->GetLayerSet() );
}
#ifdef PROFILE
search_pads.Show();
PROF_COUNTER search_tracks( "search-tracks" );
#endif
for( auto& trackItem : m_trackList )
{
auto track = static_cast<TRACK*> ( trackItem->Parent() );
int dist_max = track->GetWidth() / 2;
auto searchTracks = std::bind( checkForConnection, _1, trackItem, dist_max );
m_trackList.FindNearby( track->GetStart(), dist_max, searchTracks, track->GetLayerSet() );
m_trackList.FindNearby( track->GetEnd(), dist_max, searchTracks, track->GetLayerSet() );
}
#ifdef PROFILE
search_tracks.Show();
#endif
for( auto& viaItem : m_viaList )
{
auto via = static_cast<VIA*> ( viaItem->Parent() );
int dist_max = via->GetWidth() / 2;
auto searchVias = std::bind( checkForConnection, _1, viaItem, dist_max );
totalDirtyCount++;
m_viaList.FindNearby( via->GetStart(), dist_max, searchVias );
m_trackList.FindNearby( via->GetStart(), dist_max, searchVias );
if( aIncludeZones )
m_zoneList.FindNearby( item, visitor );
}
}
}
@ -529,8 +364,6 @@ void CN_CONNECTIVITY_ALGO::searchConnections( bool aIncludeZones )
if( aIncludeZones )
{
int cnt = 0;
if( m_progressReporter )
{
m_progressReporter->SetMaxProgress( m_zoneList.Size() );
@ -556,25 +389,17 @@ void CN_CONNECTIVITY_ALGO::searchConnections( bool aIncludeZones )
{
auto item = m_zoneList[i];
auto zoneItem = static_cast<CN_ZONE *> (item);
auto searchZones = std::bind( checkForConnection, _1, zoneItem );
if( zoneItem->Dirty() || m_padList.IsDirty() || m_trackList.IsDirty() || m_viaList.IsDirty() )
if( zoneItem->Dirty() )
{
totalDirtyCount++;
m_viaList.FindNearby( zoneItem->BBox(), searchZones );
m_trackList.FindNearby( zoneItem->BBox(), searchZones );
m_padList.FindNearby( zoneItem->BBox(), searchZones );
m_zoneList.FindNearbyZones( zoneItem->BBox(), std::bind( checkInterZoneConnection, _1, zoneItem ) );
CN_VISITOR visitor( item, &cnListLock );
m_itemList.FindNearby( item, visitor );
m_zoneList.FindNearby( item, visitor );
}
if (m_progressReporter)
{
std::lock_guard<std::mutex> lock( cnListLock );
cnt++;
if (m_progressReporter)
{
m_progressReporter->AdvanceProgress();
}
m_progressReporter->AdvanceProgress();
}
}
}
@ -582,9 +407,7 @@ void CN_CONNECTIVITY_ALGO::searchConnections( bool aIncludeZones )
m_zoneList.ClearDirtyFlags();
}
m_padList.ClearDirtyFlags();
m_viaList.ClearDirtyFlags();
m_trackList.ClearDirtyFlags();
m_itemList.ClearDirtyFlags();
#ifdef CONNECTIVITY_DEBUG
printf("Search end\n");
@ -608,6 +431,9 @@ void CN_ITEM::RemoveInvalidRefs()
void CN_LIST::RemoveInvalidItems( std::vector<CN_ITEM*>& aGarbage )
{
if( !m_hasInvalid )
return;
auto lastItem = std::remove_if(m_items.begin(), m_items.end(), [&aGarbage] ( CN_ITEM* item )
{
if( !item->Valid() )
@ -619,36 +445,22 @@ void CN_LIST::RemoveInvalidItems( std::vector<CN_ITEM*>& aGarbage )
return false;
} );
if( lastItem != m_items.end())
{
auto lastAnchor = std::remove_if(m_anchors.begin(), m_anchors.end(),
[] ( const CN_ANCHOR_PTR anchor ) {
return !anchor->Valid();
} );
m_anchors.resize( lastAnchor - m_anchors.begin() );
for( auto i = 0; i < PCB_LAYER_ID_COUNT; i++ )
{
lastAnchor = std::remove_if(m_layer_anchors[i].begin(), m_layer_anchors[i].end(),
[] ( const CN_ANCHOR_PTR anchor ) {
return !anchor->Valid();
} );
m_layer_anchors[i].resize( lastAnchor - m_layer_anchors[i].begin() );
}
m_items.resize( lastItem - m_items.begin() );
}
m_items.resize( lastItem - m_items.begin() );
// fixme: mem leaks
for( auto item : m_items )
item->RemoveInvalidRefs();
for( auto item : aGarbage )
m_index.Remove( item );
m_hasInvalid = false;
}
bool CN_CONNECTIVITY_ALGO::isDirty() const
{
return m_viaList.IsDirty() || m_trackList.IsDirty() || m_zoneList.IsDirty() || m_padList.IsDirty();
return m_itemList.IsDirty() || m_zoneList.IsDirty();
}
@ -706,9 +518,7 @@ const CN_CONNECTIVITY_ALGO::CLUSTERS CN_CONNECTIVITY_ALGO::SearchClusters( CLUST
head->ListInsert( aItem );
};
std::for_each( m_padList.begin(), m_padList.end(), addToSearchList );
std::for_each( m_trackList.begin(), m_trackList.end(), addToSearchList );
std::for_each( m_viaList.begin(), m_viaList.end(), addToSearchList );
std::for_each( m_itemList.begin(), m_itemList.end(), addToSearchList );
if( includeZones )
{
@ -972,6 +782,116 @@ void CN_CONNECTIVITY_ALGO::MarkNetAsDirty( int aNet )
}
void CN_VISITOR::checkZoneItemConnection( CN_ZONE* aZone, CN_ITEM* aItem )
{
auto zoneItem = static_cast<CN_ZONE*> ( aZone );
if( zoneItem->Net() != aItem->Net() && !aItem->CanChangeNet() )
return;
if( zoneItem->ContainsPoint( aItem->GetAnchor( 0 ) ) ||
( aItem->Parent()->Type() == PCB_TRACE_T &&
zoneItem->ContainsPoint( aItem->GetAnchor( 1 ) ) ) )
{
std::lock_guard<std::mutex> lock( *m_listLock );
CN_ITEM::Connect( zoneItem, aItem );
}
}
void CN_VISITOR::checkZoneZoneConnection( CN_ZONE* aZoneA, CN_ZONE* aZoneB )
{
const auto refParent = static_cast<const ZONE_CONTAINER*>( aZoneA->Parent() );
const auto testedParent = static_cast<const ZONE_CONTAINER*>( aZoneB->Parent() );
if( testedParent->Type () != PCB_ZONE_AREA_T )
return;
if( aZoneB == aZoneA || refParent == testedParent )
return;
if( aZoneB->Net() != aZoneA->Net() )
return; // we only test zones belonging to the same net
const auto& outline = refParent->GetFilledPolysList().COutline( aZoneA->SubpolyIndex() );
for( int i = 0; i < outline.PointCount(); i++ )
{
if( aZoneB->ContainsPoint( outline.CPoint( i ) ) )
{
std::lock_guard<std::mutex> lock( *m_listLock );
CN_ITEM::Connect( aZoneA, aZoneB );
return;
}
}
const auto& outline2 = testedParent->GetFilledPolysList().COutline( aZoneB->SubpolyIndex() );
for( int i = 0; i < outline2.PointCount(); i++ )
{
if( aZoneA->ContainsPoint( outline2.CPoint( i ) ) )
{
std::lock_guard<std::mutex> lock( *m_listLock );
CN_ITEM::Connect( aZoneA, aZoneB );
return;
}
}
}
bool CN_VISITOR::operator()( CN_ITEM* aCandidate )
{
const auto parentA = aCandidate->Parent();
const auto parentB = m_item->Parent();
if( !aCandidate->Valid() || !m_item->Valid() )
return true;
if( parentA == parentB )
return true;
if( !( parentA->GetLayerSet() & parentB->GetLayerSet() ).any() )
return true;
// We should handle zone-zone connection separately
if ( ( parentA->Type() == PCB_ZONE_AREA_T || parentA->Type() == PCB_ZONE_T ) &&
( parentB->Type() == PCB_ZONE_AREA_T || parentB->Type() == PCB_ZONE_T ) )
{
checkZoneZoneConnection( static_cast<CN_ZONE*>( m_item ),
static_cast<CN_ZONE*>( aCandidate ) );
return true;
}
if( parentA->Type() == PCB_ZONE_AREA_T || parentA->Type() == PCB_ZONE_T)
{
checkZoneItemConnection( static_cast<CN_ZONE*>( aCandidate ), m_item );
return true;
}
if( parentB->Type() == PCB_ZONE_AREA_T || parentB->Type() == PCB_ZONE_T)
{
checkZoneItemConnection( static_cast<CN_ZONE*>( m_item ), aCandidate );
return true;
}
// Items do not necessarily have reciprocity as we only check for anchors
// therefore, we check HitTest both directions A->B & B->A
// TODO: Check for collision geometry on extended features
wxPoint ptA1( aCandidate->GetAnchor( 0 ).x, aCandidate->GetAnchor( 0 ).y );
wxPoint ptA2( aCandidate->GetAnchor( 1 ).x, aCandidate->GetAnchor( 1 ).y );
wxPoint ptB1( m_item->GetAnchor( 0 ).x, m_item->GetAnchor( 0 ).y );
wxPoint ptB2( m_item->GetAnchor( 1 ).x, m_item->GetAnchor( 1 ).y );
if( parentA->HitTest( ptB1 ) || parentB->HitTest( ptA1 ) ||
( parentA->Type() == PCB_TRACE_T && parentB->HitTest( ptA2 ) ) ||
( parentB->Type() == PCB_TRACE_T && parentA->HitTest( ptB2 ) ) )
{
std::lock_guard<std::mutex> lock( *m_listLock );
CN_ITEM::Connect( m_item, aCandidate );
}
return true;
};
int CN_ITEM::AnchorCount() const
{
if( !m_valid )
@ -1064,9 +984,7 @@ void CN_CONNECTIVITY_ALGO::Clear()
m_ratsnestClusters.clear();
m_connClusters.clear();
m_itemMap.clear();
m_padList.Clear();
m_trackList.Clear();
m_viaList.Clear();
m_itemList.Clear();
m_zoneList.Clear();
}
@ -1074,13 +992,8 @@ void CN_CONNECTIVITY_ALGO::Clear()
void CN_CONNECTIVITY_ALGO::ForEachItem( const std::function<void( CN_ITEM& )>& aFunc )
{
for( auto item : m_padList )
aFunc( *item );
for( auto item : m_viaList )
aFunc( *item );
for( auto item : m_trackList )
for( auto item : m_itemList )
aFunc( *item );
for( auto item : m_zoneList )
@ -1090,17 +1003,11 @@ void CN_CONNECTIVITY_ALGO::ForEachItem( const std::function<void( CN_ITEM& )>& a
void CN_CONNECTIVITY_ALGO::ForEachAnchor( const std::function<void( CN_ANCHOR& )>& aFunc )
{
for( const auto& anchor : m_padList.Anchors() )
aFunc( *anchor );
for( const auto& anchor : m_viaList.Anchors() )
aFunc( *anchor );
for( const auto& anchor : m_trackList.Anchors() )
aFunc( *anchor );
for( const auto& anchor : m_zoneList.Anchors() )
aFunc( *anchor );
ForEachItem( [aFunc] ( CN_ITEM& item ) {
for( const auto& anchor : item.Anchors() )
aFunc( *anchor );
}
);
}

View File

@ -43,6 +43,7 @@
#include <deque>
#include <intrusive_list.h>
#include <connectivity_rtree.h>
#include <connectivity_data.h>
class CN_ITEM;
@ -286,6 +287,9 @@ private:
///> dirty flag, used to identify recently added item not yet scanned into the connectivity search
bool m_dirty;
///> bounding box for the item
BOX2I m_bbox;
public:
void Dump();
@ -301,11 +305,9 @@ public:
virtual ~CN_ITEM() {};
CN_ANCHOR_PTR AddAnchor( const VECTOR2I& aPos )
void AddAnchor( const VECTOR2I& aPos )
{
m_anchors.emplace_back( std::make_shared<CN_ANCHOR>( aPos, this ) );
//printf("%p add %d\n", this, m_anchors.size() );
return m_anchors.back();
m_anchors.emplace_back( std::make_unique<CN_ANCHOR>( aPos, this ) );
}
CN_ANCHORS& Anchors()
@ -333,6 +335,16 @@ public:
return m_dirty;
}
const BOX2I BBox()
{
if( m_dirty )
{
EDA_RECT box = m_parent->GetBoundingBox();
m_bbox = BOX2I( box.GetPosition(), box.GetSize() );
}
return m_bbox;
}
BOARD_CONNECTED_ITEM* Parent() const
{
return m_parent;
@ -407,45 +419,23 @@ class CN_LIST
{
private:
bool m_dirty;
std::vector<CN_ANCHOR_PTR> m_anchors;
CN_ANCHORS m_layer_anchors[PCB_LAYER_ID_COUNT];
bool m_hasInvalid;
CN_RTREE<CN_ITEM*> m_index;
protected:
std::vector<CN_ITEM*> m_items;
void addAnchor( VECTOR2I pos, CN_ITEM* item )
void addItemtoTree( CN_ITEM* item )
{
CN_ANCHOR_PTR new_anchor = item->AddAnchor( pos );
m_anchors.push_back( new_anchor );
for( int i = 0; i < PCB_LAYER_ID_COUNT; i++ )
{
if( ( item->Parent()->GetLayerSet() & LSET( 1, i ) ).any() )
m_layer_anchors[i].push_back( new_anchor );
}
}
private:
void sort()
{
if( m_dirty )
{
std::sort( m_anchors.begin(), m_anchors.end() );
for( auto i = 0; i < PCB_LAYER_ID_COUNT; i++ )
{
std::sort( m_layer_anchors[i].begin(), m_layer_anchors[i].end() );
}
m_dirty = false;
}
m_index.Insert( item );
}
public:
CN_LIST()
{
m_dirty = false;
m_hasInvalid = false;
}
void Clear()
@ -454,6 +444,7 @@ public:
delete item;
m_items.clear();
m_index.RemoveAll();
}
using ITER = decltype(m_items)::iterator;
@ -463,13 +454,13 @@ public:
CN_ITEM* operator[] ( int aIndex ) { return m_items[aIndex]; }
std::vector<CN_ANCHOR_PTR>& Anchors() { return m_anchors; }
template <class T>
void FindNearby( VECTOR2I aPosition, int aDistMax, T aFunc, LSET aLayers = LSET::AllLayersMask(), bool aDirtyOnly = false );
void FindNearby( CN_ITEM *aItem, T aFunc );
template <class T>
void FindNearby( BOX2I aBBox, T aFunc, LSET aLayers = LSET::AllLayersMask(), bool aDirtyOnly = false );
void SetHasInvalid( bool aInvalid = true )
{
m_hasInvalid = aInvalid;
}
void SetDirty( bool aDirty = true )
{
@ -481,12 +472,6 @@ public:
return m_dirty;
}
void ClearConnections()
{
for( auto& anchor : m_anchors )
anchor->Item()->ClearConnections();
}
void RemoveInvalidItems( std::vector<CN_ITEM*>& aGarbage );
void ClearDirtyFlags()
@ -509,52 +494,35 @@ public:
{
return m_items.size();
}
};
class CN_PAD_LIST : public CN_LIST
{
public:
CN_ITEM* Add( D_PAD* pad )
{
auto item = new CN_ITEM( pad, false, 2 );
addAnchor( pad->ShapePos(), item );
item->AddAnchor( pad->ShapePos() );
addItemtoTree( item );
m_items.push_back( item );
SetDirty();
return item;
}
};
class CN_TRACK_LIST : public CN_LIST
{
public:
CN_ITEM* Add( TRACK* track )
{
auto item = new CN_ITEM( track, true );
m_items.push_back( item );
addAnchor( track->GetStart(), item );
addAnchor( track->GetEnd(), item );
item->AddAnchor( track->GetStart() );
item->AddAnchor( track->GetEnd() );
addItemtoTree( item );
SetDirty();
return item;
}
};
class CN_VIA_LIST : public CN_LIST
{
public:
CN_ITEM* Add( VIA* via )
{
auto item = new CN_ITEM( via, true );
m_items.push_back( item );
addAnchor( via->GetStart(), item );
item->AddAnchor( via->GetStart() );
addItemtoTree( item );
SetDirty();
return item;
}
@ -624,9 +592,10 @@ public:
const auto& outline = zone->GetFilledPolysList().COutline( j );
for( int k = 0; k < outline.PointCount(); k++ )
addAnchor( outline.CPoint( k ), zitem );
zitem->AddAnchor( outline.CPoint( k ) );
m_items.push_back( zitem );
addItemtoTree( zitem );
rv.push_back( zitem );
SetDirty();
}
@ -639,39 +608,6 @@ public:
};
template <class T>
void CN_LIST::FindNearby( BOX2I aBBox, T aFunc, LSET aLayers, bool aDirtyOnly )
{
sort();
CN_ANCHORS *anchor_set = &m_anchors;
PCB_LAYER_ID layer = aLayers.ExtractLayer();
if( layer > 0 )
anchor_set = &(m_layer_anchors[ layer ]);
if( (*anchor_set).size() == 0 )
return;
CN_ANCHOR_PTR lower_ptr = std::make_shared<CN_ANCHOR>
( aBBox.GetPosition(), (*anchor_set)[0]->Item() );
auto lower_it = std::lower_bound( anchor_set->begin(), anchor_set->end(), lower_ptr,
[]( const CN_ANCHOR_PTR& a, const CN_ANCHOR_PTR& b ) -> bool
{ return a->Pos().x < b->Pos().x; } );
for( auto it = lower_it; it != anchor_set->end(); it++)
{
if( (*it)->Pos().x > aBBox.GetRight() )
break;
if( (*it)->Valid() && ( !aDirtyOnly || (*it)->IsDirty() )
&& aBBox.Contains( (*it)->Pos() ) )
aFunc( *it );
}
}
template <class T>
void CN_ZONE_LIST::FindNearbyZones( BOX2I aBBox, T aFunc, bool aDirtyOnly )
{
@ -689,47 +625,12 @@ void CN_ZONE_LIST::FindNearbyZones( BOX2I aBBox, T aFunc, bool aDirtyOnly )
}
}
template <class T>
void CN_LIST::FindNearby( VECTOR2I aPosition, int aDistMax, T aFunc, LSET aLayers, bool aDirtyOnly )
void CN_LIST::FindNearby( CN_ITEM *aItem, T aFunc )
{
/* Search items in m_Candidates that position is <= aDistMax from aPosition
* (Rectilinear distance)
* m_Candidates is sorted by X then Y values, so binary search is made for the first
* element. Then a linear iteration is made to identify all element that are also
* in the correct y range.
*/
sort();
CN_ANCHORS *anchor_set = &m_anchors;
PCB_LAYER_ID layer = aLayers.ExtractLayer();
if( layer > 0 )
anchor_set = &(m_layer_anchors[ layer ]);
if( (*anchor_set).size() == 0 )
return;
CN_ANCHOR_PTR lower = std::make_shared<CN_ANCHOR>
( aPosition - VECTOR2I( aDistMax, 0 ), (*anchor_set)[0]->Item() );
auto lower_it = std::lower_bound( anchor_set->begin(), anchor_set->end(), lower,
[]( const CN_ANCHOR_PTR& a, const CN_ANCHOR_PTR& b ) -> bool
{ return a->Pos().x < b->Pos().x; } );
for( auto it = lower_it; it != anchor_set->end(); it++ )
{
if( (*it)->Pos().x > aDistMax + aPosition.x )
break;
if( (*it)->Valid() && std::abs( (*it)->Pos().y - aPosition.y ) < aDistMax
&& ( !aDirtyOnly || (*it)->IsDirty() ) )
aFunc( *it );
}
m_index.Query( aItem->BBox(), aFunc );
}
class CN_CONNECTIVITY_ALGO
{
public:
@ -744,8 +645,6 @@ public:
private:
bool m_lastSearchWithZones = false;
class ITEM_MAP_ENTRY
{
public:
@ -776,10 +675,7 @@ public:
std::list<CN_ITEM*> m_items;
};
CN_PAD_LIST m_padList;
CN_TRACK_LIST m_trackList;
CN_VIA_LIST m_viaList;
CN_LIST m_itemList;
CN_ZONE_LIST m_zoneList;
using ITEM_MAP_PAIR = std::pair <const BOARD_CONNECTED_ITEM*, ITEM_MAP_ENTRY>;
@ -874,7 +770,7 @@ public:
const CLUSTERS& GetClusters();
int GetUnconnectedCount();
CN_PAD_LIST& PadList() { return m_padList; }
CN_LIST& ItemList() { return m_itemList; }
void ForEachAnchor( const std::function<void( CN_ANCHOR& )>& aFunc );
void ForEachItem( const std::function<void( CN_ITEM& )>& aFunc );
@ -886,4 +782,33 @@ public:
bool operator<( const CN_ANCHOR_PTR& a, const CN_ANCHOR_PTR& b );
/**
* Struct CN_VISTOR
**/
class CN_VISITOR {
public:
CN_VISITOR( CN_ITEM* aItem, std::mutex* aListLock ) :
m_item( aItem ),
m_listLock( aListLock )
{}
bool operator()( CN_ITEM* aCandidate );
protected:
void checkZoneItemConnection( CN_ZONE* aZone, CN_ITEM* aItem );
void checkZoneZoneConnection( CN_ZONE* aZoneA, CN_ZONE* aZoneB );
///> the item we are looking for connections to
CN_ITEM* m_item;
///> the mutex protecting our connection list
std::mutex* m_listLock;
};
#endif

View File

@ -475,9 +475,9 @@ unsigned int CONNECTIVITY_DATA::GetPadCount( int aNet ) const
{
int n = 0;
for( auto pad : m_connAlgo->PadList() )
for( auto pad : m_connAlgo->ItemList() )
{
if( !pad->Valid() )
if( !pad->Valid() || pad->Parent()->Type() != PCB_PAD_T)
continue;
auto dpad = static_cast<D_PAD*>( pad->Parent() );

108
pcbnew/connectivity_rtree.h Normal file
View File

@ -0,0 +1,108 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2018 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef PCBNEW_CONNECTIVITY_RTREE_H_
#define PCBNEW_CONNECTIVITY_RTREE_H_
#include <math/box2.h>
#include <geometry/rtree.h>
/**
* Class CN_RTREE -
* Implements an R-tree for fast spatial indexing of connectivity items.
* Non-owning.
*/
template< class T >
class CN_RTREE
{
public:
CN_RTREE()
{
this->m_tree = new RTree<T, int, 2, float>();
}
~CN_RTREE()
{
delete this->m_tree;
}
/**
* Function Insert()
* Inserts an item into the tree. Item's bounding box is taken via its BBox() method.
*/
void Insert( T aItem )
{
const BOX2I& bbox = aItem->BBox();
const int mmin[2] = { bbox.GetX(), bbox.GetY() };
const int mmax[2] = { bbox.GetRight(), bbox.GetBottom() };
m_tree->Insert( mmin, mmax, aItem );
}
/**
* Function Remove()
* Removes an item from the tree. Removal is done by comparing pointers, attempting
* to remove a copy of the item will fail.
*/
void Remove( T aItem )
{
const BOX2I& bbox = aItem->BBox();
const int mmin[2] = { bbox.GetX(), bbox.GetY() };
const int mmax[2] = { bbox.GetRight(), bbox.GetBottom() };
m_tree->Remove( mmin, mmax, aItem );
}
/**
* Function RemoveAll()
* Removes all items from the RTree
*/
void RemoveAll( )
{
m_tree->RemoveAll();
}
/**
* Function Query()
* Executes a function object aVisitor for each item whose bounding box intersects
* with aBounds.
*/
template <class Visitor>
void Query( const BOX2I& aBounds, Visitor& aVisitor )
{
const int mmin[2] = { aBounds.GetX(), aBounds.GetY() };
const int mmax[2] = { aBounds.GetRight(), aBounds.GetBottom() };
m_tree->Search( mmin, mmax, aVisitor );
}
private:
RTree<T, int, 2, float>* m_tree;
};
#endif /* PCBNEW_CONNECTIVITY_RTREE_H_ */