KiCad PCB EDA Suite
Loading...
Searching...
No Matches
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 <pcb_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
208 && m_Cfg->m_Render.engine == RENDER_ENGINE::OPENGL )
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
416 if( m_Cfg->m_Render.opengl_copper_thickness && m_Cfg->m_Render.engine == RENDER_ENGINE::OPENGL )
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 addPads( footprint, layerContainer, layer, renderPlatedPadsAsPlated, false );
529
530 // Micro-wave footprints may have items on copper layers
531 addFootprintShapes( footprint, layerContainer, layer );
532 }
533 }
534
535 if( renderPlatedPadsAsPlated )
536 {
537 // ADD PLATED PADS
538 for( FOOTPRINT* footprint : m_board->Footprints() )
539 {
540 addPads( footprint, m_platedPadsFront, F_Cu, false, true );
541 addPads( footprint, m_platedPadsBack, B_Cu, false, true );
542 }
543
546 }
547
548 // Add footprints PADs poly contours (vertical outlines)
549 if( m_Cfg->m_Render.opengl_copper_thickness && m_Cfg->m_Render.engine == RENDER_ENGINE::OPENGL )
550 {
551 for( PCB_LAYER_ID layer : layer_ids )
552 {
553 wxASSERT( m_layers_poly.find( layer ) != m_layers_poly.end() );
554
555 SHAPE_POLY_SET *layerPoly = m_layers_poly[layer];
556
557 // Add pads to polygon list
558 for( FOOTPRINT* footprint : m_board->Footprints() )
559 {
560 // Note: NPTH pads are not drawn on copper layers when the pad has same shape as
561 // its hole
562 footprint->TransformPadsToPolySet( *layerPoly, layer, 0, maxError, ERROR_INSIDE,
563 true, renderPlatedPadsAsPlated, false );
564
565 transformFPShapesToPolySet( footprint, layer, *layerPoly );
566 }
567 }
568
569 if( renderPlatedPadsAsPlated )
570 {
571 // ADD PLATED PADS contours
572 for( FOOTPRINT* footprint : m_board->Footprints() )
573 {
574 footprint->TransformPadsToPolySet( *m_frontPlatedPadPolys, F_Cu, 0, maxError,
575 ERROR_INSIDE, true, false, true );
576
577 footprint->TransformPadsToPolySet( *m_backPlatedPadPolys, B_Cu, 0, maxError,
578 ERROR_INSIDE, true, false, true );
579 }
580 }
581 }
582
583 // Add graphic item on copper layers to object containers
584 for( PCB_LAYER_ID layer : layer_ids )
585 {
586 wxASSERT( m_layerMap.find( layer ) != m_layerMap.end() );
587
588 BVH_CONTAINER_2D *layerContainer = m_layerMap[layer];
589
590 // Add graphic items on copper layers (texts and other graphics)
591 for( BOARD_ITEM* item : m_board->Drawings() )
592 {
593 if( !item->IsOnLayer( layer ) )
594 continue;
595
596 switch( item->Type() )
597 {
598 case PCB_SHAPE_T:
599 addShape( static_cast<PCB_SHAPE*>( item ), layerContainer, item );
600 break;
601
602 case PCB_TEXT_T:
603 addText( static_cast<PCB_TEXT*>( item ), layerContainer, item );
604 break;
605
606 case PCB_TEXTBOX_T:
607 addText( static_cast<PCB_TEXTBOX*>( item ), layerContainer, item );
608 addShape( static_cast<PCB_TEXTBOX*>( item ), layerContainer, item );
609 break;
610
612 case PCB_DIM_CENTER_T:
613 case PCB_DIM_RADIAL_T:
615 case PCB_DIM_LEADER_T:
616 addShape( static_cast<PCB_DIMENSION_BASE*>( item ), layerContainer, item );
617 break;
618
619 default:
620 wxLogTrace( m_logTrace, wxT( "createLayers: item type: %d not implemented" ),
621 item->Type() );
622 break;
623 }
624 }
625 }
626
627 // Add graphic item on copper layers to poly contours (vertical outlines)
628 if( m_Cfg->m_Render.opengl_copper_thickness && m_Cfg->m_Render.engine == RENDER_ENGINE::OPENGL )
629 {
630 for( PCB_LAYER_ID layer : layer_ids )
631 {
632 wxASSERT( m_layers_poly.find( layer ) != m_layers_poly.end() );
633
634 SHAPE_POLY_SET *layerPoly = m_layers_poly[layer];
635
636 // Add graphic items on copper layers (texts and other )
637 for( BOARD_ITEM* item : m_board->Drawings() )
638 {
639 if( !item->IsOnLayer( layer ) )
640 continue;
641
642 switch( item->Type() )
643 {
644 case PCB_SHAPE_T:
645 item->TransformShapeToPolygon( *layerPoly, layer, 0, maxError, ERROR_INSIDE );
646 break;
647
648 case PCB_TEXT_T:
649 {
650 PCB_TEXT* text = static_cast<PCB_TEXT*>( item );
651
652 text->TransformTextToPolySet( *layerPoly, 0, maxError, ERROR_INSIDE );
653 break;
654 }
655
656 case PCB_TEXTBOX_T:
657 {
658 PCB_TEXTBOX* textbox = static_cast<PCB_TEXTBOX*>( item );
659
660 textbox->TransformTextToPolySet( *layerPoly, 0, maxError, ERROR_INSIDE );
661 break;
662 }
663
664 default:
665 wxLogTrace( m_logTrace, wxT( "createLayers: item type: %d not implemented" ),
666 item->Type() );
667 break;
668 }
669 }
670 }
671 }
672
674 {
675 if( aStatusReporter )
676 aStatusReporter->Report( _( "Create zones" ) );
677
678 std::vector<std::pair<ZONE*, PCB_LAYER_ID>> zones;
679 std::unordered_map<PCB_LAYER_ID, std::unique_ptr<std::mutex>> layer_lock;
680
681 for( ZONE* zone : m_board->Zones() )
682 {
683 for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
684 {
685 zones.emplace_back( std::make_pair( zone, layer ) );
686 layer_lock.emplace( layer, std::make_unique<std::mutex>() );
687 }
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::min<size_t>( zones.size(),
695 std::max<size_t>( std::thread::hardware_concurrency(), 2 ) );
696
697 for( size_t ii = 0; ii < parallelThreadCount; ++ii )
698 {
699 std::thread t = std::thread( [&]()
700 {
701 for( size_t areaId = nextZone.fetch_add( 1 );
702 areaId < zones.size();
703 areaId = nextZone.fetch_add( 1 ) )
704 {
705 ZONE* zone = zones[areaId].first;
706
707 if( zone == nullptr )
708 break;
709
710 PCB_LAYER_ID layer = zones[areaId].second;
711
712 auto layerContainer = m_layerMap.find( layer );
713 auto layerPolyContainer = m_layers_poly.find( layer );
714
715 if( layerContainer != m_layerMap.end() )
716 addSolidAreasShapes( zone, layerContainer->second, layer );
717
718 if( m_Cfg->m_Render.opengl_copper_thickness
719 && m_Cfg->m_Render.engine == RENDER_ENGINE::OPENGL
720 && layerPolyContainer != m_layers_poly.end() )
721 {
722 auto mut_it = layer_lock.find( layer );
723
724 std::lock_guard< std::mutex > lock( *( mut_it->second ) );
725 zone->TransformSolidAreasShapesToPolygon( layer, *layerPolyContainer->second );
726 }
727 }
728
729 threadsFinished++;
730 } );
731
732 t.detach();
733 }
734
735 while( threadsFinished < parallelThreadCount )
736 std::this_thread::sleep_for( std::chrono::milliseconds( 10 ) );
737
738 }
739
740 // Simplify layer polygons
741
742 if( aStatusReporter )
743 aStatusReporter->Report( _( "Simplifying copper layers polygons" ) );
744
745 if( m_Cfg->m_Render.opengl_copper_thickness && m_Cfg->m_Render.engine == RENDER_ENGINE::OPENGL )
746 {
747 if( renderPlatedPadsAsPlated )
748 {
749 if( m_frontPlatedPadPolys && ( m_layers_poly.find( F_Cu ) != m_layers_poly.end() ) )
750 {
751 if( aStatusReporter )
752 aStatusReporter->Report( _( "Simplifying polygons on F_Cu" ) );
753
754 SHAPE_POLY_SET *layerPoly_F_Cu = m_layers_poly[F_Cu];
755 layerPoly_F_Cu->BooleanSubtract( *m_frontPlatedPadPolys, SHAPE_POLY_SET::PM_FAST );
756
757 m_frontPlatedPadPolys->Simplify( SHAPE_POLY_SET::PM_FAST );
758 }
759
760 if( m_backPlatedPadPolys && ( m_layers_poly.find( B_Cu ) != m_layers_poly.end() ) )
761 {
762 if( aStatusReporter )
763 aStatusReporter->Report( _( "Simplifying polygons on B_Cu" ) );
764
765 SHAPE_POLY_SET *layerPoly_B_Cu = m_layers_poly[B_Cu];
766 layerPoly_B_Cu->BooleanSubtract( *m_backPlatedPadPolys, SHAPE_POLY_SET::PM_FAST );
767
768 m_backPlatedPadPolys->Simplify( SHAPE_POLY_SET::PM_FAST );
769 }
770 }
771
772 std::vector<PCB_LAYER_ID> &selected_layer_id = layer_ids;
773 std::vector<PCB_LAYER_ID> layer_id_without_F_and_B;
774
775 if( renderPlatedPadsAsPlated )
776 {
777 layer_id_without_F_and_B.clear();
778 layer_id_without_F_and_B.reserve( layer_ids.size() );
779
780 for( PCB_LAYER_ID layer: layer_ids )
781 {
782 if( layer != F_Cu && layer != B_Cu )
783 layer_id_without_F_and_B.push_back( layer );
784 }
785
786 selected_layer_id = layer_id_without_F_and_B;
787 }
788
789 if( selected_layer_id.size() > 0 )
790 {
791 if( aStatusReporter )
792 {
793 aStatusReporter->Report( wxString::Format( _( "Simplifying %d copper layers" ),
794 (int) selected_layer_id.size() ) );
795 }
796
797 std::atomic<size_t> nextItem( 0 );
798 std::atomic<size_t> threadsFinished( 0 );
799
800 size_t parallelThreadCount = std::min<size_t>(
801 std::max<size_t>( std::thread::hardware_concurrency(), 2 ),
802 selected_layer_id.size() );
803
804 for( size_t ii = 0; ii < parallelThreadCount; ++ii )
805 {
806 std::thread t = std::thread(
807 [&nextItem, &threadsFinished, &selected_layer_id, this]()
808 {
809 for( size_t i = nextItem.fetch_add( 1 );
810 i < selected_layer_id.size();
811 i = nextItem.fetch_add( 1 ) )
812 {
813 auto layerPoly = m_layers_poly.find( selected_layer_id[i] );
814
815 if( layerPoly != m_layers_poly.end() )
816 // This will make a union of all added contours
817 layerPoly->second->Simplify( SHAPE_POLY_SET::PM_FAST );
818 }
819
820 threadsFinished++;
821 } );
822
823 t.detach();
824 }
825
826 while( threadsFinished < parallelThreadCount )
827 std::this_thread::sleep_for( std::chrono::milliseconds( 10 ) );
828 }
829 }
830
831 // Simplify holes polygon contours
832 if( aStatusReporter )
833 aStatusReporter->Report( _( "Simplify holes contours" ) );
834
835 for( PCB_LAYER_ID layer : layer_ids )
836 {
837 if( m_layerHoleOdPolys.find( layer ) != m_layerHoleOdPolys.end() )
838 {
839 // found
840 SHAPE_POLY_SET *polyLayer = m_layerHoleOdPolys[layer];
841 polyLayer->Simplify( SHAPE_POLY_SET::PM_FAST );
842
843 wxASSERT( m_layerHoleIdPolys.find( layer ) != m_layerHoleIdPolys.end() );
844
845 polyLayer = m_layerHoleIdPolys[layer];
846 polyLayer->Simplify( SHAPE_POLY_SET::PM_FAST );
847 }
848 }
849
850 // End Build Copper layers
851
852 // This will make a union of all added contours
853 m_throughHoleOdPolys.Simplify( SHAPE_POLY_SET::PM_FAST );
854 m_nonPlatedThroughHoleOdPolys.Simplify( SHAPE_POLY_SET::PM_FAST );
855 m_throughHoleViaOdPolys.Simplify( SHAPE_POLY_SET::PM_FAST );
856 m_throughHoleAnnularRingPolys.Simplify( SHAPE_POLY_SET::PM_FAST );
857
858 // Build Tech layers
859 // Based on:
860 // https://github.com/KiCad/kicad-source-mirror/blob/master/3d-viewer/3d_draw.cpp#L1059
861 if( aStatusReporter )
862 aStatusReporter->Report( _( "Build Tech layers" ) );
863
864 // draw graphic items, on technical layers
865
866 // Vertical walls (layer thickness) around shapes is really time consumming
867 // They are built on request
868 bool buildVerticalWallsForTechLayers = m_Cfg->m_Render.opengl_copper_thickness
869 && m_Cfg->m_Render.engine == RENDER_ENGINE::OPENGL;
870
871 static const PCB_LAYER_ID techLayerList[] = {
872 B_Adhes,
873 F_Adhes,
874 B_Paste,
875 F_Paste,
876 B_SilkS,
877 F_SilkS,
878 B_Mask,
879 F_Mask,
880
881 // Aux Layers
882 Dwgs_User,
883 Cmts_User,
884 Eco1_User,
885 Eco2_User,
886 Edge_Cuts,
887 Margin
888 };
889
890 // User layers are not drawn here, only technical layers
891 for( LSEQ seq = LSET::AllNonCuMask().Seq( techLayerList, arrayDim( techLayerList ) );
892 seq;
893 ++seq )
894 {
895 const PCB_LAYER_ID layer = *seq;
896
897 if( !Is3dLayerEnabled( layer ) )
898 continue;
899
900 if( aStatusReporter )
901 aStatusReporter->Report( wxString::Format( _( "Build Tech layer %d" ), (int) layer ) );
902
903 BVH_CONTAINER_2D *layerContainer = new BVH_CONTAINER_2D;
904 m_layerMap[layer] = layerContainer;
905
906 SHAPE_POLY_SET *layerPoly = new SHAPE_POLY_SET;
907 m_layers_poly[layer] = layerPoly;
908
909 // Add drawing objects
910 for( BOARD_ITEM* item : m_board->Drawings() )
911 {
912 if( !item->IsOnLayer( layer ) )
913 continue;
914
915 switch( item->Type() )
916 {
917 case PCB_SHAPE_T:
918 addShape( static_cast<PCB_SHAPE*>( item ), layerContainer, item );
919 break;
920
921 case PCB_TEXT_T:
922 addText( static_cast<PCB_TEXT*>( item ), layerContainer, item );
923 break;
924
925 case PCB_TEXTBOX_T:
926 addText( static_cast<PCB_TEXTBOX*>( item ), layerContainer, item );
927 addShape( static_cast<PCB_TEXTBOX*>( item ), layerContainer, item );
928 break;
929
931 case PCB_DIM_CENTER_T:
932 case PCB_DIM_RADIAL_T:
934 case PCB_DIM_LEADER_T:
935 addShape( static_cast<PCB_DIMENSION_BASE*>( item ), layerContainer, item );
936 break;
937
938 default:
939 break;
940 }
941 }
942
943 // Add drawing contours (vertical walls)
944 if( buildVerticalWallsForTechLayers )
945 {
946 for( BOARD_ITEM* item : m_board->Drawings() )
947 {
948 if( !item->IsOnLayer( layer ) )
949 continue;
950
951 switch( item->Type() )
952 {
953 case PCB_SHAPE_T:
954 item->TransformShapeToPolygon( *layerPoly, layer, 0, maxError, ERROR_INSIDE );
955 break;
956
957 case PCB_TEXT_T:
958 {
959 PCB_TEXT* text = static_cast<PCB_TEXT*>( item );
960
961 text->TransformTextToPolySet( *layerPoly, 0, maxError, ERROR_INSIDE );
962 break;
963 }
964
965 case PCB_TEXTBOX_T:
966 {
967 PCB_TEXTBOX* textbox = static_cast<PCB_TEXTBOX*>( item );
968
969 textbox->TransformTextToPolySet( *layerPoly, 0, maxError, ERROR_INSIDE );
970 break;
971 }
972
973 default:
974 break;
975 }
976 }
977 }
978
979 // Add footprints tech layers - objects
980 for( FOOTPRINT* footprint : m_board->Footprints() )
981 {
982 if( layer == F_SilkS || layer == B_SilkS )
983 {
984 int linewidth = m_board->GetDesignSettings().m_LineThickness[ LAYER_CLASS_SILK ];
985
986 for( PAD* pad : footprint->Pads() )
987 {
988 if( !pad->IsOnLayer( layer ) )
989 continue;
990
991 buildPadOutlineAsSegments( pad, layerContainer, linewidth );
992 }
993 }
994 else
995 {
996 addPads( footprint, layerContainer, layer, false, false );
997 }
998
999 addFootprintShapes( footprint, layerContainer, layer );
1000 }
1001
1002
1003 // Add footprints tech layers - contours (vertical walls)
1004 if( buildVerticalWallsForTechLayers )
1005 {
1006 for( FOOTPRINT* footprint : m_board->Footprints() )
1007 {
1008 if( layer == F_SilkS || layer == B_SilkS )
1009 {
1010 int linewidth = m_board->GetDesignSettings().m_LineThickness[ LAYER_CLASS_SILK ];
1011
1012 for( PAD* pad : footprint->Pads() )
1013 {
1014 if( !pad->IsOnLayer( layer ) )
1015 continue;
1016
1017 buildPadOutlineAsPolygon( pad, *layerPoly, linewidth );
1018 }
1019 }
1020 else
1021 {
1022 footprint->TransformPadsToPolySet( *layerPoly, layer, 0, maxError, ERROR_INSIDE );
1023 }
1024
1025 // On tech layers, use a poor circle approximation, only for texts (stroke font)
1026 footprint->TransformFPTextToPolySet( *layerPoly, layer, 0, maxError, ERROR_INSIDE );
1027
1028 // Add the remaining things with dynamic seg count for circles
1029 transformFPShapesToPolySet( footprint, layer, *layerPoly );
1030 }
1031 }
1032
1033 // Draw non copper zones
1034 if( m_Cfg->m_Render.show_zones )
1035 {
1036 for( ZONE* zone : m_board->Zones() )
1037 {
1038 if( zone->IsOnLayer( layer ) )
1039 addSolidAreasShapes( zone, layerContainer, layer );
1040 }
1041
1042 if( buildVerticalWallsForTechLayers )
1043 {
1044 for( ZONE* zone : m_board->Zones() )
1045 {
1046
1047 if( zone->IsOnLayer( layer ) )
1048 zone->TransformSolidAreasShapesToPolygon( layer, *layerPoly );
1049 }
1050 }
1051 }
1052
1053 // This will make a union of all added contours
1054 layerPoly->Simplify( SHAPE_POLY_SET::PM_FAST );
1055 }
1056 // End Build Tech layers
1057
1058 // Build BVH (Bounding volume hierarchy) for holes and vias
1059
1060 if( aStatusReporter )
1061 aStatusReporter->Report( _( "Build BVH for holes and vias" ) );
1062
1063 m_throughHoleIds.BuildBVH();
1064 m_throughHoleOds.BuildBVH();
1065 m_throughHoleAnnularRings.BuildBVH();
1066
1067 if( !m_layerHoleMap.empty() )
1068 {
1069 for( std::pair<const PCB_LAYER_ID, BVH_CONTAINER_2D*>& hole : m_layerHoleMap )
1070 hole.second->BuildBVH();
1071 }
1072
1073 // We only need the Solder mask to initialize the BVH
1074 // because..?
1075 if( m_layerMap[B_Mask] )
1076 m_layerMap[B_Mask]->BuildBVH();
1077
1078 if( m_layerMap[F_Mask] )
1079 m_layerMap[F_Mask]->BuildBVH();
1080}
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 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
void addPads(const FOOTPRINT *aFootprint, CONTAINER_2D_BASE *aDstContainer, PCB_LAYER_ID aLayerId, bool aSkipPlatedPads, bool aSkipNonPlatedPads)
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:71
virtual bool IsOnLayer(PCB_LAYER_ID aLayer, bool aIncludeCourtyards=false) const
Test to see if this object is on the given layer.
Definition: board_item.h:261
ZONES & Zones()
Definition: board.h:318
FOOTPRINTS & Footprints()
Definition: board.h:312
TRACKS & Tracks()
Definition: board.h:309
DRAWINGS & Drawings()
Definition: board.h:315
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:728
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:497
LSET is a set of PCB_LAYER_IDs.
Definition: layer_ids.h:536
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.
void TransformTextToPolySet(SHAPE_POLY_SET &aBuffer, int aClearance, int aMaxError, 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
Convert the track shape to a closed polygon.
Definition: pcb_track.cpp:1209
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 RemoveAllContours()
Remove all outlines & holes (clears) the polygon set.
void BooleanSubtract(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Perform boolean polyset difference For aFastMode meaning, see function booleanOp.
void Simplify(POLYGON_MODE aFastMode)
Simplify the polyset (merges overlapping polys, eliminates degeneracy/self-intersections) For aFastMo...
Handle a list of polygons defining a copper zone.
Definition: zone.h:72
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:831
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
VIATYPE
Definition: pcb_track.h:64
unsigned GetRunningMicroSecs()
An alternate way to calculate an elapsed time (in microsecondes) to class PROF_COUNTER.
@ 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:101
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition: typeinfo.h:98
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:93
@ PCB_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition: typeinfo.h:99
@ 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:97
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
Definition: typeinfo.h:100
glm::vec2 SFVEC2F
Definition: xv3d_types.h:42