KiCad PCB EDA Suite
create_layer_items.cpp
Go to the documentation of this file.
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-2020 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 
34 #include "board_adapter.h"
35 #include "../3d_rendering/3d_render_raytracing/shapes2D/ring_2d.h"
36 #include "../3d_rendering/3d_render_raytracing/shapes2D/filled_circle_2d.h"
37 #include "../3d_rendering/3d_render_raytracing/shapes3D/cylinder_3d.h"
38 
39 #include <board.h>
40 #include <board_design_settings.h>
41 #include <footprint.h>
42 #include <pad.h>
43 #include <pcb_text.h>
44 #include <fp_shape.h>
45 #include <zone.h>
47 #include <trigo.h>
48 #include <vector>
49 #include <thread>
50 #include <core/arraydim.h>
51 #include <algorithm>
52 #include <atomic>
53 #include <wx/log.h>
54 
55 #ifdef PRINT_STATISTICS_3D_VIEWER
56 #include <profile.h>
57 #endif
58 
59 
61 {
62  if( !m_layers_poly.empty() )
63  {
64  for( auto& poly : m_layers_poly )
65  delete poly.second;
66 
67  m_layers_poly.clear();
68  }
69 
70  delete m_frontPlatedPadPolys;
71  m_frontPlatedPadPolys = nullptr;
72 
73  delete m_backPlatedPadPolys;
74  m_backPlatedPadPolys = nullptr;
75 
76  if( !m_layerHoleIdPolys.empty() )
77  {
78  for( auto& poly : m_layerHoleIdPolys )
79  delete poly.second;
80 
81  m_layerHoleIdPolys.clear();
82  }
83 
84  if( !m_layerHoleOdPolys.empty() )
85  {
86  for( auto& poly : m_layerHoleOdPolys )
87  delete poly.second;
88 
89  m_layerHoleOdPolys.clear();
90  }
91 
92  if( !m_layerMap.empty() )
93  {
94  for( auto& poly : m_layerMap )
95  delete poly.second;
96 
97  m_layerMap.clear();
98  }
99 
100  delete m_platedPadsFront;
101  m_platedPadsFront = nullptr;
102 
103  delete m_platedPadsBack;
104  m_platedPadsBack = nullptr;
105 
106  if( !m_layerHoleMap.empty() )
107  {
108  for( auto& poly : m_layerHoleMap )
109  delete poly.second;
110 
111  m_layerHoleMap.clear();
112  }
113 
121 
124 }
125 
126 
127 void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
128 {
129  destroyLayers();
130 
131  // Build Copper layers
132  // Based on:
133  // https://github.com/KiCad/kicad-source-mirror/blob/master/3d-viewer/3d_draw.cpp#L692
134 
135 #ifdef PRINT_STATISTICS_3D_VIEWER
136  unsigned stats_startCopperLayersTime = GetRunningMicroSecs();
137 
138  unsigned start_Time = stats_startCopperLayersTime;
139 #endif
140 
141  PCB_LAYER_ID cu_seq[MAX_CU_LAYERS];
143 
144  m_trackCount = 0;
146  m_viaCount = 0;
148  m_holeCount = 0;
150 
151  if( !m_board )
152  return;
153 
154  // Prepare track list, convert in a vector. Calc statistic for the holes
155  std::vector<const PCB_TRACK*> trackList;
156  trackList.clear();
157  trackList.reserve( m_board->Tracks().size() );
158 
159  for( PCB_TRACK* track : m_board->Tracks() )
160  {
161  if( !Is3dLayerEnabled( track->GetLayer() ) ) // Skip non enabled layers
162  continue;
163 
164  // Note: a PCB_TRACK holds normal segment tracks and also vias circles (that have also
165  // drill values)
166  trackList.push_back( track );
167 
168  if( track->Type() == PCB_VIA_T )
169  {
170  const PCB_VIA *via = static_cast< const PCB_VIA*>( track );
171  m_viaCount++;
172  m_averageViaHoleDiameter += via->GetDrillValue() * m_biuTo3Dunits;
173  }
174  else
175  {
176  m_trackCount++;
177  }
178 
179  m_averageTrackWidth += track->GetWidth() * m_biuTo3Dunits;
180  }
181 
182  if( m_trackCount )
184 
185  if( m_viaCount )
187 
188  // Prepare copper layers index and containers
189  std::vector< PCB_LAYER_ID > layer_id;
190  layer_id.clear();
191  layer_id.reserve( m_copperLayersCount );
192 
193  for( unsigned i = 0; i < arrayDim( cu_seq ); ++i )
194  cu_seq[i] = ToLAYER_ID( B_Cu - i );
195 
196  for( LSEQ cu = cu_set.Seq( cu_seq, arrayDim( cu_seq ) ); cu; ++cu )
197  {
198  const PCB_LAYER_ID curr_layer_id = *cu;
199 
200  if( !Is3dLayerEnabled( curr_layer_id ) ) // Skip non enabled layers
201  continue;
202 
203  layer_id.push_back( curr_layer_id );
204 
205  BVH_CONTAINER_2D *layerContainer = new BVH_CONTAINER_2D;
206  m_layerMap[curr_layer_id] = layerContainer;
207 
210  {
211  SHAPE_POLY_SET* layerPoly = new SHAPE_POLY_SET;
212  m_layers_poly[curr_layer_id] = layerPoly;
213  }
214  }
215 
217  {
220 
223 
224  }
225 
226  if( aStatusReporter )
227  aStatusReporter->Report( _( "Create tracks and vias" ) );
228 
229  // Create tracks as objects and add it to container
230  for( PCB_LAYER_ID curr_layer_id : layer_id )
231  {
232  wxASSERT( m_layerMap.find( curr_layer_id ) != m_layerMap.end() );
233 
234  BVH_CONTAINER_2D *layerContainer = m_layerMap[curr_layer_id];
235 
236  // Add track segments shapes and via annulus shapes
237  unsigned int nTracks = trackList.size();
238 
239  for( unsigned int trackIdx = 0; trackIdx < nTracks; ++trackIdx )
240  {
241  const PCB_TRACK *track = trackList[trackIdx];
242 
243  // NOTE: Vias can be on multiple layers
244  if( !track->IsOnLayer( curr_layer_id ) )
245  continue;
246 
247  // Skip vias annulus when not connected on this layer (if removing is enabled)
248  const PCB_VIA *via = dyn_cast< const PCB_VIA*>( track );
249 
250  if( via && !via->FlashLayer( curr_layer_id ) && IsCopperLayer( curr_layer_id ) )
251  continue;
252 
253  // Add object item to layer container
254  createTrack( track, layerContainer, 0.0f );
255  }
256  }
257 
258  // Create VIAS and THTs objects and add it to holes containers
259  for( PCB_LAYER_ID curr_layer_id : layer_id )
260  {
261  // ADD TRACKS
262  unsigned int nTracks = trackList.size();
263 
264  for( unsigned int trackIdx = 0; trackIdx < nTracks; ++trackIdx )
265  {
266  const PCB_TRACK *track = trackList[trackIdx];
267 
268  if( !track->IsOnLayer( curr_layer_id ) )
269  continue;
270 
271  // ADD VIAS and THT
272  if( track->Type() == PCB_VIA_T )
273  {
274  const PCB_VIA* via = static_cast<const PCB_VIA*>( track );
275  const VIATYPE viatype = via->GetViaType();
276  const float holediameter = via->GetDrillValue() * BiuTo3dUnits();
277 
278  // holes and layer copper extend half info cylinder wall to hide transition
279  const float thickness = GetHolePlatingThickness() * BiuTo3dUnits() / 2.0f;
280  const float hole_inner_radius = holediameter / 2.0f;
281  const float ring_radius = via->GetWidth() * BiuTo3dUnits() / 2.0f;
282 
283  const SFVEC2F via_center( via->GetStart().x * m_biuTo3Dunits,
284  -via->GetStart().y * m_biuTo3Dunits );
285 
286  if( viatype != VIATYPE::THROUGH )
287  {
288 
289  // Add hole objects
290  BVH_CONTAINER_2D *layerHoleContainer = nullptr;
291 
292  // Check if the layer is already created
293  if( m_layerHoleMap.find( curr_layer_id ) == m_layerHoleMap.end() )
294  {
295  // not found, create a new container
296  layerHoleContainer = new BVH_CONTAINER_2D;
297  m_layerHoleMap[curr_layer_id] = layerHoleContainer;
298  }
299  else
300  {
301  // found
302  layerHoleContainer = m_layerHoleMap[curr_layer_id];
303  }
304 
305  // Add a hole for this layer
306  layerHoleContainer->Add( new FILLED_CIRCLE_2D( via_center,
307  hole_inner_radius + thickness,
308  *track ) );
309  }
310  else if( curr_layer_id == layer_id[0] ) // it only adds once the THT holes
311  {
312  // Add through hole object
313  m_throughHoleOds.Add( new FILLED_CIRCLE_2D( via_center,
314  hole_inner_radius + thickness,
315  *track ) );
316  m_throughHoleViaOds.Add( new FILLED_CIRCLE_2D( via_center,
317  hole_inner_radius + thickness,
318  *track ) );
319 
322  {
324  ring_radius,
325  *track ) );
326  }
327 
328  m_throughHoleIds.Add( new FILLED_CIRCLE_2D( via_center, hole_inner_radius,
329  *track ) );
330  }
331  }
332  }
333  }
334 
335  // Create VIAS and THTs objects and add it to holes containers
336  for( PCB_LAYER_ID curr_layer_id : layer_id )
337  {
338  // ADD TRACKS
339  const unsigned int nTracks = trackList.size();
340 
341  for( unsigned int trackIdx = 0; trackIdx < nTracks; ++trackIdx )
342  {
343  const PCB_TRACK *track = trackList[trackIdx];
344 
345  if( !track->IsOnLayer( curr_layer_id ) )
346  continue;
347 
348  // ADD VIAS and THT
349  if( track->Type() == PCB_VIA_T )
350  {
351  const PCB_VIA* via = static_cast<const PCB_VIA*>( track );
352  const VIATYPE viatype = via->GetViaType();
353 
354  if( viatype != VIATYPE::THROUGH )
355  {
356  // Add PCB_VIA hole contours
357 
358  // Add outer holes of VIAs
359  SHAPE_POLY_SET *layerOuterHolesPoly = nullptr;
360  SHAPE_POLY_SET *layerInnerHolesPoly = nullptr;
361 
362  // Check if the layer is already created
363  if( m_layerHoleOdPolys.find( curr_layer_id ) ==
364  m_layerHoleOdPolys.end() )
365  {
366  // not found, create a new container
367  layerOuterHolesPoly = new SHAPE_POLY_SET;
368  m_layerHoleOdPolys[curr_layer_id] = layerOuterHolesPoly;
369 
370  wxASSERT( m_layerHoleIdPolys.find( curr_layer_id ) ==
371  m_layerHoleIdPolys.end() );
372 
373  layerInnerHolesPoly = new SHAPE_POLY_SET;
374  m_layerHoleIdPolys[curr_layer_id] = layerInnerHolesPoly;
375  }
376  else
377  {
378  // found
379  layerOuterHolesPoly = m_layerHoleOdPolys[curr_layer_id];
380 
381  wxASSERT( m_layerHoleIdPolys.find( curr_layer_id ) !=
382  m_layerHoleIdPolys.end() );
383 
384  layerInnerHolesPoly = m_layerHoleIdPolys[curr_layer_id];
385  }
386 
387  const int holediameter = via->GetDrillValue();
388  const int hole_outer_radius = (holediameter / 2) + GetHolePlatingThickness();
389 
390  TransformCircleToPolygon( *layerOuterHolesPoly, via->GetStart(),
391  hole_outer_radius, ARC_HIGH_DEF, ERROR_INSIDE );
392 
393  TransformCircleToPolygon( *layerInnerHolesPoly, via->GetStart(),
394  holediameter / 2, ARC_HIGH_DEF, ERROR_INSIDE );
395  }
396  else if( curr_layer_id == layer_id[0] ) // it only adds once the THT holes
397  {
398  const int holediameter = via->GetDrillValue();
399  const int hole_outer_radius = (holediameter / 2) + GetHolePlatingThickness();
400  const int hole_outer_ring_radius = via->GetWidth() / 2.0f;
401 
402  // Add through hole contours
404  hole_outer_radius, ARC_HIGH_DEF, ERROR_INSIDE );
405 
406  // Add same thing for vias only
408  hole_outer_radius, ARC_HIGH_DEF, ERROR_INSIDE );
409 
411  {
413  via->GetStart(), hole_outer_ring_radius,
414  ARC_HIGH_DEF, ERROR_INSIDE );
415  }
416  }
417  }
418  }
419  }
420 
421  // Creates vertical outline contours of the tracks and add it to the poly of the layer
424  {
425  for( PCB_LAYER_ID curr_layer_id : layer_id )
426  {
427  wxASSERT( m_layers_poly.find( curr_layer_id ) != m_layers_poly.end() );
428 
429  SHAPE_POLY_SET *layerPoly = m_layers_poly[curr_layer_id];
430 
431  // ADD TRACKS
432  unsigned int nTracks = trackList.size();
433 
434  for( unsigned int trackIdx = 0; trackIdx < nTracks; ++trackIdx )
435  {
436  const PCB_TRACK *track = trackList[trackIdx];
437 
438  if( !track->IsOnLayer( curr_layer_id ) )
439  continue;
440 
441  // Skip vias annulus when not connected on this layer (if removing is enabled)
442  const PCB_VIA *via = dyn_cast<const PCB_VIA*>( track );
443 
444  if( via && !via->FlashLayer( curr_layer_id ) && IsCopperLayer( curr_layer_id ) )
445  continue;
446 
447  // Add the track/via contour
448  track->TransformShapeWithClearanceToPolygon( *layerPoly, curr_layer_id, 0,
449  ARC_HIGH_DEF, ERROR_INSIDE );
450  }
451  }
452  }
453 
454  // Add holes of footprints
455  for( FOOTPRINT* footprint : m_board->Footprints() )
456  {
457  for( PAD* pad : footprint->Pads() )
458  {
459  const wxSize padHole = pad->GetDrillSize();
460 
461  if( !padHole.x ) // Not drilled pad like SMD pad
462  continue;
463 
464  // The hole in the body is inflated by copper thickness, if not plated, no copper
465  const int inflate = ( pad->GetAttribute () != PAD_ATTRIB::NPTH ) ?
466  GetHolePlatingThickness() / 2 : 0;
467 
468  m_holeCount++;
469  m_averageHoleDiameter += ( ( pad->GetDrillSize().x +
470  pad->GetDrillSize().y ) / 2.0f ) * m_biuTo3Dunits;
471 
473 
475  {
477  }
478 
480  }
481  }
482 
483  if( m_holeCount )
485 
486  // Add contours of the pad holes (pads can be Circle or Segment holes)
487  for( FOOTPRINT* footprint : m_board->Footprints() )
488  {
489  for( PAD* pad : footprint->Pads() )
490  {
491  const wxSize padHole = pad->GetDrillSize();
492 
493  if( !padHole.x ) // Not drilled pad like SMD pad
494  continue;
495 
496  // The hole in the body is inflated by copper thickness.
497  const int inflate = GetHolePlatingThickness();
498 
499  if( pad->GetAttribute () != PAD_ATTRIB::NPTH )
500  {
502  {
503  pad->TransformHoleWithClearanceToPolygon( m_throughHoleAnnularRingPolys,
504  inflate, ARC_HIGH_DEF, ERROR_INSIDE );
505  }
506 
507  pad->TransformHoleWithClearanceToPolygon( m_throughHoleOdPolys, inflate,
508  ARC_HIGH_DEF, ERROR_INSIDE );
509  }
510  else
511  {
512  // If not plated, no copper.
514  {
515  pad->TransformHoleWithClearanceToPolygon( m_throughHoleAnnularRingPolys, 0,
516  ARC_HIGH_DEF, ERROR_INSIDE );
517  }
518 
519  pad->TransformHoleWithClearanceToPolygon( m_nonPlatedThroughHoleOdPolys, 0,
520  ARC_HIGH_DEF, ERROR_INSIDE );
521  }
522  }
523  }
524 
525  const bool renderPlatedPadsAsPlated = GetFlag( FL_RENDER_PLATED_PADS_AS_PLATED ) &&
527 
528  // Add footprints PADs objects to containers
529  for( PCB_LAYER_ID curr_layer_id : layer_id )
530  {
531  wxASSERT( m_layerMap.find( curr_layer_id ) != m_layerMap.end() );
532 
533  BVH_CONTAINER_2D *layerContainer = m_layerMap[curr_layer_id];
534 
535  // ADD PADS
536  for( FOOTPRINT* footprint : m_board->Footprints() )
537  {
538  // Note: NPTH pads are not drawn on copper layers when the pad
539  // has same shape as its hole
540  addPadsWithClearance( footprint, layerContainer, curr_layer_id, 0,
541  true, renderPlatedPadsAsPlated, false );
542 
543  // Micro-wave footprints may have items on copper layers
544  addFootprintShapesWithClearance( footprint, layerContainer, curr_layer_id, 0 );
545  }
546  }
547 
548  if( renderPlatedPadsAsPlated )
549  {
550  // ADD PLATED PADS
551  for( FOOTPRINT* footprint : m_board->Footprints() )
552  {
553  addPadsWithClearance( footprint, m_platedPadsFront, F_Cu, 0, true, false, true );
554 
555  addPadsWithClearance( footprint, m_platedPadsBack, B_Cu, 0, true, false, true );
556  }
557 
560  }
561 
562  // Add footprints PADs poly contours (vertical outlines)
565  {
566  for( PCB_LAYER_ID curr_layer_id : layer_id )
567  {
568  wxASSERT( m_layers_poly.find( curr_layer_id ) != m_layers_poly.end() );
569 
570  SHAPE_POLY_SET *layerPoly = m_layers_poly[curr_layer_id];
571 
572  // Add pads to polygon list
573  for( FOOTPRINT* footprint : m_board->Footprints() )
574  {
575  // Note: NPTH pads are not drawn on copper layers when the pad
576  // has same shape as its hole
577  footprint->TransformPadsWithClearanceToPolygon( *layerPoly, curr_layer_id,
578  0, ARC_HIGH_DEF, ERROR_INSIDE,
579  true, renderPlatedPadsAsPlated,
580  false );
581 
582  transformFPShapesToPolygon( footprint, curr_layer_id, *layerPoly );
583  }
584  }
585 
586  if( renderPlatedPadsAsPlated )
587  {
588  // ADD PLATED PADS contours
589  for( FOOTPRINT* footprint : m_board->Footprints() )
590  {
591  footprint->TransformPadsWithClearanceToPolygon( *m_frontPlatedPadPolys, F_Cu,
592  0, ARC_HIGH_DEF, ERROR_INSIDE,
593  true, false, true );
594 
595  footprint->TransformPadsWithClearanceToPolygon( *m_backPlatedPadPolys, B_Cu,
596  0, ARC_HIGH_DEF, ERROR_INSIDE,
597  true, false, true );
598  }
599  }
600  }
601 
602  // Add graphic item on copper layers to object containers
603  for( PCB_LAYER_ID curr_layer_id : layer_id )
604  {
605  wxASSERT( m_layerMap.find( curr_layer_id ) != m_layerMap.end() );
606 
607  BVH_CONTAINER_2D *layerContainer = m_layerMap[curr_layer_id];
608 
609  // Add graphic items on copper layers (texts and other graphics)
610  for( BOARD_ITEM* item : m_board->Drawings() )
611  {
612  if( !item->IsOnLayer( curr_layer_id ) )
613  continue;
614 
615  switch( item->Type() )
616  {
617  case PCB_SHAPE_T:
618  addShapeWithClearance( static_cast<PCB_SHAPE*>( item ), layerContainer,
619  curr_layer_id, 0 );
620  break;
621 
622  case PCB_TEXT_T:
623  addShapeWithClearance( static_cast<PCB_TEXT*>( item ), layerContainer,
624  curr_layer_id, 0 );
625  break;
626 
627  case PCB_DIM_ALIGNED_T:
628  case PCB_DIM_CENTER_T:
630  case PCB_DIM_LEADER_T:
631  addShapeWithClearance( static_cast<PCB_DIMENSION_BASE*>( item ),
632  layerContainer, curr_layer_id, 0 );
633  break;
634 
635  default:
636  wxLogTrace( m_logTrace, wxT( "createLayers: item type: %d not implemented" ),
637  item->Type() );
638  break;
639  }
640  }
641  }
642 
643  // Add graphic item on copper layers to poly contours (vertical outlines)
646  {
647  for( PCB_LAYER_ID cur_layer_id : layer_id )
648  {
649  wxASSERT( m_layers_poly.find( cur_layer_id ) != m_layers_poly.end() );
650 
651  SHAPE_POLY_SET *layerPoly = m_layers_poly[cur_layer_id];
652 
653  // Add graphic items on copper layers (texts and other )
654  for( BOARD_ITEM* item : m_board->Drawings() )
655  {
656  if( !item->IsOnLayer( cur_layer_id ) )
657  continue;
658 
659  switch( item->Type() )
660  {
661  case PCB_SHAPE_T:
662  item->TransformShapeWithClearanceToPolygon( *layerPoly, cur_layer_id, 0,
663  ARC_HIGH_DEF, ERROR_INSIDE );
664  break;
665 
666  case PCB_TEXT_T:
667  {
668  PCB_TEXT* text = static_cast<PCB_TEXT*>( item );
669 
670  text->TransformTextShapeWithClearanceToPolygon( *layerPoly, cur_layer_id, 0,
671  ARC_HIGH_DEF, ERROR_INSIDE );
672  }
673  break;
674 
675  default:
676  wxLogTrace( m_logTrace, wxT( "createLayers: item type: %d not implemented" ),
677  item->Type() );
678  break;
679  }
680  }
681  }
682  }
683 
684  if( GetFlag( FL_ZONE ) )
685  {
686  if( aStatusReporter )
687  aStatusReporter->Report( _( "Create zones" ) );
688 
689  std::vector<std::pair<ZONE*, PCB_LAYER_ID>> zones;
690  std::unordered_map<PCB_LAYER_ID, std::unique_ptr<std::mutex>> layer_lock;
691 
692  for( ZONE* zone : m_board->Zones() )
693  {
694  for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
695  {
696  zones.emplace_back( std::make_pair( zone, layer ) );
697  layer_lock.emplace( layer, std::make_unique<std::mutex>() );
698  }
699  }
700 
701  // Add zones objects
702  std::atomic<size_t> nextZone( 0 );
703  std::atomic<size_t> threadsFinished( 0 );
704 
705  size_t parallelThreadCount = std::min<size_t>( zones.size(),
706  std::max<size_t>( std::thread::hardware_concurrency(), 2 ) );
707 
708  for( size_t ii = 0; ii < parallelThreadCount; ++ii )
709  {
710  std::thread t = std::thread( [&]()
711  {
712  for( size_t areaId = nextZone.fetch_add( 1 );
713  areaId < zones.size();
714  areaId = nextZone.fetch_add( 1 ) )
715  {
716  ZONE* zone = zones[areaId].first;
717 
718  if( zone == nullptr )
719  break;
720 
721  PCB_LAYER_ID layer = zones[areaId].second;
722 
723  auto layerContainer = m_layerMap.find( layer );
724  auto layerPolyContainer = m_layers_poly.find( layer );
725 
726  if( layerContainer != m_layerMap.end() )
727  {
728  addSolidAreasShapes( zone, layerContainer->second, layer );
729  }
730 
733  && layerPolyContainer != m_layers_poly.end() )
734  {
735  auto mut_it = layer_lock.find( layer );
736 
737  std::lock_guard< std::mutex > lock( *( mut_it->second ) );
738  zone->TransformSolidAreasShapesToPolygon( layer, *layerPolyContainer->second );
739  }
740  }
741 
742  threadsFinished++;
743  } );
744 
745  t.detach();
746  }
747 
748  while( threadsFinished < parallelThreadCount )
749  std::this_thread::sleep_for( std::chrono::milliseconds( 10 ) );
750 
751  }
752 
753  // Simplify layer polygons
754 
755  if( aStatusReporter )
756  aStatusReporter->Report( _( "Simplifying copper layers polygons" ) );
757 
760  {
762  {
763  if( m_frontPlatedPadPolys && ( m_layers_poly.find( F_Cu ) != m_layers_poly.end() ) )
764  {
765  if( aStatusReporter )
766  aStatusReporter->Report( _( "Simplifying polygons on F_Cu" ) );
767 
768  SHAPE_POLY_SET *layerPoly_F_Cu = m_layers_poly[F_Cu];
770 
772  }
773 
774  if( m_backPlatedPadPolys && ( m_layers_poly.find( B_Cu ) != m_layers_poly.end() ) )
775  {
776  if( aStatusReporter )
777  aStatusReporter->Report( _( "Simplifying polygons on B_Cu" ) );
778 
779  SHAPE_POLY_SET *layerPoly_B_Cu = m_layers_poly[B_Cu];
781 
783  }
784  }
785 
786  std::vector< PCB_LAYER_ID > &selected_layer_id = layer_id;
787  std::vector< PCB_LAYER_ID > layer_id_without_F_and_B;
788 
790  {
791  layer_id_without_F_and_B.clear();
792  layer_id_without_F_and_B.reserve( layer_id.size() );
793 
794  for( size_t i = 0; i < layer_id.size(); ++i )
795  {
796  if( ( layer_id[i] != F_Cu ) && ( layer_id[i] != B_Cu ) )
797  layer_id_without_F_and_B.push_back( layer_id[i] );
798  }
799 
800  selected_layer_id = layer_id_without_F_and_B;
801  }
802 
803  if( selected_layer_id.size() > 0 )
804  {
805  if( aStatusReporter )
806  aStatusReporter->Report( wxString::Format(
807  _( "Simplifying %d copper layers" ),
808  (int)selected_layer_id.size() ) );
809 
810  std::atomic<size_t> nextItem( 0 );
811  std::atomic<size_t> threadsFinished( 0 );
812 
813  size_t parallelThreadCount = std::min<size_t>(
814  std::max<size_t>( std::thread::hardware_concurrency(), 2 ),
815  selected_layer_id.size() );
816 
817  for( size_t ii = 0; ii < parallelThreadCount; ++ii )
818  {
819  std::thread t = std::thread(
820  [&nextItem, &threadsFinished, &selected_layer_id, this]()
821  {
822  for( size_t i = nextItem.fetch_add( 1 );
823  i < selected_layer_id.size();
824  i = nextItem.fetch_add( 1 ) )
825  {
826  auto layerPoly = m_layers_poly.find( selected_layer_id[i] );
827 
828  if( layerPoly != m_layers_poly.end() )
829  // This will make a union of all added contours
830  layerPoly->second->Simplify( SHAPE_POLY_SET::PM_FAST );
831  }
832 
833  threadsFinished++;
834  } );
835 
836  t.detach();
837  }
838 
839  while( threadsFinished < parallelThreadCount )
840  std::this_thread::sleep_for( std::chrono::milliseconds( 10 ) );
841  }
842  }
843 
844  // Simplify holes polygon contours
845  if( aStatusReporter )
846  aStatusReporter->Report( _( "Simplify holes contours" ) );
847 
848  for( PCB_LAYER_ID layer : layer_id )
849  {
850  if( m_layerHoleOdPolys.find( layer ) != m_layerHoleOdPolys.end() )
851  {
852  // found
853  SHAPE_POLY_SET *polyLayer = m_layerHoleOdPolys[layer];
854  polyLayer->Simplify( SHAPE_POLY_SET::PM_FAST );
855 
856  wxASSERT( m_layerHoleIdPolys.find( layer ) != m_layerHoleIdPolys.end() );
857 
858  polyLayer = m_layerHoleIdPolys[layer];
859  polyLayer->Simplify( SHAPE_POLY_SET::PM_FAST );
860  }
861  }
862 
863  // End Build Copper layers
864 
865  // This will make a union of all added contours
870 
871  // Build Tech layers
872  // Based on:
873  // https://github.com/KiCad/kicad-source-mirror/blob/master/3d-viewer/3d_draw.cpp#L1059
874  if( aStatusReporter )
875  aStatusReporter->Report( _( "Build Tech layers" ) );
876 
877  // draw graphic items, on technical layers
878  static const PCB_LAYER_ID teckLayerList[] = {
879  B_Adhes,
880  F_Adhes,
881  B_Paste,
882  F_Paste,
883  B_SilkS,
884  F_SilkS,
885  B_Mask,
886  F_Mask,
887 
888  // Aux Layers
889  Dwgs_User,
890  Cmts_User,
891  Eco1_User,
892  Eco2_User,
893  Edge_Cuts,
894  Margin
895  };
896 
897  // User layers are not drawn here, only technical layers
898  for( LSEQ seq = LSET::AllNonCuMask().Seq( teckLayerList, arrayDim( teckLayerList ) );
899  seq;
900  ++seq )
901  {
902  const PCB_LAYER_ID curr_layer_id = *seq;
903 
904  if( !Is3dLayerEnabled( curr_layer_id ) )
905  continue;
906 
907  if( aStatusReporter )
908  aStatusReporter->Report( wxString::Format(
909  _( "Build Tech layer %d" ), (int)curr_layer_id ) );
910 
911  BVH_CONTAINER_2D *layerContainer = new BVH_CONTAINER_2D;
912  m_layerMap[curr_layer_id] = layerContainer;
913 
914  SHAPE_POLY_SET *layerPoly = new SHAPE_POLY_SET;
915  m_layers_poly[curr_layer_id] = layerPoly;
916 
917  // Add drawing objects
918  for( BOARD_ITEM* item : m_board->Drawings() )
919  {
920  if( !item->IsOnLayer( curr_layer_id ) )
921  continue;
922 
923  switch( item->Type() )
924  {
925  case PCB_SHAPE_T:
926  addShapeWithClearance( static_cast<PCB_SHAPE*>( item ), layerContainer,
927  curr_layer_id, 0 );
928  break;
929 
930  case PCB_TEXT_T:
931  addShapeWithClearance( static_cast<PCB_TEXT*>( item ), layerContainer,
932  curr_layer_id, 0 );
933  break;
934 
935  case PCB_DIM_ALIGNED_T:
936  case PCB_DIM_CENTER_T:
938  case PCB_DIM_LEADER_T:
939  addShapeWithClearance( static_cast<PCB_DIMENSION_BASE*>( item ), layerContainer,
940  curr_layer_id, 0 );
941  break;
942 
943  default:
944  break;
945  }
946  }
947 
948  // Add drawing contours
949  for( BOARD_ITEM* item : m_board->Drawings() )
950  {
951  if( !item->IsOnLayer( curr_layer_id ) )
952  continue;
953 
954  switch( item->Type() )
955  {
956  case PCB_SHAPE_T:
957  item->TransformShapeWithClearanceToPolygon( *layerPoly, curr_layer_id, 0,
958  ARC_HIGH_DEF, ERROR_INSIDE );
959  break;
960 
961  case PCB_TEXT_T:
962  {
963  PCB_TEXT* text = static_cast<PCB_TEXT*>( item );
964 
965  text->TransformTextShapeWithClearanceToPolygon( *layerPoly, curr_layer_id, 0,
966  ARC_HIGH_DEF, ERROR_INSIDE );
967  }
968  break;
969 
970  default:
971  break;
972  }
973  }
974 
975  // Add footprints tech layers - objects
976  for( FOOTPRINT* footprint : m_board->Footprints() )
977  {
978  if( ( curr_layer_id == F_SilkS ) || ( curr_layer_id == B_SilkS ) )
979  {
981 
982  for( PAD* pad : footprint->Pads() )
983  {
984  if( !pad->IsOnLayer( curr_layer_id ) )
985  continue;
986 
987  buildPadOutlineAsSegments( pad, layerContainer, linewidth );
988  }
989  }
990  else
991  {
992  addPadsWithClearance( footprint, layerContainer, curr_layer_id, 0,
993  false, false, false );
994  }
995 
996  addFootprintShapesWithClearance( footprint, layerContainer, curr_layer_id, 0 );
997  }
998 
999 
1000  // Add footprints tech layers - contours
1001  for( FOOTPRINT* footprint : m_board->Footprints() )
1002  {
1003  if( ( curr_layer_id == F_SilkS ) || ( curr_layer_id == B_SilkS ) )
1004  {
1006 
1007  for( PAD* pad : footprint->Pads() )
1008  {
1009  if( !pad->IsOnLayer( curr_layer_id ) )
1010  continue;
1011 
1012  buildPadOutlineAsPolygon( pad, *layerPoly, linewidth );
1013  }
1014  }
1015  else
1016  {
1017  footprint->TransformPadsWithClearanceToPolygon( *layerPoly, curr_layer_id, 0,
1018  ARC_HIGH_DEF, ERROR_INSIDE );
1019  }
1020 
1021  // On tech layers, use a poor circle approximation, only for texts (stroke font)
1022  footprint->TransformFPTextWithClearanceToPolygonSet( *layerPoly, curr_layer_id, 0,
1023  ARC_HIGH_DEF, ERROR_INSIDE );
1024 
1025  // Add the remaining things with dynamic seg count for circles
1026  transformFPShapesToPolygon( footprint, curr_layer_id, *layerPoly );
1027  }
1028 
1029 
1030  // Draw non copper zones
1031  if( GetFlag( FL_ZONE ) )
1032  {
1033  for( ZONE* zone : m_board->Zones() )
1034  {
1035  if( zone->IsOnLayer( curr_layer_id ) )
1036  addSolidAreasShapes( zone, layerContainer, curr_layer_id );
1037  }
1038 
1039  for( ZONE* zone : m_board->Zones() )
1040  {
1041  if( zone->IsOnLayer( curr_layer_id ) )
1042  zone->TransformSolidAreasShapesToPolygon( curr_layer_id, *layerPoly );
1043  }
1044  }
1045 
1046  // This will make a union of all added contours
1047  layerPoly->Simplify( SHAPE_POLY_SET::PM_FAST );
1048  }
1049  // End Build Tech layers
1050 
1051  // Build BVH (Bounding volume hierarchy) for holes and vias
1052 
1053  if( aStatusReporter )
1054  aStatusReporter->Report( _( "Build BVH for holes and vias" ) );
1055 
1059 
1060  if( !m_layerHoleMap.empty() )
1061  {
1062  for( auto& hole : m_layerHoleMap )
1063  hole.second->BuildBVH();
1064  }
1065 
1066  // We only need the Solder mask to initialize the BVH
1067  // because..?
1068  if( m_layerMap[B_Mask] )
1069  m_layerMap[B_Mask]->BuildBVH();
1070 
1071  if( m_layerMap[F_Mask] )
1072  m_layerMap[F_Mask]->BuildBVH();
1073 }
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:750
BVH_CONTAINER_2D m_throughHoleViaOds
List of through hole via inner diameters.
bool GetFlag(DISPLAY3D_FLG aFlag) const
Get a configuration status of a flag.
BVH_CONTAINER_2D * m_platedPadsFront
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition: typeinfo.h:100
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition: typeinfo.h:101
ZONES & Zones()
Definition: board.h:239
SHAPE_POLY_SET * m_backPlatedPadPolys
Polygon contours for hole outer diameters for each layer.
BVH_CONTAINER_2D m_throughHoleIds
List of through hole vias with the radius of the hole inflated with the copper thickness.
void buildPadOutlineAsSegments(const PAD *aPad, CONTAINER_2D_BASE *aDstContainer, int aWidth)
SHAPE_POLY_SET * m_frontPlatedPadPolys
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:80
OBJECT_2D * createPadWithDrill(const PAD *aPad, int aInflateValue)
float m_averageHoleDiameter
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition: typeinfo.h:102
SHAPE_POLY_SET m_throughHoleAnnularRingPolys
static LSET AllNonCuMask()
Return a mask holding all layer minus CU layers.
Definition: lset.cpp:773
MAP_CONTAINER_2D_BASE m_layerMap
2D elements for each layer.
void transformFPShapesToPolygon(const FOOTPRINT *aFootprint, PCB_LAYER_ID aLayer, SHAPE_POLY_SET &aCornerBuffer) const
class PCB_TEXT, text on a layer
Definition: typeinfo.h:91
BVH_CONTAINER_2D m_throughHoleOds
List of plated through hole annular rings.
SHAPE_POLY_SET m_throughHoleViaOdPolys
Polygon contours for through hole via annular rings.
float m_averageTrackWidth
Number of through hole vias in the board.
void TransformShapeWithClearanceToPolygon(SHAPE_POLY_SET &aCornerBuffer, PCB_LAYER_ID aLayer, int aClearanceValue, int aError, ERROR_LOC aErrorLoc, bool ignoreLineWidth=false) const override
Function TransformShapeWithClearanceToPolygon Convert the track shape to a closed polygon Used in fil...
A pure virtual class used to derive REPORTER objects from.
Definition: reporter.h:70
void addFootprintShapesWithClearance(const FOOTPRINT *aFootprint, CONTAINER_2D_BASE *aDstContainer, PCB_LAYER_ID aLayerId, int aInflateValue)
LSEQ Seq(const PCB_LAYER_ID *aWishListSequence, unsigned aCount) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition: lset.cpp:411
MAP_POLY m_layerHoleIdPolys
Polygon contours for non plated through hole outer diameters.
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:589
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)=0
Report a string with a given severity.
void Add(OBJECT_2D *aObject)
Definition: container_2d.h:49
bool Is3dLayerEnabled(PCB_LAYER_ID aLayer) const
Check if a layer is enabled.
like PAD_PTH, but not plated
void addShapeWithClearance(const PCB_TEXT *aText, CONTAINER_2D_BASE *aDstContainer, PCB_LAYER_ID aLayerId, int aClearanceValue)
LSET is a set of PCB_LAYER_IDs.
Definition: layer_ids.h:502
virtual bool IsOnLayer(PCB_LAYER_ID aLayer) const
Test to see if this object is on the given layer.
Definition: board_item.h:229
unsigned int m_trackCount
Track average width.
glm::vec2 SFVEC2F
Definition: xv3d_types.h:42
#define MAX_CU_LAYERS
Definition: layer_ids.h:146
BVH_CONTAINER_2D * m_platedPadsBack
The holes per each layer.
Represent a set of closed polygons.
MAP_POLY m_layerHoleOdPolys
Polygon contours for hole inner diameters for each layer.
FOOTPRINTS & Footprints()
Definition: board.h:233
void buildPadOutlineAsPolygon(const PAD *aPad, SHAPE_POLY_SET &aCornerBuffer, int aWidth) const
MAP_CONTAINER_2D_BASE m_layerHoleMap
List of through holes with the radius of the hole inflated with the copper thickness.
void addSolidAreasShapes(const ZONE *aZoneContainer, CONTAINER_2D_BASE *aDstContainer, PCB_LAYER_ID aLayerId)
void TransformCircleToPolygon(SHAPE_LINE_CHAIN &aCornerBuffer, const wxPoint &aCenter, int aRadius, int aError, ERROR_LOC aErrorLoc, int aMinSegCount=0)
Convert a circle to a polygon, using multiple straight lines.
double BiuTo3dUnits() const noexcept
Board integer units To 3D units.
void addPadsWithClearance(const FOOTPRINT *aFootprint, CONTAINER_2D_BASE *aDstContainer, PCB_LAYER_ID aLayerId, int aInflateValue, bool aSkipNPTHPadsWihNoCopper, bool aSkipPlatedPads, bool aSkipNonPlatedPads)
double m_biuTo3Dunits
Top (End) Z position of each layer in 3D units.
#define _(s)
constexpr std::size_t arrayDim(T const (&)[N]) noexcept
Returns # of elements in an array.
Definition: arraydim.h:31
void createTrack(const PCB_TRACK *aTrack, CONTAINER_2D_BASE *aDstContainer, int aClearanceValue)
void Simplify(POLYGON_MODE aFastMode)
Handle a list of polygons defining a copper zone.
Definition: zone.h:56
BVH_CONTAINER_2D m_throughHoleAnnularRings
List of through hole inner diameters.
void TransformSolidAreasShapesToPolygon(PCB_LAYER_ID aLayer, SHAPE_POLY_SET &aCornerBuffer, int aError=ARC_HIGH_DEF) const
Convert solid areas full shapes to polygon set (the full shape is the polygon area with a thick outli...
unsigned int m_viaCount
Computed average diameter of the via holes in 3D units.
int m_LineThickness[LAYER_CLASS_COUNT]
void createLayers(REPORTER *aStatusReporter)
LSEQ is a sequence (and therefore also a set) of PCB_LAYER_IDs.
Definition: layer_ids.h:463
bool IsCopperLayer(LAYER_NUM aLayerId)
Tests whether a layer is a copper layer.
Definition: layer_ids.h:787
MAP_POLY m_layers_poly
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
RENDER_ENGINE m_renderEngine
VIATYPE
Definition: pcb_track.h:60
static const wxChar * m_logTrace
Trace mask used to enable or disable the trace output of this class.
unsigned GetRunningMicroSecs()
An alternate way to calculate an elapset time (in microsecondes) to class PROF_COUNTER.
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:64
unsigned int m_copperLayersCount
Scale factor to convert board internal units to 3D units normalized between -1.0 and 1....
Definition: layer_ids.h:70
BVH_CONTAINER_2D m_throughHoleViaIds
Number of copper layers actually used by the board.
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition: typeinfo.h:103
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:96
void BooleanSubtract(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Perform boolean polyset intersection For aFastMode meaning, see function booleanOp.
Definition: pad.h:57
float m_averageViaHoleDiameter
Number of holes in the board.
SHAPE_POLY_SET m_throughHoleOdPolys
Polygon contours for through holes via outer diameters.
class PCB_SHAPE, a segment not on copper layers
Definition: typeinfo.h:90
void Clear() override
DRAWINGS & Drawings()
Definition: board.h:236
PCB_LAYER_ID ToLAYER_ID(int aLayer)
Definition: lset.cpp:905
TRACKS & Tracks()
Definition: board.h:230
SHAPE_POLY_SET m_nonPlatedThroughHoleOdPolys
Polygon contours for through hole outer diameters.
unsigned int m_holeCount
Computed average diameter of the holes in 3D units.
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:113
int GetHolePlatingThickness() const noexcept
Get the current copper layer thickness.