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 m_viewOverlay.reset();
83
84 if( aReason == RESET_REASON::SHUTDOWN )
85 {
86 m_router = nullptr;
87 m_iface = nullptr;
88 return;
89 }
90
91 // Get core objects
92 m_view = getView();
95 m_frame = frame();
96 DRC_TOOL* drcTool = m_toolMgr->GetTool<DRC_TOOL>();
97 m_drcEngine = drcTool->GetDRCEngine();
98
99 // Initialise a router instance
101 m_iface->SetBoard( m_board );
102 m_iface->SetView( m_view );
103 m_iface->SetHostTool( this );
104
105 m_router = new PNS::ROUTER;
106 m_router->SetInterface( m_iface );
107 m_router->ClearWorld();
108 m_router->SyncWorld();
109 m_router->UpdateSizes( m_savedSizes );
110
111 PCBNEW_SETTINGS* settings = m_frame->GetPcbNewSettings();
112
113 if( !settings->m_PnsSettings )
114 settings->m_PnsSettings = std::make_unique<PNS::ROUTING_SETTINGS>( settings, "tools.pns" );
115
116 m_router->LoadSettings( settings->m_PnsSettings.get() );
117
119}
120
121
123{
125 return 0;
126
128
129 SCOPED_TOOL_PUSHER raii( m_frame, aEvent );
130
131 Activate();
132
133 // Must be done after Activate() so that it gets set into the correct context
135 // controls->ShowCursor( true );
136 // controls->ForceCursorPosition( false );
137
138 // Set initial cursor
139 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::TUNE );
140
141 // Get the required tools and helpers
142 PCB_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
143 GENERAL_COLLECTORS_GUIDE guide = m_frame->GetCollectorsGuide();
144
145 // Create the VIEW_OVERLAY
146 getOverlay();
147
148 m_pickerItemFirst = nullptr;
149
150 if( aEvent.HasPosition() )
151 m_toolMgr->PrimeTool( aEvent.Position() );
152
153 // Main loop: keep receiving events
154 while( TOOL_EVENT* evt = Wait() )
155 {
156 m_cursorPos = controls->GetMousePosition();
157
158 if( evt->IsCancelInteractive() || evt->IsActivate() )
159 {
160 // Roll back our mode, or exit if we are in the initial hover mode
162 {
163 m_pickerItemFirst = nullptr;
165 m_pickerItemSecond = nullptr;
166 clearOverlay();
168 m_maxSkew.reset();
170 }
171 else
172 {
174 break;
175 }
176 }
177
178 if( evt->IsMotion() || evt->IsAction( &ACTIONS::refreshPreview ) )
179 {
180 if( GetMode() == MODE::HOVER )
181 {
182 m_pickerItemFirst = nullptr;
183 m_pickerItemSecond = nullptr;
185 doInitialHover( selectionTool, guide );
187 }
188 else if( GetMode() == MODE::SELECTED_FIRST )
189 {
190 m_pickerItemSecond = nullptr;
191 doInitialHover( selectionTool, guide );
193 }
194 else if( GetMode() == MODE::FIXED_MODE )
195 {
198 }
199 }
200 else if( ( evt->IsClick( BUT_LEFT ) || evt->IsAction( &ACTIONS::cursorClick ) )
201 && GetMode() == MODE::HOVER
203 {
205
207 {
210 }
211 else
212 {
214 }
215
217 }
218 else if( ( evt->IsClick( BUT_LEFT ) || evt->IsAction( &ACTIONS::cursorClick ) )
221 {
222 // First click to select the diff pair for inspection
227 }
228 else if( evt->IsAction( &PCB_ACTIONS::properties ) )
229 {
231 PCBNEW_SETTINGS* cfg = m_frame->GetPcbNewSettings();
232 settings = cfg->m_DiffPhaseSkewSettings;
233
235
236 if( dlg.ShowModal() == wxID_OK )
237 {
238 cfg->m_DiffPhaseSkewSettings = settings;
239
240 if( GetMode() == MODE::FIXED_MODE )
242 }
243 }
244 }
245
246 // Restore UI state
247 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
248
249 // Reset tool state
252
253 updateNetHighlights( false );
254 m_frame->GetCanvas()->Refresh();
255
256 return 0;
257}
258
259
261{
262 GENERAL_COLLECTOR collector;
264
265 if( m_frame->GetDisplayOptions().m_ContrastModeDisplay != HIGH_CONTRAST_MODE::NORMAL )
266 aGuide.SetIncludeSecondary( false );
267 else
268 aGuide.SetIncludeSecondary( true );
269
270 aGuide.SetPreferredLayer( m_frame->GetActiveLayer() );
271 collector.Collect( m_board, { PCB_TRACE_T, PCB_ARC_T }, m_cursorPos, aGuide );
272
273 if( collector.GetCount() > 1 )
274 aSelectionTool->GuessSelectionCandidates( collector, m_cursorPos );
275
276 if( collector.GetCount() > 0 )
277 {
278 double min_dist_sq = std::numeric_limits<double>::max();
279
280 for( EDA_ITEM* candidate : collector )
281 {
282 VECTOR2I candidatePos;
283
284 if( candidate->Type() == PCB_TRACE_T )
285 candidatePos = static_cast<PCB_TRACK*>( candidate )->GetCenter();
286 else if( candidate->Type() == PCB_ARC_T )
287 candidatePos = static_cast<PCB_ARC*>( candidate )->GetMid();
288
289 const double dist_sq = ( m_cursorPos - candidatePos ).SquaredEuclideanNorm();
290
291 if( dist_sq < min_dist_sq )
292 {
293 const auto bci = static_cast<BOARD_CONNECTED_ITEM*>( candidate );
294 const NETINFO_ITEM* candidateNet = bci->GetNet();
295
296 if( GetMode() == MODE::HOVER )
297 {
298 min_dist_sq = dist_sq;
299
300 // We only accept diff pairs in initial hover mode
301 const bool isDiffPairItem =
302 m_drcEngine->IsNetADiffPair( m_board, candidateNet, m_netcodeP, m_netcodeN );
303 m_pickerItemFirst = static_cast<BOARD_CONNECTED_ITEM*>( candidate );
304
305 if( isDiffPairItem )
306 {
308 }
309 else
310 {
312 m_netcodeP = candidateNet->GetNetCode();
313 }
314 }
315 else if( GetMode() == MODE::SELECTED_FIRST )
316 {
317 int fakeNCP, fakeNCN;
318
319 // Reject diff pairs here as we have a not-diff-pair selected
320 const bool isDiffPairItem = m_drcEngine->IsNetADiffPair( m_board, candidateNet, fakeNCP, fakeNCN );
321
322 auto existingBci = static_cast<BOARD_CONNECTED_ITEM*>( m_pickerItemFirst );
323 const NETINFO_ITEM* existingNet = existingBci->GetNet();
324
325 if( !isDiffPairItem && candidateNet != existingNet )
326 {
327 min_dist_sq = dist_sq;
328 m_pickerItemSecond = static_cast<BOARD_CONNECTED_ITEM*>( candidate );
329 m_netcodeN = candidateNet->GetNetCode();
330 }
331 }
332 }
333 }
334 }
335
337}
338
339
341 const PNS::SOLID* aEndPad, const NETINFO_ITEM* aNet,
342 std::vector<LENGTH_DELAY_CALCULATION_ITEM>& aItems,
343 LENGTH_DELAY_ITEM_DETAILS& aItemDetails ) const
344{
345 if( aPath.Size() == 0 )
346 return;
347
348 // Convert path to length / delay interface types
349 aItems = m_iface->GetLengthDelayCalculationItems( aPath, aNet->GetNetClass() );
350 wxASSERT( aItems.size() == static_cast<size_t>( aPath.Size() ) );
351
352 // The router returns compound lines - we need to split them in to their constituent segments / arcs
353 splitLengthItems( aItems );
354
355 // Get the per-item length / delay statistics
356 constexpr PATH_OPTIMISATIONS opts = {
357 .OptimiseVias = false, .MergeTracks = false, .OptimiseTracesInPads = false, .InferViaInPad = true
358 };
359
360 const PAD* startPad = dynamic_cast<PAD*>( aStartPad->BoardItem() );
361 const PAD* endPad = dynamic_cast<PAD*>( aEndPad->BoardItem() );
362 aItemDetails = LENGTH_DELAY_ITEM_DETAILS{};
363 m_board->GetLengthCalculation()->CalculateLengthDetails(
364 aItems, opts, startPad, endPad, LENGTH_DELAY_LAYER_OPT::NO_LAYER_DETAIL,
366 wxASSERT( aItemDetails.LengthsAndDelays.size() == aItems.size() );
367}
368
369
371{
372 // Extract the diff pair net paths
373 getNetPaths();
374
375 // Determine what we are defining as the 'start' of the net
377
378 if( reportValidityErrors( direction ) )
379 {
381 return;
382 }
383
384 m_timeDomain = false;
385
386 // Check if both nets have time domain parameters
387 if( m_selectedNetinfo->GetNetClass()->HasTuningProfile() && m_coupledNetinfo->GetNetClass()->HasTuningProfile() )
388 {
389 wxString selectedTuningProfileName = m_selectedNetinfo->GetNetClass()->GetTuningProfile();
390 wxString coupledTuningProfileName = m_coupledNetinfo->GetNetClass()->GetTuningProfile();
391
392 std::shared_ptr<TUNING_PROFILES> tuningParams = m_frame->Prj().GetProjectFile().TuningProfileParameters();
393 const TUNING_PROFILE& selectedTuningProfile = tuningParams->GetTuningProfile( selectedTuningProfileName );
394 const TUNING_PROFILE& coupledTuningProfile = tuningParams->GetTuningProfile( coupledTuningProfileName );
395
396 if( selectedTuningProfile.m_EnableTimeDomainTuning && coupledTuningProfile.m_EnableTimeDomainTuning )
397 m_timeDomain = true;
398 }
399
400 // Construct the length / delay calculation items
403
406
407 // Build the cumulative length / delay structures
414
415 // Walk the two tracks and construct the localised phase differences
416 const std::vector<PARALLEL_RUN> parallelRuns = findParallelRuns();
417
418 // Build the known delay reference points for each track
419 buildKnownRelativePoints( parallelRuns );
420
421 m_maxSkew.reset();
423
424 // Finally draw the overlay
426}
427
428
429void DIFF_PHASE_SKEW_TOOL::buildKnownRelativePoints( const std::vector<PARALLEL_RUN>& aKnownRuns )
430{
431 m_selectedKnownPoints.clear();
432 m_coupledKnownPoints.clear();
433
434 const double padLenDiff =
435 static_cast<double>( m_selectedStartEndDetails.StartPadLength - m_coupledStartEndDetails.StartPadLength );
436 const double padDelayDiff =
437 static_cast<double>( m_selectedStartEndDetails.StartPadDelay - m_coupledStartEndDetails.StartPadDelay );
438
439 struct RELATIVE_PAIR
440 {
441 double len;
442 double delay;
443 };
444
445 auto opposite = []( const RELATIVE_PAIR& a )
446 {
447 return RELATIVE_PAIR{ -a.len, -a.delay };
448 };
449
450 for( const auto& r : aKnownRuns )
451 {
452 const std::size_t segIdxA = r.segA;
453 const std::size_t segIdxB = r.segB;
454
455 const double segStartA = segIdxA == 0 ? 0.0 : static_cast<double>( m_selectedCumulative[segIdxA - 1].m_Length );
456 const double segStartB = segIdxB == 0 ? 0.0 : static_cast<double>( m_coupledCumulative[segIdxB - 1].m_Length );
457 const double segLenA = static_cast<double>( m_selectedLengthDelayDetails.LengthsAndDelays[segIdxA].first );
458 const double segLenB = static_cast<double>( m_coupledLengthDelayDetails.LengthsAndDelays[segIdxB].first );
459
460 const double s0A = segStartA + r.ta0 * segLenA;
461 const double s1A = segStartA + r.ta1 * segLenA;
462 const double s0B = segStartB + r.tb0 * segLenB;
463 const double s1B = segStartB + r.tb1 * segLenB;
464
465 const RELATIVE_PAIR startRel{ ( r.startLenA - r.startLenB ) + padLenDiff,
466 ( r.startDelayA - r.startDelayB ) + padDelayDiff };
467 const RELATIVE_PAIR endRel{ ( r.endLenA - r.endLenB ) + padLenDiff,
468 ( r.endDelayA - r.endDelayB ) + padDelayDiff };
469 const RELATIVE_PAIR startRelB = opposite( startRel );
470 const RELATIVE_PAIR endRelB = opposite( endRel );
471
472 const bool hasStartViaA = r.startViaLengthA.has_value();
473 const bool hasEndViaA = r.endViaLengthA.has_value();
474 const bool hasStartViaB = r.startViaLengthB.has_value();
475 const bool hasEndViaB = r.endViaLengthB.has_value();
476
477 const double startViaLenA = r.startViaLengthA.value_or( 0.0 );
478 const double endViaLenA = r.endViaLengthA.value_or( 0.0 );
479 const double startViaLenB = r.startViaLengthB.value_or( 0.0 );
480 const double endViaLenB = r.endViaLengthB.value_or( 0.0 );
481
482 const double startViaDelayA = r.startViaDelayA.value_or( 0.0 );
483 const double endViaDelayA = r.endViaDelayA.value_or( 0.0 );
484 const double startViaDelayB = r.startViaDelayB.value_or( 0.0 );
485 const double endViaDelayB = r.endViaDelayB.value_or( 0.0 );
486
487 // Injected start-via points
488 if( hasStartViaA && hasStartViaB )
489 {
490 const RELATIVE_PAIR rel{ startRel.len - startViaLenA + startViaLenB,
491 startRel.delay - startViaDelayA + startViaDelayB };
492 const RELATIVE_PAIR relB = opposite( rel );
493 m_selectedKnownPoints.push_back( { s0A - startViaLenA, rel.len, rel.delay, rel.len, rel.delay } );
494 m_coupledKnownPoints.push_back( { s0B - startViaLenB, relB.len, relB.delay, relB.len, relB.delay } );
495 }
496
497 // Maybe-modified values used for interpolation around via discontinuities
498 RELATIVE_PAIR startBeforeA = startRel;
499 RELATIVE_PAIR startBeforeB = startRelB;
500
501 if( hasStartViaA && !hasStartViaB )
502 {
503 startBeforeB.len += startViaLenA;
504 startBeforeB.delay += startViaDelayA;
505 m_selectedKnownPoints.push_back( { s0A - startViaLenA, startRel.len - startViaLenA,
506 startRel.delay - startViaDelayA, startRel.len - startViaLenA,
507 startRel.delay - startViaDelayA } );
508 }
509 else if( !hasStartViaA && hasStartViaB )
510 {
511 startBeforeA.len += startViaLenB;
512 startBeforeA.delay += startViaDelayB;
513 m_coupledKnownPoints.push_back( { s0B - startViaLenB, startRelB.len - startViaLenB,
514 startRelB.delay - startViaDelayB, startRelB.len - startViaLenB,
515 startRelB.delay - startViaDelayB } );
516 }
517
518 // Maybe-modified values used for interpolation around via discontinuities
519 RELATIVE_PAIR endAfterA = endRel;
520 RELATIVE_PAIR endAfterB = endRelB;
521
522 if( hasEndViaA && !hasEndViaB )
523 {
524 endAfterB.len -= endViaLenA;
525 endAfterB.delay -= endViaDelayA;
526 }
527 else if( !hasEndViaA && hasEndViaB )
528 {
529 endAfterA.len -= endViaLenB;
530 endAfterA.delay -= endViaDelayB;
531 }
532
533 // Main known points
534 m_selectedKnownPoints.push_back( { s0A, startBeforeA.len, startBeforeA.delay, startRel.len, startRel.delay } );
535 m_selectedKnownPoints.push_back( { s1A, endRel.len, endRel.delay, endAfterA.len, endAfterA.delay } );
536 m_coupledKnownPoints.push_back( { s0B, startBeforeB.len, startBeforeB.delay, startRelB.len, startRelB.delay } );
537 m_coupledKnownPoints.push_back( { s1B, endRelB.len, endRelB.delay, endAfterB.len, endAfterB.delay } );
538
539 // Injected end-via points
540 if( hasEndViaA && hasEndViaB )
541 {
542 const RELATIVE_PAIR rel{ endRel.len + endViaLenA - endViaLenB, endRel.delay + endViaDelayA - endViaDelayB };
543 const RELATIVE_PAIR relB = opposite( rel );
544 m_selectedKnownPoints.push_back( { s1A + endViaLenA, rel.len, rel.delay, rel.len, rel.delay } );
545 m_coupledKnownPoints.push_back( { s1B + endViaLenB, relB.len, relB.delay, relB.len, relB.delay } );
546 }
547 else if( hasEndViaA && !hasEndViaB )
548 {
549 m_selectedKnownPoints.push_back( { s1A + endViaLenA, endRel.len + endViaLenA, endRel.delay + endViaDelayA,
550 endRel.len + endViaLenA, endRel.delay + endViaDelayA } );
551 }
552 else if( !hasEndViaA && hasEndViaB )
553 {
554 m_coupledKnownPoints.push_back( { s1B + endViaLenB, endRelB.len + endViaLenB, endRelB.delay + endViaDelayB,
555 endRelB.len + endViaLenB, endRelB.delay + endViaDelayB } );
556 }
557 }
558}
559
560
561std::vector<double> DIFF_PHASE_SKEW_TOOL::buildSplitPositions( const std::vector<CUMULATIVE_ENTRY>& aSegments,
562 const double aTargetSubsegmentSize )
563{
564 if( aSegments.empty() )
565 return {};
566
567 std::vector<double> splits;
568
569 // Start of line
570 if( aSegments[0].m_SourceType == LENGTH_DELAY_CALCULATION_ITEM::TYPE::LINE )
571 splits.push_back( 0.0 );
572
573 double currentDistance = 0;
574
575 for( const CUMULATIVE_ENTRY& seg : aSegments )
576 {
577 const double segStart = currentDistance;
578 const double segEnd = seg.m_Length;
579 currentDistance = segEnd;
580
581 // Only emit segments for lines
582 if( seg.m_SourceType != LENGTH_DELAY_CALCULATION_ITEM::TYPE::LINE )
583 continue;
584
585 // Fixed subdivision spacing
586 for( double s = segStart; s < segEnd; s += aTargetSubsegmentSize )
587 {
588 splits.push_back( s );
589 }
590
591 splits.push_back( segEnd );
592 }
593
594 // Sort and make unique
595 std::ranges::sort( splits );
596
597 splits.erase( std::ranges::unique( splits,
598 []( const double a, const double b )
599 {
600 return std::abs( a - b ) < EPS;
601 } )
602 .begin(),
603 splits.end() );
604
605 return splits;
606}
607
608
609std::pair<VECTOR2D, std::size_t>
610DIFF_PHASE_SKEW_TOOL::pointAtDistance( const std::vector<CUMULATIVE_ENTRY>& aSegments,
611 const std::vector<LENGTH_DELAY_CALCULATION_ITEM>& aSourceItemDetails,
612 const double aDist )
613{
614 for( std::size_t i = 0; i < aSegments.size(); ++i )
615 {
616 const double segStart = i == 0 ? 0.0 : static_cast<double>( aSegments[i - 1].m_Length );
617 const double segEnd = static_cast<double>( aSegments[i].m_Length );
618
619 if( aDist <= segEnd + EPS )
620 {
621 const double segLen = segEnd - segStart;
622
623 double t = 0.0;
624
625 if( segLen > EPS )
626 t = ( aDist - segStart ) / segLen;
627
628 t = std::clamp( t, 0.0, 1.0 );
629
630 if( aSourceItemDetails[i].Type() == LENGTH_DELAY_CALCULATION_ITEM::TYPE::VIA )
631 {
632 // We've hit a via - use the end point from the previous line
633 wxASSERT( i > 0 );
634 wxASSERT( aSourceItemDetails[i - 1].Type() == LENGTH_DELAY_CALCULATION_ITEM::TYPE::LINE );
635 wxASSERT( aSourceItemDetails[i - 1].GetLine().CPoints().size() == 2 );
636 return { aSourceItemDetails[i - 1].GetLine().CPoints()[1], i - 1 };
637 }
638
639 wxASSERT( aSourceItemDetails[i].Type() == LENGTH_DELAY_CALCULATION_ITEM::TYPE::LINE );
640 wxASSERT( aSourceItemDetails[i].GetLine().CPoints().size() == 2 );
641
642 return {
643 lerp( aSourceItemDetails[i].GetLine().CPoints()[0], aSourceItemDetails[i].GetLine().CPoints()[1], t ), i
644 };
645 }
646 }
647
648 // We shouldn't reach this point...
649 wxASSERT( false );
650 return { { 0.0, 0.0 }, 0 };
651}
652
653
654COLOR4D DIFF_PHASE_SKEW_TOOL::interpolateColours( const COLOR4D& aColour1, const COLOR4D& aColour2, double aS,
655 const bool aUseLogScale ) const
656{
657 auto lerp = []( const double d1, const double d2, const double s )
658 {
659 return d1 + s * ( d2 - d1 );
660 };
661
662 if( aUseLogScale )
663 aS = std::log( 1.0 + m_colourInterpolationLogStrength * aS )
664 / std::log( 1.0 + m_colourInterpolationLogStrength );
665
666 const double r = std::clamp( lerp( aColour1.r, aColour2.r, aS ), 0.0, 1.0 );
667 const double g = std::clamp( lerp( aColour1.g, aColour2.g, aS ), 0.0, 1.0 );
668 const double b = std::clamp( lerp( aColour1.b, aColour2.b, aS ), 0.0, 1.0 );
669 const double a = std::clamp( lerp( aColour1.a, aColour2.a, aS ), 0.0, 1.0 );
670
671 return COLOR4D( r, g, b, a );
672}
673
674
682
683
684std::vector<DIFF_PHASE_SKEW_TOOL::OUTPUT_SEGMENT> DIFF_PHASE_SKEW_TOOL::buildDiffOverlaySegmentsImpl(
685 const std::vector<CUMULATIVE_ENTRY>& aSegments,
686 const std::vector<LENGTH_DELAY_CALCULATION_ITEM>& aSourceItemDetails,
687 const std::vector<KNOWN_RELATIVE_POINT>& aKnownPoints, double aTargetSubsegmentSize )
688{
689 std::vector<OUTPUT_SEGMENT> result;
690
691 if( aSegments.empty() )
692 return result;
693
694 PCBNEW_SETTINGS* cfg = m_frame->GetPcbNewSettings();
696
697 // Get min and max values
698 double minLen = 0.0;
699 double maxLen = 0.0;
700 double minDelay = 0.0;
701 double maxDelay = 0.0;
702
703 for( const auto& [_1, relLenBefore, relDelayBefore, relLenAfter, relDelayAfter] : aKnownPoints )
704 {
705 minLen = std::min( minLen, std::min( relLenBefore, relLenAfter ) );
706 maxLen = std::max( maxLen, std::max( relLenBefore, relLenAfter ) );
707 minDelay = std::min( minDelay, std::min( relDelayBefore, relDelayAfter ) );
708 maxDelay = std::max( maxDelay, std::max( relDelayBefore, relDelayAfter ) );
709 }
710
711 int maxSkew = m_maxSkew.value_or( 0 );
712
713 if( m_timeDomain )
714 m_maxSkew = std::max( static_cast<int>( std::round( maxDelay / 10 ) * 10 ), maxSkew );
715 else
716 m_maxSkew = std::max( static_cast<int>( std::round( maxLen / 10 ) * 10 ), maxSkew );
717
718 // Build all subdivision boundaries
719 const auto splits = buildSplitPositions( aSegments, aTargetSubsegmentSize );
720
721 KnownValueInterpolator interp( aKnownPoints );
722
723 // Emit subdivided segments
724 for( std::size_t i = 0; i + 1 < splits.size(); ++i )
725 {
726 const double s0 = splits[i];
727 const double s1 = splits[i + 1];
728
729 // Skip degenerate intervals
730 if( s1 - s0 <= EPS )
731 continue;
732
733 OUTPUT_SEGMENT out;
734
735 auto [startPoint, segIdx] = pointAtDistance( aSegments, aSourceItemDetails, s0 );
736 out.Width = static_cast<int>( aSourceItemDetails[segIdx].GetWidth() * m_overlayTrackInflation );
737 out.Start = startPoint;
738 auto [endPoint, _] = pointAtDistance( aSegments, aSourceItemDetails, s1 );
739 out.End = endPoint;
740
741 const double sMid = ( s0 + s1 ) / 2.0;
742
743 const std::optional<std::pair<double, double>> knownInterp = interp.ValueAt( sMid );
744 out.RelativeValueKnown = knownInterp.has_value();
745
746 double min = 0.0;
747 double max = 0.0;
748
749 if( m_timeDomain )
750 {
751 out.RelativeValueAtMid = knownInterp.value_or( std::pair<double, double>{ 0.0, 0.0 } ).second;
752 min = minDelay;
753 max = maxDelay;
754 }
755 else
756 {
757 out.RelativeValueAtMid = knownInterp.value_or( std::pair<double, double>{ 0.0, 0.0 } ).first;
758 min = minLen;
759 max = maxLen;
760 }
761
762 // Round value to nearest 10 IU to reduce low-level colour jitter on equal tracks
763 out.RelativeValueAtMid = std::round( out.RelativeValueAtMid / 10 ) * 10;
764
765 // Calculate colour value
766 if( !out.RelativeValueKnown )
767 {
768 out.Colour = settings.m_UnknownSkewColor;
769 }
770 else if( out.RelativeValueAtMid < 0.0 )
771 {
772 const double frac = fabs( out.RelativeValueAtMid / min );
773 out.Colour = interpolateColours( settings.m_ZeroSkewColor, settings.m_NegativeSkewColor, frac,
774 settings.m_UseLogScale );
775 }
776 else if( out.RelativeValueAtMid > 0.0 )
777 {
778 const double frac = fabs( out.RelativeValueAtMid / max );
779 out.Colour = interpolateColours( settings.m_ZeroSkewColor, settings.m_PositiveSkewColor, frac,
780 settings.m_UseLogScale );
781 }
782 else
783 {
784 out.Colour = settings.m_ZeroSkewColor;
785 }
786
787 result.push_back( out );
788 }
789
790 return result;
791}
792
793
795{
796 clearOverlay();
797
798 const std::size_t selIdx = m_segmentForStatisticsDisplay.first;
799 const bool isSelected = m_segmentForStatisticsDisplay.second;
800 const bool drawHighlight = selIdx < std::numeric_limits<std::size_t>::max();
801
802 m_viewOverlay->SetIsStroke( true );
803 m_viewOverlay->SetIsFill( false );
804
805 for( std::size_t i = 0; i < m_selectedDiffs.size(); ++i )
806 {
807 const OUTPUT_SEGMENT& segment = m_selectedDiffs[i];
808
809 if( drawHighlight && isSelected && i == selIdx )
810 m_viewOverlay->SetStrokeColor( COLOR4D( 1.0, 0.0, 0.937, 1.0 ) );
811 else
812 m_viewOverlay->SetStrokeColor( segment.Colour );
813
814 m_viewOverlay->Segment( segment.Start, segment.End, segment.Width );
815 }
816
817 for( std::size_t i = 0; i < m_coupledDiffs.size(); ++i )
818 {
819 const OUTPUT_SEGMENT& segment = m_coupledDiffs[i];
820
821 if( drawHighlight && !isSelected && i == selIdx )
822 m_viewOverlay->SetStrokeColor( COLOR4D( 1.0, 0.0, 0.937, 1.0 ) );
823 else
824 m_viewOverlay->SetStrokeColor( segment.Colour );
825
826 m_viewOverlay->Segment( segment.Start, segment.End, segment.Width );
827 }
828
830}
831
832
834{
835 std::vector<MSG_PANEL_ITEM> items;
836
837 if( !m_pickerItemFirst )
838 {
839 frame()->SetMsgPanel( m_board );
840 return;
841 }
842
844 {
845 wxString description = wxString::Format( _( "Net A Name" ) );
846 wxString netName = m_pickerItemFirst->GetNet()->GetDisplayNetname();
847 items.emplace_back( description, netName );
848
850 {
851 description = wxString::Format( _( "Net B Name" ) );
852 netName = m_pickerItemSecond->GetNet()->GetDisplayNetname();
853 items.emplace_back( description, netName );
854 }
855 }
856 else
857 {
858 wxString description = wxString::Format( _( "Net P Name" ) );
859 wxString netName = m_board->GetNetInfo().GetNetItem( m_netcodeP )->GetDisplayNetname();
860 items.emplace_back( description, netName );
861
862 description = wxString::Format( _( "Net N Name" ) );
863 netName = m_board->GetNetInfo().GetNetItem( m_netcodeN )->GetDisplayNetname();
864 items.emplace_back( description, netName );
865 }
866
867 if( m_maxSkew.has_value() )
868 {
869 wxString description = wxString::Format( _( "Max Skew" ) );
870 wxString value;
871
872 if( m_timeDomain )
873 value = m_frame->MessageTextFromValue( m_maxSkew.value(), true, EDA_DATA_TYPE::TIME );
874 else
875 value = m_frame->MessageTextFromValue( m_maxSkew.value(), true, EDA_DATA_TYPE::DISTANCE );
876
877 items.emplace_back( description, value );
878 }
879
880 const std::size_t selIdx = m_segmentForStatisticsDisplay.first;
881 const bool isSelected = m_segmentForStatisticsDisplay.second;
882 const bool drawHighlight = selIdx < std::numeric_limits<std::size_t>::max();
883
884 if( drawHighlight )
885 {
886 const OUTPUT_SEGMENT& segment = isSelected ? m_selectedDiffs[selIdx] : m_coupledDiffs[selIdx];
887 double normalisedValue = std::round( segment.RelativeValueAtMid );
888 normalisedValue = ( normalisedValue == 0.0 ) ? 0.0 : normalisedValue;
889
890 wxString description = _( "Local Skew" );
891 wxString value = _( "Unknown" );
892
893 if( segment.RelativeValueKnown )
894 {
895 value = m_frame->MessageTextFromValue( normalisedValue, true,
897 }
898
899
900 items.emplace_back( description, value );
901 }
902
903 frame()->SetMsgPanel( items );
904}
905
906
908{
910 {
911 // TODO: This assumes the gap is the same across all traces, but actually this can vary
912 // TODO: by layer. We should get a representative segment for each layer from the
913 // TODO: two tracks and find the max constraint across all of them.
914 const DRC_CONSTRAINT constraint =
915 m_drcEngine->EvalRules( DIFF_PAIR_GAP_CONSTRAINT, aItem, nullptr, aItem->GetLayer() );
916
917 if( constraint.IsNull() || constraint.GetSeverity() == RPT_SEVERITY_IGNORE )
918 return std::numeric_limits<int>::max();
919
920 const MINOPTMAX<int>& val = constraint.GetValue();
921
922 if( val.HasOpt() && val.HasMax() )
923 return std::max( val.Max(), val.Opt() );
924 else if( val.HasMax() )
925 return val.Max();
926 else if( val.HasOpt() )
927 return val.Opt();
928
929 return std::numeric_limits<int>::max();
930 }
931 else
932 {
934 return dist.EuclideanNorm();
935 }
936}
937
938
939void DIFF_PHASE_SKEW_TOOL::findParallelRunsImpl( std::pair<std::size_t, std::size_t> aRangeA,
940 std::pair<std::size_t, std::size_t> aRangeB, double aMaxSpacing,
941 std::vector<PARALLEL_RUN>& aRuns ) const
942{
943 for( size_t ia = aRangeA.first; ia < aRangeA.second; ++ia )
944 {
946
947 if( selectedItem.Type() != LENGTH_DELAY_CALCULATION_ITEM::TYPE::LINE )
948 continue;
949
950 const SHAPE_LINE_CHAIN& lineA = selectedItem.GetLine();
951 wxASSERT( lineA.SegmentCount() == 1 );
952 const SEG segA = lineA.Segment( 0 );
953
954 VECTOR2D A0 = segA.A;
955 VECTOR2D A1 = segA.B;
956
957 VECTOR2D dA = A1 - A0;
958 const double lenA = dA.EuclideanNorm();
959 VECTOR2D nA{ dA.x / lenA, dA.y / lenA };
960
961 for( size_t ib = aRangeB.first; ib < aRangeB.second; ++ib )
962 {
964
965 if( coupledItem.Type() != LENGTH_DELAY_CALCULATION_ITEM::TYPE::LINE )
966 continue;
967
968 if( selectedItem.GetStartLayer() != coupledItem.GetStartLayer() )
969 continue;
970
971 const SHAPE_LINE_CHAIN& lineB = coupledItem.GetLine();
972 wxASSERT( lineB.SegmentCount() == 1 );
973 const SEG segB = lineB.Segment( 0 );
974
975 VECTOR2D B0 = segB.A;
976 VECTOR2D B1 = segB.B;
977
978 VECTOR2D dB = B1 - B0;
979 const double lenB = dB.EuclideanNorm();
980 VECTOR2D nB{ dB.x / lenB, dB.y / lenB };
981
982 // Test for parallel line segments
983 const double dp = nA.Dot( nB );
984
985 // Note that this test is explicitly signed (not fabs(dp)) to ensure anti-parallel tracks are rejected
987 continue;
988
989 // Test for perpendicular distance
990 VECTOR2D midB = ( B0 + B1 ) * 0.5;
991
992 // Perpendicular distance from midpoint of B to infinite line through A
993 const double perpDistance = std::fabs( nA.Cross( midB - A0 ) );
994
995 const double maxItemGap =
996 ( aMaxSpacing + ( selectedItem.GetWidth() + coupledItem.GetWidth() ) / 2.0 ) * m_trackGapInflation;
997
998 if( perpDistance > maxItemGap )
999 continue;
1000
1001 // Project on to common axis
1002 double a0 = A0.Dot( nA );
1003 double a1 = A1.Dot( nA );
1004 double b0 = B0.Dot( nA );
1005 double b1 = B1.Dot( nA );
1006
1007 bool aReversed = false;
1008 bool bReversed = false;
1009
1010 if( a0 > a1 )
1011 {
1012 std::swap( a0, a1 );
1013 aReversed = true;
1014 }
1015
1016 if( b0 > b1 )
1017 {
1018 std::swap( b0, b1 );
1019 bReversed = true;
1020 }
1021
1022 // Compute overlap interval
1023 double overlap0 = std::max( a0, b0 );
1024 double overlap1 = std::min( a1, b1 );
1025
1026 // Test for overlap
1027 if( overlap1 <= overlap0 )
1028 continue;
1029
1030 // Convert overlap to segment parameters
1031 double tA0 = ( overlap0 - a0 ) / ( a1 - a0 );
1032 double tA1 = ( overlap1 - a0 ) / ( a1 - a0 );
1033 double tB0 = ( overlap0 - b0 ) / ( b1 - b0 );
1034 double tB1 = ( overlap1 - b0 ) / ( b1 - b0 );
1035
1036 // Handle reversed parameterization
1037 if( aReversed )
1038 {
1039 tA0 = 1.0 - tA0;
1040 tA1 = 1.0 - tA1;
1041 }
1042
1043 if( bReversed )
1044 {
1045 tB0 = 1.0 - tB0;
1046 tB1 = 1.0 - tB1;
1047 }
1048
1049 // Normalize parameter ordering
1050 if( tA0 > tA1 )
1051 std::swap( tA0, tA1 );
1052
1053 if( tB0 > tB1 )
1054 std::swap( tB0, tB1 );
1055
1056 // Clamp to physical range
1057 tA0 = std::clamp( tA0, 0.0, 1.0 );
1058 tA1 = std::clamp( tA1, 0.0, 1.0 );
1059
1060 tB0 = std::clamp( tB0, 0.0, 1.0 );
1061 tB1 = std::clamp( tB1, 0.0, 1.0 );
1062
1063 const CUMULATIVE_ENTRY& thisSelCumItem = m_selectedCumulative[ia];
1064 const CUMULATIVE_ENTRY& thisCoupledCumItem = m_coupledCumulative[ib];
1065
1066 auto HasStartingVia = []( const std::vector<CUMULATIVE_ENTRY>& cumulative, const std::size_t index,
1067 const CUMULATIVE_ENTRY& current )
1068 {
1069 if( index == 0 )
1070 return false;
1071
1072 const auto& prev = cumulative[index - 1];
1073
1074 return prev.m_SourceType == LENGTH_DELAY_CALCULATION_ITEM::TYPE::VIA && current.m_Start == prev.m_End;
1075 };
1076
1077 auto HasEndingVia = []( const std::vector<CUMULATIVE_ENTRY>& cumulative, const std::size_t index,
1078 const CUMULATIVE_ENTRY& current )
1079 {
1080 if( index + 1 >= cumulative.size() )
1081 return false;
1082
1083 const auto& next = cumulative[index + 1];
1084
1085 return next.m_SourceType == LENGTH_DELAY_CALCULATION_ITEM::TYPE::VIA && current.m_End == next.m_Start;
1086 };
1087
1088 // Emit the parallel run
1089 PARALLEL_RUN run;
1090
1091 run.ta0 = tA0;
1092 run.ta1 = tA1;
1093 run.tb0 = tB0;
1094 run.tb1 = tB1;
1095
1096 run.segA = ia;
1097 run.segB = ib;
1098
1099 // Calculate start and end points as linear interpolations along segments
1100 run.startA = lerp( A0, A1, tA0 );
1101 run.endA = lerp( A0, A1, tA1 );
1102 run.startB = lerp( B0, B1, tB0 );
1103 run.endB = lerp( B0, B1, tB1 );
1104
1105 // Calculate cumulative values for deltas
1106 auto [length1, delay1] = getCumulativeLengthAndDelayAt(
1108 run.startLenA = length1;
1109 run.startDelayA = delay1;
1110
1111 auto [length2, delay2] = getCumulativeLengthAndDelayAt(
1113 run.endLenA = length2;
1114 run.endDelayA = delay2;
1115
1116 auto [length3, delay3] = getCumulativeLengthAndDelayAt(
1118 run.startLenB = length3;
1119 run.startDelayB = delay3;
1120
1121 auto [length4, delay4] = getCumulativeLengthAndDelayAt(
1123 run.endLenB = length4;
1124 run.endDelayB = delay4;
1125
1126 // Add start / end via length and delay information
1127 if( std::abs( tA0 ) < EPS && HasStartingVia( m_selectedCumulative, ia, thisSelCumItem ) )
1128 {
1129 run.startViaLengthA = m_selectedLengthDelayDetails.LengthsAndDelays[ia - 1].first;
1130 run.startViaDelayA = m_selectedLengthDelayDetails.LengthsAndDelays[ia - 1].second;
1131 }
1132
1133 if( std::abs( tB0 ) < EPS && HasStartingVia( m_coupledCumulative, ib, thisCoupledCumItem ) )
1134 {
1135 run.startViaLengthB = m_coupledLengthDelayDetails.LengthsAndDelays[ib - 1].first;
1136 run.startViaDelayB = m_coupledLengthDelayDetails.LengthsAndDelays[ib - 1].second;
1137 }
1138
1139 if( std::abs( tA1 - 1.0 ) < EPS && HasEndingVia( m_selectedCumulative, ia, thisSelCumItem ) )
1140 {
1141 run.endViaLengthA = m_selectedLengthDelayDetails.LengthsAndDelays[ia + 1].first;
1142 run.endViaDelayA = m_selectedLengthDelayDetails.LengthsAndDelays[ia + 1].second;
1143 }
1144
1145 if( std::abs( tB1 - 1.0 ) < EPS && HasEndingVia( m_coupledCumulative, ib, thisCoupledCumItem ) )
1146 {
1147 run.endViaLengthB = m_coupledLengthDelayDetails.LengthsAndDelays[ib + 1].first;
1148 run.endViaDelayB = m_coupledLengthDelayDetails.LengthsAndDelays[ib + 1].second;
1149 }
1150
1151 aRuns.push_back( run );
1152 }
1153 }
1154}
1155
1156
1157std::vector<PARALLEL_RUN> DIFF_PHASE_SKEW_TOOL::findParallelRuns() const
1158{
1159 std::vector<PARALLEL_RUN> runs;
1160 const double maxGap = getMaxDiffPairGap( m_pickerItemFirst );
1161
1162 // First find runs with regular spacing
1163 findParallelRunsImpl( { 0, m_selectedCumulative.size() }, { 0, m_coupledCumulative.size() }, maxGap, runs );
1164
1165 if( runs.empty() )
1166 return {};
1167
1168 // Check what the min and max segment IDs of each track are
1169 const auto [minSelected, maxSelected] = std::ranges::minmax( runs, {},
1170 []( const PARALLEL_RUN& a )
1171 {
1172 return a.segA;
1173 } );
1174
1175 const auto [minCoupled, maxCoupled] = std::ranges::minmax( runs, {},
1176 []( const PARALLEL_RUN& a )
1177 {
1178 return a.segB;
1179 } );
1180
1181 // Find parallel segments with start separation if needed
1182 const std::size_t firstSegA = minSelected.segA;
1183 const std::size_t lastSegA = maxSelected.segA;
1184 const std::size_t firstSegB = minCoupled.segA;
1185 const std::size_t lastSegB = maxCoupled.segA;
1186
1187 // Assume that tracks start in parallel from pads that are wider than the diff pair spacing. Use the pad separation
1188 // to search for start tracks up to the start of the identified existing parallel segments
1189 if( firstSegA > 0 || firstSegB > 0 )
1190 {
1191 const VECTOR2I selPadLocn = m_selectedStartPad->Pos();
1192 const VECTOR2I coupledPadLocn = m_coupledStartPad->Pos();
1193 const VECTOR2D distVec = selPadLocn - coupledPadLocn;
1194 const double padSeparation = distVec.EuclideanNorm();
1195
1196 findParallelRunsImpl( { 0, firstSegA }, { 0, firstSegB }, padSeparation, runs );
1197 }
1198
1199 // Assume that tracks end in parallel from pads that are wider than the diff pair spacing. Use the pad separation
1200 // to search for end tracks from the end of the identified existing parallel segments
1201 if( lastSegA < m_selectedCumulative.size() || lastSegB > m_coupledCumulative.size() )
1202 {
1203 const VECTOR2I selPadLocn = m_selectedEndPad->Pos();
1204 const VECTOR2I coupledPadLocn = m_coupledEndPad->Pos();
1205 const VECTOR2D distVec = selPadLocn - coupledPadLocn;
1206 const double padSeparation = distVec.EuclideanNorm();
1207
1208 findParallelRunsImpl( { lastSegA, m_selectedCumulative.size() }, { lastSegB, m_coupledCumulative.size() },
1209 padSeparation, runs );
1210 }
1211
1212 std::ranges::sort( runs,
1213 []( const PARALLEL_RUN& a, const PARALLEL_RUN& b )
1214 {
1215 if( a.startLenA != b.startLenA )
1216 {
1217 return a.startLenA < b.startLenA;
1218 }
1219
1220 return a.startLenB < b.startLenB;
1221 } );
1222
1223 return runs;
1224}
1225
1226
1228 const LENGTH_DELAY_ITEM_DETAILS& aLengthDelayDetails, const START_END_DETAILS& aPadDetails,
1229 const std::vector<CUMULATIVE_ENTRY>& aCumulative, const std::size_t aSegIdx, const double aT )
1230{
1231 const double segLength = static_cast<double>( aLengthDelayDetails.LengthsAndDelays[aSegIdx].first );
1232 const double segDelay = static_cast<double>( aLengthDelayDetails.LengthsAndDelays[aSegIdx].second );
1233
1234 // cumulative[i] is the cumulative length / delay at the end of the given segment index. Therefore, subtract
1235 // the not-included fraction of the track from the cumulative value at the segment end to get the length or
1236 // delay at the required fractional distance on the source segment
1237 const double partLen = segLength * ( 1.0 - aT );
1238 const double partDelay = segDelay * ( 1.0 - aT );
1239 return { aCumulative[aSegIdx].m_Length - static_cast<int64_t>( partLen ) + aPadDetails.StartPadLength,
1240 aCumulative[aSegIdx].m_Delay - static_cast<int64_t>( partDelay ) + aPadDetails.StartPadDelay };
1241}
1242
1243
1244std::vector<DIFF_PHASE_SKEW_TOOL::CUMULATIVE_ENTRY> DIFF_PHASE_SKEW_TOOL::buildCumulativeLengthsAndDelays(
1245 const std::vector<LENGTH_DELAY_CALCULATION_ITEM>& aItems, const LENGTH_DELAY_ITEM_DETAILS& aLengthDelayDetails,
1246 const PNS::SOLID* aStartPad, const PNS::SOLID* aEndPad, START_END_DETAILS& aStartEndDetails )
1247{
1248 wxASSERT( aItems.size() == aLengthDelayDetails.LengthsAndDelays.size() );
1249
1250 if( aLengthDelayDetails.LengthsAndDelays.empty() )
1251 return {};
1252
1253 std::vector<CUMULATIVE_ENTRY> cumulative;
1254 cumulative.reserve( aLengthDelayDetails.LengthsAndDelays.size() );
1255
1256 // Calculate start and end pad details
1257 aStartEndDetails.StartPadLength = aStartPad->GetPadToDie() + aLengthDelayDetails.InferredStartViaLength;
1258 aStartEndDetails.StartPadDelay = aStartPad->GetPadToDieDelay() + aLengthDelayDetails.InferredStartViaDelay;
1259 aStartEndDetails.EndPadLength = aEndPad->GetPadToDie() + aLengthDelayDetails.InferredEndViaLength;
1260 aStartEndDetails.EndPadDelay = aEndPad->GetPadToDieDelay() + aLengthDelayDetails.InferredEndViaDelay;
1261
1262 int64_t totalLength = 0;
1263 int64_t totalDelay = 0;
1264
1265 // Add track element details. Note that this adds the cumulative length at the *end*
1266 // of each track element to the cumulative vector.
1267 for( std::size_t i = 0; i < aItems.size(); ++i )
1268 {
1269 const auto [itemLen, itemDly] = aLengthDelayDetails.LengthsAndDelays[i];
1270 const LENGTH_DELAY_CALCULATION_ITEM& item = aItems[i];
1271
1272 VECTOR2I start, end;
1273
1274 if( item.Type() == LENGTH_DELAY_CALCULATION_ITEM::TYPE::LINE )
1275 {
1276 start = item.GetLine().CPoint( 0 );
1277 end = item.GetLine().CLastPoint();
1278 }
1279 else if( item.Type() == LENGTH_DELAY_CALCULATION_ITEM::TYPE::VIA )
1280 {
1281 start = item.GetVia()->GetPosition();
1282 end = start;
1283 }
1284
1285 totalLength += itemLen;
1286 totalDelay += itemDly;
1287 cumulative.emplace_back( totalLength, totalDelay, item.Type(), start, end );
1288 }
1289
1290 return cumulative;
1291}
1292
1293
1294void DIFF_PHASE_SKEW_TOOL::splitLengthItems( std::vector<LENGTH_DELAY_CALCULATION_ITEM>& aItems )
1295{
1296 std::vector<LENGTH_DELAY_CALCULATION_ITEM> splitItems;
1297
1298 auto makeLengthDelayItem = [&splitItems]( const SEG& aSeg, const LENGTH_DELAY_CALCULATION_ITEM& aSourceItem )
1299 {
1300 SHAPE_LINE_CHAIN newLine;
1301 newLine.Append( aSeg.A );
1302 newLine.Append( aSeg.B );
1303
1305 newItem.SetLine( newLine );
1306 newItem.SetWidth( aSourceItem.GetWidth() );
1307 newItem.SetLayers( aSourceItem.GetStartLayer() );
1308 newItem.SetEffectiveNetClass( aSourceItem.GetEffectiveNetClass() );
1309 splitItems.emplace_back( std::move( newItem ) );
1310 };
1311
1312 for( const auto& sourceItem : aItems )
1313 {
1314 // Only process lines
1315 if( sourceItem.Type() != LENGTH_DELAY_CALCULATION_ITEM::TYPE::LINE )
1316 {
1317 splitItems.emplace_back( sourceItem );
1318 continue;
1319 }
1320
1321 SHAPE_LINE_CHAIN& line = sourceItem.GetLine();
1322
1323 for( int segIdx = 0; segIdx < line.SegmentCount(); ++segIdx )
1324 {
1325 SEG seg = line.GetSegment( segIdx );
1326 makeLengthDelayItem( seg, sourceItem );
1327 }
1328 }
1329
1330 aItems = std::move( splitItems );
1331}
1332
1333
1335{
1336 wxString message;
1337 bool error = true;
1338
1339 switch( aDirection )
1340 {
1342 message = wxString::Format( _( "Net %s has multiple simulation electrical source pads" ),
1343 m_selectedNetinfo->GetShortNetname() );
1344 break;
1346 message = wxString::Format( _( "Net %s has multiple simulation electrical source pads" ),
1347 m_coupledNetinfo->GetShortNetname() );
1348 break;
1350 message = wxString::Format( _( "Differential pair %s / %s is missing start and / or end pads" ),
1351 m_selectedNetinfo->GetShortNetname(), m_coupledNetinfo->GetShortNetname() );
1352 break;
1354 message = wxString::Format( _( "Net %s is missing electrical simulation source pad" ),
1355 m_selectedNetinfo->GetShortNetname() );
1356 break;
1358 message = wxString::Format( _( "Net %s is missing electrical simulation source pad" ),
1359 m_coupledNetinfo->GetShortNetname() );
1360 break;
1361 default: error = false; break;
1362 }
1363
1364 if( error )
1365 m_frame->ShowInfoBarError( message, true );
1366
1367 return error;
1368}
1369
1370
1372{
1374 {
1375 const int pnsLayer = m_iface->GetPNSLayerFromBoardLayer( m_pickerItemFirst->GetLayer() );
1376
1377 PCB_TRACK* track = nullptr;
1379 wxCHECK( track, /* void */ );
1380
1381 // Determine primary and secondary net codes
1382 m_selectedNetcode = track->GetNetCode();
1384
1385 // Get the netcodes
1386 m_selectedNetinfo = m_board->GetNetInfo().GetNetItem( m_selectedNetcode );
1387 m_coupledNetinfo = m_board->GetNetInfo().GetNetItem( m_coupledNetcode );
1388
1389 VECTOR2I startSnapPoint;
1390 PNS::LINKED_ITEM* startItem =
1391 PNS::HELPERS::PickSegment( m_router, m_originFirst, pnsLayer, startSnapPoint, SHAPE_LINE_CHAIN() );
1392
1393 if( !startItem || !startItem->OfKind( PNS::ITEM::SEGMENT_T | PNS::ITEM::ARC_T ) )
1394 {
1395 m_frame->ShowInfoBarError( _( "Phase skew initial selection failed" ), true );
1396 return;
1397 }
1398
1399 PNS::NODE* world = m_router->GetWorld()->Branch();
1400 PNS::TOPOLOGY topo( world );
1401 PNS::DIFF_PAIR originPair;
1402
1403 m_selectedStartPad = nullptr;
1404 m_selectedEndPad = nullptr;
1405 m_coupledStartPad = nullptr;
1406 m_coupledEndPad = nullptr;
1407
1408 if( !topo.AssembleDiffPair( startItem, originPair ) )
1409 {
1410 m_frame->ShowInfoBarError( _( "Differential pair identification failed" ), true );
1411 return;
1412 }
1413
1414 if( !originPair.PLine().SegmentCount() || !originPair.NLine().SegmentCount() )
1415 return;
1416
1418 {
1422 &m_coupledEndPad );
1423 }
1424 else
1425 {
1427 &m_coupledEndPad );
1430 }
1431 }
1432 else
1433 {
1434 const int pnsLayerFirst = m_iface->GetPNSLayerFromBoardLayer( m_pickerItemFirst->GetLayer() );
1435 const int pnsLayerSecond = m_iface->GetPNSLayerFromBoardLayer( m_pickerItemSecond->GetLayer() );
1436
1437 PCB_TRACK* trackFirst = nullptr;
1439 wxCHECK( trackFirst, /* void */ );
1440
1441 PCB_TRACK* trackSecond = nullptr;
1443 wxCHECK( trackSecond, /* void */ );
1444
1445 // Determine primary and secondary net codes
1446 m_selectedNetcode = trackFirst->GetNetCode();
1447 m_coupledNetcode = trackSecond->GetNetCode();
1448
1449 // Get the netcodes
1450 m_selectedNetinfo = m_board->GetNetInfo().GetNetItem( m_selectedNetcode );
1451 m_coupledNetinfo = m_board->GetNetInfo().GetNetItem( m_coupledNetcode );
1452
1453 VECTOR2I startSnapPointFirst, startSnapPointSecond;
1454 PNS::LINKED_ITEM* startItemFirst = PNS::HELPERS::PickSegment( m_router, m_originFirst, pnsLayerFirst,
1455 startSnapPointFirst, SHAPE_LINE_CHAIN() );
1456 PNS::LINKED_ITEM* startItemSecond = PNS::HELPERS::PickSegment( m_router, m_originSecond, pnsLayerSecond,
1457 startSnapPointSecond, SHAPE_LINE_CHAIN() );
1458
1459 if( !startItemFirst || !startItemFirst->OfKind( PNS::ITEM::SEGMENT_T | PNS::ITEM::ARC_T ) || !startItemSecond
1460 || !startItemSecond->OfKind( PNS::ITEM::SEGMENT_T | PNS::ITEM::ARC_T ) )
1461 {
1462 m_frame->ShowInfoBarError( _( "Phase skew initial selection failed" ), true );
1463 return;
1464 }
1465
1466 PNS::NODE* world = m_router->GetWorld()->Branch();
1467 PNS::TOPOLOGY topo( world );
1468
1469 m_selectedStartPad = nullptr;
1470 m_selectedEndPad = nullptr;
1471 m_coupledStartPad = nullptr;
1472 m_coupledEndPad = nullptr;
1473
1476 }
1477
1478 // The router can return SHAPE_LINE_CHAINS that are in a reversed order. This doesn't play nicely
1479 // with our parallel / antiparallel rejection tests, therfore we need to ensure the SHAPE_LINE_CHAIN
1480 // points are in line order here
1483}
1484
1486{
1487 if( !aStartPad )
1488 return;
1489
1490 VECTOR2I pathSearchLoc = aStartPad->Pos();
1491
1492 for( int i = 0; i < aPath.Size(); ++i )
1493 {
1494 PNS::ITEM* curItem = aPath[i];
1495
1496 if( curItem->Kind() == PNS::ITEM::LINE_T )
1497 {
1498 PNS::LINE* lineItem = dynamic_cast<PNS::LINE*>( curItem );
1499
1500 if( !lineItem )
1501 continue;
1502
1503 SHAPE_LINE_CHAIN& line = lineItem->Line();
1504 const std::size_t numPoints = line.GetPointCount();
1505 wxASSERT( numPoints >= 2 );
1506
1507 if( line.GetPoint( numPoints - 1 ) == pathSearchLoc )
1508 line = line.Reverse();
1509
1510 pathSearchLoc = line.GetPoint( numPoints - 1 );
1511 }
1512 else if( curItem->Kind() == PNS::ITEM::VIA_T )
1513 {
1514 const PNS::VIA* viaItem = dynamic_cast<PNS::VIA*>( curItem );
1515 wxASSERT( viaItem->Pos() == pathSearchLoc );
1516 }
1517 }
1518}
1519
1520
1522{
1523 // This condition can occur if the route contains items that are not tracks (e.g. unconverted arcs)
1526
1527 PAD* selectedStartPad = static_cast<PAD*>( m_selectedStartPad->BoardItem() );
1528 PAD* selectedEndPad = static_cast<PAD*>( m_selectedEndPad->BoardItem() );
1529 PAD* coupledStartPad = static_cast<PAD*>( m_coupledStartPad->BoardItem() );
1530 PAD* coupledEndPad = static_cast<PAD*>( m_coupledEndPad->BoardItem() );
1531
1532 if( !selectedStartPad || !selectedEndPad || !coupledStartPad || !coupledEndPad )
1534
1535 // Normalise directions if possible
1536 if( selectedStartPad->GetSimElectricalType() != PAD_SIM_ELECTRICAL_TYPE::SOURCE
1538 {
1540 std::swap( selectedStartPad, selectedEndPad );
1541 }
1542
1545 {
1547 std::swap( coupledStartPad, coupledEndPad );
1548 }
1549
1550 // Both start pads must be sources
1551 if( selectedStartPad->GetSimElectricalType() != PAD_SIM_ELECTRICAL_TYPE::SOURCE )
1552 {
1554 }
1555
1556 if( coupledStartPad->GetSimElectricalType() != PAD_SIM_ELECTRICAL_TYPE::SOURCE )
1557 {
1559 }
1560
1561 // End pads can't be a source
1562 if( selectedEndPad->GetSimElectricalType() == PAD_SIM_ELECTRICAL_TYPE::SOURCE )
1564
1567
1569}
1570
1571
1573{
1574 PNS::ITEM_SET reversed;
1575
1576 for( auto itr = aPath.rbegin(); itr != aPath.rend(); ++itr )
1577 {
1578 if( ( *itr )->Kind() == PNS::ITEM::LINE_T )
1579 {
1580 PNS::LINE* l = dyn_cast<PNS::LINE*>( *itr );
1581 wxASSERT( l != nullptr );
1582
1583 if( l != nullptr )
1584 l->Reverse();
1585 }
1586
1587 reversed.Add( *itr );
1588 }
1589
1590 aPath = std::move( reversed );
1591
1592 // Finally reverse the start / end pads
1593 std::swap( *aStartPad, *aEndPad );
1594}
1595
1596
1598{
1599 constexpr std::size_t maxIdx = std::numeric_limits<std::size_t>::max();
1600 const double hitTestDistance = m_view->ToWorld( DETAILS_HOVER_HITTEST_THRESHOLD_PIXELS );
1601 const auto [selectedIdx, isSelectedTrack] = getNearestDiffSegments( m_cursorPos, hitTestDistance );
1602
1603 if( selectedIdx < maxIdx )
1604 {
1605 if( isSelectedTrack )
1606 m_segmentForStatisticsDisplay = { selectedIdx, true };
1607 else
1608 m_segmentForStatisticsDisplay = { selectedIdx, false };
1609 }
1610 else
1611 {
1612 m_segmentForStatisticsDisplay = { maxIdx, false };
1613 }
1614
1616}
1617
1618
1619std::pair<std::size_t, bool> DIFF_PHASE_SKEW_TOOL::getNearestDiffSegments( const VECTOR2D& aCursorPos,
1620 const double aHitTestDistance ) const
1621{
1622 auto getNearestSegment = [&aCursorPos, aHitTestDistance]( const std::vector<OUTPUT_SEGMENT>& segments )
1623 {
1624 const double maxDistSq = aHitTestDistance * aHitTestDistance;
1625
1626 std::size_t bestIndex = std::numeric_limits<std::size_t>::max();
1627 double bestDistSq = maxDistSq;
1628
1629 for( size_t i = 0; i < segments.size(); ++i )
1630 {
1631 const auto& seg = segments[i];
1632
1633 VECTOR2D mid = ( seg.Start + seg.End ) / 2.0;
1634 const double distSq = ( aCursorPos - mid ).SquaredEuclideanNorm();
1635
1636 if( distSq <= bestDistSq )
1637 {
1638 bestDistSq = distSq;
1639 bestIndex = i;
1640 }
1641 }
1642
1643 return std::pair<std::size_t, double>( bestIndex, bestDistSq );
1644 };
1645
1646 const auto [selectedIdx, selectedDist] = getNearestSegment( m_selectedDiffs );
1647 const auto [coupledIdx, coupledDist] = getNearestSegment( m_coupledDiffs );
1648
1649 constexpr std::size_t maxIdx = std::numeric_limits<std::size_t>::max();
1650
1651 if( selectedIdx != maxIdx && coupledIdx != maxIdx )
1652 {
1653 if( selectedDist <= coupledDist )
1654 return { selectedIdx, true };
1655
1656 return { coupledIdx, false };
1657 }
1658
1659 if( selectedIdx != maxIdx )
1660 return { selectedIdx, true };
1661
1662 if( coupledIdx != maxIdx )
1663 return { coupledIdx, false };
1664
1665 return { maxIdx, true };
1666}
1667
1668
1670{
1671 RENDER_SETTINGS* renderSettings = m_frame->GetCanvas()->GetView()->GetPainter()->GetSettings();
1672 renderSettings->SetHighlight( false );
1673
1674 if( m_pickerItemFirst )
1675 {
1676 renderSettings->SetHighlight( true, m_netcodeP, true );
1677
1679 renderSettings->SetHighlight( true, m_netcodeN, true );
1680 }
1681
1682 if( m_pickerItemSecond )
1683 renderSettings->SetHighlight( true, m_netcodeN, true );
1684
1685 m_frame->GetCanvas()->GetView()->UpdateAllLayersColor();
1686
1687 if( aRefresh )
1688 m_frame->GetCanvas()->Refresh();
1689}
1690
1691
1693{
1694 // clang-format off
1696 // clang-format on
1697}
1698
1699
1701{
1702 if( !m_viewOverlay )
1703 {
1704 m_viewOverlay = m_view->MakeOverlay();
1705 m_view->Add( m_viewOverlay.get() );
1706 }
1707}
1708
1709
1711{
1712 if( m_viewOverlay )
1713 {
1714 m_viewOverlay->Clear();
1715 updateOverlay();
1716 }
1717}
1718
1719
1721{
1722 if( m_viewOverlay )
1723 {
1724 m_view->Update( m_viewOverlay.get() );
1725 }
1726}
1727
1728
1730{
1731 m_pickerItemFirst = nullptr;
1732 m_pickerItemSecond = nullptr;
1734 m_netcodeP = 0;
1735 m_netcodeN = 0;
1736 m_originFirst = { 0, 0 };
1737 m_originSecond = { 0, 0 };
1738 m_timeDomain = false;
1740 m_coupledNetcode = 0;
1741 m_selectedNetinfo = nullptr;
1742 m_coupledNetinfo = nullptr;
1743 m_selectedPath.Clear();
1744 m_coupledPath.Clear();
1745 m_selectedStartPad = nullptr;
1746 m_selectedEndPad = nullptr;
1747 m_coupledStartPad = nullptr;
1748 m_coupledEndPad = nullptr;
1751 m_selectedLengthDelayDetails.LengthsAndDelays.clear();
1752 m_coupledLengthDelayDetails.LengthsAndDelays.clear();
1753 m_selectedDiffs.clear();
1754 m_coupledDiffs.clear();
1755 m_segmentForStatisticsDisplay = { std::numeric_limits<std::size_t>::max(), false };
1756 m_maxSkew.reset();
1757}
int index
@ NORMAL
Inactive layers are shown normally (no high-contrast mode)
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:982
static TOOL_ACTION cursorClick
Definition actions.h:176
static TOOL_ACTION refreshPreview
Definition actions.h:155
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)
Set the current mode of the tool.
void setTransitions() override
Set up handlers for tool events.
void updateMessagePanel() const
Updates the message panel.
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
DIFF_PAIR_VALIDITY determinePathDirections()
Determine which end of the extracted paths we are defining as the signal start point.
bool reportValidityErrors(DIFF_PAIR_VALIDITY aDirection) const
Report to the user any errors after determining the signal direction.
START_END_DETAILS m_coupledStartEndDetails
LENGTH_DELAY_ITEM_DETAILS m_coupledLengthDelayDetails
static void reversePath(PNS::ITEM_SET &aPath, PNS::SOLID **aStartPad, PNS::SOLID **aEndPad)
Reverses the direction of the selected and coupled paths (including swapping start / end pads.
static VECTOR2D lerp(const VECTOR2D aA, const VECTOR2D aB, const double aT)
Linear interpolate from point A to B at line fraction T.
std::pair< std::size_t, bool > m_segmentForStatisticsDisplay
static void normalisePathItems(const PNS::ITEM_SET &aPath, const PNS::SOLID *aStartPad)
Normalises the path to ensure SHAPE_LINE_CHAIN points are in overall path walk order.
int getMaxDiffPairGap(const BOARD_CONNECTED_ITEM *aItem) const
Gets the maximum diff pair gap for the given item, taken from DRC rules.
static std::vector< double > buildSplitPositions(const std::vector< CUMULATIVE_ENTRY > &aSegments, double aTargetSubsegmentSize)
Determines where to apply overlay segment subsections on the source segments.
MODE GetMode() const
Return the current mode of the tool.
void clearOverlay() const
Clears the VIEW_OVERLAY.
std::vector< KNOWN_RELATIVE_POINT > m_selectedKnownPoints
std::vector< LENGTH_DELAY_CALCULATION_ITEM > m_selectedLengthDelayItems
void doDisplayOverlay()
Display the phase overlay for the current hover item.
bool Init() override
Init() is called once upon a registration of the tool.
DIFF_PAIR_VALIDITY
Flags for the analysis state of the selected tracks.
std::vector< CUMULATIVE_ENTRY > m_selectedCumulative
void getNetPaths()
Use the router to get the +ve and -ve paths from the selected item.
BOARD_CONNECTED_ITEM * m_pickerItemFirst
KIGFX::VIEW_CONTROLS * m_controls
static void splitLengthItems(std::vector< LENGTH_DELAY_CALCULATION_ITEM > &aItems)
Splits the calculation items from compound segments in to individual items.
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
Builds the length / delay calculation items from a given path.
void doInitialHover(const PCB_SELECTION_TOOL *aSelectionTool, GENERAL_COLLECTORS_GUIDE aGuide)
Handle hover events before a DP pair is selected.
PCB_BASE_EDIT_FRAME * m_frame
int ShowDiffPhaseSkew(const TOOL_EVENT &aEvent)
The tool entry point.
void doShowStatsAtCursor()
Shows the diff stats nearest the cursor.
std::vector< OUTPUT_SEGMENT > m_coupledDiffs
COLOR4D interpolateColours(const COLOR4D &aColour1, const COLOR4D &aColour2, double aS, bool aUseLogScale) const
Linearly interpolates between colour1 and colour2, with interpolation point given by aS [0-1].
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)
Builds a vector in which each entry represents the cumulative length and delay at the start of a give...
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
Finds all parallel segment runs in the selected and coupled tracks within the given segment ranges an...
std::pair< std::size_t, bool > getNearestDiffSegments(const VECTOR2D &aCursorPos, double aHitTestDistance) const
Determines the nearest points to the cursor from the diff segments.
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)
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 cumulative length and delay at the given fractional coordinate in the given segment.
static std::pair< VECTOR2D, std::size_t > pointAtDistance(const std::vector< CUMULATIVE_ENTRY > &aSegments, const std::vector< LENGTH_DELAY_CALCULATION_ITEM > &aSourceItemDetails, double aDist)
Returns the coordinate at the given linear distance along the line, along with the segment index the ...
void updateOverlay() const
Refreshes the VIEW_OVERLAY in the active VIEW.
void buildDiffOverlaySegments(double aTargetSubsegmentSize)
Builds the final overlay output segments for plotting.
std::optional< int > m_maxSkew
std::shared_ptr< KIGFX::VIEW_OVERLAY > m_viewOverlay
void buildKnownRelativePoints(const std::vector< PARALLEL_RUN > &aKnownRuns)
Builds a vector of known relative skew points on each track.
PNS::SIZES_SETTINGS m_savedSizes
void getOverlay()
Ensures we have an active VIEW_OVERLAY to display the diff graphics.
BOARD_CONNECTED_ITEM * m_pickerItemSecond
void updateNetHighlights(bool aRefresh=true) const
Highlight nets when in MODE::HOVER and we have active nets in scope.
void resetStateVariables()
Resets all select-specific variables.
std::vector< PARALLEL_RUN > findParallelRuns() const
Finds all parallel segment runs in the selected and coupled tracks.
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
Draws the visual skew overlay.
std::shared_ptr< DRC_ENGINE > m_drcEngine
SEVERITY GetSeverity() const
Definition drc_rule.h:181
const MINOPTMAX< int > & GetValue() const
Definition drc_rule.h:129
bool IsNull() const
Definition drc_rule.h:122
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:387
void SetIncludeSecondary(bool include)
Definition collectors.h:401
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:391
double g
Green component.
Definition color4d.h:392
double a
Alpha component.
Definition color4d.h:394
double b
Blue component.
Definition color4d.h:393
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:38
T Max() const
Definition minoptmax.h:30
T Opt() const
Definition minoptmax.h:31
bool HasOpt() const
Definition minoptmax.h:39
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:573
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:603
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()
Modifiable accessor to the underlying shape.
Definition pns_line.h:148
int SegmentCount() const
Definition pns_line.h:151
void Reverse()
Reverse the point/vertex order.
Keep the router "world" - i.e.
Definition pns_node.h:244
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
Return the instance of KIGFX::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:281
constexpr extended_type Dot(const VECTOR2< T > &aVector) const
Compute dot product of self with aVector.
Definition vector2d.h:567
@ ARROW
Definition cursors.h:43
#define INITIAL_HOVER_HITTEST_THRESHOLD_PIXELS
#define DETAILS_HOVER_HITTEST_THRESHOLD_PIXELS
constexpr double EPS
Floating point comparison epsilon.
@ DIFF_PAIR_GAP_CONSTRAINT
#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.
std::string message
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:437
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.
Struct to represent one cumulative length and delay point.
Struct containing a final computed output diff segment.
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.
Start and end pad lengths and delays (pad-to-die + inferred via-in-pad)
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:708
VECTOR2< double > VECTOR2D
Definition vector2d.h:707