/* * This program source code file is part of KICAD, a free EDA CAD application. * * Copyright (C) 2016-2018 CERN * Copyright (C) 2019-2022, 2024 KiCad Developers, see AUTHORS.txt for contributors. * * @author Tomasz Wlostowski * * 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 */ #include #include #include #include #include int CN_ITEM::AnchorCount() const { if( !m_valid ) return 0; switch( m_parent->Type() ) { case PCB_TRACE_T: case PCB_ARC_T: return 2; // start and end case PCB_SHAPE_T: return m_anchors.size(); default: return 1; } } const VECTOR2I CN_ITEM::GetAnchor( int n ) const { if( !m_valid ) return VECTOR2I(); switch( m_parent->Type() ) { case PCB_PAD_T: return static_cast( m_parent )->GetPosition(); case PCB_TRACE_T: case PCB_ARC_T: if( n == 0 ) return static_cast( m_parent )->GetStart(); else return static_cast( m_parent )->GetEnd(); case PCB_VIA_T: return static_cast( m_parent )->GetStart(); case PCB_SHAPE_T: return ( n < static_cast( m_anchors.size() ) ) ? m_anchors[n]->Pos() : VECTOR2I(); default: UNIMPLEMENTED_FOR( m_parent->GetClass() ); return VECTOR2I(); } } void CN_ITEM::Dump() { wxLogDebug( " valid: %d, connected: \n", !!Valid() ); for( CN_ITEM* i : m_connected ) { PCB_TRACK* t = static_cast( i->Parent() ); wxLogDebug( wxT( " - %p %d\n" ), t, t->Type() ); } } int CN_ZONE_LAYER::AnchorCount() const { if( !Valid() ) return 0; const ZONE* zone = static_cast( Parent() ); return zone->GetFilledPolysList( m_layer )->COutline( m_subpolyIndex ).PointCount() ? 1 : 0; } const VECTOR2I CN_ZONE_LAYER::GetAnchor( int n ) const { if( !Valid() ) return VECTOR2I(); const ZONE* zone = static_cast( Parent() ); return zone->GetFilledPolysList( m_layer )->COutline( m_subpolyIndex ).CPoint( 0 ); } bool CN_ZONE_LAYER::HasSingleConnection() { int count = 0; for( CN_ITEM* item : ConnectedItems() ) { if( item->Valid() ) count++; if( count > 1 ) break; } return count == 1; } void CN_ITEM::RemoveInvalidRefs() { for( auto it = m_connected.begin(); it != m_connected.end(); /* increment in loop */ ) { if( !(*it)->Valid() ) it = m_connected.erase( it ); else ++it; } } CN_ITEM* CN_LIST::Add( PAD* pad ) { if( !pad->IsOnCopperLayer() ) return nullptr; auto item = new CN_ITEM( pad, false, 1 ); item->AddAnchor( pad->ShapePos() ); item->SetLayers( LAYER_RANGE( F_Cu, B_Cu ) ); switch( pad->GetAttribute() ) { case PAD_ATTRIB::SMD: case PAD_ATTRIB::NPTH: case PAD_ATTRIB::CONN: { LSET lmsk = pad->GetLayerSet(); for( int i = 0; i <= MAX_CU_LAYERS; i++ ) { if( lmsk[i] ) { item->SetLayer( i ); break; } } break; } default: break; } addItemtoTree( item ); m_items.push_back( item ); SetDirty(); return item; } CN_ITEM* CN_LIST::Add( PCB_TRACK* track ) { CN_ITEM* item = new CN_ITEM( track, true ); m_items.push_back( item ); item->AddAnchor( track->GetStart() ); item->AddAnchor( track->GetEnd() ); item->SetLayer( track->GetLayer() ); addItemtoTree( item ); SetDirty(); return item; } CN_ITEM* CN_LIST::Add( PCB_ARC* aArc ) { CN_ITEM* item = new CN_ITEM( aArc, true ); m_items.push_back( item ); item->AddAnchor( aArc->GetStart() ); item->AddAnchor( aArc->GetEnd() ); item->SetLayer( aArc->GetLayer() ); addItemtoTree( item ); SetDirty(); return item; } CN_ITEM* CN_LIST::Add( PCB_VIA* via ) { CN_ITEM* item = new CN_ITEM( via, !via->GetIsFree(), 1 ); m_items.push_back( item ); item->AddAnchor( via->GetStart() ); item->SetLayers( LAYER_RANGE( via->TopLayer(), via->BottomLayer() ) ); addItemtoTree( item ); SetDirty(); return item; } const std::vector CN_LIST::Add( ZONE* zone, PCB_LAYER_ID aLayer ) { const std::shared_ptr& polys = zone->GetFilledPolysList( aLayer ); std::vector rv; for( int j = 0; j < polys->OutlineCount(); j++ ) { CN_ZONE_LAYER* zitem = new CN_ZONE_LAYER( zone, aLayer, j ); zitem->BuildRTree(); for( const VECTOR2I& pt : zone->GetFilledPolysList( aLayer )->COutline( j ).CPoints() ) zitem->AddAnchor( pt ); rv.push_back( Add( zitem ) ); } return rv; } CN_ITEM* CN_LIST::Add( CN_ZONE_LAYER* zitem ) { m_items.push_back( zitem ); addItemtoTree( zitem ); SetDirty(); return zitem; } CN_ITEM* CN_LIST::Add( PCB_SHAPE* shape ) { CN_ITEM* item = new CN_ITEM( shape, true ); m_items.push_back( item ); for( const VECTOR2I& point : shape->GetConnectionPoints() ) item->AddAnchor( point ); item->SetLayer( shape->GetLayer() ); addItemtoTree( item ); SetDirty(); return item; } 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() ) { aGarbage.push_back ( item ); return true; } return false; } ); m_items.resize( lastItem - m_items.begin() ); for( CN_ITEM* item : aGarbage ) m_index.Remove( item ); m_hasInvalid = false; } BOARD_CONNECTED_ITEM* CN_ANCHOR::Parent() const { assert( m_item->Valid() ); return m_item->Parent(); } bool CN_ANCHOR::Valid() const { if( !m_item ) return false; return m_item->Valid(); } bool CN_ANCHOR::Dirty() const { return !Valid() || m_item->Dirty(); } bool CN_ANCHOR::IsDangling() const { int accuracy = 0; if( !m_cluster ) return true; // the minimal number of items connected to item_ref // at this anchor point to decide the anchor is *not* dangling size_t minimal_count = 1; size_t connected_count = m_item->ConnectedItems().size(); // a via can be removed if connected to only one other item. if( Parent()->Type() == PCB_VIA_T ) return connected_count < 2; if( m_item->AnchorCount() == 1 ) return connected_count < minimal_count; if( Parent()->Type() == PCB_TRACE_T || Parent()->Type() == PCB_ARC_T ) { accuracy = KiROUND( static_cast( Parent() )->GetWidth() / 2 ); } else if( Parent()->Type() == PCB_SHAPE_T ) { auto shape = static_cast( Parent() ); if( !shape->IsFilled() ) accuracy = KiROUND( shape->GetWidth() / 2 ); } // Items with multiple anchors have usually items connected to each anchor. // We want only the item count of this anchor point connected_count = 0; for( CN_ITEM* item : m_item->ConnectedItems() ) { if( item->Parent()->Type() == PCB_ZONE_T ) { ZONE* zone = static_cast( item->Parent() ); if( zone->HitTestFilledArea( ToLAYER_ID( item->Layer() ), Pos(), accuracy ) ) connected_count++; } else if( item->Parent()->HitTest( Pos(), accuracy ) ) { connected_count++; } } return connected_count < minimal_count; } int CN_ANCHOR::ConnectedItemsCount() const { if( !m_cluster ) return 0; int connected_count = 0; for( CN_ITEM* item : m_item->ConnectedItems() ) { if( item->Parent()->Type() == PCB_ZONE_T ) { ZONE* zone = static_cast( item->Parent() ); if( zone->HitTestFilledArea( ToLAYER_ID( item->Layer() ), Pos() ) ) connected_count++; } else if( item->Parent()->HitTest( Pos() ) ) { connected_count++; } } return connected_count; } CN_CLUSTER::CN_CLUSTER() { m_items.reserve( 64 ); m_originPad = nullptr; m_originNet = -1; m_conflicting = false; } CN_CLUSTER::~CN_CLUSTER() { } wxString CN_CLUSTER::OriginNetName() const { if( !m_originPad || !m_originPad->Valid() ) return ""; else return m_originPad->Parent()->GetNetname(); } bool CN_CLUSTER::Contains( const CN_ITEM* aItem ) { return alg::contains( m_items, aItem ); } bool CN_CLUSTER::Contains( const BOARD_CONNECTED_ITEM* aItem ) { return std::find_if( m_items.begin(), m_items.end(), [&aItem]( const CN_ITEM* item ) { return item->Valid() && item->Parent() == aItem; } ) != m_items.end(); } void CN_CLUSTER::Dump() { for( CN_ITEM* item : m_items ) { wxLogTrace( wxT( "CN" ), wxT( " - item : %p bitem : %p type : %d inet %s\n" ), item, item->Parent(), item->Parent()->Type(), (const char*) item->Parent()->GetNetname().c_str() ); wxLogTrace( wxT( "CN" ), wxT( "- item : %p bitem : %p type : %d inet %s\n" ), item, item->Parent(), item->Parent()->Type(), (const char*) item->Parent()->GetNetname().c_str() ); item->Dump(); } } void CN_CLUSTER::Add( CN_ITEM* item ) { m_items.push_back( item ); int netCode = item->Net(); if( netCode <= 0 ) return; if( m_originNet <= 0 ) { m_originNet = netCode; m_netRanks[m_originNet] = 0; } if( item->Parent()->Type() == PCB_PAD_T && !static_cast( item->Parent() )->IsFreePad() ) { int rank; auto it = m_netRanks.find( netCode ); if( it == m_netRanks.end() ) { m_netRanks[netCode] = 1; rank = 1; } else { it->second++; rank = it->second; } if( !m_originPad || rank > m_netRanks[m_originNet] ) { m_originPad = item; m_originNet = netCode; } if( m_originPad && item->Net() != m_originNet ) m_conflicting = true; } }