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#include <api/api_enums.h>
45#include <api/api_utils.h>
46#include <api/board/board_types.pb.h>
47#include <google/protobuf/any.pb.h>
48
49#include <board.h>
52#include <drc/drc_engine.h>
53#include <pcb_track.h>
54#include <pcb_shape.h>
55#include <pcb_group.h>
56#include <pad.h>
57#include <footprint.h>
58
59#include <tool/edit_points.h>
60#include <tool/tool_manager.h>
61#include <tools/drawing_tool.h>
66
69#include <view/view.h>
70#include <view/view_controls.h>
71
74#include <router/pns_meander.h>
76#include <router/pns_segment.h>
77#include <router/pns_arc.h>
78#include <router/pns_solid.h>
79#include <router/pns_topology.h>
81#include <router/pns_helpers.h>
82
84
86#include <net_chain_bridging.h>
89#include <properties/property.h>
91
92// (Removed previous toggleable chain/net tuning scope. We now always display per-net
93// values and, when the net belongs to a chain, aggregated chain values concurrently.)
94
96 EDA_ITEM( NOT_USED ), // Never added to anything - just a preview
97 m_frame( aFrame ),
98 m_min( 0.0 ),
99 m_max( 0.0 ),
100 m_current( 0.0 ),
101 m_isTimeDomain( false )
102{ }
103
104
106{
107 return wxT( "TUNING_STATUS" );
108}
109
110#if defined(DEBUG)
111void TUNING_STATUS_VIEW_ITEM::Show( int nestLevel, std::ostream& os ) const {}
112#endif
113
114
117
118
119void TUNING_STATUS_VIEW_ITEM::SetMinMax( const double aMin, const double aMax )
120{
122
123 m_min = aMin;
124 m_minText = m_frame->MessageTextFromValue( m_min, false, unitType );
125 m_max = aMax;
126 m_maxText = m_frame->MessageTextFromValue( m_max, false, unitType );
127}
128
129
130void TUNING_STATUS_VIEW_ITEM::SetChainMinMax( const double aMin, const double aMax )
131{
133
134 m_chainMin = aMin;
135 m_chainMinText = m_frame->MessageTextFromValue( m_chainMin, false, unitType );
136 m_chainMax = aMax;
137 m_chainMaxText = m_frame->MessageTextFromValue( m_chainMax, false, unitType );
138}
139
140
142{
143 m_min = 0.0;
144 m_minText = wxT( "---" );
145 m_max = std::numeric_limits<double>::max();
146 m_maxText = wxT( "---" );
147}
148
149
151{
152 m_chainMin = 0.0;
153 m_chainMinText = wxT( "---" );
154 m_chainMax = std::numeric_limits<double>::max();
155 m_chainMaxText = wxT( "---" );
156}
157
158
159void TUNING_STATUS_VIEW_ITEM::SetCurrent( const double aCurrent, const wxString& aLabel )
160{
162
163 m_current = aCurrent;
164 m_currentText = m_frame->MessageTextFromValue( aCurrent, true, unitType );
165 m_currentLabel = aLabel;
166}
167
168
169void TUNING_STATUS_VIEW_ITEM::SetIsTimeDomain( const bool aIsTimeDomain )
170{
171 m_isTimeDomain = aIsTimeDomain;
172
174
175 m_chainMinText = m_frame->MessageTextFromValue( m_chainMin, false, unitType );
176 m_chainMaxText = m_frame->MessageTextFromValue( m_chainMax, false, unitType );
177}
178
179
181{
182 BOX2I tmp;
183
184 // this is an edit-time artefact; no reason to try and be smart with the bounding box
185 // (besides, we can't tell the text extents without a view to know what the scale is)
186 tmp.SetMaximum();
187 return tmp;
188}
189
190
192{
193 return { LAYER_UI_START, LAYER_UI_START + 1 };
194}
195
196
197void TUNING_STATUS_VIEW_ITEM::ViewDraw( int aLayer, KIGFX::VIEW* aView ) const
198{
199 KIGFX::GAL* gal = aView->GetGAL();
200 bool viewFlipped = gal->IsFlippedX();
201 bool drawingDropShadows = ( aLayer == LAYER_UI_START );
202
203 gal->Save();
204 gal->Scale( { 1., 1. } );
205
209 const KIFONT::METRICS& fontMetrics = KIFONT::METRICS::Default();
210 TEXT_ATTRIBUTES textAttrs;
211
212 int glyphWidth = textDims.GlyphSize.x;
213 VECTOR2I margin( KiROUND( glyphWidth * 0.4 ), glyphWidth );
214 int scopeLines = m_scopeLine.IsEmpty() ? 0 : 1;
215 int valueLines = 1 + ( m_hasSignalValue ? 1 : 0 );
216 int totalHeaderLines = scopeLines + 1;
217 int totalLines = totalHeaderLines + valueLines;
218 VECTOR2I size( glyphWidth * 38 + margin.x * 2,
219 headerDims.GlyphSize.y * totalLines + textDims.GlyphSize.y * valueLines );
220 VECTOR2I offset( margin.x * 2, -( size.y + margin.y * 2 ) );
221
222 if( drawingDropShadows )
223 {
224 gal->SetIsFill( true );
225 gal->SetIsStroke( true );
226 gal->SetLineWidth( gal->GetScreenWorldMatrix().GetScale().x * 2 );
227 gal->SetStrokeColor( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNTEXT ) );
228 KIGFX::COLOR4D bgColor( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
229 gal->SetFillColor( bgColor.WithAlpha( 0.9 ) );
230
231 gal->DrawRectangle( GetPosition() + offset - margin,
232 GetPosition() + offset + size + margin );
233 gal->Restore();
234 return;
235 }
236
237 COLOR4D bg = wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE );
238 COLOR4D normal = wxSystemSettings::GetColour( wxSYS_COLOUR_BTNTEXT );
239 COLOR4D red;
241
242 double bg_h, bg_s, bg_l;
243 bg.ToHSL( bg_h, bg_s, bg_l );
244
245 // Choose colors with reasonable contrasting with the background
246 red.FromHSL( 0.0, 1.0, bg_l < 0.5 ? 0.7 : 0.3 );
247 green.FromHSL( 120.0, 1.0, bg_l < 0.5 ? 0.8 : 0.2 );
248
249 if( viewFlipped )
251 else
252 textAttrs.m_Halign = GR_TEXT_H_ALIGN_LEFT;
253
254 gal->SetIsFill( false );
255 gal->SetIsStroke( true );
256 gal->SetStrokeColor( normal );
257 textAttrs.m_Halign = GR_TEXT_H_ALIGN_LEFT;
258
259 // Prevent text flipping when view is flipped
260 if( gal->IsFlippedX() )
261 {
262 textAttrs.m_Mirrored = true;
264 }
265
266 textAttrs.m_Size = headerDims.GlyphSize;
267 textAttrs.m_StrokeWidth = headerDims.StrokeWidth;
268
269 VECTOR2I textPos = GetPosition() + offset;
270
271 if( scopeLines )
272 {
273 textAttrs.m_Size = headerDims.GlyphSize;
274 textAttrs.m_StrokeWidth = headerDims.StrokeWidth;
275 font->Draw( gal, m_scopeLine, textPos, textAttrs, fontMetrics );
276 textPos.y += KiROUND( headerDims.LinePitch * 1.1 );
277 }
278
279 // Line 2: header labels (current length min max)
280 font->Draw( gal, m_currentLabel, textPos, textAttrs, KIFONT::METRICS::Default() );
281 textPos.x += glyphWidth * 18 + margin.x;
282 font->Draw( gal, _( "min" ), textPos, textAttrs, fontMetrics );
283 textPos.x += glyphWidth * 7 + margin.x;
284 font->Draw( gal, _( "max" ), textPos, textAttrs, fontMetrics );
285
286 // Prepare for value lines
287 textAttrs.m_Size = textDims.GlyphSize;
288 textAttrs.m_StrokeWidth = textDims.StrokeWidth;
289
290 // First value line (Net: ...)
291 textPos = GetPosition() + offset;
292 if( scopeLines )
293 textPos.y += KiROUND( headerDims.LinePitch * 1.1 );
294 // move below header line
295 textPos.y += KiROUND( headerDims.LinePitch * 1.3 );
296 gal->SetStrokeColor( normal );
297 font->Draw( gal, m_netValue, textPos, textAttrs, KIFONT::METRICS::Default() );
298
299 // Draw min / max columns for net line
300 textPos.x += glyphWidth * 18 + margin.x;
301 font->Draw( gal, m_minText, textPos, textAttrs, fontMetrics );
302 textPos.x += glyphWidth * 7 + margin.x;
303 font->Draw( gal, m_maxText, textPos, textAttrs, fontMetrics );
304
305 // Optional Chain value line with its own min/max
306 if( m_hasSignalValue )
307 {
308 textPos = GetPosition() + offset;
309 if( scopeLines )
310 textPos.y += KiROUND( headerDims.LinePitch * 1.1 );
311 textPos.y += KiROUND( headerDims.LinePitch * 2.3 );
312 gal->SetStrokeColor( normal );
313 font->Draw( gal, m_chainValue, textPos, textAttrs, KIFONT::METRICS::Default() );
314 textPos.x += glyphWidth * 18 + margin.x;
315 font->Draw( gal, m_chainMinText, textPos, textAttrs, fontMetrics );
316 textPos.x += glyphWidth * 7 + margin.x;
317 font->Draw( gal, m_chainMaxText, textPos, textAttrs, fontMetrics );
318 }
319
320 gal->Restore();
321}
322
323
324static LENGTH_TUNING_MODE tuningFromString( const std::string& aStr )
325{
326 if( aStr == "single" )
328 else if( aStr == "diff_pair" )
330 else if( aStr == "diff_pair_skew" )
332 else
333 {
334 wxFAIL_MSG( wxS( "Unknown length tuning token" ) );
336 }
337}
338
339
340static std::string tuningToString( const LENGTH_TUNING_MODE aTuning )
341{
342 switch( aTuning )
343 {
344 case LENGTH_TUNING_MODE::SINGLE: return "single";
345 case LENGTH_TUNING_MODE::DIFF_PAIR: return "diff_pair";
346 case LENGTH_TUNING_MODE::DIFF_PAIR_SKEW: return "diff_pair_skew";
347 default: wxFAIL; return "";
348 }
349}
350
351
362
363
364static PNS::MEANDER_SIDE sideFromString( const std::string& aStr )
365{
366 if( aStr == "default" )
368 else if( aStr == "left" )
370 else if( aStr == "right" )
372 else
373 {
374 wxFAIL_MSG( wxS( "Unknown length-tuning side token" ) );
376 }
377}
378
379
381{
382 switch( aStatus )
383 {
384 case PNS::MEANDER_PLACER_BASE::TOO_LONG: return "too_long";
385 case PNS::MEANDER_PLACER_BASE::TOO_SHORT: return "too_short";
386 case PNS::MEANDER_PLACER_BASE::TUNED: return "tuned";
387 default: wxFAIL; return "";
388 }
389}
390
391
393{
394 if( aStr == "too_long" )
396 else if( aStr == "too_short" )
398 else if( aStr == "tuned" )
400 else
401 {
402 wxFAIL_MSG( wxS( "Unknown tuning status token" ) );
404 }
405}
406
407
408static std::string sideToString( const PNS::MEANDER_SIDE aValue )
409{
410 switch( aValue )
411 {
412 case PNS::MEANDER_SIDE_DEFAULT: return "default";
413 case PNS::MEANDER_SIDE_LEFT: return "left";
414 case PNS::MEANDER_SIDE_RIGHT: return "right";
415 default: wxFAIL; return "";
416 }
417}
418
419// Cache invalidation heuristic for bridging distances.
420// Recompute if chain name changed, board pointer changed, or total pad count on the board changed.
421// Pad count change is a low-cost proxy for edits that might affect bridging. The bridging
422// computation itself is delegated to the shared helper in net_chain_bridging.h.
423long long PCB_TUNING_PATTERN::GetCachedBridgingLength( BOARD* aBoard, const wxString& aNetChain,
424 double* aDelayIUOut )
425{
426 if( !aBoard )
427 return 0;
428
429 size_t padCount = 0;
430 for( FOOTPRINT* fp : aBoard->Footprints() )
431 padCount += fp->Pads().size();
432
433 bool invalid = ( aNetChain != m_cachedBridgingSignal )
434 || ( aBoard != m_cachedBridgingBoardPtr )
435 || ( padCount != m_cachedBridgingPadCount );
436
437 if( invalid )
438 {
439 // Mirror the matched-length DRC predicate; see net_chain_bridging.h.
440 auto [lenIU, delayIU] = BoardChainBridging( aBoard, aNetChain );
441
442 m_cachedBridgingLen = static_cast<long long>( lenIU );
443 m_cachedBridgingDelayIU = delayIU;
444 m_cachedBridgingSignal = aNetChain;
446 m_cachedBridgingPadCount = padCount;
447 }
448
449 if( aDelayIUOut )
450 *aDelayIUOut = m_cachedBridgingDelayIU;
451
452 return m_cachedBridgingLen;
453}
454
455
457 LENGTH_TUNING_MODE aMode ) :
458 PCB_GENERATOR( aParent, aLayer ),
459 m_trackWidth( 0 ),
460 m_diffPairGap( 0 ),
461 m_tuningMode( aMode ),
462 m_tuningLength( 0 ),
463 m_tuningStatus( PNS::MEANDER_PLACER_BASE::TUNING_STATUS::TUNED ),
464 m_updateSideFromEnd( false ),
468 m_cachedBridgingBoardPtr( nullptr ),
470{
473 m_end = VECTOR2I( pcbIUScale.mmToIU( 10 ), 0 );
474 m_settings.m_initialSide = PNS::MEANDER_SIDE_LEFT;
475}
476
477
479{
480 if( m_tuningMode == DIFF_PAIR )
481 {
482 return( m_baseLine && m_baseLine->PointCount() > 1
483 && m_baseLineCoupled && m_baseLineCoupled->PointCount() > 1 );
484 }
485 else
486 {
487 return( m_baseLine && m_baseLine->PointCount() > 1 );
488 }
489}
490
491
493 PCB_BASE_EDIT_FRAME* aFrame,
494 BOARD_CONNECTED_ITEM* aStartItem,
495 LENGTH_TUNING_MODE aMode )
496{
497 BOARD* board = aStartItem->GetBoard();
499 DRC_CONSTRAINT constraint;
500 PCB_LAYER_ID layer = aStartItem->GetLayer();
501
502 PCB_TUNING_PATTERN* pattern = new PCB_TUNING_PATTERN( board, layer, aMode );
503
504 pattern->m_settings = DefaultSettings( bds, aMode );
505
506 // Stored manual targets reach the pattern through ApplyToolSettings so a rule can beat them
507 const PNS::MEANDER_SETTINGS unconstrained;
508
509 pattern->m_settings.m_targetLength = unconstrained.m_targetLength;
510 pattern->m_settings.m_targetLengthDelay = unconstrained.m_targetLengthDelay;
511 pattern->m_settings.m_targetSkew = unconstrained.m_targetSkew;
512 pattern->m_settings.m_targetSkewDelay = unconstrained.m_targetSkewDelay;
513
514 if( aMode == SINGLE || aMode == DIFF_PAIR )
515 {
516 DRC_CONSTRAINT chainConstraint =
517 bds.m_DRCEngine->EvalRules( NET_CHAIN_LENGTH_CONSTRAINT, aStartItem, nullptr, layer );
518
519 DRC_CONSTRAINT netConstraint = bds.m_DRCEngine->EvalRules( LENGTH_CONSTRAINT, aStartItem, nullptr, layer );
520
521 // Compute effective per-net target from both constraints
522 MINOPTMAX<int> effectiveTarget;
523 bool hasConstraint = false;
524 bool isTimeDomain = false;
525
526 // Start with per-net constraint as baseline
527 if( !netConstraint.IsNull() && netConstraint.GetSeverity() != RPT_SEVERITY_IGNORE )
528 {
529 effectiveTarget = netConstraint.GetValue();
530 isTimeDomain = netConstraint.GetOption( DRC_CONSTRAINT::OPTIONS::TIME_DOMAIN );
531 hasConstraint = true;
532 }
533
534 // Store chain-level target; Move() derives the per-net budget from it.
535 if( !chainConstraint.IsNull() && chainConstraint.GetSeverity() != RPT_SEVERITY_IGNORE )
536 {
537 NETINFO_ITEM* netInfo = static_cast<BOARD_CONNECTED_ITEM*>( aStartItem )->GetNet();
538 wxString chainName = netInfo ? netInfo->GetNetChain() : wxString();
539
540 if( !chainName.IsEmpty() )
541 {
542 isTimeDomain = chainConstraint.GetOption( DRC_CONSTRAINT::OPTIONS::TIME_DOMAIN );
543
544 // Subtract bridging so the copper-only budget aligns with the DRC check.
545 MINOPTMAX<int> adjustedTarget = chainConstraint.GetValue();
546 double bridgingDelayIU = 0.0;
547 long long bridging = pattern->GetCachedBridgingLength( board, chainName,
548 &bridgingDelayIU );
549
550 if( bridging > 0 )
551 {
552 long long bridgingIU = isTimeDomain ? static_cast<long long>( bridgingDelayIU )
553 : bridging;
554
555 if( adjustedTarget.HasMin() )
556 adjustedTarget.SetMin( SubtractBridgingClamped( adjustedTarget.Min(),
557 bridgingIU ) );
558
559 if( adjustedTarget.HasOpt() )
560 adjustedTarget.SetOpt( SubtractBridgingClamped( adjustedTarget.Opt(),
561 bridgingIU ) );
562
563 if( adjustedTarget.HasMax() )
564 adjustedTarget.SetMax( SubtractBridgingClamped( adjustedTarget.Max(),
565 bridgingIU ) );
566 }
567
568 pattern->m_hasRuleTarget = true;
569
570 if( isTimeDomain )
571 {
572 pattern->m_settings.SetTargetSignalLengthDelay( adjustedTarget );
573 }
574 else
575 {
576 pattern->m_settings.SetTargetSignalLength( adjustedTarget );
577 }
578 }
579 }
580
581 if( hasConstraint )
582 {
583 pattern->m_hasRuleTarget = true;
584 constraint = netConstraint.IsNull() ? chainConstraint : netConstraint;
585
586 if( isTimeDomain )
587 {
588 pattern->m_settings.SetTargetLengthDelay( effectiveTarget );
590 pattern->m_settings.m_isTimeDomain = true;
591 }
592 else
593 {
594 pattern->m_settings.SetTargetLength( effectiveTarget );
596 pattern->m_settings.m_isTimeDomain = false;
597 }
598 }
599 else if( isTimeDomain )
600 {
601 // Chain-only (no per-net rule): set time-domain flag, let Move() derive targets.
602 pattern->m_settings.m_isTimeDomain = true;
603 }
604 else if( aStartItem->GetEffectiveNetClass()->HasTuningProfile() )
605 {
606 // Check if the tuning profile has time domain tuning enabled
607 const std::shared_ptr<TUNING_PROFILES> tuningParams =
609 TUNING_PROFILE& profile =
610 tuningParams->GetTuningProfile( aStartItem->GetEffectiveNetClass()->GetTuningProfile() );
611
612 if( profile.m_EnableTimeDomainTuning )
613 pattern->m_settings.m_isTimeDomain = true;
614 }
615 }
616 else
617 {
618 constraint = bds.m_DRCEngine->EvalRules( SKEW_CONSTRAINT, aStartItem, nullptr, layer );
619
620 if( !constraint.IsNull() )
621 {
622 pattern->m_hasRuleTarget = true;
623
625 {
627 pattern->m_settings.SetTargetSkewDelay( constraint.GetValue() );
628 pattern->m_settings.m_isTimeDomain = true;
629 }
630 else
631 {
632 pattern->m_settings.SetTargetSkew( constraint.GetValue() );
634 pattern->m_settings.m_isTimeDomain = false;
635 }
636 }
637 }
638
639 pattern->SetFlags( IS_NEW );
640 pattern->m_settings.m_netClass = aStartItem->GetEffectiveNetClass();
641
642 return pattern;
643}
644
645
647{
648 switch( aMode )
649 {
650 case DIFF_PAIR: return aBds.m_DiffPairMeanderSettings;
651 case DIFF_PAIR_SKEW: return aBds.m_SkewMeanderSettings;
652 case SINGLE:
653 default: return aBds.m_SingleTrackMeanderSettings;
654 }
655}
656
657
659 const PNS::MEANDER_SETTINGS& aSettings, bool aIncludeTargets )
660{
661 PNS::MEANDER_SETTINGS& dest = DefaultSettings( aBds, aMode );
662
663 dest.m_minAmplitude = aSettings.m_minAmplitude;
664 dest.m_maxAmplitude = aSettings.m_maxAmplitude;
665 dest.m_spacing = aSettings.m_spacing;
666 dest.m_cornerStyle = aSettings.m_cornerStyle;
668 dest.m_singleSided = aSettings.m_singleSided;
669
670 if( aIncludeTargets )
671 {
672 dest.m_targetLength = aSettings.m_targetLength;
674 dest.m_targetSkew = aSettings.m_targetSkew;
675 dest.m_targetSkewDelay = aSettings.m_targetSkewDelay;
676 }
677}
678
679
681{
682 const auto origTargetLength = m_settings.m_targetLength;
683 const auto origTargetLengthDelay = m_settings.m_targetLengthDelay;
684 const auto origTargetSignalLength = m_settings.m_targetSignalLength;
685 const auto origTargetSignalLengthDelay = m_settings.m_targetSignalLengthDelay;
686 const auto origTargetSkew = m_settings.m_targetSkew;
687 const auto origTargetSkewDelay = m_settings.m_targetSkewDelay;
688 const bool origIsTimeDomain = m_settings.m_isTimeDomain;
689
690 m_settings = aToolSettings;
691
692 if( !aToolSettings.m_overrideCustomRules )
693 {
694 m_settings.m_targetSignalLength = origTargetSignalLength;
695 m_settings.m_targetSignalLengthDelay = origTargetSignalLengthDelay;
696 }
697
698 if( m_hasRuleTarget && !aToolSettings.m_overrideCustomRules )
699 {
700 m_settings.m_targetLength = origTargetLength;
701 m_settings.m_targetLengthDelay = origTargetLengthDelay;
702 m_settings.m_targetSkew = origTargetSkew;
703 m_settings.m_targetSkewDelay = origTargetSkewDelay;
704 }
705
706 if( !aToolSettings.m_overrideCustomRules )
707 m_settings.m_isTimeDomain = origIsTimeDomain;
708}
709
710
712{
713 aTool->SetRouterCommit( aCommit );
714
715 if( aCommit )
716 {
717 if( IsNew() )
718 aCommit->Add( this );
719 else
720 aCommit->Modify( this );
721 }
722
723 SetFlags( IN_EDIT );
724
725 PNS::ROUTER* router = aTool->Router();
726 int layer = router->GetInterface()->GetPNSLayerFromBoardLayer( GetLayer() );
727
728 aTool->ClearRouterChanges();
729 router->SyncWorld();
730
731 PNS::RULE_RESOLVER* resolver = router->GetRuleResolver();
732 PNS::CONSTRAINT constraint;
733
734 if( !baselineValid() )
735 initBaseLines( router, layer, aBoard );
736
738 {
739 VECTOR2I centerlineOffsetEnd;
740
742 && m_baseLineCoupled->SegmentCount() > 0 )
743 {
744 centerlineOffsetEnd =
745 ( m_baseLineCoupled->CLastPoint() - m_baseLine->CLastPoint() ) / 2;
746 }
747
748 SEG baseEnd = m_baseLine && m_baseLine->SegmentCount() > 0 ? m_baseLine->CSegment( -1 )
749 : SEG( m_origin, m_end );
750
751 baseEnd.A += centerlineOffsetEnd;
752 baseEnd.B += centerlineOffsetEnd;
753
754 if( baseEnd.A != baseEnd.B )
755 {
756 int side = baseEnd.Side( m_end );
757
758 if( side < 0 )
759 m_settings.m_initialSide = PNS::MEANDER_SIDE_LEFT;
760 else
761 m_settings.m_initialSide = PNS::MEANDER_SIDE_RIGHT;
762 }
763
764 m_updateSideFromEnd = false;
765 }
766
767 PCB_TRACK* track = nullptr;
768 m_origin = PNS::HELPERS::SnapToNearestTrack( m_origin, aBoard, nullptr, &track );
769 wxCHECK( track, /* void */ );
770
771 m_settings.m_netClass = track->GetEffectiveNetClass();
772
773 auto getTimeDomainTuningEnabled = [this, aBoard]()
774 {
775 const std::shared_ptr<TUNING_PROFILES> tuningParams =
777 const TUNING_PROFILE& profile = tuningParams->GetTuningProfile( m_settings.m_netClass->GetTuningProfile() );
778
779 if( profile.m_EnableTimeDomainTuning )
780 return true;
781
782 return false;
783 };
784
785 if( !m_settings.m_overrideCustomRules )
786 {
787 PNS::SEGMENT pnsItem;
788 NETINFO_ITEM* net = track->GetNet();
789
790 pnsItem.SetParent( track );
791 pnsItem.SetNet( net );
792
793 if( m_tuningMode == SINGLE )
794 {
796 &pnsItem, nullptr, layer, &constraint ) )
797 {
798 if( constraint.m_IsTimeDomain )
799 {
800 m_settings.SetTargetLengthDelay( constraint.m_Value );
801 m_settings.SetTargetLength( MINOPTMAX<int>() );
802 }
803 else
804 {
805 m_settings.SetTargetLengthDelay( MINOPTMAX<int>() );
806 m_settings.SetTargetLength( constraint.m_Value );
807 }
808
809 m_settings.m_isTimeDomain = constraint.m_IsTimeDomain;
811 }
812 else if( track->GetEffectiveNetClass()->HasTuningProfile() )
813 {
814 m_settings.m_isTimeDomain = getTimeDomainTuningEnabled();
816 }
817 }
818 else
819 {
820 PCB_TRACK* coupledTrack = nullptr;
821 PNS::SEGMENT pnsCoupledItem;
822 NETINFO_ITEM* coupledNet = aBoard->DpCoupledNet( net );
823
824 if( coupledNet )
825 PNS::HELPERS::SnapToNearestTrack( m_origin, aBoard, coupledNet, &coupledTrack );
826
827 pnsCoupledItem.SetParent( coupledTrack );
828 pnsCoupledItem.SetNet( coupledNet );
829
830 if( m_tuningMode == DIFF_PAIR )
831 {
833 &pnsItem, &pnsCoupledItem, layer, &constraint ) )
834 {
835 if( constraint.m_IsTimeDomain )
836 {
837 m_settings.SetTargetLengthDelay( constraint.m_Value );
838 m_settings.SetTargetLength( MINOPTMAX<int>() );
839 }
840 else
841 {
842 m_settings.SetTargetLengthDelay( MINOPTMAX<int>() );
843 m_settings.SetTargetLength( constraint.m_Value );
844 }
845
846 m_settings.m_isTimeDomain = constraint.m_IsTimeDomain;
848 }
849 else if( track->GetEffectiveNetClass()->HasTuningProfile() )
850 {
851 m_settings.m_isTimeDomain = getTimeDomainTuningEnabled();
853 }
854 }
855 else
856 {
858 &pnsItem, &pnsCoupledItem, layer, &constraint ) )
859 {
860 if( constraint.m_IsTimeDomain )
861 {
862 m_settings.SetTargetSkewDelay( constraint.m_Value );
863 m_settings.SetTargetSkew( MINOPTMAX<int>() );
864 }
865 else
866 {
867 m_settings.SetTargetSkewDelay( MINOPTMAX<int>() );
868 m_settings.SetTargetSkew( constraint.m_Value );
869 }
870
871 m_settings.m_isTimeDomain = constraint.m_IsTimeDomain;
873 }
874 }
875 }
876 }
877}
878
879
880static std::optional<PNS::LINE> getPNSLine( const VECTOR2I& aStart, const VECTOR2I& aEnd,
881 PNS::ROUTER* router, int layer, VECTOR2I& aStartOut,
882 VECTOR2I& aEndOut )
883{
884 PNS::NODE* world = router->GetWorld();
885
886 PNS::LINKED_ITEM* startItem = PNS::HELPERS::PickSegment( router, aStart, layer, aStartOut );
887 PNS::LINKED_ITEM* endItem = PNS::HELPERS::PickSegment( router, aEnd, layer, aEndOut );
888
889 for( PNS::LINKED_ITEM* testItem : { startItem, endItem } )
890 {
891 if( !testItem )
892 continue;
893
894 PNS::LINE line = world->AssembleLine( testItem, nullptr, false, false );
895 SHAPE_LINE_CHAIN oldChain = line.CLine();
896
897 if( oldChain.PointOnEdge( aStartOut, 1 ) && oldChain.PointOnEdge( aEndOut, 1 ) )
898 return line;
899 }
900
901 return std::nullopt;
902}
903
904
905bool PCB_TUNING_PATTERN::initBaseLine( PNS::ROUTER* aRouter, int aPNSLayer, BOARD* aBoard,
906 VECTOR2I& aStart, VECTOR2I& aEnd, NETINFO_ITEM* aNet,
907 std::optional<SHAPE_LINE_CHAIN>& aBaseLine )
908{
909 PNS::NODE* world = aRouter->GetWorld();
910
911 aStart = PNS::HELPERS::SnapToNearestTrack( aStart, aBoard, aNet, nullptr );
912 aEnd = PNS::HELPERS::SnapToNearestTrack( aEnd, aBoard, aNet, nullptr );
913
914 VECTOR2I startSnapPoint, endSnapPoint;
915
916 PNS::LINKED_ITEM* startItem = PNS::HELPERS::PickSegment( aRouter, aStart, aPNSLayer, startSnapPoint );
917 PNS::LINKED_ITEM* endItem = PNS::HELPERS::PickSegment( aRouter, aEnd, aPNSLayer, endSnapPoint );
918
919 wxASSERT( startItem );
920 wxASSERT( endItem );
921
922 if( !startItem || !endItem )
923 return false;
924
925 PNS::LINE line = world->AssembleLine( startItem );
926 const SHAPE_LINE_CHAIN& chain = line.CLine();
927
928 wxASSERT( line.ContainsLink( endItem ) );
929
930 wxASSERT( chain.PointOnEdge( startSnapPoint, 40000 ) );
931 wxASSERT( chain.PointOnEdge( endSnapPoint, 40000 ) );
932
935 SHAPE_LINE_CHAIN post;
936
937 chain.Split( startSnapPoint, endSnapPoint, pre, mid, post );
938
939 aBaseLine = mid;
940
941 return true;
942}
943
944
945bool PCB_TUNING_PATTERN::initBaseLines( PNS::ROUTER* aRouter, int aPNSLayer, BOARD* aBoard )
946{
947 m_baseLineCoupled.reset();
948
949 PCB_TRACK* track = nullptr;
950
951 m_origin = PNS::HELPERS::SnapToNearestTrack( m_origin, aBoard, nullptr, &track );
952 wxCHECK( track, false );
953
954 NETINFO_ITEM* net = track->GetNet();
955
956 if( !initBaseLine( aRouter, aPNSLayer, aBoard, m_origin, m_end, net, m_baseLine ) )
957 return false;
958
959 // Generate both baselines even if we're skewing. We need the coupled baseline to run the
960 // DRC rules against.
961 if( m_tuningMode == DIFF_PAIR )
962 {
963 if( NETINFO_ITEM* coupledNet = aBoard->DpCoupledNet( net ) )
964 {
965 VECTOR2I coupledStart = PNS::HELPERS::SnapToNearestTrack( m_origin, aBoard, coupledNet, nullptr );
966 VECTOR2I coupledEnd = PNS::HELPERS::SnapToNearestTrack( m_end, aBoard, coupledNet, nullptr );
967
968 return initBaseLine( aRouter, aPNSLayer, aBoard, coupledStart, coupledEnd, coupledNet,
970 }
971
972 return false;
973 }
974
975 return true;
976}
977
978
980{
981public:
983 m_pattern( aPattern ),
984 m_wasLocked( aPattern->IsLocked() )
985 {
986 m_pattern->SetLocked( false );
987 }
988
990 {
991 if( m_wasLocked )
992 m_pattern->SetLocked( true );
993 }
994
995private:
998};
999
1000
1001bool PCB_TUNING_PATTERN::removeToBaseline( PNS::ROUTER* aRouter, int aPNSLayer, SHAPE_LINE_CHAIN& aBaseLine )
1002{
1003 VECTOR2I startSnapPoint, endSnapPoint;
1004
1005 std::optional<PNS::LINE> pnsLine = getPNSLine( aBaseLine.CPoint( 0 ), aBaseLine.CLastPoint(), aRouter,
1006 aPNSLayer, startSnapPoint, endSnapPoint );
1007
1008 wxCHECK( pnsLine, false );
1009
1010 SHAPE_LINE_CHAIN pre;
1011 SHAPE_LINE_CHAIN mid;
1012 SHAPE_LINE_CHAIN post;
1013 pnsLine->CLine().Split( startSnapPoint, endSnapPoint, pre, mid, post );
1014
1015 for( PNS::LINKED_ITEM* li : pnsLine->Links() )
1016 aRouter->GetInterface()->RemoveItem( li );
1017
1018 aRouter->GetWorld()->Remove( *pnsLine );
1019
1020 SHAPE_LINE_CHAIN straightChain;
1021 straightChain.Append( pre );
1022 straightChain.Append( aBaseLine );
1023 straightChain.Append( post );
1024 straightChain.Simplify();
1025
1026 PNS::LINE straightLine( *pnsLine, straightChain );
1027
1028 aRouter->GetWorld()->Add( straightLine, false );
1029
1030 for( PNS::LINKED_ITEM* li : straightLine.Links() )
1031 aRouter->GetInterface()->AddItem( li );
1032
1033 return true;
1034}
1035
1036
1038{
1039 aTool->SetRouterCommit( aCommit );
1040
1041 UNLOCKER raiiUnlocker( this );
1042 SetFlags( IN_EDIT );
1043
1044 aTool->Router()->SyncWorld();
1045
1046 PNS::ROUTER* router = aTool->Router();
1047 PNS_KICAD_IFACE* iface = aTool->GetInterface();
1048
1049 aCommit->Remove( this );
1050
1051 aTool->ClearRouterChanges();
1052
1053 // PNS layers and PCB layers have different coding. so convert PCB layer to PNS layer
1054 int pnslayer = iface->GetPNSLayerFromBoardLayer( GetLayer() );
1055
1056 if( baselineValid() )
1057 {
1058 bool success = true;
1059
1060 success &= removeToBaseline( router, pnslayer, *m_baseLine );
1061
1062 if( m_tuningMode == DIFF_PAIR )
1063 success &= removeToBaseline( router, pnslayer, *m_baseLineCoupled );
1064
1065 if( !success )
1066 recoverBaseline( router, pnslayer );
1067 }
1068
1069 const std::vector<GENERATOR_PNS_CHANGES>& allPnsChanges = aTool->GetRouterChanges();
1070
1071 for( const GENERATOR_PNS_CHANGES& pnsChanges : allPnsChanges )
1072 {
1073 const std::set<BOARD_ITEM*> routerRemovedItems = pnsChanges.removedItems;
1074 const std::set<BOARD_ITEM*> routerAddedItems = pnsChanges.addedItems;
1075
1076 /*std::cout << "Push commits << " << allPnsChanges.size() << " routerRemovedItems "
1077 << routerRemovedItems.size() << " routerAddedItems " << routerAddedItems.size()
1078 << " m_removedItems " << m_removedItems.size() << std::endl;*/
1079
1080 for( BOARD_ITEM* item : routerRemovedItems )
1081 {
1082 item->ClearSelected();
1083 aCommit->Remove( item );
1084 }
1085
1086 for( BOARD_ITEM* item : routerAddedItems )
1087 aCommit->Add( item );
1088 }
1089
1090 aTool->SetRouterCommit( nullptr );
1091}
1092
1093
1095{
1096 PNS::SOLID queryItem;
1097
1098 SHAPE_LINE_CHAIN* chain = static_cast<SHAPE_LINE_CHAIN*>( getOutline().Clone() );
1099 queryItem.SetShape( chain ); // PNS::SOLID takes ownership
1100 queryItem.SetLayer( aPNSLayer );
1101
1102 int lineWidth = 0;
1103
1104 PNS::NODE::OBSTACLES obstacles;
1106 opts.m_useClearanceEpsilon = false;
1107
1108 PNS::NODE* world = aRouter->GetWorld();
1109 PNS::NODE* branch = world->Branch();
1110
1111 branch->QueryColliding( &queryItem, obstacles, opts );
1112
1113 for( const PNS::OBSTACLE& obs : obstacles )
1114 {
1115 PNS::ITEM* item = obs.m_item;
1116
1118 continue;
1119
1120 if( PNS::LINKED_ITEM* li = dynamic_cast<PNS::LINKED_ITEM*>( item ) )
1121 {
1122 if( lineWidth == 0 || li->Width() < lineWidth )
1123 lineWidth = li->Width();
1124 }
1125
1126 if( chain->PointInside( item->Anchor( 0 ), 10 )
1127 && chain->PointInside( item->Anchor( 1 ), 10 ) )
1128 {
1129 branch->Remove( item );
1130 }
1131 }
1132
1133 if( lineWidth == 0 )
1134 lineWidth = pcbIUScale.mmToIU( 0.1 ); // Fallback
1135
1136 if( baselineValid() )
1137 {
1138 NETINFO_ITEM* recoverNet = GetBoard()->FindNet( m_lastNetName );
1139 PNS::LINE recoverLine;
1140
1141 recoverLine.SetLayer( aPNSLayer );
1142 recoverLine.SetWidth( lineWidth );
1143 recoverLine.Line() = *m_baseLine;
1144 recoverLine.SetNet( recoverNet );
1145 branch->Add( recoverLine, false );
1146
1147 if( m_tuningMode == DIFF_PAIR )
1148 {
1149 NETINFO_ITEM* recoverCoupledNet = GetBoard()->DpCoupledNet( recoverNet );
1150 PNS::LINE recoverLineCoupled;
1151
1152 recoverLineCoupled.SetLayer( aPNSLayer );
1153 recoverLineCoupled.SetWidth( lineWidth );
1154 recoverLineCoupled.Line() = *m_baseLineCoupled;
1155 recoverLineCoupled.SetNet( recoverCoupledNet );
1156 branch->Add( recoverLineCoupled, false );
1157 }
1158 }
1159
1160 aRouter->CommitRouting( branch );
1161
1162 //wxLogWarning( "PNS baseline recovered" );
1163
1164 return true;
1165}
1166
1167
1169 bool aPrimary )
1170{
1171 PNS_KICAD_IFACE* iface = aTool->GetInterface();
1172 PNS::ROUTER* router = aTool->Router();
1173 PNS::NODE* world = router->GetWorld();
1174 VECTOR2I startSnapPoint, endSnapPoint;
1175
1176 std::optional<PNS::LINE> pnsLine = getPNSLine( aBaseLine.CPoint( 0 ), aBaseLine.CLastPoint(), router,
1177 aPNSLayer, startSnapPoint, endSnapPoint );
1178
1179 if( !pnsLine )
1180 {
1181 // TODO
1182 //recoverBaseline( aRouter );
1183 return true;
1184 }
1185
1186 PNS::NODE* branch = world->Branch();
1187
1188 SHAPE_LINE_CHAIN straightChain;
1189 {
1190 SHAPE_LINE_CHAIN pre, mid, post;
1191 pnsLine->CLine().Split( startSnapPoint, endSnapPoint, pre, mid, post );
1192
1193 straightChain.Append( pre );
1194 straightChain.Append( aBaseLine );
1195 straightChain.Append( post );
1196 straightChain.Simplify();
1197 }
1198
1199 branch->Remove( *pnsLine );
1200
1201 SHAPE_LINE_CHAIN newLineChain;
1202
1203 if( aPrimary )
1204 {
1205 m_origin = straightChain.NearestPoint( m_origin );
1206 m_end = straightChain.NearestPoint( m_end );
1207
1208 // Don't allow points too close
1209 if( ( m_end - m_origin ).EuclideanNorm() < pcbIUScale.mmToIU( 0.1 ) )
1210 {
1211 m_origin = startSnapPoint;
1212 m_end = endSnapPoint;
1213 }
1214
1215 {
1216 SHAPE_LINE_CHAIN pre, mid, post;
1217 straightChain.Split( m_origin, m_end, pre, mid, post );
1218
1219 newLineChain.Append( pre );
1220 newLineChain.Append( mid );
1221 newLineChain.Append( post );
1222
1223 m_baseLine = mid;
1224 }
1225 }
1226 else
1227 {
1228 VECTOR2I start = straightChain.NearestPoint( m_origin );
1229 VECTOR2I end = straightChain.NearestPoint( m_end );
1230
1231 {
1232 SHAPE_LINE_CHAIN pre, mid, post;
1233 straightChain.Split( start, end, pre, mid, post );
1234
1235 newLineChain.Append( pre );
1236 newLineChain.Append( mid );
1237 newLineChain.Append( post );
1238
1239 m_baseLineCoupled = mid;
1240 }
1241 }
1242
1243 // Because PNS_LINE doesn't yet support multiple widths, we need to keep track
1244 // of these separately for the scenario where someone drew a tuning pattern on a track
1245 // and then changed the width of either the tuning pattern or the rest of the line so
1246 // that they no longer match.
1247 {
1248 SHAPE_LINE_CHAIN pre, mid, post;
1249 newLineChain.Split( m_origin, m_end, pre, mid, post );
1250
1251 auto addPart = [&]( const SHAPE_LINE_CHAIN& part, int partWidth )
1252 {
1253 if( part.SegmentCount() == 0 )
1254 return;
1255
1256 PNS::LINE line( *pnsLine, part );
1257
1258 if( partWidth > 0 )
1259 line.SetWidth( partWidth );
1260
1261 branch->Add( line, false );
1262 };
1263
1264 m_assembledLineWidth = pnsLine->Width();
1265
1266 addPart( pre, m_assembledLineWidth );
1267 // m_trackWidth is the width of the generator itself, which starts out assuming the width
1268 // of whatever line we're tuning, but after that may be independently changed
1269 addPart( mid, m_trackWidth > 0 ? m_trackWidth : m_assembledLineWidth );
1270 addPart( post, m_assembledLineWidth );
1271 }
1272
1273 router->CommitRouting( branch );
1274
1275 int clearance = router->GetRuleResolver()->Clearance( &pnsLine.value(), nullptr );
1276
1277 iface->DisplayItem( &pnsLine.value(), clearance, true, PNS_COLLISION );
1278
1279 return true;
1280}
1281
1282
1284{
1285 if( !( GetFlags() & IN_EDIT ) )
1286 return false;
1287
1288 if( m_settings.m_isTimeDomain && !IsNew() )
1289 {
1291 ctx.NetClass = m_settings.m_netClass;
1292 ctx.Layer = GetLayer();
1293 ctx.Width = m_trackWidth;
1296
1297 // Without a delay model the placer targets the baseline length and strips the meanders
1298 if( !aBoard->GetLengthCalculation()->CanCalculateLengthForDelay( ctx ) )
1299 return false;
1300 }
1301
1302 UNLOCKER raiiUnlocker( this ); // Unlock the pattern for editing
1303
1304 KIGFX::VIEW* view = aTool->GetManager()->GetView();
1305 PNS::ROUTER* router = aTool->Router();
1306 PNS_KICAD_IFACE* iface = aTool->GetInterface();
1307 PCB_LAYER_ID pcblayer = GetLayer();
1308 SHAPE_LINE_CHAIN bounds = getOutline();
1309 int epsilon = aBoard->GetDesignSettings().GetDRCEpsilon();
1310
1311 auto withinBounds = [bounds, epsilon]( BOARD_ITEM* aItem )
1312 {
1313 if( PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( aItem ) )
1314 {
1315 if( bounds.PointInside( track->GetStart(), epsilon ) && bounds.PointInside( track->GetEnd(), epsilon ) )
1316 {
1317 return true;
1318 }
1319 }
1320
1321 return false;
1322 };
1323
1324 auto hideRemovedItems = [&]( bool aHide )
1325 {
1326 if( view )
1327 {
1328 for( const GENERATOR_PNS_CHANGES& pnsCommit : aTool->GetRouterChanges() )
1329 {
1330 for( BOARD_ITEM* item : pnsCommit.removedItems )
1331 {
1332 if( view && withinBounds( item ) )
1333 view->Hide( item, aHide, aHide );
1334 }
1335 }
1336 }
1337 };
1338
1339 iface->SetStartLayerFromPCBNew( pcblayer );
1340
1341 if( router->RoutingInProgress() )
1342 {
1343 router->StopRouting();
1344 }
1345
1346 // PNS layers and PCB layers have different coding. so convert PCB layer to PNS layer
1347 int pnslayer = iface->GetPNSLayerFromBoardLayer( pcblayer );
1348
1349 if( !baselineValid() )
1350 {
1351 initBaseLines( router, pnslayer, aBoard );
1352 }
1353 else
1354 {
1355 if( resetToBaseline( aTool, pnslayer, *m_baseLine, true ) )
1356 {
1357 m_origin = m_baseLine->CPoint( 0 );
1358 m_end = m_baseLine->CLastPoint();
1359 }
1360 else
1361 {
1362 //initBaseLines( router, layer, aBoard );
1363 return false;
1364 }
1365
1366 if( m_tuningMode == DIFF_PAIR )
1367 {
1368 if( !resetToBaseline( aTool, pnslayer, *m_baseLineCoupled, false ) )
1369 {
1370 initBaseLines( router, pnslayer, aBoard );
1371 return false;
1372 }
1373 }
1374 }
1375
1376 hideRemovedItems( true );
1377 // Snap points
1378 VECTOR2I startSnapPoint, endSnapPoint;
1379
1380 wxCHECK( m_baseLine, false );
1381
1382 PNS::LINKED_ITEM* startItem = PNS::HELPERS::PickSegment( router, m_origin, pnslayer, startSnapPoint, *m_baseLine );
1383 PNS::LINKED_ITEM* endItem = PNS::HELPERS::PickSegment( router, m_end, pnslayer, endSnapPoint, *m_baseLine );
1384
1385 wxASSERT( startItem );
1386 wxASSERT( endItem );
1387
1388 if( !startItem || !endItem )
1389 return false;
1390
1391 router->SetMode( GetPNSMode() );
1392
1393 if( !router->StartRouting( startSnapPoint, startItem, pnslayer ) )
1394 {
1395 //recoverBaseline( router );
1396 return false;
1397 }
1398
1399 PNS::MEANDER_PLACER_BASE* placer = static_cast<PNS::MEANDER_PLACER_BASE*>( router->Placer() );
1400
1401 m_settings.m_keepEndpoints = true; // Required for re-grouping
1402 placer->UpdateSettings( m_settings );
1403
1404 router->Move( m_end, nullptr );
1405
1406 // Only take the width from the PNS's assembled line if we don't already have an inherent
1407 // width property set, otherwise width can get reset if the tuning pattern is on a line
1408 // that has a different width
1409 if( m_trackWidth == 0 )
1410 {
1411 if( PNS::DP_MEANDER_PLACER* dpPlacer = dynamic_cast<PNS::DP_MEANDER_PLACER*>( placer ) )
1412 {
1413 m_trackWidth = dpPlacer->GetOriginPair().Dimensions().Width();
1414 m_diffPairGap = dpPlacer->GetOriginPair().Dimensions().Gap();
1415 }
1416 else
1417 {
1418 m_trackWidth = startItem->Width();
1419 m_diffPairGap = router->Sizes().DiffPairGap();
1420 }
1421 }
1422
1423 m_settings = placer->MeanderSettings();
1424 m_lastNetName = iface->GetNetName( startItem->Net() );
1425 m_tuningStatus = placer->TuningStatus();
1427
1428 // Take the display units from the board, which tracks the frame's user units; time-domain
1429 // tuning always displays in picoseconds.
1430 EDA_UNITS units = m_settings.m_isTimeDomain ? EDA_UNITS::PS
1431 : ( aBoard ? aBoard->GetUserUnits() : EDA_UNITS::MM );
1432 m_tuningInfo.Printf( wxS( "%s (%s)" ),
1434 (double) m_tuningLength ),
1436
1437 return true;
1438}
1439
1440
1442{
1443 if( !( GetFlags() & IN_EDIT ) )
1444 {
1445 aTool->SetRouterCommit( nullptr );
1446 return;
1447 }
1448 ClearFlags( IN_EDIT ); // Clear the editing flag
1449
1450 KIGFX::VIEW* view = aTool->GetManager()->GetView();
1451 PNS::ROUTER* router = aTool->Router();
1452 PNS_KICAD_IFACE* iface = aTool->GetInterface();
1453 SHAPE_LINE_CHAIN bounds = getOutline();
1454 int epsilon = aBoard->GetDesignSettings().GetDRCEpsilon();
1455
1456 iface->EraseView();
1457
1458 if( router->RoutingInProgress() )
1459 {
1460 bool forceFinish = true;
1461 bool forceCommit = false;
1462
1463 router->FixRoute( m_end, nullptr, forceFinish, forceCommit );
1464 router->StopRouting();
1465 }
1466
1467 PNS::NODE* world = router->GetWorld();
1468
1469 auto withinBounds = [bounds, epsilon]( BOARD_ITEM* aItem )
1470 {
1471 if( PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( aItem ) )
1472 {
1473 if( bounds.PointInside( track->GetStart(), epsilon ) && bounds.PointInside( track->GetEnd(), epsilon ) )
1474 {
1475 return true;
1476 }
1477 }
1478
1479 return false;
1480 };
1481
1482 const std::vector<GENERATOR_PNS_CHANGES>& pnsCommits = aTool->GetRouterChanges();
1483
1484 for( const GENERATOR_PNS_CHANGES& pnsCommit : pnsCommits )
1485 {
1486 const std::set<BOARD_ITEM*> routerRemovedItems = pnsCommit.removedItems;
1487 const std::set<BOARD_ITEM*> routerAddedItems = pnsCommit.addedItems;
1488
1489 //std::cout << "Push commits << " << allPnsChanges.size() << " routerRemovedItems "
1490 // << routerRemovedItems.size() << " routerAddedItems " << routerAddedItems.size()
1491 // << " m_removedItems " << m_removedItems.size() << std::endl;
1492
1493 for( BOARD_ITEM* item : routerRemovedItems )
1494 {
1495 // Only remove items that were originally on the board before the generator ran
1496 if( aTool->ItemCreatedBySession( item ) )
1497 continue;
1498
1499 if( view )
1500 view->Hide( item, false );
1501
1502 aCommit->Remove( item );
1503 }
1504
1505 for( BOARD_ITEM* item : routerAddedItems )
1506 {
1507 // This avoids adding transient items such as the baseline
1508 if( world->FindItemByParent( item ) == nullptr )
1509 continue;
1510
1511 // The router added items will include a reconstruction of the original line
1512 // without the tuning pattern. Because we don't currently have the infrastructure
1513 // to manage a PNS_LINE with different widths along the way, changing the width
1514 // of an existing tuning pattern (e.g. with the properties panel) will reset the
1515 // widths of the entire line, even when the segments outside the tuning pattern were
1516 // not selected. In order to prevent widths from changing outside the selection,
1517 // we need to special-case this here and restore the original width.
1518 if( !withinBounds( item ) )
1519 {
1520 if( item->IsType( { PCB_TRACE_T, PCB_ARC_T } ) )
1521 {
1522 PCB_TRACK* newTrack = static_cast<PCB_TRACK*>( item );
1523
1524 if( m_assembledLineWidth != newTrack->GetWidth() )
1525 newTrack->SetWidth( m_assembledLineWidth );
1526 }
1527 }
1528
1529 aCommit->Add( item );
1530
1531 if( withinBounds( item ) )
1532 AddItem( item );
1533 }
1534 }
1535
1536 aTool->SetRouterCommit( nullptr );
1537}
1538
1539
1541{
1542 if( !( GetFlags() & IN_EDIT ) )
1543 {
1544 aTool->SetRouterCommit( nullptr );
1545 return;
1546 }
1547
1549
1550 PNS_KICAD_IFACE* iface = aTool->GetInterface();
1551
1552 iface->EraseView();
1553
1554 if( KIGFX::VIEW* view = aTool->GetManager()->GetView() )
1555 {
1556 for( const GENERATOR_PNS_CHANGES& pnsCommit : aTool->GetRouterChanges() )
1557 {
1558 for( BOARD_ITEM* item : pnsCommit.removedItems )
1559 view->Hide( item, false );
1560 }
1561 }
1562
1563 aTool->Router()->StopRouting();
1564 aTool->SetRouterCommit( nullptr );
1565}
1566
1567
1569{
1570 VECTOR2I centerlineOffset;
1571 VECTOR2I centerlineOffsetEnd;
1572
1573 if( m_tuningMode == DIFF_PAIR && m_baseLineCoupled && m_baseLineCoupled->SegmentCount() > 0 )
1574 {
1575 centerlineOffset = ( m_baseLineCoupled->CPoint( 0 ) - m_origin ) / 2;
1576 centerlineOffsetEnd = ( m_baseLineCoupled->CLastPoint() - m_end ) / 2;
1577 }
1578
1579 aPoints.AddPoint( m_origin + centerlineOffset );
1580 aPoints.AddPoint( m_end + centerlineOffsetEnd );
1581
1582 SEG base = m_baseLine && m_baseLine->SegmentCount() > 0 ? m_baseLine->CSegment( 0 )
1583 : SEG( m_origin, m_end );
1584
1585 base.A += centerlineOffset;
1586 base.B += centerlineOffset;
1587
1588 int amplitude = m_settings.m_maxAmplitude + KiROUND( m_trackWidth / 2.0 );
1589
1590 if( m_tuningMode == DIFF_PAIR )
1591 amplitude += m_trackWidth + m_diffPairGap;
1592
1593 if( m_settings.m_initialSide == -1 )
1594 amplitude *= -1;
1595
1596 VECTOR2I widthHandleOffset = ( base.B - base.A ).Perpendicular().Resize( amplitude );
1597
1598 aPoints.AddPoint( base.A + widthHandleOffset );
1599 aPoints.Point( 2 ).SetGridConstraint( IGNORE_GRID );
1600
1601 VECTOR2I spacingHandleOffset =
1602 widthHandleOffset + ( base.B - base.A ).Resize( KiROUND( m_settings.m_spacing * 1.5 ) );
1603
1604 aPoints.AddPoint( base.A + spacingHandleOffset );
1605 aPoints.Point( 3 ).SetGridConstraint( IGNORE_GRID );
1606
1607 return true;
1608}
1609
1610
1612{
1613 VECTOR2I centerlineOffset;
1614 VECTOR2I centerlineOffsetEnd;
1615
1616 if( m_tuningMode == DIFF_PAIR && m_baseLineCoupled && m_baseLineCoupled->SegmentCount() > 0 )
1617 {
1618 centerlineOffset = ( m_baseLineCoupled->CPoint( 0 ) - m_origin ) / 2;
1619 centerlineOffsetEnd = ( m_baseLineCoupled->CLastPoint() - m_end ) / 2;
1620 }
1621
1622 SEG base = m_baseLine && m_baseLine->SegmentCount() > 0 ? m_baseLine->CSegment( 0 )
1623 : SEG( m_origin, m_end );
1624
1625 base.A += centerlineOffset;
1626 base.B += centerlineOffset;
1627
1628 m_origin = aEditPoints.Point( 0 ).GetPosition() - centerlineOffset;
1629 m_end = aEditPoints.Point( 1 ).GetPosition() - centerlineOffsetEnd;
1630
1631 if( aEditPoints.Point( 2 ).IsActive() )
1632 {
1633 VECTOR2I wHandle = aEditPoints.Point( 2 ).GetPosition();
1634
1635 int value = base.LineDistance( wHandle );
1636
1637 value -= KiROUND( m_trackWidth / 2.0 );
1638
1639 if( m_tuningMode == DIFF_PAIR )
1640 value -= m_trackWidth + m_diffPairGap;
1641
1642 SetMaxAmplitude( KiROUND( value / pcbIUScale.mmToIU( 0.01 ) ) * pcbIUScale.mmToIU( 0.01 ) );
1643
1644 int side = base.Side( wHandle );
1645
1646 if( side < 0 )
1647 m_settings.m_initialSide = PNS::MEANDER_SIDE_LEFT;
1648 else
1649 m_settings.m_initialSide = PNS::MEANDER_SIDE_RIGHT;
1650 }
1651
1652 if( aEditPoints.Point( 3 ).IsActive() )
1653 {
1654 VECTOR2I wHandle = aEditPoints.Point( 2 ).GetPosition();
1655 VECTOR2I sHandle = aEditPoints.Point( 3 ).GetPosition();
1656
1657 int value = KiROUND( SEG( base.A, wHandle ).LineDistance( sHandle ) / 1.5 );
1658
1659 SetSpacing( KiROUND( value / pcbIUScale.mmToIU( 0.01 ) ) * pcbIUScale.mmToIU( 0.01 ) );
1660 }
1661
1662 return true;
1663}
1664
1665
1667{
1668 VECTOR2I centerlineOffset;
1669 VECTOR2I centerlineOffsetEnd;
1670
1671 if( m_tuningMode == DIFF_PAIR && m_baseLineCoupled && m_baseLineCoupled->SegmentCount() > 0 )
1672 {
1673 centerlineOffset = ( m_baseLineCoupled->CPoint( 0 ) - m_origin ) / 2;
1674 centerlineOffsetEnd = ( m_baseLineCoupled->CLastPoint() - m_end ) / 2;
1675 }
1676
1677 SEG base = m_baseLine && m_baseLine->SegmentCount() > 0 ? m_baseLine->CSegment( 0 )
1678 : SEG( m_origin, m_end );
1679
1680 base.A += centerlineOffset;
1681 base.B += centerlineOffset;
1682
1683 int amplitude = m_settings.m_maxAmplitude + KiROUND( m_trackWidth / 2.0 );
1684
1685 if( m_tuningMode == DIFF_PAIR )
1686 amplitude += m_trackWidth + m_diffPairGap;
1687
1688 if( m_settings.m_initialSide == -1 )
1689 amplitude *= -1;
1690
1691 VECTOR2I widthHandleOffset = ( base.B - base.A ).Perpendicular().Resize( amplitude );
1692
1693 aEditPoints.Point( 0 ).SetPosition( m_origin + centerlineOffset );
1694 aEditPoints.Point( 1 ).SetPosition( m_end + centerlineOffsetEnd );
1695
1696 aEditPoints.Point( 2 ).SetPosition( base.A + widthHandleOffset );
1697
1698 VECTOR2I spacingHandleOffset =
1699 widthHandleOffset + ( base.B - base.A ).Resize( KiROUND( m_settings.m_spacing * 1.5 ) );
1700
1701 aEditPoints.Point( 3 ).SetPosition( base.A + spacingHandleOffset );
1702
1703 return true;
1704}
1705
1706
1708{
1709 if( m_baseLine )
1710 {
1711 int clampedMaxAmplitude = m_settings.m_maxAmplitude;
1712 int minAllowedAmplitude = 0;
1713 int baselineOffset = m_tuningMode == DIFF_PAIR ? ( m_diffPairGap + m_trackWidth ) / 2 : 0;
1714
1716 {
1717 minAllowedAmplitude = baselineOffset + m_trackWidth;
1718 }
1719 else
1720 {
1721 int correction = m_trackWidth * tan( 1 - tan( DEG2RAD( 22.5 ) ) );
1722 minAllowedAmplitude = baselineOffset + correction;
1723 }
1724
1725 clampedMaxAmplitude = std::max( clampedMaxAmplitude, minAllowedAmplitude );
1726
1727 if( m_settings.m_singleSided )
1728 {
1729 SHAPE_LINE_CHAIN clBase = *m_baseLine;
1731
1732 if( m_tuningMode != DIFF_PAIR )
1733 {
1734 int amplitude = clampedMaxAmplitude + KiROUND( m_trackWidth / 2.0 );
1735
1737
1739 true ) )
1740 {
1741 chain.Append( m_settings.m_initialSide >= 0 ? right : left );
1742 chain.Append( clBase.Reverse() );
1743 chain.SetClosed( true );
1744
1745 return chain;
1746 }
1747 }
1749 {
1750 int amplitude = clampedMaxAmplitude + m_trackWidth + KiROUND( m_diffPairGap / 2.0 );
1751
1753 SHAPE_LINE_CHAIN chain1, chain2;
1754
1756 true ) )
1757 {
1758 if( m_settings.m_initialSide >= 0 )
1759 chain1.Append( right );
1760 else
1761 chain1.Append( left );
1762
1764 ARC_LOW_DEF, left, right, true ) )
1765 {
1766 if( m_settings.m_initialSide >= 0 )
1767 chain1.Append( left.Reverse() );
1768 else
1769 chain1.Append( right.Reverse() );
1770 }
1771
1772 chain1.SetClosed( true );
1773 }
1774
1776 true ) )
1777 {
1778 if( m_settings.m_initialSide >= 0 )
1779 chain2.Append( right );
1780 else
1781 chain2.Append( left );
1782
1784 ARC_LOW_DEF, left, right, true ) )
1785 {
1786 if( m_settings.m_initialSide >= 0 )
1787 chain2.Append( left.Reverse() );
1788 else
1789 chain2.Append( right.Reverse() );
1790 }
1791
1792 chain2.SetClosed( true );
1793 }
1794
1795 SHAPE_POLY_SET merged;
1796 merged.BooleanAdd( chain1, chain2 );
1797
1798 if( merged.OutlineCount() > 0 )
1799 return merged.Outline( 0 );
1800 }
1801 }
1802
1803 // Not single-sided / fallback
1804 SHAPE_POLY_SET poly;
1806
1807 int amplitude = 0;
1808
1809 if( m_tuningMode == DIFF_PAIR )
1810 amplitude = clampedMaxAmplitude + m_diffPairGap / 2 + KiROUND( m_trackWidth );
1811 else
1812 amplitude = clampedMaxAmplitude + KiROUND( m_trackWidth / 2.0 );
1813
1815
1817 {
1818 SHAPE_POLY_SET polyCoupled;
1820 ARC_LOW_DEF, false );
1821
1822 poly.ClearArcs();
1823 polyCoupled.ClearArcs();
1824
1825 SHAPE_POLY_SET merged;
1826 merged.BooleanAdd( poly, polyCoupled );
1827
1828 if( merged.OutlineCount() > 0 )
1829 return merged.Outline( 0 );
1830 }
1831
1832 if( poly.OutlineCount() > 0 )
1833 return poly.Outline( 0 );
1834 }
1835
1836 return SHAPE_LINE_CHAIN();
1837}
1838
1839
1841{
1842 // Members can only be judged against a baseline, so keep patterns that lack one
1843 if( !m_baseLine || m_baseLine->PointCount() < 2 )
1844 return true;
1845
1846 std::vector<const SHAPE_LINE_CHAIN*> baseLines = { &*m_baseLine };
1847
1848 if( m_tuningMode == DIFF_PAIR && m_baseLineCoupled && m_baseLineCoupled->PointCount() >= 2 )
1849 baseLines.push_back( &*m_baseLineCoupled );
1850
1851 const int64_t halfWidth = m_trackWidth / 2;
1852
1853 // Stubs past the end caps project onto a baseline end and are not meanders
1854 auto isMeanderPoint =
1855 [&]( const VECTOR2I& aPt )
1856 {
1857 const SHAPE_LINE_CHAIN* nearestLine = nullptr;
1858 VECTOR2I nearestPt;
1859 int64_t nearestDistSq = std::numeric_limits<int64_t>::max();
1860
1861 for( const SHAPE_LINE_CHAIN* line : baseLines )
1862 {
1863 VECTOR2I pt = line->NearestPoint( aPt );
1864 int64_t distSq = ( aPt - pt ).SquaredEuclideanNorm();
1865
1866 if( distSq < nearestDistSq )
1867 {
1868 nearestLine = line;
1869 nearestPt = pt;
1870 nearestDistSq = distSq;
1871 }
1872 }
1873
1874 return nearestDistSq > halfWidth * halfWidth && nearestPt != nearestLine->CPoint( 0 )
1875 && nearestPt != nearestLine->CLastPoint();
1876 };
1877
1878 for( BOARD_ITEM* item : GetBoardItems() )
1879 {
1880 PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( item );
1881
1882 if( !track )
1883 continue;
1884
1885 if( isMeanderPoint( track->GetStart() ) || isMeanderPoint( track->GetEnd() ) )
1886 return true;
1887
1888 if( track->Type() == PCB_ARC_T && isMeanderPoint( static_cast<PCB_ARC*>( track )->GetMid() ) )
1889 return true;
1890 }
1891
1892 return false;
1893}
1894
1895
1896void PCB_TUNING_PATTERN::ViewDraw( int aLayer, KIGFX::VIEW* aView ) const
1897{
1898 if( !IsSelected() && !IsNew() )
1899 return;
1900
1901 KIGFX::PREVIEW::DRAW_CONTEXT ctx( *aView );
1902
1903 int size = KiROUND( aView->ToWorld( EDIT_POINT::POINT_SIZE ) * 0.8 );
1904 size = std::max( size, pcbIUScale.mmToIU( 0.05 ) );
1905
1906 if( !HasFlag( IN_EDIT ) )
1907 {
1908 if( m_baseLine )
1909 {
1910 for( int i = 0; i < m_baseLine->SegmentCount(); i++ )
1911 {
1912 SEG seg = m_baseLine->CSegment( i );
1913 ctx.DrawLineDashed( seg.A, seg.B, size, size / 6, true );
1914 }
1915 }
1916 else
1917 {
1918 ctx.DrawLineDashed( m_origin, m_end, size, size / 6, false );
1919 }
1920
1922 {
1923 for( int i = 0; i < m_baseLineCoupled->SegmentCount(); i++ )
1924 {
1925 SEG seg = m_baseLineCoupled->CSegment( i );
1926 ctx.DrawLineDashed( seg.A, seg.B, size, size / 6, true );
1927 }
1928 }
1929 }
1930
1932
1933 for( int i = 0; i < chain.SegmentCount(); i++ )
1934 {
1935 SEG seg = chain.Segment( i );
1936 ctx.DrawLineDashed( seg.A, seg.B, size, size / 2, false );
1937 }
1938}
1939
1940
1942{
1944
1945 props.set( "tuning_mode", tuningToString( m_tuningMode ) );
1946 props.set( "initial_side", sideToString( m_settings.m_initialSide ) );
1947 props.set( "last_status", statusToString( m_tuningStatus ) );
1948 props.set( "is_time_domain", m_settings.m_isTimeDomain );
1949
1950 props.set( "end", m_end );
1951 props.set( "corner_radius_percent", m_settings.m_cornerRadiusPercentage );
1952 props.set( "single_sided", m_settings.m_singleSided );
1953 props.set( "rounded", m_settings.m_cornerStyle == PNS::MEANDER_STYLE_ROUND );
1954
1955 props.set_iu( "max_amplitude", m_settings.m_maxAmplitude );
1956 props.set_iu( "min_amplitude", m_settings.m_minAmplitude );
1957 props.set_iu( "min_spacing", m_settings.m_spacing );
1958 props.set_iu( "target_length_min", m_settings.m_targetLength.Min() );
1959 props.set_iu( "target_length", m_settings.m_targetLength.Opt() );
1960 props.set_iu( "target_length_max", m_settings.m_targetLength.Max() );
1961 props.set_iu( "target_delay_min", m_settings.m_targetLengthDelay.Min() );
1962 props.set_iu( "target_delay", m_settings.m_targetLengthDelay.Opt() );
1963 props.set_iu( "target_delay_max", m_settings.m_targetLengthDelay.Max() );
1964 props.set_iu( "target_skew_min", m_settings.m_targetSkew.Min() );
1965 props.set_iu( "target_skew", m_settings.m_targetSkew.Opt() );
1966 props.set_iu( "target_skew_max", m_settings.m_targetSkew.Max() );
1967 props.set_iu( "last_track_width", m_trackWidth );
1968 props.set_iu( "last_diff_pair_gap", m_diffPairGap );
1969 props.set_iu( "last_tuning_length", m_tuningLength );
1970
1971 props.set( "last_netname", m_lastNetName );
1972 props.set( "override_custom_rules", m_settings.m_overrideCustomRules );
1973
1974 if( m_baseLine )
1975 props.set( "base_line", wxAny( *m_baseLine ) );
1976
1977 if( m_baseLineCoupled )
1978 props.set( "base_line_coupled", wxAny( *m_baseLineCoupled ) );
1979
1980 return props;
1981}
1982
1983
1985{
1987
1988 wxString tuningMode;
1989 aProps.get_to( "tuning_mode", tuningMode );
1990 m_tuningMode = tuningFromString( tuningMode.utf8_string() );
1991
1992 wxString side;
1993 aProps.get_to( "initial_side", side );
1994 m_settings.m_initialSide = sideFromString( side.utf8_string() );
1995
1996 wxString status;
1997 aProps.get_to( "last_status", status );
1998 m_tuningStatus = statusFromString( status.utf8_string() );
1999
2000 aProps.get_to( "is_time_domain", m_settings.m_isTimeDomain );
2001
2002 aProps.get_to( "end", m_end );
2003 aProps.get_to( "corner_radius_percent", m_settings.m_cornerRadiusPercentage );
2004 aProps.get_to( "single_sided", m_settings.m_singleSided );
2005 aProps.get_to( "side", m_settings.m_initialSide );
2006
2007 bool rounded = false;
2008 aProps.get_to( "rounded", rounded );
2010
2011 long long int val;
2012
2013 aProps.get_to_iu( "target_length", val );
2014 m_settings.SetTargetLength( val );
2015
2016 if( aProps.get_to_iu( "target_length_min", val ) )
2017 m_settings.m_targetLength.SetMin( val );
2018
2019 if( aProps.get_to_iu( "target_length_max", val ) )
2020 m_settings.m_targetLength.SetMax( val );
2021
2022 aProps.get_to_iu( "target_delay", val );
2023 m_settings.SetTargetLengthDelay( val );
2024
2025 if( aProps.get_to_iu( "target_delay_min", val ) )
2026 m_settings.m_targetLengthDelay.SetMin( val );
2027
2028 if( aProps.get_to_iu( "target_delay_max", val ) )
2029 m_settings.m_targetLengthDelay.SetMax( val );
2030
2031 int int_val;
2032
2033 aProps.get_to_iu( "target_skew", int_val );
2034 m_settings.SetTargetSkew( int_val );
2035
2036 if( aProps.get_to_iu( "target_skew_min", int_val ) )
2037 m_settings.m_targetSkew.SetMin( int_val );
2038
2039 if( aProps.get_to_iu( "target_skew_max", int_val ) )
2040 m_settings.m_targetSkew.SetMax( int_val );
2041
2042 aProps.get_to_iu( "max_amplitude", m_settings.m_maxAmplitude );
2043 aProps.get_to_iu( "min_amplitude", m_settings.m_minAmplitude );
2044 aProps.get_to_iu( "min_spacing", m_settings.m_spacing );
2045 aProps.get_to_iu( "last_track_width", m_trackWidth );
2046 aProps.get_to_iu( "last_diff_pair_gap", m_diffPairGap );
2047 aProps.get_to_iu( "last_tuning_length", m_tuningLength );
2048 aProps.get_to( "override_custom_rules", m_settings.m_overrideCustomRules );
2049
2050 aProps.get_to( "last_netname", m_lastNetName );
2051
2052 if( auto baseLine = aProps.get_opt<SHAPE_LINE_CHAIN>( "base_line" ) )
2053 m_baseLine = *baseLine;
2054
2055 if( auto baseLineCoupled = aProps.get_opt<SHAPE_LINE_CHAIN>( "base_line_coupled" ) )
2056 m_baseLineCoupled = *baseLineCoupled;
2057
2059}
2060
2061
2063{
2064 switch( aStatus )
2065 {
2066 case PNS::MEANDER_PLACER_BASE::TOO_LONG: return _( "too long" );
2067 case PNS::MEANDER_PLACER_BASE::TOO_SHORT: return _( "too short" );
2068 case PNS::MEANDER_PLACER_BASE::TUNED: return _( "tuned" );
2069 default: return _( "unknown" );
2070 }
2071}
2072
2073
2075{
2076 if( m_tuningLength == 0 )
2077 return;
2078
2079 EDA_UNITS units = m_settings.m_isTimeDomain ? EDA_UNITS::PS : EDA_UNITS::MM;
2080
2081 m_tuningInfo.Printf( wxS( "%s (%s)" ),
2083 static_cast<double>( m_tuningLength ) ),
2085}
2086
2087
2088void PCB_TUNING_PATTERN::Serialize( google::protobuf::Any& aContainer ) const
2089{
2090 using namespace kiapi::board::types;
2091 using namespace kiapi::common::types;
2092
2093 TuningPattern pattern;
2094
2095 pattern.mutable_id()->set_value( m_Uuid.AsStdString() );
2096 pattern.set_layer( ToProtoEnum<PCB_LAYER_ID, BoardLayer>( GetLayer() ) );
2097 kiapi::common::PackVector2( *pattern.mutable_origin(), GetPosition() );
2098 kiapi::common::PackVector2( *pattern.mutable_end(), m_end );
2100
2101 TuningPatternSettings* settings = pattern.mutable_settings();
2102 settings->set_initial_side( ToProtoEnum<PNS::MEANDER_SIDE, TuningPatternMeanderSide>( m_settings.m_initialSide ) );
2103 settings->set_corner_style( ToProtoEnum<PNS::MEANDER_STYLE, TuningPatternCornerStyle>( m_settings.m_cornerStyle ) );
2104 settings->set_corner_radius_percent( m_settings.m_cornerRadiusPercentage );
2105 settings->set_single_sided( m_settings.m_singleSided );
2106 settings->mutable_max_amplitude()->set_value_nm( pcbIUScale.IUToNm( m_settings.m_maxAmplitude ) );
2107 settings->mutable_min_amplitude()->set_value_nm( pcbIUScale.IUToNm( m_settings.m_minAmplitude ) );
2108 settings->mutable_min_spacing()->set_value_nm( pcbIUScale.IUToNm( m_settings.m_spacing ) );
2109
2110 pattern.set_target_mode( m_settings.m_isTimeDomain ? TuningPatternTargetMode::TPTM_TIME_DOMAIN
2111 : TuningPatternTargetMode::TPTM_LENGTH );
2112
2113 pattern.set_override_custom_rules( m_settings.m_overrideCustomRules );
2114
2116 {
2117 if( m_settings.m_isTimeDomain )
2118 {
2119 TimeRange* range = pattern.mutable_target_skew_delay();
2120
2121 if( m_settings.m_targetSkewDelay.HasMin() )
2122 range->set_min_as( m_settings.m_targetSkewDelay.Min() );
2123
2124 if( m_settings.m_targetSkewDelay.HasOpt() )
2125 range->set_opt_as( m_settings.m_targetSkewDelay.Opt() );
2126
2127 if( m_settings.m_targetSkewDelay.HasMax() )
2128 range->set_max_as( m_settings.m_targetSkewDelay.Max() );
2129 }
2130 else
2131 {
2132 kiapi::common::types::MinOptMax* range = pattern.mutable_target_skew();
2133
2134 if( m_settings.m_targetSkew.HasMin() )
2135 range->set_min( m_settings.m_targetSkew.Min() );
2136
2137 if( m_settings.m_targetSkew.HasOpt() )
2138 range->set_opt( m_settings.m_targetSkew.Opt() );
2139
2140 if( m_settings.m_targetSkew.HasMax() )
2141 range->set_max( m_settings.m_targetSkew.Max() );
2142 }
2143 }
2144 else if( m_settings.m_isTimeDomain )
2145 {
2146 TimeRange* range = pattern.mutable_target_delay();
2147
2148 if( m_settings.m_targetLengthDelay.HasMin() )
2149 range->set_min_as( m_settings.m_targetLengthDelay.Min() );
2150
2151 if( m_settings.m_targetLengthDelay.HasOpt() )
2152 range->set_opt_as( m_settings.m_targetLengthDelay.Opt() );
2153
2154 if( m_settings.m_targetLengthDelay.HasMax() )
2155 range->set_max_as( m_settings.m_targetLengthDelay.Max() );
2156 }
2157 else
2158 {
2159 kiapi::common::types::MinOptMax* range = pattern.mutable_target_length();
2160
2161 if( m_settings.m_targetLength.HasMin() )
2162 range->set_min( m_settings.m_targetLength.Min() );
2163
2164 if( m_settings.m_targetLength.HasOpt() )
2165 range->set_opt( m_settings.m_targetLength.Opt() );
2166
2167 if( m_settings.m_targetLength.HasMax() )
2168 range->set_max( m_settings.m_targetLength.Max() );
2169 }
2170
2171 TuningPatternState* state = pattern.mutable_state();
2173
2174 if( m_tuningLength != 0 )
2175 state->set_tuning_value( m_tuningLength );
2176
2177 pattern.set_locked( IsLocked() ? LockedState::LS_LOCKED : LockedState::LS_UNLOCKED );
2178
2179 if( const BOARD* board = GetBoard() )
2180 pattern.mutable_parent()->set_value( board->m_Uuid.AsStdString() );
2181
2182 for( EDA_ITEM* member : GetItems() )
2183 pattern.add_members()->set_value( member->m_Uuid.AsStdString() );
2184
2185 kiapi::common::PackCustomProperties( pattern.mutable_custom_properties(), *this );
2186 aContainer.PackFrom( pattern );
2187}
2188
2189
2190bool PCB_TUNING_PATTERN::Deserialize( const google::protobuf::Any& aContainer )
2191{
2192 using namespace kiapi::board::types;
2193 using namespace kiapi::common::types;
2194
2195 TuningPattern pattern;
2196
2197 if( !aContainer.UnpackTo( &pattern ) )
2198 return false;
2199
2200 BOARD* board = GetBoard();
2201
2202 if( !board )
2203 return false;
2204
2205 SetUuidDirect( ::KIID( pattern.id().value() ) );
2206 SetLayer( FromProtoEnum<PCB_LAYER_ID>( pattern.layer() ) );
2207 SetPosition( kiapi::common::UnpackVector2( pattern.origin() ) );
2208 m_end = kiapi::common::UnpackVector2( pattern.end() );
2210
2211 const TuningPatternSettings& settings = pattern.settings();
2212 m_settings.m_initialSide = FromProtoEnum<PNS::MEANDER_SIDE>( settings.initial_side() );
2213 m_settings.m_cornerStyle = FromProtoEnum<PNS::MEANDER_STYLE>( settings.corner_style() );
2214 m_settings.m_cornerRadiusPercentage = settings.corner_radius_percent();
2215 m_settings.m_singleSided = settings.single_sided();
2216 m_settings.m_maxAmplitude = pcbIUScale.NmToIU( settings.max_amplitude().value_nm() );
2217 m_settings.m_minAmplitude = pcbIUScale.NmToIU( settings.min_amplitude().value_nm() );
2218 m_settings.m_spacing = pcbIUScale.NmToIU( settings.min_spacing().value_nm() );
2219 m_settings.m_overrideCustomRules = pattern.override_custom_rules();
2220
2221 switch( pattern.target_case() )
2222 {
2223 case TuningPattern::kTargetLength:
2224 {
2225 const kiapi::common::types::MinOptMax& range = pattern.target_length();
2226
2227 m_settings.m_isTimeDomain = false;
2228
2229 if( range.has_opt() )
2230 m_settings.SetTargetLength( range.opt() );
2231
2232 if( range.has_min() )
2233 m_settings.m_targetLength.SetMin( range.min() );
2234
2235 if( range.has_max() )
2236 m_settings.m_targetLength.SetMax( range.max() );
2237
2238 break;
2239 }
2240
2241 case TuningPattern::kTargetDelay:
2242 {
2243 const TimeRange& range = pattern.target_delay();
2244
2245 m_settings.m_isTimeDomain = true;
2246
2247 if( range.has_opt_as() )
2248 m_settings.SetTargetLengthDelay( range.opt_as() );
2249
2250 if( range.has_min_as() )
2251 m_settings.m_targetLengthDelay.SetMin( range.min_as() );
2252
2253 if( range.has_max_as() )
2254 m_settings.m_targetLengthDelay.SetMax( range.max_as() );
2255
2256 break;
2257 }
2258
2259 case TuningPattern::kTargetSkew:
2260 {
2261 const kiapi::common::types::MinOptMax& range = pattern.target_skew();
2262
2263 m_settings.m_isTimeDomain = false;
2264
2265 if( range.has_opt() )
2266 m_settings.SetTargetSkew( range.opt() );
2267
2268 if( range.has_min() )
2269 m_settings.m_targetSkew.SetMin( range.min() );
2270
2271 if( range.has_max() )
2272 m_settings.m_targetSkew.SetMax( range.max() );
2273
2274 break;
2275 }
2276
2277 case TuningPattern::kTargetSkewDelay:
2278 {
2279 const TimeRange& range = pattern.target_skew_delay();
2280
2281 m_settings.m_isTimeDomain = true;
2282
2283 if( range.has_opt_as() )
2284 m_settings.SetTargetSkewDelay( range.opt_as() );
2285
2286 if( range.has_min_as() )
2287 m_settings.m_targetSkewDelay.SetMin( range.min_as() );
2288
2289 if( range.has_max_as() )
2290 m_settings.m_targetSkewDelay.SetMax( range.max_as() );
2291
2292 break;
2293 }
2294
2295 case TuningPattern::TARGET_NOT_SET: break;
2296 }
2297
2298 if( pattern.has_state() )
2299 {
2300 const TuningPatternState& state = pattern.state();
2302 m_tuningLength = state.tuning_value();
2303 }
2304
2305 SetLocked( pattern.locked() == LockedState::LS_LOCKED );
2306 kiapi::common::UnpackCustomProperties( pattern.custom_properties(), *this );
2307
2308 m_items.clear();
2309 m_deserializedItems.clear();
2310
2311 for( const kiapi::common::types::KIID& memberId : pattern.members() )
2312 {
2313 if( EDA_ITEM* item = board->ResolveItem( ::KIID( memberId.value() ), true ) )
2314 m_deserializedItems.insert( item );
2315 }
2316
2318
2319 return true;
2320}
2321
2322
2324{
2326 DRC_CONSTRAINT constraint;
2327
2328 if( !m_items.empty() )
2329 {
2330 BOARD_ITEM* startItem = static_cast<BOARD_ITEM*>( *m_items.begin() );
2331 DRC_ENGINE* drcEngine = GetBoard()->GetDesignSettings().m_DRCEngine.get();
2332
2334 {
2335 constraint = drcEngine->EvalRules( SKEW_CONSTRAINT, startItem, nullptr, GetLayer() );
2336
2337 if( !constraint.IsNull() && !settings.m_overrideCustomRules )
2338 {
2340 {
2341 settings.SetTargetSkewDelay( constraint.GetValue() );
2342 settings.SetTargetSkew( MINOPTMAX<int>() );
2343 settings.m_isTimeDomain = true;
2344 }
2345 else
2346 {
2347 settings.SetTargetSkewDelay( MINOPTMAX<int>() );
2348 settings.SetTargetSkew( constraint.GetValue() );
2349 settings.m_isTimeDomain = false;
2350 }
2351 }
2352 }
2353 else
2354 {
2355 // Prefer chain-level constraint if net is part of a chain
2356 constraint = drcEngine->EvalRules( NET_CHAIN_LENGTH_CONSTRAINT, startItem, nullptr, GetLayer() );
2357
2358 if( ( constraint.IsNull() || constraint.GetSeverity() == RPT_SEVERITY_IGNORE ) )
2359 constraint = drcEngine->EvalRules( LENGTH_CONSTRAINT, startItem, nullptr, GetLayer() );
2360
2361 if( !constraint.IsNull() && !settings.m_overrideCustomRules )
2362 {
2364 {
2365 settings.SetTargetLengthDelay( constraint.GetValue() );
2366 settings.SetTargetLength( MINOPTMAX<int>() );
2367 settings.m_isTimeDomain = true;
2368 }
2369 else
2370 {
2372 settings.SetTargetLength( constraint.GetValue() );
2373 settings.m_isTimeDomain = false;
2374 }
2375 }
2376 }
2377 }
2378
2379 DIALOG_TUNING_PATTERN_PROPERTIES dlg( aEditFrame, settings, GetPNSMode(), constraint );
2380
2381 if( dlg.ShowModal() == wxID_OK )
2382 {
2383 BOARD_COMMIT commit( aEditFrame );
2384 commit.Modify( this );
2385 m_settings = settings;
2386
2387 GENERATOR_TOOL* generatorTool = aEditFrame->GetToolManager()->GetTool<GENERATOR_TOOL>();
2388 EditStart( generatorTool, GetBoard(), &commit );
2389 Update( generatorTool, GetBoard(), &commit );
2390 EditFinish( generatorTool, GetBoard(), &commit );
2391
2392 commit.Push( _( "Edit Tuning Pattern" ) );
2393 }
2394}
2395
2396
2398 bool aStatusItemsOnly )
2399{
2400 std::vector<EDA_ITEM*> previewItems;
2401 KIGFX::VIEW* view = aFrame->GetCanvas()->GetView();
2402
2403 if( PNS::MEANDER_PLACER_BASE* placer = dynamic_cast<PNS::MEANDER_PLACER_BASE*>( aTool->Router()->Placer() ) )
2404 {
2405 if( !aStatusItemsOnly )
2406 {
2407 PNS::ITEM_SET items = placer->TunedPath();
2408
2409 for( PNS::ITEM* item : items )
2410 {
2411 previewItems.push_back( new ROUTER_PREVIEW_ITEM( item, aTool->Router()->GetInterface(), view,
2412 PNS_HOVER_ITEM ) );
2413 }
2414 }
2415
2416 TUNING_STATUS_VIEW_ITEM* statusItem = new TUNING_STATUS_VIEW_ITEM( aFrame );
2417
2418 // The unit type must be known before any value text is formatted
2419 statusItem->SetIsTimeDomain( m_settings.m_isTimeDomain );
2420
2421 // Build first line: "Net-(R1-Pad2) | Chain: Chain1" OR just net if no chain
2422 wxString scopeLine;
2423 BOARD* board = GetBoard();
2424 wxString netName = m_lastNetName;
2425 wxString netChainName;
2426
2427 if( board && !netName.IsEmpty() )
2428 {
2429 for( NETINFO_ITEM* net : board->GetNetInfo() )
2430 {
2431 if( UnescapeString( net->GetNetname() ) == netName )
2432 {
2433 netChainName = net->GetNetChain();
2434 break;
2435 }
2436 }
2437 }
2438
2439 if( !netName.IsEmpty() )
2440 {
2441 if( !netChainName.IsEmpty() )
2442 scopeLine = wxString::Format( _( "%s | %s" ), netName, netChainName );
2443 else
2444 scopeLine = netName;
2445 }
2446
2447 statusItem->SetScopeLine( scopeLine );
2448
2450 {
2451 if( m_settings.m_isTimeDomain )
2452 statusItem->SetMinMax( m_settings.m_targetSkewDelay.Min(), m_settings.m_targetSkewDelay.Max() );
2453 else
2454 statusItem->SetMinMax( m_settings.m_targetSkew.Min(), m_settings.m_targetSkew.Max() );
2455 }
2456 else
2457 {
2458 // Show the per-net LENGTH_CONSTRAINT directly from DRC, not the blended
2459 // budget in m_targetLength (which may include chain adjustments).
2460 bool hasNetConstraint = false;
2461
2462 if( board && board->GetDesignSettings().m_DRCEngine )
2463 {
2464 DRC_ENGINE* engine = board->GetDesignSettings().m_DRCEngine.get();
2465 PCB_TRACK* netRepTrack = nullptr;
2466
2467 for( BOARD_ITEM* bi : board->Tracks() )
2468 {
2469 if( PCB_TRACK* tr = dynamic_cast<PCB_TRACK*>( bi ) )
2470 {
2471 if( tr->GetNet() && UnescapeString( tr->GetNet()->GetNetname() ) == netName )
2472 {
2473 netRepTrack = tr;
2474 break;
2475 }
2476 }
2477 }
2478
2479 if( netRepTrack )
2480 {
2481 DRC_CONSTRAINT netC = engine->EvalRules( LENGTH_CONSTRAINT, netRepTrack, nullptr,
2482 netRepTrack->GetLayer() );
2483
2484 if( !netC.IsNull() && netC.GetSeverity() != RPT_SEVERITY_IGNORE )
2485 {
2486 statusItem->SetMinMax( static_cast<double>( netC.GetValue().Min() ),
2487 static_cast<double>( netC.GetValue().Max() ) );
2488 hasNetConstraint = true;
2489 }
2490 }
2491 }
2492
2493 if( !hasNetConstraint )
2494 statusItem->ClearMinMax();
2495 }
2496
2497 // Set chain-level min/max from raw chain constraint
2498 if( !netChainName.IsEmpty() && board && board->GetDesignSettings().m_DRCEngine )
2499 {
2500 DRC_ENGINE* engine = board->GetDesignSettings().m_DRCEngine.get();
2501 // Find a track on this net to evaluate the rule against
2502 PCB_TRACK* repTrack = nullptr;
2503
2504 for( BOARD_ITEM* bi : board->Tracks() )
2505 {
2506 if( PCB_TRACK* tr = dynamic_cast<PCB_TRACK*>( bi ) )
2507 {
2508 NETINFO_ITEM* ni = tr->GetNet();
2509
2510 if( ni && ni->GetNetChain() == netChainName )
2511 {
2512 repTrack = tr;
2513 break;
2514 }
2515 }
2516 }
2517
2518 if( repTrack )
2519 {
2520 DRC_CONSTRAINT chainC = engine->EvalRules( NET_CHAIN_LENGTH_CONSTRAINT, repTrack, nullptr,
2521 repTrack->GetLayer() );
2522
2523 if( !chainC.IsNull() && chainC.GetSeverity() != RPT_SEVERITY_IGNORE )
2524 {
2525 statusItem->SetChainMinMax( static_cast<double>( chainC.GetValue().Min() ),
2526 static_cast<double>( chainC.GetValue().Max() ) );
2527 }
2528 else
2529 {
2530 statusItem->ClearChainMinMax();
2531 }
2532 }
2533 else
2534 {
2535 statusItem->ClearChainMinMax();
2536 }
2537 }
2538 else
2539 {
2540 statusItem->ClearChainMinMax();
2541 }
2542
2543 // Header label line (line 2)
2545 statusItem->SetCurrent( 0.0, _( "current skew" ) );
2546 else if( m_settings.m_isTimeDomain )
2547 statusItem->SetCurrent( 0.0, _( "current delay" ) );
2548 else
2549 statusItem->SetCurrent( 0.0, _( "current length" ) );
2550
2551 // Value lines (lines 3 & 4)
2553 double netVal = m_settings.m_isTimeDomain ? static_cast<double>( placer->TuningDelayResult() )
2554 : static_cast<double>( placer->TuningLengthResult() );
2555 wxString netStr = wxString::Format( m_tuningMode == DIFF_PAIR_SKEW ? _( "Skew: %s" ) : _( "Net: %s" ),
2556 aFrame->MessageTextFromValue( netVal, true, unitType ) );
2557
2558 // Chain total from board state (GetTrackLength for every net) plus live tuning delta.
2559 wxString sigStr;
2560 bool hasSignal = false;
2561
2562 if( !netChainName.IsEmpty() && board )
2563 {
2564 double chainBoardLen = 0.0;
2565 double chainBoardDelay = 0.0;
2566
2567 // Index tracks by netcode once so each chain member is an O(1) lookup
2568 // rather than re-scanning BOARD::Tracks() per net.
2569 std::unordered_map<int, PCB_TRACK*> repByNet;
2570
2571 for( BOARD_ITEM* bi : board->Tracks() )
2572 {
2573 if( PCB_TRACK* tr = dynamic_cast<PCB_TRACK*>( bi ) )
2574 repByNet.emplace( tr->GetNetCode(), tr );
2575 }
2576
2577 for( NETINFO_ITEM* net : board->GetNetInfo() )
2578 {
2579 if( net->GetNetChain() != netChainName )
2580 continue;
2581
2582 auto it = repByNet.find( net->GetNetCode() );
2583
2584 if( it != repByNet.end() && it->second )
2585 {
2586 int cnt = 0; double trk = 0, pd = 0, tDel = 0, pdDel = 0;
2587 std::tie( cnt, trk, pd, tDel, pdDel ) = board->GetTrackLength( *it->second );
2588 chainBoardLen += trk + pd;
2589 chainBoardDelay += tDel + pdDel;
2590 }
2591 }
2592
2593 // Add the meander extension delta (path-independent).
2594 double tuningDelta = 0.0;
2595
2596 if( placer->HasBaseline() )
2597 {
2598 tuningDelta = m_settings.m_isTimeDomain ? static_cast<double>( placer->TuningDelayDelta() )
2599 : static_cast<double>( placer->TuningLengthDelta() );
2600 }
2601
2602 double sigVal = ( m_settings.m_isTimeDomain ? chainBoardDelay : chainBoardLen )
2603 + tuningDelta;
2604
2605 // Bridging: pad-to-pad gaps through series components.
2606 double delayIU = 0.0;
2607 long long bridging = GetCachedBridgingLength( board, netChainName, &delayIU );
2608
2609 if( bridging > 0 )
2610 {
2611 if( m_settings.m_isTimeDomain )
2612 sigVal += delayIU;
2613 else
2614 sigVal += static_cast<double>( bridging );
2615 }
2616
2617 sigStr = wxString::Format( _( "Chain: %s" ), aFrame->MessageTextFromValue( sigVal, true, unitType ) );
2618 hasSignal = true;
2619 }
2620 statusItem->SetNetAndSignalValues( netStr, sigStr, hasSignal );
2621
2622 statusItem->SetPosition( aFrame->GetToolManager()->GetMousePosition() );
2623 previewItems.push_back( statusItem );
2624 }
2625
2626 return previewItems;
2627}
2628
2629
2630void PCB_TUNING_PATTERN::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
2631{
2632 wxString msg;
2633 NETINFO_ITEM* primaryNet = nullptr;
2634 NETINFO_ITEM* coupledNet = nullptr;
2635 PCB_TRACK* primaryItem = nullptr;
2636 PCB_TRACK* coupledItem = nullptr;
2637 NETCLASS* netclass = nullptr;
2638 int width = 0;
2639 bool mixedWidth = false;
2640
2642
2643 aList.emplace_back( _( "Type" ), GetFriendlyName() );
2644
2645 for( EDA_ITEM* member : GetItems() )
2646 {
2647 if( PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( member ) )
2648 {
2649 if( !primaryNet )
2650 {
2651 primaryItem = track;
2652 primaryNet = track->GetNet();
2653 }
2654 else if( !coupledNet && track->GetNet() != primaryNet )
2655 {
2656 coupledItem = track;
2657 coupledNet = track->GetNet();
2658 }
2659
2660 if( !netclass )
2661 netclass = track->GetEffectiveNetClass();
2662
2663 if( track->IsType( { PCB_TRACE_T, PCB_ARC_T } ) )
2664 {
2665 if( !width )
2666 width = track->GetWidth();
2667 else if( width != track->GetWidth() )
2668 mixedWidth = true;
2669 }
2670 }
2671 }
2672
2673 if( coupledNet )
2674 {
2675 aList.emplace_back( _( "Nets" ), UnescapeString( primaryNet->GetNetname() )
2676 + wxS( ", " )
2677 + UnescapeString( coupledNet->GetNetname() ) );
2678 }
2679 else if( primaryNet )
2680 {
2681 aList.emplace_back( _( "Net" ), UnescapeString( primaryNet->GetNetname() ) );
2682 }
2683
2684 if( netclass )
2685 aList.emplace_back( _( "Resolved Netclass" ),
2686 UnescapeString( netclass->GetHumanReadableName() ) );
2687
2688 aList.emplace_back( _( "Layer" ), LayerMaskDescribe() );
2689
2690 // Show chain name if available
2691 if( primaryNet && !primaryNet->GetNetChain().IsEmpty() )
2692 aList.emplace_back( _( "Net Chain" ), primaryNet->GetNetChain() );
2693
2694 if( width && !mixedWidth )
2695 aList.emplace_back( _( "Width" ), aFrame->MessageTextFromValue( width ) );
2696
2697 BOARD* board = GetBoard();
2698 DRC_ENGINE* drcEngine = board->GetDesignSettings().m_DRCEngine.get();
2699 DRC_CONSTRAINT constraint;
2700
2701 // Display full track length (in Pcbnew)
2702 if( board && primaryItem && primaryItem->GetNetCode() > 0 )
2703 {
2704 int count = 0;
2705 double trackLen = 0.0;
2706 double lenPadToDie = 0.0;
2707 double trackDelay = 0.0;
2708 double delayPadToDie = 0.0;
2709
2710 std::tie( count, trackLen, lenPadToDie, trackDelay, delayPadToDie ) = board->GetTrackLength( *primaryItem );
2711
2712 if( coupledItem && coupledItem->GetNetCode() > 0 )
2713 {
2714 double coupledLen = 0.0;
2715 double coupledLenPadToDie = 0.0;
2716 double coupledTrackDelay = 0.0;
2717 double doubledDelayPadToDie = 0.0;
2718
2719 std::tie( count, coupledLen, coupledLenPadToDie, coupledTrackDelay, doubledDelayPadToDie ) =
2720 board->GetTrackLength( *coupledItem );
2721
2722 if( trackDelay == 0.0 || coupledTrackDelay == 0.0 )
2723 {
2724 aList.emplace_back( _( "Routed Lengths" ), aFrame->MessageTextFromValue( trackLen )
2725 + wxS( ", " )
2726 + aFrame->MessageTextFromValue( coupledLen ) );
2727 }
2728 else
2729 {
2730 aList.emplace_back( _( "Routed Delays" ),
2731 aFrame->MessageTextFromValue( trackDelay, true, EDA_DATA_TYPE::TIME )
2732 + wxS( ", " )
2733 + aFrame->MessageTextFromValue( coupledTrackDelay, true, EDA_DATA_TYPE::TIME ) );
2734 }
2735 }
2736 else
2737 {
2738 if( trackDelay == 0.0 )
2739 {
2740 aList.emplace_back( _( "Routed Length" ), aFrame->MessageTextFromValue( trackLen ) );
2741 }
2742 else
2743 {
2744 aList.emplace_back( _( "Routed Delay" ),
2745 aFrame->MessageTextFromValue( trackDelay, true, EDA_DATA_TYPE::TIME ) );
2746 }
2747 }
2748
2749 if( lenPadToDie != 0 && delayPadToDie == 0.0 )
2750 {
2751 msg = aFrame->MessageTextFromValue( lenPadToDie );
2752 aList.emplace_back( _( "Pad To Die Length" ), msg );
2753
2754 msg = aFrame->MessageTextFromValue( trackLen + lenPadToDie );
2755 aList.emplace_back( _( "Full Length" ), msg );
2756 }
2757 else if( delayPadToDie > 0.0 )
2758 {
2759 msg = aFrame->MessageTextFromValue( delayPadToDie, true, EDA_DATA_TYPE::TIME );
2760 aList.emplace_back( _( "Pad To Die Delay" ), msg );
2761
2762 msg = aFrame->MessageTextFromValue( trackDelay + delayPadToDie, true, EDA_DATA_TYPE::TIME );
2763 aList.emplace_back( _( "Full Delay" ), msg );
2764 }
2765
2766 // If part of a chain, display aggregate full chain length/delay (with bridging like overlay).
2767 if( primaryNet && !primaryNet->GetNetChain().IsEmpty() )
2768 {
2769 wxString chainName = primaryNet->GetNetChain();
2770 double totalOtherLen = 0.0;
2771 double totalOtherDelay = 0.0;
2772 BOARD* boardPtr = board;
2773
2774 if( boardPtr )
2775 {
2776 for( NETINFO_ITEM* other : boardPtr->GetNetInfo() )
2777 {
2778 if( other->GetNetChain() != chainName || other == primaryNet )
2779 continue;
2780
2781 // Representative track length for this other net
2782 double oTrackLen = 0.0, oPadDieLen = 0.0, oTrackDelay = 0.0, oPadDieDelay = 0.0;
2783 PCB_TRACK* anyTrack = nullptr;
2784
2785 for( BOARD_ITEM* bi : boardPtr->Tracks() )
2786 {
2787 if( PCB_TRACK* tr = dynamic_cast<PCB_TRACK*>( bi ) )
2788 {
2789 if( tr->GetNetCode() == other->GetNetCode() )
2790 {
2791 anyTrack = tr;
2792 break;
2793 }
2794 }
2795 }
2796
2797 if( anyTrack )
2798 {
2799 int dummyCount = 0;
2800 std::tie( dummyCount, oTrackLen, oPadDieLen, oTrackDelay, oPadDieDelay ) =
2801 boardPtr->GetTrackLength( *anyTrack );
2802 totalOtherLen += ( oTrackLen + oPadDieLen );
2803 totalOtherDelay += ( oTrackDelay + oPadDieDelay );
2804 }
2805 }
2806 }
2807
2808 // Bridging contribution (pad-to-pad gaps in 2-net series components of the chain)
2809 if( trackDelay == 0.0 )
2810 {
2811 double delayIUDummy = 0.0; // not used in length mode
2812 long long bridging = GetCachedBridgingLength( boardPtr, chainName, &delayIUDummy );
2813 aList.emplace_back( _( "Net Chain Full Length" ),
2814 aFrame->MessageTextFromValue( trackLen + lenPadToDie + totalOtherLen
2815 + (double) bridging ) );
2816 }
2817 else
2818 {
2819 double bridgingDelayIU = 0.0;
2820 GetCachedBridgingLength( boardPtr, chainName, &bridgingDelayIU );
2821 aList.emplace_back( _( "Net Chain Full Delay" ),
2822 aFrame->MessageTextFromValue( trackDelay + delayPadToDie + totalOtherDelay
2823 + bridgingDelayIU,
2824 true, EDA_DATA_TYPE::TIME ) );
2825 }
2826 }
2827 }
2828
2830 {
2831 constraint = drcEngine->EvalRules( SKEW_CONSTRAINT, primaryItem, coupledItem, GetLayer() );
2832
2833 if( constraint.IsNull() || m_settings.m_overrideCustomRules )
2834 {
2835 msg = aFrame->MessageTextFromValue( m_settings.m_targetSkew.Opt() );
2836
2837 aList.emplace_back( wxString::Format( _( "Target Skew: %s" ), msg ),
2838 wxString::Format( _( "(from tuning pattern properties)" ) ) );
2839 }
2840 else
2841 {
2842 msg = aFrame->MessageTextFromMinOptMax( constraint.GetValue() );
2843
2844 if( !msg.IsEmpty() )
2845 {
2846 aList.emplace_back( wxString::Format( _( "Skew Constraints: %s" ), msg ),
2847 wxString::Format( _( "(from %s)" ), constraint.GetName() ) );
2848 }
2849 }
2850 }
2851 else
2852 {
2853 // Prefer chain-level constraint if available
2854 constraint = drcEngine->EvalRules( NET_CHAIN_LENGTH_CONSTRAINT, primaryItem, coupledItem, GetLayer() );
2855
2856 if( constraint.IsNull() || constraint.GetSeverity() == RPT_SEVERITY_IGNORE )
2857 constraint = drcEngine->EvalRules( LENGTH_CONSTRAINT, primaryItem, coupledItem, GetLayer() );
2858
2859 if( constraint.IsNull() || m_settings.m_overrideCustomRules )
2860 {
2861 wxString caption;
2862
2863 if( m_settings.m_isTimeDomain )
2864 {
2865 caption = _( "Target Delay: %s" );
2866 msg = aFrame->MessageTextFromValue( static_cast<double>( m_settings.m_targetLengthDelay.Opt() ), true,
2868 }
2869 else
2870 {
2871 caption = _( "Target Length: %s" );
2872 msg = aFrame->MessageTextFromValue( static_cast<double>( m_settings.m_targetLength.Opt() ) );
2873 }
2874
2875 aList.emplace_back( wxString::Format( caption, msg ),
2876 wxString::Format( _( "(from tuning pattern properties)" ) ) );
2877 }
2878 else
2879 {
2880 msg = aFrame->MessageTextFromMinOptMax( constraint.GetValue(), unitType );
2881
2882 wxString caption = m_settings.m_isTimeDomain ? _( "Delay Constraints: %s" ) : _( "Length Constraints: %s" );
2883
2884 if( !msg.IsEmpty() )
2885 {
2886 aList.emplace_back( wxString::Format( caption, msg ),
2887 wxString::Format( _( "(from %s)" ), constraint.GetName() ) );
2888 }
2889 }
2890 }
2891}
2892
2893
2894const wxString PCB_TUNING_PATTERN::DISPLAY_NAME = _HKI( "Tuning Pattern" );
2895const wxString PCB_TUNING_PATTERN::GENERATOR_TYPE = wxS( "tuning_pattern" );
2896
2897
2899
2900
2901#define HITTEST_THRESHOLD_PIXELS 5
2902
2903
2905{
2907 return 0;
2908
2909 if( m_inDrawingTool )
2910 return 0;
2911
2913
2914 m_toolMgr->RunAction( ACTIONS::selectionClear );
2915
2916 SCOPED_TOOL_PUSHER raii( m_frame, aEvent );
2917 Activate();
2918
2919 BOARD* board = m_frame->GetBoard();
2920 BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings();
2921 std::shared_ptr<DRC_ENGINE>& drcEngine = bds.m_DRCEngine;
2922 GENERATOR_TOOL* generatorTool = m_toolMgr->GetTool<GENERATOR_TOOL>();
2923 PNS::ROUTER* router = generatorTool->Router();
2924 PNS::ROUTER_MODE routerMode = aEvent.Parameter<PNS::ROUTER_MODE>();
2925 LENGTH_TUNING_MODE mode = fromPNSMode( routerMode );
2926 PNS::MEANDER_SETTINGS meanderSettings = PCB_TUNING_PATTERN::DefaultSettings( bds, mode );
2927
2929 PCB_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
2930 GENERAL_COLLECTORS_GUIDE guide = m_frame->GetCollectorsGuide();
2931 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::TUNING );
2932
2933 m_pickerItem = nullptr;
2934 m_tuningPattern = nullptr;
2935
2936 // Add a VIEW_GROUP that serves as a preview for the new item
2937 m_preview.Clear();
2938 m_view->Add( &m_preview );
2939
2940 auto applyCommonSettings = [&]( PCB_TUNING_PATTERN* aPattern )
2941 {
2942 aPattern->ApplyToolSettings( meanderSettings );
2943 };
2944
2945 auto updateHoverStatus =
2946 [&]()
2947 {
2948 std::unique_ptr<PCB_TUNING_PATTERN> dummyPattern;
2949
2950 if( m_pickerItem )
2951 {
2952 dummyPattern.reset( PCB_TUNING_PATTERN::CreateNew( generatorTool, m_frame, m_pickerItem, mode ) );
2953 dummyPattern->SetPosition( m_pickerItem->GetFocusPosition() );
2954 dummyPattern->SetEnd( m_pickerItem->GetFocusPosition() );
2955 }
2956
2957 if( dummyPattern )
2958 {
2959 applyCommonSettings( dummyPattern.get() );
2960
2961 dummyPattern->EditStart( generatorTool, m_board, nullptr );
2962 dummyPattern->Update( generatorTool, m_board, nullptr );
2963
2964 m_preview.FreeItems();
2965
2966 for( EDA_ITEM* item : dummyPattern->GetPreviewItems( generatorTool, m_frame ) )
2967 m_preview.Add( item );
2968
2969 generatorTool->Router()->StopRouting();
2970
2971 m_view->Update( &m_preview );
2972 }
2973 else
2974 {
2975
2976 m_preview.FreeItems();
2977 m_view->Update( &m_preview );
2978 }
2979 };
2980
2981 auto updateTuningPattern =
2982 [&]()
2983 {
2984 if( m_tuningPattern && m_tuningPattern->GetPosition() != m_tuningPattern->GetEnd() )
2985 {
2986 m_tuningPattern->EditStart( generatorTool, m_board, nullptr );
2987 m_tuningPattern->Update( generatorTool, m_board, nullptr );
2988
2989 m_preview.FreeItems();
2990
2991 for( EDA_ITEM* item : m_tuningPattern->GetPreviewItems( generatorTool, m_frame, true ) )
2992 m_preview.Add( item );
2993
2994 m_view->Update( &m_preview );
2995 }
2996 };
2997
2998 while( TOOL_EVENT* evt = Wait() )
2999 {
3000 VECTOR2D cursorPos = controls->GetMousePosition();
3001
3002 if( evt->IsCancelInteractive() || evt->IsActivate()
3003 || ( m_tuningPattern && evt->IsAction( &ACTIONS::undo ) ) )
3004 {
3005 if( m_tuningPattern )
3006 {
3007 // First click already made; clean up tuning pattern preview
3008 m_tuningPattern->EditCancel( generatorTool, m_board, nullptr );
3009
3010 delete m_tuningPattern;
3011 m_tuningPattern = nullptr;
3012 }
3013 else
3014 {
3015 break;
3016 }
3017 }
3018 else if( evt->IsMotion() || evt->IsAction( &ACTIONS::refreshPreview ) )
3019 {
3020 if( !m_tuningPattern )
3021 {
3022 // First click not yet made; we're in highlight-net-under-cursor mode
3023
3024 GENERAL_COLLECTOR collector;
3025 collector.m_Threshold = KiROUND( getView()->ToWorld( HITTEST_THRESHOLD_PIXELS ) );
3026
3027 if( m_frame->GetDisplayOptions().m_ContrastModeDisplay != HIGH_CONTRAST_MODE::NORMAL )
3028 guide.SetIncludeSecondary( false );
3029 else
3030 guide.SetIncludeSecondary( true );
3031
3032 guide.SetPreferredLayer( m_frame->GetActiveLayer() );
3033
3034 collector.Collect( board, { PCB_TRACE_T, PCB_ARC_T }, cursorPos, guide );
3035
3036 if( collector.GetCount() > 1 )
3037 selectionTool->GuessSelectionCandidates( collector, cursorPos );
3038
3039 m_pickerItem = nullptr;
3040
3041 if( collector.GetCount() > 0 )
3042 {
3043 double min_dist_sq = std::numeric_limits<double>::max();
3044
3045 for( EDA_ITEM* candidate : collector )
3046 {
3047 VECTOR2I candidatePos;
3048
3049 if( candidate->Type() == PCB_TRACE_T )
3050 {
3051 candidatePos = static_cast<PCB_TRACK*>( candidate )->GetCenter();
3052 }
3053 else if( candidate->Type() == PCB_ARC_T )
3054 {
3055 candidatePos = static_cast<PCB_ARC*>( candidate )->GetMid();
3056 }
3057
3058 double dist_sq = ( cursorPos - candidatePos ).SquaredEuclideanNorm();
3059
3060 if( dist_sq < min_dist_sq )
3061 {
3062 min_dist_sq = dist_sq;
3063 m_pickerItem = static_cast<BOARD_CONNECTED_ITEM*>( candidate );
3064 }
3065 }
3066 }
3067
3068 updateHoverStatus();
3069 }
3070 else
3071 {
3072 // First click already made; we're in preview-tuning-pattern mode
3073
3074 m_tuningPattern->SetEnd( cursorPos );
3075 m_tuningPattern->UpdateSideFromEnd();
3076
3077 updateTuningPattern();
3078 }
3079 }
3080 else if( evt->IsClick( BUT_LEFT ) || evt->IsAction( &ACTIONS::cursorClick ) )
3081 {
3083 {
3084 // First click; create a tuning pattern
3085
3086 if( dynamic_cast<PCB_TUNING_PATTERN*>( m_pickerItem->GetParentGroup() ) )
3087 {
3088 m_frame->ShowInfoBarWarning( _( "Unable to tune segments inside other tuning patterns." ) );
3089 }
3090 else
3091 {
3092 m_preview.FreeItems();
3093
3094 m_frame->SetActiveLayer( m_pickerItem->GetLayer() );
3096
3097 m_tuningPattern->GetSettings().m_signalExtraLength = 0;
3098 m_tuningPattern->GetSettings().m_signalExtraDelay = 0;
3099
3100 applyCommonSettings( m_tuningPattern );
3101
3102 int dummyDist;
3103 int dummyClearance = std::numeric_limits<int>::max() / 2;
3104 VECTOR2I closestPt;
3105
3106 // With an artificially-large clearance this can't *not* collide, but the
3107 // if stmt keeps Coverity happy....
3108 if( m_pickerItem->GetEffectiveShape()->Collide( cursorPos, dummyClearance, &dummyDist,
3109 &closestPt ) )
3110 {
3111 m_tuningPattern->SetPosition( closestPt );
3112 m_tuningPattern->SetEnd( closestPt );
3113 }
3114
3115 m_preview.Add( m_tuningPattern->Clone() );
3116 }
3117 }
3118 else if( m_pickerItem && m_tuningPattern )
3119 {
3120 // Second click; we're done
3121 BOARD_COMMIT commit( m_frame );
3122
3123 m_tuningPattern->EditStart( generatorTool, m_board, &commit );
3124
3125 bool updated = m_tuningPattern->Update( generatorTool, m_board, &commit );
3126 auto* placer = static_cast<PNS::MEANDER_PLACER_BASE*>( router->Placer() );
3127
3128 if( updated && placer && !placer->HasMeanders() )
3129 {
3130 // A pattern without meanders would be an invisible group around a plain track
3131 PNS::MEANDER_PLACER_BASE::TUNING_STATUS status = placer->TuningStatus();
3132
3133 m_tuningPattern->EditCancel( generatorTool, m_board, &commit );
3134 commit.Revert();
3135 router->SyncWorld();
3136
3137 std::set<BOARD_ITEM*> routerItems;
3138
3139 // Router-created copper never reaches the commit, so nothing else frees it
3140 for( const GENERATOR_PNS_CHANGES& changes : generatorTool->GetRouterChanges() )
3141 routerItems.insert( changes.addedItems.begin(), changes.addedItems.end() );
3142
3143 generatorTool->ClearRouterChanges();
3144
3145 for( BOARD_ITEM* item : routerItems )
3146 delete item;
3147
3148 m_preview.FreeItems();
3149 m_view->Update( &m_preview );
3150
3151 m_frame->ShowInfoBarWarning( wxString::Format( _( "No tuning pattern added: no meanders were "
3152 "needed or would fit (%s)." ),
3154 }
3155 else
3156 {
3157 m_tuningPattern->EditFinish( generatorTool, m_board, &commit );
3158 commit.Push( _( "Tune" ) );
3159 }
3160
3161 m_tuningPattern = nullptr;
3162 m_pickerItem = nullptr;
3163 }
3164 }
3165 else if( evt->IsClick( BUT_RIGHT ) )
3166 {
3168 m_menu->ShowContextMenu( dummy );
3169 }
3170 else if( evt->IsAction( &PCB_ACTIONS::spacingIncrease )
3171 || evt->IsAction( &PCB_ACTIONS::spacingDecrease ) )
3172 {
3173 if( m_tuningPattern )
3174 {
3175 auto* placer = static_cast<PNS::MEANDER_PLACER_BASE*>( router->Placer() );
3176
3177 if( placer )
3178 {
3179 placer->SpacingStep( evt->IsAction( &PCB_ACTIONS::spacingIncrease ) ? 1 : -1 );
3180 m_tuningPattern->SetSpacing( placer->MeanderSettings().m_spacing );
3181 meanderSettings.m_spacing = placer->MeanderSettings().m_spacing;
3182 PCB_TUNING_PATTERN::StoreDefaults( bds, mode, meanderSettings, false );
3183
3184 updateTuningPattern();
3185 }
3186 }
3187 else
3188 {
3189 m_frame->ShowInfoBarWarning( _( "Select a track to tune first." ) );
3190 }
3191 }
3192 else if( evt->IsAction( &PCB_ACTIONS::amplIncrease )
3193 || evt->IsAction( &PCB_ACTIONS::amplDecrease ) )
3194 {
3195 if( m_tuningPattern )
3196 {
3197 auto* placer = static_cast<PNS::MEANDER_PLACER_BASE*>( router->Placer() );
3198
3199 if( placer )
3200 {
3201 placer->AmplitudeStep( evt->IsAction( &PCB_ACTIONS::amplIncrease ) ? 1 : -1 );
3202 m_tuningPattern->SetMaxAmplitude( placer->MeanderSettings().m_maxAmplitude );
3203 meanderSettings.m_maxAmplitude = placer->MeanderSettings().m_maxAmplitude;
3204 PCB_TUNING_PATTERN::StoreDefaults( bds, mode, meanderSettings, false );
3205
3206 updateTuningPattern();
3207 }
3208 }
3209 else
3210 {
3211 m_frame->ShowInfoBarWarning( _( "Select a track to tune first." ) );
3212 }
3213 }
3214 else if( evt->IsAction( &PCB_ACTIONS::properties )
3215 || evt->IsAction( &PCB_ACTIONS::lengthTunerSettings ) )
3216 {
3217 DRC_CONSTRAINT constraint;
3218
3219 if( m_tuningPattern )
3220 {
3221 if( !m_tuningPattern->GetItems().empty() )
3222 {
3223 BOARD_ITEM* startItem = *m_tuningPattern->GetBoardItems().begin();
3224
3225 constraint = drcEngine->EvalRules( LENGTH_CONSTRAINT, startItem, nullptr,
3226 startItem->GetLayer() );
3227 }
3228 }
3229
3230 const PNS::MEANDER_SETTINGS priorSettings = meanderSettings;
3231 const bool ruleDriven = m_tuningPattern && m_tuningPattern->HasRuleTarget();
3232
3233 // Show the rule's target rather than a stored manual one unless the rules are overridden
3234 PNS::MEANDER_SETTINGS dlgSettings = meanderSettings;
3235
3236 if( ruleDriven && !dlgSettings.m_overrideCustomRules )
3237 {
3238 const PNS::MEANDER_SETTINGS& ruleSettings = m_tuningPattern->GetSettings();
3239
3240 dlgSettings.m_targetLength = ruleSettings.m_targetLength;
3241 dlgSettings.m_targetLengthDelay = ruleSettings.m_targetLengthDelay;
3242 dlgSettings.m_targetSkew = ruleSettings.m_targetSkew;
3243 dlgSettings.m_targetSkewDelay = ruleSettings.m_targetSkewDelay;
3244 }
3245
3246 DIALOG_TUNING_PATTERN_PROPERTIES dlg( m_frame, dlgSettings, routerMode, constraint );
3247
3248 if( dlg.ShowModal() == wxID_OK )
3249 {
3250 meanderSettings = dlgSettings;
3251
3252 // The dialog echoes a rule's value into the targets unless the user overrode the rule
3253 const bool manualTargets = !ruleDriven || meanderSettings.m_overrideCustomRules;
3254
3255 if( !manualTargets )
3256 {
3257 meanderSettings.m_targetLength = priorSettings.m_targetLength;
3258 meanderSettings.m_targetLengthDelay = priorSettings.m_targetLengthDelay;
3259 meanderSettings.m_targetSkew = priorSettings.m_targetSkew;
3260 meanderSettings.m_targetSkewDelay = priorSettings.m_targetSkewDelay;
3261 }
3262
3263 PCB_TUNING_PATTERN::StoreDefaults( bds, mode, meanderSettings, manualTargets );
3264
3265 if( m_tuningPattern )
3266 applyCommonSettings( m_tuningPattern );
3267
3268 updateTuningPattern();
3269 }
3270 }
3271 // TODO: It'd be nice to be able to say "don't allow any non-trivial editing actions",
3272 // but we don't at present have that, so we just knock out some of the egregious ones.
3273 else if( ZONE_FILLER_TOOL::IsZoneFillAction( evt ) )
3274 {
3275 wxBell();
3276 }
3277 else
3278 {
3279 evt->SetPassEvent();
3280 }
3281
3282 controls->CaptureCursor( m_tuningPattern != nullptr );
3283 controls->SetAutoPan( m_tuningPattern != nullptr );
3284 }
3285
3286 controls->CaptureCursor( false );
3287 controls->SetAutoPan( false );
3288 controls->ForceCursorPosition( false );
3289 controls->ShowCursor( false );
3290 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
3291
3292 m_preview.FreeItems();
3293 m_view->Remove( &m_preview );
3294
3295 m_frame->GetCanvas()->Refresh();
3296
3297 if( m_tuningPattern )
3298 selectionTool->AddItemToSel( m_tuningPattern );
3299
3300 return 0;
3301}
3302
3303
3305{
3307 {
3309 .Map( LENGTH_TUNING_MODE::SINGLE, _HKI( "Single track" ) )
3310 .Map( LENGTH_TUNING_MODE::DIFF_PAIR, _HKI( "Differential pair" ) )
3311 .Map( LENGTH_TUNING_MODE::DIFF_PAIR_SKEW, _HKI( "Diff pair skew" ) );
3312
3314 .Map( PNS::MEANDER_SIDE_LEFT, _HKI( "Left" ) )
3315 .Map( PNS::MEANDER_SIDE_RIGHT, _HKI( "Right" ) )
3316 .Map( PNS::MEANDER_SIDE_DEFAULT, _HKI( "Default" ) );
3317
3324
3326
3327 if( layerEnum.Choices().GetCount() == 0 )
3328 {
3329 layerEnum.Undefined( UNDEFINED_LAYER );
3330
3331 for( PCB_LAYER_ID layer : LSET::AllLayersMask() )
3332 layerEnum.Map( layer, LSET::Name( layer ) );
3333 }
3334
3335 propMgr.ReplaceProperty( TYPE_HASH( BOARD_ITEM ), _HKI( "Layer" ),
3338 .SetChoices( layerEnum.Choices() );
3339
3340 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, int>( _HKI( "Width" ),
3342
3345
3346 const wxString groupTechLayers = _HKI( "Technical Layers" );
3347
3348 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, bool>( _HKI( "Soldermask" ),
3350 groupTechLayers );
3351
3352 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, std::optional<int>>( _HKI( "Soldermask Margin Override" ),
3355 groupTechLayers );
3356
3357 const wxString groupTab = _HKI( "Pattern Properties" );
3358
3359 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, int>( _HKI( "End X" ),
3362 groupTab );
3363
3364 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, int>( _HKI( "End Y" ),
3367 groupTab );
3368
3371 groupTab );
3372
3373 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, int>( _HKI( "Min Amplitude" ),
3376 groupTab );
3377
3378 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, int>( _HKI( "Max Amplitude" ),
3381 groupTab );
3382
3385 groupTab );
3386
3387 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, int>( _HKI( "Min Spacing" ),
3390 groupTab );
3391
3392 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, int>( _HKI( "Corner Radius %" ),
3395 groupTab );
3396
3397 auto isSkew =
3398 []( INSPECTABLE* aItem ) -> bool
3399 {
3400 if( PCB_TUNING_PATTERN* pattern = dynamic_cast<PCB_TUNING_PATTERN*>( aItem ) )
3401 return pattern->GetTuningMode() == DIFF_PAIR_SKEW;
3402
3403 return false;
3404 };
3405
3406 auto isTimeDomain =
3407 []( INSPECTABLE* aItem ) -> bool
3408 {
3409 if( PCB_TUNING_PATTERN* pattern = dynamic_cast<PCB_TUNING_PATTERN*>( aItem ) )
3410 return pattern->GetSettings().m_isTimeDomain;
3411
3412 return false;
3413 };
3414
3415 auto isLengthIsSpaceDomain =
3416 [&]( INSPECTABLE* aItem ) -> bool
3417 {
3418 return !isSkew( aItem ) && !isTimeDomain( aItem );
3419 };
3420
3421 auto isLengthIsTimeDomain =
3422 [&]( INSPECTABLE* aItem ) -> bool
3423 {
3424 return !isSkew( aItem ) && isTimeDomain( aItem );
3425 };
3426
3427 auto isSkewIsSpaceDomain =
3428 [&]( INSPECTABLE* aItem ) -> bool
3429 {
3430 return isSkew( aItem ) && !isTimeDomain( aItem );
3431 };
3432
3433 auto isSkewIsTimeDomain =
3434 [&]( INSPECTABLE* aItem ) -> bool
3435 {
3436 return isSkew( aItem ) && isTimeDomain( aItem );
3437 };
3438
3439 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, std::optional<int>>( _HKI( "Target Length" ),
3442 groupTab )
3443 .SetAvailableFunc( isLengthIsSpaceDomain );
3444
3445 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, std::optional<int>>( _HKI( "Target Delay" ),
3448 groupTab )
3449 .SetAvailableFunc( isLengthIsTimeDomain );
3450
3451 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, int>( _HKI( "Target Skew" ),
3454 groupTab )
3455 .SetAvailableFunc( isSkewIsSpaceDomain );
3456
3457 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, int>( _HKI( "Target Skew Delay" ),
3460 groupTab )
3461 .SetAvailableFunc( isSkewIsTimeDomain );
3462
3463 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, bool>( _HKI( "Override Custom Rules" ),
3465 groupTab );
3466
3467 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, bool>( _HKI( "Single-sided" ),
3469 groupTab );
3470
3471 propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, bool>( _HKI( "Rounded" ),
3473 groupTab );
3474 }
3476
3479
3481
3482// Also register under the 7.99 name
3483template <typename T>
3485{
3487 {
3488 GENERATORS_MGR::Instance().Register( wxS( "meanders" ), T::DISPLAY_NAME,
3489 []()
3490 {
3491 return new T;
3492 } );
3493 }
3494};
3495
int red
int green
KICOMMON_API types::KiCadObjectType ToProtoEnum(KICAD_T aValue)
KICOMMON_API KICAD_T FromProtoEnum(types::KiCadObjectType aValue)
Definition api_enums.cpp:55
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:128
constexpr int ARC_LOW_DEF
Definition base_units.h:143
@ NORMAL
Inactive layers are shown normally (no high-contrast mode)
BOX2< VECTOR2I > BOX2I
Definition box2.h:914
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:982
static TOOL_ACTION undo
Definition actions.h:71
static TOOL_ACTION cursorClick
Definition actions.h:176
static TOOL_ACTION selectionClear
Clear the current selection.
Definition actions.h:220
static TOOL_ACTION refreshPreview
Definition actions.h:155
void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Execute the changes.
void Revert() override
Revert the commit by restoring the modified items state.
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 NETINFO_ITEM 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:85
BOARD_ITEM(BOARD_ITEM *aParent, KICAD_T idtype, PCB_LAYER_ID aLayer=F_Cu)
Definition board_item.h:87
void SetUuidDirect(const KIID &aUuid)
Raw UUID assignment.
bool IsLocked() const override
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
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:411
const NETINFO_LIST & GetNetInfo() const
Definition board.h:1217
NETINFO_ITEM * DpCoupledNet(const NETINFO_ITEM *aNet)
Definition board.cpp:2864
LENGTH_DELAY_CALCULATION * GetLengthCalculation() const
Returns the track length calculator.
Definition board.h:1679
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:3470
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition board.cpp:2797
EDA_UNITS GetUserUnits()
Definition board.h:1032
const FOOTPRINTS & Footprints() const
Definition board.h:465
const TRACKS & Tracks() const
Definition board.h:463
PROJECT * GetProject() const
Definition board.h:774
BOARD_DESIGN_SETTINGS & GetDesignSettings()
Definition board.cpp:1290
constexpr void SetMaximum()
Definition box2.h:77
int GetCount() const
Return the number of objects in the list.
Definition collector.h:79
int m_Threshold
Definition collector.h:234
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:162
SEVERITY GetSeverity() const
Definition drc_rule.h:181
const MINOPTMAX< int > & GetValue() const
Definition drc_rule.h:129
bool GetOption(OPTIONS option) const
Definition drc_rule.h:193
bool IsNull() const
Definition drc_rule.h:122
Design Rule Checker object that performs all the DRC tests.
Definition drc_engine.h:129
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:106
std::unordered_set< EDA_ITEM * > m_items
Definition eda_group.h:105
std::unordered_set< EDA_ITEM * > & GetItems()
Definition eda_group.h:78
void AddItem(EDA_ITEM *aItem)
Add item to group.
Definition eda_group.cpp:58
std::unordered_set< EDA_ITEM * > m_deserializedItems
Definition eda_group.h:109
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:98
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:158
const KIID m_Uuid
Definition eda_item.h:599
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition eda_item.h:160
bool IsSelected() const
Definition eda_item.h:134
bool HasFlag(EDA_ITEM_FLAGS aFlag) const
Definition eda_item.h:168
EDA_ITEM_FLAGS GetFlags() const
Definition eda_item.h:167
EDA_ITEM(EDA_ITEM *parent, KICAD_T idType, bool isSCH_ITEM=false, bool isBOARD_ITEM=false)
Definition eda_item.cpp:84
bool IsNew() const
Definition eda_item.h:131
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:69
ENUM_MAP & Map(T aValue, const wxString &aName)
Definition property.h:798
static ENUM_MAP< T > & Instance()
Definition property.h:792
ENUM_MAP & Undefined(T aValue)
Definition property.h:805
wxPGChoices & Choices()
Definition property.h:843
static const TOOL_EVENT SelectedItemsModified
Selected item had a property changed (except movement)
Definition actions.h:353
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()
bool ItemCreatedBySession(BOARD_ITEM *aItem) const
Returns true if the given board item was created by the router interface during the current generator...
void SetRouterCommit(BOARD_COMMIT *aCommit)
Use the generator's commit for changes made to existing board items by PNS.
Handle actions specific to filling copper zones.
Class that other classes need to inherit from, in order to be inspectable.
Definition inspectable.h:39
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:144
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:243
static const METRICS & Default()
Definition font.cpp:49
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:309
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 &aEnd, 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:68
GAL * GetGAL() const
Return the GAL this view is using to draw graphical primitives.
Definition view.h:224
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:791
void Hide(VIEW_ITEM *aItem, bool aHide=true, bool aHideOverlay=false)
Temporarily hide the item in the view (e.g.
Definition view.cpp:2576
Definition kiid.h:46
bool CanCalculateLengthForDelay(const TUNING_PROFILE_GEOMETRY_CONTEXT &aCtx) const
Returns true if a track length can be derived from a delay in a specific geometry context.
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:41
bool HasMax() const
Definition minoptmax.h:38
void SetOpt(T v)
Definition minoptmax.h:43
bool HasMin() const
Definition minoptmax.h:37
void SetMax(T v)
Definition minoptmax.h:42
T Max() const
Definition minoptmax.h:30
T Opt() const
Definition minoptmax.h:31
bool HasOpt() const
Definition minoptmax.h:39
A collection of nets and the parameters used to route or test these nets.
Definition netclass.h:44
const wxString GetHumanReadableName() const
Gets the consolidated name of this netclass (which may be an aggregate).
Definition netclass.cpp:346
wxString GetTuningProfile() const
Definition netclass.h:271
bool HasTuningProfile() const
Definition netclass.h:269
Handle the data for a net.
Definition netinfo.h:50
const wxString & GetNetChain() const
Definition netinfo.h:122
const wxString & GetNetname() const
Definition netinfo.h:110
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 KIGFX::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
void SetPosition(const VECTOR2I &aPos) override
VECTOR2I GetPosition() const override
virtual const STRING_ANY_MAP GetProperties() const
std::unordered_set< BOARD_ITEM * > GetBoardItems() const
void SetLocked(bool aLocked) override
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
const VECTOR2I & GetStart() const
Definition pcb_track.h:98
const VECTOR2I & GetEnd() const
Definition pcb_track.h:95
virtual void SetWidth(int aWidth)
Definition pcb_track.h:91
virtual int GetWidth() const
Definition pcb_track.h:92
void SetTargetSkew(int aValue)
bool initBaseLines(PNS::ROUTER *aRouter, int aPNSLayer, BOARD *aBoard)
const STRING_ANY_MAP GetProperties() const override
void ApplyToolSettings(const PNS::MEANDER_SETTINGS &aToolSettings)
Adopt the tuning tool's working settings.
void SetLocalSolderMaskMargin(std::optional< int > aMargin)
bool HasMeanders() const
True when any member track leaves the baseline between its end caps, or there is no baseline.
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)
static wxString StatusMessage(PNS::MEANDER_PLACER_BASE::TUNING_STATUS aStatus)
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)
static PNS::MEANDER_SETTINGS & DefaultSettings(BOARD_DESIGN_SETTINGS &aBds, LENGTH_TUNING_MODE aMode)
The per-mode tool defaults stored on the board.
static void StoreDefaults(BOARD_DESIGN_SETTINGS &aBds, LENGTH_TUNING_MODE aMode, const PNS::MEANDER_SETTINGS &aSettings, bool aIncludeTargets)
Remember the tool's settings as the per-mode default for the next activation.
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
void Serialize(google::protobuf::Any &aContainer) const override
Serializes this object to the given Any message.
bool resetToBaseline(GENERATOR_TOOL *aTool, int aPNSLayer, SHAPE_LINE_CHAIN &aBaseLine, bool aPrimary)
bool Deserialize(const google::protobuf::Any &aContainer) override
Deserializes the given protobuf message into this object.
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)
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:272
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:149
SHAPE_LINE_CHAIN & Line()
Modifiable accessor to the underlying shape.
Definition pns_line.h:148
void SetWidth(int aWidth)
Set line width.
Definition pns_line.h:162
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
int m_minAmplitude
Minimum meandering amplitude.
void SetTargetLength(long long int aOpt)
bool m_isTimeDomain
Calculate tuning in the time domain.
int m_cornerRadiusPercentage
Rounding percentage (0 - 100).
bool m_singleSided
Place meanders on one side.
void SetTargetLengthDelay(long long int aOpt)
MINOPTMAX< long long int > m_targetLength
Desired length of the tuned line/diff pair (this is in nm, so allow more than board width).
NETCLASS * m_netClass
The net class this meander pattern belongs to.
void SetTargetSignalLengthDelay(long long int aOpt)
void SetTargetSkew(int aOpt)
void SetTargetSignalLength(long long int aOpt)
MINOPTMAX< int > m_targetSkew
Target skew value for diff pair de-skewing.
MEANDER_STYLE m_cornerStyle
Type of corners for the meandered line.
MINOPTMAX< int > m_targetSkewDelay
Target skew value for diff pair de-skewing.
int m_maxAmplitude
Maximum meandering amplitude.
MINOPTMAX< long long int > m_targetLengthDelay
Desired propagation delay of the tuned line.
void SetTargetSkewDelay(int aOpt)
int m_spacing
Meandering period/spacing (see dialog picture for explanation).
Keep the router "world" - i.e.
Definition pns_node.h:244
NODE * Branch()
Create a lightweight copy (called branch) of self that tracks the changes (added/removed items) wrs t...
Definition pns_node.cpp:163
bool Add(std::unique_ptr< SEGMENT > aSegment, bool aAllowRedundant=false)
Add an item to the current node.
Definition pns_node.cpp:753
std::set< OBSTACLE > OBSTACLES
Definition pns_node.h:256
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:273
ITEM * FindItemByParent(const BOARD_ITEM *aParent)
void Remove(ARC *aArc)
Remove an item from this branch.
Definition pns_node.cpp:997
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:263
ROUTER_IFACE * GetInterface() const
Definition pns_router.h:265
void CommitRouting()
void SyncWorld()
RULE_RESOLVER * GetRuleResolver() const
Definition pns_router.h:227
bool RoutingInProgress() const
bool StartRouting(const VECTOR2I &aP, ITEM *aItem, int aLayer)
SIZES_SETTINGS & Sizes()
Definition pns_router.h:258
bool FixRoute(const VECTOR2I &aP, ITEM *aItem, bool aForceFinish, bool aForceCommit)
NODE * GetWorld() const
Definition pns_router.h:211
bool Move(const VECTOR2I &aP, ITEM *aItem)
virtual int Clearance(const ITEM *aA, const ITEM *aB, bool aUseClearanceEpsilon=true)=0
virtual bool QueryConstraint(CONSTRAINT_TYPE aType, const ITEM *aItemA, const ITEM *aItemB, int aLayer, CONSTRAINT *aConstraint)=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:201
PROPERTY_BASE & SetAvailableFunc(std::function< bool(INSPECTABLE *)> aFunc)
Set a callback function to determine whether an object provides this property.
Definition property.h:275
virtual void SetChoices(const wxPGChoices &aChoices)
Set the possible values for for the property.
Definition property.h:250
Provide class metadata.
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:710
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.
int SegmentCount() const
Return the number of segments in this line chain.
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
Return the instance of KIGFX::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:484
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.
T * GetTool()
Return the tool of given type or nullptr if there is no such tool registered.
VECTOR2D GetMousePosition() 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:43
@ NET_CHAIN_LENGTH_CONSTRAINT
@ LENGTH_CONSTRAINT
@ SKEW_CONSTRAINT
#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
SCOPED_SET_RESET< EE_GRAPHIC_TOOL::MODE > SCOPED_DRAW_MODE
a few functions useful in geometry calculations.
@ LAYER_UI_START
Definition layer_ids.h:375
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
Initial side the meander is placed on.
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:68
@ PNS_MODE_TUNE_DIFF_PAIR
Definition pns_router.h:72
@ PNS_MODE_TUNE_SINGLE
Definition pns_router.h:71
@ PNS_MODE_TUNE_DIFF_PAIR_SKEW
Definition pns_router.h:73
KICOMMON_API void PackCustomProperties(google::protobuf::RepeatedPtrField< types::CustomProperty > *aOutput, const EDA_ITEM &aItem)
KICOMMON_API VECTOR2I UnpackVector2(const types::Vector2 &aInput, const EDA_IU_SCALE &aScale)
KICOMMON_API void PackVector2(types::Vector2 &aOutput, const VECTOR2I &aInput, const EDA_IU_SCALE &aScale)
KICOMMON_API void UnpackCustomProperties(const google::protobuf::RepeatedPtrField< types::CustomProperty > &aInput, EDA_ITEM &aItem)
std::tuple< double, double > BoardChainBridging(const BOARD *aBoard, const wxString &aNetChain, const double aDefaultBridgeUnitDelay)
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)
static REGISTER_LEGACY_TUNING_PATTERN< PCB_TUNING_PATTERN > registerMeToo
LENGTH_TUNING_MODE
@ DIFF_PAIR
@ DIFF_PAIR_SKEW
#define TYPE_HASH(x)
Macro to generate unique identifier for a type.
Definition property.h:74
#define NO_SETTER(owner, type)
Macro to define read-only fields (no setter method available)
Definition property.h:904
#define ENUM_TO_WXANY(type)
Definition property.h:899
@ 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)
Helper macro to map type hashes to names.
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
A data structure to contain basic geometry data which can affect signal propagation calculations.
int64_t DiffPairCouplingGap
The gap between coupled tracks.
const NETCLASS * NetClass
The net class this track belongs to.
int64_t Width
The width (in internal units) of the track.
bool IsDiffPairCoupled
Whether this track or via is a member of a coupled differential pair.
PCB_LAYER_ID Layer
The layer this track is on.
Represents a single line in the tuning profile configuration grid.
const SHAPE_LINE_CHAIN chain
VECTOR2I end
int clearance
@ 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:178
@ NOT_USED
the 3d code uses this value
Definition typeinfo.h:71
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:90
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:88
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
VECTOR2< double > VECTOR2D
Definition vector2d.h:707