KiCad PCB EDA Suite
Loading...
Searching...
No Matches
diff_phase_skew_tool.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 * @author James Jackson
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
22
23#include <router/pns_arc.h>
25#include <router/pns_helpers.h>
27#include <router/pns_router.h>
28#include <router/pns_topology.h>
29
30#include <advanced_config.h>
31#include <board.h>
32#include <collectors.h>
34#include <drc/drc_engine.h>
35#include <gal/painter.h>
37#include <pcb_edit_frame.h>
38#include <pad.h>
39#include <pcb_track.h>
42#include <tools/drc_tool.h>
43#include <tools/pcb_actions.h>
45#include <tool/tool_manager.h>
46#include <view/view.h>
47#include <view/view_controls.h>
48
49
50#define INITIAL_HOVER_HITTEST_THRESHOLD_PIXELS 5
51#define DETAILS_HOVER_HITTEST_THRESHOLD_PIXELS 20
52
53
64
65
69
70
72{
73 return true;
74}
75
76
78{
79 delete m_router;
80 delete m_iface; // Delete after m_router because PNS::NODE dtor needs m_ruleResolver
81
82 if( aReason == RESET_REASON::SHUTDOWN )
83 {
84 m_router = nullptr;
85 m_iface = nullptr;
86 return;
87 }
88
89 // Get core objects
90 m_view = getView();
93 m_frame = frame();
94 DRC_TOOL* drcTool = m_toolMgr->GetTool<DRC_TOOL>();
95 m_drcEngine = drcTool->GetDRCEngine();
96
97 // Initialise a router instance
99 m_iface->SetBoard( m_board );
100 m_iface->SetView( m_view );
101 m_iface->SetHostTool( this );
102
103 m_router = new PNS::ROUTER;
104 m_router->SetInterface( m_iface );
105 m_router->ClearWorld();
106 m_router->SyncWorld();
107 m_router->UpdateSizes( m_savedSizes );
108
109 PCBNEW_SETTINGS* settings = m_frame->GetPcbNewSettings();
110
111 if( !settings->m_PnsSettings )
112 settings->m_PnsSettings = std::make_unique<PNS::ROUTING_SETTINGS>( settings, "tools.pns" );
113
114 m_router->LoadSettings( settings->m_PnsSettings.get() );
115
117}
118
119
121{
123 return 0;
124
126
127 SCOPED_TOOL_PUSHER raii( m_frame, aEvent );
128
129 Activate();
130
131 // Must be done after Activate() so that it gets set into the correct context
133 // controls->ShowCursor( true );
134 // controls->ForceCursorPosition( false );
135
136 // Set initial cursor
137 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::TUNE );
138
139 // Get the required tools and helpers
140 PCB_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
141 GENERAL_COLLECTORS_GUIDE guide = m_frame->GetCollectorsGuide();
142
143 // Create the VIEW_OVERLAY
144 getOverlay();
145
146 m_pickerItemFirst = nullptr;
147
148 if( aEvent.HasPosition() )
149 m_toolMgr->PrimeTool( aEvent.Position() );
150
151 // Main loop: keep receiving events
152 while( TOOL_EVENT* evt = Wait() )
153 {
154 m_cursorPos = controls->GetMousePosition();
155
156 if( evt->IsCancelInteractive() || evt->IsActivate() )
157 {
158 // Roll back our mode, or exit if we are in the initial hover mode
160 {
161 m_pickerItemFirst = nullptr;
163 m_pickerItemSecond = nullptr;
164 clearOverlay();
166 m_maxSkew.reset();
168 }
169 else
170 {
172 break;
173 }
174 }
175
176 if( evt->IsMotion() )
177 {
178 if( GetMode() == MODE::HOVER )
179 {
180 m_pickerItemFirst = nullptr;
181 m_pickerItemSecond = nullptr;
183 doInitialHover( selectionTool, guide );
185 }
186 else if( GetMode() == MODE::SELECTED_FIRST )
187 {
188 m_pickerItemSecond = nullptr;
189 doInitialHover( selectionTool, guide );
191 }
192 else if( GetMode() == MODE::FIXED_MODE )
193 {
196 }
197 }
198 else if( evt->IsClick( BUT_LEFT ) && GetMode() == MODE::HOVER && m_pickerItemFirst )
199 {
201
203 {
206 }
207 else
208 {
210 }
211
213 }
214 else if( evt->IsClick( BUT_LEFT ) && GetMode() == MODE::SELECTED_FIRST && m_pickerItemSecond )
215 {
216 // First click to select the diff pair for inspection
221 }
222 else if( evt->IsAction( &PCB_ACTIONS::properties ) )
223 {
225 PCBNEW_SETTINGS* cfg = m_frame->GetPcbNewSettings();
226 settings = cfg->m_DiffPhaseSkewSettings;
227
229
230 if( dlg.ShowModal() == wxID_OK )
231 {
232 cfg->m_DiffPhaseSkewSettings = settings;
233
234 if( GetMode() == MODE::FIXED_MODE )
236 }
237 }
238 }
239
240 // Restore UI state
241 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
242
243 // Reset tool state
246
247 updateNetHighlights( false );
248 m_frame->GetCanvas()->Refresh();
249
250 return 0;
251}
252
253
255{
256 GENERAL_COLLECTOR collector;
258
259 if( m_frame->GetDisplayOptions().m_ContrastModeDisplay != HIGH_CONTRAST_MODE::NORMAL )
260 aGuide.SetIncludeSecondary( false );
261 else
262 aGuide.SetIncludeSecondary( true );
263
264 aGuide.SetPreferredLayer( m_frame->GetActiveLayer() );
265 collector.Collect( m_board, { PCB_TRACE_T, PCB_ARC_T }, m_cursorPos, aGuide );
266
267 if( collector.GetCount() > 1 )
268 aSelectionTool->GuessSelectionCandidates( collector, m_cursorPos );
269
270 if( collector.GetCount() > 0 )
271 {
272 double min_dist_sq = std::numeric_limits<double>::max();
273
274 for( EDA_ITEM* candidate : collector )
275 {
276 VECTOR2I candidatePos;
277
278 if( candidate->Type() == PCB_TRACE_T )
279 candidatePos = static_cast<PCB_TRACK*>( candidate )->GetCenter();
280 else if( candidate->Type() == PCB_ARC_T )
281 candidatePos = static_cast<PCB_ARC*>( candidate )->GetMid();
282
283 const double dist_sq = ( m_cursorPos - candidatePos ).SquaredEuclideanNorm();
284
285 if( dist_sq < min_dist_sq )
286 {
287 const auto bci = static_cast<BOARD_CONNECTED_ITEM*>( candidate );
288 const NETINFO_ITEM* candidateNet = bci->GetNet();
289
290 if( GetMode() == MODE::HOVER )
291 {
292 min_dist_sq = dist_sq;
293
294 // We only accept diff pairs in initial hover mode
295 const bool isDiffPairItem =
296 m_drcEngine->IsNetADiffPair( m_board, candidateNet, m_netcodeP, m_netcodeN );
297 m_pickerItemFirst = static_cast<BOARD_CONNECTED_ITEM*>( candidate );
298
299 if( isDiffPairItem )
300 {
302 }
303 else
304 {
306 m_netcodeP = candidateNet->GetNetCode();
307 }
308 }
309 else if( GetMode() == MODE::SELECTED_FIRST )
310 {
311 int fakeNCP, fakeNCN;
312
313 // Reject diff pairs here as we have a not-diff-pair selected
314 const bool isDiffPairItem = m_drcEngine->IsNetADiffPair( m_board, candidateNet, fakeNCP, fakeNCN );
315
316 auto existingBci = static_cast<BOARD_CONNECTED_ITEM*>( m_pickerItemFirst );
317 const NETINFO_ITEM* existingNet = existingBci->GetNet();
318
319 if( !isDiffPairItem && candidateNet != existingNet )
320 {
321 min_dist_sq = dist_sq;
322 m_pickerItemSecond = static_cast<BOARD_CONNECTED_ITEM*>( candidate );
323 m_netcodeN = candidateNet->GetNetCode();
324 }
325 }
326 }
327 }
328 }
329
331}
332
333
335 const PNS::SOLID* aEndPad, const NETINFO_ITEM* aNet,
336 std::vector<LENGTH_DELAY_CALCULATION_ITEM>& aItems,
337 LENGTH_DELAY_ITEM_DETAILS& aItemDetails ) const
338{
339 if( aPath.Size() == 0 )
340 return;
341
342 // Convert path to length / delay interface types
343 aItems = m_iface->GetLengthDelayCalculationItems( aPath, aNet->GetNetClass() );
344 wxASSERT( aItems.size() == static_cast<size_t>( aPath.Size() ) );
345
346 // The router returns compound lines - we need to split them in to their constituent segments / arcs
347 splitLengthItems( aItems );
348
349 // Get the per-item length / delay statistics
350 constexpr PATH_OPTIMISATIONS opts = {
351 .OptimiseVias = false, .MergeTracks = false, .OptimiseTracesInPads = false, .InferViaInPad = true
352 };
353
354 const PAD* startPad = dynamic_cast<PAD*>( aStartPad->BoardItem() );
355 const PAD* endPad = dynamic_cast<PAD*>( aEndPad->BoardItem() );
356 aItemDetails = LENGTH_DELAY_ITEM_DETAILS{};
357 m_board->GetLengthCalculation()->CalculateLengthDetails(
358 aItems, opts, startPad, endPad, LENGTH_DELAY_LAYER_OPT::NO_LAYER_DETAIL,
360 wxASSERT( aItemDetails.LengthsAndDelays.size() == aItems.size() );
361}
362
363
365{
366 // Extract the diff pair net paths
367 getNetPaths();
368
369 // Determine what we are defining as the 'start' of the net
371
372 if( reportValidityErrors( direction ) )
373 {
375 return;
376 }
377
378 m_timeDomain = false;
379
380 // Check if both nets have time domain parameters
381 if( m_selectedNetinfo->GetNetClass()->HasTuningProfile() && m_coupledNetinfo->GetNetClass()->HasTuningProfile() )
382 {
383 wxString selectedTuningProfileName = m_selectedNetinfo->GetNetClass()->GetTuningProfile();
384 wxString coupledTuningProfileName = m_coupledNetinfo->GetNetClass()->GetTuningProfile();
385
386 std::shared_ptr<TUNING_PROFILES> tuningParams = m_frame->Prj().GetProjectFile().TuningProfileParameters();
387 const TUNING_PROFILE& selectedTuningProfile = tuningParams->GetTuningProfile( selectedTuningProfileName );
388 const TUNING_PROFILE& coupledTuningProfile = tuningParams->GetTuningProfile( coupledTuningProfileName );
389
390 if( selectedTuningProfile.m_EnableTimeDomainTuning && coupledTuningProfile.m_EnableTimeDomainTuning )
391 m_timeDomain = true;
392 }
393
394 // Construct the length / delay calculation items
397
400
401 // Build the cumulative length / delay structures
408
409 // Walk the two tracks and construct the localised phase differences
410 const std::vector<PARALLEL_RUN> parallelRuns = findParallelRuns();
411
412 // Build the known delay reference points for each track
413 buildKnownRelativePoints( parallelRuns );
414
415 m_maxSkew.reset();
417
418 // Finally draw the overlay
420}
421
422
423void DIFF_PHASE_SKEW_TOOL::buildKnownRelativePoints( const std::vector<PARALLEL_RUN>& aKnownRuns )
424{
425 m_selectedKnownPoints.clear();
426 m_coupledKnownPoints.clear();
427
428 const double padLenDiff =
429 static_cast<double>( m_selectedStartEndDetails.StartPadLength - m_coupledStartEndDetails.StartPadLength );
430 const double padDelayDiff =
431 static_cast<double>( m_selectedStartEndDetails.StartPadDelay - m_coupledStartEndDetails.StartPadDelay );
432
433 struct RELATIVE_PAIR
434 {
435 double len;
436 double delay;
437 };
438
439 auto opposite = []( const RELATIVE_PAIR& a )
440 {
441 return RELATIVE_PAIR{ -a.len, -a.delay };
442 };
443
444 for( const auto& r : aKnownRuns )
445 {
446 const std::size_t segIdxA = r.segA;
447 const std::size_t segIdxB = r.segB;
448
449 const double segStartA = segIdxA == 0 ? 0.0 : static_cast<double>( m_selectedCumulative[segIdxA - 1].m_Length );
450 const double segStartB = segIdxB == 0 ? 0.0 : static_cast<double>( m_coupledCumulative[segIdxB - 1].m_Length );
451 const double segLenA = static_cast<double>( m_selectedLengthDelayDetails.LengthsAndDelays[segIdxA].first );
452 const double segLenB = static_cast<double>( m_coupledLengthDelayDetails.LengthsAndDelays[segIdxB].first );
453
454 const double s0A = segStartA + r.ta0 * segLenA;
455 const double s1A = segStartA + r.ta1 * segLenA;
456 const double s0B = segStartB + r.tb0 * segLenB;
457 const double s1B = segStartB + r.tb1 * segLenB;
458
459 const RELATIVE_PAIR startRel{ ( r.startLenA - r.startLenB ) + padLenDiff,
460 ( r.startDelayA - r.startDelayB ) + padDelayDiff };
461 const RELATIVE_PAIR endRel{ ( r.endLenA - r.endLenB ) + padLenDiff,
462 ( r.endDelayA - r.endDelayB ) + padDelayDiff };
463 const RELATIVE_PAIR startRelB = opposite( startRel );
464 const RELATIVE_PAIR endRelB = opposite( endRel );
465
466 const bool hasStartViaA = r.startViaLengthA.has_value();
467 const bool hasEndViaA = r.endViaLengthA.has_value();
468 const bool hasStartViaB = r.startViaLengthB.has_value();
469 const bool hasEndViaB = r.endViaLengthB.has_value();
470
471 const double startViaLenA = r.startViaLengthA.value_or( 0.0 );
472 const double endViaLenA = r.endViaLengthA.value_or( 0.0 );
473 const double startViaLenB = r.startViaLengthB.value_or( 0.0 );
474 const double endViaLenB = r.endViaLengthB.value_or( 0.0 );
475
476 const double startViaDelayA = r.startViaDelayA.value_or( 0.0 );
477 const double endViaDelayA = r.endViaDelayA.value_or( 0.0 );
478 const double startViaDelayB = r.startViaDelayB.value_or( 0.0 );
479 const double endViaDelayB = r.endViaDelayB.value_or( 0.0 );
480
481 // Injected start-via points
482 if( hasStartViaA && hasStartViaB )
483 {
484 const RELATIVE_PAIR rel{ startRel.len - startViaLenA + startViaLenB,
485 startRel.delay - startViaDelayA + startViaDelayB };
486 const RELATIVE_PAIR relB = opposite( rel );
487 m_selectedKnownPoints.push_back( { s0A - startViaLenA, rel.len, rel.delay, rel.len, rel.delay } );
488 m_coupledKnownPoints.push_back( { s0B - startViaLenB, relB.len, relB.delay, relB.len, relB.delay } );
489 }
490
491 // Maybe-modified values used for interpolation around via discontinuities
492 RELATIVE_PAIR startBeforeA = startRel;
493 RELATIVE_PAIR startBeforeB = startRelB;
494
495 if( hasStartViaA && !hasStartViaB )
496 {
497 startBeforeB.len += startViaLenA;
498 startBeforeB.delay += startViaDelayA;
499 m_selectedKnownPoints.push_back( { s0A - startViaLenA, startRel.len - startViaLenA,
500 startRel.delay - startViaDelayA, startRel.len - startViaLenA,
501 startRel.delay - startViaDelayA } );
502 }
503 else if( !hasStartViaA && hasStartViaB )
504 {
505 startBeforeA.len += startViaLenB;
506 startBeforeA.delay += startViaDelayB;
507 m_coupledKnownPoints.push_back( { s0B - startViaLenB, startRelB.len - startViaLenB,
508 startRelB.delay - startViaDelayB, startRelB.len - startViaLenB,
509 startRelB.delay - startViaDelayB } );
510 }
511
512 // Maybe-modified values used for interpolation around via discontinuities
513 RELATIVE_PAIR endAfterA = endRel;
514 RELATIVE_PAIR endAfterB = endRelB;
515
516 if( hasEndViaA && !hasEndViaB )
517 {
518 endAfterB.len -= endViaLenA;
519 endAfterB.delay -= endViaDelayA;
520 }
521 else if( !hasEndViaA && hasEndViaB )
522 {
523 endAfterA.len -= endViaLenB;
524 endAfterA.delay -= endViaDelayB;
525 }
526
527 // Main known points
528 m_selectedKnownPoints.push_back( { s0A, startBeforeA.len, startBeforeA.delay, startRel.len, startRel.delay } );
529 m_selectedKnownPoints.push_back( { s1A, endRel.len, endRel.delay, endAfterA.len, endAfterA.delay } );
530 m_coupledKnownPoints.push_back( { s0B, startBeforeB.len, startBeforeB.delay, startRelB.len, startRelB.delay } );
531 m_coupledKnownPoints.push_back( { s1B, endRelB.len, endRelB.delay, endAfterB.len, endAfterB.delay } );
532
533 // Injected end-via points
534 if( hasEndViaA && hasEndViaB )
535 {
536 const RELATIVE_PAIR rel{ endRel.len + endViaLenA - endViaLenB, endRel.delay + endViaDelayA - endViaDelayB };
537 const RELATIVE_PAIR relB = opposite( rel );
538 m_selectedKnownPoints.push_back( { s1A + endViaLenA, rel.len, rel.delay, rel.len, rel.delay } );
539 m_coupledKnownPoints.push_back( { s1B + endViaLenB, relB.len, relB.delay, relB.len, relB.delay } );
540 }
541 else if( hasEndViaA && !hasEndViaB )
542 {
543 m_selectedKnownPoints.push_back( { s1A + endViaLenA, endRel.len + endViaLenA, endRel.delay + endViaDelayA,
544 endRel.len + endViaLenA, endRel.delay + endViaDelayA } );
545 }
546 else if( !hasEndViaA && hasEndViaB )
547 {
548 m_coupledKnownPoints.push_back( { s1B + endViaLenB, endRelB.len + endViaLenB, endRelB.delay + endViaDelayB,
549 endRelB.len + endViaLenB, endRelB.delay + endViaDelayB } );
550 }
551 }
552}
553
554
555std::vector<double> DIFF_PHASE_SKEW_TOOL::buildSplitPositions( const std::vector<CUMULATIVE_ENTRY>& aSegments,
556 const double aTargetSubsegmentSize )
557{
558 if( aSegments.empty() )
559 return {};
560
561 std::vector<double> splits;
562
563 // Start of line
564 if( aSegments[0].m_SourceType == LENGTH_DELAY_CALCULATION_ITEM::TYPE::LINE )
565 splits.push_back( 0.0 );
566
567 double currentDistance = 0;
568
569 for( const CUMULATIVE_ENTRY& seg : aSegments )
570 {
571 const double segStart = currentDistance;
572 const double segEnd = seg.m_Length;
573 currentDistance = segEnd;
574
575 // Only emit segments for lines
576 if( seg.m_SourceType != LENGTH_DELAY_CALCULATION_ITEM::TYPE::LINE )
577 continue;
578
579 // Fixed subdivision spacing
580 for( double s = segStart; s < segEnd; s += aTargetSubsegmentSize )
581 {
582 splits.push_back( s );
583 }
584
585 splits.push_back( segEnd );
586 }
587
588 // Sort and make unique
589 std::ranges::sort( splits );
590
591 splits.erase( std::ranges::unique( splits,
592 []( const double a, const double b )
593 {
594 return std::abs( a - b ) < EPS;
595 } )
596 .begin(),
597 splits.end() );
598
599 return splits;
600}
601
602
603std::pair<VECTOR2D, std::size_t>
604DIFF_PHASE_SKEW_TOOL::pointAtDistance( const std::vector<CUMULATIVE_ENTRY>& aSegments,
605 const std::vector<LENGTH_DELAY_CALCULATION_ITEM>& aSourceItemDetails,
606 const double aDist )
607{
608 for( std::size_t i = 0; i < aSegments.size(); ++i )
609 {
610 const double segStart = i == 0 ? 0.0 : static_cast<double>( aSegments[i - 1].m_Length );
611 const double segEnd = static_cast<double>( aSegments[i].m_Length );
612
613 if( aDist <= segEnd + EPS )
614 {
615 const double segLen = segEnd - segStart;
616
617 double t = 0.0;
618
619 if( segLen > EPS )
620 t = ( aDist - segStart ) / segLen;
621
622 t = std::clamp( t, 0.0, 1.0 );
623
624 if( aSourceItemDetails[i].Type() == LENGTH_DELAY_CALCULATION_ITEM::TYPE::VIA )
625 {
626 // We've hit a via - use the end point from the previous line
627 wxASSERT( i > 0 );
628 wxASSERT( aSourceItemDetails[i - 1].Type() == LENGTH_DELAY_CALCULATION_ITEM::TYPE::LINE );
629 wxASSERT( aSourceItemDetails[i - 1].GetLine().CPoints().size() == 2 );
630 return { aSourceItemDetails[i - 1].GetLine().CPoints()[1], i - 1 };
631 }
632
633 wxASSERT( aSourceItemDetails[i].Type() == LENGTH_DELAY_CALCULATION_ITEM::TYPE::LINE );
634 wxASSERT( aSourceItemDetails[i].GetLine().CPoints().size() == 2 );
635
636 return {
637 lerp( aSourceItemDetails[i].GetLine().CPoints()[0], aSourceItemDetails[i].GetLine().CPoints()[1], t ), i
638 };
639 }
640 }
641
642 // We shouldn't reach this point...
643 wxASSERT( false );
644 return { { 0.0, 0.0 }, 0 };
645}
646
647
648COLOR4D DIFF_PHASE_SKEW_TOOL::interpolateColours( const COLOR4D& aColour1, const COLOR4D& aColour2, double aS,
649 const bool aUseLogScale ) const
650{
651 auto lerp = []( const double d1, const double d2, const double s )
652 {
653 return d1 + s * ( d2 - d1 );
654 };
655
656 if( aUseLogScale )
657 aS = std::log( 1.0 + m_colourInterpolationLogStrength * aS )
658 / std::log( 1.0 + m_colourInterpolationLogStrength );
659
660 const double r = std::clamp( lerp( aColour1.r, aColour2.r, aS ), 0.0, 1.0 );
661 const double g = std::clamp( lerp( aColour1.g, aColour2.g, aS ), 0.0, 1.0 );
662 const double b = std::clamp( lerp( aColour1.b, aColour2.b, aS ), 0.0, 1.0 );
663 const double a = std::clamp( lerp( aColour1.a, aColour2.a, aS ), 0.0, 1.0 );
664
665 return COLOR4D( r, g, b, a );
666}
667
668
676
677
678std::vector<DIFF_PHASE_SKEW_TOOL::OUTPUT_SEGMENT> DIFF_PHASE_SKEW_TOOL::buildDiffOverlaySegmentsImpl(
679 const std::vector<CUMULATIVE_ENTRY>& aSegments,
680 const std::vector<LENGTH_DELAY_CALCULATION_ITEM>& aSourceItemDetails,
681 const std::vector<KNOWN_RELATIVE_POINT>& aKnownPoints, double aTargetSubsegmentSize )
682{
683 std::vector<OUTPUT_SEGMENT> result;
684
685 if( aSegments.empty() )
686 return result;
687
688 PCBNEW_SETTINGS* cfg = m_frame->GetPcbNewSettings();
690
691 // Get min and max values
692 double minLen = 0.0;
693 double maxLen = 0.0;
694 double minDelay = 0.0;
695 double maxDelay = 0.0;
696
697 for( const auto& [_1, relLenBefore, relDelayBefore, relLenAfter, relDelayAfter] : aKnownPoints )
698 {
699 minLen = std::min( minLen, std::min( relLenBefore, relLenAfter ) );
700 maxLen = std::max( maxLen, std::max( relLenBefore, relLenAfter ) );
701 minDelay = std::min( minDelay, std::min( relDelayBefore, relDelayAfter ) );
702 maxDelay = std::max( maxDelay, std::max( relDelayBefore, relDelayAfter ) );
703 }
704
705 int maxSkew = m_maxSkew.value_or( 0 );
706
707 if( m_timeDomain )
708 m_maxSkew = std::max( static_cast<int>( std::round( maxDelay / 10 ) * 10 ), maxSkew );
709 else
710 m_maxSkew = std::max( static_cast<int>( std::round( maxLen / 10 ) * 10 ), maxSkew );
711
712 // Build all subdivision boundaries
713 const auto splits = buildSplitPositions( aSegments, aTargetSubsegmentSize );
714
715 KnownValueInterpolator interp( aKnownPoints );
716
717 // Emit subdivided segments
718 for( std::size_t i = 0; i + 1 < splits.size(); ++i )
719 {
720 const double s0 = splits[i];
721 const double s1 = splits[i + 1];
722
723 // Skip degenerate intervals
724 if( s1 - s0 <= EPS )
725 continue;
726
727 OUTPUT_SEGMENT out;
728
729 auto [startPoint, segIdx] = pointAtDistance( aSegments, aSourceItemDetails, s0 );
730 out.Width = static_cast<int>( aSourceItemDetails[segIdx].GetWidth() * m_overlayTrackInflation );
731 out.Start = startPoint;
732 auto [endPoint, _] = pointAtDistance( aSegments, aSourceItemDetails, s1 );
733 out.End = endPoint;
734
735 const double sMid = ( s0 + s1 ) / 2.0;
736
737 const std::optional<std::pair<double, double>> knownInterp = interp.ValueAt( sMid );
738 out.RelativeValueKnown = knownInterp.has_value();
739
740 double min = 0.0;
741 double max = 0.0;
742
743 if( m_timeDomain )
744 {
745 out.RelativeValueAtMid = knownInterp.value_or( std::pair<double, double>{ 0.0, 0.0 } ).second;
746 min = minDelay;
747 max = maxDelay;
748 }
749 else
750 {
751 out.RelativeValueAtMid = knownInterp.value_or( std::pair<double, double>{ 0.0, 0.0 } ).first;
752 min = minLen;
753 max = maxLen;
754 }
755
756 // Round value to nearest 10 IU to reduce low-level colour jitter on equal tracks
757 out.RelativeValueAtMid = std::round( out.RelativeValueAtMid / 10 ) * 10;
758
759 // Calculate colour value
760 if( !out.RelativeValueKnown )
761 {
762 out.Colour = settings.m_UnknownSkewColor;
763 }
764 else if( out.RelativeValueAtMid < 0.0 )
765 {
766 const double frac = fabs( out.RelativeValueAtMid / min );
767 out.Colour = interpolateColours( settings.m_ZeroSkewColor, settings.m_NegativeSkewColor, frac,
768 settings.m_UseLogScale );
769 }
770 else if( out.RelativeValueAtMid > 0.0 )
771 {
772 const double frac = fabs( out.RelativeValueAtMid / max );
773 out.Colour = interpolateColours( settings.m_ZeroSkewColor, settings.m_PositiveSkewColor, frac,
774 settings.m_UseLogScale );
775 }
776 else
777 {
778 out.Colour = settings.m_ZeroSkewColor;
779 }
780
781 result.push_back( out );
782 }
783
784 return result;
785}
786
787
789{
790 clearOverlay();
791
792 const std::size_t selIdx = m_segmentForStatisticsDisplay.first;
793 const bool isSelected = m_segmentForStatisticsDisplay.second;
794 const bool drawHighlight = selIdx < std::numeric_limits<std::size_t>::max();
795
796 m_viewOverlay->SetIsStroke( true );
797 m_viewOverlay->SetIsFill( false );
798
799 for( std::size_t i = 0; i < m_selectedDiffs.size(); ++i )
800 {
801 const OUTPUT_SEGMENT& segment = m_selectedDiffs[i];
802
803 if( drawHighlight && isSelected && i == selIdx )
804 m_viewOverlay->SetStrokeColor( COLOR4D( 1.0, 0.0, 0.937, 1.0 ) );
805 else
806 m_viewOverlay->SetStrokeColor( segment.Colour );
807
808 m_viewOverlay->Segment( segment.Start, segment.End, segment.Width );
809 }
810
811 for( std::size_t i = 0; i < m_coupledDiffs.size(); ++i )
812 {
813 const OUTPUT_SEGMENT& segment = m_coupledDiffs[i];
814
815 if( drawHighlight && !isSelected && i == selIdx )
816 m_viewOverlay->SetStrokeColor( COLOR4D( 1.0, 0.0, 0.937, 1.0 ) );
817 else
818 m_viewOverlay->SetStrokeColor( segment.Colour );
819
820 m_viewOverlay->Segment( segment.Start, segment.End, segment.Width );
821 }
822
823 std::vector<MSG_PANEL_ITEM> items;
824 wxString description, value;
825
827}
828
829
831{
832 std::vector<MSG_PANEL_ITEM> items;
833
834 if( !m_pickerItemFirst )
835 {
836 frame()->SetMsgPanel( m_board );
837 return;
838 }
839
841 {
842 wxString description = wxString::Format( _( "Net A Name" ) );
843 wxString netName = m_pickerItemFirst->GetNet()->GetDisplayNetname();
844 items.emplace_back( description, netName );
845
847 {
848 description = wxString::Format( _( "Net B Name" ) );
849 netName = m_pickerItemSecond->GetNet()->GetDisplayNetname();
850 items.emplace_back( description, netName );
851 }
852 }
853 else
854 {
855 wxString description = wxString::Format( _( "Net P Name" ) );
856 wxString netName = m_board->GetNetInfo().GetNetItem( m_netcodeP )->GetDisplayNetname();
857 items.emplace_back( description, netName );
858
859 description = wxString::Format( _( "Net N Name" ) );
860 netName = m_board->GetNetInfo().GetNetItem( m_netcodeN )->GetDisplayNetname();
861 items.emplace_back( description, netName );
862 }
863
864 if( m_maxSkew.has_value() )
865 {
866 wxString description = wxString::Format( _( "Max Skew" ) );
867 wxString value;
868
869 if( m_timeDomain )
870 value = m_frame->MessageTextFromValue( m_maxSkew.value(), true, EDA_DATA_TYPE::TIME );
871 else
872 value = m_frame->MessageTextFromValue( m_maxSkew.value(), true, EDA_DATA_TYPE::DISTANCE );
873
874 items.emplace_back( description, value );
875 }
876
877 const std::size_t selIdx = m_segmentForStatisticsDisplay.first;
878 const bool isSelected = m_segmentForStatisticsDisplay.second;
879 const bool drawHighlight = selIdx < std::numeric_limits<std::size_t>::max();
880
881 if( drawHighlight )
882 {
883 const OUTPUT_SEGMENT& segment = isSelected ? m_selectedDiffs[selIdx] : m_coupledDiffs[selIdx];
884 double normalisedValue = std::round( segment.RelativeValueAtMid );
885 normalisedValue = ( normalisedValue == 0.0 ) ? 0.0 : normalisedValue;
886
887 wxString description = _( "Local Skew" );
888 wxString value = _( "Unknown" );
889
890 if( segment.RelativeValueKnown )
891 {
892 value = m_frame->MessageTextFromValue( normalisedValue, true,
894 }
895
896
897 items.emplace_back( description, value );
898 }
899
900 frame()->SetMsgPanel( items );
901}
902
903
905{
907 {
908 // TODO: This assumes the gap is the same across all traces, but actually this can vary
909 // TODO: by layer. We should get a representative segment for each layer from the
910 // TODO: two tracks and find the max constraint across all of them.
911 const DRC_CONSTRAINT constraint =
912 m_drcEngine->EvalRules( DIFF_PAIR_GAP_CONSTRAINT, aItem, nullptr, aItem->GetLayer() );
913
914 if( constraint.IsNull() || constraint.GetSeverity() == RPT_SEVERITY_IGNORE )
915 return std::numeric_limits<int>::max();
916
917 const MINOPTMAX<int>& val = constraint.GetValue();
918
919 if( val.HasOpt() && val.HasMax() )
920 return std::max( val.Max(), val.Opt() );
921 else if( val.HasMax() )
922 return val.Max();
923 else if( val.HasOpt() )
924 return val.Opt();
925
926 return std::numeric_limits<int>::max();
927 }
928 else
929 {
931 return dist.EuclideanNorm();
932 }
933}
934
935
936void DIFF_PHASE_SKEW_TOOL::findParallelRunsImpl( std::pair<std::size_t, std::size_t> aRangeA,
937 std::pair<std::size_t, std::size_t> aRangeB, double aMaxSpacing,
938 std::vector<PARALLEL_RUN>& aRuns ) const
939{
940 for( size_t ia = aRangeA.first; ia < aRangeA.second; ++ia )
941 {
943
944 if( selectedItem.Type() != LENGTH_DELAY_CALCULATION_ITEM::TYPE::LINE )
945 continue;
946
947 const SHAPE_LINE_CHAIN& lineA = selectedItem.GetLine();
948 wxASSERT( lineA.SegmentCount() == 1 );
949 const SEG segA = lineA.Segment( 0 );
950
951 VECTOR2D A0 = segA.A;
952 VECTOR2D A1 = segA.B;
953
954 VECTOR2D dA = A1 - A0;
955 const double lenA = dA.EuclideanNorm();
956 VECTOR2D nA{ dA.x / lenA, dA.y / lenA };
957
958 for( size_t ib = aRangeB.first; ib < aRangeB.second; ++ib )
959 {
961
962 if( coupledItem.Type() != LENGTH_DELAY_CALCULATION_ITEM::TYPE::LINE )
963 continue;
964
965 if( selectedItem.GetStartLayer() != coupledItem.GetStartLayer() )
966 continue;
967
968 const SHAPE_LINE_CHAIN& lineB = coupledItem.GetLine();
969 wxASSERT( lineB.SegmentCount() == 1 );
970 const SEG segB = lineB.Segment( 0 );
971
972 VECTOR2D B0 = segB.A;
973 VECTOR2D B1 = segB.B;
974
975 VECTOR2D dB = B1 - B0;
976 const double lenB = dB.EuclideanNorm();
977 VECTOR2D nB{ dB.x / lenB, dB.y / lenB };
978
979 // Test for parallel line segments
980 const double dp = nA.Dot( nB );
981
982 // Note that this test is explicitly signed (not fabs(dp)) to ensure anti-parallel tracks are rejected
984 continue;
985
986 // Test for perpendicular distance
987 VECTOR2D midB = ( B0 + B1 ) * 0.5;
988
989 // Perpendicular distance from midpoint of B to infinite line through A
990 const double perpDistance = std::fabs( nA.Cross( midB - A0 ) );
991
992 const double maxItemGap =
993 ( aMaxSpacing + ( selectedItem.GetWidth() + coupledItem.GetWidth() ) / 2.0 ) * m_trackGapInflation;
994
995 if( perpDistance > maxItemGap )
996 continue;
997
998 // Project on to common axis
999 double a0 = A0.Dot( nA );
1000 double a1 = A1.Dot( nA );
1001 double b0 = B0.Dot( nA );
1002 double b1 = B1.Dot( nA );
1003
1004 bool aReversed = false;
1005 bool bReversed = false;
1006
1007 if( a0 > a1 )
1008 {
1009 std::swap( a0, a1 );
1010 aReversed = true;
1011 }
1012
1013 if( b0 > b1 )
1014 {
1015 std::swap( b0, b1 );
1016 bReversed = true;
1017 }
1018
1019 // Compute overlap interval
1020 double overlap0 = std::max( a0, b0 );
1021 double overlap1 = std::min( a1, b1 );
1022
1023 // Test for overlap
1024 if( overlap1 <= overlap0 )
1025 continue;
1026
1027 // Convert overlap to segment parameters
1028 double tA0 = ( overlap0 - a0 ) / ( a1 - a0 );
1029 double tA1 = ( overlap1 - a0 ) / ( a1 - a0 );
1030 double tB0 = ( overlap0 - b0 ) / ( b1 - b0 );
1031 double tB1 = ( overlap1 - b0 ) / ( b1 - b0 );
1032
1033 // Handle reversed parameterization
1034 if( aReversed )
1035 {
1036 tA0 = 1.0 - tA0;
1037 tA1 = 1.0 - tA1;
1038 }
1039
1040 if( bReversed )
1041 {
1042 tB0 = 1.0 - tB0;
1043 tB1 = 1.0 - tB1;
1044 }
1045
1046 // Normalize parameter ordering
1047 if( tA0 > tA1 )
1048 std::swap( tA0, tA1 );
1049
1050 if( tB0 > tB1 )
1051 std::swap( tB0, tB1 );
1052
1053 // Clamp to physical range
1054 tA0 = std::clamp( tA0, 0.0, 1.0 );
1055 tA1 = std::clamp( tA1, 0.0, 1.0 );
1056
1057 tB0 = std::clamp( tB0, 0.0, 1.0 );
1058 tB1 = std::clamp( tB1, 0.0, 1.0 );
1059
1060 const CUMULATIVE_ENTRY& thisSelCumItem = m_selectedCumulative[ia];
1061 const CUMULATIVE_ENTRY& thisCoupledCumItem = m_coupledCumulative[ib];
1062
1063 auto HasStartingVia = []( const std::vector<CUMULATIVE_ENTRY>& cumulative, const std::size_t index,
1064 const CUMULATIVE_ENTRY& current )
1065 {
1066 if( index == 0 )
1067 return false;
1068
1069 const auto& prev = cumulative[index - 1];
1070
1071 return prev.m_SourceType == LENGTH_DELAY_CALCULATION_ITEM::TYPE::VIA && current.m_Start == prev.m_End;
1072 };
1073
1074 auto HasEndingVia = []( const std::vector<CUMULATIVE_ENTRY>& cumulative, const std::size_t index,
1075 const CUMULATIVE_ENTRY& current )
1076 {
1077 if( index + 1 >= cumulative.size() )
1078 return false;
1079
1080 const auto& next = cumulative[index + 1];
1081
1082 return next.m_SourceType == LENGTH_DELAY_CALCULATION_ITEM::TYPE::VIA && current.m_End == next.m_Start;
1083 };
1084
1085 // Emit the parallel run
1086 PARALLEL_RUN run;
1087
1088 run.ta0 = tA0;
1089 run.ta1 = tA1;
1090 run.tb0 = tB0;
1091 run.tb1 = tB1;
1092
1093 run.segA = ia;
1094 run.segB = ib;
1095
1096 // Calculate start and end points as linear interpolations along segments
1097 run.startA = lerp( A0, A1, tA0 );
1098 run.endA = lerp( A0, A1, tA1 );
1099 run.startB = lerp( B0, B1, tB0 );
1100 run.endB = lerp( B0, B1, tB1 );
1101
1102 // Calculate cumulative values for deltas
1103 auto [length1, delay1] = getCumulativeLengthAndDelayAt(
1105 run.startLenA = length1;
1106 run.startDelayA = delay1;
1107
1108 auto [length2, delay2] = getCumulativeLengthAndDelayAt(
1110 run.endLenA = length2;
1111 run.endDelayA = delay2;
1112
1113 auto [length3, delay3] = getCumulativeLengthAndDelayAt(
1115 run.startLenB = length3;
1116 run.startDelayB = delay3;
1117
1118 auto [length4, delay4] = getCumulativeLengthAndDelayAt(
1120 run.endLenB = length4;
1121 run.endDelayB = delay4;
1122
1123 // Add start / end via length and delay information
1124 if( std::abs( tA0 ) < EPS && HasStartingVia( m_selectedCumulative, ia, thisSelCumItem ) )
1125 {
1126 run.startViaLengthA = m_selectedLengthDelayDetails.LengthsAndDelays[ia - 1].first;
1127 run.startViaDelayA = m_selectedLengthDelayDetails.LengthsAndDelays[ia - 1].second;
1128 }
1129
1130 if( std::abs( tB0 ) < EPS && HasStartingVia( m_coupledCumulative, ib, thisCoupledCumItem ) )
1131 {
1132 run.startViaLengthB = m_coupledLengthDelayDetails.LengthsAndDelays[ib - 1].first;
1133 run.startViaDelayB = m_coupledLengthDelayDetails.LengthsAndDelays[ib - 1].second;
1134 }
1135
1136 if( std::abs( tA1 - 1.0 ) < EPS && HasEndingVia( m_selectedCumulative, ia, thisSelCumItem ) )
1137 {
1138 run.endViaLengthA = m_selectedLengthDelayDetails.LengthsAndDelays[ia + 1].first;
1139 run.endViaDelayA = m_selectedLengthDelayDetails.LengthsAndDelays[ia + 1].second;
1140 }
1141
1142 if( std::abs( tB1 - 1.0 ) < EPS && HasEndingVia( m_coupledCumulative, ib, thisCoupledCumItem ) )
1143 {
1144 run.endViaLengthB = m_coupledLengthDelayDetails.LengthsAndDelays[ib + 1].first;
1145 run.endViaDelayB = m_coupledLengthDelayDetails.LengthsAndDelays[ib + 1].second;
1146 }
1147
1148 aRuns.push_back( run );
1149 }
1150 }
1151}
1152
1153
1154std::vector<PARALLEL_RUN> DIFF_PHASE_SKEW_TOOL::findParallelRuns() const
1155{
1156 std::vector<PARALLEL_RUN> runs;
1157 const double maxGap = getMaxDiffPairGap( m_pickerItemFirst );
1158
1159 // First find runs with regular spacing
1160 findParallelRunsImpl( { 0, m_selectedCumulative.size() }, { 0, m_coupledCumulative.size() }, maxGap, runs );
1161
1162 if( runs.empty() )
1163 return {};
1164
1165 // Check what the min and max segment IDs of each track are
1166 const auto [minSelected, maxSelected] = std::ranges::minmax( runs, {},
1167 []( const PARALLEL_RUN& a )
1168 {
1169 return a.segA;
1170 } );
1171
1172 const auto [minCoupled, maxCoupled] = std::ranges::minmax( runs, {},
1173 []( const PARALLEL_RUN& a )
1174 {
1175 return a.segB;
1176 } );
1177
1178 // Find parallel segments with start separation if needed
1179 const std::size_t firstSegA = minSelected.segA;
1180 const std::size_t lastSegA = maxSelected.segA;
1181 const std::size_t firstSegB = minCoupled.segA;
1182 const std::size_t lastSegB = maxCoupled.segA;
1183
1184 // Assume that tracks start in parallel from pads that are wider than the diff pair spacing. Use the pad separation
1185 // to search for start tracks up to the start of the identified existing parallel segments
1186 if( firstSegA > 0 || firstSegB > 0 )
1187 {
1188 const VECTOR2I selPadLocn = m_selectedStartPad->Pos();
1189 const VECTOR2I coupledPadLocn = m_coupledStartPad->Pos();
1190 const VECTOR2D distVec = selPadLocn - coupledPadLocn;
1191 const double padSeparation = distVec.EuclideanNorm();
1192
1193 findParallelRunsImpl( { 0, firstSegA }, { 0, firstSegB }, padSeparation, runs );
1194 }
1195
1196 // Assume that tracks end in parallel from pads that are wider than the diff pair spacing. Use the pad separation
1197 // to search for end tracks from the end of the identified existing parallel segments
1198 if( lastSegA < m_selectedCumulative.size() || lastSegB > m_coupledCumulative.size() )
1199 {
1200 const VECTOR2I selPadLocn = m_selectedEndPad->Pos();
1201 const VECTOR2I coupledPadLocn = m_coupledEndPad->Pos();
1202 const VECTOR2D distVec = selPadLocn - coupledPadLocn;
1203 const double padSeparation = distVec.EuclideanNorm();
1204
1205 findParallelRunsImpl( { lastSegA, m_selectedCumulative.size() }, { lastSegB, m_coupledCumulative.size() },
1206 padSeparation, runs );
1207 }
1208
1209 std::ranges::sort( runs,
1210 []( const PARALLEL_RUN& a, const PARALLEL_RUN& b )
1211 {
1212 if( a.startLenA != b.startLenA )
1213 {
1214 return a.startLenA < b.startLenA;
1215 }
1216
1217 return a.startLenB < b.startLenB;
1218 } );
1219
1220 return runs;
1221}
1222
1223
1225 const LENGTH_DELAY_ITEM_DETAILS& aLengthDelayDetails, const START_END_DETAILS& aPadDetails,
1226 const std::vector<CUMULATIVE_ENTRY>& aCumulative, const std::size_t aSegIdx, const double aT )
1227{
1228 const double segLength = static_cast<double>( aLengthDelayDetails.LengthsAndDelays[aSegIdx].first );
1229 const double segDelay = static_cast<double>( aLengthDelayDetails.LengthsAndDelays[aSegIdx].second );
1230
1231 // cumulative[i] is the cumulative length / delay at the end of the given segment index. Therefore, subtract
1232 // the not-included fraction of the track from the cumulative value at the segment end to get the length or
1233 // delay at the required fractional distance on the source segment
1234 const double partLen = segLength * ( 1.0 - aT );
1235 const double partDelay = segDelay * ( 1.0 - aT );
1236 return { aCumulative[aSegIdx].m_Length - static_cast<int64_t>( partLen ) + aPadDetails.StartPadLength,
1237 aCumulative[aSegIdx].m_Delay - static_cast<int64_t>( partDelay ) + aPadDetails.StartPadDelay };
1238}
1239
1240
1241std::vector<DIFF_PHASE_SKEW_TOOL::CUMULATIVE_ENTRY> DIFF_PHASE_SKEW_TOOL::buildCumulativeLengthsAndDelays(
1242 const std::vector<LENGTH_DELAY_CALCULATION_ITEM>& aItems, const LENGTH_DELAY_ITEM_DETAILS& aLengthDelayDetails,
1243 const PNS::SOLID* aStartPad, const PNS::SOLID* aEndPad, START_END_DETAILS& aStartEndDetails )
1244{
1245 wxASSERT( aItems.size() == aLengthDelayDetails.LengthsAndDelays.size() );
1246
1247 if( aLengthDelayDetails.LengthsAndDelays.empty() )
1248 return {};
1249
1250 std::vector<CUMULATIVE_ENTRY> cumulative;
1251 cumulative.reserve( aLengthDelayDetails.LengthsAndDelays.size() );
1252
1253 // Calculate start and end pad details
1254 aStartEndDetails.StartPadLength = aStartPad->GetPadToDie() + aLengthDelayDetails.InferredStartViaLength;
1255 aStartEndDetails.StartPadDelay = aStartPad->GetPadToDieDelay() + aLengthDelayDetails.InferredStartViaDelay;
1256 aStartEndDetails.EndPadLength = aEndPad->GetPadToDie() + aLengthDelayDetails.InferredEndViaLength;
1257 aStartEndDetails.EndPadDelay = aEndPad->GetPadToDieDelay() + aLengthDelayDetails.InferredEndViaDelay;
1258
1259 int64_t totalLength = 0;
1260 int64_t totalDelay = 0;
1261
1262 // Add track element details. Note that this adds the cumulative length at the *end*
1263 // of each track element to the cumulative vector.
1264 for( std::size_t i = 0; i < aItems.size(); ++i )
1265 {
1266 const auto [itemLen, itemDly] = aLengthDelayDetails.LengthsAndDelays[i];
1267 const LENGTH_DELAY_CALCULATION_ITEM& item = aItems[i];
1268
1269 VECTOR2I start, end;
1270
1271 if( item.Type() == LENGTH_DELAY_CALCULATION_ITEM::TYPE::LINE )
1272 {
1273 start = item.GetLine().CPoint( 0 );
1274 end = item.GetLine().CLastPoint();
1275 }
1276 else if( item.Type() == LENGTH_DELAY_CALCULATION_ITEM::TYPE::VIA )
1277 {
1278 start = item.GetVia()->GetPosition();
1279 end = start;
1280 }
1281
1282 totalLength += itemLen;
1283 totalDelay += itemDly;
1284 cumulative.emplace_back( totalLength, totalDelay, item.Type(), start, end );
1285 }
1286
1287 return cumulative;
1288}
1289
1290
1291void DIFF_PHASE_SKEW_TOOL::splitLengthItems( std::vector<LENGTH_DELAY_CALCULATION_ITEM>& aItems )
1292{
1293 std::vector<LENGTH_DELAY_CALCULATION_ITEM> splitItems;
1294
1295 auto makeLengthDelayItem = [&splitItems]( const SEG& aSeg, const LENGTH_DELAY_CALCULATION_ITEM& aSourceItem )
1296 {
1297 SHAPE_LINE_CHAIN newLine;
1298 newLine.Append( aSeg.A );
1299 newLine.Append( aSeg.B );
1300
1302 newItem.SetLine( newLine );
1303 newItem.SetWidth( aSourceItem.GetWidth() );
1304 newItem.SetLayers( aSourceItem.GetStartLayer() );
1305 newItem.SetEffectiveNetClass( aSourceItem.GetEffectiveNetClass() );
1306 splitItems.emplace_back( std::move( newItem ) );
1307 };
1308
1309 for( const auto& sourceItem : aItems )
1310 {
1311 // Only process lines
1312 if( sourceItem.Type() != LENGTH_DELAY_CALCULATION_ITEM::TYPE::LINE )
1313 {
1314 splitItems.emplace_back( sourceItem );
1315 continue;
1316 }
1317
1318 SHAPE_LINE_CHAIN& line = sourceItem.GetLine();
1319
1320 for( int segIdx = 0; segIdx < line.SegmentCount(); ++segIdx )
1321 {
1322 SEG seg = line.GetSegment( segIdx );
1323 makeLengthDelayItem( seg, sourceItem );
1324 }
1325 }
1326
1327 aItems = std::move( splitItems );
1328}
1329
1330
1332{
1333 wxString message;
1334 bool error = true;
1335
1336 switch( aDirection )
1337 {
1339 message = wxString::Format( _( "Net %s has multiple simulation electrical source pads" ),
1340 m_selectedNetinfo->GetShortNetname() );
1341 break;
1343 message = wxString::Format( _( "Net %s has multiple simulation electrical source pads" ),
1344 m_coupledNetinfo->GetShortNetname() );
1345 break;
1347 message = wxString::Format( _( "Differential pair %s / %s is missing start and / or end pads" ),
1348 m_selectedNetinfo->GetShortNetname(), m_coupledNetinfo->GetShortNetname() );
1349 break;
1351 message = wxString::Format( _( "Net %s is missing electrical simulation source pad" ),
1352 m_selectedNetinfo->GetShortNetname() );
1353 break;
1355 message = wxString::Format( _( "Net %s is missing electrical simulation source pad" ),
1356 m_coupledNetinfo->GetShortNetname() );
1357 break;
1358 default: error = false; break;
1359 }
1360
1361 if( error )
1362 m_frame->ShowInfoBarError( message, true );
1363
1364 return error;
1365}
1366
1367
1369{
1371 {
1372 const int pnsLayer = m_iface->GetPNSLayerFromBoardLayer( m_pickerItemFirst->GetLayer() );
1373
1374 PCB_TRACK* track = nullptr;
1376 wxCHECK( track, /* void */ );
1377
1378 // Determine primary and secondary net codes
1379 m_selectedNetcode = track->GetNetCode();
1381
1382 // Get the netcodes
1383 m_selectedNetinfo = m_board->GetNetInfo().GetNetItem( m_selectedNetcode );
1384 m_coupledNetinfo = m_board->GetNetInfo().GetNetItem( m_coupledNetcode );
1385
1386 VECTOR2I startSnapPoint;
1387 PNS::LINKED_ITEM* startItem =
1388 PNS::HELPERS::PickSegment( m_router, m_originFirst, pnsLayer, startSnapPoint, SHAPE_LINE_CHAIN() );
1389
1390 if( !startItem || !startItem->OfKind( PNS::ITEM::SEGMENT_T | PNS::ITEM::ARC_T ) )
1391 {
1392 m_frame->ShowInfoBarError( _( "Phase skew initial selection failed" ), true );
1393 return;
1394 }
1395
1396 PNS::NODE* world = m_router->GetWorld()->Branch();
1397 PNS::TOPOLOGY topo( world );
1398 PNS::DIFF_PAIR originPair;
1399
1400 m_selectedStartPad = nullptr;
1401 m_selectedEndPad = nullptr;
1402 m_coupledStartPad = nullptr;
1403 m_coupledEndPad = nullptr;
1404
1405 if( !topo.AssembleDiffPair( startItem, originPair ) )
1406 {
1407 m_frame->ShowInfoBarError( _( "Differential pair identification failed" ), true );
1408 return;
1409 }
1410
1411 if( !originPair.PLine().SegmentCount() || !originPair.NLine().SegmentCount() )
1412 return;
1413
1415 {
1419 &m_coupledEndPad );
1420 }
1421 else
1422 {
1424 &m_coupledEndPad );
1427 }
1428 }
1429 else
1430 {
1431 const int pnsLayerFirst = m_iface->GetPNSLayerFromBoardLayer( m_pickerItemFirst->GetLayer() );
1432 const int pnsLayerSecond = m_iface->GetPNSLayerFromBoardLayer( m_pickerItemSecond->GetLayer() );
1433
1434 PCB_TRACK* trackFirst = nullptr;
1436 wxCHECK( trackFirst, /* void */ );
1437
1438 PCB_TRACK* trackSecond = nullptr;
1440 wxCHECK( trackSecond, /* void */ );
1441
1442 // Determine primary and secondary net codes
1443 m_selectedNetcode = trackFirst->GetNetCode();
1444 m_coupledNetcode = trackSecond->GetNetCode();
1445
1446 // Get the netcodes
1447 m_selectedNetinfo = m_board->GetNetInfo().GetNetItem( m_selectedNetcode );
1448 m_coupledNetinfo = m_board->GetNetInfo().GetNetItem( m_coupledNetcode );
1449
1450 VECTOR2I startSnapPointFirst, startSnapPointSecond;
1451 PNS::LINKED_ITEM* startItemFirst = PNS::HELPERS::PickSegment( m_router, m_originFirst, pnsLayerFirst,
1452 startSnapPointFirst, SHAPE_LINE_CHAIN() );
1453 PNS::LINKED_ITEM* startItemSecond = PNS::HELPERS::PickSegment( m_router, m_originSecond, pnsLayerSecond,
1454 startSnapPointSecond, SHAPE_LINE_CHAIN() );
1455
1456 if( !startItemFirst || !startItemFirst->OfKind( PNS::ITEM::SEGMENT_T | PNS::ITEM::ARC_T ) || !startItemSecond
1457 || !startItemSecond->OfKind( PNS::ITEM::SEGMENT_T | PNS::ITEM::ARC_T ) )
1458 {
1459 m_frame->ShowInfoBarError( _( "Phase skew initial selection failed" ), true );
1460 return;
1461 }
1462
1463 PNS::NODE* world = m_router->GetWorld()->Branch();
1464 PNS::TOPOLOGY topo( world );
1465
1466 m_selectedStartPad = nullptr;
1467 m_selectedEndPad = nullptr;
1468 m_coupledStartPad = nullptr;
1469 m_coupledEndPad = nullptr;
1470
1473 }
1474
1475 // The router can return SHAPE_LINE_CHAINS that are in a reversed order. This doesn't play nicely
1476 // with our parallel / antiparallel rejection tests, therfore we need to ensure the SHAPE_LINE_CHAIN
1477 // points are in line order here
1480}
1481
1483{
1484 if( !aStartPad )
1485 return;
1486
1487 VECTOR2I pathSearchLoc = aStartPad->Pos();
1488
1489 for( int i = 0; i < aPath.Size(); ++i )
1490 {
1491 PNS::ITEM* curItem = aPath[i];
1492
1493 if( curItem->Kind() == PNS::ITEM::LINE_T )
1494 {
1495 PNS::LINE* lineItem = dynamic_cast<PNS::LINE*>( curItem );
1496
1497 if( !lineItem )
1498 continue;
1499
1500 SHAPE_LINE_CHAIN& line = lineItem->Line();
1501 const std::size_t numPoints = line.GetPointCount();
1502 wxASSERT( numPoints >= 2 );
1503
1504 if( line.GetPoint( numPoints - 1 ) == pathSearchLoc )
1505 line = line.Reverse();
1506
1507 pathSearchLoc = line.GetPoint( numPoints - 1 );
1508 }
1509 else if( curItem->Kind() == PNS::ITEM::VIA_T )
1510 {
1511 const PNS::VIA* viaItem = dynamic_cast<PNS::VIA*>( curItem );
1512 wxASSERT( viaItem->Pos() == pathSearchLoc );
1513 }
1514 }
1515}
1516
1517
1519{
1520 // This condition can occur if the route contains items that are not tracks (e.g. unconverted arcs)
1523
1524 PAD* selectedStartPad = static_cast<PAD*>( m_selectedStartPad->BoardItem() );
1525 PAD* selectedEndPad = static_cast<PAD*>( m_selectedEndPad->BoardItem() );
1526 PAD* coupledStartPad = static_cast<PAD*>( m_coupledStartPad->BoardItem() );
1527 PAD* coupledEndPad = static_cast<PAD*>( m_coupledEndPad->BoardItem() );
1528
1529 if( !selectedStartPad || !selectedEndPad || !coupledStartPad || !coupledEndPad )
1531
1532 // Normalise directions if possible
1533 if( selectedStartPad->GetSimElectricalType() != PAD_SIM_ELECTRICAL_TYPE::SOURCE
1535 {
1537 std::swap( selectedStartPad, selectedEndPad );
1538 }
1539
1542 {
1544 std::swap( coupledStartPad, coupledEndPad );
1545 }
1546
1547 // Both start pads must be sources
1548 if( selectedStartPad->GetSimElectricalType() != PAD_SIM_ELECTRICAL_TYPE::SOURCE )
1549 {
1551 }
1552
1553 if( coupledStartPad->GetSimElectricalType() != PAD_SIM_ELECTRICAL_TYPE::SOURCE )
1554 {
1556 }
1557
1558 // End pads can't be a source
1559 if( selectedEndPad->GetSimElectricalType() == PAD_SIM_ELECTRICAL_TYPE::SOURCE )
1561
1564
1566}
1567
1568
1570{
1571 PNS::ITEM_SET reversed;
1572
1573 for( auto itr = aPath.rbegin(); itr != aPath.rend(); ++itr )
1574 {
1575 if( ( *itr )->Kind() == PNS::ITEM::LINE_T )
1576 {
1577 PNS::LINE* l = dyn_cast<PNS::LINE*>( *itr );
1578 wxASSERT( l != nullptr );
1579
1580 if( l != nullptr )
1581 l->Reverse();
1582 }
1583
1584 reversed.Add( *itr );
1585 }
1586
1587 aPath = std::move( reversed );
1588
1589 // Finally reverse the start / end pads
1590 std::swap( *aStartPad, *aEndPad );
1591}
1592
1593
1595{
1596 constexpr std::size_t maxIdx = std::numeric_limits<std::size_t>::max();
1597 const double hitTestDistance = m_view->ToWorld( DETAILS_HOVER_HITTEST_THRESHOLD_PIXELS );
1598 const auto [selectedIdx, isSelectedTrack] = getNearestDiffSegments( m_cursorPos, hitTestDistance );
1599
1600 if( selectedIdx < maxIdx )
1601 {
1602 if( isSelectedTrack )
1603 m_segmentForStatisticsDisplay = { selectedIdx, true };
1604 else
1605 m_segmentForStatisticsDisplay = { selectedIdx, false };
1606 }
1607 else
1608 {
1609 m_segmentForStatisticsDisplay = { maxIdx, false };
1610 }
1611
1613}
1614
1615
1616std::pair<std::size_t, bool> DIFF_PHASE_SKEW_TOOL::getNearestDiffSegments( const VECTOR2D& aCursorPos,
1617 const double aHitTestDistance ) const
1618{
1619 auto getNearestSegment = [&aCursorPos, aHitTestDistance]( const std::vector<OUTPUT_SEGMENT>& segments )
1620 {
1621 const double maxDistSq = aHitTestDistance * aHitTestDistance;
1622
1623 std::size_t bestIndex = std::numeric_limits<std::size_t>::max();
1624 double bestDistSq = maxDistSq;
1625
1626 for( size_t i = 0; i < segments.size(); ++i )
1627 {
1628 const auto& seg = segments[i];
1629
1630 VECTOR2D mid = ( seg.Start + seg.End ) / 2.0;
1631 const double distSq = ( aCursorPos - mid ).SquaredEuclideanNorm();
1632
1633 if( distSq <= bestDistSq )
1634 {
1635 bestDistSq = distSq;
1636 bestIndex = i;
1637 }
1638 }
1639
1640 return std::pair<std::size_t, double>( bestIndex, bestDistSq );
1641 };
1642
1643 const auto [selectedIdx, selectedDist] = getNearestSegment( m_selectedDiffs );
1644 const auto [coupledIdx, coupledDist] = getNearestSegment( m_coupledDiffs );
1645
1646 constexpr std::size_t maxIdx = std::numeric_limits<std::size_t>::max();
1647
1648 if( selectedIdx != maxIdx && coupledIdx != maxIdx )
1649 {
1650 if( selectedDist <= coupledDist )
1651 return { selectedIdx, true };
1652
1653 return { coupledIdx, false };
1654 }
1655
1656 if( selectedIdx != maxIdx )
1657 return { selectedIdx, true };
1658
1659 if( coupledIdx != maxIdx )
1660 return { coupledIdx, false };
1661
1662 return { maxIdx, true };
1663}
1664
1665
1667{
1668 RENDER_SETTINGS* renderSettings = m_frame->GetCanvas()->GetView()->GetPainter()->GetSettings();
1669 renderSettings->SetHighlight( false );
1670
1671 if( m_pickerItemFirst )
1672 {
1673 renderSettings->SetHighlight( true, m_netcodeP, true );
1674
1676 renderSettings->SetHighlight( true, m_netcodeN, true );
1677 }
1678
1679 if( m_pickerItemSecond )
1680 renderSettings->SetHighlight( true, m_netcodeN, true );
1681
1682 m_frame->GetCanvas()->GetView()->UpdateAllLayersColor();
1683
1684 if( aRefresh )
1685 m_frame->GetCanvas()->Refresh();
1686}
1687
1688
1690{
1691 // clang-format off
1693 // clang-format on
1694}
1695
1696
1698{
1699 if( !m_viewOverlay )
1700 {
1701 m_viewOverlay = m_view->MakeOverlay();
1702 m_view->Add( m_viewOverlay.get() );
1703 }
1704}
1705
1706
1708{
1709 if( m_viewOverlay )
1710 {
1711 m_viewOverlay->Clear();
1712 updateOverlay();
1713 }
1714}
1715
1716
1718{
1719 if( m_viewOverlay )
1720 {
1721 m_view->Update( m_viewOverlay.get() );
1722 }
1723}
1724
1725
1727{
1728 m_pickerItemFirst = nullptr;
1729 m_pickerItemSecond = nullptr;
1731 m_netcodeP = 0;
1732 m_netcodeN = 0;
1733 m_originFirst = { 0, 0 };
1734 m_originSecond = { 0, 0 };
1735 m_timeDomain = false;
1737 m_coupledNetcode = 0;
1738 m_selectedNetinfo = nullptr;
1739 m_coupledNetinfo = nullptr;
1740 m_selectedPath.Clear();
1741 m_coupledPath.Clear();
1742 m_selectedStartPad = nullptr;
1743 m_selectedEndPad = nullptr;
1744 m_coupledStartPad = nullptr;
1745 m_coupledEndPad = nullptr;
1748 m_selectedLengthDelayDetails.LengthsAndDelays.clear();
1749 m_coupledLengthDelayDetails.LengthsAndDelays.clear();
1750 m_selectedDiffs.clear();
1751 m_coupledDiffs.clear();
1752 m_segmentForStatisticsDisplay = { std::numeric_limits<std::size_t>::max(), false };
1753 m_maxSkew.reset();
1754}
int index
@ NORMAL
Inactive layers are shown normally (no high-contrast mode)
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:995
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
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.
int GetCount() const
Return the number of objects in the list.
Definition collector.h:79
int m_Threshold
Definition collector.h:234
int ShowModal() override
void SetMode(const MODE aMode)
The tool entry point.
void setTransitions() override
< Set up handlers for tool events
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
DIFF_PAIR_VALIDITY determinePathDirections()
Reverses the direction of the selected and coupled paths (including swapping start / end pads.
bool reportValidityErrors(DIFF_PAIR_VALIDITY aDirection) const
Struct to represent one cumulative length and delay point.
START_END_DETAILS m_coupledStartEndDetails
LENGTH_DELAY_ITEM_DETAILS m_coupledLengthDelayDetails
static void reversePath(PNS::ITEM_SET &aPath, PNS::SOLID **aStartPad, PNS::SOLID **aEndPad)
Report to the user any errors after determining the signal direction.
static VECTOR2D lerp(const VECTOR2D aA, const VECTOR2D aB, const double aT)
Gets the cumulative length and delay at the given fractional coordinate in the given segment.
std::pair< std::size_t, bool > m_segmentForStatisticsDisplay
static void normalisePathItems(const PNS::ITEM_SET &aPath, const PNS::SOLID *aStartPad)
Determine which end of the extracted paths we are defining as the signal start point.
int getMaxDiffPairGap(const BOARD_CONNECTED_ITEM *aItem) const
Struct containing a final computed output diff segment.
static std::vector< double > buildSplitPositions(const std::vector< CUMULATIVE_ENTRY > &aSegments, double aTargetSubsegmentSize)
Returns the coordinate at the given linear distance along the line, along with the segment index the ...
MODE GetMode() const
Set the current mode of the tool.
void clearOverlay() const
Resets all select-specific variables.
std::vector< KNOWN_RELATIVE_POINT > m_selectedKnownPoints
std::vector< LENGTH_DELAY_CALCULATION_ITEM > m_selectedLengthDelayItems
void doDisplayOverlay()
Use the router to get the +ve and -ve paths from the selected item.
bool Init() override
Init() is called once upon a registration of the tool.
std::vector< CUMULATIVE_ENTRY > m_selectedCumulative
void getNetPaths()
Normalises the path to ensure SHAPE_LINE_CHAIN points are in overall path walk order.
BOARD_CONNECTED_ITEM * m_pickerItemFirst
KIGFX::VIEW_CONTROLS * m_controls
static void splitLengthItems(std::vector< LENGTH_DELAY_CALCULATION_ITEM > &aItems)
Finds all parallel segment runs in the selected and coupled tracks.
std::vector< OUTPUT_SEGMENT > m_selectedDiffs
void buildLengthDelayItems(const PNS::ITEM_SET &aPath, const PNS::SOLID *aStartPad, const PNS::SOLID *aEndPad, const NETINFO_ITEM *aNet, std::vector< LENGTH_DELAY_CALCULATION_ITEM > &aItems, LENGTH_DELAY_ITEM_DETAILS &aItemDetails) const
Start and end pad lengths and delays (pad-to-die + inferred via-in-pad)
void doInitialHover(const PCB_SELECTION_TOOL *aSelectionTool, GENERAL_COLLECTORS_GUIDE aGuide)
Display the phase overlay for the current hover item.
PCB_BASE_EDIT_FRAME * m_frame
int ShowDiffPhaseSkew(const TOOL_EVENT &aEvent)
Flags for the analysis state of the selected tracks.
void doShowStatsAtCursor()
Determines the nearest points to the cursor from the diff segments.
std::vector< OUTPUT_SEGMENT > m_coupledDiffs
COLOR4D interpolateColours(const COLOR4D &aColour1, const COLOR4D &aColour2, double aS, bool aUseLogScale) const
Draws the visual skew overlay.
static std::vector< CUMULATIVE_ENTRY > buildCumulativeLengthsAndDelays(const std::vector< LENGTH_DELAY_CALCULATION_ITEM > &aItems, const LENGTH_DELAY_ITEM_DETAILS &aLengthDelayDetails, const PNS::SOLID *aStartPad, const PNS::SOLID *aEndPad, START_END_DETAILS &aStartEndDetails)
Splits the calculation items from compound segments in to individual items.
void findParallelRunsImpl(std::pair< std::size_t, std::size_t > aRangeA, std::pair< std::size_t, std::size_t > aRangeB, double aMaxSpacing, std::vector< PARALLEL_RUN > &aRuns) const
Linear interpolate from point A to B at line fraction T.
std::pair< std::size_t, bool > getNearestDiffSegments(const VECTOR2D &aCursorPos, double aHitTestDistance) const
Ensures we have an active VIEW_OVERLAY to display the diff graphics.
std::vector< OUTPUT_SEGMENT > buildDiffOverlaySegmentsImpl(const std::vector< CUMULATIVE_ENTRY > &aSegments, const std::vector< LENGTH_DELAY_CALCULATION_ITEM > &aSourceItemDetails, const std::vector< KNOWN_RELATIVE_POINT > &aKnownPoints, double aTargetSubsegmentSize)
Builds a vector of known relative skew points on each track.
std::vector< KNOWN_RELATIVE_POINT > m_coupledKnownPoints
static std::pair< int64_t, int64_t > getCumulativeLengthAndDelayAt(const LENGTH_DELAY_ITEM_DETAILS &aLengthDelayDetails, const START_END_DETAILS &aPadDetails, const std::vector< CUMULATIVE_ENTRY > &aCumulative, std::size_t aSegIdx, double aT)
Gets the maximum diff pair gap for the given item, taken from DRC rules.
static std::pair< VECTOR2D, std::size_t > pointAtDistance(const std::vector< CUMULATIVE_ENTRY > &aSegments, const std::vector< LENGTH_DELAY_CALCULATION_ITEM > &aSourceItemDetails, double aDist)
Linearly interpolates between colour1 and colour2, with interpolation point given by aS [0-1].
void updateOverlay() const
Clears the VIEW_OVERLAY.
void buildDiffOverlaySegments(double aTargetSubsegmentSize)
std::optional< int > m_maxSkew
std::shared_ptr< KIGFX::VIEW_OVERLAY > m_viewOverlay
void buildKnownRelativePoints(const std::vector< PARALLEL_RUN > &aKnownRuns)
Determines where to apply overlay segment subsections on the source segments.
PNS::SIZES_SETTINGS m_savedSizes
void getOverlay()
Refreshes the VIEW_OVERLAY in the active VIEW.
BOARD_CONNECTED_ITEM * m_pickerItemSecond
void updateNetHighlights(bool aRefresh=true) const
Handle hover events before a DP pair is selected.
void resetStateVariables()
Updates the message panel.
std::vector< PARALLEL_RUN > findParallelRuns() const
Finds all parallel segment runs in the selected and coupled tracks within the given segment ranges an...
LENGTH_DELAY_ITEM_DETAILS m_selectedLengthDelayDetails
START_END_DETAILS m_selectedStartEndDetails
std::vector< LENGTH_DELAY_CALCULATION_ITEM > m_coupledLengthDelayItems
std::vector< CUMULATIVE_ENTRY > m_coupledCumulative
void drawDiffOverlay() const
Shows the diff stats nearest the cursor.
std::shared_ptr< DRC_ENGINE > m_drcEngine
SEVERITY GetSeverity() const
Definition drc_rule.h:221
const MINOPTMAX< int > & GetValue() const
Definition drc_rule.h:200
bool IsNull() const
Definition drc_rule.h:193
std::shared_ptr< DRC_ENGINE > GetDRCEngine()
Definition drc_tool.h:83
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:98
A general implementation of a COLLECTORS_GUIDE.
Definition collectors.h:320
void SetPreferredLayer(PCB_LAYER_ID aLayer)
Definition collectors.h:386
void SetIncludeSecondary(bool include)
Definition collectors.h:400
Used when the right click button is pressed, or when the select tool is in effect.
Definition collectors.h:203
void Collect(BOARD_ITEM *aItem, const std::vector< KICAD_T > &aScanList, const VECTOR2I &aRefPos, const COLLECTORS_GUIDE &aGuide)
Scan a BOARD_ITEM using this class's Inspector method, which does the collection.
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
double r
Red component.
Definition color4d.h:390
double g
Green component.
Definition color4d.h:391
double a
Alpha component.
Definition color4d.h:393
double b
Blue component.
Definition color4d.h:392
Container for all the knowledge about how graphical objects are drawn on any output surface/device.
void SetHighlight(bool aEnabled, int aNetcode=-1, bool aMulti=false)
Turns on/off highlighting.
An interface for classes handling user events controlling the view behavior such as zooming,...
Interpolates known relative points along a track using linear distance.
std::optional< std::pair< double, double > > ValueAt(const double s)
Lightweight class which holds a pad, via, or a routed trace outline.
void SetLine(const SHAPE_LINE_CHAIN &aLine)
Sets the source SHAPE_LINE_CHAIN of this item.
TYPE Type() const
Gets the routing item type.
int GetWidth() const
Gets the line width.
const PCB_VIA * GetVia() const
Gets the VIA associated with this item.
void SetWidth(const int aWidth)
Sets the line width.
SHAPE_LINE_CHAIN & GetLine() const
Gets the SHAPE_LINE_CHAIN associated with this item.
PCB_LAYER_ID GetStartLayer() const
Gets the start board layer for the proxied item.
void SetEffectiveNetClass(const NETCLASS *aNetClass)
Sets the effective net class for the item.
void SetLayers(const PCB_LAYER_ID aStart, const PCB_LAYER_ID aEnd=PCB_LAYER_ID::UNDEFINED_LAYER)
Sets the first and last layers associated with this item.
bool HasMax() const
Definition minoptmax.h:35
T Max() const
Definition minoptmax.h:30
T Opt() const
Definition minoptmax.h:31
bool HasOpt() const
Definition minoptmax.h:36
Handle the data for a net.
Definition netinfo.h:50
NETCLASS * GetNetClass()
Definition netinfo.h:101
int GetNetCode() const
Definition netinfo.h:104
Definition pad.h:61
PAD_SIM_ELECTRICAL_TYPE GetSimElectricalType() const
Definition pad.h:571
DIFF_PHASE_SKEW_SETTINGS m_DiffPhaseSkewSettings
std::unique_ptr< PNS::ROUTING_SETTINGS > m_PnsSettings
static TOOL_ACTION properties
Activation of the edit tool.
static TOOL_ACTION showDiffPhaseSkew
Display of phase skew between differential pair tracks.
The selection tool: currently supports:
void GuessSelectionCandidates(GENERAL_COLLECTOR &aCollector, const VECTOR2I &aWhere) const
Try to guess best selection candidates in case multiple items are clicked, by doing some brain-dead h...
T * frame() const
KIGFX::VIEW_CONTROLS * controls() const
PCB_TOOL_BASE(TOOL_ID aId, const std::string &aName)
Constructor.
VECTOR2I GetPosition() const override
Definition pcb_track.h:580
Basic class for a differential pair.
int Size() const
std::vector< ITEM * >::reverse_iterator rbegin()
void Add(const LINE &aLine)
std::vector< ITEM * >::reverse_iterator rend()
Base class for PNS router board items.
Definition pns_item.h:98
PnsKind Kind() const
Return the type (kind) of the item.
Definition pns_item.h:173
bool OfKind(int aKindMask) const
Definition pns_item.h:181
virtual BOARD_ITEM * BoardItem() const
Definition pns_item.h:207
Represents a track on a PCB, connecting two non-trivial joints (that is, vias, pads,...
Definition pns_line.h:62
SHAPE_LINE_CHAIN & Line()
Definition pns_line.h:145
int SegmentCount() const
Definition pns_line.h:148
void Reverse()
Clip the line to the nearest obstacle, traversing from the line's start vertex (0).
Keep the router "world" - i.e.
Definition pns_node.h:243
int GetPadToDie() const
Definition pns_solid.h:122
int GetPadToDieDelay() const
Definition pns_solid.h:125
const VECTOR2I & Pos() const
Definition pns_solid.h:119
const DIFF_PAIR AssembleDiffPair(SEGMENT *aStart)
const ITEM_SET AssembleTuningPath(ROUTER_IFACE *aRouterIface, ITEM *aStart, SOLID **aStartPad=nullptr, SOLID **aEndPad=nullptr)
Like AssembleTrivialPath, but follows the track length algorithm, which discards segments that are fu...
const VECTOR2I & Pos() const
Definition pns_via.h:206
Definition seg.h:38
VECTOR2I A
Definition seg.h:45
VECTOR2I B
Definition seg.h:46
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
const SHAPE_LINE_CHAIN Reverse() const
Reverse point order in the line chain.
virtual const VECTOR2I GetPoint(int aIndex) const override
SEG Segment(int aIndex) const
Return a copy of the aIndex-th segment in the line chain.
virtual size_t GetPointCount() const override
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
virtual const SEG GetSegment(int aIndex) const override
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
int SegmentCount() const
Return the number of segments in this line chain.
const VECTOR2I & CLastPoint() const
Return the last point in the line chain.
T * getModel() const
Return the model object if it matches the requested type.
Definition tool_base.h:195
KIGFX::VIEW_CONTROLS * getViewControls() const
Return the instance of VIEW_CONTROLS object used in the application.
Definition tool_base.cpp:40
TOOL_MANAGER * m_toolMgr
Definition tool_base.h:220
KIGFX::VIEW * getView() const
Returns the instance of #VIEW object used in the application.
Definition tool_base.cpp:34
RESET_REASON
Determine the reason of reset for a tool.
Definition tool_base.h:74
@ SHUTDOWN
Tool is being shut down.
Definition tool_base.h:80
Generic, UI-independent tool event.
Definition tool_event.h:167
bool HasPosition() const
Returns if it this event has a valid position (true for mouse events and context-menu or hotkey-based...
Definition tool_event.h:256
const VECTOR2D Position() const
Return mouse cursor position in world coordinates.
Definition tool_event.h:289
void Go(int(T::*aStateFunc)(const TOOL_EVENT &), const TOOL_EVENT_LIST &aConditions=TOOL_EVENT(TC_ANY, TA_ANY))
Define which state (aStateFunc) to go when a certain event arrives (aConditions).
TOOL_EVENT * Wait(const TOOL_EVENT_LIST &aEventList=TOOL_EVENT(TC_ANY, TA_ANY))
Suspend execution of the tool until an event specified in aEventList arrives.
void Activate()
Run the tool.
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition vector2d.h:279
constexpr extended_type Dot(const VECTOR2< T > &aVector) const
Compute dot product of self with aVector.
Definition vector2d.h:542
@ ARROW
Definition cursors.h:42
#define INITIAL_HOVER_HITTEST_THRESHOLD_PIXELS
#define DETAILS_HOVER_HITTEST_THRESHOLD_PIXELS
constexpr double EPS
Floating point comparison epsilon.
@ DIFF_PAIR_GAP_CONSTRAINT
Definition drc_rule.h:78
#define _(s)
double m_DiffSkewColourInterpolationLogStrength
The logarithmic weighting factor to apply to colour interpolation in the diff phase overlay tool.
double m_DiffSkewTrackGapInflation
The multiplier of constraint diff pair gap to allow identification of coupled track segments in the d...
double m_DiffSkewTargetDiffSegmentSize
The target size (in PCB IU) of diff phase skew gradient overlay segments.
double m_DiffSkewCosThetaParallelTestValue
The value of cos(theta) between two tracks used to test for parallelism in the diff phase skew overla...
double m_DiffSkewOverlayTrackInflation
The multiplier of underlying track size applied to the diff phase skew overlay.
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:411
CITER next(CITER it)
Definition ptree.cpp:120
@ RPT_SEVERITY_IGNORE
char * GetLine(FILE *File, char *Line, int *LineNum, int SizeLine)
Read one line line from aFile.
Builds the length / delay calculation items from a given path.
Builds the final overlay output segments for plotting.
VECTOR2D Start
The start point of the segment.
VECTOR2D End
The end point of the segment.
bool RelativeValueKnown
Flag whether the diff value is valid at this segment.
double RelativeValueAtMid
The value of the diff at the beginning of this segment.
Builds a vector in which each entry represents the cumulative length and delay at the start of a give...
Used to represent the results of a call to CalculateLengthDetails, including inferred via-in-pad deta...
int64_t InferredEndViaLength
The length of an inferred end via-in-pad.
int64_t InferredStartViaLength
The length of an inferred start via-in-pad.
int64_t InferredEndViaDelay
The delay of an inferred end via-in-pad.
std::vector< std::pair< int64_t, int64_t > > LengthsAndDelays
Per-item lengths and delays.
int64_t InferredStartViaDelay
The delay of an inferred start via-in-pad.
Struct to represent one segment where tracks run parallel, including information about absolute and r...
VECTOR2I endA
The ending coordinate of the run on track A.
std::optional< double > startViaLengthB
std::optional< double > endViaDelayA
std::optional< double > startViaLengthA
double endDelayA
Cumulative delay of track A at the start of the parallel run.
double startLenA
Cumulative length of track A at the start of the parallel run.
size_t segB
The index of the parallel segment on track B.
VECTOR2I startA
The starting coordinate of the run on track A.
std::optional< double > startViaDelayB
std::optional< double > startViaDelayA
VECTOR2I startB
The starting coordinate of the run on track B.
std::optional< double > endViaDelayB
double ta0
Normalised values of the start (0) and end (1) coordinates on track A and B These are normalised to t...
double startDelayA
Cumulative delay of track A at the start of the parallel run.
std::optional< double > endViaLengthA
double startLenB
Cumulative length of track B at the start of the parallel run.
double endLenA
Cumulative length of track A at the end of the parallel run.
double endDelayB
Cumulative delay of track A at the start of the parallel run.
double startDelayB
Cumulative delay of track A at the start of the parallel run.
double endLenB
Cumulative length of track B at the end of the parallel run.
size_t segA
The index of the parallel segment on track A.
VECTOR2I endB
The ending coordinate of the run on track B.
std::optional< double > endViaLengthB
Struct to control which optimisations the length calculation code runs on the given path objects.
static VECTOR2I SnapToNearestTrack(const VECTOR2I &aP, BOARD *aBoard, NETINFO_ITEM *aNet, PCB_TRACK **aNearestTrack)
static LINKED_ITEM * PickSegment(ROUTER *aRouter, const VECTOR2I &aWhere, int aLayer, VECTOR2I &aPointOut, const SHAPE_LINE_CHAIN &aBaseline=SHAPE_LINE_CHAIN())
Represents a single line in the tuning profile configuration grid.
VECTOR2I end
wxString result
Test unit parsing edge cases and error handling.
@ BUT_LEFT
Definition tool_event.h:128
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:90
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:88
Casted dyn_cast(From aObject)
A lightweight dynamic downcast.
Definition typeinfo.h:55
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683
VECTOR2< double > VECTOR2D
Definition vector2d.h:682