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