You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2678 lines
92 KiB

2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 years ago
8 months ago
5 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2023 Alex Shvartzkop <dudesuchamazing@gmail.com>
  5. * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, you may find one here:
  19. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  20. * or you may search the http://www.gnu.org website for the version 2 license,
  21. * or you may write to the Free Software Foundation, Inc.,
  22. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  23. */
  24. #include <pcb_generator.h>
  25. #include <generators_mgr.h>
  26. #include <functional>
  27. #include <optional>
  28. #include <magic_enum.hpp>
  29. #include <wx/debug.h>
  30. #include <wx/log.h>
  31. #include <gal/graphics_abstraction_layer.h>
  32. #include <geometry/shape_circle.h>
  33. #include <geometry/geometry_utils.h>
  34. #include <kiplatform/ui.h>
  35. #include <dialogs/dialog_unit_entry.h>
  36. #include <collectors.h>
  37. #include <scoped_set_reset.h>
  38. #include <core/mirror.h>
  39. #include <string_utils.h>
  40. #include <board.h>
  41. #include <board_design_settings.h>
  42. #include <drc/drc_engine.h>
  43. #include <pcb_track.h>
  44. #include <pcb_shape.h>
  45. #include <pcb_group.h>
  46. #include <tool/edit_points.h>
  47. #include <tool/tool_manager.h>
  48. #include <tools/drawing_tool.h>
  49. #include <tools/generator_tool.h>
  50. #include <tools/pcb_picker_tool.h>
  51. #include <tools/pcb_selection_tool.h>
  52. #include <tools/zone_filler_tool.h>
  53. #include <preview_items/draw_context.h>
  54. #include <preview_items/preview_utils.h>
  55. #include <view/view.h>
  56. #include <view/view_controls.h>
  57. #include <router/pns_dp_meander_placer.h>
  58. #include <router/pns_meander_placer_base.h>
  59. #include <router/pns_meander.h>
  60. #include <router/pns_kicad_iface.h>
  61. #include <router/pns_segment.h>
  62. #include <router/pns_arc.h>
  63. #include <router/pns_solid.h>
  64. #include <router/pns_topology.h>
  65. #include <router/router_preview_item.h>
  66. #include <dialogs/dialog_tuning_pattern_properties.h>
  67. #include <generators/pcb_tuning_pattern.h>
  68. TUNING_STATUS_VIEW_ITEM::TUNING_STATUS_VIEW_ITEM( PCB_BASE_EDIT_FRAME* aFrame ) :
  69. EDA_ITEM( NOT_USED ), // Never added to anything - just a preview
  70. m_frame( aFrame ),
  71. m_min( 0.0 ),
  72. m_max( 0.0 ),
  73. m_current( 0.0 ),
  74. m_isTimeDomain( false )
  75. { }
  76. wxString TUNING_STATUS_VIEW_ITEM::GetClass() const
  77. {
  78. return wxT( "TUNING_STATUS" );
  79. }
  80. #if defined(DEBUG)
  81. void TUNING_STATUS_VIEW_ITEM::Show( int nestLevel, std::ostream& os ) const {}
  82. #endif
  83. VECTOR2I TUNING_STATUS_VIEW_ITEM::GetPosition() const { return m_pos; }
  84. void TUNING_STATUS_VIEW_ITEM::SetPosition( const VECTOR2I& aPos ) { m_pos = aPos; };
  85. void TUNING_STATUS_VIEW_ITEM::SetMinMax( const double aMin, const double aMax )
  86. {
  87. const EDA_DATA_TYPE unitType = m_isTimeDomain ? EDA_DATA_TYPE::TIME : EDA_DATA_TYPE::DISTANCE;
  88. m_min = aMin;
  89. m_minText = m_frame->MessageTextFromValue( m_min, false, unitType );
  90. m_max = aMax;
  91. m_maxText = m_frame->MessageTextFromValue( m_max, false, unitType );
  92. }
  93. void TUNING_STATUS_VIEW_ITEM::ClearMinMax()
  94. {
  95. m_min = 0.0;
  96. m_minText = wxT( "---" );
  97. m_max = std::numeric_limits<double>::max();
  98. m_maxText = wxT( "---" );
  99. }
  100. void TUNING_STATUS_VIEW_ITEM::SetCurrent( const double aCurrent, const wxString& aLabel )
  101. {
  102. const EDA_DATA_TYPE unitType = m_isTimeDomain ? EDA_DATA_TYPE::TIME : EDA_DATA_TYPE::DISTANCE;
  103. m_current = aCurrent;
  104. m_currentText = m_frame->MessageTextFromValue( aCurrent, true, unitType );
  105. m_currentLabel = aLabel;
  106. }
  107. void TUNING_STATUS_VIEW_ITEM::SetIsTimeDomain( const bool aIsTimeDomain )
  108. {
  109. m_isTimeDomain = aIsTimeDomain;
  110. }
  111. const BOX2I TUNING_STATUS_VIEW_ITEM::ViewBBox() const
  112. {
  113. BOX2I tmp;
  114. // this is an edit-time artefact; no reason to try and be smart with the bounding box
  115. // (besides, we can't tell the text extents without a view to know what the scale is)
  116. tmp.SetMaximum();
  117. return tmp;
  118. }
  119. std::vector<int> TUNING_STATUS_VIEW_ITEM::ViewGetLayers() const
  120. {
  121. return { LAYER_UI_START, LAYER_UI_START + 1 };
  122. }
  123. void TUNING_STATUS_VIEW_ITEM::ViewDraw( int aLayer, KIGFX::VIEW* aView ) const
  124. {
  125. KIGFX::GAL* gal = aView->GetGAL();
  126. bool viewFlipped = gal->IsFlippedX();
  127. bool drawingDropShadows = ( aLayer == LAYER_UI_START );
  128. gal->Save();
  129. gal->Scale( { 1., 1. } );
  130. KIGFX::PREVIEW::TEXT_DIMS headerDims = KIGFX::PREVIEW::GetConstantGlyphHeight( gal, -2 );
  131. KIGFX::PREVIEW::TEXT_DIMS textDims = KIGFX::PREVIEW::GetConstantGlyphHeight( gal, -1 );
  132. KIFONT::FONT* font = KIFONT::FONT::GetFont();
  133. const KIFONT::METRICS& fontMetrics = KIFONT::METRICS::Default();
  134. TEXT_ATTRIBUTES textAttrs;
  135. int glyphWidth = textDims.GlyphSize.x;
  136. VECTOR2I margin( KiROUND( glyphWidth * 0.4 ), KiROUND( glyphWidth ) );
  137. VECTOR2I size( glyphWidth * 25 + margin.x * 2, headerDims.GlyphSize.y + textDims.GlyphSize.y );
  138. VECTOR2I offset( margin.x * 2, -( size.y + margin.y * 2 ) );
  139. if( drawingDropShadows )
  140. {
  141. gal->SetIsFill( true );
  142. gal->SetIsStroke( true );
  143. gal->SetLineWidth( gal->GetScreenWorldMatrix().GetScale().x * 2 );
  144. gal->SetStrokeColor( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNTEXT ) );
  145. KIGFX::COLOR4D bgColor( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
  146. gal->SetFillColor( bgColor.WithAlpha( 0.9 ) );
  147. gal->DrawRectangle( GetPosition() + offset - margin,
  148. GetPosition() + offset + size + margin );
  149. gal->Restore();
  150. return;
  151. }
  152. COLOR4D bg = wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE );
  153. COLOR4D normal = wxSystemSettings::GetColour( wxSYS_COLOUR_BTNTEXT );
  154. COLOR4D red;
  155. // Choose a red with reasonable contrasting with the background
  156. double bg_h, bg_s, bg_l;
  157. bg.ToHSL( bg_h, bg_s, bg_l );
  158. red.FromHSL( 0, 1.0, bg_l < 0.5 ? 0.7 : 0.3 );
  159. if( viewFlipped )
  160. textAttrs.m_Halign = GR_TEXT_H_ALIGN_RIGHT;
  161. else
  162. textAttrs.m_Halign = GR_TEXT_H_ALIGN_LEFT;
  163. gal->SetIsFill( false );
  164. gal->SetIsStroke( true );
  165. gal->SetStrokeColor( normal );
  166. textAttrs.m_Halign = GR_TEXT_H_ALIGN_LEFT;
  167. // Prevent text flipping when view is flipped
  168. if( gal->IsFlippedX() )
  169. {
  170. textAttrs.m_Mirrored = true;
  171. textAttrs.m_Halign = GR_TEXT_H_ALIGN_RIGHT;
  172. }
  173. textAttrs.m_Size = headerDims.GlyphSize;
  174. textAttrs.m_StrokeWidth = headerDims.StrokeWidth;
  175. VECTOR2I textPos = GetPosition() + offset;
  176. font->Draw( gal, m_currentLabel, textPos, textAttrs, KIFONT::METRICS::Default() );
  177. textPos.x += glyphWidth * 11 + margin.x;
  178. font->Draw( gal, _( "min" ), textPos, textAttrs, fontMetrics );
  179. textPos.x += glyphWidth * 7 + margin.x;
  180. font->Draw( gal, _( "max" ), textPos, textAttrs, fontMetrics );
  181. textAttrs.m_Size = textDims.GlyphSize;
  182. textAttrs.m_StrokeWidth = textDims.StrokeWidth;
  183. textPos = GetPosition() + offset;
  184. textPos.y += KiROUND( headerDims.LinePitch * 1.3 );
  185. font->Draw( gal, m_currentText, textPos, textAttrs, KIFONT::METRICS::Default() );
  186. textPos.x += glyphWidth * 11 + margin.x;
  187. gal->SetStrokeColor( m_current < m_min ? red : normal );
  188. font->Draw( gal, m_minText, textPos, textAttrs, fontMetrics );
  189. textPos.x += glyphWidth * 7 + margin.x;
  190. gal->SetStrokeColor( m_current > m_max ? red : normal );
  191. font->Draw( gal, m_maxText, textPos, textAttrs, fontMetrics );
  192. gal->Restore();
  193. }
  194. static LENGTH_TUNING_MODE tuningFromString( const std::string& aStr )
  195. {
  196. if( aStr == "single" )
  197. return LENGTH_TUNING_MODE::SINGLE;
  198. else if( aStr == "diff_pair" )
  199. return LENGTH_TUNING_MODE::DIFF_PAIR;
  200. else if( aStr == "diff_pair_skew" )
  201. return LENGTH_TUNING_MODE::DIFF_PAIR_SKEW;
  202. else
  203. {
  204. wxFAIL_MSG( wxS( "Unknown length tuning token" ) );
  205. return LENGTH_TUNING_MODE::SINGLE;
  206. }
  207. }
  208. static std::string tuningToString( const LENGTH_TUNING_MODE aTuning )
  209. {
  210. switch( aTuning )
  211. {
  212. case LENGTH_TUNING_MODE::SINGLE: return "single";
  213. case LENGTH_TUNING_MODE::DIFF_PAIR: return "diff_pair";
  214. case LENGTH_TUNING_MODE::DIFF_PAIR_SKEW: return "diff_pair_skew";
  215. default: wxFAIL; return "";
  216. }
  217. }
  218. static LENGTH_TUNING_MODE fromPNSMode( PNS::ROUTER_MODE aRouterMode )
  219. {
  220. switch( aRouterMode )
  221. {
  222. case PNS::PNS_MODE_TUNE_SINGLE: return LENGTH_TUNING_MODE::SINGLE;
  223. case PNS::PNS_MODE_TUNE_DIFF_PAIR: return LENGTH_TUNING_MODE::DIFF_PAIR;
  224. case PNS::PNS_MODE_TUNE_DIFF_PAIR_SKEW: return LENGTH_TUNING_MODE::DIFF_PAIR_SKEW;
  225. default: return LENGTH_TUNING_MODE::SINGLE;
  226. }
  227. }
  228. static PNS::MEANDER_SIDE sideFromString( const std::string& aStr )
  229. {
  230. if( aStr == "default" )
  231. return PNS::MEANDER_SIDE_DEFAULT;
  232. else if( aStr == "left" )
  233. return PNS::MEANDER_SIDE_LEFT;
  234. else if( aStr == "right" )
  235. return PNS::MEANDER_SIDE_RIGHT;
  236. else
  237. {
  238. wxFAIL_MSG( wxS( "Unknown length-tuning side token" ) );
  239. return PNS::MEANDER_SIDE_DEFAULT;
  240. }
  241. }
  242. static std::string statusToString( const PNS::MEANDER_PLACER_BASE::TUNING_STATUS aStatus )
  243. {
  244. switch( aStatus )
  245. {
  246. case PNS::MEANDER_PLACER_BASE::TOO_LONG: return "too_long";
  247. case PNS::MEANDER_PLACER_BASE::TOO_SHORT: return "too_short";
  248. case PNS::MEANDER_PLACER_BASE::TUNED: return "tuned";
  249. default: wxFAIL; return "";
  250. }
  251. }
  252. static PNS::MEANDER_PLACER_BASE::TUNING_STATUS statusFromString( const std::string& aStr )
  253. {
  254. if( aStr == "too_long" )
  255. return PNS::MEANDER_PLACER_BASE::TOO_LONG;
  256. else if( aStr == "too_short" )
  257. return PNS::MEANDER_PLACER_BASE::TOO_SHORT;
  258. else if( aStr == "tuned" )
  259. return PNS::MEANDER_PLACER_BASE::TUNED;
  260. else
  261. {
  262. wxFAIL_MSG( wxS( "Unknown tuning status token" ) );
  263. return PNS::MEANDER_PLACER_BASE::TUNED;
  264. }
  265. }
  266. static std::string sideToString( const PNS::MEANDER_SIDE aValue )
  267. {
  268. switch( aValue )
  269. {
  270. case PNS::MEANDER_SIDE_DEFAULT: return "default";
  271. case PNS::MEANDER_SIDE_LEFT: return "left";
  272. case PNS::MEANDER_SIDE_RIGHT: return "right";
  273. default: wxFAIL; return "";
  274. }
  275. }
  276. PCB_TUNING_PATTERN::PCB_TUNING_PATTERN( BOARD_ITEM* aParent, PCB_LAYER_ID aLayer,
  277. LENGTH_TUNING_MODE aMode ) :
  278. PCB_GENERATOR( aParent, aLayer ),
  279. m_trackWidth( 0 ),
  280. m_diffPairGap( 0 ),
  281. m_tuningMode( aMode ),
  282. m_tuningStatus( PNS::MEANDER_PLACER_BASE::TUNING_STATUS::TUNED ),
  283. m_updateSideFromEnd(false)
  284. {
  285. m_generatorType = GENERATOR_TYPE;
  286. m_name = DISPLAY_NAME;
  287. m_end = VECTOR2I( pcbIUScale.mmToIU( 10 ), 0 );
  288. m_settings.m_initialSide = PNS::MEANDER_SIDE_LEFT;
  289. }
  290. static VECTOR2I snapToNearestTrack( const VECTOR2I& aP, BOARD* aBoard, NETINFO_ITEM* aNet,
  291. PCB_TRACK** aNearestTrack )
  292. {
  293. SEG::ecoord minDist_sq = VECTOR2I::ECOORD_MAX;
  294. VECTOR2I closestPt = aP;
  295. for( PCB_TRACK *track : aBoard->Tracks() )
  296. {
  297. if( aNet && track->GetNet() != aNet )
  298. continue;
  299. VECTOR2I nearest;
  300. if( track->Type() == PCB_ARC_T )
  301. {
  302. PCB_ARC* pcbArc = static_cast<PCB_ARC*>( track );
  303. SHAPE_ARC arc( pcbArc->GetStart(), pcbArc->GetMid(), pcbArc->GetEnd(),
  304. pcbArc->GetWidth() );
  305. nearest = arc.NearestPoint( aP );
  306. }
  307. else
  308. {
  309. SEG seg( track->GetStart(), track->GetEnd() );
  310. nearest = seg.NearestPoint( aP );
  311. }
  312. SEG::ecoord dist_sq = ( nearest - aP ).SquaredEuclideanNorm();
  313. if( dist_sq < minDist_sq )
  314. {
  315. minDist_sq = dist_sq;
  316. closestPt = nearest;
  317. if( aNearestTrack )
  318. *aNearestTrack = track;
  319. }
  320. }
  321. return closestPt;
  322. }
  323. bool PCB_TUNING_PATTERN::baselineValid()
  324. {
  325. if( m_tuningMode == DIFF_PAIR || m_tuningMode == DIFF_PAIR_SKEW )
  326. {
  327. return( m_baseLine && m_baseLine->PointCount() > 1
  328. && m_baseLineCoupled && m_baseLineCoupled->PointCount() > 1 );
  329. }
  330. else
  331. {
  332. return( m_baseLine && m_baseLine->PointCount() > 1 );
  333. }
  334. }
  335. PCB_TUNING_PATTERN* PCB_TUNING_PATTERN::CreateNew( GENERATOR_TOOL* aTool,
  336. PCB_BASE_EDIT_FRAME* aFrame,
  337. BOARD_CONNECTED_ITEM* aStartItem,
  338. LENGTH_TUNING_MODE aMode )
  339. {
  340. BOARD* board = aStartItem->GetBoard();
  341. BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings();
  342. DRC_CONSTRAINT constraint;
  343. PCB_LAYER_ID layer = aStartItem->GetLayer();
  344. PCB_TUNING_PATTERN* pattern = new PCB_TUNING_PATTERN( board, layer, aMode );
  345. switch( aMode )
  346. {
  347. case SINGLE: pattern->m_settings = bds.m_SingleTrackMeanderSettings; break;
  348. case DIFF_PAIR: pattern->m_settings = bds.m_DiffPairMeanderSettings; break;
  349. case DIFF_PAIR_SKEW: pattern->m_settings = bds.m_SkewMeanderSettings; break;
  350. }
  351. if( aMode == SINGLE || aMode == DIFF_PAIR )
  352. {
  353. constraint = bds.m_DRCEngine->EvalRules( LENGTH_CONSTRAINT, aStartItem, nullptr, layer );
  354. if( !constraint.IsNull() )
  355. {
  356. if( constraint.GetOption( DRC_CONSTRAINT::OPTIONS::TIME_DOMAIN ) )
  357. {
  358. pattern->m_settings.SetTargetLengthDelay( constraint.GetValue() );
  359. pattern->m_settings.SetTargetLength( MINOPTMAX<int>() );
  360. pattern->m_settings.m_isTimeDomain = true;
  361. }
  362. else
  363. {
  364. pattern->m_settings.SetTargetLengthDelay( MINOPTMAX<int>() );
  365. pattern->m_settings.SetTargetLength( constraint.GetValue() );
  366. pattern->m_settings.m_isTimeDomain = false;
  367. }
  368. }
  369. }
  370. else
  371. {
  372. constraint = bds.m_DRCEngine->EvalRules( SKEW_CONSTRAINT, aStartItem, nullptr, layer );
  373. if( !constraint.IsNull() )
  374. {
  375. if( constraint.GetOption( DRC_CONSTRAINT::OPTIONS::TIME_DOMAIN ) )
  376. {
  377. pattern->m_settings.SetTargetSkew( MINOPTMAX<int>() );
  378. pattern->m_settings.SetTargetLengthDelay( constraint.GetValue() );
  379. pattern->m_settings.m_isTimeDomain = true;
  380. }
  381. else
  382. {
  383. pattern->m_settings.SetTargetSkew( constraint.GetValue() );
  384. pattern->m_settings.SetTargetSkewDelay( MINOPTMAX<int>() );
  385. pattern->m_settings.m_isTimeDomain = true;
  386. }
  387. }
  388. }
  389. pattern->SetFlags( IS_NEW );
  390. pattern->m_settings.m_netClass = aStartItem->GetEffectiveNetClass();
  391. return pattern;
  392. }
  393. void PCB_TUNING_PATTERN::EditStart( GENERATOR_TOOL* aTool, BOARD* aBoard, BOARD_COMMIT* aCommit )
  394. {
  395. if( aCommit )
  396. {
  397. if( IsNew() )
  398. aCommit->Add( this );
  399. else
  400. aCommit->Modify( this );
  401. }
  402. SetFlags( IN_EDIT );
  403. PNS::ROUTER* router = aTool->Router();
  404. int layer = router->GetInterface()->GetPNSLayerFromBoardLayer( GetLayer() );
  405. aTool->ClearRouterChanges();
  406. router->SyncWorld();
  407. PNS::RULE_RESOLVER* resolver = router->GetRuleResolver();
  408. PNS::CONSTRAINT constraint;
  409. if( !baselineValid() )
  410. initBaseLines( router, layer, aBoard );
  411. if( m_updateSideFromEnd )
  412. {
  413. VECTOR2I centerlineOffsetEnd;
  414. if( m_tuningMode == DIFF_PAIR && m_baseLineCoupled
  415. && m_baseLineCoupled->SegmentCount() > 0 )
  416. {
  417. centerlineOffsetEnd =
  418. ( m_baseLineCoupled->CLastPoint() - m_baseLine->CLastPoint() ) / 2;
  419. }
  420. SEG baseEnd = m_baseLine && m_baseLine->SegmentCount() > 0 ? m_baseLine->CSegment( -1 )
  421. : SEG( m_origin, m_end );
  422. baseEnd.A += centerlineOffsetEnd;
  423. baseEnd.B += centerlineOffsetEnd;
  424. if( baseEnd.A != baseEnd.B )
  425. {
  426. int side = baseEnd.Side( m_end );
  427. if( side < 0 )
  428. m_settings.m_initialSide = PNS::MEANDER_SIDE_LEFT;
  429. else
  430. m_settings.m_initialSide = PNS::MEANDER_SIDE_RIGHT;
  431. }
  432. m_updateSideFromEnd = false;
  433. }
  434. PCB_TRACK* track = nullptr;
  435. m_origin = snapToNearestTrack( m_origin, aBoard, nullptr, &track );
  436. wxCHECK( track, /* void */ );
  437. m_settings.m_netClass = track->GetEffectiveNetClass();
  438. if( !m_settings.m_overrideCustomRules )
  439. {
  440. PNS::SEGMENT pnsItem;
  441. NETINFO_ITEM* net = track->GetNet();
  442. pnsItem.SetParent( track );
  443. pnsItem.SetNet( net );
  444. if( m_tuningMode == SINGLE )
  445. {
  446. if( resolver->QueryConstraint( PNS::CONSTRAINT_TYPE::CT_LENGTH,
  447. &pnsItem, nullptr, layer, &constraint ) )
  448. {
  449. if( constraint.m_IsTimeDomain )
  450. {
  451. m_settings.SetTargetLengthDelay( constraint.m_Value );
  452. m_settings.SetTargetLength( MINOPTMAX<int>() );
  453. }
  454. else
  455. {
  456. m_settings.SetTargetLengthDelay( MINOPTMAX<int>() );
  457. m_settings.SetTargetLength( constraint.m_Value );
  458. }
  459. m_settings.m_isTimeDomain = constraint.m_IsTimeDomain;
  460. aTool->GetManager()->PostEvent( EVENTS::SelectedItemsModified );
  461. }
  462. }
  463. else
  464. {
  465. PCB_TRACK* coupledTrack = nullptr;
  466. PNS::SEGMENT pnsCoupledItem;
  467. NETINFO_ITEM* coupledNet = aBoard->DpCoupledNet( net );
  468. if( coupledNet )
  469. snapToNearestTrack( m_origin, aBoard, coupledNet, &coupledTrack );
  470. pnsCoupledItem.SetParent( coupledTrack );
  471. pnsCoupledItem.SetNet( coupledNet );
  472. if( m_tuningMode == DIFF_PAIR )
  473. {
  474. if( resolver->QueryConstraint( PNS::CONSTRAINT_TYPE::CT_LENGTH,
  475. &pnsItem, &pnsCoupledItem, layer, &constraint ) )
  476. {
  477. if( constraint.m_IsTimeDomain )
  478. {
  479. m_settings.SetTargetLengthDelay( constraint.m_Value );
  480. m_settings.SetTargetLength( MINOPTMAX<int>() );
  481. }
  482. else
  483. {
  484. m_settings.SetTargetLengthDelay( MINOPTMAX<int>() );
  485. m_settings.SetTargetLength( constraint.m_Value );
  486. }
  487. m_settings.m_isTimeDomain = constraint.m_IsTimeDomain;
  488. aTool->GetManager()->PostEvent( EVENTS::SelectedItemsModified );
  489. }
  490. }
  491. else
  492. {
  493. if( resolver->QueryConstraint( PNS::CONSTRAINT_TYPE::CT_DIFF_PAIR_SKEW,
  494. &pnsItem, &pnsCoupledItem, layer, &constraint ) )
  495. {
  496. if( constraint.m_IsTimeDomain )
  497. {
  498. m_settings.SetTargetSkewDelay( constraint.m_Value );
  499. m_settings.SetTargetSkew( MINOPTMAX<int>() );
  500. }
  501. else
  502. {
  503. m_settings.SetTargetSkewDelay( MINOPTMAX<int>() );
  504. m_settings.SetTargetSkew( constraint.m_Value );
  505. }
  506. m_settings.m_isTimeDomain = constraint.m_IsTimeDomain;
  507. aTool->GetManager()->PostEvent( EVENTS::SelectedItemsModified );
  508. }
  509. }
  510. }
  511. }
  512. }
  513. static PNS::LINKED_ITEM* pickSegment( PNS::ROUTER* aRouter, const VECTOR2I& aWhere, int aLayer,
  514. VECTOR2I& aPointOut,
  515. const SHAPE_LINE_CHAIN& aBaseline = SHAPE_LINE_CHAIN() )
  516. {
  517. int maxSlopRadius = aRouter->Sizes().Clearance() + aRouter->Sizes().TrackWidth() / 2;
  518. static const int candidateCount = 2;
  519. PNS::LINKED_ITEM* prioritized[candidateCount];
  520. SEG::ecoord dist[candidateCount];
  521. SEG::ecoord distBaseline[candidateCount];
  522. VECTOR2I point[candidateCount];
  523. for( int i = 0; i < candidateCount; i++ )
  524. {
  525. prioritized[i] = nullptr;
  526. dist[i] = VECTOR2I::ECOORD_MAX;
  527. distBaseline[i] = VECTOR2I::ECOORD_MAX;
  528. }
  529. for( int slopRadius : { 0, maxSlopRadius } )
  530. {
  531. PNS::ITEM_SET candidates = aRouter->QueryHoverItems( aWhere, slopRadius );
  532. for( PNS::ITEM* item : candidates.Items() )
  533. {
  534. if( !item->OfKind( PNS::ITEM::SEGMENT_T | PNS::ITEM::ARC_T ) )
  535. continue;
  536. if( !item->IsRoutable() )
  537. continue;
  538. if( !item->Layers().Overlaps( aLayer ) )
  539. continue;
  540. PNS::LINKED_ITEM* linked = static_cast<PNS::LINKED_ITEM*>( item );
  541. if( item->Kind() & PNS::ITEM::ARC_T )
  542. {
  543. PNS::ARC* pnsArc = static_cast<PNS::ARC*>( item );
  544. VECTOR2I nearest = pnsArc->Arc().NearestPoint( aWhere );
  545. SEG::ecoord d0 = ( nearest - aWhere ).SquaredEuclideanNorm();
  546. if( d0 > dist[1] )
  547. continue;
  548. if( aBaseline.PointCount() > 0 )
  549. {
  550. SEG::ecoord dcBaseline;
  551. VECTOR2I target = pnsArc->Arc().GetArcMid();
  552. if( aBaseline.SegmentCount() > 0 )
  553. dcBaseline = aBaseline.SquaredDistance( target );
  554. else
  555. dcBaseline = ( aBaseline.CPoint( 0 ) - target ).SquaredEuclideanNorm();
  556. if( dcBaseline > distBaseline[1] )
  557. continue;
  558. distBaseline[1] = dcBaseline;
  559. }
  560. prioritized[1] = linked;
  561. dist[1] = d0;
  562. point[1] = nearest;
  563. }
  564. else if( item->Kind() & PNS::ITEM::SEGMENT_T )
  565. {
  566. PNS::SEGMENT* segm = static_cast<PNS::SEGMENT*>( item );
  567. VECTOR2I nearest = segm->CLine().NearestPoint( aWhere, false );
  568. SEG::ecoord dd = ( aWhere - nearest ).SquaredEuclideanNorm();
  569. if( dd > dist[1] )
  570. continue;
  571. if( aBaseline.PointCount() > 0 )
  572. {
  573. SEG::ecoord dcBaseline;
  574. VECTOR2I target = segm->Shape( -1 )->Centre();
  575. if( aBaseline.SegmentCount() > 0 )
  576. dcBaseline = aBaseline.SquaredDistance( target );
  577. else
  578. dcBaseline = ( aBaseline.CPoint( 0 ) - target ).SquaredEuclideanNorm();
  579. if( dcBaseline > distBaseline[1] )
  580. continue;
  581. distBaseline[1] = dcBaseline;
  582. }
  583. prioritized[1] = segm;
  584. dist[1] = dd;
  585. point[1] = nearest;
  586. }
  587. }
  588. }
  589. PNS::LINKED_ITEM* rv = nullptr;
  590. for( int i = 0; i < candidateCount; i++ )
  591. {
  592. PNS::LINKED_ITEM* item = prioritized[i];
  593. if( item && ( aLayer < 0 || item->Layers().Overlaps( aLayer ) ) )
  594. {
  595. rv = item;
  596. aPointOut = point[i];
  597. break;
  598. }
  599. }
  600. return rv;
  601. }
  602. static std::optional<PNS::LINE> getPNSLine( const VECTOR2I& aStart, const VECTOR2I& aEnd,
  603. PNS::ROUTER* router, int layer, VECTOR2I& aStartOut,
  604. VECTOR2I& aEndOut )
  605. {
  606. PNS::NODE* world = router->GetWorld();
  607. PNS::LINKED_ITEM* startItem = pickSegment( router, aStart, layer, aStartOut );
  608. PNS::LINKED_ITEM* endItem = pickSegment( router, aEnd, layer, aEndOut );
  609. for( PNS::LINKED_ITEM* testItem : { startItem, endItem } )
  610. {
  611. if( !testItem )
  612. continue;
  613. PNS::LINE line = world->AssembleLine( testItem, nullptr, false, false );
  614. SHAPE_LINE_CHAIN oldChain = line.CLine();
  615. if( oldChain.PointOnEdge( aStartOut, 1 ) && oldChain.PointOnEdge( aEndOut, 1 ) )
  616. return line;
  617. }
  618. return std::nullopt;
  619. }
  620. bool PCB_TUNING_PATTERN::initBaseLine( PNS::ROUTER* aRouter, int aPNSLayer, BOARD* aBoard,
  621. VECTOR2I& aStart, VECTOR2I& aEnd, NETINFO_ITEM* aNet,
  622. std::optional<SHAPE_LINE_CHAIN>& aBaseLine )
  623. {
  624. PNS::NODE* world = aRouter->GetWorld();
  625. aStart = snapToNearestTrack( aStart, aBoard, aNet, nullptr );
  626. aEnd = snapToNearestTrack( aEnd, aBoard, aNet, nullptr );
  627. VECTOR2I startSnapPoint, endSnapPoint;
  628. PNS::LINKED_ITEM* startItem = pickSegment( aRouter, aStart, aPNSLayer, startSnapPoint );
  629. PNS::LINKED_ITEM* endItem = pickSegment( aRouter, aEnd, aPNSLayer, endSnapPoint );
  630. wxASSERT( startItem );
  631. wxASSERT( endItem );
  632. if( !startItem || !endItem )
  633. return false;
  634. PNS::LINE line = world->AssembleLine( startItem );
  635. const SHAPE_LINE_CHAIN& chain = line.CLine();
  636. wxASSERT( line.ContainsLink( endItem ) );
  637. wxASSERT( chain.PointOnEdge( startSnapPoint, 40000 ) );
  638. wxASSERT( chain.PointOnEdge( endSnapPoint, 40000 ) );
  639. SHAPE_LINE_CHAIN pre;
  640. SHAPE_LINE_CHAIN mid;
  641. SHAPE_LINE_CHAIN post;
  642. chain.Split( startSnapPoint, endSnapPoint, pre, mid, post );
  643. aBaseLine = mid;
  644. return true;
  645. }
  646. bool PCB_TUNING_PATTERN::initBaseLines( PNS::ROUTER* aRouter, int aPNSLayer, BOARD* aBoard )
  647. {
  648. m_baseLineCoupled.reset();
  649. PCB_TRACK* track = nullptr;
  650. m_origin = snapToNearestTrack( m_origin, aBoard, nullptr, &track );
  651. wxCHECK( track, false );
  652. NETINFO_ITEM* net = track->GetNet();
  653. if( !initBaseLine( aRouter, aPNSLayer, aBoard, m_origin, m_end, net, m_baseLine ) )
  654. return false;
  655. // Generate both baselines even if we're skewing. We need the coupled baseline to run the
  656. // DRC rules against.
  657. if( m_tuningMode == DIFF_PAIR || m_tuningMode == DIFF_PAIR_SKEW )
  658. {
  659. if( NETINFO_ITEM* coupledNet = aBoard->DpCoupledNet( net ) )
  660. {
  661. VECTOR2I coupledStart = snapToNearestTrack( m_origin, aBoard, coupledNet, nullptr );
  662. VECTOR2I coupledEnd = snapToNearestTrack( m_end, aBoard, coupledNet, nullptr );
  663. return initBaseLine( aRouter, aPNSLayer, aBoard, coupledStart, coupledEnd, coupledNet,
  664. m_baseLineCoupled );
  665. }
  666. return false;
  667. }
  668. return true;
  669. }
  670. bool PCB_TUNING_PATTERN::removeToBaseline( PNS::ROUTER* aRouter, int aPNSLayer,
  671. SHAPE_LINE_CHAIN& aBaseLine )
  672. {
  673. VECTOR2I startSnapPoint, endSnapPoint;
  674. std::optional<PNS::LINE> pnsLine = getPNSLine( aBaseLine.CPoint( 0 ), aBaseLine.CLastPoint(),
  675. aRouter, aPNSLayer, startSnapPoint, endSnapPoint );
  676. wxCHECK( pnsLine, false );
  677. SHAPE_LINE_CHAIN pre;
  678. SHAPE_LINE_CHAIN mid;
  679. SHAPE_LINE_CHAIN post;
  680. pnsLine->CLine().Split( startSnapPoint, endSnapPoint, pre, mid, post );
  681. for( PNS::LINKED_ITEM* li : pnsLine->Links() )
  682. aRouter->GetInterface()->RemoveItem( li );
  683. aRouter->GetWorld()->Remove( *pnsLine );
  684. SHAPE_LINE_CHAIN straightChain;
  685. straightChain.Append( pre );
  686. straightChain.Append( aBaseLine );
  687. straightChain.Append( post );
  688. straightChain.Simplify();
  689. PNS::LINE straightLine( *pnsLine, straightChain );
  690. aRouter->GetWorld()->Add( straightLine, false );
  691. for( PNS::LINKED_ITEM* li : straightLine.Links() )
  692. aRouter->GetInterface()->AddItem( li );
  693. return true;
  694. }
  695. void PCB_TUNING_PATTERN::Remove( GENERATOR_TOOL* aTool, BOARD* aBoard, BOARD_COMMIT* aCommit )
  696. {
  697. SetFlags( IN_EDIT );
  698. aTool->Router()->SyncWorld();
  699. PNS::ROUTER* router = aTool->Router();
  700. PNS_KICAD_IFACE* iface = aTool->GetInterface();
  701. aCommit->Remove( this );
  702. aTool->ClearRouterChanges();
  703. // PNS layers and PCB layers have different coding. so convert PCB layer to PNS layer
  704. int pnslayer = iface->GetPNSLayerFromBoardLayer( GetLayer() );
  705. if( baselineValid() )
  706. {
  707. bool success = true;
  708. success &= removeToBaseline( router, pnslayer, *m_baseLine );
  709. if( m_tuningMode == DIFF_PAIR )
  710. success &= removeToBaseline( router, pnslayer, *m_baseLineCoupled );
  711. if( !success )
  712. recoverBaseline( router );
  713. }
  714. const std::vector<GENERATOR_PNS_CHANGES>& allPnsChanges = aTool->GetRouterChanges();
  715. for( const GENERATOR_PNS_CHANGES& pnsChanges : allPnsChanges )
  716. {
  717. const std::set<BOARD_ITEM*> routerRemovedItems = pnsChanges.removedItems;
  718. const std::set<BOARD_ITEM*> routerAddedItems = pnsChanges.addedItems;
  719. /*std::cout << "Push commits << " << allPnsChanges.size() << " routerRemovedItems "
  720. << routerRemovedItems.size() << " routerAddedItems " << routerAddedItems.size()
  721. << " m_removedItems " << m_removedItems.size() << std::endl;*/
  722. for( BOARD_ITEM* item : routerRemovedItems )
  723. {
  724. item->ClearSelected();
  725. aCommit->Remove( item );
  726. }
  727. for( BOARD_ITEM* item : routerAddedItems )
  728. aCommit->Add( item );
  729. }
  730. }
  731. bool PCB_TUNING_PATTERN::recoverBaseline( PNS::ROUTER* aRouter )
  732. {
  733. PNS::SOLID queryItem;
  734. SHAPE_LINE_CHAIN* chain = static_cast<SHAPE_LINE_CHAIN*>( getOutline().Clone() );
  735. queryItem.SetShape( chain ); // PNS::SOLID takes ownership
  736. queryItem.SetLayer( m_layer );
  737. int lineWidth = 0;
  738. PNS::NODE::OBSTACLES obstacles;
  739. PNS::COLLISION_SEARCH_OPTIONS opts;
  740. opts.m_useClearanceEpsilon = false;
  741. PNS::NODE* world = aRouter->GetWorld();
  742. PNS::NODE* branch = world->Branch();
  743. branch->QueryColliding( &queryItem, obstacles, opts );
  744. for( const PNS::OBSTACLE& obs : obstacles )
  745. {
  746. PNS::ITEM* item = obs.m_item;
  747. if( !item->OfKind( PNS::ITEM::SEGMENT_T | PNS::ITEM::ARC_T ) )
  748. continue;
  749. if( PNS::LINKED_ITEM* li = dynamic_cast<PNS::LINKED_ITEM*>( item ) )
  750. {
  751. if( lineWidth == 0 || li->Width() < lineWidth )
  752. lineWidth = li->Width();
  753. }
  754. if( chain->PointInside( item->Anchor( 0 ), 10 )
  755. && chain->PointInside( item->Anchor( 1 ), 10 ) )
  756. {
  757. branch->Remove( item );
  758. }
  759. }
  760. if( lineWidth == 0 )
  761. lineWidth = pcbIUScale.mmToIU( 0.1 ); // Fallback
  762. if( baselineValid() )
  763. {
  764. NETINFO_ITEM* recoverNet = GetBoard()->FindNet( m_lastNetName );
  765. PNS::LINE recoverLine;
  766. recoverLine.SetLayer( m_layer );
  767. recoverLine.SetWidth( lineWidth );
  768. recoverLine.Line() = *m_baseLine;
  769. recoverLine.SetNet( recoverNet );
  770. branch->Add( recoverLine, false );
  771. if( m_tuningMode == DIFF_PAIR || m_tuningMode == DIFF_PAIR_SKEW )
  772. {
  773. NETINFO_ITEM* recoverCoupledNet = GetBoard()->DpCoupledNet( recoverNet );
  774. PNS::LINE recoverLineCoupled;
  775. recoverLineCoupled.SetLayer( m_layer );
  776. recoverLineCoupled.SetWidth( lineWidth );
  777. recoverLineCoupled.Line() = *m_baseLineCoupled;
  778. recoverLineCoupled.SetNet( recoverCoupledNet );
  779. branch->Add( recoverLineCoupled, false );
  780. }
  781. }
  782. aRouter->CommitRouting( branch );
  783. //wxLogWarning( "PNS baseline recovered" );
  784. return true;
  785. }
  786. bool PCB_TUNING_PATTERN::resetToBaseline( GENERATOR_TOOL* aTool, int aPNSLayer,
  787. SHAPE_LINE_CHAIN& aBaseLine, bool aPrimary )
  788. {
  789. PNS_KICAD_IFACE* iface = aTool->GetInterface();
  790. PNS::ROUTER* router = aTool->Router();
  791. PNS::NODE* world = router->GetWorld();
  792. VECTOR2I startSnapPoint, endSnapPoint;
  793. std::optional<PNS::LINE> pnsLine = getPNSLine( aBaseLine.CPoint( 0 ), aBaseLine.CLastPoint(),
  794. router, aPNSLayer, startSnapPoint, endSnapPoint );
  795. if( !pnsLine )
  796. {
  797. // TODO
  798. //recoverBaseline( aRouter );
  799. return true;
  800. }
  801. PNS::NODE* branch = world->Branch();
  802. SHAPE_LINE_CHAIN straightChain;
  803. {
  804. SHAPE_LINE_CHAIN pre, mid, post;
  805. pnsLine->CLine().Split( startSnapPoint, endSnapPoint, pre, mid, post );
  806. straightChain.Append( pre );
  807. straightChain.Append( aBaseLine );
  808. straightChain.Append( post );
  809. straightChain.Simplify();
  810. }
  811. branch->Remove( *pnsLine );
  812. SHAPE_LINE_CHAIN newLineChain;
  813. if( aPrimary )
  814. {
  815. m_origin = straightChain.NearestPoint( m_origin );
  816. m_end = straightChain.NearestPoint( m_end );
  817. // Don't allow points too close
  818. if( ( m_end - m_origin ).EuclideanNorm() < pcbIUScale.mmToIU( 0.1 ) )
  819. {
  820. m_origin = startSnapPoint;
  821. m_end = endSnapPoint;
  822. }
  823. {
  824. SHAPE_LINE_CHAIN pre, mid, post;
  825. straightChain.Split( m_origin, m_end, pre, mid, post );
  826. newLineChain.Append( pre );
  827. newLineChain.Append( mid );
  828. newLineChain.Append( post );
  829. m_baseLine = mid;
  830. }
  831. }
  832. else
  833. {
  834. VECTOR2I start = straightChain.NearestPoint( m_origin );
  835. VECTOR2I end = straightChain.NearestPoint( m_end );
  836. {
  837. SHAPE_LINE_CHAIN pre, mid, post;
  838. straightChain.Split( start, end, pre, mid, post );
  839. newLineChain.Append( pre );
  840. newLineChain.Append( mid );
  841. newLineChain.Append( post );
  842. m_baseLineCoupled = mid;
  843. }
  844. }
  845. PNS::LINE newLine( *pnsLine, newLineChain );
  846. branch->Add( newLine, false );
  847. router->CommitRouting( branch );
  848. int clearance = router->GetRuleResolver()->Clearance( &newLine, nullptr );
  849. iface->DisplayItem( &newLine, clearance, true, PNS_COLLISION );
  850. return true;
  851. }
  852. bool PCB_TUNING_PATTERN::Update( GENERATOR_TOOL* aTool, BOARD* aBoard, BOARD_COMMIT* aCommit )
  853. {
  854. if( !( GetFlags() & IN_EDIT ) )
  855. return false;
  856. KIGFX::VIEW* view = aTool->GetManager()->GetView();
  857. PNS::ROUTER* router = aTool->Router();
  858. PNS_KICAD_IFACE* iface = aTool->GetInterface();
  859. PCB_LAYER_ID pcblayer = GetLayer();
  860. auto hideRemovedItems = [&]( bool aHide )
  861. {
  862. if( view )
  863. {
  864. for( const GENERATOR_PNS_CHANGES& pnsCommit : aTool->GetRouterChanges() )
  865. {
  866. for( BOARD_ITEM* item : pnsCommit.removedItems )
  867. {
  868. if( view )
  869. view->Hide( item, aHide, aHide );
  870. }
  871. }
  872. }
  873. };
  874. iface->SetStartLayerFromPCBNew( pcblayer );
  875. if( router->RoutingInProgress() )
  876. {
  877. router->StopRouting();
  878. }
  879. // PNS layers and PCB layers have different coding. so convert PCB layer to PNS layer
  880. int pnslayer = iface->GetPNSLayerFromBoardLayer( pcblayer );
  881. if( !baselineValid() )
  882. {
  883. initBaseLines( router, pnslayer, aBoard );
  884. }
  885. else
  886. {
  887. if( resetToBaseline( aTool, pnslayer, *m_baseLine, true ) )
  888. {
  889. m_origin = m_baseLine->CPoint( 0 );
  890. m_end = m_baseLine->CLastPoint();
  891. }
  892. else
  893. {
  894. //initBaseLines( router, layer, aBoard );
  895. return false;
  896. }
  897. if( m_tuningMode == DIFF_PAIR )
  898. {
  899. if( !resetToBaseline( aTool, pnslayer, *m_baseLineCoupled, false ) )
  900. {
  901. initBaseLines( router, pnslayer, aBoard );
  902. return false;
  903. }
  904. }
  905. }
  906. hideRemovedItems( true );
  907. // Snap points
  908. VECTOR2I startSnapPoint, endSnapPoint;
  909. wxCHECK( m_baseLine, false );
  910. PNS::LINKED_ITEM* startItem = pickSegment( router, m_origin, pnslayer, startSnapPoint, *m_baseLine);
  911. PNS::LINKED_ITEM* endItem = pickSegment( router, m_end, pnslayer, endSnapPoint, *m_baseLine );
  912. wxASSERT( startItem );
  913. wxASSERT( endItem );
  914. if( !startItem || !endItem )
  915. return false;
  916. router->SetMode( GetPNSMode() );
  917. if( !router->StartRouting( startSnapPoint, startItem, pnslayer ) )
  918. {
  919. //recoverBaseline( router );
  920. return false;
  921. }
  922. PNS::MEANDER_PLACER_BASE* placer = static_cast<PNS::MEANDER_PLACER_BASE*>( router->Placer() );
  923. m_settings.m_keepEndpoints = true; // Required for re-grouping
  924. placer->UpdateSettings( m_settings );
  925. router->Move( m_end, nullptr );
  926. if( PNS::DP_MEANDER_PLACER* dpPlacer = dynamic_cast<PNS::DP_MEANDER_PLACER*>( placer ) )
  927. {
  928. m_trackWidth = dpPlacer->GetOriginPair().Width();
  929. m_diffPairGap = dpPlacer->GetOriginPair().Gap();
  930. }
  931. else
  932. {
  933. m_trackWidth = startItem->Width();
  934. m_diffPairGap = router->Sizes().DiffPairGap();
  935. }
  936. m_settings = placer->MeanderSettings();
  937. m_lastNetName = iface->GetNetName( startItem->Net() );
  938. m_tuningStatus = placer->TuningStatus();
  939. wxString statusMessage;
  940. switch ( m_tuningStatus )
  941. {
  942. case PNS::MEANDER_PLACER_BASE::TOO_LONG: statusMessage = _( "too long" ); break;
  943. case PNS::MEANDER_PLACER_BASE::TOO_SHORT: statusMessage = _( "too short" ); break;
  944. case PNS::MEANDER_PLACER_BASE::TUNED: statusMessage = _( "tuned" ); break;
  945. default: statusMessage = _( "unknown" ); break;
  946. }
  947. wxString result;
  948. EDA_UNITS userUnits = EDA_UNITS::MM;
  949. if( aTool->GetManager()->GetSettings() )
  950. userUnits = static_cast<EDA_UNITS>( aTool->GetManager()->GetSettings()->m_System.units );
  951. if( m_settings.m_isTimeDomain )
  952. {
  953. result = EDA_UNIT_UTILS::UI::MessageTextFromValue( pcbIUScale, EDA_UNITS::PS,
  954. (double) placer->TuningLengthResult() );
  955. }
  956. else
  957. {
  958. result = EDA_UNIT_UTILS::UI::MessageTextFromValue( pcbIUScale, userUnits,
  959. (double) placer->TuningLengthResult() );
  960. }
  961. m_tuningInfo.Printf( wxS( "%s (%s)" ), result, statusMessage );
  962. return true;
  963. }
  964. void PCB_TUNING_PATTERN::EditFinish( GENERATOR_TOOL* aTool, BOARD* aBoard, BOARD_COMMIT* aCommit )
  965. {
  966. if( !( GetFlags() & IN_EDIT ) )
  967. return;
  968. ClearFlags( IN_EDIT );
  969. KIGFX::VIEW* view = aTool->GetManager()->GetView();
  970. PNS::ROUTER* router = aTool->Router();
  971. PNS_KICAD_IFACE* iface = aTool->GetInterface();
  972. SHAPE_LINE_CHAIN bounds = getOutline();
  973. int epsilon = aBoard->GetDesignSettings().GetDRCEpsilon();
  974. iface->EraseView();
  975. if( router->RoutingInProgress() )
  976. {
  977. bool forceFinish = true;
  978. bool forceCommit = false;
  979. router->FixRoute( m_end, nullptr, forceFinish, forceCommit );
  980. router->StopRouting();
  981. }
  982. const std::vector<GENERATOR_PNS_CHANGES>& pnsCommits = aTool->GetRouterChanges();
  983. for( const GENERATOR_PNS_CHANGES& pnsCommit : pnsCommits )
  984. {
  985. const std::set<BOARD_ITEM*> routerRemovedItems = pnsCommit.removedItems;
  986. const std::set<BOARD_ITEM*> routerAddedItems = pnsCommit.addedItems;
  987. //std::cout << "Push commits << " << allPnsChanges.size() << " routerRemovedItems "
  988. // << routerRemovedItems.size() << " routerAddedItems " << routerAddedItems.size()
  989. // << " m_removedItems " << m_removedItems.size() << std::endl;
  990. for( BOARD_ITEM* item : routerRemovedItems )
  991. {
  992. if( view )
  993. view->Hide( item, false );
  994. aCommit->Remove( item );
  995. }
  996. for( BOARD_ITEM* item : routerAddedItems )
  997. {
  998. aCommit->Add( item );
  999. if( PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( item ) )
  1000. {
  1001. if( bounds.PointInside( track->GetStart(), epsilon )
  1002. && bounds.PointInside( track->GetEnd(), epsilon ) )
  1003. {
  1004. AddItem( item );
  1005. }
  1006. }
  1007. }
  1008. }
  1009. }
  1010. void PCB_TUNING_PATTERN::EditCancel( GENERATOR_TOOL* aTool, BOARD* aBoard, BOARD_COMMIT* aCommit )
  1011. {
  1012. if( !( GetFlags() & IN_EDIT ) )
  1013. return;
  1014. ClearFlags( IN_EDIT );
  1015. PNS_KICAD_IFACE* iface = aTool->GetInterface();
  1016. iface->EraseView();
  1017. if( KIGFX::VIEW* view = aTool->GetManager()->GetView() )
  1018. {
  1019. for( const GENERATOR_PNS_CHANGES& pnsCommit : aTool->GetRouterChanges() )
  1020. {
  1021. for( BOARD_ITEM* item : pnsCommit.removedItems )
  1022. view->Hide( item, false );
  1023. }
  1024. }
  1025. aTool->Router()->StopRouting();
  1026. }
  1027. bool PCB_TUNING_PATTERN::MakeEditPoints( EDIT_POINTS& aPoints ) const
  1028. {
  1029. VECTOR2I centerlineOffset;
  1030. VECTOR2I centerlineOffsetEnd;
  1031. if( m_tuningMode == DIFF_PAIR && m_baseLineCoupled && m_baseLineCoupled->SegmentCount() > 0 )
  1032. {
  1033. centerlineOffset = ( m_baseLineCoupled->CPoint( 0 ) - m_origin ) / 2;
  1034. centerlineOffsetEnd = ( m_baseLineCoupled->CLastPoint() - m_end ) / 2;
  1035. }
  1036. aPoints.AddPoint( m_origin + centerlineOffset );
  1037. aPoints.AddPoint( m_end + centerlineOffsetEnd );
  1038. SEG base = m_baseLine && m_baseLine->SegmentCount() > 0 ? m_baseLine->CSegment( 0 )
  1039. : SEG( m_origin, m_end );
  1040. base.A += centerlineOffset;
  1041. base.B += centerlineOffset;
  1042. int amplitude = m_settings.m_maxAmplitude + KiROUND( m_trackWidth / 2.0 );
  1043. if( m_tuningMode == DIFF_PAIR )
  1044. amplitude += m_trackWidth + m_diffPairGap;
  1045. if( m_settings.m_initialSide == -1 )
  1046. amplitude *= -1;
  1047. VECTOR2I widthHandleOffset = ( base.B - base.A ).Perpendicular().Resize( amplitude );
  1048. aPoints.AddPoint( base.A + widthHandleOffset );
  1049. aPoints.Point( 2 ).SetGridConstraint( IGNORE_GRID );
  1050. VECTOR2I spacingHandleOffset =
  1051. widthHandleOffset + ( base.B - base.A ).Resize( KiROUND( m_settings.m_spacing * 1.5 ) );
  1052. aPoints.AddPoint( base.A + spacingHandleOffset );
  1053. aPoints.Point( 3 ).SetGridConstraint( IGNORE_GRID );
  1054. return true;
  1055. }
  1056. bool PCB_TUNING_PATTERN::UpdateFromEditPoints( EDIT_POINTS& aEditPoints )
  1057. {
  1058. VECTOR2I centerlineOffset;
  1059. VECTOR2I centerlineOffsetEnd;
  1060. if( m_tuningMode == DIFF_PAIR && m_baseLineCoupled && m_baseLineCoupled->SegmentCount() > 0 )
  1061. {
  1062. centerlineOffset = ( m_baseLineCoupled->CPoint( 0 ) - m_origin ) / 2;
  1063. centerlineOffsetEnd = ( m_baseLineCoupled->CLastPoint() - m_end ) / 2;
  1064. }
  1065. SEG base = m_baseLine && m_baseLine->SegmentCount() > 0 ? m_baseLine->CSegment( 0 )
  1066. : SEG( m_origin, m_end );
  1067. base.A += centerlineOffset;
  1068. base.B += centerlineOffset;
  1069. m_origin = aEditPoints.Point( 0 ).GetPosition() - centerlineOffset;
  1070. m_end = aEditPoints.Point( 1 ).GetPosition() - centerlineOffsetEnd;
  1071. if( aEditPoints.Point( 2 ).IsActive() )
  1072. {
  1073. VECTOR2I wHandle = aEditPoints.Point( 2 ).GetPosition();
  1074. int value = base.LineDistance( wHandle );
  1075. value -= KiROUND( m_trackWidth / 2.0 );
  1076. if( m_tuningMode == DIFF_PAIR )
  1077. value -= m_trackWidth + m_diffPairGap;
  1078. SetMaxAmplitude( KiROUND( value / pcbIUScale.mmToIU( 0.01 ) ) * pcbIUScale.mmToIU( 0.01 ) );
  1079. int side = base.Side( wHandle );
  1080. if( side < 0 )
  1081. m_settings.m_initialSide = PNS::MEANDER_SIDE_LEFT;
  1082. else
  1083. m_settings.m_initialSide = PNS::MEANDER_SIDE_RIGHT;
  1084. }
  1085. if( aEditPoints.Point( 3 ).IsActive() )
  1086. {
  1087. VECTOR2I wHandle = aEditPoints.Point( 2 ).GetPosition();
  1088. VECTOR2I sHandle = aEditPoints.Point( 3 ).GetPosition();
  1089. int value = KiROUND( SEG( base.A, wHandle ).LineDistance( sHandle ) / 1.5 );
  1090. SetSpacing( KiROUND( value / pcbIUScale.mmToIU( 0.01 ) ) * pcbIUScale.mmToIU( 0.01 ) );
  1091. }
  1092. return true;
  1093. }
  1094. bool PCB_TUNING_PATTERN::UpdateEditPoints( EDIT_POINTS& aEditPoints )
  1095. {
  1096. VECTOR2I centerlineOffset;
  1097. VECTOR2I centerlineOffsetEnd;
  1098. if( m_tuningMode == DIFF_PAIR && m_baseLineCoupled && m_baseLineCoupled->SegmentCount() > 0 )
  1099. {
  1100. centerlineOffset = ( m_baseLineCoupled->CPoint( 0 ) - m_origin ) / 2;
  1101. centerlineOffsetEnd = ( m_baseLineCoupled->CLastPoint() - m_end ) / 2;
  1102. }
  1103. SEG base = m_baseLine && m_baseLine->SegmentCount() > 0 ? m_baseLine->CSegment( 0 )
  1104. : SEG( m_origin, m_end );
  1105. base.A += centerlineOffset;
  1106. base.B += centerlineOffset;
  1107. int amplitude = m_settings.m_maxAmplitude + KiROUND( m_trackWidth / 2.0 );
  1108. if( m_tuningMode == DIFF_PAIR )
  1109. amplitude += m_trackWidth + m_diffPairGap;
  1110. if( m_settings.m_initialSide == -1 )
  1111. amplitude *= -1;
  1112. VECTOR2I widthHandleOffset = ( base.B - base.A ).Perpendicular().Resize( amplitude );
  1113. aEditPoints.Point( 0 ).SetPosition( m_origin + centerlineOffset );
  1114. aEditPoints.Point( 1 ).SetPosition( m_end + centerlineOffsetEnd );
  1115. aEditPoints.Point( 2 ).SetPosition( base.A + widthHandleOffset );
  1116. VECTOR2I spacingHandleOffset =
  1117. widthHandleOffset + ( base.B - base.A ).Resize( KiROUND( m_settings.m_spacing * 1.5 ) );
  1118. aEditPoints.Point( 3 ).SetPosition( base.A + spacingHandleOffset );
  1119. return true;
  1120. }
  1121. SHAPE_LINE_CHAIN PCB_TUNING_PATTERN::getOutline() const
  1122. {
  1123. if( m_baseLine )
  1124. {
  1125. int clampedMaxAmplitude = m_settings.m_maxAmplitude;
  1126. int minAllowedAmplitude = 0;
  1127. int baselineOffset = m_tuningMode == DIFF_PAIR ? ( m_diffPairGap + m_trackWidth ) / 2 : 0;
  1128. if( m_settings.m_cornerStyle == PNS::MEANDER_STYLE::MEANDER_STYLE_ROUND )
  1129. {
  1130. minAllowedAmplitude = baselineOffset + m_trackWidth;
  1131. }
  1132. else
  1133. {
  1134. int correction = m_trackWidth * tan( 1 - tan( DEG2RAD( 22.5 ) ) );
  1135. minAllowedAmplitude = baselineOffset + correction;
  1136. }
  1137. clampedMaxAmplitude = std::max( clampedMaxAmplitude, minAllowedAmplitude );
  1138. if( m_settings.m_singleSided )
  1139. {
  1140. SHAPE_LINE_CHAIN clBase = *m_baseLine;
  1141. SHAPE_LINE_CHAIN left, right;
  1142. if( m_tuningMode != DIFF_PAIR )
  1143. {
  1144. int amplitude = clampedMaxAmplitude + KiROUND( m_trackWidth / 2.0 );
  1145. SHAPE_LINE_CHAIN chain;
  1146. if( clBase.OffsetLine( amplitude, CORNER_STRATEGY::ROUND_ALL_CORNERS, ARC_LOW_DEF, left, right,
  1147. true ) )
  1148. {
  1149. chain.Append( m_settings.m_initialSide >= 0 ? right : left );
  1150. chain.Append( clBase.Reverse() );
  1151. chain.SetClosed( true );
  1152. return chain;
  1153. }
  1154. }
  1155. else if( m_tuningMode == DIFF_PAIR && m_baseLineCoupled )
  1156. {
  1157. int amplitude = clampedMaxAmplitude + m_trackWidth + KiROUND( m_diffPairGap / 2.0 );
  1158. SHAPE_LINE_CHAIN clCoupled = *m_baseLineCoupled;
  1159. SHAPE_LINE_CHAIN chain1, chain2;
  1160. if( clBase.OffsetLine( amplitude, CORNER_STRATEGY::ROUND_ALL_CORNERS, ARC_LOW_DEF, left, right,
  1161. true ) )
  1162. {
  1163. if( m_settings.m_initialSide >= 0 )
  1164. chain1.Append( right );
  1165. else
  1166. chain1.Append( left );
  1167. if( clBase.OffsetLine( KiROUND( m_trackWidth / 2.0 ), CORNER_STRATEGY::ROUND_ALL_CORNERS,
  1168. ARC_LOW_DEF, left, right, true ) )
  1169. {
  1170. if( m_settings.m_initialSide >= 0 )
  1171. chain1.Append( left.Reverse() );
  1172. else
  1173. chain1.Append( right.Reverse() );
  1174. }
  1175. chain1.SetClosed( true );
  1176. }
  1177. if( clCoupled.OffsetLine( amplitude, CORNER_STRATEGY::ROUND_ALL_CORNERS, ARC_LOW_DEF, left, right,
  1178. true ) )
  1179. {
  1180. if( m_settings.m_initialSide >= 0 )
  1181. chain2.Append( right );
  1182. else
  1183. chain2.Append( left );
  1184. if( clCoupled.OffsetLine( KiROUND( m_trackWidth / 2.0 ), CORNER_STRATEGY::ROUND_ALL_CORNERS,
  1185. ARC_LOW_DEF, left, right, true ) )
  1186. {
  1187. if( m_settings.m_initialSide >= 0 )
  1188. chain2.Append( left.Reverse() );
  1189. else
  1190. chain2.Append( right.Reverse() );
  1191. }
  1192. chain2.SetClosed( true );
  1193. }
  1194. SHAPE_POLY_SET merged;
  1195. merged.BooleanAdd( chain1, chain2 );
  1196. if( merged.OutlineCount() > 0 )
  1197. return merged.Outline( 0 );
  1198. }
  1199. }
  1200. // Not single-sided / fallback
  1201. SHAPE_POLY_SET poly;
  1202. SHAPE_LINE_CHAIN cl = *m_baseLine;
  1203. int amplitude = 0;
  1204. if( m_tuningMode == DIFF_PAIR )
  1205. amplitude = clampedMaxAmplitude + m_diffPairGap / 2 + KiROUND( m_trackWidth );
  1206. else
  1207. amplitude = clampedMaxAmplitude + KiROUND( m_trackWidth / 2.0 );
  1208. poly.OffsetLineChain( *m_baseLine, amplitude, CORNER_STRATEGY::ROUND_ALL_CORNERS, ARC_LOW_DEF, false );
  1209. if( m_tuningMode == DIFF_PAIR && m_baseLineCoupled )
  1210. {
  1211. SHAPE_POLY_SET polyCoupled;
  1212. polyCoupled.OffsetLineChain( *m_baseLineCoupled, amplitude, CORNER_STRATEGY::ROUND_ALL_CORNERS,
  1213. ARC_LOW_DEF, false );
  1214. poly.ClearArcs();
  1215. polyCoupled.ClearArcs();
  1216. SHAPE_POLY_SET merged;
  1217. merged.BooleanAdd( poly, polyCoupled );
  1218. if( merged.OutlineCount() > 0 )
  1219. return merged.Outline( 0 );
  1220. }
  1221. if( poly.OutlineCount() > 0 )
  1222. return poly.Outline( 0 );
  1223. }
  1224. return SHAPE_LINE_CHAIN();
  1225. }
  1226. void PCB_TUNING_PATTERN::ViewDraw( int aLayer, KIGFX::VIEW* aView ) const
  1227. {
  1228. if( !IsSelected() && !IsNew() )
  1229. return;
  1230. KIGFX::PREVIEW::DRAW_CONTEXT ctx( *aView );
  1231. int size = KiROUND( aView->ToWorld( EDIT_POINT::POINT_SIZE ) * 0.8 );
  1232. size = std::max( size, pcbIUScale.mmToIU( 0.05 ) );
  1233. if( !HasFlag( IN_EDIT ) )
  1234. {
  1235. if( m_baseLine )
  1236. {
  1237. for( int i = 0; i < m_baseLine->SegmentCount(); i++ )
  1238. {
  1239. SEG seg = m_baseLine->CSegment( i );
  1240. ctx.DrawLineDashed( seg.A, seg.B, size, size / 6, true );
  1241. }
  1242. }
  1243. else
  1244. {
  1245. ctx.DrawLineDashed( m_origin, m_end, size, size / 6, false );
  1246. }
  1247. if( m_tuningMode == DIFF_PAIR && m_baseLineCoupled )
  1248. {
  1249. for( int i = 0; i < m_baseLineCoupled->SegmentCount(); i++ )
  1250. {
  1251. SEG seg = m_baseLineCoupled->CSegment( i );
  1252. ctx.DrawLineDashed( seg.A, seg.B, size, size / 6, true );
  1253. }
  1254. }
  1255. }
  1256. SHAPE_LINE_CHAIN chain = getOutline();
  1257. for( int i = 0; i < chain.SegmentCount(); i++ )
  1258. {
  1259. SEG seg = chain.Segment( i );
  1260. ctx.DrawLineDashed( seg.A, seg.B, size, size / 2, false );
  1261. }
  1262. }
  1263. const STRING_ANY_MAP PCB_TUNING_PATTERN::GetProperties() const
  1264. {
  1265. STRING_ANY_MAP props = PCB_GENERATOR::GetProperties();
  1266. props.set( "tuning_mode", tuningToString( m_tuningMode ) );
  1267. props.set( "initial_side", sideToString( m_settings.m_initialSide ) );
  1268. props.set( "last_status", statusToString( m_tuningStatus ) );
  1269. props.set( "is_time_domain", m_settings.m_isTimeDomain );
  1270. props.set( "end", m_end );
  1271. props.set( "corner_radius_percent", m_settings.m_cornerRadiusPercentage );
  1272. props.set( "single_sided", m_settings.m_singleSided );
  1273. props.set( "rounded", m_settings.m_cornerStyle == PNS::MEANDER_STYLE_ROUND );
  1274. props.set_iu( "max_amplitude", m_settings.m_maxAmplitude );
  1275. props.set_iu( "min_amplitude", m_settings.m_minAmplitude );
  1276. props.set_iu( "min_spacing", m_settings.m_spacing );
  1277. props.set_iu( "target_length_min", m_settings.m_targetLength.Min() );
  1278. props.set_iu( "target_length", m_settings.m_targetLength.Opt() );
  1279. props.set_iu( "target_length_max", m_settings.m_targetLength.Max() );
  1280. props.set_iu( "target_delay_min", m_settings.m_targetLengthDelay.Min() );
  1281. props.set_iu( "target_delay", m_settings.m_targetLengthDelay.Opt() );
  1282. props.set_iu( "target_delay_max", m_settings.m_targetLengthDelay.Max() );
  1283. props.set_iu( "target_skew_min", m_settings.m_targetSkew.Min() );
  1284. props.set_iu( "target_skew", m_settings.m_targetSkew.Opt() );
  1285. props.set_iu( "target_skew_max", m_settings.m_targetSkew.Max() );
  1286. props.set_iu( "last_track_width", m_trackWidth );
  1287. props.set_iu( "last_diff_pair_gap", m_diffPairGap );
  1288. props.set( "last_netname", m_lastNetName );
  1289. props.set( "last_tuning", m_tuningInfo );
  1290. props.set( "override_custom_rules", m_settings.m_overrideCustomRules );
  1291. if( m_baseLine )
  1292. props.set( "base_line", wxAny( *m_baseLine ) );
  1293. if( m_baseLineCoupled )
  1294. props.set( "base_line_coupled", wxAny( *m_baseLineCoupled ) );
  1295. return props;
  1296. }
  1297. void PCB_TUNING_PATTERN::SetProperties( const STRING_ANY_MAP& aProps )
  1298. {
  1299. PCB_GENERATOR::SetProperties( aProps );
  1300. wxString tuningMode;
  1301. aProps.get_to( "tuning_mode", tuningMode );
  1302. m_tuningMode = tuningFromString( tuningMode.utf8_string() );
  1303. wxString side;
  1304. aProps.get_to( "initial_side", side );
  1305. m_settings.m_initialSide = sideFromString( side.utf8_string() );
  1306. wxString status;
  1307. aProps.get_to( "last_status", status );
  1308. m_tuningStatus = statusFromString( status.utf8_string() );
  1309. aProps.get_to( "is_time_domain", m_settings.m_isTimeDomain );
  1310. aProps.get_to( "end", m_end );
  1311. aProps.get_to( "corner_radius_percent", m_settings.m_cornerRadiusPercentage );
  1312. aProps.get_to( "single_sided", m_settings.m_singleSided );
  1313. aProps.get_to( "side", m_settings.m_initialSide );
  1314. bool rounded = false;
  1315. aProps.get_to( "rounded", rounded );
  1316. m_settings.m_cornerStyle = rounded ? PNS::MEANDER_STYLE_ROUND : PNS::MEANDER_STYLE_CHAMFER;
  1317. long long int val;
  1318. aProps.get_to_iu( "target_length", val );
  1319. m_settings.SetTargetLength( val );
  1320. if( aProps.get_to_iu( "target_length_min", val ) )
  1321. m_settings.m_targetLength.SetMin( val );
  1322. if( aProps.get_to_iu( "target_length_max", val ) )
  1323. m_settings.m_targetLength.SetMax( val );
  1324. aProps.get_to_iu( "target_delay", val );
  1325. m_settings.SetTargetLengthDelay( val );
  1326. if( aProps.get_to_iu( "target_delay_min", val ) )
  1327. m_settings.m_targetLengthDelay.SetMin( val );
  1328. if( aProps.get_to_iu( "target_delay_max", val ) )
  1329. m_settings.m_targetLengthDelay.SetMax( val );
  1330. int int_val;
  1331. aProps.get_to_iu( "target_skew", int_val );
  1332. m_settings.SetTargetSkew( int_val );
  1333. if( aProps.get_to_iu( "target_skew_min", int_val ) )
  1334. m_settings.m_targetSkew.SetMin( int_val );
  1335. if( aProps.get_to_iu( "target_skew_max", int_val ) )
  1336. m_settings.m_targetSkew.SetMax( int_val );
  1337. aProps.get_to_iu( "max_amplitude", m_settings.m_maxAmplitude );
  1338. aProps.get_to_iu( "min_amplitude", m_settings.m_minAmplitude );
  1339. aProps.get_to_iu( "min_spacing", m_settings.m_spacing );
  1340. aProps.get_to_iu( "last_track_width", m_trackWidth );
  1341. aProps.get_to_iu( "last_diff_pair_gap", m_diffPairGap );
  1342. aProps.get_to( "override_custom_rules", m_settings.m_overrideCustomRules );
  1343. aProps.get_to( "last_netname", m_lastNetName );
  1344. aProps.get_to( "last_tuning", m_tuningInfo );
  1345. if( auto baseLine = aProps.get_opt<SHAPE_LINE_CHAIN>( "base_line" ) )
  1346. m_baseLine = *baseLine;
  1347. if( auto baseLineCoupled = aProps.get_opt<SHAPE_LINE_CHAIN>( "base_line_coupled" ) )
  1348. m_baseLineCoupled = *baseLineCoupled;
  1349. }
  1350. void PCB_TUNING_PATTERN::ShowPropertiesDialog( PCB_BASE_EDIT_FRAME* aEditFrame )
  1351. {
  1352. PNS::MEANDER_SETTINGS settings = m_settings;
  1353. DRC_CONSTRAINT constraint;
  1354. if( !m_items.empty() )
  1355. {
  1356. BOARD_ITEM* startItem = static_cast<BOARD_ITEM*>( *m_items.begin() );
  1357. std::shared_ptr<DRC_ENGINE>& drcEngine = GetBoard()->GetDesignSettings().m_DRCEngine;
  1358. if( m_tuningMode == DIFF_PAIR_SKEW )
  1359. {
  1360. constraint = drcEngine->EvalRules( SKEW_CONSTRAINT, startItem, nullptr, GetLayer() );
  1361. if( !constraint.IsNull() && !settings.m_overrideCustomRules )
  1362. {
  1363. if( constraint.GetOption( DRC_CONSTRAINT::OPTIONS::TIME_DOMAIN ) )
  1364. {
  1365. settings.SetTargetLengthDelay( constraint.GetValue() );
  1366. settings.SetTargetLength( MINOPTMAX<int>() );
  1367. settings.m_isTimeDomain = true;
  1368. }
  1369. else
  1370. {
  1371. settings.SetTargetLengthDelay( MINOPTMAX<int>() );
  1372. settings.SetTargetLength( constraint.GetValue() );
  1373. settings.m_isTimeDomain = false;
  1374. }
  1375. }
  1376. }
  1377. else
  1378. {
  1379. constraint = drcEngine->EvalRules( LENGTH_CONSTRAINT, startItem, nullptr, GetLayer() );
  1380. if( !constraint.IsNull() && !settings.m_overrideCustomRules )
  1381. {
  1382. if( constraint.GetOption( DRC_CONSTRAINT::OPTIONS::TIME_DOMAIN ) )
  1383. {
  1384. settings.SetTargetLengthDelay( constraint.GetValue() );
  1385. settings.SetTargetLength( MINOPTMAX<int>() );
  1386. settings.m_isTimeDomain = true;
  1387. }
  1388. else
  1389. {
  1390. settings.SetTargetLengthDelay( MINOPTMAX<int>() );
  1391. settings.SetTargetLength( constraint.GetValue() );
  1392. settings.m_isTimeDomain = false;
  1393. }
  1394. }
  1395. }
  1396. }
  1397. DIALOG_TUNING_PATTERN_PROPERTIES dlg( aEditFrame, settings, GetPNSMode(), constraint );
  1398. if( dlg.ShowModal() == wxID_OK )
  1399. {
  1400. BOARD_COMMIT commit( aEditFrame );
  1401. commit.Modify( this );
  1402. m_settings = settings;
  1403. GENERATOR_TOOL* generatorTool = aEditFrame->GetToolManager()->GetTool<GENERATOR_TOOL>();
  1404. EditStart( generatorTool, GetBoard(), &commit );
  1405. Update( generatorTool, GetBoard(), &commit );
  1406. EditFinish( generatorTool, GetBoard(), &commit );
  1407. commit.Push( _( "Edit Tuning Pattern" ) );
  1408. }
  1409. }
  1410. std::vector<EDA_ITEM*> PCB_TUNING_PATTERN::GetPreviewItems( GENERATOR_TOOL* aTool,
  1411. PCB_BASE_EDIT_FRAME* aFrame,
  1412. bool aStatusItemsOnly )
  1413. {
  1414. std::vector<EDA_ITEM*> previewItems;
  1415. KIGFX::VIEW* view = aFrame->GetCanvas()->GetView();
  1416. if( auto* placer = dynamic_cast<PNS::MEANDER_PLACER_BASE*>( aTool->Router()->Placer() ) )
  1417. {
  1418. if( !aStatusItemsOnly )
  1419. {
  1420. PNS::ITEM_SET items = placer->TunedPath();
  1421. for( PNS::ITEM* item : items )
  1422. previewItems.push_back( new ROUTER_PREVIEW_ITEM( item,
  1423. aTool->Router()->GetInterface(),
  1424. view, PNS_HOVER_ITEM ) );
  1425. }
  1426. TUNING_STATUS_VIEW_ITEM* statusItem = new TUNING_STATUS_VIEW_ITEM( aFrame );
  1427. if( m_tuningMode == DIFF_PAIR_SKEW )
  1428. {
  1429. if( m_settings.m_isTimeDomain )
  1430. statusItem->SetMinMax( m_settings.m_targetSkewDelay.Min(), m_settings.m_targetSkewDelay.Max() );
  1431. else
  1432. statusItem->SetMinMax( m_settings.m_targetSkew.Min(), m_settings.m_targetSkew.Max() );
  1433. }
  1434. else
  1435. {
  1436. if( m_settings.m_isTimeDomain )
  1437. {
  1438. if( m_settings.m_targetLengthDelay.Opt() == PNS::MEANDER_SETTINGS::DELAY_UNCONSTRAINED )
  1439. {
  1440. statusItem->ClearMinMax();
  1441. }
  1442. else
  1443. {
  1444. statusItem->SetMinMax( static_cast<double>( m_settings.m_targetLengthDelay.Min() ),
  1445. static_cast<double>( m_settings.m_targetLengthDelay.Max() ) );
  1446. }
  1447. }
  1448. else
  1449. {
  1450. if( m_settings.m_targetLength.Opt() == PNS::MEANDER_SETTINGS::LENGTH_UNCONSTRAINED )
  1451. {
  1452. statusItem->ClearMinMax();
  1453. }
  1454. else
  1455. {
  1456. statusItem->SetMinMax( static_cast<double>( m_settings.m_targetLength.Min() ),
  1457. static_cast<double>( m_settings.m_targetLength.Max() ) );
  1458. }
  1459. }
  1460. }
  1461. statusItem->SetIsTimeDomain( m_settings.m_isTimeDomain );
  1462. if( m_tuningMode == DIFF_PAIR_SKEW )
  1463. {
  1464. if( m_settings.m_isTimeDomain )
  1465. statusItem->SetCurrent( static_cast<double>( placer->TuningDelayResult() ), _( "current skew" ) );
  1466. else
  1467. statusItem->SetCurrent( static_cast<double>( placer->TuningLengthResult() ), _( "current skew" ) );
  1468. }
  1469. else
  1470. {
  1471. if( m_settings.m_isTimeDomain )
  1472. statusItem->SetCurrent( static_cast<double>( placer->TuningDelayResult() ), _( "current delay" ) );
  1473. else
  1474. statusItem->SetCurrent( static_cast<double>( placer->TuningLengthResult() ), _( "current length" ) );
  1475. }
  1476. statusItem->SetPosition( aFrame->GetToolManager()->GetMousePosition() );
  1477. previewItems.push_back( statusItem );
  1478. }
  1479. return previewItems;
  1480. }
  1481. void PCB_TUNING_PATTERN::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame,
  1482. std::vector<MSG_PANEL_ITEM>& aList )
  1483. {
  1484. wxString msg;
  1485. NETINFO_ITEM* primaryNet = nullptr;
  1486. NETINFO_ITEM* coupledNet = nullptr;
  1487. PCB_TRACK* primaryItem = nullptr;
  1488. PCB_TRACK* coupledItem = nullptr;
  1489. NETCLASS* netclass = nullptr;
  1490. int width = 0;
  1491. bool mixedWidth = false;
  1492. EDA_DATA_TYPE unitType = m_settings.m_isTimeDomain ? EDA_DATA_TYPE::TIME : EDA_DATA_TYPE::DISTANCE;
  1493. aList.emplace_back( _( "Type" ), GetFriendlyName() );
  1494. for( EDA_ITEM* member : GetItems() )
  1495. {
  1496. if( PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( member ) )
  1497. {
  1498. if( !primaryNet )
  1499. {
  1500. primaryItem = track;
  1501. primaryNet = track->GetNet();
  1502. }
  1503. else if( !coupledNet && track->GetNet() != primaryNet )
  1504. {
  1505. coupledItem = track;
  1506. coupledNet = track->GetNet();
  1507. }
  1508. if( !netclass )
  1509. netclass = track->GetEffectiveNetClass();
  1510. if( !width )
  1511. width = track->GetWidth();
  1512. else if( width != track->GetWidth() )
  1513. mixedWidth = true;
  1514. }
  1515. }
  1516. if( coupledNet )
  1517. {
  1518. aList.emplace_back( _( "Nets" ), UnescapeString( primaryNet->GetNetname() )
  1519. + wxS( ", " )
  1520. + UnescapeString( coupledNet->GetNetname() ) );
  1521. }
  1522. else if( primaryNet )
  1523. {
  1524. aList.emplace_back( _( "Net" ), UnescapeString( primaryNet->GetNetname() ) );
  1525. }
  1526. if( netclass )
  1527. aList.emplace_back( _( "Resolved Netclass" ),
  1528. UnescapeString( netclass->GetHumanReadableName() ) );
  1529. aList.emplace_back( _( "Layer" ), layerMaskDescribe() );
  1530. if( width && !mixedWidth )
  1531. aList.emplace_back( _( "Width" ), aFrame->MessageTextFromValue( width ) );
  1532. BOARD* board = GetBoard();
  1533. std::shared_ptr<DRC_ENGINE>& drcEngine = board->GetDesignSettings().m_DRCEngine;
  1534. DRC_CONSTRAINT constraint;
  1535. // Display full track length (in Pcbnew)
  1536. if( board && primaryItem && primaryItem->GetNetCode() > 0 )
  1537. {
  1538. int count = 0;
  1539. double trackLen = 0.0;
  1540. double lenPadToDie = 0.0;
  1541. double trackDelay = 0.0;
  1542. double delayPadToDie = 0.0;
  1543. std::tie( count, trackLen, lenPadToDie, trackDelay, delayPadToDie ) = board->GetTrackLength( *primaryItem );
  1544. if( coupledItem && coupledItem->GetNetCode() > 0 )
  1545. {
  1546. double coupledLen = 0.0;
  1547. double coupledLenPadToDie = 0.0;
  1548. double coupledTrackDelay = 0.0;
  1549. double doubledDelayPadToDie = 0.0;
  1550. std::tie( count, coupledLen, coupledLenPadToDie, coupledTrackDelay, doubledDelayPadToDie ) =
  1551. board->GetTrackLength( *coupledItem );
  1552. if( trackDelay == 0.0 || coupledTrackDelay == 0.0 )
  1553. {
  1554. aList.emplace_back( _( "Routed Lengths" ), aFrame->MessageTextFromValue( trackLen ) + wxS( ", " )
  1555. + aFrame->MessageTextFromValue( coupledLen ) );
  1556. }
  1557. else
  1558. {
  1559. aList.emplace_back(
  1560. _( "Routed Delays" ),
  1561. aFrame->MessageTextFromValue( trackDelay, true, EDA_DATA_TYPE::TIME ) + wxS( ", " )
  1562. + aFrame->MessageTextFromValue( coupledTrackDelay, true, EDA_DATA_TYPE::TIME ) );
  1563. }
  1564. }
  1565. else
  1566. {
  1567. if( trackDelay == 0.0 )
  1568. {
  1569. aList.emplace_back( _( "Routed Length" ), aFrame->MessageTextFromValue( trackLen ) );
  1570. }
  1571. else
  1572. {
  1573. aList.emplace_back( _( "Routed Delay" ),
  1574. aFrame->MessageTextFromValue( trackDelay, true, EDA_DATA_TYPE::TIME ) );
  1575. }
  1576. }
  1577. if( lenPadToDie != 0 && delayPadToDie == 0.0 )
  1578. {
  1579. msg = aFrame->MessageTextFromValue( lenPadToDie );
  1580. aList.emplace_back( _( "Pad To Die Length" ), msg );
  1581. msg = aFrame->MessageTextFromValue( trackLen + lenPadToDie );
  1582. aList.emplace_back( _( "Full Length" ), msg );
  1583. }
  1584. else if( delayPadToDie > 0.0 )
  1585. {
  1586. msg = aFrame->MessageTextFromValue( delayPadToDie, true, EDA_DATA_TYPE::TIME );
  1587. aList.emplace_back( _( "Pad To Die Delay" ), msg );
  1588. msg = aFrame->MessageTextFromValue( trackDelay + delayPadToDie, true, EDA_DATA_TYPE::TIME );
  1589. aList.emplace_back( _( "Full Delay" ), msg );
  1590. }
  1591. }
  1592. if( m_tuningMode == DIFF_PAIR_SKEW )
  1593. {
  1594. constraint = drcEngine->EvalRules( SKEW_CONSTRAINT, primaryItem, coupledItem, m_layer );
  1595. if( constraint.IsNull() || m_settings.m_overrideCustomRules )
  1596. {
  1597. msg = aFrame->MessageTextFromValue( m_settings.m_targetSkew.Opt() );
  1598. aList.emplace_back( wxString::Format( _( "Target Skew: %s" ), msg ),
  1599. wxString::Format( _( "(from tuning pattern properties)" ) ) );
  1600. }
  1601. else
  1602. {
  1603. msg = aFrame->MessageTextFromMinOptMax( constraint.GetValue() );
  1604. if( !msg.IsEmpty() )
  1605. {
  1606. aList.emplace_back( wxString::Format( _( "Skew Constraints: %s" ), msg ),
  1607. wxString::Format( _( "(from %s)" ), constraint.GetName() ) );
  1608. }
  1609. }
  1610. }
  1611. else
  1612. {
  1613. constraint = drcEngine->EvalRules( LENGTH_CONSTRAINT, primaryItem, coupledItem, m_layer );
  1614. if( constraint.IsNull() || m_settings.m_overrideCustomRules )
  1615. {
  1616. wxString caption;
  1617. if( m_settings.m_isTimeDomain )
  1618. {
  1619. caption = _( "Target Delay: %s" );
  1620. msg = aFrame->MessageTextFromValue( static_cast<double>( m_settings.m_targetLengthDelay.Opt() ), true,
  1621. EDA_DATA_TYPE::TIME );
  1622. }
  1623. else
  1624. {
  1625. caption = _( "Target Length: %s" );
  1626. msg = aFrame->MessageTextFromValue( static_cast<double>( m_settings.m_targetLength.Opt() ) );
  1627. }
  1628. aList.emplace_back( wxString::Format( caption, msg ),
  1629. wxString::Format( _( "(from tuning pattern properties)" ) ) );
  1630. }
  1631. else
  1632. {
  1633. msg = aFrame->MessageTextFromMinOptMax( constraint.GetValue(), unitType );
  1634. wxString caption = m_settings.m_isTimeDomain ? _( "Delay Constraints: %s" ) : _( "Length Constraints: %s" );
  1635. if( !msg.IsEmpty() )
  1636. {
  1637. aList.emplace_back( wxString::Format( caption, msg ),
  1638. wxString::Format( _( "(from %s)" ), constraint.GetName() ) );
  1639. }
  1640. }
  1641. }
  1642. }
  1643. const wxString PCB_TUNING_PATTERN::DISPLAY_NAME = _HKI( "Tuning Pattern" );
  1644. const wxString PCB_TUNING_PATTERN::GENERATOR_TYPE = wxS( "tuning_pattern" );
  1645. using SCOPED_DRAW_MODE = SCOPED_SET_RESET<DRAWING_TOOL::MODE>;
  1646. #define HITTEST_THRESHOLD_PIXELS 5
  1647. int DRAWING_TOOL::PlaceTuningPattern( const TOOL_EVENT& aEvent )
  1648. {
  1649. // TODO: (JJ) Reserving before v9 string freeze
  1650. wxLogDebug( _( "Tune Skew" ) );
  1651. if( m_isFootprintEditor )
  1652. return 0;
  1653. if( m_inDrawingTool )
  1654. return 0;
  1655. REENTRANCY_GUARD guard( &m_inDrawingTool );
  1656. m_toolMgr->RunAction( ACTIONS::selectionClear );
  1657. m_frame->PushTool( aEvent );
  1658. Activate();
  1659. BOARD* board = m_frame->GetBoard();
  1660. BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings();
  1661. std::shared_ptr<DRC_ENGINE>& drcEngine = bds.m_DRCEngine;
  1662. GENERATOR_TOOL* generatorTool = m_toolMgr->GetTool<GENERATOR_TOOL>();
  1663. PNS::ROUTER* router = generatorTool->Router();
  1664. PNS::ROUTER_MODE routerMode = aEvent.Parameter<PNS::ROUTER_MODE>();
  1665. LENGTH_TUNING_MODE mode = fromPNSMode( routerMode );
  1666. PNS::MEANDER_SETTINGS meanderSettings;
  1667. switch( mode )
  1668. {
  1669. case LENGTH_TUNING_MODE::SINGLE: meanderSettings = bds.m_SingleTrackMeanderSettings; break;
  1670. case DIFF_PAIR: meanderSettings = bds.m_DiffPairMeanderSettings; break;
  1671. case DIFF_PAIR_SKEW: meanderSettings = bds.m_SkewMeanderSettings; break;
  1672. }
  1673. KIGFX::VIEW_CONTROLS* controls = getViewControls();
  1674. PCB_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
  1675. GENERAL_COLLECTORS_GUIDE guide = m_frame->GetCollectorsGuide();
  1676. SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::TUNING );
  1677. m_pickerItem = nullptr;
  1678. m_tuningPattern = nullptr;
  1679. // Add a VIEW_GROUP that serves as a preview for the new item
  1680. m_preview.Clear();
  1681. m_view->Add( &m_preview );
  1682. auto setCursor =
  1683. [&]()
  1684. {
  1685. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::BULLSEYE );
  1686. controls->ShowCursor( true );
  1687. };
  1688. auto applyCommonSettings =
  1689. [&]( PCB_TUNING_PATTERN* aPattern )
  1690. {
  1691. const auto& origTargetLength = aPattern->GetSettings().m_targetLength;
  1692. const auto& origTargetSkew = aPattern->GetSettings().m_targetSkew;
  1693. aPattern->GetSettings() = meanderSettings;
  1694. if( meanderSettings.m_targetLength.IsNull() )
  1695. aPattern->GetSettings().m_targetLength = origTargetLength;
  1696. if( meanderSettings.m_targetSkew.IsNull() )
  1697. aPattern->GetSettings().m_targetSkew = origTargetSkew;
  1698. };
  1699. auto updateHoverStatus =
  1700. [&]()
  1701. {
  1702. std::unique_ptr<PCB_TUNING_PATTERN> dummyPattern;
  1703. if( m_pickerItem )
  1704. {
  1705. dummyPattern.reset( PCB_TUNING_PATTERN::CreateNew( generatorTool, m_frame,
  1706. m_pickerItem, mode ) );
  1707. dummyPattern->SetPosition( m_pickerItem->GetFocusPosition() );
  1708. dummyPattern->SetEnd( m_pickerItem->GetFocusPosition() );
  1709. }
  1710. if( dummyPattern )
  1711. {
  1712. applyCommonSettings( dummyPattern.get() );
  1713. dummyPattern->EditStart( generatorTool, m_board, nullptr );
  1714. dummyPattern->Update( generatorTool, m_board, nullptr );
  1715. m_preview.FreeItems();
  1716. for( EDA_ITEM* item : dummyPattern->GetPreviewItems( generatorTool, m_frame ) )
  1717. m_preview.Add( item );
  1718. generatorTool->Router()->StopRouting();
  1719. m_view->Update( &m_preview );
  1720. }
  1721. else
  1722. {
  1723. m_preview.FreeItems();
  1724. m_view->Update( &m_preview );
  1725. }
  1726. };
  1727. auto updateTuningPattern =
  1728. [&]()
  1729. {
  1730. if( m_tuningPattern && m_tuningPattern->GetPosition() != m_tuningPattern->GetEnd() )
  1731. {
  1732. m_tuningPattern->EditStart( generatorTool, m_board, nullptr );
  1733. m_tuningPattern->Update( generatorTool, m_board, nullptr );
  1734. m_preview.FreeItems();
  1735. for( EDA_ITEM* item : m_tuningPattern->GetPreviewItems( generatorTool, m_frame, true ) )
  1736. m_preview.Add( item );
  1737. m_view->Update( &m_preview );
  1738. }
  1739. };
  1740. // Set initial cursor
  1741. setCursor();
  1742. while( TOOL_EVENT* evt = Wait() )
  1743. {
  1744. setCursor();
  1745. VECTOR2D cursorPos = controls->GetMousePosition();
  1746. if( evt->IsCancelInteractive() || evt->IsActivate()
  1747. || ( m_tuningPattern && evt->IsAction( &ACTIONS::undo ) ) )
  1748. {
  1749. if( m_tuningPattern )
  1750. {
  1751. // First click already made; clean up tuning pattern preview
  1752. m_tuningPattern->EditCancel( generatorTool, m_board, nullptr );
  1753. delete m_tuningPattern;
  1754. m_tuningPattern = nullptr;
  1755. }
  1756. else
  1757. {
  1758. break;
  1759. }
  1760. }
  1761. else if( evt->IsMotion() )
  1762. {
  1763. if( !m_tuningPattern )
  1764. {
  1765. // First click not yet made; we're in highlight-net-under-cursor mode
  1766. GENERAL_COLLECTOR collector;
  1767. collector.m_Threshold = KiROUND( getView()->ToWorld( HITTEST_THRESHOLD_PIXELS ) );
  1768. if( m_frame->GetDisplayOptions().m_ContrastModeDisplay != HIGH_CONTRAST_MODE::NORMAL )
  1769. guide.SetIncludeSecondary( false );
  1770. else
  1771. guide.SetIncludeSecondary( true );
  1772. guide.SetPreferredLayer( m_frame->GetActiveLayer() );
  1773. collector.Collect( board, { PCB_TRACE_T, PCB_ARC_T }, cursorPos, guide );
  1774. if( collector.GetCount() > 1 )
  1775. selectionTool->GuessSelectionCandidates( collector, cursorPos );
  1776. m_pickerItem = nullptr;
  1777. if( collector.GetCount() > 0 )
  1778. {
  1779. double min_dist_sq = std::numeric_limits<double>::max();
  1780. for( EDA_ITEM* candidate : collector )
  1781. {
  1782. VECTOR2I candidatePos;
  1783. if( candidate->Type() == PCB_TRACE_T )
  1784. {
  1785. candidatePos = static_cast<PCB_TRACK*>( candidate )->GetCenter();
  1786. }
  1787. else if( candidate->Type() == PCB_ARC_T )
  1788. {
  1789. candidatePos = static_cast<PCB_ARC*>( candidate )->GetMid();
  1790. }
  1791. double dist_sq = ( cursorPos - candidatePos ).SquaredEuclideanNorm();
  1792. if( dist_sq < min_dist_sq )
  1793. {
  1794. min_dist_sq = dist_sq;
  1795. m_pickerItem = static_cast<BOARD_CONNECTED_ITEM*>( candidate );
  1796. }
  1797. }
  1798. }
  1799. updateHoverStatus();
  1800. }
  1801. else
  1802. {
  1803. // First click already made; we're in preview-tuning-pattern mode
  1804. m_tuningPattern->SetEnd( cursorPos );
  1805. m_tuningPattern->UpdateSideFromEnd();
  1806. updateTuningPattern();
  1807. }
  1808. }
  1809. else if( evt->IsClick( BUT_LEFT ) )
  1810. {
  1811. if( m_pickerItem && !m_tuningPattern )
  1812. {
  1813. // First click; create a tuning pattern
  1814. if( dynamic_cast<PCB_TUNING_PATTERN*>( m_pickerItem->GetParentGroup() ) )
  1815. {
  1816. m_frame->ShowInfoBarWarning( _( "Unable to tune segments inside other "
  1817. "tuning patterns." ) );
  1818. }
  1819. else
  1820. {
  1821. m_preview.FreeItems();
  1822. m_frame->SetActiveLayer( m_pickerItem->GetLayer() );
  1823. m_tuningPattern = PCB_TUNING_PATTERN::CreateNew( generatorTool, m_frame,
  1824. m_pickerItem, mode );
  1825. applyCommonSettings( m_tuningPattern );
  1826. int dummyDist;
  1827. int dummyClearance = std::numeric_limits<int>::max() / 2;
  1828. VECTOR2I closestPt;
  1829. // With an artificially-large clearance this can't *not* collide, but the
  1830. // if stmt keeps Coverity happy....
  1831. if( m_pickerItem->GetEffectiveShape()->Collide( cursorPos, dummyClearance,
  1832. &dummyDist, &closestPt ) )
  1833. {
  1834. m_tuningPattern->SetPosition( closestPt );
  1835. m_tuningPattern->SetEnd( closestPt );
  1836. }
  1837. m_preview.Add( m_tuningPattern->Clone() );
  1838. }
  1839. }
  1840. else if( m_pickerItem && m_tuningPattern )
  1841. {
  1842. // Second click; we're done
  1843. BOARD_COMMIT commit( m_frame );
  1844. m_tuningPattern->EditStart( generatorTool, m_board, &commit );
  1845. m_tuningPattern->Update( generatorTool, m_board, &commit );
  1846. m_tuningPattern->EditFinish( generatorTool, m_board, &commit );
  1847. commit.Push( _( "Tune" ) );
  1848. m_tuningPattern = nullptr;
  1849. m_pickerItem = nullptr;
  1850. }
  1851. }
  1852. else if( evt->IsClick( BUT_RIGHT ) )
  1853. {
  1854. PCB_SELECTION dummy;
  1855. m_menu->ShowContextMenu( dummy );
  1856. }
  1857. else if( evt->IsAction( &PCB_ACTIONS::spacingIncrease )
  1858. || evt->IsAction( &PCB_ACTIONS::spacingDecrease ) )
  1859. {
  1860. if( m_tuningPattern )
  1861. {
  1862. auto* placer = static_cast<PNS::MEANDER_PLACER_BASE*>( router->Placer() );
  1863. placer->SpacingStep( evt->IsAction( &PCB_ACTIONS::spacingIncrease ) ? 1 : -1 );
  1864. m_tuningPattern->SetSpacing( placer->MeanderSettings().m_spacing );
  1865. meanderSettings.m_spacing = placer->MeanderSettings().m_spacing;
  1866. updateTuningPattern();
  1867. }
  1868. else
  1869. {
  1870. m_frame->ShowInfoBarWarning( _( "Select a track to tune first." ) );
  1871. }
  1872. }
  1873. else if( evt->IsAction( &PCB_ACTIONS::amplIncrease )
  1874. || evt->IsAction( &PCB_ACTIONS::amplDecrease ) )
  1875. {
  1876. if( m_tuningPattern )
  1877. {
  1878. auto* placer = static_cast<PNS::MEANDER_PLACER_BASE*>( router->Placer() );
  1879. placer->AmplitudeStep( evt->IsAction( &PCB_ACTIONS::amplIncrease ) ? 1 : -1 );
  1880. m_tuningPattern->SetMaxAmplitude( placer->MeanderSettings().m_maxAmplitude );
  1881. meanderSettings.m_maxAmplitude = placer->MeanderSettings().m_maxAmplitude;
  1882. updateTuningPattern();
  1883. }
  1884. else
  1885. {
  1886. m_frame->ShowInfoBarWarning( _( "Select a track to tune first." ) );
  1887. }
  1888. }
  1889. else if( evt->IsAction( &PCB_ACTIONS::properties )
  1890. || evt->IsAction( &PCB_ACTIONS::lengthTunerSettings ) )
  1891. {
  1892. DRC_CONSTRAINT constraint;
  1893. if( m_tuningPattern )
  1894. {
  1895. if( !m_tuningPattern->GetItems().empty() )
  1896. {
  1897. BOARD_ITEM* startItem = *m_tuningPattern->GetBoardItems().begin();
  1898. constraint = drcEngine->EvalRules( LENGTH_CONSTRAINT, startItem, nullptr,
  1899. startItem->GetLayer() );
  1900. }
  1901. }
  1902. DIALOG_TUNING_PATTERN_PROPERTIES dlg( m_frame, meanderSettings, routerMode, constraint );
  1903. if( dlg.ShowModal() == wxID_OK )
  1904. {
  1905. if( m_tuningPattern )
  1906. applyCommonSettings( m_tuningPattern );
  1907. updateTuningPattern();
  1908. }
  1909. }
  1910. // TODO: It'd be nice to be able to say "don't allow any non-trivial editing actions",
  1911. // but we don't at present have that, so we just knock out some of the egregious ones.
  1912. else if( ZONE_FILLER_TOOL::IsZoneFillAction( evt ) )
  1913. {
  1914. wxBell();
  1915. }
  1916. else
  1917. {
  1918. evt->SetPassEvent();
  1919. }
  1920. controls->CaptureCursor( m_tuningPattern != nullptr );
  1921. controls->SetAutoPan( m_tuningPattern != nullptr );
  1922. }
  1923. controls->CaptureCursor( false );
  1924. controls->SetAutoPan( false );
  1925. controls->ForceCursorPosition( false );
  1926. controls->ShowCursor( false );
  1927. m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
  1928. m_preview.FreeItems();
  1929. m_view->Remove( &m_preview );
  1930. m_frame->GetCanvas()->Refresh();
  1931. if( m_tuningPattern )
  1932. selectionTool->AddItemToSel( m_tuningPattern );
  1933. m_frame->PopTool( aEvent );
  1934. return 0;
  1935. }
  1936. static struct PCB_TUNING_PATTERN_DESC
  1937. {
  1938. PCB_TUNING_PATTERN_DESC()
  1939. {
  1940. ENUM_MAP<LENGTH_TUNING_MODE>::Instance()
  1941. .Map( LENGTH_TUNING_MODE::SINGLE, _HKI( "Single track" ) )
  1942. .Map( LENGTH_TUNING_MODE::DIFF_PAIR, _HKI( "Differential pair" ) )
  1943. .Map( LENGTH_TUNING_MODE::DIFF_PAIR_SKEW, _HKI( "Diff pair skew" ) );
  1944. ENUM_MAP<PNS::MEANDER_SIDE>::Instance()
  1945. .Map( PNS::MEANDER_SIDE_LEFT, _HKI( "Left" ) )
  1946. .Map( PNS::MEANDER_SIDE_RIGHT, _HKI( "Right" ) )
  1947. .Map( PNS::MEANDER_SIDE_DEFAULT, _HKI( "Default" ) );
  1948. PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
  1949. REGISTER_TYPE( PCB_TUNING_PATTERN );
  1950. propMgr.AddTypeCast( new TYPE_CAST<PCB_TUNING_PATTERN, PCB_GENERATOR> );
  1951. propMgr.AddTypeCast( new TYPE_CAST<PCB_TUNING_PATTERN, BOARD_ITEM> );
  1952. propMgr.InheritsAfter( TYPE_HASH( PCB_TUNING_PATTERN ), TYPE_HASH( PCB_GENERATOR ) );
  1953. propMgr.InheritsAfter( TYPE_HASH( PCB_TUNING_PATTERN ), TYPE_HASH( BOARD_ITEM ) );
  1954. ENUM_MAP<PCB_LAYER_ID>& layerEnum = ENUM_MAP<PCB_LAYER_ID>::Instance();
  1955. if( layerEnum.Choices().GetCount() == 0 )
  1956. {
  1957. layerEnum.Undefined( UNDEFINED_LAYER );
  1958. for( PCB_LAYER_ID layer : LSET::AllLayersMask() )
  1959. layerEnum.Map( layer, LSET::Name( layer ) );
  1960. }
  1961. auto layer = new PROPERTY_ENUM<PCB_TUNING_PATTERN, PCB_LAYER_ID>(
  1962. _HKI( "Layer" ), &PCB_TUNING_PATTERN::SetLayer, &PCB_TUNING_PATTERN::GetLayer );
  1963. layer->SetChoices( layerEnum.Choices() );
  1964. propMgr.ReplaceProperty( TYPE_HASH( BOARD_ITEM ), _HKI( "Layer" ), layer );
  1965. propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, int>( _HKI( "Width" ),
  1966. &PCB_TUNING_PATTERN::SetWidth, &PCB_TUNING_PATTERN::GetWidth,
  1967. PROPERTY_DISPLAY::PT_SIZE ) );
  1968. propMgr.AddProperty( new PROPERTY_ENUM<PCB_TUNING_PATTERN, int>( _HKI( "Net" ),
  1969. &PCB_TUNING_PATTERN::SetNetCode,
  1970. &PCB_TUNING_PATTERN::GetNetCode ) );
  1971. const wxString groupTechLayers = _HKI( "Technical Layers" );
  1972. propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, bool>( _HKI( "Soldermask" ),
  1973. &PCB_TUNING_PATTERN::SetHasSolderMask,
  1974. &PCB_TUNING_PATTERN::HasSolderMask ), groupTechLayers );
  1975. propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, std::optional<int>>( _HKI( "Soldermask Margin Override" ),
  1976. &PCB_TUNING_PATTERN::SetLocalSolderMaskMargin,
  1977. &PCB_TUNING_PATTERN::GetLocalSolderMaskMargin,
  1978. PROPERTY_DISPLAY::PT_SIZE ), groupTechLayers );
  1979. const wxString groupTab = _HKI( "Pattern Properties" );
  1980. propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, int>(
  1981. _HKI( "End X" ), &PCB_TUNING_PATTERN::SetEndX,
  1982. &PCB_TUNING_PATTERN::GetEndX, PROPERTY_DISPLAY::PT_SIZE,
  1983. ORIGIN_TRANSFORMS::ABS_X_COORD ),
  1984. groupTab );
  1985. propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, int>(
  1986. _HKI( "End Y" ), &PCB_TUNING_PATTERN::SetEndY,
  1987. &PCB_TUNING_PATTERN::GetEndY, PROPERTY_DISPLAY::PT_SIZE,
  1988. ORIGIN_TRANSFORMS::ABS_Y_COORD ),
  1989. groupTab );
  1990. propMgr.AddProperty( new PROPERTY_ENUM<PCB_TUNING_PATTERN, LENGTH_TUNING_MODE>(
  1991. _HKI( "Tuning Mode" ),
  1992. NO_SETTER( PCB_TUNING_PATTERN, LENGTH_TUNING_MODE ),
  1993. &PCB_TUNING_PATTERN::GetTuningMode ),
  1994. groupTab );
  1995. propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, int>(
  1996. _HKI( "Min Amplitude" ),
  1997. &PCB_TUNING_PATTERN::SetMinAmplitude,
  1998. &PCB_TUNING_PATTERN::GetMinAmplitude,
  1999. PROPERTY_DISPLAY::PT_SIZE, ORIGIN_TRANSFORMS::ABS_X_COORD ),
  2000. groupTab );
  2001. propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, int>(
  2002. _HKI( "Max Amplitude" ),
  2003. &PCB_TUNING_PATTERN::SetMaxAmplitude,
  2004. &PCB_TUNING_PATTERN::GetMaxAmplitude,
  2005. PROPERTY_DISPLAY::PT_SIZE, ORIGIN_TRANSFORMS::ABS_X_COORD ),
  2006. groupTab );
  2007. propMgr.AddProperty( new PROPERTY_ENUM<PCB_TUNING_PATTERN, PNS::MEANDER_SIDE>(
  2008. _HKI( "Initial Side" ),
  2009. &PCB_TUNING_PATTERN::SetInitialSide,
  2010. &PCB_TUNING_PATTERN::GetInitialSide ),
  2011. groupTab );
  2012. propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, int>(
  2013. _HKI( "Min Spacing" ), &PCB_TUNING_PATTERN::SetSpacing,
  2014. &PCB_TUNING_PATTERN::GetSpacing, PROPERTY_DISPLAY::PT_SIZE,
  2015. ORIGIN_TRANSFORMS::ABS_X_COORD ),
  2016. groupTab );
  2017. propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, int>(
  2018. _HKI( "Corner Radius %" ),
  2019. &PCB_TUNING_PATTERN::SetCornerRadiusPercentage,
  2020. &PCB_TUNING_PATTERN::GetCornerRadiusPercentage,
  2021. PROPERTY_DISPLAY::PT_DEFAULT, ORIGIN_TRANSFORMS::NOT_A_COORD ),
  2022. groupTab );
  2023. auto isSkew =
  2024. []( INSPECTABLE* aItem ) -> bool
  2025. {
  2026. if( PCB_TUNING_PATTERN* pattern = dynamic_cast<PCB_TUNING_PATTERN*>( aItem ) )
  2027. return pattern->GetTuningMode() == DIFF_PAIR_SKEW;
  2028. return false;
  2029. };
  2030. auto isTimeDomain = []( INSPECTABLE* aItem ) -> bool
  2031. {
  2032. if( PCB_TUNING_PATTERN* pattern = dynamic_cast<PCB_TUNING_PATTERN*>( aItem ) )
  2033. return pattern->GetSettings().m_isTimeDomain;
  2034. return false;
  2035. };
  2036. auto isLengthIsSpaceDomain = [&]( INSPECTABLE* aItem ) -> bool
  2037. {
  2038. return !isSkew( aItem ) && !isTimeDomain( aItem );
  2039. };
  2040. auto isLengthIsTimeDomain = [&]( INSPECTABLE* aItem ) -> bool
  2041. {
  2042. return !isSkew( aItem ) && isTimeDomain( aItem );
  2043. };
  2044. auto isSkewIsSpaceDomain = [&]( INSPECTABLE* aItem ) -> bool
  2045. {
  2046. return isSkew( aItem ) && !isTimeDomain( aItem );
  2047. };
  2048. auto isSkewIsTimeDomain = [&]( INSPECTABLE* aItem ) -> bool
  2049. {
  2050. return isSkew( aItem ) && isTimeDomain( aItem );
  2051. };
  2052. propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, std::optional<int>>(
  2053. _HKI( "Target Length" ), &PCB_TUNING_PATTERN::SetTargetLength,
  2054. &PCB_TUNING_PATTERN::GetTargetLength, PROPERTY_DISPLAY::PT_SIZE,
  2055. ORIGIN_TRANSFORMS::ABS_X_COORD ),
  2056. groupTab )
  2057. .SetAvailableFunc( isLengthIsSpaceDomain );
  2058. propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, std::optional<int>>(
  2059. _HKI( "Target Delay" ), &PCB_TUNING_PATTERN::SetTargetDelay,
  2060. &PCB_TUNING_PATTERN::GetTargetDelay, PROPERTY_DISPLAY::PT_TIME,
  2061. ORIGIN_TRANSFORMS::NOT_A_COORD ),
  2062. groupTab )
  2063. .SetAvailableFunc( isLengthIsTimeDomain );
  2064. propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, int>(
  2065. _HKI( "Target Skew" ), &PCB_TUNING_PATTERN::SetTargetSkew,
  2066. &PCB_TUNING_PATTERN::GetTargetSkew, PROPERTY_DISPLAY::PT_SIZE,
  2067. ORIGIN_TRANSFORMS::ABS_X_COORD ),
  2068. groupTab )
  2069. .SetAvailableFunc( isSkewIsSpaceDomain );
  2070. propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, int>(
  2071. _HKI( "Target Skew Delay" ), &PCB_TUNING_PATTERN::SetTargetSkewDelay,
  2072. &PCB_TUNING_PATTERN::GetTargetSkewDelay, PROPERTY_DISPLAY::PT_TIME,
  2073. ORIGIN_TRANSFORMS::NOT_A_COORD ),
  2074. groupTab )
  2075. .SetAvailableFunc( isSkewIsTimeDomain );
  2076. propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, bool>(
  2077. _HKI( "Override Custom Rules" ),
  2078. &PCB_TUNING_PATTERN::SetOverrideCustomRules,
  2079. &PCB_TUNING_PATTERN::GetOverrideCustomRules ),
  2080. groupTab );
  2081. propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, bool>(
  2082. _HKI( "Single-sided" ),
  2083. &PCB_TUNING_PATTERN::SetSingleSided,
  2084. &PCB_TUNING_PATTERN::IsSingleSided ),
  2085. groupTab );
  2086. propMgr.AddProperty( new PROPERTY<PCB_TUNING_PATTERN, bool>(
  2087. _HKI( "Rounded" ), &PCB_TUNING_PATTERN::SetRounded,
  2088. &PCB_TUNING_PATTERN::IsRounded ),
  2089. groupTab );
  2090. }
  2091. } _PCB_TUNING_PATTERN_DESC;
  2092. ENUM_TO_WXANY( LENGTH_TUNING_MODE )
  2093. ENUM_TO_WXANY( PNS::MEANDER_SIDE )
  2094. static GENERATORS_MGR::REGISTER<PCB_TUNING_PATTERN> registerMe;
  2095. // Also register under the 7.99 name
  2096. template <typename T>
  2097. struct REGISTER_LEGACY_TUNING_PATTERN
  2098. {
  2099. REGISTER_LEGACY_TUNING_PATTERN()
  2100. {
  2101. GENERATORS_MGR::Instance().Register( wxS( "meanders" ), T::DISPLAY_NAME,
  2102. []()
  2103. {
  2104. return new T;
  2105. } );
  2106. }
  2107. };
  2108. static REGISTER_LEGACY_TUNING_PATTERN<PCB_TUNING_PATTERN> registerMeToo;