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 <[email protected]>
5  * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24 
34 #include "board_adapter.h"
35 #include "../3d_rendering/raytracing/shapes2D/ring_2d.h"
36 #include "../3d_rendering/raytracing/shapes2D/filled_circle_2d.h"
37 #include "../3d_rendering/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 
209  {
210  SHAPE_POLY_SET* layerPoly = new SHAPE_POLY_SET;
211  m_layers_poly[curr_layer_id] = layerPoly;
212  }
213  }
214 
216  {
219 
222 
223  }
224 
225  if( aStatusReporter )
226  aStatusReporter->Report( _( "Create tracks and vias" ) );
227 
228  // Create tracks as objects and add it to container
229  for( PCB_LAYER_ID curr_layer_id : layer_id )
230  {
231  wxASSERT( m_layerMap.find( curr_layer_id ) != m_layerMap.end() );
232 
233  BVH_CONTAINER_2D *layerContainer = m_layerMap[curr_layer_id];
234 
235  // Add track segments shapes and via annulus shapes
236  unsigned int nTracks = trackList.size();
237 
238  for( unsigned int trackIdx = 0; trackIdx < nTracks; ++trackIdx )
239  {
240  const PCB_TRACK *track = trackList[trackIdx];
241 
242  // NOTE: Vias can be on multiple layers
243  if( !track->IsOnLayer( curr_layer_id ) )
244  continue;
245 
246  // Skip vias annulus when not connected on this layer (if removing is enabled)
247  const PCB_VIA *via = dyn_cast< const PCB_VIA*>( track );
248 
249  if( via && !via->FlashLayer( curr_layer_id ) && IsCopperLayer( curr_layer_id ) )
250  continue;
251 
252  // Add object item to layer container
253  createTrack( track, layerContainer, 0.0f );
254  }
255  }
256 
257  // Create VIAS and THTs objects and add it to holes containers
258  for( PCB_LAYER_ID curr_layer_id : layer_id )
259  {
260  // ADD TRACKS
261  unsigned int nTracks = trackList.size();
262 
263  for( unsigned int trackIdx = 0; trackIdx < nTracks; ++trackIdx )
264  {
265  const PCB_TRACK *track = trackList[trackIdx];
266 
267  if( !track->IsOnLayer( curr_layer_id ) )
268  continue;
269 
270  // ADD VIAS and THT
271  if( track->Type() == PCB_VIA_T )
272  {
273  const PCB_VIA* via = static_cast<const PCB_VIA*>( track );
274  const VIATYPE viatype = via->GetViaType();
275  const float holediameter = via->GetDrillValue() * BiuTo3dUnits();
276 
277  // holes and layer copper extend half info cylinder wall to hide transition
278  const float thickness = GetHolePlatingThickness() * BiuTo3dUnits() / 2.0f;
279  const float hole_inner_radius = holediameter / 2.0f;
280  const float ring_radius = via->GetWidth() * BiuTo3dUnits() / 2.0f;
281 
282  const SFVEC2F via_center( via->GetStart().x * m_biuTo3Dunits,
283  -via->GetStart().y * m_biuTo3Dunits );
284 
285  if( viatype != VIATYPE::THROUGH )
286  {
287 
288  // Add hole objects
289  BVH_CONTAINER_2D *layerHoleContainer = nullptr;
290 
291  // Check if the layer is already created
292  if( m_layerHoleMap.find( curr_layer_id ) == m_layerHoleMap.end() )
293  {
294  // not found, create a new container
295  layerHoleContainer = new BVH_CONTAINER_2D;
296  m_layerHoleMap[curr_layer_id] = layerHoleContainer;
297  }
298  else
299  {
300  // found
301  layerHoleContainer = m_layerHoleMap[curr_layer_id];
302  }
303 
304  // Add a hole for this layer
305  layerHoleContainer->Add( new FILLED_CIRCLE_2D( via_center,
306  hole_inner_radius + thickness,
307  *track ) );
308  }
309  else if( curr_layer_id == layer_id[0] ) // it only adds once the THT holes
310  {
311  // Add through hole object
312  m_throughHoleOds.Add( new FILLED_CIRCLE_2D( via_center,
313  hole_inner_radius + thickness,
314  *track ) );
315  m_throughHoleViaOds.Add( new FILLED_CIRCLE_2D( via_center,
316  hole_inner_radius + thickness,
317  *track ) );
318 
321  {
323  ring_radius,
324  *track ) );
325  }
326 
327  m_throughHoleIds.Add( new FILLED_CIRCLE_2D( via_center, hole_inner_radius,
328  *track ) );
329  }
330  }
331  }
332  }
333 
334  // Create VIAS and THTs objects and add it to holes containers
335  for( PCB_LAYER_ID curr_layer_id : layer_id )
336  {
337  // ADD TRACKS
338  const unsigned int nTracks = trackList.size();
339 
340  for( unsigned int trackIdx = 0; trackIdx < nTracks; ++trackIdx )
341  {
342  const PCB_TRACK *track = trackList[trackIdx];
343 
344  if( !track->IsOnLayer( curr_layer_id ) )
345  continue;
346 
347  // ADD VIAS and THT
348  if( track->Type() == PCB_VIA_T )
349  {
350  const PCB_VIA* via = static_cast<const PCB_VIA*>( track );
351  const VIATYPE viatype = via->GetViaType();
352 
353  if( viatype != VIATYPE::THROUGH )
354  {
355  // Add PCB_VIA hole contours
356 
357  // Add outer holes of VIAs
358  SHAPE_POLY_SET *layerOuterHolesPoly = nullptr;
359  SHAPE_POLY_SET *layerInnerHolesPoly = nullptr;
360 
361  // Check if the layer is already created
362  if( m_layerHoleOdPolys.find( curr_layer_id ) ==
363  m_layerHoleOdPolys.end() )
364  {
365  // not found, create a new container
366  layerOuterHolesPoly = new SHAPE_POLY_SET;
367  m_layerHoleOdPolys[curr_layer_id] = layerOuterHolesPoly;
368 
369  wxASSERT( m_layerHoleIdPolys.find( curr_layer_id ) ==
370  m_layerHoleIdPolys.end() );
371 
372  layerInnerHolesPoly = new SHAPE_POLY_SET;
373  m_layerHoleIdPolys[curr_layer_id] = layerInnerHolesPoly;
374  }
375  else
376  {
377  // found
378  layerOuterHolesPoly = m_layerHoleOdPolys[curr_layer_id];
379 
380  wxASSERT( m_layerHoleIdPolys.find( curr_layer_id ) !=
381  m_layerHoleIdPolys.end() );
382 
383  layerInnerHolesPoly = m_layerHoleIdPolys[curr_layer_id];
384  }
385 
386  const int holediameter = via->GetDrillValue();
387  const int hole_outer_radius = (holediameter / 2) + GetHolePlatingThickness();
388 
389  TransformCircleToPolygon( *layerOuterHolesPoly, via->GetStart(),
390  hole_outer_radius, ARC_HIGH_DEF, ERROR_INSIDE );
391 
392  TransformCircleToPolygon( *layerInnerHolesPoly, via->GetStart(),
393  holediameter / 2, ARC_HIGH_DEF, ERROR_INSIDE );
394  }
395  else if( curr_layer_id == layer_id[0] ) // it only adds once the THT holes
396  {
397  const int holediameter = via->GetDrillValue();
398  const int hole_outer_radius = (holediameter / 2) + GetHolePlatingThickness();
399  const int hole_outer_ring_radius = via->GetWidth() / 2.0f;
400 
401  // Add through hole contours
403  hole_outer_radius, ARC_HIGH_DEF, ERROR_INSIDE );
404 
405  // Add same thing for vias only
407  hole_outer_radius, ARC_HIGH_DEF, ERROR_INSIDE );
408 
410  {
412  via->GetStart(), hole_outer_ring_radius,
413  ARC_HIGH_DEF, ERROR_INSIDE );
414  }
415  }
416  }
417  }
418  }
419 
420  // Creates vertical outline contours of the tracks and add it to the poly of the layer
422  {
423  for( PCB_LAYER_ID curr_layer_id : layer_id )
424  {
425  wxASSERT( m_layers_poly.find( curr_layer_id ) != m_layers_poly.end() );
426 
427  SHAPE_POLY_SET *layerPoly = m_layers_poly[curr_layer_id];
428 
429  // ADD TRACKS
430  unsigned int nTracks = trackList.size();
431 
432  for( unsigned int trackIdx = 0; trackIdx < nTracks; ++trackIdx )
433  {
434  const PCB_TRACK *track = trackList[trackIdx];
435 
436  if( !track->IsOnLayer( curr_layer_id ) )
437  continue;
438 
439  // Skip vias annulus when not connected on this layer (if removing is enabled)
440  const PCB_VIA *via = dyn_cast<const PCB_VIA*>( track );
441 
442  if( via && !via->FlashLayer( curr_layer_id ) && IsCopperLayer( curr_layer_id ) )
443  continue;
444 
445  // Add the track/via contour
446  track->TransformShapeWithClearanceToPolygon( *layerPoly, curr_layer_id, 0,
447  ARC_HIGH_DEF, ERROR_INSIDE );
448  }
449  }
450  }
451 
452  // Add holes of footprints
453  for( FOOTPRINT* footprint : m_board->Footprints() )
454  {
455  for( PAD* pad : footprint->Pads() )
456  {
457  const wxSize padHole = pad->GetDrillSize();
458 
459  if( !padHole.x ) // Not drilled pad like SMD pad
460  continue;
461 
462  // The hole in the body is inflated by copper thickness, if not plated, no copper
463  const int inflate = ( pad->GetAttribute () != PAD_ATTRIB::NPTH ) ?
464  GetHolePlatingThickness() / 2 : 0;
465 
466  m_holeCount++;
467  m_averageHoleDiameter += ( ( pad->GetDrillSize().x +
468  pad->GetDrillSize().y ) / 2.0f ) * m_biuTo3Dunits;
469 
471 
473  {
475  }
476 
478  }
479  }
480 
481  if( m_holeCount )
483 
484  // Add contours of the pad holes (pads can be Circle or Segment holes)
485  for( FOOTPRINT* footprint : m_board->Footprints() )
486  {
487  for( PAD* pad : footprint->Pads() )
488  {
489  const wxSize padHole = pad->GetDrillSize();
490 
491  if( !padHole.x ) // Not drilled pad like SMD pad
492  continue;
493 
494  // The hole in the body is inflated by copper thickness.
495  const int inflate = GetHolePlatingThickness();
496 
497  if( pad->GetAttribute () != PAD_ATTRIB::NPTH )
498  {
500  {
501  pad->TransformHoleWithClearanceToPolygon( m_throughHoleAnnularRingPolys,
502  inflate, ARC_HIGH_DEF, ERROR_INSIDE );
503  }
504 
505  pad->TransformHoleWithClearanceToPolygon( m_throughHoleOdPolys, inflate,
506  ARC_HIGH_DEF, ERROR_INSIDE );
507  }
508  else
509  {
510  // If not plated, no copper.
512  {
513  pad->TransformHoleWithClearanceToPolygon( m_throughHoleAnnularRingPolys, 0,
514  ARC_HIGH_DEF, ERROR_INSIDE );
515  }
516 
517  pad->TransformHoleWithClearanceToPolygon( m_nonPlatedThroughHoleOdPolys, 0,
518  ARC_HIGH_DEF, ERROR_INSIDE );
519  }
520  }
521  }
522 
523  const bool renderPlatedPadsAsPlated = GetFlag( FL_RENDER_PLATED_PADS_AS_PLATED ) &&
525 
526  // Add footprints PADs objects to containers
527  for( PCB_LAYER_ID curr_layer_id : layer_id )
528  {
529  wxASSERT( m_layerMap.find( curr_layer_id ) != m_layerMap.end() );
530 
531  BVH_CONTAINER_2D *layerContainer = m_layerMap[curr_layer_id];
532 
533  // ADD PADS
534  for( FOOTPRINT* footprint : m_board->Footprints() )
535  {
536  // Note: NPTH pads are not drawn on copper layers when the pad
537  // has same shape as its hole
538  addPadsWithClearance( footprint, layerContainer, curr_layer_id, 0,
539  true, renderPlatedPadsAsPlated, false );
540 
541  // Micro-wave footprints may have items on copper layers
542  addFootprintShapesWithClearance( footprint, layerContainer, curr_layer_id, 0 );
543  }
544  }
545 
546  if( renderPlatedPadsAsPlated )
547  {
548  // ADD PLATED PADS
549  for( FOOTPRINT* footprint : m_board->Footprints() )
550  {
551  addPadsWithClearance( footprint, m_platedPadsFront, F_Cu, 0, true, false, true );
552  addPadsWithClearance( footprint, m_platedPadsBack, B_Cu, 0, true, false, true );
553  }
554 
557  }
558 
559  // Add footprints PADs poly contours (vertical outlines)
561  {
562  for( PCB_LAYER_ID curr_layer_id : layer_id )
563  {
564  wxASSERT( m_layers_poly.find( curr_layer_id ) != m_layers_poly.end() );
565 
566  SHAPE_POLY_SET *layerPoly = m_layers_poly[curr_layer_id];
567 
568  // Add pads to polygon list
569  for( FOOTPRINT* footprint : m_board->Footprints() )
570  {
571  // Note: NPTH pads are not drawn on copper layers when the pad has same shape as
572  // its hole
573  footprint->TransformPadsWithClearanceToPolygon( *layerPoly, curr_layer_id,
574  0, ARC_HIGH_DEF, ERROR_INSIDE,
575  true, renderPlatedPadsAsPlated,
576  false );
577 
578  transformFPShapesToPolygon( footprint, curr_layer_id, *layerPoly );
579  }
580  }
581 
582  if( renderPlatedPadsAsPlated )
583  {
584  // ADD PLATED PADS contours
585  for( FOOTPRINT* footprint : m_board->Footprints() )
586  {
587  footprint->TransformPadsWithClearanceToPolygon( *m_frontPlatedPadPolys, F_Cu,
588  0, ARC_HIGH_DEF, ERROR_INSIDE,
589  true, false, true );
590 
591  footprint->TransformPadsWithClearanceToPolygon( *m_backPlatedPadPolys, B_Cu,
592  0, ARC_HIGH_DEF, ERROR_INSIDE,
593  true, false, true );
594  }
595  }
596  }
597 
598  // Add graphic item on copper layers to object containers
599  for( PCB_LAYER_ID curr_layer_id : layer_id )
600  {
601  wxASSERT( m_layerMap.find( curr_layer_id ) != m_layerMap.end() );
602 
603  BVH_CONTAINER_2D *layerContainer = m_layerMap[curr_layer_id];
604 
605  // Add graphic items on copper layers (texts and other graphics)
606  for( BOARD_ITEM* item : m_board->Drawings() )
607  {
608  if( !item->IsOnLayer( curr_layer_id ) )
609  continue;
610 
611  switch( item->Type() )
612  {
613  case PCB_SHAPE_T:
614  addShapeWithClearance( static_cast<PCB_SHAPE*>( item ), layerContainer,
615  curr_layer_id, 0 );
616  break;
617 
618  case PCB_TEXT_T:
619  addShapeWithClearance( static_cast<PCB_TEXT*>( item ), layerContainer,
620  curr_layer_id, 0 );
621  break;
622 
623  case PCB_DIM_ALIGNED_T:
624  case PCB_DIM_CENTER_T:
626  case PCB_DIM_LEADER_T:
627  addShapeWithClearance( static_cast<PCB_DIMENSION_BASE*>( item ),
628  layerContainer, curr_layer_id, 0 );
629  break;
630 
631  default:
632  wxLogTrace( m_logTrace, wxT( "createLayers: item type: %d not implemented" ),
633  item->Type() );
634  break;
635  }
636  }
637  }
638 
639  // Add graphic item on copper layers to poly contours (vertical outlines)
641  {
642  for( PCB_LAYER_ID cur_layer_id : layer_id )
643  {
644  wxASSERT( m_layers_poly.find( cur_layer_id ) != m_layers_poly.end() );
645 
646  SHAPE_POLY_SET *layerPoly = m_layers_poly[cur_layer_id];
647 
648  // Add graphic items on copper layers (texts and other )
649  for( BOARD_ITEM* item : m_board->Drawings() )
650  {
651  if( !item->IsOnLayer( cur_layer_id ) )
652  continue;
653 
654  switch( item->Type() )
655  {
656  case PCB_SHAPE_T:
657  item->TransformShapeWithClearanceToPolygon( *layerPoly, cur_layer_id, 0,
658  ARC_HIGH_DEF, ERROR_INSIDE );
659  break;
660 
661  case PCB_TEXT_T:
662  {
663  PCB_TEXT* text = static_cast<PCB_TEXT*>( item );
664 
665  text->TransformTextShapeWithClearanceToPolygon( *layerPoly, cur_layer_id, 0,
666  ARC_HIGH_DEF, ERROR_INSIDE );
667  }
668  break;
669 
670  default:
671  wxLogTrace( m_logTrace, wxT( "createLayers: item type: %d not implemented" ),
672  item->Type() );
673  break;
674  }
675  }
676  }
677  }
678 
679  if( GetFlag( FL_ZONE ) )
680  {
681  if( aStatusReporter )
682  aStatusReporter->Report( _( "Create zones" ) );
683 
684  std::vector<std::pair<ZONE*, PCB_LAYER_ID>> zones;
685  std::unordered_map<PCB_LAYER_ID, std::unique_ptr<std::mutex>> layer_lock;
686 
687  for( ZONE* zone : m_board->Zones() )
688  {
689  for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
690  {
691  zones.emplace_back( std::make_pair( zone, layer ) );
692  layer_lock.emplace( layer, std::make_unique<std::mutex>() );
693  }
694  }
695 
696  // Add zones objects
697  std::atomic<size_t> nextZone( 0 );
698  std::atomic<size_t> threadsFinished( 0 );
699 
700  size_t parallelThreadCount = std::min<size_t>( zones.size(),
701  std::max<size_t>( std::thread::hardware_concurrency(), 2 ) );
702 
703  for( size_t ii = 0; ii < parallelThreadCount; ++ii )
704  {
705  std::thread t = std::thread( [&]()
706  {
707  for( size_t areaId = nextZone.fetch_add( 1 );
708  areaId < zones.size();
709  areaId = nextZone.fetch_add( 1 ) )
710  {
711  ZONE* zone = zones[areaId].first;
712 
713  if( zone == nullptr )
714  break;
715 
716  PCB_LAYER_ID layer = zones[areaId].second;
717 
718  auto layerContainer = m_layerMap.find( layer );
719  auto layerPolyContainer = m_layers_poly.find( layer );
720 
721  if( layerContainer != m_layerMap.end() )
722  {
723  addSolidAreasShapes( zone, layerContainer->second, layer );
724  }
725 
728  && layerPolyContainer != m_layers_poly.end() )
729  {
730  auto mut_it = layer_lock.find( layer );
731 
732  std::lock_guard< std::mutex > lock( *( mut_it->second ) );
733  zone->TransformSolidAreasShapesToPolygon( layer, *layerPolyContainer->second );
734  }
735  }
736 
737  threadsFinished++;
738  } );
739 
740  t.detach();
741  }
742 
743  while( threadsFinished < parallelThreadCount )
744  std::this_thread::sleep_for( std::chrono::milliseconds( 10 ) );
745 
746  }
747 
748  // Simplify layer polygons
749 
750  if( aStatusReporter )
751  aStatusReporter->Report( _( "Simplifying copper layers polygons" ) );
752 
754  {
756  {
757  if( m_frontPlatedPadPolys && ( m_layers_poly.find( F_Cu ) != m_layers_poly.end() ) )
758  {
759  if( aStatusReporter )
760  aStatusReporter->Report( _( "Simplifying polygons on F_Cu" ) );
761 
762  SHAPE_POLY_SET *layerPoly_F_Cu = m_layers_poly[F_Cu];
764 
766  }
767 
768  if( m_backPlatedPadPolys && ( m_layers_poly.find( B_Cu ) != m_layers_poly.end() ) )
769  {
770  if( aStatusReporter )
771  aStatusReporter->Report( _( "Simplifying polygons on B_Cu" ) );
772 
773  SHAPE_POLY_SET *layerPoly_B_Cu = m_layers_poly[B_Cu];
775 
777  }
778  }
779 
780  std::vector< PCB_LAYER_ID > &selected_layer_id = layer_id;
781  std::vector< PCB_LAYER_ID > layer_id_without_F_and_B;
782 
784  {
785  layer_id_without_F_and_B.clear();
786  layer_id_without_F_and_B.reserve( layer_id.size() );
787 
788  for( size_t i = 0; i < layer_id.size(); ++i )
789  {
790  if( ( layer_id[i] != F_Cu ) && ( layer_id[i] != B_Cu ) )
791  layer_id_without_F_and_B.push_back( layer_id[i] );
792  }
793 
794  selected_layer_id = layer_id_without_F_and_B;
795  }
796 
797  if( selected_layer_id.size() > 0 )
798  {
799  if( aStatusReporter )
800  aStatusReporter->Report( wxString::Format(
801  _( "Simplifying %d copper layers" ),
802  (int)selected_layer_id.size() ) );
803 
804  std::atomic<size_t> nextItem( 0 );
805  std::atomic<size_t> threadsFinished( 0 );
806 
807  size_t parallelThreadCount = std::min<size_t>(
808  std::max<size_t>( std::thread::hardware_concurrency(), 2 ),
809  selected_layer_id.size() );
810 
811  for( size_t ii = 0; ii < parallelThreadCount; ++ii )
812  {
813  std::thread t = std::thread(
814  [&nextItem, &threadsFinished, &selected_layer_id, this]()
815  {
816  for( size_t i = nextItem.fetch_add( 1 );
817  i < selected_layer_id.size();
818  i = nextItem.fetch_add( 1 ) )
819  {
820  auto layerPoly = m_layers_poly.find( selected_layer_id[i] );
821 
822  if( layerPoly != m_layers_poly.end() )
823  // This will make a union of all added contours
824  layerPoly->second->Simplify( SHAPE_POLY_SET::PM_FAST );
825  }
826 
827  threadsFinished++;
828  } );
829 
830  t.detach();
831  }
832 
833  while( threadsFinished < parallelThreadCount )
834  std::this_thread::sleep_for( std::chrono::milliseconds( 10 ) );
835  }
836  }
837 
838  // Simplify holes polygon contours
839  if( aStatusReporter )
840  aStatusReporter->Report( _( "Simplify holes contours" ) );
841 
842  for( PCB_LAYER_ID layer : layer_id )
843  {
844  if( m_layerHoleOdPolys.find( layer ) != m_layerHoleOdPolys.end() )
845  {
846  // found
847  SHAPE_POLY_SET *polyLayer = m_layerHoleOdPolys[layer];
848  polyLayer->Simplify( SHAPE_POLY_SET::PM_FAST );
849 
850  wxASSERT( m_layerHoleIdPolys.find( layer ) != m_layerHoleIdPolys.end() );
851 
852  polyLayer = m_layerHoleIdPolys[layer];
853  polyLayer->Simplify( SHAPE_POLY_SET::PM_FAST );
854  }
855  }
856 
857  // End Build Copper layers
858 
859  // This will make a union of all added contours
864 
865  // Build Tech layers
866  // Based on:
867  // https://github.com/KiCad/kicad-source-mirror/blob/master/3d-viewer/3d_draw.cpp#L1059
868  if( aStatusReporter )
869  aStatusReporter->Report( _( "Build Tech layers" ) );
870 
871  // draw graphic items, on technical layers
872  static const PCB_LAYER_ID teckLayerList[] = {
873  B_Adhes,
874  F_Adhes,
875  B_Paste,
876  F_Paste,
877  B_SilkS,
878  F_SilkS,
879  B_Mask,
880  F_Mask,
881 
882  // Aux Layers
883  Dwgs_User,
884  Cmts_User,
885  Eco1_User,
886  Eco2_User,
887  Edge_Cuts,
888  Margin
889  };
890 
891  // User layers are not drawn here, only technical layers
892  for( LSEQ seq = LSET::AllNonCuMask().Seq( teckLayerList, arrayDim( teckLayerList ) );
893  seq;
894  ++seq )
895  {
896  const PCB_LAYER_ID curr_layer_id = *seq;
897 
898  if( !Is3dLayerEnabled( curr_layer_id ) )
899  continue;
900 
901  if( aStatusReporter )
902  aStatusReporter->Report( wxString::Format(
903  _( "Build Tech layer %d" ), (int)curr_layer_id ) );
904 
905  BVH_CONTAINER_2D *layerContainer = new BVH_CONTAINER_2D;
906  m_layerMap[curr_layer_id] = layerContainer;
907 
908  SHAPE_POLY_SET *layerPoly = new SHAPE_POLY_SET;
909  m_layers_poly[curr_layer_id] = layerPoly;
910 
911  // Add drawing objects
912  for( BOARD_ITEM* item : m_board->Drawings() )
913  {
914  if( !item->IsOnLayer( curr_layer_id ) )
915  continue;
916 
917  switch( item->Type() )
918  {
919  case PCB_SHAPE_T:
920  addShapeWithClearance( static_cast<PCB_SHAPE*>( item ), layerContainer,
921  curr_layer_id, 0 );
922  break;
923 
924  case PCB_TEXT_T:
925  addShapeWithClearance( static_cast<PCB_TEXT*>( item ), layerContainer,
926  curr_layer_id, 0 );
927  break;
928 
929  case PCB_DIM_ALIGNED_T:
930  case PCB_DIM_CENTER_T:
932  case PCB_DIM_LEADER_T:
933  addShapeWithClearance( static_cast<PCB_DIMENSION_BASE*>( item ), layerContainer,
934  curr_layer_id, 0 );
935  break;
936 
937  default:
938  break;
939  }
940  }
941 
942  // Add drawing contours
943  for( BOARD_ITEM* item : m_board->Drawings() )
944  {
945  if( !item->IsOnLayer( curr_layer_id ) )
946  continue;
947 
948  switch( item->Type() )
949  {
950  case PCB_SHAPE_T:
951  item->TransformShapeWithClearanceToPolygon( *layerPoly, curr_layer_id, 0,
952  ARC_HIGH_DEF, ERROR_INSIDE );
953  break;
954 
955  case PCB_TEXT_T:
956  {
957  PCB_TEXT* text = static_cast<PCB_TEXT*>( item );
958 
959  text->TransformTextShapeWithClearanceToPolygon( *layerPoly, curr_layer_id, 0,
960  ARC_HIGH_DEF, ERROR_INSIDE );
961  }
962  break;
963 
964  default:
965  break;
966  }
967  }
968 
969  // Add footprints tech layers - objects
970  for( FOOTPRINT* footprint : m_board->Footprints() )
971  {
972  if( ( curr_layer_id == F_SilkS ) || ( curr_layer_id == B_SilkS ) )
973  {
975 
976  for( PAD* pad : footprint->Pads() )
977  {
978  if( !pad->IsOnLayer( curr_layer_id ) )
979  continue;
980 
981  buildPadOutlineAsSegments( pad, layerContainer, linewidth );
982  }
983  }
984  else
985  {
986  addPadsWithClearance( footprint, layerContainer, curr_layer_id, 0,
987  false, false, false );
988  }
989 
990  addFootprintShapesWithClearance( footprint, layerContainer, curr_layer_id, 0 );
991  }
992 
993 
994  // Add footprints tech layers - contours
995  for( FOOTPRINT* footprint : m_board->Footprints() )
996  {
997  if( ( curr_layer_id == F_SilkS ) || ( curr_layer_id == B_SilkS ) )
998  {
1000 
1001  for( PAD* pad : footprint->Pads() )
1002  {
1003  if( !pad->IsOnLayer( curr_layer_id ) )
1004  continue;
1005 
1006  buildPadOutlineAsPolygon( pad, *layerPoly, linewidth );
1007  }
1008  }
1009  else
1010  {
1011  footprint->TransformPadsWithClearanceToPolygon( *layerPoly, curr_layer_id, 0,
1012  ARC_HIGH_DEF, ERROR_INSIDE );
1013  }
1014 
1015  // On tech layers, use a poor circle approximation, only for texts (stroke font)
1016  footprint->TransformFPTextWithClearanceToPolygonSet( *layerPoly, curr_layer_id, 0,
1017  ARC_HIGH_DEF, ERROR_INSIDE );
1018 
1019  // Add the remaining things with dynamic seg count for circles
1020  transformFPShapesToPolygon( footprint, curr_layer_id, *layerPoly );
1021  }
1022 
1023 
1024  // Draw non copper zones
1025  if( GetFlag( FL_ZONE ) )
1026  {
1027  for( ZONE* zone : m_board->Zones() )
1028  {
1029  if( zone->IsOnLayer( curr_layer_id ) )
1030  addSolidAreasShapes( zone, layerContainer, curr_layer_id );
1031  }
1032 
1033  for( ZONE* zone : m_board->Zones() )
1034  {
1035  if( zone->IsOnLayer( curr_layer_id ) )
1036  zone->TransformSolidAreasShapesToPolygon( curr_layer_id, *layerPoly );
1037  }
1038  }
1039 
1040  // This will make a union of all added contours
1041  layerPoly->Simplify( SHAPE_POLY_SET::PM_FAST );
1042  }
1043  // End Build Tech layers
1044 
1045  // Build BVH (Bounding volume hierarchy) for holes and vias
1046 
1047  if( aStatusReporter )
1048  aStatusReporter->Report( _( "Build BVH for holes and vias" ) );
1049 
1053 
1054  if( !m_layerHoleMap.empty() )
1055  {
1056  for( auto& hole : m_layerHoleMap )
1057  hole.second->BuildBVH();
1058  }
1059 
1060  // We only need the Solder mask to initialize the BVH
1061  // because..?
1062  if( m_layerMap[B_Mask] )
1063  m_layerMap[B_Mask]->BuildBVH();
1064 
1065  if( m_layerMap[F_Mask] )
1066  m_layerMap[F_Mask]->BuildBVH();
1067 }
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:759
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:240
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:49
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:782
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...
Definition: pcb_track.cpp:1032
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:590
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:516
virtual bool IsOnLayer(PCB_LAYER_ID aLayer) const
Test to see if this object is on the given layer.
Definition: board_item.h:195
unsigned int m_trackCount
Track average width.
glm::vec2 SFVEC2F
Definition: xv3d_types.h:42
#define MAX_CU_LAYERS
Definition: layer_ids.h:147
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:234
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...
Definition: zone.cpp:1377
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:477
bool IsCopperLayer(LAYER_NUM aLayerId)
Tests whether a layer is a copper layer.
Definition: layer_ids.h:808
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 elapsed time (in microsecondes) to class PROF_COUNTER.
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:65
unsigned int m_copperLayersCount
Scale factor to convert board internal units to 3D units normalized between -1.0 and 1....
Definition: layer_ids.h:71
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:237
PCB_LAYER_ID ToLAYER_ID(int aLayer)
Definition: lset.cpp:914
TRACKS & Tracks()
Definition: board.h:231
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:112
int GetHolePlatingThickness() const noexcept
Get the current copper layer thickness.