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 TOOL_EVENT pushedEvent = aEvent;
128 m_frame->PushTool( aEvent );
129 Activate();
130
131 // Must be done after Activate() so that it gets set into the correct context
133 // controls->ShowCursor( true );
134 // controls->ForceCursorPosition( false );
135
136 // Set initial cursor
137 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::TUNE );
138
139 // Get the required tools and helpers
140 PCB_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
141 GENERAL_COLLECTORS_GUIDE guide = m_frame->GetCollectorsGuide();
142
143 // Create the VIEW_OVERLAY
144 getOverlay();
145
146 m_pickerItemFirst = nullptr;
147
148 if( aEvent.HasPosition() )
149 m_toolMgr->PrimeTool( aEvent.Position() );
150
151 // Main loop: keep receiving events
152 while( TOOL_EVENT* evt = Wait() )
153 {
154 m_cursorPos = controls->GetMousePosition();
155
156 if( evt->IsCancelInteractive() || evt->IsActivate() )
157 {
158 // Roll back our mode, or exit if we are in the initial hover mode
160 {
161 m_pickerItemFirst = nullptr;
163 m_pickerItemSecond = nullptr;
164 clearOverlay();
166 m_maxSkew.reset();
168 }
169 else
170 {
172 break;
173 }
174 }
175
176 if( evt->IsMotion() )
177 {
178 if( GetMode() == MODE::HOVER )
179 {
180 m_pickerItemFirst = nullptr;
181 m_pickerItemSecond = nullptr;
183 doInitialHover( selectionTool, guide );
185 }
186 else if( GetMode() == MODE::SELECTED_FIRST )
187 {
188 m_pickerItemSecond = nullptr;
189 doInitialHover( selectionTool, guide );
191 }
192 else if( GetMode() == MODE::FIXED )
193 {
196 }
197 }
198 else if( evt->IsClick( BUT_LEFT ) && GetMode() == MODE::HOVER && m_pickerItemFirst )
199 {
201
203 {
206 }
207 else
208 {
210 }
211
213 }
214 else if( evt->IsClick( BUT_LEFT ) && GetMode() == MODE::SELECTED_FIRST && m_pickerItemSecond )
215 {
216 // First click to select the diff pair for inspection
221 }
222 else if( evt->IsAction( &PCB_ACTIONS::properties ) )
223 {
225 PCBNEW_SETTINGS* cfg = m_frame->GetPcbNewSettings();
226 settings = cfg->m_DiffPhaseSkewSettings;
227
229
230 if( dlg.ShowModal() == wxID_OK )
231 {
232 cfg->m_DiffPhaseSkewSettings = settings;
233
234 if( GetMode() == MODE::FIXED )
236 }
237 }
238 }
239
240 // Restore UI state
241 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
242
243 // Reset tool state
246
247 updateNetHighlights( false );
248 m_frame->GetCanvas()->Refresh();
249
250 // Done
251 m_frame->PopTool( aEvent );
252
253 return 0;
254}
255
256
258{
259 GENERAL_COLLECTOR collector;
261
262 if( m_frame->GetDisplayOptions().m_ContrastModeDisplay != HIGH_CONTRAST_MODE::NORMAL )
263 aGuide.SetIncludeSecondary( false );
264 else
265 aGuide.SetIncludeSecondary( true );
266
267 aGuide.SetPreferredLayer( m_frame->GetActiveLayer() );
268 collector.Collect( m_board, { PCB_TRACE_T, PCB_ARC_T }, m_cursorPos, aGuide );
269
270 if( collector.GetCount() > 1 )
271 aSelectionTool->GuessSelectionCandidates( collector, m_cursorPos );
272
273 if( collector.GetCount() > 0 )
274 {
275 double min_dist_sq = std::numeric_limits<double>::max();
276
277 for( EDA_ITEM* candidate : collector )
278 {
279 VECTOR2I candidatePos;
280
281 if( candidate->Type() == PCB_TRACE_T )
282 candidatePos = static_cast<PCB_TRACK*>( candidate )->GetCenter();
283 else if( candidate->Type() == PCB_ARC_T )
284 candidatePos = static_cast<PCB_ARC*>( candidate )->GetMid();
285
286 const double dist_sq = ( m_cursorPos - candidatePos ).SquaredEuclideanNorm();
287
288 if( dist_sq < min_dist_sq )
289 {
290 const auto bci = static_cast<BOARD_CONNECTED_ITEM*>( candidate );
291 const NETINFO_ITEM* candidateNet = bci->GetNet();
292
293 if( GetMode() == MODE::HOVER )
294 {
295 min_dist_sq = dist_sq;
296
297 // We only accept diff pairs in initial hover mode
298 const bool isDiffPairItem =
299 m_drcEngine->IsNetADiffPair( m_board, candidateNet, m_netcodeP, m_netcodeN );
300 m_pickerItemFirst = static_cast<BOARD_CONNECTED_ITEM*>( candidate );
301
302 if( isDiffPairItem )
303 {
305 }
306 else
307 {
309 m_netcodeP = candidateNet->GetNetCode();
310 }
311 }
312 else if( GetMode() == MODE::SELECTED_FIRST )
313 {
314 int fakeNCP, fakeNCN;
315
316 // Reject diff pairs here as we have a not-diff-pair selected
317 const bool isDiffPairItem = m_drcEngine->IsNetADiffPair( m_board, candidateNet, fakeNCP, fakeNCN );
318
319 auto existingBci = static_cast<BOARD_CONNECTED_ITEM*>( m_pickerItemFirst );
320 const NETINFO_ITEM* existingNet = existingBci->GetNet();
321
322 if( !isDiffPairItem && candidateNet != existingNet )
323 {
324 min_dist_sq = dist_sq;
325 m_pickerItemSecond = static_cast<BOARD_CONNECTED_ITEM*>( candidate );
326 m_netcodeN = candidateNet->GetNetCode();
327 }
328 }
329 }
330 }
331 }
332
334}
335
336
338 const PNS::SOLID* aEndPad, const NETINFO_ITEM* aNet,
339 std::vector<LENGTH_DELAY_CALCULATION_ITEM>& aItems,
340 LENGTH_DELAY_ITEM_DETAILS& aItemDetails ) const
341{
342 if( aPath.Size() == 0 )
343 return;
344
345 // Convert path to length / delay interface types
346 aItems = m_iface->GetLengthDelayCalculationItems( aPath, aNet->GetNetClass() );
347 wxASSERT( aItems.size() == static_cast<size_t>( aPath.Size() ) );
348
349 // The router returns compound lines - we need to split them in to their constituent segments / arcs
350 splitLengthItems( aItems );
351
352 // Get the per-item length / delay statistics
353 constexpr PATH_OPTIMISATIONS opts = {
354 .OptimiseVias = false, .MergeTracks = false, .OptimiseTracesInPads = false, .InferViaInPad = true
355 };
356
357 const PAD* startPad = dynamic_cast<PAD*>( aStartPad->BoardItem() );
358 const PAD* endPad = dynamic_cast<PAD*>( aEndPad->BoardItem() );
359 aItemDetails = LENGTH_DELAY_ITEM_DETAILS{};
360 m_board->GetLengthCalculation()->CalculateLengthDetails(
361 aItems, opts, startPad, endPad, LENGTH_DELAY_LAYER_OPT::NO_LAYER_DETAIL,
363 wxASSERT( aItemDetails.LengthsAndDelays.size() == aItems.size() );
364}
365
366
368{
369 // Extract the diff pair net paths
370 getNetPaths();
371
372 // Determine what we are defining as the 'start' of the net
374
375 if( reportValidityErrors( direction ) )
376 {
378 return;
379 }
380
381 m_timeDomain = false;
382
383 // Check if both nets have time domain parameters
384 if( m_selectedNetinfo->GetNetClass()->HasTuningProfile() && m_coupledNetinfo->GetNetClass()->HasTuningProfile() )
385 {
386 wxString selectedTuningProfileName = m_selectedNetinfo->GetNetClass()->GetTuningProfile();
387 wxString coupledTuningProfileName = m_coupledNetinfo->GetNetClass()->GetTuningProfile();
388
389 std::shared_ptr<TUNING_PROFILES> tuningParams = m_frame->Prj().GetProjectFile().TuningProfileParameters();
390 const TUNING_PROFILE& selectedTuningProfile = tuningParams->GetTuningProfile( selectedTuningProfileName );
391 const TUNING_PROFILE& coupledTuningProfile = tuningParams->GetTuningProfile( coupledTuningProfileName );
392
393 if( selectedTuningProfile.m_EnableTimeDomainTuning && coupledTuningProfile.m_EnableTimeDomainTuning )
394 m_timeDomain = true;
395 }
396
397 // Construct the length / delay calculation items
400
403
404 // Build the cumulative length / delay structures
411
412 // Walk the two tracks and construct the localised phase differences
413 const std::vector<PARALLEL_RUN> parallelRuns = findParallelRuns();
414
415 // Build the known delay reference points for each track
416 buildKnownRelativePoints( parallelRuns );
417
418 m_maxSkew.reset();
420
421 // Finally draw the overlay
423}
424
425
426void DIFF_PHASE_SKEW_TOOL::buildKnownRelativePoints( const std::vector<PARALLEL_RUN>& aKnownRuns )
427{
428 m_selectedKnownPoints.clear();
429 m_coupledKnownPoints.clear();
430
431 const double padLenDiff =
432 static_cast<double>( m_selectedStartEndDetails.StartPadLength - m_coupledStartEndDetails.StartPadLength );
433 const double padDelayDiff =
434 static_cast<double>( m_selectedStartEndDetails.StartPadDelay - m_coupledStartEndDetails.StartPadDelay );
435
436 struct RELATIVE_PAIR
437 {
438 double len;
439 double delay;
440 };
441
442 auto opposite = []( const RELATIVE_PAIR& a )
443 {
444 return RELATIVE_PAIR{ -a.len, -a.delay };
445 };
446
447 for( const auto& r : aKnownRuns )
448 {
449 const std::size_t segIdxA = r.segA;
450 const std::size_t segIdxB = r.segB;
451
452 const double segStartA = segIdxA == 0 ? 0.0 : static_cast<double>( m_selectedCumulative[segIdxA - 1].m_Length );
453 const double segStartB = segIdxB == 0 ? 0.0 : static_cast<double>( m_coupledCumulative[segIdxB - 1].m_Length );
454 const double segLenA = static_cast<double>( m_selectedLengthDelayDetails.LengthsAndDelays[segIdxA].first );
455 const double segLenB = static_cast<double>( m_coupledLengthDelayDetails.LengthsAndDelays[segIdxB].first );
456
457 const double s0A = segStartA + r.ta0 * segLenA;
458 const double s1A = segStartA + r.ta1 * segLenA;
459 const double s0B = segStartB + r.tb0 * segLenB;
460 const double s1B = segStartB + r.tb1 * segLenB;
461
462 const RELATIVE_PAIR startRel{ ( r.startLenA - r.startLenB ) + padLenDiff,
463 ( r.startDelayA - r.startDelayB ) + padDelayDiff };
464 const RELATIVE_PAIR endRel{ ( r.endLenA - r.endLenB ) + padLenDiff,
465 ( r.endDelayA - r.endDelayB ) + padDelayDiff };
466 const RELATIVE_PAIR startRelB = opposite( startRel );
467 const RELATIVE_PAIR endRelB = opposite( endRel );
468
469 const bool hasStartViaA = r.startViaLengthA.has_value();
470 const bool hasEndViaA = r.endViaLengthA.has_value();
471 const bool hasStartViaB = r.startViaLengthB.has_value();
472 const bool hasEndViaB = r.endViaLengthB.has_value();
473
474 const double startViaLenA = r.startViaLengthA.value_or( 0.0 );
475 const double endViaLenA = r.endViaLengthA.value_or( 0.0 );
476 const double startViaLenB = r.startViaLengthB.value_or( 0.0 );
477 const double endViaLenB = r.endViaLengthB.value_or( 0.0 );
478
479 const double startViaDelayA = r.startViaDelayA.value_or( 0.0 );
480 const double endViaDelayA = r.endViaDelayA.value_or( 0.0 );
481 const double startViaDelayB = r.startViaDelayB.value_or( 0.0 );
482 const double endViaDelayB = r.endViaDelayB.value_or( 0.0 );
483
484 // Injected start-via points
485 if( hasStartViaA && hasStartViaB )
486 {
487 const RELATIVE_PAIR rel{ startRel.len - startViaLenA + startViaLenB,
488 startRel.delay - startViaDelayA + startViaDelayB };
489 const RELATIVE_PAIR relB = opposite( rel );
490 m_selectedKnownPoints.push_back( { s0A - startViaLenA, rel.len, rel.delay, rel.len, rel.delay } );
491 m_coupledKnownPoints.push_back( { s0B - startViaLenB, relB.len, relB.delay, relB.len, relB.delay } );
492 }
493
494 // Maybe-modified values used for interpolation around via discontinuities
495 RELATIVE_PAIR startBeforeA = startRel;
496 RELATIVE_PAIR startBeforeB = startRelB;
497
498 if( hasStartViaA && !hasStartViaB )
499 {
500 startBeforeB.len += startViaLenA;
501 startBeforeB.delay += startViaDelayA;
502 m_selectedKnownPoints.push_back( { s0A - startViaLenA, startRel.len - startViaLenA,
503 startRel.delay - startViaDelayA, startRel.len - startViaLenA,
504 startRel.delay - startViaDelayA } );
505 }
506 else if( !hasStartViaA && hasStartViaB )
507 {
508 startBeforeA.len += startViaLenB;
509 startBeforeA.delay += startViaDelayB;
510 m_coupledKnownPoints.push_back( { s0B - startViaLenB, startRelB.len - startViaLenB,
511 startRelB.delay - startViaDelayB, startRelB.len - startViaLenB,
512 startRelB.delay - startViaDelayB } );
513 }
514
515 // Maybe-modified values used for interpolation around via discontinuities
516 RELATIVE_PAIR endAfterA = endRel;
517 RELATIVE_PAIR endAfterB = endRelB;
518
519 if( hasEndViaA && !hasEndViaB )
520 {
521 endAfterB.len -= endViaLenA;
522 endAfterB.delay -= endViaDelayA;
523 }
524 else if( !hasEndViaA && hasEndViaB )
525 {
526 endAfterA.len -= endViaLenB;
527 endAfterA.delay -= endViaDelayB;
528 }
529
530 // Main known points
531 m_selectedKnownPoints.push_back( { s0A, startBeforeA.len, startBeforeA.delay, startRel.len, startRel.delay } );
532 m_selectedKnownPoints.push_back( { s1A, endRel.len, endRel.delay, endAfterA.len, endAfterA.delay } );
533 m_coupledKnownPoints.push_back( { s0B, startBeforeB.len, startBeforeB.delay, startRelB.len, startRelB.delay } );
534 m_coupledKnownPoints.push_back( { s1B, endRelB.len, endRelB.delay, endAfterB.len, endAfterB.delay } );
535
536 // Injected end-via points
537 if( hasEndViaA && hasEndViaB )
538 {
539 const RELATIVE_PAIR rel{ endRel.len + endViaLenA - endViaLenB, endRel.delay + endViaDelayA - endViaDelayB };
540 const RELATIVE_PAIR relB = opposite( rel );
541 m_selectedKnownPoints.push_back( { s1A + endViaLenA, rel.len, rel.delay, rel.len, rel.delay } );
542 m_coupledKnownPoints.push_back( { s1B + endViaLenB, relB.len, relB.delay, relB.len, relB.delay } );
543 }
544 else if( hasEndViaA && !hasEndViaB )
545 {
546 m_selectedKnownPoints.push_back( { s1A + endViaLenA, endRel.len + endViaLenA, endRel.delay + endViaDelayA,
547 endRel.len + endViaLenA, endRel.delay + endViaDelayA } );
548 }
549 else if( !hasEndViaA && hasEndViaB )
550 {
551 m_coupledKnownPoints.push_back( { s1B + endViaLenB, endRelB.len + endViaLenB, endRelB.delay + endViaDelayB,
552 endRelB.len + endViaLenB, endRelB.delay + endViaDelayB } );
553 }
554 }
555}
556
557
558std::vector<double> DIFF_PHASE_SKEW_TOOL::buildSplitPositions( const std::vector<CUMULATIVE_ENTRY>& aSegments,
559 const double aTargetSubsegmentSize )
560{
561 if( aSegments.empty() )
562 return {};
563
564 std::vector<double> splits;
565
566 // Start of line
567 if( aSegments[0].m_SourceType == LENGTH_DELAY_CALCULATION_ITEM::TYPE::LINE )
568 splits.push_back( 0.0 );
569
570 double currentDistance = 0;
571
572 for( const CUMULATIVE_ENTRY& seg : aSegments )
573 {
574 const double segStart = currentDistance;
575 const double segEnd = seg.m_Length;
576 currentDistance = segEnd;
577
578 // Only emit segments for lines
579 if( seg.m_SourceType != LENGTH_DELAY_CALCULATION_ITEM::TYPE::LINE )
580 continue;
581
582 // Fixed subdivision spacing
583 for( double s = segStart; s < segEnd; s += aTargetSubsegmentSize )
584 {
585 splits.push_back( s );
586 }
587
588 splits.push_back( segEnd );
589 }
590
591 // Sort and make unique
592 std::ranges::sort( splits );
593
594 splits.erase( std::ranges::unique( splits,
595 []( const double a, const double b )
596 {
597 return std::abs( a - b ) < EPS;
598 } )
599 .begin(),
600 splits.end() );
601
602 return splits;
603}
604
605
606std::pair<VECTOR2D, std::size_t>
607DIFF_PHASE_SKEW_TOOL::pointAtDistance( const std::vector<CUMULATIVE_ENTRY>& aSegments,
608 const std::vector<LENGTH_DELAY_CALCULATION_ITEM>& aSourceItemDetails,
609 const double aDist )
610{
611 for( std::size_t i = 0; i < aSegments.size(); ++i )
612 {
613 const double segStart = i == 0 ? 0.0 : static_cast<double>( aSegments[i - 1].m_Length );
614 const double segEnd = static_cast<double>( aSegments[i].m_Length );
615
616 if( aDist <= segEnd + EPS )
617 {
618 const double segLen = segEnd - segStart;
619
620 double t = 0.0;
621
622 if( segLen > EPS )
623 t = ( aDist - segStart ) / segLen;
624
625 t = std::clamp( t, 0.0, 1.0 );
626
627 if( aSourceItemDetails[i].Type() == LENGTH_DELAY_CALCULATION_ITEM::TYPE::VIA )
628 {
629 // We've hit a via - use the end point from the previous line
630 wxASSERT( i > 0 );
631 wxASSERT( aSourceItemDetails[i - 1].Type() == LENGTH_DELAY_CALCULATION_ITEM::TYPE::LINE );
632 wxASSERT( aSourceItemDetails[i - 1].GetLine().CPoints().size() == 2 );
633 return { aSourceItemDetails[i - 1].GetLine().CPoints()[1], i - 1 };
634 }
635
636 wxASSERT( aSourceItemDetails[i].Type() == LENGTH_DELAY_CALCULATION_ITEM::TYPE::LINE );
637 wxASSERT( aSourceItemDetails[i].GetLine().CPoints().size() == 2 );
638
639 return {
640 lerp( aSourceItemDetails[i].GetLine().CPoints()[0], aSourceItemDetails[i].GetLine().CPoints()[1], t ), i
641 };
642 }
643 }
644
645 // We shouldn't reach this point...
646 wxASSERT( false );
647 return { { 0.0, 0.0 }, 0 };
648}
649
650
651COLOR4D DIFF_PHASE_SKEW_TOOL::interpolateColours( const COLOR4D& aColour1, const COLOR4D& aColour2, double aS,
652 const bool aUseLogScale ) const
653{
654 auto lerp = []( const double d1, const double d2, const double s )
655 {
656 return d1 + s * ( d2 - d1 );
657 };
658
659 if( aUseLogScale )
660 aS = std::log( 1.0 + m_colourInterpolationLogStrength * aS )
661 / std::log( 1.0 + m_colourInterpolationLogStrength );
662
663 const double r = std::clamp( lerp( aColour1.r, aColour2.r, aS ), 0.0, 1.0 );
664 const double g = std::clamp( lerp( aColour1.g, aColour2.g, aS ), 0.0, 1.0 );
665 const double b = std::clamp( lerp( aColour1.b, aColour2.b, aS ), 0.0, 1.0 );
666 const double a = std::clamp( lerp( aColour1.a, aColour2.a, aS ), 0.0, 1.0 );
667
668 return COLOR4D( r, g, b, a );
669}
670
671
679
680
681std::vector<DIFF_PHASE_SKEW_TOOL::OUTPUT_SEGMENT> DIFF_PHASE_SKEW_TOOL::buildDiffOverlaySegmentsImpl(
682 const std::vector<CUMULATIVE_ENTRY>& aSegments,
683 const std::vector<LENGTH_DELAY_CALCULATION_ITEM>& aSourceItemDetails,
684 const std::vector<KNOWN_RELATIVE_POINT>& aKnownPoints, double aTargetSubsegmentSize )
685{
686 std::vector<OUTPUT_SEGMENT> result;
687
688 if( aSegments.empty() )
689 return result;
690
691 PCBNEW_SETTINGS* cfg = m_frame->GetPcbNewSettings();
693
694 // Get min and max values
695 double minLen = 0.0;
696 double maxLen = 0.0;
697 double minDelay = 0.0;
698 double maxDelay = 0.0;
699
700 for( const auto& [_1, relLenBefore, relDelayBefore, relLenAfter, relDelayAfter] : aKnownPoints )
701 {
702 minLen = std::min( minLen, std::min( relLenBefore, relLenAfter ) );
703 maxLen = std::max( maxLen, std::max( relLenBefore, relLenAfter ) );
704 minDelay = std::min( minDelay, std::min( relDelayBefore, relDelayAfter ) );
705 maxDelay = std::max( maxDelay, std::max( relDelayBefore, relDelayAfter ) );
706 }
707
708 int maxSkew = m_maxSkew.value_or( 0 );
709
710 if( m_timeDomain )
711 m_maxSkew = std::max( static_cast<int>( std::round( maxDelay / 10 ) * 10 ), maxSkew );
712 else
713 m_maxSkew = std::max( static_cast<int>( std::round( maxLen / 10 ) * 10 ), maxSkew );
714
715 // Build all subdivision boundaries
716 const auto splits = buildSplitPositions( aSegments, aTargetSubsegmentSize );
717
718 KnownValueInterpolator interp( aKnownPoints );
719
720 // Emit subdivided segments
721 for( std::size_t i = 0; i + 1 < splits.size(); ++i )
722 {
723 const double s0 = splits[i];
724 const double s1 = splits[i + 1];
725
726 // Skip degenerate intervals
727 if( s1 - s0 <= EPS )
728 continue;
729
730 OUTPUT_SEGMENT out;
731
732 auto [startPoint, segIdx] = pointAtDistance( aSegments, aSourceItemDetails, s0 );
733 out.Width = static_cast<int>( aSourceItemDetails[segIdx].GetWidth() * m_overlayTrackInflation );
734 out.Start = startPoint;
735 auto [endPoint, _] = pointAtDistance( aSegments, aSourceItemDetails, s1 );
736 out.End = endPoint;
737
738 const double sMid = ( s0 + s1 ) / 2.0;
739
740 const std::optional<std::pair<double, double>> knownInterp = interp.ValueAt( sMid );
741 out.RelativeValueKnown = knownInterp.has_value();
742
743 double min = 0.0;
744 double max = 0.0;
745
746 if( m_timeDomain )
747 {
748 out.RelativeValueAtMid = knownInterp.value_or( std::pair<double, double>{ 0.0, 0.0 } ).second;
749 min = minDelay;
750 max = maxDelay;
751 }
752 else
753 {
754 out.RelativeValueAtMid = knownInterp.value_or( std::pair<double, double>{ 0.0, 0.0 } ).first;
755 min = minLen;
756 max = maxLen;
757 }
758
759 // Round value to nearest 10 IU to reduce low-level colour jitter on equal tracks
760 out.RelativeValueAtMid = std::round( out.RelativeValueAtMid / 10 ) * 10;
761
762 // Calculate colour value
763 if( !out.RelativeValueKnown )
764 {
765 out.Colour = settings.m_UnknownSkewColor;
766 }
767 else if( out.RelativeValueAtMid < 0.0 )
768 {
769 const double frac = fabs( out.RelativeValueAtMid / min );
770 out.Colour = interpolateColours( settings.m_ZeroSkewColor, settings.m_NegativeSkewColor, frac,
771 settings.m_UseLogScale );
772 }
773 else if( out.RelativeValueAtMid > 0.0 )
774 {
775 const double frac = fabs( out.RelativeValueAtMid / max );
776 out.Colour = interpolateColours( settings.m_ZeroSkewColor, settings.m_PositiveSkewColor, frac,
777 settings.m_UseLogScale );
778 }
779 else
780 {
781 out.Colour = settings.m_ZeroSkewColor;
782 }
783
784 result.push_back( out );
785 }
786
787 return result;
788}
789
790
792{
793 clearOverlay();
794
795 const std::size_t selIdx = m_segmentForStatisticsDisplay.first;
796 const bool isSelected = m_segmentForStatisticsDisplay.second;
797 const bool drawHighlight = selIdx < std::numeric_limits<std::size_t>::max();
798
799 m_viewOverlay->SetIsStroke( true );
800 m_viewOverlay->SetIsFill( false );
801
802 for( std::size_t i = 0; i < m_selectedDiffs.size(); ++i )
803 {
804 const OUTPUT_SEGMENT& segment = m_selectedDiffs[i];
805
806 if( drawHighlight && isSelected && i == selIdx )
807 m_viewOverlay->SetStrokeColor( COLOR4D( 1.0, 0.0, 0.937, 1.0 ) );
808 else
809 m_viewOverlay->SetStrokeColor( segment.Colour );
810
811 m_viewOverlay->Segment( segment.Start, segment.End, segment.Width );
812 }
813
814 for( std::size_t i = 0; i < m_coupledDiffs.size(); ++i )
815 {
816 const OUTPUT_SEGMENT& segment = m_coupledDiffs[i];
817
818 if( drawHighlight && !isSelected && i == selIdx )
819 m_viewOverlay->SetStrokeColor( COLOR4D( 1.0, 0.0, 0.937, 1.0 ) );
820 else
821 m_viewOverlay->SetStrokeColor( segment.Colour );
822
823 m_viewOverlay->Segment( segment.Start, segment.End, segment.Width );
824 }
825
826 std::vector<MSG_PANEL_ITEM> items;
827 wxString description, value;
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:986
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:232
int ShowModal() override
void SetMode(const MODE aMode)
The tool entry point.
void setTransitions() override
< Set up handlers for tool events
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
DIFF_PAIR_VALIDITY determinePathDirections()
Reverses the direction of the selected and coupled paths (including swapping start / end pads.
bool reportValidityErrors(DIFF_PAIR_VALIDITY aDirection) const
Struct to represent one cumulative length and delay point.
START_END_DETAILS m_coupledStartEndDetails
LENGTH_DELAY_ITEM_DETAILS m_coupledLengthDelayDetails
static void reversePath(PNS::ITEM_SET &aPath, PNS::SOLID **aStartPad, PNS::SOLID **aEndPad)
Report to the user any errors after determining the signal direction.
static VECTOR2D lerp(const VECTOR2D aA, const VECTOR2D aB, const double aT)
Gets the cumulative length and delay at the given fractional coordinate in the given segment.
std::pair< std::size_t, bool > m_segmentForStatisticsDisplay
static void normalisePathItems(const PNS::ITEM_SET &aPath, const PNS::SOLID *aStartPad)
Determine which end of the extracted paths we are defining as the signal start point.
int getMaxDiffPairGap(const BOARD_CONNECTED_ITEM *aItem) const
Struct containing a final computed output diff segment.
static std::vector< double > buildSplitPositions(const std::vector< CUMULATIVE_ENTRY > &aSegments, double aTargetSubsegmentSize)
Returns the coordinate at the given linear distance along the line, along with the segment index the ...
MODE GetMode() const
Set the current mode of the tool.
void clearOverlay() const
Resets all select-specific variables.
std::vector< KNOWN_RELATIVE_POINT > m_selectedKnownPoints
std::vector< LENGTH_DELAY_CALCULATION_ITEM > m_selectedLengthDelayItems
void doDisplayOverlay()
Use the router to get the +ve and -ve paths from the selected item.
bool Init() override
Init() is called once upon a registration of the tool.
std::vector< CUMULATIVE_ENTRY > m_selectedCumulative
void getNetPaths()
Normalises the path to ensure SHAPE_LINE_CHAIN points are in overall path walk order.
BOARD_CONNECTED_ITEM * m_pickerItemFirst
KIGFX::VIEW_CONTROLS * m_controls
static void splitLengthItems(std::vector< LENGTH_DELAY_CALCULATION_ITEM > &aItems)
Finds all parallel segment runs in the selected and coupled tracks.
std::vector< OUTPUT_SEGMENT > m_selectedDiffs
void buildLengthDelayItems(const PNS::ITEM_SET &aPath, const PNS::SOLID *aStartPad, const PNS::SOLID *aEndPad, const NETINFO_ITEM *aNet, std::vector< LENGTH_DELAY_CALCULATION_ITEM > &aItems, LENGTH_DELAY_ITEM_DETAILS &aItemDetails) const
Start and end pad lengths and delays (pad-to-die + inferred via-in-pad)
void doInitialHover(const PCB_SELECTION_TOOL *aSelectionTool, GENERAL_COLLECTORS_GUIDE aGuide)
Display the phase overlay for the current hover item.
PCB_BASE_EDIT_FRAME * m_frame
int ShowDiffPhaseSkew(const TOOL_EVENT &aEvent)
Flags for the analysis state of the selected tracks.
void doShowStatsAtCursor()
Determines the nearest points to the cursor from the diff segments.
std::vector< OUTPUT_SEGMENT > m_coupledDiffs
COLOR4D interpolateColours(const COLOR4D &aColour1, const COLOR4D &aColour2, double aS, bool aUseLogScale) const
Draws the visual skew overlay.
static std::vector< CUMULATIVE_ENTRY > buildCumulativeLengthsAndDelays(const std::vector< LENGTH_DELAY_CALCULATION_ITEM > &aItems, const LENGTH_DELAY_ITEM_DETAILS &aLengthDelayDetails, const PNS::SOLID *aStartPad, const PNS::SOLID *aEndPad, START_END_DETAILS &aStartEndDetails)
Splits the calculation items from compound segments in to individual items.
void findParallelRunsImpl(std::pair< std::size_t, std::size_t > aRangeA, std::pair< std::size_t, std::size_t > aRangeB, double aMaxSpacing, std::vector< PARALLEL_RUN > &aRuns) const
Linear interpolate from point A to B at line fraction T.
std::pair< std::size_t, bool > getNearestDiffSegments(const VECTOR2D &aCursorPos, double aHitTestDistance) const
Ensures we have an active VIEW_OVERLAY to display the diff graphics.
std::vector< OUTPUT_SEGMENT > buildDiffOverlaySegmentsImpl(const std::vector< CUMULATIVE_ENTRY > &aSegments, const std::vector< LENGTH_DELAY_CALCULATION_ITEM > &aSourceItemDetails, const std::vector< KNOWN_RELATIVE_POINT > &aKnownPoints, double aTargetSubsegmentSize)
Builds a vector of known relative skew points on each track.
std::vector< KNOWN_RELATIVE_POINT > m_coupledKnownPoints
static std::pair< int64_t, int64_t > getCumulativeLengthAndDelayAt(const LENGTH_DELAY_ITEM_DETAILS &aLengthDelayDetails, const START_END_DETAILS &aPadDetails, const std::vector< CUMULATIVE_ENTRY > &aCumulative, std::size_t aSegIdx, double aT)
Gets the maximum diff pair gap for the given item, taken from DRC rules.
static std::pair< VECTOR2D, std::size_t > pointAtDistance(const std::vector< CUMULATIVE_ENTRY > &aSegments, const std::vector< LENGTH_DELAY_CALCULATION_ITEM > &aSourceItemDetails, double aDist)
Linearly interpolates between colour1 and colour2, with interpolation point given by aS [0-1].
void updateOverlay() const
Clears the VIEW_OVERLAY.
void buildDiffOverlaySegments(double aTargetSubsegmentSize)
std::optional< int > m_maxSkew
std::shared_ptr< KIGFX::VIEW_OVERLAY > m_viewOverlay
void buildKnownRelativePoints(const std::vector< PARALLEL_RUN > &aKnownRuns)
Determines where to apply overlay segment subsections on the source segments.
PNS::SIZES_SETTINGS m_savedSizes
void getOverlay()
Refreshes the VIEW_OVERLAY in the active VIEW.
BOARD_CONNECTED_ITEM * m_pickerItemSecond
void updateNetHighlights(bool aRefresh=true) const
Handle hover events before a DP pair is selected.
void resetStateVariables()
Updates the message panel.
std::vector< PARALLEL_RUN > findParallelRuns() const
Finds all parallel segment runs in the selected and coupled tracks within the given segment ranges an...
LENGTH_DELAY_ITEM_DETAILS m_selectedLengthDelayDetails
START_END_DETAILS m_selectedStartEndDetails
std::vector< LENGTH_DELAY_CALCULATION_ITEM > m_coupledLengthDelayItems
std::vector< CUMULATIVE_ENTRY > m_coupledCumulative
void drawDiffOverlay() const
Shows the diff stats nearest the cursor.
std::shared_ptr< DRC_ENGINE > m_drcEngine
SEVERITY GetSeverity() const
Definition drc_rule.h:217
const MINOPTMAX< int > & GetValue() const
Definition drc_rule.h:196
bool IsNull() const
Definition drc_rule.h:191
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:96
A general implementation of a COLLECTORS_GUIDE.
Definition collectors.h:320
void SetPreferredLayer(PCB_LAYER_ID aLayer)
Definition collectors.h:386
void SetIncludeSecondary(bool include)
Definition collectors.h:400
Used when the right click button is pressed, or when the select tool is in effect.
Definition collectors.h:203
void Collect(BOARD_ITEM *aItem, const std::vector< KICAD_T > &aScanList, const VECTOR2I &aRefPos, const COLLECTORS_GUIDE &aGuide)
Scan a BOARD_ITEM using this class's Inspector method, which does the collection.
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
double r
Red component.
Definition color4d.h:389
double g
Green component.
Definition color4d.h:390
double a
Alpha component.
Definition color4d.h:392
double b
Blue component.
Definition color4d.h:391
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:34
T Max() const
Definition minoptmax.h:30
T Opt() const
Definition minoptmax.h:31
bool HasOpt() const
Definition minoptmax.h:35
Handle the data for a net.
Definition netinfo.h:46
NETCLASS * GetNetClass()
Definition netinfo.h:91
int GetNetCode() const
Definition netinfo.h:94
Definition pad.h:61
PAD_SIM_ELECTRICAL_TYPE GetSimElectricalType() const
Definition pad.h:568
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:553
Basic class for a differential pair.
int Size() const
std::vector< ITEM * >::reverse_iterator rbegin()
void Add(const LINE &aLine)
std::vector< ITEM * >::reverse_iterator rend()
Base class for PNS router board items.
Definition pns_item.h:98
PnsKind Kind() const
Return the type (kind) of the item.
Definition pns_item.h:173
bool OfKind(int aKindMask) const
Definition pns_item.h:181
virtual BOARD_ITEM * BoardItem() const
Definition pns_item.h:207
Represents a track on a PCB, connecting two non-trivial joints (that is, vias, pads,...
Definition pns_line.h:62
SHAPE_LINE_CHAIN & Line()
Definition pns_line.h:141
int SegmentCount() const
Definition pns_line.h:144
void Reverse()
Clip the line to the nearest obstacle, traversing from the line's start vertex (0).
Keep the router "world" - i.e.
Definition pns_node.h:242
int GetPadToDie() const
Definition pns_solid.h:122
int GetPadToDieDelay() const
Definition pns_solid.h:125
const VECTOR2I & Pos() const
Definition pns_solid.h:119
const DIFF_PAIR AssembleDiffPair(SEGMENT *aStart)
const ITEM_SET AssembleTuningPath(ROUTER_IFACE *aRouterIface, ITEM *aStart, SOLID **aStartPad=nullptr, SOLID **aEndPad=nullptr)
Like AssembleTrivialPath, but follows the track length algorithm, which discards segments that are fu...
const VECTOR2I & Pos() const
Definition pns_via.h:206
Definition seg.h:38
VECTOR2I A
Definition seg.h:45
VECTOR2I B
Definition seg.h:46
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
const SHAPE_LINE_CHAIN Reverse() const
Reverse point order in the line chain.
virtual const VECTOR2I GetPoint(int aIndex) const override
SEG Segment(int aIndex) const
Return a copy of the aIndex-th segment in the line chain.
virtual size_t GetPointCount() const override
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
virtual const SEG GetSegment(int aIndex) const override
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
int SegmentCount() const
Return the number of segments in this line chain.
const VECTOR2I & CLastPoint() const
Return the last point in the line chain.
T * getModel() const
Return the model object if it matches the requested type.
Definition tool_base.h:195
KIGFX::VIEW_CONTROLS * getViewControls() const
Return the instance of VIEW_CONTROLS object used in the application.
Definition tool_base.cpp:40
TOOL_MANAGER * m_toolMgr
Definition tool_base.h:220
KIGFX::VIEW * getView() const
Returns the instance of #VIEW object used in the application.
Definition tool_base.cpp:34
RESET_REASON
Determine the reason of reset for a tool.
Definition tool_base.h:74
@ SHUTDOWN
Tool is being shut down.
Definition tool_base.h:80
Generic, UI-independent tool event.
Definition tool_event.h:167
bool HasPosition() const
Returns if it this event has a valid position (true for mouse events and context-menu or hotkey-based...
Definition tool_event.h:256
const VECTOR2D Position() const
Return mouse cursor position in world coordinates.
Definition tool_event.h:289
void Go(int(T::*aStateFunc)(const TOOL_EVENT &), const TOOL_EVENT_LIST &aConditions=TOOL_EVENT(TC_ANY, TA_ANY))
Define which state (aStateFunc) to go when a certain event arrives (aConditions).
TOOL_EVENT * Wait(const TOOL_EVENT_LIST &aEventList=TOOL_EVENT(TC_ANY, TA_ANY))
Suspend execution of the tool until an event specified in aEventList arrives.
void Activate()
Run the tool.
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition vector2d.h:279
constexpr extended_type Dot(const VECTOR2< T > &aVector) const
Compute dot product of self with aVector.
Definition vector2d.h:542
@ ARROW
Definition cursors.h:42
#define INITIAL_HOVER_HITTEST_THRESHOLD_PIXELS
#define DETAILS_HOVER_HITTEST_THRESHOLD_PIXELS
constexpr double EPS
Floating point comparison epsilon.
@ DIFF_PAIR_GAP_CONSTRAINT
Definition drc_rule.h:78
#define _(s)
double m_DiffSkewColourInterpolationLogStrength
The logarithmic weighting factor to apply to colour interpolation in the diff phase overlay tool.
double m_DiffSkewTrackGapInflation
The multiplier of constraint diff pair gap to allow identification of coupled track segments in the d...
double m_DiffSkewTargetDiffSegmentSize
The target size (in PCB IU) of diff phase skew gradient overlay segments.
double m_DiffSkewCosThetaParallelTestValue
The value of cos(theta) between two tracks used to test for parallelism in the diff phase skew overla...
double m_DiffSkewOverlayTrackInflation
The multiplier of underlying track size applied to the diff phase skew overlay.
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
CITER next(CITER it)
Definition ptree.cpp:120
@ RPT_SEVERITY_IGNORE
char * GetLine(FILE *File, char *Line, int *LineNum, int SizeLine)
Read one line line from aFile.
Builds the length / delay calculation items from a given path.
Builds the final overlay output segments for plotting.
VECTOR2D Start
The start point of the segment.
VECTOR2D End
The end point of the segment.
bool RelativeValueKnown
Flag whether the diff value is valid at this segment.
double RelativeValueAtMid
The value of the diff at the beginning of this segment.
Builds a vector in which each entry represents the cumulative length and delay at the start of a give...
Used to represent the results of a call to CalculateLengthDetails, including inferred via-in-pad deta...
int64_t InferredEndViaLength
The length of an inferred end via-in-pad.
int64_t InferredStartViaLength
The length of an inferred start via-in-pad.
int64_t InferredEndViaDelay
The delay of an inferred end via-in-pad.
std::vector< std::pair< int64_t, int64_t > > LengthsAndDelays
Per-item lengths and delays.
int64_t InferredStartViaDelay
The delay of an inferred start via-in-pad.
Struct to represent one segment where tracks run parallel, including information about absolute and r...
VECTOR2I endA
The ending coordinate of the run on track A.
std::optional< double > startViaLengthB
std::optional< double > endViaDelayA
std::optional< double > startViaLengthA
double endDelayA
Cumulative delay of track A at the start of the parallel run.
double startLenA
Cumulative length of track A at the start of the parallel run.
size_t segB
The index of the parallel segment on track B.
VECTOR2I startA
The starting coordinate of the run on track A.
std::optional< double > startViaDelayB
std::optional< double > startViaDelayA
VECTOR2I startB
The starting coordinate of the run on track B.
std::optional< double > endViaDelayB
double ta0
Normalised values of the start (0) and end (1) coordinates on track A and B These are normalised to t...
double startDelayA
Cumulative delay of track A at the start of the parallel run.
std::optional< double > endViaLengthA
double startLenB
Cumulative length of track B at the start of the parallel run.
double endLenA
Cumulative length of track A at the end of the parallel run.
double endDelayB
Cumulative delay of track A at the start of the parallel run.
double startDelayB
Cumulative delay of track A at the start of the parallel run.
double endLenB
Cumulative length of track B at the end of the parallel run.
size_t segA
The index of the parallel segment on track A.
VECTOR2I endB
The ending coordinate of the run on track B.
std::optional< double > endViaLengthB
Struct to control which optimisations the length calculation code runs on the given path objects.
static VECTOR2I SnapToNearestTrack(const VECTOR2I &aP, BOARD *aBoard, NETINFO_ITEM *aNet, PCB_TRACK **aNearestTrack)
static LINKED_ITEM * PickSegment(ROUTER *aRouter, const VECTOR2I &aWhere, int aLayer, VECTOR2I &aPointOut, const SHAPE_LINE_CHAIN &aBaseline=SHAPE_LINE_CHAIN())
Represents a single line in the tuning profile configuration grid.
VECTOR2I end
wxString result
Test unit parsing edge cases and error handling.
@ BUT_LEFT
Definition tool_event.h:128
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:91
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:89
Casted dyn_cast(From aObject)
A lightweight dynamic downcast.
Definition typeinfo.h:56
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683
VECTOR2< double > VECTOR2D
Definition vector2d.h:682