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