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