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 <atomic>
27#include <bit>
28
29#include <reporter.h>
30#include <board_commit.h>
31#include <cleanup_item.h>
34#include <thread_pool.h>
35#include <lset.h>
36#include <tool/tool_manager.h>
37#include <tools/pcb_actions.h>
39#include <drc/drc_rtree.h>
40#include <tracks_cleaner.h>
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( 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( 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
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( 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, ARC_HIGH_DEF,
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( 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 std::set<BOARD_ITEM*> toRemove;
390
391 for( PCB_TRACK* track : m_brd->Tracks() )
392 {
393 if( track->HasFlag( IS_DELETED ) || track->IsLocked() || filterItem( track ) )
394 continue;
395
396 if( aDeleteDuplicateVias && track->Type() == PCB_VIA_T )
397 {
398 PCB_VIA* via = static_cast<PCB_VIA*>( track );
399
400 if( via->GetStart() != via->GetEnd() )
401 via->SetEnd( via->GetStart() );
402
403 rtree.QueryColliding( via, via->GetLayer(), via->GetLayer(),
404 // Filter:
405 [&]( BOARD_ITEM* aItem ) -> bool
406 {
407 return aItem->Type() == PCB_VIA_T
408 && !aItem->HasFlag( SKIP_STRUCT )
409 && !aItem->HasFlag( IS_DELETED );
410 },
411 // Visitor:
412 [&]( BOARD_ITEM* aItem ) -> bool
413 {
414 PCB_VIA* other = static_cast<PCB_VIA*>( aItem );
415
416 if( via->GetPosition() == other->GetPosition()
417 && via->GetViaType() == other->GetViaType()
418 && via->GetLayerSet() == other->GetLayerSet() )
419 {
420 auto item = std::make_shared<CLEANUP_ITEM>( CLEANUP_REDUNDANT_VIA );
421 item->SetItems( via );
422 m_itemsList->push_back( item );
423
424 via->SetFlags( IS_DELETED );
425 toRemove.insert( via );
426 }
427
428 return true;
429 } );
430
431 // To delete through Via on THT pads at same location
432 // Examine the list of connected pads: if a through pad is found, the via is redundant
433 for( PAD* pad : m_brd->GetConnectivity()->GetConnectedPads( via ) )
434 {
435 const LSET all_cu = LSET::AllCuMask();
436
437 if( ( pad->GetLayerSet() & all_cu ) == all_cu )
438 {
439 auto item = std::make_shared<CLEANUP_ITEM>( CLEANUP_REDUNDANT_VIA );
440 item->SetItems( via, pad );
441 m_itemsList->push_back( item );
442
443 via->SetFlags( IS_DELETED );
444 toRemove.insert( via );
445 break;
446 }
447 }
448
449 via->SetFlags( SKIP_STRUCT );
450 }
451
452 if( aDeleteNullSegments && track->Type() != PCB_VIA_T )
453 {
454 if( track->IsNull() )
455 {
456 auto item = std::make_shared<CLEANUP_ITEM>( CLEANUP_ZERO_LENGTH_TRACK );
457 item->SetItems( track );
458 m_itemsList->push_back( item );
459
460 track->SetFlags( IS_DELETED );
461 toRemove.insert( track );
462 }
463 }
464
465 if( aDeleteDuplicateSegments && track->Type() == PCB_TRACE_T && !track->IsNull() )
466 {
467 rtree.QueryColliding( track, track->GetLayer(), track->GetLayer(),
468 // Filter:
469 [&]( BOARD_ITEM* aItem ) -> bool
470 {
471 return aItem->Type() == PCB_TRACE_T
472 && !aItem->HasFlag( SKIP_STRUCT )
473 && !aItem->HasFlag( IS_DELETED )
474 && !static_cast<PCB_TRACK*>( aItem )->IsNull();
475 },
476 // Visitor:
477 [&]( BOARD_ITEM* aItem ) -> bool
478 {
479 PCB_TRACK* other = static_cast<PCB_TRACK*>( aItem );
480
481 if( track->IsPointOnEnds( other->GetStart() )
482 && track->IsPointOnEnds( other->GetEnd() )
483 && track->GetWidth() == other->GetWidth()
484 && track->GetLayer() == other->GetLayer() )
485 {
486 auto item = std::make_shared<CLEANUP_ITEM>( CLEANUP_DUPLICATE_TRACK );
487 item->SetItems( track );
488 m_itemsList->push_back( item );
489
490 track->SetFlags( IS_DELETED );
491 toRemove.insert( track );
492 }
493
494 return true;
495 } );
496
497 track->SetFlags( SKIP_STRUCT );
498 }
499 }
500
501 if( !m_dryRun )
502 removeItems( toRemove );
503
504
505
506
507 auto mergeSegments = [&]( std::shared_ptr<CN_CONNECTIVITY_ALGO> connectivity ) -> bool
508 {
509 auto track_loop = [&]( int aStart, int aEnd ) -> std::vector<std::pair<PCB_TRACK*, PCB_TRACK*>>
510 {
511 std::vector<std::pair<PCB_TRACK*, PCB_TRACK*>> tracks;
512
513 for( int ii = aStart; ii < aEnd; ++ii )
514 {
515 PCB_TRACK* segment = m_brd->Tracks()[ii];
516
517 // one can merge only collinear segments, not vias or arcs.
518 if( segment->Type() != PCB_TRACE_T )
519 continue;
520
521 if( segment->HasFlag( IS_DELETED ) ) // already taken into account
522 continue;
523
524 if( filterItem( segment ) )
525 continue;
526
527 // for each end of the segment:
528 for( CN_ITEM* citem : connectivity->ItemEntry( segment ).GetItems() )
529 {
530 // Do not merge an end which has different width tracks attached -- it's a
531 // common use-case for necking-down a track between pads.
532 std::vector<PCB_TRACK*> sameWidthCandidates;
533 std::vector<PCB_TRACK*> differentWidthCandidates;
534
535 for( CN_ITEM* connected : citem->ConnectedItems() )
536 {
537 if( !connected->Valid() )
538 continue;
539
540 BOARD_CONNECTED_ITEM* candidate = connected->Parent();
541
542 if( candidate->Type() == PCB_TRACE_T && !candidate->HasFlag( IS_DELETED )
543 && !filterItem( candidate ) )
544 {
545 PCB_TRACK* candidateSegment = static_cast<PCB_TRACK*>( candidate );
546
547 if( candidateSegment->GetWidth() == segment->GetWidth() )
548 {
549 sameWidthCandidates.push_back( candidateSegment );
550 }
551 else
552 {
553 differentWidthCandidates.push_back( candidateSegment );
554 break;
555 }
556 }
557 }
558
559 if( !differentWidthCandidates.empty() )
560 continue;
561
562 for( PCB_TRACK* candidate : sameWidthCandidates )
563 {
564 if( candidate < segment ) // avoid duplicate merges
565 continue;
566
567 if( segment->ApproxCollinear( *candidate )
568 && testMergeCollinearSegments( segment, candidate ) )
569 {
570 tracks.emplace_back( segment, candidate );
571 break;
572 }
573 }
574 }
575 }
576
577 return tracks;
578 };
579
580 // The idea here is to parallelize the loop that does not modify the connectivity
581 // and extract all of the pairs of segments that might be merged. Then, perform
582 // the actual merge in the main loop.
584 auto merge_returns = tp.parallelize_loop( 0, m_brd->Tracks().size(), track_loop );
585 bool retval = false;
586
587 for( size_t ii = 0; ii < merge_returns.size(); ++ii )
588 {
589 std::future<std::vector<std::pair<PCB_TRACK*, PCB_TRACK*>>>& ret = merge_returns[ii];
590
591 if( ret.valid() )
592 {
593 for( auto& [seg1, seg2] : ret.get() )
594 {
595 retval = true;
596
597 if( seg1->HasFlag( IS_DELETED ) || seg2->HasFlag( IS_DELETED ) )
598 continue;
599
600 mergeCollinearSegments( seg1, seg2 );
601 }
602 }
603 }
604
605 return retval;
606 };
607
608 if( aMergeSegments )
609 {
610 do
611 {
612 while( !m_brd->BuildConnectivity() )
613 wxSafeYield();
614
615 m_connectedItemsCache.clear();
616 } while( mergeSegments( m_brd->GetConnectivity()->GetConnectivityAlgo() ) );
617 }
618
619 for( PCB_TRACK* track : m_brd->Tracks() )
620 track->ClearFlags( IS_DELETED | SKIP_STRUCT );
621}
622
623
624const std::vector<BOARD_CONNECTED_ITEM*>& TRACKS_CLEANER::getConnectedItems( PCB_TRACK* aTrack )
625{
626 static const std::vector<KICAD_T> connectedTypes = { PCB_TRACE_T,
627 PCB_ARC_T,
628 PCB_VIA_T,
629 PCB_PAD_T,
630 PCB_ZONE_T };
631
632 const std::shared_ptr<CONNECTIVITY_DATA>& connectivity = m_brd->GetConnectivity();
633
634 if( m_connectedItemsCache.count( aTrack ) == 0 )
635 m_connectedItemsCache[ aTrack ] = connectivity->GetConnectedItems( aTrack, connectedTypes );
636
637 return m_connectedItemsCache[ aTrack ];
638}
639
640
642{
643 if( aSeg1->IsLocked() || aSeg2->IsLocked() )
644 return false;
645
646 // Collect the unique points where the two tracks are connected to other items
647 const unsigned p1s = 1 << 0;
648 const unsigned p1e = 1 << 1;
649 const unsigned p2s = 1 << 2;
650 const unsigned p2e = 1 << 3;
651 std::vector<VECTOR2I> pts = { aSeg1->GetStart(), aSeg1->GetEnd(), aSeg2->GetStart(), aSeg2->GetEnd() };
652 std::atomic<unsigned> flags = 0;
653
654 auto collectPtsSeg1 =
655 [&]( BOARD_CONNECTED_ITEM* citem )
656 {
657 if( std::popcount( flags.load() ) > 2 )
658 return;
659
660 if( citem->Type() == PCB_TRACE_T || citem->Type() == PCB_ARC_T
661 || citem->Type() == PCB_VIA_T )
662 {
663 PCB_TRACK* track = static_cast<PCB_TRACK*>( citem );
664
665 if( track->IsPointOnEnds( aSeg1->GetStart() ) )
666 flags |= p1s;
667
668 if( track->IsPointOnEnds( aSeg1->GetEnd() ) )
669 flags |= p1e;
670 }
671 else
672 {
673 if( !( flags & p1s ) && citem->HitTest( aSeg1->GetStart(), ( aSeg1->GetWidth() + 1 ) / 2 ) )
674 flags |= p1s;
675
676 if( !( flags & p1e ) && citem->HitTest( aSeg1->GetEnd(), ( aSeg1->GetWidth() + 1 ) / 2 ) )
677 flags |= p1e;
678 }
679 };
680
681 auto collectPtsSeg2 =
682 [&]( BOARD_CONNECTED_ITEM* citem )
683 {
684 if( std::popcount( flags.load() ) > 2 )
685 return;
686
687 if( citem->Type() == PCB_TRACE_T || citem->Type() == PCB_ARC_T
688 || citem->Type() == PCB_VIA_T )
689 {
690 PCB_TRACK* track = static_cast<PCB_TRACK*>( citem );
691
692 if( track->IsPointOnEnds( aSeg2->GetStart() ) )
693 flags |= p2s;
694
695 if( track->IsPointOnEnds( aSeg2->GetEnd() ) )
696 flags |= p2e;
697 }
698 else
699 {
700 if( !( flags & p2s ) && citem->HitTest( aSeg2->GetStart(), ( aSeg2->GetWidth() + 1 ) / 2 ) )
701 flags |= p2s;
702
703 if( !( flags & p2e ) && citem->HitTest( aSeg2->GetEnd(), ( aSeg2->GetWidth() + 1 ) / 2 ) )
704 flags |= p2e;
705 }
706 };
707
708 for( BOARD_CONNECTED_ITEM* item : getConnectedItems( aSeg1 ) )
709 {
710 if( item->HasFlag( IS_DELETED ) )
711 continue;
712
713 if( item != aSeg1 && item != aSeg2 )
714 collectPtsSeg1( item );
715 }
716
717 for( BOARD_CONNECTED_ITEM* item : getConnectedItems( aSeg2 ) )
718 {
719 if( item->HasFlag( IS_DELETED ) )
720 continue;
721
722 if( item != aSeg1 && item != aSeg2 )
723 collectPtsSeg2( item );
724 }
725
726 // This means there is a node in the center
727 if( std::popcount( flags.load() ) > 2 )
728 return false;
729
730 // Verify the removed point after merging is not a node.
731 // If it is a node (i.e. if more than one other item is connected, the segments cannot be merged
732
733 PCB_TRACK dummy_seg( *aSeg1 );
734
735 if( !aDummySeg )
736 aDummySeg = &dummy_seg;
737
738 // Do not copy the parent group to the dummy segment
739 dummy_seg.SetParentGroup( nullptr );
740
741 // Calculate the new ends of the segment to merge, and store them to dummy_seg:
742 int min_x = std::min( aSeg1->GetStart().x,
743 std::min( aSeg1->GetEnd().x, std::min( aSeg2->GetStart().x, aSeg2->GetEnd().x ) ) );
744 int min_y = std::min( aSeg1->GetStart().y,
745 std::min( aSeg1->GetEnd().y, std::min( aSeg2->GetStart().y, aSeg2->GetEnd().y ) ) );
746 int max_x = std::max( aSeg1->GetStart().x,
747 std::max( aSeg1->GetEnd().x, std::max( aSeg2->GetStart().x, aSeg2->GetEnd().x ) ) );
748 int max_y = std::max( aSeg1->GetStart().y,
749 std::max( aSeg1->GetEnd().y, std::max( aSeg2->GetStart().y, aSeg2->GetEnd().y ) ) );
750
751 if( ( aSeg1->GetStart().x > aSeg1->GetEnd().x )
752 == ( aSeg1->GetStart().y > aSeg1->GetEnd().y ) )
753 {
754 aDummySeg->SetStart( VECTOR2I( min_x, min_y ) );
755 aDummySeg->SetEnd( VECTOR2I( max_x, max_y ) );
756 }
757 else
758 {
759 aDummySeg->SetStart( VECTOR2I( min_x, max_y ) );
760 aDummySeg->SetEnd( VECTOR2I( max_x, min_y ) );
761 }
762
763 // The new ends of the segment must be connected to all of the same points as the original
764 // segments. If not, the segments cannot be merged.
765 for( unsigned i = 0; i < 4; ++i )
766 {
767 if( ( flags & ( 1 << i ) ) && !aDummySeg->IsPointOnEnds( pts[i] ) )
768 return false;
769 }
770
771 // Now find the removed end(s) and stop merging if it is a node:
772 return !testTrackEndpointIsNode( aSeg1, aDummySeg->IsPointOnEnds( aSeg1->GetStart() ),
773 aDummySeg->IsPointOnEnds( aSeg1->GetEnd() ) );
774}
775
777{
778 PCB_TRACK dummy_seg( *aSeg1 );
779
780 dummy_seg.SetParentGroup( nullptr );
781
782 if( !testMergeCollinearSegments( aSeg1, aSeg2, &dummy_seg ) )
783 return false;
784
785 std::shared_ptr<CLEANUP_ITEM> item = std::make_shared<CLEANUP_ITEM>( CLEANUP_MERGE_TRACKS );
786 item->SetItems( aSeg1, aSeg2 );
787 m_itemsList->push_back( item );
788
789 aSeg2->SetFlags( IS_DELETED );
790
791 if( !m_dryRun )
792 {
793 m_commit.Modify( aSeg1 );
794
795 PCB_GROUP* group = aSeg1->GetParentGroup();
796 *aSeg1 = dummy_seg;
797 aSeg1->SetParentGroup( group );
798
799
800 m_brd->GetConnectivity()->Update( aSeg1 );
801
802 // Merge successful, seg2 has to go away
803 m_brd->Remove( aSeg2 );
804 m_commit.Removed( aSeg2 );
805 }
806
807 return true;
808}
809
810
811void TRACKS_CLEANER::removeItems( std::set<BOARD_ITEM*>& aItems )
812{
813 for( BOARD_ITEM* item : aItems )
814 {
815 m_brd->Remove( item );
816 m_commit.Removed( item );
817 }
818}
@ ERROR_INSIDE
Definition: approximation.h:34
constexpr int ARC_HIGH_DEF
Definition: base_units.h:120
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:79
void SetParentGroup(PCB_GROUP *aGroup)
Definition: board_item.h:89
PCB_GROUP * GetParentGroup() const
Definition: board_item.h:90
virtual bool IsLocked() const
Definition: board_item.cpp:75
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:295
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:187
const TRACKS & Tracks() const
Definition: board.h:334
void Remove(BOARD_ITEM *aBoardItem, REMOVE_MODE aMode=REMOVE_MODE::NORMAL) override
Removes an item from the container.
Definition: board.cpp:1176
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition: board.h:483
CN_ITEM represents a BOARD_CONNETED_ITEM in the connectivity system (ie: a pad, track/arc/via,...
const std::vector< CN_ITEM * > & ConnectedItems() const
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Modify a given item in the model.
Definition: commit.h:108
COMMIT & Removed(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Definition: commit.h:98
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:217
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition: eda_item.h:127
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:101
bool HasFlag(EDA_ITEM_FLAGS aFlag) const
Definition: eda_item.h:131
LSET is a set of PCB_LAYER_IDs.
Definition: lset.h:37
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:562
Definition: pad.h:54
A set of BOARD_ITEMs (i.e., without duplicates).
Definition: pcb_group.h:52
void SetEnd(const VECTOR2I &aEnd)
Definition: pcb_track.h:118
void SetStart(const VECTOR2I &aStart)
Definition: pcb_track.h:121
bool ApproxCollinear(const PCB_TRACK &aTrack)
Definition: pcb_track.cpp:520
const VECTOR2I & GetStart() const
Definition: pcb_track.h:122
const VECTOR2I & GetEnd() const
Definition: pcb_track.h:119
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...
Definition: pcb_track.cpp:628
virtual int GetWidth() const
Definition: pcb_track.h:116
VECTOR2I GetPosition() const override
Definition: pcb_track.h:493
VIATYPE GetViaType() const
Definition: pcb_track.h:409
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: pcb_track.cpp:1062
A pure virtual class used to derive REPORTER objects from.
Definition: reporter.h:72
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)=0
Report a string with a given severity.
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.
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
Definition: cleanup_item.h:42
@ CLEANUP_DANGLING_VIA
Definition: cleanup_item.h:40
@ CLEANUP_MERGE_TRACKS
Definition: cleanup_item.h:38
@ CLEANUP_DANGLING_TRACK
Definition: cleanup_item.h:39
@ CLEANUP_ZERO_LENGTH_TRACK
Definition: cleanup_item.h:41
@ CLEANUP_SHORTING_VIA
Definition: cleanup_item.h:35
@ CLEANUP_REDUNDANT_VIA
Definition: cleanup_item.h:36
@ CLEANUP_SHORTING_TRACK
Definition: cleanup_item.h:34
#define _(s)
#define IS_DELETED
#define SKIP_STRUCT
flag indicating that the structure should be ignored
static std::vector< KICAD_T > connectedTypes
thread_pool & GetKiCadThreadPool()
Get a reference to the current thread pool.
Definition: thread_pool.cpp:30
static thread_pool * tp
Definition: thread_pool.cpp:28
BS::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:97
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition: typeinfo.h:107
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition: typeinfo.h:87
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition: typeinfo.h:98
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:96
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:695