KiCad PCB EDA Suite
TRACKS_CLEANER Class Reference

#include <tracks_cleaner.h>

Public Member Functions

 TRACKS_CLEANER (BOARD *aPcb, BOARD_COMMIT &aCommit)
 
void CleanupBoard (bool aDryRun, std::vector< std::shared_ptr< CLEANUP_ITEM > > *aItemsList, bool aCleanVias, bool aRemoveMisConnected, bool aMergeSegments, bool aDeleteUnconnected, bool aDeleteTracksinPad, bool aDeleteDanglingVias)
 the cleanup function. More...
 

Private Member Functions

void removeShortingTrackSegments ()
 
bool deleteDanglingTracks (bool aTrack, bool aVia)
 Removes tracks or vias only connected on one end. More...
 
void deleteTracksInPads ()
 
void cleanup (bool aDeleteDuplicateVias, bool aDeleteNullSegments, bool aDeleteDuplicateSegments, bool aMergeSegments)
 Geometry-based cleanup: duplicate items, null items, colinear items. More...
 
bool mergeCollinearSegments (PCB_TRACK *aSeg1, PCB_TRACK *aSeg2, const std::vector< BOARD_CONNECTED_ITEM * > &aSeg1Items)
 helper function merge aTrackRef and aCandidate, when possible, i.e. More...
 
bool testTrackEndpointIsNode (PCB_TRACK *aTrack, bool aTstStart)
 
void removeItems (std::set< BOARD_ITEM * > &aItems)
 

Private Attributes

BOARDm_brd
 
BOARD_COMMITm_commit
 
bool m_dryRun
 
std::vector< std::shared_ptr< CLEANUP_ITEM > > * m_itemsList
 

Detailed Description

Definition at line 35 of file tracks_cleaner.h.

Constructor & Destructor Documentation

◆ TRACKS_CLEANER()

TRACKS_CLEANER::TRACKS_CLEANER ( BOARD aPcb,
BOARD_COMMIT aCommit 
)

Definition at line 37 of file tracks_cleaner.cpp.

37  :
38  m_brd( aPcb ),
39  m_commit( aCommit ),
40  m_dryRun( true ),
41  m_itemsList( nullptr )
42 {
43 }
BOARD_COMMIT & m_commit
std::vector< std::shared_ptr< CLEANUP_ITEM > > * m_itemsList

Member Function Documentation

◆ cleanup()

void TRACKS_CLEANER::cleanup ( bool  aDeleteDuplicateVias,
bool  aDeleteNullSegments,
bool  aDeleteDuplicateSegments,
bool  aMergeSegments 
)
private

Geometry-based cleanup: duplicate items, null items, colinear items.

Definition at line 270 of file tracks_cleaner.cpp.

272 {
274  DRC_RTREE rtree;
275 
276  for( PCB_TRACK* track : m_brd->Tracks() )
277  {
278  track->ClearFlags( IS_DELETED | SKIP_STRUCT );
279  rtree.Insert( track, track->GetLayer() );
280  }
281 
282  std::set<BOARD_ITEM*> toRemove;
283 
284  for( PCB_TRACK* track : m_brd->Tracks() )
285  {
286  if( track->HasFlag( IS_DELETED ) || track->IsLocked() )
287  continue;
288 
289  if( aDeleteDuplicateVias && track->Type() == PCB_VIA_T )
290  {
291  PCB_VIA* via = static_cast<PCB_VIA*>( track );
292 
293  if( via->GetStart() != via->GetEnd() )
294  via->SetEnd( via->GetStart() );
295 
296  rtree.QueryColliding( via, via->GetLayer(), via->GetLayer(),
297  // Filter:
298  [&]( BOARD_ITEM* aItem ) -> bool
299  {
300  return aItem->Type() == PCB_VIA_T
301  && !aItem->HasFlag( SKIP_STRUCT )
302  && !aItem->HasFlag( IS_DELETED );
303  },
304  // Visitor:
305  [&]( BOARD_ITEM* aItem ) -> bool
306  {
307  PCB_VIA* other = static_cast<PCB_VIA*>( aItem );
308 
309  if( via->GetPosition() == other->GetPosition()
310  && via->GetViaType() == other->GetViaType()
311  && via->GetLayerSet() == other->GetLayerSet() )
312  {
313  auto item = std::make_shared<CLEANUP_ITEM>( CLEANUP_REDUNDANT_VIA );
314  item->SetItems( via );
315  m_itemsList->push_back( item );
316 
317  via->SetFlags( IS_DELETED );
318  toRemove.insert( via );
319  }
320 
321  return true;
322  } );
323 
324  // To delete through Via on THT pads at same location
325  // Examine the list of connected pads: if a through pad is found, the via is redundant
326  for( PAD* pad : m_brd->GetConnectivity()->GetConnectedPads( via ) )
327  {
328  const LSET all_cu = LSET::AllCuMask();
329 
330  if( ( pad->GetLayerSet() & all_cu ) == all_cu )
331  {
332  auto item = std::make_shared<CLEANUP_ITEM>( CLEANUP_REDUNDANT_VIA );
333  item->SetItems( via, pad );
334  m_itemsList->push_back( item );
335 
336  via->SetFlags( IS_DELETED );
337  toRemove.insert( via );
338  break;
339  }
340  }
341 
342  via->SetFlags( SKIP_STRUCT );
343  }
344 
345  if( aDeleteNullSegments && track->Type() != PCB_VIA_T )
346  {
347  if( track->IsNull() )
348  {
349  auto item = std::make_shared<CLEANUP_ITEM>( CLEANUP_ZERO_LENGTH_TRACK );
350  item->SetItems( track );
351  m_itemsList->push_back( item );
352 
353  track->SetFlags( IS_DELETED );
354  toRemove.insert( track );
355  }
356  }
357 
358  if( aDeleteDuplicateSegments && track->Type() == PCB_TRACE_T && !track->IsNull() )
359  {
360  rtree.QueryColliding( track, track->GetLayer(), track->GetLayer(),
361  // Filter:
362  [&]( BOARD_ITEM* aItem ) -> bool
363  {
364  return aItem->Type() == PCB_TRACE_T
365  && !aItem->HasFlag( SKIP_STRUCT )
366  && !aItem->HasFlag( IS_DELETED )
367  && !static_cast<PCB_TRACK*>( aItem )->IsNull();
368  },
369  // Visitor:
370  [&]( BOARD_ITEM* aItem ) -> bool
371  {
372  PCB_TRACK* other = static_cast<PCB_TRACK*>( aItem );
373 
374  if( track->IsPointOnEnds( other->GetStart() )
375  && track->IsPointOnEnds( other->GetEnd() )
376  && track->GetWidth() == other->GetWidth()
377  && track->GetLayer() == other->GetLayer() )
378  {
379  auto item = std::make_shared<CLEANUP_ITEM>( CLEANUP_DUPLICATE_TRACK );
380  item->SetItems( track );
381  m_itemsList->push_back( item );
382 
383  track->SetFlags( IS_DELETED );
384  toRemove.insert( track );
385  }
386 
387  return true;
388  } );
389 
390  track->SetFlags( SKIP_STRUCT );
391  }
392  }
393 
394  if( !m_dryRun )
395  removeItems( toRemove );
396 
397  if( aMergeSegments )
398  {
399  bool merged;
400 
401  do
402  {
403  merged = false;
405 
406  auto connectivity = m_brd->GetConnectivity()->GetConnectivityAlgo();
407 
408  // Keep a duplicate deque to all deleting in the primary
409  std::deque<PCB_TRACK*> temp_segments( m_brd->Tracks() );
410 
411  // merge collinear segments:
412  for( PCB_TRACK* segment : temp_segments )
413  {
414  // one can merge only collinear segments, not vias or arcs.
415  if( segment->Type() != PCB_TRACE_T )
416  continue;
417 
418  if( segment->HasFlag( IS_DELETED ) ) // already taken into account
419  continue;
420 
421  std::vector<BOARD_CONNECTED_ITEM*> connectedItems =
422  m_brd->GetConnectivity()->GetConnectedItems( segment, types );
423 
424  // for each end of the segment:
425  for( CN_ITEM* citem : connectivity->ItemEntry( segment ).GetItems() )
426  {
427  // Do not merge an end which has different width tracks attached -- it's a
428  // common use-case for necking-down a track between pads.
429  std::vector<PCB_TRACK*> sameWidthCandidates;
430  std::vector<PCB_TRACK*> differentWidthCandidates;
431 
432  for( CN_ITEM* connected : citem->ConnectedItems() )
433  {
434  if( !connected->Valid() )
435  continue;
436 
437  BOARD_CONNECTED_ITEM* candidate = connected->Parent();
438 
439  if( candidate->Type() == PCB_TRACE_T && !candidate->HasFlag( IS_DELETED ) )
440  {
441  PCB_TRACK* candidateSegment = static_cast<PCB_TRACK*>( candidate );
442 
443  if( candidateSegment->GetWidth() == segment->GetWidth() )
444  {
445  sameWidthCandidates.push_back( candidateSegment );
446  }
447  else
448  {
449  differentWidthCandidates.push_back( candidateSegment );
450  break;
451  }
452  }
453  }
454 
455  if( !differentWidthCandidates.empty() )
456  continue;
457 
458  for( PCB_TRACK* candidate : sameWidthCandidates )
459  {
460  if( segment->ApproxCollinear( *candidate )
461  && mergeCollinearSegments( segment, candidate, connectedItems ) )
462  {
463  merged = true;
464  break;
465  }
466  }
467  }
468  }
469  } while( merged );
470  }
471 
472  for( PCB_TRACK* track : m_brd->Tracks() )
473  track->ClearFlags( IS_DELETED | SKIP_STRUCT );
474 }
const CONNECTED_ITEMS & ConnectedItems() const
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:759
wxPoint GetPosition() const override
Definition: pcb_track.h:392
const wxPoint & GetEnd() const
Definition: pcb_track.h:105
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:49
VIATYPE GetViaType() const
Definition: pcb_track.h:354
bool HasFlag(EDA_ITEM_FLAGS aFlag) const
Definition: eda_item.h:155
class PCB_ARC, an arc track segment on a copper layer
Definition: typeinfo.h:97
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: pcb_track.cpp:375
class PAD, a pad in a footprint
Definition: typeinfo.h:89
int GetWidth() const
Definition: pcb_track.h:102
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition: typeinfo.h:77
class PCB_TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:95
LSET is a set of PCB_LAYER_IDs.
Definition: layer_ids.h:516
#define IS_DELETED
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition: board.h:345
void removeItems(std::set< BOARD_ITEM * > &aItems)
class ZONE, a copper pour area
Definition: typeinfo.h:105
bool mergeCollinearSegments(PCB_TRACK *aSeg1, PCB_TRACK *aSeg2, const std::vector< BOARD_CONNECTED_ITEM * > &aSeg1Items)
helper function merge aTrackRef and aCandidate, when possible, i.e.
void BuildConnectivity(PROGRESS_REPORTER *aReporter=nullptr)
Build or rebuild the board connectivity database for the board, especially the list of connected item...
Definition: board.cpp:137
#define SKIP_STRUCT
flag indicating that the structure should be ignored
std::vector< std::shared_ptr< CLEANUP_ITEM > > * m_itemsList
CN_ITEM represents a BOARD_CONNETED_ITEM in the connectivity system (ie: a pad, track/arc/via,...
void Insert(BOARD_ITEM *aItem, PCB_LAYER_ID aLayer, int aWorstClearance=0)
Insert an item into the tree on a particular layer with an optional worst clearance.
Definition: drc_rtree.h:93
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:96
Definition: pad.h:57
Implement an R-tree for fast spatial and layer indexing of connectable items.
Definition: drc_rtree.h:46
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition: board_item.h:143
TRACKS & Tracks()
Definition: board.h:231
const wxPoint & GetStart() const
Definition: pcb_track.h:108
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:112
int QueryColliding(BOARD_ITEM *aRefItem, PCB_LAYER_ID aRefLayer, PCB_LAYER_ID aTargetLayer, std::function< bool(BOARD_ITEM *)> aFilter=nullptr, std::function< bool(BOARD_ITEM *)> aVisitor=nullptr, int aClearance=0) const
This is a fast test which essentially does bounding-box overlap given a worst-case clearance.
Definition: drc_rtree.h:183

References LSET::AllCuMask(), BOARD::BuildConnectivity(), CLEANUP_DUPLICATE_TRACK, CLEANUP_REDUNDANT_VIA, CLEANUP_ZERO_LENGTH_TRACK, CN_ITEM::ConnectedItems(), BOARD::GetConnectivity(), PCB_TRACK::GetEnd(), BOARD_ITEM::GetLayer(), PCB_VIA::GetLayerSet(), PCB_VIA::GetPosition(), PCB_TRACK::GetStart(), PCB_VIA::GetViaType(), PCB_TRACK::GetWidth(), EDA_ITEM::HasFlag(), DRC_RTREE::Insert(), IS_DELETED, m_brd, m_dryRun, m_itemsList, mergeCollinearSegments(), pad, PCB_ARC_T, PCB_PAD_T, PCB_TRACE_T, PCB_VIA_T, PCB_ZONE_T, DRC_RTREE::QueryColliding(), removeItems(), SKIP_STRUCT, BOARD::Tracks(), EDA_ITEM::Type(), and via.

Referenced by CleanupBoard().

◆ CleanupBoard()

void TRACKS_CLEANER::CleanupBoard ( bool  aDryRun,
std::vector< std::shared_ptr< CLEANUP_ITEM > > *  aItemsList,
bool  aCleanVias,
bool  aRemoveMisConnected,
bool  aMergeSegments,
bool  aDeleteUnconnected,
bool  aDeleteTracksinPad,
bool  aDeleteDanglingVias 
)

the cleanup function.

Parameters
aCleanVias= true to remove superimposed vias
aRemoveMisConnected= true to remove segments connecting 2 different nets (short circuits)
aMergeSegments= true to merge collinear segmenst and remove 0 len segm
aDeleteUnconnected= true to remove dangling tracks
aDeleteTracksinPad= true to remove tracks fully inside pads
aDeleteDanglingVias= true to remove a via that is only connected to a single layer

Definition at line 52 of file tracks_cleaner.cpp.

55 {
56  bool has_deleted = false;
57 
58  m_dryRun = aDryRun;
59  m_itemsList = aItemsList;
60 
61  cleanup( aCleanVias, aMergeSegments || aRemoveMisConnected, aMergeSegments, aMergeSegments );
62 
63  if( aRemoveMisConnected )
65 
66  if( aDeleteTracksinPad )
68 
69  has_deleted = deleteDanglingTracks( aDeleteUnconnected, aDeleteDanglingVias );
70 
71  if( has_deleted && aMergeSegments )
72  cleanup( false, false, false, true );
73 }
void removeShortingTrackSegments()
void cleanup(bool aDeleteDuplicateVias, bool aDeleteNullSegments, bool aDeleteDuplicateSegments, bool aMergeSegments)
Geometry-based cleanup: duplicate items, null items, colinear items.
bool deleteDanglingTracks(bool aTrack, bool aVia)
Removes tracks or vias only connected on one end.
std::vector< std::shared_ptr< CLEANUP_ITEM > > * m_itemsList

References cleanup(), deleteDanglingTracks(), deleteTracksInPads(), m_dryRun, m_itemsList, and removeShortingTrackSegments().

Referenced by BOOST_FIXTURE_TEST_CASE().

◆ deleteDanglingTracks()

bool TRACKS_CLEANER::deleteDanglingTracks ( bool  aTrack,
bool  aVia 
)
private

Removes tracks or vias only connected on one end.

Parameters
aTrackif true, clean dangling tracks
aViaif true, clean dangling vias
Returns
true if any items were deleted

Definition at line 163 of file tracks_cleaner.cpp.

164 {
165  bool item_erased = false;
166  bool modified = false;
167 
168  if( !aTrack && !aVia )
169  return false;
170 
171  do // Iterate when at least one track is deleted
172  {
173  item_erased = false;
174  // Ensure the connectivity is up to date, especially after removing a dangling segment
176 
177  // Keep a duplicate deque to all deleting in the primary
178  std::deque<PCB_TRACK*> temp_tracks( m_brd->Tracks() );
179 
180  for( PCB_TRACK* track : temp_tracks )
181  {
182  if( track->IsLocked() || ( track->GetFlags() & IS_DELETED ) > 0 )
183  continue;
184 
185  if( !aVia && track->Type() == PCB_VIA_T )
186  continue;
187 
188  if( !aTrack && ( track->Type() == PCB_TRACE_T || track->Type() == PCB_ARC_T ) )
189  continue;
190 
191  // Test if a track (or a via) endpoint is not connected to another track or zone.
192  if( m_brd->GetConnectivity()->TestTrackEndpointDangling( track ) )
193  {
194  std::shared_ptr<CLEANUP_ITEM> item;
195 
196  if( track->Type() == PCB_VIA_T )
197  item = std::make_shared<CLEANUP_ITEM>( CLEANUP_DANGLING_VIA );
198  else
199  item = std::make_shared<CLEANUP_ITEM>( CLEANUP_DANGLING_TRACK );
200 
201  item->SetItems( track );
202  m_itemsList->push_back( item );
203  track->SetFlags( IS_DELETED );
204 
205  // keep iterating, because a track connected to the deleted track
206  // now perhaps is not connected and should be deleted
207  item_erased = true;
208 
209  if( !m_dryRun )
210  {
211  m_brd->Remove( track );
212  m_commit.Removed( track );
213  modified = true;
214  }
215  }
216  }
217  } while( item_erased ); // A segment was erased: test for some new dangling segments
218 
219  return modified;
220 }
class PCB_ARC, an arc track segment on a copper layer
Definition: typeinfo.h:97
class PCB_TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:95
BOARD_COMMIT & m_commit
COMMIT & Removed(EDA_ITEM *aItem)
Modify a given item in the model.
Definition: commit.h:96
#define IS_DELETED
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition: board.h:345
void BuildConnectivity(PROGRESS_REPORTER *aReporter=nullptr)
Build or rebuild the board connectivity database for the board, especially the list of connected item...
Definition: board.cpp:137
std::vector< std::shared_ptr< CLEANUP_ITEM > > * m_itemsList
void Remove(BOARD_ITEM *aBoardItem, REMOVE_MODE aMode=REMOVE_MODE::NORMAL) override
Removes an item from the container.
Definition: board.cpp:710
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:96
TRACKS & Tracks()
Definition: board.h:231

References BOARD::BuildConnectivity(), CLEANUP_DANGLING_TRACK, CLEANUP_DANGLING_VIA, BOARD::GetConnectivity(), IS_DELETED, m_brd, m_commit, m_dryRun, m_itemsList, PCB_ARC_T, PCB_TRACE_T, PCB_VIA_T, BOARD::Remove(), COMMIT::Removed(), and BOARD::Tracks().

Referenced by CleanupBoard().

◆ deleteTracksInPads()

void TRACKS_CLEANER::deleteTracksInPads ( )
private

Definition at line 223 of file tracks_cleaner.cpp.

224 {
225  std::set<BOARD_ITEM*> toRemove;
226 
227  // Delete tracks that start and end on the same pad
228  std::shared_ptr<CONNECTIVITY_DATA> connectivity = m_brd->GetConnectivity();
229 
230  for( PCB_TRACK* track : m_brd->Tracks() )
231  {
232  if( track->IsLocked() )
233  continue;
234 
235  if( track->Type() == PCB_VIA_T )
236  continue;
237 
238  // Mark track if connected to pads
239  for( PAD* pad : connectivity->GetConnectedPads( track ) )
240  {
241  if( pad->HitTest( track->GetStart() ) && pad->HitTest( track->GetEnd() ) )
242  {
243  SHAPE_POLY_SET poly;
244  track->TransformShapeWithClearanceToPolygon( poly, track->GetLayer(), 0,
245  ARC_HIGH_DEF, ERROR_INSIDE );
246 
247  poly.BooleanSubtract( *pad->GetEffectivePolygon(), SHAPE_POLY_SET::PM_FAST );
248 
249  if( poly.IsEmpty() )
250  {
251  auto item = std::make_shared<CLEANUP_ITEM>( CLEANUP_TRACK_IN_PAD );
252  item->SetItems( track );
253  m_itemsList->push_back( item );
254 
255  toRemove.insert( track );
256  track->SetFlags( IS_DELETED );
257  }
258  }
259  }
260  }
261 
262  if( !m_dryRun )
263  removeItems( toRemove );
264 }
bool IsEmpty() const
#define IS_DELETED
Represent a set of closed polygons.
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition: board.h:345
void removeItems(std::set< BOARD_ITEM * > &aItems)
std::vector< std::shared_ptr< CLEANUP_ITEM > > * m_itemsList
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:96
void BooleanSubtract(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Perform boolean polyset intersection For aFastMode meaning, see function booleanOp.
Definition: pad.h:57
TRACKS & Tracks()
Definition: board.h:231

References SHAPE_POLY_SET::BooleanSubtract(), CLEANUP_TRACK_IN_PAD, ERROR_INSIDE, BOARD::GetConnectivity(), IS_DELETED, SHAPE_POLY_SET::IsEmpty(), m_brd, m_dryRun, m_itemsList, pad, PCB_VIA_T, SHAPE_POLY_SET::PM_FAST, removeItems(), and BOARD::Tracks().

Referenced by CleanupBoard().

◆ mergeCollinearSegments()

bool TRACKS_CLEANER::mergeCollinearSegments ( PCB_TRACK aSeg1,
PCB_TRACK aSeg2,
const std::vector< BOARD_CONNECTED_ITEM * > &  aSeg1Items 
)
private

helper function merge aTrackRef and aCandidate, when possible, i.e.

when they are colinear, same width, and obviously same layer

Returns
true if the segments are merged, false if not
Parameters
aSeg1is the reference
aSeg2is the candidate, and after merging, the removed segment

Definition at line 477 of file tracks_cleaner.cpp.

479 {
480  static KICAD_T types[] = { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T, PCB_PAD_T, PCB_ZONE_T };
481 
482  if( aSeg1->IsLocked() || aSeg2->IsLocked() )
483  return false;
484 
485  std::shared_ptr<CONNECTIVITY_DATA> connectivity = m_brd->GetConnectivity();
486 
487  std::vector<BOARD_CONNECTED_ITEM*> tracks = aSeg1Items;
488  std::vector<BOARD_CONNECTED_ITEM*> tracks2 = connectivity->GetConnectedItems( aSeg2, types );
489 
490  std::move( tracks2.begin(), tracks2.end(), std::back_inserter( tracks ) );
491  std::sort( tracks.begin(), tracks.end() );
492  tracks.erase( std::unique( tracks.begin(), tracks.end() ), tracks.end() );
493 
494  tracks.erase( std::remove_if( tracks.begin(), tracks.end(),
495  [ aSeg1, aSeg2 ]( BOARD_CONNECTED_ITEM* aTest )
496  {
497  return ( aTest == aSeg1 ) || ( aTest == aSeg2 );
498  } ),
499  tracks.end() );
500 
501  std::set<VECTOR2I> pts;
502 
503  // Collect the unique points where the two tracks are connected to other items
504  for( BOARD_CONNECTED_ITEM* citem : tracks )
505  {
506 
507  if( PCB_TRACK* track = dyn_cast<PCB_TRACK*>( citem ) )
508  {
509  if( track->IsPointOnEnds( aSeg1->GetStart() ) )
510  pts.emplace( aSeg1->GetStart() );
511 
512  if( track->IsPointOnEnds( aSeg1->GetEnd() ) )
513  pts.emplace( aSeg1->GetEnd() );
514 
515  if( track->IsPointOnEnds( aSeg2->GetStart() ) )
516  pts.emplace( aSeg2->GetStart() );
517 
518  if( track->IsPointOnEnds( aSeg2->GetEnd() ) )
519  pts.emplace( aSeg2->GetEnd() );
520  }
521  else
522  {
523  if( citem->HitTest( aSeg1->GetStart(), ( aSeg1->GetWidth() + 1 ) / 2 ) )
524  pts.emplace( aSeg1->GetStart() );
525 
526  if( citem->HitTest( aSeg1->GetEnd(), ( aSeg1->GetWidth() + 1 ) / 2 ) )
527  pts.emplace( aSeg1->GetEnd() );
528 
529  if( citem->HitTest( aSeg2->GetStart(), ( aSeg2->GetWidth() + 1 ) / 2 ) )
530  pts.emplace( aSeg2->GetStart() );
531 
532  if( citem->HitTest( aSeg2->GetEnd(), ( aSeg2->GetWidth() + 1 ) / 2 ) )
533  pts.emplace( aSeg2->GetEnd() );
534  }
535  }
536 
537 
538  // This means there is a node in the center
539  if( pts.size() > 2 )
540  return false;
541 
542  // Verify the removed point after merging is not a node.
543  // If it is a node (i.e. if more than one other item is connected, the segments cannot be merged
544  PCB_TRACK dummy_seg( *aSeg1 );
545 
546  // Calculate the new ends of the segment to merge, and store them to dummy_seg:
547  int min_x = std::min( aSeg1->GetStart().x,
548  std::min( aSeg1->GetEnd().x, std::min( aSeg2->GetStart().x, aSeg2->GetEnd().x ) ) );
549  int min_y = std::min( aSeg1->GetStart().y,
550  std::min( aSeg1->GetEnd().y, std::min( aSeg2->GetStart().y, aSeg2->GetEnd().y ) ) );
551  int max_x = std::max( aSeg1->GetStart().x,
552  std::max( aSeg1->GetEnd().x, std::max( aSeg2->GetStart().x, aSeg2->GetEnd().x ) ) );
553  int max_y = std::max( aSeg1->GetStart().y,
554  std::max( aSeg1->GetEnd().y, std::max( aSeg2->GetStart().y, aSeg2->GetEnd().y ) ) );
555 
556  if( ( aSeg1->GetStart().x > aSeg1->GetEnd().x )
557  == ( aSeg1->GetStart().y > aSeg1->GetEnd().y ) )
558  {
559  dummy_seg.SetStart( wxPoint( min_x, min_y ) );
560  dummy_seg.SetEnd( wxPoint( max_x, max_y ) );
561  }
562  else
563  {
564  dummy_seg.SetStart( wxPoint( min_x, max_y ) );
565  dummy_seg.SetEnd( wxPoint( max_x, min_y ) );
566  }
567 
568  // If the existing connected points are not the same as the points generated by our
569  // min/max alg, then assign the missing points to the end closest. This ensures that
570  // our replacment track is still connected
571  for( auto pt : pts )
572  {
573  if( !dummy_seg.IsPointOnEnds( wxPoint( pt.x, pt.y ) ) )
574  {
575  if( ( VECTOR2I( dummy_seg.GetStart() ) - pt ).SquaredEuclideanNorm() <
576  ( VECTOR2I( dummy_seg.GetEnd() ) - pt ).SquaredEuclideanNorm() )
577  dummy_seg.SetStart( wxPoint( pt.x, pt.y ) );
578  else
579  dummy_seg.SetEnd( wxPoint( pt.x, pt.y ) );
580  }
581  }
582 
583  // Now find the removed end(s) and stop merging if it is a node:
584  if( aSeg1->GetStart() != dummy_seg.GetStart() && aSeg1->GetStart() != dummy_seg.GetEnd() )
585  {
586  if( testTrackEndpointIsNode( aSeg1, true ) )
587  return false;
588  }
589 
590  if( aSeg1->GetEnd() != dummy_seg.GetStart() && aSeg1->GetEnd() != dummy_seg.GetEnd() )
591  {
592  if( testTrackEndpointIsNode( aSeg1, false ) )
593  return false;
594  }
595 
596  std::shared_ptr<CLEANUP_ITEM> item = std::make_shared<CLEANUP_ITEM>( CLEANUP_MERGE_TRACKS );
597  item->SetItems( aSeg1, aSeg2 );
598  m_itemsList->push_back( item );
599 
600  aSeg2->SetFlags( IS_DELETED );
601 
602  if( !m_dryRun )
603  {
604  m_commit.Modify( aSeg1 );
605  *aSeg1 = dummy_seg;
606 
607  connectivity->Update( aSeg1 );
608 
609  // Clear the status flags here after update.
610  for( auto pad : connectivity->GetConnectedPads( aSeg1 ) )
611  {
612  aSeg1->SetState( BEGIN_ONPAD, pad->HitTest( aSeg1->GetStart() ) );
613  aSeg1->SetState( END_ONPAD, pad->HitTest( aSeg1->GetEnd() ) );
614  }
615 
616  // Merge successful, seg2 has to go away
617  m_brd->Remove( aSeg2 );
618  m_commit.Removed( aSeg2 );
619  }
620 
621  return true;
622 }
COMMIT & Modify(EDA_ITEM *aItem)
Create an undo entry for an item that has been already modified.
Definition: commit.h:103
const wxPoint & GetEnd() const
Definition: pcb_track.h:105
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition: eda_item.h:152
class PCB_ARC, an arc track segment on a copper layer
Definition: typeinfo.h:97
class PAD, a pad in a footprint
Definition: typeinfo.h:89
int GetWidth() const
Definition: pcb_track.h:102
VECTOR2< int > VECTOR2I
Definition: vector2d.h:622
virtual bool IsLocked() const
Definition: board_item.cpp:64
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition: typeinfo.h:77
class PCB_TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:95
BOARD_COMMIT & m_commit
COMMIT & Removed(EDA_ITEM *aItem)
Modify a given item in the model.
Definition: commit.h:96
void SetState(EDA_ITEM_FLAGS type, bool state)
Definition: eda_item.h:141
#define IS_DELETED
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition: board.h:345
class ZONE, a copper pour area
Definition: typeinfo.h:105
#define BEGIN_ONPAD
Pcbnew: flag set for track segment starting on a pad.
std::vector< std::shared_ptr< CLEANUP_ITEM > > * m_itemsList
bool testTrackEndpointIsNode(PCB_TRACK *aTrack, bool aTstStart)
void Remove(BOARD_ITEM *aBoardItem, REMOVE_MODE aMode=REMOVE_MODE::NORMAL) override
Removes an item from the container.
Definition: board.cpp:710
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:96
#define END_ONPAD
Pcbnew: flag set for track segment ending on a pad.
const wxPoint & GetStart() const
Definition: pcb_track.h:108

References BEGIN_ONPAD, CLEANUP_MERGE_TRACKS, END_ONPAD, BOARD::GetConnectivity(), PCB_TRACK::GetEnd(), PCB_TRACK::GetStart(), PCB_TRACK::GetWidth(), IS_DELETED, BOARD_ITEM::IsLocked(), PCB_TRACK::IsPointOnEnds(), m_brd, m_commit, m_dryRun, m_itemsList, COMMIT::Modify(), pad, PCB_ARC_T, PCB_PAD_T, PCB_TRACE_T, PCB_VIA_T, PCB_ZONE_T, BOARD::Remove(), COMMIT::Removed(), PCB_TRACK::SetEnd(), EDA_ITEM::SetFlags(), PCB_TRACK::SetStart(), EDA_ITEM::SetState(), and testTrackEndpointIsNode().

Referenced by cleanup().

◆ removeItems()

void TRACKS_CLEANER::removeItems ( std::set< BOARD_ITEM * > &  aItems)
private

Definition at line 625 of file tracks_cleaner.cpp.

626 {
627  for( auto item : aItems )
628  {
629  m_brd->Remove( item );
630  m_commit.Removed( item );
631  }
632 }
BOARD_COMMIT & m_commit
COMMIT & Removed(EDA_ITEM *aItem)
Modify a given item in the model.
Definition: commit.h:96
void Remove(BOARD_ITEM *aBoardItem, REMOVE_MODE aMode=REMOVE_MODE::NORMAL) override
Removes an item from the container.
Definition: board.cpp:710

References m_brd, m_commit, BOARD::Remove(), and COMMIT::Removed().

Referenced by cleanup(), deleteTracksInPads(), and removeShortingTrackSegments().

◆ removeShortingTrackSegments()

void TRACKS_CLEANER::removeShortingTrackSegments ( )
private

Definition at line 76 of file tracks_cleaner.cpp.

77 {
78  std::shared_ptr<CONNECTIVITY_DATA> connectivity = m_brd->GetConnectivity();
79 
80  std::set<BOARD_ITEM *> toRemove;
81 
82  for( PCB_TRACK* segment : m_brd->Tracks() )
83  {
84  // Assume that the user knows what they are doing
85  if( segment->IsLocked() )
86  continue;
87 
88  for( PAD* testedPad : connectivity->GetConnectedPads( segment ) )
89  {
90  if( segment->GetNetCode() != testedPad->GetNetCode() )
91  {
92  std::shared_ptr<CLEANUP_ITEM> item;
93 
94  if( segment->Type() == PCB_VIA_T )
95  item = std::make_shared<CLEANUP_ITEM>( CLEANUP_SHORTING_VIA );
96  else
97  item = std::make_shared<CLEANUP_ITEM>( CLEANUP_SHORTING_TRACK );
98 
99  item->SetItems( segment );
100  m_itemsList->push_back( item );
101 
102  toRemove.insert( segment );
103  }
104  }
105 
106  for( PCB_TRACK* testedTrack : connectivity->GetConnectedTracks( segment ) )
107  {
108  if( segment->GetNetCode() != testedTrack->GetNetCode() )
109  {
110  std::shared_ptr<CLEANUP_ITEM> item;
111 
112  if( segment->Type() == PCB_VIA_T )
113  item = std::make_shared<CLEANUP_ITEM>( CLEANUP_SHORTING_VIA );
114  else
115  item = std::make_shared<CLEANUP_ITEM>( CLEANUP_SHORTING_TRACK );
116 
117  item->SetItems( segment );
118  m_itemsList->push_back( item );
119 
120  toRemove.insert( segment );
121  }
122  }
123  }
124 
125  if( !m_dryRun )
126  removeItems( toRemove );
127 }
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition: board.h:345
void removeItems(std::set< BOARD_ITEM * > &aItems)
std::vector< std::shared_ptr< CLEANUP_ITEM > > * m_itemsList
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:96
Definition: pad.h:57
TRACKS & Tracks()
Definition: board.h:231

References CLEANUP_SHORTING_TRACK, CLEANUP_SHORTING_VIA, BOARD::GetConnectivity(), m_brd, m_dryRun, m_itemsList, PCB_VIA_T, removeItems(), and BOARD::Tracks().

Referenced by CleanupBoard().

◆ testTrackEndpointIsNode()

bool TRACKS_CLEANER::testTrackEndpointIsNode ( PCB_TRACK aTrack,
bool  aTstStart 
)
private
Returns
true if a track end position is a node, i.e. a end connected to more than one item.
Parameters
aTrackis the track to test.
aTstStart= true ot test the start point of the track or false for end point

Definition at line 130 of file tracks_cleaner.cpp.

131 {
132  // A node is a point where more than 2 items are connected.
133 
134  auto connectivity = m_brd->GetConnectivity();
135  auto items = connectivity->GetConnectivityAlgo()->ItemEntry( aTrack ).GetItems();
136 
137  if( items.empty() )
138  return false;
139 
140  auto citem = items.front();
141 
142  if( !citem->Valid() )
143  return false;
144 
145  auto anchors = citem->Anchors();
146 
147  VECTOR2I refpoint = aTstStart ? aTrack->GetStart() : aTrack->GetEnd();
148 
149  for( const auto& anchor : anchors )
150  {
151  if( anchor->Pos() != refpoint )
152  continue;
153 
154  // The right anchor point is found: if more than one other item
155  // (pad, via, track...) is connected, it is a node:
156  return anchor->ConnectedItemsCount() > 1;
157  }
158 
159  return false;
160 }
const wxPoint & GetEnd() const
Definition: pcb_track.h:105
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition: board.h:345
const wxPoint & GetStart() const
Definition: pcb_track.h:108

References anchor, BOARD::GetConnectivity(), PCB_TRACK::GetEnd(), PCB_TRACK::GetStart(), and m_brd.

Referenced by mergeCollinearSegments().

Member Data Documentation

◆ m_brd

◆ m_commit

BOARD_COMMIT& TRACKS_CLEANER::m_commit
private

Definition at line 99 of file tracks_cleaner.h.

Referenced by deleteDanglingTracks(), mergeCollinearSegments(), and removeItems().

◆ m_dryRun

bool TRACKS_CLEANER::m_dryRun
private

◆ m_itemsList

std::vector<std::shared_ptr<CLEANUP_ITEM> >* TRACKS_CLEANER::m_itemsList
private

The documentation for this class was generated from the following files: