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