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>
36#include <layer_range.h>
37#include <string_utils.h>
38#include <i18n_utility.h>
39#include <view/view.h>
40#include <board.h>
43#include <footprint.h>
44#include <lset.h>
45#include <pad.h>
46#include <pad_utils.h>
47#include <pcb_shape.h>
49#include <eda_units.h>
51#include <widgets/msgpanel.h>
52#include <pcb_painter.h>
54#include <wx/log.h>
55#include <api/api_enums.h>
56#include <api/api_utils.h>
57#include <api/api_pcb_utils.h>
58#include <api/board/board_types.pb.h>
59
60#include <memory>
61#include <macros.h>
62#include <magic_enum.hpp>
63#include <drc/drc_item.h>
64#include "kiface_base.h"
65#include "pcbnew_settings.h"
66
69
70
71PAD::PAD( FOOTPRINT* parent ) :
73 m_padStack( this )
74{
75 VECTOR2I& drill = m_padStack.Drill().size;
79 drill.x = drill.y = EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 30 ); // Default drill size 30 mils.
81
84
85 SetShape( F_Cu, PAD_SHAPE::CIRCLE ); // Default pad shape is PAD_CIRCLE.
86 SetAnchorPadShape( F_Cu, PAD_SHAPE::CIRCLE ); // Default shape for custom shaped pads
87 // is PAD_CIRCLE.
88 SetDrillShape( PAD_DRILL_SHAPE::CIRCLE ); // Default pad drill shape is a circle.
89 m_attribute = PAD_ATTRIB::PTH; // Default pad type is plated through hole
90 SetProperty( PAD_PROP::NONE ); // no special fabrication property
91
92 // Parameters for round rect only:
93 m_padStack.SetRoundRectRadiusRatio( 0.25, F_Cu ); // from IPC-7351C standard
94
95 // Parameters for chamfered rect only:
98
99 // Set layers mask to default for a standard thru hole pad.
101
102 SetSubRatsnest( 0 ); // used in ratsnest calculations
103
104 SetDirty();
106
109}
110
111
112PAD::PAD( const PAD& aOther ) :
113 BOARD_CONNECTED_ITEM( aOther.GetParent(), PCB_PAD_T ),
114 m_padStack( this )
115{
116 PAD::operator=( aOther );
117
118 const_cast<KIID&>( m_Uuid ) = aOther.m_Uuid;
119}
120
121
122PAD& PAD::operator=( const PAD &aOther )
123{
125
126 ImportSettingsFrom( aOther );
128 SetPosition( aOther.GetPosition() );
129 SetNumber( aOther.GetNumber() );
130 SetPinType( aOther.GetPinType() );
131 SetPinFunction( aOther.GetPinFunction() );
132 SetSubRatsnest( aOther.GetSubRatsnest() );
134
135 return *this;
136}
137
138
139void PAD::Serialize( google::protobuf::Any &aContainer ) const
140{
141 using namespace kiapi::board::types;
142 Pad pad;
143
144 pad.mutable_id()->set_value( m_Uuid.AsStdString() );
145 kiapi::common::PackVector2( *pad.mutable_position(), GetPosition() );
146 pad.set_locked( IsLocked() ? kiapi::common::types::LockedState::LS_LOCKED
147 : kiapi::common::types::LockedState::LS_UNLOCKED );
148 pad.mutable_net()->mutable_code()->set_value( GetNetCode() );
149 pad.mutable_net()->set_name( GetNetname() );
150 pad.set_number( GetNumber().ToUTF8() );
151 pad.set_type( ToProtoEnum<PAD_ATTRIB, PadType>( GetAttribute() ) );
152
153 google::protobuf::Any padStackMsg;
154 m_padStack.Serialize( padStackMsg );
155 padStackMsg.UnpackTo( pad.mutable_pad_stack() );
156
157 if( GetLocalClearance().has_value() )
158 pad.mutable_copper_clearance_override()->set_value_nm( *GetLocalClearance() );
159
160 aContainer.PackFrom( pad );
161}
162
163
164bool PAD::Deserialize( const google::protobuf::Any &aContainer )
165{
166 kiapi::board::types::Pad pad;
167
168 if( !aContainer.UnpackTo( &pad ) )
169 return false;
170
171 const_cast<KIID&>( m_Uuid ) = KIID( pad.id().value() );
173 SetNetCode( pad.net().code().value() );
174 SetLocked( pad.locked() == kiapi::common::types::LockedState::LS_LOCKED );
175 SetAttribute( FromProtoEnum<PAD_ATTRIB>( pad.type() ) );
176 SetNumber( wxString::FromUTF8( pad.number() ) );
177
178 google::protobuf::Any padStackWrapper;
179 padStackWrapper.PackFrom( pad.pad_stack() );
180 m_padStack.Deserialize( padStackWrapper );
181
183
184 if( pad.has_copper_clearance_override() )
185 SetLocalClearance( pad.copper_clearance_override().value_nm() );
186 else
187 SetLocalClearance( std::nullopt );
188
189 return true;
190}
191
192
194{
195 std::unique_lock<std::mutex> cacheLock( m_zoneLayerOverridesMutex );
196
199}
200
201
203{
204 static const ZONE_LAYER_OVERRIDE defaultOverride = ZLO_NONE;
205 auto it = m_zoneLayerOverrides.find( aLayer );
206 return it != m_zoneLayerOverrides.end() ? it->second : defaultOverride;
207}
208
209
211{
212 std::unique_lock<std::mutex> cacheLock( m_zoneLayerOverridesMutex );
213 m_zoneLayerOverrides[aLayer] = aOverride;
214}
215
216
218{
219 // Aperture pads don't get a number
220 if( IsAperturePad() )
221 return false;
222
223 // NPTH pads don't get numbers
224 if( GetAttribute() == PAD_ATTRIB::NPTH )
225 return false;
226
227 return true;
228}
229
230
231bool PAD::IsLocked() const
232{
233 if( GetParent() && GetParent()->IsLocked() )
234 return true;
235
236 return BOARD_ITEM::IsLocked();
237};
238
239
240bool PAD::SharesNetTieGroup( const PAD* aOther ) const
241{
242 FOOTPRINT* parentFp = GetParentFootprint();
243
244 if( parentFp && parentFp->IsNetTie() && aOther->GetParentFootprint() == parentFp )
245 {
246 std::map<wxString, int> padToNetTieGroupMap = parentFp->MapPadNumbersToNetTieGroups();
247 int thisNetTieGroup = padToNetTieGroupMap[ GetNumber() ];
248 int otherNetTieGroup = padToNetTieGroupMap[ aOther->GetNumber() ];
249
250 return thisNetTieGroup >= 0 && thisNetTieGroup == otherNetTieGroup;
251 }
252
253 return false;
254}
255
256
258{
259 return m_pinType.Contains( wxT( "no_connect" ) );
260}
261
262
263bool PAD::IsFreePad() const
264{
265 return GetShortNetname().StartsWith( wxT( "unconnected-(" ) )
266 && m_pinType == wxT( "free" );
267}
268
269
271{
272 static LSET saved = LSET::AllCuMask() | LSET( { F_Mask, B_Mask } );
273 return saved;
274}
275
276
278{
279 static LSET saved( { F_Cu, F_Paste, F_Mask } );
280 return saved;
281}
282
283
285{
286 static LSET saved( { F_Cu, F_Mask } );
287 return saved;
288}
289
290
292{
293 static LSET saved = LSET( { F_Cu, B_Cu, F_Mask, B_Mask } );
294 return saved;
295}
296
297
299{
300 static LSET saved( { F_Paste } );
301 return saved;
302}
303
304
305bool PAD::IsFlipped() const
306{
307 FOOTPRINT* parent = GetParentFootprint();
308
309 return ( parent && parent->GetLayer() == B_Cu );
310}
311
312
314{
315 return BOARD_ITEM::GetLayer();
316}
317
318
320{
321 if( m_attribute == PAD_ATTRIB::SMD || m_attribute == PAD_ATTRIB::CONN || GetLayerSet().none() )
322 return m_layer;
323 else
324 return GetLayerSet().Seq().front();
325
326}
327
328
329bool PAD::FlashLayer( LSET aLayers ) const
330{
331 for( PCB_LAYER_ID layer : aLayers.Seq() )
332 {
333 if( FlashLayer( layer ) )
334 return true;
335 }
336
337 return false;
338}
339
340
341bool PAD::FlashLayer( int aLayer, bool aOnlyCheckIfPermitted ) const
342{
343 if( aLayer == UNDEFINED_LAYER )
344 return true;
345
346 // Sometimes this is called with GAL layers and should just return true
347 if( aLayer > PCB_LAYER_ID_COUNT )
348 return true;
349
350 PCB_LAYER_ID layer = static_cast<PCB_LAYER_ID>( aLayer );
351
352 if( !IsOnLayer( layer ) )
353 return false;
354
355 if( GetAttribute() == PAD_ATTRIB::NPTH && IsCopperLayer( aLayer ) )
356 {
357 if( GetShape( layer ) == PAD_SHAPE::CIRCLE && GetDrillShape() == PAD_DRILL_SHAPE::CIRCLE )
358 {
359 if( GetOffset( layer ) == VECTOR2I( 0, 0 ) && GetDrillSize().x >= GetSize( layer ).x )
360 return false;
361 }
362 else if( GetShape( layer ) == PAD_SHAPE::OVAL
363 && GetDrillShape() == PAD_DRILL_SHAPE::OBLONG )
364 {
365 if( GetOffset( layer ) == VECTOR2I( 0, 0 )
366 && GetDrillSize().x >= GetSize( layer ).x
367 && GetDrillSize().y >= GetSize( layer ).y )
368 {
369 return false;
370 }
371 }
372 }
373
374 if( LSET::FrontBoardTechMask().test( aLayer ) )
375 aLayer = F_Cu;
376 else if( LSET::BackBoardTechMask().test( aLayer ) )
377 aLayer = B_Cu;
378
379 if( GetAttribute() == PAD_ATTRIB::PTH && IsCopperLayer( aLayer ) )
380 {
382
384 return true;
385
386 // Plated through hole pads need copper on the top/bottom layers for proper soldering
387 // Unless the user has removed them in the pad dialog
389 && ( aLayer == F_Cu || aLayer == B_Cu ) )
390 {
391 return true;
392 }
393
394 if( const BOARD* board = GetBoard() )
395 {
396 // Must be static to keep from raising its ugly head in performance profiles
397 static std::initializer_list<KICAD_T> types = { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T,
398 PCB_PAD_T };
399
400 if( auto it = m_zoneLayerOverrides.find( static_cast<PCB_LAYER_ID>( aLayer ) );
401 it != m_zoneLayerOverrides.end() && it->second == ZLO_FORCE_FLASHED )
402 {
403 return true;
404 }
405 else if( aOnlyCheckIfPermitted )
406 {
407 return true;
408 }
409 else
410 {
411 return board->GetConnectivity()->IsConnectedOnLayer( this, aLayer, types );
412 }
413 }
414 }
415
416 return true;
417}
418
419
420void PAD::SetDrillSizeX( const int aX )
421{
422 m_padStack.Drill().size.x = aX;
423
424 if( GetDrillShape() == PAD_DRILL_SHAPE::CIRCLE )
425 SetDrillSizeY( aX );
426
427 SetDirty();
428}
429
430
432{
433 m_padStack.Drill().shape = aShape;
434
435 if( aShape == PAD_DRILL_SHAPE::CIRCLE )
437
438 m_shapesDirty = true;
439}
440
441
443{
444 return m_padStack.RoundRectRadius( aLayer );
445}
446
447
448void PAD::SetRoundRectCornerRadius( PCB_LAYER_ID aLayer, double aRadius )
449{
450 m_padStack.SetRoundRectRadius( aRadius, aLayer );
451}
452
453
454void PAD::SetRoundRectRadiusRatio( PCB_LAYER_ID aLayer, double aRadiusScale )
455{
456 m_padStack.SetRoundRectRadiusRatio( std::clamp( aRadiusScale, 0.0, 0.5 ), aLayer );
457
458 SetDirty();
459}
460
461
462void PAD::SetFrontRoundRectRadiusRatio( double aRadiusScale )
463{
464 wxASSERT_MSG( m_padStack.Mode() == PADSTACK::MODE::NORMAL,
465 "Set front radius only meaningful for normal padstacks" );
466
467 m_padStack.SetRoundRectRadiusRatio( std::clamp( aRadiusScale, 0.0, 0.5 ), F_Cu );
468 SetDirty();
469}
470
471
473{
474 const VECTOR2I size = m_padStack.Size( F_Cu );
475 const int minSize = std::min( size.x, size.y );
476 const double newRatio = aRadius / double( minSize );
477
479}
480
481
483{
484 const VECTOR2I size = m_padStack.Size( F_Cu );
485 const int minSize = std::min( size.x, size.y );
486 const double ratio = GetFrontRoundRectRadiusRatio();
487
488 return KiROUND( ratio * minSize );
489}
490
491
492void PAD::SetChamferRectRatio( PCB_LAYER_ID aLayer, double aChamferScale )
493{
494 m_padStack.SetChamferRatio( std::clamp( aChamferScale, 0.0, 0.5 ), aLayer );
495
496 SetDirty();
497}
498
499
500const std::shared_ptr<SHAPE_POLY_SET>& PAD::GetEffectivePolygon( PCB_LAYER_ID aLayer,
501 ERROR_LOC aErrorLoc ) const
502{
503 if( m_polyDirty[ aErrorLoc ] )
504 BuildEffectivePolygon( aErrorLoc );
505
506 aLayer = Padstack().EffectiveLayerFor( aLayer );
507
508 return m_effectivePolygons[ aLayer ][ aErrorLoc ];
509}
510
511
512std::shared_ptr<SHAPE> PAD::GetEffectiveShape( PCB_LAYER_ID aLayer, FLASHING flashPTHPads ) const
513{
514 if( aLayer == Edge_Cuts )
515 {
516 std::shared_ptr<SHAPE_COMPOUND> effective_compund = std::make_shared<SHAPE_COMPOUND>();
517
518 if( GetAttribute() == PAD_ATTRIB::PTH || GetAttribute() == PAD_ATTRIB::NPTH )
519 {
520 effective_compund->AddShape( GetEffectiveHoleShape() );
521 return effective_compund;
522 }
523 else
524 {
525 effective_compund->AddShape( std::make_shared<SHAPE_NULL>() );
526 return effective_compund;
527 }
528 }
529
530 if( GetAttribute() == PAD_ATTRIB::PTH )
531 {
532 bool flash;
533 std::shared_ptr<SHAPE_COMPOUND> effective_compund = std::make_shared<SHAPE_COMPOUND>();
534
535 if( flashPTHPads == FLASHING::NEVER_FLASHED )
536 flash = false;
537 else if( flashPTHPads == FLASHING::ALWAYS_FLASHED )
538 flash = true;
539 else
540 flash = FlashLayer( aLayer );
541
542 if( !flash )
543 {
544 if( GetAttribute() == PAD_ATTRIB::PTH )
545 {
546 effective_compund->AddShape( GetEffectiveHoleShape() );
547 return effective_compund;
548 }
549 else
550 {
551 effective_compund->AddShape( std::make_shared<SHAPE_NULL>() );
552 return effective_compund;
553 }
554 }
555 }
556
557 if( m_shapesDirty )
559
560 aLayer = Padstack().EffectiveLayerFor( aLayer );
561
562 wxASSERT_MSG( m_effectiveShapes.contains( aLayer ) && m_effectiveShapes.at( aLayer ),
563 wxT( "Null shape in PAD::GetEffectiveShape!" ) );
564
565 return m_effectiveShapes[aLayer];
566}
567
568
569std::shared_ptr<SHAPE_SEGMENT> PAD::GetEffectiveHoleShape() const
570{
571 if( m_shapesDirty )
573
575}
576
577
579{
582
584}
585
586
588{
589 std::lock_guard<std::mutex> RAII_lock( m_shapesBuildingLock );
590
591 // If we had to wait for the lock then we were probably waiting for someone else to
592 // finish rebuilding the shapes. So check to see if they're clean now.
593 if( !m_shapesDirty )
594 return;
595
597
599 [&]( PCB_LAYER_ID aLayer )
600 {
601 const SHAPE_COMPOUND& layerShape = buildEffectiveShape( aLayer );
602 m_effectiveBoundingBox.Merge( layerShape.BBox() );
603 } );
604
605 // Hole shape
606 m_effectiveHoleShape = nullptr;
607
608 VECTOR2I half_size = m_padStack.Drill().size / 2;
609 int half_width = std::min( half_size.x, half_size.y );
610 VECTOR2I half_len( half_size.x - half_width, half_size.y - half_width );
611
612 RotatePoint( half_len, GetOrientation() );
613
614 m_effectiveHoleShape = std::make_shared<SHAPE_SEGMENT>( m_pos - half_len, m_pos + half_len,
615 half_width * 2 );
617
618 // All done
619 m_shapesDirty = false;
620}
621
622
624{
625 const BOARD* board = GetBoard();
626 int maxError = board ? board->GetDesignSettings().m_MaxError : ARC_HIGH_DEF;
627
628 m_effectiveShapes[aLayer] = std::make_shared<SHAPE_COMPOUND>();
629
630 auto add = [this, aLayer]( SHAPE* aShape )
631 {
632 m_effectiveShapes[aLayer]->AddShape( aShape );
633 };
634
635 VECTOR2I shapePos = ShapePos( aLayer ); // Fetch only once; rotation involves trig
636 PAD_SHAPE effectiveShape = GetShape( aLayer );
637 const VECTOR2I& size = m_padStack.Size( aLayer );
638
639 if( effectiveShape == PAD_SHAPE::CUSTOM )
640 effectiveShape = GetAnchorPadShape( aLayer );
641
642 switch( effectiveShape )
643 {
644 case PAD_SHAPE::CIRCLE:
645 add( new SHAPE_CIRCLE( shapePos, size.x / 2 ) );
646 break;
647
648 case PAD_SHAPE::OVAL:
649 if( size.x == size.y ) // the oval pad is in fact a circle
650 {
651 add( new SHAPE_CIRCLE( shapePos, size.x / 2 ) );
652 }
653 else
654 {
655 VECTOR2I half_size = size / 2;
656 int half_width = std::min( half_size.x, half_size.y );
657 VECTOR2I half_len( half_size.x - half_width, half_size.y - half_width );
658 RotatePoint( half_len, GetOrientation() );
659 add( new SHAPE_SEGMENT( shapePos - half_len, shapePos + half_len, half_width * 2 ) );
660 }
661
662 break;
663
664 case PAD_SHAPE::RECTANGLE:
665 case PAD_SHAPE::TRAPEZOID:
666 case PAD_SHAPE::ROUNDRECT:
667 {
668 int r = ( effectiveShape == PAD_SHAPE::ROUNDRECT ) ? GetRoundRectCornerRadius( aLayer ) : 0;
669 VECTOR2I half_size( size.x / 2, size.y / 2 );
670 VECTOR2I trap_delta( 0, 0 );
671
672 if( r )
673 {
674 half_size -= VECTOR2I( r, r );
675
676 // Avoid degenerated shapes (0 length segments) that always create issues
677 // For roundrect pad very near a circle, use only a circle
678 const int min_len = pcbIUScale.mmToIU( 0.0001 );
679
680 if( half_size.x < min_len && half_size.y < min_len )
681 {
682 add( new SHAPE_CIRCLE( shapePos, r ) );
683 break;
684 }
685 }
686 else if( effectiveShape == PAD_SHAPE::TRAPEZOID )
687 {
688 trap_delta = m_padStack.TrapezoidDeltaSize( aLayer ) / 2;
689 }
690
691 SHAPE_LINE_CHAIN corners;
692
693 corners.Append( -half_size.x - trap_delta.y, half_size.y + trap_delta.x );
694 corners.Append( half_size.x + trap_delta.y, half_size.y - trap_delta.x );
695 corners.Append( half_size.x - trap_delta.y, -half_size.y + trap_delta.x );
696 corners.Append( -half_size.x + trap_delta.y, -half_size.y - trap_delta.x );
697
698 corners.Rotate( GetOrientation() );
699 corners.Move( shapePos );
700
701 // GAL renders rectangles faster than 4-point polygons so it's worth checking if our
702 // body shape is a rectangle.
703 if( corners.PointCount() == 4
704 &&
705 ( ( corners.CPoint( 0 ).y == corners.CPoint( 1 ).y
706 && corners.CPoint( 1 ).x == corners.CPoint( 2 ).x
707 && corners.CPoint( 2 ).y == corners.CPoint( 3 ).y
708 && corners.CPoint( 3 ).x == corners.CPoint( 0 ).x )
709 ||
710 ( corners.CPoint( 0 ).x == corners.CPoint( 1 ).x
711 && corners.CPoint( 1 ).y == corners.CPoint( 2 ).y
712 && corners.CPoint( 2 ).x == corners.CPoint( 3 ).x
713 && corners.CPoint( 3 ).y == corners.CPoint( 0 ).y )
714 )
715 )
716 {
717 int width = std::abs( corners.CPoint( 2 ).x - corners.CPoint( 0 ).x );
718 int height = std::abs( corners.CPoint( 2 ).y - corners.CPoint( 0 ).y );
719 VECTOR2I pos( std::min( corners.CPoint( 2 ).x, corners.CPoint( 0 ).x ),
720 std::min( corners.CPoint( 2 ).y, corners.CPoint( 0 ).y ) );
721
722 add( new SHAPE_RECT( pos, width, height ) );
723 }
724 else
725 {
726 add( new SHAPE_SIMPLE( corners ) );
727 }
728
729 if( r )
730 {
731 add( new SHAPE_SEGMENT( corners.CPoint( 0 ), corners.CPoint( 1 ), r * 2 ) );
732 add( new SHAPE_SEGMENT( corners.CPoint( 1 ), corners.CPoint( 2 ), r * 2 ) );
733 add( new SHAPE_SEGMENT( corners.CPoint( 2 ), corners.CPoint( 3 ), r * 2 ) );
734 add( new SHAPE_SEGMENT( corners.CPoint( 3 ), corners.CPoint( 0 ), r * 2 ) );
735 }
736 }
737 break;
738
739 case PAD_SHAPE::CHAMFERED_RECT:
740 {
741 SHAPE_POLY_SET outline;
742
743 TransformRoundChamferedRectToPolygon( outline, shapePos, GetSize( aLayer ),
745 GetChamferRectRatio( aLayer ),
746 GetChamferPositions( aLayer ), 0, maxError,
747 ERROR_INSIDE );
748
749 add( new SHAPE_SIMPLE( outline.COutline( 0 ) ) );
750 }
751 break;
752
753 default:
754 wxFAIL_MSG( wxT( "PAD::buildEffectiveShapes: Unsupported pad shape: PAD_SHAPE::" )
755 + wxString( std::string( magic_enum::enum_name( effectiveShape ) ) ) );
756 break;
757 }
758
759 if( GetShape( aLayer ) == PAD_SHAPE::CUSTOM )
760 {
761 for( const std::shared_ptr<PCB_SHAPE>& primitive : m_padStack.Primitives( aLayer ) )
762 {
763 if( !primitive->IsProxyItem() )
764 {
765 for( SHAPE* shape : primitive->MakeEffectiveShapes() )
766 {
767 shape->Rotate( GetOrientation() );
768 shape->Move( shapePos );
769 add( shape );
770 }
771 }
772 }
773 }
774
775 return *m_effectiveShapes[aLayer];
776}
777
778
780{
781 std::lock_guard<std::mutex> RAII_lock( m_polyBuildingLock );
782
783 // If we had to wait for the lock then we were probably waiting for someone else to
784 // finish rebuilding the shapes. So check to see if they're clean now.
785 if( !m_polyDirty[ aErrorLoc ] )
786 return;
787
788 const BOARD* board = GetBoard();
789 int maxError = board ? board->GetDesignSettings().m_MaxError : ARC_HIGH_DEF;
790
792 [&]( PCB_LAYER_ID aLayer )
793 {
794 // Polygon
795 std::shared_ptr<SHAPE_POLY_SET>& effectivePolygon =
796 m_effectivePolygons[ aLayer ][ aErrorLoc ];
797
798 effectivePolygon = std::make_shared<SHAPE_POLY_SET>();
799 TransformShapeToPolygon( *effectivePolygon, aLayer, 0, maxError, aErrorLoc );
800
801 // Bounding radius
802
803 if( aErrorLoc == ERROR_OUTSIDE )
804 {
806
807 for( int cnt = 0; cnt < effectivePolygon->OutlineCount(); ++cnt )
808 {
809 const SHAPE_LINE_CHAIN& poly = effectivePolygon->COutline( cnt );
810
811 for( int ii = 0; ii < poly.PointCount(); ++ii )
812 {
813 int dist = KiROUND( ( poly.CPoint( ii ) - m_pos ).EuclideanNorm() );
815 }
816 }
817 }
818
819 } );
820
821 // All done
822 m_polyDirty[ aErrorLoc ] = false;
823}
824
825
827{
828 if( m_shapesDirty )
830
832}
833
834
836{
837 if( m_attribute != aAttribute )
838 {
839 m_attribute = aAttribute;
840
841 LSET& layerMask = m_padStack.LayerSet();
842
843 switch( aAttribute )
844 {
845 case PAD_ATTRIB::PTH:
846 // Plump up to all copper layers
847 layerMask |= LSET::AllCuMask();
848 break;
849
850 case PAD_ATTRIB::SMD:
851 case PAD_ATTRIB::CONN:
852 {
853 // Trim down to no more than one copper layer
854 LSET copperLayers = layerMask & LSET::AllCuMask();
855
856 if( copperLayers.count() > 1 )
857 {
858 layerMask &= ~LSET::AllCuMask();
859
860 if( copperLayers.test( B_Cu ) )
861 layerMask.set( B_Cu );
862 else
863 layerMask.set( copperLayers.Seq().front() );
864 }
865
866 // No hole
867 m_padStack.Drill().size = VECTOR2I( 0, 0 );
868 break;
869 }
870
871 case PAD_ATTRIB::NPTH:
872 // No number; no net
873 m_number = wxEmptyString;
875 break;
876 }
877 }
878
879 SetDirty();
880}
881
882
884{
885 const bool wasRoundable = PAD_UTILS::PadHasMeaningfulRoundingRadius( *this, F_Cu );
886
887 m_padStack.SetShape( aShape, F_Cu );
888
889 const bool isRoundable = PAD_UTILS::PadHasMeaningfulRoundingRadius( *this, F_Cu );
890
891 // If we have become roundable, set a sensible rounding default using the IPC rules.
892 if( !wasRoundable && isRoundable )
893 {
894 const double ipcRadiusRatio = PAD_UTILS::GetDefaultIpcRoundingRatio( *this, F_Cu );
895 m_padStack.SetRoundRectRadiusRatio( ipcRadiusRatio, F_Cu );
896 }
897
898 SetDirty();
899}
900
901
902void PAD::SetProperty( PAD_PROP aProperty )
903{
904 m_property = aProperty;
905
906 SetDirty();
907}
908
909
910void PAD::SetOrientation( const EDA_ANGLE& aAngle )
911{
912 m_padStack.SetOrientation( aAngle );
913 SetDirty();
914}
915
916
918{
919 if( FOOTPRINT* parentFP = GetParentFootprint() )
920 SetOrientation( aAngle + parentFP->GetOrientation() );
921 else
922 SetOrientation( aAngle );
923}
924
925
927{
928 if( FOOTPRINT* parentFP = GetParentFootprint() )
929 return GetOrientation() - parentFP->GetOrientation();
930 else
931 return GetOrientation();
932}
933
934
935void PAD::Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
936{
937 MIRROR( m_pos, aCentre, aFlipDirection );
938
940 [&]( PCB_LAYER_ID aLayer )
941 {
942 MIRROR( m_padStack.Offset( aLayer ), VECTOR2I{ 0, 0 }, aFlipDirection );
943 MIRROR( m_padStack.TrapezoidDeltaSize( aLayer ), VECTOR2I{ 0, 0 }, aFlipDirection );
944 } );
945
947
948 auto mirrorBitFlags = []( int& aBitfield, int a, int b )
949 {
950 bool temp = aBitfield & a;
951
952 if( aBitfield & b )
953 aBitfield |= a;
954 else
955 aBitfield &= ~a;
956
957 if( temp )
958 aBitfield |= b;
959 else
960 aBitfield &= ~b;
961 };
962
964 [&]( PCB_LAYER_ID aLayer )
965 {
966 if( aFlipDirection == FLIP_DIRECTION::LEFT_RIGHT )
967 {
968 mirrorBitFlags( m_padStack.ChamferPositions( aLayer ), RECT_CHAMFER_TOP_LEFT,
970 mirrorBitFlags( m_padStack.ChamferPositions( aLayer ), RECT_CHAMFER_BOTTOM_LEFT,
972 }
973 else
974 {
975 mirrorBitFlags( m_padStack.ChamferPositions( aLayer ), RECT_CHAMFER_TOP_LEFT,
977 mirrorBitFlags( m_padStack.ChamferPositions( aLayer ), RECT_CHAMFER_TOP_RIGHT,
979 }
980 } );
981
982 // Flip padstack geometry
983 int copperLayerCount = BoardCopperLayerCount();
984
985 m_padStack.FlipLayers( copperLayerCount );
986
987 // Flip pads layers after padstack geometry
988 LSET layerSet = m_padStack.LayerSet();
989 SetLayerSet( layerSet.Flip( copperLayerCount ) );
990
991 // Flip the basic shapes, in custom pads
992 FlipPrimitives( aFlipDirection );
993
994 SetDirty();
995}
996
997
999{
1001 [&]( PCB_LAYER_ID aLayer )
1002 {
1003 for( std::shared_ptr<PCB_SHAPE>& primitive : m_padStack.Primitives( aLayer ) )
1004 {
1005 // Ensure the primitive parent is up to date. Flip uses GetBoard() that
1006 // imply primitive parent is valid
1007 primitive->SetParent(this);
1008 primitive->Flip( VECTOR2I( 0, 0 ), aFlipDirection );
1009 }
1010 } );
1011
1012 SetDirty();
1013}
1014
1015
1017{
1018 VECTOR2I loc_offset = m_padStack.Offset( aLayer );
1019
1020 if( loc_offset.x == 0 && loc_offset.y == 0 )
1021 return m_pos;
1022
1023 RotatePoint( loc_offset, GetOrientation() );
1024
1025 VECTOR2I shape_pos = m_pos + loc_offset;
1026
1027 return shape_pos;
1028}
1029
1030
1032{
1033 if( GetAttribute() == PAD_ATTRIB::NPTH )
1034 {
1035 // NPTH pads have no plated hole cylinder. If their annular ring size is 0 or
1036 // negative, then they have no annular ring either.
1037 bool hasAnnularRing = true;
1038
1040 [&]( PCB_LAYER_ID aLayer )
1041 {
1042 switch( GetShape( aLayer ) )
1043 {
1044 case PAD_SHAPE::CIRCLE:
1045 if( m_padStack.Offset( aLayer ) == VECTOR2I( 0, 0 )
1046 && m_padStack.Size( aLayer ).x <= m_padStack.Drill().size.x )
1047 {
1048 hasAnnularRing = false;
1049 }
1050
1051 break;
1052
1053 case PAD_SHAPE::OVAL:
1054 if( m_padStack.Offset( aLayer ) == VECTOR2I( 0, 0 )
1055 && m_padStack.Size( aLayer ).x <= m_padStack.Drill().size.x
1056 && m_padStack.Size( aLayer ).y <= m_padStack.Drill().size.y )
1057 {
1058 hasAnnularRing = false;
1059 }
1060
1061 break;
1062
1063 default:
1064 // We could subtract the hole polygon from the shape polygon for these, but it
1065 // would be expensive and we're probably well out of the common use cases....
1066 break;
1067 }
1068 } );
1069
1070 if( !hasAnnularRing )
1071 return false;
1072 }
1073
1074 return ( GetLayerSet() & LSET::AllCuMask() ).any();
1075}
1076
1077
1078std::optional<int> PAD::GetLocalClearance( wxString* aSource ) const
1079{
1080 if( m_padStack.Clearance().has_value() && aSource )
1081 *aSource = _( "pad" );
1082
1083 return m_padStack.Clearance();
1084}
1085
1086
1087std::optional<int> PAD::GetClearanceOverrides( wxString* aSource ) const
1088{
1089 if( m_padStack.Clearance().has_value() )
1090 return GetLocalClearance( aSource );
1091
1092 if( FOOTPRINT* parentFootprint = GetParentFootprint() )
1093 return parentFootprint->GetClearanceOverrides( aSource );
1094
1095 return std::optional<int>();
1096}
1097
1098
1099int PAD::GetOwnClearance( PCB_LAYER_ID aLayer, wxString* aSource ) const
1100{
1102
1103 if( GetBoard() && GetBoard()->GetDesignSettings().m_DRCEngine )
1104 {
1106
1107 if( GetAttribute() == PAD_ATTRIB::NPTH )
1108 c = bds.m_DRCEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, this, nullptr, aLayer );
1109 else
1110 c = bds.m_DRCEngine->EvalRules( CLEARANCE_CONSTRAINT, this, nullptr, aLayer );
1111 }
1112
1113 if( c.Value().HasMin() )
1114 {
1115 if( aSource )
1116 *aSource = c.GetName();
1117
1118 return c.Value().Min();
1119 }
1120
1121 return 0;
1122}
1123
1124
1126{
1127 // Pads defined only on mask layers (and perhaps on other tech layers) use the shape
1128 // defined by the pad settings only. ALL other pads, even those that don't actually have
1129 // any copper (such as NPTH pads with holes the same size as the pad) get mask expansion.
1130 if( ( m_padStack.LayerSet() & LSET::AllCuMask() ).none() )
1131 return 0;
1132
1133 if( IsFrontLayer( aLayer ) )
1134 aLayer = F_Mask;
1135 else if( IsBackLayer( aLayer ) )
1136 aLayer = B_Mask;
1137 else
1138 return 0;
1139
1140 std::optional<int> margin = m_padStack.SolderMaskMargin( aLayer );
1141
1142 if( !margin.has_value() )
1143 {
1144 if( FOOTPRINT* parentFootprint = GetParentFootprint() )
1145 margin = parentFootprint->GetLocalSolderMaskMargin();
1146 }
1147
1148 if( !margin.has_value() )
1149 {
1150 if( const BOARD* brd = GetBoard() )
1151 margin = brd->GetDesignSettings().m_SolderMaskExpansion;
1152 }
1153
1154 int marginValue = margin.value_or( 0 );
1155
1156 PCB_LAYER_ID cuLayer = ( aLayer == B_Mask ) ? B_Cu : F_Cu;
1157
1158 // ensure mask have a size always >= 0
1159 if( marginValue < 0 )
1160 {
1161 int minsize = -std::min( m_padStack.Size( cuLayer ).x, m_padStack.Size( cuLayer ).y ) / 2;
1162
1163 if( marginValue < minsize )
1164 marginValue = minsize;
1165 }
1166
1167 return marginValue;
1168}
1169
1170
1172{
1173 // Pads defined only on mask layers (and perhaps on other tech layers) use the shape
1174 // defined by the pad settings only. ALL other pads, even those that don't actually have
1175 // any copper (such as NPTH pads with holes the same size as the pad) get paste expansion.
1176 if( ( m_padStack.LayerSet() & LSET::AllCuMask() ).none() )
1177 return VECTOR2I( 0, 0 );
1178
1179 if( IsFrontLayer( aLayer ) )
1180 aLayer = F_Paste;
1181 else if( IsBackLayer( aLayer ) )
1182 aLayer = B_Paste;
1183 else
1184 return VECTOR2I( 0, 0 );
1185
1186 std::optional<int> margin = m_padStack.SolderPasteMargin( aLayer );
1187 std::optional<double> mratio = m_padStack.SolderPasteMarginRatio( aLayer );
1188
1189 if( !margin.has_value() )
1190 {
1191 if( FOOTPRINT* parentFootprint = GetParentFootprint() )
1192 margin = parentFootprint->GetLocalSolderPasteMargin();
1193 }
1194
1195 if( !margin.has_value() )
1196 {
1197 if( const BOARD* board = GetBoard() )
1198 margin = board->GetDesignSettings().m_SolderPasteMargin;
1199 }
1200
1201 if( !mratio.has_value() )
1202 {
1203 if( FOOTPRINT* parentFootprint = GetParentFootprint() )
1204 mratio = parentFootprint->GetLocalSolderPasteMarginRatio();
1205 }
1206
1207 if( !mratio.has_value() )
1208 {
1209 if( const BOARD* board = GetBoard() )
1210 mratio = board->GetDesignSettings().m_SolderPasteMarginRatio;
1211 }
1212
1213 PCB_LAYER_ID cuLayer = ( aLayer == B_Paste ) ? B_Cu : F_Cu;
1214 VECTOR2I padSize = m_padStack.Size( cuLayer );
1215
1216 VECTOR2I pad_margin;
1217 pad_margin.x = margin.value_or( 0 ) + KiROUND( padSize.x * mratio.value_or( 0 ) );
1218 pad_margin.y = margin.value_or( 0 ) + KiROUND( padSize.y * mratio.value_or( 0 ) );
1219
1220 // ensure mask have a size always >= 0
1221 if( m_padStack.Shape( aLayer ) != PAD_SHAPE::CUSTOM )
1222 {
1223 if( pad_margin.x < -padSize.x / 2 )
1224 pad_margin.x = -padSize.x / 2;
1225
1226 if( pad_margin.y < -padSize.y / 2 )
1227 pad_margin.y = -padSize.y / 2;
1228 }
1229
1230 return pad_margin;
1231}
1232
1233
1235{
1236 ZONE_CONNECTION connection = m_padStack.ZoneConnection().value_or( ZONE_CONNECTION::INHERITED );
1237
1238 if( connection != ZONE_CONNECTION::INHERITED )
1239 {
1240 if( aSource )
1241 *aSource = _( "pad" );
1242 }
1243
1244 if( connection == ZONE_CONNECTION::INHERITED )
1245 {
1246 if( FOOTPRINT* parentFootprint = GetParentFootprint() )
1247 connection = parentFootprint->GetZoneConnectionOverrides( aSource );
1248 }
1249
1250 return connection;
1251}
1252
1253
1254int PAD::GetLocalSpokeWidthOverride( wxString* aSource ) const
1255{
1256 if( m_padStack.ThermalSpokeWidth().has_value() && aSource )
1257 *aSource = _( "pad" );
1258
1259 return m_padStack.ThermalSpokeWidth().value_or( 0 );
1260}
1261
1262
1263int PAD::GetLocalThermalGapOverride( wxString* aSource ) const
1264{
1265 if( m_padStack.ThermalGap().has_value() && aSource )
1266 *aSource = _( "pad" );
1267
1268 return GetLocalThermalGapOverride().value_or( 0 );
1269}
1270
1271
1272void PAD::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
1273{
1274 wxString msg;
1275 FOOTPRINT* parentFootprint = static_cast<FOOTPRINT*>( m_parent );
1276
1277 if( aFrame->GetName() == PCB_EDIT_FRAME_NAME )
1278 {
1279 if( parentFootprint )
1280 aList.emplace_back( _( "Footprint" ), parentFootprint->GetReference() );
1281 }
1282
1283 aList.emplace_back( _( "Pad" ), m_number );
1284
1285 if( !GetPinFunction().IsEmpty() )
1286 aList.emplace_back( _( "Pin Name" ), GetPinFunction() );
1287
1288 if( !GetPinType().IsEmpty() )
1289 aList.emplace_back( _( "Pin Type" ), GetPinType() );
1290
1291 if( aFrame->GetName() == PCB_EDIT_FRAME_NAME )
1292 {
1293 aList.emplace_back( _( "Net" ), UnescapeString( GetNetname() ) );
1294
1295 aList.emplace_back( _( "Resolved Netclass" ),
1296 UnescapeString( GetEffectiveNetClass()->GetHumanReadableName() ) );
1297
1298 if( IsLocked() )
1299 aList.emplace_back( _( "Status" ), _( "Locked" ) );
1300 }
1301
1302 if( GetAttribute() == PAD_ATTRIB::SMD || GetAttribute() == PAD_ATTRIB::CONN )
1303 aList.emplace_back( _( "Layer" ), layerMaskDescribe() );
1304
1305 if( aFrame->GetName() == FOOTPRINT_EDIT_FRAME_NAME )
1306 {
1307 if( GetAttribute() == PAD_ATTRIB::SMD )
1308 {
1309 // TOOD(JE) padstacks
1310 const std::shared_ptr<SHAPE_POLY_SET>& poly = GetEffectivePolygon( PADSTACK::ALL_LAYERS );
1311 double area = poly->Area();
1312
1313 aList.emplace_back( _( "Area" ), aFrame->MessageTextFromValue( area, true, EDA_DATA_TYPE::AREA ) );
1314 }
1315 }
1316
1317 // Show the pad shape, attribute and property
1318 wxString props = ShowPadAttr();
1319
1320 if( GetProperty() != PAD_PROP::NONE )
1321 props += ',';
1322
1323 switch( GetProperty() )
1324 {
1325 case PAD_PROP::NONE: break;
1326 case PAD_PROP::BGA: props += _( "BGA" ); break;
1327 case PAD_PROP::FIDUCIAL_GLBL: props += _( "Fiducial global" ); break;
1328 case PAD_PROP::FIDUCIAL_LOCAL: props += _( "Fiducial local" ); break;
1329 case PAD_PROP::TESTPOINT: props += _( "Test point" ); break;
1330 case PAD_PROP::HEATSINK: props += _( "Heat sink" ); break;
1331 case PAD_PROP::CASTELLATED: props += _( "Castellated" ); break;
1332 case PAD_PROP::MECHANICAL: props += _( "Mechanical" ); break;
1333 }
1334
1335 // TODO(JE) How to show complex padstack info in the message panel
1336 aList.emplace_back( ShowPadShape( PADSTACK::ALL_LAYERS ), props );
1337
1340
1341 if( ( padShape == PAD_SHAPE::CIRCLE || padShape == PAD_SHAPE::OVAL )
1342 && padSize.x == padSize.y )
1343 {
1344 aList.emplace_back( _( "Diameter" ), aFrame->MessageTextFromValue( padSize.x ) );
1345 }
1346 else
1347 {
1348 aList.emplace_back( _( "Width" ), aFrame->MessageTextFromValue( padSize.x ) );
1349 aList.emplace_back( _( "Height" ), aFrame->MessageTextFromValue( padSize.y ) );
1350 }
1351
1352 EDA_ANGLE fp_orient = parentFootprint ? parentFootprint->GetOrientation() : ANGLE_0;
1353 EDA_ANGLE pad_orient = GetOrientation() - fp_orient;
1354 pad_orient.Normalize180();
1355
1356 if( !fp_orient.IsZero() )
1357 msg.Printf( wxT( "%g(+ %g)" ), pad_orient.AsDegrees(), fp_orient.AsDegrees() );
1358 else
1359 msg.Printf( wxT( "%g" ), GetOrientation().AsDegrees() );
1360
1361 aList.emplace_back( _( "Rotation" ), msg );
1362
1363 if( GetPadToDieLength() )
1364 {
1365 aList.emplace_back( _( "Length in Package" ),
1367 }
1368
1369 const VECTOR2I& drill = m_padStack.Drill().size;
1370
1371 if( drill.x > 0 || drill.y > 0 )
1372 {
1373 if( GetDrillShape() == PAD_DRILL_SHAPE::CIRCLE )
1374 {
1375 aList.emplace_back( _( "Hole" ),
1376 wxString::Format( wxT( "%s" ),
1377 aFrame->MessageTextFromValue( drill.x ) ) );
1378 }
1379 else
1380 {
1381 aList.emplace_back( _( "Hole X / Y" ),
1382 wxString::Format( wxT( "%s / %s" ),
1383 aFrame->MessageTextFromValue( drill.x ),
1384 aFrame->MessageTextFromValue( drill.y ) ) );
1385 }
1386 }
1387
1388 wxString source;
1389 int clearance = GetOwnClearance( UNDEFINED_LAYER, &source );
1390
1391 if( !source.IsEmpty() )
1392 {
1393 aList.emplace_back( wxString::Format( _( "Min Clearance: %s" ),
1394 aFrame->MessageTextFromValue( clearance ) ),
1395 wxString::Format( _( "(from %s)" ),
1396 source ) );
1397 }
1398#if 0
1399 // useful for debug only
1400 aList.emplace_back( wxT( "UUID" ), m_Uuid.AsString() );
1401#endif
1402}
1403
1404
1405bool PAD::HitTest( const VECTOR2I& aPosition, int aAccuracy ) const
1406{
1407 VECTOR2I delta = aPosition - GetPosition();
1408 int boundingRadius = GetBoundingRadius() + aAccuracy;
1409
1410 if( delta.SquaredEuclideanNorm() > SEG::Square( boundingRadius ) )
1411 return false;
1412
1413 bool contains = false;
1414
1416 [&]( PCB_LAYER_ID l )
1417 {
1418 if( contains )
1419 return;
1420
1421 if( GetEffectivePolygon( l, ERROR_INSIDE )->Contains( aPosition, -1, aAccuracy ) )
1422 contains = true;
1423 } );
1424
1425 return contains;
1426}
1427
1428
1429bool PAD::HitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const
1430{
1431 BOX2I arect = aRect;
1432 arect.Normalize();
1433 arect.Inflate( aAccuracy );
1434
1435 BOX2I bbox = GetBoundingBox();
1436
1437 if( aContained )
1438 {
1439 return arect.Contains( bbox );
1440 }
1441 else
1442 {
1443 // Fast test: if aRect is outside the polygon bounding box,
1444 // rectangles cannot intersect
1445 if( !arect.Intersects( bbox ) )
1446 return false;
1447
1448 bool hit = false;
1449
1451 [&]( PCB_LAYER_ID aLayer )
1452 {
1453 if( hit )
1454 return;
1455
1456 const std::shared_ptr<SHAPE_POLY_SET>& poly =
1458
1459 int count = poly->TotalVertices();
1460
1461 for( int ii = 0; ii < count; ii++ )
1462 {
1463 VECTOR2I vertex = poly->CVertex( ii );
1464 VECTOR2I vertexNext = poly->CVertex( ( ii + 1 ) % count );
1465
1466 // Test if the point is within aRect
1467 if( arect.Contains( vertex ) )
1468 {
1469 hit = true;
1470 break;
1471 }
1472
1473 // Test if this edge intersects aRect
1474 if( arect.Intersects( vertex, vertexNext ) )
1475 {
1476 hit = true;
1477 break;
1478 }
1479 }
1480 } );
1481
1482 return hit;
1483 }
1484}
1485
1486
1487int PAD::Compare( const PAD* aPadRef, const PAD* aPadCmp )
1488{
1489 int diff;
1490
1491 if( ( diff = static_cast<int>( aPadRef->m_attribute ) -
1492 static_cast<int>( aPadCmp->m_attribute ) ) != 0 )
1493 return diff;
1494
1495 // Dick: specctra_export needs this
1496 // Lorenzo: gencad also needs it to implement padstacks!
1497
1498 return PADSTACK::Compare( &aPadRef->Padstack(), &aPadCmp->Padstack() );
1499}
1500
1501
1502void PAD::Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
1503{
1504 RotatePoint( m_pos, aRotCentre, aAngle );
1506
1507 SetDirty();
1508}
1509
1510
1511wxString PAD::ShowPadShape( PCB_LAYER_ID aLayer ) const
1512{
1513 switch( GetShape( aLayer ) )
1514 {
1515 case PAD_SHAPE::CIRCLE: return _( "Circle" );
1516 case PAD_SHAPE::OVAL: return _( "Oval" );
1517 case PAD_SHAPE::RECTANGLE: return _( "Rect" );
1518 case PAD_SHAPE::TRAPEZOID: return _( "Trap" );
1519 case PAD_SHAPE::ROUNDRECT: return _( "Roundrect" );
1520 case PAD_SHAPE::CHAMFERED_RECT: return _( "Chamferedrect" );
1521 case PAD_SHAPE::CUSTOM: return _( "CustomShape" );
1522 default: return wxT( "???" );
1523 }
1524}
1525
1526
1527wxString PAD::ShowPadAttr() const
1528{
1529 switch( GetAttribute() )
1530 {
1531 case PAD_ATTRIB::PTH: return _( "PTH" );
1532 case PAD_ATTRIB::SMD: return _( "SMD" );
1533 case PAD_ATTRIB::CONN: return _( "Conn" );
1534 case PAD_ATTRIB::NPTH: return _( "NPTH" );
1535 default: return wxT( "???" );
1536 }
1537}
1538
1539
1540wxString PAD::GetItemDescription( UNITS_PROVIDER* aUnitsProvider, bool aFull ) const
1541{
1542 FOOTPRINT* parentFP = nullptr;
1543
1544 if( EDA_DRAW_FRAME* frame = dynamic_cast<EDA_DRAW_FRAME*>( aUnitsProvider ) )
1545 {
1546 if( frame->GetName() == PCB_EDIT_FRAME_NAME )
1547 parentFP = GetParentFootprint();
1548 }
1549
1550 if( GetAttribute() == PAD_ATTRIB::NPTH )
1551 {
1552 if( parentFP )
1553 {
1554 return wxString::Format( _( "NPTH pad of %s" ),
1555 parentFP->GetReference() );
1556 }
1557 else
1558 {
1559 return _( "NPTH pad" );
1560 }
1561 }
1562 else if( GetNumber().IsEmpty() )
1563 {
1564 if( GetAttribute() == PAD_ATTRIB::SMD || GetAttribute() == PAD_ATTRIB::CONN )
1565 {
1566 if( parentFP )
1567 {
1568 return wxString::Format( _( "Pad %s of %s on %s" ),
1569 GetNetnameMsg(),
1570 parentFP->GetReference(),
1572 }
1573 else
1574 {
1575 return wxString::Format( _( "Pad on %s" ),
1577 }
1578 }
1579 else
1580 {
1581 if( parentFP )
1582 {
1583 return wxString::Format( _( "PTH pad %s of %s" ),
1584 GetNetnameMsg(),
1585 parentFP->GetReference() );
1586 }
1587 else
1588 {
1589 return _( "PTH pad" );
1590 }
1591 }
1592 }
1593 else
1594 {
1595 if( GetAttribute() == PAD_ATTRIB::SMD || GetAttribute() == PAD_ATTRIB::CONN )
1596 {
1597 if( parentFP )
1598 {
1599 return wxString::Format( _( "Pad %s %s of %s on %s" ),
1600 GetNumber(),
1601 GetNetnameMsg(),
1602 parentFP->GetReference(),
1604 }
1605 else
1606 {
1607 return wxString::Format( _( "Pad %s on %s" ),
1608 GetNumber(),
1610 }
1611 }
1612 else
1613 {
1614 if( parentFP )
1615 {
1616 return wxString::Format( _( "PTH pad %s %s of %s" ),
1617 GetNumber(),
1618 GetNetnameMsg(),
1619 parentFP->GetReference() );
1620 }
1621 else
1622 {
1623 return wxString::Format( _( "PTH pad %s" ),
1624 GetNumber() );
1625 }
1626 }
1627 }
1628}
1629
1630
1632{
1633 return BITMAPS::pad;
1634}
1635
1636
1638{
1639 PAD* cloned = new PAD( *this );
1640
1641 // Ensure the cloned primitives of the pad stack have the right parent
1642 cloned->Padstack().ForEachUniqueLayer(
1643 [&]( PCB_LAYER_ID aLayer )
1644 {
1645 for( std::shared_ptr<PCB_SHAPE>& primitive : cloned->m_padStack.Primitives( aLayer ) )
1646 {
1647 primitive->SetParent( cloned );
1648 }
1649 } );
1650
1651 return cloned;
1652}
1653
1654
1655std::vector<int> PAD::ViewGetLayers() const
1656{
1657 std::vector<int> layers;
1658 layers.reserve( 64 );
1659
1660 // These 2 types of pads contain a hole
1661 if( m_attribute == PAD_ATTRIB::PTH )
1662 {
1663 layers.push_back( LAYER_PAD_PLATEDHOLES );
1664 layers.push_back( LAYER_PAD_HOLEWALLS );
1665 }
1666
1667 if( m_attribute == PAD_ATTRIB::NPTH )
1668 layers.push_back( LAYER_NON_PLATEDHOLES );
1669
1670 LSET cuLayers = ( m_padStack.LayerSet() & LSET::AllCuMask() );
1671
1672 // Don't spend cycles rendering layers that aren't visible
1673 if( const BOARD* board = GetBoard() )
1674 cuLayers &= board->GetEnabledLayers();
1675
1676 if( cuLayers.count() > 1 )
1677 {
1678 // Multi layer pad
1679 for( PCB_LAYER_ID layer : cuLayers.Seq() )
1680 {
1681 layers.push_back( LAYER_PAD_COPPER_START + layer );
1682 layers.push_back( LAYER_CLEARANCE_START + layer );
1683 }
1684
1685 layers.push_back( LAYER_PAD_NETNAMES );
1686 }
1687 else if( IsOnLayer( F_Cu ) )
1688 {
1689 layers.push_back( LAYER_PAD_COPPER_START );
1690 layers.push_back( LAYER_CLEARANCE_START );
1691
1692 // Is this a PTH pad that has only front copper? If so, we need to also display the
1693 // net name on the PTH netname layer so that it isn't blocked by the drill hole.
1694 if( m_attribute == PAD_ATTRIB::PTH )
1695 layers.push_back( LAYER_PAD_NETNAMES );
1696 else
1697 layers.push_back( LAYER_PAD_FR_NETNAMES );
1698 }
1699 else if( IsOnLayer( B_Cu ) )
1700 {
1701 layers.push_back( LAYER_PAD_COPPER_START + B_Cu );
1702 layers.push_back( LAYER_CLEARANCE_START + B_Cu );
1703
1704 // Is this a PTH pad that has only back copper? If so, we need to also display the
1705 // net name on the PTH netname layer so that it isn't blocked by the drill hole.
1706 if( m_attribute == PAD_ATTRIB::PTH )
1707 layers.push_back( LAYER_PAD_NETNAMES );
1708 else
1709 layers.push_back( LAYER_PAD_BK_NETNAMES );
1710 }
1711
1712 // Check non-copper layers. This list should include all the layers that the
1713 // footprint editor allows a pad to be placed on.
1714 static const PCB_LAYER_ID layers_mech[] = { F_Mask, B_Mask, F_Paste, B_Paste,
1716
1717 for( PCB_LAYER_ID each_layer : layers_mech )
1718 {
1719 if( IsOnLayer( each_layer ) )
1720 layers.push_back( each_layer );
1721 }
1722
1723 return layers;
1724}
1725
1726
1727double PAD::ViewGetLOD( int aLayer, const KIGFX::VIEW* aView ) const
1728{
1729 PCB_PAINTER& painter = static_cast<PCB_PAINTER&>( *aView->GetPainter() );
1730 PCB_RENDER_SETTINGS& renderSettings = *painter.GetSettings();
1731 const BOARD* board = GetBoard();
1732
1733 // Meta control for hiding all pads
1734 if( !aView->IsLayerVisible( LAYER_PADS ) )
1735 return LOD_HIDE;
1736
1737 // Handle Render tab switches
1738 const PCB_LAYER_ID& pcbLayer = static_cast<PCB_LAYER_ID>( aLayer );
1739
1740 if( !IsFlipped() && !aView->IsLayerVisible( LAYER_FOOTPRINTS_FR ) )
1741 return LOD_HIDE;
1742
1743 if( IsFlipped() && !aView->IsLayerVisible( LAYER_FOOTPRINTS_BK ) )
1744 return LOD_HIDE;
1745
1746 LSET visible = board->GetVisibleLayers() & board->GetEnabledLayers();
1747
1748 if( IsHoleLayer( aLayer ) )
1749 {
1750 if( !( visible & LSET::PhysicalLayersMask() ).any() )
1751 return LOD_HIDE;
1752 }
1753 else if( IsNetnameLayer( aLayer ) )
1754 {
1755 if( renderSettings.GetHighContrast() )
1756 {
1757 // Hide netnames unless pad is flashed to a high-contrast layer
1758 if( !FlashLayer( renderSettings.GetPrimaryHighContrastLayer() ) )
1759 return LOD_HIDE;
1760 }
1761 else
1762 {
1763 // Hide netnames unless pad is flashed to a visible layer
1764 if( !FlashLayer( visible ) )
1765 return LOD_HIDE;
1766 }
1767
1768 // Netnames will be shown only if zoom is appropriate
1769 const int minSize = std::min( GetBoundingBox().GetWidth(), GetBoundingBox().GetHeight() );
1770
1772 }
1773
1774 VECTOR2L padSize = GetShape( pcbLayer ) != PAD_SHAPE::CUSTOM
1775 ? VECTOR2L( GetSize( pcbLayer ) ) : GetBoundingBox().GetSize();
1776
1777 int64_t minSide = std::min( padSize.x, padSize.y );
1778
1779 if( minSide > 0 )
1780 return std::min( (double) pcbIUScale.mmToIU( 0.2 ) / minSide, 3.5 );
1781
1782 return LOD_SHOW;
1783}
1784
1785
1787{
1788 // Bounding box includes soldermask too. Remember mask and/or paste margins can be < 0
1789 int solderMaskMargin = 0;
1790 VECTOR2I solderPasteMargin;
1791
1793 [&]( PCB_LAYER_ID aLayer )
1794 {
1795 solderMaskMargin = std::max( solderMaskMargin,
1796 std::max( GetSolderMaskExpansion( aLayer ), 0 ) );
1797 VECTOR2I layerMargin = GetSolderPasteMargin( aLayer );
1798 solderPasteMargin.x = std::max( solderPasteMargin.x, layerMargin.x );
1799 solderPasteMargin.y = std::max( solderPasteMargin.y, layerMargin.y );
1800 } );
1801
1802 BOX2I bbox = GetBoundingBox();
1803 int clearance = 0;
1804
1805 // If we're drawing clearance lines then get the biggest possible clearance
1806 if( PCBNEW_SETTINGS* cfg = dynamic_cast<PCBNEW_SETTINGS*>( Kiface().KifaceSettings() ) )
1807 {
1808 if( cfg && cfg->m_Display.m_PadClearance && GetBoard() )
1810 }
1811
1812 // Look for the biggest possible bounding box
1813 int xMargin = std::max( solderMaskMargin, solderPasteMargin.x ) + clearance;
1814 int yMargin = std::max( solderMaskMargin, solderPasteMargin.y ) + clearance;
1815
1816 return BOX2I( VECTOR2I( bbox.GetOrigin() ) - VECTOR2I( xMargin, yMargin ),
1817 VECTOR2I( bbox.GetSize() ) + VECTOR2I( 2 * xMargin, 2 * yMargin ) );
1818}
1819
1820
1821void PAD::ImportSettingsFrom( const PAD& aMasterPad )
1822{
1823 SetPadstack( aMasterPad.Padstack() );
1824 // Layer Set should be updated before calling SetAttribute()
1825 SetLayerSet( aMasterPad.GetLayerSet() );
1826 SetAttribute( aMasterPad.GetAttribute() );
1827 // Unfortunately, SetAttribute() can change m_layerMask.
1828 // Be sure we keep the original mask by calling SetLayerSet() after SetAttribute()
1829 SetLayerSet( aMasterPad.GetLayerSet() );
1830 SetProperty( aMasterPad.GetProperty() );
1831
1832 // Must be after setting attribute and layerSet
1833 if( !CanHaveNumber() )
1834 SetNumber( wxEmptyString );
1835
1836 // I am not sure the m_LengthPadToDie should be imported, because this is a parameter
1837 // really specific to a given pad (JPC).
1838#if 0
1839 SetPadToDieLength( aMasterPad.GetPadToDieLength() );
1840#endif
1841
1842 // The pad orientation, for historical reasons is the pad rotation + parent rotation.
1843 EDA_ANGLE pad_rot = aMasterPad.GetOrientation();
1844
1845 if( aMasterPad.GetParentFootprint() )
1846 pad_rot -= aMasterPad.GetParentFootprint()->GetOrientation();
1847
1848 if( GetParentFootprint() )
1849 pad_rot += GetParentFootprint()->GetOrientation();
1850
1851 SetOrientation( pad_rot );
1852
1854 [&]( PCB_LAYER_ID aLayer )
1855 {
1856 if( aMasterPad.GetShape( aLayer ) == PAD_SHAPE::CIRCLE )
1859 } );
1860
1861 switch( aMasterPad.GetAttribute() )
1862 {
1863 case PAD_ATTRIB::SMD:
1864 case PAD_ATTRIB::CONN:
1865 // These pads do not have a hole (they are expected to be on one external copper layer)
1866 SetDrillSize( VECTOR2I( 0, 0 ) );
1867 break;
1868
1869 default:
1870 ;
1871 }
1872
1873 // copy also local settings:
1874 SetLocalClearance( aMasterPad.GetLocalClearance() );
1878
1883
1885
1887
1888 SetDirty();
1889}
1890
1891
1893{
1894 assert( aImage->Type() == PCB_PAD_T );
1895
1896 std::swap( *this, *static_cast<PAD*>( aImage ) );
1897}
1898
1899
1900bool PAD::TransformHoleToPolygon( SHAPE_POLY_SET& aBuffer, int aClearance, int aError,
1901 ERROR_LOC aErrorLoc ) const
1902{
1903 VECTOR2I drillsize = GetDrillSize();
1904
1905 if( !drillsize.x || !drillsize.y )
1906 return false;
1907
1908 std::shared_ptr<SHAPE_SEGMENT> slot = GetEffectiveHoleShape();
1909
1910 TransformOvalToPolygon( aBuffer, slot->GetSeg().A, slot->GetSeg().B,
1911 slot->GetWidth() + aClearance * 2, aError, aErrorLoc );
1912
1913 return true;
1914}
1915
1916
1917void PAD::TransformShapeToPolygon( SHAPE_POLY_SET& aBuffer, PCB_LAYER_ID aLayer, int aClearance,
1918 int aMaxError, ERROR_LOC aErrorLoc, bool ignoreLineWidth ) const
1919{
1920 wxASSERT_MSG( !ignoreLineWidth, wxT( "IgnoreLineWidth has no meaning for pads." ) );
1921 wxASSERT_MSG( aLayer != UNDEFINED_LAYER,
1922 wxT( "UNDEFINED_LAYER is no longer allowed for PAD::TransformShapeToPolygon" ) );
1923
1924 // minimal segment count to approximate a circle to create the polygonal pad shape
1925 // This minimal value is mainly for very small pads, like SM0402.
1926 // Most of time pads are using the segment count given by aError value.
1927 const int pad_min_seg_per_circle_count = 16;
1928 int dx = m_padStack.Size( aLayer ).x / 2;
1929 int dy = m_padStack.Size( aLayer ).y / 2;
1930
1931 VECTOR2I padShapePos = ShapePos( aLayer ); // Note: for pad having a shape offset, the pad
1932 // position is NOT the shape position
1933
1934 switch( PAD_SHAPE shape = GetShape( aLayer ) )
1935 {
1936 case PAD_SHAPE::CIRCLE:
1937 case PAD_SHAPE::OVAL:
1938 // Note: dx == dy is not guaranteed for circle pads in legacy boards
1939 if( dx == dy || ( shape == PAD_SHAPE::CIRCLE ) )
1940 {
1941 TransformCircleToPolygon( aBuffer, padShapePos, dx + aClearance, aMaxError, aErrorLoc,
1942 pad_min_seg_per_circle_count );
1943 }
1944 else
1945 {
1946 int half_width = std::min( dx, dy );
1947 VECTOR2I delta( dx - half_width, dy - half_width );
1948
1950
1951 TransformOvalToPolygon( aBuffer, padShapePos - delta, padShapePos + delta,
1952 ( half_width + aClearance ) * 2, aMaxError, aErrorLoc,
1953 pad_min_seg_per_circle_count );
1954 }
1955
1956 break;
1957
1958 case PAD_SHAPE::TRAPEZOID:
1959 case PAD_SHAPE::RECTANGLE:
1960 {
1961 const VECTOR2I& trapDelta = m_padStack.TrapezoidDeltaSize( aLayer );
1962 int ddx = shape == PAD_SHAPE::TRAPEZOID ? trapDelta.x / 2 : 0;
1963 int ddy = shape == PAD_SHAPE::TRAPEZOID ? trapDelta.y / 2 : 0;
1964
1965 SHAPE_POLY_SET outline;
1966 TransformTrapezoidToPolygon( outline, padShapePos, m_padStack.Size( aLayer ),
1967 GetOrientation(), ddx, ddy, aClearance, aMaxError, aErrorLoc );
1968 aBuffer.Append( outline );
1969 break;
1970 }
1971
1972 case PAD_SHAPE::CHAMFERED_RECT:
1973 case PAD_SHAPE::ROUNDRECT:
1974 {
1975 bool doChamfer = shape == PAD_SHAPE::CHAMFERED_RECT;
1976
1977 SHAPE_POLY_SET outline;
1978 TransformRoundChamferedRectToPolygon( outline, padShapePos, m_padStack.Size( aLayer ),
1980 doChamfer ? GetChamferRectRatio( aLayer ) : 0,
1981 doChamfer ? GetChamferPositions( aLayer ) : 0,
1982 aClearance, aMaxError, aErrorLoc );
1983 aBuffer.Append( outline );
1984 break;
1985 }
1986
1987 case PAD_SHAPE::CUSTOM:
1988 {
1989 SHAPE_POLY_SET outline;
1990 MergePrimitivesAsPolygon( aLayer, &outline, aErrorLoc );
1991 outline.Rotate( GetOrientation() );
1992 outline.Move( VECTOR2I( padShapePos ) );
1993
1994 if( aClearance > 0 || aErrorLoc == ERROR_OUTSIDE )
1995 {
1996 if( aErrorLoc == ERROR_OUTSIDE )
1997 aClearance += aMaxError;
1998
1999 outline.Inflate( aClearance, CORNER_STRATEGY::ROUND_ALL_CORNERS, aMaxError );
2000 outline.Fracture();
2001 }
2002 else if( aClearance < 0 )
2003 {
2004 // Negative clearances are primarily for drawing solder paste layer, so we don't
2005 // worry ourselves overly about which side the error is on.
2006
2007 // aClearance is negative so this is actually a deflate
2008 outline.Inflate( aClearance, CORNER_STRATEGY::ALLOW_ACUTE_CORNERS, aMaxError );
2009 outline.Fracture();
2010 }
2011
2012 aBuffer.Append( outline );
2013 break;
2014 }
2015
2016 default:
2017 wxFAIL_MSG( wxT( "PAD::TransformShapeToPolygon no implementation for " )
2018 + wxString( std::string( magic_enum::enum_name( shape ) ) ) );
2019 break;
2020 }
2021}
2022
2023
2024std::vector<PCB_SHAPE*> PAD::Recombine( bool aIsDryRun, int maxError )
2025{
2026 FOOTPRINT* footprint = GetParentFootprint();
2027
2028 for( BOARD_ITEM* item : footprint->GraphicalItems() )
2029 item->ClearFlags( SKIP_STRUCT );
2030
2031 auto findNext =
2032 [&]( PCB_LAYER_ID aLayer ) -> PCB_SHAPE*
2033 {
2034 SHAPE_POLY_SET padPoly;
2035 TransformShapeToPolygon( padPoly, aLayer, 0, maxError, ERROR_INSIDE );
2036
2037 for( BOARD_ITEM* item : footprint->GraphicalItems() )
2038 {
2039 PCB_SHAPE* shape = dynamic_cast<PCB_SHAPE*>( item );
2040
2041 if( !shape || ( shape->GetFlags() & SKIP_STRUCT ) )
2042 continue;
2043
2044 if( shape->GetLayer() != aLayer )
2045 continue;
2046
2047 if( shape->IsProxyItem() ) // Pad number (and net name) box
2048 return shape;
2049
2050 SHAPE_POLY_SET drawPoly;
2051 shape->TransformShapeToPolygon( drawPoly, aLayer, 0, maxError, ERROR_INSIDE );
2052 drawPoly.BooleanIntersection( padPoly );
2053
2054 if( !drawPoly.IsEmpty() )
2055 return shape;
2056 }
2057
2058 return nullptr;
2059 };
2060
2061 auto findMatching =
2062 [&]( PCB_SHAPE* aShape ) -> std::vector<PCB_SHAPE*>
2063 {
2064 std::vector<PCB_SHAPE*> matching;
2065
2066 for( BOARD_ITEM* item : footprint->GraphicalItems() )
2067 {
2068 PCB_SHAPE* other = dynamic_cast<PCB_SHAPE*>( item );
2069
2070 if( !other || ( other->GetFlags() & SKIP_STRUCT ) )
2071 continue;
2072
2073 if( GetLayerSet().test( other->GetLayer() )
2074 && aShape->Compare( other ) == 0 )
2075 {
2076 matching.push_back( other );
2077 }
2078 }
2079
2080 return matching;
2081 };
2082
2083 PCB_LAYER_ID layer;
2084 std::vector<PCB_SHAPE*> mergedShapes;
2085
2086 if( IsOnLayer( F_Cu ) )
2087 layer = F_Cu;
2088 else if( IsOnLayer( B_Cu ) )
2089 layer = B_Cu;
2090 else
2091 layer = GetLayerSet().UIOrder().front();
2092
2093 PAD_SHAPE origShape = GetShape( layer );
2094
2095 // If there are intersecting items to combine, we need to first make sure the pad is a
2096 // custom-shape pad.
2097 if( !aIsDryRun && findNext( layer ) && origShape != PAD_SHAPE::CUSTOM )
2098 {
2099 if( origShape == PAD_SHAPE::CIRCLE || origShape == PAD_SHAPE::RECTANGLE )
2100 {
2101 // Use the existing pad as an anchor
2102 SetAnchorPadShape( layer, origShape );
2103 SetShape( layer, PAD_SHAPE::CUSTOM );
2104 }
2105 else
2106 {
2107 // Create a new circular anchor and convert existing pad to a polygon primitive
2108 SHAPE_POLY_SET existingOutline;
2109 TransformShapeToPolygon( existingOutline, layer, 0, maxError, ERROR_INSIDE );
2110
2111 int minExtent = std::min( GetSize( layer ).x, GetSize( layer ).y );
2112 SetAnchorPadShape( layer, PAD_SHAPE::CIRCLE );
2113 SetSize( layer, VECTOR2I( minExtent, minExtent ) );
2114 SetShape( layer, PAD_SHAPE::CUSTOM );
2115
2116 PCB_SHAPE* shape = new PCB_SHAPE( nullptr, SHAPE_T::POLY );
2117 shape->SetFilled( true );
2118 shape->SetStroke( STROKE_PARAMS( 0, LINE_STYLE::SOLID ) );
2119 shape->SetPolyShape( existingOutline );
2120 shape->Move( - ShapePos( layer ) );
2121 shape->Rotate( VECTOR2I( 0, 0 ), - GetOrientation() );
2122 AddPrimitive( layer, shape );
2123 }
2124 }
2125
2126 while( PCB_SHAPE* fpShape = findNext( layer ) )
2127 {
2128 fpShape->SetFlags( SKIP_STRUCT );
2129
2130 mergedShapes.push_back( fpShape );
2131
2132 if( !aIsDryRun )
2133 {
2134 PCB_SHAPE* primitive = static_cast<PCB_SHAPE*>( fpShape->Duplicate() );
2135
2136 primitive->SetParent( nullptr );
2137 primitive->Move( - ShapePos( layer ) );
2138 primitive->Rotate( VECTOR2I( 0, 0 ), - GetOrientation() );
2139
2140 AddPrimitive( layer, primitive );
2141 }
2142
2143 // See if there are other shapes that match and mark them for delete. (KiCad won't
2144 // produce these, but old footprints from other vendors have them.)
2145 for( PCB_SHAPE* other : findMatching( fpShape ) )
2146 {
2147 other->SetFlags( SKIP_STRUCT );
2148 mergedShapes.push_back( other );
2149 }
2150 }
2151
2152 for( BOARD_ITEM* item : footprint->GraphicalItems() )
2153 item->ClearFlags( SKIP_STRUCT );
2154
2155 if( !aIsDryRun )
2157
2158 return mergedShapes;
2159}
2160
2161
2162void PAD::CheckPad( UNITS_PROVIDER* aUnitsProvider, bool aForPadProperties,
2163 const std::function<void( int aErrorCode,
2164 const wxString& aMsg )>& aErrorHandler ) const
2165{
2167 [&]( PCB_LAYER_ID aLayer )
2168 {
2169 doCheckPad( aLayer, aUnitsProvider, aForPadProperties, aErrorHandler );
2170 } );
2171
2172 LSET padlayers_mask = GetLayerSet();
2173 VECTOR2I drill_size = GetDrillSize();
2174
2175 if( !padlayers_mask[F_Cu] && !padlayers_mask[B_Cu] )
2176 {
2177 if( ( drill_size.x || drill_size.y ) && GetAttribute() != PAD_ATTRIB::NPTH )
2178 {
2179 aErrorHandler( DRCE_PADSTACK, _( "(plated through holes normally have a copper pad on "
2180 "at least one outer layer)" ) );
2181 }
2182 }
2183
2184 if( ( GetProperty() == PAD_PROP::FIDUCIAL_GLBL || GetProperty() == PAD_PROP::FIDUCIAL_LOCAL )
2185 && GetAttribute() == PAD_ATTRIB::NPTH )
2186 {
2187 aErrorHandler( DRCE_PADSTACK, _( "('fiducial' property makes no sense on NPTH pads)" ) );
2188 }
2189
2190 if( GetProperty() == PAD_PROP::TESTPOINT && GetAttribute() == PAD_ATTRIB::NPTH )
2191 aErrorHandler( DRCE_PADSTACK, _( "('testpoint' property makes no sense on NPTH pads)" ) );
2192
2193 if( GetProperty() == PAD_PROP::HEATSINK && GetAttribute() == PAD_ATTRIB::NPTH )
2194 aErrorHandler( DRCE_PADSTACK, _( "('heatsink' property makes no sense of NPTH pads)" ) );
2195
2196 if( GetProperty() == PAD_PROP::CASTELLATED && GetAttribute() != PAD_ATTRIB::PTH )
2197 aErrorHandler( DRCE_PADSTACK, _( "('castellated' property is for PTH pads)" ) );
2198
2199 if( GetProperty() == PAD_PROP::BGA && GetAttribute() != PAD_ATTRIB::SMD )
2200 aErrorHandler( DRCE_PADSTACK, _( "('BGA' property is for SMD pads)" ) );
2201
2202 if( GetProperty() == PAD_PROP::MECHANICAL && GetAttribute() != PAD_ATTRIB::PTH )
2203 aErrorHandler( DRCE_PADSTACK, _( "('mechanical' property is for PTH pads)" ) );
2204
2205 switch( GetAttribute() )
2206 {
2207 case PAD_ATTRIB::NPTH: // Not plated, but through hole, a hole is expected
2208 case PAD_ATTRIB::PTH: // Pad through hole, a hole is also expected
2209 if( drill_size.x <= 0
2210 || ( drill_size.y <= 0 && GetDrillShape() == PAD_DRILL_SHAPE::OBLONG ) )
2211 {
2212 aErrorHandler( DRCE_PAD_TH_WITH_NO_HOLE, wxEmptyString );
2213 }
2214 break;
2215
2216 case PAD_ATTRIB::CONN: // Connector pads are smd pads, just they do not have solder paste.
2217 if( padlayers_mask[B_Paste] || padlayers_mask[F_Paste] )
2218 {
2219 aErrorHandler( DRCE_PADSTACK, _( "(connector pads normally have no solder paste; use a "
2220 "SMD pad instead)" ) );
2221 }
2223
2224 case PAD_ATTRIB::SMD: // SMD and Connector pads (One external copper layer only)
2225 {
2226 if( drill_size.x > 0 || drill_size.y > 0 )
2227 aErrorHandler( DRCE_PADSTACK_INVALID, _( "(SMD pad has a hole)" ) );
2228
2229 LSET innerlayers_mask = padlayers_mask & LSET::InternalCuMask();
2230
2231 if( IsOnLayer( F_Cu ) && IsOnLayer( B_Cu ) )
2232 {
2233 aErrorHandler( DRCE_PADSTACK, _( "(SMD pad has copper on both sides of the board)" ) );
2234 }
2235 else if( IsOnLayer( F_Cu ) )
2236 {
2237 if( IsOnLayer( B_Mask ) )
2238 {
2239 aErrorHandler( DRCE_PADSTACK, _( "(SMD pad has copper and mask layers on different "
2240 "sides of the board)" ) );
2241 }
2242 else if( IsOnLayer( B_Paste ) )
2243 {
2244 aErrorHandler( DRCE_PADSTACK, _( "(SMD pad has copper and paste layers on different "
2245 "sides of the board)" ) );
2246 }
2247 }
2248 else if( IsOnLayer( B_Cu ) )
2249 {
2250 if( IsOnLayer( F_Mask ) )
2251 {
2252 aErrorHandler( DRCE_PADSTACK, _( "(SMD pad has copper and mask layers on different "
2253 "sides of the board)" ) );
2254 }
2255 else if( IsOnLayer( F_Paste ) )
2256 {
2257 aErrorHandler( DRCE_PADSTACK, _( "(SMD pad has copper and paste layers on different "
2258 "sides of the board)" ) );
2259 }
2260 }
2261 else if( innerlayers_mask.count() != 0 )
2262 {
2263 aErrorHandler( DRCE_PADSTACK, _( "(SMD pad has no outer layers)" ) );
2264 }
2265
2266 break;
2267 }
2268 }
2269}
2270
2271
2272void PAD::doCheckPad( PCB_LAYER_ID aLayer, UNITS_PROVIDER* aUnitsProvider, bool aForPadProperties,
2273 const std::function<void( int aErrorCode,
2274 const wxString& aMsg )>& aErrorHandler ) const
2275{
2276 wxString msg;
2277
2278 VECTOR2I pad_size = GetSize( aLayer );
2279
2280 if( GetShape( aLayer ) == PAD_SHAPE::CUSTOM )
2281 pad_size = GetBoundingBox().GetSize();
2282 else if( pad_size.x <= 0 || ( pad_size.y <= 0 && GetShape( aLayer ) != PAD_SHAPE::CIRCLE ) )
2283 aErrorHandler( DRCE_PADSTACK_INVALID, _( "(Pad must have a positive size)" ) );
2284
2285 // Test hole against pad shape
2286 if( IsOnCopperLayer() && GetDrillSize().x > 0 )
2287 {
2288 // Ensure the drill size can be handled in next calculations.
2289 // Use min size = 4 IU to be able to build a polygon from a hole shape
2290 const int min_drill_size = 4;
2291
2292 if( GetDrillSizeX() <= min_drill_size || GetDrillSizeY() <= min_drill_size )
2293 {
2294 msg.Printf( _( "(PTH pad hole size must be larger than %s)" ),
2295 aUnitsProvider->StringFromValue( min_drill_size, true ) );
2296 aErrorHandler( DRCE_PADSTACK_INVALID, msg );
2297 }
2298
2299 int maxError = GetBoard()->GetDesignSettings().m_MaxError;
2300 SHAPE_POLY_SET padOutline;
2301
2302 TransformShapeToPolygon( padOutline, aLayer, 0, maxError, ERROR_INSIDE );
2303
2304 if( GetAttribute() == PAD_ATTRIB::PTH )
2305 {
2306 // Test if there is copper area outside hole
2307 std::shared_ptr<SHAPE_SEGMENT> hole = GetEffectiveHoleShape();
2308 SHAPE_POLY_SET holeOutline;
2309
2310 TransformOvalToPolygon( holeOutline, hole->GetSeg().A, hole->GetSeg().B,
2311 hole->GetWidth(), ARC_HIGH_DEF, ERROR_OUTSIDE );
2312
2313 SHAPE_POLY_SET copper = padOutline;
2314 copper.BooleanSubtract( holeOutline );
2315
2316 if( copper.IsEmpty() )
2317 {
2318 aErrorHandler( DRCE_PADSTACK, _( "(PTH pad hole leaves no copper)" ) );
2319 }
2320 else if( aForPadProperties )
2321 {
2322 // Test if the pad hole is fully inside the copper area. Note that we only run
2323 // this check for pad properties because we run the more complete annular ring
2324 // checker on the board (which handles multiple pads with the same name).
2325 holeOutline.BooleanSubtract( padOutline );
2326
2327 if( !holeOutline.IsEmpty() )
2328 aErrorHandler( DRCE_PADSTACK, _( "(PTH pad hole not fully inside copper)" ) );
2329 }
2330 }
2331 else
2332 {
2333 // Test only if the pad hole's centre is inside the copper area
2334 if( !padOutline.Collide( GetPosition() ) )
2335 aErrorHandler( DRCE_PADSTACK, _( "(pad hole not inside pad shape)" ) );
2336 }
2337 }
2338
2339 if( GetLocalClearance().value_or( 0 ) < 0 )
2340 aErrorHandler( DRCE_PADSTACK, _( "(negative local clearance values have no effect)" ) );
2341
2342 // Some pads need a negative solder mask clearance (mainly for BGA with small pads)
2343 // However the negative solder mask clearance must not create negative mask size
2344 // Therefore test for minimal acceptable negative value
2345 std::optional<int> solderMaskMargin = GetLocalSolderMaskMargin();
2346
2347 if( solderMaskMargin.has_value() && solderMaskMargin.value() < 0 )
2348 {
2349 int absMargin = abs( solderMaskMargin.value() );
2350
2351 if( GetShape( aLayer ) == PAD_SHAPE::CUSTOM )
2352 {
2353 for( const std::shared_ptr<PCB_SHAPE>& shape : GetPrimitives( aLayer ) )
2354 {
2355 BOX2I shapeBBox = shape->GetBoundingBox();
2356
2357 if( absMargin > shapeBBox.GetWidth() || absMargin > shapeBBox.GetHeight() )
2358 {
2359 aErrorHandler( DRCE_PADSTACK, _( "(negative solder mask clearance is larger "
2360 "than some shape primitives; results may be "
2361 "surprising)" ) );
2362
2363 break;
2364 }
2365 }
2366 }
2367 else if( absMargin > pad_size.x || absMargin > pad_size.y )
2368 {
2369 aErrorHandler( DRCE_PADSTACK, _( "(negative solder mask clearance is larger than pad; "
2370 "no solder mask will be generated)" ) );
2371 }
2372 }
2373
2374 // Some pads need a positive solder paste clearance (mainly for BGA with small pads)
2375 // However, a positive value can create issues if the resulting shape is too big.
2376 // (like a solder paste creating a solder paste area on a neighbor pad or on the solder mask)
2377 // So we could ask for user to confirm the choice
2378 // For now we just check for disappearing paste
2379 wxSize paste_size;
2380 int paste_margin = GetLocalSolderPasteMargin().value_or( 0 );
2381 double paste_ratio = GetLocalSolderPasteMarginRatio().value_or( 0 );
2382
2383 paste_size.x = pad_size.x + paste_margin + KiROUND( pad_size.x * paste_ratio );
2384 paste_size.y = pad_size.y + paste_margin + KiROUND( pad_size.y * paste_ratio );
2385
2386 if( paste_size.x <= 0 || paste_size.y <= 0 )
2387 {
2388 aErrorHandler( DRCE_PADSTACK, _( "(negative solder paste margin is larger than pad; "
2389 "no solder paste mask will be generated)" ) );
2390 }
2391
2392 if( GetShape( aLayer ) == PAD_SHAPE::ROUNDRECT )
2393 {
2394 if( GetRoundRectRadiusRatio( aLayer ) < 0.0 )
2395 aErrorHandler( DRCE_PADSTACK_INVALID, _( "(negative corner radius is not allowed)" ) );
2396 else if( GetRoundRectRadiusRatio( aLayer ) > 50.0 )
2397 aErrorHandler( DRCE_PADSTACK, _( "(corner size will make pad circular)" ) );
2398 }
2399 else if( GetShape( aLayer ) == PAD_SHAPE::CHAMFERED_RECT )
2400 {
2401 if( GetChamferRectRatio( aLayer ) < 0.0 )
2402 aErrorHandler( DRCE_PADSTACK_INVALID, _( "(negative corner chamfer is not allowed)" ) );
2403 else if( GetChamferRectRatio( aLayer ) > 50.0 )
2404 aErrorHandler( DRCE_PADSTACK_INVALID, _( "(corner chamfer is too large)" ) );
2405 }
2406 else if( GetShape( aLayer ) == PAD_SHAPE::TRAPEZOID )
2407 {
2408 if( ( GetDelta( aLayer ).x < 0 && GetDelta( aLayer ).x < -GetSize( aLayer ).y )
2409 || ( GetDelta( aLayer ).x > 0 && GetDelta( aLayer ).x > GetSize( aLayer ).y )
2410 || ( GetDelta( aLayer ).y < 0 && GetDelta( aLayer ).y < -GetSize( aLayer ).x )
2411 || ( GetDelta( aLayer ).y > 0 && GetDelta( aLayer ).y > GetSize( aLayer ).x ) )
2412 {
2413 aErrorHandler( DRCE_PADSTACK_INVALID, _( "(trapezoid delta is too large)" ) );
2414 }
2415 }
2416
2417 if( GetShape( aLayer ) == PAD_SHAPE::CUSTOM )
2418 {
2419 SHAPE_POLY_SET mergedPolygon;
2420 MergePrimitivesAsPolygon( aLayer, &mergedPolygon );
2421
2422 if( mergedPolygon.OutlineCount() > 1 )
2423 aErrorHandler( DRCE_PADSTACK_INVALID, _( "(custom pad shape must resolve to a single polygon)" ) );
2424 }
2425}
2426
2427
2428bool PAD::operator==( const BOARD_ITEM& aBoardItem ) const
2429{
2430 if( Type() != aBoardItem.Type() )
2431 return false;
2432
2433 if( m_parent && aBoardItem.GetParent() && m_parent->m_Uuid != aBoardItem.GetParent()->m_Uuid )
2434 return false;
2435
2436 const PAD& other = static_cast<const PAD&>( aBoardItem );
2437
2438 return *this == other;
2439}
2440
2441
2442bool PAD::operator==( const PAD& aOther ) const
2443{
2444 if( Padstack() != aOther.Padstack() )
2445 return false;
2446
2447 if( GetPosition() != aOther.GetPosition() )
2448 return false;
2449
2450 if( GetAttribute() != aOther.GetAttribute() )
2451 return false;
2452
2453 return true;
2454}
2455
2456
2457double PAD::Similarity( const BOARD_ITEM& aOther ) const
2458{
2459 if( aOther.Type() != Type() )
2460 return 0.0;
2461
2462 if( m_parent->m_Uuid != aOther.GetParent()->m_Uuid )
2463 return 0.0;
2464
2465 const PAD& other = static_cast<const PAD&>( aOther );
2466
2467 double similarity = 1.0;
2468
2469 if( GetPosition() != other.GetPosition() )
2470 similarity *= 0.9;
2471
2472 if( GetAttribute() != other.GetAttribute() )
2473 similarity *= 0.9;
2474
2475 similarity *= Padstack().Similarity( other.Padstack() );
2476
2477 return similarity;
2478}
2479
2480
2481static struct PAD_DESC
2482{
2484 {
2486 .Map( PAD_ATTRIB::PTH, _HKI( "Through-hole" ) )
2487 .Map( PAD_ATTRIB::SMD, _HKI( "SMD" ) )
2488 .Map( PAD_ATTRIB::CONN, _HKI( "Edge connector" ) )
2489 .Map( PAD_ATTRIB::NPTH, _HKI( "NPTH, mechanical" ) );
2490
2492 .Map( PAD_SHAPE::CIRCLE, _HKI( "Circle" ) )
2493 .Map( PAD_SHAPE::RECTANGLE, _HKI( "Rectangle" ) )
2494 .Map( PAD_SHAPE::OVAL, _HKI( "Oval" ) )
2495 .Map( PAD_SHAPE::TRAPEZOID, _HKI( "Trapezoid" ) )
2496 .Map( PAD_SHAPE::ROUNDRECT, _HKI( "Rounded rectangle" ) )
2497 .Map( PAD_SHAPE::CHAMFERED_RECT, _HKI( "Chamfered rectangle" ) )
2498 .Map( PAD_SHAPE::CUSTOM, _HKI( "Custom" ) );
2499
2501 .Map( PAD_PROP::NONE, _HKI( "None" ) )
2502 .Map( PAD_PROP::BGA, _HKI( "BGA pad" ) )
2503 .Map( PAD_PROP::FIDUCIAL_GLBL, _HKI( "Fiducial, global to board" ) )
2504 .Map( PAD_PROP::FIDUCIAL_LOCAL, _HKI( "Fiducial, local to footprint" ) )
2505 .Map( PAD_PROP::TESTPOINT, _HKI( "Test point pad" ) )
2506 .Map( PAD_PROP::HEATSINK, _HKI( "Heatsink pad" ) )
2507 .Map( PAD_PROP::CASTELLATED, _HKI( "Castellated pad" ) )
2508 .Map( PAD_PROP::MECHANICAL, _HKI( "Mechanical pad" ) );
2509
2511 .Map( PAD_DRILL_SHAPE::CIRCLE, _HKI( "Round" ) )
2512 .Map( PAD_DRILL_SHAPE::OBLONG, _HKI( "Oblong" ) );
2513
2515
2516 if( zcMap.Choices().GetCount() == 0 )
2517 {
2518 zcMap.Undefined( ZONE_CONNECTION::INHERITED );
2519 zcMap.Map( ZONE_CONNECTION::INHERITED, _HKI( "Inherited" ) )
2520 .Map( ZONE_CONNECTION::NONE, _HKI( "None" ) )
2521 .Map( ZONE_CONNECTION::THERMAL, _HKI( "Thermal reliefs" ) )
2522 .Map( ZONE_CONNECTION::FULL, _HKI( "Solid" ) )
2523 .Map( ZONE_CONNECTION::THT_THERMAL, _HKI( "Thermal reliefs for PTH" ) );
2524 }
2525
2527 .Map( PADSTACK::UNCONNECTED_LAYER_MODE::KEEP_ALL, _HKI( "All copper layers" ) )
2528 .Map( PADSTACK::UNCONNECTED_LAYER_MODE::REMOVE_ALL, _HKI( "Connected layers only" ) )
2530 _HKI( "Front, back and connected layers" ) );
2531
2533 REGISTER_TYPE( PAD );
2535
2536 propMgr.Mask( TYPE_HASH( PAD ), TYPE_HASH( BOARD_CONNECTED_ITEM ), _HKI( "Layer" ) );
2537 propMgr.Mask( TYPE_HASH( PAD ), TYPE_HASH( BOARD_ITEM ), _HKI( "Locked" ) );
2538
2539 propMgr.AddProperty( new PROPERTY<PAD, double>( _HKI( "Orientation" ),
2541 PROPERTY_DISPLAY::PT_DEGREE ) );
2542
2543 auto isCopperPad =
2544 []( INSPECTABLE* aItem ) -> bool
2545 {
2546 if( PAD* pad = dynamic_cast<PAD*>( aItem ) )
2547 return pad->GetAttribute() != PAD_ATTRIB::NPTH;
2548
2549 return false;
2550 };
2551
2552 auto padCanHaveHole =
2553 []( INSPECTABLE* aItem ) -> bool
2554 {
2555 if( PAD* pad = dynamic_cast<PAD*>( aItem ) )
2556 {
2557 return pad->GetAttribute() == PAD_ATTRIB::PTH
2558 || pad->GetAttribute() == PAD_ATTRIB::NPTH;
2559 }
2560
2561 return false;
2562 };
2563
2564 auto hasNormalPadstack =
2565 []( INSPECTABLE* aItem ) -> bool
2566 {
2567 if( PAD* pad = dynamic_cast<PAD*>( aItem ) )
2568 return pad->Padstack().Mode() == PADSTACK::MODE::NORMAL;
2569
2570 return true;
2571 };
2572
2574 _HKI( "Net" ), isCopperPad );
2576 _HKI( "Net Class" ), isCopperPad );
2577
2578 const wxString groupPad = _HKI( "Pad Properties" );
2579
2580 auto padType = new PROPERTY_ENUM<PAD, PAD_ATTRIB>( _HKI( "Pad Type" ),
2582 propMgr.AddProperty( padType, groupPad );
2583
2584 auto shape = new PROPERTY_ENUM<PAD, PAD_SHAPE>( _HKI( "Pad Shape" ),
2586 propMgr.AddProperty( shape, groupPad )
2587 .SetAvailableFunc( hasNormalPadstack );
2588
2589 auto padNumber = new PROPERTY<PAD, wxString>( _HKI( "Pad Number" ),
2591 padNumber->SetAvailableFunc( isCopperPad );
2592 propMgr.AddProperty( padNumber, groupPad );
2593
2594 propMgr.AddProperty( new PROPERTY<PAD, wxString>( _HKI( "Pin Name" ),
2595 NO_SETTER( PAD, wxString ), &PAD::GetPinFunction ), groupPad )
2597 propMgr.AddProperty( new PROPERTY<PAD, wxString>( _HKI( "Pin Type" ),
2598 NO_SETTER( PAD, wxString ), &PAD::GetPinType ), groupPad )
2600
2601 propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Size X" ),
2603 PROPERTY_DISPLAY::PT_SIZE ), groupPad )
2604 .SetAvailableFunc( hasNormalPadstack );
2605 propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Size Y" ),
2607 PROPERTY_DISPLAY::PT_SIZE ), groupPad )
2609 [=]( INSPECTABLE* aItem ) -> bool
2610 {
2611 if( PAD* pad = dynamic_cast<PAD*>( aItem ) )
2612 {
2613 // Custom padstacks can't have size modified through panel
2614 if( pad->Padstack().Mode() != PADSTACK::MODE::NORMAL )
2615 return false;
2616
2617 // Circle pads have no usable y-size
2618 return pad->GetShape( PADSTACK::ALL_LAYERS ) != PAD_SHAPE::CIRCLE;
2619 }
2620
2621 return true;
2622 } );
2623
2624 const auto hasRoundRadius = [=]( INSPECTABLE* aItem ) -> bool
2625 {
2626 if( PAD* pad = dynamic_cast<PAD*>( aItem ) )
2627 {
2628 // Custom padstacks can't have this property modified through panel
2629 if( pad->Padstack().Mode() != PADSTACK::MODE::NORMAL )
2630 return false;
2631
2633 }
2634
2635 return false;
2636 };
2637
2638 auto roundRadiusRatio = new PROPERTY<PAD, double>( _HKI( "Corner Radius Ratio" ),
2640 roundRadiusRatio->SetAvailableFunc( hasRoundRadius );
2641 propMgr.AddProperty( roundRadiusRatio, groupPad );
2642
2643 auto roundRadiusSize = new PROPERTY<PAD, int>( _HKI( "Corner Radius Size" ),
2645 PROPERTY_DISPLAY::PT_SIZE );
2646 roundRadiusSize->SetAvailableFunc( hasRoundRadius );
2647 propMgr.AddProperty( roundRadiusSize, groupPad );
2648
2649 propMgr.AddProperty( new PROPERTY_ENUM<PAD, PAD_DRILL_SHAPE>( _HKI( "Hole Shape" ),
2650 &PAD::SetDrillShape, &PAD::GetDrillShape ), groupPad )
2651 .SetWriteableFunc( padCanHaveHole );
2652
2653 propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Hole Size X" ),
2655 PROPERTY_DISPLAY::PT_SIZE ), groupPad )
2656 .SetWriteableFunc( padCanHaveHole )
2658
2659 propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Hole Size Y" ),
2661 PROPERTY_DISPLAY::PT_SIZE ), groupPad )
2662 .SetWriteableFunc( padCanHaveHole )
2665 [=]( INSPECTABLE* aItem ) -> bool
2666 {
2667 // Circle holes have no usable y-size
2668 if( PAD* pad = dynamic_cast<PAD*>( aItem ) )
2669 return pad->GetDrillShape() != PAD_DRILL_SHAPE::CIRCLE;
2670
2671 return true;
2672 } );
2673
2674 propMgr.AddProperty( new PROPERTY_ENUM<PAD, PAD_PROP>( _HKI( "Fabrication Property" ),
2675 &PAD::SetProperty, &PAD::GetProperty ), groupPad );
2676
2678 _HKI( "Copper Layers" ),
2680 propMgr.AddProperty( layerMode, groupPad );
2681
2682 auto padToDie = new PROPERTY<PAD, int>( _HKI( "Pad To Die Length" ),
2684 PROPERTY_DISPLAY::PT_SIZE );
2685 padToDie->SetAvailableFunc( isCopperPad );
2686 propMgr.AddProperty( padToDie, groupPad );
2687
2688 const wxString groupOverrides = _HKI( "Overrides" );
2689
2690 propMgr.AddProperty( new PROPERTY<PAD, std::optional<int>>(
2691 _HKI( "Clearance Override" ),
2693 PROPERTY_DISPLAY::PT_SIZE ), groupOverrides );
2694
2695 propMgr.AddProperty( new PROPERTY<PAD, std::optional<int>>(
2696 _HKI( "Soldermask Margin Override" ),
2698 PROPERTY_DISPLAY::PT_SIZE ), groupOverrides );
2699
2700 propMgr.AddProperty( new PROPERTY<PAD, std::optional<int>>(
2701 _HKI( "Solderpaste Margin Override" ),
2703 PROPERTY_DISPLAY::PT_SIZE ), groupOverrides );
2704
2705 propMgr.AddProperty( new PROPERTY<PAD, std::optional<double>>(
2706 _HKI( "Solderpaste Margin Ratio Override" ),
2708 PROPERTY_DISPLAY::PT_RATIO ),
2709 groupOverrides );
2710
2712 _HKI( "Zone Connection Style" ),
2714
2715 constexpr int minZoneWidth = pcbIUScale.mmToIU( ZONE_THICKNESS_MIN_VALUE_MM );
2716
2717 propMgr.AddProperty( new PROPERTY<PAD, std::optional<int>>(
2718 _HKI( "Thermal Relief Spoke Width" ),
2720 PROPERTY_DISPLAY::PT_SIZE ), groupOverrides )
2721 .SetValidator( PROPERTY_VALIDATORS::RangeIntValidator<minZoneWidth, INT_MAX> );
2722
2724 _HKI( "Thermal Relief Spoke Angle" ),
2726 PROPERTY_DISPLAY::PT_DEGREE ), groupOverrides );
2727
2728 propMgr.AddProperty( new PROPERTY<PAD, std::optional<int>>(
2729 _HKI( "Thermal Relief Gap" ),
2731 PROPERTY_DISPLAY::PT_SIZE ), groupOverrides )
2733
2734 // TODO delta, drill shape offset, layer set
2735 }
2737
ERROR_LOC
When approximating an arc or circle, should the error be placed on the outside or inside of the curve...
Definition: approximation.h:32
@ ERROR_OUTSIDE
Definition: approximation.h:33
@ ERROR_INSIDE
Definition: approximation.h:34
constexpr int ARC_HIGH_DEF
Definition: base_units.h:120
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:108
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
BITMAPS
A list of all bitmap identifiers.
Definition: bitmaps_list.h:33
ZONE_LAYER_OVERRIDE
Conditionally flashed vias and pads that interact with zones of different priority can be very squirr...
Definition: board_item.h:66
@ ZLO_NONE
Definition: board_item.h:67
@ ZLO_FORCE_FLASHED
Definition: board_item.h:68
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,...
wxString GetNetnameMsg() const
virtual NETCLASS * GetEffectiveNetClass() const
Return the NETCLASS for this item.
bool SetNetCode(int aNetCode, bool aNoAssert)
Set net using a net code.
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
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:79
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition: board_item.h:239
virtual void SetLocked(bool aLocked)
Definition: board_item.h:330
PCB_LAYER_ID m_layer
Definition: board_item.h:438
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition: board_item.h:290
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
Definition: board_item.cpp:47
FOOTPRINT * GetParentFootprint() const
Definition: board_item.cpp:298
virtual bool IsLocked() const
Definition: board_item.cpp:75
BOARD_ITEM_CONTAINER * GetParent() const
Definition: board_item.h:217
virtual int BoardCopperLayerCount() const
Return the total number of copper layers for the board that this item resides on.
Definition: board_item.cpp:117
virtual wxString layerMaskDescribe() const
Return a string (to be shown to the user) describing a layer mask.
Definition: board_item.cpp:166
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:295
LSET GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition: board.cpp:820
LSET GetVisibleLayers() const
A proxy function that calls the correspondent function in m_BoardSettings.
Definition: board.cpp:834
int GetMaxClearanceValue() const
Returns the maximum clearance value for any object on the board.
Definition: board.cpp:943
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:937
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition: box2.h:558
constexpr BOX2< Vec > & Normalize()
Ensure that the height and width are positive.
Definition: box2.h:146
constexpr size_type GetWidth() const
Definition: box2.h:214
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition: box2.h:658
constexpr size_type GetHeight() const
Definition: box2.h:215
constexpr bool Contains(const Vec &aPoint) const
Definition: box2.h:168
constexpr const Vec & GetOrigin() const
Definition: box2.h:210
constexpr const SizeVec & GetSize() const
Definition: box2.h:206
constexpr bool Intersects(const BOX2< Vec > &aRect) const
Definition: box2.h:311
wxString GetName() const
Definition: drc_rule.h:160
MINOPTMAX< int > & Value()
Definition: drc_rule.h:153
double AsDegrees() const
Definition: eda_angle.h:113
bool IsZero() const
Definition: eda_angle.h:133
EDA_ANGLE Normalize180()
Definition: eda_angle.h:260
The base class for create windows for drawing purpose.
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:89
virtual VECTOR2I GetPosition() const
Definition: eda_item.h:244
EDA_ITEM & operator=(const EDA_ITEM &aItem)
Assign the members of aItem to another object.
Definition: eda_item.cpp:261
const KIID m_Uuid
Definition: eda_item.h:490
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:101
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition: eda_item.h:129
virtual void SetParent(EDA_ITEM *aParent)
Definition: eda_item.h:104
EDA_ITEM * m_parent
Linked list: Link (parent struct).
Definition: eda_item.h:502
EDA_ITEM_FLAGS GetFlags() const
Definition: eda_item.h:130
void SetPolyShape(const SHAPE_POLY_SET &aShape)
Definition: eda_shape.h:299
virtual void SetFilled(bool aFlag)
Definition: eda_shape.h:108
ENUM_MAP & Map(T aValue, const wxString &aName)
Definition: property.h:686
static ENUM_MAP< T > & Instance()
Definition: property.h:680
ENUM_MAP & Undefined(T aValue)
Definition: property.h:693
wxPGChoices & Choices()
Definition: property.h:729
EDA_ANGLE GetOrientation() const
Definition: footprint.h:225
std::map< wxString, int > MapPadNumbersToNetTieGroups() const
Definition: footprint.cpp:3158
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition: footprint.h:234
bool IsNetTie() const
Definition: footprint.h:295
const wxString & GetReference() const
Definition: footprint.h:620
DRAWINGS & GraphicalItems()
Definition: footprint.h:207
Class that other classes need to inherit from, in order to be inspectable.
Definition: inspectable.h:37
Contains methods for drawing PCB-specific items.
Definition: pcb_painter.h:180
virtual PCB_RENDER_SETTINGS * GetSettings() override
Return a pointer to current settings that are going to be used when drawing items.
Definition: pcb_painter.h:185
PCB specific render settings.
Definition: pcb_painter.h:78
PCB_LAYER_ID GetPrimaryHighContrastLayer() const
Return the board layer which is in high-contrast mode.
bool GetHighContrast() const
static constexpr double LOD_HIDE
Return this constant from ViewGetLOD() to hide the item unconditionally.
Definition: view_item.h:174
static constexpr double LOD_SHOW
Return this constant from ViewGetLOD() to show the item unconditionally.
Definition: view_item.h:179
static constexpr double lodScaleForThreshold(int aWhatIu, int aThresholdIu)
Get the scale at which aWhatIu would be drawn at the same size as aThresholdIu on screen.
Definition: view_item.h:196
Hold a (potentially large) number of VIEW_ITEMs and renders them on a graphics device provided by the...
Definition: view.h:67
bool IsLayerVisible(int aLayer) const
Return information about visibility of a particular layer.
Definition: view.h:418
PAINTER * GetPainter() const
Return the painter object used by the view for drawing #VIEW_ITEMS.
Definition: view.h:216
Definition: kiid.h:49
wxString AsString() const
Definition: kiid.cpp:246
std::string AsStdString() const
Definition: kiid.cpp:252
LSET is a set of PCB_LAYER_IDs.
Definition: lset.h:37
LSET & Flip(int aCopperLayersCount=0)
Flip the layers in this set.
Definition: lset.cpp:469
LSEQ UIOrder() const
Return the copper, technical and user layers in the order shown in layer widget.
Definition: lset.cpp:699
static LSET FrontBoardTechMask()
Return a mask holding technical layers used in a board fabrication (no CU layer) on front side.
Definition: lset.cpp:621
static LSET InternalCuMask()
Return a complete set of internal copper layers which is all Cu layers except F_Cu and B_Cu.
Definition: lset.cpp:553
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:564
static LSET PhysicalLayersMask()
Return a mask holding all layers which are physically realized.
Definition: lset.cpp:650
LSEQ Seq(const LSEQ &aSequence) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition: lset.cpp:297
static LSET BackBoardTechMask()
Return a mask holding technical layers used in a board fabrication (no CU layer) on Back side.
Definition: lset.cpp:607
T Min() const
Definition: minoptmax.h:33
bool HasMin() const
Definition: minoptmax.h:37
static const int UNCONNECTED
Constant that holds the "unconnected net" number (typically 0) all items "connected" to this net are ...
Definition: netinfo.h:381
std::optional< int > & Clearance(PCB_LAYER_ID aLayer=F_Cu)
Definition: padstack.cpp:1181
bool Deserialize(const google::protobuf::Any &aContainer) override
Deserializes the given protobuf message into this object.
Definition: padstack.cpp:211
double Similarity(const PADSTACK &aOther) const
Return a measure of how likely the other object is to represent the same object.
Definition: padstack.cpp:614
int RoundRectRadius(PCB_LAYER_ID aLayer) const
Definition: padstack.cpp:1134
std::optional< double > & SolderPasteMarginRatio(PCB_LAYER_ID aLayer=F_Cu)
Definition: padstack.cpp:1220
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::...
Definition: padstack.cpp:879
void SetRoundRectRadiusRatio(double aRatio, PCB_LAYER_ID aLayer)
Definition: padstack.cpp:1128
std::optional< int > & ThermalSpokeWidth(PCB_LAYER_ID aLayer=F_Cu)
Definition: padstack.cpp:1246
std::optional< int > & SolderPasteMargin(PCB_LAYER_ID aLayer=F_Cu)
Definition: padstack.cpp:1207
void SetOrientation(EDA_ANGLE aAngle)
Definition: padstack.h:294
std::optional< int > & SolderMaskMargin(PCB_LAYER_ID aLayer=F_Cu)
Definition: padstack.cpp:1193
void SetChamferRatio(double aRatio, PCB_LAYER_ID aLayer)
Definition: padstack.cpp:1157
const LSET & LayerSet() const
Definition: padstack.h:274
PCB_LAYER_ID EffectiveLayerFor(PCB_LAYER_ID aLayer) const
Determines which geometry layer should be used for the given input layer.
Definition: padstack.cpp:931
void SetShape(PAD_SHAPE aShape, PCB_LAYER_ID aLayer)
Definition: padstack.cpp:1053
VECTOR2I & TrapezoidDeltaSize(PCB_LAYER_ID aLayer)
Definition: padstack.cpp:1110
VECTOR2I & Offset(PCB_LAYER_ID aLayer)
Definition: padstack.cpp:1086
void FlipLayers(int aCopperLayerCount)
Flips the padstack layers in the case that the pad's parent footprint is flipped to the other side of...
Definition: padstack.cpp:717
void SetChamferPositions(int aPositions, PCB_LAYER_ID aLayer)
Definition: padstack.cpp:1175
void SetRoundRectRadius(double aRadius, PCB_LAYER_ID aLayer)
Definition: padstack.cpp:1141
UNCONNECTED_LAYER_MODE UnconnectedLayerMode() const
Definition: padstack.h:306
std::optional< int > & ThermalGap(PCB_LAYER_ID aLayer=F_Cu)
Definition: padstack.cpp:1258
PAD_SHAPE Shape(PCB_LAYER_ID aLayer) const
Definition: padstack.cpp:1047
DRILL_PROPS & Drill()
Definition: padstack.h:300
static int Compare(const PADSTACK *aPadstackRef, const PADSTACK *aPadstackCmp)
Compare two padstacks and return 0 if they are equal.
Definition: padstack.cpp:535
int & ChamferPositions(PCB_LAYER_ID aLayer)
Definition: padstack.cpp:1163
const VECTOR2I & Size(PCB_LAYER_ID aLayer) const
Definition: padstack.cpp:1068
@ NORMAL
Shape is the same on all layers.
UNCONNECTED_LAYER_MODE
! Whether or not to remove the copper shape for unconnected layers
Definition: padstack.h:151
PCB_LAYER_ID StartLayer() const
Definition: padstack.cpp:705
MODE Mode() const
Definition: padstack.h:287
void Serialize(google::protobuf::Any &aContainer) const override
Serializes this object to the given Any message.
Definition: padstack.cpp:427
void SetSize(const VECTOR2I &aSize, PCB_LAYER_ID aLayer)
Definition: padstack.cpp:1059
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
Definition: padstack.h:144
EDA_ANGLE GetOrientation() const
Definition: padstack.h:293
void SetLayerSet(const LSET &aSet)
Definition: padstack.h:276
std::optional< ZONE_CONNECTION > & ZoneConnection(PCB_LAYER_ID aLayer=F_Cu)
Definition: padstack.cpp:1234
std::vector< std::shared_ptr< PCB_SHAPE > > & Primitives(PCB_LAYER_ID aLayer)
Definition: padstack.cpp:1295
Definition: pad.h:54
void SetAnchorPadShape(PCB_LAYER_ID aLayer, PAD_SHAPE aShape)
Set the shape of the anchor pad for custom shaped pads.
Definition: pad.h:240
bool IsAperturePad() const
Definition: pad.h:445
void SetAttribute(PAD_ATTRIB aAttribute)
Definition: pad.cpp:835
int GetOwnClearance(PCB_LAYER_ID aLayer, wxString *aSource=nullptr) const override
Return the pad's "own" clearance in internal units.
Definition: pad.cpp:1099
void CheckPad(UNITS_PROVIDER *aUnitsProvider, bool aForPadProperties, const std::function< void(int aErrorCode, const wxString &aMsg)> &aErrorHandler) const
Definition: pad.cpp:2162
PAD(FOOTPRINT *parent)
Definition: pad.cpp:71
virtual void swapData(BOARD_ITEM *aImage) override
Definition: pad.cpp:1892
PAD_PROP GetProperty() const
Definition: pad.h:441
void Serialize(google::protobuf::Any &aContainer) const override
Serializes this object to the given Any message.
Definition: pad.cpp:139
double GetFrontRoundRectRadiusRatio() const
Definition: pad.h:670
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:2272
std::optional< int > GetClearanceOverrides(wxString *aSource) const override
Return any clearance overrides set in the "classic" (ie: pre-rule) system.
Definition: pad.cpp:1087
void SetPinType(const wxString &aType)
Set the pad electrical type.
Definition: pad.h:150
LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: pad.h:435
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:363
const ZONE_LAYER_OVERRIDE & GetZoneLayerOverride(PCB_LAYER_ID aLayer) const
Definition: pad.cpp:202
int GetSizeX() const
Definition: pad.h:278
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.
int GetRoundRectCornerRadius(PCB_LAYER_ID aLayer) const
Definition: pad.cpp:442
bool FlashLayer(int aLayer, bool aOnlyCheckIfPermitted=false) const
Check to see whether the pad should be flashed on the specific layer.
Definition: pad.cpp:341
void SetLocalThermalGapOverride(const std::optional< int > &aOverride)
Definition: pad.h:641
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:512
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:1272
const BOX2I GetBoundingBox() const override
The bounding box is cached, so this will be efficient most of the time.
Definition: pad.cpp:826
bool IsOnLayer(PCB_LAYER_ID aLayer) const override
Test to see if this object is on the given layer.
Definition: pad.h:780
int GetDrillSizeY() const
Definition: pad.h:307
std::optional< double > GetLocalSolderPasteMarginRatio() const
Definition: pad.h:468
void SetFrontShape(PAD_SHAPE aShape)
Definition: pad.cpp:883
const wxString & GetPinType() const
Definition: pad.h:151
void SetZoneLayerOverride(PCB_LAYER_ID aLayer, ZONE_LAYER_OVERRIDE aOverride)
Definition: pad.cpp:210
const VECTOR2I & GetDrillSize() const
Definition: pad.h:303
PAD_ATTRIB GetAttribute() const
Definition: pad.h:438
static LSET PTHMask()
layer set for a through hole pad
Definition: pad.cpp:270
static int Compare(const PAD *aPadRef, const PAD *aPadCmp)
Compare two pads and return 0 if they are equal.
Definition: pad.cpp:1487
void SetUnconnectedLayerMode(PADSTACK::UNCONNECTED_LAYER_MODE aMode)
Definition: pad.h:748
const wxString & GetPinFunction() const
Definition: pad.h:145
std::mutex m_shapesBuildingLock
Definition: pad.h:938
bool CanHaveNumber() const
Indicates whether or not the pad can have a number.
Definition: pad.cpp:217
void SetThermalSpokeAngle(const EDA_ANGLE &aAngle)
The orientation of the thermal spokes.
Definition: pad.h:613
wxString m_pinType
Definition: pad.h:929
bool Deserialize(const google::protobuf::Any &aContainer) override
Deserializes the given protobuf message into this object.
Definition: pad.cpp:164
const wxString & GetNumber() const
Definition: pad.h:134
double ViewGetLOD(int aLayer, const KIGFX::VIEW *aView) const override
Return the level of detail (LOD) of the item.
Definition: pad.cpp:1727
const VECTOR2I & GetDelta(PCB_LAYER_ID aLayer) const
Definition: pad.h:297
void SetFrontRoundRectRadiusRatio(double aRadiusScale)
Definition: pad.cpp:462
void BuildEffectiveShapes() const
Rebuild the effective shape cache (and bounding box and radius) for the pad and clears the dirty bit.
Definition: pad.cpp:587
PAD_SHAPE GetFrontShape() const
Definition: pad.h:198
void SetLocalSolderPasteMarginRatio(std::optional< double > aRatio)
Definition: pad.h:472
PAD & operator=(const PAD &aOther)
Definition: pad.cpp:122
void SetLocalThermalSpokeWidthOverride(std::optional< int > aWidth)
Set the width of the thermal spokes connecting the pad to a zone.
Definition: pad.h:597
void SetShape(PCB_LAYER_ID aLayer, PAD_SHAPE aShape)
Set the new shape of this pad.
Definition: pad.h:184
std::shared_ptr< SHAPE_SEGMENT > m_effectiveHoleShape
Definition: pad.h:941
bool IsLocked() const override
Definition: pad.cpp:231
VECTOR2I GetPosition() const override
Definition: pad.h:206
void SetProperty(PAD_PROP aProperty)
Definition: pad.cpp:902
void SetThermalSpokeAngleDegrees(double aAngle)
Definition: pad.h:623
EDA_ANGLE GetThermalSpokeAngle() const
Definition: pad.h:617
std::map< PCB_LAYER_ID, ZONE_LAYER_OVERRIDE > m_zoneLayerOverrides
Definition: pad.h:959
PAD_ATTRIB m_attribute
Definition: pad.h:952
void Flip(const VECTOR2I &VECTOR2I, FLIP_DIRECTION aFlipDirection) override
Flip this object, i.e.
Definition: pad.cpp:935
std::vector< PCB_SHAPE * > Recombine(bool aIsDryRun, int aMaxError)
Recombines the pad with other graphical shapes in the footprint.
Definition: pad.cpp:2024
PCB_LAYER_ID GetPrincipalLayer() const
Definition: pad.cpp:319
void SetDirty()
Definition: pad.h:427
static LSET UnplatedHoleMask()
layer set for a mechanical unplated through hole pad
Definition: pad.cpp:291
EDA_ITEM * Clone() const override
Create a duplicate of this item with linked list members set to NULL.
Definition: pad.cpp:1637
double GetOrientationDegrees() const
Definition: pad.h:414
void Rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle) override
Rotate this object.
Definition: pad.cpp:1502
PADSTACK m_padStack
Definition: pad.h:933
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:998
LAYER_SHAPE_MAP m_effectiveShapes
Definition: pad.h:940
bool IsNoConnectPad() const
Definition: pad.cpp:257
int GetDrillSizeX() const
Definition: pad.h:305
PAD_PROP m_property
Definition: pad.h:954
double GetRoundRectRadiusRatio(PCB_LAYER_ID aLayer) const
Definition: pad.h:663
PAD_SHAPE GetShape(PCB_LAYER_ID aLayer) const
Definition: pad.h:193
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:1917
void SetNumber(const wxString &aNumber)
Set the pad number (note that it can be alphanumeric, such as the array reference "AA12").
Definition: pad.h:133
void SetFrontRoundRectRadiusSize(int aRadius)
Definition: pad.cpp:472
wxString ShowPadAttr() const
Definition: pad.cpp:1527
void AddPrimitive(PCB_LAYER_ID aLayer, PCB_SHAPE *aPrimitive)
Add item to the custom shape primitives list.
void SetDrillShape(PAD_DRILL_SHAPE aShape)
Definition: pad.cpp:431
int m_effectiveBoundingRadius
Definition: pad.h:947
void SetLocalSolderMaskMargin(std::optional< int > aMargin)
Definition: pad.h:457
void SetLocalZoneConnection(ZONE_CONNECTION aType)
Definition: pad.h:477
void SetChamferRectRatio(PCB_LAYER_ID aLayer, double aChamferScale)
Has meaning only for chamfered rectangular pads.
Definition: pad.cpp:492
int GetSolderMaskExpansion(PCB_LAYER_ID aLayer) const
Definition: pad.cpp:1125
std::optional< int > GetLocalClearance() const override
Return any local clearances set in the "classic" (ie: pre-rule) system.
Definition: pad.h:453
void ImportSettingsFrom(const PAD &aMasterPad)
Import the pad settings from aMasterPad.
Definition: pad.cpp:1821
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:2457
bool IsOnCopperLayer() const override
Definition: pad.cpp:1031
void SetDrillSizeX(const int aX)
Definition: pad.cpp:420
void SetPadstack(const PADSTACK &aPadstack)
Definition: pad.h:321
void SetPosition(const VECTOR2I &aPos) override
Definition: pad.h:200
const SHAPE_COMPOUND & buildEffectiveShape(PCB_LAYER_ID aLayer) const
Definition: pad.cpp:623
const PADSTACK & Padstack() const
Definition: pad.h:319
const VECTOR2I & GetOffset(PCB_LAYER_ID aLayer) const
Definition: pad.h:315
VECTOR2I m_pos
Definition: pad.h:931
void BuildEffectivePolygon(ERROR_LOC aErrorLoc=ERROR_INSIDE) const
Definition: pad.cpp:779
static LSET ConnSMDMask()
layer set for a SMD pad on Front layer used for edge board connectors
Definition: pad.cpp:284
void SetDrillSize(const VECTOR2I &aSize)
Definition: pad.h:302
bool IsFreePad() const
Definition: pad.cpp:263
EDA_ANGLE GetOrientation() const
Return the rotation angle of the pad.
Definition: pad.h:406
void SetSize(PCB_LAYER_ID aLayer, const VECTOR2I &aSize)
Definition: pad.h:257
PADSTACK::CUSTOM_SHAPE_ZONE_MODE GetCustomShapeInZoneOpt() const
Definition: pad.h:219
PAD_DRILL_SHAPE GetDrillShape() const
Definition: pad.h:420
int GetChamferPositions(PCB_LAYER_ID aLayer) const
Definition: pad.h:703
static LSET ApertureMask()
layer set for an aperture pad
Definition: pad.cpp:298
virtual const BOX2I ViewBBox() const override
Return the bounding box of the item covering all its layers.
Definition: pad.cpp:1786
bool m_shapesDirty
Definition: pad.h:937
std::mutex m_polyBuildingLock
Definition: pad.h:945
void SetRoundRectCornerRadius(PCB_LAYER_ID aLayer, double aRadius)
Has meaning only for rounded rectangle pads.
Definition: pad.cpp:448
static LSET SMDMask()
layer set for a SMD pad on Front layer
Definition: pad.cpp:277
std::optional< int > GetLocalSolderPasteMargin() const
Definition: pad.h:462
int GetFrontRoundRectRadiusSize() const
Definition: pad.cpp:482
const std::shared_ptr< SHAPE_POLY_SET > & GetEffectivePolygon(PCB_LAYER_ID aLayer, ERROR_LOC aErrorLoc=ERROR_INSIDE) const
Definition: pad.cpp:500
std::optional< int > GetLocalSolderMaskMargin() const
Definition: pad.h:456
void SetLocalSolderPasteMargin(std::optional< int > aMargin)
Definition: pad.h:463
int GetSizeY() const
Definition: pad.h:289
std::optional< int > GetLocalThermalGapOverride() const
Definition: pad.h:637
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition: pad.cpp:313
wxString GetItemDescription(UNITS_PROVIDER *aUnitsProvider, bool aFull) const override
Return a user-visible description string of this item.
Definition: pad.cpp:1540
void SetPinFunction(const wxString &aName)
Set the pad function (pin name in schematic)
Definition: pad.h:144
EDA_ANGLE GetFPRelativeOrientation() const
Definition: pad.cpp:926
double GetChamferRectRatio(PCB_LAYER_ID aLayer) const
Definition: pad.h:686
bool m_polyDirty[2]
Definition: pad.h:944
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:1405
void SetFPRelativeOrientation(const EDA_ANGLE &aAngle)
Definition: pad.cpp:917
void SetCustomShapeInZoneOpt(PADSTACK::CUSTOM_SHAPE_ZONE_MODE aOption)
Set the option for the custom pad shape to use as clearance area in copper zones.
Definition: pad.h:229
int GetBoundingRadius() const
Return the radius of a minimum sized circle which fully encloses this pad.
Definition: pad.cpp:578
void ClearZoneLayerOverrides()
Definition: pad.cpp:193
void SetOrientation(const EDA_ANGLE &aAngle)
Set the rotation angle of the pad.
Definition: pad.cpp:910
std::mutex m_zoneLayerOverridesMutex
Definition: pad.h:958
std::optional< int > GetLocalThermalSpokeWidthOverride() const
Definition: pad.h:601
PADSTACK::UNCONNECTED_LAYER_MODE GetUnconnectedLayerMode() const
Definition: pad.h:753
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:1171
BITMAPS GetMenuImage() const override
Return a pointer to an image to be used in menus.
Definition: pad.cpp:1631
wxString m_number
Definition: pad.h:927
LAYER_POLYGON_MAP m_effectivePolygons
Definition: pad.h:946
void SetLocalClearance(std::optional< int > aClearance)
Definition: pad.h:454
int GetSubRatsnest() const
Definition: pad.h:711
void SetSizeX(const int aX)
Definition: pad.h:269
ZONE_CONNECTION GetLocalZoneConnection() const
Definition: pad.h:478
int m_lengthPadToDie
Definition: pad.h:956
void SetDrillSizeY(const int aY)
Definition: pad.h:306
double GetThermalSpokeAngleDegrees() const
Definition: pad.h:627
VECTOR2I ShapePos(PCB_LAYER_ID aLayer) const
Definition: pad.cpp:1016
std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape() const override
Return a SHAPE_SEGMENT object representing the pad's hole.
Definition: pad.cpp:569
void SetOrientationDegrees(double aOrientation)
Definition: pad.h:410
ZONE_CONNECTION GetZoneConnectionOverrides(wxString *aSource=nullptr) const
Definition: pad.cpp:1234
int GetLocalThermalGapOverride(wxString *aSource) const
Definition: pad.cpp:1263
void SetLayerSet(const LSET &aLayers) override
Definition: pad.h:434
bool SharesNetTieGroup(const PAD *aOther) const
Definition: pad.cpp:240
PAD_SHAPE GetAnchorPadShape(PCB_LAYER_ID aLayer) const
Definition: pad.h:211
void SetRoundRectRadiusRatio(PCB_LAYER_ID aLayer, double aRadiusScale)
Has meaning only for rounded rectangle pads.
Definition: pad.cpp:454
void SetSubRatsnest(int aSubRatsnest)
Definition: pad.h:712
int GetLocalSpokeWidthOverride(wxString *aSource=nullptr) const
Definition: pad.cpp:1254
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:1900
void SetPadToDieLength(int aLength)
Definition: pad.h:450
bool IsFlipped() const
Definition: pad.cpp:305
bool operator==(const PAD &aOther) const
Definition: pad.cpp:2442
wxString ShowPadShape(PCB_LAYER_ID aLayer) const
Definition: pad.cpp:1511
void SetSizeY(const int aY)
Definition: pad.h:280
int GetPadToDieLength() const
Definition: pad.h:451
BOX2I m_effectiveBoundingBox
Definition: pad.h:939
const VECTOR2I & GetSize(PCB_LAYER_ID aLayer) const
Definition: pad.h:262
virtual std::vector< int > ViewGetLayers() const override
Return the all the layers within the VIEW the object is painted on.
Definition: pad.cpp:1655
void Rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle) override
Rotate this object.
Definition: pcb_shape.cpp:480
bool IsProxyItem() const override
Definition: pcb_shape.h:114
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.
Definition: pcb_shape.cpp:771
void Move(const VECTOR2I &aMoveVector) override
Move this object.
Definition: pcb_shape.cpp:387
void SetStroke(const STROKE_PARAMS &aStroke) override
Definition: pcb_shape.h:90
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition: pcb_shape.h:69
PROPERTY_BASE & SetAvailableFunc(std::function< bool(INSPECTABLE *)> aFunc)
Set a callback function to determine whether an object provides this property.
Definition: property.h:257
PROPERTY_BASE & SetWriteableFunc(std::function< bool(INSPECTABLE *)> aFunc)
Definition: property.h:268
PROPERTY_BASE & SetValidator(PROPERTY_VALIDATOR_FN &&aValidator)
Definition: property.h:330
PROPERTY_BASE & SetIsHiddenFromLibraryEditors(bool aIsHidden=true)
Definition: property.h:314
Provide class metadata.Helper macro to map type hashes to names.
Definition: property_mgr.h:85
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()
Definition: property_mgr.h:87
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.
static VALIDATOR_RESULT PositiveIntValidator(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.
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.
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 BooleanIntersection(const SHAPE_POLY_SET &b)
Perform boolean polyset intersection.
int OutlineCount() const
Return the number of outlines in the set.
void Move(const VECTOR2I &aVector) override
void BooleanSubtract(const SHAPE_POLY_SET &b)
Perform boolean polyset difference.
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
Represent a simple polygon consisting of a zero-thickness closed chain of connected line segments.
Definition: shape_simple.h:42
An abstract shape on 2D plane.
Definition: shape.h:126
Simple container to manage line stroke parameters.
Definition: stroke_params.h:94
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 std::vector< PCB_SHAPE * > &aList, unsigned aLimit)
Search for a PCB_SHAPE matching a given end point or start point in a list.
#define _HKI(x)
const int minSize
Push and Shove router track width and via size dialog.
@ DRCE_PADSTACK
Definition: drc_item.h:62
@ DRCE_PADSTACK_INVALID
Definition: drc_item.h:63
@ DRCE_PAD_TH_WITH_NO_HOLE
Definition: drc_item.h:84
@ CLEARANCE_CONSTRAINT
Definition: drc_rule.h:49
@ HOLE_CLEARANCE_CONSTRAINT
Definition: drc_rule.h:51
#define _(s)
static constexpr EDA_ANGLE ANGLE_0
Definition: eda_angle.h:401
#define FOOTPRINT_EDIT_FRAME_NAME
#define PCB_EDIT_FRAME_NAME
#define ENTERED
indicates a group has been entered
#define SKIP_STRUCT
flag indicating that the structure should be ignored
Some functions to handle hotkeys in KiCad.
@ LAYER_PAD_FR_NETNAMES
Additional netnames layers (not associated with a PCB layer).
Definition: layer_ids.h:199
@ LAYER_PAD_BK_NETNAMES
Definition: layer_ids.h:200
@ LAYER_PAD_NETNAMES
Definition: layer_ids.h:201
bool IsFrontLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a front layer.
Definition: layer_ids.h:719
FLASHING
Enum used during connectivity building to ensure we do not query connectivity while building the data...
Definition: layer_ids.h:183
bool IsBackLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a back layer.
Definition: layer_ids.h:742
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition: layer_ids.h:617
@ LAYER_PAD_COPPER_START
Virtual layers for pad copper on a given copper layer.
Definition: layer_ids.h:330
@ LAYER_FOOTPRINTS_FR
Show footprints on front.
Definition: layer_ids.h:257
@ LAYER_NON_PLATEDHOLES
Draw usual through hole vias.
Definition: layer_ids.h:237
@ LAYER_PADS
Meta control for all pads opacity/visibility (color ignored).
Definition: layer_ids.h:290
@ LAYER_PAD_PLATEDHOLES
to draw pad holes (plated)
Definition: layer_ids.h:269
@ LAYER_CLEARANCE_START
Virtual layers for pad/via/track clearance outlines for a given copper layer.
Definition: layer_ids.h:338
@ LAYER_FOOTPRINTS_BK
Show footprints on back.
Definition: layer_ids.h:258
@ LAYER_PAD_HOLEWALLS
Definition: layer_ids.h:295
bool IsNetnameLayer(int aLayer)
Test whether a layer is a netname layer.
Definition: layer_ids.h:808
bool IsHoleLayer(int aLayer)
Definition: layer_ids.h:678
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
Message panel definition file.
constexpr int Mils2IU(const EDA_IU_SCALE &aIuScale, int mils)
Definition: eda_units.h:159
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:84
KICOMMON_API void PackVector2(types::Vector2 &aOutput, const VECTOR2I &aInput)
Definition: api_utils.cpp:77
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:390
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:81
PAD_SHAPE
The set of pad shapes, used with PAD::{Set,Get}Shape()
Definition: padstack.h:52
PAD_PROP
The set of pad properties used in Gerber files (Draw files, and P&P files) to define some properties ...
Definition: padstack.h:98
#define TYPE_HASH(x)
Definition: property.h:71
#define NO_SETTER(owner, type)
Definition: property.h:791
#define ENUM_TO_WXANY(type)
Macro to define read-only fields (no setter method available)
Definition: property.h:782
#define REGISTER_TYPE(x)
Definition: property_mgr.h:371
wxString UnescapeString(const wxString &aSource)
constexpr int mmToIU(double mm) const
Definition: base_units.h:88
VECTOR2I size
Drill diameter (x == y) or slot dimensions (x != y)
Definition: padstack.h:240
PAD_DRILL_SHAPE shape
Definition: padstack.h:241
PAD_DESC()
Definition: pad.cpp:2483
int clearance
constexpr int delta
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
#define ZONE_THICKNESS_MIN_VALUE_MM
Definition: zones.h:36