26#include <unordered_map>
45constexpr int NODE_COALESCE_EPSILON = 1;
56 return x == aOther.x && y == aOther.y && layer == aOther.layer;
63 std::size_t operator()(
const NodeKey& aKey )
const noexcept
67 std::size_t h =
static_cast<std::size_t
>(
static_cast<uint32_t
>( aKey.x ) ) * 0x9E3779B185EBCA87ULL;
68 h ^=
static_cast<std::size_t
>(
static_cast<uint32_t
>( aKey.y ) ) * 0xC2B2AE3D27D4EB4FULL;
69 h ^=
static_cast<std::size_t
>( aKey.layer ) * 0x165667B19E3779F9ULL;
81 BOARD_CONNECTED_ITEM* sourceItem;
88 std::vector<VECTOR2I> nodePos;
89 std::vector<PCB_LAYER_ID> nodeLayer;
90 std::vector<Edge> edges;
91 std::vector<std::vector<int>> adj;
95 std::map<PAD*, std::vector<int>> padNodes;
98 std::unordered_map<NodeKey, int, NodeKeyHash>& aIndex )
100 int rx = ( aPos.
x / NODE_COALESCE_EPSILON ) * NODE_COALESCE_EPSILON;
101 int ry = ( aPos.
y / NODE_COALESCE_EPSILON ) * NODE_COALESCE_EPSILON;
102 NodeKey key { rx, ry,
static_cast<int>( aLayer ) };
104 auto it = aIndex.find( key );
106 if( it != aIndex.end() )
109 int idx =
static_cast<int>( nodePos.size() );
110 nodePos.emplace_back( aPos );
111 nodeLayer.emplace_back( aLayer );
113 aIndex.emplace( key, idx );
117 int addEdge(
int aNodeA,
int aNodeB,
double aLength,
double aDelay,
120 if( aNodeA == aNodeB )
123 int idx =
static_cast<int>( edges.size() );
124 edges.push_back( Edge{ aNodeA, aNodeB, aLength, aDelay, aSrc, aLayer } );
125 adj[aNodeA].push_back( idx );
126 adj[aNodeB].push_back( idx );
141double trackDelay(
const PCB_TRACK* aTrack,
double aLength,
144 if( !aCalc || aLength <= 0.0 )
149 if( lengthItem.
Type() == LENGTH_DELAY_CALCULATION_ITEM::TYPE::UNKNOWN )
150 return aFallbackDelayPerMm * aLength /
pcbIUScale.IU_PER_MM;
153 .OptimiseVias =
false, .MergeTracks =
false, .OptimiseTracesInPads =
false,
154 .InferViaInPad =
false
157 std::vector<LENGTH_DELAY_CALCULATION_ITEM> items{ lengthItem };
159 items, opts,
nullptr,
nullptr,
163 return static_cast<double>( stats.
TrackDelay );
169int addViaEdges( Graph& aGraph,
PCB_VIA* aVia,
171 std::unordered_map<NodeKey, int, NodeKeyHash>& aIndex )
175 if( stack.size() < 2 )
180 double totalDelay = 0.0;
181 double totalLength = 0.0;
187 if( lengthItem.
Type() != LENGTH_DELAY_CALCULATION_ITEM::TYPE::UNKNOWN )
190 .OptimiseVias =
false, .MergeTracks =
false, .OptimiseTracesInPads =
false,
191 .InferViaInPad =
false
194 std::vector<LENGTH_DELAY_CALCULATION_ITEM> items{ lengthItem };
196 items, opts,
nullptr,
nullptr,
200 totalDelay =
static_cast<double>( stats.
ViaDelay );
201 totalLength =
static_cast<double>( stats.
ViaLength );
208 std::vector<int> heights;
211 for(
size_t i = 0; i + 1 < stack.size(); ++i )
213 int h = aCalc ? aCalc->
StackupHeight( stack[i], stack[i + 1] ) : 0;
214 heights.push_back( h );
218 if( totalLength <= 0.0 )
219 totalLength =
static_cast<double>( heightSum );
223 for(
size_t i = 0; i + 1 < stack.size(); ++i )
225 int nA = aGraph.addNode( aVia->
GetPosition(), stack[i], aIndex );
226 int nB = aGraph.addNode( aVia->
GetPosition(), stack[i + 1], aIndex );
228 double segLen =
static_cast<double>( heights[i] );
229 double segDelay = 0.0;
231 if( totalLength > 0.0 )
232 segDelay = totalDelay * ( segLen / totalLength );
233 else if( !heights.empty() )
234 segDelay = totalDelay /
static_cast<double>( heights.size() );
236 if( aGraph.addEdge( nA, nB, segLen, segDelay, aVia, stack[i] ) >= 0 )
246std::vector<std::pair<PCB_LAYER_ID, int>>
247addPadNodes( Graph& aGraph,
PAD* aPad,
248 std::unordered_map<NodeKey, int, NodeKeyHash>& aIndex )
250 std::vector<std::pair<PCB_LAYER_ID, int>> nodes;
256 int n = aGraph.addNode( aPad->
GetCenter(), layer, aIndex );
257 nodes.emplace_back( layer, n );
267bool isInteriorParametric(
double t )
269 constexpr double kEdgeFraction = 1e-4;
270 return t > kEdgeFraction && t < 1.0 - kEdgeFraction;
278void splitTrackTJunctions( Graph& aGraph,
279 std::unordered_map<NodeKey, int, NodeKeyHash>& aIndex )
284 std::vector<int> trackEdges;
286 for(
int i = 0; i < static_cast<int>( aGraph.edges.size() ); ++i )
288 const Edge& e = aGraph.edges[i];
291 trackEdges.push_back( i );
294 for(
int endpointEdge : trackEdges )
296 for(
int endpointNodeSel = 0; endpointNodeSel < 2; ++endpointNodeSel )
298 const Edge& srcEdge = aGraph.edges[endpointEdge];
300 endpointNodeSel == 0 ? srcEdge.nodeA : srcEdge.nodeB;
301 VECTOR2I endpointPos = aGraph.nodePos[endpointNode];
302 PCB_LAYER_ID endpointLayer = aGraph.nodeLayer[endpointNode];
307 for(
int otherIdx = 0;
308 otherIdx < static_cast<int>( aGraph.edges.size() );
311 if( otherIdx == endpointEdge )
314 Edge& other = aGraph.edges[otherIdx];
319 if( other.layer != endpointLayer )
322 VECTOR2I aPos = aGraph.nodePos[other.nodeA];
323 VECTOR2I bPos = aGraph.nodePos[other.nodeB];
333 double t = ap.
Dot( ab ) / len2;
335 if( !isInteriorParametric( t ) )
339 VECTOR2I proj {
static_cast<int>( std::round( projD.
x ) ),
340 static_cast<int>( std::round( projD.
y ) ) };
351 dynamic_cast<PCB_TRACK*
>( other.sourceItem ) )
353 std::shared_ptr<SHAPE> shape =
354 otherTrack->GetEffectiveShape( endpointLayer );
356 if( !shape || !shape->Collide( endpointPos, 0 ) )
361 if( euclidean( proj, endpointPos ) > NODE_COALESCE_EPSILON )
365 else if( euclidean( proj, endpointPos ) > NODE_COALESCE_EPSILON )
373 double fullLen = other.length;
374 double fullDelay = other.delay;
375 int oldA = other.nodeA;
376 int oldB = other.nodeB;
381 aGraph.addNode( proj, endpointLayer, aIndex );
383 if( junctionNode == oldA || junctionNode == oldB )
387 auto removeFromAdj = [&](
int node,
int edgeIdx ) {
388 auto& v = aGraph.adj[node];
389 v.erase( std::remove( v.begin(), v.end(), edgeIdx ),
393 removeFromAdj( oldA, otherIdx );
394 removeFromAdj( oldB, otherIdx );
397 aGraph.edges[otherIdx].nodeA = oldA;
398 aGraph.edges[otherIdx].nodeB = junctionNode;
399 aGraph.edges[otherIdx].length = fullLen * t;
400 aGraph.edges[otherIdx].delay = fullDelay * t;
401 aGraph.adj[oldA].push_back( otherIdx );
402 aGraph.adj[junctionNode].push_back( otherIdx );
405 aGraph.addEdge( junctionNode, oldB,
406 fullLen * ( 1.0 - t ),
407 fullDelay * ( 1.0 - t ),
416 if( endpointNode != junctionNode )
418 aGraph.addEdge( endpointNode, junctionNode, 0.0, 0.0,
419 nullptr, endpointLayer );
431bool dfsSpanningTree(
const Graph& aGraph,
int aRoot,
432 std::vector<int>& aParentNode,
433 std::vector<int>& aParentEdge,
434 std::vector<bool>& aVisited )
436 aParentNode.assign( aGraph.nodePos.size(), -1 );
437 aParentEdge.assign( aGraph.nodePos.size(), -1 );
438 aVisited.assign( aGraph.nodePos.size(),
false );
440 std::stack<std::pair<int, int>> stack;
441 stack.push( { aRoot, -1 } );
445 while( !stack.empty() )
447 auto [node, fromEdge] = stack.top();
453 aVisited[node] =
true;
454 aParentEdge[node] = fromEdge;
458 const Edge& e = aGraph.edges[fromEdge];
459 aParentNode[node] = ( e.nodeA == node ) ? e.nodeB : e.nodeA;
462 for(
int eIdx : aGraph.adj[node] )
464 if( eIdx == fromEdge )
467 const Edge& e = aGraph.edges[eIdx];
468 int next = ( e.nodeA == node ) ? e.nodeB : e.nodeA;
476 stack.push( {
next, eIdx } );
487 const std::set<BOARD_CONNECTED_ITEM*>& aChainItems ) :
490 if( !aBoard || aChainName.
IsEmpty() || aChainItems.empty() )
492 m_status = STATUS::NO_ITEMS;
499 PAD* terminalA =
nullptr;
500 PAD* terminalB =
nullptr;
504 NETINFO_ITEM* ni = item->GetNet();
509 if( PAD* p0 = ni->GetTerminalPad( 0 ) )
512 if( PAD* p1 = ni->GetTerminalPad( 1 ) )
515 if( terminalA && terminalB )
519 if( !terminalA || !terminalB )
521 m_status = STATUS::NO_TERMINAL_PADS;
527 std::unordered_map<NodeKey, int, NodeKeyHash>
index;
536 std::set<PAD*> seenPads;
538 auto registerAllPadLayers = [&](
PAD*
pad )
543 if( !seenPads.insert(
pad ).second )
548 std::vector<int>& list = graph.padNodes[
pad];
554 int n = graph.addNode(
pad->GetCenter(), layer,
index );
559 registerAllPadLayers( terminalA );
560 registerAllPadLayers( terminalB );
568 switch( item->Type() )
577 double dly = trackDelay( track, len, calc, fallbackDelayPerMm );
578 graph.addEdge( nA, nB, len, dly, track, track->
GetLayer() );
584 addViaEdges( graph,
via, calc,
index );
590 registerAllPadLayers(
pad );
603 registerAllPadLayers( br.padA );
604 registerAllPadLayers( br.padB );
613 if( !commonCu.test( anchorLayer ) )
617 anchorLayer = stack[0];
622 int nA = graph.addNode( br.padA->GetCenter(), anchorLayer,
index );
623 int nB = graph.addNode( br.padB->GetCenter(), anchorLayer,
index );
624 graph.addEdge( nA, nB, br.length, br.delay,
nullptr, anchorLayer );
628 splitTrackTJunctions( graph,
index );
633 auto firstPadNode = [](
const std::vector<int>& nodes ) ->
int
635 return nodes.empty() ? -1 : nodes.front();
638 int rootA = firstPadNode( graph.padNodes[terminalA] );
639 int rootB = firstPadNode( graph.padNodes[terminalB] );
641 if( rootA < 0 || rootB < 0 )
649 auto stitchPadLayers = [&](
PAD* aPad )
651 const std::vector<int>& nodes = graph.padNodes[aPad];
653 for(
size_t i = 1; i < nodes.size(); ++i )
654 graph.addEdge( nodes[0], nodes[i], 0.0, 0.0,
nullptr,
655 graph.nodeLayer[nodes[i]] );
658 for(
auto& [
pad, nodes] : graph.padNodes )
659 stitchPadLayers(
pad );
662 std::vector<int> parentNode;
663 std::vector<int> parentEdge;
664 std::vector<bool> visited;
666 bool cycle = dfsSpanningTree( graph, rootA, parentNode, parentEdge, visited );
674 if( !visited[rootB] )
681 std::vector<int> trunkEdges;
682 std::vector<int> trunkNodes;
686 while( cur != rootA && cur >= 0 )
688 trunkNodes.push_back( cur );
689 int e = parentEdge[cur];
694 trunkEdges.push_back( e );
695 cur = parentNode[cur];
704 trunkNodes.push_back( rootA );
711 for(
int eIdx : trunkEdges )
720 std::set<int> trunkNodeSet( trunkNodes.begin(), trunkNodes.end() );
721 std::set<int> trunkEdgeSet( trunkEdges.begin(), trunkEdges.end() );
722 std::vector<bool> nodeInStubComponent( graph.nodePos.size(),
false );
724 for(
int startNode = 0;
725 startNode < static_cast<int>( graph.nodePos.size() );
728 if( !visited[startNode] )
730 if( trunkNodeSet.count( startNode ) )
732 if( nodeInStubComponent[startNode] )
738 std::set<int> compNodes;
739 std::set<int> compEdges;
740 std::set<int> trunkTouches;
741 std::vector<BOARD_CONNECTED_ITEM*> items;
742 double compLength = 0.0;
743 double compDelay = 0.0;
748 compNodes.insert( startNode );
755 for(
int eIdx : graph.adj[n] )
757 const Edge& e = graph.edges[eIdx];
758 int other = ( e.nodeA == n ) ? e.nodeB : e.nodeA;
760 if( trunkEdgeSet.count( eIdx ) )
765 if( trunkNodeSet.count( other ) )
766 trunkTouches.insert( other );
776 if( compEdges.insert( eIdx ).second )
778 compLength += e.length;
779 compDelay += e.delay;
782 items.push_back( e.sourceItem );
788 if( trunkNodeSet.count( other ) )
790 trunkTouches.insert( other );
794 if( compNodes.insert( other ).second )
799 for(
int n : compNodes )
800 nodeInStubComponent[n] =
true;
802 if( trunkTouches.empty() )
808 if( trunkTouches.size() >= 2 )
816 int branchNode = *trunkTouches.begin();
821 : graph.nodeLayer[branchNode];
822 stub.
items = std::move( items );
824 stub.
delay = compDelay;
826 m_stubs.push_back( std::move( stub ) );
bool operator==(const wxAuiPaneInfo &aLhs, const wxAuiPaneInfo &aRhs)
constexpr EDA_IU_SCALE pcbIUScale
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
FOOTPRINT * GetParentFootprint() const
Information pertinent to a Pcbnew printed circuit board.
LENGTH_DELAY_CALCULATION * GetLengthCalculation() const
Returns the track length calculator.
CHAIN_TOPOLOGY(BOARD *aBoard, const wxString &aChainName, const std::set< BOARD_CONNECTED_ITEM * > &aChainItems)
std::vector< STUB > m_stubs
KICAD_T Type() const
Returns the type of object.
Lightweight class which holds a pad, via, or a routed trace outline.
TYPE Type() const
Gets the routing item type.
Class which calculates lengths (and associated routing statistics) in a BOARD context.
int StackupHeight(PCB_LAYER_ID aFirstLayer, PCB_LAYER_ID aSecondLayer) const
Returns the stackup distance between the two given layers.
LENGTH_DELAY_STATS CalculateLengthDetails(std::vector< LENGTH_DELAY_CALCULATION_ITEM > &aItems, PATH_OPTIMISATIONS aOptimisations, const PAD *aStartPad=nullptr, const PAD *aEndPad=nullptr, LENGTH_DELAY_LAYER_OPT aLayerOpt=LENGTH_DELAY_LAYER_OPT::NO_LAYER_DETAIL, LENGTH_DELAY_DOMAIN_OPT aDomain=LENGTH_DELAY_DOMAIN_OPT::NO_DELAY_DETAIL, LENGTH_DELAY_ITEM_DETAILS *aPerItemLengthDelays=nullptr) const
Calculates the electrical length of the given items.
LENGTH_DELAY_CALCULATION_ITEM GetLengthCalculationItem(const BOARD_CONNECTED_ITEM *aBoardItem) const
Return a LENGTH_CALCULATION_ITEM constructed from the given BOARD_CONNECTED_ITEM.
LSEQ is a sequence (and therefore also a set) of PCB_LAYER_IDs.
LSET is a set of PCB_LAYER_IDs.
static const LSET & AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
LSEQ CuStack() const
Return a sequence of copper layers in starting from the front/top and extending to the back/bottom.
LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
virtual double GetLength() const
Get the length of the track using the hypotenuse calculation.
const VECTOR2I & GetStart() const
const VECTOR2I & GetEnd() const
VECTOR2I GetPosition() const override
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
constexpr extended_type SquaredEuclideanNorm() const
Compute the squared euclidean norm of the vector, which is defined as (x ** 2 + y ** 2).
constexpr extended_type Dot(const VECTOR2< T > &aVector) const
Compute dot product of self with aVector.
PCB_LAYER_ID
A quick note on layer IDs:
double ChainBridgingDelayPerMm(const BOARD *aBoard, const wxString &aNetChain)
Pick a single per-IU-per-mm delay for a given chain.
std::vector< CHAIN_BRIDGE > EnumerateChainBridges(const BOARD *aBoard, const wxString &aNetChain)
Enumerate every per-pad-pair bridge edge contributed by every footprint on the board to the named cha...
One series-component bridge edge inside a chain graph.
std::vector< BOARD_CONNECTED_ITEM * > items
Holds length measurement result details and statistics.
Struct to control which optimisations the length calculation code runs on the given path objects.
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
@ PCB_PAD_T
class PAD, a pad in a footprint
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
VECTOR2< int32_t > VECTOR2I
VECTOR2< double > VECTOR2D