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