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