KiCad PCB EDA Suite
Loading...
Searching...
No Matches
connectivity_items.cpp
Go to the documentation of this file.
1
2/*
3 * This program source code file is part of KICAD, a free EDA CAD application.
4 *
5 * Copyright (C) 2016-2018 CERN
6 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
7 *
8 * @author Tomasz Wlostowski <[email protected]>
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program. If not, see <https://www.gnu.org/licenses/>.
22 */
23
24#include <core/kicad_algo.h>
25#include <macros.h>
27#include <trigo.h>
28
29#include <footprint.h>
30#include <pad.h>
31#include <pcb_shape.h>
32#include <pcb_track.h>
33
34#include <wx/log.h>
35
37{
38 if( !m_valid )
39 return 0;
40
41 switch( m_parent->Type() )
42 {
43 case PCB_TRACE_T:
44 case PCB_ARC_T:
45 return 2; // start and end
46
47 case PCB_SHAPE_T:
48 return m_anchors.size();
49
50 default:
51 return 1;
52 }
53}
54
55
56const VECTOR2I CN_ITEM::GetAnchor( int n ) const
57{
58 if( !m_valid )
59 return VECTOR2I();
60
61 switch( m_parent->Type() )
62 {
63 case PCB_PAD_T:
64 return static_cast<PAD*>( m_parent )->GetPosition();
65
66 case PCB_TRACE_T:
67 case PCB_ARC_T:
68 if( n == 0 )
69 return static_cast<const PCB_TRACK*>( m_parent )->GetStart();
70 else
71 return static_cast<const PCB_TRACK*>( m_parent )->GetEnd();
72
73 case PCB_VIA_T:
74 return static_cast<const PCB_VIA*>( m_parent )->GetStart();
75
76 case PCB_SHAPE_T:
77 return ( n < static_cast<int>( m_anchors.size() ) ) ? m_anchors[n]->Pos() : VECTOR2I();
78
79 default:
80 UNIMPLEMENTED_FOR( m_parent->GetClass() );
81 return VECTOR2I();
82 }
83}
84
85
87{
88 wxLogTrace( wxT( "CN" ), wxT( " valid: %d, connected: \n" ), !!Valid() );
89
90 for( CN_ITEM* i : m_connected )
91 {
92 PCB_TRACK* t = static_cast<PCB_TRACK*>( i->Parent() );
93 wxLogTrace( wxT( "CN" ), wxT( " - %p %d\n" ), t, t->Type() );
94 }
95}
96
97
99{
100 if( !Valid() || !HasValidOutline() )
101 return 0;
102
103 return GetOutline().PointCount() ? 1 : 0;
104}
105
106
108{
109 if( !Valid() || !HasValidOutline() )
110 return VECTOR2I();
111
112 return GetOutline().CPoint( 0 );
113}
114
115
117{
118 int count = 0;
119
120 for( CN_ITEM* item : ConnectedItems() )
121 {
122 if( item->Valid() )
123 count++;
124
125 if( count > 1 )
126 break;
127 }
128
129 return count == 1;
130}
131
132
134{
135 std::erase_if( m_connected, []( const CN_ITEM* item ) { return !item->Valid(); } );
136}
137
138
140{
141 if( !pad->IsOnCopperLayer() )
142 return nullptr;
143
144 auto item = new CN_ITEM( pad, false, 1 );
145
146 std::set<VECTOR2I> uniqueAnchors;
147 pad->Padstack().ForEachUniqueLayer(
148 [&]( PCB_LAYER_ID aLayer )
149 {
150 uniqueAnchors.insert( pad->ShapePos( aLayer ) );
151 } );
152
153 for( const VECTOR2I& anchor : uniqueAnchors )
154 item->AddAnchor( anchor );
155
156 item->SetLayers( F_Cu, B_Cu );
157
158 switch( pad->GetAttribute() )
159 {
160 case PAD_ATTRIB::SMD:
161 case PAD_ATTRIB::NPTH:
162 case PAD_ATTRIB::CONN:
163 {
164 LSEQ lmsk = pad->GetLayerSet().CuStack();
165
166 if( !lmsk.empty() )
167 item->SetLayer( lmsk.front() );
168
169 break;
170 }
171
172 default:
173 break;
174 }
175
176 addItemtoTree( item );
177 appendItem( item );
178
179 // Re-mark dirty after tree insertion since BBox() clears the dirty flag
180 item->SetDirty( true );
181 SetDirty();
182 return item;
183}
184
185
187{
188 CN_ITEM* item = new CN_ITEM( track, true );
189 appendItem( item );
190 item->AddAnchor( track->GetStart() );
191 item->AddAnchor( track->GetEnd() );
192 item->SetLayer( track->GetLayer() );
193 addItemtoTree( item );
194
195 // Re-mark dirty after tree insertion since BBox() clears the dirty flag
196 item->SetDirty( true );
197 SetDirty();
198 return item;
199}
200
201
203{
204 CN_ITEM* item = new CN_ITEM( aArc, true );
205 appendItem( item );
206 item->AddAnchor( aArc->GetStart() );
207 item->AddAnchor( aArc->GetEnd() );
208 item->SetLayer( aArc->GetLayer() );
209 addItemtoTree( item );
210
211 // Re-mark dirty after tree insertion since BBox() clears the dirty flag
212 item->SetDirty( true );
213 SetDirty();
214 return item;
215}
216
217
219{
220 CN_ITEM* item = new CN_ITEM( via, !via->GetIsFree(), 1 );
221
222 appendItem( item );
223 item->AddAnchor( via->GetStart() );
224
225 item->SetLayers( via->TopLayer(), via->BottomLayer() );
226 addItemtoTree( item );
227
228 // Re-mark dirty after tree insertion since BBox() clears the dirty flag
229 item->SetDirty( true );
230 SetDirty();
231 return item;
232}
233
234
235const std::vector<CN_ITEM*> CN_LIST::Add( ZONE* zone, PCB_LAYER_ID aLayer )
236{
237 const std::shared_ptr<SHAPE_POLY_SET>& polys = zone->GetFilledPolysList( aLayer );
238
239 std::vector<CN_ITEM*> rv;
240
241 for( int j = 0; j < polys->OutlineCount(); j++ )
242 {
243 CN_ZONE_LAYER* zitem = new CN_ZONE_LAYER( zone, aLayer, j );
244
245 zitem->BuildRTree();
246
247 for( const VECTOR2I& pt : zone->GetFilledPolysList( aLayer )->COutline( j ).CPoints() )
248 zitem->AddAnchor( pt );
249
250 rv.push_back( Add( zitem ) );
251 }
252
253 return rv;
254}
255
256
258{
259 appendItem( zitem );
260 addItemtoTree( zitem );
261
262 // Re-mark dirty after tree insertion since BBox() clears the dirty flag
263 zitem->SetDirty( true );
264 SetDirty();
265 return zitem;
266}
267
268
270{
271 CN_ITEM* item = new CN_ITEM( shape, true );
272 appendItem( item );
273
274 for( const VECTOR2I& point : shape->GetConnectionPoints() )
275 item->AddAnchor( point );
276
277 item->SetLayer( shape->GetLayer() );
278 addItemtoTree( item );
279
280 // Re-mark dirty after tree insertion since BBox() clears the dirty flag
281 item->SetDirty( true );
282 SetDirty();
283 return item;
284}
285
286
287void CN_LIST::RemoveInvalidItems( std::vector<CN_ITEM*>& aGarbage )
288{
289 if( !m_hasInvalid )
290 return;
291
292 auto lastItem = std::remove_if( m_items.begin(), m_items.end(),
293 [&aGarbage]( CN_ITEM* item )
294 {
295 if( !item->Valid() )
296 {
297 aGarbage.push_back ( item );
298 return true;
299 }
300
301 return false;
302 } );
303
304 m_items.resize( lastItem - m_items.begin() );
305
306 // Compaction shifted everything past the first hole, so restamp before anything reads
307 for( size_t ii = 0; ii < m_items.size(); ++ii )
308 m_items[ii]->SetListIndex( static_cast<int>( ii ) );
309
310 for( CN_ITEM* item : aGarbage )
311 m_index.Remove( item );
312
313 m_hasInvalid = false;
314}
315
316
318{
319 assert( m_item->Valid() );
320 return m_item->Parent();
321}
322
323
325{
326 if( !m_item )
327 return false;
328
329 return m_item->Valid();
330}
331
332
334{
335 return !Valid() || m_item->Dirty();
336}
337
338
340{
341 int accuracy = 0;
342
343 if( !m_cluster )
344 return true;
345
346 // the minimal number of items connected to item_ref
347 // at this anchor point to decide the anchor is *not* dangling
348 size_t minimal_count = 1;
349 size_t connected_count = m_item->ConnectedItems().size();
350
351 // a via can be removed if connected to only one other item.
352 if( Parent()->Type() == PCB_VIA_T )
353 return connected_count < 2;
354
355 if( m_item->AnchorCount() == 1 )
356 return connected_count < minimal_count;
357
358 if( Parent()->Type() == PCB_TRACE_T || Parent()->Type() == PCB_ARC_T )
359 accuracy = KiROUND( static_cast<const PCB_TRACK*>( Parent() )->GetWidth() / 2.0 );
360 else if( Parent()->Type() == PCB_SHAPE_T )
361 accuracy = KiROUND( static_cast<const PCB_SHAPE*>( Parent() )->GetWidth() / 2.0 );
362
363 // Items with multiple anchors have usually items connected to each anchor.
364 // We want only the item count of this anchor point
365 connected_count = 0;
366
367 for( CN_ITEM* item : m_item->ConnectedItems() )
368 {
369 if( item->Parent()->Type() == PCB_ZONE_T )
370 {
371 ZONE* zone = static_cast<ZONE*>( item->Parent() );
372
373 if( zone->HitTestFilledArea( item->GetBoardLayer(), Pos(), accuracy ) )
374 connected_count++;
375 }
376 else if( item->Parent()->HitTest( Pos(), accuracy ) )
377 {
378 connected_count++;
379 }
380 }
381
382 return connected_count < minimal_count;
383}
384
385
387{
388 if( !m_cluster )
389 return 0;
390
391 int connected_count = 0;
392
393 for( CN_ITEM* item : m_item->ConnectedItems() )
394 {
395 if( item->Parent()->Type() == PCB_ZONE_T )
396 {
397 ZONE* zone = static_cast<ZONE*>( item->Parent() );
398
399 if( zone->HitTestFilledArea( item->GetBoardLayer(), Pos() ) )
400 connected_count++;
401 }
402 else if( item->Parent()->HitTest( Pos() ) )
403 {
404 connected_count++;
405 }
406 }
407
408 return connected_count;
409}
410
411
413{
414 m_items.reserve( 64 );
415 m_originPad = nullptr;
416 m_originNet = -1;
417 m_conflicting = false;
418}
419
420
424
425
427{
428 if( !m_originPad || !m_originPad->Valid() )
429 return "<none>";
430 else
431 return m_originPad->Parent()->GetNetname();
432}
433
434
435bool CN_CLUSTER::Contains( const CN_ITEM* aItem )
436{
437 return alg::contains( m_items, aItem );
438}
439
440
442{
443 return std::find_if( m_items.begin(), m_items.end(),
444 [&aItem]( const CN_ITEM* item )
445 {
446 return item->Valid() && item->Parent() == aItem;
447 } ) != m_items.end();
448}
449
450
452{
453 for( CN_ITEM* item : m_items )
454 {
455 wxLogTrace( wxT( "CN" ), wxT( " - item : %p bitem : %p type : %d inet %s\n" ),
456 item,
457 item->Parent(),
458 item->Parent()->Type(),
459 (const char*) item->Parent()->GetNetname().c_str() );
460 wxLogTrace( wxT( "CN" ), wxT( "- item : %p bitem : %p type : %d inet %s\n" ),
461 item,
462 item->Parent(),
463 item->Parent()->Type(),
464 (const char*) item->Parent()->GetNetname().c_str() );
465 item->Dump();
466 }
467}
468
469
471{
472 m_items.push_back( item );
473
474 int netCode = item->Net();
475
476 if( netCode <= 0 )
477 return;
478
479 if( m_originNet <= 0 )
480 {
481 m_originNet = netCode;
483 }
484
485 if( item->Parent()->Type() == PCB_PAD_T && !static_cast<PAD*>( item->Parent() )->IsFreePad() )
486 {
487 int rank;
488 auto it = m_netRanks.find( netCode );
489
490 if( it == m_netRanks.end() )
491 {
492 m_netRanks[netCode] = 1;
493 rank = 1;
494 }
495 else
496 {
497 it->second++;
498 rank = it->second;
499 }
500
501 if( !m_originPad || rank > m_netRanks[m_originNet] )
502 {
503 m_originPad = item;
504 m_originNet = netCode;
505 }
506
507 if( m_originPad && item->Net() != m_originNet )
508 m_conflicting = true;
509 }
510}
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:982
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
CN_ITEM * m_item
Pad or track/arc/via owning the anchor.
bool Valid() const
int ConnectedItemsCount() const
std::shared_ptr< CN_CLUSTER > m_cluster
Cluster to which the anchor belongs.
const VECTOR2I & Pos() const
bool Dirty() const
BOARD_CONNECTED_ITEM * Parent() const
bool IsDangling() const
The anchor point is dangling if the parent is a track and this anchor point is not connected to anoth...
std::vector< CN_ITEM * > m_items
bool Contains(const CN_ITEM *aItem)
void Add(CN_ITEM *item)
CN_ITEM * m_originPad
wxString OriginNetName() const
std::unordered_map< int, int > m_netRanks
CN_ITEM represents a BOARD_CONNECTED_ITEM in the connectivity system (ie: a pad, track/arc/via,...
virtual int AnchorCount() const
void RemoveInvalidRefs()
std::vector< CN_ITEM * > m_connected
list of physically touching items
const std::vector< CN_ITEM * > & ConnectedItems() const
int Net() const
BOARD_CONNECTED_ITEM * m_parent
bool Valid() const
std::shared_ptr< CN_ANCHOR > AddAnchor(const VECTOR2I &aPos)
void SetLayers(int aStartLayer, int aEndLayer)
Set the layers spanned by the item to aStartLayer and aEndLayer.
virtual const VECTOR2I GetAnchor(int n) const
void SetDirty(bool aDirty)
CN_ITEM(BOARD_CONNECTED_ITEM *aParent, bool aCanChangeNet, int aAnchorCount=2)
void SetLayer(int aLayer)
Set the layers spanned by the item to a single layer aLayer.
std::vector< std::shared_ptr< CN_ANCHOR > > m_anchors
bool m_valid
used to identify garbage items (we use lazy removal)
BOARD_CONNECTED_ITEM * Parent() const
void appendItem(CN_ITEM *aItem)
The sole place CN_ITEM list numbering is assigned.
CN_ITEM * Add(PAD *pad)
void SetDirty(bool aDirty=true)
std::vector< CN_ITEM * > m_items
void addItemtoTree(CN_ITEM *item)
void RemoveInvalidItems(std::vector< CN_ITEM * > &aGarbage)
Represents a single outline of a zone fill on a particular layer.
virtual int AnchorCount() const override
const SHAPE_LINE_CHAIN & GetOutline() const
virtual const VECTOR2I GetAnchor(int n) const override
bool HasValidOutline() const
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
LSEQ is a sequence (and therefore also a set) of PCB_LAYER_IDs.
Definition lseq.h:47
Definition pad.h:61
bool IsFreePad() const
Definition pad.cpp:610
std::vector< VECTOR2I > GetConnectionPoints() const
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition pcb_shape.h:68
const VECTOR2I & GetStart() const
Definition pcb_track.h:98
const VECTOR2I & GetEnd() const
Definition pcb_track.h:95
int PointCount() const
Return the number of points (vertices) in this line chain.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
const std::vector< VECTOR2I > & CPoints() const
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
Handle a list of polygons defining a copper zone.
Definition zone.h:70
std::shared_ptr< SHAPE_POLY_SET > GetFilledPolysList(PCB_LAYER_ID aLayer) const
Definition zone.h:699
bool HitTestFilledArea(PCB_LAYER_ID aLayer, const VECTOR2I &aRefPos, int aAccuracy=0) const
Test if the given VECTOR2I is within the bounds of a filled area of this zone.
Definition zone.cpp:1068
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ B_Cu
Definition layer_ids.h:61
@ F_Cu
Definition layer_ids.h:60
This file contains miscellaneous commonly used macros and functions.
#define UNIMPLEMENTED_FOR(type)
Definition macros.h:92
bool contains(const _Container &__container, _Value __value)
Returns true if the container contains the given value.
Definition kicad_algo.h:96
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:102
@ SMD
Smd pad, appears on the solder paste layer (default)
Definition padstack.h:98
@ CONN
Like smd, does not appear on the solder paste layer (default) Note: also has a special attribute in G...
Definition padstack.h:99
const int accuracy
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:80
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:89
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition typeinfo.h:100
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:79
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:90
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:88
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708