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.

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