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