You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							445 lines
						
					
					
						
							11 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							445 lines
						
					
					
						
							11 KiB
						
					
					
				| 
 | |
| /* | |
|  * This program source code file is part of KICAD, a free EDA CAD application. | |
|  * | |
|  * Copyright (C) 2016-2018 CERN | |
|  * Copyright (C) 2019-2022 KiCad Developers, see AUTHORS.txt for contributors. | |
|  * | |
|  * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch> | |
|  * | |
|  * 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 <core/kicad_algo.h> | |
| #include <macros.h> | |
| #include <connectivity/connectivity_items.h> | |
| #include <trigo.h> | |
|  | |
| #include <wx/log.h> | |
|  | |
| 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 | |
|     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<PAD*>( m_parent )->GetPosition(); | |
| 
 | |
|     case PCB_TRACE_T: | |
|     case PCB_ARC_T: | |
|         if( n == 0 ) | |
|             return static_cast<const PCB_TRACK*>( m_parent )->GetStart(); | |
|         else | |
|             return static_cast<const PCB_TRACK*>( m_parent )->GetEnd(); | |
| 
 | |
|     case PCB_VIA_T: | |
|         return static_cast<const PCB_VIA*>( m_parent )->GetStart(); | |
| 
 | |
|     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<PCB_TRACK*>( 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<const ZONE*>( 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<const ZONE*>( Parent() ); | |
| 
 | |
|     return zone->GetFilledPolysList( m_layer )->COutline( m_subpolyIndex ).CPoint( 0 ); | |
| } | |
| 
 | |
| 
 | |
| 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_ITEM*> CN_LIST::Add( ZONE* zone, PCB_LAYER_ID aLayer ) | |
| { | |
|     const std::shared_ptr<SHAPE_POLY_SET>& polys = zone->GetFilledPolysList( aLayer ); | |
| 
 | |
|     std::vector<CN_ITEM*> rv; | |
| 
 | |
|     for( int j = 0; j < polys->OutlineCount(); j++ ) | |
|     { | |
|         CN_ZONE_LAYER* zitem = new CN_ZONE_LAYER( zone, aLayer, j ); | |
| 
 | |
|         zitem->BuildRTree(); | |
| 
 | |
|         for( 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; | |
| } | |
| 
 | |
| 
 | |
| 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() ) | |
|                                         { | |
|                                             aGarbage.push_back ( item ); | |
|                                             return true; | |
|                                         } | |
| 
 | |
|                                         return false; | |
|                                     } ); | |
| 
 | |
|     m_items.resize( lastItem - m_items.begin() ); | |
| 
 | |
|     for( CN_ITEM* item : m_items ) | |
|         item->RemoveInvalidRefs(); | |
| 
 | |
|     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::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<const PCB_TRACK*>( Parent() )->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<ZONE*>( 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<ZONE*>( 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 "<none>"; | |
|     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( auto 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; | |
| 
 | |
|     if( item->Parent()->Type() == PCB_PAD_T ) | |
|     { | |
|         if( m_netRanks.count( netCode ) ) | |
|         { | |
|             m_netRanks[netCode]++; | |
| 
 | |
|             if( m_netRanks.count( m_originNet ) && m_netRanks[netCode] > m_netRanks[m_originNet] ) | |
|             { | |
|                 m_originPad = item; | |
|                 m_originNet = netCode; | |
|             } | |
|         } | |
|         else | |
|         { | |
|             m_netRanks[netCode] = 1; | |
| 
 | |
|             if( !m_originPad ) | |
|             { | |
|                 m_originPad = item; | |
|                 m_originNet = netCode; | |
|             } | |
|         } | |
| 
 | |
|         if( m_originPad && item->Net() != m_originNet ) | |
|             m_conflicting = true; | |
|     } | |
| }
 |