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