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