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.

1246 lines
45 KiB

  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) 2023 CERN
  6. * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License
  10. * as published by the Free Software Foundation; either version 2
  11. * of the License, or (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, you may find one here:
  20. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  21. * or you may search the http://www.gnu.org website for the version 2 license,
  22. * or you may write to the Free Software Foundation, Inc.,
  23. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  24. */
  25. /**
  26. * @file create_layer_items.cpp
  27. * @brief This file implements the creation of the pcb board.
  28. *
  29. * It is based on the function found in the files:
  30. * board_items_to_polygon_shape_transform.cpp
  31. * board_items_to_polygon_shape_transform.cpp
  32. */
  33. #include "board_adapter.h"
  34. #include "../3d_rendering/raytracing/shapes2D/filled_circle_2d.h"
  35. #include "raytracing/shapes2D/triangle_2d.h"
  36. #include <board_design_settings.h>
  37. #include <board.h>
  38. #include <footprint.h>
  39. #include <layer_range.h>
  40. #include <lset.h>
  41. #include <pad.h>
  42. #include <pcb_text.h>
  43. #include <pcb_textbox.h>
  44. #include <pcb_table.h>
  45. #include <pcb_shape.h>
  46. #include <zone.h>
  47. #include <convert_basic_shapes_to_polygon.h>
  48. #include <trigo.h>
  49. #include <vector>
  50. #include <thread>
  51. #include <core/arraydim.h>
  52. #include <algorithm>
  53. #include <atomic>
  54. #include <wx/log.h>
  55. #ifdef PRINT_STATISTICS_3D_VIEWER
  56. #include <core/profile.h>
  57. #endif
  58. /*
  59. * This is used to draw pad outlines on silk layers.
  60. */
  61. void buildPadOutlineAsPolygon( const PAD* aPad, PCB_LAYER_ID aLayer, SHAPE_POLY_SET& aBuffer,
  62. int aWidth, int aMaxError, ERROR_LOC aErrorLoc )
  63. {
  64. if( aPad->GetShape( aLayer ) == PAD_SHAPE::CIRCLE ) // Draw a ring
  65. {
  66. TransformRingToPolygon( aBuffer, aPad->ShapePos( aLayer ), aPad->GetSize( aLayer ).x / 2,
  67. aWidth, aMaxError, aErrorLoc );
  68. }
  69. else
  70. {
  71. // For other shapes, add outlines as thick segments in polygon buffer
  72. const SHAPE_LINE_CHAIN& path = aPad->GetEffectivePolygon( aLayer, ERROR_INSIDE )->COutline( 0 );
  73. for( int ii = 0; ii < path.PointCount(); ++ii )
  74. {
  75. const VECTOR2I& a = path.CPoint( ii );
  76. const VECTOR2I& b = path.CPoint( ii + 1 );
  77. TransformOvalToPolygon( aBuffer, a, b, aWidth, aMaxError, aErrorLoc );
  78. }
  79. }
  80. }
  81. void transformFPShapesToPolySet( const FOOTPRINT* aFootprint, PCB_LAYER_ID aLayer,
  82. SHAPE_POLY_SET& aBuffer, int aMaxError, ERROR_LOC aErrorLoc )
  83. {
  84. for( BOARD_ITEM* item : aFootprint->GraphicalItems() )
  85. {
  86. if( item->Type() == PCB_SHAPE_T || BaseType( item->Type() ) == PCB_DIMENSION_T )
  87. {
  88. if( item->GetLayer() == aLayer )
  89. item->TransformShapeToPolygon( aBuffer, aLayer, 0, aMaxError, aErrorLoc );
  90. }
  91. }
  92. }
  93. void BOARD_ADAPTER::destroyLayers()
  94. {
  95. #define DELETE_AND_FREE( ptr ) \
  96. { \
  97. delete ptr; \
  98. ptr = nullptr; \
  99. } \
  100. #define DELETE_AND_FREE_MAP( map ) \
  101. { \
  102. for( auto& [ layer, poly ] : map ) \
  103. delete poly; \
  104. \
  105. map.clear(); \
  106. }
  107. DELETE_AND_FREE_MAP( m_layers_poly );
  108. DELETE_AND_FREE( m_frontPlatedPadAndGraphicPolys )
  109. DELETE_AND_FREE( m_backPlatedPadAndGraphicPolys )
  110. DELETE_AND_FREE( m_frontPlatedCopperPolys )
  111. DELETE_AND_FREE( m_backPlatedCopperPolys )
  112. DELETE_AND_FREE_MAP( m_layerHoleOdPolys )
  113. DELETE_AND_FREE_MAP( m_layerHoleIdPolys )
  114. m_NPTH_ODPolys.RemoveAllContours();
  115. m_TH_ODPolys.RemoveAllContours();
  116. m_viaTH_ODPolys.RemoveAllContours();
  117. m_viaAnnuliPolys.RemoveAllContours();
  118. DELETE_AND_FREE_MAP( m_layerMap )
  119. DELETE_AND_FREE_MAP( m_layerHoleMap )
  120. DELETE_AND_FREE( m_platedPadsFront )
  121. DELETE_AND_FREE( m_platedPadsBack )
  122. DELETE_AND_FREE( m_offboardPadsFront )
  123. DELETE_AND_FREE( m_offboardPadsBack )
  124. m_TH_ODs.Clear();
  125. m_TH_IDs.Clear();
  126. m_viaAnnuli.Clear();
  127. m_viaTH_ODs.Clear();
  128. }
  129. void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
  130. {
  131. destroyLayers();
  132. // Build Copper layers
  133. // Based on:
  134. // https://github.com/KiCad/kicad-source-mirror/blob/master/3d-viewer/3d_draw.cpp#L692
  135. #ifdef PRINT_STATISTICS_3D_VIEWER
  136. int64_t stats_startCopperLayersTime = GetRunningMicroSecs();
  137. int64_t start_Time = stats_startCopperLayersTime;
  138. #endif
  139. EDA_3D_VIEWER_SETTINGS::RENDER_SETTINGS& cfg = m_Cfg->m_Render;
  140. std::bitset<LAYER_3D_END> visibilityFlags = GetVisibleLayers();
  141. m_trackCount = 0;
  142. m_averageTrackWidth = 0;
  143. m_viaCount = 0;
  144. m_averageViaHoleDiameter = 0;
  145. m_holeCount = 0;
  146. m_averageHoleDiameter = 0;
  147. if( !m_board )
  148. return;
  149. // Prepare track list, convert in a vector. Calc statistic for the holes
  150. std::vector<const PCB_TRACK*> trackList;
  151. trackList.clear();
  152. trackList.reserve( m_board->Tracks().size() );
  153. int maxError = m_board->GetDesignSettings().m_MaxError;
  154. for( PCB_TRACK* track : m_board->Tracks() )
  155. {
  156. // Skip tracks (not vias theyt are on more than one layer ) on disabled layers
  157. if( track->Type() != PCB_VIA_T
  158. && !Is3dLayerEnabled( track->GetLayer(), visibilityFlags ) )
  159. {
  160. continue;
  161. }
  162. // Note: a PCB_TRACK holds normal segment tracks and also vias circles (that have also
  163. // drill values)
  164. trackList.push_back( track );
  165. if( track->Type() == PCB_VIA_T )
  166. {
  167. const PCB_VIA *via = static_cast< const PCB_VIA*>( track );
  168. m_viaCount++;
  169. m_averageViaHoleDiameter += static_cast<float>( via->GetDrillValue() * m_biuTo3Dunits );
  170. }
  171. else
  172. {
  173. m_trackCount++;
  174. m_averageTrackWidth += static_cast<float>( track->GetWidth() * m_biuTo3Dunits );
  175. }
  176. }
  177. if( m_trackCount )
  178. m_averageTrackWidth /= (float)m_trackCount;
  179. if( m_viaCount )
  180. m_averageViaHoleDiameter /= (float)m_viaCount;
  181. // Prepare copper layers index and containers
  182. std::vector<PCB_LAYER_ID> layer_ids;
  183. layer_ids.clear();
  184. layer_ids.reserve( m_copperLayersCount );
  185. for( PCB_LAYER_ID layer : LAYER_RANGE( F_Cu,B_Cu, m_copperLayersCount) )
  186. {
  187. if( !Is3dLayerEnabled( layer, visibilityFlags ) ) // Skip non enabled layers
  188. continue;
  189. layer_ids.push_back( layer );
  190. BVH_CONTAINER_2D *layerContainer = new BVH_CONTAINER_2D;
  191. m_layerMap[layer] = layerContainer;
  192. if( cfg.opengl_copper_thickness && cfg.engine == RENDER_ENGINE::OPENGL )
  193. {
  194. SHAPE_POLY_SET* layerPoly = new SHAPE_POLY_SET;
  195. m_layers_poly[layer] = layerPoly;
  196. }
  197. }
  198. if( cfg.DifferentiatePlatedCopper() )
  199. {
  200. m_frontPlatedPadAndGraphicPolys = new SHAPE_POLY_SET;
  201. m_backPlatedPadAndGraphicPolys = new SHAPE_POLY_SET;
  202. m_frontPlatedCopperPolys = new SHAPE_POLY_SET;
  203. m_backPlatedCopperPolys = new SHAPE_POLY_SET;
  204. m_platedPadsFront = new BVH_CONTAINER_2D;
  205. m_platedPadsBack = new BVH_CONTAINER_2D;
  206. }
  207. if( cfg.show_off_board_silk )
  208. {
  209. m_offboardPadsFront = new BVH_CONTAINER_2D;
  210. m_offboardPadsBack = new BVH_CONTAINER_2D;
  211. }
  212. if( aStatusReporter )
  213. aStatusReporter->Report( _( "Create tracks and vias" ) );
  214. // Create tracks as objects and add it to container
  215. for( PCB_LAYER_ID layer : layer_ids )
  216. {
  217. wxASSERT( m_layerMap.find( layer ) != m_layerMap.end() );
  218. BVH_CONTAINER_2D *layerContainer = m_layerMap[layer];
  219. for( const PCB_TRACK* track : trackList )
  220. {
  221. // NOTE: Vias can be on multiple layers
  222. if( !track->IsOnLayer( layer ) )
  223. continue;
  224. // Skip vias annulus when not flashed on this layer
  225. if( track->Type() == PCB_VIA_T
  226. && !static_cast<const PCB_VIA*>( track )->FlashLayer( layer ) )
  227. {
  228. continue;
  229. }
  230. // Add object item to layer container
  231. createTrackWithMargin( track, layerContainer, layer );
  232. }
  233. }
  234. // Create VIAS and THTs objects and add it to holes containers
  235. for( PCB_LAYER_ID layer : layer_ids )
  236. {
  237. // ADD TRACKS
  238. unsigned int nTracks = trackList.size();
  239. for( unsigned int trackIdx = 0; trackIdx < nTracks; ++trackIdx )
  240. {
  241. const PCB_TRACK *track = trackList[trackIdx];
  242. if( !track->IsOnLayer( layer ) )
  243. continue;
  244. // ADD VIAS and THT
  245. if( track->Type() == PCB_VIA_T )
  246. {
  247. const PCB_VIA* via = static_cast<const PCB_VIA*>( track );
  248. const VIATYPE viatype = via->GetViaType();
  249. const double holediameter = via->GetDrillValue() * BiuTo3dUnits();
  250. const double viasize = via->GetWidth( layer ) * BiuTo3dUnits();
  251. const double plating = GetHolePlatingThickness() * BiuTo3dUnits();
  252. // holes and layer copper extend half info cylinder wall to hide transition
  253. const float thickness = static_cast<float>( plating / 2.0f );
  254. const float hole_inner_radius = static_cast<float>( holediameter / 2.0f );
  255. const float ring_radius = static_cast<float>( viasize / 2.0f );
  256. const SFVEC2F via_center( via->GetStart().x * m_biuTo3Dunits,
  257. -via->GetStart().y * m_biuTo3Dunits );
  258. if( viatype != VIATYPE::THROUGH )
  259. {
  260. // Add hole objects
  261. BVH_CONTAINER_2D *layerHoleContainer = nullptr;
  262. // Check if the layer is already created
  263. if( m_layerHoleMap.find( layer ) == m_layerHoleMap.end() )
  264. {
  265. // not found, create a new container
  266. layerHoleContainer = new BVH_CONTAINER_2D;
  267. m_layerHoleMap[layer] = layerHoleContainer;
  268. }
  269. else
  270. {
  271. // found
  272. layerHoleContainer = m_layerHoleMap[layer];
  273. }
  274. // Add a hole for this layer
  275. layerHoleContainer->Add( new FILLED_CIRCLE_2D( via_center,
  276. hole_inner_radius + thickness,
  277. *track ) );
  278. }
  279. else if( layer == layer_ids[0] ) // it only adds once the THT holes
  280. {
  281. // Add through hole object
  282. m_TH_ODs.Add( new FILLED_CIRCLE_2D( via_center, hole_inner_radius + thickness,
  283. *track ) );
  284. m_viaTH_ODs.Add( new FILLED_CIRCLE_2D( via_center, hole_inner_radius + thickness,
  285. *track ) );
  286. if( cfg.clip_silk_on_via_annuli && ring_radius > 0.0 )
  287. m_viaAnnuli.Add( new FILLED_CIRCLE_2D( via_center, ring_radius, *track ) );
  288. if( hole_inner_radius > 0.0 )
  289. m_TH_IDs.Add( new FILLED_CIRCLE_2D( via_center, hole_inner_radius, *track ) );
  290. }
  291. }
  292. if( cfg.DifferentiatePlatedCopper() && layer == F_Cu )
  293. {
  294. track->TransformShapeToPolygon( *m_frontPlatedCopperPolys, F_Cu, 0, maxError,
  295. ERROR_INSIDE );
  296. }
  297. else if( cfg.DifferentiatePlatedCopper() && layer == B_Cu )
  298. {
  299. track->TransformShapeToPolygon( *m_backPlatedCopperPolys, B_Cu, 0, maxError,
  300. ERROR_INSIDE );
  301. }
  302. }
  303. }
  304. // Create VIAS and THTs objects and add it to holes containers
  305. for( PCB_LAYER_ID layer : layer_ids )
  306. {
  307. // ADD TRACKS
  308. const unsigned int nTracks = trackList.size();
  309. for( unsigned int trackIdx = 0; trackIdx < nTracks; ++trackIdx )
  310. {
  311. const PCB_TRACK *track = trackList[trackIdx];
  312. if( !track->IsOnLayer( layer ) )
  313. continue;
  314. // ADD VIAS and THT
  315. if( track->Type() == PCB_VIA_T )
  316. {
  317. const PCB_VIA* via = static_cast<const PCB_VIA*>( track );
  318. const VIATYPE viatype = via->GetViaType();
  319. if( viatype != VIATYPE::THROUGH )
  320. {
  321. // Add PCB_VIA hole contours
  322. // Add outer holes of VIAs
  323. SHAPE_POLY_SET *layerOuterHolesPoly = nullptr;
  324. SHAPE_POLY_SET *layerInnerHolesPoly = nullptr;
  325. // Check if the layer is already created
  326. if( m_layerHoleOdPolys.find( layer ) == m_layerHoleOdPolys.end() )
  327. {
  328. // not found, create a new container
  329. layerOuterHolesPoly = new SHAPE_POLY_SET;
  330. m_layerHoleOdPolys[layer] = layerOuterHolesPoly;
  331. wxASSERT( m_layerHoleIdPolys.find( layer ) == m_layerHoleIdPolys.end() );
  332. layerInnerHolesPoly = new SHAPE_POLY_SET;
  333. m_layerHoleIdPolys[layer] = layerInnerHolesPoly;
  334. }
  335. else
  336. {
  337. // found
  338. layerOuterHolesPoly = m_layerHoleOdPolys[layer];
  339. wxASSERT( m_layerHoleIdPolys.find( layer ) != m_layerHoleIdPolys.end() );
  340. layerInnerHolesPoly = m_layerHoleIdPolys[layer];
  341. }
  342. const int holediameter = via->GetDrillValue();
  343. const int hole_outer_radius = (holediameter / 2) + GetHolePlatingThickness();
  344. TransformCircleToPolygon( *layerOuterHolesPoly, via->GetStart(),
  345. hole_outer_radius, maxError, ERROR_INSIDE );
  346. TransformCircleToPolygon( *layerInnerHolesPoly, via->GetStart(),
  347. holediameter / 2, maxError, ERROR_INSIDE );
  348. }
  349. else if( layer == layer_ids[0] ) // it only adds once the THT holes
  350. {
  351. const int holediameter = via->GetDrillValue();
  352. const int hole_outer_radius = (holediameter / 2) + GetHolePlatingThickness();
  353. const int hole_outer_ring_radius = KiROUND( via->GetWidth( layer ) / 2.0 );
  354. // Add through hole contours
  355. TransformCircleToPolygon( m_TH_ODPolys, via->GetStart(), hole_outer_radius,
  356. maxError, ERROR_INSIDE );
  357. // Add same thing for vias only
  358. TransformCircleToPolygon( m_viaTH_ODPolys, via->GetStart(), hole_outer_radius,
  359. maxError, ERROR_INSIDE );
  360. if( cfg.clip_silk_on_via_annuli )
  361. {
  362. TransformCircleToPolygon( m_viaAnnuliPolys, via->GetStart(),
  363. hole_outer_ring_radius, maxError, ERROR_INSIDE );
  364. }
  365. }
  366. }
  367. }
  368. }
  369. // Creates vertical outline contours of the tracks and add it to the poly of the layer
  370. if( cfg.opengl_copper_thickness && cfg.engine == RENDER_ENGINE::OPENGL )
  371. {
  372. for( PCB_LAYER_ID layer : layer_ids )
  373. {
  374. wxASSERT( m_layers_poly.find( layer ) != m_layers_poly.end() );
  375. SHAPE_POLY_SET *layerPoly = m_layers_poly[layer];
  376. // ADD TRACKS
  377. unsigned int nTracks = trackList.size();
  378. for( unsigned int trackIdx = 0; trackIdx < nTracks; ++trackIdx )
  379. {
  380. const PCB_TRACK *track = trackList[trackIdx];
  381. if( !track->IsOnLayer( layer ) )
  382. continue;
  383. // Skip vias annulus when not flashed on this layer
  384. if( track->Type() == PCB_VIA_T
  385. && !static_cast<const PCB_VIA*>( track )->FlashLayer( layer ) )
  386. {
  387. continue;
  388. }
  389. // Add the track/via contour
  390. track->TransformShapeToPolygon( *layerPoly, layer, 0, maxError, ERROR_INSIDE );
  391. }
  392. }
  393. }
  394. // Add holes of footprints
  395. for( FOOTPRINT* footprint : m_board->Footprints() )
  396. {
  397. for( PAD* pad : footprint->Pads() )
  398. {
  399. const VECTOR2I padHole = pad->GetDrillSize();
  400. if( !padHole.x ) // Not drilled pad like SMD pad
  401. continue;
  402. // The hole in the body is inflated by copper thickness, if not plated, no copper
  403. int inflate = 0;
  404. if( pad->GetAttribute () != PAD_ATTRIB::NPTH )
  405. inflate = KiROUND( GetHolePlatingThickness() / 2.0 );
  406. m_holeCount++;
  407. double holeDiameter = ( pad->GetDrillSize().x + pad->GetDrillSize().y ) / 2.0;
  408. m_averageHoleDiameter += static_cast<float>( holeDiameter * m_biuTo3Dunits );
  409. createPadWithHole( pad, &m_TH_ODs, inflate );
  410. if( cfg.clip_silk_on_via_annuli )
  411. createPadWithHole( pad, &m_viaAnnuli, inflate );
  412. createPadWithHole( pad, &m_TH_IDs, 0 );
  413. }
  414. }
  415. if( m_holeCount )
  416. m_averageHoleDiameter /= (float)m_holeCount;
  417. // Add contours of the pad holes (pads can be Circle or Segment holes)
  418. for( FOOTPRINT* footprint : m_board->Footprints() )
  419. {
  420. for( PAD* pad : footprint->Pads() )
  421. {
  422. const VECTOR2I padHole = pad->GetDrillSize();
  423. if( !padHole.x ) // Not drilled pad like SMD pad
  424. continue;
  425. // The hole in the body is inflated by copper thickness.
  426. const int inflate = GetHolePlatingThickness();
  427. if( pad->GetAttribute () != PAD_ATTRIB::NPTH )
  428. {
  429. if( cfg.clip_silk_on_via_annuli )
  430. pad->TransformHoleToPolygon( m_viaAnnuliPolys, inflate, maxError, ERROR_INSIDE );
  431. pad->TransformHoleToPolygon( m_TH_ODPolys, inflate, maxError, ERROR_INSIDE );
  432. }
  433. else
  434. {
  435. // If not plated, no copper.
  436. if( cfg.clip_silk_on_via_annuli )
  437. pad->TransformHoleToPolygon( m_viaAnnuliPolys, 0, maxError, ERROR_INSIDE );
  438. pad->TransformHoleToPolygon( m_NPTH_ODPolys, 0, maxError, ERROR_INSIDE );
  439. }
  440. }
  441. }
  442. // Add footprints PADs objects to containers
  443. for( PCB_LAYER_ID layer : layer_ids )
  444. {
  445. wxASSERT( m_layerMap.find( layer ) != m_layerMap.end() );
  446. BVH_CONTAINER_2D *layerContainer = m_layerMap[layer];
  447. // ADD PADS
  448. for( FOOTPRINT* footprint : m_board->Footprints() )
  449. {
  450. addPads( footprint, layerContainer, layer, cfg.DifferentiatePlatedCopper(), false );
  451. // Micro-wave footprints may have items on copper layers
  452. addFootprintShapes( footprint, layerContainer, layer, visibilityFlags );
  453. }
  454. }
  455. // Add footprints PADs poly contours (vertical outlines)
  456. if( cfg.opengl_copper_thickness && cfg.engine == RENDER_ENGINE::OPENGL )
  457. {
  458. for( PCB_LAYER_ID layer : layer_ids )
  459. {
  460. wxASSERT( m_layers_poly.find( layer ) != m_layers_poly.end() );
  461. SHAPE_POLY_SET *layerPoly = m_layers_poly[layer];
  462. // Add pads to polygon list
  463. for( FOOTPRINT* footprint : m_board->Footprints() )
  464. {
  465. // Note: NPTH pads are not drawn on copper layers when the pad has same shape as
  466. // its hole
  467. footprint->TransformPadsToPolySet( *layerPoly, layer, 0, maxError, ERROR_INSIDE,
  468. true, cfg.DifferentiatePlatedCopper(), false );
  469. transformFPShapesToPolySet( footprint, layer, *layerPoly, maxError, ERROR_INSIDE );
  470. }
  471. }
  472. if( cfg.DifferentiatePlatedCopper() )
  473. {
  474. // ADD PLATED PADS contours
  475. for( FOOTPRINT* footprint : m_board->Footprints() )
  476. {
  477. footprint->TransformPadsToPolySet( *m_frontPlatedPadAndGraphicPolys, F_Cu, 0, maxError,
  478. ERROR_INSIDE, true, false, true );
  479. footprint->TransformPadsToPolySet( *m_backPlatedPadAndGraphicPolys, B_Cu, 0, maxError,
  480. ERROR_INSIDE, true, false, true );
  481. }
  482. }
  483. }
  484. // Add graphic item on copper layers to object containers
  485. for( PCB_LAYER_ID layer : layer_ids )
  486. {
  487. wxASSERT( m_layerMap.find( layer ) != m_layerMap.end() );
  488. BVH_CONTAINER_2D *layerContainer = m_layerMap[layer];
  489. // Add graphic items on copper layers (texts and other graphics)
  490. for( BOARD_ITEM* item : m_board->Drawings() )
  491. {
  492. if( !item->IsOnLayer( layer ) )
  493. continue;
  494. switch( item->Type() )
  495. {
  496. case PCB_SHAPE_T:
  497. addShape( static_cast<PCB_SHAPE*>( item ), layerContainer, item, layer );
  498. break;
  499. case PCB_TEXT_T:
  500. addText( static_cast<PCB_TEXT*>( item ), layerContainer, item );
  501. break;
  502. case PCB_TEXTBOX_T:
  503. addShape( static_cast<PCB_TEXTBOX*>( item ), layerContainer, item );
  504. break;
  505. case PCB_TABLE_T:
  506. addTable( static_cast<PCB_TABLE*>( item ), layerContainer, item );
  507. break;
  508. case PCB_DIM_ALIGNED_T:
  509. case PCB_DIM_CENTER_T:
  510. case PCB_DIM_RADIAL_T:
  511. case PCB_DIM_ORTHOGONAL_T:
  512. case PCB_DIM_LEADER_T:
  513. addShape( static_cast<PCB_DIMENSION_BASE*>( item ), layerContainer, item );
  514. break;
  515. default:
  516. wxLogTrace( m_logTrace, wxT( "createLayers: item type: %d not implemented" ),
  517. item->Type() );
  518. break;
  519. }
  520. // add also this shape to the plated copper polygon list if required
  521. if( cfg.DifferentiatePlatedCopper() )
  522. {
  523. // Note: for TEXT and TEXTBOX, TransformShapeToPolygon returns the bounding
  524. // box shape, not the exact text shape. So it is not used for these items
  525. if( layer == F_Cu || layer == B_Cu )
  526. {
  527. SHAPE_POLY_SET* platedCopperPolys = layer == F_Cu
  528. ? m_frontPlatedCopperPolys
  529. : m_backPlatedCopperPolys;
  530. if( item->Type() == PCB_TEXTBOX_T )
  531. {
  532. PCB_TEXTBOX* text_box = static_cast<PCB_TEXTBOX*>( item );
  533. text_box->TransformTextToPolySet( *platedCopperPolys,
  534. 0, maxError, ERROR_INSIDE );
  535. // Add box outlines
  536. text_box->PCB_SHAPE::TransformShapeToPolygon( *platedCopperPolys, layer,
  537. 0, maxError, ERROR_INSIDE );
  538. }
  539. else if( item->Type() == PCB_TEXT_T )
  540. {
  541. static_cast<PCB_TEXT*>( item )->TransformTextToPolySet(
  542. *platedCopperPolys,
  543. 0, maxError, ERROR_INSIDE );
  544. }
  545. else
  546. item->TransformShapeToPolygon( *m_frontPlatedCopperPolys, F_Cu,
  547. 0, maxError, ERROR_INSIDE );
  548. }
  549. }
  550. }
  551. }
  552. // Add graphic item on copper layers to poly contours (vertical outlines)
  553. if( cfg.opengl_copper_thickness && cfg.engine == RENDER_ENGINE::OPENGL )
  554. {
  555. for( PCB_LAYER_ID layer : layer_ids )
  556. {
  557. wxASSERT( m_layers_poly.find( layer ) != m_layers_poly.end() );
  558. SHAPE_POLY_SET *layerPoly = m_layers_poly[layer];
  559. // Add graphic items on copper layers (texts and other )
  560. for( BOARD_ITEM* item : m_board->Drawings() )
  561. {
  562. if( !item->IsOnLayer( layer ) )
  563. continue;
  564. switch( item->Type() )
  565. {
  566. case PCB_SHAPE_T:
  567. item->TransformShapeToPolygon( *layerPoly, layer, 0, maxError, ERROR_INSIDE );
  568. break;
  569. case PCB_TEXT_T:
  570. {
  571. PCB_TEXT* text = static_cast<PCB_TEXT*>( item );
  572. text->TransformTextToPolySet( *layerPoly, 0, maxError, ERROR_INSIDE );
  573. break;
  574. }
  575. case PCB_TEXTBOX_T:
  576. {
  577. PCB_TEXTBOX* textbox = static_cast<PCB_TEXTBOX*>( item );
  578. textbox->TransformTextToPolySet( *layerPoly, 0, maxError, ERROR_INSIDE );
  579. break;
  580. }
  581. case PCB_TABLE_T:
  582. // JEY TODO: tables
  583. break;
  584. default:
  585. wxLogTrace( m_logTrace, wxT( "createLayers: item type: %d not implemented" ),
  586. item->Type() );
  587. break;
  588. }
  589. }
  590. }
  591. }
  592. if( cfg.show_zones )
  593. {
  594. if( aStatusReporter )
  595. aStatusReporter->Report( _( "Create zones" ) );
  596. std::vector<std::pair<ZONE*, PCB_LAYER_ID>> zones;
  597. std::unordered_map<PCB_LAYER_ID, std::unique_ptr<std::mutex>> layer_lock;
  598. for( ZONE* zone : m_board->Zones() )
  599. {
  600. for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
  601. {
  602. zones.emplace_back( std::make_pair( zone, layer ) );
  603. layer_lock.emplace( layer, std::make_unique<std::mutex>() );
  604. if( cfg.DifferentiatePlatedCopper() && layer == F_Cu )
  605. {
  606. zone->TransformShapeToPolygon( *m_frontPlatedCopperPolys, F_Cu, 0, maxError,
  607. ERROR_INSIDE );
  608. }
  609. else if( cfg.DifferentiatePlatedCopper() && layer == B_Cu )
  610. {
  611. zone->TransformShapeToPolygon( *m_backPlatedCopperPolys, B_Cu, 0, maxError,
  612. ERROR_INSIDE );
  613. }
  614. }
  615. }
  616. // Add zones objects
  617. std::atomic<size_t> nextZone( 0 );
  618. std::atomic<size_t> threadsFinished( 0 );
  619. size_t parallelThreadCount = std::min<size_t>( zones.size(),
  620. std::max<size_t>( std::thread::hardware_concurrency(), 2 ) );
  621. for( size_t ii = 0; ii < parallelThreadCount; ++ii )
  622. {
  623. std::thread t = std::thread( [&]()
  624. {
  625. for( size_t areaId = nextZone.fetch_add( 1 );
  626. areaId < zones.size();
  627. areaId = nextZone.fetch_add( 1 ) )
  628. {
  629. ZONE* zone = zones[areaId].first;
  630. if( zone == nullptr )
  631. break;
  632. PCB_LAYER_ID layer = zones[areaId].second;
  633. auto layerContainer = m_layerMap.find( layer );
  634. auto layerPolyContainer = m_layers_poly.find( layer );
  635. if( layerContainer != m_layerMap.end() )
  636. addSolidAreasShapes( zone, layerContainer->second, layer );
  637. if( cfg.opengl_copper_thickness && cfg.engine == RENDER_ENGINE::OPENGL
  638. && layerPolyContainer != m_layers_poly.end() )
  639. {
  640. auto mut_it = layer_lock.find( layer );
  641. std::lock_guard< std::mutex > lock( *( mut_it->second ) );
  642. zone->TransformSolidAreasShapesToPolygon( layer, *layerPolyContainer->second );
  643. }
  644. }
  645. threadsFinished++;
  646. } );
  647. t.detach();
  648. }
  649. while( threadsFinished < parallelThreadCount )
  650. std::this_thread::sleep_for( std::chrono::milliseconds( 10 ) );
  651. }
  652. // End Build Copper layers
  653. // This will make a union of all added contours
  654. m_TH_ODPolys.Simplify();
  655. m_NPTH_ODPolys.Simplify();
  656. m_viaTH_ODPolys.Simplify();
  657. m_viaAnnuliPolys.Simplify();
  658. // Build Tech layers
  659. // Based on:
  660. // https://github.com/KiCad/kicad-source-mirror/blob/master/3d-viewer/3d_draw.cpp#L1059
  661. if( aStatusReporter )
  662. aStatusReporter->Report( _( "Build Tech layers" ) );
  663. // draw graphic items, on technical layers
  664. LSEQ techLayerList = LSET::AllNonCuMask().Seq( {
  665. B_Adhes,
  666. F_Adhes,
  667. B_Paste,
  668. F_Paste,
  669. B_SilkS,
  670. F_SilkS,
  671. B_Mask,
  672. F_Mask,
  673. // Aux Layers
  674. Dwgs_User,
  675. Cmts_User,
  676. Eco1_User,
  677. Eco2_User
  678. } );
  679. std::bitset<LAYER_3D_END> enabledFlags = visibilityFlags;
  680. if( cfg.subtract_mask_from_silk || cfg.DifferentiatePlatedCopper() )
  681. {
  682. enabledFlags.set( LAYER_3D_SOLDERMASK_TOP );
  683. enabledFlags.set( LAYER_3D_SOLDERMASK_BOTTOM );
  684. }
  685. for( PCB_LAYER_ID layer : techLayerList )
  686. {
  687. if( aStatusReporter )
  688. aStatusReporter->Report( wxString::Format( _( "Build Tech layer %d" ), (int) layer ) );
  689. if( !Is3dLayerEnabled( layer, enabledFlags ) )
  690. continue;
  691. BVH_CONTAINER_2D *layerContainer = new BVH_CONTAINER_2D;
  692. m_layerMap[layer] = layerContainer;
  693. SHAPE_POLY_SET *layerPoly = new SHAPE_POLY_SET;
  694. m_layers_poly[layer] = layerPoly;
  695. if( Is3dLayerEnabled( layer, visibilityFlags ) )
  696. {
  697. // Add drawing objects
  698. for( BOARD_ITEM* item : m_board->Drawings() )
  699. {
  700. if( !item->IsOnLayer( layer ) )
  701. continue;
  702. switch( item->Type() )
  703. {
  704. case PCB_SHAPE_T:
  705. addShape( static_cast<PCB_SHAPE*>( item ), layerContainer, item, layer );
  706. break;
  707. case PCB_TEXT_T:
  708. addText( static_cast<PCB_TEXT*>( item ), layerContainer, item );
  709. break;
  710. case PCB_TEXTBOX_T:
  711. addShape( static_cast<PCB_TEXTBOX*>( item ), layerContainer, item );
  712. break;
  713. case PCB_TABLE_T:
  714. // JEY TODO: tables
  715. break;
  716. case PCB_DIM_ALIGNED_T:
  717. case PCB_DIM_CENTER_T:
  718. case PCB_DIM_RADIAL_T:
  719. case PCB_DIM_ORTHOGONAL_T:
  720. case PCB_DIM_LEADER_T:
  721. addShape( static_cast<PCB_DIMENSION_BASE*>( item ), layerContainer, item );
  722. break;
  723. default:
  724. break;
  725. }
  726. }
  727. // Add track, via and arc tech layers
  728. if( IsSolderMaskLayer( layer ) )
  729. {
  730. for( PCB_TRACK* track : m_board->Tracks() )
  731. {
  732. if( !track->IsOnLayer( layer ) )
  733. continue;
  734. // Only vias on a external copper layer can have a solder mask
  735. PCB_LAYER_ID copper_layer = layer == F_Mask ? F_Cu : B_Cu;
  736. if( track->Type() == PCB_VIA_T
  737. && !static_cast<const PCB_VIA*>( track )->FlashLayer( copper_layer ) )
  738. continue;
  739. int maskExpansion = track->GetSolderMaskExpansion();
  740. createTrackWithMargin( track, layerContainer, layer, maskExpansion );
  741. }
  742. }
  743. // Add footprints tech layers - objects
  744. for( FOOTPRINT* footprint : m_board->Footprints() )
  745. {
  746. if( layer == F_SilkS || layer == B_SilkS )
  747. {
  748. int linewidth = m_board->GetDesignSettings().m_LineThickness[ LAYER_CLASS_SILK ];
  749. for( PAD* pad : footprint->Pads() )
  750. {
  751. if( !pad->IsOnLayer( layer ) )
  752. continue;
  753. buildPadOutlineAsSegments( pad, layer, layerContainer, linewidth );
  754. }
  755. }
  756. else
  757. {
  758. addPads( footprint, layerContainer, layer, false, false );
  759. }
  760. addFootprintShapes( footprint, layerContainer, layer, visibilityFlags );
  761. }
  762. // Draw non copper zones
  763. if( cfg.show_zones )
  764. {
  765. for( ZONE* zone : m_board->Zones() )
  766. {
  767. if( zone->IsOnLayer( layer ) )
  768. addSolidAreasShapes( zone, layerContainer, layer );
  769. }
  770. }
  771. }
  772. // Add item contours. We need these if we're building vertical walls or if this is a
  773. // mask layer and we're differentiating copper from plated copper.
  774. if( ( cfg.engine == RENDER_ENGINE::OPENGL && cfg.opengl_copper_thickness )
  775. || ( cfg.DifferentiatePlatedCopper() && ( layer == F_Mask || layer == B_Mask ) ) )
  776. {
  777. // DRAWINGS
  778. for( BOARD_ITEM* item : m_board->Drawings() )
  779. {
  780. if( !item->IsOnLayer( layer ) )
  781. continue;
  782. switch( item->Type() )
  783. {
  784. case PCB_SHAPE_T:
  785. item->TransformShapeToPolygon( *layerPoly, layer, 0, maxError, ERROR_INSIDE );
  786. break;
  787. case PCB_TEXT_T:
  788. {
  789. PCB_TEXT* text = static_cast<PCB_TEXT*>( item );
  790. text->TransformTextToPolySet( *layerPoly, 0, maxError, ERROR_INSIDE );
  791. break;
  792. }
  793. case PCB_TEXTBOX_T:
  794. {
  795. PCB_TEXTBOX* textbox = static_cast<PCB_TEXTBOX*>( item );
  796. textbox->TransformTextToPolySet( *layerPoly, 0, maxError, ERROR_INSIDE );
  797. break;
  798. }
  799. case PCB_TABLE_T:
  800. // JEY TODO: tables
  801. break;
  802. default:
  803. break;
  804. }
  805. }
  806. // NON-TENTED VIAS
  807. if( ( layer == F_Mask || layer == B_Mask ) )
  808. {
  809. int maskExpansion = GetBoard()->GetDesignSettings().m_SolderMaskExpansion;
  810. for( PCB_TRACK* track : m_board->Tracks() )
  811. {
  812. if( track->Type() == PCB_VIA_T
  813. && static_cast<const PCB_VIA*>( track )->FlashLayer( layer )
  814. && !static_cast<const PCB_VIA*>( track )->IsTented( layer ) )
  815. {
  816. track->TransformShapeToPolygon( *layerPoly, layer, maskExpansion, maxError,
  817. ERROR_INSIDE );
  818. }
  819. }
  820. }
  821. // FOOTPRINT CHILDREN
  822. for( FOOTPRINT* footprint : m_board->Footprints() )
  823. {
  824. if( layer == F_SilkS || layer == B_SilkS )
  825. {
  826. int linewidth = m_board->GetDesignSettings().m_LineThickness[ LAYER_CLASS_SILK ];
  827. for( PAD* pad : footprint->Pads() )
  828. {
  829. if( pad->IsOnLayer( layer ) )
  830. {
  831. buildPadOutlineAsPolygon( pad, layer, *layerPoly, linewidth, maxError,
  832. ERROR_INSIDE );
  833. }
  834. }
  835. }
  836. else
  837. {
  838. footprint->TransformPadsToPolySet( *layerPoly, layer, 0, maxError, ERROR_INSIDE );
  839. }
  840. // On tech layers, use a poor circle approximation, only for texts (stroke font)
  841. footprint->TransformFPTextToPolySet( *layerPoly, layer, 0, maxError, ERROR_INSIDE );
  842. // Add the remaining things with dynamic seg count for circles
  843. transformFPShapesToPolySet( footprint, layer, *layerPoly, maxError, ERROR_INSIDE );
  844. }
  845. if( cfg.show_zones || layer == F_Mask || layer == B_Mask )
  846. {
  847. for( ZONE* zone : m_board->Zones() )
  848. {
  849. if( zone->IsOnLayer( layer ) )
  850. zone->TransformSolidAreasShapesToPolygon( layer, *layerPoly );
  851. }
  852. }
  853. // This will make a union of all added contours
  854. layerPoly->Simplify();
  855. }
  856. }
  857. // End Build Tech layers
  858. // If we're rendering off-board silk, also render pads of footprints which are entirely
  859. // outside the board outline. This makes off-board footprints more visually recognizable.
  860. if( cfg.show_off_board_silk )
  861. {
  862. BOX2I boardBBox = m_board_poly.BBox();
  863. for( FOOTPRINT* footprint : m_board->Footprints() )
  864. {
  865. if( !footprint->GetBoundingBox().Intersects( boardBBox ) )
  866. {
  867. if( footprint->IsFlipped() )
  868. addPads( footprint, m_offboardPadsBack, B_Cu, false, false );
  869. else
  870. addPads( footprint, m_offboardPadsFront, F_Cu, false, false );
  871. }
  872. }
  873. m_offboardPadsFront->BuildBVH();
  874. m_offboardPadsBack->BuildBVH();
  875. }
  876. // Simplify layer polygons
  877. if( aStatusReporter )
  878. aStatusReporter->Report( _( "Simplifying copper layer polygons" ) );
  879. if( cfg.DifferentiatePlatedCopper() )
  880. {
  881. if( aStatusReporter )
  882. aStatusReporter->Report( _( "Calculating plated copper" ) );
  883. // TRIM PLATED COPPER TO SOLDERMASK
  884. if( m_layers_poly.find( F_Mask ) != m_layers_poly.end() )
  885. {
  886. m_frontPlatedCopperPolys->BooleanIntersection( *m_layers_poly.at( F_Mask ) );
  887. }
  888. if( m_layers_poly.find( B_Mask ) != m_layers_poly.end() )
  889. {
  890. m_backPlatedCopperPolys->BooleanIntersection( *m_layers_poly.at( B_Mask ) );
  891. }
  892. // Subtract plated copper from unplated copper
  893. bool hasF_Cu = false;
  894. bool hasB_Cu = false;
  895. if( m_layers_poly.find( F_Cu ) != m_layers_poly.end() )
  896. {
  897. m_layers_poly[F_Cu]->BooleanSubtract( *m_frontPlatedPadAndGraphicPolys );
  898. m_layers_poly[F_Cu]->BooleanSubtract( *m_frontPlatedCopperPolys );
  899. hasF_Cu = true;
  900. }
  901. if( m_layers_poly.find( B_Cu ) != m_layers_poly.end() )
  902. {
  903. m_layers_poly[B_Cu]->BooleanSubtract( *m_backPlatedPadAndGraphicPolys );
  904. m_layers_poly[B_Cu]->BooleanSubtract( *m_backPlatedCopperPolys );
  905. hasB_Cu = true;
  906. }
  907. // Add plated graphic items to build vertical walls
  908. if( hasF_Cu && m_frontPlatedCopperPolys->OutlineCount() )
  909. m_frontPlatedPadAndGraphicPolys->Append( *m_frontPlatedCopperPolys );
  910. if( hasB_Cu && m_backPlatedCopperPolys->OutlineCount() )
  911. m_backPlatedPadAndGraphicPolys->Append( *m_backPlatedCopperPolys );
  912. m_frontPlatedPadAndGraphicPolys->Simplify();
  913. m_backPlatedPadAndGraphicPolys->Simplify();
  914. m_frontPlatedCopperPolys->Simplify();
  915. m_backPlatedCopperPolys->Simplify();
  916. // ADD PLATED PADS
  917. for( FOOTPRINT* footprint : m_board->Footprints() )
  918. {
  919. addPads( footprint, m_platedPadsFront, F_Cu, false, true );
  920. addPads( footprint, m_platedPadsBack, B_Cu, false, true );
  921. }
  922. // ADD PLATED COPPER
  923. ConvertPolygonToTriangles( *m_frontPlatedCopperPolys, *m_platedPadsFront, m_biuTo3Dunits,
  924. *m_board->GetItem( niluuid ) );
  925. ConvertPolygonToTriangles( *m_backPlatedCopperPolys, *m_platedPadsBack, m_biuTo3Dunits,
  926. *m_board->GetItem( niluuid ) );
  927. m_platedPadsFront->BuildBVH();
  928. m_platedPadsBack->BuildBVH();
  929. }
  930. if( cfg.opengl_copper_thickness && cfg.engine == RENDER_ENGINE::OPENGL )
  931. {
  932. std::vector<PCB_LAYER_ID> &selected_layer_id = layer_ids;
  933. std::vector<PCB_LAYER_ID> layer_id_without_F_and_B;
  934. if( cfg.DifferentiatePlatedCopper() )
  935. {
  936. layer_id_without_F_and_B.clear();
  937. layer_id_without_F_and_B.reserve( layer_ids.size() );
  938. for( PCB_LAYER_ID layer: layer_ids )
  939. {
  940. if( layer != F_Cu && layer != B_Cu )
  941. layer_id_without_F_and_B.push_back( layer );
  942. }
  943. selected_layer_id = layer_id_without_F_and_B;
  944. }
  945. if( selected_layer_id.size() > 0 )
  946. {
  947. if( aStatusReporter )
  948. {
  949. aStatusReporter->Report( wxString::Format( _( "Simplifying %d copper layers" ),
  950. (int) selected_layer_id.size() ) );
  951. }
  952. std::atomic<size_t> nextItem( 0 );
  953. std::atomic<size_t> threadsFinished( 0 );
  954. size_t parallelThreadCount = std::min<size_t>(
  955. std::max<size_t>( std::thread::hardware_concurrency(), 2 ),
  956. selected_layer_id.size() );
  957. for( size_t ii = 0; ii < parallelThreadCount; ++ii )
  958. {
  959. std::thread t = std::thread(
  960. [&nextItem, &threadsFinished, &selected_layer_id, this]()
  961. {
  962. for( size_t i = nextItem.fetch_add( 1 );
  963. i < selected_layer_id.size();
  964. i = nextItem.fetch_add( 1 ) )
  965. {
  966. auto layerPoly = m_layers_poly.find( selected_layer_id[i] );
  967. if( layerPoly != m_layers_poly.end() )
  968. {
  969. // This will make a union of all added contours
  970. layerPoly->second->ClearArcs();
  971. layerPoly->second->Simplify();
  972. }
  973. }
  974. threadsFinished++;
  975. } );
  976. t.detach();
  977. }
  978. while( threadsFinished < parallelThreadCount )
  979. std::this_thread::sleep_for( std::chrono::milliseconds( 10 ) );
  980. }
  981. }
  982. // Simplify holes polygon contours
  983. if( aStatusReporter )
  984. aStatusReporter->Report( _( "Simplify holes contours" ) );
  985. for( PCB_LAYER_ID layer : layer_ids )
  986. {
  987. if( m_layerHoleOdPolys.find( layer ) != m_layerHoleOdPolys.end() )
  988. {
  989. // found
  990. SHAPE_POLY_SET *polyLayer = m_layerHoleOdPolys[layer];
  991. polyLayer->Simplify();
  992. wxASSERT( m_layerHoleIdPolys.find( layer ) != m_layerHoleIdPolys.end() );
  993. polyLayer = m_layerHoleIdPolys[layer];
  994. polyLayer->Simplify();
  995. }
  996. }
  997. // Build BVH (Bounding volume hierarchy) for holes and vias
  998. if( aStatusReporter )
  999. aStatusReporter->Report( _( "Build BVH for holes and vias" ) );
  1000. m_TH_IDs.BuildBVH();
  1001. m_TH_ODs.BuildBVH();
  1002. m_viaAnnuli.BuildBVH();
  1003. if( !m_layerHoleMap.empty() )
  1004. {
  1005. for( std::pair<const PCB_LAYER_ID, BVH_CONTAINER_2D*>& hole : m_layerHoleMap )
  1006. hole.second->BuildBVH();
  1007. }
  1008. // We only need the Solder mask to initialize the BVH
  1009. // because..?
  1010. if( m_layerMap[B_Mask] )
  1011. m_layerMap[B_Mask]->BuildBVH();
  1012. if( m_layerMap[F_Mask] )
  1013. m_layerMap[F_Mask]->BuildBVH();
  1014. }