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 
33 #include "board_adapter.h"
34 #include "../3d_rendering/3d_render_raytracing/shapes2D/cring2d.h"
35 #include "../3d_rendering/3d_render_raytracing/shapes2D/cfilledcircle2d.h"
36 #include "../3d_rendering/3d_render_raytracing/shapes3D/ccylinder.h"
37 
38 #include <class_board.h>
39 #include <class_module.h>
40 #include <class_pad.h>
41 #include <pcb_text.h>
42 #include <fp_shape.h>
43 #include <class_zone.h>
45 #include <trigo.h>
46 #include <vector>
47 #include <thread>
48 #include <algorithm>
49 #include <atomic>
50 
51 #ifdef PRINT_STATISTICS_3D_VIEWER
52 #include <profile.h>
53 #endif
54 
55 
57 {
58  if( !m_layers_poly.empty() )
59  {
60  for( auto& poly : m_layers_poly )
61  delete poly.second;
62 
63  m_layers_poly.clear();
64  }
65 
67  m_F_Cu_PlatedPads_poly = nullptr;
68 
70  m_B_Cu_PlatedPads_poly = nullptr;
71 
72  if( !m_layers_inner_holes_poly.empty() )
73  {
74  for( auto& poly : m_layers_inner_holes_poly )
75  delete poly.second;
76 
78  }
79 
80  if( !m_layers_outer_holes_poly.empty() )
81  {
82  for( auto& poly : m_layers_outer_holes_poly )
83  delete poly.second;
84 
86  }
87 
88  if( !m_layers_container2D.empty() )
89  {
90  for( auto& poly : m_layers_container2D )
91  delete poly.second;
92 
93  m_layers_container2D.clear();
94  }
95 
98 
101 
102  if( !m_layers_holes2D.empty() )
103  {
104  for( auto& poly : m_layers_holes2D )
105  delete poly.second;
106 
107  m_layers_holes2D.clear();
108  }
109 
117 
120 }
121 
122 
123 void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
124 {
125  destroyLayers();
126 
127  // Build Copper layers
128  // Based on: https://github.com/KiCad/kicad-source-mirror/blob/master/3d-viewer/3d_draw.cpp#L692
129  // /////////////////////////////////////////////////////////////////////////
130 
131 #ifdef PRINT_STATISTICS_3D_VIEWER
132  unsigned stats_startCopperLayersTime = GetRunningMicroSecs();
133 
134  unsigned start_Time = stats_startCopperLayersTime;
135 #endif
136 
137  PCB_LAYER_ID cu_seq[MAX_CU_LAYERS];
139 
140  m_stats_nr_tracks = 0;
142  m_stats_nr_vias = 0;
144  m_stats_nr_holes = 0;
146 
147  // Prepare track list, convert in a vector. Calc statistic for the holes
148  // /////////////////////////////////////////////////////////////////////////
149  std::vector< const TRACK *> trackList;
150  trackList.clear();
151  trackList.reserve( m_board->Tracks().size() );
152 
153  for( TRACK* track : m_board->Tracks() )
154  {
155  if( !Is3DLayerEnabled( track->GetLayer() ) ) // Skip non enabled layers
156  continue;
157 
158  // Note: a TRACK holds normal segment tracks and
159  // also vias circles (that have also drill values)
160  trackList.push_back( track );
161 
162  if( track->Type() == PCB_VIA_T )
163  {
164  const VIA *via = static_cast< const VIA*>( track );
165  m_stats_nr_vias++;
167  }
168  else
169  {
171  }
172 
173  m_stats_track_med_width += track->GetWidth() * m_biuTo3Dunits;
174  }
175 
176  if( m_stats_nr_tracks )
178 
179  if( m_stats_nr_vias )
181 
182  // Prepare copper layers index and containers
183  // /////////////////////////////////////////////////////////////////////////
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  CBVHCONTAINER2D *layerContainer = new CBVHCONTAINER2D;
201  m_layers_container2D[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  // /////////////////////////////////////////////////////////////////////////
226  for( PCB_LAYER_ID curr_layer_id : layer_id )
227  {
228  wxASSERT( m_layers_container2D.find( curr_layer_id ) != m_layers_container2D.end() );
229 
230  CBVHCONTAINER2D *layerContainer = m_layers_container2D[curr_layer_id];
231 
232  // Add track segments shapes and via annulus shapes
233  unsigned int nTracks = trackList.size();
234 
235  for( unsigned int trackIdx = 0; trackIdx < nTracks; ++trackIdx )
236  {
237  const TRACK *track = trackList[trackIdx];
238 
239  // NOTE: Vias can be on multiple layers
240  if( !track->IsOnLayer( curr_layer_id ) )
241  continue;
242 
243  // Skip vias annulus when not connected on this layer (if removing is enabled)
244  const VIA *via = dyn_cast< const VIA*>( track );
245 
246  if( via && !via->FlashLayer( curr_layer_id ) && IsCopperLayer( curr_layer_id ) )
247  continue;
248 
249  // Add object item to layer container
250  createNewTrack( track, layerContainer, 0.0f );
251  }
252  }
253 
254  // Create VIAS and THTs objects and add it to holes containers
255  // /////////////////////////////////////////////////////////////////////////
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 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 VIA* via = static_cast<const VIA*>( track );
272  const VIATYPE viatype = via->GetViaType();
273  const float holediameter = via->GetDrillValue() * BiuTo3Dunits();
274  const float thickness = GetCopperThickness3DU();
275  const float hole_inner_radius = ( holediameter / 2.0f );
276  const float ring_radius = via->GetWidth() * BiuTo3Dunits() / 2.0f;
277 
278  const SFVEC2F via_center(
279  via->GetStart().x * m_biuTo3Dunits, -via->GetStart().y * m_biuTo3Dunits );
280 
281  if( viatype != VIATYPE::THROUGH )
282  {
283 
284  // Add hole objects
285  // /////////////////////////////////////////////////////////
286 
287  CBVHCONTAINER2D *layerHoleContainer = NULL;
288 
289  // Check if the layer is already created
290  if( m_layers_holes2D.find( curr_layer_id ) == m_layers_holes2D.end() )
291  {
292  // not found, create a new container
293  layerHoleContainer = new CBVHCONTAINER2D;
294  m_layers_holes2D[curr_layer_id] = layerHoleContainer;
295  }
296  else
297  {
298  // found
299  layerHoleContainer = m_layers_holes2D[curr_layer_id];
300  }
301 
302  // Add a hole for this layer
303  layerHoleContainer->Add( new CFILLEDCIRCLE2D( 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  // /////////////////////////////////////////////////////////
311  m_through_holes_outer.Add( new CFILLEDCIRCLE2D( via_center,
312  hole_inner_radius + thickness,
313  *track ) );
315  new CFILLEDCIRCLE2D( via_center,
316  hole_inner_radius + thickness,
317  *track ) );
318 
320  {
322  ring_radius,
323  *track ) );
324  }
325 
326  m_through_holes_inner.Add( new CFILLEDCIRCLE2D( via_center,
327  hole_inner_radius,
328  *track ) );
329 
330  //m_through_holes_vias_inner.Add( new CFILLEDCIRCLE2D( via_center,
331  // hole_inner_radius,
332  // *track ) );
333  }
334  }
335  }
336  }
337 
338  // Create VIAS and THTs objects and add it to holes containers
339  // /////////////////////////////////////////////////////////////////////////
340  for( PCB_LAYER_ID curr_layer_id : layer_id )
341  {
342  // ADD TRACKS
343  const unsigned int nTracks = trackList.size();
344 
345  for( unsigned int trackIdx = 0; trackIdx < nTracks; ++trackIdx )
346  {
347  const TRACK *track = trackList[trackIdx];
348 
349  if( !track->IsOnLayer( curr_layer_id ) )
350  continue;
351 
352  // ADD VIAS and THT
353  if( track->Type() == PCB_VIA_T )
354  {
355  const VIA *via = static_cast< const VIA*>( track );
356  const VIATYPE viatype = via->GetViaType();
357 
358  if( viatype != VIATYPE::THROUGH )
359  {
360  // Add VIA hole contourns
361 
362  // Add outer holes of VIAs
363  SHAPE_POLY_SET *layerOuterHolesPoly = NULL;
364  SHAPE_POLY_SET *layerInnerHolesPoly = NULL;
365 
366  // Check if the layer is already created
367  if( m_layers_outer_holes_poly.find( curr_layer_id ) ==
369  {
370  // not found, create a new container
371  layerOuterHolesPoly = new SHAPE_POLY_SET;
372  m_layers_outer_holes_poly[curr_layer_id] = layerOuterHolesPoly;
373 
374  wxASSERT( m_layers_inner_holes_poly.find( curr_layer_id ) ==
376 
377  layerInnerHolesPoly = new SHAPE_POLY_SET;
378  m_layers_inner_holes_poly[curr_layer_id] = layerInnerHolesPoly;
379  }
380  else
381  {
382  // found
383  layerOuterHolesPoly = m_layers_outer_holes_poly[curr_layer_id];
384 
385  wxASSERT( m_layers_inner_holes_poly.find( curr_layer_id ) !=
387 
388  layerInnerHolesPoly = m_layers_inner_holes_poly[curr_layer_id];
389  }
390 
391  const int holediameter = via->GetDrillValue();
392  const int hole_outer_radius = (holediameter / 2) + GetHolePlatingThicknessBIU();
393 
394  TransformCircleToPolygon( *layerOuterHolesPoly, via->GetStart(),
395  hole_outer_radius, ARC_HIGH_DEF, ERROR_INSIDE );
396 
397  TransformCircleToPolygon( *layerInnerHolesPoly, via->GetStart(),
398  holediameter / 2, ARC_HIGH_DEF, ERROR_INSIDE );
399  }
400  else if( curr_layer_id == layer_id[0] ) // it only adds once the THT holes
401  {
402  const int holediameter = via->GetDrillValue();
403  const int hole_outer_radius = (holediameter / 2) + GetHolePlatingThicknessBIU();
404  const int hole_outer_ring_radius = via->GetWidth() / 2.0f;
405 
406  // Add through hole contourns
407  // /////////////////////////////////////////////////////////
409  hole_outer_radius, ARC_HIGH_DEF, ERROR_INSIDE );
410 
411  // Add same thing for vias only
412 
414  hole_outer_radius, ARC_HIGH_DEF, ERROR_INSIDE );
415 
417  {
419  via->GetStart(), hole_outer_ring_radius,
420  ARC_HIGH_DEF, ERROR_INSIDE );
421  }
422  }
423  }
424  }
425  }
426 
427  // Creates vertical outline contours of the tracks and add it to the poly of the layer
428  // /////////////////////////////////////////////////////////////////////////
431  {
432  for( PCB_LAYER_ID curr_layer_id : layer_id )
433  {
434  wxASSERT( m_layers_poly.find( curr_layer_id ) != m_layers_poly.end() );
435 
436  SHAPE_POLY_SET *layerPoly = m_layers_poly[curr_layer_id];
437 
438  // ADD TRACKS
439  unsigned int nTracks = trackList.size();
440 
441  for( unsigned int trackIdx = 0; trackIdx < nTracks; ++trackIdx )
442  {
443  const TRACK *track = trackList[trackIdx];
444 
445  if( !track->IsOnLayer( curr_layer_id ) )
446  continue;
447 
448  // Skip vias annulus when not connected on this layer (if removing is enabled)
449  const VIA *via = dyn_cast< const VIA*>( track );
450 
451  if( via && !via->FlashLayer( curr_layer_id ) && IsCopperLayer( curr_layer_id ) )
452  continue;
453 
454  // Add the track/via contour
455  track->TransformShapeWithClearanceToPolygon( *layerPoly, curr_layer_id, 0,
456  ARC_HIGH_DEF, ERROR_INSIDE );
457  }
458  }
459  }
460 
461  // Add holes of footprints
462  // /////////////////////////////////////////////////////////////////////////
463  for( MODULE* module : m_board->Modules() )
464  {
465  for( D_PAD* pad : module->Pads() )
466  {
467  const wxSize padHole = pad->GetDrillSize();
468 
469  if( !padHole.x ) // Not drilled pad like SMD pad
470  continue;
471 
472  // The hole in the body is inflated by copper thickness, if not plated, no copper
473  const int inflate = ( pad->GetAttribute () != PAD_ATTRIB_NPTH ) ?
475 
477  m_stats_hole_med_diameter += ( ( pad->GetDrillSize().x +
478  pad->GetDrillSize().y ) / 2.0f ) * m_biuTo3Dunits;
479 
480  m_through_holes_outer.Add( createNewPadDrill( pad, inflate ) );
481 
483  {
485  }
486 
488  }
489  }
490 
491  if( m_stats_nr_holes )
493 
494  // Add contours of the pad holes (pads can be Circle or Segment holes)
495  // /////////////////////////////////////////////////////////////////////////
496  for( MODULE* module : m_board->Modules() )
497  {
498  for( D_PAD* pad : module->Pads() )
499  {
500  const wxSize padHole = pad->GetDrillSize();
501 
502  if( !padHole.x ) // Not drilled pad like SMD pad
503  continue;
504 
505  // The hole in the body is inflated by copper thickness.
506  const int inflate = GetHolePlatingThicknessBIU();
507 
508  if( pad->GetAttribute () != PAD_ATTRIB_NPTH )
509  {
511  {
512  pad->TransformHoleWithClearanceToPolygon( m_through_outer_ring_holes_poly,
513  inflate,
514  ARC_HIGH_DEF, ERROR_INSIDE );
515  }
516 
517  pad->TransformHoleWithClearanceToPolygon( m_through_outer_holes_poly, inflate,
518  ARC_HIGH_DEF, ERROR_INSIDE );
519  }
520  else
521  {
522  // If not plated, no copper.
524  {
525  pad->TransformHoleWithClearanceToPolygon( m_through_outer_ring_holes_poly, 0,
526  ARC_HIGH_DEF, ERROR_INSIDE );
527  }
528 
529  pad->TransformHoleWithClearanceToPolygon( m_through_outer_holes_poly_NPTH, 0,
530  ARC_HIGH_DEF, ERROR_INSIDE );
531  }
532  }
533  }
534 
535  const bool renderPlatedPadsAsPlated = GetFlag( FL_RENDER_PLATED_PADS_AS_PLATED );
536 
537  // Add footprints PADs objects to containers
538  for( PCB_LAYER_ID curr_layer_id : layer_id )
539  {
540  wxASSERT( m_layers_container2D.find( curr_layer_id ) != m_layers_container2D.end() );
541 
542  CBVHCONTAINER2D *layerContainer = m_layers_container2D[curr_layer_id];
543 
544  // ADD PADS
545  for( MODULE* module : m_board->Modules() )
546  {
547  // Note: NPTH pads are not drawn on copper layers when the pad
548  // has same shape as its hole
549  AddPadsShapesWithClearanceToContainer( module, layerContainer, curr_layer_id, 0,
550  true, renderPlatedPadsAsPlated, false );
551 
552  // Micro-wave footprints may have items on copper layers
553  AddGraphicsShapesWithClearanceToContainer( module, layerContainer, curr_layer_id, 0 );
554  }
555  }
556 
557  if( renderPlatedPadsAsPlated )
558  {
559  // ADD PLATED PADS
560  for( MODULE* module : m_board->Modules() )
561  {
563  true, false, true );
564 
566  true, false, true );
567  }
568  }
569 
570  // Add footprints PADs poly contourns (vertical outlines)
573  {
574  for( PCB_LAYER_ID curr_layer_id : layer_id )
575  {
576  wxASSERT( m_layers_poly.find( curr_layer_id ) != m_layers_poly.end() );
577 
578  SHAPE_POLY_SET *layerPoly = m_layers_poly[curr_layer_id];
579 
580  // Add pads to polygon list
581  for( auto module : m_board->Modules() )
582  {
583  // Note: NPTH pads are not drawn on copper layers when the pad
584  // has same shape as its hole
585  module->TransformPadsShapesWithClearanceToPolygon( *layerPoly, curr_layer_id,
586  0, ARC_HIGH_DEF, ERROR_INSIDE,
587  true, renderPlatedPadsAsPlated,
588  false );
589 
590  transformGraphicModuleEdgeToPolygonSet( module, curr_layer_id, *layerPoly );
591  }
592  }
593 
594  if( renderPlatedPadsAsPlated )
595  {
596  // ADD PLATED PADS contourns
597  for( auto module : m_board->Modules() )
598  {
599  module->TransformPadsShapesWithClearanceToPolygon( *m_F_Cu_PlatedPads_poly, F_Cu,
600  0, ARC_HIGH_DEF, ERROR_INSIDE,
601  true, false, true );
602 
603  //transformGraphicModuleEdgeToPolygonSet( module, F_Cu, *m_F_Cu_PlatedPads_poly );
604 
605  module->TransformPadsShapesWithClearanceToPolygon( *m_B_Cu_PlatedPads_poly, B_Cu,
606  0, ARC_HIGH_DEF, ERROR_INSIDE,
607  true, false, true );
608 
609  //transformGraphicModuleEdgeToPolygonSet( module, B_Cu, *m_B_Cu_PlatedPads_poly );
610  }
611  }
612  }
613 
614 
615  // Add graphic item on copper layers to object containers
616  for( PCB_LAYER_ID curr_layer_id : layer_id )
617  {
618  wxASSERT( m_layers_container2D.find( curr_layer_id ) != m_layers_container2D.end() );
619 
620  CBVHCONTAINER2D *layerContainer = m_layers_container2D[curr_layer_id];
621 
622  // Add graphic items on copper layers (texts and other graphics)
623  for( auto item : m_board->Drawings() )
624  {
625  if( !item->IsOnLayer( curr_layer_id ) )
626  continue;
627 
628  switch( item->Type() )
629  {
630  case PCB_SHAPE_T:
631  {
632  AddShapeWithClearanceToContainer( (PCB_SHAPE*)item, layerContainer, curr_layer_id,
633  0 );
634  }
635  break;
636 
637  case PCB_TEXT_T:
638  AddShapeWithClearanceToContainer( (PCB_TEXT*) item, layerContainer, curr_layer_id,
639  0 );
640  break;
641 
642  case PCB_DIM_ALIGNED_T:
643  case PCB_DIM_CENTER_T:
645  case PCB_DIM_LEADER_T:
646  AddShapeWithClearanceToContainer( (DIMENSION*) item, layerContainer, curr_layer_id,
647  0 );
648  break;
649 
650  default:
651  wxLogTrace( m_logTrace,
652  wxT( "createLayers: item type: %d not implemented" ),
653  item->Type() );
654  break;
655  }
656  }
657  }
658 
659  // Add graphic item on copper layers to poly contourns (vertical outlines)
662  {
663  for( PCB_LAYER_ID cur_layer_id : layer_id )
664  {
665  wxASSERT( m_layers_poly.find( cur_layer_id ) != m_layers_poly.end() );
666 
667  SHAPE_POLY_SET *layerPoly = m_layers_poly[cur_layer_id];
668 
669  // Add graphic items on copper layers (texts and other )
670  for( BOARD_ITEM* item : m_board->Drawings() )
671  {
672  if( !item->IsOnLayer( cur_layer_id ) )
673  continue;
674 
675  switch( item->Type() )
676  {
677  case PCB_SHAPE_T:
678  ( (PCB_SHAPE*) item )->TransformShapeWithClearanceToPolygon( *layerPoly,
679  cur_layer_id, 0,
680  ARC_HIGH_DEF,
681  ERROR_INSIDE );
682  break;
683 
684  case PCB_TEXT_T:
685  ( (PCB_TEXT*) item )->TransformShapeWithClearanceToPolygonSet( *layerPoly, 0,
686  ARC_HIGH_DEF,
687  ERROR_INSIDE );
688  break;
689 
690  default:
691  wxLogTrace( m_logTrace, wxT( "createLayers: item type: %d not implemented" ),
692  item->Type() );
693  break;
694  }
695  }
696  }
697  }
698 
699  if( GetFlag( FL_ZONE ) )
700  {
701  if( aStatusReporter )
702  aStatusReporter->Report( _( "Create zones" ) );
703 
704  std::vector<std::pair<const ZONE_CONTAINER*, PCB_LAYER_ID>> zones;
705 
706  for( ZONE_CONTAINER* zone : m_board->Zones() )
707  {
708  for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
709  zones.emplace_back( std::make_pair( zone, layer ) );
710  }
711 
712  // Add zones objects
713  // /////////////////////////////////////////////////////////////////////
714  std::atomic<size_t> nextZone( 0 );
715  std::atomic<size_t> threadsFinished( 0 );
716 
717  size_t parallelThreadCount = std::max<size_t>( std::thread::hardware_concurrency(), 2 );
718  for( size_t ii = 0; ii < parallelThreadCount; ++ii )
719  {
720  std::thread t = std::thread( [&]()
721  {
722  for( size_t areaId = nextZone.fetch_add( 1 );
723  areaId < zones.size();
724  areaId = nextZone.fetch_add( 1 ) )
725  {
726  const ZONE_CONTAINER* zone = zones[areaId].first;
727 
728  if( zone == nullptr )
729  break;
730 
731  PCB_LAYER_ID layer = zones[areaId].second;
732 
733  auto layerContainer = m_layers_container2D.find( layer );
734 
735  if( layerContainer != m_layers_container2D.end() )
736  AddSolidAreasShapesToContainer( zone, layerContainer->second, layer );
737  }
738 
739  threadsFinished++;
740  } );
741 
742  t.detach();
743  }
744 
745  while( threadsFinished < parallelThreadCount )
746  std::this_thread::sleep_for( std::chrono::milliseconds( 10 ) );
747 
748  }
749 
752  {
753  // Add copper zones
754  for( ZONE_CONTAINER* zone : m_board->Zones() )
755  {
756  if( zone == nullptr )
757  break;
758 
759  for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
760  {
761  auto layerContainer = m_layers_poly.find( layer );
762 
763  if( layerContainer != m_layers_poly.end() )
764  zone->TransformSolidAreasShapesToPolygon( layer, *layerContainer->second );
765  }
766  }
767  }
768 
769  // Simplify layer polygons
770 
771  if( aStatusReporter )
772  aStatusReporter->Report( _( "Simplifying copper layers polygons" ) );
773 
776  {
778  {
779  if( m_F_Cu_PlatedPads_poly && ( m_layers_poly.find( F_Cu ) != m_layers_poly.end() ) )
780  {
781  SHAPE_POLY_SET *layerPoly_F_Cu = m_layers_poly[F_Cu];
782  layerPoly_F_Cu->BooleanSubtract( *m_F_Cu_PlatedPads_poly, SHAPE_POLY_SET::POLYGON_MODE::PM_FAST );
783 
785  }
786 
787  if( m_B_Cu_PlatedPads_poly && ( m_layers_poly.find( B_Cu ) != m_layers_poly.end() ) )
788  {
789  SHAPE_POLY_SET *layerPoly_B_Cu = m_layers_poly[B_Cu];
790  layerPoly_B_Cu->BooleanSubtract( *m_B_Cu_PlatedPads_poly, SHAPE_POLY_SET::POLYGON_MODE::PM_FAST );
791 
793  }
794  }
795 
796  std::vector< PCB_LAYER_ID > &selected_layer_id = layer_id;
797  std::vector< PCB_LAYER_ID > layer_id_without_F_and_B;
798 
800  {
801  layer_id_without_F_and_B.clear();
802  layer_id_without_F_and_B.reserve( layer_id.size() );
803 
804  for( size_t i = 0; i < layer_id.size(); ++i )
805  {
806  if( ( layer_id[i] != F_Cu ) &&
807  ( layer_id[i] != B_Cu ) )
808  layer_id_without_F_and_B.push_back( layer_id[i] );
809  }
810 
811  selected_layer_id = layer_id_without_F_and_B;
812  }
813 
814  if( selected_layer_id.size() > 0 )
815  {
816  std::atomic<size_t> nextItem( 0 );
817  std::atomic<size_t> threadsFinished( 0 );
818 
819  size_t parallelThreadCount = std::min<size_t>(
820  std::max<size_t>( std::thread::hardware_concurrency(), 2 ),
821  selected_layer_id.size() );
822  for( size_t ii = 0; ii < parallelThreadCount; ++ii )
823  {
824  std::thread t = std::thread( [&nextItem, &threadsFinished, &selected_layer_id, this]()
825  {
826  for( size_t i = nextItem.fetch_add( 1 );
827  i < selected_layer_id.size();
828  i = nextItem.fetch_add( 1 ) )
829  {
830  auto layerPoly = m_layers_poly.find( selected_layer_id[i] );
831 
832  if( layerPoly != m_layers_poly.end() )
833  // This will make a union of all added contours
834  layerPoly->second->Simplify( SHAPE_POLY_SET::PM_FAST );
835  }
836 
837  threadsFinished++;
838  } );
839 
840  t.detach();
841  }
842 
843  while( threadsFinished < parallelThreadCount )
844  std::this_thread::sleep_for( std::chrono::milliseconds( 10 ) );
845  }
846  }
847 
848  // Simplify holes polygon contours
849  // /////////////////////////////////////////////////////////////////////////
850  if( aStatusReporter )
851  aStatusReporter->Report( _( "Simplify holes contours" ) );
852 
853  for( PCB_LAYER_ID layer : layer_id )
854  {
855  if( m_layers_outer_holes_poly.find( layer ) != m_layers_outer_holes_poly.end() )
856  {
857  // found
858  SHAPE_POLY_SET *polyLayer = m_layers_outer_holes_poly[layer];
859  polyLayer->Simplify( SHAPE_POLY_SET::PM_FAST );
860 
861  wxASSERT( m_layers_inner_holes_poly.find( layer ) != m_layers_inner_holes_poly.end() );
862 
863  polyLayer = m_layers_inner_holes_poly[layer];
864  polyLayer->Simplify( SHAPE_POLY_SET::PM_FAST );
865  }
866  }
867 
868  // End Build Copper layers
869 
870  // This will make a union of all added contourns
875 
876  // Build Tech layers
877  // Based on: https://github.com/KiCad/kicad-source-mirror/blob/master/3d-viewer/3d_draw.cpp#L1059
878  // /////////////////////////////////////////////////////////////////////////
879  if( aStatusReporter )
880  aStatusReporter->Report( _( "Build Tech layers" ) );
881 
882  // draw graphic items, on technical layers
883  static const PCB_LAYER_ID teckLayerList[] = {
884  B_Adhes,
885  F_Adhes,
886  B_Paste,
887  F_Paste,
888  B_SilkS,
889  F_SilkS,
890  B_Mask,
891  F_Mask,
892 
893  // Aux Layers
894  Dwgs_User,
895  Cmts_User,
896  Eco1_User,
897  Eco2_User,
898  Edge_Cuts,
899  Margin
900  };
901 
902  // User layers are not drawn here, only technical layers
903  for( LSEQ seq = LSET::AllNonCuMask().Seq( teckLayerList, arrayDim( teckLayerList ) );
904  seq;
905  ++seq )
906  {
907  const PCB_LAYER_ID curr_layer_id = *seq;
908 
909  if( !Is3DLayerEnabled( curr_layer_id ) )
910  continue;
911 
912  CBVHCONTAINER2D *layerContainer = new CBVHCONTAINER2D;
913  m_layers_container2D[curr_layer_id] = layerContainer;
914 
915  SHAPE_POLY_SET *layerPoly = new SHAPE_POLY_SET;
916  m_layers_poly[curr_layer_id] = layerPoly;
917 
918  // Add drawing objects
919  for( BOARD_ITEM* item : m_board->Drawings() )
920  {
921  if( !item->IsOnLayer( curr_layer_id ) )
922  continue;
923 
924  switch( item->Type() )
925  {
926  case PCB_SHAPE_T:
928  layerContainer,
929  curr_layer_id,
930  0 );
931  break;
932 
933  case PCB_TEXT_T:
935  layerContainer,
936  curr_layer_id,
937  0 );
938  break;
939 
940  case PCB_DIM_ALIGNED_T:
941  case PCB_DIM_CENTER_T:
943  case PCB_DIM_LEADER_T:
945  layerContainer,
946  curr_layer_id,
947  0 );
948  break;
949 
950  default:
951  break;
952  }
953  }
954 
955 
956  // Add drawing contours
957  for( BOARD_ITEM* item : m_board->Drawings() )
958  {
959  if( !item->IsOnLayer( curr_layer_id ) )
960  continue;
961 
962  switch( item->Type() )
963  {
964  case PCB_SHAPE_T:
965  ( (PCB_SHAPE*) item )->TransformShapeWithClearanceToPolygon( *layerPoly,
966  curr_layer_id, 0,
967  ARC_HIGH_DEF,
968  ERROR_INSIDE );
969  break;
970 
971  case PCB_TEXT_T:
972  ( (PCB_TEXT*) item )->TransformShapeWithClearanceToPolygonSet( *layerPoly, 0,
973  ARC_HIGH_DEF,
974  ERROR_INSIDE );
975  break;
976 
977  default:
978  break;
979  }
980  }
981 
982 
983  // Add footprints tech layers - objects
984  // /////////////////////////////////////////////////////////////////////
985  for( MODULE* module : m_board->Modules() )
986  {
987  if( (curr_layer_id == F_SilkS) || (curr_layer_id == B_SilkS) )
988  {
989  int linewidth = g_DrawDefaultLineThickness;
990 
991  for( D_PAD* pad : module->Pads() )
992  {
993  if( !pad->IsOnLayer( curr_layer_id ) )
994  continue;
995 
996  buildPadShapeThickOutlineAsSegments( pad, layerContainer, linewidth );
997  }
998  }
999  else
1000  {
1001  AddPadsShapesWithClearanceToContainer( module, layerContainer, curr_layer_id, 0,
1002  false,
1003  false,
1004  false );
1005  }
1006 
1007  AddGraphicsShapesWithClearanceToContainer( module, layerContainer, curr_layer_id, 0 );
1008  }
1009 
1010 
1011  // Add footprints tech layers - contours
1012  for( MODULE* module : m_board->Modules() )
1013  {
1014  if( (curr_layer_id == F_SilkS) || (curr_layer_id == B_SilkS) )
1015  {
1016  const int linewidth = g_DrawDefaultLineThickness;
1017 
1018  for( D_PAD* pad : module->Pads() )
1019  {
1020  if( !pad->IsOnLayer( curr_layer_id ) )
1021  continue;
1022 
1023  buildPadShapeThickOutlineAsPolygon( pad, *layerPoly, linewidth );
1024  }
1025  }
1026  else
1027  {
1028  module->TransformPadsShapesWithClearanceToPolygon( *layerPoly, curr_layer_id, 0,
1029  ARC_HIGH_DEF, ERROR_INSIDE );
1030  }
1031 
1032  // On tech layers, use a poor circle approximation, only for texts (stroke font)
1033  module->TransformGraphicTextWithClearanceToPolygonSet( *layerPoly, curr_layer_id, 0,
1034  ARC_HIGH_DEF, ERROR_INSIDE );
1035 
1036  // Add the remaining things with dynamic seg count for circles
1037  transformGraphicModuleEdgeToPolygonSet( module, curr_layer_id, *layerPoly );
1038  }
1039 
1040 
1041  // Draw non copper zones
1042  if( GetFlag( FL_ZONE ) )
1043  {
1044  for( ZONE_CONTAINER* zone : m_board->Zones() )
1045  {
1046  if( zone->IsOnLayer( curr_layer_id ) )
1047  AddSolidAreasShapesToContainer( zone, layerContainer, curr_layer_id );
1048  }
1049 
1050  for( ZONE_CONTAINER* zone : m_board->Zones() )
1051  {
1052  if( zone->IsOnLayer( curr_layer_id ) )
1053  zone->TransformSolidAreasShapesToPolygon( curr_layer_id, *layerPoly );
1054  }
1055  }
1056 
1057  // This will make a union of all added contours
1058  layerPoly->Simplify( SHAPE_POLY_SET::PM_FAST );
1059  }
1060  // End Build Tech layers
1061 
1062  // Build BVH (Bounding volume hierarchy) for holes and vias
1063 
1064  if( aStatusReporter )
1065  aStatusReporter->Report( _( "Build BVH for holes and vias" ) );
1066 
1070 
1071  if( !m_layers_holes2D.empty() )
1072  {
1073  for( auto& hole : m_layers_holes2D )
1074  hole.second->BuildBVH();
1075  }
1076 
1077  // We only need the Solder mask to initialize the BVH
1078  // because..?
1080  m_layers_container2D[B_Mask]->BuildBVH();
1081 
1083  m_layers_container2D[F_Mask]->BuildBVH();
1084 }
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:749
double BiuTo3Dunits() const noexcept
BiuTo3Dunits - Board integer units To 3D units.
bool GetFlag(DISPLAY3D_FLG aFlag) const
GetFlag - get a configuration status of a flag.
ZONE_CONTAINER handles a list of polygons defining a copper zone.
Definition: class_zone.h:61
void AddShapeWithClearanceToContainer(const PCB_TEXT *aText, CGENERICCONTAINER2D *aDstContainer, PCB_LAYER_ID aLayerId, int aClearanceValue)
class ALIGNED_DIMENSION, a linear dimension (graphic item)
Definition: typeinfo.h:101
class LEADER, a leader dimension (graphic item)
Definition: typeinfo.h:102
CBVHCONTAINER2D * m_platedpads_container2D_B_Cu
SHAPE_POLY_SET m_through_outer_holes_poly_NPTH
It contains polygon contours for (just) non plated through holes (outer cylinder)
BOARD_ITEM is a base class for any item which can be embedded within the BOARD container class,...
MAP_POLY m_layers_outer_holes_poly
It contains polygon contours for holes of each layer (outer holes)
const wxPoint & GetStart() const
Definition: class_track.h:116
SHAPE_POLY_SET * m_B_Cu_PlatedPads_poly
class CENTER_DIMENSION, a center point marking (graphic item)
Definition: typeinfo.h:103
CBVHCONTAINER2D m_through_holes_inner
It contains the list of throughHoles of the board, the radius is the inner hole.
void AddGraphicsShapesWithClearanceToContainer(const MODULE *aModule, CGENERICCONTAINER2D *aDstContainer, PCB_LAYER_ID aLayerId, int aInflateValue)
MAP_POLY m_layers_inner_holes_poly
It contains polygon contours for holes of each layer (inner holes)
static LSET AllNonCuMask()
Return a mask holding all layer minus CU layers.
Definition: lset.cpp:772
float GetCopperThickness3DU() const noexcept
GetCopperThickness3DU - Get the current copper layer thickness.
class PCB_TEXT, text on a layer
Definition: typeinfo.h:92
unsigned int m_stats_nr_holes
number of holes in the board
CBVHCONTAINER2D m_through_holes_outer_ring
It contains the list of throughHoles of the board, the radius of the hole is inflated with the annula...
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition: pad_shapes.h:85
REPORTER is a pure virtual class used to derive REPORTER objects from.
Definition: reporter.h:64
SHAPE_POLY_SET m_through_outer_ring_holes_poly
It contains polygon contours for through holes vias (outer annular ring)
unsigned int m_stats_nr_vias
Nr of vias.
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:410
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)=0
Function Report is a pure virtual function to override in the derived object.
void buildPadShapeThickOutlineAsSegments(const D_PAD *aPad, CGENERICCONTAINER2D *aDstContainer, int aWidth)
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...
RENDER_ENGINE m_render_engine
SHAPE_POLY_SET * m_F_Cu_PlatedPads_poly
PCB_LAYER_ID
A quick note on layer IDs:
LSET is a set of PCB_LAYER_IDs.
virtual bool IsOnLayer(PCB_LAYER_ID aLayer) const
Function IsOnLayer tests to see if this object is on the given layer.
glm::vec2 SFVEC2F
Definition: xv3d_types.h:45
#define NULL
int GetHolePlatingThicknessBIU() const noexcept
GetCopperThicknessBIU - Get the current copper layer thickness.
MAP_CONTAINER_2D m_layers_holes2D
It contains the holes per each layer.
int GetDrillValue() const
Function GetDrillValue "calculates" the drill value for vias (m-Drill if > 0, or default drill value ...
MODULES & Modules()
Definition: class_board.h:284
SHAPE_POLY_SET.
bool FlashLayer(int aLayer) const
Checks to see whether the via should have a pad on the specific layer.
VIATYPE
Definition: class_track.h:68
int g_DrawDefaultLineThickness
Default line thickness in internal units used to draw or plot items using a default thickness line va...
SHAPE_POLY_SET m_through_outer_holes_vias_poly
It contains polygon contours for through holes vias (outer cylinder)
float m_stats_track_med_width
Track average width.
double m_biuTo3Dunits
Normalization scale to convert board internal units to 3D units to normalize 3D units between -1....
bool Is3DLayerEnabled(PCB_LAYER_ID aLayer) const
Is3DLayerEnabled - Check if a layer is enabled.
void Simplify(POLYGON_MODE aFastMode)
Simplifies the polyset (merges overlapping polys, eliminates degeneracy/self-intersections) For aFast...
void buildPadShapeThickOutlineAsPolygon(const D_PAD *aPad, SHAPE_POLY_SET &aCornerBuffer, int aWidth) const
void createLayers(REPORTER *aStatusReporter)
LSEQ is a sequence (and therefore also a set) of PCB_LAYER_IDs.
Pad object description.
MAP_POLY m_layers_poly
It contains polygon contours for each layer.
CBVHCONTAINER2D m_through_holes_vias_outer
It contains the list of throughHoles vias of the board, the radius of the hole is inflated with the c...
constexpr std::size_t arrayDim(T const (&)[N]) noexcept
Definition: macros.h:121
CBVHCONTAINER2D m_through_holes_outer
It contains the list of throughHoles of the board, the radius of the hole is inflated with the copper...
void transformGraphicModuleEdgeToPolygonSet(const MODULE *aModule, PCB_LAYER_ID aLayer, SHAPE_POLY_SET &aCornerBuffer) const
void TransformCircleToPolygon(SHAPE_POLY_SET &aCornerBuffer, wxPoint aCenter, int aRadius, int aError, ERROR_LOC aErrorLoc)
Function TransformCircleToPolygon convert a circle to a polygon, using multiple straight lines.
void Add(COBJECT2D *aObject)
Definition: ccontainer2d.h:52
ZONE_CONTAINERS & Zones()
Definition: class_board.h:290
SHAPE_POLY_SET m_through_outer_holes_poly
It contains polygon contours for through holes (outer cylinder)
int GetWidth() const
Definition: class_track.h:110
CBVHCONTAINER2D m_through_holes_vias_inner
It contains the list of throughHoles vias of the board, the radius of the hole.
#define _(s)
Definition: 3d_actions.cpp:33
unsigned GetRunningMicroSecs()
Function GetRunningMicroSecs An alternate way to calculate an elapset time (in microsecondes) to clas...
unsigned int m_copperLayersCount
Number of copper layers actually used by the board.
void RemoveAllContours()
Removes all outlines & holes (clears) the polygon set.
VIATYPE GetViaType() const
Definition: class_track.h:384
void AddSolidAreasShapesToContainer(const ZONE_CONTAINER *aZoneContainer, CGENERICCONTAINER2D *aDstContainer, PCB_LAYER_ID aLayerId)
COBJECT2D * createNewPadDrill(const D_PAD *aPad, int aInflateValue)
float m_stats_via_med_hole_diameter
Computed medium diameter of the via holes in 3D units.
bool IsCopperLayer(LAYER_NUM aLayerId)
Tests whether a layer is a copper layer.
void createNewTrack(const TRACK *aTrack, CGENERICCONTAINER2D *aDstContainer, int aClearanceValue)
class ORTHOGONAL_DIMENSION, a linear dimension constrained to x/y
Definition: typeinfo.h:104
class VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:97
float m_stats_hole_med_diameter
Computed medium diameter of the holes in 3D units.
void BooleanSubtract(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Performs boolean polyset difference For aFastMode meaning, see function booleanOp
static const wxChar * m_logTrace
Trace mask used to enable or disable the trace output of this class.
MAP_CONTAINER_2D m_layers_container2D
It contains the 2d elements of each layer.
CBVHCONTAINER2D * m_platedpads_container2D_F_Cu
class PCB_SHAPE, a segment not on copper layers
Definition: typeinfo.h:91
DRAWINGS & Drawings()
Definition: class_board.h:287
PCB_LAYER_ID ToLAYER_ID(int aLayer)
Definition: lset.cpp:897
unsigned int m_stats_nr_tracks
Number of tracks in the board.
Abstract dimension API.
TRACKS & Tracks()
Definition: class_board.h:281
void AddPadsShapesWithClearanceToContainer(const MODULE *aModule, CGENERICCONTAINER2D *aDstContainer, PCB_LAYER_ID aLayerId, int aInflateValue, bool aSkipNPTHPadsWihNoCopper, bool aSkipPlatedPads, bool aSkipNonPlatedPads)
KICAD_T Type() const
Function Type()
Definition: eda_item.h:182