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.

2117 lines
59 KiB

18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
19 years ago
19 years ago
19 years ago
19 years ago
17 years ago
17 years ago
18 years ago
18 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
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 (C) 1992-2024 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 <lset.h>
  36. #include <string_utils.h>
  37. #include <view/view.h>
  38. #include <settings/color_settings.h>
  39. #include <settings/settings_manager.h>
  40. #include <geometry/geometry_utils.h>
  41. #include <geometry/seg.h>
  42. #include <geometry/shape_segment.h>
  43. #include <geometry/shape_circle.h>
  44. #include <geometry/shape_arc.h>
  45. #include <drc/drc_engine.h>
  46. #include <pcb_painter.h>
  47. #include <trigo.h>
  48. #include <google/protobuf/any.pb.h>
  49. #include <api/api_enums.h>
  50. #include <api/api_utils.h>
  51. #include <api/api_pcb_utils.h>
  52. #include <api/board/board_types.pb.h>
  53. using KIGFX::PCB_PAINTER;
  54. using KIGFX::PCB_RENDER_SETTINGS;
  55. PCB_TRACK::PCB_TRACK( BOARD_ITEM* aParent, KICAD_T idtype ) :
  56. BOARD_CONNECTED_ITEM( aParent, idtype )
  57. {
  58. m_width = pcbIUScale.mmToIU( 0.2 ); // Gives a reasonable default width
  59. m_hasSolderMask = false;
  60. }
  61. EDA_ITEM* PCB_TRACK::Clone() const
  62. {
  63. return new PCB_TRACK( *this );
  64. }
  65. PCB_ARC::PCB_ARC( BOARD_ITEM* aParent, const SHAPE_ARC* aArc ) :
  66. PCB_TRACK( aParent, PCB_ARC_T )
  67. {
  68. m_Start = aArc->GetP0();
  69. m_End = aArc->GetP1();
  70. m_Mid = aArc->GetArcMid();
  71. }
  72. EDA_ITEM* PCB_ARC::Clone() const
  73. {
  74. return new PCB_ARC( *this );
  75. }
  76. PCB_VIA::PCB_VIA( BOARD_ITEM* aParent ) :
  77. PCB_TRACK( aParent, PCB_VIA_T ),
  78. m_padStack( this )
  79. {
  80. SetViaType( VIATYPE::THROUGH );
  81. Padstack().Drill().start = F_Cu;
  82. Padstack().Drill().end = B_Cu;
  83. SetDrillDefault();
  84. m_padStack.SetUnconnectedLayerMode( PADSTACK::UNCONNECTED_LAYER_MODE::KEEP_ALL );
  85. // Padstack layerset is not used for vias right now
  86. m_padStack.LayerSet().reset();
  87. // For now, vias are always circles
  88. m_padStack.SetShape( PAD_SHAPE::CIRCLE, PADSTACK::ALL_LAYERS );
  89. for( PCB_LAYER_ID layer : LAYER_RANGE( F_Cu, B_Cu, BoardCopperLayerCount() ) )
  90. m_zoneLayerOverrides[layer] = ZLO_NONE;
  91. m_isFree = false;
  92. }
  93. PCB_VIA::PCB_VIA( const PCB_VIA& aOther ) :
  94. PCB_TRACK( aOther.GetParent(), PCB_VIA_T ),
  95. m_padStack( this )
  96. {
  97. PCB_VIA::operator=( aOther );
  98. const_cast<KIID&>( m_Uuid ) = aOther.m_Uuid;
  99. m_zoneLayerOverrides = aOther.m_zoneLayerOverrides;
  100. }
  101. PCB_VIA& PCB_VIA::operator=( const PCB_VIA &aOther )
  102. {
  103. BOARD_CONNECTED_ITEM::operator=( aOther );
  104. m_Start = aOther.m_Start;
  105. m_End = aOther.m_End;
  106. m_viaType = aOther.m_viaType;
  107. m_padStack = aOther.m_padStack;
  108. m_isFree = aOther.m_isFree;
  109. return *this;
  110. }
  111. EDA_ITEM* PCB_VIA::Clone() const
  112. {
  113. return new PCB_VIA( *this );
  114. }
  115. wxString PCB_VIA::GetItemDescription( UNITS_PROVIDER* aUnitsProvider, bool aFull ) const
  116. {
  117. wxString formatStr;
  118. switch( GetViaType() )
  119. {
  120. case VIATYPE::BLIND_BURIED: formatStr = _( "Blind/Buried Via %s on %s" ); break;
  121. case VIATYPE::MICROVIA: formatStr = _( "Micro Via %s on %s" ); break;
  122. default: formatStr = _( "Via %s on %s" ); break;
  123. }
  124. return wxString::Format( formatStr, GetNetnameMsg(), layerMaskDescribe() );
  125. }
  126. BITMAPS PCB_VIA::GetMenuImage() const
  127. {
  128. return BITMAPS::via;
  129. }
  130. bool PCB_TRACK::operator==( const BOARD_ITEM& aBoardItem ) const
  131. {
  132. if( aBoardItem.Type() != Type() )
  133. return false;
  134. const PCB_TRACK& other = static_cast<const PCB_TRACK&>( aBoardItem );
  135. return *this == other;
  136. }
  137. bool PCB_TRACK::operator==( const PCB_TRACK& aOther ) const
  138. {
  139. return m_Start == aOther.m_Start
  140. && m_End == aOther.m_End
  141. && m_layer == aOther.m_layer
  142. && m_width == aOther.m_width
  143. && m_hasSolderMask == aOther.m_hasSolderMask
  144. && m_solderMaskMargin == aOther.m_solderMaskMargin;
  145. }
  146. double PCB_TRACK::Similarity( const BOARD_ITEM& aOther ) const
  147. {
  148. if( aOther.Type() != Type() )
  149. return 0.0;
  150. const PCB_TRACK& other = static_cast<const PCB_TRACK&>( aOther );
  151. double similarity = 1.0;
  152. if( m_layer != other.m_layer )
  153. similarity *= 0.9;
  154. if( m_width != other.m_width )
  155. similarity *= 0.9;
  156. if( m_Start != other.m_Start )
  157. similarity *= 0.9;
  158. if( m_End != other.m_End )
  159. similarity *= 0.9;
  160. if( m_hasSolderMask != other.m_hasSolderMask )
  161. similarity *= 0.9;
  162. if( m_solderMaskMargin != other.m_solderMaskMargin )
  163. similarity *= 0.9;
  164. return similarity;
  165. }
  166. bool PCB_ARC::operator==( const BOARD_ITEM& aBoardItem ) const
  167. {
  168. if( aBoardItem.Type() != Type() )
  169. return false;
  170. const PCB_ARC& other = static_cast<const PCB_ARC&>( aBoardItem );
  171. return *this == other;
  172. }
  173. bool PCB_ARC::operator==( const PCB_TRACK& aOther ) const
  174. {
  175. if( aOther.Type() != Type() )
  176. return false;
  177. const PCB_ARC& other = static_cast<const PCB_ARC&>( aOther );
  178. return *this == other;
  179. }
  180. bool PCB_ARC::operator==( const PCB_ARC& aOther ) const
  181. {
  182. return m_Start == aOther.m_Start
  183. && m_End == aOther.m_End
  184. && m_Mid == aOther.m_Mid
  185. && m_layer == aOther.m_layer
  186. && GetWidth() == aOther.GetWidth()
  187. && m_hasSolderMask == aOther.m_hasSolderMask
  188. && m_solderMaskMargin == aOther.m_solderMaskMargin;
  189. }
  190. double PCB_ARC::Similarity( const BOARD_ITEM& aOther ) const
  191. {
  192. if( aOther.Type() != Type() )
  193. return 0.0;
  194. const PCB_ARC& other = static_cast<const PCB_ARC&>( aOther );
  195. double similarity = 1.0;
  196. if( m_layer != other.m_layer )
  197. similarity *= 0.9;
  198. if( GetWidth() != other.GetWidth() )
  199. similarity *= 0.9;
  200. if( m_Start != other.m_Start )
  201. similarity *= 0.9;
  202. if( m_End != other.m_End )
  203. similarity *= 0.9;
  204. if( m_Mid != other.m_Mid )
  205. similarity *= 0.9;
  206. if( m_hasSolderMask != other.m_hasSolderMask )
  207. similarity *= 0.9;
  208. if( m_solderMaskMargin != other.m_solderMaskMargin )
  209. similarity *= 0.9;
  210. return similarity;
  211. }
  212. bool PCB_VIA::operator==( const BOARD_ITEM& aBoardItem ) const
  213. {
  214. if( aBoardItem.Type() != Type() )
  215. return false;
  216. const PCB_VIA& other = static_cast<const PCB_VIA&>( aBoardItem );
  217. return *this == other;
  218. }
  219. bool PCB_VIA::operator==( const PCB_TRACK& aOther ) const
  220. {
  221. if( aOther.Type() != Type() )
  222. return false;
  223. const PCB_VIA& other = static_cast<const PCB_VIA&>( aOther );
  224. return *this == other;
  225. }
  226. bool PCB_VIA::operator==( const PCB_VIA& aOther ) const
  227. {
  228. return m_Start == aOther.m_Start
  229. && m_End == aOther.m_End
  230. && m_layer == aOther.m_layer
  231. && m_padStack == aOther.m_padStack
  232. && m_viaType == aOther.m_viaType
  233. && m_zoneLayerOverrides == aOther.m_zoneLayerOverrides;
  234. }
  235. double PCB_VIA::Similarity( const BOARD_ITEM& aOther ) const
  236. {
  237. if( aOther.Type() != Type() )
  238. return 0.0;
  239. const PCB_VIA& other = static_cast<const PCB_VIA&>( aOther );
  240. double similarity = 1.0;
  241. if( m_layer != other.m_layer )
  242. similarity *= 0.9;
  243. if( m_Start != other.m_Start )
  244. similarity *= 0.9;
  245. if( m_End != other.m_End )
  246. similarity *= 0.9;
  247. if( m_padStack != other.m_padStack )
  248. similarity *= 0.9;
  249. if( m_viaType != other.m_viaType )
  250. similarity *= 0.9;
  251. if( m_zoneLayerOverrides != other.m_zoneLayerOverrides )
  252. similarity *= 0.9;
  253. return similarity;
  254. }
  255. void PCB_VIA::SetWidth( int aWidth )
  256. {
  257. // This is present because of the parent class. It should never be actually called on a via.
  258. wxASSERT_MSG( false, "Warning: PCB_VIA::SetWidth called without a layer argument" );
  259. m_padStack.SetSize( { aWidth, aWidth }, PADSTACK::ALL_LAYERS );
  260. }
  261. int PCB_VIA::GetWidth() const
  262. {
  263. // This is present because of the parent class. It should never be actually called on a via.
  264. wxASSERT_MSG( false, "Warning: PCB_VIA::GetWidth called without a layer argument" );
  265. return m_padStack.Size( PADSTACK::ALL_LAYERS ).x;
  266. }
  267. void PCB_VIA::SetWidth( PCB_LAYER_ID aLayer, int aWidth )
  268. {
  269. m_padStack.SetSize( { aWidth, aWidth }, aLayer );
  270. }
  271. int PCB_VIA::GetWidth( PCB_LAYER_ID aLayer ) const
  272. {
  273. return m_padStack.Size( aLayer ).x;
  274. }
  275. void PCB_TRACK::Serialize( google::protobuf::Any &aContainer ) const
  276. {
  277. kiapi::board::types::Track track;
  278. track.mutable_id()->set_value( m_Uuid.AsStdString() );
  279. track.mutable_start()->set_x_nm( GetStart().x );
  280. track.mutable_start()->set_y_nm( GetStart().y );
  281. track.mutable_end()->set_x_nm( GetEnd().x );
  282. track.mutable_end()->set_y_nm( GetEnd().y );
  283. track.mutable_width()->set_value_nm( GetWidth() );
  284. track.set_layer( ToProtoEnum<PCB_LAYER_ID, kiapi::board::types::BoardLayer>( GetLayer() ) );
  285. track.set_locked( IsLocked() ? kiapi::common::types::LockedState::LS_LOCKED
  286. : kiapi::common::types::LockedState::LS_UNLOCKED );
  287. track.mutable_net()->mutable_code()->set_value( GetNetCode() );
  288. track.mutable_net()->set_name( GetNetname() );
  289. // TODO m_hasSolderMask and m_solderMaskMargin
  290. aContainer.PackFrom( track );
  291. }
  292. bool PCB_TRACK::Deserialize( const google::protobuf::Any &aContainer )
  293. {
  294. kiapi::board::types::Track track;
  295. if( !aContainer.UnpackTo( &track ) )
  296. return false;
  297. const_cast<KIID&>( m_Uuid ) = KIID( track.id().value() );
  298. SetStart( VECTOR2I( track.start().x_nm(), track.start().y_nm() ) );
  299. SetEnd( VECTOR2I( track.end().x_nm(), track.end().y_nm() ) );
  300. SetWidth( track.width().value_nm() );
  301. SetLayer( FromProtoEnum<PCB_LAYER_ID, kiapi::board::types::BoardLayer>( track.layer() ) );
  302. SetNetCode( track.net().code().value() );
  303. SetLocked( track.locked() == kiapi::common::types::LockedState::LS_LOCKED );
  304. // TODO m_hasSolderMask and m_solderMaskMargin
  305. return true;
  306. }
  307. void PCB_ARC::Serialize( google::protobuf::Any &aContainer ) const
  308. {
  309. kiapi::board::types::Arc arc;
  310. arc.mutable_id()->set_value( m_Uuid.AsStdString() );
  311. arc.mutable_start()->set_x_nm( GetStart().x );
  312. arc.mutable_start()->set_y_nm( GetStart().y );
  313. arc.mutable_mid()->set_x_nm( GetMid().x );
  314. arc.mutable_mid()->set_y_nm( GetMid().y );
  315. arc.mutable_end()->set_x_nm( GetEnd().x );
  316. arc.mutable_end()->set_y_nm( GetEnd().y );
  317. arc.mutable_width()->set_value_nm( GetWidth() );
  318. arc.set_layer( ToProtoEnum<PCB_LAYER_ID, kiapi::board::types::BoardLayer>( GetLayer() ) );
  319. arc.set_locked( IsLocked() ? kiapi::common::types::LockedState::LS_LOCKED
  320. : kiapi::common::types::LockedState::LS_UNLOCKED );
  321. arc.mutable_net()->mutable_code()->set_value( GetNetCode() );
  322. arc.mutable_net()->set_name( GetNetname() );
  323. // TODO m_hasSolderMask and m_solderMaskMargin
  324. aContainer.PackFrom( arc );
  325. }
  326. bool PCB_ARC::Deserialize( const google::protobuf::Any &aContainer )
  327. {
  328. kiapi::board::types::Arc arc;
  329. if( !aContainer.UnpackTo( &arc ) )
  330. return false;
  331. const_cast<KIID&>( m_Uuid ) = KIID( arc.id().value() );
  332. SetStart( VECTOR2I( arc.start().x_nm(), arc.start().y_nm() ) );
  333. SetMid( VECTOR2I( arc.mid().x_nm(), arc.mid().y_nm() ) );
  334. SetEnd( VECTOR2I( arc.end().x_nm(), arc.end().y_nm() ) );
  335. SetWidth( arc.width().value_nm() );
  336. SetLayer( FromProtoEnum<PCB_LAYER_ID, kiapi::board::types::BoardLayer>( arc.layer() ) );
  337. SetNetCode( arc.net().code().value() );
  338. SetLocked( arc.locked() == kiapi::common::types::LockedState::LS_LOCKED );
  339. // TODO m_hasSolderMask and m_solderMaskMargin
  340. return true;
  341. }
  342. void PCB_VIA::Serialize( google::protobuf::Any &aContainer ) const
  343. {
  344. kiapi::board::types::Via via;
  345. via.mutable_id()->set_value( m_Uuid.AsStdString() );
  346. via.mutable_position()->set_x_nm( GetPosition().x );
  347. via.mutable_position()->set_y_nm( GetPosition().y );
  348. PADSTACK padstack = Padstack();
  349. google::protobuf::Any padStackWrapper;
  350. padstack.Serialize( padStackWrapper );
  351. padStackWrapper.UnpackTo( via.mutable_pad_stack() );
  352. // PADSTACK::m_layerSet is not used by vias
  353. via.mutable_pad_stack()->clear_layers();
  354. kiapi::board::PackLayerSet( *via.mutable_pad_stack()->mutable_layers(), GetLayerSet() );
  355. via.set_type( ToProtoEnum<VIATYPE, kiapi::board::types::ViaType>( GetViaType() ) );
  356. via.set_locked( IsLocked() ? kiapi::common::types::LockedState::LS_LOCKED
  357. : kiapi::common::types::LockedState::LS_UNLOCKED );
  358. via.mutable_net()->mutable_code()->set_value( GetNetCode() );
  359. via.mutable_net()->set_name( GetNetname() );
  360. aContainer.PackFrom( via );
  361. }
  362. bool PCB_VIA::Deserialize( const google::protobuf::Any &aContainer )
  363. {
  364. kiapi::board::types::Via via;
  365. if( !aContainer.UnpackTo( &via ) )
  366. return false;
  367. const_cast<KIID&>( m_Uuid ) = KIID( via.id().value() );
  368. SetStart( VECTOR2I( via.position().x_nm(), via.position().y_nm() ) );
  369. SetEnd( GetStart() );
  370. google::protobuf::Any padStackWrapper;
  371. padStackWrapper.PackFrom( via.pad_stack() );
  372. if( !m_padStack.Deserialize( padStackWrapper ) )
  373. return false;
  374. // PADSTACK::m_layerSet is not used by vias
  375. m_padStack.LayerSet().reset();
  376. SetViaType( FromProtoEnum<VIATYPE>( via.type() ) );
  377. SetNetCode( via.net().code().value() );
  378. SetLocked( via.locked() == kiapi::common::types::LockedState::LS_LOCKED );
  379. return true;
  380. }
  381. bool PCB_TRACK::ApproxCollinear( const PCB_TRACK& aTrack )
  382. {
  383. SEG a( m_Start, m_End );
  384. SEG b( aTrack.GetStart(), aTrack.GetEnd() );
  385. return a.ApproxCollinear( b );
  386. }
  387. MINOPTMAX<int> PCB_TRACK::GetWidthConstraint( wxString* aSource ) const
  388. {
  389. DRC_CONSTRAINT constraint;
  390. if( GetBoard() && GetBoard()->GetDesignSettings().m_DRCEngine )
  391. {
  392. BOARD_DESIGN_SETTINGS& bds = GetBoard()->GetDesignSettings();
  393. constraint = bds.m_DRCEngine->EvalRules( TRACK_WIDTH_CONSTRAINT, this, nullptr, m_layer );
  394. }
  395. if( aSource )
  396. *aSource = constraint.GetName();
  397. return constraint.Value();
  398. }
  399. MINOPTMAX<int> PCB_VIA::GetWidthConstraint( wxString* aSource ) const
  400. {
  401. DRC_CONSTRAINT constraint;
  402. if( GetBoard() && GetBoard()->GetDesignSettings().m_DRCEngine )
  403. {
  404. BOARD_DESIGN_SETTINGS& bds = GetBoard()->GetDesignSettings();
  405. constraint = bds.m_DRCEngine->EvalRules( VIA_DIAMETER_CONSTRAINT, this, nullptr, m_layer );
  406. }
  407. if( aSource )
  408. *aSource = constraint.GetName();
  409. return constraint.Value();
  410. }
  411. MINOPTMAX<int> PCB_VIA::GetDrillConstraint( wxString* aSource ) const
  412. {
  413. DRC_CONSTRAINT constraint;
  414. if( GetBoard() && GetBoard()->GetDesignSettings().m_DRCEngine )
  415. {
  416. BOARD_DESIGN_SETTINGS& bds = GetBoard()->GetDesignSettings();
  417. constraint = bds.m_DRCEngine->EvalRules( HOLE_SIZE_CONSTRAINT, this, nullptr, m_layer );
  418. }
  419. if( aSource )
  420. *aSource = constraint.GetName();
  421. return constraint.Value();
  422. }
  423. int PCB_VIA::GetMinAnnulus( PCB_LAYER_ID aLayer, wxString* aSource ) const
  424. {
  425. if( !FlashLayer( aLayer ) )
  426. {
  427. if( aSource )
  428. *aSource = _( "removed annular ring" );
  429. return 0;
  430. }
  431. DRC_CONSTRAINT constraint;
  432. if( GetBoard() && GetBoard()->GetDesignSettings().m_DRCEngine )
  433. {
  434. BOARD_DESIGN_SETTINGS& bds = GetBoard()->GetDesignSettings();
  435. constraint = bds.m_DRCEngine->EvalRules( ANNULAR_WIDTH_CONSTRAINT, this, nullptr, aLayer );
  436. }
  437. if( constraint.Value().HasMin() )
  438. {
  439. if( aSource )
  440. *aSource = constraint.GetName();
  441. return constraint.Value().Min();
  442. }
  443. return 0;
  444. }
  445. int PCB_VIA::GetDrillValue() const
  446. {
  447. if( m_padStack.Drill().size.x > 0 ) // Use the specific value.
  448. return m_padStack.Drill().size.x;
  449. // Use the default value from the Netclass
  450. NETCLASS* netclass = GetEffectiveNetClass();
  451. if( GetViaType() == VIATYPE::MICROVIA )
  452. return netclass->GetuViaDrill();
  453. return netclass->GetViaDrill();
  454. }
  455. EDA_ITEM_FLAGS PCB_TRACK::IsPointOnEnds( const VECTOR2I& point, int min_dist ) const
  456. {
  457. EDA_ITEM_FLAGS result = 0;
  458. if( min_dist < 0 )
  459. min_dist = m_width / 2;
  460. if( min_dist == 0 )
  461. {
  462. if( m_Start == point )
  463. result |= STARTPOINT;
  464. if( m_End == point )
  465. result |= ENDPOINT;
  466. }
  467. else
  468. {
  469. double dist = m_Start.Distance( point );
  470. if( min_dist >= dist )
  471. result |= STARTPOINT;
  472. dist = m_End.Distance( point );
  473. if( min_dist >= dist )
  474. result |= ENDPOINT;
  475. }
  476. return result;
  477. }
  478. const BOX2I PCB_TRACK::GetBoundingBox() const
  479. {
  480. // end of track is round, this is its radius, rounded up
  481. int radius = ( m_width + 1 ) / 2;
  482. int ymax, xmax, ymin, xmin;
  483. if( Type() == PCB_VIA_T )
  484. {
  485. ymax = m_Start.y;
  486. xmax = m_Start.x;
  487. ymin = m_Start.y;
  488. xmin = m_Start.x;
  489. }
  490. else if( Type() == PCB_ARC_T )
  491. {
  492. std::shared_ptr<SHAPE> arc = GetEffectiveShape();
  493. BOX2I bbox = arc->BBox();
  494. xmin = bbox.GetLeft();
  495. xmax = bbox.GetRight();
  496. ymin = bbox.GetTop();
  497. ymax = bbox.GetBottom();
  498. }
  499. else
  500. {
  501. ymax = std::max( m_Start.y, m_End.y );
  502. xmax = std::max( m_Start.x, m_End.x );
  503. ymin = std::min( m_Start.y, m_End.y );
  504. xmin = std::min( m_Start.x, m_End.x );
  505. }
  506. ymax += radius;
  507. xmax += radius;
  508. ymin -= radius;
  509. xmin -= radius;
  510. // return a rectangle which is [pos,dim) in nature. therefore the +1
  511. return BOX2ISafe( VECTOR2I( xmin, ymin ),
  512. VECTOR2L( (int64_t) xmax - xmin + 1, (int64_t) ymax - ymin + 1 ) );
  513. }
  514. const BOX2I PCB_VIA::GetBoundingBox() const
  515. {
  516. int radius = 0;
  517. Padstack().ForEachUniqueLayer(
  518. [&]( PCB_LAYER_ID aLayer )
  519. {
  520. radius = std::max( radius, GetWidth( aLayer ) );
  521. } );
  522. // via is round, this is its radius, rounded up
  523. radius = ( radius + 1 ) / 2;
  524. int ymax = m_Start.y + radius;
  525. int xmax = m_Start.x + radius;
  526. int ymin = m_Start.y - radius;
  527. int xmin = m_Start.x - radius;
  528. // return a rectangle which is [pos,dim) in nature. therefore the +1
  529. return BOX2ISafe( VECTOR2I( xmin, ymin ),
  530. VECTOR2L( (int64_t) xmax - xmin + 1, (int64_t) ymax - ymin + 1 ) );
  531. }
  532. double PCB_TRACK::GetLength() const
  533. {
  534. return m_Start.Distance( m_End );
  535. }
  536. void PCB_TRACK::Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
  537. {
  538. RotatePoint( m_Start, aRotCentre, aAngle );
  539. RotatePoint( m_End, aRotCentre, aAngle );
  540. }
  541. void PCB_ARC::Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
  542. {
  543. RotatePoint( m_Start, aRotCentre, aAngle );
  544. RotatePoint( m_End, aRotCentre, aAngle );
  545. RotatePoint( m_Mid, aRotCentre, aAngle );
  546. }
  547. void PCB_TRACK::Mirror( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
  548. {
  549. MIRROR( m_Start, aCentre, aFlipDirection );
  550. MIRROR( m_End, aCentre, aFlipDirection );
  551. }
  552. void PCB_ARC::Mirror( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
  553. {
  554. MIRROR( m_Start, aCentre, aFlipDirection );
  555. MIRROR( m_End, aCentre, aFlipDirection );
  556. MIRROR( m_Mid, aCentre, aFlipDirection );
  557. }
  558. void PCB_TRACK::Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
  559. {
  560. if( aFlipDirection == FLIP_DIRECTION::LEFT_RIGHT )
  561. {
  562. m_Start.x = aCentre.x - ( m_Start.x - aCentre.x );
  563. m_End.x = aCentre.x - ( m_End.x - aCentre.x );
  564. }
  565. else
  566. {
  567. m_Start.y = aCentre.y - ( m_Start.y - aCentre.y );
  568. m_End.y = aCentre.y - ( m_End.y - aCentre.y );
  569. }
  570. SetLayer( GetBoard()->FlipLayer( GetLayer() ) );
  571. }
  572. void PCB_ARC::Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
  573. {
  574. if( aFlipDirection == FLIP_DIRECTION::LEFT_RIGHT )
  575. {
  576. m_Start.x = aCentre.x - ( m_Start.x - aCentre.x );
  577. m_End.x = aCentre.x - ( m_End.x - aCentre.x );
  578. m_Mid.x = aCentre.x - ( m_Mid.x - aCentre.x );
  579. }
  580. else
  581. {
  582. m_Start.y = aCentre.y - ( m_Start.y - aCentre.y );
  583. m_End.y = aCentre.y - ( m_End.y - aCentre.y );
  584. m_Mid.y = aCentre.y - ( m_Mid.y - aCentre.y );
  585. }
  586. SetLayer( GetBoard()->FlipLayer( GetLayer() ) );
  587. }
  588. bool PCB_ARC::IsCCW() const
  589. {
  590. VECTOR2L start = m_Start;
  591. VECTOR2L start_end = m_End - start;
  592. VECTOR2L start_mid = m_Mid - start;
  593. return start_end.Cross( start_mid ) < 0;
  594. }
  595. void PCB_VIA::Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
  596. {
  597. if( aFlipDirection == FLIP_DIRECTION::LEFT_RIGHT )
  598. {
  599. m_Start.x = aCentre.x - ( m_Start.x - aCentre.x );
  600. m_End.x = aCentre.x - ( m_End.x - aCentre.x );
  601. }
  602. else
  603. {
  604. m_Start.y = aCentre.y - ( m_Start.y - aCentre.y );
  605. m_End.y = aCentre.y - ( m_End.y - aCentre.y );
  606. }
  607. if( GetViaType() != VIATYPE::THROUGH )
  608. {
  609. PCB_LAYER_ID top_layer;
  610. PCB_LAYER_ID bottom_layer;
  611. LayerPair( &top_layer, &bottom_layer );
  612. top_layer = GetBoard()->FlipLayer( top_layer );
  613. bottom_layer = GetBoard()->FlipLayer( bottom_layer );
  614. SetLayerPair( top_layer, bottom_layer );
  615. }
  616. }
  617. INSPECT_RESULT PCB_TRACK::Visit( INSPECTOR inspector, void* testData,
  618. const std::vector<KICAD_T>& aScanTypes )
  619. {
  620. for( KICAD_T scanType : aScanTypes )
  621. {
  622. if( scanType == Type() )
  623. {
  624. if( INSPECT_RESULT::QUIT == inspector( this, testData ) )
  625. return INSPECT_RESULT::QUIT;
  626. }
  627. }
  628. return INSPECT_RESULT::CONTINUE;
  629. }
  630. std::shared_ptr<SHAPE_SEGMENT> PCB_VIA::GetEffectiveHoleShape() const
  631. {
  632. return std::make_shared<SHAPE_SEGMENT>( SEG( m_Start, m_Start ), Padstack().Drill().size.x );
  633. }
  634. void PCB_VIA::SetFrontTentingMode( TENTING_MODE aMode )
  635. {
  636. switch( aMode )
  637. {
  638. case TENTING_MODE::FROM_RULES: m_padStack.FrontOuterLayers().has_solder_mask.reset(); break;
  639. case TENTING_MODE::TENTED: m_padStack.FrontOuterLayers().has_solder_mask = true; break;
  640. case TENTING_MODE::NOT_TENTED: m_padStack.FrontOuterLayers().has_solder_mask = false; break;
  641. }
  642. }
  643. TENTING_MODE PCB_VIA::GetFrontTentingMode() const
  644. {
  645. if( m_padStack.FrontOuterLayers().has_solder_mask.has_value() )
  646. {
  647. return *m_padStack.FrontOuterLayers().has_solder_mask ?
  648. TENTING_MODE::TENTED : TENTING_MODE::NOT_TENTED;
  649. }
  650. return TENTING_MODE::FROM_RULES;
  651. }
  652. void PCB_VIA::SetBackTentingMode( TENTING_MODE aMode )
  653. {
  654. switch( aMode )
  655. {
  656. case TENTING_MODE::FROM_RULES: m_padStack.BackOuterLayers().has_solder_mask.reset(); break;
  657. case TENTING_MODE::TENTED: m_padStack.BackOuterLayers().has_solder_mask = true; break;
  658. case TENTING_MODE::NOT_TENTED: m_padStack.BackOuterLayers().has_solder_mask = false; break;
  659. }
  660. }
  661. TENTING_MODE PCB_VIA::GetBackTentingMode() const
  662. {
  663. if( m_padStack.BackOuterLayers().has_solder_mask.has_value() )
  664. {
  665. return *m_padStack.BackOuterLayers().has_solder_mask ?
  666. TENTING_MODE::TENTED : TENTING_MODE::NOT_TENTED;
  667. }
  668. return TENTING_MODE::FROM_RULES;
  669. }
  670. bool PCB_VIA::IsTented( PCB_LAYER_ID aLayer ) const
  671. {
  672. wxCHECK_MSG( IsFrontLayer( aLayer ) || IsBackLayer( aLayer ), true,
  673. "Invalid layer passed to IsTented" );
  674. bool front = IsFrontLayer( aLayer );
  675. if( front && m_padStack.FrontOuterLayers().has_solder_mask.has_value() )
  676. return *m_padStack.FrontOuterLayers().has_solder_mask;
  677. if( !front && m_padStack.BackOuterLayers().has_solder_mask.has_value() )
  678. return *m_padStack.BackOuterLayers().has_solder_mask;
  679. if( const BOARD* board = GetBoard() )
  680. {
  681. return front ? board->GetDesignSettings().m_TentViasFront
  682. : board->GetDesignSettings().m_TentViasBack;
  683. }
  684. return true;
  685. }
  686. int PCB_VIA::GetSolderMaskExpansion() const
  687. {
  688. if( const BOARD* board = GetBoard() )
  689. return board->GetDesignSettings().m_SolderMaskExpansion;
  690. else
  691. return 0;
  692. }
  693. int PCB_TRACK::GetSolderMaskExpansion() const
  694. {
  695. int margin = m_solderMaskMargin.value_or( 0 );
  696. // If no local margin is set, get the board's solder mask expansion value
  697. if( !m_solderMaskMargin.has_value() )
  698. {
  699. const BOARD* board = GetBoard();
  700. if( board )
  701. margin = board->GetDesignSettings().m_SolderMaskExpansion;
  702. }
  703. // Ensure the resulting mask opening has a non-negative size
  704. if( margin < 0 )
  705. margin = std::max( margin, -m_width / 2 );
  706. return margin;
  707. }
  708. bool PCB_TRACK::IsOnLayer( PCB_LAYER_ID aLayer ) const
  709. {
  710. if( aLayer == m_layer )
  711. {
  712. return true;
  713. }
  714. if( m_hasSolderMask
  715. && ( ( aLayer == F_Mask && m_layer == F_Cu )
  716. || ( aLayer == B_Mask && m_layer == B_Cu ) ) )
  717. {
  718. return true;
  719. }
  720. return false;
  721. }
  722. bool PCB_VIA::IsOnLayer( PCB_LAYER_ID aLayer ) const
  723. {
  724. #if 0
  725. // Nice and simple, but raises its ugly head in performance profiles....
  726. return GetLayerSet().test( aLayer );
  727. #endif
  728. if( IsCopperLayer( aLayer ) &&
  729. LAYER_RANGE::Contains( Padstack().Drill().start, Padstack().Drill().end, aLayer ) )
  730. {
  731. return true;
  732. }
  733. // Test for via on mask layers: a via on on a mask layer if not tented and if
  734. // it is on the corresponding external copper layer
  735. if( aLayer == F_Mask )
  736. return Padstack().Drill().start == F_Cu && !IsTented( F_Mask );
  737. else if( aLayer == B_Mask )
  738. return Padstack().Drill().end == B_Cu && !IsTented( B_Mask );
  739. return false;
  740. }
  741. bool PCB_VIA::HasValidLayerPair( int aCopperLayerCount )
  742. {
  743. // return true if top and bottom layers are valid, depending on the copper layer count
  744. // aCopperLayerCount is expected >= 2
  745. int layer_id = aCopperLayerCount*2;
  746. if( Padstack().Drill().start > B_Cu )
  747. {
  748. if( Padstack().Drill().start > layer_id )
  749. return false;
  750. }
  751. if( Padstack().Drill().end > B_Cu )
  752. {
  753. if( Padstack().Drill().end > layer_id )
  754. return false;
  755. }
  756. return true;
  757. }
  758. PCB_LAYER_ID PCB_VIA::GetLayer() const
  759. {
  760. return Padstack().Drill().start;
  761. }
  762. void PCB_VIA::SetLayer( PCB_LAYER_ID aLayer )
  763. {
  764. Padstack().Drill().start = aLayer;
  765. }
  766. void PCB_TRACK::SetLayerSet( const LSET& aLayerSet )
  767. {
  768. aLayerSet.RunOnLayers(
  769. [&]( PCB_LAYER_ID layer )
  770. {
  771. if( IsCopperLayer( layer ) )
  772. SetLayer( layer );
  773. else if( IsSolderMaskLayer( layer ) )
  774. SetHasSolderMask( true );
  775. } );
  776. }
  777. LSET PCB_TRACK::GetLayerSet() const
  778. {
  779. LSET layermask( { m_layer } );
  780. if( m_hasSolderMask )
  781. {
  782. if( layermask.test( F_Cu ) )
  783. layermask.set( F_Mask );
  784. else if( layermask.test( B_Cu ) )
  785. layermask.set( B_Mask );
  786. }
  787. return layermask;
  788. }
  789. LSET PCB_VIA::GetLayerSet() const
  790. {
  791. LSET layermask;
  792. if( Padstack().Drill().start < PCBNEW_LAYER_ID_START )
  793. return layermask;
  794. if( GetViaType() == VIATYPE::THROUGH )
  795. {
  796. layermask = LSET::AllCuMask( BoardCopperLayerCount() );
  797. }
  798. else
  799. {
  800. LAYER_RANGE range( Padstack().Drill().start, Padstack().Drill().end, BoardCopperLayerCount() );
  801. int cnt = BoardCopperLayerCount();
  802. // PCB_LAYER_IDs are numbered from front to back, this is top to bottom.
  803. for( PCB_LAYER_ID id : range )
  804. {
  805. layermask.set( id );
  806. if( --cnt <= 0 )
  807. break;
  808. }
  809. }
  810. if( !IsTented( F_Mask ) && layermask.test( F_Cu ) )
  811. layermask.set( F_Mask );
  812. if( !IsTented( B_Mask ) && layermask.test( B_Cu ) )
  813. layermask.set( B_Mask );
  814. return layermask;
  815. }
  816. void PCB_VIA::SetLayerSet( const LSET& aLayerSet )
  817. {
  818. // Vias do not use a LSET, just a top and bottom layer pair
  819. // So we need to set these 2 layers according to the allowed layers in aLayerSet
  820. // For via through, only F_Cu and B_Cu are allowed. aLayerSet is ignored
  821. if( GetViaType() == VIATYPE::THROUGH )
  822. {
  823. Padstack().Drill().start = F_Cu;
  824. Padstack().Drill().end = B_Cu;
  825. return;
  826. }
  827. // For blind buried vias, find the top and bottom layers
  828. bool top_found = false;
  829. bool bottom_found = false;
  830. aLayerSet.RunOnLayers(
  831. [&]( PCB_LAYER_ID layer )
  832. {
  833. // tpo layer and bottom Layer are copper layers, so consider only copper layers
  834. if( IsCopperLayer( layer ) )
  835. {
  836. // The top layer is the first layer found in list and
  837. // cannot the B_Cu
  838. if( !top_found && layer != B_Cu )
  839. {
  840. Padstack().Drill().start = layer;
  841. top_found = true;
  842. }
  843. // The bottom layer is the last layer found in list or B_Cu
  844. if( !bottom_found )
  845. Padstack().Drill().end = layer;
  846. if( layer == B_Cu )
  847. bottom_found = true;
  848. }
  849. } );
  850. }
  851. void PCB_VIA::SetLayerPair( PCB_LAYER_ID aTopLayer, PCB_LAYER_ID aBottomLayer )
  852. {
  853. Padstack().Drill().start = aTopLayer;
  854. Padstack().Drill().end = aBottomLayer;
  855. SanitizeLayers();
  856. }
  857. void PCB_VIA::SetTopLayer( PCB_LAYER_ID aLayer )
  858. {
  859. Padstack().Drill().start = aLayer;
  860. }
  861. void PCB_VIA::SetBottomLayer( PCB_LAYER_ID aLayer )
  862. {
  863. Padstack().Drill().end = aLayer;
  864. }
  865. void PCB_VIA::LayerPair( PCB_LAYER_ID* top_layer, PCB_LAYER_ID* bottom_layer ) const
  866. {
  867. PCB_LAYER_ID t_layer = F_Cu;
  868. PCB_LAYER_ID b_layer = B_Cu;
  869. if( GetViaType() != VIATYPE::THROUGH )
  870. {
  871. b_layer = Padstack().Drill().end;
  872. t_layer = Padstack().Drill().start;
  873. if( !IsCopperLayerLowerThan( b_layer, t_layer ) )
  874. std::swap( b_layer, t_layer );
  875. }
  876. if( top_layer )
  877. *top_layer = t_layer;
  878. if( bottom_layer )
  879. *bottom_layer = b_layer;
  880. }
  881. PCB_LAYER_ID PCB_VIA::TopLayer() const
  882. {
  883. return Padstack().Drill().start;
  884. }
  885. PCB_LAYER_ID PCB_VIA::BottomLayer() const
  886. {
  887. return Padstack().Drill().end;
  888. }
  889. void PCB_VIA::SanitizeLayers()
  890. {
  891. if( GetViaType() == VIATYPE::THROUGH )
  892. {
  893. Padstack().Drill().start = F_Cu;
  894. Padstack().Drill().end = B_Cu;
  895. }
  896. if( !IsCopperLayerLowerThan( Padstack().Drill().end, Padstack().Drill().start) )
  897. std::swap( Padstack().Drill().end, Padstack().Drill().start );
  898. }
  899. bool PCB_VIA::FlashLayer( LSET aLayers ) const
  900. {
  901. for( size_t ii = 0; ii < aLayers.size(); ++ii )
  902. {
  903. if( aLayers.test( ii ) )
  904. {
  905. PCB_LAYER_ID layer = PCB_LAYER_ID( ii );
  906. if( FlashLayer( layer ) )
  907. return true;
  908. }
  909. }
  910. return false;
  911. }
  912. bool PCB_VIA::FlashLayer( int aLayer ) const
  913. {
  914. // Return the "normal" shape if the caller doesn't specify a particular layer
  915. if( aLayer == UNDEFINED_LAYER )
  916. return true;
  917. const BOARD* board = GetBoard();
  918. if( !board )
  919. return true;
  920. if( !IsOnLayer( static_cast<PCB_LAYER_ID>( aLayer ) ) )
  921. return false;
  922. if( !IsCopperLayer( aLayer ) )
  923. return true;
  924. switch( Padstack().UnconnectedLayerMode() )
  925. {
  926. case PADSTACK::UNCONNECTED_LAYER_MODE::KEEP_ALL:
  927. return true;
  928. case PADSTACK::UNCONNECTED_LAYER_MODE::REMOVE_EXCEPT_START_AND_END:
  929. {
  930. if( aLayer == Padstack().Drill().start || aLayer == Padstack().Drill().end )
  931. return true;
  932. // Check for removal below
  933. break;
  934. }
  935. case PADSTACK::UNCONNECTED_LAYER_MODE::REMOVE_ALL:
  936. // Check for removal below
  937. break;
  938. }
  939. // Must be static to keep from raising its ugly head in performance profiles
  940. static std::initializer_list<KICAD_T> connectedTypes = { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T,
  941. PCB_PAD_T };
  942. if( GetZoneLayerOverride( static_cast<PCB_LAYER_ID>( aLayer ) ) == ZLO_FORCE_FLASHED )
  943. return true;
  944. else
  945. return board->GetConnectivity()->IsConnectedOnLayer( this, static_cast<PCB_LAYER_ID>( aLayer ), connectedTypes );
  946. }
  947. void PCB_VIA::ClearZoneLayerOverrides()
  948. {
  949. std::unique_lock<std::mutex> cacheLock( m_zoneLayerOverridesMutex );
  950. for( PCB_LAYER_ID layer : LAYER_RANGE( F_Cu, B_Cu, BoardCopperLayerCount() ) )
  951. m_zoneLayerOverrides[layer] = ZLO_NONE;
  952. }
  953. const ZONE_LAYER_OVERRIDE& PCB_VIA::GetZoneLayerOverride( PCB_LAYER_ID aLayer ) const
  954. {
  955. static const ZONE_LAYER_OVERRIDE defaultOverride = ZLO_NONE;
  956. auto it = m_zoneLayerOverrides.find( aLayer );
  957. return it != m_zoneLayerOverrides.end() ? it->second : defaultOverride;
  958. }
  959. void PCB_VIA::SetZoneLayerOverride( PCB_LAYER_ID aLayer, ZONE_LAYER_OVERRIDE aOverride )
  960. {
  961. std::unique_lock<std::mutex> cacheLock( m_zoneLayerOverridesMutex );
  962. m_zoneLayerOverrides[aLayer] = aOverride;
  963. }
  964. void PCB_VIA::GetOutermostConnectedLayers( PCB_LAYER_ID* aTopmost,
  965. PCB_LAYER_ID* aBottommost ) const
  966. {
  967. *aTopmost = UNDEFINED_LAYER;
  968. *aBottommost = UNDEFINED_LAYER;
  969. static std::initializer_list<KICAD_T> connectedTypes = { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T,
  970. PCB_PAD_T };
  971. for( int layer = TopLayer(); layer <= BottomLayer(); ++layer )
  972. {
  973. bool connected = false;
  974. if( GetZoneLayerOverride( static_cast<PCB_LAYER_ID>( layer ) ) == ZLO_FORCE_FLASHED )
  975. connected = true;
  976. else if( GetBoard()->GetConnectivity()->IsConnectedOnLayer( this, layer, connectedTypes ) )
  977. connected = true;
  978. if( connected )
  979. {
  980. if( *aTopmost == UNDEFINED_LAYER )
  981. *aTopmost = ToLAYER_ID( layer );
  982. *aBottommost = ToLAYER_ID( layer );
  983. }
  984. }
  985. }
  986. std::vector<int> PCB_TRACK::ViewGetLayers() const
  987. {
  988. // Show the track and its netname on different layers
  989. std::vector<int> layers { GetLayer(), GetNetnameLayer( GetLayer() ) };
  990. layers.reserve( 5 );
  991. if( m_hasSolderMask )
  992. {
  993. if( m_layer == F_Cu )
  994. layers.push_back( F_Mask );
  995. else if( m_layer == B_Cu )
  996. layers.push_back( B_Mask );
  997. }
  998. if( IsLocked() )
  999. layers.push_back( LAYER_LOCKED_ITEM_SHADOW );
  1000. return layers;
  1001. }
  1002. double PCB_TRACK::ViewGetLOD( int aLayer, KIGFX::VIEW* aView ) const
  1003. {
  1004. constexpr double HIDE = std::numeric_limits<double>::max();
  1005. PCB_PAINTER* painter = static_cast<PCB_PAINTER*>( aView->GetPainter() );
  1006. PCB_RENDER_SETTINGS* renderSettings = painter->GetSettings();
  1007. if( !aView->IsLayerVisible( LAYER_TRACKS ) )
  1008. return HIDE;
  1009. if( IsNetnameLayer( aLayer ) )
  1010. {
  1011. if( GetNetCode() <= NETINFO_LIST::UNCONNECTED )
  1012. return HIDE;
  1013. // Hide netnames on dimmed tracks
  1014. if( renderSettings->GetHighContrast() )
  1015. {
  1016. if( m_layer != renderSettings->GetPrimaryHighContrastLayer() )
  1017. return HIDE;
  1018. }
  1019. VECTOR2I start( GetStart() );
  1020. VECTOR2I end( GetEnd() );
  1021. // Calc the approximate size of the netname (assume square chars)
  1022. SEG::ecoord nameSize = GetDisplayNetname().size() * GetWidth();
  1023. if( VECTOR2I( end - start ).SquaredEuclideanNorm() < nameSize * nameSize )
  1024. return HIDE;
  1025. BOX2I clipBox = BOX2ISafe( aView->GetViewport() );
  1026. ClipLine( &clipBox, start.x, start.y, end.x, end.y );
  1027. if( VECTOR2I( end - start ).SquaredEuclideanNorm() == 0 )
  1028. return HIDE;
  1029. // Netnames will be shown only if zoom is appropriate
  1030. return ( double ) pcbIUScale.mmToIU( 4 ) / ( m_width + 1 );
  1031. }
  1032. if( aLayer == LAYER_LOCKED_ITEM_SHADOW )
  1033. {
  1034. // Hide shadow if the main layer is not shown
  1035. if( !aView->IsLayerVisible( m_layer ) )
  1036. return HIDE;
  1037. // Hide shadow on dimmed tracks
  1038. if( renderSettings->GetHighContrast() )
  1039. {
  1040. if( m_layer != renderSettings->GetPrimaryHighContrastLayer() )
  1041. return HIDE;
  1042. }
  1043. }
  1044. // Other layers are shown without any conditions
  1045. return 0.0;
  1046. }
  1047. const BOX2I PCB_TRACK::ViewBBox() const
  1048. {
  1049. BOX2I bbox = GetBoundingBox();
  1050. if( const BOARD* board = GetBoard() )
  1051. bbox.Inflate( 2 * board->GetDesignSettings().GetBiggestClearanceValue() );
  1052. else
  1053. bbox.Inflate( GetWidth() ); // Add a bit extra for safety
  1054. return bbox;
  1055. }
  1056. std::vector<int> PCB_VIA::ViewGetLayers() const
  1057. {
  1058. LAYER_RANGE layers( Padstack().Drill().start, Padstack().Drill().end, MAX_CU_LAYERS );
  1059. std::vector<int> ret_layers{ LAYER_VIA_HOLES, LAYER_VIA_HOLEWALLS, LAYER_PAD_NETNAMES };
  1060. ret_layers.reserve( MAX_CU_LAYERS + 6 );
  1061. // TODO(JE) Rendering order issue
  1062. #if 0
  1063. // Blind/buried vias (and microvias) use a different net name layer
  1064. PCB_LAYER_ID layerTop, layerBottom;
  1065. LayerPair( &layerTop, &layerBottom );
  1066. bool isBlindBuried =
  1067. m_viaType == VIATYPE::BLIND_BURIED
  1068. || ( m_viaType == VIATYPE::MICROVIA && ( layerTop != F_Cu || layerBottom != B_Cu ) );
  1069. #endif
  1070. for( PCB_LAYER_ID layer : layers )
  1071. ret_layers.push_back( layer );
  1072. if( IsLocked() )
  1073. ret_layers.push_back( LAYER_LOCKED_ITEM_SHADOW );
  1074. // Vias can also be on a solder mask layer. They are on these layers or not,
  1075. // depending on the plot and solder mask options
  1076. if( IsOnLayer( F_Mask ) )
  1077. ret_layers.push_back( F_Mask );
  1078. if( IsOnLayer( B_Mask ) )
  1079. ret_layers.push_back( B_Mask );
  1080. return ret_layers;
  1081. }
  1082. double PCB_VIA::ViewGetLOD( int aLayer, KIGFX::VIEW* aView ) const
  1083. {
  1084. constexpr double HIDE = (double)std::numeric_limits<double>::max();
  1085. PCB_PAINTER* painter = static_cast<PCB_PAINTER*>( aView->GetPainter() );
  1086. PCB_RENDER_SETTINGS* renderSettings = painter->GetSettings();
  1087. LSET visible = LSET::AllLayersMask();
  1088. // Meta control for hiding all vias
  1089. if( !aView->IsLayerVisible( LAYER_VIAS ) )
  1090. return HIDE;
  1091. // Handle board visibility
  1092. if( const BOARD* board = GetBoard() )
  1093. visible = board->GetVisibleLayers() & board->GetEnabledLayers();
  1094. int width = GetWidth( ToLAYER_ID( aLayer ) );
  1095. // In high contrast mode don't show vias that don't cross the high-contrast layer
  1096. if( renderSettings->GetHighContrast() )
  1097. {
  1098. PCB_LAYER_ID highContrastLayer = renderSettings->GetPrimaryHighContrastLayer();
  1099. if( LSET::FrontTechMask().Contains( highContrastLayer ) )
  1100. highContrastLayer = F_Cu;
  1101. else if( LSET::BackTechMask().Contains( highContrastLayer ) )
  1102. highContrastLayer = B_Cu;
  1103. if( !IsCopperLayer( highContrastLayer ) )
  1104. return HIDE;
  1105. if( GetViaType() != VIATYPE::THROUGH )
  1106. {
  1107. if( IsCopperLayerLowerThan( Padstack().Drill().start, highContrastLayer )
  1108. || IsCopperLayerLowerThan( highContrastLayer, Padstack().Drill().end ) )
  1109. {
  1110. return HIDE;
  1111. }
  1112. }
  1113. }
  1114. if( IsHoleLayer( aLayer ) )
  1115. {
  1116. if( m_viaType == VIATYPE::THROUGH )
  1117. {
  1118. // Show a through via's hole if any physical layer is shown
  1119. if( !( visible & LSET::PhysicalLayersMask() ).any() )
  1120. return HIDE;
  1121. }
  1122. else
  1123. {
  1124. // Show a blind or micro via's hole if it crosses a visible layer
  1125. if( !( visible & GetLayerSet() ).any() )
  1126. return HIDE;
  1127. }
  1128. // The hole won't be visible anyway at this scale
  1129. return (double) pcbIUScale.mmToIU( 0.25 ) / GetDrillValue();
  1130. }
  1131. else if( IsNetnameLayer( aLayer ) )
  1132. {
  1133. if( renderSettings->GetHighContrast() )
  1134. {
  1135. // Hide netnames unless via is flashed to a high-contrast layer
  1136. if( !FlashLayer( renderSettings->GetPrimaryHighContrastLayer() ) )
  1137. return HIDE;
  1138. }
  1139. else
  1140. {
  1141. // Hide netnames unless pad is flashed to a visible layer
  1142. if( !FlashLayer( visible ) )
  1143. return HIDE;
  1144. }
  1145. // Netnames will be shown only if zoom is appropriate
  1146. return width == 0 ? HIDE : ( (double)pcbIUScale.mmToIU( 10 ) / width );
  1147. }
  1148. if( !IsCopperLayer( aLayer ) )
  1149. return (double) pcbIUScale.mmToIU( 0.6 ) / width;
  1150. return 0.0;
  1151. }
  1152. wxString PCB_TRACK::GetFriendlyName() const
  1153. {
  1154. switch( Type() )
  1155. {
  1156. case PCB_ARC_T: return _( "Track (arc)" );
  1157. case PCB_VIA_T: return _( "Via" );
  1158. case PCB_TRACE_T:
  1159. default: return _( "Track" );
  1160. }
  1161. }
  1162. void PCB_TRACK::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
  1163. {
  1164. wxString msg;
  1165. BOARD* board = GetBoard();
  1166. aList.emplace_back( _( "Type" ), GetFriendlyName() );
  1167. GetMsgPanelInfoBase_Common( aFrame, aList );
  1168. aList.emplace_back( _( "Layer" ), layerMaskDescribe() );
  1169. aList.emplace_back( _( "Width" ), aFrame->MessageTextFromValue( m_width ) );
  1170. if( Type() == PCB_ARC_T )
  1171. {
  1172. double radius = static_cast<PCB_ARC*>( this )->GetRadius();
  1173. aList.emplace_back( _( "Radius" ), aFrame->MessageTextFromValue( radius ) );
  1174. }
  1175. aList.emplace_back( _( "Segment Length" ), aFrame->MessageTextFromValue( GetLength() ) );
  1176. // Display full track length (in Pcbnew)
  1177. if( board && GetNetCode() > 0 )
  1178. {
  1179. int count;
  1180. double trackLen;
  1181. double lenPadToDie;
  1182. std::tie( count, trackLen, lenPadToDie ) = board->GetTrackLength( *this );
  1183. aList.emplace_back( _( "Routed Length" ), aFrame->MessageTextFromValue( trackLen ) );
  1184. if( lenPadToDie != 0 )
  1185. {
  1186. msg = aFrame->MessageTextFromValue( lenPadToDie );
  1187. aList.emplace_back( _( "Pad To Die Length" ), msg );
  1188. msg = aFrame->MessageTextFromValue( trackLen + lenPadToDie );
  1189. aList.emplace_back( _( "Full Length" ), msg );
  1190. }
  1191. }
  1192. wxString source;
  1193. int clearance = GetOwnClearance( GetLayer(), &source );
  1194. aList.emplace_back( wxString::Format( _( "Min Clearance: %s" ),
  1195. aFrame->MessageTextFromValue( clearance ) ),
  1196. wxString::Format( _( "(from %s)" ), source ) );
  1197. MINOPTMAX<int> constraintValue = GetWidthConstraint( &source );
  1198. msg = aFrame->MessageTextFromMinOptMax( constraintValue );
  1199. if( !msg.IsEmpty() )
  1200. {
  1201. aList.emplace_back( wxString::Format( _( "Width Constraints: %s" ), msg ),
  1202. wxString::Format( _( "(from %s)" ), source ) );
  1203. }
  1204. }
  1205. void PCB_VIA::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
  1206. {
  1207. wxString msg;
  1208. switch( GetViaType() )
  1209. {
  1210. case VIATYPE::MICROVIA: msg = _( "Micro Via" ); break;
  1211. case VIATYPE::BLIND_BURIED: msg = _( "Blind/Buried Via" ); break;
  1212. case VIATYPE::THROUGH: msg = _( "Through Via" ); break;
  1213. default: msg = _( "Via" ); break;
  1214. }
  1215. aList.emplace_back( _( "Type" ), msg );
  1216. GetMsgPanelInfoBase_Common( aFrame, aList );
  1217. aList.emplace_back( _( "Layer" ), layerMaskDescribe() );
  1218. // TODO(JE) padstacks
  1219. aList.emplace_back( _( "Diameter" ),
  1220. aFrame->MessageTextFromValue( GetWidth( PADSTACK::ALL_LAYERS ) ) );
  1221. aList.emplace_back( _( "Hole" ), aFrame->MessageTextFromValue( GetDrillValue() ) );
  1222. wxString source;
  1223. int clearance = GetOwnClearance( GetLayer(), &source );
  1224. aList.emplace_back( wxString::Format( _( "Min Clearance: %s" ),
  1225. aFrame->MessageTextFromValue( clearance ) ),
  1226. wxString::Format( _( "(from %s)" ), source ) );
  1227. int minAnnulus = GetMinAnnulus( GetLayer(), &source );
  1228. aList.emplace_back( wxString::Format( _( "Min Annular Width: %s" ),
  1229. aFrame->MessageTextFromValue( minAnnulus ) ),
  1230. wxString::Format( _( "(from %s)" ), source ) );
  1231. }
  1232. void PCB_TRACK::GetMsgPanelInfoBase_Common( EDA_DRAW_FRAME* aFrame,
  1233. std::vector<MSG_PANEL_ITEM>& aList ) const
  1234. {
  1235. aList.emplace_back( _( "Net" ), UnescapeString( GetNetname() ) );
  1236. aList.emplace_back( _( "Resolved Netclass" ),
  1237. UnescapeString( GetEffectiveNetClass()->GetName() ) );
  1238. #if 0 // Enable for debugging
  1239. if( GetBoard() )
  1240. aList.emplace_back( _( "NetCode" ), wxString::Format( wxT( "%d" ), GetNetCode() ) );
  1241. aList.emplace_back( wxT( "Flags" ), wxString::Format( wxT( "0x%08X" ), m_flags ) );
  1242. aList.emplace_back( wxT( "Start pos" ), wxString::Format( wxT( "%d %d" ),
  1243. m_Start.x,
  1244. m_Start.y ) );
  1245. aList.emplace_back( wxT( "End pos" ), wxString::Format( wxT( "%d %d" ),
  1246. m_End.x,
  1247. m_End.y ) );
  1248. #endif
  1249. if( aFrame->GetName() == PCB_EDIT_FRAME_NAME && IsLocked() )
  1250. aList.emplace_back( _( "Status" ), _( "Locked" ) );
  1251. }
  1252. wxString PCB_VIA::layerMaskDescribe() const
  1253. {
  1254. const BOARD* board = GetBoard();
  1255. PCB_LAYER_ID top_layer;
  1256. PCB_LAYER_ID bottom_layer;
  1257. LayerPair( &top_layer, &bottom_layer );
  1258. return board->GetLayerName( top_layer ) + wxT( " - " ) + board->GetLayerName( bottom_layer );
  1259. }
  1260. bool PCB_TRACK::HitTest( const VECTOR2I& aPosition, int aAccuracy ) const
  1261. {
  1262. return TestSegmentHit( aPosition, m_Start, m_End, aAccuracy + ( m_width / 2 ) );
  1263. }
  1264. bool PCB_ARC::HitTest( const VECTOR2I& aPosition, int aAccuracy ) const
  1265. {
  1266. double max_dist = aAccuracy + ( GetWidth() / 2.0 );
  1267. // Short-circuit common cases where the arc is connected to a track or via at an endpoint
  1268. if( GetStart().Distance( aPosition ) <= max_dist || GetEnd().Distance( aPosition ) <= max_dist )
  1269. {
  1270. return true;
  1271. }
  1272. VECTOR2L center = GetPosition();
  1273. VECTOR2L relpos = aPosition - center;
  1274. int64_t dist = relpos.EuclideanNorm();
  1275. double radius = GetRadius();
  1276. if( std::abs( dist - radius ) > max_dist )
  1277. return false;
  1278. EDA_ANGLE arc_angle = GetAngle();
  1279. EDA_ANGLE arc_angle_start = GetArcAngleStart(); // Always 0.0 ... 360 deg
  1280. EDA_ANGLE arc_hittest( relpos );
  1281. // Calculate relative angle between the starting point of the arc, and the test point
  1282. arc_hittest -= arc_angle_start;
  1283. // Normalise arc_hittest between 0 ... 360 deg
  1284. arc_hittest.Normalize();
  1285. if( arc_angle < ANGLE_0 )
  1286. return arc_hittest >= ANGLE_360 + arc_angle;
  1287. return arc_hittest <= arc_angle;
  1288. }
  1289. bool PCB_VIA::HitTest( const VECTOR2I& aPosition, int aAccuracy ) const
  1290. {
  1291. bool hit = false;
  1292. Padstack().ForEachUniqueLayer(
  1293. [&]( PCB_LAYER_ID aLayer )
  1294. {
  1295. if( hit )
  1296. return;
  1297. int max_dist = aAccuracy + ( GetWidth( aLayer ) / 2 );
  1298. // rel_pos is aPosition relative to m_Start (or the center of the via)
  1299. VECTOR2D rel_pos = aPosition - m_Start;
  1300. double dist = rel_pos.x * rel_pos.x + rel_pos.y * rel_pos.y;
  1301. if( dist <= static_cast<double>( max_dist ) * max_dist )
  1302. hit = true;
  1303. } );
  1304. return hit;
  1305. }
  1306. bool PCB_TRACK::HitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const
  1307. {
  1308. BOX2I arect = aRect;
  1309. arect.Inflate( aAccuracy );
  1310. if( aContained )
  1311. return arect.Contains( GetStart() ) && arect.Contains( GetEnd() );
  1312. else
  1313. return arect.Intersects( GetStart(), GetEnd() );
  1314. }
  1315. bool PCB_ARC::HitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const
  1316. {
  1317. BOX2I arect = aRect;
  1318. arect.Inflate( aAccuracy );
  1319. BOX2I box( GetStart() );
  1320. box.Merge( GetMid() );
  1321. box.Merge( GetEnd() );
  1322. box.Inflate( GetWidth() / 2 );
  1323. if( aContained )
  1324. return arect.Contains( box );
  1325. else
  1326. return arect.Intersects( box );
  1327. }
  1328. bool PCB_VIA::HitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const
  1329. {
  1330. BOX2I arect = aRect;
  1331. arect.Inflate( aAccuracy );
  1332. bool hit = false;
  1333. Padstack().ForEachUniqueLayer(
  1334. [&]( PCB_LAYER_ID aLayer )
  1335. {
  1336. if( hit )
  1337. return;
  1338. BOX2I box( GetStart() );
  1339. box.Inflate( GetWidth( aLayer ) / 2 );
  1340. if( aContained )
  1341. hit = arect.Contains( box );
  1342. else
  1343. hit = arect.IntersectsCircle( GetStart(), GetWidth( aLayer ) / 2 );
  1344. } );
  1345. return hit;
  1346. }
  1347. wxString PCB_TRACK::GetItemDescription( UNITS_PROVIDER* aUnitsProvider, bool aFull ) const
  1348. {
  1349. return wxString::Format( Type() == PCB_ARC_T ? _("Track (arc) %s on %s, length %s" )
  1350. : _("Track %s on %s, length %s" ),
  1351. GetNetnameMsg(),
  1352. GetLayerName(),
  1353. aUnitsProvider->MessageTextFromValue( GetLength() ) );
  1354. }
  1355. BITMAPS PCB_TRACK::GetMenuImage() const
  1356. {
  1357. return BITMAPS::add_tracks;
  1358. }
  1359. void PCB_TRACK::swapData( BOARD_ITEM* aImage )
  1360. {
  1361. assert( aImage->Type() == PCB_TRACE_T );
  1362. std::swap( *((PCB_TRACK*) this), *((PCB_TRACK*) aImage) );
  1363. }
  1364. void PCB_ARC::swapData( BOARD_ITEM* aImage )
  1365. {
  1366. assert( aImage->Type() == PCB_ARC_T );
  1367. std::swap( *this, *static_cast<PCB_ARC*>( aImage ) );
  1368. }
  1369. void PCB_VIA::swapData( BOARD_ITEM* aImage )
  1370. {
  1371. assert( aImage->Type() == PCB_VIA_T );
  1372. std::swap( *((PCB_VIA*) this), *((PCB_VIA*) aImage) );
  1373. }
  1374. VECTOR2I PCB_ARC::GetPosition() const
  1375. {
  1376. VECTOR2I center = CalcArcCenter( m_Start, m_Mid, m_End );
  1377. return center;
  1378. }
  1379. double PCB_ARC::GetRadius() const
  1380. {
  1381. auto center = CalcArcCenter( m_Start, m_Mid , m_End );
  1382. return center.Distance( m_Start );
  1383. }
  1384. EDA_ANGLE PCB_ARC::GetAngle() const
  1385. {
  1386. VECTOR2D center = GetPosition();
  1387. EDA_ANGLE angle1 = EDA_ANGLE( m_Mid - center ) - EDA_ANGLE( m_Start - center );
  1388. EDA_ANGLE angle2 = EDA_ANGLE( m_End - center ) - EDA_ANGLE( m_Mid - center );
  1389. return angle1.Normalize180() + angle2.Normalize180();
  1390. }
  1391. EDA_ANGLE PCB_ARC::GetArcAngleStart() const
  1392. {
  1393. VECTOR2D pos( GetPosition() );
  1394. EDA_ANGLE angleStart( m_Start - pos );
  1395. return angleStart.Normalize();
  1396. }
  1397. // Note: used in python tests. Ignore CLion's claim that it's unused....
  1398. EDA_ANGLE PCB_ARC::GetArcAngleEnd() const
  1399. {
  1400. VECTOR2D pos( GetPosition() );
  1401. EDA_ANGLE angleEnd( m_End - pos );
  1402. return angleEnd.Normalize();
  1403. }
  1404. bool PCB_ARC::IsDegenerated( int aThreshold ) const
  1405. {
  1406. // Too small arcs cannot be really handled: arc center (and arc radius)
  1407. // cannot be safely computed if the distance between mid and end points
  1408. // is too small (a few internal units)
  1409. // len of both segments must be < aThreshold to be a very small degenerated arc
  1410. return ( GetMid() - GetStart() ).EuclideanNorm() < aThreshold
  1411. && ( GetMid() - GetEnd() ).EuclideanNorm() < aThreshold;
  1412. }
  1413. bool PCB_TRACK::cmp_tracks::operator() ( const PCB_TRACK* a, const PCB_TRACK* b ) const
  1414. {
  1415. if( a->GetNetCode() != b->GetNetCode() )
  1416. return a->GetNetCode() < b->GetNetCode();
  1417. if( a->GetLayer() != b->GetLayer() )
  1418. return a->GetLayer() < b->GetLayer();
  1419. if( a->Type() != b->Type() )
  1420. return a->Type() < b->Type();
  1421. if( a->m_Uuid != b->m_Uuid )
  1422. return a->m_Uuid < b->m_Uuid;
  1423. return a < b;
  1424. }
  1425. std::shared_ptr<SHAPE> PCB_TRACK::GetEffectiveShape( PCB_LAYER_ID aLayer, FLASHING aFlash ) const
  1426. {
  1427. int width = m_width;
  1428. if( IsSolderMaskLayer( aLayer ) )
  1429. width += 2 * GetSolderMaskExpansion();
  1430. return std::make_shared<SHAPE_SEGMENT>( m_Start, m_End, width );
  1431. }
  1432. std::shared_ptr<SHAPE> PCB_VIA::GetEffectiveShape( PCB_LAYER_ID aLayer, FLASHING aFlash ) const
  1433. {
  1434. if( aFlash == FLASHING::ALWAYS_FLASHED
  1435. || ( aFlash == FLASHING::DEFAULT && FlashLayer( aLayer ) ) )
  1436. {
  1437. int width = 0;
  1438. if( aLayer == UNDEFINED_LAYER )
  1439. {
  1440. Padstack().ForEachUniqueLayer(
  1441. [&]( PCB_LAYER_ID layer )
  1442. {
  1443. width = std::max( width, GetWidth( layer ) );
  1444. } );
  1445. width /= 2;
  1446. }
  1447. else
  1448. {
  1449. PCB_LAYER_ID cuLayer = m_padStack.EffectiveLayerFor( aLayer );
  1450. width = GetWidth( cuLayer ) / 2;
  1451. }
  1452. return std::make_shared<SHAPE_CIRCLE>( m_Start, width );
  1453. }
  1454. else
  1455. {
  1456. return std::make_shared<SHAPE_CIRCLE>( m_Start, GetDrillValue() / 2 );
  1457. }
  1458. }
  1459. std::shared_ptr<SHAPE> PCB_ARC::GetEffectiveShape( PCB_LAYER_ID aLayer, FLASHING aFlash ) const
  1460. {
  1461. int width = GetWidth();
  1462. if( IsSolderMaskLayer( aLayer ) )
  1463. width += 2 * GetSolderMaskExpansion();
  1464. return std::make_shared<SHAPE_ARC>( GetStart(), GetMid(), GetEnd(), width );
  1465. }
  1466. void PCB_TRACK::TransformShapeToPolygon( SHAPE_POLY_SET& aBuffer, PCB_LAYER_ID aLayer,
  1467. int aClearance, int aError, ERROR_LOC aErrorLoc,
  1468. bool ignoreLineWidth ) const
  1469. {
  1470. wxASSERT_MSG( !ignoreLineWidth, wxT( "IgnoreLineWidth has no meaning for tracks." ) );
  1471. switch( Type() )
  1472. {
  1473. case PCB_VIA_T:
  1474. {
  1475. int radius = ( static_cast<const PCB_VIA*>( this )->GetWidth( aLayer ) / 2 ) + aClearance;
  1476. TransformCircleToPolygon( aBuffer, m_Start, radius, aError, aErrorLoc );
  1477. break;
  1478. }
  1479. case PCB_ARC_T:
  1480. {
  1481. const PCB_ARC* arc = static_cast<const PCB_ARC*>( this );
  1482. int width = m_width + ( 2 * aClearance );
  1483. if( IsSolderMaskLayer( aLayer ) )
  1484. width += 2 * GetSolderMaskExpansion();
  1485. TransformArcToPolygon( aBuffer, arc->GetStart(), arc->GetMid(), arc->GetEnd(), width,
  1486. aError, aErrorLoc );
  1487. break;
  1488. }
  1489. default:
  1490. {
  1491. int width = m_width + ( 2 * aClearance );
  1492. if( IsSolderMaskLayer( aLayer ) )
  1493. width += 2 * GetSolderMaskExpansion();
  1494. TransformOvalToPolygon( aBuffer, m_Start, m_End, width, aError, aErrorLoc );
  1495. break;
  1496. }
  1497. }
  1498. }
  1499. static struct TRACK_VIA_DESC
  1500. {
  1501. TRACK_VIA_DESC()
  1502. {
  1503. ENUM_MAP<VIATYPE>::Instance()
  1504. .Undefined( VIATYPE::NOT_DEFINED )
  1505. .Map( VIATYPE::THROUGH, _HKI( "Through" ) )
  1506. .Map( VIATYPE::BLIND_BURIED, _HKI( "Blind/buried" ) )
  1507. .Map( VIATYPE::MICROVIA, _HKI( "Micro" ) );
  1508. ENUM_MAP<TENTING_MODE>::Instance()
  1509. .Undefined( TENTING_MODE::FROM_RULES )
  1510. .Map( TENTING_MODE::FROM_RULES, _HKI( "From design rules" ) )
  1511. .Map( TENTING_MODE::TENTED, _HKI( "Tented" ) )
  1512. .Map( TENTING_MODE::NOT_TENTED, _HKI( "Not tented" ) );
  1513. ENUM_MAP<PCB_LAYER_ID>& layerEnum = ENUM_MAP<PCB_LAYER_ID>::Instance();
  1514. if( layerEnum.Choices().GetCount() == 0 )
  1515. {
  1516. layerEnum.Undefined( UNDEFINED_LAYER );
  1517. for( PCB_LAYER_ID layer : LSET::AllLayersMask().Seq() )
  1518. layerEnum.Map( layer, LSET::Name( layer ) );
  1519. }
  1520. PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
  1521. // Track
  1522. REGISTER_TYPE( PCB_TRACK );
  1523. propMgr.InheritsAfter( TYPE_HASH( PCB_TRACK ), TYPE_HASH( BOARD_CONNECTED_ITEM ) );
  1524. propMgr.AddProperty( new PROPERTY<PCB_TRACK, int>( _HKI( "Width" ),
  1525. &PCB_TRACK::SetWidth, &PCB_TRACK::GetWidth, PROPERTY_DISPLAY::PT_SIZE ) );
  1526. propMgr.ReplaceProperty( TYPE_HASH( BOARD_ITEM ), _HKI( "Position X" ),
  1527. new PROPERTY<PCB_TRACK, int>( _HKI( "Start X" ),
  1528. &PCB_TRACK::SetStartX, &PCB_TRACK::GetStartX, PROPERTY_DISPLAY::PT_COORD,
  1529. ORIGIN_TRANSFORMS::ABS_X_COORD) );
  1530. propMgr.ReplaceProperty( TYPE_HASH( BOARD_ITEM ), _HKI( "Position Y" ),
  1531. new PROPERTY<PCB_TRACK, int>( _HKI( "Start Y" ),
  1532. &PCB_TRACK::SetStartY, &PCB_TRACK::GetStartY, PROPERTY_DISPLAY::PT_COORD,
  1533. ORIGIN_TRANSFORMS::ABS_Y_COORD ) );
  1534. propMgr.AddProperty( new PROPERTY<PCB_TRACK, int>( _HKI( "End X" ),
  1535. &PCB_TRACK::SetEndX, &PCB_TRACK::GetEndX, PROPERTY_DISPLAY::PT_COORD,
  1536. ORIGIN_TRANSFORMS::ABS_X_COORD) );
  1537. propMgr.AddProperty( new PROPERTY<PCB_TRACK, int>( _HKI( "End Y" ),
  1538. &PCB_TRACK::SetEndY, &PCB_TRACK::GetEndY, PROPERTY_DISPLAY::PT_COORD,
  1539. ORIGIN_TRANSFORMS::ABS_Y_COORD) );
  1540. const wxString groupTechLayers = _HKI( "Technical Layers" );
  1541. auto isExternalLayerTrack =
  1542. []( INSPECTABLE* aItem )
  1543. {
  1544. if( auto track = dynamic_cast<PCB_TRACK*>( aItem ) )
  1545. return track->GetLayer() == F_Cu || track->GetLayer() == B_Cu;
  1546. return false;
  1547. };
  1548. propMgr.AddProperty( new PROPERTY<PCB_TRACK, bool>( _HKI( "Soldermask" ),
  1549. &PCB_TRACK::SetHasSolderMask, &PCB_TRACK::HasSolderMask ), groupTechLayers )
  1550. .SetAvailableFunc( isExternalLayerTrack );
  1551. propMgr.AddProperty( new PROPERTY<PCB_TRACK, std::optional<int>>( _HKI( "Soldermask Margin Override" ),
  1552. &PCB_TRACK::SetLocalSolderMaskMargin, &PCB_TRACK::GetLocalSolderMaskMargin,
  1553. PROPERTY_DISPLAY::PT_SIZE ), groupTechLayers )
  1554. .SetAvailableFunc( isExternalLayerTrack );
  1555. // Arc
  1556. REGISTER_TYPE( PCB_ARC );
  1557. propMgr.InheritsAfter( TYPE_HASH( PCB_ARC ), TYPE_HASH( PCB_TRACK ) );
  1558. // Via
  1559. REGISTER_TYPE( PCB_VIA );
  1560. propMgr.InheritsAfter( TYPE_HASH( PCB_VIA ), TYPE_HASH( BOARD_CONNECTED_ITEM ) );
  1561. // TODO test drill, use getdrillvalue?
  1562. const wxString groupVia = _HKI( "Via Properties" );
  1563. propMgr.Mask( TYPE_HASH( PCB_VIA ), TYPE_HASH( BOARD_CONNECTED_ITEM ), _HKI( "Layer" ) );
  1564. propMgr.AddProperty( new PROPERTY<PCB_VIA, int>( _HKI( "Diameter" ),
  1565. &PCB_VIA::SetFrontWidth, &PCB_VIA::GetFrontWidth, PROPERTY_DISPLAY::PT_SIZE ), groupVia );
  1566. propMgr.AddProperty( new PROPERTY<PCB_VIA, int>( _HKI( "Hole" ),
  1567. &PCB_VIA::SetDrill, &PCB_VIA::GetDrillValue, PROPERTY_DISPLAY::PT_SIZE ), groupVia );
  1568. propMgr.AddProperty( new PROPERTY_ENUM<PCB_VIA, PCB_LAYER_ID>( _HKI( "Layer Top" ),
  1569. &PCB_VIA::SetLayer, &PCB_VIA::GetLayer ), groupVia );
  1570. propMgr.AddProperty( new PROPERTY_ENUM<PCB_VIA, PCB_LAYER_ID>( _HKI( "Layer Bottom" ),
  1571. &PCB_VIA::SetBottomLayer, &PCB_VIA::BottomLayer ), groupVia );
  1572. propMgr.AddProperty( new PROPERTY_ENUM<PCB_VIA, VIATYPE>( _HKI( "Via Type" ),
  1573. &PCB_VIA::SetViaType, &PCB_VIA::GetViaType ), groupVia );
  1574. propMgr.AddProperty( new PROPERTY_ENUM<PCB_VIA, TENTING_MODE>( _HKI( "Front tenting" ),
  1575. &PCB_VIA::SetFrontTentingMode, &PCB_VIA::GetFrontTentingMode ), groupVia );
  1576. propMgr.AddProperty( new PROPERTY_ENUM<PCB_VIA, TENTING_MODE>( _HKI( "Back tenting" ),
  1577. &PCB_VIA::SetBackTentingMode, &PCB_VIA::GetBackTentingMode ), groupVia );
  1578. }
  1579. } _TRACK_VIA_DESC;
  1580. ENUM_TO_WXANY( VIATYPE );
  1581. ENUM_TO_WXANY( TENTING_MODE );