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, you may find one here:
22 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
23 * or you may search the http://www.gnu.org website for the version 2 license,
24 * or you may write to the Free Software Foundation, Inc.,
25 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
26 */
27
28#include <core/kicad_algo.h>
29#include <macros.h>
31#include <trigo.h>
32
33#include <wx/log.h>
34
36{
37 if( !m_valid )
38 return 0;
39
40 switch( m_parent->Type() )
41 {
42 case PCB_TRACE_T:
43 case PCB_ARC_T:
44 return 2; // start and end
45
46 case PCB_SHAPE_T:
47 return m_anchors.size();
48
49 default:
50 return 1;
51 }
52}
53
54
55const VECTOR2I CN_ITEM::GetAnchor( int n ) const
56{
57 if( !m_valid )
58 return VECTOR2I();
59
60 switch( m_parent->Type() )
61 {
62 case PCB_PAD_T:
63 return static_cast<PAD*>( m_parent )->GetPosition();
64
65 case PCB_TRACE_T:
66 case PCB_ARC_T:
67 if( n == 0 )
68 return static_cast<const PCB_TRACK*>( m_parent )->GetStart();
69 else
70 return static_cast<const PCB_TRACK*>( m_parent )->GetEnd();
71
72 case PCB_VIA_T:
73 return static_cast<const PCB_VIA*>( m_parent )->GetStart();
74
75 case PCB_SHAPE_T:
76 return ( n < static_cast<int>( m_anchors.size() ) ) ? m_anchors[n]->Pos() : VECTOR2I();
77
78 default:
79 UNIMPLEMENTED_FOR( m_parent->GetClass() );
80 return VECTOR2I();
81 }
82}
83
84
86{
87 wxLogTrace( wxT( "CN" ), wxT( " valid: %d, connected: \n" ), !!Valid() );
88
89 for( CN_ITEM* i : m_connected )
90 {
91 PCB_TRACK* t = static_cast<PCB_TRACK*>( i->Parent() );
92 wxLogTrace( wxT( "CN" ), wxT( " - %p %d\n" ), t, t->Type() );
93 }
94}
95
96
98{
99 if( !Valid() || !HasValidOutline() )
100 return 0;
101
102 return GetOutline().PointCount() ? 1 : 0;
103}
104
105
107{
108 if( !Valid() || !HasValidOutline() )
109 return VECTOR2I();
110
111 return GetOutline().CPoint( 0 );
112}
113
114
116{
117 int count = 0;
118
119 for( CN_ITEM* item : ConnectedItems() )
120 {
121 if( item->Valid() )
122 count++;
123
124 if( count > 1 )
125 break;
126 }
127
128 return count == 1;
129}
130
131
133{
134 for( auto it = m_connected.begin(); it != m_connected.end(); /* increment in loop */ )
135 {
136 if( !(*it)->Valid() )
137 it = m_connected.erase( it );
138 else
139 ++it;
140 }
141}
142
143
145{
146 if( !pad->IsOnCopperLayer() )
147 return nullptr;
148
149 auto item = new CN_ITEM( pad, false, 1 );
150
151 std::set<VECTOR2I> uniqueAnchors;
152 pad->Padstack().ForEachUniqueLayer(
153 [&]( PCB_LAYER_ID aLayer )
154 {
155 uniqueAnchors.insert( pad->ShapePos( aLayer ) );
156 } );
157
158 for( const VECTOR2I& anchor : uniqueAnchors )
159 item->AddAnchor( anchor );
160
161 item->SetLayers( F_Cu, B_Cu );
162
163 switch( pad->GetAttribute() )
164 {
165 case PAD_ATTRIB::SMD:
166 case PAD_ATTRIB::NPTH:
167 case PAD_ATTRIB::CONN:
168 {
169 LSEQ lmsk = pad->GetLayerSet().CuStack();
170
171 if( !lmsk.empty() )
172 item->SetLayer( lmsk.front() );
173
174 break;
175 }
176
177 default:
178 break;
179 }
180
181 addItemtoTree( item );
182 m_items.push_back( item );
183
184 // Re-mark dirty after tree insertion since BBox() clears the dirty flag
185 item->SetDirty( true );
186 SetDirty();
187 return item;
188}
189
190
192{
193 CN_ITEM* item = new CN_ITEM( track, true );
194 m_items.push_back( item );
195 item->AddAnchor( track->GetStart() );
196 item->AddAnchor( track->GetEnd() );
197 item->SetLayer( track->GetLayer() );
198 addItemtoTree( item );
199
200 // Re-mark dirty after tree insertion since BBox() clears the dirty flag
201 item->SetDirty( true );
202 SetDirty();
203 return item;
204}
205
206
208{
209 CN_ITEM* item = new CN_ITEM( aArc, true );
210 m_items.push_back( item );
211 item->AddAnchor( aArc->GetStart() );
212 item->AddAnchor( aArc->GetEnd() );
213 item->SetLayer( aArc->GetLayer() );
214 addItemtoTree( item );
215
216 // Re-mark dirty after tree insertion since BBox() clears the dirty flag
217 item->SetDirty( true );
218 SetDirty();
219 return item;
220}
221
222
224{
225 CN_ITEM* item = new CN_ITEM( via, !via->GetIsFree(), 1 );
226
227 m_items.push_back( item );
228 item->AddAnchor( via->GetStart() );
229
230 item->SetLayers( via->TopLayer(), via->BottomLayer() );
231 addItemtoTree( item );
232
233 // Re-mark dirty after tree insertion since BBox() clears the dirty flag
234 item->SetDirty( true );
235 SetDirty();
236 return item;
237}
238
239
240const std::vector<CN_ITEM*> CN_LIST::Add( ZONE* zone, PCB_LAYER_ID aLayer )
241{
242 const std::shared_ptr<SHAPE_POLY_SET>& polys = zone->GetFilledPolysList( aLayer );
243
244 std::vector<CN_ITEM*> rv;
245
246 for( int j = 0; j < polys->OutlineCount(); j++ )
247 {
248 CN_ZONE_LAYER* zitem = new CN_ZONE_LAYER( zone, aLayer, j );
249
250 zitem->BuildRTree();
251
252 for( const VECTOR2I& pt : zone->GetFilledPolysList( aLayer )->COutline( j ).CPoints() )
253 zitem->AddAnchor( pt );
254
255 rv.push_back( Add( zitem ) );
256 }
257
258 return rv;
259}
260
261
263{
264 m_items.push_back( zitem );
265 addItemtoTree( zitem );
266
267 // Re-mark dirty after tree insertion since BBox() clears the dirty flag
268 zitem->SetDirty( true );
269 SetDirty();
270 return zitem;
271}
272
273
275{
276 CN_ITEM* item = new CN_ITEM( shape, true );
277 m_items.push_back( item );
278
279 for( const VECTOR2I& point : shape->GetConnectionPoints() )
280 item->AddAnchor( point );
281
282 item->SetLayer( shape->GetLayer() );
283 addItemtoTree( item );
284
285 // Re-mark dirty after tree insertion since BBox() clears the dirty flag
286 item->SetDirty( true );
287 SetDirty();
288 return item;
289}
290
291
292void CN_LIST::RemoveInvalidItems( std::vector<CN_ITEM*>& aGarbage )
293{
294 if( !m_hasInvalid )
295 return;
296
297 auto lastItem = std::remove_if( m_items.begin(), m_items.end(),
298 [&aGarbage]( CN_ITEM* item )
299 {
300 if( !item->Valid() )
301 {
302 aGarbage.push_back ( item );
303 return true;
304 }
305
306 return false;
307 } );
308
309 m_items.resize( lastItem - m_items.begin() );
310
311 for( CN_ITEM* item : aGarbage )
312 m_index.Remove( item );
313
314 m_hasInvalid = false;
315}
316
317
319{
320 assert( m_item->Valid() );
321 return m_item->Parent();
322}
323
324
326{
327 if( !m_item )
328 return false;
329
330 return m_item->Valid();
331}
332
333
335{
336 return !Valid() || m_item->Dirty();
337}
338
339
341{
342 int accuracy = 0;
343
344 if( !m_cluster )
345 return true;
346
347 // the minimal number of items connected to item_ref
348 // at this anchor point to decide the anchor is *not* dangling
349 size_t minimal_count = 1;
350 size_t connected_count = m_item->ConnectedItems().size();
351
352 // a via can be removed if connected to only one other item.
353 if( Parent()->Type() == PCB_VIA_T )
354 return connected_count < 2;
355
356 if( m_item->AnchorCount() == 1 )
357 return connected_count < minimal_count;
358
359 if( Parent()->Type() == PCB_TRACE_T || Parent()->Type() == PCB_ARC_T )
360 accuracy = KiROUND( static_cast<const PCB_TRACK*>( Parent() )->GetWidth() / 2.0 );
361 else if( Parent()->Type() == PCB_SHAPE_T )
362 accuracy = KiROUND( static_cast<const PCB_SHAPE*>( Parent() )->GetWidth() / 2.0 );
363
364 // Items with multiple anchors have usually items connected to each anchor.
365 // We want only the item count of this anchor point
366 connected_count = 0;
367
368 for( CN_ITEM* item : m_item->ConnectedItems() )
369 {
370 if( item->Parent()->Type() == PCB_ZONE_T )
371 {
372 ZONE* zone = static_cast<ZONE*>( item->Parent() );
373
374 if( zone->HitTestFilledArea( item->GetBoardLayer(), Pos(), accuracy ) )
375 connected_count++;
376 }
377 else if( item->Parent()->HitTest( Pos(), accuracy ) )
378 {
379 connected_count++;
380 }
381 }
382
383 return connected_count < minimal_count;
384}
385
386
388{
389 if( !m_cluster )
390 return 0;
391
392 int connected_count = 0;
393
394 for( CN_ITEM* item : m_item->ConnectedItems() )
395 {
396 if( item->Parent()->Type() == PCB_ZONE_T )
397 {
398 ZONE* zone = static_cast<ZONE*>( item->Parent() );
399
400 if( zone->HitTestFilledArea( item->GetBoardLayer(), Pos() ) )
401 connected_count++;
402 }
403 else if( item->Parent()->HitTest( Pos() ) )
404 {
405 connected_count++;
406 }
407 }
408
409 return connected_count;
410}
411
412
414{
415 m_items.reserve( 64 );
416 m_originPad = nullptr;
417 m_originNet = -1;
418 m_conflicting = false;
419}
420
421
425
426
428{
429 if( !m_originPad || !m_originPad->Valid() )
430 return "<none>";
431 else
432 return m_originPad->Parent()->GetNetname();
433}
434
435
436bool CN_CLUSTER::Contains( const CN_ITEM* aItem )
437{
438 return alg::contains( m_items, aItem );
439}
440
441
443{
444 return std::find_if( m_items.begin(), m_items.end(),
445 [&aItem]( const CN_ITEM* item )
446 {
447 return item->Valid() && item->Parent() == aItem;
448 } ) != m_items.end();
449}
450
451
453{
454 for( CN_ITEM* item : m_items )
455 {
456 wxLogTrace( wxT( "CN" ), wxT( " - item : %p bitem : %p type : %d inet %s\n" ),
457 item,
458 item->Parent(),
459 item->Parent()->Type(),
460 (const char*) item->Parent()->GetNetname().c_str() );
461 wxLogTrace( wxT( "CN" ), wxT( "- item : %p bitem : %p type : %d inet %s\n" ),
462 item,
463 item->Parent(),
464 item->Parent()->Type(),
465 (const char*) item->Parent()->GetNetname().c_str() );
466 item->Dump();
467 }
468}
469
470
472{
473 m_items.push_back( item );
474
475 int netCode = item->Net();
476
477 if( netCode <= 0 )
478 return;
479
480 if( m_originNet <= 0 )
481 {
482 m_originNet = netCode;
484 }
485
486 if( item->Parent()->Type() == PCB_PAD_T && !static_cast<PAD*>( item->Parent() )->IsFreePad() )
487 {
488 int rank;
489 auto it = m_netRanks.find( netCode );
490
491 if( it == m_netRanks.end() )
492 {
493 m_netRanks[netCode] = 1;
494 rank = 1;
495 }
496 else
497 {
498 it->second++;
499 rank = it->second;
500 }
501
502 if( !m_originPad || rank > m_netRanks[m_originNet] )
503 {
504 m_originPad = item;
505 m_originNet = netCode;
506 }
507
508 if( m_originPad && item->Net() != m_originNet )
509 m_conflicting = true;
510 }
511}
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
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_CONNETED_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
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)
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:55
bool IsFreePad() const
Definition pad.cpp:330
std::vector< VECTOR2I > GetConnectionPoints() const
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition pcb_shape.h:71
const VECTOR2I & GetStart() const
Definition pcb_track.h:154
const VECTOR2I & GetEnd() const
Definition pcb_track.h:151
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:74
std::shared_ptr< SHAPE_POLY_SET > GetFilledPolysList(PCB_LAYER_ID aLayer) const
Definition zone.h:607
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:847
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
@ B_Cu
Definition layer_ids.h:65
@ F_Cu
Definition layer_ids.h:64
This file contains miscellaneous commonly used macros and functions.
#define UNIMPLEMENTED_FOR(type)
Definition macros.h:96
bool contains(const _Container &__container, _Value __value)
Returns true if the container contains the given value.
Definition kicad_algo.h:100
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:103
@ SMD
Smd pad, appears on the solder paste layer (default)
Definition padstack.h:99
@ CONN
Like smd, does not appear on the solder paste layer (default) Note: also has a special attribute in G...
Definition padstack.h:100
const int accuracy
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:88
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:97
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition typeinfo.h:108
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:87
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:98
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:96
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695