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, REPORTER *aReporter=nullptr)
 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)
 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)
 
const std::vector< BOARD_CONNECTED_ITEM * > & getConnectedItems (PCB_TRACK *aTrack)
 

Private Attributes

BOARDm_brd
 
BOARD_COMMITm_commit
 
bool m_dryRun
 
std::vector< std::shared_ptr< CLEANUP_ITEM > > * m_itemsList
 
REPORTERm_reporter
 
std::map< PCB_TRACK *, std::vector< BOARD_CONNECTED_ITEM * > > m_connectedItemsCache
 

Detailed Description

Definition at line 36 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 m_reporter( nullptr )
43{
44}
REPORTER * m_reporter
std::vector< std::shared_ptr< CLEANUP_ITEM > > * m_itemsList
BOARD_COMMIT & m_commit

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 354 of file tracks_cleaner.cpp.

356{
357 DRC_RTREE rtree;
358
359 for( PCB_TRACK* track : m_brd->Tracks() )
360 {
361 track->ClearFlags( IS_DELETED | SKIP_STRUCT );
362 rtree.Insert( track, track->GetLayer() );
363 }
364
365 std::set<BOARD_ITEM*> toRemove;
366
367 for( PCB_TRACK* track : m_brd->Tracks() )
368 {
369 if( track->HasFlag( IS_DELETED ) || track->IsLocked() )
370 continue;
371
372 if( aDeleteDuplicateVias && track->Type() == PCB_VIA_T )
373 {
374 PCB_VIA* via = static_cast<PCB_VIA*>( track );
375
376 if( via->GetStart() != via->GetEnd() )
377 via->SetEnd( via->GetStart() );
378
379 rtree.QueryColliding( via, via->GetLayer(), via->GetLayer(),
380 // Filter:
381 [&]( BOARD_ITEM* aItem ) -> bool
382 {
383 return aItem->Type() == PCB_VIA_T
384 && !aItem->HasFlag( SKIP_STRUCT )
385 && !aItem->HasFlag( IS_DELETED );
386 },
387 // Visitor:
388 [&]( BOARD_ITEM* aItem ) -> bool
389 {
390 PCB_VIA* other = static_cast<PCB_VIA*>( aItem );
391
392 if( via->GetPosition() == other->GetPosition()
393 && via->GetViaType() == other->GetViaType()
394 && via->GetLayerSet() == other->GetLayerSet() )
395 {
396 auto item = std::make_shared<CLEANUP_ITEM>( CLEANUP_REDUNDANT_VIA );
397 item->SetItems( via );
398 m_itemsList->push_back( item );
399
400 via->SetFlags( IS_DELETED );
401 toRemove.insert( via );
402 }
403
404 return true;
405 } );
406
407 // To delete through Via on THT pads at same location
408 // Examine the list of connected pads: if a through pad is found, the via is redundant
409 for( PAD* pad : m_brd->GetConnectivity()->GetConnectedPads( via ) )
410 {
411 const LSET all_cu = LSET::AllCuMask();
412
413 if( ( pad->GetLayerSet() & all_cu ) == all_cu )
414 {
415 auto item = std::make_shared<CLEANUP_ITEM>( CLEANUP_REDUNDANT_VIA );
416 item->SetItems( via, pad );
417 m_itemsList->push_back( item );
418
419 via->SetFlags( IS_DELETED );
420 toRemove.insert( via );
421 break;
422 }
423 }
424
425 via->SetFlags( SKIP_STRUCT );
426 }
427
428 if( aDeleteNullSegments && track->Type() != PCB_VIA_T )
429 {
430 if( track->IsNull() )
431 {
432 auto item = std::make_shared<CLEANUP_ITEM>( CLEANUP_ZERO_LENGTH_TRACK );
433 item->SetItems( track );
434 m_itemsList->push_back( item );
435
436 track->SetFlags( IS_DELETED );
437 toRemove.insert( track );
438 }
439 }
440
441 if( aDeleteDuplicateSegments && track->Type() == PCB_TRACE_T && !track->IsNull() )
442 {
443 rtree.QueryColliding( track, track->GetLayer(), track->GetLayer(),
444 // Filter:
445 [&]( BOARD_ITEM* aItem ) -> bool
446 {
447 return aItem->Type() == PCB_TRACE_T
448 && !aItem->HasFlag( SKIP_STRUCT )
449 && !aItem->HasFlag( IS_DELETED )
450 && !static_cast<PCB_TRACK*>( aItem )->IsNull();
451 },
452 // Visitor:
453 [&]( BOARD_ITEM* aItem ) -> bool
454 {
455 PCB_TRACK* other = static_cast<PCB_TRACK*>( aItem );
456
457 if( track->IsPointOnEnds( other->GetStart() )
458 && track->IsPointOnEnds( other->GetEnd() )
459 && track->GetWidth() == other->GetWidth()
460 && track->GetLayer() == other->GetLayer() )
461 {
462 auto item = std::make_shared<CLEANUP_ITEM>( CLEANUP_DUPLICATE_TRACK );
463 item->SetItems( track );
464 m_itemsList->push_back( item );
465
466 track->SetFlags( IS_DELETED );
467 toRemove.insert( track );
468 }
469
470 return true;
471 } );
472
473 track->SetFlags( SKIP_STRUCT );
474 }
475 }
476
477 if( !m_dryRun )
478 removeItems( toRemove );
479
480 auto mergeSegments =
481 [&]( std::shared_ptr<CN_CONNECTIVITY_ALGO> connectivity ) -> bool
482 {
483 for( PCB_TRACK* segment : m_brd->Tracks() )
484 {
485 // one can merge only collinear segments, not vias or arcs.
486 if( segment->Type() != PCB_TRACE_T )
487 continue;
488
489 if( segment->HasFlag( IS_DELETED ) ) // already taken into account
490 continue;
491
492 // for each end of the segment:
493 for( CN_ITEM* citem : connectivity->ItemEntry( segment ).GetItems() )
494 {
495 // Do not merge an end which has different width tracks attached -- it's a
496 // common use-case for necking-down a track between pads.
497 std::vector<PCB_TRACK*> sameWidthCandidates;
498 std::vector<PCB_TRACK*> differentWidthCandidates;
499
500 for( CN_ITEM* connected : citem->ConnectedItems() )
501 {
502 if( !connected->Valid() )
503 continue;
504
505 BOARD_CONNECTED_ITEM* candidate = connected->Parent();
506
507 if( candidate->Type() == PCB_TRACE_T
508 && !candidate->HasFlag( IS_DELETED ) )
509 {
510 PCB_TRACK* candidateSegment = static_cast<PCB_TRACK*>( candidate );
511
512 if( candidateSegment->GetWidth() == segment->GetWidth() )
513 {
514 sameWidthCandidates.push_back( candidateSegment );
515 }
516 else
517 {
518 differentWidthCandidates.push_back( candidateSegment );
519 break;
520 }
521 }
522 }
523
524 if( !differentWidthCandidates.empty() )
525 continue;
526
527 for( PCB_TRACK* candidate : sameWidthCandidates )
528 {
529 if( segment->ApproxCollinear( *candidate )
530 && mergeCollinearSegments( segment, candidate ) )
531 {
532 return true;
533 }
534 }
535 }
536 }
537
538 return false;
539 };
540
541 if( aMergeSegments )
542 {
543 do
544 {
545 while( !m_brd->BuildConnectivity() )
546 wxSafeYield();
547
548 m_connectedItemsCache.clear();
549 } while( mergeSegments( m_brd->GetConnectivity()->GetConnectivityAlgo() ) );
550 }
551
552 for( PCB_TRACK* track : m_brd->Tracks() )
553 track->ClearFlags( IS_DELETED | SKIP_STRUCT );
554}
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:70
bool BuildConnectivity(PROGRESS_REPORTER *aReporter=nullptr)
Build or rebuild the board connectivity database for the board, especially the list of connected item...
Definition: board.cpp:162
TRACKS & Tracks()
Definition: board.h:304
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition: board.h:424
CN_ITEM represents a BOARD_CONNETED_ITEM in the connectivity system (ie: a pad, track/arc/via,...
const std::vector< CN_ITEM * > & ConnectedItems() const
Implement an R-tree for fast spatial and layer indexing of connectable items.
Definition: drc_rtree.h:48
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:104
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:211
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:97
bool HasFlag(EDA_ITEM_FLAGS aFlag) const
Definition: eda_item.h:143
LSET is a set of PCB_LAYER_IDs.
Definition: layer_ids.h:530
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:773
Definition: pad.h:59
int GetWidth() const
Definition: pcb_track.h:107
VECTOR2I GetPosition() const override
Definition: pcb_track.h:441
VIATYPE GetViaType() const
Definition: pcb_track.h:393
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: pcb_track.cpp:508
void removeItems(std::set< BOARD_ITEM * > &aItems)
bool mergeCollinearSegments(PCB_TRACK *aSeg1, PCB_TRACK *aSeg2)
helper function merge aTrackRef and aCandidate, when possible, i.e.
std::map< PCB_TRACK *, std::vector< BOARD_CONNECTED_ITEM * > > m_connectedItemsCache
@ CLEANUP_ZERO_LENGTH_TRACK
Definition: cleanup_item.h:41
@ CLEANUP_REDUNDANT_VIA
Definition: cleanup_item.h:36
#define IS_DELETED
#define SKIP_STRUCT
flag indicating that the structure should be ignored
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:102
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:101

References LSET::AllCuMask(), CLEANUP_REDUNDANT_VIA, CLEANUP_ZERO_LENGTH_TRACK, BOARD::GetConnectivity(), PCB_VIA::GetLayerSet(), PCB_VIA::GetPosition(), PCB_VIA::GetViaType(), DRC_RTREE::Insert(), IS_DELETED, m_brd, m_itemsList, pad, PCB_TRACE_T, PCB_VIA_T, DRC_RTREE::QueryColliding(), SKIP_STRUCT, BOARD::Tracks(), 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,
REPORTER aReporter = nullptr 
)

the cleanup function.

Parameters
aDryRun= true to build changes list, false to modify the board
aItemsList= list of modified items
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
aReporteris a REPORTER to print activity and info

Definition at line 53 of file tracks_cleaner.cpp.

58{
59 m_reporter = aReporter;
60 bool has_deleted = false;
61
62 m_dryRun = aDryRun;
63 m_itemsList = aItemsList;
64
65 if( m_reporter )
66 {
67 if( aDryRun )
68 m_reporter->Report( _( "Checking null tracks and vias..." ) );
69 else
70 m_reporter->Report( _( "Removing null tracks and vias..." ) );
71
72 wxSafeYield(); // Timeslice to update UI
73 }
74
75 bool removeNullSegments = aMergeSegments || aRemoveMisConnected;
76 cleanup( aCleanVias, removeNullSegments, aMergeSegments /* dup segments*/, aMergeSegments );
77
78 if( m_reporter )
79 {
80 if( aDryRun )
81 m_reporter->Report( _( "Checking redundant tracks..." ) );
82 else
83 m_reporter->Report( _( "Removing redundant tracks..." ) );
84
85 wxSafeYield(); // Timeslice to update UI
86 }
87
88 cleanup( false, false, true, aMergeSegments );
89
90 if( aRemoveMisConnected )
91 {
92 if( m_reporter )
93 {
94 if( aDryRun )
95 m_reporter->Report( _( "Checking shorting tracks..." ) );
96 else
97 m_reporter->Report( _( "Removing shorting tracks..." ) );
98
99 wxSafeYield(); // Timeslice to update UI
100 }
101
103 }
104
105 if( aDeleteTracksinPad )
106 {
107 if( m_reporter )
108 {
109 if( aDryRun )
110 m_reporter->Report( _( "Checking tracks in pads..." ) );
111 else
112 m_reporter->Report( _( "Removing tracks in pads..." ) );
113
114 wxSafeYield(); // Timeslice to update UI
115 }
116
118 }
119
120 if( aDeleteUnconnected || aDeleteDanglingVias )
121 {
122 if( m_reporter )
123 {
124 if( aDryRun )
125 {
126 m_reporter->Report( _( "Checking dangling tracks and vias..." ) );
127 }
128 else
129 {
130 if( aDeleteUnconnected )
131 m_reporter->Report( _( "Removing dangling tracks..." ) );
132
133 if( aDeleteDanglingVias )
134 m_reporter->Report( _( "Removing dangling vias..." ) );
135 }
136
137 wxSafeYield(); // Timeslice to update UI
138 }
139
140 has_deleted = deleteDanglingTracks( aDeleteUnconnected, aDeleteDanglingVias );
141 }
142
143 if( has_deleted && aMergeSegments )
144 {
145 if( m_reporter )
146 {
147 if( aDryRun )
148 m_reporter->Report( _( "Checking collinear tracks..." ) );
149 else
150 m_reporter->Report( _( "Merging collinear tracks..." ) );
151
152 wxSafeYield(); // Timeslice to update UI
153 }
154
155 cleanup( false, false, false, true );
156 }
157}
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)=0
Report a string with a given severity.
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.
void removeShortingTrackSegments()
#define _(s)

References _, cleanup(), deleteDanglingTracks(), deleteTracksInPads(), m_dryRun, m_itemsList, m_reporter, removeShortingTrackSegments(), and REPORTER::Report().

Referenced by BOOST_FIXTURE_TEST_CASE(), and DIALOG_CLEANUP_TRACKS_AND_VIAS::doCleanup().

◆ 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 247 of file tracks_cleaner.cpp.

248{
249 bool item_erased = false;
250 bool modified = false;
251
252 if( !aTrack && !aVia )
253 return false;
254
255 do // Iterate when at least one track is deleted
256 {
257 item_erased = false;
258 // Ensure the connectivity is up to date, especially after removing a dangling segment
260
261 // Keep a duplicate deque to all deleting in the primary
262 std::deque<PCB_TRACK*> temp_tracks( m_brd->Tracks() );
263
264 for( PCB_TRACK* track : temp_tracks )
265 {
266 if( track->IsLocked() || ( track->GetFlags() & IS_DELETED ) > 0 )
267 continue;
268
269 if( !aVia && track->Type() == PCB_VIA_T )
270 continue;
271
272 if( !aTrack && ( track->Type() == PCB_TRACE_T || track->Type() == PCB_ARC_T ) )
273 continue;
274
275 // Test if a track (or a via) endpoint is not connected to another track or zone.
276 if( m_brd->GetConnectivity()->TestTrackEndpointDangling( track ) )
277 {
278 std::shared_ptr<CLEANUP_ITEM> item;
279
280 if( track->Type() == PCB_VIA_T )
281 item = std::make_shared<CLEANUP_ITEM>( CLEANUP_DANGLING_VIA );
282 else
283 item = std::make_shared<CLEANUP_ITEM>( CLEANUP_DANGLING_TRACK );
284
285 item->SetItems( track );
286 m_itemsList->push_back( item );
287 track->SetFlags( IS_DELETED );
288
289 // keep iterating, because a track connected to the deleted track
290 // now perhaps is not connected and should be deleted
291 item_erased = true;
292
293 if( !m_dryRun )
294 {
295 m_brd->Remove( track );
296 m_commit.Removed( track );
297 modified = true;
298 }
299 }
300 }
301 } while( item_erased ); // A segment was erased: test for some new dangling segments
302
303 return modified;
304}
void Remove(BOARD_ITEM *aBoardItem, REMOVE_MODE aMode=REMOVE_MODE::NORMAL) override
Removes an item from the container.
Definition: board.cpp:863
COMMIT & Removed(EDA_ITEM *aItem)
Modify a given item in the model.
Definition: commit.h:96
@ CLEANUP_DANGLING_VIA
Definition: cleanup_item.h:40
@ CLEANUP_DANGLING_TRACK
Definition: cleanup_item.h:39
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition: typeinfo.h:103

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 307 of file tracks_cleaner.cpp.

308{
309 std::set<BOARD_ITEM*> toRemove;
310
311 // Delete tracks that start and end on the same pad
312 std::shared_ptr<CONNECTIVITY_DATA> connectivity = m_brd->GetConnectivity();
313
314 for( PCB_TRACK* track : m_brd->Tracks() )
315 {
316 if( track->IsLocked() )
317 continue;
318
319 if( track->Type() == PCB_VIA_T )
320 continue;
321
322 // Mark track if connected to pads
323 for( PAD* pad : connectivity->GetConnectedPads( track ) )
324 {
325 if( pad->HitTest( track->GetStart() ) && pad->HitTest( track->GetEnd() ) )
326 {
327 SHAPE_POLY_SET poly;
328 track->TransformShapeToPolygon( poly, track->GetLayer(), 0, ARC_HIGH_DEF,
329 ERROR_INSIDE );
330
331 poly.BooleanSubtract( *pad->GetEffectivePolygon(), SHAPE_POLY_SET::PM_FAST );
332
333 if( poly.IsEmpty() )
334 {
335 auto item = std::make_shared<CLEANUP_ITEM>( CLEANUP_TRACK_IN_PAD );
336 item->SetItems( track );
337 m_itemsList->push_back( item );
338
339 toRemove.insert( track );
340 track->SetFlags( IS_DELETED );
341 }
342 }
343 }
344 }
345
346 if( !m_dryRun )
347 removeItems( toRemove );
348}
constexpr int ARC_HIGH_DEF
Definition: base_units.h:121
Represent a set of closed polygons.
void BooleanSubtract(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Perform boolean polyset intersection For aFastMode meaning, see function booleanOp.
bool IsEmpty() const
@ CLEANUP_TRACK_IN_PAD
Definition: cleanup_item.h:42
@ ERROR_INSIDE

References ARC_HIGH_DEF, 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().

◆ getConnectedItems()

const std::vector< BOARD_CONNECTED_ITEM * > & TRACKS_CLEANER::getConnectedItems ( PCB_TRACK aTrack)
private

Definition at line 557 of file tracks_cleaner.cpp.

558{
559 const std::shared_ptr<CONNECTIVITY_DATA>& connectivity = m_brd->GetConnectivity();
560
561 if( m_connectedItemsCache.count( aTrack ) == 0 )
562 {
563 m_connectedItemsCache[ aTrack ] =
564 connectivity->GetConnectedItems( aTrack, { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T,
566 }
567
568 return m_connectedItemsCache[ aTrack ];
569}
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition: typeinfo.h:112
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition: typeinfo.h:87

References BOARD::GetConnectivity(), m_brd, m_connectedItemsCache, PCB_ARC_T, PCB_PAD_T, PCB_TRACE_T, PCB_VIA_T, and PCB_ZONE_T.

Referenced by mergeCollinearSegments().

◆ mergeCollinearSegments()

bool TRACKS_CLEANER::mergeCollinearSegments ( PCB_TRACK aSeg1,
PCB_TRACK aSeg2 
)
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 572 of file tracks_cleaner.cpp.

573{
574 if( aSeg1->IsLocked() || aSeg2->IsLocked() )
575 return false;
576
577 // Collect the unique points where the two tracks are connected to other items
578 std::set<VECTOR2I> pts;
579
580 auto collectPts =
581 [&]( BOARD_CONNECTED_ITEM* citem )
582 {
583 if( citem->Type() == PCB_TRACE_T || citem->Type() == PCB_ARC_T
584 || citem->Type() == PCB_VIA_T )
585 {
586 PCB_TRACK* track = static_cast<PCB_TRACK*>( citem );
587
588 if( track->IsPointOnEnds( aSeg1->GetStart() ) )
589 pts.emplace( aSeg1->GetStart() );
590
591 if( track->IsPointOnEnds( aSeg1->GetEnd() ) )
592 pts.emplace( aSeg1->GetEnd() );
593
594 if( track->IsPointOnEnds( aSeg2->GetStart() ) )
595 pts.emplace( aSeg2->GetStart() );
596
597 if( track->IsPointOnEnds( aSeg2->GetEnd() ) )
598 pts.emplace( aSeg2->GetEnd() );
599 }
600 else
601 {
602 if( citem->HitTest( aSeg1->GetStart(), ( aSeg1->GetWidth() + 1 ) / 2 ) )
603 pts.emplace( aSeg1->GetStart() );
604
605 if( citem->HitTest( aSeg1->GetEnd(), ( aSeg1->GetWidth() + 1 ) / 2 ) )
606 pts.emplace( aSeg1->GetEnd() );
607
608 if( citem->HitTest( aSeg2->GetStart(), ( aSeg2->GetWidth() + 1 ) / 2 ) )
609 pts.emplace( aSeg2->GetStart() );
610
611 if( citem->HitTest( aSeg2->GetEnd(), ( aSeg2->GetWidth() + 1 ) / 2 ) )
612 pts.emplace( aSeg2->GetEnd() );
613 }
614 };
615
616 for( BOARD_CONNECTED_ITEM* item : getConnectedItems( aSeg1 ) )
617 {
618 if( item != aSeg1 && item != aSeg2 )
619 collectPts( item );
620 }
621
622 for( BOARD_CONNECTED_ITEM* item : getConnectedItems( aSeg2 ) )
623 {
624 if( item != aSeg1 && item != aSeg2 )
625 collectPts( item );
626 }
627
628 // This means there is a node in the center
629 if( pts.size() > 2 )
630 return false;
631
632 // Verify the removed point after merging is not a node.
633 // If it is a node (i.e. if more than one other item is connected, the segments cannot be merged
634 PCB_TRACK dummy_seg( *aSeg1 );
635
636 // Calculate the new ends of the segment to merge, and store them to dummy_seg:
637 int min_x = std::min( aSeg1->GetStart().x,
638 std::min( aSeg1->GetEnd().x, std::min( aSeg2->GetStart().x, aSeg2->GetEnd().x ) ) );
639 int min_y = std::min( aSeg1->GetStart().y,
640 std::min( aSeg1->GetEnd().y, std::min( aSeg2->GetStart().y, aSeg2->GetEnd().y ) ) );
641 int max_x = std::max( aSeg1->GetStart().x,
642 std::max( aSeg1->GetEnd().x, std::max( aSeg2->GetStart().x, aSeg2->GetEnd().x ) ) );
643 int max_y = std::max( aSeg1->GetStart().y,
644 std::max( aSeg1->GetEnd().y, std::max( aSeg2->GetStart().y, aSeg2->GetEnd().y ) ) );
645
646 if( ( aSeg1->GetStart().x > aSeg1->GetEnd().x )
647 == ( aSeg1->GetStart().y > aSeg1->GetEnd().y ) )
648 {
649 dummy_seg.SetStart( VECTOR2I( min_x, min_y ) );
650 dummy_seg.SetEnd( VECTOR2I( max_x, max_y ) );
651 }
652 else
653 {
654 dummy_seg.SetStart( VECTOR2I( min_x, max_y ) );
655 dummy_seg.SetEnd( VECTOR2I( max_x, min_y ) );
656 }
657
658 // If the existing connected points are not the same as the points generated by our
659 // min/max alg, then assign the missing points to the end closest. This ensures that
660 // our replacement track is still connected
661 for( auto& pt : pts )
662 {
663 if( !dummy_seg.IsPointOnEnds( wxPoint( pt.x, pt.y ) ) )
664 {
665 if( ( VECTOR2I( dummy_seg.GetStart() ) - pt ).SquaredEuclideanNorm() <
666 ( VECTOR2I( dummy_seg.GetEnd() ) - pt ).SquaredEuclideanNorm() )
667 dummy_seg.SetStart( wxPoint( pt.x, pt.y ) );
668 else
669 dummy_seg.SetEnd( wxPoint( pt.x, pt.y ) );
670 }
671 }
672
673 // Now find the removed end(s) and stop merging if it is a node:
674 if( aSeg1->GetStart() != dummy_seg.GetStart() && aSeg1->GetStart() != dummy_seg.GetEnd() )
675 {
676 if( testTrackEndpointIsNode( aSeg1, true ) )
677 return false;
678 }
679
680 if( aSeg1->GetEnd() != dummy_seg.GetStart() && aSeg1->GetEnd() != dummy_seg.GetEnd() )
681 {
682 if( testTrackEndpointIsNode( aSeg1, false ) )
683 return false;
684 }
685
686 std::shared_ptr<CLEANUP_ITEM> item = std::make_shared<CLEANUP_ITEM>( CLEANUP_MERGE_TRACKS );
687 item->SetItems( aSeg1, aSeg2 );
688 m_itemsList->push_back( item );
689
690 aSeg2->SetFlags( IS_DELETED );
691
692 if( !m_dryRun )
693 {
694 m_commit.Modify( aSeg1 );
695 *aSeg1 = dummy_seg;
696
697 m_brd->GetConnectivity()->Update( aSeg1 );
698
699 // Clear the status flags here after update.
700 for( PAD* pad : m_brd->GetConnectivity()->GetConnectedPads( aSeg1 ) )
701 {
702 aSeg1->SetState( BEGIN_ONPAD, pad->HitTest( aSeg1->GetStart() ) );
703 aSeg1->SetState( END_ONPAD, pad->HitTest( aSeg1->GetEnd() ) );
704 }
705
706 // Merge successful, seg2 has to go away
707 m_brd->Remove( aSeg2 );
708 m_commit.Removed( aSeg2 );
709 }
710
711 return true;
712}
virtual bool IsLocked() const
Definition: board_item.cpp:71
COMMIT & Modify(EDA_ITEM *aItem)
Create an undo entry for an item that has been already modified.
Definition: commit.h:103
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition: eda_item.h:139
void SetState(EDA_ITEM_FLAGS type, bool state)
Definition: eda_item.h:128
const VECTOR2I & GetStart() const
Definition: pcb_track.h:113
const VECTOR2I & GetEnd() const
Definition: pcb_track.h:110
EDA_ITEM_FLAGS IsPointOnEnds(const VECTOR2I &point, int min_dist=0) const
Function IsPointOnEnds returns STARTPOINT if point if near (dist = min_dist) start point,...
Definition: pcb_track.cpp:237
const std::vector< BOARD_CONNECTED_ITEM * > & getConnectedItems(PCB_TRACK *aTrack)
bool testTrackEndpointIsNode(PCB_TRACK *aTrack, bool aTstStart)
@ CLEANUP_MERGE_TRACKS
Definition: cleanup_item.h:38
#define BEGIN_ONPAD
Pcbnew: flag set for track segment starting on a pad.
#define END_ONPAD
Pcbnew: flag set for track segment ending on a pad.
VECTOR2< int > VECTOR2I
Definition: vector2d.h:618

References BEGIN_ONPAD, CLEANUP_MERGE_TRACKS, END_ONPAD, getConnectedItems(), 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_TRACE_T, PCB_VIA_T, BOARD::Remove(), COMMIT::Removed(), PCB_TRACK::SetEnd(), EDA_ITEM::SetFlags(), PCB_TRACK::SetStart(), EDA_ITEM::SetState(), testTrackEndpointIsNode(), VECTOR2< T >::x, and VECTOR2< T >::y.

◆ removeItems()

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

Definition at line 715 of file tracks_cleaner.cpp.

716{
717 for( BOARD_ITEM* item : aItems )
718 {
719 m_brd->Remove( item );
720 m_commit.Removed( item );
721 }
722}

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

Referenced by deleteTracksInPads(), and removeShortingTrackSegments().

◆ removeShortingTrackSegments()

void TRACKS_CLEANER::removeShortingTrackSegments ( )
private

Definition at line 160 of file tracks_cleaner.cpp.

161{
162 std::shared_ptr<CONNECTIVITY_DATA> connectivity = m_brd->GetConnectivity();
163
164 std::set<BOARD_ITEM *> toRemove;
165
166 for( PCB_TRACK* segment : m_brd->Tracks() )
167 {
168 // Assume that the user knows what they are doing
169 if( segment->IsLocked() )
170 continue;
171
172 for( PAD* testedPad : connectivity->GetConnectedPads( segment ) )
173 {
174 if( segment->GetNetCode() != testedPad->GetNetCode() )
175 {
176 std::shared_ptr<CLEANUP_ITEM> item;
177
178 if( segment->Type() == PCB_VIA_T )
179 item = std::make_shared<CLEANUP_ITEM>( CLEANUP_SHORTING_VIA );
180 else
181 item = std::make_shared<CLEANUP_ITEM>( CLEANUP_SHORTING_TRACK );
182
183 item->SetItems( segment );
184 m_itemsList->push_back( item );
185
186 toRemove.insert( segment );
187 }
188 }
189
190 for( PCB_TRACK* testedTrack : connectivity->GetConnectedTracks( segment ) )
191 {
192 if( segment->GetNetCode() != testedTrack->GetNetCode() )
193 {
194 std::shared_ptr<CLEANUP_ITEM> item;
195
196 if( segment->Type() == PCB_VIA_T )
197 item = std::make_shared<CLEANUP_ITEM>( CLEANUP_SHORTING_VIA );
198 else
199 item = std::make_shared<CLEANUP_ITEM>( CLEANUP_SHORTING_TRACK );
200
201 item->SetItems( segment );
202 m_itemsList->push_back( item );
203
204 toRemove.insert( segment );
205 }
206 }
207 }
208
209 if( !m_dryRun )
210 removeItems( toRemove );
211}
@ CLEANUP_SHORTING_VIA
Definition: cleanup_item.h:35
@ CLEANUP_SHORTING_TRACK
Definition: cleanup_item.h:34

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 214 of file tracks_cleaner.cpp.

215{
216 // A node is a point where more than 2 items are connected.
217
218 const std::list<CN_ITEM*>& items =
219 m_brd->GetConnectivity()->GetConnectivityAlgo()->ItemEntry( aTrack ).GetItems();
220
221 if( items.empty() )
222 return false;
223
224 CN_ITEM* citem = items.front();
225
226 if( !citem->Valid() )
227 return false;
228
229 const std::vector<std::shared_ptr<CN_ANCHOR>>& anchors = citem->Anchors();
230
231 VECTOR2I refpoint = aTstStart ? aTrack->GetStart() : aTrack->GetEnd();
232
233 for( const std::shared_ptr<CN_ANCHOR>& anchor : anchors )
234 {
235 if( anchor->Pos() != refpoint )
236 continue;
237
238 // The right anchor point is found: if more than one other item
239 // (pad, via, track...) is connected, it is a node:
240 return anchor->ConnectedItemsCount() > 1;
241 }
242
243 return false;
244}
bool Valid() const
std::vector< std::shared_ptr< CN_ANCHOR > > & Anchors()

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

Referenced by mergeCollinearSegments().

Member Data Documentation

◆ m_brd

◆ m_commit

BOARD_COMMIT& TRACKS_CLEANER::m_commit
private

Definition at line 105 of file tracks_cleaner.h.

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

◆ m_connectedItemsCache

std::map<PCB_TRACK*, std::vector<BOARD_CONNECTED_ITEM*> > TRACKS_CLEANER::m_connectedItemsCache
private

Definition at line 111 of file tracks_cleaner.h.

Referenced by getConnectedItems().

◆ m_dryRun

bool TRACKS_CLEANER::m_dryRun
private

◆ m_itemsList

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

◆ m_reporter

REPORTER* TRACKS_CLEANER::m_reporter
private

Definition at line 108 of file tracks_cleaner.h.

Referenced by CleanupBoard().


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