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