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.

1531 lines
42 KiB

18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
4 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-2023 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_base_frame.h>
  27. #include <core/mirror.h>
  28. #include <connectivity/connectivity_data.h>
  29. #include <board.h>
  30. #include <board_design_settings.h>
  31. #include <convert_basic_shapes_to_polygon.h>
  32. #include <pcb_track.h>
  33. #include <base_units.h>
  34. #include <string_utils.h>
  35. #include <view/view.h>
  36. #include <settings/color_settings.h>
  37. #include <settings/settings_manager.h>
  38. #include <geometry/seg.h>
  39. #include <geometry/shape_segment.h>
  40. #include <geometry/shape_circle.h>
  41. #include <geometry/shape_arc.h>
  42. #include <drc/drc_engine.h>
  43. #include <pcb_painter.h>
  44. #include <trigo.h>
  45. using KIGFX::PCB_PAINTER;
  46. using KIGFX::PCB_RENDER_SETTINGS;
  47. PCB_TRACK::PCB_TRACK( BOARD_ITEM* aParent, KICAD_T idtype ) :
  48. BOARD_CONNECTED_ITEM( aParent, idtype )
  49. {
  50. m_Width = pcbIUScale.mmToIU( 0.2 ); // Gives a reasonable default width
  51. m_CachedScale = -1.0; // Set invalid to force update
  52. m_CachedLOD = 0.0; // Set to always display
  53. }
  54. EDA_ITEM* PCB_TRACK::Clone() const
  55. {
  56. return new PCB_TRACK( *this );
  57. }
  58. PCB_ARC::PCB_ARC( BOARD_ITEM* aParent, const SHAPE_ARC* aArc ) :
  59. PCB_TRACK( aParent, PCB_ARC_T )
  60. {
  61. m_Start = aArc->GetP0();
  62. m_End = aArc->GetP1();
  63. m_Mid = aArc->GetArcMid();
  64. }
  65. EDA_ITEM* PCB_ARC::Clone() const
  66. {
  67. return new PCB_ARC( *this );
  68. }
  69. PCB_VIA::PCB_VIA( BOARD_ITEM* aParent ) :
  70. PCB_TRACK( aParent, PCB_VIA_T )
  71. {
  72. SetViaType( VIATYPE::THROUGH );
  73. m_bottomLayer = B_Cu;
  74. SetDrillDefault();
  75. m_removeUnconnectedLayer = false;
  76. m_keepStartEndLayer = true;
  77. m_zoneLayerOverrides.fill( ZLO_NONE );
  78. m_isFree = false;
  79. }
  80. PCB_VIA::PCB_VIA( const PCB_VIA& aOther ) :
  81. PCB_TRACK( aOther.GetParent(), PCB_VIA_T )
  82. {
  83. PCB_VIA::operator=( aOther );
  84. const_cast<KIID&>( m_Uuid ) = aOther.m_Uuid;
  85. m_zoneLayerOverrides = aOther.m_zoneLayerOverrides;
  86. }
  87. PCB_VIA& PCB_VIA::operator=( const PCB_VIA &aOther )
  88. {
  89. BOARD_CONNECTED_ITEM::operator=( aOther );
  90. m_Width = aOther.m_Width;
  91. m_Start = aOther.m_Start;
  92. m_End = aOther.m_End;
  93. m_CachedLOD = aOther.m_CachedLOD;
  94. m_CachedScale = aOther.m_CachedScale;
  95. m_bottomLayer = aOther.m_bottomLayer;
  96. m_viaType = aOther.m_viaType;
  97. m_drill = aOther.m_drill;
  98. m_removeUnconnectedLayer = aOther.m_removeUnconnectedLayer;
  99. m_keepStartEndLayer = aOther.m_keepStartEndLayer;
  100. m_isFree = aOther.m_isFree;
  101. return *this;
  102. }
  103. EDA_ITEM* PCB_VIA::Clone() const
  104. {
  105. return new PCB_VIA( *this );
  106. }
  107. wxString PCB_VIA::GetItemDescription( UNITS_PROVIDER* aUnitsProvider ) const
  108. {
  109. wxString formatStr;
  110. switch( GetViaType() )
  111. {
  112. case VIATYPE::BLIND_BURIED: formatStr = _( "Blind/Buried Via %s on %s" ); break;
  113. case VIATYPE::MICROVIA: formatStr = _( "Micro Via %s on %s" ); break;
  114. default: formatStr = _( "Via %s on %s" ); break;
  115. }
  116. return wxString::Format( formatStr, GetNetnameMsg(), layerMaskDescribe() );
  117. }
  118. BITMAPS PCB_VIA::GetMenuImage() const
  119. {
  120. return BITMAPS::via;
  121. }
  122. bool PCB_TRACK::operator==( const BOARD_ITEM& aOther ) const
  123. {
  124. if( aOther.Type() != Type() )
  125. return false;
  126. const PCB_TRACK& other = static_cast<const PCB_TRACK&>( aOther );
  127. return m_Start == other.m_Start && m_End == other.m_End && m_layer == other.m_layer &&
  128. m_Width == other.m_Width;
  129. }
  130. double PCB_TRACK::Similarity( const BOARD_ITEM& aOther ) const
  131. {
  132. if( aOther.Type() != Type() )
  133. return 0.0;
  134. const PCB_TRACK& other = static_cast<const PCB_TRACK&>( aOther );
  135. double similarity = 1.0;
  136. if( m_layer != other.m_layer )
  137. similarity *= 0.9;
  138. if( m_Width != other.m_Width )
  139. similarity *= 0.9;
  140. if( m_Start != other.m_Start )
  141. similarity *= 0.9;
  142. if( m_End != other.m_End )
  143. similarity *= 0.9;
  144. return similarity;
  145. }
  146. bool PCB_ARC::operator==( const BOARD_ITEM& aOther ) const
  147. {
  148. if( aOther.Type() != Type() )
  149. return false;
  150. const PCB_ARC& other = static_cast<const PCB_ARC&>( aOther );
  151. return m_Start == other.m_Start && m_End == other.m_End && m_Mid == other.m_Mid &&
  152. m_layer == other.m_layer && m_Width == other.m_Width;
  153. }
  154. double PCB_ARC::Similarity( const BOARD_ITEM& aOther ) const
  155. {
  156. if( aOther.Type() != Type() )
  157. return 0.0;
  158. const PCB_ARC& other = static_cast<const PCB_ARC&>( aOther );
  159. double similarity = 1.0;
  160. if( m_layer != other.m_layer )
  161. similarity *= 0.9;
  162. if( m_Width != other.m_Width )
  163. similarity *= 0.9;
  164. if( m_Start != other.m_Start )
  165. similarity *= 0.9;
  166. if( m_End != other.m_End )
  167. similarity *= 0.9;
  168. if( m_Mid != other.m_Mid )
  169. similarity *= 0.9;
  170. return similarity;
  171. }
  172. bool PCB_VIA::operator==( const BOARD_ITEM& aOther ) const
  173. {
  174. if( aOther.Type() != Type() )
  175. return false;
  176. const PCB_VIA& other = static_cast<const PCB_VIA&>( aOther );
  177. return m_Start == other.m_Start && m_End == other.m_End && m_layer == other.m_layer &&
  178. m_bottomLayer == other.m_bottomLayer && m_Width == other.m_Width &&
  179. m_viaType == other.m_viaType && m_drill == other.m_drill &&
  180. m_removeUnconnectedLayer == other.m_removeUnconnectedLayer &&
  181. m_keepStartEndLayer == other.m_keepStartEndLayer &&
  182. m_zoneLayerOverrides == other.m_zoneLayerOverrides;
  183. }
  184. double PCB_VIA::Similarity( const BOARD_ITEM& aOther ) const
  185. {
  186. if( aOther.Type() != Type() )
  187. return 0.0;
  188. const PCB_VIA& other = static_cast<const PCB_VIA&>( aOther );
  189. double similarity = 1.0;
  190. if( m_layer != other.m_layer )
  191. similarity *= 0.9;
  192. if( m_Width != other.m_Width )
  193. similarity *= 0.9;
  194. if( m_Start != other.m_Start )
  195. similarity *= 0.9;
  196. if( m_End != other.m_End )
  197. similarity *= 0.9;
  198. if( m_bottomLayer != other.m_bottomLayer )
  199. similarity *= 0.9;
  200. if( m_viaType != other.m_viaType )
  201. similarity *= 0.9;
  202. if( m_drill != other.m_drill )
  203. similarity *= 0.9;
  204. if( m_removeUnconnectedLayer != other.m_removeUnconnectedLayer )
  205. similarity *= 0.9;
  206. if( m_keepStartEndLayer != other.m_keepStartEndLayer )
  207. similarity *= 0.9;
  208. if( m_zoneLayerOverrides != other.m_zoneLayerOverrides )
  209. similarity *= 0.9;
  210. return similarity;
  211. }
  212. bool PCB_TRACK::ApproxCollinear( const PCB_TRACK& aTrack )
  213. {
  214. SEG a( m_Start, m_End );
  215. SEG b( aTrack.GetStart(), aTrack.GetEnd() );
  216. return a.ApproxCollinear( b );
  217. }
  218. int PCB_TRACK::GetLocalClearance( wxString* aSource ) const
  219. {
  220. // Not currently implemented
  221. return 0;
  222. }
  223. MINOPTMAX<int> PCB_TRACK::GetWidthConstraint( wxString* aSource ) const
  224. {
  225. DRC_CONSTRAINT constraint;
  226. if( GetBoard() && GetBoard()->GetDesignSettings().m_DRCEngine )
  227. {
  228. BOARD_DESIGN_SETTINGS& bds = GetBoard()->GetDesignSettings();
  229. constraint = bds.m_DRCEngine->EvalRules( TRACK_WIDTH_CONSTRAINT, this, nullptr, m_layer );
  230. }
  231. if( aSource )
  232. *aSource = constraint.GetName();
  233. return constraint.Value();
  234. }
  235. MINOPTMAX<int> PCB_VIA::GetWidthConstraint( wxString* aSource ) const
  236. {
  237. DRC_CONSTRAINT constraint;
  238. if( GetBoard() && GetBoard()->GetDesignSettings().m_DRCEngine )
  239. {
  240. BOARD_DESIGN_SETTINGS& bds = GetBoard()->GetDesignSettings();
  241. constraint = bds.m_DRCEngine->EvalRules( VIA_DIAMETER_CONSTRAINT, this, nullptr, m_layer );
  242. }
  243. if( aSource )
  244. *aSource = constraint.GetName();
  245. return constraint.Value();
  246. }
  247. MINOPTMAX<int> PCB_VIA::GetDrillConstraint( wxString* aSource ) const
  248. {
  249. DRC_CONSTRAINT constraint;
  250. if( GetBoard() && GetBoard()->GetDesignSettings().m_DRCEngine )
  251. {
  252. BOARD_DESIGN_SETTINGS& bds = GetBoard()->GetDesignSettings();
  253. constraint = bds.m_DRCEngine->EvalRules( HOLE_SIZE_CONSTRAINT, this, nullptr, m_layer );
  254. }
  255. if( aSource )
  256. *aSource = constraint.GetName();
  257. return constraint.Value();
  258. }
  259. int PCB_VIA::GetMinAnnulus( PCB_LAYER_ID aLayer, wxString* aSource ) const
  260. {
  261. if( !FlashLayer( aLayer ) )
  262. {
  263. if( aSource )
  264. *aSource = _( "removed annular ring" );
  265. return 0;
  266. }
  267. DRC_CONSTRAINT constraint;
  268. if( GetBoard() && GetBoard()->GetDesignSettings().m_DRCEngine )
  269. {
  270. BOARD_DESIGN_SETTINGS& bds = GetBoard()->GetDesignSettings();
  271. constraint = bds.m_DRCEngine->EvalRules( ANNULAR_WIDTH_CONSTRAINT, this, nullptr, aLayer );
  272. }
  273. if( constraint.Value().HasMin() )
  274. {
  275. if( aSource )
  276. *aSource = constraint.GetName();
  277. return constraint.Value().Min();
  278. }
  279. return 0;
  280. }
  281. int PCB_VIA::GetDrillValue() const
  282. {
  283. if( m_drill > 0 ) // Use the specific value.
  284. return m_drill;
  285. // Use the default value from the Netclass
  286. NETCLASS* netclass = GetEffectiveNetClass();
  287. if( GetViaType() == VIATYPE::MICROVIA )
  288. return netclass->GetuViaDrill();
  289. return netclass->GetViaDrill();
  290. }
  291. EDA_ITEM_FLAGS PCB_TRACK::IsPointOnEnds( const VECTOR2I& point, int min_dist ) const
  292. {
  293. EDA_ITEM_FLAGS result = 0;
  294. if( min_dist < 0 )
  295. min_dist = m_Width / 2;
  296. if( min_dist == 0 )
  297. {
  298. if( m_Start == point )
  299. result |= STARTPOINT;
  300. if( m_End == point )
  301. result |= ENDPOINT;
  302. }
  303. else
  304. {
  305. double dist = GetLineLength( m_Start, point );
  306. if( min_dist >= KiROUND( dist ) )
  307. result |= STARTPOINT;
  308. dist = GetLineLength( m_End, point );
  309. if( min_dist >= KiROUND( dist ) )
  310. result |= ENDPOINT;
  311. }
  312. return result;
  313. }
  314. const BOX2I PCB_TRACK::GetBoundingBox() const
  315. {
  316. // end of track is round, this is its radius, rounded up
  317. int radius = ( m_Width + 1 ) / 2;
  318. int ymax, xmax, ymin, xmin;
  319. if( Type() == PCB_VIA_T )
  320. {
  321. ymax = m_Start.y;
  322. xmax = m_Start.x;
  323. ymin = m_Start.y;
  324. xmin = m_Start.x;
  325. }
  326. else if( Type() == PCB_ARC_T )
  327. {
  328. std::shared_ptr<SHAPE> arc = GetEffectiveShape();
  329. BOX2I bbox = arc->BBox();
  330. xmin = bbox.GetLeft();
  331. xmax = bbox.GetRight();
  332. ymin = bbox.GetTop();
  333. ymax = bbox.GetBottom();
  334. }
  335. else
  336. {
  337. ymax = std::max( m_Start.y, m_End.y );
  338. xmax = std::max( m_Start.x, m_End.x );
  339. ymin = std::min( m_Start.y, m_End.y );
  340. xmin = std::min( m_Start.x, m_End.x );
  341. }
  342. ymax += radius;
  343. xmax += radius;
  344. ymin -= radius;
  345. xmin -= radius;
  346. // return a rectangle which is [pos,dim) in nature. therefore the +1
  347. BOX2I ret( VECTOR2I( xmin, ymin ), VECTOR2I( xmax - xmin + 1, ymax - ymin + 1 ) );
  348. return ret;
  349. }
  350. double PCB_TRACK::GetLength() const
  351. {
  352. return GetLineLength( m_Start, m_End );
  353. }
  354. void PCB_TRACK::Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
  355. {
  356. RotatePoint( m_Start, aRotCentre, aAngle );
  357. RotatePoint( m_End, aRotCentre, aAngle );
  358. }
  359. void PCB_ARC::Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
  360. {
  361. RotatePoint( m_Start, aRotCentre, aAngle );
  362. RotatePoint( m_End, aRotCentre, aAngle );
  363. RotatePoint( m_Mid, aRotCentre, aAngle );
  364. }
  365. void PCB_TRACK::Mirror( const VECTOR2I& aCentre, bool aMirrorAroundXAxis )
  366. {
  367. if( aMirrorAroundXAxis )
  368. {
  369. MIRROR( m_Start.y, aCentre.y );
  370. MIRROR( m_End.y, aCentre.y );
  371. }
  372. else
  373. {
  374. MIRROR( m_Start.x, aCentre.x );
  375. MIRROR( m_End.x, aCentre.x );
  376. }
  377. }
  378. void PCB_ARC::Mirror( const VECTOR2I& aCentre, bool aMirrorAroundXAxis )
  379. {
  380. if( aMirrorAroundXAxis )
  381. {
  382. MIRROR( m_Start.y, aCentre.y );
  383. MIRROR( m_End.y, aCentre.y );
  384. MIRROR( m_Mid.y, aCentre.y );
  385. }
  386. else
  387. {
  388. MIRROR( m_Start.x, aCentre.x );
  389. MIRROR( m_End.x, aCentre.x );
  390. MIRROR( m_Mid.x, aCentre.x );
  391. }
  392. }
  393. void PCB_TRACK::Flip( const VECTOR2I& aCentre, bool aFlipLeftRight )
  394. {
  395. if( aFlipLeftRight )
  396. {
  397. m_Start.x = aCentre.x - ( m_Start.x - aCentre.x );
  398. m_End.x = aCentre.x - ( m_End.x - aCentre.x );
  399. }
  400. else
  401. {
  402. m_Start.y = aCentre.y - ( m_Start.y - aCentre.y );
  403. m_End.y = aCentre.y - ( m_End.y - aCentre.y );
  404. }
  405. int copperLayerCount = GetBoard()->GetCopperLayerCount();
  406. SetLayer( FlipLayer( GetLayer(), copperLayerCount ) );
  407. }
  408. void PCB_ARC::Flip( const VECTOR2I& aCentre, bool aFlipLeftRight )
  409. {
  410. if( aFlipLeftRight )
  411. {
  412. m_Start.x = aCentre.x - ( m_Start.x - aCentre.x );
  413. m_End.x = aCentre.x - ( m_End.x - aCentre.x );
  414. m_Mid.x = aCentre.x - ( m_Mid.x - aCentre.x );
  415. }
  416. else
  417. {
  418. m_Start.y = aCentre.y - ( m_Start.y - aCentre.y );
  419. m_End.y = aCentre.y - ( m_End.y - aCentre.y );
  420. m_Mid.y = aCentre.y - ( m_Mid.y - aCentre.y );
  421. }
  422. int copperLayerCount = GetBoard()->GetCopperLayerCount();
  423. SetLayer( FlipLayer( GetLayer(), copperLayerCount ) );
  424. }
  425. bool PCB_ARC::IsCCW() const
  426. {
  427. VECTOR2I start_end = m_End - m_Start;
  428. VECTOR2I start_mid = m_Mid - m_Start;
  429. return start_end.Cross( start_mid ) < 0;
  430. }
  431. void PCB_VIA::Flip( const VECTOR2I& aCentre, bool aFlipLeftRight )
  432. {
  433. if( aFlipLeftRight )
  434. {
  435. m_Start.x = aCentre.x - ( m_Start.x - aCentre.x );
  436. m_End.x = aCentre.x - ( m_End.x - aCentre.x );
  437. }
  438. else
  439. {
  440. m_Start.y = aCentre.y - ( m_Start.y - aCentre.y );
  441. m_End.y = aCentre.y - ( m_End.y - aCentre.y );
  442. }
  443. if( GetViaType() != VIATYPE::THROUGH )
  444. {
  445. int copperLayerCount = GetBoard()->GetCopperLayerCount();
  446. PCB_LAYER_ID top_layer;
  447. PCB_LAYER_ID bottom_layer;
  448. LayerPair( &top_layer, &bottom_layer );
  449. top_layer = FlipLayer( top_layer, copperLayerCount );
  450. bottom_layer = FlipLayer( bottom_layer, copperLayerCount );
  451. SetLayerPair( top_layer, bottom_layer );
  452. }
  453. }
  454. INSPECT_RESULT PCB_TRACK::Visit( INSPECTOR inspector, void* testData,
  455. const std::vector<KICAD_T>& aScanTypes )
  456. {
  457. for( KICAD_T scanType : aScanTypes )
  458. {
  459. if( scanType == Type() )
  460. {
  461. if( INSPECT_RESULT::QUIT == inspector( this, testData ) )
  462. return INSPECT_RESULT::QUIT;
  463. }
  464. }
  465. return INSPECT_RESULT::CONTINUE;
  466. }
  467. std::shared_ptr<SHAPE_SEGMENT> PCB_VIA::GetEffectiveHoleShape() const
  468. {
  469. return std::make_shared<SHAPE_SEGMENT>( SEG( m_Start, m_Start ), m_drill );
  470. }
  471. bool PCB_VIA::IsTented() const
  472. {
  473. if( const BOARD* board = GetBoard() )
  474. return board->GetTentVias();
  475. else
  476. return true;
  477. }
  478. int PCB_VIA::GetSolderMaskExpansion() const
  479. {
  480. if( const BOARD* board = GetBoard() )
  481. return board->GetDesignSettings().m_SolderMaskExpansion;
  482. else
  483. return 0;
  484. }
  485. bool PCB_VIA::IsOnLayer( PCB_LAYER_ID aLayer ) const
  486. {
  487. #if 0
  488. // Nice and simple, but raises its ugly head in performance profiles....
  489. return GetLayerSet().test( aLayer );
  490. #endif
  491. if( aLayer >= m_layer && aLayer <= m_bottomLayer )
  492. return true;
  493. if( !IsTented() )
  494. {
  495. if( aLayer == F_Mask )
  496. return IsOnLayer( F_Cu );
  497. else if( aLayer == B_Mask )
  498. return IsOnLayer( B_Cu );
  499. }
  500. return false;
  501. }
  502. LSET PCB_VIA::GetLayerSet() const
  503. {
  504. LSET layermask;
  505. if( GetViaType() == VIATYPE::THROUGH )
  506. layermask = LSET::AllCuMask();
  507. else
  508. wxASSERT( m_layer <= m_bottomLayer );
  509. // PCB_LAYER_IDs are numbered from front to back, this is top to bottom.
  510. for( int id = m_layer; id <= m_bottomLayer; ++id )
  511. layermask.set( id );
  512. if( !IsTented() )
  513. {
  514. if( layermask.test( F_Cu ) )
  515. layermask.set( F_Mask );
  516. if( layermask.test( B_Cu ) )
  517. layermask.set( B_Mask );
  518. }
  519. return layermask;
  520. }
  521. void PCB_VIA::SetLayerSet( LSET aLayerSet )
  522. {
  523. bool first = true;
  524. for( PCB_LAYER_ID layer : aLayerSet.Seq() )
  525. {
  526. // m_layer and m_bottomLayer are copper layers, so consider only copper layers in aLayerSet
  527. if( !IsCopperLayer( layer ) )
  528. continue;
  529. if( first )
  530. {
  531. m_layer = layer;
  532. first = false;
  533. }
  534. m_bottomLayer = layer;
  535. }
  536. }
  537. void PCB_VIA::SetLayerPair( PCB_LAYER_ID aTopLayer, PCB_LAYER_ID aBottomLayer )
  538. {
  539. m_layer = aTopLayer;
  540. m_bottomLayer = aBottomLayer;
  541. SanitizeLayers();
  542. }
  543. void PCB_VIA::SetTopLayer( PCB_LAYER_ID aLayer )
  544. {
  545. m_layer = aLayer;
  546. }
  547. void PCB_VIA::SetBottomLayer( PCB_LAYER_ID aLayer )
  548. {
  549. m_bottomLayer = aLayer;
  550. }
  551. void PCB_VIA::LayerPair( PCB_LAYER_ID* top_layer, PCB_LAYER_ID* bottom_layer ) const
  552. {
  553. PCB_LAYER_ID t_layer = F_Cu;
  554. PCB_LAYER_ID b_layer = B_Cu;
  555. if( GetViaType() != VIATYPE::THROUGH )
  556. {
  557. b_layer = m_bottomLayer;
  558. t_layer = m_layer;
  559. if( b_layer < t_layer )
  560. std::swap( b_layer, t_layer );
  561. }
  562. if( top_layer )
  563. *top_layer = t_layer;
  564. if( bottom_layer )
  565. *bottom_layer = b_layer;
  566. }
  567. PCB_LAYER_ID PCB_VIA::TopLayer() const
  568. {
  569. return m_layer;
  570. }
  571. PCB_LAYER_ID PCB_VIA::BottomLayer() const
  572. {
  573. return m_bottomLayer;
  574. }
  575. void PCB_VIA::SanitizeLayers()
  576. {
  577. if( GetViaType() == VIATYPE::THROUGH )
  578. {
  579. m_layer = F_Cu;
  580. m_bottomLayer = B_Cu;
  581. }
  582. if( m_bottomLayer < m_layer )
  583. std::swap( m_bottomLayer, m_layer );
  584. }
  585. bool PCB_VIA::FlashLayer( LSET aLayers ) const
  586. {
  587. for( PCB_LAYER_ID layer : aLayers.Seq() )
  588. {
  589. if( FlashLayer( layer ) )
  590. return true;
  591. }
  592. return false;
  593. }
  594. bool PCB_VIA::FlashLayer( int aLayer ) const
  595. {
  596. // Return the "normal" shape if the caller doesn't specify a particular layer
  597. if( aLayer == UNDEFINED_LAYER )
  598. return true;
  599. const BOARD* board = GetBoard();
  600. if( !board )
  601. return true;
  602. if( !IsOnLayer( static_cast<PCB_LAYER_ID>( aLayer ) ) )
  603. return false;
  604. if( !m_removeUnconnectedLayer || !IsCopperLayer( aLayer ) )
  605. return true;
  606. if( m_keepStartEndLayer && ( aLayer == m_layer || aLayer == m_bottomLayer ) )
  607. return true;
  608. // Must be static to keep from raising its ugly head in performance profiles
  609. static std::initializer_list<KICAD_T> connectedTypes = { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T,
  610. PCB_PAD_T };
  611. if( m_zoneLayerOverrides[ aLayer ] == ZLO_FORCE_FLASHED )
  612. return true;
  613. else
  614. return board->GetConnectivity()->IsConnectedOnLayer( this, aLayer, connectedTypes );
  615. }
  616. void PCB_VIA::GetOutermostConnectedLayers( PCB_LAYER_ID* aTopmost,
  617. PCB_LAYER_ID* aBottommost ) const
  618. {
  619. *aTopmost = UNDEFINED_LAYER;
  620. *aBottommost = UNDEFINED_LAYER;
  621. static std::initializer_list<KICAD_T> connectedTypes = { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T,
  622. PCB_PAD_T };
  623. for( int layer = TopLayer(); layer <= BottomLayer(); ++layer )
  624. {
  625. bool connected = false;
  626. if( m_zoneLayerOverrides[ layer ] == ZLO_FORCE_FLASHED )
  627. connected = true;
  628. else if( GetBoard()->GetConnectivity()->IsConnectedOnLayer( this, layer, connectedTypes ) )
  629. connected = true;
  630. if( connected )
  631. {
  632. if( *aTopmost == UNDEFINED_LAYER )
  633. *aTopmost = ToLAYER_ID( layer );
  634. *aBottommost = ToLAYER_ID( layer );
  635. }
  636. }
  637. }
  638. void PCB_TRACK::ViewGetLayers( int aLayers[], int& aCount ) const
  639. {
  640. // Show the track and its netname on different layers
  641. aLayers[0] = GetLayer();
  642. aLayers[1] = GetNetnameLayer( aLayers[0] );
  643. aCount = 2;
  644. if( IsLocked() )
  645. aLayers[ aCount++ ] = LAYER_LOCKED_ITEM_SHADOW;
  646. }
  647. double PCB_TRACK::ViewGetLOD( int aLayer, KIGFX::VIEW* aView ) const
  648. {
  649. constexpr double HIDE = std::numeric_limits<double>::max();
  650. PCB_PAINTER* painter = static_cast<PCB_PAINTER*>( aView->GetPainter() );
  651. PCB_RENDER_SETTINGS* renderSettings = painter->GetSettings();
  652. if( !aView->IsLayerVisible( LAYER_TRACKS ) )
  653. return HIDE;
  654. if( IsNetnameLayer( aLayer ) )
  655. {
  656. if( GetNetCode() <= NETINFO_LIST::UNCONNECTED )
  657. return HIDE;
  658. // Hide netnames on dimmed tracks
  659. if( renderSettings->GetHighContrast() )
  660. {
  661. if( m_layer != renderSettings->GetPrimaryHighContrastLayer() )
  662. return HIDE;
  663. }
  664. // Pick the approximate size of the netname (square chars)
  665. wxString netName = GetUnescapedShortNetname();
  666. size_t num_chars = netName.size();
  667. if( GetLength() < num_chars * GetWidth() )
  668. return HIDE;
  669. // When drawing netnames, clip the track to the viewport
  670. VECTOR2I start( GetStart() );
  671. VECTOR2I end( GetEnd() );
  672. BOX2D viewport = aView->GetViewport();
  673. BOX2I clipBox( viewport.GetOrigin(), viewport.GetSize() );
  674. ClipLine( &clipBox, start.x, start.y, end.x, end.y );
  675. VECTOR2I line = ( end - start );
  676. if( line.EuclideanNorm() == 0 )
  677. return HIDE;
  678. // Netnames will be shown only if zoom is appropriate
  679. return ( double ) pcbIUScale.mmToIU( 4 ) / ( m_Width + 1 );
  680. }
  681. if( aLayer == LAYER_LOCKED_ITEM_SHADOW )
  682. {
  683. // Hide shadow if the main layer is not shown
  684. if( !aView->IsLayerVisible( m_layer ) )
  685. return HIDE;
  686. // Hide shadow on dimmed tracks
  687. if( renderSettings->GetHighContrast() )
  688. {
  689. if( m_layer != renderSettings->GetPrimaryHighContrastLayer() )
  690. return HIDE;
  691. }
  692. }
  693. // Other layers are shown without any conditions
  694. return 0.0;
  695. }
  696. const BOX2I PCB_TRACK::ViewBBox() const
  697. {
  698. BOX2I bbox = GetBoundingBox();
  699. if( const BOARD* board = GetBoard() )
  700. bbox.Inflate( 2 * board->GetDesignSettings().GetBiggestClearanceValue() );
  701. else
  702. bbox.Inflate( GetWidth() ); // Add a bit extra for safety
  703. return bbox;
  704. }
  705. void PCB_VIA::ViewGetLayers( int aLayers[], int& aCount ) const
  706. {
  707. aLayers[0] = LAYER_VIA_HOLES;
  708. aLayers[1] = LAYER_VIA_HOLEWALLS;
  709. aLayers[2] = LAYER_VIA_NETNAMES;
  710. // Just show it on common via & via holes layers
  711. switch( GetViaType() )
  712. {
  713. case VIATYPE::THROUGH: aLayers[3] = LAYER_VIA_THROUGH; break;
  714. case VIATYPE::BLIND_BURIED: aLayers[3] = LAYER_VIA_BBLIND; break;
  715. case VIATYPE::MICROVIA: aLayers[3] = LAYER_VIA_MICROVIA; break;
  716. default: aLayers[3] = LAYER_GP_OVERLAY; break;
  717. }
  718. aCount = 4;
  719. if( IsLocked() )
  720. aLayers[ aCount++ ] = LAYER_LOCKED_ITEM_SHADOW;
  721. // Vias can also be on a solder mask layer. They are on these layers or not,
  722. // depending on the plot and solder mask options
  723. if( IsOnLayer( F_Mask ) )
  724. aLayers[ aCount++ ] = F_Mask;
  725. if( IsOnLayer( B_Mask ) )
  726. aLayers[ aCount++ ] = B_Mask;
  727. }
  728. double PCB_VIA::ViewGetLOD( int aLayer, KIGFX::VIEW* aView ) const
  729. {
  730. constexpr double HIDE = (double)std::numeric_limits<double>::max();
  731. PCB_PAINTER* painter = static_cast<PCB_PAINTER*>( aView->GetPainter() );
  732. PCB_RENDER_SETTINGS* renderSettings = painter->GetSettings();
  733. LSET visible = LSET::AllLayersMask();
  734. // Meta control for hiding all vias
  735. if( !aView->IsLayerVisible( LAYER_VIAS ) )
  736. return HIDE;
  737. // Handle board visibility
  738. if( const BOARD* board = GetBoard() )
  739. visible = board->GetVisibleLayers() & board->GetEnabledLayers();
  740. // In high contrast mode don't show vias that don't cross the high-contrast layer
  741. if( renderSettings->GetHighContrast() )
  742. {
  743. PCB_LAYER_ID highContrastLayer = renderSettings->GetPrimaryHighContrastLayer();
  744. if( LSET::FrontTechMask().Contains( highContrastLayer ) )
  745. highContrastLayer = F_Cu;
  746. else if( LSET::BackTechMask().Contains( highContrastLayer ) )
  747. highContrastLayer = B_Cu;
  748. if( !GetLayerSet().Contains( highContrastLayer ) )
  749. return HIDE;
  750. }
  751. if( IsHoleLayer( aLayer ) )
  752. {
  753. if( m_viaType == VIATYPE::BLIND_BURIED || m_viaType == VIATYPE::MICROVIA )
  754. {
  755. // Show a blind or micro via's hole if it crosses a visible layer
  756. if( !( visible & GetLayerSet() ).any() )
  757. return HIDE;
  758. }
  759. else
  760. {
  761. // Show a through via's hole if any physical layer is shown
  762. if( !( visible & LSET::PhysicalLayersMask() ).any() )
  763. return HIDE;
  764. }
  765. }
  766. else if( IsNetnameLayer( aLayer ) )
  767. {
  768. if( renderSettings->GetHighContrast() )
  769. {
  770. // Hide netnames unless via is flashed to a high-contrast layer
  771. if( !FlashLayer( renderSettings->GetPrimaryHighContrastLayer() ) )
  772. return HIDE;
  773. }
  774. else
  775. {
  776. // Hide netnames unless pad is flashed to a visible layer
  777. if( !FlashLayer( visible ) )
  778. return HIDE;
  779. }
  780. // Netnames will be shown only if zoom is appropriate
  781. return m_Width == 0 ? HIDE : ( (double)pcbIUScale.mmToIU( 10 ) / m_Width );
  782. }
  783. // Passed all tests; show.
  784. return 0.0;
  785. }
  786. wxString PCB_TRACK::GetFriendlyName() const
  787. {
  788. switch( Type() )
  789. {
  790. case PCB_ARC_T: return _( "Track (arc)" );
  791. case PCB_VIA_T: return _( "Via" );
  792. case PCB_TRACE_T:
  793. default: return _( "Track" );
  794. }
  795. }
  796. void PCB_TRACK::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
  797. {
  798. wxString msg;
  799. BOARD* board = GetBoard();
  800. aList.emplace_back( _( "Type" ), GetFriendlyName() );
  801. GetMsgPanelInfoBase_Common( aFrame, aList );
  802. aList.emplace_back( _( "Layer" ), layerMaskDescribe() );
  803. aList.emplace_back( _( "Width" ), aFrame->MessageTextFromValue( m_Width ) );
  804. if( Type() == PCB_ARC_T )
  805. {
  806. double radius = static_cast<PCB_ARC*>( this )->GetRadius();
  807. aList.emplace_back( _( "Radius" ), aFrame->MessageTextFromValue( radius ) );
  808. }
  809. aList.emplace_back( _( "Segment Length" ), aFrame->MessageTextFromValue( GetLength() ) );
  810. // Display full track length (in Pcbnew)
  811. if( board && GetNetCode() > 0 )
  812. {
  813. int count;
  814. double trackLen;
  815. double lenPadToDie;
  816. std::tie( count, trackLen, lenPadToDie ) = board->GetTrackLength( *this );
  817. aList.emplace_back( _( "Routed Length" ), aFrame->MessageTextFromValue( trackLen ) );
  818. if( lenPadToDie != 0 )
  819. {
  820. msg = aFrame->MessageTextFromValue( lenPadToDie );
  821. aList.emplace_back( _( "Pad To Die Length" ), msg );
  822. msg = aFrame->MessageTextFromValue( trackLen + lenPadToDie );
  823. aList.emplace_back( _( "Full Length" ), msg );
  824. }
  825. }
  826. wxString source;
  827. int clearance = GetOwnClearance( GetLayer(), &source );
  828. aList.emplace_back( wxString::Format( _( "Min Clearance: %s" ),
  829. aFrame->MessageTextFromValue( clearance ) ),
  830. wxString::Format( _( "(from %s)" ), source ) );
  831. MINOPTMAX<int> constraintValue = GetWidthConstraint( &source );
  832. msg = aFrame->MessageTextFromMinOptMax( constraintValue );
  833. if( !msg.IsEmpty() )
  834. {
  835. aList.emplace_back( wxString::Format( _( "Width Constraints: %s" ), msg ),
  836. wxString::Format( _( "(from %s)" ), source ) );
  837. }
  838. }
  839. void PCB_VIA::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
  840. {
  841. wxString msg;
  842. switch( GetViaType() )
  843. {
  844. case VIATYPE::MICROVIA: msg = _( "Micro Via" ); break;
  845. case VIATYPE::BLIND_BURIED: msg = _( "Blind/Buried Via" ); break;
  846. case VIATYPE::THROUGH: msg = _( "Through Via" ); break;
  847. default: msg = _( "Via" ); break;
  848. }
  849. aList.emplace_back( _( "Type" ), msg );
  850. GetMsgPanelInfoBase_Common( aFrame, aList );
  851. aList.emplace_back( _( "Layer" ), layerMaskDescribe() );
  852. aList.emplace_back( _( "Diameter" ), aFrame->MessageTextFromValue( m_Width ) );
  853. aList.emplace_back( _( "Hole" ), aFrame->MessageTextFromValue( GetDrillValue() ) );
  854. wxString source;
  855. int clearance = GetOwnClearance( GetLayer(), &source );
  856. aList.emplace_back( wxString::Format( _( "Min Clearance: %s" ),
  857. aFrame->MessageTextFromValue( clearance ) ),
  858. wxString::Format( _( "(from %s)" ), source ) );
  859. int minAnnulus = GetMinAnnulus( GetLayer(), &source );
  860. aList.emplace_back( wxString::Format( _( "Min Annular Width: %s" ),
  861. aFrame->MessageTextFromValue( minAnnulus ) ),
  862. wxString::Format( _( "(from %s)" ), source ) );
  863. }
  864. void PCB_TRACK::GetMsgPanelInfoBase_Common( EDA_DRAW_FRAME* aFrame,
  865. std::vector<MSG_PANEL_ITEM>& aList ) const
  866. {
  867. aList.emplace_back( _( "Net" ), UnescapeString( GetNetname() ) );
  868. aList.emplace_back( _( "Resolved Netclass" ),
  869. UnescapeString( GetEffectiveNetClass()->GetName() ) );
  870. #if 0 // Enable for debugging
  871. if( GetBoard() )
  872. aList.emplace_back( _( "NetCode" ), wxString::Format( wxT( "%d" ), GetNetCode() ) );
  873. aList.emplace_back( wxT( "Flags" ), wxString::Format( wxT( "0x%08X" ), m_flags ) );
  874. aList.emplace_back( wxT( "Start pos" ), wxString::Format( wxT( "%d %d" ),
  875. m_Start.x,
  876. m_Start.y ) );
  877. aList.emplace_back( wxT( "End pos" ), wxString::Format( wxT( "%d %d" ),
  878. m_End.x,
  879. m_End.y ) );
  880. #endif
  881. if( aFrame->GetName() == PCB_EDIT_FRAME_NAME && IsLocked() )
  882. aList.emplace_back( _( "Status" ), _( "Locked" ) );
  883. }
  884. wxString PCB_VIA::layerMaskDescribe() const
  885. {
  886. const BOARD* board = GetBoard();
  887. PCB_LAYER_ID top_layer;
  888. PCB_LAYER_ID bottom_layer;
  889. LayerPair( &top_layer, &bottom_layer );
  890. return board->GetLayerName( top_layer ) + wxT( " - " ) + board->GetLayerName( bottom_layer );
  891. }
  892. bool PCB_TRACK::HitTest( const VECTOR2I& aPosition, int aAccuracy ) const
  893. {
  894. return TestSegmentHit( aPosition, m_Start, m_End, aAccuracy + ( m_Width / 2 ) );
  895. }
  896. bool PCB_ARC::HitTest( const VECTOR2I& aPosition, int aAccuracy ) const
  897. {
  898. int max_dist = aAccuracy + ( m_Width / 2 );
  899. // Short-circuit common cases where the arc is connected to a track or via at an endpoint
  900. if( EuclideanNorm( GetStart() - aPosition ) <= max_dist ||
  901. EuclideanNorm( GetEnd() - aPosition ) <= max_dist )
  902. {
  903. return true;
  904. }
  905. VECTOR2I center = GetPosition();
  906. VECTOR2I relpos = aPosition - center;
  907. double dist = EuclideanNorm( relpos );
  908. double radius = GetRadius();
  909. if( std::abs( dist - radius ) > max_dist )
  910. return false;
  911. EDA_ANGLE arc_angle = GetAngle();
  912. EDA_ANGLE arc_angle_start = GetArcAngleStart(); // Always 0.0 ... 360 deg
  913. EDA_ANGLE arc_hittest( relpos );
  914. // Calculate relative angle between the starting point of the arc, and the test point
  915. arc_hittest -= arc_angle_start;
  916. // Normalise arc_hittest between 0 ... 360 deg
  917. arc_hittest.Normalize();
  918. if( arc_angle < ANGLE_0 )
  919. return arc_hittest >= ANGLE_360 + arc_angle;
  920. return arc_hittest <= arc_angle;
  921. }
  922. bool PCB_VIA::HitTest( const VECTOR2I& aPosition, int aAccuracy ) const
  923. {
  924. int max_dist = aAccuracy + ( m_Width / 2 );
  925. // rel_pos is aPosition relative to m_Start (or the center of the via)
  926. VECTOR2I rel_pos = aPosition - m_Start;
  927. double dist = (double) rel_pos.x * rel_pos.x + (double) rel_pos.y * rel_pos.y;
  928. return dist <= (double) max_dist * max_dist;
  929. }
  930. bool PCB_TRACK::HitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const
  931. {
  932. BOX2I arect = aRect;
  933. arect.Inflate( aAccuracy );
  934. if( aContained )
  935. return arect.Contains( GetStart() ) && arect.Contains( GetEnd() );
  936. else
  937. return arect.Intersects( GetStart(), GetEnd() );
  938. }
  939. bool PCB_ARC::HitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const
  940. {
  941. BOX2I arect = aRect;
  942. arect.Inflate( aAccuracy );
  943. BOX2I box( GetStart() );
  944. box.Merge( GetMid() );
  945. box.Merge( GetEnd() );
  946. box.Inflate( GetWidth() / 2 );
  947. if( aContained )
  948. return arect.Contains( box );
  949. else
  950. return arect.Intersects( box );
  951. }
  952. bool PCB_VIA::HitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const
  953. {
  954. BOX2I arect = aRect;
  955. arect.Inflate( aAccuracy );
  956. BOX2I box( GetStart() );
  957. box.Inflate( GetWidth() / 2 );
  958. if( aContained )
  959. return arect.Contains( box );
  960. else
  961. return arect.IntersectsCircle( GetStart(), GetWidth() / 2 );
  962. }
  963. wxString PCB_TRACK::GetItemDescription( UNITS_PROVIDER* aUnitsProvider ) const
  964. {
  965. return wxString::Format( Type() == PCB_ARC_T ? _("Track (arc) %s on %s, length %s" )
  966. : _("Track %s on %s, length %s" ),
  967. GetNetnameMsg(),
  968. GetLayerName(),
  969. aUnitsProvider->MessageTextFromValue( GetLength() ) );
  970. }
  971. BITMAPS PCB_TRACK::GetMenuImage() const
  972. {
  973. return BITMAPS::add_tracks;
  974. }
  975. void PCB_TRACK::swapData( BOARD_ITEM* aImage )
  976. {
  977. assert( aImage->Type() == PCB_TRACE_T );
  978. std::swap( *((PCB_TRACK*) this), *((PCB_TRACK*) aImage) );
  979. }
  980. void PCB_ARC::swapData( BOARD_ITEM* aImage )
  981. {
  982. assert( aImage->Type() == PCB_ARC_T );
  983. std::swap( *this, *static_cast<PCB_ARC*>( aImage ) );
  984. }
  985. void PCB_VIA::swapData( BOARD_ITEM* aImage )
  986. {
  987. assert( aImage->Type() == PCB_VIA_T );
  988. std::swap( *((PCB_VIA*) this), *((PCB_VIA*) aImage) );
  989. }
  990. VECTOR2I PCB_ARC::GetPosition() const
  991. {
  992. VECTOR2I center = CalcArcCenter( m_Start, m_Mid, m_End );
  993. return center;
  994. }
  995. double PCB_ARC::GetRadius() const
  996. {
  997. auto center = CalcArcCenter( m_Start, m_Mid , m_End );
  998. return GetLineLength( center, m_Start );
  999. }
  1000. EDA_ANGLE PCB_ARC::GetAngle() const
  1001. {
  1002. VECTOR2I center = GetPosition();
  1003. EDA_ANGLE angle1 = EDA_ANGLE( m_Mid - center ) - EDA_ANGLE( m_Start - center );
  1004. EDA_ANGLE angle2 = EDA_ANGLE( m_End - center ) - EDA_ANGLE( m_Mid - center );
  1005. return angle1.Normalize180() + angle2.Normalize180();
  1006. }
  1007. EDA_ANGLE PCB_ARC::GetArcAngleStart() const
  1008. {
  1009. EDA_ANGLE angleStart( m_Start - GetPosition() );
  1010. return angleStart.Normalize();
  1011. }
  1012. // Note: used in python tests. Ignore CLion's claim that it's unused....
  1013. EDA_ANGLE PCB_ARC::GetArcAngleEnd() const
  1014. {
  1015. EDA_ANGLE angleEnd( m_End - GetPosition() );
  1016. return angleEnd.Normalize();
  1017. }
  1018. bool PCB_ARC::IsDegenerated( int aThreshold ) const
  1019. {
  1020. // Too small arcs cannot be really handled: arc center (and arc radius)
  1021. // cannot be safely computed if the distance between mid and end points
  1022. // is too small (a few internal units)
  1023. // len of both segments must be < aThreshold to be a very small degenerated arc
  1024. return ( GetMid() - GetStart() ).EuclideanNorm() < aThreshold
  1025. && ( GetMid() - GetEnd() ).EuclideanNorm() < aThreshold;
  1026. }
  1027. bool PCB_TRACK::cmp_tracks::operator() ( const PCB_TRACK* a, const PCB_TRACK* b ) const
  1028. {
  1029. if( a->GetNetCode() != b->GetNetCode() )
  1030. return a->GetNetCode() < b->GetNetCode();
  1031. if( a->GetLayer() != b->GetLayer() )
  1032. return a->GetLayer() < b->GetLayer();
  1033. if( a->Type() != b->Type() )
  1034. return a->Type() < b->Type();
  1035. if( a->m_Uuid != b->m_Uuid )
  1036. return a->m_Uuid < b->m_Uuid;
  1037. return a < b;
  1038. }
  1039. std::shared_ptr<SHAPE> PCB_TRACK::GetEffectiveShape( PCB_LAYER_ID aLayer, FLASHING aFlash ) const
  1040. {
  1041. return std::make_shared<SHAPE_SEGMENT>( m_Start, m_End, m_Width );
  1042. }
  1043. std::shared_ptr<SHAPE> PCB_VIA::GetEffectiveShape( PCB_LAYER_ID aLayer, FLASHING aFlash ) const
  1044. {
  1045. if( aFlash == FLASHING::ALWAYS_FLASHED
  1046. || ( aFlash == FLASHING::DEFAULT && FlashLayer( aLayer ) ) )
  1047. {
  1048. return std::make_shared<SHAPE_CIRCLE>( m_Start, m_Width / 2 );
  1049. }
  1050. else
  1051. {
  1052. return std::make_shared<SHAPE_CIRCLE>( m_Start, GetDrillValue() / 2 );
  1053. }
  1054. }
  1055. std::shared_ptr<SHAPE> PCB_ARC::GetEffectiveShape( PCB_LAYER_ID aLayer, FLASHING aFlash ) const
  1056. {
  1057. return std::make_shared<SHAPE_ARC>( GetStart(), GetMid(), GetEnd(), GetWidth() );
  1058. }
  1059. void PCB_TRACK::TransformShapeToPolygon( SHAPE_POLY_SET& aBuffer, PCB_LAYER_ID aLayer,
  1060. int aClearance, int aError, ERROR_LOC aErrorLoc,
  1061. bool ignoreLineWidth ) const
  1062. {
  1063. wxASSERT_MSG( !ignoreLineWidth, wxT( "IgnoreLineWidth has no meaning for tracks." ) );
  1064. switch( Type() )
  1065. {
  1066. case PCB_VIA_T:
  1067. {
  1068. int radius = ( m_Width / 2 ) + aClearance;
  1069. TransformCircleToPolygon( aBuffer, m_Start, radius, aError, aErrorLoc );
  1070. break;
  1071. }
  1072. case PCB_ARC_T:
  1073. {
  1074. const PCB_ARC* arc = static_cast<const PCB_ARC*>( this );
  1075. int width = m_Width + ( 2 * aClearance );
  1076. TransformArcToPolygon( aBuffer, arc->GetStart(), arc->GetMid(), arc->GetEnd(), width,
  1077. aError, aErrorLoc );
  1078. break;
  1079. }
  1080. default:
  1081. {
  1082. int width = m_Width + ( 2 * aClearance );
  1083. TransformOvalToPolygon( aBuffer, m_Start, m_End, width, aError, aErrorLoc );
  1084. break;
  1085. }
  1086. }
  1087. }
  1088. static struct TRACK_VIA_DESC
  1089. {
  1090. TRACK_VIA_DESC()
  1091. {
  1092. ENUM_MAP<VIATYPE>::Instance()
  1093. .Undefined( VIATYPE::NOT_DEFINED )
  1094. .Map( VIATYPE::THROUGH, _HKI( "Through" ) )
  1095. .Map( VIATYPE::BLIND_BURIED, _HKI( "Blind/buried" ) )
  1096. .Map( VIATYPE::MICROVIA, _HKI( "Micro" ) );
  1097. ENUM_MAP<PCB_LAYER_ID>& layerEnum = ENUM_MAP<PCB_LAYER_ID>::Instance();
  1098. if( layerEnum.Choices().GetCount() == 0 )
  1099. {
  1100. layerEnum.Undefined( UNDEFINED_LAYER );
  1101. for( LSEQ seq = LSET::AllLayersMask().Seq(); seq; ++seq )
  1102. layerEnum.Map( *seq, LSET::Name( *seq ) );
  1103. }
  1104. PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
  1105. // Track
  1106. REGISTER_TYPE( PCB_TRACK );
  1107. propMgr.InheritsAfter( TYPE_HASH( PCB_TRACK ), TYPE_HASH( BOARD_CONNECTED_ITEM ) );
  1108. propMgr.AddProperty( new PROPERTY<PCB_TRACK, int>( _HKI( "Width" ),
  1109. &PCB_TRACK::SetWidth, &PCB_TRACK::GetWidth, PROPERTY_DISPLAY::PT_SIZE ) );
  1110. propMgr.ReplaceProperty( TYPE_HASH( BOARD_ITEM ), _HKI( "Position X" ),
  1111. new PROPERTY<PCB_TRACK, int, BOARD_ITEM>( _HKI( "Start X" ),
  1112. &PCB_TRACK::SetX, &PCB_TRACK::GetX, PROPERTY_DISPLAY::PT_COORD,
  1113. ORIGIN_TRANSFORMS::ABS_X_COORD) );
  1114. propMgr.ReplaceProperty( TYPE_HASH( BOARD_ITEM ), _HKI( "Position Y" ),
  1115. new PROPERTY<PCB_TRACK, int, BOARD_ITEM>( _HKI( "Start Y" ),
  1116. &PCB_TRACK::SetY, &PCB_TRACK::GetY, PROPERTY_DISPLAY::PT_COORD,
  1117. ORIGIN_TRANSFORMS::ABS_Y_COORD ) );
  1118. propMgr.AddProperty( new PROPERTY<PCB_TRACK, int>( _HKI( "End X" ),
  1119. &PCB_TRACK::SetEndX, &PCB_TRACK::GetEndX, PROPERTY_DISPLAY::PT_COORD,
  1120. ORIGIN_TRANSFORMS::ABS_X_COORD) );
  1121. propMgr.AddProperty( new PROPERTY<PCB_TRACK, int>( _HKI( "End Y" ),
  1122. &PCB_TRACK::SetEndY, &PCB_TRACK::GetEndY, PROPERTY_DISPLAY::PT_COORD,
  1123. ORIGIN_TRANSFORMS::ABS_Y_COORD) );
  1124. // Arc
  1125. REGISTER_TYPE( PCB_ARC );
  1126. propMgr.InheritsAfter( TYPE_HASH( PCB_ARC ), TYPE_HASH( PCB_TRACK ) );
  1127. // Via
  1128. REGISTER_TYPE( PCB_VIA );
  1129. propMgr.InheritsAfter( TYPE_HASH( PCB_VIA ), TYPE_HASH( BOARD_CONNECTED_ITEM ) );
  1130. // TODO test drill, use getdrillvalue?
  1131. const wxString groupVia = _HKI( "Via Properties" );
  1132. propMgr.Mask( TYPE_HASH( PCB_VIA ), TYPE_HASH( BOARD_CONNECTED_ITEM ), _HKI( "Layer" ) );
  1133. propMgr.ReplaceProperty( TYPE_HASH( PCB_TRACK ), _HKI( "Width" ),
  1134. new PROPERTY<PCB_VIA, int, PCB_TRACK>( _HKI( "Diameter" ),
  1135. &PCB_VIA::SetWidth, &PCB_VIA::GetWidth, PROPERTY_DISPLAY::PT_SIZE ) );
  1136. propMgr.AddProperty( new PROPERTY<PCB_VIA, int>( _HKI( "Hole" ),
  1137. &PCB_VIA::SetDrill, &PCB_VIA::GetDrillValue, PROPERTY_DISPLAY::PT_SIZE ), groupVia );
  1138. propMgr.ReplaceProperty( TYPE_HASH( BOARD_ITEM ), _HKI( "Layer" ),
  1139. new PROPERTY_ENUM<PCB_VIA, PCB_LAYER_ID, BOARD_ITEM>( _HKI( "Layer Top" ),
  1140. &PCB_VIA::SetLayer, &PCB_VIA::GetLayer ), groupVia );
  1141. propMgr.AddProperty( new PROPERTY_ENUM<PCB_VIA, PCB_LAYER_ID>( _HKI( "Layer Bottom" ),
  1142. &PCB_VIA::SetBottomLayer, &PCB_VIA::BottomLayer ), groupVia );
  1143. propMgr.AddProperty( new PROPERTY_ENUM<PCB_VIA, VIATYPE>( _HKI( "Via Type" ),
  1144. &PCB_VIA::SetViaType, &PCB_VIA::GetViaType ), groupVia );
  1145. }
  1146. } _TRACK_VIA_DESC;
  1147. ENUM_TO_WXANY( VIATYPE );