KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pcb_tuning_pattern.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 (C) 2023 Alex Shvartzkop <[email protected]>
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
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
21#include <pcb_generator.h>
22#include <generators_mgr.h>
23
24#include <algorithm>
25#include <functional>
26#include <limits>
27#include <optional>
28#include <magic_enum.hpp>
29#include <map>
30#include <unordered_map>
31
32#include <wx/debug.h>
33#include <wx/log.h>
34
38#include <kiplatform/ui.h>
40#include <collectors.h>
41#include <scoped_set_reset.h>
42#include <core/mirror.h>
43#include <string_utils.h>
44
45#include <board.h>
47#include <drc/drc_engine.h>
48#include <pcb_track.h>
49#include <pcb_shape.h>
50#include <pcb_group.h>
51#include <pad.h>
52#include <footprint.h>
53
54#include <tool/edit_points.h>
55#include <tool/tool_manager.h>
56#include <tools/drawing_tool.h>
61
64#include <view/view.h>
65#include <view/view_controls.h>
66
69#include <router/pns_meander.h>
71#include <router/pns_segment.h>
72#include <router/pns_arc.h>
73#include <router/pns_solid.h>
74#include <router/pns_topology.h>
76#include <router/pns_helpers.h>
77
79
81#include <net_chain_bridging.h>
84#include <properties/property.h>
86
87// (Removed previous toggleable chain/net tuning scope. We now always display per-net
88// values and, when the net belongs to a chain, aggregated chain values concurrently.)
89
91 EDA_ITEM( NOT_USED ), // Never added to anything - just a preview
92 m_frame( aFrame ),
93 m_min( 0.0 ),
94 m_max( 0.0 ),
95 m_current( 0.0 ),
96 m_isTimeDomain( false )
97{ }
98
99
101{
102 return wxT( "TUNING_STATUS" );
103}
104
105#if defined(DEBUG)
106void TUNING_STATUS_VIEW_ITEM::Show( int nestLevel, std::ostream& os ) const {}
107#endif
108
109
112
113
114void TUNING_STATUS_VIEW_ITEM::SetMinMax( const double aMin, const double aMax )
115{
117
118 m_min = aMin;
119 m_minText = m_frame->MessageTextFromValue( m_min, false, unitType );
120 m_max = aMax;
121 m_maxText = m_frame->MessageTextFromValue( m_max, false, unitType );
122}
123
124
125void TUNING_STATUS_VIEW_ITEM::SetChainMinMax( const double aMin, const double aMax )
126{
128
129 m_chainMin = aMin;
130 m_chainMinText = m_frame->MessageTextFromValue( m_chainMin, false, unitType );
131 m_chainMax = aMax;
132 m_chainMaxText = m_frame->MessageTextFromValue( m_chainMax, false, unitType );
133}
134
135
137{
138 m_min = 0.0;
139 m_minText = wxT( "---" );
140 m_max = std::numeric_limits<double>::max();
141 m_maxText = wxT( "---" );
142}
143
144
146{
147 m_chainMin = 0.0;
148 m_chainMinText = wxT( "---" );
149 m_chainMax = std::numeric_limits<double>::max();
150 m_chainMaxText = wxT( "---" );
151}
152
153
154void TUNING_STATUS_VIEW_ITEM::SetCurrent( const double aCurrent, const wxString& aLabel )
155{
157
158 m_current = aCurrent;
159 m_currentText = m_frame->MessageTextFromValue( aCurrent, true, unitType );
160 m_currentLabel = aLabel;
161}
162
163
164void TUNING_STATUS_VIEW_ITEM::SetIsTimeDomain( const bool aIsTimeDomain )
165{
166 m_isTimeDomain = aIsTimeDomain;
167}
168
169
171{
172 BOX2I tmp;
173
174 // this is an edit-time artefact; no reason to try and be smart with the bounding box
175 // (besides, we can't tell the text extents without a view to know what the scale is)
176 tmp.SetMaximum();
177 return tmp;
178}
179
180
182{
183 return { LAYER_UI_START, LAYER_UI_START + 1 };
184}
185
186
187void TUNING_STATUS_VIEW_ITEM::ViewDraw( int aLayer, KIGFX::VIEW* aView ) const
188{
189 KIGFX::GAL* gal = aView->GetGAL();
190 bool viewFlipped = gal->IsFlippedX();
191 bool drawingDropShadows = ( aLayer == LAYER_UI_START );
192
193 gal->Save();
194 gal->Scale( { 1., 1. } );
195
199 const KIFONT::METRICS& fontMetrics = KIFONT::METRICS::Default();
200 TEXT_ATTRIBUTES textAttrs;
201
202 int glyphWidth = textDims.GlyphSize.x;
203 VECTOR2I margin( KiROUND( glyphWidth * 0.4 ), glyphWidth );
204 int scopeLines = m_scopeLine.IsEmpty() ? 0 : 1;
205 int valueLines = 1 + ( m_hasSignalValue ? 1 : 0 );
206 int totalHeaderLines = scopeLines + 1;
207 int totalLines = totalHeaderLines + valueLines;
208 VECTOR2I size( glyphWidth * 38 + margin.x * 2,
209 headerDims.GlyphSize.y * totalLines + textDims.GlyphSize.y * valueLines );
210 VECTOR2I offset( margin.x * 2, -( size.y + margin.y * 2 ) );
211
212 if( drawingDropShadows )
213 {
214 gal->SetIsFill( true );
215 gal->SetIsStroke( true );
216 gal->SetLineWidth( gal->GetScreenWorldMatrix().GetScale().x * 2 );
217 gal->SetStrokeColor( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNTEXT ) );
218 KIGFX::COLOR4D bgColor( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
219 gal->SetFillColor( bgColor.WithAlpha( 0.9 ) );
220
221 gal->DrawRectangle( GetPosition() + offset - margin,
222 GetPosition() + offset + size + margin );
223 gal->Restore();
224 return;
225 }
226
227 COLOR4D bg = wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE );
228 COLOR4D normal = wxSystemSettings::GetColour( wxSYS_COLOUR_BTNTEXT );
229 COLOR4D red;
231
232 double bg_h, bg_s, bg_l;
233 bg.ToHSL( bg_h, bg_s, bg_l );
234
235 // Choose colors with reasonable contrasting with the background
236 red.FromHSL( 0.0, 1.0, bg_l < 0.5 ? 0.7 : 0.3 );
237 green.FromHSL( 120.0, 1.0, bg_l < 0.5 ? 0.8 : 0.2 );
238
239 if( viewFlipped )
241 else
242 textAttrs.m_Halign = GR_TEXT_H_ALIGN_LEFT;
243
244 gal->SetIsFill( false );
245 gal->SetIsStroke( true );
246 gal->SetStrokeColor( normal );
247 textAttrs.m_Halign = GR_TEXT_H_ALIGN_LEFT;
248
249 // Prevent text flipping when view is flipped
250 if( gal->IsFlippedX() )
251 {
252 textAttrs.m_Mirrored = true;
254 }
255
256 textAttrs.m_Size = headerDims.GlyphSize;
257 textAttrs.m_StrokeWidth = headerDims.StrokeWidth;
258
259 VECTOR2I textPos = GetPosition() + offset;
260
261 if( scopeLines )
262 {
263 textAttrs.m_Size = headerDims.GlyphSize;
264 textAttrs.m_StrokeWidth = headerDims.StrokeWidth;
265 font->Draw( gal, m_scopeLine, textPos, textAttrs, fontMetrics );
266 textPos.y += KiROUND( headerDims.LinePitch * 1.1 );
267 }
268
269 // Line 2: header labels (current length min max)
270 font->Draw( gal, m_currentLabel, textPos, textAttrs, KIFONT::METRICS::Default() );
271 textPos.x += glyphWidth * 18 + margin.x;
272 font->Draw( gal, _( "min" ), textPos, textAttrs, fontMetrics );
273 textPos.x += glyphWidth * 7 + margin.x;
274 font->Draw( gal, _( "max" ), textPos, textAttrs, fontMetrics );
275
276 // Prepare for value lines
277 textAttrs.m_Size = textDims.GlyphSize;
278 textAttrs.m_StrokeWidth = textDims.StrokeWidth;
279
280 // First value line (Net: ...)
281 textPos = GetPosition() + offset;
282 if( scopeLines )
283 textPos.y += KiROUND( headerDims.LinePitch * 1.1 );
284 // move below header line
285 textPos.y += KiROUND( headerDims.LinePitch * 1.3 );
286 gal->SetStrokeColor( normal );
287 font->Draw( gal, m_netValue, textPos, textAttrs, KIFONT::METRICS::Default() );
288
289 // Draw min / max columns for net line
290 textPos.x += glyphWidth * 18 + margin.x;
291 font->Draw( gal, m_minText, textPos, textAttrs, fontMetrics );
292 textPos.x += glyphWidth * 7 + margin.x;
293 font->Draw( gal, m_maxText, textPos, textAttrs, fontMetrics );
294
295 // Optional Chain value line with its own min/max
296 if( m_hasSignalValue )
297 {
298 textPos = GetPosition() + offset;
299 if( scopeLines )
300 textPos.y += KiROUND( headerDims.LinePitch * 1.1 );
301 textPos.y += KiROUND( headerDims.LinePitch * 2.3 );
302 gal->SetStrokeColor( normal );
303 font->Draw( gal, m_chainValue, textPos, textAttrs, KIFONT::METRICS::Default() );
304 textPos.x += glyphWidth * 18 + margin.x;
305 font->Draw( gal, m_chainMinText, textPos, textAttrs, fontMetrics );
306 textPos.x += glyphWidth * 7 + margin.x;
307 font->Draw( gal, m_chainMaxText, textPos, textAttrs, fontMetrics );
308 }
309
310 gal->Restore();
311}
312
313
314static LENGTH_TUNING_MODE tuningFromString( const std::string& aStr )
315{
316 if( aStr == "single" )
318 else if( aStr == "diff_pair" )
320 else if( aStr == "diff_pair_skew" )
322 else
323 {
324 wxFAIL_MSG( wxS( "Unknown length tuning token" ) );
326 }
327}
328
329
330static std::string tuningToString( const LENGTH_TUNING_MODE aTuning )
331{
332 switch( aTuning )
333 {
334 case LENGTH_TUNING_MODE::SINGLE: return "single";
335 case LENGTH_TUNING_MODE::DIFF_PAIR: return "diff_pair";
336 case LENGTH_TUNING_MODE::DIFF_PAIR_SKEW: return "diff_pair_skew";
337 default: wxFAIL; return "";
338 }
339}
340
341
352
353
354static PNS::MEANDER_SIDE sideFromString( const std::string& aStr )
355{
356 if( aStr == "default" )
358 else if( aStr == "left" )
360 else if( aStr == "right" )
362 else
363 {
364 wxFAIL_MSG( wxS( "Unknown length-tuning side token" ) );
366 }
367}
368
369
371{
372 switch( aStatus )
373 {
374 case PNS::MEANDER_PLACER_BASE::TOO_LONG: return "too_long";
375 case PNS::MEANDER_PLACER_BASE::TOO_SHORT: return "too_short";
376 case PNS::MEANDER_PLACER_BASE::TUNED: return "tuned";
377 default: wxFAIL; return "";
378 }
379}
380
381
383{
384 if( aStr == "too_long" )
386 else if( aStr == "too_short" )
388 else if( aStr == "tuned" )
390 else
391 {
392 wxFAIL_MSG( wxS( "Unknown tuning status token" ) );
394 }
395}
396
397
398static std::string sideToString( const PNS::MEANDER_SIDE aValue )
399{
400 switch( aValue )
401 {
402 case PNS::MEANDER_SIDE_DEFAULT: return "default";
403 case PNS::MEANDER_SIDE_LEFT: return "left";
404 case PNS::MEANDER_SIDE_RIGHT: return "right";
405 default: wxFAIL; return "";
406 }
407}
408
409// Cache invalidation heuristic for bridging distances.
410// Recompute if chain name changed, board pointer changed, or total pad count on the board changed.
411// Pad count change is a low-cost proxy for edits that might affect bridging. The bridging
412// computation itself is delegated to the shared helper in net_chain_bridging.h.
413long long PCB_TUNING_PATTERN::GetCachedBridgingLength( BOARD* aBoard, const wxString& aNetChain,
414 double* aDelayIUOut )
415{
416 if( !aBoard )
417 return 0;
418
419 size_t padCount = 0;
420 for( FOOTPRINT* fp : aBoard->Footprints() )
421 padCount += fp->Pads().size();
422
423 bool invalid = ( aNetChain != m_cachedBridgingSignal )
424 || ( aBoard != m_cachedBridgingBoardPtr )
425 || ( padCount != m_cachedBridgingPadCount );
426
427 if( invalid )
428 {
429 // Mirror the matched-length DRC predicate; see net_chain_bridging.h.
430 auto [lenIU, delayIU] = BoardChainBridging( aBoard, aNetChain );
431
432 m_cachedBridgingLen = static_cast<long long>( lenIU );
433 m_cachedBridgingDelayIU = delayIU;
434 m_cachedBridgingSignal = aNetChain;
436 m_cachedBridgingPadCount = padCount;
437 }
438
439 if( aDelayIUOut )
440 *aDelayIUOut = m_cachedBridgingDelayIU;
441
442 return m_cachedBridgingLen;
443}
444
445
447 LENGTH_TUNING_MODE aMode ) :
448 PCB_GENERATOR( aParent, aLayer ),
449 m_trackWidth( 0 ),
450 m_diffPairGap( 0 ),
451 m_tuningMode( aMode ),
452 m_tuningLength( 0 ),
453 m_tuningStatus( PNS::MEANDER_PLACER_BASE::TUNING_STATUS::TUNED ),
454 m_updateSideFromEnd( false ),
458 m_cachedBridgingBoardPtr( nullptr ),
460{
463 m_end = VECTOR2I( pcbIUScale.mmToIU( 10 ), 0 );
464 m_settings.m_initialSide = PNS::MEANDER_SIDE_LEFT;
465}
466
467
469{
470 if( m_tuningMode == DIFF_PAIR )
471 {
472 return( m_baseLine && m_baseLine->PointCount() > 1
473 && m_baseLineCoupled && m_baseLineCoupled->PointCount() > 1 );
474 }
475 else
476 {
477 return( m_baseLine && m_baseLine->PointCount() > 1 );
478 }
479}
480
481
483 PCB_BASE_EDIT_FRAME* aFrame,
484 BOARD_CONNECTED_ITEM* aStartItem,
485 LENGTH_TUNING_MODE aMode )
486{
487 BOARD* board = aStartItem->GetBoard();
489 DRC_CONSTRAINT constraint;
490 PCB_LAYER_ID layer = aStartItem->GetLayer();
491
492 PCB_TUNING_PATTERN* pattern = new PCB_TUNING_PATTERN( board, layer, aMode );
493
494 switch( aMode )
495 {
496 case SINGLE: pattern->m_settings = bds.m_SingleTrackMeanderSettings; break;
497 case DIFF_PAIR: pattern->m_settings = bds.m_DiffPairMeanderSettings; break;
498 case DIFF_PAIR_SKEW: pattern->m_settings = bds.m_SkewMeanderSettings; break;
499 }
500
501 if( aMode == SINGLE || aMode == DIFF_PAIR )
502 {
503 DRC_CONSTRAINT chainConstraint =
504 bds.m_DRCEngine->EvalRules( NET_CHAIN_LENGTH_CONSTRAINT, aStartItem, nullptr, layer );
505
506 DRC_CONSTRAINT netConstraint = bds.m_DRCEngine->EvalRules( LENGTH_CONSTRAINT, aStartItem, nullptr, layer );
507
508 // Compute effective per-net target from both constraints
509 MINOPTMAX<int> effectiveTarget;
510 bool hasConstraint = false;
511 bool isTimeDomain = false;
512
513 // Start with per-net constraint as baseline
514 if( !netConstraint.IsNull() && netConstraint.GetSeverity() != RPT_SEVERITY_IGNORE )
515 {
516 effectiveTarget = netConstraint.GetValue();
517 isTimeDomain = netConstraint.GetOption( DRC_CONSTRAINT::OPTIONS::TIME_DOMAIN );
518 hasConstraint = true;
519 }
520
521 // Store chain-level target; Move() derives the per-net budget from it.
522 if( !chainConstraint.IsNull() && chainConstraint.GetSeverity() != RPT_SEVERITY_IGNORE )
523 {
524 NETINFO_ITEM* netInfo = static_cast<BOARD_CONNECTED_ITEM*>( aStartItem )->GetNet();
525 wxString chainName = netInfo ? netInfo->GetNetChain() : wxString();
526
527 if( !chainName.IsEmpty() )
528 {
529 isTimeDomain = chainConstraint.GetOption( DRC_CONSTRAINT::OPTIONS::TIME_DOMAIN );
530
531 // Subtract bridging so the copper-only budget aligns with the DRC check.
532 MINOPTMAX<int> adjustedTarget = chainConstraint.GetValue();
533 double bridgingDelayIU = 0.0;
534 long long bridging = pattern->GetCachedBridgingLength( board, chainName,
535 &bridgingDelayIU );
536
537 if( bridging > 0 )
538 {
539 long long bridgingIU = isTimeDomain ? static_cast<long long>( bridgingDelayIU )
540 : bridging;
541
542 if( adjustedTarget.HasMin() )
543 adjustedTarget.SetMin( SubtractBridgingClamped( adjustedTarget.Min(),
544 bridgingIU ) );
545
546 if( adjustedTarget.HasOpt() )
547 adjustedTarget.SetOpt( SubtractBridgingClamped( adjustedTarget.Opt(),
548 bridgingIU ) );
549
550 if( adjustedTarget.HasMax() )
551 adjustedTarget.SetMax( SubtractBridgingClamped( adjustedTarget.Max(),
552 bridgingIU ) );
553 }
554
555 if( isTimeDomain )
556 {
557 pattern->m_settings.SetTargetSignalLengthDelay( adjustedTarget );
558 }
559 else
560 {
561 pattern->m_settings.SetTargetSignalLength( adjustedTarget );
562 }
563 }
564 }
565
566 if( hasConstraint )
567 {
568 constraint = netConstraint.IsNull() ? chainConstraint : netConstraint;
569
570 if( isTimeDomain )
571 {
572 pattern->m_settings.SetTargetLengthDelay( effectiveTarget );
574 pattern->m_settings.m_isTimeDomain = true;
575 }
576 else
577 {
578 pattern->m_settings.SetTargetLength( effectiveTarget );
580 pattern->m_settings.m_isTimeDomain = false;
581 }
582 }
583 else if( isTimeDomain )
584 {
585 // Chain-only (no per-net rule): set time-domain flag, let Move() derive targets.
586 pattern->m_settings.m_isTimeDomain = true;
587 }
588 else if( aStartItem->GetEffectiveNetClass()->HasTuningProfile() )
589 {
590 // Check if the tuning profile has time domain tuning enabled
591 const std::shared_ptr<TUNING_PROFILES> tuningParams =
593 TUNING_PROFILE& profile =
594 tuningParams->GetTuningProfile( aStartItem->GetEffectiveNetClass()->GetTuningProfile() );
595
596 if( profile.m_EnableTimeDomainTuning )
597 pattern->m_settings.m_isTimeDomain = true;
598 }
599 }
600 else
601 {
602 constraint = bds.m_DRCEngine->EvalRules( SKEW_CONSTRAINT, aStartItem, nullptr, layer );
603
604 if( !constraint.IsNull() )
605 {
607 {
609 pattern->m_settings.SetTargetSkewDelay( constraint.GetValue() );
610 pattern->m_settings.m_isTimeDomain = true;
611 }
612 else
613 {
614 pattern->m_settings.SetTargetSkew( constraint.GetValue() );
616 pattern->m_settings.m_isTimeDomain = false;
617 }
618 }
619 }
620
621 pattern->SetFlags( IS_NEW );
622 pattern->m_settings.m_netClass = aStartItem->GetEffectiveNetClass();
623
624 return pattern;
625}
626
628{
629 if( aCommit )
630 {
631 if( IsNew() )
632 aCommit->Add( this );
633 else
634 aCommit->Modify( this );
635 }
636
637 SetFlags( IN_EDIT );
638
639 PNS::ROUTER* router = aTool->Router();
640 int layer = router->GetInterface()->GetPNSLayerFromBoardLayer( GetLayer() );
641
642 aTool->ClearRouterChanges();
643 router->SyncWorld();
644
646 PNS::CONSTRAINT constraint;
647
648 if( !baselineValid() )
649 initBaseLines( router, layer, aBoard );
650
652 {
653 VECTOR2I centerlineOffsetEnd;
654
656 && m_baseLineCoupled->SegmentCount() > 0 )
657 {
658 centerlineOffsetEnd =
659 ( m_baseLineCoupled->CLastPoint() - m_baseLine->CLastPoint() ) / 2;
660 }
661
662 SEG baseEnd = m_baseLine && m_baseLine->SegmentCount() > 0 ? m_baseLine->CSegment( -1 )
663 : SEG( m_origin, m_end );
664
665 baseEnd.A += centerlineOffsetEnd;
666 baseEnd.B += centerlineOffsetEnd;
667
668 if( baseEnd.A != baseEnd.B )
669 {
670 int side = baseEnd.Side( m_end );
671
672 if( side < 0 )
673 m_settings.m_initialSide = PNS::MEANDER_SIDE_LEFT;
674 else
675 m_settings.m_initialSide = PNS::MEANDER_SIDE_RIGHT;
676 }
677
678 m_updateSideFromEnd = false;
679 }
680
681 PCB_TRACK* track = nullptr;
682 m_origin = PNS::HELPERS::SnapToNearestTrack( m_origin, aBoard, nullptr, &track );
683 wxCHECK( track, /* void */ );
684
685 m_settings.m_netClass = track->GetEffectiveNetClass();
686
687 if( !m_settings.m_overrideCustomRules )
688 {
689 PNS::SEGMENT pnsItem;
690 NETINFO_ITEM* net = track->GetNet();
691
692 pnsItem.SetParent( track );
693 pnsItem.SetNet( net );
694
695 if( m_tuningMode == SINGLE )
696 {
697 if( resolver->QueryConstraint( PNS::CONSTRAINT_TYPE::CT_LENGTH,
698 &pnsItem, nullptr, layer, &constraint ) )
699 {
700 if( constraint.m_IsTimeDomain )
701 {
702 m_settings.SetTargetLengthDelay( constraint.m_Value );
703 m_settings.SetTargetLength( MINOPTMAX<int>() );
704 }
705 else
706 {
707 m_settings.SetTargetLengthDelay( MINOPTMAX<int>() );
708 m_settings.SetTargetLength( constraint.m_Value );
709 }
710
711 m_settings.m_isTimeDomain = constraint.m_IsTimeDomain;
713 }
714 else if( track->GetEffectiveNetClass()->HasTuningProfile() )
715 {
716 m_settings.m_isTimeDomain = true;
718 }
719 }
720 else
721 {
722 PCB_TRACK* coupledTrack = nullptr;
723 PNS::SEGMENT pnsCoupledItem;
724 NETINFO_ITEM* coupledNet = aBoard->DpCoupledNet( net );
725
726 if( coupledNet )
727 PNS::HELPERS::SnapToNearestTrack( m_origin, aBoard, coupledNet, &coupledTrack );
728
729 pnsCoupledItem.SetParent( coupledTrack );
730 pnsCoupledItem.SetNet( coupledNet );
731
732 if( m_tuningMode == DIFF_PAIR )
733 {
734 if( resolver->QueryConstraint( PNS::CONSTRAINT_TYPE::CT_LENGTH,
735 &pnsItem, &pnsCoupledItem, layer, &constraint ) )
736 {
737 if( constraint.m_IsTimeDomain )
738 {
739 m_settings.SetTargetLengthDelay( constraint.m_Value );
740 m_settings.SetTargetLength( MINOPTMAX<int>() );
741 }
742 else
743 {
744 m_settings.SetTargetLengthDelay( MINOPTMAX<int>() );
745 m_settings.SetTargetLength( constraint.m_Value );
746 }
747
748 m_settings.m_isTimeDomain = constraint.m_IsTimeDomain;
750 }
751 else if( track->GetEffectiveNetClass()->HasTuningProfile() )
752 {
753 m_settings.m_isTimeDomain = true;
755 }
756 }
757 else
758 {
760 &pnsItem, &pnsCoupledItem, layer, &constraint ) )
761 {
762 if( constraint.m_IsTimeDomain )
763 {
764 m_settings.SetTargetSkewDelay( constraint.m_Value );
765 m_settings.SetTargetSkew( MINOPTMAX<int>() );
766 }
767 else
768 {
769 m_settings.SetTargetSkewDelay( MINOPTMAX<int>() );
770 m_settings.SetTargetSkew( constraint.m_Value );
771 }
772
773 m_settings.m_isTimeDomain = constraint.m_IsTimeDomain;
775 }
776 }
777 }
778 }
779}
780
781
782static std::optional<PNS::LINE> getPNSLine( const VECTOR2I& aStart, const VECTOR2I& aEnd,
783 PNS::ROUTER* router, int layer, VECTOR2I& aStartOut,
784 VECTOR2I& aEndOut )
785{
786 PNS::NODE* world = router->GetWorld();
787
788 PNS::LINKED_ITEM* startItem = PNS::HELPERS::PickSegment( router, aStart, layer, aStartOut );
789 PNS::LINKED_ITEM* endItem = PNS::HELPERS::PickSegment( router, aEnd, layer, aEndOut );
790
791 for( PNS::LINKED_ITEM* testItem : { startItem, endItem } )
792 {
793 if( !testItem )
794 continue;
795
796 PNS::LINE line = world->AssembleLine( testItem, nullptr, false, false );
797 SHAPE_LINE_CHAIN oldChain = line.CLine();
798
799 if( oldChain.PointOnEdge( aStartOut, 1 ) && oldChain.PointOnEdge( aEndOut, 1 ) )
800 return line;
801 }
802
803 return std::nullopt;
804}
805
806
807bool PCB_TUNING_PATTERN::initBaseLine( PNS::ROUTER* aRouter, int aPNSLayer, BOARD* aBoard,
808 VECTOR2I& aStart, VECTOR2I& aEnd, NETINFO_ITEM* aNet,
809 std::optional<SHAPE_LINE_CHAIN>& aBaseLine )
810{
811 PNS::NODE* world = aRouter->GetWorld();
812
813 aStart = PNS::HELPERS::SnapToNearestTrack( aStart, aBoard, aNet, nullptr );
814 aEnd = PNS::HELPERS::SnapToNearestTrack( aEnd, aBoard, aNet, nullptr );
815
816 VECTOR2I startSnapPoint, endSnapPoint;
817
818 PNS::LINKED_ITEM* startItem = PNS::HELPERS::PickSegment( aRouter, aStart, aPNSLayer, startSnapPoint );
819 PNS::LINKED_ITEM* endItem = PNS::HELPERS::PickSegment( aRouter, aEnd, aPNSLayer, endSnapPoint );
820
821 wxASSERT( startItem );
822 wxASSERT( endItem );
823
824 if( !startItem || !endItem )
825 return false;
826
827 PNS::LINE line = world->AssembleLine( startItem );
828 const SHAPE_LINE_CHAIN& chain = line.CLine();
829
830 wxASSERT( line.ContainsLink( endItem ) );
831
832 wxASSERT( chain.PointOnEdge( startSnapPoint, 40000 ) );
833 wxASSERT( chain.PointOnEdge( endSnapPoint, 40000 ) );
834
837 SHAPE_LINE_CHAIN post;
838
839 chain.Split( startSnapPoint, endSnapPoint, pre, mid, post );
840
841 aBaseLine = mid;
842
843 return true;
844}
845
846
847bool PCB_TUNING_PATTERN::initBaseLines( PNS::ROUTER* aRouter, int aPNSLayer, BOARD* aBoard )
848{
849 m_baseLineCoupled.reset();
850
851 PCB_TRACK* track = nullptr;
852
853 m_origin = PNS::HELPERS::SnapToNearestTrack( m_origin, aBoard, nullptr, &track );
854 wxCHECK( track, false );
855
856 NETINFO_ITEM* net = track->GetNet();
857
858 if( !initBaseLine( aRouter, aPNSLayer, aBoard, m_origin, m_end, net, m_baseLine ) )
859 return false;
860
861 // Generate both baselines even if we're skewing. We need the coupled baseline to run the
862 // DRC rules against.
863 if( m_tuningMode == DIFF_PAIR )
864 {
865 if( NETINFO_ITEM* coupledNet = aBoard->DpCoupledNet( net ) )
866 {
867 VECTOR2I coupledStart = PNS::HELPERS::SnapToNearestTrack( m_origin, aBoard, coupledNet, nullptr );
868 VECTOR2I coupledEnd = PNS::HELPERS::SnapToNearestTrack( m_end, aBoard, coupledNet, nullptr );
869
870 return initBaseLine( aRouter, aPNSLayer, aBoard, coupledStart, coupledEnd, coupledNet,
872 }
873
874 return false;
875 }
876
877 return true;
878}
879
880
882{
883public:
885 m_pattern( aPattern ),
886 m_wasLocked( aPattern->IsLocked() )
887 {
888 m_pattern->SetLocked( false );
889 }
890
892 {
893 if( m_wasLocked )
894 m_pattern->SetLocked( true );
895 }
896
897private:
900};
901
902
903bool PCB_TUNING_PATTERN::removeToBaseline( PNS::ROUTER* aRouter, int aPNSLayer, SHAPE_LINE_CHAIN& aBaseLine )
904{
905 VECTOR2I startSnapPoint, endSnapPoint;
906
907 std::optional<PNS::LINE> pnsLine = getPNSLine( aBaseLine.CPoint( 0 ), aBaseLine.CLastPoint(), aRouter,
908 aPNSLayer, startSnapPoint, endSnapPoint );
909
910 wxCHECK( pnsLine, false );
911
914 SHAPE_LINE_CHAIN post;
915 pnsLine->CLine().Split( startSnapPoint, endSnapPoint, pre, mid, post );
916
917 for( PNS::LINKED_ITEM* li : pnsLine->Links() )
918 aRouter->GetInterface()->RemoveItem( li );
919
920 aRouter->GetWorld()->Remove( *pnsLine );
921
922 SHAPE_LINE_CHAIN straightChain;
923 straightChain.Append( pre );
924 straightChain.Append( aBaseLine );
925 straightChain.Append( post );
926 straightChain.Simplify();
927
928 PNS::LINE straightLine( *pnsLine, straightChain );
929
930 aRouter->GetWorld()->Add( straightLine, false );
931
932 for( PNS::LINKED_ITEM* li : straightLine.Links() )
933 aRouter->GetInterface()->AddItem( li );
934
935 return true;
936}
937
938
940{
941 UNLOCKER raiiUnlocker( this );
942 SetFlags( IN_EDIT );
943
944 aTool->Router()->SyncWorld();
945
946 PNS::ROUTER* router = aTool->Router();
947 PNS_KICAD_IFACE* iface = aTool->GetInterface();
948
949 aCommit->Remove( this );
950
951 aTool->ClearRouterChanges();
952
953 // PNS layers and PCB layers have different coding. so convert PCB layer to PNS layer
954 int pnslayer = iface->GetPNSLayerFromBoardLayer( GetLayer() );
955
956 if( baselineValid() )
957 {
958 bool success = true;
959
960 success &= removeToBaseline( router, pnslayer, *m_baseLine );
961
962 if( m_tuningMode == DIFF_PAIR )
963 success &= removeToBaseline( router, pnslayer, *m_baseLineCoupled );
964
965 if( !success )
966 recoverBaseline( router );
967 }
968
969 const std::vector<GENERATOR_PNS_CHANGES>& allPnsChanges = aTool->GetRouterChanges();
970
971 for( const GENERATOR_PNS_CHANGES& pnsChanges : allPnsChanges )
972 {
973 const std::set<BOARD_ITEM*> routerRemovedItems = pnsChanges.removedItems;
974 const std::set<BOARD_ITEM*> routerAddedItems = pnsChanges.addedItems;
975
976 /*std::cout << "Push commits << " << allPnsChanges.size() << " routerRemovedItems "
977 << routerRemovedItems.size() << " routerAddedItems " << routerAddedItems.size()
978 << " m_removedItems " << m_removedItems.size() << std::endl;*/
979
980 for( BOARD_ITEM* item : routerRemovedItems )
981 {
982 item->ClearSelected();
983 aCommit->Remove( item );
984 }
985
986 for( BOARD_ITEM* item : routerAddedItems )
987 aCommit->Add( item );
988 }
989}
990
991
993{
994 PNS::SOLID queryItem;
995
997 queryItem.SetShape( chain ); // PNS::SOLID takes ownership
998 queryItem.SetLayer( m_layer );
999
1000 int lineWidth = 0;
1001
1002 PNS::NODE::OBSTACLES obstacles;
1004 opts.m_useClearanceEpsilon = false;
1005
1006 PNS::NODE* world = aRouter->GetWorld();
1007 PNS::NODE* branch = world->Branch();
1008
1009 branch->QueryColliding( &queryItem, obstacles, opts );
1010
1011 for( const PNS::OBSTACLE& obs : obstacles )
1012 {
1013 PNS::ITEM* item = obs.m_item;
1014
1016 continue;
1017
1018 if( PNS::LINKED_ITEM* li = dynamic_cast<PNS::LINKED_ITEM*>( item ) )
1019 {
1020 if( lineWidth == 0 || li->Width() < lineWidth )
1021 lineWidth = li->Width();
1022 }
1023
1024 if( chain->PointInside( item->Anchor( 0 ), 10 )
1025 && chain->PointInside( item->Anchor( 1 ), 10 ) )
1026 {
1027 branch->Remove( item );
1028 }
1029 }
1030
1031 if( lineWidth == 0 )
1032 lineWidth = pcbIUScale.mmToIU( 0.1 ); // Fallback
1033
1034 if( baselineValid() )
1035 {
1036 NETINFO_ITEM* recoverNet = GetBoard()->FindNet( m_lastNetName );
1037 PNS::LINE recoverLine;
1038
1039 recoverLine.SetLayer( m_layer );
1040 recoverLine.SetWidth( lineWidth );
1041 recoverLine.Line() = *m_baseLine;
1042 recoverLine.SetNet( recoverNet );
1043 branch->Add( recoverLine, false );
1044
1045 if( m_tuningMode == DIFF_PAIR )
1046 {
1047 NETINFO_ITEM* recoverCoupledNet = GetBoard()->DpCoupledNet( recoverNet );
1048 PNS::LINE recoverLineCoupled;
1049
1050 recoverLineCoupled.SetLayer( m_layer );
1051 recoverLineCoupled.SetWidth( lineWidth );
1052 recoverLineCoupled.Line() = *m_baseLineCoupled;
1053 recoverLineCoupled.SetNet( recoverCoupledNet );
1054 branch->Add( recoverLineCoupled, false );
1055 }
1056 }
1057
1058 aRouter->CommitRouting( branch );
1059
1060 //wxLogWarning( "PNS baseline recovered" );
1061
1062 return true;
1063}
1064
1065
1067 bool aPrimary )
1068{
1069 PNS_KICAD_IFACE* iface = aTool->GetInterface();
1070 PNS::ROUTER* router = aTool->Router();
1071 PNS::NODE* world = router->GetWorld();
1072 VECTOR2I startSnapPoint, endSnapPoint;
1073
1074 std::optional<PNS::LINE> pnsLine = getPNSLine( aBaseLine.CPoint( 0 ), aBaseLine.CLastPoint(), router,
1075 aPNSLayer, startSnapPoint, endSnapPoint );
1076
1077 if( !pnsLine )
1078 {
1079 // TODO
1080 //recoverBaseline( aRouter );
1081 return true;
1082 }
1083
1084 PNS::NODE* branch = world->Branch();
1085
1086 SHAPE_LINE_CHAIN straightChain;
1087 {
1088 SHAPE_LINE_CHAIN pre, mid, post;
1089 pnsLine->CLine().Split( startSnapPoint, endSnapPoint, pre, mid, post );
1090
1091 straightChain.Append( pre );
1092 straightChain.Append( aBaseLine );
1093 straightChain.Append( post );
1094 straightChain.Simplify();
1095 }
1096
1097 branch->Remove( *pnsLine );
1098
1099 SHAPE_LINE_CHAIN newLineChain;
1100
1101 if( aPrimary )
1102 {
1103 m_origin = straightChain.NearestPoint( m_origin );
1104 m_end = straightChain.NearestPoint( m_end );
1105
1106 // Don't allow points too close
1107 if( ( m_end - m_origin ).EuclideanNorm() < pcbIUScale.mmToIU( 0.1 ) )
1108 {
1109 m_origin = startSnapPoint;
1110 m_end = endSnapPoint;
1111 }
1112
1113 {
1114 SHAPE_LINE_CHAIN pre, mid, post;
1115 straightChain.Split( m_origin, m_end, pre, mid, post );
1116
1117 newLineChain.Append( pre );
1118 newLineChain.Append( mid );
1119 newLineChain.Append( post );
1120
1121 m_baseLine = mid;
1122 }
1123 }
1124 else
1125 {
1126 VECTOR2I start = straightChain.NearestPoint( m_origin );
1127 VECTOR2I end = straightChain.NearestPoint( m_end );
1128
1129 {
1130 SHAPE_LINE_CHAIN pre, mid, post;
1131 straightChain.Split( start, end, pre, mid, post );
1132
1133 newLineChain.Append( pre );
1134 newLineChain.Append( mid );
1135 newLineChain.Append( post );
1136
1137 m_baseLineCoupled = mid;
1138 }
1139 }
1140
1141 PNS::LINE newLine( *pnsLine, newLineChain );
1142
1143 branch->Add( newLine, false );
1144 router->CommitRouting( branch );
1145
1146 int clearance = router->GetRuleResolver()->Clearance( &newLine, nullptr );
1147
1148 iface->DisplayItem( &newLine, clearance, true, PNS_COLLISION );
1149
1150 return true;
1151}
1152
1153
1155{
1156 if( !( GetFlags() & IN_EDIT ) )
1157 return false;
1158
1159 UNLOCKER raiiUnlocker( this ); // Unlock the pattern for editing
1160
1161 KIGFX::VIEW* view = aTool->GetManager()->GetView();
1162 PNS::ROUTER* router = aTool->Router();
1163 PNS_KICAD_IFACE* iface = aTool->GetInterface();
1164 PCB_LAYER_ID pcblayer = GetLayer();
1165
1166 auto hideRemovedItems = [&]( bool aHide )
1167 {
1168 if( view )
1169 {
1170 for( const GENERATOR_PNS_CHANGES& pnsCommit : aTool->GetRouterChanges() )
1171 {
1172 for( BOARD_ITEM* item : pnsCommit.removedItems )
1173 {
1174 if( view )
1175 view->Hide( item, aHide, aHide );
1176 }
1177 }
1178 }
1179 };
1180
1181 iface->SetStartLayerFromPCBNew( pcblayer );
1182
1183 if( router->RoutingInProgress() )
1184 {
1185 router->StopRouting();
1186 }
1187
1188 // PNS layers and PCB layers have different coding. so convert PCB layer to PNS layer
1189 int pnslayer = iface->GetPNSLayerFromBoardLayer( pcblayer );
1190
1191 if( !baselineValid() )
1192 {
1193 initBaseLines( router, pnslayer, aBoard );
1194 }
1195 else
1196 {
1197 if( resetToBaseline( aTool, pnslayer, *m_baseLine, true ) )
1198 {
1199 m_origin = m_baseLine->CPoint( 0 );
1200 m_end = m_baseLine->CLastPoint();
1201 }
1202 else
1203 {
1204 //initBaseLines( router, layer, aBoard );
1205 return false;
1206 }
1207
1208 if( m_tuningMode == DIFF_PAIR )
1209 {
1210 if( !resetToBaseline( aTool, pnslayer, *m_baseLineCoupled, false ) )
1211 {
1212 initBaseLines( router, pnslayer, aBoard );
1213 return false;
1214 }
1215 }
1216 }
1217
1218 hideRemovedItems( true );
1219 // Snap points
1220 VECTOR2I startSnapPoint, endSnapPoint;
1221
1222 wxCHECK( m_baseLine, false );
1223
1224 PNS::LINKED_ITEM* startItem = PNS::HELPERS::PickSegment( router, m_origin, pnslayer, startSnapPoint, *m_baseLine );
1225 PNS::LINKED_ITEM* endItem = PNS::HELPERS::PickSegment( router, m_end, pnslayer, endSnapPoint, *m_baseLine );
1226
1227 wxASSERT( startItem );
1228 wxASSERT( endItem );
1229
1230 if( !startItem || !endItem )
1231 return false;
1232
1233 router->SetMode( GetPNSMode() );
1234
1235 if( !router->StartRouting( startSnapPoint, startItem, pnslayer ) )
1236 {
1237 //recoverBaseline( router );
1238 return false;
1239 }
1240
1241 PNS::MEANDER_PLACER_BASE* placer = static_cast<PNS::MEANDER_PLACER_BASE*>( router->Placer() );
1242
1243 m_settings.m_keepEndpoints = true; // Required for re-grouping
1244 placer->UpdateSettings( m_settings );
1245
1246 router->Move( m_end, nullptr );
1247
1248 if( PNS::DP_MEANDER_PLACER* dpPlacer = dynamic_cast<PNS::DP_MEANDER_PLACER*>( placer ) )
1249 {
1250 m_trackWidth = dpPlacer->GetOriginPair().Width();
1251 m_diffPairGap = dpPlacer->GetOriginPair().Gap();
1252 }
1253 else
1254 {
1255 m_trackWidth = startItem->Width();
1256 m_diffPairGap = router->Sizes().DiffPairGap();
1257 }
1258
1259 m_settings = placer->MeanderSettings();
1260 m_lastNetName = iface->GetNetName( startItem->Net() );
1261 m_tuningStatus = placer->TuningStatus();
1263
1264 wxString statusMessage;
1265
1266 switch( m_tuningStatus )
1267 {
1268 case PNS::MEANDER_PLACER_BASE::TOO_LONG: statusMessage = _( "too long" ); break;
1269 case PNS::MEANDER_PLACER_BASE::TOO_SHORT: statusMessage = _( "too short" ); break;
1270 case PNS::MEANDER_PLACER_BASE::TUNED: statusMessage = _( "tuned" ); break;
1271 default: statusMessage = _( "unknown" ); break;
1272 }
1273
1274 wxString result;
1275 EDA_UNITS userUnits = EDA_UNITS::MM;
1276
1277 if( aTool->GetManager()->GetSettings() )
1278 userUnits = static_cast<EDA_UNITS>( aTool->GetManager()->GetSettings()->m_System.units );
1279
1280 if( m_settings.m_isTimeDomain )
1281 {
1283 (double) m_tuningLength );
1284 }
1285 else
1286 {
1288 (double) m_tuningLength );
1289 }
1290
1291 m_tuningInfo.Printf( wxS( "%s (%s)" ), result, statusMessage );
1292
1293 return true;
1294}
1295
1296
1298{
1299 if( !( GetFlags() & IN_EDIT ) )
1300 return;
1301
1302 ClearFlags( IN_EDIT ); // Clear the editing flag
1303
1304 KIGFX::VIEW* view = aTool->GetManager()->GetView();
1305 PNS::ROUTER* router = aTool->Router();
1306 PNS_KICAD_IFACE* iface = aTool->GetInterface();
1307 SHAPE_LINE_CHAIN bounds = getOutline();
1308 int epsilon = aBoard->GetDesignSettings().GetDRCEpsilon();
1309
1310 iface->EraseView();
1311
1312 if( router->RoutingInProgress() )
1313 {
1314 bool forceFinish = true;
1315 bool forceCommit = false;
1316
1317 router->FixRoute( m_end, nullptr, forceFinish, forceCommit );
1318 router->StopRouting();
1319 }
1320
1321 const std::vector<GENERATOR_PNS_CHANGES>& pnsCommits = aTool->GetRouterChanges();
1322
1323 for( const GENERATOR_PNS_CHANGES& pnsCommit : pnsCommits )
1324 {
1325 const std::set<BOARD_ITEM*> routerRemovedItems = pnsCommit.removedItems;
1326 const std::set<BOARD_ITEM*> routerAddedItems = pnsCommit.addedItems;
1327
1328 //std::cout << "Push commits << " << allPnsChanges.size() << " routerRemovedItems "
1329 // << routerRemovedItems.size() << " routerAddedItems " << routerAddedItems.size()
1330 // << " m_removedItems " << m_removedItems.size() << std::endl;
1331
1332 for( BOARD_ITEM* item : routerRemovedItems )
1333 {
1334 if( view )
1335 view->Hide( item, false );
1336
1337 aCommit->Remove( item );
1338 }
1339
1340 for( BOARD_ITEM* item : routerAddedItems )
1341 {
1342 aCommit->Add( item );
1343
1344 if( PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( item ) )
1345 {
1346 if( bounds.PointInside( track->GetStart(), epsilon )
1347 && bounds.PointInside( track->GetEnd(), epsilon ) )
1348 {
1349 AddItem( item );
1350 }
1351 }
1352 }
1353 }
1354}
1355
1356
1358{
1359 if( !( GetFlags() & IN_EDIT ) )
1360 return;
1361
1363
1364 PNS_KICAD_IFACE* iface = aTool->GetInterface();
1365
1366 iface->EraseView();
1367
1368 if( KIGFX::VIEW* view = aTool->GetManager()->GetView() )
1369 {
1370 for( const GENERATOR_PNS_CHANGES& pnsCommit : aTool->GetRouterChanges() )
1371 {
1372 for( BOARD_ITEM* item : pnsCommit.removedItems )
1373 view->Hide( item, false );
1374 }
1375 }
1376
1377 aTool->Router()->StopRouting();
1378}
1379
1380
1382{
1383 VECTOR2I centerlineOffset;
1384 VECTOR2I centerlineOffsetEnd;
1385
1386 if( m_tuningMode == DIFF_PAIR && m_baseLineCoupled && m_baseLineCoupled->SegmentCount() > 0 )
1387 {
1388 centerlineOffset = ( m_baseLineCoupled->CPoint( 0 ) - m_origin ) / 2;
1389 centerlineOffsetEnd = ( m_baseLineCoupled->CLastPoint() - m_end ) / 2;
1390 }
1391
1392 aPoints.AddPoint( m_origin + centerlineOffset );
1393 aPoints.AddPoint( m_end + centerlineOffsetEnd );
1394
1395 SEG base = m_baseLine && m_baseLine->SegmentCount() > 0 ? m_baseLine->CSegment( 0 )
1396 : SEG( m_origin, m_end );
1397
1398 base.A += centerlineOffset;
1399 base.B += centerlineOffset;
1400
1401 int amplitude = m_settings.m_maxAmplitude + KiROUND( m_trackWidth / 2.0 );
1402
1403 if( m_tuningMode == DIFF_PAIR )
1404 amplitude += m_trackWidth + m_diffPairGap;
1405
1406 if( m_settings.m_initialSide == -1 )
1407 amplitude *= -1;
1408
1409 VECTOR2I widthHandleOffset = ( base.B - base.A ).Perpendicular().Resize( amplitude );
1410
1411 aPoints.AddPoint( base.A + widthHandleOffset );
1412 aPoints.Point( 2 ).SetGridConstraint( IGNORE_GRID );
1413
1414 VECTOR2I spacingHandleOffset =
1415 widthHandleOffset + ( base.B - base.A ).Resize( KiROUND( m_settings.m_spacing * 1.5 ) );
1416
1417 aPoints.AddPoint( base.A + spacingHandleOffset );
1418 aPoints.Point( 3 ).SetGridConstraint( IGNORE_GRID );
1419
1420 return true;
1421}
1422
1423
1425{
1426 VECTOR2I centerlineOffset;
1427 VECTOR2I centerlineOffsetEnd;
1428
1429 if( m_tuningMode == DIFF_PAIR && m_baseLineCoupled && m_baseLineCoupled->SegmentCount() > 0 )
1430 {
1431 centerlineOffset = ( m_baseLineCoupled->CPoint( 0 ) - m_origin ) / 2;
1432 centerlineOffsetEnd = ( m_baseLineCoupled->CLastPoint() - m_end ) / 2;
1433 }
1434
1435 SEG base = m_baseLine && m_baseLine->SegmentCount() > 0 ? m_baseLine->CSegment( 0 )
1436 : SEG( m_origin, m_end );
1437
1438 base.A += centerlineOffset;
1439 base.B += centerlineOffset;
1440
1441 m_origin = aEditPoints.Point( 0 ).GetPosition() - centerlineOffset;
1442 m_end = aEditPoints.Point( 1 ).GetPosition() - centerlineOffsetEnd;
1443
1444 if( aEditPoints.Point( 2 ).IsActive() )
1445 {
1446 VECTOR2I wHandle = aEditPoints.Point( 2 ).GetPosition();
1447
1448 int value = base.LineDistance( wHandle );
1449
1450 value -= KiROUND( m_trackWidth / 2.0 );
1451
1452 if( m_tuningMode == DIFF_PAIR )
1453 value -= m_trackWidth + m_diffPairGap;
1454
1455 SetMaxAmplitude( KiROUND( value / pcbIUScale.mmToIU( 0.01 ) ) * pcbIUScale.mmToIU( 0.01 ) );
1456
1457 int side = base.Side( wHandle );
1458
1459 if( side < 0 )
1460 m_settings.m_initialSide = PNS::MEANDER_SIDE_LEFT;
1461 else
1462 m_settings.m_initialSide = PNS::MEANDER_SIDE_RIGHT;
1463 }
1464
1465 if( aEditPoints.Point( 3 ).IsActive() )
1466 {
1467 VECTOR2I wHandle = aEditPoints.Point( 2 ).GetPosition();
1468 VECTOR2I sHandle = aEditPoints.Point( 3 ).GetPosition();
1469
1470 int value = KiROUND( SEG( base.A, wHandle ).LineDistance( sHandle ) / 1.5 );
1471
1472 SetSpacing( KiROUND( value / pcbIUScale.mmToIU( 0.01 ) ) * pcbIUScale.mmToIU( 0.01 ) );
1473 }
1474
1475 return true;
1476}
1477
1478
1480{
1481 VECTOR2I centerlineOffset;
1482 VECTOR2I centerlineOffsetEnd;
1483
1484 if( m_tuningMode == DIFF_PAIR && m_baseLineCoupled && m_baseLineCoupled->SegmentCount() > 0 )
1485 {
1486 centerlineOffset = ( m_baseLineCoupled->CPoint( 0 ) - m_origin ) / 2;
1487 centerlineOffsetEnd = ( m_baseLineCoupled->CLastPoint() - m_end ) / 2;
1488 }
1489
1490 SEG base = m_baseLine && m_baseLine->SegmentCount() > 0 ? m_baseLine->CSegment( 0 )
1491 : SEG( m_origin, m_end );
1492
1493 base.A += centerlineOffset;
1494 base.B += centerlineOffset;
1495
1496 int amplitude = m_settings.m_maxAmplitude + KiROUND( m_trackWidth / 2.0 );
1497
1498 if( m_tuningMode == DIFF_PAIR )
1499 amplitude += m_trackWidth + m_diffPairGap;
1500
1501 if( m_settings.m_initialSide == -1 )
1502 amplitude *= -1;
1503
1504 VECTOR2I widthHandleOffset = ( base.B - base.A ).Perpendicular().Resize( amplitude );
1505
1506 aEditPoints.Point( 0 ).SetPosition( m_origin + centerlineOffset );
1507 aEditPoints.Point( 1 ).SetPosition( m_end + centerlineOffsetEnd );
1508
1509 aEditPoints.Point( 2 ).SetPosition( base.A + widthHandleOffset );
1510
1511 VECTOR2I spacingHandleOffset =
1512 widthHandleOffset + ( base.B - base.A ).Resize( KiROUND( m_settings.m_spacing * 1.5 ) );
1513
1514 aEditPoints.Point( 3 ).SetPosition( base.A + spacingHandleOffset );
1515
1516 return true;
1517}
1518
1519
1521{
1522 if( m_baseLine )
1523 {
1524 int clampedMaxAmplitude = m_settings.m_maxAmplitude;
1525 int minAllowedAmplitude = 0;
1526 int baselineOffset = m_tuningMode == DIFF_PAIR ? ( m_diffPairGap + m_trackWidth ) / 2 : 0;
1527
1529 {
1530 minAllowedAmplitude = baselineOffset + m_trackWidth;
1531 }
1532 else
1533 {
1534 int correction = m_trackWidth * tan( 1 - tan( DEG2RAD( 22.5 ) ) );
1535 minAllowedAmplitude = baselineOffset + correction;
1536 }
1537
1538 clampedMaxAmplitude = std::max( clampedMaxAmplitude, minAllowedAmplitude );
1539
1540 if( m_settings.m_singleSided )
1541 {
1542 SHAPE_LINE_CHAIN clBase = *m_baseLine;
1544
1545 if( m_tuningMode != DIFF_PAIR )
1546 {
1547 int amplitude = clampedMaxAmplitude + KiROUND( m_trackWidth / 2.0 );
1548
1550
1552 true ) )
1553 {
1554 chain.Append( m_settings.m_initialSide >= 0 ? right : left );
1555 chain.Append( clBase.Reverse() );
1556 chain.SetClosed( true );
1557
1558 return chain;
1559 }
1560 }
1562 {
1563 int amplitude = clampedMaxAmplitude + m_trackWidth + KiROUND( m_diffPairGap / 2.0 );
1564
1566 SHAPE_LINE_CHAIN chain1, chain2;
1567
1569 true ) )
1570 {
1571 if( m_settings.m_initialSide >= 0 )
1572 chain1.Append( right );
1573 else
1574 chain1.Append( left );
1575
1577 ARC_LOW_DEF, left, right, true ) )
1578 {
1579 if( m_settings.m_initialSide >= 0 )
1580 chain1.Append( left.Reverse() );
1581 else
1582 chain1.Append( right.Reverse() );
1583 }
1584
1585 chain1.SetClosed( true );
1586 }
1587
1589 true ) )
1590 {
1591 if( m_settings.m_initialSide >= 0 )
1592 chain2.Append( right );
1593 else
1594 chain2.Append( left );
1595
1597 ARC_LOW_DEF, left, right, true ) )
1598 {
1599 if( m_settings.m_initialSide >= 0 )
1600 chain2.Append( left.Reverse() );
1601 else
1602 chain2.Append( right.Reverse() );
1603 }
1604
1605 chain2.SetClosed( true );
1606 }
1607
1608 SHAPE_POLY_SET merged;
1609 merged.BooleanAdd( chain1, chain2 );
1610
1611 if( merged.OutlineCount() > 0 )
1612 return merged.Outline( 0 );
1613 }
1614 }
1615
1616 // Not single-sided / fallback
1617 SHAPE_POLY_SET poly;
1619
1620 int amplitude = 0;
1621
1622 if( m_tuningMode == DIFF_PAIR )
1623 amplitude = clampedMaxAmplitude + m_diffPairGap / 2 + KiROUND( m_trackWidth );
1624 else
1625 amplitude = clampedMaxAmplitude + KiROUND( m_trackWidth / 2.0 );
1626
1628
1630 {
1631 SHAPE_POLY_SET polyCoupled;
1633 ARC_LOW_DEF, false );
1634
1635 poly.ClearArcs();
1636 polyCoupled.ClearArcs();
1637
1638 SHAPE_POLY_SET merged;
1639 merged.BooleanAdd( poly, polyCoupled );
1640
1641 if( merged.OutlineCount() > 0 )
1642 return merged.Outline( 0 );
1643 }
1644
1645 if( poly.OutlineCount() > 0 )
1646 return poly.Outline( 0 );
1647 }
1648
1649 return SHAPE_LINE_CHAIN();
1650}
1651
1652
1653void PCB_TUNING_PATTERN::ViewDraw( int aLayer, KIGFX::VIEW* aView ) const
1654{
1655 if( !IsSelected() && !IsNew() )
1656 return;
1657
1658 KIGFX::PREVIEW::DRAW_CONTEXT ctx( *aView );
1659
1660 int size = KiROUND( aView->ToWorld( EDIT_POINT::POINT_SIZE ) * 0.8 );
1661 size = std::max( size, pcbIUScale.mmToIU( 0.05 ) );
1662
1663 if( !HasFlag( IN_EDIT ) )
1664 {
1665 if( m_baseLine )
1666 {
1667 for( int i = 0; i < m_baseLine->SegmentCount(); i++ )
1668 {
1669 SEG seg = m_baseLine->CSegment( i );
1670 ctx.DrawLineDashed( seg.A, seg.B, size, size / 6, true );
1671 }
1672 }
1673 else
1674 {
1675 ctx.DrawLineDashed( m_origin, m_end, size, size / 6, false );
1676 }
1677
1679 {
1680 for( int i = 0; i < m_baseLineCoupled->SegmentCount(); i++ )
1681 {
1682 SEG seg = m_baseLineCoupled->CSegment( i );
1683 ctx.DrawLineDashed( seg.A, seg.B, size, size / 6, true );
1684 }
1685 }
1686 }
1687
1689
1690 for( int i = 0; i < chain.SegmentCount(); i++ )
1691 {
1692 SEG seg = chain.Segment( i );
1693 ctx.DrawLineDashed( seg.A, seg.B, size, size / 2, false );
1694 }
1695}
1696
1697
1699{
1701
1702 props.set( "tuning_mode", tuningToString( m_tuningMode ) );
1703 props.set( "initial_side", sideToString( m_settings.m_initialSide ) );
1704 props.set( "last_status", statusToString( m_tuningStatus ) );
1705 props.set( "is_time_domain", m_settings.m_isTimeDomain );
1706
1707 props.set( "end", m_end );
1708 props.set( "corner_radius_percent", m_settings.m_cornerRadiusPercentage );
1709 props.set( "single_sided", m_settings.m_singleSided );
1710 props.set( "rounded", m_settings.m_cornerStyle == PNS::MEANDER_STYLE_ROUND );
1711
1712 props.set_iu( "max_amplitude", m_settings.m_maxAmplitude );
1713 props.set_iu( "min_amplitude", m_settings.m_minAmplitude );
1714 props.set_iu( "min_spacing", m_settings.m_spacing );
1715 props.set_iu( "target_length_min", m_settings.m_targetLength.Min() );
1716 props.set_iu( "target_length", m_settings.m_targetLength.Opt() );
1717 props.set_iu( "target_length_max", m_settings.m_targetLength.Max() );
1718 props.set_iu( "target_delay_min", m_settings.m_targetLengthDelay.Min() );
1719 props.set_iu( "target_delay", m_settings.m_targetLengthDelay.Opt() );
1720 props.set_iu( "target_delay_max", m_settings.m_targetLengthDelay.Max() );
1721 props.set_iu( "target_skew_min", m_settings.m_targetSkew.Min() );
1722 props.set_iu( "target_skew", m_settings.m_targetSkew.Opt() );
1723 props.set_iu( "target_skew_max", m_settings.m_targetSkew.Max() );
1724 props.set_iu( "last_track_width", m_trackWidth );
1725 props.set_iu( "last_diff_pair_gap", m_diffPairGap );
1726 props.set_iu( "last_tuning_length", m_tuningLength );
1727
1728 props.set( "last_netname", m_lastNetName );
1729 props.set( "override_custom_rules", m_settings.m_overrideCustomRules );
1730
1731 if( m_baseLine )
1732 props.set( "base_line", wxAny( *m_baseLine ) );
1733
1734 if( m_baseLineCoupled )
1735 props.set( "base_line_coupled", wxAny( *m_baseLineCoupled ) );
1736
1737 return props;
1738}
1739
1740
1742{
1744
1745 wxString tuningMode;
1746 aProps.get_to( "tuning_mode", tuningMode );
1747 m_tuningMode = tuningFromString( tuningMode.utf8_string() );
1748
1749 wxString side;
1750 aProps.get_to( "initial_side", side );
1751 m_settings.m_initialSide = sideFromString( side.utf8_string() );
1752
1753 wxString status;
1754 aProps.get_to( "last_status", status );
1755 m_tuningStatus = statusFromString( status.utf8_string() );
1756
1757 aProps.get_to( "is_time_domain", m_settings.m_isTimeDomain );
1758
1759 aProps.get_to( "end", m_end );
1760 aProps.get_to( "corner_radius_percent", m_settings.m_cornerRadiusPercentage );
1761 aProps.get_to( "single_sided", m_settings.m_singleSided );
1762 aProps.get_to( "side", m_settings.m_initialSide );
1763
1764 bool rounded = false;
1765 aProps.get_to( "rounded", rounded );
1767
1768 long long int val;
1769
1770 aProps.get_to_iu( "target_length", val );
1771 m_settings.SetTargetLength( val );
1772
1773 if( aProps.get_to_iu( "target_length_min", val ) )
1774 m_settings.m_targetLength.SetMin( val );
1775
1776 if( aProps.get_to_iu( "target_length_max", val ) )
1777 m_settings.m_targetLength.SetMax( val );
1778
1779 aProps.get_to_iu( "target_delay", val );
1780 m_settings.SetTargetLengthDelay( val );
1781
1782 if( aProps.get_to_iu( "target_delay_min", val ) )
1783 m_settings.m_targetLengthDelay.SetMin( val );
1784
1785 if( aProps.get_to_iu( "target_delay_max", val ) )
1786 m_settings.m_targetLengthDelay.SetMax( val );
1787
1788 int int_val;
1789
1790 aProps.get_to_iu( "target_skew", int_val );
1791 m_settings.SetTargetSkew( int_val );
1792
1793 if( aProps.get_to_iu( "target_skew_min", int_val ) )
1794 m_settings.m_targetSkew.SetMin( int_val );
1795
1796 if( aProps.get_to_iu( "target_skew_max", int_val ) )
1797 m_settings.m_targetSkew.SetMax( int_val );
1798
1799 aProps.get_to_iu( "max_amplitude", m_settings.m_maxAmplitude );
1800 aProps.get_to_iu( "min_amplitude", m_settings.m_minAmplitude );
1801 aProps.get_to_iu( "min_spacing", m_settings.m_spacing );
1802 aProps.get_to_iu( "last_track_width", m_trackWidth );
1803 aProps.get_to_iu( "last_diff_pair_gap", m_diffPairGap );
1804 aProps.get_to_iu( "last_tuning_length", m_tuningLength );
1805 aProps.get_to( "override_custom_rules", m_settings.m_overrideCustomRules );
1806
1807 aProps.get_to( "last_netname", m_lastNetName );
1808
1809 if( auto baseLine = aProps.get_opt<SHAPE_LINE_CHAIN>( "base_line" ) )
1810 m_baseLine = *baseLine;
1811
1812 if( auto baseLineCoupled = aProps.get_opt<SHAPE_LINE_CHAIN>( "base_line_coupled" ) )
1813 m_baseLineCoupled = *baseLineCoupled;
1814
1815 // Reconstruct m_tuningInfo from loaded length and status
1816 if( m_tuningLength != 0 )
1817 {
1818 wxString statusMessage;
1819
1820 switch( m_tuningStatus )
1821 {
1822 case PNS::MEANDER_PLACER_BASE::TOO_LONG: statusMessage = _( "too long" ); break;
1823 case PNS::MEANDER_PLACER_BASE::TOO_SHORT: statusMessage = _( "too short" ); break;
1824 case PNS::MEANDER_PLACER_BASE::TUNED: statusMessage = _( "tuned" ); break;
1825 default: statusMessage = _( "unknown" ); break;
1826 }
1827
1828 EDA_UNITS units = m_settings.m_isTimeDomain ? EDA_UNITS::PS : EDA_UNITS::MM;
1829 wxString lengthStr = EDA_UNIT_UTILS::UI::MessageTextFromValue( pcbIUScale, units,
1830 (double) m_tuningLength );
1831
1832 m_tuningInfo.Printf( wxS( "%s (%s)" ), lengthStr, statusMessage );
1833 }
1834}
1835
1836
1838{
1840 DRC_CONSTRAINT constraint;
1841
1842 if( !m_items.empty() )
1843 {
1844 BOARD_ITEM* startItem = static_cast<BOARD_ITEM*>( *m_items.begin() );
1845 std::shared_ptr<DRC_ENGINE>& drcEngine = GetBoard()->GetDesignSettings().m_DRCEngine;
1846
1848 {
1849 constraint = drcEngine->EvalRules( SKEW_CONSTRAINT, startItem, nullptr, GetLayer() );
1850
1851 if( !constraint.IsNull() && !settings.m_overrideCustomRules )
1852 {
1854 {
1855 settings.SetTargetSkewDelay( constraint.GetValue() );
1856 settings.SetTargetSkew( MINOPTMAX<int>() );
1857 settings.m_isTimeDomain = true;
1858 }
1859 else
1860 {
1861 settings.SetTargetSkewDelay( MINOPTMAX<int>() );
1862 settings.SetTargetSkew( constraint.GetValue() );
1863 settings.m_isTimeDomain = false;
1864 }
1865 }
1866 }
1867 else
1868 {
1869 // Prefer chain-level constraint if net is part of a chain
1870 constraint = drcEngine->EvalRules( NET_CHAIN_LENGTH_CONSTRAINT, startItem, nullptr, GetLayer() );
1871
1872 if( ( constraint.IsNull() || constraint.GetSeverity() == RPT_SEVERITY_IGNORE ) )
1873 constraint = drcEngine->EvalRules( LENGTH_CONSTRAINT, startItem, nullptr, GetLayer() );
1874
1875 if( !constraint.IsNull() && !settings.m_overrideCustomRules )
1876 {
1878 {
1879 settings.SetTargetLengthDelay( constraint.GetValue() );
1880 settings.SetTargetLength( MINOPTMAX<int>() );
1881 settings.m_isTimeDomain = true;
1882 }
1883 else
1884 {
1886 settings.SetTargetLength( constraint.GetValue() );
1887 settings.m_isTimeDomain = false;
1888 }
1889 }
1890 }
1891 }
1892
1893 DIALOG_TUNING_PATTERN_PROPERTIES dlg( aEditFrame, settings, GetPNSMode(), constraint );
1894
1895 if( dlg.ShowModal() == wxID_OK )
1896 {
1897 BOARD_COMMIT commit( aEditFrame );
1898 commit.Modify( this );
1899 m_settings = settings;
1900
1901 GENERATOR_TOOL* generatorTool = aEditFrame->GetToolManager()->GetTool<GENERATOR_TOOL>();
1902 EditStart( generatorTool, GetBoard(), &commit );
1903 Update( generatorTool, GetBoard(), &commit );
1904 EditFinish( generatorTool, GetBoard(), &commit );
1905
1906 commit.Push( _( "Edit Tuning Pattern" ) );
1907 }
1908}
1909
1910
1912 PCB_BASE_EDIT_FRAME* aFrame,
1913 bool aStatusItemsOnly )
1914{
1915 std::vector<EDA_ITEM*> previewItems;
1916 KIGFX::VIEW* view = aFrame->GetCanvas()->GetView();
1917
1918 if( auto* placer = dynamic_cast<PNS::MEANDER_PLACER_BASE*>( aTool->Router()->Placer() ) )
1919 {
1920 if( !aStatusItemsOnly )
1921 {
1922 PNS::ITEM_SET items = placer->TunedPath();
1923
1924 for( PNS::ITEM* item : items )
1925 previewItems.push_back( new ROUTER_PREVIEW_ITEM( item,
1926 aTool->Router()->GetInterface(),
1927 view, PNS_HOVER_ITEM ) );
1928 }
1929
1930 TUNING_STATUS_VIEW_ITEM* statusItem = new TUNING_STATUS_VIEW_ITEM( aFrame );
1931
1932 // Build first line: "Net-(R1-Pad2) | Chain: Chain1" OR just net if no chain
1933 wxString scopeLine;
1934 BOARD* board = GetBoard();
1935 wxString netName = m_lastNetName;
1936 wxString netChainName;
1937 if( board && !netName.IsEmpty() )
1938 {
1939 for( NETINFO_ITEM* net : board->GetNetInfo() )
1940 {
1941 if( UnescapeString( net->GetNetname() ) == netName )
1942 {
1943 netChainName = net->GetNetChain();
1944 break;
1945 }
1946 }
1947 }
1948 if( !netName.IsEmpty() )
1949 {
1950 if( !netChainName.IsEmpty() )
1951 scopeLine = wxString::Format( _( "%s | %s" ), netName, netChainName );
1952 else
1953 scopeLine = netName;
1954 }
1955 statusItem->SetScopeLine( scopeLine );
1956
1958 {
1959 if( m_settings.m_isTimeDomain )
1960 statusItem->SetMinMax( m_settings.m_targetSkewDelay.Min(), m_settings.m_targetSkewDelay.Max() );
1961 else
1962 statusItem->SetMinMax( m_settings.m_targetSkew.Min(), m_settings.m_targetSkew.Max() );
1963 }
1964 else
1965 {
1966 // Show the per-net LENGTH_CONSTRAINT directly from DRC, not the blended
1967 // budget in m_targetLength (which may include chain adjustments).
1968 bool hasNetConstraint = false;
1969
1970 if( board && board->GetDesignSettings().m_DRCEngine )
1971 {
1972 PCB_TRACK* netRepTrack = nullptr;
1973
1974 for( BOARD_ITEM* bi : board->Tracks() )
1975 {
1976 if( PCB_TRACK* tr = dynamic_cast<PCB_TRACK*>( bi ) )
1977 {
1978 if( tr->GetNet() && UnescapeString( tr->GetNet()->GetNetname() ) == netName )
1979 {
1980 netRepTrack = tr;
1981 break;
1982 }
1983 }
1984 }
1985
1986 if( netRepTrack )
1987 {
1989 LENGTH_CONSTRAINT, netRepTrack, nullptr, netRepTrack->GetLayer() );
1990
1991 if( !netC.IsNull() && netC.GetSeverity() != RPT_SEVERITY_IGNORE )
1992 {
1993 statusItem->SetMinMax(
1994 static_cast<double>( netC.GetValue().Min() ),
1995 static_cast<double>( netC.GetValue().Max() ) );
1996 hasNetConstraint = true;
1997 }
1998 }
1999 }
2000
2001 if( !hasNetConstraint )
2002 statusItem->ClearMinMax();
2003 }
2004
2005 // Set chain-level min/max from raw chain constraint
2006 if( !netChainName.IsEmpty() && board && board->GetDesignSettings().m_DRCEngine )
2007 {
2008 // Find a track on this net to evaluate the rule against
2009 PCB_TRACK* repTrack = nullptr;
2010
2011 for( BOARD_ITEM* bi : board->Tracks() )
2012 {
2013 if( PCB_TRACK* tr = dynamic_cast<PCB_TRACK*>( bi ) )
2014 {
2015 NETINFO_ITEM* ni = tr->GetNet();
2016
2017 if( ni && ni->GetNetChain() == netChainName )
2018 {
2019 repTrack = tr;
2020 break;
2021 }
2022 }
2023 }
2024
2025 if( repTrack )
2026 {
2028 NET_CHAIN_LENGTH_CONSTRAINT, repTrack, nullptr, repTrack->GetLayer() );
2029
2030 if( !chainC.IsNull() && chainC.GetSeverity() != RPT_SEVERITY_IGNORE )
2031 {
2032 statusItem->SetChainMinMax(
2033 static_cast<double>( chainC.GetValue().Min() ),
2034 static_cast<double>( chainC.GetValue().Max() ) );
2035 }
2036 else
2037 {
2038 statusItem->ClearChainMinMax();
2039 }
2040 }
2041 else
2042 {
2043 statusItem->ClearChainMinMax();
2044 }
2045 }
2046 else
2047 {
2048 statusItem->ClearChainMinMax();
2049 }
2050
2051 statusItem->SetIsTimeDomain( m_settings.m_isTimeDomain );
2052
2053 // Header label line (line 2)
2055 statusItem->SetCurrent( 0.0, _( "current skew" ) );
2056 else if( m_settings.m_isTimeDomain )
2057 statusItem->SetCurrent( 0.0, _( "current delay" ) );
2058 else
2059 statusItem->SetCurrent( 0.0, _( "current length" ) );
2060
2061 // Value lines (lines 3 & 4)
2063 double netVal = m_settings.m_isTimeDomain ? static_cast<double>( placer->TuningDelayResult() )
2064 : static_cast<double>( placer->TuningLengthResult() );
2065 wxString netStr = wxString::Format( _( "Net: %s" ),
2066 aFrame->MessageTextFromValue( netVal, true, unitType ) );
2067
2068 // Chain total from board state (GetTrackLength for every net) plus live tuning delta.
2069 wxString sigStr;
2070 bool hasSignal = false;
2071 if( !netChainName.IsEmpty() && board )
2072 {
2073 double chainBoardLen = 0.0;
2074 double chainBoardDelay = 0.0;
2075
2076 // Index tracks by netcode once so each chain member is an O(1) lookup
2077 // rather than re-scanning BOARD::Tracks() per net.
2078 std::unordered_map<int, PCB_TRACK*> repByNet;
2079
2080 for( BOARD_ITEM* bi : board->Tracks() )
2081 {
2082 if( PCB_TRACK* tr = dynamic_cast<PCB_TRACK*>( bi ) )
2083 repByNet.emplace( tr->GetNetCode(), tr );
2084 }
2085
2086 for( NETINFO_ITEM* net : board->GetNetInfo() )
2087 {
2088 if( net->GetNetChain() != netChainName )
2089 continue;
2090
2091 auto it = repByNet.find( net->GetNetCode() );
2092
2093 if( it != repByNet.end() && it->second )
2094 {
2095 int cnt = 0; double trk = 0, pd = 0, tDel = 0, pdDel = 0;
2096 std::tie( cnt, trk, pd, tDel, pdDel ) = board->GetTrackLength( *it->second );
2097 chainBoardLen += trk + pd;
2098 chainBoardDelay += tDel + pdDel;
2099 }
2100 }
2101
2102 // Add the meander extension delta (path-independent).
2103 double tuningDelta = 0.0;
2104
2105 if( placer->HasBaseline() )
2106 {
2107 tuningDelta = m_settings.m_isTimeDomain
2108 ? static_cast<double>( placer->TuningDelayDelta() )
2109 : static_cast<double>( placer->TuningLengthDelta() );
2110 }
2111
2112 double sigVal = ( m_settings.m_isTimeDomain ? chainBoardDelay : chainBoardLen )
2113 + tuningDelta;
2114
2115 // Bridging: pad-to-pad gaps through series components.
2116 double delayIU = 0.0;
2117 long long bridging = GetCachedBridgingLength( board, netChainName, &delayIU );
2118
2119 if( bridging > 0 )
2120 {
2121 if( m_settings.m_isTimeDomain )
2122 sigVal += delayIU;
2123 else
2124 sigVal += static_cast<double>( bridging );
2125 }
2126
2127 sigStr = wxString::Format( _( "Chain: %s" ),
2128 aFrame->MessageTextFromValue( sigVal, true, unitType ) );
2129 hasSignal = true;
2130 }
2131 statusItem->SetNetAndSignalValues( netStr, sigStr, hasSignal );
2132
2133 statusItem->SetPosition( aFrame->GetToolManager()->GetMousePosition() );
2134 previewItems.push_back( statusItem );
2135 }
2136
2137 return previewItems;
2138}
2139
2140
2142 std::vector<MSG_PANEL_ITEM>& aList )
2143{
2144 wxString msg;
2145 NETINFO_ITEM* primaryNet = nullptr;
2146 NETINFO_ITEM* coupledNet = nullptr;
2147 PCB_TRACK* primaryItem = nullptr;
2148 PCB_TRACK* coupledItem = nullptr;
2149 NETCLASS* netclass = nullptr;
2150 int width = 0;
2151 bool mixedWidth = false;
2152
2154
2155 aList.emplace_back( _( "Type" ), GetFriendlyName() );
2156
2157 for( EDA_ITEM* member : GetItems() )
2158 {
2159 if( PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( member ) )
2160 {
2161 if( !primaryNet )
2162 {
2163 primaryItem = track;
2164 primaryNet = track->GetNet();
2165 }
2166 else if( !coupledNet && track->GetNet() != primaryNet )
2167 {
2168 coupledItem = track;
2169 coupledNet = track->GetNet();
2170 }
2171
2172 if( !netclass )
2173 netclass = track->GetEffectiveNetClass();
2174
2175 if( !width )
2176 width = track->GetWidth();
2177 else if( width != track->GetWidth() )
2178 mixedWidth = true;
2179 }
2180 }
2181
2182 if( coupledNet )
2183 {
2184 aList.emplace_back( _( "Nets" ), UnescapeString( primaryNet->GetNetname() )
2185 + wxS( ", " )
2186 + UnescapeString( coupledNet->GetNetname() ) );
2187 }
2188 else if( primaryNet )
2189 {
2190 aList.emplace_back( _( "Net" ), UnescapeString( primaryNet->GetNetname() ) );
2191 }
2192
2193 if( netclass )
2194 aList.emplace_back( _( "Resolved Netclass" ),
2195 UnescapeString( netclass->GetHumanReadableName() ) );
2196
2197 aList.emplace_back( _( "Layer" ), LayerMaskDescribe() );
2198
2199 // Show chain name if available
2200 if( primaryNet && !primaryNet->GetNetChain().IsEmpty() )
2201 aList.emplace_back( _( "Net Chain" ), primaryNet->GetNetChain() );
2202
2203 if( width && !mixedWidth )
2204 aList.emplace_back( _( "Width" ), aFrame->MessageTextFromValue( width ) );
2205
2206 BOARD* board = GetBoard();
2207 std::shared_ptr<DRC_ENGINE>& drcEngine = board->GetDesignSettings().m_DRCEngine;
2208 DRC_CONSTRAINT constraint;
2209
2210 // Display full track length (in Pcbnew)
2211 if( board && primaryItem && primaryItem->GetNetCode() > 0 )
2212 {
2213 int count = 0;
2214 double trackLen = 0.0;
2215 double lenPadToDie = 0.0;
2216 double trackDelay = 0.0;
2217 double delayPadToDie = 0.0;
2218
2219 std::tie( count, trackLen, lenPadToDie, trackDelay, delayPadToDie ) = board->GetTrackLength( *primaryItem );
2220
2221 if( coupledItem && coupledItem->GetNetCode() > 0 )
2222 {
2223 double coupledLen = 0.0;
2224 double coupledLenPadToDie = 0.0;
2225 double coupledTrackDelay = 0.0;
2226 double doubledDelayPadToDie = 0.0;
2227
2228 std::tie( count, coupledLen, coupledLenPadToDie, coupledTrackDelay, doubledDelayPadToDie ) =
2229 board->GetTrackLength( *coupledItem );
2230
2231 if( trackDelay == 0.0 || coupledTrackDelay == 0.0 )
2232 {
2233 aList.emplace_back( _( "Routed Lengths" ), aFrame->MessageTextFromValue( trackLen ) + wxS( ", " )
2234 + aFrame->MessageTextFromValue( coupledLen ) );
2235 }
2236 else
2237 {
2238 aList.emplace_back(
2239 _( "Routed Delays" ),
2240 aFrame->MessageTextFromValue( trackDelay, true, EDA_DATA_TYPE::TIME ) + wxS( ", " )
2241 + aFrame->MessageTextFromValue( coupledTrackDelay, true, EDA_DATA_TYPE::TIME ) );
2242 }
2243 }
2244 else
2245 {
2246 if( trackDelay == 0.0 )
2247 {
2248 aList.emplace_back( _( "Routed Length" ), aFrame->MessageTextFromValue( trackLen ) );
2249 }
2250 else
2251 {
2252 aList.emplace_back( _( "Routed Delay" ),
2253 aFrame->MessageTextFromValue( trackDelay, true, EDA_DATA_TYPE::TIME ) );
2254 }
2255 }
2256
2257 if( lenPadToDie != 0 && delayPadToDie == 0.0 )
2258 {
2259 msg = aFrame->MessageTextFromValue( lenPadToDie );
2260 aList.emplace_back( _( "Pad To Die Length" ), msg );
2261
2262 msg = aFrame->MessageTextFromValue( trackLen + lenPadToDie );
2263 aList.emplace_back( _( "Full Length" ), msg );
2264 }
2265 else if( delayPadToDie > 0.0 )
2266 {
2267 msg = aFrame->MessageTextFromValue( delayPadToDie, true, EDA_DATA_TYPE::TIME );
2268 aList.emplace_back( _( "Pad To Die Delay" ), msg );
2269
2270 msg = aFrame->MessageTextFromValue( trackDelay + delayPadToDie, true, EDA_DATA_TYPE::TIME );
2271 aList.emplace_back( _( "Full Delay" ), msg );
2272 }
2273
2274 // If part of a chain, display aggregate full chain length/delay (with bridging like overlay).
2275 if( primaryNet && !primaryNet->GetNetChain().IsEmpty() )
2276 {
2277 wxString chainName = primaryNet->GetNetChain();
2278 double totalOtherLen = 0.0;
2279 double totalOtherDelay = 0.0;
2280 BOARD* boardPtr = board;
2281 if( boardPtr )
2282 {
2283 for( NETINFO_ITEM* other : boardPtr->GetNetInfo() )
2284 {
2285 if( other->GetNetChain() != chainName || other == primaryNet )
2286 continue;
2287
2288 // Representative track length for this other net
2289 double oTrackLen = 0.0, oPadDieLen = 0.0, oTrackDelay = 0.0, oPadDieDelay = 0.0;
2290 PCB_TRACK* anyTrack = nullptr;
2291 for( BOARD_ITEM* bi : boardPtr->Tracks() )
2292 {
2293 if( PCB_TRACK* tr = dynamic_cast<PCB_TRACK*>( bi ) )
2294 {
2295 if( tr->GetNetCode() == other->GetNetCode() ) { anyTrack = tr; break; }
2296 }
2297 }
2298 if( anyTrack )
2299 {
2300 int dummyCount = 0;
2301 std::tie( dummyCount, oTrackLen, oPadDieLen, oTrackDelay, oPadDieDelay ) =
2302 boardPtr->GetTrackLength( *anyTrack );
2303 totalOtherLen += ( oTrackLen + oPadDieLen );
2304 totalOtherDelay += ( oTrackDelay + oPadDieDelay );
2305 }
2306 }
2307 }
2308
2309 // Bridging contribution (pad-to-pad gaps in 2-net series components of the chain)
2310 if( trackDelay == 0.0 )
2311 {
2312 double delayIUDummy = 0.0; // not used in length mode
2313 long long bridging = GetCachedBridgingLength( boardPtr, chainName, &delayIUDummy );
2314 aList.emplace_back( _( "Net Chain Full Length" ),
2315 aFrame->MessageTextFromValue( ( trackLen + lenPadToDie ) + totalOtherLen
2316 + (double) bridging ) );
2317 }
2318 else
2319 {
2320 double bridgingDelayIU = 0.0;
2321 GetCachedBridgingLength( boardPtr, chainName, &bridgingDelayIU );
2322 aList.emplace_back( _( "Net Chain Full Delay" ),
2323 aFrame->MessageTextFromValue( ( trackDelay + delayPadToDie ) + totalOtherDelay
2324 + bridgingDelayIU,
2325 true, EDA_DATA_TYPE::TIME ) );
2326 }
2327 }
2328 }
2329
2331 {
2332 constraint = drcEngine->EvalRules( SKEW_CONSTRAINT, primaryItem, coupledItem, m_layer );
2333
2334 if( constraint.IsNull() || m_settings.m_overrideCustomRules )
2335 {
2336 msg = aFrame->MessageTextFromValue( m_settings.m_targetSkew.Opt() );
2337
2338 aList.emplace_back( wxString::Format( _( "Target Skew: %s" ), msg ),
2339 wxString::Format( _( "(from tuning pattern properties)" ) ) );
2340 }
2341 else
2342 {
2343 msg = aFrame->MessageTextFromMinOptMax( constraint.GetValue() );
2344
2345 if( !msg.IsEmpty() )
2346 {
2347 aList.emplace_back( wxString::Format( _( "Skew Constraints: %s" ), msg ),
2348 wxString::Format( _( "(from %s)" ), constraint.GetName() ) );
2349 }
2350 }
2351 }
2352 else
2353 {
2354 // Prefer chain-level constraint if available
2355 constraint = drcEngine->EvalRules( NET_CHAIN_LENGTH_CONSTRAINT, primaryItem, coupledItem, m_layer );
2356
2357 if( constraint.IsNull() || constraint.GetSeverity() == RPT_SEVERITY_IGNORE )
2358 constraint = drcEngine->EvalRules( LENGTH_CONSTRAINT, primaryItem, coupledItem, m_layer );
2359
2360 if( constraint.IsNull() || m_settings.m_overrideCustomRules )
2361 {
2362 wxString caption;
2363
2364 if( m_settings.m_isTimeDomain )
2365 {
2366 caption = _( "Target Delay: %s" );
2367 msg = aFrame->MessageTextFromValue( static_cast<double>( m_settings.m_targetLengthDelay.Opt() ), true,
2369 }
2370 else
2371 {
2372 caption = _( "Target Length: %s" );
2373 msg = aFrame->MessageTextFromValue( static_cast<double>( m_settings.m_targetLength.Opt() ) );
2374 }
2375
2376 aList.emplace_back( wxString::Format( caption, msg ),
2377 wxString::Format( _( "(from tuning pattern properties)" ) ) );
2378 }
2379 else
2380 {
2381 msg = aFrame->MessageTextFromMinOptMax( constraint.GetValue(), unitType );
2382
2383 wxString caption = m_settings.m_isTimeDomain ? _( "Delay Constraints: %s" ) : _( "Length Constraints: %s" );
2384
2385 if( !msg.IsEmpty() )
2386 {
2387 aList.emplace_back( wxString::Format( caption, msg ),
2388 wxString::Format( _( "(from %s)" ), constraint.GetName() ) );
2389 }
2390 }
2391 }
2392}
2393
2394
2395const wxString PCB_TUNING_PATTERN::DISPLAY_NAME = _HKI( "Tuning Pattern" );
2396const wxString PCB_TUNING_PATTERN::GENERATOR_TYPE = wxS( "tuning_pattern" );
2397
2398
2400
2401
2402#define HITTEST_THRESHOLD_PIXELS 5
2403
2404
2406{
2408 return 0;
2409
2410 if( m_inDrawingTool )
2411 return 0;
2412
2414
2415 m_toolMgr->RunAction( ACTIONS::selectionClear );
2416
2417 m_frame->PushTool( aEvent );
2418 Activate();
2419
2420 BOARD* board = m_frame->GetBoard();
2421 BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings();
2422 std::shared_ptr<DRC_ENGINE>& drcEngine = bds.m_DRCEngine;
2423 GENERATOR_TOOL* generatorTool = m_toolMgr->GetTool<GENERATOR_TOOL>();
2424 PNS::ROUTER* router = generatorTool->Router();
2425 PNS::ROUTER_MODE routerMode = aEvent.Parameter<PNS::ROUTER_MODE>();
2426 LENGTH_TUNING_MODE mode = fromPNSMode( routerMode );
2427 PNS::MEANDER_SETTINGS meanderSettings;
2428
2429 switch( mode )
2430 {
2431 case LENGTH_TUNING_MODE::SINGLE: meanderSettings = bds.m_SingleTrackMeanderSettings; break;
2432 case DIFF_PAIR: meanderSettings = bds.m_DiffPairMeanderSettings; break;
2433 case DIFF_PAIR_SKEW: meanderSettings = bds.m_SkewMeanderSettings; break;
2434 }
2435
2437 PCB_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
2438 GENERAL_COLLECTORS_GUIDE guide = m_frame->GetCollectorsGuide();
2439 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::TUNING );
2440
2441 m_pickerItem = nullptr;
2442 m_tuningPattern = nullptr;
2443
2444 // Add a VIEW_GROUP that serves as a preview for the new item
2445 m_preview.Clear();
2446 m_view->Add( &m_preview );
2447
2448 auto applyCommonSettings = [&]( PCB_TUNING_PATTERN* aPattern )
2449 {
2450 const auto origTargetLength = aPattern->GetSettings().m_targetLength;
2451 const auto origTargetLengthDelay = aPattern->GetSettings().m_targetLengthDelay;
2452 const auto origTargetSignalLength = aPattern->GetSettings().m_targetSignalLength;
2453 const auto origTargetSignalLengthDelay = aPattern->GetSettings().m_targetSignalLengthDelay;
2454 const auto origTargetSkew = aPattern->GetSettings().m_targetSkew;
2455 const bool origIsTimeDomain = aPattern->GetSettings().m_isTimeDomain;
2456
2457 aPattern->GetSettings() = meanderSettings;
2458
2459 // Always preserve DRC-evaluated targets
2460 aPattern->GetSettings().m_targetLength = origTargetLength;
2461 aPattern->GetSettings().m_targetLengthDelay = origTargetLengthDelay;
2462 aPattern->GetSettings().m_targetSignalLength = origTargetSignalLength;
2463 aPattern->GetSettings().m_targetSignalLengthDelay = origTargetSignalLengthDelay;
2464 aPattern->GetSettings().m_targetSkew = origTargetSkew;
2465 aPattern->GetSettings().m_isTimeDomain = origIsTimeDomain;
2466 };
2467
2468 auto updateHoverStatus =
2469 [&]()
2470 {
2471 std::unique_ptr<PCB_TUNING_PATTERN> dummyPattern;
2472
2473 if( m_pickerItem )
2474 {
2475 dummyPattern.reset( PCB_TUNING_PATTERN::CreateNew( generatorTool, m_frame,
2476 m_pickerItem, mode ) );
2477 dummyPattern->SetPosition( m_pickerItem->GetFocusPosition() );
2478 dummyPattern->SetEnd( m_pickerItem->GetFocusPosition() );
2479 }
2480
2481 if( dummyPattern )
2482 {
2483 applyCommonSettings( dummyPattern.get() );
2484
2485 dummyPattern->EditStart( generatorTool, m_board, nullptr );
2486 dummyPattern->Update( generatorTool, m_board, nullptr );
2487
2488 m_preview.FreeItems();
2489
2490 for( EDA_ITEM* item : dummyPattern->GetPreviewItems( generatorTool, m_frame ) )
2491 m_preview.Add( item );
2492
2493 generatorTool->Router()->StopRouting();
2494
2495 m_view->Update( &m_preview );
2496 }
2497 else
2498 {
2499
2500 m_preview.FreeItems();
2501 m_view->Update( &m_preview );
2502 }
2503 };
2504
2505 auto updateTuningPattern =
2506 [&]()
2507 {
2508 if( m_tuningPattern && m_tuningPattern->GetPosition() != m_tuningPattern->GetEnd() )
2509 {
2510 m_tuningPattern->EditStart( generatorTool, m_board, nullptr );
2511 m_tuningPattern->Update( generatorTool, m_board, nullptr );
2512
2513 m_preview.FreeItems();
2514
2515 for( EDA_ITEM* item : m_tuningPattern->GetPreviewItems( generatorTool, m_frame, true ) )
2516 m_preview.Add( item );
2517
2518 m_view->Update( &m_preview );
2519 }
2520 };
2521
2522 while( TOOL_EVENT* evt = Wait() )
2523 {
2524 VECTOR2D cursorPos = controls->GetMousePosition();
2525
2526 if( evt->IsCancelInteractive() || evt->IsActivate()
2527 || ( m_tuningPattern && evt->IsAction( &ACTIONS::undo ) ) )
2528 {
2529 if( m_tuningPattern )
2530 {
2531 // First click already made; clean up tuning pattern preview
2532 m_tuningPattern->EditCancel( generatorTool, m_board, nullptr );
2533
2534 delete m_tuningPattern;
2535 m_tuningPattern = nullptr;
2536 }
2537 else
2538 {
2539 break;
2540 }
2541 }
2542 else if( evt->IsMotion() )
2543 {
2544 if( !m_tuningPattern )
2545 {
2546 // First click not yet made; we're in highlight-net-under-cursor mode
2547
2548 GENERAL_COLLECTOR collector;
2549 collector.m_Threshold = KiROUND( getView()->ToWorld( HITTEST_THRESHOLD_PIXELS ) );
2550
2551 if( m_frame->GetDisplayOptions().m_ContrastModeDisplay != HIGH_CONTRAST_MODE::NORMAL )
2552 guide.SetIncludeSecondary( false );
2553 else
2554 guide.SetIncludeSecondary( true );
2555
2556 guide.SetPreferredLayer( m_frame->GetActiveLayer() );
2557
2558 collector.Collect( board, { PCB_TRACE_T, PCB_ARC_T }, cursorPos, guide );
2559
2560 if( collector.GetCount() > 1 )
2561 selectionTool->GuessSelectionCandidates( collector, cursorPos );
2562
2563 m_pickerItem = nullptr;
2564
2565 if( collector.GetCount() > 0 )
2566 {
2567 double min_dist_sq = std::numeric_limits<double>::max();
2568
2569 for( EDA_ITEM* candidate : collector )
2570 {
2571 VECTOR2I candidatePos;
2572
2573 if( candidate->Type() == PCB_TRACE_T )
2574 {
2575 candidatePos = static_cast<PCB_TRACK*>( candidate )->GetCenter();
2576 }
2577 else if( candidate->Type() == PCB_ARC_T )
2578 {
2579 candidatePos = static_cast<PCB_ARC*>( candidate )->GetMid();
2580 }
2581
2582 double dist_sq = ( cursorPos - candidatePos ).SquaredEuclideanNorm();
2583
2584 if( dist_sq < min_dist_sq )
2585 {
2586 min_dist_sq = dist_sq;
2587 m_pickerItem = static_cast<BOARD_CONNECTED_ITEM*>( candidate );
2588 }
2589 }
2590 }
2591
2592 updateHoverStatus();
2593 }
2594 else
2595 {
2596 // First click already made; we're in preview-tuning-pattern mode
2597
2598 m_tuningPattern->SetEnd( cursorPos );
2599 m_tuningPattern->UpdateSideFromEnd();
2600
2601 updateTuningPattern();
2602 }
2603 }
2604 else if( evt->IsClick( BUT_LEFT ) )
2605 {
2607 {
2608 // First click; create a tuning pattern
2609
2610 if( dynamic_cast<PCB_TUNING_PATTERN*>( m_pickerItem->GetParentGroup() ) )
2611 {
2612 m_frame->ShowInfoBarWarning( _( "Unable to tune segments inside other "
2613 "tuning patterns." ) );
2614 }
2615 else
2616 {
2617 m_preview.FreeItems();
2618
2619 m_frame->SetActiveLayer( m_pickerItem->GetLayer() );
2621 m_pickerItem, mode );
2622
2623 m_tuningPattern->GetSettings().m_signalExtraLength = 0;
2624 m_tuningPattern->GetSettings().m_signalExtraDelay = 0;
2625
2626 applyCommonSettings( m_tuningPattern );
2627
2628 int dummyDist;
2629 int dummyClearance = std::numeric_limits<int>::max() / 2;
2630 VECTOR2I closestPt;
2631
2632 // With an artificially-large clearance this can't *not* collide, but the
2633 // if stmt keeps Coverity happy....
2634 if( m_pickerItem->GetEffectiveShape()->Collide( cursorPos, dummyClearance,
2635 &dummyDist, &closestPt ) )
2636 {
2637 m_tuningPattern->SetPosition( closestPt );
2638 m_tuningPattern->SetEnd( closestPt );
2639 }
2640
2641 m_preview.Add( m_tuningPattern->Clone() );
2642 }
2643 }
2644 else if( m_pickerItem && m_tuningPattern )
2645 {
2646 // Second click; we're done
2647 BOARD_COMMIT commit( m_frame );
2648
2649 m_tuningPattern->EditStart( generatorTool, m_board, &commit );
2650 m_tuningPattern->Update( generatorTool, m_board, &commit );
2651 m_tuningPattern->EditFinish( generatorTool, m_board, &commit );
2652
2653 commit.Push( _( "Tune" ) );
2654
2655 m_tuningPattern = nullptr;
2656 m_pickerItem = nullptr;
2657 }
2658 }
2659 else if( evt->IsClick( BUT_RIGHT ) )
2660 {
2662 m_menu->ShowContextMenu( dummy );
2663 }
2664 else if( evt->IsAction( &PCB_ACTIONS::spacingIncrease )
2665 || evt->IsAction( &PCB_ACTIONS::spacingDecrease ) )
2666 {
2667 if( m_tuningPattern )
2668 {
2669 auto* placer = static_cast<PNS::MEANDER_PLACER_BASE*>( router->Placer() );
2670
2671 if( placer )
2672 {
2673 placer->SpacingStep( evt->IsAction( &PCB_ACTIONS::spacingIncrease ) ? 1 : -1 );
2674 m_tuningPattern->SetSpacing( placer->MeanderSettings().m_spacing );
2675 meanderSettings.m_spacing = placer->MeanderSettings().m_spacing;
2676
2677 updateTuningPattern();
2678 }
2679 }
2680 else
2681 {
2682 m_frame->ShowInfoBarWarning( _( "Select a track to tune first." ) );
2683 }
2684 }
2685 else if( evt->IsAction( &PCB_ACTIONS::amplIncrease )
2686 || evt->IsAction( &PCB_ACTIONS::amplDecrease ) )
2687 {
2688 if( m_tuningPattern )
2689 {
2690 auto* placer = static_cast<PNS::MEANDER_PLACER_BASE*>( router->Placer() );
2691
2692 if( placer )
2693 {
2694 placer->AmplitudeStep( evt->IsAction( &PCB_ACTIONS::amplIncrease ) ? 1 : -1 );
2695 m_tuningPattern->SetMaxAmplitude( placer->MeanderSettings().m_maxAmplitude );
2696 meanderSettings.m_maxAmplitude = placer->MeanderSettings().m_maxAmplitude;
2697
2698 updateTuningPattern();
2699 }
2700 }
2701 else
2702 {
2703 m_frame->ShowInfoBarWarning( _( "Select a track to tune first." ) );
2704 }
2705 }
2706 else if( evt->IsAction( &PCB_ACTIONS::properties )
2707 || evt->IsAction( &PCB_ACTIONS::lengthTunerSettings ) )
2708 {
2709 DRC_CONSTRAINT constraint;
2710
2711 if( m_tuningPattern )
2712 {
2713 if( !m_tuningPattern->GetItems().empty() )
2714 {
2715 BOARD_ITEM* startItem = *m_tuningPattern->GetBoardItems().begin();
2716
2717 constraint = drcEngine->EvalRules( LENGTH_CONSTRAINT, startItem, nullptr,
2718 startItem->GetLayer() );
2719 }
2720 }
2721
2722 DIALOG_TUNING_PATTERN_PROPERTIES dlg( m_frame, meanderSettings, routerMode, constraint );
2723
2724 if( dlg.ShowModal() == wxID_OK )
2725 {
2726 if( m_tuningPattern )
2727 applyCommonSettings( m_tuningPattern );
2728
2729 updateTuningPattern();
2730 }
2731 }
2732 // TODO: It'd be nice to be able to say "don't allow any non-trivial editing actions",
2733 // but we don't at present have that, so we just knock out some of the egregious ones.
2734 else if( ZONE_FILLER_TOOL::IsZoneFillAction( evt ) )
2735 {
2736 wxBell();
2737 }
2738 else
2739 {
2740 evt->SetPassEvent();
2741 }
2742
2743 controls->CaptureCursor( m_tuningPattern != nullptr );
2744 controls->SetAutoPan( m_tuningPattern != nullptr );
2745 }
2746
2747 controls->CaptureCursor( false );
2748 controls->SetAutoPan( false );
2749 controls->ForceCursorPosition( false );
2750 controls->ShowCursor( false );
2751 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2752
2753 m_preview.FreeItems();
2754 m_view->Remove( &m_preview );
2755
2756 m_frame->GetCanvas()->Refresh();
2757
2758 if( m_tuningPattern )
2759 selectionTool->AddItemToSel( m_tuningPattern );
2760
2761 m_frame->PopTool( aEvent );
2762 return 0;
2763}
2764
2765
2767{
2769 {
2771 .Map( LENGTH_TUNING_MODE::SINGLE, _HKI( "Single track" ) )
2772 .Map( LENGTH_TUNING_MODE::DIFF_PAIR, _HKI( "Differential pair" ) )
2773 .Map( LENGTH_TUNING_MODE::DIFF_PAIR_SKEW, _HKI( "Diff pair skew" ) );
2774
2776 .Map( PNS::MEANDER_SIDE_LEFT, _HKI( "Left" ) )
2777 .Map( PNS::MEANDER_SIDE_RIGHT, _HKI( "Right" ) )
2778 .Map( PNS::MEANDER_SIDE_DEFAULT, _HKI( "Default" ) );
2779
2786
2788
2789 if( layerEnum.Choices().GetCount() == 0 )
2790 {
2791 layerEnum.Undefined( UNDEFINED_LAYER );
2792
2793 for( PCB_LAYER_ID layer : LSET::AllLayersMask() )
2794 layerEnum.Map( layer, LSET::Name( layer ) );
2795 }
2796
2799 layer->SetChoices( layerEnum.Choices() );
2800 propMgr.ReplaceProperty( TYPE_HASH( BOARD_ITEM ), _HKI( "Layer" ), layer );
2801
2802 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, int>( _HKI( "Width" ),
2805
2808
2809 const wxString groupTechLayers = _HKI( "Technical Layers" );
2810
2811 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, bool>( _HKI( "Soldermask" ),
2813 groupTechLayers );
2814
2815 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, std::optional<int>>( _HKI( "Soldermask Margin Override" ),
2819 groupTechLayers );
2820
2821 const wxString groupTab = _HKI( "Pattern Properties" );
2822
2823 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, int>( _HKI( "End X" ),
2826 groupTab );
2827
2828 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, int>( _HKI( "End Y" ),
2831 groupTab );
2832
2836 groupTab );
2837
2838 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, int>( _HKI( "Min Amplitude" ),
2841 groupTab );
2842
2843 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, int>( _HKI( "Max Amplitude" ),
2846 groupTab );
2847
2850 groupTab );
2851
2852 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, int>( _HKI( "Min Spacing" ),
2855 groupTab );
2856
2857 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, int>( _HKI( "Corner Radius %" ),
2861 groupTab );
2862
2863 auto isSkew =
2864 []( INSPECTABLE* aItem ) -> bool
2865 {
2866 if( PCB_TUNING_PATTERN* pattern = dynamic_cast<PCB_TUNING_PATTERN*>( aItem ) )
2867 return pattern->GetTuningMode() == DIFF_PAIR_SKEW;
2868
2869 return false;
2870 };
2871
2872 auto isTimeDomain = []( INSPECTABLE* aItem ) -> bool
2873 {
2874 if( PCB_TUNING_PATTERN* pattern = dynamic_cast<PCB_TUNING_PATTERN*>( aItem ) )
2875 return pattern->GetSettings().m_isTimeDomain;
2876
2877 return false;
2878 };
2879
2880 auto isLengthIsSpaceDomain = [&]( INSPECTABLE* aItem ) -> bool
2881 {
2882 return !isSkew( aItem ) && !isTimeDomain( aItem );
2883 };
2884
2885 auto isLengthIsTimeDomain = [&]( INSPECTABLE* aItem ) -> bool
2886 {
2887 return !isSkew( aItem ) && isTimeDomain( aItem );
2888 };
2889
2890 auto isSkewIsSpaceDomain = [&]( INSPECTABLE* aItem ) -> bool
2891 {
2892 return isSkew( aItem ) && !isTimeDomain( aItem );
2893 };
2894
2895 auto isSkewIsTimeDomain = [&]( INSPECTABLE* aItem ) -> bool
2896 {
2897 return isSkew( aItem ) && isTimeDomain( aItem );
2898 };
2899
2900 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, std::optional<int>>( _HKI( "Target Length" ),
2903 groupTab )
2904 .SetAvailableFunc( isLengthIsSpaceDomain );
2905
2906 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, std::optional<int>>( _HKI( "Target Delay" ),
2909 groupTab )
2910 .SetAvailableFunc( isLengthIsTimeDomain );
2911
2912 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, int>( _HKI( "Target Skew" ),
2915 groupTab )
2916 .SetAvailableFunc( isSkewIsSpaceDomain );
2917
2918 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, int>( _HKI( "Target Skew Delay" ),
2921 groupTab )
2922 .SetAvailableFunc( isSkewIsTimeDomain );
2923
2924 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, bool>( _HKI( "Override Custom Rules" ),
2927 groupTab );
2928
2929 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, bool>( _HKI( "Single-sided" ),
2931 groupTab );
2932
2933 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, bool>( _HKI( "Rounded" ),
2935 groupTab );
2936 }
2938
2941
2943
2944// Also register under the 7.99 name
2945template <typename T>
2947{
2949 {
2950 GENERATORS_MGR::Instance().Register( wxS( "meanders" ), T::DISPLAY_NAME,
2951 []()
2952 {
2953 return new T;
2954 } );
2955 }
2956};
2957
int red
int green
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:121
constexpr int ARC_LOW_DEF
Definition base_units.h:136
@ NORMAL
Inactive layers are shown normally (no high-contrast mode)
BOX2< VECTOR2I > BOX2I
Definition box2.h:918
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:986
static TOOL_ACTION undo
Definition actions.h:71
static TOOL_ACTION selectionClear
Clear the current selection.
Definition actions.h:220
virtual void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Execute the changes.
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
virtual NETCLASS * GetEffectiveNetClass() const
Return the NETCLASS for this item.
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
NETINFO_ITEM * GetNet() const
Return #NET_INFO object for a given item.
Container for design settings for a BOARD object.
std::shared_ptr< DRC_ENGINE > m_DRCEngine
int GetDRCEpsilon() const
Return an epsilon which accounts for rounding errors, etc.
PNS::MEANDER_SETTINGS m_DiffPairMeanderSettings
PNS::MEANDER_SETTINGS m_SingleTrackMeanderSettings
PNS::MEANDER_SETTINGS m_SkewMeanderSettings
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:81
BOARD_ITEM(BOARD_ITEM *aParent, KICAD_T idtype, PCB_LAYER_ID aLayer=F_Cu)
Definition board_item.h:83
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition board_item.h:265
friend class BOARD
Definition board_item.h:512
PCB_LAYER_ID m_layer
Definition board_item.h:508
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
virtual wxString LayerMaskDescribe() const
Return a string (to be shown to the user) describing a layer mask.
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:372
const NETINFO_LIST & GetNetInfo() const
Definition board.h:1086
NETINFO_ITEM * DpCoupledNet(const NETINFO_ITEM *aNet)
Definition board.cpp:2724
std::tuple< int, double, double, double, double > GetTrackLength(const PCB_TRACK &aTrack) const
Return data on the length and number of track segments connected to a given track.
Definition board.cpp:3238
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition board.cpp:2657
const FOOTPRINTS & Footprints() const
Definition board.h:420
const TRACKS & Tracks() const
Definition board.h:418
PROJECT * GetProject() const
Definition board.h:650
bool IsEmpty() const
Definition board.cpp:662
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1149
constexpr void SetMaximum()
Definition box2.h:76
int GetCount() const
Return the number of objects in the list.
Definition collector.h:79
int m_Threshold
Definition collector.h:232
COMMIT & Remove(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Remove a new item from the model.
Definition commit.h:86
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr, RECURSE_MODE aRecurse=RECURSE_MODE::NO_RECURSE)
Modify a given item in the model.
Definition commit.h:102
COMMIT & Add(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Add a new item to the model.
Definition commit.h:74
int ShowModal() override
PCB_SELECTION m_preview
KIGFX::VIEW * m_view
BOARD_CONNECTED_ITEM * m_pickerItem
int PlaceTuningPattern(const TOOL_EVENT &aEvent)
BOARD * m_board
PCB_BASE_EDIT_FRAME * m_frame
PCB_TUNING_PATTERN * m_tuningPattern
wxString GetName() const
Definition drc_rule.h:204
SEVERITY GetSeverity() const
Definition drc_rule.h:217
const MINOPTMAX< int > & GetValue() const
Definition drc_rule.h:196
bool GetOption(OPTIONS option) const
Definition drc_rule.h:229
bool IsNull() const
Definition drc_rule.h:191
DRC_CONSTRAINT EvalRules(DRC_CONSTRAINT_T aConstraintType, const BOARD_ITEM *a, const BOARD_ITEM *b, PCB_LAYER_ID aLayer, REPORTER *aReporter=nullptr)
The base class for create windows for drawing purpose.
wxString m_name
Definition eda_group.h:78
std::unordered_set< EDA_ITEM * > m_items
Definition eda_group.h:77
std::unordered_set< EDA_ITEM * > & GetItems()
Definition eda_group.h:50
void AddItem(EDA_ITEM *aItem)
Add item to group.
Definition eda_group.cpp:58
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:96
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:152
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition eda_item.h:154
bool IsSelected() const
Definition eda_item.h:132
bool HasFlag(EDA_ITEM_FLAGS aFlag) const
Definition eda_item.h:156
EDA_ITEM_FLAGS GetFlags() const
Definition eda_item.h:155
EDA_ITEM(EDA_ITEM *parent, KICAD_T idType, bool isSCH_ITEM=false, bool isBOARD_ITEM=false)
Definition eda_item.cpp:37
bool IsNew() const
Definition eda_item.h:129
EDIT_POINTS is a VIEW_ITEM that manages EDIT_POINTs and EDIT_LINEs and draws them.
void AddPoint(const EDIT_POINT &aPoint)
Add an EDIT_POINT.
EDIT_POINT & Point(unsigned int aIndex)
void SetGridConstraint(GRID_CONSTRAINT_TYPE aConstraint)
bool IsActive() const
static const int POINT_SIZE
Single point size in pixels.
virtual void SetPosition(const VECTOR2I &aPosition)
Set new coordinates for an EDIT_POINT.
virtual VECTOR2I GetPosition() const
Return coordinates of an EDIT_POINT.
Definition edit_points.h:68
ENUM_MAP & Map(T aValue, const wxString &aName)
Definition property.h:727
static ENUM_MAP< T > & Instance()
Definition property.h:721
ENUM_MAP & Undefined(T aValue)
Definition property.h:734
wxPGChoices & Choices()
Definition property.h:772
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition actions.h:348
A general implementation of a COLLECTORS_GUIDE.
Definition collectors.h:320
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 factory which returns an instance of a PCB_GENERATOR.
void Register(const wxString &aTypeStr, const wxString &aName, std::function< PCB_GENERATOR *(void)> aCreateFunc)
Associate a type string to display name and create function.
static GENERATORS_MGR & Instance()
const std::vector< GENERATOR_PNS_CHANGES > & GetRouterChanges()
Handle actions specific to filling copper zones.
Class that other classes need to inherit from, in order to be inspectable.
Definition inspectable.h:38
FONT is an abstract base class for both outline and stroke fonts.
Definition font.h:94
static FONT * GetFont(const wxString &aFontName=wxEmptyString, bool aBold=false, bool aItalic=false, const std::vector< wxString > *aEmbeddedFiles=nullptr, bool aForDrawingSheet=false)
Definition font.cpp:143
void Draw(KIGFX::GAL *aGal, const wxString &aText, const VECTOR2I &aPosition, const VECTOR2I &aCursor, const TEXT_ATTRIBUTES &aAttributes, const METRICS &aFontMetrics, std::optional< VECTOR2I > aMousePos=std::nullopt, wxString *aActiveUrl=nullptr) const
Draw a string.
Definition font.cpp:246
static const METRICS & Default()
Definition font.cpp:48
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
COLOR4D WithAlpha(double aAlpha) const
Return a color with the same color, but the given alpha.
Definition color4d.h:308
void ToHSL(double &aOutHue, double &aOutSaturation, double &aOutLightness) const
Converts current color (stored in RGB) to HSL format.
Definition color4d.cpp:309
Abstract interface for drawing on a 2D-surface.
virtual void SetIsFill(bool aIsFillEnabled)
Enable/disable fill.
virtual void DrawRectangle(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint)
Draw a rectangle.
virtual void SetFillColor(const COLOR4D &aColor)
Set the fill color.
const MATRIX3x3D & GetScreenWorldMatrix() const
Get the screen <-> world transformation matrix.
virtual void Restore()
Restore the context.
virtual void SetLineWidth(float aLineWidth)
Set the line width.
virtual void SetStrokeColor(const COLOR4D &aColor)
Set the stroke color.
virtual void SetIsStroke(bool aIsStrokeEnabled)
Enable/disable stroked outlines.
virtual void Scale(const VECTOR2D &aScale)
Scale the context.
virtual void Save()
Save the context.
A KIGFX::PREVIEW::DRAW_CONTEXT is a wrapper around a GAL and some other settings that makes it easy t...
void DrawLineDashed(const VECTOR2I &aStart, const VECTOR2I &aEn, int aDashStep, int aDashFill, bool aDeEmphasised)
Draw a dashed line on the current layer.
An interface for classes handling user events controlling the view behavior such as zooming,...
Hold a (potentially large) number of VIEW_ITEMs and renders them on a graphics device provided by the...
Definition view.h:63
GAL * GetGAL() const
Return the GAL this view is using to draw graphical primitives.
Definition view.h:207
VECTOR2D ToWorld(const VECTOR2D &aCoord, bool aAbsolute=true) const
Converts a screen space point/vector to a point/vector in world space coordinates.
Definition view.cpp:534
void Hide(VIEW_ITEM *aItem, bool aHide=true, bool aHideOverlay=false)
Temporarily hide the item in the view (e.g.
Definition view.cpp:1780
static const LSET & AllLayersMask()
Definition lset.cpp:637
static wxString Name(PCB_LAYER_ID aLayerId)
Return the fixed name association with aLayerId.
Definition lset.cpp:184
VECTOR2< T > GetScale() const
Get the scale components of the matrix.
Definition matrix3x3.h:291
T Min() const
Definition minoptmax.h:29
void SetMin(T v)
Definition minoptmax.h:37
bool HasMax() const
Definition minoptmax.h:34
void SetOpt(T v)
Definition minoptmax.h:39
bool HasMin() const
Definition minoptmax.h:33
void SetMax(T v)
Definition minoptmax.h:38
T Max() const
Definition minoptmax.h:30
T Opt() const
Definition minoptmax.h:31
bool HasOpt() const
Definition minoptmax.h:35
A collection of nets and the parameters used to route or test these nets.
Definition netclass.h:38
const wxString GetHumanReadableName() const
Gets the consolidated name of this netclass (which may be an aggregate).
Definition netclass.cpp:320
wxString GetTuningProfile() const
Definition netclass.h:252
bool HasTuningProfile() const
Definition netclass.h:250
Handle the data for a net.
Definition netinfo.h:46
const wxString & GetNetChain() const
Definition netinfo.h:112
const wxString & GetNetname() const
Definition netinfo.h:100
static TOOL_ACTION properties
Activation of the edit tool.
static TOOL_ACTION spacingDecrease
static TOOL_ACTION amplIncrease
static TOOL_ACTION amplDecrease
static TOOL_ACTION lengthTunerSettings
static TOOL_ACTION spacingIncrease
Common, abstract interface for edit frames.
PCB_DRAW_PANEL_GAL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
virtual KIGFX::PCB_VIEW * GetView() const override
Return a pointer to the #VIEW instance used in the panel.
virtual void SetProperties(const STRING_ANY_MAP &aProps)
wxString m_generatorType
PCB_GENERATOR(BOARD_ITEM *aParent, PCB_LAYER_ID aLayer)
VECTOR2I m_origin
virtual const STRING_ANY_MAP GetProperties() const
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...
KIGFX::VIEW_CONTROLS * controls() const
BOARD * board() const
void SetTargetSkew(int aValue)
bool initBaseLines(PNS::ROUTER *aRouter, int aPNSLayer, BOARD *aBoard)
const STRING_ANY_MAP GetProperties() const override
void SetLocalSolderMaskMargin(std::optional< int > aMargin)
void SetMinAmplitude(int aValue)
void SetSpacing(int aValue)
static const wxString DISPLAY_NAME
void GetMsgPanelInfo(EDA_DRAW_FRAME *aFrame, std::vector< MSG_PANEL_ITEM > &aList) override
Populate aList of MSG_PANEL_ITEM objects with it's internal state for display purposes.
PNS::ROUTER_MODE GetPNSMode()
bool initBaseLine(PNS::ROUTER *aRouter, int aPNSLayer, BOARD *aBoard, VECTOR2I &aStart, VECTOR2I &aEnd, NETINFO_ITEM *aNet, std::optional< SHAPE_LINE_CHAIN > &aBaseLine)
void SetTargetSkewDelay(int aValue)
PCB_TUNING_PATTERN(BOARD_ITEM *aParent=nullptr, PCB_LAYER_ID aLayer=F_Cu, LENGTH_TUNING_MODE aMode=LENGTH_TUNING_MODE::SINGLE)
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
bool recoverBaseline(PNS::ROUTER *aRouter)
void SetWidth(int aValue)
PNS::MEANDER_SIDE GetInitialSide() const
std::optional< int > GetLocalSolderMaskMargin() const
void SetInitialSide(PNS::MEANDER_SIDE aValue)
wxString GetFriendlyName() const override
void SetTargetDelay(std::optional< int > aValue)
LENGTH_TUNING_MODE GetTuningMode() const
void SetHasSolderMask(bool aVal)
std::optional< int > GetTargetDelay() const
std::vector< EDA_ITEM * > GetPreviewItems(GENERATOR_TOOL *aTool, PCB_BASE_EDIT_FRAME *aFrame, bool aStatusItemsOnly=false) override
void EditStart(GENERATOR_TOOL *aTool, BOARD *aBoard, BOARD_COMMIT *aCommit) override
long long GetCachedBridgingLength(BOARD *aBoard, const wxString &aNetChain, double *aDelayIUOut)
void SetOverrideCustomRules(bool aOverride)
void SetRounded(bool aFlag)
LENGTH_TUNING_MODE m_tuningMode
static const wxString GENERATOR_TYPE
bool resetToBaseline(GENERATOR_TOOL *aTool, int aPNSLayer, SHAPE_LINE_CHAIN &aBaseLine, bool aPrimary)
bool MakeEditPoints(EDIT_POINTS &points) const override
void SetCornerRadiusPercentage(int aValue)
PNS::MEANDER_PLACER_BASE::TUNING_STATUS m_tuningStatus
std::optional< SHAPE_LINE_CHAIN > m_baseLineCoupled
const BOARD * m_cachedBridgingBoardPtr
void SetProperties(const STRING_ANY_MAP &aProps) override
void ViewDraw(int aLayer, KIGFX::VIEW *aView) const override final
Draw the parts of the object belonging to layer aLayer.
void Remove(GENERATOR_TOOL *aTool, BOARD *aBoard, BOARD_COMMIT *aCommit) override
bool UpdateFromEditPoints(EDIT_POINTS &aEditPoints) override
std::optional< SHAPE_LINE_CHAIN > m_baseLine
PNS::MEANDER_SETTINGS m_settings
void SetEndX(int aValue)
void ShowPropertiesDialog(PCB_BASE_EDIT_FRAME *aEditFrame) override
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
bool Update(GENERATOR_TOOL *aTool, BOARD *aBoard, BOARD_COMMIT *aCommit) override
void SetNetCode(int aNetCode)
bool UpdateEditPoints(EDIT_POINTS &aEditPoints) override
void EditCancel(GENERATOR_TOOL *aTool, BOARD *aBoard, BOARD_COMMIT *aCommit) override
bool GetOverrideCustomRules() const
void SetMaxAmplitude(int aValue)
PNS::MEANDER_SETTINGS & GetSettings()
void SetSingleSided(bool aValue)
bool removeToBaseline(PNS::ROUTER *aRouter, int aPNSLayer, SHAPE_LINE_CHAIN &aBaseLine)
std::optional< int > GetTargetLength() const
void EditFinish(GENERATOR_TOOL *aTool, BOARD *aBoard, BOARD_COMMIT *aCommit) override
int GetCornerRadiusPercentage() const
SHAPE_LINE_CHAIN getOutline() const
void SetEndY(int aValue)
void SetTargetLength(std::optional< int > aValue)
static PCB_TUNING_PATTERN * CreateNew(GENERATOR_TOOL *aTool, PCB_BASE_EDIT_FRAME *aFrame, BOARD_CONNECTED_ITEM *aStartItem, LENGTH_TUNING_MODE aMode)
Differential Pair length-matching/meandering tool.
Base class for PNS router board items.
Definition pns_item.h:98
virtual NET_HANDLE Net() const
Definition pns_item.h:210
void SetNet(NET_HANDLE aNet)
Definition pns_item.h:209
void SetLayer(int aLayer)
Definition pns_item.h:215
void SetParent(BOARD_ITEM *aParent)
Definition pns_item.h:191
bool OfKind(int aKindMask) const
Definition pns_item.h:181
virtual VECTOR2I Anchor(int n) const
Definition pns_item.h:268
Represents a track on a PCB, connecting two non-trivial joints (that is, vias, pads,...
Definition pns_line.h:62
const SHAPE_LINE_CHAIN & CLine() const
Definition pns_line.h:142
SHAPE_LINE_CHAIN & Line()
Definition pns_line.h:141
void SetWidth(int aWidth)
Return line width.
Definition pns_line.h:155
virtual int Width() const
Base class for Single trace & Differential pair meandering tools, as both of them share a lot of code...
virtual void UpdateSettings(const MEANDER_SETTINGS &aSettings)
TUNING_STATUS
< Result of the length tuning operation
virtual TUNING_STATUS TuningStatus() const =0
Return the tuning status (too short, too long, etc.) of the trace(s) being tuned.
virtual const MEANDER_SETTINGS & MeanderSettings() const
Return the current meandering configuration.
virtual long long int TuningLengthResult() const =0
Return the resultant length or skew of the tuned traces.
Dimensions for the meandering algorithm.
Definition pns_meander.h:70
void SetTargetLength(long long int aOpt)
bool m_isTimeDomain
The net class this meander pattern belongs to.
void SetTargetLengthDelay(long long int aOpt)
MINOPTMAX< long long int > m_targetLength
Desired propagation delay of the tuned line.
void SetTargetSignalLengthDelay(long long int aOpt)
void SetTargetSkew(int aOpt)
void SetTargetSignalLength(long long int aOpt)
int m_maxAmplitude
Meandering period/spacing (see dialog picture for explanation).
bool m_overrideCustomRules
Type of corners for the meandered line.
void SetTargetSkewDelay(int aOpt)
int m_spacing
Amplitude/spacing adjustment step.
Keep the router "world" - i.e.
Definition pns_node.h:242
NODE * Branch()
Create a lightweight copy (called branch) of self that tracks the changes (added/removed items) wrs t...
Definition pns_node.cpp:157
bool Add(std::unique_ptr< SEGMENT > aSegment, bool aAllowRedundant=false)
Add an item to the current node.
Definition pns_node.cpp:747
std::set< OBSTACLE > OBSTACLES
Definition pns_node.h:254
const LINE AssembleLine(LINKED_ITEM *aSeg, int *aOriginSegmentIndex=nullptr, bool aStopAtLockedJoints=false, bool aFollowLockedSegments=false, bool aAllowSegmentSizeMismatch=true)
Follow the joint map to assemble a line connecting two non-trivial joints starting from segment aSeg.
int QueryColliding(const ITEM *aItem, OBSTACLES &aObstacles, const COLLISION_SEARCH_OPTIONS &aOpts=COLLISION_SEARCH_OPTIONS()) const
Find items colliding (closer than clearance) with the item aItem.
Definition pns_node.cpp:267
void Remove(ARC *aArc)
Remove an item from this branch.
Definition pns_node.cpp:991
virtual int GetPNSLayerFromBoardLayer(PCB_LAYER_ID aLayer) const =0
virtual void RemoveItem(ITEM *aItem)=0
virtual void AddItem(ITEM *aItem)=0
void SetMode(ROUTER_MODE aMode)
void StopRouting()
PLACEMENT_ALGO * Placer()
Definition pns_router.h:238
ROUTER_IFACE * GetInterface() const
Definition pns_router.h:240
void CommitRouting()
void SyncWorld()
RULE_RESOLVER * GetRuleResolver() const
Definition pns_router.h:202
bool RoutingInProgress() const
bool StartRouting(const VECTOR2I &aP, ITEM *aItem, int aLayer)
SIZES_SETTINGS & Sizes()
Definition pns_router.h:233
bool FixRoute(const VECTOR2I &aP, ITEM *aItem, bool aForceFinish, bool aForceCommit)
NODE * GetWorld() const
Definition pns_router.h:186
bool Move(const VECTOR2I &aP, ITEM *aItem)
virtual int Clearance(const ITEM *aA, const ITEM *aB, bool aUseClearanceEpsilon=true)=0
void SetShape(SHAPE *shape)
Definition pns_solid.h:113
ROUTER * Router() const
PNS_KICAD_IFACE * GetInterface() const
void SetStartLayerFromPCBNew(PCB_LAYER_ID aLayer)
int GetPNSLayerFromBoardLayer(PCB_LAYER_ID aLayer) const override
void DisplayItem(const PNS::ITEM *aItem, int aClearance, bool aEdit=false, int aFlags=0) override
void EraseView() override
wxString GetNetName(PNS::NET_HANDLE aNet) const override
std::shared_ptr< TUNING_PROFILES > & TuningProfileParameters()
virtual PROJECT_FILE & GetProjectFile() const
Definition project.h:200
PROPERTY_BASE & SetAvailableFunc(std::function< bool(INSPECTABLE *)> aFunc)
Set a callback function to determine whether an object provides this property.
Definition property.h:262
Provide class metadata.Helper macro to map type hashes to names.
void InheritsAfter(TYPE_ID aDerived, TYPE_ID aBase)
Declare an inheritance relationship between types.
static PROPERTY_MANAGER & Instance()
PROPERTY_BASE & AddProperty(PROPERTY_BASE *aProperty, const wxString &aGroup=wxEmptyString)
Register a property.
PROPERTY_BASE & ReplaceProperty(size_t aBase, const wxString &aName, PROPERTY_BASE *aNew, const wxString &aGroup=wxEmptyString)
Replace an existing property for a specific type.
void AddTypeCast(TYPE_CAST_BASE *aCast)
Register a type converter.
RAII class that sets an value at construction and resets it to the original value at destruction.
Definition seg.h:38
VECTOR2I A
Definition seg.h:45
int LineDistance(const VECTOR2I &aP, bool aDetermineSide=false) const
Return the closest Euclidean distance between point aP and the line defined by the ends of segment (t...
Definition seg.cpp:742
VECTOR2I B
Definition seg.h:46
int Side(const VECTOR2I &aP) const
Determine on which side of directed line passing via segment ends point aP lies.
Definition seg.h:139
int AddItemToSel(const TOOL_EVENT &aEvent)
bool PointOnEdge(const VECTOR2I &aP, int aAccuracy=0) const
Check if point aP lies on an edge or vertex of the line chain.
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.
int Split(const VECTOR2I &aP, bool aExact=false)
Insert the point aP belonging to one of the our segments, splitting the adjacent segment in two.
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
void Simplify(int aTolerance=0)
Simplify the line chain by removing colinear adjacent segments and duplicate vertices.
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
const VECTOR2I NearestPoint(const VECTOR2I &aP, bool aAllowInternalShapePoints=true) const
Find a point on the line chain that is closest to point aP.
const VECTOR2I & CLastPoint() const
Return the last point in the line chain.
bool PointInside(const VECTOR2I &aPt, int aAccuracy=0, bool aUseBBoxCache=false) const override
Check if point aP lies inside a closed shape.
SHAPE * Clone() const override
Return a dynamically allocated copy of the shape.
bool OffsetLine(int aAmount, CORNER_STRATEGY aCornerStrategy, int aMaxError, SHAPE_LINE_CHAIN &aLeft, SHAPE_LINE_CHAIN &aRight, bool aSimplify=false) const
Creates line chains aLeft and aRight offset to this line chain.
Represent a set of closed polygons.
void BooleanAdd(const SHAPE_POLY_SET &b)
Perform boolean polyset union.
void ClearArcs()
Removes all arc references from all the outlines and holes in the polyset.
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
void OffsetLineChain(const SHAPE_LINE_CHAIN &aLine, int aAmount, CORNER_STRATEGY aCornerStrategy, int aMaxError, bool aSimplify)
Perform offsetting of a line chain.
int OutlineCount() const
Return the number of outlines in the set.
A name/value tuple with unique names and wxAny values.
void set_iu(const std::string &aKey, const T &aVar)
bool get_to(const std::string &aKey, T &aVar) const
std::optional< T > get_opt(const std::string &aKey) const
void set(const std::string &aKey, const T &aVar)
bool get_to_iu(const std::string &aKey, T &aVar) const
GR_TEXT_H_ALIGN_T m_Halign
TOOL_MANAGER * GetToolManager() const
Return the MVC controller.
TOOL_MANAGER * GetManager() const
Return the instance of TOOL_MANAGER that takes care of the tool.
Definition tool_base.h:142
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
Generic, UI-independent tool event.
Definition tool_event.h:167
T Parameter() const
Return a parameter assigned to the event.
Definition tool_event.h:469
std::unique_ptr< TOOL_MENU > m_menu
The functions below are not yet implemented - their interface may change.
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.
void PostEvent(const TOOL_EVENT &aEvent)
Put an event to the event queue to be processed at the end of event processing cycle.
VECTOR2D GetMousePosition() const
APP_SETTINGS_BASE * GetSettings() const
KIGFX::VIEW * GetView() const
void SetIsTimeDomain(const bool aIsTimeDomain)
void SetCurrent(const double aCurrent, const wxString &aLabel)
wxString GetClass() const override
Return the class name.
VECTOR2I GetPosition() const override
void SetScopeLine(const wxString &aLine)
void SetMinMax(const double aMin, const double aMax)
void ViewDraw(int aLayer, KIGFX::VIEW *aView) const override
Draw the parts of the object belonging to layer aLayer.
void SetChainMinMax(const double aMin, const double aMax)
std::vector< int > ViewGetLayers() const override
Return the all the layers within the VIEW the object is painted on.
TUNING_STATUS_VIEW_ITEM(PCB_BASE_EDIT_FRAME *aFrame)
const BOX2I ViewBBox() const override
Return the bounding box of the item covering all its layers.
void SetNetAndSignalValues(const wxString &aNetVal, const wxString &aSignalVal, bool aHasSignal)
void SetPosition(const VECTOR2I &aPos) override
wxString MessageTextFromValue(double aValue, bool aAddUnitLabel=true, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE) const
A lower-precision version of StringFromValue().
wxString MessageTextFromMinOptMax(const MINOPTMAX< int > &aValue, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE) const
PCB_TUNING_PATTERN * m_pattern
UNLOCKER(PCB_TUNING_PATTERN *aPattern)
static bool IsZoneFillAction(const TOOL_EVENT *aEvent)
@ ROUND_ALL_CORNERS
All angles are rounded.
@ ARROW
Definition cursors.h:42
@ NET_CHAIN_LENGTH_CONSTRAINT
Definition drc_rule.h:74
@ LENGTH_CONSTRAINT
Definition drc_rule.h:73
@ SKEW_CONSTRAINT
Definition drc_rule.h:77
#define _(s)
#define IS_NEW
New item, just created.
#define IN_EDIT
Item currently edited.
EDA_DATA_TYPE
The type of unit.
Definition eda_units.h:34
EDA_UNITS
Definition eda_units.h:44
@ IGNORE_GRID
static FILENAME_RESOLVER * resolver
a few functions useful in geometry calculations.
@ LAYER_UI_START
Definition layer_ids.h:355
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ UNDEFINED_LAYER
Definition layer_ids.h:57
KICOMMON_API wxString MessageTextFromValue(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits, double aValue, bool aAddUnitsText=true, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
A helper to convert the double length aValue to a string in inches, millimeters, or unscaled units.
TEXT_DIMS GetConstantGlyphHeight(KIGFX::GAL *aGal, int aRelativeSize=0)
Set the GAL glyph height to a constant scaled value, so that it always looks the same on screen.
Push and Shove diff pair dimensions (gap) settings dialog.
MEANDER_SIDE
Definition pns_meander.h:60
@ MEANDER_SIDE_RIGHT
Definition pns_meander.h:63
@ MEANDER_SIDE_DEFAULT
Definition pns_meander.h:62
@ MEANDER_SIDE_LEFT
Definition pns_meander.h:61
@ MEANDER_STYLE_ROUND
Definition pns_meander.h:54
@ MEANDER_STYLE_CHAMFER
Definition pns_meander.h:55
ROUTER_MODE
Definition pns_router.h:67
@ PNS_MODE_TUNE_DIFF_PAIR
Definition pns_router.h:71
@ PNS_MODE_TUNE_SINGLE
Definition pns_router.h:70
@ PNS_MODE_TUNE_DIFF_PAIR_SKEW
Definition pns_router.h:72
std::tuple< double, double > BoardChainBridging(const BOARD *aBoard, const wxString &aNetChain)
Compute both the chain bridging length and its associated propagation delay (in internal delay IU,...
int SubtractBridgingClamped(int aValue, long long aDelta)
Saturating subtract for bridge-adjusting MINOPTMAX<int> bounds without overflow when the delta is in ...
#define _HKI(x)
Definition page_info.cpp:40
Class to handle a set of BOARD_ITEMs.
static PNS::MEANDER_SIDE sideFromString(const std::string &aStr)
static LENGTH_TUNING_MODE tuningFromString(const std::string &aStr)
static std::string tuningToString(const LENGTH_TUNING_MODE aTuning)
static struct PCB_TUNING_PATTERN_DESC _PCB_TUNING_PATTERN_DESC
static LENGTH_TUNING_MODE fromPNSMode(PNS::ROUTER_MODE aRouterMode)
static PNS::MEANDER_PLACER_BASE::TUNING_STATUS statusFromString(const std::string &aStr)
static std::string sideToString(const PNS::MEANDER_SIDE aValue)
static std::string statusToString(const PNS::MEANDER_PLACER_BASE::TUNING_STATUS aStatus)
static GENERATORS_MGR::REGISTER< PCB_TUNING_PATTERN > registerMe
static std::optional< PNS::LINE > getPNSLine(const VECTOR2I &aStart, const VECTOR2I &aEnd, PNS::ROUTER *router, int layer, VECTOR2I &aStartOut, VECTOR2I &aEndOut)
SCOPED_SET_RESET< DRAWING_TOOL::MODE > SCOPED_DRAW_MODE
static REGISTER_LEGACY_TUNING_PATTERN< PCB_TUNING_PATTERN > registerMeToo
LENGTH_TUNING_MODE
@ DIFF_PAIR
@ DIFF_PAIR_SKEW
#define TYPE_HASH(x)
Definition property.h:74
#define NO_SETTER(owner, type)
Definition property.h:833
#define ENUM_TO_WXANY(type)
Macro to define read-only fields (no setter method available)
Definition property.h:828
@ PT_DEFAULT
Default property for a given type.
Definition property.h:62
@ PT_SIZE
Size expressed in distance units (mm/inch)
Definition property.h:63
@ PT_NET
Net selection property.
Definition property.h:70
@ PT_TIME
Time expressed in ps.
Definition property.h:69
#define REGISTER_TYPE(x)
constexpr double correction
@ RPT_SEVERITY_IGNORE
const double epsilon
#define PNS_HOVER_ITEM
#define PNS_COLLISION
#define HITTEST_THRESHOLD_PIXELS
std::vector< FAB_LAYER_COLOR > dummy
wxString UnescapeString(const wxString &aSource)
An abstract function object, returning a design rule (clearance, diff pair gap, etc) required between...
Definition pns_node.h:74
bool m_IsTimeDomain
Definition pns_node.h:81
MINOPTMAX< int > m_Value
Definition pns_node.h:76
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())
Hold an object colliding with another object, along with some useful data about the collision.
Definition pns_node.h:89
Represents a single line in the tuning profile configuration grid.
const SHAPE_LINE_CHAIN chain
VECTOR2I end
int clearance
wxString result
Test unit parsing edge cases and error handling.
@ GR_TEXT_H_ALIGN_RIGHT
@ GR_TEXT_H_ALIGN_LEFT
@ BUT_LEFT
Definition tool_event.h:128
@ BUT_RIGHT
Definition tool_event.h:129
double DEG2RAD(double deg)
Definition trigo.h:162
@ NOT_USED
the 3d code uses this value
Definition typeinfo.h:72
@ 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
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683
VECTOR2< double > VECTOR2D
Definition vector2d.h:682