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