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.

2453 lines
70 KiB

18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
4 months ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
17 years ago
17 years ago
18 years ago
18 years ago
7 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
Introduction of Graphics Abstraction Layer based rendering for pcbnew. New classes: - VIEW - represents view that is seen by user, takes care of layer ordering & visibility and how it is displayed (which location, how much zoomed, etc.) - VIEW_ITEM - Base class for every item that can be displayed on VIEW (the biggest change is that now it may be necessary to override ViewBBox & ViewGetLayers method for derived classes). - EDA_DRAW_PANEL_GAL - Inherits after EDA_DRAW_PANEL, displays VIEW output, right now it is not editable (in opposite to usual EDA_DRAW_PANEL). - GAL/OPENGL_GAL/CAIRO_GAL - Base Graphics Abstraction Layer class + two different flavours (Cairo is not fully supported yet), that offers methods to draw primitives using different libraries. - WX_VIEW_CONTROLS - Controller for VIEW, handles user events, allows zooming, panning, etc. - PAINTER/PCB_PAINTER - Classes that uses GAL interface to draw items (as you may have already guessed - PCB_PAINTER is a class for drawing PCB specific object, PAINTER is an abstract class). Its methods are invoked by VIEW, when an item has to be drawn. To display a new type of item - you need to implement draw(ITEM_TYPE*) method that draws it using GAL methods. - STROKE_FONT - Implements stroke font drawing using GAL methods. Most important changes to Kicad original code: * EDA_ITEM now inherits from VIEW_ITEM, which is a base class for all drawable objects. * EDA_DRAW_FRAME contains both usual EDA_DRAW_PANEL and new EDA_DRAW_PANEL_GAL, that can be switched anytime. * There are some new layers for displaying multilayer pads, vias & pads holes (these are not shown yet on the right sidebar in pcbnew) * Display order of layers is different than in previous versions (if you are curious - you may check m_galLayerOrder@pcbnew/basepcbframe.cpp). Preserving usual order would result in not very natural display, such as showing silkscreen texts on the bottom. * Introduced new hotkey (Alt+F12) and new menu option (View->Switch canvas) for switching canvas during runtime. * Some of classes (mostly derived from BOARD_ITEM) now includes ViewBBox & ViewGetLayers methods. * Removed tools/class_painter.h, as now it is extended and included in source code. Build changes: * GAL-based rendering option is turned on by a new compilation CMake option KICAD_GAL. * When compiling with CMake option KICAD_GAL=ON, GLEW and Cairo libraries are required. * GAL-related code is compiled into a static library (common/libgal). * Build with KICAD_GAL=OFF should not need any new libraries and should come out as a standard version of Kicad Currently most of items in pcbnew can be displayed using OpenGL (to be done are DIMENSIONS and MARKERS). More details about GAL can be found in: http://www.ohwr.org/attachments/1884/view-spec.pdf
13 years ago
Introduction of Graphics Abstraction Layer based rendering for pcbnew. New classes: - VIEW - represents view that is seen by user, takes care of layer ordering & visibility and how it is displayed (which location, how much zoomed, etc.) - VIEW_ITEM - Base class for every item that can be displayed on VIEW (the biggest change is that now it may be necessary to override ViewBBox & ViewGetLayers method for derived classes). - EDA_DRAW_PANEL_GAL - Inherits after EDA_DRAW_PANEL, displays VIEW output, right now it is not editable (in opposite to usual EDA_DRAW_PANEL). - GAL/OPENGL_GAL/CAIRO_GAL - Base Graphics Abstraction Layer class + two different flavours (Cairo is not fully supported yet), that offers methods to draw primitives using different libraries. - WX_VIEW_CONTROLS - Controller for VIEW, handles user events, allows zooming, panning, etc. - PAINTER/PCB_PAINTER - Classes that uses GAL interface to draw items (as you may have already guessed - PCB_PAINTER is a class for drawing PCB specific object, PAINTER is an abstract class). Its methods are invoked by VIEW, when an item has to be drawn. To display a new type of item - you need to implement draw(ITEM_TYPE*) method that draws it using GAL methods. - STROKE_FONT - Implements stroke font drawing using GAL methods. Most important changes to Kicad original code: * EDA_ITEM now inherits from VIEW_ITEM, which is a base class for all drawable objects. * EDA_DRAW_FRAME contains both usual EDA_DRAW_PANEL and new EDA_DRAW_PANEL_GAL, that can be switched anytime. * There are some new layers for displaying multilayer pads, vias & pads holes (these are not shown yet on the right sidebar in pcbnew) * Display order of layers is different than in previous versions (if you are curious - you may check m_galLayerOrder@pcbnew/basepcbframe.cpp). Preserving usual order would result in not very natural display, such as showing silkscreen texts on the bottom. * Introduced new hotkey (Alt+F12) and new menu option (View->Switch canvas) for switching canvas during runtime. * Some of classes (mostly derived from BOARD_ITEM) now includes ViewBBox & ViewGetLayers methods. * Removed tools/class_painter.h, as now it is extended and included in source code. Build changes: * GAL-based rendering option is turned on by a new compilation CMake option KICAD_GAL. * When compiling with CMake option KICAD_GAL=ON, GLEW and Cairo libraries are required. * GAL-related code is compiled into a static library (common/libgal). * Build with KICAD_GAL=OFF should not need any new libraries and should come out as a standard version of Kicad Currently most of items in pcbnew can be displayed using OpenGL (to be done are DIMENSIONS and MARKERS). More details about GAL can be found in: http://www.ohwr.org/attachments/1884/view-spec.pdf
13 years ago
18 years ago
4 years ago
4 years ago
4 years ago
4 years ago
18 years ago
18 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2012 Jean-Pierre Charras, jp.charras at wanadoo.fr
  5. * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
  6. * Copyright (C) 2012 Wayne Stambaugh <stambaughw@gmail.com>
  7. * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
  8. *
  9. * This program is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU General Public License
  11. * as published by the Free Software Foundation; either version 2
  12. * of the License, or (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program; if not, you may find one here:
  21. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  22. * or you may search the http://www.gnu.org website for the version 2 license,
  23. * or you may write to the Free Software Foundation, Inc.,
  24. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  25. */
  26. #include "pcb_track.h"
  27. #include <pcb_base_frame.h>
  28. #include <core/mirror.h>
  29. #include <connectivity/connectivity_data.h>
  30. #include <board.h>
  31. #include <board_design_settings.h>
  32. #include <convert_basic_shapes_to_polygon.h>
  33. #include <base_units.h>
  34. #include <layer_range.h>
  35. #include <length_delay_calculation/length_delay_calculation.h>
  36. #include <lset.h>
  37. #include <string_utils.h>
  38. #include <view/view.h>
  39. #include <settings/color_settings.h>
  40. #include <settings/settings_manager.h>
  41. #include <geometry/geometry_utils.h>
  42. #include <geometry/seg.h>
  43. #include <geometry/shape_segment.h>
  44. #include <geometry/shape_circle.h>
  45. #include <geometry/shape_arc.h>
  46. #include <drc/drc_engine.h>
  47. #include <pcb_painter.h>
  48. #include <trigo.h>
  49. #include <google/protobuf/any.pb.h>
  50. #include <api/api_enums.h>
  51. #include <api/api_utils.h>
  52. #include <api/api_pcb_utils.h>
  53. #include <api/board/board_types.pb.h>
  54. using KIGFX::PCB_PAINTER;
  55. using KIGFX::PCB_RENDER_SETTINGS;
  56. PCB_TRACK::PCB_TRACK( BOARD_ITEM* aParent, KICAD_T idtype ) :
  57. BOARD_CONNECTED_ITEM( aParent, idtype )
  58. {
  59. m_width = pcbIUScale.mmToIU( 0.2 ); // Gives a reasonable default width
  60. m_hasSolderMask = false;
  61. }
  62. EDA_ITEM* PCB_TRACK::Clone() const
  63. {
  64. return new PCB_TRACK( *this );
  65. }
  66. void PCB_TRACK::CopyFrom( const BOARD_ITEM* aOther )
  67. {
  68. wxCHECK( aOther && aOther->Type() == PCB_TRACE_T, /* void */ );
  69. *this = *static_cast<const PCB_TRACK*>( aOther );
  70. }
  71. PCB_ARC::PCB_ARC( BOARD_ITEM* aParent, const SHAPE_ARC* aArc ) :
  72. PCB_TRACK( aParent, PCB_ARC_T )
  73. {
  74. m_Start = aArc->GetP0();
  75. m_End = aArc->GetP1();
  76. m_Mid = aArc->GetArcMid();
  77. }
  78. EDA_ITEM* PCB_ARC::Clone() const
  79. {
  80. return new PCB_ARC( *this );
  81. }
  82. void PCB_ARC::CopyFrom( const BOARD_ITEM* aOther )
  83. {
  84. wxCHECK( aOther && aOther->Type() == PCB_ARC_T, /* void */ );
  85. *this = *static_cast<const PCB_ARC*>( aOther );
  86. }
  87. PCB_VIA::PCB_VIA( BOARD_ITEM* aParent ) :
  88. PCB_TRACK( aParent, PCB_VIA_T ),
  89. m_padStack( this )
  90. {
  91. SetViaType( VIATYPE::THROUGH );
  92. Padstack().Drill().start = F_Cu;
  93. Padstack().Drill().end = B_Cu;
  94. SetDrillDefault();
  95. m_padStack.SetUnconnectedLayerMode( PADSTACK::UNCONNECTED_LAYER_MODE::KEEP_ALL );
  96. // Padstack layerset is not used for vias right now
  97. m_padStack.LayerSet().reset();
  98. // For now, vias are always circles
  99. m_padStack.SetShape( PAD_SHAPE::CIRCLE, PADSTACK::ALL_LAYERS );
  100. for( PCB_LAYER_ID layer : LAYER_RANGE( F_Cu, B_Cu, BoardCopperLayerCount() ) )
  101. m_zoneLayerOverrides[layer] = ZLO_NONE;
  102. m_isFree = false;
  103. }
  104. PCB_VIA::PCB_VIA( const PCB_VIA& aOther ) :
  105. PCB_TRACK( aOther.GetParent(), PCB_VIA_T ),
  106. m_padStack( this )
  107. {
  108. PCB_VIA::operator=( aOther );
  109. const_cast<KIID&>( m_Uuid ) = aOther.m_Uuid;
  110. m_zoneLayerOverrides = aOther.m_zoneLayerOverrides;
  111. }
  112. PCB_VIA& PCB_VIA::operator=( const PCB_VIA &aOther )
  113. {
  114. BOARD_CONNECTED_ITEM::operator=( aOther );
  115. m_Start = aOther.m_Start;
  116. m_End = aOther.m_End;
  117. m_viaType = aOther.m_viaType;
  118. m_padStack = aOther.m_padStack;
  119. m_isFree = aOther.m_isFree;
  120. return *this;
  121. }
  122. void PCB_VIA::CopyFrom( const BOARD_ITEM* aOther )
  123. {
  124. wxCHECK( aOther && aOther->Type() == PCB_VIA_T, /* void */ );
  125. *this = *static_cast<const PCB_VIA*>( aOther );
  126. }
  127. EDA_ITEM* PCB_VIA::Clone() const
  128. {
  129. return new PCB_VIA( *this );
  130. }
  131. wxString PCB_VIA::GetItemDescription( UNITS_PROVIDER* aUnitsProvider, bool aFull ) const
  132. {
  133. wxString formatStr;
  134. switch( GetViaType() )
  135. {
  136. case VIATYPE::BLIND_BURIED: formatStr = _( "Blind/Buried Via %s on %s" ); break;
  137. case VIATYPE::MICROVIA: formatStr = _( "Micro Via %s on %s" ); break;
  138. default: formatStr = _( "Via %s on %s" ); break;
  139. }
  140. return wxString::Format( formatStr, GetNetnameMsg(), layerMaskDescribe() );
  141. }
  142. BITMAPS PCB_VIA::GetMenuImage() const
  143. {
  144. return BITMAPS::via;
  145. }
  146. bool PCB_TRACK::operator==( const BOARD_ITEM& aBoardItem ) const
  147. {
  148. if( aBoardItem.Type() != Type() )
  149. return false;
  150. const PCB_TRACK& other = static_cast<const PCB_TRACK&>( aBoardItem );
  151. return *this == other;
  152. }
  153. bool PCB_TRACK::operator==( const PCB_TRACK& aOther ) const
  154. {
  155. return m_Start == aOther.m_Start
  156. && m_End == aOther.m_End
  157. && m_layer == aOther.m_layer
  158. && m_width == aOther.m_width
  159. && m_hasSolderMask == aOther.m_hasSolderMask
  160. && m_solderMaskMargin == aOther.m_solderMaskMargin;
  161. }
  162. double PCB_TRACK::Similarity( const BOARD_ITEM& aOther ) const
  163. {
  164. if( aOther.Type() != Type() )
  165. return 0.0;
  166. const PCB_TRACK& other = static_cast<const PCB_TRACK&>( aOther );
  167. double similarity = 1.0;
  168. if( m_layer != other.m_layer )
  169. similarity *= 0.9;
  170. if( m_width != other.m_width )
  171. similarity *= 0.9;
  172. if( m_Start != other.m_Start )
  173. similarity *= 0.9;
  174. if( m_End != other.m_End )
  175. similarity *= 0.9;
  176. if( m_hasSolderMask != other.m_hasSolderMask )
  177. similarity *= 0.9;
  178. if( m_solderMaskMargin != other.m_solderMaskMargin )
  179. similarity *= 0.9;
  180. return similarity;
  181. }
  182. bool PCB_ARC::operator==( const BOARD_ITEM& aBoardItem ) const
  183. {
  184. if( aBoardItem.Type() != Type() )
  185. return false;
  186. const PCB_ARC& other = static_cast<const PCB_ARC&>( aBoardItem );
  187. return *this == other;
  188. }
  189. bool PCB_ARC::operator==( const PCB_TRACK& aOther ) const
  190. {
  191. if( aOther.Type() != Type() )
  192. return false;
  193. const PCB_ARC& other = static_cast<const PCB_ARC&>( aOther );
  194. return *this == other;
  195. }
  196. bool PCB_ARC::operator==( const PCB_ARC& aOther ) const
  197. {
  198. return m_Start == aOther.m_Start
  199. && m_End == aOther.m_End
  200. && m_Mid == aOther.m_Mid
  201. && m_layer == aOther.m_layer
  202. && GetWidth() == aOther.GetWidth()
  203. && m_hasSolderMask == aOther.m_hasSolderMask
  204. && m_solderMaskMargin == aOther.m_solderMaskMargin;
  205. }
  206. double PCB_ARC::Similarity( const BOARD_ITEM& aOther ) const
  207. {
  208. if( aOther.Type() != Type() )
  209. return 0.0;
  210. const PCB_ARC& other = static_cast<const PCB_ARC&>( aOther );
  211. double similarity = 1.0;
  212. if( m_layer != other.m_layer )
  213. similarity *= 0.9;
  214. if( GetWidth() != other.GetWidth() )
  215. similarity *= 0.9;
  216. if( m_Start != other.m_Start )
  217. similarity *= 0.9;
  218. if( m_End != other.m_End )
  219. similarity *= 0.9;
  220. if( m_Mid != other.m_Mid )
  221. similarity *= 0.9;
  222. if( m_hasSolderMask != other.m_hasSolderMask )
  223. similarity *= 0.9;
  224. if( m_solderMaskMargin != other.m_solderMaskMargin )
  225. similarity *= 0.9;
  226. return similarity;
  227. }
  228. bool PCB_VIA::operator==( const BOARD_ITEM& aBoardItem ) const
  229. {
  230. if( aBoardItem.Type() != Type() )
  231. return false;
  232. const PCB_VIA& other = static_cast<const PCB_VIA&>( aBoardItem );
  233. return *this == other;
  234. }
  235. bool PCB_VIA::operator==( const PCB_TRACK& aOther ) const
  236. {
  237. if( aOther.Type() != Type() )
  238. return false;
  239. const PCB_VIA& other = static_cast<const PCB_VIA&>( aOther );
  240. return *this == other;
  241. }
  242. bool PCB_VIA::operator==( const PCB_VIA& aOther ) const
  243. {
  244. return m_Start == aOther.m_Start
  245. && m_End == aOther.m_End
  246. && m_layer == aOther.m_layer
  247. && m_padStack == aOther.m_padStack
  248. && m_viaType == aOther.m_viaType
  249. && m_zoneLayerOverrides == aOther.m_zoneLayerOverrides;
  250. }
  251. double PCB_VIA::Similarity( const BOARD_ITEM& aOther ) const
  252. {
  253. if( aOther.Type() != Type() )
  254. return 0.0;
  255. const PCB_VIA& other = static_cast<const PCB_VIA&>( aOther );
  256. double similarity = 1.0;
  257. if( m_layer != other.m_layer )
  258. similarity *= 0.9;
  259. if( m_Start != other.m_Start )
  260. similarity *= 0.9;
  261. if( m_End != other.m_End )
  262. similarity *= 0.9;
  263. if( m_padStack != other.m_padStack )
  264. similarity *= 0.9;
  265. if( m_viaType != other.m_viaType )
  266. similarity *= 0.9;
  267. if( m_zoneLayerOverrides != other.m_zoneLayerOverrides )
  268. similarity *= 0.9;
  269. return similarity;
  270. }
  271. void PCB_VIA::SetWidth( int aWidth )
  272. {
  273. m_padStack.SetSize( { aWidth, aWidth }, PADSTACK::ALL_LAYERS );
  274. }
  275. int PCB_VIA::GetWidth() const
  276. {
  277. return m_padStack.Size( PADSTACK::ALL_LAYERS ).x;
  278. }
  279. void PCB_VIA::SetWidth( PCB_LAYER_ID aLayer, int aWidth )
  280. {
  281. m_padStack.SetSize( { aWidth, aWidth }, aLayer );
  282. }
  283. int PCB_VIA::GetWidth( PCB_LAYER_ID aLayer ) const
  284. {
  285. return m_padStack.Size( aLayer ).x;
  286. }
  287. void PCB_TRACK::Serialize( google::protobuf::Any &aContainer ) const
  288. {
  289. kiapi::board::types::Track track;
  290. track.mutable_id()->set_value( m_Uuid.AsStdString() );
  291. track.mutable_start()->set_x_nm( GetStart().x );
  292. track.mutable_start()->set_y_nm( GetStart().y );
  293. track.mutable_end()->set_x_nm( GetEnd().x );
  294. track.mutable_end()->set_y_nm( GetEnd().y );
  295. track.mutable_width()->set_value_nm( GetWidth() );
  296. track.set_layer( ToProtoEnum<PCB_LAYER_ID, kiapi::board::types::BoardLayer>( GetLayer() ) );
  297. track.set_locked( IsLocked() ? kiapi::common::types::LockedState::LS_LOCKED
  298. : kiapi::common::types::LockedState::LS_UNLOCKED );
  299. PackNet( track.mutable_net() );
  300. // TODO m_hasSolderMask and m_solderMaskMargin
  301. aContainer.PackFrom( track );
  302. }
  303. bool PCB_TRACK::Deserialize( const google::protobuf::Any &aContainer )
  304. {
  305. kiapi::board::types::Track track;
  306. if( !aContainer.UnpackTo( &track ) )
  307. return false;
  308. const_cast<KIID&>( m_Uuid ) = KIID( track.id().value() );
  309. SetStart( VECTOR2I( track.start().x_nm(), track.start().y_nm() ) );
  310. SetEnd( VECTOR2I( track.end().x_nm(), track.end().y_nm() ) );
  311. SetWidth( track.width().value_nm() );
  312. SetLayer( FromProtoEnum<PCB_LAYER_ID, kiapi::board::types::BoardLayer>( track.layer() ) );
  313. UnpackNet( track.net() );
  314. SetLocked( track.locked() == kiapi::common::types::LockedState::LS_LOCKED );
  315. // TODO m_hasSolderMask and m_solderMaskMargin
  316. return true;
  317. }
  318. void PCB_ARC::Serialize( google::protobuf::Any &aContainer ) const
  319. {
  320. kiapi::board::types::Arc arc;
  321. arc.mutable_id()->set_value( m_Uuid.AsStdString() );
  322. arc.mutable_start()->set_x_nm( GetStart().x );
  323. arc.mutable_start()->set_y_nm( GetStart().y );
  324. arc.mutable_mid()->set_x_nm( GetMid().x );
  325. arc.mutable_mid()->set_y_nm( GetMid().y );
  326. arc.mutable_end()->set_x_nm( GetEnd().x );
  327. arc.mutable_end()->set_y_nm( GetEnd().y );
  328. arc.mutable_width()->set_value_nm( GetWidth() );
  329. arc.set_layer( ToProtoEnum<PCB_LAYER_ID, kiapi::board::types::BoardLayer>( GetLayer() ) );
  330. arc.set_locked( IsLocked() ? kiapi::common::types::LockedState::LS_LOCKED
  331. : kiapi::common::types::LockedState::LS_UNLOCKED );
  332. PackNet( arc.mutable_net() );
  333. // TODO m_hasSolderMask and m_solderMaskMargin
  334. aContainer.PackFrom( arc );
  335. }
  336. bool PCB_ARC::Deserialize( const google::protobuf::Any &aContainer )
  337. {
  338. kiapi::board::types::Arc arc;
  339. if( !aContainer.UnpackTo( &arc ) )
  340. return false;
  341. const_cast<KIID&>( m_Uuid ) = KIID( arc.id().value() );
  342. SetStart( VECTOR2I( arc.start().x_nm(), arc.start().y_nm() ) );
  343. SetMid( VECTOR2I( arc.mid().x_nm(), arc.mid().y_nm() ) );
  344. SetEnd( VECTOR2I( arc.end().x_nm(), arc.end().y_nm() ) );
  345. SetWidth( arc.width().value_nm() );
  346. SetLayer( FromProtoEnum<PCB_LAYER_ID, kiapi::board::types::BoardLayer>( arc.layer() ) );
  347. UnpackNet( arc.net() );
  348. SetLocked( arc.locked() == kiapi::common::types::LockedState::LS_LOCKED );
  349. // TODO m_hasSolderMask and m_solderMaskMargin
  350. return true;
  351. }
  352. void PCB_VIA::Serialize( google::protobuf::Any &aContainer ) const
  353. {
  354. kiapi::board::types::Via via;
  355. via.mutable_id()->set_value( m_Uuid.AsStdString() );
  356. via.mutable_position()->set_x_nm( GetPosition().x );
  357. via.mutable_position()->set_y_nm( GetPosition().y );
  358. PADSTACK padstack = Padstack();
  359. google::protobuf::Any padStackWrapper;
  360. padstack.Serialize( padStackWrapper );
  361. padStackWrapper.UnpackTo( via.mutable_pad_stack() );
  362. // PADSTACK::m_layerSet is not used by vias
  363. via.mutable_pad_stack()->clear_layers();
  364. kiapi::board::PackLayerSet( *via.mutable_pad_stack()->mutable_layers(), GetLayerSet() );
  365. via.set_type( ToProtoEnum<VIATYPE, kiapi::board::types::ViaType>( GetViaType() ) );
  366. via.set_locked( IsLocked() ? kiapi::common::types::LockedState::LS_LOCKED
  367. : kiapi::common::types::LockedState::LS_UNLOCKED );
  368. PackNet( via.mutable_net() );
  369. aContainer.PackFrom( via );
  370. }
  371. bool PCB_VIA::Deserialize( const google::protobuf::Any &aContainer )
  372. {
  373. kiapi::board::types::Via via;
  374. if( !aContainer.UnpackTo( &via ) )
  375. return false;
  376. const_cast<KIID&>( m_Uuid ) = KIID( via.id().value() );
  377. SetStart( VECTOR2I( via.position().x_nm(), via.position().y_nm() ) );
  378. SetEnd( GetStart() );
  379. google::protobuf::Any padStackWrapper;
  380. padStackWrapper.PackFrom( via.pad_stack() );
  381. if( !m_padStack.Deserialize( padStackWrapper ) )
  382. return false;
  383. // PADSTACK::m_layerSet is not used by vias
  384. m_padStack.LayerSet().reset();
  385. SetViaType( FromProtoEnum<VIATYPE>( via.type() ) );
  386. UnpackNet( via.net() );
  387. SetLocked( via.locked() == kiapi::common::types::LockedState::LS_LOCKED );
  388. return true;
  389. }
  390. bool PCB_TRACK::ApproxCollinear( const PCB_TRACK& aTrack )
  391. {
  392. SEG a( m_Start, m_End );
  393. SEG b( aTrack.GetStart(), aTrack.GetEnd() );
  394. return a.ApproxCollinear( b );
  395. }
  396. MINOPTMAX<int> PCB_TRACK::GetWidthConstraint( wxString* aSource ) const
  397. {
  398. DRC_CONSTRAINT constraint;
  399. if( GetBoard() && GetBoard()->GetDesignSettings().m_DRCEngine )
  400. {
  401. BOARD_DESIGN_SETTINGS& bds = GetBoard()->GetDesignSettings();
  402. constraint = bds.m_DRCEngine->EvalRules( TRACK_WIDTH_CONSTRAINT, this, nullptr, m_layer );
  403. }
  404. if( aSource )
  405. *aSource = constraint.GetName();
  406. return constraint.Value();
  407. }
  408. MINOPTMAX<int> PCB_VIA::GetWidthConstraint( wxString* aSource ) const
  409. {
  410. DRC_CONSTRAINT constraint;
  411. if( GetBoard() && GetBoard()->GetDesignSettings().m_DRCEngine )
  412. {
  413. BOARD_DESIGN_SETTINGS& bds = GetBoard()->GetDesignSettings();
  414. constraint = bds.m_DRCEngine->EvalRules( VIA_DIAMETER_CONSTRAINT, this, nullptr, m_layer );
  415. }
  416. if( aSource )
  417. *aSource = constraint.GetName();
  418. return constraint.Value();
  419. }
  420. MINOPTMAX<int> PCB_VIA::GetDrillConstraint( wxString* aSource ) const
  421. {
  422. DRC_CONSTRAINT constraint;
  423. if( GetBoard() && GetBoard()->GetDesignSettings().m_DRCEngine )
  424. {
  425. BOARD_DESIGN_SETTINGS& bds = GetBoard()->GetDesignSettings();
  426. constraint = bds.m_DRCEngine->EvalRules( HOLE_SIZE_CONSTRAINT, this, nullptr, m_layer );
  427. }
  428. if( aSource )
  429. *aSource = constraint.GetName();
  430. return constraint.Value();
  431. }
  432. int PCB_VIA::GetMinAnnulus( PCB_LAYER_ID aLayer, wxString* aSource ) const
  433. {
  434. if( !FlashLayer( aLayer ) )
  435. {
  436. if( aSource )
  437. *aSource = _( "removed annular ring" );
  438. return 0;
  439. }
  440. DRC_CONSTRAINT constraint;
  441. if( GetBoard() && GetBoard()->GetDesignSettings().m_DRCEngine )
  442. {
  443. BOARD_DESIGN_SETTINGS& bds = GetBoard()->GetDesignSettings();
  444. constraint = bds.m_DRCEngine->EvalRules( ANNULAR_WIDTH_CONSTRAINT, this, nullptr, aLayer );
  445. }
  446. if( constraint.Value().HasMin() )
  447. {
  448. if( aSource )
  449. *aSource = constraint.GetName();
  450. return constraint.Value().Min();
  451. }
  452. return 0;
  453. }
  454. int PCB_VIA::GetDrillValue() const
  455. {
  456. if( m_padStack.Drill().size.x > 0 ) // Use the specific value.
  457. return m_padStack.Drill().size.x;
  458. // Use the default value from the Netclass
  459. NETCLASS* netclass = GetEffectiveNetClass();
  460. if( GetViaType() == VIATYPE::MICROVIA )
  461. return netclass->GetuViaDrill();
  462. return netclass->GetViaDrill();
  463. }
  464. EDA_ITEM_FLAGS PCB_TRACK::IsPointOnEnds( const VECTOR2I& point, int min_dist ) const
  465. {
  466. EDA_ITEM_FLAGS result = 0;
  467. if( min_dist < 0 )
  468. min_dist = m_width / 2;
  469. if( min_dist == 0 )
  470. {
  471. if( m_Start == point )
  472. result |= STARTPOINT;
  473. if( m_End == point )
  474. result |= ENDPOINT;
  475. }
  476. else
  477. {
  478. double dist = m_Start.Distance( point );
  479. if( min_dist >= dist )
  480. result |= STARTPOINT;
  481. dist = m_End.Distance( point );
  482. if( min_dist >= dist )
  483. result |= ENDPOINT;
  484. }
  485. return result;
  486. }
  487. const BOX2I PCB_TRACK::GetBoundingBox() const
  488. {
  489. // end of track is round, this is its radius, rounded up
  490. int radius = ( m_width + 1 ) / 2;
  491. int ymax, xmax, ymin, xmin;
  492. if( Type() == PCB_VIA_T )
  493. {
  494. ymax = m_Start.y;
  495. xmax = m_Start.x;
  496. ymin = m_Start.y;
  497. xmin = m_Start.x;
  498. }
  499. else if( Type() == PCB_ARC_T )
  500. {
  501. std::shared_ptr<SHAPE> arc = GetEffectiveShape();
  502. BOX2I bbox = arc->BBox();
  503. xmin = bbox.GetLeft();
  504. xmax = bbox.GetRight();
  505. ymin = bbox.GetTop();
  506. ymax = bbox.GetBottom();
  507. }
  508. else
  509. {
  510. ymax = std::max( m_Start.y, m_End.y );
  511. xmax = std::max( m_Start.x, m_End.x );
  512. ymin = std::min( m_Start.y, m_End.y );
  513. xmin = std::min( m_Start.x, m_End.x );
  514. }
  515. ymax += radius;
  516. xmax += radius;
  517. ymin -= radius;
  518. xmin -= radius;
  519. // return a rectangle which is [pos,dim) in nature. therefore the +1
  520. return BOX2ISafe( VECTOR2I( xmin, ymin ),
  521. VECTOR2L( (int64_t) xmax - xmin + 1, (int64_t) ymax - ymin + 1 ) );
  522. }
  523. const BOX2I PCB_VIA::GetBoundingBox() const
  524. {
  525. int radius = 0;
  526. Padstack().ForEachUniqueLayer(
  527. [&]( PCB_LAYER_ID aLayer )
  528. {
  529. radius = std::max( radius, GetWidth( aLayer ) );
  530. } );
  531. // via is round, this is its radius, rounded up
  532. radius = ( radius + 1 ) / 2;
  533. int ymax = m_Start.y + radius;
  534. int xmax = m_Start.x + radius;
  535. int ymin = m_Start.y - radius;
  536. int xmin = m_Start.x - radius;
  537. // return a rectangle which is [pos,dim) in nature. therefore the +1
  538. return BOX2ISafe( VECTOR2I( xmin, ymin ),
  539. VECTOR2L( (int64_t) xmax - xmin + 1, (int64_t) ymax - ymin + 1 ) );
  540. }
  541. const BOX2I PCB_VIA::GetBoundingBox( PCB_LAYER_ID aLayer ) const
  542. {
  543. int radius = GetWidth( aLayer );
  544. // via is round, this is its radius, rounded up
  545. radius = ( radius + 1 ) / 2;
  546. int ymax = m_Start.y + radius;
  547. int xmax = m_Start.x + radius;
  548. int ymin = m_Start.y - radius;
  549. int xmin = m_Start.x - radius;
  550. // return a rectangle which is [pos,dim) in nature. therefore the +1
  551. return BOX2ISafe( VECTOR2I( xmin, ymin ),
  552. VECTOR2L( (int64_t) xmax - xmin + 1, (int64_t) ymax - ymin + 1 ) );
  553. }
  554. double PCB_TRACK::GetLength() const
  555. {
  556. return m_Start.Distance( m_End );
  557. }
  558. double PCB_TRACK::GetDelay() const
  559. {
  560. const BOARD* board = GetBoard();
  561. if( !board )
  562. return 0.0;
  563. const LENGTH_DELAY_CALCULATION* calc = board->GetLengthCalculation();
  564. std::vector<LENGTH_DELAY_CALCULATION_ITEM> items{ calc->GetLengthCalculationItem( this ) };
  565. constexpr PATH_OPTIMISATIONS opts = { .OptimiseViaLayers = false,
  566. .MergeTracks = false,
  567. .OptimiseTracesInPads = false,
  568. .InferViaInPad = false
  569. };
  570. return (double) calc->CalculateDelay( items, opts );
  571. }
  572. void PCB_TRACK::Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
  573. {
  574. RotatePoint( m_Start, aRotCentre, aAngle );
  575. RotatePoint( m_End, aRotCentre, aAngle );
  576. }
  577. void PCB_ARC::Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
  578. {
  579. RotatePoint( m_Start, aRotCentre, aAngle );
  580. RotatePoint( m_End, aRotCentre, aAngle );
  581. RotatePoint( m_Mid, aRotCentre, aAngle );
  582. }
  583. void PCB_TRACK::Mirror( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
  584. {
  585. MIRROR( m_Start, aCentre, aFlipDirection );
  586. MIRROR( m_End, aCentre, aFlipDirection );
  587. }
  588. void PCB_ARC::Mirror( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
  589. {
  590. MIRROR( m_Start, aCentre, aFlipDirection );
  591. MIRROR( m_End, aCentre, aFlipDirection );
  592. MIRROR( m_Mid, aCentre, aFlipDirection );
  593. }
  594. void PCB_TRACK::Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
  595. {
  596. if( aFlipDirection == FLIP_DIRECTION::LEFT_RIGHT )
  597. {
  598. m_Start.x = aCentre.x - ( m_Start.x - aCentre.x );
  599. m_End.x = aCentre.x - ( m_End.x - aCentre.x );
  600. }
  601. else
  602. {
  603. m_Start.y = aCentre.y - ( m_Start.y - aCentre.y );
  604. m_End.y = aCentre.y - ( m_End.y - aCentre.y );
  605. }
  606. SetLayer( GetBoard()->FlipLayer( GetLayer() ) );
  607. }
  608. void PCB_ARC::Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
  609. {
  610. if( aFlipDirection == FLIP_DIRECTION::LEFT_RIGHT )
  611. {
  612. m_Start.x = aCentre.x - ( m_Start.x - aCentre.x );
  613. m_End.x = aCentre.x - ( m_End.x - aCentre.x );
  614. m_Mid.x = aCentre.x - ( m_Mid.x - aCentre.x );
  615. }
  616. else
  617. {
  618. m_Start.y = aCentre.y - ( m_Start.y - aCentre.y );
  619. m_End.y = aCentre.y - ( m_End.y - aCentre.y );
  620. m_Mid.y = aCentre.y - ( m_Mid.y - aCentre.y );
  621. }
  622. SetLayer( GetBoard()->FlipLayer( GetLayer() ) );
  623. }
  624. bool PCB_ARC::IsCCW() const
  625. {
  626. VECTOR2L start = m_Start;
  627. VECTOR2L start_end = m_End - start;
  628. VECTOR2L start_mid = m_Mid - start;
  629. return start_end.Cross( start_mid ) < 0;
  630. }
  631. void PCB_VIA::Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
  632. {
  633. if( aFlipDirection == FLIP_DIRECTION::LEFT_RIGHT )
  634. {
  635. m_Start.x = aCentre.x - ( m_Start.x - aCentre.x );
  636. m_End.x = aCentre.x - ( m_End.x - aCentre.x );
  637. }
  638. else
  639. {
  640. m_Start.y = aCentre.y - ( m_Start.y - aCentre.y );
  641. m_End.y = aCentre.y - ( m_End.y - aCentre.y );
  642. }
  643. if( GetViaType() != VIATYPE::THROUGH )
  644. {
  645. PCB_LAYER_ID top_layer;
  646. PCB_LAYER_ID bottom_layer;
  647. LayerPair( &top_layer, &bottom_layer );
  648. top_layer = GetBoard()->FlipLayer( top_layer );
  649. bottom_layer = GetBoard()->FlipLayer( bottom_layer );
  650. SetLayerPair( top_layer, bottom_layer );
  651. }
  652. }
  653. INSPECT_RESULT PCB_TRACK::Visit( INSPECTOR inspector, void* testData,
  654. const std::vector<KICAD_T>& aScanTypes )
  655. {
  656. for( KICAD_T scanType : aScanTypes )
  657. {
  658. if( scanType == Type() )
  659. {
  660. if( INSPECT_RESULT::QUIT == inspector( this, testData ) )
  661. return INSPECT_RESULT::QUIT;
  662. }
  663. }
  664. return INSPECT_RESULT::CONTINUE;
  665. }
  666. std::shared_ptr<SHAPE_SEGMENT> PCB_VIA::GetEffectiveHoleShape() const
  667. {
  668. return std::make_shared<SHAPE_SEGMENT>( SEG( m_Start, m_Start ), Padstack().Drill().size.x );
  669. }
  670. // clang-format off: the suggestion is slightly less readable
  671. void PCB_VIA::SetFrontTentingMode( TENTING_MODE aMode )
  672. {
  673. switch( aMode )
  674. {
  675. case TENTING_MODE::FROM_RULES: m_padStack.FrontOuterLayers().has_solder_mask.reset(); break;
  676. case TENTING_MODE::TENTED: m_padStack.FrontOuterLayers().has_solder_mask = true; break;
  677. case TENTING_MODE::NOT_TENTED: m_padStack.FrontOuterLayers().has_solder_mask = false; break;
  678. }
  679. }
  680. TENTING_MODE PCB_VIA::GetFrontTentingMode() const
  681. {
  682. if( m_padStack.FrontOuterLayers().has_solder_mask.has_value() )
  683. {
  684. return *m_padStack.FrontOuterLayers().has_solder_mask ?
  685. TENTING_MODE::TENTED : TENTING_MODE::NOT_TENTED;
  686. }
  687. return TENTING_MODE::FROM_RULES;
  688. }
  689. void PCB_VIA::SetBackTentingMode( TENTING_MODE aMode )
  690. {
  691. switch( aMode )
  692. {
  693. case TENTING_MODE::FROM_RULES: m_padStack.BackOuterLayers().has_solder_mask.reset(); break;
  694. case TENTING_MODE::TENTED: m_padStack.BackOuterLayers().has_solder_mask = true; break;
  695. case TENTING_MODE::NOT_TENTED: m_padStack.BackOuterLayers().has_solder_mask = false; break;
  696. }
  697. }
  698. TENTING_MODE PCB_VIA::GetBackTentingMode() const
  699. {
  700. if( m_padStack.BackOuterLayers().has_solder_mask.has_value() )
  701. {
  702. return *m_padStack.BackOuterLayers().has_solder_mask ?
  703. TENTING_MODE::TENTED : TENTING_MODE::NOT_TENTED;
  704. }
  705. return TENTING_MODE::FROM_RULES;
  706. }
  707. void PCB_VIA::SetFrontCoveringMode( COVERING_MODE aMode )
  708. {
  709. switch( aMode )
  710. {
  711. case COVERING_MODE::FROM_RULES: m_padStack.FrontOuterLayers().has_covering.reset(); break;
  712. case COVERING_MODE::COVERED: m_padStack.FrontOuterLayers().has_covering = true; break;
  713. case COVERING_MODE::NOT_COVERED: m_padStack.FrontOuterLayers().has_covering = false; break;
  714. }
  715. }
  716. COVERING_MODE PCB_VIA::GetFrontCoveringMode() const
  717. {
  718. if( m_padStack.FrontOuterLayers().has_covering.has_value() )
  719. {
  720. return *m_padStack.FrontOuterLayers().has_covering ?
  721. COVERING_MODE::COVERED : COVERING_MODE::NOT_COVERED;
  722. }
  723. return COVERING_MODE::FROM_RULES;
  724. }
  725. void PCB_VIA::SetBackCoveringMode( COVERING_MODE aMode )
  726. {
  727. switch( aMode )
  728. {
  729. case COVERING_MODE::FROM_RULES: m_padStack.BackOuterLayers().has_covering.reset(); break;
  730. case COVERING_MODE::COVERED: m_padStack.BackOuterLayers().has_covering = true; break;
  731. case COVERING_MODE::NOT_COVERED: m_padStack.BackOuterLayers().has_covering = false; break;
  732. }
  733. }
  734. COVERING_MODE PCB_VIA::GetBackCoveringMode() const
  735. {
  736. if( m_padStack.BackOuterLayers().has_covering.has_value() )
  737. {
  738. return *m_padStack.BackOuterLayers().has_covering ?
  739. COVERING_MODE::COVERED : COVERING_MODE::NOT_COVERED;
  740. }
  741. return COVERING_MODE::FROM_RULES;
  742. }
  743. void PCB_VIA::SetFrontPluggingMode( PLUGGING_MODE aMode )
  744. {
  745. switch( aMode )
  746. {
  747. case PLUGGING_MODE::FROM_RULES: m_padStack.FrontOuterLayers().has_plugging.reset(); break;
  748. case PLUGGING_MODE::PLUGGED: m_padStack.FrontOuterLayers().has_plugging = true; break;
  749. case PLUGGING_MODE::NOT_PLUGGED: m_padStack.FrontOuterLayers().has_plugging = false; break;
  750. }
  751. }
  752. PLUGGING_MODE PCB_VIA::GetFrontPluggingMode() const
  753. {
  754. if( m_padStack.FrontOuterLayers().has_plugging.has_value() )
  755. {
  756. return *m_padStack.FrontOuterLayers().has_plugging ?
  757. PLUGGING_MODE::PLUGGED : PLUGGING_MODE::NOT_PLUGGED;
  758. }
  759. return PLUGGING_MODE::FROM_RULES;
  760. }
  761. void PCB_VIA::SetBackPluggingMode( PLUGGING_MODE aMode )
  762. {
  763. switch( aMode )
  764. {
  765. case PLUGGING_MODE::FROM_RULES: m_padStack.BackOuterLayers().has_plugging.reset(); break;
  766. case PLUGGING_MODE::PLUGGED: m_padStack.BackOuterLayers().has_plugging = true; break;
  767. case PLUGGING_MODE::NOT_PLUGGED: m_padStack.BackOuterLayers().has_plugging = false; break;
  768. }
  769. }
  770. PLUGGING_MODE PCB_VIA::GetBackPluggingMode() const
  771. {
  772. if( m_padStack.BackOuterLayers().has_plugging.has_value() )
  773. {
  774. return *m_padStack.BackOuterLayers().has_plugging ?
  775. PLUGGING_MODE::PLUGGED : PLUGGING_MODE::NOT_PLUGGED;
  776. }
  777. return PLUGGING_MODE::FROM_RULES;
  778. }
  779. void PCB_VIA::SetCappingMode( CAPPING_MODE aMode )
  780. {
  781. switch( aMode )
  782. {
  783. case CAPPING_MODE::FROM_RULES: m_padStack.Drill().is_capped.reset(); break;
  784. case CAPPING_MODE::CAPPED: m_padStack.Drill().is_capped = true; break;
  785. case CAPPING_MODE::NOT_CAPPED: m_padStack.Drill().is_capped = false; break;
  786. }
  787. }
  788. CAPPING_MODE PCB_VIA::GetCappingMode() const
  789. {
  790. if( m_padStack.Drill().is_capped.has_value() )
  791. {
  792. return *m_padStack.Drill().is_capped ?
  793. CAPPING_MODE::CAPPED : CAPPING_MODE::NOT_CAPPED;
  794. }
  795. return CAPPING_MODE::FROM_RULES;
  796. }
  797. void PCB_VIA::SetFillingMode( FILLING_MODE aMode )
  798. {
  799. switch( aMode )
  800. {
  801. case FILLING_MODE::FROM_RULES: m_padStack.Drill().is_filled.reset(); break;
  802. case FILLING_MODE::FILLED: m_padStack.Drill().is_filled = true; break;
  803. case FILLING_MODE::NOT_FILLED: m_padStack.Drill().is_filled = false; break;
  804. }
  805. }
  806. FILLING_MODE PCB_VIA::GetFillingMode() const
  807. {
  808. if( m_padStack.Drill().is_filled.has_value() )
  809. {
  810. return *m_padStack.Drill().is_filled ?
  811. FILLING_MODE::FILLED : FILLING_MODE::NOT_FILLED;
  812. }
  813. return FILLING_MODE::FROM_RULES;
  814. }
  815. // clang-format on: the suggestion is slightly less readable
  816. bool PCB_VIA::IsTented( PCB_LAYER_ID aLayer ) const
  817. {
  818. wxCHECK_MSG( IsFrontLayer( aLayer ) || IsBackLayer( aLayer ), true,
  819. "Invalid layer passed to IsTented" );
  820. bool front = IsFrontLayer( aLayer );
  821. if( front && m_padStack.FrontOuterLayers().has_solder_mask.has_value() )
  822. return *m_padStack.FrontOuterLayers().has_solder_mask;
  823. if( !front && m_padStack.BackOuterLayers().has_solder_mask.has_value() )
  824. return *m_padStack.BackOuterLayers().has_solder_mask;
  825. if( const BOARD* board = GetBoard() )
  826. {
  827. return front ? board->GetDesignSettings().m_TentViasFront
  828. : board->GetDesignSettings().m_TentViasBack;
  829. }
  830. return true;
  831. }
  832. int PCB_VIA::GetSolderMaskExpansion() const
  833. {
  834. if( const BOARD* board = GetBoard() )
  835. return board->GetDesignSettings().m_SolderMaskExpansion;
  836. else
  837. return 0;
  838. }
  839. int PCB_TRACK::GetSolderMaskExpansion() const
  840. {
  841. int margin = 0;
  842. if( const BOARD* board = GetBoard() )
  843. {
  844. DRC_CONSTRAINT constraint;
  845. std::shared_ptr<DRC_ENGINE> drcEngine = board->GetDesignSettings().m_DRCEngine;
  846. constraint = drcEngine->EvalRules( SOLDER_MASK_EXPANSION_CONSTRAINT, this, nullptr, m_layer );
  847. if( constraint.m_Value.HasOpt() )
  848. margin = constraint.m_Value.Opt();
  849. }
  850. else if( m_solderMaskMargin.has_value() )
  851. {
  852. margin = m_solderMaskMargin.value();
  853. }
  854. // Ensure the resulting mask opening has a non-negative size
  855. if( margin < 0 )
  856. margin = std::max( margin, -m_width / 2 );
  857. return margin;
  858. }
  859. bool PCB_TRACK::IsOnLayer( PCB_LAYER_ID aLayer ) const
  860. {
  861. if( aLayer == m_layer )
  862. {
  863. return true;
  864. }
  865. if( m_hasSolderMask && ( ( aLayer == F_Mask && m_layer == F_Cu )
  866. || ( aLayer == B_Mask && m_layer == B_Cu ) ) )
  867. {
  868. return true;
  869. }
  870. return false;
  871. }
  872. bool PCB_VIA::IsOnLayer( PCB_LAYER_ID aLayer ) const
  873. {
  874. #if 0
  875. // Nice and simple, but raises its ugly head in performance profiles....
  876. return GetLayerSet().test( aLayer );
  877. #endif
  878. if( IsCopperLayer( aLayer ) &&
  879. LAYER_RANGE::Contains( Padstack().Drill().start, Padstack().Drill().end, aLayer ) )
  880. {
  881. return true;
  882. }
  883. // Test for via on mask layers: a via on on a mask layer if not tented and if
  884. // it is on the corresponding external copper layer
  885. if( aLayer == F_Mask )
  886. return Padstack().Drill().start == F_Cu && !IsTented( F_Mask );
  887. else if( aLayer == B_Mask )
  888. return Padstack().Drill().end == B_Cu && !IsTented( B_Mask );
  889. return false;
  890. }
  891. bool PCB_VIA::HasValidLayerPair( int aCopperLayerCount )
  892. {
  893. // return true if top and bottom layers are valid, depending on the copper layer count
  894. // aCopperLayerCount is expected >= 2
  895. int layer_id = aCopperLayerCount*2;
  896. if( Padstack().Drill().start > B_Cu )
  897. {
  898. if( Padstack().Drill().start > layer_id )
  899. return false;
  900. }
  901. if( Padstack().Drill().end > B_Cu )
  902. {
  903. if( Padstack().Drill().end > layer_id )
  904. return false;
  905. }
  906. return true;
  907. }
  908. PCB_LAYER_ID PCB_VIA::GetLayer() const
  909. {
  910. return Padstack().Drill().start;
  911. }
  912. void PCB_VIA::SetLayer( PCB_LAYER_ID aLayer )
  913. {
  914. Padstack().Drill().start = aLayer;
  915. }
  916. void PCB_TRACK::SetLayerSet( const LSET& aLayerSet )
  917. {
  918. aLayerSet.RunOnLayers(
  919. [&]( PCB_LAYER_ID layer )
  920. {
  921. if( IsCopperLayer( layer ) )
  922. SetLayer( layer );
  923. else if( IsSolderMaskLayer( layer ) )
  924. SetHasSolderMask( true );
  925. } );
  926. }
  927. LSET PCB_TRACK::GetLayerSet() const
  928. {
  929. LSET layermask( { m_layer } );
  930. if( m_hasSolderMask )
  931. {
  932. if( layermask.test( F_Cu ) )
  933. layermask.set( F_Mask );
  934. else if( layermask.test( B_Cu ) )
  935. layermask.set( B_Mask );
  936. }
  937. return layermask;
  938. }
  939. LSET PCB_VIA::GetLayerSet() const
  940. {
  941. LSET layermask;
  942. if( Padstack().Drill().start < PCBNEW_LAYER_ID_START )
  943. return layermask;
  944. if( GetViaType() == VIATYPE::THROUGH )
  945. {
  946. layermask = LSET::AllCuMask( BoardCopperLayerCount() );
  947. }
  948. else
  949. {
  950. LAYER_RANGE range( Padstack().Drill().start, Padstack().Drill().end, BoardCopperLayerCount() );
  951. int cnt = BoardCopperLayerCount();
  952. // PCB_LAYER_IDs are numbered from front to back, this is top to bottom.
  953. for( PCB_LAYER_ID id : range )
  954. {
  955. layermask.set( id );
  956. if( --cnt <= 0 )
  957. break;
  958. }
  959. }
  960. if( !IsTented( F_Mask ) && layermask.test( F_Cu ) )
  961. layermask.set( F_Mask );
  962. if( !IsTented( B_Mask ) && layermask.test( B_Cu ) )
  963. layermask.set( B_Mask );
  964. return layermask;
  965. }
  966. void PCB_VIA::SetLayerSet( const LSET& aLayerSet )
  967. {
  968. // Vias do not use a LSET, just a top and bottom layer pair
  969. // So we need to set these 2 layers according to the allowed layers in aLayerSet
  970. // For via through, only F_Cu and B_Cu are allowed. aLayerSet is ignored
  971. if( GetViaType() == VIATYPE::THROUGH )
  972. {
  973. Padstack().Drill().start = F_Cu;
  974. Padstack().Drill().end = B_Cu;
  975. return;
  976. }
  977. // For blind buried vias, find the top and bottom layers
  978. bool top_found = false;
  979. bool bottom_found = false;
  980. aLayerSet.RunOnLayers(
  981. [&]( PCB_LAYER_ID layer )
  982. {
  983. // tpo layer and bottom Layer are copper layers, so consider only copper layers
  984. if( IsCopperLayer( layer ) )
  985. {
  986. // The top layer is the first layer found in list and
  987. // cannot the B_Cu
  988. if( !top_found && layer != B_Cu )
  989. {
  990. Padstack().Drill().start = layer;
  991. top_found = true;
  992. }
  993. // The bottom layer is the last layer found in list or B_Cu
  994. if( !bottom_found )
  995. Padstack().Drill().end = layer;
  996. if( layer == B_Cu )
  997. bottom_found = true;
  998. }
  999. } );
  1000. }
  1001. void PCB_VIA::SetLayerPair( PCB_LAYER_ID aTopLayer, PCB_LAYER_ID aBottomLayer )
  1002. {
  1003. Padstack().Drill().start = aTopLayer;
  1004. Padstack().Drill().end = aBottomLayer;
  1005. SanitizeLayers();
  1006. }
  1007. void PCB_VIA::SetTopLayer( PCB_LAYER_ID aLayer )
  1008. {
  1009. // refuse invalid via
  1010. if( aLayer == Padstack().Drill().end )
  1011. return;
  1012. Padstack().Drill().start = aLayer;
  1013. SanitizeLayers();
  1014. }
  1015. void PCB_VIA::SetBottomLayer( PCB_LAYER_ID aLayer )
  1016. {
  1017. // refuse invalid via
  1018. if( aLayer == Padstack().Drill().start )
  1019. return;
  1020. Padstack().Drill().end = aLayer;
  1021. SanitizeLayers();
  1022. }
  1023. void PCB_VIA::LayerPair( PCB_LAYER_ID* top_layer, PCB_LAYER_ID* bottom_layer ) const
  1024. {
  1025. PCB_LAYER_ID t_layer = F_Cu;
  1026. PCB_LAYER_ID b_layer = B_Cu;
  1027. if( GetViaType() != VIATYPE::THROUGH )
  1028. {
  1029. b_layer = Padstack().Drill().end;
  1030. t_layer = Padstack().Drill().start;
  1031. if( !IsCopperLayerLowerThan( b_layer, t_layer ) )
  1032. std::swap( b_layer, t_layer );
  1033. }
  1034. if( top_layer )
  1035. *top_layer = t_layer;
  1036. if( bottom_layer )
  1037. *bottom_layer = b_layer;
  1038. }
  1039. PCB_LAYER_ID PCB_VIA::TopLayer() const
  1040. {
  1041. return Padstack().Drill().start;
  1042. }
  1043. PCB_LAYER_ID PCB_VIA::BottomLayer() const
  1044. {
  1045. return Padstack().Drill().end;
  1046. }
  1047. void PCB_VIA::SanitizeLayers()
  1048. {
  1049. if( GetViaType() == VIATYPE::THROUGH )
  1050. {
  1051. Padstack().Drill().start = F_Cu;
  1052. Padstack().Drill().end = B_Cu;
  1053. }
  1054. if( !IsCopperLayerLowerThan( Padstack().Drill().end, Padstack().Drill().start) )
  1055. std::swap( Padstack().Drill().end, Padstack().Drill().start );
  1056. }
  1057. bool PCB_VIA::FlashLayer( const LSET& aLayers ) const
  1058. {
  1059. for( PCB_LAYER_ID layer : aLayers )
  1060. {
  1061. if( FlashLayer( layer ) )
  1062. return true;
  1063. }
  1064. return false;
  1065. }
  1066. bool PCB_VIA::FlashLayer( int aLayer ) const
  1067. {
  1068. // Return the "normal" shape if the caller doesn't specify a particular layer
  1069. if( aLayer == UNDEFINED_LAYER )
  1070. return true;
  1071. const BOARD* board = GetBoard();
  1072. PCB_LAYER_ID layer = static_cast<PCB_LAYER_ID>( aLayer );
  1073. if( !board )
  1074. return true;
  1075. if( !IsOnLayer( layer ) )
  1076. return false;
  1077. if( !IsCopperLayer( layer ) )
  1078. return true;
  1079. switch( Padstack().UnconnectedLayerMode() )
  1080. {
  1081. case PADSTACK::UNCONNECTED_LAYER_MODE::KEEP_ALL:
  1082. return true;
  1083. case PADSTACK::UNCONNECTED_LAYER_MODE::REMOVE_EXCEPT_START_AND_END:
  1084. {
  1085. if( layer == Padstack().Drill().start || layer == Padstack().Drill().end )
  1086. return true;
  1087. // Check for removal below
  1088. break;
  1089. }
  1090. case PADSTACK::UNCONNECTED_LAYER_MODE::REMOVE_ALL:
  1091. // Check for removal below
  1092. break;
  1093. }
  1094. if( GetZoneLayerOverride( layer ) == ZLO_FORCE_FLASHED )
  1095. {
  1096. return true;
  1097. }
  1098. else
  1099. {
  1100. // Must be static to keep from raising its ugly head in performance profiles
  1101. static std::initializer_list<KICAD_T> nonZoneTypes = { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T,
  1102. PCB_PAD_T };
  1103. return board->GetConnectivity()->IsConnectedOnLayer( this, layer, nonZoneTypes );
  1104. }
  1105. }
  1106. void PCB_VIA::ClearZoneLayerOverrides()
  1107. {
  1108. std::unique_lock<std::mutex> cacheLock( m_zoneLayerOverridesMutex );
  1109. for( PCB_LAYER_ID layer : LAYER_RANGE( F_Cu, B_Cu, BoardCopperLayerCount() ) )
  1110. m_zoneLayerOverrides[layer] = ZLO_NONE;
  1111. }
  1112. const ZONE_LAYER_OVERRIDE& PCB_VIA::GetZoneLayerOverride( PCB_LAYER_ID aLayer ) const
  1113. {
  1114. static const ZONE_LAYER_OVERRIDE defaultOverride = ZLO_NONE;
  1115. auto it = m_zoneLayerOverrides.find( aLayer );
  1116. return it != m_zoneLayerOverrides.end() ? it->second : defaultOverride;
  1117. }
  1118. void PCB_VIA::SetZoneLayerOverride( PCB_LAYER_ID aLayer, ZONE_LAYER_OVERRIDE aOverride )
  1119. {
  1120. std::unique_lock<std::mutex> cacheLock( m_zoneLayerOverridesMutex );
  1121. m_zoneLayerOverrides[aLayer] = aOverride;
  1122. }
  1123. void PCB_VIA::GetOutermostConnectedLayers( PCB_LAYER_ID* aTopmost,
  1124. PCB_LAYER_ID* aBottommost ) const
  1125. {
  1126. *aTopmost = UNDEFINED_LAYER;
  1127. *aBottommost = UNDEFINED_LAYER;
  1128. static std::initializer_list<KICAD_T> nonZoneTypes = { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T,
  1129. PCB_PAD_T };
  1130. for( int layer = TopLayer(); layer <= BottomLayer(); ++layer )
  1131. {
  1132. bool connected = false;
  1133. if( GetZoneLayerOverride( static_cast<PCB_LAYER_ID>( layer ) ) == ZLO_FORCE_FLASHED )
  1134. {
  1135. connected = true;
  1136. }
  1137. else if( GetBoard()->GetConnectivity()->IsConnectedOnLayer( this, layer, nonZoneTypes ) )
  1138. {
  1139. connected = true;
  1140. }
  1141. if( connected )
  1142. {
  1143. if( *aTopmost == UNDEFINED_LAYER )
  1144. *aTopmost = ToLAYER_ID( layer );
  1145. *aBottommost = ToLAYER_ID( layer );
  1146. }
  1147. }
  1148. }
  1149. std::vector<int> PCB_TRACK::ViewGetLayers() const
  1150. {
  1151. // Show the track and its netname on different layers
  1152. const PCB_LAYER_ID layer = GetLayer();
  1153. std::vector<int> layers{
  1154. layer,
  1155. GetNetnameLayer( layer ),
  1156. LAYER_CLEARANCE_START + layer,
  1157. };
  1158. layers.reserve( 6 );
  1159. if( m_hasSolderMask )
  1160. {
  1161. if( m_layer == F_Cu )
  1162. layers.push_back( F_Mask );
  1163. else if( m_layer == B_Cu )
  1164. layers.push_back( B_Mask );
  1165. }
  1166. if( IsLocked() )
  1167. layers.push_back( LAYER_LOCKED_ITEM_SHADOW );
  1168. return layers;
  1169. }
  1170. double PCB_TRACK::ViewGetLOD( int aLayer, const KIGFX::VIEW* aView ) const
  1171. {
  1172. PCB_PAINTER* painter = static_cast<PCB_PAINTER*>( aView->GetPainter() );
  1173. PCB_RENDER_SETTINGS* renderSettings = painter->GetSettings();
  1174. if( !aView->IsLayerVisible( LAYER_TRACKS ) )
  1175. return LOD_HIDE;
  1176. if( IsNetnameLayer( aLayer ) )
  1177. {
  1178. if( GetNetCode() <= NETINFO_LIST::UNCONNECTED )
  1179. return LOD_HIDE;
  1180. // Hide netnames on dimmed tracks
  1181. if( renderSettings->GetHighContrast() )
  1182. {
  1183. if( m_layer != renderSettings->GetPrimaryHighContrastLayer() )
  1184. return LOD_HIDE;
  1185. }
  1186. VECTOR2I start( GetStart() );
  1187. VECTOR2I end( GetEnd() );
  1188. // Calc the approximate size of the netname (assume square chars)
  1189. SEG::ecoord nameSize = GetDisplayNetname().size() * GetWidth();
  1190. if( VECTOR2I( end - start ).SquaredEuclideanNorm() < nameSize * nameSize )
  1191. return LOD_HIDE;
  1192. BOX2I clipBox = BOX2ISafe( aView->GetViewport() );
  1193. ClipLine( &clipBox, start.x, start.y, end.x, end.y );
  1194. if( VECTOR2I( end - start ).SquaredEuclideanNorm() == 0 )
  1195. return LOD_HIDE;
  1196. // Netnames will be shown only if zoom is appropriate
  1197. return lodScaleForThreshold( aView, m_width, pcbIUScale.mmToIU( 4.0 ) );
  1198. }
  1199. if( aLayer == LAYER_LOCKED_ITEM_SHADOW )
  1200. {
  1201. // Hide shadow if the main layer is not shown
  1202. if( !aView->IsLayerVisible( m_layer ) )
  1203. return LOD_HIDE;
  1204. // Hide shadow on dimmed tracks
  1205. if( renderSettings->GetHighContrast() )
  1206. {
  1207. if( m_layer != renderSettings->GetPrimaryHighContrastLayer() )
  1208. return LOD_HIDE;
  1209. }
  1210. }
  1211. // Other layers are shown without any conditions
  1212. return LOD_SHOW;
  1213. }
  1214. const BOX2I PCB_TRACK::ViewBBox() const
  1215. {
  1216. BOX2I bbox = GetBoundingBox();
  1217. if( const BOARD* board = GetBoard() )
  1218. bbox.Inflate( 2 * board->GetDesignSettings().GetBiggestClearanceValue() );
  1219. else
  1220. bbox.Inflate( GetWidth() ); // Add a bit extra for safety
  1221. return bbox;
  1222. }
  1223. std::vector<int> PCB_VIA::ViewGetLayers() const
  1224. {
  1225. LAYER_RANGE layers( Padstack().Drill().start, Padstack().Drill().end, MAX_CU_LAYERS );
  1226. std::vector<int> ret_layers{ LAYER_VIA_HOLES, LAYER_VIA_HOLEWALLS, LAYER_VIA_NETNAMES };
  1227. ret_layers.reserve( MAX_CU_LAYERS + 6 );
  1228. // TODO(JE) Rendering order issue
  1229. #if 0
  1230. // Blind/buried vias (and microvias) use a different net name layer
  1231. PCB_LAYER_ID layerTop, layerBottom;
  1232. LayerPair( &layerTop, &layerBottom );
  1233. bool isBlindBuried =
  1234. m_viaType == VIATYPE::BLIND_BURIED
  1235. || ( m_viaType == VIATYPE::MICROVIA && ( layerTop != F_Cu || layerBottom != B_Cu ) );
  1236. #endif
  1237. LSET cuMask = LSET::AllCuMask();
  1238. if( const BOARD* board = GetBoard() )
  1239. cuMask &= board->GetEnabledLayers();
  1240. for( PCB_LAYER_ID layer : layers )
  1241. {
  1242. if( !cuMask.Contains( layer ) )
  1243. continue;
  1244. ret_layers.push_back( LAYER_VIA_COPPER_START + layer );
  1245. ret_layers.push_back( LAYER_CLEARANCE_START + layer );
  1246. }
  1247. if( IsLocked() )
  1248. ret_layers.push_back( LAYER_LOCKED_ITEM_SHADOW );
  1249. // Vias can also be on a solder mask layer. They are on these layers or not,
  1250. // depending on the plot and solder mask options
  1251. if( IsOnLayer( F_Mask ) )
  1252. ret_layers.push_back( F_Mask );
  1253. if( IsOnLayer( B_Mask ) )
  1254. ret_layers.push_back( B_Mask );
  1255. return ret_layers;
  1256. }
  1257. double PCB_VIA::ViewGetLOD( int aLayer, const KIGFX::VIEW* aView ) const
  1258. {
  1259. PCB_PAINTER* painter = static_cast<PCB_PAINTER*>( aView->GetPainter() );
  1260. PCB_RENDER_SETTINGS* renderSettings = painter->GetSettings();
  1261. const BOARD* board = GetBoard();
  1262. // Meta control for hiding all vias
  1263. if( !aView->IsLayerVisible( LAYER_VIAS ) )
  1264. return LOD_HIDE;
  1265. // In high contrast mode don't show vias that don't cross the high-contrast layer
  1266. if( renderSettings->GetHighContrast() )
  1267. {
  1268. PCB_LAYER_ID highContrastLayer = renderSettings->GetPrimaryHighContrastLayer();
  1269. if( LSET::FrontTechMask().Contains( highContrastLayer ) )
  1270. highContrastLayer = F_Cu;
  1271. else if( LSET::BackTechMask().Contains( highContrastLayer ) )
  1272. highContrastLayer = B_Cu;
  1273. if( IsCopperLayer( highContrastLayer ) && GetViaType() != VIATYPE::THROUGH )
  1274. {
  1275. if( IsCopperLayerLowerThan( Padstack().Drill().start, highContrastLayer )
  1276. || IsCopperLayerLowerThan( highContrastLayer, Padstack().Drill().end ) )
  1277. {
  1278. return LOD_HIDE;
  1279. }
  1280. }
  1281. }
  1282. if( IsHoleLayer( aLayer ) )
  1283. {
  1284. LSET visible;
  1285. if( board )
  1286. {
  1287. visible = board->GetVisibleLayers();
  1288. visible &= board->GetEnabledLayers();
  1289. }
  1290. else
  1291. {
  1292. visible = LSET::AllLayersMask();
  1293. }
  1294. if( m_viaType == VIATYPE::THROUGH )
  1295. {
  1296. // Show a through via's hole if any physical layer is shown
  1297. visible &= LSET::PhysicalLayersMask();
  1298. if( !visible.any() )
  1299. return LOD_HIDE;
  1300. }
  1301. else
  1302. {
  1303. // Show a blind or micro via's hole if it crosses a visible layer
  1304. visible &= GetLayerSet();
  1305. if( !visible.any() )
  1306. return LOD_HIDE;
  1307. }
  1308. // The hole won't be visible anyway at this scale
  1309. return lodScaleForThreshold( aView, GetDrillValue(), pcbIUScale.mmToIU( 0.25 ) );
  1310. }
  1311. else if( IsNetnameLayer( aLayer ) )
  1312. {
  1313. if( renderSettings->GetHighContrast() )
  1314. {
  1315. // Hide netnames unless via is flashed to a high-contrast layer
  1316. if( !FlashLayer( renderSettings->GetPrimaryHighContrastLayer() ) )
  1317. return LOD_HIDE;
  1318. }
  1319. else
  1320. {
  1321. LSET visible;
  1322. if( board )
  1323. {
  1324. visible = board->GetVisibleLayers();
  1325. visible &= board->GetEnabledLayers();
  1326. }
  1327. else
  1328. {
  1329. visible = LSET::AllLayersMask();
  1330. }
  1331. // Hide netnames unless pad is flashed to a visible layer
  1332. if( !FlashLayer( visible ) )
  1333. return LOD_HIDE;
  1334. }
  1335. int width = GetWidth( ToLAYER_ID( aLayer ) );
  1336. // Netnames will be shown only if zoom is appropriate
  1337. return lodScaleForThreshold( aView, width, pcbIUScale.mmToIU( 10 ) );
  1338. }
  1339. if( !IsCopperLayer( aLayer ) )
  1340. {
  1341. int width = GetWidth( ToLAYER_ID( aLayer ) );
  1342. return lodScaleForThreshold( aView, width, pcbIUScale.mmToIU( 0.6 ) );
  1343. }
  1344. return LOD_SHOW;
  1345. }
  1346. wxString PCB_TRACK::GetFriendlyName() const
  1347. {
  1348. switch( Type() )
  1349. {
  1350. case PCB_ARC_T: return _( "Track (arc)" );
  1351. case PCB_VIA_T: return _( "Via" );
  1352. case PCB_TRACE_T:
  1353. default: return _( "Track" );
  1354. }
  1355. }
  1356. void PCB_TRACK::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
  1357. {
  1358. wxString msg;
  1359. BOARD* board = GetBoard();
  1360. aList.emplace_back( _( "Type" ), GetFriendlyName() );
  1361. GetMsgPanelInfoBase_Common( aFrame, aList );
  1362. aList.emplace_back( _( "Layer" ), layerMaskDescribe() );
  1363. aList.emplace_back( _( "Width" ), aFrame->MessageTextFromValue( m_width ) );
  1364. if( Type() == PCB_ARC_T )
  1365. {
  1366. double radius = static_cast<PCB_ARC*>( this )->GetRadius();
  1367. aList.emplace_back( _( "Radius" ), aFrame->MessageTextFromValue( radius ) );
  1368. }
  1369. double segmentLength = GetLength();
  1370. double segmentDelay = GetDelay();
  1371. if( segmentDelay == 0.0 )
  1372. {
  1373. aList.emplace_back( _( "Segment Length" ), aFrame->MessageTextFromValue( segmentLength ) );
  1374. }
  1375. else
  1376. {
  1377. aList.emplace_back( _( "Segment Delay" ),
  1378. aFrame->MessageTextFromValue( segmentDelay, true, EDA_DATA_TYPE::TIME ) );
  1379. }
  1380. // Display full track length (in Pcbnew)
  1381. if( board && GetNetCode() > 0 )
  1382. {
  1383. int count = 0;
  1384. double trackLen = 0.0;
  1385. double lenPadToDie = 0.0;
  1386. double trackDelay = 0.0;
  1387. double delayPadToDie = 0.0;
  1388. std::tie( count, trackLen, lenPadToDie, trackDelay, delayPadToDie ) = board->GetTrackLength( *this );
  1389. if( trackDelay == 0.0 )
  1390. {
  1391. aList.emplace_back( _( "Routed Length" ), aFrame->MessageTextFromValue( trackLen ) );
  1392. if( lenPadToDie != 0 )
  1393. {
  1394. msg = aFrame->MessageTextFromValue( lenPadToDie );
  1395. aList.emplace_back( _( "Pad To Die Length" ), msg );
  1396. msg = aFrame->MessageTextFromValue( trackLen + lenPadToDie );
  1397. aList.emplace_back( _( "Full Length" ), msg );
  1398. }
  1399. }
  1400. else
  1401. {
  1402. aList.emplace_back( _( "Routed Delay" ),
  1403. aFrame->MessageTextFromValue( trackDelay, true, EDA_DATA_TYPE::TIME ) );
  1404. if( delayPadToDie != 0.0 )
  1405. {
  1406. msg = aFrame->MessageTextFromValue( delayPadToDie, true, EDA_DATA_TYPE::TIME );
  1407. aList.emplace_back( _( "Pad To Die Delay" ), msg );
  1408. msg = aFrame->MessageTextFromValue( trackDelay + delayPadToDie, true, EDA_DATA_TYPE::TIME );
  1409. aList.emplace_back( _( "Full Delay" ), msg );
  1410. }
  1411. }
  1412. }
  1413. SHAPE_POLY_SET copper;
  1414. TransformShapeToPolySet( copper, GetLayer(), 0, ARC_LOW_DEF, ERROR_INSIDE );
  1415. aList.emplace_back( _( "Copper Area" ),
  1416. aFrame->MessageTextFromValue( copper.Area(), true, EDA_DATA_TYPE::AREA ) );
  1417. wxString source;
  1418. int clearance = GetOwnClearance( GetLayer(), &source );
  1419. aList.emplace_back( wxString::Format( _( "Min Clearance: %s" ),
  1420. aFrame->MessageTextFromValue( clearance ) ),
  1421. wxString::Format( _( "(from %s)" ), source ) );
  1422. MINOPTMAX<int> constraintValue = GetWidthConstraint( &source );
  1423. msg = aFrame->MessageTextFromMinOptMax( constraintValue );
  1424. if( !msg.IsEmpty() )
  1425. {
  1426. aList.emplace_back( wxString::Format( _( "Width Constraints: %s" ), msg ),
  1427. wxString::Format( _( "(from %s)" ), source ) );
  1428. }
  1429. }
  1430. void PCB_VIA::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
  1431. {
  1432. wxString msg;
  1433. switch( GetViaType() )
  1434. {
  1435. case VIATYPE::MICROVIA: msg = _( "Micro Via" ); break;
  1436. case VIATYPE::BLIND_BURIED: msg = _( "Blind/Buried Via" ); break;
  1437. case VIATYPE::THROUGH: msg = _( "Through Via" ); break;
  1438. default: msg = _( "Via" ); break;
  1439. }
  1440. aList.emplace_back( _( "Type" ), msg );
  1441. GetMsgPanelInfoBase_Common( aFrame, aList );
  1442. aList.emplace_back( _( "Layer" ), layerMaskDescribe() );
  1443. // TODO(JE) padstacks
  1444. aList.emplace_back( _( "Diameter" ),
  1445. aFrame->MessageTextFromValue( GetWidth( PADSTACK::ALL_LAYERS ) ) );
  1446. aList.emplace_back( _( "Hole" ), aFrame->MessageTextFromValue( GetDrillValue() ) );
  1447. wxString source;
  1448. int clearance = GetOwnClearance( GetLayer(), &source );
  1449. aList.emplace_back( wxString::Format( _( "Min Clearance: %s" ),
  1450. aFrame->MessageTextFromValue( clearance ) ),
  1451. wxString::Format( _( "(from %s)" ), source ) );
  1452. int minAnnulus = GetMinAnnulus( GetLayer(), &source );
  1453. aList.emplace_back( wxString::Format( _( "Min Annular Width: %s" ),
  1454. aFrame->MessageTextFromValue( minAnnulus ) ),
  1455. wxString::Format( _( "(from %s)" ), source ) );
  1456. }
  1457. void PCB_TRACK::GetMsgPanelInfoBase_Common( EDA_DRAW_FRAME* aFrame,
  1458. std::vector<MSG_PANEL_ITEM>& aList ) const
  1459. {
  1460. aList.emplace_back( _( "Net" ), UnescapeString( GetNetname() ) );
  1461. aList.emplace_back( _( "Resolved Netclass" ),
  1462. UnescapeString( GetEffectiveNetClass()->GetHumanReadableName() ) );
  1463. #if 0 // Enable for debugging
  1464. if( GetBoard() )
  1465. aList.emplace_back( _( "NetCode" ), fmt::format( "{}", GetNetCode() ) );
  1466. aList.emplace_back( wxT( "Flags" ), fmt::format( "#08X", m_flags ) );
  1467. aList.emplace_back( wxT( "Start pos" ), fmt::format( "{} {}", m_Start.x, m_Start.y ) );
  1468. aList.emplace_back( wxT( "End pos" ), fmt::format( "{} {}", m_End.x, m_End.y ) );
  1469. #endif
  1470. if( aFrame->GetName() == PCB_EDIT_FRAME_NAME && IsLocked() )
  1471. aList.emplace_back( _( "Status" ), _( "Locked" ) );
  1472. }
  1473. wxString PCB_VIA::layerMaskDescribe() const
  1474. {
  1475. const BOARD* board = GetBoard();
  1476. PCB_LAYER_ID top_layer;
  1477. PCB_LAYER_ID bottom_layer;
  1478. LayerPair( &top_layer, &bottom_layer );
  1479. return board->GetLayerName( top_layer ) + wxT( " - " ) + board->GetLayerName( bottom_layer );
  1480. }
  1481. bool PCB_TRACK::HitTest( const VECTOR2I& aPosition, int aAccuracy ) const
  1482. {
  1483. return TestSegmentHit( aPosition, m_Start, m_End, aAccuracy + ( m_width / 2 ) );
  1484. }
  1485. bool PCB_ARC::HitTest( const VECTOR2I& aPosition, int aAccuracy ) const
  1486. {
  1487. double max_dist = aAccuracy + ( GetWidth() / 2.0 );
  1488. // Short-circuit common cases where the arc is connected to a track or via at an endpoint
  1489. if( GetStart().Distance( aPosition ) <= max_dist || GetEnd().Distance( aPosition ) <= max_dist )
  1490. {
  1491. return true;
  1492. }
  1493. VECTOR2L center = GetPosition();
  1494. VECTOR2L relpos = aPosition - center;
  1495. int64_t dist = relpos.EuclideanNorm();
  1496. double radius = GetRadius();
  1497. if( std::abs( dist - radius ) > max_dist )
  1498. return false;
  1499. EDA_ANGLE arc_angle = GetAngle();
  1500. EDA_ANGLE arc_angle_start = GetArcAngleStart(); // Always 0.0 ... 360 deg
  1501. EDA_ANGLE arc_hittest( relpos );
  1502. // Calculate relative angle between the starting point of the arc, and the test point
  1503. arc_hittest -= arc_angle_start;
  1504. // Normalise arc_hittest between 0 ... 360 deg
  1505. arc_hittest.Normalize();
  1506. if( arc_angle < ANGLE_0 )
  1507. return arc_hittest >= ANGLE_360 + arc_angle;
  1508. return arc_hittest <= arc_angle;
  1509. }
  1510. bool PCB_VIA::HitTest( const VECTOR2I& aPosition, int aAccuracy ) const
  1511. {
  1512. bool hit = false;
  1513. Padstack().ForEachUniqueLayer(
  1514. [&]( PCB_LAYER_ID aLayer )
  1515. {
  1516. if( hit )
  1517. return;
  1518. int max_dist = aAccuracy + ( GetWidth( aLayer ) / 2 );
  1519. // rel_pos is aPosition relative to m_Start (or the center of the via)
  1520. VECTOR2D rel_pos = aPosition - m_Start;
  1521. double dist = rel_pos.x * rel_pos.x + rel_pos.y * rel_pos.y;
  1522. if( dist <= static_cast<double>( max_dist ) * max_dist )
  1523. hit = true;
  1524. } );
  1525. return hit;
  1526. }
  1527. bool PCB_TRACK::HitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const
  1528. {
  1529. BOX2I arect = aRect;
  1530. arect.Inflate( aAccuracy );
  1531. if( aContained )
  1532. return arect.Contains( GetStart() ) && arect.Contains( GetEnd() );
  1533. else
  1534. return arect.Intersects( GetStart(), GetEnd() );
  1535. }
  1536. bool PCB_ARC::HitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const
  1537. {
  1538. BOX2I arect = aRect;
  1539. arect.Inflate( aAccuracy );
  1540. BOX2I box( GetStart() );
  1541. box.Merge( GetMid() );
  1542. box.Merge( GetEnd() );
  1543. box.Inflate( GetWidth() / 2 );
  1544. if( aContained )
  1545. return arect.Contains( box );
  1546. else
  1547. return arect.Intersects( box );
  1548. }
  1549. bool PCB_VIA::HitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const
  1550. {
  1551. BOX2I arect = aRect;
  1552. arect.Inflate( aAccuracy );
  1553. bool hit = false;
  1554. Padstack().ForEachUniqueLayer(
  1555. [&]( PCB_LAYER_ID aLayer )
  1556. {
  1557. if( hit )
  1558. return;
  1559. BOX2I box( GetStart() );
  1560. box.Inflate( GetWidth( aLayer ) / 2 );
  1561. if( aContained )
  1562. hit = arect.Contains( box );
  1563. else
  1564. hit = arect.IntersectsCircle( GetStart(), GetWidth( aLayer ) / 2 );
  1565. } );
  1566. return hit;
  1567. }
  1568. bool PCB_TRACK::HitTest( const SHAPE_LINE_CHAIN& aPoly, bool aContained ) const
  1569. {
  1570. return KIGEOM::ShapeHitTest( aPoly, *GetEffectiveShape(), aContained );
  1571. }
  1572. wxString PCB_TRACK::GetItemDescription( UNITS_PROVIDER* aUnitsProvider, bool aFull ) const
  1573. {
  1574. return wxString::Format( Type() == PCB_ARC_T ? _("Track (arc) %s on %s, length %s" )
  1575. : _("Track %s on %s, length %s" ),
  1576. GetNetnameMsg(),
  1577. GetLayerName(),
  1578. aUnitsProvider->MessageTextFromValue( GetLength() ) );
  1579. }
  1580. BITMAPS PCB_TRACK::GetMenuImage() const
  1581. {
  1582. return BITMAPS::add_tracks;
  1583. }
  1584. void PCB_TRACK::swapData( BOARD_ITEM* aImage )
  1585. {
  1586. assert( aImage->Type() == PCB_TRACE_T );
  1587. std::swap( *((PCB_TRACK*) this), *((PCB_TRACK*) aImage) );
  1588. }
  1589. void PCB_ARC::swapData( BOARD_ITEM* aImage )
  1590. {
  1591. assert( aImage->Type() == PCB_ARC_T );
  1592. std::swap( *this, *static_cast<PCB_ARC*>( aImage ) );
  1593. }
  1594. void PCB_VIA::swapData( BOARD_ITEM* aImage )
  1595. {
  1596. assert( aImage->Type() == PCB_VIA_T );
  1597. std::swap( *((PCB_VIA*) this), *((PCB_VIA*) aImage) );
  1598. }
  1599. VECTOR2I PCB_ARC::GetPosition() const
  1600. {
  1601. VECTOR2I center = CalcArcCenter( m_Start, m_Mid, m_End );
  1602. return center;
  1603. }
  1604. double PCB_ARC::GetRadius() const
  1605. {
  1606. auto center = CalcArcCenter( m_Start, m_Mid , m_End );
  1607. return std::min( center.Distance( m_Start ), (double) INT_MAX / 2.0 );
  1608. }
  1609. EDA_ANGLE PCB_ARC::GetAngle() const
  1610. {
  1611. VECTOR2D center = GetPosition();
  1612. EDA_ANGLE angle1 = EDA_ANGLE( m_Mid - center ) - EDA_ANGLE( m_Start - center );
  1613. EDA_ANGLE angle2 = EDA_ANGLE( m_End - center ) - EDA_ANGLE( m_Mid - center );
  1614. return angle1.Normalize180() + angle2.Normalize180();
  1615. }
  1616. EDA_ANGLE PCB_ARC::GetArcAngleStart() const
  1617. {
  1618. VECTOR2D pos( GetPosition() );
  1619. EDA_ANGLE angleStart( m_Start - pos );
  1620. return angleStart.Normalize();
  1621. }
  1622. // Note: used in python tests. Ignore CLion's claim that it's unused....
  1623. EDA_ANGLE PCB_ARC::GetArcAngleEnd() const
  1624. {
  1625. VECTOR2D pos( GetPosition() );
  1626. EDA_ANGLE angleEnd( m_End - pos );
  1627. return angleEnd.Normalize();
  1628. }
  1629. bool PCB_ARC::IsDegenerated( int aThreshold ) const
  1630. {
  1631. // We have lots of code that will blow up if the radius overflows an int.
  1632. if( GetRadius() >= (double)INT_MAX/2.0 )
  1633. return true;
  1634. // Too small arcs cannot be really handled: arc center (and arc radius)
  1635. // cannot be safely computed if the distance between mid and end points
  1636. // is too small (a few internal units)
  1637. // len of both segments must be < aThreshold to be a very small degenerated arc
  1638. return ( GetMid() - GetStart() ).EuclideanNorm() < aThreshold
  1639. && ( GetMid() - GetEnd() ).EuclideanNorm() < aThreshold;
  1640. }
  1641. bool PCB_TRACK::cmp_tracks::operator() ( const PCB_TRACK* a, const PCB_TRACK* b ) const
  1642. {
  1643. if( a->GetNetCode() != b->GetNetCode() )
  1644. return a->GetNetCode() < b->GetNetCode();
  1645. if( a->GetLayer() != b->GetLayer() )
  1646. return a->GetLayer() < b->GetLayer();
  1647. if( a->Type() != b->Type() )
  1648. return a->Type() < b->Type();
  1649. if( a->m_Uuid != b->m_Uuid )
  1650. return a->m_Uuid < b->m_Uuid;
  1651. return a < b;
  1652. }
  1653. std::shared_ptr<SHAPE> PCB_TRACK::GetEffectiveShape( PCB_LAYER_ID aLayer, FLASHING aFlash ) const
  1654. {
  1655. int width = m_width;
  1656. if( IsSolderMaskLayer( aLayer ) )
  1657. width += 2 * GetSolderMaskExpansion();
  1658. return std::make_shared<SHAPE_SEGMENT>( m_Start, m_End, width );
  1659. }
  1660. std::shared_ptr<SHAPE> PCB_VIA::GetEffectiveShape( PCB_LAYER_ID aLayer, FLASHING aFlash ) const
  1661. {
  1662. if( aFlash == FLASHING::ALWAYS_FLASHED
  1663. || ( aFlash == FLASHING::DEFAULT && FlashLayer( aLayer ) ) )
  1664. {
  1665. int width = 0;
  1666. if( aLayer == UNDEFINED_LAYER )
  1667. {
  1668. Padstack().ForEachUniqueLayer(
  1669. [&]( PCB_LAYER_ID layer )
  1670. {
  1671. width = std::max( width, GetWidth( layer ) );
  1672. } );
  1673. width /= 2;
  1674. }
  1675. else
  1676. {
  1677. PCB_LAYER_ID cuLayer = m_padStack.EffectiveLayerFor( aLayer );
  1678. width = GetWidth( cuLayer ) / 2;
  1679. }
  1680. return std::make_shared<SHAPE_CIRCLE>( m_Start, width );
  1681. }
  1682. else
  1683. {
  1684. return std::make_shared<SHAPE_CIRCLE>( m_Start, GetDrillValue() / 2 );
  1685. }
  1686. }
  1687. std::shared_ptr<SHAPE> PCB_ARC::GetEffectiveShape( PCB_LAYER_ID aLayer, FLASHING aFlash ) const
  1688. {
  1689. int width = GetWidth();
  1690. if( IsSolderMaskLayer( aLayer ) )
  1691. width += 2 * GetSolderMaskExpansion();
  1692. SHAPE_ARC arc( GetStart(), GetMid(), GetEnd(), width );
  1693. if( arc.IsEffectiveLine() )
  1694. return std::make_shared<SHAPE_SEGMENT>( GetStart(), GetEnd(), width );
  1695. return std::make_shared<SHAPE_ARC>( arc );
  1696. }
  1697. void PCB_TRACK::TransformShapeToPolygon( SHAPE_POLY_SET& aBuffer, PCB_LAYER_ID aLayer,
  1698. int aClearance, int aError, ERROR_LOC aErrorLoc,
  1699. bool ignoreLineWidth ) const
  1700. {
  1701. wxASSERT_MSG( !ignoreLineWidth, wxT( "IgnoreLineWidth has no meaning for tracks." ) );
  1702. switch( Type() )
  1703. {
  1704. case PCB_VIA_T:
  1705. {
  1706. int radius = ( static_cast<const PCB_VIA*>( this )->GetWidth( aLayer ) / 2 ) + aClearance;
  1707. TransformCircleToPolygon( aBuffer, m_Start, radius, aError, aErrorLoc );
  1708. break;
  1709. }
  1710. case PCB_ARC_T:
  1711. {
  1712. const PCB_ARC* arc = static_cast<const PCB_ARC*>( this );
  1713. int width = m_width + ( 2 * aClearance );
  1714. if( IsSolderMaskLayer( aLayer ) )
  1715. width += 2 * GetSolderMaskExpansion();
  1716. TransformArcToPolygon( aBuffer, arc->GetStart(), arc->GetMid(), arc->GetEnd(), width,
  1717. aError, aErrorLoc );
  1718. break;
  1719. }
  1720. default:
  1721. {
  1722. int width = m_width + ( 2 * aClearance );
  1723. if( IsSolderMaskLayer( aLayer ) )
  1724. width += 2 * GetSolderMaskExpansion();
  1725. TransformOvalToPolygon( aBuffer, m_Start, m_End, width, aError, aErrorLoc );
  1726. break;
  1727. }
  1728. }
  1729. }
  1730. static struct TRACK_VIA_DESC
  1731. {
  1732. TRACK_VIA_DESC()
  1733. {
  1734. // clang-format off: the suggestion is less readable
  1735. ENUM_MAP<VIATYPE>::Instance()
  1736. .Undefined( VIATYPE::NOT_DEFINED )
  1737. .Map( VIATYPE::THROUGH, _HKI( "Through" ) )
  1738. .Map( VIATYPE::BLIND_BURIED, _HKI( "Blind/buried" ) )
  1739. .Map( VIATYPE::MICROVIA, _HKI( "Micro" ) );
  1740. ENUM_MAP<TENTING_MODE>::Instance()
  1741. .Undefined( TENTING_MODE::FROM_RULES )
  1742. .Map( TENTING_MODE::FROM_RULES, _HKI( "From design rules" ) )
  1743. .Map( TENTING_MODE::TENTED, _HKI( "Tented" ) )
  1744. .Map( TENTING_MODE::NOT_TENTED, _HKI( "Not tented" ) );
  1745. ENUM_MAP<COVERING_MODE>::Instance()
  1746. .Undefined( COVERING_MODE::FROM_RULES )
  1747. .Map( COVERING_MODE::FROM_RULES, _HKI( "From design rules" ) )
  1748. .Map( COVERING_MODE::COVERED, _HKI( "Covered" ) )
  1749. .Map( COVERING_MODE::NOT_COVERED, _HKI( "Not covered" ) );
  1750. ENUM_MAP<PLUGGING_MODE>::Instance()
  1751. .Undefined( PLUGGING_MODE::FROM_RULES )
  1752. .Map( PLUGGING_MODE::FROM_RULES, _HKI( "From design rules" ) )
  1753. .Map( PLUGGING_MODE::PLUGGED, _HKI( "Plugged" ) )
  1754. .Map( PLUGGING_MODE::NOT_PLUGGED, _HKI( "Not plugged" ) );
  1755. ENUM_MAP<CAPPING_MODE>::Instance()
  1756. .Undefined( CAPPING_MODE::FROM_RULES )
  1757. .Map( CAPPING_MODE::FROM_RULES, _HKI( "From design rules" ) )
  1758. .Map( CAPPING_MODE::CAPPED, _HKI( "Capped" ) )
  1759. .Map( CAPPING_MODE::NOT_CAPPED, _HKI( "Not capped" ) );
  1760. ENUM_MAP<FILLING_MODE>::Instance()
  1761. .Undefined( FILLING_MODE::FROM_RULES )
  1762. .Map( FILLING_MODE::FROM_RULES, _HKI( "From design rules" ) )
  1763. .Map( FILLING_MODE::FILLED, _HKI( "Filled" ) )
  1764. .Map( FILLING_MODE::NOT_FILLED, _HKI( "Not filled" ) );
  1765. // clang-format on: the suggestion is less readable
  1766. ENUM_MAP<PCB_LAYER_ID>& layerEnum = ENUM_MAP<PCB_LAYER_ID>::Instance();
  1767. if( layerEnum.Choices().GetCount() == 0 )
  1768. {
  1769. layerEnum.Undefined( UNDEFINED_LAYER );
  1770. for( PCB_LAYER_ID layer : LSET::AllLayersMask() )
  1771. layerEnum.Map( layer, LSET::Name( layer ) );
  1772. }
  1773. PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
  1774. // Track
  1775. REGISTER_TYPE( PCB_TRACK );
  1776. propMgr.InheritsAfter( TYPE_HASH( PCB_TRACK ), TYPE_HASH( BOARD_CONNECTED_ITEM ) );
  1777. propMgr.AddProperty( new PROPERTY<PCB_TRACK, int>( _HKI( "Width" ),
  1778. &PCB_TRACK::SetWidth, &PCB_TRACK::GetWidth, PROPERTY_DISPLAY::PT_SIZE ) );
  1779. propMgr.ReplaceProperty( TYPE_HASH( BOARD_ITEM ), _HKI( "Position X" ),
  1780. new PROPERTY<PCB_TRACK, int>( _HKI( "Start X" ),
  1781. &PCB_TRACK::SetStartX, &PCB_TRACK::GetStartX, PROPERTY_DISPLAY::PT_COORD,
  1782. ORIGIN_TRANSFORMS::ABS_X_COORD) );
  1783. propMgr.ReplaceProperty( TYPE_HASH( BOARD_ITEM ), _HKI( "Position Y" ),
  1784. new PROPERTY<PCB_TRACK, int>( _HKI( "Start Y" ),
  1785. &PCB_TRACK::SetStartY, &PCB_TRACK::GetStartY, PROPERTY_DISPLAY::PT_COORD,
  1786. ORIGIN_TRANSFORMS::ABS_Y_COORD ) );
  1787. propMgr.AddProperty( new PROPERTY<PCB_TRACK, int>( _HKI( "End X" ),
  1788. &PCB_TRACK::SetEndX, &PCB_TRACK::GetEndX, PROPERTY_DISPLAY::PT_COORD,
  1789. ORIGIN_TRANSFORMS::ABS_X_COORD) );
  1790. propMgr.AddProperty( new PROPERTY<PCB_TRACK, int>( _HKI( "End Y" ),
  1791. &PCB_TRACK::SetEndY, &PCB_TRACK::GetEndY, PROPERTY_DISPLAY::PT_COORD,
  1792. ORIGIN_TRANSFORMS::ABS_Y_COORD) );
  1793. const wxString groupTechLayers = _HKI( "Technical Layers" );
  1794. auto isExternalLayerTrack =
  1795. []( INSPECTABLE* aItem )
  1796. {
  1797. if( PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( aItem ) )
  1798. return IsExternalCopperLayer( track->GetLayer() );
  1799. return false;
  1800. };
  1801. propMgr.AddProperty( new PROPERTY<PCB_TRACK, bool>( _HKI( "Soldermask" ),
  1802. &PCB_TRACK::SetHasSolderMask, &PCB_TRACK::HasSolderMask ), groupTechLayers )
  1803. .SetAvailableFunc( isExternalLayerTrack );
  1804. propMgr.AddProperty( new PROPERTY<PCB_TRACK, std::optional<int>>( _HKI( "Soldermask Margin Override" ),
  1805. &PCB_TRACK::SetLocalSolderMaskMargin, &PCB_TRACK::GetLocalSolderMaskMargin,
  1806. PROPERTY_DISPLAY::PT_SIZE ), groupTechLayers )
  1807. .SetAvailableFunc( isExternalLayerTrack );
  1808. // Arc
  1809. REGISTER_TYPE( PCB_ARC );
  1810. propMgr.InheritsAfter( TYPE_HASH( PCB_ARC ), TYPE_HASH( PCB_TRACK ) );
  1811. // Via
  1812. REGISTER_TYPE( PCB_VIA );
  1813. propMgr.InheritsAfter( TYPE_HASH( PCB_VIA ), TYPE_HASH( BOARD_CONNECTED_ITEM ) );
  1814. // TODO test drill, use getdrillvalue?
  1815. const wxString groupVia = _HKI( "Via Properties" );
  1816. propMgr.Mask( TYPE_HASH( PCB_VIA ), TYPE_HASH( BOARD_CONNECTED_ITEM ), _HKI( "Layer" ) );
  1817. // clang-format off: the suggestion is less readable
  1818. propMgr.AddProperty( new PROPERTY<PCB_VIA, int>( _HKI( "Diameter" ),
  1819. &PCB_VIA::SetFrontWidth, &PCB_VIA::GetFrontWidth, PROPERTY_DISPLAY::PT_SIZE ), groupVia );
  1820. propMgr.AddProperty( new PROPERTY<PCB_VIA, int>( _HKI( "Hole" ),
  1821. &PCB_VIA::SetDrill, &PCB_VIA::GetDrillValue, PROPERTY_DISPLAY::PT_SIZE ), groupVia );
  1822. propMgr.AddProperty( new PROPERTY_ENUM<PCB_VIA, PCB_LAYER_ID>( _HKI( "Layer Top" ),
  1823. &PCB_VIA::SetTopLayer, &PCB_VIA::GetLayer ), groupVia );
  1824. propMgr.AddProperty( new PROPERTY_ENUM<PCB_VIA, PCB_LAYER_ID>( _HKI( "Layer Bottom" ),
  1825. &PCB_VIA::SetBottomLayer, &PCB_VIA::BottomLayer ), groupVia );
  1826. propMgr.AddProperty( new PROPERTY_ENUM<PCB_VIA, VIATYPE>( _HKI( "Via Type" ),
  1827. &PCB_VIA::SetViaType, &PCB_VIA::GetViaType ), groupVia );
  1828. propMgr.AddProperty( new PROPERTY_ENUM<PCB_VIA, TENTING_MODE>( _HKI( "Front tenting" ),
  1829. &PCB_VIA::SetFrontTentingMode, &PCB_VIA::GetFrontTentingMode ), groupVia );
  1830. propMgr.AddProperty( new PROPERTY_ENUM<PCB_VIA, TENTING_MODE>( _HKI( "Back tenting" ),
  1831. &PCB_VIA::SetBackTentingMode, &PCB_VIA::GetBackTentingMode ), groupVia );
  1832. propMgr.AddProperty( new PROPERTY_ENUM<PCB_VIA, COVERING_MODE>( _HKI( "Front covering" ),
  1833. &PCB_VIA::SetFrontCoveringMode, &PCB_VIA::GetFrontCoveringMode ), groupVia );
  1834. propMgr.AddProperty( new PROPERTY_ENUM<PCB_VIA, COVERING_MODE>( _HKI( "Back covering" ),
  1835. &PCB_VIA::SetBackCoveringMode, &PCB_VIA::GetBackCoveringMode ), groupVia );
  1836. propMgr.AddProperty( new PROPERTY_ENUM<PCB_VIA, PLUGGING_MODE>( _HKI( "Front plugging" ),
  1837. &PCB_VIA::SetFrontPluggingMode, &PCB_VIA::GetFrontPluggingMode ), groupVia );
  1838. propMgr.AddProperty( new PROPERTY_ENUM<PCB_VIA, PLUGGING_MODE>( _HKI( "Back plugging" ),
  1839. &PCB_VIA::SetBackPluggingMode, &PCB_VIA::GetBackPluggingMode ), groupVia );
  1840. propMgr.AddProperty( new PROPERTY_ENUM<PCB_VIA, CAPPING_MODE>( _HKI( "Capping" ),
  1841. &PCB_VIA::SetCappingMode, &PCB_VIA::GetCappingMode ), groupVia );
  1842. propMgr.AddProperty( new PROPERTY_ENUM<PCB_VIA, FILLING_MODE>( _HKI( "Filling" ),
  1843. &PCB_VIA::SetFillingMode, &PCB_VIA::GetFillingMode ), groupVia );
  1844. // clang-format on: the suggestion is less readable
  1845. }
  1846. } _TRACK_VIA_DESC;
  1847. ENUM_TO_WXANY( VIATYPE );
  1848. ENUM_TO_WXANY( TENTING_MODE );
  1849. ENUM_TO_WXANY( COVERING_MODE );
  1850. ENUM_TO_WXANY( PLUGGING_MODE );
  1851. ENUM_TO_WXANY( CAPPING_MODE );
  1852. ENUM_TO_WXANY( FILLING_MODE );