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 std::set<BOARD_ITEM*> toRemove;
394
395 for( PCB_TRACK* track : m_brd->Tracks() )
396 {
397 if( track->HasFlag( IS_DELETED ) || track->IsLocked() || filterItem( track ) )
398 continue;
399
400 if( aDeleteDuplicateVias && track->Type() == PCB_VIA_T )
401 {
402 PCB_VIA* via = static_cast<PCB_VIA*>( track );
403
404 if( via->GetStart() != via->GetEnd() )
405 via->SetEnd( via->GetStart() );
406
407 rtree.QueryColliding( via, via->GetLayer(), via->GetLayer(),
408 // Filter:
409 [&]( BOARD_ITEM* aItem ) -> bool
410 {
411 return aItem->Type() == PCB_VIA_T
412 && !aItem->HasFlag( SKIP_STRUCT )
413 && !aItem->HasFlag( IS_DELETED );
414 },
415 // Visitor:
416 [&]( BOARD_ITEM* aItem ) -> bool
417 {
418 PCB_VIA* other = static_cast<PCB_VIA*>( aItem );
419
420 if( via->GetPosition() == other->GetPosition()
421 && via->GetViaType() == other->GetViaType()
422 && via->GetLayerSet() == other->GetLayerSet() )
423 {
424 auto item = std::make_shared<CLEANUP_ITEM>( CLEANUP_REDUNDANT_VIA );
425 item->SetItems( via );
426 m_itemsList->push_back( std::move( item ) );
427
428 via->SetFlags( IS_DELETED );
429 toRemove.insert( via );
430 }
431
432 return true;
433 } );
434
435 // To delete through Via on THT pads at same location
436 // Examine the list of connected pads: if a through pad is found, the via is redundant
437 for( PAD* pad : m_brd->GetConnectivity()->GetConnectedPads( via ) )
438 {
439 const LSET all_cu = LSET::AllCuMask( m_brd->GetCopperLayerCount() );
440
441 if( ( pad->GetLayerSet() & all_cu ) == all_cu )
442 {
443 auto item = std::make_shared<CLEANUP_ITEM>( CLEANUP_REDUNDANT_VIA );
444 item->SetItems( via, pad );
445 m_itemsList->push_back( std::move( item ) );
446
447 via->SetFlags( IS_DELETED );
448 toRemove.insert( via );
449 break;
450 }
451 }
452
453 via->SetFlags( SKIP_STRUCT );
454 }
455
456 if( aDeleteNullSegments && track->Type() != PCB_VIA_T )
457 {
458 if( track->IsNull() )
459 {
460 auto item = std::make_shared<CLEANUP_ITEM>( CLEANUP_ZERO_LENGTH_TRACK );
461 item->SetItems( track );
462 m_itemsList->push_back( std::move( item ) );
463
464 track->SetFlags( IS_DELETED );
465 toRemove.insert( track );
466 }
467 }
468
469 if( aDeleteDuplicateSegments && track->Type() == PCB_TRACE_T && !track->IsNull() )
470 {
471 rtree.QueryColliding( track, track->GetLayer(), track->GetLayer(),
472 // Filter:
473 [&]( BOARD_ITEM* aItem ) -> bool
474 {
475 return aItem->Type() == PCB_TRACE_T
476 && !aItem->HasFlag( SKIP_STRUCT )
477 && !aItem->HasFlag( IS_DELETED )
478 && !static_cast<PCB_TRACK*>( aItem )->IsNull();
479 },
480 // Visitor:
481 [&]( BOARD_ITEM* aItem ) -> bool
482 {
483 PCB_TRACK* other = static_cast<PCB_TRACK*>( aItem );
484
485 if( track->IsPointOnEnds( other->GetStart() )
486 && track->IsPointOnEnds( other->GetEnd() )
487 && track->GetWidth() == other->GetWidth()
488 && track->GetLayer() == other->GetLayer() )
489 {
490 auto item = std::make_shared<CLEANUP_ITEM>( CLEANUP_DUPLICATE_TRACK );
491 item->SetItems( track );
492 m_itemsList->push_back( std::move( item ) );
493
494 track->SetFlags( IS_DELETED );
495 toRemove.insert( track );
496 }
497
498 return true;
499 } );
500
501 track->SetFlags( SKIP_STRUCT );
502 }
503 }
504
505 if( !m_dryRun )
506 removeItems( toRemove );
507
508
509
510
511 auto mergeSegments = [&]( std::shared_ptr<CN_CONNECTIVITY_ALGO> connectivity ) -> bool
512 {
513 auto track_loop = [&]( int aStart, int aEnd ) -> std::vector<std::pair<PCB_TRACK*, PCB_TRACK*>>
514 {
515 std::vector<std::pair<PCB_TRACK*, PCB_TRACK*>> tracks;
516
517 for( int ii = aStart; ii < aEnd; ++ii )
518 {
519 PCB_TRACK* segment = m_brd->Tracks()[ii];
520
521 // one can merge only collinear segments, not vias or arcs.
522 if( segment->Type() != PCB_TRACE_T )
523 continue;
524
525 if( segment->HasFlag( IS_DELETED ) ) // already taken into account
526 continue;
527
528 if( filterItem( segment ) )
529 continue;
530
531 // for each end of the segment:
532 auto& cnItems = connectivity->ItemEntry( segment ).GetItems();
533
534 for( CN_ITEM* citem : cnItems )
535 {
536 // Do not merge an end which has different width tracks attached -- it's a
537 // common use-case for necking-down a track between pads.
538 std::vector<PCB_TRACK*> sameWidthCandidates;
539 std::vector<PCB_TRACK*> differentWidthCandidates;
540
541 for( CN_ITEM* connected : citem->ConnectedItems() )
542 {
543 if( !connected->Valid() )
544 continue;
545
546 BOARD_CONNECTED_ITEM* candidate = connected->Parent();
547
548 if( candidate->Type() == PCB_TRACE_T && !candidate->HasFlag( IS_DELETED )
549 && !filterItem( candidate ) )
550 {
551 PCB_TRACK* candidateSegment = static_cast<PCB_TRACK*>( candidate );
552
553 if( candidateSegment->GetWidth() == segment->GetWidth() )
554 {
555 sameWidthCandidates.push_back( candidateSegment );
556 }
557 else
558 {
559 differentWidthCandidates.push_back( candidateSegment );
560 break;
561 }
562 }
563 }
564
565 if( !differentWidthCandidates.empty() )
566 continue;
567
568 for( PCB_TRACK* candidate : sameWidthCandidates )
569 {
570 if( candidate < segment ) // avoid duplicate merges
571 continue;
572
573 if( segment->ApproxCollinear( *candidate )
574 && testMergeCollinearSegments( segment, candidate ) )
575 {
576 tracks.emplace_back( segment, candidate );
577 break;
578 }
579 }
580 }
581 }
582
583 return tracks;
584 };
585
586 // The idea here is to parallelize the loop that does not modify the connectivity
587 // and extract all of the pairs of segments that might be merged. Then, perform
588 // the actual merge in the main loop.
590 auto merge_returns = tp.submit_blocks( 0, m_brd->Tracks().size(), track_loop );
591 bool retval = false;
592
593 for( size_t ii = 0; ii < merge_returns.size(); ++ii )
594 {
595 std::future<std::vector<std::pair<PCB_TRACK*, PCB_TRACK*>>>& ret = merge_returns[ii];
596
597 if( ret.valid() )
598 {
599 for( auto& [seg1, seg2] : ret.get() )
600 {
601 retval = true;
602
603 if( seg1->HasFlag( IS_DELETED ) || seg2->HasFlag( IS_DELETED ) )
604 continue;
605
606 mergeCollinearSegments( seg1, seg2 );
607 }
608 }
609 }
610
611 return retval;
612 };
613
614 if( aMergeSegments )
615 {
616 do
617 {
618 while( !m_brd->BuildConnectivity() )
619 wxSafeYield();
620
621 // BuildConnectivity adds items but doesn't establish connections between them.
622 // RecalculateRatsnest triggers searchConnections which actually finds and links
623 // connected items in the connectivity graph.
624 m_brd->GetConnectivity()->RecalculateRatsnest();
625
626 std::lock_guard lock( m_mutex );
627 m_connectedItemsCache.clear();
628 } while( mergeSegments( m_brd->GetConnectivity()->GetConnectivityAlgo() ) );
629 }
630
631 for( PCB_TRACK* track : m_brd->Tracks() )
632 track->ClearFlags( IS_DELETED | SKIP_STRUCT );
633}
634
635
636const std::vector<BOARD_CONNECTED_ITEM*>& TRACKS_CLEANER::getConnectedItems( PCB_TRACK* aTrack )
637{
638 const std::shared_ptr<CONNECTIVITY_DATA>& connectivity = m_brd->GetConnectivity();
639 std::lock_guard lock( m_mutex );
640
641 if( !m_connectedItemsCache.contains( aTrack ) )
642 m_connectedItemsCache[aTrack] = connectivity->GetConnectedItems( aTrack );
643
644 return m_connectedItemsCache.at( aTrack );
645}
646
647
649{
650 if( aSeg1->IsLocked() || aSeg2->IsLocked() )
651 return false;
652
653 // Collect the unique points where the two tracks are connected to other items
654 const unsigned p1s = 1 << 0;
655 const unsigned p1e = 1 << 1;
656 const unsigned p2s = 1 << 2;
657 const unsigned p2e = 1 << 3;
658 std::vector<VECTOR2I> pts = { aSeg1->GetStart(), aSeg1->GetEnd(), aSeg2->GetStart(), aSeg2->GetEnd() };
659 std::atomic<unsigned> flags = 0;
660
661 auto collectPtsSeg1 =
662 [&]( BOARD_CONNECTED_ITEM* citem )
663 {
664 if( std::popcount( flags.load() ) > 2 )
665 return;
666
667 if( citem->Type() == PCB_TRACE_T || citem->Type() == PCB_ARC_T
668 || citem->Type() == PCB_VIA_T )
669 {
670 PCB_TRACK* track = static_cast<PCB_TRACK*>( citem );
671
672 if( track->IsPointOnEnds( aSeg1->GetStart() ) )
673 flags |= p1s;
674
675 if( track->IsPointOnEnds( aSeg1->GetEnd() ) )
676 flags |= p1e;
677 }
678 else
679 {
680 if( !( flags & p1s ) && citem->HitTest( aSeg1->GetStart(), ( aSeg1->GetWidth() + 1 ) / 2 ) )
681 flags |= p1s;
682
683 if( !( flags & p1e ) && citem->HitTest( aSeg1->GetEnd(), ( aSeg1->GetWidth() + 1 ) / 2 ) )
684 flags |= p1e;
685 }
686 };
687
688 auto collectPtsSeg2 =
689 [&]( BOARD_CONNECTED_ITEM* citem )
690 {
691 if( std::popcount( flags.load() ) > 2 )
692 return;
693
694 if( citem->Type() == PCB_TRACE_T || citem->Type() == PCB_ARC_T
695 || citem->Type() == PCB_VIA_T )
696 {
697 PCB_TRACK* track = static_cast<PCB_TRACK*>( citem );
698
699 if( track->IsPointOnEnds( aSeg2->GetStart() ) )
700 flags |= p2s;
701
702 if( track->IsPointOnEnds( aSeg2->GetEnd() ) )
703 flags |= p2e;
704 }
705 else
706 {
707 if( !( flags & p2s ) && citem->HitTest( aSeg2->GetStart(), ( aSeg2->GetWidth() + 1 ) / 2 ) )
708 flags |= p2s;
709
710 if( !( flags & p2e ) && citem->HitTest( aSeg2->GetEnd(), ( aSeg2->GetWidth() + 1 ) / 2 ) )
711 flags |= p2e;
712 }
713 };
714
715 for( BOARD_CONNECTED_ITEM* item : getConnectedItems( aSeg1 ) )
716 {
717 if( item->HasFlag( IS_DELETED ) )
718 continue;
719
720 if( item != aSeg1 && item != aSeg2 )
721 collectPtsSeg1( item );
722 }
723
724 for( BOARD_CONNECTED_ITEM* item : getConnectedItems( aSeg2 ) )
725 {
726 if( item->HasFlag( IS_DELETED ) )
727 continue;
728
729 if( item != aSeg1 && item != aSeg2 )
730 collectPtsSeg2( item );
731 }
732
733 // This means there is a node in the center
734 if( std::popcount( flags.load() ) > 2 )
735 return false;
736
737 // Verify the removed point after merging is not a node.
738 // If it is a node (i.e. if more than one other item is connected, the segments cannot be merged
739
740 PCB_TRACK dummy_seg( *aSeg1 );
741
742 if( !aDummySeg )
743 aDummySeg = &dummy_seg;
744
745 // Calculate the new ends of the segment to merge, and store them to dummy_seg:
746 int min_x = std::min( aSeg1->GetStart().x,
747 std::min( aSeg1->GetEnd().x, std::min( aSeg2->GetStart().x, aSeg2->GetEnd().x ) ) );
748 int min_y = std::min( aSeg1->GetStart().y,
749 std::min( aSeg1->GetEnd().y, std::min( aSeg2->GetStart().y, aSeg2->GetEnd().y ) ) );
750 int max_x = std::max( aSeg1->GetStart().x,
751 std::max( aSeg1->GetEnd().x, std::max( aSeg2->GetStart().x, aSeg2->GetEnd().x ) ) );
752 int max_y = std::max( aSeg1->GetStart().y,
753 std::max( aSeg1->GetEnd().y, std::max( aSeg2->GetStart().y, aSeg2->GetEnd().y ) ) );
754
755 if( ( aSeg1->GetStart().x > aSeg1->GetEnd().x )
756 == ( aSeg1->GetStart().y > aSeg1->GetEnd().y ) )
757 {
758 aDummySeg->SetStart( VECTOR2I( min_x, min_y ) );
759 aDummySeg->SetEnd( VECTOR2I( max_x, max_y ) );
760 }
761 else
762 {
763 aDummySeg->SetStart( VECTOR2I( min_x, max_y ) );
764 aDummySeg->SetEnd( VECTOR2I( max_x, min_y ) );
765 }
766
767 // The new ends of the segment must be connected to all of the same points as the original
768 // segments. If not, the segments cannot be merged.
769 for( unsigned i = 0; i < 4; ++i )
770 {
771 if( ( flags & ( 1 << i ) ) && !aDummySeg->IsPointOnEnds( pts[i] ) )
772 return false;
773 }
774
775 // Now find the removed end(s) and stop merging if it is a node:
776 return !testTrackEndpointIsNode( aSeg1, aDummySeg->IsPointOnEnds( aSeg1->GetStart() ),
777 aDummySeg->IsPointOnEnds( aSeg1->GetEnd() ) );
778}
779
781{
782 PCB_TRACK dummy_seg( *aSeg1 );
783
784 if( !testMergeCollinearSegments( aSeg1, aSeg2, &dummy_seg ) )
785 return false;
786
787 std::shared_ptr<CLEANUP_ITEM> item = std::make_shared<CLEANUP_ITEM>( CLEANUP_MERGE_TRACKS );
788 item->SetItems( aSeg1, aSeg2 );
789 m_itemsList->push_back( std::move( item ) );
790
791 aSeg2->SetFlags( IS_DELETED );
792
793 if( !m_dryRun )
794 {
795 m_commit.Modify( aSeg1 );
796
797 *aSeg1 = dummy_seg;
798
799 m_brd->GetConnectivity()->Update( aSeg1 );
800
801 // Merge successful, seg2 has to go away
802 m_brd->Remove( aSeg2 );
803 m_commit.Removed( aSeg2 );
804 }
805
806 return true;
807}
808
809
810void TRACKS_CLEANER::removeItems( std::set<BOARD_ITEM*>& aItems )
811{
812 for( BOARD_ITEM* item : aItems )
813 {
814 m_brd->Remove( item );
815 m_commit.Removed( item );
816 }
817}
@ 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:322
CN_ITEM represents a BOARD_CONNETED_ITEM in the connectivity system (ie: a pad, track/arc/via,...
const std::vector< CN_ITEM * > & ConnectedItems() const
Implement an R-tree for fast spatial and layer indexing of connectable items.
Definition drc_rtree.h:49
int QueryColliding(BOARD_ITEM *aRefItem, PCB_LAYER_ID aRefLayer, PCB_LAYER_ID aTargetLayer, std::function< bool(BOARD_ITEM *)> aFilter=nullptr, std::function< bool(BOARD_ITEM *)> aVisitor=nullptr, int aClearance=0) const
This is a fast test which essentially does bounding-box overlap given a worst-case clearance.
Definition drc_rtree.h:221
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:101
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:148
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:111
bool HasFlag(EDA_ITEM_FLAGS aFlag) const
Definition eda_item.h:152
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:97
@ 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