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