112 const std::vector<SHEET_SYMBOLS>& aSheets )
119 result.edges.reserve( 256 );
123 const auto& sheet = *view.input;
130 auto consider = [&](
SCH_ITEM* cand ) ->
bool
143 if( s.
y == e.
y && p.
y == s.
y )
145 int minx = std::min( s.
x, e.
x );
146 int maxx = std::max( s.
x, e.
x );
148 if( p.
x >= minx && p.
x <= maxx )
154 else if( s.
x == e.
x && p.
x == s.
x )
156 int miny = std::min( s.
y, e.
y );
157 int maxy = std::max( s.
y, e.
y );
159 if( p.
y >= miny && p.
y <= maxy )
180 for(
const auto& [symbol, pins] : view.symbols )
182 if( pins.size() != 2 )
191 if( !findWireOnScreen( pins[0], wireA ) || !findWireOnScreen( pins[1], wireB ) )
202 if( pins[0]->IsPower() || pins[1]->IsPower() )
210 if( aS.
x == aE.
x && bS.
x == bE.
x && aS.
x == bS.
x )
212 else if( aS.
y == aE.
y && bS.
y == bE.
y && aS.
y == bS.
y )
219 const wxString& netA = sheet.Key( pins[0] );
220 const wxString& netB = sheet.Key( pins[1] );
222 if( netA.IsEmpty() || netB.IsEmpty() || netA == netB )
225 result.edges.push_back( { netA, netB, symbol, sc } );
233 std::set<wxString> powerNets;
237 const auto& sheet = *view.input;
239 for(
const auto& entry : view.symbols )
245 if(
const auto* net = sheet.Find( p ) )
246 powerNets.insert( net->key );
256 std::set<wxString> powerAdjacentNets;
260 if( powerNets.contains( be.
a ) || powerNets.contains( be.
b ) )
262 if( !powerNets.contains( be.
a ) )
263 powerAdjacentNets.insert( be.
a );
265 if( !powerNets.contains( be.
b ) )
266 powerAdjacentNets.insert( be.
b );
271 result.adjacency[be.
a].push_back( { be.
b, be.
sym } );
272 result.adjacency[be.
b].push_back( { be.
a, be.
sym } );
278 std::map<wxString, int> degree;
280 for(
const auto&
kv :
result.adjacency )
281 degree[
kv.first] =
static_cast<int>(
kv.second.size() );
283 if(
result.adjacency.size() <= 2 )
284 powerAdjacentNets.clear();
286 if( powerAdjacentNets.size() <= 2 )
287 powerAdjacentNets.clear();
289 std::queue<wxString> q;
290 std::set<wxString> removed;
292 for(
const auto&
kv : degree )
294 if(
kv.second <= 1 && powerAdjacentNets.contains(
kv.first ) )
300 wxString n = q.front();
303 if( removed.contains( n ) )
310 if( removed.contains( e.other ) )
313 if( degree.count( e.other ) )
317 if( degree[e.other] <= 1 && powerAdjacentNets.contains( e.other ) )
323 if( !removed.empty() )
325 std::map<wxString, std::vector<BRIDGE_NEIGHBOR>> newAdj;
327 for(
const auto&
kv :
result.adjacency )
329 if( removed.contains(
kv.first ) )
334 if( removed.contains( e.
other ) )
337 newAdj[
kv.first].push_back( e );
341 result.adjacency.swap( newAdj );
564 std::set<wxString> unresolvedTerminals;
573 chain->ReplaceNets( {} );
574 unresolvedTerminals.insert(
chain->GetName() );
578 std::unordered_map<wxString, SCH_NETCHAIN*> netToNetChain;
581 std::vector<SHEET_SYMBOLS> sheetSymbols;
582 sheetSymbols.reserve( aConnectivity.
sheets.size() );
584 for(
const auto& sheet : aConnectivity.
sheets )
596 view.
symbols.push_back( { symbol, symbol->
GetPins( &sheet.path ) } );
601 wxLogTrace(
traceSchNetChain,
"RebuildNetChains: screens=%zu (global build)", sheetSymbols.size() );
607 wxLogTrace(
traceSchNetChain,
"RebuildNetChains: pass 1 (per-sheet 2-pin symbols)" );
610 auto& bridgeEdges = bridgeGraph.
edges;
613 wxLogTrace(
traceSchNetChain,
"RebuildNetChains: bridgeEdges=%zu adjacency=%zu",
614 bridgeEdges.size(), adjacency.size() );
620 wxLogTrace(
traceSchNetChain,
"RebuildNetChains: targeted stub pruning start (adj=%zu)", adjacency.size() );
621 std::set<wxString> seen;
622 std::set<wxString> globalPrune;
623 for(
const auto&
kv : adjacency )
625 const wxString& start =
kv.first;
626 if( seen.contains( start ) )
continue;
630 std::vector<wxString>
comp; std::queue<wxString> q; q.push( start ); seen.insert( start );
633 wxString cur = q.front(); q.pop();
comp.push_back( cur );
634 for(
const BRIDGE_NEIGHBOR& e : adjacency.at( cur ) )
if( !seen.contains( e.other ) ) { seen.insert( e.other ); q.push( e.other ); }
639 if(
comp.size() <= 4 )
continue;
640 std::map<wxString,int> degree;
641 for(
const wxString& n :
comp ) degree[n] = (int) adjacency.at( n ).size();
642 std::vector<wxString> candidates;
643 for(
const wxString& n :
comp )
645 const auto& nbrs = adjacency.at( n );
646 if( nbrs.size() == 1 )
648 const wxString neigh = nbrs[0].other;
649 if( degree.count( neigh ) && degree[neigh] > 2 ) candidates.push_back( n );
655 if( candidates.empty() )
continue;
656 std::sort( candidates.begin(), candidates.end(), [](
const wxString& a,
const wxString& b ){ return a.CmpNoCase( b ) < 0; } );
657 size_t needPrune =
comp.size() - 4;
if( needPrune > candidates.size() ) needPrune = candidates.size();
661 for(
size_t i = 0; i < needPrune; ++i ) globalPrune.insert( candidates[i] );
663 if( !globalPrune.empty() )
665 std::map<wxString,std::vector<BRIDGE_NEIGHBOR>> newAdj;
666 for(
const auto& kv2 : adjacency )
668 if( globalPrune.contains( kv2.first ) )
continue;
671 if( globalPrune.contains( e.
other ) )
continue;
672 newAdj[kv2.first].push_back( e );
675 adjacency.swap( newAdj );
676 wxLogTrace(
traceSchNetChain,
"RebuildNetChains: pruned %zu targeted stub nets", globalPrune.size() );
681 auto neighbors_of = [&](
const wxString& n ) ->
const std::vector<BRIDGE_NEIGHBOR>*
683 if(
auto it = adjacency.find(n); it != adjacency.end() )
return &it->second;
691 std::set<wxString> netsAll;
692 for(
const auto&
kv : adjacency ) netsAll.insert(
kv.first );
695 std::set<wxString> visited;
696 for(
const wxString& start : netsAll )
698 if( visited.contains( start ) )
continue;
699 std::queue<wxString> q; q.push( start );
700 std::set<wxString>
comp;
comp.insert( start ); visited.insert( start );
703 wxString cur = q.front(); q.pop();
704 if(
auto nbrs = neighbors_of( cur ) )
708 if( visited.contains( e.other ) )
continue;
709 visited.insert( e.other );
710 comp.insert( e.other );
715 if(
comp.size() >= 2 )
717 auto sig = std::make_unique<SCH_NETCHAIN>();
718 for(
const wxString& n :
comp ) sig->AddNet( n );
723 netToNetChain.reserve( adjacency.size() );
725 if( sigUP )
for(
const wxString& n : sigUP->GetNets() ) netToNetChain[n] = sigUP.get();
729 const auto first = netToNetChain.find( edge.a );
730 const auto second = netToNetChain.find( edge.b );
732 if( first != netToNetChain.end() && second != netToNetChain.end()
733 && first->second == second->second && edge.sym )
735 first->second->AddSymbol( edge.sym );
743 wxLogTrace(
traceSchNetChain,
"RebuildNetChains: pre-label potentialNetChains=%zu",
754 for(
const wxString& n : sigUP->GetNets() )
759 netsStr += wxS(
" " );
763 netsStr += wxS(
"..." );
770 wxLogTrace(
traceSchNetChain,
" chain %p name='%s' nets=%zu [%s]", (
void*) sigUP.get(),
771 sigUP->GetName(), sigUP->GetNets().size(), netsStr );
780 std::set<wxString> committedNames;
785 committedNames.insert(
chain->GetName() );
788 for(
const auto& sheet : aConnectivity.
sheets )
797 const auto* connection = sheet.Find( item );
803 const wxString& net = connection->name;
806 if( !net.IsEmpty() && net.Length() < 2048 && netToNetChain.count( net ) )
810 if(
name.Length() > 512 )
811 name.Truncate( 512 );
813 if(
name.StartsWith( wxS(
"/" ) ) )
820 if( !committedNames.contains(
name )
831 wxLogTrace(
traceSchNetChain,
"RebuildNetChains: pass 3 (default naming)" );
834 if( sig->GetName().IsEmpty() )
836 sig->SetName( wxString::Format( wxT(
"NetChain%d" ), idx ) );
849 std::map<SCH_NETCHAIN*, std::vector<PIN_INFO>> chainPins;
855 const auto& sheet = *view.input;
858 for(
const auto& [sym, pins] : view.symbols )
862 const auto chain = netToNetChain.find( sheet.Key( p ) );
864 if(
chain != netToNetChain.end() )
865 chainPins[
chain->second].push_back( { p, sym, &sheetPath, p->GetPosition() } );
874 const auto& pins = chainPins[sig.get()];
878 size_t bestI = 0, bestJ = 0;
880 for(
size_t i = 0; i < pins.size(); ++i )
882 for(
size_t j = i + 1; j < pins.size(); ++j )
886 int64_t dx = pa.
x - pb.
x;
887 int64_t dy = pa.
y - pb.
y;
888 int64_t d = dx * dx + dy * dy;
893 a = pins[i].pin->m_Uuid;
894 b = pins[j].pin->m_Uuid;
901 sig->SetTerminalPins( a, b );
903 if( best >= 0 && bestI < pins.size() && bestJ < pins.size() )
905 sig->SetTerminalPaths( pins[bestI].sheet->Path(), pins[bestJ].sheet->Path() );
906 sig->SetTerminalRefs( pins[bestI].sym->GetRef( pins[bestI].sheet ), pins[bestI].pin->GetNumber(),
907 pins[bestJ].sym->GetRef( pins[bestJ].sheet ), pins[bestJ].pin->GetNumber() );
911 wxLogTrace(
traceSchNetChain,
"RebuildNetChains: pass 5 (apply symbol names)" );
925 for(
const wxString& n : sig->
GetNets() )
926 netsStr += n + wxS(
" " );
929 sig->
GetNets().size(), netsStr );
939 std::set<wxString> alreadyCommitted;
944 alreadyCommitted.insert(
chain->GetName() );
948 std::map<std::pair<wxString, wxString>, wxString> refPinToNet;
954 const auto& sheet = *view.input;
956 for(
const auto& [sym, pins] : view.symbols )
958 const wxString ref = sym->GetRef( &sheet.path );
962 if(
const auto* net = sheet.Find(
pin ) )
963 refPinToNet[{ ref,
pin->GetNumber() }] = net->key;
971 std::unordered_map<wxString, SCH_NETCHAIN*> committedByName;
976 committedByName[
chain->GetName()] =
chain.get();
981 std::set<wxString> refreshedThisPass;
985 if( unresolvedTerminals.contains( chainName ) )
989 const auto committed = committedByName.find( chainName );
991 if( committed != committedByName.end() )
996 for(
int endpoint = 0; endpoint < 2; ++endpoint )
998 const KIID&
id = endpoint == 0 ?
chain.GetTerminalPinA() :
chain.GetTerminalPinB();
1000 for(
const auto& sheet : aConnectivity.
sheets )
1002 SCH_SCREEN* screen = sheet.path.LastScreen();
1004 if( screen && sheet.path.PathRef() ==
chain.GetTerminalPath( endpoint ) )
1023 if( alreadyCommitted.count( chainName ) )
1025 auto it = committedByName.find( chainName );
1027 if( it != committedByName.end() && it->second )
1030 refreshedThisPass.insert( chainName );
1038 alreadyCommitted.insert( chainName );
1039 refreshedThisPass.insert( chainName );
1047 if( memberNets.empty() || unresolvedTerminals.contains( chainName ) )
1052 if( alreadyCommitted.count( chainName ) && refreshedThisPass.count( chainName ) )
1062 SCH_PIN* terminalPinA =
nullptr;
1063 SCH_PIN* terminalPinB =
nullptr;
1064 std::set<SCH_SYMBOL*> symbols;
1068 const auto& sheet = *view.input;
1070 for(
const auto& [sym, pins] : view.symbols )
1072 const wxString ref = sym->GetRef( &sheet.path );
1073 bool symContributes =
false;
1077 const auto* net = sheet.Find(
pin );
1082 if( memberNets.count( net->name ) )
1083 symContributes =
true;
1085 if( ref == termRefs.first.ref &&
pin->GetNumber() == termRefs.first.pin )
1088 if( ref == termRefs.second.ref &&
pin->GetNumber() == termRefs.second.pin )
1092 if( symContributes )
1093 symbols.insert( sym );
1097 if( !terminalPinA || !terminalPinB || symbols.empty() )
1100 "RebuildNetChains: cannot restore manual chain '%s' "
1101 "(terminals or member nets unresolved)",
1106 if( alreadyCommitted.count( chainName ) )
1108 auto it = committedByName.find( chainName );
1110 if( it != committedByName.end() && it->second )
1119 terminalPinB->
m_Uuid, termRefs.first.ref, termRefs.first.pin,
1120 termRefs.second.ref, termRefs.second.pin );
1121 alreadyCommitted.insert( chainName );