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 auto getTimeDomainTuningEnabled = [this, aBoard]()
688 {
689 const std::shared_ptr<TUNING_PROFILES> tuningParams =
691 const TUNING_PROFILE& profile = tuningParams->GetTuningProfile( m_settings.m_netClass->GetTuningProfile() );
692
693 if( profile.m_EnableTimeDomainTuning )
694 return true;
695
696 return false;
697 };
698
699 if( !m_settings.m_overrideCustomRules )
700 {
701 PNS::SEGMENT pnsItem;
702 NETINFO_ITEM* net = track->GetNet();
703
704 pnsItem.SetParent( track );
705 pnsItem.SetNet( net );
706
707 if( m_tuningMode == SINGLE )
708 {
709 if( resolver->QueryConstraint( PNS::CONSTRAINT_TYPE::CT_LENGTH,
710 &pnsItem, nullptr, layer, &constraint ) )
711 {
712 if( constraint.m_IsTimeDomain )
713 {
714 m_settings.SetTargetLengthDelay( constraint.m_Value );
715 m_settings.SetTargetLength( MINOPTMAX<int>() );
716 }
717 else
718 {
719 m_settings.SetTargetLengthDelay( MINOPTMAX<int>() );
720 m_settings.SetTargetLength( constraint.m_Value );
721 }
722
723 m_settings.m_isTimeDomain = constraint.m_IsTimeDomain;
725 }
726 else if( track->GetEffectiveNetClass()->HasTuningProfile() )
727 {
728 m_settings.m_isTimeDomain = getTimeDomainTuningEnabled();
730 }
731 }
732 else
733 {
734 PCB_TRACK* coupledTrack = nullptr;
735 PNS::SEGMENT pnsCoupledItem;
736 NETINFO_ITEM* coupledNet = aBoard->DpCoupledNet( net );
737
738 if( coupledNet )
739 PNS::HELPERS::SnapToNearestTrack( m_origin, aBoard, coupledNet, &coupledTrack );
740
741 pnsCoupledItem.SetParent( coupledTrack );
742 pnsCoupledItem.SetNet( coupledNet );
743
744 if( m_tuningMode == DIFF_PAIR )
745 {
746 if( resolver->QueryConstraint( PNS::CONSTRAINT_TYPE::CT_LENGTH,
747 &pnsItem, &pnsCoupledItem, layer, &constraint ) )
748 {
749 if( constraint.m_IsTimeDomain )
750 {
751 m_settings.SetTargetLengthDelay( constraint.m_Value );
752 m_settings.SetTargetLength( MINOPTMAX<int>() );
753 }
754 else
755 {
756 m_settings.SetTargetLengthDelay( MINOPTMAX<int>() );
757 m_settings.SetTargetLength( constraint.m_Value );
758 }
759
760 m_settings.m_isTimeDomain = constraint.m_IsTimeDomain;
762 }
763 else if( track->GetEffectiveNetClass()->HasTuningProfile() )
764 {
765 m_settings.m_isTimeDomain = getTimeDomainTuningEnabled();
767 }
768 }
769 else
770 {
772 &pnsItem, &pnsCoupledItem, layer, &constraint ) )
773 {
774 if( constraint.m_IsTimeDomain )
775 {
776 m_settings.SetTargetSkewDelay( constraint.m_Value );
777 m_settings.SetTargetSkew( MINOPTMAX<int>() );
778 }
779 else
780 {
781 m_settings.SetTargetSkewDelay( MINOPTMAX<int>() );
782 m_settings.SetTargetSkew( constraint.m_Value );
783 }
784
785 m_settings.m_isTimeDomain = constraint.m_IsTimeDomain;
787 }
788 }
789 }
790 }
791}
792
793
794static std::optional<PNS::LINE> getPNSLine( const VECTOR2I& aStart, const VECTOR2I& aEnd,
795 PNS::ROUTER* router, int layer, VECTOR2I& aStartOut,
796 VECTOR2I& aEndOut )
797{
798 PNS::NODE* world = router->GetWorld();
799
800 PNS::LINKED_ITEM* startItem = PNS::HELPERS::PickSegment( router, aStart, layer, aStartOut );
801 PNS::LINKED_ITEM* endItem = PNS::HELPERS::PickSegment( router, aEnd, layer, aEndOut );
802
803 for( PNS::LINKED_ITEM* testItem : { startItem, endItem } )
804 {
805 if( !testItem )
806 continue;
807
808 PNS::LINE line = world->AssembleLine( testItem, nullptr, false, false );
809 SHAPE_LINE_CHAIN oldChain = line.CLine();
810
811 if( oldChain.PointOnEdge( aStartOut, 1 ) && oldChain.PointOnEdge( aEndOut, 1 ) )
812 return line;
813 }
814
815 return std::nullopt;
816}
817
818
819bool PCB_TUNING_PATTERN::initBaseLine( PNS::ROUTER* aRouter, int aPNSLayer, BOARD* aBoard,
820 VECTOR2I& aStart, VECTOR2I& aEnd, NETINFO_ITEM* aNet,
821 std::optional<SHAPE_LINE_CHAIN>& aBaseLine )
822{
823 PNS::NODE* world = aRouter->GetWorld();
824
825 aStart = PNS::HELPERS::SnapToNearestTrack( aStart, aBoard, aNet, nullptr );
826 aEnd = PNS::HELPERS::SnapToNearestTrack( aEnd, aBoard, aNet, nullptr );
827
828 VECTOR2I startSnapPoint, endSnapPoint;
829
830 PNS::LINKED_ITEM* startItem = PNS::HELPERS::PickSegment( aRouter, aStart, aPNSLayer, startSnapPoint );
831 PNS::LINKED_ITEM* endItem = PNS::HELPERS::PickSegment( aRouter, aEnd, aPNSLayer, endSnapPoint );
832
833 wxASSERT( startItem );
834 wxASSERT( endItem );
835
836 if( !startItem || !endItem )
837 return false;
838
839 PNS::LINE line = world->AssembleLine( startItem );
840 const SHAPE_LINE_CHAIN& chain = line.CLine();
841
842 wxASSERT( line.ContainsLink( endItem ) );
843
844 wxASSERT( chain.PointOnEdge( startSnapPoint, 40000 ) );
845 wxASSERT( chain.PointOnEdge( endSnapPoint, 40000 ) );
846
849 SHAPE_LINE_CHAIN post;
850
851 chain.Split( startSnapPoint, endSnapPoint, pre, mid, post );
852
853 aBaseLine = mid;
854
855 return true;
856}
857
858
859bool PCB_TUNING_PATTERN::initBaseLines( PNS::ROUTER* aRouter, int aPNSLayer, BOARD* aBoard )
860{
861 m_baseLineCoupled.reset();
862
863 PCB_TRACK* track = nullptr;
864
865 m_origin = PNS::HELPERS::SnapToNearestTrack( m_origin, aBoard, nullptr, &track );
866 wxCHECK( track, false );
867
868 NETINFO_ITEM* net = track->GetNet();
869
870 if( !initBaseLine( aRouter, aPNSLayer, aBoard, m_origin, m_end, net, m_baseLine ) )
871 return false;
872
873 // Generate both baselines even if we're skewing. We need the coupled baseline to run the
874 // DRC rules against.
875 if( m_tuningMode == DIFF_PAIR )
876 {
877 if( NETINFO_ITEM* coupledNet = aBoard->DpCoupledNet( net ) )
878 {
879 VECTOR2I coupledStart = PNS::HELPERS::SnapToNearestTrack( m_origin, aBoard, coupledNet, nullptr );
880 VECTOR2I coupledEnd = PNS::HELPERS::SnapToNearestTrack( m_end, aBoard, coupledNet, nullptr );
881
882 return initBaseLine( aRouter, aPNSLayer, aBoard, coupledStart, coupledEnd, coupledNet,
884 }
885
886 return false;
887 }
888
889 return true;
890}
891
892
894{
895public:
897 m_pattern( aPattern ),
898 m_wasLocked( aPattern->IsLocked() )
899 {
900 m_pattern->SetLocked( false );
901 }
902
904 {
905 if( m_wasLocked )
906 m_pattern->SetLocked( true );
907 }
908
909private:
912};
913
914
915bool PCB_TUNING_PATTERN::removeToBaseline( PNS::ROUTER* aRouter, int aPNSLayer, SHAPE_LINE_CHAIN& aBaseLine )
916{
917 VECTOR2I startSnapPoint, endSnapPoint;
918
919 std::optional<PNS::LINE> pnsLine = getPNSLine( aBaseLine.CPoint( 0 ), aBaseLine.CLastPoint(), aRouter,
920 aPNSLayer, startSnapPoint, endSnapPoint );
921
922 wxCHECK( pnsLine, false );
923
926 SHAPE_LINE_CHAIN post;
927 pnsLine->CLine().Split( startSnapPoint, endSnapPoint, pre, mid, post );
928
929 for( PNS::LINKED_ITEM* li : pnsLine->Links() )
930 aRouter->GetInterface()->RemoveItem( li );
931
932 aRouter->GetWorld()->Remove( *pnsLine );
933
934 SHAPE_LINE_CHAIN straightChain;
935 straightChain.Append( pre );
936 straightChain.Append( aBaseLine );
937 straightChain.Append( post );
938 straightChain.Simplify();
939
940 PNS::LINE straightLine( *pnsLine, straightChain );
941
942 aRouter->GetWorld()->Add( straightLine, false );
943
944 for( PNS::LINKED_ITEM* li : straightLine.Links() )
945 aRouter->GetInterface()->AddItem( li );
946
947 return true;
948}
949
950
952{
953 UNLOCKER raiiUnlocker( this );
954 SetFlags( IN_EDIT );
955
956 aTool->Router()->SyncWorld();
957
958 PNS::ROUTER* router = aTool->Router();
959 PNS_KICAD_IFACE* iface = aTool->GetInterface();
960
961 aCommit->Remove( this );
962
963 aTool->ClearRouterChanges();
964
965 // PNS layers and PCB layers have different coding. so convert PCB layer to PNS layer
966 int pnslayer = iface->GetPNSLayerFromBoardLayer( GetLayer() );
967
968 if( baselineValid() )
969 {
970 bool success = true;
971
972 success &= removeToBaseline( router, pnslayer, *m_baseLine );
973
974 if( m_tuningMode == DIFF_PAIR )
975 success &= removeToBaseline( router, pnslayer, *m_baseLineCoupled );
976
977 if( !success )
978 recoverBaseline( router, pnslayer );
979 }
980
981 const std::vector<GENERATOR_PNS_CHANGES>& allPnsChanges = aTool->GetRouterChanges();
982
983 for( const GENERATOR_PNS_CHANGES& pnsChanges : allPnsChanges )
984 {
985 const std::set<BOARD_ITEM*> routerRemovedItems = pnsChanges.removedItems;
986 const std::set<BOARD_ITEM*> routerAddedItems = pnsChanges.addedItems;
987
988 /*std::cout << "Push commits << " << allPnsChanges.size() << " routerRemovedItems "
989 << routerRemovedItems.size() << " routerAddedItems " << routerAddedItems.size()
990 << " m_removedItems " << m_removedItems.size() << std::endl;*/
991
992 for( BOARD_ITEM* item : routerRemovedItems )
993 {
994 item->ClearSelected();
995 aCommit->Remove( item );
996 }
997
998 for( BOARD_ITEM* item : routerAddedItems )
999 aCommit->Add( item );
1000 }
1001}
1002
1003
1005{
1006 PNS::SOLID queryItem;
1007
1008 SHAPE_LINE_CHAIN* chain = static_cast<SHAPE_LINE_CHAIN*>( getOutline().Clone() );
1009 queryItem.SetShape( chain ); // PNS::SOLID takes ownership
1010 queryItem.SetLayer( aPNSLayer );
1011
1012 int lineWidth = 0;
1013
1014 PNS::NODE::OBSTACLES obstacles;
1016 opts.m_useClearanceEpsilon = false;
1017
1018 PNS::NODE* world = aRouter->GetWorld();
1019 PNS::NODE* branch = world->Branch();
1020
1021 branch->QueryColliding( &queryItem, obstacles, opts );
1022
1023 for( const PNS::OBSTACLE& obs : obstacles )
1024 {
1025 PNS::ITEM* item = obs.m_item;
1026
1028 continue;
1029
1030 if( PNS::LINKED_ITEM* li = dynamic_cast<PNS::LINKED_ITEM*>( item ) )
1031 {
1032 if( lineWidth == 0 || li->Width() < lineWidth )
1033 lineWidth = li->Width();
1034 }
1035
1036 if( chain->PointInside( item->Anchor( 0 ), 10 )
1037 && chain->PointInside( item->Anchor( 1 ), 10 ) )
1038 {
1039 branch->Remove( item );
1040 }
1041 }
1042
1043 if( lineWidth == 0 )
1044 lineWidth = pcbIUScale.mmToIU( 0.1 ); // Fallback
1045
1046 if( baselineValid() )
1047 {
1048 NETINFO_ITEM* recoverNet = GetBoard()->FindNet( m_lastNetName );
1049 PNS::LINE recoverLine;
1050
1051 recoverLine.SetLayer( aPNSLayer );
1052 recoverLine.SetWidth( lineWidth );
1053 recoverLine.Line() = *m_baseLine;
1054 recoverLine.SetNet( recoverNet );
1055 branch->Add( recoverLine, false );
1056
1057 if( m_tuningMode == DIFF_PAIR )
1058 {
1059 NETINFO_ITEM* recoverCoupledNet = GetBoard()->DpCoupledNet( recoverNet );
1060 PNS::LINE recoverLineCoupled;
1061
1062 recoverLineCoupled.SetLayer( aPNSLayer );
1063 recoverLineCoupled.SetWidth( lineWidth );
1064 recoverLineCoupled.Line() = *m_baseLineCoupled;
1065 recoverLineCoupled.SetNet( recoverCoupledNet );
1066 branch->Add( recoverLineCoupled, false );
1067 }
1068 }
1069
1070 aRouter->CommitRouting( branch );
1071
1072 //wxLogWarning( "PNS baseline recovered" );
1073
1074 return true;
1075}
1076
1077
1079 bool aPrimary )
1080{
1081 PNS_KICAD_IFACE* iface = aTool->GetInterface();
1082 PNS::ROUTER* router = aTool->Router();
1083 PNS::NODE* world = router->GetWorld();
1084 VECTOR2I startSnapPoint, endSnapPoint;
1085
1086 std::optional<PNS::LINE> pnsLine = getPNSLine( aBaseLine.CPoint( 0 ), aBaseLine.CLastPoint(), router,
1087 aPNSLayer, startSnapPoint, endSnapPoint );
1088
1089 if( !pnsLine )
1090 {
1091 // TODO
1092 //recoverBaseline( aRouter );
1093 return true;
1094 }
1095
1096 PNS::NODE* branch = world->Branch();
1097
1098 SHAPE_LINE_CHAIN straightChain;
1099 {
1100 SHAPE_LINE_CHAIN pre, mid, post;
1101 pnsLine->CLine().Split( startSnapPoint, endSnapPoint, pre, mid, post );
1102
1103 straightChain.Append( pre );
1104 straightChain.Append( aBaseLine );
1105 straightChain.Append( post );
1106 straightChain.Simplify();
1107 }
1108
1109 branch->Remove( *pnsLine );
1110
1111 SHAPE_LINE_CHAIN newLineChain;
1112
1113 if( aPrimary )
1114 {
1115 m_origin = straightChain.NearestPoint( m_origin );
1116 m_end = straightChain.NearestPoint( m_end );
1117
1118 // Don't allow points too close
1119 if( ( m_end - m_origin ).EuclideanNorm() < pcbIUScale.mmToIU( 0.1 ) )
1120 {
1121 m_origin = startSnapPoint;
1122 m_end = endSnapPoint;
1123 }
1124
1125 {
1126 SHAPE_LINE_CHAIN pre, mid, post;
1127 straightChain.Split( m_origin, m_end, pre, mid, post );
1128
1129 newLineChain.Append( pre );
1130 newLineChain.Append( mid );
1131 newLineChain.Append( post );
1132
1133 m_baseLine = mid;
1134 }
1135 }
1136 else
1137 {
1138 VECTOR2I start = straightChain.NearestPoint( m_origin );
1139 VECTOR2I end = straightChain.NearestPoint( m_end );
1140
1141 {
1142 SHAPE_LINE_CHAIN pre, mid, post;
1143 straightChain.Split( start, end, pre, mid, post );
1144
1145 newLineChain.Append( pre );
1146 newLineChain.Append( mid );
1147 newLineChain.Append( post );
1148
1149 m_baseLineCoupled = mid;
1150 }
1151 }
1152
1153 PNS::LINE newLine( *pnsLine, newLineChain );
1154
1155 branch->Add( newLine, false );
1156 router->CommitRouting( branch );
1157
1158 int clearance = router->GetRuleResolver()->Clearance( &newLine, nullptr );
1159
1160 iface->DisplayItem( &newLine, clearance, true, PNS_COLLISION );
1161
1162 return true;
1163}
1164
1165
1167{
1168 if( !( GetFlags() & IN_EDIT ) )
1169 return false;
1170
1171 UNLOCKER raiiUnlocker( this ); // Unlock the pattern for editing
1172
1173 KIGFX::VIEW* view = aTool->GetManager()->GetView();
1174 PNS::ROUTER* router = aTool->Router();
1175 PNS_KICAD_IFACE* iface = aTool->GetInterface();
1176 PCB_LAYER_ID pcblayer = GetLayer();
1177
1178 auto hideRemovedItems = [&]( bool aHide )
1179 {
1180 if( view )
1181 {
1182 for( const GENERATOR_PNS_CHANGES& pnsCommit : aTool->GetRouterChanges() )
1183 {
1184 for( BOARD_ITEM* item : pnsCommit.removedItems )
1185 {
1186 if( view )
1187 view->Hide( item, aHide, aHide );
1188 }
1189 }
1190 }
1191 };
1192
1193 iface->SetStartLayerFromPCBNew( pcblayer );
1194
1195 if( router->RoutingInProgress() )
1196 {
1197 router->StopRouting();
1198 }
1199
1200 // PNS layers and PCB layers have different coding. so convert PCB layer to PNS layer
1201 int pnslayer = iface->GetPNSLayerFromBoardLayer( pcblayer );
1202
1203 if( !baselineValid() )
1204 {
1205 initBaseLines( router, pnslayer, aBoard );
1206 }
1207 else
1208 {
1209 if( resetToBaseline( aTool, pnslayer, *m_baseLine, true ) )
1210 {
1211 m_origin = m_baseLine->CPoint( 0 );
1212 m_end = m_baseLine->CLastPoint();
1213 }
1214 else
1215 {
1216 //initBaseLines( router, layer, aBoard );
1217 return false;
1218 }
1219
1220 if( m_tuningMode == DIFF_PAIR )
1221 {
1222 if( !resetToBaseline( aTool, pnslayer, *m_baseLineCoupled, false ) )
1223 {
1224 initBaseLines( router, pnslayer, aBoard );
1225 return false;
1226 }
1227 }
1228 }
1229
1230 hideRemovedItems( true );
1231 // Snap points
1232 VECTOR2I startSnapPoint, endSnapPoint;
1233
1234 wxCHECK( m_baseLine, false );
1235
1236 PNS::LINKED_ITEM* startItem = PNS::HELPERS::PickSegment( router, m_origin, pnslayer, startSnapPoint, *m_baseLine );
1237 PNS::LINKED_ITEM* endItem = PNS::HELPERS::PickSegment( router, m_end, pnslayer, endSnapPoint, *m_baseLine );
1238
1239 wxASSERT( startItem );
1240 wxASSERT( endItem );
1241
1242 if( !startItem || !endItem )
1243 return false;
1244
1245 router->SetMode( GetPNSMode() );
1246
1247 if( !router->StartRouting( startSnapPoint, startItem, pnslayer ) )
1248 {
1249 //recoverBaseline( router );
1250 return false;
1251 }
1252
1253 PNS::MEANDER_PLACER_BASE* placer = static_cast<PNS::MEANDER_PLACER_BASE*>( router->Placer() );
1254
1255 m_settings.m_keepEndpoints = true; // Required for re-grouping
1256 placer->UpdateSettings( m_settings );
1257
1258 router->Move( m_end, nullptr );
1259
1260 if( PNS::DP_MEANDER_PLACER* dpPlacer = dynamic_cast<PNS::DP_MEANDER_PLACER*>( placer ) )
1261 {
1262 m_trackWidth = dpPlacer->GetOriginPair().Width();
1263 m_diffPairGap = dpPlacer->GetOriginPair().Gap();
1264 }
1265 else
1266 {
1267 m_trackWidth = startItem->Width();
1268 m_diffPairGap = router->Sizes().DiffPairGap();
1269 }
1270
1271 m_settings = placer->MeanderSettings();
1272 m_lastNetName = iface->GetNetName( startItem->Net() );
1273 m_tuningStatus = placer->TuningStatus();
1275
1276 wxString statusMessage;
1277
1278 switch( m_tuningStatus )
1279 {
1280 case PNS::MEANDER_PLACER_BASE::TOO_LONG: statusMessage = _( "too long" ); break;
1281 case PNS::MEANDER_PLACER_BASE::TOO_SHORT: statusMessage = _( "too short" ); break;
1282 case PNS::MEANDER_PLACER_BASE::TUNED: statusMessage = _( "tuned" ); break;
1283 default: statusMessage = _( "unknown" ); break;
1284 }
1285
1286 wxString result;
1287 EDA_UNITS userUnits = EDA_UNITS::MM;
1288
1289 if( aTool->GetManager()->GetSettings() )
1290 userUnits = static_cast<EDA_UNITS>( aTool->GetManager()->GetSettings()->m_System.units );
1291
1292 if( m_settings.m_isTimeDomain )
1293 {
1295 (double) m_tuningLength );
1296 }
1297 else
1298 {
1300 (double) m_tuningLength );
1301 }
1302
1303 m_tuningInfo.Printf( wxS( "%s (%s)" ), result, statusMessage );
1304
1305 return true;
1306}
1307
1308
1310{
1311 if( !( GetFlags() & IN_EDIT ) )
1312 return;
1313
1314 ClearFlags( IN_EDIT ); // Clear the editing flag
1315
1316 KIGFX::VIEW* view = aTool->GetManager()->GetView();
1317 PNS::ROUTER* router = aTool->Router();
1318 PNS_KICAD_IFACE* iface = aTool->GetInterface();
1319 SHAPE_LINE_CHAIN bounds = getOutline();
1320 int epsilon = aBoard->GetDesignSettings().GetDRCEpsilon();
1321
1322 iface->EraseView();
1323
1324 if( router->RoutingInProgress() )
1325 {
1326 bool forceFinish = true;
1327 bool forceCommit = false;
1328
1329 router->FixRoute( m_end, nullptr, forceFinish, forceCommit );
1330 router->StopRouting();
1331 }
1332
1333 const std::vector<GENERATOR_PNS_CHANGES>& pnsCommits = aTool->GetRouterChanges();
1334
1335 for( const GENERATOR_PNS_CHANGES& pnsCommit : pnsCommits )
1336 {
1337 const std::set<BOARD_ITEM*> routerRemovedItems = pnsCommit.removedItems;
1338 const std::set<BOARD_ITEM*> routerAddedItems = pnsCommit.addedItems;
1339
1340 //std::cout << "Push commits << " << allPnsChanges.size() << " routerRemovedItems "
1341 // << routerRemovedItems.size() << " routerAddedItems " << routerAddedItems.size()
1342 // << " m_removedItems " << m_removedItems.size() << std::endl;
1343
1344 for( BOARD_ITEM* item : routerRemovedItems )
1345 {
1346 if( view )
1347 view->Hide( item, false );
1348
1349 aCommit->Remove( item );
1350 }
1351
1352 for( BOARD_ITEM* item : routerAddedItems )
1353 {
1354 aCommit->Add( item );
1355
1356 if( PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( item ) )
1357 {
1358 if( bounds.PointInside( track->GetStart(), epsilon )
1359 && bounds.PointInside( track->GetEnd(), epsilon ) )
1360 {
1361 AddItem( item );
1362 }
1363 }
1364 }
1365 }
1366}
1367
1368
1370{
1371 if( !( GetFlags() & IN_EDIT ) )
1372 return;
1373
1375
1376 PNS_KICAD_IFACE* iface = aTool->GetInterface();
1377
1378 iface->EraseView();
1379
1380 if( KIGFX::VIEW* view = aTool->GetManager()->GetView() )
1381 {
1382 for( const GENERATOR_PNS_CHANGES& pnsCommit : aTool->GetRouterChanges() )
1383 {
1384 for( BOARD_ITEM* item : pnsCommit.removedItems )
1385 view->Hide( item, false );
1386 }
1387 }
1388
1389 aTool->Router()->StopRouting();
1390}
1391
1392
1394{
1395 VECTOR2I centerlineOffset;
1396 VECTOR2I centerlineOffsetEnd;
1397
1398 if( m_tuningMode == DIFF_PAIR && m_baseLineCoupled && m_baseLineCoupled->SegmentCount() > 0 )
1399 {
1400 centerlineOffset = ( m_baseLineCoupled->CPoint( 0 ) - m_origin ) / 2;
1401 centerlineOffsetEnd = ( m_baseLineCoupled->CLastPoint() - m_end ) / 2;
1402 }
1403
1404 aPoints.AddPoint( m_origin + centerlineOffset );
1405 aPoints.AddPoint( m_end + centerlineOffsetEnd );
1406
1407 SEG base = m_baseLine && m_baseLine->SegmentCount() > 0 ? m_baseLine->CSegment( 0 )
1408 : SEG( m_origin, m_end );
1409
1410 base.A += centerlineOffset;
1411 base.B += centerlineOffset;
1412
1413 int amplitude = m_settings.m_maxAmplitude + KiROUND( m_trackWidth / 2.0 );
1414
1415 if( m_tuningMode == DIFF_PAIR )
1416 amplitude += m_trackWidth + m_diffPairGap;
1417
1418 if( m_settings.m_initialSide == -1 )
1419 amplitude *= -1;
1420
1421 VECTOR2I widthHandleOffset = ( base.B - base.A ).Perpendicular().Resize( amplitude );
1422
1423 aPoints.AddPoint( base.A + widthHandleOffset );
1424 aPoints.Point( 2 ).SetGridConstraint( IGNORE_GRID );
1425
1426 VECTOR2I spacingHandleOffset =
1427 widthHandleOffset + ( base.B - base.A ).Resize( KiROUND( m_settings.m_spacing * 1.5 ) );
1428
1429 aPoints.AddPoint( base.A + spacingHandleOffset );
1430 aPoints.Point( 3 ).SetGridConstraint( IGNORE_GRID );
1431
1432 return true;
1433}
1434
1435
1437{
1438 VECTOR2I centerlineOffset;
1439 VECTOR2I centerlineOffsetEnd;
1440
1441 if( m_tuningMode == DIFF_PAIR && m_baseLineCoupled && m_baseLineCoupled->SegmentCount() > 0 )
1442 {
1443 centerlineOffset = ( m_baseLineCoupled->CPoint( 0 ) - m_origin ) / 2;
1444 centerlineOffsetEnd = ( m_baseLineCoupled->CLastPoint() - m_end ) / 2;
1445 }
1446
1447 SEG base = m_baseLine && m_baseLine->SegmentCount() > 0 ? m_baseLine->CSegment( 0 )
1448 : SEG( m_origin, m_end );
1449
1450 base.A += centerlineOffset;
1451 base.B += centerlineOffset;
1452
1453 m_origin = aEditPoints.Point( 0 ).GetPosition() - centerlineOffset;
1454 m_end = aEditPoints.Point( 1 ).GetPosition() - centerlineOffsetEnd;
1455
1456 if( aEditPoints.Point( 2 ).IsActive() )
1457 {
1458 VECTOR2I wHandle = aEditPoints.Point( 2 ).GetPosition();
1459
1460 int value = base.LineDistance( wHandle );
1461
1462 value -= KiROUND( m_trackWidth / 2.0 );
1463
1464 if( m_tuningMode == DIFF_PAIR )
1465 value -= m_trackWidth + m_diffPairGap;
1466
1467 SetMaxAmplitude( KiROUND( value / pcbIUScale.mmToIU( 0.01 ) ) * pcbIUScale.mmToIU( 0.01 ) );
1468
1469 int side = base.Side( wHandle );
1470
1471 if( side < 0 )
1472 m_settings.m_initialSide = PNS::MEANDER_SIDE_LEFT;
1473 else
1474 m_settings.m_initialSide = PNS::MEANDER_SIDE_RIGHT;
1475 }
1476
1477 if( aEditPoints.Point( 3 ).IsActive() )
1478 {
1479 VECTOR2I wHandle = aEditPoints.Point( 2 ).GetPosition();
1480 VECTOR2I sHandle = aEditPoints.Point( 3 ).GetPosition();
1481
1482 int value = KiROUND( SEG( base.A, wHandle ).LineDistance( sHandle ) / 1.5 );
1483
1484 SetSpacing( KiROUND( value / pcbIUScale.mmToIU( 0.01 ) ) * pcbIUScale.mmToIU( 0.01 ) );
1485 }
1486
1487 return true;
1488}
1489
1490
1492{
1493 VECTOR2I centerlineOffset;
1494 VECTOR2I centerlineOffsetEnd;
1495
1496 if( m_tuningMode == DIFF_PAIR && m_baseLineCoupled && m_baseLineCoupled->SegmentCount() > 0 )
1497 {
1498 centerlineOffset = ( m_baseLineCoupled->CPoint( 0 ) - m_origin ) / 2;
1499 centerlineOffsetEnd = ( m_baseLineCoupled->CLastPoint() - m_end ) / 2;
1500 }
1501
1502 SEG base = m_baseLine && m_baseLine->SegmentCount() > 0 ? m_baseLine->CSegment( 0 )
1503 : SEG( m_origin, m_end );
1504
1505 base.A += centerlineOffset;
1506 base.B += centerlineOffset;
1507
1508 int amplitude = m_settings.m_maxAmplitude + KiROUND( m_trackWidth / 2.0 );
1509
1510 if( m_tuningMode == DIFF_PAIR )
1511 amplitude += m_trackWidth + m_diffPairGap;
1512
1513 if( m_settings.m_initialSide == -1 )
1514 amplitude *= -1;
1515
1516 VECTOR2I widthHandleOffset = ( base.B - base.A ).Perpendicular().Resize( amplitude );
1517
1518 aEditPoints.Point( 0 ).SetPosition( m_origin + centerlineOffset );
1519 aEditPoints.Point( 1 ).SetPosition( m_end + centerlineOffsetEnd );
1520
1521 aEditPoints.Point( 2 ).SetPosition( base.A + widthHandleOffset );
1522
1523 VECTOR2I spacingHandleOffset =
1524 widthHandleOffset + ( base.B - base.A ).Resize( KiROUND( m_settings.m_spacing * 1.5 ) );
1525
1526 aEditPoints.Point( 3 ).SetPosition( base.A + spacingHandleOffset );
1527
1528 return true;
1529}
1530
1531
1533{
1534 if( m_baseLine )
1535 {
1536 int clampedMaxAmplitude = m_settings.m_maxAmplitude;
1537 int minAllowedAmplitude = 0;
1538 int baselineOffset = m_tuningMode == DIFF_PAIR ? ( m_diffPairGap + m_trackWidth ) / 2 : 0;
1539
1541 {
1542 minAllowedAmplitude = baselineOffset + m_trackWidth;
1543 }
1544 else
1545 {
1546 int correction = m_trackWidth * tan( 1 - tan( DEG2RAD( 22.5 ) ) );
1547 minAllowedAmplitude = baselineOffset + correction;
1548 }
1549
1550 clampedMaxAmplitude = std::max( clampedMaxAmplitude, minAllowedAmplitude );
1551
1552 if( m_settings.m_singleSided )
1553 {
1554 SHAPE_LINE_CHAIN clBase = *m_baseLine;
1556
1557 if( m_tuningMode != DIFF_PAIR )
1558 {
1559 int amplitude = clampedMaxAmplitude + KiROUND( m_trackWidth / 2.0 );
1560
1562
1564 true ) )
1565 {
1566 chain.Append( m_settings.m_initialSide >= 0 ? right : left );
1567 chain.Append( clBase.Reverse() );
1568 chain.SetClosed( true );
1569
1570 return chain;
1571 }
1572 }
1574 {
1575 int amplitude = clampedMaxAmplitude + m_trackWidth + KiROUND( m_diffPairGap / 2.0 );
1576
1578 SHAPE_LINE_CHAIN chain1, chain2;
1579
1581 true ) )
1582 {
1583 if( m_settings.m_initialSide >= 0 )
1584 chain1.Append( right );
1585 else
1586 chain1.Append( left );
1587
1589 ARC_LOW_DEF, left, right, true ) )
1590 {
1591 if( m_settings.m_initialSide >= 0 )
1592 chain1.Append( left.Reverse() );
1593 else
1594 chain1.Append( right.Reverse() );
1595 }
1596
1597 chain1.SetClosed( true );
1598 }
1599
1601 true ) )
1602 {
1603 if( m_settings.m_initialSide >= 0 )
1604 chain2.Append( right );
1605 else
1606 chain2.Append( left );
1607
1609 ARC_LOW_DEF, left, right, true ) )
1610 {
1611 if( m_settings.m_initialSide >= 0 )
1612 chain2.Append( left.Reverse() );
1613 else
1614 chain2.Append( right.Reverse() );
1615 }
1616
1617 chain2.SetClosed( true );
1618 }
1619
1620 SHAPE_POLY_SET merged;
1621 merged.BooleanAdd( chain1, chain2 );
1622
1623 if( merged.OutlineCount() > 0 )
1624 return merged.Outline( 0 );
1625 }
1626 }
1627
1628 // Not single-sided / fallback
1629 SHAPE_POLY_SET poly;
1631
1632 int amplitude = 0;
1633
1634 if( m_tuningMode == DIFF_PAIR )
1635 amplitude = clampedMaxAmplitude + m_diffPairGap / 2 + KiROUND( m_trackWidth );
1636 else
1637 amplitude = clampedMaxAmplitude + KiROUND( m_trackWidth / 2.0 );
1638
1640
1642 {
1643 SHAPE_POLY_SET polyCoupled;
1645 ARC_LOW_DEF, false );
1646
1647 poly.ClearArcs();
1648 polyCoupled.ClearArcs();
1649
1650 SHAPE_POLY_SET merged;
1651 merged.BooleanAdd( poly, polyCoupled );
1652
1653 if( merged.OutlineCount() > 0 )
1654 return merged.Outline( 0 );
1655 }
1656
1657 if( poly.OutlineCount() > 0 )
1658 return poly.Outline( 0 );
1659 }
1660
1661 return SHAPE_LINE_CHAIN();
1662}
1663
1664
1665void PCB_TUNING_PATTERN::ViewDraw( int aLayer, KIGFX::VIEW* aView ) const
1666{
1667 if( !IsSelected() && !IsNew() )
1668 return;
1669
1670 KIGFX::PREVIEW::DRAW_CONTEXT ctx( *aView );
1671
1672 int size = KiROUND( aView->ToWorld( EDIT_POINT::POINT_SIZE ) * 0.8 );
1673 size = std::max( size, pcbIUScale.mmToIU( 0.05 ) );
1674
1675 if( !HasFlag( IN_EDIT ) )
1676 {
1677 if( m_baseLine )
1678 {
1679 for( int i = 0; i < m_baseLine->SegmentCount(); i++ )
1680 {
1681 SEG seg = m_baseLine->CSegment( i );
1682 ctx.DrawLineDashed( seg.A, seg.B, size, size / 6, true );
1683 }
1684 }
1685 else
1686 {
1687 ctx.DrawLineDashed( m_origin, m_end, size, size / 6, false );
1688 }
1689
1691 {
1692 for( int i = 0; i < m_baseLineCoupled->SegmentCount(); i++ )
1693 {
1694 SEG seg = m_baseLineCoupled->CSegment( i );
1695 ctx.DrawLineDashed( seg.A, seg.B, size, size / 6, true );
1696 }
1697 }
1698 }
1699
1701
1702 for( int i = 0; i < chain.SegmentCount(); i++ )
1703 {
1704 SEG seg = chain.Segment( i );
1705 ctx.DrawLineDashed( seg.A, seg.B, size, size / 2, false );
1706 }
1707}
1708
1709
1711{
1713
1714 props.set( "tuning_mode", tuningToString( m_tuningMode ) );
1715 props.set( "initial_side", sideToString( m_settings.m_initialSide ) );
1716 props.set( "last_status", statusToString( m_tuningStatus ) );
1717 props.set( "is_time_domain", m_settings.m_isTimeDomain );
1718
1719 props.set( "end", m_end );
1720 props.set( "corner_radius_percent", m_settings.m_cornerRadiusPercentage );
1721 props.set( "single_sided", m_settings.m_singleSided );
1722 props.set( "rounded", m_settings.m_cornerStyle == PNS::MEANDER_STYLE_ROUND );
1723
1724 props.set_iu( "max_amplitude", m_settings.m_maxAmplitude );
1725 props.set_iu( "min_amplitude", m_settings.m_minAmplitude );
1726 props.set_iu( "min_spacing", m_settings.m_spacing );
1727 props.set_iu( "target_length_min", m_settings.m_targetLength.Min() );
1728 props.set_iu( "target_length", m_settings.m_targetLength.Opt() );
1729 props.set_iu( "target_length_max", m_settings.m_targetLength.Max() );
1730 props.set_iu( "target_delay_min", m_settings.m_targetLengthDelay.Min() );
1731 props.set_iu( "target_delay", m_settings.m_targetLengthDelay.Opt() );
1732 props.set_iu( "target_delay_max", m_settings.m_targetLengthDelay.Max() );
1733 props.set_iu( "target_skew_min", m_settings.m_targetSkew.Min() );
1734 props.set_iu( "target_skew", m_settings.m_targetSkew.Opt() );
1735 props.set_iu( "target_skew_max", m_settings.m_targetSkew.Max() );
1736 props.set_iu( "last_track_width", m_trackWidth );
1737 props.set_iu( "last_diff_pair_gap", m_diffPairGap );
1738 props.set_iu( "last_tuning_length", m_tuningLength );
1739
1740 props.set( "last_netname", m_lastNetName );
1741 props.set( "override_custom_rules", m_settings.m_overrideCustomRules );
1742
1743 if( m_baseLine )
1744 props.set( "base_line", wxAny( *m_baseLine ) );
1745
1746 if( m_baseLineCoupled )
1747 props.set( "base_line_coupled", wxAny( *m_baseLineCoupled ) );
1748
1749 return props;
1750}
1751
1752
1754{
1756
1757 wxString tuningMode;
1758 aProps.get_to( "tuning_mode", tuningMode );
1759 m_tuningMode = tuningFromString( tuningMode.utf8_string() );
1760
1761 wxString side;
1762 aProps.get_to( "initial_side", side );
1763 m_settings.m_initialSide = sideFromString( side.utf8_string() );
1764
1765 wxString status;
1766 aProps.get_to( "last_status", status );
1767 m_tuningStatus = statusFromString( status.utf8_string() );
1768
1769 aProps.get_to( "is_time_domain", m_settings.m_isTimeDomain );
1770
1771 aProps.get_to( "end", m_end );
1772 aProps.get_to( "corner_radius_percent", m_settings.m_cornerRadiusPercentage );
1773 aProps.get_to( "single_sided", m_settings.m_singleSided );
1774 aProps.get_to( "side", m_settings.m_initialSide );
1775
1776 bool rounded = false;
1777 aProps.get_to( "rounded", rounded );
1779
1780 long long int val;
1781
1782 aProps.get_to_iu( "target_length", val );
1783 m_settings.SetTargetLength( val );
1784
1785 if( aProps.get_to_iu( "target_length_min", val ) )
1786 m_settings.m_targetLength.SetMin( val );
1787
1788 if( aProps.get_to_iu( "target_length_max", val ) )
1789 m_settings.m_targetLength.SetMax( val );
1790
1791 aProps.get_to_iu( "target_delay", val );
1792 m_settings.SetTargetLengthDelay( val );
1793
1794 if( aProps.get_to_iu( "target_delay_min", val ) )
1795 m_settings.m_targetLengthDelay.SetMin( val );
1796
1797 if( aProps.get_to_iu( "target_delay_max", val ) )
1798 m_settings.m_targetLengthDelay.SetMax( val );
1799
1800 int int_val;
1801
1802 aProps.get_to_iu( "target_skew", int_val );
1803 m_settings.SetTargetSkew( int_val );
1804
1805 if( aProps.get_to_iu( "target_skew_min", int_val ) )
1806 m_settings.m_targetSkew.SetMin( int_val );
1807
1808 if( aProps.get_to_iu( "target_skew_max", int_val ) )
1809 m_settings.m_targetSkew.SetMax( int_val );
1810
1811 aProps.get_to_iu( "max_amplitude", m_settings.m_maxAmplitude );
1812 aProps.get_to_iu( "min_amplitude", m_settings.m_minAmplitude );
1813 aProps.get_to_iu( "min_spacing", m_settings.m_spacing );
1814 aProps.get_to_iu( "last_track_width", m_trackWidth );
1815 aProps.get_to_iu( "last_diff_pair_gap", m_diffPairGap );
1816 aProps.get_to_iu( "last_tuning_length", m_tuningLength );
1817 aProps.get_to( "override_custom_rules", m_settings.m_overrideCustomRules );
1818
1819 aProps.get_to( "last_netname", m_lastNetName );
1820
1821 if( auto baseLine = aProps.get_opt<SHAPE_LINE_CHAIN>( "base_line" ) )
1822 m_baseLine = *baseLine;
1823
1824 if( auto baseLineCoupled = aProps.get_opt<SHAPE_LINE_CHAIN>( "base_line_coupled" ) )
1825 m_baseLineCoupled = *baseLineCoupled;
1826
1827 // Reconstruct m_tuningInfo from loaded length and status
1828 if( m_tuningLength != 0 )
1829 {
1830 wxString statusMessage;
1831
1832 switch( m_tuningStatus )
1833 {
1834 case PNS::MEANDER_PLACER_BASE::TOO_LONG: statusMessage = _( "too long" ); break;
1835 case PNS::MEANDER_PLACER_BASE::TOO_SHORT: statusMessage = _( "too short" ); break;
1836 case PNS::MEANDER_PLACER_BASE::TUNED: statusMessage = _( "tuned" ); break;
1837 default: statusMessage = _( "unknown" ); break;
1838 }
1839
1840 EDA_UNITS units = m_settings.m_isTimeDomain ? EDA_UNITS::PS : EDA_UNITS::MM;
1841 wxString lengthStr = EDA_UNIT_UTILS::UI::MessageTextFromValue( pcbIUScale, units,
1842 (double) m_tuningLength );
1843
1844 m_tuningInfo.Printf( wxS( "%s (%s)" ), lengthStr, statusMessage );
1845 }
1846}
1847
1848
1850{
1852 DRC_CONSTRAINT constraint;
1853
1854 if( !m_items.empty() )
1855 {
1856 BOARD_ITEM* startItem = static_cast<BOARD_ITEM*>( *m_items.begin() );
1857 std::shared_ptr<DRC_ENGINE>& drcEngine = GetBoard()->GetDesignSettings().m_DRCEngine;
1858
1860 {
1861 constraint = drcEngine->EvalRules( SKEW_CONSTRAINT, startItem, nullptr, GetLayer() );
1862
1863 if( !constraint.IsNull() && !settings.m_overrideCustomRules )
1864 {
1866 {
1867 settings.SetTargetSkewDelay( constraint.GetValue() );
1868 settings.SetTargetSkew( MINOPTMAX<int>() );
1869 settings.m_isTimeDomain = true;
1870 }
1871 else
1872 {
1873 settings.SetTargetSkewDelay( MINOPTMAX<int>() );
1874 settings.SetTargetSkew( constraint.GetValue() );
1875 settings.m_isTimeDomain = false;
1876 }
1877 }
1878 }
1879 else
1880 {
1881 // Prefer chain-level constraint if net is part of a chain
1882 constraint = drcEngine->EvalRules( NET_CHAIN_LENGTH_CONSTRAINT, startItem, nullptr, GetLayer() );
1883
1884 if( ( constraint.IsNull() || constraint.GetSeverity() == RPT_SEVERITY_IGNORE ) )
1885 constraint = drcEngine->EvalRules( LENGTH_CONSTRAINT, startItem, nullptr, GetLayer() );
1886
1887 if( !constraint.IsNull() && !settings.m_overrideCustomRules )
1888 {
1890 {
1891 settings.SetTargetLengthDelay( constraint.GetValue() );
1892 settings.SetTargetLength( MINOPTMAX<int>() );
1893 settings.m_isTimeDomain = true;
1894 }
1895 else
1896 {
1898 settings.SetTargetLength( constraint.GetValue() );
1899 settings.m_isTimeDomain = false;
1900 }
1901 }
1902 }
1903 }
1904
1905 DIALOG_TUNING_PATTERN_PROPERTIES dlg( aEditFrame, settings, GetPNSMode(), constraint );
1906
1907 if( dlg.ShowModal() == wxID_OK )
1908 {
1909 BOARD_COMMIT commit( aEditFrame );
1910 commit.Modify( this );
1911 m_settings = settings;
1912
1913 GENERATOR_TOOL* generatorTool = aEditFrame->GetToolManager()->GetTool<GENERATOR_TOOL>();
1914 EditStart( generatorTool, GetBoard(), &commit );
1915 Update( generatorTool, GetBoard(), &commit );
1916 EditFinish( generatorTool, GetBoard(), &commit );
1917
1918 commit.Push( _( "Edit Tuning Pattern" ) );
1919 }
1920}
1921
1922
1924 PCB_BASE_EDIT_FRAME* aFrame,
1925 bool aStatusItemsOnly )
1926{
1927 std::vector<EDA_ITEM*> previewItems;
1928 KIGFX::VIEW* view = aFrame->GetCanvas()->GetView();
1929
1930 if( auto* placer = dynamic_cast<PNS::MEANDER_PLACER_BASE*>( aTool->Router()->Placer() ) )
1931 {
1932 if( !aStatusItemsOnly )
1933 {
1934 PNS::ITEM_SET items = placer->TunedPath();
1935
1936 for( PNS::ITEM* item : items )
1937 previewItems.push_back( new ROUTER_PREVIEW_ITEM( item,
1938 aTool->Router()->GetInterface(),
1939 view, PNS_HOVER_ITEM ) );
1940 }
1941
1942 TUNING_STATUS_VIEW_ITEM* statusItem = new TUNING_STATUS_VIEW_ITEM( aFrame );
1943
1944 // Build first line: "Net-(R1-Pad2) | Chain: Chain1" OR just net if no chain
1945 wxString scopeLine;
1946 BOARD* board = GetBoard();
1947 wxString netName = m_lastNetName;
1948 wxString netChainName;
1949 if( board && !netName.IsEmpty() )
1950 {
1951 for( NETINFO_ITEM* net : board->GetNetInfo() )
1952 {
1953 if( UnescapeString( net->GetNetname() ) == netName )
1954 {
1955 netChainName = net->GetNetChain();
1956 break;
1957 }
1958 }
1959 }
1960 if( !netName.IsEmpty() )
1961 {
1962 if( !netChainName.IsEmpty() )
1963 scopeLine = wxString::Format( _( "%s | %s" ), netName, netChainName );
1964 else
1965 scopeLine = netName;
1966 }
1967 statusItem->SetScopeLine( scopeLine );
1968
1970 {
1971 if( m_settings.m_isTimeDomain )
1972 statusItem->SetMinMax( m_settings.m_targetSkewDelay.Min(), m_settings.m_targetSkewDelay.Max() );
1973 else
1974 statusItem->SetMinMax( m_settings.m_targetSkew.Min(), m_settings.m_targetSkew.Max() );
1975 }
1976 else
1977 {
1978 // Show the per-net LENGTH_CONSTRAINT directly from DRC, not the blended
1979 // budget in m_targetLength (which may include chain adjustments).
1980 bool hasNetConstraint = false;
1981
1982 if( board && board->GetDesignSettings().m_DRCEngine )
1983 {
1984 PCB_TRACK* netRepTrack = nullptr;
1985
1986 for( BOARD_ITEM* bi : board->Tracks() )
1987 {
1988 if( PCB_TRACK* tr = dynamic_cast<PCB_TRACK*>( bi ) )
1989 {
1990 if( tr->GetNet() && UnescapeString( tr->GetNet()->GetNetname() ) == netName )
1991 {
1992 netRepTrack = tr;
1993 break;
1994 }
1995 }
1996 }
1997
1998 if( netRepTrack )
1999 {
2001 LENGTH_CONSTRAINT, netRepTrack, nullptr, netRepTrack->GetLayer() );
2002
2003 if( !netC.IsNull() && netC.GetSeverity() != RPT_SEVERITY_IGNORE )
2004 {
2005 statusItem->SetMinMax(
2006 static_cast<double>( netC.GetValue().Min() ),
2007 static_cast<double>( netC.GetValue().Max() ) );
2008 hasNetConstraint = true;
2009 }
2010 }
2011 }
2012
2013 if( !hasNetConstraint )
2014 statusItem->ClearMinMax();
2015 }
2016
2017 // Set chain-level min/max from raw chain constraint
2018 if( !netChainName.IsEmpty() && board && board->GetDesignSettings().m_DRCEngine )
2019 {
2020 // Find a track on this net to evaluate the rule against
2021 PCB_TRACK* repTrack = nullptr;
2022
2023 for( BOARD_ITEM* bi : board->Tracks() )
2024 {
2025 if( PCB_TRACK* tr = dynamic_cast<PCB_TRACK*>( bi ) )
2026 {
2027 NETINFO_ITEM* ni = tr->GetNet();
2028
2029 if( ni && ni->GetNetChain() == netChainName )
2030 {
2031 repTrack = tr;
2032 break;
2033 }
2034 }
2035 }
2036
2037 if( repTrack )
2038 {
2040 NET_CHAIN_LENGTH_CONSTRAINT, repTrack, nullptr, repTrack->GetLayer() );
2041
2042 if( !chainC.IsNull() && chainC.GetSeverity() != RPT_SEVERITY_IGNORE )
2043 {
2044 statusItem->SetChainMinMax(
2045 static_cast<double>( chainC.GetValue().Min() ),
2046 static_cast<double>( chainC.GetValue().Max() ) );
2047 }
2048 else
2049 {
2050 statusItem->ClearChainMinMax();
2051 }
2052 }
2053 else
2054 {
2055 statusItem->ClearChainMinMax();
2056 }
2057 }
2058 else
2059 {
2060 statusItem->ClearChainMinMax();
2061 }
2062
2063 statusItem->SetIsTimeDomain( m_settings.m_isTimeDomain );
2064
2065 // Header label line (line 2)
2067 statusItem->SetCurrent( 0.0, _( "current skew" ) );
2068 else if( m_settings.m_isTimeDomain )
2069 statusItem->SetCurrent( 0.0, _( "current delay" ) );
2070 else
2071 statusItem->SetCurrent( 0.0, _( "current length" ) );
2072
2073 // Value lines (lines 3 & 4)
2075 double netVal = m_settings.m_isTimeDomain ? static_cast<double>( placer->TuningDelayResult() )
2076 : static_cast<double>( placer->TuningLengthResult() );
2077 wxString netStr = wxString::Format( _( "Net: %s" ),
2078 aFrame->MessageTextFromValue( netVal, true, unitType ) );
2079
2080 // Chain total from board state (GetTrackLength for every net) plus live tuning delta.
2081 wxString sigStr;
2082 bool hasSignal = false;
2083 if( !netChainName.IsEmpty() && board )
2084 {
2085 double chainBoardLen = 0.0;
2086 double chainBoardDelay = 0.0;
2087
2088 // Index tracks by netcode once so each chain member is an O(1) lookup
2089 // rather than re-scanning BOARD::Tracks() per net.
2090 std::unordered_map<int, PCB_TRACK*> repByNet;
2091
2092 for( BOARD_ITEM* bi : board->Tracks() )
2093 {
2094 if( PCB_TRACK* tr = dynamic_cast<PCB_TRACK*>( bi ) )
2095 repByNet.emplace( tr->GetNetCode(), tr );
2096 }
2097
2098 for( NETINFO_ITEM* net : board->GetNetInfo() )
2099 {
2100 if( net->GetNetChain() != netChainName )
2101 continue;
2102
2103 auto it = repByNet.find( net->GetNetCode() );
2104
2105 if( it != repByNet.end() && it->second )
2106 {
2107 int cnt = 0; double trk = 0, pd = 0, tDel = 0, pdDel = 0;
2108 std::tie( cnt, trk, pd, tDel, pdDel ) = board->GetTrackLength( *it->second );
2109 chainBoardLen += trk + pd;
2110 chainBoardDelay += tDel + pdDel;
2111 }
2112 }
2113
2114 // Add the meander extension delta (path-independent).
2115 double tuningDelta = 0.0;
2116
2117 if( placer->HasBaseline() )
2118 {
2119 tuningDelta = m_settings.m_isTimeDomain
2120 ? static_cast<double>( placer->TuningDelayDelta() )
2121 : static_cast<double>( placer->TuningLengthDelta() );
2122 }
2123
2124 double sigVal = ( m_settings.m_isTimeDomain ? chainBoardDelay : chainBoardLen )
2125 + tuningDelta;
2126
2127 // Bridging: pad-to-pad gaps through series components.
2128 double delayIU = 0.0;
2129 long long bridging = GetCachedBridgingLength( board, netChainName, &delayIU );
2130
2131 if( bridging > 0 )
2132 {
2133 if( m_settings.m_isTimeDomain )
2134 sigVal += delayIU;
2135 else
2136 sigVal += static_cast<double>( bridging );
2137 }
2138
2139 sigStr = wxString::Format( _( "Chain: %s" ),
2140 aFrame->MessageTextFromValue( sigVal, true, unitType ) );
2141 hasSignal = true;
2142 }
2143 statusItem->SetNetAndSignalValues( netStr, sigStr, hasSignal );
2144
2145 statusItem->SetPosition( aFrame->GetToolManager()->GetMousePosition() );
2146 previewItems.push_back( statusItem );
2147 }
2148
2149 return previewItems;
2150}
2151
2152
2154 std::vector<MSG_PANEL_ITEM>& aList )
2155{
2156 wxString msg;
2157 NETINFO_ITEM* primaryNet = nullptr;
2158 NETINFO_ITEM* coupledNet = nullptr;
2159 PCB_TRACK* primaryItem = nullptr;
2160 PCB_TRACK* coupledItem = nullptr;
2161 NETCLASS* netclass = nullptr;
2162 int width = 0;
2163 bool mixedWidth = false;
2164
2166
2167 aList.emplace_back( _( "Type" ), GetFriendlyName() );
2168
2169 for( EDA_ITEM* member : GetItems() )
2170 {
2171 if( PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( member ) )
2172 {
2173 if( !primaryNet )
2174 {
2175 primaryItem = track;
2176 primaryNet = track->GetNet();
2177 }
2178 else if( !coupledNet && track->GetNet() != primaryNet )
2179 {
2180 coupledItem = track;
2181 coupledNet = track->GetNet();
2182 }
2183
2184 if( !netclass )
2185 netclass = track->GetEffectiveNetClass();
2186
2187 if( !width )
2188 width = track->GetWidth();
2189 else if( width != track->GetWidth() )
2190 mixedWidth = true;
2191 }
2192 }
2193
2194 if( coupledNet )
2195 {
2196 aList.emplace_back( _( "Nets" ), UnescapeString( primaryNet->GetNetname() )
2197 + wxS( ", " )
2198 + UnescapeString( coupledNet->GetNetname() ) );
2199 }
2200 else if( primaryNet )
2201 {
2202 aList.emplace_back( _( "Net" ), UnescapeString( primaryNet->GetNetname() ) );
2203 }
2204
2205 if( netclass )
2206 aList.emplace_back( _( "Resolved Netclass" ),
2207 UnescapeString( netclass->GetHumanReadableName() ) );
2208
2209 aList.emplace_back( _( "Layer" ), LayerMaskDescribe() );
2210
2211 // Show chain name if available
2212 if( primaryNet && !primaryNet->GetNetChain().IsEmpty() )
2213 aList.emplace_back( _( "Net Chain" ), primaryNet->GetNetChain() );
2214
2215 if( width && !mixedWidth )
2216 aList.emplace_back( _( "Width" ), aFrame->MessageTextFromValue( width ) );
2217
2218 BOARD* board = GetBoard();
2219 std::shared_ptr<DRC_ENGINE>& drcEngine = board->GetDesignSettings().m_DRCEngine;
2220 DRC_CONSTRAINT constraint;
2221
2222 // Display full track length (in Pcbnew)
2223 if( board && primaryItem && primaryItem->GetNetCode() > 0 )
2224 {
2225 int count = 0;
2226 double trackLen = 0.0;
2227 double lenPadToDie = 0.0;
2228 double trackDelay = 0.0;
2229 double delayPadToDie = 0.0;
2230
2231 std::tie( count, trackLen, lenPadToDie, trackDelay, delayPadToDie ) = board->GetTrackLength( *primaryItem );
2232
2233 if( coupledItem && coupledItem->GetNetCode() > 0 )
2234 {
2235 double coupledLen = 0.0;
2236 double coupledLenPadToDie = 0.0;
2237 double coupledTrackDelay = 0.0;
2238 double doubledDelayPadToDie = 0.0;
2239
2240 std::tie( count, coupledLen, coupledLenPadToDie, coupledTrackDelay, doubledDelayPadToDie ) =
2241 board->GetTrackLength( *coupledItem );
2242
2243 if( trackDelay == 0.0 || coupledTrackDelay == 0.0 )
2244 {
2245 aList.emplace_back( _( "Routed Lengths" ), aFrame->MessageTextFromValue( trackLen ) + wxS( ", " )
2246 + aFrame->MessageTextFromValue( coupledLen ) );
2247 }
2248 else
2249 {
2250 aList.emplace_back(
2251 _( "Routed Delays" ),
2252 aFrame->MessageTextFromValue( trackDelay, true, EDA_DATA_TYPE::TIME ) + wxS( ", " )
2253 + aFrame->MessageTextFromValue( coupledTrackDelay, true, EDA_DATA_TYPE::TIME ) );
2254 }
2255 }
2256 else
2257 {
2258 if( trackDelay == 0.0 )
2259 {
2260 aList.emplace_back( _( "Routed Length" ), aFrame->MessageTextFromValue( trackLen ) );
2261 }
2262 else
2263 {
2264 aList.emplace_back( _( "Routed Delay" ),
2265 aFrame->MessageTextFromValue( trackDelay, true, EDA_DATA_TYPE::TIME ) );
2266 }
2267 }
2268
2269 if( lenPadToDie != 0 && delayPadToDie == 0.0 )
2270 {
2271 msg = aFrame->MessageTextFromValue( lenPadToDie );
2272 aList.emplace_back( _( "Pad To Die Length" ), msg );
2273
2274 msg = aFrame->MessageTextFromValue( trackLen + lenPadToDie );
2275 aList.emplace_back( _( "Full Length" ), msg );
2276 }
2277 else if( delayPadToDie > 0.0 )
2278 {
2279 msg = aFrame->MessageTextFromValue( delayPadToDie, true, EDA_DATA_TYPE::TIME );
2280 aList.emplace_back( _( "Pad To Die Delay" ), msg );
2281
2282 msg = aFrame->MessageTextFromValue( trackDelay + delayPadToDie, true, EDA_DATA_TYPE::TIME );
2283 aList.emplace_back( _( "Full Delay" ), msg );
2284 }
2285
2286 // If part of a chain, display aggregate full chain length/delay (with bridging like overlay).
2287 if( primaryNet && !primaryNet->GetNetChain().IsEmpty() )
2288 {
2289 wxString chainName = primaryNet->GetNetChain();
2290 double totalOtherLen = 0.0;
2291 double totalOtherDelay = 0.0;
2292 BOARD* boardPtr = board;
2293 if( boardPtr )
2294 {
2295 for( NETINFO_ITEM* other : boardPtr->GetNetInfo() )
2296 {
2297 if( other->GetNetChain() != chainName || other == primaryNet )
2298 continue;
2299
2300 // Representative track length for this other net
2301 double oTrackLen = 0.0, oPadDieLen = 0.0, oTrackDelay = 0.0, oPadDieDelay = 0.0;
2302 PCB_TRACK* anyTrack = nullptr;
2303 for( BOARD_ITEM* bi : boardPtr->Tracks() )
2304 {
2305 if( PCB_TRACK* tr = dynamic_cast<PCB_TRACK*>( bi ) )
2306 {
2307 if( tr->GetNetCode() == other->GetNetCode() ) { anyTrack = tr; break; }
2308 }
2309 }
2310 if( anyTrack )
2311 {
2312 int dummyCount = 0;
2313 std::tie( dummyCount, oTrackLen, oPadDieLen, oTrackDelay, oPadDieDelay ) =
2314 boardPtr->GetTrackLength( *anyTrack );
2315 totalOtherLen += ( oTrackLen + oPadDieLen );
2316 totalOtherDelay += ( oTrackDelay + oPadDieDelay );
2317 }
2318 }
2319 }
2320
2321 // Bridging contribution (pad-to-pad gaps in 2-net series components of the chain)
2322 if( trackDelay == 0.0 )
2323 {
2324 double delayIUDummy = 0.0; // not used in length mode
2325 long long bridging = GetCachedBridgingLength( boardPtr, chainName, &delayIUDummy );
2326 aList.emplace_back( _( "Net Chain Full Length" ),
2327 aFrame->MessageTextFromValue( ( trackLen + lenPadToDie ) + totalOtherLen
2328 + (double) bridging ) );
2329 }
2330 else
2331 {
2332 double bridgingDelayIU = 0.0;
2333 GetCachedBridgingLength( boardPtr, chainName, &bridgingDelayIU );
2334 aList.emplace_back( _( "Net Chain Full Delay" ),
2335 aFrame->MessageTextFromValue( ( trackDelay + delayPadToDie ) + totalOtherDelay
2336 + bridgingDelayIU,
2337 true, EDA_DATA_TYPE::TIME ) );
2338 }
2339 }
2340 }
2341
2343 {
2344 constraint = drcEngine->EvalRules( SKEW_CONSTRAINT, primaryItem, coupledItem, GetLayer() );
2345
2346 if( constraint.IsNull() || m_settings.m_overrideCustomRules )
2347 {
2348 msg = aFrame->MessageTextFromValue( m_settings.m_targetSkew.Opt() );
2349
2350 aList.emplace_back( wxString::Format( _( "Target Skew: %s" ), msg ),
2351 wxString::Format( _( "(from tuning pattern properties)" ) ) );
2352 }
2353 else
2354 {
2355 msg = aFrame->MessageTextFromMinOptMax( constraint.GetValue() );
2356
2357 if( !msg.IsEmpty() )
2358 {
2359 aList.emplace_back( wxString::Format( _( "Skew Constraints: %s" ), msg ),
2360 wxString::Format( _( "(from %s)" ), constraint.GetName() ) );
2361 }
2362 }
2363 }
2364 else
2365 {
2366 // Prefer chain-level constraint if available
2367 constraint = drcEngine->EvalRules( NET_CHAIN_LENGTH_CONSTRAINT, primaryItem, coupledItem, GetLayer() );
2368
2369 if( constraint.IsNull() || constraint.GetSeverity() == RPT_SEVERITY_IGNORE )
2370 constraint = drcEngine->EvalRules( LENGTH_CONSTRAINT, primaryItem, coupledItem, GetLayer() );
2371
2372 if( constraint.IsNull() || m_settings.m_overrideCustomRules )
2373 {
2374 wxString caption;
2375
2376 if( m_settings.m_isTimeDomain )
2377 {
2378 caption = _( "Target Delay: %s" );
2379 msg = aFrame->MessageTextFromValue( static_cast<double>( m_settings.m_targetLengthDelay.Opt() ), true,
2381 }
2382 else
2383 {
2384 caption = _( "Target Length: %s" );
2385 msg = aFrame->MessageTextFromValue( static_cast<double>( m_settings.m_targetLength.Opt() ) );
2386 }
2387
2388 aList.emplace_back( wxString::Format( caption, msg ),
2389 wxString::Format( _( "(from tuning pattern properties)" ) ) );
2390 }
2391 else
2392 {
2393 msg = aFrame->MessageTextFromMinOptMax( constraint.GetValue(), unitType );
2394
2395 wxString caption = m_settings.m_isTimeDomain ? _( "Delay Constraints: %s" ) : _( "Length Constraints: %s" );
2396
2397 if( !msg.IsEmpty() )
2398 {
2399 aList.emplace_back( wxString::Format( caption, msg ),
2400 wxString::Format( _( "(from %s)" ), constraint.GetName() ) );
2401 }
2402 }
2403 }
2404}
2405
2406
2407const wxString PCB_TUNING_PATTERN::DISPLAY_NAME = _HKI( "Tuning Pattern" );
2408const wxString PCB_TUNING_PATTERN::GENERATOR_TYPE = wxS( "tuning_pattern" );
2409
2410
2412
2413
2414#define HITTEST_THRESHOLD_PIXELS 5
2415
2416
2418{
2420 return 0;
2421
2422 if( m_inDrawingTool )
2423 return 0;
2424
2426
2427 m_toolMgr->RunAction( ACTIONS::selectionClear );
2428
2429 m_frame->PushTool( aEvent );
2430 Activate();
2431
2432 BOARD* board = m_frame->GetBoard();
2433 BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings();
2434 std::shared_ptr<DRC_ENGINE>& drcEngine = bds.m_DRCEngine;
2435 GENERATOR_TOOL* generatorTool = m_toolMgr->GetTool<GENERATOR_TOOL>();
2436 PNS::ROUTER* router = generatorTool->Router();
2437 PNS::ROUTER_MODE routerMode = aEvent.Parameter<PNS::ROUTER_MODE>();
2438 LENGTH_TUNING_MODE mode = fromPNSMode( routerMode );
2439 PNS::MEANDER_SETTINGS meanderSettings;
2440
2441 switch( mode )
2442 {
2443 case LENGTH_TUNING_MODE::SINGLE: meanderSettings = bds.m_SingleTrackMeanderSettings; break;
2444 case DIFF_PAIR: meanderSettings = bds.m_DiffPairMeanderSettings; break;
2445 case DIFF_PAIR_SKEW: meanderSettings = bds.m_SkewMeanderSettings; break;
2446 }
2447
2449 PCB_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
2450 GENERAL_COLLECTORS_GUIDE guide = m_frame->GetCollectorsGuide();
2451 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::TUNING );
2452
2453 m_pickerItem = nullptr;
2454 m_tuningPattern = nullptr;
2455
2456 // Add a VIEW_GROUP that serves as a preview for the new item
2457 m_preview.Clear();
2458 m_view->Add( &m_preview );
2459
2460 auto applyCommonSettings = [&]( PCB_TUNING_PATTERN* aPattern )
2461 {
2462 const auto origTargetLength = aPattern->GetSettings().m_targetLength;
2463 const auto origTargetLengthDelay = aPattern->GetSettings().m_targetLengthDelay;
2464 const auto origTargetSignalLength = aPattern->GetSettings().m_targetSignalLength;
2465 const auto origTargetSignalLengthDelay = aPattern->GetSettings().m_targetSignalLengthDelay;
2466 const auto origTargetSkew = aPattern->GetSettings().m_targetSkew;
2467 const bool origIsTimeDomain = aPattern->GetSettings().m_isTimeDomain;
2468
2469 aPattern->GetSettings() = meanderSettings;
2470
2471 // Always preserve DRC-evaluated targets
2472 aPattern->GetSettings().m_targetLength = origTargetLength;
2473 aPattern->GetSettings().m_targetLengthDelay = origTargetLengthDelay;
2474 aPattern->GetSettings().m_targetSignalLength = origTargetSignalLength;
2475 aPattern->GetSettings().m_targetSignalLengthDelay = origTargetSignalLengthDelay;
2476 aPattern->GetSettings().m_targetSkew = origTargetSkew;
2477 aPattern->GetSettings().m_isTimeDomain = origIsTimeDomain;
2478 };
2479
2480 auto updateHoverStatus =
2481 [&]()
2482 {
2483 std::unique_ptr<PCB_TUNING_PATTERN> dummyPattern;
2484
2485 if( m_pickerItem )
2486 {
2487 dummyPattern.reset( PCB_TUNING_PATTERN::CreateNew( generatorTool, m_frame,
2488 m_pickerItem, mode ) );
2489 dummyPattern->SetPosition( m_pickerItem->GetFocusPosition() );
2490 dummyPattern->SetEnd( m_pickerItem->GetFocusPosition() );
2491 }
2492
2493 if( dummyPattern )
2494 {
2495 applyCommonSettings( dummyPattern.get() );
2496
2497 dummyPattern->EditStart( generatorTool, m_board, nullptr );
2498 dummyPattern->Update( generatorTool, m_board, nullptr );
2499
2500 m_preview.FreeItems();
2501
2502 for( EDA_ITEM* item : dummyPattern->GetPreviewItems( generatorTool, m_frame ) )
2503 m_preview.Add( item );
2504
2505 generatorTool->Router()->StopRouting();
2506
2507 m_view->Update( &m_preview );
2508 }
2509 else
2510 {
2511
2512 m_preview.FreeItems();
2513 m_view->Update( &m_preview );
2514 }
2515 };
2516
2517 auto updateTuningPattern =
2518 [&]()
2519 {
2520 if( m_tuningPattern && m_tuningPattern->GetPosition() != m_tuningPattern->GetEnd() )
2521 {
2522 m_tuningPattern->EditStart( generatorTool, m_board, nullptr );
2523 m_tuningPattern->Update( generatorTool, m_board, nullptr );
2524
2525 m_preview.FreeItems();
2526
2527 for( EDA_ITEM* item : m_tuningPattern->GetPreviewItems( generatorTool, m_frame, true ) )
2528 m_preview.Add( item );
2529
2530 m_view->Update( &m_preview );
2531 }
2532 };
2533
2534 while( TOOL_EVENT* evt = Wait() )
2535 {
2536 VECTOR2D cursorPos = controls->GetMousePosition();
2537
2538 if( evt->IsCancelInteractive() || evt->IsActivate()
2539 || ( m_tuningPattern && evt->IsAction( &ACTIONS::undo ) ) )
2540 {
2541 if( m_tuningPattern )
2542 {
2543 // First click already made; clean up tuning pattern preview
2544 m_tuningPattern->EditCancel( generatorTool, m_board, nullptr );
2545
2546 delete m_tuningPattern;
2547 m_tuningPattern = nullptr;
2548 }
2549 else
2550 {
2551 break;
2552 }
2553 }
2554 else if( evt->IsMotion() )
2555 {
2556 if( !m_tuningPattern )
2557 {
2558 // First click not yet made; we're in highlight-net-under-cursor mode
2559
2560 GENERAL_COLLECTOR collector;
2561 collector.m_Threshold = KiROUND( getView()->ToWorld( HITTEST_THRESHOLD_PIXELS ) );
2562
2563 if( m_frame->GetDisplayOptions().m_ContrastModeDisplay != HIGH_CONTRAST_MODE::NORMAL )
2564 guide.SetIncludeSecondary( false );
2565 else
2566 guide.SetIncludeSecondary( true );
2567
2568 guide.SetPreferredLayer( m_frame->GetActiveLayer() );
2569
2570 collector.Collect( board, { PCB_TRACE_T, PCB_ARC_T }, cursorPos, guide );
2571
2572 if( collector.GetCount() > 1 )
2573 selectionTool->GuessSelectionCandidates( collector, cursorPos );
2574
2575 m_pickerItem = nullptr;
2576
2577 if( collector.GetCount() > 0 )
2578 {
2579 double min_dist_sq = std::numeric_limits<double>::max();
2580
2581 for( EDA_ITEM* candidate : collector )
2582 {
2583 VECTOR2I candidatePos;
2584
2585 if( candidate->Type() == PCB_TRACE_T )
2586 {
2587 candidatePos = static_cast<PCB_TRACK*>( candidate )->GetCenter();
2588 }
2589 else if( candidate->Type() == PCB_ARC_T )
2590 {
2591 candidatePos = static_cast<PCB_ARC*>( candidate )->GetMid();
2592 }
2593
2594 double dist_sq = ( cursorPos - candidatePos ).SquaredEuclideanNorm();
2595
2596 if( dist_sq < min_dist_sq )
2597 {
2598 min_dist_sq = dist_sq;
2599 m_pickerItem = static_cast<BOARD_CONNECTED_ITEM*>( candidate );
2600 }
2601 }
2602 }
2603
2604 updateHoverStatus();
2605 }
2606 else
2607 {
2608 // First click already made; we're in preview-tuning-pattern mode
2609
2610 m_tuningPattern->SetEnd( cursorPos );
2611 m_tuningPattern->UpdateSideFromEnd();
2612
2613 updateTuningPattern();
2614 }
2615 }
2616 else if( evt->IsClick( BUT_LEFT ) )
2617 {
2619 {
2620 // First click; create a tuning pattern
2621
2622 if( dynamic_cast<PCB_TUNING_PATTERN*>( m_pickerItem->GetParentGroup() ) )
2623 {
2624 m_frame->ShowInfoBarWarning( _( "Unable to tune segments inside other "
2625 "tuning patterns." ) );
2626 }
2627 else
2628 {
2629 m_preview.FreeItems();
2630
2631 m_frame->SetActiveLayer( m_pickerItem->GetLayer() );
2633 m_pickerItem, mode );
2634
2635 m_tuningPattern->GetSettings().m_signalExtraLength = 0;
2636 m_tuningPattern->GetSettings().m_signalExtraDelay = 0;
2637
2638 applyCommonSettings( m_tuningPattern );
2639
2640 int dummyDist;
2641 int dummyClearance = std::numeric_limits<int>::max() / 2;
2642 VECTOR2I closestPt;
2643
2644 // With an artificially-large clearance this can't *not* collide, but the
2645 // if stmt keeps Coverity happy....
2646 if( m_pickerItem->GetEffectiveShape()->Collide( cursorPos, dummyClearance,
2647 &dummyDist, &closestPt ) )
2648 {
2649 m_tuningPattern->SetPosition( closestPt );
2650 m_tuningPattern->SetEnd( closestPt );
2651 }
2652
2653 m_preview.Add( m_tuningPattern->Clone() );
2654 }
2655 }
2656 else if( m_pickerItem && m_tuningPattern )
2657 {
2658 // Second click; we're done
2659 BOARD_COMMIT commit( m_frame );
2660
2661 m_tuningPattern->EditStart( generatorTool, m_board, &commit );
2662 m_tuningPattern->Update( generatorTool, m_board, &commit );
2663 m_tuningPattern->EditFinish( generatorTool, m_board, &commit );
2664
2665 commit.Push( _( "Tune" ) );
2666
2667 m_tuningPattern = nullptr;
2668 m_pickerItem = nullptr;
2669 }
2670 }
2671 else if( evt->IsClick( BUT_RIGHT ) )
2672 {
2674 m_menu->ShowContextMenu( dummy );
2675 }
2676 else if( evt->IsAction( &PCB_ACTIONS::spacingIncrease )
2677 || evt->IsAction( &PCB_ACTIONS::spacingDecrease ) )
2678 {
2679 if( m_tuningPattern )
2680 {
2681 auto* placer = static_cast<PNS::MEANDER_PLACER_BASE*>( router->Placer() );
2682
2683 if( placer )
2684 {
2685 placer->SpacingStep( evt->IsAction( &PCB_ACTIONS::spacingIncrease ) ? 1 : -1 );
2686 m_tuningPattern->SetSpacing( placer->MeanderSettings().m_spacing );
2687 meanderSettings.m_spacing = placer->MeanderSettings().m_spacing;
2688
2689 updateTuningPattern();
2690 }
2691 }
2692 else
2693 {
2694 m_frame->ShowInfoBarWarning( _( "Select a track to tune first." ) );
2695 }
2696 }
2697 else if( evt->IsAction( &PCB_ACTIONS::amplIncrease )
2698 || evt->IsAction( &PCB_ACTIONS::amplDecrease ) )
2699 {
2700 if( m_tuningPattern )
2701 {
2702 auto* placer = static_cast<PNS::MEANDER_PLACER_BASE*>( router->Placer() );
2703
2704 if( placer )
2705 {
2706 placer->AmplitudeStep( evt->IsAction( &PCB_ACTIONS::amplIncrease ) ? 1 : -1 );
2707 m_tuningPattern->SetMaxAmplitude( placer->MeanderSettings().m_maxAmplitude );
2708 meanderSettings.m_maxAmplitude = placer->MeanderSettings().m_maxAmplitude;
2709
2710 updateTuningPattern();
2711 }
2712 }
2713 else
2714 {
2715 m_frame->ShowInfoBarWarning( _( "Select a track to tune first." ) );
2716 }
2717 }
2718 else if( evt->IsAction( &PCB_ACTIONS::properties )
2719 || evt->IsAction( &PCB_ACTIONS::lengthTunerSettings ) )
2720 {
2721 DRC_CONSTRAINT constraint;
2722
2723 if( m_tuningPattern )
2724 {
2725 if( !m_tuningPattern->GetItems().empty() )
2726 {
2727 BOARD_ITEM* startItem = *m_tuningPattern->GetBoardItems().begin();
2728
2729 constraint = drcEngine->EvalRules( LENGTH_CONSTRAINT, startItem, nullptr,
2730 startItem->GetLayer() );
2731 }
2732 }
2733
2734 DIALOG_TUNING_PATTERN_PROPERTIES dlg( m_frame, meanderSettings, routerMode, constraint );
2735
2736 if( dlg.ShowModal() == wxID_OK )
2737 {
2738 if( m_tuningPattern )
2739 applyCommonSettings( m_tuningPattern );
2740
2741 updateTuningPattern();
2742 }
2743 }
2744 // TODO: It'd be nice to be able to say "don't allow any non-trivial editing actions",
2745 // but we don't at present have that, so we just knock out some of the egregious ones.
2746 else if( ZONE_FILLER_TOOL::IsZoneFillAction( evt ) )
2747 {
2748 wxBell();
2749 }
2750 else
2751 {
2752 evt->SetPassEvent();
2753 }
2754
2755 controls->CaptureCursor( m_tuningPattern != nullptr );
2756 controls->SetAutoPan( m_tuningPattern != nullptr );
2757 }
2758
2759 controls->CaptureCursor( false );
2760 controls->SetAutoPan( false );
2761 controls->ForceCursorPosition( false );
2762 controls->ShowCursor( false );
2763 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2764
2765 m_preview.FreeItems();
2766 m_view->Remove( &m_preview );
2767
2768 m_frame->GetCanvas()->Refresh();
2769
2770 if( m_tuningPattern )
2771 selectionTool->AddItemToSel( m_tuningPattern );
2772
2773 m_frame->PopTool( aEvent );
2774 return 0;
2775}
2776
2777
2779{
2781 {
2783 .Map( LENGTH_TUNING_MODE::SINGLE, _HKI( "Single track" ) )
2784 .Map( LENGTH_TUNING_MODE::DIFF_PAIR, _HKI( "Differential pair" ) )
2785 .Map( LENGTH_TUNING_MODE::DIFF_PAIR_SKEW, _HKI( "Diff pair skew" ) );
2786
2788 .Map( PNS::MEANDER_SIDE_LEFT, _HKI( "Left" ) )
2789 .Map( PNS::MEANDER_SIDE_RIGHT, _HKI( "Right" ) )
2790 .Map( PNS::MEANDER_SIDE_DEFAULT, _HKI( "Default" ) );
2791
2798
2800
2801 if( layerEnum.Choices().GetCount() == 0 )
2802 {
2803 layerEnum.Undefined( UNDEFINED_LAYER );
2804
2805 for( PCB_LAYER_ID layer : LSET::AllLayersMask() )
2806 layerEnum.Map( layer, LSET::Name( layer ) );
2807 }
2808
2811 layer->SetChoices( layerEnum.Choices() );
2812 propMgr.ReplaceProperty( TYPE_HASH( BOARD_ITEM ), _HKI( "Layer" ), layer );
2813
2814 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, int>( _HKI( "Width" ),
2817
2820
2821 const wxString groupTechLayers = _HKI( "Technical Layers" );
2822
2823 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, bool>( _HKI( "Soldermask" ),
2825 groupTechLayers );
2826
2827 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, std::optional<int>>( _HKI( "Soldermask Margin Override" ),
2831 groupTechLayers );
2832
2833 const wxString groupTab = _HKI( "Pattern Properties" );
2834
2835 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, int>( _HKI( "End X" ),
2838 groupTab );
2839
2840 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, int>( _HKI( "End Y" ),
2843 groupTab );
2844
2848 groupTab );
2849
2850 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, int>( _HKI( "Min Amplitude" ),
2853 groupTab );
2854
2855 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, int>( _HKI( "Max Amplitude" ),
2858 groupTab );
2859
2862 groupTab );
2863
2864 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, int>( _HKI( "Min Spacing" ),
2867 groupTab );
2868
2869 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, int>( _HKI( "Corner Radius %" ),
2873 groupTab );
2874
2875 auto isSkew =
2876 []( INSPECTABLE* aItem ) -> bool
2877 {
2878 if( PCB_TUNING_PATTERN* pattern = dynamic_cast<PCB_TUNING_PATTERN*>( aItem ) )
2879 return pattern->GetTuningMode() == DIFF_PAIR_SKEW;
2880
2881 return false;
2882 };
2883
2884 auto isTimeDomain = []( INSPECTABLE* aItem ) -> bool
2885 {
2886 if( PCB_TUNING_PATTERN* pattern = dynamic_cast<PCB_TUNING_PATTERN*>( aItem ) )
2887 return pattern->GetSettings().m_isTimeDomain;
2888
2889 return false;
2890 };
2891
2892 auto isLengthIsSpaceDomain = [&]( INSPECTABLE* aItem ) -> bool
2893 {
2894 return !isSkew( aItem ) && !isTimeDomain( aItem );
2895 };
2896
2897 auto isLengthIsTimeDomain = [&]( INSPECTABLE* aItem ) -> bool
2898 {
2899 return !isSkew( aItem ) && isTimeDomain( aItem );
2900 };
2901
2902 auto isSkewIsSpaceDomain = [&]( INSPECTABLE* aItem ) -> bool
2903 {
2904 return isSkew( aItem ) && !isTimeDomain( aItem );
2905 };
2906
2907 auto isSkewIsTimeDomain = [&]( INSPECTABLE* aItem ) -> bool
2908 {
2909 return isSkew( aItem ) && isTimeDomain( aItem );
2910 };
2911
2912 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, std::optional<int>>( _HKI( "Target Length" ),
2915 groupTab )
2916 .SetAvailableFunc( isLengthIsSpaceDomain );
2917
2918 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, std::optional<int>>( _HKI( "Target Delay" ),
2921 groupTab )
2922 .SetAvailableFunc( isLengthIsTimeDomain );
2923
2924 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, int>( _HKI( "Target Skew" ),
2927 groupTab )
2928 .SetAvailableFunc( isSkewIsSpaceDomain );
2929
2930 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, int>( _HKI( "Target Skew Delay" ),
2933 groupTab )
2934 .SetAvailableFunc( isSkewIsTimeDomain );
2935
2936 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, bool>( _HKI( "Override Custom Rules" ),
2939 groupTab );
2940
2941 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, bool>( _HKI( "Single-sided" ),
2943 groupTab );
2944
2945 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, bool>( _HKI( "Rounded" ),
2947 groupTab );
2948 }
2950
2953
2955
2956// Also register under the 7.99 name
2957template <typename T>
2959{
2961 {
2962 GENERATORS_MGR::Instance().Register( wxS( "meanders" ), T::DISPLAY_NAME,
2963 []()
2964 {
2965 return new T;
2966 } );
2967 }
2968};
2969
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
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:373
const NETINFO_LIST & GetNetInfo() const
Definition board.h:1094
NETINFO_ITEM * DpCoupledNet(const NETINFO_ITEM *aNet)
Definition board.cpp:2775
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:3289
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition board.cpp:2708
const FOOTPRINTS & Footprints() const
Definition board.h:421
const TRACKS & Tracks() const
Definition board.h:419
PROJECT * GetProject() const
Definition board.h:658
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)
bool recoverBaseline(PNS::ROUTER *aRouter, int aPNSLayer)
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.
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