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.

782 lines
27 KiB

Clean up arc/circle polygonization. 1) For a while now we've been using a calculated seg count from a given maxError, and a correction factor to push the radius out so that all the error is outside the arc/circle. However, the second calculation (which pre-dates the first) is pretty much just the inverse of the first (and yields nothing more than maxError back). This is particularly sub-optimal given the cost of trig functions. 2) There are a lot of old optimizations to reduce segcounts in certain situations, someting that our error-based calculation compensates for anyway. (Smaller radii need fewer segments to meet the maxError condition.) But perhaps more importantly we now surface maxError in the UI and we don't really want to call it "Max deviation except when it's not". 3) We were also clamping the segCount twice: once in the calculation routine and once in most of it's callers. Furthermore, the caller clamping was inconsistent (both in being done and in the clamping value). We now clamp only in the calculation routine. 4) There's no reason to use the correction factors in the 3Dviewer; it's just a visualization and whether the polygonization error is inside or outside the shape isn't really material. 5) The arc-correction-disabling stuff (used for solder mask layer) was somewhat fragile in that it depended on the caller to turn it back on afterwards. It's now only exposed as a RAII object which automatically cleans up when it goes out of scope. 6) There were also bugs in a couple of the polygonization routines where we'd accumulate round-off error in adding up the segments and end up with an overly long last segment (which of course would voilate the error max). This was the cause of the linked bug and also some issues with vias that we had fudged in the past with extra clearance. Fixes https://gitlab.com/kicad/code/kicad/issues/5567
5 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2015-2016 Mario Luzeiro <mrluzeiro@ua.pt>
  5. * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, you may find one here:
  19. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  20. * or you may search the http://www.gnu.org website for the version 2 license,
  21. * or you may write to the Free Software Foundation, Inc.,
  22. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  23. */
  24. #include "../3d_rendering/camera.h"
  25. #include "board_adapter.h"
  26. #include <board_design_settings.h>
  27. #include <board_stackup_manager/board_stackup.h>
  28. #include <board_stackup_manager/stackup_predefined_prms.h>
  29. #include <3d_rendering/raytracing/shapes2D/polygon_2d.h>
  30. #include <board.h>
  31. #include <dialogs/dialog_color_picker.h>
  32. #include <3d_math.h>
  33. #include "3d_fastmath.h"
  34. #include <geometry/geometry_utils.h>
  35. #include <convert_to_biu.h>
  36. #include <pgm_base.h>
  37. #include <settings/settings_manager.h>
  38. #include <wx/log.h>
  39. CUSTOM_COLORS_LIST BOARD_ADAPTER::g_SilkscreenColors;
  40. CUSTOM_COLORS_LIST BOARD_ADAPTER::g_MaskColors;
  41. CUSTOM_COLORS_LIST BOARD_ADAPTER::g_PasteColors;
  42. CUSTOM_COLORS_LIST BOARD_ADAPTER::g_FinishColors;
  43. CUSTOM_COLORS_LIST BOARD_ADAPTER::g_BoardColors;
  44. KIGFX::COLOR4D BOARD_ADAPTER::g_DefaultBackgroundTop;
  45. KIGFX::COLOR4D BOARD_ADAPTER::g_DefaultBackgroundBot;
  46. KIGFX::COLOR4D BOARD_ADAPTER::g_DefaultSilkscreen;
  47. KIGFX::COLOR4D BOARD_ADAPTER::g_DefaultSolderMask;
  48. KIGFX::COLOR4D BOARD_ADAPTER::g_DefaultSolderPaste;
  49. KIGFX::COLOR4D BOARD_ADAPTER::g_DefaultSurfaceFinish;
  50. KIGFX::COLOR4D BOARD_ADAPTER::g_DefaultBoardBody;
  51. static bool g_ColorsLoaded = false;
  52. /**
  53. * Trace mask used to enable or disable the trace output of this class.
  54. * The debug output can be turned on by setting the WXTRACE environment variable to
  55. * "KI_TRACE_EDA_CINFO3D_VISU". See the wxWidgets documentation on wxLogTrace for
  56. * more information.
  57. *
  58. * @ingroup trace_env_vars
  59. */
  60. const wxChar *BOARD_ADAPTER::m_logTrace = wxT( "KI_TRACE_EDA_CINFO3D_VISU" );
  61. BOARD_ADAPTER::BOARD_ADAPTER() :
  62. m_board( nullptr ),
  63. m_3dModelManager( nullptr ),
  64. m_colors( nullptr ),
  65. m_layerZcoordTop(),
  66. m_layerZcoordBottom()
  67. {
  68. wxLogTrace( m_logTrace, wxT( "BOARD_ADAPTER::BOARD_ADAPTER" ) );
  69. m_gridType = GRID3D_TYPE::NONE;
  70. m_antiAliasingMode = ANTIALIASING_MODE::AA_8X;
  71. m_drawFlags.resize( FL_LAST, false );
  72. if( PgmOrNull() )
  73. m_colors = Pgm().GetSettingsManager().GetColorSettings();
  74. m_renderEngine = RENDER_ENGINE::OPENGL;
  75. m_materialMode = MATERIAL_MODE::NORMAL;
  76. m_boardPos = wxPoint();
  77. m_boardSize = wxSize();
  78. m_boardCenter = SFVEC3F( 0.0f );
  79. m_boardBoundingBox.Reset();
  80. m_throughHoleIds.Clear();
  81. m_throughHoleOds.Clear();
  82. m_throughHoleAnnularRings.Clear();
  83. m_copperLayersCount = -1;
  84. m_epoxyThickness3DU = 0.0f;
  85. m_copperThickness3DU = 0.0f;
  86. m_nonCopperLayerThickness3DU = 0.0f;
  87. m_solderPasteLayerThickness3DU = 0.0f;
  88. m_biuTo3Dunits = 1.0;
  89. m_trackCount = 0;
  90. m_viaCount = 0;
  91. m_averageViaHoleDiameter = 0.0f;
  92. m_holeCount = 0;
  93. m_averageHoleDiameter = 0.0f;
  94. m_averageTrackWidth = 0.0f;
  95. SetFlag( FL_USE_REALISTIC_MODE, true );
  96. SetFlag( FL_FP_ATTRIBUTES_NORMAL, true );
  97. SetFlag( FL_SHOW_BOARD_BODY, true );
  98. SetFlag( FL_CLIP_SILK_ON_VIA_ANNULUS, false );
  99. SetFlag( FL_FP_ATTRIBUTES_NORMAL, true );
  100. SetFlag( FL_FP_ATTRIBUTES_NORMAL_INSERT, true );
  101. SetFlag( FL_FP_ATTRIBUTES_VIRTUAL, true );
  102. SetFlag( FL_ZONE, true );
  103. SetFlag( FL_SILKSCREEN, true );
  104. SetFlag( FL_SOLDERMASK, true );
  105. SetFlag( FL_SUBTRACT_MASK_FROM_SILK, false );
  106. SetFlag( FL_RENDER_OPENGL_COPPER_THICKNESS, true );
  107. SetFlag( FL_RENDER_OPENGL_AA_DISABLE_ON_MOVE, false );
  108. SetFlag( FL_RENDER_OPENGL_THICKNESS_DISABLE_ON_MOVE, false );
  109. SetFlag( FL_RENDER_OPENGL_VIAS_DISABLE_ON_MOVE, false );
  110. SetFlag( FL_RENDER_OPENGL_HOLES_DISABLE_ON_MOVE, false );
  111. SetFlag( FL_USE_SELECTION, true );
  112. SetFlag( FL_HIGHLIGHT_ROLLOVER_ITEM, true );
  113. m_BgColorBot = SFVEC4F( 0.4, 0.4, 0.5, 1.0 );
  114. m_BgColorTop = SFVEC4F( 0.8, 0.8, 0.9, 1.0 );
  115. m_BoardBodyColor = SFVEC4F( 0.4, 0.4, 0.5, 0.9 );
  116. m_SolderMaskColorTop = SFVEC4F( 0.1, 0.2, 0.1, 0.83 );
  117. m_SolderMaskColorBot = SFVEC4F( 0.1, 0.2, 0.1, 0.83 );
  118. m_SolderPasteColor = SFVEC4F( 0.4, 0.4, 0.4, 1.0 );
  119. m_SilkScreenColorTop = SFVEC4F( 0.9, 0.9, 0.9, 1.0 );
  120. m_SilkScreenColorBot = SFVEC4F( 0.9, 0.9, 0.9, 1.0 );
  121. m_CopperColor = SFVEC4F( 0.75, 0.61, 0.23, 1.0 );
  122. m_platedPadsFront = nullptr;
  123. m_platedPadsBack = nullptr;
  124. m_frontPlatedPadPolys = nullptr;
  125. m_backPlatedPadPolys = nullptr;
  126. // Avoid raytracing options not initialized:
  127. m_RtShadowSampleCount = 0;
  128. m_RtReflectionSampleCount = 0;
  129. m_RtRefractionSampleCount = 0;
  130. m_RtSpreadShadows = 0.0;
  131. m_RtSpreadReflections = 0.0;
  132. m_RtSpreadRefractions = 0.0;
  133. m_RtRecursiveReflectionCount = 0;
  134. m_RtRecursiveRefractionCount = 0;
  135. if( !g_ColorsLoaded )
  136. {
  137. #define ADD_COLOR( list, r, g, b, a, name ) \
  138. list.push_back( CUSTOM_COLOR_ITEM( r/255.0, g/255.0, b/255.0, a, name ) )
  139. ADD_COLOR( g_SilkscreenColors, 245, 245, 245, 1.0, NotSpecifiedPrm() ); // White
  140. ADD_COLOR( g_SilkscreenColors, 20, 51, 36, 1.0, "Green" );
  141. ADD_COLOR( g_SilkscreenColors, 181, 19, 21, 1.0, "Red" );
  142. ADD_COLOR( g_SilkscreenColors, 2, 59, 162, 1.0, "Blue" );
  143. ADD_COLOR( g_SilkscreenColors, 11, 11, 11, 1.0, "Black" );
  144. ADD_COLOR( g_SilkscreenColors, 245, 245, 245, 1.0, "White" );
  145. ADD_COLOR( g_SilkscreenColors, 32, 2, 53, 1.0, "Purple" );
  146. ADD_COLOR( g_SilkscreenColors, 194, 195, 0, 1.0, "Yellow" );
  147. ADD_COLOR( g_MaskColors, 20, 51, 36, 0.83, NotSpecifiedPrm() ); // Green
  148. ADD_COLOR( g_MaskColors, 20, 51, 36, 0.83, "Green" );
  149. ADD_COLOR( g_MaskColors, 91, 168, 12, 0.83, "Light Green" );
  150. ADD_COLOR( g_MaskColors, 13, 104, 11, 0.83, "Saturated Green" );
  151. ADD_COLOR( g_MaskColors, 181, 19, 21, 0.83, "Red" );
  152. ADD_COLOR( g_MaskColors, 210, 40, 14, 0.83, "Light Red" );
  153. ADD_COLOR( g_MaskColors, 239, 53, 41, 0.83, "Red/Orange" );
  154. ADD_COLOR( g_MaskColors, 2, 59, 162, 0.83, "Blue" );
  155. ADD_COLOR( g_MaskColors, 54, 79, 116, 0.83, "Light Blue 1" );
  156. ADD_COLOR( g_MaskColors, 61, 85, 130, 0.83, "Light Blue 2" );
  157. ADD_COLOR( g_MaskColors, 21, 70, 80, 0.83, "Green/Blue" );
  158. ADD_COLOR( g_MaskColors, 11, 11, 11, 0.83, "Black" );
  159. ADD_COLOR( g_MaskColors, 245, 245, 245, 0.83, "White" );
  160. ADD_COLOR( g_MaskColors, 32, 2, 53, 0.83, "Purple" );
  161. ADD_COLOR( g_MaskColors, 119, 31, 91, 0.83, "Light Purple" );
  162. ADD_COLOR( g_MaskColors, 194, 195, 0, 0.83, "Yellow" );
  163. ADD_COLOR( g_PasteColors, 128, 128, 128, 1.0, "Grey" );
  164. ADD_COLOR( g_PasteColors, 90, 90, 90, 1.0, "Dark Grey" );
  165. ADD_COLOR( g_PasteColors, 213, 213, 213, 1.0, "Silver" );
  166. ADD_COLOR( g_FinishColors, 184, 115, 50, 1.0, "Copper" );
  167. ADD_COLOR( g_FinishColors, 178, 156, 0, 1.0, "Gold" );
  168. ADD_COLOR( g_FinishColors, 213, 213, 213, 1.0, "Silver" );
  169. ADD_COLOR( g_FinishColors, 160, 160, 160, 1.0, "Tin" );
  170. ADD_COLOR( g_BoardColors, 51, 43, 22, 0.83, "FR4 natural, dark" );
  171. ADD_COLOR( g_BoardColors, 109, 116, 75, 0.83, "FR4 natural" );
  172. ADD_COLOR( g_BoardColors, 252, 252, 250, 0.90, "PTFE natural" );
  173. ADD_COLOR( g_BoardColors, 205, 130, 0, 0.68, "Polyimide" );
  174. ADD_COLOR( g_BoardColors, 92, 17, 6, 0.90, "Phenolic natural" );
  175. ADD_COLOR( g_BoardColors, 146, 99, 47, 0.83, "Brown 1" );
  176. ADD_COLOR( g_BoardColors, 160, 123, 54, 0.83, "Brown 2" );
  177. ADD_COLOR( g_BoardColors, 146, 99, 47, 0.83, "Brown 3" );
  178. ADD_COLOR( g_BoardColors, 213, 213, 213, 1.0, "Aluminum" );
  179. g_DefaultBackgroundTop = COLOR4D( 0.80, 0.80, 0.90, 1.0 );
  180. g_DefaultBackgroundBot = COLOR4D( 0.40, 0.40, 0.50, 1.0 );
  181. g_DefaultSilkscreen = COLOR4D( 0.94, 0.94, 0.94, 1.0 );
  182. g_DefaultSolderMask = COLOR4D( 0.08, 0.20, 0.14, 0.83 );
  183. g_DefaultSolderPaste = COLOR4D( 0.50, 0.50, 0.50, 1.0 );
  184. g_DefaultSurfaceFinish = COLOR4D( 0.75, 0.61, 0.23, 1.0 );
  185. g_DefaultBoardBody = COLOR4D( 0.43, 0.45, 0.30, 0.90 );
  186. g_ColorsLoaded = true;
  187. }
  188. #undef ADD_COLOR
  189. }
  190. BOARD_ADAPTER::~BOARD_ADAPTER()
  191. {
  192. destroyLayers();
  193. }
  194. bool BOARD_ADAPTER::Is3dLayerEnabled( PCB_LAYER_ID aLayer ) const
  195. {
  196. wxASSERT( aLayer < PCB_LAYER_ID_COUNT );
  197. if( m_board && !m_board->IsLayerEnabled( aLayer ) )
  198. return false;
  199. // see if layer needs to be shown
  200. // check the flags
  201. switch( aLayer )
  202. {
  203. case B_Adhes:
  204. case F_Adhes:
  205. return GetFlag( FL_ADHESIVE );
  206. case B_Paste:
  207. case F_Paste:
  208. return GetFlag( FL_SOLDERPASTE );
  209. case B_SilkS:
  210. case F_SilkS:
  211. return GetFlag( FL_SILKSCREEN );
  212. case B_Mask:
  213. case F_Mask:
  214. return GetFlag( FL_SOLDERMASK );
  215. case Dwgs_User:
  216. case Cmts_User:
  217. if( GetFlag( FL_USE_REALISTIC_MODE ) )
  218. return false;
  219. return GetFlag( FL_COMMENTS );
  220. case Eco1_User:
  221. case Eco2_User:
  222. if( GetFlag( FL_USE_REALISTIC_MODE ) )
  223. return false;
  224. return GetFlag( FL_ECO );
  225. case Edge_Cuts:
  226. if( GetFlag( FL_SHOW_BOARD_BODY ) || GetFlag( FL_USE_REALISTIC_MODE ) )
  227. return false;
  228. return true;
  229. case Margin:
  230. if( GetFlag( FL_USE_REALISTIC_MODE ) )
  231. return false;
  232. return true;
  233. case B_Cu:
  234. case F_Cu:
  235. return m_board ? m_board->IsLayerVisible( aLayer ) || GetFlag( FL_USE_REALISTIC_MODE )
  236. : true;
  237. default:
  238. // the layer is an internal copper layer, used the visibility
  239. return m_board && m_board->IsLayerVisible( aLayer );
  240. }
  241. }
  242. bool BOARD_ADAPTER::GetFlag( DISPLAY3D_FLG aFlag ) const
  243. {
  244. wxASSERT( aFlag < FL_LAST );
  245. return m_drawFlags[aFlag];
  246. }
  247. void BOARD_ADAPTER::SetFlag( DISPLAY3D_FLG aFlag, bool aState )
  248. {
  249. wxASSERT( aFlag < FL_LAST );
  250. m_drawFlags[aFlag] = aState;
  251. }
  252. bool BOARD_ADAPTER::IsFootprintShown( FOOTPRINT_ATTR_T aFPAttributes ) const
  253. {
  254. if( aFPAttributes & FP_SMD )
  255. return GetFlag( FL_FP_ATTRIBUTES_NORMAL_INSERT );
  256. else if( aFPAttributes & FP_THROUGH_HOLE )
  257. return GetFlag( FL_FP_ATTRIBUTES_NORMAL );
  258. else
  259. return GetFlag( FL_FP_ATTRIBUTES_VIRTUAL );
  260. }
  261. // !TODO: define the actual copper thickness by user from board stackup
  262. #define COPPER_THICKNESS Millimeter2iu( 0.035 ) // for 35 um
  263. // The solder mask layer (and silkscreen) thickness
  264. #define TECH_LAYER_THICKNESS Millimeter2iu( 0.025 )
  265. // The solder paste thickness is chosen bigger than the solder mask layer
  266. // to be sure is covers the mask when overlapping.
  267. #define SOLDERPASTE_LAYER_THICKNESS Millimeter2iu( 0.04 )
  268. int BOARD_ADAPTER::GetHolePlatingThickness() const noexcept
  269. {
  270. return m_board ? m_board->GetDesignSettings().GetHolePlatingThickness()
  271. : 0.035 * PCB_IU_PER_MM;
  272. }
  273. unsigned int BOARD_ADAPTER::GetCircleSegmentCount( float aDiameter3DU ) const
  274. {
  275. wxASSERT( aDiameter3DU > 0.0f );
  276. return GetCircleSegmentCount( (int)( aDiameter3DU / m_biuTo3Dunits ) );
  277. }
  278. unsigned int BOARD_ADAPTER::GetCircleSegmentCount( int aDiameterBIU ) const
  279. {
  280. wxASSERT( aDiameterBIU > 0 );
  281. return GetArcToSegmentCount( aDiameterBIU / 2, ARC_HIGH_DEF, 360.0 );
  282. }
  283. void BOARD_ADAPTER::InitSettings( REPORTER* aStatusReporter, REPORTER* aWarningReporter )
  284. {
  285. wxLogTrace( m_logTrace, wxT( "BOARD_ADAPTER::InitSettings" ) );
  286. if( aStatusReporter )
  287. aStatusReporter->Report( _( "Build board outline" ) );
  288. wxString msg;
  289. const bool succeedToGetBoardPolygon = createBoardPolygon( &msg );
  290. if( aWarningReporter )
  291. {
  292. if( !succeedToGetBoardPolygon )
  293. aWarningReporter->Report( msg, RPT_SEVERITY_WARNING );
  294. else
  295. aWarningReporter->Report( wxEmptyString );
  296. }
  297. // Calculates the board bounding box (board outlines + items)
  298. // to ensure any item, even outside the board outlines can be seen
  299. bool boardEdgesOnly = true;
  300. if( ( m_board && m_board->IsFootprintHolder() ) || !GetFlag( FL_USE_REALISTIC_MODE )
  301. || !succeedToGetBoardPolygon )
  302. {
  303. boardEdgesOnly = false;
  304. }
  305. EDA_RECT bbbox;
  306. if( m_board )
  307. bbbox = m_board->ComputeBoundingBox( boardEdgesOnly );
  308. // Gives a non null size to avoid issues in zoom / scale calculations
  309. if( ( bbbox.GetWidth() == 0 ) && ( bbbox.GetHeight() == 0 ) )
  310. bbbox.Inflate( Millimeter2iu( 10 ) );
  311. m_boardSize = bbbox.GetSize();
  312. m_boardPos = bbbox.Centre();
  313. wxASSERT( (m_boardSize.x > 0) && (m_boardSize.y > 0) );
  314. m_boardPos.y = -m_boardPos.y; // The y coord is inverted in 3D viewer
  315. m_copperLayersCount = m_board ? m_board->GetCopperLayerCount() : 2;
  316. // Ensure the board has 2 sides for 3D views, because it is hard to find
  317. // a *really* single side board in the true life...
  318. if( m_copperLayersCount < 2 )
  319. m_copperLayersCount = 2;
  320. // Calculate the conversion to apply to all positions.
  321. m_biuTo3Dunits = RANGE_SCALE_3D / std::max( m_boardSize.x, m_boardSize.y );
  322. m_epoxyThickness3DU = m_board
  323. ? m_board->GetDesignSettings().GetBoardThickness() * m_biuTo3Dunits
  324. : 1.6 * PCB_IU_PER_MM * m_biuTo3Dunits;
  325. // !TODO: use value defined by user (currently use default values by ctor
  326. m_copperThickness3DU = COPPER_THICKNESS * m_biuTo3Dunits;
  327. m_nonCopperLayerThickness3DU = TECH_LAYER_THICKNESS * m_biuTo3Dunits;
  328. m_solderPasteLayerThickness3DU = SOLDERPASTE_LAYER_THICKNESS * m_biuTo3Dunits;
  329. // Init Z position of each layer
  330. // calculate z position for each copper layer
  331. // Zstart = -m_epoxyThickness / 2.0 is the z position of the back (bottom layer) (layer id = 31)
  332. // Zstart = +m_epoxyThickness / 2.0 is the z position of the front (top layer) (layer id = 0)
  333. // all unused copper layer z position are set to 0
  334. // ____==__________==________==______ <- Bottom = +m_epoxyThickness / 2.0,
  335. // | | Top = Bottom + m_copperThickness
  336. // |__________________________________|
  337. // == == == == <- Bottom = -m_epoxyThickness / 2.0,
  338. // Top = Bottom - m_copperThickness
  339. unsigned int layer;
  340. for( layer = 0; layer < m_copperLayersCount; ++layer )
  341. {
  342. m_layerZcoordBottom[layer] = m_epoxyThickness3DU / 2.0f -
  343. (m_epoxyThickness3DU * layer / (m_copperLayersCount - 1) );
  344. if( layer < (m_copperLayersCount / 2) )
  345. m_layerZcoordTop[layer] = m_layerZcoordBottom[layer] + m_copperThickness3DU;
  346. else
  347. m_layerZcoordTop[layer] = m_layerZcoordBottom[layer] - m_copperThickness3DU;
  348. }
  349. #define layerThicknessMargin 1.1
  350. const float zpos_offset = m_nonCopperLayerThickness3DU * layerThicknessMargin;
  351. // Fill remaining unused copper layers and back layer zpos
  352. // with -m_epoxyThickness / 2.0
  353. for( ; layer < MAX_CU_LAYERS; layer++ )
  354. {
  355. m_layerZcoordBottom[layer] = -(m_epoxyThickness3DU / 2.0f);
  356. m_layerZcoordTop[layer] = -(m_epoxyThickness3DU / 2.0f) - m_copperThickness3DU;
  357. }
  358. // This is the top of the copper layer thickness.
  359. const float zpos_copperTop_back = m_layerZcoordTop[B_Cu];
  360. const float zpos_copperTop_front = m_layerZcoordTop[F_Cu];
  361. // calculate z position for each non copper layer
  362. // Solder mask and Solder paste have the same Z position
  363. for( int layer_id = MAX_CU_LAYERS; layer_id < PCB_LAYER_ID_COUNT; ++layer_id )
  364. {
  365. float zposTop;
  366. float zposBottom;
  367. switch( layer_id )
  368. {
  369. case B_Adhes:
  370. zposBottom = zpos_copperTop_back - 2.0f * zpos_offset;
  371. zposTop = zposBottom - m_nonCopperLayerThickness3DU;
  372. break;
  373. case F_Adhes:
  374. zposBottom = zpos_copperTop_front + 2.0f * zpos_offset;
  375. zposTop = zposBottom + m_nonCopperLayerThickness3DU;
  376. break;
  377. case B_Mask:
  378. zposBottom = zpos_copperTop_back;
  379. zposTop = zpos_copperTop_back - m_nonCopperLayerThickness3DU;
  380. break;
  381. case B_Paste:
  382. zposBottom = zpos_copperTop_back;
  383. zposTop = zpos_copperTop_back - m_solderPasteLayerThickness3DU;
  384. break;
  385. case F_Mask:
  386. zposBottom = zpos_copperTop_front;
  387. zposTop = zpos_copperTop_front + m_nonCopperLayerThickness3DU;
  388. break;
  389. case F_Paste:
  390. zposBottom = zpos_copperTop_front;
  391. zposTop = zpos_copperTop_front + m_solderPasteLayerThickness3DU;
  392. break;
  393. case B_SilkS:
  394. zposBottom = zpos_copperTop_back - 1.0f * zpos_offset;
  395. zposTop = zposBottom - m_nonCopperLayerThickness3DU;
  396. break;
  397. case F_SilkS:
  398. zposBottom = zpos_copperTop_front + 1.0f * zpos_offset;
  399. zposTop = zposBottom + m_nonCopperLayerThickness3DU;
  400. break;
  401. // !TODO: review
  402. default:
  403. zposTop = zpos_copperTop_front + (layer_id - MAX_CU_LAYERS + 3.0f) * zpos_offset;
  404. zposBottom = zposTop - m_nonCopperLayerThickness3DU;
  405. break;
  406. }
  407. m_layerZcoordTop[layer_id] = zposTop;
  408. m_layerZcoordBottom[layer_id] = zposBottom;
  409. }
  410. m_boardCenter = SFVEC3F( m_boardPos.x * m_biuTo3Dunits, m_boardPos.y * m_biuTo3Dunits, 0.0f );
  411. SFVEC3F boardSize = SFVEC3F( m_boardSize.x * m_biuTo3Dunits, m_boardSize.y * m_biuTo3Dunits,
  412. 0.0f );
  413. boardSize /= 2.0f;
  414. SFVEC3F boardMin = ( m_boardCenter - boardSize );
  415. SFVEC3F boardMax = ( m_boardCenter + boardSize );
  416. boardMin.z = m_layerZcoordTop[B_Adhes];
  417. boardMax.z = m_layerZcoordTop[F_Adhes];
  418. m_boardBoundingBox = BBOX_3D( boardMin, boardMax );
  419. #ifdef PRINT_STATISTICS_3D_VIEWER
  420. unsigned stats_startCreateBoardPolyTime = GetRunningMicroSecs();
  421. #endif
  422. if( aStatusReporter )
  423. aStatusReporter->Report( _( "Create layers" ) );
  424. createLayers( aStatusReporter );
  425. COLOR_SETTINGS* colors = Pgm().GetSettingsManager().GetColorSettings();
  426. auto to_SFVEC4F =
  427. []( const COLOR4D& src )
  428. {
  429. return SFVEC4F( src.r, src.g, src.b, src.a );
  430. };
  431. m_BgColorTop = to_SFVEC4F( colors->GetColor( LAYER_3D_BACKGROUND_TOP ) );
  432. m_BgColorBot = to_SFVEC4F( colors->GetColor( LAYER_3D_BACKGROUND_BOTTOM ) );
  433. m_SolderPasteColor = to_SFVEC4F( colors->GetColor( LAYER_3D_SOLDERPASTE ) );
  434. if( m_board && colors->GetUseBoardStackupColors() )
  435. {
  436. const BOARD_STACKUP& stackup = m_board->GetDesignSettings().GetStackupDescriptor();
  437. auto findColor =
  438. []( const wxString& aColorName, const CUSTOM_COLORS_LIST& aColorSet )
  439. {
  440. if( aColorName.StartsWith( "#" ) )
  441. {
  442. return KIGFX::COLOR4D( wxColour( aColorName ) );
  443. }
  444. else
  445. {
  446. for( const CUSTOM_COLOR_ITEM& color : aColorSet )
  447. {
  448. if( color.m_ColorName == aColorName )
  449. return color.m_Color;
  450. }
  451. }
  452. return KIGFX::COLOR4D();
  453. };
  454. m_SilkScreenColorTop = to_SFVEC4F( g_DefaultSilkscreen );
  455. m_SilkScreenColorBot = to_SFVEC4F( g_DefaultSilkscreen );
  456. m_SolderMaskColorTop = to_SFVEC4F( g_DefaultSolderMask );
  457. m_SolderMaskColorBot = to_SFVEC4F( g_DefaultSolderMask );
  458. KIGFX::COLOR4D bodyColor( 0, 0, 0, 0 );
  459. for( const BOARD_STACKUP_ITEM* stackupItem : stackup.GetList() )
  460. {
  461. wxString colorName = stackupItem->GetColor();
  462. switch( stackupItem->GetType() )
  463. {
  464. case BS_ITEM_TYPE_SILKSCREEN:
  465. if( stackupItem->GetBrdLayerId() == F_SilkS )
  466. m_SilkScreenColorTop = to_SFVEC4F( findColor( colorName, g_SilkscreenColors ) );
  467. else
  468. m_SilkScreenColorBot = to_SFVEC4F( findColor( colorName, g_SilkscreenColors ) );
  469. break;
  470. case BS_ITEM_TYPE_SOLDERMASK:
  471. if( stackupItem->GetBrdLayerId() == F_Mask )
  472. m_SolderMaskColorTop = to_SFVEC4F( findColor( colorName, g_MaskColors ) );
  473. else
  474. m_SolderMaskColorBot = to_SFVEC4F( findColor( colorName, g_MaskColors ) );
  475. break;
  476. case BS_ITEM_TYPE_DIELECTRIC:
  477. {
  478. KIGFX::COLOR4D layerColor = COLOR4D::UNSPECIFIED;
  479. const wxString& materialName = stackupItem->GetMaterial();
  480. if( materialName.StartsWith( "FR4" ) )
  481. {
  482. layerColor = findColor( "FR4 natural", g_BoardColors );
  483. }
  484. else if( materialName.IsSameAs( "PTFE" )
  485. || materialName.IsSameAs( "Teflon" ) )
  486. {
  487. layerColor = findColor( "PTFE natural", g_BoardColors );
  488. }
  489. else if( materialName.IsSameAs( "Polyimide" )
  490. || materialName.IsSameAs( "Kapton" ) )
  491. {
  492. layerColor = findColor( "Polyimide", g_BoardColors );
  493. }
  494. else if( materialName.IsSameAs( "Al" ) )
  495. {
  496. layerColor = findColor( "Aluminum", g_BoardColors );
  497. }
  498. else // A default color value for unknown dielectric material
  499. // (i.e. an exotic name entered by hand)
  500. {
  501. layerColor = findColor( "FR4 natural", g_BoardColors );
  502. }
  503. if( bodyColor == COLOR4D( 0, 0, 0, 0 ) )
  504. bodyColor = layerColor;
  505. else
  506. bodyColor = bodyColor.Mix( layerColor, 1.0 - layerColor.a );
  507. bodyColor.a += ( 1.0 - bodyColor.a ) * layerColor.a / 2;
  508. break;
  509. }
  510. default:
  511. break;
  512. }
  513. }
  514. if( bodyColor != COLOR4D( 0, 0, 0, 0 ) )
  515. m_BoardBodyColor = to_SFVEC4F( bodyColor );
  516. else
  517. m_BoardBodyColor = to_SFVEC4F( g_DefaultBoardBody );
  518. const wxString& finishName = stackup.m_FinishType;
  519. if( finishName.EndsWith( "OSP" ) )
  520. {
  521. m_CopperColor = to_SFVEC4F( findColor( "Copper", g_FinishColors ) );
  522. }
  523. else if( finishName.EndsWith( "IG" )
  524. || finishName.EndsWith( "gold" ) )
  525. {
  526. m_CopperColor = to_SFVEC4F( findColor( "Gold", g_FinishColors ) );
  527. }
  528. else if( finishName.StartsWith( "HAL" )
  529. || finishName.StartsWith( "HASL" )
  530. || finishName.EndsWith( "tin" )
  531. || finishName.EndsWith( "nickel" ) )
  532. {
  533. m_CopperColor = to_SFVEC4F( findColor( "Tin", g_FinishColors ) );
  534. }
  535. else if( finishName.EndsWith( "silver" ) )
  536. {
  537. m_CopperColor = to_SFVEC4F( findColor( "Silver", g_FinishColors ) );
  538. }
  539. else
  540. {
  541. m_CopperColor = to_SFVEC4F( g_DefaultSurfaceFinish );
  542. }
  543. }
  544. else
  545. {
  546. m_SilkScreenColorBot = to_SFVEC4F( colors->GetColor( LAYER_3D_SILKSCREEN_BOTTOM ) );
  547. m_SilkScreenColorTop = to_SFVEC4F( colors->GetColor( LAYER_3D_SILKSCREEN_TOP ) );
  548. m_SolderMaskColorBot = to_SFVEC4F( colors->GetColor( LAYER_3D_SOLDERMASK_BOTTOM ) );
  549. m_SolderMaskColorTop = to_SFVEC4F( colors->GetColor( LAYER_3D_SOLDERMASK_TOP ) );
  550. m_CopperColor = to_SFVEC4F( colors->GetColor( LAYER_3D_COPPER ) );
  551. m_BoardBodyColor = to_SFVEC4F( colors->GetColor( LAYER_3D_BOARD ) );
  552. }
  553. }
  554. extern bool BuildFootprintPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines,
  555. int aErrorMax, int aChainingEpsilon,
  556. OUTLINE_ERROR_HANDLER* aErrorHandler = nullptr );
  557. bool BOARD_ADAPTER::createBoardPolygon( wxString* aErrorMsg )
  558. {
  559. m_board_poly.RemoveAllContours();
  560. if( !m_board )
  561. return false;
  562. bool success;
  563. if( m_board->IsFootprintHolder() )
  564. {
  565. if( !m_board->GetFirstFootprint() )
  566. {
  567. if( aErrorMsg )
  568. *aErrorMsg = _( "No footprint loaded." );
  569. return false;
  570. }
  571. int chainingEpsilon = Millimeter2iu( 0.02 ); // max dist from one endPt to next startPt
  572. success = BuildFootprintPolygonOutlines( m_board, m_board_poly,
  573. m_board->GetDesignSettings().m_MaxError,
  574. chainingEpsilon );
  575. // Make polygon strictly simple to avoid issues (especially in 3D viewer)
  576. m_board_poly.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
  577. if( !success && aErrorMsg )
  578. {
  579. *aErrorMsg = _( "Footprint outline is missing or malformed. Run Footprint Checker for "
  580. "a full analysis." );
  581. }
  582. }
  583. else
  584. {
  585. success = m_board->GetBoardPolygonOutlines( m_board_poly );
  586. if( !success && aErrorMsg )
  587. *aErrorMsg = _( "Board outline is missing or malformed. Run DRC for a full analysis." );
  588. }
  589. return success;
  590. }
  591. float BOARD_ADAPTER::GetFootprintZPos( bool aIsFlipped ) const
  592. {
  593. if( aIsFlipped )
  594. {
  595. if( GetFlag( FL_SOLDERPASTE ) )
  596. return m_layerZcoordBottom[B_SilkS];
  597. else
  598. return m_layerZcoordBottom[B_Paste];
  599. }
  600. else
  601. {
  602. if( GetFlag( FL_SOLDERPASTE ) )
  603. return m_layerZcoordTop[F_SilkS];
  604. else
  605. return m_layerZcoordTop[F_Paste];
  606. }
  607. }
  608. SFVEC4F BOARD_ADAPTER::GetLayerColor( PCB_LAYER_ID aLayerId ) const
  609. {
  610. wxASSERT( aLayerId < PCB_LAYER_ID_COUNT );
  611. const COLOR4D color = m_colors->GetColor( aLayerId );
  612. return SFVEC4F( color.r, color.g, color.b, color.a );
  613. }
  614. SFVEC4F BOARD_ADAPTER::GetItemColor( int aItemId ) const
  615. {
  616. return GetColor( m_colors->GetColor( aItemId ) );
  617. }
  618. SFVEC4F BOARD_ADAPTER::GetColor( const COLOR4D& aColor ) const
  619. {
  620. return SFVEC4F( aColor.r, aColor.g, aColor.b, aColor.a );
  621. }