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_ */