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