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