From 63a952d239d41ba70f73d9d572297d85a0b8abc6 Mon Sep 17 00:00:00 2001 From: Seth Hillbrand Date: Sun, 20 May 2018 17:06:01 -0700 Subject: [PATCH] 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 --- pcbnew/class_board.cpp | 4 +- pcbnew/connectivity_algo.cpp | 409 ++++++++++++++--------------------- pcbnew/connectivity_algo.h | 215 ++++++------------ pcbnew/connectivity_data.cpp | 4 +- pcbnew/connectivity_rtree.h | 108 +++++++++ 5 files changed, 340 insertions(+), 400 deletions(-) create mode 100644 pcbnew/connectivity_rtree.h diff --git a/pcbnew/class_board.cpp b/pcbnew/class_board.cpp index ab7b9a7bc9..5dd60414a6 100644 --- a/pcbnew/class_board.cpp +++ b/pcbnew/class_board.cpp @@ -2727,9 +2727,9 @@ void BOARD::ReplaceNetlist( NETLIST& aNetlist, bool aDeleteSinglePadNets, { std::vector 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(); diff --git a/pcbnew/connectivity_algo.cpp b/pcbnew/connectivity_algo.cpp index c9c4f6b21c..9d110ddbfc 100644 --- a/pcbnew/connectivity_algo.cpp +++ b/pcbnew/connectivity_algo.cpp @@ -24,6 +24,7 @@ #include #include +#include #include #include @@ -168,25 +169,25 @@ bool CN_CONNECTIVITY_ALGO::Remove( BOARD_ITEM* aItem ) m_itemMap.erase( static_cast( pad ) ); } - m_padList.SetDirty( true ); + m_itemList.SetDirty( true ); break; case PCB_PAD_T: m_itemMap[ static_cast( aItem ) ].MarkItemsAsInvalid(); m_itemMap.erase( static_cast( aItem ) ); - m_padList.SetDirty( true ); + m_itemList.SetDirty( true ); break; case PCB_TRACE_T: m_itemMap[ static_cast( aItem ) ].MarkItemsAsInvalid(); m_itemMap.erase( static_cast( aItem ) ); - m_trackList.SetDirty( true ); + m_itemList.SetDirty( true ); break; case PCB_VIA_T: m_itemMap[ static_cast( aItem ) ].MarkItemsAsInvalid(); m_itemMap.erase( static_cast( 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( aItem ) ) != m_itemMap.end() ) return false; - add( m_padList, static_cast( aItem ) ); + add( m_itemList, static_cast( aItem ) ); break; @@ -261,7 +266,7 @@ bool CN_CONNECTIVITY_ALGO::Add( BOARD_ITEM* aItem ) if( m_itemMap.find( static_cast( aItem ) ) != m_itemMap.end() ) return false; - add( m_trackList, static_cast( aItem ) ); + add( m_itemList, static_cast( aItem ) ); break; } @@ -270,7 +275,7 @@ bool CN_CONNECTIVITY_ALGO::Add( BOARD_ITEM* aItem ) if( m_itemMap.find( static_cast( aItem ) ) != m_itemMap.end() ) return false; - add( m_viaList, static_cast( aItem ) ); + add( m_itemList, static_cast( 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 lock( cnListLock ); - CN_ITEM::Connect( aRefItem, point->Item() ); - } - - break; - - case PCB_TRACE_T: - { - const auto track = static_cast ( 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 lock( cnListLock ); - CN_ITEM::Connect( aRefItem, point->Item() ); - } - break; - } - - case PCB_ZONE_T: - case PCB_ZONE_AREA_T: - { - const auto zone = static_cast ( parent ); - auto zoneItem = static_cast ( aRefItem ); - - if( point->Item()->Net() != parent->GetNetCode() ) - return; - - if( !( zone->GetLayerSet() & - point->Item()->Parent()->GetLayerSet() ).any() ) - return; - - if( zoneItem->ContainsAnchor( point ) ) - { - std::lock_guard 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( 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 lock( cnListLock ); - - CN_ITEM::Connect( aRefZone, testedZone ); - return; - } - } - - const auto testedZoneParent = static_cast( 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 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 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 ( 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 ( 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 ( 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 (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 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& 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& 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 ( 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 lock( *m_listLock ); + CN_ITEM::Connect( zoneItem, aItem ); + } +} + +void CN_VISITOR::checkZoneZoneConnection( CN_ZONE* aZoneA, CN_ZONE* aZoneB ) +{ + const auto refParent = static_cast( aZoneA->Parent() ); + const auto testedParent = static_cast( 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 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 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( m_item ), + static_cast( aCandidate ) ); + return true; + } + + if( parentA->Type() == PCB_ZONE_AREA_T || parentA->Type() == PCB_ZONE_T) + { + checkZoneItemConnection( static_cast( aCandidate ), m_item ); + return true; + } + + if( parentB->Type() == PCB_ZONE_AREA_T || parentB->Type() == PCB_ZONE_T) + { + checkZoneItemConnection( static_cast( 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 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& 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& a void CN_CONNECTIVITY_ALGO::ForEachAnchor( const std::function& 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 ); + } + ); } diff --git a/pcbnew/connectivity_algo.h b/pcbnew/connectivity_algo.h index a36af5fbf6..ea79b9fea2 100644 --- a/pcbnew/connectivity_algo.h +++ b/pcbnew/connectivity_algo.h @@ -43,6 +43,7 @@ #include #include +#include #include 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( aPos, this ) ); - //printf("%p add %d\n", this, m_anchors.size() ); - return m_anchors.back(); + m_anchors.emplace_back( std::make_unique( 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 m_anchors; - CN_ANCHORS m_layer_anchors[PCB_LAYER_ID_COUNT]; + bool m_hasInvalid; + + CN_RTREE m_index; protected: std::vector 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& Anchors() { return m_anchors; } - template - void FindNearby( VECTOR2I aPosition, int aDistMax, T aFunc, LSET aLayers = LSET::AllLayersMask(), bool aDirtyOnly = false ); + void FindNearby( CN_ITEM *aItem, T aFunc ); - template - 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& 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 -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 - ( 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 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 -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 - ( 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 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 ; @@ -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& aFunc ); void ForEachItem( const std::function& 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 diff --git a/pcbnew/connectivity_data.cpp b/pcbnew/connectivity_data.cpp index b5a4f7aa6d..cae4645b7c 100644 --- a/pcbnew/connectivity_data.cpp +++ b/pcbnew/connectivity_data.cpp @@ -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( pad->Parent() ); diff --git a/pcbnew/connectivity_rtree.h b/pcbnew/connectivity_rtree.h new file mode 100644 index 0000000000..cdf8faab09 --- /dev/null +++ b/pcbnew/connectivity_rtree.h @@ -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 + +#include + + +/** + * 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(); + } + + ~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 + 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* m_tree; +}; + + +#endif /* PCBNEW_CONNECTIVITY_RTREE_H_ */