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