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