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 <dick@softplc.com>
6  * Copyright (C) 1992-2021 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 <core/mirror.h>
29 #include <math/util.h>      // for KiROUND
30 #include <eda_draw_frame.h>
31 #include <geometry/shape_circle.h>
32 #include <geometry/shape_segment.h>
33 #include <geometry/shape_simple.h>
34 #include <geometry/shape_rect.h>
35 #include <geometry/shape_compound.h>
36 #include <string_utils.h>
37 #include <i18n_utility.h>
38 #include <view/view.h>
39 #include <board.h>
40 #include <board_connected_item.h>
41 #include <board_design_settings.h>
42 #include <footprint.h>
43 #include <pad.h>
44 #include <pcb_shape.h>
45 #include <connectivity/connectivity_data.h>
46 #include <convert_to_biu.h>
47 #include <convert_basic_shapes_to_polygon.h>
48 #include <widgets/msgpanel.h>
49 #include <pcb_painter.h>
50 #include <wx/log.h>
51 
52 #include <memory>
53 #include <macros.h>
54 
55 using KIGFX::PCB_PAINTER;
56 using KIGFX::PCB_RENDER_SETTINGS;
57 
58 
PAD(FOOTPRINT * parent)59 PAD::PAD( FOOTPRINT* parent ) :
60     BOARD_CONNECTED_ITEM( parent, PCB_PAD_T )
61 {
62     m_size.x = m_size.y   = Mils2iu( 60 );  // Default pad size 60 mils.
63     m_drill.x = m_drill.y = Mils2iu( 30 );  // Default drill size 30 mils.
64     m_orient              = 0;              // Pad rotation in 1/10 degrees.
65     m_lengthPadToDie      = 0;
66 
67     if( m_parent && m_parent->Type() == PCB_FOOTPRINT_T )
68         m_pos = GetParent()->GetPosition();
69 
70     SetShape( PAD_SHAPE::CIRCLE );               // Default pad shape is PAD_CIRCLE.
71     SetAnchorPadShape( PAD_SHAPE::CIRCLE );      // Default shape for custom shaped pads
72                                                  // is PAD_CIRCLE.
73     SetDrillShape( PAD_DRILL_SHAPE_CIRCLE );     // Default pad drill shape is a circle.
74     m_attribute           = PAD_ATTRIB::PTH;     // Default pad type is plated through hole
75     SetProperty( PAD_PROP::NONE );               // no special fabrication property
76     m_localClearance      = 0;
77     m_localSolderMaskMargin  = 0;
78     m_localSolderPasteMargin = 0;
79     m_localSolderPasteMarginRatio = 0.0;
80 
81     // Parameters for round rect only:
82     m_roundedCornerScale = 0.25;                 // from IPC-7351C standard
83 
84     // Parameters for chamfered rect only:
85     m_chamferScale = 0.2;                        // Size of chamfer: ratio of smallest of X,Y size
86     m_chamferPositions  = RECT_NO_CHAMFER;       // No chamfered corner
87 
88     m_zoneConnection = ZONE_CONNECTION::INHERITED; // Use parent setting by default
89     m_thermalWidth = 0;                            // Use parent setting by default
90     m_thermalGap = 0;                              // Use parent setting by default
91 
92     m_customShapeClearanceArea = CUST_PAD_SHAPE_IN_ZONE_OUTLINE;
93 
94     // Set layers mask to default for a standard thru hole pad.
95     m_layerMask = PTHMask();
96 
97     SetSubRatsnest( 0 );                       // used in ratsnest calculations
98 
99     SetDirty();
100     m_effectiveBoundingRadius = 0;
101     m_removeUnconnectedLayer = false;
102     m_keepTopBottomLayer = true;
103 }
104 
105 
PAD(const PAD & aOther)106 PAD::PAD( const PAD& aOther ) :
107     BOARD_CONNECTED_ITEM( aOther.GetParent(), PCB_PAD_T )
108 {
109     PAD::operator=( aOther );
110 
111     const_cast<KIID&>( m_Uuid ) = aOther.m_Uuid;
112 }
113 
114 
operator =(const PAD & aOther)115 PAD& PAD::operator=( const PAD &aOther )
116 {
117     BOARD_CONNECTED_ITEM::operator=( aOther );
118 
119     ImportSettingsFrom( aOther );
120     SetPadToDieLength( aOther.GetPadToDieLength() );
121     SetPosition( aOther.GetPosition() );
122     SetPos0( aOther.GetPos0() );
123     SetNumber( aOther.GetNumber() );
124     SetPinType( aOther.GetPinType() );
125     SetPinFunction( aOther.GetPinFunction() );
126     SetSubRatsnest( aOther.GetSubRatsnest() );
127     m_effectiveBoundingRadius = aOther.m_effectiveBoundingRadius;
128     m_removeUnconnectedLayer = aOther.m_removeUnconnectedLayer;
129     m_keepTopBottomLayer = aOther.m_keepTopBottomLayer;
130 
131     return *this;
132 }
133 
134 
CanHaveNumber() const135 bool PAD::CanHaveNumber() const
136 {
137     // Aperture pads don't get a number
138     if( IsAperturePad() )
139         return false;
140 
141     // NPTH pads don't get numbers
142     if( GetAttribute() == PAD_ATTRIB::NPTH )
143         return false;
144 
145     return true;
146 }
147 
148 
IsLocked() const149 bool PAD::IsLocked() const
150 {
151     if( GetParent() && GetParent()->IsLocked() )
152         return true;
153 
154     return BOARD_ITEM::IsLocked();
155 };
156 
157 
PTHMask()158 LSET PAD::PTHMask()
159 {
160     static LSET saved = LSET::AllCuMask() | LSET( 2, F_Mask, B_Mask );
161     return saved;
162 }
163 
164 
SMDMask()165 LSET PAD::SMDMask()
166 {
167     static LSET saved( 3, F_Cu, F_Paste, F_Mask );
168     return saved;
169 }
170 
171 
ConnSMDMask()172 LSET PAD::ConnSMDMask()
173 {
174     static LSET saved( 2, F_Cu, F_Mask );
175     return saved;
176 }
177 
178 
UnplatedHoleMask()179 LSET PAD::UnplatedHoleMask()
180 {
181     static LSET saved = LSET( 4, F_Cu, B_Cu, F_Mask, B_Mask );
182     return saved;
183 }
184 
185 
ApertureMask()186 LSET PAD::ApertureMask()
187 {
188     static LSET saved( 1, F_Paste );
189     return saved;
190 }
191 
192 
IsFlipped() const193 bool PAD::IsFlipped() const
194 {
195     if( GetParent() &&  GetParent()->GetLayer() == B_Cu )
196         return true;
197     return false;
198 }
199 
200 
FlashLayer(LSET aLayers) const201 bool PAD::FlashLayer( LSET aLayers ) const
202 {
203     for( auto layer : aLayers.Seq() )
204     {
205         if( FlashLayer( layer ) )
206             return true;
207     }
208 
209     return false;
210 }
211 
212 
FlashLayer(int aLayer) const213 bool PAD::FlashLayer( int aLayer ) const
214 {
215     std::vector<KICAD_T> types
216     { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T, PCB_PAD_T, PCB_ZONE_T, PCB_FP_ZONE_T };
217 
218     const BOARD* board = GetBoard();
219 
220     switch( GetAttribute() )
221     {
222     case PAD_ATTRIB::PTH:
223         if( aLayer == UNDEFINED_LAYER )
224             return true;
225 
226         /// Heat sink pads always get copper
227         if( GetProperty() == PAD_PROP::HEATSINK )
228             return IsOnLayer( static_cast<PCB_LAYER_ID>( aLayer ) );
229 
230         if( !m_removeUnconnectedLayer )
231             return IsOnLayer( static_cast<PCB_LAYER_ID>( aLayer ) );
232 
233         // Plated through hole pads need copper on the top/bottom layers for proper soldering
234         // Unless the user has removed them in the pad dialog
235         if( m_keepTopBottomLayer && ( aLayer == F_Cu || aLayer == B_Cu ) )
236             return IsOnLayer( static_cast<PCB_LAYER_ID>( aLayer ) );
237 
238         return board && board->GetConnectivity()->IsConnectedOnLayer( this,
239                                                                       static_cast<int>( aLayer ),
240                                                                       types );
241 
242     case PAD_ATTRIB::NPTH:
243         if( GetShape() == PAD_SHAPE::CIRCLE && GetDrillShape() == PAD_DRILL_SHAPE_CIRCLE )
244         {
245             if( GetOffset() == wxPoint( 0, 0 ) && GetDrillSize().x >= GetSize().x )
246                 return false;
247         }
248         else if( GetShape() == PAD_SHAPE::OVAL && GetDrillShape() == PAD_DRILL_SHAPE_OBLONG )
249         {
250             if( GetOffset() == wxPoint( 0, 0 )
251                     && GetDrillSize().x >= GetSize().x && GetDrillSize().y >= GetSize().y )
252             {
253                 return false;
254             }
255         }
256 
257         KI_FALLTHROUGH;
258 
259     case PAD_ATTRIB::SMD:
260     case PAD_ATTRIB::CONN:
261     default:
262         if( aLayer == UNDEFINED_LAYER )
263             return true;
264 
265         return IsOnLayer( static_cast<PCB_LAYER_ID>( aLayer ) );
266     }
267 }
268 
269 
GetRoundRectCornerRadius() const270 int PAD::GetRoundRectCornerRadius() const
271 {
272     return KiROUND( std::min( m_size.x, m_size.y ) * m_roundedCornerScale );
273 }
274 
275 
SetRoundRectCornerRadius(double aRadius)276 void PAD::SetRoundRectCornerRadius( double aRadius )
277 {
278     int min_r = std::min( m_size.x, m_size.y );
279 
280     if( min_r > 0 )
281         SetRoundRectRadiusRatio( aRadius / min_r );
282 }
283 
284 
SetRoundRectRadiusRatio(double aRadiusScale)285 void PAD::SetRoundRectRadiusRatio( double aRadiusScale )
286 {
287     m_roundedCornerScale = std::max( 0.0, std::min( aRadiusScale, 0.5 ) );
288 
289     SetDirty();
290 }
291 
292 
SetChamferRectRatio(double aChamferScale)293 void PAD::SetChamferRectRatio( double aChamferScale )
294 {
295     m_chamferScale = std::max( 0.0, std::min( aChamferScale, 0.5 ) );
296 
297     SetDirty();
298 }
299 
300 
GetEffectivePolygon() const301 const std::shared_ptr<SHAPE_POLY_SET>& PAD::GetEffectivePolygon() const
302 {
303     if( m_polyDirty )
304         BuildEffectivePolygon();
305 
306     return m_effectivePolygon;
307 }
308 
309 
GetEffectiveShape(PCB_LAYER_ID aLayer) const310 std::shared_ptr<SHAPE> PAD::GetEffectiveShape( PCB_LAYER_ID aLayer ) const
311 {
312     if( m_shapesDirty )
313         BuildEffectiveShapes( aLayer );
314 
315     return m_effectiveShape;
316 }
317 
318 
GetEffectiveHoleShape() const319 const SHAPE_SEGMENT* PAD::GetEffectiveHoleShape() const
320 {
321     if( m_shapesDirty )
322         BuildEffectiveShapes( UNDEFINED_LAYER );
323 
324     return m_effectiveHoleShape.get();
325 }
326 
327 
GetBoundingRadius() const328 int PAD::GetBoundingRadius() const
329 {
330     if( m_polyDirty )
331         BuildEffectivePolygon();
332 
333     return m_effectiveBoundingRadius;
334 }
335 
336 
BuildEffectiveShapes(PCB_LAYER_ID aLayer) const337 void PAD::BuildEffectiveShapes( PCB_LAYER_ID aLayer ) const
338 {
339     std::lock_guard<std::mutex> RAII_lock( m_shapesBuildingLock );
340 
341     // If we had to wait for the lock then we were probably waiting for someone else to
342     // finish rebuilding the shapes.  So check to see if they're clean now.
343     if( !m_shapesDirty )
344         return;
345 
346     const BOARD* board = GetBoard();
347     int          maxError = board ? board->GetDesignSettings().m_MaxError : ARC_HIGH_DEF;
348 
349     m_effectiveShape = std::make_shared<SHAPE_COMPOUND>();
350     m_effectiveHoleShape = nullptr;
351 
352     auto add = [this]( SHAPE* aShape )
353                {
354                    m_effectiveShape->AddShape( aShape );
355                };
356 
357     wxPoint shapePos = ShapePos();  // Fetch only once; rotation involves trig
358     PAD_SHAPE effectiveShape = GetShape();
359 
360     if( GetShape() == PAD_SHAPE::CUSTOM )
361         effectiveShape = GetAnchorPadShape();
362 
363     switch( effectiveShape )
364     {
365     case PAD_SHAPE::CIRCLE:
366         add( new SHAPE_CIRCLE( shapePos, m_size.x / 2 ) );
367         break;
368 
369     case PAD_SHAPE::OVAL:
370         if( m_size.x == m_size.y ) // the oval pad is in fact a circle
371         {
372             add( new SHAPE_CIRCLE( shapePos, m_size.x / 2 ) );
373         }
374         else
375         {
376             wxSize  half_size = m_size / 2;
377             int     half_width = std::min( half_size.x, half_size.y );
378             wxPoint half_len( half_size.x - half_width, half_size.y - half_width );
379             RotatePoint( &half_len, m_orient );
380             add( new SHAPE_SEGMENT( shapePos - half_len, shapePos + half_len, half_width * 2 ) );
381         }
382 
383         break;
384 
385     case PAD_SHAPE::RECT:
386     case PAD_SHAPE::TRAPEZOID:
387     case PAD_SHAPE::ROUNDRECT:
388     {
389         int     r = ( effectiveShape == PAD_SHAPE::ROUNDRECT ) ? GetRoundRectCornerRadius() : 0;
390         wxPoint half_size( m_size.x / 2, m_size.y / 2 );
391         wxSize  trap_delta( 0, 0 );
392 
393         if( r )
394         {
395             half_size -= wxPoint( r, r );
396 
397             // Avoid degenerated shapes (0 length segments) that always create issues
398             // For roundrect pad very near a circle, use only a circle
399             const int min_len = Millimeter2iu( 0.0001);
400 
401             if( half_size.x < min_len && half_size.y < min_len )
402             {
403                 add( new SHAPE_CIRCLE( shapePos, r ) );
404                 break;
405             }
406         }
407         else if( effectiveShape == PAD_SHAPE::TRAPEZOID )
408         {
409             trap_delta = m_deltaSize / 2;
410         }
411 
412         SHAPE_LINE_CHAIN corners;
413 
414         corners.Append( -half_size.x - trap_delta.y,  half_size.y + trap_delta.x );
415         corners.Append(  half_size.x + trap_delta.y,  half_size.y - trap_delta.x );
416         corners.Append(  half_size.x - trap_delta.y, -half_size.y + trap_delta.x );
417         corners.Append( -half_size.x + trap_delta.y, -half_size.y - trap_delta.x );
418 
419         corners.Rotate( -DECIDEG2RAD( m_orient ) );
420         corners.Move( shapePos );
421 
422         // GAL renders rectangles faster than 4-point polygons so it's worth checking if our
423         // body shape is a rectangle.
424         if( corners.PointCount() == 4
425                 &&
426                 ( ( corners.CPoint( 0 ).y == corners.CPoint( 1 ).y
427                     && corners.CPoint( 1 ).x == corners.CPoint( 2 ).x
428                     && corners.CPoint( 2 ).y == corners.CPoint( 3 ).y
429                     && corners.CPoint( 3 ).x == corners.CPoint( 0 ).x )
430                     ||
431                 ( corners.CPoint( 0 ).x == corners.CPoint( 1 ).x
432                     && corners.CPoint( 1 ).y == corners.CPoint( 2 ).y
433                     && corners.CPoint( 2 ).x == corners.CPoint( 3 ).x
434                     && corners.CPoint( 3 ).y == corners.CPoint( 0 ).y )
435                 )
436             )
437         {
438             int width  = std::abs( corners.CPoint( 2 ).x - corners.CPoint( 0 ).x );
439             int height = std::abs( corners.CPoint( 2 ).y - corners.CPoint( 0 ).y );
440             VECTOR2I pos( std::min( corners.CPoint( 2 ).x, corners.CPoint( 0 ).x ),
441                           std::min( corners.CPoint( 2 ).y, corners.CPoint( 0 ).y ) );
442 
443             add( new SHAPE_RECT( pos, width, height ) );
444         }
445         else
446         {
447             add( new SHAPE_SIMPLE( corners ) );
448         }
449 
450         if( r )
451         {
452             add( new SHAPE_SEGMENT( corners.CPoint( 0 ), corners.CPoint( 1 ), r * 2 ) );
453             add( new SHAPE_SEGMENT( corners.CPoint( 1 ), corners.CPoint( 2 ), r * 2 ) );
454             add( new SHAPE_SEGMENT( corners.CPoint( 2 ), corners.CPoint( 3 ), r * 2 ) );
455             add( new SHAPE_SEGMENT( corners.CPoint( 3 ), corners.CPoint( 0 ), r * 2 ) );
456         }
457     }
458         break;
459 
460     case PAD_SHAPE::CHAMFERED_RECT:
461     {
462         SHAPE_POLY_SET outline;
463 
464         TransformRoundChamferedRectToPolygon( outline, shapePos, GetSize(), m_orient,
465                                               GetRoundRectCornerRadius(), GetChamferRectRatio(),
466                                               GetChamferPositions(), 0, maxError, ERROR_INSIDE );
467 
468         add( new SHAPE_SIMPLE( outline.COutline( 0 ) ) );
469     }
470         break;
471 
472     default:
473         wxFAIL_MSG( "PAD::buildEffectiveShapes: Unsupported pad shape: "
474                     + PAD_SHAPE_T_asString( effectiveShape ) );
475         break;
476     }
477 
478     if( GetShape() == PAD_SHAPE::CUSTOM )
479     {
480         for( const std::shared_ptr<PCB_SHAPE>& primitive : m_editPrimitives )
481         {
482             for( SHAPE* shape : primitive->MakeEffectiveShapes() )
483             {
484                 shape->Rotate( -DECIDEG2RAD( m_orient ) );
485                 shape->Move( shapePos );
486                 add( shape );
487             }
488         }
489     }
490 
491     BOX2I bbox = m_effectiveShape->BBox();
492     m_effectiveBoundingBox = EDA_RECT( (wxPoint) bbox.GetPosition(),
493                                        wxSize( bbox.GetWidth(), bbox.GetHeight() ) );
494 
495     // Hole shape
496     wxSize  half_size = m_drill / 2;
497     int     half_width = std::min( half_size.x, half_size.y );
498     wxPoint half_len( half_size.x - half_width, half_size.y - half_width );
499 
500     RotatePoint( &half_len, m_orient );
501 
502     m_effectiveHoleShape = std::make_shared<SHAPE_SEGMENT>( m_pos - half_len, m_pos + half_len,
503                                                             half_width * 2 );
504     bbox = m_effectiveHoleShape->BBox();
505     m_effectiveBoundingBox.Merge( EDA_RECT( (wxPoint) bbox.GetPosition(),
506                                             wxSize( bbox.GetWidth(), bbox.GetHeight() ) ) );
507 
508     // All done
509     m_shapesDirty = false;
510 }
511 
512 
BuildEffectivePolygon() const513 void PAD::BuildEffectivePolygon() const
514 {
515     std::lock_guard<std::mutex> RAII_lock( m_polyBuildingLock );
516 
517     // If we had to wait for the lock then we were probably waiting for someone else to
518     // finish rebuilding the shapes.  So check to see if they're clean now.
519     if( !m_polyDirty )
520         return;
521 
522     const BOARD* board = GetBoard();
523     int          maxError = board ? board->GetDesignSettings().m_MaxError : ARC_HIGH_DEF;
524 
525     // Polygon
526     m_effectivePolygon = std::make_shared<SHAPE_POLY_SET>();
527     TransformShapeWithClearanceToPolygon( *m_effectivePolygon, UNDEFINED_LAYER, 0, maxError,
528                                           ERROR_INSIDE );
529 
530     // Bounding radius
531     //
532     // PADSTACKS TODO: these will both need to cycle through all layers to get the largest
533     // values....
534     m_effectiveBoundingRadius = 0;
535 
536     for( int cnt = 0; cnt < m_effectivePolygon->OutlineCount(); ++cnt )
537     {
538         const SHAPE_LINE_CHAIN& poly = m_effectivePolygon->COutline( cnt );
539 
540         for( int ii = 0; ii < poly.PointCount(); ++ii )
541         {
542             int dist = KiROUND( ( poly.CPoint( ii ) - m_pos ).EuclideanNorm() );
543             m_effectiveBoundingRadius = std::max( m_effectiveBoundingRadius, dist );
544         }
545     }
546 
547     // All done
548     m_polyDirty = false;
549 }
550 
551 
GetBoundingBox() const552 const EDA_RECT PAD::GetBoundingBox() const
553 {
554     if( m_shapesDirty )
555         BuildEffectiveShapes( UNDEFINED_LAYER );
556 
557     return m_effectiveBoundingBox;
558 }
559 
560 
SetDrawCoord()561 void PAD::SetDrawCoord()
562 {
563     FOOTPRINT* parentFootprint = static_cast<FOOTPRINT*>( m_parent );
564 
565     m_pos = m_pos0;
566 
567     if( parentFootprint == nullptr )
568         return;
569 
570     double angle = parentFootprint->GetOrientation();
571 
572     RotatePoint( &m_pos.x, &m_pos.y, angle );
573     m_pos += parentFootprint->GetPosition();
574 
575     SetDirty();
576 }
577 
578 
SetLocalCoord()579 void PAD::SetLocalCoord()
580 {
581     FOOTPRINT* parentFootprint = static_cast<FOOTPRINT*>( m_parent );
582 
583     if( parentFootprint == nullptr )
584     {
585         m_pos0 = m_pos;
586         return;
587     }
588 
589     m_pos0 = m_pos - parentFootprint->GetPosition();
590     RotatePoint( &m_pos0.x, &m_pos0.y, -parentFootprint->GetOrientation() );
591 }
592 
593 
SetAttribute(PAD_ATTRIB aAttribute)594 void PAD::SetAttribute( PAD_ATTRIB aAttribute )
595 {
596     m_attribute = aAttribute;
597 
598     if( aAttribute == PAD_ATTRIB::SMD )
599         m_drill = wxSize( 0, 0 );
600 
601     SetDirty();
602 }
603 
604 
SetProperty(PAD_PROP aProperty)605 void PAD::SetProperty( PAD_PROP aProperty )
606 {
607     m_property = aProperty;
608 
609     SetDirty();
610 }
611 
612 
SetOrientation(double aAngle)613 void PAD::SetOrientation( double aAngle )
614 {
615     NORMALIZE_ANGLE_POS( aAngle );
616     m_orient = aAngle;
617 
618     SetDirty();
619 }
620 
621 
Flip(const wxPoint & aCentre,bool aFlipLeftRight)622 void PAD::Flip( const wxPoint& aCentre, bool aFlipLeftRight )
623 {
624     if( aFlipLeftRight )
625     {
626         MIRROR( m_pos.x, aCentre.x );
627         MIRROR( m_pos0.x, 0 );
628         MIRROR( m_offset.x, 0 );
629         MIRROR( m_deltaSize.x, 0 );
630     }
631     else
632     {
633         MIRROR( m_pos.y, aCentre.y );
634         MIRROR( m_pos0.y, 0 );
635         MIRROR( m_offset.y, 0 );
636         MIRROR( m_deltaSize.y, 0 );
637     }
638 
639     SetOrientation( -GetOrientation() );
640 
641     auto mirrorBitFlags = []( int& aBitfield, int a, int b )
642                           {
643                               bool temp = aBitfield & a;
644 
645                               if( aBitfield & b )
646                                   aBitfield |= a;
647                               else
648                                   aBitfield &= ~a;
649 
650                               if( temp )
651                                   aBitfield |= b;
652                               else
653                                   aBitfield &= ~b;
654                           };
655 
656     if( aFlipLeftRight )
657     {
658         mirrorBitFlags( m_chamferPositions, RECT_CHAMFER_TOP_LEFT, RECT_CHAMFER_TOP_RIGHT );
659         mirrorBitFlags( m_chamferPositions, RECT_CHAMFER_BOTTOM_LEFT, RECT_CHAMFER_BOTTOM_RIGHT );
660     }
661     else
662     {
663         mirrorBitFlags( m_chamferPositions, RECT_CHAMFER_TOP_LEFT, RECT_CHAMFER_BOTTOM_LEFT );
664         mirrorBitFlags( m_chamferPositions, RECT_CHAMFER_TOP_RIGHT, RECT_CHAMFER_BOTTOM_RIGHT );
665     }
666 
667     // flip pads layers
668     // PADS items are currently on all copper layers, or
669     // currently, only on Front or Back layers.
670     // So the copper layers count is not taken in account
671     SetLayerSet( FlipLayerMask( m_layerMask ) );
672 
673     // Flip the basic shapes, in custom pads
674     FlipPrimitives( aFlipLeftRight );
675 
676     SetDirty();
677 }
678 
679 
FlipPrimitives(bool aFlipLeftRight)680 void PAD::FlipPrimitives( bool aFlipLeftRight )
681 {
682     for( std::shared_ptr<PCB_SHAPE>& primitive : m_editPrimitives )
683         primitive->Flip( wxPoint( 0, 0 ), aFlipLeftRight );
684 
685     SetDirty();
686 }
687 
688 
ShapePos() const689 wxPoint PAD::ShapePos() const
690 {
691     if( m_offset.x == 0 && m_offset.y == 0 )
692         return m_pos;
693 
694     wxPoint loc_offset = m_offset;
695 
696     RotatePoint( &loc_offset, m_orient );
697 
698     wxPoint shape_pos = m_pos + loc_offset;
699 
700     return shape_pos;
701 }
702 
703 
GetLocalClearanceOverrides(wxString * aSource) const704 int PAD::GetLocalClearanceOverrides( wxString* aSource ) const
705 {
706     // A pad can have specific clearance that overrides its NETCLASS clearance value
707     if( GetLocalClearance() )
708         return GetLocalClearance( aSource );
709 
710     // A footprint can have a specific clearance value
711     if( GetParent() && GetParent()->GetLocalClearance() )
712         return GetParent()->GetLocalClearance( aSource );
713 
714     return 0;
715 }
716 
717 
GetLocalClearance(wxString * aSource) const718 int PAD::GetLocalClearance( wxString* aSource ) const
719 {
720     if( aSource )
721         *aSource = _( "pad" );
722 
723     return m_localClearance;
724 }
725 
726 
GetSolderMaskMargin() const727 int PAD::GetSolderMaskMargin() const
728 {
729     // The pad inherits the margin only to calculate a default shape,
730     // therefore only if it is also a copper layer
731     // Pads defined only on mask layers (and perhaps on other tech layers) use the shape
732     // defined by the pad settings only
733     bool isOnCopperLayer = ( m_layerMask & LSET::AllCuMask() ).any();
734 
735     if( !isOnCopperLayer )
736         return 0;
737 
738     int     margin = m_localSolderMaskMargin;
739 
740     FOOTPRINT* parentFootprint = GetParent();
741 
742     if( parentFootprint )
743     {
744         if( margin == 0 )
745         {
746             if( parentFootprint->GetLocalSolderMaskMargin() )
747                 margin = parentFootprint->GetLocalSolderMaskMargin();
748         }
749 
750         if( margin == 0 )
751         {
752             const BOARD* brd = GetBoard();
753 
754             if( brd )
755                 margin = brd->GetDesignSettings().m_SolderMaskMargin;
756         }
757     }
758 
759     // ensure mask have a size always >= 0
760     if( margin < 0 )
761     {
762         int minsize = -std::min( m_size.x, m_size.y ) / 2;
763 
764         if( margin < minsize )
765             margin = minsize;
766     }
767 
768     return margin;
769 }
770 
771 
GetSolderPasteMargin() const772 wxSize PAD::GetSolderPasteMargin() const
773 {
774     // The pad inherits the margin only to calculate a default shape,
775     // therefore only if it is also a copper layer.
776     // Pads defined only on mask layers (and perhaps on other tech layers) use the shape
777     // defined by the pad settings only
778     bool isOnCopperLayer = ( m_layerMask & LSET::AllCuMask() ).any();
779 
780     if( !isOnCopperLayer )
781         return wxSize( 0, 0 );
782 
783     int     margin = m_localSolderPasteMargin;
784     double  mratio = m_localSolderPasteMarginRatio;
785 
786     FOOTPRINT* parentFootprint = GetParent();
787 
788     if( parentFootprint )
789     {
790         if( margin == 0 )
791             margin = parentFootprint->GetLocalSolderPasteMargin();
792 
793         auto brd = GetBoard();
794 
795         if( margin == 0 && brd )
796             margin = brd->GetDesignSettings().m_SolderPasteMargin;
797 
798         if( mratio == 0.0 )
799             mratio = parentFootprint->GetLocalSolderPasteMarginRatio();
800 
801         if( mratio == 0.0 && brd )
802         {
803             mratio = brd->GetDesignSettings().m_SolderPasteMarginRatio;
804         }
805     }
806 
807     wxSize pad_margin;
808     pad_margin.x = margin + KiROUND( m_size.x * mratio );
809     pad_margin.y = margin + KiROUND( m_size.y * mratio );
810 
811     // ensure mask have a size always >= 0
812     if( pad_margin.x < -m_size.x / 2 )
813         pad_margin.x = -m_size.x / 2;
814 
815     if( pad_margin.y < -m_size.y / 2 )
816         pad_margin.y = -m_size.y / 2;
817 
818     return pad_margin;
819 }
820 
821 
GetEffectiveZoneConnection(wxString * aSource) const822 ZONE_CONNECTION PAD::GetEffectiveZoneConnection( wxString* aSource ) const
823 {
824     FOOTPRINT* parentFootprint = GetParent();
825 
826     if( m_zoneConnection == ZONE_CONNECTION::INHERITED && parentFootprint )
827     {
828         if( aSource )
829             *aSource = _( "parent footprint" );
830 
831         return parentFootprint->GetZoneConnection();
832     }
833     else
834     {
835         if( aSource )
836             *aSource = _( "pad" );
837 
838         return m_zoneConnection;
839     }
840 }
841 
842 
GetEffectiveThermalSpokeWidth(wxString * aSource) const843 int PAD::GetEffectiveThermalSpokeWidth( wxString* aSource ) const
844 {
845     FOOTPRINT* parentFootprint = GetParent();
846 
847     if( m_thermalWidth == 0 && parentFootprint )
848     {
849         if( aSource )
850             *aSource = _( "parent footprint" );
851 
852         return parentFootprint->GetThermalWidth();
853     }
854 
855     if( aSource )
856         *aSource = _( "pad" );
857 
858     return m_thermalWidth;
859 }
860 
861 
GetEffectiveThermalGap(wxString * aSource) const862 int PAD::GetEffectiveThermalGap( wxString* aSource ) const
863 {
864     FOOTPRINT* parentFootprint = GetParent();
865 
866     if( m_thermalGap == 0 && parentFootprint )
867     {
868         if( aSource )
869             *aSource = _( "parent footprint" );
870 
871         return parentFootprint->GetThermalGap();
872     }
873 
874     if( aSource )
875         *aSource = _( "pad" );
876 
877     return m_thermalGap;
878 }
879 
880 
GetMsgPanelInfo(EDA_DRAW_FRAME * aFrame,std::vector<MSG_PANEL_ITEM> & aList)881 void PAD::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
882 {
883     EDA_UNITS  units = aFrame->GetUserUnits();
884     wxString   msg;
885     FOOTPRINT* parentFootprint = static_cast<FOOTPRINT*>( m_parent );
886 
887     if( aFrame->GetName() == PCB_EDIT_FRAME_NAME )
888     {
889         if( parentFootprint )
890             aList.emplace_back( _( "Footprint" ), parentFootprint->GetReference() );
891     }
892 
893     aList.emplace_back( _( "Pad" ), m_number );
894 
895     if( !GetPinFunction().IsEmpty() )
896         aList.emplace_back( _( "Pin Name" ), GetPinFunction() );
897 
898     if( !GetPinType().IsEmpty() )
899         aList.emplace_back( _( "Pin Type" ), GetPinType() );
900 
901     if( aFrame->GetName() == PCB_EDIT_FRAME_NAME )
902     {
903         aList.emplace_back( _( "Net" ), UnescapeString( GetNetname() ) );
904 
905         aList.emplace_back( _( "Net Class" ), UnescapeString( GetNetClass()->GetName() ) );
906 
907         if( IsLocked() )
908             aList.emplace_back( _( "Status" ), _( "Locked" ) );
909     }
910 
911     if( GetAttribute() == PAD_ATTRIB::SMD || GetAttribute() == PAD_ATTRIB::CONN )
912         aList.emplace_back( _( "Layer" ), layerMaskDescribe() );
913 
914     // Show the pad shape, attribute and property
915     wxString props = ShowPadAttr();
916 
917     if( GetProperty() != PAD_PROP::NONE )
918         props += ',';
919 
920     switch( GetProperty() )
921     {
922     case PAD_PROP::NONE:                                            break;
923     case PAD_PROP::BGA:            props += _( "BGA" );             break;
924     case PAD_PROP::FIDUCIAL_GLBL:  props += _( "Fiducial global" ); break;
925     case PAD_PROP::FIDUCIAL_LOCAL: props += _( "Fiducial local" );  break;
926     case PAD_PROP::TESTPOINT:      props += _( "Test point" );      break;
927     case PAD_PROP::HEATSINK:       props += _( "Heat sink" );       break;
928     case PAD_PROP::CASTELLATED:    props += _( "Castellated" );     break;
929     }
930 
931     aList.emplace_back( ShowPadShape(), props );
932 
933     if( ( GetShape() == PAD_SHAPE::CIRCLE || GetShape() == PAD_SHAPE::OVAL ) &&
934         m_size.x == m_size.y )
935     {
936         aList.emplace_back( _( "Diameter" ), MessageTextFromValue( units, m_size.x ) );
937     }
938     else
939     {
940         aList.emplace_back( _( "Width" ), MessageTextFromValue( units, m_size.x ) );
941         aList.emplace_back( _( "Height" ), MessageTextFromValue( units, m_size.y ) );
942     }
943 
944     double fp_orient_degrees = parentFootprint ? parentFootprint->GetOrientationDegrees() : 0;
945     double pad_orient_degrees = GetOrientationDegrees() - fp_orient_degrees;
946     pad_orient_degrees = NormalizeAngleDegrees( pad_orient_degrees, -180.0, +180.0 );
947 
948     if( fp_orient_degrees != 0.0 )
949         msg.Printf( wxT( "%g(+ %g)" ), pad_orient_degrees, fp_orient_degrees );
950     else
951         msg.Printf( wxT( "%g" ), GetOrientationDegrees() );
952 
953     aList.emplace_back( _( "Rotation" ), msg );
954 
955     if( GetPadToDieLength() )
956     {
957         msg = MessageTextFromValue(units, GetPadToDieLength() );
958         aList.emplace_back( _( "Length in Package" ), msg );
959     }
960 
961     if( m_drill.x > 0 || m_drill.y > 0 )
962     {
963         if( GetDrillShape() == PAD_DRILL_SHAPE_CIRCLE )
964         {
965             aList.emplace_back( _( "Hole" ),
966                                 wxString::Format( "%s",
967                                                   MessageTextFromValue( units, m_drill.x ) ) );
968         }
969         else
970         {
971             aList.emplace_back( _( "Hole X / Y" ),
972                                 wxString::Format( "%s / %s",
973                                                   MessageTextFromValue( units, m_drill.x ),
974                                                   MessageTextFromValue( units, m_drill.y ) ) );
975         }
976     }
977 
978     wxString source;
979     int      clearance = GetOwnClearance( GetLayer(), &source );
980 
981     if( !source.IsEmpty() )
982     {
983         aList.emplace_back( wxString::Format( _( "Min Clearance: %s" ),
984                                               MessageTextFromValue( units, clearance ) ),
985                             wxString::Format( _( "(from %s)" ),
986                                               source ) );
987     }
988 #if 0
989     // useful for debug only
990     aList.emplace_back( "UUID", m_Uuid.AsString() );
991 #endif
992 }
993 
994 
HitTest(const wxPoint & aPosition,int aAccuracy) const995 bool PAD::HitTest( const wxPoint& aPosition, int aAccuracy ) const
996 {
997     VECTOR2I delta = aPosition - GetPosition();
998     int      boundingRadius = GetBoundingRadius() + aAccuracy;
999 
1000     if( delta.SquaredEuclideanNorm() > SEG::Square( boundingRadius ) )
1001         return false;
1002 
1003     return GetEffectivePolygon()->Contains( aPosition, -1, aAccuracy );
1004 }
1005 
1006 
HitTest(const EDA_RECT & aRect,bool aContained,int aAccuracy) const1007 bool PAD::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const
1008 {
1009     EDA_RECT arect = aRect;
1010     arect.Normalize();
1011     arect.Inflate( aAccuracy );
1012 
1013     EDA_RECT bbox = GetBoundingBox();
1014 
1015     if( aContained )
1016     {
1017         return arect.Contains( bbox );
1018     }
1019     else
1020     {
1021         // Fast test: if aRect is outside the polygon bounding box,
1022         // rectangles cannot intersect
1023         if( !arect.Intersects( bbox ) )
1024             return false;
1025 
1026         const std::shared_ptr<SHAPE_POLY_SET>& poly = GetEffectivePolygon();
1027 
1028         int count = poly->TotalVertices();
1029 
1030         for( int ii = 0; ii < count; ii++ )
1031         {
1032             auto vertex = poly->CVertex( ii );
1033             auto vertexNext = poly->CVertex(( ii + 1 ) % count );
1034 
1035             // Test if the point is within aRect
1036             if( arect.Contains( ( wxPoint ) vertex ) )
1037                 return true;
1038 
1039             // Test if this edge intersects aRect
1040             if( arect.Intersects( ( wxPoint ) vertex, ( wxPoint ) vertexNext ) )
1041                 return true;
1042         }
1043 
1044         return false;
1045     }
1046 }
1047 
1048 
Compare(const PAD * aPadRef,const PAD * aPadCmp)1049 int PAD::Compare( const PAD* aPadRef, const PAD* aPadCmp )
1050 {
1051     int diff;
1052 
1053     if( ( diff = static_cast<int>( aPadRef->GetShape() ) -
1054           static_cast<int>( aPadCmp->GetShape() ) ) != 0 )
1055         return diff;
1056 
1057     if( ( diff = static_cast<int>( aPadRef->m_attribute ) -
1058           static_cast<int>( aPadCmp->m_attribute ) ) != 0 )
1059         return diff;
1060 
1061     if( ( diff = aPadRef->m_drillShape - aPadCmp->m_drillShape ) != 0 )
1062         return diff;
1063 
1064     if( ( diff = aPadRef->m_drill.x - aPadCmp->m_drill.x ) != 0 )
1065         return diff;
1066 
1067     if( ( diff = aPadRef->m_drill.y - aPadCmp->m_drill.y ) != 0 )
1068         return diff;
1069 
1070     if( ( diff = aPadRef->m_size.x - aPadCmp->m_size.x ) != 0 )
1071         return diff;
1072 
1073     if( ( diff = aPadRef->m_size.y - aPadCmp->m_size.y ) != 0 )
1074         return diff;
1075 
1076     if( ( diff = aPadRef->m_offset.x - aPadCmp->m_offset.x ) != 0 )
1077         return diff;
1078 
1079     if( ( diff = aPadRef->m_offset.y - aPadCmp->m_offset.y ) != 0 )
1080         return diff;
1081 
1082     if( ( diff = aPadRef->m_deltaSize.x - aPadCmp->m_deltaSize.x ) != 0 )
1083         return diff;
1084 
1085     if( ( diff = aPadRef->m_deltaSize.y - aPadCmp->m_deltaSize.y ) != 0 )
1086         return diff;
1087 
1088     if( ( diff = aPadRef->m_roundedCornerScale - aPadCmp->m_roundedCornerScale ) != 0 )
1089         return diff;
1090 
1091     if( ( diff = aPadRef->m_chamferPositions - aPadCmp->m_chamferPositions ) != 0 )
1092         return diff;
1093 
1094     if( ( diff = aPadRef->m_chamferScale - aPadCmp->m_chamferScale ) != 0 )
1095         return diff;
1096 
1097     if( ( diff = static_cast<int>( aPadRef->m_editPrimitives.size() ) -
1098           static_cast<int>( aPadCmp->m_editPrimitives.size() ) ) != 0 )
1099         return diff;
1100 
1101     // @todo: Compare custom pad primitives for pads that have the same number of primitives
1102     //        here.  Currently there is no compare function for PCB_SHAPE objects.
1103 
1104     // Dick: specctra_export needs this
1105     // Lorenzo: gencad also needs it to implement padstacks!
1106 
1107 #if __cplusplus >= 201103L
1108     long long d = aPadRef->m_layerMask.to_ullong() - aPadCmp->m_layerMask.to_ullong();
1109 
1110     if( d < 0 )
1111         return -1;
1112     else if( d > 0 )
1113         return 1;
1114 
1115     return 0;
1116 #else
1117     // these strings are not typically constructed, since we don't get here often.
1118     std::string s1 = aPadRef->m_layerMask.to_string();
1119     std::string s2 = aPadCmp->m_layerMask.to_string();
1120     return s1.compare( s2 );
1121 #endif
1122 }
1123 
1124 
Rotate(const wxPoint & aRotCentre,double aAngle)1125 void PAD::Rotate( const wxPoint& aRotCentre, double aAngle )
1126 {
1127     RotatePoint( &m_pos, aRotCentre, aAngle );
1128 
1129     m_orient = NormalizeAngle360Min( m_orient + aAngle );
1130 
1131     SetLocalCoord();
1132 
1133     SetDirty();
1134 }
1135 
1136 
ShowPadShape() const1137 wxString PAD::ShowPadShape() const
1138 {
1139     switch( GetShape() )
1140     {
1141     case PAD_SHAPE::CIRCLE:         return _( "Circle" );
1142     case PAD_SHAPE::OVAL:           return _( "Oval" );
1143     case PAD_SHAPE::RECT:           return _( "Rect" );
1144     case PAD_SHAPE::TRAPEZOID:      return _( "Trap" );
1145     case PAD_SHAPE::ROUNDRECT:      return _( "Roundrect" );
1146     case PAD_SHAPE::CHAMFERED_RECT: return _( "Chamferedrect" );
1147     case PAD_SHAPE::CUSTOM:         return _( "CustomShape" );
1148     default:                        return wxT( "???" );
1149     }
1150 }
1151 
1152 
ShowPadAttr() const1153 wxString PAD::ShowPadAttr() const
1154 {
1155     switch( GetAttribute() )
1156     {
1157     case PAD_ATTRIB::PTH:    return _( "PTH" );
1158     case PAD_ATTRIB::SMD:    return _( "SMD" );
1159     case PAD_ATTRIB::CONN:   return _( "Conn" );
1160     case PAD_ATTRIB::NPTH:   return _( "NPTH" );
1161     default:                 return wxT( "???" );
1162     }
1163 }
1164 
1165 
GetSelectMenuText(EDA_UNITS aUnits) const1166 wxString PAD::GetSelectMenuText( EDA_UNITS aUnits ) const
1167 {
1168     if( GetNumber().IsEmpty() )
1169     {
1170         if( GetAttribute() == PAD_ATTRIB::SMD || GetAttribute() == PAD_ATTRIB::CONN )
1171         {
1172             return wxString::Format( _( "Pad %s of %s on %s" ),
1173                                      GetNetnameMsg(),
1174                                      GetParent()->GetReference(),
1175                                      layerMaskDescribe() );
1176         }
1177         else if( GetAttribute() == PAD_ATTRIB::NPTH && !FlashLayer( UNDEFINED_LAYER ) )
1178         {
1179             return wxString::Format( _( "Through hole pad %s of %s" ),
1180                                      wxT( "(" ) + _( "NPTH, Mechanical" ) + wxT( ")" ),
1181                                      GetParent()->GetReference() );
1182         }
1183         else
1184         {
1185             return wxString::Format( _( "Through hole pad %s of %s" ),
1186                                      GetNetnameMsg(),
1187                                      GetParent()->GetReference() );
1188         }
1189     }
1190     else
1191     {
1192         if( GetAttribute() == PAD_ATTRIB::SMD || GetAttribute() == PAD_ATTRIB::CONN )
1193         {
1194             return wxString::Format( _( "Pad %s %s of %s on %s" ),
1195                                      GetNumber(),
1196                                      GetNetnameMsg(),
1197                                      GetParent()->GetReference(),
1198                                      layerMaskDescribe() );
1199         }
1200         else if( GetAttribute() == PAD_ATTRIB::NPTH && !FlashLayer( UNDEFINED_LAYER ) )
1201         {
1202             return wxString::Format( _( "Through hole pad %s of %s" ),
1203                                      wxT( "(" ) + _( "NPTH, Mechanical" ) + wxT( ")" ),
1204                                      GetParent()->GetReference() );
1205         }
1206         else
1207         {
1208             return wxString::Format( _( "Through hole pad %s %s of %s" ),
1209                                      GetNumber(),
1210                                      GetNetnameMsg(),
1211                                      GetParent()->GetReference() );
1212         }
1213     }
1214 }
1215 
1216 
GetMenuImage() const1217 BITMAPS PAD::GetMenuImage() const
1218 {
1219     return BITMAPS::pad;
1220 }
1221 
1222 
Clone() const1223 EDA_ITEM* PAD::Clone() const
1224 {
1225     return new PAD( *this );
1226 }
1227 
1228 
ViewGetLayers(int aLayers[],int & aCount) const1229 void PAD::ViewGetLayers( int aLayers[], int& aCount ) const
1230 {
1231     aCount = 0;
1232 
1233     // These 2 types of pads contain a hole
1234     if( m_attribute == PAD_ATTRIB::PTH )
1235     {
1236         aLayers[aCount++] = LAYER_PAD_PLATEDHOLES;
1237         aLayers[aCount++] = LAYER_PAD_HOLEWALLS;
1238     }
1239 
1240     if( m_attribute == PAD_ATTRIB::NPTH )
1241         aLayers[aCount++] = LAYER_NON_PLATEDHOLES;
1242 
1243     if( IsOnLayer( F_Cu ) && IsOnLayer( B_Cu ) )
1244     {
1245         // Multi layer pad
1246         aLayers[aCount++] = LAYER_PADS_TH;
1247         aLayers[aCount++] = LAYER_PAD_NETNAMES;
1248     }
1249     else if( IsOnLayer( F_Cu ) )
1250     {
1251         aLayers[aCount++] = LAYER_PAD_FR;
1252 
1253         // Is this a PTH pad that has only front copper?  If so, we need to also display the
1254         // net name on the PTH netname layer so that it isn't blocked by the drill hole.
1255         if( m_attribute == PAD_ATTRIB::PTH )
1256             aLayers[aCount++] = LAYER_PAD_NETNAMES;
1257         else
1258             aLayers[aCount++] = LAYER_PAD_FR_NETNAMES;
1259     }
1260     else if( IsOnLayer( B_Cu ) )
1261     {
1262         aLayers[aCount++] = LAYER_PAD_BK;
1263 
1264         // Is this a PTH pad that has only back copper?  If so, we need to also display the
1265         // net name on the PTH netname layer so that it isn't blocked by the drill hole.
1266         if( m_attribute == PAD_ATTRIB::PTH )
1267             aLayers[aCount++] = LAYER_PAD_NETNAMES;
1268         else
1269             aLayers[aCount++] = LAYER_PAD_BK_NETNAMES;
1270     }
1271     else
1272     {
1273         // Internal layers only.  (Not yet supported in GUI, but is being used by Python
1274         // footprint generators and will be needed anyway once pad stacks are supported.)
1275         for ( int internal = In1_Cu; internal < In30_Cu; ++internal )
1276         {
1277             if( IsOnLayer( (PCB_LAYER_ID) internal ) )
1278                 aLayers[aCount++] = internal;
1279         }
1280     }
1281 
1282     // Check non-copper layers. This list should include all the layers that the
1283     // footprint editor allows a pad to be placed on.
1284     static const PCB_LAYER_ID layers_mech[] = { F_Mask, B_Mask, F_Paste, B_Paste,
1285         F_Adhes, B_Adhes, F_SilkS, B_SilkS, Dwgs_User, Eco1_User, Eco2_User };
1286 
1287     for( PCB_LAYER_ID each_layer : layers_mech )
1288     {
1289         if( IsOnLayer( each_layer ) )
1290             aLayers[aCount++] = each_layer;
1291     }
1292 
1293 #ifdef DEBUG
1294     if( aCount == 0 )    // Should not occur
1295     {
1296         wxString msg;
1297         msg.Printf( wxT( "footprint %s, pad %s: could not find valid layer for pad" ),
1298                 GetParent() ? GetParent()->GetReference() : "<null>",
1299                 GetNumber().IsEmpty() ? "(unnumbered)" : GetNumber() );
1300         wxLogDebug( msg );
1301     }
1302 #endif
1303 }
1304 
1305 
ViewGetLOD(int aLayer,KIGFX::VIEW * aView) const1306 double PAD::ViewGetLOD( int aLayer, KIGFX::VIEW* aView ) const
1307 {
1308     constexpr double HIDE = std::numeric_limits<double>::max();
1309 
1310     PCB_PAINTER*         painter = static_cast<PCB_PAINTER*>( aView->GetPainter() );
1311     PCB_RENDER_SETTINGS* renderSettings = painter->GetSettings();
1312     const BOARD*         board = GetBoard();
1313     LSET                 visible = LSET::AllLayersMask();
1314 
1315     // Meta control for hiding all pads
1316     if( !aView->IsLayerVisible( LAYER_PADS ) )
1317         return HIDE;
1318 
1319     // Handle board visibility
1320     if( board )
1321         visible = board->GetVisibleLayers() & board->GetEnabledLayers();
1322 
1323     // Handle Render tab switches
1324     if( ( GetAttribute() == PAD_ATTRIB::PTH || GetAttribute() == PAD_ATTRIB::NPTH )
1325          && !aView->IsLayerVisible( LAYER_PADS_TH ) )
1326     {
1327         return HIDE;
1328     }
1329 
1330     if( !IsFlipped() && !aView->IsLayerVisible( LAYER_MOD_FR ) )
1331         return HIDE;
1332 
1333     if( IsFlipped() && !aView->IsLayerVisible( LAYER_MOD_BK ) )
1334         return HIDE;
1335 
1336     if( IsFrontLayer( (PCB_LAYER_ID) aLayer ) && !aView->IsLayerVisible( LAYER_PAD_FR ) )
1337         return HIDE;
1338 
1339     if( IsBackLayer( (PCB_LAYER_ID) aLayer ) && !aView->IsLayerVisible( LAYER_PAD_BK ) )
1340         return HIDE;
1341 
1342     if( aLayer == LAYER_PADS_TH )
1343     {
1344         if( !FlashLayer( visible ) )
1345             return HIDE;
1346     }
1347     else if( IsHoleLayer( aLayer ) )
1348     {
1349         if( !( visible & LSET::PhysicalLayersMask() ).any() )
1350             return HIDE;
1351     }
1352     else if( IsNetnameLayer( aLayer ) )
1353     {
1354         if( renderSettings->GetHighContrast() )
1355         {
1356             // Hide netnames unless pad is flashed to a high-contrast layer
1357             if( !FlashLayer( renderSettings->GetPrimaryHighContrastLayer() ) )
1358                 return HIDE;
1359         }
1360         else
1361         {
1362             // Hide netnames unless pad is flashed to a visible layer
1363             if( !FlashLayer( visible ) )
1364                 return HIDE;
1365         }
1366 
1367         // Netnames will be shown only if zoom is appropriate
1368         int divisor = std::min( GetBoundingBox().GetWidth(), GetBoundingBox().GetHeight() );
1369 
1370         // Pad sizes can be zero briefly when someone is typing a number like "0.5"
1371         // in the pad properties dialog
1372         if( divisor == 0 )
1373             return HIDE;
1374 
1375         return ( double ) Millimeter2iu( 5 ) / divisor;
1376     }
1377 
1378     if( aLayer == LAYER_PADS_TH
1379             && GetShape() != PAD_SHAPE::CUSTOM
1380             && GetSizeX() <= GetDrillSizeX()
1381             && GetSizeY() <= GetDrillSizeY() )
1382     {
1383         // Don't tweak the drawing code with a degenerate pad
1384         return HIDE;
1385     }
1386 
1387     // Passed all tests; show.
1388     return 0.0;
1389 }
1390 
1391 
ViewBBox() const1392 const BOX2I PAD::ViewBBox() const
1393 {
1394     // Bounding box includes soldermask too. Remember mask and/or paste
1395     // margins can be < 0
1396     int solderMaskMargin       = std::max( GetSolderMaskMargin(), 0 );
1397     VECTOR2I solderPasteMargin = VECTOR2D( GetSolderPasteMargin() );
1398     EDA_RECT bbox              = GetBoundingBox();
1399 
1400     // get the biggest possible clearance
1401     int clearance = 0;
1402 
1403     for( PCB_LAYER_ID layer : GetLayerSet().Seq() )
1404         clearance = std::max( clearance, GetOwnClearance( layer ) );
1405 
1406     // Look for the biggest possible bounding box
1407     int xMargin = std::max( solderMaskMargin, solderPasteMargin.x ) + clearance;
1408     int yMargin = std::max( solderMaskMargin, solderPasteMargin.y ) + clearance;
1409 
1410     return BOX2I( VECTOR2I( bbox.GetOrigin() ) - VECTOR2I( xMargin, yMargin ),
1411                   VECTOR2I( bbox.GetSize() ) + VECTOR2I( 2 * xMargin, 2 * yMargin ) );
1412 }
1413 
1414 
GetParent() const1415 FOOTPRINT* PAD::GetParent() const
1416 {
1417     return dynamic_cast<FOOTPRINT*>( m_parent );
1418 }
1419 
1420 
ImportSettingsFrom(const PAD & aMasterPad)1421 void PAD::ImportSettingsFrom( const PAD& aMasterPad )
1422 {
1423     SetShape( aMasterPad.GetShape() );
1424     SetLayerSet( aMasterPad.GetLayerSet() );
1425     SetAttribute( aMasterPad.GetAttribute() );
1426     SetProperty( aMasterPad.GetProperty() );
1427 
1428     // I am not sure the m_LengthPadToDie must be imported, because this is
1429     // a parameter really specific to a given pad (JPC).
1430     // So this is currently non imported
1431 #if 0
1432     SetPadToDieLength( aMasterPad.GetPadToDieLength() );
1433 #endif
1434 
1435     // The pad orientation, for historical reasons is the
1436     // pad rotation + parent rotation.
1437     // So we have to manage this parent rotation
1438     double pad_rot = aMasterPad.GetOrientation();
1439 
1440     if( aMasterPad.GetParent() )
1441         pad_rot -= aMasterPad.GetParent()->GetOrientation();
1442 
1443     if( GetParent() )
1444         pad_rot += GetParent()->GetOrientation();
1445 
1446     SetOrientation( pad_rot );
1447 
1448     SetSize( aMasterPad.GetSize() );
1449     SetDelta( wxSize( 0, 0 ) );
1450     SetOffset( aMasterPad.GetOffset() );
1451     SetDrillSize( aMasterPad.GetDrillSize() );
1452     SetDrillShape( aMasterPad.GetDrillShape() );
1453     SetRoundRectRadiusRatio( aMasterPad.GetRoundRectRadiusRatio() );
1454     SetChamferRectRatio( aMasterPad.GetChamferRectRatio() );
1455     SetChamferPositions( aMasterPad.GetChamferPositions() );
1456 
1457     switch( aMasterPad.GetShape() )
1458     {
1459     case PAD_SHAPE::TRAPEZOID:
1460         SetDelta( aMasterPad.GetDelta() );
1461         break;
1462 
1463     case PAD_SHAPE::CIRCLE:
1464         // ensure size.y == size.x
1465         SetSize( wxSize( GetSize().x, GetSize().x ) );
1466         break;
1467 
1468     default:
1469         ;
1470     }
1471 
1472     switch( aMasterPad.GetAttribute() )
1473     {
1474     case PAD_ATTRIB::SMD:
1475     case PAD_ATTRIB::CONN:
1476         // These pads do not have hole (they are expected to be only on one
1477         // external copper layer)
1478         SetDrillSize( wxSize( 0, 0 ) );
1479         break;
1480 
1481     default:
1482         ;
1483     }
1484 
1485     // copy also local settings:
1486     SetLocalClearance( aMasterPad.GetLocalClearance() );
1487     SetLocalSolderMaskMargin( aMasterPad.GetLocalSolderMaskMargin() );
1488     SetLocalSolderPasteMargin( aMasterPad.GetLocalSolderPasteMargin() );
1489     SetLocalSolderPasteMarginRatio( aMasterPad.GetLocalSolderPasteMarginRatio() );
1490 
1491     SetZoneConnection( aMasterPad.GetEffectiveZoneConnection() );
1492     SetThermalSpokeWidth( aMasterPad.GetThermalSpokeWidth() );
1493     SetThermalGap( aMasterPad.GetThermalGap() );
1494 
1495     SetCustomShapeInZoneOpt( aMasterPad.GetCustomShapeInZoneOpt() );
1496 
1497     // Add or remove custom pad shapes:
1498     ReplacePrimitives( aMasterPad.GetPrimitives() );
1499     SetAnchorPadShape( aMasterPad.GetAnchorPadShape() );
1500 
1501     SetDirty();
1502 }
1503 
1504 
SwapData(BOARD_ITEM * aImage)1505 void PAD::SwapData( BOARD_ITEM* aImage )
1506 {
1507     assert( aImage->Type() == PCB_PAD_T );
1508 
1509     std::swap( *((PAD*) this), *((PAD*) aImage) );
1510 }
1511 
1512 
TransformHoleWithClearanceToPolygon(SHAPE_POLY_SET & aCornerBuffer,int aInflateValue,int aError,ERROR_LOC aErrorLoc) const1513 bool PAD::TransformHoleWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, int aInflateValue,
1514                                                int aError, ERROR_LOC aErrorLoc ) const
1515 {
1516     wxSize drillsize = GetDrillSize();
1517 
1518     if( !drillsize.x || !drillsize.y )
1519         return false;
1520 
1521     const SHAPE_SEGMENT* seg = GetEffectiveHoleShape();
1522 
1523     TransformOvalToPolygon( aCornerBuffer, (wxPoint) seg->GetSeg().A, (wxPoint) seg->GetSeg().B,
1524                             seg->GetWidth() + aInflateValue * 2, aError, aErrorLoc );
1525 
1526     return true;
1527 }
1528 
1529 
TransformShapeWithClearanceToPolygon(SHAPE_POLY_SET & aCornerBuffer,PCB_LAYER_ID aLayer,int aClearanceValue,int aError,ERROR_LOC aErrorLoc,bool ignoreLineWidth) const1530 void PAD::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
1531                                                 PCB_LAYER_ID aLayer, int aClearanceValue,
1532                                                 int aError, ERROR_LOC aErrorLoc,
1533                                                 bool ignoreLineWidth ) const
1534 {
1535     wxASSERT_MSG( !ignoreLineWidth, "IgnoreLineWidth has no meaning for pads." );
1536 
1537     // minimal segment count to approximate a circle to create the polygonal pad shape
1538     // This minimal value is mainly for very small pads, like SM0402.
1539     // Most of time pads are using the segment count given by aError value.
1540     const int pad_min_seg_per_circle_count = 16;
1541     double  angle = m_orient;
1542     int     dx = m_size.x / 2;
1543     int     dy = m_size.y / 2;
1544 
1545     wxPoint padShapePos = ShapePos();         // Note: for pad having a shape offset,
1546                                               // the pad position is NOT the shape position
1547 
1548     switch( GetShape() )
1549     {
1550     case PAD_SHAPE::CIRCLE:
1551     case PAD_SHAPE::OVAL:
1552         // Note: dx == dy is not guaranteed for circle pads in legacy boards
1553         if( dx == dy || ( GetShape() == PAD_SHAPE::CIRCLE ) )
1554         {
1555             TransformCircleToPolygon( aCornerBuffer, padShapePos, dx + aClearanceValue, aError,
1556                                       aErrorLoc, pad_min_seg_per_circle_count );
1557         }
1558         else
1559         {
1560             int     half_width = std::min( dx, dy );
1561             wxPoint delta( dx - half_width, dy - half_width );
1562 
1563             RotatePoint( &delta, angle );
1564 
1565             TransformOvalToPolygon( aCornerBuffer, padShapePos - delta, padShapePos + delta,
1566                                     ( half_width + aClearanceValue ) * 2, aError, aErrorLoc,
1567                                     pad_min_seg_per_circle_count );
1568         }
1569 
1570         break;
1571 
1572     case PAD_SHAPE::TRAPEZOID:
1573     case PAD_SHAPE::RECT:
1574     {
1575         int  ddx = GetShape() == PAD_SHAPE::TRAPEZOID ? m_deltaSize.x / 2 : 0;
1576         int  ddy = GetShape() == PAD_SHAPE::TRAPEZOID ? m_deltaSize.y / 2 : 0;
1577 
1578         SHAPE_POLY_SET outline;
1579         TransformTrapezoidToPolygon( outline, padShapePos, m_size, angle, ddx, ddy,
1580                                      aClearanceValue, aError, aErrorLoc );
1581         aCornerBuffer.Append( outline );
1582         break;
1583     }
1584 
1585     case PAD_SHAPE::CHAMFERED_RECT:
1586     case PAD_SHAPE::ROUNDRECT:
1587     {
1588         bool doChamfer = GetShape() == PAD_SHAPE::CHAMFERED_RECT;
1589 
1590         SHAPE_POLY_SET outline;
1591         TransformRoundChamferedRectToPolygon( outline, padShapePos, m_size, angle,
1592                                               GetRoundRectCornerRadius(),
1593                                               doChamfer ? GetChamferRectRatio() : 0,
1594                                               doChamfer ? GetChamferPositions() : 0,
1595                                               aClearanceValue, aError, aErrorLoc );
1596         aCornerBuffer.Append( outline );
1597         break;
1598     }
1599 
1600     case PAD_SHAPE::CUSTOM:
1601     {
1602         SHAPE_POLY_SET outline;
1603         MergePrimitivesAsPolygon( &outline, aErrorLoc );
1604         outline.Rotate( -DECIDEG2RAD( m_orient ) );
1605         outline.Move( VECTOR2I( m_pos ) );
1606 
1607         if( aClearanceValue )
1608         {
1609             int numSegs = std::max( GetArcToSegmentCount( aClearanceValue, aError, 360.0 ),
1610                                                           pad_min_seg_per_circle_count );
1611             int clearance = aClearanceValue;
1612 
1613             if( aErrorLoc == ERROR_OUTSIDE )
1614             {
1615                 int actual_error = CircleToEndSegmentDeltaRadius( clearance, numSegs );
1616                 clearance += GetCircleToPolyCorrection( actual_error );
1617             }
1618 
1619             outline.Inflate( clearance, numSegs );
1620             outline.Simplify( SHAPE_POLY_SET::PM_FAST );
1621             outline.Fracture( SHAPE_POLY_SET::PM_FAST );
1622         }
1623 
1624         aCornerBuffer.Append( outline );
1625         break;
1626     }
1627 
1628     default:
1629         wxFAIL_MSG( "PAD::TransformShapeWithClearanceToPolygon no implementation for "
1630                     + PAD_SHAPE_T_asString( GetShape() ) );
1631         break;
1632     }
1633 }
1634 
1635 
1636 static struct PAD_DESC
1637 {
PAD_DESCPAD_DESC1638     PAD_DESC()
1639     {
1640         ENUM_MAP<PAD_ATTRIB>::Instance()
1641                 .Map( PAD_ATTRIB::PTH,             _HKI( "Through-hole" ) )
1642                 .Map( PAD_ATTRIB::SMD,             _HKI( "SMD" ) )
1643                 .Map( PAD_ATTRIB::CONN,            _HKI( "Edge connector" ) )
1644                 .Map( PAD_ATTRIB::NPTH,            _HKI( "NPTH, mechanical" ) );
1645 
1646         ENUM_MAP<PAD_SHAPE>::Instance()
1647                 .Map( PAD_SHAPE::CIRCLE,           _HKI( "Circle" ) )
1648                 .Map( PAD_SHAPE::RECT,             _HKI( "Rectangle" ) )
1649                 .Map( PAD_SHAPE::OVAL,             _HKI( "Oval" ) )
1650                 .Map( PAD_SHAPE::TRAPEZOID,        _HKI( "Trapezoid" ) )
1651                 .Map( PAD_SHAPE::ROUNDRECT,        _HKI( "Rounded rectangle" ) )
1652                 .Map( PAD_SHAPE::CHAMFERED_RECT,   _HKI( "Chamfered rectangle" ) )
1653                 .Map( PAD_SHAPE::CUSTOM,           _HKI( "Custom" ) );
1654 
1655         ENUM_MAP<PAD_PROP>::Instance()
1656                 .Map( PAD_PROP::NONE,              _HKI( "None" ) )
1657                 .Map( PAD_PROP::BGA,               _HKI( "BGA pad" ) )
1658                 .Map( PAD_PROP::FIDUCIAL_GLBL,     _HKI( "Fiducial, global to board" ) )
1659                 .Map( PAD_PROP::FIDUCIAL_LOCAL,    _HKI( "Fiducial, local to footprint" ) )
1660                 .Map( PAD_PROP::TESTPOINT,         _HKI( "Test point pad" ) )
1661                 .Map( PAD_PROP::HEATSINK,          _HKI( "Heatsink pad" ) )
1662                 .Map( PAD_PROP::CASTELLATED,       _HKI( "Castellated pad" ) );
1663 
1664         PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
1665         REGISTER_TYPE( PAD );
1666         propMgr.InheritsAfter( TYPE_HASH( PAD ), TYPE_HASH( BOARD_CONNECTED_ITEM ) );
1667 
1668         auto padType = new PROPERTY_ENUM<PAD, PAD_ATTRIB>( _HKI( "Pad Type" ),
1669                     &PAD::SetAttribute, &PAD::GetAttribute );
1670         propMgr.AddProperty( padType );
1671 
1672         auto shape = new PROPERTY_ENUM<PAD, PAD_SHAPE>( _HKI( "Shape" ),
1673                     &PAD::SetShape, &PAD::GetShape );
1674         propMgr.AddProperty( shape );
1675 
1676         propMgr.AddProperty( new PROPERTY<PAD, wxString>( _HKI( "Pad Number" ),
1677                     &PAD::SetNumber, &PAD::GetNumber ) );
1678         propMgr.AddProperty( new PROPERTY<PAD, wxString>( _HKI( "Pin Name" ),
1679                     &PAD::SetPinFunction, &PAD::GetPinFunction ) );
1680         propMgr.AddProperty( new PROPERTY<PAD, wxString>( _HKI( "Pin Type" ),
1681                     &PAD::SetPinType, &PAD::GetPinType ) );
1682         propMgr.AddProperty( new PROPERTY<PAD, double>( _HKI( "Orientation" ),
1683                     &PAD::SetOrientationDegrees, &PAD::GetOrientationDegrees,
1684                     PROPERTY_DISPLAY::DEGREE ) );
1685         propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Size X" ),
1686                     &PAD::SetSizeX, &PAD::GetSizeX,
1687                     PROPERTY_DISPLAY::DISTANCE ) );
1688         propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Size Y" ),
1689                     &PAD::SetSizeY, &PAD::GetSizeY,
1690                     PROPERTY_DISPLAY::DISTANCE ) );
1691         propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Hole Size X" ),
1692                     &PAD::SetDrillSizeX, &PAD::GetDrillSizeX,
1693                     PROPERTY_DISPLAY::DISTANCE ) );
1694         propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Hole Size Y" ),
1695                     &PAD::SetDrillSizeY, &PAD::GetDrillSizeY,
1696                     PROPERTY_DISPLAY::DISTANCE ) );
1697         propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Pad To Die Length" ),
1698                     &PAD::SetPadToDieLength, &PAD::GetPadToDieLength,
1699                     PROPERTY_DISPLAY::DISTANCE ) );
1700         propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Soldermask Margin Override" ),
1701                     &PAD::SetLocalSolderMaskMargin, &PAD::GetLocalSolderMaskMargin,
1702                     PROPERTY_DISPLAY::DISTANCE ) );
1703         propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Solderpaste Margin Override" ),
1704                     &PAD::SetLocalSolderPasteMargin, &PAD::GetLocalSolderPasteMargin,
1705                     PROPERTY_DISPLAY::DISTANCE ) );
1706         propMgr.AddProperty( new PROPERTY<PAD, double>( _HKI( "Solderpaste Margin Ratio Override" ),
1707                     &PAD::SetLocalSolderPasteMarginRatio, &PAD::GetLocalSolderPasteMarginRatio ) );
1708         propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Thermal Relief Width" ),
1709                     &PAD::SetThermalSpokeWidth, &PAD::GetThermalSpokeWidth,
1710                     PROPERTY_DISPLAY::DISTANCE ) );
1711         propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Thermal Relief Gap" ),
1712                     &PAD::SetThermalGap, &PAD::GetThermalGap,
1713                     PROPERTY_DISPLAY::DISTANCE ) );
1714         propMgr.AddProperty( new PROPERTY_ENUM<PAD, PAD_PROP>( _HKI( "Fabrication Property" ),
1715                     &PAD::SetProperty, &PAD::GetProperty ) );
1716 
1717         auto roundRadiusRatio = new PROPERTY<PAD, double>( _HKI( "Round Radius Ratio" ),
1718                     &PAD::SetRoundRectRadiusRatio, &PAD::GetRoundRectRadiusRatio );
1719         roundRadiusRatio->SetAvailableFunc(
1720                     [=]( INSPECTABLE* aItem ) -> bool
1721                     {
1722                         return aItem->Get( shape ) == static_cast<int>( PAD_SHAPE::ROUNDRECT );
1723                     } );
1724         propMgr.AddProperty( roundRadiusRatio );
1725 
1726         propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Clearance Override" ),
1727                     &PAD::SetLocalClearance, &PAD::GetLocalClearance,
1728                     PROPERTY_DISPLAY::DISTANCE ) );
1729         propMgr.AddProperty( new PROPERTY<PAD, wxString>( _HKI( "Parent" ),
1730                     NO_SETTER( PAD, wxString ), &PAD::GetParentAsString ) );
1731 
1732         // TODO delta, drill shape offset, layer set, zone connection
1733     }
1734 } _PAD_DESC;
1735 
1736 ENUM_TO_WXANY( PAD_ATTRIB );
1737 ENUM_TO_WXANY( PAD_SHAPE );
1738 ENUM_TO_WXANY( PAD_PROP );
1739