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  // Prepare track list, convert in a vector. Calc statistic for the holes
152  std::vector<const PCB_TRACK*> trackList;
153  trackList.clear();
154  trackList.reserve( m_board->Tracks().size() );
155 
156  for( PCB_TRACK* track : m_board->Tracks() )
157  {
158  if( !Is3dLayerEnabled( track->GetLayer() ) ) // Skip non enabled layers
159  continue;
160 
161  // Note: a PCB_TRACK holds normal segment tracks and also vias circles (that have also
162  // drill values)
163  trackList.push_back( track );
164 
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 += via->GetDrillValue() * m_biuTo3Dunits;
170  }
171  else
172  {
173  m_trackCount++;
174  }
175 
176  m_averageTrackWidth += track->GetWidth() * m_biuTo3Dunits;
177  }
178 
179  if( m_trackCount )
181 
182  if( m_viaCount )
184 
185  // Prepare copper layers index and containers
186  std::vector< PCB_LAYER_ID > layer_id;
187  layer_id.clear();
188  layer_id.reserve( m_copperLayersCount );
189 
190  for( unsigned i = 0; i < arrayDim( cu_seq ); ++i )
191  cu_seq[i] = ToLAYER_ID( B_Cu - i );
192 
193  for( LSEQ cu = cu_set.Seq( cu_seq, arrayDim( cu_seq ) ); cu; ++cu )
194  {
195  const PCB_LAYER_ID curr_layer_id = *cu;
196 
197  if( !Is3dLayerEnabled( curr_layer_id ) ) // Skip non enabled layers
198  continue;
199 
200  layer_id.push_back( curr_layer_id );
201 
202  BVH_CONTAINER_2D *layerContainer = new BVH_CONTAINER_2D;
203  m_layerMap[curr_layer_id] = layerContainer;
204 
207  {
208  SHAPE_POLY_SET* layerPoly = new SHAPE_POLY_SET;
209  m_layers_poly[curr_layer_id] = layerPoly;
210  }
211  }
212 
214  {
217 
220 
221  }
222 
223  if( aStatusReporter )
224  aStatusReporter->Report( _( "Create tracks and vias" ) );
225 
226  // Create tracks as objects and add it to container
227  for( PCB_LAYER_ID curr_layer_id : layer_id )
228  {
229  wxASSERT( m_layerMap.find( curr_layer_id ) != m_layerMap.end() );
230 
231  BVH_CONTAINER_2D *layerContainer = m_layerMap[curr_layer_id];
232 
233  // Add track segments shapes and via annulus shapes
234  unsigned int nTracks = trackList.size();
235 
236  for( unsigned int trackIdx = 0; trackIdx < nTracks; ++trackIdx )
237  {
238  const PCB_TRACK *track = trackList[trackIdx];
239 
240  // NOTE: Vias can be on multiple layers
241  if( !track->IsOnLayer( curr_layer_id ) )
242  continue;
243 
244  // Skip vias annulus when not connected on this layer (if removing is enabled)
245  const PCB_VIA *via = dyn_cast< const PCB_VIA*>( track );
246 
247  if( via && !via->FlashLayer( curr_layer_id ) && IsCopperLayer( curr_layer_id ) )
248  continue;
249 
250  // Add object item to layer container
251  createTrack( track, layerContainer, 0.0f );
252  }
253  }
254 
255  // Create VIAS and THTs objects and add it to holes containers
256  for( PCB_LAYER_ID curr_layer_id : layer_id )
257  {
258  // ADD TRACKS
259  unsigned int nTracks = trackList.size();
260 
261  for( unsigned int trackIdx = 0; trackIdx < nTracks; ++trackIdx )
262  {
263  const PCB_TRACK *track = trackList[trackIdx];
264 
265  if( !track->IsOnLayer( curr_layer_id ) )
266  continue;
267 
268  // ADD VIAS and THT
269  if( track->Type() == PCB_VIA_T )
270  {
271  const PCB_VIA* via = static_cast<const PCB_VIA*>( track );
272  const VIATYPE viatype = via->GetViaType();
273  const float holediameter = via->GetDrillValue() * BiuTo3dUnits();
274 
275  // holes and layer copper extend half info cylinder wall to hide transition
276  const float thickness = GetHolePlatingThickness() * BiuTo3dUnits() / 2.0f;
277  const float hole_inner_radius = holediameter / 2.0f;
278  const float ring_radius = via->GetWidth() * BiuTo3dUnits() / 2.0f;
279 
280  const SFVEC2F via_center( via->GetStart().x * m_biuTo3Dunits,
281  -via->GetStart().y * m_biuTo3Dunits );
282 
283  if( viatype != VIATYPE::THROUGH )
284  {
285 
286  // Add hole objects
287  BVH_CONTAINER_2D *layerHoleContainer = nullptr;
288 
289  // Check if the layer is already created
290  if( m_layerHoleMap.find( curr_layer_id ) == m_layerHoleMap.end() )
291  {
292  // not found, create a new container
293  layerHoleContainer = new BVH_CONTAINER_2D;
294  m_layerHoleMap[curr_layer_id] = layerHoleContainer;
295  }
296  else
297  {
298  // found
299  layerHoleContainer = m_layerHoleMap[curr_layer_id];
300  }
301 
302  // Add a hole for this layer
303  layerHoleContainer->Add( new FILLED_CIRCLE_2D( via_center,
304  hole_inner_radius + thickness,
305  *track ) );
306  }
307  else if( curr_layer_id == layer_id[0] ) // it only adds once the THT holes
308  {
309  // Add through hole object
310  m_throughHoleOds.Add( new FILLED_CIRCLE_2D( via_center,
311  hole_inner_radius + thickness,
312  *track ) );
313  m_throughHoleViaOds.Add( new FILLED_CIRCLE_2D( via_center,
314  hole_inner_radius + thickness,
315  *track ) );
316 
319  {
321  ring_radius,
322  *track ) );
323  }
324 
325  m_throughHoleIds.Add( new FILLED_CIRCLE_2D( via_center, hole_inner_radius,
326  *track ) );
327  }
328  }
329  }
330  }
331 
332  // Create VIAS and THTs objects and add it to holes containers
333  for( PCB_LAYER_ID curr_layer_id : layer_id )
334  {
335  // ADD TRACKS
336  const unsigned int nTracks = trackList.size();
337 
338  for( unsigned int trackIdx = 0; trackIdx < nTracks; ++trackIdx )
339  {
340  const PCB_TRACK *track = trackList[trackIdx];
341 
342  if( !track->IsOnLayer( curr_layer_id ) )
343  continue;
344 
345  // ADD VIAS and THT
346  if( track->Type() == PCB_VIA_T )
347  {
348  const PCB_VIA* via = static_cast<const PCB_VIA*>( track );
349  const VIATYPE viatype = via->GetViaType();
350 
351  if( viatype != VIATYPE::THROUGH )
352  {
353  // Add PCB_VIA hole contours
354 
355  // Add outer holes of VIAs
356  SHAPE_POLY_SET *layerOuterHolesPoly = nullptr;
357  SHAPE_POLY_SET *layerInnerHolesPoly = nullptr;
358 
359  // Check if the layer is already created
360  if( m_layerHoleOdPolys.find( curr_layer_id ) ==
361  m_layerHoleOdPolys.end() )
362  {
363  // not found, create a new container
364  layerOuterHolesPoly = new SHAPE_POLY_SET;
365  m_layerHoleOdPolys[curr_layer_id] = layerOuterHolesPoly;
366 
367  wxASSERT( m_layerHoleIdPolys.find( curr_layer_id ) ==
368  m_layerHoleIdPolys.end() );
369 
370  layerInnerHolesPoly = new SHAPE_POLY_SET;
371  m_layerHoleIdPolys[curr_layer_id] = layerInnerHolesPoly;
372  }
373  else
374  {
375  // found
376  layerOuterHolesPoly = m_layerHoleOdPolys[curr_layer_id];
377 
378  wxASSERT( m_layerHoleIdPolys.find( curr_layer_id ) !=
379  m_layerHoleIdPolys.end() );
380 
381  layerInnerHolesPoly = m_layerHoleIdPolys[curr_layer_id];
382  }
383 
384  const int holediameter = via->GetDrillValue();
385  const int hole_outer_radius = (holediameter / 2) + GetHolePlatingThickness();
386 
387  TransformCircleToPolygon( *layerOuterHolesPoly, via->GetStart(),
388  hole_outer_radius, ARC_HIGH_DEF, ERROR_INSIDE );
389 
390  TransformCircleToPolygon( *layerInnerHolesPoly, via->GetStart(),
391  holediameter / 2, ARC_HIGH_DEF, ERROR_INSIDE );
392  }
393  else if( curr_layer_id == layer_id[0] ) // it only adds once the THT holes
394  {
395  const int holediameter = via->GetDrillValue();
396  const int hole_outer_radius = (holediameter / 2) + GetHolePlatingThickness();
397  const int hole_outer_ring_radius = via->GetWidth() / 2.0f;
398 
399  // Add through hole contours
401  hole_outer_radius, ARC_HIGH_DEF, ERROR_INSIDE );
402 
403  // Add same thing for vias only
405  hole_outer_radius, ARC_HIGH_DEF, ERROR_INSIDE );
406 
408  {
410  via->GetStart(), hole_outer_ring_radius,
411  ARC_HIGH_DEF, ERROR_INSIDE );
412  }
413  }
414  }
415  }
416  }
417 
418  // Creates vertical outline contours of the tracks and add it to the poly of the layer
421  {
422  for( PCB_LAYER_ID curr_layer_id : layer_id )
423  {
424  wxASSERT( m_layers_poly.find( curr_layer_id ) != m_layers_poly.end() );
425 
426  SHAPE_POLY_SET *layerPoly = m_layers_poly[curr_layer_id];
427 
428  // ADD TRACKS
429  unsigned int nTracks = trackList.size();
430 
431  for( unsigned int trackIdx = 0; trackIdx < nTracks; ++trackIdx )
432  {
433  const PCB_TRACK *track = trackList[trackIdx];
434 
435  if( !track->IsOnLayer( curr_layer_id ) )
436  continue;
437 
438  // Skip vias annulus when not connected on this layer (if removing is enabled)
439  const PCB_VIA *via = dyn_cast<const PCB_VIA*>( track );
440 
441  if( via && !via->FlashLayer( curr_layer_id ) && IsCopperLayer( curr_layer_id ) )
442  continue;
443 
444  // Add the track/via contour
445  track->TransformShapeWithClearanceToPolygon( *layerPoly, curr_layer_id, 0,
446  ARC_HIGH_DEF, ERROR_INSIDE );
447  }
448  }
449  }
450 
451  // Add holes of footprints
452  for( FOOTPRINT* footprint : m_board->Footprints() )
453  {
454  for( PAD* pad : footprint->Pads() )
455  {
456  const wxSize padHole = pad->GetDrillSize();
457 
458  if( !padHole.x ) // Not drilled pad like SMD pad
459  continue;
460 
461  // The hole in the body is inflated by copper thickness, if not plated, no copper
462  const int inflate = ( pad->GetAttribute () != PAD_ATTRIB::NPTH ) ?
463  GetHolePlatingThickness() / 2 : 0;
464 
465  m_holeCount++;
466  m_averageHoleDiameter += ( ( pad->GetDrillSize().x +
467  pad->GetDrillSize().y ) / 2.0f ) * m_biuTo3Dunits;
468 
470 
472  {
474  }
475 
477  }
478  }
479 
480  if( m_holeCount )
482 
483  // Add contours of the pad holes (pads can be Circle or Segment holes)
484  for( FOOTPRINT* footprint : m_board->Footprints() )
485  {
486  for( PAD* pad : footprint->Pads() )
487  {
488  const wxSize padHole = pad->GetDrillSize();
489 
490  if( !padHole.x ) // Not drilled pad like SMD pad
491  continue;
492 
493  // The hole in the body is inflated by copper thickness.
494  const int inflate = GetHolePlatingThickness();
495 
496  if( pad->GetAttribute () != PAD_ATTRIB::NPTH )
497  {
499  {
500  pad->TransformHoleWithClearanceToPolygon( m_throughHoleAnnularRingPolys,
501  inflate, ARC_HIGH_DEF, ERROR_INSIDE );
502  }
503 
504  pad->TransformHoleWithClearanceToPolygon( m_throughHoleOdPolys, inflate,
505  ARC_HIGH_DEF, ERROR_INSIDE );
506  }
507  else
508  {
509  // If not plated, no copper.
511  {
512  pad->TransformHoleWithClearanceToPolygon( m_throughHoleAnnularRingPolys, 0,
513  ARC_HIGH_DEF, ERROR_INSIDE );
514  }
515 
516  pad->TransformHoleWithClearanceToPolygon( m_nonPlatedThroughHoleOdPolys, 0,
517  ARC_HIGH_DEF, ERROR_INSIDE );
518  }
519  }
520  }
521 
522  const bool renderPlatedPadsAsPlated = GetFlag( FL_RENDER_PLATED_PADS_AS_PLATED ) &&
524 
525  // Add footprints PADs objects to containers
526  for( PCB_LAYER_ID curr_layer_id : layer_id )
527  {
528  wxASSERT( m_layerMap.find( curr_layer_id ) != m_layerMap.end() );
529 
530  BVH_CONTAINER_2D *layerContainer = m_layerMap[curr_layer_id];
531 
532  // ADD PADS
533  for( FOOTPRINT* footprint : m_board->Footprints() )
534  {
535  // Note: NPTH pads are not drawn on copper layers when the pad
536  // has same shape as its hole
537  addPadsWithClearance( footprint, layerContainer, curr_layer_id, 0,
538  true, renderPlatedPadsAsPlated, false );
539 
540  // Micro-wave footprints may have items on copper layers
541  addFootprintShapesWithClearance( footprint, layerContainer, curr_layer_id, 0 );
542  }
543  }
544 
545  if( renderPlatedPadsAsPlated )
546  {
547  // ADD PLATED PADS
548  for( FOOTPRINT* footprint : m_board->Footprints() )
549  {
550  addPadsWithClearance( footprint, m_platedPadsFront, F_Cu, 0, true, false, true );
551 
552  addPadsWithClearance( footprint, m_platedPadsBack, B_Cu, 0, true, false, true );
553  }
554 
557  }
558 
559  // Add footprints PADs poly contours (vertical outlines)
562  {
563  for( PCB_LAYER_ID curr_layer_id : layer_id )
564  {
565  wxASSERT( m_layers_poly.find( curr_layer_id ) != m_layers_poly.end() );
566 
567  SHAPE_POLY_SET *layerPoly = m_layers_poly[curr_layer_id];
568 
569  // Add pads to polygon list
570  for( FOOTPRINT* footprint : m_board->Footprints() )
571  {
572  // Note: NPTH pads are not drawn on copper layers when the pad
573  // has same shape as its hole
574  footprint->TransformPadsWithClearanceToPolygon( *layerPoly, curr_layer_id,
575  0, ARC_HIGH_DEF, ERROR_INSIDE,
576  true, renderPlatedPadsAsPlated,
577  false );
578 
579  transformFPShapesToPolygon( footprint, curr_layer_id, *layerPoly );
580  }
581  }
582 
583  if( renderPlatedPadsAsPlated )
584  {
585  // ADD PLATED PADS contours
586  for( FOOTPRINT* footprint : m_board->Footprints() )
587  {
588  footprint->TransformPadsWithClearanceToPolygon( *m_frontPlatedPadPolys, F_Cu,
589  0, ARC_HIGH_DEF, ERROR_INSIDE,
590  true, false, true );
591 
592  footprint->TransformPadsWithClearanceToPolygon( *m_backPlatedPadPolys, B_Cu,
593  0, ARC_HIGH_DEF, ERROR_INSIDE,
594  true, false, true );
595  }
596  }
597  }
598 
599  // Add graphic item on copper layers to object containers
600  for( PCB_LAYER_ID curr_layer_id : layer_id )
601  {
602  wxASSERT( m_layerMap.find( curr_layer_id ) != m_layerMap.end() );
603 
604  BVH_CONTAINER_2D *layerContainer = m_layerMap[curr_layer_id];
605 
606  // Add graphic items on copper layers (texts and other graphics)
607  for( BOARD_ITEM* item : m_board->Drawings() )
608  {
609  if( !item->IsOnLayer( curr_layer_id ) )
610  continue;
611 
612  switch( item->Type() )
613  {
614  case PCB_SHAPE_T:
615  addShapeWithClearance( static_cast<PCB_SHAPE*>( item ), layerContainer,
616  curr_layer_id, 0 );
617  break;
618 
619  case PCB_TEXT_T:
620  addShapeWithClearance( static_cast<PCB_TEXT*>( item ), layerContainer,
621  curr_layer_id, 0 );
622  break;
623 
624  case PCB_DIM_ALIGNED_T:
625  case PCB_DIM_CENTER_T:
627  case PCB_DIM_LEADER_T:
628  addShapeWithClearance( static_cast<PCB_DIMENSION_BASE*>( item ),
629  layerContainer, curr_layer_id, 0 );
630  break;
631 
632  default:
633  wxLogTrace( m_logTrace, wxT( "createLayers: item type: %d not implemented" ),
634  item->Type() );
635  break;
636  }
637  }
638  }
639 
640  // Add graphic item on copper layers to poly contours (vertical outlines)
643  {
644  for( PCB_LAYER_ID cur_layer_id : layer_id )
645  {
646  wxASSERT( m_layers_poly.find( cur_layer_id ) != m_layers_poly.end() );
647 
648  SHAPE_POLY_SET *layerPoly = m_layers_poly[cur_layer_id];
649 
650  // Add graphic items on copper layers (texts and other )
651  for( BOARD_ITEM* item : m_board->Drawings() )
652  {
653  if( !item->IsOnLayer( cur_layer_id ) )
654  continue;
655 
656  switch( item->Type() )
657  {
658  case PCB_SHAPE_T:
659  item->TransformShapeWithClearanceToPolygon( *layerPoly, cur_layer_id, 0,
660  ARC_HIGH_DEF, ERROR_INSIDE );
661  break;
662 
663  case PCB_TEXT_T:
664  {
665  PCB_TEXT* text = static_cast<PCB_TEXT*>( item );
666 
667  text->TransformTextShapeWithClearanceToPolygon( *layerPoly, cur_layer_id, 0,
668  ARC_HIGH_DEF, ERROR_INSIDE );
669  }
670  break;
671 
672  default:
673  wxLogTrace( m_logTrace, wxT( "createLayers: item type: %d not implemented" ),
674  item->Type() );
675  break;
676  }
677  }
678  }
679  }
680 
681  if( GetFlag( FL_ZONE ) )
682  {
683  if( aStatusReporter )
684  aStatusReporter->Report( _( "Create zones" ) );
685 
686  std::vector<std::pair<const ZONE*, PCB_LAYER_ID>> zones;
687 
688  for( ZONE* zone : m_board->Zones() )
689  {
690  for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
691  zones.emplace_back( std::make_pair( zone, layer ) );
692  }
693 
694  // Add zones objects
695  std::atomic<size_t> nextZone( 0 );
696  std::atomic<size_t> threadsFinished( 0 );
697 
698  size_t parallelThreadCount = std::max<size_t>( std::thread::hardware_concurrency(), 2 );
699 
700  for( size_t ii = 0; ii < parallelThreadCount; ++ii )
701  {
702  std::thread t = std::thread( [&]()
703  {
704  for( size_t areaId = nextZone.fetch_add( 1 );
705  areaId < zones.size();
706  areaId = nextZone.fetch_add( 1 ) )
707  {
708  const ZONE* zone = zones[areaId].first;
709 
710  if( zone == nullptr )
711  break;
712 
713  PCB_LAYER_ID layer = zones[areaId].second;
714 
715  auto layerContainer = m_layerMap.find( layer );
716 
717  if( layerContainer != m_layerMap.end() )
718  addSolidAreasShapes( zone, layerContainer->second, layer );
719  }
720 
721  threadsFinished++;
722  } );
723 
724  t.detach();
725  }
726 
727  while( threadsFinished < parallelThreadCount )
728  std::this_thread::sleep_for( std::chrono::milliseconds( 10 ) );
729 
730  }
731 
734  {
735  // Add copper zones
736  for( ZONE* zone : m_board->Zones() )
737  {
738  if( zone == nullptr )
739  break;
740 
741  for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
742  {
743  auto layerContainer = m_layers_poly.find( layer );
744 
745  if( layerContainer != m_layers_poly.end() )
746  zone->TransformSolidAreasShapesToPolygon( layer, *layerContainer->second );
747  }
748  }
749  }
750 
751  // Simplify layer polygons
752 
753  if( aStatusReporter )
754  aStatusReporter->Report( _( "Simplifying copper layers polygons" ) );
755 
758  {
760  {
761  if( m_frontPlatedPadPolys && ( m_layers_poly.find( F_Cu ) != m_layers_poly.end() ) )
762  {
763  SHAPE_POLY_SET *layerPoly_F_Cu = m_layers_poly[F_Cu];
765 
767  }
768 
769  if( m_backPlatedPadPolys && ( m_layers_poly.find( B_Cu ) != m_layers_poly.end() ) )
770  {
771  SHAPE_POLY_SET *layerPoly_B_Cu = m_layers_poly[B_Cu];
773 
775  }
776  }
777 
778  std::vector< PCB_LAYER_ID > &selected_layer_id = layer_id;
779  std::vector< PCB_LAYER_ID > layer_id_without_F_and_B;
780 
782  {
783  layer_id_without_F_and_B.clear();
784  layer_id_without_F_and_B.reserve( layer_id.size() );
785 
786  for( size_t i = 0; i < layer_id.size(); ++i )
787  {
788  if( ( layer_id[i] != F_Cu ) && ( layer_id[i] != B_Cu ) )
789  layer_id_without_F_and_B.push_back( layer_id[i] );
790  }
791 
792  selected_layer_id = layer_id_without_F_and_B;
793  }
794 
795  if( selected_layer_id.size() > 0 )
796  {
797  std::atomic<size_t> nextItem( 0 );
798  std::atomic<size_t> threadsFinished( 0 );
799 
800  size_t parallelThreadCount = std::min<size_t>(
801  std::max<size_t>( std::thread::hardware_concurrency(), 2 ),
802  selected_layer_id.size() );
803 
804  for( size_t ii = 0; ii < parallelThreadCount; ++ii )
805  {
806  std::thread t = std::thread(
807  [&nextItem, &threadsFinished, &selected_layer_id, this]()
808  {
809  for( size_t i = nextItem.fetch_add( 1 );
810  i < selected_layer_id.size();
811  i = nextItem.fetch_add( 1 ) )
812  {
813  auto layerPoly = m_layers_poly.find( selected_layer_id[i] );
814 
815  if( layerPoly != m_layers_poly.end() )
816  // This will make a union of all added contours
817  layerPoly->second->Simplify( SHAPE_POLY_SET::PM_FAST );
818  }
819 
820  threadsFinished++;
821  } );
822 
823  t.detach();
824  }
825 
826  while( threadsFinished < parallelThreadCount )
827  std::this_thread::sleep_for( std::chrono::milliseconds( 10 ) );
828  }
829  }
830 
831  // Simplify holes polygon contours
832  if( aStatusReporter )
833  aStatusReporter->Report( _( "Simplify holes contours" ) );
834 
835  for( PCB_LAYER_ID layer : layer_id )
836  {
837  if( m_layerHoleOdPolys.find( layer ) != m_layerHoleOdPolys.end() )
838  {
839  // found
840  SHAPE_POLY_SET *polyLayer = m_layerHoleOdPolys[layer];
841  polyLayer->Simplify( SHAPE_POLY_SET::PM_FAST );
842 
843  wxASSERT( m_layerHoleIdPolys.find( layer ) != m_layerHoleIdPolys.end() );
844 
845  polyLayer = m_layerHoleIdPolys[layer];
846  polyLayer->Simplify( SHAPE_POLY_SET::PM_FAST );
847  }
848  }
849 
850  // End Build Copper layers
851 
852  // This will make a union of all added contours
857 
858  // Build Tech layers
859  // Based on:
860  // https://github.com/KiCad/kicad-source-mirror/blob/master/3d-viewer/3d_draw.cpp#L1059
861  if( aStatusReporter )
862  aStatusReporter->Report( _( "Build Tech layers" ) );
863 
864  // draw graphic items, on technical layers
865  static const PCB_LAYER_ID teckLayerList[] = {
866  B_Adhes,
867  F_Adhes,
868  B_Paste,
869  F_Paste,
870  B_SilkS,
871  F_SilkS,
872  B_Mask,
873  F_Mask,
874 
875  // Aux Layers
876  Dwgs_User,
877  Cmts_User,
878  Eco1_User,
879  Eco2_User,
880  Edge_Cuts,
881  Margin
882  };
883 
884  // User layers are not drawn here, only technical layers
885  for( LSEQ seq = LSET::AllNonCuMask().Seq( teckLayerList, arrayDim( teckLayerList ) );
886  seq;
887  ++seq )
888  {
889  const PCB_LAYER_ID curr_layer_id = *seq;
890 
891  if( !Is3dLayerEnabled( curr_layer_id ) )
892  continue;
893 
894  BVH_CONTAINER_2D *layerContainer = new BVH_CONTAINER_2D;
895  m_layerMap[curr_layer_id] = layerContainer;
896 
897  SHAPE_POLY_SET *layerPoly = new SHAPE_POLY_SET;
898  m_layers_poly[curr_layer_id] = layerPoly;
899 
900  // Add drawing objects
901  for( BOARD_ITEM* item : m_board->Drawings() )
902  {
903  if( !item->IsOnLayer( curr_layer_id ) )
904  continue;
905 
906  switch( item->Type() )
907  {
908  case PCB_SHAPE_T:
909  addShapeWithClearance( static_cast<PCB_SHAPE*>( item ), layerContainer,
910  curr_layer_id, 0 );
911  break;
912 
913  case PCB_TEXT_T:
914  addShapeWithClearance( static_cast<PCB_TEXT*>( item ), layerContainer,
915  curr_layer_id, 0 );
916  break;
917 
918  case PCB_DIM_ALIGNED_T:
919  case PCB_DIM_CENTER_T:
921  case PCB_DIM_LEADER_T:
922  addShapeWithClearance( static_cast<PCB_DIMENSION_BASE*>( item ), layerContainer,
923  curr_layer_id, 0 );
924  break;
925 
926  default:
927  break;
928  }
929  }
930 
931  // Add drawing contours
932  for( BOARD_ITEM* item : m_board->Drawings() )
933  {
934  if( !item->IsOnLayer( curr_layer_id ) )
935  continue;
936 
937  switch( item->Type() )
938  {
939  case PCB_SHAPE_T:
940  item->TransformShapeWithClearanceToPolygon( *layerPoly, curr_layer_id, 0,
941  ARC_HIGH_DEF, ERROR_INSIDE );
942  break;
943 
944  case PCB_TEXT_T:
945  {
946  PCB_TEXT* text = static_cast<PCB_TEXT*>( item );
947 
948  text->TransformTextShapeWithClearanceToPolygon( *layerPoly, curr_layer_id, 0,
949  ARC_HIGH_DEF, ERROR_INSIDE );
950  }
951  break;
952 
953  default:
954  break;
955  }
956  }
957 
958  // Add footprints tech layers - objects
959  for( FOOTPRINT* footprint : m_board->Footprints() )
960  {
961  if( ( curr_layer_id == F_SilkS ) || ( curr_layer_id == B_SilkS ) )
962  {
964 
965  for( PAD* pad : footprint->Pads() )
966  {
967  if( !pad->IsOnLayer( curr_layer_id ) )
968  continue;
969 
970  buildPadOutlineAsSegments( pad, layerContainer, linewidth );
971  }
972  }
973  else
974  {
975  addPadsWithClearance( footprint, layerContainer, curr_layer_id, 0,
976  false, false, false );
977  }
978 
979  addFootprintShapesWithClearance( footprint, layerContainer, curr_layer_id, 0 );
980  }
981 
982 
983  // Add footprints tech layers - contours
984  for( FOOTPRINT* footprint : m_board->Footprints() )
985  {
986  if( ( curr_layer_id == F_SilkS ) || ( curr_layer_id == B_SilkS ) )
987  {
989 
990  for( PAD* pad : footprint->Pads() )
991  {
992  if( !pad->IsOnLayer( curr_layer_id ) )
993  continue;
994 
995  buildPadOutlineAsPolygon( pad, *layerPoly, linewidth );
996  }
997  }
998  else
999  {
1000  footprint->TransformPadsWithClearanceToPolygon( *layerPoly, curr_layer_id, 0,
1001  ARC_HIGH_DEF, ERROR_INSIDE );
1002  }
1003 
1004  // On tech layers, use a poor circle approximation, only for texts (stroke font)
1005  footprint->TransformFPTextWithClearanceToPolygonSet( *layerPoly, curr_layer_id, 0,
1006  ARC_HIGH_DEF, ERROR_INSIDE );
1007 
1008  // Add the remaining things with dynamic seg count for circles
1009  transformFPShapesToPolygon( footprint, curr_layer_id, *layerPoly );
1010  }
1011 
1012 
1013  // Draw non copper zones
1014  if( GetFlag( FL_ZONE ) )
1015  {
1016  for( ZONE* zone : m_board->Zones() )
1017  {
1018  if( zone->IsOnLayer( curr_layer_id ) )
1019  addSolidAreasShapes( zone, layerContainer, curr_layer_id );
1020  }
1021 
1022  for( ZONE* zone : m_board->Zones() )
1023  {
1024  if( zone->IsOnLayer( curr_layer_id ) )
1025  zone->TransformSolidAreasShapesToPolygon( curr_layer_id, *layerPoly );
1026  }
1027  }
1028 
1029  // This will make a union of all added contours
1030  layerPoly->Simplify( SHAPE_POLY_SET::PM_FAST );
1031  }
1032  // End Build Tech layers
1033 
1034  // Build BVH (Bounding volume hierarchy) for holes and vias
1035 
1036  if( aStatusReporter )
1037  aStatusReporter->Report( _( "Build BVH for holes and vias" ) );
1038 
1042 
1043  if( !m_layerHoleMap.empty() )
1044  {
1045  for( auto& hole : m_layerHoleMap )
1046  hole.second->BuildBVH();
1047  }
1048 
1049  // We only need the Solder mask to initialize the BVH
1050  // because..?
1051  if( m_layerMap[B_Mask] )
1052  m_layerMap[B_Mask]->BuildBVH();
1053 
1054  if( m_layerMap[F_Mask] )
1055  m_layerMap[F_Mask]->BuildBVH();
1056 }
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:64
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:588
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
PCB_LAYER_ID
A quick note on layer IDs:
void addShapeWithClearance(const PCB_TEXT *aText, CONTAINER_2D_BASE *aDstContainer, PCB_LAYER_ID aLayerId, int aClearanceValue)
LSET is a set of PCB_LAYER_IDs.
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
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)
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:57
BVH_CONTAINER_2D m_throughHoleAnnularRings
List of through hole inner diameters.
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.
MAP_POLY m_layers_poly
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.
unsigned int m_copperLayersCount
Scale factor to convert board internal units to 3D units normalized between -1.0 and 1....
BVH_CONTAINER_2D m_throughHoleViaIds
Number of copper layers actually used by the board.
void TransformCircleToPolygon(SHAPE_LINE_CHAIN &aCornerBuffer, wxPoint aCenter, int aRadius, int aError, ERROR_LOC aErrorLoc)
Function TransformCircleToPolygon convert a circle to a polygon, using multiple straight lines.
bool IsCopperLayer(LAYER_NUM aLayerId)
Tests whether a layer is a copper layer.
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.