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 for( size_t ii = 0; ii < merge_returns.size(); ++ii )
596 {
597 std::future<std::vector<std::pair<PCB_TRACK*, PCB_TRACK*>>>& ret = merge_returns[ii];
598
599 if( ret.valid() )
600 {
601 for( auto& [seg1, seg2] : ret.get() )
602 {
603 retval = true;
604
605 if( seg1->HasFlag( IS_DELETED ) || seg2->HasFlag( IS_DELETED ) )
606 continue;
607
608 mergeCollinearSegments( seg1, seg2 );
609 }
610 }
611 }
612
613 return retval;
614 };
615
616 if( aMergeSegments )
617 {
618 do
619 {
620 while( !m_brd->BuildConnectivity() )
621 wxSafeYield();
622
623 // BuildConnectivity adds items but doesn't establish connections between them.
624 // RecalculateRatsnest triggers searchConnections which actually finds and links
625 // connected items in the connectivity graph.
626 m_brd->GetConnectivity()->RecalculateRatsnest();
627
628 std::lock_guard lock( m_mutex );
629 m_connectedItemsCache.clear();
630 } while( mergeSegments( m_brd->GetConnectivity()->GetConnectivityAlgo() ) );
631 }
632
633 for( PCB_TRACK* track : m_brd->Tracks() )
634 track->ClearFlags( IS_DELETED | SKIP_STRUCT );
635}
636
637
638const std::vector<BOARD_CONNECTED_ITEM*>& TRACKS_CLEANER::getConnectedItems( PCB_TRACK* aTrack )
639{
640 const std::shared_ptr<CONNECTIVITY_DATA>& connectivity = m_brd->GetConnectivity();
641 std::lock_guard lock( m_mutex );
642
643 if( !m_connectedItemsCache.contains( aTrack ) )
644 m_connectedItemsCache[aTrack] = connectivity->GetConnectedItems( aTrack );
645
646 return m_connectedItemsCache.at( aTrack );
647}
648
649
651{
652 if( aSeg1->IsLocked() || aSeg2->IsLocked() )
653 return false;
654
655 // Collect the unique points where the two tracks are connected to other items
656 const unsigned p1s = 1 << 0;
657 const unsigned p1e = 1 << 1;
658 const unsigned p2s = 1 << 2;
659 const unsigned p2e = 1 << 3;
660 std::vector<VECTOR2I> pts = { aSeg1->GetStart(), aSeg1->GetEnd(), aSeg2->GetStart(), aSeg2->GetEnd() };
661 std::atomic<unsigned> flags = 0;
662
663 auto collectPtsSeg1 =
664 [&]( BOARD_CONNECTED_ITEM* citem )
665 {
666 if( std::popcount( flags.load() ) > 2 )
667 return;
668
669 if( citem->Type() == PCB_TRACE_T || citem->Type() == PCB_ARC_T
670 || citem->Type() == PCB_VIA_T )
671 {
672 PCB_TRACK* track = static_cast<PCB_TRACK*>( citem );
673
674 if( track->IsPointOnEnds( aSeg1->GetStart() ) )
675 flags |= p1s;
676
677 if( track->IsPointOnEnds( aSeg1->GetEnd() ) )
678 flags |= p1e;
679 }
680 else
681 {
682 if( !( flags & p1s ) && citem->HitTest( aSeg1->GetStart(), ( aSeg1->GetWidth() + 1 ) / 2 ) )
683 flags |= p1s;
684
685 if( !( flags & p1e ) && citem->HitTest( aSeg1->GetEnd(), ( aSeg1->GetWidth() + 1 ) / 2 ) )
686 flags |= p1e;
687 }
688 };
689
690 auto collectPtsSeg2 =
691 [&]( BOARD_CONNECTED_ITEM* citem )
692 {
693 if( std::popcount( flags.load() ) > 2 )
694 return;
695
696 if( citem->Type() == PCB_TRACE_T || citem->Type() == PCB_ARC_T
697 || citem->Type() == PCB_VIA_T )
698 {
699 PCB_TRACK* track = static_cast<PCB_TRACK*>( citem );
700
701 if( track->IsPointOnEnds( aSeg2->GetStart() ) )
702 flags |= p2s;
703
704 if( track->IsPointOnEnds( aSeg2->GetEnd() ) )
705 flags |= p2e;
706 }
707 else
708 {
709 if( !( flags & p2s ) && citem->HitTest( aSeg2->GetStart(), ( aSeg2->GetWidth() + 1 ) / 2 ) )
710 flags |= p2s;
711
712 if( !( flags & p2e ) && citem->HitTest( aSeg2->GetEnd(), ( aSeg2->GetWidth() + 1 ) / 2 ) )
713 flags |= p2e;
714 }
715 };
716
717 for( BOARD_CONNECTED_ITEM* item : getConnectedItems( aSeg1 ) )
718 {
719 if( item->HasFlag( IS_DELETED ) )
720 continue;
721
722 if( item != aSeg1 && item != aSeg2 )
723 collectPtsSeg1( item );
724 }
725
726 for( BOARD_CONNECTED_ITEM* item : getConnectedItems( aSeg2 ) )
727 {
728 if( item->HasFlag( IS_DELETED ) )
729 continue;
730
731 if( item != aSeg1 && item != aSeg2 )
732 collectPtsSeg2( item );
733 }
734
735 // This means there is a node in the center
736 if( std::popcount( flags.load() ) > 2 )
737 return false;
738
739 // Verify the removed point after merging is not a node.
740 // If it is a node (i.e. if more than one other item is connected, the segments cannot be merged
741
742 PCB_TRACK dummy_seg( *aSeg1 );
743
744 if( !aDummySeg )
745 aDummySeg = &dummy_seg;
746
747 // Calculate the new ends of the segment to merge, and store them to dummy_seg:
748 int min_x = std::min( aSeg1->GetStart().x,
749 std::min( aSeg1->GetEnd().x, std::min( aSeg2->GetStart().x, aSeg2->GetEnd().x ) ) );
750 int min_y = std::min( aSeg1->GetStart().y,
751 std::min( aSeg1->GetEnd().y, std::min( aSeg2->GetStart().y, aSeg2->GetEnd().y ) ) );
752 int max_x = std::max( aSeg1->GetStart().x,
753 std::max( aSeg1->GetEnd().x, std::max( aSeg2->GetStart().x, aSeg2->GetEnd().x ) ) );
754 int max_y = std::max( aSeg1->GetStart().y,
755 std::max( aSeg1->GetEnd().y, std::max( aSeg2->GetStart().y, aSeg2->GetEnd().y ) ) );
756
757 if( ( aSeg1->GetStart().x > aSeg1->GetEnd().x )
758 == ( aSeg1->GetStart().y > aSeg1->GetEnd().y ) )
759 {
760 aDummySeg->SetStart( VECTOR2I( min_x, min_y ) );
761 aDummySeg->SetEnd( VECTOR2I( max_x, max_y ) );
762 }
763 else
764 {
765 aDummySeg->SetStart( VECTOR2I( min_x, max_y ) );
766 aDummySeg->SetEnd( VECTOR2I( max_x, min_y ) );
767 }
768
769 // The new ends of the segment must be connected to all of the same points as the original
770 // segments. If not, the segments cannot be merged.
771 for( unsigned i = 0; i < 4; ++i )
772 {
773 if( ( flags & ( 1 << i ) ) && !aDummySeg->IsPointOnEnds( pts[i] ) )
774 return false;
775 }
776
777 // Now find the removed end(s) and stop merging if it is a node:
778 return !testTrackEndpointIsNode( aSeg1, aDummySeg->IsPointOnEnds( aSeg1->GetStart() ),
779 aDummySeg->IsPointOnEnds( aSeg1->GetEnd() ) );
780}
781
783{
784 PCB_TRACK dummy_seg( *aSeg1 );
785
786 if( !testMergeCollinearSegments( aSeg1, aSeg2, &dummy_seg ) )
787 return false;
788
789 std::shared_ptr<CLEANUP_ITEM> item = std::make_shared<CLEANUP_ITEM>( CLEANUP_MERGE_TRACKS );
790 item->SetItems( aSeg1, aSeg2 );
791 m_itemsList->push_back( std::move( item ) );
792
793 aSeg2->SetFlags( IS_DELETED );
794
795 if( !m_dryRun )
796 {
797 m_commit.Modify( aSeg1 );
798
799 *aSeg1 = dummy_seg;
800
801 m_brd->GetConnectivity()->Update( aSeg1 );
802
803 // Merge successful, seg2 has to go away
804 m_brd->Remove( aSeg2 );
805 m_commit.Removed( aSeg2 );
806 }
807
808 return true;
809}
810
811
812void TRACKS_CLEANER::removeItems( std::set<BOARD_ITEM*>& aItems )
813{
814 for( BOARD_ITEM* item : aItems )
815 {
816 m_brd->Remove( item );
817 m_commit.Removed( item );
818 }
819}
@ 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:50
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:96
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:73
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