1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
|
/**
* @file connect.cpp
* @brief Functions to handle existing tracks in ratsnest calculations.
*/
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2012 Jean-Pierre Charras, jean-pierre.charras@ujf-grenoble.fr
* Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
* Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <fctsys.h>
#include <common.h>
#include <macros.h>
#include <wxBasePcbFrame.h>
#include <pcbnew.h>
// Helper classes to handle connection points
#include <connect.h>
extern void Merge_SubNets_Connected_By_CopperAreas( BOARD* aPcb );
extern void Merge_SubNets_Connected_By_CopperAreas( BOARD* aPcb, int aNetcode );
// Local functions
static void RebuildTrackChain( BOARD* pcb );
CONNECTIONS::CONNECTIONS( BOARD * aBrd )
{
m_brd = aBrd;
m_firstTrack = NULL;
m_lastTrack = NULL;
}
/* Fills m_sortedPads with all pads that be connected to tracks
* pads are sorted by X coordinate ( and Y coordinates for same X value )
* aNetcode = net code to filter pads or < 0 to put all pads in list
*/
void CONNECTIONS::BuildPadsList( int aNetcode )
{
// Creates sorted pad list if not exists
m_sortedPads.clear();
m_brd->GetSortedPadListByXthenYCoord( m_sortedPads, aNetcode < 0 ? -1 : aNetcode );
}
/* Explores the list of pads and adds to m_PadsConnected member
* of each pad pads connected to
* Here, connections are due to intersecting pads, not tracks
*/
void CONNECTIONS::SearchConnectionsPadsToIntersectingPads()
{
std::vector<CONNECTED_POINT*> candidates;
BuildPadsCandidatesList();
for( unsigned ii = 0; ii < m_sortedPads.size(); ii++ )
{
D_PAD* pad = m_sortedPads[ii];
pad->m_PadsConnected.clear();
candidates.clear();
CollectItemsNearTo( candidates, pad->ShapePos(), pad->GetBoundingRadius() );
// add pads to pad.m_PadsConnected, if they are connected
for( unsigned jj = 0; jj < candidates.size(); jj++ )
{
CONNECTED_POINT* item = candidates[jj];
D_PAD* candidate_pad = item->GetPad();
if( pad == candidate_pad )
continue;
if( !( pad->GetLayerSet() & candidate_pad->GetLayerSet() ).any() )
continue;
if( pad->HitTest( item->GetPoint() ) )
{
pad->m_PadsConnected.push_back( candidate_pad );
}
}
}
}
/* Explores the list of pads
* Adds to m_PadsConnected member of each track the pad(s) connected to
* Adds to m_TracksConnected member of each pad the track(s) connected to
* D_PAD::m_TracksConnected is cleared before adding items
* TRACK::m_PadsConnected is not cleared
*/
void CONNECTIONS::SearchTracksConnectedToPads( bool add_to_padlist, bool add_to_tracklist)
{
std::vector<CONNECTED_POINT*> candidates;
for( unsigned ii = 0; ii < m_sortedPads.size(); ii++ )
{
D_PAD * pad = m_sortedPads[ii];
pad->m_TracksConnected.clear();
candidates.clear();
CollectItemsNearTo( candidates, pad->GetPosition(), pad->GetBoundingRadius() );
// add this pad to track.m_PadsConnected, if it is connected
for( unsigned jj = 0; jj < candidates.size(); jj++ )
{
CONNECTED_POINT* cp_item = candidates[jj];
if( !( pad->GetLayerSet() & cp_item->GetTrack()->GetLayerSet() ).any() )
continue;
if( pad->HitTest( cp_item->GetPoint() ) )
{
if( add_to_padlist )
cp_item->GetTrack()->m_PadsConnected.push_back( pad );
if( add_to_tracklist )
pad->m_TracksConnected.push_back( cp_item->GetTrack() );
}
}
}
}
void CONNECTIONS::CollectItemsNearTo( std::vector<CONNECTED_POINT*>& aList,
const wxPoint& aPosition, int aDistMax )
{
/* Search items in m_Candidates that position is <= aDistMax from aPosition
* (Rectilinear distance)
* m_Candidates is sorted by X then Y values, so a fast binary search is used
* to locate the "best" entry point in list
* The best entry is a pad having its m_Pos.x == (or near) aPosition.x
* All candidates are near this candidate in list
* So from this entry point, a linear search is made to find all candidates
*/
int idxmax = m_candidates.size()-1;
int delta = m_candidates.size();
int idx = 0; // Starting index is the beginning of list
while( delta )
{
// Calculate half size of remaining interval to test.
// Ensure the computed value is not truncated (too small)
if( (delta & 1) && ( delta > 1 ) )
delta++;
delta /= 2;
CONNECTED_POINT& item = m_candidates[idx];
int dist = item.GetPoint().x - aPosition.x;
if( abs(dist) <= aDistMax )
{
break; // A good entry point is found. The list can be scanned from this point.
}
else if( item.GetPoint().x < aPosition.x ) // We should search after this item
{
idx += delta;
if( idx > idxmax )
idx = idxmax;
}
else // We should search before this item
{
idx -= delta;
if( idx < 0 )
idx = 0;
}
}
/* Now explore the candidate list from the "best" entry point found
* (candidate "near" aPosition.x)
* We explore the list until abs(candidate->m_Point.x - aPosition.x) > aDistMax
* because the list is sorted by X position (and for a given X pos, by Y pos)
* Currently a linear search is made because the number of candidates
* having the right X position is usually small
*/
// search next candidates in list
wxPoint diff;
for( int ii = idx; ii <= idxmax; ii++ )
{
CONNECTED_POINT* item = &m_candidates[ii];
diff = item->GetPoint() - aPosition;
if( abs(diff.x) > aDistMax )
break; // Exit: the distance is to long, we cannot find other candidates
if( abs(diff.y) > aDistMax )
continue; // the y distance is to long, but we can find other candidates
// We have here a good candidate: add it
aList.push_back( item );
}
// search previous candidates in list
for( int ii = idx-1; ii >=0; ii-- )
{
CONNECTED_POINT * item = &m_candidates[ii];
diff = item->GetPoint() - aPosition;
if( abs(diff.x) > aDistMax )
break;
if( abs(diff.y) > aDistMax )
continue;
// We have here a good candidate:add it
aList.push_back( item );
}
}
void CONNECTIONS::BuildPadsCandidatesList()
{
m_candidates.clear();
m_candidates.reserve( m_sortedPads.size() );
for( unsigned ii = 0; ii < m_sortedPads.size(); ii++ )
{
D_PAD * pad = m_sortedPads[ii];
CONNECTED_POINT candidate( pad, pad->GetPosition() );
m_candidates.push_back( candidate );
}
}
/* sort function used to sort .m_Connected by X the Y values
* items are sorted by X coordinate value,
* and for same X value, by Y coordinate value.
*/
static bool sortConnectedPointByXthenYCoordinates( const CONNECTED_POINT & aRef,
const CONNECTED_POINT & aTst )
{
if( aRef.GetPoint().x == aTst.GetPoint().x )
return aRef.GetPoint().y < aTst.GetPoint().y;
return aRef.GetPoint().x < aTst.GetPoint().x;
}
void CONNECTIONS::BuildTracksCandidatesList( TRACK* aBegin, TRACK* aEnd)
{
m_candidates.clear();
m_firstTrack = m_lastTrack = aBegin;
unsigned ii = 0;
// Count candidates ( i.e. end points )
for( const TRACK* track = aBegin; track; track = track->Next() )
{
if( track->Type() == PCB_VIA_T )
ii++;
else
ii += 2;
m_lastTrack = track;
if( track == aEnd )
break;
}
// Build candidate list
m_candidates.reserve( ii );
for( TRACK* track = aBegin; track; track = track->Next() )
{
CONNECTED_POINT candidate( track, track->GetStart() );
m_candidates.push_back( candidate );
if( track->Type() != PCB_VIA_T )
{
CONNECTED_POINT candidate2( track, track->GetEnd());
m_candidates.push_back( candidate2 );
}
if( track == aEnd )
break;
}
// Sort list by increasing X coordinate,
// and for increasing Y coordinate when items have the same X coordinate
// So candidates to the same location are consecutive in list.
sort( m_candidates.begin(), m_candidates.end(), sortConnectedPointByXthenYCoordinates );
}
/* Populates .m_connected with tracks/vias connected to aTrack
* param aTrack = track or via to use as reference
* For calculation time reason, an exhaustive search cannot be made
* and a proximity search is made:
* Only tracks with one end near one end of aTrack are collected.
* near means dist <= aTrack width / 2
* because with this constraint we can make a fast search in track list
* m_candidates is expected to be populated by the track candidates ends list
*/
int CONNECTIONS::SearchConnectedTracks( const TRACK* aTrack )
{
int count = 0;
m_connected.clear();
LSET layerMask = aTrack->GetLayerSet();
// Search for connections to starting point:
#define USE_EXTENDED_SEARCH
#ifdef USE_EXTENDED_SEARCH
int dist_max = aTrack->GetWidth() / 2;
static std::vector<CONNECTED_POINT*> tracks_candidates;
#endif
wxPoint position = aTrack->GetStart();
for( int kk = 0; kk < 2; kk++ )
{
#ifndef USE_EXTENDED_SEARCH
int idx = searchEntryPointInCandidatesList( position );
if( idx >= 0 )
{
// search after:
for( unsigned ii = idx; ii < m_candidates.size(); ii ++ )
{
if( m_candidates[ii].GetTrack() == aTrack )
continue;
if( m_candidates[ii].GetPoint() != position )
break;
if( ( m_candidates[ii].GetTrack()->GetLayerSet() & layerMask ).any() )
m_connected.push_back( m_candidates[ii].GetTrack() );
}
// search before:
for( int ii = idx-1; ii >= 0; ii -- )
{
if( m_candidates[ii].GetTrack() == aTrack )
continue;
if( m_candidates[ii].GetPoint() != position )
break;
if( ( m_candidates[ii].GetTrack()->GetLayerSet() & layerMask ).any() )
m_connected.push_back( m_candidates[ii].GetTrack() );
}
}
#else
tracks_candidates.clear();
CollectItemsNearTo( tracks_candidates, position, dist_max );
for( unsigned ii = 0; ii < tracks_candidates.size(); ii++ )
{
TRACK* ctrack = tracks_candidates[ii]->GetTrack();
if( !( ctrack->GetLayerSet() & layerMask ).any() )
continue;
if( ctrack == aTrack )
continue;
// We have a good candidate: calculate the actual distance
// between ends, which should be <= dist max.
wxPoint delta = tracks_candidates[ii]->GetPoint() - position;
int dist = KiROUND( EuclideanNorm( delta ) );
if( dist > dist_max )
continue;
m_connected.push_back( ctrack );
}
#endif
// Search for connections to ending point:
if( aTrack->Type() == PCB_VIA_T )
break;
position = aTrack->GetEnd();
}
return count;
}
int CONNECTIONS::searchEntryPointInCandidatesList( const wxPoint& aPoint )
{
// Search the aPoint coordinates in m_Candidates
// m_Candidates is sorted by X then Y values, and a fast binary search is used
int idxmax = m_candidates.size()-1;
int delta = m_candidates.size();
int idx = 0; // Starting index is the beginning of list
while( delta )
{
// Calculate half size of remaining interval to test.
// Ensure the computed value is not truncated (too small)
if( ( delta & 1 ) && ( delta > 1 ) )
delta++;
delta /= 2;
CONNECTED_POINT& candidate = m_candidates[idx];
if( candidate.GetPoint() == aPoint ) // candidate found
{
return idx;
}
// Not found: test the middle of the remaining sub list
if( candidate.GetPoint().x == aPoint.x ) // Must search considering Y coordinate
{
if(candidate.GetPoint().y < aPoint.y) // Must search after this item
{
idx += delta;
if( idx > idxmax )
idx = idxmax;
}
else // Must search before this item
{
idx -= delta;
if( idx < 0 )
idx = 0;
}
}
else if( candidate.GetPoint().x < aPoint.x ) // Must search after this item
{
idx += delta;
if( idx > idxmax )
idx = idxmax;
}
else // Must search before this item
{
idx -= delta;
if( idx < 0 )
idx = 0;
}
}
return -1;
}
/* Used after a track change (delete a track ou add a track)
* Connections to pads are recalculated
* Note also aFirstTrack (and aLastTrack ) can be NULL
*/
void CONNECTIONS::Build_CurrNet_SubNets_Connections( TRACK* aFirstTrack, TRACK* aLastTrack, int aNetcode )
{
m_firstTrack = aFirstTrack; // The first track used to build m_Candidates
m_lastTrack = aLastTrack; // The last track used to build m_Candidates
// Pads subnets are expected already cleared, because this function
// does not know the full list of pads
BuildTracksCandidatesList( aFirstTrack, aLastTrack );
TRACK* curr_track;
for( curr_track = aFirstTrack; curr_track != NULL; curr_track = curr_track->Next() )
{
// Clear track subnet id (Pads subnets are cleared outside this function)
curr_track->SetSubNet( 0 );
curr_track->m_TracksConnected.clear();
curr_track->m_PadsConnected.clear();
// Update connections between tracks:
SearchConnectedTracks( curr_track );
curr_track->m_TracksConnected = m_connected;
if( curr_track == aLastTrack )
break;
}
// Update connections between tracks and pads
BuildPadsList( aNetcode );
SearchTracksConnectedToPads();
// Update connections between intersecting pads (no tracks)
SearchConnectionsPadsToIntersectingPads();
// Creates sub nets (clusters) for the current net:
Propagate_SubNets();
}
/**
* Change a subnet value to a new value, in m_sortedPads pad list
* After that, 2 cluster (or subnets) are merged into only one.
* Note: the resulting subnet value is the smallest between aOldSubNet et aNewSubNet
*/
int CONNECTIONS::Merge_PadsSubNets( int aOldSubNet, int aNewSubNet )
{
int change_count = 0;
if( aOldSubNet == aNewSubNet )
return 0;
if( (aOldSubNet > 0) && (aOldSubNet < aNewSubNet) )
std::swap( aOldSubNet, aNewSubNet );
// Examine connections between intersecting pads
for( unsigned ii = 0; ii < m_sortedPads.size(); ii++ )
{
D_PAD * curr_pad = m_sortedPads[ii];
if( curr_pad->GetSubNet() != aOldSubNet )
continue;
change_count++;
curr_pad->SetSubNet( aNewSubNet );
}
return change_count;
}
/*
* Change a subnet value to a new value, for tracks and pads which are connected to.
* The result is merging 2 clusters (or subnets) into only one cluster.
* Note: the resulting sub net value is the smallest between aOldSubNet et aNewSubNet
*/
int CONNECTIONS::Merge_SubNets( int aOldSubNet, int aNewSubNet )
{
TRACK* curr_track;
int change_count = 0;
if( aOldSubNet == aNewSubNet )
return 0;
if( (aOldSubNet > 0) && (aOldSubNet < aNewSubNet) )
std::swap( aOldSubNet, aNewSubNet );
curr_track = (TRACK*)m_firstTrack;
for( ; curr_track != NULL; curr_track = curr_track->Next() )
{
if( curr_track->GetSubNet() != aOldSubNet )
{
if( curr_track == m_lastTrack )
break;
continue;
}
change_count++;
curr_track->SetSubNet( aNewSubNet );
for( unsigned ii = 0; ii < curr_track->m_PadsConnected.size(); ii++ )
{
D_PAD * pad = curr_track->m_PadsConnected[ii];
if( pad->GetSubNet() == aOldSubNet )
{
pad->SetSubNet( curr_track->GetSubNet() );
}
}
if( curr_track == m_lastTrack )
break;
}
return change_count;
}
/* Test a list of track segments, to create or propagate a sub netcode to pads and
* segments connected together.
* The track list must be sorted by nets, and all segments
* from m_firstTrack to m_lastTrack have the same net
* When 2 items are connected (a track to a pad, or a track to an other track),
* they are grouped in a cluster.
* The .m_Subnet member is the cluster identifier (subnet id)
* For a given net, if all tracks are created, there is only one cluster.
* but if not all tracks are created, there are more than one cluster,
* and some ratsnests will be left active.
* A ratsnest is active when it "connect" 2 items having different subnet id
*/
void CONNECTIONS::Propagate_SubNets()
{
int sub_netcode = 1;
TRACK* curr_track = (TRACK*)m_firstTrack;
if( curr_track )
curr_track->SetSubNet( sub_netcode );
// Examine connections between tracks and pads
for( ; curr_track != NULL; curr_track = curr_track->Next() )
{
// First: handling connections to pads
for( unsigned ii = 0; ii < curr_track->m_PadsConnected.size(); ii++ )
{
D_PAD * pad = curr_track->m_PadsConnected[ii];
if( curr_track->GetSubNet() ) // the track segment is already a cluster member
{
if( pad->GetSubNet() > 0 )
{
// The pad is already a cluster member, so we can merge the 2 clusters
Merge_SubNets( pad->GetSubNet(), curr_track->GetSubNet() );
}
else
{
/* The pad is not yet attached to a cluster , so we can add this pad to
* the cluster */
pad->SetSubNet( curr_track->GetSubNet() );
}
}
else // the track segment is not attached to a cluster
{
if( pad->GetSubNet() > 0 )
{
// it is connected to a pad in a cluster, merge this track
curr_track->SetSubNet( pad->GetSubNet() );
}
else
{
/* it is connected to a pad not in a cluster, so we must create a new
* cluster (only with the 2 items: the track and the pad) */
sub_netcode++;
curr_track->SetSubNet( sub_netcode );
pad->SetSubNet( curr_track->GetSubNet() );
}
}
}
// Test connections between segments
for( unsigned ii = 0; ii < curr_track->m_TracksConnected.size(); ii++ )
{
BOARD_CONNECTED_ITEM* track = curr_track->m_TracksConnected[ii];
if( curr_track->GetSubNet() ) // The current track is already a cluster member
{
// The other track is already a cluster member, so we can merge the 2 clusters
if( track->GetSubNet() )
{
Merge_SubNets( track->GetSubNet(), curr_track->GetSubNet() );
}
else
{
// The other track is not yet attached to a cluster , so we can add this
// other track to the cluster
track->SetSubNet( curr_track->GetSubNet() );
}
}
else // the current track segment is not yet attached to a cluster
{
if( track->GetSubNet() )
{
// The other track is already a cluster member, so we can add
// the current segment to the cluster
curr_track->SetSubNet( track->GetSubNet() );
}
else
{
// it is connected to an other segment not in a cluster, so we must
// create a new cluster (only with the 2 track segments)
sub_netcode++;
curr_track->SetSubNet( sub_netcode );
track->SetSubNet( curr_track->GetSubNet() );
}
}
}
if( curr_track == m_lastTrack )
break;
}
// Examine connections between intersecting pads, and propagate
// sub_netcodes to intersecting pads
for( unsigned ii = 0; ii < m_sortedPads.size(); ii++ )
{
D_PAD* curr_pad = m_sortedPads[ii];
for( unsigned jj = 0; jj < curr_pad->m_PadsConnected.size(); jj++ )
{
D_PAD* pad = curr_pad->m_PadsConnected[jj];
if( curr_pad->GetSubNet() ) // the current pad is already attached to a cluster
{
if( pad->GetSubNet() > 0 )
{
// The pad is already a cluster member, so we can merge the 2 clusters
// Store the initial subnets, which will be modified by Merge_PadsSubNets
int subnet1 = pad->GetSubNet();
int subnet2 = curr_pad->GetSubNet();
// merge subnets of pads only, even those not connected by tracks
Merge_PadsSubNets( subnet1, subnet2 );
// merge subnets of tracks (and pads, which are already merged)
Merge_SubNets( subnet1, subnet2 );
}
else
{
// The pad is not yet attached to a cluster,
// so we can add this pad to the cluster
pad->SetSubNet( curr_pad->GetSubNet() );
}
}
else // the current pad is not attached to a cluster
{
if( pad->GetSubNet() > 0 )
{
// the connected pad is in a cluster,
// so we can add the current pad to the cluster
curr_pad->SetSubNet( pad->GetSubNet() );
}
else
{
// the connected pad is not in a cluster,
// so we must create a new cluster, with the 2 pads.
sub_netcode++;
curr_pad->SetSubNet( sub_netcode );
pad->SetSubNet( curr_pad->GetSubNet() );
}
}
}
}
}
/*
* Test all connections of the board,
* and update subnet variable of pads and tracks
* TestForActiveLinksInRatsnest must be called after this function
* to update active/inactive ratsnest items status
*/
void PCB_BASE_FRAME::TestConnections()
{
// Clear the cluster identifier for all pads
for( unsigned i = 0; i< m_Pcb->GetPadCount(); ++i )
{
D_PAD* pad = m_Pcb->GetPad(i);
pad->SetZoneSubNet( 0 );
pad->SetSubNet( 0 );
}
m_Pcb->Test_Connections_To_Copper_Areas();
// Test existing connections net by net
// note some nets can have no tracks, and pads intersecting
// so Build_CurrNet_SubNets_Connections must be called for each net
CONNECTIONS connections( m_Pcb );
int last_net_tested = 0;
int current_net_code = 0;
for( TRACK* track = m_Pcb->m_Track; track; )
{
// At this point, track is the first track of a given net
current_net_code = track->GetNetCode();
// Get last track of the current net
TRACK* lastTrack = track->GetEndNetCode( current_net_code );
if( current_net_code > 0 ) // do not spend time if net code = 0 ( dummy net )
{
// Test all previous nets having no tracks
for( int net = last_net_tested+1; net < current_net_code; net++ )
connections.Build_CurrNet_SubNets_Connections( NULL, NULL, net );
connections.Build_CurrNet_SubNets_Connections( track, lastTrack, current_net_code );
last_net_tested = current_net_code;
}
track = lastTrack->Next(); // this is now the first track of the next net
}
// Test last nets without tracks, if any
int netsCount = m_Pcb->GetNetCount();
for( int net = last_net_tested+1; net < netsCount; net++ )
connections.Build_CurrNet_SubNets_Connections( NULL, NULL, net );
Merge_SubNets_Connected_By_CopperAreas( m_Pcb );
return;
}
void PCB_BASE_FRAME::TestNetConnection( wxDC* aDC, int aNetCode )
{
// Skip dummy net -1, and "not connected" net 0 (grouping all not connected pads)
if( aNetCode <= 0 )
return;
if( (m_Pcb->m_Status_Pcb & LISTE_RATSNEST_ITEM_OK) == 0 )
Compile_Ratsnest( aDC, true );
// Clear the cluster identifier (subnet) of pads for this net
// Pads are grouped by netcode (and in netname alphabetic order)
for( unsigned i = 0; i < m_Pcb->GetPadCount(); ++i )
{
D_PAD* pad = m_Pcb->GetPad(i);
if( m_Pcb->GetPad(i)->GetNetCode() == aNetCode )
pad->SetSubNet( 0 );
}
m_Pcb->Test_Connections_To_Copper_Areas( aNetCode );
// Search for the first and the last segment relative to the given net code
if( m_Pcb->m_Track )
{
CONNECTIONS connections( m_Pcb );
TRACK* lastTrack = NULL;
TRACK* firstTrack = m_Pcb->m_Track.GetFirst()->GetStartNetCode( aNetCode );
if( firstTrack )
lastTrack = firstTrack->GetEndNetCode( aNetCode );
if( firstTrack && lastTrack ) // i.e. if there are segments
{
connections.Build_CurrNet_SubNets_Connections( firstTrack, lastTrack, aNetCode );
}
}
Merge_SubNets_Connected_By_CopperAreas( m_Pcb, aNetCode );
// rebuild the active ratsnest for this net
DrawGeneralRatsnest( aDC, aNetCode );
TestForActiveLinksInRatsnest( aNetCode );
DrawGeneralRatsnest( aDC, aNetCode );
// Display results
wxString msg;
int net_notconnected_count = 0;
NETINFO_ITEM* net = m_Pcb->FindNet( aNetCode );
if( net ) // Should not occur, but ...
{
for( unsigned ii = net->m_RatsnestStartIdx; ii < net->m_RatsnestEndIdx; ii++ )
{
if( m_Pcb->m_FullRatsnest[ii].IsActive() )
net_notconnected_count++;
}
msg.Printf( wxT( "links %d nc %d net %d: not conn %d" ),
m_Pcb->GetRatsnestsCount(), m_Pcb->GetUnconnectedNetCount(), aNetCode,
net_notconnected_count );
}
else
msg.Printf( wxT( "net not found: netcode %d" ), aNetCode );
SetStatusText( msg );
return;
}
/* search connections between tracks and pads and propagate pad net codes to the track
* segments.
* Pads netcodes are assumed to be up to date.
*/
void PCB_BASE_FRAME::RecalculateAllTracksNetcode()
{
// Build the net info list
GetBoard()->BuildListOfNets();
// Reset variables and flags used in computation
for( TRACK* t = m_Pcb->m_Track; t; t = t->Next() )
{
t->m_TracksConnected.clear();
t->m_PadsConnected.clear();
t->start = NULL;
t->end = NULL;
t->SetState( BUSY | IN_EDIT | BEGIN_ONPAD | END_ONPAD, false );
t->SetZoneSubNet( 0 );
t->SetNetCode( NETINFO_LIST::UNCONNECTED );
}
// If no pad, reset pointers and netcode, and do nothing else
if( m_Pcb->GetPadCount() == 0 )
return;
CONNECTIONS connections( m_Pcb );
connections.BuildPadsList();
connections.BuildTracksCandidatesList(m_Pcb->m_Track);
// First pass: build connections between track segments and pads.
connections.SearchTracksConnectedToPads();
// For tracks connected to at least one pad,
// set the track net code to the pad netcode
for( TRACK* t = m_Pcb->m_Track; t; t = t->Next() )
{
if( t->m_PadsConnected.size() )
t->SetNetCode( t->m_PadsConnected[0]->GetNetCode() );
}
// Pass 2: build connections between track ends
for( TRACK* t = m_Pcb->m_Track; t; t = t->Next() )
{
connections.SearchConnectedTracks( t );
connections.GetConnectedTracks( t );
}
// Propagate net codes from a segment to other connected segments
bool new_pass_request = true; // set to true if a track has its netcode changed from 0
// to a known netcode to re-evaluate netcodes
// of connected items
while( new_pass_request )
{
new_pass_request = false;
for( TRACK* t = m_Pcb->m_Track; t; t = t->Next() )
{
int netcode = t->GetNetCode();
if( netcode == 0 )
{
// try to find a connected item having a netcode
for( unsigned kk = 0; kk < t->m_TracksConnected.size(); kk++ )
{
int altnetcode = t->m_TracksConnected[kk]->GetNetCode();
if( altnetcode )
{
new_pass_request = true;
netcode = altnetcode;
t->SetNetCode(netcode);
break;
}
}
}
if( netcode ) // this track has a netcode
{
// propagate this netcode to connected tracks having no netcode
for( unsigned kk = 0; kk < t->m_TracksConnected.size(); kk++ )
{
int altnetcode = t->m_TracksConnected[kk]->GetNetCode();
if( altnetcode == 0 )
{
t->m_TracksConnected[kk]->SetNetCode(netcode);
new_pass_request = true;
}
}
}
}
}
// Sort the track list by net codes:
RebuildTrackChain( m_Pcb );
}
/*
* Function SortTracksByNetCode used in RebuildTrackChain()
* to sort track segments by net code.
*/
static bool SortTracksByNetCode( const TRACK* const & ref, const TRACK* const & compare )
{
// For items having the same Net, keep the order in list
if( ref->GetNetCode() == compare->GetNetCode())
return ref->m_Param < compare->m_Param;
return ref->GetNetCode() < compare->GetNetCode();
}
/**
* Helper function RebuildTrackChain
* rebuilds the track segment linked list in order to have a chain
* sorted by increasing netcodes.
* We try to keep order of track segments in list, when possible
* @param pcb = board to rebuild
*/
static void RebuildTrackChain( BOARD* pcb )
{
if( pcb->m_Track == NULL )
return;
int item_count = pcb->m_Track.GetCount();
std::vector<TRACK*> trackList;
trackList.reserve( item_count );
// Put track list in a temporary list to sort tracks by netcode
// We try to keep the initial order of track segments in list, when possible
// so we use m_Param (a member variable used for temporary storage)
// to temporary keep trace of the order of segments
// The sort function uses this variable to sort items that
// have the same net code.
// Without this, during sorting, the initial order is sometimes lost
// by the sort algorithm
for( int ii = 0; ii < item_count; ++ii )
{
pcb->m_Track->m_Param = ii;
trackList.push_back( pcb->m_Track.PopFront() );
}
// the list is empty now
wxASSERT( pcb->m_Track == NULL && pcb->m_Track.GetCount()==0 );
sort( trackList.begin(), trackList.end(), SortTracksByNetCode );
// add them back to the list
for( int i = 0; i < item_count; ++i )
pcb->m_Track.PushBack( trackList[i] );
}
|