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