27#include <unordered_map>
46constexpr int NODE_COALESCE_EPSILON = 1;
57 return x == aOther.x && y == aOther.y && layer == aOther.layer;
64 std::size_t operator()(
const NodeKey& aKey )
const noexcept
68 std::size_t h =
static_cast<std::size_t
>(
static_cast<uint32_t
>( aKey.x ) ) * 0x9E3779B185EBCA87ULL;
69 h ^=
static_cast<std::size_t
>(
static_cast<uint32_t
>( aKey.y ) ) * 0xC2B2AE3D27D4EB4FULL;
70 h ^=
static_cast<std::size_t
>( aKey.layer ) * 0x165667B19E3779F9ULL;
82 BOARD_CONNECTED_ITEM* sourceItem;
89 std::vector<VECTOR2I> nodePos;
90 std::vector<PCB_LAYER_ID> nodeLayer;
91 std::vector<Edge> edges;
92 std::vector<std::vector<int>> adj;
96 std::map<PAD*, std::vector<int>> padNodes;
99 std::unordered_map<NodeKey, int, NodeKeyHash>& aIndex )
101 int rx = ( aPos.
x / NODE_COALESCE_EPSILON ) * NODE_COALESCE_EPSILON;
102 int ry = ( aPos.
y / NODE_COALESCE_EPSILON ) * NODE_COALESCE_EPSILON;
103 NodeKey key { rx, ry,
static_cast<int>( aLayer ) };
105 auto it = aIndex.find( key );
107 if( it != aIndex.end() )
110 int idx =
static_cast<int>( nodePos.size() );
111 nodePos.emplace_back( aPos );
112 nodeLayer.emplace_back( aLayer );
114 aIndex.emplace( key, idx );
118 int addEdge(
int aNodeA,
int aNodeB,
double aLength,
double aDelay,
121 if( aNodeA == aNodeB )
124 int idx =
static_cast<int>( edges.size() );
125 edges.push_back( Edge{ aNodeA, aNodeB, aLength, aDelay, aSrc, aLayer } );
126 adj[aNodeA].push_back( idx );
127 adj[aNodeB].push_back( idx );
142double trackDelay(
const PCB_TRACK* aTrack,
double aLength,
145 if( !aCalc || aLength <= 0.0 )
150 if( lengthItem.
Type() == LENGTH_DELAY_CALCULATION_ITEM::TYPE::UNKNOWN )
151 return aFallbackDelayPerMm * aLength /
pcbIUScale.IU_PER_MM;
154 .OptimiseVias =
false, .MergeTracks =
false, .OptimiseTracesInPads =
false,
155 .InferViaInPad =
false
158 std::vector<LENGTH_DELAY_CALCULATION_ITEM> items{ lengthItem };
160 items, opts,
nullptr,
nullptr,
164 return static_cast<double>( stats.
TrackDelay );
170int addViaEdges( Graph& aGraph,
PCB_VIA* aVia,
172 std::unordered_map<NodeKey, int, NodeKeyHash>& aIndex )
176 if( stack.size() < 2 )
181 double totalDelay = 0.0;
182 double totalLength = 0.0;
188 if( lengthItem.
Type() != LENGTH_DELAY_CALCULATION_ITEM::TYPE::UNKNOWN )
191 .OptimiseVias =
false, .MergeTracks =
false, .OptimiseTracesInPads =
false,
192 .InferViaInPad =
false
195 std::vector<LENGTH_DELAY_CALCULATION_ITEM> items{ lengthItem };
197 items, opts,
nullptr,
nullptr,
201 totalDelay =
static_cast<double>( stats.
ViaDelay );
202 totalLength =
static_cast<double>( stats.
ViaLength );
209 std::vector<int> heights;
212 for(
size_t i = 0; i + 1 < stack.size(); ++i )
214 int h = aCalc ? aCalc->
StackupHeight( stack[i], stack[i + 1] ) : 0;
215 heights.push_back( h );
219 if( totalLength <= 0.0 )
220 totalLength =
static_cast<double>( heightSum );
224 for(
size_t i = 0; i + 1 < stack.size(); ++i )
226 int nA = aGraph.addNode( aVia->
GetPosition(), stack[i], aIndex );
227 int nB = aGraph.addNode( aVia->
GetPosition(), stack[i + 1], aIndex );
229 double segLen =
static_cast<double>( heights[i] );
230 double segDelay = 0.0;
232 if( totalLength > 0.0 )
233 segDelay = totalDelay * ( segLen / totalLength );
234 else if( !heights.empty() )
235 segDelay = totalDelay /
static_cast<double>( heights.size() );
237 if( aGraph.addEdge( nA, nB, segLen, segDelay, aVia, stack[i] ) >= 0 )
247std::vector<std::pair<PCB_LAYER_ID, int>>
248addPadNodes( Graph& aGraph,
PAD* aPad,
249 std::unordered_map<NodeKey, int, NodeKeyHash>& aIndex )
251 std::vector<std::pair<PCB_LAYER_ID, int>> nodes;
257 int n = aGraph.addNode( aPad->
GetCenter(), layer, aIndex );
258 nodes.emplace_back( layer, n );
268bool isInteriorParametric(
double t )
270 constexpr double kEdgeFraction = 1e-4;
271 return t > kEdgeFraction && t < 1.0 - kEdgeFraction;
279void splitTrackTJunctions( Graph& aGraph,
280 std::unordered_map<NodeKey, int, NodeKeyHash>& aIndex )
285 std::vector<int> trackEdges;
287 for(
int i = 0; i < static_cast<int>( aGraph.edges.size() ); ++i )
289 const Edge& e = aGraph.edges[i];
292 trackEdges.push_back( i );
295 for(
int endpointEdge : trackEdges )
297 for(
int endpointNodeSel = 0; endpointNodeSel < 2; ++endpointNodeSel )
299 const Edge& srcEdge = aGraph.edges[endpointEdge];
301 endpointNodeSel == 0 ? srcEdge.nodeA : srcEdge.nodeB;
302 VECTOR2I endpointPos = aGraph.nodePos[endpointNode];
303 PCB_LAYER_ID endpointLayer = aGraph.nodeLayer[endpointNode];
308 for(
int otherIdx = 0;
309 otherIdx < static_cast<int>( aGraph.edges.size() );
312 if( otherIdx == endpointEdge )
315 Edge& other = aGraph.edges[otherIdx];
320 if( other.layer != endpointLayer )
323 VECTOR2I aPos = aGraph.nodePos[other.nodeA];
324 VECTOR2I bPos = aGraph.nodePos[other.nodeB];
334 double t = ap.
Dot( ab ) / len2;
336 if( !isInteriorParametric( t ) )
340 VECTOR2I proj {
static_cast<int>( std::round( projD.
x ) ),
341 static_cast<int>( std::round( projD.
y ) ) };
352 dynamic_cast<PCB_TRACK*
>( other.sourceItem ) )
354 std::shared_ptr<SHAPE> shape =
355 otherTrack->GetEffectiveShape( endpointLayer );
357 if( !shape || !shape->Collide( endpointPos, 0 ) )
362 if( euclidean( proj, endpointPos ) > NODE_COALESCE_EPSILON )
366 else if( euclidean( proj, endpointPos ) > NODE_COALESCE_EPSILON )
374 double fullLen = other.length;
375 double fullDelay = other.delay;
376 int oldA = other.nodeA;
377 int oldB = other.nodeB;
382 aGraph.addNode( proj, endpointLayer, aIndex );
384 if( junctionNode == oldA || junctionNode == oldB )
388 auto removeFromAdj = [&](
int node,
int edgeIdx ) {
389 auto& v = aGraph.adj[node];
390 v.erase( std::remove( v.begin(), v.end(), edgeIdx ),
394 removeFromAdj( oldA, otherIdx );
395 removeFromAdj( oldB, otherIdx );
398 aGraph.edges[otherIdx].nodeA = oldA;
399 aGraph.edges[otherIdx].nodeB = junctionNode;
400 aGraph.edges[otherIdx].length = fullLen * t;
401 aGraph.edges[otherIdx].delay = fullDelay * t;
402 aGraph.adj[oldA].push_back( otherIdx );
403 aGraph.adj[junctionNode].push_back( otherIdx );
406 aGraph.addEdge( junctionNode, oldB,
407 fullLen * ( 1.0 - t ),
408 fullDelay * ( 1.0 - t ),
417 if( endpointNode != junctionNode )
419 aGraph.addEdge( endpointNode, junctionNode, 0.0, 0.0,
420 nullptr, endpointLayer );
432bool dfsSpanningTree(
const Graph& aGraph,
int aRoot,
433 std::vector<int>& aParentNode,
434 std::vector<int>& aParentEdge,
435 std::vector<bool>& aVisited )
437 aParentNode.assign( aGraph.nodePos.size(), -1 );
438 aParentEdge.assign( aGraph.nodePos.size(), -1 );
439 aVisited.assign( aGraph.nodePos.size(),
false );
441 std::stack<std::pair<int, int>> stack;
442 stack.push( { aRoot, -1 } );
446 while( !stack.empty() )
448 auto [node, fromEdge] = stack.top();
454 aVisited[node] =
true;
455 aParentEdge[node] = fromEdge;
459 const Edge& e = aGraph.edges[fromEdge];
460 aParentNode[node] = ( e.nodeA == node ) ? e.nodeB : e.nodeA;
463 for(
int eIdx : aGraph.adj[node] )
465 if( eIdx == fromEdge )
468 const Edge& e = aGraph.edges[eIdx];
469 int next = ( e.nodeA == node ) ? e.nodeB : e.nodeA;
477 stack.push( {
next, eIdx } );
488 const std::set<BOARD_CONNECTED_ITEM*>& aChainItems ) :
491 if( !aBoard || aChainName.
IsEmpty() || aChainItems.empty() )
493 m_status = STATUS::NO_ITEMS;
500 PAD* terminalA =
nullptr;
501 PAD* terminalB =
nullptr;
505 NETINFO_ITEM* ni = item->GetNet();
510 if( PAD* p0 = ni->GetTerminalPad( 0 ) )
513 if( PAD* p1 = ni->GetTerminalPad( 1 ) )
516 if( terminalA && terminalB )
520 if( !terminalA || !terminalB )
522 m_status = STATUS::NO_TERMINAL_PADS;
528 std::unordered_map<NodeKey, int, NodeKeyHash>
index;
537 std::set<PAD*> seenPads;
539 auto registerAllPadLayers = [&](
PAD*
pad )
544 if( !seenPads.insert(
pad ).second )
549 std::vector<int>& list = graph.padNodes[
pad];
555 int n = graph.addNode(
pad->GetCenter(), layer,
index );
560 registerAllPadLayers( terminalA );
561 registerAllPadLayers( terminalB );
569 switch( item->Type() )
578 double dly = trackDelay( track, len, calc, fallbackDelayPerMm );
579 graph.addEdge( nA, nB, len, dly, track, track->
GetLayer() );
585 addViaEdges( graph,
via, calc,
index );
591 registerAllPadLayers(
pad );
604 registerAllPadLayers( br.padA );
605 registerAllPadLayers( br.padB );
614 if( !commonCu.test( anchorLayer ) )
618 anchorLayer = stack[0];
623 int nA = graph.addNode( br.padA->GetCenter(), anchorLayer,
index );
624 int nB = graph.addNode( br.padB->GetCenter(), anchorLayer,
index );
625 graph.addEdge( nA, nB, br.length, br.delay,
nullptr, anchorLayer );
629 splitTrackTJunctions( graph,
index );
634 auto firstPadNode = [](
const std::vector<int>& nodes ) ->
int
636 return nodes.empty() ? -1 : nodes.front();
639 int rootA = firstPadNode( graph.padNodes[terminalA] );
640 int rootB = firstPadNode( graph.padNodes[terminalB] );
642 if( rootA < 0 || rootB < 0 )
650 auto stitchPadLayers = [&](
PAD* aPad )
652 const std::vector<int>& nodes = graph.padNodes[aPad];
654 for(
size_t i = 1; i < nodes.size(); ++i )
655 graph.addEdge( nodes[0], nodes[i], 0.0, 0.0,
nullptr,
656 graph.nodeLayer[nodes[i]] );
659 for(
auto& [
pad, nodes] : graph.padNodes )
660 stitchPadLayers(
pad );
663 std::vector<int> parentNode;
664 std::vector<int> parentEdge;
665 std::vector<bool> visited;
667 bool cycle = dfsSpanningTree( graph, rootA, parentNode, parentEdge, visited );
675 if( !visited[rootB] )
682 std::vector<int> trunkEdges;
683 std::vector<int> trunkNodes;
687 while( cur != rootA && cur >= 0 )
689 trunkNodes.push_back( cur );
690 int e = parentEdge[cur];
695 trunkEdges.push_back( e );
696 cur = parentNode[cur];
705 trunkNodes.push_back( rootA );
712 for(
int eIdx : trunkEdges )
721 std::set<int> trunkNodeSet( trunkNodes.begin(), trunkNodes.end() );
722 std::set<int> trunkEdgeSet( trunkEdges.begin(), trunkEdges.end() );
723 std::vector<bool> nodeInStubComponent( graph.nodePos.size(),
false );
725 for(
int startNode = 0;
726 startNode < static_cast<int>( graph.nodePos.size() );
729 if( !visited[startNode] )
731 if( trunkNodeSet.count( startNode ) )
733 if( nodeInStubComponent[startNode] )
739 std::set<int> compNodes;
740 std::set<int> compEdges;
741 std::set<int> trunkTouches;
742 std::vector<BOARD_CONNECTED_ITEM*> items;
743 double compLength = 0.0;
744 double compDelay = 0.0;
749 compNodes.insert( startNode );
756 for(
int eIdx : graph.adj[n] )
758 const Edge& e = graph.edges[eIdx];
759 int other = ( e.nodeA == n ) ? e.nodeB : e.nodeA;
761 if( trunkEdgeSet.count( eIdx ) )
766 if( trunkNodeSet.count( other ) )
767 trunkTouches.insert( other );
777 if( compEdges.insert( eIdx ).second )
779 compLength += e.length;
780 compDelay += e.delay;
783 items.push_back( e.sourceItem );
789 if( trunkNodeSet.count( other ) )
791 trunkTouches.insert( other );
795 if( compNodes.insert( other ).second )
800 for(
int n : compNodes )
801 nodeInStubComponent[n] =
true;
803 if( trunkTouches.empty() )
809 if( trunkTouches.size() >= 2 )
817 int branchNode = *trunkTouches.begin();
822 : graph.nodeLayer[branchNode];
823 stub.
items = std::move( items );
825 stub.
delay = compDelay;
827 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_CALCULATION_ITEM GetLengthCalculationItem(const BOARD_CONNECTED_ITEM *aBoardItem) const
Return a LENGTH_CALCULATION_ITEM constructed from the given BOARD_CONNECTED_ITEM.
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) const
Calculates the electrical length of the given items.
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)
Compute both the chain bridging length and its associated propagation delay (in internal delay IU,...
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