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.

738 lines
26 KiB

4 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. #define DEFAULT_BOARD_THICKNESS Millimeter2iu( 1.6 )
  40. #define DEFAULT_COPPER_THICKNESS Millimeter2iu( 0.035 ) // for 35 um
  41. // The solder mask layer (and silkscreen) thickness
  42. #define DEFAULT_TECH_LAYER_THICKNESS Millimeter2iu( 0.025 )
  43. // The solder paste thickness is chosen bigger than the solder mask layer
  44. // to be sure is covers the mask when overlapping.
  45. #define SOLDERPASTE_LAYER_THICKNESS Millimeter2iu( 0.04 )
  46. CUSTOM_COLORS_LIST BOARD_ADAPTER::g_SilkscreenColors;
  47. CUSTOM_COLORS_LIST BOARD_ADAPTER::g_MaskColors;
  48. CUSTOM_COLORS_LIST BOARD_ADAPTER::g_PasteColors;
  49. CUSTOM_COLORS_LIST BOARD_ADAPTER::g_FinishColors;
  50. CUSTOM_COLORS_LIST BOARD_ADAPTER::g_BoardColors;
  51. KIGFX::COLOR4D BOARD_ADAPTER::g_DefaultBackgroundTop;
  52. KIGFX::COLOR4D BOARD_ADAPTER::g_DefaultBackgroundBot;
  53. KIGFX::COLOR4D BOARD_ADAPTER::g_DefaultSilkscreen;
  54. KIGFX::COLOR4D BOARD_ADAPTER::g_DefaultSolderMask;
  55. KIGFX::COLOR4D BOARD_ADAPTER::g_DefaultSolderPaste;
  56. KIGFX::COLOR4D BOARD_ADAPTER::g_DefaultSurfaceFinish;
  57. KIGFX::COLOR4D BOARD_ADAPTER::g_DefaultBoardBody;
  58. static bool g_ColorsLoaded = false;
  59. /**
  60. * Trace mask used to enable or disable the trace output of this class.
  61. * The debug output can be turned on by setting the WXTRACE environment variable to
  62. * "KI_TRACE_EDA_CINFO3D_VISU". See the wxWidgets documentation on wxLogTrace for
  63. * more information.
  64. *
  65. * @ingroup trace_env_vars
  66. */
  67. const wxChar *BOARD_ADAPTER::m_logTrace = wxT( "KI_TRACE_EDA_CINFO3D_VISU" );
  68. BOARD_ADAPTER::BOARD_ADAPTER() :
  69. m_Cfg( nullptr ),
  70. m_IsBoardView( true ),
  71. m_MousewheelPanning( true ),
  72. m_IsPreviewer( false ),
  73. m_board( nullptr ),
  74. m_3dModelManager( nullptr ),
  75. m_colors( nullptr ),
  76. m_layerZcoordTop(),
  77. m_layerZcoordBottom()
  78. {
  79. wxLogTrace( m_logTrace, wxT( "BOARD_ADAPTER::BOARD_ADAPTER" ) );
  80. if( PgmOrNull() )
  81. m_colors = Pgm().GetSettingsManager().GetColorSettings();
  82. m_boardPos = VECTOR2I();
  83. m_boardSize = VECTOR2I();
  84. m_boardCenter = SFVEC3F( 0.0f );
  85. m_boardBoundingBox.Reset();
  86. m_throughHoleIds.Clear();
  87. m_throughHoleOds.Clear();
  88. m_throughHoleAnnularRings.Clear();
  89. m_copperLayersCount = 2;
  90. m_biuTo3Dunits = 1.0;
  91. m_boardBodyThickness3DU = DEFAULT_BOARD_THICKNESS * m_biuTo3Dunits;
  92. m_frontCopperThickness3DU = DEFAULT_COPPER_THICKNESS * m_biuTo3Dunits;
  93. m_backCopperThickness3DU = DEFAULT_COPPER_THICKNESS * m_biuTo3Dunits;
  94. m_nonCopperLayerThickness3DU = DEFAULT_TECH_LAYER_THICKNESS * m_biuTo3Dunits;
  95. m_solderPasteLayerThickness3DU = SOLDERPASTE_LAYER_THICKNESS * m_biuTo3Dunits;
  96. m_trackCount = 0;
  97. m_viaCount = 0;
  98. m_averageViaHoleDiameter = 0.0f;
  99. m_holeCount = 0;
  100. m_averageHoleDiameter = 0.0f;
  101. m_averageTrackWidth = 0.0f;
  102. m_BgColorBot = SFVEC4F( 0.4, 0.4, 0.5, 1.0 );
  103. m_BgColorTop = SFVEC4F( 0.8, 0.8, 0.9, 1.0 );
  104. m_BoardBodyColor = SFVEC4F( 0.4, 0.4, 0.5, 0.9 );
  105. m_SolderMaskColorTop = SFVEC4F( 0.1, 0.2, 0.1, 0.83 );
  106. m_SolderMaskColorBot = SFVEC4F( 0.1, 0.2, 0.1, 0.83 );
  107. m_SolderPasteColor = SFVEC4F( 0.4, 0.4, 0.4, 1.0 );
  108. m_SilkScreenColorTop = SFVEC4F( 0.9, 0.9, 0.9, 1.0 );
  109. m_SilkScreenColorBot = SFVEC4F( 0.9, 0.9, 0.9, 1.0 );
  110. m_CopperColor = SFVEC4F( 0.75, 0.61, 0.23, 1.0 );
  111. m_platedPadsFront = nullptr;
  112. m_platedPadsBack = nullptr;
  113. m_frontPlatedPadPolys = nullptr;
  114. m_backPlatedPadPolys = nullptr;
  115. if( !g_ColorsLoaded )
  116. {
  117. #define ADD_COLOR( list, r, g, b, a, name ) \
  118. list.emplace_back( r/255.0, g/255.0, b/255.0, a, name )
  119. ADD_COLOR( g_SilkscreenColors, 245, 245, 245, 1.0, NotSpecifiedPrm() ); // White
  120. ADD_COLOR( g_SilkscreenColors, 20, 51, 36, 1.0, wxT( "Green" ) );
  121. ADD_COLOR( g_SilkscreenColors, 181, 19, 21, 1.0, wxT( "Red" ) );
  122. ADD_COLOR( g_SilkscreenColors, 2, 59, 162, 1.0, wxT( "Blue" ) );
  123. ADD_COLOR( g_SilkscreenColors, 11, 11, 11, 1.0, wxT( "Black" ) );
  124. ADD_COLOR( g_SilkscreenColors, 245, 245, 245, 1.0, wxT( "White" ) );
  125. ADD_COLOR( g_SilkscreenColors, 32, 2, 53, 1.0, wxT( "Purple" ) );
  126. ADD_COLOR( g_SilkscreenColors, 194, 195, 0, 1.0, wxT( "Yellow" ) );
  127. ADD_COLOR( g_MaskColors, 20, 51, 36, 0.83, NotSpecifiedPrm() ); // Green
  128. ADD_COLOR( g_MaskColors, 20, 51, 36, 0.83, wxT( "Green" ) );
  129. ADD_COLOR( g_MaskColors, 91, 168, 12, 0.83, wxT( "Light Green" ) );
  130. ADD_COLOR( g_MaskColors, 13, 104, 11, 0.83, wxT( "Saturated Green" ) );
  131. ADD_COLOR( g_MaskColors, 181, 19, 21, 0.83, wxT( "Red" ) );
  132. ADD_COLOR( g_MaskColors, 210, 40, 14, 0.83, wxT( "Light Red" ) );
  133. ADD_COLOR( g_MaskColors, 239, 53, 41, 0.83, wxT( "Red/Orange" ) );
  134. ADD_COLOR( g_MaskColors, 2, 59, 162, 0.83, wxT( "Blue" ) );
  135. ADD_COLOR( g_MaskColors, 54, 79, 116, 0.83, wxT( "Light Blue 1" ) );
  136. ADD_COLOR( g_MaskColors, 61, 85, 130, 0.83, wxT( "Light Blue 2" ) );
  137. ADD_COLOR( g_MaskColors, 21, 70, 80, 0.83, wxT( "Green/Blue" ) );
  138. ADD_COLOR( g_MaskColors, 11, 11, 11, 0.83, wxT( "Black" ) );
  139. ADD_COLOR( g_MaskColors, 245, 245, 245, 0.83, wxT( "White" ) );
  140. ADD_COLOR( g_MaskColors, 32, 2, 53, 0.83, wxT( "Purple" ) );
  141. ADD_COLOR( g_MaskColors, 119, 31, 91, 0.83, wxT( "Light Purple" ) );
  142. ADD_COLOR( g_MaskColors, 194, 195, 0, 0.83, wxT( "Yellow" ) );
  143. ADD_COLOR( g_PasteColors, 128, 128, 128, 1.0, wxT( "Grey" ) );
  144. ADD_COLOR( g_PasteColors, 90, 90, 90, 1.0, wxT( "Dark Grey" ) );
  145. ADD_COLOR( g_PasteColors, 213, 213, 213, 1.0, wxT( "Silver" ) );
  146. ADD_COLOR( g_FinishColors, 184, 115, 50, 1.0, wxT( "Copper" ) );
  147. ADD_COLOR( g_FinishColors, 178, 156, 0, 1.0, wxT( "Gold" ) );
  148. ADD_COLOR( g_FinishColors, 213, 213, 213, 1.0, wxT( "Silver" ) );
  149. ADD_COLOR( g_FinishColors, 160, 160, 160, 1.0, wxT( "Tin" ) );
  150. ADD_COLOR( g_BoardColors, 51, 43, 22, 0.83, wxT( "FR4 natural, dark" ) );
  151. ADD_COLOR( g_BoardColors, 109, 116, 75, 0.83, wxT( "FR4 natural" ) );
  152. ADD_COLOR( g_BoardColors, 252, 252, 250, 0.90, wxT( "PTFE natural" ) );
  153. ADD_COLOR( g_BoardColors, 205, 130, 0, 0.68, wxT( "Polyimide" ) );
  154. ADD_COLOR( g_BoardColors, 92, 17, 6, 0.90, wxT( "Phenolic natural" ) );
  155. ADD_COLOR( g_BoardColors, 146, 99, 47, 0.83, wxT( "Brown 1" ) );
  156. ADD_COLOR( g_BoardColors, 160, 123, 54, 0.83, wxT( "Brown 2" ) );
  157. ADD_COLOR( g_BoardColors, 146, 99, 47, 0.83, wxT( "Brown 3" ) );
  158. ADD_COLOR( g_BoardColors, 213, 213, 213, 1.0, wxT( "Aluminum" ) );
  159. g_DefaultBackgroundTop = COLOR4D( 0.80, 0.80, 0.90, 1.0 );
  160. g_DefaultBackgroundBot = COLOR4D( 0.40, 0.40, 0.50, 1.0 );
  161. g_DefaultSilkscreen = COLOR4D( 0.94, 0.94, 0.94, 1.0 );
  162. g_DefaultSolderMask = COLOR4D( 0.08, 0.20, 0.14, 0.83 );
  163. g_DefaultSolderPaste = COLOR4D( 0.50, 0.50, 0.50, 1.0 );
  164. g_DefaultSurfaceFinish = COLOR4D( 0.75, 0.61, 0.23, 1.0 );
  165. g_DefaultBoardBody = COLOR4D( 0.43, 0.45, 0.30, 0.90 );
  166. g_ColorsLoaded = true;
  167. }
  168. #undef ADD_COLOR
  169. }
  170. BOARD_ADAPTER::~BOARD_ADAPTER()
  171. {
  172. destroyLayers();
  173. }
  174. bool BOARD_ADAPTER::Is3dLayerEnabled( PCB_LAYER_ID aLayer ) const
  175. {
  176. wxASSERT( aLayer < PCB_LAYER_ID_COUNT );
  177. if( m_board && !m_board->IsLayerEnabled( aLayer ) )
  178. return false;
  179. // see if layer needs to be shown
  180. // check the flags
  181. switch( aLayer )
  182. {
  183. case B_Adhes:
  184. case F_Adhes:
  185. return m_Cfg->m_Render.show_adhesive;
  186. case B_Paste:
  187. case F_Paste:
  188. return m_Cfg->m_Render.show_solderpaste;
  189. case B_SilkS:
  190. case F_SilkS:
  191. return m_Cfg->m_Render.show_silkscreen;
  192. case B_Mask:
  193. case F_Mask:
  194. return m_Cfg->m_Render.show_soldermask;
  195. case Dwgs_User:
  196. case Cmts_User:
  197. return !m_Cfg->m_Render.realistic && m_Cfg->m_Render.show_comments;
  198. case Eco1_User:
  199. case Eco2_User:
  200. return !m_Cfg->m_Render.realistic && m_Cfg->m_Render.show_eco;
  201. case Edge_Cuts:
  202. return !m_Cfg->m_Render.realistic && !m_Cfg->m_Render.show_board_body;
  203. case Margin:
  204. return !m_Cfg->m_Render.realistic;
  205. case B_Cu:
  206. case F_Cu:
  207. return !m_board || m_board->IsLayerVisible( aLayer ) || m_Cfg->m_Render.realistic;
  208. default:
  209. // the layer is an internal copper layer, used the visibility
  210. return m_board && m_board->IsLayerVisible( aLayer );
  211. }
  212. }
  213. bool BOARD_ADAPTER::IsFootprintShown( FOOTPRINT_ATTR_T aFPAttributes ) const
  214. {
  215. if( m_IsPreviewer ) // In panel Preview, footprints are always shown, of cource
  216. return true;
  217. if( aFPAttributes & FP_SMD )
  218. return m_Cfg->m_Render.show_footprints_insert;
  219. else if( aFPAttributes & FP_THROUGH_HOLE )
  220. return m_Cfg->m_Render.show_footprints_normal;
  221. else
  222. return m_Cfg->m_Render.show_footprints_virtual;
  223. }
  224. int BOARD_ADAPTER::GetHolePlatingThickness() const noexcept
  225. {
  226. return m_board ? m_board->GetDesignSettings().GetHolePlatingThickness()
  227. : DEFAULT_COPPER_THICKNESS;
  228. }
  229. unsigned int BOARD_ADAPTER::GetCircleSegmentCount( float aDiameter3DU ) const
  230. {
  231. wxASSERT( aDiameter3DU > 0.0f );
  232. return GetCircleSegmentCount( (int)( aDiameter3DU / m_biuTo3Dunits ) );
  233. }
  234. unsigned int BOARD_ADAPTER::GetCircleSegmentCount( int aDiameterBIU ) const
  235. {
  236. wxASSERT( aDiameterBIU > 0 );
  237. return GetArcToSegmentCount( aDiameterBIU / 2, ARC_HIGH_DEF, FULL_CIRCLE );
  238. }
  239. void BOARD_ADAPTER::InitSettings( REPORTER* aStatusReporter, REPORTER* aWarningReporter )
  240. {
  241. wxLogTrace( m_logTrace, wxT( "BOARD_ADAPTER::InitSettings" ) );
  242. if( aStatusReporter )
  243. aStatusReporter->Report( _( "Build board outline" ) );
  244. wxString msg;
  245. const bool haveOutline = createBoardPolygon( &msg );
  246. if( aWarningReporter )
  247. {
  248. if( !haveOutline )
  249. aWarningReporter->Report( msg, RPT_SEVERITY_WARNING );
  250. else
  251. aWarningReporter->Report( wxEmptyString );
  252. }
  253. EDA_RECT bbbox;
  254. if( m_board )
  255. {
  256. bbbox = m_board->ComputeBoundingBox( !m_board->IsFootprintHolder()
  257. && m_Cfg->m_Render.realistic
  258. && haveOutline );
  259. }
  260. // Gives a non null size to avoid issues in zoom / scale calculations
  261. if( ( bbbox.GetWidth() == 0 ) && ( bbbox.GetHeight() == 0 ) )
  262. bbbox.Inflate( Millimeter2iu( 10 ) );
  263. m_boardSize = bbbox.GetSize();
  264. m_boardPos = bbbox.Centre();
  265. wxASSERT( (m_boardSize.x > 0) && (m_boardSize.y > 0) );
  266. m_boardPos.y = -m_boardPos.y; // The y coord is inverted in 3D viewer
  267. m_copperLayersCount = m_board ? m_board->GetCopperLayerCount() : 2;
  268. // Ensure the board has 2 sides for 3D views, because it is hard to find
  269. // a *really* single side board in the true life...
  270. if( m_copperLayersCount < 2 )
  271. m_copperLayersCount = 2;
  272. // Calculate the conversion to apply to all positions.
  273. m_biuTo3Dunits = RANGE_SCALE_3D / std::max( m_boardSize.x, m_boardSize.y );
  274. m_boardBodyThickness3DU = DEFAULT_BOARD_THICKNESS * m_biuTo3Dunits;
  275. m_frontCopperThickness3DU = DEFAULT_COPPER_THICKNESS * m_biuTo3Dunits;
  276. m_backCopperThickness3DU = DEFAULT_COPPER_THICKNESS * m_biuTo3Dunits;
  277. m_nonCopperLayerThickness3DU = DEFAULT_TECH_LAYER_THICKNESS * m_biuTo3Dunits;
  278. m_solderPasteLayerThickness3DU = SOLDERPASTE_LAYER_THICKNESS * m_biuTo3Dunits;
  279. if( m_board )
  280. {
  281. const BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
  282. if( bds.GetStackupDescriptor().GetCount() )
  283. {
  284. int thickness = 0;
  285. for( BOARD_STACKUP_ITEM* item : bds.GetStackupDescriptor().GetList() )
  286. {
  287. switch( item->GetType() )
  288. {
  289. case BS_ITEM_TYPE_DIELECTRIC:
  290. thickness += item->GetThickness();
  291. break;
  292. case BS_ITEM_TYPE_COPPER:
  293. if( item->GetBrdLayerId() == F_Cu )
  294. m_frontCopperThickness3DU = item->GetThickness() * m_biuTo3Dunits;
  295. else if( item->GetBrdLayerId() == B_Cu )
  296. m_backCopperThickness3DU = item->GetThickness() * m_biuTo3Dunits;
  297. else if( item->IsEnabled() )
  298. thickness += item->GetThickness();
  299. break;
  300. default:
  301. break;
  302. }
  303. }
  304. m_boardBodyThickness3DU = thickness * m_biuTo3Dunits;
  305. }
  306. }
  307. // Init Z position of each layer
  308. // calculate z position for each copper layer
  309. // Zstart = -m_epoxyThickness / 2.0 is the z position of the back (bottom layer) (layer id = 31)
  310. // Zstart = +m_epoxyThickness / 2.0 is the z position of the front (top layer) (layer id = 0)
  311. // ____==__________==________==______ <- Bottom = +m_epoxyThickness / 2.0,
  312. // | | Top = Bottom + m_copperThickness
  313. // |__________________________________|
  314. // == == == == <- Bottom = -m_epoxyThickness / 2.0,
  315. // Top = Bottom - m_copperThickness
  316. unsigned int layer;
  317. for( layer = 0; layer < m_copperLayersCount; ++layer )
  318. {
  319. // This approximates internal layer positions (because we're treating all the dielectric
  320. // layers as having the same thickness). But we don't render them anyway so it doesn't
  321. // really matter.
  322. m_layerZcoordBottom[layer] = m_boardBodyThickness3DU / 2.0f -
  323. (m_boardBodyThickness3DU * layer / (m_copperLayersCount - 1) );
  324. if( layer < (m_copperLayersCount / 2) )
  325. m_layerZcoordTop[layer] = m_layerZcoordBottom[layer] + m_frontCopperThickness3DU;
  326. else
  327. m_layerZcoordTop[layer] = m_layerZcoordBottom[layer] - m_backCopperThickness3DU;
  328. }
  329. #define layerThicknessMargin 1.1
  330. const float zpos_offset = m_nonCopperLayerThickness3DU * layerThicknessMargin;
  331. // Fill remaining unused copper layers and back layer zpos with -m_boardBodyThickness / 2.0
  332. for( ; layer < MAX_CU_LAYERS; layer++ )
  333. {
  334. m_layerZcoordBottom[layer] = -( m_boardBodyThickness3DU / 2.0f );
  335. m_layerZcoordTop[layer] = -( m_boardBodyThickness3DU / 2.0f ) - m_backCopperThickness3DU;
  336. }
  337. // This is the top of the copper layer thickness.
  338. const float zpos_copperTop_back = m_layerZcoordTop[B_Cu];
  339. const float zpos_copperTop_front = m_layerZcoordTop[F_Cu];
  340. // calculate z position for each non copper layer
  341. // Solder mask and Solder paste have the same Z position
  342. for( int layer_id = MAX_CU_LAYERS; layer_id < PCB_LAYER_ID_COUNT; ++layer_id )
  343. {
  344. float zposTop;
  345. float zposBottom;
  346. switch( layer_id )
  347. {
  348. case B_Adhes:
  349. zposBottom = zpos_copperTop_back - 2.0f * zpos_offset;
  350. zposTop = zposBottom - m_nonCopperLayerThickness3DU;
  351. break;
  352. case F_Adhes:
  353. zposBottom = zpos_copperTop_front + 2.0f * zpos_offset;
  354. zposTop = zposBottom + m_nonCopperLayerThickness3DU;
  355. break;
  356. case B_Mask:
  357. zposBottom = zpos_copperTop_back;
  358. zposTop = zpos_copperTop_back - m_nonCopperLayerThickness3DU;
  359. break;
  360. case B_Paste:
  361. zposBottom = zpos_copperTop_back;
  362. zposTop = zpos_copperTop_back - m_solderPasteLayerThickness3DU;
  363. break;
  364. case F_Mask:
  365. zposBottom = zpos_copperTop_front;
  366. zposTop = zpos_copperTop_front + m_nonCopperLayerThickness3DU;
  367. break;
  368. case F_Paste:
  369. zposBottom = zpos_copperTop_front;
  370. zposTop = zpos_copperTop_front + m_solderPasteLayerThickness3DU;
  371. break;
  372. case B_SilkS:
  373. zposBottom = zpos_copperTop_back - 1.0f * zpos_offset;
  374. zposTop = zposBottom - m_nonCopperLayerThickness3DU;
  375. break;
  376. case F_SilkS:
  377. zposBottom = zpos_copperTop_front + 1.0f * zpos_offset;
  378. zposTop = zposBottom + m_nonCopperLayerThickness3DU;
  379. break;
  380. // !TODO: review
  381. default:
  382. zposTop = zpos_copperTop_front + (layer_id - MAX_CU_LAYERS + 3.0f) * zpos_offset;
  383. zposBottom = zposTop - m_nonCopperLayerThickness3DU;
  384. break;
  385. }
  386. m_layerZcoordTop[layer_id] = zposTop;
  387. m_layerZcoordBottom[layer_id] = zposBottom;
  388. }
  389. m_boardCenter = SFVEC3F( m_boardPos.x * m_biuTo3Dunits, m_boardPos.y * m_biuTo3Dunits, 0.0f );
  390. SFVEC3F boardSize = SFVEC3F( m_boardSize.x * m_biuTo3Dunits, m_boardSize.y * m_biuTo3Dunits,
  391. 0.0f );
  392. boardSize /= 2.0f;
  393. SFVEC3F boardMin = ( m_boardCenter - boardSize );
  394. SFVEC3F boardMax = ( m_boardCenter + boardSize );
  395. boardMin.z = m_layerZcoordTop[B_Adhes];
  396. boardMax.z = m_layerZcoordTop[F_Adhes];
  397. m_boardBoundingBox = BBOX_3D( boardMin, boardMax );
  398. #ifdef PRINT_STATISTICS_3D_VIEWER
  399. unsigned stats_startCreateBoardPolyTime = GetRunningMicroSecs();
  400. #endif
  401. if( aStatusReporter )
  402. aStatusReporter->Report( _( "Create layers" ) );
  403. createLayers( aStatusReporter );
  404. COLOR_SETTINGS* colors = Pgm().GetSettingsManager().GetColorSettings();
  405. auto to_SFVEC4F =
  406. []( const COLOR4D& src )
  407. {
  408. return SFVEC4F( src.r, src.g, src.b, src.a );
  409. };
  410. m_BgColorTop = to_SFVEC4F( colors->GetColor( LAYER_3D_BACKGROUND_TOP ) );
  411. m_BgColorBot = to_SFVEC4F( colors->GetColor( LAYER_3D_BACKGROUND_BOTTOM ) );
  412. m_SolderPasteColor = to_SFVEC4F( colors->GetColor( LAYER_3D_SOLDERPASTE ) );
  413. if( m_board && colors->GetUseBoardStackupColors() )
  414. {
  415. const BOARD_STACKUP& stackup = m_board->GetDesignSettings().GetStackupDescriptor();
  416. auto findColor =
  417. []( const wxString& aColorName, const CUSTOM_COLORS_LIST& aColorSet )
  418. {
  419. if( aColorName.StartsWith( wxT( "#" ) ) )
  420. {
  421. return KIGFX::COLOR4D( aColorName );
  422. }
  423. else
  424. {
  425. for( const CUSTOM_COLOR_ITEM& color : aColorSet )
  426. {
  427. if( color.m_ColorName == aColorName )
  428. return color.m_Color;
  429. }
  430. }
  431. return KIGFX::COLOR4D();
  432. };
  433. m_SilkScreenColorTop = to_SFVEC4F( g_DefaultSilkscreen );
  434. m_SilkScreenColorBot = to_SFVEC4F( g_DefaultSilkscreen );
  435. m_SolderMaskColorTop = to_SFVEC4F( g_DefaultSolderMask );
  436. m_SolderMaskColorBot = to_SFVEC4F( g_DefaultSolderMask );
  437. KIGFX::COLOR4D bodyColor( 0, 0, 0, 0 );
  438. for( const BOARD_STACKUP_ITEM* stackupItem : stackup.GetList() )
  439. {
  440. wxString colorName = stackupItem->GetColor();
  441. switch( stackupItem->GetType() )
  442. {
  443. case BS_ITEM_TYPE_SILKSCREEN:
  444. if( stackupItem->GetBrdLayerId() == F_SilkS )
  445. m_SilkScreenColorTop = to_SFVEC4F( findColor( colorName, g_SilkscreenColors ) );
  446. else
  447. m_SilkScreenColorBot = to_SFVEC4F( findColor( colorName, g_SilkscreenColors ) );
  448. break;
  449. case BS_ITEM_TYPE_SOLDERMASK:
  450. if( stackupItem->GetBrdLayerId() == F_Mask )
  451. m_SolderMaskColorTop = to_SFVEC4F( findColor( colorName, g_MaskColors ) );
  452. else
  453. m_SolderMaskColorBot = to_SFVEC4F( findColor( colorName, g_MaskColors ) );
  454. break;
  455. case BS_ITEM_TYPE_DIELECTRIC:
  456. {
  457. KIGFX::COLOR4D layerColor = findColor( colorName, g_BoardColors );
  458. if( bodyColor == COLOR4D( 0, 0, 0, 0 ) )
  459. bodyColor = layerColor;
  460. else
  461. bodyColor = bodyColor.Mix( layerColor, 1.0 - layerColor.a );
  462. bodyColor.a += ( 1.0 - bodyColor.a ) * layerColor.a / 2;
  463. break;
  464. }
  465. default:
  466. break;
  467. }
  468. }
  469. if( bodyColor != COLOR4D( 0, 0, 0, 0 ) )
  470. m_BoardBodyColor = to_SFVEC4F( bodyColor );
  471. else
  472. m_BoardBodyColor = to_SFVEC4F( g_DefaultBoardBody );
  473. const wxString& finishName = stackup.m_FinishType;
  474. if( finishName.EndsWith( wxT( "OSP" ) ) )
  475. {
  476. m_CopperColor = to_SFVEC4F( findColor( wxT( "Copper" ), g_FinishColors ) );
  477. }
  478. else if( finishName.EndsWith( wxT( "IG" ) )
  479. || finishName.EndsWith( wxT( "gold" ) ) )
  480. {
  481. m_CopperColor = to_SFVEC4F( findColor( wxT( "Gold" ), g_FinishColors ) );
  482. }
  483. else if( finishName.StartsWith( wxT( "HAL" ) )
  484. || finishName.StartsWith( wxT( "HASL" ) )
  485. || finishName.EndsWith( wxT( "tin" ) )
  486. || finishName.EndsWith( wxT( "nickel" ) ) )
  487. {
  488. m_CopperColor = to_SFVEC4F( findColor( wxT( "Tin" ), g_FinishColors ) );
  489. }
  490. else if( finishName.EndsWith( wxT( "silver" ) ) )
  491. {
  492. m_CopperColor = to_SFVEC4F( findColor( wxT( "Silver" ), g_FinishColors ) );
  493. }
  494. else
  495. {
  496. m_CopperColor = to_SFVEC4F( g_DefaultSurfaceFinish );
  497. }
  498. }
  499. else
  500. {
  501. m_SilkScreenColorBot = to_SFVEC4F( colors->GetColor( LAYER_3D_SILKSCREEN_BOTTOM ) );
  502. m_SilkScreenColorTop = to_SFVEC4F( colors->GetColor( LAYER_3D_SILKSCREEN_TOP ) );
  503. m_SolderMaskColorBot = to_SFVEC4F( colors->GetColor( LAYER_3D_SOLDERMASK_BOTTOM ) );
  504. m_SolderMaskColorTop = to_SFVEC4F( colors->GetColor( LAYER_3D_SOLDERMASK_TOP ) );
  505. m_CopperColor = to_SFVEC4F( colors->GetColor( LAYER_3D_COPPER ) );
  506. m_BoardBodyColor = to_SFVEC4F( colors->GetColor( LAYER_3D_BOARD ) );
  507. }
  508. }
  509. extern bool BuildFootprintPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines,
  510. int aErrorMax, int aChainingEpsilon,
  511. OUTLINE_ERROR_HANDLER* aErrorHandler = nullptr );
  512. bool BOARD_ADAPTER::createBoardPolygon( wxString* aErrorMsg )
  513. {
  514. m_board_poly.RemoveAllContours();
  515. if( !m_board )
  516. return false;
  517. bool success;
  518. if( m_board->IsFootprintHolder() )
  519. {
  520. if( !m_board->GetFirstFootprint() )
  521. {
  522. if( aErrorMsg )
  523. *aErrorMsg = _( "No footprint loaded." );
  524. return false;
  525. }
  526. int chainingEpsilon = Millimeter2iu( 0.02 ); // max dist from one endPt to next startPt
  527. success = BuildFootprintPolygonOutlines( m_board, m_board_poly,
  528. m_board->GetDesignSettings().m_MaxError,
  529. chainingEpsilon );
  530. // Make polygon strictly simple to avoid issues (especially in 3D viewer)
  531. m_board_poly.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
  532. if( !success && aErrorMsg )
  533. {
  534. *aErrorMsg = _( "Footprint outline is missing or malformed. Run Footprint Checker for "
  535. "a full analysis." );
  536. }
  537. }
  538. else
  539. {
  540. success = m_board->GetBoardPolygonOutlines( m_board_poly );
  541. if( !success && aErrorMsg )
  542. *aErrorMsg = _( "Board outline is missing or malformed. Run DRC for a full analysis." );
  543. }
  544. return success;
  545. }
  546. float BOARD_ADAPTER::GetFootprintZPos( bool aIsFlipped ) const
  547. {
  548. if( aIsFlipped )
  549. {
  550. if( m_Cfg->m_Render.show_solderpaste )
  551. return m_layerZcoordBottom[B_SilkS];
  552. else
  553. return m_layerZcoordBottom[B_Paste];
  554. }
  555. else
  556. {
  557. if( m_Cfg->m_Render.show_solderpaste )
  558. return m_layerZcoordTop[F_SilkS];
  559. else
  560. return m_layerZcoordTop[F_Paste];
  561. }
  562. }
  563. SFVEC4F BOARD_ADAPTER::GetLayerColor( PCB_LAYER_ID aLayerId ) const
  564. {
  565. wxASSERT( aLayerId < PCB_LAYER_ID_COUNT );
  566. const COLOR4D color = m_colors->GetColor( aLayerId );
  567. return SFVEC4F( color.r, color.g, color.b, color.a );
  568. }
  569. SFVEC4F BOARD_ADAPTER::GetItemColor( int aItemId ) const
  570. {
  571. return GetColor( m_colors->GetColor( aItemId ) );
  572. }
  573. SFVEC4F BOARD_ADAPTER::GetColor( const COLOR4D& aColor ) const
  574. {
  575. return SFVEC4F( aColor.r, aColor.g, aColor.b, aColor.a );
  576. }
  577. SFVEC2F BOARD_ADAPTER::GetSphericalCoord( int i ) const
  578. {
  579. SFVEC2F sphericalCoord =
  580. SFVEC2F( ( m_Cfg->m_Render.raytrace_lightElevation[i] + 90.0f ) / 180.0f,
  581. m_Cfg->m_Render.raytrace_lightAzimuth[i] / 180.0f );
  582. sphericalCoord.x = glm::clamp( sphericalCoord.x, 0.0f, 1.0f );
  583. sphericalCoord.y = glm::clamp( sphericalCoord.y, 0.0f, 2.0f );
  584. return sphericalCoord;
  585. }