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