KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pad.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) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr
5 * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <[email protected]>
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, you may find one here:
20 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21 * or you may search the http://www.gnu.org website for the version 2 license,
22 * or you may write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
26#include <base_units.h>
27#include <bitmaps.h>
28#include <math/util.h> // for KiROUND
29#include <eda_draw_frame.h>
33#include <geometry/shape_rect.h>
35#include <geometry/shape_null.h>
37#include <layer_range.h>
38#include <string_utils.h>
39#include <i18n_utility.h>
40#include <view/view.h>
41#include <board.h>
44#include <footprint.h>
45#include <lset.h>
46#include <pad.h>
47#include <pad_utils.h>
48#include <pcb_shape.h>
50#include <eda_units.h>
52#include <widgets/msgpanel.h>
53#include <pcb_painter.h>
55#include <wx/log.h>
56#include <api/api_enums.h>
57#include <api/api_utils.h>
58#include <api/api_pcb_utils.h>
59#include <api/board/board_types.pb.h>
60
61#include <memory>
62#include <macros.h>
63#include <magic_enum.hpp>
64#include <drc/drc_item.h>
65#include "kiface_base.h"
66#include "pcbnew_settings.h"
67
68#include <pcb_group.h>
70#include <pin_type.h>
71
74
75
76PAD::PAD( FOOTPRINT* parent ) :
78 m_padStack( this )
79{
80 VECTOR2I& drill = m_padStack.Drill().size;
84 drill.x = drill.y = EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 30 ); // Default drill size 30 mils.
87
88 if( m_parent && m_parent->Type() == PCB_FOOTPRINT_T )
90
91 SetShape( F_Cu, PAD_SHAPE::CIRCLE ); // Default pad shape is PAD_CIRCLE.
92 SetAnchorPadShape( F_Cu, PAD_SHAPE::CIRCLE ); // Default shape for custom shaped pads
93 // is PAD_CIRCLE.
94 SetDrillShape( PAD_DRILL_SHAPE::CIRCLE ); // Default pad drill shape is a circle.
95 m_attribute = PAD_ATTRIB::PTH; // Default pad type is plated through hole
96 SetProperty( PAD_PROP::NONE ); // no special fabrication property
97
98 // Parameters for round rect only:
99 m_padStack.SetRoundRectRadiusRatio( 0.25, F_Cu ); // from IPC-7351C standard
100
101 // Parameters for chamfered rect only:
102 m_padStack.SetChamferRatio( 0.2, F_Cu );
103 m_padStack.SetChamferPositions( RECT_NO_CHAMFER, F_Cu );
104
105 // Set layers mask to default for a standard thru hole pad.
106 m_padStack.SetLayerSet( PTHMask() );
107
108 SetSubRatsnest( 0 ); // used in ratsnest calculations
109
110 SetDirty();
112
115
116 m_lastGalZoomLevel = 0.0;
117}
118
119
120PAD::PAD( const PAD& aOther ) :
122 m_padStack( this )
123{
124 PAD::operator=( aOther );
125
126 const_cast<KIID&>( m_Uuid ) = aOther.m_Uuid;
127}
128
129
130PAD& PAD::operator=( const PAD &aOther )
131{
133
134 ImportSettingsFrom( aOther );
137 SetPosition( aOther.GetPosition() );
138 SetNumber( aOther.GetNumber() );
139 SetPinType( aOther.GetPinType() );
140 SetPinFunction( aOther.GetPinFunction() );
141 SetSubRatsnest( aOther.GetSubRatsnest() );
143
144 return *this;
145}
146
147
148void PAD::CopyFrom( const BOARD_ITEM* aOther )
149{
150 wxCHECK( aOther && aOther->Type() == PCB_PAD_T, /* void */ );
151 *this = *static_cast<const PAD*>( aOther );
152}
153
154
155// This should probably move elsewhere once it is needed elsewhere
156std::optional<std::pair<ELECTRICAL_PINTYPE, bool>> parsePinType( const wxString& aPinTypeString )
157{
158 // The netlister formats the pin type as "<canonical_name>[+no_connect]"
159 static std::map<wxString, ELECTRICAL_PINTYPE> map = {
160 { wxT( "input" ), ELECTRICAL_PINTYPE::PT_INPUT },
161 { wxT( "output" ), ELECTRICAL_PINTYPE::PT_OUTPUT },
162 { wxT( "bidirectional" ), ELECTRICAL_PINTYPE::PT_BIDI },
163 { wxT( "tri_state" ), ELECTRICAL_PINTYPE::PT_TRISTATE },
164 { wxT( "passive" ), ELECTRICAL_PINTYPE::PT_PASSIVE },
165 { wxT( "free" ), ELECTRICAL_PINTYPE::PT_NIC },
166 { wxT( "unspecified" ), ELECTRICAL_PINTYPE::PT_UNSPECIFIED },
167 { wxT( "power_in" ), ELECTRICAL_PINTYPE::PT_POWER_IN },
168 { wxT( "power_out" ), ELECTRICAL_PINTYPE::PT_POWER_OUT },
169 { wxT( "open_collector" ), ELECTRICAL_PINTYPE::PT_OPENCOLLECTOR },
170 { wxT( "open_emitter" ), ELECTRICAL_PINTYPE::PT_OPENEMITTER },
171 { wxT( "no_connect" ), ELECTRICAL_PINTYPE::PT_NC }
172 };
173
174 bool hasNoConnect = aPinTypeString.EndsWith( wxT( "+no_connect" ) );
175
176 if( auto it = map.find( aPinTypeString.BeforeFirst( '+' ) ); it != map.end() )
177 return std::make_pair( it->second, hasNoConnect );
178
179 return std::nullopt;
180}
181
182
183void PAD::Serialize( google::protobuf::Any &aContainer ) const
184{
185 using namespace kiapi::board::types;
186 using namespace kiapi::common::types;
187 Pad pad;
188
189 pad.mutable_id()->set_value( m_Uuid.AsStdString() );
190 kiapi::common::PackVector2( *pad.mutable_position(), GetPosition() );
191 pad.set_locked( IsLocked() ? LockedState::LS_LOCKED
192 : LockedState::LS_UNLOCKED );
193 PackNet( pad.mutable_net() );
194 pad.set_number( GetNumber().ToUTF8() );
196 pad.mutable_pad_to_die_length()->set_value_nm( GetPadToDieLength() );
197 pad.mutable_pad_to_die_delay()->set_value_as( GetPadToDieDelay() );
198
199 google::protobuf::Any padStackMsg;
200 m_padStack.Serialize( padStackMsg );
201 padStackMsg.UnpackTo( pad.mutable_pad_stack() );
202
203 if( GetLocalClearance().has_value() )
204 pad.mutable_copper_clearance_override()->set_value_nm( *GetLocalClearance() );
205
206 pad.mutable_symbol_pin()->set_name( m_pinFunction.ToUTF8() );
207
208 if( std::optional<std::pair<ELECTRICAL_PINTYPE, bool>> pt = parsePinType( m_pinType ) )
209 {
210 pad.mutable_symbol_pin()->set_type( ToProtoEnum<ELECTRICAL_PINTYPE, ElectricalPinType>( pt->first ) );
211 pad.mutable_symbol_pin()->set_no_connect( pt->second );
212 }
213
214 aContainer.PackFrom( pad );
215}
216
217
218bool PAD::Deserialize( const google::protobuf::Any &aContainer )
219{
220 kiapi::board::types::Pad pad;
221
222 if( !aContainer.UnpackTo( &pad ) )
223 return false;
224
225 const_cast<KIID&>( m_Uuid ) = KIID( pad.id().value() );
227 UnpackNet( pad.net() );
228 SetLocked( pad.locked() == kiapi::common::types::LockedState::LS_LOCKED );
230 SetNumber( wxString::FromUTF8( pad.number() ) );
231 SetPadToDieLength( pad.pad_to_die_length().value_nm() );
232 SetPadToDieDelay( pad.pad_to_die_delay().value_as() );
233
234 google::protobuf::Any padStackWrapper;
235 padStackWrapper.PackFrom( pad.pad_stack() );
236 m_padStack.Deserialize( padStackWrapper );
237
238 SetLayer( m_padStack.StartLayer() );
239
240 if( pad.has_copper_clearance_override() )
241 SetLocalClearance( pad.copper_clearance_override().value_nm() );
242 else
243 SetLocalClearance( std::nullopt );
244
245 m_pinFunction = wxString::FromUTF8( pad.symbol_pin().name() );
246
247 if( pad.symbol_pin().type() != kiapi::common::types::EPT_UNKNOWN )
248 {
249 ELECTRICAL_PINTYPE type = FromProtoEnum<ELECTRICAL_PINTYPE>( pad.symbol_pin().type() );
251
252 if( pad.symbol_pin().no_connect() )
253 m_pinType += wxT( "+no_connect" );
254 }
255
256 return true;
257}
258
259
261{
262 std::unique_lock<std::mutex> cacheLock( m_zoneLayerOverridesMutex );
263
266}
267
268
270{
271 std::unique_lock<std::mutex> cacheLock( m_zoneLayerOverridesMutex );
272
273 static const ZONE_LAYER_OVERRIDE defaultOverride = ZLO_NONE;
274 auto it = m_zoneLayerOverrides.find( aLayer );
275 return it != m_zoneLayerOverrides.end() ? it->second : defaultOverride;
276}
277
278
280{
281 std::unique_lock<std::mutex> cacheLock( m_zoneLayerOverridesMutex );
282 m_zoneLayerOverrides[aLayer] = aOverride;
283}
284
285
287{
288 // Aperture pads don't get a number
289 if( IsAperturePad() )
290 return false;
291
292 // NPTH pads don't get numbers
294 return false;
295
296 return true;
297}
298
299
300bool PAD::IsLocked() const
301{
302 if( GetParent() && GetParent()->IsLocked() )
303 return true;
304
305 return BOARD_ITEM::IsLocked();
306};
307
308
309bool PAD::SharesNetTieGroup( const PAD* aOther ) const
310{
311 FOOTPRINT* parentFp = GetParentFootprint();
312
313 if( parentFp && parentFp->IsNetTie() && aOther->GetParentFootprint() == parentFp )
314 {
315 std::map<wxString, int> padToNetTieGroupMap = parentFp->MapPadNumbersToNetTieGroups();
316 int thisNetTieGroup = padToNetTieGroupMap[ GetNumber() ];
317 int otherNetTieGroup = padToNetTieGroupMap[ aOther->GetNumber() ];
318
319 return thisNetTieGroup >= 0 && thisNetTieGroup == otherNetTieGroup;
320 }
321
322 return false;
323}
324
325
327{
328 return m_pinType.Contains( wxT( "no_connect" ) );
329}
330
331
332bool PAD::IsFreePad() const
333{
334 return GetShortNetname().StartsWith( wxT( "unconnected-(" ) )
335 && m_pinType == wxT( "free" );
336}
337
338
340{
341 static LSET saved = LSET::AllCuMask() | LSET( { F_Mask, B_Mask } );
342 return saved;
343}
344
345
347{
348 static LSET saved( { F_Cu, F_Paste, F_Mask } );
349 return saved;
350}
351
352
354{
355 static LSET saved( { F_Cu, F_Mask } );
356 return saved;
357}
358
359
361{
362 static LSET saved = LSET( { F_Cu, B_Cu, F_Mask, B_Mask } );
363 return saved;
364}
365
366
368{
369 static LSET saved( { F_Paste } );
370 return saved;
371}
372
373
374bool PAD::IsFlipped() const
375{
376 FOOTPRINT* parent = GetParentFootprint();
377
378 return ( parent && parent->GetLayer() == B_Cu );
379}
380
381
383{
384 return BOARD_ITEM::GetLayer();
385}
386
387
389{
391 return m_layer;
392 else
393 return GetLayerSet().Seq().front();
394
395}
396
397
398bool PAD::FlashLayer( const LSET& aLayers ) const
399{
400 for( PCB_LAYER_ID layer : aLayers )
401 {
402 if( FlashLayer( layer ) )
403 return true;
404 }
405
406 return false;
407}
408
409
410bool PAD::FlashLayer( int aLayer, bool aOnlyCheckIfPermitted ) const
411{
412 if( aLayer == UNDEFINED_LAYER )
413 return true;
414
415 // Sometimes this is called with GAL layers and should just return true
416 if( aLayer > PCB_LAYER_ID_COUNT )
417 return true;
418
419 PCB_LAYER_ID layer = static_cast<PCB_LAYER_ID>( aLayer );
420
421 if( !IsOnLayer( layer ) )
422 return false;
423
424 if( GetAttribute() == PAD_ATTRIB::NPTH && IsCopperLayer( aLayer ) )
425 {
427 {
428 if( GetOffset( layer ) == VECTOR2I( 0, 0 ) && GetDrillSize().x >= GetSize( layer ).x )
429 return false;
430 }
431 else if( GetShape( layer ) == PAD_SHAPE::OVAL
433 {
434 if( GetOffset( layer ) == VECTOR2I( 0, 0 )
435 && GetDrillSize().x >= GetSize( layer ).x
436 && GetDrillSize().y >= GetSize( layer ).y )
437 {
438 return false;
439 }
440 }
441 }
442
443 if( LSET::FrontBoardTechMask().test( aLayer ) )
444 aLayer = F_Cu;
445 else if( LSET::BackBoardTechMask().test( aLayer ) )
446 aLayer = B_Cu;
447
448 if( GetAttribute() == PAD_ATTRIB::PTH && IsCopperLayer( aLayer ) )
449 {
450 UNCONNECTED_LAYER_MODE mode = m_padStack.UnconnectedLayerMode();
451
453 return true;
454
455 // Plated through hole pads need copper on the top/bottom layers for proper soldering
456 // Unless the user has removed them in the pad dialog
458 {
459 return aLayer == m_padStack.Drill().start || aLayer == m_padStack.Drill().end;
460 }
461
463 && IsExternalCopperLayer( aLayer ) )
464 {
465 return true;
466 }
467
468 if( const BOARD* board = GetBoard() )
469 {
471 {
472 return true;
473 }
474 else if( aOnlyCheckIfPermitted )
475 {
476 return true;
477 }
478 else
479 {
480 // Must be static to keep from raising its ugly head in performance profiles
481 static std::initializer_list<KICAD_T> nonZoneTypes = { PCB_TRACE_T, PCB_ARC_T,
483
484 return board->GetConnectivity()->IsConnectedOnLayer( this, aLayer, nonZoneTypes );
485 }
486 }
487 }
488
489 return true;
490}
491
492
494{
495 m_padStack.Drill().size = aSize;
496 SetDirty();
497}
498
499
500void PAD::SetPrimaryDrillSizeX( const int aX )
501{
502 m_padStack.Drill().size.x = aX;
503
505 m_padStack.Drill().size.y = aX;
506
507 SetDirty();
508}
509
510
511void PAD::SetDrillSizeX( const int aX )
512{
514}
515
516
517void PAD::SetPrimaryDrillSizeY( const int aY )
518{
519 m_padStack.Drill().size.y = aY;
520 SetDirty();
521}
522
523
524void PAD::SetDrillSizeY( const int aY )
525{
527}
528
529
531{
532 m_padStack.Drill().shape = aShape;
533
534 if( aShape == PAD_DRILL_SHAPE::CIRCLE )
535 m_padStack.Drill().size.y = m_padStack.Drill().size.x;
536
537 m_shapesDirty = true;
538 SetDirty();
539}
540
541
543{
544 m_padStack.Drill().start = aLayer;
545 SetDirty();
546}
547
548
550{
551 m_padStack.Drill().end = aLayer;
552 SetDirty();
553}
554
555
557{
558 if( !IsCopperLayer( aLayer ) )
559 return false;
560
561 const BOARD* board = GetBoard();
562
563 if( !board )
564 return false;
565
566 // Check secondary drill (backdrill from top)
567 const PADSTACK::DRILL_PROPS& secondaryDrill = m_padStack.SecondaryDrill();
568
569 if( secondaryDrill.size.x > 0 && secondaryDrill.start != UNDEFINED_LAYER
570 && secondaryDrill.end != UNDEFINED_LAYER )
571 {
572 // Secondary drill goes from start to end layer, removing copper on those layers
573 int startOrdinal = board->IsLayerEnabled( secondaryDrill.start )
574 ? board->IsLayerEnabled( F_Cu ) ? ( secondaryDrill.start == F_Cu ? 0 : secondaryDrill.start / 2 + 1 )
575 : secondaryDrill.start / 2
576 : -1;
577 int endOrdinal = board->IsLayerEnabled( secondaryDrill.end )
578 ? board->IsLayerEnabled( F_Cu ) ? ( secondaryDrill.end == B_Cu ? board->GetCopperLayerCount() - 1 : secondaryDrill.end / 2 + 1 )
579 : secondaryDrill.end / 2
580 : -1;
581 int layerOrdinal = board->IsLayerEnabled( aLayer )
582 ? board->IsLayerEnabled( F_Cu ) ? ( aLayer == F_Cu ? 0 : aLayer == B_Cu ? board->GetCopperLayerCount() - 1 : aLayer / 2 + 1 )
583 : aLayer / 2
584 : -1;
585
586 if( layerOrdinal >= 0 && startOrdinal >= 0 && endOrdinal >= 0 )
587 {
588 if( startOrdinal > endOrdinal )
589 std::swap( startOrdinal, endOrdinal );
590
591 if( layerOrdinal >= startOrdinal && layerOrdinal <= endOrdinal )
592 return true;
593 }
594 }
595
596 // Check tertiary drill (backdrill from bottom)
597 const PADSTACK::DRILL_PROPS& tertiaryDrill = m_padStack.TertiaryDrill();
598
599 if( tertiaryDrill.size.x > 0 && tertiaryDrill.start != UNDEFINED_LAYER
600 && tertiaryDrill.end != UNDEFINED_LAYER )
601 {
602 int startOrdinal = board->IsLayerEnabled( tertiaryDrill.start )
603 ? board->IsLayerEnabled( F_Cu ) ? ( tertiaryDrill.start == F_Cu ? 0 : tertiaryDrill.start / 2 + 1 )
604 : tertiaryDrill.start / 2
605 : -1;
606 int endOrdinal = board->IsLayerEnabled( tertiaryDrill.end )
607 ? board->IsLayerEnabled( F_Cu ) ? ( tertiaryDrill.end == B_Cu ? board->GetCopperLayerCount() - 1 : tertiaryDrill.end / 2 + 1 )
608 : tertiaryDrill.end / 2
609 : -1;
610 int layerOrdinal = board->IsLayerEnabled( aLayer )
611 ? board->IsLayerEnabled( F_Cu ) ? ( aLayer == F_Cu ? 0 : aLayer == B_Cu ? board->GetCopperLayerCount() - 1 : aLayer / 2 + 1 )
612 : aLayer / 2
613 : -1;
614
615 if( layerOrdinal >= 0 && startOrdinal >= 0 && endOrdinal >= 0 )
616 {
617 if( startOrdinal > endOrdinal )
618 std::swap( startOrdinal, endOrdinal );
619
620 if( layerOrdinal >= startOrdinal && layerOrdinal <= endOrdinal )
621 return true;
622 }
623 }
624
625 // Check if the layer is affected by post-machining
626 if( GetPostMachiningKnockout( aLayer ) > 0 )
627 return true;
628
629 return false;
630}
631
632
634{
635 if( !IsCopperLayer( aLayer ) )
636 return 0;
637
638 const BOARD* board = GetBoard();
639
640 if( !board )
641 return 0;
642
643 const BOARD_STACKUP& stackup = board->GetDesignSettings().GetStackupDescriptor();
644
645 // Check front post-machining (counterbore/countersink from top)
646 const PADSTACK::POST_MACHINING_PROPS& frontPM = m_padStack.FrontPostMachining();
647
648 if( frontPM.mode.has_value() && *frontPM.mode != PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED
649 && *frontPM.mode != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN && frontPM.size > 0 )
650 {
651 int pmDepth = frontPM.depth;
652
653 // For countersink without explicit depth, calculate from diameter and angle
654 if( pmDepth <= 0 && *frontPM.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK
655 && frontPM.angle > 0 )
656 {
657 double halfAngleRad = ( frontPM.angle / 10.0 ) * M_PI / 180.0 / 2.0;
658 pmDepth = static_cast<int>( ( frontPM.size / 2.0 ) / tan( halfAngleRad ) );
659 }
660
661 if( pmDepth > 0 )
662 {
663 // Calculate distance from F_Cu to aLayer
664 int layerDist = stackup.GetLayerDistance( F_Cu, aLayer );
665
666 if( layerDist < pmDepth )
667 {
668 // For countersink, diameter decreases with depth
669 if( *frontPM.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK && frontPM.angle > 0 )
670 {
671 double halfAngleRad = ( frontPM.angle / 10.0 ) * M_PI / 180.0 / 2.0;
672 int diameterAtLayer = frontPM.size - static_cast<int>( 2.0 * layerDist * tan( halfAngleRad ) );
673 return std::max( 0, diameterAtLayer );
674 }
675 else
676 {
677 // Counterbore - constant diameter
678 return frontPM.size;
679 }
680 }
681 }
682 }
683
684 // Check back post-machining (counterbore/countersink from bottom)
685 const PADSTACK::POST_MACHINING_PROPS& backPM = m_padStack.BackPostMachining();
686
687 if( backPM.mode.has_value() && *backPM.mode != PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED
688 && *backPM.mode != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN && backPM.size > 0 )
689 {
690 int pmDepth = backPM.depth;
691
692 // For countersink without explicit depth, calculate from diameter and angle
693 if( pmDepth <= 0 && *backPM.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK
694 && backPM.angle > 0 )
695 {
696 double halfAngleRad = ( backPM.angle / 10.0 ) * M_PI / 180.0 / 2.0;
697 pmDepth = static_cast<int>( ( backPM.size / 2.0 ) / tan( halfAngleRad ) );
698 }
699
700 if( pmDepth > 0 )
701 {
702 // Calculate distance from B_Cu to aLayer
703 int layerDist = stackup.GetLayerDistance( B_Cu, aLayer );
704
705 if( layerDist < pmDepth )
706 {
707 // For countersink, diameter decreases with depth
708 if( *backPM.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK && backPM.angle > 0 )
709 {
710 double halfAngleRad = ( backPM.angle / 10.0 ) * M_PI / 180.0 / 2.0;
711 int diameterAtLayer = backPM.size - static_cast<int>( 2.0 * layerDist * tan( halfAngleRad ) );
712 return std::max( 0, diameterAtLayer );
713 }
714 else
715 {
716 // Counterbore - constant diameter
717 return backPM.size;
718 }
719 }
720 }
721 }
722
723 return 0;
724}
725
726
727void PAD::SetPrimaryDrillFilled( const std::optional<bool>& aFilled )
728{
729 m_padStack.Drill().is_filled = aFilled;
730 SetDirty();
731}
732
733
735{
736 m_padStack.Drill().is_filled = aFilled;
737 SetDirty();
738}
739
740
741void PAD::SetPrimaryDrillCapped( const std::optional<bool>& aCapped )
742{
743 m_padStack.Drill().is_capped = aCapped;
744 SetDirty();
745}
746
747
749{
750 m_padStack.Drill().is_capped = aCapped;
751 SetDirty();
752}
753
754
756{
757 m_padStack.SecondaryDrill().size = aSize;
758 SetDirty();
759}
760
761
763{
764 m_padStack.SecondaryDrill().size.x = aX;
765
767 m_padStack.SecondaryDrill().size.y = aX;
768
769 SetDirty();
770}
771
772
774{
775 m_padStack.SecondaryDrill().size.y = aY;
776 SetDirty();
777}
778
779
781{
782 m_padStack.SecondaryDrill().size = VECTOR2I( 0, 0 );
783 SetDirty();
784}
785
786
788{
789 m_padStack.SecondaryDrill().shape = aShape;
790 SetDirty();
791}
792
793
795{
796 m_padStack.SecondaryDrill().start = aLayer;
797 SetDirty();
798}
799
800
802{
803 m_padStack.SecondaryDrill().end = aLayer;
804 SetDirty();
805}
806
807
809{
810 m_padStack.TertiaryDrill().size = aSize;
811 SetDirty();
812}
813
814
816{
817 m_padStack.TertiaryDrill().size.x = aX;
818
820 m_padStack.TertiaryDrill().size.y = aX;
821
822 SetDirty();
823}
824
825
827{
828 m_padStack.TertiaryDrill().size.y = aY;
829 SetDirty();
830}
831
832
834{
835 m_padStack.TertiaryDrill().size = VECTOR2I( 0, 0 );
836 SetDirty();
837}
838
839
841{
842 m_padStack.TertiaryDrill().shape = aShape;
843 SetDirty();
844}
845
846
848{
849 m_padStack.TertiaryDrill().start = aLayer;
850 SetDirty();
851}
852
853
855{
856 m_padStack.TertiaryDrill().end = aLayer;
857 SetDirty();
858}
859
860
862{
863 return m_padStack.RoundRectRadius( aLayer );
864}
865
866
867void PAD::SetRoundRectCornerRadius( PCB_LAYER_ID aLayer, double aRadius )
868{
869 m_padStack.SetRoundRectRadius( aRadius, aLayer );
870}
871
872
873void PAD::SetRoundRectRadiusRatio( PCB_LAYER_ID aLayer, double aRadiusScale )
874{
875 m_padStack.SetRoundRectRadiusRatio( std::clamp( aRadiusScale, 0.0, 0.5 ), aLayer );
876
877 SetDirty();
878}
879
880
881void PAD::SetFrontRoundRectRadiusRatio( double aRadiusScale )
882{
883 wxASSERT_MSG( m_padStack.Mode() == PADSTACK::MODE::NORMAL,
884 "Set front radius only meaningful for normal padstacks" );
885
886 m_padStack.SetRoundRectRadiusRatio( std::clamp( aRadiusScale, 0.0, 0.5 ), F_Cu );
887 SetDirty();
888}
889
890
892{
893 const VECTOR2I size = m_padStack.Size( F_Cu );
894 const int minSize = std::min( size.x, size.y );
895 const double newRatio = aRadius / double( minSize );
896
898}
899
900
902{
903 const VECTOR2I size = m_padStack.Size( F_Cu );
904 const int minSize = std::min( size.x, size.y );
905 const double ratio = GetFrontRoundRectRadiusRatio();
906
907 return KiROUND( ratio * minSize );
908}
909
910
911void PAD::SetChamferRectRatio( PCB_LAYER_ID aLayer, double aChamferScale )
912{
913 m_padStack.SetChamferRatio( aChamferScale, aLayer );
914
915 SetDirty();
916}
917
918
919const std::shared_ptr<SHAPE_POLY_SET>& PAD::GetEffectivePolygon( PCB_LAYER_ID aLayer,
920 ERROR_LOC aErrorLoc ) const
921{
922 if( m_polyDirty[ aErrorLoc ] )
923 BuildEffectivePolygon( aErrorLoc );
924
925 aLayer = Padstack().EffectiveLayerFor( aLayer );
926
927 return m_effectivePolygons[ aLayer ][ aErrorLoc ];
928}
929
930
931std::shared_ptr<SHAPE> PAD::GetEffectiveShape( PCB_LAYER_ID aLayer, FLASHING flashPTHPads ) const
932{
933 if( aLayer == Edge_Cuts )
934 {
935 std::shared_ptr<SHAPE_COMPOUND> effective_compund = std::make_shared<SHAPE_COMPOUND>();
936
938 {
939 effective_compund->AddShape( GetEffectiveHoleShape() );
940 return effective_compund;
941 }
942 else
943 {
944 effective_compund->AddShape( std::make_shared<SHAPE_NULL>() );
945 return effective_compund;
946 }
947 }
948
949 // Check if this layer has copper removed by backdrill or post-machining
950 if( IsBackdrilledOrPostMachined( aLayer ) )
951 {
952 std::shared_ptr<SHAPE_COMPOUND> effective_compound = std::make_shared<SHAPE_COMPOUND>();
953
954 // Return the larger of the backdrill or post-machining hole
955 int holeSize = 0;
956
959
962 {
963 holeSize = std::max( holeSize, frontPM.size );
964 }
965
968 {
969 holeSize = std::max( holeSize, backPM.size );
970 }
971
972 const PADSTACK::DRILL_PROPS& secDrill = Padstack().SecondaryDrill();
973
974 if( secDrill.start != UNDEFINED_LAYER && secDrill.end != UNDEFINED_LAYER )
975 holeSize = std::max( holeSize, secDrill.size.x );
976
977 if( holeSize > 0 )
978 {
979 effective_compound->AddShape(
980 std::make_shared<SHAPE_CIRCLE>( GetPosition(), holeSize / 2 ) );
981 }
982 else
983 {
984 effective_compound->AddShape( GetEffectiveHoleShape() );
985 }
986
987 return effective_compound;
988 }
989
991 {
992 bool flash;
993 std::shared_ptr<SHAPE_COMPOUND> effective_compund = std::make_shared<SHAPE_COMPOUND>();
994
995 if( flashPTHPads == FLASHING::NEVER_FLASHED )
996 flash = false;
997 else if( flashPTHPads == FLASHING::ALWAYS_FLASHED )
998 flash = true;
999 else
1000 flash = FlashLayer( aLayer );
1001
1002 if( !flash )
1003 {
1004 if( GetAttribute() == PAD_ATTRIB::PTH )
1005 {
1006 effective_compund->AddShape( GetEffectiveHoleShape() );
1007 return effective_compund;
1008 }
1009 else
1010 {
1011 effective_compund->AddShape( std::make_shared<SHAPE_NULL>() );
1012 return effective_compund;
1013 }
1014 }
1015 }
1016
1017 if( m_shapesDirty )
1019
1020 aLayer = Padstack().EffectiveLayerFor( aLayer );
1021
1022 wxCHECK_MSG( m_effectiveShapes.contains( aLayer ), nullptr,
1023 wxString::Format( wxT( "Missing shape in PAD::GetEffectiveShape for layer %s." ),
1024 magic_enum::enum_name( aLayer ) ) );
1025 wxCHECK_MSG( m_effectiveShapes.at( aLayer ), nullptr,
1026 wxString::Format( wxT( "Null shape in PAD::GetEffectiveShape for layer %s." ),
1027 magic_enum::enum_name( aLayer ) ) );
1028
1029 return m_effectiveShapes[aLayer];
1030}
1031
1032
1033std::shared_ptr<SHAPE_SEGMENT> PAD::GetEffectiveHoleShape() const
1034{
1035 if( m_shapesDirty )
1037
1038 return m_effectiveHoleShape;
1039}
1040
1041
1049
1050
1052{
1053 std::lock_guard<std::mutex> RAII_lock( m_shapesBuildingLock );
1054
1055 // If we had to wait for the lock then we were probably waiting for someone else to
1056 // finish rebuilding the shapes. So check to see if they're clean now.
1057 if( !m_shapesDirty )
1058 return;
1059
1061
1063 [&]( PCB_LAYER_ID aLayer )
1064 {
1065 const SHAPE_COMPOUND& layerShape = buildEffectiveShape( aLayer );
1066 m_effectiveBoundingBox.Merge( layerShape.BBox() );
1067 } );
1068
1069 // Hole shape
1070 m_effectiveHoleShape = nullptr;
1071
1072 VECTOR2I half_size = m_padStack.Drill().size / 2;
1073 int half_width;
1074 VECTOR2I half_len;
1075
1076 if( m_padStack.Drill().shape == PAD_DRILL_SHAPE::CIRCLE )
1077 {
1078 half_width = half_size.x;
1079 }
1080 else
1081 {
1082 half_width = std::min( half_size.x, half_size.y );
1083 half_len = VECTOR2I( half_size.x - half_width, half_size.y - half_width );
1084 }
1085
1086 RotatePoint( half_len, GetOrientation() );
1087
1088 m_effectiveHoleShape = std::make_shared<SHAPE_SEGMENT>( m_pos - half_len, m_pos + half_len,
1089 half_width * 2 );
1091
1092 // All done
1093 m_shapesDirty = false;
1094}
1095
1096
1098{
1099 m_effectiveShapes[aLayer] = std::make_shared<SHAPE_COMPOUND>();
1100
1101 auto add = [this, aLayer]( SHAPE* aShape )
1102 {
1103 m_effectiveShapes[aLayer]->AddShape( aShape );
1104 };
1105
1106 VECTOR2I shapePos = ShapePos( aLayer ); // Fetch only once; rotation involves trig
1107 PAD_SHAPE effectiveShape = GetShape( aLayer );
1108 const VECTOR2I& size = m_padStack.Size( aLayer );
1109
1110 if( effectiveShape == PAD_SHAPE::CUSTOM )
1111 effectiveShape = GetAnchorPadShape( aLayer );
1112
1113 switch( effectiveShape )
1114 {
1115 case PAD_SHAPE::CIRCLE:
1116 add( new SHAPE_CIRCLE( shapePos, size.x / 2 ) );
1117 break;
1118
1119 case PAD_SHAPE::OVAL:
1120 if( size.x == size.y ) // the oval pad is in fact a circle
1121 {
1122 add( new SHAPE_CIRCLE( shapePos, size.x / 2 ) );
1123 }
1124 else
1125 {
1126 VECTOR2I half_size = size / 2;
1127 int half_width = std::min( half_size.x, half_size.y );
1128 VECTOR2I half_len( half_size.x - half_width, half_size.y - half_width );
1129 RotatePoint( half_len, GetOrientation() );
1130 add( new SHAPE_SEGMENT( shapePos - half_len, shapePos + half_len, half_width * 2 ) );
1131 }
1132
1133 break;
1134
1138 {
1139 int r = ( effectiveShape == PAD_SHAPE::ROUNDRECT ) ? GetRoundRectCornerRadius( aLayer ) : 0;
1140 VECTOR2I half_size( size.x / 2, size.y / 2 );
1141 VECTOR2I trap_delta( 0, 0 );
1142
1143 if( r )
1144 {
1145 half_size -= VECTOR2I( r, r );
1146
1147 // Avoid degenerated shapes (0 length segments) that always create issues
1148 // For roundrect pad very near a circle, use only a circle
1149 const int min_len = pcbIUScale.mmToIU( 0.0001 );
1150
1151 if( half_size.x < min_len && half_size.y < min_len )
1152 {
1153 add( new SHAPE_CIRCLE( shapePos, r ) );
1154 break;
1155 }
1156 }
1157 else if( effectiveShape == PAD_SHAPE::TRAPEZOID )
1158 {
1159 trap_delta = m_padStack.TrapezoidDeltaSize( aLayer ) / 2;
1160 }
1161
1162 SHAPE_LINE_CHAIN corners;
1163
1164 corners.Append( -half_size.x - trap_delta.y, half_size.y + trap_delta.x );
1165 corners.Append( half_size.x + trap_delta.y, half_size.y - trap_delta.x );
1166 corners.Append( half_size.x - trap_delta.y, -half_size.y + trap_delta.x );
1167 corners.Append( -half_size.x + trap_delta.y, -half_size.y - trap_delta.x );
1168
1169 corners.Rotate( GetOrientation() );
1170 corners.Move( shapePos );
1171
1172 // GAL renders rectangles faster than 4-point polygons so it's worth checking if our
1173 // body shape is a rectangle.
1174 if( corners.PointCount() == 4
1175 &&
1176 ( ( corners.CPoint( 0 ).y == corners.CPoint( 1 ).y
1177 && corners.CPoint( 1 ).x == corners.CPoint( 2 ).x
1178 && corners.CPoint( 2 ).y == corners.CPoint( 3 ).y
1179 && corners.CPoint( 3 ).x == corners.CPoint( 0 ).x )
1180 ||
1181 ( corners.CPoint( 0 ).x == corners.CPoint( 1 ).x
1182 && corners.CPoint( 1 ).y == corners.CPoint( 2 ).y
1183 && corners.CPoint( 2 ).x == corners.CPoint( 3 ).x
1184 && corners.CPoint( 3 ).y == corners.CPoint( 0 ).y )
1185 )
1186 )
1187 {
1188 int width = std::abs( corners.CPoint( 2 ).x - corners.CPoint( 0 ).x );
1189 int height = std::abs( corners.CPoint( 2 ).y - corners.CPoint( 0 ).y );
1190 VECTOR2I pos( std::min( corners.CPoint( 2 ).x, corners.CPoint( 0 ).x ),
1191 std::min( corners.CPoint( 2 ).y, corners.CPoint( 0 ).y ) );
1192
1193 add( new SHAPE_RECT( pos, width, height ) );
1194 }
1195 else
1196 {
1197 add( new SHAPE_SIMPLE( corners ) );
1198 }
1199
1200 if( r )
1201 {
1202 add( new SHAPE_SEGMENT( corners.CPoint( 0 ), corners.CPoint( 1 ), r * 2 ) );
1203 add( new SHAPE_SEGMENT( corners.CPoint( 1 ), corners.CPoint( 2 ), r * 2 ) );
1204 add( new SHAPE_SEGMENT( corners.CPoint( 2 ), corners.CPoint( 3 ), r * 2 ) );
1205 add( new SHAPE_SEGMENT( corners.CPoint( 3 ), corners.CPoint( 0 ), r * 2 ) );
1206 }
1207 }
1208 break;
1209
1211 {
1212 SHAPE_POLY_SET outline;
1213
1214 TransformRoundChamferedRectToPolygon( outline, shapePos, GetSize( aLayer ),
1216 GetChamferRectRatio( aLayer ),
1217 GetChamferPositions( aLayer ), 0, GetMaxError(),
1218 ERROR_INSIDE );
1219
1220 add( new SHAPE_SIMPLE( outline.COutline( 0 ) ) );
1221 }
1222 break;
1223
1224 default:
1225 wxFAIL_MSG( wxT( "PAD::buildEffectiveShapes: Unsupported pad shape: PAD_SHAPE::" )
1226 + wxString( std::string( magic_enum::enum_name( effectiveShape ) ) ) );
1227 break;
1228 }
1229
1230 if( GetShape( aLayer ) == PAD_SHAPE::CUSTOM )
1231 {
1232 for( const std::shared_ptr<PCB_SHAPE>& primitive : m_padStack.Primitives( aLayer ) )
1233 {
1234 if( !primitive->IsProxyItem() )
1235 {
1236 for( SHAPE* shape : primitive->MakeEffectiveShapes() )
1237 {
1238 shape->Rotate( GetOrientation() );
1239 shape->Move( shapePos );
1240 add( shape );
1241 }
1242 }
1243 }
1244 }
1245
1246 return *m_effectiveShapes[aLayer];
1247}
1248
1249
1251{
1252 std::lock_guard<std::mutex> RAII_lock( m_polyBuildingLock );
1253
1254 // Only calculate this once, not for both ERROR_INSIDE and ERROR_OUTSIDE
1255 bool doBoundingRadius = aErrorLoc == ERROR_OUTSIDE;
1256
1257 // If we had to wait for the lock then we were probably waiting for someone else to
1258 // finish rebuilding the shapes. So check to see if they're clean now.
1259 if( !m_polyDirty[ aErrorLoc ] )
1260 return;
1261
1263 [&]( PCB_LAYER_ID aLayer )
1264 {
1265 // Polygon
1266 std::shared_ptr<SHAPE_POLY_SET>& effectivePolygon = m_effectivePolygons[ aLayer ][ aErrorLoc ];
1267
1268 effectivePolygon = std::make_shared<SHAPE_POLY_SET>();
1269 TransformShapeToPolygon( *effectivePolygon, aLayer, 0, GetMaxError(), aErrorLoc );
1270 } );
1271
1272 if( doBoundingRadius )
1273 {
1275
1277 [&]( PCB_LAYER_ID aLayer )
1278 {
1279 std::shared_ptr<SHAPE_POLY_SET>& effectivePolygon = m_effectivePolygons[ aLayer ][ aErrorLoc ];
1280
1281 for( int cnt = 0; cnt < effectivePolygon->OutlineCount(); ++cnt )
1282 {
1283 const SHAPE_LINE_CHAIN& poly = effectivePolygon->COutline( cnt );
1284
1285 for( int ii = 0; ii < poly.PointCount(); ++ii )
1286 {
1287 int dist = KiROUND( ( poly.CPoint( ii ) - m_pos ).EuclideanNorm() );
1289 }
1290 }
1291 } );
1292
1295 }
1296
1297 // All done
1298 m_polyDirty[ aErrorLoc ] = false;
1299}
1300
1301
1303{
1304 if( m_shapesDirty )
1306
1308}
1309
1310
1311// Thermal spokes are built on the bounding box, so we must have a layer-specific version
1313{
1314 return buildEffectiveShape( aLayer ).BBox();
1315}
1316
1317
1319{
1320 if( m_attribute != aAttribute )
1321 {
1322 m_attribute = aAttribute;
1323
1324 LSET& layerMask = m_padStack.LayerSet();
1325
1326 switch( aAttribute )
1327 {
1328 case PAD_ATTRIB::PTH:
1329 // Plump up to all copper layers
1330 layerMask |= LSET::AllCuMask();
1331 break;
1332
1333 case PAD_ATTRIB::SMD:
1334 case PAD_ATTRIB::CONN:
1335 {
1336 // Trim down to no more than one copper layer
1337 LSET copperLayers = layerMask & LSET::AllCuMask();
1338
1339 if( copperLayers.count() > 1 )
1340 {
1341 layerMask &= ~LSET::AllCuMask();
1342
1343 if( copperLayers.test( B_Cu ) )
1344 layerMask.set( B_Cu );
1345 else
1346 layerMask.set( copperLayers.Seq().front() );
1347 }
1348
1349 // No hole
1350 m_padStack.Drill().size = VECTOR2I( 0, 0 );
1351 break;
1352 }
1353
1354 case PAD_ATTRIB::NPTH:
1355 // No number; no net
1356 m_number = wxEmptyString;
1358 break;
1359 }
1360 }
1361
1362 SetDirty();
1363}
1364
1365
1367{
1368 const bool wasRoundable = PAD_UTILS::PadHasMeaningfulRoundingRadius( *this, F_Cu );
1369
1370 m_padStack.SetShape( aShape, F_Cu );
1371
1372 const bool isRoundable = PAD_UTILS::PadHasMeaningfulRoundingRadius( *this, F_Cu );
1373
1374 // If we have become roundable, set a sensible rounding default using the IPC rules.
1375 if( !wasRoundable && isRoundable )
1376 {
1377 const double ipcRadiusRatio = PAD_UTILS::GetDefaultIpcRoundingRatio( *this, F_Cu );
1378 m_padStack.SetRoundRectRadiusRatio( ipcRadiusRatio, F_Cu );
1379 }
1380
1381 SetDirty();
1382}
1383
1384
1386{
1387 m_property = aProperty;
1388
1389 SetDirty();
1390}
1391
1392
1393void PAD::SetOrientation( const EDA_ANGLE& aAngle )
1394{
1395 m_padStack.SetOrientation( aAngle );
1396 SetDirty();
1397}
1398
1399
1401{
1402 if( FOOTPRINT* parentFP = GetParentFootprint() )
1403 SetOrientation( aAngle + parentFP->GetOrientation() );
1404 else
1405 SetOrientation( aAngle );
1406}
1407
1408
1410{
1411 if( FOOTPRINT* parentFP = GetParentFootprint() )
1412 return GetOrientation() - parentFP->GetOrientation();
1413 else
1414 return GetOrientation();
1415}
1416
1417
1418void PAD::Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
1419{
1420 MIRROR( m_pos, aCentre, aFlipDirection );
1421
1422 m_padStack.ForEachUniqueLayer(
1423 [&]( PCB_LAYER_ID aLayer )
1424 {
1425 MIRROR( m_padStack.Offset( aLayer ), VECTOR2I{ 0, 0 }, aFlipDirection );
1426 MIRROR( m_padStack.TrapezoidDeltaSize( aLayer ), VECTOR2I{ 0, 0 }, aFlipDirection );
1427 } );
1428
1430
1431 auto mirrorBitFlags = []( int& aBitfield, int a, int b )
1432 {
1433 bool temp = aBitfield & a;
1434
1435 if( aBitfield & b )
1436 aBitfield |= a;
1437 else
1438 aBitfield &= ~a;
1439
1440 if( temp )
1441 aBitfield |= b;
1442 else
1443 aBitfield &= ~b;
1444 };
1445
1447 [&]( PCB_LAYER_ID aLayer )
1448 {
1449 if( aFlipDirection == FLIP_DIRECTION::LEFT_RIGHT )
1450 {
1451 mirrorBitFlags( m_padStack.ChamferPositions( aLayer ), RECT_CHAMFER_TOP_LEFT,
1453 mirrorBitFlags( m_padStack.ChamferPositions( aLayer ), RECT_CHAMFER_BOTTOM_LEFT,
1455 }
1456 else
1457 {
1458 mirrorBitFlags( m_padStack.ChamferPositions( aLayer ), RECT_CHAMFER_TOP_LEFT,
1460 mirrorBitFlags( m_padStack.ChamferPositions( aLayer ), RECT_CHAMFER_TOP_RIGHT,
1462 }
1463 } );
1464
1465 // Flip padstack geometry
1466 int copperLayerCount = BoardCopperLayerCount();
1467
1468 m_padStack.FlipLayers( copperLayerCount );
1469
1470 // Flip pads layers after padstack geometry
1471 LSET flipped;
1472
1473 for( PCB_LAYER_ID layer : m_padStack.LayerSet() )
1474 flipped.set( GetBoard()->FlipLayer( layer ) );
1475
1476 SetLayerSet( flipped );
1477
1478 // Flip the basic shapes, in custom pads
1479 FlipPrimitives( aFlipDirection );
1480
1481 SetDirty();
1482}
1483
1484
1486{
1488 [&]( PCB_LAYER_ID aLayer )
1489 {
1490 for( std::shared_ptr<PCB_SHAPE>& primitive : m_padStack.Primitives( aLayer ) )
1491 {
1492 // Ensure the primitive parent is up to date. Flip uses GetBoard() that
1493 // imply primitive parent is valid
1494 primitive->SetParent(this);
1495 primitive->Flip( VECTOR2I( 0, 0 ), aFlipDirection );
1496 }
1497 } );
1498
1499 SetDirty();
1500}
1501
1502
1504{
1505 VECTOR2I loc_offset = m_padStack.Offset( aLayer );
1506
1507 if( loc_offset.x == 0 && loc_offset.y == 0 )
1508 return m_pos;
1509
1510 RotatePoint( loc_offset, GetOrientation() );
1511
1512 VECTOR2I shape_pos = m_pos + loc_offset;
1513
1514 return shape_pos;
1515}
1516
1517
1519{
1521 {
1522 // NPTH pads have no plated hole cylinder. If their annular ring size is 0 or
1523 // negative, then they have no annular ring either.
1524 bool hasAnnularRing = true;
1525
1527 [&]( PCB_LAYER_ID aLayer )
1528 {
1529 switch( GetShape( aLayer ) )
1530 {
1531 case PAD_SHAPE::CIRCLE:
1532 if( m_padStack.Offset( aLayer ) == VECTOR2I( 0, 0 )
1533 && m_padStack.Size( aLayer ).x <= m_padStack.Drill().size.x )
1534 {
1535 hasAnnularRing = false;
1536 }
1537
1538 break;
1539
1540 case PAD_SHAPE::OVAL:
1541 if( m_padStack.Offset( aLayer ) == VECTOR2I( 0, 0 )
1542 && m_padStack.Size( aLayer ).x <= m_padStack.Drill().size.x
1543 && m_padStack.Size( aLayer ).y <= m_padStack.Drill().size.y )
1544 {
1545 hasAnnularRing = false;
1546 }
1547
1548 break;
1549
1550 default:
1551 // We could subtract the hole polygon from the shape polygon for these, but it
1552 // would be expensive and we're probably well out of the common use cases....
1553 break;
1554 }
1555 } );
1556
1557 if( !hasAnnularRing )
1558 return false;
1559 }
1560
1561 return ( GetLayerSet() & LSET::AllCuMask() ).any();
1562}
1563
1564
1565std::optional<int> PAD::GetLocalClearance( wxString* aSource ) const
1566{
1567 if( m_padStack.Clearance().has_value() && aSource )
1568 *aSource = _( "pad" );
1569
1570 return m_padStack.Clearance();
1571}
1572
1573
1574std::optional<int> PAD::GetClearanceOverrides( wxString* aSource ) const
1575{
1576 if( m_padStack.Clearance().has_value() )
1577 return GetLocalClearance( aSource );
1578
1579 if( FOOTPRINT* parentFootprint = GetParentFootprint() )
1580 return parentFootprint->GetClearanceOverrides( aSource );
1581
1582 return std::optional<int>();
1583}
1584
1585
1586int PAD::GetOwnClearance( PCB_LAYER_ID aLayer, wxString* aSource ) const
1587{
1589
1590 if( GetBoard() && GetBoard()->GetDesignSettings().m_DRCEngine )
1591 {
1593
1595 c = bds.m_DRCEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, this, nullptr, aLayer );
1596 else
1597 c = bds.m_DRCEngine->EvalRules( CLEARANCE_CONSTRAINT, this, nullptr, aLayer );
1598 }
1599
1600 if( c.Value().HasMin() )
1601 {
1602 if( aSource )
1603 *aSource = c.GetName();
1604
1605 return c.Value().Min();
1606 }
1607
1608 return 0;
1609}
1610
1611
1613{
1614 // Pads defined only on mask layers (and perhaps on other tech layers) use the shape
1615 // defined by the pad settings only. ALL other pads, even those that don't actually have
1616 // any copper (such as NPTH pads with holes the same size as the pad) get mask expansion.
1617 if( ( m_padStack.LayerSet() & LSET::AllCuMask() ).none() )
1618 return 0;
1619
1620 if( IsFrontLayer( aLayer ) )
1621 aLayer = F_Mask;
1622 else if( IsBackLayer( aLayer ) )
1623 aLayer = B_Mask;
1624 else
1625 return 0;
1626
1627 std::optional<int> margin;
1628
1629 if( GetBoard() && GetBoard()->GetDesignSettings().m_DRCEngine )
1630 {
1631 DRC_CONSTRAINT constraint;
1632 std::shared_ptr<DRC_ENGINE> drcEngine = GetBoard()->GetDesignSettings().m_DRCEngine;
1633
1634 constraint = drcEngine->EvalRules( SOLDER_MASK_EXPANSION_CONSTRAINT, this, nullptr, aLayer );
1635
1636 if( constraint.m_Value.HasOpt() )
1637 margin = constraint.m_Value.Opt();
1638 }
1639 else
1640 {
1641 margin = m_padStack.SolderMaskMargin( aLayer );
1642
1643 if( !margin.has_value() )
1644 {
1645 if( FOOTPRINT* parentFootprint = GetParentFootprint() )
1646 margin = parentFootprint->GetLocalSolderMaskMargin();
1647 }
1648 }
1649
1650 int marginValue = margin.value_or( 0 );
1651
1652 PCB_LAYER_ID cuLayer = ( aLayer == B_Mask ) ? B_Cu : F_Cu;
1653
1654 // ensure mask have a size always >= 0
1655 if( marginValue < 0 )
1656 {
1657 int minsize = -std::min( m_padStack.Size( cuLayer ).x, m_padStack.Size( cuLayer ).y ) / 2;
1658
1659 if( marginValue < minsize )
1660 marginValue = minsize;
1661 }
1662
1663 return marginValue;
1664}
1665
1666
1668{
1669 // Pads defined only on mask layers (and perhaps on other tech layers) use the shape
1670 // defined by the pad settings only. ALL other pads, even those that don't actually have
1671 // any copper (such as NPTH pads with holes the same size as the pad) get paste expansion.
1672 if( ( m_padStack.LayerSet() & LSET::AllCuMask() ).none() )
1673 return VECTOR2I( 0, 0 );
1674
1675 if( IsFrontLayer( aLayer ) )
1676 aLayer = F_Paste;
1677 else if( IsBackLayer( aLayer ) )
1678 aLayer = B_Paste;
1679 else
1680 return VECTOR2I( 0, 0 );
1681
1682 std::optional<int> margin;
1683 std::optional<double> mratio;
1684
1685 if( GetBoard() && GetBoard()->GetDesignSettings().m_DRCEngine )
1686 {
1687 DRC_CONSTRAINT constraint;
1688 std::shared_ptr<DRC_ENGINE> drcEngine = GetBoard()->GetDesignSettings().m_DRCEngine;
1689
1690 constraint = drcEngine->EvalRules( SOLDER_PASTE_ABS_MARGIN_CONSTRAINT, this, nullptr, aLayer );
1691
1692 if( constraint.m_Value.HasOpt() )
1693 margin = constraint.m_Value.Opt();
1694
1695 constraint = drcEngine->EvalRules( SOLDER_PASTE_REL_MARGIN_CONSTRAINT, this, nullptr, aLayer );
1696
1697 if( constraint.m_Value.HasOpt() )
1698 mratio = constraint.m_Value.Opt() / 1000.0;
1699 }
1700 else
1701 {
1702 margin = m_padStack.SolderPasteMargin( aLayer );
1703 mratio = m_padStack.SolderPasteMarginRatio( aLayer );
1704
1705 if( !margin.has_value() )
1706 {
1707 if( FOOTPRINT* parentFootprint = GetParentFootprint() )
1708 margin = parentFootprint->GetLocalSolderPasteMargin();
1709 }
1710
1711 if( !mratio.has_value() )
1712 {
1713 if( FOOTPRINT* parentFootprint = GetParentFootprint() )
1714 mratio = parentFootprint->GetLocalSolderPasteMarginRatio();
1715 }
1716 }
1717
1718 PCB_LAYER_ID cuLayer = ( aLayer == B_Paste ) ? B_Cu : F_Cu;
1719 VECTOR2I padSize = m_padStack.Size( cuLayer );
1720
1721 VECTOR2I pad_margin;
1722 pad_margin.x = margin.value_or( 0 ) + KiROUND( padSize.x * mratio.value_or( 0 ) );
1723 pad_margin.y = margin.value_or( 0 ) + KiROUND( padSize.y * mratio.value_or( 0 ) );
1724
1725 // ensure paste have a size always >= 0
1726 if( m_padStack.Shape( aLayer ) != PAD_SHAPE::CUSTOM )
1727 {
1728 if( pad_margin.x < -padSize.x / 2 )
1729 pad_margin.x = -padSize.x / 2;
1730
1731 if( pad_margin.y < -padSize.y / 2 )
1732 pad_margin.y = -padSize.y / 2;
1733 }
1734
1735 return pad_margin;
1736}
1737
1738
1740{
1741 ZONE_CONNECTION connection = m_padStack.ZoneConnection().value_or( ZONE_CONNECTION::INHERITED );
1742
1743 if( connection != ZONE_CONNECTION::INHERITED )
1744 {
1745 if( aSource )
1746 *aSource = _( "pad" );
1747 }
1748
1749 if( connection == ZONE_CONNECTION::INHERITED )
1750 {
1751 if( FOOTPRINT* parentFootprint = GetParentFootprint() )
1752 connection = parentFootprint->GetZoneConnectionOverrides( aSource );
1753 }
1754
1755 return connection;
1756}
1757
1758
1759int PAD::GetLocalSpokeWidthOverride( wxString* aSource ) const
1760{
1761 if( m_padStack.ThermalSpokeWidth().has_value() && aSource )
1762 *aSource = _( "pad" );
1763
1764 return m_padStack.ThermalSpokeWidth().value_or( 0 );
1765}
1766
1767
1768int PAD::GetLocalThermalGapOverride( wxString* aSource ) const
1769{
1770 if( m_padStack.ThermalGap().has_value() && aSource )
1771 *aSource = _( "pad" );
1772
1773 return GetLocalThermalGapOverride().value_or( 0 );
1774}
1775
1776
1777void PAD::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
1778{
1779 wxString msg;
1780 FOOTPRINT* parentFootprint = static_cast<FOOTPRINT*>( m_parent );
1781
1782 if( aFrame->GetName() == PCB_EDIT_FRAME_NAME )
1783 {
1784 if( parentFootprint )
1785 aList.emplace_back( _( "Footprint" ), parentFootprint->GetReference() );
1786 }
1787
1788 aList.emplace_back( _( "Pad" ), m_number );
1789
1790 if( !GetPinFunction().IsEmpty() )
1791 aList.emplace_back( _( "Pin Name" ), GetPinFunction() );
1792
1793 if( !GetPinType().IsEmpty() )
1794 aList.emplace_back( _( "Pin Type" ), GetPinType() );
1795
1796 if( aFrame->GetName() == PCB_EDIT_FRAME_NAME )
1797 {
1798 aList.emplace_back( _( "Net" ), UnescapeString( GetNetname() ) );
1799
1800 aList.emplace_back( _( "Resolved Netclass" ),
1801 UnescapeString( GetEffectiveNetClass()->GetHumanReadableName() ) );
1802
1803 if( IsLocked() )
1804 aList.emplace_back( _( "Status" ), _( "Locked" ) );
1805 }
1806
1808 aList.emplace_back( _( "Layer" ), LayerMaskDescribe() );
1809
1810 if( aFrame->GetName() == FOOTPRINT_EDIT_FRAME_NAME )
1811 {
1812 if( GetAttribute() == PAD_ATTRIB::SMD )
1813 {
1814 // TOOD(JE) padstacks
1815 const std::shared_ptr<SHAPE_POLY_SET>& poly = GetEffectivePolygon( PADSTACK::ALL_LAYERS );
1816 double area = poly->Area();
1817
1818 aList.emplace_back( _( "Area" ), aFrame->MessageTextFromValue( area, true, EDA_DATA_TYPE::AREA ) );
1819 }
1820 }
1821
1822 // Show the pad shape, attribute and property
1823 wxString props = ShowPadAttr();
1824
1825 if( GetProperty() != PAD_PROP::NONE )
1826 props += ',';
1827
1828 switch( GetProperty() )
1829 {
1830 case PAD_PROP::NONE: break;
1831 case PAD_PROP::BGA: props += _( "BGA" ); break;
1832 case PAD_PROP::FIDUCIAL_GLBL: props += _( "Fiducial global" ); break;
1833 case PAD_PROP::FIDUCIAL_LOCAL: props += _( "Fiducial local" ); break;
1834 case PAD_PROP::TESTPOINT: props += _( "Test point" ); break;
1835 case PAD_PROP::HEATSINK: props += _( "Heat sink" ); break;
1836 case PAD_PROP::CASTELLATED: props += _( "Castellated" ); break;
1837 case PAD_PROP::MECHANICAL: props += _( "Mechanical" ); break;
1838 case PAD_PROP::PRESSFIT: props += _( "Press-fit" ); break;
1839 }
1840
1841 // TODO(JE) How to show complex padstack info in the message panel
1842 aList.emplace_back( ShowPadShape( PADSTACK::ALL_LAYERS ), props );
1843
1845 VECTOR2I padSize = m_padStack.Size( PADSTACK::ALL_LAYERS );
1846
1847 if( ( padShape == PAD_SHAPE::CIRCLE || padShape == PAD_SHAPE::OVAL )
1848 && padSize.x == padSize.y )
1849 {
1850 aList.emplace_back( _( "Diameter" ), aFrame->MessageTextFromValue( padSize.x ) );
1851 }
1852 else
1853 {
1854 aList.emplace_back( _( "Width" ), aFrame->MessageTextFromValue( padSize.x ) );
1855 aList.emplace_back( _( "Height" ), aFrame->MessageTextFromValue( padSize.y ) );
1856 }
1857
1858 EDA_ANGLE fp_orient = parentFootprint ? parentFootprint->GetOrientation() : ANGLE_0;
1859 EDA_ANGLE pad_orient = GetOrientation() - fp_orient;
1860 pad_orient.Normalize180();
1861
1862 if( !fp_orient.IsZero() )
1863 msg.Printf( wxT( "%g(+ %g)" ), pad_orient.AsDegrees(), fp_orient.AsDegrees() );
1864 else
1865 msg.Printf( wxT( "%g" ), GetOrientation().AsDegrees() );
1866
1867 aList.emplace_back( _( "Rotation" ), msg );
1868
1869 if( GetPadToDieLength() )
1870 {
1871 aList.emplace_back( _( "Length in Package" ),
1873 }
1874
1875 const VECTOR2I& drill = m_padStack.Drill().size;
1876
1877 if( drill.x > 0 || drill.y > 0 )
1878 {
1880 {
1881 aList.emplace_back( _( "Hole" ),
1882 wxString::Format( wxT( "%s" ),
1883 aFrame->MessageTextFromValue( drill.x ) ) );
1884 }
1885 else
1886 {
1887 aList.emplace_back( _( "Hole X / Y" ),
1888 wxString::Format( wxT( "%s / %s" ),
1889 aFrame->MessageTextFromValue( drill.x ),
1890 aFrame->MessageTextFromValue( drill.y ) ) );
1891 }
1892 }
1893
1894 wxString source;
1895 int clearance = GetOwnClearance( UNDEFINED_LAYER, &source );
1896
1897 if( !source.IsEmpty() )
1898 {
1899 aList.emplace_back( wxString::Format( _( "Min Clearance: %s" ),
1900 aFrame->MessageTextFromValue( clearance ) ),
1901 wxString::Format( _( "(from %s)" ),
1902 source ) );
1903 }
1904#if 0
1905 // useful for debug only
1906 aList.emplace_back( wxT( "UUID" ), m_Uuid.AsString() );
1907#endif
1908}
1909
1910
1911bool PAD::HitTest( const VECTOR2I& aPosition, int aAccuracy, PCB_LAYER_ID aLayer ) const
1912{
1913 if( !IsOnLayer( aLayer ) )
1914 return false;
1915
1916 VECTOR2I delta = aPosition - GetPosition();
1917 int boundingRadius = GetBoundingRadius() + aAccuracy;
1918
1919 if( delta.SquaredEuclideanNorm() > SEG::Square( boundingRadius ) )
1920 return false;
1921
1922 bool contains = GetEffectivePolygon( aLayer, ERROR_INSIDE )->Contains( aPosition, -1, aAccuracy );
1923
1924 return contains;
1925}
1926
1927
1928bool PAD::HitTest( const VECTOR2I& aPosition, int aAccuracy ) const
1929{
1930 VECTOR2I delta = aPosition - GetPosition();
1931 int boundingRadius = GetBoundingRadius() + aAccuracy;
1932
1933 if( delta.SquaredEuclideanNorm() > SEG::Square( boundingRadius ) )
1934 return false;
1935
1936 bool contains = false;
1937
1939 [&]( PCB_LAYER_ID l )
1940 {
1941 if( contains )
1942 return;
1943
1944 if( GetEffectivePolygon( l, ERROR_INSIDE )->Contains( aPosition, -1, aAccuracy ) )
1945 contains = true;
1946 } );
1947
1948 contains |= GetEffectiveHoleShape()->Collide( aPosition, aAccuracy );
1949
1950 return contains;
1951}
1952
1953
1954bool PAD::HitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const
1955{
1956 BOX2I arect = aRect;
1957 arect.Normalize();
1958 arect.Inflate( aAccuracy );
1959
1960 BOX2I bbox = GetBoundingBox();
1961
1962 if( aContained )
1963 {
1964 return arect.Contains( bbox );
1965 }
1966 else
1967 {
1968 // Fast test: if aRect is outside the polygon bounding box,
1969 // rectangles cannot intersect
1970 if( !arect.Intersects( bbox ) )
1971 return false;
1972
1973 bool hit = false;
1974
1976 [&]( PCB_LAYER_ID aLayer )
1977 {
1978 if( hit )
1979 return;
1980
1981 const std::shared_ptr<SHAPE_POLY_SET>& poly = GetEffectivePolygon( aLayer, ERROR_INSIDE );
1982
1983 int count = poly->TotalVertices();
1984
1985 for( int ii = 0; ii < count; ii++ )
1986 {
1987 VECTOR2I vertex = poly->CVertex( ii );
1988 VECTOR2I vertexNext = poly->CVertex( ( ii + 1 ) % count );
1989
1990 // Test if the point is within aRect
1991 if( arect.Contains( vertex ) )
1992 {
1993 hit = true;
1994 break;
1995 }
1996
1997 // Test if this edge intersects aRect
1998 if( arect.Intersects( vertex, vertexNext ) )
1999 {
2000 hit = true;
2001 break;
2002 }
2003 }
2004 } );
2005
2006 if( !hit )
2007 {
2008 SHAPE_RECT rect( arect );
2009 hit |= GetEffectiveHoleShape()->Collide( &rect );
2010 }
2011
2012 return hit;
2013 }
2014}
2015
2016
2017bool PAD::HitTest( const SHAPE_LINE_CHAIN& aPoly, bool aContained ) const
2018{
2019 SHAPE_COMPOUND effectiveShape;
2020
2021 // Add padstack shapes
2023 [&]( PCB_LAYER_ID aLayer )
2024 {
2025 effectiveShape.AddShape( GetEffectiveShape( aLayer ) );
2026 } );
2027
2028 // Add hole shape
2029 effectiveShape.AddShape( GetEffectiveHoleShape() );
2030
2031 return KIGEOM::ShapeHitTest( aPoly, effectiveShape, aContained );
2032}
2033
2034
2035int PAD::Compare( const PAD* aPadRef, const PAD* aPadCmp )
2036{
2037 int diff;
2038
2039 if( ( diff = static_cast<int>( aPadRef->m_attribute ) - static_cast<int>( aPadCmp->m_attribute ) ) != 0 )
2040 return diff;
2041
2042 return PADSTACK::Compare( &aPadRef->Padstack(), &aPadCmp->Padstack() );
2043}
2044
2045
2046void PAD::Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
2047{
2048 RotatePoint( m_pos, aRotCentre, aAngle );
2049 m_padStack.SetOrientation( m_padStack.GetOrientation() + aAngle );
2050
2051 SetDirty();
2052}
2053
2054
2056{
2057 switch( aShape )
2058 {
2059 case PAD_SHAPE::CIRCLE: return _( "Circle" );
2060 case PAD_SHAPE::OVAL: return _( "Oval" );
2061 case PAD_SHAPE::RECTANGLE: return _( "Rectangle" );
2062 case PAD_SHAPE::TRAPEZOID: return _( "Trapezoid" );
2063 case PAD_SHAPE::ROUNDRECT: return _( "Rounded rectangle" );
2064 case PAD_SHAPE::CHAMFERED_RECT: return _( "Chamfered rectangle" );
2065 case PAD_SHAPE::CUSTOM: return _( "Custom shape" );
2066 default: return wxT( "???" );
2067 }
2068}
2069
2070
2071wxString PAD::ShowPadShape( PCB_LAYER_ID aLayer ) const
2072{
2073 return ShowPadShape( GetShape( aLayer ) );
2074}
2075
2076
2078{
2079 switch( GetShape( aLayer ) )
2080 {
2081 case PAD_SHAPE::CIRCLE: return _( "Circle" );
2082 case PAD_SHAPE::OVAL: return _( "Oval" );
2083 case PAD_SHAPE::RECTANGLE: return _( "Rect" );
2084 case PAD_SHAPE::TRAPEZOID: return _( "Trap" );
2085 case PAD_SHAPE::ROUNDRECT: return _( "Roundrect" );
2086 case PAD_SHAPE::CHAMFERED_RECT: return _( "Chamferedrect" );
2087 case PAD_SHAPE::CUSTOM: return _( "CustomShape" );
2088 default: return wxT( "???" );
2089 }
2090}
2091
2092
2093wxString PAD::ShowPadAttr() const
2094{
2095 switch( GetAttribute() )
2096 {
2097 case PAD_ATTRIB::PTH: return _( "PTH" );
2098 case PAD_ATTRIB::SMD: return _( "SMD" );
2099 case PAD_ATTRIB::CONN: return _( "Conn" );
2100 case PAD_ATTRIB::NPTH: return _( "NPTH" );
2101 default: return wxT( "???" );
2102 }
2103}
2104
2105
2106wxString PAD::GetItemDescription( UNITS_PROVIDER* aUnitsProvider, bool aFull ) const
2107{
2108 FOOTPRINT* parentFP = GetParentFootprint();
2109
2110 // Don't report parent footprint info from footprint editor, viewer, etc.
2111 if( GetBoard() && GetBoard()->GetBoardUse() == BOARD_USE::FPHOLDER )
2112 parentFP = nullptr;
2113
2115 {
2116 if( parentFP )
2117 return wxString::Format( _( "NPTH pad of %s" ), parentFP->GetReference() );
2118 else
2119 return _( "NPTH pad" );
2120 }
2121 else if( GetNumber().IsEmpty() )
2122 {
2124 {
2125 if( parentFP )
2126 {
2127 return wxString::Format( _( "Pad %s of %s on %s" ),
2128 GetNetnameMsg(),
2129 parentFP->GetReference(),
2131 }
2132 else
2133 {
2134 return wxString::Format( _( "Pad on %s" ),
2136 }
2137 }
2138 else
2139 {
2140 if( parentFP )
2141 {
2142 return wxString::Format( _( "PTH pad %s of %s" ),
2143 GetNetnameMsg(),
2144 parentFP->GetReference() );
2145 }
2146 else
2147 {
2148 return _( "PTH pad" );
2149 }
2150 }
2151 }
2152 else
2153 {
2155 {
2156 if( parentFP )
2157 {
2158 return wxString::Format( _( "Pad %s %s of %s on %s" ),
2159 GetNumber(),
2160 GetNetnameMsg(),
2161 parentFP->GetReference(),
2163 }
2164 else
2165 {
2166 return wxString::Format( _( "Pad %s on %s" ),
2167 GetNumber(),
2169 }
2170 }
2171 else
2172 {
2173 if( parentFP )
2174 {
2175 return wxString::Format( _( "PTH pad %s %s of %s" ),
2176 GetNumber(),
2177 GetNetnameMsg(),
2178 parentFP->GetReference() );
2179 }
2180 else
2181 {
2182 return wxString::Format( _( "PTH pad %s" ),
2183 GetNumber() );
2184 }
2185 }
2186 }
2187}
2188
2189
2191{
2192 return BITMAPS::pad;
2193}
2194
2195
2197{
2198 PAD* cloned = new PAD( *this );
2199
2200 // Ensure the cloned primitives of the pad stack have the right parent
2201 cloned->Padstack().ForEachUniqueLayer(
2202 [&]( PCB_LAYER_ID aLayer )
2203 {
2204 for( std::shared_ptr<PCB_SHAPE>& primitive : cloned->m_padStack.Primitives( aLayer ) )
2205 primitive->SetParent( cloned );
2206 } );
2207
2208 return cloned;
2209}
2210
2211
2212std::vector<int> PAD::ViewGetLayers() const
2213{
2214 std::vector<int> layers;
2215 layers.reserve( 64 );
2216
2217 // These 2 types of pads contain a hole
2219 {
2220 layers.push_back( LAYER_PAD_PLATEDHOLES );
2221 layers.push_back( LAYER_PAD_HOLEWALLS );
2222 }
2223
2225 layers.push_back( LAYER_NON_PLATEDHOLES );
2226
2227
2229 layers.push_back( LAYER_LOCKED_ITEM_SHADOW );
2230
2231 LSET cuLayers = ( m_padStack.LayerSet() & LSET::AllCuMask() );
2232
2233 // Don't spend cycles rendering layers that aren't visible
2234 if( const BOARD* board = GetBoard() )
2235 cuLayers &= board->GetEnabledLayers();
2236
2237 if( cuLayers.count() > 1 )
2238 {
2239 // Multi layer pad
2240 for( PCB_LAYER_ID layer : cuLayers.Seq() )
2241 {
2242 layers.push_back( LAYER_PAD_COPPER_START + layer );
2243 layers.push_back( LAYER_CLEARANCE_START + layer );
2244 }
2245
2246 layers.push_back( LAYER_PAD_NETNAMES );
2247 }
2248 else if( IsOnLayer( F_Cu ) )
2249 {
2250 layers.push_back( LAYER_PAD_COPPER_START );
2251 layers.push_back( LAYER_CLEARANCE_START );
2252
2253 // Is this a PTH pad that has only front copper? If so, we need to also display the
2254 // net name on the PTH netname layer so that it isn't blocked by the drill hole.
2256 layers.push_back( LAYER_PAD_NETNAMES );
2257 else
2258 layers.push_back( LAYER_PAD_FR_NETNAMES );
2259 }
2260 else if( IsOnLayer( B_Cu ) )
2261 {
2262 layers.push_back( LAYER_PAD_COPPER_START + B_Cu );
2263 layers.push_back( LAYER_CLEARANCE_START + B_Cu );
2264
2265 // Is this a PTH pad that has only back copper? If so, we need to also display the
2266 // net name on the PTH netname layer so that it isn't blocked by the drill hole.
2268 layers.push_back( LAYER_PAD_NETNAMES );
2269 else
2270 layers.push_back( LAYER_PAD_BK_NETNAMES );
2271 }
2272
2273 // Check non-copper layers. This list should include all the layers that the
2274 // footprint editor allows a pad to be placed on.
2275 static const PCB_LAYER_ID layers_mech[] = { F_Mask, B_Mask, F_Paste, B_Paste,
2277
2278 for( PCB_LAYER_ID each_layer : layers_mech )
2279 {
2280 if( IsOnLayer( each_layer ) )
2281 layers.push_back( each_layer );
2282 }
2283
2284 return layers;
2285}
2286
2287
2288double PAD::ViewGetLOD( int aLayer, const KIGFX::VIEW* aView ) const
2289{
2290 PCB_PAINTER& painter = static_cast<PCB_PAINTER&>( *aView->GetPainter() );
2291 PCB_RENDER_SETTINGS& renderSettings = *painter.GetSettings();
2292 const BOARD* board = GetBoard();
2293
2294 // Meta control for hiding all pads
2295 if( !aView->IsLayerVisible( LAYER_PADS ) )
2296 return LOD_HIDE;
2297
2298 // Handle Render tab switches
2299 //const PCB_LAYER_ID& pcbLayer = static_cast<PCB_LAYER_ID>( aLayer );
2300
2301 if( !IsFlipped() && !aView->IsLayerVisible( LAYER_FOOTPRINTS_FR ) )
2302 return LOD_HIDE;
2303
2304 if( IsFlipped() && !aView->IsLayerVisible( LAYER_FOOTPRINTS_BK ) )
2305 return LOD_HIDE;
2306
2307 if( IsHoleLayer( aLayer ) )
2308 {
2309 LSET visiblePhysical = board->GetVisibleLayers();
2310 visiblePhysical &= board->GetEnabledLayers();
2311 visiblePhysical &= LSET::PhysicalLayersMask();
2312
2313 if( !visiblePhysical.any() )
2314 return LOD_HIDE;
2315 }
2316 else if( IsNetnameLayer( aLayer ) )
2317 {
2318 if( renderSettings.GetHighContrast() )
2319 {
2320 // Hide netnames unless pad is flashed to a high-contrast layer
2321 if( !FlashLayer( renderSettings.GetPrimaryHighContrastLayer() ) )
2322 return LOD_HIDE;
2323 }
2324 else
2325 {
2326 LSET visible = board->GetVisibleLayers();
2327 visible &= board->GetEnabledLayers();
2328
2329 // Hide netnames unless pad is flashed to a visible layer
2330 if( !FlashLayer( visible ) )
2331 return LOD_HIDE;
2332 }
2333
2334 // Netnames will be shown only if zoom is appropriate
2335 const int minSize = std::min( GetBoundingBox().GetWidth(), GetBoundingBox().GetHeight() );
2336
2337 return lodScaleForThreshold( aView, minSize, pcbIUScale.mmToIU( 0.5 ) );
2338 }
2339
2340 VECTOR2L padSize = GetBoundingBox().GetSize();
2341 int64_t minSide = std::min( padSize.x, padSize.y );
2342
2343 if( minSide > 0 )
2344 return std::min( lodScaleForThreshold( aView, minSide, pcbIUScale.mmToIU( 0.2 ) ), 3.5 );
2345
2346 return LOD_SHOW;
2347}
2348
2349
2351{
2352 // Bounding box includes soldermask too. Remember mask and/or paste margins can be < 0
2353 int solderMaskMargin = 0;
2354 VECTOR2I solderPasteMargin;
2355
2357 [&]( PCB_LAYER_ID aLayer )
2358 {
2359 solderMaskMargin = std::max( solderMaskMargin, std::max( GetSolderMaskExpansion( aLayer ), 0 ) );
2360 VECTOR2I layerMargin = GetSolderPasteMargin( aLayer );
2361 solderPasteMargin.x = std::max( solderPasteMargin.x, layerMargin.x );
2362 solderPasteMargin.y = std::max( solderPasteMargin.y, layerMargin.y );
2363 } );
2364
2365 BOX2I bbox = GetBoundingBox();
2366 int clearance = 0;
2367
2368 // If we're drawing clearance lines then get the biggest possible clearance
2369 if( PCBNEW_SETTINGS* cfg = dynamic_cast<PCBNEW_SETTINGS*>( Kiface().KifaceSettings() ) )
2370 {
2371 if( cfg && cfg->m_Display.m_PadClearance && GetBoard() )
2373 }
2374
2375 // Look for the biggest possible bounding box
2376 int xMargin = std::max( solderMaskMargin, solderPasteMargin.x ) + clearance;
2377 int yMargin = std::max( solderMaskMargin, solderPasteMargin.y ) + clearance;
2378
2379 return BOX2I( VECTOR2I( bbox.GetOrigin() ) - VECTOR2I( xMargin, yMargin ),
2380 VECTOR2I( bbox.GetSize() ) + VECTOR2I( 2 * xMargin, 2 * yMargin ) );
2381}
2382
2383
2384void PAD::ImportSettingsFrom( const PAD& aMasterPad )
2385{
2386 SetPadstack( aMasterPad.Padstack() );
2387 // Layer Set should be updated before calling SetAttribute()
2388 SetLayerSet( aMasterPad.GetLayerSet() );
2389 SetAttribute( aMasterPad.GetAttribute() );
2390 // Unfortunately, SetAttribute() can change m_layerMask.
2391 // Be sure we keep the original mask by calling SetLayerSet() after SetAttribute()
2392 SetLayerSet( aMasterPad.GetLayerSet() );
2393 SetProperty( aMasterPad.GetProperty() );
2394
2395 // Must be after setting attribute and layerSet
2396 if( !CanHaveNumber() )
2397 SetNumber( wxEmptyString );
2398
2399 // I am not sure the m_LengthPadToDie should be imported, because this is a parameter
2400 // really specific to a given pad (JPC).
2401#if 0
2402 SetPadToDieLength( aMasterPad.GetPadToDieLength() );
2403 SetPadToDieDelay( aMasterPad.GetPadToDieDelay() );
2404#endif
2405
2406 // The pad orientation, for historical reasons is the pad rotation + parent rotation.
2407 EDA_ANGLE pad_rot = aMasterPad.GetOrientation();
2408
2409 if( aMasterPad.GetParentFootprint() )
2410 pad_rot -= aMasterPad.GetParentFootprint()->GetOrientation();
2411
2412 if( GetParentFootprint() )
2413 pad_rot += GetParentFootprint()->GetOrientation();
2414
2415 SetOrientation( pad_rot );
2416
2418 [&]( PCB_LAYER_ID aLayer )
2419 {
2420 // Ensure that circles are circles
2421 if( aMasterPad.GetShape( aLayer ) == PAD_SHAPE::CIRCLE )
2422 SetSize( aLayer, VECTOR2I( GetSize( aLayer ).x, GetSize( aLayer ).x ) );
2423 } );
2424
2425 switch( aMasterPad.GetAttribute() )
2426 {
2427 case PAD_ATTRIB::SMD:
2428 case PAD_ATTRIB::CONN:
2429 // These pads do not have a hole (they are expected to be on one external copper layer)
2430 SetDrillSize( VECTOR2I( 0, 0 ) );
2431 break;
2432
2433 default:
2434 ;
2435 }
2436
2437 // copy also local settings:
2438 SetLocalClearance( aMasterPad.GetLocalClearance() );
2442
2447
2449
2451
2452 SetDirty();
2453}
2454
2455
2457{
2458 assert( aImage->Type() == PCB_PAD_T );
2459
2460 std::swap( *this, *static_cast<PAD*>( aImage ) );
2461}
2462
2463
2464bool PAD::TransformHoleToPolygon( SHAPE_POLY_SET& aBuffer, int aClearance, int aError,
2465 ERROR_LOC aErrorLoc ) const
2466{
2467 VECTOR2I drillsize = GetDrillSize();
2468
2469 if( !drillsize.x || !drillsize.y )
2470 return false;
2471
2472 std::shared_ptr<SHAPE_SEGMENT> slot = GetEffectiveHoleShape();
2473
2474 TransformOvalToPolygon( aBuffer, slot->GetSeg().A, slot->GetSeg().B, slot->GetWidth() + aClearance * 2,
2475 aError, aErrorLoc );
2476
2477 return true;
2478}
2479
2480
2481void PAD::TransformShapeToPolygon( SHAPE_POLY_SET& aBuffer, PCB_LAYER_ID aLayer, int aClearance,
2482 int aMaxError, ERROR_LOC aErrorLoc, bool ignoreLineWidth ) const
2483{
2484 wxASSERT_MSG( !ignoreLineWidth, wxT( "IgnoreLineWidth has no meaning for pads." ) );
2485 wxASSERT_MSG( aLayer != UNDEFINED_LAYER,
2486 wxT( "UNDEFINED_LAYER is no longer allowed for PAD::TransformShapeToPolygon" ) );
2487
2488 // minimal segment count to approximate a circle to create the polygonal pad shape
2489 // This minimal value is mainly for very small pads, like SM0402.
2490 // Most of time pads are using the segment count given by aError value.
2491 const int pad_min_seg_per_circle_count = 16;
2492 int dx = m_padStack.Size( aLayer ).x / 2;
2493 int dy = m_padStack.Size( aLayer ).y / 2;
2494
2495 VECTOR2I padShapePos = ShapePos( aLayer ); // Note: for pad having a shape offset, the pad
2496 // position is NOT the shape position
2497
2498 switch( PAD_SHAPE shape = GetShape( aLayer ) )
2499 {
2500 case PAD_SHAPE::CIRCLE:
2501 case PAD_SHAPE::OVAL:
2502 // Note: dx == dy is not guaranteed for circle pads in legacy boards
2503 if( dx == dy || ( shape == PAD_SHAPE::CIRCLE ) )
2504 {
2505 TransformCircleToPolygon( aBuffer, padShapePos, dx + aClearance, aMaxError, aErrorLoc,
2506 pad_min_seg_per_circle_count );
2507 }
2508 else
2509 {
2510 int half_width = std::min( dx, dy );
2511 VECTOR2I delta( dx - half_width, dy - half_width );
2512
2514
2515 TransformOvalToPolygon( aBuffer, padShapePos - delta, padShapePos + delta,
2516 ( half_width + aClearance ) * 2, aMaxError, aErrorLoc,
2517 pad_min_seg_per_circle_count );
2518 }
2519
2520 break;
2521
2524 {
2525 const VECTOR2I& trapDelta = m_padStack.TrapezoidDeltaSize( aLayer );
2526 int ddx = shape == PAD_SHAPE::TRAPEZOID ? trapDelta.x / 2 : 0;
2527 int ddy = shape == PAD_SHAPE::TRAPEZOID ? trapDelta.y / 2 : 0;
2528
2529 SHAPE_POLY_SET outline;
2530 TransformTrapezoidToPolygon( outline, padShapePos, m_padStack.Size( aLayer ), GetOrientation(),
2531 ddx, ddy, aClearance, aMaxError, aErrorLoc );
2532 aBuffer.Append( outline );
2533 break;
2534 }
2535
2538 {
2539 bool doChamfer = shape == PAD_SHAPE::CHAMFERED_RECT;
2540
2541 SHAPE_POLY_SET outline;
2542 TransformRoundChamferedRectToPolygon( outline, padShapePos, m_padStack.Size( aLayer ),
2544 doChamfer ? GetChamferRectRatio( aLayer ) : 0,
2545 doChamfer ? GetChamferPositions( aLayer ) : 0,
2546 aClearance, aMaxError, aErrorLoc );
2547 aBuffer.Append( outline );
2548 break;
2549 }
2550
2551 case PAD_SHAPE::CUSTOM:
2552 {
2553 SHAPE_POLY_SET outline;
2554 MergePrimitivesAsPolygon( aLayer, &outline, aErrorLoc );
2555 outline.Rotate( GetOrientation() );
2556 outline.Move( VECTOR2I( padShapePos ) );
2557
2558 if( aClearance > 0 || aErrorLoc == ERROR_OUTSIDE )
2559 {
2560 if( aErrorLoc == ERROR_OUTSIDE )
2561 aClearance += aMaxError;
2562
2563 outline.Inflate( aClearance, CORNER_STRATEGY::ROUND_ALL_CORNERS, aMaxError );
2564 outline.Fracture();
2565 }
2566 else if( aClearance < 0 )
2567 {
2568 // Negative clearances are primarily for drawing solder paste layer, so we don't
2569 // worry ourselves overly about which side the error is on.
2570
2571 // aClearance is negative so this is actually a deflate
2572 outline.Inflate( aClearance, CORNER_STRATEGY::ALLOW_ACUTE_CORNERS, aMaxError );
2573 outline.Fracture();
2574 }
2575
2576 aBuffer.Append( outline );
2577 break;
2578 }
2579
2580 default:
2581 wxFAIL_MSG( wxT( "PAD::TransformShapeToPolygon no implementation for " )
2582 + wxString( std::string( magic_enum::enum_name( shape ) ) ) );
2583 break;
2584 }
2585}
2586
2587
2588std::vector<PCB_SHAPE*> PAD::Recombine( bool aIsDryRun, int maxError )
2589{
2590 FOOTPRINT* footprint = GetParentFootprint();
2591
2592 for( BOARD_ITEM* item : footprint->GraphicalItems() )
2593 item->ClearFlags( SKIP_STRUCT );
2594
2595 auto findNext =
2596 [&]( PCB_LAYER_ID aLayer ) -> PCB_SHAPE*
2597 {
2598 SHAPE_POLY_SET padPoly;
2599 TransformShapeToPolygon( padPoly, aLayer, 0, maxError, ERROR_INSIDE );
2600
2601 for( BOARD_ITEM* item : footprint->GraphicalItems() )
2602 {
2603 PCB_SHAPE* shape = dynamic_cast<PCB_SHAPE*>( item );
2604
2605 if( !shape || ( shape->GetFlags() & SKIP_STRUCT ) )
2606 continue;
2607
2608 if( shape->GetLayer() != aLayer )
2609 continue;
2610
2611 if( shape->IsProxyItem() ) // Pad number (and net name) box
2612 return shape;
2613
2614 SHAPE_POLY_SET drawPoly;
2615 shape->TransformShapeToPolygon( drawPoly, aLayer, 0, maxError, ERROR_INSIDE );
2616 drawPoly.BooleanIntersection( padPoly );
2617
2618 if( !drawPoly.IsEmpty() )
2619 return shape;
2620 }
2621
2622 return nullptr;
2623 };
2624
2625 auto findMatching =
2626 [&]( PCB_SHAPE* aShape ) -> std::vector<PCB_SHAPE*>
2627 {
2628 std::vector<PCB_SHAPE*> matching;
2629
2630 for( BOARD_ITEM* item : footprint->GraphicalItems() )
2631 {
2632 PCB_SHAPE* other = dynamic_cast<PCB_SHAPE*>( item );
2633
2634 if( !other || ( other->GetFlags() & SKIP_STRUCT ) )
2635 continue;
2636
2637 if( GetLayerSet().test( other->GetLayer() ) && aShape->Compare( other ) == 0 )
2638 matching.push_back( other );
2639 }
2640
2641 return matching;
2642 };
2643
2644 PCB_LAYER_ID layer;
2645 std::vector<PCB_SHAPE*> mergedShapes;
2646
2647 if( IsOnLayer( F_Cu ) )
2648 layer = F_Cu;
2649 else if( IsOnLayer( B_Cu ) )
2650 layer = B_Cu;
2651 else
2652 layer = GetLayerSet().UIOrder().front();
2653
2654 PAD_SHAPE origShape = GetShape( layer );
2655
2656 // If there are intersecting items to combine, we need to first make sure the pad is a
2657 // custom-shape pad.
2658 if( !aIsDryRun && findNext( layer ) && origShape != PAD_SHAPE::CUSTOM )
2659 {
2660 if( origShape == PAD_SHAPE::CIRCLE || origShape == PAD_SHAPE::RECTANGLE )
2661 {
2662 // Use the existing pad as an anchor
2663 SetAnchorPadShape( layer, origShape );
2664 SetShape( layer, PAD_SHAPE::CUSTOM );
2665 }
2666 else
2667 {
2668 // Create a new circular anchor and convert existing pad to a polygon primitive
2669 SHAPE_POLY_SET existingOutline;
2670 TransformShapeToPolygon( existingOutline, layer, 0, maxError, ERROR_INSIDE );
2671
2672 int minExtent = std::min( GetSize( layer ).x, GetSize( layer ).y );
2674 SetSize( layer, VECTOR2I( minExtent, minExtent ) );
2675 SetShape( layer, PAD_SHAPE::CUSTOM );
2676
2677 PCB_SHAPE* shape = new PCB_SHAPE( nullptr, SHAPE_T::POLY );
2678 shape->SetFilled( true );
2680 shape->SetPolyShape( existingOutline );
2681 shape->Move( - ShapePos( layer ) );
2682 shape->Rotate( VECTOR2I( 0, 0 ), - GetOrientation() );
2683 AddPrimitive( layer, shape );
2684 }
2685 }
2686
2687 while( PCB_SHAPE* fpShape = findNext( layer ) )
2688 {
2689 fpShape->SetFlags( SKIP_STRUCT );
2690
2691 mergedShapes.push_back( fpShape );
2692
2693 if( !aIsDryRun )
2694 {
2695 // If the editor was inside a group when the pad was exploded, the added exploded shapes
2696 // will be part of the group. Remove them here before duplicating; we don't want the
2697 // primitives to wind up in a group.
2698 if( EDA_GROUP* group = fpShape->GetParentGroup(); group )
2699 group->RemoveItem( fpShape );
2700
2701 PCB_SHAPE* primitive = static_cast<PCB_SHAPE*>( fpShape->Duplicate( IGNORE_PARENT_GROUP ) );
2702
2703 primitive->SetParent( nullptr );
2704
2705 // Convert any hatched fills to solid
2706 if( primitive->IsAnyFill() )
2707 primitive->SetFillMode( FILL_T::FILLED_SHAPE );
2708
2709 primitive->Move( - ShapePos( layer ) );
2710 primitive->Rotate( VECTOR2I( 0, 0 ), - GetOrientation() );
2711
2712 AddPrimitive( layer, primitive );
2713 }
2714
2715 // See if there are other shapes that match and mark them for delete. (KiCad won't
2716 // produce these, but old footprints from other vendors have them.)
2717 for( PCB_SHAPE* other : findMatching( fpShape ) )
2718 {
2719 other->SetFlags( SKIP_STRUCT );
2720 mergedShapes.push_back( other );
2721 }
2722 }
2723
2724 for( BOARD_ITEM* item : footprint->GraphicalItems() )
2725 item->ClearFlags( SKIP_STRUCT );
2726
2727 if( !aIsDryRun )
2729
2730 return mergedShapes;
2731}
2732
2733
2734void PAD::CheckPad( UNITS_PROVIDER* aUnitsProvider, bool aForPadProperties,
2735 const std::function<void( int aErrorCode, const wxString& aMsg )>& aErrorHandler ) const
2736{
2738 [&]( PCB_LAYER_ID aLayer )
2739 {
2740 doCheckPad( aLayer, aUnitsProvider, aForPadProperties, aErrorHandler );
2741 } );
2742
2743 LSET padlayers_mask = GetLayerSet();
2744 VECTOR2I drill_size = GetDrillSize();
2745
2746 if( !padlayers_mask[F_Cu] && !padlayers_mask[B_Cu] )
2747 {
2748 if( ( drill_size.x || drill_size.y ) && GetAttribute() != PAD_ATTRIB::NPTH )
2749 {
2750 aErrorHandler( DRCE_PADSTACK, _( "(plated through holes normally have a copper pad on "
2751 "at least one outer layer)" ) );
2752 }
2753 }
2754
2757 {
2758 aErrorHandler( DRCE_PADSTACK, _( "('fiducial' pads are normally plated)" ) );
2759 }
2760
2762 aErrorHandler( DRCE_PADSTACK, _( "('testpoint' pads are normally plated)" ) );
2763
2765 aErrorHandler( DRCE_PADSTACK, _( "('heatsink' pads are normally plated)" ) );
2766
2768 aErrorHandler( DRCE_PADSTACK, _( "('castellated' pads are normally PTH)" ) );
2769
2771 aErrorHandler( DRCE_PADSTACK, _( "('BGA' property is for SMD pads)" ) );
2772
2774 aErrorHandler( DRCE_PADSTACK, _( "('mechanical' pads are normally PTH)" ) );
2775
2777 && ( GetAttribute() != PAD_ATTRIB::PTH || !HasDrilledHole() ) )
2778 {
2779 aErrorHandler( DRCE_PADSTACK, _( "('press-fit' pads are normally PTH with round holes)" ) );
2780 }
2781
2782 switch( GetAttribute() )
2783 {
2784 case PAD_ATTRIB::NPTH: // Not plated, but through hole, a hole is expected
2785 case PAD_ATTRIB::PTH: // Pad through hole, a hole is also expected
2786 if( drill_size.x <= 0
2787 || ( drill_size.y <= 0 && GetDrillShape() == PAD_DRILL_SHAPE::OBLONG ) )
2788 {
2789 aErrorHandler( DRCE_PAD_TH_WITH_NO_HOLE, wxEmptyString );
2790 }
2791 break;
2792
2793 case PAD_ATTRIB::CONN: // Connector pads are smd pads, just they do not have solder paste.
2794 if( padlayers_mask[B_Paste] || padlayers_mask[F_Paste] )
2795 {
2796 aErrorHandler( DRCE_PADSTACK, _( "(connector pads normally have no solder paste; use a "
2797 "SMD pad instead)" ) );
2798 }
2800
2801 case PAD_ATTRIB::SMD: // SMD and Connector pads (One external copper layer only)
2802 {
2803 if( drill_size.x > 0 || drill_size.y > 0 )
2804 aErrorHandler( DRCE_PADSTACK_INVALID, _( "(SMD pad has a hole)" ) );
2805
2806 LSET innerlayers_mask = padlayers_mask & LSET::InternalCuMask();
2807
2808 if( IsOnLayer( F_Cu ) && IsOnLayer( B_Cu ) )
2809 {
2810 aErrorHandler( DRCE_PADSTACK, _( "(SMD pad has copper on both sides of the board)" ) );
2811 }
2812 else if( IsOnLayer( F_Cu ) )
2813 {
2814 if( IsOnLayer( B_Mask ) )
2815 {
2816 aErrorHandler( DRCE_PADSTACK, _( "(SMD pad has copper and mask layers on different "
2817 "sides of the board)" ) );
2818 }
2819 else if( IsOnLayer( B_Paste ) )
2820 {
2821 aErrorHandler( DRCE_PADSTACK, _( "(SMD pad has copper and paste layers on different "
2822 "sides of the board)" ) );
2823 }
2824 }
2825 else if( IsOnLayer( B_Cu ) )
2826 {
2827 if( IsOnLayer( F_Mask ) )
2828 {
2829 aErrorHandler( DRCE_PADSTACK, _( "(SMD pad has copper and mask layers on different "
2830 "sides of the board)" ) );
2831 }
2832 else if( IsOnLayer( F_Paste ) )
2833 {
2834 aErrorHandler( DRCE_PADSTACK, _( "(SMD pad has copper and paste layers on different "
2835 "sides of the board)" ) );
2836 }
2837 }
2838 else if( innerlayers_mask.count() != 0 )
2839 {
2840 aErrorHandler( DRCE_PADSTACK, _( "(SMD pad has no outer layers)" ) );
2841 }
2842
2843 break;
2844 }
2845 }
2846}
2847
2848
2849void PAD::doCheckPad( PCB_LAYER_ID aLayer, UNITS_PROVIDER* aUnitsProvider, bool aForPadProperties,
2850 const std::function<void( int aErrorCode, const wxString& aMsg )>& aErrorHandler ) const
2851{
2852 wxString msg;
2853
2854 VECTOR2I pad_size = GetSize( aLayer );
2855
2856 if( GetShape( aLayer ) == PAD_SHAPE::CUSTOM )
2857 pad_size = GetBoundingBox().GetSize();
2858 else if( pad_size.x <= 0 || ( pad_size.y <= 0 && GetShape( aLayer ) != PAD_SHAPE::CIRCLE ) )
2859 aErrorHandler( DRCE_PADSTACK_INVALID, _( "(Pad must have a positive size)" ) );
2860
2861 // Test hole against pad shape
2862 if( IsOnCopperLayer() && GetDrillSize().x > 0 )
2863 {
2864 // Ensure the drill size can be handled in next calculations.
2865 // Use min size = 4 IU to be able to build a polygon from a hole shape
2866 const int min_drill_size = 4;
2867
2868 if( GetDrillSizeX() <= min_drill_size || GetDrillSizeY() <= min_drill_size )
2869 {
2870 msg.Printf( _( "(PTH pad hole size must be larger than %s)" ),
2871 aUnitsProvider->StringFromValue( min_drill_size, true ) );
2872 aErrorHandler( DRCE_PADSTACK_INVALID, msg );
2873 }
2874
2875 SHAPE_POLY_SET padOutline;
2876
2877 TransformShapeToPolygon( padOutline, aLayer, 0, GetMaxError(), ERROR_INSIDE );
2878
2879 if( GetAttribute() == PAD_ATTRIB::PTH )
2880 {
2881 // Test if there is copper area outside hole
2882 std::shared_ptr<SHAPE_SEGMENT> hole = GetEffectiveHoleShape();
2883 SHAPE_POLY_SET holeOutline;
2884
2885 TransformOvalToPolygon( holeOutline, hole->GetSeg().A, hole->GetSeg().B, hole->GetWidth(),
2887
2888 SHAPE_POLY_SET copper = padOutline;
2889 copper.BooleanSubtract( holeOutline );
2890
2891 if( copper.IsEmpty() )
2892 {
2893 aErrorHandler( DRCE_PADSTACK, _( "(PTH pad hole leaves no copper)" ) );
2894 }
2895 else if( aForPadProperties )
2896 {
2897 // Test if the pad hole is fully inside the copper area. Note that we only run
2898 // this check for pad properties because we run the more complete annular ring
2899 // checker on the board (which handles multiple pads with the same name).
2900 holeOutline.BooleanSubtract( padOutline );
2901
2902 if( !holeOutline.IsEmpty() )
2903 aErrorHandler( DRCE_PADSTACK, _( "(PTH pad hole not fully inside copper)" ) );
2904 }
2905 }
2906 else
2907 {
2908 // Test only if the pad hole's centre is inside the copper area
2909 if( !padOutline.Collide( GetPosition() ) )
2910 aErrorHandler( DRCE_PADSTACK, _( "(pad hole not inside pad shape)" ) );
2911 }
2912 }
2913
2914 if( GetLocalClearance().value_or( 0 ) < 0 )
2915 aErrorHandler( DRCE_PADSTACK, _( "(negative local clearance values have no effect)" ) );
2916
2917 // Some pads need a negative solder mask clearance (mainly for BGA with small pads)
2918 // However the negative solder mask clearance must not create negative mask size
2919 // Therefore test for minimal acceptable negative value
2920 std::optional<int> solderMaskMargin = GetLocalSolderMaskMargin();
2921
2922 if( solderMaskMargin.has_value() && solderMaskMargin.value() < 0 )
2923 {
2924 int absMargin = abs( solderMaskMargin.value() );
2925
2926 if( GetShape( aLayer ) == PAD_SHAPE::CUSTOM )
2927 {
2928 for( const std::shared_ptr<PCB_SHAPE>& shape : GetPrimitives( aLayer ) )
2929 {
2930 BOX2I shapeBBox = shape->GetBoundingBox();
2931
2932 if( absMargin > shapeBBox.GetWidth() || absMargin > shapeBBox.GetHeight() )
2933 {
2934 aErrorHandler( DRCE_PADSTACK, _( "(negative solder mask clearance is larger "
2935 "than some shape primitives; results may be "
2936 "surprising)" ) );
2937
2938 break;
2939 }
2940 }
2941 }
2942 else if( absMargin > pad_size.x || absMargin > pad_size.y )
2943 {
2944 aErrorHandler( DRCE_PADSTACK, _( "(negative solder mask clearance is larger than pad; "
2945 "no solder mask will be generated)" ) );
2946 }
2947 }
2948
2949 // Some pads need a positive solder paste clearance (mainly for BGA with small pads)
2950 // However, a positive value can create issues if the resulting shape is too big.
2951 // (like a solder paste creating a solder paste area on a neighbor pad or on the solder mask)
2952 // So we could ask for user to confirm the choice
2953 // For now we just check for disappearing paste
2954 wxSize paste_size;
2955 int paste_margin = GetLocalSolderPasteMargin().value_or( 0 );
2956 auto mratio = GetLocalSolderPasteMarginRatio();
2957
2958 paste_size.x = pad_size.x + paste_margin + KiROUND( pad_size.x * mratio.value_or( 0 ) );
2959 paste_size.y = pad_size.y + paste_margin + KiROUND( pad_size.y * mratio.value_or( 0 ) );
2960
2961 if( paste_size.x <= 0 || paste_size.y <= 0 )
2962 {
2963 aErrorHandler( DRCE_PADSTACK, _( "(negative solder paste margin is larger than pad; "
2964 "no solder paste mask will be generated)" ) );
2965 }
2966
2967 if( GetShape( aLayer ) == PAD_SHAPE::ROUNDRECT )
2968 {
2969 if( GetRoundRectRadiusRatio( aLayer ) < 0.0 )
2970 aErrorHandler( DRCE_PADSTACK_INVALID, _( "(negative corner radius is not allowed)" ) );
2971 else if( GetRoundRectRadiusRatio( aLayer ) > 50.0 )
2972 aErrorHandler( DRCE_PADSTACK, _( "(corner size will make pad circular)" ) );
2973 }
2974 else if( GetShape( aLayer ) == PAD_SHAPE::CHAMFERED_RECT )
2975 {
2976 if( GetChamferRectRatio( aLayer ) < 0.0 )
2977 aErrorHandler( DRCE_PADSTACK_INVALID, _( "(negative corner chamfer is not allowed)" ) );
2978 else if( GetChamferRectRatio( aLayer ) > 50.0 )
2979 aErrorHandler( DRCE_PADSTACK_INVALID, _( "(corner chamfer is too large)" ) );
2980 }
2981 else if( GetShape( aLayer ) == PAD_SHAPE::TRAPEZOID )
2982 {
2983 if( ( GetDelta( aLayer ).x < 0 && GetDelta( aLayer ).x < -GetSize( aLayer ).y )
2984 || ( GetDelta( aLayer ).x > 0 && GetDelta( aLayer ).x > GetSize( aLayer ).y )
2985 || ( GetDelta( aLayer ).y < 0 && GetDelta( aLayer ).y < -GetSize( aLayer ).x )
2986 || ( GetDelta( aLayer ).y > 0 && GetDelta( aLayer ).y > GetSize( aLayer ).x ) )
2987 {
2988 aErrorHandler( DRCE_PADSTACK_INVALID, _( "(trapezoid delta is too large)" ) );
2989 }
2990 }
2991
2992 if( GetShape( aLayer ) == PAD_SHAPE::CUSTOM )
2993 {
2994 SHAPE_POLY_SET mergedPolygon;
2995 MergePrimitivesAsPolygon( aLayer, &mergedPolygon );
2996
2997 if( mergedPolygon.OutlineCount() > 1 )
2998 aErrorHandler( DRCE_PADSTACK_INVALID, _( "(custom pad shape must resolve to a single polygon)" ) );
2999 }
3000}
3001
3002
3003bool PAD::operator==( const BOARD_ITEM& aBoardItem ) const
3004{
3005 if( Type() != aBoardItem.Type() )
3006 return false;
3007
3008 if( m_parent && aBoardItem.GetParent() && m_parent->m_Uuid != aBoardItem.GetParent()->m_Uuid )
3009 return false;
3010
3011 const PAD& other = static_cast<const PAD&>( aBoardItem );
3012
3013 return *this == other;
3014}
3015
3016
3017bool PAD::operator==( const PAD& aOther ) const
3018{
3019 if( Padstack() != aOther.Padstack() )
3020 return false;
3021
3022 if( GetPosition() != aOther.GetPosition() )
3023 return false;
3024
3025 if( GetAttribute() != aOther.GetAttribute() )
3026 return false;
3027
3028 return true;
3029}
3030
3031
3032double PAD::Similarity( const BOARD_ITEM& aOther ) const
3033{
3034 if( aOther.Type() != Type() )
3035 return 0.0;
3036
3037 if( m_parent->m_Uuid != aOther.GetParent()->m_Uuid )
3038 return 0.0;
3039
3040 const PAD& other = static_cast<const PAD&>( aOther );
3041
3042 double similarity = 1.0;
3043
3044 if( GetPosition() != other.GetPosition() )
3045 similarity *= 0.9;
3046
3047 if( GetAttribute() != other.GetAttribute() )
3048 similarity *= 0.9;
3049
3050 similarity *= Padstack().Similarity( other.Padstack() );
3051
3052 return similarity;
3053}
3054
3055
3056void PAD::AddPrimitivePoly( PCB_LAYER_ID aLayer, const SHAPE_POLY_SET& aPoly, int aThickness,
3057 bool aFilled )
3058{
3059 // If aPoly has holes, convert it to a polygon with no holes.
3060 SHAPE_POLY_SET poly_no_hole;
3061 poly_no_hole.Append( aPoly );
3062
3063 if( poly_no_hole.HasHoles() )
3064 poly_no_hole.Fracture();
3065
3066 // There should never be multiple shapes, but if there are, we split them into
3067 // primitives so that we can edit them both.
3068 for( int ii = 0; ii < poly_no_hole.OutlineCount(); ++ii )
3069 {
3070 SHAPE_POLY_SET poly_outline( poly_no_hole.COutline( ii ) );
3071 PCB_SHAPE* item = new PCB_SHAPE();
3072 item->SetShape( SHAPE_T::POLY );
3073 item->SetFilled( aFilled );
3074 item->SetPolyShape( poly_outline );
3075 item->SetStroke( STROKE_PARAMS( aThickness, LINE_STYLE::SOLID ) );
3076 item->SetParent( this );
3077 m_padStack.AddPrimitive( item, aLayer );
3078 }
3079
3080 SetDirty();
3081}
3082
3083
3084void PAD::AddPrimitivePoly( PCB_LAYER_ID aLayer, const std::vector<VECTOR2I>& aPoly, int aThickness,
3085 bool aFilled )
3086{
3087 PCB_SHAPE* item = new PCB_SHAPE( nullptr, SHAPE_T::POLY );
3088 item->SetFilled( aFilled );
3089 item->SetPolyPoints( aPoly );
3090 item->SetStroke( STROKE_PARAMS( aThickness, LINE_STYLE::SOLID ) );
3091 item->SetParent( this );
3092 m_padStack.AddPrimitive( item, aLayer );
3093 SetDirty();
3094}
3095
3096
3097void PAD::ReplacePrimitives( PCB_LAYER_ID aLayer, const std::vector<std::shared_ptr<PCB_SHAPE>>& aPrimitivesList )
3098{
3099 // clear old list
3100 DeletePrimitivesList( aLayer );
3101
3102 // Import to the given shape list
3103 if( aPrimitivesList.size() )
3104 AppendPrimitives( aLayer, aPrimitivesList );
3105
3106 SetDirty();
3107}
3108
3109
3110void PAD::AppendPrimitives( PCB_LAYER_ID aLayer, const std::vector<std::shared_ptr<PCB_SHAPE>>& aPrimitivesList )
3111{
3112 // Add duplicates of aPrimitivesList to the pad primitives list:
3113 for( const std::shared_ptr<PCB_SHAPE>& prim : aPrimitivesList )
3114 AddPrimitive( aLayer, new PCB_SHAPE( *prim ) );
3115
3116 SetDirty();
3117}
3118
3119
3120void PAD::AddPrimitive( PCB_LAYER_ID aLayer, PCB_SHAPE* aPrimitive )
3121{
3122 aPrimitive->SetParent( this );
3123 m_padStack.AddPrimitive( aPrimitive, aLayer );
3124
3125 SetDirty();
3126}
3127
3128
3130{
3131 if( aLayer == UNDEFINED_LAYER )
3132 {
3133 m_padStack.ForEachUniqueLayer(
3134 [&]( PCB_LAYER_ID l )
3135 {
3136 m_padStack.ClearPrimitives( l );
3137 } );
3138 }
3139 else
3140 {
3141 m_padStack.ClearPrimitives( aLayer);
3142 }
3143
3144 SetDirty();
3145}
3146
3147
3149 ERROR_LOC aErrorLoc ) const
3150{
3151 aMergedPolygon->RemoveAllContours();
3152
3153 // Add the anchor pad shape in aMergedPolygon, others in aux_polyset:
3154 // The anchor pad is always at 0,0
3155 VECTOR2I padSize = GetSize( aLayer );
3156
3157 switch( GetAnchorPadShape( aLayer ) )
3158 {
3160 {
3161 SHAPE_RECT rect( -padSize.x / 2, -padSize.y / 2, padSize.x, padSize.y );
3162 aMergedPolygon->AddOutline( rect.Outline() );
3163 break;
3164 }
3165
3166 default:
3167 case PAD_SHAPE::CIRCLE:
3168 TransformCircleToPolygon( *aMergedPolygon, VECTOR2I( 0, 0 ), padSize.x / 2, GetMaxError(), aErrorLoc );
3169 break;
3170 }
3171
3172 SHAPE_POLY_SET polyset;
3173
3174 for( const std::shared_ptr<PCB_SHAPE>& primitive : m_padStack.Primitives( aLayer ) )
3175 {
3176 if( !primitive->IsProxyItem() )
3177 primitive->TransformShapeToPolygon( polyset, UNDEFINED_LAYER, 0, GetMaxError(), aErrorLoc );
3178 }
3179
3180 polyset.Simplify();
3181
3182 // Merge all polygons with the initial pad anchor shape
3183 if( polyset.OutlineCount() )
3184 {
3185 aMergedPolygon->BooleanAdd( polyset );
3186 aMergedPolygon->Fracture();
3187 }
3188}
3189
3190
3191static struct PAD_DESC
3192{
3194 {
3196 .Map( PAD_ATTRIB::PTH, _HKI( "Through-hole" ) )
3197 .Map( PAD_ATTRIB::SMD, _HKI( "SMD" ) )
3198 .Map( PAD_ATTRIB::CONN, _HKI( "Edge connector" ) )
3199 .Map( PAD_ATTRIB::NPTH, _HKI( "NPTH, mechanical" ) );
3200
3202 .Map( PAD_SHAPE::CIRCLE, _HKI( "Circle" ) )
3203 .Map( PAD_SHAPE::RECTANGLE, _HKI( "Rectangle" ) )
3204 .Map( PAD_SHAPE::OVAL, _HKI( "Oval" ) )
3205 .Map( PAD_SHAPE::TRAPEZOID, _HKI( "Trapezoid" ) )
3206 .Map( PAD_SHAPE::ROUNDRECT, _HKI( "Rounded rectangle" ) )
3207 .Map( PAD_SHAPE::CHAMFERED_RECT, _HKI( "Chamfered rectangle" ) )
3208 .Map( PAD_SHAPE::CUSTOM, _HKI( "Custom" ) );
3209
3211 .Map( PAD_PROP::NONE, _HKI( "None" ) )
3212 .Map( PAD_PROP::BGA, _HKI( "BGA pad" ) )
3213 .Map( PAD_PROP::FIDUCIAL_GLBL, _HKI( "Fiducial, global to board" ) )
3214 .Map( PAD_PROP::FIDUCIAL_LOCAL, _HKI( "Fiducial, local to footprint" ) )
3215 .Map( PAD_PROP::TESTPOINT, _HKI( "Test point pad" ) )
3216 .Map( PAD_PROP::HEATSINK, _HKI( "Heatsink pad" ) )
3217 .Map( PAD_PROP::CASTELLATED, _HKI( "Castellated pad" ) )
3218 .Map( PAD_PROP::MECHANICAL, _HKI( "Mechanical pad" ) )
3219 .Map( PAD_PROP::PRESSFIT, _HKI( "Press-fit pad" ) );
3220
3222 .Map( PAD_DRILL_SHAPE::UNDEFINED, _HKI( "Undefined" ) )
3223 .Map( PAD_DRILL_SHAPE::CIRCLE, _HKI( "Round" ) )
3224 .Map( PAD_DRILL_SHAPE::OBLONG, _HKI( "Oblong" ) );
3225
3226 // Ensure post-machining mode enum choices are defined before properties use them
3227 {
3230
3231 if( pmMap.Choices().GetCount() == 0 )
3232 {
3237 }
3238 }
3239
3240 // Ensure backdrill mode enum choices are defined before properties use them
3241 {
3243
3244 if( bdMap.Choices().GetCount() == 0 )
3245 {
3247 .Map( BACKDRILL_MODE::NO_BACKDRILL, _HKI( "No backdrill" ) )
3248 .Map( BACKDRILL_MODE::BACKDRILL_BOTTOM, _HKI( "Backdrill bottom" ) )
3249 .Map( BACKDRILL_MODE::BACKDRILL_TOP, _HKI( "Backdrill top" ) )
3250 .Map( BACKDRILL_MODE::BACKDRILL_BOTH, _HKI( "Backdrill both" ) );
3251 }
3252 }
3253
3255
3256 if( zcMap.Choices().GetCount() == 0 )
3257 {
3259 zcMap.Map( ZONE_CONNECTION::INHERITED, _HKI( "Inherited" ) )
3260 .Map( ZONE_CONNECTION::NONE, _HKI( "None" ) )
3261 .Map( ZONE_CONNECTION::THERMAL, _HKI( "Thermal reliefs" ) )
3262 .Map( ZONE_CONNECTION::FULL, _HKI( "Solid" ) )
3263 .Map( ZONE_CONNECTION::THT_THERMAL, _HKI( "Thermal reliefs for PTH" ) );
3264 }
3265
3267 .Map( UNCONNECTED_LAYER_MODE::KEEP_ALL, _HKI( "All copper layers" ) )
3268 .Map( UNCONNECTED_LAYER_MODE::REMOVE_ALL, _HKI( "Connected layers only" ) )
3269 .Map( UNCONNECTED_LAYER_MODE::REMOVE_EXCEPT_START_AND_END, _HKI( "Front, back and connected layers" ) )
3270 .Map( UNCONNECTED_LAYER_MODE::START_END_ONLY, _HKI( "Start and end layers only" ) );
3271
3273 REGISTER_TYPE( PAD );
3278
3279 propMgr.Mask( TYPE_HASH( PAD ), TYPE_HASH( BOARD_CONNECTED_ITEM ), _HKI( "Layer" ) );
3280 propMgr.Mask( TYPE_HASH( PAD ), TYPE_HASH( BOARD_ITEM ), _HKI( "Locked" ) );
3281
3282 propMgr.AddProperty( new PROPERTY<PAD, double>( _HKI( "Orientation" ),
3284
3285 auto isCopperPad =
3286 []( INSPECTABLE* aItem ) -> bool
3287 {
3288 if( PAD* pad = dynamic_cast<PAD*>( aItem ) )
3289 return pad->GetAttribute() != PAD_ATTRIB::NPTH;
3290
3291 return false;
3292 };
3293
3294 auto padCanHaveHole =
3295 []( INSPECTABLE* aItem ) -> bool
3296 {
3297 if( PAD* pad = dynamic_cast<PAD*>( aItem ) )
3298 return pad->GetAttribute() == PAD_ATTRIB::PTH || pad->GetAttribute() == PAD_ATTRIB::NPTH;
3299
3300 return false;
3301 };
3302
3303 auto hasNormalPadstack =
3304 []( INSPECTABLE* aItem ) -> bool
3305 {
3306 if( PAD* pad = dynamic_cast<PAD*>( aItem ) )
3307 return pad->Padstack().Mode() == PADSTACK::MODE::NORMAL;
3308
3309 return true;
3310 };
3311
3313 isCopperPad );
3314 propMgr.OverrideAvailability( TYPE_HASH( PAD ), TYPE_HASH( BOARD_CONNECTED_ITEM ), _HKI( "Net Class" ),
3315 isCopperPad );
3316
3317 const wxString groupPad = _HKI( "Pad Properties" );
3318 const wxString groupPostMachining = _HKI( "Post-machining Properties" );
3319 const wxString groupBackdrill = _HKI( "Backdrill Properties" );
3320
3321 propMgr.AddProperty( new PROPERTY_ENUM<PAD, PAD_ATTRIB>( _HKI( "Pad Type" ),
3323 groupPad );
3324
3325 propMgr.AddProperty( new PROPERTY_ENUM<PAD, PAD_SHAPE>( _HKI( "Pad Shape" ),
3327 groupPad )
3328 .SetAvailableFunc( hasNormalPadstack );
3329
3330 propMgr.AddProperty( new PROPERTY<PAD, wxString>( _HKI( "Pad Number" ),
3332 groupPad )
3333 .SetAvailableFunc( isCopperPad );
3334
3335 propMgr.AddProperty( new PROPERTY<PAD, wxString>( _HKI( "Pin Name" ),
3337 groupPad )
3339 propMgr.AddProperty( new PROPERTY<PAD, wxString>( _HKI( "Pin Type" ),
3341 groupPad )
3343 .SetChoicesFunc( []( INSPECTABLE* aItem )
3344 {
3345 wxPGChoices choices;
3346
3347 for( int ii = 0; ii < ELECTRICAL_PINTYPES_TOTAL; ii++ )
3349
3350 return choices;
3351 } );
3352
3353 propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Size X" ),
3355 groupPad )
3356 .SetAvailableFunc( hasNormalPadstack );
3357 propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Size Y" ),
3359 groupPad )
3360 .SetAvailableFunc( []( INSPECTABLE* aItem ) -> bool
3361 {
3362 if( PAD* pad = dynamic_cast<PAD*>( aItem ) )
3363 {
3364 // Custom padstacks can't have size modified through panel
3365 if( pad->Padstack().Mode() != PADSTACK::MODE::NORMAL )
3366 return false;
3367
3368 // Circle pads have no usable y-size
3369 return pad->GetShape( PADSTACK::ALL_LAYERS ) != PAD_SHAPE::CIRCLE;
3370 }
3371
3372 return true;
3373 } );
3374
3375 const auto hasRoundRadius =
3376 []( INSPECTABLE* aItem ) -> bool
3377 {
3378 if( PAD* pad = dynamic_cast<PAD*>( aItem ) )
3379 {
3380 // Custom padstacks can't have this property modified through panel
3381 if( pad->Padstack().Mode() != PADSTACK::MODE::NORMAL )
3382 return false;
3383
3385 }
3386
3387 return false;
3388 };
3389
3390 propMgr.AddProperty( new PROPERTY<PAD, double>( _HKI( "Corner Radius Ratio" ),
3392 groupPad )
3393 .SetAvailableFunc( hasRoundRadius );
3394
3395 propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Corner Radius Size" ),
3397 groupPad )
3398 .SetAvailableFunc( hasRoundRadius );
3399
3400 propMgr.AddProperty( new PROPERTY_ENUM<PAD, PAD_DRILL_SHAPE>( _HKI( "Hole Shape" ),
3401 &PAD::SetDrillShape, &PAD::GetDrillShape ), groupPad )
3402 .SetWriteableFunc( padCanHaveHole );
3403
3404 propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Hole Size X" ),
3406 groupPad )
3407 .SetWriteableFunc( padCanHaveHole )
3409
3410 propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Hole Size Y" ),
3412 groupPad )
3413 .SetWriteableFunc( padCanHaveHole )
3415 .SetAvailableFunc( []( INSPECTABLE* aItem ) -> bool
3416 {
3417 // Circle holes have no usable y-size
3418 if( PAD* pad = dynamic_cast<PAD*>( aItem ) )
3419 return pad->GetDrillShape() != PAD_DRILL_SHAPE::CIRCLE;
3420
3421 return true;
3422 } );
3423
3424 propMgr.AddProperty( new PROPERTY_ENUM<PAD, PAD_DRILL_POST_MACHINING_MODE>( _HKI( "Top Post-machining" ),
3426 groupPostMachining )
3427 .SetWriteableFunc( padCanHaveHole )
3428 .SetAvailableFunc( []( INSPECTABLE* aItem ) {
3429 if( PAD* pad = dynamic_cast<PAD*>( aItem ) )
3430 {
3431 return pad->GetDrillShape() == PAD_DRILL_SHAPE::CIRCLE;
3432 }
3433
3434 return false;
3435 } );
3436
3437 propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Top Post-machining Size" ),
3439 groupPostMachining )
3440 .SetWriteableFunc( padCanHaveHole )
3441 .SetAvailableFunc( []( INSPECTABLE* aItem ) {
3442 if( PAD* pad = dynamic_cast<PAD*>( aItem ) )
3443 {
3444 if( pad->GetDrillShape() != PAD_DRILL_SHAPE::CIRCLE )
3445 return false;
3446
3447 auto mode = pad->GetFrontPostMachining();
3450 }
3451
3452 return false;
3453 } );
3454
3455 propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Top Counterbore Depth" ),
3457 groupPostMachining )
3458 .SetWriteableFunc( padCanHaveHole )
3459 .SetAvailableFunc( []( INSPECTABLE* aItem ) {
3460 if( PAD* pad = dynamic_cast<PAD*>( aItem ) )
3461 {
3462 if( pad->GetDrillShape() != PAD_DRILL_SHAPE::CIRCLE )
3463 return false;
3464
3465 auto mode = pad->GetFrontPostMachining();
3467 }
3468
3469 return false;
3470 } );
3471
3472 propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Top Countersink Angle" ),
3474 groupPostMachining )
3475 .SetWriteableFunc( padCanHaveHole )
3476 .SetAvailableFunc( []( INSPECTABLE* aItem ) {
3477 if( PAD* pad = dynamic_cast<PAD*>( aItem ) )
3478 {
3479 if( pad->GetDrillShape() != PAD_DRILL_SHAPE::CIRCLE )
3480 return false;
3481
3482 auto mode = pad->GetFrontPostMachining();
3484 }
3485
3486 return false;
3487 } );
3488
3489 propMgr.AddProperty( new PROPERTY_ENUM<PAD, PAD_DRILL_POST_MACHINING_MODE>( _HKI( "Bottom Post-machining" ),
3491 groupPostMachining )
3492 .SetWriteableFunc( padCanHaveHole )
3493 .SetAvailableFunc( []( INSPECTABLE* aItem ) {
3494 if( PAD* pad = dynamic_cast<PAD*>( aItem ) )
3495 {
3496 return pad->GetDrillShape() == PAD_DRILL_SHAPE::CIRCLE;
3497 }
3498
3499 return false;
3500 } );
3501
3502 propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Bottom Post-machining Size" ),
3504 groupPostMachining )
3505 .SetWriteableFunc( padCanHaveHole )
3506 .SetAvailableFunc( []( INSPECTABLE* aItem ) {
3507 if( PAD* pad = dynamic_cast<PAD*>( aItem ) )
3508 {
3509 if( pad->GetDrillShape() != PAD_DRILL_SHAPE::CIRCLE )
3510 return false;
3511
3512 auto mode = pad->GetBackPostMachining();
3515 }
3516
3517 return false;
3518 } );
3519
3520 propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Bottom Counterbore Depth" ),
3522 groupPostMachining )
3523 .SetWriteableFunc( padCanHaveHole )
3524 .SetAvailableFunc( []( INSPECTABLE* aItem ) {
3525 if( PAD* pad = dynamic_cast<PAD*>( aItem ) )
3526 {
3527 if( pad->GetDrillShape() != PAD_DRILL_SHAPE::CIRCLE )
3528 return false;
3529
3530 auto mode = pad->GetBackPostMachining();
3532 }
3533
3534 return false;
3535 } );
3536
3537 propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Bottom Countersink Angle" ),
3539 groupPostMachining )
3540 .SetWriteableFunc( padCanHaveHole )
3541 .SetAvailableFunc( []( INSPECTABLE* aItem ) {
3542 if( PAD* pad = dynamic_cast<PAD*>( aItem ) )
3543 {
3544 if( pad->GetDrillShape() != PAD_DRILL_SHAPE::CIRCLE )
3545 return false;
3546
3547 auto mode = pad->GetBackPostMachining();
3549 }
3550
3551 return false;
3552 } );
3553
3554 propMgr.AddProperty( new PROPERTY_ENUM<PAD, BACKDRILL_MODE>( _HKI( "Backdrill Mode" ),
3555 &PAD::SetBackdrillMode, &PAD::GetBackdrillMode ), groupBackdrill );
3556
3557 propMgr.AddProperty( new PROPERTY<PAD, std::optional<int>>( _HKI( "Bottom Backdrill Size" ),
3559 groupBackdrill )
3560 .SetAvailableFunc( []( INSPECTABLE* aItem ) -> bool
3561 {
3562 if( PAD* pad = dynamic_cast<PAD*>( aItem ) )
3563 {
3564 if( pad->GetDrillShape() != PAD_DRILL_SHAPE::CIRCLE )
3565 return false;
3566
3567 auto mode = pad->GetBackdrillMode();
3569 }
3570
3571 return false;
3572 } );
3573
3574 propMgr.AddProperty( new PROPERTY_ENUM<PAD, PCB_LAYER_ID>( _HKI( "Bottom Backdrill Must-Cut" ),
3576 groupBackdrill )
3577 .SetAvailableFunc( []( INSPECTABLE* aItem ) -> bool
3578 {
3579 if( PAD* pad = dynamic_cast<PAD*>( aItem ) )
3580 {
3581 if( pad->GetDrillShape() != PAD_DRILL_SHAPE::CIRCLE )
3582 return false;
3583
3584 auto mode = pad->GetBackdrillMode();
3586 }
3587
3588 return false;
3589 } );
3590
3591 propMgr.AddProperty( new PROPERTY<PAD, std::optional<int>>( _HKI( "Top Backdrill Size" ),
3593 groupBackdrill )
3594 .SetAvailableFunc( []( INSPECTABLE* aItem ) -> bool
3595 {
3596 if( PAD* pad = dynamic_cast<PAD*>( aItem ) )
3597 {
3598 if( pad->GetDrillShape() != PAD_DRILL_SHAPE::CIRCLE )
3599 return false;
3600
3601 auto mode = pad->GetBackdrillMode();
3603 }
3604
3605 return false;
3606 } );
3607
3608 propMgr.AddProperty( new PROPERTY_ENUM<PAD, PCB_LAYER_ID>( _HKI( "Top Backdrill Must-Cut" ),
3610 groupBackdrill )
3611 .SetAvailableFunc( []( INSPECTABLE* aItem ) -> bool
3612 {
3613 if( PAD* pad = dynamic_cast<PAD*>( aItem ) )
3614 {
3615 if( pad->GetDrillShape() != PAD_DRILL_SHAPE::CIRCLE )
3616 return false;
3617
3618 auto mode = pad->GetBackdrillMode();
3620 }
3621
3622 return false;
3623 } );
3624
3625
3626 propMgr.AddProperty( new PROPERTY_ENUM<PAD, PAD_PROP>( _HKI( "Fabrication Property" ),
3628 groupPad );
3629
3630 propMgr.AddProperty( new PROPERTY_ENUM<PAD, UNCONNECTED_LAYER_MODE>( _HKI( "Copper Layers" ),
3632 groupPad );
3633
3634 propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Pad To Die Length" ),
3636 groupPad )
3637 .SetAvailableFunc( isCopperPad );
3638
3639 propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Pad To Die Delay" ),
3641 groupPad )
3642 .SetAvailableFunc( isCopperPad );
3643
3644 const wxString groupOverrides = _HKI( "Overrides" );
3645
3646 propMgr.AddProperty( new PROPERTY<PAD, std::optional<int>>( _HKI( "Clearance Override" ),
3648 groupOverrides );
3649
3650 propMgr.AddProperty( new PROPERTY<PAD, std::optional<int>>( _HKI( "Soldermask Margin Override" ),
3652 groupOverrides );
3653
3654 propMgr.AddProperty( new PROPERTY<PAD, std::optional<int>>( _HKI( "Solderpaste Margin Override" ),
3656 groupOverrides );
3657
3658 propMgr.AddProperty( new PROPERTY<PAD, std::optional<double>>( _HKI( "Solderpaste Margin Ratio Override" ),
3661 groupOverrides );
3662
3663 propMgr.AddProperty( new PROPERTY_ENUM<PAD, ZONE_CONNECTION>( _HKI( "Zone Connection Style" ),
3665 groupOverrides );
3666
3667 constexpr int minZoneWidth = pcbIUScale.mmToIU( ZONE_THICKNESS_MIN_VALUE_MM );
3668
3669 propMgr.AddProperty( new PROPERTY<PAD, std::optional<int>>( _HKI( "Thermal Relief Spoke Width" ),
3672 groupOverrides )
3674
3675 propMgr.AddProperty( new PROPERTY<PAD, double>( _HKI( "Thermal Relief Spoke Angle" ),
3678 groupOverrides );
3679
3680 propMgr.AddProperty( new PROPERTY<PAD, std::optional<int>>( _HKI( "Thermal Relief Gap" ),
3683 groupOverrides )
3685
3686 // TODO delta, drill shape offset, layer set
3687 }
3689
types::KiCadObjectType ToProtoEnum(KICAD_T aValue)
Definition api_enums.cpp:97
KICAD_T FromProtoEnum(types::KiCadObjectType aValue)
Definition api_enums.cpp:36
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
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:112
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
BITMAPS
A list of all bitmap identifiers.
@ FPHOLDER
Definition board.h:314
ZONE_LAYER_OVERRIDE
Conditionally flashed vias and pads that interact with zones of different priority can be very squirr...
Definition board_item.h:71
@ ZLO_NONE
Definition board_item.h:72
@ ZLO_FORCE_FLASHED
Definition board_item.h:73
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
BASE_SET & set(size_t pos)
Definition base_set.h:116
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
virtual NETCLASS * GetEffectiveNetClass() const
Return the NETCLASS for this item.
bool SetNetCode(int aNetCode, bool aNoAssert)
Set net using a net code.
BOARD_CONNECTED_ITEM(BOARD_ITEM *aParent, KICAD_T idtype)
void PackNet(kiapi::board::types::Net *aProto) const
TEARDROP_PARAMETERS m_teardropParams
Not all BOARD_CONNECTED_ITEMs support teardrops, but we want those that do to share a single section ...
const wxString & GetShortNetname() const
void UnpackNet(const kiapi::board::types::Net &aProto)
Assigns a net to this item from an API message.
Container for design settings for a BOARD object.
std::shared_ptr< DRC_ENGINE > m_DRCEngine
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:83
BOARD_ITEM(BOARD_ITEM *aParent, KICAD_T idtype, PCB_LAYER_ID aLayer=F_Cu)
Definition board_item.h:85
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition board_item.h:236
void SetLocked(bool aLocked) override
Definition board_item.h:327
PCB_LAYER_ID m_layer
Definition board_item.h:458
bool IsLocked() const override
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition board_item.h:284
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
FOOTPRINT * GetParentFootprint() const
virtual wxString LayerMaskDescribe() const
Return a string (to be shown to the user) describing a layer mask.
BOARD_ITEM_CONTAINER * GetParent() const
Definition board_item.h:214
virtual int BoardCopperLayerCount() const
Return the total number of copper layers for the board that this item resides on.
int GetMaxError() const
Manage layers needed to make a physical board.
int GetLayerDistance(PCB_LAYER_ID aFirstLayer, PCB_LAYER_ID aSecondLayer) const
Calculate the distance (height) between the two given copper layers.
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:322
int GetMaxClearanceValue() const
Returns the maximum clearance value for any object on the board.
Definition board.cpp:1081
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1069
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition box2.h:558
constexpr BOX2< Vec > & Normalize()
Ensure that the height and width are positive.
Definition box2.h:146
constexpr size_type GetWidth() const
Definition box2.h:214
constexpr size_type GetHeight() const
Definition box2.h:215
constexpr bool Contains(const Vec &aPoint) const
Definition box2.h:168
constexpr const Vec & GetOrigin() const
Definition box2.h:210
constexpr const SizeVec & GetSize() const
Definition box2.h:206
constexpr bool Intersects(const BOX2< Vec > &aRect) const
Definition box2.h:311
wxString GetName() const
Definition drc_rule.h:194
MINOPTMAX< int > & Value()
Definition drc_rule.h:187
MINOPTMAX< int > m_Value
Definition drc_rule.h:228
DRC_CONSTRAINT EvalRules(DRC_CONSTRAINT_T aConstraintType, const BOARD_ITEM *a, const BOARD_ITEM *b, PCB_LAYER_ID aLayer, REPORTER *aReporter=nullptr)
double AsDegrees() const
Definition eda_angle.h:116
bool IsZero() const
Definition eda_angle.h:136
EDA_ANGLE Normalize180()
Definition eda_angle.h:268
The base class for create windows for drawing purpose.
A set of EDA_ITEMs (i.e., without duplicates).
Definition eda_group.h:46
virtual VECTOR2I GetPosition() const
Definition eda_item.h:272
EDA_ITEM & operator=(const EDA_ITEM &aItem)
Assign the members of aItem to another object.
Definition eda_item.cpp:328
const KIID m_Uuid
Definition eda_item.h:516
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition eda_item.h:144
virtual void SetParent(EDA_ITEM *aParent)
Definition eda_item.h:113
EDA_ITEM * m_parent
Owner.
Definition eda_item.h:528
EDA_ITEM_FLAGS GetFlags() const
Definition eda_item.h:145
EDA_ITEM(EDA_ITEM *parent, KICAD_T idType, bool isSCH_ITEM=false, bool isBOARD_ITEM=false)
Definition eda_item.cpp:39
void SetPolyShape(const SHAPE_POLY_SET &aShape)
Definition eda_shape.h:344
virtual void SetFilled(bool aFlag)
Definition eda_shape.h:136
void SetShape(SHAPE_T aShape)
Definition eda_shape.h:167
bool IsAnyFill() const
Definition eda_shape.h:112
void SetPolyPoints(const std::vector< VECTOR2I > &aPoints)
void SetFillMode(FILL_T aFill)
ENUM_MAP & Map(T aValue, const wxString &aName)
Definition property.h:727
static ENUM_MAP< T > & Instance()
Definition property.h:721
ENUM_MAP & Undefined(T aValue)
Definition property.h:734
wxPGChoices & Choices()
Definition property.h:770
EDA_ANGLE GetOrientation() const
Definition footprint.h:248
std::map< wxString, int > MapPadNumbersToNetTieGroups() const
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition footprint.h:257
bool IsNetTie() const
Definition footprint.h:340
const wxString & GetReference() const
Definition footprint.h:661
DRAWINGS & GraphicalItems()
Definition footprint.h:227
Class that other classes need to inherit from, in order to be inspectable.
Definition inspectable.h:37
Contains methods for drawing PCB-specific items.
virtual PCB_RENDER_SETTINGS * GetSettings() override
Return a pointer to current settings that are going to be used when drawing items.
PCB specific render settings.
Definition pcb_painter.h:82
PCB_LAYER_ID GetPrimaryHighContrastLayer() const
Return the board layer which is in high-contrast mode.
static double lodScaleForThreshold(const KIGFX::VIEW *aView, int aWhatIu, int aThresholdIu)
Get the scale at which aWhatIu would be drawn at the same size as aThresholdIu on screen.
Definition view_item.cpp:39
static constexpr double LOD_HIDE
Return this constant from ViewGetLOD() to hide the item unconditionally.
Definition view_item.h:180
static constexpr double LOD_SHOW
Return this constant from ViewGetLOD() to show the item unconditionally.
Definition view_item.h:185
Hold a (potentially large) number of VIEW_ITEMs and renders them on a graphics device provided by the...
Definition view.h:66
bool IsLayerVisible(int aLayer) const
Return information about visibility of a particular layer.
Definition view.h:422
PAINTER * GetPainter() const
Return the painter object used by the view for drawing #VIEW_ITEMS.
Definition view.h:220
Definition kiid.h:49
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static const LSET & FrontBoardTechMask()
Return a mask holding technical layers used in a board fabrication (no CU layer) on front side.
Definition lset.cpp:652
LSEQ UIOrder() const
Return the copper, technical and user layers in the order shown in layer widget.
Definition lset.cpp:726
LSEQ Seq(const LSEQ &aSequence) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition lset.cpp:296
static LSET AllCuMask(int aCuLayerCount)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition lset.cpp:582
static LSET AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Definition lset.cpp:591
static const LSET & PhysicalLayersMask()
Return a mask holding all layers which are physically realized.
Definition lset.cpp:680
static const LSET & BackBoardTechMask()
Return a mask holding technical layers used in a board fabrication (no CU layer) on Back side.
Definition lset.cpp:638
static const LSET & InternalCuMask()
Return a complete set of internal copper layers which is all Cu layers except F_Cu and B_Cu.
Definition lset.cpp:560
T Min() const
Definition minoptmax.h:33
bool HasMin() const
Definition minoptmax.h:37
T Opt() const
Definition minoptmax.h:35
bool HasOpt() const
Definition minoptmax.h:39
static const int UNCONNECTED
Constant that holds the "unconnected net" number (typically 0) all items "connected" to this net are ...
Definition netinfo.h:247
double Similarity(const PADSTACK &aOther) const
Return a measure of how likely the other object is to represent the same object.
void ForEachUniqueLayer(const std::function< void(PCB_LAYER_ID)> &aMethod) const
Runs the given callable for each active unique copper layer in this padstack, meaning F_Cu for MODE::...
PCB_LAYER_ID EffectiveLayerFor(PCB_LAYER_ID aLayer) const
Determines which geometry layer should be used for the given input layer.
POST_MACHINING_PROPS & FrontPostMachining()
Definition padstack.h:344
static int Compare(const PADSTACK *aPadstackRef, const PADSTACK *aPadstackCmp)
Compare two padstacks and return 0 if they are equal.
@ NORMAL
Shape is the same on all layers.
Definition padstack.h:171
DRILL_PROPS & SecondaryDrill()
Definition padstack.h:338
POST_MACHINING_PROPS & BackPostMachining()
Definition padstack.h:347
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
Definition padstack.h:177
std::vector< std::shared_ptr< PCB_SHAPE > > & Primitives(PCB_LAYER_ID aLayer)
Definition pad.h:55
void SetFrontPostMachiningSize(int aSize)
Definition pad.h:452
int GetBackPostMachiningSize() const
Definition pad.h:473
void SetAnchorPadShape(PCB_LAYER_ID aLayer, PAD_SHAPE aShape)
Set the shape of the anchor pad for custom shaped pads.
Definition pad.h:243
bool IsAperturePad() const
Definition pad.h:565
void SetAttribute(PAD_ATTRIB aAttribute)
Definition pad.cpp:1318
int GetOwnClearance(PCB_LAYER_ID aLayer, wxString *aSource=nullptr) const override
Return the pad's "own" clearance in internal units.
Definition pad.cpp:1586
void CheckPad(UNITS_PROVIDER *aUnitsProvider, bool aForPadProperties, const std::function< void(int aErrorCode, const wxString &aMsg)> &aErrorHandler) const
Definition pad.cpp:2734
PAD(FOOTPRINT *parent)
Definition pad.cpp:76
virtual void swapData(BOARD_ITEM *aImage) override
Definition pad.cpp:2456
PAD_PROP GetProperty() const
Definition pad.h:561
void SetFrontPostMachiningAngle(int aAngle)
Definition pad.h:456
void SetPrimaryDrillFilledFlag(bool aFilled)
Definition pad.cpp:734
void Serialize(google::protobuf::Any &aContainer) const override
Serializes this object to the given Any message.
Definition pad.cpp:183
double GetFrontRoundRectRadiusRatio() const
Definition pad.h:796
void doCheckPad(PCB_LAYER_ID aLayer, UNITS_PROVIDER *aUnitsProvider, bool aForPadProperties, const std::function< void(int aErrorCode, const wxString &aMsg)> &aErrorHandler) const
Definition pad.cpp:2849
static wxString ShowPadShape(PAD_SHAPE aShape)
Definition pad.cpp:2055
std::optional< int > GetClearanceOverrides(wxString *aSource) const override
Return any clearance overrides set in the "classic" (ie: pre-rule) system.
Definition pad.cpp:1574
void SetPinType(const wxString &aType)
Set the pad electrical type.
Definition pad.h:153
LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition pad.h:555
const std::vector< std::shared_ptr< PCB_SHAPE > > & GetPrimitives(PCB_LAYER_ID aLayer) const
Accessor to the basic shape list for custom-shaped pads.
Definition pad.h:372
const ZONE_LAYER_OVERRIDE & GetZoneLayerOverride(PCB_LAYER_ID aLayer) const
Definition pad.cpp:269
int GetSizeX() const
Definition pad.h:280
void MergePrimitivesAsPolygon(PCB_LAYER_ID aLayer, SHAPE_POLY_SET *aMergedPolygon, ERROR_LOC aErrorLoc=ERROR_INSIDE) const
Merge all basic shapes to a SHAPE_POLY_SET.
Definition pad.cpp:3148
int GetRoundRectCornerRadius(PCB_LAYER_ID aLayer) const
Definition pad.cpp:861
bool FlashLayer(int aLayer, bool aOnlyCheckIfPermitted=false) const
Check to see whether the pad should be flashed on the specific layer.
Definition pad.cpp:410
void SetLocalThermalGapOverride(const std::optional< int > &aOverride)
Definition pad.h:767
void SetPrimaryDrillSize(const VECTOR2I &aSize)
Definition pad.cpp:493
virtual std::shared_ptr< SHAPE > GetEffectiveShape(PCB_LAYER_ID aLayer, FLASHING flashPTHPads=FLASHING::DEFAULT) const override
Some pad shapes can be complex (rounded/chamfered rectangle), even without considering custom shapes.
Definition pad.cpp:931
void GetMsgPanelInfo(EDA_DRAW_FRAME *aFrame, std::vector< MSG_PANEL_ITEM > &aList) override
Populate aList of MSG_PANEL_ITEM objects with it's internal state for display purposes.
Definition pad.cpp:1777
const BOX2I GetBoundingBox() const override
The bounding box is cached, so this will be efficient most of the time.
Definition pad.cpp:1302
void SetTertiaryDrillStartLayer(PCB_LAYER_ID aLayer)
Definition pad.cpp:847
bool IsOnLayer(PCB_LAYER_ID aLayer) const override
Test to see if this object is on the given layer.
Definition pad.h:901
int GetDrillSizeY() const
Definition pad.h:316
void AddPrimitivePoly(PCB_LAYER_ID aLayer, const SHAPE_POLY_SET &aPoly, int aThickness, bool aFilled)
Has meaning only for custom shape pads.
Definition pad.cpp:3056
std::optional< double > GetLocalSolderPasteMarginRatio() const
Definition pad.h:593
void SetFrontPostMachiningDepth(int aDepth)
Definition pad.h:454
void SetFrontShape(PAD_SHAPE aShape)
Definition pad.cpp:1366
void SetTopBackdrillLayer(PCB_LAYER_ID aLayer)
Definition pad.h:1049
const wxString & GetPinType() const
Definition pad.h:154
void SetZoneLayerOverride(PCB_LAYER_ID aLayer, ZONE_LAYER_OVERRIDE aOverride)
Definition pad.cpp:279
void SetSecondaryDrillSize(const VECTOR2I &aSize)
Definition pad.cpp:755
void SetPrimaryDrillFilled(const std::optional< bool > &aFilled)
Definition pad.cpp:727
const VECTOR2I & GetDrillSize() const
Definition pad.h:312
PAD_ATTRIB GetAttribute() const
Definition pad.h:558
static LSET PTHMask()
layer set for a through hole pad
Definition pad.cpp:339
static int Compare(const PAD *aPadRef, const PAD *aPadCmp)
Compare two pads and return 0 if they are equal.
Definition pad.cpp:2035
const wxString & GetPinFunction() const
Definition pad.h:148
std::mutex m_shapesBuildingLock
Definition pad.h:1081
bool CanHaveNumber() const
Indicates whether or not the pad can have a number.
Definition pad.cpp:286
void SetThermalSpokeAngle(const EDA_ANGLE &aAngle)
The orientation of the thermal spokes.
Definition pad.h:739
wxString m_pinType
Definition pad.h:1072
std::optional< int > GetBottomBackdrillSize() const
Definition pad.h:1039
bool Deserialize(const google::protobuf::Any &aContainer) override
Deserializes the given protobuf message into this object.
Definition pad.cpp:218
const wxString & GetNumber() const
Definition pad.h:137
double ViewGetLOD(int aLayer, const KIGFX::VIEW *aView) const override
Return the level of detail (LOD) of the item.
Definition pad.cpp:2288
const VECTOR2I & GetDelta(PCB_LAYER_ID aLayer) const
Definition pad.h:299
void SetSecondaryDrillSizeX(int aX)
Definition pad.cpp:762
void SetFrontRoundRectRadiusRatio(double aRadiusScale)
Definition pad.cpp:881
void SetPrimaryDrillSizeX(int aX)
Definition pad.cpp:500
void BuildEffectiveShapes() const
Rebuild the effective shape cache (and bounding box and radius) for the pad and clears the dirty bit.
Definition pad.cpp:1051
void SetPrimaryDrillEndLayer(PCB_LAYER_ID aLayer)
Definition pad.cpp:549
PAD_SHAPE GetFrontShape() const
Definition pad.h:201
void SetFrontPostMachiningMode(PAD_DRILL_POST_MACHINING_MODE aMode)
Definition pad.h:442
void CopyFrom(const BOARD_ITEM *aOther) override
Definition pad.cpp:148
void SetLocalSolderPasteMarginRatio(std::optional< double > aRatio)
Definition pad.h:597
PAD & operator=(const PAD &aOther)
Definition pad.cpp:130
void SetLocalThermalSpokeWidthOverride(std::optional< int > aWidth)
Set the width of the thermal spokes connecting the pad to a zone.
Definition pad.h:723
void SetShape(PCB_LAYER_ID aLayer, PAD_SHAPE aShape)
Set the new shape of this pad.
Definition pad.h:187
void SetSecondaryDrillStartLayer(PCB_LAYER_ID aLayer)
Definition pad.cpp:794
std::shared_ptr< SHAPE_SEGMENT > m_effectiveHoleShape
Definition pad.h:1084
bool IsLocked() const override
Definition pad.cpp:300
wxString ShowLegacyPadShape(PCB_LAYER_ID aLayer) const
An older version still used by place file writer and SWIG interface.
Definition pad.cpp:2077
VECTOR2I GetPosition() const override
Definition pad.h:209
void SetProperty(PAD_PROP aProperty)
Definition pad.cpp:1385
void SetThermalSpokeAngleDegrees(double aAngle)
Definition pad.h:749
void SetPrimaryDrillSizeY(int aY)
Definition pad.cpp:517
EDA_ANGLE GetThermalSpokeAngle() const
Definition pad.h:743
std::map< PCB_LAYER_ID, ZONE_LAYER_OVERRIDE > m_zoneLayerOverrides
Definition pad.h:1106
PAD_ATTRIB m_attribute
Definition pad.h:1098
void Flip(const VECTOR2I &VECTOR2I, FLIP_DIRECTION aFlipDirection) override
Flip this object, i.e.
Definition pad.cpp:1418
void SetBackPostMachiningSize(int aSize)
Definition pad.h:472
std::vector< PCB_SHAPE * > Recombine(bool aIsDryRun, int aMaxError)
Recombines the pad with other graphical shapes in the footprint.
Definition pad.cpp:2588
PCB_LAYER_ID GetPrincipalLayer() const
Definition pad.cpp:388
void ClearTertiaryDrillSize()
Definition pad.cpp:833
void SetDirty()
Definition pad.h:547
PAD_DRILL_SHAPE GetTertiaryDrillShape() const
Definition pad.h:535
static LSET UnplatedHoleMask()
layer set for a mechanical unplated through hole pad
Definition pad.cpp:360
void SetBottomBackdrillLayer(PCB_LAYER_ID aLayer)
Definition pad.h:1043
EDA_ITEM * Clone() const override
Create a duplicate of this item with linked list members set to NULL.
Definition pad.cpp:2196
void SetTertiaryDrillShape(PAD_DRILL_SHAPE aShape)
Definition pad.cpp:840
double GetOrientationDegrees() const
Definition pad.h:423
void SetBackdrillMode(BACKDRILL_MODE aMode)
Definition pad.h:1037
void Rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle) override
Rotate this object.
Definition pad.cpp:2046
int GetBackPostMachiningAngle() const
Definition pad.h:477
PADSTACK m_padStack
Definition pad.h:1076
void SetPadToDieDelay(int aDelay)
Definition pad.h:573
void FlipPrimitives(FLIP_DIRECTION aFlipDirection)
Flip (mirror) the primitives left to right or top to bottom, around the anchor position in custom pad...
Definition pad.cpp:1485
LAYER_SHAPE_MAP m_effectiveShapes
Definition pad.h:1083
bool IsNoConnectPad() const
Definition pad.cpp:326
int GetDrillSizeX() const
Definition pad.h:314
PAD_PROP m_property
Definition pad.h:1100
double GetRoundRectRadiusRatio(PCB_LAYER_ID aLayer) const
Definition pad.h:789
int GetFrontPostMachiningSize() const
Definition pad.h:453
void SetTertiaryDrillSizeX(int aX)
Definition pad.cpp:815
void DeletePrimitivesList(PCB_LAYER_ID aLayer=UNDEFINED_LAYER)
Clear the basic shapes list.
Definition pad.cpp:3129
void SetUnconnectedLayerMode(UNCONNECTED_LAYER_MODE aMode)
Definition pad.h:871
PAD_SHAPE GetShape(PCB_LAYER_ID aLayer) const
Definition pad.h:196
void TransformShapeToPolygon(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aMaxError, ERROR_LOC aErrorLoc=ERROR_INSIDE, bool ignoreLineWidth=false) const override
Convert the pad shape to a closed polygon.
Definition pad.cpp:2481
void SetNumber(const wxString &aNumber)
Set the pad number (note that it can be alphanumeric, such as the array reference "AA12").
Definition pad.h:136
BACKDRILL_MODE GetBackdrillMode() const
Definition pad.h:1036
void SetTertiaryDrillSize(const VECTOR2I &aSize)
Definition pad.cpp:808
void SetFrontRoundRectRadiusSize(int aRadius)
Definition pad.cpp:891
wxString ShowPadAttr() const
Definition pad.cpp:2093
wxString m_pinFunction
Definition pad.h:1071
void SetSecondaryDrillEndLayer(PCB_LAYER_ID aLayer)
Definition pad.cpp:801
void AddPrimitive(PCB_LAYER_ID aLayer, PCB_SHAPE *aPrimitive)
Add item to the custom shape primitives list.
Definition pad.cpp:3120
int GetFrontPostMachiningDepth() const
Definition pad.h:455
void SetDrillShape(PAD_DRILL_SHAPE aShape)
Definition pad.h:431
int m_effectiveBoundingRadius
Definition pad.h:1090
void SetLocalSolderMaskMargin(std::optional< int > aMargin)
Definition pad.h:580
void SetBackPostMachiningMode(PAD_DRILL_POST_MACHINING_MODE aMode)
Definition pad.h:462
void SetCustomShapeInZoneOpt(CUSTOM_SHAPE_ZONE_MODE aOption)
Set the option for the custom pad shape to use as clearance area in copper zones.
Definition pad.h:232
void SetLocalZoneConnection(ZONE_CONNECTION aType)
Definition pad.h:603
void SetChamferRectRatio(PCB_LAYER_ID aLayer, double aChamferScale)
Has meaning only for chamfered rectangular pads.
Definition pad.cpp:911
void SetPrimaryDrillCappedFlag(bool aCapped)
Definition pad.cpp:748
int GetSolderMaskExpansion(PCB_LAYER_ID aLayer) const
Definition pad.cpp:1612
int GetPadToDieDelay() const
Definition pad.h:574
std::optional< int > GetLocalClearance() const override
Return any local clearances set in the "classic" (ie: pre-rule) system.
Definition pad.h:576
void ImportSettingsFrom(const PAD &aMasterPad)
Import the pad settings from aMasterPad.
Definition pad.cpp:2384
double Similarity(const BOARD_ITEM &aOther) const override
Return a measure of how likely the other object is to represent the same object.
Definition pad.cpp:3032
bool IsOnCopperLayer() const override
Definition pad.cpp:1518
void SetTertiaryDrillEndLayer(PCB_LAYER_ID aLayer)
Definition pad.cpp:854
void SetPadstack(const PADSTACK &aPadstack)
Definition pad.h:330
void SetPosition(const VECTOR2I &aPos) override
Definition pad.h:203
const SHAPE_COMPOUND & buildEffectiveShape(PCB_LAYER_ID aLayer) const
Definition pad.cpp:1097
void SetPrimaryDrillShape(PAD_DRILL_SHAPE aShape)
Definition pad.cpp:530
const PADSTACK & Padstack() const
Definition pad.h:328
const VECTOR2I & GetOffset(PCB_LAYER_ID aLayer) const
Definition pad.h:324
VECTOR2I m_pos
Definition pad.h:1074
double m_lastGalZoomLevel
Definition pad.h:1093
PAD_DRILL_SHAPE GetSecondaryDrillShape() const
Definition pad.h:518
void BuildEffectivePolygon(ERROR_LOC aErrorLoc=ERROR_INSIDE) const
Definition pad.cpp:1250
static LSET ConnSMDMask()
layer set for a SMD pad on Front layer used for edge board connectors
Definition pad.cpp:353
void SetDrillSize(const VECTOR2I &aSize)
Definition pad.h:311
PAD_DRILL_POST_MACHINING_MODE GetBackPostMachiningMode() const
Definition pad.h:467
bool IsFreePad() const
Definition pad.cpp:332
int GetFrontPostMachiningAngle() const
Definition pad.h:457
PAD_DRILL_POST_MACHINING_MODE GetFrontPostMachiningMode() const
Definition pad.h:447
EDA_ANGLE GetOrientation() const
Return the rotation angle of the pad.
Definition pad.h:415
void SetSize(PCB_LAYER_ID aLayer, const VECTOR2I &aSize)
Definition pad.h:259
int m_delayPadToDie
Definition pad.h:1103
PAD_DRILL_SHAPE GetDrillShape() const
Definition pad.h:432
void SetSecondaryDrillShape(PAD_DRILL_SHAPE aShape)
Definition pad.cpp:787
void ReplacePrimitives(PCB_LAYER_ID aLayer, const std::vector< std::shared_ptr< PCB_SHAPE > > &aPrimitivesList)
Clear the current custom shape primitives list and import a new list.
Definition pad.cpp:3097
int GetChamferPositions(PCB_LAYER_ID aLayer) const
Definition pad.h:829
static LSET ApertureMask()
layer set for an aperture pad
Definition pad.cpp:367
virtual const BOX2I ViewBBox() const override
Return the bounding box of the item covering all its layers.
Definition pad.cpp:2350
UNCONNECTED_LAYER_MODE GetUnconnectedLayerMode() const
Definition pad.h:876
bool m_shapesDirty
Definition pad.h:1080
std::mutex m_polyBuildingLock
Definition pad.h:1088
void SetRoundRectCornerRadius(PCB_LAYER_ID aLayer, double aRadius)
Has meaning only for rounded rectangle pads.
Definition pad.cpp:867
void SetDrillSizeY(int aY)
Definition pad.cpp:524
static LSET SMDMask()
layer set for a SMD pad on Front layer
Definition pad.cpp:346
std::optional< int > GetLocalSolderPasteMargin() const
Definition pad.h:586
int GetFrontRoundRectRadiusSize() const
Definition pad.cpp:901
const std::shared_ptr< SHAPE_POLY_SET > & GetEffectivePolygon(PCB_LAYER_ID aLayer, ERROR_LOC aErrorLoc=ERROR_INSIDE) const
Definition pad.cpp:919
int GetBackPostMachiningDepth() const
Definition pad.h:475
std::optional< int > GetLocalSolderMaskMargin() const
Definition pad.h:579
void SetDrillSizeX(int aX)
Definition pad.cpp:511
void SetLocalSolderPasteMargin(std::optional< int > aMargin)
Definition pad.h:587
int GetSizeY() const
Definition pad.h:291
std::optional< int > GetLocalThermalGapOverride() const
Definition pad.h:763
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition pad.cpp:382
wxString GetItemDescription(UNITS_PROVIDER *aUnitsProvider, bool aFull) const override
Return a user-visible description string of this item.
Definition pad.cpp:2106
void SetPinFunction(const wxString &aName)
Set the pad function (pin name in schematic)
Definition pad.h:147
EDA_ANGLE GetFPRelativeOrientation() const
Definition pad.cpp:1409
double GetChamferRectRatio(PCB_LAYER_ID aLayer) const
Definition pad.h:812
bool m_polyDirty[2]
Definition pad.h:1087
bool HitTest(const VECTOR2I &aPosition, int aAccuracy=0) const override
Test if aPosition is inside or on the boundary of this item.
Definition pad.cpp:1928
void SetFPRelativeOrientation(const EDA_ANGLE &aAngle)
Definition pad.cpp:1400
int GetPostMachiningKnockout(PCB_LAYER_ID aLayer) const
Get the knockout diameter for a layer affected by post-machining.
Definition pad.cpp:633
int GetBoundingRadius() const
Return the radius of a minimum sized circle which fully encloses this pad.
Definition pad.cpp:1042
std::optional< int > GetTopBackdrillSize() const
Definition pad.h:1045
void ClearZoneLayerOverrides()
Definition pad.cpp:260
void SetOrientation(const EDA_ANGLE &aAngle)
Set the rotation angle of the pad.
Definition pad.cpp:1393
std::mutex m_zoneLayerOverridesMutex
Definition pad.h:1105
std::optional< int > GetLocalThermalSpokeWidthOverride() const
Definition pad.h:727
PAD_DRILL_SHAPE GetPrimaryDrillShape() const
Definition pad.h:429
bool IsBackdrilledOrPostMachined(PCB_LAYER_ID aLayer) const
Check if a layer is affected by backdrilling or post-machining operations.
Definition pad.cpp:556
VECTOR2I GetSolderPasteMargin(PCB_LAYER_ID aLayer) const
Usually < 0 (mask shape smaller than pad)because the margin can be dependent on the pad size,...
Definition pad.cpp:1667
void SetTopBackdrillSize(std::optional< int > aSize)
Definition pad.h:1046
BITMAPS GetMenuImage() const override
Return a pointer to an image to be used in menus.
Definition pad.cpp:2190
void AppendPrimitives(PCB_LAYER_ID aLayer, const std::vector< std::shared_ptr< PCB_SHAPE > > &aPrimitivesList)
Import a custom shape primitive list (composed of basic shapes) and add items to the current list.
Definition pad.cpp:3110
wxString m_number
Definition pad.h:1070
void SetPrimaryDrillStartLayer(PCB_LAYER_ID aLayer)
Definition pad.cpp:542
void SetBackPostMachiningDepth(int aDepth)
Definition pad.h:474
bool HasDrilledHole() const override
Definition pad.h:112
void SetPrimaryDrillCapped(const std::optional< bool > &aCapped)
Definition pad.cpp:741
LAYER_POLYGON_MAP m_effectivePolygons
Definition pad.h:1089
PCB_LAYER_ID GetBottomBackdrillLayer() const
Definition pad.h:1042
void SetLocalClearance(std::optional< int > aClearance)
Definition pad.h:577
int GetSubRatsnest() const
Definition pad.h:837
void SetSizeX(const int aX)
Definition pad.h:271
ZONE_CONNECTION GetLocalZoneConnection() const
Definition pad.h:604
void SetTertiaryDrillSizeY(int aY)
Definition pad.cpp:826
int m_lengthPadToDie
Definition pad.h:1102
double GetThermalSpokeAngleDegrees() const
Definition pad.h:753
CUSTOM_SHAPE_ZONE_MODE GetCustomShapeInZoneOpt() const
Definition pad.h:222
VECTOR2I ShapePos(PCB_LAYER_ID aLayer) const
Definition pad.cpp:1503
void SetSecondaryDrillSizeY(int aY)
Definition pad.cpp:773
PCB_LAYER_ID GetTopBackdrillLayer() const
Definition pad.h:1048
std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape() const override
Return a SHAPE_SEGMENT object representing the pad's hole.
Definition pad.cpp:1033
void SetOrientationDegrees(double aOrientation)
Definition pad.h:419
ZONE_CONNECTION GetZoneConnectionOverrides(wxString *aSource=nullptr) const
Definition pad.cpp:1739
int GetLocalThermalGapOverride(wxString *aSource) const
Definition pad.cpp:1768
void SetLayerSet(const LSET &aLayers) override
Definition pad.h:554
bool SharesNetTieGroup(const PAD *aOther) const
Definition pad.cpp:309
PAD_SHAPE GetAnchorPadShape(PCB_LAYER_ID aLayer) const
Definition pad.h:214
void SetBottomBackdrillSize(std::optional< int > aSize)
Definition pad.h:1040
void SetRoundRectRadiusRatio(PCB_LAYER_ID aLayer, double aRadiusScale)
Has meaning only for rounded rectangle pads.
Definition pad.cpp:873
void ClearSecondaryDrillSize()
Definition pad.cpp:780
void SetSubRatsnest(int aSubRatsnest)
Definition pad.h:838
int GetLocalSpokeWidthOverride(wxString *aSource=nullptr) const
Definition pad.cpp:1759
bool TransformHoleToPolygon(SHAPE_POLY_SET &aBuffer, int aClearance, int aError, ERROR_LOC aErrorLoc=ERROR_INSIDE) const
Build the corner list of the polygonal drill shape in the board coordinate system.
Definition pad.cpp:2464
void SetPadToDieLength(int aLength)
Definition pad.h:570
bool IsFlipped() const
Definition pad.cpp:374
bool operator==(const PAD &aOther) const
Definition pad.cpp:3017
void SetSizeY(const int aY)
Definition pad.h:282
int GetPadToDieLength() const
Definition pad.h:571
void SetBackPostMachiningAngle(int aAngle)
Definition pad.h:476
BOX2I m_effectiveBoundingBox
Definition pad.h:1082
const VECTOR2I & GetSize(PCB_LAYER_ID aLayer) const
Definition pad.h:264
virtual std::vector< int > ViewGetLayers() const override
Return the all the layers within the VIEW the object is painted on.
Definition pad.cpp:2212
void Rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle) override
Rotate this object.
bool IsProxyItem() const override
Definition pcb_shape.h:116
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.
void Move(const VECTOR2I &aMoveVector) override
Move this object.
void SetStroke(const STROKE_PARAMS &aStroke) override
Definition pcb_shape.h:92
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition pcb_shape.h:71
PROPERTY_BASE & SetChoicesFunc(std::function< wxPGChoices(INSPECTABLE *)> aFunc)
Definition property.h:276
PROPERTY_BASE & SetAvailableFunc(std::function< bool(INSPECTABLE *)> aFunc)
Set a callback function to determine whether an object provides this property.
Definition property.h:262
PROPERTY_BASE & SetWriteableFunc(std::function< bool(INSPECTABLE *)> aFunc)
Definition property.h:287
PROPERTY_BASE & SetValidator(PROPERTY_VALIDATOR_FN &&aValidator)
Definition property.h:349
PROPERTY_BASE & SetIsHiddenFromLibraryEditors(bool aIsHidden=true)
Definition property.h:333
Provide class metadata.Helper macro to map type hashes to names.
void InheritsAfter(TYPE_ID aDerived, TYPE_ID aBase)
Declare an inheritance relationship between types.
void Mask(TYPE_ID aDerived, TYPE_ID aBase, const wxString &aName)
Sets a base class property as masked in a derived class.
static PROPERTY_MANAGER & Instance()
PROPERTY_BASE & AddProperty(PROPERTY_BASE *aProperty, const wxString &aGroup=wxEmptyString)
Register a property.
void OverrideAvailability(TYPE_ID aDerived, TYPE_ID aBase, const wxString &aName, std::function< bool(INSPECTABLE *)> aFunc)
Sets an override availability functor for a base class property of a given derived class.
void AddTypeCast(TYPE_CAST_BASE *aCast)
Register a type converter.
static VALIDATOR_RESULT PositiveIntValidator(const wxAny &&aValue, EDA_ITEM *aItem)
static VALIDATOR_RESULT RangeIntValidator(const wxAny &&aValue, EDA_ITEM *aItem)
static SEG::ecoord Square(int a)
Definition seg.h:123
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
void AddShape(SHAPE *aShape)
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
void Move(const VECTOR2I &aVector) override
int PointCount() const
Return the number of points (vertices) in this line chain.
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
void Rotate(const EDA_ANGLE &aAngle, const VECTOR2I &aCenter={ 0, 0 }) override
Rotate all vertices by a given angle.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
Represent a set of closed polygons.
void Rotate(const EDA_ANGLE &aAngle, const VECTOR2I &aCenter={ 0, 0 }) override
Rotate all vertices by a given angle.
void RemoveAllContours()
Remove all outlines & holes (clears) the polygon set.
bool HasHoles() const
Return true if the polygon set has any holes.
void BooleanAdd(const SHAPE_POLY_SET &b)
Perform boolean polyset union.
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new outline to the set and returns its index.
bool IsEmpty() const
Return true if the set is empty (no polygons at all)
void Fracture()
Convert a set of polygons with holes to a single outline with "slits"/"fractures" connecting the oute...
bool Collide(const SHAPE *aShape, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const override
Check if the boundary of shape (this) lies closer to the shape aShape than aClearance,...
void Inflate(int aAmount, CORNER_STRATEGY aCornerStrategy, int aMaxError, bool aSimplify=false)
Perform outline inflation/deflation.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Appends a vertex at the end of the given outline/hole (default: the last outline)
void Simplify()
Simplify the polyset (merges overlapping polys, eliminates degeneracy/self-intersections)
void BooleanIntersection(const SHAPE_POLY_SET &b)
Perform boolean polyset intersection.
int OutlineCount() const
Return the number of outlines in the set.
void Move(const VECTOR2I &aVector) override
bool Contains(const VECTOR2I &aP, int aSubpolyIndex=-1, int aAccuracy=0, bool aUseBBoxCaches=false) const
Return true if a given subpolygon contains the point aP.
void BooleanSubtract(const SHAPE_POLY_SET &b)
Perform boolean polyset difference.
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
const SHAPE_LINE_CHAIN Outline() const
bool Collide(const SHAPE *aShape, int aClearance, VECTOR2I *aMTV) const override
Check if the boundary of shape (this) lies closer to the shape aShape than aClearance,...
Represent a simple polygon consisting of a zero-thickness closed chain of connected line segments.
An abstract shape on 2D plane.
Definition shape.h:126
Simple container to manage line stroke parameters.
wxString MessageTextFromValue(double aValue, bool aAddUnitLabel=true, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE) const
A lower-precision version of StringFromValue().
wxString StringFromValue(double aValue, bool aAddUnitLabel=false, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE) const
Converts aValue in internal units into a united string.
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 TransformRoundChamferedRectToPolygon(SHAPE_POLY_SET &aBuffer, const VECTOR2I &aPosition, const VECTOR2I &aSize, const EDA_ANGLE &aRotation, int aCornerRadius, double aChamferRatio, int aChamferCorners, int aInflate, int aError, ERROR_LOC aErrorLoc)
Convert a rectangle with rounded corners and/or chamfered corners to a polygon.
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.
void TransformTrapezoidToPolygon(SHAPE_POLY_SET &aBuffer, const VECTOR2I &aPosition, const VECTOR2I &aSize, const EDA_ANGLE &aRotation, int aDeltaX, int aDeltaY, int aInflate, int aError, ERROR_LOC aErrorLoc)
Convert a rectangle or trapezoid to a polygon.
static PCB_SHAPE * findNext(PCB_SHAPE *aShape, const VECTOR2I &aPoint, const KDTree &kdTree, const PCB_SHAPE_ENDPOINTS_ADAPTOR &adaptor, double aChainingEpsilon)
@ ROUND_ALL_CORNERS
All angles are rounded.
@ ALLOW_ACUTE_CORNERS
just inflate the polygon. Acute angles create spikes
const int minSize
Push and Shove router track width and via size dialog.
@ DRCE_PADSTACK
Definition drc_item.h:63
@ DRCE_PADSTACK_INVALID
Definition drc_item.h:64
@ DRCE_PAD_TH_WITH_NO_HOLE
Definition drc_item.h:85
@ CLEARANCE_CONSTRAINT
Definition drc_rule.h:49
@ HOLE_CLEARANCE_CONSTRAINT
Definition drc_rule.h:51
@ SOLDER_PASTE_ABS_MARGIN_CONSTRAINT
Definition drc_rule.h:67
@ SOLDER_MASK_EXPANSION_CONSTRAINT
Definition drc_rule.h:66
@ SOLDER_PASTE_REL_MARGIN_CONSTRAINT
Definition drc_rule.h:68
#define _(s)
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:411
#define FOOTPRINT_EDIT_FRAME_NAME
#define PCB_EDIT_FRAME_NAME
#define IGNORE_PARENT_GROUP
Definition eda_item.h:55
#define ENTERED
indicates a group has been entered
#define SKIP_STRUCT
flag indicating that the structure should be ignored
@ FILLED_SHAPE
Fill with object color.
Definition eda_shape.h:58
a few functions useful in geometry calculations.
Some functions to handle hotkeys in KiCad.
PCB_LAYER_ID FlipLayer(PCB_LAYER_ID aLayerId, int aCopperLayersCount)
Definition layer_id.cpp:172
@ LAYER_PAD_FR_NETNAMES
Additional netnames layers (not associated with a PCB layer).
Definition layer_ids.h:200
@ LAYER_PAD_BK_NETNAMES
Definition layer_ids.h:201
@ LAYER_PAD_NETNAMES
Definition layer_ids.h:202
bool IsFrontLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a front layer.
Definition layer_ids.h:780
FLASHING
Enum used during connectivity building to ensure we do not query connectivity while building the data...
Definition layer_ids.h:184
@ NEVER_FLASHED
Never flashed for connectivity.
Definition layer_ids.h:187
@ ALWAYS_FLASHED
Always flashed for connectivity.
Definition layer_ids.h:186
bool IsBackLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a back layer.
Definition layer_ids.h:803
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:677
@ LAYER_LOCKED_ITEM_SHADOW
Shadow layer for locked items.
Definition layer_ids.h:307
@ LAYER_PAD_COPPER_START
Virtual layers for pad copper on a given copper layer.
Definition layer_ids.h:337
@ LAYER_FOOTPRINTS_FR
Show footprints on front.
Definition layer_ids.h:259
@ LAYER_NON_PLATEDHOLES
Draw usual through hole vias.
Definition layer_ids.h:239
@ LAYER_PADS
Meta control for all pads opacity/visibility (color ignored).
Definition layer_ids.h:292
@ LAYER_PAD_PLATEDHOLES
to draw pad holes (plated)
Definition layer_ids.h:271
@ LAYER_CLEARANCE_START
Virtual layers for pad/via/track clearance outlines for a given copper layer.
Definition layer_ids.h:345
@ LAYER_FOOTPRINTS_BK
Show footprints on back.
Definition layer_ids.h:260
@ LAYER_PAD_HOLEWALLS
Definition layer_ids.h:297
bool IsNetnameLayer(int aLayer)
Test whether a layer is a netname layer.
Definition layer_ids.h:869
bool IsHoleLayer(int aLayer)
Definition layer_ids.h:739
bool IsExternalCopperLayer(int aLayerId)
Test whether a layer is an external (F_Cu or B_Cu) copper layer.
Definition layer_ids.h:688
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
@ B_Adhes
Definition layer_ids.h:103
@ Edge_Cuts
Definition layer_ids.h:112
@ Dwgs_User
Definition layer_ids.h:107
@ F_Paste
Definition layer_ids.h:104
@ F_Adhes
Definition layer_ids.h:102
@ B_Mask
Definition layer_ids.h:98
@ B_Cu
Definition layer_ids.h:65
@ Eco1_User
Definition layer_ids.h:109
@ F_Mask
Definition layer_ids.h:97
@ B_Paste
Definition layer_ids.h:105
@ F_SilkS
Definition layer_ids.h:100
@ UNDEFINED_LAYER
Definition layer_ids.h:61
@ Eco2_User
Definition layer_ids.h:110
@ B_SilkS
Definition layer_ids.h:101
@ PCB_LAYER_ID_COUNT
Definition layer_ids.h:171
@ F_Cu
Definition layer_ids.h:64
This file contains miscellaneous commonly used macros and functions.
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition macros.h:83
constexpr void MIRROR(T &aPoint, const T &aMirrorRef)
Updates aPoint with the mirror of aPoint relative to the aMirrorRef.
Definition mirror.h:45
FLIP_DIRECTION
Definition mirror.h:27
@ LEFT_RIGHT
Flip left to right (around the Y axis)
Definition mirror.h:28
Message panel definition file.
constexpr int Mils2IU(const EDA_IU_SCALE &aIuScale, int mils)
Definition eda_units.h:175
bool ShapeHitTest(const SHAPE_LINE_CHAIN &aHitter, const SHAPE &aHittee, bool aHitteeContained)
Perform a shape-to-shape hit test.
bool PadHasMeaningfulRoundingRadius(const PAD &aPad, PCB_LAYER_ID aLayer)
Returns true if the pad's rounding ratio is valid (i.e.
Definition pad_utils.cpp:44
double GetDefaultIpcRoundingRatio(const PAD &aPad, PCB_LAYER_ID aLayer)
Get a sensible default for a rounded rectangle pad's rounding ratio.
Definition pad_utils.cpp:27
KICOMMON_API VECTOR2I UnpackVector2(const types::Vector2 &aInput)
Definition api_utils.cpp:86
KICOMMON_API void PackVector2(types::Vector2 &aOutput, const VECTOR2I &aInput)
Definition api_utils.cpp:79
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
std::optional< std::pair< ELECTRICAL_PINTYPE, bool > > parsePinType(const wxString &aPinTypeString)
Definition pad.cpp:156
static struct PAD_DESC _PAD_DESC
PAD_DRILL_SHAPE
The set of pad drill shapes, used with PAD::{Set,Get}DrillShape()
Definition padstack.h:69
PAD_ATTRIB
The set of pad shapes, used with PAD::{Set,Get}Attribute().
Definition padstack.h:97
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:103
@ SMD
Smd pad, appears on the solder paste layer (default)
Definition padstack.h:99
@ PTH
Plated through hole pad.
Definition padstack.h:98
@ CONN
Like smd, does not appear on the solder paste layer (default) Note: also has a special attribute in G...
Definition padstack.h:100
PAD_SHAPE
The set of pad shapes, used with PAD::{Set,Get}Shape()
Definition padstack.h:52
@ CHAMFERED_RECT
Definition padstack.h:60
@ ROUNDRECT
Definition padstack.h:57
@ TRAPEZOID
Definition padstack.h:56
@ RECTANGLE
Definition padstack.h:54
PAD_PROP
The set of pad properties used in Gerber files (Draw files, and P&P files) to define some properties ...
Definition padstack.h:114
@ FIDUCIAL_LOCAL
a fiducial (usually a smd) local to the parent footprint
Definition padstack.h:118
@ FIDUCIAL_GLBL
a fiducial (usually a smd) for the full board
Definition padstack.h:117
@ MECHANICAL
a pad used for mechanical support
Definition padstack.h:122
@ PRESSFIT
a PTH with a hole diameter with tight tolerances for press fit pin
Definition padstack.h:123
@ HEATSINK
a pad used as heat sink, usually in SMD footprints
Definition padstack.h:120
@ NONE
no special fabrication property
Definition padstack.h:115
@ TESTPOINT
a test point pad
Definition padstack.h:119
@ CASTELLATED
a pad with a castellated through hole
Definition padstack.h:121
@ BGA
Smd pad, used in BGA footprints.
Definition padstack.h:116
UNCONNECTED_LAYER_MODE
Definition padstack.h:128
#define _HKI(x)
Definition page_info.cpp:44
Class to handle a set of BOARD_ITEMs.
ELECTRICAL_PINTYPE
The symbol library pin object electrical types used in ERC tests.
Definition pin_type.h:36
@ PT_INPUT
usual pin input: must be connected
Definition pin_type.h:37
@ PT_NC
not connected (must be left open)
Definition pin_type.h:50
@ PT_OUTPUT
usual output
Definition pin_type.h:38
@ PT_TRISTATE
tri state bus pin
Definition pin_type.h:40
@ PT_NIC
not internally connected (may be connected to anything)
Definition pin_type.h:44
@ PT_BIDI
input or output (like port for a microprocessor)
Definition pin_type.h:39
@ PT_OPENEMITTER
pin type open emitter
Definition pin_type.h:49
@ PT_POWER_OUT
output of a regulator: intended to be connected to power input pins
Definition pin_type.h:47
@ PT_OPENCOLLECTOR
pin type open collector
Definition pin_type.h:48
@ PT_POWER_IN
power input (GND, VCC for ICs). Must be connected to a power output.
Definition pin_type.h:46
@ PT_UNSPECIFIED
unknown electrical properties: creates always a warning when connected
Definition pin_type.h:45
@ PT_PASSIVE
pin for passive symbols: must be connected, and can be connected to any pin.
Definition pin_type.h:43
wxString GetCanonicalElectricalTypeName(ELECTRICAL_PINTYPE aType)
Definition pin_type.h:58
#define ELECTRICAL_PINTYPES_TOTAL
Definition pin_type.h:56
#define TYPE_HASH(x)
Definition property.h:74
#define ENUM_TO_WXANY(type)
Macro to define read-only fields (no setter method available)
Definition property.h:823
@ PT_DEGREE
Angle expressed in degrees.
Definition property.h:66
@ PT_RATIO
Definition property.h:68
@ PT_DECIDEGREE
Angle expressed in decidegrees.
Definition property.h:67
@ PT_SIZE
Size expressed in distance units (mm/inch)
Definition property.h:63
@ PT_TIME
Time expressed in ps.
Definition property.h:69
#define REGISTER_TYPE(x)
wxString UnescapeString(const wxString &aSource)
! The properties of a padstack drill. Drill position is always the pad position (origin).
Definition padstack.h:261
PCB_LAYER_ID start
Definition padstack.h:264
PCB_LAYER_ID end
Definition padstack.h:265
VECTOR2I size
Drill diameter (x == y) or slot dimensions (x != y)
Definition padstack.h:262
std::optional< PAD_DRILL_POST_MACHINING_MODE > mode
Definition padstack.h:275
PAD_DESC()
Definition pad.cpp:3193
int clearance
int delta
#define M_PI
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
Definition trigo.cpp:229
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:97
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition typeinfo.h:86
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:87
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:98
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:96
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695
VECTOR2< int64_t > VECTOR2L
Definition vector2d.h:696
ZONE_CONNECTION
How pads are covered by copper in zone.
Definition zones.h:47
@ THERMAL
Use thermal relief for pads.
Definition zones.h:50
@ THT_THERMAL
Thermal relief only for THT pads.
Definition zones.h:52
@ NONE
Pads are not covered.
Definition zones.h:49
@ FULL
pads are covered by copper
Definition zones.h:51
#define ZONE_THICKNESS_MIN_VALUE_MM
Definition zones.h:35