KiCad PCB EDA Suite
Loading...
Searching...
No Matches
tracks_cleaner.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) 2004-2018 Jean-Pierre Charras, jp.charras at wanadoo.fr
5 * Copyright (C) 2011 Wayne Stambaugh <[email protected]>
6 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, you may find one here:
20 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21 * or you may search the http://www.gnu.org website for the version 2 license,
22 * or you may write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
26#include "tracks_cleaner.h"
27
28#include <atomic>
29#include <bit>
30
31#include <board_commit.h>
32#include <cleanup_item.h>
35#include <drc/drc_rtree.h>
36#include <reporter.h>
37#include <thread_pool.h>
38#include <lset.h>
39#include <pad.h>
40#include <pcb_track.h>
41#include <tool/tool_manager.h>
42#include <tools/pcb_actions.h>
44
45
47 m_brd( aPcb ),
48 m_commit( aCommit ),
49 m_dryRun( true ),
50 m_itemsList( nullptr ),
51 m_reporter( nullptr ),
52 m_filter( nullptr )
53{
54}
55
56
57/* Main cleaning function.
58 * Delete
59 * - Redundant points on tracks (merge aligned segments)
60 * - vias on pad
61 * - null length segments
62 */
64 std::vector<std::shared_ptr<CLEANUP_ITEM> >* aItemsList,
65 bool aRemoveMisConnected, bool aCleanVias, bool aMergeSegments,
66 bool aDeleteUnconnected, bool aDeleteTracksinPad,
67 bool aDeleteDanglingVias, REPORTER* aReporter )
68{
69 m_reporter = aReporter;
70 bool has_deleted = false;
71
72 m_dryRun = aDryRun;
73 m_itemsList = aItemsList;
74
75 if( m_reporter )
76 {
77 if( aDryRun )
78 m_reporter->Report( _( "Checking null tracks and vias..." ) );
79 else
80 m_reporter->Report( _( "Removing null tracks and vias..." ) );
81
82 wxSafeYield(); // Timeslice to update UI
83 }
84
85 bool removeNullSegments = aMergeSegments || aRemoveMisConnected;
86 cleanup( aCleanVias, removeNullSegments, aMergeSegments /* dup segments*/, aMergeSegments );
87
88 if( m_reporter )
89 {
90 if( aDryRun )
91 m_reporter->Report( _( "Checking redundant tracks..." ) );
92 else
93 m_reporter->Report( _( "Removing redundant tracks..." ) );
94
95 wxSafeYield(); // Timeslice to update UI
96 }
97
98 // If we didn't remove duplicates above, do it now
99 if( !aMergeSegments )
100 cleanup( false, false, true, false );
101
102 if( aRemoveMisConnected )
103 {
104 if( m_reporter )
105 {
106 if( aDryRun )
107 m_reporter->Report( _( "Checking shorting tracks..." ) );
108 else
109 m_reporter->Report( _( "Removing shorting tracks..." ) );
110
111 wxSafeYield(); // Timeslice to update UI
112 }
113
115 }
116
117 if( aDeleteTracksinPad )
118 {
119 if( m_reporter )
120 {
121 if( aDryRun )
122 m_reporter->Report( _( "Checking tracks in pads..." ) );
123 else
124 m_reporter->Report( _( "Removing tracks in pads..." ) );
125
126 wxSafeYield(); // Timeslice to update UI
127 }
128
130 }
131
132 if( aDeleteUnconnected || aDeleteDanglingVias )
133 {
134 if( m_reporter )
135 {
136 if( aDryRun )
137 {
138 m_reporter->Report( _( "Checking dangling tracks and vias..." ) );
139 }
140 else
141 {
142 if( aDeleteUnconnected )
143 m_reporter->Report( _( "Removing dangling tracks..." ) );
144
145 if( aDeleteDanglingVias )
146 m_reporter->Report( _( "Removing dangling vias..." ) );
147 }
148
149 wxSafeYield(); // Timeslice to update UI
150 }
151
152 has_deleted = deleteDanglingTracks( aDeleteUnconnected, aDeleteDanglingVias );
153 }
154
155 if( has_deleted && aMergeSegments )
156 {
157 if( m_reporter )
158 {
159 if( aDryRun )
160 m_reporter->Report( _( "Checking collinear tracks..." ) );
161 else
162 m_reporter->Report( _( "Merging collinear tracks..." ) );
163
164 wxSafeYield(); // Timeslice to update UI
165 }
166
167 cleanup( false, false, false, true );
168 }
169}
170
171
173{
174 if( !m_filter )
175 return false;
176
177 return (m_filter)( aItem );
178}
179
180
182{
183 std::shared_ptr<CONNECTIVITY_DATA> connectivity = m_brd->GetConnectivity();
184
185 std::set<BOARD_ITEM *> toRemove;
186
187 for( PCB_TRACK* segment : m_brd->Tracks() )
188 {
189 if( segment->IsLocked() || filterItem( segment ) )
190 continue;
191
192 for( PAD* testedPad : connectivity->GetConnectedPads( segment ) )
193 {
194 if( segment->GetNetCode() != testedPad->GetNetCode() )
195 {
196 std::shared_ptr<CLEANUP_ITEM> item;
197
198 if( segment->Type() == PCB_VIA_T )
199 item = std::make_shared<CLEANUP_ITEM>( CLEANUP_SHORTING_VIA );
200 else
201 item = std::make_shared<CLEANUP_ITEM>( CLEANUP_SHORTING_TRACK );
202
203 item->SetItems( segment );
204 m_itemsList->push_back( std::move( item ) );
205
206 toRemove.insert( segment );
207 }
208 }
209
210 for( PCB_TRACK* testedTrack : connectivity->GetConnectedTracks( segment ) )
211 {
212 if( segment->GetNetCode() != testedTrack->GetNetCode() )
213 {
214 std::shared_ptr<CLEANUP_ITEM> item;
215
216 if( segment->Type() == PCB_VIA_T )
217 item = std::make_shared<CLEANUP_ITEM>( CLEANUP_SHORTING_VIA );
218 else
219 item = std::make_shared<CLEANUP_ITEM>( CLEANUP_SHORTING_TRACK );
220
221 item->SetItems( segment );
222 m_itemsList->push_back( std::move( item ) );
223
224 toRemove.insert( segment );
225 }
226 }
227 }
228
229 if( !m_dryRun )
230 removeItems( toRemove );
231}
232
233
234bool TRACKS_CLEANER::testTrackEndpointIsNode( PCB_TRACK* aTrack, bool aTstStart, bool aTstEnd )
235{
236 if( !( aTstStart && aTstEnd ) )
237 return false;
238
239 // A node is a point where more than 2 items are connected. However, we elide tracks that are
240 // collinear with the track being tested.
241 const std::list<CN_ITEM*>& items =
242 m_brd->GetConnectivity()->GetConnectivityAlgo()->ItemEntry( aTrack ).GetItems();
243
244 if( items.empty() )
245 return false;
246
247 int itemcount = 0;
248
249 for( CN_ITEM* item : items )
250 {
251 if( !item->Valid() || item->Parent() == aTrack || item->Parent()->HasFlag( IS_DELETED ) )
252 continue;
253
254 if( item->Parent()->Type() == PCB_TRACE_T &&
255 static_cast<PCB_TRACK*>( item->Parent() )->ApproxCollinear( aTrack ) )
256 {
257 continue;
258 }
259
260 for( const std::shared_ptr<CN_ANCHOR>& anchor : item->Anchors() )
261 {
262 if( ( aTstStart && anchor->Pos() == aTrack->GetStart() )
263 && ( aTstEnd && anchor->Pos() == aTrack->GetEnd() ) )
264 {
265 itemcount++;
266 break;
267 }
268 }
269 }
270
271 return itemcount > 1;
272}
273
274
275bool TRACKS_CLEANER::deleteDanglingTracks( bool aTrack, bool aVia )
276{
277 bool item_erased = false;
278 bool modified = false;
279
280 if( !aTrack && !aVia )
281 return false;
282
283 do // Iterate when at least one track is deleted
284 {
285 item_erased = false;
286 // Ensure the connectivity is up to date, especially after removing a dangling segment
287 m_brd->BuildConnectivity();
288
289 // Keep a duplicate deque to all deleting in the primary
290 std::deque<PCB_TRACK*> temp_tracks( m_brd->Tracks() );
291
292 for( PCB_TRACK* track : temp_tracks )
293 {
294 if( track->HasFlag( IS_DELETED ) || track->IsLocked() || filterItem( track ) )
295 continue;
296
297 if( !aVia && track->Type() == PCB_VIA_T )
298 continue;
299
300 if( !aTrack && ( track->Type() == PCB_TRACE_T || track->Type() == PCB_ARC_T ) )
301 continue;
302
303 // Test if a track (or a via) endpoint is not connected to another track or zone.
304 if( m_brd->GetConnectivity()->TestTrackEndpointDangling( track, false ) )
305 {
306 std::shared_ptr<CLEANUP_ITEM> item;
307
308 if( track->Type() == PCB_VIA_T )
309 item = std::make_shared<CLEANUP_ITEM>( CLEANUP_DANGLING_VIA );
310 else
311 item = std::make_shared<CLEANUP_ITEM>( CLEANUP_DANGLING_TRACK );
312
313 item->SetItems( track );
314 m_itemsList->push_back( std::move( item ) );
315 track->SetFlags( IS_DELETED );
316
317 // keep iterating, because a track connected to the deleted track
318 // now perhaps is not connected and should be deleted
319 item_erased = true;
320
321 if( !m_dryRun )
322 {
323 m_brd->Remove( track );
324 m_commit.Removed( track );
325 modified = true;
326 }
327 }
328 }
329 } while( item_erased ); // A segment was erased: test for some new dangling segments
330
331 return modified;
332}
333
334
336{
337 std::set<BOARD_ITEM*> toRemove;
338
339 // Delete tracks that start and end on the same pad
340 std::shared_ptr<CONNECTIVITY_DATA> connectivity = m_brd->GetConnectivity();
341
342 for( PCB_TRACK* track : m_brd->Tracks() )
343 {
344 if( track->IsLocked() || filterItem( track ) )
345 continue;
346
347 if( track->Type() == PCB_VIA_T )
348 continue;
349
350 // Mark track if connected to pads
351 for( PAD* pad : connectivity->GetConnectedPads( track ) )
352 {
353 if( pad->HitTest( track->GetStart() ) && pad->HitTest( track->GetEnd() ) )
354 {
355 SHAPE_POLY_SET poly;
356 track->TransformShapeToPolygon( poly, track->GetLayer(), 0, track->GetMaxError(),
357 ERROR_INSIDE );
358
359 poly.BooleanSubtract( *pad->GetEffectivePolygon( track->GetLayer(), ERROR_INSIDE ) );
360
361 if( poly.IsEmpty() )
362 {
363 auto item = std::make_shared<CLEANUP_ITEM>( CLEANUP_TRACK_IN_PAD );
364 item->SetItems( track );
365 m_itemsList->push_back( std::move( item ) );
366
367 toRemove.insert( track );
368 track->SetFlags( IS_DELETED );
369 }
370 }
371 }
372 }
373
374 if( !m_dryRun )
375 removeItems( toRemove );
376}
377
378
382void TRACKS_CLEANER::cleanup( bool aDeleteDuplicateVias, bool aDeleteNullSegments,
383 bool aDeleteDuplicateSegments, bool aMergeSegments )
384{
385 DRC_RTREE rtree;
386
387 for( PCB_TRACK* track : m_brd->Tracks() )
388 {
389 track->ClearFlags( IS_DELETED | SKIP_STRUCT );
390 rtree.Insert( track, track->GetLayer() );
391 }
392
393 rtree.Build();
394
395 std::set<BOARD_ITEM*> toRemove;
396
397 for( PCB_TRACK* track : m_brd->Tracks() )
398 {
399 if( track->HasFlag( IS_DELETED ) || track->IsLocked() || filterItem( track ) )
400 continue;
401
402 if( aDeleteDuplicateVias && track->Type() == PCB_VIA_T )
403 {
404 PCB_VIA* via = static_cast<PCB_VIA*>( track );
405
406 if( via->GetStart() != via->GetEnd() )
407 via->SetEnd( via->GetStart() );
408
409 rtree.QueryColliding( via, via->GetLayer(), via->GetLayer(),
410 // Filter:
411 [&]( BOARD_ITEM* aItem ) -> bool
412 {
413 return aItem->Type() == PCB_VIA_T
414 && !aItem->HasFlag( SKIP_STRUCT )
415 && !aItem->HasFlag( IS_DELETED );
416 },
417 // Visitor:
418 [&]( BOARD_ITEM* aItem ) -> bool
419 {
420 PCB_VIA* other = static_cast<PCB_VIA*>( aItem );
421
422 if( via->GetPosition() == other->GetPosition()
423 && via->GetViaType() == other->GetViaType()
424 && via->GetLayerSet() == other->GetLayerSet() )
425 {
426 auto item = std::make_shared<CLEANUP_ITEM>( CLEANUP_REDUNDANT_VIA );
427 item->SetItems( via );
428 m_itemsList->push_back( std::move( item ) );
429
430 via->SetFlags( IS_DELETED );
431 toRemove.insert( via );
432 }
433
434 return true;
435 } );
436
437 // To delete through Via on THT pads at same location
438 // Examine the list of connected pads: if a through pad is found, the via is redundant
439 for( PAD* pad : m_brd->GetConnectivity()->GetConnectedPads( via ) )
440 {
441 const LSET all_cu = LSET::AllCuMask( m_brd->GetCopperLayerCount() );
442
443 if( ( pad->GetLayerSet() & all_cu ) == all_cu )
444 {
445 auto item = std::make_shared<CLEANUP_ITEM>( CLEANUP_REDUNDANT_VIA );
446 item->SetItems( via, pad );
447 m_itemsList->push_back( std::move( item ) );
448
449 via->SetFlags( IS_DELETED );
450 toRemove.insert( via );
451 break;
452 }
453 }
454
455 via->SetFlags( SKIP_STRUCT );
456 }
457
458 if( aDeleteNullSegments && track->Type() != PCB_VIA_T )
459 {
460 if( track->IsNull() )
461 {
462 auto item = std::make_shared<CLEANUP_ITEM>( CLEANUP_ZERO_LENGTH_TRACK );
463 item->SetItems( track );
464 m_itemsList->push_back( std::move( item ) );
465
466 track->SetFlags( IS_DELETED );
467 toRemove.insert( track );
468 }
469 }
470
471 if( aDeleteDuplicateSegments && track->Type() == PCB_TRACE_T && !track->IsNull() )
472 {
473 rtree.QueryColliding( track, track->GetLayer(), track->GetLayer(),
474 // Filter:
475 [&]( BOARD_ITEM* aItem ) -> bool
476 {
477 return aItem->Type() == PCB_TRACE_T
478 && !aItem->HasFlag( SKIP_STRUCT )
479 && !aItem->HasFlag( IS_DELETED )
480 && !static_cast<PCB_TRACK*>( aItem )->IsNull();
481 },
482 // Visitor:
483 [&]( BOARD_ITEM* aItem ) -> bool
484 {
485 PCB_TRACK* other = static_cast<PCB_TRACK*>( aItem );
486
487 if( track->IsPointOnEnds( other->GetStart() )
488 && track->IsPointOnEnds( other->GetEnd() )
489 && track->GetWidth() == other->GetWidth()
490 && track->GetLayer() == other->GetLayer() )
491 {
492 auto item = std::make_shared<CLEANUP_ITEM>( CLEANUP_DUPLICATE_TRACK );
493 item->SetItems( track );
494 m_itemsList->push_back( std::move( item ) );
495
496 track->SetFlags( IS_DELETED );
497 toRemove.insert( track );
498 }
499
500 return true;
501 } );
502
503 track->SetFlags( SKIP_STRUCT );
504 }
505 }
506
507 if( !m_dryRun )
508 removeItems( toRemove );
509
510
511
512
513 auto mergeSegments = [&]( std::shared_ptr<CN_CONNECTIVITY_ALGO> connectivity ) -> bool
514 {
515 auto track_loop = [&]( int aStart, int aEnd ) -> std::vector<std::pair<PCB_TRACK*, PCB_TRACK*>>
516 {
517 std::vector<std::pair<PCB_TRACK*, PCB_TRACK*>> tracks;
518
519 for( int ii = aStart; ii < aEnd; ++ii )
520 {
521 PCB_TRACK* segment = m_brd->Tracks()[ii];
522
523 // one can merge only collinear segments, not vias or arcs.
524 if( segment->Type() != PCB_TRACE_T )
525 continue;
526
527 if( segment->HasFlag( IS_DELETED ) ) // already taken into account
528 continue;
529
530 if( filterItem( segment ) )
531 continue;
532
533 // for each end of the segment:
534 auto& cnItems = connectivity->ItemEntry( segment ).GetItems();
535
536 for( CN_ITEM* citem : cnItems )
537 {
538 // Do not merge an end which has different width tracks attached -- it's a
539 // common use-case for necking-down a track between pads.
540 std::vector<PCB_TRACK*> sameWidthCandidates;
541 std::vector<PCB_TRACK*> differentWidthCandidates;
542
543 for( CN_ITEM* connected : citem->ConnectedItems() )
544 {
545 if( !connected->Valid() )
546 continue;
547
548 BOARD_CONNECTED_ITEM* candidate = connected->Parent();
549
550 if( candidate->Type() == PCB_TRACE_T && !candidate->HasFlag( IS_DELETED )
551 && !filterItem( candidate ) )
552 {
553 PCB_TRACK* candidateSegment = static_cast<PCB_TRACK*>( candidate );
554
555 if( candidateSegment->GetWidth() == segment->GetWidth() )
556 {
557 sameWidthCandidates.push_back( candidateSegment );
558 }
559 else
560 {
561 differentWidthCandidates.push_back( candidateSegment );
562 break;
563 }
564 }
565 }
566
567 if( !differentWidthCandidates.empty() )
568 continue;
569
570 for( PCB_TRACK* candidate : sameWidthCandidates )
571 {
572 if( candidate < segment ) // avoid duplicate merges
573 continue;
574
575 if( segment->ApproxCollinear( *candidate )
576 && testMergeCollinearSegments( segment, candidate ) )
577 {
578 tracks.emplace_back( segment, candidate );
579 break;
580 }
581 }
582 }
583 }
584
585 return tracks;
586 };
587
588 // The idea here is to parallelize the loop that does not modify the connectivity
589 // and extract all of the pairs of segments that might be merged. Then, perform
590 // the actual merge in the main loop.
592 auto merge_returns = tp.submit_blocks( 0, m_brd->Tracks().size(), track_loop );
593 bool retval = false;
594
595 // Drain every worker before mutating any track flags. mergeCollinearSegments writes
596 // IS_DELETED on aSeg2, and worker threads still in track_loop read the same flags via
597 // HasFlag and via the PCB_TRACK copy constructor in testMergeCollinearSegments.
598 std::vector<std::pair<PCB_TRACK*, PCB_TRACK*>> mergePairs;
599
600 for( auto& ret : merge_returns )
601 {
602 if( ret.valid() )
603 {
604 std::vector<std::pair<PCB_TRACK*, PCB_TRACK*>> pairs = ret.get();
605 mergePairs.insert( mergePairs.end(), pairs.begin(), pairs.end() );
606 }
607 }
608
609 for( auto& [seg1, seg2] : mergePairs )
610 {
611 retval = true;
612
613 if( seg1->HasFlag( IS_DELETED ) || seg2->HasFlag( IS_DELETED ) )
614 continue;
615
616 mergeCollinearSegments( seg1, seg2 );
617 }
618
619 return retval;
620 };
621
622 if( aMergeSegments )
623 {
624 do
625 {
626 while( !m_brd->BuildConnectivity() )
627 wxSafeYield();
628
629 // BuildConnectivity adds items but doesn't establish connections between them.
630 // RecalculateRatsnest triggers searchConnections which actually finds and links
631 // connected items in the connectivity graph.
632 m_brd->GetConnectivity()->RecalculateRatsnest();
633
634 std::lock_guard lock( m_mutex );
635 m_connectedItemsCache.clear();
636 } while( mergeSegments( m_brd->GetConnectivity()->GetConnectivityAlgo() ) );
637 }
638
639 for( PCB_TRACK* track : m_brd->Tracks() )
640 track->ClearFlags( IS_DELETED | SKIP_STRUCT );
641}
642
643
644const std::vector<BOARD_CONNECTED_ITEM*>& TRACKS_CLEANER::getConnectedItems( PCB_TRACK* aTrack )
645{
646 const std::shared_ptr<CONNECTIVITY_DATA>& connectivity = m_brd->GetConnectivity();
647 std::lock_guard lock( m_mutex );
648
649 if( !m_connectedItemsCache.contains( aTrack ) )
650 m_connectedItemsCache[aTrack] = connectivity->GetConnectedItems( aTrack );
651
652 return m_connectedItemsCache.at( aTrack );
653}
654
655
657{
658 if( aSeg1->IsLocked() || aSeg2->IsLocked() )
659 return false;
660
661 // Collect the unique points where the two tracks are connected to other items
662 const unsigned p1s = 1 << 0;
663 const unsigned p1e = 1 << 1;
664 const unsigned p2s = 1 << 2;
665 const unsigned p2e = 1 << 3;
666 std::vector<VECTOR2I> pts = { aSeg1->GetStart(), aSeg1->GetEnd(), aSeg2->GetStart(), aSeg2->GetEnd() };
667 std::atomic<unsigned> flags = 0;
668
669 auto collectPtsSeg1 =
670 [&]( BOARD_CONNECTED_ITEM* citem )
671 {
672 if( std::popcount( flags.load() ) > 2 )
673 return;
674
675 if( citem->Type() == PCB_TRACE_T || citem->Type() == PCB_ARC_T
676 || citem->Type() == PCB_VIA_T )
677 {
678 PCB_TRACK* track = static_cast<PCB_TRACK*>( citem );
679
680 if( track->IsPointOnEnds( aSeg1->GetStart() ) )
681 flags |= p1s;
682
683 if( track->IsPointOnEnds( aSeg1->GetEnd() ) )
684 flags |= p1e;
685 }
686 else
687 {
688 if( !( flags & p1s ) && citem->HitTest( aSeg1->GetStart(), ( aSeg1->GetWidth() + 1 ) / 2 ) )
689 flags |= p1s;
690
691 if( !( flags & p1e ) && citem->HitTest( aSeg1->GetEnd(), ( aSeg1->GetWidth() + 1 ) / 2 ) )
692 flags |= p1e;
693 }
694 };
695
696 auto collectPtsSeg2 =
697 [&]( BOARD_CONNECTED_ITEM* citem )
698 {
699 if( std::popcount( flags.load() ) > 2 )
700 return;
701
702 if( citem->Type() == PCB_TRACE_T || citem->Type() == PCB_ARC_T
703 || citem->Type() == PCB_VIA_T )
704 {
705 PCB_TRACK* track = static_cast<PCB_TRACK*>( citem );
706
707 if( track->IsPointOnEnds( aSeg2->GetStart() ) )
708 flags |= p2s;
709
710 if( track->IsPointOnEnds( aSeg2->GetEnd() ) )
711 flags |= p2e;
712 }
713 else
714 {
715 if( !( flags & p2s ) && citem->HitTest( aSeg2->GetStart(), ( aSeg2->GetWidth() + 1 ) / 2 ) )
716 flags |= p2s;
717
718 if( !( flags & p2e ) && citem->HitTest( aSeg2->GetEnd(), ( aSeg2->GetWidth() + 1 ) / 2 ) )
719 flags |= p2e;
720 }
721 };
722
723 for( BOARD_CONNECTED_ITEM* item : getConnectedItems( aSeg1 ) )
724 {
725 if( item->HasFlag( IS_DELETED ) )
726 continue;
727
728 if( item != aSeg1 && item != aSeg2 )
729 collectPtsSeg1( item );
730 }
731
732 for( BOARD_CONNECTED_ITEM* item : getConnectedItems( aSeg2 ) )
733 {
734 if( item->HasFlag( IS_DELETED ) )
735 continue;
736
737 if( item != aSeg1 && item != aSeg2 )
738 collectPtsSeg2( item );
739 }
740
741 // This means there is a node in the center
742 if( std::popcount( flags.load() ) > 2 )
743 return false;
744
745 // Verify the removed point after merging is not a node.
746 // If it is a node (i.e. if more than one other item is connected, the segments cannot be merged
747
748 PCB_TRACK dummy_seg( *aSeg1 );
749
750 if( !aDummySeg )
751 aDummySeg = &dummy_seg;
752
753 // Calculate the new ends of the segment to merge, and store them to dummy_seg:
754 int min_x = std::min( aSeg1->GetStart().x,
755 std::min( aSeg1->GetEnd().x, std::min( aSeg2->GetStart().x, aSeg2->GetEnd().x ) ) );
756 int min_y = std::min( aSeg1->GetStart().y,
757 std::min( aSeg1->GetEnd().y, std::min( aSeg2->GetStart().y, aSeg2->GetEnd().y ) ) );
758 int max_x = std::max( aSeg1->GetStart().x,
759 std::max( aSeg1->GetEnd().x, std::max( aSeg2->GetStart().x, aSeg2->GetEnd().x ) ) );
760 int max_y = std::max( aSeg1->GetStart().y,
761 std::max( aSeg1->GetEnd().y, std::max( aSeg2->GetStart().y, aSeg2->GetEnd().y ) ) );
762
763 if( ( aSeg1->GetStart().x > aSeg1->GetEnd().x )
764 == ( aSeg1->GetStart().y > aSeg1->GetEnd().y ) )
765 {
766 aDummySeg->SetStart( VECTOR2I( min_x, min_y ) );
767 aDummySeg->SetEnd( VECTOR2I( max_x, max_y ) );
768 }
769 else
770 {
771 aDummySeg->SetStart( VECTOR2I( min_x, max_y ) );
772 aDummySeg->SetEnd( VECTOR2I( max_x, min_y ) );
773 }
774
775 // The new ends of the segment must be connected to all of the same points as the original
776 // segments. If not, the segments cannot be merged.
777 for( unsigned i = 0; i < 4; ++i )
778 {
779 if( ( flags & ( 1 << i ) ) && !aDummySeg->IsPointOnEnds( pts[i] ) )
780 return false;
781 }
782
783 // Now find the removed end(s) and stop merging if it is a node:
784 return !testTrackEndpointIsNode( aSeg1, aDummySeg->IsPointOnEnds( aSeg1->GetStart() ),
785 aDummySeg->IsPointOnEnds( aSeg1->GetEnd() ) );
786}
787
789{
790 PCB_TRACK dummy_seg( *aSeg1 );
791
792 if( !testMergeCollinearSegments( aSeg1, aSeg2, &dummy_seg ) )
793 return false;
794
795 std::shared_ptr<CLEANUP_ITEM> item = std::make_shared<CLEANUP_ITEM>( CLEANUP_MERGE_TRACKS );
796 item->SetItems( aSeg1, aSeg2 );
797 m_itemsList->push_back( std::move( item ) );
798
799 aSeg2->SetFlags( IS_DELETED );
800
801 if( !m_dryRun )
802 {
803 m_commit.Modify( aSeg1 );
804
805 *aSeg1 = dummy_seg;
806
807 m_brd->GetConnectivity()->Update( aSeg1 );
808
809 // Merge successful, seg2 has to go away
810 m_brd->Remove( aSeg2 );
811 m_commit.Removed( aSeg2 );
812 }
813
814 return true;
815}
816
817
818void TRACKS_CLEANER::removeItems( std::set<BOARD_ITEM*>& aItems )
819{
820 for( BOARD_ITEM* item : aItems )
821 {
822 m_brd->Remove( item );
823 m_commit.Removed( item );
824 }
825}
@ ERROR_INSIDE
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:84
bool IsLocked() const override
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:323
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:49
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:229
void Build()
Finalize all pending inserts by bulk-building packed R-trees from the staged items.
Definition drc_rtree.h:168
void Insert(BOARD_ITEM *aItem, PCB_LAYER_ID aLayer, int aWorstClearance=0, bool aAtomicTables=false)
Insert an item into the tree on a particular layer with an optional worst clearance.
Definition drc_rtree.h:95
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:149
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:112
bool HasFlag(EDA_ITEM_FLAGS aFlag) const
Definition eda_item.h:153
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static const LSET & AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Definition lset.cpp:608
Definition pad.h:55
void SetEnd(const VECTOR2I &aEnd)
Definition pcb_track.h:93
void SetStart(const VECTOR2I &aStart)
Definition pcb_track.h:96
bool ApproxCollinear(const PCB_TRACK &aTrack)
const VECTOR2I & GetStart() const
Definition pcb_track.h:97
const VECTOR2I & GetEnd() const
Definition pcb_track.h:94
EDA_ITEM_FLAGS IsPointOnEnds(const VECTOR2I &point, int min_dist=0) const
Return STARTPOINT if point if near (dist = min_dist) start point, ENDPOINT if point if near (dist = m...
virtual int GetWidth() const
Definition pcb_track.h:91
A pure virtual class used to derive REPORTER objects from.
Definition reporter.h:75
Represent a set of closed polygons.
bool IsEmpty() const
Return true if the set is empty (no polygons at all)
void BooleanSubtract(const SHAPE_POLY_SET &b)
Perform boolean polyset difference.
const std::vector< BOARD_CONNECTED_ITEM * > & getConnectedItems(PCB_TRACK *aTrack)
bool testMergeCollinearSegments(PCB_TRACK *aSeg1, PCB_TRACK *aSeg2, PCB_TRACK *aDummySeg=nullptr)
helper function test if 2 segments are colinear.
bool filterItem(BOARD_CONNECTED_ITEM *aItem)
void removeItems(std::set< BOARD_ITEM * > &aItems)
void cleanup(bool aDeleteDuplicateVias, bool aDeleteNullSegments, bool aDeleteDuplicateSegments, bool aMergeSegments)
Geometry-based cleanup: duplicate items, null items, colinear items.
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.
std::mutex m_mutex
REPORTER * m_reporter
bool mergeCollinearSegments(PCB_TRACK *aSeg1, PCB_TRACK *aSeg2)
helper function merge aTrackRef and aCandidate, when possible, i.e.
bool deleteDanglingTracks(bool aTrack, bool aVia)
Removes tracks or vias only connected on one end.
bool testTrackEndpointIsNode(PCB_TRACK *aTrack, bool aTstStart, bool aTstEnd)
std::map< PCB_TRACK *, std::vector< BOARD_CONNECTED_ITEM * > > m_connectedItemsCache
std::function< bool(BOARD_CONNECTED_ITEM *aItem)> m_filter
std::vector< std::shared_ptr< CLEANUP_ITEM > > * m_itemsList
TRACKS_CLEANER(BOARD *aPcb, BOARD_COMMIT &aCommit)
void removeShortingTrackSegments()
BOARD_COMMIT & m_commit
@ CLEANUP_TRACK_IN_PAD
@ CLEANUP_DANGLING_VIA
@ CLEANUP_MERGE_TRACKS
@ CLEANUP_DANGLING_TRACK
@ CLEANUP_ZERO_LENGTH_TRACK
@ CLEANUP_SHORTING_VIA
@ CLEANUP_REDUNDANT_VIA
@ CLEANUP_SHORTING_TRACK
#define _(s)
#define IS_DELETED
#define SKIP_STRUCT
flag indicating that the structure should be ignored
thread_pool & GetKiCadThreadPool()
Get a reference to the current thread pool.
static thread_pool * tp
BS::priority_thread_pool thread_pool
Definition thread_pool.h:31
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:94
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:95
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:93
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:687