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) 2023 CERN
6 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 */
21
22#include <vector>
23#include <thread>
24#include <algorithm>
25#include <atomic>
26
27#include <wx/log.h>
28
29#include "board_adapter.h"
33#include <board.h>
34#include <footprint.h>
35#include <layer_range.h>
36#include <lset.h>
38#include <trigo.h>
39#include <pad.h>
40#include <pcb_barcode.h>
41#include <pcb_shape.h>
42#include <pcb_track.h>
43#include <zone.h>
44
45#ifdef PRINT_STATISTICS_3D_VIEWER
46#include <core/profile.h>
47#endif
48
49/*
50 * This is used to draw pad outlines on silk layers.
51 */
52void buildPadOutlineAsPolygon( const PAD* aPad, PCB_LAYER_ID aLayer, SHAPE_POLY_SET& aBuffer,
53 int aWidth, int aMaxError, ERROR_LOC aErrorLoc )
54{
55 if( aPad->GetShape( aLayer ) == PAD_SHAPE::CIRCLE ) // Draw a ring
56 {
57 TransformRingToPolygon( aBuffer, aPad->ShapePos( aLayer ), aPad->GetSize( aLayer ).x / 2,
58 aWidth, aMaxError, aErrorLoc );
59 }
60 else
61 {
62 // For other shapes, add outlines as thick segments in polygon buffer
63 const SHAPE_LINE_CHAIN& path = aPad->GetEffectivePolygon( aLayer, ERROR_INSIDE )->COutline( 0 );
64
65 for( int ii = 0; ii < path.PointCount(); ++ii )
66 {
67 const VECTOR2I& a = path.CPoint( ii );
68 const VECTOR2I& b = path.CPoint( ii + 1 );
69
70 TransformOvalToPolygon( aBuffer, a, b, aWidth, aMaxError, aErrorLoc );
71 }
72 }
73}
74
75
76void transformFPShapesToPolySet( const FOOTPRINT* aFootprint, PCB_LAYER_ID aLayer,
77 SHAPE_POLY_SET& aBuffer, int aMaxError, ERROR_LOC aErrorLoc )
78{
79 for( BOARD_ITEM* item : aFootprint->GraphicalItems() )
80 {
81 if( !item->IsOnLayer( aLayer ) )
82 continue;
83
84 switch( item->Type() )
85 {
86 case PCB_SHAPE_T:
87 {
88 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
89 int margin = 0;
90
91 if( IsSolderMaskLayer( aLayer ) && shape->HasSolderMask() )
92 margin = shape->GetSolderMaskExpansion();
93
94 item->TransformShapeToPolySet( aBuffer, aLayer, margin, aMaxError, aErrorLoc );
95 break;
96 }
97
98 case PCB_BARCODE_T:
99 item->TransformShapeToPolySet( aBuffer, aLayer, 0, aMaxError, aErrorLoc );
100 break;
101
103 case PCB_DIM_CENTER_T:
104 case PCB_DIM_RADIAL_T:
106 case PCB_DIM_LEADER_T:
107 item->TransformShapeToPolySet( aBuffer, aLayer, 0, aMaxError, aErrorLoc );
108 break;
109
110 default:
111 break;
112 }
113 }
114}
115
116
117void transformFPTextToPolySet( const FOOTPRINT* aFootprint, PCB_LAYER_ID aLayer,
118 const std::bitset<LAYER_3D_END>& aFlags, SHAPE_POLY_SET& aBuffer,
119 int aMaxError, ERROR_LOC aErrorLoc )
120{
121 for( BOARD_ITEM* item : aFootprint->GraphicalItems() )
122 {
123 if( item->Type() == PCB_TEXT_T )
124 {
125 PCB_TEXT* text = static_cast<PCB_TEXT*>( item );
126
127 if( !aFlags.test( LAYER_FP_TEXT ) )
128 continue;
129
130 if( text->GetText() == wxT( "${REFERENCE}" ) && !aFlags.test( LAYER_FP_REFERENCES ) )
131 continue;
132
133 if( text->GetText() == wxT( "${VALUE}" ) && !aFlags.test( LAYER_FP_VALUES ) )
134 continue;
135
136 if( text->IsOnLayer( aLayer ) )
137 text->TransformTextToPolySet( aBuffer, 0, aMaxError, aErrorLoc );
138 }
139
140 if( item->Type() == PCB_TEXTBOX_T )
141 {
142 PCB_TEXTBOX* textbox = static_cast<PCB_TEXTBOX*>( item );
143
144 if( textbox->IsOnLayer( aLayer ) )
145 {
146 // border
147 if( textbox->IsBorderEnabled() )
148 textbox->PCB_SHAPE::TransformShapeToPolygon( aBuffer, aLayer, 0, aMaxError, aErrorLoc );
149
150 // text
151 textbox->TransformTextToPolySet( aBuffer, 0, aMaxError, aErrorLoc );
152 }
153 }
154 }
155
156 for( const PCB_FIELD* field : aFootprint->GetFields() )
157 {
158 if( !aFlags.test( LAYER_FP_TEXT ) )
159 continue;
160
161 wxCHECK2( field, continue );
162
163 if( field->IsReference() && !aFlags.test( LAYER_FP_REFERENCES ) )
164 continue;
165
166 if( field->IsValue() && !aFlags.test( LAYER_FP_VALUES ) )
167 continue;
168
169 if( field->IsOnLayer( aLayer ) && field->IsVisible() )
170 field->TransformTextToPolySet( aBuffer, 0, aMaxError, aErrorLoc );
171 }
172}
173
174
176{
177#define DELETE_AND_FREE( ptr ) \
178 { \
179 delete ptr; \
180 ptr = nullptr; \
181 } \
182
183#define DELETE_AND_FREE_MAP( map ) \
184 { \
185 for( auto& [ layer, poly ] : map ) \
186 delete poly; \
187 \
188 map.clear(); \
189 }
190
192
195
198
199 m_NPTH_ODPolys.RemoveAllContours();
200 m_TH_ODPolys.RemoveAllContours();
201 m_viaTH_ODPolys.RemoveAllContours();
202 m_viaAnnuliPolys.RemoveAllContours();
203
204 m_frontCounterborePolys.RemoveAllContours();
205 m_backCounterborePolys.RemoveAllContours();
206 m_frontCountersinkPolys.RemoveAllContours();
207 m_backCountersinkPolys.RemoveAllContours();
208
211
216
217 m_TH_ODs.Clear();
218 m_TH_IDs.Clear();
219 m_viaAnnuli.Clear();
220 m_viaTH_ODs.Clear();
221
226 m_backdrillCutouts.Clear();
228}
229
230
231void BOARD_ADAPTER::createLayers( std::shared_ptr<REPORTER> aStatusReporter, std::stop_token aStop )
232{
234
235 if( aStop.stop_requested() )
236 return;
237
238 // Build Copper layers
239 // Based on:
240 // https://github.com/KiCad/kicad-source-mirror/blob/master/3d-viewer/3d_draw.cpp#L692
241
242#ifdef PRINT_STATISTICS_3D_VIEWER
243 int64_t stats_startCopperLayersTime = GetRunningMicroSecs();
244
245 int64_t start_Time = stats_startCopperLayersTime;
246#endif
247
249
250 std::bitset<LAYER_3D_END> visibilityFlags = GetVisibleLayers();
251
252 m_trackCount = 0;
254 m_viaCount = 0;
256 m_holeCount = 0;
258
259 if( !m_board )
260 return;
261
262 // Prepare track list, convert in a vector. Calc statistic for the holes
263 std::vector<const PCB_TRACK*> trackList;
264 trackList.clear();
265 trackList.reserve( m_board->Tracks().size() );
266
267 for( PCB_TRACK* track : m_board->Tracks() )
268 {
269 // Skip tracks (not vias theyt are on more than one layer ) on disabled layers
270 if( track->Type() != PCB_VIA_T && !Is3dLayerEnabled( track->GetLayer(), visibilityFlags ) )
271 {
272 continue;
273 }
274
275 // Note: a PCB_TRACK holds normal segment tracks and also vias circles (that have also
276 // drill values)
277 trackList.push_back( track );
278
279 if( track->Type() == PCB_VIA_T )
280 {
281 const PCB_VIA *via = static_cast< const PCB_VIA*>( track );
282 m_viaCount++;
283 m_averageViaHoleDiameter += static_cast<float>( via->GetDrillValue() * m_biuTo3Dunits );
284 }
285 else
286 {
287 m_trackCount++;
288 m_averageTrackWidth += static_cast<float>( track->GetWidth() * m_biuTo3Dunits );
289 }
290 }
291
292 if( m_trackCount )
294
295 if( m_viaCount )
297
298 // Prepare copper layers index and containers
299 std::vector<PCB_LAYER_ID> layer_ids;
300 layer_ids.clear();
301 layer_ids.reserve( m_copperLayersCount );
302
304 {
305 if( !Is3dLayerEnabled( layer, visibilityFlags ) ) // Skip non enabled layers
306 continue;
307
308 layer_ids.push_back( layer );
309
310 BVH_CONTAINER_2D *layerContainer = new BVH_CONTAINER_2D;
311 m_layerMap[layer] = layerContainer;
312
314 {
315 SHAPE_POLY_SET* layerPoly = new SHAPE_POLY_SET;
316 m_layers_poly[layer] = layerPoly;
317 }
318 }
319
320 if( cfg.DifferentiatePlatedCopper() )
321 {
324
327 }
328
329 if( cfg.show_off_board_silk )
330 {
333 }
334
335 if( aStop.stop_requested() )
336 return;
337
338 if( aStatusReporter )
339 aStatusReporter->Report( _( "Create tracks and vias" ) );
340
341 // Create tracks as objects and add it to container
342 for( PCB_LAYER_ID layer : layer_ids )
343 {
344 wxASSERT( m_layerMap.contains( layer ) );
345
346 BVH_CONTAINER_2D *layerContainer = m_layerMap[layer];
347
348 for( const PCB_TRACK* track : trackList )
349 {
350 // NOTE: Vias can be on multiple layers
351 if( !track->IsOnLayer( layer ) )
352 continue;
353
354 // Skip vias annulus when not flashed on this layer
355 if( track->Type() == PCB_VIA_T && !static_cast<const PCB_VIA*>( track )->FlashLayer( layer ) )
356 continue;
357
358 // Add object item to layer container
359 createTrackWithMargin( track, layerContainer, layer );
360 }
361 }
362
363 if( aStop.stop_requested() )
364 return;
365
366 // Create VIAS and THTs objects and add it to holes containers
367 for( PCB_LAYER_ID layer : layer_ids )
368 {
369 // Check if the layer is already created
370 if( !m_layerHoleMap.contains( layer ) )
371 {
372 m_layerHoleMap[layer] = new BVH_CONTAINER_2D;
373 }
374
375 // ADD TRACKS
376 unsigned int nTracks = trackList.size();
377
378 for( unsigned int trackIdx = 0; trackIdx < nTracks; ++trackIdx )
379 {
380 const PCB_TRACK *track = trackList[trackIdx];
381
382 if( !track->IsOnLayer( layer ) )
383 continue;
384
385 // ADD VIAS and THT
386 if( track->Type() == PCB_VIA_T )
387 {
388 const PCB_VIA* via = static_cast<const PCB_VIA*>( track );
389 const VIATYPE viatype = via->GetViaType();
390 const double holediameter = via->GetDrillValue() * BiuTo3dUnits();
391 const double viasize = via->GetWidth( layer ) * BiuTo3dUnits();
392 const double plating = GetHolePlatingThickness() * BiuTo3dUnits();
393
394 // holes and layer copper extend half info cylinder wall to hide transition
395 const float thickness = static_cast<float>( plating / 2.0f );
396 const float hole_inner_radius = static_cast<float>( holediameter / 2.0f );
397 const float ring_radius = static_cast<float>( viasize / 2.0f );
398
399 const bool capped = via->GetCappingMode() == CAPPING_MODE::CAPPED;
400
401 const SFVEC2F via_center( via->GetStart().x * m_biuTo3Dunits,
402 -via->GetStart().y * m_biuTo3Dunits );
403
404 if( viatype != VIATYPE::THROUGH )
405 {
406 // Add hole objects
407 BVH_CONTAINER_2D *layerHoleContainer = nullptr;
408 layerHoleContainer = m_layerHoleMap[layer];
409
410 // Add a hole for this layer
411 layerHoleContainer->Add( new FILLED_CIRCLE_2D( via_center, hole_inner_radius + thickness,
412 *track ) );
413 }
414 else if( layer == layer_ids[0] ) // it only adds once the THT holes
415 {
416 // Add through hole object
417 if( !capped || !( IsFrontLayer( layer ) || IsBackLayer( layer ) ) )
418 {
419 m_TH_ODs.Add( new FILLED_CIRCLE_2D( via_center, hole_inner_radius + thickness, *track ) );
420 m_viaTH_ODs.Add( new FILLED_CIRCLE_2D( via_center, hole_inner_radius + thickness, *track ) );
421 }
422
423 if( cfg.clip_silk_on_via_annuli && ring_radius > 0.0 )
424 m_viaAnnuli.Add( new FILLED_CIRCLE_2D( via_center, ring_radius, *track ) );
425
426 if( hole_inner_radius > 0.0 )
427 m_TH_IDs.Add( new FILLED_CIRCLE_2D( via_center, hole_inner_radius, *track ) );
428 }
429
430 // Add counterbore/countersink cutouts for vias
431 const auto frontMode = via->GetFrontPostMachining().value_or( PAD_DRILL_POST_MACHINING_MODE::UNKNOWN );
432 const double frontRadius = via->GetFrontPostMachiningSize() * m_biuTo3Dunits / 2.0;
433
436 && frontRadius > hole_inner_radius )
437 {
438 if( layer == layer_ids[0] ) // only add once for front layer
439 {
441 m_frontCounterboreCutouts.Add( new FILLED_CIRCLE_2D( via_center, frontRadius, *track ) );
442 else if( frontMode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
443 m_frontCountersinkCutouts.Add( new FILLED_CIRCLE_2D( via_center, frontRadius, *track ) );
444 }
445
446 BVH_CONTAINER_2D *layerHoleContainer = m_layerHoleMap[layer];
447 layerHoleContainer->Add( new FILLED_CIRCLE_2D( via_center, frontRadius + thickness, *track ) );
448 }
449
450 const auto backMode = via->GetBackPostMachining().value_or( PAD_DRILL_POST_MACHINING_MODE::UNKNOWN );
451 const double backRadius = via->GetBackPostMachiningSize() * m_biuTo3Dunits / 2.0;
452
455 && backRadius > hole_inner_radius )
456 {
457
458 if( layer == layer_ids.back() ) // only add once for back layer
459 {
461 m_backCounterboreCutouts.Add( new FILLED_CIRCLE_2D( via_center, backRadius, *track ) );
462 else if( backMode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
463 m_backCountersinkCutouts.Add( new FILLED_CIRCLE_2D( via_center, backRadius, *track ) );
464 }
465
466 BVH_CONTAINER_2D *layerHoleContainer = m_layerHoleMap[layer];
467 layerHoleContainer->Add( new FILLED_CIRCLE_2D( via_center, backRadius + thickness, *track ) );
468 }
469
470 // Add backdrill cutouts for vias - affects specific layers only
471 float backdrillRadius = via->GetSecondaryDrillSize().value_or( 0 ) * m_biuTo3Dunits / 2.0f ;
472
473 // Only add if backdrill is larger than original hole
474 if( backdrillRadius > hole_inner_radius + thickness )
475 {
476 PCB_LAYER_ID secStart = via->GetSecondaryDrillStartLayer();
477 PCB_LAYER_ID secEnd = via->GetSecondaryDrillEndLayer();
478
479 bool validLyPair = secStart >= 0 && secEnd >= 0;
480
481 if( validLyPair && LAYER_RANGE( secStart, secEnd, m_copperLayersCount ).Contains( layer ) )
482 {
483 // Add to layer hole map for this layer
484 BVH_CONTAINER_2D* layerHoleContainer = m_layerHoleMap[layer];
485
486 layerHoleContainer->Add( new FILLED_CIRCLE_2D( via_center, backdrillRadius, *track ) );
487 }
488
489 if( validLyPair && layer == secStart )
490 m_backdrillCutouts.Add( new FILLED_CIRCLE_2D( via_center, backdrillRadius, *track ) );
491 }
492
493 // Add tertiary drill cutouts for vias - affects specific layers only
494 float tertiaryDrillRadius = via->GetTertiaryDrillSize().value_or( 0 ) * m_biuTo3Dunits / 2.0f ;
495
496 // Only add if tertiary drill is larger than original hole
497 if( tertiaryDrillRadius > hole_inner_radius + thickness )
498 {
499 PCB_LAYER_ID terStart = via->GetTertiaryDrillStartLayer();
500 PCB_LAYER_ID terEnd = via->GetTertiaryDrillEndLayer();
501
502 bool validLyPair = terStart >= 0 && terEnd >= 0;
503
504 if( validLyPair && LAYER_RANGE( terStart, terEnd, m_copperLayersCount ).Contains( layer ) )
505 {
506 // Add to layer hole map for this layer
507 BVH_CONTAINER_2D* layerHoleContainer = m_layerHoleMap[layer];
508
509 layerHoleContainer->Add( new FILLED_CIRCLE_2D( via_center, tertiaryDrillRadius, *track ) );
510 }
511
512 if( validLyPair && layer == terStart )
513 m_tertiarydrillCutouts.Add( new FILLED_CIRCLE_2D( via_center, tertiaryDrillRadius, *track ) );
514 }
515 }
516
517 if( cfg.DifferentiatePlatedCopper() && layer == F_Cu )
518 {
520 ERROR_INSIDE );
521 }
522 else if( cfg.DifferentiatePlatedCopper() && layer == B_Cu )
523 {
525 ERROR_INSIDE );
526 }
527 }
528
529 if( aStop.stop_requested() )
530 return;
531 }
532
533 // Create VIAS and THTs objects and add it to holes containers
534 for( PCB_LAYER_ID layer : layer_ids )
535 {
536 if( !m_layerHoleOdPolys.contains( layer ) )
537 {
539 }
540
541 if( !m_layerHoleIdPolys.contains( layer ) )
542 {
544 }
545
546 // ADD TRACKS
547 const unsigned int nTracks = trackList.size();
548
549 for( unsigned int trackIdx = 0; trackIdx < nTracks; ++trackIdx )
550 {
551 const PCB_TRACK *track = trackList[trackIdx];
552
553 if( !track->IsOnLayer( layer ) )
554 continue;
555
556 // ADD VIAS and THT
557 if( track->Type() == PCB_VIA_T )
558 {
559 const PCB_VIA* via = static_cast<const PCB_VIA*>( track );
560 const VIATYPE viatype = via->GetViaType();
561
562 // Add outer holes of VIAs
563 SHAPE_POLY_SET *layerOuterHolesPoly = nullptr;
564 SHAPE_POLY_SET *layerInnerHolesPoly = nullptr;
565
566 // found
567 wxASSERT( m_layerHoleOdPolys.contains( layer ) );
568 wxASSERT( m_layerHoleIdPolys.contains( layer ) );
569
570 layerOuterHolesPoly = m_layerHoleOdPolys[layer];
571 layerInnerHolesPoly = m_layerHoleIdPolys[layer];
572
573 const int holediameter = via->GetDrillValue();
574 const int hole_outer_radius = (holediameter / 2) + GetHolePlatingThickness();
575
576 if( viatype != VIATYPE::THROUGH )
577 {
578 // Add PCB_VIA hole contours
579 TransformCircleToPolygon( *layerOuterHolesPoly, via->GetStart(), hole_outer_radius,
580 via->GetMaxError(), ERROR_INSIDE );
581
582 TransformCircleToPolygon( *layerInnerHolesPoly, via->GetStart(), holediameter / 2,
583 via->GetMaxError(), ERROR_INSIDE );
584 }
585 else if( layer == layer_ids[0] ) // it only adds once the THT holes
586 {
587 const int hole_outer_ring_radius = KiROUND( via->GetWidth( layer ) / 2.0 );
588
589 // Add through hole contours
590 TransformCircleToPolygon( m_TH_ODPolys, via->GetStart(), hole_outer_radius,
591 via->GetMaxError(), ERROR_INSIDE );
592
593 // Add same thing for vias only
594 TransformCircleToPolygon( m_viaTH_ODPolys, via->GetStart(), hole_outer_radius,
595 via->GetMaxError(), ERROR_INSIDE );
596
598 {
599 TransformCircleToPolygon( m_viaAnnuliPolys, via->GetStart(), hole_outer_ring_radius,
600 via->GetMaxError(), ERROR_INSIDE );
601 }
602 }
603
604 // Add counterbore/countersink polygons for vias
605 const auto frontMode = via->GetFrontPostMachining();
606
607 if( frontMode.has_value()
609 && frontMode.value() != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN )
610 {
611 const int frontRadiusBIU = via->GetFrontPostMachiningSize() / 2;
612
613 if( frontRadiusBIU > holediameter / 2 )
614 {
615 int frontRadiusOuterBIU = frontRadiusBIU + GetHolePlatingThickness();
616
617 if( frontMode.value() == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE )
618 {
619 TransformCircleToPolygon( m_frontCounterborePolys, via->GetStart(), frontRadiusBIU,
620 via->GetMaxError(), ERROR_INSIDE );
621 }
622 else if( frontMode.value() == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
623 {
624 TransformCircleToPolygon( m_frontCountersinkPolys, via->GetStart(), frontRadiusBIU,
625 via->GetMaxError(), ERROR_INSIDE );
626 }
627
628 TransformCircleToPolygon( *layerOuterHolesPoly, via->GetStart(), frontRadiusOuterBIU,
629 via->GetMaxError(), ERROR_INSIDE );
630 TransformCircleToPolygon( *layerInnerHolesPoly, via->GetStart(), frontRadiusBIU,
631 via->GetMaxError(), ERROR_INSIDE );
632 }
633 }
634
635 const auto backMode = via->GetBackPostMachining();
636
637 if( backMode.has_value()
639 && backMode.value() != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN )
640 {
641 const int backRadiusBIU = via->GetBackPostMachiningSize() / 2;
642
643 if( backRadiusBIU > holediameter / 2 )
644 {
645 int backRadiusOuterBIU = backRadiusBIU + GetHolePlatingThickness();
646
647 if( backMode.value() == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE )
648 {
649 TransformCircleToPolygon( m_backCounterborePolys, via->GetStart(), backRadiusBIU,
650 via->GetMaxError(), ERROR_INSIDE );
651 }
652 else if( backMode.value() == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
653 {
654 TransformCircleToPolygon( m_backCountersinkPolys, via->GetStart(), backRadiusBIU,
655 via->GetMaxError(), ERROR_INSIDE );
656 }
657
658 TransformCircleToPolygon( *layerOuterHolesPoly, via->GetStart(), backRadiusOuterBIU,
659 via->GetMaxError(), ERROR_INSIDE );
660 TransformCircleToPolygon( *layerInnerHolesPoly, via->GetStart(), backRadiusBIU,
661 via->GetMaxError(), ERROR_INSIDE );
662 }
663 }
664
665 // Add backdrill polygons for vias - affects specific layers only
666 const auto secondaryDrillSize = via->GetSecondaryDrillSize();
667
668 if( secondaryDrillSize.has_value() && secondaryDrillSize.value() > 0 )
669 {
670 const int backdrillRadiusBIU = secondaryDrillSize.value() / 2;
671 const int backdrillOuterRadiusBIU = backdrillRadiusBIU + GetHolePlatingThickness();
672 const int holeOuterRadiusBIU = hole_outer_radius;
673
674 // Only add if backdrill is larger than original hole outer diameter
675 if( backdrillOuterRadiusBIU > holeOuterRadiusBIU )
676 {
677 PCB_LAYER_ID secStart = via->GetSecondaryDrillStartLayer();
678 PCB_LAYER_ID secEnd = via->GetSecondaryDrillEndLayer();
679 bool validLyPair = secStart >= 0 && secEnd >= 0;
680
681 // Iterate through layers affected by backdrill
682 if( validLyPair && LAYER_RANGE( secStart, secEnd, m_copperLayersCount ).Contains( layer ) )
683 {
684 TransformCircleToPolygon( *m_layerHoleOdPolys[layer], via->GetStart(),
685 backdrillOuterRadiusBIU, via->GetMaxError(), ERROR_INSIDE );
686
687 TransformCircleToPolygon( *m_layerHoleIdPolys[layer], via->GetStart(),
688 backdrillRadiusBIU, via->GetMaxError(), ERROR_INSIDE );
689
691 backdrillOuterRadiusBIU, via->GetMaxError(), ERROR_INSIDE );
692 }
693 }
694 }
695
696 // Add backdrill polygons for vias - affects specific layers only
697 const auto tertiaryDrillSize = via->GetTertiaryDrillSize();
698
699 if( tertiaryDrillSize.has_value() && tertiaryDrillSize.value() > 0 )
700 {
701 const int backdrillRadiusBIU = tertiaryDrillSize.value() / 2;
702 const int backdrillOuterRadiusBIU = backdrillRadiusBIU + GetHolePlatingThickness();
703 const int holeOuterRadiusBIU = hole_outer_radius;
704
705 // Only add if backdrill is larger than original hole outer diameter
706 if( backdrillOuterRadiusBIU > holeOuterRadiusBIU )
707 {
708 PCB_LAYER_ID terStart = via->GetTertiaryDrillStartLayer();
709 PCB_LAYER_ID terEnd = via->GetTertiaryDrillEndLayer();
710 bool validLyPair = terStart >= 0 && terEnd >= 0;
711
712 if( validLyPair && LAYER_RANGE( terStart, terEnd, m_copperLayersCount ).Contains( layer ) )
713 {
714 PCB_LAYER_ID backdrillLayer = layer;
715
716 TransformCircleToPolygon( *m_layerHoleOdPolys[backdrillLayer], via->GetStart(),
717 backdrillOuterRadiusBIU, via->GetMaxError(), ERROR_INSIDE );
718
719 TransformCircleToPolygon( *m_layerHoleIdPolys[backdrillLayer], via->GetStart(),
720 backdrillRadiusBIU, via->GetMaxError(), ERROR_INSIDE );
721
723 backdrillOuterRadiusBIU, via->GetMaxError(), ERROR_INSIDE );
724 }
725 }
726 }
727 }
728
729 if( aStop.stop_requested() )
730 return;
731 }
732 }
733
734 // Creates vertical outline contours of the tracks and add it to the poly of the layer
736 {
737 for( PCB_LAYER_ID layer : layer_ids )
738 {
739 wxASSERT( m_layers_poly.contains( layer ) );
740
741 SHAPE_POLY_SET *layerPoly = m_layers_poly[layer];
742
743 // ADD TRACKS
744 unsigned int nTracks = trackList.size();
745
746 for( unsigned int trackIdx = 0; trackIdx < nTracks; ++trackIdx )
747 {
748 const PCB_TRACK *track = trackList[trackIdx];
749
750 if( !track->IsOnLayer( layer ) )
751 continue;
752
753 // Skip vias annulus when not flashed on this layer
754 if( track->Type() == PCB_VIA_T && !static_cast<const PCB_VIA*>( track )->FlashLayer( layer ) )
755 continue;
756
757 // Add the track/via contour
758 track->TransformShapeToPolygon( *layerPoly, layer, 0, track->GetMaxError(), ERROR_INSIDE );
759 }
760 }
761 }
762
763 if( aStop.stop_requested() )
764 return;
765
766 // Add holes of footprints
767 for( FOOTPRINT* footprint : m_board->Footprints() )
768 {
769 for( PAD* pad : footprint->Pads() )
770 {
771 // Note: holes of NPTH are already built by GetBoardPolygonOutlines
772 if( !pad->HasHole() )
773 continue;
774
775 m_holeCount++;
776 double holeDiameter = ( pad->GetDrillSize().x + pad->GetDrillSize().y ) / 2.0;
777 m_averageHoleDiameter += static_cast<float>( holeDiameter * m_biuTo3Dunits );
778
779 if( pad->GetAttribute() == PAD_ATTRIB::NPTH )
780 {
781 // Ensure the silk drawings are clipped to the NPTH hole, like other pad/via holes
782 // even if the clip to board body is not activated (remember NPTH holes are part of
783 // the board body)
785 continue;
786 }
787
788 // The hole in the body is inflated by copper thickness
789 int inflate = KiROUND( GetHolePlatingThickness() / 2.0 );
790
791 createPadHoleShape( pad, &m_TH_ODs, inflate );
792
794 createPadHoleShape( pad, &m_viaAnnuli, inflate );
795
797
798 // Add counterbore/countersink cutouts for pads
799 const float holeDiameterUnits = static_cast<float>( ( pad->GetDrillSize().x + pad->GetDrillSize().y )
800 / 2.0 * m_biuTo3Dunits );
801 const float holeInnerRadius = holeDiameterUnits / 2.0f;
802 const SFVEC2F padCenter( pad->GetPosition().x * static_cast<float>( m_biuTo3Dunits ),
803 -pad->GetPosition().y * static_cast<float>( m_biuTo3Dunits ) );
804
805 const auto frontMode = pad->GetFrontPostMachining();
806
807 if( frontMode.has_value()
809 && frontMode.value() != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN )
810 {
811 float frontRadius = pad->GetFrontPostMachiningSize() * 0.5f * static_cast<float>( m_biuTo3Dunits );
812
813 if( frontRadius > holeInnerRadius )
814 {
815 if( frontMode.value() == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE )
816 {
817 m_frontCounterboreCutouts.Add( new FILLED_CIRCLE_2D( padCenter, frontRadius, *pad ) );
818 }
819 else if( frontMode.value() == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
820 {
821 m_frontCountersinkCutouts.Add( new FILLED_CIRCLE_2D( padCenter, frontRadius, *pad ) );
822 }
823 }
824 }
825
826 const auto backMode = pad->GetBackPostMachining();
827
828 if( backMode.has_value()
830 && backMode.value() != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN )
831 {
832 float backRadius = pad->GetBackPostMachiningSize() * 0.5f * static_cast<float>( m_biuTo3Dunits );
833
834 if( backRadius > holeInnerRadius )
835 {
836 if( backMode.value() == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE )
837 {
838 m_backCounterboreCutouts.Add( new FILLED_CIRCLE_2D( padCenter, backRadius, *pad ) );
839 }
840 else if( backMode.value() == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
841 {
842 m_backCountersinkCutouts.Add( new FILLED_CIRCLE_2D( padCenter, backRadius, *pad ) );
843 }
844 }
845 }
846
847 // Add backdrill cutouts for pads - affects specific layers only
848 const VECTOR2I& secDrillSize = pad->GetSecondaryDrillSize();
849
850 if( secDrillSize.x > 0 || secDrillSize.y > 0 )
851 {
852 const float backdrillRadius = ( secDrillSize.x + secDrillSize.y ) * 0.25f
853 * static_cast<float>( m_biuTo3Dunits );
854
855 // The hole outer diameter with plating
856 const float holeOuterRadius = holeInnerRadius
857 + static_cast<float>( GetHolePlatingThickness() * m_biuTo3Dunits );
858
859 // Only add if backdrill is larger than original hole outer diameter
860 if( backdrillRadius > holeOuterRadius )
861 {
862 PCB_LAYER_ID secStart = pad->GetSecondaryDrillStartLayer();
863 PCB_LAYER_ID secEnd = pad->GetSecondaryDrillEndLayer();
864 bool validLyPair = secStart >= 0 && secEnd >= 0;
865
866 // Iterate through layers affected by backdrill
867 if( validLyPair )
868 {
869 for( PCB_LAYER_ID backdrillLayer : LAYER_RANGE( secStart, secEnd, m_copperLayersCount ) )
870 {
871 // Add to layer hole map for this layer
872 BVH_CONTAINER_2D* layerHoleContainer = nullptr;
873
874 if( !m_layerHoleMap.contains( backdrillLayer ) )
875 {
876 layerHoleContainer = new BVH_CONTAINER_2D;
877 m_layerHoleMap[backdrillLayer] = layerHoleContainer;
878 }
879 else
880 {
881 layerHoleContainer = m_layerHoleMap[backdrillLayer];
882 }
883
884 layerHoleContainer->Add( new FILLED_CIRCLE_2D( padCenter, backdrillRadius, *pad ) );
885 }
886 }
887 }
888 }
889 }
890
891 if( aStop.stop_requested() )
892 return;
893 }
894
895 if( m_holeCount )
897
898 // Add contours of the pad holes (pads can be Circle or Segment holes)
899 for( FOOTPRINT* footprint : m_board->Footprints() )
900 {
901 for( PAD* pad : footprint->Pads() )
902 {
903 if( !pad->HasHole() )
904 continue;
905
906 // The hole in the body is inflated by copper thickness.
907 const int inflate = GetHolePlatingThickness();
908
909 if( pad->GetAttribute() != PAD_ATTRIB::NPTH )
910 {
912 pad->TransformHoleToPolygon( m_viaAnnuliPolys, inflate, pad->GetMaxError(), ERROR_INSIDE );
913
914 pad->TransformHoleToPolygon( m_TH_ODPolys, inflate, pad->GetMaxError(), ERROR_INSIDE );
915 }
916 else
917 {
918 // If not plated, no copper.
920 pad->TransformHoleToPolygon( m_viaAnnuliPolys, 0, pad->GetMaxError(), ERROR_INSIDE );
921
922 pad->TransformHoleToPolygon( m_NPTH_ODPolys, 0, pad->GetMaxError(), ERROR_INSIDE );
923 }
924
925
926 // Add counterbore/countersink polygons for pads
927 const double holeDiameter = ( pad->GetDrillSize().x + pad->GetDrillSize().y ) / 2.0;
928 const int holeRadius = KiROUND( holeDiameter / 2.0 );
929
930 const std::optional<PAD_DRILL_POST_MACHINING_MODE> frontMode = pad->GetFrontPostMachining();
931
932 if( frontMode.has_value()
934 && frontMode.value() != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN )
935 {
936 const int frontRadiusBIU = pad->GetFrontPostMachiningSize() / 2;
937
938 if( frontRadiusBIU > holeRadius )
939 {
940 if( frontMode.value() == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE )
941 {
942 TransformCircleToPolygon( m_frontCounterborePolys, pad->GetPosition(), frontRadiusBIU,
943 pad->GetMaxError(), ERROR_INSIDE );
944 }
945 else if( frontMode.value() == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
946 {
947 TransformCircleToPolygon( m_frontCountersinkPolys, pad->GetPosition(), frontRadiusBIU,
948 pad->GetMaxError(), ERROR_INSIDE );
949 }
950 }
951 }
952
953 const std::optional<PAD_DRILL_POST_MACHINING_MODE> backMode = pad->GetBackPostMachining();
954
955 if( backMode.has_value()
957 && backMode.value() != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN )
958 {
959 const int backRadiusBIU = pad->GetBackPostMachiningSize() / 2;
960
961 if( backRadiusBIU > holeRadius )
962 {
963 if( backMode.value() == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE )
964 {
965 TransformCircleToPolygon( m_backCounterborePolys, pad->GetPosition(), backRadiusBIU,
966 pad->GetMaxError(), ERROR_INSIDE );
967 }
968 else if( backMode.value() == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
969 {
970 TransformCircleToPolygon( m_backCountersinkPolys, pad->GetPosition(), backRadiusBIU,
971 pad->GetMaxError(), ERROR_INSIDE );
972 }
973 }
974 }
975
976 // Add backdrill polygons for pads - affects specific layers only
977 const VECTOR2I& secDrillSize = pad->GetSecondaryDrillSize();
978
979 if( secDrillSize.x > 0 || secDrillSize.y > 0 )
980 {
981 const int backdrillRadiusBIU = ( secDrillSize.x + secDrillSize.y ) / 4;
982 const int holeOuterRadiusBIU = holeRadius + inflate;
983
984 // Only add if backdrill is larger than original hole outer diameter
985 if( backdrillRadiusBIU > holeOuterRadiusBIU )
986 {
987 PCB_LAYER_ID secStart = pad->GetSecondaryDrillStartLayer();
988 PCB_LAYER_ID secEnd = pad->GetSecondaryDrillEndLayer();
989 bool validLyPair = secStart >= 0 && secEnd >= 0;
990
991 if( validLyPair )
992 {
993 TransformCircleToPolygon( m_BackdrillPolys, pad->GetPosition(), backdrillRadiusBIU,
994 pad->GetMaxError(), ERROR_INSIDE );
995
996 // Iterate through layers affected by backdrill
997 for( PCB_LAYER_ID backdrillLayer : LAYER_RANGE( secStart, secEnd, m_copperLayersCount ) )
998 {
999 // Add polygon to per-layer hole polys
1000 SHAPE_POLY_SET* layerHolePoly = nullptr;
1001
1002 if( !m_layerHoleOdPolys.contains( backdrillLayer ) )
1003 {
1004 layerHolePoly = new SHAPE_POLY_SET;
1005 m_layerHoleOdPolys[backdrillLayer] = layerHolePoly;
1006 }
1007 else
1008 {
1009 layerHolePoly = m_layerHoleOdPolys[backdrillLayer];
1010 }
1011
1012 TransformCircleToPolygon( *layerHolePoly, pad->GetPosition(), backdrillRadiusBIU,
1013 pad->GetMaxError(), ERROR_INSIDE );
1014 }
1015 }
1016 }
1017 }
1018 }
1019
1020 if( aStop.stop_requested() )
1021 return;
1022 }
1023
1024 // Add footprints copper items (pads, shapes and text) to containers
1025 for( PCB_LAYER_ID layer : layer_ids )
1026 {
1027 wxASSERT( m_layerMap.contains( layer ) );
1028
1029 BVH_CONTAINER_2D *layerContainer = m_layerMap[layer];
1030
1031 for( FOOTPRINT* fp : m_board->Footprints() )
1032 {
1033 addPads( fp, layerContainer, layer );
1034 addFootprintShapes( fp, layerContainer, layer, visibilityFlags );
1035
1036 // Add copper item to the plated copper polygon list if required
1037 if( cfg.DifferentiatePlatedCopper() && IsExternalCopperLayer( layer ) )
1038 {
1040
1041 fp->TransformPadsToPolySet( *layerPoly, layer, 0, fp->GetMaxError(), ERROR_INSIDE );
1042 transformFPTextToPolySet( fp, layer, visibilityFlags, *layerPoly, fp->GetMaxError(), ERROR_INSIDE );
1043 transformFPShapesToPolySet( fp, layer, *layerPoly, fp->GetMaxError(), ERROR_INSIDE );
1044 }
1045
1046 // Add copper item to poly contours (vertical outlines) if required
1048 {
1049 wxASSERT( m_layers_poly.contains( layer ) );
1050
1051 SHAPE_POLY_SET* layerPoly = m_layers_poly[layer];
1052
1053 fp->TransformPadsToPolySet( *layerPoly, layer, 0, fp->GetMaxError(), ERROR_INSIDE );
1054 transformFPTextToPolySet( fp, layer, visibilityFlags, *layerPoly, fp->GetMaxError(), ERROR_INSIDE );
1055 transformFPShapesToPolySet( fp, layer, *layerPoly, fp->GetMaxError(), ERROR_INSIDE );
1056 }
1057
1058 if( aStop.stop_requested() )
1059 return;
1060 }
1061 }
1062
1063 // Add graphic item on copper layers to object containers
1064 for( PCB_LAYER_ID layer : layer_ids )
1065 {
1066 wxASSERT( m_layerMap.contains( layer ) );
1067
1068 BVH_CONTAINER_2D *layerContainer = m_layerMap[layer];
1069
1070 // Add graphic items on copper layers (texts and other graphics)
1071 for( BOARD_ITEM* item : m_board->Drawings() )
1072 {
1073 if( !item->IsOnLayer( layer ) )
1074 continue;
1075
1076 switch( item->Type() )
1077 {
1078 case PCB_SHAPE_T:
1079 addShape( static_cast<PCB_SHAPE*>( item ), layerContainer, item, layer );
1080 break;
1081
1082 case PCB_TEXT_T:
1083 addText( static_cast<PCB_TEXT*>( item ), layerContainer, item );
1084 break;
1085
1086 case PCB_TEXTBOX_T:
1087 addShape( static_cast<PCB_TEXTBOX*>( item ), layerContainer, item );
1088 break;
1089
1090 case PCB_TABLE_T:
1091 addTable( static_cast<PCB_TABLE*>( item ), layerContainer, item );
1092 break;
1093
1094 case PCB_BARCODE_T:
1095 addBarCode( static_cast<PCB_BARCODE*>( item ), layerContainer, item );
1096 break;
1097
1098 case PCB_DIM_ALIGNED_T:
1099 case PCB_DIM_CENTER_T:
1100 case PCB_DIM_RADIAL_T:
1102 case PCB_DIM_LEADER_T:
1103 addShape( static_cast<PCB_DIMENSION_BASE*>( item ), layerContainer, item );
1104 break;
1105
1106 case PCB_REFERENCE_IMAGE_T: // ignore
1107 break;
1108
1109 default:
1110 wxLogTrace( m_logTrace, wxT( "createLayers: item type: %d not implemented" ), item->Type() );
1111 break;
1112 }
1113
1114 // Add copper item to the plated copper polygon list if required
1115 if( cfg.DifferentiatePlatedCopper() && IsExternalCopperLayer( layer ) )
1116 {
1118
1119 // Note: for TEXT and TEXTBOX, TransformShapeToPolygon returns the bounding
1120 // box shape, not the exact text shape. So it is not used for these items
1121 if( item->Type() == PCB_TEXTBOX_T )
1122 {
1123 PCB_TEXTBOX* text_box = static_cast<PCB_TEXTBOX*>( item );
1124 text_box->TransformTextToPolySet( *copperPolys, 0, text_box->GetMaxError(), ERROR_INSIDE );
1125
1126 // Add box outlines
1127 text_box->PCB_SHAPE::TransformShapeToPolygon( *copperPolys, layer, 0, text_box->GetMaxError(),
1128 ERROR_INSIDE );
1129 }
1130 else if( item->Type() == PCB_TEXT_T )
1131 {
1132 PCB_TEXT* text = static_cast<PCB_TEXT*>( item );
1133 text->TransformTextToPolySet( *copperPolys, 0, text->GetMaxError(), ERROR_INSIDE );
1134 }
1135 else if( item->Type() != PCB_REFERENCE_IMAGE_T )
1136 {
1137 item->TransformShapeToPolySet( *copperPolys, layer, 0, item->GetMaxError(), ERROR_INSIDE );
1138 }
1139 }
1140
1141 // Add copper item to poly contours (vertical outlines) if required
1143 {
1144 wxASSERT( m_layers_poly.contains( layer ) );
1145
1146 SHAPE_POLY_SET *layerPoly = m_layers_poly[layer];
1147
1148 switch( item->Type() )
1149 {
1150 case PCB_SHAPE_T:
1151 item->TransformShapeToPolySet( *layerPoly, layer, 0, item->GetMaxError(), ERROR_INSIDE );
1152 break;
1153
1154 case PCB_TEXT_T:
1155 {
1156 PCB_TEXT* text = static_cast<PCB_TEXT*>( item );
1157
1158 text->TransformTextToPolySet( *layerPoly, 0, text->GetMaxError(), ERROR_INSIDE );
1159 break;
1160 }
1161
1162 case PCB_TEXTBOX_T:
1163 {
1164 PCB_TEXTBOX* textbox = static_cast<PCB_TEXTBOX*>( item );
1165
1166 if( textbox->IsBorderEnabled() )
1167 {
1168 textbox->PCB_SHAPE::TransformShapeToPolygon( *layerPoly, layer, 0, textbox->GetMaxError(),
1169 ERROR_INSIDE );
1170 }
1171
1172 textbox->TransformTextToPolySet( *layerPoly, 0, textbox->GetMaxError(), ERROR_INSIDE );
1173 break;
1174 }
1175
1176 case PCB_TABLE_T:
1177 {
1178 PCB_TABLE* table = static_cast<PCB_TABLE*>( item );
1179
1180 for( PCB_TABLECELL* cell : table->GetCells() )
1181 cell->TransformTextToPolySet( *layerPoly, 0, cell->GetMaxError(), ERROR_INSIDE );
1182
1183 table->DrawBorders(
1184 [&]( const VECTOR2I& ptA, const VECTOR2I& ptB,
1185 const STROKE_PARAMS& stroke )
1186 {
1187 SHAPE_SEGMENT seg( ptA, ptB, stroke.GetWidth() );
1188 seg.TransformToPolygon( *layerPoly, table->GetMaxError(), ERROR_INSIDE );
1189 } );
1190 break;
1191 }
1192
1193 case PCB_BARCODE_T:
1194 {
1195 PCB_BARCODE* bar_code = static_cast<PCB_BARCODE*>( item );
1196
1197 bar_code->TransformShapeToPolySet( *layerPoly, layer, 0, 0, ERROR_INSIDE );
1198 break;
1199 }
1200
1201 case PCB_DIM_ALIGNED_T:
1202 case PCB_DIM_CENTER_T:
1203 case PCB_DIM_RADIAL_T:
1205 case PCB_DIM_LEADER_T:
1206 {
1207 PCB_DIMENSION_BASE* dimension = static_cast<PCB_DIMENSION_BASE*>( item );
1208
1209 dimension->TransformTextToPolySet( *layerPoly, 0, dimension->GetMaxError(), ERROR_INSIDE );
1210
1211 for( const std::shared_ptr<SHAPE>& shape : dimension->GetShapes() )
1212 shape->TransformToPolygon( *layerPoly, dimension->GetMaxError(), ERROR_INSIDE );
1213
1214 break;
1215 }
1216
1217 case PCB_REFERENCE_IMAGE_T: // ignore
1218 break;
1219
1220 default:
1221 wxLogTrace( m_logTrace, wxT( "createLayers: item type: %d not implemented" ), item->Type() );
1222 break;
1223 }
1224 }
1225 }
1226
1227 if( aStop.stop_requested() )
1228 return;
1229 }
1230
1231 if( aStop.stop_requested() )
1232 return;
1233
1234 if( cfg.show_zones )
1235 {
1236 if( aStatusReporter )
1237 aStatusReporter->Report( _( "Create zones" ) );
1238
1239 std::vector<std::pair<ZONE*, PCB_LAYER_ID>> zones;
1240 std::unordered_map<PCB_LAYER_ID, std::unique_ptr<std::mutex>> layer_lock;
1241
1242 for( ZONE* zone : m_board->Zones() )
1243 {
1244 for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
1245 {
1246 zones.emplace_back( std::make_pair( zone, layer ) );
1247 layer_lock.emplace( layer, std::make_unique<std::mutex>() );
1248
1249 if( cfg.DifferentiatePlatedCopper() && IsExternalCopperLayer( layer ) )
1250 {
1252
1253 zone->TransformShapeToPolygon( *copperPolys, layer, 0, zone->GetMaxError(), ERROR_INSIDE );
1254 }
1255 }
1256
1257 if( aStop.stop_requested() )
1258 return;
1259 }
1260
1261 // Add zones objects
1262 std::atomic<size_t> nextZone( 0 );
1263 std::atomic<size_t> threadsFinished( 0 );
1264
1265 size_t parallelThreadCount = std::min<size_t>( zones.size(),
1266 std::max<size_t>( std::thread::hardware_concurrency(), 2 ) );
1267
1268 for( size_t ii = 0; ii < parallelThreadCount; ++ii )
1269 {
1270 std::thread t = std::thread( [&]()
1271 {
1272 for( size_t areaId = nextZone.fetch_add( 1 );
1273 areaId < zones.size();
1274 areaId = nextZone.fetch_add( 1 ) )
1275 {
1276 ZONE* zone = zones[areaId].first;
1277
1278 if( zone == nullptr )
1279 break;
1280
1281 PCB_LAYER_ID layer = zones[areaId].second;
1282
1283 if( m_layerMap.contains( layer ) )
1284 addSolidAreasShapes( zone, m_layerMap[layer], layer );
1285
1287 && m_layers_poly.contains( layer ) )
1288 {
1289 auto mut_it = layer_lock.find( layer );
1290
1291 if( mut_it != layer_lock.end() )
1292 {
1293 std::lock_guard< std::mutex > lock( *( mut_it->second ) );
1294 zone->TransformSolidAreasShapesToPolygon( layer, *m_layers_poly[layer] );
1295 }
1296 }
1297 }
1298
1299 threadsFinished++;
1300 } );
1301
1302 t.detach();
1303 }
1304
1305 while( threadsFinished < parallelThreadCount )
1306 std::this_thread::sleep_for( std::chrono::milliseconds( 10 ) );
1307 }
1308 // End Build Copper layers
1309
1310 if( aStop.stop_requested() )
1311 return;
1312
1313 // This will make a union of all added contours
1314 m_TH_ODPolys.Simplify();
1315 m_NPTH_ODPolys.Simplify();
1316 m_viaTH_ODPolys.Simplify();
1317 m_viaAnnuliPolys.Simplify();
1318
1319 m_frontCounterborePolys.Simplify();
1320 m_backCounterborePolys.Simplify();
1321 m_frontCountersinkPolys.Simplify();
1322 m_backCountersinkPolys.Simplify();
1323
1324 // Remove counterbore/countersink/backdrill cutouts from via annuli (both front and back)
1325 // The via annuli are used for silkscreen clipping and shouldn't include areas removed
1326 // by counterbore, countersink, or backdrill operations
1327 if( m_viaAnnuliPolys.OutlineCount() > 0 )
1328 {
1329 if( m_frontCounterborePolys.OutlineCount() > 0 )
1330 m_viaAnnuliPolys.BooleanSubtract( m_frontCounterborePolys );
1331
1332 if( m_backCounterborePolys.OutlineCount() > 0 )
1333 m_viaAnnuliPolys.BooleanSubtract( m_backCounterborePolys );
1334
1335 if( m_frontCountersinkPolys.OutlineCount() > 0 )
1336 m_viaAnnuliPolys.BooleanSubtract( m_frontCountersinkPolys );
1337
1338 if( m_backCountersinkPolys.OutlineCount() > 0 )
1339 m_viaAnnuliPolys.BooleanSubtract( m_backCountersinkPolys );
1340
1341 if( m_BackdrillPolys.OutlineCount() > 0 )
1342 m_viaAnnuliPolys.BooleanSubtract( m_BackdrillPolys );
1343
1344 if( m_TertiarydrillPolys.OutlineCount() > 0 )
1345 m_viaAnnuliPolys.BooleanSubtract( m_TertiarydrillPolys );
1346 }
1347
1348 // Build Tech layers
1349 // Based on:
1350 // https://github.com/KiCad/kicad-source-mirror/blob/master/3d-viewer/3d_draw.cpp#L1059
1351 if( aStatusReporter )
1352 aStatusReporter->Report( _( "Build Tech layers" ) );
1353
1354 // draw graphic items, on technical layers
1355
1356 LSEQ techLayerList = LSET::AllNonCuMask().Seq( {
1357 B_Adhes,
1358 F_Adhes,
1359 B_Paste,
1360 F_Paste,
1361 B_SilkS,
1362 F_SilkS,
1363 B_Mask,
1364 F_Mask,
1365
1366 // Aux Layers
1367 Dwgs_User,
1368 Cmts_User,
1369 Eco1_User,
1370 Eco2_User,
1371 F_Fab,
1372 B_Fab,
1373 F_CrtYd,
1374 B_CrtYd,
1375 User_1,
1376 User_2,
1377 User_3,
1378 User_4,
1379 User_5,
1380 User_6,
1381 User_7,
1382 User_8,
1383 User_9,
1384 User_10,
1385 User_11,
1386 User_12,
1387 User_13,
1388 User_14,
1389 User_15,
1390 User_16,
1391 User_17,
1392 User_18,
1393 User_19,
1394 User_20,
1395 User_21,
1396 User_22,
1397 User_23,
1398 User_24,
1399 User_25,
1400 User_26,
1401 User_27,
1402 User_28,
1403 User_29,
1404 User_30,
1405 User_31,
1406 User_32,
1407 User_33,
1408 User_34,
1409 User_35,
1410 User_36,
1411 User_37,
1412 User_38,
1413 User_39,
1414 User_40,
1415 User_41,
1416 User_42,
1417 User_43,
1418 User_44,
1419 User_45,
1420 } );
1421
1422 std::bitset<LAYER_3D_END> enabledFlags = visibilityFlags;
1423
1425 {
1426 enabledFlags.set( LAYER_3D_SOLDERMASK_TOP );
1427 enabledFlags.set( LAYER_3D_SOLDERMASK_BOTTOM );
1428 }
1429
1430 if( aStop.stop_requested() )
1431 return;
1432
1433 for( PCB_LAYER_ID layer : techLayerList )
1434 {
1435 if( aStatusReporter )
1436 aStatusReporter->Report( wxString::Format( _( "Build Tech layer %d" ), (int) layer ) );
1437
1438 if( !Is3dLayerEnabled( layer, enabledFlags ) )
1439 continue;
1440
1441 BVH_CONTAINER_2D *layerContainer = new BVH_CONTAINER_2D;
1442 m_layerMap[layer] = layerContainer;
1443
1444 SHAPE_POLY_SET *layerPoly = new SHAPE_POLY_SET;
1445 m_layers_poly[layer] = layerPoly;
1446
1447 if( Is3dLayerEnabled( layer, visibilityFlags ) )
1448 {
1449 // Add drawing objects
1450 for( BOARD_ITEM* item : m_board->Drawings() )
1451 {
1452 if( !item->IsOnLayer( layer ) )
1453 continue;
1454
1455 switch( item->Type() )
1456 {
1457 case PCB_SHAPE_T:
1458 addShape( static_cast<PCB_SHAPE*>( item ), layerContainer, item, layer );
1459 break;
1460
1461 case PCB_TEXT_T:
1462 addText( static_cast<PCB_TEXT*>( item ), layerContainer, item );
1463 break;
1464
1465 case PCB_TEXTBOX_T:
1466 addShape( static_cast<PCB_TEXTBOX*>( item ), layerContainer, item );
1467 break;
1468
1469 case PCB_TABLE_T:
1470 addTable( static_cast<PCB_TABLE*>( item ), layerContainer, item );
1471 break;
1472
1473 case PCB_BARCODE_T:
1474 addBarCode( static_cast<PCB_BARCODE*>( item ), layerContainer, item );
1475 break;
1476
1477 case PCB_DIM_ALIGNED_T:
1478 case PCB_DIM_CENTER_T:
1479 case PCB_DIM_RADIAL_T:
1481 case PCB_DIM_LEADER_T:
1482 addShape( static_cast<PCB_DIMENSION_BASE*>( item ), layerContainer, item );
1483 break;
1484
1485 default:
1486 break;
1487 }
1488 }
1489
1490 // Add track, via and arc tech layers
1491 if( IsSolderMaskLayer( layer ) )
1492 {
1493 for( PCB_TRACK* track : m_board->Tracks() )
1494 {
1495 if( !track->IsOnLayer( layer ) )
1496 continue;
1497
1498 // Only vias on a external copper layer can have a solder mask
1499 PCB_LAYER_ID copper_layer = ( layer == F_Mask ) ? F_Cu : B_Cu;
1500
1501 if( track->Type() == PCB_VIA_T )
1502 {
1503 const PCB_VIA* via = static_cast<const PCB_VIA*>( track );
1504
1505 if( !via->FlashLayer( copper_layer ) )
1506 continue;
1507 }
1508
1509 int maskExpansion = track->GetSolderMaskExpansion();
1510 createTrackWithMargin( track, layerContainer, layer, maskExpansion );
1511 }
1512 }
1513
1514 // Add footprints tech layers - objects. Paste, mask, silk and fab belong to the
1515 // board fabrication outputs and render for every footprint, matching the gerber
1516 // plotters. DNP affects only the 3D model, handled by IsFootprintShown().
1517 for( FOOTPRINT* footprint : m_board->Footprints() )
1518 {
1519 if( layer == F_SilkS || layer == B_SilkS )
1520 {
1521 int linewidth = m_board->GetDesignSettings().m_LineThickness[ LAYER_CLASS_SILK ];
1522
1523 for( PAD* pad : footprint->Pads() )
1524 {
1525 if( !pad->IsOnLayer( layer ) )
1526 continue;
1527
1528 buildPadOutlineAsSegments( pad, layer, layerContainer, linewidth );
1529 }
1530 }
1531 else
1532 {
1533 addPads( footprint, layerContainer, layer );
1534 }
1535
1536 addFootprintShapes( footprint, layerContainer, layer, visibilityFlags );
1537 }
1538
1539 // Draw non copper zones
1540 if( cfg.show_zones )
1541 {
1542 for( ZONE* zone : m_board->Zones() )
1543 {
1544 if( zone->IsOnLayer( layer ) )
1545 addSolidAreasShapes( zone, layerContainer, layer );
1546 }
1547 }
1548 }
1549
1550 if( aStop.stop_requested() )
1551 return;
1552
1553 // Add item contours. We need these if we're building vertical walls or if this is a
1554 // mask layer and we're differentiating copper from plated copper.
1556 || ( cfg.DifferentiatePlatedCopper() && IsSolderMaskLayer( layer ) ) )
1557 {
1558 // DRAWINGS
1559 for( BOARD_ITEM* item : m_board->Drawings() )
1560 {
1561 if( !item->IsOnLayer( layer ) )
1562 continue;
1563
1564 switch( item->Type() )
1565 {
1566 case PCB_SHAPE_T:
1567 {
1568 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
1569 int margin = 0;
1570
1571 if( IsSolderMaskLayer( layer ) && shape->HasSolderMask() )
1572 margin = shape->GetSolderMaskExpansion();
1573
1574 item->TransformShapeToPolySet( *layerPoly, layer, margin, item->GetMaxError(), ERROR_INSIDE );
1575 break;
1576 }
1577
1578 case PCB_TEXT_T:
1579 {
1580 PCB_TEXT* text = static_cast<PCB_TEXT*>( item );
1581
1582 text->TransformTextToPolySet( *layerPoly, 0, text->GetMaxError(), ERROR_INSIDE );
1583 break;
1584 }
1585
1586 case PCB_TEXTBOX_T:
1587 {
1588 PCB_TEXTBOX* textbox = static_cast<PCB_TEXTBOX*>( item );
1589
1590 if( textbox->IsBorderEnabled() )
1591 {
1592 textbox->PCB_SHAPE::TransformShapeToPolygon( *layerPoly, layer, 0, textbox->GetMaxError(),
1593 ERROR_INSIDE );
1594 }
1595
1596 textbox->TransformTextToPolySet( *layerPoly, 0, textbox->GetMaxError(), ERROR_INSIDE );
1597 break;
1598 }
1599
1600 case PCB_TABLE_T:
1601 {
1602 PCB_TABLE* table = static_cast<PCB_TABLE*>( item );
1603
1604 for( PCB_TABLECELL* cell : table->GetCells() )
1605 cell->TransformTextToPolySet( *layerPoly, 0, cell->GetMaxError(), ERROR_INSIDE );
1606
1607 table->DrawBorders(
1608 [&]( const VECTOR2I& ptA, const VECTOR2I& ptB,
1609 const STROKE_PARAMS& stroke )
1610 {
1611 SHAPE_SEGMENT seg( ptA, ptB, stroke.GetWidth() );
1612 seg.TransformToPolygon( *layerPoly, table->GetMaxError(), ERROR_INSIDE );
1613 } );
1614
1615 break;
1616 }
1617
1618 case PCB_BARCODE_T:
1619 {
1620 PCB_BARCODE* bar_code = static_cast<PCB_BARCODE*>( item );
1621
1622 bar_code->TransformShapeToPolySet( *layerPoly, layer, 0, 0, ERROR_INSIDE );
1623 break;
1624 }
1625
1626 default:
1627 break;
1628 }
1629 }
1630
1631 if( aStop.stop_requested() )
1632 return;
1633
1634 // NON-TENTED VIAS
1635 if( ( layer == F_Mask || layer == B_Mask ) )
1636 {
1637 int maskExpansion = GetBoard()->GetDesignSettings().m_SolderMaskExpansion;
1638
1639 for( PCB_TRACK* track : m_board->Tracks() )
1640 {
1641 if( track->Type() == PCB_VIA_T )
1642 {
1643 const PCB_VIA* via = static_cast<const PCB_VIA*>( track );
1644
1645 if( via->FlashLayer( layer ) && !via->IsTented( layer ) )
1646 {
1647 track->TransformShapeToPolygon( *layerPoly, layer, maskExpansion, track->GetMaxError(),
1648 ERROR_INSIDE );
1649 }
1650 }
1651 else
1652 {
1653 if( track->HasSolderMask() )
1654 {
1655 track->TransformShapeToPolySet( *layerPoly, layer, maskExpansion, track->GetMaxError(),
1656 ERROR_INSIDE );
1657 }
1658 }
1659 }
1660 }
1661
1662 if( aStop.stop_requested() )
1663 return;
1664
1665 // FOOTPRINT CHILDREN
1666 for( FOOTPRINT* footprint : m_board->Footprints() )
1667 {
1668 if( layer == F_SilkS || layer == B_SilkS )
1669 {
1670 int linewidth = m_board->GetDesignSettings().m_LineThickness[ LAYER_CLASS_SILK ];
1671
1672 for( PAD* pad : footprint->Pads() )
1673 {
1674 if( pad->IsOnLayer( layer ) )
1675 {
1676 buildPadOutlineAsPolygon( pad, layer, *layerPoly, linewidth, pad->GetMaxError(),
1677 ERROR_INSIDE );
1678 }
1679 }
1680 }
1681 else
1682 {
1683 footprint->TransformPadsToPolySet( *layerPoly, layer, 0, footprint->GetMaxError(), ERROR_INSIDE );
1684 }
1685
1686 transformFPTextToPolySet( footprint, layer, visibilityFlags, *layerPoly, footprint->GetMaxError(),
1687 ERROR_INSIDE );
1688 transformFPShapesToPolySet( footprint, layer, *layerPoly, footprint->GetMaxError(), ERROR_INSIDE );
1689 }
1690
1691 if( aStop.stop_requested() )
1692 return;
1693
1694 if( cfg.show_zones || layer == F_Mask || layer == B_Mask )
1695 {
1696 for( ZONE* zone : m_board->Zones() )
1697 {
1698 if( zone->IsOnLayer( layer ) )
1699 zone->TransformSolidAreasShapesToPolygon( layer, *layerPoly );
1700 }
1701 }
1702
1703 if( aStop.stop_requested() )
1704 return;
1705
1706 // This will make a union of all added contours
1707 layerPoly->Simplify();
1708
1709 if( aStop.stop_requested() )
1710 return;
1711 }
1712 }
1713 // End Build Tech layers
1714
1715 if( aStop.stop_requested() )
1716 return;
1717
1718 // If we're rendering off-board silk, also render pads of footprints which are entirely
1719 // outside the board outline. This makes off-board footprints more visually recognizable.
1720 if( cfg.show_off_board_silk )
1721 {
1722 BOX2I boardBBox = m_board_poly.BBox();
1723
1724 for( FOOTPRINT* footprint : m_board->Footprints() )
1725 {
1726 if( !footprint->GetBoundingBox().Intersects( boardBBox ) )
1727 {
1728 if( footprint->IsFlipped() )
1729 addPads( footprint, m_offboardPadsBack, B_Cu );
1730 else
1731 addPads( footprint, m_offboardPadsFront, F_Cu );
1732 }
1733 }
1734
1735 m_offboardPadsFront->BuildBVH();
1736 m_offboardPadsBack->BuildBVH();
1737 }
1738
1739 if( aStop.stop_requested() )
1740 return;
1741
1742 // Simplify layer polygons
1743 if( aStatusReporter )
1744 aStatusReporter->Report( _( "Simplifying copper layer polygons" ) );
1745
1746 if( cfg.DifferentiatePlatedCopper() )
1747 {
1748 if( aStatusReporter )
1749 aStatusReporter->Report( _( "Calculating plated copper" ) );
1750
1751 // TRIM PLATED COPPER TO SOLDERMASK
1752 if( m_layers_poly.contains( F_Mask ) )
1753 m_frontPlatedCopperPolys->BooleanIntersection( *m_layers_poly.at( F_Mask ) );
1754
1755 if( m_layers_poly.contains( B_Mask ))
1756 m_backPlatedCopperPolys->BooleanIntersection( *m_layers_poly.at( B_Mask ) );
1757
1758 // Subtract plated copper from unplated copper
1759 if( m_layers_poly.contains( F_Cu ) )
1760 m_layers_poly[F_Cu]->BooleanSubtract( *m_frontPlatedCopperPolys );
1761
1762 if( m_layers_poly.contains( B_Cu ) )
1763 m_layers_poly[B_Cu]->BooleanSubtract( *m_backPlatedCopperPolys );
1764
1765 // ADD PLATED COPPER
1768
1771
1772 m_platedPadsFront->BuildBVH();
1773 m_platedPadsBack->BuildBVH();
1774
1775 if( aStop.stop_requested() )
1776 return;
1777 }
1778
1780 {
1781 std::vector<PCB_LAYER_ID> &selected_layer_id = layer_ids;
1782 std::vector<PCB_LAYER_ID> layer_id_without_F_and_B;
1783
1784 if( cfg.DifferentiatePlatedCopper() )
1785 {
1786 layer_id_without_F_and_B.clear();
1787 layer_id_without_F_and_B.reserve( layer_ids.size() );
1788
1789 for( PCB_LAYER_ID layer: layer_ids )
1790 {
1791 if( layer != F_Cu && layer != B_Cu )
1792 layer_id_without_F_and_B.push_back( layer );
1793 }
1794
1795 selected_layer_id = layer_id_without_F_and_B;
1796 }
1797
1798 if( selected_layer_id.size() > 0 )
1799 {
1800 if( aStatusReporter )
1801 {
1802 aStatusReporter->Report( wxString::Format( _( "Simplifying %d copper layers" ),
1803 (int) selected_layer_id.size() ) );
1804 }
1805
1806 std::atomic<size_t> nextItem( 0 );
1807 std::atomic<size_t> threadsFinished( 0 );
1808
1809 size_t parallelThreadCount = std::min<size_t>(
1810 std::max<size_t>( std::thread::hardware_concurrency(), 2 ),
1811 selected_layer_id.size() );
1812
1813 for( size_t ii = 0; ii < parallelThreadCount; ++ii )
1814 {
1815 std::thread t = std::thread(
1816 [&nextItem, &threadsFinished, &selected_layer_id, this]()
1817 {
1818 for( size_t i = nextItem.fetch_add( 1 );
1819 i < selected_layer_id.size();
1820 i = nextItem.fetch_add( 1 ) )
1821 {
1822 if( m_layers_poly.contains( selected_layer_id[i] ) )
1823 {
1824 // This will make a union of all added contours
1825 m_layers_poly[ selected_layer_id[i] ]->ClearArcs();
1826 m_layers_poly[ selected_layer_id[i] ]->Simplify();
1827 }
1828 }
1829
1830 threadsFinished++;
1831 } );
1832
1833 t.detach();
1834 }
1835
1836 while( threadsFinished < parallelThreadCount )
1837 std::this_thread::sleep_for( std::chrono::milliseconds( 10 ) );
1838 }
1839
1840 if( aStop.stop_requested() )
1841 return;
1842 }
1843
1844 // Simplify holes polygon contours
1845 if( aStatusReporter )
1846 aStatusReporter->Report( _( "Simplify holes contours" ) );
1847
1848 for( PCB_LAYER_ID layer : layer_ids )
1849 {
1850 if( m_layerHoleOdPolys.contains( layer ) )
1851 {
1852 // found
1853 SHAPE_POLY_SET *polyLayer = m_layerHoleOdPolys[layer];
1854 polyLayer->Simplify();
1855
1856 wxASSERT( m_layerHoleIdPolys.contains( layer ) );
1857
1858 polyLayer = m_layerHoleIdPolys[layer];
1859 polyLayer->Simplify();
1860 }
1861
1862 if( aStop.stop_requested() )
1863 return;
1864 }
1865
1866 // Build BVH (Bounding volume hierarchy) for holes and vias
1867
1868 if( aStatusReporter )
1869 aStatusReporter->Report( _( "Build BVH for holes and vias" ) );
1870
1871 m_TH_IDs.BuildBVH();
1872 m_TH_ODs.BuildBVH();
1873 m_viaAnnuli.BuildBVH();
1874
1875 if( aStop.stop_requested() )
1876 return;
1877
1878 m_frontCounterboreCutouts.BuildBVH();
1879 m_backCounterboreCutouts.BuildBVH();
1880 m_frontCountersinkCutouts.BuildBVH();
1881 m_backCountersinkCutouts.BuildBVH();
1882 m_backdrillCutouts.BuildBVH();
1883 m_tertiarydrillCutouts.BuildBVH();
1884
1885 if( aStop.stop_requested() )
1886 return;
1887
1888 if( !m_layerHoleMap.empty() )
1889 {
1890 for( std::pair<const PCB_LAYER_ID, BVH_CONTAINER_2D*>& hole : m_layerHoleMap )
1891 hole.second->BuildBVH();
1892 }
1893
1894 if( aStop.stop_requested() )
1895 return;
1896
1897 // We only need the Solder mask to initialize the BVH
1898 // because..?
1899 if( m_layerMap[B_Mask] )
1900 m_layerMap[B_Mask]->BuildBVH();
1901
1902 if( m_layerMap[F_Mask] )
1903 m_layerMap[F_Mask]->BuildBVH();
1904}
ERROR_LOC
When approximating an arc or circle, should the error be placed on the outside or inside of the curve...
@ ERROR_INSIDE
@ LAYER_CLASS_SILK
BOX2< VECTOR2I > BOX2I
Definition box2.h:927
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:995
MAP_CONTAINER_2D_BASE m_layerHoleMap
Holes for each layer.
BVH_CONTAINER_2D m_backdrillCutouts
Backdrill cutouts.
double BiuTo3dUnits() const noexcept
Board integer units To 3D units.
BVH_CONTAINER_2D * m_offboardPadsBack
BVH_CONTAINER_2D m_frontCounterboreCutouts
Counterbore cutouts on front (top)
SHAPE_POLY_SET m_TertiarydrillPolys
Board tertiary drill polygons F.Cu->in.
SHAPE_POLY_SET m_backCounterborePolys
Counterbore outer diameters on back.
SHAPE_POLY_SET m_TH_ODPolys
PTH outer diameters.
void createLayers(std::shared_ptr< REPORTER > aStatusReporter, std::stop_token aStop)
void addPads(const FOOTPRINT *aFootprint, CONTAINER_2D_BASE *aDstContainer, PCB_LAYER_ID aLayerId)
unsigned int m_viaCount
BVH_CONTAINER_2D m_frontCountersinkCutouts
Countersink cutouts on front (top)
void createTrackWithMargin(const PCB_TRACK *aTrack, CONTAINER_2D_BASE *aDstContainer, PCB_LAYER_ID aLayer, int aMargin=0)
MAP_POLY m_layerHoleOdPolys
Hole outer diameters (per layer)
void addSolidAreasShapes(const ZONE *aZone, CONTAINER_2D_BASE *aDstContainer, PCB_LAYER_ID aLayerId)
SHAPE_POLY_SET * m_frontPlatedCopperPolys
MAP_CONTAINER_2D_BASE m_layerMap
2D elements for each layer.
BVH_CONTAINER_2D m_TH_ODs
List of PTH outer diameters.
unsigned int m_trackCount
float m_averageTrackWidth
SHAPE_POLY_SET m_board_poly
Board outline polygon.
std::bitset< LAYER_3D_END > GetVisibleLayers() const
void addFootprintShapes(const FOOTPRINT *aFootprint, CONTAINER_2D_BASE *aDstContainer, PCB_LAYER_ID aLayerId, const std::bitset< LAYER_3D_END > &aVisibilityFlags)
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_viaAnnuliPolys
Via annular ring outer diameters.
BVH_CONTAINER_2D * m_offboardPadsFront
float m_averageViaHoleDiameter
BVH_CONTAINER_2D m_viaTH_ODs
List of via hole outer diameters.
SHAPE_POLY_SET m_frontCounterborePolys
Counterbore outer diameters on front.
BVH_CONTAINER_2D m_tertiarydrillCutouts
Tertiary drill cutouts.
float m_averageHoleDiameter
SHAPE_POLY_SET m_viaTH_ODPolys
Via hole outer diameters.
void addBarCode(const PCB_BARCODE *aBarCode, CONTAINER_2D_BASE *aDstContainer, const BOARD_ITEM *aOwner)
SHAPE_POLY_SET m_BackdrillPolys
Board backdrill polygons B.Cu->in.
const BOARD * GetBoard() const noexcept
BVH_CONTAINER_2D * m_platedPadsBack
void buildPadOutlineAsSegments(const PAD *aPad, PCB_LAYER_ID aLayer, CONTAINER_2D_BASE *aDstContainer, int aWidth)
MAP_POLY m_layers_poly
Amalgamated polygon contours for various types of items.
SHAPE_POLY_SET * m_backPlatedCopperPolys
EDA_3D_VIEWER_SETTINGS * m_Cfg
void addTable(const PCB_TABLE *aTable, CONTAINER_2D_BASE *aContainer, const BOARD_ITEM *aOwner)
void createPadHoleShape(const PAD *aPad, CONTAINER_2D_BASE *aDstContainer, int aInflateValue)
SHAPE_POLY_SET m_frontCountersinkPolys
Countersink outer diameters on front.
unsigned int m_holeCount
BVH_CONTAINER_2D m_viaAnnuli
List of via annular rings.
unsigned int m_copperLayersCount
BVH_CONTAINER_2D m_backCountersinkCutouts
Countersink cutouts on back (bottom)
BVH_CONTAINER_2D m_backCounterboreCutouts
Counterbore cutouts on back (bottom)
BVH_CONTAINER_2D m_TH_IDs
List of PTH inner diameters.
BVH_CONTAINER_2D * m_platedPadsFront
void addShape(const PCB_SHAPE *aShape, CONTAINER_2D_BASE *aContainer, const BOARD_ITEM *aOwner, PCB_LAYER_ID aLayer)
SHAPE_POLY_SET m_NPTH_ODPolys
NPTH outer diameters.
SHAPE_POLY_SET m_backCountersinkPolys
Countersink outer diameters on back.
bool Is3dLayerEnabled(PCB_LAYER_ID aLayer, const std::bitset< LAYER_3D_END > &aVisibilityFlags) const
Check if a layer is enabled.
double m_biuTo3Dunits
Scale factor to convert board internal units.
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:84
virtual void TransformShapeToPolySet(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aError, ERROR_LOC aErrorLoc, KIGFX::RENDER_SETTINGS *aRenderSettings=nullptr) const
Convert the item shape to a polyset.
Definition board_item.h:542
int GetMaxError() const
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1299
void Add(OBJECT_2D *aObject)
static DELETED_BOARD_ITEM * GetInstance()
Definition board_item.h:609
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
void GetFields(std::vector< PCB_FIELD * > &aVector, bool aVisibleOnly) const
Populate a std::vector with PCB_TEXTs.
DRAWINGS & GraphicalItems()
Definition footprint.h:407
LSEQ is a sequence (and therefore also a set) of PCB_LAYER_IDs.
Definition lseq.h:47
static LSET AllNonCuMask()
Return a mask holding all layer minus CU layers.
Definition lset.cpp:623
LSEQ Seq(const LSEQ &aSequence) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition lset.cpp:309
Definition pad.h:61
PAD_SHAPE GetShape(PCB_LAYER_ID aLayer) const
Definition pad.h:205
VECTOR2I GetSize(PCB_LAYER_ID aLayer) const
Definition pad.cpp:288
const std::shared_ptr< SHAPE_POLY_SET > & GetEffectivePolygon(PCB_LAYER_ID aLayer, ERROR_LOC aErrorLoc=ERROR_INSIDE) const
Definition pad.cpp:1228
VECTOR2I ShapePos(PCB_LAYER_ID aLayer) const
Definition pad.cpp:1855
Abstract dimension API.
const std::vector< std::shared_ptr< SHAPE > > & GetShapes() const
bool HasSolderMask() const
Definition pcb_shape.h:336
int GetSolderMaskExpansion() const
bool IsOnLayer(PCB_LAYER_ID aLayer) const override
Test to see if this object is on the given layer.
bool IsBorderEnabled() const
Disables the border, this is done by changing the stroke internally.
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 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...
Definition pcb_text.cpp:786
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.
bool IsOnLayer(PCB_LAYER_ID aLayer) const override
Test to see if this object is on the given layer.
bool FlashLayer(int aLayer) const
Check to see whether the via should have a pad on the specific layer.
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
Represent a set of closed polygons.
void Simplify()
Simplify the polyset (merges overlapping polys, eliminates degeneracy/self-intersections)
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
void TransformToPolygon(SHAPE_POLY_SET &aBuffer, int aError, ERROR_LOC aErrorLoc) const override
Fills a SHAPE_POLY_SET with a polygon representation of this shape.
Simple container to manage line stroke parameters.
int GetWidth() const
Handle a list of polygons defining a copper zone.
Definition zone.h:70
void TransformSolidAreasShapesToPolygon(PCB_LAYER_ID aLayer, SHAPE_POLY_SET &aBuffer) const
Convert solid areas full shapes to polygon set (the full shape is the polygon area with a thick outli...
Definition zone.cpp:1993
void TransformRingToPolygon(SHAPE_POLY_SET &aBuffer, const VECTOR2I &aCentre, int aRadius, int aWidth, int aError, ERROR_LOC aErrorLoc)
Convert arcs to multiple straight segments.
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.
void TransformOvalToPolygon(SHAPE_POLY_SET &aBuffer, const VECTOR2I &aStart, const VECTOR2I &aEnd, int aWidth, int aError, ERROR_LOC aErrorLoc, int aMinSegCount=0)
Convert a oblong shape to a polygon, using multiple segments.
#define DELETE_AND_FREE_MAP(map)
void transformFPShapesToPolySet(const FOOTPRINT *aFootprint, PCB_LAYER_ID aLayer, SHAPE_POLY_SET &aBuffer, int aMaxError, ERROR_LOC aErrorLoc)
void transformFPTextToPolySet(const FOOTPRINT *aFootprint, PCB_LAYER_ID aLayer, const std::bitset< LAYER_3D_END > &aFlags, SHAPE_POLY_SET &aBuffer, int aMaxError, ERROR_LOC aErrorLoc)
void buildPadOutlineAsPolygon(const PAD *aPad, PCB_LAYER_ID aLayer, SHAPE_POLY_SET &aBuffer, int aWidth, int aMaxError, ERROR_LOC aErrorLoc)
#define DELETE_AND_FREE(ptr)
#define _(s)
static const wxChar * m_logTrace
Trace mask used to enable or disable debug output for this class.
bool IsSolderMaskLayer(int aLayer)
Definition layer_ids.h:774
@ LAYER_3D_SOLDERMASK_TOP
Definition layer_ids.h:582
@ LAYER_3D_SOLDERMASK_BOTTOM
Definition layer_ids.h:581
bool IsFrontLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a front layer.
Definition layer_ids.h:806
bool IsBackLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a back layer.
Definition layer_ids.h:829
@ LAYER_FP_REFERENCES
Show footprints references (when texts are visible).
Definition layer_ids.h:262
@ LAYER_FP_TEXT
Definition layer_ids.h:236
@ LAYER_FP_VALUES
Show footprints values (when texts are visible).
Definition layer_ids.h:259
bool IsExternalCopperLayer(int aLayerId)
Test whether a layer is an external (F_Cu or B_Cu) copper layer.
Definition layer_ids.h:714
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ User_16
Definition layer_ids.h:135
@ User_29
Definition layer_ids.h:148
@ User_40
Definition layer_ids.h:159
@ User_15
Definition layer_ids.h:134
@ User_8
Definition layer_ids.h:127
@ F_CrtYd
Definition layer_ids.h:112
@ User_11
Definition layer_ids.h:130
@ User_25
Definition layer_ids.h:144
@ User_34
Definition layer_ids.h:153
@ User_45
Definition layer_ids.h:164
@ B_Adhes
Definition layer_ids.h:99
@ User_36
Definition layer_ids.h:155
@ Dwgs_User
Definition layer_ids.h:103
@ F_Paste
Definition layer_ids.h:100
@ Cmts_User
Definition layer_ids.h:104
@ User_6
Definition layer_ids.h:125
@ User_7
Definition layer_ids.h:126
@ User_19
Definition layer_ids.h:138
@ User_23
Definition layer_ids.h:142
@ F_Adhes
Definition layer_ids.h:98
@ User_41
Definition layer_ids.h:160
@ B_Mask
Definition layer_ids.h:94
@ B_Cu
Definition layer_ids.h:61
@ User_14
Definition layer_ids.h:133
@ User_39
Definition layer_ids.h:158
@ User_5
Definition layer_ids.h:124
@ User_20
Definition layer_ids.h:139
@ Eco1_User
Definition layer_ids.h:105
@ F_Mask
Definition layer_ids.h:93
@ User_42
Definition layer_ids.h:161
@ User_43
Definition layer_ids.h:162
@ B_Paste
Definition layer_ids.h:101
@ User_10
Definition layer_ids.h:129
@ User_9
Definition layer_ids.h:128
@ User_27
Definition layer_ids.h:146
@ User_28
Definition layer_ids.h:147
@ F_Fab
Definition layer_ids.h:115
@ F_SilkS
Definition layer_ids.h:96
@ B_CrtYd
Definition layer_ids.h:111
@ Eco2_User
Definition layer_ids.h:106
@ User_35
Definition layer_ids.h:154
@ User_31
Definition layer_ids.h:150
@ User_3
Definition layer_ids.h:122
@ User_1
Definition layer_ids.h:120
@ User_12
Definition layer_ids.h:131
@ B_SilkS
Definition layer_ids.h:97
@ User_30
Definition layer_ids.h:149
@ User_37
Definition layer_ids.h:156
@ User_22
Definition layer_ids.h:141
@ User_38
Definition layer_ids.h:157
@ User_4
Definition layer_ids.h:123
@ User_21
Definition layer_ids.h:140
@ User_24
Definition layer_ids.h:143
@ User_13
Definition layer_ids.h:132
@ User_2
Definition layer_ids.h:121
@ User_17
Definition layer_ids.h:136
@ User_33
Definition layer_ids.h:152
@ User_26
Definition layer_ids.h:145
@ User_32
Definition layer_ids.h:151
@ User_18
Definition layer_ids.h:137
@ User_44
Definition layer_ids.h:163
@ F_Cu
Definition layer_ids.h:60
@ B_Fab
Definition layer_ids.h:114
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:102
BARCODE class definition.
VIATYPE
int64_t GetRunningMicroSecs()
An alternate way to calculate an elapsed time (in microsecondes) to class PROF_COUNTER.
bool DifferentiatePlatedCopper()
return true if platted copper aeras and non platted copper areas must be drawn using a different colo...
std::string path
void ConvertPolygonToTriangles(const SHAPE_POLY_SET &aPolyList, CONTAINER_2D_BASE &aDstContainer, float aBiuTo3dUnitsScale, const BOARD_ITEM &aBoardItem)
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:80
@ PCB_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition typeinfo.h:98
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition typeinfo.h:95
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:89
@ PCB_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition typeinfo.h:96
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
Definition typeinfo.h:85
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition typeinfo.h:84
@ PCB_REFERENCE_IMAGE_T
class PCB_REFERENCE_IMAGE, bitmap on a layer
Definition typeinfo.h:81
@ PCB_BARCODE_T
class PCB_BARCODE, a barcode (graphic item)
Definition typeinfo.h:93
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition typeinfo.h:94
@ PCB_TABLE_T
class PCB_TABLE, table of PCB_TABLECELLs
Definition typeinfo.h:86
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
Definition typeinfo.h:97
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683
glm::vec2 SFVEC2F
Definition xv3d_types.h:38