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 (C) 1992-2023 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 <core/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
231{
232 // A node is a point where more than 2 items are connected.
233
234 const std::list<CN_ITEM*>& items =
235 m_brd->GetConnectivity()->GetConnectivityAlgo()->ItemEntry( aTrack ).GetItems();
236
237 if( items.empty() )
238 return false;
239
240 CN_ITEM* citem = items.front();
241
242 if( !citem->Valid() )
243 return false;
244
245 const std::vector<std::shared_ptr<CN_ANCHOR>>& anchors = citem->Anchors();
246
247 VECTOR2I refpoint = aTstStart ? aTrack->GetStart() : aTrack->GetEnd();
248
249 for( const std::shared_ptr<CN_ANCHOR>& anchor : anchors )
250 {
251 if( anchor->Pos() != refpoint )
252 continue;
253
254 // The right anchor point is found: if more than one other item
255 // (pad, via, track...) is connected, it is a node:
256 return anchor->ConnectedItemsCount() > 1;
257 }
258
259 return false;
260}
261
262
263bool TRACKS_CLEANER::deleteDanglingTracks( bool aTrack, bool aVia )
264{
265 bool item_erased = false;
266 bool modified = false;
267
268 if( !aTrack && !aVia )
269 return false;
270
271 do // Iterate when at least one track is deleted
272 {
273 item_erased = false;
274 // Ensure the connectivity is up to date, especially after removing a dangling segment
276
277 // Keep a duplicate deque to all deleting in the primary
278 std::deque<PCB_TRACK*> temp_tracks( m_brd->Tracks() );
279
280 for( PCB_TRACK* track : temp_tracks )
281 {
282 if( track->HasFlag( IS_DELETED ) || track->IsLocked() || filterItem( track ) )
283 continue;
284
285 if( !aVia && track->Type() == PCB_VIA_T )
286 continue;
287
288 if( !aTrack && ( track->Type() == PCB_TRACE_T || track->Type() == PCB_ARC_T ) )
289 continue;
290
291 // Test if a track (or a via) endpoint is not connected to another track or zone.
292 if( m_brd->GetConnectivity()->TestTrackEndpointDangling( track, false ) )
293 {
294 std::shared_ptr<CLEANUP_ITEM> item;
295
296 if( track->Type() == PCB_VIA_T )
297 item = std::make_shared<CLEANUP_ITEM>( CLEANUP_DANGLING_VIA );
298 else
299 item = std::make_shared<CLEANUP_ITEM>( CLEANUP_DANGLING_TRACK );
300
301 item->SetItems( track );
302 m_itemsList->push_back( item );
303 track->SetFlags( IS_DELETED );
304
305 // keep iterating, because a track connected to the deleted track
306 // now perhaps is not connected and should be deleted
307 item_erased = true;
308
309 if( !m_dryRun )
310 {
311 m_brd->Remove( track );
312 m_commit.Removed( track );
313 modified = true;
314 }
315 }
316 }
317 } while( item_erased ); // A segment was erased: test for some new dangling segments
318
319 return modified;
320}
321
322
324{
325 std::set<BOARD_ITEM*> toRemove;
326
327 // Delete tracks that start and end on the same pad
328 std::shared_ptr<CONNECTIVITY_DATA> connectivity = m_brd->GetConnectivity();
329
330 for( PCB_TRACK* track : m_brd->Tracks() )
331 {
332 if( track->IsLocked() || filterItem( track ) )
333 continue;
334
335 if( track->Type() == PCB_VIA_T )
336 continue;
337
338 // Mark track if connected to pads
339 for( PAD* pad : connectivity->GetConnectedPads( track ) )
340 {
341 if( pad->HitTest( track->GetStart() ) && pad->HitTest( track->GetEnd() ) )
342 {
343 SHAPE_POLY_SET poly;
344 track->TransformShapeToPolygon( poly, track->GetLayer(), 0, ARC_HIGH_DEF,
345 ERROR_INSIDE );
346
347 poly.BooleanSubtract( *pad->GetEffectivePolygon( track->GetLayer(), ERROR_INSIDE ),
349
350 if( poly.IsEmpty() )
351 {
352 auto item = std::make_shared<CLEANUP_ITEM>( CLEANUP_TRACK_IN_PAD );
353 item->SetItems( track );
354 m_itemsList->push_back( item );
355
356 toRemove.insert( track );
357 track->SetFlags( IS_DELETED );
358 }
359 }
360 }
361 }
362
363 if( !m_dryRun )
364 removeItems( toRemove );
365}
366
367
371void TRACKS_CLEANER::cleanup( bool aDeleteDuplicateVias, bool aDeleteNullSegments,
372 bool aDeleteDuplicateSegments, bool aMergeSegments )
373{
374 DRC_RTREE rtree;
375
376 for( PCB_TRACK* track : m_brd->Tracks() )
377 {
378 track->ClearFlags( IS_DELETED | SKIP_STRUCT );
379 rtree.Insert( track, track->GetLayer() );
380 }
381
382 std::set<BOARD_ITEM*> toRemove;
383
384 for( PCB_TRACK* track : m_brd->Tracks() )
385 {
386 if( track->HasFlag( IS_DELETED ) || track->IsLocked() || filterItem( track ) )
387 continue;
388
389 if( aDeleteDuplicateVias && track->Type() == PCB_VIA_T )
390 {
391 PCB_VIA* via = static_cast<PCB_VIA*>( track );
392
393 if( via->GetStart() != via->GetEnd() )
394 via->SetEnd( via->GetStart() );
395
396 rtree.QueryColliding( via, via->GetLayer(), via->GetLayer(),
397 // Filter:
398 [&]( BOARD_ITEM* aItem ) -> bool
399 {
400 return aItem->Type() == PCB_VIA_T
401 && !aItem->HasFlag( SKIP_STRUCT )
402 && !aItem->HasFlag( IS_DELETED );
403 },
404 // Visitor:
405 [&]( BOARD_ITEM* aItem ) -> bool
406 {
407 PCB_VIA* other = static_cast<PCB_VIA*>( aItem );
408
409 if( via->GetPosition() == other->GetPosition()
410 && via->GetViaType() == other->GetViaType()
411 && via->GetLayerSet() == other->GetLayerSet() )
412 {
413 auto item = std::make_shared<CLEANUP_ITEM>( CLEANUP_REDUNDANT_VIA );
414 item->SetItems( via );
415 m_itemsList->push_back( item );
416
417 via->SetFlags( IS_DELETED );
418 toRemove.insert( via );
419 }
420
421 return true;
422 } );
423
424 // To delete through Via on THT pads at same location
425 // Examine the list of connected pads: if a through pad is found, the via is redundant
426 for( PAD* pad : m_brd->GetConnectivity()->GetConnectedPads( via ) )
427 {
428 const LSET all_cu = LSET::AllCuMask();
429
430 if( ( pad->GetLayerSet() & all_cu ) == all_cu )
431 {
432 auto item = std::make_shared<CLEANUP_ITEM>( CLEANUP_REDUNDANT_VIA );
433 item->SetItems( via, pad );
434 m_itemsList->push_back( item );
435
436 via->SetFlags( IS_DELETED );
437 toRemove.insert( via );
438 break;
439 }
440 }
441
442 via->SetFlags( SKIP_STRUCT );
443 }
444
445 if( aDeleteNullSegments && track->Type() != PCB_VIA_T )
446 {
447 if( track->IsNull() )
448 {
449 auto item = std::make_shared<CLEANUP_ITEM>( CLEANUP_ZERO_LENGTH_TRACK );
450 item->SetItems( track );
451 m_itemsList->push_back( item );
452
453 track->SetFlags( IS_DELETED );
454 toRemove.insert( track );
455 }
456 }
457
458 if( aDeleteDuplicateSegments && track->Type() == PCB_TRACE_T && !track->IsNull() )
459 {
460 rtree.QueryColliding( track, track->GetLayer(), track->GetLayer(),
461 // Filter:
462 [&]( BOARD_ITEM* aItem ) -> bool
463 {
464 return aItem->Type() == PCB_TRACE_T
465 && !aItem->HasFlag( SKIP_STRUCT )
466 && !aItem->HasFlag( IS_DELETED )
467 && !static_cast<PCB_TRACK*>( aItem )->IsNull();
468 },
469 // Visitor:
470 [&]( BOARD_ITEM* aItem ) -> bool
471 {
472 PCB_TRACK* other = static_cast<PCB_TRACK*>( aItem );
473
474 if( track->IsPointOnEnds( other->GetStart() )
475 && track->IsPointOnEnds( other->GetEnd() )
476 && track->GetWidth() == other->GetWidth()
477 && track->GetLayer() == other->GetLayer() )
478 {
479 auto item = std::make_shared<CLEANUP_ITEM>( CLEANUP_DUPLICATE_TRACK );
480 item->SetItems( track );
481 m_itemsList->push_back( item );
482
483 track->SetFlags( IS_DELETED );
484 toRemove.insert( track );
485 }
486
487 return true;
488 } );
489
490 track->SetFlags( SKIP_STRUCT );
491 }
492 }
493
494 if( !m_dryRun )
495 removeItems( toRemove );
496
497
498
499
500 auto mergeSegments = [&]( std::shared_ptr<CN_CONNECTIVITY_ALGO> connectivity ) -> bool
501 {
502 auto track_loop = [&]( int aStart, int aEnd ) -> std::vector<std::pair<PCB_TRACK*, PCB_TRACK*>>
503 {
504 std::vector<std::pair<PCB_TRACK*, PCB_TRACK*>> tracks;
505
506 for( int ii = aStart; ii < aEnd; ++ii )
507 {
508 PCB_TRACK* segment = m_brd->Tracks()[ii];
509
510 // one can merge only collinear segments, not vias or arcs.
511 if( segment->Type() != PCB_TRACE_T )
512 continue;
513
514 if( segment->HasFlag( IS_DELETED ) ) // already taken into account
515 continue;
516
517 if( filterItem( segment ) )
518 continue;
519
520 // for each end of the segment:
521 for( CN_ITEM* citem : connectivity->ItemEntry( segment ).GetItems() )
522 {
523 // Do not merge an end which has different width tracks attached -- it's a
524 // common use-case for necking-down a track between pads.
525 std::vector<PCB_TRACK*> sameWidthCandidates;
526 std::vector<PCB_TRACK*> differentWidthCandidates;
527
528 for( CN_ITEM* connected : citem->ConnectedItems() )
529 {
530 if( !connected->Valid() )
531 continue;
532
533 BOARD_CONNECTED_ITEM* candidate = connected->Parent();
534
535 if( candidate->Type() == PCB_TRACE_T && !candidate->HasFlag( IS_DELETED )
536 && !filterItem( candidate ) )
537 {
538 PCB_TRACK* candidateSegment = static_cast<PCB_TRACK*>( candidate );
539
540 if( candidateSegment->GetWidth() == segment->GetWidth() )
541 {
542 sameWidthCandidates.push_back( candidateSegment );
543 }
544 else
545 {
546 differentWidthCandidates.push_back( candidateSegment );
547 break;
548 }
549 }
550 }
551
552 if( !differentWidthCandidates.empty() )
553 continue;
554
555 for( PCB_TRACK* candidate : sameWidthCandidates )
556 {
557 if( candidate < segment ) // avoid duplicate merges
558 continue;
559
560 if( segment->ApproxCollinear( *candidate )
561 && testMergeCollinearSegments( segment, candidate ) )
562 {
563 tracks.emplace_back( segment, candidate );
564 break;
565 }
566 }
567 }
568 }
569
570 return tracks;
571 };
572
574 auto merge_returns = tp.parallelize_loop( 0, m_brd->Tracks().size(), track_loop );
575
576 for( size_t ii = 0; ii < merge_returns.size(); ++ii )
577 {
578 std::future<std::vector<std::pair<PCB_TRACK*, PCB_TRACK*>>>& ret = merge_returns[ii];
579
580 if( ret.valid() )
581 {
582 for( auto& [seg1, seg2] : ret.get() )
583 {
584 if( seg1->HasFlag( IS_DELETED ) || seg2->HasFlag( IS_DELETED ) )
585 continue;
586
587 mergeCollinearSegments( seg1, seg2 );
588 }
589 }
590 }
591
592 return false;
593 };
594
595 if( aMergeSegments )
596 {
597 do
598 {
599 while( !m_brd->BuildConnectivity() )
600 wxSafeYield();
601
602 m_connectedItemsCache.clear();
603 } while( mergeSegments( m_brd->GetConnectivity()->GetConnectivityAlgo() ) );
604 }
605
606 for( PCB_TRACK* track : m_brd->Tracks() )
607 track->ClearFlags( IS_DELETED | SKIP_STRUCT );
608}
609
610
611const std::vector<BOARD_CONNECTED_ITEM*>& TRACKS_CLEANER::getConnectedItems( PCB_TRACK* aTrack )
612{
613 static const std::vector<KICAD_T> connectedTypes = { PCB_TRACE_T,
614 PCB_ARC_T,
615 PCB_VIA_T,
616 PCB_PAD_T,
617 PCB_ZONE_T };
618
619 const std::shared_ptr<CONNECTIVITY_DATA>& connectivity = m_brd->GetConnectivity();
620
621 if( m_connectedItemsCache.count( aTrack ) == 0 )
622 m_connectedItemsCache[ aTrack ] = connectivity->GetConnectedItems( aTrack, connectedTypes );
623
624 return m_connectedItemsCache[ aTrack ];
625}
626
627
629{
630 if( aSeg1->IsLocked() || aSeg2->IsLocked() )
631 return false;
632
633 // Collect the unique points where the two tracks are connected to other items
634 const unsigned p1s = 1 << 0;
635 const unsigned p1e = 1 << 1;
636 const unsigned p2s = 1 << 2;
637 const unsigned p2e = 1 << 3;
638 std::vector<VECTOR2I> pts = { aSeg1->GetStart(), aSeg1->GetEnd(), aSeg2->GetStart(), aSeg2->GetEnd() };
639 std::atomic<unsigned> flags = 0;
640
641 auto collectPts =
642 [&]( BOARD_CONNECTED_ITEM* citem )
643 {
644 if( std::popcount( flags.load() ) > 2 )
645 return;
646
647 if( citem->Type() == PCB_TRACE_T || citem->Type() == PCB_ARC_T
648 || citem->Type() == PCB_VIA_T )
649 {
650 PCB_TRACK* track = static_cast<PCB_TRACK*>( citem );
651
652 if( !( flags & p1s ) && track->IsPointOnEnds( aSeg1->GetStart() ) )
653 flags |= p1s;
654
655 if( !( flags & p1e ) && track->IsPointOnEnds( aSeg1->GetEnd() ) )
656 flags |= p1e;
657
658 if( !( flags & p2s ) && track->IsPointOnEnds( aSeg2->GetStart() ) )
659 flags |= p2s;
660
661 if( !( flags & p2e ) && track->IsPointOnEnds( aSeg2->GetEnd() ) )
662 flags |= p2e;
663 }
664 else
665 {
666 if( !( flags & p1s ) && citem->HitTest( aSeg1->GetStart(), ( aSeg1->GetWidth() + 1 ) / 2 ) )
667 flags |= p1s;
668
669 if( !( flags & p1e ) && citem->HitTest( aSeg1->GetEnd(), ( aSeg1->GetWidth() + 1 ) / 2 ) )
670 flags |= p1e;
671
672 if( !( flags & p2s ) && citem->HitTest( aSeg2->GetStart(), ( aSeg2->GetWidth() + 1 ) / 2 ) )
673 flags |= p2s;
674
675 if( !( flags & p2e ) && citem->HitTest( aSeg2->GetEnd(), ( aSeg2->GetWidth() + 1 ) / 2 ) )
676 flags |= p2e;
677 }
678 };
679
680 for( BOARD_CONNECTED_ITEM* item : getConnectedItems( aSeg1 ) )
681 {
682 if( item != aSeg1 && item != aSeg2 )
683 collectPts( item );
684 }
685
686 for( BOARD_CONNECTED_ITEM* item : getConnectedItems( aSeg2 ) )
687 {
688 if( item != aSeg1 && item != aSeg2 )
689 collectPts( item );
690 }
691
692 // This means there is a node in the center
693 if( std::popcount( flags.load() ) > 2 )
694 return false;
695
696 // Verify the removed point after merging is not a node.
697 // If it is a node (i.e. if more than one other item is connected, the segments cannot be merged
698
699 PCB_TRACK dummy_seg( *aSeg1 );
700
701 if( !aDummySeg )
702 aDummySeg = &dummy_seg;
703
704 // Do not copy the parent group to the dummy segment
705 dummy_seg.SetParentGroup( nullptr );
706
707 // Calculate the new ends of the segment to merge, and store them to dummy_seg:
708 int min_x = std::min( aSeg1->GetStart().x,
709 std::min( aSeg1->GetEnd().x, std::min( aSeg2->GetStart().x, aSeg2->GetEnd().x ) ) );
710 int min_y = std::min( aSeg1->GetStart().y,
711 std::min( aSeg1->GetEnd().y, std::min( aSeg2->GetStart().y, aSeg2->GetEnd().y ) ) );
712 int max_x = std::max( aSeg1->GetStart().x,
713 std::max( aSeg1->GetEnd().x, std::max( aSeg2->GetStart().x, aSeg2->GetEnd().x ) ) );
714 int max_y = std::max( aSeg1->GetStart().y,
715 std::max( aSeg1->GetEnd().y, std::max( aSeg2->GetStart().y, aSeg2->GetEnd().y ) ) );
716
717 if( ( aSeg1->GetStart().x > aSeg1->GetEnd().x )
718 == ( aSeg1->GetStart().y > aSeg1->GetEnd().y ) )
719 {
720 aDummySeg->SetStart( VECTOR2I( min_x, min_y ) );
721 aDummySeg->SetEnd( VECTOR2I( max_x, max_y ) );
722 }
723 else
724 {
725 aDummySeg->SetStart( VECTOR2I( min_x, max_y ) );
726 aDummySeg->SetEnd( VECTOR2I( max_x, min_y ) );
727 }
728
729 // The new ends of the segment must be connected to all of the same points as the original
730 // segments. If not, the segments cannot be merged.
731 for( unsigned i = 0; i < 4; ++i )
732 {
733 if( ( flags & ( 1 << i ) ) && !aDummySeg->IsPointOnEnds( pts[i] ) )
734 return false;
735 }
736
737 // Now find the removed end(s) and stop merging if it is a node:
738 if( aSeg1->GetStart() != aDummySeg->GetStart() && aSeg1->GetStart() != aDummySeg->GetEnd() )
739 {
740 if( testTrackEndpointIsNode( aSeg1, true ) )
741 return false;
742 }
743
744 if( aSeg1->GetEnd() != aDummySeg->GetStart() && aSeg1->GetEnd() != aDummySeg->GetEnd() )
745 {
746 if( testTrackEndpointIsNode( aSeg1, false ) )
747 return false;
748 }
749
750 return true;
751}
752
754{
755 PCB_TRACK dummy_seg( *aSeg1 );
756
757 dummy_seg.SetParentGroup( nullptr );
758
759 if( !testMergeCollinearSegments( aSeg1, aSeg2, &dummy_seg ) )
760 return false;
761
762 std::shared_ptr<CLEANUP_ITEM> item = std::make_shared<CLEANUP_ITEM>( CLEANUP_MERGE_TRACKS );
763 item->SetItems( aSeg1, aSeg2 );
764 m_itemsList->push_back( item );
765
766 aSeg2->SetFlags( IS_DELETED );
767
768 if( !m_dryRun )
769 {
770 m_commit.Modify( aSeg1 );
771
772 PCB_GROUP* group = aSeg1->GetParentGroup();
773 *aSeg1 = dummy_seg;
774 aSeg1->SetParentGroup( group );
775
776
777 m_brd->GetConnectivity()->Update( aSeg1 );
778
779 // Merge successful, seg2 has to go away
780 m_brd->Remove( aSeg2 );
781 m_commit.Removed( aSeg2 );
782 }
783
784 return true;
785}
786
787
788void TRACKS_CLEANER::removeItems( std::set<BOARD_ITEM*>& aItems )
789{
790 for( BOARD_ITEM* item : aItems )
791 {
792 m_brd->Remove( item );
793 m_commit.Removed( item );
794 }
795}
@ 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:290
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:329
void Remove(BOARD_ITEM *aBoardItem, REMOVE_MODE aMode=REMOVE_MODE::NORMAL) override
Removes an item from the container.
Definition: board.cpp:1137
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition: board.h:475
CN_ITEM represents a BOARD_CONNETED_ITEM in the connectivity system (ie: a pad, track/arc/via,...
const std::vector< CN_ITEM * > & ConnectedItems() const
bool Valid() const
std::vector< std::shared_ptr< CN_ANCHOR > > & Anchors()
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Create an undo entry for an item that has been already modified.
Definition: commit.h:105
COMMIT & Removed(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Modify a given item in the model.
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:36
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:686
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.
void BooleanSubtract(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Perform boolean polyset difference For aFastMode meaning, see function booleanOp.
bool IsEmpty() const
Return true if the set is empty (no polygons at all)
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.
bool testTrackEndpointIsNode(PCB_TRACK *aTrack, bool aTstStart)
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.
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
static thread_pool * tp
Definition: thread_pool.cpp:30
BS::thread_pool thread_pool
Definition: thread_pool.h:30
thread_pool & GetKiCadThreadPool()
Get a reference to the current thread pool.
Definition: thread_pool.cpp:32
@ 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:691