KiCad PCB EDA Suite
connectivity_items.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KICAD, a free EDA CAD application.
3  *
4  * Copyright (C) 2016-2018 CERN
5  * Copyright (C) 2019 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, you may find one here:
21  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
22  * or you may search the http://www.gnu.org website for the version 2 license,
23  * or you may write to the Free Software Foundation, Inc.,
24  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
25  */
26 
28 
30 {
31  if( !m_valid )
32  return 0;
33 
34  switch( m_parent->Type() )
35  {
36  case PCB_PAD_T:
37  return 5; // center, north, south, east and west
38  case PCB_TRACE_T:
39  case PCB_ARC_T:
40  return 2; // start and end
41  default:
42  return 1;
43  }
44 }
45 
46 
47 const VECTOR2I CN_ITEM::GetAnchor( int n ) const
48 {
49  VECTOR2I pt0;
50 
51  if( !m_valid )
52  return pt0;
53 
54  switch( m_parent->Type() )
55  {
56  case PCB_PAD_T:
57  {
58  D_PAD* pad = (D_PAD*) m_parent;
59 
60  if( n == 0 )
61  return VECTOR2I( pad->GetPosition() );
62 
63  // ShapePos() is the geometric center (not anchor) for the pad
64  pt0 = pad->ShapePos();
65  VECTOR2I pt1 = pt0;
66 
67  switch( pad->GetShape() )
68  {
69  case PAD_SHAPE_RECT:
70  case PAD_SHAPE_CIRCLE:
71  case PAD_SHAPE_OVAL:
74  switch( n )
75  {
76  case 1: pt1.y -= pad->GetSize().y / 2; break; // North
77  case 2: pt1.y += pad->GetSize().y / 2; break; // South
78  case 3: pt1.x -= pad->GetSize().x / 2; break; // East
79  case 4: pt1.x += pad->GetSize().x / 2; break; // West
80  default: break; // Wicked witch
81  }
82 
83  if( pad->GetOrientation() )
84  RotatePoint( pt1, pad->ShapePos(), pad->GetOrientation() );
85 
86  // Thermal spokes on circular pads form an 'X' instead of a '+'
87  if( pad->GetShape() == PAD_SHAPE_CIRCLE )
88  RotatePoint( pt1, pad->ShapePos(), 450 );
89 
90  return pt1;
91 
93  case PAD_SHAPE_CUSTOM:
94  {
95  switch( n )
96  {
97  case 1: pt1.y = INT_MIN / 2; break; // North
98  case 2: pt1.y = INT_MAX / 2; break; // South
99  case 3: pt1.x = INT_MIN / 2; break; // East
100  case 4: pt1.x = INT_MAX / 2; break; // West
101  default: break; // Wicked witch
102  }
103 
104  if( pad->GetOrientation() )
105  RotatePoint( pt1, pad->ShapePos(), pad->GetOrientation() );
106 
107  SHAPE_POLY_SET padPolySet;
108  pad->BuildPadShapePolygon( padPolySet, wxSize( 0, 0 ), ARC_LOW_DEF );
109  const SHAPE_LINE_CHAIN& padOutline = padPolySet.COutline( 0 );
110  SHAPE_LINE_CHAIN::INTERSECTIONS intersections;
111 
112  padOutline.Intersect( SEG( pt0, pt1 ), intersections );
113 
114  if( intersections.empty() )
115  {
116  // There should always be at least some copper outside the hole and/or
117  // shapePos center
118  assert( false );
119  return pt0;
120  }
121 
122  return intersections[ intersections.size() - 1 ].p;
123  }
124  }
125 
126  break;
127  }
128  case PCB_TRACE_T:
129  case PCB_ARC_T:
130  if( n == 0 )
131  return static_cast<const TRACK*>( m_parent )->GetStart();
132  else
133  return static_cast<const TRACK*>( m_parent )->GetEnd();
134 
135  case PCB_VIA_T:
136  return static_cast<const VIA*>( m_parent )->GetStart();
137 
138  default:
139  assert( false );
140  break;
141  }
142 
143  return pt0;
144 }
145 
146 
148 {
149  printf(" valid: %d, connected: \n", !!Valid());
150 
151  for( auto i : m_connected )
152  {
153  TRACK* t = static_cast<TRACK*>( i->Parent() );
154  printf( " - %p %d\n", t, t->Type() );
155  }
156 }
157 
158 
160 {
161  if( !Valid() )
162  return 0;
163 
164  const auto zone = static_cast<const ZONE_CONTAINER*>( Parent() );
165  const auto& outline = zone->GetFilledPolysList().COutline( m_subpolyIndex );
166 
167  return outline.PointCount() ? 1 : 0;
168 }
169 
170 
171 const VECTOR2I CN_ZONE::GetAnchor( int n ) const
172 {
173  if( !Valid() )
174  return VECTOR2I();
175 
176  const auto zone = static_cast<const ZONE_CONTAINER*> ( Parent() );
177  const auto& outline = zone->GetFilledPolysList().COutline( m_subpolyIndex );
178 
179  return outline.CPoint( 0 );
180 }
181 
182 
184 {
185  for( auto it = m_connected.begin(); it != m_connected.end(); )
186  {
187  if( !(*it)->Valid() )
188  it = m_connected.erase( it );
189  else
190  ++it;
191  }
192 }
193 
194 
196  {
197  if( !pad->IsOnCopperLayer() )
198  return nullptr;
199 
200  auto item = new CN_ITEM( pad, false, 1 );
201  item->AddAnchor( pad->ShapePos() );
202  item->SetLayers( LAYER_RANGE( F_Cu, B_Cu ) );
203 
204  switch( pad->GetAttribute() )
205  {
206  case PAD_ATTRIB_SMD:
208  case PAD_ATTRIB_CONN:
209  {
210  LSET lmsk = pad->GetLayerSet();
211 
212  for( int i = 0; i <= MAX_CU_LAYERS; i++ )
213  {
214  if( lmsk[i] )
215  {
216  item->SetLayer( i );
217  break;
218  }
219  }
220  break;
221  }
222  default:
223  break;
224  }
225 
226  addItemtoTree( item );
227  m_items.push_back( item );
228  SetDirty();
229  return item;
230 }
231 
233 {
234  auto item = new CN_ITEM( track, true );
235  m_items.push_back( item );
236  item->AddAnchor( track->GetStart() );
237  item->AddAnchor( track->GetEnd() );
238  item->SetLayer( track->GetLayer() );
239  addItemtoTree( item );
240  SetDirty();
241  return item;
242 }
243 
245 {
246  auto item = new CN_ITEM( aArc, true );
247  m_items.push_back( item );
248  item->AddAnchor( aArc->GetStart() );
249  item->AddAnchor( aArc->GetEnd() );
250  item->SetLayer( aArc->GetLayer() );
251  addItemtoTree( item );
252  SetDirty();
253  return item;
254 }
255 
257  {
258  auto item = new CN_ITEM( via, true, 1 );
259 
260  m_items.push_back( item );
261  item->AddAnchor( via->GetStart() );
262  item->SetLayers( LAYER_RANGE( F_Cu, B_Cu ) );
263  addItemtoTree( item );
264  SetDirty();
265  return item;
266  }
267 
268  const std::vector<CN_ITEM*> CN_LIST::Add( ZONE_CONTAINER* zone )
269  {
270  const auto& polys = zone->GetFilledPolysList();
271 
272  std::vector<CN_ITEM*> rv;
273 
274  for( int j = 0; j < polys.OutlineCount(); j++ )
275  {
276  CN_ZONE* zitem = new CN_ZONE( zone, false, j );
277  const auto& outline = zone->GetFilledPolysList().COutline( j );
278 
279  for( int k = 0; k < outline.PointCount(); k++ )
280  zitem->AddAnchor( outline.CPoint( k ) );
281 
282  m_items.push_back( zitem );
283  zitem->SetLayer( zone->GetLayer() );
284  addItemtoTree( zitem );
285  rv.push_back( zitem );
286  SetDirty();
287  }
288 
289  return rv;
290  }
291 
292 
293 void CN_LIST::RemoveInvalidItems( std::vector<CN_ITEM*>& aGarbage )
294 {
295  if( !m_hasInvalid )
296  return;
297 
298  auto lastItem = std::remove_if(m_items.begin(), m_items.end(), [&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( auto item : m_items )
312  item->RemoveInvalidRefs();
313 
314  for( auto item : aGarbage )
315  m_index.Remove( item );
316 
317  m_hasInvalid = false;
318 }
319 
320 
322 {
323  assert( m_item->Valid() );
324  return m_item->Parent();
325 }
326 
327 
328 bool CN_ANCHOR::Valid() const
329 {
330  if( !m_item )
331  return false;
332 
333  return m_item->Valid();
334 }
335 
336 
338 {
339  if( !m_cluster )
340  return true;
341 
342  // the minimal number of items connected to item_ref
343  // at this anchor point to decide the anchor is *not* dangling
344  size_t minimal_count = 1;
345  size_t connected_count = m_item->ConnectedItems().size();
346 
347  // a via can be removed if connected to only one other item.
348  if( Parent()->Type() == PCB_VIA_T )
349  return connected_count < 2;
350 
351  if( m_item->AnchorCount() == 1 )
352  return connected_count < minimal_count;
353 
354  // Items with multiple anchors have usually items connected to each anchor.
355  // We want only the item count of this anchor point
356  connected_count = 0;
357  for( auto item : m_item->ConnectedItems() )
358  {
359  if( item->Parent()->Type() == PCB_ZONE_AREA_T )
360  {
361  ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item->Parent() );
362 
363  if( zone->HitTestFilledArea( (wxPoint) Pos() ) )
364  connected_count++;
365  }
366  else if( item->Parent()->HitTest( (wxPoint) Pos() ) )
367  connected_count++;
368  }
369 
370  return connected_count < minimal_count;
371 }
372 
373 
375 {
376  if( !m_cluster )
377  return 0;
378 
379  int connected_count = 0;
380 
381  for( auto item : m_item->ConnectedItems() )
382  {
383  if( item->Parent()->Type() == PCB_ZONE_AREA_T )
384  {
385  ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item->Parent() );
386 
387  if( zone->HitTestFilledArea( wxPoint( Pos().x, Pos().y ) ) )
388  connected_count++;
389  }
390  else if( item->Parent()->HitTest( wxPoint( Pos().x, Pos().y ) ) )
391  connected_count++;
392  }
393 
394  return connected_count;
395 }
396 
397 
399 {
400  m_items.reserve( 64 );
401  m_originPad = nullptr;
402  m_originNet = -1;
403  m_conflicting = false;
404 }
405 
406 
408 {
409 }
410 
411 
413 {
414  if( !m_originPad || !m_originPad->Valid() )
415  return "<none>";
416  else
417  return m_originPad->Parent()->GetNetname();
418 }
419 
420 
421 bool CN_CLUSTER::Contains( const CN_ITEM* aItem )
422 {
423  return std::find( m_items.begin(), m_items.end(), aItem ) != m_items.end();
424 }
425 
426 
428 {
429  return std::find_if( m_items.begin(), m_items.end(), [ &aItem ] ( const CN_ITEM* item )
430  { return item->Valid() && item->Parent() == aItem; } ) != m_items.end();
431 }
432 
433 
435 {
436  for( auto item : m_items )
437  {
438  wxLogTrace( "CN", " - item : %p bitem : %p type : %d inet %s\n", item, item->Parent(),
439  item->Parent()->Type(), (const char*) item->Parent()->GetNetname().c_str() );
440  printf( "- item : %p bitem : %p type : %d inet %s\n", item, item->Parent(),
441  item->Parent()->Type(), (const char*) item->Parent()->GetNetname().c_str() );
442  item->Dump();
443  }
444 }
445 
446 
448 {
449  m_items.push_back( item );
450 
451  if( item->Net() <= 0 )
452  return;
453 
454  if( m_originNet <= 0 )
455  {
456  m_originNet = item->Net();
457  }
458 
459  if( item->Parent()->Type() == PCB_PAD_T )
460  {
461  if( !m_originPad )
462  {
463  m_originPad = item;
464  m_originNet = item->Net();
465  }
466 
467  if( m_originPad && item->Net() != m_originNet )
468  {
469  m_conflicting = true;
470  }
471  }
472 }
void RemoveInvalidItems(std::vector< CN_ITEM * > &aGarbage)
const CONNECTED_ITEMS & ConnectedItems() const
bool Contains(const CN_ITEM *aItem)
virtual int AnchorCount() const override
std::vector< CN_ITEM * > m_items
ZONE_CONTAINER handles a list of polygons defining a copper zone.
Definition: class_zone.h:60
int Intersect(const SEG &aSeg, INTERSECTIONS &aIp) const
Function Intersect()
std::vector< INTERSECTION > INTERSECTIONS
BOARD_CONNECTED_ITEM * m_parent
like PAD_STANDARD, but not plated mechanical use only, no connection allowed
Definition: pad_shapes.h:66
std::shared_ptr< CN_CLUSTER > m_cluster
Cluster to which the anchor belongs.
const wxPoint & GetStart() const
Definition: class_track.h:118
void addItemtoTree(CN_ITEM *item)
bool m_valid
valid flag, used to identify garbage items (we use lazy removal)
CN_ITEM * Add(D_PAD *pad)
Smd pad, appears on the solder paste layer (default)
Definition: pad_shapes.h:62
class ZONE_CONTAINER, a zone area
Definition: typeinfo.h:102
virtual PCB_LAYER_ID GetLayer() const override
Function GetLayer returns the primary layer this item is on.
Definition: class_zone.cpp:219
void BuildPadShapePolygon(SHAPE_POLY_SET &aCornerBuffer, wxSize aInflateValue, int aError=ARC_HIGH_DEF) const
Function BuildPadShapePolygon Build the Corner list of the polygonal shape, depending on shape,...
class ARC, an arc track segment on a copper layer
Definition: typeinfo.h:98
bool HitTestFilledArea(const wxPoint &aRefPos) const
Function HitTestFilledArea tests if the given wxPoint is within the bounds of a filled area of this z...
Definition: class_zone.cpp:549
class D_PAD, a pad in a footprint
Definition: typeinfo.h:90
BOARD_CONNECTED_ITEM * Parent() const
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:208
bool IsDangling() const
has meaning only for tracks and vias.
VECTOR2< int > VECTOR2I
Definition: vector2d.h:594
BOARD_CONNECTED_ITEM is a base class derived from BOARD_ITEM for items that can be connected and have...
void SetDirty(bool aDirty=true)
int ConnectedItemsCount() const
has meaning only for tracks and vias.
int Net() const
PAD_ATTR_T GetAttribute() const
Definition: class_pad.h:438
class TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:96
virtual int AnchorCount() const
std::vector< CN_ITEM * > m_items
LSET is a set of PCB_LAYER_IDs.
SHAPE_POLY_SET.
bool IsOnCopperLayer() const override
Definition: class_pad.h:270
LSET GetLayerSet() const override
Function GetLayerSet returns a "layer mask", which is a bitmap of all layers on which the TRACK segme...
Definition: class_pad.h:435
void Add(CN_ITEM *item)
const wxString & GetNetname() const
Function GetNetname.
Like smd, does not appear on the solder paste layer (default) note also has a special attribute in Ge...
Definition: pad_shapes.h:63
bool Valid() const
virtual const VECTOR2I GetAnchor(int n) const
Definition: seg.h:39
wxString OriginNetName() const
void AddAnchor(const VECTOR2I &aPos)
virtual const VECTOR2I GetAnchor(int n) const override
CN_ITEM * m_item
Item owning the anchor.
SHAPE_LINE_CHAIN.
void SetLayer(int aLayer)
Function SetLayer()
CN_ITEM * m_originPad
double GetOrientation() const
Function GetOrientation returns the rotation angle of the pad in tenths of degrees,...
Definition: class_pad.h:411
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
wxPoint ShapePos() const
Definition: class_pad.cpp:601
BOARD_CONNECTED_ITEM * Parent() const
const wxPoint & GetEnd() const
Definition: class_track.h:115
void Remove(T aItem)
Function Remove() Removes an item from the tree.
const VECTOR2I & Pos() const
CONNECTED_ITEMS m_connected
list of items physically connected (touching)
class VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:97
PAD_SHAPE_T GetShape() const
Function GetShape.
Definition: class_pad.h:222
const wxSize & GetSize() const
Definition: class_pad.h:285
const wxPoint GetPosition() const override
Definition: class_pad.h:226
virtual PCB_LAYER_ID GetLayer() const
Function GetLayer returns the primary layer this item is on.
LAYER_RANGE.
Definition: pns_layerset.h:32
void RemoveInvalidRefs()
bool Valid() const
CN_RTREE< CN_ITEM * > m_index
KICAD_T Type() const
Function Type()
Definition: base_struct.h:197
const SHAPE_POLY_SET & GetFilledPolysList() const
Function GetFilledPolysList returns a reference to the list of filled polygons.
Definition: class_zone.h:575