1 /*
2  * This program source code file is part of KICAD, a free EDA CAD application.
3  *
4  * Copyright (C) 2016-2018 CERN
5  * Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, you may find one here:
21  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
22  * or you may search the http://www.gnu.org website for the version 2 license,
23  * or you may write to the Free Software Foundation, Inc.,
24  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
25  */
26 
27 #include <connectivity/connectivity_algo.h>
28 #include <progress_reporter.h>
29 #include <geometry/geometry_utils.h>
30 #include <board_commit.h>
31 
32 #include <wx/log.h>
33 
34 #include <thread>
35 #include <mutex>
36 #include <algorithm>
37 #include <future>
38 
39 #ifdef PROFILE
40 #include <profile.h>
41 #endif
42 
43 
Remove(BOARD_ITEM * aItem)44 bool CN_CONNECTIVITY_ALGO::Remove( BOARD_ITEM* aItem )
45 {
46     markItemNetAsDirty( aItem );
47 
48     switch( aItem->Type() )
49     {
50     case PCB_FOOTPRINT_T:
51         for( PAD* pad : static_cast<FOOTPRINT*>( aItem )->Pads() )
52         {
53             m_itemMap[pad].MarkItemsAsInvalid();
54             m_itemMap.erase( pad );
55         }
56 
57         m_itemList.SetDirty( true );
58         break;
59 
60     case PCB_PAD_T:
61         m_itemMap[aItem].MarkItemsAsInvalid();
62         m_itemMap.erase( aItem );
63         m_itemList.SetDirty( true );
64         break;
65 
66     case PCB_TRACE_T:
67     case PCB_ARC_T:
68         m_itemMap[aItem].MarkItemsAsInvalid();
69         m_itemMap.erase( aItem );
70         m_itemList.SetDirty( true );
71         break;
72 
73     case PCB_VIA_T:
74         m_itemMap[aItem].MarkItemsAsInvalid();
75         m_itemMap.erase( aItem );
76         m_itemList.SetDirty( true );
77         break;
78 
79     case PCB_ZONE_T:
80         m_itemMap[aItem].MarkItemsAsInvalid();
81         m_itemMap.erase ( aItem );
82         m_itemList.SetDirty( true );
83         break;
84 
85     default:
86         return false;
87     }
88 
89     // Once we delete an item, it may connect between lists, so mark both as potentially invalid
90     m_itemList.SetHasInvalid( true );
91 
92     return true;
93 }
94 
95 
markItemNetAsDirty(const BOARD_ITEM * aItem)96 void CN_CONNECTIVITY_ALGO::markItemNetAsDirty( const BOARD_ITEM* aItem )
97 {
98     if( aItem->IsConnected() )
99     {
100         auto citem = static_cast<const BOARD_CONNECTED_ITEM*>( aItem );
101         MarkNetAsDirty( citem->GetNetCode() );
102     }
103     else
104     {
105         if( aItem->Type() == PCB_FOOTPRINT_T )
106         {
107             const FOOTPRINT* footprint = static_cast<const FOOTPRINT*>( aItem );
108 
109             for( PAD* pad : footprint->Pads() )
110                 MarkNetAsDirty( pad->GetNetCode() );
111         }
112     }
113 }
114 
115 
Add(BOARD_ITEM * aItem)116 bool CN_CONNECTIVITY_ALGO::Add( BOARD_ITEM* aItem )
117 {
118     if( !aItem->IsOnCopperLayer() )
119         return false;
120 
121     switch( aItem->Type() )
122     {
123     case PCB_NETINFO_T:
124         MarkNetAsDirty( static_cast<NETINFO_ITEM*>( aItem )->GetNetCode() );
125         break;
126 
127     case PCB_FOOTPRINT_T:
128     {
129         if( static_cast<FOOTPRINT*>( aItem )->GetAttributes() & FP_JUST_ADDED )
130             return false;
131 
132         for( PAD* pad : static_cast<FOOTPRINT*>( aItem )->Pads() )
133         {
134             if( m_itemMap.find( pad ) != m_itemMap.end() )
135                 return false;
136 
137             add( m_itemList, pad );
138         }
139 
140         break;
141     }
142 
143     case PCB_PAD_T:
144     {
145         if( FOOTPRINT* fp = dynamic_cast<FOOTPRINT*>( aItem->GetParentFootprint() ) )
146         {
147             if( fp->GetAttributes() & FP_JUST_ADDED )
148                 return false;
149         }
150 
151         if( m_itemMap.find( aItem ) != m_itemMap.end() )
152             return false;
153 
154         add( m_itemList, static_cast<PAD*>( aItem ) );
155         break;
156     }
157 
158     case PCB_TRACE_T:
159         if( m_itemMap.find( aItem ) != m_itemMap.end() )
160             return false;
161 
162         add( m_itemList, static_cast<PCB_TRACK*>( aItem ) );
163         break;
164 
165     case PCB_ARC_T:
166         if( m_itemMap.find( aItem ) != m_itemMap.end() )
167             return false;
168 
169         add( m_itemList, static_cast<PCB_ARC*>( aItem ) );
170         break;
171 
172     case PCB_VIA_T:
173         if( m_itemMap.find( aItem ) != m_itemMap.end() )
174             return false;
175 
176         add( m_itemList, static_cast<PCB_VIA*>( aItem ) );
177         break;
178 
179     case PCB_ZONE_T:
180     {
181         ZONE* zone = static_cast<ZONE*>( aItem );
182 
183         if( m_itemMap.find( aItem ) != m_itemMap.end() )
184             return false;
185 
186         m_itemMap[zone] = ITEM_MAP_ENTRY();
187 
188         for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
189         {
190             for( CN_ITEM* zitem : m_itemList.Add( zone, layer ) )
191                 m_itemMap[zone].Link( zitem );
192         }
193     }
194         break;
195 
196     default:
197         return false;
198     }
199 
200     markItemNetAsDirty( aItem );
201 
202     return true;
203 }
204 
205 
searchConnections()206 void CN_CONNECTIVITY_ALGO::searchConnections()
207 {
208 #ifdef PROFILE
209     PROF_COUNTER garbage_collection( "garbage-collection" );
210 #endif
211     std::vector<CN_ITEM*> garbage;
212     garbage.reserve( 1024 );
213 
214     m_itemList.RemoveInvalidItems( garbage );
215 
216     for( auto item : garbage )
217         delete item;
218 
219 #ifdef PROFILE
220     garbage_collection.Show();
221     PROF_COUNTER search_basic( "search-basic" );
222 #endif
223 
224     std::vector<CN_ITEM*> dirtyItems;
225     std::copy_if( m_itemList.begin(), m_itemList.end(), std::back_inserter( dirtyItems ),
226                   [] ( CN_ITEM* aItem )
227                   {
228                       return aItem->Dirty();
229                   } );
230 
231     if( m_progressReporter )
232     {
233         m_progressReporter->SetMaxProgress( dirtyItems.size() );
234 
235         if( !m_progressReporter->KeepRefreshing() )
236             return;
237     }
238 
239     if( m_itemList.IsDirty() )
240     {
241         size_t parallelThreadCount = std::min<size_t>( std::thread::hardware_concurrency(),
242                                                        ( dirtyItems.size() + 7 ) / 8 );
243 
244         std::atomic<size_t> nextItem( 0 );
245         std::vector<std::future<size_t>> returns( parallelThreadCount );
246 
247         auto conn_lambda =
248                 [&nextItem, &dirtyItems]( CN_LIST* aItemList,
249                                           PROGRESS_REPORTER* aReporter) -> size_t
250                 {
251                     for( size_t i = nextItem++; i < dirtyItems.size(); i = nextItem++ )
252                     {
253                         CN_VISITOR visitor( dirtyItems[i] );
254                         aItemList->FindNearby( dirtyItems[i], visitor );
255 
256                         if( aReporter )
257                         {
258                             if( aReporter->IsCancelled() )
259                                 break;
260                             else
261                                 aReporter->AdvanceProgress();
262                         }
263                     }
264 
265                     return 1;
266                 };
267 
268         if( parallelThreadCount <= 1 )
269             conn_lambda( &m_itemList, m_progressReporter );
270         else
271         {
272             for( size_t ii = 0; ii < parallelThreadCount; ++ii )
273             {
274                 returns[ii] = std::async( std::launch::async, conn_lambda, &m_itemList,
275                                           m_progressReporter );
276             }
277 
278             for( size_t ii = 0; ii < parallelThreadCount; ++ii )
279             {
280                 // Here we balance returns with a 100ms timeout to allow UI updating
281                 std::future_status status;
282                 do
283                 {
284                     if( m_progressReporter )
285                         m_progressReporter->KeepRefreshing();
286 
287                     status = returns[ii].wait_for( std::chrono::milliseconds( 100 ) );
288                 } while( status != std::future_status::ready );
289             }
290         }
291 
292         if( m_progressReporter )
293             m_progressReporter->KeepRefreshing();
294     }
295 
296 #ifdef PROFILE
297         search_basic.Show();
298 #endif
299 
300     m_itemList.ClearDirtyFlags();
301 }
302 
303 
SearchClusters(CLUSTER_SEARCH_MODE aMode)304 const CN_CONNECTIVITY_ALGO::CLUSTERS CN_CONNECTIVITY_ALGO::SearchClusters( CLUSTER_SEARCH_MODE aMode )
305 {
306     constexpr KICAD_T types[] = { PCB_TRACE_T, PCB_ARC_T, PCB_PAD_T, PCB_VIA_T, PCB_ZONE_T,
307                                   PCB_FOOTPRINT_T, EOT };
308     constexpr KICAD_T no_zones[] = { PCB_TRACE_T, PCB_ARC_T, PCB_PAD_T, PCB_VIA_T,
309                                      PCB_FOOTPRINT_T, EOT };
310 
311     if( aMode == CSM_PROPAGATE )
312         return SearchClusters( aMode, no_zones, -1 );
313     else
314         return SearchClusters( aMode, types, -1 );
315 }
316 
317 
SearchClusters(CLUSTER_SEARCH_MODE aMode,const KICAD_T aTypes[],int aSingleNet)318 const CN_CONNECTIVITY_ALGO::CLUSTERS CN_CONNECTIVITY_ALGO::SearchClusters( CLUSTER_SEARCH_MODE aMode,
319                                                                            const KICAD_T aTypes[],
320                                                                            int aSingleNet )
321 {
322     bool withinAnyNet = ( aMode != CSM_PROPAGATE );
323 
324     std::deque<CN_ITEM*> Q;
325     std::set<CN_ITEM*> item_set;
326 
327     CLUSTERS clusters;
328 
329     if( m_itemList.IsDirty() )
330         searchConnections();
331 
332     auto addToSearchList =
333             [&item_set, withinAnyNet, aSingleNet, aTypes]( CN_ITEM *aItem )
334             {
335                 if( withinAnyNet && aItem->Net() <= 0 )
336                     return;
337 
338                 if( !aItem->Valid() )
339                     return;
340 
341                 if( aSingleNet >=0 && aItem->Net() != aSingleNet )
342                     return;
343 
344                 bool found = false;
345 
346                 for( int i = 0; aTypes[i] != EOT; i++ )
347                 {
348                     if( aItem->Parent()->Type() == aTypes[i] )
349                     {
350                         found = true;
351                         break;
352                     }
353                 }
354 
355                 if( !found )
356                     return;
357 
358                 aItem->SetVisited( false );
359 
360                 item_set.insert( aItem );
361             };
362 
363     std::for_each( m_itemList.begin(), m_itemList.end(), addToSearchList );
364 
365     if( m_progressReporter && m_progressReporter->IsCancelled() )
366         return CLUSTERS();
367 
368     while( !item_set.empty() )
369     {
370         CN_CLUSTER_PTR cluster = std::make_shared<CN_CLUSTER>();
371         CN_ITEM*       root;
372         auto           it = item_set.begin();
373 
374         while( it != item_set.end() && (*it)->Visited() )
375             it = item_set.erase( item_set.begin() );
376 
377         if( it == item_set.end() )
378             break;
379 
380         root = *it;
381         root->SetVisited( true );
382 
383         Q.clear();
384         Q.push_back( root );
385 
386         while( Q.size() )
387         {
388             CN_ITEM* current = Q.front();
389 
390             Q.pop_front();
391             cluster->Add( current );
392 
393             for( auto n : current->ConnectedItems() )
394             {
395                 if( withinAnyNet && n->Net() != root->Net() )
396                     continue;
397 
398                 if( !n->Visited() && n->Valid() )
399                 {
400                     n->SetVisited( true );
401                     Q.push_back( n );
402                 }
403             }
404         }
405 
406         clusters.push_back( cluster );
407     }
408 
409     if( m_progressReporter && m_progressReporter->IsCancelled() )
410         return CLUSTERS();
411 
412     std::sort( clusters.begin(), clusters.end(),
413                []( CN_CLUSTER_PTR a, CN_CLUSTER_PTR b )
414                {
415                    return a->OriginNet() < b->OriginNet();
416                } );
417 
418     return clusters;
419 }
420 
421 
reportProgress(PROGRESS_REPORTER * aReporter,int aCount,int aSize,int aDelta)422 void reportProgress( PROGRESS_REPORTER* aReporter, int aCount, int aSize, int aDelta )
423 {
424     if( aReporter && ( ( aCount % aDelta ) == 0 || aCount == aSize -  1 ) )
425     {
426         aReporter->SetCurrentProgress( (double) aCount / (double) aSize );
427         aReporter->KeepRefreshing( false );
428     }
429 }
430 
431 
Build(BOARD * aBoard,PROGRESS_REPORTER * aReporter)432 void CN_CONNECTIVITY_ALGO::Build( BOARD* aBoard, PROGRESS_REPORTER* aReporter )
433 {
434     int delta = 100;  // Number of additions between 2 calls to the progress bar
435     int ii = 0;
436     int size = 0;
437 
438     size += aBoard->Zones().size();
439     size += aBoard->Tracks().size();
440 
441     for( FOOTPRINT* footprint : aBoard->Footprints() )
442         size += footprint->Pads().size();
443 
444     size *= 2;      // Our caller us gets the other half of the progress bar
445 
446     delta = std::max( delta, size / 50 );
447 
448     for( ZONE* zone : aBoard->Zones() )
449     {
450         Add( zone );
451         reportProgress( aReporter, ii++, size, delta );
452     }
453 
454     for( PCB_TRACK* tv : aBoard->Tracks() )
455     {
456         Add( tv );
457         reportProgress( aReporter, ii++, size, delta );
458     }
459 
460     for( FOOTPRINT* footprint : aBoard->Footprints() )
461     {
462         for( PAD* pad : footprint->Pads() )
463         {
464             Add( pad );
465             reportProgress( aReporter, ii++, size, delta );
466         }
467     }
468 }
469 
470 
Build(const std::vector<BOARD_ITEM * > & aItems)471 void CN_CONNECTIVITY_ALGO::Build( const std::vector<BOARD_ITEM*>& aItems )
472 {
473     for( auto item : aItems )
474     {
475         switch( item->Type() )
476         {
477             case PCB_TRACE_T:
478             case PCB_ARC_T:
479             case PCB_VIA_T:
480             case PCB_PAD_T:
481                 Add( item );
482                 break;
483 
484             case PCB_FOOTPRINT_T:
485                 for( PAD* pad : static_cast<FOOTPRINT*>( item )->Pads() )
486                     Add( pad );
487 
488                 break;
489 
490             default:
491                 break;
492         }
493     }
494 }
495 
496 
propagateConnections(BOARD_COMMIT * aCommit,PROPAGATE_MODE aMode)497 void CN_CONNECTIVITY_ALGO::propagateConnections( BOARD_COMMIT* aCommit, PROPAGATE_MODE aMode )
498 {
499     bool skipConflicts = ( aMode == PROPAGATE_MODE::SKIP_CONFLICTS );
500 
501     wxLogTrace( "CN", "propagateConnections: propagate skip conflicts? %d", skipConflicts );
502 
503     for( const auto& cluster : m_connClusters )
504     {
505         if( skipConflicts && cluster->IsConflicting() )
506         {
507             wxLogTrace( "CN", "Conflicting nets in cluster %p; skipping update", cluster.get() );
508         }
509         else if( cluster->IsOrphaned() )
510         {
511             wxLogTrace( "CN", "Skipping orphaned cluster %p [net: %s]", cluster.get(),
512                     (const char*) cluster->OriginNetName().c_str() );
513         }
514         else if( cluster->HasValidNet() )
515         {
516             if( cluster->IsConflicting() )
517             {
518                 wxLogTrace( "CN", "Conflicting nets in cluster %p; chose %d (%s)", cluster.get(),
519                             cluster->OriginNet(), cluster->OriginNetName() );
520             }
521 
522             // normal cluster: just propagate from the pads
523             int n_changed = 0;
524 
525             for( auto item : *cluster )
526             {
527                 if( item->CanChangeNet() )
528                 {
529                     if( item->Valid() && item->Parent()->GetNetCode() != cluster->OriginNet() )
530                     {
531                         MarkNetAsDirty( item->Parent()->GetNetCode() );
532                         MarkNetAsDirty( cluster->OriginNet() );
533 
534                         if( aCommit )
535                             aCommit->Modify( item->Parent() );
536 
537                         item->Parent()->SetNetCode( cluster->OriginNet() );
538                         n_changed++;
539                     }
540                 }
541             }
542 
543             if( n_changed )
544             {
545                 wxLogTrace( "CN", "Cluster %p : net : %d %s", cluster.get(),
546                         cluster->OriginNet(), (const char*) cluster->OriginNetName().c_str() );
547             }
548             else
549                 wxLogTrace( "CN", "Cluster %p : nothing to propagate", cluster.get() );
550         }
551         else
552         {
553             wxLogTrace( "CN", "Cluster %p : connected to unused net", cluster.get() );
554         }
555     }
556 }
557 
558 
PropagateNets(BOARD_COMMIT * aCommit,PROPAGATE_MODE aMode)559 void CN_CONNECTIVITY_ALGO::PropagateNets( BOARD_COMMIT* aCommit, PROPAGATE_MODE aMode )
560 {
561     m_connClusters = SearchClusters( CSM_PROPAGATE );
562     propagateConnections( aCommit, aMode );
563 }
564 
565 
FindIsolatedCopperIslands(ZONE * aZone,PCB_LAYER_ID aLayer,std::vector<int> & aIslands)566 void CN_CONNECTIVITY_ALGO::FindIsolatedCopperIslands( ZONE* aZone, PCB_LAYER_ID aLayer,
567                                                       std::vector<int>& aIslands )
568 {
569     if( aZone->GetFilledPolysList( aLayer ).IsEmpty() )
570         return;
571 
572     aIslands.clear();
573 
574     Remove( aZone );
575     Add( aZone );
576 
577     m_connClusters = SearchClusters( CSM_CONNECTIVITY_CHECK );
578 
579     for( const auto& cluster : m_connClusters )
580     {
581         if( cluster->Contains( aZone ) && cluster->IsOrphaned() )
582         {
583             for( auto z : *cluster )
584             {
585                 if( z->Parent() == aZone && z->Layer() == aLayer )
586                 {
587                     aIslands.push_back( static_cast<CN_ZONE_LAYER*>(z)->SubpolyIndex() );
588                 }
589             }
590         }
591     }
592 
593     wxLogTrace( "CN", "Found %u isolated islands\n", (unsigned)aIslands.size() );
594 }
595 
FindIsolatedCopperIslands(std::vector<CN_ZONE_ISOLATED_ISLAND_LIST> & aZones)596 void CN_CONNECTIVITY_ALGO::FindIsolatedCopperIslands( std::vector<CN_ZONE_ISOLATED_ISLAND_LIST>& aZones )
597 {
598     for( auto& z : aZones )
599     {
600         Remove( z.m_zone );
601         Add( z.m_zone );
602     }
603 
604     m_connClusters = SearchClusters( CSM_CONNECTIVITY_CHECK );
605 
606     for( CN_ZONE_ISOLATED_ISLAND_LIST& zone : aZones )
607     {
608         for( PCB_LAYER_ID layer : zone.m_zone->GetLayerSet().Seq() )
609         {
610             if( zone.m_zone->GetFilledPolysList( layer ).IsEmpty() )
611                 continue;
612 
613             for( const CN_CLUSTER_PTR& cluster : m_connClusters )
614             {
615                 if( cluster->Contains( zone.m_zone ) && cluster->IsOrphaned() )
616                 {
617                     for( CN_ITEM* z : *cluster )
618                     {
619                         if( z->Parent() == zone.m_zone && z->Layer() == layer )
620                         {
621                             zone.m_islands[layer].push_back(
622                                     static_cast<CN_ZONE_LAYER*>( z )->SubpolyIndex() );
623                         }
624                     }
625                 }
626             }
627         }
628     }
629 }
630 
631 
GetClusters()632 const CN_CONNECTIVITY_ALGO::CLUSTERS& CN_CONNECTIVITY_ALGO::GetClusters()
633 {
634     m_ratsnestClusters = SearchClusters( CSM_RATSNEST );
635     return m_ratsnestClusters;
636 }
637 
638 
MarkNetAsDirty(int aNet)639 void CN_CONNECTIVITY_ALGO::MarkNetAsDirty( int aNet )
640 {
641     if( aNet < 0 )
642         return;
643 
644     if( (int) m_dirtyNets.size() <= aNet )
645     {
646         int lastNet = m_dirtyNets.size() - 1;
647 
648         if( lastNet < 0 )
649             lastNet = 0;
650 
651         m_dirtyNets.resize( aNet + 1 );
652 
653         for( int i = lastNet; i < aNet + 1; i++ )
654             m_dirtyNets[i] = true;
655     }
656 
657     m_dirtyNets[aNet] = true;
658 }
659 
660 
checkZoneItemConnection(CN_ZONE_LAYER * aZoneLayer,CN_ITEM * aItem)661 void CN_VISITOR::checkZoneItemConnection( CN_ZONE_LAYER* aZoneLayer, CN_ITEM* aItem )
662 {
663     if( aZoneLayer->Net() != aItem->Net() && !aItem->CanChangeNet() )
664         return;
665 
666     if( !aZoneLayer->BBox().Intersects( aItem->BBox() ) )
667         return;
668 
669     int accuracy = 0;
670 
671     if( aItem->Parent()->Type() == PCB_VIA_T
672             || aItem->Parent()->Type() == PCB_TRACE_T
673             || aItem->Parent()->Type() == PCB_ARC_T )
674     {
675         accuracy = ( static_cast<PCB_TRACK*>( aItem->Parent() )->GetWidth() + 1 ) / 2;
676     }
677 
678     for( int i = 0; i < aItem->AnchorCount(); ++i )
679     {
680         if( aZoneLayer->ContainsPoint( aItem->GetAnchor( i ), accuracy ) )
681         {
682             aZoneLayer->Connect( aItem );
683             aItem->Connect( aZoneLayer );
684             return;
685         }
686     }
687 }
688 
checkZoneZoneConnection(CN_ZONE_LAYER * aZoneLayerA,CN_ZONE_LAYER * aZoneLayerB)689 void CN_VISITOR::checkZoneZoneConnection( CN_ZONE_LAYER* aZoneLayerA, CN_ZONE_LAYER* aZoneLayerB )
690 {
691     const ZONE* zoneA = static_cast<const ZONE*>( aZoneLayerA->Parent() );
692     const ZONE* zoneB = static_cast<const ZONE*>( aZoneLayerB->Parent() );
693 
694     if( aZoneLayerA->Layer() != aZoneLayerB->Layer() )
695         return;
696 
697     if( aZoneLayerB->Net() != aZoneLayerA->Net() )
698         return; // we only test zones belonging to the same net
699 
700     const BOX2I& boxA = aZoneLayerA->BBox();
701     const BOX2I& boxB = aZoneLayerB->BBox();
702 
703     int radiusA = 0;
704     int radiusB = 0;
705 
706     if( zoneA->GetFilledPolysUseThickness() )
707         radiusA = ( zoneA->GetMinThickness() + 1 ) / 2;
708 
709     if( zoneB->GetFilledPolysUseThickness() )
710         radiusB = ( zoneB->GetMinThickness() + 1 ) / 2;
711 
712     PCB_LAYER_ID layer = static_cast<PCB_LAYER_ID>( aZoneLayerA->Layer() );
713 
714     const SHAPE_LINE_CHAIN& outline =
715             zoneA->GetFilledPolysList( layer ).COutline( aZoneLayerA->SubpolyIndex() );
716 
717     for( int i = 0; i < outline.PointCount(); i++ )
718     {
719         if( !boxB.Contains( outline.CPoint( i ) ) )
720             continue;
721 
722         if( aZoneLayerB->ContainsPoint( outline.CPoint( i ), radiusA ) )
723         {
724             aZoneLayerA->Connect( aZoneLayerB );
725             aZoneLayerB->Connect( aZoneLayerA );
726             return;
727         }
728     }
729 
730     const SHAPE_LINE_CHAIN& outline2 =
731             zoneB->GetFilledPolysList( layer ).COutline( aZoneLayerB->SubpolyIndex() );
732 
733     for( int i = 0; i < outline2.PointCount(); i++ )
734     {
735         if( !boxA.Contains( outline2.CPoint( i ) ) )
736             continue;
737 
738         if( aZoneLayerA->ContainsPoint( outline2.CPoint( i ), radiusB ) )
739         {
740             aZoneLayerA->Connect( aZoneLayerB );
741             aZoneLayerB->Connect( aZoneLayerA );
742             return;
743         }
744     }
745 }
746 
747 
operator ()(CN_ITEM * aCandidate)748 bool CN_VISITOR::operator()( CN_ITEM* aCandidate )
749 {
750     const BOARD_CONNECTED_ITEM* parentA = aCandidate->Parent();
751     const BOARD_CONNECTED_ITEM* parentB = m_item->Parent();
752 
753     if( !aCandidate->Valid() || !m_item->Valid() )
754         return true;
755 
756     if( parentA == parentB )
757         return true;
758 
759     if( !( parentA->GetLayerSet() & parentB->GetLayerSet() ).any() )
760         return true;
761 
762     // If both m_item and aCandidate are marked dirty, they will both be searched
763     // Since we are reciprocal in our connection, we arbitrarily pick one of the connections
764     // to conduct the expensive search
765     if( aCandidate->Dirty() && aCandidate < m_item )
766         return true;
767 
768     // We should handle zone-zone connection separately
769     if ( parentA->Type() == PCB_ZONE_T && parentB->Type() == PCB_ZONE_T )
770     {
771         checkZoneZoneConnection( static_cast<CN_ZONE_LAYER*>( m_item ),
772                                  static_cast<CN_ZONE_LAYER*>( aCandidate ) );
773         return true;
774     }
775 
776     if( parentA->Type() == PCB_ZONE_T )
777     {
778         checkZoneItemConnection( static_cast<CN_ZONE_LAYER*>( aCandidate ), m_item );
779         return true;
780     }
781 
782     if( parentB->Type() == PCB_ZONE_T )
783     {
784         checkZoneItemConnection( static_cast<CN_ZONE_LAYER*>( m_item ), aCandidate );
785         return true;
786     }
787 
788     int accuracyA = 0;
789     int accuracyB = 0;
790 
791     if( parentA->Type() == PCB_VIA_T
792             || parentA->Type() == PCB_TRACE_T
793             || parentA->Type() == PCB_ARC_T)
794         accuracyA = ( static_cast<const PCB_TRACK*>( parentA )->GetWidth() + 1 ) / 2;
795 
796     if( parentB->Type() == PCB_VIA_T
797             || parentB->Type() == PCB_TRACE_T
798             || parentB->Type() == PCB_ARC_T )
799         accuracyB = ( static_cast<const PCB_TRACK*>( parentB )->GetWidth() + 1 ) / 2;
800 
801     // Items do not necessarily have reciprocity as we only check for anchors
802     //  therefore, we check HitTest both directions A->B & B->A
803     for( int i = 0; i < aCandidate->AnchorCount(); ++i )
804     {
805         if( parentB->HitTest( wxPoint( aCandidate->GetAnchor( i ) ), accuracyA ) )
806         {
807             m_item->Connect( aCandidate );
808             aCandidate->Connect( m_item );
809             return true;
810         }
811     }
812 
813     for( int i = 0; i < m_item->AnchorCount(); ++i )
814     {
815         if( parentA->HitTest( wxPoint( m_item->GetAnchor( i ) ), accuracyB ) )
816         {
817             m_item->Connect( aCandidate );
818             aCandidate->Connect( m_item );
819             return true;
820         }
821     }
822 
823     return true;
824 };
825 
826 
Clear()827 void CN_CONNECTIVITY_ALGO::Clear()
828 {
829     m_ratsnestClusters.clear();
830     m_connClusters.clear();
831     m_itemMap.clear();
832     m_itemList.Clear();
833 
834 }
835 
SetProgressReporter(PROGRESS_REPORTER * aReporter)836 void CN_CONNECTIVITY_ALGO::SetProgressReporter( PROGRESS_REPORTER* aReporter )
837 {
838     m_progressReporter = aReporter;
839 }
840